FFmpeg old video coding and new video flow

Call process of old video coding

(1)API registration
Only the interface of libavcodec part is used, so avcodec can be used when registering to use FFmpeg interface_ register_ All to register

avcodec_register_all();

(2) Find encoder
After the registration operation is completed, you first need to find the encoder you use. You can use the interface avcodec_find_encoder to set:

AVCodec *codec;
codec = avcodec_find_encoder(codec_id);
if(!codec)
{
    fprintf(stderr,"...%s...\n");
    exit(1);
}

When setting to find AVCodec, you need to pass codec_id, for example, if the encoder is set to encode H.264, it needs to encode AV_CODEC_ID_H264 encoder, of course, only if the H.264 encoder has been connected in FFmpeg, such as libx264, openh264 and h264_qsv et al

(3) Apply for AVCodecContext
After creating AVCodec, you need to create an AVCodecContext according to the AVCodec information, and then hang AVCodec on the

AVCodecContext above
AVCodecContext *c = NULL;
c = avcodec_alloc_context3(codec);
if(!c)
{
    fprintf(stderr,"...%s...\n");
    exit(1);
}

After applying for AVCodecContext, you need to set the encoding parameters. After setting the parameters of AVCodecContext, you can pass the parameters to the encoder:
Then the parameters can be passed to the encoder:
Yes verosnzg, you need to set the parameters of the code and set the o number e of T Neae

/* put sample parameters */
c->bit_ate  =  400000;
/* resolution must be a multipleof two */
c->width = 352;
c->height = 288;
/* frames per second */
c->time_base" = (AVRational){1,25};
/* emit one Intra frame every ten frames, check frame pict_ type before passing
frame to encoder, if frame->pict type is AV_ PICTURE TYPE I then gop_ size is ignored
and the output of encoder will always be I frame irrespective to gop_ size*/
c->gop_size = 10;
c->max_b_frames = 1;
c->pix_fmt = AV_PIX_FMT_YUV420P;

In the parameter setting, the video code rate is 400kbiv/s, the video width is 352, the height is 288, the frame rate is 25FPS, and the GOP size is 10 frames - one GOP It can contain 1 B frame at most, and the pixel color format is YUV420P.

(4) Encoder on
After setting the parameters, you can call avcodee_copen2 open encoder:

if(avcodec_open2(c,codec,NULL) < 0)
{
    fprintf(stderr,"Could not open codec\n");
    exit(1);
}

(5) Application frame structure AVFrame
After the encoder is turned on, you need to apply for video frame storage space to store video data of each frame:

AVFrame *frame;
frame = av_frame_alloc();
if(!frame)
{
    fprintf(stderr,"......\n");
    exit(1);
}
frame->format = c->pix_fmt;
frame->width = c->width;
frame->height = c->height;
/*the image can be allocated by any means and av_ image alloc() is just the most convenient way if av malloc() is to be used*/
ret = av image alloc(frame->data, frame->linesize, c->width, c->height, c->pix_fmt, 32);
if(ret<0)
{
    fprintf(stderr, "Could not allocate raw picture buffer\n");
    exit(1);
}

(6) Frame coding
(6) Frame coding
After the application for frame information storage space is completed, the video data can be written into frame - > data. When writing, it should be noted that if it is YUV data, the YUV storage space should be distinguished. After the frame data is written, the data can be encoded:

