|
|
|
@ -47,6 +47,7 @@ |
|
|
|
|
#include "libavcodec/get_bits.h" |
|
|
|
|
#include "id3v1.h" |
|
|
|
|
#include "mov_chan.h" |
|
|
|
|
#include "replaygain.h" |
|
|
|
|
|
|
|
|
|
#if CONFIG_ZLIB |
|
|
|
|
#include <zlib.h> |
|
|
|
@ -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; |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|