ffmpeg视频编码例子:https://git.ffmpeg.org/gitweb/ffmpeg.git/blob/HEAD:/doc/examples/encode_video.c
从main函数入手,核心接口:avcodec_find_encoder_by_name、avcodec_open2、avcodec_send_frame、avcodec_receive_packet
程序主要流程:
- 查找编码器
- 初始化编码器(编码参数)
- 构建视频数据包
- 编码数据包
- 清理解码器
这个例子编码的是裸流,所以没有封装等操作。
从输入里获得编码器名称
codec = avcodec_find_encoder_by_name(codec_name);
if (!codec) {
fprintf(stderr, "Codec '%s' not found
", codec_name);
exit(1);
}
设置详细编码参数(码率、分辨率、帧率、GOP、像素格式等)
/* put sample parameters */
c->bit_rate = 400000;
/* resolution must be a multiple of two */
c->width = 352;
c->height = 288;
/* frames per second */
c->time_base = (AVRational){1, 25};
c->framerate = (AVRational){25, 1};
/* 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;
构建数据包,数据包构建只是一些颜色(yuv),没有实际的图像,对应YUV格式赋值填满即可
/* prepare a dummy image */
/* 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;
}
}
编码输出,对应两个接口 avcodec_send_frame 和 avcodec_receive_packet,分辨是将数据包传入编码器和从编码器中得到已编码好的数据包,其中 while 循环的意义在于,由于I帧P帧B帧的存在,帧与帧之间需要做参考,所以一帧数据传入,并不一定会及时得到编码好的帧,同理,一帧数据的传入,如果之前编码器中的数据已经不具备参考意义,这时可能会输出多帧数据。
ret = avcodec_send_frame(enc_ctx, frame);
if (ret < 0) {
fprintf(stderr, "Error sending a frame for encoding
");
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
");
exit(1);
}
printf("Write packet %3"PRId64" (size=%5d)
", pkt->pts, pkt->size);
fwrite(pkt->data, 1, pkt->size, outfile);
av_packet_unref(pkt);
}
清理编码器对应编码输出,只是传入的数据包为NULL,在ffmpeg接口中有说明,传入NULL时,会将编码器中剩余的数据全部输出
......
* @param[in] frame
......
* It can be NULL, in which case it is considered a flush
* packet. This signals the end of the stream. If the encoder
* still has packets buffered, it will return them after this
* call. Once flushing mode has been entered, additional flush
* packets are ignored, and sending frames will return
* AVERROR_EOF.
......
int avcodec_send_frame(AVCodecContext *avctx, const AVFrame *frame);
其余诸如:打开文件、关闭文件、清理资源等不一一展开。