[vlc-devel] [PATCH] Smooth Streaming: clean / factorize code

Frédéric Yhuel yhuelf at gmail.com
Thu Oct 4 13:12:55 CEST 2012


and prepare subtitle support
---
 modules/stream_filter/smooth/downloader.c |  193 +++++++++++------------------
 modules/stream_filter/smooth/smooth.c     |   49 +++-----
 modules/stream_filter/smooth/smooth.h     |   26 ++--
 modules/stream_filter/smooth/utils.c      |   60 +++++++++
 4 files changed, 163 insertions(+), 165 deletions(-)

diff --git a/modules/stream_filter/smooth/downloader.c b/modules/stream_filter/smooth/downloader.c
index 8f6582e..79a4a83 100644
--- a/modules/stream_filter/smooth/downloader.c
+++ b/modules/stream_filter/smooth/downloader.c
@@ -140,14 +140,8 @@ static int sms_Download( stream_t *s, chunk_t *chunk, char *url )
     stream_Delete( p_ts );
 
     vlc_mutex_lock( &p_sys->download.lock_wait );
-
-    if( chunk->type == AUDIO_ES )
-        p_sys->download.alead += chunk->duration;
-    else if( chunk->type == VIDEO_ES )
-        p_sys->download.vlead += chunk->duration;
-    else if( chunk->type == SPU_ES )
-        p_sys->download.tlead += chunk->duration;
-
+    int index = es_cat_to_index( chunk->type );
+    p_sys->download.lead[index] += chunk->duration;
     vlc_mutex_unlock( &p_sys->download.lock_wait );
 
     return VLC_SUCCESS;
@@ -205,12 +199,7 @@ static int get_new_chunks( stream_t *s, chunk_t *ck )
     UUID_t uuid;
     TfrfBoxDataFields_t *tfrf_df;
 
-    if( ck->type == AUDIO_ES )
-        sms = p_sys->astream;
-    else if ( ck->type == VIDEO_ES )
-        sms = p_sys->vstream;
-    else
-        return 0;
+    sms = SMS_GET_SELECTED_ST( ck->type );
 
     SMS_GET4BYTES( size );
     SMS_GETFOURCC( type );
