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

Frédéric Yhuel fyhuel at viotech.net
Fri Mar 16 15:23:32 CET 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 patchi, 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.
---
 include/vlc_stream.h         |    4 +
 modules/demux/mp4/Modules.am |    1 +
 modules/demux/mp4/fmp4.c     |  403 ++++++++++++++++++++++++++++++++++++++++++
 modules/demux/mp4/libmp4.c   |  133 ++++++++++++++
 modules/demux/mp4/libmp4.h   |   10 +
 modules/demux/mp4/mp4.c      |  103 +++++++++--
 6 files changed, 638 insertions(+), 16 deletions(-)
 create mode 100644 modules/demux/mp4/fmp4.c

diff --git a/include/vlc_stream.h b/include/vlc_stream.h
index c929836..dd366f4 100644
--- a/include/vlc_stream.h
+++ b/include/vlc_stream.h
@@ -111,6 +111,10 @@ enum stream_query_e
 
     /* XXX only data read through stream_Read/Block will be recorded */
     STREAM_SET_RECORD_STATE,     /**< arg1=bool, arg2=const char *psz_ext (if arg1 is true)  res=can fail */
+    STREAM_GET_TIME_SCALE,
+    STREAM_GET_ITRACKS,
+    STREAM_GET_TRACK,
+    STREAM_GET_FMT,
 };
 
 VLC_API int stream_Read( stream_t *s, void *p_read, int i_read );
diff --git a/modules/demux/mp4/Modules.am b/modules/demux/mp4/Modules.am
index 7c2c3d8..ab9f652 100644
--- a/modules/demux/mp4/Modules.am
+++ b/modules/demux/mp4/Modules.am
@@ -1,5 +1,6 @@
 SOURCES_mp4 = \
 	mp4.c \
+	fmp4.c \
 	libmp4.c \
 	libmp4.h \
 	mp4.h \
diff --git a/modules/demux/mp4/fmp4.c b/modules/demux/mp4/fmp4.c
new file mode 100644
index 0000000..5c0717b
--- /dev/null
+++ b/modules/demux/mp4/fmp4.c
@@ -0,0 +1,403 @@
+/*****************************************************************************
+ * fmp4.c : Demux() for fragmented MP4
+ *****************************************************************************
+ * Copyright (C) 1996-2012 VLC authors and VideoLAN
+ *
+ * Author: Frédéric Yhuel <fyhuel _AT_ viotech _DOT_ net>
+ *
+ * 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., 51 Franklin Street, Fifth Floor, Boston MA 02110-1301, USA.
+ *****************************************************************************/
+
+#ifdef HAVE_CONFIG_H
+# include "config.h"
+#endif
+
+#include <vlc_common.h>
+#include <assert.h>
+#include "mp4.h"
+
+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 )
+        {
+            return NULL;
+            msg_Err( p_demux, "fMP4_GetTrack: track %"PRIu16" not found!", tid );
+        }
+    }
+    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 )
+{
+    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;
+    }
+    MP4_Box_t *p_mdat = MP4_BoxGet( p_chunk, "mdat");
+    if( p_mdat == NULL)
+    {
+        msg_Warn( p_demux, "no mdat 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_traf == 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;
+    uint16_t tid = i_track_ID & 0xffff;
+    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 );
+    mp4_chunk_t *ret = p_track->cchunk;
+    if( ret->i_sample_count )
+        FreeChunk( ret );
+    ret->i_sample = 0;
+    ret->i_offset = p_moof->i_pos;
+
+    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_offset += p_trun_data->i_data_offset;
+    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;
+
+    stream_Read( p_demux->s, NULL, 8 );
+    for( uint32_t i = 0; i < ret->i_sample_count; i++)
+    {
+        if( default_duration == 0 )
+            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( default_size == 0 )
+            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;
+    //uint32_t chunk_bitrate = chunk_size * 8 * p_track->i_timescale / chunk_duration;
+
+    /* 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_Info( 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_Info( 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->cchunk = ret;
+    return VLC_SUCCESS;
+}
+
+static int fMP4_GetChunks( demux_t *p_demux )
+{
+    demux_sys_t *p_sys = p_demux->p_sys;
+
+    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 ) != VLC_SUCCESS )
+            return VLC_EGENERIC;
+    }
+
+    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 demux" );
+    demux_sys_t *p_sys = p_demux->p_sys;
+    unsigned int i_track;
+    unsigned int 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 200ms for each stream so ...*/
+    p_sys->i_time += __MAX( p_sys->i_timescale / 5, 1 );
+
+    bool b_all_chunks_done = true;
+    for( i_track = 0; i_track < p_sys->i_tracks; i_track++ )
+    {
+        mp4_track_t *tk = &p_sys->track[i_track];
+        if( !tk->b_end_of_chunk )
+            b_all_chunks_done = false;
+    }
+    if( b_all_chunks_done )
+    {
+        fMP4_GetChunks( p_demux );
+        for( i_track = 0; i_track < p_sys->i_tracks; i_track++ )
+        {
+            mp4_track_t *tk = &p_sys->track[i_track];
+            tk->b_end_of_chunk = false;
+        }
+    }
+    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 || tk->b_end_of_chunk )
+            continue;
+
+        while( MP4_TrackGetDTS( p_demux, tk ) < MP4_GetMoviePTS( p_sys ) )
+        {
+            if( tk->b_end_of_chunk )
+                break;
+#if 1
+            msg_Info( p_demux, "tk(%i)=%lld mv=%lld", i_track,
+                     MP4_TrackGetDTS( p_demux, tk ),
+                     MP4_GetMoviePTS( p_sys ) );
+#endif
+            block_t *p_block;
+            int64_t i_delta;
+
+            mp4_chunk_t *ck = tk->cchunk;
+            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++;
+        }
+    }
+
+    return 1;
+}
+
diff --git a/modules/demux/mp4/libmp4.c b/modules/demux/mp4/libmp4.c
index 5696176..ba92d86 100644
--- a/modules/demux/mp4/libmp4.c
+++ b/modules/demux/mp4/libmp4.c
@@ -3392,6 +3392,139 @@ void MP4_BoxFree( stream_t *s, MP4_Box_t *p_box )
     free( p_box );
 }
 
