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: default 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 ) ...@@ -159,15 +159,20 @@ static int MP4_NextBox( stream_t *p_stream, MP4_Box_t *p_box )
if( p_box->p_father ) if( p_box->p_father )
{ {
const off_t i_box_end = p_box->i_size + p_box->i_pos; /* if father's size == 0, it means unknown or infinite size,
const off_t i_father_end = p_box->p_father->i_size + p_box->p_father->i_pos; * and we skip the followong check */
if( p_box->p_father->i_size > 0 )
/* check if it's within p-father */
if( i_box_end >= i_father_end )
{ {
if( i_box_end > i_father_end ) const off_t i_box_end = p_box->i_size + p_box->i_pos;
msg_Dbg( p_stream, "out of bound child" ); const off_t i_father_end = p_box->p_father->i_size + p_box->p_father->i_pos;
return 0; /* out of bound */
/* 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 ) ) 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 ) ...@@ -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 * after called one of theses functions, file position is unknown
* you need to call MP4_GotoBox to go where you want * 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; 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) ) (off_t)(p_container->i_pos + p_container->i_size) )
)
{ {
/* there is no box to load */ /* there is no box to load */
return 0; return 0;
...@@ -204,14 +214,23 @@ static int MP4_ReadBoxContainerRaw( stream_t *p_stream, MP4_Box_t *p_container ) ...@@ -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; else p_container->p_last->p_next = p_box;
p_container->p_last = 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 ); } while( MP4_NextBox( p_stream, p_box ) == 1 );
return 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 ) 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 */ /* container is empty, 8 stand for the first header in this box */
return 1; return 1;
...@@ -497,22 +516,23 @@ static int MP4_ReadBox_stra( stream_t *p_stream, MP4_Box_t *p_box ) ...@@ -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->BitsPerSample );
MP4_GET4BYTES( p_stra->PacketSize ); MP4_GET4BYTES( p_stra->PacketSize );
MP4_GET4BYTES( p_stra->AudioTag ); MP4_GET4BYTES( p_stra->AudioTag );
MP4_GET4BYTES( p_stra->AvgBytesPerSec );
MP4_GET2BYTES( p_stra->nBlockAlign ); MP4_GET2BYTES( p_stra->nBlockAlign );
MP4_GET1BYTE( i_reserved ); MP4_GET1BYTE( i_reserved );
MP4_GET1BYTE( i_reserved ); MP4_GET1BYTE( i_reserved );
MP4_GET1BYTE( i_reserved ); MP4_GET1BYTE( i_reserved );
uint8_t codec_data_length; MP4_GET1BYTE( p_stra->cpd_len );
MP4_GET1BYTE( codec_data_length ); if( p_stra->cpd_len > i_read )
p_stra->CodecPrivateData = malloc( codec_data_length + 1); goto error;
p_stra->CodecPrivateData = malloc( p_stra->cpd_len );
if( unlikely( p_stra->CodecPrivateData == NULL ) ) if( unlikely( p_stra->CodecPrivateData == NULL ) )
goto error; goto error;
MP4_GETSTRINGZ( p_stra->CodecPrivateData ); memcpy( p_stra->CodecPrivateData, p_peek, p_stra->cpd_len );
#ifdef MP4_VERBOSE #ifdef MP4_VERBOSE
msg_Dbg( p_stream, "es_cat is %"PRIu8", birate is %"PRIu32", "\ msg_Dbg( p_stream, "es_cat is %"PRIu8", birate is %"PRIu32,
"CodecPrivateData is %s", p_stra->i_es_cat, p_stra->i_es_cat, p_stra->Bitrate );
p_stra->Bitrate, p_stra->CodecPrivateData );
#endif #endif
MP4_READBOX_EXIT( 1 ); MP4_READBOX_EXIT( 1 );
...@@ -3474,57 +3494,7 @@ error: ...@@ -3474,57 +3494,7 @@ error:
return NULL; 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 #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 ) MP4_Box_t *MP4_BoxGetNextChunk( stream_t *s )
{ {
/* p_chunk is a virtual root container for the moof and mdat boxes */ /* 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 ) ...@@ -3546,7 +3516,7 @@ MP4_Box_t *MP4_BoxGetNextChunk( stream_t *s )
} }
else if( p_tmp_box->i_type == ATOM_ftyp ) else if( p_tmp_box->i_type == ATOM_ftyp )
{ {
return MP4_BoxGetInitFrag( s ); return MP4_BoxGetRoot( s );
} }
free( p_tmp_box ); free( p_tmp_box );
...@@ -3620,7 +3590,8 @@ MP4_Box_t *MP4_BoxGetRoot( stream_t *s ) ...@@ -3620,7 +3590,8 @@ MP4_Box_t *MP4_BoxGetRoot( stream_t *s )
p_root->i_pos = 0; p_root->i_pos = 0;
p_root->i_type = ATOM_root; p_root->i_type = ATOM_root;
p_root->i_shortsize = 1; 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 ); CreateUUID( &p_root->i_uuid, p_root->i_type );
p_root->data.p_data = NULL; p_root->data.p_data = NULL;
...@@ -3631,37 +3602,52 @@ MP4_Box_t *MP4_BoxGetRoot( stream_t *s ) ...@@ -3631,37 +3602,52 @@ MP4_Box_t *MP4_BoxGetRoot( stream_t *s )
p_stream = 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 ); i_result = MP4_ReadBoxContainerRaw( p_stream, p_root );
if( i_result ) if( !i_result )
{ goto error;
MP4_Box_t *p_moov;
MP4_Box_t *p_cmov;
/* check if there is a cmov, if so replace MP4_Box_t *p_moov;
compressed moov by uncompressed one */ MP4_Box_t *p_cmov;
if( ( ( p_moov = MP4_BoxGet( p_root, "moov" ) ) &&
( p_cmov = MP4_BoxGet( p_root, "moov/cmov" ) ) ) || /* check if there is a cmov, if so replace
( ( p_moov = MP4_BoxGet( p_root, "foov" ) ) && compressed moov by uncompressed one */
( p_cmov = MP4_BoxGet( p_root, "foov/cmov" ) ) ) ) if( ( ( p_moov = MP4_BoxGet( p_root, "moov" ) ) &&
{ ( p_cmov = MP4_BoxGet( p_root, "moov/cmov" ) ) ) ||
/* rename the compressed moov as a box to skip */ ( ( p_moov = MP4_BoxGet( p_root, "foov" ) ) &&
p_moov->i_type = ATOM_skip; ( 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 */ /* get uncompressed p_moov */
p_moov = p_cmov->data.p_cmov->p_moov; p_moov = p_cmov->data.p_cmov->p_moov;
p_cmov->data.p_cmov->p_moov = NULL; p_cmov->data.p_cmov->p_moov = NULL;
/* make p_root father of this new moov */ /* make p_root father of this new moov */
p_moov->p_father = p_root; p_moov->p_father = p_root;
/* insert this new moov box as first child of p_root */ /* insert this new moov box as first child of p_root */
p_moov->p_next = p_root->p_first; p_moov->p_next = p_root->p_first;
p_root->p_first = p_moov; p_root->p_first = p_moov;
}
} }
return p_root; return p_root;
error:
free( p_root );
return NULL;
} }
......
...@@ -1140,12 +1140,14 @@ typedef struct ...@@ -1140,12 +1140,14 @@ typedef struct
uint32_t MaxWidth; uint32_t MaxWidth;
uint32_t MaxHeight; uint32_t MaxHeight;
uint32_t SamplingRate; uint32_t SamplingRate;
uint32_t AvgBytesPerSec;
uint32_t Channels; uint32_t Channels;
uint32_t BitsPerSample; uint32_t BitsPerSample;
uint32_t PacketSize; uint32_t PacketSize;
uint32_t AudioTag; uint32_t AudioTag;
uint16_t nBlockAlign; uint16_t nBlockAlign;
char *CodecPrivateData; uint8_t cpd_len;
uint8_t *CodecPrivateData;
} MP4_Box_data_stra_t; } MP4_Box_data_stra_t;
/* /*
...@@ -1346,7 +1348,8 @@ typedef struct ...@@ -1346,7 +1348,8 @@ typedef struct
MP4_Box_t *p_sample;/* point on actual sdsd */ MP4_Box_t *p_sample;/* point on actual sdsd */
bool b_drms; bool b_drms;
bool b_end_of_chunk; bool b_has_non_empty_cchunk;
bool b_codec_need_restart;
void *p_drms; void *p_drms;
MP4_Box_t *p_skcr; MP4_Box_t *p_skcr;
...@@ -1477,13 +1480,6 @@ static const UUID_t StraBoxUUID = { ...@@ -1477,13 +1480,6 @@ static const UUID_t StraBoxUUID = {
0x96, 0xc7, 0xbf, 0x25, 0xf9, 0x7e, 0x24, 0x47 } }; 0x96, 0xc7, 0xbf, 0x25, 0xf9, 0x7e, 0x24, 0x47 } };
MP4_Box_t *MP4_BoxGetSmooBox( stream_t * ); 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. * MP4_BoxGetNextChunk : Parse the entire moof box.
......
This diff is collapsed.
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment