ffmpeg 源码分析-转码5

1,042 阅读1分钟

本文已参与「新人创作礼」活动,一起开启掘金创作之路。

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

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

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


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

ffmpeg_c-10-1.png

decode_video() 这个函数不仅仅要注意内部逻辑。函数返回值,函数传递的参数也相当有考究。下面贴一下 decode_video() 的形参与实参。

形参:decode_video(InputStream *ist, AVPacket *pkt, int *got_output, int64_t *duration_pts, int eof,int *decode_failed)
实参:decode_video(ist, repeating ? NULL : &avpkt, &got_output, &duration_pts, !pkt, &decode_failed)

返回值分析:

decode_video() 最后一行代码 是 return err < 0 ? err : ret; err 是 send_frame_to_filters() 的错误,ret 是解码返回的错误。跑到最后一行,ret肯定是 0。decode_video() 的返回值是 0 跟 非0 值,0代表没问题。

参数分析:

  1. decode_video() 第二个参数是 AVPacket *pkt,第二个参数其实有3种场景传值。

    • repeating 等于1,传NULL,传NULL主要控制 decode() 函数内部 不调用avcodec_send_packet(), 只调用avcodec_receive_frame() 来刷解码器frame。

    • 直接传 avpkt 变量,avpkt 实际上也有两种情况,avpkt.size 等于0 或者不等于 0。

      avpkt.size == 0 代表后续已经没有 正常pakcet 数据给解码器,是 flush 解码器。

      avpkt.size != 0 代表正常 pakcet 发送给解码器。

  2. got_output 代表解码器有没输出frame。有时候要输入多个 pakcet 才能解码出 frame


这里面最重要的就是 decode_video() , decode() 这两个函数的传参跟返回值,其他的代码都是一些参数赋值,数据统计,比较容易理解。

所以下面的流程图主要以 pkt 传参,repeating 分支为主。

ffmpeg_c-10-2.png

从流程图可以看出,在处理文件末尾的时候, process_input_packet() 的参数 pkt = null,但只执行了一次 avcodec_receive_frame() 就跳出while循环,没有设置 reapting 继续,所以解码器可能还有frame未读出来,所以可能会调用多次 process_input_packet() 传递 pkt = null,直到avcodec_receive_frame() 返回 EOF,才算读完全部的 frame。


decode_video() 的函数调用流程图如下,下面开始讲解 send_frame_to_filters() 函数的内部实现。

ffmpeg_c-10-3.png

send_frame_to_filters() 里面的代码,非常简单。就是往 InputStream 关联的多个 InputFilter 发送 frame,但是由于本文使用的命令 InputStream 没有多个 filter, 所以相当于直接执行 ifilter_send_frame()

ifilter_send_frame() 的内部逻辑也比较简单,通过对比 InputFilterAVFrame 的格式,采样率,宽高等参数,判断需不需要重新初始化 filtergraph。如果需要就执行 ifilter_parameters_from_frame() ,把 frame 的参数 赋值给 InputFilter。然后调用 configure_filtergraph() 初始出 filtergraph,没调用 ifilter_parameters_from_frame() 之前,InputFilter 大部分参数是空,所以 need_reinit 肯定是1。configure_filtergraph() 这个函数特别重要,就是在这里把 InputStream::filtersOutputStream::filter LINK 起来的。


下面仔细 分析 configure_filtergraph() 的内部逻辑。贴上代码图,重点代码用红笔圈出来了,没有圈出来的是不会执行或者是检测类型的代码。

ffmpeg_c-10-4.png

ffmpeg_c-10-5.png

流程图跟代码相对比较容易理解,主要是 avfilter_graph_parse2() 函数后面的两个变量 inputsoutputs。实际上这两个变量相当于一个filter头尾连接器。画个流程图便于理解。

ffmpeg_c-10-6.png

可以看到 configure_filtergraph()InputFilterOutputFilter 关联了在一起。

configure_input_filte() 的逻辑也相对简单,就是把 InputFilter::filter 初始化 为 buffer filter, 然后 Link 到 inputs[0]->filter_ctx

configure_output_filte()OutputFilter::filter 初始化 为 buffersink filter, 然后 Link 到 outputs[0]->filter_ctx


decode_audio() 的逻辑跟 decode_video() 类似,就不再分析了


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

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