PR #22624 opened by toots
URL: https://code.ffmpeg.org/FFmpeg/FFmpeg/pulls/22624
Patch URL: https://code.ffmpeg.org/FFmpeg/FFmpeg/pulls/22624.patch


>From 14cbfefef8438cb291eec4072fe9ca66bd6e91c8 Mon Sep 17 00:00:00 2001
From: Romain Beauxis <[email protected]>
Date: Wed, 25 Mar 2026 23:46:18 -0500
Subject: [PATCH] fftools/ffmpeg_mux_init.c: delete encoder-specific metadata
 when re-encoding.

---
 doc/ffmpeg.texi                               | 14 ++++++
 fftools/ffmpeg.h                              |  1 +
 fftools/ffmpeg_mux_init.c                     | 46 +++++++++++++++++++
 fftools/ffmpeg_opt.c                          |  3 ++
 tests/fate/id3v2.mak                          |  9 ++++
 tests/ref/fate/id3v2-itunes-remux             | 19 ++++++++
 tests/ref/fate/id3v2-reenc-delete-metadata    | 16 +++++++
 .../ref/fate/id3v2-reenc-delete-metadata-keep | 17 +++++++
 8 files changed, 125 insertions(+)
 create mode 100644 tests/ref/fate/id3v2-itunes-remux
 create mode 100644 tests/ref/fate/id3v2-reenc-delete-metadata
 create mode 100644 tests/ref/fate/id3v2-reenc-delete-metadata-keep

diff --git a/doc/ffmpeg.texi b/doc/ffmpeg.texi
index 533131adfc..c2384ee755 100644
--- a/doc/ffmpeg.texi
+++ b/doc/ffmpeg.texi
@@ -920,6 +920,20 @@ To set the language of the first audio stream:
 ffmpeg -i INPUT -metadata:s:a:0 language=eng OUTPUT
 @end example
 
+@item -keep_metadata[:metadata_specifier] @var{key} 
(@emph{output,per-metadata})
+When re-encoding a stream, certain metadata keys that are likely to become 
stale
+(such as @code{iTunSMPB} or @code{encoded_by}) are automatically discarded.
+This option prevents a specific key from being discarded, preserving its 
original
+value from the input.
+
+For example, to keep @code{iTunSMPB} when re-encoding to MP3:
+@example
+ffmpeg -i in.mp3 -c:a mp3 -keep_metadata iTunSMPB out.mp3
+@end example
+
+Note that this option has no effect when copying streams without re-encoding,
+as metadata is always preserved in that case.
+
 @item -disposition[:stream_specifier] @var{value} (@emph{output,per-stream})
 Sets the disposition flags for a stream.
 
