分析到这里,是时候画一个 transcode()
的整体流程图。
转码的流程已经分析完毕了,接下来主要讲解 EOF 的处理跟转码结束各种状态的变化。
EOF 分为以5种场景。
1,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
之后导致整个链路的反应。
2,avcodec_send_packet()
返回 AVERROR_EOF
,这个什么情况会返回 AVERROR_EOF
,就是第二次传递 pkt->size==0
就会返回 AVERROR_EOF,但是这个EOF不用关注,在ffmpeg 里面是直接跳过的,请看代码。
3,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。
这个链路太长,画个流程图便于理解
4,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()
这个函数做的。
5,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()
。
6,最后是 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。