You've already forked FFmpeg
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:
@@ -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,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
|
||||
|
||||
Reference in New Issue
Block a user