1
0
mirror of https://github.com/FFmpeg/FFmpeg.git synced 2026-06-19 19:03:00 +02:00

avformat/hls: Also report stream-level metadata update.

This commit is contained in:
Romain Beauxis
2026-04-26 19:00:30 -05:00
parent b441d97486
commit e8cfe912f4
2 changed files with 62 additions and 1 deletions
+61
View File
@@ -174,6 +174,14 @@ struct playlist {
int n_init_sections;
struct segment **init_sections;
int is_subtitle; /* Indicates if it's a subtitle playlist */
/* Some HLS streams repeat the current timed-ID3 metadata at every segment
* boundary so late-joining clients get a fresh copy. Track the last seen
* value to avoid raising AVSTREAM_EVENT_FLAG_METADATA_UPDATED spuriously
* on those duplicates. timed_id3_stream_index is the subdemuxer stream
* index of the first timed-ID3 stream seen; -1 if none. */
AVDictionary *timed_id3_metadata;
int timed_id3_stream_index;
};
/*
@@ -275,6 +283,7 @@ static void free_playlist_list(HLSContext *c)
av_freep(&pls->renditions);
av_freep(&pls->id3_buf);
av_dict_free(&pls->id3_initial);
av_dict_free(&pls->timed_id3_metadata);
ff_id3v2_free_extra_meta(&pls->id3_deferred_extra);
av_freep(&pls->init_sec_buf);
av_packet_free(&pls->pkt);
@@ -335,6 +344,7 @@ static struct playlist *new_playlist(HLSContext *c, const char *url,
pls->is_id3_timestamped = -1;
pls->id3_mpegts_timestamp = AV_NOPTS_VALUE;
pls->timed_id3_stream_index = -1;
dynarray_add(&c->playlists, &c->n_playlists, pls);
return pls;
@@ -2108,6 +2118,16 @@ static int update_streams_from_subdemuxer(AVFormatContext *s, struct playlist *p
err = set_stream_info_from_input_stream(st, pls, ist);
if (err < 0)
return err;
if (ist->codecpar->codec_id == AV_CODEC_ID_TIMED_ID3) {
if (pls->timed_id3_stream_index < 0)
pls->timed_id3_stream_index = ist_idx;
else
av_log(s, AV_LOG_WARNING,
"Playlist has multiple timed ID3 streams; "
"only stream %d will be used for metadata propagation\n",
pls->timed_id3_stream_index);
}
}
return 0;
@@ -2553,6 +2573,29 @@ static int compare_ts_with_wrapdetect(int64_t ts_a, struct playlist *pls_a,
return av_compare_mod(scaled_ts_a, scaled_ts_b, 1LL << 33);
}
/**
* Check whether any entry in @p update differs from the corresponding entry
* in @p current.
*
* HLS segments can repeat timed ID3 metadata in every segment so that
* listeners joining mid-stream immediately receive the current track
* information. Only signal a metadata change when values actually differ
* to avoid spurious updates on every segment boundary.
*/
static int metadata_has_changed(const AVDictionary *current,
const AVDictionary *update)
{
if (av_dict_count(current) != av_dict_count(update))
return 1;
const AVDictionaryEntry *e = NULL;
while ((e = av_dict_iterate(update, e))) {
const AVDictionaryEntry *cur = av_dict_get(current, e->key, NULL, 0);
if (!cur || strcmp(cur->value, e->value))
return 1;
}
return 0;
}
static int hls_read_packet(AVFormatContext *s, AVPacket *pkt)
{
HLSContext *c = s->priv_data;
@@ -2694,6 +2737,24 @@ static int hls_read_packet(AVFormatContext *s, AVPacket *pkt)
ist = pls->ctx->streams[pls->pkt->stream_index];
st = pls->main_streams[pls->pkt->stream_index];
// Propagate timed-ID3 metadata changes to the main stream, suppressing
// duplicate packets that repeat unchanged metadata at segment boundaries.
if (ist->codecpar->codec_id == AV_CODEC_ID_TIMED_ID3 &&
pls->pkt->stream_index == pls->timed_id3_stream_index &&
ist->event_flags & AVSTREAM_EVENT_FLAG_METADATA_UPDATED) {
ist->event_flags &= ~AVSTREAM_EVENT_FLAG_METADATA_UPDATED;
if (metadata_has_changed(pls->timed_id3_metadata, ist->metadata)) {
int ret = av_dict_copy(&st->metadata, ist->metadata, 0);
if (ret < 0)
return ret;
av_dict_free(&pls->timed_id3_metadata);
ret = av_dict_copy(&pls->timed_id3_metadata, ist->metadata, 0);
if (ret < 0)
return ret;
st->event_flags |= AVSTREAM_EVENT_FLAG_METADATA_UPDATED;
}
}
av_packet_move_ref(pkt, pls->pkt);
pkt->stream_index = st->index;
+1 -1
View File
@@ -1,4 +1,4 @@
packet|codec_type=data|stream_index=0|pts=126000|pts_time=1.400000|dts=126000|dts_time=1.400000|duration=N/A|duration_time=N/A|size=26|pos=564|flags=K__|data_hash=CRC32:469f474b|side_datum/mpegts_stream_id:side_data_type=MPEGTS Stream ID|side_datum/mpegts_stream_id:id=189
packet|codec_type=data|stream_index=0|pts=577350|pts_time=6.415000|dts=577350|dts_time=6.415000|duration=N/A|duration_time=N/A|size=26|pos=1316|flags=K__|data_hash=CRC32:469f474b|side_datum/mpegts_stream_id:side_data_type=MPEGTS Stream ID|side_datum/mpegts_stream_id:id=189
stream|index=0|codec_name=timed_id3|profile=unknown|codec_type=data|codec_tag_string=ID3 |codec_tag=0x20334449|id=0x0|r_frame_rate=0/0|avg_frame_rate=0/0|time_base=1/90000|start_pts=126000|start_time=1.400000|duration_ts=N/A|duration=N/A|bit_rate=N/A|max_bit_rate=N/A|bits_per_raw_sample=N/A|nb_frames=N/A|nb_read_frames=N/A|nb_read_packets=2|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|tag:variant_bitrate=0
stream|index=0|codec_name=timed_id3|profile=unknown|codec_type=data|codec_tag_string=ID3 |codec_tag=0x20334449|id=0x0|r_frame_rate=0/0|avg_frame_rate=0/0|time_base=1/90000|start_pts=126000|start_time=1.400000|duration_ts=N/A|duration=N/A|bit_rate=N/A|max_bit_rate=N/A|bits_per_raw_sample=N/A|nb_frames=N/A|nb_read_frames=N/A|nb_read_packets=2|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|tag:variant_bitrate=0|tag:title=test
format|filename=id3.m3u8|nb_streams=1|nb_programs=1|nb_stream_groups=0|format_name=hls|start_time=1.400000|duration=2.000000|size=65|bit_rate=260|probe_score=100