[vlc-devel] [PATCH 3/3] demux/mp4: Add fragmented MP4 support

Frédéric Yhuel fyhuel at viotech.net
Wed Apr 11 17:24:42 CEST 2012


Fragmented MP4 support is needed for DASH and Smooth Streaming.
This patch also enables resolution switches with Smooth Streaming, and I
plan to send another patch, to enable it also for DASH.

Note that an important difference between Smooth Streaming and DASH is
that DASH provides an initialization fragment, contrary to Smooth
Streaming, which provides the necesary information to init the decoder
directly in the Manifest file.
---
 modules/demux/mp4/mp4.c |  525 +++++++++++++++++++++++++++++++++++++++++++++--
 1 files changed, 504 insertions(+), 21 deletions(-)

diff --git a/modules/demux/mp4/mp4.c b/modules/demux/mp4/mp4.c
index 3f7c7cb..c864da0 100644
--- a/modules/demux/mp4/mp4.c
+++ b/modules/demux/mp4/mp4.c
@@ -35,6 +35,8 @@
 #include <vlc_charset.h>                           /* EnsureUTF8 */
 #include <vlc_meta.h>                              /* vlc_meta_t, vlc_meta_ */
 #include <vlc_input.h>
+#include <assert.h>
+#include <vlc_modules.h>
 
 #include "libmp4.h"
 #include "id3genres.h"                             /* for ATOM_gnre */
@@ -59,6 +61,7 @@ vlc_module_end ()
  *****************************************************************************/
 static int   Demux   ( demux_t * );
 static int   DemuxRef( demux_t *p_demux ){ (void)p_demux; return 0;}
+static int   DemuxFrg( demux_t * );
 static int   Seek    ( demux_t *, mtime_t );
 static int   Control ( demux_t *, int, va_list );
 
@@ -76,6 +79,10 @@ struct demux_sys_t
     mp4_track_t  *track;         /* array of track */
     float        f_fps;          /* number of frame per seconds */
 
+    bool         b_smooth;       /* Smooth Streaming => no moov box */
+    bool         b_dash;         /* DASH */
+    bool         b_fragmented;   /* fMP4 */
+
     /* */
     MP4_Box_t    *p_tref_chap;
 
@@ -105,9 +112,20 @@ static const char *MP4_ConvertMacCode( uint16_t );
 /* Return time in s of a track */
 static inline int64_t MP4_TrackGetDTS( demux_t *p_demux, mp4_track_t *p_track )
 {
-#define chunk p_track->chunk[p_track->i_chunk]
+    demux_sys_t *p_sys = p_demux->p_sys;
+    mp4_chunk_t chunk;
+    if( p_sys->b_fragmented )
+        chunk = *p_track->cchunk;
+    else
+        chunk = p_track->chunk[p_track->i_chunk];
 
     unsigned int i_index = 0;
+    if( p_track->i_sample < chunk.i_sample_first )
+    {
+        msg_Err( p_demux, "tk->i_sample is %u and ck->i_sample_first is %u",
+                p_track->i_sample, chunk.i_sample_first );
+    }
+    assert( p_track->i_sample >= chunk.i_sample_first );
     unsigned int i_sample = p_track->i_sample - chunk.i_sample_first;
     int64_t i_dts = chunk.i_first_dts;
 
@@ -127,8 +145,6 @@ static inline int64_t MP4_TrackGetDTS( demux_t *p_demux, mp4_track_t *p_track )
         }
     }
 
-#undef chunk
-
     /* now handle elst */
     if( p_track->p_elst )
     {
@@ -153,9 +169,15 @@ static inline int64_t MP4_TrackGetDTS( demux_t *p_demux, mp4_track_t *p_track )
     return INT64_C(1000000) * i_dts / p_track->i_timescale;
 }
 
