PR #24751 opened by branko_s67 URL: https://code.ffmpeg.org/FFmpeg/FFmpeg/pulls/24751 Patch URL: https://code.ffmpeg.org/FFmpeg/FFmpeg/pulls/24751.patch
Some side data, like producer reference timestamp (PRFT), must be attached to a packet with the same time stamp as the side data's. But for some protocols, such as RTP, PRFT is calculated for one packet but attached to an earlier one. This is because parse_packet(), in libavformat/demux.c, transfers the current input packet’s side data to whichever parser output is first produced. This patch adds a mechanism where side data which must be attached to an out packet with a matching timestamp is queued until the correct out packet arrives and then attaches the side data to it. Currently only PRFT side data is queued. Fixes: ticket #23818 # Summary of changes Briefly describe what this PR does and why. <!-- If this PR requires new FATE test samples, attach them to the PR and list their target paths below (relative to the fate-suite root). Attached filenames must match the sample's filename: ```fate-samples # e.g. vorbis/new-sample.ogg ``` --> From 9ccb4ce4d898551c29de83a85bc21d4b59e4073b Mon Sep 17 00:00:00 2001 From: Branko Subasic <[email protected]> Date: Mon, 24 Aug 2026 12:11:42 +0200 Subject: [PATCH] avformat/demux: sync side data with matching out packet MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Some side data, like producer reference timestamp (PRFT), must be attached to a packet with the same time stamp as the side data's. But for some protocols, such as RTP, PRFT is calculated for one packet but attached to an earlier one. This is because parse_packet(), in libavformat/demux.c, transfers the current input packet’s side data to whichever parser output is first produced. This patch adds a mechanism where side data which must be attached to an out packet with a matching timestamp is queued until the correct out packet arrives and then attaches the side data to it. Currently only PRFT side data is queued. Fixes: ticket #23818 --- libavformat/avformat.c | 1 + libavformat/demux.c | 110 +++++++++++++++++++++++++++++++++++++++++ libavformat/internal.h | 4 ++ 3 files changed, 115 insertions(+) diff --git a/libavformat/avformat.c b/libavformat/avformat.c index 5d9d8c3697..04b9d7aa40 100644 --- a/libavformat/avformat.c +++ b/libavformat/avformat.c @@ -58,6 +58,7 @@ void ff_free_stream(AVStream **pst) av_parser_close(sti->parser); avcodec_free_context(&sti->avctx); av_bsf_free(&sti->bsfc); + ff_packet_list_free(&sti->pkt_side_data_queue); av_freep(&sti->index_entries); av_freep(&sti->probe_data.buf); diff --git a/libavformat/demux.c b/libavformat/demux.c index 97eaa5f9bc..a7ef8e4d35 100644 --- a/libavformat/demux.c +++ b/libavformat/demux.c @@ -1169,6 +1169,104 @@ static void compute_pkt_fields(AVFormatContext *s, AVStream *st, pkt->flags |= AV_PKT_FLAG_KEY; } +static int parse_and_queue_side_data(FFStream *sti, AVPacket *pkt) +{ + AVPacket side_data_pkt = { 0 }; + int ret; + + /* side data types which must be attached a packet with matching timestamp. + */ + static const enum AVPacketSideDataType queued_types[] = { + AV_PKT_DATA_PRFT, + }; + + /* for each type in the list above, if present in the packet's side data remove it + and put it in the side data queue. */ + for (int i = 0; i < FF_ARRAY_ELEMS(queued_types); i++) { + enum AVPacketSideDataType type = queued_types[i]; + const AVPacketSideData *side_data = + av_packet_side_data_get(pkt->side_data, pkt->side_data_elems, type); + + if (!side_data) + continue; + + uint8_t *dst = av_packet_new_side_data(&side_data_pkt, type, side_data->size); + if (!dst) { + ret = AVERROR(ENOMEM); + goto fail; + } + + memcpy(dst, side_data->data, side_data->size); + av_packet_side_data_remove(pkt->side_data, &pkt->side_data_elems, type); + } + + if (!side_data_pkt.side_data_elems) + return 0; + + side_data_pkt.pts = pkt->pts; + side_data_pkt.dts = pkt->dts; + + ret = ff_packet_list_put(&sti->pkt_side_data_queue, &side_data_pkt, NULL, 0); + if (ret < 0) + goto fail; + + return 0; + +fail: + av_packet_unref(&side_data_pkt); + return ret; +} + +static int attach_queued_side_data(FFStream *sti, AVPacket *pkt) +{ + PacketList *list = &sti->pkt_side_data_queue; + PacketListEntry *entry = list->head; + PacketListEntry *prev = NULL; + + /* any side data in the queue matching pkt's pts must be removed from the + queue and attached to pkt. */ + while (entry) { + PacketListEntry *tmp; + AVPacket side_data_pkt; + + if (entry->pkt.pts != pkt->pts) { + prev = entry; + entry = entry->next; + continue; + } + + tmp = entry->next; + if (prev != NULL) + prev->next = entry->next; + else + list->head = entry->next; + if (list->tail == entry) + list->tail = prev; + + side_data_pkt = entry->pkt; + av_free(entry); + + for (int i = 0; i < side_data_pkt.side_data_elems; i++) { + const AVPacketSideData *sd = &side_data_pkt.side_data[i]; + uint8_t *dst = av_packet_new_side_data(pkt, sd->type, sd->size); + if (!dst) { + av_packet_unref(&side_data_pkt); + return AVERROR(ENOMEM); + } + + memcpy(dst, sd->data, sd->size); + av_log(NULL, AV_LOG_DEBUG, "attaching PRFT side data with pts=%"PRId64" dts=%"PRId64" " + "to packet with pts=%"PRId64" dts=%"PRId64"\n", + side_data_pkt.pts, side_data_pkt.dts, pkt->pts, pkt->dts); + } + av_packet_unref(&side_data_pkt); + + entry = tmp; + } + + return 0; +} + /** * Parse a packet, add all split parts to parse_queue. * @@ -1212,6 +1310,12 @@ static int parse_packet(AVFormatContext *s, AVPacket *pkt, sti->avctx->extradata_size = sd->size; } + /* need to queue any side data that must be sent with an output packet + with the same timestamp? */ + ret = parse_and_queue_side_data(sti, pkt); + if (ret < 0) + goto fail; + while (size > 0 || (flush && got_output)) { int64_t next_pts = pkt->pts; int64_t next_dts = pkt->dts; @@ -1290,6 +1394,12 @@ static int parse_packet(AVFormatContext *s, AVPacket *pkt, out_pkt->pos = sti->parser->pos; out_pkt->flags |= pkt->flags & (AV_PKT_FLAG_DISCARD | AV_PKT_FLAG_CORRUPT); + /* now when out_pkt has a proper pts we can attach possible queued side data + targeted with the same timestamp */ + ret = attach_queued_side_data(sti, out_pkt); + if (ret < 0) + goto fail; + if (sti->need_parsing == AVSTREAM_PARSE_FULL_RAW) out_pkt->pos = sti->parser->frame_offset; diff --git a/libavformat/internal.h b/libavformat/internal.h index b77292a532..448f30d8ed 100644 --- a/libavformat/internal.h +++ b/libavformat/internal.h @@ -321,6 +321,10 @@ typedef struct FFStream { enum AVStreamParseType need_parsing; struct AVCodecParserContext *parser; + /* Queue for packet side data which is to be attached to a packet with a + specific timestamp. */ + PacketList pkt_side_data_queue; + /** * The generic code uses this as a temporary packet * to parse packets or for muxing, especially flushing. -- 2.52.0 _______________________________________________ ffmpeg-devel mailing list -- [email protected] To unsubscribe send an email to [email protected]
