[vlc-devel] [PATCH] Add Smooth Streaming stream_filter module

Frédéric Yhuel fyhuel at viotech.net
Fri Mar 16 15:25:09 CET 2012


Not clean yet, I provide it for reviewers to test my fMP4 patch, and for
people interested in Smooth Streaming. Comments and patches welcome!

@courmish: please don't look at it, because you would not like it :-)

Test it:

***** Live *****
- http://demo.anevia.com:3129/html/player/smooth/channel2.isml/manifest
- http://demo.anevia.com:3128/html/player/smooth/channel1.isml/manifest

***** VOD *****
- http://demo.anevia.com:3130/html/player/smooth/vod/content1.ism/Manifest
- http://demo.anevia.com:3131/html/player/smooth/vod/IbcVoile2011.ism/Manifest

(Thanks Anevia!)

Note: unfortunately, Anevia streams have a constant resolution, and I'm
not sure I can give the URL of our potential client Smooth Streaming
server. Please share if you know Smooth Streaming servers with H.264/AAC
content.
---
 modules/stream_filter/Modules.am |    2 +
 modules/stream_filter/smooth.c   | 1781 ++++++++++++++++++++++++++++++++++++++
 2 files changed, 1783 insertions(+), 0 deletions(-)
 create mode 100644 modules/stream_filter/smooth.c

diff --git a/modules/stream_filter/Modules.am b/modules/stream_filter/Modules.am
index 78efb7d..c91f20b 100644
--- a/modules/stream_filter/Modules.am
+++ b/modules/stream_filter/Modules.am
@@ -2,9 +2,11 @@ SUBDIRS = dash
 
 SOURCES_decomp = decomp.c
 SOURCES_stream_filter_record = record.c
+SOURCES_stream_filter_smooth = smooth.c
 
 libvlc_LTLIBRARIES += \
    libstream_filter_record_plugin.la \
+   libstream_filter_smooth_plugin.la \
    $(NULL)
 
 if HAVE_GCRYPT
