* [FFmpeg-devel] [PATCH] avformat/mov: read and write additional iTunes style metadata
@ 2025-02-12 16:00 Damiano Galassi
0 siblings, 0 replies; only message in thread
From: Damiano Galassi @ 2025-02-12 16:00 UTC (permalink / raw)
To: ffmpeg-devel; +Cc: Damiano Galassi
---
libavformat/mov.c | 67 +++++++++++++++++++++++++++-
libavformat/movenc.c | 69 +++++++++++++++++++++++++++--
tests/ref/fate/caf-alac-remux | 5 ++-
tests/ref/fate/cover-art-flac-remux | 5 ++-
tests/ref/fate/matroska-alac-remux | 5 ++-
tests/ref/fate/mov-cover-image | 4 +-
6 files changed, 142 insertions(+), 13 deletions(-)
diff --git a/libavformat/mov.c b/libavformat/mov.c
index 85aef33b19..f70a04c37d 100644
--- a/libavformat/mov.c
+++ b/libavformat/mov.c
@@ -131,6 +131,33 @@ static int mov_metadata_int8_no_padding(MOVContext *c, AVIOContext *pb,
return 0;
}
+static int mov_metadata_int16_no_padding(MOVContext *c, AVIOContext *pb,
+ unsigned len, const char *key)
+{
+ c->fc->event_flags |= AVFMT_EVENT_FLAG_METADATA_UPDATED;
+ av_dict_set_int(&c->fc->metadata, key, avio_rb16(pb), 0);
+
+ return 0;
+}
+
+static int mov_metadata_int32_no_padding(MOVContext *c, AVIOContext *pb,
+ unsigned len, const char *key)
+{
+ c->fc->event_flags |= AVFMT_EVENT_FLAG_METADATA_UPDATED;
+ av_dict_set_int(&c->fc->metadata, key, avio_rb32(pb), 0);
+
+ return 0;
+}
+
+static int mov_metadata_int64_no_padding(MOVContext *c, AVIOContext *pb,
+ unsigned len, const char *key)
+{
+ c->fc->event_flags |= AVFMT_EVENT_FLAG_METADATA_UPDATED;
+ av_dict_set_int(&c->fc->metadata, key, avio_rb64(pb), 0);
+
+ return 0;
+}
+
static int mov_metadata_gnre(MOVContext *c, AVIOContext *pb,
unsigned len, const char *key)
{
@@ -370,7 +397,13 @@ static int mov_read_udta_string(MOVContext *c, AVIOContext *pb, MOVAtom atom)
case MKTAG( 'a','k','I','D'): key = "account_type";
parse = mov_metadata_int8_no_padding; break;
case MKTAG( 'a','p','I','D'): key = "account_id"; break;
+ case MKTAG( 'a','t','I','D'): key = "artist_id";
+ parse = mov_metadata_int32_no_padding; break;
case MKTAG( 'c','a','t','g'): key = "category"; break;
+ case MKTAG( 'c','m','I','D'): key = "composer_id";
+ parse = mov_metadata_int32_no_padding; break;
+ case MKTAG( 'c','n','I','D'): key = "content_id";
+ parse = mov_metadata_int32_no_padding; break;
case MKTAG( 'c','p','i','l'): key = "compilation";
parse = mov_metadata_int8_no_padding; break;
case MKTAG( 'c','p','r','t'): key = "copyright"; break;
@@ -380,12 +413,16 @@ static int mov_read_udta_string(MOVContext *c, AVIOContext *pb, MOVAtom atom)
case MKTAG( 'e','g','i','d'): key = "episode_uid";
parse = mov_metadata_int8_no_padding; break;
case MKTAG( 'F','I','R','M'): key = "firmware"; raw = 1; break;
+ case MKTAG( 'g','e','I','D'): key = "genre_id";
+ parse = mov_metadata_int32_no_padding; break;
case MKTAG( 'g','n','r','e'): key = "genre";
parse = mov_metadata_gnre; break;
case MKTAG( 'h','d','v','d'): key = "hd_video";
parse = mov_metadata_int8_no_padding; break;
case MKTAG( 'H','M','M','T'):
return mov_metadata_hmmt(c, pb, atom.size);
+ case MKTAG( 'i','t','n','u'): key = "itunes_u";
+ parse = mov_metadata_int8_no_padding; break;
case MKTAG( 'k','e','y','w'): key = "keywords"; break;
case MKTAG( 'l','d','e','s'): key = "synopsis"; break;
case MKTAG( 'l','o','c','i'):
@@ -396,9 +433,16 @@ static int mov_read_udta_string(MOVContext *c, AVIOContext *pb, MOVAtom atom)
parse = mov_metadata_int8_no_padding; break;
case MKTAG( 'p','g','a','p'): key = "gapless_playback";
parse = mov_metadata_int8_no_padding; break;
+ case MKTAG( 'p','l','I','D'): key = "playlist_id";
+ parse = mov_metadata_int64_no_padding; break;
case MKTAG( 'p','u','r','d'): key = "purchase_date"; break;
case MKTAG( 'r','t','n','g'): key = "rating";
parse = mov_metadata_int8_no_padding; break;
+ case MKTAG( 's','f','I','D'): key = "itunes_country";
+ parse = mov_metadata_int32_no_padding; break;
+ case MKTAG( 's','d','e','s'): key = "series_description"; break;
+ case MKTAG( 's','h','w','m'): key = "show_work_and_movement";
+ parse = mov_metadata_int8_no_padding; break;
case MKTAG( 's','o','a','a'): key = "sort_album_artist"; break;
case MKTAG( 's','o','a','l'): key = "sort_album"; break;
case MKTAG( 's','o','a','r'): key = "sort_artist"; break;
@@ -407,6 +451,8 @@ static int mov_read_udta_string(MOVContext *c, AVIOContext *pb, MOVAtom atom)
case MKTAG( 's','o','s','n'): key = "sort_show"; break;
case MKTAG( 's','t','i','k'): key = "media_type";
parse = mov_metadata_int8_no_padding; break;
+ case MKTAG( 't','m','p','o'): key = "tmpo";
+ parse = mov_metadata_int16_no_padding; break;
case MKTAG( 't','r','k','n'): key = "track";
parse = mov_metadata_track_or_disc_number; break;
case MKTAG( 't','v','e','n'): key = "episode_id"; break;
@@ -416,17 +462,23 @@ static int mov_read_udta_string(MOVContext *c, AVIOContext *pb, MOVAtom atom)
case MKTAG( 't','v','s','h'): key = "show"; break;
case MKTAG( 't','v','s','n'): key = "season_number";
parse = mov_metadata_int8_bypass_padding; break;
+ case MKTAG( 'x','i','d',' '): key = "xid"; break;
case MKTAG(0xa9,'A','R','T'): key = "artist"; break;
case MKTAG(0xa9,'P','R','D'): key = "producer"; break;
case MKTAG(0xa9,'a','l','b'): key = "album"; break;
- case MKTAG(0xa9,'a','u','t'): key = "artist"; break;
+ case MKTAG(0xa9,'a','r','d'): key = "art_director"; break;
+ case MKTAG(0xa9,'a','r','g'): key = "arranger"; break;
+ case MKTAG(0xa9,'a','u','t'): key = "author"; break;
+ case MKTAG(0xa9,'c','a','k'): key = "acknowledgement"; break;
case MKTAG(0xa9,'c','h','p'): key = "chapter"; break;
case MKTAG(0xa9,'c','m','t'): key = "comment"; break;
case MKTAG(0xa9,'c','o','m'): key = "composer"; break;
+ case MKTAG(0xa9,'c','o','n'): key = "conductor"; break;
case MKTAG(0xa9,'c','p','y'): key = "copyright"; break;
case MKTAG(0xa9,'d','a','y'): key = "date"; break;
case MKTAG(0xa9,'d','i','r'): key = "director"; break;
case MKTAG(0xa9,'d','i','s'): key = "disclaimer"; break;
+ case MKTAG(0xa9,'d','e','s'): key = "song_description"; break;
case MKTAG(0xa9,'e','d','1'): key = "edit_date"; break;
case MKTAG(0xa9,'e','n','c'): key = "encoder"; break;
case MKTAG(0xa9,'f','m','t'): key = "original_format"; break;
@@ -434,22 +486,35 @@ static int mov_read_udta_string(MOVContext *c, AVIOContext *pb, MOVAtom atom)
case MKTAG(0xa9,'g','r','p'): key = "grouping"; break;
case MKTAG(0xa9,'h','s','t'): key = "host_computer"; break;
case MKTAG(0xa9,'i','n','f'): key = "comment"; break;
+ case MKTAG(0xa9,'l','n','t'): key = "linear_notes"; break;
case MKTAG(0xa9,'l','y','r'): key = "lyrics"; break;
case MKTAG(0xa9,'m','a','k'): key = "make"; break;
case MKTAG(0xa9,'m','o','d'): key = "model"; break;
+ case MKTAG(0xa9,'m','v','n'): key = "movement_name"; break;
+ case MKTAG(0xa9,'m','v','i'): key = "movement_number";
+ parse = mov_metadata_int16_no_padding; break;
+ case MKTAG(0xa9,'m','v','c'): key = "movement_count";
+ parse = mov_metadata_int16_no_padding; break;
case MKTAG(0xa9,'n','a','m'): key = "title"; break;
case MKTAG(0xa9,'o','p','e'): key = "original_artist"; break;
+ case MKTAG(0xa9,'p','h','g'): key = "phonogram_rights"; break;
case MKTAG(0xa9,'p','r','d'): key = "producer"; break;
case MKTAG(0xa9,'p','r','f'): key = "performers"; break;
+ case MKTAG(0xa9,'p','u','b'): key = "publisher"; break;
case MKTAG(0xa9,'r','e','q'): key = "playback_requirements"; break;
+ case MKTAG(0xa9,'s','n','e'): key = "sound_engineer"; break;
+ case MKTAG(0xa9,'s','o','l'): key = "soloist"; break;
case MKTAG(0xa9,'s','r','c'): key = "original_source"; break;
case MKTAG(0xa9,'s','t','3'): key = "subtitle"; break;
case MKTAG(0xa9,'s','w','r'): key = "encoder"; break;
+ case MKTAG(0xa9,'t','h','x'): key = "thanks"; break;
case MKTAG(0xa9,'t','o','o'): key = "encoder"; break;
case MKTAG(0xa9,'t','r','k'): key = "track"; break;
case MKTAG(0xa9,'u','r','l'): key = "URL"; break;
+ case MKTAG(0xa9,'w','r','k'): key = "work_name"; break;
case MKTAG(0xa9,'w','r','n'): key = "warning"; break;
case MKTAG(0xa9,'w','r','t'): key = "composer"; break;
+ case MKTAG(0xa9,'x','p','d'): key = "executive_producer"; break;
case MKTAG(0xa9,'x','y','z'): key = "location"; break;
}
retry:
diff --git a/libavformat/movenc.c b/libavformat/movenc.c
index 76dce9e6e5..740dbfef5f 100644
--- a/libavformat/movenc.c
+++ b/libavformat/movenc.c
@@ -4518,15 +4518,19 @@ static int mov_write_int8_metadata(AVFormatContext *s, AVIOContext *pb,
int len)
{
AVDictionaryEntry *t = NULL;
- uint8_t num;
+ uint64_t num;
int size = 24 + len;
- if (len != 1 && len != 4)
+ if (len != 1 && len != 4 &&
+ len != 2 && len != 8)
return -1;
if (!(t = av_dict_get(s->metadata, tag, NULL, 0)))
return 0;
- num = atoi(t->value);
+ if (len <= 4)
+ num = atoi(t->value);
+ else
+ num = atol(t->value);
avio_wb32(pb, size);
ffio_wfourcc(pb, name);
@@ -4534,7 +4538,9 @@ static int mov_write_int8_metadata(AVFormatContext *s, AVIOContext *pb,
ffio_wfourcc(pb, "data");
avio_wb32(pb, 0x15);
avio_wb32(pb, 0);
- if (len==4) avio_wb32(pb, num);
+ if (len==8) avio_wb64(pb, num);
+ else if (len==4) avio_wb32(pb, num);
+ else if (len==2) avio_wb16(pb, num);
else avio_w8 (pb, num);
return size;
@@ -4590,6 +4596,8 @@ static int mov_write_ilst_tag(AVIOContext *pb, MOVMuxContext *mov,
mov_write_string_metadata(s, pb, "\251lyr", "lyrics" , 1);
mov_write_string_metadata(s, pb, "desc", "description",1);
mov_write_string_metadata(s, pb, "ldes", "synopsis" , 1);
+ mov_write_string_metadata(s, pb, "sdes", "series_description", 1);
+ mov_write_string_metadata(s, pb, "rtng", "rating", 1);
mov_write_string_metadata(s, pb, "tvsh", "show" , 1);
mov_write_string_metadata(s, pb, "tven", "episode_id",1);
mov_write_string_metadata(s, pb, "tvnn", "network" , 1);
@@ -4600,6 +4608,59 @@ static int mov_write_ilst_tag(AVIOContext *pb, MOVMuxContext *mov,
mov_write_int8_metadata (s, pb, "hdvd", "hd_video", 1);
mov_write_int8_metadata (s, pb, "pgap", "gapless_playback",1);
mov_write_int8_metadata (s, pb, "cpil", "compilation", 1);
+
+ mov_write_string_metadata(s, pb, "\251st3", "subtitle" , 1);
+ mov_write_string_metadata(s, pb, "\251des", "song_description", 1);
+ mov_write_string_metadata(s, pb, "\251ard", "art_director" , 1);
+ mov_write_string_metadata(s, pb, "\251arg", "arranger" , 1);
+ mov_write_string_metadata(s, pb, "\251aut", "author" , 1);
+ mov_write_string_metadata(s, pb, "\251cak", "acknowledgement" , 1);
+ mov_write_string_metadata(s, pb, "\251con", "conductor" , 1);
+
+ mov_write_string_metadata(s, pb, "\251wrk", "work_name" , 1);
+ mov_write_string_metadata(s, pb, "\251mvn", "movement_name" , 1);
+ mov_write_int8_metadata (s, pb, "\251mvi", "movement_number", 2);
+ mov_write_int8_metadata (s, pb, "\251mvc", "movement_count" , 2);
+ mov_write_int8_metadata (s, pb, "shwm", "show_work_and_movement", 1);
+
+ mov_write_string_metadata(s, pb, "\251lnt", "linear_notes" , 1);
+ mov_write_string_metadata(s, pb, "\251mak", "make" , 1); // Record company
+ mov_write_string_metadata(s, pb, "\251ope", "original_artist" , 1);
+ mov_write_string_metadata(s, pb, "\251phg", "phonogram_rights" , 1);
+ mov_write_string_metadata(s, pb, "\251prd", "producer" , 1);
+ mov_write_string_metadata(s, pb, "\251prf", "performers" , 1);
+ mov_write_string_metadata(s, pb, "\251pub", "publisher" , 1);
+ mov_write_string_metadata(s, pb, "\251sne", "sound_engineer" , 1);
+ mov_write_string_metadata(s, pb, "\251sol", "soloist" , 1);
+ mov_write_string_metadata(s, pb, "\251src", "original_source" , 1); // Credits
+ mov_write_string_metadata(s, pb, "\251thx", "thanks" , 1);
+ mov_write_string_metadata(s, pb, "\251url", "URL" , 1); // Online extras
+ mov_write_string_metadata(s, pb, "\251xpd", "executive_producer", 1);
+
+ mov_write_string_metadata(s, pb, "sonm", "sort_name" , 1);
+ mov_write_string_metadata(s, pb, "soar", "sort_artist" , 1);
+ mov_write_string_metadata(s, pb, "soaa", "sort_album_artist", 1);
+ mov_write_string_metadata(s, pb, "soal", "sort_album" , 1);
+ mov_write_string_metadata(s, pb, "soco", "sort_composer" , 1);
+ mov_write_string_metadata(s, pb, "sosn", "sort_show" , 1);
+
+ mov_write_string_metadata(s, pb, "\251enc", "encoder" , 1); // Encoded by
+ mov_write_string_metadata(s, pb, "purd", "purchase_date" , 1);
+
+ mov_write_int8_metadata (s, pb, "itnu", "itunes_u" , 1);
+ mov_write_int8_metadata (s, pb, "pcst", "podcast" , 1);
+ mov_write_string_metadata(s, pb, "catg", "category" , 1);
+
+ mov_write_string_metadata(s, pb, "apID", "account_id" , 1);
+ mov_write_int8_metadata (s, pb, "akID", "account_type" , 1);
+ mov_write_int8_metadata (s, pb, "sfID", "itunes_country", 4);
+ mov_write_int8_metadata (s, pb, "cnID", "content_id" , 4);
+ mov_write_int8_metadata (s, pb, "atID", "artist_id" , 4);
+ mov_write_int8_metadata (s, pb, "plID", "playlist_id" , 8);
+ mov_write_int8_metadata (s, pb, "geID", "genre_id" , 4);
+ mov_write_int8_metadata (s, pb, "cmID", "composer_id" , 4);
+ mov_write_string_metadata(s, pb, "xid ", "xid" , 1);
+
mov_write_covr(pb, s);
mov_write_trkn_tag(pb, mov, s, 0); // track number
mov_write_trkn_tag(pb, mov, s, 1); // disc number
diff --git a/tests/ref/fate/caf-alac-remux b/tests/ref/fate/caf-alac-remux
index f33182b721..7ad9cf74a1 100644
--- a/tests/ref/fate/caf-alac-remux
+++ b/tests/ref/fate/caf-alac-remux
@@ -1,5 +1,5 @@
-9ef40186fb3e24789df03f8c08110486 *tests/data/fate/caf-alac-remux.caf
-1292684 tests/data/fate/caf-alac-remux.caf
+a49516372a950d86498ceba69709c589 *tests/data/fate/caf-alac-remux.caf
+1292691 tests/data/fate/caf-alac-remux.caf
#extradata 0: 36, 0x562b05d8
#tb 0: 1/44100
#media_type 0: audio
@@ -17,6 +17,7 @@ TAG:disc=1
TAG:title=Inside
TAG:compilation=1
TAG:gapless_playback=0
+TAG:tmpo=0
TAG:genre=Rock
TAG:Encoding Params=vers
TAG:iTunNORM= 000004DF 000004C2 00001E64 00001AB3 00000FB9 00000FB9 00006480 00006480 00000FB9 00000B52
diff --git a/tests/ref/fate/cover-art-flac-remux b/tests/ref/fate/cover-art-flac-remux
index fa91975881..0eb42cb26c 100644
--- a/tests/ref/fate/cover-art-flac-remux
+++ b/tests/ref/fate/cover-art-flac-remux
@@ -1,5 +1,5 @@
-6defc5081a59ab12c8a5f9e263b25068 *tests/data/fate/cover-art-flac-remux.flac
-1098537 tests/data/fate/cover-art-flac-remux.flac
+51e126d3ea5cadbfcc0ad039a6fb61cb *tests/data/fate/cover-art-flac-remux.flac
+1098547 tests/data/fate/cover-art-flac-remux.flac
#extradata 0: 34, 0x8d830abd
#tb 0: 1/44100
#media_type 0: audio
@@ -97,6 +97,7 @@ TAG:disc=1
TAG:title=Inside
TAG:compilation=1
TAG:gapless_playback=0
+TAG:tmpo=0
TAG:track=5/13
TAG:Encoding Params=vers
TAG:iTunNORM= 000004DF 000004C2 00001E64 00001AB3 00000FB9 00000FB9 00006480 00006480 00000FB9 00000B52
diff --git a/tests/ref/fate/matroska-alac-remux b/tests/ref/fate/matroska-alac-remux
index 9b73263acd..4e730bb21c 100644
--- a/tests/ref/fate/matroska-alac-remux
+++ b/tests/ref/fate/matroska-alac-remux
@@ -1,5 +1,5 @@
-90c54a00ad8662c3eb93150791fa8328 *tests/data/fate/matroska-alac-remux.matroska
-1293824 tests/data/fate/matroska-alac-remux.matroska
+1b35d6d679219aa63eab3a34d2eb50dd *tests/data/fate/matroska-alac-remux.matroska
+1293838 tests/data/fate/matroska-alac-remux.matroska
#extradata 0: 36, 0x562b05d8
#tb 0: 1/1000
#media_type 0: audio
@@ -165,6 +165,7 @@ TAG:COMPATIBLE_BRANDS=M4A mp42isom
TAG:DISC=1
TAG:COMPILATION=1
TAG:GAPLESS_PLAYBACK=0
+TAG:TMPO=0
TAG:ENCODING_PARAMS=vers
TAG:ITUNNORM= 000004DF 000004C2 00001E64 00001AB3 00000FB9 00000FB9 00006480 00006480 00000FB9 00000B52
TAG:ARTIST=Maxwell Strait
diff --git a/tests/ref/fate/mov-cover-image b/tests/ref/fate/mov-cover-image
index 5f65c630ea..59941de7dd 100644
--- a/tests/ref/fate/mov-cover-image
+++ b/tests/ref/fate/mov-cover-image
@@ -1,5 +1,5 @@
-54a8870d5d1e6cc4da28ae422aa70898 *tests/data/fate/mov-cover-image.mp4
-1011919 tests/data/fate/mov-cover-image.mp4
+33e107deb271ca991acf8336d01785e1 *tests/data/fate/mov-cover-image.mp4
+1011945 tests/data/fate/mov-cover-image.mp4
#extradata 0: 2, 0x00340022
#tb 0: 1/44100
#media_type 0: audio
--
2.39.5 (Apple Git-154)
_______________________________________________
ffmpeg-devel mailing list
ffmpeg-devel@ffmpeg.org
https://ffmpeg.org/mailman/listinfo/ffmpeg-devel
To unsubscribe, visit link above, or email
ffmpeg-devel-request@ffmpeg.org with subject "unsubscribe".
^ permalink raw reply [flat|nested] only message in thread
only message in thread, other threads:[~2025-02-12 16:00 UTC | newest]
Thread overview: (only message) (download: mbox.gz / follow: Atom feed)
-- links below jump to the message on this page --
2025-02-12 16:00 [FFmpeg-devel] [PATCH] avformat/mov: read and write additional iTunes style metadata Damiano Galassi
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