@@ -307,6 +296,7 @@ static int build_smoo_box( stream_t *s, uint8_t *smoo_box )
     for( int i = 0; i < 3; i++ )
     {
         sms = NULL;
+        int cat = UNKNOWN_ES;
         stra_box = smoo_box + i * STRA_SIZE;
 
         stra_box[26] = (STRA_SIZE & 0xff00)>>8;
@@ -322,21 +312,9 @@ static int build_smoo_box( stream_t *s, uint8_t *smoo_box )
         ((uint32_t *)stra_box)[10] = bswap32( 0x96c7bf25 );
         ((uint32_t *)stra_box)[11] = bswap32( 0xf97e2447 );
 
-        if( i == 0)
-        {
-            stra_box[48] = VIDEO_ES;
-            sms = p_sys->vstream;
-        }
-        else if( i == 1 )
-        {
-            stra_box[48] = AUDIO_ES;
-            sms = p_sys->astream;
-        }
-        else if( i == 2 )
-        {
-            stra_box[48] = SPU_ES;
-            sms = p_sys->tstream;
-        }
+        cat = index_to_es_cat( i );
+        stra_box[48] = cat;
+        sms = SMS_GET_SELECTED_ST( cat );
 
         stra_box[49] = 0; /* reserved */
         if( sms == NULL )
@@ -359,6 +337,8 @@ static int build_smoo_box( stream_t *s, uint8_t *smoo_box )
         ((uint32_t *)stra_box)[23] = bswap32( qlvl->AudioTag );
         ((uint16_t *)stra_box)[48] = bswap16( qlvl->nBlockAlign );
 
+        if( !qlvl->CodecPrivateData )
+            continue;
         stra_box[98] = stra_box[99] = stra_box[100] = 0; /* reserved */
         assert( strlen( qlvl->CodecPrivateData ) < 512 );
         stra_box[101] = strlen( qlvl->CodecPrivateData ) / 2;
@@ -396,16 +376,8 @@ static int Download( stream_t *s, sms_stream_t *sms )
 {
     stream_sys_t *p_sys = s->p_sys;
 
-    int64_t start_time;
-
-    if( sms->type == AUDIO_ES )
-        start_time = p_sys->download.alead;
-    else if ( sms->type == VIDEO_ES )
-        start_time = p_sys->download.vlead;
-    else
-    {
-        return VLC_EGENERIC;
-    }
+    int index = es_cat_to_index( sms->type );
+    int64_t start_time = p_sys->download.lead[index];
 
     quality_level_t *qlevel = get_qlevel( sms, sms->download_qlvl );
     if( unlikely( !qlevel ) )
@@ -485,21 +457,15 @@ static int Download( stream_t *s, sms_stream_t *sms )
                 chunk->sequence, sms->name, qlevel->Bitrate );
 
     uint64_t actual_lead = chunk->start_time + chunk->duration;
-    if( sms->type == AUDIO_ES )
-    {
-        p_sys->download.aindex = chunk->sequence;
-        p_sys->download.alead = __MIN( p_sys->download.alead, actual_lead );
-    }
-    else if( sms->type == VIDEO_ES )
-    {
-        p_sys->download.vindex = chunk->sequence;
-        p_sys->download.vlead = __MIN( p_sys->download.vlead, actual_lead );
-        p_sys->playback.toffset = __MIN( p_sys->playback.toffset, (uint64_t)chunk->start_time );
-    }
-    else if( sms->type == SPU_ES )
+    int ind = es_cat_to_index( sms->type );
+    p_sys->download.ck_index[ind] = chunk->sequence;
+    p_sys->download.lead[ind] = __MIN( p_sys->download.lead[ind], actual_lead );
+
+    if( sms->type == VIDEO_ES ||
+            ( !SMS_GET_SELECTED_ST( VIDEO_ES ) && sms->type == AUDIO_ES ) )
     {
-        p_sys->download.sindex = chunk->sequence;
-        p_sys->download.tlead = __MIN( p_sys->download.tlead, actual_lead );
+        p_sys->playback.toffset = __MIN( p_sys->playback.toffset,
+                                            (uint64_t)chunk->start_time );
     }
 
     unsigned dur_ms = __MAX( 1, duration / 1000 );
@@ -555,30 +521,49 @@ static inline int64_t get_lead( stream_t *s )
 {
     stream_sys_t *p_sys = s->p_sys;
     int64_t lead = 0;
-
-    if( p_sys->vstream && p_sys->astream )
-        lead = __MIN( p_sys->download.vlead, p_sys->download.alead );
-    else if( p_sys->vstream )
-        lead = p_sys->download.vlead;
+    int64_t alead = p_sys->download.lead[es_cat_to_index( AUDIO_ES )];
+    int64_t vlead = p_sys->download.lead[es_cat_to_index( VIDEO_ES )];
+    bool video = SMS_GET_SELECTED_ST( VIDEO_ES ) ? true : false;
+    bool audio = SMS_GET_SELECTED_ST( AUDIO_ES ) ? true : false;
+
+    if( video && audio )
+        lead = __MIN( vlead, alead );
+    else if( video )
+        lead = vlead;
     else
-        lead = p_sys->download.alead;
+        lead = alead;
 
     lead -= p_sys->playback.toffset;
     return lead;
 }
 
+static int who_s_turn( stream_t *s )
+{
+    stream_sys_t *p_sys = s->p_sys;
+    uint64_t tmp, min = 0;
+    int cat, ret = UNKNOWN_ES;
+    for( int i = 0; i < 3; i++ )
+    {
+        tmp = p_sys->download.lead[i];
+        cat = index_to_es_cat( i );
+        if( (!min || tmp < min) && SMS_GET_SELECTED_ST( cat ) )
+        {
+            min = tmp;
+            ret = cat;
+        }
+    }
+    return ret;
+}
+
 void* sms_Thread( void *p_this )
 {
     stream_t *s = (stream_t *)p_this;
     stream_sys_t *p_sys = s->p_sys;
+    sms_stream_t *sms = NULL;
+    chunk_t *chunk;
 
     int canc = vlc_savecancel();
 
-    sms_stream_t *vsms = p_sys->vstream;
-    sms_stream_t *asms = p_sys->astream;
-    if( !vsms && !asms )
-        goto cancel;
-
     /* We compute the average bandwidth of the 4 last downloaded
      * chunks, but feel free to replace '4' by whatever you wish */
     p_sys->bws = sms_queue_init( 4 );
@@ -595,57 +580,31 @@ void* sms_Thread( void *p_this )
 
     p_sys->download.next_chunk_offset = init_ck->size;
 
-    chunk_t *audio_chunk, *video_chunk;
-    if( vsms )
-        video_chunk = vlc_array_item_at_index( vsms->chunks, 0 );
-    if( asms )
-        audio_chunk = vlc_array_item_at_index( asms->chunks, 0 );
-    if( (vsms && !video_chunk) || (asms && !audio_chunk) )
-        goto cancel;
-
-    /* Sometimes, the video stream is cut into pieces of one exact length,
+    /* XXX Sometimes, the video stream is cut into pieces of one exact length,
      * while the audio stream fragments can't be made to match exactly,
      * and for some reason the n^th advertised video fragment is related to
      * the n+1^th advertised audio chunk or vice versa */
-    if( asms && vsms )
-    {
-        int64_t amid, vmid;
-        amid = audio_chunk->duration / 2;
-        vmid = video_chunk->duration / 2;
 
-        if( audio_chunk->start_time > video_chunk->start_time + vmid )
-        {
-            video_chunk = vlc_array_item_at_index( vsms->chunks, 1 );
-        }
-        else if( video_chunk->start_time > audio_chunk->start_time + amid )
-        {
-            audio_chunk = vlc_array_item_at_index( asms->chunks, 1 );
-        }
-    }
+    int64_t start_time = 0, lead = 0;
 
-    if( p_sys->b_live )
+    for( int i = 0; i < 3; i++ )
     {
-        p_sys->download.vlead = vsms ?
-            video_chunk->start_time + p_sys->timescale / 1000 : 0;
-        p_sys->download.alead = asms ?
-            audio_chunk->start_time + p_sys->timescale / 1000 : 0;
-    }
+        sms = SMS_GET_SELECTED_ST( index_to_es_cat( i ) );
+        if( sms )
+        {
+            chunk = vlc_array_item_at_index( sms->chunks, 0 );
+            p_sys->download.lead[i] = chunk->start_time + p_sys->timescale / 1000;
+            if( !start_time )
+                start_time = chunk->start_time;
 
-    if( vsms && Download( s, vsms ) != VLC_SUCCESS )
-    {
-        goto cancel;
-    }
-    if( asms && Download( s, asms ) != VLC_SUCCESS )
-    {
-        goto cancel;
+            if( Download( s, sms ) != VLC_SUCCESS )
+                goto cancel;
+        }
     }
 
-    int64_t lead = 0;
-    int64_t start_time = vsms ? video_chunk->start_time : audio_chunk->start_time;
-
     while( 1 )
     {
-        /* XXX replace magic number 20 by a value depending on
+        /* XXX replace magic number 10 by a value depending on
          * LookAheadFragmentCount and DVRWindowLength */
         vlc_mutex_lock( &p_sys->download.lock_wait );
 
@@ -655,13 +614,9 @@ void* sms_Thread( void *p_this )
             break;
         }
 
-        bool no_more_chunks = !p_sys->b_live &&
-            (!vsms || p_sys->download.vindex >= vsms->vod_chunks_nb - 1) &&
-            (!asms || p_sys->download.aindex >= asms->vod_chunks_nb - 1);
-
         lead = get_lead( s );
 
-        while( lead > 10 * p_sys->timescale + start_time || no_more_chunks )
+        while( lead > 10 * p_sys->timescale + start_time || NO_MORE_CHUNKS )
         {
             vlc_cond_wait( &p_sys->download.wait, &p_sys->download.lock_wait );
             lead = get_lead( s );
@@ -690,8 +645,11 @@ void* sms_Thread( void *p_this )
             p_sys->download.chunks = vlc_array_new();
 
             p_sys->playback.toffset = p_sys->time_pos;
-            p_sys->download.vlead = p_sys->download.alead = p_sys->time_pos;
-            p_sys->download.aindex = p_sys->download.vindex = 0;
+            for( int i = 0; i < 3; i++ )
+            {
+                p_sys->download.lead[i] = p_sys->time_pos;
+                p_sys->download.ck_index[i] = 0;
+            }
             p_sys->download.next_chunk_offset = 0;
 
             p_sys->playback.boffset = 0;
@@ -709,16 +667,9 @@ void* sms_Thread( void *p_this )
         }
         vlc_mutex_unlock( &p_sys->download.lock_wait );
 
-        if( !vsms || (asms && p_sys->download.alead < p_sys->download.vlead) )
-        {
-            if( Download( s, asms ) != VLC_SUCCESS )
-                    break;
-        }
-        else if( !asms || (vsms && p_sys->download.vlead <= p_sys->download.alead ) )
-        {
-            if( Download( s, vsms ) != VLC_SUCCESS )
-                    break;
-        }
+        sms = SMS_GET_SELECTED_ST( who_s_turn( s ) );
+        if( Download( s, sms ) != VLC_SUCCESS )
+            goto cancel;
     }
 
 cancel:
diff --git a/modules/stream_filter/smooth/smooth.c b/modules/stream_filter/smooth/smooth.c
index 07e8391..01281cc 100644
--- a/modules/stream_filter/smooth/smooth.c
+++ b/modules/stream_filter/smooth/smooth.c
@@ -404,8 +404,10 @@ static int Open( vlc_object_t *p_this )
     p_sys->b_cache = false;
 
     p_sys->sms_streams = vlc_array_new();
+    p_sys->selected_st = vlc_array_new();
     p_sys->download.chunks = vlc_array_new();
-    if( unlikely( !p_sys->sms_streams || !p_sys->download.chunks ) )
+    if( unlikely( !p_sys->sms_streams || !p_sys->download.chunks ||
+                  !p_sys->selected_st ) )
     {
         free( p_sys );
         return VLC_ENOMEM;
@@ -423,34 +425,14 @@ static int Open( vlc_object_t *p_this )
 
     p_sys->i_tracks = vlc_array_count( p_sys->sms_streams );
 
-    /* FIXME */
-    p_sys->i_selected_tracks = 2; /* one video track and one audio track */
-
-    /* Choose first video stream available */
-    sms_stream_t *vsms = NULL;
+    /* Choose first video / audio / subtitle stream available */
+    sms_stream_t *tmp = NULL, *selected = NULL;
     for( unsigned i = 0; i < p_sys->i_tracks; i++ )
     {
-        vsms = vlc_array_item_at_index( p_sys->sms_streams, i );
-        if( vsms->type == VIDEO_ES )
-        {
-            msg_Dbg( s, "Video stream chosen is %s", vsms->name );
-            p_sys->vstream = vsms;
-            break;
-        }
-    }
-
-    /* Choose first audio stream available */
-    sms_stream_t *asms = NULL;
-    for( unsigned i = 0; i < p_sys->i_tracks; i++ )
-    {
-        asms = vlc_array_item_at_index( p_sys->sms_streams, i );
-        //if( asms->type == AUDIO_ES && !strcmp( asms->name, "audio_eng" ) )
-        if( asms->type == AUDIO_ES )
-        {
-            msg_Dbg( s, "Audio stream chosen is %s", asms->name );
-            p_sys->astream = asms;
-            break;
-        }
+        tmp = vlc_array_item_at_index( p_sys->sms_streams, i );
+        selected = SMS_GET_SELECTED_ST( tmp->type );
+        if( !selected )
+            vlc_array_append( p_sys->selected_st, tmp );
     }
 
     /* Choose lowest quality for the first chunks */
@@ -498,7 +480,8 @@ static void Close( vlc_object_t *p_this )
     vlc_mutex_lock( &p_sys->download.lock_wait );
     p_sys->b_close = true;
     /* Negate the condition variable's predicate */
-    p_sys->download.vlead = p_sys->download.alead = 0;
+    for( int i = 0; i < 3; i++ )
+        p_sys->download.lead[i] = 0;
     p_sys->playback.toffset = 0;
     vlc_cond_signal(&p_sys->download.wait);
     vlc_mutex_unlock( &p_sys->download.lock_wait );
@@ -544,9 +527,7 @@ static chunk_t *get_chunk( stream_t *s, const bool wait )
                     p_sys->playback.index );
             return NULL;
         }
