#include "libavutil/intreadwrite.h"
#include "libavutil/intfloat.h"
#include "libavutil/mathematics.h"
+#include "libavutil/time_internal.h"
#include "libavutil/avstring.h"
#include "libavutil/dict.h"
#include "libavcodec/ac3tab.h"
#include "libavcodec/get_bits.h"
#include "id3v1.h"
#include "mov_chan.h"
+#include "replaygain.h"
#if CONFIG_ZLIB
#include <zlib.h>
snprintf(buf, sizeof(buf), "%d", current);
else
snprintf(buf, sizeof(buf), "%d/%d", current, total);
+ c->fc->event_flags |= AVFMT_EVENT_FLAG_METADATA_UPDATED;
av_dict_set(&c->fc->metadata, key, buf, 0);
return 0;
avio_r8(pb);
snprintf(buf, sizeof(buf), "%d", avio_r8(pb));
+ c->fc->event_flags |= AVFMT_EVENT_FLAG_METADATA_UPDATED;
av_dict_set(&c->fc->metadata, key, buf, 0);
return 0;
char buf[16];
snprintf(buf, sizeof(buf), "%d", avio_r8(pb));
+ c->fc->event_flags |= AVFMT_EVENT_FLAG_METADATA_UPDATED;
av_dict_set(&c->fc->metadata, key, buf, 0);
return 0;
if (genre < 1 || genre > ID3v1_GENRE_MAX)
return 0;
snprintf(buf, sizeof(buf), "%s", ff_id3v1_genre_str[genre-1]);
+ c->fc->event_flags |= AVFMT_EVENT_FLAG_METADATA_UPDATED;
av_dict_set(&c->fc->metadata, key, buf, 0);
return 0;
return 0;
}
+static int mov_metadata_loci(MOVContext *c, AVIOContext *pb, unsigned len)
+{
+ char language[4] = { 0 };
+ char buf[100];
+ uint16_t langcode = 0;
+ double longitude, latitude;
+ const char *key = "location";
+
+ if (len < 4 + 2 + 1 + 1 + 4 + 4 + 4)
+ return AVERROR_INVALIDDATA;
+
+ avio_skip(pb, 4); // version+flags
+ langcode = avio_rb16(pb);
+ ff_mov_lang_to_iso639(langcode, language);
+ len -= 6;
+
+ len -= avio_get_str(pb, len, buf, sizeof(buf)); // place name
+ if (len < 1)
+ return AVERROR_INVALIDDATA;
+ avio_skip(pb, 1); // role
+ len -= 1;
+
+ if (len < 14)
+ return AVERROR_INVALIDDATA;
+ longitude = ((int32_t) avio_rb32(pb)) / (float) (1 << 16);
+ latitude = ((int32_t) avio_rb32(pb)) / (float) (1 << 16);
+
+ // Try to output in the same format as the ?xyz field
+ snprintf(buf, sizeof(buf), "%+08.4f%+09.4f/", latitude, longitude);
+ if (*language && strcmp(language, "und")) {
+ char key2[16];
+ snprintf(key2, sizeof(key2), "%s-%s", key, language);
+ av_dict_set(&c->fc->metadata, key2, buf, 0);
+ }
+ c->fc->event_flags |= AVFMT_EVENT_FLAG_METADATA_UPDATED;
+ return av_dict_set(&c->fc->metadata, key, buf, 0);
+}
+
static int mov_read_udta_string(MOVContext *c, AVIOContext *pb, MOVAtom atom)
{
#ifdef MOV_EXPORT_ALL_METADATA
case MKTAG(0xa9,'t','o','o'):
case MKTAG(0xa9,'s','w','r'): key = "encoder"; break;
case MKTAG(0xa9,'e','n','c'): key = "encoder"; break;
+ case MKTAG(0xa9,'x','y','z'): key = "location"; break;
case MKTAG( 'd','e','s','c'): key = "description";break;
case MKTAG( 'l','d','e','s'): key = "synopsis"; break;
case MKTAG( 't','v','s','h'): key = "show"; break;
parse = mov_metadata_int8_no_padding; break;
case MKTAG( 'p','g','a','p'): key = "gapless_playback";
parse = mov_metadata_int8_no_padding; break;
+ case MKTAG( 'l','o','c','i'):
+ return mov_metadata_loci(c, pb, atom.size);
}
if (c->itunes_metadata && atom.size > 8) {
avio_read(pb, str, str_size);
str[str_size] = 0;
}
+ c->fc->event_flags |= AVFMT_EVENT_FLAG_METADATA_UPDATED;
av_dict_set(&c->fc->metadata, key, str, 0);
if (*language && strcmp(language, "und")) {
snprintf(key2, sizeof(key2), "%s-%s", key, language);
return 0;
}
-int ff_mov_read_esds(AVFormatContext *fc, AVIOContext *pb, MOVAtom atom)
+int ff_mov_read_esds(AVFormatContext *fc, AVIOContext *pb)
{
AVStream *st;
int tag;
static int mov_read_esds(MOVContext *c, AVIOContext *pb, MOVAtom atom)
{
- return ff_mov_read_esds(c->fc, pb, atom);
+ return ff_mov_read_esds(c->fc, pb);
}
static int mov_read_dac3(MOVContext *c, AVIOContext *pb, MOVAtom atom)
return 0;
st = c->fc->streams[c->fc->nb_streams-1];
- ff_get_wav_header(pb, st->codec, atom.size);
-
- return 0;
+ return ff_get_wav_header(pb, st->codec, atom.size);
}
static int mov_read_pasp(MOVContext *c, AVIOContext *pb, MOVAtom atom)
static int mov_read_moof(MOVContext *c, AVIOContext *pb, MOVAtom atom)
{
- c->fragment.moof_offset = avio_tell(pb) - 8;
+ c->fragment.moof_offset = c->fragment.implicit_offset = avio_tell(pb) - 8;
av_dlog(c->fc, "moof offset %"PRIx64"\n", c->fragment.moof_offset);
return mov_read_default(c, pb, atom);
}
{
char buffer[32];
if (time) {
- struct tm *ptm;
+ struct tm *ptm, tmbuf;
time -= 2082844800; /* seconds between 1904-01-01 and Epoch */
- ptm = gmtime(&time);
+ ptm = gmtime_r(&time, &tmbuf);
if (!ptm) return;
- strftime(buffer, sizeof(buffer), "%Y-%m-%d %H:%M:%S", ptm);
- av_dict_set(metadata, "creation_time", buffer, 0);
+ if (strftime(buffer, sizeof(buffer), "%Y-%m-%d %H:%M:%S", ptm))
+ av_dict_set(metadata, "creation_time", buffer, 0);
}
}
return 0;
}
+static int mov_read_colr(MOVContext *c, AVIOContext *pb, MOVAtom atom)
+{
+ AVStream *st;
+ char color_parameter_type[5] = { 0 };
+ int color_primaries, color_trc, color_matrix;
+
+ if (c->fc->nb_streams < 1)
+ return 0;
+ st = c->fc->streams[c->fc->nb_streams - 1];
+
+ avio_read(pb, color_parameter_type, 4);
+ if (strncmp(color_parameter_type, "nclx", 4) &&
+ strncmp(color_parameter_type, "nclc", 4)) {
+ av_log(c->fc, AV_LOG_WARNING, "unsupported color_parameter_type %s\n",
+ color_parameter_type);
+ return 0;
+ }
+
+ color_primaries = avio_rb16(pb);
+ color_trc = avio_rb16(pb);
+ color_matrix = avio_rb16(pb);
+
+ av_dlog(c->fc, "%s: pri %"PRIu16" trc %"PRIu16" matrix %"PRIu16"",
+ color_parameter_type, color_primaries, color_trc, color_matrix);
+
+ if (c->isom) {
+ uint8_t color_range = avio_r8(pb) >> 7;
+ av_dlog(c->fc, " full %"PRIu8"", color_range)
+ if (color_range)
+ st->codec->color_range = AVCOL_RANGE_JPEG;
+ else
+ st->codec->color_range = AVCOL_RANGE_MPEG;
+ /* 14496-12 references JPEG XR specs (rather than the more complete
+ * 23001-8) so some adjusting is required */
+ if (color_primaries >= AVCOL_PRI_FILM)
+ color_primaries = AVCOL_PRI_UNSPECIFIED;
+ if ((color_trc >= AVCOL_TRC_LINEAR &&
+ color_trc <= AVCOL_TRC_LOG_SQRT) ||
+ color_trc >= AVCOL_TRC_BT2020_10)
+ color_trc = AVCOL_TRC_UNSPECIFIED;
+ if (color_matrix >= AVCOL_SPC_BT2020_NCL)
+ color_matrix = AVCOL_SPC_UNSPECIFIED;
+ st->codec->color_primaries = color_primaries;
+ st->codec->color_trc = color_trc;
+ st->codec->colorspace = color_matrix;
+ } else {
+ /* color primaries, Table 4-4 */
+ switch (color_primaries) {
+ case 1: st->codec->color_primaries = AVCOL_PRI_BT709; break;
+ case 5: st->codec->color_primaries = AVCOL_PRI_SMPTE170M; break;
+ case 6: st->codec->color_primaries = AVCOL_PRI_SMPTE240M; break;
+ }
+ /* color transfer, Table 4-5 */
+ switch (color_trc) {
+ case 1: st->codec->color_trc = AVCOL_TRC_BT709; break;
+ case 7: st->codec->color_trc = AVCOL_TRC_SMPTE240M; break;
+ }
+ /* color matrix, Table 4-6 */
+ switch (color_matrix) {
+ case 1: st->codec->colorspace = AVCOL_SPC_BT709; break;
+ case 6: st->codec->colorspace = AVCOL_SPC_BT470BG; break;
+ case 7: st->codec->colorspace = AVCOL_SPC_SMPTE240M; break;
+ }
+ }
+ av_dlog(c->fc, "\n")
+
+ return 0;
+}
+
static int mov_read_fiel(MOVContext *c, AVIOContext *pb, MOVAtom atom)
{
AVStream *st;
static void mov_parse_stsd_video(MOVContext *c, AVIOContext *pb,
AVStream *st, MOVStreamContext *sc)
{
+ uint8_t codec_name[32];
unsigned int color_depth, len, j;
int color_greyscale;
int color_table_id;
len = avio_r8(pb); /* codec name, pascal string */
if (len > 31)
len = 31;
- mov_read_mac_string(c, pb, len, st->codec->codec_name, 32);
+ mov_read_mac_string(c, pb, len, codec_name, sizeof(codec_name));
if (len < 31)
avio_skip(pb, 31 - len);
+
+ if (codec_name[0])
+ av_dict_set(&st->metadata, "encoder", codec_name, 0);
+
/* codec_tag YV12 triggers an UV swap in rawdec.c */
- if (!memcmp(st->codec->codec_name, "Planar Y'CbCr 8-bit 4:2:0", 25))
+ if (!memcmp(codec_name, "Planar Y'CbCr 8-bit 4:2:0", 25))
st->codec->codec_tag = MKTAG('I', '4', '2', '0');
/* Flash Media Server uses tag H263 with Sorenson Spark */
if (st->codec->codec_tag == MKTAG('H','2','6','3') &&
- !memcmp(st->codec->codec_name, "Sorenson H263", 13))
+ !memcmp(codec_name, "Sorenson H263", 13))
st->codec->codec_id = AV_CODEC_ID_FLV1;
st->codec->bits_per_coded_sample = avio_rb16(pb); /* depth */
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) {
int width;
int height;
int64_t disp_transform[2];
- int display_matrix[3][2];
+ int display_matrix[3][3];
AVStream *st;
MOVStreamContext *sc;
int version;
//read in the display matrix (outlined in ISO 14496-12, Section 6.2.2)
// they're kept in fixed point format through all calculations
- // ignore u,v,z b/c we don't need the scale factor to calc aspect ratio
+ // save u,v,z to store the whole matrix in the AV_PKT_DATA_DISPLAYMATRIX
+ // side data, but the scale factor is not needed to calculate aspect ratio
for (i = 0; i < 3; i++) {
display_matrix[i][0] = avio_rb32(pb); // 16.16 fixed point
display_matrix[i][1] = avio_rb32(pb); // 16.16 fixed point
- avio_rb32(pb); // 2.30 fixed point (not used)
+ display_matrix[i][2] = avio_rb32(pb); // 2.30 fixed point
}
width = avio_rb32(pb); // 16.16 fixed point track width
sc->width = width >> 16;
sc->height = height >> 16;
+ // save the matrix when it is not the default identity
+ if (display_matrix[0][0] != (1 << 16) ||
+ display_matrix[1][1] != (1 << 16) ||
+ display_matrix[2][2] != (1 << 30) ||
+ display_matrix[0][1] || display_matrix[0][2] ||
+ display_matrix[1][0] || display_matrix[1][2] ||
+ display_matrix[2][0] || display_matrix[2][1]) {
+ int i, j;
+
+ av_freep(&sc->display_matrix);
+ sc->display_matrix = av_malloc(sizeof(int32_t) * 9);
+ if (!sc->display_matrix)
+ return AVERROR(ENOMEM);
+
+ for (i = 0; i < 3; i++)
+ for (j = 0; j < 3; j++)
+ sc->display_matrix[i * 3 + j] = display_matrix[j][i];
+ }
+
// transform the display width/height according to the matrix
// skip this if the display matrix is the default identity matrix
// or if it is rotating the picture, ex iPhone 3GS
}
frag->base_data_offset = flags & MOV_TFHD_BASE_DATA_OFFSET ?
- avio_rb64(pb) : frag->moof_offset;
+ avio_rb64(pb) : flags & MOV_TFHD_DEFAULT_BASE_IS_MOOF ?
+ frag->moof_offset : frag->implicit_offset;
frag->stsd_id = flags & MOV_TFHD_STSD_ID ? avio_rb32(pb) : trex->stsd_id;
frag->duration = flags & MOV_TFHD_DEFAULT_DURATION ?
return 0;
}
+static int mov_read_tfdt(MOVContext *c, AVIOContext *pb, MOVAtom atom)
+{
+ MOVFragment *frag = &c->fragment;
+ AVStream *st = NULL;
+ MOVStreamContext *sc;
+ int version, i;
+
+ for (i = 0; i < c->fc->nb_streams; i++) {
+ if (c->fc->streams[i]->id == frag->track_id) {
+ st = c->fc->streams[i];
+ break;
+ }
+ }
+ if (!st) {
+ av_log(c->fc, AV_LOG_ERROR, "could not find corresponding track id %d\n", frag->track_id);
+ return AVERROR_INVALIDDATA;
+ }
+ sc = st->priv_data;
+ if (sc->pseudo_stream_id + 1 != frag->stsd_id)
+ return 0;
+ version = avio_r8(pb);
+ avio_rb24(pb); /* flags */
+ if (version) {
+ sc->track_end = avio_rb64(pb);
+ } else {
+ sc->track_end = avio_rb32(pb);
+ }
+ return 0;
+}
+
static int mov_read_trun(MOVContext *c, AVIOContext *pb, MOVAtom atom)
{
MOVFragment *frag = &c->fragment;
if (pb->eof_reached)
return AVERROR_EOF;
- frag->moof_offset = offset;
+ frag->implicit_offset = offset;
st->duration = sc->track_end = dts + sc->time_offset;
return 0;
}
{ MKTAG('a','v','s','s'), mov_read_extradata },
{ MKTAG('c','h','p','l'), mov_read_chpl },
{ MKTAG('c','o','6','4'), mov_read_stco },
+{ MKTAG('c','o','l','r'), mov_read_colr },
{ MKTAG('c','t','t','s'), mov_read_ctts }, /* composition time to sample */
{ MKTAG('d','i','n','f'), mov_read_default },
{ MKTAG('d','r','e','f'), mov_read_dref },
{ MKTAG('s','t','t','s'), mov_read_stts },
{ MKTAG('s','t','z','2'), mov_read_stsz }, /* compact sample size */
{ MKTAG('t','k','h','d'), mov_read_tkhd }, /* track header */
+{ MKTAG('t','f','d','t'), mov_read_tfdt },
{ MKTAG('t','f','h','d'), mov_read_tfhd }, /* track fragment header */
{ MKTAG('t','r','a','k'), mov_read_trak },
{ MKTAG('t','r','a','f'), mov_read_default },
{ MKTAG('d','v','c','1'), mov_read_dvc1 },
{ MKTAG('s','b','g','p'), mov_read_sbgp },
{ MKTAG('h','v','c','C'), mov_read_glbl },
+{ MKTAG('-','-','-','-'), mov_read_custom },
{ 0, NULL }
};
av_freep(&sc->stts_data);
av_freep(&sc->stps_data);
av_freep(&sc->rap_group);
+ av_freep(&sc->display_matrix);
}
if (mov->dv_demux) {
- for (i = 0; i < mov->dv_fctx->nb_streams; i++) {
- av_freep(&mov->dv_fctx->streams[i]->codec);
- av_freep(&mov->dv_fctx->streams[i]);
- }
- av_freep(&mov->dv_fctx);
- av_freep(&mov->dv_demux);
+ avformat_free_context(mov->dv_fctx);
+ mov->dv_fctx = NULL;
}
av_freep(&mov->trex_data);
MOVStreamContext *sc = st->priv_data;
if (st->codec->codec_type == AVMEDIA_TYPE_SUBTITLE) {
- if (st->codec->width <= 0 && st->codec->width <= 0) {
+ if (st->codec->width <= 0 || st->codec->height <= 0) {
st->codec->width = sc->width;
st->codec->height = sc->height;
}
}
}
+ for (i = 0; i < s->nb_streams; i++) {
+ AVStream *st = s->streams[i];
+ MOVStreamContext *sc = st->priv_data;
+
+ switch (st->codec->codec_type) {
+ case AVMEDIA_TYPE_AUDIO:
+ err = ff_replaygain_export(st, s->metadata);
+ if (err < 0) {
+ mov_read_close(s);
+ return err;
+ }
+ break;
+ case AVMEDIA_TYPE_VIDEO:
+ if (sc->display_matrix) {
+ AVPacketSideData *sd, *tmp;
+
+ tmp = av_realloc_array(st->side_data,
+ st->nb_side_data + 1, sizeof(*tmp));
+ if (!tmp)
+ return AVERROR(ENOMEM);
+
+ st->side_data = tmp;
+ st->nb_side_data++;
+
+ sd = &st->side_data[st->nb_side_data - 1];
+ sd->type = AV_PKT_DATA_DISPLAYMATRIX;
+ sd->size = sizeof(int32_t) * 9;
+ sd->data = (uint8_t*)sc->display_matrix;
+ sc->display_matrix = NULL;
+ }
+ break;
+ }
+ }
+
return 0;
}