From eb147d85d5d8abafd056514379fcf0e55c56dfbf Mon Sep 17 00:00:00 2001 From: Jian Wang Date: Thu, 18 Feb 2016 08:14:28 +0000 Subject: PD#118065: cts: add mov support parsing QuickTime Metadata Keys merge it from newer ffmpeg version commit 9ea812692c38c58a343cec5f66e8949655906edc Author: Tinglin Liu Date: Thu Oct 22 15:04:46 2015 -0700 mov: Add support parsing QuickTime Metadata Keys. Change-Id: I96c03fc4c6d7525db84f5767d1325e44c3f686d7 --- diff --git a/libavformat/isom.h b/libavformat/isom.h index 828e500..73ae25a 100644 --- a/libavformat/isom.h +++ b/libavformat/isom.h @@ -151,6 +151,10 @@ typedef struct MOVContext { int64_t duration; ///< duration of the longest track int found_moov; ///< 'moov' atom has been found int found_mdat; ///< 'mdat' atom has been found + int found_hdlr_mdta; ///< 'hdlr' atom with type 'mdta' has been found + int trak_index; ///< Index of the current 'trak' + char **meta_keys; + unsigned meta_keys_count; DVDemuxContext *dv_demux; AVFormatContext *dv_fctx; int isom; ///< 1 if file is ISO Media (mp4/3gp) diff --git a/libavformat/mov.c b/libavformat/mov.c index 7d026df..ff0d0be 100644 --- a/libavformat/mov.c +++ b/libavformat/mov.c @@ -551,6 +551,16 @@ static int mov_read_udta_string(MOVContext *c, AVIOContext *pb, MOVAtom atom) av_log(c->fc, AV_LOG_ERROR, "Error parsing cover art.\n"); 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 if (atom.size > 4 && key && !c->itunes_metadata) { @@ -587,6 +597,17 @@ static int mov_read_udta_string(MOVContext *c, AVIOContext *pb, MOVAtom atom) mov_read_mac_string(c, pb, str_size, str, sizeof(str)); } else if (data_type == 13 || data_type == 14){ mov_extract_cover_pic(c->fc, pb, data_type, cover_size, str); + } else if (data_type == 23 && str_size >= 4) { // BE float32 + // Allocates enough space if data_type is a float32 number + int str_size_alloc = 512 + 1; + 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); + av_free(str); + return AVERROR_INVALIDDATA; + } + } else { avio_read(pb, str, str_size); str[str_size] = 0; @@ -759,11 +780,6 @@ static int mov_read_hdlr(MOVContext *c, AVIOContext *pb, MOVAtom atom) int title_size; char *title_str; - 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_rb24(pb); /* flags */ @@ -774,6 +790,15 @@ static int mov_read_hdlr(MOVContext *c, AVIOContext *pb, MOVAtom atom) av_dlog(c->fc, "ctype= %.4s (0x%08x)\n", (char*)&ctype, ctype); av_dlog(c->fc, "stype= %.4s\n", (char*)&type); + if (c->trak_index < 0) { // meta not inside a 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')) st->codec->codec_type = AVMEDIA_TYPE_VIDEO; else if (type == MKTAG('s','o','u','n')) @@ -2538,10 +2563,13 @@ static int mov_read_trak(MOVContext *c, AVIOContext *pb, MOVAtom atom) st->priv_data = sc; st->codec->codec_type = AVMEDIA_TYPE_DATA; sc->ffindex = st->index; + c->trak_index = st->index; if ((ret = mov_read_default(c, pb, atom)) < 0) return ret; + c->trak_index = -1; + /* sanity checks */ if (sc->chunk_count && (!sc->stts_count || !sc->stsc_count || (!sc->sample_size && !sc->sample_count))) { @@ -2630,6 +2658,48 @@ static int mov_read_ilst(MOVContext *c, AVIOContext *pb, MOVAtom atom) 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_meta(MOVContext *c, AVIOContext *pb, MOVAtom atom) { while (atom.size > 8) { @@ -3279,6 +3349,14 @@ static int mov_read_default(MOVContext *c, AVIOContext *pb, MOVAtom atom) atom.type == MKTAG('i','l','s','t'))) 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 */ avio_skip(pb, a.size); } else { @@ -3541,6 +3619,13 @@ static int mov_read_close(AVFormatContext *s) av_freep(&mov->dv_demux); } + 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->bitrates); @@ -3589,6 +3674,7 @@ static int mov_read_header(AVFormatContext *s) MOVAtom atom = { AV_RL32("root") }; mov->fc = s; + mov->trak_index = -1; /* .mov and .mp4 aren't streamable anyway (only progressive download if moov is before mdat) */ if (pb->seekable) atom.size = avio_size(pb); -- cgit