From patchwork Tue Mar 26 00:56:40 2024 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Romain Beauxis X-Patchwork-Id: 47472 Delivered-To: ffmpegpatchwork2@gmail.com Received: by 2002:a05:6a20:c889:b0:1a3:b6bb:3029 with SMTP id hb9csp1526248pzb; Mon, 25 Mar 2024 17:58:51 -0700 (PDT) X-Forwarded-Encrypted: i=2; AJvYcCUiDxdXbmqIlNVrqJzN7nkKBkV3RKBfPHhPsyDtaw/Vu2BEI3/pfQp2Y8EB01eb6vINInXA8zIZB0AbHDhg1WVwD/PeUdR+sSqusQ== X-Google-Smtp-Source: AGHT+IFiIGdqVOBT0s/029LSi2bnpTQ4kv/2lTkw1Ue+9bBqyY6CsVim1Cp8puWSMFft9YqQ6Qgr X-Received: by 2002:a17:907:7245:b0:a47:5143:b67c with SMTP id ds5-20020a170907724500b00a475143b67cmr5283045ejc.37.1711414730957; Mon, 25 Mar 2024 17:58:50 -0700 (PDT) ARC-Seal: i=1; a=rsa-sha256; t=1711414730; cv=none; d=google.com; s=arc-20160816; b=f9YTad9nZv3S5xdzyF95R+9p2gLfLk+oJKhZw9RD8kl5WMJG4ANJqW/70h4g6+jvff dLRVCD7mt9v/rbQKXvV6R9VEJ961u6mTSKHNmK3ttk+A9BHrDC9KanxNhlLIDYL0Hk/4 WVHxaTO3dKMJq03nJ96TOONudw6a15CED0mx4Xhf/pcUuVA09wEmRS4lh+YQXxuPgtXE xILauw2z8opdXhKGHsZxjn8ESf3r134gXR/tvCWKhMPUDnjjY0o/8wDlERgiYrKqVumx z3UOVcDtxug8G8f3PO425D4DtgyGWV12ergzzlKzBGLZ8absU5xjMqdFRpzqK/S7v7mE nCaw== ARC-Message-Signature: i=1; a=rsa-sha256; c=relaxed/relaxed; d=google.com; s=arc-20160816; h=sender:errors-to:content-transfer-encoding:cc:reply-to :list-subscribe:list-help:list-post:list-archive:list-unsubscribe :list-id:precedence:subject:mime-version:message-id:date:to:from :delivered-to; bh=zOhzyqIuhnwKdshQxevY/G22nS9OtBkZ+6wbG5yU+nk=; fh=Dy2HmWbIgzG07YKb/uJ0dh3GBmODWRQaNOnRjO8Xj4c=; b=WFbffn2Wh3OOND188533C3W+LUc7Fu5JDTRO8rRL6Srb8xh/3fXlo4YcG9HPrb4EpV EO4yG77CicjjAA18ymaKe5GPfjK93biW3KpWPX6hFqjtGnjeYSuqv/NPr+g+Ww9Zyu4o N1JX+BO0ap1FzSva0/WXQw489HecwvS67bYrsm39UdiLkxDaNKwBAH641iO7K/GYwjxZ DQBlcis4lFYXWaL20bcSoPslLsxlKRRKJX7C4Z/1KqWJxGLtwoQ4+EC1O3nDYAQq7uZY auA3LtrQDXE37vjUCtn+Im8wpuwf9kSDlicz9z3Zy7F9gUxbHLIFVU4pjypAj+C3iFvZ v+EA==; dara=google.com ARC-Authentication-Results: i=1; mx.google.com; spf=pass (google.com: domain of ffmpeg-devel-bounces@ffmpeg.org designates 79.124.17.100 as permitted sender) smtp.mailfrom=ffmpeg-devel-bounces@ffmpeg.org Return-Path: Received: from ffbox0-bg.mplayerhq.hu (ffbox0-bg.ffmpeg.org. [79.124.17.100]) by mx.google.com with ESMTP id lj19-20020a170906f9d300b00a47483b8a8esi2513978ejb.292.2024.03.25.17.58.50; Mon, 25 Mar 2024 17:58:50 -0700 (PDT) Received-SPF: pass (google.com: domain of ffmpeg-devel-bounces@ffmpeg.org designates 79.124.17.100 as permitted sender) client-ip=79.124.17.100; Authentication-Results: mx.google.com; spf=pass (google.com: domain of ffmpeg-devel-bounces@ffmpeg.org designates 79.124.17.100 as permitted sender) smtp.mailfrom=ffmpeg-devel-bounces@ffmpeg.org Received: from [127.0.1.1] (localhost [127.0.0.1]) by ffbox0-bg.mplayerhq.hu (Postfix) with ESMTP id 246E668D523; Tue, 26 Mar 2024 02:58:47 +0200 (EET) X-Original-To: ffmpeg-devel@ffmpeg.org Delivered-To: ffmpeg-devel@ffmpeg.org Received: from mail-ot1-f52.google.com (mail-ot1-f52.google.com [209.85.210.52]) by ffbox0-bg.mplayerhq.hu (Postfix) with ESMTPS id 5EE2B68D180 for ; Tue, 26 Mar 2024 02:58:39 +0200 (EET) Received: by mail-ot1-f52.google.com with SMTP id 46e09a7af769-6e6a40a15c5so1566078a34.0 for ; Mon, 25 Mar 2024 17:58:39 -0700 (PDT) X-Google-DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=1e100.net; s=20230601; t=1711414717; x=1712019517; h=content-transfer-encoding:mime-version:message-id:date:subject:cc :to:from:x-gm-message-state:from:to:cc:subject:date:message-id :reply-to; bh=4lNxbbaoWWz0jBW73Izg1mZhVxaC4Z7drpXdO/jiaPQ=; b=jszWXZEhyFDBnXlRwulDoLoLt6zc5/Bzc3/iQtMT7SCZdQYvLQv7U/ljoyl1SZw+5L J7g4qJcXkbcZfhV5B+mJI4lufsBs3zybxDLZmKJwDszzIJHxsX19hrwZaCyqm9O71Fzb zjZBvZQ1Odb/kgp7XrQ5k142NK74m/3cQnL2qa4TImnwcww5n8oOxu0OG40+VLV1aOQo 8it0qkGVXzbfhe2s/B9MWhre7TdIlTEh1DAZpQ+fWxpMxYEy9oxKEkG4/we5hbaFK1Nm NJXl3O5Q35arcAqpT79xfjDB7i37W7G8iCZiCbqj6d8m9R1MRcdSfNEZqqKFyfz/s3AR yG2Q== X-Gm-Message-State: AOJu0Yzlk4tz4qftfLiVJQFlmQvcMRVKhtmP3ovuuv7p+C3mKId+JjLp uytMd+fNbzoOXvZ555KGAbMxBfwZwYTl04KUNf6jXEfBEnTn40eTA7RooQIdIUoxVA== X-Received: by 2002:a05:6808:f08:b0:3c3:d4c4:abc7 with SMTP id m8-20020a0568080f0800b003c3d4c4abc7mr2625441oiw.4.1711414717371; Mon, 25 Mar 2024 17:58:37 -0700 (PDT) Received: from romains-mbp-2.lan (ip68-107-252-55.no.no.cox.net. [68.107.252.55]) by smtp.gmail.com with ESMTPSA id a12-20020aca1a0c000000b003c3d10519adsm530573oia.57.2024.03.25.17.58.36 (version=TLS1_3 cipher=TLS_CHACHA20_POLY1305_SHA256 bits=256/256); Mon, 25 Mar 2024 17:58:36 -0700 (PDT) From: Romain Beauxis To: ffmpeg-devel@ffmpeg.org Date: Mon, 25 Mar 2024 19:56:40 -0500 Message-Id: <20240326005639.27000-1-toots@rastageeks.org> X-Mailer: git-send-email 2.39.3 (Apple Git-145) MIME-Version: 1.0 Subject: [FFmpeg-devel] [PATCH 1/2] libavformat/hls.c: support in-stream ID3 metadata update. X-BeenThere: ffmpeg-devel@ffmpeg.org X-Mailman-Version: 2.1.29 Precedence: list List-Id: FFmpeg development discussions and patches List-Unsubscribe: , List-Archive: List-Post: List-Help: List-Subscribe: , Reply-To: FFmpeg development discussions and patches Cc: Romain Beauxis Errors-To: ffmpeg-devel-bounces@ffmpeg.org Sender: "ffmpeg-devel" X-TUID: BxNgP/SrGJMZ 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); }