]> git.sesse.net Git - vlc/blob - modules/stream_filter/hds/hds.c
55acc20298ffc8c111d14435ac5fee17b41076d8
[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         return NULL;
924     }
925
926     int srun_entry = 0;
927     unsigned int segment = 0;
928     uint64_t fragments_accum = chunk->frag_num;
929     for( srun_entry = 0; srun_entry < hds_stream->segment_run_count;
930          srun_entry++ )
931     {
932         segment = hds_stream->segment_runs[srun_entry].first_segment +
933             (chunk->frag_num - fragments_accum ) / hds_stream->segment_runs[srun_entry].fragments_per_segment;
934
935         if( srun_entry + 1 == hds_stream->segment_run_count ||
936             hds_stream->segment_runs[srun_entry+1].first_segment > segment )
937         {
938             break;
939         }
940
941         fragments_accum += (
942             (hds_stream->segment_runs[srun_entry+1].first_segment -
943              hds_stream->segment_runs[srun_entry].first_segment) *
944             hds_stream->segment_runs[srun_entry].fragments_per_segment );
945     }
946
947     chunk->seg_num = segment;
948     chunk->frun_entry = frun_entry;
949
950     if( ! sys->live )
951     {
952         if( (chunk->timestamp + chunk->duration) / hds_stream->afrt_timescale  >= sys->duration_seconds )
953         {
954             chunk->eof = true;
955         }
956     }
957
958     return chunk;
959 }
960
961 static void maintain_live_chunks(
962     vlc_object_t* p_this,
963     hds_stream_t* hds_stream
964     )
965 {
966     if( ! hds_stream->chunks_head )
967     {
968         /* just start with the earliest in the abst
969          * maybe it would be better to use the currentMediaTime?
970          * but then we are right on the edge of buffering, esp for
971          * small fragments */
972         hds_stream->chunks_head = generate_new_chunk(
973             p_this, 0, hds_stream );
974         hds_stream->chunks_livereadpos = hds_stream->chunks_head;
975     }
976
977     chunk_t* chunk = hds_stream->chunks_head;
978     bool dl = false;
979     while( chunk && ( chunk->timestamp * ((uint64_t)hds_stream->timescale) )
980            / ((uint64_t)hds_stream->afrt_timescale)
981            <= hds_stream->live_current_time )
982     {
983         if( chunk->next )
984         {
985             chunk = chunk->next;
986         }
987         else
988         {
989             chunk->next = generate_new_chunk( p_this, chunk, hds_stream );
990             chunk = chunk->next;
991             dl = true;
992         }
993     }
994
995     if( dl )
996         vlc_cond_signal( & hds_stream->dl_cond );
997
998     chunk = hds_stream->chunks_head;
999     while( chunk && chunk->data && chunk->mdat_pos >= chunk->mdat_len && chunk->next )
1000     {
1001         chunk_t* next_chunk = chunk->next;
1002         chunk_free( chunk );
1003         chunk = next_chunk;
1004     }
1005
1006     if( ! hds_stream->chunks_livereadpos )
1007         hds_stream->chunks_livereadpos = hds_stream->chunks_head;
1008
1009     hds_stream->chunks_head = chunk;
1010 }
1011
1012 #define SAFE_STRDUP( dest, src, ret )   \
1013     if( !( (dest) = strdup( (src) ) ) ) \
1014         return (ret)
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         SAFE_STRDUP( abst_url, hds_stream->abst_url, NULL );
1036     }
1037     else
1038     {
1039         char* server_base = sys->base_url;
1040
1041
1042         if( 0 > asprintf( &abst_url, "%s/%s",
1043                           server_base,
1044                           hds_stream->abst_url ) )
1045         {
1046             return NULL;
1047         }
1048     }
1049
1050     mtime_t last_dl_start_time;
1051
1052     while( ! sys->closed )
1053     {
1054         last_dl_start_time = mdate();
1055         stream_t* download_stream = stream_UrlNew( p_this, abst_url );
1056         if( ! download_stream )
1057         {
1058             msg_Err( p_this, "Failed to download abst %s", abst_url );
1059         }
1060         else
1061         {
1062             int64_t size = stream_Size( download_stream );
1063             uint8_t* data = malloc( size );
1064             int read = stream_Read( download_stream, data,
1065                                     size );
1066             if( read < size )
1067             {
1068                 msg_Err( p_this, "Requested %"PRIi64" bytes, "  \
1069                          "but only got %d", size, read );
1070
1071             }
1072             else
1073             {
1074                 vlc_mutex_lock( & hds_stream->abst_lock );
1075                 parse_BootstrapData( p_this, hds_stream,
1076                                      data, data + read );
1077                 vlc_mutex_unlock( & hds_stream->abst_lock );
1078                 maintain_live_chunks( p_this, hds_stream );
1079             }
1080
1081             free( data );
1082
1083             stream_Delete( download_stream );
1084         }
1085
1086         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) );
1087
1088
1089     }
1090
1091     free( abst_url );
1092
1093     vlc_restorecancel( canc );
1094     return NULL;
1095 }
1096
1097 static int parse_Manifest( stream_t *s )
1098 {
1099     xml_t *vlc_xml = NULL;
1100     xml_reader_t *vlc_reader = NULL;
1101     int type = UNKNOWN_ES;
1102     stream_t *st = s->p_source;
1103
1104     msg_Dbg( s, "Manifest parsing\n" );
1105
1106     vlc_xml = xml_Create( st );
1107     if( !vlc_xml )
1108     {
1109         msg_Err( s, "Failed to open XML parser" );
1110         return VLC_EGENERIC;
1111     }
1112
1113     vlc_reader = xml_ReaderCreate( vlc_xml, st );
1114     if( !vlc_reader )
1115     {
1116         msg_Err( s, "Failed to open source for parsing" );
1117         xml_Delete( vlc_xml );
1118         return VLC_EGENERIC;
1119     }
1120
1121     char *node;
1122
1123     stream_sys_t *sys = s->p_sys;
1124
1125     sys->duration_seconds = 0;
1126
1127 #define MAX_BOOTSTRAP_INFO 10
1128     bootstrap_info bootstraps[MAX_BOOTSTRAP_INFO];
1129     uint8_t bootstrap_idx = 0;
1130     memset( bootstraps, 0, sizeof(bootstrap_info) * MAX_BOOTSTRAP_INFO );
1131
1132 #define MAX_MEDIA_ELEMENTS 10
1133     media_info medias[MAX_MEDIA_ELEMENTS];
1134     uint8_t media_idx = 0;
1135     memset( medias, 0, sizeof(media_info) * MAX_MEDIA_ELEMENTS );
1136
1137 #define MAX_XML_DEPTH 256
1138     char* element_stack[256];
1139     uint8_t current_element_idx = 0;
1140     char* current_element = NULL;
1141     memset( element_stack, 0, sizeof(char*) * MAX_XML_DEPTH );
1142
1143     const char* attr_name;
1144     const char* attr_value;
1145
1146     char* media_id = NULL;
1147
1148 #define TIMESCALE 10000000
1149     while( (type = xml_ReaderNextNode( vlc_reader, (const char**) &node )) > 0 )
1150     {
1151         switch( type )
1152         {
1153         case XML_READER_STARTELEM:
1154             if( current_element_idx == 0 && element_stack[current_element_idx] == 0 ) {
1155                 SAFE_STRDUP( element_stack[current_element_idx], node, VLC_ENOMEM );
1156             } else {
1157                 SAFE_STRDUP( element_stack[++current_element_idx], node, VLC_ENOMEM );
1158             }
1159
1160             break;
1161         case XML_READER_ENDELEM:
1162             if( ! strcmp( current_element, "bootstrapInfo") ) {
1163                 if( bootstrap_idx + 1 == MAX_BOOTSTRAP_INFO ) {
1164                     msg_Warn( (vlc_object_t*) s, "Too many bootstraps, ignoring" );
1165                 } else {
1166                     bootstrap_idx++;
1167                 }
1168             }
1169
1170             free( current_element );
1171             element_stack[current_element_idx--] = 0;
1172             break;
1173         }
1174
1175         if( ! element_stack[current_element_idx] ) {
1176             continue;
1177         }
1178
1179         current_element = element_stack[current_element_idx];
1180
1181         if( type == XML_READER_STARTELEM && ! strcmp( current_element, "media") )
1182         {
1183             if( media_idx == MAX_MEDIA_ELEMENTS )
1184             {
1185                 msg_Err( (vlc_object_t*) s, "Too many media elements, quitting" );
1186                 return VLC_EGENERIC;
1187             }
1188
1189             while( ( attr_name = xml_ReaderNextAttr( vlc_reader, &attr_value )) )
1190             {
1191                 if( !strcmp(attr_name, "streamId" ) )
1192                 {
1193                     SAFE_STRDUP( medias[media_idx].stream_id, attr_value, VLC_ENOMEM );
1194                 }
1195                 if( !strcmp(attr_name, "url" ) )
1196                 {
1197                     SAFE_STRDUP( medias[media_idx].media_url, attr_value, VLC_ENOMEM );
1198                 }
1199                 if( !strcmp(attr_name, "bootstrapInfoId" ) )
1200                 {
1201                     SAFE_STRDUP( medias[media_idx].bootstrap_id, attr_value, VLC_ENOMEM );
1202                 }
1203             }
1204
1205             media_idx++;
1206         }
1207
1208         if( type == XML_READER_STARTELEM && ! strcmp( current_element, "bootstrapInfo") )
1209         {
1210             while( ( attr_name = xml_ReaderNextAttr( vlc_reader, &attr_value )) )
1211             {
1212                 if( !strcmp(attr_name, "url" ) )
1213                 {
1214                     SAFE_STRDUP( bootstraps[bootstrap_idx].url, attr_value, VLC_ENOMEM );
1215                 }
1216                 if( !strcmp(attr_name, "id" ) )
1217                 {
1218                     SAFE_STRDUP( bootstraps[bootstrap_idx].id, attr_value, VLC_ENOMEM );
1219                 }
1220                 if( !strcmp(attr_name, "profile" ) )
1221                 {
1222                     SAFE_STRDUP( bootstraps[bootstrap_idx].profile, attr_value, VLC_ENOMEM );
1223                 }
1224             }
1225         }
1226
1227         if( type == XML_READER_TEXT )
1228         {
1229             if( ! strcmp( current_element, "bootstrapInfo" ) )
1230             {
1231                 char* start = node;
1232                 char* end = start + strlen(start);
1233                 whitespace_substr( &start, &end );
1234                 *end = '\0';
1235
1236                 bootstraps[bootstrap_idx].data_len =
1237                     vlc_b64_decode_binary( (uint8_t**)&bootstraps[bootstrap_idx].data, start );
1238                 if( ! bootstraps[bootstrap_idx].data )
1239                 {
1240                     msg_Err( (vlc_object_t*) s, "Couldn't decode bootstrap info" );
1241                 }
1242             }
1243             if( ! strcmp( current_element, "duration" ) )
1244             {
1245                 double shutup_gcc = atof( node );
1246                 sys->duration_seconds = (uint64_t) shutup_gcc;
1247             }
1248             if( ! strcmp( current_element, "id" ) )
1249             {
1250                 if( current_element &&
1251                     ! strcmp( element_stack[current_element_idx-1], "manifest" ) )
1252                 {
1253                     SAFE_STRDUP( media_id, node, VLC_ENOMEM );
1254                 }
1255             }
1256         }
1257     }
1258
1259     xml_ReaderDelete( vlc_reader );
1260     xml_Delete( vlc_xml );
1261
1262     for( int i = 0; i <= media_idx; i++ )
1263     {
1264         for( int j = 0; j < bootstrap_idx; j++ )
1265         {
1266             if( ( ! medias[i].bootstrap_id && ! bootstraps[j].id ) ||
1267                 (medias[i].bootstrap_id && bootstraps[j].id &&
1268                  ! strcmp( medias[i].bootstrap_id, bootstraps[j].id ) ) )
1269             {
1270                 hds_stream_t* new_stream = malloc(sizeof(hds_stream_t));
1271                 memset( new_stream, 0, sizeof(hds_stream_t));
1272
1273                 vlc_mutex_init( & new_stream->abst_lock );
1274                 vlc_mutex_init( & new_stream->dl_lock );
1275                 vlc_cond_init( & new_stream->dl_cond );
1276
1277                 if( sys->duration_seconds )
1278                 {
1279                     sys->live = false;
1280                 }
1281                 else
1282                 {
1283                     sys->live = true;
1284                 }
1285
1286                 if( medias[i].media_url )
1287                 {
1288                     SAFE_STRDUP( new_stream->url, medias[i].media_url, VLC_ENOMEM );
1289                 }
1290
1291                 if( ! sys->live )
1292                 {
1293                     parse_BootstrapData( (vlc_object_t*)s,
1294                                          new_stream,
1295                                          bootstraps[j].data,
1296                                          bootstraps[j].data + bootstraps[j].data_len );
1297
1298                     new_stream->download_leadtime = 15;
1299
1300                     new_stream->chunks_head = generate_new_chunk(
1301                         (vlc_object_t*) s, 0, new_stream );
1302                     chunk_t* chunk = new_stream->chunks_head;
1303                     uint64_t total_duration = chunk->duration;
1304                     while( chunk && total_duration/new_stream->afrt_timescale < new_stream->download_leadtime )
1305                     {
1306                         chunk->next = generate_new_chunk(
1307                             (vlc_object_t*) s, chunk, new_stream );
1308                         chunk = chunk->next;
1309                         if( chunk )
1310                             total_duration += chunk->duration;
1311                     }
1312                 }
1313                 else
1314                 {
1315                     SAFE_STRDUP( new_stream->abst_url, bootstraps[j].url, VLC_ENOMEM );
1316                 }
1317
1318                 vlc_array_append( sys->hds_streams, new_stream );
1319
1320                 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)",
1321                           new_stream->quality_segment_modifier?"":new_stream->quality_segment_modifier, new_stream->timescale,
1322                           new_stream->movie_id, new_stream->segment_run_count, new_stream->fragment_run_count );
1323
1324             }
1325         }
1326     }
1327
1328     for( int i = 0; i < MAX_MEDIA_ELEMENTS; i++ )
1329     {
1330         FREENULL( medias[media_idx].stream_id );
1331         FREENULL( medias[media_idx].media_url );
1332         FREENULL( medias[media_idx].bootstrap_id );
1333     }
1334
1335     for( int i = 0; i < MAX_BOOTSTRAP_INFO; i++ )
1336     {
1337         FREENULL( bootstraps[i].data );
1338         FREENULL( bootstraps[i].id );
1339         FREENULL( bootstraps[i].url );
1340         FREENULL( bootstraps[i].profile );
1341     }
1342
1343     FREENULL( media_id );
1344
1345     return VLC_SUCCESS;
1346 }
1347
1348 #undef SAFE_STRDUP
1349
1350 static void hds_free( hds_stream_t *p_stream )
1351 {
1352     FREENULL( p_stream->quality_segment_modifier );
1353
1354     FREENULL( p_stream->abst_url );
1355
1356     FREENULL( p_stream->url );
1357     FREENULL( p_stream->movie_id );
1358     for( int i = 0; i < p_stream->server_entry_count; i++ )
1359     {
1360         FREENULL( p_stream->server_entries[i] );
1361     }
1362
1363     chunk_t* chunk = p_stream->chunks_head;
1364     while( chunk )
1365     {
1366         chunk_t* next = chunk->next;
1367         chunk_free( chunk );
1368         chunk = next;
1369     }
1370
1371     free( p_stream );
1372 }
1373
1374 static void SysCleanup( stream_sys_t *p_sys )
1375 {
1376     if ( p_sys->hds_streams )
1377     {
1378         for ( int i=0; i< p_sys->hds_streams->i_count ; i++ )
1379             hds_free( p_sys->hds_streams->pp_elems[i] );
1380         vlc_array_destroy( p_sys->hds_streams );
1381     }
1382     free( p_sys->base_url );
1383 }
1384
1385 static int Open( vlc_object_t *p_this )
1386 {
1387     stream_t *s = (stream_t*)p_this;
1388     stream_sys_t *p_sys;
1389
1390     if( !isHDS( s ) )
1391         return VLC_EGENERIC;
1392
1393     msg_Info( p_this, "HTTP Dynamic Streaming (%s)", s->psz_path );
1394
1395     s->p_sys = p_sys = calloc( 1, sizeof(*p_sys ) );
1396     if( unlikely( p_sys == NULL ) )
1397         return VLC_ENOMEM;
1398
1399     char *uri = NULL;
1400     if( unlikely( asprintf( &uri, "%s://%s", s->psz_access, s->psz_path ) < 0 ) )
1401     {
1402         free( p_sys );
1403         return VLC_ENOMEM;
1404     }
1405
1406     /* remove the last part of the url */
1407     char *pos = strrchr( uri, '/');
1408     *pos = '\0';
1409     p_sys->base_url = uri;
1410
1411     p_sys->flv_header_bytes_sent = 0;
1412
1413     p_sys->hds_streams = vlc_array_new();
1414
1415     if( parse_Manifest( s ) != VLC_SUCCESS )
1416     {
1417         goto error;
1418     }
1419
1420     s->pf_read = Read;
1421     s->pf_peek = Peek;
1422     s->pf_control = Control;
1423
1424     if( vlc_clone( &p_sys->dl_thread, download_thread, s, VLC_THREAD_PRIORITY_INPUT ) )
1425     {
1426         goto error;
1427     }
1428
1429     if( p_sys->live ) {
1430         msg_Info( p_this, "Live stream detected" );
1431
1432         if( vlc_clone( &p_sys->live_thread, live_thread, s, VLC_THREAD_PRIORITY_INPUT ) )
1433         {
1434             goto error;
1435         }
1436     }
1437
1438     return VLC_SUCCESS;
1439
1440 error:
1441     SysCleanup( p_sys );
1442     free( p_sys );
1443     return VLC_EGENERIC;
1444 }
1445
1446 static void Close( vlc_object_t *p_this )
1447 {
1448     stream_t *s = (stream_t*)p_this;
1449     stream_sys_t *p_sys = s->p_sys;
1450
1451     // TODO: Change here for selectable stream
1452     hds_stream_t *stream = vlc_array_count(p_sys->hds_streams) ?
1453         s->p_sys->hds_streams->pp_elems[0] : NULL;
1454
1455     p_sys->closed = true;
1456     if (stream)
1457         vlc_cond_signal( & stream->dl_cond );
1458
1459     vlc_join( p_sys->dl_thread, NULL );
1460
1461     if (stream)
1462     {
1463         vlc_mutex_destroy( &stream->dl_lock );
1464         vlc_cond_destroy( &stream->dl_cond );
1465         vlc_mutex_destroy( &stream->abst_lock );
1466     }
1467
1468     if( p_sys->live )
1469     {
1470         vlc_join( p_sys->live_thread, NULL );
1471     }
1472
1473     SysCleanup( p_sys );
1474     free( p_sys );
1475 }
1476
1477 static unsigned char flv_header[] = {
1478         'F',
1479         'L',
1480         'V',
1481         0x1, //version
1482         0x5, //indicates audio and video
1483         0x0, // length
1484         0x0, // length
1485         0x0, // length
1486         0x9, // length of header
1487         0x0,
1488         0x0,
1489         0x0,
1490         0x0, // initial "trailer"
1491 };
1492
1493 static int send_flv_header( stream_sys_t* p_sys, void* buffer, unsigned i_read,
1494                             bool peek )
1495 {
1496     uint32_t to_be_read = i_read;
1497     if( to_be_read > 13 - p_sys->flv_header_bytes_sent ) {
1498         to_be_read = 13 - p_sys->flv_header_bytes_sent;
1499     }
1500
1501     memcpy( buffer, flv_header + p_sys->flv_header_bytes_sent, to_be_read );
1502
1503     if( ! peek )
1504     {
1505         p_sys->flv_header_bytes_sent += to_be_read;
1506     }
1507     return to_be_read;
1508 }
1509
1510 static unsigned read_chunk_data(
1511     vlc_object_t* p_this,
1512     uint8_t* buffer, unsigned read_len,
1513     hds_stream_t* stream,
1514     bool* eof
1515     )
1516 {
1517     stream_t* s = (stream_t*) p_this;
1518     stream_sys_t* sys = s->p_sys;
1519     chunk_t* chunk = stream->chunks_head;
1520     uint8_t* buffer_start = buffer;
1521     bool dl = false;
1522
1523     if( chunk && chunk->eof && chunk->mdat_pos >= chunk->mdat_len ) {
1524         *eof = true;
1525         return 0;
1526     }
1527
1528     while( chunk && chunk->data && read_len > 0 && ! (chunk->eof && chunk->mdat_pos >= chunk->mdat_len ) )
1529     {
1530         /* in the live case, it is necessary to store the next
1531          * pointer here, since as soon as we increment the mdat_pos, that
1532          * chunk may be deleted */
1533         chunk_t* next = chunk->next;
1534
1535         if( chunk->mdat_pos < chunk->mdat_len )
1536         {
1537             unsigned cp_len = chunk->mdat_len - chunk->mdat_pos;
1538             if( cp_len > read_len )
1539                 cp_len = read_len;
1540             memcpy( buffer, chunk->mdat_data + chunk->mdat_pos,
1541                     cp_len );
1542
1543             read_len -= cp_len;
1544             buffer += cp_len;
1545             chunk->mdat_pos += cp_len;
1546         }
1547
1548         if( ! sys->live && (chunk->mdat_pos >= chunk->mdat_len || chunk->failed) )
1549         {
1550             if( chunk->eof )
1551             {
1552                 *eof = true;
1553             }
1554
1555             /* make sure there is at least one chunk in the queue */
1556             if( ! chunk->next && ! chunk->eof )
1557             {
1558                 chunk->next = generate_new_chunk( p_this, chunk,  stream );
1559                 dl = true;
1560             }
1561
1562             if( ! chunk->eof )
1563             {
1564                 chunk_free( chunk );
1565                 chunk = next;
1566                 stream->chunks_head = chunk;
1567             }
1568         }
1569         else if( sys->live && (chunk->mdat_pos >= chunk->mdat_len || chunk->failed) )
1570         {
1571             chunk = next;
1572         }
1573     }
1574
1575     if( sys->live )
1576     {
1577         stream->chunks_livereadpos = chunk;
1578     }
1579
1580     /* new chunk generation is handled by a different thread in live case */
1581     if( ! sys->live )
1582     {
1583         chunk = stream->chunks_head;
1584         if( chunk )
1585         {
1586             uint64_t total_duration = chunk->duration;
1587             while( chunk && total_duration/stream->afrt_timescale < stream->download_leadtime && ! chunk->eof )
1588             {
1589                 if( ! chunk->next && ! chunk->eof )
1590                 {
1591                     chunk->next = generate_new_chunk( p_this, chunk, stream );
1592                     dl = true;
1593                 }
1594
1595                 if( ! chunk->eof )
1596                 {
1597                     chunk = chunk->next;
1598                     if( chunk )
1599                     {
1600                         total_duration += chunk->duration;
1601                     }
1602                 }
1603             }
1604         }
1605
1606         if( dl )
1607             vlc_cond_signal( & stream->dl_cond );
1608     }
1609
1610     return ( ((uint8_t*)buffer) - ((uint8_t*)buffer_start));
1611 }
1612
1613 static int Read( stream_t *s, void *buffer, unsigned i_read )
1614 {
1615     stream_sys_t *p_sys = s->p_sys;
1616
1617     if ( vlc_array_count( p_sys->hds_streams ) == 0 )
1618         return 0;
1619
1620     // TODO: change here for selectable stream
1621     hds_stream_t *stream = s->p_sys->hds_streams->pp_elems[0];
1622     int length = 0;
1623
1624     uint8_t *buffer_uint8 = (uint8_t*) buffer;
1625
1626     unsigned hdr_bytes = send_flv_header( p_sys, buffer, i_read, false );
1627     length += hdr_bytes;
1628     i_read -= hdr_bytes;
1629     buffer_uint8 += hdr_bytes;
1630
1631     bool eof = false;
1632     while( i_read > 0 && ! eof )
1633     {
1634         int tmp_length = read_chunk_data( (vlc_object_t*)s, buffer_uint8, i_read, stream, &eof );
1635         buffer_uint8 += tmp_length;
1636         i_read -= tmp_length;
1637         length += tmp_length;
1638     }
1639
1640     return length;
1641 }
1642
1643 static int Peek( stream_t *s, const uint8_t **pp_peek, unsigned i_peek )
1644 {
1645     stream_sys_t *p_sys = s->p_sys;
1646
1647     if ( vlc_array_count( p_sys->hds_streams ) == 0 )
1648         return 0;
1649
1650     // TODO: change here for selectable stream
1651     hds_stream_t *stream = p_sys->hds_streams->pp_elems[0];
1652
1653     if( p_sys->flv_header_bytes_sent < 13 )
1654     {
1655         *pp_peek = flv_header + p_sys->flv_header_bytes_sent;
1656         return 13 - p_sys->flv_header_bytes_sent;
1657     }
1658
1659     if( stream->chunks_head && ! stream->chunks_head->failed && stream->chunks_head->data )
1660     {
1661         // TODO: change here for selectable stream
1662         chunk_t* chunk = stream->chunks_head;
1663         *pp_peek = chunk->mdat_data + chunk->mdat_pos;
1664         if( chunk->mdat_len - chunk->mdat_pos < i_peek )
1665         {
1666             return chunk->mdat_len - chunk->mdat_pos;
1667         }
1668         else
1669         {
1670             return i_peek;
1671         }
1672     } else
1673     {
1674         return 0;
1675     }
1676 }
1677
1678 static int Control( stream_t *s, int i_query, va_list args )
1679 {
1680     switch( i_query )
1681     {
1682         case STREAM_CAN_SEEK:
1683             *(va_arg( args, bool * )) = false;
1684             break;
1685         case STREAM_CAN_FASTSEEK:
1686         case STREAM_CAN_PAUSE: /* TODO */
1687             *(va_arg( args, bool * )) = false;
1688             break;
1689         case STREAM_CAN_CONTROL_PACE:
1690             *(va_arg( args, bool * )) = true;
1691             break;
1692         case STREAM_GET_PTS_DELAY:
1693             *va_arg (args, int64_t *) = INT64_C(1000) *
1694                 var_InheritInteger(s, "network-caching");
1695              break;
1696         default:
1697             return VLC_EGENERIC;
1698     }
1699     return VLC_SUCCESS;
1700 }