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