+MP4_Box_t *MP4_BoxGetInitFrag( stream_t *s )
+{
+    /* p_chunk is a virtual root container for the ftyp and moov boxes */
+    MP4_Box_t *p_chunk;
+    MP4_Box_t *p_ftyp;
+    MP4_Box_t *p_moov;
+    MP4_Box_t *p_trash;
+
+    p_chunk = malloc( sizeof( MP4_Box_t ) );
+    if( unlikely( p_chunk == NULL ) )
+        return NULL;
+
+    p_chunk->i_type = ATOM_root;
+    p_chunk->i_shortsize = 1;
+
+    p_chunk->data.p_data = NULL;
+    p_chunk->p_father    = NULL;
+    p_chunk->p_first     = NULL;
+    p_chunk->p_last      = NULL;
+    p_chunk->p_next      = NULL;
+
+    p_ftyp = MP4_ReadBox( s, p_chunk );
+    enum{ TRIES = 8 };
+    /* there may be some boxes between ftyp and moov,
+     * we skip them, but put a reasonable limit */
+    for( int i = 0 ; i < TRIES; i++ )
+    {
+        p_moov = MP4_ReadBox( s, p_chunk );
+        if( p_moov->i_type != ATOM_moov )
+        {
+            if( i == TRIES-1 )
+                return NULL;
+            stream_Read( s, NULL, p_moov->i_size );
+            MP4_BoxFree( s, p_moov );
+        }
+        else
+            break;
+    }
+
+    if( p_ftyp )
+    {
+        p_chunk->p_first = p_ftyp;
+        p_ftyp->p_next = p_moov;
+        p_chunk->p_last = p_moov;
+    }
+    else
+    {
+        msg_Warn( s, "no ftyp box found!");
+        return NULL;
+    }
+
+    return p_chunk;
+}
+
+MP4_Box_t *MP4_BoxGetNextChunk( stream_t *s )
+{
+    /* p_chunk is a virtual root container for the moof and mdat boxes */
+    MP4_Box_t *p_chunk;
+    MP4_Box_t *p_moof = NULL;
+    MP4_Box_t *p_mdat = NULL;
+    MP4_Box_t *p_sidx = NULL;
+
+    p_chunk = calloc( 1, sizeof( MP4_Box_t ) );
+    p_mdat = calloc( 1, sizeof( MP4_Box_t ) );
+    if( unlikely( p_chunk == NULL || p_mdat == NULL ) )
+        return NULL;
+
+    p_chunk->i_type = ATOM_root;
+    p_chunk->i_shortsize = 1;
+
+    enum{ TRIES = 8 };
+
+    /* there may be some boxes before moof,
+     * we skip them (but sidx) for now, but put a reasonable limit */
+    for( int i = 0 ; i < TRIES; i++ )
+    {
+        p_moof = MP4_ReadBox( s, p_chunk );
+        if( p_moof->i_type != ATOM_moof )
+        {
+            if( i == TRIES-1 )
+                return NULL;
+            if( p_moof->i_type != ATOM_sidx )
+            {
+                MP4_BoxFree( s, p_moof );
+                stream_Read( s, NULL, p_moof->i_size );
+            }
+            else
+                p_sidx = p_moof;
+        }
+        else
+            break;
+    }
+
+    /* there may be some boxes between moof and mdat,
+     * we skip them for now, but put a reasonable limit */
+    for( int i = 0 ; i < TRIES; i++ )
+    {
+        MP4_ReadBoxCommon( s, p_mdat );
+        if( p_mdat->i_type != ATOM_mdat )
+        {
+            if( i == TRIES-1 )
+                return NULL;
+            stream_Read( s, NULL, p_mdat->i_size );
+        }
+        else
+            break;
+    }
+
+    if( p_moof && p_mdat )
+    {
+        p_chunk->p_first = p_moof;
+        p_moof->p_next = p_mdat;
+        p_chunk->p_last = p_mdat;
+    }
+    else
+    {
+        msg_Warn( s, "no moof box found!");
+        MP4_BoxFree( s, p_chunk );
+        MP4_BoxFree( s, p_mdat );
+        MP4_BoxFree( s, p_moof );
+        MP4_BoxFree( s, p_sidx );
+        return NULL;
+    }
+    if( p_sidx )
+    {
+        p_chunk->p_first = p_sidx;
+        p_sidx->p_next = p_moof;
+    }
+
+    return p_chunk;
+}
+
+
 /*****************************************************************************
  * MP4_BoxGetRoot : Parse the entire file, and create all boxes in memory
  *****************************************************************************
diff --git a/modules/demux/mp4/libmp4.h b/modules/demux/mp4/libmp4.h
index 5b6b106..a33a8e4 100644
--- a/modules/demux/mp4/libmp4.h
+++ b/modules/demux/mp4/libmp4.h
@@ -1185,6 +1185,16 @@ typedef struct MP4_Box_s
 } MP4_Box_t;
 
 
+MP4_Box_t *MP4_BoxGetInitFrag( stream_t * );
+
+/*****************************************************************************
+ * MP4_BoxGetNextChunk : Parse the entire moof box.
+ *****************************************************************************
+ *  The first box is a virtual box "root".
+ *  if i_tk_id > 0, then seek to the next chunk, until
+ *  i_tk_id match the tfhd's id of the traf box.
+ *****************************************************************************/
+MP4_Box_t *MP4_BoxGetNextChunk( stream_t * );
 
 /*****************************************************************************
  * MP4_BoxGetRoot : Parse the entire file, and create all boxes in memory
diff --git a/modules/demux/mp4/mp4.c b/modules/demux/mp4/mp4.c
index d565254..ec98b7e 100644
--- a/modules/demux/mp4/mp4.c
+++ b/modules/demux/mp4/mp4.c
@@ -52,6 +52,7 @@ vlc_module_end ()
  *****************************************************************************/
 static int   Demux   ( demux_t * );
 static int   DemuxRef( demux_t *p_demux ){ (void)p_demux; return 0;}
