* [FFmpeg-devel] [PATCH] Add support for COMM frames in id3v2 tags. (PR #21079)
@ 2025-12-02 16:47 toots via ffmpeg-devel
0 siblings, 0 replies; only message in thread
From: toots via ffmpeg-devel @ 2025-12-02 16:47 UTC (permalink / raw)
To: ffmpeg-devel; +Cc: toots
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 <romain.beauxis@gmail.com>
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 -- ffmpeg-devel@ffmpeg.org
To unsubscribe send an email to ffmpeg-devel-leave@ffmpeg.org
^ permalink raw reply [flat|nested] only message in thread
only message in thread, other threads:[~2025-12-02 16:48 UTC | newest]
Thread overview: (only message) (download: mbox.gz / follow: Atom feed)
-- links below jump to the message on this page --
2025-12-02 16:47 [FFmpeg-devel] [PATCH] Add support for COMM frames in id3v2 tags. (PR #21079) toots via ffmpeg-devel
Git Inbox Mirror of the ffmpeg-devel mailing list - see https://ffmpeg.org/mailman/listinfo/ffmpeg-devel
This inbox may be cloned and mirrored by anyone:
git clone --mirror https://master.gitmailbox.com/ffmpegdev/0 ffmpegdev/git/0.git
# If you have public-inbox 1.1+ installed, you may
# initialize and index your mirror using the following commands:
public-inbox-init -V2 ffmpegdev ffmpegdev/ https://master.gitmailbox.com/ffmpegdev \
ffmpegdev@gitmailbox.com
public-inbox-index ffmpegdev
Example config snippet for mirrors.
AGPL code for this site: git clone https://public-inbox.org/public-inbox.git