-static inline int64_t MP4_TrackGetPTSDelta( mp4_track_t *p_track )
+static inline int64_t MP4_TrackGetPTSDelta( demux_t *p_demux, mp4_track_t *p_track )
 {
-    mp4_chunk_t *ck = &p_track->chunk[p_track->i_chunk];
+    demux_sys_t *p_sys = p_demux->p_sys;
+    mp4_chunk_t *ck;
+    if( p_sys->b_fragmented )
+        ck = p_track->cchunk;
+    else
+        ck = &p_track->chunk[p_track->i_chunk];
+
     unsigned int i_index = 0;
     unsigned int i_sample = p_track->i_sample - ck->i_sample_first;
 
@@ -179,6 +201,7 @@ static inline int64_t MP4_GetMoviePTS(demux_sys_t *p_sys )
 
 static void LoadChapter( demux_t  *p_demux );
 
+
 /*****************************************************************************
  * Open: check file and initializes MP4 structures
  *****************************************************************************/
@@ -219,7 +242,7 @@ static int Open( vlc_object_t * p_this )
     }
 
     /* I need to seek */
-    stream_Control( p_demux->s, STREAM_CAN_FASTSEEK, &b_seekable );
+    stream_Control( p_demux->s, STREAM_CAN_SEEK, &b_seekable );
     if( !b_seekable )
     {
         msg_Warn( p_demux, "MP4 plugin discarded (not fastseekable)" );
@@ -233,11 +256,54 @@ static int Open( vlc_object_t * p_this )
     /* create our structure that will contains all data */
     p_demux->p_sys = p_sys = calloc( 1, sizeof( demux_sys_t ) );
 
-    /* Now load all boxes ( except raw data ) */
-    if( ( p_sys->p_root = MP4_BoxGetRoot( p_demux->s ) ) == NULL )
+    p_sys->b_fragmented = false;
+    p_sys->b_smooth = false;
+    /* Is it smooth streaming ? */
+    char *parent_name = NULL;
+    if( p_demux->s->p_source && p_demux->s->p_source->p_module )
+        parent_name = (char *)module_get_name( p_demux->s->p_source->p_module, false );
+    if( parent_name && !strcmp( parent_name, "Smooth Streaming" ) )
     {
-        msg_Warn( p_demux, "MP4 plugin discarded (not a valid file)" );
-        goto error;
+        p_sys->b_smooth = true;
+        p_sys->b_fragmented = true;
+    }
+    if( parent_name && !strcmp( parent_name, "DASH" ) )
+    {
+        p_sys->b_dash = true;
+        p_sys->b_fragmented = true;
+    }
+    if( p_sys->b_fragmented )
+        p_demux->pf_demux = DemuxFrg;
+
+    if( p_sys->b_smooth )
+    {
+        msg_Dbg( p_demux, "Smooth Streaming" );
+        /* get TS, duration, and i_tracks */
+        stream_Control( p_demux->s, STREAM_GET_TIME_SCALE, &p_sys->i_timescale );
+        stream_Control( p_demux->s, STREAM_GET_ITRACKS, &p_sys->i_tracks );
+        /* TODO VOD duration? */
+        msg_Dbg( p_demux, "There are %u tracks!", p_sys->i_tracks );
+        goto allocate_memory;
+    }
+
+    if( p_sys->b_fragmented )
+    {
+        /* Now load init segment */
+        if( ( p_sys->p_root = MP4_BoxGetInitFrag( p_demux->s ) ) == NULL )
+        {
+            msg_Warn( p_demux,
+                    "MP4 plugin discarded (not a valid initilization fragment)" );
+            goto error;
+        }
+    }
+    else
+    {
+        /* Now load all boxes ( except raw data ) */
+        if( ( p_sys->p_root = MP4_BoxGetRoot( p_demux->s ) ) == NULL )
+        {
+            msg_Warn( p_demux, "MP4 plugin discarded (not a valid file)" );
+            goto error;
+        }
     }
 
     MP4_BoxDumpStructure( p_demux->s, p_sys->p_root );
@@ -288,13 +354,8 @@ static int Open( vlc_object_t * p_this )
 
         if( !p_foov )
         {
-            /* search also for moof box used by smoothstreaming */
-            p_foov = MP4_BoxGet( p_sys->p_root, "/moof" );
-            if( !p_foov )
-            {
-                msg_Err( p_demux, "MP4 plugin discarded (no moov,foov,moof box)" );
-                goto error;
-            }
+            msg_Err( p_demux, "MP4 plugin discarded (no moov,foov,moof box)" );
+            goto error;
         }
         /* we have a free box as a moov, rename it */
         p_foov->i_type = ATOM_moov;
@@ -411,11 +472,35 @@ static int Open( vlc_object_t * p_this )
                         p_sys->i_tracks,
                         p_sys->i_tracks ? 's':' ' );
 
