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