Opened 3 years ago

Closed 3 years ago

#2873 closed enhancement (fixed)

ffmpeg concat demuxer fails: mpeg files with different audio and video stream order

Reported by: kadmandux Owned by:
Priority: wish Component: avformat
Version: git-master Keywords: concat demuxer
Cc: Blocked By:
Blocking: Reproduced by developer: no
Analyzed by developer: no

Description

Summary of the bug:
I've 2 MPEG files with different order in audio and video stream, which I want to join:

ffprobe MU2000134b.mpg
Input #0, mpeg, from 'MU2000134b.mpg':
  Duration: 00:00:40.14, start: 0.500000, bitrate: 7713 kb/s
    Stream #0:0[0x1e0]: Video: mpeg2video (Main), yuv420p, 720x576 [SAR 64:45 DAR 16:9], max. 9100 kb/s, 25 fps, 25 tbr, 90k tbn, 50 tbc
    Stream #0:1[0x80]: Audio: ac3, 48000 Hz, stereo, fltp, 256 kb/s

ffprobe MU2000135b.mpg
Input #0, mpeg, from 'MU2000135b.mpg':
  Duration: 00:02:09.82, start: 0.500000, bitrate: 7583 kb/s
    Stream #0:0[0x80]: Audio: ac3, 48000 Hz, stereo, fltp, 256 kb/s
    Stream #0:1[0x1e0]: Video: mpeg2video (Main), yuv420p, 720x576 [SAR 64:45 DAR 16:9], max. 9100 kb/s, 25 fps, 25 tbr, 90k tbn, 50 tbc

How to reproduce:

% ffmpeg -f concat -i lista.txt -c:a copy -c:v copy video.mpg
ffmpeg version N-55393-g3b2e99f Copyright (c) 2000-2013 the FFmpeg developers
  built on Aug  8 2013 21:32:57 with gcc 4.7.3 (GCC)
  configuration: --enable-gpl --enable-version3 --disable-w32threads --enable-av
isynth --enable-bzlib --enable-fontconfig --enable-frei0r --enable-gnutls --enab
le-iconv --enable-libass --enable-libbluray --enable-libcaca --enable-libfreetyp
e --enable-libgsm --enable-libilbc --enable-libmodplug --enable-libmp3lame --ena
ble-libopencore-amrnb --enable-libopencore-amrwb --enable-libopenjpeg --enable-l
ibopus --enable-librtmp --enable-libschroedinger --enable-libsoxr --enable-libsp
eex --enable-libtheora --enable-libtwolame --enable-libvo-aacenc --enable-libvo-
amrwbenc --enable-libvorbis --enable-libvpx --enable-libx264 --enable-libxavs --
enable-libxvid --enable-zlib
  libavutil      52. 41.100 / 52. 41.100
  libavcodec     55. 24.100 / 55. 24.100
  libavformat    55. 13.102 / 55. 13.102
  libavdevice    55.  3.100 / 55.  3.100
  libavfilter     3. 82.100 /  3. 82.100
  libswscale      2.  4.100 /  2.  4.100
  libswresample   0. 17.103 /  0. 17.103
  libpostproc    52.  3.100 / 52.  3.100
[concat @ 026eb160] Estimating duration from bitrate, this may be inaccurate
Input #0, concat, from 'lista.txt':
  Duration: 00:00:00.00, start: 0.000000, bitrate: 256 kb/s
    Stream #0:0: Video: mpeg2video (Main), yuv420p, 720x576 [SAR 64:45 DAR 16:9]
, max. 9100 kb/s, 25 fps, 25 tbr, 90k tbn, 50 tbc
    Stream #0:1: Audio: ac3, 48000 Hz, stereo, fltp, 256 kb/s
Output #0, mpeg, to 'video.mpg':
  Metadata:
    encoder         : Lavf55.13.102
    Stream #0:0: Video: mpeg2video, yuv420p, 720x576 [SAR 64:45 DAR 16:9], q=2-3
1, max. 9100 kb/s, 25 fps, 90k tbn, 25 tbc
    Stream #0:1: Audio: ac3, 48000 Hz, stereo, 256 kb/s
Stream mapping:
  Stream #0:0 -> #0:0 (copy)
  Stream #0:1 -> #0:1 (copy)
Press [q] to stop, [?] for help
frame=  368 fps=0.0 q=-1.0 size=   14306kB time=00:00:14.90 bitrate=7863.3kbits/
frame=  718 fps=717 q=-1.0 size=   27334kB time=00:00:28.90 bitrate=7747.0kbits/
frame=  915 fps=608 q=-1.0 size=   34646kB time=00:00:36.78 bitrate=7715.9kbits/
[mpeg @ 03a9ba40] buffer underflow i=1 bufi=3763 size=41205
[mpeg @ 03a9ba40] packet too large, ignoring buffer limits to mux it
[mpeg @ 03a9ba40] buffer underflow i=1 bufi=3763 size=41205
[mpeg @ 03a9ba40] buffer underflow i=1 bufi=5788 size=41205
[mpeg @ 03a9ba40] packet too large, ignoring buffer limits to mux it
[mpeg @ 03a9ba40] buffer underflow i=1 bufi=5788 size=41205
[mpeg @ 03a9ba40] buffer underflow i=1 bufi=7825 size=41205
[mpeg @ 03a9ba40] packet too large, ignoring buffer limits to mux it
[mpeg @ 03a9ba40] buffer underflow i=1 bufi=7825 size=41205
[mpeg @ 03a9ba40] buffer underflow i=1 bufi=9862 size=41205
[mpeg @ 03a9ba40] packet too large, ignoring buffer limits to mux it
[mpeg @ 03a9ba40] buffer underflow i=1 bufi=9862 size=41205
[mpeg @ 03a9ba40] buffer underflow i=1 bufi=11899 size=41205

