ffmpeg源码分析-reap_filter - 弦外之音

/ 0评 / 0

本系列 以 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,不用特别关注,还有delta0deltanb0_framesnb_framesost->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。

发表回复

您的邮箱地址不会被公开。 必填项已用 * 标注