1 /*****************************************************************************
2 * httplive.c: HTTP Live Streaming stream filter
3 *****************************************************************************
4 * Copyright (C) 2010-2011 M2X BV
7 * Author: Jean-Paul Saman <jpsaman _AT_ videolan _DOT_ org>
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.
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.
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 *****************************************************************************/
24 /*****************************************************************************
26 *****************************************************************************/
34 #include <vlc_common.h>
35 #include <vlc_plugin.h>
39 #include <vlc_threads.h>
40 #include <vlc_arrays.h>
41 #include <vlc_stream.h>
43 #include <vlc_memory.h>
45 /*****************************************************************************
47 *****************************************************************************/
48 static int Open (vlc_object_t *);
49 static void Close(vlc_object_t *);
52 set_category(CAT_INPUT)
53 set_subcategory(SUBCAT_INPUT_STREAM_FILTER)
54 set_description(N_("Http Live Streaming stream filter"))
55 set_capability("stream_filter", 20)
56 set_callbacks(Open, Close)
59 /*****************************************************************************
61 *****************************************************************************/
62 typedef struct segment_s
64 int sequence; /* unique sequence number */
65 int duration; /* segment duration (seconds) */
66 uint64_t size; /* segment size in bytes */
67 uint64_t bandwidth; /* bandwidth usage of segments (bits per second)*/
71 block_t *data; /* data */
74 typedef struct hls_stream_s
76 int id; /* program id */
77 int version; /* protocol version should be 1 */
78 int sequence; /* media sequence number */
79 int duration; /* maximum duration per segment (ms) */
80 uint64_t bandwidth; /* bandwidth usage of segments (bits per second)*/
81 uint64_t size; /* stream length (segment->duration * hls->bandwidth/8) */
83 vlc_array_t *segments; /* list of segments */
84 vlc_url_t url; /* uri to m3u8 */
86 bool b_cache; /* allow caching */
91 vlc_url_t m3u8; /* M3U8 url */
92 vlc_thread_t reload; /* HLS m3u8 reload thread */
93 vlc_thread_t thread; /* HLS segment download thread */
98 vlc_array_t *hls_stream; /* bandwidth adaptation */
99 uint64_t bandwidth; /* measured bandwidth (bits per second) */
102 struct hls_download_s
104 int stream; /* current hls_stream */
105 int segment; /* current segment for downloading */
106 int seek; /* segment requested by seek (default -1) */
107 vlc_mutex_t lock_wait; /* protect segment download counter */
108 vlc_cond_t wait; /* some condition to wait on */
112 struct hls_playback_s
114 uint64_t offset; /* current offset in media */
115 int stream; /* current hls_stream */
116 int segment; /* current segment for playback */
120 struct hls_playlist_s
122 mtime_t last; /* playlist last loaded */
123 mtime_t wakeup; /* next reload time */
124 int tries; /* times it was not changed */
128 bool b_cache; /* can cache files */
129 bool b_meta; /* meta playlist */
130 bool b_live; /* live stream? or vod? */
131 bool b_error; /* parsing error */
134 /****************************************************************************
136 ****************************************************************************/
137 static int Read (stream_t *, void *p_read, unsigned int i_read);
138 static int Peek (stream_t *, const uint8_t **pp_peek, unsigned int i_peek);
139 static int Control(stream_t *, int i_query, va_list);
141 static ssize_t read_M3U8_from_stream(stream_t *s, uint8_t **buffer);
142 static ssize_t read_M3U8_from_url(stream_t *s, vlc_url_t *url, uint8_t **buffer);
143 static char *ReadLine(uint8_t *buffer, uint8_t **pos, size_t len);
145 static int hls_Download(stream_t *s, segment_t *segment);
147 static void* hls_Thread(void *);
148 static void* hls_Reload(void *);
150 static segment_t *segment_GetSegment(hls_stream_t *hls, int wanted);
151 static void segment_Free(segment_t *segment);
153 static char *ConstructUrl(vlc_url_t *url);
155 /****************************************************************************
157 ****************************************************************************/
158 static const char *const ext[] = {
159 "#EXT-X-TARGETDURATION",
160 "#EXT-X-MEDIA-SEQUENCE",
162 "#EXT-X-ALLOW-CACHE",
165 "#EXT-X-DISCONTINUITY",
169 static bool isHTTPLiveStreaming(stream_t *s)
171 const uint8_t *peek, *peek_end;
173 int64_t i_size = stream_Peek(s->p_source, &peek, 46);
177 if (strncasecmp((const char*)peek, "#EXTM3U", 7) != 0)
180 /* Parse stream and search for
181 * EXT-X-TARGETDURATION or EXT-X-STREAM-INF tag, see
182 * http://tools.ietf.org/html/draft-pantos-http-live-streaming-04#page-8 */
183 peek_end = peek + i_size;
184 while(peek <= peek_end)
188 for (unsigned int i = 0; i < ARRAY_SIZE(ext); i++)
190 char *p = strstr((const char*)peek, ext[i]);
201 /* HTTP Live Streaming */
202 static hls_stream_t *hls_New(vlc_array_t *hls_stream, const int id, const uint64_t bw, const char *uri)
204 hls_stream_t *hls = (hls_stream_t *)malloc(sizeof(hls_stream_t));
205 if (hls == NULL) return NULL;
209 hls->duration = -1;/* unknown */
211 hls->sequence = 0; /* default is 0 */
212 hls->version = 1; /* default protocol version */
214 vlc_UrlParse(&hls->url, uri, 0);
215 hls->segments = vlc_array_new();
216 vlc_array_append(hls_stream, hls);
217 vlc_mutex_init(&hls->lock);
221 static void hls_Free(hls_stream_t *hls)
223 vlc_mutex_destroy(&hls->lock);
227 for (int n = 0; n < vlc_array_count(hls->segments); n++)
229 segment_t *segment = (segment_t *)vlc_array_item_at_index(hls->segments, n);
230 if (segment) segment_Free(segment);
232 vlc_array_destroy(hls->segments);
235 vlc_UrlClean(&hls->url);
240 static hls_stream_t *hls_Copy(hls_stream_t *src, const bool b_cp_segments)
243 assert(!b_cp_segments); /* FIXME: copying segments is not implemented */
245 hls_stream_t *dst = (hls_stream_t *)malloc(sizeof(hls_stream_t));
246 if (dst == NULL) return NULL;
249 dst->bandwidth = src->bandwidth;
250 dst->duration = src->duration;
251 dst->size = src->size;
252 dst->sequence = src->sequence;
253 dst->version = src->version;
254 dst->b_cache = src->b_cache;
255 char *uri = ConstructUrl(&src->url);
261 vlc_UrlParse(&dst->url, uri, 0);
263 dst->segments = vlc_array_new();
264 vlc_mutex_init(&dst->lock);
268 static hls_stream_t *hls_Get(vlc_array_t *hls_stream, const int wanted)
270 int count = vlc_array_count(hls_stream);
273 if ((wanted < 0) || (wanted >= count))
275 return (hls_stream_t *) vlc_array_item_at_index(hls_stream, wanted);
278 static inline hls_stream_t *hls_GetFirst(vlc_array_t *hls_stream)
280 return (hls_stream_t*) hls_Get(hls_stream, 0);
283 static hls_stream_t *hls_GetLast(vlc_array_t *hls_stream)
285 int count = vlc_array_count(hls_stream);
289 return (hls_stream_t *) hls_Get(hls_stream, count);
292 static hls_stream_t *hls_Find(vlc_array_t *hls_stream, hls_stream_t *hls_new)
294 int count = vlc_array_count(hls_stream);
295 for (int n = 0; n < count; n++)
297 hls_stream_t *hls = vlc_array_item_at_index(hls_stream, n);
301 if ((hls->id == hls_new->id) &&
302 (hls->bandwidth == hls_new->bandwidth))
309 static uint64_t hls_GetStreamSize(hls_stream_t *hls)
311 /* NOTE: Stream size is calculated based on segment duration and
312 * HLS stream bandwidth from the .m3u8 file. If these are not correct
313 * then the deviation from exact byte size will be big and the seek/
314 * progressbar will not behave entirely as one expects. */
316 int count = vlc_array_count(hls->segments);
317 for (int n = 0; n < count; n++)
319 segment_t *segment = segment_GetSegment(hls, n);
322 size += (segment->duration * (hls->bandwidth / 8));
329 static segment_t *segment_New(hls_stream_t* hls, const int duration, const char *uri)
331 segment_t *segment = (segment_t *)malloc(sizeof(segment_t));
335 segment->duration = duration; /* seconds */
336 segment->size = 0; /* bytes */
337 segment->sequence = 0;
338 segment->bandwidth = 0;
339 vlc_UrlParse(&segment->url, uri, 0);
340 segment->data = NULL;
341 vlc_array_append(hls->segments, segment);
342 vlc_mutex_init(&segment->lock);
346 static void segment_Free(segment_t *segment)
348 vlc_mutex_destroy(&segment->lock);
350 vlc_UrlClean(&segment->url);
352 block_Release(segment->data);
357 static segment_t *segment_GetSegment(hls_stream_t *hls, const int wanted)
361 int count = vlc_array_count(hls->segments);
364 if ((wanted < 0) || (wanted >= count))
366 return (segment_t *) vlc_array_item_at_index(hls->segments, wanted);
369 static segment_t *segment_Find(hls_stream_t *hls, const int sequence)
373 int count = vlc_array_count(hls->segments);
374 if (count <= 0) return NULL;
375 for (int n = 0; n < count; n++)
377 segment_t *segment = vlc_array_item_at_index(hls->segments, n);
378 if (segment == NULL) break;
379 if (segment->sequence == sequence)
385 static int ChooseSegment(stream_t *s, const int current)
387 stream_sys_t *p_sys = (stream_sys_t *)s->p_sys;
388 hls_stream_t *hls = hls_Get(p_sys->hls_stream, current);
389 if (hls == NULL) return 0;
391 /* Choose a segment to start which is no closer than
392 * 3 times the target duration from the end of the playlist.
397 int count = vlc_array_count(hls->segments);
398 int i = p_sys->b_live ? count - 1 : 0;
400 while((i >= 0) && (i < count) && vlc_object_alive(s))
402 segment_t *segment = segment_GetSegment(hls, i);
405 if (segment->duration > hls->duration)
407 msg_Err(s, "EXTINF:%d duration is larger than EXT-X-TARGETDURATION:%d",
408 segment->duration, hls->duration);
411 duration += segment->duration;
412 if (duration >= 3 * hls->duration)
414 /* Start point found */
415 wanted = p_sys->b_live ? i : 0;
416 sequence = segment->sequence;
426 msg_Info(s, "Choose segment %d/%d (sequence=%d)", wanted, count, sequence);
431 static char *parse_Attributes(const char *line, const char *attr)
434 char *begin = (char *) line;
435 char *end = begin + strlen(line);
437 /* Find start of attributes */
438 if ((p = strchr(begin, ':' )) == NULL)
444 if (strncasecmp(begin, attr, strlen(attr)) == 0)
446 /* <attr>=<value>[,]* */
447 p = strchr(begin, ',');
448 begin += strlen(attr) + 1;
451 if (p == NULL) /* last attribute */
452 return strndup(begin, end - begin);
454 return strndup(begin, p - begin);
457 } while(begin < end);
462 static char *relative_URI(stream_t *s, const char *uri, const char *path)
464 stream_sys_t *p_sys = s->p_sys;
466 char *p = strchr(uri, ':');
470 if (p_sys->m3u8.psz_path == NULL)
473 char *psz_path = strdup(p_sys->m3u8.psz_path);
474 if (psz_path == NULL) return NULL;
475 p = strrchr(psz_path, '/');
478 char *psz_uri = NULL;
479 if (p_sys->m3u8.psz_password || p_sys->m3u8.psz_username)
481 if (asprintf(&psz_uri, "%s://%s:%s@%s:%d%s/%s", p_sys->m3u8.psz_protocol,
482 p_sys->m3u8.psz_username, p_sys->m3u8.psz_password,
483 p_sys->m3u8.psz_host, p_sys->m3u8.i_port,
484 path ? path : psz_path, uri) < 0)
489 if (asprintf(&psz_uri, "%s://%s:%d%s/%s", p_sys->m3u8.psz_protocol,
490 p_sys->m3u8.psz_host, p_sys->m3u8.i_port,
491 path ? path : psz_path, uri) < 0)
502 static char *ConstructUrl(vlc_url_t *url)
504 if ((url->psz_protocol == NULL) ||
505 (url->psz_path == NULL))
508 if (url->i_port <= 0)
510 if (strncmp(url->psz_protocol, "https", 5) == 0)
516 char *psz_url = NULL;
517 if (url->psz_password || url->psz_username)
519 if (asprintf(&psz_url, "%s://%s:%s@%s:%d%s",
521 url->psz_username, url->psz_password,
522 url->psz_host, url->i_port, url->psz_path) < 0)
527 if (asprintf(&psz_url, "%s://%s:%d%s",
529 url->psz_host, url->i_port, url->psz_path) < 0)
536 static int parse_SegmentInformation(hls_stream_t *hls, char *p_read, int *duration)
541 /* strip of #EXTINF: */
543 char *token = strtok_r(p_read, ":", &p_next);
548 token = strtok_r(NULL, ",", &p_next);
553 if (hls->version < 3)
555 value = strtol(token, NULL, 10);
565 double d = strtod(token, (char **) NULL);
571 if ((d) - ((int)d) >= 0.5)
572 value = ((int)d) + 1;
577 /* Ignore the rest of the line */
582 static int parse_AddSegment(stream_t *s, hls_stream_t *hls, const int duration, const char *uri)
587 /* Store segment information */
588 char *psz_path = NULL;
589 if (hls->url.psz_path != NULL)
591 psz_path = strdup(hls->url.psz_path);
592 if (psz_path == NULL)
594 char *p = strrchr(psz_path, '/');
597 char *psz_uri = relative_URI(s, uri, psz_path);
600 vlc_mutex_lock(&hls->lock);
601 segment_t *segment = segment_New(hls, duration, psz_uri ? psz_uri : uri);
603 segment->sequence = hls->sequence + vlc_array_count(hls->segments) - 1;
604 vlc_mutex_unlock(&hls->lock);
607 return segment ? VLC_SUCCESS : VLC_ENOMEM;
610 static int parse_TargetDuration(stream_t *s, hls_stream_t *hls, char *p_read)
615 int ret = sscanf(p_read, "#EXT-X-TARGETDURATION:%d", &duration);
618 msg_Err(s, "expected #EXT-X-TARGETDURATION:<s>");
622 hls->duration = duration; /* seconds */
626 static int parse_StreamInformation(stream_t *s, vlc_array_t **hls_stream,
627 hls_stream_t **hls, char *p_read, const char *uri)
633 assert(*hls == NULL);
635 attr = parse_Attributes(p_read, "PROGRAM-ID");
638 msg_Err(s, "#EXT-X-STREAM-INF: expected PROGRAM-ID=<value>");
644 attr = parse_Attributes(p_read, "BANDWIDTH");
647 msg_Err(s, "#EXT-X-STREAM-INF: expected BANDWIDTH=<value>");
655 msg_Err(s, "#EXT-X-STREAM-INF: bandwidth cannot be 0");
659 msg_Info(s, "bandwidth adaptation detected (program-id=%d, bandwidth=%"PRIu64").", id, bw);
661 char *psz_uri = relative_URI(s, uri, NULL);
663 *hls = hls_New(*hls_stream, id, bw, psz_uri ? psz_uri : uri);
667 return (*hls == NULL) ? VLC_ENOMEM : VLC_SUCCESS;
670 static int parse_MediaSequence(stream_t *s, hls_stream_t *hls, char *p_read)
675 int ret = sscanf(p_read, "#EXT-X-MEDIA-SEQUENCE:%d", &sequence);
678 msg_Err(s, "expected #EXT-X-MEDIA-SEQUENCE:<s>");
682 if (hls->sequence > 0)
683 msg_Err(s, "EXT-X-MEDIA-SEQUENCE already present in playlist (new=%d, old=%d)",
684 sequence, hls->sequence);
686 hls->sequence = sequence;
690 static int parse_Key(stream_t *s, hls_stream_t *hls, char *p_read)
694 /* #EXT-X-KEY:METHOD=<method>[,URI="<URI>"][,IV=<IV>] */
695 int err = VLC_SUCCESS;
696 char *attr = parse_Attributes(p_read, "METHOD");
699 msg_Err(s, "#EXT-X-KEY: expected METHOD=<value>");
703 if (strncasecmp(attr, "NONE", 4) == 0)
706 char *uri = parse_Attributes(p_read, "URI");
709 msg_Err(s, "#EXT-X-KEY: URI not expected");
713 /* IV is only supported in version 2 and above */
714 if (hls->version >= 2)
716 char *iv = parse_Attributes(p_read, "IV");
719 msg_Err(s, "#EXT-X-KEY: IV not expected");
727 msg_Warn(s, "playback of encrypted HTTP Live media is not supported.");
734 static int parse_ProgramDateTime(stream_t *s, hls_stream_t *hls, char *p_read)
737 msg_Dbg(s, "tag not supported: #EXT-X-PROGRAM-DATE-TIME %s", p_read);
741 static int parse_AllowCache(stream_t *s, hls_stream_t *hls, char *p_read)
745 char answer[4] = "\0";
746 int ret = sscanf(p_read, "#EXT-X-ALLOW-CACHE:%3s", answer);
749 msg_Err(s, "#EXT-X-ALLOW-CACHE, ignoring ...");
753 hls->b_cache = (strncmp(answer, "NO", 2) != 0);
757 static int parse_Version(stream_t *s, hls_stream_t *hls, char *p_read)
762 int ret = sscanf(p_read, "#EXT-X-VERSION:%d", &version);
765 msg_Err(s, "#EXT-X-VERSION: no protocol version found, should be version 1.");
770 hls->version = version;
771 if (hls->version != 1)
773 msg_Err(s, "#EXT-X-VERSION should be version 1 iso %d", version);
779 static int parse_EndList(stream_t *s, hls_stream_t *hls)
783 s->p_sys->b_live = false;
784 msg_Info(s, "video on demand (vod) mode");
788 static int parse_Discontinuity(stream_t *s, hls_stream_t *hls, char *p_read)
792 /* FIXME: Do we need to act on discontinuity ?? */
793 msg_Dbg(s, "#EXT-X-DISCONTINUITY %s", p_read);
797 /* The http://tools.ietf.org/html/draft-pantos-http-live-streaming-04#page-8
798 * document defines the following new tags: EXT-X-TARGETDURATION,
799 * EXT-X-MEDIA-SEQUENCE, EXT-X-KEY, EXT-X-PROGRAM-DATE-TIME, EXT-X-
800 * ALLOW-CACHE, EXT-X-STREAM-INF, EXT-X-ENDLIST, EXT-X-DISCONTINUITY,
803 static int parse_M3U8(stream_t *s, vlc_array_t *streams, uint8_t *buffer, const ssize_t len)
805 stream_sys_t *p_sys = s->p_sys;
806 uint8_t *p_read, *p_begin, *p_end;
811 msg_Dbg(s, "parse_M3U8\n%s", buffer);
813 p_end = p_begin + len;
815 char *line = ReadLine(p_begin, &p_read, p_end - p_begin);
820 if (strncmp(line, "#EXTM3U", 7) != 0)
822 msg_Err(s, "missing #EXTM3U tag .. aborting");
830 /* What is the version ? */
832 uint8_t *p = (uint8_t *)strstr((const char *)buffer, "#EXT-X-VERSION:");
836 char *psz_version = ReadLine(p, &tmp, p_end - p);
837 if (psz_version == NULL)
839 int ret = sscanf((const char*)psz_version, "#EXT-X-VERSION:%d", &version);
842 msg_Warn(s, "#EXT-X-VERSION: no protocol version found, assuming version 1.");
849 /* Is it a live stream ? */
850 p_sys->b_live = (strstr((const char *)buffer, "#EXT-X-ENDLIST") == NULL) ? true : false;
852 /* Is it a meta index file ? */
853 bool b_meta = (strstr((const char *)buffer, "#EXT-X-STREAM-INF") == NULL) ? false : true;
855 int err = VLC_SUCCESS;
859 msg_Info(s, "Meta playlist");
861 /* M3U8 Meta Index file */
864 line = ReadLine(p_begin, &p_read, p_end - p_begin);
870 if (strncmp(line, "#EXT-X-STREAM-INF", 17) == 0)
872 p_sys->b_meta = true;
873 char *uri = ReadLine(p_begin, &p_read, p_end - p_begin);
878 hls_stream_t *hls = NULL;
879 err = parse_StreamInformation(s, &streams, &hls, line, uri);
882 /* Download playlist file from server */
884 ssize_t len = read_M3U8_from_url(s, &hls->url, &buf);
889 /* Parse HLS m3u8 content. */
890 err = parse_M3U8(s, streams, buf, len);
896 hls->version = version;
898 hls->size = hls_GetStreamSize(hls); /* Stream size (approximate) */
907 if (p_begin >= p_end)
910 } while ((err == VLC_SUCCESS) && vlc_object_alive(s));
915 msg_Info(s, "%s Playlist HLS protocol version: %d", p_sys->b_live ? "Live": "VOD", version);
917 hls_stream_t *hls = NULL;
919 hls = hls_GetLast(streams);
922 /* No Meta playlist used */
923 hls = hls_New(streams, 0, -1, NULL);
926 /* Get TARGET-DURATION first */
927 p = (uint8_t *)strstr((const char *)buffer, "#EXT-X-TARGETDURATION:");
930 uint8_t *p_rest = NULL;
931 char *psz_duration = ReadLine(p, &p_rest, p_end - p);
932 if (psz_duration == NULL)
934 err = parse_TargetDuration(s, hls, psz_duration);
940 hls->version = version;
942 else return VLC_ENOMEM;
947 int segment_duration = -1;
951 line = ReadLine(p_begin, &p_read, p_end - p_begin);
956 if (strncmp(line, "#EXTINF", 7) == 0)
957 err = parse_SegmentInformation(hls, line, &segment_duration);
958 else if (strncmp(line, "#EXT-X-TARGETDURATION", 21) == 0)
959 err = parse_TargetDuration(s, hls, line);
960 else if (strncmp(line, "#EXT-X-MEDIA-SEQUENCE", 21) == 0)
961 err = parse_MediaSequence(s, hls, line);
962 else if (strncmp(line, "#EXT-X-KEY", 10) == 0)
963 err = parse_Key(s, hls, line);
964 else if (strncmp(line, "#EXT-X-PROGRAM-DATE-TIME", 24) == 0)
965 err = parse_ProgramDateTime(s, hls, line);
966 else if (strncmp(line, "#EXT-X-ALLOW-CACHE", 18) == 0)
967 err = parse_AllowCache(s, hls, line);
968 else if (strncmp(line, "#EXT-X-DISCONTINUITY", 20) == 0)
969 err = parse_Discontinuity(s, hls, line);
970 else if (strncmp(line, "#EXT-X-VERSION", 14) == 0)
971 err = parse_Version(s, hls, line);
972 else if (strncmp(line, "#EXT-X-ENDLIST", 14) == 0)
973 err = parse_EndList(s, hls);
974 else if ((strncmp(line, "#", 1) != 0) && (*line != '\0') )
976 err = parse_AddSegment(s, hls, segment_duration, line);
977 segment_duration = -1; /* reset duration */
983 if (p_begin >= p_end)
986 } while ((err == VLC_SUCCESS) && vlc_object_alive(s));
994 static int get_HTTPLiveMetaPlaylist(stream_t *s, vlc_array_t **streams)
996 stream_sys_t *p_sys = s->p_sys;
998 int err = VLC_EGENERIC;
1000 /* Duplicate HLS stream META information */
1001 for (int i = 0; i < vlc_array_count(p_sys->hls_stream); i++)
1003 hls_stream_t *src, *dst;
1004 src = (hls_stream_t *)vlc_array_item_at_index(p_sys->hls_stream, i);
1006 return VLC_EGENERIC;
1008 dst = hls_Copy(src, false);
1012 vlc_array_append(*streams, dst);
1015 /* Download new playlist file from server */
1016 for (int i = 0; i < vlc_array_count(*streams); i++)
1019 hls = (hls_stream_t *)vlc_array_item_at_index(*streams, i);
1021 return VLC_EGENERIC;
1023 /* Download playlist file from server */
1024 uint8_t *buf = NULL;
1025 ssize_t len = read_M3U8_from_url(s, &hls->url, &buf);
1030 /* Parse HLS m3u8 content. */
1031 err = parse_M3U8(s, *streams, buf, len);
1038 /* Reload playlist */
1039 static int hls_UpdatePlaylist(stream_t *s, hls_stream_t *hls_new, hls_stream_t **hls)
1041 int count = vlc_array_count(hls_new->segments);
1043 msg_Info(s, "updating hls stream (program-id=%d, bandwidth=%"PRIu64") has %d segments",
1044 hls_new->id, hls_new->bandwidth, count);
1046 for (int n = 0; n < count; n++)
1048 segment_t *p = segment_GetSegment(hls_new, n);
1049 if (p == NULL) return VLC_EGENERIC;
1051 vlc_mutex_lock(&(*hls)->lock);
1052 segment_t *segment = segment_Find(*hls, p->sequence);
1055 assert(p->url.psz_path);
1056 assert(segment->url.psz_path);
1058 /* they should be the same */
1059 if ((p->sequence != segment->sequence) ||
1060 (p->duration != segment->duration) ||
1061 (strcmp(p->url.psz_path, segment->url.psz_path) != 0))
1063 msg_Warn(s, "existing segment found with different content - resetting");
1064 msg_Warn(s, "- sequence: new=%d, old=%d", p->sequence, segment->sequence);
1065 msg_Warn(s, "- duration: new=%d, old=%d", p->duration, segment->duration);
1066 msg_Warn(s, "- file: new=%s", p->url.psz_path);
1067 msg_Warn(s, " old=%s", segment->url.psz_path);
1069 /* Resetting content */
1070 char *psz_url = ConstructUrl(&p->url);
1071 if (psz_url == NULL)
1073 msg_Err(s, "Failed updating segment %d - skipping it", p->sequence);
1077 segment->sequence = p->sequence;
1078 segment->duration = p->duration;
1079 vlc_UrlClean(&segment->url);
1080 vlc_UrlParse(&segment->url, psz_url, 0);
1087 int last = vlc_array_count((*hls)->segments) - 1;
1088 segment_t *l = segment_GetSegment(*hls, last);
1089 if (l == NULL) goto fail_and_unlock;
1091 if ((l->sequence + 1) != p->sequence)
1093 msg_Err(s, "gap in sequence numbers found: new=%d expected %d",
1094 p->sequence, l->sequence+1);
1096 vlc_array_append((*hls)->segments, p);
1097 msg_Info(s, "- segment %d appended", p->sequence);
1099 vlc_mutex_unlock(&(*hls)->lock);
1105 vlc_mutex_unlock(&(*hls)->lock);
1106 return VLC_EGENERIC;
1109 static int hls_ReloadPlaylist(stream_t *s)
1111 stream_sys_t *p_sys = s->p_sys;
1113 vlc_array_t *hls_streams = vlc_array_new();
1114 if (hls_streams == NULL)
1117 msg_Info(s, "Reloading HLS live meta playlist");
1119 if (get_HTTPLiveMetaPlaylist(s, &hls_streams) != VLC_SUCCESS)
1121 /* Free hls streams */
1122 for (int i = 0; i < vlc_array_count(hls_streams); i++)
1125 hls = (hls_stream_t *)vlc_array_item_at_index(hls_streams, i);
1126 if (hls) hls_Free(hls);
1128 vlc_array_destroy(hls_streams);
1130 msg_Err(s, "reloading playlist failed");
1131 return VLC_EGENERIC;
1134 /* merge playlists */
1135 int count = vlc_array_count(hls_streams);
1136 for (int n = 0; n < count; n++)
1138 hls_stream_t *hls_new = hls_Get(hls_streams, n);
1139 if (hls_new == NULL)
1142 hls_stream_t *hls_old = hls_Find(p_sys->hls_stream, hls_new);
1143 if (hls_old == NULL)
1144 { /* new hls stream - append */
1145 vlc_array_append(p_sys->hls_stream, hls_new);
1146 msg_Info(s, "new HLS stream appended (id=%d, bandwidth=%"PRIu64")",
1147 hls_new->id, hls_new->bandwidth);
1149 else if (hls_UpdatePlaylist(s, hls_new, &hls_old) != VLC_SUCCESS)
1150 msg_Info(s, "failed updating HLS stream (id=%d, bandwidth=%"PRIu64")",
1151 hls_new->id, hls_new->bandwidth);
1153 vlc_array_destroy(hls_streams);
1157 /****************************************************************************
1159 ****************************************************************************/
1160 static int BandwidthAdaptation(stream_t *s, int progid, uint64_t *bandwidth)
1162 stream_sys_t *p_sys = s->p_sys;
1164 uint64_t bw = *bandwidth;
1165 uint64_t bw_candidate = 0;
1167 int count = vlc_array_count(p_sys->hls_stream);
1168 for (int n = 0; n < count; n++)
1170 /* Select best bandwidth match */
1171 hls_stream_t *hls = hls_Get(p_sys->hls_stream, n);
1172 if (hls == NULL) break;
1174 /* only consider streams with the same PROGRAM-ID */
1175 if (hls->id == progid)
1177 if ((bw >= hls->bandwidth) && (bw_candidate < hls->bandwidth))
1179 msg_Dbg(s, "candidate %d bandwidth (bits/s) %"PRIu64" >= %"PRIu64,
1180 n, bw, hls->bandwidth); /* bits / s */
1181 bw_candidate = hls->bandwidth;
1182 candidate = n; /* possible candidate */
1186 *bandwidth = bw_candidate;
1190 static int Download(stream_t *s, hls_stream_t *hls, segment_t *segment, int *cur_stream)
1192 stream_sys_t *p_sys = s->p_sys;
1197 vlc_mutex_lock(&segment->lock);
1198 if (segment->data != NULL)
1200 /* Segment already downloaded */
1201 vlc_mutex_unlock(&segment->lock);
1205 /* sanity check - can we download this segment on time? */
1206 if ((p_sys->bandwidth > 0) && (hls->bandwidth > 0))
1208 uint64_t size = (segment->duration * hls->bandwidth); /* bits */
1209 int estimated = (int)(size / p_sys->bandwidth);
1210 if (estimated > segment->duration)
1212 msg_Warn(s,"downloading of segment %d takes %ds, which is longer than its playback (%ds)",
1213 segment->sequence, estimated, segment->duration);
1217 mtime_t start = mdate();
1218 if (hls_Download(s, segment) != VLC_SUCCESS)
1220 msg_Err(s, "downloaded segment %d from stream %d failed",
1221 segment->sequence, *cur_stream);
1222 vlc_mutex_unlock(&segment->lock);
1223 return VLC_EGENERIC;
1225 mtime_t duration = mdate() - start;
1227 vlc_mutex_unlock(&segment->lock);
1229 msg_Info(s, "downloaded segment %d from stream %d",
1230 segment->sequence, *cur_stream);
1232 /* check for division by zero */
1233 double ms = (double)duration / 1000.0; /* ms */
1237 uint64_t bw = ((double)(segment->size * 8) / ms) * 1000; /* bits / s */
1238 p_sys->bandwidth = bw;
1239 if (p_sys->b_meta && (hls->bandwidth != bw))
1241 int newstream = BandwidthAdaptation(s, hls->id, &bw);
1243 /* FIXME: we need an average here */
1244 if ((newstream >= 0) && (newstream != *cur_stream))
1246 msg_Info(s, "detected %s bandwidth (%"PRIu64") stream",
1247 (bw >= hls->bandwidth) ? "faster" : "lower", bw);
1248 *cur_stream = newstream;
1254 static void* hls_Thread(void *p_this)
1256 stream_t *s = (stream_t *)p_this;
1257 stream_sys_t *p_sys = s->p_sys;
1259 int canc = vlc_savecancel();
1261 while (vlc_object_alive(s))
1263 hls_stream_t *hls = hls_Get(p_sys->hls_stream, p_sys->download.stream);
1266 /* Sliding window (~60 seconds worth of movie) */
1267 vlc_mutex_lock(&hls->lock);
1268 int count = vlc_array_count(hls->segments);
1269 vlc_mutex_unlock(&hls->lock);
1271 /* Is there a new segment to process? */
1272 if ((!p_sys->b_live && (p_sys->playback.segment < (count - 6))) ||
1273 (p_sys->download.segment >= count))
1276 vlc_mutex_lock(&p_sys->download.lock_wait);
1277 while (((p_sys->download.segment - p_sys->playback.segment > 6) ||
1278 (p_sys->download.segment >= count)) &&
1279 (p_sys->download.seek == -1))
1281 vlc_cond_wait(&p_sys->download.wait, &p_sys->download.lock_wait);
1282 if (p_sys->b_live /*&& (mdate() >= p_sys->playlist.wakeup)*/)
1284 if (!vlc_object_alive(s))
1288 if (p_sys->download.seek >= 0)
1290 p_sys->download.segment = p_sys->download.seek;
1291 p_sys->download.seek = -1;
1293 vlc_mutex_unlock(&p_sys->download.lock_wait);
1296 if (!vlc_object_alive(s)) break;
1298 vlc_mutex_lock(&hls->lock);
1299 segment_t *segment = segment_GetSegment(hls, p_sys->download.segment);
1300 vlc_mutex_unlock(&hls->lock);
1302 if ((segment != NULL) &&
1303 (Download(s, hls, segment, &p_sys->download.stream) != VLC_SUCCESS))
1305 if (!vlc_object_alive(s)) break;
1309 p_sys->b_error = true;
1314 /* download succeeded */
1315 /* determine next segment to download */
1316 vlc_mutex_lock(&p_sys->download.lock_wait);
1317 if (p_sys->download.seek >= 0)
1319 p_sys->download.segment = p_sys->download.seek;
1320 p_sys->download.seek = -1;
1322 else if (p_sys->download.segment < count)
1323 p_sys->download.segment++;
1324 vlc_cond_signal(&p_sys->download.wait);
1325 vlc_mutex_unlock(&p_sys->download.lock_wait);
1328 vlc_restorecancel(canc);
1332 static void* hls_Reload(void *p_this)
1334 stream_t *s = (stream_t *)p_this;
1335 stream_sys_t *p_sys = s->p_sys;
1337 assert(p_sys->b_live);
1339 int canc = vlc_savecancel();
1342 while (vlc_object_alive(s))
1344 mtime_t now = mdate();
1345 if (now >= p_sys->playlist.wakeup)
1347 /* reload the m3u8 */
1348 if (hls_ReloadPlaylist(s) != VLC_SUCCESS)
1350 /* No change in playlist, then backoff */
1351 p_sys->playlist.tries++;
1352 if (p_sys->playlist.tries == 1) wait = 0.5;
1353 else if (p_sys->playlist.tries == 2) wait = 1;
1354 else if (p_sys->playlist.tries >= 3) wait = 2;
1356 /* Can we afford to backoff? */
1357 if (p_sys->download.segment - p_sys->playback.segment < 3)
1359 p_sys->playlist.tries = 0;
1365 p_sys->playlist.tries = 0;
1369 hls_stream_t *hls = hls_Get(p_sys->hls_stream, p_sys->download.stream);
1372 /* determine next time to update playlist */
1373 p_sys->playlist.last = now;
1374 p_sys->playlist.wakeup = now + ((mtime_t)(hls->duration * wait)
1375 * (mtime_t)1000000);
1378 mwait(p_sys->playlist.wakeup);
1381 vlc_restorecancel(canc);
1385 static int Prefetch(stream_t *s, int *current)
1387 stream_sys_t *p_sys = s->p_sys;
1390 /* Try to pick best matching stream */;
1394 hls_stream_t *hls = hls_Get(p_sys->hls_stream, *current);
1396 return VLC_EGENERIC;
1398 segment_t *segment = segment_GetSegment(hls, p_sys->download.segment);
1399 if (segment == NULL )
1400 return VLC_EGENERIC;
1402 if (Download(s, hls, segment, current) != VLC_SUCCESS)
1403 return VLC_EGENERIC;
1405 /* Found better bandwidth match, try again */
1406 if (*current != stream)
1409 /* Download first 2 segments of this HLS stream */
1411 for (int i = 0; i < 2; i++)
1413 segment_t *segment = segment_GetSegment(hls, p_sys->download.segment);
1414 if (segment == NULL )
1415 return VLC_EGENERIC;
1419 p_sys->download.segment++;
1423 if (Download(s, hls, segment, current) != VLC_SUCCESS)
1424 return VLC_EGENERIC;
1426 p_sys->download.segment++;
1428 /* adapt bandwidth? */
1429 if (*current != stream)
1431 hls_stream_t *hls = hls_Get(p_sys->hls_stream, *current);
1433 return VLC_EGENERIC;
1442 /****************************************************************************
1444 ****************************************************************************/
1445 static int hls_Download(stream_t *s, segment_t *segment)
1450 char *psz_url = ConstructUrl(&segment->url);
1451 if (psz_url == NULL)
1454 stream_t *p_ts = stream_UrlNew(s, psz_url);
1457 return VLC_EGENERIC;
1459 segment->size = stream_Size(p_ts);
1460 assert(segment->size > 0);
1462 segment->data = block_Alloc(segment->size);
1463 if (segment->data == NULL)
1465 stream_Delete(p_ts);
1469 assert(segment->data->i_buffer == segment->size);
1471 ssize_t length = 0, curlen = 0;
1475 size = stream_Size(p_ts);
1476 if (size > segment->size)
1478 msg_Dbg(s, "size changed %"PRIu64, segment->size);
1479 block_t *p_block = block_Realloc(segment->data, 0, size);
1480 if (p_block == NULL)
1482 stream_Delete(p_ts);
1483 block_Release(segment->data);
1484 segment->data = NULL;
1487 segment->data = p_block;
1488 segment->size = size;
1489 assert(segment->data->i_buffer == segment->size);
1492 length = stream_Read(p_ts, segment->data->p_buffer + curlen, segment->size - curlen);
1496 } while (vlc_object_alive(s));
1498 stream_Delete(p_ts);
1502 /* Read M3U8 file */
1503 static ssize_t read_M3U8_from_stream(stream_t *s, uint8_t **buffer)
1505 int64_t total_bytes = 0;
1506 int64_t total_allocated = 0;
1514 bytes = stream_Read(s, buf, sizeof(buf));
1520 if ( (total_bytes + bytes + 1) > total_allocated )
1522 if (total_allocated)
1523 total_allocated *= 2;
1525 total_allocated = __MIN(bytes+1, sizeof(buf));
1527 p = realloc_or_free(p, total_allocated);
1532 memcpy(p+total_bytes, buf, bytes);
1533 total_bytes += bytes;
1536 if (total_allocated == 0)
1537 return VLC_EGENERIC;
1539 p[total_bytes] = '\0';
1545 static ssize_t read_M3U8_from_url(stream_t *s, vlc_url_t *url, uint8_t **buffer)
1547 assert(*buffer == NULL);
1550 char *psz_url = ConstructUrl(url);
1551 if (psz_url == NULL)
1554 stream_t *p_m3u8 = stream_UrlNew(s, psz_url);
1557 return VLC_EGENERIC;
1559 ssize_t size = read_M3U8_from_stream(p_m3u8, buffer);
1560 stream_Delete(p_m3u8);
1565 static char *ReadLine(uint8_t *buffer, uint8_t **pos, const size_t len)
1570 uint8_t *begin = buffer;
1572 uint8_t *end = p + len;
1576 if ((*p == '\n') || (*p == '\0'))
1581 /* copy line excluding \n or \0 */
1582 line = strndup((char *)begin, p - begin);
1588 /* next pass start after \n */
1596 /****************************************************************************
1598 ****************************************************************************/
1599 static int Open(vlc_object_t *p_this)
1601 stream_t *s = (stream_t*)p_this;
1602 stream_sys_t *p_sys;
1604 if (!isHTTPLiveStreaming(s))
1605 return VLC_EGENERIC;
1607 msg_Info(p_this, "HTTP Live Streaming (%s)", s->psz_path);
1610 s->p_sys = p_sys = calloc(1, sizeof(*p_sys));
1614 char *psz_uri = NULL;
1615 if (asprintf(&psz_uri,"%s://%s", s->psz_access, s->psz_path) < 0)
1620 vlc_UrlParse(&p_sys->m3u8, psz_uri, 0);
1623 p_sys->bandwidth = -1;
1624 p_sys->b_live = true;
1625 p_sys->b_meta = false;
1626 p_sys->b_error = false;
1628 p_sys->hls_stream = vlc_array_new();
1629 if (p_sys->hls_stream == NULL)
1631 vlc_UrlClean(&p_sys->m3u8);
1639 s->pf_control = Control;
1641 /* Parse HLS m3u8 content. */
1642 uint8_t *buffer = NULL;
1643 ssize_t len = read_M3U8_from_stream(s->p_source, &buffer);
1646 if (parse_M3U8(s, p_sys->hls_stream, buffer, len) != VLC_SUCCESS)
1653 /* Choose first HLS stream to start with */
1654 int current = p_sys->playback.stream = 0;
1655 p_sys->playback.segment = p_sys->download.segment = ChooseSegment(s, current);
1657 if (p_sys->b_live && (p_sys->playback.segment < 0))
1659 msg_Warn(s, "less data than 3 times 'target duration' available for live playback, playback may stall");
1662 if (Prefetch(s, ¤t) != VLC_SUCCESS)
1664 msg_Err(s, "fetching first segment failed.");
1669 p_sys->download.stream = current;
1670 p_sys->playback.stream = current;
1671 p_sys->download.seek = -1;
1673 vlc_mutex_init(&p_sys->download.lock_wait);
1674 vlc_cond_init(&p_sys->download.wait);
1676 /* Initialize HLS live stream */
1679 hls_stream_t *hls = hls_Get(p_sys->hls_stream, current);
1680 p_sys->playlist.last = mdate();
1681 p_sys->playlist.wakeup = p_sys->playlist.last +
1682 ((mtime_t)hls->duration * UINT64_C(1000000));
1684 if (vlc_clone(&p_sys->reload, hls_Reload, s, VLC_THREAD_PRIORITY_LOW))
1690 if (vlc_clone(&p_sys->thread, hls_Thread, s, VLC_THREAD_PRIORITY_INPUT))
1693 vlc_join(p_sys->reload, NULL);
1700 vlc_mutex_destroy(&p_sys->download.lock_wait);
1701 vlc_cond_destroy(&p_sys->download.wait);
1704 /* Free hls streams */
1705 for (int i = 0; i < vlc_array_count(p_sys->hls_stream); i++)
1708 hls = (hls_stream_t *)vlc_array_item_at_index(p_sys->hls_stream, i);
1709 if (hls) hls_Free(hls);
1711 vlc_array_destroy(p_sys->hls_stream);
1714 vlc_UrlClean(&p_sys->m3u8);
1716 return VLC_EGENERIC;
1719 /****************************************************************************
1721 ****************************************************************************/
1722 static void Close(vlc_object_t *p_this)
1724 stream_t *s = (stream_t*)p_this;
1725 stream_sys_t *p_sys = s->p_sys;
1727 assert(p_sys->hls_stream);
1730 vlc_mutex_lock(&p_sys->download.lock_wait);
1731 vlc_cond_signal(&p_sys->download.wait);
1732 vlc_mutex_unlock(&p_sys->download.lock_wait);
1736 vlc_join(p_sys->reload, NULL);
1737 vlc_join(p_sys->thread, NULL);
1738 vlc_mutex_destroy(&p_sys->download.lock_wait);
1739 vlc_cond_destroy(&p_sys->download.wait);
1741 /* Free hls streams */
1742 for (int i = 0; i < vlc_array_count(p_sys->hls_stream); i++)
1745 hls = (hls_stream_t *)vlc_array_item_at_index(p_sys->hls_stream, i);
1746 if (hls) hls_Free(hls);
1748 vlc_array_destroy(p_sys->hls_stream);
1751 vlc_UrlClean(&p_sys->m3u8);
1753 block_Release (p_sys->peeked);
1757 /****************************************************************************
1758 * Stream filters functions
1759 ****************************************************************************/
1760 static segment_t *GetSegment(stream_t *s)
1762 stream_sys_t *p_sys = s->p_sys;
1763 segment_t *segment = NULL;
1765 /* Is this segment of the current HLS stream ready? */
1766 hls_stream_t *hls = hls_Get(p_sys->hls_stream, p_sys->playback.stream);
1769 vlc_mutex_lock(&hls->lock);
1770 segment = segment_GetSegment(hls, p_sys->playback.segment);
1771 if (segment != NULL)
1773 /* This segment is ready? */
1774 if (segment->data != NULL)
1776 p_sys->b_cache = hls->b_cache;
1777 vlc_mutex_unlock(&hls->lock);
1781 vlc_mutex_unlock(&hls->lock);
1784 /* Was the HLS stream changed to another bitrate? */
1787 while(vlc_object_alive(s))
1789 /* Is the next segment ready */
1790 hls_stream_t *hls = hls_Get(p_sys->hls_stream, i_stream);
1794 vlc_mutex_lock(&hls->lock);
1795 segment = segment_GetSegment(hls, p_sys->playback.segment);
1796 if (segment == NULL)
1798 vlc_mutex_unlock(&hls->lock);
1802 vlc_mutex_lock(&p_sys->download.lock_wait);
1803 int i_segment = p_sys->download.segment;
1804 vlc_mutex_unlock(&p_sys->download.lock_wait);
1806 /* This segment is ready? */
1807 if ((segment->data != NULL) &&
1808 (p_sys->playback.segment < i_segment))
1810 p_sys->playback.stream = i_stream;
1811 p_sys->b_cache = hls->b_cache;
1812 vlc_mutex_unlock(&hls->lock);
1815 vlc_mutex_unlock(&hls->lock);
1820 /* Was the stream changed to another bitrate? */
1822 if (i_stream >= vlc_array_count(p_sys->hls_stream))
1830 if (segment->data->i_buffer == 0)
1832 vlc_mutex_lock(&hls->lock);
1833 int count = vlc_array_count(hls->segments);
1834 vlc_mutex_unlock(&hls->lock);
1836 if ((p_sys->download.segment - p_sys->playback.segment == 0) &&
1837 ((count != p_sys->download.segment) || p_sys->b_live))
1838 msg_Err(s, "playback will stall");
1839 else if ((p_sys->download.segment - p_sys->playback.segment < 3) &&
1840 ((count != p_sys->download.segment) || p_sys->b_live))
1841 msg_Warn(s, "playback in danger of stalling");
1846 static ssize_t hls_Read(stream_t *s, uint8_t *p_read, unsigned int i_read)
1848 stream_sys_t *p_sys = s->p_sys;
1853 /* Determine next segment to read. If this is a meta playlist and
1854 * bandwidth conditions changed, then the stream might have switched
1855 * to another bandwidth. */
1856 segment_t *segment = GetSegment(s);
1857 if (segment == NULL)
1860 vlc_mutex_lock(&segment->lock);
1861 if (segment->data->i_buffer == 0)
1863 if (!p_sys->b_cache || p_sys->b_live)
1865 block_Release(segment->data);
1866 segment->data = NULL;
1869 { /* reset playback pointer to start of buffer */
1870 uint64_t size = segment->size - segment->data->i_buffer;
1873 segment->data->i_buffer += size;
1874 segment->data->p_buffer -= size;
1877 p_sys->playback.segment++;
1878 vlc_mutex_unlock(&segment->lock);
1880 /* signal download thread */
1881 vlc_mutex_lock(&p_sys->download.lock_wait);
1882 vlc_cond_signal(&p_sys->download.wait);
1883 vlc_mutex_unlock(&p_sys->download.lock_wait);
1887 if (segment->size == segment->data->i_buffer)
1888 msg_Info(s, "playing segment %d from stream %d",
1889 segment->sequence, p_sys->playback.stream);
1892 if (i_read <= segment->data->i_buffer)
1894 else if (i_read > segment->data->i_buffer)
1895 len = segment->data->i_buffer;
1899 memcpy(p_read + copied, segment->data->p_buffer, len);
1900 segment->data->i_buffer -= len;
1901 segment->data->p_buffer += len;
1905 vlc_mutex_unlock(&segment->lock);
1907 } while ((i_read > 0) && vlc_object_alive(s));
1912 static int Read(stream_t *s, void *buffer, unsigned int i_read)
1914 stream_sys_t *p_sys = s->p_sys;
1917 assert(p_sys->hls_stream);
1924 /* caller skips data, get big enough buffer */
1925 msg_Warn(s, "buffer is NULL (allocate %d)", i_read);
1926 buffer = calloc(1, i_read);
1928 return 0; /* NO MEMORY left*/
1931 length = hls_Read(s, (uint8_t*) buffer, i_read);
1935 p_sys->playback.offset += length;
1939 static int Peek(stream_t *s, const uint8_t **pp_peek, unsigned int i_peek)
1941 stream_sys_t *p_sys = s->p_sys;
1943 unsigned int len = i_peek;
1945 segment = GetSegment(s);
1946 if (segment == NULL)
1948 msg_Err(s, "segment %d should have been available (stream %d)",
1949 p_sys->playback.segment, p_sys->playback.stream);
1950 return 0; /* eof? */
1953 vlc_mutex_lock(&segment->lock);
1955 size_t i_buff = segment->data->i_buffer;
1956 uint8_t *p_buff = segment->data->p_buffer;
1958 if (i_peek < i_buff)
1961 vlc_mutex_unlock(&segment->lock);
1965 else /* This will seldom be run */
1967 /* remember segment to read */
1968 int peek_segment = p_sys->playback.segment;
1970 segment_t *nsegment;
1971 p_sys->playback.segment++;
1972 block_t *peeked = p_sys->peeked;
1975 peeked = block_Alloc (i_peek);
1976 else if (peeked->i_buffer < i_peek)
1977 peeked = block_Realloc (peeked, 0, i_peek);
1981 memcpy(peeked->p_buffer, p_buff, i_buff);
1984 vlc_mutex_unlock(&segment->lock);
1986 i_buff = peeked->i_buffer;
1987 p_buff = peeked->p_buffer;
1990 while ((curlen < i_peek) && vlc_object_alive(s))
1992 nsegment = GetSegment(s);
1993 if (nsegment == NULL)
1995 msg_Err(s, "segment %d should have been available (stream %d)",
1996 p_sys->playback.segment, p_sys->playback.stream);
1997 /* restore segment to read */
1998 p_sys->playback.segment = peek_segment;
1999 return curlen; /* eof? */
2002 vlc_mutex_lock(&nsegment->lock);
2004 if (len < nsegment->data->i_buffer)
2006 memcpy(p_buff + curlen, nsegment->data->p_buffer, len);
2011 size_t i_nbuff = nsegment->data->i_buffer;
2012 memcpy(p_buff + curlen, nsegment->data->p_buffer, i_nbuff);
2016 p_sys->playback.segment++;
2019 vlc_mutex_unlock(&nsegment->lock);
2022 /* restore segment to read */
2023 p_sys->playback.segment = peek_segment;
2028 static bool hls_MaySeek(stream_t *s)
2030 stream_sys_t *p_sys = s->p_sys;
2032 if (p_sys->hls_stream == NULL)
2035 hls_stream_t *hls = hls_Get(p_sys->hls_stream, p_sys->playback.stream);
2036 if (hls == NULL) return false;
2040 vlc_mutex_lock(&hls->lock);
2041 int count = vlc_array_count(hls->segments);
2042 vlc_mutex_unlock(&hls->lock);
2044 vlc_mutex_lock(&p_sys->download.lock_wait);
2045 bool may_seek = (p_sys->download.segment < (count - 2));
2046 vlc_mutex_unlock(&p_sys->download.lock_wait);
2052 static uint64_t GetStreamSize(stream_t *s)
2054 stream_sys_t *p_sys = s->p_sys;
2059 hls_stream_t *hls = hls_Get(p_sys->hls_stream, p_sys->playback.stream);
2060 if (hls == NULL) return 0;
2062 vlc_mutex_lock(&hls->lock);
2063 uint64_t size = hls->size;
2064 vlc_mutex_unlock(&hls->lock);
2069 static int segment_Seek(stream_t *s, const uint64_t pos)
2071 stream_sys_t *p_sys = s->p_sys;
2073 hls_stream_t *hls = hls_Get(p_sys->hls_stream, p_sys->playback.stream);
2075 return VLC_EGENERIC;
2077 vlc_mutex_lock(&hls->lock);
2079 bool b_found = false;
2080 uint64_t length = 0;
2081 uint64_t size = hls->size;
2082 int count = vlc_array_count(hls->segments);
2084 for (int n = 0; n < count; n++)
2086 segment_t *segment = vlc_array_item_at_index(hls->segments, n);
2087 if (segment == NULL)
2089 vlc_mutex_unlock(&hls->lock);
2090 return VLC_EGENERIC;
2093 vlc_mutex_lock(&segment->lock);
2094 length += segment->duration * (hls->bandwidth/8);
2095 vlc_mutex_unlock(&segment->lock);
2097 if (!b_found && (pos <= length))
2101 p_sys->playback.segment = n;
2105 /* Do not search in last 3 segments */
2106 vlc_mutex_unlock(&hls->lock);
2107 return VLC_EGENERIC;
2112 if (!b_found && (pos >= size))
2114 p_sys->playback.segment = count - 1;
2121 /* restore segment to start position */
2122 segment_t *segment = segment_GetSegment(hls, p_sys->playback.segment);
2123 if (segment == NULL)
2125 vlc_mutex_unlock(&hls->lock);
2126 return VLC_EGENERIC;
2129 vlc_mutex_lock(&segment->lock);
2132 uint64_t size = segment->size -segment->data->i_buffer;
2135 segment->data->i_buffer += size;
2136 segment->data->p_buffer -= size;
2139 vlc_mutex_unlock(&segment->lock);
2141 /* start download at current playback segment */
2142 vlc_mutex_unlock(&hls->lock);
2144 /* Wake up download thread */
2145 vlc_mutex_lock(&p_sys->download.lock_wait);
2146 p_sys->download.seek = p_sys->playback.segment;
2147 vlc_cond_signal(&p_sys->download.wait);
2148 vlc_mutex_unlock(&p_sys->download.lock_wait);
2150 /* Wait for download to be finished */
2151 vlc_mutex_lock(&p_sys->download.lock_wait);
2152 msg_Info(s, "seek to segment %d", p_sys->playback.segment);
2153 while (((p_sys->download.seek != -1) ||
2154 (p_sys->download.segment - p_sys->playback.segment < 3)) &&
2155 (p_sys->download.segment < (count - 6)))
2157 vlc_cond_wait(&p_sys->download.wait, &p_sys->download.lock_wait);
2158 if (!vlc_object_alive(s) || s->b_error) break;
2160 vlc_mutex_unlock(&p_sys->download.lock_wait);
2164 vlc_mutex_unlock(&hls->lock);
2166 return b_found ? VLC_SUCCESS : VLC_EGENERIC;
2169 static int Control(stream_t *s, int i_query, va_list args)
2171 stream_sys_t *p_sys = s->p_sys;
2175 case STREAM_CAN_SEEK:
2176 *(va_arg (args, bool *)) = hls_MaySeek(s);
2178 case STREAM_GET_POSITION:
2179 *(va_arg (args, uint64_t *)) = p_sys->playback.offset;
2181 case STREAM_SET_POSITION:
2184 uint64_t pos = (uint64_t)va_arg(args, uint64_t);
2185 if (segment_Seek(s, pos) == VLC_SUCCESS)
2187 p_sys->playback.offset = pos;
2191 return VLC_EGENERIC;
2192 case STREAM_GET_SIZE:
2193 *(va_arg (args, uint64_t *)) = GetStreamSize(s);
2196 return VLC_EGENERIC;