diff --git a/fftools/ffmpeg.h b/fftools/ffmpeg.h
index 3a19e5878d..45001efa45 100644
--- a/fftools/ffmpeg.h
+++ b/fftools/ffmpeg.h
@@ -206,6 +206,7 @@ typedef struct OptionsContext {
     AVDictionary *streamid;
 
     SpecifierOptList metadata;
+    SpecifierOptList keep_metadata;
     SpecifierOptList max_frames;
     SpecifierOptList bitstream_filters;
     SpecifierOptList codec_tags;
diff --git a/fftools/ffmpeg_mux_init.c b/fftools/ffmpeg_mux_init.c
index e0eecf78f2..099b8d5444 100644
--- a/fftools/ffmpeg_mux_init.c
+++ b/fftools/ffmpeg_mux_init.c
@@ -3017,6 +3017,23 @@ static int copy_metadata(Muxer *mux, AVFormatContext *ic,
     return 0;
 }
 
+static const char * const reenc_delete_keys[] = {
+    "iTunPGAP", "encoded_by", "iTunSMPB", NULL,
+};
+
+static int reenc_delete_metadata_key(const OptionsContext *o, const char *key)
+{
+    for (int i = 0; i < o->keep_metadata.nb_opt; i++)
+        if (!strcmp(key, o->keep_metadata.opt[i].u.str))
+            return 0;
+    for (int i = 0; i < o->metadata.nb_opt; i++) {
+        size_t klen = strcspn(o->metadata.opt[i].u.str, "=");
+        if (!strncmp(key, o->metadata.opt[i].u.str, klen) && key[klen] == '\0')
+            return 0;
+    }
+    return 1;
+}
+
 static int copy_meta(Muxer *mux, const OptionsContext *o)
 {
     OutputFile      *of = &mux->of;
@@ -3076,6 +3093,22 @@ static int copy_meta(Muxer *mux, const OptionsContext *o)
         av_dict_set(&oc->metadata, "company_name", NULL, 0);
         av_dict_set(&oc->metadata, "product_name", NULL, 0);
         av_dict_set(&oc->metadata, "product_version", NULL, 0);
+
+        for (int i = 0; i < of->nb_streams; i++) {
+            if (of->streams[i]->enc) {
+                for (int j = 0; reenc_delete_keys[j]; j++) {
+                    if (reenc_delete_metadata_key(o, reenc_delete_keys[j]) &&
+                        av_dict_get(oc->metadata, reenc_delete_keys[j], NULL, 
0)) {
+                        av_log(mux, AV_LOG_WARNING,
+                               "Discarding format metadata '%s' because a 
stream is being "
+                               "re-encoded. Use '-keep_metadata %s' to keep 
it.\n",
+                               reenc_delete_keys[j], reenc_delete_keys[j]);
+                        av_dict_set(&oc->metadata, reenc_delete_keys[j], NULL, 
0);
+                    }
+                }
+                break;
+            }
+        }
     }
     if (!metadata_streams_manual)
         for (int i = 0; i < of->nb_streams; i++) {
@@ -3084,6 +3117,19 @@ static int copy_meta(Muxer *mux, const OptionsContext *o)
             if (!ost->ist)         /* this is true e.g. for attached files */
                 continue;
             av_dict_copy(&ost->st->metadata, ost->ist->st->metadata, 
AV_DICT_DONT_OVERWRITE);
+            if (!ost->enc)
+                continue;
+
+            for (int j = 0; reenc_delete_keys[j]; j++) {
+                if (reenc_delete_metadata_key(o, reenc_delete_keys[j]) &&
+                    av_dict_get(ost->st->metadata, reenc_delete_keys[j], NULL, 
0)) {
+                    av_log(ost, AV_LOG_WARNING,
+                           "Discarding stream metadata '%s' because the stream 
is being "
+                           "re-encoded. Use '-keep_metadata %s' to keep it.\n",
+                           reenc_delete_keys[j], reenc_delete_keys[j]);
+                    av_dict_set(&ost->st->metadata, reenc_delete_keys[j], 
NULL, 0);
+                }
+            }
         }
 
     return 0;
diff --git a/fftools/ffmpeg_opt.c b/fftools/ffmpeg_opt.c
index 48e6816c19..1ab722d5b1 100644
--- a/fftools/ffmpeg_opt.c
+++ b/fftools/ffmpeg_opt.c
@@ -1701,6 +1701,9 @@ const OptionDef options[] = {
     { "metadata",               OPT_TYPE_STRING, OPT_SPEC | OPT_OUTPUT,
         { .off = OFFSET(metadata) },
         "add metadata", "key=value" },
+    { "keep_metadata",          OPT_TYPE_STRING, OPT_SPEC | OPT_OUTPUT,
+        { .off = OFFSET(keep_metadata) },
+        "keep metadata key from input when re-encoding", "key" },
     { "program",                OPT_TYPE_STRING, OPT_SPEC | OPT_EXPERT | 
OPT_OUTPUT,
         { .off = OFFSET(program) },
         "add program with specified streams", "title=string:st=number..." },
diff --git a/tests/fate/id3v2.mak b/tests/fate/id3v2.mak
index a3040e9759..34bf54831f 100644
--- a/tests/fate/id3v2.mak
+++ b/tests/fate/id3v2.mak
@@ -5,9 +5,18 @@ FATE_ID3V2_FFMPEG-$(CONFIG_MP3_DEMUXER) += 
fate-id3v2-invalid-tags
 fate-id3v2-invalid-tags: CMD = run $(FFMPEG) -nostdin -hide_banner -i 
$(TARGET_SAMPLES)/id3v2/invalid-tags.mp3 -f null - || true
 fate-id3v2-invalid-tags: CMP = null
 
+FATE_ID3V2_FFMPEG_FFPROBE-$(call REMUX, MP3) += fate-id3v2-itunes-remux
+fate-id3v2-itunes-remux: CMD = transcode mp3 
$(TARGET_SAMPLES)/gapless/gapless-itunes.mp3 mp3 "-c copy" "-c copy -t 0.1" 
"-show_entries format_tags"
+
 FATE_ID3V2_FFMPEG_FFPROBE-$(call REMUX, MP3) += fate-id3v2-priv-remux
 fate-id3v2-priv-remux: CMD = transcode mp3 
$(TARGET_SAMPLES)/id3v2/id3v2_priv.mp3 mp3 "-c copy" "-c copy -t 0.1" 
"-show_entries format_tags"
 
+FATE_ID3V2_FFMPEG_FFPROBE-$(call ENCDEC, LIBMP3LAME MP3, MP3) += 
fate-id3v2-reenc-delete-metadata
+fate-id3v2-reenc-delete-metadata: CMD = transcode mp3 
$(TARGET_SAMPLES)/gapless/gapless-itunes.mp3 mp3 "-c:a mp3 -bitexact" "-c copy 
-t 0.1" "-show_entries format_tags"
+
+FATE_ID3V2_FFMPEG_FFPROBE-$(call ENCDEC, LIBMP3LAME MP3, MP3) += 
fate-id3v2-reenc-delete-metadata-keep
+fate-id3v2-reenc-delete-metadata-keep: CMD = transcode mp3 
$(TARGET_SAMPLES)/gapless/gapless-itunes.mp3 mp3 "-c:a mp3 -bitexact 
-keep_metadata iTunSMPB" "-c copy -t 0.1" "-show_entries format_tags"
+
 FATE_ID3V2_FFMPEG_FFPROBE-$(call REMUX, AIFF, WAV_DEMUXER) += 
fate-id3v2-chapters
 fate-id3v2-chapters: CMD = transcode wav $(TARGET_SAMPLES)/wav/200828-005.wav 
aiff "-c copy -metadata:c:0 description=foo -metadata:c:0 date=2021 -metadata:c 
copyright=none -metadata:c:1 genre=nonsense -write_id3v2 1" "-c copy -t 0.05" 
"-show_entries format_tags:chapters"
 
diff --git a/tests/ref/fate/id3v2-itunes-remux 
b/tests/ref/fate/id3v2-itunes-remux
new file mode 100644
index 0000000000..ecbf7a45ad
--- /dev/null
+++ b/tests/ref/fate/id3v2-itunes-remux
@@ -0,0 +1,19 @@
+263462774b77f2b20a9cb5cbe492f910 *tests/data/fate/id3v2-itunes-remux.mp3
+192072 tests/data/fate/id3v2-itunes-remux.mp3
+#tb 0: 1/14112000
+#media_type 0: audio
+#codec_id 0: mp3
+#sample_rate 0: 44100
+#channel_layout_name 0: stereo
+0,    -169273,    -169273,   368640,      522, 0xbe74f65c, S=1, Skip Samples,  
     10, 0x00bc0013
+0,     199367,     199367,   368640,      523, 0xbba5059f
+0,     568007,     568007,   368640,      522, 0xd8a2153d
+0,     936647,     936647,   368640,      523, 0xae220034
+0,    1305287,    1305287,   368640,      522, 0xf661f22c
+[FORMAT]
+TAG:title=7rk
+TAG:iTunPGAP=0
+TAG:encoded_by=iTunes 12.7.0.166
+TAG:iTunNORM= 00000362 000004C0 0000308F 00003CC5 00000DAC 00000DAC 00007D14 
00007AC9 000007C1 0000175E
+TAG:iTunSMPB= 00000000 00000210 0000086A 0000000000066486 00000000 0002DA9D 
00000000 00000000 00000000 00000000 00000000 00000000
+[/FORMAT]
diff --git a/tests/ref/fate/id3v2-reenc-delete-metadata 
b/tests/ref/fate/id3v2-reenc-delete-metadata
new file mode 100644
index 0000000000..c981443f84
--- /dev/null
+++ b/tests/ref/fate/id3v2-reenc-delete-metadata
@@ -0,0 +1,16 @@
+81fb5e219394c97f48a0f2598595add1 
*tests/data/fate/id3v2-reenc-delete-metadata.mp3
+153745 tests/data/fate/id3v2-reenc-delete-metadata.mp3
+#tb 0: 1/14112000
+#media_type 0: audio
+#codec_id 0: mp3
+#sample_rate 0: 44100
+#channel_layout_name 0: stereo
+0,    -353604,    -353604,   368640,      417, 0xdf4cd4db, S=1, Skip Samples,  
     10, 0x034e0055
+0,      15036,      15036,   368640,      418, 0x8011cd14
+0,     383676,     383676,   368640,      418, 0x6424c5e0
+0,     752316,     752316,   368640,      418, 0xa358bbae
+0,    1120956,    1120956,   368640,      418, 0xfb1ac3ae
+[FORMAT]
+TAG:title=7rk
+TAG:iTunNORM= 00000362 000004C0 0000308F 00003CC5 00000DAC 00000DAC 00007D14 
00007AC9 000007C1 0000175E
+[/FORMAT]
diff --git a/tests/ref/fate/id3v2-reenc-delete-metadata-keep 
b/tests/ref/fate/id3v2-reenc-delete-metadata-keep
new file mode 100644
index 0000000000..00e1e13c47
--- /dev/null
+++ b/tests/ref/fate/id3v2-reenc-delete-metadata-keep
@@ -0,0 +1,17 @@
+19738194dc7418362789ba85f299721f 
*tests/data/fate/id3v2-reenc-delete-metadata-keep.mp3
+153882 tests/data/fate/id3v2-reenc-delete-metadata-keep.mp3
+#tb 0: 1/14112000
+#media_type 0: audio
+#codec_id 0: mp3
+#sample_rate 0: 44100
+#channel_layout_name 0: stereo
+0,    -353604,    -353604,   368640,      417, 0xdf4cd4db, S=1, Skip Samples,  
     10, 0x034e0055
+0,      15036,      15036,   368640,      418, 0x8011cd14
+0,     383676,     383676,   368640,      418, 0x6424c5e0
+0,     752316,     752316,   368640,      418, 0xa358bbae
+0,    1120956,    1120956,   368640,      418, 0xfb1ac3ae
+[FORMAT]
+TAG:title=7rk
+TAG:iTunSMPB= 00000000 00000210 0000086A 0000000000066486 00000000 0002DA9D 
00000000 00000000 00000000 00000000 00000000 00000000
+TAG:iTunNORM= 00000362 000004C0 0000308F 00003CC5 00000DAC 00000DAC 00007D14 
00007AC9 000007C1 0000175E
+[/FORMAT]
-- 
2.52.0

_______________________________________________
ffmpeg-devel mailing list -- [email protected]
To unsubscribe send an email to [email protected]

Reply via email to