1 /*****************************************************************************
2 * httplive.c: HTTP Live Streaming stream filter
3 *****************************************************************************
4 * Copyright (C) 2010 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 *****************************************************************************/
31 #include <vlc_common.h>
32 #include <vlc_plugin.h>
36 #include <vlc_threads.h>
37 #include <vlc_arrays.h>
38 #include <vlc_stream.h>
41 #include <vlc_modules.h>
42 #include <vlc_access.h>
44 /*****************************************************************************
46 *****************************************************************************/
47 static int Open (vlc_object_t *);
48 static void Close(vlc_object_t *);
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)
58 /*****************************************************************************
60 *****************************************************************************/
61 typedef struct segment_s
63 int sequence; /* unique sequence number */
64 int length; /* segment duration (ms) */
65 uint64_t size; /* segment size in bytes */
69 block_t *data; /* data */
72 typedef struct hls_stream_s
74 int id; /* program id */
75 uint64_t bandwidth; /* bandwidth usage of segments (kbps)*/
76 int version; /* protocol version should be 1 */
77 int sequence; /* media sequence number */
78 int duration; /* maximum duration per segment (ms) */
79 int segment; /* current segment downloading */
80 vlc_array_t *segments; /* list of segments */
83 vlc_url_t url; /* uri to m3u8 */
84 bool b_cache; /* allow caching */
92 vlc_array_t *hls_stream;/* bandwidth adaptation */
93 int current; /* current hls_stream */
100 access_t *p_access; /* HTTP access input */
103 hls_thread_t *thread;
104 vlc_array_t *hls_stream;/* bandwidth adaptation */
107 uint64_t offset; /* current offset in media */
108 int current; /* current hls_stream */
109 int segment; /* current segment for playback */
112 mtime_t last; /* playlist last loaded */
113 mtime_t wakeup; /* next reload time */
114 int tries; /* times it was not changed */
117 bool b_cache; /* can cache files */
118 bool b_meta; /* meta playlist */
119 bool b_live; /* live stream? or vod? */
120 bool b_error; /* parsing error */
123 /****************************************************************************
125 ****************************************************************************/
126 static int Read (stream_t *, void *p_read, unsigned int i_read);
127 static int Peek (stream_t *, const uint8_t **pp_peek, unsigned int i_peek);
128 static int Control(stream_t *, int i_query, va_list);
130 static int AccessOpen(stream_t *s, vlc_url_t *url);
131 static void AccessClose(stream_t *s);
132 static char *AccessReadLine(access_t *p_access, uint8_t *psz_tmp, size_t i_len);
133 static int AccessDownload(stream_t *s, segment_t *segment);
135 static void* hls_Thread(vlc_object_t *);
136 static int get_HTTPLivePlaylist(stream_t *s, hls_stream_t *hls);
138 static void segment_Free(segment_t *segment);
140 /****************************************************************************
142 ****************************************************************************/
143 static bool isHTTPLiveStreaming(stream_t *s)
145 const uint8_t *peek, *peek_end;
147 int64_t i_size = stream_Peek(s->p_source, &peek, 46);
151 if (strncasecmp((const char*)peek, "#EXTM3U", 7) != 0)
154 /* Parse stream and search for
155 * EXT-X-TARGETDURATION or EXT-X-STREAM-INF tag, see
156 * http://tools.ietf.org/html/draft-pantos-http-live-streaming-04#page-8 */
157 peek_end = peek + i_size;
158 while(peek <= peek_end)
162 if (strncasecmp((const char*)peek, "#EXT-X-TARGETDURATION", 21) == 0)
164 else if (strncasecmp((const char*)peek, "#EXT-X-STREAM-INF", 17) == 0)
173 /* HTTP Live Streaming */
174 static hls_stream_t *hls_New(vlc_array_t *hls_stream, int id, uint64_t bw, char *uri)
176 hls_stream_t *hls = (hls_stream_t *)malloc(sizeof(hls_stream_t));
177 if (hls == NULL) return NULL;
181 hls->sequence = 0; /* default is 0 */
182 hls->version = 1; /* default protocol version */
185 vlc_UrlParse(&hls->url, uri, 0);
186 hls->segments = vlc_array_new();
187 vlc_array_append(hls_stream, hls);
188 vlc_mutex_init(&hls->lock);
192 static void hls_Free(hls_stream_t *hls)
194 vlc_mutex_destroy(&hls->lock);
198 for (int n = 0; n < vlc_array_count(hls->segments); n++)
200 segment_t *segment = (segment_t *)vlc_array_item_at_index(hls->segments, n);
201 if (segment) segment_Free(segment);
203 vlc_array_destroy(hls->segments);
206 vlc_UrlClean(&hls->url);
211 static hls_stream_t *hls_Get(vlc_array_t *hls_stream, int wanted)
213 int count = vlc_array_count(hls_stream);
216 if ((wanted < 0) || (wanted >= count))
218 return (hls_stream_t *) vlc_array_item_at_index(hls_stream, wanted);
221 static inline hls_stream_t *hls_GetFirst(vlc_array_t *hls_stream)
223 return (hls_stream_t*) hls_Get(hls_stream, 0);
226 static hls_stream_t *hls_GetLast(vlc_array_t *hls_stream)
228 int count = vlc_array_count(hls_stream);
232 return (hls_stream_t *) hls_Get(hls_stream, count);
236 static segment_t *segment_New(hls_stream_t* hls, int duration, char *uri)
238 segment_t *segment = (segment_t *)malloc(sizeof(segment_t));
242 segment->length = duration; /* seconds */
243 segment->size = 0; /* bytes */
244 segment->sequence = 0;
245 vlc_UrlParse(&segment->url, uri, 0);
246 segment->data = NULL;
247 vlc_array_append(hls->segments, segment);
248 vlc_mutex_init(&segment->lock);
252 static void segment_Free(segment_t *segment)
254 vlc_mutex_destroy(&segment->lock);
256 vlc_UrlClean(&segment->url);
258 block_Release(segment->data);
263 static segment_t *segment_GetSegment(hls_stream_t *hls, int wanted)
267 int count = vlc_array_count(hls->segments);
270 if ((wanted < 0) || (wanted >= count))
272 return (segment_t *) vlc_array_item_at_index(hls->segments, wanted);
276 static char *parse_Attributes(const char *line, const char *attr)
279 char *begin = (char *) line;
280 char *end = begin + strlen(line);
282 /* Find start of attributes */
283 if ((p = strchr(begin, ':' )) == NULL)
289 if (strncasecmp(begin, attr, strlen(attr)) == 0)
291 /* <attr>=<value>[,]* */
292 p = strchr(begin, ',');
293 begin += strlen(attr) + 1;
296 if (p == NULL) /* last attribute */
297 return strndup(begin, end - begin);
299 return strndup(begin, p - begin);
302 } while(begin < end);
307 static void parse_SegmentInformation(stream_t *s, hls_stream_t *hls, char *p_read, char *uri)
309 stream_sys_t *p_sys = s->p_sys;
314 int ret = sscanf(p_read, "#EXTINF:%d,", &duration);
317 msg_Err(s, "expected #EXTINF:<s>,");
318 p_sys->b_error = true;
322 vlc_mutex_lock(&hls->lock);
323 segment_t *segment = segment_New(hls, duration, uri);
325 segment->sequence = hls->sequence + vlc_array_count(hls->segments);
326 if (duration > hls->duration)
328 msg_Err(s, "EXTINF:%d duration is larger then EXT-X-TARGETDURATION:%d",
329 duration, hls->duration);
331 vlc_mutex_unlock(&hls->lock);
334 static void parse_TargetDuration(stream_t *s, char *p_read)
336 stream_sys_t *p_sys = s->p_sys;
339 int ret = sscanf(p_read, "#EXT-X-TARGETDURATION:%d", &duration);
342 msg_Err(s, "expected #EXT-X-TARGETDURATION:<s>");
343 p_sys->b_error = true;
347 hls_stream_t *hls = hls_GetLast(p_sys->hls_stream);
349 hls->duration = duration; /* seconds */
352 static void parse_StreamInformation(stream_t *s, char *p_read, char *uri)
354 stream_sys_t *p_sys = s->p_sys;
359 attr = parse_Attributes(p_read, "PROGRAM-ID");
362 msg_Err(s, "#EXT-X-STREAM-INF: expected PROGRAM-ID=<value>");
363 p_sys->b_error = true;
369 attr = parse_Attributes(p_read, "BANDWIDTH");
372 msg_Err(s, "#EXT-X-STREAM-INF: expected BANDWIDTH=<value>");
373 p_sys->b_error = true;
381 msg_Err(s, "#EXT-X-STREAM-INF: bandwidth cannot be 0");
382 p_sys->b_error = true;
386 msg_Info(s, "bandwidth adaption detected (program-id=%d, bandwidth=%d).", id, bw);
388 hls_stream_t *hls = hls_New(p_sys->hls_stream, id, bw, uri);
390 p_sys->b_error = true;
393 static void parse_MediaSequence(stream_t *s, char *p_read)
395 stream_sys_t *p_sys = s->p_sys;
398 int ret = sscanf(p_read, "#EXT-X-MEDIA-SEQUENCE:%d", &sequence);
401 msg_Err(s, "expected #EXT-X-MEDIA-SEQUENCE:<s>");
402 p_sys->b_error = true;
406 hls_stream_t *hls = hls_GetLast(p_sys->hls_stream);
408 if (hls->sequence > 0)
409 msg_Err(s, "EXT-X-MEDIA-SEQUENCE already present in playlist");
411 hls->sequence = sequence;
414 static void parse_Key(stream_t *s, char *p_read)
416 stream_sys_t *p_sys = s->p_sys;
418 /* #EXT-X-KEY:METHOD=<method>[,URI="<URI>"][,IV=<IV>] */
420 attr = parse_Attributes(p_read, "METHOD");
423 msg_Err(s, "#EXT-X-KEY: expected METHOD=<value>");
424 p_sys->b_error = true;
426 else if (strncasecmp(attr, "NONE", 4) == 0)
428 char *uri = parse_Attributes(p_read, "URI");
429 char *iv = parse_Attributes(p_read, "IV");
430 if ((iv != NULL) || (uri != NULL))
432 msg_Err(s, "#EXT-X-KEY: URI and IV not expected");
433 p_sys->b_error = true;
441 msg_Warn(s, "playback of encrypted HTTP Live media is not supported.");
442 p_sys->b_error = true;
447 static void parse_ProgramDateTime(stream_t *s, char *p_read)
449 msg_Dbg(s, "tag not supported: #EXT-X-PROGRAM-DATE-TIME %s", p_read);
452 static void parse_AllowCache(stream_t *s, char *p_read)
454 stream_sys_t *p_sys = s->p_sys;
456 char answer[4] = "\0";
457 int ret = sscanf(p_read, "#EXT-X-ALLOW-CACHE:%3s", answer);
460 msg_Err(s, "#EXT-X-ALLOW-CACHE, ignoring ...");
461 p_sys->b_error = true;
465 hls_stream_t *hls = hls_GetLast(p_sys->hls_stream);
468 hls->b_cache = (strncmp(answer, "NO", 2) != 0);
471 static void parse_Version(stream_t *s, char *p_read)
473 stream_sys_t *p_sys = s->p_sys;
476 int ret = sscanf(p_read, "#EXT-X-VERSION:%d", &version);
479 msg_Err(s, "#EXT-X-VERSION: no protocol version found, should be version 1.");
480 p_sys->b_error = true;
484 hls_stream_t *hls = hls_GetLast(p_sys->hls_stream);
488 hls->version = version;
489 if (hls->version != 1)
491 msg_Err(s, "#EXT-X-VERSION should be version 1 iso %d", version);
492 p_sys->b_error = true;
496 static void parse_EndList(stream_t *s)
498 stream_sys_t *p_sys = s->p_sys;
499 p_sys->b_live = false;
501 msg_Info(s, "video on demand (vod) mode");
504 static void parse_Discontinuity(stream_t *s, char *p_read)
506 /* FIXME: Do we need to act on discontinuity ?? */
507 msg_Dbg(s, "#EXT-X-DISCONTINUITY %s", p_read);
510 static void parse_M3U8ExtLine(stream_t *s, char *line)
514 if (strncmp(line, "#EXT-X-TARGETDURATION", 21) == 0)
515 parse_TargetDuration(s, line);
516 else if (strncmp(line, "#EXT-X-MEDIA-SEQUENCE", 22) == 0)
517 parse_MediaSequence(s, line);
518 else if (strncmp(line, "#EXT-X-KEY", 11) == 0)
520 else if (strncmp(line, "#EXT-X-PROGRAM-DATE-TIME", 25) == 0)
521 parse_ProgramDateTime(s, line);
522 else if (strncmp(line, "#EXT-X-ALLOW-CACHE", 17) == 0)
523 parse_AllowCache(s, line);
524 else if (strncmp(line, "#EXT-X-DISCONTINUITY", 20) == 0)
525 parse_Discontinuity(s, line);
526 else if (strncmp(line, "#EXT-X-VERSION", 14) == 0)
527 parse_Version(s, line);
528 else if (strncmp(line, "#EXT-X-ENDLIST", 14) == 0)
533 #define HTTPLIVE_MAX_LINE 4096
534 static int get_HTTPLivePlaylist(stream_t *s, hls_stream_t *hls)
536 stream_sys_t *p_sys = s->p_sys;
538 /* Download new playlist file from server */
539 if (AccessOpen(s, &hls->url) != VLC_SUCCESS)
542 /* Parse the rest of the reply */
543 uint8_t *tmp = calloc(1, HTTPLIVE_MAX_LINE);
550 char *line = AccessReadLine(p_sys->p_access, tmp, HTTPLIVE_MAX_LINE);
551 if (strncmp(line, "#EXTM3U", 7) != 0)
553 msg_Err(s, "missing #EXTM3U tag");
561 line = AccessReadLine(p_sys->p_access, tmp, HTTPLIVE_MAX_LINE);
564 msg_Dbg(s, "end of data");
568 if (!vlc_object_alive(s))
571 /* some more checks for actual data */
572 if (strncmp(line, "#EXTINF", 7) == 0)
574 char *uri = AccessReadLine(p_sys->p_access, tmp, HTTPLIVE_MAX_LINE);
576 p_sys->b_error = true;
579 parse_SegmentInformation(s, hls, line, uri);
585 parse_M3U8ExtLine(s, line);
588 /* Error during m3u8 parsing abort */
606 #undef HTTPLIVE_MAX_LINE
608 /* The http://tools.ietf.org/html/draft-pantos-http-live-streaming-04#page-8
609 * document defines the following new tags: EXT-X-TARGETDURATION,
610 * EXT-X-MEDIA-SEQUENCE, EXT-X-KEY, EXT-X-PROGRAM-DATE-TIME, EXT-X-
611 * ALLOW-CACHE, EXT-X-STREAM-INF, EXT-X-ENDLIST, EXT-X-DISCONTINUITY,
614 static int parse_HTTPLiveStreaming(stream_t *s)
616 stream_sys_t *p_sys = s->p_sys;
617 char *p_read, *p_begin, *p_end;
619 assert(p_sys->hls_stream);
621 p_begin = p_read = stream_ReadLine(s->p_source);
626 int i_len = strlen(p_begin);
627 p_end = p_read + i_len;
629 if (strncmp(p_read, "#EXTM3U", 7) != 0)
631 msg_Err(s, "missing #EXTM3U tag .. aborting");
643 p_begin = stream_ReadLine(s->p_source);
647 i_len = strlen(p_begin);
649 p_end = p_read + i_len;
651 if (strncmp(p_read, "#EXT-X-STREAM-INF", 17) == 0)
653 p_sys->b_meta = true;
654 char *uri = stream_ReadLine(s->p_source);
656 p_sys->b_error = true;
659 parse_StreamInformation(s, p_read, uri);
663 else if (strncmp(p_read, "#EXTINF", 7) == 0)
665 char *uri = stream_ReadLine(s->p_source);
667 p_sys->b_error = true;
670 hls_stream_t *hls = hls_GetLast(p_sys->hls_stream);
672 parse_SegmentInformation(s, hls, p_read, uri);
674 p_sys->b_error = true;
682 hls_stream_t *hls = hls_GetLast(p_sys->hls_stream);
685 hls = hls_New(p_sys->hls_stream, -1, -1, NULL);
688 p_sys->b_error = true;
693 /* Parse M3U8 Ext Line */
694 parse_M3U8ExtLine(s, p_read);
696 } while(p_read < p_end);
701 int count = vlc_array_count(p_sys->hls_stream);
702 for (int n = 0; n < count; n++)
704 hls_stream_t *hls = hls_Get(p_sys->hls_stream, n);
705 if (hls == NULL) break;
707 /* Is it a meta playlist? */
710 if (get_HTTPLivePlaylist(s, hls) != VLC_SUCCESS)
712 msg_Err(s, "could not parse playlist file from meta index." );
719 /* There should at least be 3 segments of hls->duration */
721 int num = vlc_array_count(hls->segments);
722 for (int i = 0; i < num; i++)
724 segment_t *segment = segment_GetSegment(hls, i);
725 if (segment && segment->length >= hls->duration)
730 msg_Err(s, "cannot start live playback at this time, try again later.");
733 /* Determine next time to reload playlist */
734 p_sys->wakeup = p_sys->last + (hls->duration * 2 * (mtime_t)1000000);
736 /* Can we cache files after playback */
737 p_sys->b_cache = hls->b_cache;
743 /****************************************************************************
745 ****************************************************************************/
746 static int BandwidthAdaptation(stream_t *s, int progid, uint64_t *bandwidth)
748 stream_sys_t *p_sys = s->p_sys;
750 uint64_t bw = *bandwidth;
752 msg_Dbg(s, "bandwidth (bits/s) %"PRIu64, bw * 1000); /* bits / s */
754 int count = vlc_array_count(p_sys->hls_stream);
755 for (int n = 0; n < count; n++)
757 /* Select best bandwidth match */
758 hls_stream_t *hls = hls_Get(p_sys->hls_stream, n);
759 if (hls == NULL) break;
761 /* only consider streams with the same PROGRAM-ID */
762 if (hls->id == progid)
764 if (bw <= hls->bandwidth)
766 *bandwidth = hls->bandwidth;
767 candidate = n; /* possible candidate */
774 static int Download(stream_t *s, hls_stream_t *hls, segment_t *segment, int *cur_stream)
779 vlc_mutex_lock(&segment->lock);
780 if (segment->data != NULL)
782 /* Segment already downloaded */
783 vlc_mutex_unlock(&segment->lock);
787 mtime_t start = mdate();
788 if (AccessDownload(s, segment) != VLC_SUCCESS)
790 vlc_mutex_unlock(&segment->lock);
793 mtime_t duration = mdate() - start;
795 vlc_mutex_unlock(&segment->lock);
797 uint64_t bw = (segment->size * 8) / (duration/1000); /* bits / ms */
798 if (hls->bandwidth != bw)
800 int newstream = BandwidthAdaptation(s, hls->id, &bw);
801 if ((newstream > 0) && (newstream != *cur_stream))
803 msg_Info(s, "switching to %s bandwidth (%"PRIu64") stream",
804 (hls->bandwidth <= bw) ? "faster" : "lower", bw);
805 *cur_stream = newstream;
811 static void* hls_Thread(vlc_object_t *p_this)
813 hls_thread_t *client = (hls_thread_t *) p_this;
814 stream_t *s = client->s;
815 stream_sys_t *p_sys = s->p_sys;
817 int canc = vlc_savecancel();
819 while (vlc_object_alive(p_this))
821 hls_stream_t *hls = hls_Get(client->hls_stream, client->current);
824 vlc_mutex_lock(&hls->lock);
825 segment_t *segment = segment_GetSegment(hls, hls->segment);
826 if (segment) hls->segment++;
827 vlc_mutex_unlock(&hls->lock);
829 /* Is there a new segment to process? */
832 if (!p_sys->b_live) break;
833 mwait(p_sys->wakeup);
835 else if (Download(client->s, hls, segment, &client->current) != VLC_SUCCESS)
837 if (!p_sys->b_live) break;
840 /* FIXME: Reread the m3u8 index file */
844 mtime_t now = mdate();
845 if (now >= p_sys->wakeup)
848 /** FIXME: Implement m3u8 playlist reloading */
849 if (!hls_ReloadPlaylist(client->s))
851 /* No change in playlist, then backoff */
853 if (p_sys->tries == 1) wait = 0.5;
854 else if (p_sys->tries == 2) wait = 1;
855 else if (p_sys->tries >= 3) wait = 3;
858 /* determine next time to update playlist */
860 p_sys->wakeup = now + ((mtime_t)(hls->duration * wait) * (mtime_t)1000000);
865 vlc_restorecancel(canc);
869 static int Prefetch(stream_t *s)
871 stream_sys_t *p_sys = s->p_sys;
875 current = p_sys->current;
877 hls_stream_t *hls = hls_Get(p_sys->hls_stream, p_sys->current);
881 segment_t *segment = segment_GetSegment(hls, hls->segment);
882 if (segment == NULL )
885 if (Download(s, hls, segment, &p_sys->current) != VLC_SUCCESS)
888 /* Bandwidth changed? */
889 if (current != p_sys->current)
895 /****************************************************************************
897 ****************************************************************************/
898 static int AccessOpen(stream_t *s, vlc_url_t *url)
900 stream_sys_t *p_sys = (stream_sys_t *) s->p_sys;
902 if ((url->psz_protocol == NULL) ||
903 (url->psz_path == NULL))
906 p_sys->p_access = vlc_object_create(s, sizeof(access_t));
909 p_sys->p_access->psz_access = strdup(url->psz_protocol);
910 p_sys->p_access->psz_filepath = strdup(url->psz_path);
911 if (url->psz_password || url->psz_username)
913 if (asprintf(&p_sys->p_access->psz_location, "%s:%s@%s%s",
914 url->psz_username, url->psz_password,
915 url->psz_host, url->psz_path) < 0)
917 msg_Err(s, "creating http access module");
923 if (asprintf(&p_sys->p_access->psz_location, "%s%s",
924 url->psz_host, url->psz_path) < 0)
926 msg_Err(s, "creating http access module");
930 vlc_object_attach(p_sys->p_access, s);
931 p_sys->p_access->p_module =
932 module_need(p_sys->p_access, "access", "http", true);
933 if (p_sys->p_access->p_module == NULL)
935 msg_Err(s, "could not load http access module");
943 vlc_object_release(p_sys->p_access);
944 p_sys->p_access = NULL;
948 static void AccessClose(stream_t *s)
950 stream_sys_t *p_sys = (stream_sys_t *) s->p_sys;
954 vlc_object_kill(p_sys->p_access);
955 free(p_sys->p_access->psz_access);
956 if (p_sys->p_access->p_module)
957 module_unneed(p_sys->p_access,
958 p_sys->p_access->p_module);
960 vlc_object_release(p_sys->p_access);
961 p_sys->p_access = NULL;
965 static char *AccessReadLine(access_t *p_access, uint8_t *psz_tmp, size_t i_len)
968 char *begin = (char *)psz_tmp;
972 int skip = strlen(begin);
973 ssize_t len = p_access->pf_read(p_access, psz_tmp + skip, i_len - skip);
974 if (len < 0) return NULL;
975 if ((len == 0) && (skip == 0))
979 char *end = p + len + skip;
990 line = calloc(1, p - begin + 1);
993 /* copy line excluding \n */
994 line = strndup(begin, p - begin);
999 psz_tmp = memmove(begin, p, end - p);
1000 psz_tmp[end - p] = '\0';
1002 else memset(psz_tmp, 0, i_len);
1007 static int AccessDownload(stream_t *s, segment_t *segment)
1009 stream_sys_t *p_sys = (stream_sys_t *) s->p_sys;
1013 /* Download new playlist file from server */
1014 if (AccessOpen(s, &segment->url) != VLC_SUCCESS)
1015 return VLC_EGENERIC;
1017 segment->size = p_sys->p_access->info.i_size;
1018 assert(segment->size > 0);
1020 segment->data = block_Alloc(segment->size);
1021 if (segment->data == NULL)
1027 assert(segment->data->i_buffer == segment->size);
1029 ssize_t length = 0, curlen = 0;
1032 if (p_sys->p_access->info.i_size > segment->size)
1034 msg_Dbg(s, "size changed %"PRIu64, segment->size);
1035 segment->data = block_Realloc(segment->data, 0, p_sys->p_access->info.i_size);
1037 segment->size = p_sys->p_access->info.i_size;
1038 assert(segment->data->i_buffer == segment->size);
1040 length = p_sys->p_access->pf_read(p_sys->p_access,
1041 segment->data->p_buffer + curlen, segment->size - curlen);
1042 if ((length <= 0) || ((uint64_t)length >= segment->size))
1045 } while (vlc_object_alive(s));
1047 assert(curlen == (ssize_t)segment->size);
1053 /****************************************************************************
1055 ****************************************************************************/
1056 static int Open(vlc_object_t *p_this)
1058 stream_t *s = (stream_t*)p_this;
1059 stream_sys_t *p_sys;
1061 if (!isHTTPLiveStreaming(s))
1062 return VLC_EGENERIC;
1064 msg_Info(p_this, "HTTP Live Streaming (%s)", s->psz_path);
1067 s->p_sys = p_sys = calloc(1, sizeof(*p_sys));
1071 p_sys->b_live = true;
1072 p_sys->b_meta = false;
1074 p_sys->hls_stream = vlc_array_new();
1075 if (p_sys->hls_stream == NULL)
1084 s->pf_control = Control;
1086 /* Select first segment to play */
1087 p_sys->last = mdate();
1088 if (parse_HTTPLiveStreaming(s) != VLC_SUCCESS)
1097 if (Prefetch(s) != VLC_SUCCESS)
1099 msg_Err(s, "fetching first segment.");
1103 p_sys->thread = vlc_object_create(s, sizeof(hls_thread_t));
1104 if( p_sys->thread == NULL )
1106 msg_Err(s, "creating HTTP Live Streaming client thread");
1110 p_sys->thread->hls_stream = p_sys->hls_stream;
1111 p_sys->thread->current = p_sys->current;
1112 p_sys->thread->s = s;
1114 if (vlc_thread_create(p_sys->thread, "HTTP Live Streaming client",
1115 hls_Thread, VLC_THREAD_PRIORITY_INPUT))
1120 vlc_object_attach(p_sys->thread, s);
1126 return VLC_EGENERIC;
1129 /****************************************************************************
1131 ****************************************************************************/
1132 static void Close(vlc_object_t *p_this)
1134 stream_t *s = (stream_t*)p_this;
1135 stream_sys_t *p_sys = s->p_sys;
1137 assert(p_sys->hls_stream);
1142 vlc_object_kill(p_sys->thread);
1143 vlc_thread_join(p_sys->thread);
1144 vlc_object_release(p_sys->thread);
1147 /* Free hls streams */
1148 for (int i = 0; i < vlc_array_count(p_sys->hls_stream); i++)
1151 hls = (hls_stream_t *)vlc_array_item_at_index(p_sys->hls_stream, i);
1152 if (hls) hls_Free(hls);
1154 vlc_array_destroy(p_sys->hls_stream);
1160 /****************************************************************************
1161 * Stream filters functions
1162 ****************************************************************************/
1163 static segment_t *NextSegment(stream_t *s)
1165 stream_sys_t *p_sys = s->p_sys;
1170 /* Is the next segment ready */
1171 hls_stream_t *hls = hls_Get(p_sys->hls_stream, p_sys->current);
1172 if (hls == NULL) return NULL;
1174 segment = segment_GetSegment(hls, p_sys->segment);
1175 if (segment == NULL) return NULL;
1177 /* This segment is ready? */
1178 if (segment->data != NULL)
1181 /* Was the stream changed to another bitrate? */
1182 if (!p_sys->b_meta) return NULL;
1184 if (p_sys->current != p_sys->thread->current)
1187 p_sys->current = p_sys->thread->current;
1192 /* Not downloaded yet, do it here */
1193 if (Download(s, hls, segment, &p_sys->current) != VLC_SUCCESS)
1200 while(vlc_object_alive(s));
1205 static ssize_t hls_Read(stream_t *s, uint8_t *p_read, unsigned int i_read)
1207 stream_sys_t *p_sys = s->p_sys;
1212 /* Determine next segment to read. If this is a meta playlist and
1213 * bandwidth conditions changed, then the stream might have switched
1214 * to another bandwidth. */
1215 segment_t *segment = NextSegment(s);
1216 if (segment == NULL)
1219 vlc_mutex_lock(&segment->lock);
1220 if (segment->data->i_buffer == 0)
1222 if (!p_sys->b_cache)
1224 block_Release(segment->data);
1225 segment->data = NULL;
1227 msg_Dbg(s, "switching to segment %d", p_sys->segment);
1229 vlc_mutex_unlock(&segment->lock);
1234 if (i_read <= segment->data->i_buffer)
1236 else if (i_read > segment->data->i_buffer)
1237 len = segment->data->i_buffer;
1241 memcpy(p_read + copied, segment->data->p_buffer, len);
1242 segment->data->i_buffer -= len;
1243 segment->data->p_buffer += len;
1247 vlc_mutex_unlock(&segment->lock);
1249 } while ((i_read > 0) && vlc_object_alive(s));
1254 static int Read(stream_t *s, void *buffer, unsigned int i_read)
1256 stream_sys_t *p_sys = s->p_sys;
1259 assert(p_sys->hls_stream);
1263 /* caller skips data, get big enough buffer */
1264 msg_Warn(s, "buffer is NULL (allocate %d)", i_read);
1265 buffer = calloc(1, i_read);
1267 return 0; /* NO MEMORY left*/
1270 length = hls_Read(s, (uint8_t*) buffer, i_read);
1274 p_sys->offset += length;
1278 static int Peek(stream_t *s, const uint8_t **pp_peek, unsigned int i_peek)
1280 stream_sys_t *p_sys = s->p_sys;
1283 hls_stream_t *hls = hls_Get(p_sys->hls_stream, p_sys->current);
1287 int peek_segment = p_sys->segment;
1290 segment_t *segment = segment_GetSegment(hls, peek_segment);
1291 if (segment == NULL) return 0;
1293 vlc_mutex_lock(&segment->lock);
1294 if (i_peek < segment->data->i_buffer)
1296 *pp_peek = segment->data->p_buffer;
1303 vlc_mutex_unlock(&segment->lock);
1304 } while ((curlen < i_peek) && vlc_object_alive(s));
1309 static bool hls_MaySeek(stream_t *s)
1311 stream_sys_t *p_sys = s->p_sys;
1313 if (p_sys->hls_stream == NULL)
1316 hls_stream_t *hls = hls_Get(p_sys->hls_stream, p_sys->current);
1317 if (hls == NULL) return false;
1321 int count = vlc_array_count(hls->segments);
1322 vlc_mutex_lock(&hls->lock);
1323 bool may_seek = (hls->segment < count - 2);
1324 vlc_mutex_unlock(&hls->lock);
1330 static uint64_t GetStreamSize(stream_t *s)
1332 stream_sys_t *p_sys = s->p_sys;
1337 hls_stream_t *hls = hls_Get(p_sys->hls_stream, p_sys->current);
1338 if (hls == NULL) return 0;
1340 /* FIXME: Stream size is not entirely exacts at this point */
1341 uint64_t length = 0UL;
1342 int count = vlc_array_count(hls->segments);
1343 for (int n = 0; n < count; n++)
1345 segment_t *segment = segment_GetSegment(hls, n);
1348 length += (segment->size > 0) ? segment->size :
1349 (segment->length * hls->bandwidth);
1355 static int segment_Seek(stream_t *s, uint64_t pos)
1357 stream_sys_t *p_sys = s->p_sys;
1359 /* Find the right offset */
1360 hls_stream_t *hls = hls_Get(p_sys->hls_stream, p_sys->current);
1362 return VLC_EGENERIC;
1364 uint64_t length = 0;
1365 bool b_found = false;
1367 int count = vlc_array_count(hls->segments);
1368 for (int n = 0; n < count; n++)
1370 /* FIXME: Seeking in segments not dowloaded is not supported. */
1371 if (n >= hls->segment)
1373 msg_Err(s, "seeking in segment not downloaded yet.");
1374 return VLC_EGENERIC;
1377 segment_t *segment = vlc_array_item_at_index(hls->segments, n);
1378 if (segment == NULL)
1379 return VLC_EGENERIC;
1381 vlc_mutex_lock(&segment->lock);
1382 length += segment->size;
1383 uint64_t size = segment->size -segment->data->i_buffer;
1386 segment->data->i_buffer += size;
1387 segment->data->p_buffer -= size;
1390 if (!b_found && (pos <= length))
1392 uint64_t used = length - pos;
1393 segment->data->i_buffer -= used;
1394 segment->data->p_buffer += used;
1396 count = p_sys->segment;
1400 vlc_mutex_unlock(&segment->lock);
1405 static int Control(stream_t *s, int i_query, va_list args)
1407 stream_sys_t *p_sys = s->p_sys;
1411 case STREAM_CAN_SEEK:
1412 case STREAM_CAN_FASTSEEK:
1413 *(va_arg (args, bool *)) = hls_MaySeek(s);
1415 case STREAM_GET_POSITION:
1416 *(va_arg (args, uint64_t *)) = p_sys->offset;
1418 case STREAM_SET_POSITION:
1421 uint64_t pos = (uint64_t)va_arg(args, uint64_t);
1422 if (segment_Seek(s, pos) == VLC_SUCCESS)
1424 p_sys->offset = pos;
1428 return VLC_EGENERIC;
1429 case STREAM_GET_SIZE:
1430 *(va_arg (args, uint64_t *)) = GetStreamSize(s);
1433 return VLC_EGENERIC;