[FFmpeg-devel] [PATCH v4 1/1] avformat/mpegts: Add duration_probesize AVOption

Nicolas Gaullier nicolas.gaullier at cji.paris
Tue Mar 26 15:18:31 EET 2024


Yet another probesize option aimed at users interested
in better durations probing for itself, or because using
avformat_find_stream_info indirectly and requiring exact values: for
concatdec for example, especially if streamcopying above it.
The current code is a performance trade-off that can fail to get video
stream durations in a scenario with high bitrates and buffering for
files ending cleanly (as opposed to live captures): the physical gap
between the last video packet and the last audio packet is very high in
such a case.

Default behaviour is unchanged: 250k up to 250k << 6 (step by step).
Setting this new option has two effects:
- override the maximum probesize (currently 250k << 6)
- reduce the number of steps to 1 instead of 6, this is to avoid
detecting the audio "too early" and failing to reach a video packet.
Even if a single audio stream duration is found but not the other
audio/video stream durations, there will be a retry, so at the end the
full user-overriden probesize will be used as expected by the user.

Signed-off-by: Nicolas Gaullier <nicolas.gaullier at cji.paris>
---
 doc/demuxers.texi     |  13 +++++
 doc/formats.texi      |   2 +-
 libavformat/demux.c   |  23 ++++++---
 libavformat/mpegts.c  | 104 +---------------------------------------
 libavformat/mpegts.h  | 108 +++++++++++++++++++++++++++++++++++++++++-
 libavformat/version.h |   2 +-
 6 files changed, 140 insertions(+), 112 deletions(-)

diff --git a/doc/demuxers.texi b/doc/demuxers.texi
index b70f3a38d7..f88ae18a6e 100644
--- a/doc/demuxers.texi
+++ b/doc/demuxers.texi
@@ -992,6 +992,19 @@ streams move to different PIDs. Default value is 0.
 @item max_packet_size
 Set maximum size, in bytes, of packet emitted by the demuxer. Payloads above this size
 are split across multiple packets. Range is 1 to INT_MAX/2. Default is 204800 bytes.
+
+ at item duration_probesize
+Set probing size, in bytes, for input duration estimation which requires a specific probing
+for PTS at end of file.
+It is aimed at users interested in better durations probing for itself, or indirectly
+for specific use cases like using the concat demuxer.
+Files with high bitrates and ending cleanly (as opposed to live captures), can lead
+to a large physical gap between the last video packet and the last audio packet,
+so many bytes have to be read in order to get a video stream duration.
+Setting this value has a performance impact even for small files because the probing size is fixed.
+Default behaviour is a trade-off, largely adaptive: the probing size may range from
+250000 up to 16M, but it is not extended to get streams durations at all costs.
+Must be an integer not lesser than 1, or 0 for default behaviour.
 @end table
 
 @section mpjpeg
diff --git a/doc/formats.texi b/doc/formats.texi
index 69fc1457a4..e16cd88b2c 100644
--- a/doc/formats.texi
+++ b/doc/formats.texi
@@ -225,7 +225,7 @@ Specifies the maximum number of streams. This can be used to reject files that
 would require too many resources due to a large number of streams.
 
 @item skip_estimate_duration_from_pts @var{bool} (@emph{input})
-Skip estimation of input duration when calculated using PTS.
+Skip estimation of input duration if it requires an additional probing for pts at end of file.
 At present, applicable for MPEG-PS and MPEG-TS.
 
 @item strict, f_strict @var{integer} (@emph{input/output})
diff --git a/libavformat/demux.c b/libavformat/demux.c
index 147f3b93ac..bc23786410 100644
--- a/libavformat/demux.c
+++ b/libavformat/demux.c
@@ -47,6 +47,7 @@
 #include "id3v2.h"
 #include "internal.h"
 #include "url.h"
