[FFmpeg-devel] [PATCH 1/2] libavformat/hls.c: support in-stream ID3 metadata update.
Romain Beauxis
toots at rastageeks.org
Tue Mar 26 02:56:40 EET 2024
This patch adds support for updating HLS metadata passed as ID3 frames.
This seems like a pretty straight-forward improvement. Updating the
metadaata of the first stream seems to be the mechanism is other places
in the code and works as expected.
---
libavformat/hls.c | 54 ++++++++++++++++++++++++++++-------------------
1 file changed, 32 insertions(+), 22 deletions(-)
diff --git a/libavformat/hls.c b/libavformat/hls.c
index f6b44c2e35..ba6634d57a 100644
--- a/libavformat/hls.c
+++ b/libavformat/hls.c
@@ -93,6 +93,12 @@ enum PlaylistType {
PLS_TYPE_VOD
};
+#define ID3_PRIV_OWNER_TS "com.apple.streaming.transportStreamTimestamp"
+#define ID3_PRIV_OWNER_AUDIO_SETUP "com.apple.streaming.audioDescription"
+
+#define ID3v2_PRIV_OWNER_TS ID3v2_PRIV_METADATA_PREFIX ID3_PRIV_OWNER_TS
+#define ID3v2_PRIV_OWNER_AUDIO_SETUP ID3v2_PRIV_METADATA_PREFIX ID3_PRIV_OWNER_AUDIO_SETUP
+
/*
* Each playlist has its own demuxer. If it currently is active,
* it has an open AVIOContext too, and potentially an AVPacket
@@ -150,9 +156,7 @@ struct playlist {
int64_t id3_offset; /* in stream original tb */
uint8_t* id3_buf; /* temp buffer for id3 parsing */
unsigned int id3_buf_size;
- AVDictionary *id3_initial; /* data from first id3 tag */
- int id3_found; /* ID3 tag found at some point */
- int id3_changed; /* ID3 tag data has changed at some point */
+ AVDictionary *last_id3; /* data from the last id3 tag */
ID3v2ExtraMeta *id3_deferred_extra; /* stored here until subdemuxer is opened */
HLSAudioSetupInfo audio_setup_info;
@@ -270,7 +274,7 @@ static void free_playlist_list(HLSContext *c)
av_freep(&pls->main_streams);
av_freep(&pls->renditions);
av_freep(&pls->id3_buf);
- av_dict_free(&pls->id3_initial);
+ av_dict_free(&pls->last_id3);
ff_id3v2_free_extra_meta(&pls->id3_deferred_extra);
av_freep(&pls->init_sec_buf);
av_packet_free(&pls->pkt);
@@ -1083,15 +1087,13 @@ static void parse_id3(AVFormatContext *s, AVIOContext *pb,
AVDictionary **metadata, int64_t *dts, HLSAudioSetupInfo *audio_setup_info,
ID3v2ExtraMetaAPIC **apic, ID3v2ExtraMeta **extra_meta)
{
- static const char id3_priv_owner_ts[] = "com.apple.streaming.transportStreamTimestamp";
- static const char id3_priv_owner_audio_setup[] = "com.apple.streaming.audioDescription";
ID3v2ExtraMeta *meta;
ff_id3v2_read_dict(pb, metadata, ID3v2_DEFAULT_MAGIC, extra_meta);
for (meta = *extra_meta; meta; meta = meta->next) {
if (!strcmp(meta->tag, "PRIV")) {
ID3v2ExtraMetaPRIV *priv = &meta->data.priv;
- if (priv->datasize == 8 && !av_strncasecmp(priv->owner, id3_priv_owner_ts, 44)) {
+ if (priv->datasize == 8 && !av_strncasecmp(priv->owner, ID3_PRIV_OWNER_TS, strlen(ID3_PRIV_OWNER_TS))) {
/* 33-bit MPEG timestamp */
int64_t ts = AV_RB64(priv->data);
av_log(s, AV_LOG_DEBUG, "HLS ID3 audio timestamp %"PRId64"\n", ts);
@@ -1099,7 +1101,9 @@ static void parse_id3(AVFormatContext *s, AVIOContext *pb,
*dts = ts;
else
av_log(s, AV_LOG_ERROR, "Invalid HLS ID3 audio timestamp %"PRId64"\n", ts);
- } else if (priv->datasize >= 8 && !av_strncasecmp(priv->owner, id3_priv_owner_audio_setup, 36)) {
+ } else if (priv->datasize >= 8 &&
+ !av_strncasecmp(priv->owner, ID3_PRIV_OWNER_AUDIO_SETUP, 36) &&
+ audio_setup_info) {
ff_hls_senc_read_audio_setup_info(audio_setup_info, priv->data, priv->datasize);
}
} else if (!strcmp(meta->tag, "APIC") && apic)
@@ -1113,9 +1117,10 @@ static int id3_has_changed_values(struct playlist *pls, AVDictionary *metadata,
{
const AVDictionaryEntry *entry = NULL;
const AVDictionaryEntry *oldentry;
+
/* check that no keys have changed values */
while ((entry = av_dict_iterate(metadata, entry))) {
- oldentry = av_dict_get(pls->id3_initial, entry->key, NULL, AV_DICT_MATCH_CASE);
+ oldentry = av_dict_get(pls->last_id3, entry->key, NULL, AV_DICT_MATCH_CASE);
if (!oldentry || strcmp(oldentry->value, entry->value) != 0)
return 1;
}
@@ -1143,35 +1148,40 @@ static void handle_id3(AVIOContext *pb, struct playlist *pls)
ID3v2ExtraMetaAPIC *apic = NULL;
ID3v2ExtraMeta *extra_meta = NULL;
int64_t timestamp = AV_NOPTS_VALUE;
+ // Only set audio_setup_info on first id3 chunk.
+ HLSAudioSetupInfo *audio_setup_info = pls->last_id3 ? NULL : &pls->audio_setup_info;
- parse_id3(pls->ctx, pb, &metadata, ×tamp, &pls->audio_setup_info, &apic, &extra_meta);
+ parse_id3(pls->ctx, pb, &metadata, ×tamp, audio_setup_info, &apic, &extra_meta);
- if (timestamp != AV_NOPTS_VALUE) {
+ if (pls->id3_mpegts_timestamp == AV_NOPTS_VALUE && timestamp != AV_NOPTS_VALUE) {
pls->id3_mpegts_timestamp = timestamp;
pls->id3_offset = 0;
}
- if (!pls->id3_found) {
- /* initial ID3 tags */
- av_assert0(!pls->id3_deferred_extra);
- pls->id3_found = 1;
-
+ if (id3_has_changed_values(pls, metadata, apic)) {
/* get picture attachment and set text metadata */
if (pls->ctx->nb_streams)
ff_id3v2_parse_apic(pls->ctx, extra_meta);
- else
+ else {
+ av_assert0(!pls->id3_deferred_extra);
/* demuxer not yet opened, defer picture attachment */
pls->id3_deferred_extra = extra_meta;
+ }
ff_id3v2_parse_priv_dict(&metadata, extra_meta);
+
+ av_dict_set(&metadata, ID3v2_PRIV_OWNER_TS, NULL, 0);
+ av_dict_set(&metadata, ID3v2_PRIV_OWNER_AUDIO_SETUP, NULL, 0);
+
+ av_dict_free(&pls->ctx->metadata);
av_dict_copy(&pls->ctx->metadata, metadata, 0);
- pls->id3_initial = metadata;
+ if (pls->n_main_streams)
+ av_dict_copy(&pls->main_streams[0]->metadata, metadata, 0);
+
+ if (pls->last_id3) av_dict_free(&pls->last_id3);
+ pls->last_id3 = metadata;
} else {
- if (!pls->id3_changed && id3_has_changed_values(pls, metadata, apic)) {
- avpriv_report_missing_feature(pls->parent, "Changing ID3 metadata in HLS audio elementary stream");
- pls->id3_changed = 1;
- }
av_dict_free(&metadata);
}
--
2.39.3 (Apple Git-145)
More information about the ffmpeg-devel
mailing list