Commit 86e8c9d5 authored by Frédéric Yhuel's avatar Frédéric Yhuel Committed by Jean-Baptiste Kempf

demux/mp4: Add fragmented MP4 support

Signed-off-by: Jean-Baptiste Kempf's avatarJean-Baptiste Kempf <jb@videolan.org>
parent 2f1fe72f
......@@ -159,15 +159,20 @@ static int MP4_NextBox( stream_t *p_stream, MP4_Box_t *p_box )
if( p_box->p_father )
{
const off_t i_box_end = p_box->i_size + p_box->i_pos;
const off_t i_father_end = p_box->p_father->i_size + p_box->p_father->i_pos;
/* check if it's within p-father */
if( i_box_end >= i_father_end )
/* if father's size == 0, it means unknown or infinite size,
* and we skip the followong check */
if( p_box->p_father->i_size > 0 )
{
if( i_box_end > i_father_end )
msg_Dbg( p_stream, "out of bound child" );
return 0; /* out of bound */
const off_t i_box_end = p_box->i_size + p_box->i_pos;
const off_t i_father_end = p_box->p_father->i_size + p_box->p_father->i_pos;
/* check if it's within p-father */
if( i_box_end >= i_father_end )
{
if( i_box_end > i_father_end )
msg_Dbg( p_stream, "out of bound child" );
return 0; /* out of bound */
}
}
}
if( stream_Seek( p_stream, p_box->i_size + p_box->i_pos ) )
......@@ -184,12 +189,17 @@ static int MP4_NextBox( stream_t *p_stream, MP4_Box_t *p_box )
* after called one of theses functions, file position is unknown
* you need to call MP4_GotoBox to go where you want
*****************************************************************************/
static int MP4_ReadBoxContainerRaw( stream_t *p_stream, MP4_Box_t *p_container )
static int MP4_ReadBoxContainerChildren( stream_t *p_stream,
MP4_Box_t *p_container, uint32_t i_last_child )
{
MP4_Box_t *p_box;
if( stream_Tell( p_stream ) + 8 >
/* Size of root container is set to 0 when unknown, for exemple
* with a DASH stream. In that case, we skip the following check */
if( p_container->i_size
&& ( stream_Tell( p_stream ) + 8 >
(off_t)(p_container->i_pos + p_container->i_size) )
)
{
/* there is no box to load */
return 0;
......@@ -204,14 +214,23 @@ static int MP4_ReadBoxContainerRaw( stream_t *p_stream, MP4_Box_t *p_container )
else p_container->p_last->p_next = p_box;
p_container->p_last = p_box;
if( p_box->i_type == i_last_child )
break;
} while( MP4_NextBox( p_stream, p_box ) == 1 );
return 1;
}
static int MP4_ReadBoxContainerRaw( stream_t *p_stream, MP4_Box_t *p_container )
{
return MP4_ReadBoxContainerChildren( p_stream, p_container, 0 );
}
static int MP4_ReadBoxContainer( stream_t *p_stream, MP4_Box_t *p_container )
{
if( p_container->i_size <= (size_t)mp4_box_headersize(p_container ) + 8 )
if( p_container->i_size &&
( p_container->i_size <= (size_t)mp4_box_headersize(p_container ) + 8 ) )
{
/* container is empty, 8 stand for the first header in this box */
return 1;
......@@ -497,22 +516,23 @@ static int MP4_ReadBox_stra( stream_t *p_stream, MP4_Box_t *p_box )
MP4_GET4BYTES( p_stra->BitsPerSample );
MP4_GET4BYTES( p_stra->PacketSize );
MP4_GET4BYTES( p_stra->AudioTag );
MP4_GET4BYTES( p_stra->AvgBytesPerSec );
MP4_GET2BYTES( p_stra->nBlockAlign );
MP4_GET1BYTE( i_reserved );
MP4_GET1BYTE( i_reserved );
MP4_GET1BYTE( i_reserved );
uint8_t codec_data_length;
MP4_GET1BYTE( codec_data_length );
p_stra->CodecPrivateData = malloc( codec_data_length + 1);
MP4_GET1BYTE( p_stra->cpd_len );
if( p_stra->cpd_len > i_read )
goto error;
p_stra->CodecPrivateData = malloc( p_stra->cpd_len );
if( unlikely( p_stra->CodecPrivateData == NULL ) )
goto error;
MP4_GETSTRINGZ( p_stra->CodecPrivateData );
memcpy( p_stra->CodecPrivateData, p_peek, p_stra->cpd_len );
#ifdef MP4_VERBOSE
msg_Dbg( p_stream, "es_cat is %"PRIu8", birate is %"PRIu32", "\
"CodecPrivateData is %s", p_stra->i_es_cat,
p_stra->Bitrate, p_stra->CodecPrivateData );
msg_Dbg( p_stream, "es_cat is %"PRIu8", birate is %"PRIu32,
p_stra->i_es_cat, p_stra->Bitrate );
#endif
MP4_READBOX_EXIT( 1 );
......@@ -3474,57 +3494,7 @@ error:
return NULL;
}
MP4_Box_t *MP4_BoxGetInitFrag( stream_t *s )
{
/* p_chunk is a virtual root container for the ftyp and moov boxes */
MP4_Box_t *p_chunk;
MP4_Box_t *p_ftyp;
MP4_Box_t *p_moov;
p_chunk = calloc( 1, sizeof( MP4_Box_t ) );
if( unlikely( p_chunk == NULL ) )
return NULL;
p_chunk->i_type = ATOM_root;
p_chunk->i_shortsize = 1;
p_ftyp = MP4_ReadBox( s, p_chunk );
if( !p_ftyp )
{
msg_Warn( s, "no ftyp box found!");
goto error;
}
/* there may be some boxes between ftyp and moov,
* we skip them, but put a reasonable limit */
#define MAX_SKIP 8
for( int i = 0 ; i < MAX_SKIP; i++ )
{
p_moov = MP4_ReadBox( s, p_chunk );
if( !p_moov )
goto error;
if( p_moov->i_type != ATOM_moov )
{
if( i == MAX_SKIP - 1 )
return NULL;
stream_Read( s, NULL, p_moov->i_size );
MP4_BoxFree( s, p_moov );
}
else
break;
}
p_chunk->p_first = p_ftyp;
p_ftyp->p_next = p_moov;
p_chunk->p_last = p_moov;
return p_chunk;
error:
free( p_chunk );
return NULL;
}
MP4_Box_t *MP4_BoxGetNextChunk( stream_t *s )
{
/* p_chunk is a virtual root container for the moof and mdat boxes */
......@@ -3546,7 +3516,7 @@ MP4_Box_t *MP4_BoxGetNextChunk( stream_t *s )
}
else if( p_tmp_box->i_type == ATOM_ftyp )
{
return MP4_BoxGetInitFrag( s );
return MP4_BoxGetRoot( s );
}
free( p_tmp_box );
......@@ -3620,7 +3590,8 @@ MP4_Box_t *MP4_BoxGetRoot( stream_t *s )
p_root->i_pos = 0;
p_root->i_type = ATOM_root;
p_root->i_shortsize = 1;
p_root->i_size = stream_Size( s );
/* could be a DASH stream for exemple, 0 means unknown or infinite size */
p_root->i_size = 0;
CreateUUID( &p_root->i_uuid, p_root->i_type );
p_root->data.p_data = NULL;
......@@ -3631,37 +3602,52 @@ MP4_Box_t *MP4_BoxGetRoot( stream_t *s )
p_stream = s;
/* First get the moov */
i_result = MP4_ReadBoxContainerChildren( p_stream, p_root, ATOM_moov );
if( !i_result )
goto error;
/* If there is a mvex box, it means fragmented MP4, and we're done */
else if( MP4_BoxCount( p_root, "moov/mvex" ) > 0 )
return p_root;
p_root->i_size = stream_Size( s );
/* Get the rest of the file */
i_result = MP4_ReadBoxContainerRaw( p_stream, p_root );
if( i_result )
{
MP4_Box_t *p_moov;
MP4_Box_t *p_cmov;
if( !i_result )
goto error;
/* check if there is a cmov, if so replace
compressed moov by uncompressed one */
if( ( ( p_moov = MP4_BoxGet( p_root, "moov" ) ) &&
( p_cmov = MP4_BoxGet( p_root, "moov/cmov" ) ) ) ||
( ( p_moov = MP4_BoxGet( p_root, "foov" ) ) &&
( p_cmov = MP4_BoxGet( p_root, "foov/cmov" ) ) ) )
{
/* rename the compressed moov as a box to skip */
p_moov->i_type = ATOM_skip;
MP4_Box_t *p_moov;
MP4_Box_t *p_cmov;
/* check if there is a cmov, if so replace
compressed moov by uncompressed one */
if( ( ( p_moov = MP4_BoxGet( p_root, "moov" ) ) &&
( p_cmov = MP4_BoxGet( p_root, "moov/cmov" ) ) ) ||
( ( p_moov = MP4_BoxGet( p_root, "foov" ) ) &&
( p_cmov = MP4_BoxGet( p_root, "foov/cmov" ) ) ) )
{
/* rename the compressed moov as a box to skip */
p_moov->i_type = ATOM_skip;
/* get uncompressed p_moov */
p_moov = p_cmov->data.p_cmov->p_moov;
p_cmov->data.p_cmov->p_moov = NULL;
/* get uncompressed p_moov */
p_moov = p_cmov->data.p_cmov->p_moov;
p_cmov->data.p_cmov->p_moov = NULL;
/* make p_root father of this new moov */
p_moov->p_father = p_root;
/* make p_root father of this new moov */
p_moov->p_father = p_root;
/* insert this new moov box as first child of p_root */
p_moov->p_next = p_root->p_first;
p_root->p_first = p_moov;
}
/* insert this new moov box as first child of p_root */
p_moov->p_next = p_root->p_first;
p_root->p_first = p_moov;
}
return p_root;
error:
free( p_root );
return NULL;
}
......
......@@ -1140,12 +1140,14 @@ typedef struct
uint32_t MaxWidth;
uint32_t MaxHeight;
uint32_t SamplingRate;
uint32_t AvgBytesPerSec;
uint32_t Channels;
uint32_t BitsPerSample;
uint32_t PacketSize;
uint32_t AudioTag;
uint16_t nBlockAlign;
char *CodecPrivateData;
uint8_t cpd_len;
uint8_t *CodecPrivateData;
} MP4_Box_data_stra_t;
/*
......@@ -1346,7 +1348,8 @@ typedef struct
MP4_Box_t *p_sample;/* point on actual sdsd */
bool b_drms;
bool b_end_of_chunk;
bool b_has_non_empty_cchunk;
bool b_codec_need_restart;
void *p_drms;
MP4_Box_t *p_skcr;
......@@ -1477,13 +1480,6 @@ static const UUID_t StraBoxUUID = {
0x96, 0xc7, 0xbf, 0x25, 0xf9, 0x7e, 0x24, 0x47 } };
MP4_Box_t *MP4_BoxGetSmooBox( stream_t * );
/*****************************************************************************
* MP4_BoxGetInitFrag : Parse the initialization segment.
*****************************************************************************
* The first box is a virtual box "root", and is the father of the boxes
* 'ftyp' and 'moov'.
*****************************************************************************/
MP4_Box_t *MP4_BoxGetInitFrag( stream_t * );
/*****************************************************************************
* MP4_BoxGetNextChunk : Parse the entire moof box.
......
......@@ -35,6 +35,7 @@
#include <vlc_charset.h> /* EnsureUTF8 */
#include <vlc_meta.h> /* vlc_meta_t, vlc_meta_ */
#include <vlc_input.h>
#include <assert.h>
#include "libmp4.h"
#include "id3genres.h" /* for ATOM_gnre */
......@@ -59,6 +60,7 @@ vlc_module_end ()
*****************************************************************************/
static int Demux ( demux_t * );
static int DemuxRef( demux_t *p_demux ){ (void)p_demux; return 0;}
static int DemuxFrg( demux_t * );
static int Seek ( demux_t *, mtime_t );
static int Control ( demux_t *, int, va_list );
......@@ -76,6 +78,8 @@ struct demux_sys_t
mp4_track_t *track; /* array of track */
float f_fps; /* number of frame per seconds */
bool b_fragmented; /* fMP4 */
/* */
MP4_Box_t *p_tref_chap;
......@@ -87,6 +91,7 @@ struct demux_sys_t
* Declaration of local function
*****************************************************************************/
static void MP4_TrackCreate ( demux_t *, mp4_track_t *, MP4_Box_t *, bool b_force_enable );
static int MP4_frg_TrackCreate( demux_t *, mp4_track_t *, MP4_Box_t *);
static void MP4_TrackDestroy( mp4_track_t * );
static int MP4_TrackSelect ( demux_t *, mp4_track_t *, mtime_t );
......@@ -102,10 +107,15 @@ static void MP4_TrackSetELST( demux_t *, mp4_track_t *, int64_t );
static void MP4_UpdateSeekpoint( demux_t * );
static const char *MP4_ConvertMacCode( uint16_t );
/* Return time in s of a track */
/* Return time in microsecond of a track */
static inline int64_t MP4_TrackGetDTS( demux_t *p_demux, mp4_track_t *p_track )
{
#define chunk p_track->chunk[p_track->i_chunk]
demux_sys_t *p_sys = p_demux->p_sys;
mp4_chunk_t chunk;
if( p_sys->b_fragmented )
chunk = *p_track->cchunk;
else
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;
......@@ -127,8 +137,6 @@ static inline int64_t MP4_TrackGetDTS( demux_t *p_demux, mp4_track_t *p_track )
}
}
#undef chunk
/* now handle elst */
if( p_track->p_elst )
{
......@@ -153,9 +161,15 @@ static inline int64_t MP4_TrackGetDTS( demux_t *p_demux, mp4_track_t *p_track )
return INT64_C(1000000) * i_dts / p_track->i_timescale;
}
static inline int64_t MP4_TrackGetPTSDelta( mp4_track_t *p_track )
static inline int64_t MP4_TrackGetPTSDelta( demux_t *p_demux, mp4_track_t *p_track )
{
mp4_chunk_t *ck = &p_track->chunk[p_track->i_chunk];
demux_sys_t *p_sys = p_demux->p_sys;
mp4_chunk_t *ck;
if( p_sys->b_fragmented )
ck = p_track->cchunk;
else
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;
......@@ -179,6 +193,98 @@ static inline int64_t MP4_GetMoviePTS(demux_sys_t *p_sys )
static void LoadChapter( demux_t *p_demux );
static int LoadInitFrag( demux_t *p_demux, const bool b_smooth )
{
demux_sys_t *p_sys = p_demux->p_sys;
if( b_smooth ) /* Smooth Streaming */
{
if( ( p_sys->p_root = MP4_BoxGetSmooBox( p_demux->s ) ) == NULL )
{
goto LoadInitFragError;
}
else
{
MP4_Box_t *p_smoo = MP4_BoxGet( p_sys->p_root, "uuid" );
if( !p_smoo || CmpUUID( &p_smoo->i_uuid, &SmooBoxUUID ) )
goto LoadInitFragError;
/* Get number of tracks */
p_sys->i_tracks = 0;
for( int i = 0; i < 3; i++ )
{
MP4_Box_t *p_stra = MP4_BoxGet( p_smoo, "uuid[%d]", i );
if( p_stra && p_stra->data.p_stra->i_track_ID )
p_sys->i_tracks++;
/* Get timescale and duration of the video track; */
if( i == 0 )
{
p_sys->i_timescale = p_stra->data.p_stra->i_timescale;
p_sys->i_duration = p_stra->data.p_stra->i_duration;
}
}
}
}
else
{
/* Load all boxes ( except raw data ) */
if( ( p_sys->p_root = MP4_BoxGetRoot( p_demux->s ) ) == NULL )
{
goto LoadInitFragError;
}
}
return VLC_SUCCESS;
LoadInitFragError:
msg_Warn( p_demux, "MP4 plugin discarded (not a valid initialization chunk)" );
return VLC_EGENERIC;
}
static int InitTracks( demux_t *p_demux )
{
demux_sys_t *p_sys = p_demux->p_sys;
p_sys->track = calloc( p_sys->i_tracks, sizeof( mp4_track_t ) );
if( p_sys->track == NULL )
return VLC_EGENERIC;
if( p_sys->b_fragmented )
{
mp4_track_t *p_track;
for( uint16_t i = 0; i < p_sys->i_tracks; i++ )
{
p_track = &p_sys->track[i];
p_track->cchunk = calloc( 1, sizeof( mp4_chunk_t ) );
if( unlikely( !p_track->cchunk ) )
{
free( p_sys->track );
return VLC_EGENERIC;
}
}
}
return VLC_SUCCESS;
}
static void CreateTracksFromSmooBox( demux_t *p_demux )
{
demux_sys_t *p_sys = p_demux->p_sys;
MP4_Box_t *p_smoo = MP4_BoxGet( p_sys->p_root, "uuid" );
mp4_track_t *p_track;
int j = 0;
for( int i = 0; i < 3; i++ )
{
MP4_Box_t *p_stra = MP4_BoxGet( p_smoo, "uuid[%d]", i );
if( !p_stra || p_stra->data.p_stra->i_track_ID == 0 )
continue;
else
{
p_track = &p_sys->track[j]; j++;
MP4_frg_TrackCreate( p_demux, p_track, p_stra );
p_track->p_es = es_out_Add( p_demux->out, &p_track->fmt );
}
}
}
/*****************************************************************************
* Open: check file and initializes MP4 structures
*****************************************************************************/
......@@ -211,6 +317,7 @@ static int Open( vlc_object_t * p_this )
case ATOM_free:
case ATOM_skip:
case ATOM_wide:
case ATOM_uuid:
case VLC_FOURCC( 'p', 'n', 'o', 't' ):
break;
case ATOM_ftyp:
......@@ -223,10 +330,10 @@ static int Open( vlc_object_t * p_this )
}
/* I need to seek */
stream_Control( p_demux->s, STREAM_CAN_FASTSEEK, &b_seekable );
stream_Control( p_demux->s, STREAM_CAN_SEEK, &b_seekable );
if( !b_seekable )
{
msg_Warn( p_demux, "MP4 plugin discarded (not fastseekable)" );
msg_Warn( p_demux, "MP4 plugin discarded (not seekable)" );
return VLC_EGENERIC;
}
......@@ -237,10 +344,49 @@ static int Open( vlc_object_t * p_this )
/* create our structure that will contains all data */
p_demux->p_sys = p_sys = calloc( 1, sizeof( demux_sys_t ) );
/* Now load all boxes ( except raw data ) */
if( ( p_sys->p_root = MP4_BoxGetRoot( p_demux->s ) ) == NULL )
/* Is it Smooth Streaming? */
bool b_smooth = false;
if( stream_Peek( p_demux->s, &p_peek, 24 ) < 24 ) return VLC_EGENERIC;
if( !CmpUUID( (UUID_t *)(p_peek + 8), &SmooBoxUUID ) )
{
b_smooth = true;
p_sys->b_fragmented = true;
}
if( LoadInitFrag( p_demux, b_smooth ) != VLC_SUCCESS )
goto error;
if( MP4_BoxCount( p_sys->p_root, "/moov/mvex" ) > 0 )
{
p_sys->b_fragmented = true;
}
if( p_sys->b_fragmented )
{
p_demux->pf_demux = DemuxFrg;
}
stream_Control( p_demux->s, STREAM_CAN_FASTSEEK, &b_seekable );
if( b_smooth )
{
if( InitTracks( p_demux ) != VLC_SUCCESS )
goto error;
CreateTracksFromSmooBox( p_demux );
return VLC_SUCCESS;
}
else if( p_sys->b_fragmented && b_seekable )
{
/* We are not yet able to demux a fragmented MP4 file, using the 'mfra'
* box. So if STREAM_CAN_FASTSEEK is true, we're assuming we've got such
* a file, and we let avformat do the job. */
msg_Warn( p_demux, "MP4 plugin discarded "\
"(fast-seekable and fragmented, let avformat demux it)" );
stream_Seek( p_demux->s, 0 ); /* rewind, for other demux */
goto error;
}
else if( !p_sys->b_fragmented && !b_seekable )
{
msg_Warn( p_demux, "MP4 plugin discarded (not a valid file)" );
msg_Warn( p_demux, "MP4 plugin discarded (not fast-seekable)" );
stream_Seek( p_demux->s, 0 ); /* rewind, for other demux */
goto error;
}
......@@ -292,13 +438,8 @@ static int Open( vlc_object_t * p_this )
if( !p_foov )
{
/* search also for moof box used by smoothstreaming */
p_foov = MP4_BoxGet( p_sys->p_root, "/moof" );
if( !p_foov )
{
msg_Err( p_demux, "MP4 plugin discarded (no moov,foov,moof box)" );
goto error;
}
msg_Err( p_demux, "MP4 plugin discarded (no moov,foov,moof box)" );
goto error;
}
/* we have a free box as a moov, rename it */
p_foov->i_type = ATOM_moov;
......@@ -415,9 +556,7 @@ static int Open( vlc_object_t * p_this )
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 )
if( InitTracks( p_demux ) != VLC_SUCCESS )
goto error;
/* Search the first chap reference (like quicktime) and
......@@ -640,7 +779,7 @@ static int Demux( demux_t *p_demux )
/* dts */
p_block->i_dts = VLC_TS_0 + MP4_TrackGetDTS( p_demux, tk );
/* pts */
i_delta = MP4_TrackGetPTSDelta( tk );
i_delta = MP4_TrackGetPTSDelta( p_demux, tk );
if( i_delta != -1 )
p_block->i_pts = p_block->i_dts + i_delta;
else if( tk->fmt.i_cat != VIDEO_ES )
......@@ -706,6 +845,40 @@ static int Seek( demux_t *p_demux, mtime_t i_date )
return VLC_SUCCESS;
}
static int MP4_frg_Seek( demux_t *p_demux, double f )
{
demux_sys_t *p_sys = p_demux->p_sys;
int64_t i64 = stream_Size( p_demux->s );
if( stream_Seek( p_demux->s, (int64_t)(i64 * f) ) )
{
return VLC_EGENERIC;
}
else
{
/* update global time */
p_sys->i_time = (uint64_t)(f * (double)p_sys->i_duration);
p_sys->i_pcr = MP4_GetMoviePTS( p_sys );
for( unsigned i_track = 0; i_track < p_sys->i_tracks; i_track++ )
{
mp4_track_t *tk = &p_sys->track[i_track];
/* We don't want the current chunk to be flushed */
tk->cchunk->i_sample = tk->cchunk->i_sample_count;
/* reset/update some values */
tk->i_sample = tk->i_sample_first = 0;
tk->i_first_dts = p_sys->i_time;
/* We want to discard the current chunk and get the next one at once */
tk->b_has_non_empty_cchunk = false;
}
es_out_Control( p_demux->out, ES_OUT_SET_NEXT_DISPLAY_TIME, p_sys->i_pcr );
return VLC_SUCCESS;
}
}
/*****************************************************************************
* Control:
*****************************************************************************/
......@@ -732,7 +905,11 @@ static int Control( demux_t *p_demux, int i_query, va_list args )
case DEMUX_SET_POSITION:
f = (double)va_arg( args, double );
if( p_sys->i_timescale > 0 )
if( p_sys->b_fragmented )
{
return MP4_frg_Seek( p_demux, f );
}
else if( p_sys->i_timescale > 0 )
{
i64 = (int64_t)( f * (double)1000000 *
(double)p_sys->i_duration /
......@@ -1024,7 +1201,7 @@ static void LoadChapterApple( demux_t *p_demux, mp4_track_t *tk )
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 int64_t i_pts_delta = MP4_TrackGetPTSDelta( p_demux, tk );
const unsigned int i_size = MP4_TrackSampleSize( tk );
if( i_size > 0 && !stream_Seek( p_demux->s, MP4_TrackGetPos( tk ) ) )
......@@ -1096,6 +1273,10 @@ static void LoadChapter( demux_t *p_demux )
static int TrackCreateChunksIndex( demux_t *p_demux,
mp4_track_t *p_demux_track )
{
demux_sys_t *p_sys = p_demux->p_sys;
if( p_sys->b_fragmented )
return VLC_SUCCESS;
MP4_Box_t *p_co64; /* give offset for each chunk, same for stco and co64 */
MP4_Box_t *p_stsc;
......@@ -1183,6 +1364,10 @@ static int TrackCreateChunksIndex( demux_t *p_demux,
static int TrackCreateSamplesIndex( demux_t *p_demux,
mp4_track_t *p_demux_track )
{
demux_sys_t *p_sys = p_demux->p_sys;
if( p_sys->b_fragmented )
return VLC_SUCCESS;
MP4_Box_t *p_box;
MP4_Box_data_stsz_t *stsz;
MP4_Box_data_stts_t *stts;
......@@ -1432,8 +1617,15 @@ static void TrackGetESSampleRate( unsigned *pi_num, unsigned *pi_den,
static int TrackCreateES( demux_t *p_demux, mp4_track_t *p_track,
unsigned int i_chunk, es_out_id_t **pp_es )
{
const unsigned i_sample_description_index =
p_track->chunk[i_chunk].i_sample_description_index;
demux_sys_t *p_sys = p_demux->p_sys;
unsigned int i_sample_description_index;
if( p_sys->b_fragmented )
i_sample_description_index = 1; /* XXX */
else
i_sample_description_index =
p_track->chunk[i_chunk].i_sample_description_index;
MP4_Box_t *p_sample;
MP4_Box_t *p_esds;
MP4_Box_t *p_frma;
......@@ -2825,3 +3017,716 @@ static const char *MP4_ConvertMacCode( uint16_t i_code )
}
return "";
}
/******************************************************************************
* Here are the functions used for fragmented MP4
*****************************************************************************/
/**
* It computes the sample rate for a video track using current video chunk
*/
static void ChunkGetESSampleRate( unsigned *pi_num, unsigned *pi_den,
const mp4_track_t *p_track )
{
if( p_track->cchunk->i_last_dts == 0 )
return;
*pi_num = 0;
*pi_den = 0;
/* */
const mp4_chunk_t *p_chunk = p_track->cchunk;
uint32_t i_sample = p_chunk->i_sample_count;
uint64_t i_first_dts = p_chunk->i_first_dts;
uint64_t i_last_dts = p_chunk->i_last_dts;
if( i_sample > 1 && i_first_dts < i_last_dts )
vlc_ureduce( pi_num, pi_den,
( i_sample - 1) * p_track->i_timescale,
i_last_dts - i_first_dts,
UINT16_MAX);
}
/**
* Build raw avcC box (without the 8 bytes header)
* \return The size of the box.
*/
static int build_raw_avcC( uint8_t **p_extra, const uint8_t *CodecPrivateData,
const unsigned cpd_len )
{
uint8_t *avcC;
unsigned sps_len = 0, pps_len = 0;
const uint32_t mark = 0x00000001;
assert( CodecPrivateData[0] == 0 );
assert( CodecPrivateData[1] == 0 );
assert( CodecPrivateData[2] == 0 );
assert( CodecPrivateData[3] == 1 );