/*****************************************************************************
* mp4.c : MP4 file input module for vlc
*****************************************************************************
- * Copyright (C) 2001 VideoLAN
- * $Id: mp4.c,v 1.43 2003/11/27 12:32:51 fenrir Exp $
+ * Copyright (C) 2001-2004 VideoLAN
+ * $Id$
* Authors: Laurent Aimar <fenrir@via.ecp.fr>
*
* This program is free software; you can redistribute it and/or modify
#include <vlc/vlc.h>
#include <vlc/input.h>
#include <vlc_playlist.h>
+#include "iso_lang.h"
+#include "vlc_meta.h"
#include "libmp4.h"
#include "mp4.h"
+#include "drms.h"
/*****************************************************************************
* Module descriptor
static void Close( vlc_object_t * );
vlc_module_begin();
- set_description( _("MP4 demuxer") );
+ set_description( _("MP4 stream demuxer") );
set_capability( "demux", 242 );
set_callbacks( Open, Close );
vlc_module_end();
/*****************************************************************************
* Declaration of local function
*****************************************************************************/
-static void MP4_TrackCreate ( input_thread_t *, track_data_mp4_t *, MP4_Box_t *);
-static void MP4_TrackDestroy( input_thread_t *, track_data_mp4_t * );
+static void MP4_TrackCreate ( input_thread_t *, mp4_track_t *, MP4_Box_t *);
+static void MP4_TrackDestroy( input_thread_t *, mp4_track_t * );
-static int MP4_TrackSelect ( input_thread_t *, track_data_mp4_t *, mtime_t );
-static void MP4_TrackUnselect(input_thread_t *, track_data_mp4_t * );
+static int MP4_TrackSelect ( input_thread_t *, mp4_track_t *, mtime_t );
+static void MP4_TrackUnselect(input_thread_t *, mp4_track_t * );
-static int MP4_TrackSeek ( input_thread_t *, track_data_mp4_t *, mtime_t );
+static int MP4_TrackSeek ( input_thread_t *, mp4_track_t *, mtime_t );
-static uint64_t MP4_GetTrackPos ( track_data_mp4_t * );
-static int MP4_TrackSampleSize( track_data_mp4_t * );
-static int MP4_TrackNextSample( input_thread_t *, track_data_mp4_t * );
+static uint64_t MP4_TrackGetPos ( mp4_track_t * );
+static int MP4_TrackSampleSize( mp4_track_t * );
+static int MP4_TrackNextSample( input_thread_t *, mp4_track_t * );
+static void MP4_TrackSetELST( input_thread_t *, mp4_track_t *, int64_t );
-#define FREE( p ) \
- if( p ) { free( p ); (p) = NULL;}
+/* Return time in µs of a track */
+static inline int64_t MP4_TrackGetPTS( input_thread_t *p_input, mp4_track_t *p_track )
+{
+ unsigned int i_sample;
+ unsigned int i_index;
+ int64_t i_dts;
+
+ i_sample = p_track->i_sample - p_track->chunk[p_track->i_chunk].i_sample_first;
+ i_dts = p_track->chunk[p_track->i_chunk].i_first_dts;
+ i_index = 0;
+ while( i_sample > 0 )
+ {
+ if( i_sample > p_track->chunk[p_track->i_chunk].p_sample_count_dts[i_index] )
+ {
+ i_dts += p_track->chunk[p_track->i_chunk].p_sample_count_dts[i_index] *
+ p_track->chunk[p_track->i_chunk].p_sample_delta_dts[i_index];
+ i_sample -= p_track->chunk[p_track->i_chunk].p_sample_count_dts[i_index];
+ i_index++;
+ }
+ else
+ {
+ i_dts += i_sample *
+ p_track->chunk[p_track->i_chunk].p_sample_delta_dts[i_index];
+ i_sample = 0;
+ break;
+ }
+ }
+
+ /* now handle elst */
+ if( p_track->p_elst )
+ {
+ demux_sys_t *p_sys = p_input->p_demux_data;
+ MP4_Box_data_elst_t *elst = p_track->p_elst->data.p_elst;
+
+ /* convert to offset */
+ if( ( elst->i_media_rate_integer[p_track->i_elst] > 0 ||
+ elst->i_media_rate_fraction[p_track->i_elst] > 0 ) &&
+ elst->i_media_time[p_track->i_elst] > 0 )
+ {
+ i_dts -= elst->i_media_time[p_track->i_elst];
+ }
+
+ /* add i_elst_time */
+ i_dts += p_track->i_elst_time * p_track->i_timescale / p_sys->i_timescale;
+
+ if( i_dts < 0 ) i_dts = 0;
+ }
+
+ return (int64_t)1000000 * i_dts / p_track->i_timescale;
+}
+static inline int64_t MP4_GetMoviePTS(demux_sys_t *p_sys )
+{
+ return (int64_t)1000000 * p_sys->i_time / p_sys->i_timescale;
+}
+
+#define FREE( p ) if( p ) { free( p ); (p) = NULL;}
/*****************************************************************************
* Open: check file and initializes MP4 structures
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)" );
!strncmp( psz_ref, "rtsp://", 7 ) )
{
msg_Dbg( p_input, "adding ref = `%s'", psz_ref );
- playlist_Add( p_playlist, psz_ref, 0, 0,
+ playlist_Add( p_playlist, psz_ref, psz_ref,
PLAYLIST_APPEND, PLAYLIST_END );
}
else
}
strcat( psz_absolute, psz_ref );
msg_Dbg( p_input, "adding ref = `%s'", psz_absolute );
- playlist_Add( p_playlist, psz_absolute, 0, 0,
+ playlist_Add( p_playlist, psz_absolute, psz_absolute,
PLAYLIST_APPEND, PLAYLIST_END );
}
}
else
{
- msg_Err( p_input, "unknown ref type=%4.4s FIXME (send a bug report)", (char*)&p_rdrf->data.p_rdrf->i_ref_type );
+ msg_Err( p_input, "unknown ref type=%4.4s FIXME (send a bug report)",
+ (char*)&p_rdrf->data.p_rdrf->i_ref_type );
}
}
vlc_object_release( p_playlist );
/* allocate memory */
- p_sys->track = calloc( p_sys->i_tracks, sizeof( track_data_mp4_t ) );
- memset( p_sys->track, 0, p_sys->i_tracks * sizeof( track_data_mp4_t ) );
+ p_sys->track = calloc( p_sys->i_tracks, sizeof( mp4_track_t ) );
+ memset( p_sys->track, 0, p_sys->i_tracks * sizeof( mp4_track_t ) );
/* now process each track and extract all usefull informations */
for( i = 0; i < p_sys->i_tracks; i++ )
/* check for newly selected/unselected track */
for( i_track = 0, i_track_selected = 0; i_track < p_sys->i_tracks; i_track++ )
{
-#define track p_sys->track[i_track]
- if( track.b_selected && track.i_sample >= track.i_sample_count )
+ mp4_track_t *tk = &p_sys->track[i_track];
+
+ if( tk->b_selected && tk->i_sample >= tk->i_sample_count )
{
- msg_Warn( p_input, "track[0x%x] will be disabled", track.i_track_ID );
- MP4_TrackUnselect( p_input, &track );
+ msg_Warn( p_input, "track[0x%x] will be disabled", tk->i_track_ID );
+ MP4_TrackUnselect( p_input, tk);
}
- else if( track.b_ok )
+ else if( tk->b_ok )
{
vlc_bool_t b;
- es_out_Control( p_input->p_es_out, ES_OUT_GET_ES_STATE, track.p_es, &b );
+ es_out_Control( p_input->p_es_out, ES_OUT_GET_ES_STATE, tk->p_es, &b );
- if( track.b_selected && !b )
+ if( tk->b_selected && !b )
{
- MP4_TrackUnselect( p_input, &track );
+ MP4_TrackUnselect( p_input, tk );
}
- else if( !track.b_selected && b)
+ else if( !tk->b_selected && b)
{
- MP4_TrackSelect( p_input, &track, MP4_GetMoviePTS( p_sys ) );
+ MP4_TrackSelect( p_input, tk, MP4_GetMoviePTS( p_sys ) );
}
- if( track.b_selected )
+ if( tk->b_selected )
{
i_track_selected++;
}
}
-#undef track
}
if( i_track_selected <= 0 )
{
msg_Warn( p_input, "no track selected, exiting..." );
- return( 0 );
+ return 0;
}
/* first wait for the good time to read a packet */
for( i_track = 0; i_track < p_sys->i_tracks; i_track++ )
{
-#define track p_sys->track[i_track]
- if( !track.b_ok ||
- !track.b_selected ||
- MP4_GetTrackPTS( &track ) >= MP4_GetMoviePTS( p_sys ) )
+ mp4_track_t *tk = &p_sys->track[i_track];
+
+ if( !tk->b_ok || !tk->b_selected )
{
continue;
}
- while( MP4_GetTrackPTS( &track ) < MP4_GetMoviePTS( p_sys ) )
+ while( MP4_TrackGetPTS( p_input, tk ) < MP4_GetMoviePTS( p_sys ) )
{
+#if 0
+ msg_Dbg( p_input, "tk=%lld mv=%lld",
+ MP4_TrackGetPTS( p_input, tk ),
+ MP4_GetMoviePTS( p_sys ) );
+#endif
- if( !b_play_audio && track.fmt.i_cat == AUDIO_ES )
- {
- if( MP4_TrackNextSample( p_input, &track ) )
- {
- break;
- }
- }
- else
+ if( MP4_TrackSampleSize( tk ) > 0 &&
+ ( b_play_audio || tk->fmt.i_cat != AUDIO_ES ) )
{
block_t *p_block;
/* go,go go ! */
- if( stream_Seek( p_input->s, MP4_GetTrackPos( &track ) ) )
+ if( stream_Seek( p_input->s, MP4_TrackGetPos( tk ) ) )
{
- msg_Warn( p_input, "track[0x%x] will be disabled (eof?)", track.i_track_ID );
- MP4_TrackUnselect( p_input, &track );
+ msg_Warn( p_input, "track[0x%x] will be disabled (eof?)", tk->i_track_ID );
+ MP4_TrackUnselect( p_input, tk );
break;
}
/* now read pes */
if( ( p_block = stream_Block( p_input->s,
- MP4_TrackSampleSize( &track ) ) ) == NULL )
+ MP4_TrackSampleSize( tk ) ) ) == NULL )
{
- msg_Warn( p_input, "track[0x%x] will be disabled (eof?)", track.i_track_ID );
- MP4_TrackUnselect( p_input, &track );
+ msg_Warn( p_input, "track[0x%x] will be disabled (eof?)", tk->i_track_ID );
+ MP4_TrackUnselect( p_input, tk );
break;
}
- if( track.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
+ if( tk->b_drms && tk->p_drms )
{
- p_block->i_pts =
- p_block->i_dts =
- input_ClockGetTS( p_input,
- p_input->stream.p_selected_program,
- MP4_GetTrackPTS( &track ) * 9/100 );
+ drms_decrypt( tk->p_drms,
+ (uint32_t*)p_block->p_buffer,
+ p_block->i_buffer );
}
+ p_block->i_dts =
+ input_ClockGetTS( p_input,
+ p_input->stream.p_selected_program,
+ MP4_TrackGetPTS( p_input, tk ) * 9/100 );
- es_out_Send( p_input->p_es_out, track.p_es, p_block );
+ p_block->i_pts = tk->fmt.i_cat == VIDEO_ES ? 0 : p_block->i_dts;
- if( MP4_TrackNextSample( p_input, &track ) )
+ if( !tk->b_drms || ( tk->b_drms && tk->p_drms ) )
{
- break;
+ es_out_Send( p_input->p_es_out, tk->p_es, p_block );
}
}
+
+ /* Next sample */
+ if( MP4_TrackNextSample( p_input, tk ) )
+ {
+ break;
+ }
}
-#undef track
}
- return( 1 );
+ return 1;
}
/*****************************************************************************
* Seek: Got to i_date
{
demux_sys_t *p_sys = p_input->p_demux_data;
unsigned int i_track;
+
/* First update update global time */
p_sys->i_time = i_date * p_sys->i_timescale / 1000000;
p_sys->i_pcr = i_date* 9 / 100;
/* Now for each stream try to go to this time */
for( i_track = 0; i_track < p_sys->i_tracks; i_track++ )
{
-#define track p_sys->track[i_track]
- if( track.b_ok && track.b_selected )
+ mp4_track_t *tk = &p_sys->track[i_track];
+
+ if( tk->b_ok && tk->b_selected )
{
- MP4_TrackSeek( p_input, &track, i_date );
+ MP4_TrackSeek( p_input, tk, i_date );
}
-#undef track
}
return( 1 );
}
/*****************************************************************************
* Control:
*****************************************************************************/
-static int Control ( input_thread_t *p_input, int i_query, va_list args )
+static int Control( input_thread_t *p_input, int i_query, va_list args )
{
demux_sys_t *p_sys = p_input->p_demux_data;
- double f, *pf;
+ double f, *pf;
int64_t i64, *pi64;
switch( i_query )
case DEMUX_SET_POSITION:
f = (double)va_arg( args, double );
- i64 = (int64_t)( f *
- (double)1000000 *
- (double)p_sys->i_duration /
- (double)p_sys->i_timescale );
- return Seek( p_input, i64 );
+ if( p_sys->i_timescale > 0 )
+ {
+ i64 = (int64_t)( f * (double)1000000 *
+ (double)p_sys->i_duration /
+ (double)p_sys->i_timescale );
+ return Seek( p_input, i64 );
+ }
+ else return VLC_SUCCESS;
case DEMUX_GET_TIME:
pi64 = (int64_t*)va_arg( args, int64_t * );
- *pi64 = (mtime_t)1000000 *
- (mtime_t)p_sys->i_time /
- (mtime_t)p_sys->i_timescale;
+ if( p_sys->i_timescale > 0 )
+ {
+ *pi64 = (mtime_t)1000000 *
+ (mtime_t)p_sys->i_time /
+ (mtime_t)p_sys->i_timescale;
+ }
+ else *pi64 = 0;
return VLC_SUCCESS;
case DEMUX_SET_TIME:
case DEMUX_GET_LENGTH:
pi64 = (int64_t*)va_arg( args, int64_t * );
- *pi64 = (mtime_t)1000000 *
- (mtime_t)p_sys->i_duration /
- (mtime_t)p_sys->i_timescale;
+ if( p_sys->i_timescale > 0 )
+ {
+ *pi64 = (mtime_t)1000000 *
+ (mtime_t)p_sys->i_duration /
+ (mtime_t)p_sys->i_timescale;
+ }
+ else *pi64 = 0;
return VLC_SUCCESS;
+
case DEMUX_GET_FPS:
msg_Warn( p_input, "DEMUX_GET_FPS unimplemented !!" );
return VLC_EGENERIC;
+
+ case DEMUX_GET_META:
+ {
+ vlc_meta_t **pp_meta = (vlc_meta_t**)va_arg( args, vlc_meta_t** );
+ vlc_meta_t *meta;
+ MP4_Box_t *p_udta = MP4_BoxGet( p_sys->p_root, "/moov/udta" );
+ MP4_Box_t *p_0xa9xxx;
+ if( p_udta == NULL )
+ {
+ return VLC_EGENERIC;
+ }
+ *pp_meta = meta = vlc_meta_New();
+ for( p_0xa9xxx = p_udta->p_first; p_0xa9xxx != NULL;
+ p_0xa9xxx = p_0xa9xxx->p_next )
+ {
+ switch( p_0xa9xxx->i_type )
+ {
+ case FOURCC_0xa9nam:
+ vlc_meta_Add( meta, VLC_META_TITLE,
+ p_0xa9xxx->data.p_0xa9xxx->psz_text );
+ break;
+ case FOURCC_0xa9aut:
+ vlc_meta_Add( meta, VLC_META_AUTHOR,
+ p_0xa9xxx->data.p_0xa9xxx->psz_text );
+ break;
+ case FOURCC_0xa9ART:
+ vlc_meta_Add( meta, VLC_META_ARTIST,
+ p_0xa9xxx->data.p_0xa9xxx->psz_text );
+ break;
+ case FOURCC_0xa9cpy:
+ vlc_meta_Add( meta, VLC_META_COPYRIGHT,
+ p_0xa9xxx->data.p_0xa9xxx->psz_text );
+ break;
+ case FOURCC_0xa9day:
+ vlc_meta_Add( meta, VLC_META_DATE,
+ p_0xa9xxx->data.p_0xa9xxx->psz_text );
+ break;
+ case FOURCC_0xa9des:
+ vlc_meta_Add( meta, VLC_META_DESCRIPTION,
+ p_0xa9xxx->data.p_0xa9xxx->psz_text );
+ break;
+
+ case FOURCC_0xa9swr:
+ case FOURCC_0xa9inf:
+ case FOURCC_0xa9dir:
+ case FOURCC_0xa9cmt:
+ case FOURCC_0xa9req:
+ case FOURCC_0xa9fmt:
+ case FOURCC_0xa9prd:
+ case FOURCC_0xa9prf:
+ case FOURCC_0xa9src:
+ /* TODO one day, but they aren't really meaningfull */
+ break;
+
+ default:
+ break;
+ }
+ }
+ return VLC_SUCCESS;
+ }
+
default:
msg_Err( p_input, "control query unimplemented !!!" );
return demux_vaControlDefault( p_input, i_query, args );
/* now create basic chunk data, the rest will be filled by MP4_CreateSamplesIndex */
static int TrackCreateChunksIndex( input_thread_t *p_input,
- track_data_mp4_t *p_demux_track )
+ mp4_track_t *p_demux_track )
{
MP4_Box_t *p_co64; /* give offset for each chunk, same for stco and co64 */
MP4_Box_t *p_stsc;
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++ )
msg_Dbg( p_input,
"track[Id 0x%x] read %d chunk",
p_demux_track->i_track_ID,
- p_demux_track->i_chunk_count );
+ p_demux_track->i_chunk_count );
return( VLC_SUCCESS );
}
static int TrackCreateSamplesIndex( input_thread_t *p_input,
- track_data_mp4_t *p_demux_track )
+ mp4_track_t *p_demux_track )
{
MP4_Box_t *p_stts; /* makes mapping between sample and decoding time,
ctts make same mapping but for composition time,
* Create ES and PES to init decoder if needed, for a track starting at i_chunk
*/
static int TrackCreateES ( input_thread_t *p_input,
- track_data_mp4_t *p_track,
+ mp4_track_t *p_track,
unsigned int i_chunk,
es_out_id_t **pp_es )
{
default:
break;
}
+
+ }
+ else if( p_soun->i_qt_version == 1 && p_soun->i_sample_per_packet <= 0 )
+ {
+ p_soun->i_qt_version = 0;
}
}
+
/* It's a little ugly but .. there are special cases */
switch( p_sample->i_type )
{
#undef p_decconfig
/* some last initialisation */
- /* XXX I create a bitmapinfoheader_t or
- waveformatex_t for each stream, up to now it's the best thing
- I've found but it could exist a better solution :) as something
- like adding some new fields in p_es ...
-
- XXX I don't set all values, only thoses that are interesting or known
- --> bitmapinfoheader_t : width and height
- --> waveformatex_t : channels, samplerate, bitspersample
- and at the end I add p_decoder_specific_info
-
- TODO set more values
-
- */
-
switch( p_track->fmt.i_cat )
{
- 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;
+ 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;
- /* fall on display size */
- if( p_track->fmt.video.i_width <= 0 )
- {
- p_track->fmt.video.i_width = p_track->i_width;
- }
- if( p_track->fmt.video.i_height <= 0 )
- {
- p_track->fmt.video.i_height = p_track->i_height;
- }
- break;
+ /* fall on display size */
+ if( p_track->fmt.video.i_width <= 0 )
+ p_track->fmt.video.i_width = p_track->i_width;
+ if( p_track->fmt.video.i_height <= 0 )
+ p_track->fmt.video.i_height = p_track->i_height;
- case( AUDIO_ES ):
- p_track->fmt.audio.i_channels = p_sample->data.p_sample_soun->i_channelcount;
- p_track->fmt.audio.i_rate = p_sample->data.p_sample_soun->i_sampleratehi;
- p_track->fmt.i_bitrate =p_sample->data.p_sample_soun->i_channelcount *
- p_sample->data.p_sample_soun->i_sampleratehi *
- p_sample->data.p_sample_soun->i_samplesize;
- p_track->fmt.audio.i_bitspersample = p_sample->data.p_sample_soun->i_samplesize;
- break;
+ /* Find out apect ratio from display size */
+ if( p_track->i_width > 0 && p_track->i_height > 0 )
+ p_track->fmt.video.i_aspect =
+ VOUT_ASPECT_FACTOR * p_track->i_width / p_track->i_height;
- default:
- break;
+ break;
+
+ case( AUDIO_ES ):
+ p_track->fmt.audio.i_channels =
+ p_sample->data.p_sample_soun->i_channelcount;
+ p_track->fmt.audio.i_rate =
+ p_sample->data.p_sample_soun->i_sampleratehi;
+ p_track->fmt.i_bitrate = p_sample->data.p_sample_soun->i_channelcount *
+ p_sample->data.p_sample_soun->i_sampleratehi *
+ p_sample->data.p_sample_soun->i_samplesize;
+ p_track->fmt.audio.i_bitspersample =
+ p_sample->data.p_sample_soun->i_samplesize;
+ break;
+
+ default:
+ break;
}
+
*pp_es = es_out_Add( p_input->p_es_out, &p_track->fmt );
return VLC_SUCCESS;
}
-/* given a time it return sample/chunk */
+/* given a time it return sample/chunk
+ * it also update elst field of the track
+ */
static int TrackTimeToSampleChunk( input_thread_t *p_input,
- track_data_mp4_t *p_track,
- uint64_t i_start,
+ mp4_track_t *p_track,
+ int64_t i_start,
uint32_t *pi_chunk,
uint32_t *pi_sample )
{
- MP4_Box_t *p_stss;
+ demux_sys_t *p_sys = p_input->p_demux_data;
+ MP4_Box_t *p_stss;
uint64_t i_dts;
unsigned int i_sample;
unsigned int i_chunk;
int i_index;
- /* convert absolute time to in timescale unit */
- i_start = i_start * (mtime_t)p_track->i_timescale / (mtime_t)1000000;
-
/* FIXME see if it's needed to check p_track->i_chunk_count */
if( !p_track->b_ok || p_track->i_chunk_count == 0 )
{
return( VLC_EGENERIC );
}
+ /* handle elst (find the correct one) */
+ MP4_TrackSetELST( p_input, p_track, i_start );
+ if( p_track->p_elst && p_track->p_elst->data.p_elst->i_entry_count > 0 )
+ {
+ MP4_Box_data_elst_t *elst = p_track->p_elst->data.p_elst;
+ int64_t i_mvt= i_start * p_sys->i_timescale / (int64_t)1000000;
+
+ /* now calculate i_start for this elst */
+ /* offset */
+ i_start -= p_track->i_elst_time * (int64_t)1000000 / p_sys->i_timescale;
+ if( i_start < 0 )
+ {
+ *pi_chunk = 0;
+ *pi_sample= 0;
+
+ return VLC_SUCCESS;
+ }
+ /* to track time scale */
+ i_start = i_start * p_track->i_timescale / (int64_t)1000000;
+ /* add elst offset */
+ if( ( elst->i_media_rate_integer[p_track->i_elst] > 0 ||
+ elst->i_media_rate_fraction[p_track->i_elst] > 0 ) &&
+ elst->i_media_time[p_track->i_elst] > 0 )
+ {
+ i_start += elst->i_media_time[p_track->i_elst];
+ }
+
+ msg_Dbg( p_input, "elst (%d) gives "I64Fd"ms (movie)-> "I64Fd"ms (track)",
+ p_track->i_elst,
+ i_mvt * 1000 / p_sys->i_timescale,
+ i_start * 1000 / p_track->i_timescale );
+ }
+ else
+ {
+ /* convert absolute time to in timescale unit */
+ i_start = i_start * p_track->i_timescale / (int64_t)1000000;
+ }
+
/* we start from sample 0/chunk 0, hope it won't take too much time */
/* *** find good chunk *** */
for( i_chunk = 0; ; i_chunk++ )
p_track->i_track_ID );
}
- if( pi_chunk ) *pi_chunk = i_chunk;
- if( pi_sample ) *pi_sample = i_sample;
- return( VLC_SUCCESS );
+ *pi_chunk = i_chunk;
+ *pi_sample = i_sample;
+
+ return VLC_SUCCESS;
}
static int TrackGotoChunkSample( input_thread_t *p_input,
- track_data_mp4_t *p_track,
+ mp4_track_t *p_track,
unsigned int i_chunk,
unsigned int i_sample )
{
p_track->chunk[p_track->i_chunk].i_sample_description_index !=
p_track->chunk[i_chunk].i_sample_description_index )
{
- msg_Warn( p_input, "Recreate ES" );
+ msg_Warn( p_input, "recreate ES" );
es_out_Control( p_input->p_es_out, ES_OUT_GET_ES_STATE, p_track->p_es, &b_reselect );
* If it succeed b_ok is set to 1 else to 0
****************************************************************************/
static void MP4_TrackCreate( input_thread_t *p_input,
- track_data_mp4_t *p_track,
+ mp4_track_t *p_track,
MP4_Box_t * p_box_trak )
{
+ demux_sys_t *p_sys = p_input->p_demux_data;
+
MP4_Box_t *p_tkhd = MP4_BoxGet( p_box_trak, "tkhd" );
MP4_Box_t *p_tref = MP4_BoxGet( p_box_trak, "tref" );
MP4_Box_t *p_elst;
MP4_Box_t *p_vmhd;
MP4_Box_t *p_smhd;
+ MP4_Box_t *p_drms;
+
unsigned int i;
char language[4];
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" ); */
default:
return;
}
+
+ p_track->i_elst = 0;
+ p_track->i_elst_time = 0;
+ if( ( p_track->p_elst = p_elst = MP4_BoxGet( p_box_trak, "edts/elst" ) ) )
+ {
+ MP4_Box_data_elst_t *elst = p_elst->data.p_elst;
+ int i;
+
+ msg_Warn( p_input, "elst box found" );
+ for( i = 0; i < elst->i_entry_count; i++ )
+ {
+ msg_Dbg( p_input, " - [%d] duration="I64Fd"ms media time="I64Fd"ms) rate=%d.%d",
+ i,
+ elst->i_segment_duration[i] * 1000 / p_sys->i_timescale,
+ elst->i_media_time[i] >= 0 ?
+ elst->i_media_time[i] * 1000 / p_track->i_timescale : -1,
+ elst->i_media_rate_integer[i],
+ elst->i_media_rate_fraction[i] );
+ }
+ }
+
+
/* TODO
add support for:
p_dinf = MP4_BoxGet( p_minf, "dinf" );
return;
}
+ p_drms = MP4_BoxGet( p_track->p_stsd, "drms" );
+ p_track->b_drms = p_drms != NULL;
+ p_track->p_drms = p_track->b_drms ?
+ p_drms->data.p_sample_soun->p_drms : NULL;
+
/* Set language */
if( strcmp( language, "```" ) && strcmp( language, "und" ) )
{
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 )
+ if( p_soun->i_qt_version == 0 &&
+ p_track->i_timescale != p_soun->i_sampleratehi )
{
- msg_Warn( p_input, "i_timescale != i_sampleratehi with qt_version == 0\nMaking both equal ? (report any problem)" );
- p_track->i_timescale = p_soun->i_sampleratehi;
+ msg_Warn( p_input,
+ "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;
}
}
}
* Destroy a track created by MP4_TrackCreate.
****************************************************************************/
static void MP4_TrackDestroy( input_thread_t *p_input,
- track_data_mp4_t *p_track )
+ mp4_track_t *p_track )
{
unsigned int i_chunk;
}
static int MP4_TrackSelect ( input_thread_t *p_input,
- track_data_mp4_t *p_track,
+ mp4_track_t *p_track,
mtime_t i_start )
{
- uint32_t i_chunk;
- uint32_t i_sample;
-
if( !p_track->b_ok )
{
- return( VLC_EGENERIC );
+ return VLC_EGENERIC;
}
if( p_track->b_selected )
msg_Warn( p_input,
"track[Id 0x%x] already selected",
p_track->i_track_ID );
- return( VLC_SUCCESS );
- }
-
- if( TrackTimeToSampleChunk( p_input,
- p_track, i_start,
- &i_chunk, &i_sample ) )
- {
- msg_Warn( p_input,
- "cannot select track[Id 0x%x]",
- p_track->i_track_ID );
- return( VLC_EGENERIC );
+ return VLC_SUCCESS;
}
- p_track->b_selected = VLC_TRUE;
-
- if( TrackGotoChunkSample( p_input, p_track, i_chunk, i_sample ) )
- {
- p_track->b_selected = VLC_FALSE;
- }
- return p_track->b_selected ? VLC_SUCCESS : VLC_EGENERIC;
+ return MP4_TrackSeek( p_input, p_track, i_start );
}
static void MP4_TrackUnselect(input_thread_t *p_input,
- track_data_mp4_t *p_track )
+ mp4_track_t *p_track )
{
if( !p_track->b_ok )
{
}
static int MP4_TrackSeek ( input_thread_t *p_input,
- track_data_mp4_t *p_track,
+ mp4_track_t *p_track,
mtime_t i_start )
{
uint32_t i_chunk;
p_track->b_selected = VLC_TRUE;
- TrackGotoChunkSample( p_input, p_track, i_chunk, i_sample );
-
+ if( TrackGotoChunkSample( p_input, p_track, i_chunk, i_sample ) )
+ {
+ p_track->b_selected = VLC_FALSE;
+ }
return( p_track->b_selected ? VLC_SUCCESS : VLC_EGENERIC );
}
-
-
-
/*
* 3 types: for audio
*
*/
#define QT_V0_MAX_SAMPLES 1500
-static int MP4_TrackSampleSize( track_data_mp4_t *p_track )
+static int MP4_TrackSampleSize( mp4_track_t *p_track )
{
int i_size;
MP4_Box_data_sample_soun_t *p_soun;
}
-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;
}
static int MP4_TrackNextSample( input_thread_t *p_input,
- track_data_mp4_t *p_track )
+ mp4_track_t *p_track )
{
if( p_track->fmt.i_cat == AUDIO_ES &&
/* we have reach end of the track so free decoder stuff */
msg_Warn( p_input, "track[0x%x] will be disabled", p_track->i_track_ID );
MP4_TrackUnselect( p_input, p_track );
- return( VLC_EGENERIC );
+ return VLC_EGENERIC;
}
/* Have we changed chunk ? */
{
msg_Warn( p_input, "track[0x%x] will be disabled (cannot restart decoder)", p_track->i_track_ID );
MP4_TrackUnselect( p_input, p_track );
- return( VLC_EGENERIC );
+ return VLC_EGENERIC;
}
}
- return( VLC_SUCCESS );
+ /* Have we changed elst */
+ if( p_track->p_elst && p_track->p_elst->data.p_elst->i_entry_count > 0 )
+ {
+ demux_sys_t *p_sys = p_input->p_demux_data;
+ MP4_Box_data_elst_t *elst = p_track->p_elst->data.p_elst;
+ int64_t i_mvt = MP4_TrackGetPTS( p_input, p_track ) * p_sys->i_timescale / (int64_t)1000000;
+
+ if( p_track->i_elst < elst->i_entry_count &&
+ i_mvt >= p_track->i_elst_time + elst->i_segment_duration[p_track->i_elst] )
+ {
+ MP4_TrackSetELST( p_input, p_track, MP4_TrackGetPTS( p_input, p_track ) );
+ }
+ }
+
+ return VLC_SUCCESS;
}
+static void MP4_TrackSetELST( input_thread_t *p_input, mp4_track_t *tk, int64_t i_time )
+{
+ demux_sys_t *p_sys = p_input->p_demux_data;
+ int i_elst_last = tk->i_elst;
+
+ /* handle elst (find the correct one) */
+ tk->i_elst = 0;
+ tk->i_elst_time = 0;
+ if( tk->p_elst && tk->p_elst->data.p_elst->i_entry_count > 0 )
+ {
+ MP4_Box_data_elst_t *elst = tk->p_elst->data.p_elst;
+ int64_t i_mvt= i_time * p_sys->i_timescale / (int64_t)1000000;
+
+ for( tk->i_elst = 0; tk->i_elst < elst->i_entry_count; tk->i_elst++ )
+ {
+ mtime_t i_dur = elst->i_segment_duration[tk->i_elst];
+
+ if( tk->i_elst_time <= i_mvt && i_mvt < tk->i_elst_time + i_dur )
+ {
+ break;
+ }
+ tk->i_elst_time += i_dur;
+ }
+
+ if( tk->i_elst >= elst->i_entry_count )
+ {
+ /* msg_Dbg( p_input, "invalid number of entry in elst" ); */
+ tk->i_elst = elst->i_entry_count - 1;
+ tk->i_elst_time -= elst->i_segment_duration[tk->i_elst];
+ }
+
+ if( elst->i_media_time[tk->i_elst] < 0 )
+ {
+ /* track offset */
+ tk->i_elst_time += elst->i_segment_duration[tk->i_elst];
+ }
+ }
+ if( i_elst_last != tk->i_elst )
+ {
+ msg_Warn( p_input, "elst old=%d new=%d", i_elst_last, tk->i_elst );
+ }
+}