X-Git-Url: https://git.sesse.net/?a=blobdiff_plain;f=libavformat%2Fmov.c;h=2c46523373bf72d0c16b71ccfb250fd38855b2cb;hb=69b192acb8311951adf603259539ed3ceaccbe50;hp=f088e5c1e6eee7f8775acadc7146ea8c82a75702;hpb=e809d7e50ab6336f077dccb48159741fd0693bc2;p=ffmpeg diff --git a/libavformat/mov.c b/libavformat/mov.c index f088e5c1e6e..2c46523373b 100644 --- a/libavformat/mov.c +++ b/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) @@ -66,109 +58,21 @@ #undef NDEBUG #include -static const AVCodecTag mov_video_tags[] = { -/* { CODEC_ID_, MKTAG('c', 'v', 'i', 'd') }, *//* Cinepak */ -/* { CODEC_ID_H263, MKTAG('r', 'a', 'w', ' ') }, *//* Uncompressed RGB */ -/* { CODEC_ID_H263, MKTAG('Y', 'u', 'v', '2') }, *//* Uncompressed YUV422 */ -/* { CODEC_ID_RAWVIDEO, MKTAG('A', 'V', 'U', 'I') }, *//* YUV with alpha-channel (AVID Uncompressed) */ -/* Graphics */ -/* Animation */ -/* Apple video */ -/* Kodak Photo CD */ - { CODEC_ID_MJPEG, MKTAG('j', 'p', 'e', 'g') }, /* PhotoJPEG */ - { CODEC_ID_MPEG1VIDEO, MKTAG('m', 'p', 'e', 'g') }, /* MPEG */ - { CODEC_ID_MJPEG, MKTAG('m', 'j', 'p', 'a') }, /* Motion-JPEG (format A) */ - { CODEC_ID_MJPEGB, MKTAG('m', 'j', 'p', 'b') }, /* Motion-JPEG (format B) */ - { CODEC_ID_MJPEG, MKTAG('A', 'V', 'D', 'J') }, /* MJPEG with alpha-channel (AVID JFIF meridien compressed) */ -/* { CODEC_ID_MJPEG, MKTAG('A', 'V', 'R', 'n') }, *//* MJPEG with alpha-channel (AVID ABVB/Truevision NuVista) */ - { CODEC_ID_GIF, MKTAG('g', 'i', 'f', ' ') }, /* embedded gif files as frames (usually one "click to play movie" frame) */ -/* Sorenson video */ - { CODEC_ID_SVQ1, MKTAG('S', 'V', 'Q', '1') }, /* Sorenson Video v1 */ - { CODEC_ID_SVQ1, MKTAG('s', 'v', 'q', '1') }, /* Sorenson Video v1 */ - { CODEC_ID_SVQ1, MKTAG('s', 'v', 'q', 'i') }, /* Sorenson Video v1 (from QT specs)*/ - { CODEC_ID_SVQ3, MKTAG('S', 'V', 'Q', '3') }, /* Sorenson Video v3 */ - { CODEC_ID_MPEG4, MKTAG('m', 'p', '4', 'v') }, - { CODEC_ID_MPEG4, MKTAG('D', 'I', 'V', 'X') }, /* OpenDiVX *//* sample files at http://heroinewarrior.com/xmovie.php3 use this tag */ - { CODEC_ID_MPEG4, MKTAG('X', 'V', 'I', 'D') }, - { CODEC_ID_MPEG4, MKTAG('3', 'I', 'V', '2') }, /* experimental: 3IVX files before ivx D4 4.5.1 */ -/* { CODEC_ID_, MKTAG('I', 'V', '5', '0') }, *//* Indeo 5.0 */ - { CODEC_ID_H263, MKTAG('h', '2', '6', '3') }, /* H263 */ - { CODEC_ID_H263, MKTAG('s', '2', '6', '3') }, /* H263 ?? works */ - { CODEC_ID_DVVIDEO, MKTAG('d', 'v', 'c', ' ') }, /* DV NTSC */ - { CODEC_ID_DVVIDEO, MKTAG('d', 'v', 'c', 'p') }, /* DV PAL */ - { CODEC_ID_VP3, MKTAG('V', 'P', '3', '1') }, /* On2 VP3 */ - { CODEC_ID_RPZA, MKTAG('r', 'p', 'z', 'a') }, /* Apple Video (RPZA) */ - { CODEC_ID_CINEPAK, MKTAG('c', 'v', 'i', 'd') }, /* Cinepak */ - { CODEC_ID_8BPS, MKTAG('8', 'B', 'P', 'S') }, /* Planar RGB (8BPS) */ - { CODEC_ID_SMC, MKTAG('s', 'm', 'c', ' ') }, /* Apple Graphics (SMC) */ - { CODEC_ID_QTRLE, MKTAG('r', 'l', 'e', ' ') }, /* Apple Animation (RLE) */ - { CODEC_ID_MSRLE, MKTAG('W', 'R', 'L', 'E') }, - { CODEC_ID_QDRAW, MKTAG('q', 'd', 'r', 'w') }, /* QuickDraw */ - { CODEC_ID_H264, MKTAG('a', 'v', 'c', '1') }, /* AVC-1/H.264 */ - { CODEC_ID_MPEG2VIDEO, MKTAG('h', 'd', 'v', '2') }, /* MPEG2 produced by Sony HD camera */ - { CODEC_ID_MPEG2VIDEO, MKTAG('h', 'd', 'v', '3') }, /* HDV produced by FCP */ - { CODEC_ID_MPEG2VIDEO, MKTAG('m', 'x', '5', 'n') }, /* MPEG2 IMX NTSC 525/60 50mb/s produced by FCP */ - { CODEC_ID_MPEG2VIDEO, MKTAG('m', 'x', '5', 'p') }, /* MPEG2 IMX PAL 625/50 50mb/s produced by FCP */ - { CODEC_ID_MPEG2VIDEO, MKTAG('m', 'x', '3', 'n') }, /* MPEG2 IMX NTSC 525/60 30mb/s produced by FCP */ - { CODEC_ID_MPEG2VIDEO, MKTAG('m', 'x', '3', 'p') }, /* MPEG2 IMX PAL 625/50 30mb/s produced by FCP */ - { CODEC_ID_DVVIDEO, MKTAG('d', 'v', 'p', 'p') }, /* DVCPRO PAL produced by FCP */ - //{ CODEC_ID_DVVIDEO, MKTAG('d', 'v', 'h', '5') }, /* DVCPRO HD 50i produced by FCP */ - //{ CODEC_ID_DVVIDEO, MKTAG('d', 'v', 'h', '6') }, /* DVCPRO HD 60i produced by FCP */ - { CODEC_ID_DVVIDEO, MKTAG('d', 'v', '5', 'p') }, /* DVCPRO50 PAL produced by FCP */ - { CODEC_ID_DVVIDEO, MKTAG('d', 'v', '5', 'n') }, /* DVCPRO50 NTSC produced by FCP */ - { CODEC_ID_DVVIDEO, MKTAG('A', 'V', 'd', 'v') }, /* AVID DV */ - //{ CODEC_ID_JPEG2000, MKTAG('m', 'j', 'p', '2') }, /* JPEG 2000 produced by FCP */ - { CODEC_ID_TARGA, MKTAG('t', 'g', 'a', ' ') }, /* Truevision Targa */ - { CODEC_ID_TIFF, MKTAG('t', 'i', 'f', 'f') }, /* TIFF embedded in MOV */ - { CODEC_ID_RAWVIDEO, MKTAG('2', 'v', 'u', 'y') }, /* UNCOMPRESSED 8BIT 4:2:2 */ - { CODEC_ID_VC1, MKTAG('v', 'c', '-', '1') }, /* SMPTE RP 2025 */ - { CODEC_ID_WMV3, MKTAG('v', 'c', '-', '1') }, /* SMPTE RP 2025 */ - { CODEC_ID_PNG, MKTAG('p', 'n', 'g', ' ') }, - { CODEC_ID_NONE, 0 }, -}; - -static const AVCodecTag mov_audio_tags[] = { - { CODEC_ID_PCM_S32BE, MKTAG('i', 'n', '3', '2') }, - { CODEC_ID_PCM_S24BE, MKTAG('i', 'n', '2', '4') }, - { CODEC_ID_PCM_S16BE, MKTAG('N', 'O', 'N', 'E') }, /* uncompressed */ - { CODEC_ID_PCM_S16BE, MKTAG('t', 'w', 'o', 's') }, /* 16 bits */ - { CODEC_ID_PCM_U8, MKTAG('r', 'a', 'w', ' ') }, /* 8 bits unsigned */ - { CODEC_ID_PCM_S16LE, MKTAG('s', 'o', 'w', 't') }, /* */ - { CODEC_ID_PCM_MULAW, MKTAG('u', 'l', 'a', 'w') }, /* */ - { CODEC_ID_PCM_ALAW, MKTAG('a', 'l', 'a', 'w') }, /* */ - { CODEC_ID_ADPCM_IMA_QT, MKTAG('i', 'm', 'a', '4') }, /* IMA-4 ADPCM */ - { CODEC_ID_ADPCM_MS, MKTAG('m', 's', 0x00, 0x02) }, /* MS ADPCM */ - { CODEC_ID_MACE3, MKTAG('M', 'A', 'C', '3') }, /* Macintosh Audio Compression and Expansion 3:1 */ - { CODEC_ID_MACE6, MKTAG('M', 'A', 'C', '6') }, /* Macintosh Audio Compression and Expansion 6:1 */ - - { CODEC_ID_MP3, MKTAG('.', 'm', 'p', '3') }, /* MPEG layer 3 */ /* sample files at http://www.3ivx.com/showcase.html use this tag */ - { CODEC_ID_MP2, 0x6D730055 }, /* MPEG layer 3 */ - { CODEC_ID_MP2, 0x5500736D }, /* MPEG layer 3 *//* XXX: check endianness */ -/* { CODEC_ID_OGG_VORBIS, MKTAG('O', 'g', 'g', 'S') }, *//* sample files at http://heroinewarrior.com/xmovie.php3 use this tag */ -/* MP4 tags */ - { CODEC_ID_AAC, MKTAG('m', 'p', '4', 'a') }, /* MPEG-4 AAC */ - /* The standard for mpeg4 audio is still not normalised AFAIK anyway */ - { CODEC_ID_AMR_NB, MKTAG('s', 'a', 'm', 'r') }, /* AMR-NB 3gp */ - { CODEC_ID_AMR_WB, MKTAG('s', 'a', 'w', 'b') }, /* AMR-WB 3gp */ - { CODEC_ID_AC3, MKTAG('m', 's', 0x20, 0x00) }, /* Dolby AC-3 */ - { CODEC_ID_ALAC,MKTAG('a', 'l', 'a', 'c') }, /* Apple Lossless */ - { CODEC_ID_QDM2,MKTAG('Q', 'D', 'M', '2') }, /* QDM2 */ - { CODEC_ID_DVAUDIO, MKTAG('v', 'd', 'v', 'a') }, - { CODEC_ID_DVAUDIO, MKTAG('d', 'v', 'c', 'a') }, - { CODEC_ID_PCM_S16LE, MKTAG('l', 'p', 'c', 'm') }, - { CODEC_ID_NONE, 0 }, -}; - /* the QuickTime file format is quite convoluted... * it has lots of index tables, each indexing something in another one... * 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; @@ -176,93 +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; - AVRational sample_size_v1; + 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 { @@ -271,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) */ @@ -297,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; @@ -315,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("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; @@ -336,21 +176,23 @@ static int mov_read_default(MOVContext *c, ByteIOContext *pb, MOV_atom_t atom) if (a.size <= 8) break; } - for (i = 0; c->parse_table[i].type != 0L - && c->parse_table[i].type != a.type; i++) - /* empty */; - a.size -= 8; - if(a.size < 0) break; + a.size = FFMIN(a.size, atom.size - total_size); - if (c->parse_table[i].type == 0) { /* skip leaf atoms data */ + for (i = 0; mov_default_parse_table[i].type != 0 + && mov_default_parse_table[i].type != a.type; i++) + /* empty */; + + 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); @@ -367,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; } @@ -404,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("ctype= %c%c%c%c (0x%08lx)\n", *((char *)&ctype), ((char *)&ctype)[1], ((char *)&ctype)[2], ((char *)&ctype)[3], (long) ctype); - dprintf("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')) @@ -416,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 */ @@ -429,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; @@ -442,51 +329,55 @@ static int mov_mp4_read_descr_len(ByteIOContext *pb) return len; } -static int mov_mp4_read_descr(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); - dprintf("MPEG4 description: tag=0x%02x len=%d\n", *tag, len); + 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(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(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_mov_obj_type, sc->esds.object_type_id); - dprintf("esds object type id %d\n", sc->esds.object_type_id); - len = mov_mp4_read_descr(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("Specific MPEG4 header len=%d\n", len); + 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; } } } @@ -498,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); @@ -525,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; @@ -540,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; @@ -581,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 */ @@ -614,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("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; } @@ -647,23 +529,23 @@ static int mov_read_enda(MOVContext *c, ByteIOContext *pb, MOV_atom_t atom) return 0; } -static int mov_read_alac(MOVContext *c, ByteIOContext *pb, MOV_atom_t atom) +/* FIXME modify qdm2/svq3/h264 decoders to take full atom as extradata */ +static int mov_read_extradata(MOVContext *c, ByteIOContext *pb, MOV_atom_t atom) { AVStream *st = c->fc->streams[c->fc->nb_streams-1]; - - // currently ALAC decoder expect full atom header - so let's fake it - // this should be fixed and just ALAC header should be passed - - av_free(st->codec->extradata); - st->codec->extradata_size = 36; - st->codec->extradata = av_mallocz(st->codec->extradata_size + FF_INPUT_BUFFER_PADDING_SIZE); - - if (st->codec->extradata) { - memcpy(st->codec->extradata + 4, "alac", 4); // fake - get_buffer(pb, st->codec->extradata + 8, 36 - 8); - dprintf("Reading alac %d %s\n", st->codec->extradata_size, st->codec->extradata); - } else - url_fskip(pb, atom.size); + 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; + 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; } @@ -677,42 +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); - } else - url_fskip(pb, atom.size); - return 0; -} - -static int mov_read_jp2h(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)) - 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); - - /* pass all jp2h atom to codec */ - if (st->codec->extradata) { - memcpy(st->codec->extradata + 4, "jp2h", 4); - get_buffer(pb, st->codec->extradata + 8, atom.size); + 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]; @@ -720,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 */ @@ -767,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 */ @@ -789,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_idcodec->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 is not correct, we should + * export it as a separate 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(mov_audio_tags, 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 */ format && format != MKTAG('m', 'p', '4', 's')) { /* skip old asf mpeg4 tag */ - id = codec_get_id(mov_video_tags, format); + id = codec_get_id(codec_movvideo_tags, format); if (id <= 0) 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("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; @@ -830,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 */ @@ -839,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]); @@ -850,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; @@ -858,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); @@ -873,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) @@ -892,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 @@ -929,12 +802,10 @@ static int mov_read_stsd(MOVContext *c, ByteIOContext *pb, MOV_atom_t atom) get_be32(pb); /* vendor */ st->codec->channels = get_be16(pb); /* channel count */ - dprintf("audio channels %d\n", st->codec->channels); + 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)); @@ -952,17 +823,30 @@ 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 - dprintf("version =%d, isom =%d\n",version,c->isom); + //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) { - sc->sample_size_v1.den = get_be32(pb); /* samples per packet */ + sc->samples_per_frame = get_be32(pb); get_be32(pb); /* bytes per packet */ - sc->sample_size_v1.num = get_be32(pb); /* bytes per frame */ + sc->bytes_per_frame = get_be32(pb); get_be32(pb); /* bytes per sample */ } else if(version==2) { get_be32(pb); /* sizeof struct only */ @@ -981,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(); @@ -1042,8 +907,13 @@ static int mov_read_stsd(MOVContext *c, ByteIOContext *pb, MOV_atom_t atom) st->codec->channels= 1; /* really needed */ break; 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; @@ -1055,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 */ @@ -1063,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; ifc->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 */ @@ -1092,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; ikeyframes[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; } @@ -1114,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 */ @@ -1124,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; isample_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; } @@ -1149,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; @@ -1157,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; @@ -1180,7 +1043,7 @@ av_log(NULL, AV_LOG_DEBUG, "track[%i].stts.entries = %i\n", c->fc->nb_streams-1, sc->time_rate= ff_gcd(sc->time_rate, sample_duration); - dprintf("sample_count=%d, sample_duration=%d\n",sample_count,sample_duration); + dprintf(c->fc, "sample_count=%d, sample_duration=%d\n",sample_count,sample_duration); duration+=(int64_t)sample_duration*sample_count; total_sample_count+=sample_count; @@ -1195,19 +1058,20 @@ 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)); - - dprintf("track[%i].ctts.entries = %i\n", c->fc->nb_streams-1, entries); + 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; ipriv_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]; @@ -1368,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; ifc, AV_LOG_WARNING, "edit list not starting at 0, " + "a/v desync might occur, patch welcome\n"); } - dprintf("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; } @@ -1387,12 +1298,16 @@ 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_jp2h }, +{ MKTAG( 'j', 'p', '2', 'h' ), mov_read_extradata }, { MKTAG( 'm', 'd', 'a', 't' ), mov_read_mdat }, { MKTAG( 'm', 'd', 'h', 'd' ), mov_read_mdhd }, { MKTAG( 'm', 'd', 'i', 'a' ), mov_read_default }, @@ -1400,8 +1315,8 @@ static const MOVParseTableEntry mov_default_parse_table[] = { { MKTAG( 'm', 'o', 'o', 'v' ), mov_read_moov }, { MKTAG( 'm', 'v', 'h', 'd' ), mov_read_mvhd }, { MKTAG( 'S', 'M', 'I', ' ' ), mov_read_smi }, /* Sorenson extension ??? */ -{ MKTAG( 'a', 'l', 'a', 'c' ), mov_read_alac }, /* alac specific atom */ -{ MKTAG( 'a', 'v', 'c', 'C' ), mov_read_avcC }, +{ MKTAG( 'a', 'l', 'a', 'c' ), mov_read_extradata }, /* alac specific atom */ +{ 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 }, @@ -1411,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) { @@ -1435,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 */ @@ -1452,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' ): @@ -1480,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("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; @@ -1522,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->sample_size_v1.den > 0 && (chunk_samples * sc->sample_size_v1.num % sc->sample_size_v1.den == 0)) - chunk_size = chunk_samples * sc->sample_size_v1.num / sc->sample_size_v1.den; - 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("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("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: @@ -1588,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); @@ -1608,45 +1508,75 @@ static int mov_read_header(AVFormatContext *s, AVFormatParameters *ap) err, mov->found_moov, mov->found_mdat, url_ftell(pb)); return -1; } - dprintf("on_parse_exit_offset=%d\n", (int) url_ftell(pb)); + 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; itotal_streams; i++) { - MOVStreamContext *sc = mov->streams[i]; + for(i=0; inb_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)){ av_log(s, AV_LOG_ERROR, "missing mandatory atoms, broken header\n"); - return -1; + sc->sample_count = 0; //ignore track + continue; } if(!sc->time_rate) sc->time_rate=1; if(!sc->time_scale) sc->time_scale= mov->time_scale; - av_set_pts_info(s->streams[i], 64, sc->time_rate, sc->time_scale); + av_set_pts_info(st, 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; + if (st->codec->codec_type == CODEC_TYPE_AUDIO && sc->stts_count == 1) + 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); + st->duration /= sc->time_rate; } sc->ffindex = i; - mov_build_index(mov, s->streams[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; itotal_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; inb_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; } @@ -1658,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("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; @@ -1677,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("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; @@ -1704,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; } @@ -1713,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("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; } @@ -1724,21 +1655,22 @@ static int mov_seek_stream(AVStream *st, int64_t timestamp, int flags) int i; sample = av_index_search_timestamp(st, timestamp, flags); - dprintf("stream %d, timestamp %"PRId64", sample %d\n", st->index, timestamp, sample); + dprintf(st->codec, "stream %d, timestamp %"PRId64", sample %d\n", st->index, timestamp, sample); if (sample < 0) /* not sure what to do */ return -1; sc->current_sample = sample; - dprintf("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; @@ -1775,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; itotal_streams; i++) - mov_free_stream_context(mov->streams[i]); - /* free color tabs */ - for(i=0; ictab_size; i++) - av_freep(&mov->ctab[i]); + int i, j; + MOVContext *mov = s->priv_data; + for(i=0; inb_streams; i++) { + MOVStreamContext *sc = s->streams[i]->priv_data; + av_freep(&sc->ctts_data); + for (j=0; jdrefs_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; idv_fctx->nb_streams; i++){ av_freep(&mov->dv_fctx->streams[i]->codec); @@ -1790,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; }