X-Git-Url: https://git.sesse.net/?a=blobdiff_plain;f=modules%2Fdemux%2Fmp4%2Fmp4.c;h=90d1b9b8cff33b34b2722a8edf8bd13c9cba1086;hb=4dcaa605095cb451ee8cdf7cef10c02e85ffcfa3;hp=5b76eead962b8382250f38006222bfdf41475fcb;hpb=dc437b8d2222d93316e71cb5e7d6a6c085eba27a;p=vlc diff --git a/modules/demux/mp4/mp4.c b/modules/demux/mp4/mp4.c index 5b76eead96..90d1b9b8cf 100644 --- a/modules/demux/mp4/mp4.c +++ b/modules/demux/mp4/mp4.c @@ -1,7 +1,7 @@ /***************************************************************************** * mp4.c : MP4 file input module for vlc ***************************************************************************** - * Copyright (C) 2001-2004 VideoLAN + * Copyright (C) 2001-2004 the VideoLAN team * $Id$ * Authors: Laurent Aimar * @@ -17,19 +17,27 @@ * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software - * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111, USA. + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston MA 02110-1301, USA. *****************************************************************************/ /***************************************************************************** * Preamble *****************************************************************************/ -#include /* malloc(), free() */ -#include -#include -#include -#include "iso_lang.h" -#include "vlc_meta.h" +#ifdef HAVE_CONFIG_H +# include "config.h" +#endif + +#include +#include + + +#include +#include +#include +#include +#include +#include #include "libmp4.h" #include "drms.h" @@ -45,21 +53,20 @@ static int Open ( vlc_object_t * ); static void Close( vlc_object_t * ); vlc_module_begin(); - set_description( _("MP4 stream demuxer") ); - set_capability( "demux2", 242 ); + set_category( CAT_INPUT ); + set_subcategory( SUBCAT_INPUT_DEMUX ); + set_description( N_("MP4 stream demuxer") ); + set_capability( "demux", 242 ); set_callbacks( Open, Close ); vlc_module_end(); /***************************************************************************** * Local prototypes *****************************************************************************/ -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 ); +static int Demux ( demux_t * ); +static int DemuxRef( demux_t *p_demux ){ (void)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 @@ -75,22 +82,25 @@ typedef struct /* 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 */ + uint32_t *p_sample_delta_dts; /* dts delta */ + + uint32_t *p_sample_count_pts; + int32_t *p_sample_offset_pts; /* pts-dts */ /* 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 */ + unsigned 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 */ + int b_ok; /* The track is usable */ + int b_enable; /* is the trak enable by default */ + bool b_selected; /* is the trak being played */ + bool b_chapter; /* True when used for chapter only */ es_format_t fmt; es_out_id_t *p_es; @@ -127,7 +137,7 @@ typedef struct 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; + bool b_drms; void *p_drms; } mp4_track_t; @@ -139,20 +149,25 @@ struct demux_sys_t 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 */ -}; + 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 */ + /* */ + MP4_Box_t *p_tref_chap; + + /* */ + input_title_t *p_title; +}; /***************************************************************************** * Declaration of local function *****************************************************************************/ -static void MP4_TrackCreate ( demux_t *, mp4_track_t *, MP4_Box_t *); -static void MP4_TrackDestroy( demux_t *, mp4_track_t * ); +static void MP4_TrackCreate ( demux_t *, mp4_track_t *, MP4_Box_t *, bool b_force_enable ); +static void MP4_TrackDestroy( mp4_track_t * ); static int MP4_TrackSelect ( demux_t *, mp4_track_t *, mtime_t ); static void MP4_TrackUnselect(demux_t *, mp4_track_t * ); @@ -164,34 +179,36 @@ 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 ); -/* Return time in µs of a track */ -static inline int64_t MP4_TrackGetPTS( demux_t *p_demux, mp4_track_t *p_track ) +static void MP4_UpdateSeekpoint( demux_t * ); + +/* Return time in s of a track */ +static inline int64_t MP4_TrackGetDTS( demux_t *p_demux, mp4_track_t *p_track ) { - unsigned int i_sample; - unsigned int i_index; - int64_t i_dts; +#define chunk p_track->chunk[p_track->i_chunk] + + 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; - 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] ) + if( i_sample > 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_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 * - p_track->chunk[p_track->i_chunk].p_sample_delta_dts[i_index]; + 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 ) { @@ -207,19 +224,40 @@ static inline int64_t MP4_TrackGetPTS( demux_t *p_demux, mp4_track_t *p_track ) } /* add i_elst_time */ - i_dts += p_track->i_elst_time * p_track->i_timescale / p_sys->i_timescale; + 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; + return INT64_C(1000000) * i_dts / p_track->i_timescale; +} + +static inline int64_t MP4_TrackGetPTSDelta( mp4_track_t *p_track ) +{ + mp4_chunk_t *ck = &p_track->chunk[p_track->i_chunk]; + unsigned int i_index = 0; + unsigned int i_sample = p_track->i_sample - ck->i_sample_first; + + if( ck->p_sample_count_pts == NULL || ck->p_sample_offset_pts == NULL ) + return -1; + + for( i_index = 0;; i_index++ ) + { + if( i_sample < ck->p_sample_count_pts[i_index] ) + return ck->p_sample_offset_pts[i_index] * INT64_C(1000000) / + (int64_t)p_track->i_timescale; + + i_sample -= ck->p_sample_count_pts[i_index]; + } } + static inline int64_t MP4_GetMoviePTS(demux_sys_t *p_sys ) { - return (int64_t)1000000 * p_sys->i_time / p_sys->i_timescale; + return INT64_C(1000000) * p_sys->i_time / p_sys->i_timescale; } -#define FREE( p ) if( p ) { free( p ); (p) = NULL;} +static void LoadChapter( demux_t *p_demux ); /***************************************************************************** * Open: check file and initializes MP4 structures @@ -229,7 +267,7 @@ static int Open( vlc_object_t * p_this ) demux_t *p_demux = (demux_t *)p_this; demux_sys_t *p_sys; - uint8_t *p_peek; + const uint8_t *p_peek; MP4_Box_t *p_ftyp; MP4_Box_t *p_rmra; @@ -237,14 +275,12 @@ static int Open( vlc_object_t * p_this ) MP4_Box_t *p_trak; unsigned int i; - vlc_bool_t b_seekable; + bool b_seekable; + bool b_enabled_es; + + /* A little test to see if it could be a mp4 */ + if( stream_Peek( p_demux->s, &p_peek, 8 ) < 8 ) return VLC_EGENERIC; - /* a little test to see if it could be a mp4 */ - if( stream_Peek( p_demux->s, &p_peek, 8 ) < 8 ) - { - 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] ) ) { case FOURCC_ftyp: @@ -259,7 +295,6 @@ static int Open( vlc_object_t * p_this ) case VLC_FOURCC( 'p', 'n', 'o', 't' ): break; default: - msg_Warn( p_demux, "MP4 plugin discarded (not a valid file)" ); return VLC_EGENERIC; } @@ -325,85 +360,74 @@ static int Open( vlc_object_t * p_this ) if( ( p_rmra = MP4_BoxGet( p_sys->p_root, "/moov/rmra" ) ) ) { - playlist_t *p_playlist; int i_count = MP4_BoxCount( p_rmra, "rmda" ); int i; msg_Dbg( p_demux, "detected playlist mov file (%d ref)", i_count ); - p_playlist = - (playlist_t *)vlc_object_find( p_demux, - VLC_OBJECT_PLAYLIST, - FIND_ANYWHERE ); - if( p_playlist ) + input_thread_t * p_input = vlc_object_find( p_demux, VLC_OBJECT_INPUT, FIND_PARENT ); + input_item_t * p_current = input_GetItem( p_input ); + p_current->i_type = ITEM_TYPE_PLAYLIST; + + for( i = 0; i < i_count; i++ ) { - //p_playlist->pp_items[p_playlist->i_index]->b_autodeletion = VLC_TRUE; - for( i = 0; i < i_count; i++ ) + MP4_Box_t *p_rdrf = MP4_BoxGet( p_rmra, "rmda[%d]/rdrf", i ); + char *psz_ref; + uint32_t i_ref_type; + + if( !p_rdrf || !( psz_ref = strdup( p_rdrf->data.p_rdrf->psz_ref ) ) ) { - MP4_Box_t *p_rdrf = MP4_BoxGet( p_rmra, "rmda[%d]/rdrf", i ); - char *psz_ref; - uint32_t i_ref_type; - int i_position = p_playlist->i_index; + continue; + } + i_ref_type = p_rdrf->data.p_rdrf->i_ref_type; - if( !p_rdrf || !( psz_ref = p_rdrf->data.p_rdrf->psz_ref ) ) + 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_demux, "ignoring pseudo ref =`%s'", psz_ref ); continue; } - i_ref_type = p_rdrf->data.p_rdrf->i_ref_type; - - 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( !strncmp( psz_ref, "http://", 7 ) || + !strncmp( psz_ref, "rtsp://", 7 ) ) { - if( strstr( psz_ref, "qt5gateQT" ) ) - { - 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_demux, "adding ref = `%s'", psz_ref ); - playlist_Add( p_playlist, psz_ref, psz_ref, - PLAYLIST_APPEND, i_position ); - } - else - { - /* msg dbg relative ? */ - 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_demux->psz_path; - - 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_demux, "adding ref = `%s'", psz_absolute ); - playlist_Add( p_playlist, psz_absolute, psz_absolute, - PLAYLIST_APPEND, i_position ); - } + ; } else { - msg_Err( p_demux, "unknown ref type=%4.4s FIXME (send a bug report)", - (char*)&p_rdrf->data.p_rdrf->i_ref_type ); + char *psz_absolute; + char *psz_path = strdup( p_demux->psz_path ); + char *end = strrchr( psz_path, '/' ); + if( end ) end[1] = '\0'; + else *psz_path = '\0'; + + if( asprintf( &psz_absolute, "%s://%s%s", + p_demux->psz_access, psz_path, psz_ref ) < 0 ) + return VLC_ENOMEM; + + free( psz_ref ); + psz_ref = psz_absolute; + free( psz_path ); } + input_item_t *p_input; + msg_Dbg( p_demux, "adding ref = `%s'", psz_ref ); + p_input = input_ItemNewExt( p_demux, psz_ref, NULL, + 0, NULL, -1 ); + input_ItemCopyOptions( p_current, p_input ); + input_ItemAddSubItem( p_current, p_input ); + vlc_gc_decref( p_input ); } - vlc_object_release( p_playlist ); - } - else - { - msg_Err( p_demux, "can't find playlist" ); + else + { + msg_Err( p_demux, "unknown ref type=%4.4s FIXME (send a bug report)", + (char*)&p_rdrf->data.p_rdrf->i_ref_type ); + } + free( psz_ref ); } + vlc_object_release( p_input ); } if( !(p_mvhd = MP4_BoxGet( p_sys->p_root, "/moov/mvhd" ) ) ) @@ -423,6 +447,11 @@ static int Open( vlc_object_t * p_this ) else { p_sys->i_timescale = p_mvhd->data.p_mvhd->i_timescale; + if( p_sys->i_timescale == 0 ) + { + msg_Err( p_this, "bad timescale" ); + goto error; + } p_sys->i_duration = p_mvhd->data.p_mvhd->i_duration; } @@ -431,23 +460,43 @@ static int Open( vlc_object_t * p_this ) msg_Err( p_demux, "cannot find any /moov/trak" ); goto error; } - msg_Dbg( p_demux, "find %d track%c", + msg_Dbg( p_demux, "found %d track%c", p_sys->i_tracks, p_sys->i_tracks ? 's':' ' ); /* allocate memory */ p_sys->track = calloc( p_sys->i_tracks, sizeof( mp4_track_t ) ); + if( p_sys->track == NULL ) + goto error; memset( p_sys->track, 0, p_sys->i_tracks * sizeof( mp4_track_t ) ); + /* Search the first chap reference (like quicktime) and + * check that at least 1 stream is enabled */ + p_sys->p_tref_chap = NULL; + b_enabled_es = false; + for( i = 0; i < p_sys->i_tracks; i++ ) + { + MP4_Box_t *p_trak = MP4_BoxGet( p_sys->p_root, "/moov/trak[%d]", i ); + + + MP4_Box_t *p_tkhd = MP4_BoxGet( p_trak, "tkhd" ); + if( p_tkhd && (p_tkhd->data.p_tkhd->i_flags&MP4_TRACK_ENABLED) ) + b_enabled_es = true; + + MP4_Box_t *p_chap = MP4_BoxGet( p_trak, "tref/chap", i ); + if( p_chap && p_chap->data.p_tref_generic->i_entry_count > 0 && !p_sys->p_tref_chap ) + p_sys->p_tref_chap = p_chap; + } + /* 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_demux, &p_sys->track[i], p_trak ); + MP4_TrackCreate( p_demux, &p_sys->track[i], p_trak, !b_enabled_es ); - if( p_sys->track[i].b_ok ) + if( p_sys->track[i].b_ok && !p_sys->track[i].b_chapter ) { - char *psz_cat; + const char *psz_cat; switch( p_sys->track[i].fmt.i_cat ) { case( VIDEO_ES ): @@ -466,17 +515,28 @@ static int Open( vlc_object_t * p_this ) } 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" ); + 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 if( p_sys->track[i].b_ok && p_sys->track[i].b_chapter ) + { + msg_Dbg( p_demux, "using track[Id 0x%x] for chapter language %s", + p_sys->track[i].i_track_ID, + p_sys->track[i].fmt.psz_language ? + p_sys->track[i].fmt.psz_language : "undef" ); } else { - msg_Dbg( p_demux, "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 ); } - } + + /* */ + LoadChapter( p_demux ); + return VLC_SUCCESS; error: @@ -506,30 +566,28 @@ static int Demux( demux_t *p_demux ) i_track++ ) { mp4_track_t *tk = &p_sys->track[i_track]; + bool b; - if( tk->b_selected && tk->i_sample >= tk->i_sample_count ) + if( !tk->b_ok || tk->b_chapter || + ( tk->b_selected && tk->i_sample >= tk->i_sample_count ) ) { - msg_Warn( p_demux, "track[0x%x] will be disabled", tk->i_track_ID ); - MP4_TrackUnselect( p_demux, tk); + continue; } - else if( tk->b_ok ) - { - vlc_bool_t b; - es_out_Control( p_demux->out, ES_OUT_GET_ES_STATE, tk->p_es, &b ); - if( tk->b_selected && !b ) - { - MP4_TrackUnselect( p_demux, tk ); - } - else if( !tk->b_selected && b) - { - MP4_TrackSelect( p_demux, tk, MP4_GetMoviePTS( p_sys ) ); - } + es_out_Control( p_demux->out, ES_OUT_GET_ES_STATE, tk->p_es, &b ); - if( tk->b_selected ) - { - i_track_selected++; - } + if( tk->b_selected && !b ) + { + MP4_TrackUnselect( p_demux, tk ); + } + else if( !tk->b_selected && b) + { + MP4_TrackSelect( p_demux, tk, MP4_GetMoviePTS( p_sys ) ); + } + + if( tk->b_selected ) + { + i_track_selected++; } } @@ -550,6 +608,9 @@ static int Demux( demux_t *p_demux ) return 0; } + /* */ + MP4_UpdateSeekpoint( p_demux ); + /* first wait for the good time to read a packet */ es_out_Control( p_demux->out, ES_OUT_SET_PCR, p_sys->i_pcr + 1 ); @@ -562,49 +623,50 @@ static int Demux( demux_t *p_demux ) { mp4_track_t *tk = &p_sys->track[i_track]; - if( !tk->b_ok || !tk->b_selected ) - { + if( !tk->b_ok || tk->b_chapter || !tk->b_selected || tk->i_sample >= tk->i_sample_count ) continue; - } - while( MP4_TrackGetPTS( p_demux, tk ) < MP4_GetMoviePTS( p_sys ) ) + while( MP4_TrackGetDTS( p_demux, tk ) < MP4_GetMoviePTS( p_sys ) ) { #if 0 - msg_Dbg( p_demux, "tk=%lld mv=%lld", - MP4_TrackGetPTS( p_demux, tk ), + msg_Dbg( p_demux, "tk(%i)=%lld mv=%lld", i_track, + MP4_TrackGetDTS( p_demux, tk ), MP4_GetMoviePTS( p_sys ) ); #endif if( MP4_TrackSampleSize( tk ) > 0 ) { block_t *p_block; + int64_t i_delta; /* go,go go ! */ if( stream_Seek( p_demux->s, MP4_TrackGetPos( tk ) ) ) { - msg_Warn( p_demux, "track[0x%x] will be disabled (eof?)", tk->i_track_ID ); + 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_demux->s, - MP4_TrackSampleSize( tk ) ) ) == NULL ) + if( !(p_block = + stream_Block( p_demux->s, MP4_TrackSampleSize(tk) )) ) { - msg_Warn( p_demux, "track[0x%x] will be disabled (eof?)", tk->i_track_ID ); + msg_Warn( p_demux, "track[0x%x] will be disabled (eof?)", + tk->i_track_ID ); MP4_TrackUnselect( p_demux, tk ); break; } if( tk->b_drms && tk->p_drms ) { - drms_decrypt( tk->p_drms, - (uint32_t*)p_block->p_buffer, + drms_decrypt( tk->p_drms, (uint32_t*)p_block->p_buffer, p_block->i_buffer ); } else if( tk->fmt.i_cat == SPU_ES ) { - if( tk->fmt.i_codec == VLC_FOURCC( 's', 'u', 'b', 't' ) && p_block->i_buffer >= 2 ) + 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 ); @@ -612,12 +674,13 @@ static int Demux( demux_t *p_demux ) { char *p; /* remove the length field, and append a '\0' */ - memmove( &p_block->p_buffer[0], &p_block->p_buffer[2], i_size ); + 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' ) ) ) + while( ( p = strchr((char *) p_block->p_buffer, '\r' ) ) ) { *p = '\n'; } @@ -629,26 +692,51 @@ static int Demux( demux_t *p_demux ) } } } - 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; + /* dts */ + p_block->i_dts = MP4_TrackGetDTS( p_demux, tk ) + 1; + /* pts */ + i_delta = MP4_TrackGetPTSDelta( tk ); + if( i_delta != -1 ) + p_block->i_pts = p_block->i_dts + i_delta; + else if( tk->fmt.i_cat != VIDEO_ES ) + p_block->i_pts = p_block->i_dts; + else + p_block->i_pts = 0; if( !tk->b_drms || ( tk->b_drms && tk->p_drms ) ) - { es_out_Send( p_demux->out, tk->p_es, p_block ); - } } /* Next sample */ if( MP4_TrackNextSample( p_demux, tk ) ) - { break; - } } } return 1; } + +static void MP4_UpdateSeekpoint( demux_t *p_demux ) +{ + demux_sys_t *p_sys = p_demux->p_sys; + int64_t i_time; + int i; + if( !p_sys->p_title ) + return; + i_time = MP4_GetMoviePTS( p_sys ); + for( i = 0; i < p_sys->p_title->i_seekpoint; i++ ) + { + if( i_time < p_sys->p_title->seekpoint[i]->i_time_offset ) + break; + } + i--; + + if( i != p_demux->info.i_seekpoint && i >= 0 ) + { + p_demux->info.i_seekpoint = i; + p_demux->info.i_update |= INPUT_UPDATE_SEEKPOINT; + } +} /***************************************************************************** * Seek: Got to i_date ******************************************************************************/ @@ -667,6 +755,7 @@ static int Seek( demux_t *p_demux, mtime_t i_date ) mp4_track_t *tk = &p_sys->track[i_track]; MP4_TrackSeek( p_demux, tk, i_date ); } + MP4_UpdateSeekpoint( p_demux ); return VLC_SUCCESS; } @@ -737,58 +826,81 @@ static int Control( demux_t *p_demux, int i_query, va_list args ) 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" ); + vlc_meta_t *p_meta = (vlc_meta_t *)va_arg( args, vlc_meta_t*); MP4_Box_t *p_0xa9xxx; + + MP4_Box_t *p_udta = MP4_BoxGet( p_sys->p_root, "/moov/udta/meta/ilst" ); if( p_udta == NULL ) { - return VLC_EGENERIC; + p_udta = MP4_BoxGet( p_sys->p_root, "/moov/udta" ); + 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 ) { + + if( !p_0xa9xxx || !p_0xa9xxx->data.p_0xa9xxx ) + continue; + + /* FIXME FIXME: should convert from whatever the character + * encoding of MP4 meta data is to UTF-8. */ +#define SET(fct) do { char *psz_utf = strdup( p_0xa9xxx->data.p_0xa9xxx->psz_text ? p_0xa9xxx->data.p_0xa9xxx->psz_text : "" ); \ + if( psz_utf ) { EnsureUTF8( psz_utf ); \ + fct( p_meta, psz_utf ); free( psz_utf ); } } while(0) + + /* XXX Becarefull p_udta can have box that are not 0xa9xx */ switch( p_0xa9xxx->i_type ) { case FOURCC_0xa9nam: /* Full name */ - vlc_meta_Add( meta, VLC_META_TITLE, - p_0xa9xxx->data.p_0xa9xxx->psz_text ); + SET( vlc_meta_SetTitle ); break; case FOURCC_0xa9aut: - vlc_meta_Add( meta, VLC_META_AUTHOR, - p_0xa9xxx->data.p_0xa9xxx->psz_text ); + SET( vlc_meta_SetArtist ); break; case FOURCC_0xa9ART: - vlc_meta_Add( meta, VLC_META_ARTIST, - p_0xa9xxx->data.p_0xa9xxx->psz_text ); + SET( vlc_meta_SetArtist ); break; case FOURCC_0xa9cpy: - vlc_meta_Add( meta, VLC_META_COPYRIGHT, - p_0xa9xxx->data.p_0xa9xxx->psz_text ); + SET( vlc_meta_SetCopyright ); break; case FOURCC_0xa9day: /* Creation Date */ - vlc_meta_Add( meta, VLC_META_DATE, - p_0xa9xxx->data.p_0xa9xxx->psz_text ); + SET( vlc_meta_SetDate ); break; case FOURCC_0xa9des: /* Description */ - vlc_meta_Add( meta, VLC_META_DESCRIPTION, - p_0xa9xxx->data.p_0xa9xxx->psz_text ); + SET( vlc_meta_SetDescription ); break; case FOURCC_0xa9gen: /* Genre */ - vlc_meta_Add( meta, VLC_META_GENRE, - p_0xa9xxx->data.p_0xa9xxx->psz_text ); + SET( vlc_meta_SetGenre ); 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 */ + SET( vlc_meta_SetAlbum ); + break; + case FOURCC_0xa9trk: /* Track */ + SET( vlc_meta_SetTracknum ); + break; + case FOURCC_0xa9cmt: /* Commment */ + SET( vlc_meta_SetDescription ); + break; + case FOURCC_0xa9url: /* URL */ + SET( vlc_meta_SetURL ); + break; + + case FOURCC_0xa9enc: /* Encoded By */ + SET( vlc_meta_SetEncodedBy ); + break; + + case FOURCC_0xa9swr: + case FOURCC_0xa9inf: /* Information */ + case FOURCC_0xa9dir: /* Director */ + case FOURCC_0xa9dis: /* Disclaimer */ case FOURCC_0xa9req: /* Requirements */ case FOURCC_0xa9fmt: /* Original Format */ case FOURCC_0xa9dsa: /* Display Source As */ @@ -802,6 +914,7 @@ static int Control( demux_t *p_demux, int i_query, va_list args ) case FOURCC_WLOC: /* Window Location */ /* TODO one day, but they aren't really meaningfull */ break; +#undef SET default: break; @@ -811,12 +924,45 @@ static int Control( demux_t *p_demux, int i_query, va_list args ) } case DEMUX_GET_TITLE_INFO: + { + input_title_t ***ppp_title = (input_title_t***)va_arg( args, input_title_t*** ); + int *pi_int = (int*)va_arg( args, int* ); + int *pi_title_offset = (int*)va_arg( args, int* ); + int *pi_seekpoint_offset = (int*)va_arg( args, int* ); + + if( !p_sys->p_title ) + return VLC_EGENERIC; + + *pi_int = 1; + *ppp_title = malloc( sizeof( input_title_t**) ); + (*ppp_title)[0] = vlc_input_title_Duplicate( p_sys->p_title ); + *pi_title_offset = 0; + *pi_seekpoint_offset = 0; + return VLC_SUCCESS; + } + case DEMUX_SET_TITLE: + { + const int i_title = (int)va_arg( args, int ); + if( !p_sys->p_title || i_title != 0 ) + return VLC_EGENERIC; + return VLC_SUCCESS; + } + case DEMUX_SET_SEEKPOINT: + { + const int i_seekpoint = (int)va_arg( args, int ); + if( !p_sys->p_title ) + return VLC_EGENERIC; + return Seek( p_demux, p_sys->p_title->seekpoint[i_seekpoint]->i_time_offset ); + } + case DEMUX_SET_NEXT_DEMUX_TIME: case DEMUX_SET_GROUP: + case DEMUX_HAS_UNSUPPORTED_META: + case DEMUX_GET_ATTACHMENTS: return VLC_EGENERIC; default: - msg_Warn( p_demux, "control query unimplemented !!!" ); + msg_Warn( p_demux, "control query %u unimplemented", i_query ); return VLC_EGENERIC; } } @@ -826,18 +972,21 @@ static int Control( demux_t *p_demux, int i_query, va_list args ) *****************************************************************************/ static void Close ( vlc_object_t * p_this ) { - unsigned int i_track; demux_t * p_demux = (demux_t *)p_this; demux_sys_t *p_sys = p_demux->p_sys; + unsigned int i_track; msg_Dbg( p_demux, "freeing all memory" ); MP4_BoxFree( p_demux->s, p_sys->p_root ); for( i_track = 0; i_track < p_sys->i_tracks; i_track++ ) { - MP4_TrackDestroy( p_demux, &p_sys->track[i_track] ); + MP4_TrackDestroy( &p_sys->track[i_track] ); } - FREE( p_sys->track ); + FREENULL( p_sys->track ); + + if( p_sys->p_title ) + vlc_input_title_Delete( p_sys->p_title ); free( p_sys ); } @@ -847,6 +996,90 @@ static void Close ( vlc_object_t * p_this ) /**************************************************************************** * Local functions, specific to vlc ****************************************************************************/ +/* Chapters */ +static void LoadChapterGpac( demux_t *p_demux, MP4_Box_t *p_chpl ) +{ + demux_sys_t *p_sys = p_demux->p_sys; + int i; + + p_sys->p_title = vlc_input_title_New(); + for( i = 0; i < p_chpl->data.p_chpl->i_chapter; i++ ) + { + seekpoint_t *s = vlc_seekpoint_New(); + + s->psz_name = strdup( p_chpl->data.p_chpl->chapter[i].psz_name ); + EnsureUTF8( s->psz_name ); + s->i_time_offset = p_chpl->data.p_chpl->chapter[i].i_start / 10; + TAB_APPEND( p_sys->p_title->i_seekpoint, p_sys->p_title->seekpoint, s ); + } +} +static void LoadChapterApple( demux_t *p_demux, mp4_track_t *tk ) +{ + demux_sys_t *p_sys = p_demux->p_sys; + + for( tk->i_sample = 0; tk->i_sample < tk->i_sample_count; tk->i_sample++ ) + { + const int64_t i_dts = MP4_TrackGetDTS( p_demux, tk ); + const int64_t i_pts_delta = MP4_TrackGetPTSDelta( tk ); + const unsigned int i_size = MP4_TrackSampleSize( tk ); + + if( i_size > 0 && !stream_Seek( p_demux->s, MP4_TrackGetPos( tk ) ) ) + { + char p_buffer[256]; + const int i_read = stream_Read( p_demux->s, p_buffer, __MIN( sizeof(p_buffer), i_size ) ); + const int i_len = __MIN( GetWBE(p_buffer), i_read-2 ); + + if( i_len > 0 ) + { + seekpoint_t *s = vlc_seekpoint_New(); + + s->psz_name = strndup( &p_buffer[2], i_len ); + EnsureUTF8( s->psz_name ); + + s->i_time_offset = i_dts + __MAX( i_pts_delta, 0 ); + + if( !p_sys->p_title ) + p_sys->p_title = vlc_input_title_New(); + TAB_APPEND( p_sys->p_title->i_seekpoint, p_sys->p_title->seekpoint, s ); + } + } + if( tk->i_sample+1 >= tk->chunk[tk->i_chunk].i_sample_first + + tk->chunk[tk->i_chunk].i_sample_count ) + tk->i_chunk++; + } +} +static void LoadChapter( demux_t *p_demux ) +{ + demux_sys_t *p_sys = p_demux->p_sys; + MP4_Box_t *p_chpl; + + if( ( p_chpl = MP4_BoxGet( p_sys->p_root, "/moov/udta/chpl" ) ) && p_chpl->data.p_chpl->i_chapter > 0 ) + { + LoadChapterGpac( p_demux, p_chpl ); + } + else if( p_sys->p_tref_chap ) + { + MP4_Box_data_tref_generic_t *p_chap = p_sys->p_tref_chap->data.p_tref_generic; + unsigned int i, j; + + /* Load the first subtitle track like quicktime */ + for( i = 0; i < p_chap->i_entry_count; i++ ) + { + for( j = 0; j < p_sys->i_tracks; j++ ) + { + mp4_track_t *tk = &p_sys->track[j]; + if( tk->b_ok && tk->i_track_ID == p_chap->i_track_ID[i] && + tk->fmt.i_cat == SPU_ES && tk->fmt.i_codec == VLC_FOURCC( 's', 'u', 'b', 't' ) ) + break; + } + if( j < p_sys->i_tracks ) + { + LoadChapterApple( p_demux, &p_sys->track[i] ); + break; + } + } + } +} /* now create basic chunk data, the rest will be filled by MP4_CreateSamplesIndex */ static int TrackCreateChunksIndex( demux_t *p_demux, @@ -873,12 +1106,23 @@ static int TrackCreateChunksIndex( demux_t *p_demux, } p_demux_track->chunk = calloc( p_demux_track->i_chunk_count, sizeof( mp4_chunk_t ) ); + if( p_demux_track->chunk == NULL ) + { + return VLC_ENOMEM; + } /* first we read chunk offset */ for( i_chunk = 0; i_chunk < p_demux_track->i_chunk_count; i_chunk++ ) { - p_demux_track->chunk[i_chunk].i_offset = - p_co64->data.p_co64->i_chunk_offset[i_chunk]; + mp4_chunk_t *ck = &p_demux_track->chunk[i_chunk]; + + ck->i_offset = p_co64->data.p_co64->i_chunk_offset[i_chunk]; + + ck->i_first_dts = 0; + ck->p_sample_count_dts = NULL; + ck->p_sample_delta_dts = NULL; + ck->p_sample_count_pts = NULL; + ck->p_sample_offset_pts = NULL; } /* now we read index for SampleEntry( soun vide mp4a mp4v ...) @@ -892,12 +1136,17 @@ static int TrackCreateChunksIndex( demux_t *p_demux, 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++ ) { + if( i_chunk >= p_demux_track->i_chunk_count ) + { + msg_Warn( p_demux, "corrupted chunk table" ); + return VLC_EGENERIC; + } + p_demux_track->chunk[i_chunk].i_sample_description_index = p_stsc->data.p_stsc->i_sample_description_index[i_index]; p_demux_track->chunk[i_chunk].i_sample_count = @@ -914,22 +1163,18 @@ static int TrackCreateChunksIndex( demux_t *p_demux, p_demux_track->chunk[i_chunk-1].i_sample_count; } - msg_Dbg( p_demux, - "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( 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, - not yet used and probably not usefull */ - MP4_Box_t *p_stsz; /* gives sample size of each samples, there is also stz2 - that uses a compressed form FIXME make them in libmp4 - as a unique type */ + MP4_Box_t *p_box; + MP4_Box_data_stsz_t *stsz; + MP4_Box_data_stts_t *stts; /* TODO use also stss and stsh table for seeking */ /* FIXME use edit table */ int64_t i_sample; @@ -940,23 +1185,35 @@ static int TrackCreateSamplesIndex( demux_t *p_demux, int64_t i_last_dts; - p_stts = MP4_BoxGet( p_demux_track->p_stbl, "stts" ); - p_stsz = MP4_BoxGet( p_demux_track->p_stbl, "stsz" ); /* FIXME and stz2 */ - - if( ( !p_stts )||( !p_stsz ) ) + /* Find stsz + * Gives the sample size for each samples. There is also a stz2 table + * (compressed form) that we need to implement TODO */ + p_box = MP4_BoxGet( p_demux_track->p_stbl, "stsz" ); + if( !p_box ) { - msg_Warn( p_demux, "cannot read sample table" ); - return( VLC_EGENERIC ); + /* FIXME and stz2 */ + msg_Warn( p_demux, "cannot find STSZ box" ); + return VLC_EGENERIC; } + stsz = p_box->data.p_stsz; - p_demux_track->i_sample_count = p_stsz->data.p_stsz->i_sample_count; - + /* Find stts + * Gives mapping between sample and decoding time + */ + p_box = MP4_BoxGet( p_demux_track->p_stbl, "stts" ); + if( !p_box ) + { + msg_Warn( p_demux, "cannot find STTS box" ); + return VLC_EGENERIC; + } + stts = p_box->data.p_stts; - /* for sample size, there are 2 case */ - if( p_stsz->data.p_stsz->i_sample_size ) + /* Use stsz table to create a sample number -> sample size table */ + p_demux_track->i_sample_count = stsz->i_sample_count; + if( stsz->i_sample_size ) { /* 1: all sample have the same size, so no need to construct a table */ - p_demux_track->i_sample_size = p_stsz->data.p_stsz->i_sample_size; + p_demux_track->i_sample_size = stsz->i_sample_size; p_demux_track->p_sample_size = NULL; } else @@ -965,92 +1222,147 @@ static int TrackCreateSamplesIndex( demux_t *p_demux, p_demux_track->i_sample_size = 0; p_demux_track->p_sample_size = calloc( p_demux_track->i_sample_count, sizeof( uint32_t ) ); + if( p_demux_track->p_sample_size == NULL ) + return VLC_ENOMEM; for( i_sample = 0; i_sample < p_demux_track->i_sample_count; i_sample++ ) { p_demux_track->p_sample_size[i_sample] = - p_stsz->data.p_stsz->i_entry_size[i_sample]; + stsz->i_entry_size[i_sample]; } } - /* we have extract all 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 - for fast research */ + /* Use stts table to create a sample number -> dts table. + * XXX: 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 + * for fast research (problem with raw stream where a sample is sometime + * just channels*bits_per_sample/8 */ i_last_dts = 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++ ) + i_index = 0; i_index_sample_used = 0; + for( i_chunk = 0; i_chunk < p_demux_track->i_chunk_count; i_chunk++ ) { - + mp4_chunk_t *ck = &p_demux_track->chunk[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 */ + ck->i_first_dts = i_last_dts; - i_sample_count = p_demux_track->chunk[i_chunk].i_sample_count; + /* count how many entries are needed for this chunk + * for p_sample_delta_dts and p_sample_count_dts */ + i_sample_count = ck->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 -= stts->i_sample_count[i_index+i_entry]; + /* don't count already used sample in this entry */ if( i_entry == 0 ) - { - i_sample_count += i_index_sample_used; /* don't count already used sample - int this entry */ - } + i_sample_count += i_index_sample_used; + i_entry++; } /* allocate them */ - p_demux_track->chunk[i_chunk].p_sample_count_dts = - calloc( i_entry, sizeof( uint32_t ) ); - p_demux_track->chunk[i_chunk].p_sample_delta_dts = - calloc( i_entry, sizeof( uint32_t ) ); + ck->p_sample_count_dts = calloc( i_entry, sizeof( uint32_t ) ); + ck->p_sample_delta_dts = calloc( i_entry, sizeof( uint32_t ) ); /* now copy */ - i_sample_count = p_demux_track->chunk[i_chunk].i_sample_count; + i_sample_count = ck->i_sample_count; for( i = 0; i < i_entry; i++ ) { 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 = stts->i_sample_count[i_index] - i_index_sample_used; i_used = __MIN( i_rest, i_sample_count ); i_index_sample_used += i_used; i_sample_count -= i_used; - p_demux_track->chunk[i_chunk].p_sample_count_dts[i] = i_used; + ck->p_sample_count_dts[i] = i_used; + ck->p_sample_delta_dts[i] = stts->i_sample_delta[i_index]; - p_demux_track->chunk[i_chunk].p_sample_delta_dts[i] = - p_stts->data.p_stts->i_sample_delta[i_index]; + i_last_dts += i_used * ck->p_sample_delta_dts[i]; - i_last_dts += i_used * - 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] ) + if( i_index_sample_used >= stts->i_sample_count[i_index] ) { - i_index++; i_index_sample_used = 0; } } + } + + /* Find ctts + * Gives the delta between decoding time (dts) and composition table (pts) + */ + p_box = MP4_BoxGet( p_demux_track->p_stbl, "ctts" ); + if( p_box ) + { + MP4_Box_data_ctts_t *ctts = p_box->data.p_ctts; + msg_Warn( p_demux, "CTTS table" ); + + /* Create pts-dts table per chunk */ + i_index = 0; i_index_sample_used = 0; + for( i_chunk = 0; i_chunk < p_demux_track->i_chunk_count; i_chunk++ ) + { + mp4_chunk_t *ck = &p_demux_track->chunk[i_chunk]; + int64_t i_entry, i_sample_count, i; + + /* count how many entries are needed for this chunk + * for p_sample_delta_dts and p_sample_count_dts */ + i_sample_count = ck->i_sample_count; + + i_entry = 0; + while( i_sample_count > 0 ) + { + i_sample_count -= ctts->i_sample_count[i_index+i_entry]; + + /* don't count already used sample in this entry */ + if( i_entry == 0 ) + i_sample_count += i_index_sample_used; + + i_entry++; + } + + /* allocate them */ + ck->p_sample_count_pts = calloc( i_entry, sizeof( uint32_t ) ); + ck->p_sample_offset_pts = calloc( i_entry, sizeof( int32_t ) ); + + /* now copy */ + i_sample_count = ck->i_sample_count; + for( i = 0; i < i_entry; i++ ) + { + int64_t i_used; + int64_t i_rest; + + i_rest = ctts->i_sample_count[i_index] - + i_index_sample_used; + + i_used = __MIN( i_rest, i_sample_count ); + + i_index_sample_used += i_used; + i_sample_count -= i_used; + + ck->p_sample_count_pts[i] = i_used; + ck->p_sample_offset_pts[i] = ctts->i_sample_offset[i_index]; + + if( i_index_sample_used >= ctts->i_sample_count[i_index] ) + { + i_index++; + i_index_sample_used = 0; + } + } + } } - 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, + msg_Dbg( p_demux, "track[Id 0x%x] read %d samples length:%"PRId64"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; } /* @@ -1062,13 +1374,14 @@ static int TrackCreateES( demux_t *p_demux, mp4_track_t *p_track, { MP4_Box_t *p_sample; MP4_Box_t *p_esds; + MP4_Box_t *p_box; - *pp_es = NULL; + if( pp_es ) + *pp_es = NULL; if( !p_track->chunk[i_chunk].i_sample_description_index ) { - msg_Warn( p_demux, - "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; } @@ -1076,17 +1389,17 @@ static int TrackCreateES( demux_t *p_demux, mp4_track_t *p_track, 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 && p_track->fmt.i_cat != SPU_ES ) ) + if( !p_sample || + ( !p_sample->data.p_data && p_track->fmt.i_cat != SPU_ES ) ) { - msg_Warn( p_demux, - "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->fmt.i_cat == AUDIO_ES && p_track->i_sample_size == 1 ) + if( p_track->fmt.i_cat == AUDIO_ES && ( p_track->i_sample_size == 1 || p_track->i_sample_size == 2 ) ) { MP4_Box_data_sample_soun_t *p_soun; @@ -1121,6 +1434,13 @@ static int TrackCreateES( demux_t *p_demux, mp4_track_t *p_track, case VLC_FOURCC( 'u', 'l', 'a', 'w' ): p_soun->i_samplesize = 8; break; + case VLC_FOURCC( 'N', 'O', 'N', 'E' ): + case VLC_FOURCC( 'r', 'a', 'w', ' ' ): + case VLC_FOURCC( 't', 'w', 'o', 's' ): + case VLC_FOURCC( 's', 'o', 'w', 't' ): + /* What would be the fun if you could trust the .mov */ + p_track->i_sample_size = ((p_soun->i_samplesize+7)/8) * p_soun->i_channelcount; + break; default: break; } @@ -1132,7 +1452,6 @@ static int TrackCreateES( demux_t *p_demux, mp4_track_t *p_track, } } - /* It's a little ugly but .. there are special cases */ switch( p_sample->i_type ) { @@ -1140,18 +1459,64 @@ static int TrackCreateES( demux_t *p_demux, mp4_track_t *p_track, case( VLC_FOURCC( 'm', 's', 0x00, 0x55 ) ): p_track->fmt.i_codec = VLC_FOURCC( 'm', 'p', 'g', 'a' ); break; + case( VLC_FOURCC( 'r', 'a', 'w', ' ' ) ): - p_track->fmt.i_codec = VLC_FOURCC( 'a', 'r', 'a', 'w' ); + case( VLC_FOURCC( 'N', 'O', 'N', 'E' ) ): + { + MP4_Box_data_sample_soun_t *p_soun = p_sample->data.p_sample_soun; + + if(p_soun && (p_soun->i_samplesize+7)/8 == 1 ) + p_track->fmt.i_codec = VLC_FOURCC( 'u', '8', ' ', ' ' ); + else + p_track->fmt.i_codec = VLC_FOURCC( 't', 'w', 'o', 's' ); + + /* Buggy files workaround */ + if( p_sample->data.p_sample_soun && (p_track->i_timescale != + p_sample->data.p_sample_soun->i_sampleratehi) ) + { + MP4_Box_data_sample_soun_t *p_soun = + p_sample->data.p_sample_soun; + + msg_Warn( p_demux, "i_timescale (%"PRIu64") != i_sampleratehi " + "(%u), making both equal (report any problem).", + p_track->i_timescale, p_soun->i_sampleratehi ); + + if( p_soun->i_sampleratehi ) + p_track->i_timescale = p_soun->i_sampleratehi; + else + p_soun->i_sampleratehi = p_track->i_timescale; + } break; + } + 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' ) ): + case( VLC_FOURCC( 't', 'x', '3', 'g' ) ): 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" ); */ + p_track->fmt.subs.psz_encoding = strdup( "UTF-8" ); + break; + + case VLC_FOURCC('y','v','1','2'): + p_track->fmt.i_codec = VLC_FOURCC('Y','V','1','2'); + break; + case VLC_FOURCC('y','u','v','2'): + p_track->fmt.i_codec = VLC_FOURCC('Y','U','Y','2'); + break; + + case VLC_FOURCC('i','n','2','4'): + /* in in24/in32 there's enda-atom to tell it's little-endian (if present) */ + if( ( MP4_BoxGet( p_sample, "wave/enda" ) ) || + ( MP4_BoxGet( p_sample, "enda" ) ) ) + { + p_track->fmt.i_codec = VLC_FOURCC('4','2','n','i'); + } else { + p_track->fmt.i_codec = p_sample->i_type; + } break; default: @@ -1203,14 +1568,34 @@ static int TrackCreateES( demux_t *p_demux, mp4_track_t *p_track, case( 0x6c ): /* jpeg */ p_track->fmt.i_codec = VLC_FOURCC( 'j','p','e','g' ); break; + case( 0x6d ): /* png */ + p_track->fmt.i_codec = VLC_FOURCC( 'p','n','g',' ' ); + break; + case( 0x6e ): /* jpeg200 */ + p_track->fmt.i_codec = VLC_FOURCC( 'M','J','2','C' ); + break; + case( 0xa3 ): /* vc1 */ + p_track->fmt.i_codec = VLC_FOURCC( 'W','V','C','1' ); + 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',' ' ); + if( p_track->i_width > 0 ) + p_track->fmt.subs.spu.i_original_frame_width = p_track->i_width; + if( p_track->i_height > 0 ) + p_track->fmt.subs.spu.i_original_frame_height = p_track->i_height; break; } + case( 0xe1 ): /* QCelp for 3gp */ + if( p_track->fmt.i_cat == AUDIO_ES ) + { + p_track->fmt.i_codec = VLC_FOURCC( 'Q','c','l','p' ); + } + break; + /* Fallback */ default: /* Unknown entry, but don't touch i_fourcc */ @@ -1233,6 +1618,21 @@ static int TrackCreateES( demux_t *p_demux, mp4_track_t *p_track, switch( p_sample->i_type ) { /* qt decoder, send the complete chunk */ + case VLC_FOURCC ('h', 'd', 'v', '1'): // HDV 720p30 + case VLC_FOURCC ('h', 'd', 'v', '2'): // HDV 1080i60 + case VLC_FOURCC ('h', 'd', 'v', '3'): // HDV 1080i50 + case VLC_FOURCC ('h', 'd', 'v', '5'): // HDV 720p25 + case VLC_FOURCC ('m', 'x', '5', 'n'): // MPEG2 IMX NTSC 525/60 50mb/s produced by FCP + case VLC_FOURCC ('m', 'x', '5', 'p'): // MPEG2 IMX PAL 625/60 50mb/s produced by FCP + case VLC_FOURCC ('m', 'x', '4', 'n'): // MPEG2 IMX NTSC 525/60 40mb/s produced by FCP + case VLC_FOURCC ('m', 'x', '4', 'p'): // MPEG2 IMX PAL 625/60 40mb/s produced by FCP + case VLC_FOURCC ('m', 'x', '3', 'n'): // MPEG2 IMX NTSC 525/60 30mb/s produced by FCP + case VLC_FOURCC ('m', 'x', '3', 'p'): // MPEG2 IMX PAL 625/50 30mb/s produced by FCP + case VLC_FOURCC ('x', 'd', 'v', '2'): // XDCAM HD 1080i60 + case VLC_FOURCC ('A', 'V', 'm', 'p'): // AVID IMX PAL + p_track->fmt.i_codec = VLC_FOURCC( 'm','p','g','v' ); + break; + /* qt decoder, send the complete chunk */ case VLC_FOURCC( 'S', 'V', 'Q', '3' ): case VLC_FOURCC( 'S', 'V', 'Q', '1' ): case VLC_FOURCC( 'V', 'P', '3', '1' ): @@ -1252,6 +1652,7 @@ static int TrackCreateES( demux_t *p_demux, mp4_track_t *p_track, case VLC_FOURCC( 'Q', 'D', 'M', '2' ): case VLC_FOURCC( 'Q', 'c', 'l', 'p' ): case VLC_FOURCC( 's', 'a', 'm', 'r' ): + case VLC_FOURCC( 'a', 'l', 'a', 'c' ): p_track->fmt.i_extra = p_sample->data.p_sample_soun->i_qt_description; if( p_track->fmt.i_extra > 0 ) @@ -1270,12 +1671,10 @@ static int TrackCreateES( demux_t *p_demux, mp4_track_t *p_track, 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; + memcpy( p_track->fmt.p_extra, p_avcC->data.p_avcC->p_avcC, + p_track->fmt.i_extra ); } else { @@ -1284,6 +1683,11 @@ static int TrackCreateES( demux_t *p_demux, mp4_track_t *p_track, break; } + case VLC_FOURCC('m','s',0x00,0x02): + case VLC_FOURCC('m','s',0x00,0x11): + p_track->fmt.audio.i_blockalign = p_sample->data.p_sample_soun->i_bytes_per_frame; + break; + default: break; } @@ -1297,6 +1701,8 @@ static int TrackCreateES( demux_t *p_demux, mp4_track_t *p_track, case( VIDEO_ES ): p_track->fmt.video.i_width = p_sample->data.p_sample_vide->i_width; p_track->fmt.video.i_height = p_sample->data.p_sample_vide->i_height; + p_track->fmt.video.i_bits_per_pixel = + p_sample->data.p_sample_vide->i_depth; /* fall on display size */ if( p_track->fmt.video.i_width <= 0 ) @@ -1311,6 +1717,22 @@ static int TrackCreateES( demux_t *p_demux, mp4_track_t *p_track, p_track->fmt.video.i_aspect = VOUT_ASPECT_FACTOR * p_track->i_width / p_track->i_height; + /* Support for cropping (eg. in H263 files) */ + p_track->fmt.video.i_visible_width = p_track->fmt.video.i_width; + p_track->fmt.video.i_visible_height = p_track->fmt.video.i_height; + + /* Frame rate */ + p_track->fmt.video.i_frame_rate = p_track->i_timescale; + p_track->fmt.video.i_frame_rate_base = 1; + + if( p_track->fmt.video.i_frame_rate && + (p_box = MP4_BoxGet( p_track->p_stbl, "stts" )) && + p_box->data.p_stts->i_entry_count >= 1 ) + { + p_track->fmt.video.i_frame_rate_base = + p_box->data.p_stts->i_sample_delta[0]; + } + break; case( AUDIO_ES ): @@ -1329,7 +1751,8 @@ static int TrackCreateES( demux_t *p_demux, mp4_track_t *p_track, break; } - *pp_es = es_out_Add( p_demux->out, &p_track->fmt ); + if( pp_es ) + *pp_es = es_out_Add( p_demux->out, &p_track->fmt ); return VLC_SUCCESS; } @@ -1349,10 +1772,8 @@ static int TrackTimeToSampleChunk( demux_t *p_demux, mp4_track_t *p_track, int i_index; /* FIXME see if it's needed to check p_track->i_chunk_count */ - if( !p_track->b_ok || p_track->i_chunk_count == 0 ) - { + if( p_track->i_chunk_count == 0 ) return( VLC_EGENERIC ); - } /* handle elst (find the correct one) */ MP4_TrackSetELST( p_demux, p_track, i_start ); @@ -1363,7 +1784,7 @@ static int TrackTimeToSampleChunk( demux_t *p_demux, mp4_track_t *p_track, /* now calculate i_start for this elst */ /* offset */ - i_start -= p_track->i_elst_time * I64C(1000000) / p_sys->i_timescale; + i_start -= p_track->i_elst_time * INT64_C(1000000) / p_sys->i_timescale; if( i_start < 0 ) { *pi_chunk = 0; @@ -1381,7 +1802,7 @@ static int TrackTimeToSampleChunk( demux_t *p_demux, mp4_track_t *p_track, i_start += elst->i_media_time[p_track->i_elst]; } - msg_Dbg( p_demux, "elst (%d) gives "I64Fd"ms (movie)-> "I64Fd + msg_Dbg( p_demux, "elst (%d) gives %"PRId64"ms (movie)-> %"PRId64 "ms (track)", p_track->i_elst, i_mvt * 1000 / p_sys->i_timescale, i_start * 1000 / p_track->i_timescale ); @@ -1404,8 +1825,8 @@ static int TrackTimeToSampleChunk( demux_t *p_demux, mp4_track_t *p_track, break; } - if( i_start >= p_track->chunk[i_chunk].i_first_dts && - i_start < p_track->chunk[i_chunk + 1].i_first_dts ) + if( (uint64_t)i_start >= p_track->chunk[i_chunk].i_first_dts && + (uint64_t)i_start < p_track->chunk[i_chunk + 1].i_first_dts ) { break; } @@ -1418,7 +1839,7 @@ static int TrackTimeToSampleChunk( demux_t *p_demux, mp4_track_t *p_track, { if( i_dts + p_track->chunk[i_chunk].p_sample_count_dts[i_index] * - p_track->chunk[i_chunk].p_sample_delta_dts[i_index] < i_start ) + p_track->chunk[i_chunk].p_sample_delta_dts[i_index] < (uint64_t)i_start ) { i_dts += p_track->chunk[i_chunk].p_sample_count_dts[i_index] * @@ -1505,13 +1926,12 @@ static int TrackTimeToSampleChunk( demux_t *p_demux, mp4_track_t *p_track, 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; + bool b_reselect = false; /* now see if actual es is ok */ - if( p_track->i_chunk < 0 || - 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 ) + if( (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_demux, "recreate ES for track[Id 0x%x]", p_track->i_track_ID ); @@ -1528,8 +1948,8 @@ static int TrackGotoChunkSample( demux_t *p_demux, mp4_track_t *p_track, msg_Err( p_demux, "cannot create es for track[Id 0x%x]", p_track->i_track_ID ); - p_track->b_ok = VLC_FALSE; - p_track->b_selected = VLC_FALSE; + p_track->b_ok = false; + p_track->b_selected = false; return VLC_EGENERIC; } } @@ -1553,7 +1973,8 @@ static int TrackGotoChunkSample( demux_t *p_demux, mp4_track_t *p_track, * If it succeed b_ok is set to 1 else to 0 ****************************************************************************/ static void MP4_TrackCreate( demux_t *p_demux, mp4_track_t *p_track, - MP4_Box_t *p_box_trak ) + MP4_Box_t *p_box_trak, + bool b_force_enable ) { demux_sys_t *p_sys = p_demux->p_sys; @@ -1573,12 +1994,13 @@ static void MP4_TrackCreate( demux_t *p_demux, mp4_track_t *p_track, unsigned int i; char language[4]; - /* hint track unsuported */ + /* hint track unsupported */ /* set default value (-> track unusable) */ - p_track->b_ok = VLC_FALSE; - p_track->b_enable = VLC_FALSE; - p_track->b_selected = VLC_FALSE; + p_track->b_ok = false; + p_track->b_enable = false; + p_track->b_selected = false; + p_track->b_chapter = false; es_format_Init( &p_track->fmt, UNKNOWN_ES, 0 ); @@ -1590,6 +2012,8 @@ static void MP4_TrackCreate( demux_t *p_demux, mp4_track_t *p_track, /* do we launch this track by default ? */ p_track->b_enable = ( ( p_tkhd->data.p_tkhd->i_flags&MP4_TRACK_ENABLED ) != 0 ); + if( !p_track->b_enable ) + p_track->fmt.i_priority = -1; p_track->i_track_ID = p_tkhd->data.p_tkhd->i_track_ID; p_track->i_width = p_tkhd->data.p_tkhd->i_width / 65536; @@ -1609,6 +2033,8 @@ static void MP4_TrackCreate( demux_t *p_demux, mp4_track_t *p_track, } p_track->i_timescale = p_mdhd->data.p_mdhd->i_timescale; + if( !p_track->i_timescale ) + return; for( i = 0; i < 3; i++ ) { @@ -1636,6 +2062,7 @@ static void MP4_TrackCreate( demux_t *p_demux, mp4_track_t *p_track, case( FOURCC_text ): case( FOURCC_subp ): + case( FOURCC_tx3g ): p_track->fmt.i_cat = SPU_ES; break; @@ -1648,16 +2075,17 @@ static void MP4_TrackCreate( demux_t *p_demux, mp4_track_t *p_track, 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; + unsigned 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, + msg_Dbg( p_demux, " - [%d] duration=%"PRId64"ms media time=%"PRId64 + "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, + (int64_t)(elst->i_media_time[i] * 1000 / p_track->i_timescale) : + INT64_C(-1), elst->i_media_rate_integer[i], elst->i_media_rate_fraction[i] ); } @@ -1695,38 +2123,13 @@ static void MP4_TrackCreate( demux_t *p_demux, mp4_track_t *p_track, switch( p_0xa9xxx->i_type ) { case FOURCC_0xa9nam: - p_track->fmt.psz_description = strdup( p_0xa9xxx->data.p_0xa9xxx->psz_text ); + 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 ) - { - MP4_Box_t *p_sample; - - p_sample = MP4_BoxGet( p_track->p_stsd, "[0]" ); - if( p_sample && p_sample->data.p_sample_soun) - { - MP4_Box_data_sample_soun_t *p_soun = p_sample->data.p_sample_soun; - if( p_soun->i_qt_version == 0 && - p_track->i_timescale != p_soun->i_sampleratehi ) - { - msg_Warn( p_demux, - "i_timescale ("I64Fu") != i_sampleratehi (%u) with " - "qt_version == 0\n" - "Making both equal. (report any problem)", - p_track->i_timescale, p_soun->i_sampleratehi ); - - if( p_soun->i_sampleratehi ) - p_track->i_timescale = p_soun->i_sampleratehi; - else - p_soun->i_sampleratehi = p_track->i_timescale; - } - } - } - /* Create chunk index table and sample index table */ if( TrackCreateChunksIndex( p_demux,p_track ) || TrackCreateSamplesIndex( p_demux, p_track ) ) @@ -1737,16 +2140,42 @@ static void MP4_TrackCreate( demux_t *p_demux, mp4_track_t *p_track, p_track->i_chunk = 0; p_track->i_sample = 0; + /* Mark chapter only track */ + if( !p_track->b_enable && p_sys->p_tref_chap ) + { + MP4_Box_data_tref_generic_t *p_chap = p_sys->p_tref_chap->data.p_tref_generic; + unsigned int i; + + for( i = 0; i < p_chap->i_entry_count; i++ ) + { + if( p_track->i_track_ID == p_chap->i_track_ID[i] ) + { + p_track->b_chapter = true; + break; + } + } + } + /* now create es */ + if( b_force_enable && + ( p_track->fmt.i_cat == VIDEO_ES || p_track->fmt.i_cat == AUDIO_ES ) ) + { + msg_Warn( p_demux, "Enabling track[Id 0x%x] (buggy file without enabled track)", + p_track->i_track_ID ); + p_track->b_enable = true; + p_track->fmt.i_priority = 0; + } + + p_track->p_es = NULL; if( TrackCreateES( p_demux, p_track, p_track->i_chunk, - &p_track->p_es ) ) + p_track->b_chapter ? NULL : &p_track->p_es ) ) { msg_Err( p_demux, "cannot create es for track[Id 0x%x]", p_track->i_track_ID ); return; } - + p_track->b_ok = true; #if 0 { int i; @@ -1759,7 +2188,6 @@ static void MP4_TrackCreate( demux_t *p_demux, mp4_track_t *p_track, } } #endif - p_track->b_ok = VLC_TRUE; } /**************************************************************************** @@ -1767,13 +2195,13 @@ static void MP4_TrackCreate( demux_t *p_demux, mp4_track_t *p_track, **************************************************************************** * Destroy a track created by MP4_TrackCreate. ****************************************************************************/ -static void MP4_TrackDestroy( demux_t *p_demux, mp4_track_t *p_track ) +static void MP4_TrackDestroy( mp4_track_t *p_track ) { unsigned int i_chunk; - p_track->b_ok = VLC_FALSE; - p_track->b_enable = VLC_FALSE; - p_track->b_selected = VLC_FALSE; + p_track->b_ok = false; + p_track->b_enable = false; + p_track->b_selected = false; es_format_Clean( &p_track->fmt ); @@ -1781,22 +2209,25 @@ static void MP4_TrackDestroy( demux_t *p_demux, mp4_track_t *p_track ) { if( p_track->chunk ) { - FREE(p_track->chunk[i_chunk].p_sample_count_dts); - FREE(p_track->chunk[i_chunk].p_sample_delta_dts ); + FREENULL(p_track->chunk[i_chunk].p_sample_count_dts); + FREENULL(p_track->chunk[i_chunk].p_sample_delta_dts ); + + FREENULL(p_track->chunk[i_chunk].p_sample_count_pts); + FREENULL(p_track->chunk[i_chunk].p_sample_offset_pts ); } } - FREE( p_track->chunk ); + FREENULL( p_track->chunk ); if( !p_track->i_sample_size ) { - FREE( p_track->p_sample_size ); + FREENULL( p_track->p_sample_size ); } } static int MP4_TrackSelect( demux_t *p_demux, mp4_track_t *p_track, mtime_t i_start ) { - if( !p_track->b_ok ) + if( !p_track->b_ok || p_track->b_chapter ) { return VLC_EGENERIC; } @@ -1813,7 +2244,7 @@ static int MP4_TrackSelect( demux_t *p_demux, mp4_track_t *p_track, static void MP4_TrackUnselect( demux_t *p_demux, mp4_track_t *p_track ) { - if( !p_track->b_ok ) + if( !p_track->b_ok || p_track->b_chapter ) { return; } @@ -1827,10 +2258,10 @@ static void MP4_TrackUnselect( demux_t *p_demux, mp4_track_t *p_track ) if( p_track->p_es ) { es_out_Control( p_demux->out, ES_OUT_SET_ES_STATE, - p_track->p_es, VLC_FALSE ); + p_track->p_es, false ); } - p_track->b_selected = VLC_FALSE; + p_track->b_selected = false; } static int MP4_TrackSeek( demux_t *p_demux, mp4_track_t *p_track, @@ -1839,37 +2270,39 @@ static int MP4_TrackSeek( demux_t *p_demux, mp4_track_t *p_track, uint32_t i_chunk; uint32_t i_sample; - if( !p_track->b_ok ) - { - return( VLC_EGENERIC ); - } + if( !p_track->b_ok || p_track->b_chapter ) + return VLC_EGENERIC; - p_track->b_selected = VLC_FALSE; + p_track->b_selected = false; if( TrackTimeToSampleChunk( p_demux, p_track, i_start, &i_chunk, &i_sample ) ) { msg_Warn( p_demux, "cannot select track[Id 0x%x]", p_track->i_track_ID ); - return( VLC_EGENERIC ); + return VLC_EGENERIC; } - p_track->b_selected = VLC_TRUE; + p_track->b_selected = true; if( TrackGotoChunkSample( p_demux, p_track, i_chunk, i_sample ) == VLC_SUCCESS ) { - p_track->b_selected = VLC_TRUE; + p_track->b_selected = true; + + es_out_Control( p_demux->out, ES_OUT_SET_NEXT_DISPLAY_TIME, + p_track->p_es, i_start ); } - return( p_track->b_selected ? VLC_SUCCESS : VLC_EGENERIC ); + + return p_track->b_selected ? VLC_SUCCESS : VLC_EGENERIC; } /* * 3 types: for audio - * + * */ -#define QT_V0_MAX_SAMPLES 1500 +#define QT_V0_MAX_SAMPLES 1024 static int MP4_TrackSampleSize( mp4_track_t *p_track ) { int i_size; @@ -1878,58 +2311,58 @@ static int MP4_TrackSampleSize( mp4_track_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_demux, "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_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 { @@ -1940,21 +2373,18 @@ static uint64_t MP4_TrackGetPos( mp4_track_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( demux_t *p_demux, - mp4_track_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; @@ -1967,11 +2397,19 @@ static int MP4_TrackNextSample( demux_t *p_demux, 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 + @@ -1985,12 +2423,7 @@ static int MP4_TrackNextSample( demux_t *p_demux, } if( p_track->i_sample >= p_track->i_sample_count ) - { - /* we have reach end of the track so free decoder stuff */ - 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 ? */ if( p_track->i_sample >= @@ -2012,15 +2445,15 @@ static int MP4_TrackNextSample( demux_t *p_demux, { 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 ) * + uint64_t i_mvt = MP4_TrackGetDTS( p_demux, p_track ) * p_sys->i_timescale / (int64_t)1000000; - if( p_track->i_elst < elst->i_entry_count && + if( (unsigned int)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 ) ); + MP4_TrackGetDTS( p_demux, p_track ) ); } } @@ -2041,7 +2474,7 @@ static void MP4_TrackSetELST( demux_t *p_demux, mp4_track_t *tk, 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++ ) + for( tk->i_elst = 0; (unsigned int)tk->i_elst < elst->i_entry_count; tk->i_elst++ ) { mtime_t i_dur = elst->i_segment_duration[tk->i_elst]; @@ -2052,7 +2485,7 @@ static void MP4_TrackSetELST( demux_t *p_demux, mp4_track_t *tk, tk->i_elst_time += i_dur; } - if( tk->i_elst >= elst->i_entry_count ) + if( (unsigned int)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;