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


>From e87ebff53376354ffc5902c9404e65df534a3041 Mon Sep 17 00:00:00 2001
From: Romain Beauxis <[email protected]>
Date: Tue, 2 Dec 2025 10:43:46 -0600
Subject: [PATCH] Add support for COMM frames in id3v2 tags.

---
 libavformat/id3v2.c       |  1 +
 libavformat/id3v2enc.c    | 58 +++++++++++++++++++++++++++++++++++++++
 tests/fate-run.sh         | 13 +++++++++
 tests/fate/id3v2.mak      |  4 +++
 tests/ref/fate/id3v2-comm |  5 ++++
 5 files changed, 81 insertions(+)
 create mode 100644 tests/ref/fate/id3v2-comm

diff --git a/libavformat/id3v2.c b/libavformat/id3v2.c
index 3ce2fadce8..392a7e6730 100644
--- a/libavformat/id3v2.c
+++ b/libavformat/id3v2.c
@@ -44,6 +44,7 @@
 #include "id3v2.h"
 
 const AVMetadataConv ff_id3v2_34_metadata_conv[] = {
+    { "COMM", "comment"      },
     { "TALB", "album"        },
     { "TCOM", "composer"     },
     { "TCON", "genre"        },
diff --git a/libavformat/id3v2enc.c b/libavformat/id3v2enc.c
index ac907c2758..02dc3fc68a 100644
--- a/libavformat/id3v2enc.c
+++ b/libavformat/id3v2enc.c
@@ -25,6 +25,7 @@
 #include "libavutil/dict.h"
 #include "libavutil/intreadwrite.h"
 #include "avformat.h"
+#include "avlanguage.h"
 #include "avio.h"
 #include "avio_internal.h"
 #include "id3v2.h"
@@ -58,6 +59,48 @@ static void id3v2_encode_string(AVIOContext *pb, const 
uint8_t *str,
     put(pb, str);
 }
 
+/**
+ * Write a comment frame according to encoding (only UTF-8 or UTF-16+BOM
+ * supported).
+ * @return number of bytes written or a negative error code.
+ */
+static int id3v2_put_comm(ID3v2EncContext *id3, AVIOContext *avioc, const char 
*lang, const char *descr,
+                          const char *comment, enum ID3v2Encoding enc)
+{
+    int len, ret;
+    uint8_t *pb;
+    AVIOContext *dyn_buf;
+    if ((ret = avio_open_dyn_buf(&dyn_buf)) < 0)
+        return ret;
+
+    /* check if the strings are ASCII-only and use UTF16 only if
+     * they're not */
+    if (enc == ID3v2_ENCODING_UTF16BOM && string_is_ascii(comment) &&
+        (!descr || string_is_ascii(descr)))
+        enc = ID3v2_ENCODING_ISO8859;
+
+    avio_w8(dyn_buf, enc);
+    avio_write(dyn_buf, lang && strlen(lang) == 3 ? lang : "und", 3);
+    if (descr)
+        id3v2_encode_string(dyn_buf, descr, enc);
+    else
+        avio_w8(dyn_buf, 0);
+    id3v2_encode_string(dyn_buf, comment, enc);
+    len = avio_get_dyn_buf(dyn_buf, &pb);
+
+    avio_wb32(avioc, MKBETAG('C', 'O', 'M', 'M'));
+    /* ID3v2.3 frame size is not sync-safe */
+    if (id3->version == 3)
+        avio_wb32(avioc, len);
+    else
+        id3v2_put_size(avioc, len);
+    avio_wb16(avioc, 0);
+    avio_write(avioc, pb, len);
+
+    ffio_free_dyn_buf(&dyn_buf);
+    return len + ID3v2_HEADER_SIZE;
+}
+
 /**
  * Write a text frame with one (normal frames) or two (TXXX frames) strings
  * according to encoding (only UTF-8 or UTF-16+BOM supported).
@@ -221,8 +264,14 @@ static int write_metadata(AVIOContext *pb, AVDictionary 
**metadata,
                           ID3v2EncContext *id3, int enc)
 {
     const AVDictionaryEntry *t = NULL;
+    const char *lang = NULL;
+    const AVDictionaryEntry *lang_tag;
     int ret;
 
+    lang_tag = av_dict_get(*metadata, "language", NULL, 0);
+    if (lang_tag)
+        lang = ff_convert_lang_to(lang_tag->value, AV_LANG_ISO639_2_BIBL);
+
     ff_metadata_conv(metadata, ff_id3v2_34_metadata_conv, NULL);
     if (id3->version == 3)
         id3v2_3_metadata_split_date(metadata);
@@ -230,6 +279,15 @@ static int write_metadata(AVIOContext *pb, AVDictionary 
**metadata,
         ff_metadata_conv(metadata, ff_id3v2_4_metadata_conv, NULL);
 
     while ((t = av_dict_iterate(*metadata, t))) {
+        if (!strncmp(t->key, "COMM", 4)) {
+            ret = id3v2_put_comm(id3, pb, lang, NULL, t->value, enc);
+            if (ret < 0)
+                return ret;
+
+            id3->len += ret;
+            continue;
+        }
+
         if ((ret = id3v2_check_write_tag(id3, pb, t, ff_id3v2_tags, enc)) > 0) 
{
             id3->len += ret;
             continue;
diff --git a/tests/fate-run.sh b/tests/fate-run.sh
index 6d1fe1185c..fd564ff945 100755
--- a/tests/fate-run.sh
+++ b/tests/fate-run.sh
@@ -94,6 +94,19 @@ runecho(){
     $target_exec $target_path/"$@" >&3
 }
 
+run_with_temp(){
+    tmpfile=`mktemp`
+    trap 'rm -rf "$tmpfile"' EXIT
+    create_tmp=$1
+    run "$create_tmp $tmpfile"
+    ret=$?
+    if [ $ret -ne 0 ]; then
+        exit $?
+    fi
+    process_tmp=$2
+    run "$process_tmp $tmpfile"
+}
+
 probefmt(){
     run ffprobe${PROGSUF}${EXECSUF} -bitexact -threads $threads -show_entries 
format=format_name -print_format default=nw=1:nk=1 "$@"
 }
diff --git a/tests/fate/id3v2.mak b/tests/fate/id3v2.mak
index 7ad4d877a4..0ff8451529 100644
--- a/tests/fate/id3v2.mak
+++ b/tests/fate/id3v2.mak
@@ -4,6 +4,10 @@ fate-id3v2-priv: CMD = probetags 
$(TARGET_SAMPLES)/id3v2/id3v2_priv.mp3
 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 REMUX, MP3) += fate-id3v2-comm
+fate-id3v2-comm: $(FFMPEG) $(FFPROBE)
+fate-id3v2-comm: CMD = run_with_temp "$(FFMPEG) -nostdin -hide_banner 
-loglevel quiet -f lavfi -i sine=frequency=1000:duration=2 -id3v2_version 3 
-metadata \"comment=Testing Comment\" -metadata language=eng -y" "$(FFPROBE) 
-bitexact -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-comm b/tests/ref/fate/id3v2-comm
new file mode 100644
index 0000000000..11b48bb831
--- /dev/null
+++ b/tests/ref/fate/id3v2-comm
@@ -0,0 +1,5 @@
+[FORMAT]
+TAG:comment=Testing Comment
+TAG:language=eng
+TAG:encoder=Lavf62.6.103
+[/FORMAT]
-- 
2.49.1

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

Reply via email to