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 duration; /* segment duration (seconds) */
65 uint64_t size; /* segment size in bytes */
66 uint64_t bandwidth; /* bandwidth usage of segments (bits per second)*/
70 block_t *data; /* data */
73 typedef struct hls_stream_s
75 int id; /* program id */
76 int version; /* protocol version should be 1 */
77 int sequence; /* media sequence number */
78 int duration; /* maximum duration per segment (ms) */
79 uint64_t bandwidth; /* bandwidth usage of segments (bits per second)*/
80 uint64_t size; /* stream length (segment->duration * hls->bandwidth/8) */
82 vlc_array_t *segments; /* list of segments */
83 vlc_url_t url; /* uri to m3u8 */
85 bool b_cache; /* allow caching */
93 int current; /* current hls_stream */
94 int segment; /* current segment for downloading */
95 int seek; /* segment requested by seek (default -1) */
96 vlc_mutex_t lock_wait; /* protect segment download counter */
97 vlc_cond_t wait; /* some condition to wait on */
98 vlc_array_t *hls_stream;/* bandwidth adaptation */
105 access_t *p_access; /* HTTP access input */
108 hls_thread_t *thread;
109 vlc_array_t *hls_stream;/* bandwidth adaptation */
112 uint64_t offset; /* current offset in media */
113 int current; /* current hls_stream */
114 int segment; /* current segment for playback */
117 mtime_t last; /* playlist last loaded */
118 mtime_t wakeup; /* next reload time */
119 int tries; /* times it was not changed */
122 bool b_cache; /* can cache files */
123 bool b_meta; /* meta playlist */
124 bool b_live; /* live stream? or vod? */
125 bool b_error; /* parsing error */
128 /****************************************************************************
130 ****************************************************************************/
131 static int Read (stream_t *, void *p_read, unsigned int i_read);
132 static int Peek (stream_t *, const uint8_t **pp_peek, unsigned int i_peek);
133 static int Control(stream_t *, int i_query, va_list);
135 static int AccessOpen(stream_t *s, vlc_url_t *url);
136 static void AccessClose(stream_t *s);
137 static char *AccessReadLine(access_t *p_access, uint8_t *psz_tmp, size_t i_len);
138 static int AccessDownload(stream_t *s, segment_t *segment);
140 static void* hls_Thread(vlc_object_t *);
141 static int get_HTTPLivePlaylist(stream_t *s, hls_stream_t *hls);
143 static segment_t *segment_GetSegment(hls_stream_t *hls, int wanted);
144 static void segment_Free(segment_t *segment);
146 /****************************************************************************
148 ****************************************************************************/
149 static bool isHTTPLiveStreaming(stream_t *s)
151 const uint8_t *peek, *peek_end;
153 int64_t i_size = stream_Peek(s->p_source, &peek, 46);
157 if (strncasecmp((const char*)peek, "#EXTM3U", 7) != 0)
160 /* Parse stream and search for
161 * EXT-X-TARGETDURATION or EXT-X-STREAM-INF tag, see
162 * http://tools.ietf.org/html/draft-pantos-http-live-streaming-04#page-8 */
163 peek_end = peek + i_size;
164 while(peek <= peek_end)
168 if (strncasecmp((const char*)peek, "#EXT-X-TARGETDURATION", 21) == 0)
170 else if (strncasecmp((const char*)peek, "#EXT-X-STREAM-INF", 17) == 0)
179 /* HTTP Live Streaming */
180 static hls_stream_t *hls_New(vlc_array_t *hls_stream, int id, uint64_t bw, char *uri)
182 hls_stream_t *hls = (hls_stream_t *)malloc(sizeof(hls_stream_t));
183 if (hls == NULL) return NULL;
187 hls->duration = -1;/* unknown */
189 hls->sequence = 0; /* default is 0 */
190 hls->version = 1; /* default protocol version */
192 vlc_UrlParse(&hls->url, uri, 0);
193 hls->segments = vlc_array_new();
194 vlc_array_append(hls_stream, hls);
195 vlc_mutex_init(&hls->lock);
199 static void hls_Free(hls_stream_t *hls)
201 vlc_mutex_destroy(&hls->lock);
205 for (int n = 0; n < vlc_array_count(hls->segments); n++)
207 segment_t *segment = (segment_t *)vlc_array_item_at_index(hls->segments, n);
208 if (segment) segment_Free(segment);
210 vlc_array_destroy(hls->segments);
213 vlc_UrlClean(&hls->url);
218 static hls_stream_t *hls_Get(vlc_array_t *hls_stream, int wanted)
220 int count = vlc_array_count(hls_stream);
223 if ((wanted < 0) || (wanted >= count))
225 return (hls_stream_t *) vlc_array_item_at_index(hls_stream, wanted);
228 static inline hls_stream_t *hls_GetFirst(vlc_array_t *hls_stream)
230 return (hls_stream_t*) hls_Get(hls_stream, 0);
233 static hls_stream_t *hls_GetLast(vlc_array_t *hls_stream)
235 int count = vlc_array_count(hls_stream);
239 return (hls_stream_t *) hls_Get(hls_stream, count);
242 static uint64_t hls_GetStreamSize(hls_stream_t *hls)
244 /* NOTE: Stream size is calculated based on segment duration and
245 * HLS stream bandwidth from the .m3u8 file. If these are not correct
246 * then the deviation from exact byte size will be big and the seek/
247 * progressbar will not behave entirely as one expects. */
249 int count = vlc_array_count(hls->segments);
250 for (int n = 0; n < count; n++)
252 segment_t *segment = segment_GetSegment(hls, n);
255 size += (segment->duration * (hls->bandwidth / 8));
262 static segment_t *segment_New(hls_stream_t* hls, int duration, char *uri)
264 segment_t *segment = (segment_t *)malloc(sizeof(segment_t));
268 segment->duration = duration; /* seconds */
269 segment->size = 0; /* bytes */
270 segment->sequence = 0;
271 segment->bandwidth = 0;
272 vlc_UrlParse(&segment->url, uri, 0);
273 segment->data = NULL;
274 vlc_array_append(hls->segments, segment);
275 vlc_mutex_init(&segment->lock);
279 static void segment_Free(segment_t *segment)
281 vlc_mutex_destroy(&segment->lock);
283 vlc_UrlClean(&segment->url);
285 block_Release(segment->data);
290 static segment_t *segment_GetSegment(hls_stream_t *hls, int wanted)
294 int count = vlc_array_count(hls->segments);
297 if ((wanted < 0) || (wanted >= count))
299 return (segment_t *) vlc_array_item_at_index(hls->segments, wanted);
303 static char *parse_Attributes(const char *line, const char *attr)
306 char *begin = (char *) line;
307 char *end = begin + strlen(line);
309 /* Find start of attributes */
310 if ((p = strchr(begin, ':' )) == NULL)
316 if (strncasecmp(begin, attr, strlen(attr)) == 0)
318 /* <attr>=<value>[,]* */
319 p = strchr(begin, ',');
320 begin += strlen(attr) + 1;
323 if (p == NULL) /* last attribute */
324 return strndup(begin, end - begin);
326 return strndup(begin, p - begin);
329 } while(begin < end);
334 static char *relative_URI(stream_t *s, const char *uri, char *psz_uri)
336 char *p = strchr(uri, ':');
341 if (asprintf(&tmp,"%s://%s", s->psz_access, s->psz_path) < 0)
343 s->p_sys->b_error = true;
347 char *psz_path = strrchr(tmp, '/');
348 if (psz_path) *psz_path = '\0';
351 vlc_UrlParse(&url, tmp, 0);
352 if (asprintf(&psz_uri, "%s://%s%s/%s",
353 url.psz_protocol, url.psz_host, url.psz_path, uri) < 0)
357 s->p_sys->b_error = true;
365 static void parse_SegmentInformation(stream_t *s, hls_stream_t *hls, char *p_read, char *uri)
367 stream_sys_t *p_sys = s->p_sys;
372 int ret = sscanf(p_read, "#EXTINF:%d,", &duration);
375 msg_Err(s, "expected #EXTINF:<s>,");
376 p_sys->b_error = true;
380 char *psz_uri = NULL;
381 psz_uri = relative_URI(s, uri, psz_uri);
383 vlc_mutex_lock(&hls->lock);
384 segment_t *segment = segment_New(hls, duration, psz_uri ? psz_uri : uri);
386 segment->sequence = hls->sequence + vlc_array_count(hls->segments) - 1;
387 if (duration > hls->duration)
389 msg_Err(s, "EXTINF:%d duration is larger then EXT-X-TARGETDURATION:%d",
390 duration, hls->duration);
392 vlc_mutex_unlock(&hls->lock);
397 static void parse_TargetDuration(stream_t *s, hls_stream_t *hls, char *p_read)
399 stream_sys_t *p_sys = s->p_sys;
404 int ret = sscanf(p_read, "#EXT-X-TARGETDURATION:%d", &duration);
407 msg_Err(s, "expected #EXT-X-TARGETDURATION:<s>");
408 p_sys->b_error = true;
412 hls->duration = duration; /* seconds */
415 static void parse_StreamInformation(stream_t *s, char *p_read, char *uri)
417 stream_sys_t *p_sys = s->p_sys;
423 attr = parse_Attributes(p_read, "PROGRAM-ID");
426 msg_Err(s, "#EXT-X-STREAM-INF: expected PROGRAM-ID=<value>");
427 p_sys->b_error = true;
433 attr = parse_Attributes(p_read, "BANDWIDTH");
436 msg_Err(s, "#EXT-X-STREAM-INF: expected BANDWIDTH=<value>");
437 p_sys->b_error = true;
445 msg_Err(s, "#EXT-X-STREAM-INF: bandwidth cannot be 0");
446 p_sys->b_error = true;
450 msg_Info(s, "bandwidth adaption detected (program-id=%d, bandwidth=%"PRIu64").", id, bw);
452 char *psz_uri = NULL;
453 psz_uri = relative_URI(s, uri, psz_uri);
455 hls_stream_t *hls = hls_New(p_sys->hls_stream, id, bw, psz_uri ? psz_uri : uri);
457 p_sys->b_error = true;
462 static void parse_MediaSequence(stream_t *s, hls_stream_t *hls, char *p_read)
464 stream_sys_t *p_sys = s->p_sys;
469 int ret = sscanf(p_read, "#EXT-X-MEDIA-SEQUENCE:%d", &sequence);
472 msg_Err(s, "expected #EXT-X-MEDIA-SEQUENCE:<s>");
473 p_sys->b_error = true;
477 if (hls->sequence > 0)
478 msg_Err(s, "EXT-X-MEDIA-SEQUENCE already present in playlist");
480 hls->sequence = sequence;
483 static void parse_Key(stream_t *s, hls_stream_t *hls, char *p_read)
485 stream_sys_t *p_sys = s->p_sys;
489 /* #EXT-X-KEY:METHOD=<method>[,URI="<URI>"][,IV=<IV>] */
491 attr = parse_Attributes(p_read, "METHOD");
494 msg_Err(s, "#EXT-X-KEY: expected METHOD=<value>");
495 p_sys->b_error = true;
497 else if (strncasecmp(attr, "NONE", 4) == 0)
499 char *uri = parse_Attributes(p_read, "URI");
502 msg_Err(s, "#EXT-X-KEY: URI not expected");
503 p_sys->b_error = true;
506 /* IV is only supported in version 2 and above */
507 if (hls->version >= 2)
509 char *iv = parse_Attributes(p_read, "IV");
512 msg_Err(s, "#EXT-X-KEY: IV not expected");
513 p_sys->b_error = true;
520 msg_Warn(s, "playback of encrypted HTTP Live media is not supported.");
521 p_sys->b_error = true;
526 static void parse_ProgramDateTime(stream_t *s, hls_stream_t *hls, char *p_read)
529 msg_Dbg(s, "tag not supported: #EXT-X-PROGRAM-DATE-TIME %s", p_read);
532 static void parse_AllowCache(stream_t *s, hls_stream_t *hls, char *p_read)
534 stream_sys_t *p_sys = s->p_sys;
538 char answer[4] = "\0";
539 int ret = sscanf(p_read, "#EXT-X-ALLOW-CACHE:%3s", answer);
542 msg_Err(s, "#EXT-X-ALLOW-CACHE, ignoring ...");
543 p_sys->b_error = true;
547 hls->b_cache = (strncmp(answer, "NO", 2) != 0);
550 static void parse_Version(stream_t *s, hls_stream_t *hls, char *p_read)
552 stream_sys_t *p_sys = s->p_sys;
557 int ret = sscanf(p_read, "#EXT-X-VERSION:%d", &version);
560 msg_Err(s, "#EXT-X-VERSION: no protocol version found, should be version 1.");
561 p_sys->b_error = true;
566 hls->version = version;
567 if (hls->version != 1)
569 msg_Err(s, "#EXT-X-VERSION should be version 1 iso %d", version);
570 p_sys->b_error = true;
574 static void parse_EndList(stream_t *s, hls_stream_t *hls)
576 stream_sys_t *p_sys = s->p_sys;
580 p_sys->b_live = false;
581 msg_Info(s, "video on demand (vod) mode");
584 static void parse_Discontinuity(stream_t *s, hls_stream_t *hls, char *p_read)
588 /* FIXME: Do we need to act on discontinuity ?? */
589 msg_Dbg(s, "#EXT-X-DISCONTINUITY %s", p_read);
592 static void parse_M3U8ExtLine(stream_t *s, hls_stream_t *hls, char *line)
596 if (strncmp(line, "#EXT-X-TARGETDURATION", 21) == 0)
597 parse_TargetDuration(s, hls, line);
598 else if (strncmp(line, "#EXT-X-MEDIA-SEQUENCE", 22) == 0)
599 parse_MediaSequence(s, hls, line);
600 else if (strncmp(line, "#EXT-X-KEY", 11) == 0)
601 parse_Key(s, hls, line);
602 else if (strncmp(line, "#EXT-X-PROGRAM-DATE-TIME", 25) == 0)
603 parse_ProgramDateTime(s, hls, line);
604 else if (strncmp(line, "#EXT-X-ALLOW-CACHE", 17) == 0)
605 parse_AllowCache(s, hls, line);
606 else if (strncmp(line, "#EXT-X-DISCONTINUITY", 20) == 0)
607 parse_Discontinuity(s, hls, line);
608 else if (strncmp(line, "#EXT-X-VERSION", 14) == 0)
609 parse_Version(s, hls, line);
610 else if (strncmp(line, "#EXT-X-ENDLIST", 14) == 0)
611 parse_EndList(s, hls);
615 #define HTTPLIVE_MAX_LINE 4096
616 static int get_HTTPLivePlaylist(stream_t *s, hls_stream_t *hls)
618 stream_sys_t *p_sys = s->p_sys;
620 /* Download new playlist file from server */
621 if (AccessOpen(s, &hls->url) != VLC_SUCCESS)
624 /* Parse the rest of the reply */
625 uint8_t *tmp = calloc(1, HTTPLIVE_MAX_LINE);
632 char *line = AccessReadLine(p_sys->p_access, tmp, HTTPLIVE_MAX_LINE);
633 if (strncmp(line, "#EXTM3U", 7) != 0)
635 msg_Err(s, "missing #EXTM3U tag");
643 line = AccessReadLine(p_sys->p_access, tmp, HTTPLIVE_MAX_LINE);
646 msg_Dbg(s, "end of data");
650 if (!vlc_object_alive(s))
653 /* some more checks for actual data */
654 if (strncmp(line, "#EXTINF", 7) == 0)
656 char *uri = AccessReadLine(p_sys->p_access, tmp, HTTPLIVE_MAX_LINE);
658 p_sys->b_error = true;
661 parse_SegmentInformation(s, hls, line, uri);
667 parse_M3U8ExtLine(s, hls, line);
670 /* Error during m3u8 parsing abort */
688 #undef HTTPLIVE_MAX_LINE
690 /* The http://tools.ietf.org/html/draft-pantos-http-live-streaming-04#page-8
691 * document defines the following new tags: EXT-X-TARGETDURATION,
692 * EXT-X-MEDIA-SEQUENCE, EXT-X-KEY, EXT-X-PROGRAM-DATE-TIME, EXT-X-
693 * ALLOW-CACHE, EXT-X-STREAM-INF, EXT-X-ENDLIST, EXT-X-DISCONTINUITY,
696 static int parse_HTTPLiveStreaming(stream_t *s)
698 stream_sys_t *p_sys = s->p_sys;
699 char *p_read, *p_begin, *p_end;
701 assert(p_sys->hls_stream);
703 p_begin = p_read = stream_ReadLine(s->p_source);
708 int i_len = strlen(p_begin);
709 p_end = p_read + i_len;
711 if (strncmp(p_read, "#EXTM3U", 7) != 0)
713 msg_Err(s, "missing #EXTM3U tag .. aborting");
725 p_begin = stream_ReadLine(s->p_source);
729 i_len = strlen(p_begin);
731 p_end = p_read + i_len;
733 if (strncmp(p_read, "#EXT-X-STREAM-INF", 17) == 0)
735 p_sys->b_meta = true;
736 char *uri = stream_ReadLine(s->p_source);
738 p_sys->b_error = true;
741 parse_StreamInformation(s, p_read, uri);
745 else if (strncmp(p_read, "#EXTINF", 7) == 0)
747 char *uri = stream_ReadLine(s->p_source);
749 p_sys->b_error = true;
752 hls_stream_t *hls = hls_GetLast(p_sys->hls_stream);
754 parse_SegmentInformation(s, hls, p_read, uri);
756 p_sys->b_error = true;
762 hls_stream_t *hls = hls_GetLast(p_sys->hls_stream);
767 hls = hls_New(p_sys->hls_stream, -1, -1, NULL);
770 p_sys->b_error = true;
775 /* Parse M3U8 Ext Line */
776 parse_M3U8ExtLine(s, hls, p_read);
778 } while(p_read < p_end);
783 int count = vlc_array_count(p_sys->hls_stream);
784 for (int n = 0; n < count; n++)
786 hls_stream_t *hls = hls_Get(p_sys->hls_stream, n);
787 if (hls == NULL) break;
789 /* Is it a meta playlist? */
792 msg_Dbg(s, "parsing %s", hls->url.psz_path);
793 if (get_HTTPLivePlaylist(s, hls) != VLC_SUCCESS)
795 msg_Err(s, "could not parse playlist file from meta index." );
800 vlc_mutex_lock(&hls->lock);
804 /* There should at least be 3 segments of hls->duration */
806 int num = vlc_array_count(hls->segments);
807 for (int i = 0; i < num; i++)
809 segment_t *segment = segment_GetSegment(hls, i);
810 if (segment && segment->duration >= hls->duration)
815 msg_Err(s, "cannot start live playback at this time, try again later.");
816 vlc_mutex_unlock(&hls->lock);
821 /* Stream size (approximate) */
822 hls->size = hls_GetStreamSize(hls);
824 /* Can we cache files after playback */
825 p_sys->b_cache = hls->b_cache;
827 vlc_mutex_unlock(&hls->lock);
833 /****************************************************************************
835 ****************************************************************************/
836 static int BandwidthAdaptation(stream_t *s, int progid, uint64_t *bandwidth)
838 stream_sys_t *p_sys = s->p_sys;
840 uint64_t bw = *bandwidth;
841 uint64_t bw_candidate = 0;
843 int count = vlc_array_count(p_sys->hls_stream);
844 for (int n = 0; n < count; n++)
846 /* Select best bandwidth match */
847 hls_stream_t *hls = hls_Get(p_sys->hls_stream, n);
848 if (hls == NULL) break;
850 /* only consider streams with the same PROGRAM-ID */
851 if (hls->id == progid)
853 if ((bw >= hls->bandwidth) && (bw_candidate < hls->bandwidth))
855 msg_Dbg(s, "candidate %d bandwidth (bits/s) %"PRIu64" >= %"PRIu64,
856 n, bw, hls->bandwidth); /* bits / s */
857 bw_candidate = hls->bandwidth;
858 candidate = n; /* possible candidate */
862 *bandwidth = bw_candidate;
866 static int Download(stream_t *s, hls_stream_t *hls, segment_t *segment, int *cur_stream)
871 vlc_mutex_lock(&segment->lock);
872 if (segment->data != NULL)
874 /* Segment already downloaded */
875 vlc_mutex_unlock(&segment->lock);
879 mtime_t start = mdate();
880 if (AccessDownload(s, segment) != VLC_SUCCESS)
882 vlc_mutex_unlock(&segment->lock);
885 mtime_t duration = mdate() - start;
887 vlc_mutex_unlock(&segment->lock);
889 msg_Info(s, "downloaded segment %d from stream %d",
890 segment->sequence, *cur_stream);
892 /* check for division by zero */
893 double ms = (double)duration / 1000.0; /* ms */
897 uint64_t bw = ((double)(segment->size * 8) / ms) * 1000; /* bits / s */
898 segment->bandwidth = bw;
899 if (hls->bandwidth != bw)
901 int newstream = BandwidthAdaptation(s, hls->id, &bw);
903 /* FIXME: we need an average here */
904 if ((newstream >= 0) && (newstream != *cur_stream))
906 msg_Info(s, "detected %s bandwidth (%"PRIu64") stream",
907 (bw >= hls->bandwidth) ? "faster" : "lower", bw);
908 *cur_stream = newstream;
914 static void* hls_Thread(vlc_object_t *p_this)
916 hls_thread_t *client = (hls_thread_t *) p_this;
917 stream_t *s = client->s;
918 stream_sys_t *p_sys = s->p_sys;
920 int canc = vlc_savecancel();
922 while (vlc_object_alive(p_this))
924 hls_stream_t *hls = hls_Get(client->hls_stream, client->current);
927 vlc_mutex_lock(&hls->lock);
928 segment_t *segment = segment_GetSegment(hls, client->segment);
929 vlc_mutex_unlock(&hls->lock);
931 /* Is there a new segment to process? */
936 p_sys->last = mdate();
937 p_sys->wakeup = p_sys->last + (2 * (mtime_t)1000000);
939 mwait(p_sys->wakeup);
941 /* reset download segment to current playback segment */
942 client->segment = p_sys->segment + 1;
944 else if (Download(client->s, hls, segment, &client->current) != VLC_SUCCESS)
946 if (!p_sys->b_live) break;
949 /* download succeeded */
950 /* determine next segment to download */
951 vlc_mutex_lock(&client->lock_wait);
952 if (client->seek >= 0)
954 client->segment = client->seek;
957 else client->segment++;
958 vlc_cond_signal(&client->wait);
959 vlc_mutex_unlock(&client->lock_wait);
961 /* FIXME: Reread the m3u8 index file */
965 mtime_t now = mdate();
966 if (now >= p_sys->wakeup)
969 /** FIXME: Implement m3u8 playlist reloading */
970 if (!hls_ReloadPlaylist(client->s))
972 /* No change in playlist, then backoff */
974 if (p_sys->tries == 1) wait = 0.5;
975 else if (p_sys->tries == 2) wait = 1;
976 else if (p_sys->tries >= 3) wait = 3;
979 /* determine next time to update playlist */
981 p_sys->wakeup = now + ((mtime_t)(hls->duration * wait) * (mtime_t)1000000);
986 vlc_restorecancel(canc);
990 static int Prefetch(stream_t *s, int *current)
992 stream_sys_t *p_sys = s->p_sys;
995 /* Try to pick best matching stream */
999 hls_stream_t *hls = hls_Get(p_sys->hls_stream, *current);
1001 return VLC_EGENERIC;
1003 segment_t *segment = segment_GetSegment(hls, p_sys->segment);
1004 if (segment == NULL )
1005 return VLC_EGENERIC;
1007 if (Download(s, hls, segment, current) != VLC_SUCCESS)
1008 return VLC_EGENERIC;
1010 /* Found better bandwidth match, try again */
1011 if (*current != stream)
1014 /* Download first 2 segments of this HLS stream */
1015 for (int i = 0; i < 2; i++)
1017 segment_t *segment = segment_GetSegment(hls, p_sys->segment);
1018 if (segment == NULL )
1019 return VLC_EGENERIC;
1027 if (Download(s, hls, segment, current) != VLC_SUCCESS)
1028 return VLC_EGENERIC;
1036 /****************************************************************************
1038 ****************************************************************************/
1039 static int AccessOpen(stream_t *s, vlc_url_t *url)
1041 stream_sys_t *p_sys = (stream_sys_t *) s->p_sys;
1043 if ((url->psz_protocol == NULL) ||
1044 (url->psz_path == NULL))
1045 return VLC_EGENERIC;
1047 p_sys->p_access = vlc_object_create(s, sizeof(access_t));
1048 if (p_sys->p_access == NULL)
1051 p_sys->p_access->psz_access = strdup(url->psz_protocol);
1052 p_sys->p_access->psz_filepath = strdup(url->psz_path);
1053 if (url->psz_password || url->psz_username)
1055 if (asprintf(&p_sys->p_access->psz_location, "%s:%s@%s%s",
1056 url->psz_username, url->psz_password,
1057 url->psz_host, url->psz_path) < 0)
1059 msg_Err(s, "creating http access module");
1065 if (asprintf(&p_sys->p_access->psz_location, "%s%s",
1066 url->psz_host, url->psz_path) < 0)
1068 msg_Err(s, "creating http access module");
1072 vlc_object_attach(p_sys->p_access, s);
1073 p_sys->p_access->p_module =
1074 module_need(p_sys->p_access, "access", "http", true);
1075 if (p_sys->p_access->p_module == NULL)
1077 msg_Err(s, "could not load http access module");
1084 vlc_object_release(p_sys->p_access);
1085 p_sys->p_access = NULL;
1086 return VLC_EGENERIC;
1089 static void AccessClose(stream_t *s)
1091 stream_sys_t *p_sys = (stream_sys_t *) s->p_sys;
1093 if (p_sys->p_access)
1095 vlc_object_kill(p_sys->p_access);
1096 free(p_sys->p_access->psz_access);
1097 if (p_sys->p_access->p_module)
1098 module_unneed(p_sys->p_access,
1099 p_sys->p_access->p_module);
1101 vlc_object_release(p_sys->p_access);
1102 p_sys->p_access = NULL;
1106 static char *AccessReadLine(access_t *p_access, uint8_t *psz_tmp, size_t i_len)
1109 char *begin = (char *)psz_tmp;
1113 int skip = strlen(begin);
1114 ssize_t len = p_access->pf_read(p_access, psz_tmp + skip, i_len - skip);
1115 if (len < 0) return NULL;
1116 if ((len == 0) && (skip == 0))
1120 char *end = p + len + skip;
1130 /* copy line excluding \n */
1131 line = strndup(begin, p - begin);
1136 psz_tmp = memmove(begin, p, end - p);
1137 psz_tmp[end - p] = '\0';
1139 else memset(psz_tmp, 0, i_len);
1144 static int AccessDownload(stream_t *s, segment_t *segment)
1146 stream_sys_t *p_sys = (stream_sys_t *) s->p_sys;
1150 /* Download new playlist file from server */
1151 if (AccessOpen(s, &segment->url) != VLC_SUCCESS)
1152 return VLC_EGENERIC;
1154 segment->size = p_sys->p_access->info.i_size;
1155 assert(segment->size > 0);
1157 segment->data = block_Alloc(segment->size);
1158 if (segment->data == NULL)
1164 assert(segment->data->i_buffer == segment->size);
1166 ssize_t length = 0, curlen = 0;
1169 if (p_sys->p_access->info.i_size > segment->size)
1171 msg_Dbg(s, "size changed %"PRIu64, segment->size);
1172 segment->data = block_Realloc(segment->data, 0, p_sys->p_access->info.i_size);
1173 if (segment->data == NULL)
1178 segment->size = p_sys->p_access->info.i_size;
1179 assert(segment->data->i_buffer == segment->size);
1181 length = p_sys->p_access->pf_read(p_sys->p_access,
1182 segment->data->p_buffer + curlen, segment->size - curlen);
1183 if ((length <= 0) || ((uint64_t)length >= segment->size))
1186 } while (vlc_object_alive(s));
1192 /****************************************************************************
1194 ****************************************************************************/
1195 static int Open(vlc_object_t *p_this)
1197 stream_t *s = (stream_t*)p_this;
1198 stream_sys_t *p_sys;
1200 if (!isHTTPLiveStreaming(s))
1201 return VLC_EGENERIC;
1203 msg_Info(p_this, "HTTP Live Streaming (%s)", s->psz_path);
1206 s->p_sys = p_sys = calloc(1, sizeof(*p_sys));
1210 p_sys->b_live = true;
1211 p_sys->b_meta = false;
1213 p_sys->hls_stream = vlc_array_new();
1214 if (p_sys->hls_stream == NULL)
1223 s->pf_control = Control;
1225 /* Select first segment to play */
1226 p_sys->last = mdate();
1227 if (parse_HTTPLiveStreaming(s) != VLC_SUCCESS)
1232 /* Choose first HLS stream to start with */
1233 int current = p_sys->current = 0;
1236 if (Prefetch(s, ¤t) != VLC_SUCCESS)
1238 msg_Err(s, "fetching first segment.");
1242 p_sys->thread = vlc_object_create(s, sizeof(hls_thread_t));
1243 if( p_sys->thread == NULL )
1245 msg_Err(s, "creating HTTP Live Streaming client thread");
1249 p_sys->thread->hls_stream = p_sys->hls_stream;
1250 p_sys->thread->current = current;
1251 p_sys->current = current;
1252 p_sys->thread->segment = p_sys->segment;
1253 p_sys->thread->seek = -1;
1254 p_sys->segment = 0; /* reset to first segment */
1255 p_sys->thread->s = s;
1257 vlc_mutex_init(&p_sys->thread->lock_wait);
1258 vlc_cond_init(&p_sys->thread->wait);
1260 if (vlc_thread_create(p_sys->thread, "HTTP Live Streaming client",
1261 hls_Thread, VLC_THREAD_PRIORITY_INPUT))
1266 vlc_object_attach(p_sys->thread, s);
1272 return VLC_EGENERIC;
1275 /****************************************************************************
1277 ****************************************************************************/
1278 static void Close(vlc_object_t *p_this)
1280 stream_t *s = (stream_t*)p_this;
1281 stream_sys_t *p_sys = s->p_sys;
1283 assert(p_sys->hls_stream);
1288 vlc_mutex_lock(&p_sys->thread->lock_wait);
1289 vlc_object_kill(p_sys->thread);
1290 vlc_cond_signal(&p_sys->thread->wait);
1291 vlc_mutex_unlock(&p_sys->thread->lock_wait);
1294 vlc_thread_join(p_sys->thread);
1295 vlc_mutex_destroy(&p_sys->thread->lock_wait);
1296 vlc_cond_destroy(&p_sys->thread->wait);
1297 vlc_object_release(p_sys->thread);
1300 /* Free hls streams */
1301 for (int i = 0; i < vlc_array_count(p_sys->hls_stream); i++)
1304 hls = (hls_stream_t *)vlc_array_item_at_index(p_sys->hls_stream, i);
1305 if (hls) hls_Free(hls);
1307 vlc_array_destroy(p_sys->hls_stream);
1313 /****************************************************************************
1314 * Stream filters functions
1315 ****************************************************************************/
1316 static segment_t *GetSegment(stream_t *s)
1318 stream_sys_t *p_sys = s->p_sys;
1319 segment_t *segment = NULL;
1321 /* Is this segment of the current HLS stream ready? */
1322 hls_stream_t *hls = hls_Get(p_sys->hls_stream, p_sys->current);
1325 vlc_mutex_lock(&hls->lock);
1326 segment = segment_GetSegment(hls, p_sys->segment);
1327 if (segment != NULL)
1329 /* This segment is ready? */
1330 if (segment->data != NULL)
1332 vlc_mutex_unlock(&hls->lock);
1336 vlc_mutex_unlock(&hls->lock);
1339 /* Was the HLS stream changed to another bitrate? */
1342 while(vlc_object_alive(s))
1344 /* Is the next segment ready */
1345 hls_stream_t *hls = hls_Get(p_sys->hls_stream, i_stream);
1349 vlc_mutex_lock(&hls->lock);
1350 segment = segment_GetSegment(hls, p_sys->segment);
1351 if (segment == NULL)
1353 vlc_mutex_unlock(&hls->lock);
1357 vlc_mutex_lock(&p_sys->thread->lock_wait);
1358 int i_segment = p_sys->thread->segment;
1359 vlc_mutex_unlock(&p_sys->thread->lock_wait);
1361 /* This segment is ready? */
1362 if ((segment->data != NULL) &&
1363 (p_sys->segment < i_segment))
1365 p_sys->current = i_stream;
1366 vlc_mutex_unlock(&hls->lock);
1369 vlc_mutex_unlock(&hls->lock);
1374 /* Was the stream changed to another bitrate? */
1376 if (i_stream >= vlc_array_count(p_sys->hls_stream))
1383 static ssize_t hls_Read(stream_t *s, uint8_t *p_read, unsigned int i_read)
1385 stream_sys_t *p_sys = s->p_sys;
1390 /* Determine next segment to read. If this is a meta playlist and
1391 * bandwidth conditions changed, then the stream might have switched
1392 * to another bandwidth. */
1393 segment_t *segment = GetSegment(s);
1394 if (segment == NULL)
1397 vlc_mutex_lock(&segment->lock);
1398 if (segment->data->i_buffer == 0)
1400 if (!p_sys->b_cache)
1402 block_Release(segment->data);
1403 segment->data = NULL;
1406 { /* reset playback pointer to start of buffer */
1407 uint64_t size = segment->size - segment->data->i_buffer;
1410 segment->data->i_buffer += size;
1411 segment->data->p_buffer -= size;
1415 vlc_mutex_unlock(&segment->lock);
1419 if (segment->size == segment->data->i_buffer)
1420 msg_Info(s, "playing segment %d from stream %d",
1421 p_sys->segment, p_sys->current);
1424 if (i_read <= segment->data->i_buffer)
1426 else if (i_read > segment->data->i_buffer)
1427 len = segment->data->i_buffer;
1431 memcpy(p_read + copied, segment->data->p_buffer, len);
1432 segment->data->i_buffer -= len;
1433 segment->data->p_buffer += len;
1437 vlc_mutex_unlock(&segment->lock);
1439 } while ((i_read > 0) && vlc_object_alive(s));
1444 static int Read(stream_t *s, void *buffer, unsigned int i_read)
1446 stream_sys_t *p_sys = s->p_sys;
1449 assert(p_sys->hls_stream);
1453 /* caller skips data, get big enough buffer */
1454 msg_Warn(s, "buffer is NULL (allocate %d)", i_read);
1455 buffer = calloc(1, i_read);
1457 return 0; /* NO MEMORY left*/
1460 length = hls_Read(s, (uint8_t*) buffer, i_read);
1464 p_sys->offset += length;
1468 static int Peek(stream_t *s, const uint8_t **pp_peek, unsigned int i_peek)
1470 stream_sys_t *p_sys = s->p_sys;
1475 segment = GetSegment(s);
1476 if (segment == NULL)
1478 msg_Err(s, "segment should have been available");
1479 return 0; /* eof? */
1482 vlc_mutex_lock(&segment->lock);
1484 /* remember segment to peek */
1485 int peek_segment = p_sys->segment;
1488 if (i_peek < segment->data->i_buffer)
1490 *pp_peek = segment->data->p_buffer;
1496 vlc_mutex_unlock(&segment->lock);
1499 } while ((curlen < i_peek) && vlc_object_alive(s));
1501 /* restore segment to read */
1502 p_sys->segment = peek_segment;
1504 vlc_mutex_unlock(&segment->lock);
1509 static bool hls_MaySeek(stream_t *s)
1511 stream_sys_t *p_sys = s->p_sys;
1513 if ((p_sys->hls_stream == NULL) ||
1514 (p_sys->thread == NULL))
1517 hls_stream_t *hls = hls_Get(p_sys->hls_stream, p_sys->current);
1518 if (hls == NULL) return false;
1522 vlc_mutex_lock(&hls->lock);
1523 int count = vlc_array_count(hls->segments);
1524 vlc_mutex_unlock(&hls->lock);
1526 vlc_mutex_lock(&p_sys->thread->lock_wait);
1527 bool may_seek = (p_sys->thread->segment < (count - 2));
1528 vlc_mutex_unlock(&p_sys->thread->lock_wait);
1534 static uint64_t GetStreamSize(stream_t *s)
1536 stream_sys_t *p_sys = s->p_sys;
1541 hls_stream_t *hls = hls_Get(p_sys->hls_stream, p_sys->current);
1542 if (hls == NULL) return 0;
1544 vlc_mutex_lock(&hls->lock);
1545 uint64_t size = hls->size;
1546 vlc_mutex_unlock(&hls->lock);
1551 static int segment_Seek(stream_t *s, uint64_t pos)
1553 stream_sys_t *p_sys = s->p_sys;
1555 hls_stream_t *hls = hls_Get(p_sys->hls_stream, p_sys->current);
1557 return VLC_EGENERIC;
1559 vlc_mutex_lock(&hls->lock);
1561 bool b_found = false;
1562 uint64_t length = 0;
1563 uint64_t size = hls->size;
1564 int count = vlc_array_count(hls->segments);
1566 for (int n = 0; n < count; n++)
1568 segment_t *segment = vlc_array_item_at_index(hls->segments, n);
1569 if (segment == NULL)
1571 vlc_mutex_unlock(&hls->lock);
1572 return VLC_EGENERIC;
1575 vlc_mutex_lock(&segment->lock);
1576 length += segment->duration * (hls->bandwidth/8);
1578 if (!b_found && (pos <= length))
1580 count = p_sys->segment;
1584 vlc_mutex_unlock(&segment->lock);
1588 if (!b_found && (pos >= size))
1590 p_sys->segment = count - 1;
1597 /* restore segment to start position */
1598 segment_t *segment = segment_GetSegment(hls, p_sys->segment);
1599 if (segment == NULL)
1601 vlc_mutex_unlock(&hls->lock);
1602 return VLC_EGENERIC;
1605 vlc_mutex_lock(&segment->lock);
1608 uint64_t size = segment->size -segment->data->i_buffer;
1611 segment->data->i_buffer += size;
1612 segment->data->p_buffer -= size;
1615 vlc_mutex_unlock(&segment->lock);
1617 /* start download at current playback segment */
1620 vlc_mutex_unlock(&hls->lock);
1622 /* Wait for download to be finished */
1623 vlc_mutex_lock(&p_sys->thread->lock_wait);
1624 p_sys->thread->seek = p_sys->segment;
1625 msg_Info(s, "seek to segment %d", p_sys->segment);
1626 while ((p_sys->thread->seek != -1) ||
1627 (p_sys->thread->segment - p_sys->segment < 3))
1629 vlc_cond_wait(&p_sys->thread->wait, &p_sys->thread->lock_wait);
1630 if (!vlc_object_alive (s) ||
1633 vlc_mutex_unlock(&p_sys->thread->lock_wait);
1638 vlc_mutex_unlock(&hls->lock);
1640 return b_found ? VLC_SUCCESS : VLC_EGENERIC;
1643 static int Control(stream_t *s, int i_query, va_list args)
1645 stream_sys_t *p_sys = s->p_sys;
1649 case STREAM_CAN_SEEK:
1650 case STREAM_CAN_FASTSEEK:
1651 *(va_arg (args, bool *)) = hls_MaySeek(s);
1653 case STREAM_GET_POSITION:
1654 *(va_arg (args, uint64_t *)) = p_sys->offset;
1656 case STREAM_SET_POSITION:
1659 uint64_t pos = (uint64_t)va_arg(args, uint64_t);
1660 if (segment_Seek(s, pos) == VLC_SUCCESS)
1662 p_sys->offset = pos;
1666 return VLC_EGENERIC;
1667 case STREAM_GET_SIZE:
1668 *(va_arg (args, uint64_t *)) = GetStreamSize(s);
1671 return VLC_EGENERIC;