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

/ 0评 / 0

本系列 以 ffmpeg4.2 源码为准,下载地址:链接:百度网盘 提取码:g3k8

ffmpeg 源码分析系列以一条简单的命令开始,ffmpeg -i a.mp4 b.flv,分析其内部逻辑。

a.mp4下载链接:百度网盘,提取码:nl0s 。


本文主要分析 transcode_step() 的内部逻辑,流程图如下

transcode_step() 的代码如下,为了简洁,有些不重要代码缩进了,还有一些无关的if,会在图中标注,重点代码会已经框出来。

如图所示,transcode_step() 主要逻辑有4点。

1,调用 choose_output() 在所有输出流中选出最合适的那个 OutputStreamchoose_output() 的内部实现是优选选择 没有初始化的 OutputStream,如果 OutputStream 都初始化了,就选当前 stream 时长最短的返回,最短时间通过 ost->st->cur_dts 来判断,choose_output() 比较简单,自行阅读代码可理解。

2,根据已经选择的 OutputStream 来确定 InputStream,确定 InputStream 这个逻辑比较绕,需要重点讲一下。图中可以看到,一共有3个 if 条件来设置 ist。这里我直接指明一下,第一第二个if 会跑进去,第三个if在当前命令不会跑进去。那第一第二个if什么时候会跑进去呢?

从代码上看,确定输入流 InputStream 有两个分支,解码器有无开始输出 AVFrame,输出了跑第一个if,没有开始输出继续跑第二个if。

从逻辑上看,确定输入流 InputStream的逻辑是,优先选择解码器还没输出frame的输入流,如果所有解码器都已经输出过 frame,就调用 transcode_from_filter()来确定选择哪个输入流来处理。

3,调用 process_input()process_input() 里面的逻辑这里简单介绍一下,就是从文件读出 AVPacket ,丢给解码器解码,如果解出 frame,就把frame丢给 filter 链。这里有个注意点需要提及一下,虽然以上逻辑已经选出了 InputStream,假如InputStream是video,但从文件读出来的 packet或许是audio。

4,调用 reap_filter()buffersink filter里面读取 frame,然后丢给编码器,然后mux,写进去文件。这里只是简单介绍 reap_filter() 的作用,后续会详细讲解。reap 的意思是收割的意思。



至此,transcode_step() 的内部逻辑分析了一大半了。下面画个流程图,流程图会把一些关联逻辑画出来,不是简单地把代码里的 if 翻译成中文,因为那样会让初学者感到很茫然,本文想表达的是,if 中的条件在什么样的场景下会是 true,什么样的场景下是false,便于读者理解整体逻辑,如果只是把 if 翻译成中文,不如直接看代码 。

可以看到 transcode_step() 的前半部分,主要功能就是确定 该选择哪个ist 输入流来处理。


接下来仔细分析里面的 transcode_from_filter() 。先上代码图。

如图所示, transcode_from_filter() 有两个地方需要注意,

  1. avfilter_graph_request_oldest() 这个api函数的作用是获取graph 里面有多少frame可以读,就是从 buffersink filter能读出多少 frame。这里的返回值,在当前命令 ffmpeg -i a.mp4 b.flv 下,大部分的返回值都是 AVERROR(EAGAIN),小于0的值。为什么这里ret大部分都小于0,是因为 之前已经执行过一遍 reap_filter() 把 filter 的 frame 收割完了。所以这里的 reap_filter()大部分情况不会执行。只有在后面刷 graph 的末点数据的时候才会有可能返回 ret 大于等于0。所以不用特别关注。
  2. 第二个框出的代码,av_buffersrc_get_nb_failed_requests() 获取 buffer filter 的失败次数,来确定 best_ist。这个的逻辑是这样的,如果之前已经请求了那么多次这个filter都没读出东西,肯定应该先选择这个filter关联的输入流来读取 pakcet,然后发给 filter,让下次 filter能尽快读出数据。因为filter已经读了很多次都读不出数据,所以应该优选处理这个 filter 关联的 InputStream

下一篇文章分析 process_input() 函数的内部逻辑


©版权所属:知识星球:弦外之音,QQ:2338195090。

由于笔者的水平有限, 加之编写的同时还要参与开发工作,文中难免会出现一些错误或者不准确的地方,恳请读者批评指正。如果读者有任何宝贵意见,可以加我微信 Loken1。

发表回复

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