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