分析到这里,是时候画一个 transcode() 的整体流程图。

emby解码问题 emby 转码_linux


转码的流程已经分析完毕了,接下来主要讲解 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 里面是直接跳过的,请看代码。

emby解码问题 emby 转码_emby解码问题_02

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。

这个链路太长,画个流程图便于理解

emby解码问题 emby 转码_linux_03

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() 。

emby解码问题 emby 转码_docker_04

6,最后是 flush_encoders() 的调用,flush_encoders() 会调用 avcodec_send_frame(enc, NULL) ,通过传递 NULL 把编码器剩下的packet全部刷出来。

emby解码问题 emby 转码_容器_05


可以看到 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 去刷编码器