diff --git a/libav/mov.c b/libav/mov.c index 7db4d2517603e6d6493d12e8090805238d72a930..0662c615359c1814f96cdd1b43db2a49b4116929 100644 --- a/libav/mov.c +++ b/libav/mov.c @@ -51,6 +51,10 @@ //#define DEBUG +/* allows chunk splitting - should work now... */ +/* in case you can't read a file, try commenting */ +#define MOV_SPLIT_CHUNKS + #ifdef DEBUG /* * XXX: static sux, even more in a multithreaded environment... @@ -66,7 +70,7 @@ void print_atom(const char *str, UINT32 type, UINT64 offset, UINT64 size) while(i--) printf("|"); printf("parse:"); - printf(" %s: tag=%c%c%c%c offset=%d size=0x%x\n", + printf(" %s: tag=%c%c%c%c offset=0x%x size=0x%x\n", str, tag & 0xff, (tag >> 8) & 0xff, (tag >> 16) & 0xff, @@ -90,14 +94,15 @@ static const CodecTag mov_video_tags[] = { /* Animation */ /* Apple video */ /* Kodak Photo CD */ -/* { CODEC_ID_JPEG, MKTAG('j', 'p', 'e', 'g') }, *//* JPEG ? */ + { 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', 'b') }, /* Motion-JPEG (format A) */ + { CODEC_ID_MJPEG, MKTAG('m', 'j', 'p', 'a') }, /* Motion-JPEG (format A) */ { CODEC_ID_MJPEG, MKTAG('m', 'j', 'p', 'b') }, /* Motion-JPEG (format B) */ /* { 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_MPEG4, MKTAG('m', 'p', '4', 'v') }, /* OpenDiVX *//* yeah ! */ { CODEC_ID_MPEG4, MKTAG('D', 'I', 'V', 'X') }, /* OpenDiVX *//* sample files at http://heroinewarrior.com/xmovie.php3 use this tag */ /* { CODEC_ID_, MKTAG('I', 'V', '5', '0') }, *//* Indeo 5.0 */ @@ -144,10 +149,13 @@ typedef struct MOVStreamContext { INT64 *chunk_offsets; long sample_to_chunk_sz; MOV_sample_to_chunk_tbl *sample_to_chunk; + long sample_to_chunk_index; long sample_size; long sample_count; long *sample_sizes; long time_scale; + long current_sample; + long left_in_chunk; /* how many samples before next chunk */ } MOVStreamContext; typedef struct MOVContext { @@ -165,6 +173,7 @@ typedef struct MOVContext { MOVStreamContext *streams[MAX_STREAMS]; INT64 next_chunk_offset; + int partial; /* != 0 : there is still to read in the current chunk (=id of the stream + 1) */ } MOVContext; @@ -337,6 +346,35 @@ static int parse_mdat(const MOVParseTableEntry *parse_table, ByteIOContext *pb, return 0; /* now go for moov */ } +/* this atom should be null (from specs), but some buggy files put the 'moov' atom inside it... */ +/* like the files created with Adobe Premiere 5.0, for samples see */ +/* http://graphics.tudelft.nl/~wouter/publications/soundtests/ */ +static int parse_wide(const MOVParseTableEntry *parse_table, ByteIOContext *pb, UINT32 atom_type, INT64 atom_offset, INT64 atom_size, void *param) +{ + int err; + UINT32 type; +#ifdef DEBUG + print_atom("wide", atom_type, atom_offset, atom_size); + debug_indent++; +#endif + if (atom_size < 8) + return 0; /* continue */ + if (get_be32(pb) != 0) { /* 0 sized mdat atom... use the 'wide' atom size */ + url_fskip(pb, atom_size - 4); + return 0; + } + type = get_le32(pb); + if (type != MKTAG('m', 'd', 'a', 't')) { + url_fskip(pb, atom_size - 8); + return 0; + } + err = parse_mdat(parse_table, pb, type, atom_offset + 8, atom_size - 8, param); +#ifdef DEBUG + debug_indent--; +#endif + return err; +} + static int parse_trak(const MOVParseTableEntry *parse_table, ByteIOContext *pb, UINT32 atom_type, INT64 atom_offset, INT64 atom_size, void *param) { MOVContext *c; @@ -347,14 +385,13 @@ static int parse_trak(const MOVParseTableEntry *parse_table, ByteIOContext *pb, #endif c = (MOVContext *)param; - st = av_malloc(sizeof(AVStream)); + st = av_new_stream(c->fc, c->fc->nb_streams); if (!st) return -2; - memset(st, 0, sizeof(AVStream)); - c->fc->streams[c->fc->nb_streams] = st; sc = av_malloc(sizeof(MOVStreamContext)); + sc->sample_to_chunk_index = -1; st->priv_data = sc; st->codec.codec_type = CODEC_TYPE_MOV_OTHER; - c->streams[c->fc->nb_streams++] = sc; + c->streams[c->fc->nb_streams-1] = sc; return parse_default(parse_table, pb, atom_type, atom_offset, atom_size, param); } @@ -437,7 +474,7 @@ static int parse_mdhd(const MOVParseTableEntry *parse_table, ByteIOContext *pb, static int parse_hdlr(const MOVParseTableEntry *parse_table, ByteIOContext *pb, UINT32 atom_type, INT64 atom_offset, INT64 atom_size, void *param) { MOVContext *c; - int len; + int len = 0; char *buf; UINT32 type; AVStream *st; @@ -627,6 +664,12 @@ static int parse_stsd(const MOVParseTableEntry *parse_table, ByteIOContext *pb, st->codec.frame_rate = 25 * FRAME_RATE_BASE; st->codec.bit_rate = 100000; } +// if(format == MKTAG('m', 'p', '4', 'a')) { /* XXX */ +// st->codec.codec_type=CODEC_TYPE_AUDIO; /* force things */ +// st->codec.codec_id = CODEC_ID_AAC; +//// st->codec.frame_rate = 25 * FRAME_RATE_BASE; +//// st->codec.bit_rate = 100000; +// } #if 0 @@ -706,6 +749,9 @@ static int parse_stsc(const MOVParseTableEntry *parse_table, ByteIOContext *pb, get_byte(pb); get_byte(pb); get_byte(pb); /* flags */ entries = get_be32(pb); +#ifdef DEBUG +printf("track[%i].stsc.entries = %i\n", c->fc->nb_streams-1, entries); +#endif sc->sample_to_chunk_sz = entries; sc->sample_to_chunk = av_malloc(entries * sizeof(MOV_sample_to_chunk_tbl)); for(i=0; ifc->nb_streams-1, entries); +#endif for(i=0; icodec.codec_type==CODEC_TYPE_VIDEO) { - st->codec.frame_rate = FRAME_RATE_BASE * c->streams[c->total_streams]->time_scale / sample_duration; + st->codec.frame_rate = FRAME_RATE_BASE * c->streams[c->total_streams]->time_scale; + if (sample_duration) + st->codec.frame_rate /= sample_duration; #ifdef DEBUG - printf("VIDEO FRAME RATE= %li (sd= %li)\n", st->codec.frame_rate, sample_duration); + printf("VIDEO FRAME RATE= %i (sd= %i)\n", st->codec.frame_rate, sample_duration); #endif } } @@ -857,7 +908,7 @@ static const MOVParseTableEntry mov_default_parse_table[] = { { MKTAG( 'u', 'u', 'i', 'd' ), parse_default }, { MKTAG( 'f', 'r', 'e', 'e' ), parse_leaf }, { MKTAG( 'h', 'd', 'l', 'r' ), parse_hdlr }, -{ MKTAG( 'h', 'm', 'h', 'd' ), parse_default }, +{ MKTAG( 'h', 'm', 'h', 'd' ), parse_leaf }, { MKTAG( 'h', 'i', 'n', 't' ), parse_leaf }, { MKTAG( 'n', 'm', 'h', 'd' ), parse_leaf }, { MKTAG( 'm', 'p', '4', 's' ), parse_default }, @@ -904,7 +955,7 @@ static const MOVParseTableEntry mov_default_parse_table[] = { { MKTAG( 's', 'y', 'n', 'c' ), parse_leaf }, { MKTAG( 's', 's', 'r', 'c' ), parse_leaf }, { MKTAG( 't', 'c', 'm', 'd' ), parse_leaf }, -{ MKTAG( 'w', 'i', 'd', 'e' ), parse_leaf }, /* place holder */ +{ MKTAG( 'w', 'i', 'd', 'e' ), parse_wide }, /* place holder */ #ifdef CONFIG_ZLIB { MKTAG( 'c', 'm', 'o', 'v' ), parse_cmov }, #else @@ -925,6 +976,8 @@ static void mov_free_stream_context(MOVStreamContext *sc) /* XXX: is it suffisant ? */ static int mov_probe(AVProbeData *p) { + int offset; + /* check file header */ if (p->buf_size <= 12) return 0; @@ -937,8 +990,24 @@ static int mov_probe(AVProbeData *p) (p->buf[4] == 'm' && p->buf[5] == 'd' && p->buf[6] == 'a' && p->buf[7] == 't')) return AVPROBE_SCORE_MAX; - else - return 0; + if (p->buf[4] == 'f' && p->buf[5] == 't' && + p->buf[6] == 'y' && p->buf[7] == 'p') { + offset = p->buf[0]; offset <<= 8; + offset |= p->buf[1]; offset <<= 8; + offset |= p->buf[2]; offset <<= 8; + offset |= p->buf[3]; + if (offset > p->buf_size) + return 0; + if ((p->buf[offset+4] == 'm' && p->buf[offset+5] == 'o' && + p->buf[offset+6] == 'o' && p->buf[offset+7] == 'v') || + (p->buf[offset+4] == 'w' && p->buf[offset+5] == 'i' && + p->buf[offset+6] == 'd' && p->buf[offset+7] == 'e') || + (p->buf[offset+4] == 'm' && p->buf[offset+5] == 'd' && + p->buf[offset+6] == 'a' && p->buf[offset+7] == 't')) + return AVPROBE_SCORE_MAX; + + } + return 0; } static int mov_read_header(AVFormatContext *s, AVFormatParameters *ap) @@ -1019,44 +1088,85 @@ static int mov_read_packet(AVFormatContext *s, AVPacket *pkt) int i; int st_id = 0, size; size = 0x0FFFFFFF; - + +#ifdef MOV_SPLIT_CHUNKS + if (mov->partial) { + + int idx; + + st_id = mov->partial - 1; + idx = mov->streams[st_id]->sample_to_chunk_index; + if (idx < 0) return 0; + size = mov->streams[st_id]->sample_sizes[mov->streams[st_id]->current_sample]; + + mov->streams[st_id]->current_sample++; + mov->streams[st_id]->left_in_chunk--; + + if(mov->streams[st_id]->left_in_chunk <= 0) + mov->partial = 0; + offset = mov->next_chunk_offset; + /* extract the sample */ + + goto readchunk; + } +#endif + again: for(i=0; itotal_streams; i++) { -/* printf("%8ld ", mov->streams[i]->chunk_offsets[mov->streams[i]->next_chunk]); */ if((mov->streams[i]->next_chunk < mov->streams[i]->chunk_count) && (mov->streams[i]->chunk_offsets[mov->streams[i]->next_chunk] < offset)) { -/* printf("y"); */ st_id = i; offset = mov->streams[i]->chunk_offsets[mov->streams[i]->next_chunk]; } -/* else printf("n"); */ } mov->streams[st_id]->next_chunk++; if(offset==0x0FFFFFFFFFFFFFFF) return -1; - if(mov->next_chunk_offset < offset) /* some meta data */ + if(mov->next_chunk_offset < offset) { /* some meta data */ url_fskip(&s->pb, (offset - mov->next_chunk_offset)); + mov->next_chunk_offset = offset; + } + +//printf("chunk: [%i] %lli -> %lli\n", st_id, mov->next_chunk_offset, offset); if(!mov->streams[st_id]->is_ff_stream) { url_fskip(&s->pb, (offset - mov->next_chunk_offset)); + mov->next_chunk_offset = offset; offset = 0x0FFFFFFFFFFFFFFF; -/* puts("*"); */ goto again; } -/* printf("\nchunk offset = %ld\n", offset); */ /* now get the chunk size... */ for(i=0; itotal_streams; i++) { -/* printf("%ld ", mov->streams[i]->chunk_offsets[mov->streams[i]->next_chunk] - offset); */ if((mov->streams[i]->next_chunk < mov->streams[i]->chunk_count) && ((mov->streams[i]->chunk_offsets[mov->streams[i]->next_chunk] - offset) < size)) { -/* printf("y"); */ size = mov->streams[i]->chunk_offsets[mov->streams[i]->next_chunk] - offset; } -/* else printf("n"); */ } -/* printf("\nchunk size = %ld\n", size); */ +#ifdef MOV_SPLIT_CHUNKS + /* split chunks into samples */ + if(mov->streams[st_id]->sample_size == 0) { + int idx; + idx = mov->streams[st_id]->sample_to_chunk_index; + if ((idx + 1 < mov->streams[st_id]->sample_to_chunk_sz) + && (mov->streams[st_id]->next_chunk >= mov->streams[st_id]->sample_to_chunk[idx + 1].first)) + idx++; + mov->streams[st_id]->sample_to_chunk_index = idx; + if(idx >= 0 && mov->streams[st_id]->sample_to_chunk[idx].count != 1) { + mov->partial = st_id+1; + /* we'll have to get those samples before next chunk */ + mov->streams[st_id]->left_in_chunk = (mov->streams[st_id]->sample_to_chunk[idx].count) - 1; + size = mov->streams[st_id]->sample_sizes[mov->streams[st_id]->current_sample]; + } + + mov->streams[st_id]->current_sample++; + } +#endif + +readchunk: + +//printf("chunk: [%i] %lli -> %lli (%i)\n", st_id, offset, offset + size, size); if(size == 0x0FFFFFFF) size = mov->mdat_size + mov->mdat_offset - offset; if(size < 0)