From patchwork Tue Mar 26 13:18:31 2024 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Nicolas Gaullier X-Patchwork-Id: 47491 Delivered-To: ffmpegpatchwork2@gmail.com Received: by 2002:a05:6a20:c889:b0:1a3:b6bb:3029 with SMTP id hb9csp121667pzb; Tue, 26 Mar 2024 06:18:51 -0700 (PDT) X-Forwarded-Encrypted: i=2; AJvYcCV9N+CyDs+CDeaQXMnFzVVI9bteManBeBTuq8ylddenihvHbVAHOcd43v3xhXaZpj/0wEBQftQniIz1FC2Kv5C0kKIJnQ80aopn0g== X-Google-Smtp-Source: AGHT+IG6lGD2VHNwmnGOnpswUO8ygcnex0RP+9L/atlMg3ku8mEmD0ng2DjGJmFq/B23qIGBpDvQ X-Received: by 2002:a50:d6dd:0:b0:568:a9f3:b3fb with SMTP id l29-20020a50d6dd000000b00568a9f3b3fbmr925877edj.8.1711459130724; Tue, 26 Mar 2024 06:18:50 -0700 (PDT) ARC-Seal: i=1; a=rsa-sha256; t=1711459130; cv=none; d=google.com; s=arc-20160816; b=SBreEVqxTOwMY6mrNvxPIyN/lBfVHHvsbpIorLKd5V3na2+311cDlrbuggLgZ618BT njY4bhsNCSWYsrOTG84MNdaFIpBPUpSOx2Nn8hrJO/oPfkB2gmjE2l+1V8vVdyhX2N3h UsBNwD4ucOAUJp1vGqAaCy7GzlR/yBR2beUtE0HFAq+Be6NntBCKsi0pg88OXN2d4+xr cdlEjecP4xWKhKPCIOQSiunSr/q+fiF6L4PI+8TZmwOwm6rcN2DbNT6sS5E89n00y6wg +0gRtRGEQlaIdhEt26KGXhKcCNqJUfKYB5dXL+XHk3sbdnMoGmdCnX4HCplyFUUh1be8 2S5A== ARC-Message-Signature: i=1; a=rsa-sha256; c=relaxed/relaxed; d=google.com; s=arc-20160816; h=sender:errors-to:content-transfer-encoding:cc:reply-to :list-subscribe:list-help:list-post:list-archive:list-unsubscribe :list-id:precedence:subject:mime-version:references:in-reply-to :message-id:date:to:from:delivered-to; bh=AMPvWXPHnDEHGquztQdfJmwUuKaUcHbgSrNICQERDTQ=; fh=zgMS+X4RExv2k7fU8QdWClnqVJdqO/9UbUuBS5xxJDs=; b=CkDIcAkjQ3HsM0uSqyt+n+Mjs+Tm/n07/7trqhN9uqr51Uqs6pUSaGkN9Uw4agruRS IPP+i8rRAD0BF8w0THgyKYLACdrmhNxjWXW+9cTHb5uhZ05+H+eGsCKDJfq+dLermjBg zO+g0sSQvV35e2LJByF9BD6GT8xcdbT14tzLY/UGxurDY2hkFIcJsIbpQNhDgFGIvGRz cv5ftf3zUtA96QLxrzELdjPQX29UeUTDmBN9yvuC7kjYber5IO6foLwJhXuMXEoWXGlR m4H/JF8V/9vHxfN4oIzhZi7VaPJ7VufcjV9ZqIIznYUJcDiiD0Ty1XY2G3uQXxkaZgTm WIFQ==; dara=google.com ARC-Authentication-Results: i=1; mx.google.com; spf=pass (google.com: domain of ffmpeg-devel-bounces@ffmpeg.org designates 79.124.17.100 as permitted sender) smtp.mailfrom=ffmpeg-devel-bounces@ffmpeg.org Return-Path: Received: from ffbox0-bg.mplayerhq.hu (ffbox0-bg.ffmpeg.org. [79.124.17.100]) by mx.google.com with ESMTP id m8-20020a056402510800b0056c05d08ec9si2706758edd.128.2024.03.26.06.18.50; Tue, 26 Mar 2024 06:18:50 -0700 (PDT) Received-SPF: pass (google.com: domain of ffmpeg-devel-bounces@ffmpeg.org designates 79.124.17.100 as permitted sender) client-ip=79.124.17.100; Authentication-Results: mx.google.com; spf=pass (google.com: domain of ffmpeg-devel-bounces@ffmpeg.org designates 79.124.17.100 as permitted sender) smtp.mailfrom=ffmpeg-devel-bounces@ffmpeg.org Received: from [127.0.1.1] (localhost [127.0.0.1]) by ffbox0-bg.mplayerhq.hu (Postfix) with ESMTP id 7746668D51D; Tue, 26 Mar 2024 15:18:39 +0200 (EET) X-Original-To: ffmpeg-devel@ffmpeg.org Delivered-To: ffmpeg-devel@ffmpeg.org Received: from srv-infra-2.infra.inf.glb.tvvideoms.com (www.inf.tvvideoms.com [213.205.126.156]) by ffbox0-bg.mplayerhq.hu (Postfix) with ESMTPS id 6C2A768D51D for ; Tue, 26 Mar 2024 15:18:36 +0200 (EET) Received: from cji.paris (unknown [172.16.3.159]) by srv-infra-2.infra.inf.glb.tvvideoms.com (Postfix) with ESMTP id 6CE2B4135A; Tue, 26 Mar 2024 13:18:35 +0000 (UTC) From: Nicolas Gaullier To: ffmpeg-devel@ffmpeg.org Date: Tue, 26 Mar 2024 14:18:31 +0100 Message-Id: <20240326131831.170184-2-nicolas.gaullier@cji.paris> X-Mailer: git-send-email 2.30.2 In-Reply-To: <20240326131831.170184-1-nicolas.gaullier@cji.paris> References: <20240326131831.170184-1-nicolas.gaullier@cji.paris> MIME-Version: 1.0 Subject: [FFmpeg-devel] [PATCH v4 1/1] avformat/mpegts: Add duration_probesize AVOption X-BeenThere: ffmpeg-devel@ffmpeg.org X-Mailman-Version: 2.1.29 Precedence: list List-Id: FFmpeg development discussions and patches List-Unsubscribe: , List-Archive: List-Post: List-Help: List-Subscribe: , Reply-To: FFmpeg development discussions and patches Cc: Nicolas Gaullier Errors-To: ffmpeg-devel-bounces@ffmpeg.org Sender: "ffmpeg-devel" X-TUID: tYYSnNLACIzP 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 --- 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. + +@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, \