diff options
Diffstat (limited to 'contrib/ffmpeg/libavformat/mov.c')
-rw-r--r-- | contrib/ffmpeg/libavformat/mov.c | 913 |
1 files changed, 479 insertions, 434 deletions
diff --git a/contrib/ffmpeg/libavformat/mov.c b/contrib/ffmpeg/libavformat/mov.c index e9b577576..a67604b0e 100644 --- a/contrib/ffmpeg/libavformat/mov.c +++ b/contrib/ffmpeg/libavformat/mov.c @@ -38,16 +38,8 @@ * * Features and limitations: * - reads most of the QT files I have (at least the structure), - * the exceptions are .mov with zlib compressed headers ('cmov' section). It shouldn't be hard to implement. - * FIXED, Francois Revol, 07/17/2002 - * - ffmpeg has nearly none of the usual QuickTime codecs, - * although I succesfully dumped raw and mp3 audio tracks off .mov files. * Sample QuickTime files with mp3 audio can be found at: http://www.3ivx.com/showcase.html - * - .mp4 parsing is still hazardous, although the format really is QuickTime with some minor changes - * (to make .mov parser crash maybe ?), despite what they say in the MPEG FAQ at - * http://mpeg.telecomitalialab.com/faq.htm * - the code is quite ugly... maybe I won't do it recursive next time :-) - * - seek is not supported with files that contain edit list * * Funny I didn't know about http://sourceforge.net/projects/qt-ffmpeg/ * when coding this :) (it's a writer anyway) @@ -71,11 +63,16 @@ * Here we just use what is needed to read the chunks */ -typedef struct MOV_sample_to_chunk_tbl { - long first; - long count; - long id; -} MOV_sample_to_chunk_tbl; +typedef struct { + int first; + int count; + int id; +} MOV_stsc_t; + +typedef struct { + uint32_t type; + char *path; +} MOV_dref_t; typedef struct { uint32_t type; @@ -83,94 +80,39 @@ typedef struct { int64_t size; /* total size (excluding the size and type fields) */ } MOV_atom_t; -typedef struct { - int seed; - int flags; - int size; - void* clrs; -} MOV_ctab_t; - -typedef struct MOV_mdat_atom_s { - offset_t offset; - int64_t size; -} MOV_mdat_atom_t; - -typedef struct { - uint8_t version; - uint32_t flags; // 24bit - - /* 0x03 ESDescrTag */ - uint16_t es_id; -#define MP4ODescrTag 0x01 -#define MP4IODescrTag 0x02 -#define MP4ESDescrTag 0x03 -#define MP4DecConfigDescrTag 0x04 -#define MP4DecSpecificDescrTag 0x05 -#define MP4SLConfigDescrTag 0x06 -#define MP4ContentIdDescrTag 0x07 -#define MP4SupplContentIdDescrTag 0x08 -#define MP4IPIPtrDescrTag 0x09 -#define MP4IPMPPtrDescrTag 0x0A -#define MP4IPMPDescrTag 0x0B -#define MP4RegistrationDescrTag 0x0D -#define MP4ESIDIncDescrTag 0x0E -#define MP4ESIDRefDescrTag 0x0F -#define MP4FileIODescrTag 0x10 -#define MP4FileODescrTag 0x11 -#define MP4ExtProfileLevelDescrTag 0x13 -#define MP4ExtDescrTagsStart 0x80 -#define MP4ExtDescrTagsEnd 0xFE - uint8_t stream_priority; - - /* 0x04 DecConfigDescrTag */ - uint8_t object_type_id; - uint8_t stream_type; - /* XXX: really streamType is - * only 6bit, followed by: - * 1bit upStream - * 1bit reserved - */ - uint32_t buffer_size_db; // 24 - uint32_t max_bitrate; - uint32_t avg_bitrate; - - /* 0x05 DecSpecificDescrTag */ - uint8_t decoder_cfg_len; - uint8_t *decoder_cfg; - - /* 0x06 SLConfigDescrTag */ - uint8_t sl_config_len; - uint8_t *sl_config; -} MOV_esds_t; - struct MOVParseTableEntry; typedef struct MOVStreamContext { + ByteIOContext *pb; int ffindex; /* the ffmpeg stream id */ - long next_chunk; + int next_chunk; unsigned int chunk_count; int64_t *chunk_offsets; unsigned int stts_count; - Time2Sample *stts_data; + MOV_stts_t *stts_data; unsigned int ctts_count; - Time2Sample *ctts_data; + MOV_stts_t *ctts_data; unsigned int edit_count; /* number of 'edit' (elst atom) */ unsigned int sample_to_chunk_sz; - MOV_sample_to_chunk_tbl *sample_to_chunk; + MOV_stsc_t *sample_to_chunk; int sample_to_ctime_index; int sample_to_ctime_sample; unsigned int sample_size; unsigned int sample_count; - long *sample_sizes; + int *sample_sizes; unsigned int keyframe_count; - long *keyframes; + int *keyframes; int time_scale; int time_rate; - long current_sample; - MOV_esds_t esds; + int current_sample; unsigned int bytes_per_frame; unsigned int samples_per_frame; int dv_audio_container; + int pseudo_stream_id; + int16_t audio_cid; ///< stsd audio compression id + unsigned drefs_count; + MOV_dref_t *drefs; + int dref_id; } MOVStreamContext; typedef struct MOVContext { @@ -179,18 +121,7 @@ typedef struct MOVContext { int64_t duration; /* duration of the longest track */ int found_moov; /* when both 'moov' and 'mdat' sections has been found */ int found_mdat; /* we suppose we have enough data to read the file */ - int64_t mdat_offset; - int total_streams; - MOVStreamContext *streams[MAX_STREAMS]; - - int ctab_size; - MOV_ctab_t **ctab; /* color tables */ - const struct MOVParseTableEntry *parse_table; /* could be eventually used to change the table */ - /* NOTE: for recursion save to/ restore from local variable! */ - AVPaletteControl palette_control; - MOV_mdat_atom_t *mdat_list; - int mdat_count; DVDemuxContext *dv_demux; AVFormatContext *dv_fctx; int isom; /* 1 if file is ISO Media (mp4/3gp) */ @@ -205,14 +136,14 @@ typedef struct MOVContext { 0: continue to parse next atom -1: error occured, exit */ -typedef int (*mov_parse_function)(MOVContext *ctx, ByteIOContext *pb, MOV_atom_t atom); - /* links atom IDs to parse functions */ typedef struct MOVParseTableEntry { uint32_t type; - mov_parse_function func; + int (*parse)(MOVContext *ctx, ByteIOContext *pb, MOV_atom_t atom); } MOVParseTableEntry; +static const MOVParseTableEntry mov_default_parse_table[]; + static int mov_read_default(MOVContext *c, ByteIOContext *pb, MOV_atom_t atom) { int64_t total_size = 0; @@ -223,17 +154,18 @@ static int mov_read_default(MOVContext *c, ByteIOContext *pb, MOV_atom_t atom) a.offset = atom.offset; if (atom.size < 0) - atom.size = 0x7fffffffffffffffLL; + atom.size = INT64_MAX; while(((total_size + 8) < atom.size) && !url_feof(pb) && !err) { a.size = atom.size; - a.type=0L; + a.type=0; if(atom.size >= 8) { a.size = get_be32(pb); a.type = get_le32(pb); } total_size += 8; a.offset += 8; - dprintf(c->fc, "type: %08x %.4s sz: %"PRIx64" %"PRIx64" %"PRIx64"\n", a.type, (char*)&a.type, a.size, atom.size, total_size); + dprintf(c->fc, "type: %08x %.4s sz: %"PRIx64" %"PRIx64" %"PRIx64"\n", + a.type, (char*)&a.type, a.size, atom.size, total_size); if (a.size == 1) { /* 64 bit extended size */ a.size = get_be64(pb) - 8; a.offset += 8; @@ -245,19 +177,22 @@ static int mov_read_default(MOVContext *c, ByteIOContext *pb, MOV_atom_t atom) break; } a.size -= 8; - if(a.size < 0 || a.size > atom.size - total_size) + if(a.size < 0) break; + a.size = FFMIN(a.size, atom.size - total_size); - for (i = 0; c->parse_table[i].type != 0L - && c->parse_table[i].type != a.type; i++) + for (i = 0; mov_default_parse_table[i].type != 0 + && mov_default_parse_table[i].type != a.type; i++) /* empty */; - if (c->parse_table[i].type == 0) { /* skip leaf atoms data */ + if (mov_default_parse_table[i].type == 0) { /* skip leaf atoms data */ url_fskip(pb, a.size); } else { offset_t start_pos = url_ftell(pb); int64_t left; - err = (c->parse_table[i].func)(c, pb, a); + err = mov_default_parse_table[i].parse(c, pb, a); + if (c->found_moov && c->found_mdat) + break; left = a.size - url_ftell(pb) + start_pos; if (left > 0) /* skip garbage at atom end */ url_fskip(pb, left); @@ -274,27 +209,71 @@ static int mov_read_default(MOVContext *c, ByteIOContext *pb, MOV_atom_t atom) return err; } -static int mov_read_ctab(MOVContext *c, ByteIOContext *pb, MOV_atom_t atom) +static int mov_read_dref(MOVContext *c, ByteIOContext *pb, MOV_atom_t atom) { -#if 1 - url_fskip(pb, atom.size); // for now -#else - VERY VERY BROKEN, NEVER execute this, needs rewrite - unsigned int len; - MOV_ctab_t *t; - c->ctab = av_realloc(c->ctab, ++c->ctab_size); - t = c->ctab[c->ctab_size]; - t->seed = get_be32(pb); - t->flags = get_be16(pb); - t->size = get_be16(pb) + 1; - len = 2 * t->size * 4; - if (len > 0) { - t->clrs = av_malloc(len); // 16bit A R G B - if (t->clrs) - get_buffer(pb, t->clrs, len); - } -#endif + AVStream *st = c->fc->streams[c->fc->nb_streams-1]; + MOVStreamContext *sc = st->priv_data; + int entries, i, j; + get_be32(pb); // version + flags + 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)); + + for (i = 0; i < sc->drefs_count; i++) { + MOV_dref_t *dref = &sc->drefs[i]; + uint32_t size = get_be32(pb); + offset_t next = url_ftell(pb) + size - 4; + + dref->type = get_le32(pb); + get_be32(pb); // version + flags + dprintf(c->fc, "type %.4s size %d\n", (char*)&dref->type, size); + + if (dref->type == MKTAG('a','l','i','s') && size > 150) { + /* macintosh alias record */ + uint16_t volume_len, len; + char volume[28]; + int16_t type; + + url_fskip(pb, 10); + + volume_len = get_byte(pb); + volume_len = FFMIN(volume_len, 27); + get_buffer(pb, volume, 27); + volume[volume_len] = 0; + av_log(c->fc, AV_LOG_DEBUG, "volume %s, len %d\n", volume, volume_len); + + url_fskip(pb, 112); + + for (type = 0; type != -1 && url_ftell(pb) < next; ) { + type = get_be16(pb); + len = get_be16(pb); + av_log(c->fc, AV_LOG_DEBUG, "type %d, len %d\n", type, len); + if (len&1) + len += 1; + if (type == 2) { // absolute path + av_free(dref->path); + dref->path = av_mallocz(len+1); + if (!dref->path) + return AVERROR(ENOMEM); + get_buffer(pb, dref->path, len); + if (len > volume_len && !strncmp(dref->path, volume, volume_len)) { + len -= volume_len; + memmove(dref->path, dref->path+volume_len, len); + dref->path[len] = 0; + } + for (j = 0; j < len; j++) + if (dref->path[j] == ':') + dref->path[j] = '/'; + av_log(c->fc, AV_LOG_DEBUG, "path %s\n", dref->path); + } else + url_fskip(pb, len); + } + } + url_fseek(pb, next, SEEK_SET); + } return 0; } @@ -311,8 +290,10 @@ static int mov_read_hdlr(MOVContext *c, ByteIOContext *pb, MOV_atom_t atom) ctype = get_le32(pb); type = get_le32(pb); /* component subtype */ - dprintf(c->fc, "ctype= %c%c%c%c (0x%08lx)\n", *((char *)&ctype), ((char *)&ctype)[1], ((char *)&ctype)[2], ((char *)&ctype)[3], (long) ctype); - dprintf(c->fc, "stype= %c%c%c%c\n", *((char *)&type), ((char *)&type)[1], ((char *)&type)[2], ((char *)&type)[3]); + dprintf(c->fc, "ctype= %c%c%c%c (0x%08x)\n", *((char *)&ctype), ((char *)&ctype)[1], + ((char *)&ctype)[2], ((char *)&ctype)[3], (int) ctype); + dprintf(c->fc, "stype= %c%c%c%c\n", + *((char *)&type), ((char *)&type)[1], ((char *)&type)[2], ((char *)&type)[3]); if(!ctype) c->isom = 1; if(type == MKTAG('v', 'i', 'd', 'e')) @@ -323,7 +304,6 @@ static int mov_read_hdlr(MOVContext *c, ByteIOContext *pb, MOV_atom_t atom) st->codec->codec_id = CODEC_ID_MP2; else if(type == MKTAG('s', 'u', 'b', 'p')) { st->codec->codec_type = CODEC_TYPE_SUBTITLE; - st->codec->codec_id = CODEC_ID_DVD_SUBTITLE; } get_be32(pb); /* component manufacture */ get_be32(pb); /* component flags */ @@ -336,7 +316,7 @@ static int mov_read_hdlr(MOVContext *c, ByteIOContext *pb, MOV_atom_t atom) return 0; } -static int mov_mp4_read_descr_len(ByteIOContext *pb) +static int mp4_read_descr_len(ByteIOContext *pb) { int len = 0; int count = 4; @@ -349,51 +329,55 @@ static int mov_mp4_read_descr_len(ByteIOContext *pb) return len; } -static int mov_mp4_read_descr(MOVContext *c, ByteIOContext *pb, int *tag) +static int mp4_read_descr(MOVContext *c, ByteIOContext *pb, int *tag) { int len; *tag = get_byte(pb); - len = mov_mp4_read_descr_len(pb); + len = mp4_read_descr_len(pb); dprintf(c->fc, "MPEG4 description: tag=0x%02x len=%d\n", *tag, len); return len; } +#define MP4ESDescrTag 0x03 +#define MP4DecConfigDescrTag 0x04 +#define MP4DecSpecificDescrTag 0x05 + static int mov_read_esds(MOVContext *c, ByteIOContext *pb, MOV_atom_t atom) { AVStream *st = c->fc->streams[c->fc->nb_streams-1]; - MOVStreamContext *sc = (MOVStreamContext *)st->priv_data; int tag, len; - /* Well, broken but suffisant for some MP4 streams */ get_be32(pb); /* version + flags */ - len = mov_mp4_read_descr(c, pb, &tag); + len = mp4_read_descr(c, pb, &tag); if (tag == MP4ESDescrTag) { get_be16(pb); /* ID */ get_byte(pb); /* priority */ } else get_be16(pb); /* ID */ - len = mov_mp4_read_descr(c, pb, &tag); + len = mp4_read_descr(c, pb, &tag); if (tag == MP4DecConfigDescrTag) { - sc->esds.object_type_id = get_byte(pb); - sc->esds.stream_type = get_byte(pb); - sc->esds.buffer_size_db = get_be24(pb); - sc->esds.max_bitrate = get_be32(pb); - sc->esds.avg_bitrate = get_be32(pb); - - st->codec->codec_id= codec_get_id(ff_mp4_obj_type, sc->esds.object_type_id); - dprintf(c->fc, "esds object type id %d\n", sc->esds.object_type_id); - len = mov_mp4_read_descr(c, pb, &tag); + int object_type_id = get_byte(pb); + get_byte(pb); /* stream type */ + get_be24(pb); /* buffer size db */ + get_be32(pb); /* max bitrate */ + get_be32(pb); /* avg bitrate */ + + st->codec->codec_id= codec_get_id(ff_mp4_obj_type, object_type_id); + dprintf(c->fc, "esds object type id %d\n", object_type_id); + len = mp4_read_descr(c, pb, &tag); if (tag == MP4DecSpecificDescrTag) { dprintf(c->fc, "Specific MPEG4 header len=%d\n", len); + if((uint64_t)len > (1<<30)) + return -1; st->codec->extradata = av_mallocz(len + FF_INPUT_BUFFER_PADDING_SIZE); - if (st->codec->extradata) { - get_buffer(pb, st->codec->extradata, len); - st->codec->extradata_size = len; - /* from mplayer */ - if ((*st->codec->extradata >> 3) == 29) { - st->codec->codec_id = CODEC_ID_MP3ON4; - } + if (!st->codec->extradata) + return AVERROR(ENOMEM); + get_buffer(pb, st->codec->extradata, len); + st->codec->extradata_size = len; + /* from mplayer */ + if ((*st->codec->extradata >> 3) == 29) { + st->codec->codec_id = CODEC_ID_MP3ON4; } } } @@ -405,12 +389,7 @@ static int mov_read_mdat(MOVContext *c, ByteIOContext *pb, MOV_atom_t atom) { if(atom.size == 0) /* wrong one (MP4) */ return 0; - c->mdat_list = av_realloc(c->mdat_list, (c->mdat_count + 1) * sizeof(*c->mdat_list)); - c->mdat_list[c->mdat_count].offset = atom.offset; - c->mdat_list[c->mdat_count].size = atom.size; - c->mdat_count++; c->found_mdat=1; - c->mdat_offset = atom.offset; if(c->found_moov) return 1; /* found both, just go */ url_fskip(pb, atom.size); @@ -432,9 +411,8 @@ static int mov_read_ftyp(MOVContext *c, ByteIOContext *pb, MOV_atom_t atom) /* this atom should contain all header atoms */ static int mov_read_moov(MOVContext *c, ByteIOContext *pb, MOV_atom_t atom) { - int err; - - err = mov_read_default(c, pb, atom); + if (mov_read_default(c, pb, atom) < 0) + return -1; /* we parsed the 'moov' atom, we can terminate the parsing as soon as we find the 'mdat' */ /* so we don't parse the whole file if over a network */ c->found_moov=1; @@ -447,7 +425,7 @@ static int mov_read_moov(MOVContext *c, ByteIOContext *pb, MOV_atom_t atom) static int mov_read_mdhd(MOVContext *c, ByteIOContext *pb, MOV_atom_t atom) { AVStream *st = c->fc->streams[c->fc->nb_streams-1]; - MOVStreamContext *sc = (MOVStreamContext *)st->priv_data; + MOVStreamContext *sc = st->priv_data; int version = get_byte(pb); int lang; @@ -488,9 +466,9 @@ static int mov_read_mvhd(MOVContext *c, ByteIOContext *pb, MOV_atom_t atom) get_be32(pb); /* modification time */ } c->time_scale = get_be32(pb); /* time scale */ -#ifdef DEBUG - av_log(NULL, AV_LOG_DEBUG, "time scale = %i\n", c->time_scale); -#endif + + dprintf(c->fc, "time scale = %i\n", c->time_scale); + c->duration = (version == 1) ? get_be64(pb) : get_be32(pb); /* duration */ get_be32(pb); /* preferred scale */ @@ -521,16 +499,13 @@ static int mov_read_smi(MOVContext *c, ByteIOContext *pb, MOV_atom_t atom) // currently SVQ3 decoder expect full STSD header - so let's fake it // this should be fixed and just SMI header should be passed av_free(st->codec->extradata); + st->codec->extradata = av_mallocz(atom.size + 0x5a + FF_INPUT_BUFFER_PADDING_SIZE); + if (!st->codec->extradata) + return AVERROR(ENOMEM); st->codec->extradata_size = 0x5a + atom.size; - st->codec->extradata = av_mallocz(st->codec->extradata_size + FF_INPUT_BUFFER_PADDING_SIZE); - - if (st->codec->extradata) { - memcpy(st->codec->extradata, "SVQ3", 4); // fake - get_buffer(pb, st->codec->extradata + 0x5a, atom.size); - dprintf(c->fc, "Reading SMI %"PRId64" %s\n", atom.size, st->codec->extradata + 0x5a); - } else - url_fskip(pb, atom.size); - + memcpy(st->codec->extradata, "SVQ3", 4); // fake + get_buffer(pb, st->codec->extradata + 0x5a, atom.size); + dprintf(c->fc, "Reading SMI %"PRId64" %s\n", atom.size, st->codec->extradata + 0x5a); return 0; } @@ -558,16 +533,19 @@ static int mov_read_enda(MOVContext *c, ByteIOContext *pb, MOV_atom_t atom) static int mov_read_extradata(MOVContext *c, ByteIOContext *pb, MOV_atom_t atom) { AVStream *st = c->fc->streams[c->fc->nb_streams-1]; - if((uint64_t)atom.size > (1<<30)) + uint64_t size= (uint64_t)st->codec->extradata_size + atom.size + 8 + FF_INPUT_BUFFER_PADDING_SIZE; + uint8_t *buf; + if(size > INT_MAX || (uint64_t)atom.size > INT_MAX) return -1; - av_free(st->codec->extradata); - st->codec->extradata_size = atom.size + 8; - st->codec->extradata = av_mallocz(st->codec->extradata_size + FF_INPUT_BUFFER_PADDING_SIZE); - if (st->codec->extradata) { - AV_WL32(st->codec->extradata + 4, atom.type); - get_buffer(pb, st->codec->extradata + 8, atom.size); - } else - url_fskip(pb, atom.size); + buf= av_realloc(st->codec->extradata, size); + if(!buf) + return -1; + st->codec->extradata= buf; + buf+= st->codec->extradata_size; + st->codec->extradata_size= size - FF_INPUT_BUFFER_PADDING_SIZE; + AV_WB32( buf , atom.size + 8); + AV_WL32( buf + 4, atom.type); + get_buffer(pb, buf + 8, atom.size); return 0; } @@ -581,21 +559,24 @@ static int mov_read_wave(MOVContext *c, ByteIOContext *pb, MOV_atom_t atom) if (st->codec->codec_id == CODEC_ID_QDM2) { // pass all frma atom to codec, needed at least for QDM2 av_free(st->codec->extradata); + st->codec->extradata = av_mallocz(atom.size + FF_INPUT_BUFFER_PADDING_SIZE); + if (!st->codec->extradata) + return AVERROR(ENOMEM); st->codec->extradata_size = atom.size; - st->codec->extradata = av_mallocz(st->codec->extradata_size + FF_INPUT_BUFFER_PADDING_SIZE); - - if (st->codec->extradata) { - get_buffer(pb, st->codec->extradata, atom.size); - } else - url_fskip(pb, atom.size); + get_buffer(pb, st->codec->extradata, atom.size); } else if (atom.size > 8) { /* to read frma, esds atoms */ - mov_read_default(c, pb, atom); + if (mov_read_default(c, pb, atom) < 0) + return -1; } else url_fskip(pb, atom.size); return 0; } -static int mov_read_avcC(MOVContext *c, ByteIOContext *pb, MOV_atom_t atom) +/** + * This function reads atom content and puts data in extradata without tag + * nor size unlike mov_read_extradata. + */ +static int mov_read_glbl(MOVContext *c, ByteIOContext *pb, MOV_atom_t atom) { AVStream *st = c->fc->streams[c->fc->nb_streams-1]; @@ -603,22 +584,18 @@ static int mov_read_avcC(MOVContext *c, ByteIOContext *pb, MOV_atom_t atom) return -1; av_free(st->codec->extradata); - + st->codec->extradata = av_mallocz(atom.size + FF_INPUT_BUFFER_PADDING_SIZE); + if (!st->codec->extradata) + return AVERROR(ENOMEM); st->codec->extradata_size = atom.size; - st->codec->extradata = av_mallocz(st->codec->extradata_size + FF_INPUT_BUFFER_PADDING_SIZE); - - if (st->codec->extradata) { - get_buffer(pb, st->codec->extradata, atom.size); - } else - url_fskip(pb, atom.size); - + get_buffer(pb, st->codec->extradata, atom.size); return 0; } static int mov_read_stco(MOVContext *c, ByteIOContext *pb, MOV_atom_t atom) { AVStream *st = c->fc->streams[c->fc->nb_streams-1]; - MOVStreamContext *sc = (MOVStreamContext *)st->priv_data; + MOVStreamContext *sc = st->priv_data; unsigned int i, entries; get_byte(pb); /* version */ @@ -650,21 +627,21 @@ static int mov_read_stco(MOVContext *c, ByteIOContext *pb, MOV_atom_t atom) static int mov_read_stsd(MOVContext *c, ByteIOContext *pb, MOV_atom_t atom) { AVStream *st = c->fc->streams[c->fc->nb_streams-1]; - MOVStreamContext *sc = (MOVStreamContext *)st->priv_data; + MOVStreamContext *sc = st->priv_data; int entries, frames_per_sample; uint32_t format; uint8_t codec_name[32]; /* for palette traversal */ - int color_depth; - int color_start; - int color_count; - int color_end; + unsigned int color_depth; + unsigned int color_start; + unsigned int color_count; + unsigned int color_end; int color_index; int color_dec; int color_greyscale; - unsigned char *color_table; - int j; + const uint8_t *color_table; + int j, pseudo_stream_id; unsigned char r, g, b; get_byte(pb); /* version */ @@ -672,8 +649,9 @@ static int mov_read_stsd(MOVContext *c, ByteIOContext *pb, MOV_atom_t atom) entries = get_be32(pb); - while(entries--) { //Parsing Sample description table + for(pseudo_stream_id=0; pseudo_stream_id<entries; pseudo_stream_id++) { //Parsing Sample description table enum CodecID id; + int dref_id; MOV_atom_t a = { 0, 0, 0 }; offset_t start_pos = url_ftell(pb); int size = get_be32(pb); /* size */ @@ -681,16 +659,26 @@ static int mov_read_stsd(MOVContext *c, ByteIOContext *pb, MOV_atom_t atom) get_be32(pb); /* reserved */ get_be16(pb); /* reserved */ - get_be16(pb); /* index */ - - if (st->codec->codec_tag) { - /* multiple fourcc, just skip for now */ + dref_id = get_be16(pb); + + if (st->codec->codec_tag && + (c->fc->video_codec_id ? codec_get_id(codec_movvideo_tags, format) != c->fc->video_codec_id + : st->codec->codec_tag != MKTAG('j', 'p', 'e', 'g')) + ){ + /* multiple fourcc, we skip jpeg, this isnt correct, we should export it as + seperate AVStream but this needs a few changes in the mov demuxer, patch + welcome */ url_fskip(pb, size - (url_ftell(pb) - start_pos)); continue; } + sc->pseudo_stream_id= pseudo_stream_id; + sc->dref_id= dref_id; st->codec->codec_tag = format; id = codec_get_id(codec_movaudio_tags, format); + if (id<=0 && (format&0xFFFF) == 'm' + ('s'<<8)) + id = codec_get_id(codec_wav_tags, bswap_32(format)&0xFFFF); + if (st->codec->codec_type != CODEC_TYPE_VIDEO && id > 0) { st->codec->codec_type = CODEC_TYPE_AUDIO; } else if (st->codec->codec_type != CODEC_TYPE_AUDIO && /* do not overwrite codec type */ @@ -700,12 +688,16 @@ static int mov_read_stsd(MOVContext *c, ByteIOContext *pb, MOV_atom_t atom) id = codec_get_id(codec_bmp_tags, format); if (id > 0) st->codec->codec_type = CODEC_TYPE_VIDEO; + else if(st->codec->codec_type == CODEC_TYPE_DATA){ + id = codec_get_id(ff_codec_movsubtitle_tags, format); + if(id > 0) + st->codec->codec_type = CODEC_TYPE_SUBTITLE; + } } - dprintf(c->fc, "size=%d 4CC= %c%c%c%c codec_type=%d\n", - size, - (format >> 0) & 0xff, (format >> 8) & 0xff, (format >> 16) & 0xff, (format >> 24) & 0xff, - st->codec->codec_type); + dprintf(c->fc, "size=%d 4CC= %c%c%c%c codec_type=%d\n", size, + (format >> 0) & 0xff, (format >> 8) & 0xff, (format >> 16) & 0xff, + (format >> 24) & 0xff, st->codec->codec_type); if(st->codec->codec_type==CODEC_TYPE_VIDEO) { st->codec->codec_id = id; @@ -713,7 +705,7 @@ static int mov_read_stsd(MOVContext *c, ByteIOContext *pb, MOV_atom_t atom) get_be16(pb); /* revision level */ get_be32(pb); /* vendor */ get_be32(pb); /* temporal quality */ - get_be32(pb); /* spacial quality */ + get_be32(pb); /* spatial quality */ st->codec->width = get_be16(pb); /* width */ st->codec->height = get_be16(pb); /* height */ @@ -722,9 +714,9 @@ static int mov_read_stsd(MOVContext *c, ByteIOContext *pb, MOV_atom_t atom) get_be32(pb); /* vert resolution */ get_be32(pb); /* data size, always 0 */ frames_per_sample = get_be16(pb); /* frames per samples */ -#ifdef DEBUG - av_log(NULL, AV_LOG_DEBUG, "frames/samples = %d\n", frames_per_sample); -#endif + + dprintf(c->fc, "frames/samples = %d\n", frames_per_sample); + get_buffer(pb, codec_name, 32); /* codec name, pascal string (FIXME: true for mp4?) */ if (codec_name[0] <= 31) { memcpy(st->codec->codec_name, &codec_name[1],codec_name[0]); @@ -733,7 +725,8 @@ static int mov_read_stsd(MOVContext *c, ByteIOContext *pb, MOV_atom_t atom) st->codec->bits_per_sample = get_be16(pb); /* depth */ st->codec->color_table_id = get_be16(pb); /* colortable id */ - + dprintf(c->fc, "depth %d, ctab id %d\n", + st->codec->bits_per_sample, st->codec->color_table_id); /* figure out the palette situation */ color_depth = st->codec->bits_per_sample & 0x1F; color_greyscale = st->codec->bits_per_sample & 0x20; @@ -741,10 +734,9 @@ static int mov_read_stsd(MOVContext *c, ByteIOContext *pb, MOV_atom_t atom) /* if the depth is 2, 4, or 8 bpp, file is palettized */ if ((color_depth == 2) || (color_depth == 4) || (color_depth == 8)) { - if (color_greyscale) { - /* compute the greyscale palette */ + st->codec->bits_per_sample = color_depth; color_count = 1 << color_depth; color_index = 255; color_dec = 256 / (color_count - 1); @@ -756,9 +748,7 @@ static int mov_read_stsd(MOVContext *c, ByteIOContext *pb, MOV_atom_t atom) if (color_index < 0) color_index = 0; } - } 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) @@ -775,30 +765,30 @@ static int mov_read_stsd(MOVContext *c, ByteIOContext *pb, MOV_atom_t atom) 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); + if ((color_start <= 255) && + (color_end <= 255)) { + 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); + } } } - st->codec->palctrl = &c->palette_control; st->codec->palctrl->palette_changed = 1; } else @@ -814,10 +804,8 @@ static int mov_read_stsd(MOVContext *c, ByteIOContext *pb, MOV_atom_t atom) st->codec->channels = get_be16(pb); /* channel count */ dprintf(c->fc, "audio channels %d\n", st->codec->channels); 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*/ + sc->audio_cid = get_be16(pb); get_be16(pb); /* packet size = 0 */ st->codec->sample_rate = ((get_be32(pb) >> 16)); @@ -835,11 +823,24 @@ static int mov_read_stsd(MOVContext *c, ByteIOContext *pb, MOV_atom_t atom) else if (st->codec->bits_per_sample == 24) st->codec->codec_id = CODEC_ID_PCM_S24BE; break; + /* set values for old format before stsd version 1 appeared */ + case CODEC_ID_MACE3: + sc->samples_per_frame = 6; + sc->bytes_per_frame = 2*st->codec->channels; + break; + case CODEC_ID_MACE6: + sc->samples_per_frame = 6; + sc->bytes_per_frame = 1*st->codec->channels; + break; + case CODEC_ID_ADPCM_IMA_QT: + sc->samples_per_frame = 64; + sc->bytes_per_frame = 34*st->codec->channels; + break; default: break; } - //Read QT version 1 fields. In version 0 theese dont exist + //Read QT version 1 fields. In version 0 these do not exist. dprintf(c->fc, "version =%d, isom =%d\n",version,c->isom); if(!c->isom) { if(version==1) { @@ -864,45 +865,26 @@ static int mov_read_stsd(MOVContext *c, ByteIOContext *pb, MOV_atom_t atom) st->codec->bits_per_sample = bits_per_sample; sc->sample_size = (bits_per_sample >> 3) * st->codec->channels; } + } else if(st->codec->codec_type==CODEC_TYPE_SUBTITLE){ + st->codec->codec_id= id; } else { /* other codec type, just skip (rtp, mp4s, tmcd ...) */ url_fskip(pb, size - (url_ftell(pb) - start_pos)); } /* 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) + if (a.size > 8) { + if (mov_read_default(c, pb, a) < 0) + return -1; + } else if (a.size > 0) url_fskip(pb, a.size); } - if(st->codec->codec_type==CODEC_TYPE_AUDIO && st->codec->sample_rate==0 && sc->time_scale>1) { + if(st->codec->codec_type==CODEC_TYPE_AUDIO && st->codec->sample_rate==0 && sc->time_scale>1) st->codec->sample_rate= sc->time_scale; - } /* special codec parameters handling */ switch (st->codec->codec_id) { -#ifdef CONFIG_H261_DECODER - case CODEC_ID_H261: -#endif -#ifdef CONFIG_H263_DECODER - case CODEC_ID_H263: -#endif -#ifdef CONFIG_MPEG4_DECODER - case CODEC_ID_MPEG4: -#endif - st->codec->width= 0; /* let decoder init width/height */ - st->codec->height= 0; - break; -#ifdef CONFIG_LIBFAAD - case CODEC_ID_AAC: -#endif -#ifdef CONFIG_VORBIS_DECODER - case CODEC_ID_VORBIS: -#endif - case CODEC_ID_MP3ON4: - st->codec->sample_rate= 0; /* let decoder init parameters properly */ - break; #ifdef CONFIG_DV_DEMUXER case CODEC_ID_DVAUDIO: c->dv_fctx = av_alloc_format_context(); @@ -927,7 +909,11 @@ static int mov_read_stsd(MOVContext *c, ByteIOContext *pb, MOV_atom_t atom) case CODEC_ID_MP2: case CODEC_ID_MP3: st->codec->codec_type = CODEC_TYPE_AUDIO; /* force type after stsd for m1a hdlr */ - st->need_parsing = 1; + st->need_parsing = AVSTREAM_PARSE_FULL; + break; + case CODEC_ID_ADPCM_MS: + case CODEC_ID_ADPCM_IMA_WAV: + st->codec->block_align = sc->bytes_per_frame; break; default: break; @@ -939,7 +925,7 @@ static int mov_read_stsd(MOVContext *c, ByteIOContext *pb, MOV_atom_t atom) 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; + MOVStreamContext *sc = st->priv_data; unsigned int i, entries; get_byte(pb); /* version */ @@ -947,14 +933,13 @@ static int mov_read_stsc(MOVContext *c, ByteIOContext *pb, MOV_atom_t atom) entries = get_be32(pb); - if(entries >= UINT_MAX / sizeof(MOV_sample_to_chunk_tbl)) + if(entries >= UINT_MAX / sizeof(MOV_stsc_t)) return -1; -#ifdef DEBUG -av_log(NULL, AV_LOG_DEBUG, "track[%i].stsc.entries = %i\n", c->fc->nb_streams-1, entries); -#endif + 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(MOV_sample_to_chunk_tbl)); + sc->sample_to_chunk = av_malloc(entries * sizeof(MOV_stsc_t)); if (!sc->sample_to_chunk) return -1; for(i=0; i<entries; i++) { @@ -968,7 +953,7 @@ av_log(NULL, AV_LOG_DEBUG, "track[%i].stsc.entries = %i\n", c->fc->nb_streams-1, 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; + MOVStreamContext *sc = st->priv_data; unsigned int i, entries; get_byte(pb); /* version */ @@ -976,21 +961,19 @@ static int mov_read_stss(MOVContext *c, ByteIOContext *pb, MOV_atom_t atom) entries = get_be32(pb); - if(entries >= UINT_MAX / sizeof(long)) + if(entries >= UINT_MAX / sizeof(int)) return -1; sc->keyframe_count = entries; -#ifdef DEBUG - av_log(NULL, AV_LOG_DEBUG, "keyframe_count = %d\n", sc->keyframe_count); -#endif - sc->keyframes = av_malloc(entries * sizeof(long)); + + dprintf(c->fc, "keyframe_count = %d\n", sc->keyframe_count); + + sc->keyframes = av_malloc(entries * sizeof(int)); 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 + //dprintf(c->fc, "keyframes[]=%d\n", sc->keyframes[i]); } return 0; } @@ -998,7 +981,7 @@ static int mov_read_stss(MOVContext *c, ByteIOContext *pb, MOV_atom_t atom) 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; + MOVStreamContext *sc = st->priv_data; unsigned int i, entries, sample_size; get_byte(pb); /* version */ @@ -1008,24 +991,21 @@ static int mov_read_stsz(MOVContext *c, ByteIOContext *pb, MOV_atom_t atom) 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(long)) + if(entries >= UINT_MAX / sizeof(int)) return -1; sc->sample_count = entries; if (sample_size) return 0; -#ifdef DEBUG - av_log(NULL, AV_LOG_DEBUG, "sample_size = %d sample_count = %d\n", sc->sample_size, sc->sample_count); -#endif - sc->sample_sizes = av_malloc(entries * sizeof(long)); + dprintf(c->fc, "sample_size = %d sample_count = %d\n", sc->sample_size, sc->sample_count); + + sc->sample_sizes = av_malloc(entries * sizeof(int)); 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 + dprintf(c->fc, "sample_sizes[]=%d\n", sc->sample_sizes[i]); } return 0; } @@ -1033,7 +1013,7 @@ static int mov_read_stsz(MOVContext *c, ByteIOContext *pb, MOV_atom_t atom) 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; + MOVStreamContext *sc = st->priv_data; unsigned int i, entries; int64_t duration=0; int64_t total_sample_count=0; @@ -1041,15 +1021,14 @@ static int mov_read_stts(MOVContext *c, ByteIOContext *pb, MOV_atom_t atom) get_byte(pb); /* version */ get_byte(pb); get_byte(pb); get_byte(pb); /* flags */ entries = get_be32(pb); - if(entries >= UINT_MAX / sizeof(Time2Sample)) + if(entries >= UINT_MAX / sizeof(MOV_stts_t)) return -1; sc->stts_count = entries; - sc->stts_data = av_malloc(entries * sizeof(Time2Sample)); - -#ifdef DEBUG -av_log(NULL, AV_LOG_DEBUG, "track[%i].stts.entries = %i\n", c->fc->nb_streams-1, entries); -#endif + sc->stts_data = av_malloc(entries * sizeof(MOV_stts_t)); + 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; @@ -1079,18 +1058,19 @@ av_log(NULL, AV_LOG_DEBUG, "track[%i].stts.entries = %i\n", c->fc->nb_streams-1, static int mov_read_ctts(MOVContext *c, ByteIOContext *pb, MOV_atom_t atom) { AVStream *st = c->fc->streams[c->fc->nb_streams-1]; - MOVStreamContext *sc = (MOVStreamContext *)st->priv_data; + MOVStreamContext *sc = 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)) + if(entries >= UINT_MAX / sizeof(MOV_stts_t)) return -1; sc->ctts_count = entries; - sc->ctts_data = av_malloc(entries * sizeof(Time2Sample)); - + sc->ctts_data = av_malloc(entries * sizeof(MOV_stts_t)); + if (!sc->ctts_data) + return -1; dprintf(c->fc, "track[%i].ctts.entries = %i\n", c->fc->nb_streams-1, entries); for(i=0; i<entries; i++) { @@ -1127,11 +1107,53 @@ static int mov_read_trak(MOVContext *c, ByteIOContext *pb, MOV_atom_t atom) st->priv_data = sc; st->codec->codec_type = CODEC_TYPE_DATA; st->start_time = 0; /* XXX: check */ - c->streams[c->fc->nb_streams-1] = sc; return mov_read_default(c, pb, atom); } +static void mov_parse_udta_string(ByteIOContext *pb, char *str, int size) +{ + uint16_t str_size = get_be16(pb); /* string length */; + + get_be16(pb); /* skip language */ + get_buffer(pb, str, FFMIN(size, str_size)); +} + +static int mov_read_udta(MOVContext *c, ByteIOContext *pb, MOV_atom_t atom) +{ + uint64_t end = url_ftell(pb) + atom.size; + + while (url_ftell(pb) + 8 < end) { + uint32_t tag_size = get_be32(pb); + uint32_t tag = get_le32(pb); + uint64_t next = url_ftell(pb) + tag_size - 8; + + if (next > end) // stop if tag_size is wrong + break; + + switch (tag) { + case MKTAG(0xa9,'n','a','m'): + mov_parse_udta_string(pb, c->fc->title, sizeof(c->fc->title)); + break; + case MKTAG(0xa9,'w','r','t'): + mov_parse_udta_string(pb, c->fc->author, sizeof(c->fc->author)); + break; + case MKTAG(0xa9,'c','p','y'): + mov_parse_udta_string(pb, c->fc->copyright, sizeof(c->fc->copyright)); + break; + case MKTAG(0xa9,'i','n','f'): + mov_parse_udta_string(pb, c->fc->comment, sizeof(c->fc->comment)); + break; + default: + break; + } + + url_fseek(pb, next, SEEK_SET); + } + + return 0; +} + static int mov_read_tkhd(MOVContext *c, ByteIOContext *pb, MOV_atom_t atom) { AVStream *st = c->fc->streams[c->fc->nb_streams-1]; @@ -1252,18 +1274,23 @@ static int mov_read_cmov(MOVContext *c, ByteIOContext *pb, MOV_atom_t atom) /* edit list atom */ static int mov_read_elst(MOVContext *c, ByteIOContext *pb, MOV_atom_t atom) { + MOVStreamContext *sc = c->fc->streams[c->fc->nb_streams-1]->priv_data; int i, edit_count; get_byte(pb); /* version */ get_byte(pb); get_byte(pb); get_byte(pb); /* flags */ - edit_count= c->streams[c->fc->nb_streams-1]->edit_count = get_be32(pb); /* entries */ + edit_count= sc->edit_count = get_be32(pb); /* entries */ for(i=0; i<edit_count; i++){ + int time; get_be32(pb); /* Track duration */ - get_be32(pb); /* Media time */ + 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"); } - dprintf(c->fc, "track[%i].edit_count = %i\n", c->fc->nb_streams-1, c->streams[c->fc->nb_streams-1]->edit_count); + dprintf(c->fc, "track[%i].edit_count = %i\n", c->fc->nb_streams-1, sc->edit_count); return 0; } @@ -1271,11 +1298,14 @@ static const MOVParseTableEntry mov_default_parse_table[] = { /* mp4 atoms */ { MKTAG( 'c', 'o', '6', '4' ), mov_read_stco }, { 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( 'e', 'd', 't', 's' ), mov_read_default }, { MKTAG( 'e', 'l', 's', 't' ), mov_read_elst }, { MKTAG( 'e', 'n', 'd', 'a' ), mov_read_enda }, { MKTAG( 'f', 'i', 'e', 'l' ), mov_read_extradata }, { MKTAG( 'f', 't', 'y', 'p' ), mov_read_ftyp }, +{ MKTAG( 'g', 'l', 'b', 'l' ), mov_read_glbl }, { MKTAG( 'h', 'd', 'l', 'r' ), mov_read_hdlr }, { MKTAG( 'j', 'p', '2', 'h' ), mov_read_extradata }, { MKTAG( 'm', 'd', 'a', 't' ), mov_read_mdat }, @@ -1286,7 +1316,7 @@ static const MOVParseTableEntry mov_default_parse_table[] = { { MKTAG( 'm', 'v', 'h', 'd' ), mov_read_mvhd }, { MKTAG( 'S', 'M', 'I', ' ' ), mov_read_smi }, /* Sorenson extension ??? */ { MKTAG( 'a', 'l', 'a', 'c' ), mov_read_extradata }, /* alac specific atom */ -{ MKTAG( 'a', 'v', 'c', 'C' ), mov_read_avcC }, +{ MKTAG( 'a', 'v', 'c', 'C' ), mov_read_glbl }, { MKTAG( 's', 't', 'b', 'l' ), mov_read_default }, { MKTAG( 's', 't', 'c', 'o' ), mov_read_stco }, { MKTAG( 's', 't', 's', 'c' ), mov_read_stsc }, @@ -1296,22 +1326,14 @@ static const MOVParseTableEntry mov_default_parse_table[] = { { 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( 'u', 'd', 't', 'a' ), mov_read_udta }, { MKTAG( 'w', 'a', 'v', 'e' ), mov_read_wave }, -{ MKTAG( 'c', 't', 'a', 'b' ), mov_read_ctab }, { MKTAG( 'e', 's', 'd', 's' ), mov_read_esds }, { MKTAG( 'w', 'i', 'd', 'e' ), mov_read_wide }, /* place holder */ { MKTAG( 'c', 'm', 'o', 'v' ), mov_read_cmov }, -{ 0L, NULL } +{ 0, NULL } }; -static void mov_free_stream_context(MOVStreamContext *sc) -{ - if(sc) { - av_freep(&sc->ctts_data); - av_freep(&sc); - } -} - /* XXX: is it sufficient ? */ static int mov_probe(AVProbeData *p) { @@ -1320,8 +1342,6 @@ static int mov_probe(AVProbeData *p) int score = 0; /* check file header */ - if (p->buf_size <= 12) - return 0; offset = 0; for(;;) { /* ignore invalid offset */ @@ -1337,11 +1357,13 @@ static int mov_probe(AVProbeData *p) 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( 'e', 'd', 'i', 'w' ): /* xdcam files have reverted first tags */ 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(0x82,0x82,0x7f,0x7d ): case MKTAG( 'f', 't', 'y', 'p' ): case MKTAG( 's', 'k', 'i', 'p' ): case MKTAG( 'u', 'u', 'i', 'd' ): @@ -1365,34 +1387,40 @@ static void mov_build_index(MOVContext *mov, AVStream *st) unsigned int stts_index = 0; unsigned int stsc_index = 0; unsigned int stss_index = 0; - unsigned int i, j, k; + unsigned int i, j; - if (sc->sample_sizes || st->codec->codec_type == CODEC_TYPE_VIDEO || sc->dv_audio_container) { + if (sc->sample_sizes || st->codec->codec_type == CODEC_TYPE_VIDEO || + sc->audio_cid == -2) { unsigned int current_sample = 0; unsigned int stts_sample = 0; unsigned int keyframe, sample_size; unsigned int distance = 0; + int key_off = sc->keyframes && sc->keyframes[0] == 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->sample_to_chunk_sz && + i + 1 == sc->sample_to_chunk[stsc_index + 1].first) stsc_index++; for (j = 0; j < sc->sample_to_chunk[stsc_index].count; j++) { if (current_sample >= sc->sample_count) { av_log(mov->fc, AV_LOG_ERROR, "wrong sample count\n"); goto out; } - keyframe = !sc->keyframe_count || current_sample + 1 == sc->keyframes[stss_index]; + keyframe = !sc->keyframe_count || current_sample+key_off == sc->keyframes[stss_index]; if (keyframe) { distance = 0; if (stss_index + 1 < sc->keyframe_count) stss_index++; } sample_size = sc->sample_size > 0 ? sc->sample_size : sc->sample_sizes[current_sample]; - dprintf(mov->fc, "AVIndex stream %d, sample %d, offset %"PRIx64", dts %"PRId64", size %d, distance %d, keyframe %d\n", - st->index, current_sample, current_offset, current_dts, sample_size, distance, keyframe); - 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", " + "size %d, distance %d, keyframe %d\n", st->index, current_sample, + current_offset, current_dts, sample_size, distance, keyframe); + if(sc->sample_to_chunk[stsc_index].id - 1 == sc->pseudo_stream_id) + av_add_index_entry(st, current_offset, current_dts, sample_size, distance, + keyframe ? AVINDEX_KEYFRAME : 0); current_offset += sample_size; assert(sc->stts_data[stts_index].duration % sc->time_rate == 0); current_dts += sc->stts_data[stts_index].duration / sc->time_rate; @@ -1407,63 +1435,51 @@ static void mov_build_index(MOVContext *mov, AVStream *st) } } else { /* read whole chunk */ unsigned int chunk_samples, chunk_size, chunk_duration; - + 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->sample_to_chunk_sz && + i + 1 == sc->sample_to_chunk[stsc_index + 1].first) stsc_index++; chunk_samples = sc->sample_to_chunk[stsc_index].count; - /* get chunk size */ - if (sc->sample_size > 1 || st->codec->codec_id == CODEC_ID_PCM_U8 || st->codec->codec_id == CODEC_ID_PCM_S8) - chunk_size = chunk_samples * sc->sample_size; - else if (sc->samples_per_frame > 0 && (chunk_samples * sc->bytes_per_frame % sc->samples_per_frame == 0)) - chunk_size = chunk_samples * sc->bytes_per_frame / sc->samples_per_frame; - else { /* workaround to find nearest next chunk offset */ - chunk_size = INT_MAX; - for (j = 0; j < mov->total_streams; j++) { - MOVStreamContext *msc = mov->streams[j]; - - for (k = msc->next_chunk; k < msc->chunk_count; k++) { - if (msc->chunk_offsets[k] > current_offset && msc->chunk_offsets[k] - current_offset < chunk_size) { - chunk_size = msc->chunk_offsets[k] - current_offset; - msc->next_chunk = k; - break; - } - } - } - /* check for last chunk */ - if (chunk_size == INT_MAX) - for (j = 0; j < mov->mdat_count; j++) { - dprintf(mov->fc, "mdat %d, offset %"PRIx64", size %"PRId64", current offset %"PRIx64"\n", - j, mov->mdat_list[j].offset, mov->mdat_list[j].size, current_offset); - if (mov->mdat_list[j].offset <= current_offset && mov->mdat_list[j].offset + mov->mdat_list[j].size > current_offset) - chunk_size = mov->mdat_list[j].offset + mov->mdat_list[j].size - current_offset; - } - assert(chunk_size != INT_MAX); - for (j = 0; j < mov->total_streams; j++) { - mov->streams[j]->next_chunk = 0; + /* 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)) { + if (sc->samples_per_frame < 1024) + chunk_size = chunk_samples * sc->bytes_per_frame / sc->samples_per_frame; + else { + chunk_size = sc->bytes_per_frame; + frames = chunk_samples / sc->samples_per_frame; + chunk_samples = sc->samples_per_frame; } + } else if (sc->sample_size > 1 || st->codec->bits_per_sample == 8) { + chunk_size = chunk_samples * sc->sample_size; + } else { + av_log(mov->fc, AV_LOG_ERROR, "could not determine chunk size, report problem\n"); + goto out; } - av_add_index_entry(st, current_offset, current_dts, chunk_size, 0, AVINDEX_KEYFRAME); - /* get chunk duration */ - chunk_duration = 0; - while (chunk_samples > 0) { - if (chunk_samples < sc->stts_data[stts_index].count) { - chunk_duration += sc->stts_data[stts_index].duration * chunk_samples; - sc->stts_data[stts_index].count -= chunk_samples; - break; - } else { - chunk_duration += sc->stts_data[stts_index].duration * chunk_samples; - chunk_samples -= sc->stts_data[stts_index].count; - if (stts_index + 1 < sc->stts_count) { - stts_index++; + for (j = 0; j < frames; j++) { + av_add_index_entry(st, current_offset, current_dts, chunk_size, 0, AVINDEX_KEYFRAME); + /* get chunk duration */ + chunk_duration = 0; + while (chunk_samples > 0) { + if (chunk_samples < sc->stts_data[stts_index].count) { + chunk_duration += sc->stts_data[stts_index].duration * chunk_samples; + sc->stts_data[stts_index].count -= chunk_samples; + break; + } else { + chunk_duration += sc->stts_data[stts_index].duration * chunk_samples; + chunk_samples -= sc->stts_data[stts_index].count; + if (stts_index + 1 < sc->stts_count) + stts_index++; } } + current_offset += sc->bytes_per_frame; + dprintf(mov->fc, "AVIndex stream %d, chunk %d, offset %"PRIx64", dts %"PRId64", size %d, " + "duration %d\n", st->index, i, current_offset, current_dts, chunk_size, chunk_duration); + assert(chunk_duration % sc->time_rate == 0); + current_dts += chunk_duration / sc->time_rate; } - dprintf(mov->fc, "AVIndex stream %d, chunk %d, offset %"PRIx64", dts %"PRId64", size %d, duration %d\n", - st->index, i, current_offset, current_dts, chunk_size, chunk_duration); - assert(chunk_duration % sc->time_rate == 0); - current_dts += chunk_duration / sc->time_rate; } } out: @@ -1473,18 +1489,17 @@ static void mov_build_index(MOVContext *mov, AVStream *st) static int mov_read_header(AVFormatContext *s, AVFormatParameters *ap) { - MOVContext *mov = (MOVContext *) s->priv_data; - ByteIOContext *pb = &s->pb; + MOVContext *mov = s->priv_data; + ByteIOContext *pb = s->pb; int i, err; MOV_atom_t atom = { 0, 0, 0 }; mov->fc = s; - mov->parse_table = mov_default_parse_table; 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; + atom.size = INT64_MAX; /* check MOV header */ err = mov_read_default(mov, pb, atom); @@ -1495,15 +1510,9 @@ static int mov_read_header(AVFormatContext *s, AVFormatParameters *ap) } dprintf(mov->fc, "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->total_streams = s->nb_streams; - - for(i=0; i<mov->total_streams; i++) { - MOVStreamContext *sc = mov->streams[i]; + for(i=0; i<s->nb_streams; i++) { AVStream *st = s->streams[i]; + MOVStreamContext *sc = st->priv_data; /* sanity checks */ if(!sc->stts_count || !sc->chunk_count || !sc->sample_to_chunk_sz || (!sc->sample_size && !sc->sample_count)){ @@ -1518,7 +1527,7 @@ static int mov_read_header(AVFormatContext *s, AVFormatParameters *ap) av_set_pts_info(st, 64, sc->time_rate, sc->time_scale); if (st->codec->codec_type == CODEC_TYPE_AUDIO && sc->stts_count == 1) - st->codec->frame_size = sc->stts_data[0].duration; + st->codec->frame_size = av_rescale(sc->time_rate, st->codec->sample_rate, sc->time_scale); if(st->duration != AV_NOPTS_VALUE){ assert(st->duration % sc->time_rate == 0); @@ -1526,17 +1535,48 @@ static int mov_read_header(AVFormatContext *s, AVFormatParameters *ap) } sc->ffindex = i; mov_build_index(mov, st); + + if (sc->dref_id-1 < sc->drefs_count && sc->drefs[sc->dref_id-1].path) { + if (url_fopen(&sc->pb, sc->drefs[sc->dref_id-1].path, URL_RDONLY) < 0) + av_log(s, AV_LOG_ERROR, "stream %d, error opening external essence: %s\n", + st->index, strerror(errno)); + } else + sc->pb = s->pb; + + switch (st->codec->codec_id) { +#ifdef CONFIG_H261_DECODER + case CODEC_ID_H261: +#endif +#ifdef CONFIG_H263_DECODER + case CODEC_ID_H263: +#endif +#ifdef CONFIG_MPEG4_DECODER + case CODEC_ID_MPEG4: +#endif + st->codec->width= 0; /* let decoder init width/height */ + st->codec->height= 0; + break; +#ifdef CONFIG_LIBFAAD + case CODEC_ID_AAC: +#endif +#ifdef CONFIG_VORBIS_DECODER + case CODEC_ID_VORBIS: +#endif + case CODEC_ID_MP3ON4: + st->codec->sample_rate= 0; /* let decoder init parameters properly */ + break; + } } - for(i=0; i<mov->total_streams; i++) { - /* dont need those anymore */ - av_freep(&mov->streams[i]->chunk_offsets); - av_freep(&mov->streams[i]->sample_to_chunk); - av_freep(&mov->streams[i]->sample_sizes); - av_freep(&mov->streams[i]->keyframes); - av_freep(&mov->streams[i]->stts_data); + for(i=0; i<s->nb_streams; i++) { + MOVStreamContext *sc = s->streams[i]->priv_data; + /* Do not need those anymore. */ + 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(&mov->mdat_list); return 0; } @@ -1548,15 +1588,19 @@ static int mov_read_packet(AVFormatContext *s, AVPacket *pkt) int64_t best_dts = INT64_MAX; int i; - for (i = 0; i < mov->total_streams; i++) { - MOVStreamContext *msc = mov->streams[i]; - - if (s->streams[i]->discard != AVDISCARD_ALL && msc->current_sample < msc->sample_count) { - AVIndexEntry *current_sample = &s->streams[i]->index_entries[msc->current_sample]; - int64_t dts = av_rescale(current_sample->timestamp * (int64_t)msc->time_rate, AV_TIME_BASE, msc->time_scale); - - dprintf(s, "stream %d, sample %ld, dts %"PRId64"\n", i, msc->current_sample, dts); - if (dts < best_dts) { + for (i = 0; i < s->nb_streams; i++) { + AVStream *st = s->streams[i]; + MOVStreamContext *msc = st->priv_data; + if (st->discard != AVDISCARD_ALL && msc->pb && msc->current_sample < msc->sample_count) { + AVIndexEntry *current_sample = &st->index_entries[msc->current_sample]; + int64_t dts = av_rescale(current_sample->timestamp * (int64_t)msc->time_rate, + AV_TIME_BASE, msc->time_scale); + dprintf(s, "stream %d, sample %d, dts %"PRId64"\n", i, msc->current_sample, dts); + if (!sample || (url_is_streamed(s->pb) && current_sample->pos < sample->pos) || + (!url_is_streamed(s->pb) && + ((msc->pb != s->pb && dts < best_dts) || (msc->pb == s->pb && + ((FFABS(best_dts - dts) <= AV_TIME_BASE && current_sample->pos < sample->pos) || + (FFABS(best_dts - dts) > AV_TIME_BASE && dts < best_dts)))))) { sample = current_sample; best_dts = dts; sc = msc; @@ -1567,24 +1611,19 @@ static int mov_read_packet(AVFormatContext *s, AVPacket *pkt) return -1; /* must be done just before reading, to avoid infinite loop on sample */ sc->current_sample++; - if (sample->pos >= url_fsize(&s->pb)) { - av_log(mov->fc, AV_LOG_ERROR, "stream %d, offset 0x%"PRIx64": partial file\n", sc->ffindex, sample->pos); + if (url_fseek(sc->pb, sample->pos, SEEK_SET) != sample->pos) { + av_log(mov->fc, AV_LOG_ERROR, "stream %d, offset 0x%"PRIx64": partial file\n", + sc->ffindex, sample->pos); return -1; } + av_get_packet(sc->pb, pkt, sample->size); #ifdef CONFIG_DV_DEMUXER - if (sc->dv_audio_container) { - dv_get_packet(mov->dv_demux, pkt); - dprintf(s, "dv audio pkt size %d\n", pkt->size); - } else { -#endif - url_fseek(&s->pb, sample->pos, SEEK_SET); - av_get_packet(&s->pb, pkt, sample->size); -#ifdef CONFIG_DV_DEMUXER - if (mov->dv_demux) { - void *pkt_destruct_func = pkt->destruct; - dv_produce_packet(mov->dv_demux, pkt, pkt->data, pkt->size); - pkt->destruct = pkt_destruct_func; - } + if (mov->dv_demux && sc->dv_audio_container) { + dv_produce_packet(mov->dv_demux, pkt, pkt->data, pkt->size); + av_free(pkt->data); + pkt->size = 0; + if (dv_get_packet(mov->dv_demux, pkt) < 0) + return -1; } #endif pkt->stream_index = sc->ffindex; @@ -1594,7 +1633,8 @@ static int mov_read_packet(AVFormatContext *s, AVPacket *pkt) pkt->pts = pkt->dts + sc->ctts_data[sc->sample_to_ctime_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) { + 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; } @@ -1603,7 +1643,8 @@ static int mov_read_packet(AVFormatContext *s, AVPacket *pkt) } pkt->flags |= sample->flags & AVINDEX_KEYFRAME ? PKT_FLAG_KEY : 0; pkt->pos = sample->pos; - dprintf(s, "stream %d, pts %"PRId64", dts %"PRId64", pos 0x%"PRIx64", duration %d\n", pkt->stream_index, pkt->pts, pkt->dts, pkt->pos, pkt->duration); + dprintf(s, "stream %d, pts %"PRId64", dts %"PRId64", pos 0x%"PRIx64", duration %d\n", + pkt->stream_index, pkt->pts, pkt->dts, pkt->pos, pkt->duration); return 0; } @@ -1618,17 +1659,18 @@ static int mov_seek_stream(AVStream *st, int64_t timestamp, int flags) if (sample < 0) /* not sure what to do */ return -1; sc->current_sample = sample; - dprintf(st->codec, "stream %d, found sample %ld\n", st->index, sc->current_sample); + dprintf(st->codec, "stream %d, found sample %d\n", st->index, sc->current_sample); /* adjust ctts index */ if (sc->ctts_data) { time_sample = 0; for (i = 0; i < sc->ctts_count; i++) { - time_sample += sc->ctts_data[i].count; - if (time_sample >= sc->current_sample) { + 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 = time_sample - sc->current_sample; + sc->sample_to_ctime_sample = sc->current_sample - time_sample; break; } + time_sample = next; } } return sample; @@ -1665,13 +1707,17 @@ static int mov_read_seek(AVFormatContext *s, int stream_index, int64_t sample_ti static int mov_read_close(AVFormatContext *s) { - int i; - MOVContext *mov = (MOVContext *) s->priv_data; - for(i=0; i<mov->total_streams; i++) - mov_free_stream_context(mov->streams[i]); - /* free color tabs */ - for(i=0; i<mov->ctab_size; i++) - av_freep(&mov->ctab[i]); + int i, j; + MOVContext *mov = s->priv_data; + for(i=0; i<s->nb_streams; i++) { + MOVStreamContext *sc = s->streams[i]->priv_data; + av_freep(&sc->ctts_data); + for (j=0; j<sc->drefs_count; j++) + av_freep(&sc->drefs[j].path); + av_freep(&sc->drefs); + if (sc->pb && sc->pb != s->pb) + url_fclose(sc->pb); + } if(mov->dv_demux){ for(i=0; i<mov->dv_fctx->nb_streams; i++){ av_freep(&mov->dv_fctx->streams[i]->codec); @@ -1680,7 +1726,6 @@ static int mov_read_close(AVFormatContext *s) av_freep(&mov->dv_fctx); av_freep(&mov->dv_demux); } - av_freep(&mov->ctab); return 0; } |