Commit 960df9a9 authored by Laurent Aimar's avatar Laurent Aimar

* all : preliminary mp4 (and some mov) demux plugin. I've not enabled

it ( add by hand to Makefile ... and link it with zlib -lz ) as I will
probably break it too many times. It work for some mp4(but without
audio, need aac decoder) and mov with sorenson v1(ffmpeg :).
parent 7d5c3cfd
mp4_SOURCES = mp4.c libmp4.c
/*****************************************************************************
* libmp4.c : LibMP4 library for mp4 module for vlc
*****************************************************************************
* Copyright (C) 2001 VideoLAN
* $Id: libmp4.c,v 1.1 2002/07/17 21:37:27 fenrir Exp $
* Authors: Laurent Aimar <fenrir@via.ecp.fr>
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 2 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* 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.
*****************************************************************************/
#include <stdlib.h> /* malloc(), free() */
#include <string.h> /* strdup() */
#include <errno.h>
#include <sys/types.h>
#include <vlc/vlc.h>
#include <vlc/input.h>
#include <zlib.h> /* for compressed moov */
#include "libmp4.h"
/*****************************************************************************
* Here are defined some macro to make life simpler but before using it
* *look* at the code.
*
* XXX: All macro are written in capital letters
*
*****************************************************************************/
#define MP4_BOX_HEADERSIZE( p_box ) \
( 8 + ( p_box->i_shortsize == 1 ? 8 : 0 ) \
+ ( p_box->i_type == FOURCC_uuid ? 16 : 0 ) )
#define MP4_BOX_DESCEND( p_box ) \
MP4_SeekStream( p_stream, p_box->i_pos + MP4_BOX_HEADERSIZE( p_box ) );
#define MP4_GET1BYTE( dst ) \
dst = *p_peek; p_peek++; i_read--
#define MP4_GET2BYTES( dst ) \
dst = GetWBE( p_peek ); p_peek += 2; i_read -= 2
#define MP4_GET3BYTES( dst ) \
dst = GetDWBE( p_peek ) >> 8 ; p_peek += 3; i_read -= 3
#define MP4_GET4BYTES( dst ) \
dst = GetDWBE( p_peek ); p_peek += 4; i_read -= 4
#define MP4_GET8BYTES( dst ) \
dst = GetQWBE( p_peek ); p_peek += 8; i_read -= 8
#define MP4_GETVERSIONFLAGS( p_void ) \
MP4_GET1BYTE( p_void->i_version ); \
MP4_GET3BYTES( p_void->i_flags )
#define MP4_GETSTRING( p_str ) \
if( p_peek[0] ) \
{ \
p_str = calloc( sizeof( char ), __MIN( strlen( p_peek ), i_read )+1);\
memcpy( p_str, p_peek, __MIN( strlen( p_peek ), i_read ) ); \
p_str[__MIN( strlen( p_peek ), i_read )] = 0; \
p_peek += strlen( p_str ) + 1; \
i_read -= strlen( p_str ) + 1; \
} \
else \
{ \
p_str = NULL; \
}
#define MP4_READBOX_ENTER( MP4_Box_data_TYPE_t ) \
s64 i_read = p_box->i_size; \
u8 *p_peek, *p_buff; \
i_read = p_box->i_size; \
if( !( p_peek = p_buff = malloc( i_read ) ) ) \
{ \
return( 0 ); \
} \
if( !MP4_ReadStream( p_stream, p_peek, i_read ) )\
{ \
free( p_buff ); \
return( 0 ); \
} \
p_peek += MP4_BOX_HEADERSIZE( p_box ); \
i_read -= MP4_BOX_HEADERSIZE( p_box ); \
if( !( p_box->data.p_data = malloc( sizeof( MP4_Box_data_TYPE_t ) ) ) ) \
{ \
free( p_buff ); \
return( 0 ); \
}
#define MP4_READBOX_EXIT( i_code ) \
if( !i_code ) \
free( p_buff ); \
if( i_read < 0 ) \
{ \
msg_Warn( p_stream->p_input, "Not enougth data" ); \
} \
return( i_code )
/* Some assumptions:
* The input method HAVE to be seekable
*/
/* Some functions to manipulate memory */
static inline u16 GetWLE( u8 *p_buff )
{
return( (p_buff[0]) + ( p_buff[1] <<8 ) );
}
static inline u32 GetDWLE( u8 *p_buff )
{
return( p_buff[0] + ( p_buff[1] <<8 ) +
( p_buff[2] <<16 ) + ( p_buff[3] <<24 ) );
}
static inline u16 GetWBE( u8 *p_buff )
{
return( (p_buff[0]<<8) + p_buff[1] );
}
static inline u32 GetDWBE( u8 *p_buff )
{
return( (p_buff[0] << 24) + ( p_buff[1] <<16 ) +
( p_buff[2] <<8 ) + p_buff[3] );
}
static inline u64 GetQWBE( u8 *p_buff )
{
return( ( (u64)GetDWBE( p_buff ) << 32 )|( (u64)GetDWBE( p_buff + 4 ) ) );
}
static inline void GetUUID( UUID_t *p_uuid, u8 *p_buff )
{
memcpy( p_uuid,
p_buff,
16 );
}
static inline void CreateUUID( UUID_t *p_uuid, u32 i_fourcc )
{
/* made by 0xXXXXXXXX-0011-0010-8000-00aa00389b71
where XXXXXXXX is the fourcc */
/* FIXME implement this */
}
/* some functions for mp4 encoding of variables */
void MP4_ConvertDate2Str( char *psz, u64 i_date )
{
int i_day;
int i_hour;
int i_min;
int i_sec;
i_day = i_date / ( 60*60*24);
i_hour = ( i_date /( 60*60 ) ) % 60;
i_min = ( i_date / 60 ) % 60;
i_sec = i_date % 60;
/* FIXME do it correctly, date begin at 1 jan 1904 */
sprintf( psz, "%dd-%2.2dh:%2.2dm:%2.2ds",
i_day, i_hour, i_min, i_sec );
}
/*****************************************************************************
* Some basic functions to manipulate stream more easily in vlc
*
* MP4_TellAbsolute get file position
*
* MP4_SeekAbsolute seek in the file
*
* MP4_ReadData read data from the file in a buffer
*
*****************************************************************************/
off_t MP4_TellAbsolute( input_thread_t *p_input )
{
off_t i_pos;
vlc_mutex_lock( &p_input->stream.stream_lock );
i_pos= p_input->stream.p_selected_area->i_tell -
( p_input->p_last_data - p_input->p_current_data );
vlc_mutex_unlock( &p_input->stream.stream_lock );
return( i_pos );
}
int MP4_SeekAbsolute( input_thread_t *p_input,
off_t i_pos)
{
off_t i_filepos;
if( i_pos >= p_input->stream.p_selected_area->i_size )
{
return( 0 );
}
i_filepos = MP4_TellAbsolute( p_input );
if( i_pos != i_filepos )
{
p_input->pf_seek( p_input, i_pos );
input_AccessReinit( p_input );
}
return( 1 );
}
/* return 1 if success, 0 if fail */
int MP4_ReadData( input_thread_t *p_input, u8 *p_buff, int i_size )
{
data_packet_t *p_data;
int i_read;
if( !i_size )
{
return( 1 );
}
do
{
i_read = input_SplitBuffer(p_input, &p_data, __MIN( i_size, 1024 ) );
if( i_read <= 0 )
{
return( 0 );
}
memcpy( p_buff, p_data->p_payload_start, i_read );
input_DeletePacket( p_input->p_method_data, p_data );
p_buff += i_read;
i_size -= i_read;
} while( i_size );
return( 1 );
}
/*****************************************************************************
* Some basic functions to manipulate MP4_Stream_t, an abstraction o p_input
* in the way that you can read from a memory buffer or from an input
*
*****************************************************************************/
/**** ------- First some function to make abstract from input -------- */
/****************************************************************************
* MP4_InputStream create an stram with an input
*
****************************************************************************/
MP4_Stream_t *MP4_InputStream( input_thread_t *p_input )
{
MP4_Stream_t *p_stream;
if( !( p_stream = malloc( sizeof( MP4_Stream_t ) ) ) )
{
return( NULL );
}
p_stream->b_memory = 0;
p_stream->p_input = p_input;
p_stream->i_start = 0;
p_stream->i_stop = 0;
p_stream->p_buffer = NULL;
return( p_stream );
}
/****************************************************************************
* MP4_MemoryStream create a memory stream
* if p_buffer == NULL, will allocate a buffer of i_size, else
* it uses p_buffer XXX you have to unallocate it yourself !
*
****************************************************************************/
MP4_Stream_t *MP4_MemoryStream( input_thread_t *p_input,
int i_size, u8 *p_buffer )
{
MP4_Stream_t *p_stream;
if( !( p_stream = malloc( sizeof( MP4_Stream_t ) ) ) )
{
return( NULL );
}
p_stream->b_memory = 1;
p_stream->p_input = p_input;
p_stream->i_start = 0;
p_stream->i_stop = i_size;
if( !p_stream->p_buffer )
{
if( !( p_stream->p_buffer = malloc( i_size ) ) )
{
free( p_stream );
return( NULL );
}
}
else
{
p_stream->p_buffer = p_buffer;
}
return( p_stream );
}
/****************************************************************************
* MP4_ReadStream read from a MP4_Stream_t
*
****************************************************************************/
int MP4_ReadStream( MP4_Stream_t *p_stream, u8 *p_buff, int i_size )
{
if( p_stream->b_memory )
{
if( i_size > p_stream->i_stop - p_stream->i_start )
{
return( 0 );
}
memcpy( p_buff,
p_stream->p_buffer + p_stream->i_start,
i_size );
p_stream->i_start += i_size;
return( 1 );
}
else
{
return( MP4_ReadData( p_stream->p_input, p_buff, i_size ) );
}
}
/****************************************************************************
* MP4_PeekStream peek from a MP4_Stream_t
*
****************************************************************************/
int MP4_PeekStream( MP4_Stream_t *p_stream, u8 **pp_peek, int i_size )
{
if( p_stream->b_memory )
{
*pp_peek = p_stream->p_buffer + p_stream->i_start;
return( __MIN(i_size,p_stream->i_stop - p_stream->i_start ));
}
else
{
return( input_Peek( p_stream->p_input, pp_peek, i_size ) );
}
}
/****************************************************************************
* MP4_TellStream give absolute position in the stream
* XXX for a memory stream give position from begining of the buffer
****************************************************************************/
off_t MP4_TellStream( MP4_Stream_t *p_stream )
{
if( p_stream->b_memory )
{
return( p_stream->i_start );
}
else
{
return( MP4_TellAbsolute( p_stream->p_input ) );
}
}
/****************************************************************************
* MP4_SeekStream seek in a MP4_Stream_t
*
****************************************************************************/
int MP4_SeekStream( MP4_Stream_t *p_stream, off_t i_pos)
{
if( p_stream->b_memory )
{
if( i_pos < p_stream->i_stop )
{
p_stream->i_start = i_pos;
return( 1 );
}
else
{
return( 0 );
}
}
else
{
return( MP4_SeekAbsolute( p_stream->p_input, i_pos ) );
}
}
/*****************************************************************************
* MP4_ReadBoxCommon : Load only common parameters for all boxes
*****************************************************************************
* p_box need to be an already allocated MP4_Box_t, and all data
* will only be peek not read
*
* RETURN : 0 if it fail, 1 otherwise
*****************************************************************************/
int MP4_ReadBoxCommon( MP4_Stream_t *p_stream, MP4_Box_t *p_box )
{
int i_read;
u8 *p_peek;
if( ( ( i_read = MP4_PeekStream( p_stream, &p_peek, 32 ) ) < 8 ) )
{
return( 0 );
}
p_box->i_pos = MP4_TellStream( p_stream );
p_box->data.p_data = NULL;
p_box->p_father = NULL;
p_box->p_first = NULL;
p_box->p_last = NULL;
p_box->p_next = NULL;
MP4_GET4BYTES( p_box->i_shortsize );
MP4_GET4BYTES( p_box->i_type );
/* Now special case */
if( p_box->i_shortsize == 1 )
{
/* get the true size on 64 bits */
MP4_GET8BYTES( p_box->i_size );
}
else
{
p_box->i_size = p_box->i_shortsize;
/* XXX size of 0 means that the box extends to end of file */
}
if( p_box->i_type == FOURCC_uuid )
{
/* get extented type on 16 bytes */
GetUUID( &p_box->i_uuid, p_peek );
p_peek += 16; i_read -= 16;
}
else
{
CreateUUID( &p_box->i_uuid, p_box->i_type );
}
#ifdef MP4_VERBOSE
/* FIXME how to write u64 ??? */
if( p_box->i_size )
{
msg_Dbg( p_stream->p_input, "Found Box: %c%c%c%c size %d",
(p_box->i_type>>24)&0xff, (p_box->i_type>>16)&0xff,
(p_box->i_type>>8)&0xff, (p_box->i_type)&0xff,
(u32)p_box->i_size );
}
#endif
return( 1 );
}
/*****************************************************************************
* MP4_MP4_NextBox : Go to the next box
*****************************************************************************
* if p_box == NULL, go to the next box in witch we are( at the begining ).
*****************************************************************************/
int MP4_NextBox( MP4_Stream_t *p_stream, MP4_Box_t *p_box )
{
MP4_Box_t box;
if( !p_box )
{
MP4_ReadBoxCommon( p_stream, &box );
p_box = &box;
}
if( !p_box->i_size )
{
return( 2 ); /* Box with infinite size */
}
if( p_box->p_father )
{
/* check if it's within p-father */
if( p_box->i_size + p_box->i_pos >=
p_box->p_father->i_size + p_box->p_father->i_pos )
{
return( 0 ); /* out of bound */
}
}
return( MP4_SeekStream( p_stream, p_box->i_size + p_box->i_pos ) );
}
/*****************************************************************************
* MP4_MP4_GotoBox : Go to this particular box
*****************************************************************************
* RETURN : 0 if it fail, 1 otherwise
*****************************************************************************/
int MP4_GotoBox( MP4_Stream_t *p_stream, MP4_Box_t *p_box )
{
return( MP4_SeekStream( p_stream, p_box->i_pos ) );
}
/*****************************************************************************
* For all known box a loader is given,
* XXX: all common struct have to be already read by MP4_ReadBoxCommon
* after called one of theses functions, file position is unknown
* you need to call MP4_GotoBox to go where you want
*****************************************************************************/
int MP4_ReadBoxContainerRaw( MP4_Stream_t *p_stream, MP4_Box_t *p_container )
{
MP4_Box_t *p_box;
if( MP4_TellStream( p_stream ) + 8 >
p_container->i_pos + p_container->i_size )
{
/* there is no box to load */
return( 0 );
}
do
{
p_box = malloc( sizeof( MP4_Box_t ) );
if( MP4_ReadBox( p_stream, p_box , p_container ) )
{
/* chain this box with the father and the other at same level */
if( !p_container->p_first )
{
p_container->p_first = p_box;
}
else
{
p_container->p_last->p_next = p_box;
}
p_container->p_last = p_box;
}
else
{
/* free memory */
free( p_box );
break;
}
}while( MP4_NextBox( p_stream, p_box ) == 1 );
return( 1 );
}
int MP4_ReadBoxContainer( MP4_Stream_t *p_stream, MP4_Box_t *p_container )
{
if( p_container->i_size <= MP4_BOX_HEADERSIZE(p_container ) + 8 )
{
/* container is empty, 8 stand for the first header in this box */
return( 1 );
}
/* enter box */
MP4_BOX_DESCEND( p_container );
return( MP4_ReadBoxContainerRaw( p_stream, p_container ) );
}
void MP4_FreeBox_Common( input_thread_t *p_input, MP4_Box_t *p_box )
{
if( p_box->data.p_data )
{
free( p_box->data.p_data );
p_box->data.p_data = NULL;
}
}
int MP4_ReadBoxSkip( MP4_Stream_t *p_stream, MP4_Box_t *p_box )
{
#ifdef MP4_VERBOSE
msg_Dbg( p_stream->p_input, "
Skip box: \"%c%c%c%c\"",
(p_box->i_type>>24)&0xff,
(p_box->i_type>>16)&0xff,
(p_box->i_type>>8)&0xff,
(p_box->i_type)&0xff );
#endif
return( 1 );
}
int MP4_ReadBox_ftyp( MP4_Stream_t *p_stream, MP4_Box_t *p_box )
{
MP4_READBOX_ENTER( MP4_Box_data_ftyp_t );
MP4_GET4BYTES( p_box->data.p_ftyp->i_major_brand );
MP4_GET4BYTES( p_box->data.p_ftyp->i_minor_version );
if( ( p_box->data.p_ftyp->i_compatible_brands_count = i_read / 4 ) )
{
int i;
p_box->data.p_ftyp->i_compatible_brands =
calloc( p_box->data.p_ftyp->i_compatible_brands_count, sizeof(u32));
for( i =0; i < p_box->data.p_ftyp->i_compatible_brands_count; i++ )
{
MP4_GET4BYTES( p_box->data.p_ftyp->i_compatible_brands[i] );
}
}
else
{
p_box->data.p_ftyp->i_compatible_brands = NULL;
}
MP4_READBOX_EXIT( 1 );
}
int MP4_ReadBox_mvhd( MP4_Stream_t *p_stream, MP4_Box_t *p_box )
{
int i;
#ifdef MP4_VERBOSE
char s_creation_time[128];
char s_modification_time[128];
char s_duration[128];
#endif
MP4_READBOX_ENTER( MP4_Box_data_mvhd_t );
MP4_GETVERSIONFLAGS( p_box->data.p_mvhd );
if( p_box->data.p_mvhd->i_version )
{
MP4_GET8BYTES( p_box->data.p_mvhd->i_creation_time );
MP4_GET8BYTES( p_box->data.p_mvhd->i_modification_time );
MP4_GET4BYTES( p_box->data.p_mvhd->i_timescale );
MP4_GET8BYTES( p_box->data.p_mvhd->i_duration );
}
else
{
MP4_GET4BYTES( p_box->data.p_mvhd->i_creation_time );
MP4_GET4BYTES( p_box->data.p_mvhd->i_modification_time );
MP4_GET4BYTES( p_box->data.p_mvhd->i_timescale );
MP4_GET4BYTES( p_box->data.p_mvhd->i_duration );
}
MP4_GET4BYTES( p_box->data.p_mvhd->i_rate );
MP4_GET2BYTES( p_box->data.p_mvhd->i_volume );
MP4_GET2BYTES( p_box->data.p_mvhd->i_reserved1 );
for( i = 0; i < 2; i++ )
{
MP4_GET4BYTES( p_box->data.p_mvhd->i_reserved2[i] );
}
for( i = 0; i < 9; i++ )
{
MP4_GET4BYTES( p_box->data.p_mvhd->i_matrix[i] );
}
for( i = 0; i < 6; i++ )
{
MP4_GET4BYTES( p_box->data.p_mvhd->i_predefined[i] );
}
MP4_GET4BYTES( p_box->data.p_mvhd->i_next_track_id );
#ifdef MP4_VERBOSE
MP4_ConvertDate2Str( s_creation_time, p_box->data.p_mvhd->i_creation_time );
MP4_ConvertDate2Str( s_modification_time,
p_box->data.p_mvhd->i_modification_time );
if( p_box->data.p_mvhd->i_rate )
{
MP4_ConvertDate2Str( s_duration,
p_box->data.p_mvhd->i_duration / p_box->data.p_mvhd->i_rate );
}
else
{
s_duration[0] = 0;
}
msg_Dbg( p_stream->p_input, "Read Box: \"mvhd\" creation %s modification %s time scale %d duration %s rate %f volume %f next track id %d",
s_creation_time,
s_modification_time,
(u32)p_box->data.p_mvhd->i_timescale,
s_duration,
(float)p_box->data.p_mvhd->i_rate / (1<<16 ),
(float)p_box->data.p_mvhd->i_volume / 256 ,
(u32)p_box->data.p_mvhd->i_next_track_id );
#endif
MP4_READBOX_EXIT( 1 );
}
int MP4_ReadBox_tkhd( MP4_Stream_t *p_stream, MP4_Box_t *p_box )
{
int i;
#ifdef MP4_VERBOSE
char s_creation_time[128];
char s_modification_time[128];
char s_duration[128];
#endif
MP4_READBOX_ENTER( MP4_Box_data_tkhd_t );
MP4_GETVERSIONFLAGS( p_box->data.p_tkhd );
if( p_box->data.p_tkhd->i_version )
{
MP4_GET8BYTES( p_box->data.p_tkhd->i_creation_time );
MP4_GET8BYTES( p_box->data.p_tkhd->i_modification_time );
MP4_GET4BYTES( p_box->data.p_tkhd->i_track_ID );
MP4_GET4BYTES( p_box->data.p_tkhd->i_reserved );
MP4_GET8BYTES( p_box->data.p_tkhd->i_duration );
}
else
{
MP4_GET4BYTES( p_box->data.p_tkhd->i_creation_time );
MP4_GET4BYTES( p_box->data.p_tkhd->i_modification_time );
MP4_GET4BYTES( p_box->data.p_tkhd->i_track_ID );
MP4_GET4BYTES( p_box->data.p_tkhd->i_reserved );
MP4_GET4BYTES( p_box->data.p_tkhd->i_duration );
}
for( i = 0; i < 2; i++ )
{
MP4_GET4BYTES( p_box->data.p_tkhd->i_reserved2[i] );
}
MP4_GET2BYTES( p_box->data.p_tkhd->i_layer );
MP4_GET2BYTES( p_box->data.p_tkhd->i_predefined );
MP4_GET2BYTES( p_box->data.p_tkhd->i_volume );
MP4_GET2BYTES( p_box->data.p_tkhd->i_reserved3 );
for( i = 0; i < 9; i++ )
{
MP4_GET4BYTES( p_box->data.p_tkhd->i_matrix[i] );
}
MP4_GET4BYTES( p_box->data.p_tkhd->i_width );
MP4_GET4BYTES( p_box->data.p_tkhd->i_height );
#ifdef MP4_VERBOSE
MP4_ConvertDate2Str( s_creation_time, p_box->data.p_mvhd->i_creation_time );
MP4_ConvertDate2Str( s_modification_time, p_box->data.p_mvhd->i_modification_time );
MP4_ConvertDate2Str( s_duration, p_box->data.p_mvhd->i_duration );
msg_Dbg( p_stream->p_input, "Read Box: \"tkhd\" creation %s modification %s duration %s track ID %d layer %d volume %f width %f height %f",
s_creation_time,
s_modification_time,
s_duration,
p_box->data.p_tkhd->i_track_ID,
p_box->data.p_tkhd->i_layer,
(float)p_box->data.p_tkhd->i_volume / 256 ,
(float)p_box->data.p_tkhd->i_width / 65536,
(float)p_box->data.p_tkhd->i_height / 65536 );
#endif
MP4_READBOX_EXIT( 1 );
}
int MP4_ReadBox_tref( MP4_Stream_t *p_stream, MP4_Box_t *p_box )
{
MP4_Box_t *p_ref;
if( p_box->i_size < MP4_BOX_HEADERSIZE(p_box ) + 8 )
{
/* container is empty, 8 stand for the first header in this box */
return( 1 );
}
if( !MP4_SeekStream( p_stream, p_box->i_pos + MP4_BOX_HEADERSIZE( p_box ) ) )
{
return( 0 );
}
do
{
p_ref = malloc( sizeof( MP4_Box_t ) );
if( MP4_ReadBoxCommon( p_stream, p_ref ) )
{
switch( p_ref->i_type )
{
case( FOURCC_hint ):
case( FOURCC_dpnd ):
case( FOURCC_ipir ):
case( FOURCC_mpod ):
default:
msg_Warn( p_stream->p_input, "Unknown TrackReferenceType %c%c%c%c (uncompletetly loaded)",
( p_ref->i_type >> 24 )&0xff,
( p_ref->i_type >> 16 )&0xff,
( p_ref->i_type >> 8 )&0xff,
( p_ref->i_type )&0xff );
break;
}
/* chain this box with the father and the other at same level */
if( !p_box->p_first )
{
p_box->p_first = p_ref;
p_box->p_last = p_ref;
}
else
{
p_box->p_last->p_next = p_ref;
p_box->p_last = p_ref;
}
p_ref->p_father = p_box;
}
else
{
/* free memory */
free( p_ref );
}
}while( MP4_NextBox( p_stream, p_ref ) == 1 );
#ifdef MP4_VERBOSE
msg_Dbg( p_stream->p_input, "Read Box: \"tref\" " );
#endif
return( 1 );
}
int MP4_ReadBox_mdhd( MP4_Stream_t *p_stream, MP4_Box_t *p_box )
{
int i;
u16 i_language;
#ifdef MP4_VERBOSE
char s_creation_time[128];
char s_modification_time[128];
char s_duration[128];
#endif
MP4_READBOX_ENTER( MP4_Box_data_mdhd_t );
MP4_GETVERSIONFLAGS( p_box->data.p_mdhd );
if( p_box->data.p_mdhd->i_version )
{
MP4_GET8BYTES( p_box->data.p_mdhd->i_creation_time );
MP4_GET8BYTES( p_box->data.p_mdhd->i_modification_time );
MP4_GET4BYTES( p_box->data.p_mdhd->i_timescale );
MP4_GET8BYTES( p_box->data.p_mdhd->i_duration );
}
else
{
MP4_GET4BYTES( p_box->data.p_mdhd->i_creation_time );
MP4_GET4BYTES( p_box->data.p_mdhd->i_modification_time );
MP4_GET4BYTES( p_box->data.p_mdhd->i_timescale );
MP4_GET4BYTES( p_box->data.p_mdhd->i_duration );
}
i_language = GetWBE( p_peek );
for( i = 0; i < 3; i++ )
{
p_box->data.p_mdhd->i_language[i] =
( ( i_language >> ( (2-i)*5 ) )&0x1f ) + 0x60;
}
MP4_GET2BYTES( p_box->data.p_mdhd->i_predefined );
#ifdef MP4_VERBOSE
MP4_ConvertDate2Str( s_creation_time, p_box->data.p_mdhd->i_creation_time );
MP4_ConvertDate2Str( s_modification_time, p_box->data.p_mdhd->i_modification_time );
MP4_ConvertDate2Str( s_duration, p_box->data.p_mdhd->i_duration );
msg_Dbg( p_stream->p_input, "Read Box: \"mdhd\" creation %s modification %s time scale %d duration %s language %c%c%c",
s_creation_time,
s_modification_time,
(u32)p_box->data.p_mdhd->i_timescale,
s_duration,
p_box->data.p_mdhd->i_language[0],
p_box->data.p_mdhd->i_language[1],
p_box->data.p_mdhd->i_language[2] );
#endif
MP4_READBOX_EXIT( 1 );
}
int MP4_ReadBox_hdlr( MP4_Stream_t *p_stream, MP4_Box_t *p_box )
{
MP4_READBOX_ENTER( MP4_Box_data_hdlr_t );
MP4_GETVERSIONFLAGS( p_box->data.p_hdlr );
MP4_GET4BYTES( p_box->data.p_hdlr->i_predefined );
MP4_GET4BYTES( p_box->data.p_hdlr->i_handler_type );
p_box->data.p_hdlr->psz_name = calloc( sizeof( char ), i_read + 1 );
memcpy( p_box->data.p_hdlr->psz_name, p_peek, i_read );
#ifdef MP4_VERBOSE
msg_Dbg( p_stream->p_input, "Read Box: \"hdlr\" hanler type %c%c%c%c name %s",
( p_box->data.p_hdlr->i_handler_type >> 24 )&0xff,
( p_box->data.p_hdlr->i_handler_type >> 16 )&0xff,
( p_box->data.p_hdlr->i_handler_type >> 8 )&0xff,
( p_box->data.p_hdlr->i_handler_type )&0xff,
p_box->data.p_hdlr->psz_name );
#endif
MP4_READBOX_EXIT( 1 );
}
int MP4_ReadBox_vmhd( MP4_Stream_t *p_stream, MP4_Box_t *p_box )
{
int i;
MP4_READBOX_ENTER( MP4_Box_data_vmhd_t );
MP4_GETVERSIONFLAGS( p_box->data.p_vmhd );
MP4_GET2BYTES( p_box->data.p_vmhd->i_graphics_mode );
for( i = 0; i < 3; i++ )
{
MP4_GET2BYTES( p_box->data.p_vmhd->i_opcolor[i] );
}
#ifdef MP4_VERBOSE
msg_Dbg( p_stream->p_input, "Read Box: \"vmhd\" graphics-mode %d opcolor (%d, %d, %d)",
p_box->data.p_vmhd->i_graphics_mode,
p_box->data.p_vmhd->i_opcolor[0],
p_box->data.p_vmhd->i_opcolor[1],
p_box->data.p_vmhd->i_opcolor[2] );
#endif
MP4_READBOX_EXIT( 1 );
}
int MP4_ReadBox_smhd( MP4_Stream_t *p_stream, MP4_Box_t *p_box )
{
MP4_READBOX_ENTER( MP4_Box_data_smhd_t );
MP4_GETVERSIONFLAGS( p_box->data.p_smhd );
MP4_GET2BYTES( p_box->data.p_smhd->i_balance );
MP4_GET4BYTES( p_box->data.p_smhd->i_reserved );
#ifdef MP4_VERBOSE
msg_Dbg( p_stream->p_input, "Read Box: \"smhd\" balance %f",
(float)p_box->data.p_smhd->i_balance / 256 );
#endif
MP4_READBOX_EXIT( 1 );
}
int MP4_ReadBox_hmhd( MP4_Stream_t *p_stream, MP4_Box_t *p_box )
{
MP4_READBOX_ENTER( MP4_Box_data_hmhd_t );
MP4_GETVERSIONFLAGS( p_box->data.p_hmhd );
MP4_GET2BYTES( p_box->data.p_hmhd->i_max_PDU_size );
MP4_GET2BYTES( p_box->data.p_hmhd->i_avg_PDU_size );
MP4_GET4BYTES( p_box->data.p_hmhd->i_max_bitrate );
MP4_GET4BYTES( p_box->data.p_hmhd->i_avg_bitrate );
MP4_GET4BYTES( p_box->data.p_hmhd->i_reserved );
#ifdef MP4_VERBOSE
msg_Dbg( p_stream->p_input, "Read Box: \"hmhd\" maxPDU-size %d avgPDU-size %d max-bitrate %d avg-bitrate %d",
p_box->data.p_hmhd->i_max_PDU_size,
p_box->data.p_hmhd->i_avg_PDU_size,
p_box->data.p_hmhd->i_max_bitrate,
p_box->data.p_hmhd->i_avg_bitrate );
#endif
MP4_READBOX_EXIT( 1 );
}
int MP4_ReadBox_url( MP4_Stream_t *p_stream, MP4_Box_t *p_box )
{
MP4_READBOX_ENTER( MP4_Box_data_url_t );
MP4_GETVERSIONFLAGS( p_box->data.p_url );
MP4_GETSTRING( p_box->data.p_url->psz_location );
#ifdef MP4_VERBOSE
msg_Dbg( p_stream->p_input, "Read Box: \"url\" url: %s",
p_box->data.p_url->psz_location );
#endif
MP4_READBOX_EXIT( 1 );
}
int MP4_ReadBox_urn( MP4_Stream_t *p_stream, MP4_Box_t *p_box )
{
MP4_READBOX_ENTER( MP4_Box_data_urn_t );
MP4_GETVERSIONFLAGS( p_box->data.p_urn );
MP4_GETSTRING( p_box->data.p_urn->psz_name );
MP4_GETSTRING( p_box->data.p_urn->psz_location );
#ifdef MP4_VERBOSE
msg_Dbg( p_stream->p_input, "Read Box: \"urn\" name %s location %s",
p_box->data.p_urn->psz_name,
p_box->data.p_urn->psz_location );
#endif
MP4_READBOX_EXIT( 1 );
}
int MP4_ReadBox_dref( MP4_Stream_t *p_stream, MP4_Box_t *p_box )
{
MP4_READBOX_ENTER( MP4_Box_data_dref_t );
MP4_GETVERSIONFLAGS( p_box->data.p_dref );
MP4_GET4BYTES( p_box->data.p_dref->i_entry_count );
MP4_SeekStream( p_stream, p_box->i_pos + MP4_BOX_HEADERSIZE( p_box ) + 8 );
MP4_ReadBoxContainerRaw( p_stream, p_box );
#ifdef MP4_VERBOSE
msg_Dbg( p_stream->p_input, "Read Box: \"dref\" entry-count %d",
p_box->data.p_dref->i_entry_count );
#endif
MP4_READBOX_EXIT( 1 );
}
int MP4_ReadBox_stts( MP4_Stream_t *p_stream, MP4_Box_t *p_box )
{
int i;
MP4_READBOX_ENTER( MP4_Box_data_stts_t );
MP4_GETVERSIONFLAGS( p_box->data.p_stts );
MP4_GET4BYTES( p_box->data.p_stts->i_entry_count );
p_box->data.p_stts->i_sample_count =
calloc( sizeof( u32 ), p_box->data.p_stts->i_entry_count );
p_box->data.p_stts->i_sample_delta =
calloc( sizeof( u32 ), p_box->data.p_stts->i_entry_count );
for( i = 0; (i < p_box->data.p_stts->i_entry_count )&&( i_read >=8 ); i++ )
{
MP4_GET4BYTES( p_box->data.p_stts->i_sample_count[i] );
MP4_GET4BYTES( p_box->data.p_stts->i_sample_delta[i] );
}
#ifdef MP4_VERBOSE
msg_Dbg( p_stream->p_input, "Read Box: \"stts\" entry-count %d",
p_box->data.p_stts->i_entry_count );
#endif
MP4_READBOX_EXIT( 1 );
}
int MP4_ReadBox_ctts( MP4_Stream_t *p_stream, MP4_Box_t *p_box )
{
int i;
MP4_READBOX_ENTER( MP4_Box_data_ctts_t );
MP4_GETVERSIONFLAGS( p_box->data.p_ctts );
MP4_GET4BYTES( p_box->data.p_ctts->i_entry_count );
p_box->data.p_ctts->i_sample_count =
calloc( sizeof( u32 ), p_box->data.p_ctts->i_entry_count );
p_box->data.p_ctts->i_sample_offset =
calloc( sizeof( u32 ), p_box->data.p_ctts->i_entry_count );
for( i = 0; (i < p_box->data.p_ctts->i_entry_count )&&( i_read >=8 ); i++ )
{
MP4_GET4BYTES( p_box->data.p_ctts->i_sample_count[i] );
MP4_GET4BYTES( p_box->data.p_ctts->i_sample_offset[i] );
}
#ifdef MP4_VERBOSE
msg_Dbg( p_stream->p_input, "Read Box: \"ctts\" entry-count %d",
p_box->data.p_ctts->i_entry_count );
#endif
MP4_READBOX_EXIT( 1 );
}
static inline int MP4_ReadLengthDescriptor( u8 **pp_peek, s64 *i_read )
{
int i_b;
int i_len = 0;
do
{
i_b = **pp_peek;
(*pp_peek)++;
(*i_read)--;
i_len = ( i_len << 7 ) + ( i_b&0x7f );
} while( i_b&0x80 );
return( i_len );
}
int MP4_ReadBox_esds( MP4_Stream_t *p_stream, MP4_Box_t *p_box )
{
#define es_descriptor p_box->data.p_esds->es_descriptor
int i_len;
int i_flags;
int i_type;
MP4_READBOX_ENTER( MP4_Box_data_esds_t );
MP4_GETVERSIONFLAGS( p_box->data.p_esds );
MP4_GET1BYTE( i_type );
if( i_type == 0x03 ) /* MP4ESDescrTag */
{
i_len = MP4_ReadLengthDescriptor( &p_peek, &i_read );
MP4_GET2BYTES( es_descriptor.i_ES_ID );
MP4_GET1BYTE( i_flags );
es_descriptor.b_stream_dependence = ( (i_flags&0x80) != 0);
es_descriptor.b_url = ( (i_flags&0x40) != 0);
es_descriptor.b_OCRstream = ( (i_flags&0x20) != 0);
es_descriptor.i_stream_priority = i_flags&0x1f;
if( es_descriptor.b_stream_dependence )
{
MP4_GET2BYTES( es_descriptor.i_depend_on_ES_ID );
}
if( es_descriptor.b_url )
{
int i_len;
MP4_GET1BYTE( i_len );
es_descriptor.psz_URL = calloc( sizeof(char), i_len + 1 );
memcpy( es_descriptor.psz_URL, p_peek, i_len );
es_descriptor.psz_URL[i_len] = 0;
}
else
{
es_descriptor.psz_URL = NULL;
}
if( es_descriptor.b_OCRstream )
{
MP4_GET2BYTES( es_descriptor.i_OCR_ES_ID );
}
MP4_GET1BYTE( i_type ); /* get next type */
}
if( i_type != 0x04)/* MP4DecConfigDescrTag */
{
MP4_READBOX_EXIT( 1 ); /* rest isn't interesting up to now */
}
i_len = MP4_ReadLengthDescriptor( &p_peek, &i_read );
es_descriptor.p_decConfigDescr =
malloc( sizeof( MP4_descriptor_decoder_config_t ));
MP4_GET1BYTE( es_descriptor.p_decConfigDescr->i_objectTypeIndication );
MP4_GET1BYTE( i_flags );
es_descriptor.p_decConfigDescr->i_streamType = i_flags >> 2;
es_descriptor.p_decConfigDescr->b_upStream = ( i_flags >> 1 )&0x01;
MP4_GET3BYTES( es_descriptor.p_decConfigDescr->i_buffer_sizeDB );
MP4_GET4BYTES( es_descriptor.p_decConfigDescr->i_max_bitrate );
MP4_GET4BYTES( es_descriptor.p_decConfigDescr->i_avg_bitrate );
MP4_GET1BYTE( i_type );
if( i_type != 0x05 )/* MP4DecSpecificDescrTag */
{
MP4_READBOX_EXIT( 1 );
}
i_len = MP4_ReadLengthDescriptor( &p_peek, &i_read );
es_descriptor.p_decConfigDescr->i_decoder_specific_info_len = i_len;
es_descriptor.p_decConfigDescr->p_decoder_specific_info = malloc( i_len );
memcpy( es_descriptor.p_decConfigDescr->p_decoder_specific_info,
p_peek, i_len );
MP4_READBOX_EXIT( 1 );
#undef es_descriptor
}
int MP4_ReadBox_sample_soun( MP4_Stream_t *p_stream, MP4_Box_t *p_box )
{
int i;
MP4_READBOX_ENTER( MP4_Box_data_sample_soun_t );
for( i = 0; i < 6 ; i++ )
{
MP4_GET1BYTE( p_box->data.p_sample_soun->i_reserved1[i] );
}
MP4_GET2BYTES( p_box->data.p_sample_soun->i_data_reference_index );
for( i = 0; i < 2 ; i++ )
{
MP4_GET4BYTES( p_box->data.p_sample_soun->i_reserved2[i] );
}
MP4_GET2BYTES( p_box->data.p_sample_soun->i_channelcount );
MP4_GET2BYTES( p_box->data.p_sample_soun->i_samplesize );
MP4_GET2BYTES( p_box->data.p_sample_soun->i_predefined );
MP4_GET2BYTES( p_box->data.p_sample_soun->i_reserved3 );
MP4_GET2BYTES( p_box->data.p_sample_soun->i_sampleratehi );
MP4_GET2BYTES( p_box->data.p_sample_soun->i_sampleratelo );
#ifdef MP4_VERBOSE
msg_Dbg( p_stream->p_input, "Read Box: \"soun\" in stsd channel %d sample size %d sampl rate %f",
p_box->data.p_sample_soun->i_channelcount,
p_box->data.p_sample_soun->i_samplesize,
(float)p_box->data.p_sample_soun->i_sampleratehi +
(float)p_box->data.p_sample_soun->i_sampleratelo / 65536 );
#endif
MP4_READBOX_EXIT( 1 );
}
int MP4_ReadBox_sample_mp4a( MP4_Stream_t *p_stream, MP4_Box_t *p_box )
{
int i;
MP4_READBOX_ENTER( MP4_Box_data_sample_mp4a_t );
for( i = 0; i < 6 ; i++ )
{
MP4_GET1BYTE( p_box->data.p_sample_mp4a->i_reserved1[i] );
}
MP4_GET2BYTES( p_box->data.p_sample_mp4a->i_data_reference_index );
for( i = 0; i < 2 ; i++ )
{
MP4_GET4BYTES( p_box->data.p_sample_mp4a->i_reserved2[i] );
}
MP4_GET2BYTES( p_box->data.p_sample_mp4a->i_channelcount );
MP4_GET2BYTES( p_box->data.p_sample_mp4a->i_samplesize );
MP4_GET2BYTES( p_box->data.p_sample_mp4a->i_predefined );
MP4_GET2BYTES( p_box->data.p_sample_mp4a->i_reserved3 );
MP4_GET2BYTES( p_box->data.p_sample_mp4a->i_sampleratehi );
MP4_GET2BYTES( p_box->data.p_sample_mp4a->i_sampleratelo );
MP4_SeekStream( p_stream, p_box->i_pos + MP4_BOX_HEADERSIZE( p_box ) + 28 );
MP4_ReadBoxContainerRaw( p_stream, p_box ); /* esds */
#ifdef MP4_VERBOSE
msg_Dbg( p_stream->p_input, "Read Box: \"mp4a\" in stsd channel %d sample size %d sampl rate %f",
p_box->data.p_sample_mp4a->i_channelcount,
p_box->data.p_sample_mp4a->i_samplesize,
(float)p_box->data.p_sample_mp4a->i_sampleratehi +
(float)p_box->data.p_sample_mp4a->i_sampleratelo / 65536 );
#endif
MP4_READBOX_EXIT( 1 );
}
int MP4_ReadBox_sample_vide( MP4_Stream_t *p_stream, MP4_Box_t *p_box )
{
int i;
MP4_READBOX_ENTER( MP4_Box_data_sample_vide_t );
for( i = 0; i < 6 ; i++ )
{
MP4_GET1BYTE( p_box->data.p_sample_vide->i_reserved1[i] );
}
MP4_GET2BYTES( p_box->data.p_sample_vide->i_data_reference_index );
MP4_GET2BYTES( p_box->data.p_sample_vide->i_predefined1 );
MP4_GET2BYTES( p_box->data.p_sample_vide->i_reserved2 );
for( i = 0; i < 3 ; i++ )
{
MP4_GET4BYTES( p_box->data.p_sample_vide->i_predefined2[i] );
}
MP4_GET2BYTES( p_box->data.p_sample_vide->i_width );
MP4_GET2BYTES( p_box->data.p_sample_vide->i_height );
MP4_GET4BYTES( p_box->data.p_sample_vide->i_horizresolution );
MP4_GET4BYTES( p_box->data.p_sample_vide->i_vertresolution );
MP4_GET4BYTES( p_box->data.p_sample_vide->i_reserved3 );
MP4_GET2BYTES( p_box->data.p_sample_vide->i_predefined3 );
memcpy( &p_box->data.p_sample_vide->i_compressorname, p_peek, 32 );
p_peek += 32; i_read -= 32;
MP4_GET2BYTES( p_box->data.p_sample_vide->i_depth );
MP4_GET2BYTES( p_box->data.p_sample_vide->i_predefined4 );
#ifdef MP4_VERBOSE
msg_Dbg( p_stream->p_input, "Read Box: \"vide\" in stsd %dx%d depth %d",
p_box->data.p_sample_vide->i_width,
p_box->data.p_sample_vide->i_height,
p_box->data.p_sample_vide->i_depth );
#endif
MP4_READBOX_EXIT( 1 );
}
int MP4_ReadBox_sample_mp4v( MP4_Stream_t *p_stream, MP4_Box_t *p_box )
{
int i;
MP4_READBOX_ENTER( MP4_Box_data_sample_mp4v_t );
for( i = 0; i < 6 ; i++ )
{
MP4_GET1BYTE( p_box->data.p_sample_mp4v->i_reserved1[i] );
}
MP4_GET2BYTES( p_box->data.p_sample_mp4v->i_data_reference_index );
MP4_GET2BYTES( p_box->data.p_sample_mp4v->i_predefined1 );
MP4_GET2BYTES( p_box->data.p_sample_mp4v->i_reserved2 );
for( i = 0; i < 3 ; i++ )
{
MP4_GET4BYTES( p_box->data.p_sample_mp4v->i_predefined2[i] );
}
MP4_GET2BYTES( p_box->data.p_sample_mp4v->i_width );
MP4_GET2BYTES( p_box->data.p_sample_mp4v->i_height );
MP4_GET4BYTES( p_box->data.p_sample_mp4v->i_horizresolution );
MP4_GET4BYTES( p_box->data.p_sample_mp4v->i_vertresolution );
MP4_GET4BYTES( p_box->data.p_sample_mp4v->i_reserved3 );
MP4_GET2BYTES( p_box->data.p_sample_mp4v->i_predefined3 );
/* This is a pascal string, padded to 32 bytes */
memcpy( &p_box->data.p_sample_mp4v->i_compressorname, p_peek+1, 31 );
p_box->data.p_sample_mp4v->i_compressorname[*p_peek] = 0;
p_peek += 32; i_read -= 32;
MP4_GET2BYTES( p_box->data.p_sample_mp4v->i_depth );
MP4_GET2BYTES( p_box->data.p_sample_mp4v->i_predefined4 );
MP4_SeekStream( p_stream, p_box->i_pos + MP4_BOX_HEADERSIZE( p_box ) + 78);
MP4_ReadBoxContainerRaw( p_stream, p_box ); /* esds */
#ifdef MP4_VERBOSE
msg_Dbg( p_stream->p_input, "Read Box: \"mp4v\" in stsd %dx%d depth %d compressor %s",
p_box->data.p_sample_mp4v->i_width,
p_box->data.p_sample_mp4v->i_height,
p_box->data.p_sample_mp4v->i_depth,
p_box->data.p_sample_mp4v->i_compressorname );
#endif
MP4_READBOX_EXIT( 1 );
}
int MP4_ReadBox_stsd( MP4_Stream_t *p_stream, MP4_Box_t *p_box )
{
MP4_Box_t *p_sample;
MP4_READBOX_ENTER( MP4_Box_data_stsd_t );
MP4_GETVERSIONFLAGS( p_box->data.p_stsd );
MP4_GET4BYTES( p_box->data.p_stsd->i_entry_count );
MP4_SeekStream( p_stream, p_box->i_pos + MP4_BOX_HEADERSIZE( p_box ) + 8 );
do
{
p_sample = malloc( sizeof( MP4_Box_t ) );
if( MP4_ReadBoxCommon( p_stream, p_sample ) )
{
switch( p_sample->i_type )
{
case( FOURCC_soun ):
case( FOURCC__mp3 ):
MP4_ReadBox_sample_soun( p_stream, p_sample );
break;
case( FOURCC_mp4a ):
MP4_ReadBox_sample_mp4a( p_stream, p_sample );
break;
case( FOURCC_DIVX ):
case( FOURCC_SVQ1 ):
case( FOURCC_vide ):
case( FOURCC_3IV1 ):
case( FOURCC_h263 ):
MP4_ReadBox_sample_vide( p_stream, p_sample );
break;
case( FOURCC_mp4v ):
MP4_ReadBox_sample_mp4v( p_stream, p_sample );
break;
case( FOURCC_mp4s ):
case( FOURCC_hint ):
default:
msg_Warn( p_stream->p_input, "Unknown SampleEntry type %c%c%c%c (uncompletetly loaded)",
( p_sample->i_type >> 24 )&0xff,
( p_sample->i_type >> 16 )&0xff,
( p_sample->i_type >> 8 )&0xff,
( p_sample->i_type )&0xff );
break;
}
MP4_GotoBox( p_stream, p_sample );
/* chain this box with the father and the other at same level */
if( !p_box->p_first )
{
p_box->p_first = p_sample;
p_box->p_last = p_sample;
}
else
{
p_box->p_last->p_next = p_sample;
p_box->p_last = p_sample;
}
p_sample->p_father = p_box;
}
else
{
/* free memory */
free( p_sample );
}
}while( MP4_NextBox( p_stream, p_sample ) == 1 );
#ifdef MP4_VERBOSE
msg_Dbg( p_stream->p_input, "Read Box: \"stsd\" entry-count %d",
p_box->data.p_stsd->i_entry_count );
#endif
MP4_READBOX_EXIT( 1 );
}
int MP4_ReadBox_stsz( MP4_Stream_t *p_stream, MP4_Box_t *p_box )
{
int i;
MP4_READBOX_ENTER( MP4_Box_data_stsz_t );
MP4_GETVERSIONFLAGS( p_box->data.p_stsz );
MP4_GET4BYTES( p_box->data.p_stsz->i_sample_size );
MP4_GET4BYTES( p_box->data.p_stsz->i_sample_count );
p_box->data.p_stsz->i_entry_size =
calloc( sizeof( u32 ), p_box->data.p_stsz->i_sample_count );
if( !p_box->data.p_stsz->i_sample_size )
{
for( i=0; (i<p_box->data.p_stsz->i_sample_count)&&(i_read >= 4 ); i++ )
{
MP4_GET4BYTES( p_box->data.p_stsz->i_entry_size[i] );
}
}
#ifdef MP4_VERBOSE
msg_Dbg( p_stream->p_input, "Read Box: \"stsz\" sample-size %d sample-count %d",
p_box->data.p_stsz->i_sample_size,
p_box->data.p_stsz->i_sample_count );
#endif
MP4_READBOX_EXIT( 1 );
}
int MP4_ReadBox_stsc( MP4_Stream_t *p_stream, MP4_Box_t *p_box )
{
int i;
MP4_READBOX_ENTER( MP4_Box_data_stsc_t );
MP4_GETVERSIONFLAGS( p_box->data.p_stsc );
MP4_GET4BYTES( p_box->data.p_stsc->i_entry_count );
p_box->data.p_stsc->i_first_chunk =
calloc( sizeof( u32 ), p_box->data.p_stsc->i_entry_count );
p_box->data.p_stsc->i_samples_per_chunk =
calloc( sizeof( u32 ), p_box->data.p_stsc->i_entry_count );
p_box->data.p_stsc->i_sample_description_index =
calloc( sizeof( u32 ), p_box->data.p_stsc->i_entry_count );
for( i = 0; (i < p_box->data.p_stsc->i_entry_count )&&( i_read >= 12 );i++ )
{
MP4_GET4BYTES( p_box->data.p_stsc->i_first_chunk[i] );
MP4_GET4BYTES( p_box->data.p_stsc->i_samples_per_chunk[i] );
MP4_GET4BYTES( p_box->data.p_stsc->i_sample_description_index[i] );
}
#ifdef MP4_VERBOSE
msg_Dbg( p_stream->p_input, "Read Box: \"stsc\" entry-count %d",
p_box->data.p_stsc->i_entry_count );
#endif
MP4_READBOX_EXIT( 1 );
}
int MP4_ReadBox_stco_co64( MP4_Stream_t *p_stream, MP4_Box_t *p_box )
{
int i;
MP4_READBOX_ENTER( MP4_Box_data_co64_t );
MP4_GETVERSIONFLAGS( p_box->data.p_co64 );
MP4_GET4BYTES( p_box->data.p_co64->i_entry_count );
p_box->data.p_co64->i_chunk_offset =
calloc( sizeof( u64 ), p_box->data.p_co64->i_entry_count );
for( i = 0; (i < p_box->data.p_co64->i_entry_count )&&( i_read >= 8 ); i++ )
{
if( p_box->i_type == FOURCC_stco )
{
MP4_GET4BYTES( p_box->data.p_co64->i_chunk_offset[i] );
}
else
{
MP4_GET8BYTES( p_box->data.p_co64->i_chunk_offset[i] );
}
}
#ifdef MP4_VERBOSE
msg_Dbg( p_stream->p_input, "Read Box: \"co64\" entry-count %d",
p_box->data.p_co64->i_entry_count );
#endif
MP4_READBOX_EXIT( 1 );
}
int MP4_ReadBox_stss( MP4_Stream_t *p_stream, MP4_Box_t *p_box )
{
int i;
MP4_READBOX_ENTER( MP4_Box_data_stss_t );
MP4_GETVERSIONFLAGS( p_box->data.p_stss );
MP4_GET4BYTES( p_box->data.p_stss->i_entry_count );
p_box->data.p_stss->i_sample_number =
calloc( sizeof( u32 ), p_box->data.p_stss->i_entry_count );
for( i = 0; (i < p_box->data.p_stss->i_entry_count )&&( i_read >= 4 ); i++ )
{
MP4_GET4BYTES( p_box->data.p_stss->i_sample_number[i] );
}
#ifdef MP4_VERBOSE
msg_Dbg( p_stream->p_input, "Read Box: \"stss\" entry-count %d",
p_box->data.p_stss->i_entry_count );
#endif
MP4_READBOX_EXIT( 1 );
}
int MP4_ReadBox_stsh( MP4_Stream_t *p_stream, MP4_Box_t *p_box )
{
int i;
MP4_READBOX_ENTER( MP4_Box_data_stsh_t );
MP4_GETVERSIONFLAGS( p_box->data.p_stsh );
MP4_GET4BYTES( p_box->data.p_stsh->i_entry_count );
p_box->data.p_stsh->i_shadowed_sample_number =
calloc( sizeof( u32 ), p_box->data.p_stsh->i_entry_count );
p_box->data.p_stsh->i_sync_sample_number =
calloc( sizeof( u32 ), p_box->data.p_stsh->i_entry_count );
for( i = 0; (i < p_box->data.p_stss->i_entry_count )&&( i_read >= 8 ); i++ )
{
MP4_GET4BYTES( p_box->data.p_stsh->i_shadowed_sample_number[i] );
MP4_GET4BYTES( p_box->data.p_stsh->i_sync_sample_number[i] );
}
#ifdef MP4_VERBOSE
msg_Dbg( p_stream->p_input, "Read Box: \"stsh\" entry-count %d",
p_box->data.p_stsh->i_entry_count );
#endif
MP4_READBOX_EXIT( 1 );
}
int MP4_ReadBox_stdp( MP4_Stream_t *p_stream, MP4_Box_t *p_box )
{
int i;
MP4_READBOX_ENTER( MP4_Box_data_stdp_t );
MP4_GETVERSIONFLAGS( p_box->data.p_stdp );
p_box->data.p_stdp->i_priority =
calloc( sizeof( u16 ), i_read / 2 );
for( i = 0; i < i_read / 2 ; i++ )
{
MP4_GET2BYTES( p_box->data.p_stdp->i_priority[i] );
}
#ifdef MP4_VERBOSE
msg_Dbg( p_stream->p_input, "Read Box: \"stdp\" entry-count %d",
i_read / 2 );
#endif
MP4_READBOX_EXIT( 1 );
}
int MP4_ReadBox_padb( MP4_Stream_t *p_stream, MP4_Box_t *p_box )
{
int i;
MP4_READBOX_ENTER( MP4_Box_data_padb_t );
MP4_GETVERSIONFLAGS( p_box->data.p_padb );
MP4_GET4BYTES( p_box->data.p_padb->i_sample_count );
p_box->data.p_padb->i_reserved1 =
calloc( sizeof( u16 ), ( p_box->data.p_padb->i_sample_count + 1 ) / 2 );
p_box->data.p_padb->i_pad2 =
calloc( sizeof( u16 ), ( p_box->data.p_padb->i_sample_count + 1 ) / 2 );
p_box->data.p_padb->i_reserved2 =
calloc( sizeof( u16 ), ( p_box->data.p_padb->i_sample_count + 1 ) / 2 );
p_box->data.p_padb->i_pad1 =
calloc( sizeof( u16 ), ( p_box->data.p_padb->i_sample_count + 1 ) / 2 );
for( i = 0; i < i_read / 2 ; i++ )
{
p_box->data.p_padb->i_reserved1[i] = ( (*p_peek) >> 7 )&0x01;
p_box->data.p_padb->i_pad2[i] = ( (*p_peek) >> 4 )&0x07;
p_box->data.p_padb->i_reserved1[i] = ( (*p_peek) >> 3 )&0x01;
p_box->data.p_padb->i_pad1[i] = ( (*p_peek) )&0x07;
p_peek += 1; i_read -= 1;
}
#ifdef MP4_VERBOSE
msg_Dbg( p_stream->p_input, "Read Box: \"stdp\" entry-count %d",
i_read / 2 );
#endif
MP4_READBOX_EXIT( 1 );
}
int MP4_ReadBox_elst( MP4_Stream_t *p_stream, MP4_Box_t *p_box )
{
int i;
MP4_READBOX_ENTER( MP4_Box_data_padb_t );
MP4_GETVERSIONFLAGS( p_box->data.p_elst );
MP4_GET4BYTES( p_box->data.p_elst->i_entry_count );
p_box->data.p_elst->i_segment_duration =
calloc( sizeof( u64 ), p_box->data.p_elst->i_entry_count );
p_box->data.p_elst->i_media_time =
calloc( sizeof( u64 ), p_box->data.p_elst->i_entry_count );
p_box->data.p_elst->i_media_rate_integer =
calloc( sizeof( u16 ), p_box->data.p_elst->i_entry_count );
p_box->data.p_elst->i_media_rate_fraction=
calloc( sizeof( u16 ), p_box->data.p_elst->i_entry_count );
for( i = 0; i < p_box->data.p_elst->i_entry_count; i++ )
{
if( p_box->data.p_elst->i_version == 1 )
{
MP4_GET8BYTES( p_box->data.p_elst->i_segment_duration[i] );
MP4_GET8BYTES( p_box->data.p_elst->i_media_time[i] );
}
else
{
MP4_GET4BYTES( p_box->data.p_elst->i_segment_duration[i] );
MP4_GET4BYTES( p_box->data.p_elst->i_media_time[i] );
}
MP4_GET2BYTES( p_box->data.p_elst->i_media_rate_integer[i] );
MP4_GET2BYTES( p_box->data.p_elst->i_media_rate_fraction[i] );
}
#ifdef MP4_VERBOSE
msg_Dbg( p_stream->p_input, "Read Box: \"elst\" entry-count %d",
i_read / 2 );
#endif
MP4_READBOX_EXIT( 1 );
}
int MP4_ReadBox_cprt( MP4_Stream_t *p_stream, MP4_Box_t *p_box )
{
int i_language;
int i;
MP4_READBOX_ENTER( MP4_Box_data_cprt_t );
MP4_GETVERSIONFLAGS( p_box->data.p_cprt );
i_language = GetWBE( p_peek );
for( i = 0; i < 3; i++ )
{
p_box->data.p_cprt->i_language[i] =
( ( i_language >> ( (2-i)*5 ) )&0x1f ) + 0x60;
}
p_peek += 2; i_read -= 2;
MP4_GETSTRING( p_box->data.p_cprt->psz_notice );
#ifdef MP4_VERBOSE
msg_Dbg( p_stream->p_input, "Read Box: \"cprt\" language %c%c%c notivce %s",
p_box->data.p_cprt->i_language[0],
p_box->data.p_cprt->i_language[1],
p_box->data.p_cprt->i_language[2],
p_box->data.p_cprt->psz_notice );
#endif
MP4_READBOX_EXIT( 1 );
}
int MP4_ReadBox_hnti( MP4_Stream_t *p_stream, MP4_Box_t *p_box )
{
MP4_Box_t *p_ref;
// MP4_Box_t *p_rtp;
if( p_box->i_size < MP4_BOX_HEADERSIZE(p_box ) + 8 )
{
/* container is empty, 8 stand for the first header in this box */
return( 1 );
}
if( !MP4_SeekStream( p_stream,p_box->i_pos + MP4_BOX_HEADERSIZE( p_box ) ) )
{
return( 0 );
}
do
{
p_ref = malloc( sizeof( MP4_Box_t ) );
if( MP4_ReadBoxCommon( p_stream, p_ref ) )
{
switch( p_ref->i_type )
{
case( FOURCC_rtp ):
default:
msg_Warn( p_stream->p_input, "Unknown moviehintinformation %c%c%c%c (uncompletetly loaded)",
( p_ref->i_type >> 24 )&0xff,
( p_ref->i_type >> 16 )&0xff,
( p_ref->i_type >> 8 )&0xff,
( p_ref->i_type )&0xff );
break;
}
/* chain this box with the father and the other at same level */
if( !p_box->p_first )
{
p_box->p_first = p_ref;
p_box->p_last = p_ref;
}
else
{
p_box->p_last->p_next = p_ref;
p_box->p_last = p_ref;
}
p_ref->p_father = p_box;
}
else
{
/* free memory */
free( p_ref );
}
}while( MP4_NextBox( p_stream, p_ref ) == 1 );
#ifdef MP4_VERBOSE
msg_Dbg( p_stream->p_input, "Read Box: \"hnti\" " );
#endif
return( 1 );
}
int MP4_ReadBox_dcom( MP4_Stream_t *p_stream, MP4_Box_t *p_box )
{
MP4_READBOX_ENTER( MP4_Box_data_dcom_t );
MP4_GET4BYTES( p_box->data.p_dcom->i_algorithm );
#ifdef MP4_VERBOSE
msg_Dbg( p_stream->p_input,
"Read Box: \"dcom\" compression algorithm : %c%c%c%c",
( p_box->data.p_dcom->i_algorithm >> 24 )&0xff,
( p_box->data.p_dcom->i_algorithm >> 16 )&0xff,
( p_box->data.p_dcom->i_algorithm >> 8 )&0xff,
( p_box->data.p_dcom->i_algorithm )&0xff );
#endif
MP4_READBOX_EXIT( 1 );
}
int MP4_ReadBox_cmvd( MP4_Stream_t *p_stream, MP4_Box_t *p_box )
{
MP4_READBOX_ENTER( MP4_Box_data_cmvd_t );
MP4_GET4BYTES( p_box->data.p_cmvd->i_uncompressed_size );
p_box->data.p_cmvd->i_compressed_size = i_read;
if( !( p_box->data.p_cmvd->p_data = malloc( i_read ) ) )
{
msg_Dbg( p_stream->p_input, "Read Box: \"cmvd\" not enough memory to load data" );
return( 1 );
}
/* now copy compressed data */
memcpy( p_box->data.p_cmvd->p_data,
p_peek,
i_read);
p_box->data.p_cmvd->b_compressed = 1;
#ifdef MP4_VERBOSE
msg_Dbg( p_stream->p_input, "Read Box: \"cmvd\" compressed data size %d",
p_box->data.p_cmvd->i_compressed_size );
#endif
MP4_READBOX_EXIT( 1 );
}
int MP4_ReadBox_cmov( MP4_Stream_t *p_stream, MP4_Box_t *p_box )
{
MP4_Stream_t *p_stream_memory;
MP4_Box_t *p_umov;
MP4_Box_t *p_dcom;
MP4_Box_t *p_cmvd;
z_stream z_data;
u8 *p_data;
int i_result;
if( !( p_box->data.p_cmov = malloc( sizeof( MP4_Box_data_cmov_t ) ) ) )
{
msg_Err( p_stream->p_input, "out of memory" );
return( 0 );
}
memset( p_box->data.p_cmov, 0, sizeof( MP4_Box_data_cmov_t ) );
if( !( p_box->p_father )||
( p_box->p_father->i_type != FOURCC_moov ) )
{
msg_Warn( p_stream->p_input, "Read box: \"cmov\" box alone" );
return( 1 );
}
if( !(i_result = MP4_ReadBoxContainer( p_stream, p_box ) ) )
{
return( 0 );
}
if( !( p_dcom = MP4_FindBox( p_box, FOURCC_dcom ) )||
!( p_cmvd = MP4_FindBox( p_box, FOURCC_cmvd ) )||
!( p_cmvd->data.p_cmvd->p_data ) )
{
msg_Warn( p_stream->p_input, "Read Box: \"cmov\" incomplete" );
return( 1 );
}
if( p_dcom->data.p_dcom->i_algorithm != FOURCC_zlib )
{
msg_Dbg( p_stream->p_input, "Read Box: \"cmov\" compression algorithm : %c%c%c%c not supported",
( p_dcom->data.p_dcom->i_algorithm >> 24 )&0xff,
( p_dcom->data.p_dcom->i_algorithm >> 16 )&0xff,
( p_dcom->data.p_dcom->i_algorithm >> 8 )&0xff,
( p_dcom->data.p_dcom->i_algorithm )&0xff );
return( 1 );
}
/* decompress data */
/* allocate a new buffer */
if( !( p_data = malloc( p_cmvd->data.p_cmvd->i_uncompressed_size ) ) )
{
msg_Err( p_stream->p_input,
"Read Box: \"cmov\" not enough memory to uncompress data" );
return( 1 );
}
/* init default structures */
z_data.next_in = p_cmvd->data.p_cmvd->p_data;
z_data.avail_in = p_cmvd->data.p_cmvd->i_compressed_size;
z_data.next_out = p_data;
z_data.avail_out = p_cmvd->data.p_cmvd->i_uncompressed_size;
z_data.zalloc = (alloc_func)Z_NULL;
z_data.zfree = (free_func)Z_NULL;
z_data.opaque = (voidpf)Z_NULL;
/* init zlib */
if( ( i_result = inflateInit( &z_data ) ) != Z_OK )
{
msg_Err( p_stream->p_input,
"Read Box: \"cmov\" error while uncompressing data" );
free( p_data );
return( 1 );
}
/* uncompress */
i_result = inflate( &z_data, Z_NO_FLUSH );
if( ( i_result != Z_OK )&&( i_result != Z_STREAM_END ) )
{
msg_Err( p_stream->p_input,
"Read Box: \"cmov\" error while uncompressing data" );
free( p_data );
return( 1 );
}
if( p_cmvd->data.p_cmvd->i_uncompressed_size != z_data.total_out )
{
msg_Warn( p_stream->p_input,
"Read Box: \"cmov\" uncompressing data size mismatch" );
}
p_cmvd->data.p_cmvd->i_uncompressed_size = z_data.total_out;
/* close zlib */
i_result = inflateEnd( &z_data );
if( i_result != Z_OK )
{
msg_Warn( p_stream->p_input,
"Read Box: \"cmov\" error while uncompressing data (ignored)" );
}
free( p_cmvd->data.p_cmvd->p_data );
p_cmvd->data.p_cmvd->p_data = p_data;
p_cmvd->data.p_cmvd->b_compressed = 0;
msg_Dbg( p_stream->p_input,
"Read Box: \"cmov\" box succesfully uncompressed" );
/* now create a memory stream */
p_stream_memory = MP4_MemoryStream( p_stream->p_input,
p_cmvd->data.p_cmvd->i_uncompressed_size,
p_cmvd->data.p_cmvd->p_data );
/* and read uncompressd moov */
p_umov = malloc( sizeof( MP4_Box_t ) );
i_result = MP4_ReadBox( p_stream_memory, p_umov, NULL );
p_box->data.p_cmov->p_moov = p_umov;
free( p_stream_memory );
#ifdef MP4_VERBOSE
msg_Dbg( p_stream->p_input,
"Read Box: \"cmov\" compressed movie header completed" );
#endif
return( i_result );
}
/**** ------------------------------------------------------------------- ****/
/**** "Higher level" Functions ****/
/**** ------------------------------------------------------------------- ****/
static struct
{
u32 i_type;
int (*MP4_ReadBox_function )( MP4_Stream_t *p_stream, MP4_Box_t *p_box );
void (*MP4_FreeBox_function )( input_thread_t *p_input, MP4_Box_t *p_box );
} MP4_Box_Function [] =
{
/* Containers */
{ FOURCC_moov, MP4_ReadBoxContainer, MP4_FreeBox_Common },
{ FOURCC_trak, MP4_ReadBoxContainer, MP4_FreeBox_Common },
{ FOURCC_mdia, MP4_ReadBoxContainer, MP4_FreeBox_Common },
{ FOURCC_moof, MP4_ReadBoxContainer, MP4_FreeBox_Common },
{ FOURCC_minf, MP4_ReadBoxContainer, MP4_FreeBox_Common },
{ FOURCC_stbl, MP4_ReadBoxContainer, MP4_FreeBox_Common },
{ FOURCC_dinf, MP4_ReadBoxContainer, MP4_FreeBox_Common },
{ FOURCC_edts, MP4_ReadBoxContainer, MP4_FreeBox_Common },
{ FOURCC_udta, MP4_ReadBoxContainer, MP4_FreeBox_Common },
{ FOURCC_nmhd, MP4_ReadBoxContainer, MP4_FreeBox_Common },
/* specific sbox */
{ FOURCC_ftyp, MP4_ReadBox_ftyp, NULL },
{ FOURCC_cmov, MP4_ReadBox_cmov, NULL },
{ FOURCC_mvhd, MP4_ReadBox_mvhd, NULL },
{ FOURCC_tkhd, MP4_ReadBox_tkhd, NULL },
{ FOURCC_tref, MP4_ReadBox_tref, NULL },
{ FOURCC_mdhd, MP4_ReadBox_mdhd, NULL },
{ FOURCC_hdlr, MP4_ReadBox_hdlr, NULL },
{ FOURCC_vmhd, MP4_ReadBox_vmhd, NULL },
{ FOURCC_smhd, MP4_ReadBox_smhd, NULL },
{ FOURCC_hmhd, MP4_ReadBox_hmhd, NULL },
{ FOURCC_url, MP4_ReadBox_url, NULL },
{ FOURCC_urn, MP4_ReadBox_urn, NULL },
{ FOURCC_dref, MP4_ReadBox_dref, NULL },
{ FOURCC_stts, MP4_ReadBox_stts, NULL },
{ FOURCC_ctts, MP4_ReadBox_ctts, NULL },
{ FOURCC_stsd, MP4_ReadBox_stsd, NULL },
{ FOURCC_stsz, MP4_ReadBox_stsz, NULL },
{ FOURCC_stsc, MP4_ReadBox_stsc, NULL },
{ FOURCC_stco, MP4_ReadBox_stco_co64, NULL },
{ FOURCC_co64, MP4_ReadBox_stco_co64, NULL },
{ FOURCC_stss, MP4_ReadBox_stss, NULL },
{ FOURCC_stsh, MP4_ReadBox_stsh, NULL },
{ FOURCC_stdp, MP4_ReadBox_stdp, NULL },
{ FOURCC_padb, MP4_ReadBox_padb, NULL },
{ FOURCC_elst, MP4_ReadBox_elst, NULL },
{ FOURCC_cprt, MP4_ReadBox_cprt, NULL },
{ FOURCC_hnti, MP4_ReadBox_hnti, NULL },
{ FOURCC_esds, MP4_ReadBox_esds, NULL },
{ FOURCC_dcom, MP4_ReadBox_dcom, NULL },
{ FOURCC_cmvd, MP4_ReadBox_cmvd, NULL },
/* Nothing to do with this box */
{ FOURCC_mdat, MP4_ReadBoxSkip, MP4_FreeBox_Common },
{ FOURCC_skip, MP4_ReadBoxSkip, MP4_FreeBox_Common },
{ FOURCC_free, MP4_ReadBoxSkip, MP4_FreeBox_Common },
{ FOURCC_wide, MP4_ReadBoxSkip, MP4_FreeBox_Common },
/* Last entry */
{ 0, NULL, NULL }
};
/*****************************************************************************
* MP4_ReadBox : parse the actual box and the children
* XXX : Do not go to the next box
*****************************************************************************/
int MP4_ReadBox( MP4_Stream_t *p_stream, MP4_Box_t *p_box, MP4_Box_t *p_father )
{
int i_result;
int i_index;
if( !MP4_ReadBoxCommon( p_stream, p_box ) )
{
msg_Err( p_stream->p_input, "Cannot read one box" );
return( 0 );
}
if( !p_box->i_size )
{
msg_Dbg( p_stream->p_input, "Found an empty box (null size)" );
return( 0 );
}
p_box->p_father = p_father;
/* Now search function to call */
for( i_index = 0; ; i_index++ )
{
if( ( MP4_Box_Function[i_index].i_type == p_box->i_type )||
( MP4_Box_Function[i_index].i_type == 0 ) )
{
break;
}
}
if( MP4_Box_Function[i_index].MP4_ReadBox_function == NULL )
{
msg_Warn( p_stream->p_input,
"Unknown box type %c%c%c%c (uncompletetly loaded)",
(p_box->i_type>>24)&0xff,
(p_box->i_type>>16)&0xff,
(p_box->i_type>>8)&0xff,
(p_box->i_type)&0xff );
return( 1 );
}
else
{
i_result =
(MP4_Box_Function[i_index].MP4_ReadBox_function)( p_stream, p_box );
}
if( !MP4_GotoBox( p_stream, p_box ) )
{
return( 0 );
}
return( i_result );
}
/*****************************************************************************
* MP4_FreeBox : free memory after read with MP4_ReadBox and all
* the children
*****************************************************************************/
void MP4_FreeBox( input_thread_t *p_input, MP4_Box_t *p_box )
{
#define FREE( p ) \
if( p ) \
{ \
free( p ); \
}
int i_index;
MP4_Box_t *p_child;
MP4_Box_t *p_next;
if( !p_box )
{
return; /* hehe */
}
p_child = p_box->p_first;
while( p_child )
{
p_next = p_child->p_next;
MP4_FreeBox( p_input, p_child );
/* MP4_FreeBoxChildren have free all data expect p_child itself */
free( p_child );
p_child = p_next;
}
#if 0
/* Now search function to call */
if( p_box->data.p_data )
{
for( i_index = 0; ; i_index++ )
{
if( ( MP4_Box_Function[i_index].i_type == p_box->i_type )||
( MP4_Box_Function[i_index].i_type == 0 ) )
{
break;
}
}
if( MP4_Box_Function[i_index].MP4_FreeBox_function == NULL )
{
msg_Warn( p_input,
"cannot free box %c%c%c%c, type unknown",
(p_box->i_type >> 24)&0xff,
(p_box->i_type >> 16)&0xff,
(p_box->i_type >> 8)&0xff,
(p_box->i_type )&0xff );
}
else
{
MP4_Box_Function[i_index].MP4_FreeBox_function( p_input, p_box );
}
}
p_box->data.p_data = NULL;
p_box->p_first = NULL;
p_box->p_last = NULL;
#endif
if( p_box->data.p_data )
{
switch( p_box->i_type )
{
case( FOURCC_ftyp):
FREE(p_box->data.p_ftyp->i_compatible_brands );
break;
case( FOURCC_hdlr ):
FREE( p_box->data.p_hdlr->psz_name );
break;
case( FOURCC_url ):
FREE( p_box->data.p_url->psz_location );
break;
case( FOURCC_urn ):
FREE( p_box->data.p_urn->psz_name );
FREE( p_box->data.p_urn->psz_location );
break;
case( FOURCC_stts ):
FREE( p_box->data.p_stts->i_sample_count );
FREE( p_box->data.p_stts->i_sample_delta );
break;
case( FOURCC_ctts ):
FREE( p_box->data.p_ctts->i_sample_count );
FREE( p_box->data.p_ctts->i_sample_offset );
break;
case( FOURCC_stsz ):
FREE( p_box->data.p_stsz->i_entry_size );
break;
case( FOURCC_stz2 ):
FREE( p_box->data.p_stz2->i_entry_size );
break;
case( FOURCC_stsc ):
FREE( p_box->data.p_stsc->i_first_chunk );
FREE( p_box->data.p_stsc->i_samples_per_chunk );
FREE( p_box->data.p_stsc->i_sample_description_index );
break;
case( FOURCC_stco ):
case( FOURCC_co64 ):
FREE( p_box->data.p_co64->i_chunk_offset );
break;
case( FOURCC_stss ):
FREE( p_box->data.p_stss->i_sample_number );
break;
case( FOURCC_stsh ):
FREE( p_box->data.p_stsh->i_shadowed_sample_number );
FREE( p_box->data.p_stsh->i_sync_sample_number );
break;
case( FOURCC_stdp ):
FREE( p_box->data.p_stdp->i_priority );
break;
case( FOURCC_padb ):
FREE( p_box->data.p_padb->i_reserved1 );
FREE( p_box->data.p_padb->i_pad2 );
FREE( p_box->data.p_padb->i_reserved2 );
FREE( p_box->data.p_padb->i_pad1 );
break;
case( FOURCC_elst ):
FREE( p_box->data.p_elst->i_segment_duration );
FREE( p_box->data.p_elst->i_media_time );
FREE( p_box->data.p_elst->i_media_rate_integer );
FREE( p_box->data.p_elst->i_media_rate_fraction );
break;
case( FOURCC_cprt ):
FREE( p_box->data.p_cprt->psz_notice );
break;
case( FOURCC_cmvd ):
FREE( p_box->data.p_cmvd->p_data );
break;
case( FOURCC_moov ): /* theses are containers for others boxes */
case( FOURCC_trak ):
case( FOURCC_mdia ):
case( FOURCC_moof ):
case( FOURCC_minf ):
case( FOURCC_stbl ):
case( FOURCC_dinf ):
case( FOURCC_edts ):
case( FOURCC_udta ):
case( FOURCC_nmhd ):
case( FOURCC_mvhd ): /* nothing to free */
case( FOURCC_tkhd ):
case( FOURCC_mdhd ):
case( FOURCC_vmhd ):
case( FOURCC_smhd ):
case( FOURCC_hmhd ):
case( FOURCC_dref ):
case( FOURCC_soun ):
case( FOURCC_vide ):
case( FOURCC_SVQ1 ):
case( FOURCC_mp4s ):
case( FOURCC_mp4a ):
case( FOURCC_mp4v ):
case( FOURCC_dcom ):
case( FOURCC_stsd ):
case( FOURCC_cmov ):
case( FOURCC_DIVX ):
case( FOURCC_3IV1 ):
case( FOURCC_h263 ):
break;
default:
msg_Warn( p_input, "cannot free box %c%c%c%c, type unknown",
(p_box->i_type >> 24)&0xff,
(p_box->i_type >> 16)&0xff,
(p_box->i_type >> 8)&0xff,
(p_box->i_type )&0xff );
break;
}
free( p_box->data.p_data );
}
p_box->data.p_data = NULL;
p_box->p_first = NULL;
p_box->p_last = NULL;
#undef FREE
}
/*****************************************************************************
* MP4_ReadRoot : Parse the entire file, and create all boxes in memory
*****************************************************************************
* The first box is a virtual box "root" and is the father for all first
* level boxes for the file, a sort of virtual contener
*****************************************************************************/
int MP4_ReadBoxRoot( input_thread_t *p_input, MP4_Box_t *p_root )
{
MP4_Stream_t *p_stream;
int i_result;
MP4_SeekAbsolute( p_input, 0 ); /* Go to the begining */
p_root->i_pos = 0;
p_root->i_type = MP4_FOURCC( 'r', 'o', 'o', 't' );
p_root->i_shortsize = 1;
p_root->i_size = p_input->stream.p_selected_area->i_size;
CreateUUID( &p_root->i_uuid, p_root->i_type );
p_root->data.p_data = NULL;
p_root->p_father = NULL;
p_root->p_first = NULL;
p_root->p_last = NULL;
p_root->p_next = NULL;
p_stream = MP4_InputStream( p_input );
i_result = MP4_ReadBoxContainerRaw( p_stream, p_root );
free( p_stream );
if( i_result )
{
MP4_Box_t *p_child;
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_FindBox( p_root, FOURCC_moov ) )&&
( p_cmov = MP4_FindBox( p_moov, FOURCC_cmov ) ) )
{
/* rename the compressed moov as a box to skip */
p_moov->i_type = FOURCC_skip;
/* 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;
/* insert this new moov box as first child of p_root */
p_moov->p_next = p_child = p_root->p_first;
p_root->p_first = p_moov;
}
}
return( i_result );
}
static void __MP4_DumpBoxStructure( input_thread_t *p_input,
MP4_Box_t *p_box, int i_level )
{
MP4_Box_t *p_child;
if( !i_level )
{
msg_Dbg( p_input, "Dumping root Box \"%c%c%c%c \"",
(p_box->i_type>>24 ) &0xff,
(p_box->i_type>>16 ) &0xff,
(p_box->i_type>> 8 ) &0xff,
(p_box->i_type ) &0xff );
}
else
{
char str[512];
int i;
memset( str, (u8)' ', 512 );
for( i = 0; i < i_level; i++ )
{
str[i*5] = '|';
}
sprintf( str + i_level * 5, "+ %c%c%c%c size %d",
(p_box->i_type>>24 ) &0xff,
(p_box->i_type>>16 ) &0xff,
(p_box->i_type>> 8 ) &0xff,
(p_box->i_type ) &0xff,
(u32)p_box->i_size );
msg_Dbg( p_input, "%s", str );
}
p_child = p_box->p_first;
while( p_child )
{
__MP4_DumpBoxStructure( p_input, p_child, i_level + 1 );
p_child = p_child->p_next;
}
}
void MP4_DumpBoxStructure( input_thread_t *p_input, MP4_Box_t *p_box )
{
__MP4_DumpBoxStructure( p_input, p_box, 0 );
}
/*****************************************************************************
* MP4_CountBox: given a box, count how many child have the requested type
* FIXME : support GUUID
*****************************************************************************/
int MP4_CountBox( MP4_Box_t *p_box, u32 i_type )
{
int i_count;
MP4_Box_t *p_child;
if( !p_box )
{
return( 0 );
}
i_count = 0;
p_child = p_box->p_first;
while( p_child )
{
if( p_child->i_type == i_type )
{
i_count++;
}
p_child = p_child->p_next;
}
return( i_count );
}
/*****************************************************************************
* MP4_FindBox: find first box with i_type child of p_box
* return NULL if not found
*****************************************************************************/
MP4_Box_t *MP4_FindBox( MP4_Box_t *p_box, u32 i_type )
{
MP4_Box_t *p_child;
if( !p_box )
{
return( NULL );
}
p_child = p_box->p_first;
while( p_child )
{
if( p_child->i_type == i_type )
{
return( p_child );
}
p_child = p_child->p_next;
}
return( NULL );
}
/*****************************************************************************
* MP4_FindNextBox: find next box with thesame type and at the same level
* than p_box
*****************************************************************************/
MP4_Box_t *MP4_FindNextBox( MP4_Box_t *p_box )
{
MP4_Box_t *p_next;
if( !p_box )
{
return( NULL );
}
p_next = p_box->p_next;
while( p_next )
{
if( p_next->i_type == p_box->i_type )
{
return( p_next );
}
p_next = p_next->p_next;
}
return( NULL );
}
/*****************************************************************************
* MP4_FindNbBox: find the box i_number
*****************************************************************************/
MP4_Box_t *MP4_FindNbBox( MP4_Box_t *p_box, u32 i_number )
{
MP4_Box_t *p_child = p_box->p_first;
if( !p_child )
{
return( NULL );
}
while( i_number )
{
if( !( p_child = p_child->p_next ) )
{
return( NULL );
}
i_number--;
}
return( p_child );
}
/*****************************************************************************
* libmp4.h : LibMP4 library for mp4 module for vlc
*****************************************************************************
* Copyright (C) 2001 VideoLAN
* $Id: libmp4.h,v 1.1 2002/07/17 21:37:27 fenrir Exp $
* Authors: Laurent Aimar <fenrir@via.ecp.fr>
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 2 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* 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.
*****************************************************************************/
/* XXX It's not the same than VLC_FOURCC */
#define MP4_FOURCC( a, b, c, d ) \
( ((u32)d) | ( ((u32)c) << 8 ) | ( ((u32)b) << 16 ) | ( ((u32)a) << 24 ) )
#define FOURCC_uuid MP4_FOURCC( 'u', 'u', 'i', 'd' )
#define FOURCC_ftyp MP4_FOURCC( 'f', 't', 'y', 'p' )
#define FOURCC_moov MP4_FOURCC( 'm', 'o', 'o', 'v' )
#define FOURCC_cmov MP4_FOURCC( 'c', 'm', 'o', 'v' )
#define FOURCC_dcom MP4_FOURCC( 'd', 'c', 'o', 'm' )
#define FOURCC_cmvd MP4_FOURCC( 'c', 'm', 'v', 'd' )
#define FOURCC_moof MP4_FOURCC( 'm', 'o', 'o', 'f' )
#define FOURCC_mdat MP4_FOURCC( 'm', 'd', 'a', 't' )
#define FOURCC_skip MP4_FOURCC( 's', 'k', 'i', 'p' )
#define FOURCC_free MP4_FOURCC( 'f', 'r', 'e', 'e' )
#define FOURCC_udta MP4_FOURCC( 'u', 'd', 't', 'a' )
#define FOURCC_wide MP4_FOURCC( 'w', 'i', 'd', 'e' )
#define FOURCC_trak MP4_FOURCC( 't', 'r', 'a', 'k' )
#define FOURCC_mvhd MP4_FOURCC( 'm', 'v', 'h', 'd' )
#define FOURCC_tkhd MP4_FOURCC( 't', 'k', 'h', 'd' )
#define FOURCC_tref MP4_FOURCC( 't', 'r', 'e', 'f' )
#define FOURCC_mdia MP4_FOURCC( 'm', 'd', 'i', 'a' )
#define FOURCC_mdhd MP4_FOURCC( 'm', 'd', 'h', 'd' )
#define FOURCC_hdlr MP4_FOURCC( 'h', 'd', 'l', 'r' )
#define FOURCC_minf MP4_FOURCC( 'm', 'i', 'n', 'f' )
#define FOURCC_vmhd MP4_FOURCC( 'v', 'm', 'h', 'd' )
#define FOURCC_smhd MP4_FOURCC( 's', 'm', 'h', 'd' )
#define FOURCC_hmhd MP4_FOURCC( 'h', 'm', 'h', 'd' )
#define FOURCC_dinf MP4_FOURCC( 'd', 'i', 'n', 'f' )
#define FOURCC_url MP4_FOURCC( 'u', 'r', 'l', ' ' )
#define FOURCC_urn MP4_FOURCC( 'u', 'r', 'n', ' ' )
#define FOURCC_dref MP4_FOURCC( 'd', 'r', 'e', 'f' )
#define FOURCC_stbl MP4_FOURCC( 's', 't', 'b', 'l' )
#define FOURCC_stts MP4_FOURCC( 's', 't', 't', 's' )
#define FOURCC_ctts MP4_FOURCC( 'c', 't', 't', 's' )
#define FOURCC_stsd MP4_FOURCC( 's', 't', 's', 'd' )
#define FOURCC_stsz MP4_FOURCC( 's', 't', 's', 'z' )
#define FOURCC_stz2 MP4_FOURCC( 's', 't', 'z', '2' )
#define FOURCC_stsc MP4_FOURCC( 's', 't', 's', 'c' )
#define FOURCC_stco MP4_FOURCC( 's', 't', 'c', 'o' )
#define FOURCC_co64 MP4_FOURCC( 'c', 'o', '6', '4' )
#define FOURCC_stss MP4_FOURCC( 's', 't', 's', 's' )
#define FOURCC_stsh MP4_FOURCC( 's', 't', 's', 'h' )
#define FOURCC_stdp MP4_FOURCC( 's', 't', 'd', 'p' )
#define FOURCC_padb MP4_FOURCC( 'p', 'a', 'd', 'b' )
#define FOURCC_edts MP4_FOURCC( 'e', 'd', 't', 's' )
#define FOURCC_elst MP4_FOURCC( 'e', 'l', 's', 't' )
#define FOURCC_udta MP4_FOURCC( 'u', 'd', 't', 'a' )
#define FOURCC_mvex MP4_FOURCC( 'm', 'v', 'e', 'x' )
#define FOURCC_trex MP4_FOURCC( 't', 'r', 'e', 'x' )
#define FOURCC_mfhd MP4_FOURCC( 'm', 'f', 'h', 'd' )
#define FOURCC_traf MP4_FOURCC( 't', 'r', 'a', 'f' )
#define FOURCC_tfhd MP4_FOURCC( 't', 'f', 'h', 'd' )
#define FOURCC_trun MP4_FOURCC( 't', 'r', 'u', 'n' )
#define FOURCC_cprt MP4_FOURCC( 'c', 'p', 'r', 't' )
#define FOURCC_iods MP4_FOURCC( 'i', 'o', 'd', 's' )
#define FOURCC_nmhd MP4_FOURCC( 'n', 'm', 'h', 'd' )
#define FOURCC_mp4v MP4_FOURCC( 'm', 'p', '4', 'v' )
#define FOURCC_mp4a MP4_FOURCC( 'm', 'p', '4', 'a' )
#define FOURCC_mp4s MP4_FOURCC( 'm', 'p', '4', 's' )
#define FOURCC_vide MP4_FOURCC( 'v', 'i', 'd', 'e' )
#define FOURCC_soun MP4_FOURCC( 's', 'o', 'u', 'n' )
#define FOURCC_hint MP4_FOURCC( 'h', 'i', 'n', 't' )
#define FOURCC_dpnd MP4_FOURCC( 'd', 'p', 'n', 'd' )
#define FOURCC_ipir MP4_FOURCC( 'i', 'p', 'i', 'r' )
#define FOURCC_mpod MP4_FOURCC( 'm', 'p', 'o', 'd' )
#define FOURCC_hnti MP4_FOURCC( 'h', 'n', 't', 'i' )
#define FOURCC_rtp MP4_FOURCC( 'r', 't', 'p', ' ' )
#define FOURCC_isom MP4_FOURCC( 'i', 's', 'o', 'm' )
#define FOURCC_esds MP4_FOURCC( 'e', 's', 'd', 's' )
#define FOURCC_zlib MP4_FOURCC( 'z', 'l', 'i', 'b' )
#define FOURCC_SVQ1 MP4_FOURCC( 'S', 'V', 'Q', '1' )
#define FOURCC__mp3 MP4_FOURCC( '.', 'm', 'p', '3' )
#define FOURCC_3IV1 MP4_FOURCC( '3', 'I', 'V', '1' )
#define FOURCC_h263 MP4_FOURCC( 'h', '2', '6', '3' )
#define FOURCC_DIVX MP4_FOURCC( 'D', 'I', 'V', 'X' )
/*
#define FOURCC_ MP4_FOURCC( '', '', '', '' )
*/
/* Do you want some debug information on all read boxes ? */
#define MP4_VERBOSE 1
/* memory stream and file stream object */
typedef struct MP4_Stream_s
{
int b_memory; /* do we uses a memory buffer */
input_thread_t *p_input;
off_t i_start; /* in the buffer position for memory stream */
off_t i_stop;
u8 *p_buffer;
} MP4_Stream_t;
struct MP4_Box_s;
/* uuid Universal Unique IDentifiers */
typedef struct UUID_s
{
u8 b[16];
} UUID_t;
/* specific structure for all boxes */
typedef struct MP4_Box_data_ftyp_s
{
u32 i_major_brand;
u32 i_minor_version;
u32 i_compatible_brands_count;
u32 *i_compatible_brands;
} MP4_Box_data_ftyp_t;
typedef struct MP4_Box_data_mvhd_s
{
u8 i_version;
u32 i_flags;
u64 i_creation_time;
u64 i_modification_time;
u32 i_timescale;
u64 i_duration;
s32 i_rate;
s16 i_volume;
s16 i_reserved1;
u32 i_reserved2[2];
s32 i_matrix[9];
u32 i_predefined[6];
u32 i_next_track_id;
} MP4_Box_data_mvhd_t;
#define MP4_TRACK_ENABLED 0x000001
#define MP4_TRACK_IN_MOVIE 0x000002
#define MP4_TRACK_IN_PREVIEW 0x000004
typedef struct MP4_Box_data_tkhd_s
{
u8 i_version;
u32 i_flags;
u64 i_creation_time;
u64 i_modification_time;
u32 i_track_ID;
u32 i_reserved;
u64 i_duration;
u32 i_reserved2[2];
s16 i_layer;
s16 i_predefined;
s16 i_volume;
u16 i_reserved3;
s32 i_matrix[9];
s32 i_width;
s32 i_height;
} MP4_Box_data_tkhd_t;
typedef struct MP4_Box_data_mdhd_s
{
u8 i_version;
u32 i_flags;
u64 i_creation_time;
u64 i_modification_time;
u32 i_timescale;
u64 i_duration;
/* one bit for pad */
/* unsigned int(5)[3] language difference with 0x60*/
unsigned char i_language[3];
u16 i_predefined;
} MP4_Box_data_mdhd_t;
typedef struct MP4_Box_data_hdlr_s
{
u8 i_version;
u32 i_flags;
u32 i_predefined;
u32 i_handler_type; /* "vide" "soun" "hint" "odsm"
"crsm" "sdsm" "m7sm" "ocsm"
"ipsm" "mjsm" */
unsigned char *psz_name; /* in UTF-8 */
} MP4_Box_data_hdlr_t;
typedef struct MP4_Box_data_vmhd_s
{
u8 i_version;
u32 i_flags;
s16 i_graphics_mode;
s16 i_opcolor[3];
} MP4_Box_data_vmhd_t;
typedef struct MP4_Box_data_smhd_s
{
u8 i_version;
u32 i_flags;
s16 i_balance;
s16 i_reserved;
} MP4_Box_data_smhd_t;
typedef struct MP4_Box_data_hmhd_s
{
u8 i_version;
u32 i_flags;
u16 i_max_PDU_size;
u16 i_avg_PDU_size;
u32 i_max_bitrate;
u32 i_avg_bitrate;
u32 i_reserved;
} MP4_Box_data_hmhd_t;
typedef struct MP4_Box_data_url_s
{
u8 i_version;
u32 i_flags;
unsigned char *psz_location;
} MP4_Box_data_url_t;
typedef struct MP4_Box_data_urn_s
{
u8 i_version;
u32 i_flags;
unsigned char *psz_name;
unsigned char *psz_location;
} MP4_Box_data_urn_t;
typedef struct MP4_Box_data_dref_s
{
u8 i_version;
u32 i_flags;
u32 i_entry_count;
/* XXX it's also a container with i_entry_count entry */
} MP4_Box_data_dref_t;
typedef struct MP4_Box_data_stts_s
{
u8 i_version;
u32 i_flags;
u32 i_entry_count;
u32 *i_sample_count; /* these are array */
s32 *i_sample_delta;
} MP4_Box_data_stts_t;
typedef struct MP4_Box_data_ctts_s
{
u8 i_version;
u32 i_flags;
u32 i_entry_count;
u32 *i_sample_count; /* these are array */
s32 *i_sample_offset;
} MP4_Box_data_ctts_t;
typedef struct MP4_Box_data_sample_soun_s
{
u8 i_reserved1[6];
u16 i_data_reference_index;
u32 i_reserved2[2];
u16 i_channelcount;
u16 i_samplesize;
u16 i_predefined;
u16 i_reserved3;
u16 i_sampleratehi; /* timescale of track */
u16 i_sampleratelo;
} MP4_Box_data_sample_soun_t;
typedef struct MP4_Box_data_sample_mp4a_s
{
u8 i_reserved1[6];
u16 i_data_reference_index;
u32 i_reserved2[2];
u16 i_channelcount;
u16 i_samplesize;
u16 i_predefined;
u16 i_reserved3;
u16 i_sampleratehi; /* timescale of track */
u16 i_sampleratelo;
} MP4_Box_data_sample_mp4a_t;
typedef struct MP4_Box_data_sample_vide_s
{
u8 i_reserved1[6];
u16 i_data_reference_index;
u16 i_predefined1;
u16 i_reserved2;
u32 i_predefined2[3];
s16 i_width;
s16 i_height;
u32 i_horizresolution;
u32 i_vertresolution;
u32 i_reserved3;
u16 i_predefined3;
u8 i_compressorname[32];
s16 i_depth;
s16 i_predefined4;
} MP4_Box_data_sample_vide_t;
typedef struct MP4_Box_data_sample_mp4v_s
{
u8 i_reserved1[6];
u16 i_data_reference_index;
u16 i_predefined1;
u16 i_reserved2;
u32 i_predefined2[3];
s16 i_width;
s16 i_height;
u32 i_horizresolution;
u32 i_vertresolution;
u32 i_reserved3;
u16 i_predefined3;
u8 i_compressorname[32];
s16 i_depth;
s16 i_predefined4;
} MP4_Box_data_sample_mp4v_t;
typedef struct MP4_Box_data_sample_hint_s
{
u8 i_reserved1[6];
u16 i_data_reference_index;
u8 *p_data;
} MP4_Box_data_sample_hint_t;
typedef struct MP4_Box_data_moviehintinformation_rtp_s
{
u32 i_description_format;
unsigned char *psz_text;
} MP4_Box_data_moviehintinformation_rtp_t;
typedef struct MP4_Box_data_stsd_s
{
u8 i_version;
u32 i_flags;
u32 i_entry_count;
/* it contains SampleEntry handled as if it was Box */
} MP4_Box_data_stsd_t;
typedef struct MP4_Box_data_stsz_s
{
u8 i_version;
u32 i_flags;
u32 i_sample_size;
u32 i_sample_count;
u32 *i_entry_size; /* array , empty if i_sample_size != 0 */
} MP4_Box_data_stsz_t;
typedef struct MP4_Box_data_stz2_s
{
u8 i_version;
u32 i_flags;
u32 i_sample_size; /* 24 bits */
u8 i_field_size;
u32 i_sample_count;
u32 *i_entry_size; /* array: unsigned int(i_field_size) entry_size */
} MP4_Box_data_stz2_t;
typedef struct MP4_Box_data_stsc_s
{
u8 i_version;
u32 i_flags;
u32 i_entry_count;
u32 *i_first_chunk; /* theses are arrays */
u32 *i_samples_per_chunk;
u32 *i_sample_description_index;
} MP4_Box_data_stsc_t;
typedef struct MP4_Box_data_co64_s
{
u8 i_version;
u32 i_flags;
u32 i_entry_count;
u64 *i_chunk_offset;
} MP4_Box_data_co64_t;
typedef struct MP4_Box_data_stss_s
{
u8 i_version;
u32 i_flags;
u32 i_entry_count;
u32 *i_sample_number;
} MP4_Box_data_stss_t;
typedef struct MP4_Box_data_stsh_s
{
u8 i_version;
u32 i_flags;
u32 i_entry_count;
u32 *i_shadowed_sample_number;
u32 *i_sync_sample_number;
} MP4_Box_data_stsh_t;
typedef struct MP4_Box_data_stdp_s
{
u8 i_version;
u32 i_flags;
u16 *i_priority;
} MP4_Box_data_stdp_t;
typedef struct MP4_Box_data_padb_s
{
u8 i_version;
u32 i_flags;
u32 i_sample_count;
u16 *i_reserved1; /* 1bit */
u16 *i_pad2; /* 3bits */
u16 *i_reserved2; /* 1bit */
u16 *i_pad1; /* 3bits */
} MP4_Box_data_padb_t;
typedef struct MP4_Box_data_elst_s
{
u8 i_version;
u32 i_flags;
u32 i_entry_count;
u64 *i_segment_duration;
s64 *i_media_time;
u16 *i_media_rate_integer;
u16 *i_media_rate_fraction;
} MP4_Box_data_elst_t;
typedef struct MP4_Box_data_cprt_s
{
u8 i_version;
u32 i_flags;
/* 1 pad bit */
unsigned char i_language[3];
unsigned char *psz_notice;
} MP4_Box_data_cprt_t;
/* DecoderConfigDescriptor */
typedef struct MP4_descriptor_decoder_config_s
{
u8 i_objectTypeIndication;
u8 i_streamType;
int b_upStream;
int i_buffer_sizeDB;
int i_max_bitrate;
int i_avg_bitrate;
int i_decoder_specific_info_len;
u8 *p_decoder_specific_info;
/* some other stuff */
} MP4_descriptor_decoder_config_t;
typedef struct MP4_descriptor_SL_config_s
{
} MP4_descriptor_SL_config_t;
typedef struct MP4_descriptor_ES_s
{
u16 i_ES_ID;
int b_stream_dependence;
int b_url;
int b_OCRstream;
int i_stream_priority;
int i_depend_on_ES_ID; /* if b_stream_dependence set */
unsigned char *psz_URL;
u16 i_OCR_ES_ID; /* if b_OCRstream */
MP4_descriptor_decoder_config_t *p_decConfigDescr;
MP4_descriptor_SL_config_t *p_slConfigDescr;
/* some other stuff ... */
} MP4_descriptor_ES_t;
/* ES descriptor */
typedef struct MP4_Box_data_esds_s
{
u8 i_version;
u32 i_flags;
MP4_descriptor_ES_t es_descriptor;
} MP4_Box_data_esds_t;
typedef struct MP4_Box_data_dcom_s
{
u32 i_algorithm; /* fourcc */
} MP4_Box_data_dcom_t;
typedef struct MP4_Box_data_cmvd_s
{
u32 i_uncompressed_size;
u32 i_compressed_size;
int b_compressed; /* Set to 1 if compressed data, 0 if uncompressed */
u8 *p_data;
} MP4_Box_data_cmvd_t;
typedef struct MP4_Box_data_cmov_s
{
struct MP4_Box_s *p_moov; /* uncompressed moov */
} MP4_Box_data_cmov_t;
/*
typedef struct MP4_Box_data_cmov_s
{
u8 i_version;
u32 i_flags;
} MP4_Box_data__t;
*/
typedef union MP4_Box_data_s
{
MP4_Box_data_ftyp_t *p_ftyp;
MP4_Box_data_mvhd_t *p_mvhd;
MP4_Box_data_tkhd_t *p_tkhd;
MP4_Box_data_mdhd_t *p_mdhd;
MP4_Box_data_hdlr_t *p_hdlr;
MP4_Box_data_vmhd_t *p_vmhd;
MP4_Box_data_smhd_t *p_smhd;
MP4_Box_data_hmhd_t *p_hmhd;
MP4_Box_data_url_t *p_url;
MP4_Box_data_urn_t *p_urn;
MP4_Box_data_dref_t *p_dref;
MP4_Box_data_stts_t *p_stts;
MP4_Box_data_ctts_t *p_ctts;
MP4_Box_data_stsd_t *p_stsd;
MP4_Box_data_sample_vide_t *p_sample_vide;
MP4_Box_data_sample_vide_t *p_sample_mp4v;
MP4_Box_data_sample_soun_t *p_sample_soun;
MP4_Box_data_sample_soun_t *p_sample_mp4a;
MP4_Box_data_sample_hint_t *p_sample_hint;
MP4_Box_data_esds_t *p_esds;
MP4_Box_data_stsz_t *p_stsz;
MP4_Box_data_stz2_t *p_stz2;
MP4_Box_data_stsc_t *p_stsc;
MP4_Box_data_co64_t *p_co64;
MP4_Box_data_stss_t *p_stss;
MP4_Box_data_stsh_t *p_stsh;
MP4_Box_data_stdp_t *p_stdp;
MP4_Box_data_padb_t *p_padb;
MP4_Box_data_elst_t *p_elst;
MP4_Box_data_cprt_t *p_cprt;
MP4_Box_data_dcom_t *p_dcom;
MP4_Box_data_cmvd_t *p_cmvd;
MP4_Box_data_cmov_t *p_cmov;
MP4_Box_data_moviehintinformation_rtp_t p_moviehintinformation_rtp;
void *p_data; /* for unknow type */
} MP4_Box_data_t;
/* the most basic structure */
typedef struct MP4_Box_s
{
off_t i_pos; /* absolute position */
u32 i_type;
u32 i_shortsize;
UUID_t i_uuid; /* Set if i_type == "uuid" */
u64 i_size; /* always set so use it */
MP4_Box_data_t data; /* union of pointers on extended data depending on i_type (or i_usertype) */
struct MP4_Box_s *p_father; /* pointer on the father Box */
struct MP4_Box_s *p_first; /* pointer on the first child Box */
struct MP4_Box_s *p_last;
struct MP4_Box_s *p_next; /* pointer on the next boxes at the same level */
} MP4_Box_t;
/*---------------------------------------------------------------------------*/
/****----------------------- High level functions ------------------------****/
/*---------------------------------------------------------------------------*/
/*****************************************************************************
* Function for manipulate stream easily
*****************************************************************************/
off_t MP4_TellAbsolute( input_thread_t *p_input );
int MP4_SeekAbsolute( input_thread_t *p_input, off_t i_pos);
int MP4_ReadData( input_thread_t *p_input, u8 *p_buff, int i_size );
/*****************************************************************************
* MP4_ReadRoot : Parse the entire file, and create all boxes in memory
*****************************************************************************
* The first box is a virtual box "root" and is the father for all first
* level boxes
*
* RETURN : 1 if succes and 0 if it failed
*****************************************************************************/
int MP4_ReadBoxRoot( input_thread_t *p_input, MP4_Box_t *p_root );
/*****************************************************************************
* MP4_FreeBox : free memory allocated after read with MP4_ReadBox
* this, means also children boxes
* XXX : all children have to be allocated by a malloc !! and
* p_box isn't freeing
*****************************************************************************/
void MP4_FreeBox( input_thread_t *p_input, MP4_Box_t *p_box );
/*****************************************************************************
* MP4_DumpBoxStructure: print the structure of the p_box
*****************************************************************************/
void MP4_DumpBoxStructure( input_thread_t *p_input, MP4_Box_t *p_box );
/*****************************************************************************
* MP4_CountBox: given a box, count how many child have the requested type
* FIXME : support GUUID
*****************************************************************************/
int MP4_CountBox( MP4_Box_t *p_box, u32 i_type );
/*****************************************************************************
* MP4_FindBox: find first box with i_type child of p_box
* return NULL if not found
*****************************************************************************/
MP4_Box_t *MP4_FindBox( MP4_Box_t *p_box, u32 i_type );
/*****************************************************************************
* MP4_FindNextBox: find next box with thesame type and at the same level
* than p_box
*****************************************************************************/
MP4_Box_t *MP4_FindNextBox( MP4_Box_t *p_box );
/*****************************************************************************
* MP4_FindNbBox: find the box i_number
*****************************************************************************/
MP4_Box_t *MP4_FindNbBox( MP4_Box_t *p_box, u32 i_number );
/*---------------------------------------------------------------------------*/
/****----------------------- Lower level functions -----------------------****/
/**** ****/
/****------Use them only when you known what they really do and need------****/
/**** ****/
/****---------------------------------------------------------------------****/
/*---------------------------------------------------------------------------*/
/**** ------- First some function to make abstract from input -------- */
/****************************************************************************
* MP4_InputStream create an stram with an input
*
****************************************************************************/
MP4_Stream_t *MP4_InputStream( input_thread_t *p_input );
/****************************************************************************
* MP4_MemoryStream create a memory stream
* if p_buffer == NULL, will allocate a buffer of i_size, else
* it uses p_buffer XXX you have to unallocate yourself !
*
****************************************************************************/
MP4_Stream_t *MP4_MemoryStream( input_thread_t *p_input,
int i_size, u8 *p_buffer );
/****************************************************************************
* MP4_ReadStream read from a MP4_Stream_t
*
****************************************************************************/
int MP4_ReadStream( MP4_Stream_t *p_stream, u8 *p_buff, int i_size );
/****************************************************************************
* MP4_PeekStream guess it ;)
*
****************************************************************************/
int MP4_PeekStream( MP4_Stream_t *p_stream, u8 **pp_peek, int i_size );
/****************************************************************************
* MP4_TellStream give absolute position in the stream
* XXX for a memory stream give position from begining of the buffer
****************************************************************************/
off_t MP4_TellStream( MP4_Stream_t *p_stream );
/****************************************************************************
* MP4_SeekStream seek in a MP4_Stream_t
*
****************************************************************************/
int MP4_SeekStream( MP4_Stream_t *p_stream, off_t i_pos);
/*****************************************************************************
* MP4_ReadBox : parse the actual box and the children if they exist
*
* RETURN : 1 if succes and 0 if it failed
*****************************************************************************/
int MP4_ReadBox( MP4_Stream_t *p_stream, MP4_Box_t *p_box, MP4_Box_t *p_father );
/*****************************************************************************
* MP4_ReadBoxCommon : Load only common parameters for all boxes
*****************************************************************************
* p_box need to be an already allocated MP4_Box_t, and all data
* will only be peek not read
*
* RETURN : 0 if it fail, 1 otherwise
*****************************************************************************/
int MP4_ReadBoxCommon( MP4_Stream_t *p_stream, MP4_Box_t *p_box );
/*****************************************************************************
* MP4_MP4_GotoBox : Go to this particular box
*****************************************************************************
* RETURN : 0 if it fail, 1 otherwise
*****************************************************************************/
int MP4_GotoBox( MP4_Stream_t *p_stream, MP4_Box_t *p_box );
/*****************************************************************************
* MP4_MP4_NextBox : Go to the next box wiithin p_box->p_father
*****************************************************************************
* if p_box == NULL, go to the next box in witch we are( at the begining ).
*****************************************************************************/
int MP4_NextBox( MP4_Stream_t *p_stream, MP4_Box_t *p_box );
/*****************************************************************************
* mp4.c : MP4 file input module for vlc
*****************************************************************************
* Copyright (C) 2001 VideoLAN
* $Id: mp4.c,v 1.1 2002/07/17 21:37:27 fenrir Exp $
* Authors: Laurent Aimar <fenrir@via.ecp.fr>
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 2 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* 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.
*****************************************************************************/
/*****************************************************************************
* Preamble
*****************************************************************************/
#include <stdlib.h> /* malloc(), free() */
#include <string.h> /* strdup() */
#include <errno.h>
#include <sys/types.h>
#include <vlc/vlc.h>
#include <vlc/input.h>
#include "libmp4.h"
#include "mp4.h"
/*****************************************************************************
* Local prototypes
*****************************************************************************/
static void input_getfunctions( function_list_t * );
static int MP4Demux ( input_thread_t * );
static int MP4Init ( input_thread_t * );
static void MP4End ( input_thread_t * );
/*****************************************************************************
* Build configuration tree.
*****************************************************************************/
MODULE_CONFIG_START
MODULE_CONFIG_STOP
MODULE_INIT_START
SET_DESCRIPTION( "MP4 file input" )
ADD_CAPABILITY( DEMUX, 242 )
MODULE_INIT_STOP
MODULE_ACTIVATE_START
input_getfunctions( &p_module->p_functions->demux );
MODULE_ACTIVATE_STOP
MODULE_DEACTIVATE_START
MODULE_DEACTIVATE_STOP
/*****************************************************************************
* Functions exported as capabilities. They are declared as static so that
* we don't pollute the namespace too much.
*****************************************************************************/
static void input_getfunctions( function_list_t * p_function_list )
{
#define input p_function_list->functions.demux
input.pf_init = MP4Init;
input.pf_end = MP4End;
input.pf_demux = MP4Demux;
input.pf_rewind = NULL;
#undef input
}
/*****************************************************************************
* Declaration of local function
*****************************************************************************/
static void MP4_ParseTrack();
static int MP4_CreateChunksIndex();
static int MP4_CreateSamplesIndex();
static void MP4_StartDecoder();
static void MP4_StopDecoder();
static int MP4_ReadSample();
static int MP4_DecodeSample();
#define MP4_Set4BytesLE( p, dw ) \
*((u8*)p) = ( dw&0xff ); \
*((u8*)p+1) = ( ((dw)>> 8)&0xff ); \
*((u8*)p+2) = ( ((dw)>>16)&0xff ); \
*((u8*)p+3) = ( ((dw)>>24)&0xff )
#define MP4_Set2BytesLE( p, dw ) \
*((u8*)p) = ( (dw)&0xff ); \
*((u8*)p+1) = ( ((dw)>> 8)&0xff )
/*****************************************************************************
* MP4Init: check file and initializes MP4 structures
*****************************************************************************/
static int MP4Init( input_thread_t *p_input )
{
u8 *p_peek;
u32 i_type;
demux_data_mp4_t *p_demux;
MP4_Box_t *p_moov;
MP4_Box_t *p_ftyp;
MP4_Box_t *p_mvhd;
MP4_Box_t *p_trak;
int i;
/* I need to seek */
if( !p_input->stream.b_seekable )
{
msg_Warn( p_input, "MP4 plugin discarded (unseekable)" );
return( -1 );
}
/* Initialize access plug-in structures. */
if( p_input->i_mtu == 0 )
{
/* Improve speed. */
p_input->i_bufsize = INPUT_DEFAULT_BUFSIZE ;
}
/* a little test to see if it could be a mp4 */
if( input_Peek( p_input, &p_peek, 8 ) < 8 )
{
msg_Warn( p_input, "MP4 plugin discarded (cannot peek)" );
return( -1 );
}
i_type = ( p_peek[4] << 24 ) + ( p_peek[5] << 16 ) +
( p_peek[6] << 8 ) + ( p_peek[7] );
switch( i_type )
{
case( FOURCC_ftyp ):
case( FOURCC_moov ):
case( FOURCC_moof ):
case( FOURCC_mdat ):
case( FOURCC_udta ): /* should never match but ... */
case( FOURCC_free ):
case( FOURCC_skip ):
case( FOURCC_wide ): /* not mp4 compliant but ... */
break;
default:
msg_Warn( p_input, "MP4 plugin discarded (not a valid file)" );
return( -1 );
}
/* create our structure that will contains all data */
if( !( p_input->p_demux_data =
p_demux = malloc( sizeof( demux_data_mp4_t ) ) ) )
{
msg_Err( p_input, "out of memory" );
return( -1 );
}
memset( p_demux, 0, sizeof( demux_data_mp4_t ) );
p_input->p_demux_data = p_demux;
/* Now load all boxes ( except raw data ) */
if( !MP4_ReadBoxRoot( p_input, &p_demux->box_root ) )
{
msg_Warn( p_input, "MP4 plugin discarded (not a valid file)" );
return( -1 );
}
MP4_DumpBoxStructure( p_input, &p_demux->box_root );
if( ( p_ftyp = MP4_FindBox( &p_demux->box_root, FOURCC_ftyp ) ) )
{
switch( p_ftyp->data.p_ftyp->i_major_brand )
{
case( FOURCC_isom ):
msg_Info( p_input,
"ISO Media file (isom) version %d.",
p_ftyp->data.p_ftyp->i_minor_version );
break;
default:
msg_Info( p_input,
"Unrecognize major file specification." );
break;
}
}
else
{
msg_Info( p_input, "File Type box missing(assume ISO Media file)" );
}
/* the file need to have one moov box */
if( !( p_moov = MP4_FindBox( &p_demux->box_root, FOURCC_moov ) ) )
{
msg_Warn( p_input, "MP4 plugin discarded (missing moov box)" );
MP4End( p_input );
return( -1 );
}
if( MP4_CountBox( &p_demux->box_root, FOURCC_moov ) != 1 )
{
msg_Warn( p_input, "more than one \"moov\" box (continuying anyway)" );
}
if( !(p_mvhd = MP4_FindBox( p_moov, FOURCC_mvhd ) ) )
{
msg_Err( p_input, "cannot find \"mvhd\" box" );
MP4End( p_input );
return( -1 );
}
else
{
p_demux->i_timescale = p_mvhd->data.p_mvhd->i_timescale;
}
p_demux->i_tracks = MP4_CountBox( p_moov, FOURCC_trak );
msg_Dbg( p_input, "find %d track%c",
p_demux->i_tracks,
p_demux->i_tracks ? 's':' ' );
if( !( p_trak = MP4_FindBox( p_moov, FOURCC_trak ) ) )
{
msg_Err( p_input, "cannot find /moov/trak !" );
MP4End( p_input );
return( -1 );
}
/* allocate memory */
p_demux->track = calloc( p_demux->i_tracks, sizeof( track_data_mp4_t ) );
/* now process each track and extract all usefull informations */
for( i = 0; i < p_demux->i_tracks; i++ )
{
MP4_ParseTrack( p_input, &p_demux->track[i], p_trak );
if( p_demux->track[i].b_ok )
{
char *psz_cat;
switch( p_demux->track[i].i_cat )
{
case( VIDEO_ES ):
psz_cat = "video";
break;
case( AUDIO_ES ):
psz_cat = "audio";
break;
default:
psz_cat = "";
break;
}
msg_Dbg( p_input, "adding track(%d) %s (%s) language %c%c%c",
i,
psz_cat,
p_demux->track[i].b_enable ? "enable":"disable",
p_demux->track[i].i_language[0],
p_demux->track[i].i_language[1],
p_demux->track[i].i_language[2] );
}
else
{
msg_Dbg( p_input, "ignoring track(%d)", i );
}
p_trak = MP4_FindNextBox( p_trak );
}
/* create one program */
vlc_mutex_lock( &p_input->stream.stream_lock );
if( input_InitStream( p_input, 0 ) == -1)
{
vlc_mutex_unlock( &p_input->stream.stream_lock );
msg_Err( p_input, "cannot init stream" );
MP4End( p_input );
return( -1 );
}
if( input_AddProgram( p_input, 0, 0) == NULL )
{
vlc_mutex_unlock( &p_input->stream.stream_lock );
msg_Err( p_input, "cannot add program" );
MP4End( p_input );
return( -1 );
}
p_input->stream.p_selected_program = p_input->stream.pp_programs[0];
p_input->stream.i_mux_rate = 0 ; /* FIXME */
vlc_mutex_unlock( &p_input->stream.stream_lock );
for( i = 0; i < p_demux->i_tracks; i++ )
{
/* start decoder for this track if enable by default*/
if( p_demux->track[i].b_enable )
{
MP4_StartDecoder( p_input, &p_demux->track[i] );
}
}
vlc_mutex_lock( &p_input->stream.stream_lock );
p_input->stream.p_selected_program->b_is_ok = 1;
vlc_mutex_unlock( &p_input->stream.stream_lock );
return( 0 );
}
/*****************************************************************************
* MP4Demux: read packet and send them to decoders
*****************************************************************************/
static int MP4Demux( input_thread_t *p_input )
{
demux_data_mp4_t *p_demux = p_input->p_demux_data;
int i_track;
/* first wait for the good time to read a packet */
input_ClockManageRef( p_input,
p_input->stream.p_selected_program,
p_demux->i_pcr );
/* update pcr XXX in mpeg scale so in 90000 unit/s */
p_demux->i_pcr = MP4_GetMoviePTS( p_demux ) * 9 / 100;
/* we will read 100ms for each stream so ...*/
p_demux->i_time += __MAX( p_demux->i_timescale / 10 , 1 );
for( i_track = 0; i_track < p_demux->i_tracks; i_track++ )
{
if( ( !p_demux->track[i_track].b_ok )||
( !p_demux->track[i_track].p_es )||
( !p_demux->track[i_track].p_es->p_decoder_fifo )||
( MP4_GetTrackPTS( &p_demux->track[i_track] ) >=
MP4_GetMoviePTS( p_demux ) ) )
{
continue; /* no need to read something */
}
while( MP4_GetTrackPTS( &p_demux->track[i_track] ) <
MP4_GetMoviePTS( p_demux ) )
{
pes_packet_t *p_pes;
/* read a sample */
if( !MP4_ReadSample( p_input ,
&p_demux->track[i_track],
&p_pes ) )
{
break;
}
/* send it to decoder and update time of this track
it also launch a new decoder if needed */
MP4_DecodeSample( p_input ,
&p_demux->track[i_track],
p_pes );
}
}
/* now check if all tracks are finished or unhandled*/
for( i_track = 0; i_track < p_demux->i_tracks; i_track++ )
{
if( ( p_demux->track[i_track].b_ok )&&
( p_demux->track[i_track].i_sample < p_demux->track[i_track].i_sample_count )&&
( p_demux->track[i_track].p_es )&&
( p_demux->track[i_track].p_es->p_decoder_fifo ) )
{
return( 1 );
}
}
return( 0 ); /* EOF */
}
/*****************************************************************************
* MP4End: frees unused data
*****************************************************************************/
static void MP4End( input_thread_t *p_input )
{
#define FREE( p ) \
if( p ) { free( p ); }
int i_track;
demux_data_mp4_t *p_demux = p_input->p_demux_data;
msg_Dbg( p_input, "Freeing all memory" );
MP4_FreeBox( p_input, &p_demux->box_root );
for( i_track = 0; i_track < p_demux->i_tracks; i_track++ )
{
int i_chunk;
for( i_chunk = 0;
i_chunk < p_demux->track[i_track].i_chunk_count; i_chunk++ )
{
if( p_demux->track[i_track].chunk )
{
FREE(p_demux->track[i_track].chunk[i_chunk].p_sample_count_dts);
FREE(p_demux->track[i_track].chunk[i_chunk].p_sample_delta_dts );
}
}
if( p_demux->track->p_data_init )
{
input_DeletePacket( p_input->p_method_data,
p_demux->track->p_data_init );
}
if( !p_demux->track[i_track].i_sample_size )
{
FREE( p_demux->track[i_track].p_sample_size );
}
}
FREE( p_demux->track );
#undef FREE
}
/****************************************************************************
* Local functions, specific to vlc
****************************************************************************/
/****************************************************************************
* Parse track information and create all needed data to run a track
* If it succeed b_ok is set to 1 else to 0
****************************************************************************/
static void MP4_ParseTrack( input_thread_t *p_input,
track_data_mp4_t *p_demux_track,
MP4_Box_t * p_trak )
{
int i;
MP4_Box_t *p_tkhd = MP4_FindBox( p_trak, FOURCC_tkhd );
MP4_Box_t *p_tref = MP4_FindBox( p_trak, FOURCC_tref );
MP4_Box_t *p_edts = MP4_FindBox( p_trak, FOURCC_edts );
MP4_Box_t *p_mdia = MP4_FindBox( p_trak, FOURCC_mdia );
MP4_Box_t *p_mdhd;
MP4_Box_t *p_hdlr;
MP4_Box_t *p_minf;
MP4_Box_t *p_vmhd;
MP4_Box_t *p_smhd;
/* hint track unsuported */
/* by default, track isn't usable */
p_demux_track->b_ok = 0;
/* by default, we don't known the categorie */
p_demux_track->i_cat = UNKNOWN_ES;
if( ( !p_tkhd )||( !p_mdia ) )
{
return;
}
/* do we launch this track by default ? */
p_demux_track->b_enable = ( ( p_tkhd->data.p_tkhd->i_flags&MP4_TRACK_ENABLED ) != 0 );
p_demux_track->i_track_ID = p_tkhd->data.p_tkhd->i_track_ID;
p_demux_track->i_width = p_tkhd->data.p_tkhd->i_width / 65536;
p_demux_track->i_height = p_tkhd->data.p_tkhd->i_height / 65536;
if( !p_edts )
{
// msg_Warn( p_input, "Unhandled box: edts --> FIXME" );
}
if( !p_tref )
{
// msg_Warn( p_input, "Unhandled box: tref --> FIXME" );
}
p_mdhd = MP4_FindBox( p_mdia, FOURCC_mdhd );
p_hdlr = MP4_FindBox( p_mdia, FOURCC_hdlr );
p_minf = MP4_FindBox( p_mdia, FOURCC_minf );
if( ( !p_mdhd )||( !p_hdlr )||( !p_minf ) )
{
return;
}
p_demux_track->i_timescale = p_mdhd->data.p_mdhd->i_timescale;
for( i = 0; i < 3; i++ )
{
p_demux_track->i_language[i] = p_mdhd->data.p_mdhd->i_language[i];
}
switch( p_hdlr->data.p_hdlr->i_handler_type )
{
case( FOURCC_soun ):
if( !( p_smhd = MP4_FindBox( p_minf, FOURCC_smhd ) ) )
{
return;
}
p_demux_track->i_cat = AUDIO_ES;
break;
case( FOURCC_vide ):
if( !( p_vmhd = MP4_FindBox( p_minf, FOURCC_vmhd ) ) )
{
return;
}
p_demux_track->i_cat = VIDEO_ES;
break;
default:
return;
}
/* FIXME
add support to:
p_dinf = MP4_FindBox( p_minf, FOURCC_dinf );
*/
if( !( p_demux_track->p_stbl = MP4_FindBox( p_minf, FOURCC_stbl ) ) )
{
return;
}
if( !( p_demux_track->p_stsd = MP4_FindBox( p_demux_track->p_stbl, FOURCC_stsd ) ) )
{
return;
}
/* Create chunk index table */
if( !MP4_CreateChunksIndex( p_input,p_demux_track ) )
{
return; /* cannot create chunks index */
}
/* create sample index table needed for reading and seeking */
if( !MP4_CreateSamplesIndex( p_input, p_demux_track ) )
{
return; /* cannot create samples index */
}
p_demux_track->b_ok = 1;
}
/* now create basic chunk data, the rest will be filled by MP4_CreateSamplesIndex */
static int MP4_CreateChunksIndex( input_thread_t *p_input,
track_data_mp4_t *p_demux_track )
{
MP4_Box_t *p_co64; /* give offset for each chunk, same for stco and co64 */
MP4_Box_t *p_stsc;
int i_chunk;
int i_index, i_last;
if( ( !(p_co64 = MP4_FindBox( p_demux_track->p_stbl, FOURCC_stco ) )&&
!(p_co64 = MP4_FindBox( p_demux_track->p_stbl, FOURCC_co64 ) ) )||
( !(p_stsc = MP4_FindBox( p_demux_track->p_stbl, FOURCC_stsc ) ) ))
{
return( 0 );
}
p_demux_track->i_chunk_count = p_co64->data.p_co64->i_entry_count;
if( !p_demux_track->i_chunk_count )
{
msg_Warn( p_input, "No chunk defined" );
return( 0 );
}
p_demux_track->chunk = calloc( p_demux_track->i_chunk_count,
sizeof( chunk_data_mp4_t ) );
/* 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];
}
/* now we read index for SampleEntry( soun vide mp4a mp4v ...)
to be used for the sample XXX begin to 1
We construct it begining at the end */
i_last = p_demux_track->i_chunk_count; /* last chunk proceded */
i_index = p_stsc->data.p_stsc->i_entry_count;
if( !i_index )
{
msg_Warn( p_input, "cannot read chunk table or table empty" );
return( 0 );
}
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++ )
{
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 =
p_stsc->data.p_stsc->i_samples_per_chunk[i_index];
}
i_last = p_stsc->data.p_stsc->i_first_chunk[i_index] - 1;
}
p_demux_track->chunk[i_chunk].i_sample_first = 0;
for( i_chunk = 1; i_chunk < p_demux_track->i_chunk_count; i_chunk++ )
{
p_demux_track->chunk[i_chunk].i_sample_first =
p_demux_track->chunk[i_chunk-1].i_sample_first +
p_demux_track->chunk[i_chunk-1].i_sample_count;
}
msg_Dbg( p_input, "read %d chunk", p_demux_track->i_chunk_count );
return( 1 );
}
static int MP4_CreateSamplesIndex( input_thread_t *p_input,
track_data_mp4_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 */
/* TODO use also stss and stsh table for seeking */
/* FIXME use edit table */
int i_sample;
int i_chunk;
int i_index;
int i_index_sample_used;
u64 i_last_dts;
p_stts = MP4_FindBox( p_demux_track->p_stbl, FOURCC_stts );
p_stsz = MP4_FindBox( p_demux_track->p_stbl, FOURCC_stsz ); /* FIXME and stz2 */
if( ( !p_stts )||( !p_stsz ) )
{
msg_Warn( p_input, "cannot read sample table" );
return( 0 );
}
p_demux_track->i_sample_count = p_stsz->data.p_stsz->i_sample_count;
/* for sample size, there are 2 case */
if( p_stsz->data.p_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->p_sample_size = NULL;
}
else
{
/* 2: each sample can have a different size */
p_demux_track->i_sample_size = 0;
p_demux_track->p_sample_size =
calloc( p_demux_track->i_sample_count, sizeof( u32 ) );
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];
}
}
/* 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 */
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++ )
{
int 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 */
i_entry = 0;
i_sample_count = p_demux_track->chunk[i_chunk].i_sample_count;
while( i_sample_count > 0 )
{
i_sample_count -= p_stts->data.p_stts->i_sample_count[i_index+i_entry];
if( i_entry == 0 )
{
i_sample_count += i_index_sample_used; /* don't count already used sample
int this entry */
}
i_entry++;
}
/* allocate them */
p_demux_track->chunk[i_chunk].p_sample_count_dts =
calloc( i_entry, sizeof( u32 ) );
p_demux_track->chunk[i_chunk].p_sample_delta_dts =
calloc( i_entry, sizeof( u32 ) );
/* now copy */
i_sample_count = p_demux_track->chunk[i_chunk].i_sample_count;
for( i = 0; i < i_entry; i++ )
{
int i_used;
int i_rest;
i_rest = p_stts->data.p_stts->i_sample_count[i_index] - i_index_sample_used;
i_used = __MIN( i_rest, i_sample_count );
i_index_sample_used += i_used;
p_demux_track->chunk[i_chunk].p_sample_count_dts[i] = i_used;
p_demux_track->chunk[i_chunk].p_sample_delta_dts[i] =
p_stts->data.p_stts->i_sample_delta[i_index];
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] )
{
i_index++;
i_index_sample_used = 0;
}
}
}
msg_Dbg( p_input, "read %d samples", p_demux_track->i_sample_count );
return( 1 );
}
static void MP4_StartDecoder( input_thread_t *p_input,
track_data_mp4_t *p_demux_track )
{
MP4_Box_t *p_sample;
int i;
int i_chunk;
u8 *p_bmih;
int i_codec;
char *psz_name;
MP4_Box_t *p_esds;
if( (!p_demux_track->b_ok )||( p_demux_track->i_cat == UNKNOWN_ES ) )
{
return;
}
msg_Dbg( p_input, "Starting decoder (track ID 0x%x)",
p_demux_track->i_track_ID );
/* launch decoder according in chunk we are */
i_chunk = p_demux_track->i_chunk;
if( !p_demux_track->chunk[i_chunk].i_sample_description_index )
{
msg_Warn( p_input, "invalid SampleEntry index for this track i_cat %d" );
return;
}
p_sample = MP4_FindNbBox( p_demux_track->p_stsd,
p_demux_track->chunk[i_chunk].i_sample_description_index - 1);
if( !p_sample )
{
msg_Warn( p_input, "cannot find SampleEntry for this track" );
return;
}
vlc_mutex_lock( &p_input->stream.stream_lock );
p_demux_track->p_es = input_AddES( p_input,
p_input->stream.p_selected_program,
p_demux_track->i_track_ID,
0 );
vlc_mutex_unlock( &p_input->stream.stream_lock );
/* Initialise ES, first language as description */
for( i = 0; i < 3; i++ )
{
p_demux_track->p_es->psz_desc[i] = p_demux_track->i_language[i];
}
p_demux_track->p_es->psz_desc[4] = 0;
p_demux_track->p_es->i_stream_id = p_demux_track->i_track_ID;
p_demux_track->p_es->i_type = UNKNOWN_ES;
p_demux_track->p_es->i_cat = p_demux_track->i_cat;
/* search for the codec */
if( !MP4_GetCodec( p_sample->i_type, &i_codec, &psz_name ) )
{
msg_Warn( p_input, "%s (%c%c%c%c) unsupported",
psz_name,
(p_sample->i_type >> 24)&0xff,
(p_sample->i_type >> 16)&0xff,
(p_sample->i_type >> 8)&0xff,
(p_sample->i_type )&0xff);
p_demux_track->b_ok = 0;
return;
}
else
{
p_demux_track->p_es->i_type = i_codec;
msg_Info( p_input, "%s supported", psz_name );
}
switch( p_demux_track->i_cat )
{
case( VIDEO_ES ):
p_demux_track->p_es->b_audio = 0;
/* now create a bitmapinfoheader_t for decoder */
p_bmih = malloc( 40 );
memset( p_bmih, 0, 40);
MP4_Set4BytesLE( p_bmih, 40 );
if( p_sample->data.p_sample_mp4v->i_width )
{
MP4_Set4BytesLE( p_bmih + 4, p_sample->data.p_sample_mp4v->i_width );
}
else
{
/* use display size */
MP4_Set4BytesLE( p_bmih + 4, p_demux_track->i_width );
}
if( p_sample->data.p_sample_mp4v->i_height )
{
MP4_Set4BytesLE( p_bmih + 8, p_sample->data.p_sample_mp4v->i_height );
}
else
{
MP4_Set4BytesLE( p_bmih + 8, p_demux_track->i_height );
}
p_demux_track->p_es->p_demux_data = p_bmih;
break;
case( AUDIO_ES ):
p_demux_track->p_es->b_audio = 1;
break;
default:
break;
}
vlc_mutex_lock( &p_input->stream.stream_lock );
input_SelectES( p_input, p_demux_track->p_es );
vlc_mutex_unlock( &p_input->stream.stream_lock );
p_demux_track->b_ok = 1;
/* now see if esds is present and i so create a data packet
with decoder_specific_info */
if( ( p_esds = MP4_FindBox( p_sample, FOURCC_esds ) )&&
( p_esds->data.p_esds->es_descriptor.p_decConfigDescr )&&
( p_esds->data.p_esds->es_descriptor.p_decConfigDescr->i_decoder_specific_info_len ) )
{
data_packet_t *p_data;
int i_size = p_esds->data.p_esds->es_descriptor.p_decConfigDescr->i_decoder_specific_info_len;
/* data packet for the data */
if( !(p_data = input_NewPacket( p_input->p_method_data, i_size ) ) )
{
return;
}
/* initialisation of all the field */
memcpy( p_data->p_payload_start,
p_esds->data.p_esds->es_descriptor.p_decConfigDescr->p_decoder_specific_info,
i_size );
p_demux_track->p_data_init = p_data;
}
return;
}
static void MP4_StopDecoder( input_thread_t *p_input,
track_data_mp4_t *p_demux_track )
{
msg_Dbg( p_input, "Stopping decoder (track ID 0x%x)",
p_demux_track->i_track_ID );
input_UnselectES( p_input, p_demux_track->p_es );
p_demux_track->p_es = NULL;
if( p_demux_track->p_data_init )
{
input_DeletePacket( p_input->p_method_data,
p_demux_track->p_data_init );
p_demux_track->p_data_init = NULL;
}
}
static int MP4_ReadSample( input_thread_t *p_input,
track_data_mp4_t *p_demux_track,
pes_packet_t **pp_pes )
{
int i_size;
off_t i_pos;
data_packet_t *p_data;
/* this track have already reach the end */
if( p_demux_track->i_sample >= p_demux_track->i_sample_count )
{
*pp_pes = NULL;
return( 0 );
}
/* caculate size and position for this sample */
i_size = p_demux_track->i_sample_size ?
p_demux_track->i_sample_size : p_demux_track->p_sample_size[p_demux_track->i_sample];
/* TODO */
i_pos = MP4_GetTrackPos( p_demux_track );
/* go,go go ! */
if( ! MP4_SeekAbsolute( p_input, i_pos ) )
{
return( 0 );
}
/* now create a pes */
if( !(*pp_pes = input_NewPES( p_input->p_method_data ) ) )
{
return( 0 );
}
/* and a data packet for the data */
if( !(p_data = input_NewPacket( p_input->p_method_data, i_size ) ) )
{
input_DeletePES( p_input->p_method_data, *pp_pes );
*pp_pes = NULL;
return( 0 );
}
/* initialisation of all the field */
(*pp_pes)->i_dts =
(*pp_pes)->i_pts = MP4_GetTrackPTS( p_demux_track );
(*pp_pes)->p_first = (*pp_pes)->p_last = p_data;
(*pp_pes)->i_nb_data = 1;
(*pp_pes)->i_pes_size = i_size;
if( !i_size )
{
return( 1 );
}
// msg_Dbg( p_input, "will read %d bytes", i_size );
if( !MP4_ReadData( p_input, p_data->p_payload_start, i_size ) )
{
input_DeletePES( p_input->p_method_data, *pp_pes );
input_DeletePacket( p_input->p_method_data, p_data );
return( 0 );
}
return( 1 );
}
static int MP4_DecodeSample( input_thread_t *p_input,
track_data_mp4_t *p_demux_track,
pes_packet_t *p_pes )
{
if( !p_pes )
{
return( 0 );
}
/* don't forget to convert in mpeg clock */
/* FIXME correct ffmpeg to use dts instead of pts that it incorrect
and, set it here ( and correct avi demux ) */
p_pes->i_dts =
p_pes->i_pts = input_ClockGetTS( p_input,
p_input->stream.p_selected_program,
p_pes->i_pts * 9/100);
if( p_demux_track->p_data_init )
{
pes_packet_t *p_pes_init;
/* create a pes packet containing decoder initialisation
with the one we will send to decoder */
if( !(p_pes_init = input_NewPES( p_input->p_method_data ) ) )
{
msg_Err( p_input, "out of memory" );
return( 0 );
}
p_pes_init->p_first =
p_pes_init->p_last = p_demux_track->p_data_init;
p_pes_init->i_pes_size = p_demux_track->p_data_init->p_payload_end -
p_demux_track->p_data_init->p_payload_start;
p_pes_init->i_nb_data = 1;
input_DecodePES( p_demux_track->p_es->p_decoder_fifo, p_pes_init );
p_demux_track->p_data_init = NULL;
}
input_DecodePES( p_demux_track->p_es->p_decoder_fifo, p_pes );
/* now update sample position */
p_demux_track->i_sample++; /* easy ;) */
if( p_demux_track->i_sample >= p_demux_track->i_sample_count )
{
/* we have reach end of the track so free decoder stuff */
MP4_StopDecoder( p_input, p_demux_track );
return( 1 );
}
/* Have we changed chunk ? */
if( p_demux_track->i_sample >=
p_demux_track->chunk[p_demux_track->i_chunk].i_sample_first +
p_demux_track->chunk[p_demux_track->i_chunk].i_sample_count )
{
/* we haven't reached the end of the track, so see if we
have to change the decoder for the next frame because
i_sample_description_index have changed */
p_demux_track->i_chunk++;
if( p_demux_track->chunk[p_demux_track->i_chunk-1].i_sample_description_index
!= p_demux_track->chunk[p_demux_track->i_chunk].i_sample_description_index )
{
/* FIXME */
msg_Err( p_input, "I need to change the decoder but not yet implemented" );
return( 0 );
}
}
return( 1 );
}
/*****************************************************************************
* mp4.h : MP4 file input module for vlc
*****************************************************************************
* Copyright (C) 2001 VideoLAN
* $Id: mp4.h,v 1.1 2002/07/17 21:37:27 fenrir Exp $
* Authors: Laurent Aimar <fenrir@via.ecp.fr>
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 2 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* 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.
*****************************************************************************/
/*****************************************************************************
* Structure needed for ffmpeg decoder
*****************************************************************************/
typedef struct bitmapinfoheader_s
{
u32 i_size; /* size of header */
u32 i_width;
u32 i_height;
u16 i_planes;
u16 i_bitcount;
u32 i_compression;
u32 i_sizeimage;
u32 i_xpelspermeter;
u32 i_ypelspermeter;
u32 i_clrused;
u32 i_clrimportant;
} bitmapinfoheader_t;
/*****************************************************************************
* Contain all information about a chunk
*****************************************************************************/
typedef struct chunk_data_mp4_s
{
u64 i_offset; /* absolute position of this chunk in the file */
u32 i_sample_description_index; /* index for SampleEntry to use */
u32 i_sample_count; /* how many samples in this chunk */
u32 i_sample_first; /* index of the first sample in this chunk */
/* now provide way to calculate pts, dts, and offset without to
much memory and with fast acces */
/* with this we can calculate dts/pts without waste memory */
u64 i_first_dts;
u32 *p_sample_count_dts;
u32 *p_sample_delta_dts; /* dts delta */
/* TODO if needed add pts
but quickly *add* support for edts and seeking */
} chunk_data_mp4_t;
/*****************************************************************************
* Contain all needed information for read all track with vlc
*****************************************************************************/
typedef struct track_data_mp4_s
{
int b_ok; /* The track is usable */
int i_track_ID; /* this should be unique */
int b_enable; /* is the trak enable by default */
int i_cat; /* Type of the track, VIDEO_ES, AUDIO_ES, UNKNOWN_ES ... */
char i_language[3];
/* display size only ! */
int i_width;
int i_height;
/* more internal data */
u64 i_timescale; /* time scale for this track only */
/* give the next sample to read, i_chunk is to find quickly where
the sample is located */
u32 i_sample; /* next sample to read */
u32 i_chunk; /* chunk where next sample is stored */
/* total count of chunk and sample */
u32 i_chunk_count;
u32 i_sample_count;
chunk_data_mp4_t *chunk; /* always defined for each chunk */
/* sample size, p_sample_size defined only if i_sample_size == 0
else i_sample_size is size for all sample */
u32 i_sample_size;
u32 *p_sample_size; /* XXX perhaps add file offset if take
too much time to do sumations each time*/
es_descriptor_t *p_es; /* vlc es for this track */
data_packet_t *p_data_init; /* send this with the first packet,
and then discarded it*/
MP4_Box_t *p_stbl; /* will contain all timing information */
MP4_Box_t *p_stsd; /* will contain all data to initialize decoder */
MP4_Box_t *p_sample; /* actual SampleEntry to make life simpler */
} track_data_mp4_t;
/*****************************************************************************
*
*****************************************************************************/
typedef struct demux_data_mp4_s
{
MP4_Box_t box_root; /* container for the hole file */
mtime_t i_pcr;
u64 i_time; /* time position of the presentation in movie timescale */
u64 i_timescale; /* movie time scale */
int i_tracks; /* number of track */
track_data_mp4_t *track; /* array of track */
} demux_data_mp4_t;
static inline u64 MP4_GetTrackPos( track_data_mp4_t *p_track )
{
int i_sample;
u64 i_pos;
i_pos = p_track->chunk[p_track->i_chunk].i_offset;
if( p_track->i_sample_size )
{
i_pos += ( p_track->i_sample -
p_track->chunk[p_track->i_chunk].i_sample_first ) *
p_track->i_sample_size;
}
else
{
for( i_sample = p_track->chunk[p_track->i_chunk].i_sample_first;
i_sample < p_track->i_sample; i_sample++ )
{
i_pos += p_track->p_sample_size[i_sample];
}
}
return( i_pos );
}
/* Return time in s of a track */
static inline mtime_t MP4_GetTrackPTS( track_data_mp4_t *p_track )
{
int i_sample;
int i_index;
u64 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;
}
}
return( (mtime_t)(
(mtime_t)1000000 *
(mtime_t)i_dts /
(mtime_t)p_track->i_timescale ) );
}
static inline mtime_t MP4_GetMoviePTS(demux_data_mp4_t *p_demux )
{
return( (mtime_t)(
(mtime_t)1000000 *
(mtime_t)p_demux->i_time /
(mtime_t)p_demux->i_timescale )
);
}
static struct
{
u32 i_fourcc;
int i_codec;
char *psz_name;
} MP4_Codecs [] = {
/* Video codec */
{ FOURCC_vide, UNKNOWN_ES, "Generic VisualSampleEntry" },
{ FOURCC_DIVX, MPEG4_VIDEO_ES, "DIVX SampleEntry (MPEG-4)" },
{ FOURCC_h263, H263_VIDEO_ES, "H263 SampleEntry" },
{ FOURCC_SVQ1, SVQ1_VIDEO_ES, "SVQ1 SampleEntry (Sorenson Video v1" },
{ FOURCC_mp4v, MPEG4_VIDEO_ES, "MP4VisualSampleEntry (MPEG-4)" },
{ FOURCC_3IV1, UNKNOWN_ES, "3IV1 SampleEntry" },
/* Audio codec */
{ FOURCC_soun, UNKNOWN_ES, "Generic AudioSampleEntry" },
{ FOURCC_mp4a, UNKNOWN_ES, "MP4AudioSampleEntry (AAC)" },
{ FOURCC__mp3, MPEG2_AUDIO_ES, ".mp3 SampleEntry (MPEG-I/II layer 3)" },
/* Last entry with fourcc == 0 */
{ 0, UNKNOWN_ES, "Unknown SampleEntry" }
};
static int MP4_GetCodec( u32 i_fourcc,
int *pi_codec,
char **ppsz_name )
{
int i;
for( i = 0; ; i++ )
{
if( ( MP4_Codecs[i].i_fourcc == i_fourcc )||
( MP4_Codecs[i].i_fourcc == 0) )
{
break;
}
}
if( pi_codec )
*pi_codec = MP4_Codecs[i].i_codec;
if( ppsz_name )
*ppsz_name = MP4_Codecs[i].psz_name;
return( MP4_Codecs[i].i_codec == UNKNOWN_ES ? 0 : 1 );
}
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