X-Git-Url: https://git.sesse.net/?a=blobdiff_plain;f=libavformat%2Fmov.c;h=99fd2af57380aa6a9817f509c0931c59d3f354f2;hb=6e9bbc6525989f7dc51acc76712c6ca674053b60;hp=cede1f751ad640618d486a3abd530e28c3040be6;hpb=5f0bb0baefd506d684adfa1ad4259c65973b455e;p=ffmpeg diff --git a/libavformat/mov.c b/libavformat/mov.c index cede1f751ad..99fd2af5738 100644 --- a/libavformat/mov.c +++ b/libavformat/mov.c @@ -23,16 +23,24 @@ #include //#define DEBUG -//#define DEBUG_METADATA //#define MOV_EXPORT_ALL_METADATA +#include "libavutil/attributes.h" +#include "libavutil/audioconvert.h" #include "libavutil/intreadwrite.h" +#include "libavutil/intfloat.h" +#include "libavutil/mathematics.h" #include "libavutil/avstring.h" +#include "libavutil/dict.h" +#include "libavcodec/ac3tab.h" #include "avformat.h" +#include "internal.h" #include "avio_internal.h" #include "riff.h" #include "isom.h" #include "libavcodec/get_bits.h" +#include "id3v1.h" +#include "mov_chan.h" #if CONFIG_ZLIB #include @@ -41,21 +49,6 @@ /* * First version by Francois Revol revol@free.fr * Seek function by Gael Chardon gael.dev@4now.net - * - * Features and limitations: - * - reads most of the QT files I have (at least the structure), - * Sample QuickTime files with mp3 audio can be found at: http://www.3ivx.com/showcase.html - * - the code is quite ugly... maybe I won't do it recursive next time :-) - * - * Funny I didn't know about http://sourceforge.net/projects/qt-ffmpeg/ - * when coding this :) (it's a writer anyway) - * - * Reference documents: - * http://www.geocities.com/xhelmboyx/quicktime/formats/qtm-layout.txt - * Apple: - * http://developer.apple.com/documentation/QuickTime/QTFF/ - * http://developer.apple.com/documentation/QuickTime/QTFF/qtff.pdf - * QuickTime is a trademark of Apple (AFAIK :)) */ #include "qtpalette.h" @@ -64,30 +57,74 @@ #undef NDEBUG #include -/* XXX: it's the first time I make a recursive parser I think... sorry if it's ugly :P */ - /* those functions parse an atom */ -/* return code: - 0: continue to parse next atom - <0: error occurred, exit -*/ /* links atom IDs to parse functions */ typedef struct MOVParseTableEntry { uint32_t type; int (*parse)(MOVContext *ctx, AVIOContext *pb, MOVAtom atom); } MOVParseTableEntry; -static const MOVParseTableEntry mov_default_parse_table[]; +static int mov_read_default(MOVContext *c, AVIOContext *pb, MOVAtom atom); -static int mov_metadata_trkn(MOVContext *c, AVIOContext *pb, unsigned len) +static int mov_metadata_track_or_disc_number(MOVContext *c, AVIOContext *pb, + unsigned len, const char *key) { char buf[16]; + short current, total = 0; avio_rb16(pb); // unknown - snprintf(buf, sizeof(buf), "%d", avio_rb16(pb)); - av_metadata_set2(&c->fc->metadata, "track", buf, 0); + current = avio_rb16(pb); + if (len >= 6) + total = avio_rb16(pb); + if (!total) + snprintf(buf, sizeof(buf), "%d", current); + else + snprintf(buf, sizeof(buf), "%d/%d", current, total); + av_dict_set(&c->fc->metadata, key, buf, 0); + + return 0; +} + +static int mov_metadata_int8_bypass_padding(MOVContext *c, AVIOContext *pb, + unsigned len, const char *key) +{ + char buf[16]; + + /* bypass padding bytes */ + avio_r8(pb); + avio_r8(pb); + avio_r8(pb); - avio_rb16(pb); // total tracks + snprintf(buf, sizeof(buf), "%d", avio_r8(pb)); + av_dict_set(&c->fc->metadata, key, buf, 0); + + return 0; +} + +static int mov_metadata_int8_no_padding(MOVContext *c, AVIOContext *pb, + unsigned len, const char *key) +{ + char buf[16]; + + snprintf(buf, sizeof(buf), "%d", avio_r8(pb)); + av_dict_set(&c->fc->metadata, key, buf, 0); + + return 0; +} + +static int mov_metadata_gnre(MOVContext *c, AVIOContext *pb, + unsigned len, const char *key) +{ + short genre; + char buf[20]; + + avio_r8(pb); // unknown + + genre = avio_r8(pb); + if (genre < 1 || genre > ID3v1_GENRE_MAX) + return 0; + snprintf(buf, sizeof(buf), "%s", ff_id3v1_genre_str[genre-1]); + av_dict_set(&c->fc->metadata, key, buf, 0); return 0; } @@ -129,6 +166,48 @@ static int mov_read_mac_string(MOVContext *c, AVIOContext *pb, int len, return p - dst; } +static int mov_read_covr(MOVContext *c, AVIOContext *pb, int type, int len) +{ + AVPacket pkt; + AVStream *st; + MOVStreamContext *sc; + enum AVCodecID id; + int ret; + + switch (type) { + case 0xd: id = AV_CODEC_ID_MJPEG; break; + case 0xe: id = AV_CODEC_ID_PNG; break; + case 0x1b: id = AV_CODEC_ID_BMP; break; + default: + av_log(c->fc, AV_LOG_WARNING, "Unknown cover type: 0x%x.\n", type); + avio_skip(pb, len); + return 0; + } + + st = avformat_new_stream(c->fc, NULL); + if (!st) + return AVERROR(ENOMEM); + sc = av_mallocz(sizeof(*sc)); + if (!sc) + return AVERROR(ENOMEM); + st->priv_data = sc; + + ret = av_get_packet(pb, &pkt, len); + if (ret < 0) + return ret; + + st->disposition |= AV_DISPOSITION_ATTACHED_PIC; + + st->attached_pic = pkt; + st->attached_pic.stream_index = st->index; + st->attached_pic.flags |= AV_PKT_FLAG_KEY; + + st->codec->codec_type = AVMEDIA_TYPE_VIDEO; + st->codec->codec_id = id; + + return 0; +} + static int mov_read_udta_string(MOVContext *c, AVIOContext *pb, MOVAtom atom) { #ifdef MOV_EXPORT_ALL_METADATA @@ -136,14 +215,15 @@ static int mov_read_udta_string(MOVContext *c, AVIOContext *pb, MOVAtom atom) #endif char str[1024], key2[16], language[4] = {0}; const char *key = NULL; - uint16_t str_size, langcode = 0; - uint32_t data_type = 0; - int (*parse)(MOVContext*, AVIOContext*, unsigned) = NULL; + uint16_t langcode = 0; + uint32_t data_type = 0, str_size; + int (*parse)(MOVContext*, AVIOContext*, unsigned, const char*) = NULL; switch (atom.type) { case MKTAG(0xa9,'n','a','m'): key = "title"; break; case MKTAG(0xa9,'a','u','t'): case MKTAG(0xa9,'A','R','T'): key = "artist"; break; + case MKTAG( 'a','A','R','T'): key = "album_artist"; break; case MKTAG(0xa9,'w','r','t'): key = "composer"; break; case MKTAG( 'c','p','r','t'): case MKTAG(0xa9,'c','p','y'): key = "copyright"; break; @@ -152,6 +232,8 @@ static int mov_read_udta_string(MOVContext *c, AVIOContext *pb, MOVAtom atom) case MKTAG(0xa9,'a','l','b'): key = "album"; break; case MKTAG(0xa9,'d','a','y'): key = "date"; break; case MKTAG(0xa9,'g','e','n'): key = "genre"; break; + case MKTAG( 'g','n','r','e'): key = "genre"; + parse = mov_metadata_gnre; break; case MKTAG(0xa9,'t','o','o'): case MKTAG(0xa9,'s','w','r'): key = "encoder"; break; case MKTAG(0xa9,'e','n','c'): key = "encoder"; break; @@ -161,7 +243,19 @@ static int mov_read_udta_string(MOVContext *c, AVIOContext *pb, MOVAtom atom) case MKTAG( 't','v','e','n'): key = "episode_id";break; case MKTAG( 't','v','n','n'): key = "network"; break; case MKTAG( 't','r','k','n'): key = "track"; - parse = mov_metadata_trkn; break; + parse = mov_metadata_track_or_disc_number; break; + case MKTAG( 'd','i','s','k'): key = "disc"; + parse = mov_metadata_track_or_disc_number; break; + case MKTAG( 't','v','e','s'): key = "episode_sort"; + parse = mov_metadata_int8_bypass_padding; break; + case MKTAG( 't','v','s','n'): key = "season_number"; + parse = mov_metadata_int8_bypass_padding; break; + case MKTAG( 's','t','i','k'): key = "media_type"; + parse = mov_metadata_int8_no_padding; break; + case MKTAG( 'h','d','v','d'): key = "hd_video"; + parse = mov_metadata_int8_no_padding; break; + case MKTAG( 'p','g','a','p'): key = "gapless_playback"; + parse = mov_metadata_int8_no_padding; break; } if (c->itunes_metadata && atom.size > 8) { @@ -172,6 +266,14 @@ static int mov_read_udta_string(MOVContext *c, AVIOContext *pb, MOVAtom atom) avio_rb32(pb); // unknown str_size = data_size - 16; atom.size -= 16; + + if (atom.type == MKTAG('c', 'o', 'v', 'r')) { + int ret = mov_read_covr(c, pb, data_type, str_size); + if (ret < 0) { + av_log(c->fc, AV_LOG_ERROR, "Error parsing cover art.\n"); + return ret; + } + } } else return 0; } else if (atom.size > 4 && key && !c->itunes_metadata) { str_size = avio_rb16(pb); // string length @@ -191,12 +293,12 @@ static int mov_read_udta_string(MOVContext *c, AVIOContext *pb, MOVAtom atom) if (!key) return 0; if (atom.size < 0) - return -1; + return AVERROR_INVALIDDATA; str_size = FFMIN3(sizeof(str)-1, str_size, atom.size); if (parse) - parse(c, pb, str_size); + parse(c, pb, str_size, key); else { if (data_type == 3 || (data_type == 0 && langcode < 0x800)) { // MAC Encoded mov_read_mac_string(c, pb, str_size, str, sizeof(str)); @@ -204,17 +306,15 @@ static int mov_read_udta_string(MOVContext *c, AVIOContext *pb, MOVAtom atom) avio_read(pb, str, str_size); str[str_size] = 0; } - av_metadata_set2(&c->fc->metadata, key, str, 0); + av_dict_set(&c->fc->metadata, key, str, 0); if (*language && strcmp(language, "und")) { snprintf(key2, sizeof(key2), "%s-%s", key, language); - av_metadata_set2(&c->fc->metadata, key2, str, 0); + av_dict_set(&c->fc->metadata, key2, str, 0); } } -#ifdef DEBUG_METADATA - av_log(c->fc, AV_LOG_DEBUG, "lang \"%3s\" ", language); - av_log(c->fc, AV_LOG_DEBUG, "tag \"%s\" value \"%s\" atom \"%.4s\" %d %lld\n", - key, str, (char*)&atom.type, str_size, atom.size); -#endif + av_dlog(c->fc, "lang \"%3s\" ", language); + av_dlog(c->fc, "tag \"%s\" value \"%s\" atom \"%.4s\" %d %"PRId64"\n", + key, str, (char*)&atom.type, str_size, atom.size); return 0; } @@ -246,77 +346,8 @@ static int mov_read_chpl(MOVContext *c, AVIOContext *pb, MOVAtom atom) avio_read(pb, str, str_len); str[str_len] = 0; - ff_new_chapter(c->fc, i, (AVRational){1,10000000}, start, AV_NOPTS_VALUE, str); - } - return 0; -} - -static int mov_read_default(MOVContext *c, AVIOContext *pb, MOVAtom atom) -{ - int64_t total_size = 0; - MOVAtom a; - int i; - - if (atom.size < 0) - atom.size = INT64_MAX; - while (total_size + 8 < atom.size && !pb->eof_reached) { - int (*parse)(MOVContext*, AVIOContext*, MOVAtom) = NULL; - a.size = atom.size; - a.type=0; - if(atom.size >= 8) { - a.size = avio_rb32(pb); - a.type = avio_rl32(pb); - } - av_dlog(c->fc, "type: %08x '%.4s' parent:'%.4s' sz: %"PRId64" %"PRId64" %"PRId64"\n", - a.type, (char*)&a.type, (char*)&atom.type, a.size, total_size, atom.size); - total_size += 8; - if (a.size == 1) { /* 64 bit extended size */ - a.size = avio_rb64(pb) - 8; - total_size += 8; - } - if (a.size == 0) { - a.size = atom.size - total_size; - if (a.size <= 8) - break; - } - a.size -= 8; - if(a.size < 0) - break; - a.size = FFMIN(a.size, atom.size - total_size); - - for (i = 0; mov_default_parse_table[i].type; i++) - if (mov_default_parse_table[i].type == a.type) { - parse = mov_default_parse_table[i].parse; - break; - } - - // container is user data - if (!parse && (atom.type == MKTAG('u','d','t','a') || - atom.type == MKTAG('i','l','s','t'))) - parse = mov_read_udta_string; - - if (!parse) { /* skip leaf atoms data */ - avio_skip(pb, a.size); - } else { - int64_t start_pos = avio_tell(pb); - int64_t left; - int err = parse(c, pb, a); - if (err < 0) - return err; - if (c->found_moov && c->found_mdat && - (!pb->seekable || start_pos + a.size == avio_size(pb))) - return 0; - left = a.size - avio_tell(pb) + start_pos; - if (left > 0) /* skip garbage at atom end */ - avio_skip(pb, left); - } - - total_size += a.size; + avpriv_new_chapter(c->fc, i, (AVRational){1,10000000}, start, AV_NOPTS_VALUE, str); } - - if (total_size < atom.size && atom.size < 0x7ffff) - avio_skip(pb, atom.size - total_size); - return 0; } @@ -334,7 +365,8 @@ static int mov_read_dref(MOVContext *c, AVIOContext *pb, MOVAtom atom) avio_rb32(pb); // version + flags entries = avio_rb32(pb); if (entries >= UINT_MAX / sizeof(*sc->drefs)) - return -1; + return AVERROR_INVALIDDATA; + av_free(sc->drefs); sc->drefs = av_mallocz(entries * sizeof(*sc->drefs)); if (!sc->drefs) return AVERROR(ENOMEM); @@ -346,7 +378,7 @@ static int mov_read_dref(MOVContext *c, AVIOContext *pb, MOVAtom atom) int64_t next = avio_tell(pb) + size - 4; if (size < 12) - return -1; + return AVERROR_INVALIDDATA; dref->type = avio_rl32(pb); avio_rb32(pb); // version + flags @@ -428,7 +460,7 @@ static int mov_read_hdlr(MOVContext *c, AVIOContext *pb, MOVAtom atom) { AVStream *st; uint32_t type; - uint32_t ctype; + uint32_t av_unused ctype; if (c->fc->nb_streams < 1) // meta before first trak return 0; @@ -447,11 +479,11 @@ static int mov_read_hdlr(MOVContext *c, AVIOContext *pb, MOVAtom atom) if (type == MKTAG('v','i','d','e')) st->codec->codec_type = AVMEDIA_TYPE_VIDEO; - else if(type == MKTAG('s','o','u','n')) + else if (type == MKTAG('s','o','u','n')) st->codec->codec_type = AVMEDIA_TYPE_AUDIO; - else if(type == MKTAG('m','1','a',' ')) - st->codec->codec_id = CODEC_ID_MP2; - else if(type == MKTAG('s','u','b','p')) + else if (type == MKTAG('m','1','a',' ')) + st->codec->codec_id = AV_CODEC_ID_MP2; + else if ((type == MKTAG('s','u','b','p')) || (type == MKTAG('c','l','c','p'))) st->codec->codec_type = AVMEDIA_TYPE_SUBTITLE; avio_rb32(pb); /* component manufacture */ @@ -464,21 +496,20 @@ static int mov_read_hdlr(MOVContext *c, AVIOContext *pb, MOVAtom atom) int ff_mov_read_esds(AVFormatContext *fc, AVIOContext *pb, MOVAtom atom) { AVStream *st; - int tag, len; + int tag; if (fc->nb_streams < 1) return 0; st = fc->streams[fc->nb_streams-1]; avio_rb32(pb); /* version + flags */ - len = ff_mp4_read_descr(fc, pb, &tag); + ff_mp4_read_descr(fc, pb, &tag); if (tag == MP4ESDescrTag) { - avio_rb16(pb); /* ID */ - avio_r8(pb); /* priority */ + ff_mp4_parse_es_descr(pb, NULL); } else avio_rb16(pb); /* ID */ - len = ff_mp4_read_descr(fc, pb, &tag); + ff_mp4_read_descr(fc, pb, &tag); if (tag == MP4DecConfigDescrTag) ff_mp4_read_dec_config_descr(fc, st, pb); return 0; @@ -503,6 +534,37 @@ static int mov_read_dac3(MOVContext *c, AVIOContext *pb, MOVAtom atom) acmod = (ac3info >> 11) & 0x7; lfeon = (ac3info >> 10) & 0x1; st->codec->channels = ((int[]){2,1,2,3,3,4,4,5})[acmod] + lfeon; + st->codec->channel_layout = avpriv_ac3_channel_layout_tab[acmod]; + if (lfeon) + st->codec->channel_layout |= AV_CH_LOW_FREQUENCY; + st->codec->audio_service_type = bsmod; + if (st->codec->channels > 1 && bsmod == 0x7) + st->codec->audio_service_type = AV_AUDIO_SERVICE_TYPE_KARAOKE; + + return 0; +} + +static int mov_read_dec3(MOVContext *c, AVIOContext *pb, MOVAtom atom) +{ + AVStream *st; + int eac3info, acmod, lfeon, bsmod; + + if (c->fc->nb_streams < 1) + return 0; + st = c->fc->streams[c->fc->nb_streams-1]; + + /* No need to parse fields for additional independent substreams and its + * associated dependent substreams since libavcodec's E-AC-3 decoder + * does not support them yet. */ + avio_rb16(pb); /* data_rate and num_ind_sub */ + eac3info = avio_rb24(pb); + bsmod = (eac3info >> 12) & 0x1f; + acmod = (eac3info >> 9) & 0x7; + lfeon = (eac3info >> 8) & 0x1; + st->codec->channel_layout = avpriv_ac3_channel_layout_tab[acmod]; + if (lfeon) + st->codec->channel_layout |= AV_CH_LOW_FREQUENCY; + st->codec->channels = av_get_channel_layout_nb_channels(st->codec->channel_layout); st->codec->audio_service_type = bsmod; if (st->codec->channels > 1 && bsmod == 0x7) st->codec->audio_service_type = AV_AUDIO_SERVICE_TYPE_KARAOKE; @@ -510,6 +572,35 @@ static int mov_read_dac3(MOVContext *c, AVIOContext *pb, MOVAtom atom) return 0; } +static int mov_read_chan(MOVContext *c, AVIOContext *pb, MOVAtom atom) +{ + AVStream *st; + + if (c->fc->nb_streams < 1) + return 0; + st = c->fc->streams[c->fc->nb_streams-1]; + + if (atom.size < 16) + return 0; + + ff_mov_read_chan(c->fc, st, atom.size - 4); + + return 0; +} + +static int mov_read_wfex(MOVContext *c, AVIOContext *pb, MOVAtom atom) +{ + AVStream *st; + + if (c->fc->nb_streams < 1) + return 0; + st = c->fc->streams[c->fc->nb_streams-1]; + + ff_get_wav_header(pb, st->codec, atom.size); + + return 0; +} + static int mov_read_pasp(MOVContext *c, AVIOContext *pb, MOVAtom atom) { const int num = avio_rb32(pb); @@ -536,7 +627,7 @@ static int mov_read_pasp(MOVContext *c, AVIOContext *pb, MOVAtom atom) /* this atom contains actual media data */ static int mov_read_mdat(MOVContext *c, AVIOContext *pb, MOVAtom atom) { - if(atom.size == 0) /* wrong one (MP4) */ + if (atom.size == 0) /* wrong one (MP4) */ return 0; c->found_mdat=1; return 0; /* now go for moov */ @@ -555,20 +646,20 @@ static int mov_read_ftyp(MOVContext *c, AVIOContext *pb, MOVAtom atom) if (strcmp(type, "qt ")) c->isom = 1; av_log(c->fc, AV_LOG_DEBUG, "ISO: File Type Major Brand: %.4s\n",(char *)&type); - av_metadata_set2(&c->fc->metadata, "major_brand", type, 0); + av_dict_set(&c->fc->metadata, "major_brand", type, 0); minor_ver = avio_rb32(pb); /* minor version */ snprintf(minor_ver_str, sizeof(minor_ver_str), "%d", minor_ver); - av_metadata_set2(&c->fc->metadata, "minor_version", minor_ver_str, 0); + av_dict_set(&c->fc->metadata, "minor_version", minor_ver_str, 0); comp_brand_size = atom.size - 8; if (comp_brand_size < 0) - return -1; + return AVERROR_INVALIDDATA; comp_brands_str = av_malloc(comp_brand_size + 1); /* Add null terminator */ if (!comp_brands_str) return AVERROR(ENOMEM); avio_read(pb, comp_brands_str, comp_brand_size); comp_brands_str[comp_brand_size] = 0; - av_metadata_set2(&c->fc->metadata, "compatible_brands", comp_brands_str, 0); + av_dict_set(&c->fc->metadata, "compatible_brands", comp_brands_str, 0); av_freep(&comp_brands_str); return 0; @@ -577,8 +668,10 @@ static int mov_read_ftyp(MOVContext *c, AVIOContext *pb, MOVAtom atom) /* this atom should contain all header atoms */ static int mov_read_moov(MOVContext *c, AVIOContext *pb, MOVAtom atom) { - if (mov_read_default(c, pb, atom) < 0) - return -1; + int ret; + + if ((ret = mov_read_default(c, pb, atom)) < 0) + return ret; /* 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; @@ -592,7 +685,7 @@ static int mov_read_moof(MOVContext *c, AVIOContext *pb, MOVAtom atom) return mov_read_default(c, pb, atom); } -static void mov_metadata_creation_time(AVMetadata **metadata, time_t time) +static void mov_metadata_creation_time(AVDictionary **metadata, time_t time) { char buffer[32]; if (time) { @@ -601,7 +694,7 @@ static void mov_metadata_creation_time(AVMetadata **metadata, time_t time) ptm = gmtime(&time); if (!ptm) return; strftime(buffer, sizeof(buffer), "%Y-%m-%d %H:%M:%S", ptm); - av_metadata_set2(metadata, "creation_time", buffer, 0); + av_dict_set(metadata, "creation_time", buffer, 0); } } @@ -620,9 +713,10 @@ static int mov_read_mdhd(MOVContext *c, AVIOContext *pb, MOVAtom atom) sc = st->priv_data; version = avio_r8(pb); - if (version > 1) - return -1; /* unsupported */ - + if (version > 1) { + av_log_ask_for_sample(c, "unsupported version %d\n", version); + return AVERROR_PATCHWELCOME; + } avio_rb24(pb); /* flags */ if (version == 1) { creation_time = avio_rb64(pb); @@ -638,7 +732,7 @@ static int mov_read_mdhd(MOVContext *c, AVIOContext *pb, MOVAtom atom) lang = avio_rb16(pb); /* language */ if (ff_mov_lang_to_iso639(lang, language)) - av_metadata_set2(&st->metadata, "language", language, 0); + av_dict_set(&st->metadata, "language", language, 0); avio_rb16(pb); /* quality */ return 0; @@ -690,8 +784,8 @@ static int mov_read_smi(MOVContext *c, AVIOContext *pb, MOVAtom atom) return 0; st = c->fc->streams[c->fc->nb_streams-1]; - if((uint64_t)atom.size > (1<<30)) - return -1; + if ((uint64_t)atom.size > (1<<30)) + return AVERROR_INVALIDDATA; // currently SVQ3 decoder expect full STSD header - so let's fake it // this should be fixed and just SMI header should be passed @@ -719,17 +813,17 @@ static int mov_read_enda(MOVContext *c, AVIOContext *pb, MOVAtom atom) av_dlog(c->fc, "enda %d\n", little_endian); if (little_endian == 1) { switch (st->codec->codec_id) { - case CODEC_ID_PCM_S24BE: - st->codec->codec_id = CODEC_ID_PCM_S24LE; + case AV_CODEC_ID_PCM_S24BE: + st->codec->codec_id = AV_CODEC_ID_PCM_S24LE; break; - case CODEC_ID_PCM_S32BE: - st->codec->codec_id = CODEC_ID_PCM_S32LE; + case AV_CODEC_ID_PCM_S32BE: + st->codec->codec_id = AV_CODEC_ID_PCM_S32LE; break; - case CODEC_ID_PCM_F32BE: - st->codec->codec_id = CODEC_ID_PCM_F32LE; + case AV_CODEC_ID_PCM_F32BE: + st->codec->codec_id = AV_CODEC_ID_PCM_F32LE; break; - case CODEC_ID_PCM_F64BE: - st->codec->codec_id = CODEC_ID_PCM_F64LE; + case AV_CODEC_ID_PCM_F64BE: + st->codec->codec_id = AV_CODEC_ID_PCM_F64LE; break; default: break; @@ -738,6 +832,40 @@ static int mov_read_enda(MOVContext *c, AVIOContext *pb, MOVAtom atom) return 0; } +static int mov_read_fiel(MOVContext *c, AVIOContext *pb, MOVAtom atom) +{ + AVStream *st; + unsigned mov_field_order; + enum AVFieldOrder decoded_field_order = AV_FIELD_UNKNOWN; + + if (c->fc->nb_streams < 1) // will happen with jp2 files + return 0; + st = c->fc->streams[c->fc->nb_streams-1]; + if (atom.size < 2) + return AVERROR_INVALIDDATA; + mov_field_order = avio_rb16(pb); + if ((mov_field_order & 0xFF00) == 0x0100) + decoded_field_order = AV_FIELD_PROGRESSIVE; + else if ((mov_field_order & 0xFF00) == 0x0200) { + switch (mov_field_order & 0xFF) { + case 0x01: decoded_field_order = AV_FIELD_TT; + break; + case 0x06: decoded_field_order = AV_FIELD_BB; + break; + case 0x09: decoded_field_order = AV_FIELD_TB; + break; + case 0x0E: decoded_field_order = AV_FIELD_BT; + break; + } + } + if (decoded_field_order == AV_FIELD_UNKNOWN && mov_field_order) { + av_log(NULL, AV_LOG_ERROR, "Unknown MOV field order 0x%04x\n", mov_field_order); + } + st->codec->field_order = decoded_field_order; + + return 0; +} + /* FIXME modify qdm2/svq3/h264 decoders to take full atom as extradata */ static int mov_read_extradata(MOVContext *c, AVIOContext *pb, MOVAtom atom) { @@ -749,11 +877,11 @@ static int mov_read_extradata(MOVContext *c, AVIOContext *pb, MOVAtom atom) return 0; st= c->fc->streams[c->fc->nb_streams-1]; size= (uint64_t)st->codec->extradata_size + atom.size + 8 + FF_INPUT_BUFFER_PADDING_SIZE; - if(size > INT_MAX || (uint64_t)atom.size > INT_MAX) - return -1; + if (size > INT_MAX || (uint64_t)atom.size > INT_MAX) + return AVERROR_INVALIDDATA; buf= av_realloc(st->codec->extradata, size); - if(!buf) - return -1; + if (!buf) + return AVERROR(ENOMEM); st->codec->extradata= buf; buf+= st->codec->extradata_size; st->codec->extradata_size= size - FF_INPUT_BUFFER_PADDING_SIZE; @@ -771,10 +899,10 @@ static int mov_read_wave(MOVContext *c, AVIOContext *pb, MOVAtom atom) return 0; st = c->fc->streams[c->fc->nb_streams-1]; - if((uint64_t)atom.size > (1<<30)) - return -1; + if ((uint64_t)atom.size > (1<<30)) + return AVERROR_INVALIDDATA; - if (st->codec->codec_id == CODEC_ID_QDM2 || st->codec->codec_id == CODEC_ID_QDMC) { + if (st->codec->codec_id == AV_CODEC_ID_QDM2 || st->codec->codec_id == AV_CODEC_ID_QDMC) { // pass all frma atom to codec, needed at least for QDMC and QDM2 av_free(st->codec->extradata); st->codec->extradata = av_mallocz(atom.size + FF_INPUT_BUFFER_PADDING_SIZE); @@ -783,8 +911,9 @@ static int mov_read_wave(MOVContext *c, AVIOContext *pb, MOVAtom atom) st->codec->extradata_size = atom.size; avio_read(pb, st->codec->extradata, atom.size); } else if (atom.size > 8) { /* to read frma, esds atoms */ - if (mov_read_default(c, pb, atom) < 0) - return -1; + int ret; + if ((ret = mov_read_default(c, pb, atom)) < 0) + return ret; } else avio_skip(pb, atom.size); return 0; @@ -802,9 +931,18 @@ static int mov_read_glbl(MOVContext *c, AVIOContext *pb, MOVAtom atom) return 0; st = c->fc->streams[c->fc->nb_streams-1]; - if((uint64_t)atom.size > (1<<30)) - return -1; + if ((uint64_t)atom.size > (1<<30)) + return AVERROR_INVALIDDATA; + if (atom.size >= 10) { + // Broken files created by legacy versions of libavformat will + // wrap a whole fiel atom inside of a glbl atom. + unsigned size = avio_rb32(pb); + unsigned type = avio_rl32(pb); + avio_seek(pb, -8, SEEK_CUR); + if (type == MKTAG('f','i','e','l') && size == atom.size) + return mov_read_default(c, pb, atom); + } av_free(st->codec->extradata); st->codec->extradata = av_mallocz(atom.size + FF_INPUT_BUFFER_PADDING_SIZE); if (!st->codec->extradata) @@ -814,6 +952,32 @@ static int mov_read_glbl(MOVContext *c, AVIOContext *pb, MOVAtom atom) return 0; } +static int mov_read_dvc1(MOVContext *c, AVIOContext *pb, MOVAtom atom) +{ + AVStream *st; + uint8_t profile_level; + + if (c->fc->nb_streams < 1) + return 0; + st = c->fc->streams[c->fc->nb_streams-1]; + + if (atom.size >= (1<<28) || atom.size < 7) + return AVERROR_INVALIDDATA; + + profile_level = avio_r8(pb); + if ((profile_level & 0xf0) != 0xc0) + return 0; + + av_free(st->codec->extradata); + st->codec->extradata = av_mallocz(atom.size - 7 + FF_INPUT_BUFFER_PADDING_SIZE); + if (!st->codec->extradata) + return AVERROR(ENOMEM); + st->codec->extradata_size = atom.size - 7; + avio_seek(pb, 6, SEEK_CUR); + avio_read(pb, st->codec->extradata, st->codec->extradata_size); + return 0; +} + /** * An strf atom is a BITMAPINFOHEADER struct. This struct is 40 bytes itself, * but can have extradata appended at the end after the 40 bytes belonging @@ -829,8 +993,8 @@ static int mov_read_strf(MOVContext *c, AVIOContext *pb, MOVAtom atom) return 0; st = c->fc->streams[c->fc->nb_streams-1]; - if((uint64_t)atom.size > (1<<30)) - return -1; + if ((uint64_t)atom.size > (1<<30)) + return AVERROR_INVALIDDATA; av_free(st->codec->extradata); st->codec->extradata = av_mallocz(atom.size - 40 + FF_INPUT_BUFFER_PADDING_SIZE); @@ -858,8 +1022,10 @@ static int mov_read_stco(MOVContext *c, AVIOContext *pb, MOVAtom atom) entries = avio_rb32(pb); - if(entries >= UINT_MAX/sizeof(int64_t)) - return -1; + if (!entries) + return 0; + if (entries >= UINT_MAX/sizeof(int64_t)) + return AVERROR_INVALIDDATA; sc->chunk_offsets = av_malloc(entries * sizeof(int64_t)); if (!sc->chunk_offsets) @@ -867,13 +1033,13 @@ static int mov_read_stco(MOVContext *c, AVIOContext *pb, MOVAtom atom) sc->chunk_count = entries; if (atom.type == MKTAG('s','t','c','o')) - for(i=0; ichunk_offsets[i] = avio_rb32(pb); else if (atom.type == MKTAG('c','o','6','4')) - for(i=0; ichunk_offsets[i] = avio_rb64(pb); else - return -1; + return AVERROR_INVALIDDATA; return 0; } @@ -882,35 +1048,35 @@ static int mov_read_stco(MOVContext *c, AVIOContext *pb, MOVAtom atom) * Compute codec id for 'lpcm' tag. * See CoreAudioTypes and AudioStreamBasicDescription at Apple. */ -enum CodecID ff_mov_get_lpcm_codec_id(int bps, int flags) +enum AVCodecID ff_mov_get_lpcm_codec_id(int bps, int flags) { if (flags & 1) { // floating point if (flags & 2) { // big endian - if (bps == 32) return CODEC_ID_PCM_F32BE; - else if (bps == 64) return CODEC_ID_PCM_F64BE; + if (bps == 32) return AV_CODEC_ID_PCM_F32BE; + else if (bps == 64) return AV_CODEC_ID_PCM_F64BE; } else { - if (bps == 32) return CODEC_ID_PCM_F32LE; - else if (bps == 64) return CODEC_ID_PCM_F64LE; + if (bps == 32) return AV_CODEC_ID_PCM_F32LE; + else if (bps == 64) return AV_CODEC_ID_PCM_F64LE; } } else { if (flags & 2) { if (bps == 8) // signed integer - if (flags & 4) return CODEC_ID_PCM_S8; - else return CODEC_ID_PCM_U8; - else if (bps == 16) return CODEC_ID_PCM_S16BE; - else if (bps == 24) return CODEC_ID_PCM_S24BE; - else if (bps == 32) return CODEC_ID_PCM_S32BE; + if (flags & 4) return AV_CODEC_ID_PCM_S8; + else return AV_CODEC_ID_PCM_U8; + else if (bps == 16) return AV_CODEC_ID_PCM_S16BE; + else if (bps == 24) return AV_CODEC_ID_PCM_S24BE; + else if (bps == 32) return AV_CODEC_ID_PCM_S32BE; } else { if (bps == 8) - if (flags & 4) return CODEC_ID_PCM_S8; - else return CODEC_ID_PCM_U8; - else if (bps == 16) return CODEC_ID_PCM_S16LE; - else if (bps == 24) return CODEC_ID_PCM_S24LE; - else if (bps == 32) return CODEC_ID_PCM_S32LE; + if (flags & 4) return AV_CODEC_ID_PCM_S8; + else return AV_CODEC_ID_PCM_U8; + else if (bps == 16) return AV_CODEC_ID_PCM_S16LE; + else if (bps == 24) return AV_CODEC_ID_PCM_S24LE; + else if (bps == 32) return AV_CODEC_ID_PCM_S32LE; } } - return CODEC_ID_NONE; + return AV_CODEC_ID_NONE; } int ff_mov_read_stsd_entries(MOVContext *c, AVIOContext *pb, int entries) @@ -924,9 +1090,9 @@ int ff_mov_read_stsd_entries(MOVContext *c, AVIOContext *pb, int entries) st = c->fc->streams[c->fc->nb_streams-1]; sc = st->priv_data; - for(pseudo_stream_id=0; pseudo_stream_idcodec->codec_tag && st->codec->codec_tag != format && - (c->fc->video_codec_id ? ff_codec_get_id(codec_movvideo_tags, format) != c->fc->video_codec_id + (c->fc->video_codec_id ? ff_codec_get_id(ff_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 @@ -959,7 +1125,7 @@ int ff_mov_read_stsd_entries(MOVContext *c, AVIOContext *pb, int entries) sc->dref_id= dref_id; st->codec->codec_tag = format; - id = ff_codec_get_id(codec_movaudio_tags, format); + id = ff_codec_get_id(ff_codec_movaudio_tags, format); if (id<=0 && ((format&0xFFFF) == 'm'+('s'<<8) || (format&0xFFFF) == 'T'+('S'<<8))) id = ff_codec_get_id(ff_codec_wav_tags, av_bswap32(format)&0xFFFF); @@ -967,14 +1133,14 @@ int ff_mov_read_stsd_entries(MOVContext *c, AVIOContext *pb, int entries) st->codec->codec_type = AVMEDIA_TYPE_AUDIO; } else if (st->codec->codec_type != AVMEDIA_TYPE_AUDIO && /* do not overwrite codec type */ format && format != MKTAG('m','p','4','s')) { /* skip old asf mpeg4 tag */ - id = ff_codec_get_id(codec_movvideo_tags, format); + id = ff_codec_get_id(ff_codec_movvideo_tags, format); if (id <= 0) id = ff_codec_get_id(ff_codec_bmp_tags, format); if (id > 0) st->codec->codec_type = AVMEDIA_TYPE_VIDEO; - else if(st->codec->codec_type == AVMEDIA_TYPE_DATA){ + else if (st->codec->codec_type == AVMEDIA_TYPE_DATA){ id = ff_codec_get_id(ff_codec_movsubtitle_tags, format); - if(id > 0) + if (id > 0) st->codec->codec_type = AVMEDIA_TYPE_SUBTITLE; } } @@ -983,9 +1149,10 @@ int ff_mov_read_stsd_entries(MOVContext *c, AVIOContext *pb, int entries) (format >> 0) & 0xff, (format >> 8) & 0xff, (format >> 16) & 0xff, (format >> 24) & 0xff, st->codec->codec_type); - if(st->codec->codec_type==AVMEDIA_TYPE_VIDEO) { + if (st->codec->codec_type==AVMEDIA_TYPE_VIDEO) { unsigned int color_depth, len; int color_greyscale; + int color_table_id; st->codec->codec_id = id; avio_rb16(pb); /* version */ @@ -1013,9 +1180,9 @@ int ff_mov_read_stsd_entries(MOVContext *c, AVIOContext *pb, int entries) st->codec->codec_tag=MKTAG('I', '4', '2', '0'); st->codec->bits_per_coded_sample = avio_rb16(pb); /* depth */ - st->codec->color_table_id = avio_rb16(pb); /* colortable id */ + color_table_id = avio_rb16(pb); /* colortable id */ av_dlog(c->fc, "depth %d, ctab id %d\n", - st->codec->bits_per_coded_sample, st->codec->color_table_id); + st->codec->bits_per_coded_sample, color_table_id); /* figure out the palette situation */ color_depth = st->codec->bits_per_coded_sample & 0x1F; color_greyscale = st->codec->bits_per_coded_sample & 0x20; @@ -1042,7 +1209,7 @@ int ff_mov_read_stsd_entries(MOVContext *c, AVIOContext *pb, int entries) if (color_index < 0) color_index = 0; } - } else if (st->codec->color_table_id) { + } else if (color_table_id) { const uint8_t *color_table; /* if flag bit 3 is set, use the default palette */ color_count = 1 << color_depth; @@ -1086,7 +1253,7 @@ int ff_mov_read_stsd_entries(MOVContext *c, AVIOContext *pb, int entries) } sc->has_palette = 1; } - } else if(st->codec->codec_type==AVMEDIA_TYPE_AUDIO) { + } else if (st->codec->codec_type==AVMEDIA_TYPE_AUDIO) { int bits_per_sample, flags; uint16_t version = avio_rb16(pb); @@ -1105,15 +1272,15 @@ int ff_mov_read_stsd_entries(MOVContext *c, AVIOContext *pb, int entries) //Read QT version 1 fields. In version 0 these do not exist. av_dlog(c->fc, "version =%d, isom =%d\n",version,c->isom); - if(!c->isom) { - if(version==1) { + if (!c->isom) { + if (version==1) { sc->samples_per_frame = avio_rb32(pb); avio_rb32(pb); /* bytes per packet */ sc->bytes_per_frame = avio_rb32(pb); avio_rb32(pb); /* bytes per sample */ - } else if(version==2) { + } else if (version==2) { avio_rb32(pb); /* sizeof struct only */ - st->codec->sample_rate = av_int2dbl(avio_rb64(pb)); /* float 64 */ + st->codec->sample_rate = av_int2double(avio_rb64(pb)); /* float 64 */ st->codec->channels = avio_rb32(pb); avio_rb32(pb); /* always 0x7F000000 */ st->codec->bits_per_coded_sample = avio_rb32(pb); /* bits per channel if sound is uncompressed */ @@ -1126,34 +1293,34 @@ int ff_mov_read_stsd_entries(MOVContext *c, AVIOContext *pb, int entries) } switch (st->codec->codec_id) { - case CODEC_ID_PCM_S8: - case CODEC_ID_PCM_U8: + case AV_CODEC_ID_PCM_S8: + case AV_CODEC_ID_PCM_U8: if (st->codec->bits_per_coded_sample == 16) - st->codec->codec_id = CODEC_ID_PCM_S16BE; + st->codec->codec_id = AV_CODEC_ID_PCM_S16BE; break; - case CODEC_ID_PCM_S16LE: - case CODEC_ID_PCM_S16BE: + case AV_CODEC_ID_PCM_S16LE: + case AV_CODEC_ID_PCM_S16BE: if (st->codec->bits_per_coded_sample == 8) - st->codec->codec_id = CODEC_ID_PCM_S8; + st->codec->codec_id = AV_CODEC_ID_PCM_S8; else if (st->codec->bits_per_coded_sample == 24) st->codec->codec_id = - st->codec->codec_id == CODEC_ID_PCM_S16BE ? - CODEC_ID_PCM_S24BE : CODEC_ID_PCM_S24LE; + st->codec->codec_id == AV_CODEC_ID_PCM_S16BE ? + AV_CODEC_ID_PCM_S24BE : AV_CODEC_ID_PCM_S24LE; break; /* set values for old format before stsd version 1 appeared */ - case CODEC_ID_MACE3: + case AV_CODEC_ID_MACE3: sc->samples_per_frame = 6; sc->bytes_per_frame = 2*st->codec->channels; break; - case CODEC_ID_MACE6: + case AV_CODEC_ID_MACE6: sc->samples_per_frame = 6; sc->bytes_per_frame = 1*st->codec->channels; break; - case CODEC_ID_ADPCM_IMA_QT: + case AV_CODEC_ID_ADPCM_IMA_QT: sc->samples_per_frame = 64; sc->bytes_per_frame = 34*st->codec->channels; break; - case CODEC_ID_GSM: + case AV_CODEC_ID_GSM: sc->samples_per_frame = 160; sc->bytes_per_frame = 33; break; @@ -1166,7 +1333,7 @@ int ff_mov_read_stsd_entries(MOVContext *c, AVIOContext *pb, int entries) st->codec->bits_per_coded_sample = bits_per_sample; sc->sample_size = (bits_per_sample >> 3) * st->codec->channels; } - } else if(st->codec->codec_type==AVMEDIA_TYPE_SUBTITLE){ + } else if (st->codec->codec_type==AVMEDIA_TYPE_SUBTITLE){ // ttxt stsd contains display flags, justification, background // color, fonts, and default styles, so fake an atom to read it MOVAtom fake_atom = { .size = size - (avio_tell(pb) - start_pos) }; @@ -1182,65 +1349,66 @@ int ff_mov_read_stsd_entries(MOVContext *c, AVIOContext *pb, int entries) /* this will read extra atoms at the end (wave, alac, damr, avcC, SMI ...) */ a.size = size - (avio_tell(pb) - start_pos); if (a.size > 8) { - if (mov_read_default(c, pb, a) < 0) - return -1; + int ret; + if ((ret = mov_read_default(c, pb, a)) < 0) + return ret; } else if (a.size > 0) avio_skip(pb, a.size); } - if(st->codec->codec_type==AVMEDIA_TYPE_AUDIO && st->codec->sample_rate==0 && sc->time_scale>1) + if (st->codec->codec_type==AVMEDIA_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) { #if CONFIG_DV_DEMUXER - case CODEC_ID_DVAUDIO: + case AV_CODEC_ID_DVAUDIO: c->dv_fctx = avformat_alloc_context(); - c->dv_demux = dv_init_demux(c->dv_fctx); + c->dv_demux = avpriv_dv_init_demux(c->dv_fctx); if (!c->dv_demux) { av_log(c->fc, AV_LOG_ERROR, "dv demux context init error\n"); - return -1; + return AVERROR(ENOMEM); } sc->dv_audio_container = 1; - st->codec->codec_id = CODEC_ID_PCM_S16LE; + st->codec->codec_id = AV_CODEC_ID_PCM_S16LE; break; #endif /* no ifdef since parameters are always those */ - case CODEC_ID_QCELP: + case AV_CODEC_ID_QCELP: // force sample rate for qcelp when not stored in mov if (st->codec->codec_tag != MKTAG('Q','c','l','p')) st->codec->sample_rate = 8000; - st->codec->frame_size= 160; st->codec->channels= 1; /* really needed */ break; - case CODEC_ID_AMR_NB: - case CODEC_ID_AMR_WB: - st->codec->frame_size= sc->samples_per_frame; + case AV_CODEC_ID_AMR_NB: st->codec->channels= 1; /* really needed */ /* force sample rate for amr, stsd in 3gp does not store sample rate */ - if (st->codec->codec_id == CODEC_ID_AMR_NB) - st->codec->sample_rate = 8000; - else if (st->codec->codec_id == CODEC_ID_AMR_WB) - st->codec->sample_rate = 16000; + st->codec->sample_rate = 8000; + break; + case AV_CODEC_ID_AMR_WB: + st->codec->channels = 1; + st->codec->sample_rate = 16000; break; - case CODEC_ID_MP2: - case CODEC_ID_MP3: + case AV_CODEC_ID_MP2: + case AV_CODEC_ID_MP3: st->codec->codec_type = AVMEDIA_TYPE_AUDIO; /* force type after stsd for m1a hdlr */ st->need_parsing = AVSTREAM_PARSE_FULL; break; - case CODEC_ID_GSM: - case CODEC_ID_ADPCM_MS: - case CODEC_ID_ADPCM_IMA_WAV: - st->codec->frame_size = sc->samples_per_frame; + case AV_CODEC_ID_GSM: + case AV_CODEC_ID_ADPCM_MS: + case AV_CODEC_ID_ADPCM_IMA_WAV: + case AV_CODEC_ID_ILBC: st->codec->block_align = sc->bytes_per_frame; break; - case CODEC_ID_ALAC: + case AV_CODEC_ID_ALAC: if (st->codec->extradata_size == 36) { - st->codec->frame_size = AV_RB32(st->codec->extradata+12); st->codec->channels = AV_RB8 (st->codec->extradata+21); st->codec->sample_rate = AV_RB32(st->codec->extradata+32); } break; + case AV_CODEC_ID_VC1: + st->need_parsing = AVSTREAM_PARSE_FULL; + break; default: break; } @@ -1277,14 +1445,16 @@ static int mov_read_stsc(MOVContext *c, AVIOContext *pb, MOVAtom atom) av_dlog(c->fc, "track[%i].stsc.entries = %i\n", c->fc->nb_streams-1, entries); - if(entries >= UINT_MAX / sizeof(*sc->stsc_data)) - return -1; + if (!entries) + return 0; + if (entries >= UINT_MAX / sizeof(*sc->stsc_data)) + return AVERROR_INVALIDDATA; sc->stsc_data = av_malloc(entries * sizeof(*sc->stsc_data)); if (!sc->stsc_data) return AVERROR(ENOMEM); sc->stsc_count = entries; - for(i=0; istsc_data[i].first = avio_rb32(pb); sc->stsc_data[i].count = avio_rb32(pb); sc->stsc_data[i].id = avio_rb32(pb); @@ -1307,7 +1477,7 @@ static int mov_read_stps(MOVContext *c, AVIOContext *pb, MOVAtom atom) entries = avio_rb32(pb); if (entries >= UINT_MAX / sizeof(*sc->stps_data)) - return -1; + return AVERROR_INVALIDDATA; sc->stps_data = av_malloc(entries * sizeof(*sc->stps_data)); if (!sc->stps_data) return AVERROR(ENOMEM); @@ -1339,14 +1509,19 @@ static int mov_read_stss(MOVContext *c, AVIOContext *pb, MOVAtom atom) av_dlog(c->fc, "keyframe_count = %d\n", entries); - if(entries >= UINT_MAX / sizeof(int)) - return -1; + if (!entries) + { + sc->keyframe_absent = 1; + return 0; + } + if (entries >= UINT_MAX / sizeof(int)) + return AVERROR_INVALIDDATA; sc->keyframes = av_malloc(entries * sizeof(int)); if (!sc->keyframes) return AVERROR(ENOMEM); sc->keyframe_count = entries; - for(i=0; ikeyframes[i] = avio_rb32(pb); //av_dlog(c->fc, "keyframes[]=%d\n", sc->keyframes[i]); } @@ -1389,11 +1564,13 @@ static int mov_read_stsz(MOVContext *c, AVIOContext *pb, MOVAtom atom) if (field_size != 4 && field_size != 8 && field_size != 16 && field_size != 32) { av_log(c->fc, AV_LOG_ERROR, "Invalid sample field size %d\n", field_size); - return -1; + return AVERROR_INVALIDDATA; } + if (!entries) + return 0; if (entries >= UINT_MAX / sizeof(int) || entries >= (UINT_MAX - 4) / field_size) - return -1; + return AVERROR_INVALIDDATA; sc->sample_sizes = av_malloc(entries * sizeof(int)); if (!sc->sample_sizes) return AVERROR(ENOMEM); @@ -1409,13 +1586,15 @@ static int mov_read_stsz(MOVContext *c, AVIOContext *pb, MOVAtom atom) if (avio_read(pb, buf, num_bytes) < num_bytes) { av_freep(&sc->sample_sizes); av_free(buf); - return -1; + return AVERROR_INVALIDDATA; } init_get_bits(&gb, buf, 8*num_bytes); - for(i=0; isample_sizes[i] = get_bits_long(&gb, field_size); + sc->data_size += sc->sample_sizes[i]; + } av_free(buf); return 0; @@ -1438,16 +1617,21 @@ static int mov_read_stts(MOVContext *c, AVIOContext *pb, MOVAtom atom) avio_rb24(pb); /* flags */ entries = avio_rb32(pb); - av_dlog(c->fc, "track[%i].stts.entries = %i\n", c->fc->nb_streams-1, entries); + av_dlog(c->fc, "track[%i].stts.entries = %i\n", + c->fc->nb_streams-1, entries); + + if (!entries) + return 0; + if (entries >= UINT_MAX / sizeof(*sc->stts_data)) + return AVERROR(EINVAL); - if(entries >= UINT_MAX / sizeof(*sc->stts_data)) - return -1; sc->stts_data = av_malloc(entries * sizeof(*sc->stts_data)); if (!sc->stts_data) return AVERROR(ENOMEM); + sc->stts_count = entries; - for(i=0; istts_data[i].count= sample_count; sc->stts_data[i].duration= sample_duration; - av_dlog(c->fc, "sample_count=%d, sample_duration=%d\n",sample_count,sample_duration); + av_dlog(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; } st->nb_frames= total_sample_count; - if(duration) + if (duration) st->duration= duration; + sc->track_end = duration; return 0; } @@ -1485,14 +1671,16 @@ static int mov_read_ctts(MOVContext *c, AVIOContext *pb, MOVAtom atom) av_dlog(c->fc, "track[%i].ctts.entries = %i\n", c->fc->nb_streams-1, entries); - if(entries >= UINT_MAX / sizeof(*sc->ctts_data)) - return -1; + if (!entries) + return 0; + if (entries >= UINT_MAX / sizeof(*sc->ctts_data)) + return AVERROR_INVALIDDATA; sc->ctts_data = av_malloc(entries * sizeof(*sc->ctts_data)); if (!sc->ctts_data) return AVERROR(ENOMEM); sc->ctts_count = entries; - for(i=0; itime_offset && mov->time_scale > 0) { - int rescaled = sc->time_offset < 0 ? av_rescale(sc->time_offset, sc->time_scale, mov->time_scale) : sc->time_offset; - current_dts = -rescaled; - if (sc->ctts_data && sc->stts_data && + if (sc->time_offset < 0) + sc->time_offset = av_rescale(sc->time_offset, sc->time_scale, mov->time_scale); + current_dts = -sc->time_offset; + if (sc->ctts_data && sc->stts_data && sc->stts_data[0].duration && sc->ctts_data[0].duration / sc->stts_data[0].duration > 16) { /* more than 16 frames delay, dts are likely wrong this happens with files created by iMovie */ @@ -1539,16 +1729,19 @@ static void mov_build_index(MOVContext *mov, AVStream *st) unsigned int stts_sample = 0; unsigned int sample_size; unsigned int distance = 0; - int key_off = sc->keyframes && sc->keyframes[0] == 1; + int key_off = (sc->keyframes && sc->keyframes[0] > 0) || (sc->stps_data && sc->stps_data[0] > 0); current_dts -= sc->dts_shift; - if (sc->sample_count >= UINT_MAX / sizeof(*st->index_entries)) + if (!sc->sample_count) return; - st->index_entries = av_malloc(sc->sample_count*sizeof(*st->index_entries)); - if (!st->index_entries) + if (sc->sample_count >= UINT_MAX / sizeof(*st->index_entries) - st->nb_index_entries) return; - st->index_entries_allocated_size = sc->sample_count*sizeof(*st->index_entries); + mem = av_realloc(st->index_entries, (st->nb_index_entries + sc->sample_count) * sizeof(*st->index_entries)); + if (!mem) + return; + st->index_entries = mem; + st->index_entries_allocated_size = (st->nb_index_entries + sc->sample_count) * sizeof(*st->index_entries); for (i = 0; i < sc->chunk_count; i++) { current_offset = sc->chunk_offsets[i]; @@ -1562,7 +1755,7 @@ static void mov_build_index(MOVContext *mov, AVStream *st) return; } - if (!sc->keyframe_count || current_sample+key_off == sc->keyframes[stss_index]) { + if (!sc->keyframe_absent && (!sc->keyframe_count || current_sample+key_off == sc->keyframes[stss_index])) { keyframe = 1; if (stss_index + 1 < sc->keyframe_count) stss_index++; @@ -1574,7 +1767,7 @@ static void mov_build_index(MOVContext *mov, AVStream *st) if (keyframe) distance = 0; sample_size = sc->sample_size > 0 ? sc->sample_size : sc->sample_sizes[current_sample]; - if(sc->pseudo_stream_id == -1 || + if (sc->pseudo_stream_id == -1 || sc->stsc_data[stsc_index].id - 1 == sc->pseudo_stream_id) { AVIndexEntry *e = &st->index_entries[st->nb_index_entries++]; e->pos = current_offset; @@ -1609,7 +1802,8 @@ static void mov_build_index(MOVContext *mov, AVStream *st) unsigned count, chunk_count; chunk_samples = sc->stsc_data[i].count; - if (sc->samples_per_frame && chunk_samples % sc->samples_per_frame) { + if (i != sc->stsc_count - 1 && + sc->samples_per_frame && chunk_samples % sc->samples_per_frame) { av_log(mov->fc, AV_LOG_ERROR, "error unaligned chunk\n"); return; } @@ -1631,12 +1825,13 @@ static void mov_build_index(MOVContext *mov, AVStream *st) } av_dlog(mov->fc, "chunk count %d\n", total); - if (total >= UINT_MAX / sizeof(*st->index_entries)) + if (total >= UINT_MAX / sizeof(*st->index_entries) - st->nb_index_entries) return; - st->index_entries = av_malloc(total*sizeof(*st->index_entries)); - if (!st->index_entries) + mem = av_realloc(st->index_entries, (st->nb_index_entries + total) * sizeof(*st->index_entries)); + if (!mem) return; - st->index_entries_allocated_size = total*sizeof(*st->index_entries); + st->index_entries = mem; + st->index_entries_allocated_size = (st->nb_index_entries + total) * sizeof(*st->index_entries); // populate index for (i = 0; i < sc->chunk_count; i++) { @@ -1686,7 +1881,8 @@ static void mov_build_index(MOVContext *mov, AVStream *st) } } -static int mov_open_dref(AVIOContext **pb, char *src, MOVDref *ref) +static int mov_open_dref(AVIOContext **pb, char *src, MOVDref *ref, + AVIOInterruptCB *int_cb) { /* try relative path, we do not try the absolute because it can leak information about our system to an attacker */ @@ -1721,7 +1917,7 @@ static int mov_open_dref(AVIOContext **pb, char *src, MOVDref *ref) av_strlcat(filename, ref->path + l + 1, 1024); - if (!avio_open(pb, filename, AVIO_FLAG_READ)) + if (!avio_open2(pb, filename, AVIO_FLAG_READ, int_cb, NULL)) return 0; } } @@ -1735,8 +1931,9 @@ static int mov_read_trak(MOVContext *c, AVIOContext *pb, MOVAtom atom) MOVStreamContext *sc; int ret; - st = av_new_stream(c->fc, c->fc->nb_streams); + st = avformat_new_stream(c->fc, NULL); if (!st) return AVERROR(ENOMEM); + st->id = c->fc->nb_streams; sc = av_mallocz(sizeof(MOVStreamContext)); if (!sc) return AVERROR(ENOMEM); @@ -1762,20 +1959,13 @@ static int mov_read_trak(MOVContext *c, AVIOContext *pb, MOVAtom atom) sc->time_scale = 1; } - av_set_pts_info(st, 64, 1, sc->time_scale); - - if (st->codec->codec_type == AVMEDIA_TYPE_AUDIO && - !st->codec->frame_size && sc->stts_count == 1) { - st->codec->frame_size = av_rescale(sc->stts_data[0].duration, - st->codec->sample_rate, sc->time_scale); - av_dlog(c->fc, "frame size %d\n", st->codec->frame_size); - } + avpriv_set_pts_info(st, 64, 1, sc->time_scale); mov_build_index(c, st); if (sc->dref_id-1 < sc->drefs_count && sc->drefs[sc->dref_id-1].path) { MOVDref *dref = &sc->drefs[sc->dref_id - 1]; - if (mov_open_dref(&sc->pb, c->fc->filename, dref) < 0) + if (mov_open_dref(&sc->pb, c->fc->filename, dref, &c->fc->interrupt_callback) < 0) av_log(c->fc, AV_LOG_ERROR, "stream %d, error opening alias: path='%s', dir='%s', " "filename='%s', volume='%s', nlvl_from=%d, nlvl_to=%d\n", @@ -1794,23 +1984,22 @@ static int mov_read_trak(MOVContext *c, AVIOContext *pb, MOVAtom atom) av_reduce(&st->avg_frame_rate.num, &st->avg_frame_rate.den, sc->time_scale*st->nb_frames, st->duration, INT_MAX); +#if FF_API_R_FRAME_RATE if (sc->stts_count == 1 || (sc->stts_count == 2 && sc->stts_data[1].count == 1)) av_reduce(&st->r_frame_rate.num, &st->r_frame_rate.den, sc->time_scale, sc->stts_data[0].duration, INT_MAX); +#endif } switch (st->codec->codec_id) { #if CONFIG_H261_DECODER - case CODEC_ID_H261: + case AV_CODEC_ID_H261: #endif #if CONFIG_H263_DECODER - case CODEC_ID_H263: -#endif -#if CONFIG_H264_DECODER - case CODEC_ID_H264: + case AV_CODEC_ID_H263: #endif #if CONFIG_MPEG4_DECODER - case CODEC_ID_MPEG4: + case AV_CODEC_ID_MPEG4: #endif st->codec->width = 0; /* let decoder init width/height */ st->codec->height= 0; @@ -1945,7 +2134,7 @@ static int mov_read_tfhd(MOVContext *c, AVIOContext *pb, MOVAtom atom) track_id = avio_rb32(pb); if (!track_id) - return -1; + return AVERROR_INVALIDDATA; frag->track_id = track_id; for (i = 0; i < c->trex_count; i++) if (c->trex_data[i].track_id == frag->track_id) { @@ -1954,17 +2143,19 @@ static int mov_read_tfhd(MOVContext *c, AVIOContext *pb, MOVAtom atom) } if (!trex) { av_log(c->fc, AV_LOG_ERROR, "could not find corresponding trex\n"); - return -1; + return AVERROR_INVALIDDATA; } - if (flags & 0x01) frag->base_data_offset = avio_rb64(pb); - else frag->base_data_offset = frag->moof_offset; - if (flags & 0x02) frag->stsd_id = avio_rb32(pb); - else frag->stsd_id = trex->stsd_id; + frag->base_data_offset = flags & MOV_TFHD_BASE_DATA_OFFSET ? + avio_rb64(pb) : frag->moof_offset; + frag->stsd_id = flags & MOV_TFHD_STSD_ID ? avio_rb32(pb) : trex->stsd_id; - frag->duration = flags & 0x08 ? avio_rb32(pb) : trex->duration; - frag->size = flags & 0x10 ? avio_rb32(pb) : trex->size; - frag->flags = flags & 0x20 ? avio_rb32(pb) : trex->flags; + frag->duration = flags & MOV_TFHD_DEFAULT_DURATION ? + avio_rb32(pb) : trex->duration; + frag->size = flags & MOV_TFHD_DEFAULT_SIZE ? + avio_rb32(pb) : trex->size; + frag->flags = flags & MOV_TFHD_DEFAULT_FLAGS ? + avio_rb32(pb) : trex->flags; av_dlog(c->fc, "frag flags 0x%x\n", frag->flags); return 0; } @@ -1980,7 +2171,7 @@ static int mov_read_trex(MOVContext *c, AVIOContext *pb, MOVAtom atom) MOVTrackExt *trex; if ((uint64_t)c->trex_count+1 >= UINT_MAX / sizeof(*c->trex_data)) - return -1; + return AVERROR_INVALIDDATA; trex = av_realloc(c->trex_data, (c->trex_count+1)*sizeof(*c->trex_data)); if (!trex) return AVERROR(ENOMEM); @@ -2006,7 +2197,7 @@ static int mov_read_trun(MOVContext *c, AVIOContext *pb, MOVAtom atom) int64_t dts; int data_offset = 0; unsigned entries, first_sample_flags = frag->flags; - int flags, distance, i; + int flags, distance, i, found_keyframe = 0; for (i = 0; i < c->fc->nb_streams; i++) { if (c->fc->streams[i]->id == frag->track_id) { @@ -2016,7 +2207,7 @@ static int mov_read_trun(MOVContext *c, AVIOContext *pb, MOVAtom atom) } if (!st) { av_log(c->fc, AV_LOG_ERROR, "could not find corresponding track id %d\n", frag->track_id); - return -1; + return AVERROR_INVALIDDATA; } sc = st->priv_data; if (sc->pseudo_stream_id+1 != frag->stsd_id) @@ -2043,16 +2234,16 @@ static int mov_read_trun(MOVContext *c, AVIOContext *pb, MOVAtom atom) sc->ctts_count++; } if ((uint64_t)entries+sc->ctts_count >= UINT_MAX/sizeof(*sc->ctts_data)) - return -1; + return AVERROR_INVALIDDATA; ctts_data = av_realloc(sc->ctts_data, (entries+sc->ctts_count)*sizeof(*sc->ctts_data)); if (!ctts_data) return AVERROR(ENOMEM); sc->ctts_data = ctts_data; - if (flags & 0x001) data_offset = avio_rb32(pb); - if (flags & 0x004) first_sample_flags = avio_rb32(pb); - dts = st->duration; + if (flags & MOV_TRUN_DATA_OFFSET) data_offset = avio_rb32(pb); + if (flags & MOV_TRUN_FIRST_SAMPLE_FLAGS) first_sample_flags = avio_rb32(pb); + dts = sc->track_end - sc->time_offset; offset = frag->base_data_offset + data_offset; distance = 0; av_dlog(c->fc, "first sample flags 0x%x\n", first_sample_flags); @@ -2060,16 +2251,22 @@ static int mov_read_trun(MOVContext *c, AVIOContext *pb, MOVAtom atom) unsigned sample_size = frag->size; int sample_flags = i ? frag->flags : first_sample_flags; unsigned sample_duration = frag->duration; - int keyframe; + int keyframe = 0; - if (flags & 0x100) sample_duration = avio_rb32(pb); - if (flags & 0x200) sample_size = avio_rb32(pb); - if (flags & 0x400) sample_flags = avio_rb32(pb); + if (flags & MOV_TRUN_SAMPLE_DURATION) sample_duration = avio_rb32(pb); + if (flags & MOV_TRUN_SAMPLE_SIZE) sample_size = avio_rb32(pb); + if (flags & MOV_TRUN_SAMPLE_FLAGS) sample_flags = avio_rb32(pb); sc->ctts_data[sc->ctts_count].count = 1; - sc->ctts_data[sc->ctts_count].duration = (flags & 0x800) ? avio_rb32(pb) : 0; + sc->ctts_data[sc->ctts_count].duration = (flags & MOV_TRUN_SAMPLE_CTS) ? + avio_rb32(pb) : 0; sc->ctts_count++; - if ((keyframe = st->codec->codec_type == AVMEDIA_TYPE_AUDIO || - (flags & 0x004 && !i && !sample_flags) || sample_flags & 0x2000000)) + if (st->codec->codec_type == AVMEDIA_TYPE_AUDIO) + keyframe = 1; + else if (!found_keyframe) + keyframe = found_keyframe = + !(sample_flags & (MOV_FRAG_SAMPLE_FLAG_IS_NON_SYNC | + MOV_FRAG_SAMPLE_FLAG_DEPENDS_YES)); + if (keyframe) distance = 0; av_add_index_entry(st, offset, dts, sample_size, distance, keyframe ? AVINDEX_KEYFRAME : 0); @@ -2079,9 +2276,10 @@ static int mov_read_trun(MOVContext *c, AVIOContext *pb, MOVAtom atom) distance++; dts += sample_duration; offset += sample_size; + sc->data_size += sample_size; } frag->moof_offset = offset; - st->duration = dts; + st->duration = sc->track_end = dts + sc->time_offset; return 0; } @@ -2119,14 +2317,14 @@ static int mov_read_cmov(MOVContext *c, AVIOContext *pb, MOVAtom atom) avio_rb32(pb); /* dcom atom */ if (avio_rl32(pb) != MKTAG('d','c','o','m')) - return -1; + return AVERROR_INVALIDDATA; if (avio_rl32(pb) != MKTAG('z','l','i','b')) { av_log(c->fc, AV_LOG_ERROR, "unknown compression for cmov atom !"); - return -1; + return AVERROR_INVALIDDATA; } avio_rb32(pb); /* cmvd atom */ if (avio_rl32(pb) != MKTAG('c','m','v','d')) - return -1; + return AVERROR_INVALIDDATA; moov_len = avio_rb32(pb); /* uncompressed size */ cmov_len = atom.size - 6 * 4; @@ -2139,15 +2337,12 @@ static int mov_read_cmov(MOVContext *c, AVIOContext *pb, MOVAtom atom) return AVERROR(ENOMEM); } avio_read(pb, cmov_data, cmov_len); - if(uncompress (moov_data, (uLongf *) &moov_len, (const Bytef *)cmov_data, cmov_len) != Z_OK) + if (uncompress (moov_data, (uLongf *) &moov_len, (const Bytef *)cmov_data, cmov_len) != Z_OK) goto free_and_return; - if(ffio_init_context(&ctx, moov_data, moov_len, 0, NULL, NULL, NULL, NULL) != 0) + if (ffio_init_context(&ctx, moov_data, moov_len, 0, NULL, NULL, NULL, NULL) != 0) goto free_and_return; atom.type = MKTAG('m','o','o','v'); atom.size = moov_len; -#ifdef DEBUG -// { int fd = open("/tmp/uncompheader.mov", O_WRONLY | O_CREAT); write(fd, moov_data, moov_len); close(fd); } -#endif ret = mov_read_default(c, &ctx, atom); free_and_return: av_free(moov_data); @@ -2155,7 +2350,7 @@ free_and_return: return ret; #else av_log(c->fc, AV_LOG_ERROR, "this file requires zlib support compiled in\n"); - return -1; + return AVERROR(ENOSYS); #endif } @@ -2173,10 +2368,10 @@ static int mov_read_elst(MOVContext *c, AVIOContext *pb, MOVAtom atom) avio_rb24(pb); /* flags */ edit_count = avio_rb32(pb); /* entries */ - if((uint64_t)edit_count*12+8 > atom.size) - return -1; + if ((uint64_t)edit_count*12+8 > atom.size) + return AVERROR_INVALIDDATA; - for(i=0; i= -1) { @@ -2192,7 +2387,7 @@ static int mov_read_elst(MOVContext *c, AVIOContext *pb, MOVAtom atom) } } - if(edit_count > 1) + if (edit_count > 1) av_log(c->fc, AV_LOG_WARNING, "multiple edit list entries, " "a/v desync might occur, patch welcome\n"); @@ -2210,7 +2405,7 @@ static const MOVParseTableEntry mov_default_parse_table[] = { { 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','i','e','l'), mov_read_fiel }, { MKTAG('f','t','y','p'), mov_read_ftyp }, { MKTAG('g','l','b','l'), mov_read_glbl }, { MKTAG('h','d','l','r'), mov_read_hdlr }, @@ -2251,11 +2446,88 @@ static const MOVParseTableEntry mov_default_parse_table[] = { { MKTAG('w','a','v','e'), mov_read_wave }, { MKTAG('e','s','d','s'), mov_read_esds }, { MKTAG('d','a','c','3'), mov_read_dac3 }, /* AC-3 info */ +{ MKTAG('d','e','c','3'), mov_read_dec3 }, /* EAC-3 info */ { MKTAG('w','i','d','e'), mov_read_wide }, /* place holder */ +{ MKTAG('w','f','e','x'), mov_read_wfex }, { MKTAG('c','m','o','v'), mov_read_cmov }, +{ MKTAG('c','h','a','n'), mov_read_chan }, /* channel layout */ +{ MKTAG('d','v','c','1'), mov_read_dvc1 }, { 0, NULL } }; +static int mov_read_default(MOVContext *c, AVIOContext *pb, MOVAtom atom) +{ + int64_t total_size = 0; + MOVAtom a; + int i; + + if (atom.size < 0) + atom.size = INT64_MAX; + while (total_size + 8 < atom.size && !pb->eof_reached) { + int (*parse)(MOVContext*, AVIOContext*, MOVAtom) = NULL; + a.size = atom.size; + a.type=0; + if (atom.size >= 8) { + a.size = avio_rb32(pb); + a.type = avio_rl32(pb); + } + av_dlog(c->fc, "type: %08x '%.4s' parent:'%.4s' sz: %"PRId64" %"PRId64" %"PRId64"\n", + a.type, (char*)&a.type, (char*)&atom.type, a.size, total_size, atom.size); + total_size += 8; + if (a.size == 1) { /* 64 bit extended size */ + a.size = avio_rb64(pb) - 8; + total_size += 8; + } + if (a.size == 0) { + a.size = atom.size - total_size; + if (a.size <= 8) + break; + } + a.size -= 8; + if (a.size < 0) + break; + a.size = FFMIN(a.size, atom.size - total_size); + + for (i = 0; mov_default_parse_table[i].type; i++) + if (mov_default_parse_table[i].type == a.type) { + parse = mov_default_parse_table[i].parse; + break; + } + + // container is user data + if (!parse && (atom.type == MKTAG('u','d','t','a') || + atom.type == MKTAG('i','l','s','t'))) + parse = mov_read_udta_string; + + if (!parse) { /* skip leaf atoms data */ + avio_skip(pb, a.size); + } else { + int64_t start_pos = avio_tell(pb); + int64_t left; + int err = parse(c, pb, a); + if (err < 0) + return err; + if (c->found_moov && c->found_mdat && + ((!pb->seekable || c->fc->flags & AVFMT_FLAG_IGNIDX) || + start_pos + a.size == avio_size(pb))) { + if (!pb->seekable || c->fc->flags & AVFMT_FLAG_IGNIDX) + c->next_root_atom = start_pos + a.size; + return 0; + } + left = a.size - avio_tell(pb) + start_pos; + if (left > 0) /* skip garbage at atom end */ + avio_skip(pb, left); + } + + total_size += a.size; + } + + if (total_size < atom.size && atom.size < 0x7ffff) + avio_skip(pb, atom.size - total_size); + + return 0; +} + static int mov_probe(AVProbeData *p) { unsigned int offset; @@ -2264,7 +2536,7 @@ static int mov_probe(AVProbeData *p) /* check file header */ offset = 0; - for(;;) { + for (;;) { /* ignore invalid offset */ if ((offset + 8) > (unsigned int)p->buf_size) return score; @@ -2298,7 +2570,6 @@ static int mov_probe(AVProbeData *p) return score; } } - return score; } // must be done after parsing all trak because there's no order requirement @@ -2347,24 +2618,64 @@ static void mov_read_chapters(AVFormatContext *s) // The samples could theoretically be in any encoding if there's an encd // atom following, but in practice are only utf-8 or utf-16, distinguished // instead by the presence of a BOM - ch = avio_rb16(sc->pb); - if (ch == 0xfeff) - avio_get_str16be(sc->pb, len, title, title_len); - else if (ch == 0xfffe) - avio_get_str16le(sc->pb, len, title, title_len); - else { - AV_WB16(title, ch); - avio_get_str(sc->pb, len - 2, title + 2, title_len - 2); + if (!len) { + title[0] = 0; + } else { + ch = avio_rb16(sc->pb); + if (ch == 0xfeff) + avio_get_str16be(sc->pb, len, title, title_len); + else if (ch == 0xfffe) + avio_get_str16le(sc->pb, len, title, title_len); + else { + AV_WB16(title, ch); + if (len == 1 || len == 2) + title[len] = 0; + else + avio_get_str(sc->pb, len - 2, title + 2, title_len - 2); + } } - ff_new_chapter(s, i, st->time_base, sample->timestamp, end, title); + avpriv_new_chapter(s, i, st->time_base, sample->timestamp, end, title); av_freep(&title); } finish: avio_seek(sc->pb, cur_pos, SEEK_SET); } -static int mov_read_header(AVFormatContext *s, AVFormatParameters *ap) +static int mov_read_close(AVFormatContext *s) +{ + MOVContext *mov = s->priv_data; + int i, j; + + for (i = 0; i < s->nb_streams; i++) { + AVStream *st = s->streams[i]; + MOVStreamContext *sc = st->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[j].dir); + } + av_freep(&sc->drefs); + if (sc->pb && sc->pb != s->pb) + avio_close(sc->pb); + } + + if (mov->dv_demux) { + for (i = 0; i < mov->dv_fctx->nb_streams; i++) { + av_freep(&mov->dv_fctx->streams[i]->codec); + av_freep(&mov->dv_fctx->streams[i]); + } + av_freep(&mov->dv_fctx); + av_freep(&mov->dv_demux); + } + + av_freep(&mov->trex_data); + + return 0; +} + +static int mov_read_header(AVFormatContext *s) { MOVContext *mov = s->priv_data; AVIOContext *pb = s->pb; @@ -2373,7 +2684,7 @@ static int mov_read_header(AVFormatContext *s, AVFormatParameters *ap) mov->fc = s; /* .mov and .mp4 aren't streamable anyway (only progressive download if moov is before mdat) */ - if(pb->seekable) + if (pb->seekable) atom.size = avio_size(pb); else atom.size = INT64_MAX; @@ -2381,17 +2692,29 @@ static int mov_read_header(AVFormatContext *s, AVFormatParameters *ap) /* check MOV header */ if ((err = mov_read_default(mov, pb, atom)) < 0) { av_log(s, AV_LOG_ERROR, "error reading header: %d\n", err); + mov_read_close(s); return err; } if (!mov->found_moov) { av_log(s, AV_LOG_ERROR, "moov atom not found\n"); - return -1; + mov_read_close(s); + return AVERROR_INVALIDDATA; } av_dlog(mov->fc, "on_parse_exit_offset=%"PRId64"\n", avio_tell(pb)); if (pb->seekable && mov->chapter_track > 0) mov_read_chapters(s); + if (mov->trex_data) { + int i; + for (i = 0; i < s->nb_streams; i++) { + AVStream *st = s->streams[i]; + MOVStreamContext *sc = st->priv_data; + if (st->duration) + st->codec->bit_rate = sc->data_size * 8 * sc->time_scale / st->duration; + } + } + return 0; } @@ -2432,8 +2755,11 @@ static int mov_read_packet(AVFormatContext *s, AVPacket *pkt) sample = mov_find_next_sample(s, &st); if (!sample) { mov->found_mdat = 0; - if (s->pb->seekable|| - mov_read_default(mov, s->pb, (MOVAtom){ AV_RL32("root"), INT64_MAX }) < 0 || + if (!mov->next_root_atom) + return AVERROR_EOF; + avio_seek(s->pb, mov->next_root_atom, SEEK_SET); + mov->next_root_atom = 0; + if (mov_read_default(mov, s->pb, (MOVAtom){ AV_RL32("root"), INT64_MAX }) < 0 || s->pb->eof_reached) return AVERROR_EOF; av_dlog(s, "read fragments, offset 0x%"PRIx64"\n", avio_tell(s->pb)); @@ -2447,7 +2773,7 @@ static int mov_read_packet(AVFormatContext *s, AVPacket *pkt) if (avio_seek(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; + return AVERROR_INVALIDDATA; } ret = av_get_packet(sc->pb, pkt, sample->size); if (ret < 0) @@ -2465,10 +2791,10 @@ static int mov_read_packet(AVFormatContext *s, AVPacket *pkt) } #if CONFIG_DV_DEMUXER if (mov->dv_demux && sc->dv_audio_container) { - dv_produce_packet(mov->dv_demux, pkt, pkt->data, pkt->size); + avpriv_dv_produce_packet(mov->dv_demux, pkt, pkt->data, pkt->size); av_free(pkt->data); pkt->size = 0; - ret = dv_get_packet(mov->dv_demux, pkt); + ret = avpriv_dv_get_packet(mov->dv_demux, pkt); if (ret < 0) return ret; } @@ -2477,7 +2803,7 @@ static int mov_read_packet(AVFormatContext *s, AVPacket *pkt) pkt->stream_index = sc->ffindex; pkt->dts = sample->timestamp; - if (sc->ctts_data) { + if (sc->ctts_data && sc->ctts_index < sc->ctts_count) { pkt->pts = pkt->dts + sc->dts_shift + sc->ctts_data[sc->ctts_index].duration; /* update ctts context */ sc->ctts_sample++; @@ -2514,7 +2840,7 @@ static int mov_seek_stream(AVFormatContext *s, AVStream *st, int64_t timestamp, if (sample < 0 && st->nb_index_entries && timestamp < st->index_entries[0].timestamp) sample = 0; if (sample < 0) /* not sure what to do */ - return -1; + return AVERROR_INVALIDDATA; sc->current_sample = sample; av_dlog(s, "stream %d, found sample %d\n", st->index, sc->current_sample); /* adjust ctts index */ @@ -2541,14 +2867,14 @@ static int mov_read_seek(AVFormatContext *s, int stream_index, int64_t sample_ti int i; if (stream_index >= s->nb_streams) - return -1; + return AVERROR_INVALIDDATA; if (sample_time < 0) sample_time = 0; st = s->streams[stream_index]; sample = mov_seek_stream(s, st, sample_time, flags); if (sample < 0) - return -1; + return sample; /* adjust seek timestamp to found sample timestamp */ seek_timestamp = st->index_entries[sample].timestamp; @@ -2564,48 +2890,13 @@ static int mov_read_seek(AVFormatContext *s, int stream_index, int64_t sample_ti return 0; } -static int mov_read_close(AVFormatContext *s) -{ - MOVContext *mov = s->priv_data; - int i, j; - - for (i = 0; i < s->nb_streams; i++) { - AVStream *st = s->streams[i]; - MOVStreamContext *sc = st->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[j].dir); - } - av_freep(&sc->drefs); - if (sc->pb && sc->pb != s->pb) - avio_close(sc->pb); - - av_freep(&st->codec->palctrl); - } - - if (mov->dv_demux) { - for(i = 0; i < mov->dv_fctx->nb_streams; i++) { - av_freep(&mov->dv_fctx->streams[i]->codec); - av_freep(&mov->dv_fctx->streams[i]); - } - av_freep(&mov->dv_fctx); - av_freep(&mov->dv_demux); - } - - av_freep(&mov->trex_data); - - return 0; -} - AVInputFormat ff_mov_demuxer = { - "mov,mp4,m4a,3gp,3g2,mj2", - NULL_IF_CONFIG_SMALL("QuickTime/MPEG-4/Motion JPEG 2000 format"), - sizeof(MOVContext), - mov_probe, - mov_read_header, - mov_read_packet, - mov_read_close, - mov_read_seek, + .name = "mov,mp4,m4a,3gp,3g2,mj2", + .long_name = NULL_IF_CONFIG_SMALL("QuickTime / MOV"), + .priv_data_size = sizeof(MOVContext), + .read_probe = mov_probe, + .read_header = mov_read_header, + .read_packet = mov_read_packet, + .read_close = mov_read_close, + .read_seek = mov_read_seek, };