[FFmpeg-devel] [PATCH 2/2] ffmpeg: Made execution of reap_filters() more deterministic with respect to when headers are written
wm4
nfxjfg at googlemail.com
Sat May 13 09:44:26 EEST 2017
On Fri, 12 May 2017 13:28:14 -0700
Aaron Levinson <alevinsn at aracnet.com> wrote:
> Purpose: Made execution of reap_filters() more deterministic with
> respect to when headers are written in relationship with the
> initialization of output streams and the processing of input audio
> and/or video frames. This change fixes a bug in ffmpeg encountered
> when decoding interlaced video. In some cases, ffmpeg would treat it
> as progressive.
>
> Detailed description of problem: Run the following command: "ffmpeg -i
> test8_1080i.h264 -c:v mpeg2video test8_1080i_mp2.ts". In this case,
> test8_1080i.h264 is an H.264-encoded file with 1080i59.94
> (interlaced). Prior to the patch, the following output is generated:
>
> Input #0, h264, from 'test8_1080i.h264':
> Duration: N/A, bitrate: N/A
> Stream #0:0: Video: h264 (High), yuv420p(top first), 1920x1080 [SAR 1:1 DAR 16:9], 29.97 fps, 29.97 tbr, 1200k tbn, 59.94 tbc
> Stream mapping:
> Stream #0:0 -> #0:0 (h264 (native) -> mpeg2video (native))
> Press [q] to stop, [?] for help
> Output #0, mpegts, to 'test8_1080i_mp2_2.ts':
> Metadata:
> encoder : Lavf57.72.100
> Stream #0:0: Video: mpeg2video (Main), yuv420p, 1920x1080 [SAR 1:1 DAR 16:9], q=2-31, 200 kb/s, 29.97 fps, 90k tbn, 29.97 tbc
> Metadata:
> encoder : Lavc57.92.100 mpeg2video
>
> which demonstrates the bug. The output stream should instead look like:
>
> Stream #0:0: Video: mpeg2video (Main), yuv420p(top coded first (swapped)), 1920x1080 [SAR 1:1 DAR 16:9], q=2-31, 200 kb/s, 29.97 fps, 90k tbn, 29.97 tbc
>
> The bug is caused by the fact that reap_filters() calls
> init_output_stream(), which is then immediately followed by a call to
> check_init_output_file(), and this is all done prior to the first call
> to do_video_out(). An initial call to do_video_out() is necessary to
> populate the interlaced video information to the output stream's
> codecpar (mux_par->field_order in do_video_out()). However,
> check_init_output_file() calls avformat_write_header() prior to the
> initial call to do_video_out(), so field_order is populated too late,
> and the header is written with the default field_order value,
> progressive.
>
> Signed-off-by: Aaron Levinson <alevinsn at aracnet.com>
> ---
> ffmpeg.c | 43 ++++++++++++++++++++++++++++++++++++++++---
> 1 file changed, 40 insertions(+), 3 deletions(-)
>
> diff --git a/ffmpeg.c b/ffmpeg.c
> index 3cd45ba665..7b044b068c 100644
> --- a/ffmpeg.c
> +++ b/ffmpeg.c
> @@ -1434,6 +1434,7 @@ static int reap_filters(int flush)
> AVFilterContext *filter;
> AVCodecContext *enc = ost->enc_ctx;
> int ret = 0;
> + int do_check_init_output_file = 0;
>
> if (!ost->filter || !ost->filter->graph->graph)
> continue;
> @@ -1448,9 +1449,7 @@ static int reap_filters(int flush)
> exit_program(1);
> }
>
> - ret = check_init_output_file(of, ost->file_index);
> - if (ret < 0)
> - exit_program(1);
> + do_check_init_output_file = 1;
> }
>
> if (!ost->filtered_frame && !(ost->filtered_frame = av_frame_alloc())) {
> @@ -1526,6 +1525,44 @@ static int reap_filters(int flush)
> }
>
> av_frame_unref(filtered_frame);
> +
> + /*
> + * It is important to call check_init_output_file() here, after
> + * do_video_out() was called, instead of in init_output_stream(),
> + * as was done previously.
> + * If called from init_output_stream(), it is possible that not
> + * everything will have been fully initialized by the time that
> + * check_init_output_file() is called, and if
> + * check_init_output_file() determines that all output streams
> + * have been initialized, it will write the header. An example
> + * of initialization that depends on do_video_out() being called
> + * at least once is the specification of interlaced video, which
> + * happens in do_video_out(). This is particularly relevant when
> + * decoding--without processing a video frame, the interlaced
> + * video setting is not propagated before the header is written,
> + * and that causes problems.
> + * TODO: should probably handle interlaced video differently
> + * and not depend on it being setup in do_video_out(). Another
> + * solution would be to set it up once by examining the input
> + * header.
> + */
> + if (do_check_init_output_file) {
> + ret = check_init_output_file(of, ost->file_index);
> + if (ret < 0)
> + exit_program(1);
> + do_check_init_output_file = 0;
> + }
> + }
> +
> + /*
> + * Can't wait too long to call check_init_output_file().
> + * Otherwise, bad things start to occur.
> + * If didn't do it earlier, do it by the time it gets here.
> + */
> + if (do_check_init_output_file) {
> + ret = check_init_output_file(of, ost->file_index);
> + if (ret < 0)
> + exit_program(1);
> }
> }
>
Doesn't look good to me. The big comment compared with the small
amount of code is indicative that this approach is way too complex and
fragile.
More information about the ffmpeg-devel
mailing list