X-Git-Url: https://git.sesse.net/?a=blobdiff_plain;f=modules%2Fdemux%2Fmp4%2Fmp4.c;h=c988aedb08f4918acd0c356070010f6927e45986;hb=a90a19a6b0468ea9fedadc27cfc1118d70295263;hp=be2b371c78e8bcbd3a58c288946f32a783d86f4b;hpb=614e91f2c0e18312086d310b4c1d89a4eca8dbc5;p=vlc diff --git a/modules/demux/mp4/mp4.c b/modules/demux/mp4/mp4.c index be2b371c78..c988aedb08 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.50 2004/01/09 04:37:43 jlj Exp $ + * Copyright (C) 2001-2004 VideoLAN + * $Id$ * Authors: Laurent Aimar * * This program is free software; you can redistribute it and/or modify @@ -28,11 +28,16 @@ #include #include #include +#include "iso_lang.h" +#include "vlc_meta.h" #include "libmp4.h" -#include "mp4.h" #include "drms.h" +#ifdef UNDER_CE +#define uint64_t int64_t +#endif + /***************************************************************************** * Module descriptor *****************************************************************************/ @@ -40,46 +45,187 @@ static int Open ( vlc_object_t * ); static void Close( vlc_object_t * ); vlc_module_begin(); - set_description( _("MP4 demuxer") ); - set_capability( "demux", 242 ); + set_category( CAT_INPUT ); + set_subcategory( SUBCAT_INPUT_DEMUX ); + set_description( _("MP4 stream demuxer") ); + set_capability( "demux2", 242 ); set_callbacks( Open, Close ); vlc_module_end(); /***************************************************************************** * Local prototypes *****************************************************************************/ -static int Demux ( input_thread_t * ); -static int DemuxRef( input_thread_t *p_input ) +static int Demux ( demux_t * ); +static int DemuxRef( demux_t *p_demux ){ return 0;} +static int Seek ( demux_t *, mtime_t ); +static int Control ( demux_t *, int, va_list ); + +/* Contain all information about a chunk */ +typedef struct { - return 0; -} -static int Seek ( input_thread_t *, mtime_t ); -static int Control ( input_thread_t *, int, va_list ); + uint64_t i_offset; /* absolute position of this chunk in the file */ + uint32_t i_sample_description_index; /* index for SampleEntry to use */ + uint32_t i_sample_count; /* how many samples in this chunk */ + uint32_t i_sample_first; /* index of the first sample in this chunk */ + + /* now provide way to calculate pts, dts, and offset without to + much memory and with fast acces */ + + /* with this we can calculate dts/pts without waste memory */ + uint64_t i_first_dts; + uint32_t *p_sample_count_dts; + uint32_t *p_sample_delta_dts; /* dts delta */ + + /* TODO if needed add pts + but quickly *add* support for edts and seeking */ + +} mp4_chunk_t; + + /* Contain all needed information for read all track with vlc */ +typedef struct +{ + int i_track_ID; /* this should be unique */ + + int b_ok; /* The track is usable */ + int b_enable; /* is the trak enable by default */ + vlc_bool_t b_selected; /* is the trak being played */ + + es_format_t fmt; + es_out_id_t *p_es; + + /* display size only ! */ + int i_width; + int i_height; + + /* more internal data */ + uint64_t i_timescale; /* time scale for this track only */ + + /* elst */ + int i_elst; /* current elst */ + int64_t i_elst_time; /* current elst start time (in movie time scale)*/ + MP4_Box_t *p_elst; /* elst (could be NULL) */ + + /* give the next sample to read, i_chunk is to find quickly where + the sample is located */ + uint32_t i_sample; /* next sample to read */ + uint32_t i_chunk; /* chunk where next sample is stored */ + /* total count of chunk and sample */ + uint32_t i_chunk_count; + uint32_t i_sample_count; + + mp4_chunk_t *chunk; /* always defined for each chunk */ + + /* sample size, p_sample_size defined only if i_sample_size == 0 + else i_sample_size is size for all sample */ + uint32_t i_sample_size; + uint32_t *p_sample_size; /* XXX perhaps add file offset if take + too much time to do sumations each time*/ + + MP4_Box_t *p_stbl; /* will contain all timing information */ + MP4_Box_t *p_stsd; /* will contain all data to initialize decoder */ + MP4_Box_t *p_sample;/* point on actual sdsd */ + + vlc_bool_t b_drms; + void *p_drms; + +} mp4_track_t; + + +struct demux_sys_t +{ + MP4_Box_t *p_root; /* container for the whole file */ + + mtime_t i_pcr; + + uint64_t i_time; /* time position of the presentation + * in movie timescale */ + uint64_t i_timescale; /* movie time scale */ + uint64_t i_duration; /* movie duration */ + unsigned int i_tracks; /* number of tracks */ + mp4_track_t *track; /* array of track */ +}; /***************************************************************************** * 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 ( demux_t *, mp4_track_t *, MP4_Box_t *); +static void MP4_TrackDestroy( demux_t *, mp4_track_t * ); + +static int MP4_TrackSelect ( demux_t *, mp4_track_t *, mtime_t ); +static void MP4_TrackUnselect(demux_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_TrackSeek ( demux_t *, mp4_track_t *, mtime_t ); -static int MP4_TrackSeek ( input_thread_t *, track_data_mp4_t *, mtime_t ); +static uint64_t MP4_TrackGetPos ( mp4_track_t * ); +static int MP4_TrackSampleSize( mp4_track_t * ); +static int MP4_TrackNextSample( demux_t *, mp4_track_t * ); +static void MP4_TrackSetELST( demux_t *, mp4_track_t *, int64_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 * ); +/* Return time in µs of a track */ +static inline int64_t MP4_TrackGetPTS( demux_t *p_demux, mp4_track_t *p_track ) +{ +#define chunk p_track->chunk[p_track->i_chunk] -#define FREE( p ) \ - if( p ) { free( p ); (p) = NULL;} + unsigned int i_index = 0; + unsigned int i_sample = p_track->i_sample - chunk.i_sample_first; + int64_t i_dts = chunk.i_first_dts; + + while( i_sample > 0 ) + { + if( i_sample > chunk.p_sample_count_dts[i_index] ) + { + i_dts += chunk.p_sample_count_dts[i_index] * + chunk.p_sample_delta_dts[i_index]; + i_sample -= chunk.p_sample_count_dts[i_index]; + i_index++; + } + else + { + i_dts += i_sample * chunk.p_sample_delta_dts[i_index]; + i_sample = 0; + break; + } + } + +#undef chunk + + /* now handle elst */ + if( p_track->p_elst ) + { + demux_sys_t *p_sys = p_demux->p_sys; + 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 I64C(1000000) * i_dts / p_track->i_timescale; +} + +static inline int64_t MP4_GetMoviePTS(demux_sys_t *p_sys ) +{ + return I64C(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 *****************************************************************************/ static int Open( vlc_object_t * p_this ) { - input_thread_t *p_input = (input_thread_t *)p_this; + demux_t *p_demux = (demux_t *)p_this; demux_sys_t *p_sys; uint8_t *p_peek; @@ -93,9 +239,9 @@ static int Open( vlc_object_t * p_this ) vlc_bool_t b_seekable; /* a little test to see if it could be a mp4 */ - if( stream_Peek( p_input->s, &p_peek, 8 ) < 8 ) + if( stream_Peek( p_demux->s, &p_peek, 8 ) < 8 ) { - msg_Warn( p_input, "MP4 plugin discarded (cannot peek)" ); + msg_Warn( p_demux, "MP4 plugin discarded (cannot peek)" ); return VLC_EGENERIC; } switch( VLC_FOURCC( p_peek[4], p_peek[5], p_peek[6], p_peek[7] ) ) @@ -109,48 +255,49 @@ 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)" ); + msg_Warn( p_demux, "MP4 plugin discarded (not a valid file)" ); return VLC_EGENERIC; } /* I need to seek */ - stream_Control( p_input->s, STREAM_CAN_SEEK, &b_seekable ); + stream_Control( p_demux->s, STREAM_CAN_SEEK, &b_seekable ); if( !b_seekable ) { - msg_Warn( p_input, "MP4 plugin discarded (unseekable)" ); + msg_Warn( p_demux, "MP4 plugin discarded (unseekable)" ); return VLC_EGENERIC; } /*Set exported functions */ - p_input->pf_demux = Demux; - p_input->pf_demux_control = Control; + p_demux->pf_demux = Demux; + p_demux->pf_control = Control; /* create our structure that will contains all data */ - p_input->p_demux_data = p_sys = malloc( sizeof( demux_sys_t ) ); + p_demux->p_sys = p_sys = malloc( sizeof( demux_sys_t ) ); memset( p_sys, 0, sizeof( demux_sys_t ) ); /* Now load all boxes ( except raw data ) */ - if( ( p_sys->p_root = MP4_BoxGetRoot( p_input ) ) == NULL ) + if( ( p_sys->p_root = MP4_BoxGetRoot( p_demux->s ) ) == NULL ) { - msg_Warn( p_input, "MP4 plugin discarded (not a valid file)" ); + msg_Warn( p_demux, "MP4 plugin discarded (not a valid file)" ); goto error; } - MP4_BoxDumpStructure( p_input, p_sys->p_root ); + MP4_BoxDumpStructure( p_demux->s, p_sys->p_root ); if( ( p_ftyp = MP4_BoxGet( p_sys->p_root, "/ftyp" ) ) ) { switch( p_ftyp->data.p_ftyp->i_major_brand ) { case( FOURCC_isom ): - msg_Dbg( p_input, + msg_Dbg( p_demux, "ISO Media file (isom) version %d.", p_ftyp->data.p_ftyp->i_minor_version ); break; default: - msg_Dbg( p_input, + msg_Dbg( p_demux, "unrecognized major file specification (%4.4s).", (char*)&p_ftyp->data.p_ftyp->i_major_brand ); break; @@ -158,7 +305,7 @@ static int Open( vlc_object_t * p_this ) } else { - msg_Dbg( p_input, "file type box missing (assuming ISO Media file)" ); + msg_Dbg( p_demux, "file type box missing (assuming ISO Media file)" ); } /* the file need to have one moov box */ @@ -168,7 +315,7 @@ static int Open( vlc_object_t * p_this ) if( !p_foov ) { - msg_Err( p_input, "MP4 plugin discarded (no moov box)" ); + msg_Err( p_demux, "MP4 plugin discarded (no moov box)" ); goto error; } /* we have a free box as a moov, rename it */ @@ -178,18 +325,23 @@ static int Open( vlc_object_t * p_this ) if( ( p_rmra = MP4_BoxGet( p_sys->p_root, "/moov/rmra" ) ) ) { playlist_t *p_playlist; + playlist_item_t *p_item; int i_count = MP4_BoxCount( p_rmra, "rmda" ); int i; + vlc_bool_t b_play = VLC_FALSE; - msg_Dbg( p_input, "detected playlist mov file (%d ref)", i_count ); + msg_Dbg( p_demux, "detected playlist mov file (%d ref)", i_count ); p_playlist = - (playlist_t *)vlc_object_find( p_input, + (playlist_t *)vlc_object_find( p_demux, VLC_OBJECT_PLAYLIST, FIND_ANYWHERE ); if( p_playlist ) { - //p_playlist->pp_items[p_playlist->i_index]->b_autodeletion = VLC_TRUE; + p_item = playlist_ItemGetByInput( p_playlist, + ((input_thread_t *)p_demux->p_parent)->input.p_item ); + playlist_ItemToNode( p_playlist, p_item ); + for( i = 0; i < i_count; i++ ) { MP4_Box_t *p_rdrf = MP4_BoxGet( p_rmra, "rmda[%d]/rdrf", i ); @@ -202,56 +354,91 @@ static int Open( vlc_object_t * p_this ) } i_ref_type = p_rdrf->data.p_rdrf->i_ref_type; - msg_Dbg( p_input, "new ref=`%s' type=%4.4s", + msg_Dbg( p_demux, "new ref=`%s' type=%4.4s", psz_ref, (char*)&i_ref_type ); if( i_ref_type == VLC_FOURCC( 'u', 'r', 'l', ' ' ) ) { if( strstr( psz_ref, "qt5gateQT" ) ) { - msg_Dbg( p_input, "ignoring pseudo ref =`%s'", psz_ref ); + msg_Dbg( p_demux, "ignoring pseudo ref =`%s'", psz_ref ); continue; } if( !strncmp( psz_ref, "http://", 7 ) || !strncmp( psz_ref, "rtsp://", 7 ) ) { - msg_Dbg( p_input, "adding ref = `%s'", psz_ref ); - playlist_Add( p_playlist, psz_ref, psz_ref, - PLAYLIST_APPEND, PLAYLIST_END ); + msg_Dbg( p_demux, "adding ref = `%s'", psz_ref ); + if( p_item ) + { + playlist_item_t *p_child = + playlist_ItemNew( p_playlist, + psz_ref, psz_ref ); + if( p_child ) + { + playlist_NodeAddItem( p_playlist, p_child, + p_item->pp_parents[0]->i_view, + p_item, PLAYLIST_APPEND, + PLAYLIST_END ); + playlist_CopyParents( p_item, p_child ); + b_play = VLC_TRUE; + } + } } else { /* msg dbg relative ? */ - char *psz_absolute = alloca( strlen( p_input->psz_source ) + strlen( psz_ref ) + 1); - char *end = strrchr( p_input->psz_source, '/' ); + char *psz_absolute = alloca( strlen( p_demux->psz_access ) + 3 + strlen( p_demux->psz_path ) + strlen( psz_ref ) + 1); + char *end = strrchr( p_demux->psz_path, '/' ); if( end ) { - int i_len = end + 1 - p_input->psz_source; + int i_len = end + 1 - p_demux->psz_path; - strncpy( psz_absolute, p_input->psz_source, i_len); - psz_absolute[i_len] = '\0'; + strcpy( psz_absolute, p_demux->psz_access ); + strcat( psz_absolute, "://" ); + strncat( psz_absolute, p_demux->psz_path, i_len); } else { strcpy( psz_absolute, "" ); } strcat( psz_absolute, psz_ref ); - msg_Dbg( p_input, "adding ref = `%s'", psz_absolute ); - playlist_Add( p_playlist, psz_absolute, psz_absolute, - PLAYLIST_APPEND, PLAYLIST_END ); + msg_Dbg( p_demux, "adding ref = `%s'", psz_absolute ); + if( p_item ) + { + playlist_item_t *p_child = + playlist_ItemNew( p_playlist, + psz_absolute, + psz_absolute ); + if( p_child ) + { + playlist_NodeAddItem( p_playlist, p_child, + p_item->pp_parents[0]->i_view, + p_item, PLAYLIST_APPEND, + PLAYLIST_END ); + playlist_CopyParents( p_item, p_child ); + b_play = VLC_TRUE; + } + } } } 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_demux, "unknown ref type=%4.4s FIXME (send a bug report)", + (char*)&p_rdrf->data.p_rdrf->i_ref_type ); } } + if( b_play == VLC_TRUE ) + { + playlist_Control( p_playlist, PLAYLIST_VIEWPLAY, + p_playlist->status.i_view, + p_playlist->status.p_item, NULL ); + } vlc_object_release( p_playlist ); } else { - msg_Err( p_input, "can't find playlist" ); + msg_Err( p_demux, "can't find playlist" ); } } @@ -259,13 +446,13 @@ static int Open( vlc_object_t * p_this ) { if( !p_rmra ) { - msg_Err( p_input, "cannot find /moov/mvhd" ); + msg_Err( p_demux, "cannot find /moov/mvhd" ); goto error; } else { - msg_Warn( p_input, "cannot find /moov/mvhd (pure ref file)" ); - p_input->pf_demux = DemuxRef; + msg_Warn( p_demux, "cannot find /moov/mvhd (pure ref file)" ); + p_demux->pf_demux = DemuxRef; return VLC_SUCCESS; } } @@ -277,43 +464,22 @@ static int Open( vlc_object_t * p_this ) if( !( p_sys->i_tracks = MP4_BoxCount( p_sys->p_root, "/moov/trak" ) ) ) { - msg_Err( p_input, "cannot find any /moov/trak" ); + msg_Err( p_demux, "cannot find any /moov/trak" ); goto error; } - msg_Dbg( p_input, "find %d track%c", + msg_Dbg( p_demux, "find %d track%c", p_sys->i_tracks, p_sys->i_tracks ? 's':' ' ); - /* create one program */ - vlc_mutex_lock( &p_input->stream.stream_lock ); - if( input_InitStream( p_input, 0 ) == -1) - { - vlc_mutex_unlock( &p_input->stream.stream_lock ); - msg_Err( p_input, "cannot init stream" ); - goto error; - } - if( p_sys->i_duration/p_sys->i_timescale > 0 ) - { - p_input->stream.i_mux_rate = - p_input->stream.p_selected_area->i_size / 50 / - ( p_sys->i_duration / p_sys->i_timescale ); - } - else - { - p_input->stream.i_mux_rate = 0; - } - vlc_mutex_unlock( &p_input->stream.stream_lock ); - - /* 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 */ + /* now process each track and extract all usefull information */ for( i = 0; i < p_sys->i_tracks; i++ ) { p_trak = MP4_BoxGet( p_sys->p_root, "/moov/trak[%d]", i ); - MP4_TrackCreate( p_input, &p_sys->track[i], p_trak ); + MP4_TrackCreate( p_demux, &p_sys->track[i], p_trak ); if( p_sys->track[i].b_ok ) { @@ -326,20 +492,25 @@ static int Open( vlc_object_t * p_this ) case( AUDIO_ES ): psz_cat = "audio"; break; + case( SPU_ES ): + psz_cat = "subtitle"; + break; + default: psz_cat = "unknown"; break; } - msg_Dbg( p_input, "adding track[Id 0x%x] %s (%s) language %s", - p_sys->track[i].i_track_ID, - psz_cat, - p_sys->track[i].b_enable ? "enable":"disable", - p_sys->track[i].fmt.psz_language ? p_sys->track[i].fmt.psz_language : "undef" ); + msg_Dbg( p_demux, "adding track[Id 0x%x] %s (%s) language %s", + p_sys->track[i].i_track_ID, psz_cat, + p_sys->track[i].b_enable ? "enable":"disable", + p_sys->track[i].fmt.psz_language ? + p_sys->track[i].fmt.psz_language : "undef" ); } else { - msg_Dbg( p_input, "ignoring track[Id 0x%x]", p_sys->track[i].i_track_ID ); + msg_Dbg( p_demux, "ignoring track[Id 0x%x]", + p_sys->track[i].i_track_ID ); } } @@ -348,7 +519,7 @@ static int Open( vlc_object_t * p_this ) error: if( p_sys->p_root ) { - MP4_BoxFree( p_input, p_sys->p_root ); + MP4_BoxFree( p_demux->s, p_sys->p_root ); } free( p_sys ); return VLC_EGENERIC; @@ -359,180 +530,195 @@ error: ***************************************************************************** * TODO check for newly selected track (ie audio upt to now ) *****************************************************************************/ -static int Demux( input_thread_t *p_input ) +static int Demux( demux_t *p_demux ) { - demux_sys_t *p_sys = p_input->p_demux_data; + demux_sys_t *p_sys = p_demux->p_sys; unsigned int i_track; unsigned int i_track_selected; - vlc_bool_t b_play_audio; /* check for newly selected/unselected track */ - for( i_track = 0, i_track_selected = 0; i_track < p_sys->i_tracks; i_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_demux, "track[0x%x] will be disabled", tk->i_track_ID ); + MP4_TrackUnselect( p_demux, 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_demux->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_demux, 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_demux, 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 ); + p_sys->i_time += __MAX( p_sys->i_timescale / 10 , 1 ); + if( p_sys->i_timescale > 0 ) + { + int64_t i_length = (mtime_t)1000000 * + (mtime_t)p_sys->i_duration / + (mtime_t)p_sys->i_timescale; + if( MP4_GetMoviePTS( p_sys ) >= i_length ) + return 0; + return 1; + } + + msg_Warn( p_demux, "no track selected, exiting..." ); + return 0; } /* first wait for the good time to read a packet */ - input_ClockManageRef( p_input, - p_input->stream.p_selected_program, - p_sys->i_pcr ); + es_out_Control( p_demux->out, ES_OUT_SET_PCR, p_sys->i_pcr + 1 ); - /* update pcr XXX in mpeg scale so in 90000 unit/s */ - p_sys->i_pcr = MP4_GetMoviePTS( p_sys ) * 9 / 100; + p_sys->i_pcr = MP4_GetMoviePTS( p_sys ); /* we will read 100ms for each stream so ...*/ p_sys->i_time += __MAX( p_sys->i_timescale / 10 , 1 ); - /* Check if we need to send the audio data to decoder */ - b_play_audio = !p_input->stream.control.b_mute; - 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_demux, tk ) < MP4_GetMoviePTS( p_sys ) ) { +#if 0 + msg_Dbg( p_demux, "tk(%i)=%lld mv=%lld", i_track, + MP4_TrackGetPTS( p_demux, tk ), + MP4_GetMoviePTS( p_sys ) ); +#endif - if( !b_play_audio && track.fmt.i_cat == AUDIO_ES ) - { - if( MP4_TrackNextSample( p_input, &track ) ) - { - break; - } - } - else + if( MP4_TrackSampleSize( tk ) > 0 ) { block_t *p_block; /* go,go go ! */ - if( stream_Seek( p_input->s, MP4_GetTrackPos( &track ) ) ) + if( stream_Seek( p_demux->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_demux, "track[0x%x] will be disabled (eof?)", + tk->i_track_ID ); + MP4_TrackUnselect( p_demux, tk ); break; } /* now read pes */ - if( ( p_block = stream_Block( p_input->s, - MP4_TrackSampleSize( &track ) ) ) == NULL ) + if( !(p_block = + stream_Block( p_demux->s, MP4_TrackSampleSize(tk) )) ) { - msg_Warn( p_input, "track[0x%x] will be disabled (eof?)", track.i_track_ID ); - MP4_TrackUnselect( p_input, &track ); + msg_Warn( p_demux, "track[0x%x] will be disabled (eof?)", + tk->i_track_ID ); + MP4_TrackUnselect( p_demux, 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 ); } - - 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 + else if( tk->fmt.i_cat == SPU_ES ) { - p_block->i_pts = - p_block->i_dts = - input_ClockGetTS( p_input, - p_input->stream.p_selected_program, - MP4_GetTrackPTS( &track ) * 9/100 ); - } + if( tk->fmt.i_codec == VLC_FOURCC( 's', 'u', 'b', 't' ) && + p_block->i_buffer >= 2 ) + { + uint16_t i_size = GetWBE( p_block->p_buffer ); - if( !track.b_drms || ( track.b_drms && track.p_drms ) ) - { - es_out_Send( p_input->p_es_out, track.p_es, p_block ); + if( i_size + 2 <= p_block->i_buffer ) + { + char *p; + /* remove the length field, and append a '\0' */ + memmove( &p_block->p_buffer[0], + &p_block->p_buffer[2], i_size ); + p_block->p_buffer[i_size] = '\0'; + p_block->i_buffer = i_size + 1; + + /* convert \r -> \n */ + while( ( p = strchr( p_block->p_buffer, '\r' ) ) ) + { + *p = '\n'; + } + } + else + { + /* Invalid */ + p_block->i_buffer = 0; + } + } } + p_block->i_dts = MP4_TrackGetPTS( p_demux, tk ) + 1; + + p_block->i_pts = tk->fmt.i_cat == VIDEO_ES ? + 0 : p_block->i_dts + 1; - if( MP4_TrackNextSample( p_input, &track ) ) + if( !tk->b_drms || ( tk->b_drms && tk->p_drms ) ) { - break; + es_out_Send( p_demux->out, tk->p_es, p_block ); } } + + /* Next sample */ + if( MP4_TrackNextSample( p_demux, tk ) ) + { + break; + } } -#undef track } - return( 1 ); + return 1; } /***************************************************************************** * Seek: Got to i_date - ******************************************************************************/ -static int Seek ( input_thread_t *p_input, mtime_t i_date ) +******************************************************************************/ +static int Seek( demux_t *p_demux, mtime_t i_date ) { - demux_sys_t *p_sys = p_input->p_demux_data; + demux_sys_t *p_sys = p_demux->p_sys; 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; + p_sys->i_pcr = 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_TrackSeek( p_input, &track, i_date ); - } -#undef track + mp4_track_t *tk = &p_sys->track[i_track]; + MP4_TrackSeek( p_demux, tk, i_date ); } - return( 1 ); + return VLC_SUCCESS; } /***************************************************************************** * Control: *****************************************************************************/ -static int Control ( input_thread_t *p_input, int i_query, va_list args ) +static int Control( demux_t *p_demux, int i_query, va_list args ) { - demux_sys_t *p_sys = p_input->p_demux_data; + demux_sys_t *p_sys = p_demux->p_sys; - double f, *pf; + double f, *pf; int64_t i64, *pi64; switch( i_query ) @@ -551,35 +737,128 @@ 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_demux, 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: i64 = (int64_t)va_arg( args, int64_t ); - return Seek( p_input, i64 ); + return Seek( p_demux, i64 ); 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 !!" ); + msg_Warn( p_demux, "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: /* Full name */ + 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: /* Creation Date */ + vlc_meta_Add( meta, VLC_META_DATE, + p_0xa9xxx->data.p_0xa9xxx->psz_text ); + break; + case FOURCC_0xa9des: /* Description */ + vlc_meta_Add( meta, VLC_META_DESCRIPTION, + p_0xa9xxx->data.p_0xa9xxx->psz_text ); + break; + case FOURCC_0xa9gen: /* Genre */ + vlc_meta_Add( meta, VLC_META_GENRE, + p_0xa9xxx->data.p_0xa9xxx->psz_text ); + break; + + case FOURCC_0xa9swr: + case FOURCC_0xa9inf: /* Information */ + case FOURCC_0xa9alb: /* Album */ + case FOURCC_0xa9dir: /* Director */ + case FOURCC_0xa9dis: /* Disclaimer */ + case FOURCC_0xa9enc: /* Encoded By */ + case FOURCC_0xa9trk: /* Track */ + case FOURCC_0xa9cmt: /* Commment */ + case FOURCC_0xa9url: /* URL */ + case FOURCC_0xa9req: /* Requirements */ + case FOURCC_0xa9fmt: /* Original Format */ + case FOURCC_0xa9dsa: /* Display Source As */ + case FOURCC_0xa9hst: /* Host Computer */ + case FOURCC_0xa9prd: /* Producer */ + case FOURCC_0xa9prf: /* Performers */ + case FOURCC_0xa9ope: /* Original Performer */ + case FOURCC_0xa9src: /* Providers Source Content */ + case FOURCC_0xa9wrt: /* Writer */ + case FOURCC_0xa9com: /* Composer */ + case FOURCC_WLOC: /* Window Location */ + /* TODO one day, but they aren't really meaningfull */ + break; + + default: + break; + } + } + return VLC_SUCCESS; + } + + case DEMUX_GET_TITLE_INFO: + case DEMUX_SET_NEXT_DEMUX_TIME: + case DEMUX_SET_GROUP: + return VLC_EGENERIC; + default: - msg_Err( p_input, "control query unimplemented !!!" ); - return demux_vaControlDefault( p_input, i_query, args ); + msg_Warn( p_demux, "control query unimplemented !!!" ); + return VLC_EGENERIC; } } @@ -589,15 +868,15 @@ static int Control ( input_thread_t *p_input, int i_query, va_list args ) static void Close ( vlc_object_t * p_this ) { unsigned int i_track; - input_thread_t * p_input = (input_thread_t *)p_this; - demux_sys_t *p_sys = p_input->p_demux_data; + demux_t * p_demux = (demux_t *)p_this; + demux_sys_t *p_sys = p_demux->p_sys; - msg_Dbg( p_input, "freeing all memory" ); + msg_Dbg( p_demux, "freeing all memory" ); - MP4_BoxFree( p_input, p_sys->p_root ); + MP4_BoxFree( p_demux->s, p_sys->p_root ); for( i_track = 0; i_track < p_sys->i_tracks; i_track++ ) { - MP4_TrackDestroy( p_input, &p_sys->track[i_track] ); + MP4_TrackDestroy( p_demux, &p_sys->track[i_track] ); } FREE( p_sys->track ); @@ -611,8 +890,8 @@ 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 ) +static int TrackCreateChunksIndex( demux_t *p_demux, + 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; @@ -630,11 +909,11 @@ static int TrackCreateChunksIndex( input_thread_t *p_input, p_demux_track->i_chunk_count = p_co64->data.p_co64->i_entry_count; if( !p_demux_track->i_chunk_count ) { - msg_Warn( p_input, "no chunk defined" ); + msg_Warn( p_demux, "no chunk defined" ); 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++ ) @@ -650,15 +929,14 @@ static int TrackCreateChunksIndex( input_thread_t *p_input, i_index = p_stsc->data.p_stsc->i_entry_count; if( !i_index ) { - msg_Warn( p_input, "cannot read chunk table or table empty" ); + msg_Warn( p_demux, "cannot read chunk table or table empty" ); return( VLC_EGENERIC ); } - while( i_index ) + while( i_index-- ) { - i_index--; for( i_chunk = p_stsc->data.p_stsc->i_first_chunk[i_index] - 1; - i_chunk < i_last; i_chunk++ ) + i_chunk < i_last; i_chunk++ ) { p_demux_track->chunk[i_chunk].i_sample_description_index = p_stsc->data.p_stsc->i_sample_description_index[i_index]; @@ -676,15 +954,14 @@ static int TrackCreateChunksIndex( input_thread_t *p_input, p_demux_track->chunk[i_chunk-1].i_sample_count; } - msg_Dbg( p_input, - "track[Id 0x%x] read %d chunk", - p_demux_track->i_track_ID, - p_demux_track->i_chunk_count ); + msg_Dbg( p_demux, "track[Id 0x%x] read %d chunk", + p_demux_track->i_track_ID, p_demux_track->i_chunk_count ); - return( VLC_SUCCESS ); + return VLC_SUCCESS; } -static int TrackCreateSamplesIndex( input_thread_t *p_input, - track_data_mp4_t *p_demux_track ) + +static int TrackCreateSamplesIndex( demux_t *p_demux, + 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, @@ -707,7 +984,7 @@ static int TrackCreateSamplesIndex( input_thread_t *p_input, if( ( !p_stts )||( !p_stsz ) ) { - msg_Warn( p_input, "cannot read sample table" ); + msg_Warn( p_demux, "cannot read sample table" ); return( VLC_EGENERIC ); } @@ -734,36 +1011,37 @@ static int TrackCreateSamplesIndex( input_thread_t *p_input, p_stsz->data.p_stsz->i_entry_size[i_sample]; } } - /* we have extract all information from stsz, - now use stts */ + /* we have extracted all the information from stsz, now use stts */ /* if we don't want to waste too much memory, we can't expand - the box !, so each chunk will contain an "extract" of this table + the box! so each chunk will contain an "extract" of this table for fast research */ i_last_dts = 0; - i_index = 0; i_index_sample_used =0; + i_index = 0; i_index_sample_used = 0; /* create and init last data for each chunk */ - for(i_chunk = 0 ; i_chunk < p_demux_track->i_chunk_count; i_chunk++ ) + for( i_chunk = 0; i_chunk < p_demux_track->i_chunk_count; i_chunk++ ) { int64_t i_entry, i_sample_count, i; + /* save last dts */ p_demux_track->chunk[i_chunk].i_first_dts = i_last_dts; - /* count how many entries needed for this chunk - for p_sample_delta_dts and p_sample_count_dts */ + /* count how many entries are needed for this chunk + * for p_sample_delta_dts and p_sample_count_dts */ i_sample_count = p_demux_track->chunk[i_chunk].i_sample_count; i_entry = 0; while( i_sample_count > 0 ) { - i_sample_count -= p_stts->data.p_stts->i_sample_count[i_index+i_entry]; + i_sample_count -= + p_stts->data.p_stts->i_sample_count[i_index+i_entry]; if( i_entry == 0 ) { - i_sample_count += i_index_sample_used; /* don't count already used sample - int this entry */ + /* don't count already used sample in this entry */ + i_sample_count += i_index_sample_used; } i_entry++; } @@ -781,7 +1059,8 @@ static int TrackCreateSamplesIndex( input_thread_t *p_input, int64_t i_used; int64_t i_rest; - i_rest = p_stts->data.p_stts->i_sample_count[i_index] - i_index_sample_used; + i_rest = p_stts->data.p_stts->i_sample_count[i_index] - + i_index_sample_used; i_used = __MIN( i_rest, i_sample_count ); @@ -791,39 +1070,33 @@ static int TrackCreateSamplesIndex( input_thread_t *p_input, p_demux_track->chunk[i_chunk].p_sample_count_dts[i] = i_used; p_demux_track->chunk[i_chunk].p_sample_delta_dts[i] = - p_stts->data.p_stts->i_sample_delta[i_index]; + p_stts->data.p_stts->i_sample_delta[i_index]; i_last_dts += i_used * - p_demux_track->chunk[i_chunk].p_sample_delta_dts[i]; + p_demux_track->chunk[i_chunk].p_sample_delta_dts[i]; if( i_index_sample_used >= - p_stts->data.p_stts->i_sample_count[i_index] ) + p_stts->data.p_stts->i_sample_count[i_index] ) { - i_index++; i_index_sample_used = 0; } } - } - msg_Dbg( p_input, - "track[Id 0x%x] read %d samples length:"I64Fd"s", - p_demux_track->i_track_ID, - p_demux_track->i_sample_count, + msg_Dbg( p_demux, "track[Id 0x%x] read %d samples length:"I64Fd"s", + p_demux_track->i_track_ID, p_demux_track->i_sample_count, i_last_dts / p_demux_track->i_timescale ); - return( VLC_SUCCESS ); + return VLC_SUCCESS; } /* * TrackCreateES: - * Create ES and PES to init decoder if needed, for a track starting at i_chunk + * 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, - unsigned int i_chunk, - es_out_id_t **pp_es ) +static int TrackCreateES( demux_t *p_demux, mp4_track_t *p_track, + unsigned int i_chunk, es_out_id_t **pp_es ) { MP4_Box_t *p_sample; MP4_Box_t *p_esds; @@ -832,8 +1105,7 @@ static int TrackCreateES ( input_thread_t *p_input, if( !p_track->chunk[i_chunk].i_sample_description_index ) { - msg_Warn( p_input, - "invalid SampleEntry index (track[Id 0x%x])", + msg_Warn( p_demux, "invalid SampleEntry index (track[Id 0x%x])", p_track->i_track_ID ); return VLC_EGENERIC; } @@ -841,17 +1113,17 @@ static int TrackCreateES ( input_thread_t *p_input, p_sample = MP4_BoxGet( p_track->p_stsd, "[%d]", p_track->chunk[i_chunk].i_sample_description_index - 1 ); - if( !p_sample || !p_sample->data.p_data ) + if( !p_sample || + ( !p_sample->data.p_data && p_track->fmt.i_cat != SPU_ES ) ) { - msg_Warn( p_input, - "cannot find SampleEntry (track[Id 0x%x])", + msg_Warn( p_demux, "cannot find SampleEntry (track[Id 0x%x])", p_track->i_track_ID ); - return( VLC_EGENERIC ); + return VLC_EGENERIC; } p_track->p_sample = p_sample; - if( p_track->i_sample_size == 1 ) + if( p_track->fmt.i_cat == AUDIO_ES && p_track->i_sample_size == 1 ) { MP4_Box_data_sample_soun_t *p_soun; @@ -882,6 +1154,10 @@ static int TrackCreateES ( input_thread_t *p_input, p_soun->i_bytes_per_frame = 2 * p_soun->i_channelcount; p_soun->i_bytes_per_sample = 2; break; + case VLC_FOURCC( 'a', 'l', 'a', 'w' ): + case VLC_FOURCC( 'u', 'l', 'a', 'w' ): + p_soun->i_samplesize = 8; + break; default: break; } @@ -907,6 +1183,14 @@ static int TrackCreateES ( input_thread_t *p_input, case( VLC_FOURCC( 's', '2', '6', '3' ) ): p_track->fmt.i_codec = VLC_FOURCC( 'h', '2', '6', '3' ); break; + + case( VLC_FOURCC( 't', 'e', 'x', 't' ) ): + p_track->fmt.i_codec = VLC_FOURCC( 's', 'u', 'b', 't' ); + /* FIXME: Not true, could be UTF-16 with a Byte Order Mark (0xfeff) */ + /* FIXME UTF-8 doesn't work here ? */ + /* p_track->fmt.subs.psz_encoding = strdup( "UTF-8" ); */ + break; + default: p_track->fmt.i_codec = p_sample->i_type; break; @@ -956,9 +1240,18 @@ static int TrackCreateES ( input_thread_t *p_input, case( 0x6c ): /* jpeg */ p_track->fmt.i_codec = VLC_FOURCC( 'j','p','e','g' ); break; + + /* Private ID */ + case( 0xe0 ): /* NeroDigital: dvd subs */ + if( p_track->fmt.i_cat == SPU_ES ) + { + p_track->fmt.i_codec = VLC_FOURCC( 's','p','u',' ' ); + break; + } + /* Fallback */ default: /* Unknown entry, but don't touch i_fourcc */ - msg_Warn( p_input, + msg_Warn( p_demux, "unknown objectTypeIndication(0x%x) (Track[ID 0x%x])", p_decconfig->i_objectTypeIndication, p_track->i_track_ID ); @@ -1006,6 +1299,28 @@ static int TrackCreateES ( input_thread_t *p_input, p_track->fmt.i_extra); } break; + + /* avc1: send avcC (h264 without annexe B, ie without start code)*/ + case VLC_FOURCC( 'a', 'v', 'c', '1' ): + { + MP4_Box_t *p_avcC = MP4_BoxGet( p_sample, "avcC" ); + + if( p_avcC ) + { + /* Hack: use a packetizer to reecampsulate data in anexe B format */ + msg_Dbg( p_demux, "avcC: size=%d", p_avcC->data.p_avcC->i_avcC ); + p_track->fmt.i_extra = p_avcC->data.p_avcC->i_avcC; + p_track->fmt.p_extra = malloc( p_avcC->data.p_avcC->i_avcC ); + memcpy( p_track->fmt.p_extra, p_avcC->data.p_avcC->p_avcC, p_track->fmt.i_extra ); + p_track->fmt.b_packetized = VLC_FALSE; + } + else + { + msg_Err( p_demux, "missing avcC" ); + } + break; + } + default: break; } @@ -1027,7 +1342,9 @@ static int TrackCreateES ( input_thread_t *p_input, p_track->fmt.video.i_height = p_track->i_height; /* Find out apect ratio from display size */ - if( p_track->i_width > 0 && p_track->i_height > 0 ) + if( p_track->i_width > 0 && p_track->i_height > 0 && + /* Work-around buggy muxed files */ + p_sample->data.p_sample_vide->i_width != p_track->i_width ) p_track->fmt.video.i_aspect = VOUT_ASPECT_FACTOR * p_track->i_width / p_track->i_height; @@ -1049,33 +1366,69 @@ static int TrackCreateES ( input_thread_t *p_input, break; } - *pp_es = es_out_Add( p_input->p_es_out, &p_track->fmt ); + *pp_es = es_out_Add( p_demux->out, &p_track->fmt ); return VLC_SUCCESS; } -/* given a time it return sample/chunk */ -static int TrackTimeToSampleChunk( input_thread_t *p_input, - track_data_mp4_t *p_track, - uint64_t i_start, - uint32_t *pi_chunk, - uint32_t *pi_sample ) +/* given a time it return sample/chunk + * it also update elst field of the track + */ +static int TrackTimeToSampleChunk( demux_t *p_demux, 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_demux->p_sys; + 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_demux, 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 * I64C(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_demux, "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++ ) @@ -1117,15 +1470,16 @@ static int TrackTimeToSampleChunk( input_thread_t *p_input, { break; } - i_sample += ( i_start - i_dts ) / p_track->chunk[i_chunk].p_sample_delta_dts[i_index]; + i_sample += ( i_start - i_dts ) / + p_track->chunk[i_chunk].p_sample_delta_dts[i_index]; break; } } if( i_sample >= p_track->i_sample_count ) { - msg_Warn( p_input, - "track[Id 0x%x] will be disabled (seeking too far) chunk=%d sample=%d", + msg_Warn( p_demux, "track[Id 0x%x] will be disabled " + "(seeking too far) chunk=%d sample=%d", p_track->i_track_ID, i_chunk, i_sample ); return( VLC_EGENERIC ); } @@ -1135,7 +1489,7 @@ static int TrackTimeToSampleChunk( input_thread_t *p_input, if( ( p_stss = MP4_BoxGet( p_track->p_stbl, "stss" ) ) ) { unsigned int i_index; - msg_Dbg( p_input, + msg_Dbg( p_demux, "track[Id 0x%x] using Sync Sample Box (stss)", p_track->i_track_ID ); for( i_index = 0; i_index < p_stss->data.p_stss->i_entry_count; i_index++ ) @@ -1144,7 +1498,7 @@ static int TrackTimeToSampleChunk( input_thread_t *p_input, { if( i_index > 0 ) { - msg_Dbg( p_input, "stts gives %d --> %d (sample number)", + msg_Dbg( p_demux, "stts gives %d --> %d (sample number)", i_sample, p_stss->data.p_stss->i_sample_number[i_index-1] ); i_sample = p_stss->data.p_stss->i_sample_number[i_index-1]; @@ -1157,14 +1511,14 @@ static int TrackTimeToSampleChunk( input_thread_t *p_input, } else { - msg_Dbg( p_input, "stts gives %d --> %d (sample number)", + msg_Dbg( p_demux, "stts gives %d --> %d (sample number)", i_sample, p_stss->data.p_stss->i_sample_number[i_index] ); i_sample = p_stss->data.p_stss->i_sample_number[i_index]; /* new i_sample is more than old so i_chunk can only increased */ while( i_chunk < p_track->i_chunk_count - 1 && i_sample >= p_track->chunk[i_chunk].i_sample_first + - p_track->chunk[i_chunk].i_sample_count ) + p_track->chunk[i_chunk].i_sample_count ) { i_chunk++; } @@ -1175,42 +1529,40 @@ static int TrackTimeToSampleChunk( input_thread_t *p_input, } else { - msg_Dbg( p_input, - "track[Id 0x%x] does not provide Sync Sample Box (stss)", - p_track->i_track_ID ); + msg_Dbg( p_demux, "track[Id 0x%x] does not provide Sync " + "Sample Box (stss)", 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, - unsigned int i_chunk, - unsigned int i_sample ) +static int TrackGotoChunkSample( demux_t *p_demux, mp4_track_t *p_track, + unsigned int i_chunk, unsigned int i_sample ) { vlc_bool_t b_reselect = VLC_FALSE; /* now see if actual es is ok */ if( p_track->i_chunk < 0 || - p_track->i_chunk >= p_track->i_chunk_count || + p_track->i_chunk >= p_track->i_chunk_count - 1 || 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_demux, "recreate ES for track[Id 0x%x]", + p_track->i_track_ID ); - es_out_Control( p_input->p_es_out, ES_OUT_GET_ES_STATE, p_track->p_es, &b_reselect ); + es_out_Control( p_demux->out, ES_OUT_GET_ES_STATE, + p_track->p_es, &b_reselect ); - es_out_Del( p_input->p_es_out, p_track->p_es ); + es_out_Del( p_demux->out, p_track->p_es ); p_track->p_es = NULL; - if( TrackCreateES( p_input, - p_track, i_chunk, - &p_track->p_es ) ) + if( TrackCreateES( p_demux, p_track, i_chunk, &p_track->p_es ) ) { - msg_Err( p_input, "cannot create es for track[Id 0x%x]", + msg_Err( p_demux, "cannot create es for track[Id 0x%x]", p_track->i_track_ID ); p_track->b_ok = VLC_FALSE; @@ -1222,7 +1574,7 @@ static int TrackGotoChunkSample( input_thread_t *p_input, /* select again the new decoder */ if( b_reselect ) { - es_out_Control( p_input->p_es_out, ES_OUT_SET_ES, p_track->p_es ); + es_out_Control( p_demux->out, ES_OUT_SET_ES, p_track->p_es ); } p_track->i_chunk = i_chunk; @@ -1237,15 +1589,17 @@ static int TrackGotoChunkSample( input_thread_t *p_input, * Parse track information and create all needed data to run a track * 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_Box_t * p_box_trak ) +static void MP4_TrackCreate( demux_t *p_demux, mp4_track_t *p_track, + MP4_Box_t *p_box_trak ) { + demux_sys_t *p_sys = p_demux->p_sys; + 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; MP4_Box_t *p_mdhd; + MP4_Box_t *p_udta; MP4_Box_t *p_hdlr; MP4_Box_t *p_vmhd; @@ -1278,14 +1632,9 @@ 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" ); */ +/* msg_Warn( p_demux, "unhandled box: tref --> FIXME" ); */ } p_mdhd = MP4_BoxGet( p_box_trak, "mdia/mdhd" ); @@ -1322,9 +1671,36 @@ static void MP4_TrackCreate( input_thread_t *p_input, p_track->fmt.i_cat = VIDEO_ES; break; + case( FOURCC_text ): + case( FOURCC_subp ): + p_track->fmt.i_cat = SPU_ES; + break; + 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_demux, "elst box found" ); + for( i = 0; i < elst->i_entry_count; i++ ) + { + msg_Dbg( p_demux, " - [%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" ); @@ -1346,6 +1722,23 @@ static void MP4_TrackCreate( input_thread_t *p_input, p_track->fmt.psz_language = strdup( language ); } + p_udta = MP4_BoxGet( p_box_trak, "udta" ); + if( p_udta ) + { + MP4_Box_t *p_0xa9xxx; + for( p_0xa9xxx = p_udta->p_first; p_0xa9xxx != NULL; + p_0xa9xxx = p_0xa9xxx->p_next ) + { + switch( p_0xa9xxx->i_type ) + { + case FOURCC_0xa9nam: + p_track->fmt.psz_description = + strdup( p_0xa9xxx->data.p_0xa9xxx->psz_text ); + break; + } + } + } + /* fxi i_timescale for AUDIO_ES with i_qt_version == 0 */ if( p_track->fmt.i_cat == AUDIO_ES ) //&& p_track->i_sample_size == 1 ) { @@ -1358,7 +1751,7 @@ static void MP4_TrackCreate( input_thread_t *p_input, if( p_soun->i_qt_version == 0 && p_track->i_timescale != p_soun->i_sampleratehi ) { - msg_Warn( p_input, + msg_Warn( p_demux, "i_timescale ("I64Fu") != i_sampleratehi (%u) with " "qt_version == 0\n" "Making both equal. (report any problem)", @@ -1373,8 +1766,8 @@ static void MP4_TrackCreate( input_thread_t *p_input, } /* Create chunk index table and sample index table */ - if( TrackCreateChunksIndex( p_input,p_track ) || - TrackCreateSamplesIndex( p_input, p_track ) ) + if( TrackCreateChunksIndex( p_demux,p_track ) || + TrackCreateSamplesIndex( p_demux, p_track ) ) { return; /* cannot create chunks index */ } @@ -1383,11 +1776,11 @@ static void MP4_TrackCreate( input_thread_t *p_input, p_track->i_sample = 0; /* now create es */ - if( TrackCreateES( p_input, + if( TrackCreateES( p_demux, p_track, p_track->i_chunk, &p_track->p_es ) ) { - msg_Err( p_input, "cannot create es for track[Id 0x%x]", + msg_Err( p_demux, "cannot create es for track[Id 0x%x]", p_track->i_track_ID ); return; } @@ -1397,7 +1790,9 @@ static void MP4_TrackCreate( input_thread_t *p_input, int i; for( i = 0; i < p_track->i_chunk_count; i++ ) { - fprintf( stderr, "%-5d sample_count=%d pts=%lld\n", i, p_track->chunk[i].i_sample_count, p_track->chunk[i].i_first_dts ); + fprintf( stderr, "%-5d sample_count=%d pts=%lld\n", + i, p_track->chunk[i].i_sample_count, + p_track->chunk[i].i_first_dts ); } } @@ -1410,8 +1805,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 ) +static void MP4_TrackDestroy( demux_t *p_demux, mp4_track_t *p_track ) { unsigned int i_chunk; @@ -1419,7 +1813,7 @@ static void MP4_TrackDestroy( input_thread_t *p_input, p_track->b_enable = VLC_FALSE; p_track->b_selected = VLC_FALSE; - es_format_Init( &p_track->fmt, UNKNOWN_ES, 0 ); + es_format_Clean( &p_track->fmt ); for( i_chunk = 0; i_chunk < p_track->i_chunk_count; i_chunk++ ) { @@ -1437,47 +1831,25 @@ static void MP4_TrackDestroy( input_thread_t *p_input, } } -static int MP4_TrackSelect ( input_thread_t *p_input, - track_data_mp4_t *p_track, - mtime_t i_start ) +static int MP4_TrackSelect( demux_t *p_demux, 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 ) { - 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]", + msg_Warn( p_demux, "track[Id 0x%x] already selected", 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_demux, p_track, i_start ); } -static void MP4_TrackUnselect(input_thread_t *p_input, - track_data_mp4_t *p_track ) +static void MP4_TrackUnselect( demux_t *p_demux, mp4_track_t *p_track ) { if( !p_track->b_ok ) { @@ -1486,22 +1858,21 @@ static void MP4_TrackUnselect(input_thread_t *p_input, if( !p_track->b_selected ) { - msg_Warn( p_input, - "track[Id 0x%x] already unselected", + msg_Warn( p_demux, "track[Id 0x%x] already unselected", p_track->i_track_ID ); return; } if( p_track->p_es ) { - es_out_Control( p_input->p_es_out, ES_OUT_SET_ES_STATE, p_track->p_es, VLC_FALSE ); + es_out_Control( p_demux->out, ES_OUT_SET_ES_STATE, + p_track->p_es, VLC_FALSE ); } p_track->b_selected = VLC_FALSE; } -static int MP4_TrackSeek ( input_thread_t *p_input, - track_data_mp4_t *p_track, - mtime_t i_start ) +static int MP4_TrackSeek( demux_t *p_demux, mp4_track_t *p_track, + mtime_t i_start ) { uint32_t i_chunk; uint32_t i_sample; @@ -1511,33 +1882,33 @@ static int MP4_TrackSeek ( input_thread_t *p_input, return( VLC_EGENERIC ); } - if( TrackTimeToSampleChunk( p_input, - p_track, i_start, + p_track->b_selected = VLC_FALSE; + + if( TrackTimeToSampleChunk( p_demux, p_track, i_start, &i_chunk, &i_sample ) ) { - msg_Warn( p_input, - "cannot select track[Id 0x%x]", + msg_Warn( p_demux, "cannot select track[Id 0x%x]", p_track->i_track_ID ); return( VLC_EGENERIC ); } p_track->b_selected = VLC_TRUE; - TrackGotoChunkSample( p_input, p_track, i_chunk, i_sample ); - + if( TrackGotoChunkSample( p_demux, p_track, i_chunk, i_sample ) == + VLC_SUCCESS ) + { + p_track->b_selected = VLC_TRUE; + } 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 ) +#define QT_V0_MAX_SAMPLES 1024 +static int MP4_TrackSampleSize( mp4_track_t *p_track ) { int i_size; MP4_Box_data_sample_soun_t *p_soun; @@ -1545,58 +1916,58 @@ static int MP4_TrackSampleSize( track_data_mp4_t *p_track ) if( p_track->i_sample_size == 0 ) { /* most simple case */ - return( p_track->p_sample_size[p_track->i_sample] ); + return p_track->p_sample_size[p_track->i_sample]; } if( p_track->fmt.i_cat != AUDIO_ES ) { - return( p_track->i_sample_size ); - } - - if( p_track->i_sample_size != 1 ) - { - //msg_Warn( p_input, "SampleSize != 1" ); - return( p_track->i_sample_size ); + return p_track->i_sample_size; } p_soun = p_track->p_sample->data.p_sample_soun; if( p_soun->i_qt_version == 1 ) { - i_size = p_track->chunk[p_track->i_chunk].i_sample_count / p_soun->i_sample_per_packet * p_soun->i_bytes_per_frame; + i_size = p_track->chunk[p_track->i_chunk].i_sample_count / + p_soun->i_sample_per_packet * p_soun->i_bytes_per_frame; + } + else if( p_track->i_sample_size > 256 ) + { + /* We do that so we don't read too much data + * (in this case we are likely dealing with compressed data) */ + i_size = p_track->i_sample_size; } else { - /* FIXME */ + /* Read a bunch of samples at once */ int i_samples = p_track->chunk[p_track->i_chunk].i_sample_count - - ( p_track->i_sample - p_track->chunk[p_track->i_chunk].i_sample_first ); - if( i_samples > QT_V0_MAX_SAMPLES ) - { - i_samples = QT_V0_MAX_SAMPLES; - } - i_size = i_samples * p_soun->i_channelcount * p_soun->i_samplesize / 8; + ( p_track->i_sample - + p_track->chunk[p_track->i_chunk].i_sample_first ); + + i_samples = __MIN( QT_V0_MAX_SAMPLES, i_samples ); + i_size = i_samples * p_track->i_sample_size; } //fprintf( stderr, "size=%d\n", i_size ); - return( i_size ); + return i_size; } - -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; - i_pos = p_track->chunk[p_track->i_chunk].i_offset; if( p_track->i_sample_size ) { - MP4_Box_data_sample_soun_t *p_soun = p_track->p_sample->data.p_sample_soun; + MP4_Box_data_sample_soun_t *p_soun = + p_track->p_sample->data.p_sample_soun; if( p_soun->i_qt_version == 0 ) { - i_pos += ( p_track->i_sample - p_track->chunk[p_track->i_chunk].i_sample_first ) * - p_soun->i_channelcount * p_soun->i_samplesize / 8; + i_pos += ( p_track->i_sample - + p_track->chunk[p_track->i_chunk].i_sample_first ) * + p_track->i_sample_size; } else { @@ -1607,21 +1978,18 @@ static uint64_t MP4_GetTrackPos( track_data_mp4_t *p_track ) else { for( i_sample = p_track->chunk[p_track->i_chunk].i_sample_first; - i_sample < p_track->i_sample; i_sample++ ) + i_sample < p_track->i_sample; i_sample++ ) { i_pos += p_track->p_sample_size[i_sample]; } - } - return( i_pos ); + + return i_pos; } -static int MP4_TrackNextSample( input_thread_t *p_input, - track_data_mp4_t *p_track ) +static int MP4_TrackNextSample( demux_t *p_demux, mp4_track_t *p_track ) { - - if( p_track->fmt.i_cat == AUDIO_ES && - p_track->i_sample_size != 0 ) + if( p_track->fmt.i_cat == AUDIO_ES && p_track->i_sample_size != 0 ) { MP4_Box_data_sample_soun_t *p_soun; @@ -1634,11 +2002,19 @@ static int MP4_TrackNextSample( input_thread_t *p_input, p_track->chunk[p_track->i_chunk].i_sample_first + p_track->chunk[p_track->i_chunk].i_sample_count; } + else if( p_track->i_sample_size > 256 ) + { + /* We do that so we don't read too much data + * (in this case we are likely dealing with compressed data) */ + p_track->i_sample += 1; + } else { /* FIXME */ p_track->i_sample += QT_V0_MAX_SAMPLES; - if( p_track->i_sample > p_track->chunk[p_track->i_chunk].i_sample_first + p_track->chunk[p_track->i_chunk].i_sample_count ) + if( p_track->i_sample > + p_track->chunk[p_track->i_chunk].i_sample_first + + p_track->chunk[p_track->i_chunk].i_sample_count ) { p_track->i_sample = p_track->chunk[p_track->i_chunk].i_sample_first + @@ -1654,9 +2030,9 @@ static int MP4_TrackNextSample( input_thread_t *p_input, if( p_track->i_sample >= p_track->i_sample_count ) { /* 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 ); + msg_Warn( p_demux, "track[0x%x] will be disabled", p_track->i_track_ID ); + MP4_TrackUnselect( p_demux, p_track ); + return VLC_EGENERIC; } /* Have we changed chunk ? */ @@ -1664,18 +2040,76 @@ static int MP4_TrackNextSample( input_thread_t *p_input, p_track->chunk[p_track->i_chunk].i_sample_first + p_track->chunk[p_track->i_chunk].i_sample_count ) { - if( TrackGotoChunkSample( p_input, - p_track, - p_track->i_chunk + 1, + if( TrackGotoChunkSample( p_demux, p_track, p_track->i_chunk + 1, p_track->i_sample ) ) { - 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 ); + msg_Warn( p_demux, "track[0x%x] will be disabled " + "(cannot restart decoder)", p_track->i_track_ID ); + MP4_TrackUnselect( p_demux, p_track ); + return VLC_EGENERIC; + } + } + + /* 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_demux->p_sys; + MP4_Box_data_elst_t *elst = p_track->p_elst->data.p_elst; + int64_t i_mvt = MP4_TrackGetPTS( p_demux, 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_demux, p_track, + MP4_TrackGetPTS( p_demux, p_track ) ); } } - return( VLC_SUCCESS ); + return VLC_SUCCESS; } +static void MP4_TrackSetELST( demux_t *p_demux, mp4_track_t *tk, + int64_t i_time ) +{ + demux_sys_t *p_sys = p_demux->p_sys; + 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_demux, "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_demux, "elst old=%d new=%d", i_elst_last, tk->i_elst ); + } +}