The problem doesn't appears if, before the join operation, you reorder the streams (with "-map" option) to have the same order in both files.

Change History (7)

comment:1 Changed 3 years ago by Cigaes

  • Component changed from FFmpeg to avformat
  • Status changed from new to open
  • Type changed from defect to enhancement
  • Version changed from unspecified to git-master

This is currently the expected behaviour (“All files must have the same streams” says the doc; similar streams in different order are not “the same”).
Patches to implement per-file stream mapping are welcome.

comment:2 Changed 3 years ago by kadmandux

It seems that there is no stream order concept in mpeg-ps:

http://www.ffmpeg.org/trac/ffmpeg/ticket/2876

So, I think that there should be at least a little automatic guessing when you use the concat demuxer with mpeg files, so that you can join files with same content and simple stream schemes, like the files I use (only 2 streams: 1 video + 1 audio).

comment:3 Changed 3 years ago by kadmandux

What do you think about this approach?

1.- Add two simple functions to utils.c for to sort the streams based on id:

int av_compare_streams(const void *st1, const void *st2)
{
	return ((((AVStream *)st1)->id > ((AVStream *)st2)->id) ? 1 : 
			(((AVStream *)st1)->id < ((AVStream *)st2)->id) ? -1 : 0);
}

void av_sort_streams_by_id(AVFormatContext *s)
	qsort((void *)s->streams, s->nb_streams, sizeof(s->streams[0]), av_compare_streams);
	/*Adjust the indexes */
	for (int i=0;i<s->nb_streams;i++) {
		s->streams[i]->index = i;
	}
}


2.- Add the prototypes to "avformat.h"

void av_sort_streams_by_id(AVFormatContext *s);
int av_compare_streams(const void *st1, const void *st2);

3.- Modify ffprobe.c, for to use the new function, from inside "open_input_file" function:

	/* Reorder streams if the format requires it (added by kadmandux)
	 * Is the flag AVFMTCTX_NOHEADER a good candidate to use for this purpose?
	 * Or should I define a new specific flag in "avformat.h"  */
	if (fmt_ctx->ctx_flags & AVFMTCTX_NOHEADER)
		av_sort_streams_by_id(fmt_ctx);

This code should be executed after the call to "avformat_find_stream_info".

4.- Do the equivalent change in ffmpeg_opt.c, also inside "open_input_file" function.

Last edited 3 years ago by kadmandux (previous) (diff)

comment:4 Changed 3 years ago by Cigaes

Changing the order of the streams would probably not be as simple as changing the order in the array, but it can probably be done.

Something making the order of streams from MPEG-PS less random would probably be welcome (although I may be missing some drawbacks). Please post patches on the mailing list.

You can make it simpler than you suggested, though: just change avformat_find_stream_info(): if format is MPEG-PS and there was no streams already detected, then reorder at the end.

Unfortunately, for your use, this is still too fragile, IMHO. Unless you have a constraint you did not talk about, you would be better off just acknowledging that MPEG-PS is a very bad format and using another, especially for intermediate files.

comment:5 Changed 3 years ago by kadmandux

I did some testing and the stream_id doesn't seem a good candidate to sort the streams ... EDITING: There were two problems with my first tests. Now it works!

In the short time, I'm going to try your suggestion: use a different container format, like MKV, to use as input to the join step. Thanks!

Last edited 3 years ago by kadmandux (previous) (diff)

comment:6 Changed 3 years ago by kadmandux

It works now!

There were two problems:

1) The function av_compare_streams had an error. It was expecting pointers to streams and it gets passed pointers to pointers to streams (we are sorting an array with pointers, not an array with streams). So, this is the correct code:

int av_compare_streams(const void *st1, const void *st2)
{
	return (((*(AVStream **)st1)->id > (*(AVStream **)st2)->id) ? 1 : 
			((*(AVStream **)st1)->id < (*(AVStream **)st2)->id) ? -1 : 0);

2) The funcion avformat_find_stream_info was invoked from more places than I fixed, so I've made the changes at the end of function avformat_find_stream_info

And now it works great!

Last edited 3 years ago by kadmandux (previous) (diff)

comment:7 Changed 3 years ago by cehoyos

  • Priority changed from normal to wish
  • Resolution set to fixed
  • Status changed from open to closed

I believe this was fixed by Nicolas in 26dea773

Note: See TracTickets for help on using tickets.