Newer
Older
color_index = 255;
color_dec = 256 / (color_count - 1);
for (j = 0; j < color_count; j++) {
r = g = b = color_index;
c->palette_control.palette[j] =
(r << 16) | (g << 8) | (b);
color_index -= color_dec;
if (color_index < 0)
color_index = 0;
}
Michael Niedermayer
committed
} else if (st->codec->color_table_id & 0x08) {
/* if flag bit 3 is set, use the default palette */
color_count = 1 << color_depth;
if (color_depth == 2)
else if (color_depth == 4)
else
1022
1023
1024
1025
1026
1027
1028
1029
1030
1031
1032
1033
1034
1035
1036
1037
1038
1039
1040
1041
1042
1043
1044
1045
1046
1047
1048
1049
1050
1051
1052
1053
for (j = 0; j < color_count; j++) {
r = color_table[j * 4 + 0];
g = color_table[j * 4 + 1];
b = color_table[j * 4 + 2];
c->palette_control.palette[j] =
(r << 16) | (g << 8) | (b);
}
} else {
/* load the palette from the file */
color_start = get_be32(pb);
color_count = get_be16(pb);
color_end = get_be16(pb);
for (j = color_start; j <= color_end; j++) {
/* each R, G, or B component is 16 bits;
* only use the top 8 bits; skip alpha bytes
* up front */
get_byte(pb);
get_byte(pb);
r = get_byte(pb);
get_byte(pb);
g = get_byte(pb);
get_byte(pb);
b = get_byte(pb);
get_byte(pb);
c->palette_control.palette[j] =
(r << 16) | (g << 8) | (b);
}
}
Michael Niedermayer
committed
st->codec->palctrl = &c->palette_control;
st->codec->palctrl->palette_changed = 1;
} else
Michael Niedermayer
committed
st->codec->palctrl = NULL;
Baptiste Coudurier
committed
} else if(st->codec->codec_type==CODEC_TYPE_AUDIO) {
uint16_t version = get_be16(pb);
Michael Niedermayer
committed
st->codec->codec_id = codec_get_id(mov_audio_tags, format);
Baptiste Coudurier
committed
get_be16(pb); /* revision level */
get_be32(pb); /* vendor */
Baptiste Coudurier
committed
1065
1066
1067
1068
1069
1070
1071
1072
1073
1074
1075
1076
1077
1078
1079
1080
1081
1082
1083
1084
1085
1086
1087
1088
1089
1090
1091
1092
1093
1094
st->codec->channels = get_be16(pb); /* channel count */
st->codec->bits_per_sample = get_be16(pb); /* sample size */
/* do we need to force to 16 for AMR ? */
/* handle specific s8 codec */
get_be16(pb); /* compression id = 0*/
get_be16(pb); /* packet size = 0 */
st->codec->sample_rate = ((get_be32(pb) >> 16));
switch (st->codec->codec_id) {
case CODEC_ID_PCM_S16BE:
if (st->codec->bits_per_sample == 8)
st->codec->codec_id = CODEC_ID_PCM_S8;
/* fall */
case CODEC_ID_PCM_U8:
if (st->codec->bits_per_sample == 16)
st->codec->codec_id = CODEC_ID_PCM_S16BE;
st->codec->bit_rate = st->codec->sample_rate * 8;
break;
case CODEC_ID_AMR_WB:
st->codec->sample_rate = 16000; /* should really we ? */
st->codec->channels=1; /* really needed */
break;
case CODEC_ID_AMR_NB:
st->codec->sample_rate = 8000; /* should really we ? */
st->codec->channels=1; /* really needed */
break;
default:
break;
}
Baptiste Coudurier
committed
//Read QT version 1 fields. In version 0 theese dont exist
dprintf("version =%d mp4=%d\n",version,c->mp4);
if(version==1) {
get_be32(pb); /* samples per packet */
get_be32(pb); /* bytes per packet */
get_be32(pb); /* bytes per frame */
get_be32(pb); /* bytes per sample */
} else if(version==2) {
get_be32(pb); /* sizeof struct only */
st->codec->sample_rate = av_int2dbl(get_be64(pb)); /* float 64 */
st->codec->channels = get_be32(pb);
get_be32(pb); /* always 0x7F000000 */
get_be32(pb); /* bits per channel if sound is uncompressed */
get_be32(pb); /* lcpm format specific flag */
get_be32(pb); /* bytes per audio packet if constant */
get_be32(pb); /* lpcm frames per audio packet if constant */
Baptiste Coudurier
committed
} else {
/* other codec type, just skip (rtp, mp4s, tmcd ...) */
url_fskip(pb, size - (url_ftell(pb) - start_pos));
Baptiste Coudurier
committed
/* this will read extra atoms at the end (wave, alac, damr, avcC, SMI ...) */
a.size = size - (url_ftell(pb) - start_pos);
if (a.size > 8)
mov_read_default(c, pb, a);
else if (a.size > 0)
url_fskip(pb, a.size);
Michael Niedermayer
committed
if(st->codec->codec_type==CODEC_TYPE_AUDIO && st->codec->sample_rate==0 && sc->time_scale>1) {
st->codec->sample_rate= sc->time_scale;
}
return 0;
}
static int mov_read_stsc(MOVContext *c, ByteIOContext *pb, MOV_atom_t atom)
AVStream *st = c->fc->streams[c->fc->nb_streams-1];
MOVStreamContext *sc = (MOVStreamContext *)st->priv_data;
get_byte(pb); /* version */
get_byte(pb); get_byte(pb); get_byte(pb); /* flags */
entries = get_be32(pb);
if(entries >= UINT_MAX / sizeof(MOV_sample_to_chunk_tbl))
return -1;
av_log(NULL, AV_LOG_DEBUG, "track[%i].stsc.entries = %i\n", c->fc->nb_streams-1, entries);
sc->sample_to_chunk_sz = entries;
sc->sample_to_chunk = (MOV_sample_to_chunk_tbl*) av_malloc(entries * sizeof(MOV_sample_to_chunk_tbl));
if (!sc->sample_to_chunk)
return -1;
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);
}
return 0;
}
Brian Becker
committed
static int mov_read_stss(MOVContext *c, ByteIOContext *pb, MOV_atom_t atom)
{
AVStream *st = c->fc->streams[c->fc->nb_streams-1];
MOVStreamContext *sc = (MOVStreamContext *)st->priv_data;
Brian Becker
committed
get_byte(pb); /* version */
get_byte(pb); get_byte(pb); get_byte(pb); /* flags */
entries = get_be32(pb);
if(entries >= UINT_MAX / sizeof(long))
return -1;
Brian Becker
committed
sc->keyframe_count = entries;
#ifdef DEBUG
av_log(NULL, AV_LOG_DEBUG, "keyframe_count = %ld\n", sc->keyframe_count);
#endif
sc->keyframes = (long*) av_malloc(entries * sizeof(long));
if (!sc->keyframes)
return -1;
for(i=0; i<entries; i++) {
sc->keyframes[i] = get_be32(pb);
#ifdef DEBUG
/* av_log(NULL, AV_LOG_DEBUG, "keyframes[]=%ld\n", sc->keyframes[i]); */
#endif
}
return 0;
}
static int mov_read_stsz(MOVContext *c, ByteIOContext *pb, MOV_atom_t atom)
AVStream *st = c->fc->streams[c->fc->nb_streams-1];
MOVStreamContext *sc = (MOVStreamContext *)st->priv_data;
get_byte(pb); /* version */
get_byte(pb); get_byte(pb); get_byte(pb); /* flags */
sc->sample_size = get_be32(pb);
entries = get_be32(pb);
if(entries >= UINT_MAX / sizeof(long))
return -1;
sc->sample_count = entries;
#ifdef DEBUG
av_log(NULL, AV_LOG_DEBUG, "sample_size = %ld sample_count = %ld\n", sc->sample_size, sc->sample_count);
1210
1211
1212
1213
1214
1215
1216
1217
1218
1219
1220
1221
1222
1223
1224
1225
1226
1227
1228
1229
1230
1231
1232
1233
1234
if(sc->sample_size) {
/* override sample size for uncompressed sound */
switch (st->codec->codec_id) {
case CODEC_ID_PCM_S32BE:
case CODEC_ID_PCM_S32LE:
sc->sample_size = 4 * st->codec->channels;
break;
case CODEC_ID_PCM_S24BE:
case CODEC_ID_PCM_S24LE:
sc->sample_size = 3 * st->codec->channels;
break;
case CODEC_ID_PCM_S16BE:
case CODEC_ID_PCM_S16LE:
sc->sample_size = 2 * st->codec->channels;
break;
case CODEC_ID_PCM_MULAW:
case CODEC_ID_PCM_ALAW:
case CODEC_ID_PCM_S8:
case CODEC_ID_PCM_U8:
sc->sample_size = 1 * st->codec->channels;
break;
default:
break;
}
assert(sc->sample_size);
return 0; /* there isn't any table following */
sc->sample_sizes = (long*) av_malloc(entries * sizeof(long));
if (!sc->sample_sizes)
return -1;
for(i=0; i<entries; i++) {
sc->sample_sizes[i] = get_be32(pb);
#ifdef DEBUG
av_log(NULL, AV_LOG_DEBUG, "sample_sizes[]=%ld\n", sc->sample_sizes[i]);
#endif
}
return 0;
}
static int mov_read_stts(MOVContext *c, ByteIOContext *pb, MOV_atom_t atom)
AVStream *st = c->fc->streams[c->fc->nb_streams-1];
MOVStreamContext *sc = (MOVStreamContext *)st->priv_data;
int64_t duration=0;
int64_t total_sample_count=0;
get_byte(pb); /* version */
get_byte(pb); get_byte(pb); get_byte(pb); /* flags */
entries = get_be32(pb);
sc->stts_count = entries;
sc->stts_data = av_malloc(entries * sizeof(Time2Sample));
av_log(NULL, AV_LOG_DEBUG, "track[%i].stts.entries = %i\n", c->fc->nb_streams-1, entries);
for(i=0; i<entries; i++) {
sample_count=get_be32(pb);
sample_duration = get_be32(pb);
sc->stts_data[i].count= sample_count;
sc->stts_data[i].duration= sample_duration;
sc->time_rate= ff_gcd(sc->time_rate, sample_duration);
Baptiste Coudurier
committed
dprintf("sample_count=%d, sample_duration=%d\n",sample_count,sample_duration);
total_sample_count+=sample_count;
}
st->nb_frames= total_sample_count;
if(duration)
st->duration= duration;
return 0;
}
static int mov_read_ctts(MOVContext *c, ByteIOContext *pb, MOV_atom_t atom)
{
Michael Niedermayer
committed
AVStream *st = c->fc->streams[c->fc->nb_streams-1];
MOVStreamContext *sc = (MOVStreamContext *)st->priv_data;
unsigned int i, entries;
get_byte(pb); /* version */
get_byte(pb); get_byte(pb); get_byte(pb); /* flags */
entries = get_be32(pb);
if(entries >= UINT_MAX / sizeof(Time2Sample))
return -1;
Michael Niedermayer
committed
sc->ctts_count = entries;
sc->ctts_data = av_malloc(entries * sizeof(Time2Sample));
Baptiste Coudurier
committed
dprintf("track[%i].ctts.entries = %i\n", c->fc->nb_streams-1, entries);
Michael Niedermayer
committed
int count =get_be32(pb);
int duration =get_be32(pb);
sc->ctts_data[i].count = count;
sc->ctts_data[i].duration= duration;
sc->time_rate= ff_gcd(sc->time_rate, duration);
static int mov_read_trak(MOVContext *c, ByteIOContext *pb, MOV_atom_t atom)
{
AVStream *st;
MOVStreamContext *sc;
st = av_new_stream(c->fc, c->fc->nb_streams);
if (!st) return -2;
sc = (MOVStreamContext*) av_mallocz(sizeof(MOVStreamContext));
if (!sc) {
return -1;
}
sc->sample_to_chunk_index = -1;
st->priv_data = sc;
Michael Niedermayer
committed
st->codec->codec_type = CODEC_TYPE_MOV_OTHER;
c->streams[c->fc->nb_streams-1] = sc;
return mov_read_default(c, pb, atom);
}
static int mov_read_tkhd(MOVContext *c, ByteIOContext *pb, MOV_atom_t atom)
{
AVStream *st = c->fc->streams[c->fc->nb_streams-1];
int version = get_byte(pb);
get_byte(pb); get_byte(pb);
get_byte(pb); /* flags */
/*
MOV_TRACK_ENABLED 0x0001
MOV_TRACK_IN_MOVIE 0x0002
MOV_TRACK_IN_PREVIEW 0x0004
MOV_TRACK_IN_POSTER 0x0008
*/
if (version == 1) {
get_be64(pb);
get_be64(pb);
} else {
get_be32(pb); /* creation time */
get_be32(pb); /* modification time */
}
st->id = (int)get_be32(pb); /* track id (NOT 0 !)*/
get_be32(pb); /* reserved */
(version == 1) ? get_be64(pb) : get_be32(pb); /* highlevel (considering edits) duration in movie timebase */
get_be32(pb); /* reserved */
get_be32(pb); /* reserved */
get_be16(pb); /* layer */
get_be16(pb); /* alternate group */
get_be16(pb); /* volume */
get_be16(pb); /* reserved */
url_fskip(pb, 36); /* display matrix */
/* those are fixed-point */
Michael Niedermayer
committed
/*st->codec->width =*/ get_be32(pb) >> 16; /* track width */
/*st->codec->height =*/ get_be32(pb) >> 16; /* track height */
return 0;
}
/* this atom should be null (from specs), but some buggy files put the 'moov' atom inside it... */
/* like the files created with Adobe Premiere 5.0, for samples see */
/* http://graphics.tudelft.nl/~wouter/publications/soundtests/ */
static int mov_read_wide(MOVContext *c, ByteIOContext *pb, MOV_atom_t atom)
{
int err;
if (atom.size < 8)
return 0; /* continue */
if (get_be32(pb) != 0) { /* 0 sized mdat atom... use the 'wide' atom size */
url_fskip(pb, atom.size - 4);
return 0;
}
atom.type = get_le32(pb);
atom.offset += 8;
atom.size -= 8;
if (atom.type != MKTAG('m', 'd', 'a', 't')) {
url_fskip(pb, atom.size);
return 0;
}
err = mov_read_mdat(c, pb, atom);
return err;
}
#ifdef CONFIG_ZLIB
static int null_read_packet(void *opaque, uint8_t *buf, int buf_size)
{
return -1;
}
static int mov_read_cmov(MOVContext *c, ByteIOContext *pb, MOV_atom_t atom)
{
ByteIOContext ctx;
uint8_t *cmov_data;
uint8_t *moov_data; /* uncompressed data */
long cmov_len, moov_len;
int ret;
get_be32(pb); /* dcom atom */
if (get_le32(pb) != MKTAG( 'd', 'c', 'o', 'm' ))
return -1;
if (get_le32(pb) != MKTAG( 'z', 'l', 'i', 'b' )) {
Baptiste Coudurier
committed
av_log(NULL, AV_LOG_ERROR, "unknown compression for cmov atom !");
return -1;
}
get_be32(pb); /* cmvd atom */
if (get_le32(pb) != MKTAG( 'c', 'm', 'v', 'd' ))
return -1;
moov_len = get_be32(pb); /* uncompressed size */
cmov_len = atom.size - 6 * 4;
cmov_data = (uint8_t *) av_malloc(cmov_len);
if (!cmov_data)
return -1;
moov_data = (uint8_t *) av_malloc(moov_len);
if (!moov_data) {
av_free(cmov_data);
return -1;
}
get_buffer(pb, cmov_data, cmov_len);
if(uncompress (moov_data, (uLongf *) &moov_len, (const Bytef *)cmov_data, cmov_len) != Z_OK)
return -1;
if(init_put_byte(&ctx, moov_data, moov_len, 0, NULL, null_read_packet, NULL, NULL) != 0)
return -1;
ctx.buf_end = ctx.buffer + moov_len;
atom.type = MKTAG( 'm', 'o', 'o', 'v' );
atom.offset = 0;
atom.size = moov_len;
#ifdef DEBUG
// { int fd = open("/tmp/uncompheader.mov", O_WRONLY | O_CREAT); write(fd, moov_data, moov_len); close(fd); }
#endif
ret = mov_read_default(c, &ctx, atom);
av_free(moov_data);
av_free(cmov_data);
return ret;
}
#endif
/* edit list atom */
static int mov_read_elst(MOVContext *c, ByteIOContext *pb, MOV_atom_t atom)
{
Michael Niedermayer
committed
int i, edit_count;
get_byte(pb); /* version */
get_byte(pb); get_byte(pb); get_byte(pb); /* flags */
Michael Niedermayer
committed
edit_count= c->streams[c->fc->nb_streams-1]->edit_count = get_be32(pb); /* entries */
Michael Niedermayer
committed
for(i=0; i<edit_count; i++){
get_be32(pb); /* Track duration */
get_be32(pb); /* Media time */
get_be32(pb); /* Media rate */
}
Baptiste Coudurier
committed
dprintf("track[%i].edit_count = %i\n", c->fc->nb_streams-1, c->streams[c->fc->nb_streams-1]->edit_count);
static const MOVParseTableEntry mov_default_parse_table[] = {
/* mp4 atoms */
{ MKTAG( 'c', 'o', '6', '4' ), mov_read_stco },
{ MKTAG( 'c', 'p', 'r', 't' ), mov_read_default },
{ MKTAG( 'c', 'r', 'h', 'd' ), mov_read_default },
{ MKTAG( 'c', 't', 't', 's' ), mov_read_ctts }, /* composition time to sample */
{ MKTAG( 'd', 'i', 'n', 'f' ), mov_read_default }, /* data information */
{ MKTAG( 'd', 'p', 'n', 'd' ), mov_read_leaf },
{ MKTAG( 'd', 'r', 'e', 'f' ), mov_read_leaf },
{ MKTAG( 'e', 'd', 't', 's' ), mov_read_default },
Michael Niedermayer
committed
{ MKTAG( 'e', 'l', 's', 't' ), mov_read_elst },
Baptiste Coudurier
committed
{ MKTAG( 'e', 'n', 'd', 'a' ), mov_read_enda },
{ MKTAG( 'f', 'r', 'e', 'e' ), mov_read_leaf },
Baptiste Coudurier
committed
{ MKTAG( 'f', 't', 'y', 'p' ), mov_read_ftyp },
{ MKTAG( 'h', 'd', 'l', 'r' ), mov_read_hdlr },
{ MKTAG( 'h', 'i', 'n', 't' ), mov_read_leaf },
{ MKTAG( 'h', 'm', 'h', 'd' ), mov_read_leaf },
{ MKTAG( 'i', 'o', 'd', 's' ), mov_read_leaf },
Baptiste Coudurier
committed
{ MKTAG( 'j', 'p', '2', 'h' ), mov_read_jp2h },
{ MKTAG( 'm', 'd', 'a', 't' ), mov_read_mdat },
{ MKTAG( 'm', 'd', 'h', 'd' ), mov_read_mdhd },
{ MKTAG( 'm', 'd', 'i', 'a' ), mov_read_default },
{ MKTAG( 'm', 'i', 'n', 'f' ), mov_read_default },
{ MKTAG( 'm', 'o', 'o', 'v' ), mov_read_moov },
{ MKTAG( 'm', 'p', '4', 'a' ), mov_read_default },
{ MKTAG( 'm', 'p', '4', 's' ), mov_read_default },
{ MKTAG( 'm', 'p', '4', 'v' ), mov_read_default },
{ MKTAG( 'm', 'p', 'o', 'd' ), mov_read_leaf },
{ MKTAG( 'm', 'v', 'h', 'd' ), mov_read_mvhd },
{ MKTAG( 'n', 'm', 'h', 'd' ), mov_read_leaf },
{ MKTAG( 'o', 'd', 'h', 'd' ), mov_read_default },
{ MKTAG( 's', 'd', 'h', 'd' ), mov_read_default },
{ MKTAG( 's', 'k', 'i', 'p' ), mov_read_leaf },
{ MKTAG( 's', 'm', 'h', 'd' ), mov_read_leaf }, /* sound media info header */
{ MKTAG( 'S', 'M', 'I', ' ' ), mov_read_smi }, /* Sorenson extension ??? */
Baptiste Coudurier
committed
{ MKTAG( 'a', 'l', 'a', 'c' ), mov_read_alac }, /* alac specific atom */
{ MKTAG( 'a', 'v', 'c', 'C' ), mov_read_avcC },
{ MKTAG( 's', 't', 'b', 'l' ), mov_read_default },
{ MKTAG( 's', 't', 'c', 'o' ), mov_read_stco },
{ MKTAG( 's', 't', 'd', 'p' ), mov_read_default },
{ MKTAG( 's', 't', 's', 'c' ), mov_read_stsc },
{ MKTAG( 's', 't', 's', 'd' ), mov_read_stsd }, /* sample description */
{ MKTAG( 's', 't', 's', 'h' ), mov_read_default },
Brian Becker
committed
{ MKTAG( 's', 't', 's', 's' ), mov_read_stss }, /* sync sample */
{ MKTAG( 's', 't', 's', 'z' ), mov_read_stsz }, /* sample size */
{ MKTAG( 's', 't', 't', 's' ), mov_read_stts },
{ MKTAG( 't', 'k', 'h', 'd' ), mov_read_tkhd }, /* track header */
{ MKTAG( 't', 'r', 'a', 'k' ), mov_read_trak },
{ MKTAG( 't', 'r', 'e', 'f' ), mov_read_default }, /* not really */
{ MKTAG( 'u', 'd', 't', 'a' ), mov_read_leaf },
{ MKTAG( 'u', 'r', 'l', ' ' ), mov_read_leaf },
{ MKTAG( 'u', 'r', 'n', ' ' ), mov_read_leaf },
{ MKTAG( 'u', 'u', 'i', 'd' ), mov_read_leaf },
{ MKTAG( 'v', 'm', 'h', 'd' ), mov_read_leaf }, /* video media info header */
{ MKTAG( 'w', 'a', 'v', 'e' ), mov_read_wave },
/* extra mp4 */
{ MKTAG( 'M', 'D', 'E', 'S' ), mov_read_leaf },
/* QT atoms */
{ MKTAG( 'c', 'h', 'a', 'p' ), mov_read_leaf },
{ MKTAG( 'c', 'l', 'i', 'p' ), mov_read_default },
{ MKTAG( 'c', 'r', 'g', 'n' ), mov_read_leaf },
{ MKTAG( 'c', 't', 'a', 'b' ), mov_read_ctab },
{ MKTAG( 'e', 's', 'd', 's' ), mov_read_esds },
{ MKTAG( 'k', 'm', 'a', 't' ), mov_read_leaf },
{ MKTAG( 'm', 'a', 't', 't' ), mov_read_default },
{ MKTAG( 'r', 'd', 'r', 'f' ), mov_read_leaf },
{ MKTAG( 'r', 'm', 'd', 'a' ), mov_read_default },
{ MKTAG( 'r', 'm', 'd', 'r' ), mov_read_leaf },
{ MKTAG( 'r', 'm', 'r', 'a' ), mov_read_default },
{ MKTAG( 's', 'c', 'p', 't' ), mov_read_leaf },
{ MKTAG( 's', 's', 'r', 'c' ), mov_read_leaf },
{ MKTAG( 's', 'y', 'n', 'c' ), mov_read_leaf },
{ MKTAG( 't', 'c', 'm', 'd' ), mov_read_leaf },
{ MKTAG( 'w', 'i', 'd', 'e' ), mov_read_wide }, /* place holder */
//{ MKTAG( 'r', 'm', 'q', 'u' ), mov_read_leaf },
#ifdef CONFIG_ZLIB
{ MKTAG( 'c', 'm', 'o', 'v' ), mov_read_cmov },
{ MKTAG( 'c', 'm', 'o', 'v' ), mov_read_leaf },
{ 0L, mov_read_leaf }
};
static void mov_free_stream_context(MOVStreamContext *sc)
{
if(sc) {
av_freep(&sc->chunk_offsets);
av_freep(&sc->sample_to_chunk);
av_freep(&sc->sample_sizes);
av_freep(&sc->keyframes);
av_freep(&sc->stts_data);
av_freep(&sc->ctts_data);
}
}
static inline uint32_t mov_to_tag(uint8_t *buf)
return MKTAG(buf[0], buf[1], buf[2], buf[3]);
static inline uint32_t to_be32(uint8_t *buf)
{
return (buf[0] << 24) | (buf[1] << 16) | (buf[2] << 8) | buf[3];
}
/* XXX: is it sufficient ? */
unsigned int offset;
uint32_t tag;
/* check file header */
if (p->buf_size <= 12)
return 0;
offset = 0;
for(;;) {
/* ignore invalid offset */
if ((offset + 8) > (unsigned int)p->buf_size)
tag = mov_to_tag(p->buf + offset + 4);
switch(tag) {
/* check for obvious tags */
Baptiste Coudurier
committed
case MKTAG( 'j', 'P', ' ', ' ' ): /* jpeg 2000 signature */
case MKTAG( 'm', 'o', 'o', 'v' ):
case MKTAG( 'm', 'd', 'a', 't' ):
case MKTAG( 'p', 'n', 'o', 't' ): /* detect movs with preview pics like ew.mov and april.mov */
case MKTAG( 'u', 'd', 't', 'a' ): /* Packet Video PVAuthor adds this and a lot of more junk */
return AVPROBE_SCORE_MAX;
/* those are more common words, so rate then a bit less */
case MKTAG( 'w', 'i', 'd', 'e' ):
case MKTAG( 'f', 'r', 'e', 'e' ):
case MKTAG( 'j', 'u', 'n', 'k' ):
case MKTAG( 'p', 'i', 'c', 't' ):
return AVPROBE_SCORE_MAX - 5;
case MKTAG( 'f', 't', 'y', 'p' ):
case MKTAG( 's', 'k', 'i', 'p' ):
François Revol
committed
case MKTAG( 'u', 'u', 'i', 'd' ):
offset = to_be32(p->buf+offset) + offset;
/* if we only find those cause probedata is too small at least rate them */
score = AVPROBE_SCORE_MAX - 50;
break;
default:
/* unrecognized tag */
static int mov_read_header(AVFormatContext *s, AVFormatParameters *ap)
MOVContext *mov = (MOVContext *) s->priv_data;
ByteIOContext *pb = &s->pb;
MOV_atom_t atom = { 0, 0, 0 };
mov->fc = s;
mov->parse_table = mov_default_parse_table;
Michael Niedermayer
committed
if(!url_is_streamed(pb)) /* .mov and .mp4 aren't streamable anyway (only progressive download if moov is before mdat) */
atom.size = url_fsize(pb);
else
atom.size = 0x7FFFFFFFFFFFFFFFLL;
/* check MOV header */
err = mov_read_default(mov, pb, atom);
if (err<0 || (!mov->found_moov && !mov->found_mdat)) {
av_log(s, AV_LOG_ERROR, "mov: header not found !!! (err:%d, moov:%d, mdat:%d) pos:%"PRId64"\n",
err, mov->found_moov, mov->found_mdat, url_ftell(pb));
return -1;
Baptiste Coudurier
committed
dprintf("on_parse_exit_offset=%d\n", (int) url_ftell(pb));
/* some cleanup : make sure we are on the mdat atom */
if(!url_is_streamed(pb) && (url_ftell(pb) != mov->mdat_offset))
url_fseek(pb, mov->mdat_offset, SEEK_SET);
mov->next_chunk_offset = mov->mdat_offset; /* initialise reading */
mov->total_streams = nb = s->nb_streams;
#if 1
for(i=0; i<s->nb_streams;) {
Michael Niedermayer
committed
if(s->streams[i]->codec->codec_type == CODEC_TYPE_MOV_OTHER) {/* not audio, not video, delete */
for(j=i+1; j<s->nb_streams; j++)
s->streams[j-1] = s->streams[j];
s->nb_streams--;
} else
i++;
}
for(i=0; i<s->nb_streams;i++) {
MOVStreamContext *sc = (MOVStreamContext *)s->streams[i]->priv_data;
if(!sc->time_rate)
sc->time_rate=1;
Baptiste Coudurier
committed
if(!sc->time_scale)
sc->time_scale= mov->time_scale;
av_set_pts_info(s->streams[i], 64, sc->time_rate, sc->time_scale);
if(s->streams[i]->duration != AV_NOPTS_VALUE){
assert(s->streams[i]->duration % sc->time_rate == 0);
s->streams[i]->duration /= sc->time_rate;
}
sc->ffindex = i;
sc->is_ff_stream = 1;
}
#endif
return 0;
}
/* Yes, this is ugly... I didn't write the specs of QT :p */
/* XXX:remove useless commented code sometime */
static int mov_read_packet(AVFormatContext *s, AVPacket *pkt)
MOVContext *mov = (MOVContext *) s->priv_data;
MOVStreamContext *sc;
int64_t offset = INT64_MAX;
int64_t best_dts = INT64_MAX;
Brian Becker
committed
int i, a, b, m;
Roine Gustafsson
committed
int idx;
size = 0x0FFFFFFF;
sc = mov->partial;
idx = sc->sample_to_chunk_index;
Baptiste Coudurier
committed
dprintf("sc[ffid %d]->sample_size = %ld\n", sc->ffindex, sc->sample_size);
//size = sc->sample_sizes[sc->current_sample];
// that ain't working...
//size = (sc->sample_size)?sc->sample_size:sc->sample_sizes[sc->current_sample];
size = (sc->sample_size > 1)?sc->sample_size:sc->sample_sizes[sc->current_sample];
next_sample= sc->current_sample+1;
mov->partial = 0;
offset = mov->next_chunk_offset;
/* extract the sample */
goto readchunk;
}
again:
if(offset == INT64_MAX)
best_dts= INT64_MAX;
for(i=0; i<mov->total_streams; i++) {
MOVStreamContext *msc = mov->streams[i];
if ((msc->next_chunk < msc->chunk_count) && msc->next_chunk >= 0){
if (msc->sample_to_time_index < msc->stts_count && mov->ni) {
int64_t dts;
int index= msc->sample_to_time_index;
int sample= msc->sample_to_time_sample;
int time= msc->sample_to_time_time;
int duration = msc->stts_data[index].duration;
int count = msc->stts_data[index].count;
if (sample + count <= msc->current_sample) {
sample += count;
time += count*duration;
index ++;
duration = msc->stts_data[index].duration;
}
dts = time + (msc->current_sample - sample) * (int64_t)duration;
dts = av_rescale(dts, AV_TIME_BASE, msc->time_scale);
dprintf("stream: %d dts: %"PRId64" best_dts: %"PRId64" offset: %"PRId64"\n", i, dts, best_dts, offset);
if(dts < best_dts){
best_dts= dts;
sc = msc;
offset = msc->chunk_offsets[msc->next_chunk];
}
}else{
if ((msc->chunk_offsets[msc->next_chunk] < offset)) {
sc = msc;
offset = msc->chunk_offsets[msc->next_chunk];
}
}
}
}
if(mov->next_chunk_offset < offset) { /* some meta data */
url_fskip(&s->pb, (offset - mov->next_chunk_offset));
mov->next_chunk_offset = offset;
}
if(!sc->is_ff_stream || (s->streams[sc->ffindex]->discard >= AVDISCARD_ALL)) {
url_fskip(&s->pb, (offset - mov->next_chunk_offset));
mov->next_chunk_offset = offset;
goto again;
}
/* now get the chunk size... */
for(i=0; i<mov->total_streams; i++) {
MOVStreamContext *msc = mov->streams[i];
if ((msc->next_chunk < msc->chunk_count)
&& msc->chunk_offsets[msc->next_chunk] - offset < size
&& msc->chunk_offsets[msc->next_chunk] > offset)
size = msc->chunk_offsets[msc->next_chunk] - offset;
//Make sure that size is according to sample_size (Needed by .mov files
//created on a Minolta Dimage Xi where audio chunks contains waste data in the end)
//Maybe we should really not only check sc->sample_size, but also sc->sample_sizes
//but I have no such movies
int foundsize=0;
for(i=0; i<(sc->sample_to_chunk_sz); i++) {
Roine Gustafsson
committed
if( (sc->sample_to_chunk[i].first)<=(sc->next_chunk) )
foundsize=sc->sample_to_chunk[i].count*sc->sample_size;
Baptiste Coudurier
committed
dprintf("sample_to_chunk first=%ld count=%ld, id=%ld\n", sc->sample_to_chunk[i].first, sc->sample_to_chunk[i].count, sc->sample_to_chunk[i].id);
}
if( (foundsize>0) && (foundsize<size) )
{
size=foundsize;
}
}
#endif //MOV_MINOLTA_FIX
idx = sc->sample_to_chunk_index;
if (idx + 1 < sc->sample_to_chunk_sz && sc->next_chunk >= sc->sample_to_chunk[idx + 1].first)
idx++;
sc->sample_to_chunk_index = idx;
/* split chunks into samples */
if (sc->sample_size == 0 || sc->sample_size > 100) {
if (idx >= 0 && sc->sample_to_chunk[idx].count != 1) {
/* we'll have to get those samples before next chunk */
sc->left_in_chunk = sc->sample_to_chunk[idx].count - 1;
size = (sc->sample_size > 1)?sc->sample_size:sc->sample_sizes[sc->current_sample];
Roine Gustafsson
committed
next_sample= sc->current_sample+1;
}else if(idx < sc->sample_to_chunk_sz){
next_sample= sc->current_sample + sc->sample_to_chunk[idx].count;
}else
next_sample= sc->current_sample;
dprintf("chunk: %"PRId64" -> %"PRId64" (%i)\n", offset, offset + size, size);
if(size == 0x0FFFFFFF)
size = mov->mdat_size + mov->mdat_offset - offset;
if(size < 0)
return -1;
if(size == 0)
return -1;
url_fseek(&s->pb, offset, SEEK_SET);
pkt->stream_index = sc->ffindex;
Brian Becker
committed
// If the keyframes table exists, mark any samples that are in the table as key frames.
// If no table exists, treat very sample as a key frame.
Brian Becker
committed
a = 0;
b = sc->keyframe_count - 1;
Brian Becker
committed
while (a < b) {
m = (a + b + 1) >> 1;
if (sc->keyframes[m] > sc->current_sample) {
b = m - 1;
} else {
a = m;
Brian Becker
committed
}
Brian Becker
committed
if (sc->keyframes[a] == sc->current_sample)
pkt->flags |= PKT_FLAG_KEY;
}
else
pkt->flags |= PKT_FLAG_KEY;
mov->next_chunk_offset = offset + size;
/* find the corresponding dts */
if (sc && sc->sample_to_time_index < sc->stts_count && pkt) {
unsigned int duration = sc->stts_data[sc->sample_to_time_index].duration;
count = sc->stts_data[sc->sample_to_time_index].count;
if ((sc->sample_to_time_sample + count) <= sc->current_sample) {
sc->sample_to_time_sample += count;
sc->sample_to_time_time += count*duration;
sc->sample_to_time_index ++;
duration = sc->stts_data[sc->sample_to_time_index].duration;
dts = sc->sample_to_time_time + (sc->current_sample - sc->sample_to_time_sample) * (int64_t)duration;
/* find the corresponding pts */
if (sc->sample_to_ctime_index < sc->ctts_count) {
int duration = sc->ctts_data[sc->sample_to_ctime_index].duration;
int count = sc->ctts_data[sc->sample_to_ctime_index].count;
if ((sc->sample_to_ctime_sample + count) <= sc->current_sample) {
sc->sample_to_ctime_sample += count;
sc->sample_to_ctime_index ++;
duration = sc->ctts_data[sc->sample_to_ctime_index].duration;
}
pts = dts + duration;
}else
pts = dts;
st= s->streams[ sc->ffindex ];
assert(pts % st->time_base.num == 0);
assert(dts % st->time_base.num == 0);
pkt->pts = pts / st->time_base.num;
pkt->dts = dts / st->time_base.num;
dprintf("stream #%d smp #%ld dts = %"PRId64" pts = %"PRId64" (smp:%ld time:%"PRId64" idx:%d ent:%d count:%d dur:%d)\n"
Baptiste Coudurier
committed
, pkt->stream_index, sc->current_sample-1, pkt->dts, pkt->pts
, sc->sample_to_time_sample
, sc->sample_to_time_time
, sc->sample_to_time_index
, sc->stts_count
, count
, duration);
assert(next_sample>=0);
sc->current_sample= next_sample;
return 0;
}
Baptiste Coudurier
committed
#if defined(MOV_SEEK)
/**
* Seek method based on the one described in the Appendix C of QTFileFormat.pdf
*/
static int mov_read_seek(AVFormatContext *s, int stream_index, int64_t sample_time, int flags)
{
MOVContext* mov = (MOVContext *) s->priv_data;
MOVStreamContext* sc;
int32_t i, a, b, m;
int64_t start_time;
int32_t seek_sample, sample;
int32_t duration;
int32_t count;
int32_t chunk;
int32_t left_in_chunk;
int64_t chunk_file_offset;
int64_t sample_file_offset;
int32_t first_chunk_sample;
int32_t sample_to_chunk_idx;
int sample_to_time_index;
long sample_to_time_sample = 0;
uint64_t sample_to_time_time = 0;
int mov_idx;
// Find the corresponding mov stream
for (mov_idx = 0; mov_idx < mov->total_streams; mov_idx++)
if (mov->streams[mov_idx]->ffindex == stream_index)
break;
if (mov_idx == mov->total_streams) {
av_log(s, AV_LOG_ERROR, "mov: requested stream was not found in mov streams (idx=%i)\n", stream_index);
return -1;
}
sc = mov->streams[mov_idx];
sample_time *= s->streams[stream_index]->time_base.num;
// Step 1. Find the edit that contains the requested time (elst)
// FIXME should handle edit list
av_log(s, AV_LOG_ERROR, "mov: does not handle seeking in files that contain edit list (c:%d)\n", sc->edit_count);
return -1;
}
// Step 2. Find the corresponding sample using the Time-to-sample atom (stts) */
Baptiste Coudurier
committed
dprintf("Searching for time %li in stream #%i (time_scale=%i)\n", (long)sample_time, mov_idx, sc->time_scale);
start_time = 0; // FIXME use elst atom
sample = 1; // sample are 0 based in table
Baptiste Coudurier
committed
count = sc->stts_data[i].count;
duration = sc->stts_data[i].duration;
if ((start_time + count*duration) > sample_time) {
sample_to_time_time = start_time;
sample_to_time_index = i;
sample_to_time_sample = sample;
sample += (sample_time - start_time) / duration;
break;
}
sample += count;
start_time += count * duration;
}
sample_to_time_time = start_time;
sample_to_time_index = i;
/* NOTE: despite what qt doc say, the dt value (Display Time in qt vocabulary) computed with the stts atom
is a decoding time stamp (dts) not a presentation time stamp. And as usual dts != pts for stream with b frames */
Baptiste Coudurier
committed
dprintf("Found time %li at sample #%u\n", (long)sample_time, sample);
if (sample > sc->sample_count) {
av_log(s, AV_LOG_ERROR, "mov: sample pos is too high, unable to seek (req. sample=%i, sample count=%ld)\n", sample, sc->sample_count);
return -1;
}
// Step 3. Find the prior sync. sample using the Sync sample atom (stss)
if (sc->keyframes) {
a = 0;