Commit 1202e3a4 authored by Laurent Aimar's avatar Laurent Aimar

Implemented GPAC chapter support (but not apple ones).

Fixed a few invalid access on broken files.
Fixed compilation warnings.
parent 5cc69890
......@@ -39,44 +39,40 @@
* *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; \
}
......@@ -820,7 +816,7 @@ static void MP4_FreeBox_ctts( MP4_Box_t *p_box )
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;
......@@ -2155,6 +2151,76 @@ static void MP4_FreeBox_0xa9xxx( MP4_Box_t *p_box )
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_meta( stream_t *p_stream, MP4_Box_t *p_box )
{
uint8_t meta_data[8];
......@@ -2402,6 +2468,8 @@ static struct
{ 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 },
......
......@@ -209,6 +209,7 @@
#define FOURCC_0xa9wrt VLC_FOURCC( 0xa9, 'w', 'r', 't' )
#define FOURCC_0xa9com VLC_FOURCC( 0xa9, 'c', 'o', 'm' )
#define FOURCC_0xa9gen VLC_FOURCC( 0xa9, 'g', 'e', 'n' )
#define FOURCC_chpl VLC_FOURCC( 'c', 'h', 'p', 'l' )
#define FOURCC_WLOC VLC_FOURCC( 'W', 'L', 'O', 'C' )
#define FOURCC_meta VLC_FOURCC( 'm', 'e', 't', 'a' )
......@@ -790,6 +791,19 @@ typedef struct
} MP4_Box_data_0xa9xxx_t;
typedef struct
{
uint8_t i_version;
uint32_t i_flags;
uint8_t i_chapter;
struct
{
char *psz_name;
int64_t i_start;
} chapter[256];
} MP4_Box_data_chpl_t;
typedef struct
{
uint8_t i_version;
......@@ -872,6 +886,7 @@ typedef union MP4_Box_data_s
MP4_Box_data_rmvc_t *p_rmvc;
MP4_Box_data_0xa9xxx_t *p_0xa9xxx;
MP4_Box_data_chpl_t *p_chpl;
void *p_data; /* for unknow type */
} MP4_Box_data_t;
......
......@@ -151,6 +151,9 @@ struct demux_sys_t
uint64_t i_duration; /* movie duration */
unsigned int i_tracks; /* number of tracks */
mp4_track_t *track; /* array of track */
/* */
input_title_t *p_title;
};
/*****************************************************************************
......@@ -169,6 +172,8 @@ static int MP4_TrackSampleSize( mp4_track_t * );
static int MP4_TrackNextSample( demux_t *, mp4_track_t * );
static void MP4_TrackSetELST( demux_t *, mp4_track_t *, int64_t );
static void MP4_UpdateSeekpoint( demux_t * );
/* Return time in s of a track */
static inline int64_t MP4_TrackGetDTS( demux_t *p_demux, mp4_track_t *p_track )
{
......@@ -263,6 +268,7 @@ static int Open( vlc_object_t * p_this )
MP4_Box_t *p_rmra;
MP4_Box_t *p_mvhd;
MP4_Box_t *p_trak;
MP4_Box_t *p_chpl;
unsigned int i;
vlc_bool_t b_seekable;
......@@ -515,6 +521,21 @@ static int Open( vlc_object_t * p_this )
}
}
/* Process chapter if any */
if( ( p_chpl = MP4_BoxGet( p_sys->p_root, "/moov/udta/chpl" ) ) && p_chpl->data.p_chpl->i_chapter > 0 )
{
int i;
p_sys->p_title = vlc_input_title_New();
for( i = 0; i < p_chpl->data.p_chpl->i_chapter; i++ )
{
seekpoint_t *s = vlc_seekpoint_New();
s->psz_name = strdup( p_chpl->data.p_chpl->chapter[i].psz_name );
s->i_time_offset = p_chpl->data.p_chpl->chapter[i].i_start / 10;
TAB_APPEND( p_sys->p_title->i_seekpoint, p_sys->p_title->seekpoint, s );
}
}
return VLC_SUCCESS;
error:
......@@ -586,6 +607,9 @@ static int Demux( demux_t *p_demux )
return 0;
}
/* */
MP4_UpdateSeekpoint( p_demux );
/* first wait for the good time to read a packet */
es_out_Control( p_demux->out, ES_OUT_SET_PCR, p_sys->i_pcr + 1 );
......@@ -694,6 +718,28 @@ static int Demux( demux_t *p_demux )
return 1;
}
static void MP4_UpdateSeekpoint( demux_t *p_demux )
{
demux_sys_t *p_sys = p_demux->p_sys;
int64_t i_time;
int i;
if( !p_sys->p_title )
return;
i_time = MP4_GetMoviePTS( p_sys );
for( i = 0; i < p_sys->p_title->i_seekpoint; i++ )
{
if( i_time < p_sys->p_title->seekpoint[i]->i_time_offset )
break;
}
i--;
if( i != p_demux->info.i_seekpoint && i >= 0 )
{
p_demux->info.i_seekpoint = i;
p_demux->info.i_update |= INPUT_UPDATE_SEEKPOINT;
}
}
/*****************************************************************************
* Seek: Got to i_date
******************************************************************************/
......@@ -712,6 +758,7 @@ static int Seek( demux_t *p_demux, mtime_t i_date )
mp4_track_t *tk = &p_sys->track[i_track];
MP4_TrackSeek( p_demux, tk, i_date );
}
MP4_UpdateSeekpoint( p_demux );
return VLC_SUCCESS;
}
......@@ -798,43 +845,43 @@ static int Control( demux_t *p_demux, int i_query, va_list args )
for( p_0xa9xxx = p_udta->p_first; p_0xa9xxx != NULL;
p_0xa9xxx = p_0xa9xxx->p_next )
{
char *psz_utf;
if( !p_0xa9xxx || !p_0xa9xxx->data.p_0xa9xxx )
continue;
psz_utf = strdup( p_0xa9xxx->data.p_0xa9xxx->psz_text );
if( psz_utf == NULL )
continue;
/* FIXME FIXME: should convert from whatever the character
* encoding of MP4 meta data is to UTF-8. */
EnsureUTF8( psz_utf );
#define SET(fct) do { char *psz_utf = strdup( p_0xa9xxx->data.p_0xa9xxx->psz_text ? p_0xa9xxx->data.p_0xa9xxx->psz_text : "" ); \
if( psz_utf ) { EnsureUTF8( psz_utf ); \
fct( p_meta, psz_utf ); free( psz_utf ); } } while(0)
/* XXX Becarefull p_udta can have box that are not 0xa9xx */
switch( p_0xa9xxx->i_type )
{
case FOURCC_0xa9nam: /* Full name */
vlc_meta_SetTitle( p_meta, psz_utf );
SET( vlc_meta_SetTitle );
break;
case FOURCC_0xa9aut:
vlc_meta_SetArtist( p_meta, psz_utf );
SET( vlc_meta_SetArtist );
break;
case FOURCC_0xa9ART:
vlc_meta_SetArtist( p_meta, psz_utf );
SET( vlc_meta_SetArtist );
break;
case FOURCC_0xa9cpy:
vlc_meta_SetCopyright( p_meta, psz_utf );
SET( vlc_meta_SetCopyright );
break;
case FOURCC_0xa9day: /* Creation Date */
vlc_meta_SetDate( p_meta, psz_utf );
SET( vlc_meta_SetDate );
break;
case FOURCC_0xa9des: /* Description */
vlc_meta_SetDescription( p_meta, psz_utf );
SET( vlc_meta_SetDescription );
break;
case FOURCC_0xa9gen: /* Genre */
vlc_meta_SetGenre( p_meta, psz_utf );
SET( vlc_meta_SetGenre );
break;
case FOURCC_0xa9alb: /* Album */
vlc_meta_SetAlbum( p_meta, psz_utf );
SET( vlc_meta_SetAlbum );
break;
case FOURCC_0xa9swr:
......@@ -858,16 +905,47 @@ static int Control( demux_t *p_demux, int i_query, va_list args )
case FOURCC_WLOC: /* Window Location */
/* TODO one day, but they aren't really meaningfull */
break;
#undef SET
default:
break;
}
free( psz_utf );
}
return VLC_SUCCESS;
}
case DEMUX_GET_TITLE_INFO:
{
input_title_t ***ppp_title = (input_title_t***)va_arg( args, input_title_t*** );
int *pi_int = (int*)va_arg( args, int* );
int *pi_title_offset = (int*)va_arg( args, int* );
int *pi_seekpoint_offset = (int*)va_arg( args, int* );
if( !p_sys->p_title )
return VLC_EGENERIC;
*pi_int = 1;
*ppp_title = malloc( sizeof( input_title_t**) );
(*ppp_title)[0] = vlc_input_title_Duplicate( p_sys->p_title );
*pi_title_offset = 0;
*pi_seekpoint_offset = 0;
return VLC_SUCCESS;
}
case DEMUX_SET_TITLE:
{
const int i_title = (int)va_arg( args, int );
if( !p_sys->p_title || i_title != 0 )
return VLC_EGENERIC;
return VLC_SUCCESS;
}
case DEMUX_SET_SEEKPOINT:
{
const int i_seekpoint = (int)va_arg( args, int );
if( !p_sys->p_title )
return VLC_EGENERIC;
return Seek( p_demux, p_sys->p_title->seekpoint[i_seekpoint]->i_time_offset );
}
case DEMUX_SET_NEXT_DEMUX_TIME:
case DEMUX_SET_GROUP:
return VLC_EGENERIC;
......@@ -896,6 +974,9 @@ static void Close ( vlc_object_t * p_this )
}
FREENULL( p_sys->track );
if( p_sys->p_title )
vlc_input_title_Delete( p_sys->p_title );
free( p_sys );
}
......
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