On Thu, 4 May 2017 23:46:30 -0700 Aaron Levinson <alevi...@aracnet.com> wrote:
> On 4/12/2017 6:08 PM, Aaron Levinson wrote: > > On 3/26/2017 10:34 AM, Aaron Levinson wrote: > >> On 3/26/2017 4:41 AM, Matthias Hunstock wrote: > >>> Am 26.03.2017 um 11:50 schrieb Aaron Levinson: > >>>> When using the following command to play back either file: > >>>> ffmpeg -i <mpegts file> -f decklink -pix_fmt uyvy422 "DeckLink SDI > >>>> 4K", I noticed that with the mpegts file with the AAC audio stream, > >>>> it would correctly select an interlaced video mode for the video > >>>> output stream, but with the mpegts file with the Opus audio stream, > >>>> it would use a progressive video mode (1080p29.97) for the video > >>>> output stream. > >>> > >>> Which FFmpeg version did you test this with? > >>> > >>> There was a change related to this just short time ago. > >>> > >>> Does it happen with current git HEAD? > >>> > >>> Matthias > >> > >> This issue occurs with the current git HEAD. I'm aware of the > >> Blackmagic improvement that was added in February to add support for > >> interlaced video modes on output, and actually that's one of the reasons > >> why I'm using the latest git sources, as opposed to, say, 3.2.4. This > >> particular issue has nothing to do with Blackmagic, and I only used > >> Blackmagic in the example that reproduces the bug because it is > >> something that can be reproduced on both Windows and Linux (and > >> presumably also on OS/X). The issue also occurs if I do something like > >> -f rawvideo out.avi on Windows, and I'm sure that there are plenty of > >> other examples. > >> > >> Aaron Levinson > > > > Has anyone had a chance to review this patch yet, which I submitted on > > March 26th? To demonstrate the impact of this patch, here's some output of > > before and after for an .h264 file with interlaced 1080i59.94 video content: > > > > Command-line: ffmpeg -i test8_1080i.h264 -c:v mpeg2video test8_1080i_mp2.ts > > > > Before patch: > > > > -------------------------------------- > > > > 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 > > Side data: > > cpb: bitrate max/min/avg: 0/0/200000 buffer size: 0 vbv_delay: -1 > > > > -------------------------------------- > > > > After patch: > > > > -------------------------------------- > > > > 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(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 > > Metadata: > > encoder : Lavc57.92.100 mpeg2video > > Side data: > > cpb: bitrate max/min/avg: 0/0/200000 buffer size: 0 vbv_delay: -1 > > > > -------------------------------------- > > > > As can be seen, before the patch, after decoding the .h264 file and then > > re-encoding it as mpeg2video in an mpegts container, the interlaced aspect > > of the video has been lost in the output, and it is now effectively > > 1080p29.97, although the video hasn't actually been converted to > > progressive. ffmpeg simply thinks that the video is progressive when it is > > not. With the patch, the interlaced aspect is not lost and propagates to > > the output. So, this conclusively demonstrates that the issue has nothing > > to do with Blackmagic and is a more general issue with interlaced video and > > decoding. > > > > I can make the input file available if that would be helpful. > > > > Anyway, it would be great if this bug fix could make it into ffmpeg. > > > > Thanks, > > Aaron Levinson > > I've provided a new version of the patch. When I created the first version > of the patch on March 26th, this was the first patch that I submitted to > ffmpeg, and some aspects were rough. I had indicated that the patch passed > regression tests, but all I did was run "make fate", instead of "make fate > SAMPLES=fate-suite/", and once I understood that I should use fate-suite, I > discovered that some of the FATE tests failed with this patch. I fixed the > issues with the patch, adjusted some comments, and adjusted the patch > description text. I've confirmed that this patch passes all fate-suite tests > for 64-bit MSVC on Windows and 64-bit gcc on Linux. > > Thanks, > Aaron Levinson > > ------------------------------------------------------------------------ > > From 85aa383f5753795652bae015f4fe91b016f7387e Mon Sep 17 00:00:00 2001 > From: Aaron Levinson <alevi...@aracnet.com> > Date: Thu, 4 May 2017 22:54:31 -0700 > Subject: [PATCH] ffmpeg: Fixed bug with decoding interlaced video > > Purpose: Fixed bug in ffmpeg encountered when decoding interlaced > video. In some cases, ffmpeg would treat it as progressive. As a > result of the change, the issues with interlaced video are fixed. > > 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 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 in turn calls 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. > > Comments: > > -- ffmpeg.c: > a) Enhanced init_output_stream() to take an additional input > indicating whether or not check_init_output_file() should be > called. There are other places that call init_output_stream(), and > it was important to make sure that these continued to work > properly. > b) Adjusted reap_filters() such that, in the case that > init_output_stream() is called, it indicates that it should not > call check_init_output_file() in init_output_stream(). Instead, it > makes the call to check_init_output_file() directly, but after it > does the filtered frame setup and processing. > > Signed-off-by: Aaron Levinson <alevi...@aracnet.com> > --- > ffmpeg.c | 61 +++++++++++++++++++++++++++++++++++++++++++++++++++++-------- > 1 file changed, 53 insertions(+), 8 deletions(-) > > diff --git a/ffmpeg.c b/ffmpeg.c > index e798d92277..45dbfafc04 100644 > --- a/ffmpeg.c > +++ b/ffmpeg.c > @@ -1400,7 +1400,7 @@ static void do_video_stats(OutputStream *ost, int > frame_size) > } > } > > -static int init_output_stream(OutputStream *ost, char *error, int error_len); > +static int init_output_stream(OutputStream *ost, char *error, int error_len, > int do_check_output_file); > > static void finish_output_stream(OutputStream *ost) > { > @@ -1415,6 +1415,8 @@ static void finish_output_stream(OutputStream *ost) > } > } > > +static int check_init_output_file(OutputFile *of, int file_index); > + > /** > * Get and encode new output from any of the filtergraphs, without causing > * activity. > @@ -1433,6 +1435,7 @@ static int reap_filters(int flush) > AVFilterContext *filter; > AVCodecContext *enc = ost->enc_ctx; > int ret = 0; > + int did_init_output_stream = 0; > > if (!ost->filter || !ost->filter->graph->graph) > continue; > @@ -1440,12 +1443,14 @@ static int reap_filters(int flush) > > if (!ost->initialized) { > char error[1024] = ""; > - ret = init_output_stream(ost, error, sizeof(error)); > + ret = init_output_stream(ost, error, sizeof(error), 0); > if (ret < 0) { > av_log(NULL, AV_LOG_ERROR, "Error initializing output stream > %d:%d -- %s\n", > ost->file_index, ost->index, error); > exit_program(1); > } > + > + did_init_output_stream = 1; > } > > if (!ost->filtered_frame && !(ost->filtered_frame = > av_frame_alloc())) { > @@ -1521,6 +1526,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 (did_init_output_stream) { > + ret = check_init_output_file(of, ost->file_index); > + if (ret < 0) > + return ret; > + did_init_output_stream = 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 (did_init_output_stream) { > + ret = check_init_output_file(of, ost->file_index); > + if (ret < 0) > + return ret; > } > } > > @@ -1888,7 +1931,7 @@ static void flush_encoders(void) > finish_output_stream(ost); > } > > - ret = init_output_stream(ost, error, sizeof(error)); > + ret = init_output_stream(ost, error, sizeof(error), 1); > if (ret < 0) { > av_log(NULL, AV_LOG_ERROR, "Error initializing output stream > %d:%d -- %s\n", > ost->file_index, ost->index, error); > @@ -3396,7 +3439,7 @@ static int init_output_stream_encode(OutputStream *ost) > return 0; > } > > -static int init_output_stream(OutputStream *ost, char *error, int error_len) > +static int init_output_stream(OutputStream *ost, char *error, int error_len, > int do_check_output_file) > { > int ret = 0; > > @@ -3564,9 +3607,11 @@ static int init_output_stream(OutputStream *ost, char > *error, int error_len) > > ost->initialized = 1; > > - ret = check_init_output_file(output_files[ost->file_index], > ost->file_index); > - if (ret < 0) > - return ret; > + if (do_check_output_file) { > + ret = check_init_output_file(output_files[ost->file_index], > ost->file_index); > + if (ret < 0) > + return ret; > + } > > return ret; > } > @@ -3633,7 +3678,7 @@ static int transcode_init(void) > if (output_streams[i]->filter) > continue; > > - ret = init_output_stream(output_streams[i], error, sizeof(error)); > + ret = init_output_stream(output_streams[i], error, sizeof(error), 1); > if (ret < 0) > goto dump_format; > } As I said on IRC, I'm skeptic against this, but I'm also not sure whether I understand the situation. First, your change seems a bit fragile, and almost relies on coincidences. This could easily break in the future. (And if we add a FATE test, it would probably hit random people trying to change ffmpeg.c, and would cause them more work.) Looking at the current do_video_out() function (which btw. is way too long and also has broken indentation), I see the following line: mux_par->field_order = AV_FIELD_PROGRESSIVE; (and similar assignments to field_order to set interlaced modes) This is apparently run on every frame. This looks pretty wrong to me. It should _never_ change the codecpar after headers were written. This is simply invalid API use. If this is really a parameter that can change per frame, and that the _muxer_ needs this information (per packet I suppose), a side data signaling the field order should be added. mpegtsenc.c doesn't seem to read field_order at all, though. So I remain confused. _______________________________________________ ffmpeg-devel mailing list ffmpeg-devel@ffmpeg.org http://ffmpeg.org/mailman/listinfo/ffmpeg-devel