本文已参与「新人创作礼」活动,一起开启掘金创作之路。
分析到这里,是时候画一个 transcode() 的整体流程图。
转码的流程已经分析完毕了,接下来主要讲解 EOF 的处理跟转码结束各种状态的变化。
EOF 分为以下5种场景。
-
av_read_frame()返回AVERROR_EOF,这个时候代表输入文件已经没有packet可以读取。就会导致 执行process_input_packet(ist, NULL, 0),可以看到,传递进去process_input_packet()的pkt是NULL。process_input_packet()的pkt是NULL就会导致 传递给decode_video()的avpkt.size == 0,就会再导致 传递 给avcodec_send_packet()的 pkt 的 size 也是0。可以看到一行英文注释 ,pkt->size==0 is a flush/drain packet。以上就是
av_read_frame()返回AVERROR_EOF之后导致整个链路的反应。 -
avcodec_send_packet()返回AVERROR_EOF,这个什么情况会返回AVERROR_EOF,就是第二次传递pkt->size==0就会返回 AVERROR_EOF,但是这个EOF不用关注,在ffmpeg 里面是直接跳过的,请看代码。
-
avcodec_receive_frame()返回AVERROR_EOF,这里返回 EOF,说明之前已经传递过 size==0 的 pkt 给avcodec_send_packet()。avcodec_receive_frame()返回AVERROR_EOF代表 解码器已经没有 frame 可以输出了。avcodec_receive_frame()返回AVERROR_EOF会导致decode()返回AVERROR_EOF,再导致 decode_video() 返回AVERROR_EOF。也就会导致调用decode_video()的process_input_packet()的eof_reached= 1 ,导致send_filter_eof()执行刷filter链,导致 process_input_packet() 返回!eof_reached也就是返回 0 。最后会导致process_input_packet(ist, NULL, 0)返回 0 ,注意这里process_input_packet已经开始传 NULL 了。接下来就会导致ifile->eof_reached = 1。再导致process_input()返回AVERROR(EAGAIN)。这样就会导致transcode_step()不执行reap_filters(),直接返回 0。这个链路太长,画个流程图便于理解
-
reap_filters()里面的 EOF,reap_filter()的EOF 其实就是av_buffersink_get_frame_flags(),这个EOF 其实不用特别关注,因为他不会导致ost->finished的状态变更。这里需要注意的是,av_buffersink_get_frame_flags()返回AVERROR_EOF并不会导致 传递 NULL pkt 给avcodec_send_frame()。传递 NULL pkt 刷新编码器,是flush_encoders()这个函数做的。 -
transcode_from_filter()里面的 EOF,也就是avfilter_graph_request_oldest()返回AVERROR_EOF,会导致close_output_stream()执行,然后导致ost->finished |= ENCODER_FINISHED,最后导致need_output()的时候退出转码流程。注意:
avfilter_graph_request_oldest()返回AVERROR_EOF是因为之前avcodec_receive_frame()返回AVERROR_EOF,导致调用了send_filter_eof()。
- 最后是
flush_encoders()的调用,flush_encoders()会调用avcodec_send_frame(enc, NULL),通过传递 NULL 把编码器剩下的packet全部刷出来。
可以看到 ost->finished 有两个状态ENCODER_FINISHED 跟 MUXER_FINISHED 。
ENCODER_FINISHED是在 读取 buffersink 返回 EOF 的时候设置的。MUXER_FINISHED在本文命令里并没有设置,也能正常退出,有点奇怪。
最后在精简一个重点,ost->finished 设置为 ENCODER_FINISHED 是在 解码器返回 EOF,后续没有frame输入了,也就是 avcodec_receive_frame() 返回 AVERROR_EOF 的场景下设置的。
因为没有 frame 输入了,就会调用send_filter_eof()里面的 av_buffersrc_close() 给filter链输入一个NULL,此时 filter链可能还有 frame 缓存,所以必须等 avfilter_graph_request_oldest() 返回 AVERROR_EOF,才能说明 filter 链已经没有 frame了,才能调 close_output_stream() 把 ost->finished 设置为 ENCODER_FINISHED 。
但是此时,编码器还没有发送NULL包去刷,所以后面还会执行 flush_encoder() 去发送NULL 去刷编码器
©版权所属:知识星球:弦外之音,QQ:2338195090。
由于笔者的水平有限, 加之编写的同时还要参与开发工作,文中难免会出现一些错误或者不准确的地方,恳请读者批评指正。如果读者有任何宝贵意见,可以加我微信 Loken1。