/*
* MOV demuxer
- * Copyright (c) 2001 Fabrice Bellard.
+ * Copyright (c) 2001 Fabrice Bellard
*
* This file is part of FFmpeg.
*
//#define DEBUG
#include "libavutil/intreadwrite.h"
+#include "libavutil/avstring.h"
#include "avformat.h"
#include "riff.h"
#include "isom.h"
MOVStts *stts_data;
unsigned int ctts_count;
MOVStts *ctts_data;
- unsigned int edit_count; /* number of 'edit' (elst atom) */
- unsigned int sample_to_chunk_sz;
- MOVStsc *sample_to_chunk;
- int sample_to_ctime_index;
- int sample_to_ctime_sample;
+ unsigned int stsc_count;
+ MOVStsc *stsc_data;
+ int ctts_index;
+ int ctts_sample;
unsigned int sample_size;
unsigned int sample_count;
int *sample_sizes;
int *keyframes;
int time_scale;
int time_rate;
+ int time_offset; ///< time offset of the first edit list entry
int current_sample;
unsigned int bytes_per_frame;
unsigned int samples_per_frame;
entries = get_be32(pb);
if (entries >= UINT_MAX / sizeof(*sc->drefs))
return -1;
- sc->drefs_count = entries;
sc->drefs = av_mallocz(entries * sizeof(*sc->drefs));
+ if (!sc->drefs)
+ return AVERROR(ENOMEM);
+ sc->drefs_count = entries;
for (i = 0; i < sc->drefs_count; i++) {
MOVDref *dref = &sc->drefs[i];
const int den = get_be32(pb);
AVStream * const st = c->fc->streams[c->fc->nb_streams-1];
if (den != 0) {
- if ((st->sample_aspect_ratio.den && den != st->sample_aspect_ratio.den) ||
- (st->sample_aspect_ratio.num && num != st->sample_aspect_ratio.num))
+ if ((st->sample_aspect_ratio.den != 1 || st->sample_aspect_ratio.num) && // default
+ (den != st->sample_aspect_ratio.den || num != st->sample_aspect_ratio.num))
av_log(c->fc, AV_LOG_WARNING,
- "sample aspect ratio already set, overriding by 'pasp' atom\n");
+ "sample aspect ratio already set to %d:%d, overriding by 'pasp' atom\n",
+ st->sample_aspect_ratio.num, st->sample_aspect_ratio.den);
st->sample_aspect_ratio.num = num;
st->sample_aspect_ratio.den = den;
}
AVStream *st = c->fc->streams[c->fc->nb_streams-1];
MOVStreamContext *sc = st->priv_data;
int version = get_byte(pb);
- int lang;
+ char language[4] = {0};
+ unsigned lang;
if (version > 1)
return -1; /* unsupported */
st->duration = (version == 1) ? get_be64(pb) : get_be32(pb); /* duration */
lang = get_be16(pb); /* language */
- ff_mov_lang_to_iso639(lang, st->language);
+ if (ff_mov_lang_to_iso639(lang, language))
+ av_metadata_set(&st->metadata, "language", language);
get_be16(pb); /* quality */
return 0;
if(entries >= UINT_MAX/sizeof(int64_t))
return -1;
- sc->chunk_count = entries;
sc->chunk_offsets = av_malloc(entries * sizeof(int64_t));
if (!sc->chunk_offsets)
- return -1;
+ return AVERROR(ENOMEM);
+ sc->chunk_count = entries;
+
if (atom.type == MKTAG('s','t','c','o'))
for(i=0; i<entries; i++)
sc->chunk_offsets[i] = get_be32(pb);
switch (st->codec->codec_id) {
#if CONFIG_DV_DEMUXER
case CODEC_ID_DVAUDIO:
- c->dv_fctx = av_alloc_format_context();
+ c->dv_fctx = avformat_alloc_context();
c->dv_demux = dv_init_demux(c->dv_fctx);
if (!c->dv_demux) {
av_log(c->fc, AV_LOG_ERROR, "dv demux context init error\n");
entries = get_be32(pb);
- if(entries >= UINT_MAX / sizeof(*sc->sample_to_chunk))
- return -1;
-
dprintf(c->fc, "track[%i].stsc.entries = %i\n", c->fc->nb_streams-1, entries);
- sc->sample_to_chunk_sz = entries;
- sc->sample_to_chunk = av_malloc(entries * sizeof(*sc->sample_to_chunk));
- if (!sc->sample_to_chunk)
+ if(entries >= UINT_MAX / sizeof(*sc->stsc_data))
return -1;
+ sc->stsc_data = av_malloc(entries * sizeof(*sc->stsc_data));
+ if (!sc->stsc_data)
+ return AVERROR(ENOMEM);
+ sc->stsc_count = entries;
+
for(i=0; i<entries; i++) {
- sc->sample_to_chunk[i].first = get_be32(pb);
- sc->sample_to_chunk[i].count = get_be32(pb);
- sc->sample_to_chunk[i].id = get_be32(pb);
+ sc->stsc_data[i].first = get_be32(pb);
+ sc->stsc_data[i].count = get_be32(pb);
+ sc->stsc_data[i].id = get_be32(pb);
}
return 0;
}
entries = get_be32(pb);
+ dprintf(c->fc, "keyframe_count = %d\n", entries);
+
if(entries >= UINT_MAX / sizeof(int))
return -1;
-
- sc->keyframe_count = entries;
-
- dprintf(c->fc, "keyframe_count = %d\n", sc->keyframe_count);
-
sc->keyframes = av_malloc(entries * sizeof(int));
if (!sc->keyframes)
- return -1;
+ return AVERROR(ENOMEM);
+ sc->keyframe_count = entries;
+
for(i=0; i<entries; i++) {
sc->keyframes[i] = get_be32(pb);
//dprintf(c->fc, "keyframes[]=%d\n", sc->keyframes[i]);
if (!sc->sample_size) /* do not overwrite value computed in stsd */
sc->sample_size = sample_size;
entries = get_be32(pb);
- if(entries >= UINT_MAX / sizeof(int))
- return -1;
+
+ dprintf(c->fc, "sample_size = %d sample_count = %d\n", sc->sample_size, entries);
sc->sample_count = entries;
if (sample_size)
return 0;
- dprintf(c->fc, "sample_size = %d sample_count = %d\n", sc->sample_size, sc->sample_count);
-
+ if(entries >= UINT_MAX / sizeof(int))
+ return -1;
sc->sample_sizes = av_malloc(entries * sizeof(int));
if (!sc->sample_sizes)
- return -1;
+ return AVERROR(ENOMEM);
+
for(i=0; i<entries; i++)
sc->sample_sizes[i] = get_be32(pb);
return 0;
get_byte(pb); /* version */
get_be24(pb); /* flags */
entries = get_be32(pb);
+
+ dprintf(c->fc, "track[%i].stts.entries = %i\n", c->fc->nb_streams-1, entries);
+
if(entries >= UINT_MAX / sizeof(*sc->stts_data))
return -1;
-
- sc->stts_count = entries;
sc->stts_data = av_malloc(entries * sizeof(*sc->stts_data));
if (!sc->stts_data)
- return -1;
- dprintf(c->fc, "track[%i].stts.entries = %i\n", c->fc->nb_streams-1, entries);
-
- sc->time_rate=0;
+ return AVERROR(ENOMEM);
+ sc->stts_count = entries;
for(i=0; i<entries; i++) {
int sample_duration;
sc->stts_data[i].count= sample_count;
sc->stts_data[i].duration= sample_duration;
- sc->time_rate= ff_gcd(sc->time_rate, sample_duration);
+ sc->time_rate= av_gcd(sc->time_rate, sample_duration);
dprintf(c->fc, "sample_count=%d, sample_duration=%d\n",sample_count,sample_duration);
get_byte(pb); /* version */
get_be24(pb); /* flags */
entries = get_be32(pb);
+
+ dprintf(c->fc, "track[%i].ctts.entries = %i\n", c->fc->nb_streams-1, entries);
+
if(entries >= UINT_MAX / sizeof(*sc->ctts_data))
return -1;
-
- sc->ctts_count = entries;
sc->ctts_data = av_malloc(entries * sizeof(*sc->ctts_data));
if (!sc->ctts_data)
- return -1;
- dprintf(c->fc, "track[%i].ctts.entries = %i\n", c->fc->nb_streams-1, entries);
+ return AVERROR(ENOMEM);
+ sc->ctts_count = entries;
for(i=0; i<entries; i++) {
int count =get_be32(pb);
sc->ctts_data[i].count = count;
sc->ctts_data[i].duration= duration;
- sc->time_rate= ff_gcd(sc->time_rate, FFABS(duration));
+ sc->time_rate= av_gcd(sc->time_rate, FFABS(duration));
}
return 0;
}
unsigned int stss_index = 0;
unsigned int i, j;
+ /* adjust first dts according to edit list */
+ if (sc->time_offset) {
+ assert(sc->time_offset % sc->time_rate == 0);
+ current_dts = - (sc->time_offset / sc->time_rate);
+ }
+
/* only use old uncompressed audio chunk demuxing when stts specifies it */
if (!(st->codec->codec_type == CODEC_TYPE_AUDIO &&
sc->stts_count == 1 && sc->stts_data[0].duration == 1)) {
st->nb_frames = sc->sample_count;
for (i = 0; i < sc->chunk_count; i++) {
current_offset = sc->chunk_offsets[i];
- if (stsc_index + 1 < sc->sample_to_chunk_sz &&
- i + 1 == sc->sample_to_chunk[stsc_index + 1].first)
+ if (stsc_index + 1 < sc->stsc_count &&
+ i + 1 == sc->stsc_data[stsc_index + 1].first)
stsc_index++;
- for (j = 0; j < sc->sample_to_chunk[stsc_index].count; j++) {
+ for (j = 0; j < sc->stsc_data[stsc_index].count; j++) {
if (current_sample >= sc->sample_count) {
av_log(mov->fc, AV_LOG_ERROR, "wrong sample count\n");
goto out;
}
sample_size = sc->sample_size > 0 ? sc->sample_size : sc->sample_sizes[current_sample];
if(sc->pseudo_stream_id == -1 ||
- sc->sample_to_chunk[stsc_index].id - 1 == sc->pseudo_stream_id) {
+ sc->stsc_data[stsc_index].id - 1 == sc->pseudo_stream_id) {
av_add_index_entry(st, current_offset, current_dts, sample_size, distance,
keyframe ? AVINDEX_KEYFRAME : 0);
dprintf(mov->fc, "AVIndex stream %d, sample %d, offset %"PRIx64", dts %"PRId64", "
unsigned int frames = 1;
for (i = 0; i < sc->chunk_count; i++) {
current_offset = sc->chunk_offsets[i];
- if (stsc_index + 1 < sc->sample_to_chunk_sz &&
- i + 1 == sc->sample_to_chunk[stsc_index + 1].first)
+ if (stsc_index + 1 < sc->stsc_count &&
+ i + 1 == sc->stsc_data[stsc_index + 1].first)
stsc_index++;
- chunk_samples = sc->sample_to_chunk[stsc_index].count;
+ chunk_samples = sc->stsc_data[stsc_index].count;
/* get chunk size, beware of alaw/ulaw/mace */
if (sc->samples_per_frame > 0 &&
(chunk_samples * sc->bytes_per_frame % sc->samples_per_frame == 0)) {
st->priv_data = sc;
st->codec->codec_type = CODEC_TYPE_DATA;
- st->start_time = 0; /* XXX: check */
sc->ffindex = st->index;
if ((ret = mov_read_default(c, pb, atom)) < 0)
return ret;
/* sanity checks */
- if(sc->chunk_count && (!sc->stts_count || !sc->sample_to_chunk_sz ||
+ if(sc->chunk_count && (!sc->stts_count || !sc->stsc_count ||
(!sc->sample_size && !sc->sample_count))){
av_log(c->fc, AV_LOG_ERROR, "stream %d, missing mandatory atoms, broken header\n",
st->index);
av_set_pts_info(st, 64, sc->time_rate, sc->time_scale);
if (st->codec->codec_type == CODEC_TYPE_AUDIO &&
- !st->codec->frame_size && sc->stts_count == 1)
- st->codec->frame_size = av_rescale(sc->time_rate, st->codec->sample_rate, sc->time_scale);
+ !st->codec->frame_size && sc->stts_count == 1) {
+ st->codec->frame_size = av_rescale(sc->stts_data[0].duration,
+ st->codec->sample_rate, sc->time_scale);
+ dprintf(c->fc, "frame size %d\n", st->codec->frame_size);
+ }
if(st->duration != AV_NOPTS_VALUE){
assert(st->duration % sc->time_rate == 0);
/* Do not need those anymore. */
av_freep(&sc->chunk_offsets);
- av_freep(&sc->sample_to_chunk);
+ av_freep(&sc->stsc_data);
av_freep(&sc->sample_sizes);
av_freep(&sc->keyframes);
av_freep(&sc->stts_data);
static int mov_read_udta_string(MOVContext *c, ByteIOContext *pb, MOVAtom atom)
{
- char *str = NULL;
- int size;
+ char str[1024], key2[16], language[4] = {0};
+ const char *key = NULL;
uint16_t str_size;
if (c->itunes_metadata) {
get_be32(pb); // type
get_be32(pb); // unknown
str_size = data_size - 16;
+ atom.size -= 16;
} else return 0;
} else {
str_size = get_be16(pb); // string length
- get_be16(pb); // language
+ ff_mov_lang_to_iso639(get_be16(pb), language);
+ atom.size -= 4;
}
switch (atom.type) {
- case MKTAG(0xa9,'n','a','m'):
- str = c->fc->title; size = sizeof(c->fc->title); break;
+ case MKTAG(0xa9,'n','a','m'): key = "title"; break;
+ case MKTAG(0xa9,'a','u','t'):
case MKTAG(0xa9,'A','R','T'):
- case MKTAG(0xa9,'w','r','t'):
- str = c->fc->author; size = sizeof(c->fc->author); break;
- case MKTAG(0xa9,'c','p','y'):
- str = c->fc->copyright; size = sizeof(c->fc->copyright); break;
+ case MKTAG(0xa9,'w','r','t'): key = "author"; break;
+ case MKTAG(0xa9,'c','p','y'): key = "copyright"; break;
case MKTAG(0xa9,'c','m','t'):
- case MKTAG(0xa9,'i','n','f'):
- str = c->fc->comment; size = sizeof(c->fc->comment); break;
- case MKTAG(0xa9,'a','l','b'):
- str = c->fc->album; size = sizeof(c->fc->album); break;
+ case MKTAG(0xa9,'i','n','f'): key = "comment"; break;
+ case MKTAG(0xa9,'a','l','b'): key = "album"; break;
+ case MKTAG(0xa9,'d','a','y'): key = "year"; break;
+ case MKTAG(0xa9,'g','e','n'): key = "genre"; break;
+ case MKTAG(0xa9,'t','o','o'):
+ case MKTAG(0xa9,'e','n','c'): key = "muxer"; break;
}
- if (!str)
+ if (!key)
return 0;
- get_buffer(pb, str, FFMIN(size, str_size));
- dprintf(c->fc, "%.4s %s\n", (char*)&atom.type, str);
+ if (atom.size < 0)
+ return -1;
+
+ str_size = FFMIN3(sizeof(str)-1, str_size, atom.size);
+ get_buffer(pb, str, str_size);
+ str[str_size] = 0;
+ av_metadata_set(&c->fc->metadata, key, str);
+ if (*language && strcmp(language, "und")) {
+ snprintf(key2, sizeof(key2), "%s-%s", key, language);
+ av_metadata_set(&c->fc->metadata, key2, str);
+ }
+ dprintf(c->fc, "%.4s %s %d %lld\n", (char*)&atom.type, str, str_size, atom.size);
return 0;
}
}
st->id = (int)get_be32(pb); /* track id (NOT 0 !)*/
get_be32(pb); /* reserved */
- st->start_time = 0; /* check */
+
/* highlevel (considering edits) duration in movie timebase */
(version == 1) ? get_be64(pb) : get_be32(pb);
get_be32(pb); /* reserved */
if ((uint64_t)c->trex_count+1 >= UINT_MAX / sizeof(*c->trex_data))
return -1;
- c->trex_data = av_realloc(c->trex_data, (c->trex_count+1)*sizeof(*c->trex_data));
- if (!c->trex_data)
+ trex = av_realloc(c->trex_data, (c->trex_count+1)*sizeof(*c->trex_data));
+ if (!trex)
return AVERROR(ENOMEM);
+ c->trex_data = trex;
trex = &c->trex_data[c->trex_count++];
get_byte(pb); /* version */
get_be24(pb); /* flags */
if (flags & 0x001) data_offset = get_be32(pb);
if (flags & 0x004) first_sample_flags = get_be32(pb);
if (flags & 0x800) {
+ MOVStts *ctts_data;
if ((uint64_t)entries+sc->ctts_count >= UINT_MAX/sizeof(*sc->ctts_data))
return -1;
- sc->ctts_data = av_realloc(sc->ctts_data,
- (entries+sc->ctts_count)*sizeof(*sc->ctts_data));
- if (!sc->ctts_data)
+ ctts_data = av_realloc(sc->ctts_data,
+ (entries+sc->ctts_count)*sizeof(*sc->ctts_data));
+ if (!ctts_data)
return AVERROR(ENOMEM);
+ sc->ctts_data = ctts_data;
}
dts = st->duration;
offset = frag->base_data_offset + data_offset;
cmov_data = av_malloc(cmov_len);
if (!cmov_data)
- return -1;
+ return AVERROR(ENOMEM);
moov_data = av_malloc(moov_len);
if (!moov_data) {
av_free(cmov_data);
- return -1;
+ return AVERROR(ENOMEM);
}
get_buffer(pb, cmov_data, cmov_len);
if(uncompress (moov_data, (uLongf *) &moov_len, (const Bytef *)cmov_data, cmov_len) != Z_OK)
get_byte(pb); /* version */
get_be24(pb); /* flags */
- edit_count= sc->edit_count = get_be32(pb); /* entries */
+ edit_count = get_be32(pb); /* entries */
for(i=0; i<edit_count; i++){
int time;
get_be32(pb); /* Track duration */
time = get_be32(pb); /* Media time */
get_be32(pb); /* Media rate */
- if (time != 0)
- av_log(c->fc, AV_LOG_WARNING, "edit list not starting at 0, "
- "a/v desync might occur, patch welcome\n");
+ if (i == 0 && time != -1) {
+ sc->time_offset = time;
+ sc->time_rate = av_gcd(sc->time_rate, time);
+ }
}
- dprintf(c->fc, "track[%i].edit_count = %i\n", c->fc->nb_streams-1, sc->edit_count);
+
+ if(edit_count > 1)
+ av_log(c->fc, AV_LOG_WARNING, "multiple edit list entries, "
+ "a/v desync might occur, patch welcome\n");
+
+ dprintf(c->fc, "track[%i].edit_count = %i\n", c->fc->nb_streams-1, edit_count);
return 0;
}
{ MKTAG(0xa9,'A','R','T'), mov_read_udta_string },
{ MKTAG(0xa9,'a','l','b'), mov_read_udta_string },
{ MKTAG(0xa9,'c','m','t'), mov_read_udta_string },
+{ MKTAG(0xa9,'a','u','t'), mov_read_udta_string },
+{ MKTAG(0xa9,'d','a','y'), mov_read_udta_string },
+{ MKTAG(0xa9,'g','e','n'), mov_read_udta_string },
+{ MKTAG(0xa9,'e','n','c'), mov_read_udta_string },
+{ MKTAG(0xa9,'t','o','o'), mov_read_udta_string },
{ 0, NULL }
};
MOVStreamContext *sc = 0;
AVIndexEntry *sample = 0;
int64_t best_dts = INT64_MAX;
- int i;
+ int i, ret;
retry:
for (i = 0; i < s->nb_streams; i++) {
AVStream *st = s->streams[i];
sc->ffindex, sample->pos);
return -1;
}
- av_get_packet(sc->pb, pkt, sample->size);
+ ret = av_get_packet(sc->pb, pkt, sample->size);
+ if (ret < 0)
+ return ret;
#if CONFIG_DV_DEMUXER
if (mov->dv_demux && sc->dv_audio_container) {
dv_produce_packet(mov->dv_demux, pkt, pkt->data, pkt->size);
pkt->stream_index = sc->ffindex;
pkt->dts = sample->timestamp;
if (sc->ctts_data) {
- assert(sc->ctts_data[sc->sample_to_ctime_index].duration % sc->time_rate == 0);
- pkt->pts = pkt->dts + sc->ctts_data[sc->sample_to_ctime_index].duration / sc->time_rate;
+ assert(sc->ctts_data[sc->ctts_index].duration % sc->time_rate == 0);
+ pkt->pts = pkt->dts + sc->ctts_data[sc->ctts_index].duration / sc->time_rate;
/* update ctts context */
- sc->sample_to_ctime_sample++;
- if (sc->sample_to_ctime_index < sc->ctts_count &&
- sc->ctts_data[sc->sample_to_ctime_index].count == sc->sample_to_ctime_sample) {
- sc->sample_to_ctime_index++;
- sc->sample_to_ctime_sample = 0;
+ sc->ctts_sample++;
+ if (sc->ctts_index < sc->ctts_count &&
+ sc->ctts_data[sc->ctts_index].count == sc->ctts_sample) {
+ sc->ctts_index++;
+ sc->ctts_sample = 0;
}
if (sc->wrong_dts)
pkt->dts = AV_NOPTS_VALUE;
for (i = 0; i < sc->ctts_count; i++) {
int next = time_sample + sc->ctts_data[i].count;
if (next > sc->current_sample) {
- sc->sample_to_ctime_index = i;
- sc->sample_to_ctime_sample = sc->current_sample - time_sample;
+ sc->ctts_index = i;
+ sc->ctts_sample = sc->current_sample - time_sample;
break;
}
time_sample = next;
if (stream_index >= s->nb_streams)
return -1;
+ if (sample_time < 0)
+ sample_time = 0;
st = s->streams[stream_index];
sample = mov_seek_stream(st, sample_time, flags);