lavf: improve handling of sparse streams when muxing
authorLuca Barbato <lu_zero@gentoo.org>
Mon, 20 Jan 2014 12:28:37 +0000 (13:28 +0100)
committerAnton Khirnov <anton@khirnov.net>
Tue, 4 Feb 2014 21:01:42 +0000 (22:01 +0100)
Currently ff_interleave_packet_per_dts() waits until it gets a frame for
each stream before outputting packets in interleaved order.

Sparse streams (i.e. streams with much fewer packets than the other
streams, like subtitles or audio with DTX) tend to add up latency and in
specific cases end up allocating a large amount of memory.

Emit the top packet from the packet_buffer if it has a time delta
larger than a specified threshold.

Original report of the issue and initial proposed solution by
mus.svz@gmail.com.

Bug-id: 31
Signed-off-by: Anton Khirnov <anton@khirnov.net>
doc/APIchanges
libavformat/avformat.h
libavformat/mux.c
libavformat/options_table.h
libavformat/version.h

index e4bd6d8..6b9efd0 100644 (file)
@@ -13,6 +13,10 @@ libavutil:     2013-12-xx
 
 API changes, most recent first:
 
 
 API changes, most recent first:
 
+2014-02-xx - xxxxxxx - lavf 55.11.0 - avformat.h
+  Add AVFormatContext.max_interleave_delta for controlling amount of buffering
+  when interleaving.
+
 2014-01-xx - xxxxxxx - lavc 55.32.1 - avcodec.h
   Edges are not required anymore on video buffers allocated by get_buffer2()
   (i.e. as if the CODEC_FLAG_EMU_EDGE flag was always on). Deprecate
 2014-01-xx - xxxxxxx - lavc 55.32.1 - avcodec.h
   Edges are not required anymore on video buffers allocated by get_buffer2()
   (i.e. as if the CODEC_FLAG_EMU_EDGE flag was always on). Deprecate
