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