PR #21388 opened by James Almer (jamrial)
URL: https://code.ffmpeg.org/FFmpeg/FFmpeg/pulls/21388
Patch URL: https://code.ffmpeg.org/FFmpeg/FFmpeg/pulls/21388.patch

Many parsers will request data until they find what will be the start of the 
next assembled packet in order to decide where to cut the current one. If this 
happens, the loop in demux.c will, in case the demuxer exports already fully 
assembled packets as is sometimes the case for MPEG-TS, discard the already 
handled first input packet before it tries to move its side data to the output.

The affected FATE tests reflect this change by no longer dropping the side data 
from the first input packet, nor exporting every other side data in the wrong 
output packet.


>From 54490f8ea42b47fee3364fa99c2b9e72831b8070 Mon Sep 17 00:00:00 2001
From: James Almer <[email protected]>
Date: Mon, 5 Jan 2026 23:50:41 -0300
Subject: [PATCH 1/2] avformat/demux: use a stream specific temporary packet
 for the parser

This will be useful for the next commit.

Signed-off-by: James Almer <[email protected]>
---
 libavformat/avformat.c |  2 ++
 libavformat/demux.c    |  2 +-
 libavformat/internal.h | 15 +++++++++++++++
 libavformat/options.c  |  4 ++++
 4 files changed, 22 insertions(+), 1 deletion(-)

diff --git a/libavformat/avformat.c b/libavformat/avformat.c
index 18ca4643ee..ee3f7ee1b2 100644
--- a/libavformat/avformat.c
+++ b/libavformat/avformat.c
@@ -59,6 +59,8 @@ void ff_free_stream(AVStream **pst)
     av_freep(&sti->index_entries);
     av_freep(&sti->probe_data.buf);
 
