From 8c1a9101b2c17f8ad3aee48880f38131732d45f3 Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?Fr=C3=A9d=C3=A9ric=20Yhuel?= <fyhuel@viotech.net>
Date: Wed, 21 Dec 2011 10:31:47 +0100
Subject: [PATCH] Add Smooth Streaming module

This is a "alpha" version. No adaptation, no DRMs, and others
limitations.

As you can see, it is heavily inspired by HLS module of Jean-Paul Saman.

Moreover, you need to have a Python script installed in
/usr/local/share/ to test it. Non Linux users may adapt the code. The
script is available in a public git repository:

git clone http://46.105.6.148:81/vlcScriptsViotech.git

Note: this script is quite ugly, at least for some parts. Patches and
comments are welcome, though it will be eventually moved into C code.
A work on the MP4 demuxer of VLC has started in order to get rid of it.

If all goes well, it should work with Anevia Smooth Streaming demo
server:

***** 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
---
 modules/stream_filter/Modules.am |    2 +
 modules/stream_filter/smooth.c   | 1368 ++++++++++++++++++++++++++++++++++++++
 2 files changed, 1370 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 a864eba..183853e 100644
--- a/modules/stream_filter/Modules.am
+++ b/modules/stream_filter/Modules.am
@@ -3,10 +3,12 @@ SUBDIRS = dash
 SOURCES_decomp = decomp.c
 SOURCES_stream_filter_record = record.c
 SOURCES_stream_filter_httplive = httplive.c
+SOURCES_stream_filter_smooth = smooth.c
 
 libvlc_LTLIBRARIES += \
    libstream_filter_record_plugin.la \
    libstream_filter_httplive_plugin.la \
+   libstream_filter_smooth_plugin.la \
    $(NULL)
 if !HAVE_WIN32
 if !HAVE_WINCE