/* Y*/
for(y = 0; y < c->height; y++)
 {
    for(x = 0; x< c->width; x++) 
    {
        frame->data[0][y * frame->linesize[0]+x]=x+y+i*3;
    }    
}
/*Cb and Cr*/
for(y = 0; y< c->height/2; y++) 
{
    for (x =0; x< c->width/2; x++)
    {
        frame->data[1][y * frame->linesize[1] + x] = 128 + y + i * 2;
        frame->data[2][y * frame->linesize[2] +x] = 64 + x + 1 * 5;
/* encode the image */
ret = avcodec encode video2(C, &pkt, frame, &got_ output);
if(ret<0)
{
    fprintf(stderr, "Error encoding frame\n");
    exit(1);
}

After encoding, the encoded AVPacket will be generated, that is, the pkt in the code. After encoding, the pkt can be transferred by calling av_interleaved_write_frame interface encapsulation, but the focus here is not to introduce coding and then encapsulate it into a certain format, just coding. Therefore, you only need to save the encoded data:

fwrite(pkt.data,1,pkt.size,f);

(7) Finishing
Release previously applied resources

New video coding process

Complete code example

#include<stdio.h>
#include<stdlib.h>
#include<string.h>
#include"libavcodec/avcodec.h"
#include"libavutil/opt.h"
#include"libavutil/imgutils.h"

static void encode(AVCodecContext *enc_ctx,AVFrame *frame,AVPacket *pkt,FILE *outfile)
{
	int ret;
	if(frame)
		printf("Send frame %3"PRId64"\n",frame->pts);
	ret = avcodec_send_frame(enc_ctx,frame);
	if(ret < 0)
	{
		fprintf(stderr,"Error sending a frame for encoding\n");
		exit(1);
	}
	while(ret >= 0)
	{
		ret = avcodec_receive_packet(enc_ctx,pkt);
		if(ret == AVERROR(EAGAIN) || ret == AVERROR_EOF)
			return;
		else if(ret < 0)
		{
			fprintf(stderr,"Error during encoding\n");
			exit(1);
		}
		printf("Write packet %3"PRId64"(size=%5d)\n",pkt->pts,pkt->size);
		/*Save the encoded data*/
		fwrite(pkt->data,1,pkt->size,outfile);
		/*After AV_ packet_ After the unref function, just set data to NULL and size to 0*/
		av_packet_unref(pkt);
	}
}

int main(int argc,char **argv)
{
	const char *filename,codec_name;
	const AVCodec *codec;	//Audio and video codec structure
	AVCodecContext *c = NULL;	//Codec context structure
	int i,ret,x,y;
	FILE *f;
	AVFrame *frame;	//Decoded audio and video data
	AVPacket *pkt;	//Store a frame of compressed encoded data.
	uint_8 endcode[] = {0,0,1,0xb7};//???
	if(argc <= 2)
	{
		fprintf(stderr,"Usage:%s<output file><codec name>\n"argv[0]);
		exit(0);
	}
	filename = argv[1];
	codec_name = argv[2];
	codec = avcodec_find_encoder_by_name(codec_name);
	if(!codec)
	{
		fprintf(stderr,"Codec '%s' not found\n"codec_name);
		exit(1);
	}
	c = avcodec_alloc_context(codec);
	if(!c)
	{
		fprintf(stderr,"Could not allicate video codec context\n");
		exit(1);
	}
	pkt = av_packet_alloc();
	if(!pkt)
	{
		exit(1);
	}
	c->bit_rate = 400000;	//The bit rate is 400kbit/s
	c->width = 352;	//Width of video 352
	c->hight = 288;	//Video height 288
	c->time_base = (AVRational){1,25};	//The frame rate is 25fps
	c->framerate = (AVRational){25,1};
	/*
	   GOP A GOP with a size of 10 frames
	   It can contain up to one b frame
	   The pixel format is YUV420P
	 */
	c->gop_size = 10;
	c->max_b_frames = 1;
	c->pix_fmt = AV_PIX_FMT_YUV420P;
	if(codec->id == AV_CODEC_ID_H264)
		av_opt_set(c->priv_data,"preset","slow",0);//Set the parameter value of AVCodecContext
	ret = avcodec_open2(c,codec,NULL);
	if(ret < 0)
	{
		fprintf(stderr,"Could not open codec:%s\n",av_err2str(ret));
		exit(1);
	}
	f = fopen(filename,"wb");
	if(!f)
	{
		fprintf(stderr,"Could not open %s\n",filename);
		exit(1);
	}
	/*After the encoder is turned on, you need to apply for video frame storage space to store the video data of each frame*/
	frame = av_frame_alloc();
	if(!frame)
	{
		fprintf(stderr,"Could not allocate video frame\n");
		exit(1);
	}
	/*
	   format Original data after decoding
	   pix_fmt Supported pixel formats (video only)
	 */
	frame->format = c->pix_fmt;
	frame->width = c->width;
	frame->height = c->height;

	ret = av_frame_get_buffer(frame,0);	//Allocate AVFrame data area in advance
	if(ret < 0)
	{
		fprintf(stderr,"Could not allocate the video frame data\n");
		exit(1);
	}
	/*Encode one second video*/
	for(i = 0;i < 25; i++)
	{
		fflush(stdout);	/*Flush the output buffer of the output stream*/
		/*Make sure the frame data is writable*/
		ret = av_frame_make_writable(frame);
		if(ret < 0)
			exit(1);
		/*frame After the application for information storage space is completed, the video data can be written into frame - > data. The written data should be noted that if it is YUV data, the YUV storage space should be distinguished. After the frame data is written, the data can be encoded*/
		/*Y*/
		for(y = 0;y < c->height;y++)
		{
			for(x = 0;x < c->width;x++)
			{
				frame->data[0][y * frame->linesize[0] + X] = X+Y+i*3;
			}
		}
		/*Cb and Cr*/
		for(y = 0;y < c->height/2;y++)
		{
			for(x = 0;x < c->width/2;x++)
			{
				frame->data[1][y * frame->linesize[1] + X] = 128+y+i*2;
				frame->data[2][y * frame->linesize[2] + X] = 64+x+i*5;
			}
		}
		frame->pts = i;
		/*code*/
		encode(c,frame,pkt,f);
	}

	/*Clear all buffers of the stream so that all buffered data is written (not clear, right)*/
	encodec(c,NULL,pkt,f);
	/*Add the sequence end code to obtain the real MPEG file*/
	if(codec->id == AV_CODEC_ID_MPEG1VIDEO || codec->id == AV_CODEC_ID_MPEG2VIDEO)
		fwrite(encode,1,sizeof(encode),f);
	fclose(f);
	avcodec_free_context(&c);
	av_frame_free(&frame);
	av_packet_free(&pkt);
	return 0;

}

Introduction to important functions

avcodec_find_encoder_by_name: find the registered decoder according to the specified encoder name.
avcodec_alloc_context3: allocate memory for AVCodecContext.
avcodec_open2: open the decoder.
avcodec_send_frame: send AVFrame uncompressed data to the encoder. For detailed introduction, see the introduction of encoding and decoding API of FFmpeg audio decoding.
avcodec_receive_packet: get the encoded AVPacket data.
av_frame_get_buffer: allocate a new buffer for audio or video data. Before calling this function, you must set the following properties on AVFame: format (video is in pixel format, audio is in sample format), Nb_ Samples (number of samples for audio), channel_ Layout (channel type, for audio), width / height (width / height, for video).
av_frame_make_writable: ensure that AVFrame is writable and avoid data replication as much as possible. If AVFrame is not writable, new buffer will be allocated and data copied.

odec = avcodec_find_encoder_by_name(codec_name);
Get the encoder name in FFmpeg (codec - > name), and then directly through avcodec_find_encoder_by_name gets the encoder;
codec = avcodec_find_encoder_by_name("h264_nvenc");
Of course, you can also obtain codec - > ID (avcodecid) through avcodec_ find_ The encoder obtains the codec;
codec = avcodec_find_encoder( AV_CODEC_ID_H264 );
But there is a difference, h264, if through AV_CODEC_ID_H264 acquisition, the default acquisition is soft coding, usually x264;
But through name, h264_nvenc, you can obtain the hardware encoder to specify the encoding H264;
Because the encoder AVcodecID of nvenc and libx264 or libopenh264 is AV_CODEC_ID_H264, but the name is different;

In some scenarios, the data area of AVFrame needs to be allocated in advance. There are two methods that need to be used according to their own needs

(1)av_frame_get_buffer
The data space allocated by using this interface is reusable, that is, there is an internal reference count. After using frame data this time, the reference can be released, AV_ frame_ After unref (avframe * frame) is called, the reference count is reduced by 1. If the reference count becomes 0, the data space is released.

(2)av_image_alloc(uint8_t *pointers[4], int linesizes[4], int w, int h, enum AVPixelFormat pix_fmt, int align);
The difference between the above interface and (1) is that (1) only needs to input the frame pointer, while this interface cannot see the data structure of the frame. Compared with (1), the assigned level is lower,
See "the allocated image buffer has to be free by using av_free (& pointers [0]) in the note, that is, this allocation method can not be released implicitly by dereferencing in (2), and it needs to be released by calling itself. Pay attention to the pit in the use process: after using (1) to allocate, call an underlying interface of ffmpeg. As a result, the interface dereferences the input frame once, and the external call is unaware of this operation, resulting in possible memory error release and program collapse.

reference resources:
https://blog.csdn.net/leixiaohua1020/article/details/15811977
http://ffmpeg.org/doxygen/trunk/encode_video_8c-example.html

Tags: C C++ Android ffmpeg

Posted by dannydefreak on Sat, 14 May 2022 13:06:19 +0300