本系列 以 ffmpeg4.2 源码为准,下载地址:链接:百度网盘 提取码:g3k8
ffmpeg 源码分析系列以一条简单的命令开始,ffmpeg -i a.mp4 b.flv
,分析其内部逻辑。
a.mp4下载链接:百度网盘,提取码:nl0s 。
本文主要分析 reap_filter()
的内部逻辑,流程图如下
在此贴上 reap_filter() 的代码图。红笔重点,绿笔不会执行或者不重要。
可以看到,reap_filters()
的逻辑比较简单。先 init_output_stream()
初始化 输出流,然后不断循环,调用 av_buffersink_get_frame_flags()
不断从 buffersink
那里读取 经过 filter 转换后的 frame,知道读不到就会跳出 while。随后就是 do_video_out()
跟 do_audio_out()
的调用。
do_video_out()
的逻辑其实是比较复杂的,里面貌似实现了一个帧率转换算法之类的东西。但是如图,绿色框出来的duration,应该是根据不用场景计算duration,不用特别关注,还有delta0
,delta
,nb0_frames
,nb_frames
,ost->last_nb0_frames
,这些变量的赋值跟计算,会搞得你头晕目眩。不利于初学者理解。 所以这里我直接指名,这些变量都不用特别关注,看一遍就行了,看不懂也没事,这些算法应该需要结合一个具体的帧率变换命令才能更好的解释。 只需要关注 nb_frames
,因为他在后面的for循环里面用了,而且nb_frames
在本命令下经常是1。
do_video_out()
里面还有个强制 I 帧算法,也不用特别关注,本命令没用到。因为代码量太大,这里只截图了很关键的段落。
重点已经用流程图,画笔画出来了。可以看到 do_video_out() 往编码器发一个 frame 就会不断地读取packet,直到读不到packet。也就是说,如果 buffersink
没有frame 出来了,编码器也就不会再有packet,缓存中的 packet 都不会有,因为上次已经循环读完了所有 pakcet,这个比较重要。所以不需要处理 avcodec_receive_packet()
的EOF。
还有一个 output_packet()
没有分析。本命令没有用 bitstream_filters ,所以 output_packet()
实际上是直接调用了 write_packet()。
write_packet() 的流程比较简单。主要的一点是写缓存 muxing_queue
,这里解释一下什么情况会写缓存。
输出文件的所有流,包括音频,视频流,全部初始化完成就会调用 avformat_write_header()
,然后设置 of->header_written
为 1 。
of->header_written 等于 0 就代表有输出流没有初始化,init_output_stream()
函数没有调用。什么情况会没有调用呢?
视频还没解码出任何一帧,导致 ifilter_send_frame()
没有执行,之前说过 ifilter_send_frame()
里面会调用 configure_filtergraph()
,configure_filtergraph()
没调用就会导致 ost->filter->graph->graph
没有初始化。 然后在reap_filter()函数内部 大概1437行,判断 ost->filter->graph->graph
时候执行 continue
了,导致没有执行 init_output_stream
,没有初始化视频流,所以没有调用 avformat_write_header()
写入 header。
视频还没解码出任何一帧,但是音频先解码出数据了,所以音频 packet 就会先写进去 缓存 muxing_queue
,等视频出数据了,写完头部了,再写到文件里面。
do_audio_out() 相对简单原理类似,这里不分析了。
©版权所属:知识星球:弦外之音,QQ:2338195090。
由于笔者的水平有限, 加之编写的同时还要参与开发工作,文中难免会出现一些错误或者不准确的地方,恳请读者批评指正。如果读者有任何宝贵意见,可以加我微信 Loken1。