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