X-Git-Url: https://git.sesse.net/?a=blobdiff_plain;f=modules%2Fdemux%2Fmp4%2Fmp4.c;h=e791711c3a01caabf896170324586786ba97f2f3;hb=ab94829b29b7c817bec5ff29190e6dfccff73824;hp=846fa156d2ac06324104b6c70bf34c3a1b782ae2;hpb=22bf38073706ebe3a1f9633aba135a646a7e400a;p=vlc diff --git a/modules/demux/mp4/mp4.c b/modules/demux/mp4/mp4.c index 846fa156d2..e791711c3a 100644 --- a/modules/demux/mp4/mp4.c +++ b/modules/demux/mp4/mp4.c @@ -1,8 +1,8 @@ /***************************************************************************** * mp4.c : MP4 file input module for vlc ***************************************************************************** - * Copyright (C) 2001 VideoLAN - * $Id: mp4.c,v 1.51 2004/01/13 01:44:49 fenrir Exp $ + * Copyright (C) 2001-2004 VideoLAN + * $Id$ * Authors: Laurent Aimar * * This program is free software; you can redistribute it and/or modify @@ -28,6 +28,8 @@ #include #include #include +#include "iso_lang.h" +#include "vlc_meta.h" #include "libmp4.h" #include "mp4.h" @@ -40,7 +42,7 @@ static int Open ( vlc_object_t * ); static void Close( vlc_object_t * ); vlc_module_begin(); - set_description( _("MP4 demuxer") ); + set_description( _("MP4 stream demuxer") ); set_capability( "demux", 242 ); set_callbacks( Open, Close ); vlc_module_end(); @@ -59,20 +61,75 @@ static int Control ( input_thread_t *, int, va_list ); /***************************************************************************** * Declaration of local function *****************************************************************************/ -static void MP4_TrackCreate ( input_thread_t *, track_data_mp4_t *, MP4_Box_t *); -static void MP4_TrackDestroy( input_thread_t *, track_data_mp4_t * ); +static void MP4_TrackCreate ( input_thread_t *, mp4_track_t *, MP4_Box_t *); +static void MP4_TrackDestroy( input_thread_t *, mp4_track_t * ); -static int MP4_TrackSelect ( input_thread_t *, track_data_mp4_t *, mtime_t ); -static void MP4_TrackUnselect(input_thread_t *, track_data_mp4_t * ); +static int MP4_TrackSelect ( input_thread_t *, mp4_track_t *, mtime_t ); +static void MP4_TrackUnselect(input_thread_t *, mp4_track_t * ); -static int MP4_TrackSeek ( input_thread_t *, track_data_mp4_t *, mtime_t ); +static int MP4_TrackSeek ( input_thread_t *, mp4_track_t *, mtime_t ); -static uint64_t MP4_GetTrackPos ( track_data_mp4_t * ); -static int MP4_TrackSampleSize( track_data_mp4_t * ); -static int MP4_TrackNextSample( input_thread_t *, track_data_mp4_t * ); +static uint64_t MP4_TrackGetPos ( mp4_track_t * ); +static int MP4_TrackSampleSize( mp4_track_t * ); +static int MP4_TrackNextSample( input_thread_t *, mp4_track_t * ); +static void MP4_TrackSetELST( input_thread_t *, mp4_track_t *, int64_t ); -#define FREE( p ) \ - if( p ) { free( p ); (p) = NULL;} +/* Return time in µs of a track */ +static inline int64_t MP4_TrackGetPTS( input_thread_t *p_input, mp4_track_t *p_track ) +{ + unsigned int i_sample; + unsigned int i_index; + int64_t i_dts; + + i_sample = p_track->i_sample - p_track->chunk[p_track->i_chunk].i_sample_first; + i_dts = p_track->chunk[p_track->i_chunk].i_first_dts; + i_index = 0; + while( i_sample > 0 ) + { + if( i_sample > p_track->chunk[p_track->i_chunk].p_sample_count_dts[i_index] ) + { + i_dts += p_track->chunk[p_track->i_chunk].p_sample_count_dts[i_index] * + p_track->chunk[p_track->i_chunk].p_sample_delta_dts[i_index]; + i_sample -= p_track->chunk[p_track->i_chunk].p_sample_count_dts[i_index]; + i_index++; + } + else + { + i_dts += i_sample * + p_track->chunk[p_track->i_chunk].p_sample_delta_dts[i_index]; + i_sample = 0; + break; + } + } + + /* now handle elst */ + if( p_track->p_elst ) + { + demux_sys_t *p_sys = p_input->p_demux_data; + MP4_Box_data_elst_t *elst = p_track->p_elst->data.p_elst; + + /* convert to offset */ + if( ( elst->i_media_rate_integer[p_track->i_elst] > 0 || + elst->i_media_rate_fraction[p_track->i_elst] > 0 ) && + elst->i_media_time[p_track->i_elst] > 0 ) + { + i_dts -= elst->i_media_time[p_track->i_elst]; + } + + /* add i_elst_time */ + i_dts += p_track->i_elst_time * p_track->i_timescale / p_sys->i_timescale; + + if( i_dts < 0 ) i_dts = 0; + } + + return (int64_t)1000000 * i_dts / p_track->i_timescale; +} +static inline int64_t MP4_GetMoviePTS(demux_sys_t *p_sys ) +{ + return (int64_t)1000000 * p_sys->i_time / p_sys->i_timescale; +} + +#define FREE( p ) if( p ) { free( p ); (p) = NULL;} /***************************************************************************** * Open: check file and initializes MP4 structures @@ -109,6 +166,7 @@ static int Open( vlc_object_t * p_this ) case FOURCC_free: case FOURCC_skip: case FOURCC_wide: + case VLC_FOURCC( 'p', 'n', 'o', 't' ): break; default: msg_Warn( p_input, "MP4 plugin discarded (not a valid file)" ); @@ -244,7 +302,8 @@ static int Open( vlc_object_t * p_this ) } else { - msg_Err( p_input, "unknown ref type=%4.4s FIXME (send a bug report)", (char*)&p_rdrf->data.p_rdrf->i_ref_type ); + msg_Err( p_input, "unknown ref type=%4.4s FIXME (send a bug report)", + (char*)&p_rdrf->data.p_rdrf->i_ref_type ); } } vlc_object_release( p_playlist ); @@ -306,8 +365,8 @@ static int Open( vlc_object_t * p_this ) /* allocate memory */ - p_sys->track = calloc( p_sys->i_tracks, sizeof( track_data_mp4_t ) ); - memset( p_sys->track, 0, p_sys->i_tracks * sizeof( track_data_mp4_t ) ); + p_sys->track = calloc( p_sys->i_tracks, sizeof( mp4_track_t ) ); + memset( p_sys->track, 0, p_sys->i_tracks * sizeof( mp4_track_t ) ); /* now process each track and extract all usefull informations */ for( i = 0; i < p_sys->i_tracks; i++ ) @@ -371,38 +430,38 @@ static int Demux( input_thread_t *p_input ) /* check for newly selected/unselected track */ for( i_track = 0, i_track_selected = 0; i_track < p_sys->i_tracks; i_track++ ) { -#define track p_sys->track[i_track] - if( track.b_selected && track.i_sample >= track.i_sample_count ) + mp4_track_t *tk = &p_sys->track[i_track]; + + if( tk->b_selected && tk->i_sample >= tk->i_sample_count ) { - msg_Warn( p_input, "track[0x%x] will be disabled", track.i_track_ID ); - MP4_TrackUnselect( p_input, &track ); + msg_Warn( p_input, "track[0x%x] will be disabled", tk->i_track_ID ); + MP4_TrackUnselect( p_input, tk); } - else if( track.b_ok ) + else if( tk->b_ok ) { vlc_bool_t b; - es_out_Control( p_input->p_es_out, ES_OUT_GET_ES_STATE, track.p_es, &b ); + es_out_Control( p_input->p_es_out, ES_OUT_GET_ES_STATE, tk->p_es, &b ); - if( track.b_selected && !b ) + if( tk->b_selected && !b ) { - MP4_TrackUnselect( p_input, &track ); + MP4_TrackUnselect( p_input, tk ); } - else if( !track.b_selected && b) + else if( !tk->b_selected && b) { - MP4_TrackSelect( p_input, &track, MP4_GetMoviePTS( p_sys ) ); + MP4_TrackSelect( p_input, tk, MP4_GetMoviePTS( p_sys ) ); } - if( track.b_selected ) + if( tk->b_selected ) { i_track_selected++; } } -#undef track } if( i_track_selected <= 0 ) { msg_Warn( p_input, "no track selected, exiting..." ); - return( 0 ); + return 0; } /* first wait for the good time to read a packet */ @@ -421,86 +480,71 @@ static int Demux( input_thread_t *p_input ) for( i_track = 0; i_track < p_sys->i_tracks; i_track++ ) { -#define track p_sys->track[i_track] - if( !track.b_ok || - !track.b_selected || - MP4_GetTrackPTS( &track ) >= MP4_GetMoviePTS( p_sys ) ) + mp4_track_t *tk = &p_sys->track[i_track]; + + if( !tk->b_ok || !tk->b_selected ) { continue; } - while( MP4_GetTrackPTS( &track ) < MP4_GetMoviePTS( p_sys ) ) + while( MP4_TrackGetPTS( p_input, tk ) < MP4_GetMoviePTS( p_sys ) ) { +#if 0 + msg_Dbg( p_input, "tk=%lld mv=%lld", + MP4_TrackGetPTS( p_input, tk ), + MP4_GetMoviePTS( p_sys ) ); +#endif - if( ( !b_play_audio && track.fmt.i_cat == AUDIO_ES ) || - MP4_TrackSampleSize( &track ) <= 0 ) - { - if( MP4_TrackNextSample( p_input, &track ) ) - { - break; - } - } - else + if( MP4_TrackSampleSize( tk ) > 0 && + ( b_play_audio || tk->fmt.i_cat != AUDIO_ES ) ) { block_t *p_block; /* go,go go ! */ - if( stream_Seek( p_input->s, MP4_GetTrackPos( &track ) ) ) + if( stream_Seek( p_input->s, MP4_TrackGetPos( tk ) ) ) { - msg_Warn( p_input, "track[0x%x] will be disabled (eof?)", track.i_track_ID ); - MP4_TrackUnselect( p_input, &track ); + msg_Warn( p_input, "track[0x%x] will be disabled (eof?)", tk->i_track_ID ); + MP4_TrackUnselect( p_input, tk ); break; } /* now read pes */ if( ( p_block = stream_Block( p_input->s, - MP4_TrackSampleSize( &track ) ) ) == NULL ) + MP4_TrackSampleSize( tk ) ) ) == NULL ) { - msg_Warn( p_input, "track[0x%x] will be disabled (eof?)", track.i_track_ID ); - MP4_TrackUnselect( p_input, &track ); + msg_Warn( p_input, "track[0x%x] will be disabled (eof?)", tk->i_track_ID ); + MP4_TrackUnselect( p_input, tk ); break; } - if( track.b_drms && track.p_drms ) + if( tk->b_drms && tk->p_drms ) { - drms_decrypt( track.p_drms, - (uint32_t *)p_block->p_buffer, + drms_decrypt( tk->p_drms, + (uint32_t*)p_block->p_buffer, p_block->i_buffer ); } + p_block->i_dts = + input_ClockGetTS( p_input, + p_input->stream.p_selected_program, + MP4_TrackGetPTS( p_input, tk ) * 9/100 ); - if( track.fmt.i_cat == VIDEO_ES ) - { - /* FIXME sometime we can calculate PTS */ - p_block->i_pts = 0; - p_block->i_dts = - input_ClockGetTS( p_input, - p_input->stream.p_selected_program, - MP4_GetTrackPTS( &track ) * 9/100 ); - } - else - { - p_block->i_pts = - p_block->i_dts = - input_ClockGetTS( p_input, - p_input->stream.p_selected_program, - MP4_GetTrackPTS( &track ) * 9/100 ); - } + p_block->i_pts = tk->fmt.i_cat == VIDEO_ES ? 0 : p_block->i_dts; - if( !track.b_drms || ( track.b_drms && track.p_drms ) ) + if( !tk->b_drms || ( tk->b_drms && tk->p_drms ) ) { - es_out_Send( p_input->p_es_out, track.p_es, p_block ); + es_out_Send( p_input->p_es_out, tk->p_es, p_block ); } + } - if( MP4_TrackNextSample( p_input, &track ) ) - { - break; - } + /* Next sample */ + if( MP4_TrackNextSample( p_input, tk ) ) + { + break; } } -#undef track } - return( 1 ); + return 1; } /***************************************************************************** * Seek: Got to i_date @@ -509,6 +553,7 @@ static int Seek ( input_thread_t *p_input, mtime_t i_date ) { demux_sys_t *p_sys = p_input->p_demux_data; unsigned int i_track; + /* First update update global time */ p_sys->i_time = i_date * p_sys->i_timescale / 1000000; p_sys->i_pcr = i_date* 9 / 100; @@ -516,12 +561,12 @@ static int Seek ( input_thread_t *p_input, mtime_t i_date ) /* Now for each stream try to go to this time */ for( i_track = 0; i_track < p_sys->i_tracks; i_track++ ) { -#define track p_sys->track[i_track] - if( track.b_ok && track.b_selected ) + mp4_track_t *tk = &p_sys->track[i_track]; + + if( tk->b_ok && tk->b_selected ) { - MP4_TrackSeek( p_input, &track, i_date ); + MP4_TrackSeek( p_input, tk, i_date ); } -#undef track } return( 1 ); } @@ -529,11 +574,11 @@ static int Seek ( input_thread_t *p_input, mtime_t i_date ) /***************************************************************************** * Control: *****************************************************************************/ -static int Control ( input_thread_t *p_input, int i_query, va_list args ) +static int Control( input_thread_t *p_input, int i_query, va_list args ) { demux_sys_t *p_sys = p_input->p_demux_data; - double f, *pf; + double f, *pf; int64_t i64, *pi64; switch( i_query ) @@ -552,17 +597,24 @@ static int Control ( input_thread_t *p_input, int i_query, va_list args ) case DEMUX_SET_POSITION: f = (double)va_arg( args, double ); - i64 = (int64_t)( f * - (double)1000000 * - (double)p_sys->i_duration / - (double)p_sys->i_timescale ); - return Seek( p_input, i64 ); + if( p_sys->i_timescale > 0 ) + { + i64 = (int64_t)( f * (double)1000000 * + (double)p_sys->i_duration / + (double)p_sys->i_timescale ); + return Seek( p_input, i64 ); + } + else return VLC_SUCCESS; case DEMUX_GET_TIME: pi64 = (int64_t*)va_arg( args, int64_t * ); - *pi64 = (mtime_t)1000000 * - (mtime_t)p_sys->i_time / - (mtime_t)p_sys->i_timescale; + if( p_sys->i_timescale > 0 ) + { + *pi64 = (mtime_t)1000000 * + (mtime_t)p_sys->i_time / + (mtime_t)p_sys->i_timescale; + } + else *pi64 = 0; return VLC_SUCCESS; case DEMUX_SET_TIME: @@ -571,13 +623,79 @@ static int Control ( input_thread_t *p_input, int i_query, va_list args ) case DEMUX_GET_LENGTH: pi64 = (int64_t*)va_arg( args, int64_t * ); - *pi64 = (mtime_t)1000000 * - (mtime_t)p_sys->i_duration / - (mtime_t)p_sys->i_timescale; + if( p_sys->i_timescale > 0 ) + { + *pi64 = (mtime_t)1000000 * + (mtime_t)p_sys->i_duration / + (mtime_t)p_sys->i_timescale; + } + else *pi64 = 0; return VLC_SUCCESS; + case DEMUX_GET_FPS: msg_Warn( p_input, "DEMUX_GET_FPS unimplemented !!" ); return VLC_EGENERIC; + + case DEMUX_GET_META: + { + vlc_meta_t **pp_meta = (vlc_meta_t**)va_arg( args, vlc_meta_t** ); + vlc_meta_t *meta; + MP4_Box_t *p_udta = MP4_BoxGet( p_sys->p_root, "/moov/udta" ); + MP4_Box_t *p_0xa9xxx; + if( p_udta == NULL ) + { + return VLC_EGENERIC; + } + *pp_meta = meta = vlc_meta_New(); + for( p_0xa9xxx = p_udta->p_first; p_0xa9xxx != NULL; + p_0xa9xxx = p_0xa9xxx->p_next ) + { + switch( p_0xa9xxx->i_type ) + { + case FOURCC_0xa9nam: + vlc_meta_Add( meta, VLC_META_TITLE, + p_0xa9xxx->data.p_0xa9xxx->psz_text ); + break; + case FOURCC_0xa9aut: + vlc_meta_Add( meta, VLC_META_AUTHOR, + p_0xa9xxx->data.p_0xa9xxx->psz_text ); + break; + case FOURCC_0xa9ART: + vlc_meta_Add( meta, VLC_META_ARTIST, + p_0xa9xxx->data.p_0xa9xxx->psz_text ); + break; + case FOURCC_0xa9cpy: + vlc_meta_Add( meta, VLC_META_COPYRIGHT, + p_0xa9xxx->data.p_0xa9xxx->psz_text ); + break; + case FOURCC_0xa9day: + vlc_meta_Add( meta, VLC_META_DATE, + p_0xa9xxx->data.p_0xa9xxx->psz_text ); + break; + case FOURCC_0xa9des: + vlc_meta_Add( meta, VLC_META_DESCRIPTION, + p_0xa9xxx->data.p_0xa9xxx->psz_text ); + break; + + case FOURCC_0xa9swr: + case FOURCC_0xa9inf: + case FOURCC_0xa9dir: + case FOURCC_0xa9cmt: + case FOURCC_0xa9req: + case FOURCC_0xa9fmt: + case FOURCC_0xa9prd: + case FOURCC_0xa9prf: + case FOURCC_0xa9src: + /* TODO one day, but they aren't really meaningfull */ + break; + + default: + break; + } + } + return VLC_SUCCESS; + } + default: msg_Err( p_input, "control query unimplemented !!!" ); return demux_vaControlDefault( p_input, i_query, args ); @@ -613,7 +731,7 @@ static void Close ( vlc_object_t * p_this ) /* now create basic chunk data, the rest will be filled by MP4_CreateSamplesIndex */ static int TrackCreateChunksIndex( input_thread_t *p_input, - track_data_mp4_t *p_demux_track ) + mp4_track_t *p_demux_track ) { MP4_Box_t *p_co64; /* give offset for each chunk, same for stco and co64 */ MP4_Box_t *p_stsc; @@ -635,7 +753,7 @@ static int TrackCreateChunksIndex( input_thread_t *p_input, return( VLC_EGENERIC ); } p_demux_track->chunk = calloc( p_demux_track->i_chunk_count, - sizeof( chunk_data_mp4_t ) ); + sizeof( mp4_chunk_t ) ); /* first we read chunk offset */ for( i_chunk = 0; i_chunk < p_demux_track->i_chunk_count; i_chunk++ ) @@ -680,12 +798,12 @@ static int TrackCreateChunksIndex( input_thread_t *p_input, msg_Dbg( p_input, "track[Id 0x%x] read %d chunk", p_demux_track->i_track_ID, - p_demux_track->i_chunk_count ); + p_demux_track->i_chunk_count ); return( VLC_SUCCESS ); } static int TrackCreateSamplesIndex( input_thread_t *p_input, - track_data_mp4_t *p_demux_track ) + mp4_track_t *p_demux_track ) { MP4_Box_t *p_stts; /* makes mapping between sample and decoding time, ctts make same mapping but for composition time, @@ -822,7 +940,7 @@ static int TrackCreateSamplesIndex( input_thread_t *p_input, * Create ES and PES to init decoder if needed, for a track starting at i_chunk */ static int TrackCreateES ( input_thread_t *p_input, - track_data_mp4_t *p_track, + mp4_track_t *p_track, unsigned int i_chunk, es_out_id_t **pp_es ) { @@ -1055,28 +1173,66 @@ static int TrackCreateES ( input_thread_t *p_input, return VLC_SUCCESS; } -/* given a time it return sample/chunk */ +/* given a time it return sample/chunk + * it also update elst field of the track + */ static int TrackTimeToSampleChunk( input_thread_t *p_input, - track_data_mp4_t *p_track, - uint64_t i_start, + mp4_track_t *p_track, + int64_t i_start, uint32_t *pi_chunk, uint32_t *pi_sample ) { - MP4_Box_t *p_stss; + demux_sys_t *p_sys = p_input->p_demux_data; + MP4_Box_t *p_stss; uint64_t i_dts; unsigned int i_sample; unsigned int i_chunk; int i_index; - /* convert absolute time to in timescale unit */ - i_start = i_start * (mtime_t)p_track->i_timescale / (mtime_t)1000000; - /* FIXME see if it's needed to check p_track->i_chunk_count */ if( !p_track->b_ok || p_track->i_chunk_count == 0 ) { return( VLC_EGENERIC ); } + /* handle elst (find the correct one) */ + MP4_TrackSetELST( p_input, p_track, i_start ); + if( p_track->p_elst && p_track->p_elst->data.p_elst->i_entry_count > 0 ) + { + MP4_Box_data_elst_t *elst = p_track->p_elst->data.p_elst; + int64_t i_mvt= i_start * p_sys->i_timescale / (int64_t)1000000; + + /* now calculate i_start for this elst */ + /* offset */ + i_start -= p_track->i_elst_time * (int64_t)1000000 / p_sys->i_timescale; + if( i_start < 0 ) + { + *pi_chunk = 0; + *pi_sample= 0; + + return VLC_SUCCESS; + } + /* to track time scale */ + i_start = i_start * p_track->i_timescale / (int64_t)1000000; + /* add elst offset */ + if( ( elst->i_media_rate_integer[p_track->i_elst] > 0 || + elst->i_media_rate_fraction[p_track->i_elst] > 0 ) && + elst->i_media_time[p_track->i_elst] > 0 ) + { + i_start += elst->i_media_time[p_track->i_elst]; + } + + msg_Dbg( p_input, "elst (%d) gives "I64Fd"ms (movie)-> "I64Fd"ms (track)", + p_track->i_elst, + i_mvt * 1000 / p_sys->i_timescale, + i_start * 1000 / p_track->i_timescale ); + } + else + { + /* convert absolute time to in timescale unit */ + i_start = i_start * p_track->i_timescale / (int64_t)1000000; + } + /* we start from sample 0/chunk 0, hope it won't take too much time */ /* *** find good chunk *** */ for( i_chunk = 0; ; i_chunk++ ) @@ -1181,13 +1337,14 @@ static int TrackTimeToSampleChunk( input_thread_t *p_input, p_track->i_track_ID ); } - if( pi_chunk ) *pi_chunk = i_chunk; - if( pi_sample ) *pi_sample = i_sample; - return( VLC_SUCCESS ); + *pi_chunk = i_chunk; + *pi_sample = i_sample; + + return VLC_SUCCESS; } static int TrackGotoChunkSample( input_thread_t *p_input, - track_data_mp4_t *p_track, + mp4_track_t *p_track, unsigned int i_chunk, unsigned int i_sample ) { @@ -1199,7 +1356,7 @@ static int TrackGotoChunkSample( input_thread_t *p_input, p_track->chunk[p_track->i_chunk].i_sample_description_index != p_track->chunk[i_chunk].i_sample_description_index ) { - msg_Warn( p_input, "Recreate ES" ); + msg_Warn( p_input, "recreate ES" ); es_out_Control( p_input->p_es_out, ES_OUT_GET_ES_STATE, p_track->p_es, &b_reselect ); @@ -1239,9 +1396,11 @@ static int TrackGotoChunkSample( input_thread_t *p_input, * If it succeed b_ok is set to 1 else to 0 ****************************************************************************/ static void MP4_TrackCreate( input_thread_t *p_input, - track_data_mp4_t *p_track, + mp4_track_t *p_track, MP4_Box_t * p_box_trak ) { + demux_sys_t *p_sys = p_input->p_demux_data; + MP4_Box_t *p_tkhd = MP4_BoxGet( p_box_trak, "tkhd" ); MP4_Box_t *p_tref = MP4_BoxGet( p_box_trak, "tref" ); MP4_Box_t *p_elst; @@ -1279,11 +1438,6 @@ static void MP4_TrackCreate( input_thread_t *p_input, p_track->i_width = p_tkhd->data.p_tkhd->i_width / 65536; p_track->i_height = p_tkhd->data.p_tkhd->i_height / 65536; - if( ( p_elst = MP4_BoxGet( p_box_trak, "edts/elst" ) ) ) - { -/* msg_Warn( p_input, "unhandled box: edts --> FIXME" ); */ - } - if( p_tref ) { /* msg_Warn( p_input, "unhandled box: tref --> FIXME" ); */ @@ -1326,6 +1480,28 @@ static void MP4_TrackCreate( input_thread_t *p_input, default: return; } + + p_track->i_elst = 0; + p_track->i_elst_time = 0; + if( ( p_track->p_elst = p_elst = MP4_BoxGet( p_box_trak, "edts/elst" ) ) ) + { + MP4_Box_data_elst_t *elst = p_elst->data.p_elst; + int i; + + msg_Warn( p_input, "elst box found" ); + for( i = 0; i < elst->i_entry_count; i++ ) + { + msg_Dbg( p_input, " - [%d] duration="I64Fd"ms media time="I64Fd"ms) rate=%d.%d", + i, + elst->i_segment_duration[i] * 1000 / p_sys->i_timescale, + elst->i_media_time[i] >= 0 ? + elst->i_media_time[i] * 1000 / p_track->i_timescale : -1, + elst->i_media_rate_integer[i], + elst->i_media_rate_fraction[i] ); + } + } + + /* TODO add support for: p_dinf = MP4_BoxGet( p_minf, "dinf" ); @@ -1412,7 +1588,7 @@ static void MP4_TrackCreate( input_thread_t *p_input, * Destroy a track created by MP4_TrackCreate. ****************************************************************************/ static void MP4_TrackDestroy( input_thread_t *p_input, - track_data_mp4_t *p_track ) + mp4_track_t *p_track ) { unsigned int i_chunk; @@ -1439,15 +1615,12 @@ static void MP4_TrackDestroy( input_thread_t *p_input, } static int MP4_TrackSelect ( input_thread_t *p_input, - track_data_mp4_t *p_track, + mp4_track_t *p_track, mtime_t i_start ) { - uint32_t i_chunk; - uint32_t i_sample; - if( !p_track->b_ok ) { - return( VLC_EGENERIC ); + return VLC_EGENERIC; } if( p_track->b_selected ) @@ -1455,30 +1628,14 @@ static int MP4_TrackSelect ( input_thread_t *p_input, msg_Warn( p_input, "track[Id 0x%x] already selected", p_track->i_track_ID ); - return( VLC_SUCCESS ); - } - - if( TrackTimeToSampleChunk( p_input, - p_track, i_start, - &i_chunk, &i_sample ) ) - { - msg_Warn( p_input, - "cannot select track[Id 0x%x]", - p_track->i_track_ID ); - return( VLC_EGENERIC ); + return VLC_SUCCESS; } - p_track->b_selected = VLC_TRUE; - - if( TrackGotoChunkSample( p_input, p_track, i_chunk, i_sample ) ) - { - p_track->b_selected = VLC_FALSE; - } - return p_track->b_selected ? VLC_SUCCESS : VLC_EGENERIC; + return MP4_TrackSeek( p_input, p_track, i_start ); } static void MP4_TrackUnselect(input_thread_t *p_input, - track_data_mp4_t *p_track ) + mp4_track_t *p_track ) { if( !p_track->b_ok ) { @@ -1501,7 +1658,7 @@ static void MP4_TrackUnselect(input_thread_t *p_input, } static int MP4_TrackSeek ( input_thread_t *p_input, - track_data_mp4_t *p_track, + mp4_track_t *p_track, mtime_t i_start ) { uint32_t i_chunk; @@ -1524,21 +1681,20 @@ static int MP4_TrackSeek ( input_thread_t *p_input, p_track->b_selected = VLC_TRUE; - TrackGotoChunkSample( p_input, p_track, i_chunk, i_sample ); - + if( TrackGotoChunkSample( p_input, p_track, i_chunk, i_sample ) ) + { + p_track->b_selected = VLC_FALSE; + } return( p_track->b_selected ? VLC_SUCCESS : VLC_EGENERIC ); } - - - /* * 3 types: for audio * */ #define QT_V0_MAX_SAMPLES 1500 -static int MP4_TrackSampleSize( track_data_mp4_t *p_track ) +static int MP4_TrackSampleSize( mp4_track_t *p_track ) { int i_size; MP4_Box_data_sample_soun_t *p_soun; @@ -1582,7 +1738,7 @@ static int MP4_TrackSampleSize( track_data_mp4_t *p_track ) } -static uint64_t MP4_GetTrackPos( track_data_mp4_t *p_track ) +static uint64_t MP4_TrackGetPos( mp4_track_t *p_track ) { unsigned int i_sample; uint64_t i_pos; @@ -1618,7 +1774,7 @@ static uint64_t MP4_GetTrackPos( track_data_mp4_t *p_track ) } static int MP4_TrackNextSample( input_thread_t *p_input, - track_data_mp4_t *p_track ) + mp4_track_t *p_track ) { if( p_track->fmt.i_cat == AUDIO_ES && @@ -1657,7 +1813,7 @@ static int MP4_TrackNextSample( input_thread_t *p_input, /* we have reach end of the track so free decoder stuff */ msg_Warn( p_input, "track[0x%x] will be disabled", p_track->i_track_ID ); MP4_TrackUnselect( p_input, p_track ); - return( VLC_EGENERIC ); + return VLC_EGENERIC; } /* Have we changed chunk ? */ @@ -1672,11 +1828,67 @@ static int MP4_TrackNextSample( input_thread_t *p_input, { msg_Warn( p_input, "track[0x%x] will be disabled (cannot restart decoder)", p_track->i_track_ID ); MP4_TrackUnselect( p_input, p_track ); - return( VLC_EGENERIC ); + return VLC_EGENERIC; } } - return( VLC_SUCCESS ); + /* Have we changed elst */ + if( p_track->p_elst && p_track->p_elst->data.p_elst->i_entry_count > 0 ) + { + demux_sys_t *p_sys = p_input->p_demux_data; + MP4_Box_data_elst_t *elst = p_track->p_elst->data.p_elst; + int64_t i_mvt = MP4_TrackGetPTS( p_input, p_track ) * p_sys->i_timescale / (int64_t)1000000; + + if( p_track->i_elst < elst->i_entry_count && + i_mvt >= p_track->i_elst_time + elst->i_segment_duration[p_track->i_elst] ) + { + MP4_TrackSetELST( p_input, p_track, MP4_TrackGetPTS( p_input, p_track ) ); + } + } + + return VLC_SUCCESS; } +static void MP4_TrackSetELST( input_thread_t *p_input, mp4_track_t *tk, int64_t i_time ) +{ + demux_sys_t *p_sys = p_input->p_demux_data; + int i_elst_last = tk->i_elst; + + /* handle elst (find the correct one) */ + tk->i_elst = 0; + tk->i_elst_time = 0; + if( tk->p_elst && tk->p_elst->data.p_elst->i_entry_count > 0 ) + { + MP4_Box_data_elst_t *elst = tk->p_elst->data.p_elst; + int64_t i_mvt= i_time * p_sys->i_timescale / (int64_t)1000000; + + for( tk->i_elst = 0; tk->i_elst < elst->i_entry_count; tk->i_elst++ ) + { + mtime_t i_dur = elst->i_segment_duration[tk->i_elst]; + + if( tk->i_elst_time <= i_mvt && i_mvt < tk->i_elst_time + i_dur ) + { + break; + } + tk->i_elst_time += i_dur; + } + + if( tk->i_elst >= elst->i_entry_count ) + { + /* msg_Dbg( p_input, "invalid number of entry in elst" ); */ + tk->i_elst = elst->i_entry_count - 1; + tk->i_elst_time -= elst->i_segment_duration[tk->i_elst]; + } + + if( elst->i_media_time[tk->i_elst] < 0 ) + { + /* track offset */ + tk->i_elst_time += elst->i_segment_duration[tk->i_elst]; + } + } + if( i_elst_last != tk->i_elst ) + { + msg_Warn( p_input, "elst old=%d new=%d", i_elst_last, tk->i_elst ); + } +}