diff --git a/modules/stream_filter/smooth.c b/modules/stream_filter/smooth.c
new file mode 100644
index 0000000..576a0c8
--- /dev/null
+++ b/modules/stream_filter/smooth.c
@@ -0,0 +1,1368 @@
+/*****************************************************************************
+ * smooth.c: Smooth Streaming stream filter
+ *****************************************************************************
+ * Copyright (C) 1996-2011 VLC authors and VideoLAN
+ * $Id$
+ *
+ * Author: Frédéric Yhuel <fyhuel _AT_ viotech _DOT_ net>
+ *
+ * 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
+ *****************************************************************************/
+
+/*****************************************************************************
+ * Preamble
+ *****************************************************************************/
+#ifdef HAVE_CONFIG_H
+# include "config.h"
+#endif
+
+#include <limits.h>
+#include <errno.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>
+
+/* In all the file, "segment" and "chunk" both refer to the same thing,
+ * and are used intechangeably. The same goes for "bitrate" and "bandwidth". */
+
+/*****************************************************************************
+ * 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 stream filter") )
+    set_capability( "stream_filter", 20 )
+    set_callbacks( Open, Close )
+vlc_module_end()
+
+/*****************************************************************************
+ *
+ *****************************************************************************/
+
+/* Time Scale, I've never seen another value, but it may happen */
+#define TS 10000000
+/* Lowest video bitrate wanted. You can also set to 0 to get the lowest available,
+ * and to 1 in order to get the highest */
+#define BITRATE 50000UL
+#define DISABLE_BANDWIDTH_ADAPTATION
+
+/* If want to disable audio, you also need to comment a line in
+ *  the Python scricpt: "moov += atrak" */
+//#define DISABLE_AUDIO
+
+enum Type {
+    NULL_TYPE=0,
+    VIDEO,
+    AUDIO,
+    TEXT,
+};
+
+enum FourCC {
+    NULL_CC=0,
+    H264 = VLC_FOURCC( 'H', '2', '6', '4' ),
+    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 */
+    int         peek_pos;   /* position in the segment for Peek() */
+    int         read_pos;   /* position in the segment for Read() */
+    enum Type   type;       /* video or audio ? */
+
+    vlc_mutex_t lock;
+    block_t     *data;      /* data */
+} segment_t;
+
+typedef struct quality_level_s
+{
+    int         Index;
+    enum FourCC FourCC;
+    uint64_t    Bitrate;
+    uint32_t    MaxWidth;
+    uint32_t    MaxHeight;
+    uint32_t    SamplingRate;
+    uint32_t    Channels;
+    uint32_t    BitsPerSample;
+    uint32_t    PacketSize;
+    uint32_t    AudioTag;
+    uint8_t    *CodecPrivateData;
+
+} 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 */
+    char        *name;
+    char        *url_template;
+    enum Type    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;
+
+    /* */
+    segment_t    *init_sgmt;    /* ftyp + moov */
+    vlc_array_t  *sms_streams;  /* array of sms_stream_t */
+    vlc_array_t  *dld_chunks;   /* segments that have been downloaded*/
+    sms_stream_t *vstream;      /* current video stream  */
+    sms_stream_t *astream;      /* current audio stream  */
+    sms_stream_t *tstream;      /* current text stream  */
+    int           stream_nb;
+    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_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_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 );
+
+static char *inverse_string( const char *);
+static int sms_Download( stream_t *, segment_t *, char *);
+static int Download( stream_t *, uint64_t, sms_stream_t *, quality_level_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 = (quality_level_t *)malloc( sizeof( quality_level_t ) );
+    if( ql == NULL )
+        return NULL;
+    ql->Index = -1;
+    ql->FourCC = NULL_CC;
+    ql->Bitrate = 0;
+    ql->CodecPrivateData = NULL;
+    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 = (segment_t *)malloc( sizeof( segment_t ) );
+    if( segment == NULL )
+        return NULL;
+
+    segment->duration = duration;
+    segment->start_time = start_time;
+    segment->size = 0;
+    segment->bandwidth = 0;
+    segment->data = NULL;
+    segment->peek_pos = 0;
+    segment->read_pos = 0;
+    segment->type = NULL_TYPE;
+    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 = (sms_stream_t *)malloc( sizeof( sms_stream_t ) );
+    if( sms == NULL )
+        return NULL;
+    sms->qlevels = vlc_array_new();
+    sms->segments = vlc_array_new();
+    sms->vod_chunks_nb = 0;
+    sms->qlevel_nb = 0;
+    sms->name = NULL;
+    sms->url_template = NULL;
+    sms->type = NULL_TYPE;
+    sms->download_qlvl = NULL;
+    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;
+    enum Type type = NULL_TYPE;
+    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;
+    enum Type stream_type = NULL_TYPE;
+    sms_stream_t *sms = NULL;
+    quality_level_t *ql = NULL;
+    uint64_t start_time = 0;
+    uint64_t *last_stime;
+    uint64_t duration = 0;
+    uint64_t computed_start_time = 0;
+    uint64_t computed_duration = 0;
+
+    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();
+
+                    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;
+                            else if( !strcmp( value, "audio" ) )
+                                stream_type = AUDIO;
+                            else if( !strcmp( value, "text" ) )
+                                stream_type = TEXT;
+                        }
+
+                        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( type == VIDEO )
+                            stream_name = strdup( "video" );
+                        else if( type == AUDIO )
+                            stream_name = strdup( "audio" );
+                        else if( type == TEXT )
+                            stream_name = strdup( "text" );
+                    }
+
+                    if( !update )
+                    {
+                        sms->name = stream_name;
+                        sms->type = stream_type;
+                        vlc_array_append( p_sys->sms_streams, sms );
+                    }
+                    else
+                        for( int i = 0; i < p_sys->stream_nb; 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->stream_nb - 1 )
+                                msg_Err( s, "Updating of the manifest failed" );
+                        }
+                }
+
+                if( !strcmp( node, "QualityLevel" ) && !update )
+                {
+                    ql = ql_New();
+                    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] );
+                    }
+                    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 )
+                            last_stime = &(p_sys->playlist.v_start_time);
+                    else if( sms->type == AUDIO )
+                            last_stime = &(p_sys->playlist.a_start_time);
+
+                    if( !computed_start_time ||
+                            (computed_start_time > *last_stime) )
+                    {
+                        segment_New( sms, computed_duration,
+                                                    computed_start_time );
+                        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 = NULL_TYPE;
+                    computed_start_time = 0;
+                    computed_duration = 0;
+
+                    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 build_init_segment( stream_t *s, char *uri, uint64_t bitrate,
+                                                                bool update )
+{
+    stream_sys_t *p_sys = s->p_sys;
+    char *cmd = NULL;
+    if( asprintf(&cmd, "/usr/local/share/build_moov_box.py %s %"PRIu64" %i",
+            uri, bitrate, update) < 0 )
+        return VLC_ENOMEM;
+    int ret = system( cmd );
+    if( ret )
+    {
+        msg_Err( s, "Building of initialization segment failed\n" );
+        return VLC_EGENERIC;
+    }
+    p_sys->init_sgmt = (segment_t *)malloc( sizeof( segment_t ) );
+    if( p_sys->init_sgmt == NULL)
+        return VLC_ENOMEM;
+
+    const char *spath = "/tmp/piff_initialization_segment";
+    FILE *fp = fopen( spath, "rb" );
+
+    /* compute the size */
+    fseek( fp, 0L, SEEK_END );
+    p_sys->init_sgmt->size = (uint64_t)ftell( fp );
+    fseek( fp, 0L, SEEK_SET );
+
+    p_sys->init_sgmt->duration = 0;
+    p_sys->init_sgmt->start_time = 0;
+    p_sys->init_sgmt->bandwidth = 0;
+    p_sys->init_sgmt->sequence = 0;
+    p_sys->init_sgmt->peek_pos = 0;
+    p_sys->init_sgmt->read_pos = 0;
+    vlc_mutex_init( &p_sys->init_sgmt->lock );
+    p_sys->init_sgmt->data = block_Alloc( p_sys->init_sgmt->size );
+    if( p_sys->init_sgmt->data == NULL )
+        return VLC_ENOMEM;
+
+    size_t read = fread( p_sys->init_sgmt->data->p_buffer, 1,
+           (size_t) p_sys->init_sgmt->size, fp );
+    if( read != (size_t)p_sys->init_sgmt->size )
+        return VLC_EGENERIC;
+    fclose( fp );
+
+    return VLC_SUCCESS;
+}
+
+
+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;
+
+    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;
+#ifdef DISABLE_AUDIO
+    p_sys->download.alead = UINT64_MAX;
+#else
+    p_sys->download.alead = 0;
+#endif
+    p_sys->download.tlead = 0;
+    p_sys->playback.boffset = 0;
+    /* UINT32_MAX is the index value of the initialization segment */
+    p_sys->playback.index = UINT32_MAX;
+    p_sys->peek_index = UINT32_MAX;
+    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->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;
+
+    build_init_segment( s, uri, BITRATE, false );
+
+    /* 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->stream_nb = vlc_array_count( p_sys->sms_streams );
+
+    /* Choose first video stream available (TO FIX) */
+    sms_stream_t *vsms = NULL;
+    for( int i=0; i<p_sys->stream_nb; i++ )
+    {
+        vsms = vlc_array_item_at_index( p_sys->sms_streams, i );
+        if( vsms->type == VIDEO )
+        {
+            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 ( int i=0; i<p_sys->stream_nb; i++ )
+    {
+        asms = vlc_array_item_at_index( p_sys->sms_streams, i );
+        //if( asms->type == AUDIO && !strcmp( asms->name, "audio_eng" ) )
+        if( asms->type == AUDIO )
+        {
+            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(  (BITRATE == 0UL) && (qlvl->Bitrate < wanted->Bitrate) )
+            wanted = qlvl;
+        else if(  (BITRATE == 1UL) && (qlvl->Bitrate > wanted->Bitrate) )
+            wanted = qlvl;
+        else if(  (BITRATE >= 2) && (((qlvl->Bitrate >= BITRATE) &&
+                                       qlvl->Bitrate < wanted->Bitrate) ||
+                                        wanted->Bitrate < 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 */
+    segment_Free( p_sys->init_sgmt );
+    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 )
+        return 0;
+
+    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 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 * )) = false;
+            break;
+        case STREAM_GET_POSITION:
+            //*(va_arg( args, uint64_t * )) = p_sys->playback.boffset;
+            //break;
+        case STREAM_SET_POSITION:
+            return VLC_EGENERIC;
+        case STREAM_GET_SIZE:
+            *(va_arg( args, uint64_t * )) = GetStreamSize( s );
+            break;
+        default:
+            return VLC_EGENERIC;
+    }
+    return VLC_SUCCESS;
+}
+
+static ssize_t sms_Read( stream_t *s, uint8_t *p_read, unsigned int i_read,
+                                                                   bool peek )
+{
+    stream_sys_t *p_sys = s->p_sys;
+    ssize_t copied = 0;
+    segment_t *segment = NULL;
+    int count;
+    uint32_t *chunk_index;
+    int *position;
+
+    if( peek )
+        chunk_index = &(p_sys->peek_index);
+    else
+        chunk_index = &(p_sys->playback.index);
+
+    do
+    {
+        if( *chunk_index == UINT32_MAX )
+            segment = p_sys->init_sgmt;
+        else
+        {
+            vlc_mutex_lock( &p_sys->download.lock_wait );
+            count = vlc_array_count( p_sys->dld_chunks );
+            if( count == 0 || (int)*chunk_index >= count-1 )
+            {
+                vlc_cond_wait( &p_sys->download.wait,
+                        &p_sys->download.lock_wait );
+                vlc_mutex_unlock( &p_sys->download.lock_wait );
+                continue;
+            }
+            segment = vlc_array_item_at_index( p_sys->dld_chunks, *chunk_index );
+            vlc_mutex_unlock( &p_sys->download.lock_wait );
+        }
+
+        if( peek )
+            position = &(segment->peek_pos);
+        else
+            position = &(segment->read_pos);
+
+        vlc_mutex_lock( &segment->lock);
+
+
+        if( *position >= (int)segment->size )
+        {
+            if( segment->type == VIDEO && !peek )
+                p_sys->playback.toffset += segment->duration;
+
+            if( !peek && (!p_sys->b_cache || p_sys->b_live) )
+            {
+                block_Release( segment->data );
+                segment->data = NULL;
+            }
+            else if( segment->size > 0 )
+                *position = 0;
+
+            if( *chunk_index == UINT32_MAX )
+                *chunk_index = 0;
+            else
+                *chunk_index += 1;
+            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( !peek && *position == 0 )
+            msg_Info( s, "playing segment %"PRIu32", bandwidth %"PRIu64"",
+                     segment->sequence, segment->bandwidth );
+
+        ssize_t 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)
+        {
+            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 int i_read )
+{
+    msg_Dbg( s, "Demuxer want to read %u bytes", i_read );
+    stream_sys_t *p_sys = s->p_sys;
+    ssize_t length = 0;
+
+    if( p_sys->b_error )
+        return 0;
+
+    if( buffer == NULL )
+    {
+        /* caller skips data, get big enough buffer */
+        msg_Dbg( s, "buffer is NULL (allocate %d)", i_read );
+        buffer = calloc( 1, i_read );
+        if( buffer == NULL )
+            return 0; /* NO MEMORY left */
+    }
+
+    length = sms_Read( s, (uint8_t*) buffer, i_read, false );
+    if( length < 0 )
+        return 0;
+
+    p_sys->playback.boffset += 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;
+    block_t *peeked = p_sys->peeked;
+    size_t curlen = 0;
+
+    if( peeked == NULL )
+        peeked = block_Alloc ( i_peek );
+    else if( (curlen = peeked->i_buffer) < i_peek)
+        peeked = block_Realloc( peeked, 0, i_peek );
+
+    if( (p_sys->peeked = peeked) == NULL )
+        return 0;
+
+    if( curlen < i_peek )
+    {
+        int val = sms_Read( s, peeked->p_buffer + curlen, i_peek-curlen, true );
+        if( val >= 0 )
+        {
+            curlen += val;
+            peeked->i_buffer = curlen;
+        }
+    }
+
+    *pp_peek = peeked->p_buffer;
+    return curlen;
+}
+
+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;
+}
+
+/******************************************************************************
+ *  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 *tmp;
+    char *url_template = strdup( template );
+    qual = strtok( url_template, "{" );
+    tmp = strtok( NULL, "}" );
+    frag = strtok( NULL, "{" );
+    tmp = 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;
+    }
+
+    ssize_t 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->dld_chunks, chunk );
+    vlc_mutex_unlock( &p_sys->download.lock_wait );
+    if( chunk->type == AUDIO )
+        p_sys->download.alead += chunk->duration;
+    else if( chunk->type == VIDEO )
+        p_sys->download.vlead += chunk->duration;
+    else if( chunk->type == TEXT )
+        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 )
+{
+    stream_sys_t *p_sys = s->p_sys;
+    uint64_t bw_candidate = 0;
+    quality_level_t *qlevel;
+    quality_level_t *ret = NULL;
+
+    for( int 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
+
+static int Download( stream_t *s, uint64_t start_time, sms_stream_t *sms,
+        quality_level_t *qlevel )
+{
+    stream_sys_t *p_sys = s->p_sys;
+
+    assert( sms );
+    assert( qlevel );
+
+    sms->download_qlvl = qlevel;
+    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;
+
+    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 )
+        p_sys->download.aindex++;
+    else if( sms->type == VIDEO )
+        p_sys->download.vindex++;
+    else if( sms->type == TEXT )
+        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 )
+    {
+        quality_level_t *new_qlevel = BandwidthAdaptation( s, sms, &bw );
+
+        /* FIXME: we need an average here */
+        if( (new_qlevel) && (new_qlevel->Bitrate != qlevel->Bitrate) )
+        {
+            msg_Info( s, "detected %s bandwidth (%"PRIu64") stream",
+                     (bw >= qlevel->Bitrate) ? "faster" : "lower", bw );
+            sms->download_qlvl = new_qlevel;
+#if 0
+            build_init_segment( s, p_sys->ismc, new_qlevel->Bitrate, true );
+            vlc_array_append( p_sys->dld_chunks, p_sys->init_sgmt );
+            p_sys->playback.index++;
+            p_sys->peek_index++;
+#endif
+        }
+    }
+    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;
+    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;
+#ifdef DISABLE_AUDIO
+        p_sys->download.alead = UINT64_MAX;
+#else
+        p_sys->download.alead = first_audio_chunk->start_time +
+            3 * first_audio_chunk->duration;
+#endif
+    }
+
+    if( Download( s, p_sys->download.vlead, vsms, vsms->download_qlvl ) != 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 );
+            lead = p_sys->download.vlead - p_sys->playback.toffset;
+            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, p_sys->download.alead, asms,
+                        asms->download_qlvl ) != VLC_SUCCESS )
+            {
+                    p_sys->b_error = true;
+                    break;
+            }
+
+        if( p_sys->download.vlead < p_sys->download.alead )
+            if( Download( s, p_sys->download.vlead, vsms,
+                        vsms->download_qlvl) != 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;
+}
-- 
1.7.5.4

