*****************************************************************************/
#include <vlc/vlc.h>
-#include <stdio.h>
-#include <stdlib.h> /* malloc(), free() */
-#include <vlc/input.h>
+#include <vlc_demux.h>
#ifdef HAVE_ZLIB_H
# include <zlib.h> /* for compressed moov */
* *look* at the code.
*
*****************************************************************************/
-#define MP4_BOX_HEADERSIZE( p_box ) \
- ( 8 + ( p_box->i_shortsize == 1 ? 8 : 0 ) \
+#define MP4_BOX_HEADERSIZE( p_box ) \
+ ( 8 + ( p_box->i_shortsize == 1 ? 8 : 0 ) \
+ ( p_box->i_type == FOURCC_uuid ? 16 : 0 ) )
-#define MP4_GET1BYTE( dst ) \
- dst = *p_peek; p_peek++; i_read--
+#define MP4_GETX_PRIVATE(dst, code, size) do { \
+ if( (i_read) >= (size) ) { dst = (code); p_peek += (size); } \
+ else { dst = 0; } \
+ i_read -= (size); \
+ } while(0)
-#define MP4_GET2BYTES( dst ) \
- dst = GetWBE( p_peek ); p_peek += 2; i_read -= 2
-
-#define MP4_GET3BYTES( dst ) \
- dst = Get24bBE( p_peek ); p_peek += 3; i_read -= 3
-
-#define MP4_GET4BYTES( dst ) \
- dst = GetDWBE( p_peek ); p_peek += 4; i_read -= 4
-
-#define MP4_GETFOURCC( dst ) \
- dst = VLC_FOURCC( p_peek[0], p_peek[1], p_peek[2], p_peek[3] ); \
- p_peek += 4; i_read -= 4
-
-#define MP4_GET8BYTES( dst ) \
- dst = GetQWBE( p_peek ); p_peek += 8; i_read -= 8
+#define MP4_GET1BYTE( dst ) MP4_GETX_PRIVATE( dst, *p_peek, 1 )
+#define MP4_GET2BYTES( dst ) MP4_GETX_PRIVATE( dst, GetWBE(p_peek), 2 )
+#define MP4_GET3BYTES( dst ) MP4_GETX_PRIVATE( dst, Get24bBE(p_peek), 3 )
+#define MP4_GET4BYTES( dst ) MP4_GETX_PRIVATE( dst, GetDWBE(p_peek), 4 )
+#define MP4_GET8BYTES( dst ) MP4_GETX_PRIVATE( dst, GetQWBE(p_peek), 8 )
+#define MP4_GETFOURCC( dst ) MP4_GETX_PRIVATE( dst, \
+ VLC_FOURCC(p_peek[0],p_peek[1],p_peek[2],p_peek[3]), 4)
#define MP4_GETVERSIONFLAGS( p_void ) \
MP4_GET1BYTE( p_void->i_version ); \
MP4_GET3BYTES( p_void->i_flags )
-#define MP4_GETSTRINGZ( p_str ) \
- if( ( i_read > 0 )&&(p_peek[0] ) ) \
- { \
- p_str = calloc( sizeof( char ), __MIN( strlen( (char*)p_peek ), i_read )+1);\
- memcpy( p_str, p_peek, __MIN( strlen( (char*)p_peek ), i_read ) ); \
- p_str[__MIN( strlen( (char*)p_peek ), i_read )] = 0; \
- p_peek += strlen( (char *)p_str ) + 1; \
- i_read -= strlen( (char *)p_str ) + 1; \
- } \
- else \
- { \
+#define MP4_GETSTRINGZ( p_str ) \
+ if( (i_read > 0) && (p_peek[0]) ) \
+ { \
+ const int __i_copy__ = strnlen( (char*)p_peek, i_read-1 ); \
+ p_str = calloc( sizeof(char), __i_copy__+1 ); \
+ if( __i_copy__ > 0 ) memcpy( p_str, p_peek, __i_copy__ ); \
+ p_str[__i_copy__] = 0; \
+ p_peek += __i_copy__ + 1; \
+ i_read -= __i_copy__ + 1; \
+ } \
+ else \
+ { \
p_str = NULL; \
}
-
#define MP4_READBOX_ENTER( MP4_Box_data_TYPE_t ) \
- uint64_t i_read = p_box->i_size; \
+ int64_t i_read = p_box->i_size; \
uint8_t *p_peek, *p_buff; \
int i_actually_read; \
if( !( p_peek = p_buff = malloc( i_read ) ) ) \
return( 0 ); \
} \
i_actually_read = stream_Read( p_stream, p_peek, i_read ); \
- if( i_actually_read < 0 || (uint64_t)i_actually_read < i_read )\
+ if( i_actually_read < 0 || (int64_t)i_actually_read < i_read )\
{ \
free( p_buff ); \
return( 0 ); \
*/
-static uint32_t Get24bBE( uint8_t *p )
+static uint32_t Get24bBE( const uint8_t *p )
{
return( ( p[0] <<16 ) + ( p[1] <<8 ) + p[2] );
}
-static void GetUUID( UUID_t *p_uuid, uint8_t *p_buff )
+static void GetUUID( UUID_t *p_uuid, const uint8_t *p_buff )
{
memcpy( p_uuid, p_buff, 16 );
}
int MP4_ReadBoxCommon( stream_t *p_stream, MP4_Box_t *p_box )
{
int i_read;
- uint8_t *p_peek;
+ const uint8_t *p_peek;
if( ( ( i_read = stream_Peek( p_stream, &p_peek, 32 ) ) < 8 ) )
{
if( p_box->p_father )
{
+ const int i_box_end = p_box->i_size + p_box->i_pos;
+ const int i_father_end = p_box->p_father->i_size + p_box->p_father->i_pos;
+
/* 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 )
+ if( i_box_end >= i_father_end )
{
- msg_Dbg( p_stream, "out of bound child" );
+ if( i_box_end > i_father_end )
+ msg_Dbg( p_stream, "out of bound child" );
return 0; /* out of bound */
}
}
p_box->p_father->i_type == VLC_FOURCC( 'r', 'o', 'o', 't' ) &&
p_box->i_type == FOURCC_free )
{
- uint8_t *p_peek;
+ const uint8_t *p_peek;
int i_read;
vlc_fourcc_t i_fcc;
FREENULL( p_box->data.p_ctts->i_sample_offset );
}
-static int MP4_ReadLengthDescriptor( uint8_t **pp_peek, uint64_t *i_read )
+static int MP4_ReadLengthDescriptor( uint8_t **pp_peek, int64_t *i_read )
{
unsigned int i_b;
unsigned int i_len = 0;
{
/* SoundDescriptionV2 */
double f_sample_rate;
- int64_t dummy;
+ int64_t dummy;
uint32_t i_channel;
MP4_GET4BYTES( p_box->data.p_sample_soun->i_sample_per_packet );
MP4_GET8BYTES( dummy );
- memcpy( &f_sample_rate, &dummy, 8 );
+ memcpy( &f_sample_rate, &dummy, 8 );
msg_Dbg( p_stream, "read box: %f Hz", f_sample_rate );
p_box->data.p_sample_soun->i_sampleratehi = (int)f_sample_rate % 65536;
{
unsigned int i;
- MP4_READBOX_ENTER( MP4_Box_data_padb_t );
+ MP4_READBOX_ENTER( MP4_Box_data_elst_t );
MP4_GETVERSIONFLAGS( p_box->data.p_elst );
}
#ifdef MP4_VERBOSE
- msg_Dbg( p_stream, "read box: \"elst\" entry-count "I64Fd,
- i_read / 2 );
-
+ msg_Dbg( p_stream, "read box: \"elst\" entry-count %lu",
+ (unsigned long)p_box->data.p_elst->i_entry_count );
#endif
MP4_READBOX_EXIT( 1 );
}
}
if( i_ret )
{
- char *psz_error;
+ const char *psz_error;
switch( i_ret )
{
p_box->data.p_0xa9xxx->psz_text = NULL;
MP4_GET2BYTES( i_length );
- MP4_GET2BYTES( i_dummy );
if( i_length > 0 )
{
+ MP4_GET2BYTES( i_dummy );
if( i_length > i_read ) i_length = i_read;
p_box->data.p_0xa9xxx->psz_text = malloc( i_length + 1 );
#ifdef MP4_VERBOSE
msg_Dbg( p_stream,
- "read box: \"%4.4s\" text=`%s'",
- (char*)&p_box->i_type,
+ "read box: \"c%3.3s\" text=`%s'",
+ ((char*)&p_box->i_type + 1),
p_box->data.p_0xa9xxx->psz_text );
#endif
}
+ else
+ {
+ /* try iTune/Quicktime format, rewind to start */
+ p_peek -= 2; i_read += 2;
+ // we are expecting a 'data' box
+ uint32_t i_data_len;
+ uint32_t i_data_tag;
+
+ MP4_GET4BYTES( i_data_len );
+ if( i_data_len > i_read ) i_data_len = i_read;
+ MP4_GETFOURCC( i_data_tag );
+ if( (i_data_len > 0) && (i_data_tag == VLC_FOURCC('d', 'a', 't', 'a')) )
+ {
+ /* data box contains a version/flags field */
+ uint32_t i_version;
+ uint32_t i_reserved;
+ MP4_GET4BYTES( i_version );
+ MP4_GET4BYTES( i_reserved );
+ // version should be 0, flags should be 1 for text, 0 for data
+ if( i_version == 0x00000001 )
+ {
+ // the rest is the text
+ i_data_len -= 12;
+ p_box->data.p_0xa9xxx->psz_text = malloc( i_data_len + 1 );
+
+ memcpy( p_box->data.p_0xa9xxx->psz_text,
+ p_peek, i_data_len );
+ p_box->data.p_0xa9xxx->psz_text[i_data_len] = '\0';
+#ifdef MP4_VERBOSE
+ msg_Dbg( p_stream,
+ "read box: \"c%3.3s\" text=`%s'",
+ ((char*)&p_box->i_type+1),
+ p_box->data.p_0xa9xxx->psz_text );
+#endif
+ }
+ else
+ {
+ // TODO: handle data values for ID3 tag values, track num or cover art,etc...
+ }
+ }
+ }
MP4_READBOX_EXIT( 1 );
}
FREENULL( p_box->data.p_0xa9xxx->psz_text );
}
+/* Chapter support */
+static int MP4_ReadBox_chpl( stream_t *p_stream, MP4_Box_t *p_box )
+{
+ MP4_Box_data_chpl_t *p_chpl;
+ uint32_t i_dummy;
+ int i;
+ MP4_READBOX_ENTER( MP4_Box_data_chpl_t );
+
+ p_chpl = p_box->data.p_chpl;
+
+ MP4_GETVERSIONFLAGS( p_chpl );
+
+ MP4_GET4BYTES( i_dummy );
+
+ MP4_GET1BYTE( p_chpl->i_chapter );
+
+ for( i = 0; i < p_chpl->i_chapter; i++ )
+ {
+ uint64_t i_start;
+ uint8_t i_len;
+ int i_copy;
+ MP4_GET8BYTES( i_start );
+ MP4_GET1BYTE( i_len );
+
+ p_chpl->chapter[i].psz_name = malloc( i_len + 1 );
+ i_copy = __MIN( i_len, i_read );
+ if( i_copy > 0 )
+ memcpy( p_chpl->chapter[i].psz_name, p_peek, i_copy );
+ p_chpl->chapter[i].psz_name[i_copy] = '\0';
+ p_chpl->chapter[i].i_start = i_start;
+
+ p_peek += i_copy;
+ i_read -= i_copy;
+ }
+ /* Bubble sort by increasing start date */
+ do
+ {
+ for( i = 0; i < p_chpl->i_chapter - 1; i++ )
+ {
+ if( p_chpl->chapter[i].i_start > p_chpl->chapter[i+1].i_start )
+ {
+ char *psz = p_chpl->chapter[i+1].psz_name;
+ int64_t i64 = p_chpl->chapter[i+1].i_start;
+
+ p_chpl->chapter[i+1].psz_name = p_chpl->chapter[i].psz_name;
+ p_chpl->chapter[i+1].i_start = p_chpl->chapter[i].i_start;
+
+ p_chpl->chapter[i].psz_name = psz;
+ p_chpl->chapter[i].i_start = i64;
+
+ i = -1;
+ break;
+ }
+ }
+ } while( i == -1 );
+
+#ifdef MP4_VERBOSE
+ msg_Dbg( p_stream, "read box: \"chpl\" %d chapters",
+ p_chpl->i_chapter );
+#endif
+ MP4_READBOX_EXIT( 1 );
+}
+static void MP4_FreeBox_chpl( MP4_Box_t *p_box )
+{
+ MP4_Box_data_chpl_t *p_chpl = p_box->data.p_chpl;
+ int i;
+ for( i = 0; i < p_chpl->i_chapter; i++ )
+ free( p_chpl->chapter[i].psz_name );
+}
+
+static int MP4_ReadBox_tref_generic( stream_t *p_stream, MP4_Box_t *p_box )
+{
+ unsigned int i;
+ MP4_READBOX_ENTER( MP4_Box_data_tref_generic_t );
+
+ p_box->data.p_tref_generic->i_track_ID = NULL;
+ p_box->data.p_tref_generic->i_entry_count = i_read / sizeof(uint32_t);
+ if( p_box->data.p_tref_generic->i_entry_count > 0 )
+ p_box->data.p_tref_generic->i_track_ID = malloc( p_box->data.p_tref_generic->i_entry_count * sizeof(uint32_t) );
+
+ for( i = 0; i < p_box->data.p_tref_generic->i_entry_count; i++ )
+ {
+ MP4_GET4BYTES( p_box->data.p_tref_generic->i_track_ID[i] );
+ }
+#ifdef MP4_VERBOSE
+ msg_Dbg( p_stream, "read box: \"chap\" %d references",
+ p_box->data.p_tref_generic->i_entry_count );
+#endif
+
+ MP4_READBOX_EXIT( 1 );
+}
+static void MP4_FreeBox_tref_generic( MP4_Box_t *p_box )
+{
+ FREENULL( p_box->data.p_tref_generic->i_track_ID );
+}
+
+static int MP4_ReadBox_meta( stream_t *p_stream, MP4_Box_t *p_box )
+{
+ uint8_t meta_data[8];
+ int i_actually_read;
+
+ // skip over box header
+ i_actually_read = stream_Read( p_stream, meta_data, 8 );
+ if( i_actually_read < 8 )
+ return 0;
+
+ /* meta content starts with a 4 byte version/flags value (should be 0) */
+ i_actually_read = stream_Read( p_stream, meta_data, 4 );
+ if( i_actually_read < 4 )
+ return 0;
+
+ /* then it behaves like a container */
+ return MP4_ReadBoxContainerRaw( p_stream, p_box );
+}
+
/* For generic */
static int MP4_ReadBox_default( stream_t *p_stream, MP4_Box_t *p_box )
{
{ FOURCC_tref, MP4_ReadBoxContainer, MP4_FreeBox_Common },
{ FOURCC_gmhd, MP4_ReadBoxContainer, MP4_FreeBox_Common },
{ FOURCC_wave, MP4_ReadBoxContainer, MP4_FreeBox_Common },
+ { FOURCC_ilst, MP4_ReadBoxContainer, MP4_FreeBox_Common },
/* specific box */
{ FOURCC_ftyp, MP4_ReadBox_ftyp, MP4_FreeBox_ftyp },
{ FOURCC_jpeg, MP4_ReadBox_sample_vide, MP4_FreeBox_sample_vide },
{ FOURCC_avc1, MP4_ReadBox_sample_vide, MP4_FreeBox_sample_vide },
+ { FOURCC_yv12, MP4_ReadBox_sample_vide, MP4_FreeBox_sample_vide },
+ { FOURCC_yuv2, MP4_ReadBox_sample_vide, MP4_FreeBox_sample_vide },
+
{ FOURCC_mp4s, MP4_ReadBox_sample_mp4s, MP4_FreeBox_Common },
/* XXX there is 2 box where we could find this entry stbl and tref*/
{ FOURCC_dpnd, MP4_ReadBox_default, NULL },
{ FOURCC_ipir, MP4_ReadBox_default, NULL },
{ FOURCC_mpod, MP4_ReadBox_default, NULL },
+ { FOURCC_chap, MP4_ReadBox_tref_generic, MP4_FreeBox_tref_generic },
/* found in hnti */
{ FOURCC_rtp, MP4_ReadBox_default, NULL },
{ FOURCC_0xa9ope,MP4_ReadBox_0xa9xxx, MP4_FreeBox_0xa9xxx },
{ FOURCC_0xa9com,MP4_ReadBox_0xa9xxx, MP4_FreeBox_0xa9xxx },
+ { FOURCC_chpl, MP4_ReadBox_chpl, MP4_FreeBox_chpl },
+
+ /* iTunes/Quicktime meta info */
+ { FOURCC_meta, MP4_ReadBox_meta, MP4_FreeBox_Common },
+
/* Last entry */
{ 0, MP4_ReadBox_default, NULL }
};
}
static void __MP4_BoxGet( MP4_Box_t **pp_result,
- MP4_Box_t *p_box, char *psz_fmt, va_list args)
+ MP4_Box_t *p_box, const char *psz_fmt, va_list args)
{
+ char *psz_dup;
char *psz_path;
if( !p_box )
}
// fprintf( stderr, "path:'%s'\n", psz_path );
- psz_fmt = psz_path; /* keep this pointer, as it need to be unallocated */
+ psz_dup = psz_path; /* keep this pointer, as it need to be unallocated */
for( ; ; )
{
char *psz_token;
if( !psz_token )
{
FREENULL( psz_token );
- free( psz_fmt );
+ free( psz_dup );
*pp_result = p_box;
return;
}
if( !p_box )
{
free( psz_token );
- free( psz_fmt );
+ free( psz_dup );
*pp_result = NULL;
return;
}
if( !p_box )
{
free( psz_token );
- free( psz_fmt );
+ free( psz_dup );
*pp_result = NULL;
return;
}
if( !p_box )
{
free( psz_token );
- free( psz_fmt );
+ free( psz_dup );
*pp_result = NULL;
return;
}
if( !p_box )
{
free( psz_token );
- free( psz_fmt );
+ free( psz_dup );
*pp_result = NULL;
return;
}
{
// fprintf( stderr, "Argg malformed token \"%s\"",psz_token );
FREENULL( psz_token );
- free( psz_fmt );
+ free( psz_dup );
*pp_result = NULL;
return;
}
* ex: /moov/trak[12]
* ../mdia
*****************************************************************************/
-MP4_Box_t *MP4_BoxGet( MP4_Box_t *p_box, char *psz_fmt, ... )
+MP4_Box_t *MP4_BoxGet( MP4_Box_t *p_box, const char *psz_fmt, ... )
{
va_list args;
MP4_Box_t *p_result;
* ex: /moov/trak[12]
* ../mdia
*****************************************************************************/
-int MP4_BoxCount( MP4_Box_t *p_box, char *psz_fmt, ... )
+int MP4_BoxCount( MP4_Box_t *p_box, const char *psz_fmt, ... )
{
va_list args;
int i_count;