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