index 00380d7..ab09efd 100644 (file)
@@ -1002,6 +1002,25 @@ typedef struct AVFormatContext {
      */
     int debug;
 #define FF_FDEBUG_TS        0x0001
      */
     int debug;
 #define FF_FDEBUG_TS        0x0001
+
+    /**
+     * Maximum buffering duration for interleaving.
+     *
+     * To ensure all the streams are interleaved correctly,
+     * av_interleaved_write_frame() will wait until it has at least one packet
+     * for each stream before actually writing any packets to the output file.
+     * When some streams are "sparse" (i.e. there are large gaps between
+     * successive packets), this can result in excessive buffering.
+     *
+     * This field specifies the maximum difference between the timestamps of the
+     * first and the last packet in the muxing queue, above which libavformat
+     * will output a packet regardless of whether it has queued a packet for all
+     * the streams.
+     *
+     * Muxing only, set by the caller before avformat_write_header().
+     */
+    int64_t max_interleave_delta;
+
     /*****************************************************************
      * All fields below this line are not part of the public API. They
      * may not be used outside of libavformat and can be changed and
     /*****************************************************************
      * All fields below this line are not part of the public API. They
      * may not be used outside of libavformat and can be changed and
index 06dacea..c16d1c8 100644 (file)
@@ -541,8 +541,39 @@ int ff_interleave_packet_per_dts(AVFormatContext *s, AVPacket *out,
         ff_interleave_add_packet(s, pkt, interleave_compare_dts);
     }
 
         ff_interleave_add_packet(s, pkt, interleave_compare_dts);
     }
 
-    for (i = 0; i < s->nb_streams; i++)
-        stream_count += !!s->streams[i]->last_in_packet_buffer;
+    if (s->max_interleave_delta > 0 && s->packet_buffer && !flush) {
+        AVPacket *top_pkt = &s->packet_buffer->pkt;
+        int64_t delta_dts = INT64_MIN;
+        int64_t top_dts = av_rescale_q(top_pkt->dts,
+                                       s->streams[top_pkt->stream_index]->time_base,
+                                       AV_TIME_BASE_Q);
+
+        for (i = 0; i < s->nb_streams; i++) {
+            int64_t last_dts;
+            const AVPacketList *last = s->streams[i]->last_in_packet_buffer;
+
+            if (!last)
+                continue;
+
+            last_dts = av_rescale_q(last->pkt.dts,
+                                    s->streams[i]->time_base,
+                                    AV_TIME_BASE_Q);
+            delta_dts = FFMAX(delta_dts, last_dts - top_dts);
+            stream_count++;
+        }
+
+        if (delta_dts > s->max_interleave_delta) {
+            av_log(s, AV_LOG_DEBUG,
+                   "Delay between the first packet and last packet in the "
+                   "muxing queue is %"PRId64" > %"PRId64": forcing output\n",
+                   delta_dts, s->max_interleave_delta);
+            flush = 1;
+        }
+    } else {
+        for (i = 0; i < s->nb_streams; i++)
+            stream_count += !!s->streams[i]->last_in_packet_buffer;
+    }
+
 
     if (stream_count && (s->internal->nb_interleaved_streams == stream_count || flush)) {
         pktl = s->packet_buffer;
 
     if (stream_count && (s->internal->nb_interleaved_streams == stream_count || flush)) {
         pktl = s->packet_buffer;
index 54bf25f..cc84e1c 100644 (file)
@@ -58,6 +58,7 @@ static const AVOption avformat_options[] = {
 {"bitstream", "detect bitstream specification deviations", 0, AV_OPT_TYPE_CONST, {.i64 = AV_EF_BITSTREAM }, INT_MIN, INT_MAX, D, "err_detect"},
 {"buffer", "detect improper bitstream length", 0, AV_OPT_TYPE_CONST, {.i64 = AV_EF_BUFFER }, INT_MIN, INT_MAX, D, "err_detect"},
 {"explode", "abort decoding on minor error detection", 0, AV_OPT_TYPE_CONST, {.i64 = AV_EF_EXPLODE }, INT_MIN, INT_MAX, D, "err_detect"},
 {"bitstream", "detect bitstream specification deviations", 0, AV_OPT_TYPE_CONST, {.i64 = AV_EF_BITSTREAM }, INT_MIN, INT_MAX, D, "err_detect"},
 {"buffer", "detect improper bitstream length", 0, AV_OPT_TYPE_CONST, {.i64 = AV_EF_BUFFER }, INT_MIN, INT_MAX, D, "err_detect"},
 {"explode", "abort decoding on minor error detection", 0, AV_OPT_TYPE_CONST, {.i64 = AV_EF_EXPLODE }, INT_MIN, INT_MAX, D, "err_detect"},
+{"max_interleave_delta", "maximum buffering duration for interleaving", OFFSET(max_interleave_delta), AV_OPT_TYPE_INT64, { .i64 = 10000000 }, 0, INT64_MAX, E },
 {NULL},
 };
 
 {NULL},
 };
 
index dbdccb4..3b96d0a 100644 (file)
@@ -30,8 +30,8 @@
 #include "libavutil/version.h"
 
 #define LIBAVFORMAT_VERSION_MAJOR 55
 #include "libavutil/version.h"
 
 #define LIBAVFORMAT_VERSION_MAJOR 55
-#define LIBAVFORMAT_VERSION_MINOR 10
-#define LIBAVFORMAT_VERSION_MICRO  3
+#define LIBAVFORMAT_VERSION_MINOR 11
+#define LIBAVFORMAT_VERSION_MICRO  0
 
 #define LIBAVFORMAT_VERSION_INT AV_VERSION_INT(LIBAVFORMAT_VERSION_MAJOR, \
                                                LIBAVFORMAT_VERSION_MINOR, \
 
 #define LIBAVFORMAT_VERSION_INT AV_VERSION_INT(LIBAVFORMAT_VERSION_MAJOR, \
                                                LIBAVFORMAT_VERSION_MINOR, \