Index: libmpdemux/demux_mov.c =================================================================== --- libmpdemux/demux_mov.c (revision 24882) +++ libmpdemux/demux_mov.c (working copy) @@ -109,6 +109,7 @@ int id; int type; off_t pos; + int track_ID; // unsigned int media_handler; unsigned int data_handler; @@ -147,6 +148,158 @@ void* desc; // image/sound/etc description (pointer to ImageDescription etc) } mov_track_t; +#define MFRA_MAX_KEYFRAMES 500 + +typedef struct { + int track_ID; + // + uint64_t base_data_offset; + uint32_t sample_description_index; + uint32_t default_sample_duration; + uint32_t default_sample_size; + uint32_t default_sample_flags; + // + int samples_size; + mov_sample_t* samples; + int chunks_size; + mov_chunk_t* chunks; + int durmap_size; + mov_durmap_t* durmap; + int keyframes_size; + unsigned int keyframes[MFRA_MAX_KEYFRAMES]; +} mov_traf_t; + +void add_traf_to_track(mov_track_t* trak,int timescale,mov_traf_t* traf) +{ + int i, s; + unsigned int original_samples_size, constant_sample_size; + + // Add samples + original_samples_size = trak->samples_size; + constant_sample_size = 0; + if(!trak->samples_size){ // constant samplesize in trak + if( (trak->samplesize == traf->default_sample_size) || traf->default_sample_size==0 ){ + constant_sample_size = 1; + } else { + s=0; + for(i=0;ichunks_size;i++){ + trak->chunks[i].sample=s; + s+=trak->chunks[i].size; + } + trak->samples_size=s+traf->samples_size; + trak->samples=calloc(s+traf->samples_size, sizeof(mov_sample_t)); + for(i=0;isamples[i].size=trak->samplesize; + for(i=0;isamples_size;i++) trak->samples[s + i].size = traf->samples[i].size; + trak->samplesize=0; + } + } else { + trak->samples = realloc_struct(trak->samples, trak->samples_size+traf->samples_size, sizeof(mov_sample_t)); + for(i=0;isamples_size;i++) + trak->samples[trak->samples_size + i].size = traf->samples[i].size; + + trak->samples_size += traf->samples_size; + //mp_msg(MSGT_DEMUX, MSGL_V, "Added %d samples, total %d\n",traf->samples_size,trak->samples_size); + } + + // Add chunks + trak->chunks = realloc_struct(trak->chunks, trak->chunks_size+traf->chunks_size, sizeof(mov_chunk_t)); + for(i=0;ichunks_size;i++){ + trak->chunks[trak->chunks_size + i].size = traf->chunks[i].size; + trak->chunks[trak->chunks_size + i].pos = traf->chunks[i].pos; + trak->chunks[trak->chunks_size + i].desc = traf->chunks[i].desc; + } + trak->chunks_size += traf->chunks_size; + //mp_msg(MSGT_DEMUX, MSGL_V, "Added %d chunks\n",traf->chunks_size); + + // Add durmaps + if( !constant_sample_size ) { + trak->durmap = realloc_struct(trak->durmap, trak->durmap_size+traf->durmap_size, sizeof(mov_durmap_t)); + for(i=0;idurmap_size;i++){ + trak->durmap[trak->durmap_size + i].dur = traf->durmap[i].dur; + trak->durmap[trak->durmap_size + i].num = traf->durmap[i].num; + } + trak->durmap_size += traf->durmap_size; + } else { + for(i=0;idurmap_size;i++){ + trak->durmap[0].dur += traf->durmap[i].dur; + trak->durmap[0].num += traf->durmap[i].num; + } + } + + if( trak->keyframes_size>0 && trak->keyframes_size>0 ) { + trak->keyframes = realloc(trak->keyframes, (trak->keyframes_size+traf->keyframes_size)*sizeof(unsigned int)); + for( i=0; ikeyframes_size; i++ ) { + trak->keyframes[trak->keyframes_size+i]=original_samples_size+trak->keyframes[i]; + } + trak->keyframes_size += traf->keyframes_size; + } else if( trak->keyframes_size>0 ) { // no mfra supplied but needed, need to create it?? + trak->keyframes = realloc(trak->keyframes, (trak->keyframes_size+traf->samples_size)*sizeof(unsigned int)); + for( i=0; isamples_size; i++ ) { + trak->keyframes[trak->keyframes_size+i]=original_samples_size+i; + } + trak->keyframes_size += traf->samples_size; + } +} + +void mov_rebuild_index(mov_track_t* trak,int timescale) +{ + int i,j,s; + unsigned int pts=0; + + // calc pts of chunks: + s=0; + for(j=0;jchunks_size;j++){ + trak->chunks[j].sample=s; + s+=trak->chunks[j].size; + } + i = 0; + for (j = 0; j < trak->durmap_size; j++) i += trak->durmap[j].num; + if (i != s) { + mp_msg(MSGT_DEMUX, MSGL_WARN,"MOV: durmap and chunkmap sample count differ (%i vs %i)\n", i, s); + if (i > s) s = i; + } + + if (trak->samples_size < s) { + if( trak->samples_size ) { + mp_msg(MSGT_DEMUX, MSGL_WARN,"MOV: durmap or chunkmap bigger than sample count (%i vs %i)\n",s, trak->samples_size); + } + trak->samples_size = s; + trak->samples = realloc_struct(trak->samples, s, sizeof(mov_sample_t)); + } + + // calc pts: + s=0; + for(j=0;jdurmap_size;j++){ + for(i=0;idurmap[j].num;i++){ + trak->samples[s].pts=pts; + ++s; + pts+=trak->durmap[j].dur; + if(!trak->durmap[j].dur) + pts+=trak->durmap[0].dur; + } + } + + if( trak->length != pts ) { + // normal behavior when TRAFS are present + //mp_msg(MSGT_DEMUX, MSGL_WARN, + // "MOV: track length is: %d, and durmap length is: %d\n", trak->length, pts ); + trak->length = pts; + } + + // calc sample offsets + s=0; + for(j=0;jchunks_size;j++){ + off_t pos=trak->chunks[j].pos; + mp_msg(MSGT_DEMUX, MSGL_DBG3, "Chunk %5d: sample=%8d off=0x%08X size=%d\n",j,trak->chunks[j].sample,trak->chunks[j].pos, trak->chunks[j].size); + for(i=0;ichunks[j].size;i++){ + trak->samples[s].pos=pos; + mp_msg(MSGT_DEMUX, MSGL_DBG3, "Sample %5d: pts=%8d off=0x%08X size=%d\n",s,trak->samples[s].pts,(int)trak->samples[s].pos,trak->samples[s].size); + pos+=trak->samples[s].size; + ++s; + } + } +} + void mov_build_index(mov_track_t* trak,int timescale){ int i,j,s; int last=trak->chunks_size; @@ -224,9 +377,11 @@ } if (trak->samples_size < s) { + if( trak->samples_size ) { mp_msg(MSGT_DEMUX, MSGL_WARN, "MOV: durmap or chunkmap bigger than sample count (%i vs %i)\n", s, trak->samples_size); + } trak->samples_size = s; trak->samples = realloc_struct(trak->samples, s, sizeof(mov_sample_t)); } @@ -269,10 +424,21 @@ // skip! el->frames=0; continue; } - // find start sample + // find start sample. If track has keyframes, choose the nearest + // (earlier) keyframe. Otherwise, just choose the nearest sample. + if(trak->keyframes_size) { + unsigned int kfi; + for(kfi=0;kfikeyframes_size-1;kfi++){ + if(trak->samples[trak->keyframes[kfi+1]].pts > pts){ + sample = trak->keyframes[kfi]; + break; + } + } + } else { for(;samplesamples_size;sample++){ if(pts<=trak->samples[sample].pts) break; } + } el->start_sample=sample; el->pts_offset=((long long)e_pts*(long long)trak->timescale)/(long long)timescale-trak->samples[sample].pts; pts+=((long long)el->dur*(long long)trak->timescale)/(long long)timescale; @@ -293,12 +459,24 @@ #define MOV_MAX_TRACKS 256 #define MOV_MAX_SUBLEN 1024 +#define MOV_MAX_FRAGMENTS 256 +#define MOV_MAX_TRAFS 256 typedef struct { off_t moov_start; off_t moov_end; - off_t mdat_start; - off_t mdat_end; + off_t mdat_start[MOV_MAX_FRAGMENTS]; + off_t mdat_end[MOV_MAX_FRAGMENTS]; + off_t moof_start[MOV_MAX_FRAGMENTS]; + off_t moof_end[MOV_MAX_FRAGMENTS]; + off_t mfra_start; + off_t mfra_end; + int mvex_counter; + mov_traf_t mvexs[MOV_MAX_TRACKS]; + int mdat_counter; + int moof_counter; + int traf_counter; + mov_traf_t* trafs[MOV_MAX_TRAFS]; int track_db; mov_track_t* tracks[MOV_MAX_TRACKS]; int timescale; // movie timescale @@ -310,6 +488,133 @@ #define MOV_FOURCC(a,b,c,d) ((a<<24)|(b<<16)|(c<<8)|(d)) +// Helper fnction, should be moved to stream.h +inline static unsigned int stream_read_x(stream_t *s,int x) +{ + switch(x) { + case 0: return stream_read_char(s); + case 1: return stream_read_word(s); + case 2: return stream_read_int24(s); + case 3: return stream_read_dword(s); + } + return 0; // error +} + +static void lschunks_inmfra(demuxer_t* demuxer,int level,off_t endpos,mov_track_t* trak) +{ + mov_priv_t* priv=demuxer->priv; + off_t len = 0; + off_t pos = stream_tell(demuxer->stream); + + while(1){ + unsigned int id; + pos += len; + + if(!stream_seek(demuxer->stream,pos)) { + mp_msg(MSGT_DEMUX,MSGL_ERR,"MOV: Cannot seek to the beginning of the 'mfra' header (0x%"PRIx64")\n",(int64_t)pos); + return; + } + + if(pos>=endpos) return; // END + len=stream_read_dword(demuxer->stream); + if(len<8) return; // error + + + id=stream_read_dword(demuxer->stream); + switch(id) { + case MOV_FOURCC('t','f','r','a'): { + int version = stream_read_dword(demuxer->stream); + int flags = version & 0x0ffffff; + version = version >> 24; + mp_msg(MSGT_DEMUX, MSGL_V,"MOV:\tTrack Fragment Random Access Box 'tfra' (%d bytes): v=%d flags=%d\n",(int)len, (int)version,(int)flags); + uint32_t track_ID = stream_read_dword(demuxer->stream); + uint32_t tmp = stream_read_dword(demuxer->stream); + uint32_t reserved = tmp >> 6; + uint32_t length_size_of_traf_num = (tmp & 0x030) >> 4; + uint32_t length_size_of_trun_num = (tmp & 0x0c) >> 2; + uint32_t length_size_of_sample_num = (tmp & 0x03); + uint32_t number_of_entry = stream_read_dword(demuxer->stream); + mp_msg(MSGT_DEMUX, MSGL_DBG3,"MOV:\t\ttrack_ID = %d\n",track_ID); + mp_msg(MSGT_DEMUX, MSGL_DBG3,"MOV:\t\treserved = %d\n",reserved); + mp_msg(MSGT_DEMUX, MSGL_DBG3,"MOV:\t\tlength_size_of_traf_num = %d\n",length_size_of_traf_num); + mp_msg(MSGT_DEMUX, MSGL_DBG3,"MOV:\t\tlength_size_of_trun_num = %d\n",length_size_of_trun_num); + mp_msg(MSGT_DEMUX, MSGL_DBG3,"MOV:\t\tlength_size_of_sample_num = %d\n",length_size_of_sample_num); + mp_msg(MSGT_DEMUX, MSGL_DBG3,"MOV:\t\tnumber_of_entry = %d\n",number_of_entry); + int i, j, k, count_trafs; + mov_traf_t *traf = NULL; + uint64_t count_sample; + for(i=0;istream); + moof_offset = stream_read_qword(demuxer->stream); + }else{ + time = stream_read_dword(demuxer->stream);; + moof_offset = stream_read_dword(demuxer->stream);; + } + traf_number = stream_read_x(demuxer->stream,length_size_of_traf_num); + trun_number = stream_read_x(demuxer->stream,length_size_of_trun_num); + sample_number = stream_read_x(demuxer->stream,length_size_of_sample_num); + + mp_msg(MSGT_DEMUX, MSGL_DBG3,"MOV:\t\t[entry %d]\n",i); + mp_msg(MSGT_DEMUX, MSGL_DBG3,"MOV:\t\ttime = %d\n",time); + mp_msg(MSGT_DEMUX, MSGL_DBG3,"MOV:\t\tmoof_offset = %d\n",moof_offset); + mp_msg(MSGT_DEMUX, MSGL_DBG3,"MOV:\t\ttraf_number = %d\n",traf_number); + mp_msg(MSGT_DEMUX, MSGL_DBG3,"MOV:\t\ttrun_number = %d\n",trun_number); + mp_msg(MSGT_DEMUX, MSGL_DBG3,"MOV:\t\tsample_number = %d\n",sample_number); + + // look for the specified traf + if( traf!= NULL ) { + if( traf->track_ID == track_ID ) { + if( count_trafs == traf_number ) { // already selected traf, no need to search it + if( trun_numberchunks_size && trun_number>0 ) { + for( k=0, count_sample=0; kchunks[k].size; + count_sample += sample_number-1; + traf->keyframes[traf->keyframes_size++] = count_sample; + //printf( "keyframe created WHITHOUT!!!!: %d\n", count_sample ); + continue; + } + } + } + } + // full search + for( j=0, count_trafs=0; jtraf_counter; j++ ) { + if( priv->trafs[j]->track_ID == track_ID ) { + traf = priv->trafs[j]; + if( ++count_trafs == traf_number ) { // found the right traf + if( trun_numberchunks_size && trun_number>0 ) { + //then sample number is + for( k=0, count_sample=0; kchunks[k].size; + count_sample += sample_number-1; + traf->keyframes[traf->keyframes_size++] = count_sample; + //printf( "keyframe created: %d\n", count_sample ); + break; + } + } + } + } + } break; + } + case MOV_FOURCC('m','f','r','o'):{ + int version = stream_read_dword(demuxer->stream); + int flags = version & 0x0ffffff; + version = version >> 24; + mp_msg(MSGT_DEMUX, MSGL_V,"MOV:\tMovie Fragment Random Access Offset Box 'mfro' (%d bytes): v=%d flags=%d\n",(int)len, (int)version,(int)flags); + uint32_t size = stream_read_dword(demuxer->stream); + mp_msg(MSGT_DEMUX, MSGL_DBG3,"MOV:\t\tsize = %d\n",size); + } break; + default: + id = be2me_32(id); + mp_msg(MSGT_DEMUX,MSGL_DBG3,"MOV: unknown chunk: %.4s %d\n",(char *)&id,(int)len); + } + } +} + static int mov_check_file(demuxer_t* demuxer){ int flags=0; int no=0; @@ -474,17 +779,20 @@ mp_msg(MSGT_DEMUX,MSGL_V,"MOV: 'WIDE' chunk found!\n"); if(flags&2) break; case MOV_FOURCC('m','d','a','t'): - mp_msg(MSGT_DEMUX,MSGL_V,"MOV: Movie DATA found!\n"); - priv->mdat_start=stream_tell(demuxer->stream); - priv->mdat_end=priv->mdat_start+len-skipped; - mp_msg(MSGT_DEMUX,MSGL_DBG2,"MOV: Movie data: start: %"PRIx64" end: %"PRIx64"\n", - (int64_t)priv->mdat_start, (int64_t)priv->mdat_end); + mp_msg(MSGT_DEMUX,MSGL_V,"MOV: Movie DATA #%"PRIx64" found!\n",(int64_t)priv->mdat_counter); + priv->mdat_start[priv->mdat_counter]=stream_tell(demuxer->stream); + priv->mdat_end[priv->mdat_counter]=priv->mdat_start[priv->mdat_counter]+len-skipped; + priv->mdat_counter++; + mp_msg(MSGT_DEMUX,MSGL_DBG2,"MOV: Movie data #%"PRIx64": start: %"PRIx64" end: %"PRIx64"\n", + (int64_t)priv->mdat_counter,(int64_t)priv->mdat_start[priv->mdat_counter-1], + (int64_t)priv->mdat_end[priv->mdat_counter-1]); flags|=2; - if(flags==3){ + //if(flags==3){ // if we're over the headers, then we can stop parsing here! - demuxer->priv=priv; - return DEMUXER_TYPE_MOV; - } + // demuxer->priv=priv; + // return DEMUXER_TYPE_MOV; + //} + // Don't stop here, keep parsing the file, looking for moofs break; case MOV_FOURCC('f','r','e','e'): case MOV_FOURCC('s','k','i','p'): @@ -496,6 +804,21 @@ case MOV_FOURCC('P','I','C','T'): /* dunno what, but we shoudl ignore it */ break; + case MOV_FOURCC('m','f','r','a'): { + off_t pos = stream_tell(demuxer->stream); + priv->mfra_start = pos; + priv->mfra_end = pos+len-skipped; + mp_msg(MSGT_DEMUX,MSGL_V,"MOV: Movie Fragment Random Access Box 'mfra' (%d bytes)\n",(int)len); + } break; + /* Now we handle moofs */ + case MOV_FOURCC('m','o','o','f'):{ + priv->moof_start[priv->moof_counter]=stream_tell(demuxer->stream); + priv->moof_end[priv->moof_counter]=priv->moof_start[priv->moof_counter]+len-skipped; + priv->moof_counter++; + mp_msg(MSGT_DEMUX,MSGL_V,"MOV: moof fragment #%d: start: %"PRIx64" end: %"PRIx64"\n",priv->moof_counter, + (int64_t)priv->moof_start[priv->moof_counter-1], (int64_t)priv->moof_end[priv->moof_counter-1]); + } break; + default: if(no==0){ free(priv); return 0;} // first chunk is bad! id = be2me_32(id); @@ -544,9 +867,301 @@ free(track); } } + for (i = 0; i < MOV_MAX_TRAFS; i++) { + mov_traf_t *traf = priv->trafs[i]; + if (traf) { + // free all traf fileds here + free(traf->samples); + free(traf->durmap); + free(traf->chunks); + } + } free(priv); } +static void lschunks_intraf(demuxer_t* demuxer,int level,off_t endpos,mov_traf_t* traf) +{ + mov_priv_t* priv=demuxer->priv; + off_t len = 0; + off_t pos = stream_tell(demuxer->stream); + off_t start = stream_tell(demuxer->stream); + int trun_count = 0; + unsigned int found_variable_samplesize = 0; + unsigned int id; + + // First read: parse tfhd and count number of truns + while((pos+len)stream,pos)) { + mp_msg(MSGT_DEMUX,MSGL_ERR,"MOV: Cannot seek to the beginning of the 'traf' atom (0x%"PRIx64")\n",(int64_t)pos); + return; + } + + len=stream_read_dword(demuxer->stream); + if(len<8) return; // error + id=stream_read_dword(demuxer->stream); + switch(id) { + case MOV_FOURCC('t','f','h','d'): { + int k, version = stream_read_dword(demuxer->stream); + int tf_flags = version & 0x0ffffff; + version = version >> 24; + mp_msg(MSGT_DEMUX, MSGL_V,"MOV:\tTrack Fragment Header Box 'tfhd'(%d bytes): v=%d tf_flags=%d\n", + (int)len, (int) version,(int)tf_flags); + traf->track_ID = stream_read_dword(demuxer->stream); // all the following are optional fields + mp_msg(MSGT_DEMUX, MSGL_DBG3,"MOV:\t\tTrack_ID=%d\n",traf->track_ID); + + // first apply whatever mvex found! + for( k=0; k< priv->mvex_counter; k++ ) { + if( priv->mvexs[k].track_ID == traf->track_ID ) { + traf->sample_description_index = priv->mvexs[k].sample_description_index; + traf->default_sample_duration = priv->mvexs[k].default_sample_duration; + traf->default_sample_size = priv->mvexs[k].default_sample_size; + traf->default_sample_flags = priv->mvexs[k].default_sample_flags; + } + } + + // now continue parse + if(tf_flags & 0x000001) { + traf->base_data_offset = stream_read_qword(demuxer->stream); + mp_msg(MSGT_DEMUX, MSGL_DBG3,"MOV:\t\tbase_data_offset=%d\n",traf->base_data_offset); + } + if(tf_flags & 0x000002) { + traf->sample_description_index = stream_read_dword(demuxer->stream); + mp_msg(MSGT_DEMUX, MSGL_DBG3,"MOV:\t\tsample_description_index=%d\n",traf->sample_description_index); + } + if(tf_flags & 0x000008) { + traf->default_sample_duration = stream_read_dword(demuxer->stream); + mp_msg(MSGT_DEMUX, MSGL_DBG3,"MOV:\t\tdefault_sample_duration=%d\n",traf->default_sample_duration); + } + if(tf_flags & 0x000010) { + traf->default_sample_size = stream_read_dword(demuxer->stream); + mp_msg(MSGT_DEMUX, MSGL_DBG3,"MOV:\t\tdefault_sample_size=%d\n",traf->default_sample_size); + } + if(tf_flags & 0x000020) { + traf->default_sample_flags = stream_read_dword(demuxer->stream); + mp_msg(MSGT_DEMUX, MSGL_DBG3,"MOV:\t\tdefault_sample_flags=%d\n",traf->default_sample_flags); + } + }break; + case MOV_FOURCC('t','r','u','n'): { + int i; + int version = stream_read_dword(demuxer->stream); + int tf_flags = version & 0x0ffffff; + version = version >> 24; + uint32_t sample_count = stream_read_dword(demuxer->stream); + + traf->samples_size += sample_count; + if(tf_flags & 0x000200) found_variable_samplesize = 1; + traf->chunks_size += 1; + if(tf_flags & 0x000100) + traf->durmap_size += sample_count; + else + traf->durmap_size += 1; + } break; + default: + id = be2me_32(id); + mp_msg(MSGT_DEMUX,MSGL_V,"MOV:\tunknown chunk: %.4s %d\n",(char *)&id,(int)len); + } + } + + // Alloc arrays + if( found_variable_samplesize ) { + traf->samples = (mov_sample_t*) malloc(traf->samples_size*sizeof(mov_sample_t)); + memset( traf->samples, 0, traf->samples_size*sizeof(mov_sample_t)); + } else { + traf->samples_size=0; + } + traf->durmap = (mov_durmap_t*)malloc(traf->durmap_size*sizeof(mov_durmap_t)); + traf->chunks = (mov_chunk_t*)malloc(traf->chunks_size*sizeof(mov_chunk_t)); + memset( traf->durmap, 0, traf->durmap_size*sizeof(mov_durmap_t) ); + memset( traf->chunks, 0, traf->chunks_size*sizeof(mov_chunk_t)); + + len = 0; + pos = start; + int sample_count = 0; + int durmap_count = 0; + int chunk_count = 0; + + // Second read: parse truns + while(1){ + pos += len; + + if(!stream_seek(demuxer->stream,pos)) { + mp_msg(MSGT_DEMUX,MSGL_ERR,"MOV: Cannot seek to the beginning of the 'traf' atom (0x%"PRIx64")\n", (int64_t)pos); + return; + } + if(pos>=endpos) { + mp_msg(MSGT_DEMUX, MSGL_DBG3,"MOV:\tTraf #%d: samples: %d, chunks: %d\n", + priv->traf_counter-1,traf->samples_size,traf->chunks_size); + return; // END + } + + len=stream_read_dword(demuxer->stream); + if(len<8) return; // error + + id=stream_read_dword(demuxer->stream); + switch(id) { + case MOV_FOURCC('t','f','h','d'): break; + case MOV_FOURCC('t','r','u','n'): { + int i; + int version = stream_read_dword(demuxer->stream); + int tf_flags = version & 0x0ffffff; + int32_t data_offset = 0; + uint32_t first_sample_flags; + uint32_t K = stream_read_dword(demuxer->stream); + + version = version >> 24; + mp_msg(MSGT_DEMUX, MSGL_V,"MOV:\tTrack Fragment Run Box 'trun' (%d bytes): v=%d tf_flags=%d\n", + (int)len, (int)version,(int)tf_flags); + + if(tf_flags & 0x000001) data_offset = stream_read_dword(demuxer->stream); + if(tf_flags & 0x000004) first_sample_flags = stream_read_dword(demuxer->stream); + traf->chunks[chunk_count].size = K; + traf->chunks[chunk_count].desc = traf->sample_description_index; + traf->chunks[chunk_count].pos = traf->base_data_offset + data_offset; + chunk_count++; + + for(i=0;idurmap[durmap_count].dur = stream_read_dword(demuxer->stream); + traf->durmap[durmap_count].num = 1; + durmap_count++; + } + if( found_variable_samplesize ) { + if(tf_flags & 0x000200) traf->samples[sample_count].size = stream_read_dword(demuxer->stream); + else traf->samples[sample_count].size = traf->default_sample_size; + sample_count++; + } + if(tf_flags & 0x000400) { + sample_flags = stream_read_dword(demuxer->stream); + mp_msg(MSGT_DEMUX, MSGL_DBG3,"MOV:\t\t\tsample_flags=%d\n",sample_flags); + } + if(tf_flags & 0x000800) { + sample_composition_time_offset = stream_read_dword(demuxer->stream); + mp_msg(MSGT_DEMUX, MSGL_DBG3,"MOV:\t\t\tsample_composition_time_offset=%d\n", + sample_composition_time_offset); + } + } + if( !(tf_flags & 0x000100) ) { + traf->durmap[durmap_count].num = K; + traf->durmap[durmap_count].dur = traf->default_sample_duration; + durmap_count++; + } + } break; + default: + id = be2me_32(id); + mp_msg(MSGT_DEMUX,MSGL_V,"MOV:\tunknown chunk: %.4s %d\n",(char *)&id,(int)len); + } + } +} + +static void lschunks_inmoof(demuxer_t* demuxer,int level,off_t endpos,mov_track_t* trak) +{ + mov_priv_t* priv=demuxer->priv; + off_t len = 0; + off_t pos = stream_tell(demuxer->stream); + + while(1){ + unsigned int id; + + pos += len; + if(!stream_seek(demuxer->stream,pos)) { + mp_msg(MSGT_DEMUX,MSGL_ERR,"MOV: Cannot seek to the beginning of the 'moof' header (0x%"PRIx64")\n", (int64_t)pos); + return; + } + if(pos>=endpos) return; // END + len=stream_read_dword(demuxer->stream); + if(len<8) return; // error + id=stream_read_dword(demuxer->stream); + switch(id) { + case MOV_FOURCC('m','f','h','d'): { + int version = stream_read_dword(demuxer->stream); + int flags = version & 0x0ffffff; + version = version >> 24; + int sequence_number = stream_read_dword(demuxer->stream); // unordered or incomplete incoming trafs not supported + mp_msg(MSGT_DEMUX, MSGL_V,"MOV:Movie Fragment Header Box 'mfhd' (%d bytes): v=%d flags=%d\n", + (int)len, (int)version,(int)flags); + mp_msg(MSGT_DEMUX, MSGL_DBG3,"MOV:\tsequence_number = %d\n",sequence_number); + }break; + case MOV_FOURCC('t','r','a','f'):{ + mp_msg(MSGT_DEMUX, MSGL_V,"MOV:Track Fragment Box 'traf' #%d (%d bytes)\n", + priv->traf_counter,(int)len); + priv->trafs[priv->traf_counter] = (mov_traf_t*) malloc(sizeof(mov_traf_t)); + memset(priv->trafs[priv->traf_counter],0,sizeof(mov_traf_t)); + lschunks_intraf(demuxer,1,pos+len,priv->trafs[priv->traf_counter]); + priv->traf_counter++; + } break; + default: + id = be2me_32(id); + mp_msg(MSGT_DEMUX,MSGL_V,"MOV: unknown chunk: %.4s %d\n",(char *)&id,(int)len); + } + } +} + +static void lschunks_inmvex(demuxer_t* demuxer,int level,off_t endpos,mov_track_t* trak) +{ + mov_priv_t* priv=demuxer->priv; + off_t len = 0; + off_t pos = stream_tell(demuxer->stream); + + while(1){ + unsigned int id; + pos += len; + if(!stream_seek(demuxer->stream,pos)) { + mp_msg(MSGT_DEMUX,MSGL_ERR,"MOV: Cannot seek to the beginning of the 'mvex' header (0x%"PRIx64")\n", + (int64_t)pos); + return; + } + if(pos>=endpos) return; // END + len=stream_read_dword(demuxer->stream); + if(len<8) return; // error + + id=stream_read_dword(demuxer->stream); + switch(id) { + case MOV_FOURCC('m','e','h','d'): { + int version = stream_read_dword(demuxer->stream); + int flags = version & 0x0ffffff; + int64_t fragment_duration; + version = version >> 24; + if (version==1) fragment_duration = stream_read_qword(demuxer->stream); + else fragment_duration = stream_read_dword(demuxer->stream); // version==0 + mp_msg(MSGT_DEMUX, MSGL_V,"MOV:\tMovie Extends Header Box 'mehd' (%d bytes): v=%d flags=%d\n", + (int)len, (int)version,(int)flags); + mp_msg(MSGT_DEMUX, MSGL_V,"MOV:\t\tfragment_duration=%d\n",fragment_duration); + }break; + case MOV_FOURCC('t','r','e','x'):{ + int version = stream_read_dword(demuxer->stream); + int flags = version & 0x0ffffff; + mp_msg(MSGT_DEMUX, MSGL_V,"MOV:\tTrack Extends Box 'trex' (%d bytes): v=%d flags=%d\n", + (int)len, (int)version,(int)flags); + + if( priv->mvex_counter>=MOV_MAX_TRACKS ) continue; + priv->mvexs[priv->mvex_counter].track_ID = stream_read_dword(demuxer->stream); + priv->mvexs[priv->mvex_counter].sample_description_index = stream_read_dword(demuxer->stream); + priv->mvexs[priv->mvex_counter].default_sample_duration = stream_read_dword(demuxer->stream); + priv->mvexs[priv->mvex_counter].default_sample_size = stream_read_dword(demuxer->stream); + priv->mvexs[priv->mvex_counter].default_sample_flags = stream_read_dword(demuxer->stream); + priv->mvex_counter++; + + mp_msg(MSGT_DEMUX, MSGL_DBG3,"MOV:\t\tTrack_ID=%d\n",priv->mvexs[priv->mvex_counter].track_ID); + mp_msg(MSGT_DEMUX, MSGL_DBG3,"MOV:\t\tdefault_sample_description_index=%d\n", + priv->mvexs[priv->mvex_counter].sample_description_index); + mp_msg(MSGT_DEMUX, MSGL_DBG3,"MOV:\t\tdefault_sample_duration=%d\n", + priv->mvexs[priv->mvex_counter].default_sample_duration); + mp_msg(MSGT_DEMUX, MSGL_DBG3,"MOV:\t\tdefault_sample_size=%d\n", + priv->mvexs[priv->mvex_counter].default_sample_size); + mp_msg(MSGT_DEMUX, MSGL_DBG3,"MOV:\t\tdefault_sample_flags=%d\n", + priv->mvexs[priv->mvex_counter].default_sample_flags); + } break; + default: + id = be2me_32(id); + mp_msg(MSGT_DEMUX,MSGL_V,"MOV: unknown chunk: %.4s %d\n",(char *)&id,(int)len); + } + } +} + unsigned int store_ughvlc(unsigned char *s, unsigned int v){ unsigned int n = 0; @@ -1100,8 +1715,30 @@ // printf("pos=%d max=%d\n",pos,trak->stdata_len); } } - sh->fps=trak->timescale/ - ((trak->durmap_size>=1)?(float)trak->durmap[0].dur:1); + if(trak->durmap_size>=1){ + int i,totsmpls=0; + double mean=0.0; + //mp_msg(MSGT_DEMUX, MSGL_DBG2,"timescale=%d durmap_siz=%d\ndurmap=\n", + // trak->timescale,trak->durmap_size); + + for(i=0;idurmap_size;totsmpls+=trak->durmap[i++].num) { + mean+=trak->durmap[i].num*trak->durmap[i].dur; + //mp_msg(MSGT_DEMUX,MSGL_DBG2,"{%d,%d},", + // trak->durmap[i].num,trak->durmap[i].dur); + } + mean/=totsmpls; + //mp_msg(MSGT_DEMUX,MSGL_DBG2,"\n"); + + /*be conservative w/this adjustment for the time being. decreasing + the framerate and encoding w/o softskip is bound to cause decode + errors and possibly cause the output framerate to be suboptimal. + really this should be marked as variable framerate in the case + of durmap_size>1*/ + if(mean>(double)trak->durmap[0].dur) mean= trak->durmap[0].dur; + sh->fps=trak->timescale/mean; + }else{ + sh->fps=trak->timescale; + } sh->frametime=1.0f/sh->fps; sh->disp_w=trak->stdata[25]|(trak->stdata[24]<<8); @@ -1277,6 +1914,10 @@ return; } else { /* not in track */ switch(id) { + case MOV_FOURCC('m','v','e','x'): { + mp_msg(MSGT_DEMUX, MSGL_V,"MOV:Movie Extends Box (%d bytes)\n",(int)len); + lschunks_inmvex(demuxer, 0, pos+len, NULL); + } break; case MOV_FOURCC('m','v','h','d'): { int version = stream_read_char(demuxer->stream); stream_skip(demuxer->stream, (version == 1) ? 19 : 11); @@ -1607,6 +2248,7 @@ trak->tkdata_len = len; trak->tkdata = malloc(trak->tkdata_len); stream_read(demuxer->stream, trak->tkdata, trak->tkdata_len); + trak->track_ID = char2int(trak->tkdata, 12); /* 0 1 Version 1 3 Flags @@ -1906,6 +2548,7 @@ static demuxer_t* mov_read_header(demuxer_t* demuxer){ mov_priv_t* priv=demuxer->priv; + int i,j; int t_no; int best_a_id=-1, best_a_len=0; int best_v_id=-1, best_v_len=0; @@ -1921,6 +2564,44 @@ return 0; } lschunks(demuxer, 0, priv->moov_end, NULL); + + // Parse moofs: + for(i=0;imoof_counter;i++) { + stream_reset(demuxer->stream); + if(!stream_seek(demuxer->stream,priv->moof_start[i])) { + mp_msg(MSGT_DEMUX,MSGL_ERR,"MOV: Cannot seek to the beginning of the moff (0x%"PRIx64")\n", + (int64_t)priv->moof_start[i]); + return 0; + } + mp_msg(MSGT_DEMUX,MSGL_V,"\nParsing moof #%d\n",i); + lschunks_inmoof(demuxer, 0, priv->moof_end[i], NULL); + } + + // Parse mfra + stream_reset(demuxer->stream); + if( !stream_seek(demuxer->stream, priv->mfra_start ) ) { + mp_msg(MSGT_DEMUX,MSGL_ERR,"MOV: Cannot seek to the beginning of the mfra (0x%"PRIx64")\n", + (int64_t)priv->mfra_start); + return 0; + } else { + //mp_msg(MSGT_DEMUX,MSGL_V,"\nParsing mfra\n"); + lschunks_inmfra(demuxer, 0, priv->mfra_end, NULL); + } + + // Attach trafs + for(i=0;itrack_db;i++){ + mov_track_t* trak = priv->tracks[i]; + mov_traf_t *traf; + for(j=0;jtraf_counter;j++){ + traf = priv->trafs[j]; + if((trak->track_ID) == traf->track_ID) { + add_traf_to_track(trak,priv->timescale,traf); + mp_msg(MSGT_DEMUX,MSGL_V,"Added traf #%d to track #%d\n",j,trak->track_ID); + } + } + mov_rebuild_index(trak,priv->timescale); + } + // just in case we have hit eof while parsing... demuxer->stream->eof = 0; // mp_msg(MSGT_DEMUX, MSGL_INFO, "--------------\n"); @@ -2295,9 +2976,22 @@ case DEMUXER_CTRL_GET_PERCENT_POS: { off_t pos = track->pos; - if (track->durmap_size >= 1) - pos *= track->durmap[0].dur; - *((int *)arg) = (int)(100 * pos / track->length); + int dur_count = 0; + int sample_count = 0; + int i; + if (track->durmap_size == 1) + pos *= track->durmap[0].dur; + else for( i=0; idurmap_size; i++ ) { + if( pos>sample_count+track->durmap[i].num ) { + dur_count += track->durmap[i].num*track->durmap[i].dur; + sample_count += track->durmap[i].num; + } else { + dur_count += (((int)pos)-sample_count)*track->durmap[i].dur; + pos = dur_count; + break; + } + } + *((int *)arg) = (int)(100 *pos / track->length); return DEMUXER_CTRL_OK; } }