You've already forked FFmpeg
mirror of
https://github.com/FFmpeg/FFmpeg.git
synced 2025-08-15 14:13:16 +02:00
mov: Add support parsing QuickTime Metadata Keys.
The Apple dev specification: https://developer.apple.com/library/mac/documentation/QuickTime/QTFF/Metadata/Metadata.html Basically the structure is like: |--meta |----hdlr |----keys |----ilst 1) The handler type in the metadata handler atom is ‘mdta’. 2) The key and value are stored separately for each key-value pair. The 'keys' atom stores the key table, while 'ilst' atom stores the values corresponding to the indices in the key table. Signed-off-by: Derek Buitenhuis <derek.buitenhuis@gmail.com>
This commit is contained in:
committed by
Derek Buitenhuis
parent
63ea8e0610
commit
9ea812692c
@@ -177,6 +177,9 @@ typedef struct MOVContext {
|
|||||||
int64_t duration; ///< duration of the longest track
|
int64_t duration; ///< duration of the longest track
|
||||||
int found_moov; ///< 'moov' atom has been found
|
int found_moov; ///< 'moov' atom has been found
|
||||||
int found_mdat; ///< 'mdat' atom has been found
|
int found_mdat; ///< 'mdat' atom has been found
|
||||||
|
int found_hdlr_mdta; ///< 'hdlr' atom with type 'mdta' has been found
|
||||||
|
char **meta_keys;
|
||||||
|
unsigned meta_keys_count;
|
||||||
DVDemuxContext *dv_demux;
|
DVDemuxContext *dv_demux;
|
||||||
AVFormatContext *dv_fctx;
|
AVFormatContext *dv_fctx;
|
||||||
int isom; ///< 1 if file is ISO Media (mp4/3gp)
|
int isom; ///< 1 if file is ISO Media (mp4/3gp)
|
||||||
|
@@ -265,6 +265,7 @@ static int mov_read_udta_string(MOVContext *c, AVIOContext *pb, MOVAtom atom)
|
|||||||
uint32_t data_type = 0, str_size, str_size_alloc;
|
uint32_t data_type = 0, str_size, str_size_alloc;
|
||||||
int (*parse)(MOVContext*, AVIOContext*, unsigned, const char*) = NULL;
|
int (*parse)(MOVContext*, AVIOContext*, unsigned, const char*) = NULL;
|
||||||
int raw = 0;
|
int raw = 0;
|
||||||
|
int num = 0;
|
||||||
|
|
||||||
switch (atom.type) {
|
switch (atom.type) {
|
||||||
case MKTAG( '@','P','R','M'): key = "premiere_version"; raw = 1; break;
|
case MKTAG( '@','P','R','M'): key = "premiere_version"; raw = 1; break;
|
||||||
@@ -368,6 +369,15 @@ retry:
|
|||||||
av_log(c->fc, AV_LOG_ERROR, "Error parsing cover art.\n");
|
av_log(c->fc, AV_LOG_ERROR, "Error parsing cover art.\n");
|
||||||
}
|
}
|
||||||
return ret;
|
return ret;
|
||||||
|
} else if (!key && c->found_hdlr_mdta && c->meta_keys) {
|
||||||
|
uint32_t index = AV_RB32(&atom.type);
|
||||||
|
if (index < c->meta_keys_count) {
|
||||||
|
key = c->meta_keys[index];
|
||||||
|
} else {
|
||||||
|
av_log(c->fc, AV_LOG_WARNING,
|
||||||
|
"The index of 'data' is out of range: %d >= %d.\n",
|
||||||
|
index, c->meta_keys_count);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
} else return 0;
|
} else return 0;
|
||||||
} else if (atom.size > 4 && key && !c->itunes_metadata && !raw) {
|
} else if (atom.size > 4 && key && !c->itunes_metadata && !raw) {
|
||||||
@@ -394,8 +404,10 @@ retry:
|
|||||||
if (atom.size < 0 || str_size >= INT_MAX/2)
|
if (atom.size < 0 || str_size >= INT_MAX/2)
|
||||||
return AVERROR_INVALIDDATA;
|
return AVERROR_INVALIDDATA;
|
||||||
|
|
||||||
|
// Allocates enough space if data_type is a float32 number, otherwise
|
||||||
// worst-case requirement for output string in case of utf8 coded input
|
// worst-case requirement for output string in case of utf8 coded input
|
||||||
str_size_alloc = (raw ? str_size : str_size * 2) + 1;
|
num = (data_type == 23);
|
||||||
|
str_size_alloc = (num ? 512 : (raw ? str_size : str_size * 2)) + 1;
|
||||||
str = av_mallocz(str_size_alloc);
|
str = av_mallocz(str_size_alloc);
|
||||||
if (!str)
|
if (!str)
|
||||||
return AVERROR(ENOMEM);
|
return AVERROR(ENOMEM);
|
||||||
@@ -405,6 +417,13 @@ retry:
|
|||||||
else {
|
else {
|
||||||
if (!raw && (data_type == 3 || (data_type == 0 && (langcode < 0x400 || langcode == 0x7fff)))) { // MAC Encoded
|
if (!raw && (data_type == 3 || (data_type == 0 && (langcode < 0x400 || langcode == 0x7fff)))) { // MAC Encoded
|
||||||
mov_read_mac_string(c, pb, str_size, str, str_size_alloc);
|
mov_read_mac_string(c, pb, str_size, str, str_size_alloc);
|
||||||
|
} else if (data_type == 23 && str_size >= 4) { // BE float32
|
||||||
|
float val = av_int2float(avio_rb32(pb));
|
||||||
|
if (snprintf(str, str_size_alloc, "%f", val) >= str_size_alloc) {
|
||||||
|
av_log(c->fc, AV_LOG_ERROR,
|
||||||
|
"Failed to store the float32 number (%f) in string.\n", val);
|
||||||
|
return AVERROR_INVALIDDATA;
|
||||||
|
}
|
||||||
} else {
|
} else {
|
||||||
int ret = ffio_read_size(pb, str, str_size);
|
int ret = ffio_read_size(pb, str, str_size);
|
||||||
if (ret < 0) {
|
if (ret < 0) {
|
||||||
@@ -599,11 +618,6 @@ static int mov_read_hdlr(MOVContext *c, AVIOContext *pb, MOVAtom atom)
|
|||||||
char *title_str;
|
char *title_str;
|
||||||
int ret;
|
int ret;
|
||||||
|
|
||||||
if (c->fc->nb_streams < 1) // meta before first trak
|
|
||||||
return 0;
|
|
||||||
|
|
||||||
st = c->fc->streams[c->fc->nb_streams-1];
|
|
||||||
|
|
||||||
avio_r8(pb); /* version */
|
avio_r8(pb); /* version */
|
||||||
avio_rb24(pb); /* flags */
|
avio_rb24(pb); /* flags */
|
||||||
|
|
||||||
@@ -614,6 +628,15 @@ static int mov_read_hdlr(MOVContext *c, AVIOContext *pb, MOVAtom atom)
|
|||||||
av_log(c->fc, AV_LOG_TRACE, "ctype= %.4s (0x%08x)\n", (char*)&ctype, ctype);
|
av_log(c->fc, AV_LOG_TRACE, "ctype= %.4s (0x%08x)\n", (char*)&ctype, ctype);
|
||||||
av_log(c->fc, AV_LOG_TRACE, "stype= %.4s\n", (char*)&type);
|
av_log(c->fc, AV_LOG_TRACE, "stype= %.4s\n", (char*)&type);
|
||||||
|
|
||||||
|
if (c->fc->nb_streams < 1) { // meta before first trak
|
||||||
|
if (type == MKTAG('m','d','t','a')) {
|
||||||
|
c->found_hdlr_mdta = 1;
|
||||||
|
}
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
st = c->fc->streams[c->fc->nb_streams-1];
|
||||||
|
|
||||||
if (type == MKTAG('v','i','d','e'))
|
if (type == MKTAG('v','i','d','e'))
|
||||||
st->codec->codec_type = AVMEDIA_TYPE_VIDEO;
|
st->codec->codec_type = AVMEDIA_TYPE_VIDEO;
|
||||||
else if (type == MKTAG('s','o','u','n'))
|
else if (type == MKTAG('s','o','u','n'))
|
||||||
@@ -3134,6 +3157,48 @@ static int mov_read_ilst(MOVContext *c, AVIOContext *pb, MOVAtom atom)
|
|||||||
return ret;
|
return ret;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static int mov_read_keys(MOVContext *c, AVIOContext *pb, MOVAtom atom)
|
||||||
|
{
|
||||||
|
uint32_t count;
|
||||||
|
uint32_t i;
|
||||||
|
|
||||||
|
if (atom.size < 8)
|
||||||
|
return 0;
|
||||||
|
|
||||||
|
avio_skip(pb, 4);
|
||||||
|
count = avio_rb32(pb);
|
||||||
|
if (count > UINT_MAX / sizeof(*c->meta_keys)) {
|
||||||
|
av_log(c->fc, AV_LOG_ERROR,
|
||||||
|
"The 'keys' atom with the invalid key count: %d\n", count);
|
||||||
|
return AVERROR_INVALIDDATA;
|
||||||
|
}
|
||||||
|
|
||||||
|
c->meta_keys_count = count + 1;
|
||||||
|
c->meta_keys = av_mallocz(c->meta_keys_count * sizeof(*c->meta_keys));
|
||||||
|
if (!c->meta_keys)
|
||||||
|
return AVERROR(ENOMEM);
|
||||||
|
|
||||||
|
for (i = 1; i <= count; ++i) {
|
||||||
|
uint32_t key_size = avio_rb32(pb);
|
||||||
|
uint32_t type = avio_rl32(pb);
|
||||||
|
if (key_size < 8) {
|
||||||
|
av_log(c->fc, AV_LOG_ERROR,
|
||||||
|
"The key# %d in meta has invalid size: %d\n", i, key_size);
|
||||||
|
return AVERROR_INVALIDDATA;
|
||||||
|
}
|
||||||
|
key_size -= 8;
|
||||||
|
if (type != MKTAG('m','d','t','a')) {
|
||||||
|
avio_skip(pb, key_size);
|
||||||
|
}
|
||||||
|
c->meta_keys[i] = av_mallocz(key_size + 1);
|
||||||
|
if (!c->meta_keys[i])
|
||||||
|
return AVERROR(ENOMEM);
|
||||||
|
avio_read(pb, c->meta_keys[i], key_size);
|
||||||
|
}
|
||||||
|
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
static int mov_read_custom_2plus(MOVContext *c, AVIOContext *pb, int size)
|
static int mov_read_custom_2plus(MOVContext *c, AVIOContext *pb, int size)
|
||||||
{
|
{
|
||||||
int64_t end = avio_tell(pb) + size;
|
int64_t end = avio_tell(pb) + size;
|
||||||
@@ -4078,6 +4143,14 @@ static int mov_read_default(MOVContext *c, AVIOContext *pb, MOVAtom atom)
|
|||||||
atom.type == MKTAG('i','l','s','t')))
|
atom.type == MKTAG('i','l','s','t')))
|
||||||
parse = mov_read_udta_string;
|
parse = mov_read_udta_string;
|
||||||
|
|
||||||
|
// Supports parsing the QuickTime Metadata Keys.
|
||||||
|
// https://developer.apple.com/library/mac/documentation/QuickTime/QTFF/Metadata/Metadata.html
|
||||||
|
if (!parse && c->found_hdlr_mdta &&
|
||||||
|
atom.type == MKTAG('m','e','t','a') &&
|
||||||
|
a.type == MKTAG('k','e','y','s')) {
|
||||||
|
parse = mov_read_keys;
|
||||||
|
}
|
||||||
|
|
||||||
if (!parse) { /* skip leaf atoms data */
|
if (!parse) { /* skip leaf atoms data */
|
||||||
avio_skip(pb, a.size);
|
avio_skip(pb, a.size);
|
||||||
} else {
|
} else {
|
||||||
@@ -4359,6 +4432,13 @@ static int mov_read_close(AVFormatContext *s)
|
|||||||
mov->dv_fctx = NULL;
|
mov->dv_fctx = NULL;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (mov->meta_keys) {
|
||||||
|
for (i = 1; i < mov->meta_keys_count; i++) {
|
||||||
|
av_freep(&mov->meta_keys[i]);
|
||||||
|
}
|
||||||
|
av_freep(&mov->meta_keys);
|
||||||
|
}
|
||||||
|
|
||||||
av_freep(&mov->trex_data);
|
av_freep(&mov->trex_data);
|
||||||
av_freep(&mov->bitrates);
|
av_freep(&mov->bitrates);
|
||||||
|
|
||||||
|
Reference in New Issue
Block a user