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