+allocate_memory:
     /* allocate memory */
     p_sys->track = calloc( p_sys->i_tracks, sizeof( mp4_track_t ) );
     if( p_sys->track == NULL )
         goto error;
+    if( p_sys->b_fragmented )
+    {
+        mp4_track_t *p_track;
+        for( uint16_t i = 0; i < p_sys->i_tracks; i++ )
+        {
+            p_track = &p_sys->track[i];
+            p_track->cchunk = calloc( 1, sizeof( mp4_chunk_t ) );
+        }
+    }
 
+    if( p_sys->b_smooth )
+    {
+        /*track create */
+        mp4_track_t *p_track;
+
+        for( uint16_t i = 0; i < p_sys->i_tracks; i++ )
+        {
+            p_track = &p_sys->track[i];
+            p_track->b_end_of_chunk = true;
+            stream_Control( p_demux->s, STREAM_GET_TRACK, i+1, p_track );
+            p_track->p_es = es_out_Add( p_demux->out, &p_track->fmt );
+        }
+        return VLC_SUCCESS;
+    }
     /* Search the first chap reference (like quicktime) and
      * check that at least 1 stream is enabled */
     p_sys->p_tref_chap = NULL;
@@ -439,6 +524,7 @@ static int Open( vlc_object_t * p_this )
     {
         p_trak = MP4_BoxGet( p_sys->p_root, "/moov/trak[%d]", i );
         MP4_TrackCreate( p_demux, &p_sys->track[i], p_trak, !b_enabled_es );
+        p_sys->track[i].b_end_of_chunk = true;
 
         if( p_sys->track[i].b_ok && !p_sys->track[i].b_chapter )
         {
@@ -636,7 +722,7 @@ static int Demux( demux_t *p_demux )
                 /* dts */
                 p_block->i_dts = VLC_TS_0 + MP4_TrackGetDTS( p_demux, tk );
                 /* pts */
-                i_delta = MP4_TrackGetPTSDelta( tk );
+                i_delta = MP4_TrackGetPTSDelta( p_demux, tk );
                 if( i_delta != -1 )
                     p_block->i_pts = p_block->i_dts + i_delta;
                 else if( tk->fmt.i_cat != VIDEO_ES )
@@ -999,7 +1085,7 @@ static void LoadChapterApple( demux_t  *p_demux, mp4_track_t *tk )
     for( tk->i_sample = 0; tk->i_sample < tk->i_sample_count; tk->i_sample++ )
     {
         const int64_t i_dts = MP4_TrackGetDTS( p_demux, tk );
-        const int64_t i_pts_delta = MP4_TrackGetPTSDelta( tk );
+        const int64_t i_pts_delta = MP4_TrackGetPTSDelta( p_demux, tk );
         const unsigned int i_size = MP4_TrackSampleSize( tk );
 
         if( i_size > 0 && !stream_Seek( p_demux->s, MP4_TrackGetPos( tk ) ) )
@@ -1071,6 +1157,10 @@ static void LoadChapter( demux_t  *p_demux )
 static int TrackCreateChunksIndex( demux_t *p_demux,
                                    mp4_track_t *p_demux_track )
 {
+    demux_sys_t *p_sys = p_demux->p_sys;
+    if( p_sys->b_fragmented )
+        return VLC_SUCCESS;
+
     MP4_Box_t *p_co64; /* give offset for each chunk, same for stco and co64 */
     MP4_Box_t *p_stsc;
 
@@ -1158,6 +1248,10 @@ static int TrackCreateChunksIndex( demux_t *p_demux,
 static int TrackCreateSamplesIndex( demux_t *p_demux,
                                     mp4_track_t *p_demux_track )
 {
+    demux_sys_t *p_sys = p_demux->p_sys;
+    if( p_sys->b_fragmented )
+        return VLC_SUCCESS;
+
     MP4_Box_t *p_box;
     MP4_Box_data_stsz_t *stsz;
     MP4_Box_data_stts_t *stts;
@@ -1407,8 +1501,15 @@ static void TrackGetESSampleRate( unsigned *pi_num, unsigned *pi_den,
 static int TrackCreateES( demux_t *p_demux, mp4_track_t *p_track,
                           unsigned int i_chunk, es_out_id_t **pp_es )
 {
-    const unsigned i_sample_description_index =
-        p_track->chunk[i_chunk].i_sample_description_index;
+    demux_sys_t *p_sys = p_demux->p_sys;
+    unsigned int i_sample_description_index;
+
+    if( p_sys->b_fragmented )
+        i_sample_description_index = 1; /* XXX */
+    else
+        i_sample_description_index =
+                p_track->chunk[i_chunk].i_sample_description_index;
+
     MP4_Box_t   *p_sample;
     MP4_Box_t   *p_esds;
     MP4_Box_t   *p_frma;
@@ -1485,12 +1586,15 @@ static int TrackCreateES( demux_t *p_demux, mp4_track_t *p_track,
         p_track->fmt.video.i_visible_width = p_track->fmt.video.i_width;
         p_track->fmt.video.i_visible_height = p_track->fmt.video.i_height;
 
+        /* TODO: Set frame rate for DASH and Smooth Streaming */
+        if( !p_sys->b_fragmented ) {
         /* Frame rate */
         TrackGetESSampleRate( &p_track->fmt.video.i_frame_rate,
                               &p_track->fmt.video.i_frame_rate_base,
                               p_track, i_sample_description_index, i_chunk );
         p_demux->p_sys->f_fps = (float)p_track->fmt.video.i_frame_rate /
                                 (float)p_track->fmt.video.i_frame_rate_base;
+        }
 
         /* Rotation */
         switch( (int)p_track->f_rotation ) {
@@ -2680,6 +2784,7 @@ static int MP4_TrackNextSample( demux_t *p_demux, mp4_track_t *p_track )
         return VLC_EGENERIC;
 
     /* Have we changed chunk ? */
+
     if( p_track->i_sample >=
             p_track->chunk[p_track->i_chunk].i_sample_first +
             p_track->chunk[p_track->i_chunk].i_sample_count )
@@ -2798,3 +2903,381 @@ static const char *MP4_ConvertMacCode( uint16_t i_code )
     }
     return "";
 }
+
+/******************************************************************************
+ *     Here are the functions used for fragmented MP4
+ *****************************************************************************/
+
+static mp4_track_t *fMP4_GetTrack( demux_t *p_demux, uint16_t tid )
+{
+    demux_sys_t *p_sys = p_demux->p_sys;
+
+    mp4_track_t *ret = NULL;
+    for( unsigned i = 0; i < p_sys->i_tracks; i++ )
+    {
+        ret = &p_sys->track[i];
+        if( ret->i_track_ID == tid )
+            break;
+        if( i == p_sys->i_tracks - 1 )
+        {
+            msg_Err( p_demux, "fMP4_GetTrack: track %"PRIu16" not found!", tid );
+            return NULL;
+        }
+    }
+    return ret;
+}
+
+static int FreeChunk( mp4_chunk_t *ck )
+{
+    free( ck->p_sample_count_dts );
+    free( ck->p_sample_delta_dts );
+    free( ck->p_sample_count_pts );
+    free( ck->p_sample_offset_pts );
+    free( ck->p_sample_size );
+    for( uint32_t i = 0; i < ck->i_sample_count; i++ )
+        free( ck->p_sample_data[i] );
+    free( ck->p_sample_data );
+    memset( ck, 0, sizeof( mp4_chunk_t ) );
+    return VLC_SUCCESS;
+}
+
+static int fMP4_GetChunk( demux_t *p_demux, MP4_Box_t *p_chunk, unsigned *i_tk_id )
+{
+    demux_sys_t *p_sys = p_demux->p_sys;
+    MP4_Box_t *p_sidx = MP4_BoxGet( p_chunk, "sidx" );
+    MP4_Box_t *p_moof = MP4_BoxGet( p_chunk, "moof" );
+    if( p_moof == NULL)
+    {
+        msg_Warn( p_demux, "no moof box found!" );
+        return VLC_EGENERIC;
+    }
+
+    /* There is only one traf per moof in fMP4 */
+    MP4_Box_t *p_traf = MP4_BoxGet( p_moof, "traf" );
+    if( p_traf == NULL)
+    {
+        msg_Warn( p_demux, "no traf box found!" );
+        return VLC_EGENERIC;
+    }
+    MP4_Box_t *p_tfhd = MP4_BoxGet( p_traf, "tfhd" );
+    if( p_tfhd == NULL)
+    {
+        msg_Warn( p_demux, "no tfhd box found!" );
+        return VLC_EGENERIC;
+    }
+    uint32_t i_track_ID = p_tfhd->data.p_tfhd->i_track_ID;
+    /* In Smooth Streaming we use the 16 most significant bits
+     * to store a "quality level ID". */
+    uint16_t tid = i_track_ID & 0xffff;
+    *i_tk_id = tid;
+    uint16_t qid = (i_track_ID & 0xffff0000)>>16;
+    assert( tid > 0 );
+    msg_Dbg( p_demux, "GetChunk: track ID is %"PRIu16"", tid );
+    msg_Dbg( p_demux, "GetChunk: qid is %"PRIu16"", qid );
+    mp4_track_t *p_track = fMP4_GetTrack( p_demux, tid );
+    if( !p_track )
+        return VLC_EGENERIC;
+    mp4_chunk_t *ret = p_track->cchunk;
+
+    if( p_tfhd->data.p_tfhd->b_empty )
+    {
+        msg_Warn( p_demux, "No samples in this chunk!" );
+    }
+
+    if( ret->i_sample_count )
+        FreeChunk( ret );
+
+    uint32_t default_duration = 0;
+    if( p_tfhd->data.p_tfhd->i_flags & MP4_TFHD_DFLT_SAMPLE_DURATION )
+        default_duration = p_tfhd->data.p_tfhd->i_default_sample_duration;
+
+    uint32_t default_size = 0;
+    if( p_tfhd->data.p_tfhd->i_flags & MP4_TFHD_DFLT_SAMPLE_SIZE )
+        default_size = p_tfhd->data.p_tfhd->i_default_sample_size;
+
+    MP4_Box_t *p_trun = MP4_BoxGet( p_traf, "trun");
+    if( p_trun == NULL)
+    {
+        msg_Warn( p_demux, "no trun box found!" );
+        return VLC_EGENERIC;
+    }
+    MP4_Box_data_trun_t *p_trun_data = p_trun->data.p_trun;
+
+    ret->i_sample_count = p_trun_data->i_sample_count;
+    assert( ret->i_sample_count > 0 );
+    ret->i_sample_description_index = 1; /* FIXME */
+    ret->i_sample_first = p_track->i_sample_first;
+    p_track->i_sample_first += ret->i_sample_count;
+
+    ret->i_first_dts = p_track->i_first_dts;
+
+    /* temporary hack for DASH */
+    /* you may edit this value (look at the MPD for segment duration */
+    enum{ DASH_FRAG_DURATION = 4 };
+    if( p_sys->b_dash && !default_duration )
+    {
+        if( p_sidx )
+        {
+            MP4_Box_data_sidx_t *p_sidx_data = p_sidx->data.p_sidx;
+            assert( p_sidx_data->i_reference_count == 1 );
+            unsigned i_chunk_duration = p_sidx_data->p_items[0].i_subsegment_duration /
+                                                       p_sidx_data->i_timescale;
+            default_duration = i_chunk_duration *
+                p_track->i_timescale / ret->i_sample_count;
+        }
+        else
+        {
+            default_duration = DASH_FRAG_DURATION *
+                p_track->i_timescale / ret->i_sample_count;
+        }
+    }
+
+    msg_Dbg( p_demux, "Default sample duration is %"PRIu32, default_duration );
+
+    ret->p_sample_count_dts = calloc( ret->i_sample_count, sizeof( uint32_t ) );
+    ret->p_sample_delta_dts = calloc( ret->i_sample_count, sizeof( uint32_t ) );
+
+    if( !ret->p_sample_count_dts || !ret->p_sample_delta_dts )
+        return VLC_ENOMEM;
+
+    ret->p_sample_count_pts = calloc( ret->i_sample_count, sizeof( uint32_t ) );
+    ret->p_sample_offset_pts = calloc( ret->i_sample_count, sizeof( int32_t ) );
+
+    if( !ret->p_sample_count_pts || !ret->p_sample_offset_pts )
+        return VLC_ENOMEM;
+
+    ret->p_sample_size = calloc( ret->i_sample_count, sizeof( uint32_t ) );
+    if( !ret->p_sample_size )
+        return VLC_ENOMEM;
+
+    ret->p_sample_data = calloc( ret->i_sample_count, sizeof( uint8_t * ) );
+    if( !ret->p_sample_data )
+        return VLC_ENOMEM;
+
+    uint32_t dur = 0, len;
+    uint32_t chunk_duration = 0;
+    uint32_t chunk_size = 0;
+
+    /* Skip header of mdat */
+    stream_Read( p_demux->s, NULL, 8 );
+
+    for( uint32_t i = 0; i < ret->i_sample_count; i++)
+    {
+        if( p_trun_data->i_flags & MP4_TRUN_SAMPLE_DURATION )
+            dur = p_trun_data->p_samples[i].i_duration;
+        else
+            dur = default_duration;
+        ret->p_sample_delta_dts[i] = dur;
+        chunk_duration += dur;
+        ret->p_sample_count_dts[i] = 1;
+        ret->p_sample_count_pts[i] = 1;
+        if( p_trun_data->i_flags & MP4_TRUN_SAMPLE_TIME_OFFSET )
+            ret->p_sample_offset_pts[i] =
+                        p_trun_data->p_samples[i].i_composition_time_offset;
+        else
+            ret->p_sample_offset_pts[i] = 0;
+        if( p_trun_data->i_flags & MP4_TRUN_SAMPLE_SIZE )
+            len = ret->p_sample_size[i] = p_trun_data->p_samples[i].i_size;
+        else
+            len = ret->p_sample_size[i] = default_size;
+        ret->p_sample_data[i] = malloc( len );
+        int read = stream_Read( p_demux->s, ret->p_sample_data[i], len );
+        if( read < (int)len )
+            return VLC_EGENERIC;
+        else
+            msg_Dbg( p_demux, "GetChunk: read a sample of size %"PRIu32"", len );
+        chunk_size += len;
+    }
+    ret->i_last_dts = chunk_duration - dur;
+    p_track->i_first_dts = chunk_duration + ret->i_first_dts;
+
+    /* Warning! If we do a es_out_Del / es_out_Add too soon,
+     * before the track has been selected by MP4_TrackSelect
+     * (during the first execution of Demux), then the track gets disabled */
+    if( qid != p_track->current_qid && p_sys->b_smooth )
+    {
+        if( p_track->fmt.i_cat == VIDEO_ES )
+        {
+            msg_Dbg( p_demux, "Adaptation! previous resolution was %i x %i",
+                            p_track->i_width, p_track->i_height );
+            es_out_Del( p_demux->out, p_track->p_es );
+            stream_Control( p_demux->s, STREAM_GET_FMT, tid, qid, &p_track->fmt );
+            p_track->current_qid = qid;
+            p_track->i_width = p_track->fmt.video.i_width;
+            p_track->i_height = p_track->fmt.video.i_height;
+            msg_Dbg( p_demux, "New resolution is %i x %i",
+                            p_track->i_width, p_track->i_height );
+            p_track->p_es = es_out_Add( p_demux->out, &p_track->fmt );
+        }
+    }
+
+    p_track->b_end_of_chunk = false;
+    return VLC_SUCCESS;
+}
+
+/* Get the next chunk of the track identified by i_tk_id.
+ * We don't want to seek all the time, so if the first chunk given by the
+ * input method doesn't belong to the right track, we don't throw it away,
+ * and so, in general, this function fetch more than one chunk */
+static int fMP4_GetChunks( demux_t *p_demux, unsigned i_tk_id )
+{
+    demux_sys_t *p_sys = p_demux->p_sys;
+    unsigned tid;
+
+    for( unsigned i = 0; i < p_sys->i_tracks; i++ )
+    {
+        MP4_Box_t *p_chunk = MP4_BoxGetNextChunk( p_demux->s );
+        if( !p_chunk )
+            return VLC_EGENERIC;
+        if( fMP4_GetChunk( p_demux, p_chunk, &tid ) != VLC_SUCCESS )
+            return VLC_EGENERIC;
+        if( tid == i_tk_id )
+            break;
+    }
+
+    return VLC_SUCCESS;
+}
+
+static int fMP4_TrackSelect( demux_t *p_demux, mp4_track_t *p_track )
+{
+    if( !p_track->b_ok || p_track->b_chapter )
+    {
+        return VLC_EGENERIC;
+    }
+
+    if( p_track->b_selected )
+    {
+        msg_Warn( p_demux, "track[Id 0x%x] already selected",
+                  p_track->i_track_ID );
+        return VLC_SUCCESS;
+    }
+
+    msg_Dbg( p_demux, "Select track id %u", p_track->i_track_ID );
+    p_track->b_selected = true;
+    return VLC_SUCCESS;
+}
+
+/*****************************************************************************
+ * DemuxFrg: read packet and send them to decoders
+ *****************************************************************************
+ * TODO check for newly selected track
+ *****************************************************************************/
+int DemuxFrg( demux_t *p_demux )
+{
+    msg_Dbg( p_demux, "Entering DemuxFrag" );
+    demux_sys_t *p_sys = p_demux->p_sys;
+    unsigned i_track;
+    unsigned i_track_selected;
+
+    /* check for newly selected/unselected track */
+    for( i_track = 0, i_track_selected = 0; i_track < p_sys->i_tracks;
+         i_track++ )
+    {
+        mp4_track_t *tk = &p_sys->track[i_track];
+        bool b;
+
+        if( !tk->b_ok || tk->b_chapter )
+        {
+            continue;
+        }
+
+        es_out_Control( p_demux->out, ES_OUT_GET_ES_STATE, tk->p_es, &b );
+        msg_Dbg( p_demux, "track %u %s!", tk->i_track_ID, b ? "enabled" : "disabled" );
+
+        if( tk->b_selected && !b )
+        {
+            MP4_TrackUnselect( p_demux, tk );
+        }
+        else if( !tk->b_selected && b)
+        {
+            fMP4_TrackSelect( p_demux, tk );
+        }
+
+        if( tk->b_selected )
+        {
+            i_track_selected++;
+        }
+    }
+
+    if( i_track_selected <= 0 )
+    {
+        p_sys->i_time += __MAX( p_sys->i_timescale / 10 , 1 );
+        if( p_sys->i_timescale > 0 )
+        {
+            int64_t i_length = (mtime_t)1000000 *
+                               (mtime_t)p_sys->i_duration /
+                               (mtime_t)p_sys->i_timescale;
+            if( MP4_GetMoviePTS( p_sys ) >= i_length )
+                return 0;
+            return 1;
+        }
+
+        msg_Warn( p_demux, "no track selected, exiting..." );
+        return 0;
+    }
+
+    /* first wait for the good time to read a packet */
+    es_out_Control( p_demux->out, ES_OUT_SET_PCR, VLC_TS_0 + p_sys->i_pcr );
+
+    p_sys->i_pcr = MP4_GetMoviePTS( p_sys );
+
+    /* we will read 100ms for each stream so ...*/
+    p_sys->i_time += __MAX( p_sys->i_timescale / 10, 1 );
+
+    for( i_track = 0; i_track < p_sys->i_tracks; i_track++ )
+    {
+        mp4_track_t *tk = &p_sys->track[i_track];
+
+        if( !tk->b_ok || tk->b_chapter || !tk->b_selected )
+            continue;
+        if( tk->b_end_of_chunk )
+        {
+            if( fMP4_GetChunks( p_demux, tk->i_track_ID ) != VLC_SUCCESS )
+                return 0;
+        }
+
+        while( MP4_TrackGetDTS( p_demux, tk ) < MP4_GetMoviePTS( p_sys ) )
+        {
+            msg_Dbg( p_demux, "tk(%i)=%lld mv=%lld", i_track,
+                     MP4_TrackGetDTS( p_demux, tk ),
+                     MP4_GetMoviePTS( p_sys ) );
+            block_t *p_block;
+            int64_t i_delta;
+
+            mp4_chunk_t *ck = tk->cchunk;
+            if( ck->i_sample >= ck->i_sample_count )
+            {
+                msg_Err( p_demux, "sample %"PRIu32" of %"PRIu32"",
+                                    ck->i_sample, ck->i_sample_count );
+            }
+            assert( ck->i_sample < ck->i_sample_count );
+            uint32_t sample_size = ck->p_sample_size[ck->i_sample];
+            assert( sample_size > 0 );
+            p_block = block_Alloc( sample_size );
+            uint8_t *src = ck->p_sample_data[ck->i_sample];
+            memcpy( p_block->p_buffer, src, sample_size );
+            ck->i_sample++;
+            if( ck->i_sample == ck->i_sample_count )
+                tk->b_end_of_chunk = true;
+
+            /* dts */
+            p_block->i_dts = VLC_TS_0 + MP4_TrackGetDTS( p_demux, tk );
+            /* pts */
+            i_delta = MP4_TrackGetPTSDelta( p_demux, tk );
+            if( i_delta != -1 )
+                p_block->i_pts = p_block->i_dts + i_delta;
+            else if( tk->fmt.i_cat != VIDEO_ES )
+                p_block->i_pts = p_block->i_dts;
+            else
+                p_block->i_pts = VLC_TS_INVALID;
+
+            es_out_Send( p_demux->out, tk->p_es, p_block );
+
+            tk->i_sample++;
+            if( tk->b_end_of_chunk )
+                break;
+        }
+    }
+
+    return 1;
+}
-- 
1.7.5.4




More information about the vlc-devel mailing list