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