-        if( !p_sys->b_live &&
-            p_sys->download.aindex >= (p_sys->vstream->vod_chunks_nb -1) &&
-            p_sys->download.vindex >= (p_sys->astream->vod_chunks_nb -1) )
+        if( NO_MORE_CHUNKS )
         {
             vlc_mutex_unlock( &p_sys->download.lock_wait );
             msg_Info( s, "No more chunks, end of the VOD" );
@@ -580,7 +561,8 @@ static int sms_Read( stream_t *s, uint8_t *p_read, int i_read )
 
         if( chunk->read_pos >= (int)chunk->size )
         {
-            if( chunk->type == VIDEO_ES || !p_sys->vstream )
+            if( chunk->type == VIDEO_ES ||
+                ( !SMS_GET_SELECTED_ST( VIDEO_ES ) && chunk->type == AUDIO_ES ) )
             {
                 vlc_mutex_lock( &p_sys->download.lock_wait );
                 p_sys->playback.toffset += chunk->duration;
@@ -727,7 +709,8 @@ static int chunk_Seek( stream_t *s, const uint64_t pos )
 
         p_sys->b_tseek = true;
         p_sys->time_pos = p_sys->vod_duration * pos / FAKE_STREAM_SIZE;
-        p_sys->download.vlead = p_sys->download.alead = 0;
+        for( int i = 0; i < 3; i++ )
+            p_sys->download.lead[i] = 0;
         p_sys->playback.toffset = 0;
 
         vlc_cond_signal( &p_sys->download.wait);
diff --git a/modules/stream_filter/smooth/smooth.h b/modules/stream_filter/smooth/smooth.h
index da8283d..32adda6 100644
--- a/modules/stream_filter/smooth/smooth.h
+++ b/modules/stream_filter/smooth/smooth.h
@@ -89,12 +89,9 @@ struct stream_sys_t
     char         *base_url;    /* URL common part for chunks */
     vlc_thread_t thread;       /* SMS chunk download thread */
 
-    vlc_array_t  *sms_streams; /* array of sms_stream_t */
-    sms_stream_t *vstream;     /* current video stream  */
-    sms_stream_t *astream;     /* current audio stream  */
-    sms_stream_t *tstream;     /* current text stream  */
+    vlc_array_t  *sms_streams; /* available streams */
+    vlc_array_t  *selected_st; /* selected streams */
     unsigned     i_tracks;     /* Total number of tracks in the Manifest */
-    unsigned     i_selected_tracks;
     sms_queue_t  *bws;         /* Measured bandwidths of the N last chunks */
     uint64_t     vod_duration; /* total duration of the VOD media */
     int64_t      time_pos;
@@ -103,13 +100,10 @@ struct stream_sys_t
     /* Download */
     struct sms_download_s
     {
-        uint64_t     alead;       // how much audio/video/text data is
-        uint64_t     vlead;       // available (downloaded),
-        uint64_t     tlead;       // in seconds / TimeScale
+        uint64_t     lead[3];     /* how much audio/video/text data is available
+                                     (downloaded), in seconds / TimeScale */
 
-        unsigned     aindex;      /* current audio chunk for download */
-        unsigned     vindex;      /*         video                    */
-        unsigned     sindex;      /*         spu                      */
+        unsigned     ck_index[3]; /* current chunk for download */
 
         uint64_t     next_chunk_offset;
         vlc_array_t  *chunks;     /* chunks that have been downloaded */
@@ -164,6 +158,12 @@ struct stream_sys_t
     slice += 4; \
   } while(0)
 
+#define SMS_GET_SELECTED_ST( cat ) \
+    sms_get_stream_by_cat( p_sys->selected_st, cat )
+
+#define NO_MORE_CHUNKS !p_sys->b_live && \
+    no_more_chunks( p_sys->download.ck_index, p_sys->selected_st )
+
 sms_queue_t *sms_queue_init( const int );
 int sms_queue_put( sms_queue_t *, const uint64_t );
 uint64_t sms_queue_avg( sms_queue_t *);
@@ -176,5 +176,9 @@ void chunk_Free( chunk_t *);
 sms_stream_t * sms_New( void );
 void sms_Free( sms_stream_t *);
 uint8_t *decode_string_hex_to_binary( const char * );
+sms_stream_t * sms_get_stream_by_cat( vlc_array_t *, int );
+bool no_more_chunks( unsigned[], vlc_array_t *);
+int index_to_es_cat( int );
+int es_cat_to_index( int );
 
 #endif
diff --git a/modules/stream_filter/smooth/utils.c b/modules/stream_filter/smooth/utils.c
index 9b57f66..be27c03 100644
--- a/modules/stream_filter/smooth/utils.c
+++ b/modules/stream_filter/smooth/utils.c
@@ -202,3 +202,63 @@ uint64_t sms_queue_avg( sms_queue_t *queue )
     }
     return sum / queue->length;
 }
+
+sms_stream_t * sms_get_stream_by_cat( vlc_array_t *streams, int i_cat )
+{
+    sms_stream_t *ret = NULL;
+    int count = vlc_array_count( streams );
+    assert( count >= 0 && count <= 3 );
+
+    for( int i = 0; i < count; i++ )
+    {
+        ret = vlc_array_item_at_index( streams, i );
+        if( ret->type == i_cat )
+            return ret;
+    }
+    return NULL;
+}
+
+int es_cat_to_index( int i_cat )
+{
+    switch( i_cat )
+    {
+        case VIDEO_ES:
+            return 0;
+        case AUDIO_ES:
+            return 1;
+        case SPU_ES:
+            return 2;
+        default:
+            return -1;
+    }
+}
+
+int index_to_es_cat( int index )
+{
+    switch( index )
+    {
+        case 0:
+            return VIDEO_ES;
+        case 1:
+            return AUDIO_ES;
+        case 2:
+            return SPU_ES;
+        default:
+            return -1;
+    }
+}
+
+bool no_more_chunks( unsigned *indexes, vlc_array_t *streams )
+{
+    sms_stream_t *sms = NULL;
+    int count = vlc_array_count( streams );
+    unsigned ck_index;
+    for( int i = 0; i < count; i++ )
+    {
+        sms = vlc_array_item_at_index( streams, i );
+        ck_index = indexes[es_cat_to_index( sms->type )];
+        if( ck_index < sms->vod_chunks_nb - 1 )
+            return false;
+    }
+    return true;
+}
-- 
1.7.9.5



More information about the vlc-devel mailing list