diff --git a/libavformat/Makefile b/libavformat/Makefile index 0fac33efe7..18b8817f2a 100644 --- a/libavformat/Makefile +++ b/libavformat/Makefile @@ -220,7 +220,7 @@ OBJS-$(CONFIG_MLP_MUXER) += rawenc.o OBJS-$(CONFIG_MM_DEMUXER) += mm.o OBJS-$(CONFIG_MMF_DEMUXER) += mmf.o OBJS-$(CONFIG_MMF_MUXER) += mmf.o rawenc.o -OBJS-$(CONFIG_MOV_DEMUXER) += mov.o isom.o mov_chan.o +OBJS-$(CONFIG_MOV_DEMUXER) += mov.o isom.o mov_chan.o replaygain.o OBJS-$(CONFIG_MOV_MUXER) += movenc.o isom.o avc.o hevc.o \ movenchint.o mov_chan.o rtp.o OBJS-$(CONFIG_MP2_MUXER) += mp3enc.o rawenc.o id3v2enc.o diff --git a/libavformat/mov.c b/libavformat/mov.c index d10bac5678..79bc918cea 100644 --- a/libavformat/mov.c +++ b/libavformat/mov.c @@ -47,6 +47,7 @@ #include "libavcodec/get_bits.h" #include "id3v1.h" #include "mov_chan.h" +#include "replaygain.h" #if CONFIG_ZLIB #include @@ -2508,6 +2509,88 @@ static int mov_read_ilst(MOVContext *c, AVIOContext *pb, MOVAtom atom) return ret; } +static int mov_read_replaygain(MOVContext *c, AVIOContext *pb, int size) +{ + int64_t end = avio_tell(pb) + size; + uint8_t *key = NULL, *val = NULL; + int i; + + for (i = 0; i < 2; i++) { + uint8_t **p; + uint32_t len, tag; + + if (end - avio_tell(pb) <= 12) + break; + + len = avio_rb32(pb); + tag = avio_rl32(pb); + avio_skip(pb, 4); // flags + + if (len < 12 || len - 12 > end - avio_tell(pb)) + break; + len -= 12; + + if (tag == MKTAG('n', 'a', 'm', 'e')) + p = &key; + else if (tag == MKTAG('d', 'a', 't', 'a') && len > 4) { + avio_skip(pb, 4); + len -= 4; + p = &val; + } else + break; + + *p = av_malloc(len + 1); + if (!*p) + break; + avio_read(pb, *p, len); + (*p)[len] = 0; + } + + if (key && val) { + av_dict_set(&c->fc->metadata, key, val, + AV_DICT_DONT_STRDUP_KEY | AV_DICT_DONT_STRDUP_VAL); + key = val = NULL; + } + + avio_seek(pb, end, SEEK_SET); + av_freep(&key); + av_freep(&val); + return 0; +} + +static int mov_read_custom(MOVContext *c, AVIOContext *pb, MOVAtom atom) +{ + int64_t end = avio_tell(pb) + atom.size; + uint32_t tag, len; + + if (atom.size < 8) + goto fail; + + len = avio_rb32(pb); + tag = avio_rl32(pb); + + if (len > atom.size) + goto fail; + + if (tag == MKTAG('m', 'e', 'a', 'n') && len > 12) { + uint8_t domain[128]; + int domain_len; + + avio_skip(pb, 4); // flags + len -= 12; + + domain_len = avio_get_str(pb, len, domain, sizeof(domain)); + avio_skip(pb, len - domain_len); + if (!strcmp(domain, "org.hydrogenaudio.replaygain")) + return mov_read_replaygain(c, pb, end - avio_tell(pb)); + } + +fail: + av_log(c->fc, AV_LOG_VERBOSE, + "Unhandled or malformed custom metadata of size %"PRId64"\n", atom.size); + return 0; +} + static int mov_read_meta(MOVContext *c, AVIOContext *pb, MOVAtom atom) { while (atom.size > 8) { @@ -3051,6 +3134,7 @@ static const MOVParseTableEntry mov_default_parse_table[] = { { MKTAG('h','v','c','C'), mov_read_glbl }, { MKTAG('u','u','i','d'), mov_read_uuid }, { MKTAG('C','i','n', 0x8e), mov_read_targa_y216 }, +{ MKTAG('-','-','-','-'), mov_read_custom }, { 0, NULL } }; @@ -3509,6 +3593,19 @@ static int mov_read_header(AVFormatContext *s) ff_rfps_calculate(s); + for (i = 0; i < s->nb_streams; i++) { + AVStream *st = s->streams[i]; + + if (st->codec->codec_type != AVMEDIA_TYPE_AUDIO) + continue; + + err = ff_replaygain_export(st, s->metadata); + if (err < 0) { + mov_read_close(s); + return err; + } + } + return 0; }