diff mbox series

[FFmpeg-devel,v2,1/1] avformat/demux: Add durationprobesize AVOption

Message ID 20240305120751.2025216-2-nicolas.gaullier@cji.paris
State New
Headers show
Series avformat/demux: Add durationprobesize AVOption | expand

Checks

Context Check Description
yinshiyou/commit_msg_loongarch64 warning Please wrap lines in the body of the commit message between 60 and 72 characters.
andriy/commit_msg_x86 warning Please wrap lines in the body of the commit message between 60 and 72 characters.
andriy/make_x86 success Make finished
andriy/make_fate_x86 success Make fate finished

Commit Message

Nicolas Gaullier March 5, 2024, 12:07 p.m. UTC
Yet another probesize used to get the durations when estimate_timings_from_pts
is required. It is 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@cji.paris>
---
 doc/APIchanges              |  3 +++
 doc/formats.texi            | 13 +++++++++++++
 libavformat/avformat.h      |  9 +++++++++
 libavformat/demux.c         | 13 ++++++++-----
 libavformat/options_table.h |  1 +
 5 files changed, 34 insertions(+), 5 deletions(-)

Comments

Anton Khirnov March 5, 2024, 2:59 p.m. UTC | #1
Quoting Nicolas Gaullier (2024-03-05 13:07:51)
> diff --git a/doc/APIchanges b/doc/APIchanges
> index 7d46ebb006..548c91effe 100644
> --- a/doc/APIchanges
> +++ b/doc/APIchanges
> @@ -2,6 +2,9 @@ The last version increases of all libraries were on 2023-02-09
>  
>  API changes, most recent first:
>  
> +2024-03-05 - xxxxxxxxxx - lavf 60.23.100 - options_table.h
> +  Add AVOption durationprobesize

This is wrong:
* options_table.h is not a public header, so it has no meaning for API
  users
* the main thing that should be mentioned is the AVFormatContext field,
  we usually do not mention avoptions here

> +
>  2024-02-28 - xxxxxxxxxx - swr   4.14.100 - swresample.h
>    swr_convert() now accepts arrays of const pointers (to input and output).
>  
> diff --git a/doc/formats.texi b/doc/formats.texi
> index 69fc1457a4..b9feef5d15 100644
> --- a/doc/formats.texi
> +++ b/doc/formats.texi
> @@ -228,6 +228,19 @@ would require too many resources due to a large number of streams.
>  Skip estimation of input duration when calculated using PTS.
>  At present, applicable for MPEG-PS and MPEG-TS.
>  
> +@item durationprobesize @var{integer} (@emph{input})
> +Set probing size in bytes for estimating durations when calculated using PTS.

I don't understand what 'calculated using PTS' is supposed to mean.