+int          DemuxFrg( demux_t * );
 static int   Seek    ( demux_t *, mtime_t );
 static int   Control ( demux_t *, int, va_list );
 
@@ -114,7 +115,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)" );
@@ -128,11 +129,55 @@ 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_Info( 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 );
+        //p_sys->i_tracks = 1;
+        /* TODO duration? */
+        msg_Info(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 );
@@ -183,13 +228,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;
@@ -306,11 +346,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;
@@ -334,6 +398,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 )
         {
@@ -1485,12 +1550,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;
 
+    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;
+    }
         break;
 
     case AUDIO_ES:
@@ -2261,11 +2329,14 @@ static void MP4_TrackCreate( demux_t *p_demux, mp4_track_t *p_track,
         }
     }
 
-    /* Create chunk index table and sample index table */
-    if( TrackCreateChunksIndex( p_demux,p_track  ) ||
-        TrackCreateSamplesIndex( p_demux, p_track ) )
+    if( !p_sys->b_fragmented )
     {
-        return; /* cannot create chunks index */
+        /* Create chunk index table and sample index table */
+        if( TrackCreateChunksIndex( p_demux,p_track  ) ||
+            TrackCreateSamplesIndex( p_demux, p_track ) )
+        {
+            return; /* cannot create chunks index */
+        }
     }
 
     p_track->i_chunk  = 0;
-- 
1.7.5.4




More information about the vlc-devel mailing list