diff --git a/modules/stream_filter/smooth.c b/modules/stream_filter/smooth.c
new file mode 100644
index 0000000..c539483
--- /dev/null
+++ b/modules/stream_filter/smooth.c
@@ -0,0 +1,1781 @@
+/*****************************************************************************
+ * smooth.c: Smooth Streaming stream filter
+ *****************************************************************************
+ * Copyright (C) 1996-2012 VLC authors and VideoLAN
+ * $Id$
+ *
+ * Author: Frédéric Yhuel <fyhuel _AT_ viotech _DOT_ net>
+ * Heavily inspired by HLS module of Jean-Paul Saman
+ * <jpsaman _AT_ videolan _DOT_ org>
+ * Some code is taken from an unmerged patch of Luc Saillard
+ * <luc _DOT_ saillard _AT_ sfr _DOT_ com>
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2.1 of the License, or (at your option) any later version.
+ *
+ * This library 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
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301 USA
+ *****************************************************************************/
+
+/* TODO
+ * Don't reload the manifest, get the duration of further segments from the
+ * Tfxd / Tfrf Boxes.
+ */
+
+/*****************************************************************************
+ * Preamble
+ *****************************************************************************/
+#ifdef HAVE_CONFIG_H
+# include "config.h"
+#endif
+
+#include <limits.h>
+
+#include <vlc_common.h>
+#include <vlc_plugin.h>
+
+#include <assert.h>
+#include <inttypes.h>
+
+#include <vlc_xml.h>
+#include <vlc_charset.h>
+#include <vlc_threads.h>
+#include <vlc_arrays.h>
+#include <vlc_stream.h>
+#include <vlc_url.h>
+#include <vlc_memory.h>
+
+#include "../demux/mp4/mp4.h"
+
+/* In all the file, "segment" and "chunk" both refer to the same thing,
+ * and are used interchangeably. The same goes for "bitrate" and "bandwidth". */
+
+#define DA_TEXT N_("Disable audio stream")
+#define DA_LONGTEXT N_("Disable audio stream")
+#define VB_TEXT N_("Desired bitrate for the video stream")
+#define VB_LONGTEXT N_("Lowest video bitrate wanted. You can also set to 0 to" \
+        "get the lowest available, and to 1 in order to get the highest")
+
+/*****************************************************************************
+ * Module descriptor
+ *****************************************************************************/
+static int  Open( vlc_object_t * );
+static void Close( vlc_object_t * );
+
+vlc_module_begin()
+    set_category( CAT_INPUT )
+    set_subcategory( SUBCAT_INPUT_STREAM_FILTER )
+    set_description( N_("Smooth Streaming") )
+    set_shortname( "Smooth Streaming")
+    add_shortcut( "smooth" )
+    set_capability( "stream_filter", 30 )
+    set_callbacks( Open, Close )
+    add_integer( "smooth-video-bitrate", 450678, VB_TEXT, VB_LONGTEXT, false )
+        change_safe()
+    add_bool( "smooth-disable-audio", false, DA_TEXT, DA_LONGTEXT, false )
+        change_safe()
+    add_bool( "smooth-can-seek", false, "can seek?", "Enable seeking", false )
+        change_safe()
+vlc_module_end()
+
+/*****************************************************************************
+ *
+ *****************************************************************************/
+
+/* Time Scale, I've never seen another value, but it may happen */
+#define TS 10000000
+//#define DISABLE_BANDWIDTH_ADAPTATION
+
+enum FourCC {
+    NULL_CC=0,
+    H264 = VLC_FOURCC( 'H', '2', '6', '4' ),
+    AVC1 = VLC_FOURCC( 'A', 'V', 'C', '1' ),
+    AACL = VLC_FOURCC( 'A', 'A', 'C', 'L' ),
+    AACH = VLC_FOURCC( 'A', 'A', 'C', 'H' ),
+    WVC1 = VLC_FOURCC( 'W', 'V', 'C', '1' ),
+    WMAP = VLC_FOURCC( 'W', 'M', 'A', 'P' ),
+    TTML = VLC_FOURCC( 'T', 'T', 'M', 'L' ),
+};
+
+typedef struct segment_s
+{
+    uint64_t    duration;   /* segment duration (seconds / TimeScale) */
+    uint64_t    start_time; /* PTS (seconds / TimeScale) */
+    uint64_t    size;       /* segment size in bytes */
+    uint64_t    bandwidth;  /* max bandwidth (bits per second)*/
+    uint32_t    sequence;   /* unique sequence number */
+    uint64_t    offset;     /* offset in the media */
+    int         read_pos;   /* position in the segment */
+    int         type;       /* video, audio, or subtitles */
+
+    vlc_mutex_t lock;
+    block_t     *data;
+} segment_t;
+
+typedef struct quality_level_s
+{
+    int             Index;
+    enum FourCC     FourCC;
+    uint64_t        Bitrate;
+    uint32_t        MaxWidth;
+    uint32_t        MaxHeight;
+    unsigned long   SamplingRate;
+    unsigned long   Channels;
+    unsigned long   BitsPerSample;
+    uint32_t        PacketSize;
+    uint32_t        AudioTag;
+    char            *CodecPrivateData; /* hex encoded string */
+    uint16_t        id;
+
+} quality_level_t;
+
+typedef struct sms_stream_s
+{
+    vlc_array_t     *qlevels;       /* list of available Quality Levels */
+    vlc_array_t     *segments;      /* list of segments */
+    uint32_t        vod_chunks_nb;  /* total num of chunks of the VOD stream */
+    uint32_t        qlevel_nb;      /* number of quality levels */
+    uint16_t        id;             /* track id, will be set arbitrarily */
+    char            *name;
+    char            *url_template;
+    int             type;
+    quality_level_t *download_qlvl; /* current quality level for Download() */
+
+} sms_stream_t;
+
+
+struct stream_sys_t
+{
+    char         *ismc;        /* Manifest url */
+    char         *base_url;    /* URL common part for chunks */
+    vlc_thread_t thread;       /* SMS segment download thread */
+    vlc_thread_t reload;       /* SMS updating thread */
+
+    block_t      *peeked;
+
+    /* */
+    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  */
+    unsigned     i_tracks;     /* Total number of tracks in the Manifest */
+    uint64_t     bandwidth;    /* measured bandwidth (bits per second) */
+    uint64_t     vod_duration; /* total duration of the VOD media */
+    uint32_t     peek_index;   /* current chunk for peeking */
+
+    /* 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
+
+        uint32_t     aindex;      /* current audio segment for download */
+        uint32_t     vindex;
+        uint32_t     tindex;
+
+        vlc_array_t  *dld_chunks; /* segments that have been downloaded */
+        vlc_mutex_t  lock_wait;   /* protect segment download counter */
+        vlc_cond_t   wait;        /* some condition to wait on */
+    } download;
+
+    /* Playback */
+    struct sms_playback_s
+    {
+        uint64_t    boffset;     /* current byte offset in media */
+        uint64_t    toffset;     /* current time offset in media */
+        uint32_t    index;       /* current segment for playback */
+    } playback;
+
+    /* Manifest */
+    struct sms_playlist_s
+    {
+        mtime_t     last;          /* manifest last loaded */
+        mtime_t     wakeup;        /* next reload time */
+        int         tries;         /* times it was not changed */
+        uint64_t    v_start_time;  /* start time of the last video segment */
+        uint64_t    a_start_time;  /* start time of the last audio segment */
+    } playlist;
+
+    /* state */
+    bool        b_seek;      /* can seek */
+    bool        b_cache;     /* can cache files */
+    bool        b_live;      /* live stream? or vod? */
+    bool        b_error;     /* parsing error */
+};
+
+
+/****************************************************************************
+ * Local prototypes
+ ****************************************************************************/
+static int   Read( stream_t *, void *, unsigned int );
+static int   Peek( stream_t *, const uint8_t **, unsigned int );
+static int   Control( stream_t *, int , va_list );
+
+quality_level_t *get_qlevel( sms_stream_t *, uint16_t );
+sms_stream_t    *get_sms_stream( stream_t *, uint16_t );
+uint16_t        set_track_id( segment_t *, uint16_t, uint16_t );
+static char     *inverse_string( const char *);
+static int      sms_TrackCreate( stream_t *, uint16_t, mp4_track_t * );
+static int      sms_FmtCreate( stream_t *, uint16_t, uint16_t, es_format_t * );
+int             build_raw_avcC( uint8_t **, const char * );
+int             build_raw_esds( uint8_t **, const char * );
+static int      chunk_index_add( uint32_t *, int32_t );
+static          segment_t *get_segment( stream_t *, uint32_t, bool );
+static int      sms_Download( stream_t *, segment_t *, char *);
+static int      Download( stream_t *, sms_stream_t *, uint64_t *);
+static void     *sms_Thread( void *);
+static void     *sms_Reload( void *);
+/****************************************************************************
+ *
+ ****************************************************************************/
+
+static bool isSmoothStreaming( stream_t *s )
+{
+    const uint8_t *peek;
+    const char *conv_peek;
+    const char *needle = "<SmoothStreamingMedia";
+
+    int i_size = stream_Peek( s->p_source, &peek, 1024 );
+    if(  i_size < 1 )
+        return false;
+
+    if( strstr( (const char*)peek, needle ) != NULL )
+        return true;
+    else
+    /* maybe it's utf-16 encoding, should we also test other encodings? */
+    {
+        conv_peek = FromCharset( "UTF-16", peek, 1024 );
+        if( conv_peek != NULL )
+            if( strstr( conv_peek, needle ) != NULL )
+                return true;
+    }
+    return false;
+}
+
+static quality_level_t * ql_New( void )
+{
+    quality_level_t *ql = calloc( 1, sizeof( quality_level_t ) );
+    if( ql == NULL )
+        return NULL;
+    ql->Index = -1;
+    ql->FourCC = NULL_CC;
+    return ql;
+}
+
+static void ql_Free( quality_level_t *qlevel )
+{
+    free( qlevel->CodecPrivateData );
+    free( qlevel );
+    qlevel = NULL;
+}
+
+static segment_t *segment_New( sms_stream_t* sms, uint64_t duration,\
+        uint64_t start_time )
+{
+    segment_t *segment = calloc( 1, sizeof( segment_t ) );
+    if( segment == NULL )
+        return NULL;
+
+    segment->duration = duration;
+    segment->start_time = start_time;
+    segment->type = UNKNOWN_ES;
+    segment->sequence = vlc_array_count( sms->segments );
+    vlc_array_append( sms->segments, segment );
+    vlc_mutex_init( &segment->lock );
+    return segment;
+}
+
+static void segment_Free( segment_t *segment )
+{
+    vlc_mutex_destroy( &segment->lock );
+
+    if( segment->data )
+        block_Release( segment->data );
+    free( segment );
+    segment = NULL;
+}
+
+static sms_stream_t * sms_New( void )
+{
+    sms_stream_t *sms = calloc( 1, sizeof( sms_stream_t ) );
+    if( sms == NULL )
+        return NULL;
+    sms->qlevels = vlc_array_new();
+    sms->segments = vlc_array_new();
+    sms->type = UNKNOWN_ES;
+    return sms;
+}
+
+static void sms_Free( sms_stream_t *sms )
+{
+    if( sms->qlevels )
+    {
+        for( int n = 0; n < vlc_array_count( sms->qlevels ); n++ )
+        {
+            quality_level_t *qlevel =\
+                (quality_level_t *)vlc_array_item_at_index( sms->qlevels, n );
+            if( qlevel ) ql_Free( qlevel );
+        }
+        vlc_array_destroy( sms->qlevels );
+    }
+
+    if( sms->segments )
+    {
+        for( int n = 0; n < vlc_array_count( sms->segments ); n++ )
+        {
+            segment_t *segment =\
+                (segment_t *)vlc_array_item_at_index( sms->segments, n );
+            if( segment) segment_Free( segment );
+        }
+        vlc_array_destroy( sms->segments );
+    }
+
+    free( sms );
+    sms = NULL;
+}
+
+static int parse_Manifest( stream_t *s, bool update )
+{
+    stream_sys_t *p_sys = s->p_sys;
+    xml_t *vlc_xml = NULL;
+    xml_reader_t *vlc_reader = NULL;
+    int type = UNKNOWN_ES;
+    const char *name, *value;
+    stream_t *st = s->p_source;
+    if( update )
+    {
+        st = stream_UrlNew( s, p_sys->ismc );
+        if( st == NULL )
+            return VLC_EGENERIC;
+    }
+
+    msg_Dbg( s, "Manifest parsing\n" );
+
+    vlc_xml = xml_Create( st );
+    if( !vlc_xml )
+    {
+        msg_Err( s, "Failed to open XML parser" );
+        return VLC_EGENERIC;
+    }
+
+    vlc_reader = xml_ReaderCreate( vlc_xml, st );
+    if( !vlc_reader )
+    {
+        msg_Err( s, "Failed to open source for parsing" );
+    }
+
+    const char *node;
+    char *stream_name = NULL;
+    int stream_type = UNKNOWN_ES;
+    sms_stream_t *sms = NULL;
+    quality_level_t *ql = NULL;
+    uint64_t start_time = 0;
+    uint64_t *last_stime = NULL;
+    uint64_t duration = 0;
+    uint64_t computed_start_time = 0;
+    uint64_t computed_duration = 0;
+    uint32_t next_track_id = 1;
+    uint32_t next_qid = 1;
+
+    while( (type = xml_ReaderNextNode( vlc_reader, &node )) > 0 )
+    {
+        switch( type )
+        {
+            case XML_READER_STARTELEM:
+                if( !strcmp( node, "SmoothStreamingMedia" ) )
+                    while( (name = xml_ReaderNextAttr( vlc_reader, &value )) )
+                        if( !strcmp(name, "Duration" ) )
+                            p_sys->vod_duration = strtoull( value, NULL, 10 );
+
+                if( !strcmp( node, "StreamIndex" ) )
+                {
+                    if( !update )
+                    {
+                        sms = sms_New();
+                        if( unlikely( !sms ) )
+                            return VLC_ENOMEM;
+                        sms->id = next_track_id;
+                        next_track_id++;
+                    }
+
+                    while( (name = xml_ReaderNextAttr( vlc_reader, &value )) )
+                    {
+                        /* If I already found the stream I want to update */
+                        if( stream_type && stream_name && update)
+                            break;
+
+                        if( !strcmp( name, "Type" ) )
+                        {
+                            if( !strcmp( value, "video" ) )
+                                stream_type = VIDEO_ES;
+                            else if( !strcmp( value, "audio" ) )
+                                stream_type = AUDIO_ES;
+                            else if( !strcmp( value, "text" ) )
+                                stream_type = SPU_ES;
+                        }
+
+                        if( !strcmp( name, "Name" ) )
+                            stream_name = strdup( value );
+
+                        if( !strcmp( name, "Chunks" ) && !update )
+                        {
+                            sms->vod_chunks_nb = strtol( value, NULL, 10 );
+                            if( sms->vod_chunks_nb == 0 ) /* live */
+                                sms->vod_chunks_nb = UINT32_MAX;
+                        }
+
+                        if( !strcmp( name, "QualityLevels" ) && !update )
+                            sms->qlevel_nb = strtoul( value, NULL, 10 );
+                        if( !strcmp( name, "Url" ) && !update )
+                            sms->url_template = strdup(value);
+                    }
+
+                    if( !stream_name )
+                    {
+                        if( stream_type == VIDEO_ES )
+                            stream_name = strdup( "video" );
+                        else if( stream_type == AUDIO_ES )
+                            stream_name = strdup( "audio" );
+                        else if( stream_type == SPU_ES )
+                            stream_name = strdup( "text" );
+                    }
+
+                    if( !update )
+                    {
+                        sms->name = stream_name;
+                        sms->type = stream_type;
+                        vlc_array_append( p_sys->sms_streams, sms );
+                    }
+                    else
+                        for( unsigned i = 0; i < p_sys->i_tracks; i++ )
+                        {
+                            sms = vlc_array_item_at_index( p_sys->sms_streams, i );
+                            if( (sms->type == stream_type) &&
+                                    !(strcmp( stream_name, sms->name )) )
+                                break;
+                            else if( i == p_sys->i_tracks - 1 )
+                                msg_Err( s, "Updating of the manifest failed" );
+                        }
+                }
+
+                if( !strcmp( node, "QualityLevel" ) && !update )
+                {
+                    ql = ql_New();
+                    if( !ql )
+                        return VLC_ENOMEM;
+                    ql->id = next_qid;
+                    next_qid++;
+                    while( (name = xml_ReaderNextAttr( vlc_reader, &value )) )
+                    {
+                        if( !strcmp( name, "Index" ) )
+                            ql->Index = strtol( value, NULL, 10 );
+                        if( !strcmp( name, "Bitrate" ) )
+                            ql->Bitrate = strtoull( value, NULL, 10 );
+                        if( !strcmp( name, "FourCC" ) )
+                            ql->FourCC = VLC_FOURCC( value[0], value[1],
+                                                     value[2], value[3] );
+                        if( !strcmp( name, "CodecPrivateData" ) )
+                            ql->CodecPrivateData = strdup( value );
+                        if( !strcmp( name, "WaveFormatEx" ) )
+                            ql->CodecPrivateData = strdup( value );
+                        if( !strcmp( name, "MaxWidth" ) )
+                            ql->MaxWidth = strtoul( value, NULL, 10 );
+                        if( !strcmp( name, "MaxHeight" ) )
+                            ql->MaxHeight = strtoul( value, NULL, 10 );
+                        if( !strcmp( name, "Channels" ) )
+                            ql->Channels = strtoul( value, NULL, 10 );
+                        if( !strcmp( name, "SamplingRate" ) )
+                            ql->SamplingRate = strtoul( value, NULL, 10 );
+                        if( !strcmp( name, "BitsPerSample" ) )
+                            ql->BitsPerSample = strtoul( value, NULL, 10 );
+                    }
+                    vlc_array_append( sms->qlevels, ql );
+                }
+
+                if( !strcmp( node, "c" ) )
+                {
+                    start_time = duration = UINT64_MAX;
+                    while( (name = xml_ReaderNextAttr( vlc_reader, &value )) )
+                    {
+                        if( !strcmp( name, "t" ) )
+                            start_time = strtoull( value, NULL, 10 );
+                        if( !strcmp( name, "d" ) )
+                            duration = strtoull( value, NULL, 10 );
+                    }
+                    if( start_time == UINT64_MAX )
+                    {
+                        assert( duration != UINT64_MAX );
+                        computed_start_time += computed_duration;
+                        computed_duration = duration;
+                    }
+                    else if( duration == UINT64_MAX )
+                    {
+                        assert( start_time != UINT64_MAX );
+                        computed_duration = start_time - computed_start_time;
+                        computed_start_time = start_time;
+                    }
+                    else
+                    {
+                        computed_start_time = start_time;
+                        computed_duration = duration;
+                    }
+
+                    /* When updating, only add new segments of the refreshed
+                     * manifest file */
+                    if( sms->type == VIDEO_ES )
+                            last_stime = &(p_sys->playlist.v_start_time);
+                    else if( sms->type == AUDIO_ES )
+                            last_stime = &(p_sys->playlist.a_start_time);
+
+                    if( !computed_start_time ||
+                            (computed_start_time > *last_stime) )
+                    {
+                        if( unlikely( segment_New( sms, computed_duration,
+                                            computed_start_time ) == NULL ) )
+                            return VLC_ENOMEM;
+                        if( update ) msg_Dbg(s, "***** Hey, a new segment! *****");
+                        *last_stime = computed_start_time;
+                    }
+                }
+                break;
+
+            case XML_READER_ENDELEM:
+                if( !strcmp( node, "StreamIndex" ) )
+                {
+                    stream_name = NULL;
+                    stream_type = UNKNOWN_ES;
+                    computed_start_time = 0;
+                    computed_duration = 0;
+                    next_qid = 1;
+
+                    if( sms->qlevel_nb == 0 )
+                        sms->qlevel_nb = vlc_array_count( sms->qlevels );
+                }
+                break;
+            case XML_READER_NONE:
+                break;
+            case XML_READER_TEXT:
+                break;
+            default:
+                return VLC_EGENERIC;
+        }
+    }
+
+    xml_ReaderDelete( vlc_reader );
+    xml_Delete( vlc_xml );
+    if( update )
+        stream_Delete( st );
+
+    return VLC_SUCCESS;
+}
+
+/****************************************************************************
+ * Open
+ ****************************************************************************/
+
+static int Open( vlc_object_t *p_this )
+{
+    stream_t *s = (stream_t*)p_this;
+    stream_sys_t *p_sys;
+
+    if( !isSmoothStreaming( s ) )
+    {
+        msg_Dbg(p_this, "Smooth Streaming: this is not a valid manifest");
+        return VLC_EGENERIC;
+    }
+
+    msg_Info( p_this, "Smooth Streaming (%s)", s->psz_path );
+
+    /* */
+    s->p_sys = p_sys = calloc( 1, sizeof(*p_sys ) );
+    if( p_sys == NULL )
+        return VLC_ENOMEM;
+
+    p_sys->b_seek = var_CreateGetBool( s, "smooth-can-seek" );
+    char *uri = NULL;
+    if( asprintf( &uri,"%s://%s", s->psz_access, s->psz_path ) < 0)
+    {
+        free( p_sys );
+        return VLC_ENOMEM;
+    }
+    p_sys->ismc = uri;
+
+    /* remove the last part of the url */
+    char *inv = inverse_string( uri );
+    char *tmp = strtok( inv, "/" );
+    tmp = strtok( NULL, "" );
+    p_sys->base_url = inverse_string( tmp );
+
+    p_sys->download.vlead = 0;
+    bool disable_audio = var_CreateGetBool( s, "smooth-disable-audio" );
+    if( disable_audio )
+        p_sys->download.alead = UINT64_MAX;
+    else
+        p_sys->download.alead = 0;
+    p_sys->download.tlead = 0;
+    p_sys->playback.boffset = 0;
+    /* UINT32_MAX is the index value of the initialization segment */
+    p_sys->download.vindex = 0;
+    p_sys->download.aindex = 0;
+    p_sys->bandwidth = 0;
+    p_sys->b_live = false;
+    p_sys->b_cache = true;
+    p_sys->b_error = false;
+
+    p_sys->playlist.v_start_time = 0;
+    p_sys->playlist.a_start_time = 0;
+
+    p_sys->sms_streams = vlc_array_new();
+    p_sys->download.dld_chunks = vlc_array_new();
+    if( p_sys->sms_streams == NULL )
+    {
+        free( p_sys );
+        return VLC_ENOMEM;
+    }
+
+    /* */
+    s->pf_read = Read;
+    s->pf_peek = Peek;
+    s->pf_control = Control;
+
+    uint64_t video_bitrate = var_CreateGetInteger( s, "smooth-video-bitrate" );
+    msg_Info( s, "video_bitrate chosen for start is %"PRIu64, video_bitrate );
+
+    p_sys->playback.index = 0;
+    p_sys->peek_index = p_sys->playback.index;
+
+    /* Parse SMS ismc content. */
+    if( parse_Manifest( s, false ) != VLC_SUCCESS )
+        return VLC_EGENERIC;
+
+    if( !p_sys->vod_duration )
+       p_sys->b_live = true;
+
+    p_sys->i_tracks = vlc_array_count( p_sys->sms_streams );
+
+    /* Choose first video stream available (TO FIX) */
+    sms_stream_t *vsms = 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_Info( s, "Video stream chosen is %s", vsms->name );
+            break;
+        }
+    }
+    p_sys->vstream = vsms;
+
+    /* Choose first audio stream available (TO FIX) */
+    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_Info( s, "Audio stream chosen is %s", asms->name );
+            break;
+        }
+    }
+    p_sys->astream = asms;
+
+    /* Choose SMS quality to start with */
+    quality_level_t *wanted = vlc_array_item_at_index( vsms->qlevels, 0 );
+    quality_level_t *qlvl = NULL;
+    for( uint32_t i=1; i < vsms->qlevel_nb; i++ )
+    {
+        qlvl = vlc_array_item_at_index( vsms->qlevels, i );
+        if(  (video_bitrate == 0UL) && (qlvl->Bitrate < wanted->Bitrate) )
+            wanted = qlvl;
+        else if(  (video_bitrate == 1UL) && (qlvl->Bitrate > wanted->Bitrate) )
+            wanted = qlvl;
+        else if(  (video_bitrate >= 2) && (((qlvl->Bitrate >= video_bitrate) &&
+                                       qlvl->Bitrate < wanted->Bitrate) ||
+                                        wanted->Bitrate < video_bitrate) )
+            wanted = qlvl;
+    }
+    vsms->download_qlvl = wanted;
+    msg_Info( s, "Video quality level chosen is %"PRIu64"", wanted->Bitrate );
+
+    wanted = vlc_array_item_at_index( asms->qlevels, 0 );
+    for( uint32_t i = 1; i < asms->qlevel_nb; i++)
+    {
+        qlvl = vlc_array_item_at_index( asms->qlevels, i );
+        if( qlvl->Bitrate < wanted->Bitrate )
+            wanted = qlvl;
+    }
+    asms->download_qlvl = wanted;
+
+
+
+    p_sys->playback.toffset = 0;
+
+    vlc_mutex_init( &p_sys->download.lock_wait );
+    vlc_cond_init( &p_sys->download.wait );
+
+    if( vlc_clone( &p_sys->thread, sms_Thread, s, VLC_THREAD_PRIORITY_INPUT ) )
+    {
+        vlc_mutex_destroy( &p_sys->download.lock_wait );
+        vlc_cond_destroy( &p_sys->download.wait );
+        return VLC_EGENERIC;
+    }
+
+    if( p_sys->b_live )
+    {
+        p_sys->playlist.last = mdate();
+        p_sys->playlist.wakeup = p_sys->playlist.last +
+                (8 * UINT64_C( 1000000 ));
+
+        if( vlc_clone( &p_sys->reload, sms_Reload, s, VLC_THREAD_PRIORITY_LOW ) )
+        {
+            return VLC_EGENERIC;
+        }
+    }
+
+    return VLC_SUCCESS;
+}
+
+
+
+/****************************************************************************
+ * Close
+ ****************************************************************************/
+static void Close( vlc_object_t *p_this )
+{
+    stream_t *s = (stream_t*)p_this;
+    stream_sys_t *p_sys = s->p_sys;
+
+    vlc_mutex_lock( &p_sys->download.lock_wait );
+    vlc_cond_signal( &p_sys->download.wait );
+    vlc_mutex_unlock( &p_sys->download.lock_wait );
+
+    if( p_sys->b_live )
+        vlc_join(p_sys->reload, NULL);
+    vlc_join( p_sys->thread, NULL );
+    vlc_mutex_destroy( &p_sys->download.lock_wait );
+    vlc_cond_destroy( &p_sys->download.wait );
+
+    /* Free sms streams */
+    for( int i = 0; i < vlc_array_count( p_sys->sms_streams ); i++ )
+    {
+        sms_stream_t *sms;
+        sms = (sms_stream_t *)vlc_array_item_at_index( p_sys->sms_streams, i );
+        if( sms) sms_Free( sms );
+    }
+    vlc_array_destroy( p_sys->sms_streams );
+
+    if( p_sys->peeked )
+        block_Release( p_sys->peeked );
+    free( p_sys );
+}
+
+static uint64_t GetStreamSize( stream_t *s )
+{
+    /* The returned size will be very approximative, but bitrate adaptation
+     * make computations a tad tricky anyway */
+    stream_sys_t *p_sys = s->p_sys;
+
+    if( p_sys->b_live )
+    {
+        segment_t *chunk = get_segment( s, p_sys->playback.index, false );
+        return chunk->size;
+    }
+
+    uint32_t video_chunk_nb = p_sys->vstream->vod_chunks_nb;
+    segment_t *first_video_chunk = vlc_array_item_at_index(
+            p_sys->vstream->segments, 0 );
+    uint64_t chunk_duration = first_video_chunk->duration;
+    uint64_t total_duration = video_chunk_nb * chunk_duration / TS;
+    uint64_t bitrate = p_sys->vstream->download_qlvl->Bitrate;
+    uint64_t size = bitrate * total_duration;
+
+    return size;
+}
+
+static int segment_Seek( stream_t *s, const uint64_t pos )
+{
+    stream_sys_t *p_sys = s->p_sys;
+    int ret;
+
+    if( pos == p_sys->playback.boffset )
+        return VLC_SUCCESS;
+    segment_t *segment = get_segment( s, p_sys->playback.index, false );
+    if( segment == NULL )
+        goto fail;
+    if( pos < segment->offset )
+    {
+        msg_Warn( s, "Seeking backward outside the current segment" );
+        //if( p_sys->b_live || !p_sys->b_cache )
+        if( !p_sys->b_cache )
+        {
+            msg_Warn( s, "Can't seek backward outside the current segment" );
+            return VLC_EGENERIC;
+        }
+        else
+        {
+            segment->read_pos = 0;
+            msg_Dbg( s, "playback index was %"PRIu32"", p_sys->playback.index);
+            ret = chunk_index_add( &p_sys->playback.index, -1 );
+            msg_Dbg( s, "Seeking backward, to dld segment %"PRIu32"",
+                                                        p_sys->playback.index);
+            if ( ret == VLC_SUCCESS )
+            {
+                segment = get_segment( s, p_sys->playback.index, false );
+                if( segment == NULL )
+                    goto fail;
+                segment->read_pos = 0;
+                p_sys->playback.boffset = segment->offset;
+                msg_Dbg( s, "re-entering segment_Seek" );
+                return segment_Seek( s, pos );
+            }
+        }
+    }
+    else if( pos < segment->offset + segment->read_pos )
+    {
+        msg_Warn( s, "Seeking backward inside the current segment" );
+        segment->read_pos = pos - segment->offset;
+        goto success;
+    }
+    else
+    {
+        msg_Dbg( s, "pos is %"PRIu64", boffset is %"PRIu64"", pos, p_sys->playback.boffset );
+        assert( pos >= p_sys->playback.boffset );
+        int len = pos - p_sys->playback.boffset;
+        int skipped = Read( s, NULL, len);
+        if( skipped == len )
+            goto success;
+    }
+
+fail:
+    msg_Warn( s, "segment_Seek failed, maybe not enough dld chunks");
+    return VLC_EGENERIC;
+
+success:
+    segment = get_segment( s, p_sys->playback.index, false );
+    if( segment == NULL )
+        return VLC_EGENERIC;
+    msg_Dbg( s, "Seek successful! We are now at segment %"PRIu32" quality \
+%"PRIu64", offset %"PRIu64" + %i", segment->sequence,
+segment->bandwidth, segment->offset, segment->read_pos );
+    msg_Info( s, "(segment size is %"PRIu64")", segment->size );
+    return VLC_SUCCESS;
+}
+
+static int Control( stream_t *s, int i_query, va_list args )
+{
+    stream_sys_t *p_sys = s->p_sys;
+
+    switch( i_query )
+    {
+        case STREAM_CAN_SEEK:
+            *(va_arg( args, bool * )) = p_sys->b_seek;
+            break;
+        case STREAM_GET_POSITION:
+            *(va_arg( args, uint64_t * )) = p_sys->playback.boffset;
+            break;
+        case STREAM_SET_POSITION:
+            if( true )
+            {
+                uint64_t pos = (uint64_t)va_arg(args, uint64_t);
+                if (segment_Seek(s, pos) == VLC_SUCCESS)
+                {
+                    p_sys->playback.boffset = pos;
+                    break;
+                }
+            }
+            return VLC_EGENERIC;
+        case STREAM_GET_SIZE:
+            *(va_arg( args, uint64_t * )) = GetStreamSize( s );
+            break;
+        case STREAM_GET_ITRACKS:
+            *(va_arg( args, unsigned * )) = p_sys->i_tracks;
+            break;
+        case STREAM_GET_TRACK:
+            if( true )
+            {
+                uint16_t tid = (uint16_t)va_arg( args, int );
+                mp4_track_t *p_track = va_arg( args, mp4_track_t * );
+                int ret = sms_TrackCreate( s, tid, p_track );
+                if( !ret )
+                    return VLC_EGENERIC;
+                break;
+            }
+            return VLC_EGENERIC;
+        case STREAM_GET_FMT:
+            if( true )
+            {
+                uint16_t tid = (uint16_t)va_arg( args, int );
+                uint16_t qid = (uint16_t)va_arg( args, int );
+                es_format_t *fmt = va_arg( args, es_format_t * );
+                int ret = sms_FmtCreate( s, tid, qid, fmt );
+                if( !ret )
+                    return VLC_EGENERIC;
+                break;
+            }
+            return VLC_EGENERIC;
+        case STREAM_GET_TIME_SCALE:
+            *(va_arg( args, uint64_t * )) = TS;
+            break;
+        default:
+            return VLC_EGENERIC;
+    }
+    return VLC_SUCCESS;
+}
+
+static int sms_Read( stream_t *s, uint8_t *p_read, unsigned int i_read,
+                                                                   bool peek )
+{
+    stream_sys_t *p_sys = s->p_sys;
+    int copied = 0;
+    segment_t *segment = NULL;
+    int loop_count = 0;
+    uint32_t *chunk_index;
+    int *position;
+    int peek_pos = 0;
+
+    if( peek )
+        chunk_index = &(p_sys->peek_index);
+    else
+        chunk_index = &(p_sys->playback.index);
+
+    do
+    {
+        segment = get_segment( s, *chunk_index, true );
+
+        if( peek )
+        {
+            if( loop_count == 0 ) /* we start at read position */
+                peek_pos = segment->read_pos;
+            position = &peek_pos;
+        }
+        else
+            position = &segment->read_pos;
+
+        vlc_mutex_lock( &segment->lock );
+
+        loop_count++;
+        if( *position >= (int)segment->size )
+        {
+            if( segment->type == VIDEO_ES && !peek )
+                p_sys->playback.toffset += segment->duration;
+
+            //if( !peek && (!p_sys->b_cache || p_sys->b_live) )
+            if( !peek && !p_sys->b_cache )
+            {
+                block_Release( segment->data );
+                segment->data = NULL;
+            }
+            else if( segment->size > 0 )
+                *position = 0;
+
+            chunk_index_add( chunk_index, 1);
+            msg_Dbg( s, "Incrementing playback index" );
+            vlc_mutex_unlock( &segment->lock );
+
+            /* signal download thread */
+            vlc_mutex_lock( &p_sys->download.lock_wait );
+            vlc_cond_signal( &p_sys->download.wait );
+            vlc_mutex_unlock( &p_sys->download.lock_wait );
+            continue;
+        }
+
+        if( *position == 0 )
+        {
+            const char *verb = p_read == NULL ? "skipping" : "reading";
+            msg_Info( s, "%s segment %"PRIu32" (%u bytes), bandwidth %"PRIu64"",
+                        peek ? "peeking at" : verb,
+                        segment->sequence, i_read, segment->bandwidth );
+        }
+
+        int len = -1;
+        uint8_t *src = segment->data->p_buffer + *position;
+        if( i_read <= (segment->size - *position) )
+            len = i_read;
+        else
+            len = segment->size - *position;
+
+        if( len > 0 )
+        {
+            if( p_read ) /* otherwise caller skips data */
+                memcpy( p_read + copied, src, len );
+            *position += len;
+            copied += len;
+            i_read -= len;
+        }
+        vlc_mutex_unlock( &segment->lock );
+
+    } while ( i_read > 0 );
+
+    return copied;
+}
+
+static int Read( stream_t *s, void *buffer, unsigned i_read )
+{
+    stream_sys_t *p_sys = s->p_sys;
+    int length = 0;
+
+    if( p_sys->b_error )
+        return 0;
+
+    if( !buffer ) msg_Dbg(s, "Caller skips data (%i bytes)", i_read );
+    else msg_Dbg( s, "Demuxer wants to read %i bytes", i_read );
+
+    length = sms_Read( s, (uint8_t*) buffer, i_read, false );
+    if( length < 0 )
+        return 0;
+
+    p_sys->playback.boffset += length;
+    if( (unsigned)length < i_read )
+        msg_Warn( s, "could not read %u bytes, only %i!", i_read, length );
+    return length;
+}
+
+static int Peek( stream_t *s, const uint8_t **pp_peek, unsigned int i_peek )
+{
+    stream_sys_t *p_sys = s->p_sys;
+    msg_Dbg( s, "Demuxer wants to peek %i bytes", i_peek );
+    block_t *peeked = p_sys->peeked;
+    p_sys->peek_index = p_sys->playback.index;
+
+    if( peeked == NULL )
+        peeked = block_Alloc ( i_peek );
+    else if( peeked->i_buffer < i_peek)
+        peeked = block_Realloc( peeked, 0, i_peek );
+
+    if( (p_sys->peeked = peeked) == NULL )
+        return 0;
+
+    int val = sms_Read( s, peeked->p_buffer, i_peek, true );
+
+    *pp_peek = peeked->p_buffer;
+    if( (unsigned)val < i_peek )
+        msg_Warn( s, "could not peek %u bytes, only %i!", i_peek, val );
+
+    return val;
+}
+
+static char *inverse_string( const char *str )
+{
+    size_t len = strlen( str );
+    char *ret = (char *)malloc( len+1 );
+    for( unsigned int i = 0; i < len; i++ )
+        ret[i] = str[len-i-1];
+    ret[len] = '\0';
+    return ret;
+}
+
+static int chunk_index_add( uint32_t *chunk_index, int32_t val )
+{
+    *chunk_index += val;
+    return VLC_SUCCESS;
+}
+
+static segment_t *get_segment( stream_t *s, uint32_t chunk_index, bool wait )
+{
+    stream_sys_t *p_sys = s->p_sys;
+    int count;
+    segment_t *segment = NULL;
+
+    vlc_mutex_lock( &p_sys->download.lock_wait );
+    count = vlc_array_count( p_sys->download.dld_chunks );
+    if( chunk_index >= (uint32_t)count )
+    {
+        if( !wait )
+            goto fail;
+
+        vlc_cond_wait( &p_sys->download.wait, &p_sys->download.lock_wait );
+        vlc_mutex_unlock( &p_sys->download.lock_wait );
+        return get_segment( s, chunk_index, true );
+
+    }
+    segment = vlc_array_item_at_index( p_sys->download.dld_chunks, chunk_index );
+    vlc_mutex_unlock( &p_sys->download.lock_wait );
+    return segment;
+
+fail:
+    vlc_mutex_unlock( &p_sys->download.lock_wait );
+    msg_Warn( s, "get_segment failed! (chunk_index %"PRIu32")", chunk_index );
+    return NULL;
+}
+
+/******************************************************************************
+ *  Download thread
+ *****************************************************************************/
+
+static char *ConstructUrl( const char *template, const char *base_url,
+        uint64_t bandwidth, uint64_t start_time )
+{
+    char *frag;
+    char *end;
+    char *qual;
+    char *url_template = strdup( template );
+    qual = strtok( url_template, "{" );
+    strtok( NULL, "}" );
+    frag = strtok( NULL, "{" );
+    strtok( NULL, "}" );
+    end = strtok( NULL, "" );
+    char *url = NULL;
+
+    if( asprintf( &url, "%s/%s%"PRIu64"%s%"PRIu64"%s", base_url, qual,
+                bandwidth, frag, start_time, end) < 0 )
+       return NULL;
+
+    free( url_template );
+    return url;
+}
+
+static segment_t * chunk_Get( stream_t *s,
+        sms_stream_t *sms, uint64_t start_time )
+{
+    int len = vlc_array_count( sms->segments );
+    for( int i = 0; i < len; i++ )
+    {
+        segment_t * chunk = vlc_array_item_at_index( sms->segments, i );
+        if( chunk->start_time <= start_time &&
+                chunk->start_time + chunk->duration > start_time )
+            return chunk;
+    }
+    msg_Warn( s, "Could not find a chunk for stream %s, start time = %"PRIu64"",
+           sms->name, start_time );
+    return NULL;
+}
+
+static int sms_Download( stream_t *s, segment_t *chunk, char *url )
+{
+    assert( chunk );
+    stream_sys_t *p_sys = s->p_sys;
+
+    msg_Dbg( s, "chunk url is %s\n", url );
+    if( url == NULL )
+        return VLC_ENOMEM;
+
+    stream_t *p_ts = stream_UrlNew( s, url );
+    free( url );
+    if( p_ts == NULL )
+        return VLC_EGENERIC;
+
+    chunk->size = stream_Size( p_ts );
+    assert( chunk->size > 0 );
+
+    chunk->data = block_Alloc( chunk->size );
+    if( chunk->data == NULL )
+    {
+        stream_Delete( p_ts );
+        return VLC_ENOMEM;
+    }
+
+    int length = 0, curlen = 0;
+    uint64_t size;
+    do
+    {
+        size = stream_Size( p_ts );
+        if( size > chunk->size )
+        {
+            msg_Dbg( s, "size changed %"PRIu64"", chunk->size );
+            block_t *p_block = block_Realloc( chunk->data, 0, size );
+            if( p_block == NULL )
+            {
+                stream_Delete( p_ts );
+                block_Release( chunk->data );
+                chunk->data = NULL;
+                return VLC_ENOMEM;
+            }
+            chunk->data = p_block;
+            chunk->size = size;
+            p_block = NULL;
+        }
+        length = stream_Read( p_ts, chunk->data->p_buffer + curlen,
+                chunk->size - curlen );
+        if( length <= 0 )
+            break;
+        curlen += length;
+    } while( 1 );
+
+    vlc_mutex_lock( &p_sys->download.lock_wait );
+    vlc_array_append( p_sys->download.dld_chunks, chunk );
+    vlc_mutex_unlock( &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;
+    stream_Delete( p_ts );
+
+    return VLC_SUCCESS;
+}
+
+#ifdef DISABLE_BANDWIDTH_ADAPTATION
+static quality_level_t * BandwidthAdaptation( stream_t *s,
+        sms_stream_t *sms, uint64_t *bandwidth )
+{
+    return sms->download_qlvl;
+}
+#else
+
+static quality_level_t * BandwidthAdaptation( stream_t *s,
+        sms_stream_t *sms, uint64_t *bandwidth )
+{
+    if( sms->type == AUDIO_ES )
+        return sms->download_qlvl;
+    uint64_t bw_candidate = 0;
+    quality_level_t *qlevel;
+    quality_level_t *ret = NULL;
+    msg_Dbg( s, "bw is %"PRIu64"", *bandwidth );
+
+    for( unsigned i = 0; i < sms->qlevel_nb; i++ )
+    {
+        qlevel = vlc_array_item_at_index( sms->qlevels, i );
+        if( qlevel->Bitrate < *bandwidth &&
+                qlevel->Bitrate > bw_candidate )
+        {
+            bw_candidate = qlevel->Bitrate;
+            ret = qlevel;
+        }
+    }
+    if( bw_candidate )
+        *bandwidth = bw_candidate;
+    return ret;
+}
+#endif
+
+/* Set track ID *and* quality id */
+uint16_t set_track_id( segment_t *chunk, uint16_t tid, uint16_t qid )
+{
+    uint8_t *slice = chunk->data->p_buffer;
+    for( size_t stop = chunk->data->i_buffer; stop > 0; )
+    {
+        slice = memchr( slice , 't', stop );
+        if( slice == NULL )
+            return 0;
+        if( slice[0] == 't' && slice[1] == 'f' &&
+            slice[2] == 'h' && slice[3] == 'd' )
+            break;
+        else
+        {
+            stop = (size_t)chunk->data->i_buffer - 1 -
+                    (size_t)(slice - chunk->data->p_buffer);
+            slice++;
+        }
+    }
+    uint16_t ret = slice[11] + (slice[10]<<8);
+    slice[11] = tid & 0xff;
+    slice[10] = (tid & 0xff00)>>8;
+
+    slice[9] = qid & 0xff;
+    slice[8] = (qid & 0xff00)>>8;
+    return ret;
+}
+
+static int Download( stream_t *s, sms_stream_t *sms, uint64_t *next_chunk_offset )
+{
+    stream_sys_t *p_sys = s->p_sys;
+
+    assert( sms );
+
+    uint64_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;
+
+    quality_level_t *qlevel = sms->download_qlvl;
+    segment_t *segment = chunk_Get( s, sms, start_time );
+    if( !segment )
+        return VLC_EGENERIC;
+
+    vlc_mutex_lock( &segment->lock );
+    if( segment->data != NULL )
+    {
+        /* Segment already downloaded */
+        vlc_mutex_unlock( &segment->lock );
+        return VLC_SUCCESS;
+    }
+
+    segment->bandwidth = sms->download_qlvl->Bitrate;
+    segment->type = sms->type;
+    char *url = ConstructUrl( sms->url_template,
+                                 p_sys->base_url,
+                                 sms->download_qlvl->Bitrate,
+                                 segment->start_time );
+
+    /* sanity check - can we download this segment on time? */
+    if( (p_sys->bandwidth > 0) && (qlevel->Bitrate > 0) )
+    {
+        uint32_t sgmt_duration = segment->duration / TS; /* duration in seconds */
+        uint64_t size = (sgmt_duration * qlevel->Bitrate); /* bits */
+        uint32_t estimated = (uint32_t)(size / p_sys->bandwidth);
+        if( estimated > sgmt_duration )
+        {
+            msg_Warn( s,"downloading of segment %d takes %ds,\
+                    which is longer than its playback (%ds)",
+                        segment->sequence, estimated, sgmt_duration );
+        }
+    }
+
+    mtime_t start = mdate();
+    if( sms_Download( s, segment, url ) != VLC_SUCCESS )
+    {
+        msg_Err( s, "downloaded segment %"PRIu32" from stream %s at quality\
+            %"PRIu64" failed", segment->sequence, sms->name, qlevel->Bitrate );
+        vlc_mutex_unlock( &segment->lock );
+        return VLC_EGENERIC;
+    }
+    mtime_t duration = mdate() - start;
+
+    segment->offset = *next_chunk_offset;
+    *next_chunk_offset += segment->size;
+    uint16_t real_id = set_track_id( segment, sms->id, qlevel->id );
+    msg_Info( s, "chunk ID was %i and is now %i", real_id, sms->id );
+    vlc_mutex_unlock( &segment->lock );
+
+    msg_Info( s, "downloaded segment %d from stream %s at quality %"PRIu64"",
+                segment->sequence, sms->name, qlevel->Bitrate );
+    if( sms->type == AUDIO_ES )
+        p_sys->download.aindex++;
+    else if( sms->type == VIDEO_ES )
+        p_sys->download.vindex++;
+    else if( sms->type == SPU_ES )
+        p_sys->download.tindex++;
+
+    /* check for division by zero */
+    double ms = (double)duration / 1000.0; /* ms */
+    if( ms <= 0.0)
+        return VLC_SUCCESS;
+
+    uint64_t bw = ((double)(segment->size * 8) / ms) * 1000; /* bits / s */
+    p_sys->bandwidth = bw;
+    if( qlevel->Bitrate != bw && segment->sequence > 1 )
+    {
+        quality_level_t *new_qlevel = BandwidthAdaptation( s, sms, &bw );
+
+        /* FIXME: we need an average here */
+        if( (new_qlevel) && (new_qlevel->Bitrate != qlevel->Bitrate) )
+        {
+            msg_Warn( s, "detected %s bandwidth (%"PRIu64") stream",
+                     (bw >= qlevel->Bitrate) ? "faster" : "lower", bw );
+            sms->download_qlvl = new_qlevel;
+        }
+    }
+    return VLC_SUCCESS;
+}
+
+static void* sms_Thread( void *p_this )
+{
+    stream_t *s = (stream_t *)p_this;
+    stream_sys_t *p_sys = s->p_sys;
+    uint64_t lead=0, time_left=0;
+    uint64_t next_chunk_offset = 0;
+    sms_stream_t *vsms, *asms;
+    vsms = p_sys->vstream;
+    asms = p_sys->astream;
+    assert( vsms );
+    assert( asms );
+
+    int canc = vlc_savecancel();
+
+    segment_t *first_video_chunk = vlc_array_item_at_index( vsms->segments, 0 );
+    segment_t *first_audio_chunk = vlc_array_item_at_index( asms->segments, 0 );
+    if( p_sys->b_live )
+    {
+        p_sys->download.vlead = first_video_chunk->start_time +
+            3 * first_video_chunk->duration;
+        bool disable_audio = var_CreateGetBool( s, "smooth-disable-audio" );
+        if( disable_audio )
+            p_sys->download.alead = UINT64_MAX;
+        else
+            p_sys->download.alead = first_audio_chunk->start_time +
+                                        3 * first_audio_chunk->duration;
+    }
+
+    if( Download( s, vsms, &next_chunk_offset ) != VLC_SUCCESS )
+    {
+        if( Download( s, asms, &next_chunk_offset ) != VLC_SUCCESS )
+        {
+            p_sys->b_error = true;
+            goto cancel;
+        }
+    }
+
+    while( vlc_object_alive( s ) )
+    {
+        /* Is there a new segment to process? */
+        if( p_sys->download.aindex >= (asms->vod_chunks_nb - 1) &&
+            p_sys->download.vindex >= (vsms->vod_chunks_nb - 1) &&
+                                                    !p_sys->b_live )
+           break;
+        time_left = p_sys->vod_duration - p_sys->playback.toffset;
+        if( time_left > 60*TS || p_sys->b_live )
+        {
+            /* wait */
+            vlc_mutex_lock( &p_sys->download.lock_wait );
+            if( p_sys->download.vlead >= p_sys->playback.toffset )
+                lead = p_sys->download.vlead - p_sys->playback.toffset;
+            else
+                lead = 0;
+            while( lead > 30*TS + first_video_chunk->start_time )
+            {
+                msg_Dbg( s, "sms_Thread is waiting!" );
+                vlc_cond_wait( &p_sys->download.wait,
+                        &p_sys->download.lock_wait );
+                lead = p_sys->download.vlead - p_sys->playback.toffset;
+            }
+            vlc_mutex_unlock( &p_sys->download.lock_wait );
+        }
+
+        if( p_sys->download.alead <= p_sys->download.vlead )
+            if( Download( s, asms, &next_chunk_offset ) != VLC_SUCCESS )
+            {
+                    p_sys->b_error = true;
+                    break;
+            }
+
+        if( p_sys->download.vlead < p_sys->download.alead )
+            if( Download( s, vsms, &next_chunk_offset) != VLC_SUCCESS )
+            {
+                    p_sys->b_error = true;
+                    break;
+            }
+
+        /* download succeeded */
+        vlc_mutex_lock( &p_sys->download.lock_wait );
+        vlc_cond_signal( &p_sys->download.wait );
+        vlc_mutex_unlock( &p_sys->download.lock_wait );
+    }
+
+cancel:
+    msg_Warn(s, "Canceling download thread!");
+    vlc_restorecancel( canc );
+    return NULL;
+}
+
+static int sms_ReloadManifest( stream_t *s )
+{
+    stream_sys_t *p_sys = s->p_sys;
+    int ret;
+    uint64_t tmp = p_sys->playlist.a_start_time;
+
+    msg_Info(s, "Reload Manifest" );
+    ret = parse_Manifest(s, true );
+    if( p_sys->playlist.a_start_time > tmp )
+        return ret;
+    else
+        return VLC_EGENERIC;
+}
+
+static void* sms_Reload( void *p_this )
+{
+    stream_t *s = (stream_t *)p_this;
+    stream_sys_t *p_sys = s->p_sys;
+
+    assert( p_sys->b_live );
+
+    int canc = vlc_savecancel();
+
+    double wait = 10;
+    while( vlc_object_alive( s ) )
+    {
+        mtime_t now = mdate();
+        if( now >= p_sys->playlist.wakeup )
+        {
+            /* reload the m3u8 */
+            if( sms_ReloadManifest( s ) != VLC_SUCCESS )
+            {
+                /* No change in playlist, then backoff */
+                p_sys->playlist.tries++;
+                if( p_sys->playlist.tries == 1 ) wait = 5;
+                else if( p_sys->playlist.tries == 2 ) wait = 10;
+                else if( p_sys->playlist.tries >= 3 ) wait = 25;
+
+                /* Can we afford to backoff? */
+                uint32_t download_index = p_sys->download.aindex
+                                                + p_sys->download.vindex;
+                if( download_index - p_sys->playback.index < 3 )
+                {
+                    p_sys->playlist.tries = 0;
+                    wait = 1;
+                }
+            }
+            else
+            {
+                p_sys->playlist.tries = 0;
+                wait = 10;
+            }
+
+            /* determine next time to update playlist */
+            uint32_t segment_duration = 2;
+            p_sys->playlist.last = now;
+            p_sys->playlist.wakeup = now + ((mtime_t)( segment_duration * wait )
+                                                   * (mtime_t)1000000);
+        }
+
+        mwait( p_sys->playlist.wakeup );
+    }
+
+    vlc_restorecancel( canc );
+    return NULL;
+}
+
+sms_stream_t *get_sms_stream( stream_t *s, uint16_t tid )
+{
+    stream_sys_t *p_sys = s->p_sys;
+    sms_stream_t *sms = NULL;
+    int stop = p_sys->i_tracks;
+    for( int i = 0; i < stop; i++ )
+    {
+        sms = vlc_array_item_at_index( p_sys->sms_streams, i );
+        if( sms->id == tid )
+            break;
+        else if( i == stop - 1 )
+            return NULL;
+    }
+    return sms;
+}
+
+quality_level_t *get_qlevel( sms_stream_t *sms, uint16_t qid )
+{
+    quality_level_t *qlevel = NULL;
+    for( unsigned i = 0; i < sms->qlevel_nb; i++ )
+    {
+        qlevel = vlc_array_item_at_index( sms->qlevels, i );
+        if( qlevel->id == qid )
+            break;
+        else if( i == sms->qlevel_nb - 1 )
+            return NULL;
+    }
+    return qlevel;
+}
+
+static int sms_FmtCreate( stream_t *s, uint16_t tid, uint16_t qid,
+                                                        es_format_t *fmt )
+{
+    sms_stream_t *sms = get_sms_stream( s, tid );
+    if( sms == NULL )
+    {
+        msg_Err( s, "Could not find track with ID %"PRIu16"", tid );
+        return VLC_EGENERIC;
+    }
+
+    quality_level_t *qlevel = get_qlevel( sms, qid );
+    if( qlevel == NULL )
+    {
+        msg_Err( s, "Couldn't find qlevel with ID %"PRIu16" in track %"PRIu16"",
+                                                                     qid, tid );
+        return VLC_EGENERIC;
+    }
+
+    char language[4] = "en";
+    es_format_Init( fmt, sms->type, 0 );
+
+    /* Set language */
+    if( *language && strcmp( language, "```" ) && strcmp( language, "und" ) )
+    {
+        fmt->psz_language = strdup( language );
+    }
+
+    switch( fmt->i_cat )
+    {
+        case VIDEO_ES:
+            if( qlevel->FourCC == H264 || qlevel->FourCC == AVC1 )
+            {
+                fmt->i_extra = build_raw_avcC( (uint8_t **)&fmt->p_extra,
+                                                    qlevel->CodecPrivateData );
+                fmt->i_codec = ATOM_avc1;
+            }
+            else
+            {
+                fmt->i_extra = build_raw_esds( (uint8_t **)&fmt->p_extra,
+                                                    qlevel->CodecPrivateData );
+                fmt->i_codec = WVC1;
+            }
+
+            fmt->video.i_width = qlevel->MaxWidth;
+            fmt->video.i_height = qlevel->MaxHeight;
+            fmt->video.i_bits_per_pixel = 0x18;
+            fmt->video.i_visible_width = qlevel->MaxWidth;
+            fmt->video.i_visible_height = qlevel->MaxHeight;
+            break;
+
+        case AUDIO_ES:
+            fmt->i_extra = build_raw_esds( (uint8_t **)&fmt->p_extra,
+                                                qlevel->CodecPrivateData );
+            if( qlevel->FourCC == AACH || qlevel->FourCC == AACL )
+                fmt->i_codec = ATOM_mp4a;
+            else
+                fmt->i_codec = WMAP;
+            fmt->audio.i_channels = qlevel->Channels;
+            fmt->audio.i_rate = qlevel->SamplingRate;
+            fmt->audio.i_bitspersample = qlevel->BitsPerSample;
+            break;
+
+        default:
+            break;
+    }
+
+    return VLC_SUCCESS;
+}
+
+/****************************************************************************
+ * sms_TrackCreate:
+ ****************************************************************************
+ * Parse track information and create all needed data to run a track
+ * If it succeed b_ok is set to 1 else to 0
+ ****************************************************************************/
+static int sms_TrackCreate( stream_t *s, uint16_t tid,
+                                                     mp4_track_t *p_track )
+{
+    sms_stream_t *sms = get_sms_stream( s, tid );
+    if( sms == NULL )
+    {
+        msg_Err( s, "Could not find track with ID %"PRIu32"", tid );
+        return VLC_EGENERIC;
+    }
+
+#if 1
+    if( tid == 2 )
+        p_track->b_enable = true;
+    else
+        p_track->b_enable = true;
+#endif
+
+    p_track->b_ok       = true;
+    p_track->b_selected = false;
+    p_track->b_chapter  = false;
+    p_track->b_drms     = false;
+    p_track->b_mac_encoding = false;
+    p_track->p_es = NULL;
+    p_track->i_sample_size = 0;
+    p_track->i_chunk  = 0;
+    p_track->i_sample_count = UINT32_MAX;
+
+    quality_level_t * qlevel = sms->download_qlvl;
+    if( !qlevel )
+        qlevel = vlc_array_item_at_index( sms->qlevels, 0 );
+    p_track->i_width = qlevel->MaxWidth;
+    p_track->i_height = qlevel->MaxHeight;
+    p_track->current_qid = qlevel->id;
+    p_track->i_timescale = TS; /*FIXME*/
+    p_track->i_track_ID = sms->id;
+    p_track->i_sample_count = UINT32_MAX;
+
+    sms_FmtCreate( s, sms->id, qlevel->id, &p_track->fmt );
+
+    return VLC_SUCCESS;
+}
+
+/* Luc Saillard code */
+static int hex_digit( char c )
+{
+    if (c >= 'A' && c <= 'F')
+        return c - 'A' + 10;
+    else if (c >= 'a' && c <= 'f')
+        return c - 'a' + 10;
+    else if (c >= '0' && c<= '9')
+        return c - '0';
+    else
+        return -1;
+}
+
+/* Luc Saillard code */
+static uint8_t *decode_string_hex_to_binary( const char *psz_src )
+{
+    int i=0, j=0, first_digit, second_digit;
+    int i_len = strlen( psz_src );
+    uint8_t *p_data = malloc ( i_len/2 );
+
+    if( !p_data )
+        return NULL;
+
+    while( i < i_len )
+    {
+        first_digit = hex_digit( psz_src[i++] );
+        second_digit = hex_digit( psz_src[i++] );
+        p_data[j++] = ( first_digit << 4 ) | second_digit;
+    }
+
+    return p_data;
+}
+
+/* actually just the CodecPrivateData */
+int build_raw_esds( uint8_t **p_extra, const char *CodecPrivateData )
+{
+    uint8_t *esds;
+    uint8_t *codec_data = decode_string_hex_to_binary( CodecPrivateData );
+    size_t codec_data_length = strlen( CodecPrivateData ) / 2;
+    esds = calloc( 1, codec_data_length );
+    if( unlikely( esds == NULL ) )
+        return 0;
+
+    memcpy( esds, codec_data, codec_data_length );
+
+    *p_extra = esds;
+    return codec_data_length;
+}
+
+/* raw avcC box without the 8 bytes header */
+int build_raw_avcC( uint8_t **p_extra, const char *CodecPrivateData )
+{
+    uint8_t *avcC;
+    const char *mark = "00000001";
+    char head[8];
+    char *pos;
+    char tmp[512];
+    char sps_nal_unit[512], pps_nal_unit[512];
+
+    strncpy( head, CodecPrivateData, 8);
+    assert( !strcmp( head, mark ) );
+    strncpy( tmp, CodecPrivateData + 8, 512 );
+    pos = strstr( tmp, mark );
+    if( pos == NULL )
+        return 0;
+    strncpy( pps_nal_unit, pos + 8, 512 );
+    size_t len = (size_t)(pos - tmp);
+    assert( len < 512 );
+    strncpy( sps_nal_unit, tmp, len );
+    sps_nal_unit[len] = '\0';
+    uint8_t *sps = decode_string_hex_to_binary( sps_nal_unit );
+    uint8_t *pps = decode_string_hex_to_binary( pps_nal_unit );
+
+    uint32_t length = 8 + (strlen( CodecPrivateData ) - 16) / 2 + 11;
+    avcC = calloc( length, 1 );
+    if( unlikely( avcC == NULL ) )
+        return 0;
+
+    uint8_t AVCProfileIndication = 0x64;
+    uint8_t profile_compatibility = 0x40;
+    uint8_t AVCLevelIndication = 0x1f;
+    uint8_t lengthSizeMinusOne = 0x03;
+
+    int sps_len = strlen(sps_nal_unit) / 2;
+    int pps_len = strlen(pps_nal_unit) / 2;
+
+    avcC[0] = 1;
+    avcC[1] = AVCProfileIndication;
+    avcC[2] = profile_compatibility;
+    avcC[3] = AVCLevelIndication;
+    avcC[4] = 0xfc + lengthSizeMinusOne;
+    avcC[5] = 0xe0 + 1;
+    avcC[6] = (sps_len & 0xff00)>>8;
+    avcC[7] = sps_len & 0xff;
+    memcpy( avcC+8, sps, sps_len );
+
+    avcC[8+sps_len] = 1;
+    avcC[9+sps_len] = (pps_len & 0xff00)>>8;
+    avcC[10+sps_len] = pps_len & 0xff;
+    memcpy( avcC+11+sps_len, pps, pps_len );
+
+    *p_extra = avcC;
+    return length;
+}
-- 
1.7.5.4




More information about the vlc-devel mailing list