首先致敬雷神!这篇博客是基于雷神的《最简单的基于FFMPEG的视频编码器(YUV编码为H.264)》
编写的,自己研究了两天终于算是明白了雷神的这个例子。现在分析给大家,如果有不对的地方希望大家能
指正出来。
大家可以根据这个图中显示的object的关系来理解雷神的代码,下面说几处不太好理解的地方:
1)AVDictionary 结构体,这个结构体是用来设置ffmpeg中一些上下文的选项用的,key对应value,雷神的代码中有如下:
AVDictionary *param = 0;
//H.264
if(pCodecCtx->codec_id == AV_CODEC_ID_H264) {
av_dict_set(param, "preset", "slow", 0);
av_dict_set(param, "tune", "zerolatency", 0);
//av_dict_set(param, "profile", "main", 0);
}
接着下面用param设置:
if (avcodec_open2(pCodecCtx, pCodec,param) < 0){
printf("Failed to open encoder! \n");
return -1;
}
ffmpeg中采用H264,H265标准编码时,可能需要设置preset、tune和profile,ffmpeg中需要采用额外参数AVDictionary传入avcodec_open2()函数中实现。avcodec_open2是用于初始化AVCodecContext的。可以在《X264.h》文件中 找到如下声明:
static const char * const x264_tune_names[] = { "film", "animation",\
"grain", "stillimage", "psnr", "ssim", "fastdecode", "zerolatency", 0 };
static const char * const x264_preset_names[] = { "ultrafast", "superfast", "veryfast", "faster", \
"fast", "medium", "slow", "slower", "veryslow", "placebo", 0 };
2)雷神的代码中故意体现出了ffmpeg中结构体的关系。
int flush_encoder(AVFormatContext *fmt_ctx,unsigned int stream_index){
...
if (!(fmt_ctx->streams[stream_index]->codec->codec->capabilities &
CODEC_CAP_DELAY))
return 0;
}
贯穿整个程序中的AVFormatContext 结构体与相关编码器的对应关系,AVFormatContext ->AVStream **streams[index]->AVCodecContext *codec->const struct AVCodec *codec->。AVFormatContext 和AVCodecContext的对应关系是:AVFormatContext ->AVStream **streams[index]->AVCodecContext codec。
3)代码中有两处调用了avcodec_encode_video2,一处是在Main中,另外一处是在flush_encoder中。我把flush_encoder调用注释掉了之后得到的文件《dsbk.h264》和不注释得到的文件《ds.h264》进行比较,如图:
很显然注释掉的执行结果会比不注释的要小很多,紧接着对文件做了对比可以看到《ds.h264》结尾处比《dsbk.h264》多了很多数据,结合flush_encoder(冲刷编码)和对( capabilites & CODEC_CAP_DELAY )的理解得知,编码器在不断输入原始帧 输出编码包的过程中是有延时的,即不是立即得出输出包的,所以在原始帧输入编码器结束后,需要将编码器内剩余的数据冲刷出来。
结尾处想说点和本文无关的。首先雷神的精神值得我们所有人学习,我现在只是一个小白,连入门都不算。但每每想要放弃的时候都会想到雷神那种不求回报、无私奉献的精神,还有那颗对技术和知识热爱、痴迷的初心,每每想到这些我的灵魂都在震撼,我们生活在一个和平的年代和平的国家,对于我们每个工程师来讲可能都有着不错的生活条件,在这样的情况下可能还会被生活或者工作的琐事烦心,为了一时的平衡、待遇影响着所思所想,这样是不是很可笑,善待身边的人,也善待自己,不要忘记那颗因为技术知识而欢乐的初心。