Implemented GPAC chapter support (but not apple ones).
authorLaurent Aimar <fenrir@videolan.org>
Sun, 27 May 2007 16:18:08 +0000 (16:18 +0000)
committerLaurent Aimar <fenrir@videolan.org>
Sun, 27 May 2007 16:18:08 +0000 (16:18 +0000)
Fixed a few invalid access on broken files.
Fixed compilation warnings.

modules/demux/mp4/libmp4.c
modules/demux/mp4/libmp4.h
modules/demux/mp4/mp4.c

index 2e69773..60ce127 100644 (file)
  *  *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 },
 
index 950465b..148ea62 100644 (file)
 #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;
index 9da6621..17fec04 100644 (file)
@@ -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 );
 }