Commit 57e46db5 authored by John Stebbins's avatar John Stebbins

ffmpeg: add support to read/write mp4 'titl' track tag

parent 3999c06c
From b6ce7b6a4ec0429069f05e63edce907c0f80aaea Mon Sep 17 00:00:00 2001
From 375d09561cb88a7002a833614dbd399446479cd1 Mon Sep 17 00:00:00 2001
From: John Stebbins <jstebbins@jetheaddev.com>
Date: Wed, 31 Jul 2019 15:27:40 -0700
Subject: [PATCH] mov: read 'name' track tag written by movenc
Subject: [PATCH 1/3] mov: read 'name' track tag written by movenc
---
libavformat/mov.c | 26 +++++++++++++++++---------
1 file changed, 17 insertions(+), 9 deletions(-)
diff --git a/libavformat/mov.c b/libavformat/mov.c
index 24de5429d1..fce69129d9 100644
index 24de5429d1..1f933cccfd 100644
--- a/libavformat/mov.c
+++ b/libavformat/mov.c
@@ -306,6 +306,7 @@ static int mov_read_udta_string(MOVContext *c, AVIOContext *pb, MOVAtom atom)
......@@ -19,14 +19,14 @@ index 24de5429d1..fce69129d9 100644
switch (atom.type) {
case MKTAG( '@','P','R','M'): key = "premiere_version"; raw = 1; break;
@@ -384,6 +385,7 @@ static int mov_read_udta_string(MOVContext *c, AVIOContext *pb, MOVAtom atom)
case MKTAG(0xa9,'m','a','k'): key = "make"; break;
case MKTAG(0xa9,'m','o','d'): key = "model"; break;
case MKTAG(0xa9,'n','a','m'): key = "title"; break;
+ case MKTAG( 'n','a','m','e'): key = "title"; break;
case MKTAG(0xa9,'o','p','e'): key = "original_artist"; break;
case MKTAG(0xa9,'p','r','d'): key = "producer"; break;
case MKTAG(0xa9,'p','r','f'): key = "performers"; break;
@@ -338,6 +339,7 @@ static int mov_read_udta_string(MOVContext *c, AVIOContext *pb, MOVAtom atom)
return mov_metadata_loci(c, pb, atom.size);
case MKTAG( 'm','a','n','u'): key = "make"; break;
case MKTAG( 'm','o','d','l'): key = "model"; break;
+ case MKTAG( 'n','a','m','e'): key = "title"; raw = 1; break;
case MKTAG( 'p','c','s','t'): key = "podcast";
parse = mov_metadata_int8_no_padding; break;
case MKTAG( 'p','g','a','p'): key = "gapless_playback";
@@ -514,17 +516,23 @@ retry:
}
str[str_size] = 0;
......
From e9dde141a5fc27df87eb4b3454e34d725b211708 Mon Sep 17 00:00:00 2001
From: John Stebbins <jstebbins@jetheaddev.com>
Date: Sun, 4 Aug 2019 10:52:30 -0700
Subject: [PATCH 2/3] movenc: write 3gpp track 'titl' tag
Apple software used to use 'name' raw tag for track titles. When they
rewrote everything with AVFoundation, they switched to using 3gpp 'titl'
tag for track titles and 'name' no longer works.
---
libavformat/movenc.c | 78 +++++++++++++++++++++++---------------------
1 file changed, 40 insertions(+), 38 deletions(-)
diff --git a/libavformat/movenc.c b/libavformat/movenc.c
index a96139077b..b1b6ef0e4e 100644
--- a/libavformat/movenc.c
+++ b/libavformat/movenc.c
@@ -3077,6 +3077,35 @@ static int mov_write_udta_sdp(AVIOContext *pb, MOVTrack *track)
return len + 24;
}
+static uint16_t language_code(const char *str)
+{
+ return (((str[0] - 0x60) & 0x1F) << 10) +
+ (((str[1] - 0x60) & 0x1F) << 5) +
+ (( str[2] - 0x60) & 0x1F);
+}
+
+static int mov_write_3gp_udta_tag(AVIOContext *pb, AVDictionary *metadata,
+ const char *tag, const char *str)
+{
+ int64_t pos = avio_tell(pb);
+ AVDictionaryEntry *t = av_dict_get(metadata, str, NULL, 0);
+ if (!t || !utf8len(t->value))
+ return 0;
+ avio_wb32(pb, 0); /* size */
+ ffio_wfourcc(pb, tag); /* type */
+ avio_wb32(pb, 0); /* version + flags */
+ if (!strcmp(tag, "yrrc"))
+ avio_wb16(pb, atoi(t->value));
+ else {
+ avio_wb16(pb, language_code("eng")); /* language */
+ avio_write(pb, t->value, strlen(t->value) + 1); /* UTF8 string value */
+ if (!strcmp(tag, "albm") &&
+ (t = av_dict_get(metadata, "track", NULL, 0)))
+ avio_w8(pb, atoi(t->value));
+ }
+ return update_size(pb, pos);
+}
+
static int mov_write_track_metadata(AVIOContext *pb, AVStream *st,
const char *tag, const char *str)
{
@@ -3105,8 +3134,10 @@ static int mov_write_track_udta_tag(AVIOContext *pb, MOVMuxContext *mov,
if (ret < 0)
return ret;
- if (mov->mode & (MODE_MP4|MODE_MOV))
+ if (mov->mode & (MODE_MP4|MODE_MOV)) {
mov_write_track_metadata(pb_buf, st, "name", "title");
+ mov_write_3gp_udta_tag(pb_buf, st->metadata, "titl", "title");
+ }
if ((size = avio_close_dyn_buf(pb_buf, &buf)) > 0) {
avio_wb32(pb, size + 8);
@@ -3680,35 +3711,6 @@ static int ascii_to_wc(AVIOContext *pb, const uint8_t *b)
return 0;
}
-static uint16_t language_code(const char *str)
-{
- return (((str[0] - 0x60) & 0x1F) << 10) +
- (((str[1] - 0x60) & 0x1F) << 5) +
- (( str[2] - 0x60) & 0x1F);
-}
-
-static int mov_write_3gp_udta_tag(AVIOContext *pb, AVFormatContext *s,
- const char *tag, const char *str)
-{
- int64_t pos = avio_tell(pb);
- AVDictionaryEntry *t = av_dict_get(s->metadata, str, NULL, 0);
- if (!t || !utf8len(t->value))
- return 0;
- avio_wb32(pb, 0); /* size */
- ffio_wfourcc(pb, tag); /* type */
- avio_wb32(pb, 0); /* version + flags */
- if (!strcmp(tag, "yrrc"))
- avio_wb16(pb, atoi(t->value));
- else {
- avio_wb16(pb, language_code("eng")); /* language */
- avio_write(pb, t->value, strlen(t->value) + 1); /* UTF8 string value */
- if (!strcmp(tag, "albm") &&
- (t = av_dict_get(s->metadata, "track", NULL, 0)))
- avio_w8(pb, atoi(t->value));
- }
- return update_size(pb, pos);
-}
-
static int mov_write_chpl_tag(AVIOContext *pb, AVFormatContext *s)
{
int64_t pos = avio_tell(pb);
@@ -3747,14 +3749,14 @@ static int mov_write_udta_tag(AVIOContext *pb, MOVMuxContext *mov,
return ret;
if (mov->mode & MODE_3GP) {
- mov_write_3gp_udta_tag(pb_buf, s, "perf", "artist");
- mov_write_3gp_udta_tag(pb_buf, s, "titl", "title");
- mov_write_3gp_udta_tag(pb_buf, s, "auth", "author");
- mov_write_3gp_udta_tag(pb_buf, s, "gnre", "genre");
- mov_write_3gp_udta_tag(pb_buf, s, "dscp", "comment");
- mov_write_3gp_udta_tag(pb_buf, s, "albm", "album");
- mov_write_3gp_udta_tag(pb_buf, s, "cprt", "copyright");
- mov_write_3gp_udta_tag(pb_buf, s, "yrrc", "date");
+ mov_write_3gp_udta_tag(pb_buf, s->metadata, "perf", "artist");
+ mov_write_3gp_udta_tag(pb_buf, s->metadata, "titl", "title");
+ mov_write_3gp_udta_tag(pb_buf, s->metadata, "auth", "author");
+ mov_write_3gp_udta_tag(pb_buf, s->metadata, "gnre", "genre");
+ mov_write_3gp_udta_tag(pb_buf, s->metadata, "dscp", "comment");
+ mov_write_3gp_udta_tag(pb_buf, s->metadata, "albm", "album");
+ mov_write_3gp_udta_tag(pb_buf, s->metadata, "cprt", "copyright");
+ mov_write_3gp_udta_tag(pb_buf, s->metadata, "yrrc", "date");
mov_write_loci_tag(s, pb_buf);
} else if (mov->mode == MODE_MOV && !(mov->flags & FF_MOV_FLAG_USE_MDTA)) { // the title field breaks gtkpod with mp4 and my suspicion is that stuff is not valid in mp4
mov_write_string_metadata(s, pb_buf, "\251ART", "artist", 0);
--
2.21.0
From 1b02b0afe201cd14a97d83dc08716bf9cc188cb0 Mon Sep 17 00:00:00 2001
From: John Stebbins <jstebbins@jetheaddev.com>
Date: Sun, 4 Aug 2019 10:59:38 -0700
Subject: [PATCH 3/3] mov: read 3gpp udta tags
---
libavformat/mov.c | 110 ++++++++++++++++++++++++++++++++++++++++++++--
1 file changed, 107 insertions(+), 3 deletions(-)
diff --git a/libavformat/mov.c b/libavformat/mov.c
index 1f933cccfd..0c365cde3e 100644
--- a/libavformat/mov.c
+++ b/libavformat/mov.c
@@ -50,6 +50,7 @@
#include "libavcodec/flac.h"
#include "libavcodec/mpegaudiodecheader.h"
#include "avformat.h"
+#include "avlanguage.h"
#include "internal.h"
#include "avio_internal.h"
#include "riff.h"
@@ -295,6 +296,82 @@ static int mov_metadata_hmmt(MOVContext *c, AVIOContext *pb, unsigned len)
return 0;
}
+static int mov_read_3gp_udta_tag(MOVContext *c, AVIOContext *pb, MOVAtom atom)
+{
+ const char *key;
+ AVDictionary *metadata;
+ uint16_t langcode = 0;
+ char key2[32], language[4] = {0};
+ uint32_t str_size, version;
+ char *str;
+
+ if (atom.size < 6)
+ return AVERROR_INVALIDDATA;
+
+ switch (atom.type) {
+ case MKTAG( 'a','l','b','m'): key = "album"; break;
+ case MKTAG( 'a','u','t','h'): key = "author"; break;
+ case MKTAG( 'c','p','r','t'): key = "copyright"; break;
+ case MKTAG( 'd','s','c','p'): key = "comment"; break;
+ case MKTAG( 'g','n','r','e'): key = "genre"; break;
+ case MKTAG( 'p','e','r','f'): key = "artist"; break;
+ case MKTAG( 't','i','t','l'): key = "title"; break;
+ case MKTAG( 'y','r','r','c'): key = "date"; break;
+ default: return 0;
+ }
+
+ version = avio_rb32(pb); // version + flags
+ if (version != 0)
+ return AVERROR_INVALIDDATA;
+
+ if (MKTAG( 'y','r','r','c') == atom.type) {
+ int year;
+ year = avio_rb16(pb);
+ str = av_asprintf("%d", year);
+ if (!str)
+ return AVERROR(ENOMEM);
+ } else {
+ int ret;
+ const char *tmp;
+ langcode = avio_rb16(pb);
+ ff_mov_lang_to_iso639(langcode, language);
+ tmp = ff_convert_lang_to(language, AV_LANG_ISO639_2_BIBL);
+ if (!tmp)
+ return AVERROR_INVALIDDATA;
+
+ str_size = atom.size - 6;
+ if (str_size <= 0 || str_size >= INT_MAX/2)
+ return AVERROR_INVALIDDATA;
+
+ str = av_mallocz(str_size + 1);
+ if (!str)
+ return AVERROR(ENOMEM);
+
+ ret = ffio_read_size(pb, str, str_size);
+ if (ret < 0) {
+ av_free(str);
+ return ret;
+ }
+ str[str_size] = 0;
+ }
+
+ if (c->trak_index < 0) {
+ metadata = c->fc->metadata;
+ c->fc->event_flags |= AVFMT_EVENT_FLAG_METADATA_UPDATED;
+ }
+ else {
+ metadata = c->fc->streams[c->trak_index]->metadata;
+ }
+ av_dict_set(&metadata, key, str, 0);
+ if (*language && strcmp(language, "und")) {
+ snprintf(key2, sizeof(key2), "%s-%s", key, language);
+ av_dict_set(&metadata, key2, str, 0);
+ }
+
+ av_freep(&str);
+ return 0;
+}
+
static int mov_read_udta_string(MOVContext *c, AVIOContext *pb, MOVAtom atom)
{
char tmp_key[5];
@@ -320,15 +397,33 @@ static int mov_read_udta_string(MOVContext *c, AVIOContext *pb, MOVAtom atom)
case MKTAG( 'c','a','t','g'): key = "category"; 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;
+ case MKTAG( 'c','p','r','t'):
+ key = "copyright";
+ if (!c->itunes_metadata) {
+ int64_t pos = avio_tell(pb);
+ int ret = mov_read_3gp_udta_tag(c, pb, atom);
+ if (ret != AVERROR_INVALIDDATA)
+ return ret;
+ avio_seek(pb, pos, SEEK_SET);
+ }
+ break;
case MKTAG( 'd','e','s','c'): key = "description"; break;
case MKTAG( 'd','i','s','k'): key = "disc";
parse = mov_metadata_track_or_disc_number; break;
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','n','r','e'): key = "genre";
- parse = mov_metadata_gnre; break;
+ case MKTAG( 'g','n','r','e'):
+ key = "genre";
+ parse = mov_metadata_gnre;
+ if (!c->itunes_metadata) {
+ int64_t pos = avio_tell(pb);
+ int ret = mov_read_3gp_udta_tag(c, pb, atom);
+ if (ret != AVERROR_INVALIDDATA)
+ return ret;
+ avio_seek(pb, pos, SEEK_SET);
+ }
+ break;
case MKTAG( 'h','d','v','d'): key = "hd_video";
parse = mov_metadata_int8_no_padding; break;
case MKTAG( 'H','M','M','T'):
@@ -399,6 +494,15 @@ static int mov_read_udta_string(MOVContext *c, AVIOContext *pb, MOVAtom atom)
case MKTAG(0xa9,'w','r','n'): key = "warning"; break;
case MKTAG(0xa9,'w','r','t'): key = "composer"; break;
case MKTAG(0xa9,'x','y','z'): key = "location"; break;
+ case MKTAG( 'a','l','b','m'):
+ case MKTAG( 'a','u','t','h'):
+ case MKTAG( 'd','s','c','p'):
+ case MKTAG( 'p','e','r','f'):
+ case MKTAG( 't','i','t','l'):
+ case MKTAG( 'y','r','r','c'):
+ if (!c->itunes_metadata) {
+ return mov_read_3gp_udta_tag(c, pb, atom);
+ }
}
retry:
if (c->itunes_metadata && atom.size > 8) {
--
2.21.0
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment