From cb114ed464069ac83f05a4ebdfe992e8f1549815 Mon Sep 17 00:00:00 2001 From: Michael Niedermayer Date: Fri, 12 Aug 2016 21:28:08 +0200 Subject: [PATCH] avformat/mux: implement AVFMT_FLAG_SHORTEST This will allow fixing several bugs with the -shortest option Signed-off-by: Michael Niedermayer --- doc/formats.texi | 4 ++++ libavformat/avformat.h | 1 + libavformat/internal.h | 5 +++++ libavformat/mux.c | 39 +++++++++++++++++++++++++++++++++++++++ libavformat/options.c | 1 + libavformat/options_table.h | 1 + 6 files changed, 51 insertions(+) diff --git a/doc/formats.texi b/doc/formats.texi index f79ebe28ac..dda81e6f8d 100644 --- a/doc/formats.texi +++ b/doc/formats.texi @@ -61,6 +61,10 @@ Reduce the latency introduced by optional buffering Only write platform-, build- and time-independent data. This ensures that file and data checksums are reproducible and match between platforms. Its primary use is for regression testing. +@item shortest +Stop muxing at the end of the shortest stream. +It may be needed to increase max_interleave_delta to avoid flusing the longer +streams before EOF. @end table @item seek2any @var{integer} (@emph{input}) diff --git a/libavformat/avformat.h b/libavformat/avformat.h index 3ee705198c..74915a1482 100644 --- a/libavformat/avformat.h +++ b/libavformat/avformat.h @@ -1448,6 +1448,7 @@ typedef struct AVFormatContext { #define AVFMT_FLAG_PRIV_OPT 0x20000 ///< Enable use of private options by delaying codec open (this could be made default once all code is converted) #define AVFMT_FLAG_KEEP_SIDE_DATA 0x40000 ///< Don't merge side data but keep it separate. #define AVFMT_FLAG_FAST_SEEK 0x80000 ///< Enable fast, but inaccurate seeks for some formats +#define AVFMT_FLAG_SHORTEST 0x100000 ///< Stop muxing when the shortest stream stops. /** * Maximum size of the data read from input for determining diff --git a/libavformat/internal.h b/libavformat/internal.h index f9278c5b9a..49244faf23 100644 --- a/libavformat/internal.h +++ b/libavformat/internal.h @@ -125,6 +125,11 @@ struct AVFormatInternal { */ int header_written; int write_header_ret; + + /** + * Timestamp of the end of the shortest stream. + */ + int64_t shortest_end; }; struct AVStreamInternal { diff --git a/libavformat/mux.c b/libavformat/mux.c index a427f4659b..176af59f13 100644 --- a/libavformat/mux.c +++ b/libavformat/mux.c @@ -1032,6 +1032,7 @@ int ff_interleave_packet_per_dts(AVFormatContext *s, AVPacket *out, int stream_count = 0; int noninterleaved_count = 0; int i, ret; + int eof = flush; if (pkt) { if ((ret = ff_interleave_add_packet(s, pkt, interleave_compare_dts)) < 0) @@ -1084,6 +1085,44 @@ int ff_interleave_packet_per_dts(AVFormatContext *s, AVPacket *out, } } + if (s->internal->packet_buffer && + eof && + (s->flags & AVFMT_FLAG_SHORTEST) && + s->internal->shortest_end == AV_NOPTS_VALUE) { + AVPacket *top_pkt = &s->internal->packet_buffer->pkt; + + s->internal->shortest_end = av_rescale_q(top_pkt->dts, + s->streams[top_pkt->stream_index]->time_base, + AV_TIME_BASE_Q); + } + + if (s->internal->shortest_end != AV_NOPTS_VALUE) { + while (s->internal->packet_buffer) { + AVPacket *top_pkt = &s->internal->packet_buffer->pkt; + AVStream *st; + int64_t top_dts = av_rescale_q(top_pkt->dts, + s->streams[top_pkt->stream_index]->time_base, + AV_TIME_BASE_Q); + + if (s->internal->shortest_end + 1 >= top_dts) + break; + + pktl = s->internal->packet_buffer; + st = s->streams[pktl->pkt.stream_index]; + + s->internal->packet_buffer = pktl->next; + if (!s->internal->packet_buffer) + s->internal->packet_buffer_end = NULL; + + if (st->last_in_packet_buffer == pktl) + st->last_in_packet_buffer = NULL; + + av_packet_unref(&pktl->pkt); + av_freep(&pktl); + flush = 0; + } + } + if (stream_count && flush) { AVStream *st; pktl = s->internal->packet_buffer; diff --git a/libavformat/options.c b/libavformat/options.c index 04d9c454d3..25a506eef8 100644 --- a/libavformat/options.c +++ b/libavformat/options.c @@ -143,6 +143,7 @@ AVFormatContext *avformat_alloc_context(void) } ic->internal->offset = AV_NOPTS_VALUE; ic->internal->raw_packet_buffer_remaining_size = RAW_PACKET_BUFFER_SIZE; + ic->internal->shortest_end = AV_NOPTS_VALUE; return ic; } diff --git a/libavformat/options_table.h b/libavformat/options_table.h index 3b74d1b2fd..699809a84c 100644 --- a/libavformat/options_table.h +++ b/libavformat/options_table.h @@ -54,6 +54,7 @@ static const AVOption avformat_options[] = { {"nobuffer", "reduce the latency introduced by optional buffering", 0, AV_OPT_TYPE_CONST, {.i64 = AVFMT_FLAG_NOBUFFER }, 0, INT_MAX, D, "fflags"}, {"seek2any", "allow seeking to non-keyframes on demuxer level when supported", OFFSET(seek2any), AV_OPT_TYPE_BOOL, {.i64 = 0 }, 0, 1, D}, {"bitexact", "do not write random/volatile data", 0, AV_OPT_TYPE_CONST, { .i64 = AVFMT_FLAG_BITEXACT }, 0, 0, E, "fflags" }, +{"shortest", "stop muxing with the shortest stream", 0, AV_OPT_TYPE_CONST, { .i64 = AVFMT_FLAG_SHORTEST }, 0, 0, E, "fflags" }, {"analyzeduration", "specify how many microseconds are analyzed to probe the input", OFFSET(max_analyze_duration), AV_OPT_TYPE_INT64, {.i64 = 0 }, 0, INT64_MAX, D}, {"cryptokey", "decryption key", OFFSET(key), AV_OPT_TYPE_BINARY, {.dbl = 0}, 0, 0, D}, {"indexmem", "max memory used for timestamp index (per stream)", OFFSET(max_index_size), AV_OPT_TYPE_INT, {.i64 = 1<<20 }, 0, INT_MAX, D}, -- 2.11.0