本系列 以 ffmpeg4.2 源码为准,下载地址:链接:百度网盘 提取码:g3k8
ffmpeg 源码分析系列以一条简单的命令开始,ffmpeg -i a.mp4 b.flv
,分析其内部逻辑。
a.mp4下载链接:百度网盘,提取码:nl0s 。
本文主要分析 decode_video()
跟 decode_audio()
的内部逻辑,流程图如下:
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代表没问题。
参数分析:
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
发送给解码器。
- repeating 等于1,传NULL,传NULL主要控制
got_output
代表解码器有没输出frame。有时候要输入多个pakcet
才能解码出frame
。
这里面最重要的就是 decode_video()
, decode()
这两个函数的传参跟返回值,其他的代码都是一些参数赋值,数据统计,比较容易理解。
所以下面的流程图主要以 pkt
传参,repeating
分支为主。
从流程图可以看出,在处理文件末尾的时候, 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()
函数的内部实现。
send_frame_to_filters()
里面的代码,非常简单。就是往 InputStream
关联的多个 InputFilter
发送 frame
,但是由于本文使用的命令 InputStream
没有多个 filter, 所以相当于直接执行 ifilter_send_frame()
。
ifilter_send_frame()
的内部逻辑也比较简单,通过对比 InputFilter
跟 AVFrame
的格式,采样率,宽高等参数,判断需不需要重新初始化 filtergraph
。如果需要就执行 ifilter_parameters_from_frame()
,把 frame
的参数 赋值给 InputFilter
。然后调用 configure_filtergraph()
初始出 filtergraph,没调用 ifilter_parameters_from_frame()
之前,InputFilter
大部分参数是空,所以 need_reinit
肯定是1。configure_filtergraph()
这个函数特别重要,就是在这里把 InputStream::filters
跟 OutputStream::filter
LINK 起来的。
下面仔细 分析 configure_filtergraph()
的内部逻辑。贴上代码图,重点代码用红笔圈出来了,没有圈出来的是不会执行或者是检测类型的代码。
流程图跟代码相对比较容易理解,主要是 avfilter_graph_parse2()
函数后面的两个变量 inputs
,outputs
。实际上这两个变量相当于一个filter头尾连接器。画个流程图便于理解。
可以看到 configure_filtergraph()
把 InputFilter
跟 OutputFilter
关联了在一起。
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。