]> git.sesse.net Git - vlc/blob - modules/stream_filter/httplive.c
stream_filter/httplive.c: HLS stream size calculation
[vlc] / modules / stream_filter / httplive.c
1 /*****************************************************************************
2  * httplive.c: HTTP Live Streaming stream filter
3  *****************************************************************************
4  * Copyright (C) 2010 M2X BV
5  * $Id$
6  *
7  * Author: Jean-Paul Saman <jpsaman _AT_ videolan _DOT_ org>
8  *
9  * This program is free software; you can redistribute it and/or modify
10  * it under the terms of the GNU General Public License as published by
11  * the Free Software Foundation; either version 2 of the License, or
12  * (at your option) any later version.
13  *
14  * This program is distributed in the hope that it will be useful,
15  * but WITHOUT ANY WARRANTY; without even the implied warranty of
16  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
17  * GNU General Public License for more details.
18  *
19  * You should have received a copy of the GNU General Public License
20  * along with this program; if not, write to the Free Software
21  * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston MA 02110-1301, USA.
22  *****************************************************************************/
23
24 /*****************************************************************************
25  * Preamble
26  *****************************************************************************/
27 #ifdef HAVE_CONFIG_H
28 # include "config.h"
29 #endif
30
31 #include <vlc_common.h>
32 #include <vlc_plugin.h>
33
34 #include <assert.h>
35
36 #include <vlc_threads.h>
37 #include <vlc_arrays.h>
38 #include <vlc_stream.h>
39 #include <vlc_url.h>
40
41 #include <vlc_modules.h>
42 #include <vlc_access.h>
43
44 /*****************************************************************************
45  * Module descriptor
46  *****************************************************************************/
47 static int  Open (vlc_object_t *);
48 static void Close(vlc_object_t *);
49
50 vlc_module_begin()
51     set_category(CAT_INPUT)
52     set_subcategory(SUBCAT_INPUT_STREAM_FILTER)
53     set_description(N_("Http Live Streaming stream filter"))
54     set_capability("stream_filter", 20)
55     set_callbacks(Open, Close)
56 vlc_module_end()
57
58 /*****************************************************************************
59  *
60  *****************************************************************************/
61 typedef struct segment_s
62 {
63     int         sequence;   /* unique sequence number */
64     int         duration;     /* segment duration (seconds) */
65     uint64_t    size;       /* segment size in bytes */
66     uint64_t    bandwidth;  /* bandwidth usage of segments (bits per second)*/
67
68     vlc_url_t   url;
69     vlc_mutex_t lock;
70     block_t     *data;      /* data */
71 } segment_t;
72
73 typedef struct hls_stream_s
74 {
75     int         id;         /* program id */
76     int         version;    /* protocol version should be 1 */
77     int         sequence;   /* media sequence number */
78     int         duration;   /* maximum duration per segment (ms) */
79     uint64_t    bandwidth;  /* bandwidth usage of segments (bits per second)*/
80     uint64_t    size;       /* stream length (segment->duration * hls->bandwidth/8) */
81
82     vlc_array_t *segments;  /* list of segments */
83     vlc_url_t   url;        /* uri to m3u8 */
84     vlc_mutex_t lock;
85     bool        b_cache;    /* allow caching */
86 } hls_stream_t;
87
88 typedef struct
89 {
90     VLC_COMMON_MEMBERS
91
92     /* */
93     int         current;    /* current hls_stream  */
94     int         segment;    /* current segment for downloading */
95     vlc_array_t *hls_stream;/* bandwidth adaptation */
96
97     stream_t    *s;
98 } hls_thread_t;
99
100 struct stream_sys_t
101 {
102     access_t    *p_access;  /* HTTP access input */
103
104     /* */
105     hls_thread_t *thread;
106     vlc_array_t *hls_stream;/* bandwidth adaptation */
107
108     /* Playback */
109     uint64_t    offset;     /* current offset in media */
110     int         current;    /* current hls_stream  */
111     int         segment;    /* current segment for playback */
112
113     /* Playlist */
114     mtime_t     last;       /* playlist last loaded */
115     mtime_t     wakeup;     /* next reload time */
116     int         tries;      /* times it was not changed */
117
118     /* state */
119     bool        b_cache;    /* can cache files */
120     bool        b_meta;     /* meta playlist */
121     bool        b_live;     /* live stream? or vod? */
122     bool        b_error;    /* parsing error */
123 };
124
125 /****************************************************************************
126  * Local prototypes
127  ****************************************************************************/
128 static int  Read   (stream_t *, void *p_read, unsigned int i_read);
129 static int  Peek   (stream_t *, const uint8_t **pp_peek, unsigned int i_peek);
130 static int  Control(stream_t *, int i_query, va_list);
131
132 static int  AccessOpen(stream_t *s, vlc_url_t *url);
133 static void AccessClose(stream_t *s);
134 static char *AccessReadLine(access_t *p_access, uint8_t *psz_tmp, size_t i_len);
135 static int AccessDownload(stream_t *s, segment_t *segment);
136
137 static void* hls_Thread(vlc_object_t *);
138 static int get_HTTPLivePlaylist(stream_t *s, hls_stream_t *hls);
139
140 static segment_t *segment_GetSegment(hls_stream_t *hls, int wanted);
141 static void segment_Free(segment_t *segment);
142
143 /****************************************************************************
144  *
145  ****************************************************************************/
146 static bool isHTTPLiveStreaming(stream_t *s)
147 {
148     const uint8_t *peek, *peek_end;
149
150     int64_t i_size = stream_Peek(s->p_source, &peek, 46);
151     if (i_size < 1)
152         return false;
153
154     if (strncasecmp((const char*)peek, "#EXTM3U", 7) != 0)
155         return false;
156
157     /* Parse stream and search for
158      * EXT-X-TARGETDURATION or EXT-X-STREAM-INF tag, see
159      * http://tools.ietf.org/html/draft-pantos-http-live-streaming-04#page-8 */
160     peek_end = peek + i_size;
161     while(peek <= peek_end)
162     {
163         if (*peek == '#')
164         {
165             if (strncasecmp((const char*)peek, "#EXT-X-TARGETDURATION", 21) == 0)
166                 return true;
167             else if (strncasecmp((const char*)peek, "#EXT-X-STREAM-INF", 17) == 0)
168                 return true;
169         }
170         peek++;
171     };
172
173     return false;
174 }
175
176 /* HTTP Live Streaming */
177 static hls_stream_t *hls_New(vlc_array_t *hls_stream, int id, uint64_t bw, char *uri)
178 {
179     hls_stream_t *hls = (hls_stream_t *)malloc(sizeof(hls_stream_t));
180     if (hls == NULL) return NULL;
181
182     hls->id = id;
183     hls->bandwidth = bw;
184     hls->duration = -1;/* unknown */
185     hls->size = 0;
186     hls->sequence = 0; /* default is 0 */
187     hls->version = 1;  /* default protocol version */
188     hls->b_cache = true;
189     vlc_UrlParse(&hls->url, uri, 0);
190     hls->segments = vlc_array_new();
191     vlc_array_append(hls_stream, hls);
192     vlc_mutex_init(&hls->lock);
193     return hls;
194 }
195
196 static void hls_Free(hls_stream_t *hls)
197 {
198     vlc_mutex_destroy(&hls->lock);
199
200     if (hls->segments)
201     {
202         for (int n = 0; n < vlc_array_count(hls->segments); n++)
203         {
204             segment_t *segment = (segment_t *)vlc_array_item_at_index(hls->segments, n);
205             if (segment) segment_Free(segment);
206         }
207         vlc_array_destroy(hls->segments);
208     }
209
210     vlc_UrlClean(&hls->url);
211     free(hls);
212     hls = NULL;
213 }
214
215 static hls_stream_t *hls_Get(vlc_array_t *hls_stream, int wanted)
216 {
217     int count = vlc_array_count(hls_stream);
218     if (count <= 0)
219         return NULL;
220     if ((wanted < 0) || (wanted >= count))
221         return NULL;
222     return (hls_stream_t *) vlc_array_item_at_index(hls_stream, wanted);
223 }
224
225 static inline hls_stream_t *hls_GetFirst(vlc_array_t *hls_stream)
226 {
227     return (hls_stream_t*) hls_Get(hls_stream, 0);
228 }
229
230 static hls_stream_t *hls_GetLast(vlc_array_t *hls_stream)
231 {
232     int count = vlc_array_count(hls_stream);
233     if (count <= 0)
234         return NULL;
235     count--;
236     return (hls_stream_t *) hls_Get(hls_stream, count);
237 }
238
239 static uint64_t hls_GetStreamSize(hls_stream_t *hls)
240 {
241     /* NOTE: Stream size is calculated based on segment duration and
242      * HLS stream bandwidth from the .m3u8 file. If these are not correct
243      * then the deviation from exact byte size will be big and the seek/
244      * progressbar will not behave entirely as one expects. */
245     uint64_t size = 0UL;
246     int count = vlc_array_count(hls->segments);
247     for (int n = 0; n < count; n++)
248     {
249         segment_t *segment = segment_GetSegment(hls, n);
250         if (segment)
251         {
252             size += (segment->duration * (hls->bandwidth / 8));
253         }
254     }
255     return size;
256 }
257
258 /* Segment */
259 static segment_t *segment_New(hls_stream_t* hls, int duration, char *uri)
260 {
261     segment_t *segment = (segment_t *)malloc(sizeof(segment_t));
262     if (segment == NULL)
263         return NULL;
264
265     segment->duration = duration; /* seconds */
266     segment->size = 0; /* bytes */
267     segment->sequence = 0;
268     segment->bandwidth = 0;
269     vlc_UrlParse(&segment->url, uri, 0);
270     segment->data = NULL;
271     vlc_array_append(hls->segments, segment);
272     vlc_mutex_init(&segment->lock);
273     return segment;
274 }
275
276 static void segment_Free(segment_t *segment)
277 {
278     vlc_mutex_destroy(&segment->lock);
279
280     vlc_UrlClean(&segment->url);
281     if (segment->data)
282         block_Release(segment->data);
283     free(segment);
284     segment = NULL;
285 }
286
287 static segment_t *segment_GetSegment(hls_stream_t *hls, int wanted)
288 {
289     assert(hls);
290
291     int count = vlc_array_count(hls->segments);
292     if (count <= 0)
293         return NULL;
294     if ((wanted < 0) || (wanted >= count))
295         return NULL;
296     return (segment_t *) vlc_array_item_at_index(hls->segments, wanted);
297 }
298
299 /* Parsing */
300 static char *parse_Attributes(const char *line, const char *attr)
301 {
302     char *p;
303     char *begin = (char *) line;
304     char *end = begin + strlen(line);
305
306     /* Find start of attributes */
307     if ((p = strchr(begin, ':' )) == NULL)
308         return NULL;
309
310     begin = p;
311     do
312     {
313         if (strncasecmp(begin, attr, strlen(attr)) == 0)
314         {
315             /* <attr>=<value>[,]* */
316             p = strchr(begin, ',');
317             begin += strlen(attr) + 1;
318             if (begin >= end)
319                 return NULL;
320             if (p == NULL) /* last attribute */
321                 return strndup(begin, end - begin);
322             /* copy till ',' */
323             return strndup(begin, p - begin);
324         }
325         begin++;
326     } while(begin < end);
327
328     return NULL;
329 }
330
331 static char *relative_URI(stream_t *s, const char *uri, char *psz_uri)
332 {
333     char *p = strchr(uri, ':');
334     if (p != NULL)
335         return NULL;
336
337     char *tmp;
338     if (asprintf(&tmp,"%s://%s", s->psz_access, s->psz_path) < 0)
339     {
340         s->p_sys->b_error = true;
341         return NULL;
342     }
343
344     char *psz_path = strrchr(tmp, '/');
345     if (psz_path) *psz_path = '\0';
346
347     vlc_url_t url;
348     vlc_UrlParse(&url, tmp, 0);
349     if (asprintf(&psz_uri, "%s://%s%s/%s",
350            url.psz_protocol, url.psz_host, url.psz_path, uri) < 0)
351     {
352         free(tmp);
353         vlc_UrlClean(&url);
354         s->p_sys->b_error = true;
355         return NULL;
356     }
357     vlc_UrlClean(&url);
358     free(tmp);
359     return psz_uri;
360 }
361
362 static void parse_SegmentInformation(stream_t *s, hls_stream_t *hls, char *p_read, char *uri)
363 {
364     stream_sys_t *p_sys = s->p_sys;
365
366     assert(hls);
367
368     int duration;
369     int ret = sscanf(p_read, "#EXTINF:%d,", &duration);
370     if (ret != 1)
371     {
372         msg_Err(s, "expected #EXTINF:<s>,");
373         p_sys->b_error = true;
374         return;
375     }
376
377     char *psz_uri = NULL;
378     psz_uri = relative_URI(s, uri, psz_uri);
379
380     vlc_mutex_lock(&hls->lock);
381     segment_t *segment = segment_New(hls, duration, psz_uri ? psz_uri : uri);
382     if (segment)
383         segment->sequence = hls->sequence + vlc_array_count(hls->segments) - 1;
384     if (duration > hls->duration)
385     {
386         msg_Err(s, "EXTINF:%d duration is larger then EXT-X-TARGETDURATION:%d",
387                 duration, hls->duration);
388     }
389     vlc_mutex_unlock(&hls->lock);
390
391     free(psz_uri);
392 }
393
394 static void parse_TargetDuration(stream_t *s, hls_stream_t *hls, char *p_read)
395 {
396     stream_sys_t *p_sys = s->p_sys;
397
398     assert(hls);
399
400     int duration = -1;
401     int ret = sscanf(p_read, "#EXT-X-TARGETDURATION:%d", &duration);
402     if (ret != 1)
403     {
404         msg_Err(s, "expected #EXT-X-TARGETDURATION:<s>");
405         p_sys->b_error = true;
406         return;
407     }
408
409     hls->duration = duration; /* seconds */
410 }
411
412 static void parse_StreamInformation(stream_t *s, char *p_read, char *uri)
413 {
414     stream_sys_t *p_sys = s->p_sys;
415
416     int id;
417     uint64_t bw;
418     char *attr;
419
420     attr = parse_Attributes(p_read, "PROGRAM-ID");
421     if (attr == NULL)
422     {
423         msg_Err(s, "#EXT-X-STREAM-INF: expected PROGRAM-ID=<value>");
424         p_sys->b_error = true;
425         return;
426     }
427     id = atol(attr);
428     free(attr);
429
430     attr = parse_Attributes(p_read, "BANDWIDTH");
431     if (attr == NULL)
432     {
433         msg_Err(s, "#EXT-X-STREAM-INF: expected BANDWIDTH=<value>");
434         p_sys->b_error = true;
435         return;
436     }
437     bw = atoll(attr);
438     free(attr);
439
440     if (bw == 0)
441     {
442         msg_Err(s, "#EXT-X-STREAM-INF: bandwidth cannot be 0");
443         p_sys->b_error = true;
444         return;
445     }
446
447     msg_Info(s, "bandwidth adaption detected (program-id=%d, bandwidth=%"PRIu64").", id, bw);
448
449     char *psz_uri = NULL;
450     psz_uri = relative_URI(s, uri, psz_uri);
451
452     hls_stream_t *hls = hls_New(p_sys->hls_stream, id, bw, psz_uri ? psz_uri : uri);
453     if (hls == NULL)
454         p_sys->b_error = true;
455
456     free(psz_uri);
457 }
458
459 static void parse_MediaSequence(stream_t *s, hls_stream_t *hls, char *p_read)
460 {
461     stream_sys_t *p_sys = s->p_sys;
462
463     assert(hls);
464
465     int sequence;
466     int ret = sscanf(p_read, "#EXT-X-MEDIA-SEQUENCE:%d", &sequence);
467     if (ret != 1)
468     {
469         msg_Err(s, "expected #EXT-X-MEDIA-SEQUENCE:<s>");
470         p_sys->b_error = true;
471         return;
472     }
473
474     if (hls->sequence > 0)
475         msg_Err(s, "EXT-X-MEDIA-SEQUENCE already present in playlist");
476
477     hls->sequence = sequence;
478 }
479
480 static void parse_Key(stream_t *s, hls_stream_t *hls, char *p_read)
481 {
482     stream_sys_t *p_sys = s->p_sys;
483
484     assert(hls);
485
486     /* #EXT-X-KEY:METHOD=<method>[,URI="<URI>"][,IV=<IV>] */
487     char *attr;
488     attr = parse_Attributes(p_read, "METHOD");
489     if (attr == NULL)
490     {
491         msg_Err(s, "#EXT-X-KEY: expected METHOD=<value>");
492         p_sys->b_error = true;
493     }
494     else if (strncasecmp(attr, "NONE", 4) == 0)
495     {
496         char *uri = parse_Attributes(p_read, "URI");
497         if (uri != NULL)
498         {
499             msg_Err(s, "#EXT-X-KEY: URI not expected");
500             p_sys->b_error = true;
501         }
502         free(uri);
503         /* IV is only supported in version 2 and above */
504         if (hls->version >= 2)
505         {
506             char *iv = parse_Attributes(p_read, "IV");
507             if (iv != NULL)
508             {
509                 msg_Err(s, "#EXT-X-KEY: IV not expected");
510                 p_sys->b_error = true;
511             }
512             free(iv);
513         }
514     }
515     else
516     {
517         msg_Warn(s, "playback of encrypted HTTP Live media is not supported.");
518         p_sys->b_error = true;
519     }
520     free(attr);
521 }
522
523 static void parse_ProgramDateTime(stream_t *s, hls_stream_t *hls, char *p_read)
524 {
525     VLC_UNUSED(hls);
526     msg_Dbg(s, "tag not supported: #EXT-X-PROGRAM-DATE-TIME %s", p_read);
527 }
528
529 static void parse_AllowCache(stream_t *s, hls_stream_t *hls, char *p_read)
530 {
531     stream_sys_t *p_sys = s->p_sys;
532
533     assert(hls);
534
535     char answer[4] = "\0";
536     int ret = sscanf(p_read, "#EXT-X-ALLOW-CACHE:%3s", answer);
537     if (ret != 1)
538     {
539         msg_Err(s, "#EXT-X-ALLOW-CACHE, ignoring ...");
540         p_sys->b_error = true;
541         return;
542     }
543
544     hls->b_cache = (strncmp(answer, "NO", 2) != 0);
545 }
546
547 static void parse_Version(stream_t *s, hls_stream_t *hls, char *p_read)
548 {
549     stream_sys_t *p_sys = s->p_sys;
550
551     assert(hls);
552
553     int version;
554     int ret = sscanf(p_read, "#EXT-X-VERSION:%d", &version);
555     if (ret != 1)
556     {
557         msg_Err(s, "#EXT-X-VERSION: no protocol version found, should be version 1.");
558         p_sys->b_error = true;
559         return;
560     }
561
562     /* Check version */
563     hls->version = version;
564     if (hls->version != 1)
565     {
566         msg_Err(s, "#EXT-X-VERSION should be version 1 iso %d", version);
567         p_sys->b_error = true;
568     }
569 }
570
571 static void parse_EndList(stream_t *s, hls_stream_t *hls)
572 {
573     stream_sys_t *p_sys = s->p_sys;
574
575     assert(hls);
576
577     p_sys->b_live = false;
578     msg_Info(s, "video on demand (vod) mode");
579 }
580
581 static void parse_Discontinuity(stream_t *s, hls_stream_t *hls, char *p_read)
582 {
583     assert(hls);
584
585     /* FIXME: Do we need to act on discontinuity ?? */
586     msg_Dbg(s, "#EXT-X-DISCONTINUITY %s", p_read);
587 }
588
589 static void parse_M3U8ExtLine(stream_t *s, hls_stream_t *hls, char *line)
590 {
591     if (*line == '#')
592     {
593         if (strncmp(line, "#EXT-X-TARGETDURATION", 21) == 0)
594             parse_TargetDuration(s, hls, line);
595         else if (strncmp(line, "#EXT-X-MEDIA-SEQUENCE", 22) == 0)
596             parse_MediaSequence(s, hls, line);
597         else if (strncmp(line, "#EXT-X-KEY", 11) == 0)
598             parse_Key(s, hls, line);
599         else if (strncmp(line, "#EXT-X-PROGRAM-DATE-TIME", 25) == 0)
600             parse_ProgramDateTime(s, hls, line);
601         else if (strncmp(line, "#EXT-X-ALLOW-CACHE", 17) == 0)
602             parse_AllowCache(s, hls, line);
603         else if (strncmp(line, "#EXT-X-DISCONTINUITY", 20) == 0)
604             parse_Discontinuity(s, hls, line);
605         else if (strncmp(line, "#EXT-X-VERSION", 14) == 0)
606             parse_Version(s, hls, line);
607         else if (strncmp(line, "#EXT-X-ENDLIST", 14) == 0)
608             parse_EndList(s, hls);
609     }
610 }
611
612 #define HTTPLIVE_MAX_LINE 4096
613 static int get_HTTPLivePlaylist(stream_t *s, hls_stream_t *hls)
614 {
615     stream_sys_t *p_sys = s->p_sys;
616
617     /* Download new playlist file from server */
618     if (AccessOpen(s, &hls->url) != VLC_SUCCESS)
619         return VLC_EGENERIC;
620
621     /* Parse the rest of the reply */
622     uint8_t *tmp = calloc(1, HTTPLIVE_MAX_LINE);
623     if (tmp == NULL)
624     {
625         AccessClose(s);
626         return VLC_ENOMEM;
627     }
628
629     char *line = AccessReadLine(p_sys->p_access, tmp, HTTPLIVE_MAX_LINE);
630     if (strncmp(line, "#EXTM3U", 7) != 0)
631     {
632         msg_Err(s, "missing #EXTM3U tag");
633         goto error;
634     }
635     free(line);
636     line = NULL;
637
638     for( ; ; )
639     {
640         line = AccessReadLine(p_sys->p_access, tmp, HTTPLIVE_MAX_LINE);
641         if (line == NULL)
642         {
643             msg_Dbg(s, "end of data");
644             break;
645         }
646
647         if (!vlc_object_alive(s))
648             goto error;
649
650         /* some more checks for actual data */
651         if (strncmp(line, "#EXTINF", 7) == 0)
652         {
653             char *uri = AccessReadLine(p_sys->p_access, tmp, HTTPLIVE_MAX_LINE);
654             if (uri == NULL)
655                 p_sys->b_error = true;
656             else
657             {
658                 parse_SegmentInformation(s, hls, line, uri);
659                 free(uri);
660             }
661         }
662         else
663         {
664             parse_M3U8ExtLine(s, hls, line);
665         }
666
667         /* Error during m3u8 parsing abort */
668         if (p_sys->b_error)
669             goto error;
670
671         free(line);
672     }
673
674     free(line);
675     free(tmp);
676     AccessClose(s);
677     return VLC_SUCCESS;
678
679 error:
680     free(line);
681     free(tmp);
682     AccessClose(s);
683     return VLC_EGENERIC;
684 }
685 #undef HTTPLIVE_MAX_LINE
686
687 /* The http://tools.ietf.org/html/draft-pantos-http-live-streaming-04#page-8
688  * document defines the following new tags: EXT-X-TARGETDURATION,
689  * EXT-X-MEDIA-SEQUENCE, EXT-X-KEY, EXT-X-PROGRAM-DATE-TIME, EXT-X-
690  * ALLOW-CACHE, EXT-X-STREAM-INF, EXT-X-ENDLIST, EXT-X-DISCONTINUITY,
691  * and EXT-X-VERSION.
692  */
693 static int parse_HTTPLiveStreaming(stream_t *s)
694 {
695     stream_sys_t *p_sys = s->p_sys;
696     char *p_read, *p_begin, *p_end;
697
698     assert(p_sys->hls_stream);
699
700     p_begin = p_read = stream_ReadLine(s->p_source);
701     if (!p_begin)
702         return VLC_ENOMEM;
703
704     /* */
705     int i_len = strlen(p_begin);
706     p_end = p_read + i_len;
707
708     if (strncmp(p_read, "#EXTM3U", 7) != 0)
709     {
710         msg_Err(s, "missing #EXTM3U tag .. aborting");
711         free(p_begin);
712         return VLC_EGENERIC;
713     }
714
715     do {
716         free(p_begin);
717
718         if (p_sys->b_error)
719             return VLC_EGENERIC;
720
721         /* Next line */
722         p_begin = stream_ReadLine(s->p_source);
723         if (p_begin == NULL)
724             break;
725
726         i_len = strlen(p_begin);
727         p_read = p_begin;
728         p_end = p_read + i_len;
729
730         if (strncmp(p_read, "#EXT-X-STREAM-INF", 17) == 0)
731         {
732             p_sys->b_meta = true;
733             char *uri = stream_ReadLine(s->p_source);
734             if (uri == NULL)
735                 p_sys->b_error = true;
736             else
737             {
738                 parse_StreamInformation(s, p_read, uri);
739                 free(uri);
740             }
741         }
742         else if (strncmp(p_read, "#EXTINF", 7) == 0)
743         {
744             char *uri = stream_ReadLine(s->p_source);
745             if (uri == NULL)
746                 p_sys->b_error = true;
747             else
748             {
749                 hls_stream_t *hls = hls_GetLast(p_sys->hls_stream);
750                 if (hls)
751                     parse_SegmentInformation(s, hls, p_read, uri);
752                 else
753                     p_sys->b_error = true;
754                 free(uri);
755             }
756         }
757         else
758         {
759             hls_stream_t *hls = hls_GetLast(p_sys->hls_stream);
760             if (hls == NULL)
761             {
762                 if (!p_sys->b_meta)
763                 {
764                     hls = hls_New(p_sys->hls_stream, -1, -1, NULL);
765                     if (hls == NULL)
766                     {
767                         p_sys->b_error = true;
768                         return VLC_ENOMEM;
769                     }
770                 }
771             }
772             /* Parse M3U8 Ext Line */
773             parse_M3U8ExtLine(s, hls, p_read);
774         }
775     } while(p_read < p_end);
776
777     free(p_begin);
778
779     /* */
780     int count = vlc_array_count(p_sys->hls_stream);
781     for (int n = 0; n < count; n++)
782     {
783         hls_stream_t *hls = hls_Get(p_sys->hls_stream, n);
784         if (hls == NULL) break;
785
786         /* Is it a meta playlist? */
787         if (p_sys->b_meta)
788         {
789             msg_Dbg(s, "parsing %s", hls->url.psz_path);
790             if (get_HTTPLivePlaylist(s, hls) != VLC_SUCCESS)
791             {
792                 msg_Err(s, "could not parse playlist file from meta index." );
793                 return VLC_EGENERIC;
794             }
795         }
796
797         vlc_mutex_lock(&hls->lock);
798         if (p_sys->b_live)
799         {
800
801             /* There should at least be 3 segments of hls->duration */
802             int ok = 0;
803             int num = vlc_array_count(hls->segments);
804             for (int i = 0; i < num; i++)
805             {
806                 segment_t *segment = segment_GetSegment(hls, i);
807                 if (segment && segment->duration >= hls->duration)
808                     ok++;
809             }
810             if (ok < 3)
811             {
812                 msg_Err(s, "cannot start live playback at this time, try again later.");
813                 vlc_mutex_unlock(&hls->lock);
814                 return VLC_EGENERIC;
815             }
816
817             /* Determine next time to reload playlist */
818             p_sys->wakeup = p_sys->last + (hls->duration * 2 * (mtime_t)1000000);
819         }
820
821         /* Stream size (approximate) */
822         hls->size = hls_GetStreamSize(hls);
823
824         /* Can we cache files after playback */
825         p_sys->b_cache = hls->b_cache;
826
827         vlc_mutex_unlock(&hls->lock);
828     }
829
830     return VLC_SUCCESS;
831 }
832
833 /****************************************************************************
834  * hls_Thread
835  ****************************************************************************/
836 static int BandwidthAdaptation(stream_t *s, int progid, uint64_t *bandwidth)
837 {
838     stream_sys_t *p_sys = s->p_sys;
839     int candidate = -1;
840     uint64_t bw = *bandwidth;
841     uint64_t bw_candidate = 0;
842
843     int count = vlc_array_count(p_sys->hls_stream);
844     for (int n = 0; n < count; n++)
845     {
846         /* Select best bandwidth match */
847         hls_stream_t *hls = hls_Get(p_sys->hls_stream, n);
848         if (hls == NULL) break;
849
850         /* only consider streams with the same PROGRAM-ID */
851         if (hls->id == progid)
852         {
853             if ((bw >= hls->bandwidth) && (bw_candidate < hls->bandwidth))
854             {
855                 msg_Dbg(s, "candidate %d bandwidth (bits/s) %"PRIu64" >= %"PRIu64,
856                          n, bw, hls->bandwidth); /* bits / s */
857                 bw_candidate = hls->bandwidth;
858                 candidate = n; /* possible candidate */
859             }
860         }
861     }
862     *bandwidth = bw_candidate;
863     return candidate;
864 }
865
866 static int Download(stream_t *s, hls_stream_t *hls, segment_t *segment, int *cur_stream)
867 {
868     assert(hls);
869     assert(segment);
870
871     vlc_mutex_lock(&segment->lock);
872     if (segment->data != NULL)
873     {
874         /* Segment already downloaded */
875         vlc_mutex_unlock(&segment->lock);
876         return VLC_SUCCESS;
877     }
878
879     mtime_t start = mdate();
880     if (AccessDownload(s, segment) != VLC_SUCCESS)
881     {
882         vlc_mutex_unlock(&segment->lock);
883         return VLC_EGENERIC;
884     }
885     mtime_t duration = mdate() - start;
886
887     vlc_mutex_unlock(&segment->lock);
888
889     msg_Info(s, "downloaded segment %d from stream %d",
890                 segment->sequence, *cur_stream);
891
892     /* check for division by zero */
893     double ms = (double)duration / 1000.0; /* ms */
894     if (ms <= 0.0)
895         return VLC_SUCCESS;
896
897     uint64_t bw = ((double)(segment->size * 8) / ms) * 1000; /* bits / s */
898     segment->bandwidth = bw;
899     if (hls->bandwidth != bw)
900     {
901         int newstream = BandwidthAdaptation(s, hls->id, &bw);
902
903         /* FIXME: we need an average here */
904         if ((newstream >= 0) && (newstream != *cur_stream))
905         {
906             msg_Info(s, "detected %s bandwidth (%"PRIu64") stream",
907                      (bw >= hls->bandwidth) ? "faster" : "lower", bw);
908             *cur_stream = newstream;
909         }
910     }
911     return VLC_SUCCESS;
912 }
913
914 static void* hls_Thread(vlc_object_t *p_this)
915 {
916     hls_thread_t *client = (hls_thread_t *) p_this;
917     stream_t *s = client->s;
918     stream_sys_t *p_sys = s->p_sys;
919
920     int canc = vlc_savecancel();
921
922     while (vlc_object_alive(p_this))
923     {
924         hls_stream_t *hls = hls_Get(client->hls_stream, client->current);
925         assert(hls);
926
927         vlc_mutex_lock(&hls->lock);
928         segment_t *segment = segment_GetSegment(hls, client->segment);
929         vlc_mutex_unlock(&hls->lock);
930
931         /* Is there a new segment to process? */
932         if (segment == NULL)
933         {
934             if (!p_sys->b_live) break;
935             mwait(p_sys->wakeup);
936         }
937         else if (Download(client->s, hls, segment, &client->current) != VLC_SUCCESS)
938         {
939             if (!p_sys->b_live) break;
940         }
941
942         /* download succeeded */
943         client->segment++;
944
945         /* FIXME: Reread the m3u8 index file */
946         if (p_sys->b_live)
947         {
948             double wait = 1;
949             mtime_t now = mdate();
950             if (now >= p_sys->wakeup)
951             {
952 #if 0
953                 /** FIXME: Implement m3u8 playlist reloading */
954                 if (!hls_ReloadPlaylist(client->s))
955                 {
956                     /* No change in playlist, then backoff */
957                     p_sys->tries++;
958                     if (p_sys->tries == 1) wait = 0.5;
959                     else if (p_sys->tries == 2) wait = 1;
960                     else if (p_sys->tries >= 3) wait = 3;
961                 }
962 #endif
963                 /* determine next time to update playlist */
964                 p_sys->last = now;
965                 p_sys->wakeup = now + ((mtime_t)(hls->duration * wait) * (mtime_t)1000000);
966             }
967         }
968     }
969
970     vlc_restorecancel(canc);
971     return NULL;
972 }
973
974 static int Prefetch(stream_t *s, int *current)
975 {
976     stream_sys_t *p_sys = s->p_sys;
977     int stream;
978
979     /* Try to pick best matching stream */
980 again:
981     stream = *current;
982
983     hls_stream_t *hls = hls_Get(p_sys->hls_stream, *current);
984     if (hls == NULL)
985         return VLC_EGENERIC;
986
987     segment_t *segment = segment_GetSegment(hls,  p_sys->segment);
988     if (segment == NULL )
989         return VLC_EGENERIC;
990
991     if (Download(s, hls, segment, current) != VLC_SUCCESS)
992         return VLC_EGENERIC;
993
994     /* Found better bandwidth match, try again */
995     if (*current != stream)
996         goto again;
997
998     /* Download first 2 segments of this HLS stream */
999     for (int i = 0; i < 2; i++)
1000     {
1001         segment_t *segment = segment_GetSegment(hls, p_sys->segment);
1002         if (segment == NULL )
1003             return VLC_EGENERIC;
1004
1005         if (segment->data)
1006         {
1007             p_sys->segment++;
1008             continue;
1009         }
1010
1011         if (Download(s, hls, segment, current) != VLC_SUCCESS)
1012             return VLC_EGENERIC;
1013
1014         p_sys->segment++;
1015     }
1016
1017     return VLC_SUCCESS;
1018 }
1019
1020 /****************************************************************************
1021  * Access
1022  ****************************************************************************/
1023 static int AccessOpen(stream_t *s, vlc_url_t *url)
1024 {
1025     stream_sys_t *p_sys = (stream_sys_t *) s->p_sys;
1026
1027     if ((url->psz_protocol == NULL) ||
1028         (url->psz_path == NULL))
1029         return VLC_EGENERIC;
1030
1031     p_sys->p_access = vlc_object_create(s, sizeof(access_t));
1032     if (p_sys->p_access == NULL)
1033         return VLC_ENOMEM;
1034
1035     p_sys->p_access->psz_access = strdup(url->psz_protocol);
1036     p_sys->p_access->psz_filepath = strdup(url->psz_path);
1037     if (url->psz_password || url->psz_username)
1038     {
1039         if (asprintf(&p_sys->p_access->psz_location, "%s:%s@%s%s",
1040                      url->psz_username, url->psz_password,
1041                      url->psz_host, url->psz_path) < 0)
1042         {
1043             msg_Err(s, "creating http access module");
1044             goto fail;
1045         }
1046     }
1047     else
1048     {
1049         if (asprintf(&p_sys->p_access->psz_location, "%s%s",
1050                      url->psz_host, url->psz_path) < 0)
1051         {
1052             msg_Err(s, "creating http access module");
1053             goto fail;
1054         }
1055     }
1056     vlc_object_attach(p_sys->p_access, s);
1057     p_sys->p_access->p_module =
1058         module_need(p_sys->p_access, "access", "http", true);
1059     if (p_sys->p_access->p_module == NULL)
1060     {
1061         msg_Err(s, "could not load http access module");
1062         goto fail;
1063     }
1064
1065     return VLC_SUCCESS;
1066
1067 fail:
1068     vlc_object_release(p_sys->p_access);
1069     p_sys->p_access = NULL;
1070     return VLC_EGENERIC;
1071 }
1072
1073 static void AccessClose(stream_t *s)
1074 {
1075     stream_sys_t *p_sys = (stream_sys_t *) s->p_sys;
1076
1077     if (p_sys->p_access)
1078     {
1079         vlc_object_kill(p_sys->p_access);
1080         free(p_sys->p_access->psz_access);
1081         if (p_sys->p_access->p_module)
1082             module_unneed(p_sys->p_access,
1083                           p_sys->p_access->p_module);
1084
1085         vlc_object_release(p_sys->p_access);
1086         p_sys->p_access = NULL;
1087     }
1088 }
1089
1090 static char *AccessReadLine(access_t *p_access, uint8_t *psz_tmp, size_t i_len)
1091 {
1092     char *line = NULL;
1093     char *begin = (char *)psz_tmp;
1094
1095     assert(psz_tmp);
1096
1097     int skip = strlen(begin);
1098     ssize_t len = p_access->pf_read(p_access, psz_tmp + skip, i_len - skip);
1099     if (len < 0) return NULL;
1100     if ((len == 0) && (skip == 0))
1101         return NULL;
1102
1103     char *p = begin;
1104     char *end = p + len + skip;
1105
1106     while (p < end)
1107     {
1108         if (*p == '\n')
1109             break;
1110
1111         p++;
1112     }
1113
1114     /* copy line excluding \n */
1115     line = strndup(begin, p - begin);
1116
1117     p++;
1118     if (p < end)
1119     {
1120         psz_tmp = memmove(begin, p, end - p);
1121         psz_tmp[end - p] = '\0';
1122     }
1123     else memset(psz_tmp, 0, i_len);
1124
1125     return line;
1126 }
1127
1128 static int AccessDownload(stream_t *s, segment_t *segment)
1129 {
1130     stream_sys_t *p_sys = (stream_sys_t *) s->p_sys;
1131
1132     assert(segment);
1133
1134     /* Download new playlist file from server */
1135     if (AccessOpen(s, &segment->url) != VLC_SUCCESS)
1136         return VLC_EGENERIC;
1137
1138     segment->size = p_sys->p_access->info.i_size;
1139     assert(segment->size > 0);
1140
1141     segment->data = block_Alloc(segment->size);
1142     if (segment->data == NULL)
1143     {
1144         AccessClose(s);
1145         return VLC_ENOMEM;
1146     }
1147
1148     assert(segment->data->i_buffer == segment->size);
1149
1150     ssize_t length = 0, curlen = 0;
1151     do
1152     {
1153         if (p_sys->p_access->info.i_size > segment->size)
1154         {
1155             msg_Dbg(s, "size changed %"PRIu64, segment->size);
1156             segment->data = block_Realloc(segment->data, 0, p_sys->p_access->info.i_size);
1157             if (segment->data == NULL)
1158             {
1159                 AccessClose(s);
1160                 return VLC_ENOMEM;
1161             }
1162             segment->size = p_sys->p_access->info.i_size;
1163             assert(segment->data->i_buffer == segment->size);
1164         }
1165         length = p_sys->p_access->pf_read(p_sys->p_access,
1166                     segment->data->p_buffer + curlen, segment->size - curlen);
1167         if ((length <= 0) || ((uint64_t)length >= segment->size))
1168             break;
1169         curlen += length;
1170     } while (vlc_object_alive(s));
1171
1172     AccessClose(s);
1173     return VLC_SUCCESS;
1174 }
1175
1176 /****************************************************************************
1177  * Open
1178  ****************************************************************************/
1179 static int Open(vlc_object_t *p_this)
1180 {
1181     stream_t *s = (stream_t*)p_this;
1182     stream_sys_t *p_sys;
1183
1184     if (!isHTTPLiveStreaming(s))
1185         return VLC_EGENERIC;
1186
1187     msg_Info(p_this, "HTTP Live Streaming (%s)", s->psz_path);
1188
1189     /* */
1190     s->p_sys = p_sys = calloc(1, sizeof(*p_sys));
1191     if (p_sys == NULL)
1192         return VLC_ENOMEM;
1193
1194     p_sys->b_live = true;
1195     p_sys->b_meta = false;
1196
1197     p_sys->hls_stream = vlc_array_new();
1198     if (p_sys->hls_stream == NULL)
1199     {
1200         free(p_sys);
1201         return VLC_ENOMEM;
1202     }
1203
1204     /* */
1205     s->pf_read = Read;
1206     s->pf_peek = Peek;
1207     s->pf_control = Control;
1208
1209     /* Select first segment to play */
1210     p_sys->last = mdate();
1211     if (parse_HTTPLiveStreaming(s) != VLC_SUCCESS)
1212     {
1213         goto fail;
1214     }
1215
1216     /* Choose first HLS stream to start with */
1217     int current = p_sys->current = 0;
1218     p_sys->segment = 0;
1219
1220     if (Prefetch(s, &current) != VLC_SUCCESS)
1221     {
1222         msg_Err(s, "fetching first segment.");
1223         goto fail;
1224     }
1225
1226     p_sys->thread = vlc_object_create(s, sizeof(hls_thread_t));
1227     if( p_sys->thread == NULL )
1228     {
1229         msg_Err(s, "creating HTTP Live Streaming client thread");
1230         goto fail;
1231     }
1232
1233     p_sys->thread->hls_stream = p_sys->hls_stream;
1234     p_sys->thread->current = current;
1235     p_sys->current = current;
1236     p_sys->thread->segment = p_sys->segment;
1237     p_sys->segment = 0; /* reset to first segment */
1238     p_sys->thread->s = s;
1239
1240     if (vlc_thread_create(p_sys->thread, "HTTP Live Streaming client",
1241                           hls_Thread, VLC_THREAD_PRIORITY_INPUT))
1242     {
1243         goto fail;
1244     }
1245
1246     vlc_object_attach(p_sys->thread, s);
1247
1248     return VLC_SUCCESS;
1249
1250 fail:
1251     Close(p_this);
1252     return VLC_EGENERIC;
1253 }
1254
1255 /****************************************************************************
1256  * Close
1257  ****************************************************************************/
1258 static void Close(vlc_object_t *p_this)
1259 {
1260     stream_t *s = (stream_t*)p_this;
1261     stream_sys_t *p_sys = s->p_sys;
1262
1263     assert(p_sys->hls_stream);
1264
1265     /* */
1266     if (p_sys->thread)
1267     {
1268         vlc_object_kill(p_sys->thread);
1269         vlc_thread_join(p_sys->thread);
1270         vlc_object_release(p_sys->thread);
1271     }
1272
1273     /* Free hls streams */
1274     for (int i = 0; i < vlc_array_count(p_sys->hls_stream); i++)
1275     {
1276         hls_stream_t *hls;
1277         hls = (hls_stream_t *)vlc_array_item_at_index(p_sys->hls_stream, i);
1278         if (hls) hls_Free(hls);
1279     }
1280     vlc_array_destroy(p_sys->hls_stream);
1281
1282     /* */
1283     free(p_sys);
1284 }
1285
1286 /****************************************************************************
1287  * Stream filters functions
1288  ****************************************************************************/
1289 static segment_t *NextSegment(stream_t *s)
1290 {
1291     stream_sys_t *p_sys = s->p_sys;
1292     segment_t *segment = NULL;
1293
1294     /* Is the next segment of the current HLS stream ready? */
1295     hls_stream_t *hls = hls_Get(p_sys->hls_stream, p_sys->current);
1296     if (hls != NULL)
1297     {
1298         vlc_mutex_lock(&hls->lock);
1299         segment = segment_GetSegment(hls, p_sys->segment);
1300         if (segment != NULL)
1301         {
1302             /* This segment is ready? */
1303             if (segment->data != NULL)
1304             {
1305                vlc_mutex_unlock(&hls->lock);
1306                return segment;
1307             }
1308         }
1309         vlc_mutex_unlock(&hls->lock);
1310     }
1311
1312     /* Was the HLS stream changed to another bitrate? */
1313     int i_stream = 0;
1314     segment = NULL;
1315     while(vlc_object_alive(s))
1316     {
1317         /* Is the next segment ready */
1318         hls_stream_t *hls = hls_Get(p_sys->hls_stream, i_stream);
1319         if (hls == NULL)
1320             return NULL;
1321
1322         vlc_mutex_lock(&hls->lock);
1323         segment = segment_GetSegment(hls, p_sys->segment);
1324         if (segment == NULL)
1325         {
1326             vlc_mutex_unlock(&hls->lock);
1327             break;
1328         }
1329
1330         /* This segment is ready? */
1331         if ((segment->data != NULL) &&
1332             (p_sys->segment < p_sys->thread->segment))
1333         {
1334             p_sys->current = i_stream;
1335             vlc_mutex_unlock(&hls->lock);
1336             return segment;
1337         }
1338         vlc_mutex_unlock(&hls->lock);
1339
1340         if (!p_sys->b_meta)
1341             break;
1342
1343         /* Was the stream changed to another bitrate? */
1344         i_stream++;
1345         if (i_stream >= vlc_array_count(p_sys->hls_stream))
1346             break;
1347     }
1348     /* */
1349     return NULL;
1350 }
1351
1352 static ssize_t hls_Read(stream_t *s, uint8_t *p_read, unsigned int i_read)
1353 {
1354     stream_sys_t *p_sys = s->p_sys;
1355     ssize_t copied = 0;
1356
1357     do
1358     {
1359         /* Determine next segment to read. If this is a meta playlist and
1360          * bandwidth conditions changed, then the stream might have switched
1361          * to another bandwidth. */
1362         segment_t *segment = NextSegment(s);
1363         if (segment == NULL)
1364             break;
1365
1366         vlc_mutex_lock(&segment->lock);
1367         if (segment->data->i_buffer == 0)
1368         {
1369             if (!p_sys->b_cache)
1370             {
1371                 block_Release(segment->data);
1372                 segment->data = NULL;
1373             }
1374             p_sys->segment++;
1375             vlc_mutex_unlock(&segment->lock);
1376             continue;
1377         }
1378
1379         if (segment->size == segment->data->i_buffer)
1380             msg_Info(s, "playing segment %d from stream %d",
1381                         p_sys->segment, p_sys->current);
1382
1383         ssize_t len = -1;
1384         if (i_read <= segment->data->i_buffer)
1385             len = i_read;
1386         else if (i_read > segment->data->i_buffer)
1387             len = segment->data->i_buffer;
1388
1389         if (len > 0)
1390         {
1391             memcpy(p_read + copied, segment->data->p_buffer, len);
1392             segment->data->i_buffer -= len;
1393             segment->data->p_buffer += len;
1394             copied += len;
1395             i_read -= len;
1396         }
1397         vlc_mutex_unlock(&segment->lock);
1398
1399     } while ((i_read > 0) && vlc_object_alive(s));
1400
1401     return copied;
1402 }
1403
1404 static int Read(stream_t *s, void *buffer, unsigned int i_read)
1405 {
1406     stream_sys_t *p_sys = s->p_sys;
1407     ssize_t length = 0;
1408
1409     assert(p_sys->hls_stream);
1410
1411     if (buffer == NULL)
1412     {
1413         /* caller skips data, get big enough buffer */
1414         msg_Warn(s, "buffer is NULL (allocate %d)", i_read);
1415         buffer = calloc(1, i_read);
1416         if (buffer == NULL)
1417             return 0; /* NO MEMORY left*/
1418     }
1419
1420     length = hls_Read(s, (uint8_t*) buffer, i_read);
1421     if (length < 0)
1422         return 0;
1423
1424     p_sys->offset += length;
1425     return length;
1426 }
1427
1428 static int Peek(stream_t *s, const uint8_t **pp_peek, unsigned int i_peek)
1429 {
1430     stream_sys_t *p_sys = s->p_sys;
1431     size_t curlen = 0;
1432     segment_t *segment;
1433
1434 again:
1435     segment = NextSegment(s);
1436     if (segment == NULL)
1437     {
1438         msg_Err(s, "segment should have been available");
1439         return 0; /* eof? */
1440     }
1441
1442     vlc_mutex_lock(&segment->lock);
1443
1444     /* remember segment to peek */
1445     int peek_segment = p_sys->segment;
1446     do
1447     {
1448         if (i_peek < segment->data->i_buffer)
1449         {
1450             *pp_peek = segment->data->p_buffer;
1451             curlen += i_peek;
1452         }
1453         else
1454         {
1455             p_sys->segment++;
1456             vlc_mutex_unlock(&segment->lock);
1457             goto again;
1458         }
1459     } while ((curlen < i_peek) && vlc_object_alive(s));
1460
1461     /* restore segment to read */
1462     p_sys->segment = peek_segment;
1463
1464     vlc_mutex_unlock(&segment->lock);
1465
1466     return curlen;
1467 }
1468
1469 static bool hls_MaySeek(stream_t *s)
1470 {
1471     stream_sys_t *p_sys = s->p_sys;
1472
1473     if (p_sys->hls_stream == NULL)
1474         return false;
1475
1476     hls_stream_t *hls = hls_Get(p_sys->hls_stream, p_sys->current);
1477     if (hls == NULL) return false;
1478
1479     if (p_sys->b_live)
1480     {
1481        vlc_mutex_lock(&hls->lock);
1482        int count = vlc_array_count(hls->segments);
1483        bool may_seek = (p_sys->thread == NULL) ? false :
1484                             (p_sys->thread->segment < count - 2);
1485        vlc_mutex_unlock(&hls->lock);
1486        return may_seek;
1487     }
1488     return true;
1489 }
1490
1491 static uint64_t GetStreamSize(stream_t *s)
1492 {
1493     stream_sys_t *p_sys = s->p_sys;
1494
1495     if (p_sys->b_live)
1496         return 0;
1497
1498     hls_stream_t *hls = hls_Get(p_sys->hls_stream, p_sys->current);
1499     if (hls == NULL) return 0;
1500
1501     vlc_mutex_lock(&hls->lock);
1502     uint64_t size = hls->size;
1503     vlc_mutex_unlock(&hls->lock);
1504
1505     return size;
1506 }
1507
1508 static int segment_Seek(stream_t *s, uint64_t pos)
1509 {
1510     stream_sys_t *p_sys = s->p_sys;
1511
1512     /* Find the right offset */
1513     hls_stream_t *hls = hls_Get(p_sys->hls_stream, p_sys->current);
1514     if (hls == NULL)
1515         return VLC_EGENERIC;
1516
1517     uint64_t length = 0;
1518     bool b_found = false;
1519
1520     vlc_mutex_lock(&hls->lock);
1521     int count = vlc_array_count(hls->segments);
1522     vlc_mutex_unlock(&hls->lock);
1523
1524     for (int n = 0; n < count; n++)
1525     {
1526         /* FIXME: Seeking in segments not dowloaded is not supported. */
1527         if (n >= p_sys->thread->segment)
1528         {
1529             msg_Err(s, "seeking in segment not downloaded yet.");
1530             return VLC_EGENERIC;
1531         }
1532
1533         segment_t *segment = vlc_array_item_at_index(hls->segments, n);
1534         if (segment == NULL)
1535             return VLC_EGENERIC;
1536
1537         vlc_mutex_lock(&segment->lock);
1538         if (segment->data)
1539         {
1540             length += segment->size;
1541             uint64_t size = segment->size -segment->data->i_buffer;
1542             if (size > 0)
1543             {
1544                 segment->data->i_buffer += size;
1545                 segment->data->p_buffer -= size;
1546             }
1547
1548             if (!b_found && (pos <= length))
1549             {
1550                 uint64_t used = length - pos;
1551                 segment->data->i_buffer -= used;
1552                 segment->data->p_buffer += used;
1553
1554                 count = p_sys->segment;
1555                 p_sys->segment = n;
1556                 b_found = true;
1557             }
1558         }
1559         else
1560         {
1561             /* FIXME: seeking is weird when seeking in segments
1562                that have not been downloaded yet */
1563             length += segment->duration * hls->bandwidth;
1564
1565             if (!b_found && (pos <= length))
1566             {
1567                 count = p_sys->segment;
1568                 p_sys->segment = n;
1569                 b_found = true;
1570             }
1571         }
1572         vlc_mutex_unlock(&segment->lock);
1573     }
1574     return VLC_SUCCESS;
1575 }
1576
1577 static int Control(stream_t *s, int i_query, va_list args)
1578 {
1579     stream_sys_t *p_sys = s->p_sys;
1580
1581     switch (i_query)
1582     {
1583         case STREAM_CAN_SEEK:
1584         case STREAM_CAN_FASTSEEK:
1585             *(va_arg (args, bool *)) = hls_MaySeek(s);
1586             break;
1587         case STREAM_GET_POSITION:
1588             *(va_arg (args, uint64_t *)) = p_sys->offset;
1589             break;
1590         case STREAM_SET_POSITION:
1591             if (hls_MaySeek(s))
1592             {
1593                 uint64_t pos = (uint64_t)va_arg(args, uint64_t);
1594                 if (segment_Seek(s, pos) == VLC_SUCCESS)
1595                 {
1596                     p_sys->offset = pos;
1597                     break;
1598                 }
1599             }
1600             return VLC_EGENERIC;
1601         case STREAM_GET_SIZE:
1602             *(va_arg (args, uint64_t *)) = GetStreamSize(s);
1603             break;
1604         default:
1605             return VLC_EGENERIC;
1606     }
1607     return VLC_SUCCESS;
1608 }