]> git.sesse.net Git - vlc/blob - modules/stream_filter/hds/hds.c
hds: fix out of bounds access (cid #1224531)
[vlc] / modules / stream_filter / hds / hds.c
1 /*****************************************************************************
2  * hds.c: Http Dynamic Streaming (HDS) stream filter
3  *****************************************************************************
4  *
5  * Author: Jonathan Thambidurai <jonathan _AT_ fastly _DOT_ com>
6  * Heavily inspired by SMooth module of Frédéric Yhuel <fyhuel _AT_ viotech _DOT_ net>
7  *
8  * This library is free software; you can redistribute it and/or
9  * modify it under the terms of the GNU Lesser General Public
10  * License as published by the Free Software Foundation; either
11  * version 2.1 of the License, or (at your option) any later version.
12  *
13  * This library is distributed in the hope that it will be useful,
14  * but WITHOUT ANY WARRANTY; without even the implied warranty of
15  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
16  * Lesser General Public License for more details.
17  *
18  * You should have received a copy of the GNU Lesser General Public
19  * License along with this library; if not, write to the Free Software
20  * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301 USA
21  *****************************************************************************/
22
23 #ifdef HAVE_CONFIG_H
24 # include "config.h"
25 #endif
26
27 #include <limits.h> /* INT_MAX */
28
29 #include <vlc_common.h>
30 #include <vlc_plugin.h>
31 #include <vlc_stream.h>
32 #include <vlc_strings.h>            /* b64_decode */
33 #include <vlc_xml.h>
34 #include <vlc_charset.h>            /* FromCharset */
35 #include <vlc_es.h>                 /* UNKNOWN_ES */
36
37 typedef struct chunk_s
38 {
39     int64_t     duration;   /* chunk duration in afrt timescale units */
40     uint64_t    timestamp;
41     uint32_t    frag_num;
42     uint32_t    seg_num;
43     uint32_t    frun_entry; /* Used to speed things up in vod situations */
44
45     uint32_t    data_len;
46
47     uint32_t    mdat_pos;   /* position in the mdat */
48     uint32_t    mdat_len;
49
50     void        *next;
51
52     uint8_t     *mdat_data;
53     uint8_t     *data;
54     bool        failed;
55     bool        eof;
56 } chunk_t;
57
58 typedef struct segment_run_s
59 {
60     uint32_t first_segment;
61     uint32_t fragments_per_segment;
62 } segment_run_t;
63
64 typedef struct fragment_run_s
65 {
66     uint32_t fragment_number_start;
67     uint32_t fragment_duration;
68     uint64_t fragment_timestamp;
69     uint8_t  discont;
70 } fragment_run_t;
71
72 typedef struct hds_stream_s
73 {
74     /* linked-list of chunks */
75     chunk_t        *chunks_head;
76     chunk_t        *chunks_livereadpos;
77     chunk_t        *chunks_downloadpos;
78
79     char*          quality_segment_modifier;
80
81     /* we can make this configurable */
82     uint64_t       download_leadtime;
83
84     /* in timescale units */
85     uint32_t       total_duration;
86
87     uint32_t       afrt_timescale;
88
89     /* these two values come from the abst */
90     uint32_t       timescale;
91     uint64_t       live_current_time;
92
93     vlc_mutex_t    abst_lock;
94
95     vlc_mutex_t    dl_lock;
96     vlc_cond_t     dl_cond;
97
98     /* can be left as null */
99     char*          abst_url;
100
101     /* this comes from the manifest media section  */
102     char*          url;
103
104     /* this comes from the bootstrap info */
105     char*          movie_id;
106
107 #define MAX_HDS_SERVERS 10
108     char*          server_entries[MAX_HDS_SERVERS];
109     uint8_t        server_entry_count;
110
111 #define MAX_HDS_SEGMENT_RUNS 256
112     segment_run_t  segment_runs[MAX_HDS_SEGMENT_RUNS];
113     uint8_t        segment_run_count;
114
115 #define MAX_HDS_FRAGMENT_RUNS 10000
116     fragment_run_t fragment_runs[MAX_HDS_FRAGMENT_RUNS];
117     uint32_t       fragment_run_count;
118 } hds_stream_t;
119
120 /* this is effectively just a sanity check  mechanism */
121 #define MAX_REQUEST_SIZE (50*1024*1024)
122
123 struct stream_sys_t
124 {
125     char         *base_url;    /* URL common part for chunks */
126     vlc_thread_t live_thread;
127     vlc_thread_t dl_thread;
128
129     /* we pend on peek until some number of segments arrives; otherwise
130      * the downstream system dies in case of playback */
131     uint64_t     chunk_count;
132
133     vlc_array_t  *hds_streams; /* available streams */
134
135     uint32_t     flv_header_bytes_sent;
136     uint64_t     duration_seconds;
137
138     bool         live;
139     bool         closed;
140 };
141
142 typedef struct _bootstrap_info {
143     uint8_t* data;
144     char*    id;
145     char*    url;
146     char*    profile;
147     int      data_len;
148 } bootstrap_info;
149
150 typedef struct _media_info {
151     char* stream_id;
152     char* media_url;
153     char* bootstrap_id;
154 } media_info;
155
156 /*****************************************************************************
157  * Module descriptor
158  *****************************************************************************/
159 static int  Open( vlc_object_t * );
160 static void Close( vlc_object_t * );
161
162 vlc_module_begin()
163     set_category( CAT_INPUT )
164     set_subcategory( SUBCAT_INPUT_STREAM_FILTER )
165     set_description( N_("HTTP Dynamic Streaming") )
166     set_shortname( "Dynamic Streaming")
167     add_shortcut( "hds" )
168     set_capability( "stream_filter", 30 )
169     set_callbacks( Open, Close )
170 vlc_module_end()
171
172 static int   Read( stream_t *, void *, unsigned );
173 static int   Peek( stream_t *, const uint8_t **, unsigned );
174 static int   Control( stream_t *, int , va_list );
175
176 static inline bool isFQUrl( char* url )
177 {
178     return ( NULL != vlc_strcasestr( url, "https://") ||
179              NULL != vlc_strcasestr( url, "http://" ) );
180 }
181
182 static bool isHDS( stream_t *s )
183 {
184     const char *peek;
185     int i_size = stream_Peek( s->p_source, (const uint8_t**) &peek, 200 );
186     if( i_size < 200 )
187         return false;
188
189     const char *str;
190
191     if( !memcmp( peek, "\xFF\xFE", 2 ) )
192     {
193         str = FromCharset( "UTF-16LE", peek, 512 );
194     }
195     else if( !memcmp( peek, "\xFE\xFF", 2 ) )
196     {
197         str = FromCharset( "UTF-16BE", peek, 512 );
198     }
199     else
200         str = peek;
201
202     if( str == NULL )
203         return false;
204
205     bool ret = strstr( str, "<manifest" ) != NULL;
206     return ret;
207 }
208
209 static uint8_t* parse_asrt( vlc_object_t* p_this,
210                         hds_stream_t* s,
211                         uint8_t* data,
212                         uint8_t* data_end )
213 {
214     uint8_t* data_p = data;
215
216     uint32_t asrt_len = 0;
217     asrt_len = U32_AT( data_p );
218     if( asrt_len > data_end - data ||
219         data_end - data <  14 )
220     {
221         msg_Err( p_this, "Not enough asrt data (%"PRIu32", %lu)", asrt_len, data_end - data );
222         return NULL;
223     }
224
225     data_p += sizeof(asrt_len);
226
227     if( 0 != memcmp( "asrt", data_p, 4 ) )
228     {
229         msg_Err( p_this, "Cant find asrt in bootstrap" );
230         return NULL;
231     }
232     data_p += 4;
233
234     /* ignore flags and versions (we don't handle multiple updates) */
235     data_p += 4;
236
237     uint8_t quality_entry_count = *data_p;
238     bool quality_found = false;
239     data_p++;
240
241     if( ! s->quality_segment_modifier )
242     {
243         quality_found = true;
244     }
245
246     while( quality_entry_count-- > 0 )
247     {
248         char* str_start = (char*) data_p;
249         data_p = memchr( data_p, '\0', data_end - data_p );
250         if( ! data_p )
251         {
252             msg_Err( p_this, "Couldn't find quality entry string in asrt" );
253             return NULL;
254         }
255         data_p++;
256
257         if( ! quality_found )
258         {
259             if( ! strncmp( str_start, s->quality_segment_modifier,
260                            strlen(s->quality_segment_modifier) ) )
261             {
262                 quality_found = true;
263             }
264         }
265
266         if( data_p >= data_end )
267         {
268             msg_Err( p_this, "Premature end of asrt in quality entries" );
269             return NULL;
270         }
271     }
272
273     if( data_end - data_p < 4 )
274     {
275         msg_Err( p_this, "Premature end of asrt after quality entries" );
276         return NULL;
277     }
278
279     uint32_t segment_run_entry_count = U32_AT( data_p );
280     data_p += sizeof(segment_run_entry_count);
281
282     if( data_end - data_p < 8 * segment_run_entry_count )
283     {
284         msg_Err( p_this, "Not enough data in asrt for segment run entries" );
285         return NULL;
286     }
287
288     if( segment_run_entry_count >= MAX_HDS_SEGMENT_RUNS )
289     {
290         msg_Err( p_this, "Too many segment runs" );
291         return NULL;
292     }
293
294     while( segment_run_entry_count-- > 0 )
295     {
296         if( quality_found )
297         {
298             s->segment_runs[s->segment_run_count].first_segment = U32_AT(data_p);
299         }
300         data_p+=4;
301
302         if( quality_found )
303         {
304             s->segment_runs[s->segment_run_count].fragments_per_segment = U32_AT(data_p);
305         }
306         data_p+=4;
307
308         s->segment_run_count++;
309     }
310
311     return data_p;
312 }
313
314 static uint8_t* parse_afrt( vlc_object_t* p_this,
315                         hds_stream_t* s,
316                         uint8_t* data,
317                         uint8_t* data_end )
318 {
319     uint8_t* data_p = data;
320
321     uint32_t afrt_len = U32_AT( data_p );
322     if( afrt_len > data_end - data ||
323         data_end - data <  9 )
324     {
325         msg_Err( p_this, "Not enough afrt data %u, %ld", afrt_len, data_end - data );
326         return NULL;
327     }
328     data_p += sizeof(afrt_len);
329
330     if( 0 != memcmp( data_p, "afrt", 4 ) )
331     {
332         msg_Err( p_this, "Cant find afrt in bootstrap" );
333         return NULL;
334     }
335     data_p += 4;
336
337     /* ignore flags and versions (we don't handle multiple updates) */
338     data_p += 4;
339
340     if( data_end - data_p < 9 )
341     {
342         msg_Err( p_this, "afrt is too short" );
343         return NULL;
344     }
345
346     s->afrt_timescale = U32_AT( data_p );
347     data_p += 4;
348
349     bool quality_found = false;
350     if( ! s->quality_segment_modifier )
351     {
352         quality_found = true;
353     }
354
355     uint32_t quality_entry_count = *data_p;
356     data_p++;
357     while( quality_entry_count-- > 0 )
358     {
359         char* str_start = (char*)data_p;
360         data_p = memchr( data_p, '\0', data_end - data_p );
361         if( ! data_p )
362         {
363             msg_Err( p_this, "Couldn't find quality entry string in afrt" );
364             return NULL;
365         }
366         data_p++;
367
368         if( ! quality_found )
369         {
370             if( ! strncmp( str_start, s->quality_segment_modifier,
371                            strlen(s->quality_segment_modifier) ) )
372             {
373                 quality_found = true;
374             }
375         }
376     }
377
378     if( data_end - data_p < 5 )
379     {
380         msg_Err( p_this, "No more space in afrt after quality entries" );
381         return NULL;
382     }
383
384     uint32_t fragment_run_entry_count = U32_AT( data_p );
385     data_p += sizeof(uint32_t);
386
387     while(fragment_run_entry_count-- > 0)
388     {
389         if( data_end - data_p < 16 )
390         {
391             msg_Err( p_this, "Not enough data in afrt" );
392             return NULL;
393         }
394
395         if( s->fragment_run_count >= MAX_HDS_FRAGMENT_RUNS )
396         {
397             msg_Err( p_this, "Too many fragment runs, exiting" );
398             return NULL;
399         }
400
401         s->fragment_runs[s->fragment_run_count].fragment_number_start = U32_AT(data_p);
402         data_p += 4;
403
404         s->fragment_runs[s->fragment_run_count].fragment_timestamp = U64_AT( data_p );
405         data_p += 8;
406
407         s->fragment_runs[s->fragment_run_count].fragment_duration = U32_AT( data_p );
408         data_p += 4;
409
410         s->fragment_runs[s->fragment_run_count].discont = 0;
411         if( s->fragment_runs[s->fragment_run_count].fragment_duration == 0 )
412         {
413             /* discontinuity flag */
414             s->fragment_runs[s->fragment_run_count].discont = *(data_p++);
415         }
416
417         s->fragment_run_count++;
418     }
419
420     return data_p;
421 }
422
423 static inline chunk_t* chunk_new()
424 {
425     chunk_t* chunk = calloc(1, sizeof(chunk_t));
426     return chunk;
427 }
428
429 static void chunk_free( chunk_t * chunk )
430 {
431     FREENULL( chunk->data );
432     free( chunk );
433 }
434
435 static void parse_BootstrapData( vlc_object_t* p_this,
436                                  hds_stream_t * s,
437                                  uint8_t* data,
438                                  uint8_t* data_end )
439 {
440     uint8_t* data_p = data;
441
442     uint32_t abst_len = U32_AT( data_p );
443     if( abst_len > data_end - data
444         || data_end - data < 29 /* min size of data */ )
445     {
446         msg_Warn( p_this, "Not enough bootstrap data" );
447         return;
448     }
449     data_p += sizeof(abst_len);
450
451     if( 0 != memcmp( data_p, "abst", 4 ) )
452     {
453         msg_Warn( p_this, "Cant find abst in bootstrap" );
454         return;
455     }
456     data_p += 4;
457
458     /* version, flags*/
459     data_p += 4;
460
461     /* we ignore the version */
462     data_p += 4;
463
464     /* some flags we don't care about here because they are
465      * in the manifest
466      */
467     data_p += 1;
468
469     /* timescale */
470     s->timescale = U32_AT( data_p );
471     data_p += sizeof(s->timescale);
472
473     s->live_current_time = U64_AT( data_p );
474     data_p += sizeof(s->live_current_time);
475
476     /* smtpe time code offset */
477     data_p += 8;
478
479     s->movie_id = strndup( (char*)data_p, data_end - data_p );
480     data_p += ( strlen( s->movie_id ) + 1 );
481
482     if( data_end - data_p < 4 ) {
483         msg_Warn( p_this, "Not enough bootstrap after Movie Identifier" );
484         return;
485     }
486
487     uint8_t server_entry_count = 0;
488     server_entry_count = (uint8_t) *data_p;
489     data_p++;
490
491     s->server_entry_count = 0;
492     while( server_entry_count-- > 0 )
493     {
494         if( s->server_entry_count < MAX_HDS_SERVERS )
495         {
496             s->server_entries[s->server_entry_count++] = strndup( (char*)data_p,
497                                                                   data_end - data_p );
498             data_p += strlen( s->server_entries[s->server_entry_count-1] ) + 1;
499         }
500         else
501         {
502             msg_Warn( p_this, "Too many servers" );
503             data_p = memchr( data_p, '\0', data_end - data_p );
504             if( ! data_p )
505             {
506                 msg_Err( p_this, "Couldn't find server entry" );
507                 return;
508             }
509             data_p++;
510         }
511
512         if( data_p >= data_end )
513         {
514             msg_Warn( p_this, "Premature end of bootstrap info while reading servers" );
515             return;
516         }
517     }
518
519     if( data_end - data_p < 3 ) {
520         msg_Warn( p_this, "Not enough bootstrap after Servers" );
521         return;
522     }
523
524     s->quality_segment_modifier = NULL;
525
526     uint8_t quality_entry_count = *data_p;
527     data_p++;
528
529     if( quality_entry_count > 1 )
530     {
531         msg_Err( p_this, "I don't know what to do with multiple quality levels in the bootstrap - shouldn't this be handled at the manifest level?" );
532         return;
533     }
534
535     s->quality_segment_modifier = NULL;
536     while( quality_entry_count-- > 0 )
537     {
538         if( s->quality_segment_modifier )
539         {
540             s->quality_segment_modifier = strndup( (char*)data_p, data_end - data_p );
541         }
542         data_p += strnlen( (char*)data_p, data_end - data_p ) + 1;
543     }
544
545     if( data_end - data_p < 2 ) {
546         msg_Warn( p_this, "Not enough bootstrap after quality entries" );
547         return;
548     }
549
550     /* ignoring "DrmData" */
551     data_p = memchr( data_p, '\0', data_end - data_p );
552     if( ! data_p )
553     {
554         msg_Err( p_this, "Couldn't find DRM Data" );
555         return;
556     }
557     data_p++;
558
559     if( data_end - data_p < 2 ) {
560         msg_Warn( p_this, "Not enough bootstrap after drm data" );
561         return;
562     }
563
564     /* ignoring "metadata" */
565     data_p = memchr( data_p, '\0', data_end - data_p );
566     if( ! data_p )
567     {
568         msg_Err( p_this, "Couldn't find metadata");
569         return;
570     }
571     data_p++;
572
573     if( data_end - data_p < 2 ) {
574         msg_Warn( p_this, "Not enough bootstrap after drm data" );
575         return;
576     }
577
578     uint8_t asrt_count = *data_p;
579     data_p++;
580
581     s->segment_run_count = 0;
582     while( asrt_count-- > 0 &&
583            data_end > data_p &&
584            (data_p = parse_asrt( p_this, s, data_p, data_end )) );
585
586     uint8_t afrt_count = *data_p;
587     data_p++;
588
589     s->fragment_run_count = 0;
590     while( afrt_count-- > 0 &&
591            data_end > data_p &&
592            (data_p = parse_afrt( p_this, s, data_p, data_end )) );
593 }
594
595 /* this only works with ANSI characters - this is ok
596    for the bootstrapinfo field which this function is
597    exclusively used for since it is merely a base64 encoding
598 */
599 static bool is_whitespace( char c )
600 {
601     return ( ' '  == c ||
602              '\t' == c ||
603              '\n' == c ||
604              '\v' == c ||
605              '\f' == c ||
606              '\r' == c );
607 }
608
609 /* see above note for is_whitespace */
610 static void whitespace_substr( char** start,
611                                char** end )
612 {
613     while( is_whitespace( **start ) && *start != *end ) {
614         (*start)++;
615     }
616
617     if( *start == *end )
618         return;
619
620     while( is_whitespace(*(*end - 1) ) ) {
621         (*end)--;
622     }
623 }
624
625 /* returns length (could be zero, indicating all of remaining data) */
626 /* ptr is to start of data, right after 'mdat' string */
627 static uint32_t find_chunk_mdat( vlc_object_t* p_this,
628                                  uint8_t* chunkdata, uint8_t* chunkdata_end,
629                                  uint8_t** mdatptr )
630 {
631     uint8_t* boxname = NULL;
632     uint8_t* boxdata = NULL;
633     uint64_t boxsize = 0;
634
635     do {
636         if( chunkdata_end < chunkdata ||
637             chunkdata_end - chunkdata < 8 )
638         {
639             msg_Err( p_this, "Couldn't find mdat in box 1!" );
640             *mdatptr = 0;
641             return 0;
642         }
643
644         boxsize = (uint64_t)U32_AT( chunkdata );
645         chunkdata += 4;
646
647         boxname = chunkdata;
648         chunkdata += 4;
649
650         if( boxsize == 1 )
651         {
652             if( chunkdata_end - chunkdata >= 12 )
653             {
654                 boxsize =  U64_AT(chunkdata);
655                 chunkdata += 8;
656             }
657             else
658             {
659                 msg_Err( p_this, "Couldn't find mdat in box 2!");
660                 *mdatptr = 0;
661                 return 0;
662             }
663             boxdata = chunkdata;
664             chunkdata += (boxsize - 16);
665         }
666         else
667         {
668             boxdata = chunkdata;
669             chunkdata += (boxsize - 8);
670         }
671     } while ( 0 != memcmp( boxname, "mdat", 4 ) );
672
673     *mdatptr = boxdata;
674
675     return chunkdata_end - ((uint8_t*)boxdata);
676 }
677
678 /* returns data ptr if valid (updating the chunk itself
679    tells the reader that the chunk is safe to read, which is not yet correct)*/
680 static uint8_t* download_chunk( stream_t *s,
681                                 stream_sys_t* sys,
682                                 hds_stream_t* stream, chunk_t* chunk )
683 {
684     const char* quality = "";
685     char* server_base = sys->base_url;
686     if( stream->server_entry_count > 0 &&
687         strlen(stream->server_entries[0]) > 0 )
688     {
689         server_base = stream->server_entries[0];
690     }
691
692     if( stream->quality_segment_modifier )
693     {
694         quality = stream->quality_segment_modifier;
695     }
696
697     const char* movie_id = "";
698     if( stream->url && strlen(stream->url) > 0 )
699     {
700         if( isFQUrl( stream->url ) )
701         {
702             server_base = stream->url;
703         }
704         else
705         {
706             movie_id = stream->url;
707         }
708     }
709
710     char* fragment_url;
711     if( 0 > asprintf( &fragment_url, "%s/%s%sSeg%u-Frag%u",
712               server_base,
713               movie_id,
714               quality,
715               chunk->seg_num,
716                       chunk->frag_num ) ) {
717         msg_Err(s, "Failed to allocate memory for fragment url" );
718         return NULL;
719     }
720
721     msg_Info(s, "Downloading fragment %s",  fragment_url );
722
723     stream_t* download_stream = stream_UrlNew( s, fragment_url );
724     if( ! download_stream )
725     {
726         msg_Err(s, "Failed to download fragment %s", fragment_url );
727         free( fragment_url );
728         chunk->failed = true;
729         return NULL;
730     }
731     free( fragment_url );
732
733     int64_t size = stream_Size( download_stream );
734     chunk->data_len = (uint32_t) size;
735
736     if( size > MAX_REQUEST_SIZE )
737     {
738         msg_Err(s, "Strangely-large chunk of %"PRIi64" Bytes", size );
739         return NULL;
740     }
741
742     uint8_t* data = malloc( size );
743     if( ! data )
744     {
745         msg_Err(s, "Couldn't allocate chunk" );
746         return NULL;
747     }
748
749     int read = stream_Read( download_stream, data,
750                             size );
751     chunk->data_len = read;
752
753     if( read < size )
754     {
755         msg_Err( s, "Requested %"PRIi64" bytes, "\
756                  "but only got %d", size, read );
757         data = realloc( chunk->data, read );
758         chunk->failed = true;
759         return NULL;
760     }
761     else
762     {
763         chunk->failed = false;
764     }
765
766     stream_Delete( download_stream );
767     return data;
768 }
769
770 static void* download_thread( void* p )
771 {
772     vlc_object_t* p_this = (vlc_object_t*)p;
773     stream_t* s = (stream_t*) p_this;
774     stream_sys_t* sys = s->p_sys;
775
776     if ( vlc_array_count( sys->hds_streams ) == 0 )
777         return NULL;
778
779     // TODO: Change here for selectable stream
780     hds_stream_t* hds_stream = sys->hds_streams->pp_elems[0];
781
782     int canc = vlc_savecancel();
783
784     vlc_mutex_lock( & hds_stream->dl_lock );
785
786     while( ! sys->closed )
787     {
788         if( ! hds_stream->chunks_downloadpos )
789         {
790             chunk_t* chunk = hds_stream->chunks_head;
791             while(chunk && chunk->data )
792             {
793                 chunk = chunk->next;
794             }
795
796             if( chunk && ! chunk->data )
797                 hds_stream->chunks_downloadpos = chunk;
798         }
799
800         while( hds_stream->chunks_downloadpos )
801         {
802             chunk_t *chunk = hds_stream->chunks_downloadpos;
803
804             uint8_t *data = download_chunk( (stream_t*)p_this,
805                                             sys,
806                                             hds_stream,
807                                             chunk );
808
809             if( ! chunk->failed )
810             {
811                 chunk->mdat_len =
812                     find_chunk_mdat( p_this,
813                                      data,
814                                      data + chunk->data_len,
815                                      & chunk->mdat_data );
816                 if( chunk->mdat_len == 0 ) {
817                     chunk->mdat_len = chunk->data_len - (chunk->mdat_data - data);
818                 }
819                 hds_stream->chunks_downloadpos = chunk->next;
820                 chunk->data = data;
821
822                 sys->chunk_count++;
823             }
824         }
825
826         vlc_cond_wait( & hds_stream->dl_cond,
827                        & hds_stream->dl_lock );
828     }
829
830     vlc_mutex_unlock( & hds_stream->dl_lock );
831
832     vlc_restorecancel( canc );
833     return NULL;
834 }
835
836 static chunk_t* generate_new_chunk(
837     vlc_object_t* p_this,
838     chunk_t* last_chunk,
839     hds_stream_t* hds_stream )
840 {
841     stream_t* s = (stream_t*) p_this;
842     stream_sys_t *sys = s->p_sys;
843     chunk_t *chunk = chunk_new();
844     unsigned int frun_entry = 0;
845
846     if( ! chunk ) {
847         msg_Err( p_this, "Couldn't allocate new chunk!" );
848         return NULL;
849     }
850
851     if( last_chunk )
852     {
853         chunk->timestamp = last_chunk->timestamp + last_chunk->duration;
854         chunk->frag_num = last_chunk->frag_num + 1;
855
856         if( ! sys->live )
857         {
858             frun_entry = last_chunk->frun_entry;
859         }
860     }
861     else
862     {
863         fragment_run_t* first_frun  = hds_stream->fragment_runs;
864         if( sys->live )
865         {
866             chunk->timestamp = (hds_stream->live_current_time * ((uint64_t)hds_stream->afrt_timescale)) / ((uint64_t)hds_stream->timescale);
867         }
868         else
869         {
870             chunk->timestamp = first_frun->fragment_timestamp;
871             chunk->frag_num =  first_frun->fragment_number_start;
872         }
873     }
874
875     for( ; frun_entry < hds_stream->fragment_run_count;
876          frun_entry++ )
877     {
878         /* check for discontinuity first */
879         if( hds_stream->fragment_runs[frun_entry].fragment_duration == 0 )
880         {
881             if( frun_entry == hds_stream->fragment_run_count - 1 )
882             {
883                 msg_Err( p_this, "Discontinuity but can't find next timestamp!");
884                 return NULL;
885             }
886
887             chunk->frag_num = hds_stream->fragment_runs[frun_entry+1].fragment_number_start;
888             chunk->duration = hds_stream->fragment_runs[frun_entry+1].fragment_duration;
889             chunk->timestamp = hds_stream->fragment_runs[frun_entry+1].fragment_timestamp;
890
891             frun_entry++;
892             break;
893         }
894
895         if( chunk->frag_num == 0 )
896         {
897             if( frun_entry == hds_stream->fragment_run_count - 1 ||
898                 ( chunk->timestamp >= hds_stream->fragment_runs[frun_entry].fragment_timestamp &&
899                   chunk->timestamp < hds_stream->fragment_runs[frun_entry+1].fragment_timestamp )
900                 )
901             {
902                 fragment_run_t* frun = hds_stream->fragment_runs + frun_entry;
903                 chunk->frag_num = frun->fragment_number_start + ( (chunk->timestamp - frun->fragment_timestamp) /
904                                                                   frun->fragment_duration );
905                 chunk->duration = frun->fragment_duration;
906             }
907
908         }
909
910         if( hds_stream->fragment_runs[frun_entry].fragment_number_start <=
911             chunk->frag_num &&
912             (frun_entry == hds_stream->fragment_run_count - 1 ||
913              hds_stream->fragment_runs[frun_entry+1].fragment_number_start > chunk->frag_num ) )
914         {
915             chunk->duration = hds_stream->fragment_runs[frun_entry].fragment_duration;
916             chunk->timestamp = hds_stream->fragment_runs[frun_entry].fragment_timestamp +
917                 chunk->duration * (chunk->frag_num - hds_stream->fragment_runs[frun_entry].fragment_number_start);
918             break;
919         }
920     }
921
922     if( frun_entry == hds_stream->fragment_run_count )
923     {
924         msg_Err( p_this, "Couldn'd find the fragment run!" );
925         chunk_free( chunk );
926         return NULL;
927     }
928
929     int srun_entry = 0;
930     unsigned int segment = 0;
931     uint64_t fragments_accum = chunk->frag_num;
932     for( srun_entry = 0; srun_entry < hds_stream->segment_run_count;
933          srun_entry++ )
934     {
935         segment = hds_stream->segment_runs[srun_entry].first_segment +
936             (chunk->frag_num - fragments_accum ) / hds_stream->segment_runs[srun_entry].fragments_per_segment;
937
938         if( srun_entry + 1 == hds_stream->segment_run_count ||
939             hds_stream->segment_runs[srun_entry+1].first_segment > segment )
940         {
941             break;
942         }
943
944         fragments_accum += (
945             (hds_stream->segment_runs[srun_entry+1].first_segment -
946              hds_stream->segment_runs[srun_entry].first_segment) *
947             hds_stream->segment_runs[srun_entry].fragments_per_segment );
948     }
949
950     chunk->seg_num = segment;
951     chunk->frun_entry = frun_entry;
952
953     if( ! sys->live )
954     {
955         if( (chunk->timestamp + chunk->duration) / hds_stream->afrt_timescale  >= sys->duration_seconds )
956         {
957             chunk->eof = true;
958         }
959     }
960
961     return chunk;
962 }
963
964 static void maintain_live_chunks(
965     vlc_object_t* p_this,
966     hds_stream_t* hds_stream
967     )
968 {
969     if( ! hds_stream->chunks_head )
970     {
971         /* just start with the earliest in the abst
972          * maybe it would be better to use the currentMediaTime?
973          * but then we are right on the edge of buffering, esp for
974          * small fragments */
975         hds_stream->chunks_head = generate_new_chunk(
976             p_this, 0, hds_stream );
977         hds_stream->chunks_livereadpos = hds_stream->chunks_head;
978     }
979
980     chunk_t* chunk = hds_stream->chunks_head;
981     bool dl = false;
982     while( chunk && ( chunk->timestamp * ((uint64_t)hds_stream->timescale) )
983            / ((uint64_t)hds_stream->afrt_timescale)
984            <= hds_stream->live_current_time )
985     {
986         if( chunk->next )
987         {
988             chunk = chunk->next;
989         }
990         else
991         {
992             chunk->next = generate_new_chunk( p_this, chunk, hds_stream );
993             chunk = chunk->next;
994             dl = true;
995         }
996     }
997
998     if( dl )
999         vlc_cond_signal( & hds_stream->dl_cond );
1000
1001     chunk = hds_stream->chunks_head;
1002     while( chunk && chunk->data && chunk->mdat_pos >= chunk->mdat_len && chunk->next )
1003     {
1004         chunk_t* next_chunk = chunk->next;
1005         chunk_free( chunk );
1006         chunk = next_chunk;
1007     }
1008
1009     if( ! hds_stream->chunks_livereadpos )
1010         hds_stream->chunks_livereadpos = hds_stream->chunks_head;
1011
1012     hds_stream->chunks_head = chunk;
1013 }
1014
1015
1016 static void* live_thread( void* p )
1017 {
1018     vlc_object_t* p_this = (vlc_object_t*)p;
1019     stream_t* s = (stream_t*) p_this;
1020     stream_sys_t* sys = s->p_sys;
1021
1022     if ( vlc_array_count( sys->hds_streams ) == 0 )
1023         return NULL;
1024
1025     // TODO: Change here for selectable stream
1026     hds_stream_t* hds_stream = sys->hds_streams->pp_elems[0];
1027
1028     int canc = vlc_savecancel();
1029
1030     char* abst_url;
1031
1032     if( hds_stream->abst_url &&
1033         ( isFQUrl( hds_stream->abst_url ) ) )
1034     {
1035         if( !( abst_url = strdup( hds_stream->abst_url ) ) )
1036             return NULL;
1037     }
1038     else
1039     {
1040         char* server_base = sys->base_url;
1041
1042
1043         if( 0 > asprintf( &abst_url, "%s/%s",
1044                           server_base,
1045                           hds_stream->abst_url ) )
1046         {
1047             return NULL;
1048         }
1049     }
1050
1051     mtime_t last_dl_start_time;
1052
1053     while( ! sys->closed )
1054     {
1055         last_dl_start_time = mdate();
1056         stream_t* download_stream = stream_UrlNew( p_this, abst_url );
1057         if( ! download_stream )
1058         {
1059             msg_Err( p_this, "Failed to download abst %s", abst_url );
1060         }
1061         else
1062         {
1063             int64_t size = stream_Size( download_stream );
1064             uint8_t* data = malloc( size );
1065             int read = stream_Read( download_stream, data,
1066                                     size );
1067             if( read < size )
1068             {
1069                 msg_Err( p_this, "Requested %"PRIi64" bytes, "  \
1070                          "but only got %d", size, read );
1071
1072             }
1073             else
1074             {
1075                 vlc_mutex_lock( & hds_stream->abst_lock );
1076                 parse_BootstrapData( p_this, hds_stream,
1077                                      data, data + read );
1078                 vlc_mutex_unlock( & hds_stream->abst_lock );
1079                 maintain_live_chunks( p_this, hds_stream );
1080             }
1081
1082             free( data );
1083
1084             stream_Delete( download_stream );
1085         }
1086
1087         mwait( last_dl_start_time + ( ((int64_t)hds_stream->fragment_runs[hds_stream->fragment_run_count-1].fragment_duration) * 1000000LL) / ((int64_t)hds_stream->afrt_timescale) );
1088
1089
1090     }
1091
1092     free( abst_url );
1093
1094     vlc_restorecancel( canc );
1095     return NULL;
1096 }
1097
1098 static int parse_Manifest( stream_t *s )
1099 {
1100     xml_t *vlc_xml = NULL;
1101     xml_reader_t *vlc_reader = NULL;
1102     int type = UNKNOWN_ES;
1103     stream_t *st = s->p_source;
1104
1105     msg_Dbg( s, "Manifest parsing\n" );
1106
1107     vlc_xml = xml_Create( st );
1108     if( !vlc_xml )
1109     {
1110         msg_Err( s, "Failed to open XML parser" );
1111         return VLC_EGENERIC;
1112     }
1113
1114     vlc_reader = xml_ReaderCreate( vlc_xml, st );
1115     if( !vlc_reader )
1116     {
1117         msg_Err( s, "Failed to open source for parsing" );
1118         xml_Delete( vlc_xml );
1119         return VLC_EGENERIC;
1120     }
1121
1122     char *node;
1123
1124     stream_sys_t *sys = s->p_sys;
1125
1126     sys->duration_seconds = 0;
1127
1128 #define MAX_BOOTSTRAP_INFO 10
1129     bootstrap_info bootstraps[MAX_BOOTSTRAP_INFO];
1130     uint8_t bootstrap_idx = 0;
1131     memset( bootstraps, 0, sizeof(bootstrap_info) * MAX_BOOTSTRAP_INFO );
1132
1133 #define MAX_MEDIA_ELEMENTS 10
1134     media_info medias[MAX_MEDIA_ELEMENTS];
1135     uint8_t media_idx = 0;
1136     memset( medias, 0, sizeof(media_info) * MAX_MEDIA_ELEMENTS );
1137
1138 #define MAX_XML_DEPTH 256
1139     char* element_stack[256];
1140     uint8_t current_element_idx = 0;
1141     char* current_element = NULL;
1142     memset( element_stack, 0, sizeof(char*) * MAX_XML_DEPTH );
1143
1144     const char* attr_name;
1145     const char* attr_value;
1146
1147     char* media_id = NULL;
1148
1149 #define TIMESCALE 10000000
1150     while( (type = xml_ReaderNextNode( vlc_reader, (const char**) &node )) > 0 )
1151     {
1152         switch( type )
1153         {
1154         case XML_READER_STARTELEM:
1155             if( current_element_idx == 0 && element_stack[current_element_idx] == 0 ) {
1156                 if( !( element_stack[current_element_idx] = strdup( node ) ) )
1157                     return VLC_ENOMEM;
1158             } else {
1159                 if ( !( element_stack[++current_element_idx] = strdup( node ) ) )
1160                     return VLC_ENOMEM;
1161             }
1162
1163             break;
1164         case XML_READER_ENDELEM:
1165             if( ! strcmp( current_element, "bootstrapInfo") ) {
1166                 if( bootstrap_idx + 1 == MAX_BOOTSTRAP_INFO ) {
1167                     msg_Warn( (vlc_object_t*) s, "Too many bootstraps, ignoring" );
1168                 } else {
1169                     bootstrap_idx++;
1170                 }
1171             }
1172
1173             free( current_element );
1174             element_stack[current_element_idx--] = 0;
1175             break;
1176         }
1177
1178         if( ! element_stack[current_element_idx] ) {
1179             continue;
1180         }
1181
1182         current_element = element_stack[current_element_idx];
1183
1184         if( type == XML_READER_STARTELEM && ! strcmp( current_element, "media") )
1185         {
1186             if( media_idx == MAX_MEDIA_ELEMENTS )
1187             {
1188                 msg_Err( (vlc_object_t*) s, "Too many media elements, quitting" );
1189                 return VLC_EGENERIC;
1190             }
1191
1192             while( ( attr_name = xml_ReaderNextAttr( vlc_reader, &attr_value )) )
1193             {
1194                 if( !strcmp(attr_name, "streamId" ) )
1195                 {
1196                     if( !( medias[media_idx].stream_id = strdup( attr_value ) ) )
1197                         return VLC_ENOMEM;
1198                 }
1199                 if( !strcmp(attr_name, "url" ) )
1200                 {
1201                     if( !( medias[media_idx].media_url = strdup( attr_value ) ) )
1202                         return VLC_ENOMEM;
1203                 }
1204                 if( !strcmp(attr_name, "bootstrapInfoId" ) )
1205                 {
1206                     if( !( medias[media_idx].bootstrap_id = strdup( attr_value ) ) )
1207                         return VLC_ENOMEM;
1208                 }
1209             }
1210
1211             media_idx++;
1212         }
1213
1214         if( type == XML_READER_STARTELEM && ! strcmp( current_element, "bootstrapInfo") )
1215         {
1216             while( ( attr_name = xml_ReaderNextAttr( vlc_reader, &attr_value )) )
1217             {
1218                 if( !strcmp(attr_name, "url" ) )
1219                 {
1220                     if( !( bootstraps[bootstrap_idx].url = strdup( attr_value ) ) )
1221                         return VLC_ENOMEM;
1222                 }
1223                 if( !strcmp(attr_name, "id" ) )
1224                 {
1225                     if( !( bootstraps[bootstrap_idx].id = strdup( attr_value ) ) )
1226                        return VLC_ENOMEM;
1227                 }
1228                 if( !strcmp(attr_name, "profile" ) )
1229                 {
1230                     if( !( bootstraps[bootstrap_idx].profile = strdup( attr_value ) ) )
1231                         return VLC_ENOMEM;
1232                 }
1233             }
1234         }
1235
1236         if( type == XML_READER_TEXT )
1237         {
1238             if( ! strcmp( current_element, "bootstrapInfo" ) )
1239             {
1240                 char* start = node;
1241                 char* end = start + strlen(start);
1242                 whitespace_substr( &start, &end );
1243                 *end = '\0';
1244
1245                 bootstraps[bootstrap_idx].data_len =
1246                     vlc_b64_decode_binary( (uint8_t**)&bootstraps[bootstrap_idx].data, start );
1247                 if( ! bootstraps[bootstrap_idx].data )
1248                 {
1249                     msg_Err( (vlc_object_t*) s, "Couldn't decode bootstrap info" );
1250                 }
1251             }
1252             if( ! strcmp( current_element, "duration" ) )
1253             {
1254                 double shutup_gcc = atof( node );
1255                 sys->duration_seconds = (uint64_t) shutup_gcc;
1256             }
1257             if( ! strcmp( current_element, "id" ) )
1258             {
1259                 if( current_element &&
1260                     ! strcmp( element_stack[current_element_idx-1], "manifest" ) )
1261                 {
1262                     if( !( media_id = strdup( node ) ) )
1263                         return VLC_ENOMEM;
1264                 }
1265             }
1266         }
1267     }
1268
1269     xml_ReaderDelete( vlc_reader );
1270     xml_Delete( vlc_xml );
1271
1272     for( int i = 0; i <= media_idx; i++ )
1273     {
1274         for( int j = 0; j < bootstrap_idx; j++ )
1275         {
1276             if( ( ! medias[i].bootstrap_id && ! bootstraps[j].id ) ||
1277                 (medias[i].bootstrap_id && bootstraps[j].id &&
1278                  ! strcmp( medias[i].bootstrap_id, bootstraps[j].id ) ) )
1279             {
1280                 hds_stream_t* new_stream = malloc(sizeof(hds_stream_t));
1281                 memset( new_stream, 0, sizeof(hds_stream_t));
1282
1283                 vlc_mutex_init( & new_stream->abst_lock );
1284                 vlc_mutex_init( & new_stream->dl_lock );
1285                 vlc_cond_init( & new_stream->dl_cond );
1286
1287                 if( sys->duration_seconds )
1288                 {
1289                     sys->live = false;
1290                 }
1291                 else
1292                 {
1293                     sys->live = true;
1294                 }
1295
1296                 if( medias[i].media_url )
1297                 {
1298                     if( !(new_stream->url = strdup( medias[i].media_url ) ) )
1299                     {
1300                         free(new_stream);
1301                         return VLC_ENOMEM;
1302                     }
1303                 }
1304
1305                 if( ! sys->live )
1306                 {
1307                     parse_BootstrapData( (vlc_object_t*)s,
1308                                          new_stream,
1309                                          bootstraps[j].data,
1310                                          bootstraps[j].data + bootstraps[j].data_len );
1311
1312                     new_stream->download_leadtime = 15;
1313
1314                     new_stream->chunks_head = generate_new_chunk(
1315                         (vlc_object_t*) s, 0, new_stream );
1316                     chunk_t* chunk = new_stream->chunks_head;
1317                     uint64_t total_duration = chunk->duration;
1318                     while( chunk && total_duration/new_stream->afrt_timescale < new_stream->download_leadtime )
1319                     {
1320                         chunk->next = generate_new_chunk(
1321                             (vlc_object_t*) s, chunk, new_stream );
1322                         chunk = chunk->next;
1323                         if( chunk )
1324                             total_duration += chunk->duration;
1325                     }
1326                 }
1327                 else
1328                 {
1329                     if( !(new_stream->abst_url = strdup( bootstraps[j].url ) ) )
1330                     {
1331                         free(new_stream);
1332                         return VLC_ENOMEM;
1333                     }
1334                 }
1335
1336                 vlc_array_append( sys->hds_streams, new_stream );
1337
1338                 msg_Info( (vlc_object_t*)s, "New track with quality_segment(%s), timescale(%u), movie_id(%s), segment_run_count(%d), fragment_run_count(%u)",
1339                           new_stream->quality_segment_modifier?"":new_stream->quality_segment_modifier, new_stream->timescale,
1340                           new_stream->movie_id, new_stream->segment_run_count, new_stream->fragment_run_count );
1341
1342             }
1343         }
1344     }
1345
1346     for( int i = 0; i < MAX_MEDIA_ELEMENTS; i++ )
1347     {
1348         FREENULL( medias[media_idx].stream_id );
1349         FREENULL( medias[media_idx].media_url );
1350         FREENULL( medias[media_idx].bootstrap_id );
1351     }
1352
1353     for( int i = 0; i < MAX_BOOTSTRAP_INFO; i++ )
1354     {
1355         FREENULL( bootstraps[i].data );
1356         FREENULL( bootstraps[i].id );
1357         FREENULL( bootstraps[i].url );
1358         FREENULL( bootstraps[i].profile );
1359     }
1360
1361     FREENULL( media_id );
1362
1363     return VLC_SUCCESS;
1364 }
1365
1366
1367 static void hds_free( hds_stream_t *p_stream )
1368 {
1369     FREENULL( p_stream->quality_segment_modifier );
1370
1371     FREENULL( p_stream->abst_url );
1372
1373     FREENULL( p_stream->url );
1374     FREENULL( p_stream->movie_id );
1375     for( int i = 0; i < p_stream->server_entry_count; i++ )
1376     {
1377         FREENULL( p_stream->server_entries[i] );
1378     }
1379
1380     chunk_t* chunk = p_stream->chunks_head;
1381     while( chunk )
1382     {
1383         chunk_t* next = chunk->next;
1384         chunk_free( chunk );
1385         chunk = next;
1386     }
1387
1388     free( p_stream );
1389 }
1390
1391 static void SysCleanup( stream_sys_t *p_sys )
1392 {
1393     if ( p_sys->hds_streams )
1394     {
1395         for ( int i=0; i< p_sys->hds_streams->i_count ; i++ )
1396             hds_free( p_sys->hds_streams->pp_elems[i] );
1397         vlc_array_destroy( p_sys->hds_streams );
1398     }
1399     free( p_sys->base_url );
1400 }
1401
1402 static int Open( vlc_object_t *p_this )
1403 {
1404     stream_t *s = (stream_t*)p_this;
1405     stream_sys_t *p_sys;
1406
1407     if( !isHDS( s ) )
1408         return VLC_EGENERIC;
1409
1410     msg_Info( p_this, "HTTP Dynamic Streaming (%s)", s->psz_path );
1411
1412     s->p_sys = p_sys = calloc( 1, sizeof(*p_sys ) );
1413     if( unlikely( p_sys == NULL ) )
1414         return VLC_ENOMEM;
1415
1416     char *uri_without_query = NULL;
1417     size_t pathlen = strcspn( s->psz_path, "?" );
1418     if( unlikely( ( pathlen > INT_MAX ) ||
1419         ( asprintf( &uri_without_query, "%s://%.*s", s->psz_access,
1420                     (int)pathlen, s->psz_path ) < 0 ) ) )
1421     {
1422         free( p_sys );
1423         return VLC_ENOMEM;
1424     }
1425
1426     /* remove the last part of the url */
1427     char *pos = strrchr( uri_without_query, '/');
1428     *pos = '\0';
1429     p_sys->base_url = uri_without_query;
1430
1431     p_sys->flv_header_bytes_sent = 0;
1432
1433     p_sys->hds_streams = vlc_array_new();
1434
1435     if( parse_Manifest( s ) != VLC_SUCCESS )
1436     {
1437         goto error;
1438     }
1439
1440     s->pf_read = Read;
1441     s->pf_peek = Peek;
1442     s->pf_control = Control;
1443
1444     if( vlc_clone( &p_sys->dl_thread, download_thread, s, VLC_THREAD_PRIORITY_INPUT ) )
1445     {
1446         goto error;
1447     }
1448
1449     if( p_sys->live ) {
1450         msg_Info( p_this, "Live stream detected" );
1451
1452         if( vlc_clone( &p_sys->live_thread, live_thread, s, VLC_THREAD_PRIORITY_INPUT ) )
1453         {
1454             goto error;
1455         }
1456     }
1457
1458     return VLC_SUCCESS;
1459
1460 error:
1461     SysCleanup( p_sys );
1462     free( p_sys );
1463     return VLC_EGENERIC;
1464 }
1465
1466 static void Close( vlc_object_t *p_this )
1467 {
1468     stream_t *s = (stream_t*)p_this;
1469     stream_sys_t *p_sys = s->p_sys;
1470
1471     // TODO: Change here for selectable stream
1472     hds_stream_t *stream = vlc_array_count(p_sys->hds_streams) ?
1473         s->p_sys->hds_streams->pp_elems[0] : NULL;
1474
1475     p_sys->closed = true;
1476     if (stream)
1477         vlc_cond_signal( & stream->dl_cond );
1478
1479     vlc_join( p_sys->dl_thread, NULL );
1480
1481     if (stream)
1482     {
1483         vlc_mutex_destroy( &stream->dl_lock );
1484         vlc_cond_destroy( &stream->dl_cond );
1485         vlc_mutex_destroy( &stream->abst_lock );
1486     }
1487
1488     if( p_sys->live )
1489     {
1490         vlc_join( p_sys->live_thread, NULL );
1491     }
1492
1493     SysCleanup( p_sys );
1494     free( p_sys );
1495 }
1496
1497 static unsigned char flv_header[] = {
1498         'F',
1499         'L',
1500         'V',
1501         0x1, //version
1502         0x5, //indicates audio and video
1503         0x0, // length
1504         0x0, // length
1505         0x0, // length
1506         0x9, // length of header
1507         0x0,
1508         0x0,
1509         0x0,
1510         0x0, // initial "trailer"
1511 };
1512
1513 static int send_flv_header( stream_sys_t* p_sys, void* buffer, unsigned i_read,
1514                             bool peek )
1515 {
1516     uint32_t to_be_read = i_read;
1517     if( to_be_read > 13 - p_sys->flv_header_bytes_sent ) {
1518         to_be_read = 13 - p_sys->flv_header_bytes_sent;
1519     }
1520
1521     memcpy( buffer, flv_header + p_sys->flv_header_bytes_sent, to_be_read );
1522
1523     if( ! peek )
1524     {
1525         p_sys->flv_header_bytes_sent += to_be_read;
1526     }
1527     return to_be_read;
1528 }
1529
1530 static unsigned read_chunk_data(
1531     vlc_object_t* p_this,
1532     uint8_t* buffer, unsigned read_len,
1533     hds_stream_t* stream,
1534     bool* eof
1535     )
1536 {
1537     stream_t* s = (stream_t*) p_this;
1538     stream_sys_t* sys = s->p_sys;
1539     chunk_t* chunk = stream->chunks_head;
1540     uint8_t* buffer_start = buffer;
1541     bool dl = false;
1542
1543     if( chunk && chunk->eof && chunk->mdat_pos >= chunk->mdat_len ) {
1544         *eof = true;
1545         return 0;
1546     }
1547
1548     while( chunk && chunk->data && read_len > 0 && ! (chunk->eof && chunk->mdat_pos >= chunk->mdat_len ) )
1549     {
1550         /* in the live case, it is necessary to store the next
1551          * pointer here, since as soon as we increment the mdat_pos, that
1552          * chunk may be deleted */
1553         chunk_t* next = chunk->next;
1554
1555         if( chunk->mdat_pos < chunk->mdat_len )
1556         {
1557             unsigned cp_len = chunk->mdat_len - chunk->mdat_pos;
1558             if( cp_len > read_len )
1559                 cp_len = read_len;
1560             memcpy( buffer, chunk->mdat_data + chunk->mdat_pos,
1561                     cp_len );
1562
1563             read_len -= cp_len;
1564             buffer += cp_len;
1565             chunk->mdat_pos += cp_len;
1566         }
1567
1568         if( ! sys->live && (chunk->mdat_pos >= chunk->mdat_len || chunk->failed) )
1569         {
1570             if( chunk->eof )
1571             {
1572                 *eof = true;
1573             }
1574
1575             /* make sure there is at least one chunk in the queue */
1576             if( ! chunk->next && ! chunk->eof )
1577             {
1578                 chunk->next = generate_new_chunk( p_this, chunk,  stream );
1579                 dl = true;
1580             }
1581
1582             if( ! chunk->eof )
1583             {
1584                 chunk_free( chunk );
1585                 chunk = next;
1586                 stream->chunks_head = chunk;
1587             }
1588         }
1589         else if( sys->live && (chunk->mdat_pos >= chunk->mdat_len || chunk->failed) )
1590         {
1591             chunk = next;
1592         }
1593     }
1594
1595     if( sys->live )
1596     {
1597         stream->chunks_livereadpos = chunk;
1598     }
1599
1600     /* new chunk generation is handled by a different thread in live case */
1601     if( ! sys->live )
1602     {
1603         chunk = stream->chunks_head;
1604         if( chunk )
1605         {
1606             uint64_t total_duration = chunk->duration;
1607             while( chunk && total_duration/stream->afrt_timescale < stream->download_leadtime && ! chunk->eof )
1608             {
1609                 if( ! chunk->next && ! chunk->eof )
1610                 {
1611                     chunk->next = generate_new_chunk( p_this, chunk, stream );
1612                     dl = true;
1613                 }
1614
1615                 if( ! chunk->eof )
1616                 {
1617                     chunk = chunk->next;
1618                     if( chunk )
1619                     {
1620                         total_duration += chunk->duration;
1621                     }
1622                 }
1623             }
1624         }
1625
1626         if( dl )
1627             vlc_cond_signal( & stream->dl_cond );
1628     }
1629
1630     return ( ((uint8_t*)buffer) - ((uint8_t*)buffer_start));
1631 }
1632
1633 static int Read( stream_t *s, void *buffer, unsigned i_read )
1634 {
1635     stream_sys_t *p_sys = s->p_sys;
1636
1637     if ( vlc_array_count( p_sys->hds_streams ) == 0 )
1638         return 0;
1639
1640     // TODO: change here for selectable stream
1641     hds_stream_t *stream = s->p_sys->hds_streams->pp_elems[0];
1642     int length = 0;
1643
1644     uint8_t *buffer_uint8 = (uint8_t*) buffer;
1645
1646     unsigned hdr_bytes = send_flv_header( p_sys, buffer, i_read, false );
1647     length += hdr_bytes;
1648     i_read -= hdr_bytes;
1649     buffer_uint8 += hdr_bytes;
1650
1651     bool eof = false;
1652     while( i_read > 0 && ! eof )
1653     {
1654         int tmp_length = read_chunk_data( (vlc_object_t*)s, buffer_uint8, i_read, stream, &eof );
1655         buffer_uint8 += tmp_length;
1656         i_read -= tmp_length;
1657         length += tmp_length;
1658     }
1659
1660     return length;
1661 }
1662
1663 static int Peek( stream_t *s, const uint8_t **pp_peek, unsigned i_peek )
1664 {
1665     stream_sys_t *p_sys = s->p_sys;
1666
1667     if ( vlc_array_count( p_sys->hds_streams ) == 0 )
1668         return 0;
1669
1670     // TODO: change here for selectable stream
1671     hds_stream_t *stream = p_sys->hds_streams->pp_elems[0];
1672
1673     if( p_sys->flv_header_bytes_sent < 13 )
1674     {
1675         *pp_peek = flv_header + p_sys->flv_header_bytes_sent;
1676         return 13 - p_sys->flv_header_bytes_sent;
1677     }
1678
1679     if( stream->chunks_head && ! stream->chunks_head->failed && stream->chunks_head->data )
1680     {
1681         // TODO: change here for selectable stream
1682         chunk_t* chunk = stream->chunks_head;
1683         *pp_peek = chunk->mdat_data + chunk->mdat_pos;
1684         if( chunk->mdat_len - chunk->mdat_pos < i_peek )
1685         {
1686             return chunk->mdat_len - chunk->mdat_pos;
1687         }
1688         else
1689         {
1690             return i_peek;
1691         }
1692     } else
1693     {
1694         return 0;
1695     }
1696 }
1697
1698 static int Control( stream_t *s, int i_query, va_list args )
1699 {
1700     switch( i_query )
1701     {
1702         case STREAM_CAN_SEEK:
1703             *(va_arg( args, bool * )) = false;
1704             break;
1705         case STREAM_CAN_FASTSEEK:
1706         case STREAM_CAN_PAUSE: /* TODO */
1707             *(va_arg( args, bool * )) = false;
1708             break;
1709         case STREAM_CAN_CONTROL_PACE:
1710             *(va_arg( args, bool * )) = true;
1711             break;
1712         case STREAM_GET_PTS_DELAY:
1713             *va_arg (args, int64_t *) = INT64_C(1000) *
1714                 var_InheritInteger(s, "network-caching");
1715              break;
1716         default:
1717             return VLC_EGENERIC;
1718     }
1719     return VLC_SUCCESS;
1720 }