> diff --git a/libavformat/avformat.h b/libavformat/avformat.h
> index f4506f4cf1..7b714f3c65 100644
> --- a/libavformat/avformat.h
> +++ b/libavformat/avformat.h
> @@ -1994,6 +1994,15 @@ typedef struct AVFormatContext {
>       * Freed by libavformat in avformat_free_context().
>       */
>      AVStreamGroup **stream_groups;
> +
> +    /**
> +     * Maximum number of bytes read from input in order to determine stream durations
> +     * when using estimate_timings_from_pts in avformat_find_stream_info().
> +     *
> +     * - encoding: unused
> +     * - decoding: set by user
> +     */
> +    int64_t duration_probesize;

Presumably 0 means some special behaviour? The documentation should
say so.
diff mbox series

Patch

diff --git a/doc/APIchanges b/doc/APIchanges
index 7d46ebb006..548c91effe 100644
--- a/doc/APIchanges
+++ b/doc/APIchanges
@@ -2,6 +2,9 @@  The last version increases of all libraries were on 2023-02-09
 
 API changes, most recent first:
 
+2024-03-05 - xxxxxxxxxx - lavf 60.23.100 - options_table.h
+  Add AVOption durationprobesize
+
 2024-02-28 - xxxxxxxxxx - swr   4.14.100 - swresample.h
   swr_convert() now accepts arrays of const pointers (to input and output).
 
diff --git a/doc/formats.texi b/doc/formats.texi
index 69fc1457a4..b9feef5d15 100644
--- a/doc/formats.texi
+++ b/doc/formats.texi
@@ -228,6 +228,19 @@  would require too many resources due to a large number of streams.
 Skip estimation of input duration when calculated using PTS.
 At present, applicable for MPEG-PS and MPEG-TS.
 
+@item durationprobesize @var{integer} (@emph{input})
+Set probing size in bytes for estimating durations when calculated using PTS.
+At present, applicable for MPEG-PS and MPEG-TS.
+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
+250K 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.
+
 @item strict, f_strict @var{integer} (@emph{input/output})
 Specify how strictly to follow the standards. @code{f_strict} is deprecated and
 should be used only via the @command{ffmpeg} tool.
diff --git a/libavformat/avformat.h b/libavformat/avformat.h
index f4506f4cf1..7b714f3c65 100644
--- a/libavformat/avformat.h
+++ b/libavformat/avformat.h
@@ -1994,6 +1994,15 @@  typedef struct AVFormatContext {
      * Freed by libavformat in avformat_free_context().
      */
     AVStreamGroup **stream_groups;
+
+    /**
+     * Maximum number of bytes read from input in order to determine stream durations
+     * when using estimate_timings_from_pts in avformat_find_stream_info().
+     *
+     * - encoding: unused
+     * - decoding: set by user
+     */
+    int64_t duration_probesize;
 } AVFormatContext;
 
 /**
diff --git a/libavformat/demux.c b/libavformat/demux.c
index 2e1d0ed66d..4a570ca2ce 100644
--- a/libavformat/demux.c
+++ b/libavformat/demux.c
@@ -1836,8 +1836,9 @@  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_MAX_READ_SIZE_DEFAULT 250000LL
+#define DURATION_MAX_RETRY_DEFAULT 6
+#define DURATION_MAX_RETRY_USER 1
 
 /* only usable for MPEG-PS streams */
 static void estimate_timings_from_pts(AVFormatContext *ic, int64_t old_offset)
@@ -1845,6 +1846,8 @@  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 = ic->duration_probesize ? ic->duration_probesize >> DURATION_MAX_RETRY_USER : DURATION_MAX_READ_SIZE_DEFAULT;
+    int duration_max_retry = ic->duration_probesize ? DURATION_MAX_RETRY_USER : DURATION_MAX_RETRY_DEFAULT;
     int found_duration = 0;
     int is_end;
     int64_t filesize, offset, duration;
@@ -1880,7 +1883,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;
 
@@ -1889,7 +1892,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 {
@@ -1943,7 +1946,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/options_table.h b/libavformat/options_table.h
index b9dca147f9..0cc921a28d 100644
--- a/libavformat/options_table.h
+++ b/libavformat/options_table.h
@@ -108,6 +108,7 @@  static const AVOption avformat_options[] = {
 {"max_streams", "maximum number of streams", OFFSET(max_streams), AV_OPT_TYPE_INT, { .i64 = 1000 }, 0, INT_MAX, D },
 {"skip_estimate_duration_from_pts", "skip duration calculation in estimate_timings_from_pts", OFFSET(skip_estimate_duration_from_pts), AV_OPT_TYPE_BOOL, {.i64 = 0}, 0, 1, D},
 {"max_probe_packets", "Maximum number of packets to probe a codec", OFFSET(max_probe_packets), AV_OPT_TYPE_INT, { .i64 = 2500 }, 0, INT_MAX, D },
+{"durationprobesize", "maximum number of bytes to probe the durations of the streams in estimate_timings_from_pts", OFFSET(duration_probesize), AV_OPT_TYPE_INT64, {.i64 = 0 }, 0, INT64_MAX, D},
 {NULL},
 };