+#include "mpegts.h"
 
 static int64_t wrap_timestamp(const AVStream *st, int64_t timestamp)
 {
@@ -1803,20 +1804,30 @@ static void estimate_timings_from_bit_rate(AVFormatContext *ic)
                "Estimating duration from bitrate, this may be inaccurate\n");
 }
 
-#define DURATION_MAX_READ_SIZE 250000LL
-#define DURATION_MAX_RETRY 6
+#define DURATION_DEFAULT_MAX_READ_SIZE 250000LL
+#define DURATION_DEFAULT_MAX_RETRY 6
 
-/* only usable for MPEG-PS streams */
+/* only usable for MPEG-PS/TS streams */
 static void estimate_timings_from_pts(AVFormatContext *ic, int64_t old_offset)
 {
     FFFormatContext *const si = ffformatcontext(ic);
     AVPacket *const pkt = si->pkt;
     int num, den, read_size, ret;
+    int64_t duration_max_read_size = DURATION_DEFAULT_MAX_READ_SIZE;
+    int duration_max_retry = DURATION_DEFAULT_MAX_RETRY;
     int found_duration = 0;
     int is_end;
     int64_t filesize, offset, duration;
     int retry = 0;
 
+    if (!strcmp(ic->iformat->name, "mpegts")) {
+        MpegTSContext *ts = ic->priv_data;
+        if (ts->duration_probesize) {
+            duration_max_retry = 1;
+            duration_max_read_size = ts->duration_probesize >> duration_max_retry;
+        }
+    }
+
     /* flush packet queue */
     ff_flush_packet_queue(ic);
 
@@ -1847,7 +1858,7 @@ static void estimate_timings_from_pts(AVFormatContext *ic, int64_t old_offset)
     filesize = ic->pb ? avio_size(ic->pb) : 0;
     do {
         is_end = found_duration;
-        offset = filesize - (DURATION_MAX_READ_SIZE << retry);
+        offset = filesize - (duration_max_read_size << retry);
         if (offset < 0)
             offset = 0;
 
@@ -1856,7 +1867,7 @@ static void estimate_timings_from_pts(AVFormatContext *ic, int64_t old_offset)
         for (;;) {
             AVStream *st;
             FFStream *sti;
-            if (read_size >= DURATION_MAX_READ_SIZE << (FFMAX(retry - 1, 0)))
+            if (read_size >= duration_max_read_size << (FFMAX(retry - 1, 0)))
                 break;
 
             do {
@@ -1910,7 +1921,7 @@ static void estimate_timings_from_pts(AVFormatContext *ic, int64_t old_offset)
         }
     } while (!is_end &&
              offset &&
-             ++retry <= DURATION_MAX_RETRY);
+             ++retry <= duration_max_retry);
 
     av_opt_set_int(ic, "skip_changes", 0, AV_OPT_SEARCH_CHILDREN);
 
diff --git a/libavformat/mpegts.c b/libavformat/mpegts.c
index de7a3c8b45..dbe58cb57c 100644
--- a/libavformat/mpegts.c
+++ b/libavformat/mpegts.c
@@ -64,52 +64,6 @@
 #define PROBE_PACKET_MAX_BUF 8192
 #define PROBE_PACKET_MARGIN 5
 
-enum MpegTSFilterType {
-    MPEGTS_PES,
-    MPEGTS_SECTION,
-    MPEGTS_PCR,
-};
-
-typedef struct MpegTSFilter MpegTSFilter;
-
-typedef int PESCallback (MpegTSFilter *f, const uint8_t *buf, int len,
-                         int is_start, int64_t pos);
-
-typedef struct MpegTSPESFilter {
-    PESCallback *pes_cb;
-    void *opaque;
-} MpegTSPESFilter;
-
-typedef void SectionCallback (MpegTSFilter *f, const uint8_t *buf, int len);
-
-typedef void SetServiceCallback (void *opaque, int ret);
-
-typedef struct MpegTSSectionFilter {
-    int section_index;
-    int section_h_size;
-    int last_ver;
-    unsigned crc;
-    unsigned last_crc;
-    uint8_t *section_buf;
-    unsigned int check_crc : 1;
-    unsigned int end_of_section_reached : 1;
-    SectionCallback *section_cb;
-    void *opaque;
-} MpegTSSectionFilter;
-
-struct MpegTSFilter {
-    int pid;
-    int es_id;
-    int last_cc; /* last cc code (-1 if first packet) */
-    int64_t last_pcr;
-    int discard;
-    enum MpegTSFilterType type;
-    union {
-        MpegTSPESFilter pes_filter;
-        MpegTSSectionFilter section_filter;
-    } u;
-};
-
 struct Stream {
     int idx;
     int stream_identifier;
@@ -128,63 +82,6 @@ struct Program {
     int pmt_found;
 };
 
-struct MpegTSContext {
-    const AVClass *class;
-    /* user data */
-    AVFormatContext *stream;
-    /** raw packet size, including FEC if present */
-    int raw_packet_size;
-
-    int64_t pos47_full;
-
-    /** if true, all pids are analyzed to find streams */
-    int auto_guess;
-
-    /** compute exact PCR for each transport stream packet */
-    int mpeg2ts_compute_pcr;
-
-    /** fix dvb teletext pts                                 */
-    int fix_teletext_pts;
-
-    int64_t cur_pcr;    /**< used to estimate the exact PCR */
-    int64_t pcr_incr;   /**< used to estimate the exact PCR */
-
-    /* data needed to handle file based ts */
-    /** stop parsing loop */
-    int stop_parse;
-    /** packet containing Audio/Video data */
-    AVPacket *pkt;
-    /** to detect seek */
-    int64_t last_pos;
-
-    int skip_changes;
-    int skip_clear;
-    int skip_unknown_pmt;
-
-    int scan_all_pmts;
-
-    int resync_size;
-    int merge_pmt_versions;
-    int max_packet_size;
-
-    int id;
-
-    /******************************************/
-    /* private mpegts data */
-    /* scan context */
-    /** structure to keep track of Program->pids mapping */
-    unsigned int nb_prg;
-    struct Program *prg;
-
-    int8_t crc_validity[NB_PID_MAX];
-    /** filters for various streams specified by PMT + for the PAT and PMT */
-    MpegTSFilter *pids[NB_PID_MAX];
-    int current_pid;
-
-    AVStream *epg_stream;
-    AVBufferPool* pools[32];
-};
-
 #define MPEGTS_OPTIONS \
     { "resync_size",   "set size limit for looking up a new synchronization",  \
         offsetof(MpegTSContext, resync_size), AV_OPT_TYPE_INT,                 \
@@ -212,6 +109,7 @@ static const AVOption options[] = {
      {.i64 = 0}, 0, 1, 0 },
     {"max_packet_size", "maximum size of emitted packet", offsetof(MpegTSContext, max_packet_size), AV_OPT_TYPE_INT,
      {.i64 = 204800}, 1, INT_MAX/2, AV_OPT_FLAG_DECODING_PARAM },
+    {"duration_probesize", "maximum number of bytes to probe the durations of the streams", offsetof(MpegTSContext,duration_probesize), AV_OPT_TYPE_INT64, {.i64 = 0 }, 0, INT64_MAX, AV_OPT_FLAG_DECODING_PARAM},
     { NULL },
 };
 
diff --git a/libavformat/mpegts.h b/libavformat/mpegts.h
index 14ae312c50..8b31276bf1 100644
--- a/libavformat/mpegts.h
+++ b/libavformat/mpegts.h
@@ -165,7 +165,113 @@
 #define METADATA_DESCRIPTOR          0x26
 #define METADATA_STD_DESCRIPTOR      0x27
 
-typedef struct MpegTSContext MpegTSContext;
+enum MpegTSFilterType {
+    MPEGTS_PES,
+    MPEGTS_SECTION,
+    MPEGTS_PCR,
+};
+
+typedef struct MpegTSFilter MpegTSFilter;
+
+typedef int PESCallback (MpegTSFilter *f, const uint8_t *buf, int len,
+                         int is_start, int64_t pos);
+
+typedef struct MpegTSPESFilter {
+    PESCallback *pes_cb;
+    void *opaque;
+} MpegTSPESFilter;
+
+typedef void SectionCallback (MpegTSFilter *f, const uint8_t *buf, int len);
+
+typedef void SetServiceCallback (void *opaque, int ret);
+
+typedef struct MpegTSSectionFilter {
+    int section_index;
+    int section_h_size;
+    int last_ver;
+    unsigned crc;
+    unsigned last_crc;
+    uint8_t *section_buf;
+    unsigned int check_crc : 1;
+    unsigned int end_of_section_reached : 1;
+    SectionCallback *section_cb;
+    void *opaque;
+} MpegTSSectionFilter;
+
+struct MpegTSFilter {
+    int pid;
+    int es_id;
+    int last_cc; /* last cc code (-1 if first packet) */
+    int64_t last_pcr;
+    int discard;
+    enum MpegTSFilterType type;
+    union {
+        MpegTSPESFilter pes_filter;
+        MpegTSSectionFilter section_filter;
+    } u;
+};
+
+typedef struct MpegTSContext {
+    const AVClass *class;
+    /* user data */
+    AVFormatContext *stream;
+    /** raw packet size, including FEC if present */
+    int raw_packet_size;
+
+    int64_t pos47_full;
+
+    /** if true, all pids are analyzed to find streams */
+    int auto_guess;
+
+    /** compute exact PCR for each transport stream packet */
+    int mpeg2ts_compute_pcr;
+
+    /** fix dvb teletext pts                                 */
+    int fix_teletext_pts;
+
+    int64_t cur_pcr;    /**< used to estimate the exact PCR */
+    int64_t pcr_incr;   /**< used to estimate the exact PCR */
+
+    /* data needed to handle file based ts */
+    /** stop parsing loop */
+    int stop_parse;
+    /** packet containing Audio/Video data */
+    AVPacket *pkt;
+    /** to detect seek */
+    int64_t last_pos;
+
+    int skip_changes;
+    int skip_clear;
+    int skip_unknown_pmt;
+
+    int scan_all_pmts;
+
+    int resync_size;
+    int merge_pmt_versions;
+    int max_packet_size;
+
+    int id;
+
+    /* for external use by avformat_find_stream_info() */
+    /** Maximum number of bytes read from input in order to determine stream durations.
+        Can be set to 0 to let avformat choose using a heuristic. */
+    int64_t duration_probesize;
+
+    /******************************************/
+    /* private mpegts data */
+    /* scan context */
+    /** structure to keep track of Program->pids mapping */
+    unsigned int nb_prg;
+    struct Program *prg;
+
+    int8_t crc_validity[NB_PID_MAX];
+    /** filters for various streams specified by PMT + for the PAT and PMT */
+    MpegTSFilter *pids[NB_PID_MAX];
+    int current_pid;
+
+    AVStream *epg_stream;
+    AVBufferPool* pools[32];
+} MpegTSContext;
 
 MpegTSContext *avpriv_mpegts_parse_open(AVFormatContext *s);
 int avpriv_mpegts_parse_packet(MpegTSContext *ts, AVPacket *pkt,
diff --git a/libavformat/version.h b/libavformat/version.h
index 752aac16f7..a7c80dc564 100644
--- a/libavformat/version.h
+++ b/libavformat/version.h
@@ -31,7 +31,7 @@
 
 #include "version_major.h"
 
-#define LIBAVFORMAT_VERSION_MINOR   0
+#define LIBAVFORMAT_VERSION_MINOR   1
 #define LIBAVFORMAT_VERSION_MICRO 100
 
 #define LIBAVFORMAT_VERSION_INT AV_VERSION_INT(LIBAVFORMAT_VERSION_MAJOR, \
-- 
2.30.2



More information about the ffmpeg-devel mailing list