+    av_packet_free(&sti->parse_pkt);
+
     av_bsf_free(&sti->extract_extradata.bsf);
 
     if (sti->info) {
diff --git a/libavformat/demux.c b/libavformat/demux.c
index b40739dc3a..89b947730b 100644
--- a/libavformat/demux.c
+++ b/libavformat/demux.c
@@ -1175,9 +1175,9 @@ static int parse_packet(AVFormatContext *s, AVPacket *pkt,
 {
     FormatContextInternal *const fci = ff_fc_internal(s);
     FFFormatContext *const si = &fci->fc;
-    AVPacket *out_pkt = si->parse_pkt;
     AVStream *st = s->streams[stream_index];
     FFStream *const sti = ffstream(st);
+    AVPacket *out_pkt = sti->parse_pkt;
     const AVPacketSideData *sd = NULL;
     const uint8_t *data = pkt->data;
     uint8_t *extradata = sti->avctx->extradata;
diff --git a/libavformat/internal.h b/libavformat/internal.h
index 245f6eeb86..64452cce6e 100644
--- a/libavformat/internal.h
+++ b/libavformat/internal.h
@@ -314,6 +314,21 @@ typedef struct FFStream {
     enum AVStreamParseType need_parsing;
     struct AVCodecParserContext *parser;
 
+    /**
+     * The generic code uses this as a temporary packet
+     * to parse packets or for muxing, especially flushing.
+     * For demuxers, it may also be used for other means
+     * for short periods that are guaranteed not to overlap
+     * with calls to av_read_frame() (or ff_read_packet())
+     * or with each other.
+     * It may be used by demuxers as a replacement for
+     * stack packets (unless they call one of the aforementioned
+     * functions with their own AVFormatContext).
+     * Every user has to ensure that this packet is blank
+     * after using it.
+     */
+    AVPacket *parse_pkt;
+
     /**
      * Number of frames that have been demuxed during 
avformat_find_stream_info()
      */
diff --git a/libavformat/options.c b/libavformat/options.c
index 7e4130b405..28aa2da942 100644
--- a/libavformat/options.c
+++ b/libavformat/options.c
@@ -270,6 +270,10 @@ AVStream *avformat_new_stream(AVFormatContext *s, const 
AVCodec *c)
 
     sti->fmtctx = s;
 
+    sti->parse_pkt = av_packet_alloc();
+    if (!sti->parse_pkt)
+        goto fail;
+
     if (s->iformat) {
         sti->avctx = avcodec_alloc_context3(NULL);
         if (!sti->avctx)
-- 
2.49.1


>From 097a757f5f7a2b1349b583e8561e66fa554c3993 Mon Sep 17 00:00:00 2001
From: James Almer <[email protected]>
Date: Mon, 5 Jan 2026 23:52:04 -0300
Subject: [PATCH 2/2] avformat/demux: prevent the loss of packet side data when
 using an parser

Many parsers will request data until they find what will be the start of the
next assembled packet in order to decide where to cut the current one. If this
happens, the loop in demux.c will, in case the demuxer exports already fully
assembled packets as is sometimes the case for MPEG-TS, discard the already
handled first input packet before it tries to move its side data to the output.

The affected FATE tests reflect this change by no longer dropping the side data
from the first input packet, nor exporting every other side data in the wrong
output packet.

Signed-off-by: James Almer <[email protected]>
---
 libavformat/demux.c                           | 14 +++++++-------
 tests/ref/fate/concat-demuxer-simple2-lavf-ts |  6 +++---
 tests/ref/fate/segment-mp4-to-ts              |  2 +-
 tests/ref/fate/ts-demux                       |  2 +-
 tests/ref/fate/ts-small-demux                 |  2 +-
 5 files changed, 13 insertions(+), 13 deletions(-)

diff --git a/libavformat/demux.c b/libavformat/demux.c
index 89b947730b..72d23c3aa6 100644
--- a/libavformat/demux.c
+++ b/libavformat/demux.c
@@ -1226,6 +1226,13 @@ static int parse_packet(AVFormatContext *s, AVPacket 
*pkt,
 
         got_output = !!out_pkt->size;
 
+        if (pkt->side_data && !out_pkt->side_data) {
+            out_pkt->side_data       = pkt->side_data;
+            out_pkt->side_data_elems = pkt->side_data_elems;
+            pkt->side_data          = NULL;
+            pkt->side_data_elems    = 0;
+        }
+
         if (!out_pkt->size)
             continue;
 
@@ -1245,13 +1252,6 @@ static int parse_packet(AVFormatContext *s, AVPacket 
*pkt,
                 goto fail;
         }
 
-        if (pkt->side_data) {
-            out_pkt->side_data       = pkt->side_data;
-            out_pkt->side_data_elems = pkt->side_data_elems;
-            pkt->side_data          = NULL;
-            pkt->side_data_elems    = 0;
-        }
-
         /* set the duration */
         out_pkt->duration = (sti->parser->flags & PARSER_FLAG_COMPLETE_FRAMES) 
? pkt->duration : 0;
         if (st->codecpar->codec_type == AVMEDIA_TYPE_AUDIO) {
diff --git a/tests/ref/fate/concat-demuxer-simple2-lavf-ts 
b/tests/ref/fate/concat-demuxer-simple2-lavf-ts
index 620f1944ba..ab29edea88 100644
--- a/tests/ref/fate/concat-demuxer-simple2-lavf-ts
+++ b/tests/ref/fate/concat-demuxer-simple2-lavf-ts
@@ -61,7 +61,7 @@ 
audio|0|84637|0.940411|84637|0.940411|2351|0.026122|209|N/A|K__
 audio|0|86988|0.966533|86988|0.966533|2351|0.026122|209|N/A|K__
 audio|0|89339|0.992656|89339|0.992656|2351|0.026122|209|N/A|K__
 video|1|83782|0.930911|80182|0.890911|3600|0.040000|12678|347800|___|MPEGTS 
Stream ID|224
-video|1|87382|0.970911|83782|0.930911|3600|0.040000|24711|361336|K__
+video|1|87382|0.970911|83782|0.930911|3600|0.040000|24711|361336|K__|MPEGTS 
Stream ID|224
 video|1|91964|1.021822|88364|0.981822|3600|0.040000|24801|564|K__|MPEGTS 
Stream ID|224
 video|1|95564|1.061822|91964|1.021822|3600|0.040000|16429|25944|___|MPEGTS 
Stream ID|224
 video|1|99164|1.101822|95564|1.061822|3600|0.040000|14508|42864|___|MPEGTS 
Stream ID|224
@@ -125,7 +125,7 @@ 
audio|0|175619|1.951322|175619|1.951322|2351|0.026122|209|N/A|K__
 audio|0|177970|1.977444|177970|1.977444|2351|0.026122|209|N/A|K__
 audio|0|180321|2.003567|180321|2.003567|2351|0.026122|209|N/A|K__
 video|1|174764|1.941822|171164|1.901822|3600|0.040000|12678|347800|___|MPEGTS 
Stream ID|224
-video|1|178364|1.981822|174764|1.941822|3600|0.040000|24711|361336|K__
+video|1|178364|1.981822|174764|1.941822|3600|0.040000|24711|361336|K__|MPEGTS 
Stream ID|224
 video|1|139582|1.550911|135982|1.510911|3600|0.040000|12692|311516|___|MPEGTS 
Stream ID|224
 video|1|143182|1.590911|139582|1.550911|3600|0.040000|10824|325052|___|MPEGTS 
Stream ID|224
 video|1|146782|1.630911|143182|1.590911|3600|0.040000|11286|336144|___|MPEGTS 
Stream ID|224
@@ -141,7 +141,7 @@ 
audio|0|151237|1.680411|151237|1.680411|2351|0.026122|209|N/A|K__
 audio|0|153588|1.706533|153588|1.706533|2351|0.026122|209|N/A|K__
 audio|0|155939|1.732656|155939|1.732656|2351|0.026122|209|N/A|K__
 video|1|150382|1.670911|146782|1.630911|3600|0.040000|12678|347800|___|MPEGTS 
Stream ID|224
-video|1|153982|1.710911|150382|1.670911|3600|0.040000|24711|361336|K__
+video|1|153982|1.710911|150382|1.670911|3600|0.040000|24711|361336|K__|MPEGTS 
Stream ID|224
 video|1|161182|1.790911|157582|1.750911|3600|0.040000|12135|155852|___|MPEGTS 
Stream ID|224
 video|1|164782|1.830911|161182|1.790911|3600|0.040000|12282|168448|___|MPEGTS 
Stream ID|224
 video|1|168382|1.870911|164782|1.830911|3600|0.040000|24786|181420|K__|MPEGTS 
Stream ID|224
diff --git a/tests/ref/fate/segment-mp4-to-ts b/tests/ref/fate/segment-mp4-to-ts
index 642d2a9fc9..9f3bdf4e1d 100644
--- a/tests/ref/fate/segment-mp4-to-ts
+++ b/tests/ref/fate/segment-mp4-to-ts
@@ -129,4 +129,4 @@
 0,     432000,     439200,     3600,      330, 0x150d9b60, F=0x0, S=1, MPEGTS 
Stream ID,        1, 0x00e000e0
 0,     435600,     446400,     3600,      324, 0x558194ee, F=0x0, S=1, MPEGTS 
Stream ID,        1, 0x00e000e0
 0,     439200,     442800,     3600,      191, 0x108e54d1, F=0x0, S=1, MPEGTS 
Stream ID,        1, 0x00e000e0
-0,     442800,     450000,     3600,      233, 0xac5b6486, F=0x0
+0,     442800,     450000,     3600,      233, 0xac5b6486, F=0x0, S=1, MPEGTS 
Stream ID,        1, 0x00e000e0
diff --git a/tests/ref/fate/ts-demux b/tests/ref/fate/ts-demux
index 0a76d6cdc0..5c4d6bca3d 100644
--- a/tests/ref/fate/ts-demux
+++ b/tests/ref/fate/ts-demux
@@ -20,7 +20,7 @@ 
packet|codec_type=video|stream_index=0|pts=3912687864|pts_time=43474.309600|dts=
 
packet|codec_type=video|stream_index=0|pts=3912684861|pts_time=43474.276233|dts=3912684861|dts_time=43474.276233|duration=1501|duration_time=0.016678|size=16296|pos=489176|flags=___|data_hash=CRC32:911b1649|side_datum/mpegts_stream_id:side_data_type=MPEGTS
 Stream ID|side_datum/mpegts_stream_id:id=224
 
packet|codec_type=audio|stream_index=1|pts=3912641945|pts_time=43473.799389|dts=3912641945|dts_time=43473.799389|duration=2880|duration_time=0.032000|size=1536|pos=N/A|flags=K_C|data_hash=CRC32:d2f2012f|side_datum/mpegts_stream_id:side_data_type=MPEGTS
 Stream ID|side_datum/mpegts_stream_id:id=189
 
packet|codec_type=audio|stream_index=2|pts=3912642700|pts_time=43473.807778|dts=3912642700|dts_time=43473.807778|duration=2880|duration_time=0.032000|size=768|pos=N/A|flags=K_C|data_hash=CRC32:3dad674a|side_datum/mpegts_stream_id:side_data_type=MPEGTS
 Stream ID|side_datum/mpegts_stream_id:id=189
-packet|codec_type=video|stream_index=0|pts=3912686363|pts_time=43474.292922|dts=3912686363|dts_time=43474.292922|duration=1501|duration_time=0.016678|size=4944|pos=506660|flags=___|data_hash=CRC32:54a86cbb
+packet|codec_type=video|stream_index=0|pts=3912686363|pts_time=43474.292922|dts=3912686363|dts_time=43474.292922|duration=1501|duration_time=0.016678|size=4944|pos=506660|flags=___|data_hash=CRC32:54a86cbb|side_datum/mpegts_stream_id:side_data_type=MPEGTS
 Stream ID|side_datum/mpegts_stream_id:id=224
 
packet|codec_type=audio|stream_index=1|pts=3912644825|pts_time=43473.831389|dts=3912644825|dts_time=43473.831389|duration=2880|duration_time=0.032000|size=906|pos=474888|flags=K__|data_hash=CRC32:0893d398
 
packet|codec_type=audio|stream_index=2|pts=3912645580|pts_time=43473.839778|dts=3912645580|dts_time=43473.839778|duration=2880|duration_time=0.032000|size=354|pos=491808|flags=K__|data_hash=CRC32:f5963fa6
 
stream|index=0|codec_name=mpeg2video|profile=4|codec_type=video|codec_tag_string=[2][0][0][0]|codec_tag=0x0002|width=1280|height=720|coded_width=0|coded_height=0|has_b_frames=1|sample_aspect_ratio=1:1|display_aspect_ratio=16:9|pix_fmt=yuv420p|level=4|color_range=tv|color_space=unknown|color_transfer=unknown|color_primaries=unknown|chroma_location=left|field_order=progressive|refs=1|ts_id=32776|ts_packetsize=188|id=0x31|r_frame_rate=60000/1001|avg_frame_rate=60000/1001|time_base=1/90000|start_pts=3912669846|start_time=43474.109400|duration_ts=19519|duration=0.216878|bit_rate=15000000|max_bit_rate=N/A|bits_per_raw_sample=N/A|nb_frames=N/A|nb_read_frames=N/A|nb_read_packets=15|extradata_size=150|extradata_hash=CRC32:53134fa8|disposition:default=0|disposition:dub=0|disposition:original=0|disposition:comment=0|disposition:lyrics=0|disposition:karaoke=0|disposition:forced=0|disposition:hearing_impaired=0|disposition:visual_impaired=0|disposition:clean_effects=0|disposition:attached_pic=0|
 
disposition:timed_thumbnails=0|disposition:non_diegetic=0|disposition:captions=0|disposition:descriptions=0|disposition:metadata=0|disposition:dependent=0|disposition:still_image=0|disposition:multilayer=0|side_datum/cpb_properties:side_data_type=CPB
 
properties|side_datum/cpb_properties:max_bitrate=15000000|side_datum/cpb_properties:min_bitrate=0|side_datum/cpb_properties:avg_bitrate=0|side_datum/cpb_properties:buffer_size=9781248|side_datum/cpb_properties:vbv_delay=-1
diff --git a/tests/ref/fate/ts-small-demux b/tests/ref/fate/ts-small-demux
index 7b4e54322d..596d3cb5c4 100644
--- a/tests/ref/fate/ts-small-demux
+++ b/tests/ref/fate/ts-small-demux
@@ -71,6 +71,6 @@ 
packet|codec_type=video|stream_index=0|pts=540000|pts_time=6.000000|dts=540000|d
 
packet|codec_type=video|stream_index=0|pts=546000|pts_time=6.066667|dts=546000|dts_time=6.066667|duration=6000|duration_time=0.066667|size=15|pos=15416|flags=___|data_hash=CRC32:391aa740|side_datum/mpegts_stream_id:side_data_type=MPEGTS
 Stream ID|side_datum/mpegts_stream_id:id=224
 
packet|codec_type=video|stream_index=0|pts=552000|pts_time=6.133333|dts=552000|dts_time=6.133333|duration=6000|duration_time=0.066667|size=16|pos=15604|flags=___|data_hash=CRC32:cca62b67|side_datum/mpegts_stream_id:side_data_type=MPEGTS
 Stream ID|side_datum/mpegts_stream_id:id=224
 
packet|codec_type=video|stream_index=0|pts=558000|pts_time=6.200000|dts=558000|dts_time=6.200000|duration=6000|duration_time=0.066667|size=16|pos=15792|flags=___|data_hash=CRC32:27b943ef|side_datum/mpegts_stream_id:side_data_type=MPEGTS
 Stream ID|side_datum/mpegts_stream_id:id=224
-packet|codec_type=video|stream_index=0|pts=564000|pts_time=6.266667|dts=564000|dts_time=6.266667|duration=6000|duration_time=0.066667|size=16|pos=16356|flags=___|data_hash=CRC32:f7116111
+packet|codec_type=video|stream_index=0|pts=564000|pts_time=6.266667|dts=564000|dts_time=6.266667|duration=6000|duration_time=0.066667|size=16|pos=16356|flags=___|data_hash=CRC32:f7116111|side_datum/mpegts_stream_id:side_data_type=MPEGTS
 Stream ID|side_datum/mpegts_stream_id:id=224
 
stream|index=0|codec_name=h264|profile=578|codec_type=video|codec_tag_string=[27][0][0][0]|codec_tag=0x001b|mime_codec_string=avc1.42c00a|width=82|height=144|coded_width=82|coded_height=144|has_b_frames=0|sample_aspect_ratio=1:1|display_aspect_ratio=41:72|pix_fmt=yuv420p|level=10|color_range=unknown|color_space=unknown|color_transfer=unknown|color_primaries=unknown|chroma_location=left|field_order=progressive|refs=1|is_avc=false|nal_length_size=0|ts_id=1|ts_packetsize=188|id=0x100|r_frame_rate=15/1|avg_frame_rate=15/1|time_base=1/90000|start_pts=126000|start_time=1.400000|duration_ts=444000|duration=4.933333|bit_rate=N/A|max_bit_rate=N/A|bits_per_raw_sample=8|nb_frames=N/A|nb_read_frames=N/A|nb_read_packets=74|extradata_size=35|extradata_hash=CRC32:e62cae27|disposition:default=0|disposition:dub=0|disposition:original=0|disposition:comment=0|disposition:lyrics=0|disposition:karaoke=0|disposition:forced=0|disposition:hearing_impaired=0|disposition:visual_impaired=0|disposition:clean_e
 
ffects=0|disposition:attached_pic=0|disposition:timed_thumbnails=0|disposition:non_diegetic=0|disposition:captions=0|disposition:descriptions=0|disposition:metadata=0|disposition:dependent=0|disposition:still_image=0|disposition:multilayer=0
 
format|filename=h264small.ts|nb_streams=1|nb_programs=1|nb_stream_groups=0|format_name=mpegts|start_time=1.400000|duration=4.933333|size=16544|bit_rate=26828|probe_score=50
-- 
2.49.1

_______________________________________________
ffmpeg-devel mailing list -- [email protected]
To unsubscribe send an email to [email protected]

Reply via email to