VLC_COMMON_MEMBERS
/* */
- int current; /* current hls_stream */
- int segment; /* current segment for downloading */
- int seek; /* segment requested by seek (default -1) */
- vlc_mutex_t lock_wait; /* protect segment download counter */
- vlc_cond_t wait; /* some condition to wait on */
- vlc_array_t *hls_stream;/* bandwidth adaptation */
-
stream_t *s;
} hls_thread_t;
vlc_array_t *hls_stream;/* bandwidth adaptation */
uint64_t bandwidth; /* measured bandwidth (bits per second) */
+ /* Download */
+ struct hls_download_s
+ {
+ int current; /* current hls_stream */
+ int segment; /* current segment for downloading */
+ int seek; /* segment requested by seek (default -1) */
+ vlc_mutex_t lock_wait; /* protect segment download counter */
+ vlc_cond_t wait; /* some condition to wait on */
+ } download;
+
/* Playback */
- struct hls_playback_t
+ struct hls_playback_s
{
uint64_t offset; /* current offset in media */
int current; /* current hls_stream */
return (hls_stream_t *) hls_Get(hls_stream, count);
}
+static hls_stream_t *hls_Find(vlc_array_t *hls_stream, hls_stream_t *hls_new)
+{
+ int count = vlc_array_count(hls_stream);
+ for (int n = 0; n < count; n++)
+ {
+ hls_stream_t *hls = vlc_array_item_at_index(hls_stream, n);
+ if (hls)
+ {
+ /* compare */
+ if ((hls->id == hls_new->id) &&
+ (hls->bandwidth == hls_new->bandwidth))
+ return hls;
+ }
+ }
+ return NULL;
+}
+
static uint64_t hls_GetStreamSize(hls_stream_t *hls)
{
/* NOTE: Stream size is calculated based on segment duration and
return (segment_t *) vlc_array_item_at_index(hls->segments, wanted);
}
+static segment_t *segment_Find(hls_stream_t *hls, int sequence)
+{
+ assert(hls);
+
+ int count = vlc_array_count(hls->segments);
+ if (count <= 0) return NULL;
+ for (int n = 0; n < count; n++)
+ {
+ segment_t *segment = vlc_array_item_at_index(hls->segments, n);
+ if (segment == NULL) break;
+ if (segment->sequence == sequence)
+ return segment;
+ }
+ return NULL;
+}
+
/* Parsing */
static char *parse_Attributes(const char *line, const char *attr)
{
return NULL;
}
-static char *relative_URI(stream_t *s, const char *uri, char *psz_uri)
+static char *relative_URI(stream_t *s, const char *uri, const char *path)
{
stream_sys_t *p_sys = s->p_sys;
if (p != NULL)
return NULL;
- if (asprintf(&psz_uri, "%s://%s%s/%s", p_sys->m3u8.psz_protocol,
- p_sys->m3u8.psz_host, p_sys->m3u8.psz_path, uri) < 0)
- return NULL;
+ char *psz_path = strdup(p_sys->m3u8.psz_path);
+ if (psz_path == NULL) return NULL;
+ p = strrchr(psz_path, '/');
+ if (p) *p = '\0';
+ char *psz_uri = NULL;
+ if (p_sys->m3u8.psz_password || p_sys->m3u8.psz_username)
+ {
+ if (asprintf(&psz_uri, "%s://%s:%s@%s%s/%s", p_sys->m3u8.psz_protocol,
+ p_sys->m3u8.psz_username, p_sys->m3u8.psz_password,
+ p_sys->m3u8.psz_host, path ? path : psz_path, uri) < 0)
+ goto fail;
+ }
+ else
+ {
+ if (asprintf(&psz_uri, "%s://%s%s/%s", p_sys->m3u8.psz_protocol,
+ p_sys->m3u8.psz_host, path ? path : psz_path, uri) < 0)
+ goto fail;
+ }
+ free(psz_path);
return psz_uri;
+
+fail:
+ free(psz_path);
+ return NULL;
}
static void parse_SegmentInformation(stream_t *s, hls_stream_t *hls, char *p_read, char *uri)
return;
}
- char *psz_uri = NULL;
- psz_uri = relative_URI(s, uri, psz_uri);
+ char *psz_path = strdup(hls->url.psz_path);
+ if (psz_path == NULL)
+ {
+ p_sys->b_error = true;
+ return;
+ }
+ char *p = strrchr(psz_path, '/');
+ if (p) *p = '\0';
+ char *psz_uri = relative_URI(s, uri, psz_path);
+ free(psz_path);
vlc_mutex_lock(&hls->lock);
segment_t *segment = segment_New(hls, duration, psz_uri ? psz_uri : uri);
free(psz_uri);
}
-static void parse_TargetDuration(stream_t *s, hls_stream_t *hls, char *p_read)
+static int parse_TargetDuration(stream_t *s, hls_stream_t *hls, char *p_read)
{
- stream_sys_t *p_sys = s->p_sys;
-
assert(hls);
int duration = -1;
if (ret != 1)
{
msg_Err(s, "expected #EXT-X-TARGETDURATION:<s>");
- p_sys->b_error = true;
- return;
+ return VLC_EGENERIC;
}
hls->duration = duration; /* seconds */
+ return VLC_SUCCESS;
}
-static void parse_StreamInformation(stream_t *s, char *p_read, char *uri)
+static void parse_StreamInformation(stream_t *s, vlc_array_t **hls_stream,
+ char *p_read, char *uri)
{
stream_sys_t *p_sys = s->p_sys;
msg_Info(s, "bandwidth adaption detected (program-id=%d, bandwidth=%"PRIu64").", id, bw);
- char *psz_uri = NULL;
- psz_uri = relative_URI(s, uri, psz_uri);
+ char *psz_uri = relative_URI(s, uri, NULL);
- hls_stream_t *hls = hls_New(p_sys->hls_stream, id, bw, psz_uri ? psz_uri : uri);
+ hls_stream_t *hls = hls_New(*hls_stream, id, bw, psz_uri ? psz_uri : uri);
if (hls == NULL)
p_sys->b_error = true;
free(psz_uri);
}
-static void parse_MediaSequence(stream_t *s, hls_stream_t *hls, char *p_read)
+static int parse_MediaSequence(stream_t *s, hls_stream_t *hls, char *p_read)
{
- stream_sys_t *p_sys = s->p_sys;
-
assert(hls);
int sequence;
if (ret != 1)
{
msg_Err(s, "expected #EXT-X-MEDIA-SEQUENCE:<s>");
- p_sys->b_error = true;
- return;
+ return VLC_EGENERIC;
}
if (hls->sequence > 0)
msg_Err(s, "EXT-X-MEDIA-SEQUENCE already present in playlist");
hls->sequence = sequence;
+ return VLC_SUCCESS;
}
-static void parse_Key(stream_t *s, hls_stream_t *hls, char *p_read)
+static int parse_Key(stream_t *s, hls_stream_t *hls, char *p_read)
{
- stream_sys_t *p_sys = s->p_sys;
-
assert(hls);
/* #EXT-X-KEY:METHOD=<method>[,URI="<URI>"][,IV=<IV>] */
- char *attr;
- attr = parse_Attributes(p_read, "METHOD");
+ int err = VLC_SUCCESS;
+ char *attr = parse_Attributes(p_read, "METHOD");
if (attr == NULL)
{
msg_Err(s, "#EXT-X-KEY: expected METHOD=<value>");
- p_sys->b_error = true;
+ return err;
}
- else if (strncasecmp(attr, "NONE", 4) == 0)
+
+ if (strncasecmp(attr, "NONE", 4) == 0)
{
+
char *uri = parse_Attributes(p_read, "URI");
if (uri != NULL)
{
msg_Err(s, "#EXT-X-KEY: URI not expected");
- p_sys->b_error = true;
+ err = VLC_EGENERIC;
}
free(uri);
/* IV is only supported in version 2 and above */
if (iv != NULL)
{
msg_Err(s, "#EXT-X-KEY: IV not expected");
- p_sys->b_error = true;
+ err = VLC_EGENERIC;
}
free(iv);
}
else
{
msg_Warn(s, "playback of encrypted HTTP Live media is not supported.");
- p_sys->b_error = true;
+ err = VLC_EGENERIC;
}
free(attr);
+ return err;
}
-static void parse_ProgramDateTime(stream_t *s, hls_stream_t *hls, char *p_read)
+static int parse_ProgramDateTime(stream_t *s, hls_stream_t *hls, char *p_read)
{
VLC_UNUSED(hls);
msg_Dbg(s, "tag not supported: #EXT-X-PROGRAM-DATE-TIME %s", p_read);
+ return VLC_SUCCESS;
}
-static void parse_AllowCache(stream_t *s, hls_stream_t *hls, char *p_read)
+static int parse_AllowCache(stream_t *s, hls_stream_t *hls, char *p_read)
{
- stream_sys_t *p_sys = s->p_sys;
-
assert(hls);
char answer[4] = "\0";
if (ret != 1)
{
msg_Err(s, "#EXT-X-ALLOW-CACHE, ignoring ...");
- p_sys->b_error = true;
- return;
+ return VLC_EGENERIC;
}
hls->b_cache = (strncmp(answer, "NO", 2) != 0);
+ return VLC_SUCCESS;
}
-static void parse_Version(stream_t *s, hls_stream_t *hls, char *p_read)
+static int parse_Version(stream_t *s, hls_stream_t *hls, char *p_read)
{
- stream_sys_t *p_sys = s->p_sys;
-
assert(hls);
int version;
if (ret != 1)
{
msg_Err(s, "#EXT-X-VERSION: no protocol version found, should be version 1.");
- p_sys->b_error = true;
- return;
+ return VLC_EGENERIC;
}
/* Check version */
if (hls->version != 1)
{
msg_Err(s, "#EXT-X-VERSION should be version 1 iso %d", version);
- p_sys->b_error = true;
+ return VLC_EGENERIC;
}
+ return VLC_SUCCESS;
}
-static void parse_EndList(stream_t *s, hls_stream_t *hls)
+static int parse_EndList(stream_t *s, hls_stream_t *hls)
{
- stream_sys_t *p_sys = s->p_sys;
-
assert(hls);
- p_sys->b_live = false;
+ s->p_sys->b_live = false;
msg_Info(s, "video on demand (vod) mode");
+ return VLC_SUCCESS;
}
-static void parse_Discontinuity(stream_t *s, hls_stream_t *hls, char *p_read)
+static int parse_Discontinuity(stream_t *s, hls_stream_t *hls, char *p_read)
{
assert(hls);
/* FIXME: Do we need to act on discontinuity ?? */
msg_Dbg(s, "#EXT-X-DISCONTINUITY %s", p_read);
+ return VLC_SUCCESS;
}
static void parse_M3U8ExtLine(stream_t *s, hls_stream_t *hls, char *line)
{
if (*line == '#')
{
+ int err = VLC_SUCCESS;
if (strncmp(line, "#EXT-X-TARGETDURATION", 21) == 0)
- parse_TargetDuration(s, hls, line);
- else if (strncmp(line, "#EXT-X-MEDIA-SEQUENCE", 22) == 0)
- parse_MediaSequence(s, hls, line);
- else if (strncmp(line, "#EXT-X-KEY", 11) == 0)
- parse_Key(s, hls, line);
- else if (strncmp(line, "#EXT-X-PROGRAM-DATE-TIME", 25) == 0)
- parse_ProgramDateTime(s, hls, line);
- else if (strncmp(line, "#EXT-X-ALLOW-CACHE", 17) == 0)
- parse_AllowCache(s, hls, line);
+ err = parse_TargetDuration(s, hls, line);
+ else if (strncmp(line, "#EXT-X-MEDIA-SEQUENCE", 21) == 0)
+ err = parse_MediaSequence(s, hls, line);
+ else if (strncmp(line, "#EXT-X-KEY", 10) == 0)
+ err = parse_Key(s, hls, line);
+ else if (strncmp(line, "#EXT-X-PROGRAM-DATE-TIME", 24) == 0)
+ err = parse_ProgramDateTime(s, hls, line);
+ else if (strncmp(line, "#EXT-X-ALLOW-CACHE", 18) == 0)
+ err = parse_AllowCache(s, hls, line);
else if (strncmp(line, "#EXT-X-DISCONTINUITY", 20) == 0)
- parse_Discontinuity(s, hls, line);
+ err = parse_Discontinuity(s, hls, line);
else if (strncmp(line, "#EXT-X-VERSION", 14) == 0)
- parse_Version(s, hls, line);
+ err = parse_Version(s, hls, line);
else if (strncmp(line, "#EXT-X-ENDLIST", 14) == 0)
- parse_EndList(s, hls);
+ err = parse_EndList(s, hls);
+
+ if (err != VLC_SUCCESS)
+ s->p_sys->b_error = true;
}
}
AccessClose(s);
return VLC_SUCCESS;
+error:
+ free(line);
+ free(tmp);
+ AccessClose(s);
+ return VLC_EGENERIC;
+}
+
+static int get_HTTPLiveMetaPlaylist(stream_t *s, vlc_array_t **streams)
+{
+ stream_sys_t *p_sys = s->p_sys;
+ assert(*streams);
+
+ /* Download new playlist file from server */
+ if (AccessOpen(s, &p_sys->m3u8) != VLC_SUCCESS)
+ return VLC_EGENERIC;
+
+ /* Parse the rest of the reply */
+ uint8_t *tmp = calloc(1, HTTPLIVE_MAX_LINE);
+ if (tmp == NULL)
+ {
+ AccessClose(s);
+ return VLC_ENOMEM;
+ }
+
+ char *line = AccessReadLine(p_sys->p_access, tmp, HTTPLIVE_MAX_LINE);
+ if (strncmp(line, "#EXTM3U", 7) != 0)
+ {
+ msg_Err(s, "missing #EXTM3U tag");
+ goto error;
+ }
+ free(line);
+ line = NULL;
+
+ for( ; ; )
+ {
+ line = AccessReadLine(p_sys->p_access, tmp, HTTPLIVE_MAX_LINE);
+ if (line == NULL)
+ {
+ msg_Dbg(s, "end of data");
+ break;
+ }
+
+ if (!vlc_object_alive(s))
+ goto error;
+
+ /* some more checks for actual data */
+ if (strncmp(line, "#EXT-X-STREAM-INF", 17) == 0)
+ {
+ p_sys->b_meta = true;
+ char *uri = AccessReadLine(p_sys->p_access, tmp, HTTPLIVE_MAX_LINE);
+ if (uri == NULL)
+ p_sys->b_error = true;
+ else
+ {
+ parse_StreamInformation(s, streams, line, uri);
+ free(uri);
+ }
+ }
+ else if (strncmp(line, "#EXTINF", 7) == 0)
+ {
+ char *uri = AccessReadLine(p_sys->p_access, tmp, HTTPLIVE_MAX_LINE);
+ if (uri == NULL)
+ p_sys->b_error = true;
+ else
+ {
+ hls_stream_t *hls = hls_GetLast(*streams);
+ if (hls)
+ parse_SegmentInformation(s, hls, line, uri);
+ else
+ p_sys->b_error = true;
+ free(uri);
+ }
+ }
+ else
+ {
+ hls_stream_t *hls = hls_GetLast(*streams);
+ if ((hls == NULL) && (!p_sys->b_meta))
+ {
+ hls = hls_New(*streams, -1, -1, NULL);
+ if (hls == NULL)
+ {
+ p_sys->b_error = true;
+ return VLC_ENOMEM;
+ }
+ }
+ parse_M3U8ExtLine(s, hls, line);
+ }
+
+ /* Error during m3u8 parsing abort */
+ if (p_sys->b_error)
+ goto error;
+
+ free(line);
+ }
+
+ free(line);
+ free(tmp);
+ AccessClose(s);
+ return VLC_SUCCESS;
+
error:
free(line);
free(tmp);
p_sys->b_error = true;
else
{
- parse_StreamInformation(s, p_read, uri);
+ parse_StreamInformation(s, &p_sys->hls_stream, p_read, uri);
free(uri);
}
}
return VLC_EGENERIC;
}
}
+ else
+ {
+ /* Stream size (approximate) */
+ hls->size = hls_GetStreamSize(hls);
+ }
+ vlc_mutex_unlock(&hls->lock);
+ }
- /* Stream size (approximate) */
- hls->size = hls_GetStreamSize(hls);
+ return VLC_SUCCESS;
+}
- /* Can we cache files after playback */
- p_sys->b_cache = hls->b_cache;
+/* Reload playlist */
+static int hls_UpdatePlaylist(stream_t *s, hls_stream_t *hls_new, hls_stream_t **hls)
+{
+ int count = vlc_array_count(hls_new->segments);
- vlc_mutex_unlock(&hls->lock);
+ msg_Info(s, "updating hls stream (program-id=%d, bandwidth=%"PRIu64") has %d segments",
+ hls_new->id, hls_new->bandwidth, count);
+ for (int n = 0; n < count; n++)
+ {
+ segment_t *p = segment_GetSegment(hls_new, n);
+ if (p == NULL) return VLC_EGENERIC;
+
+ vlc_mutex_lock(&(*hls)->lock);
+ segment_t *segment = segment_Find(*hls, p->sequence);
+ if (segment)
+ {
+ /* they should be the same */
+ if ((p->sequence != segment->sequence) ||
+ (p->duration != segment->duration) ||
+ (strcmp(p->url.psz_path, segment->url.psz_path) != 0))
+ {
+ msg_Err(s, "existing segment %d found with different content",
+ p->sequence);
+ msg_Err(s, "- sequence: new=%d, old=%d", p->sequence, segment->sequence);
+ msg_Err(s, "- duration: new=%d, old=%d", p->duration, segment->duration);
+ msg_Err(s, "- file: new=%s, old=%s", p->url.psz_path, segment->url.psz_path);
+ }
+ }
+ else
+ {
+ int last = vlc_array_count((*hls)->segments) - 1;
+ segment_t *l = segment_GetSegment(*hls, last);
+ if (l == NULL) goto fail_and_unlock;
+
+ if ((l->sequence + 1) == p->sequence)
+ {
+ vlc_array_append((*hls)->segments, p);
+ msg_Info(s, "- segment %d appended", p->sequence);
+ }
+ else /* there is a gap */
+ {
+ msg_Err(s, "gap in sequence numbers found: new=%d expected old=%d",
+ p->sequence, l->sequence);
+ goto fail_and_unlock;
+ }
+ }
+ vlc_mutex_unlock(&(*hls)->lock);
+ }
+ return VLC_SUCCESS;
+
+fail_and_unlock:
+ vlc_mutex_unlock(&(*hls)->lock);
+ return VLC_EGENERIC;
+}
+
+static int hls_ReloadPlaylist(stream_t *s)
+{
+ stream_sys_t *p_sys = s->p_sys;
+
+ vlc_array_t *hls_streams = vlc_array_new();
+ if (hls_streams == NULL)
+ return VLC_ENOMEM;
+
+ msg_Info(s, "Reloading HLS live meta playlist");
+ if (get_HTTPLiveMetaPlaylist(s, &hls_streams) != VLC_SUCCESS)
+ goto fail;
+
+ int count = vlc_array_count(hls_streams);
+
+ /* Is it a meta playlist? */
+ if (p_sys->b_meta)
+ {
+ for (int n = 0; n < count; n++)
+ {
+ hls_stream_t *hls = hls_Get(hls_streams, n);
+ if (hls == NULL) goto fail;
+
+ msg_Info(s, "parsing %s", hls->url.psz_path);
+ if (get_HTTPLivePlaylist(s, hls) != VLC_SUCCESS)
+ {
+ msg_Err(s, "could not parse playlist file from meta index." );
+ goto fail;
+ }
+ }
}
+ /* merge playlists */
+ for (int n = 0; n < count; n++)
+ {
+ hls_stream_t *hls_new = hls_Get(hls_streams, n);
+ if (hls_new == NULL) goto fail;
+
+ hls_stream_t *hls_old = hls_Find(p_sys->hls_stream, hls_new);
+ if (hls_old == NULL)
+ { /* new hls stream - append */
+ vlc_array_append(p_sys->hls_stream, hls_new);
+ msg_Info(s, "new HLS stream appended (id=%d, bandwidth=%"PRIu64")",
+ hls_new->id, hls_new->bandwidth);
+ }
+ else if (hls_UpdatePlaylist(s, hls_new, &hls_old) != VLC_SUCCESS)
+ goto fail;
+ }
+
+ vlc_array_destroy(hls_streams);
return VLC_SUCCESS;
+
+fail:
+ msg_Err(s, "reloading playlist failed");
+ vlc_array_destroy(hls_streams);
+ return VLC_EGENERIC;
}
/****************************************************************************
while (vlc_object_alive(p_this))
{
- hls_stream_t *hls = hls_Get(client->hls_stream, client->current);
+ hls_stream_t *hls = hls_Get(p_sys->hls_stream, p_sys->download.current);
assert(hls);
/* Sliding window (~60 seconds worth of movie) */
vlc_mutex_unlock(&hls->lock);
/* Is there a new segment to process? */
- if ((p_sys->playback.segment < (count - 6)) ||
- (client->segment >= count))
+ if ((!p_sys->b_live && (p_sys->playback.segment < (count - 6))) ||
+ (p_sys->download.segment >= count))
{
/* wait */
- vlc_mutex_lock(&client->lock_wait);
- while (((client->segment - p_sys->playback.segment > 6) ||
- (client->segment >= count)) &&
- (client->seek == -1))
+ vlc_mutex_lock(&p_sys->download.lock_wait);
+ while (((p_sys->download.segment - p_sys->playback.segment > 6) ||
+ (p_sys->download.segment >= count)) &&
+ (p_sys->download.seek == -1) &&
+ (mdate() < p_sys->playlist.wakeup))
{
- vlc_cond_wait(&client->wait, &client->lock_wait);
+ vlc_cond_wait(&p_sys->download.wait, &p_sys->download.lock_wait);
if (!vlc_object_alive(p_this)) break;
}
/* */
- if (client->seek >= 0)
+ if (p_sys->download.seek >= 0)
{
- client->segment = client->seek;
- client->seek = -1;
+ p_sys->download.segment = p_sys->download.seek;
+ p_sys->download.seek = -1;
}
- vlc_mutex_unlock(&client->lock_wait);
+ vlc_mutex_unlock(&p_sys->download.lock_wait);
}
if (!vlc_object_alive(p_this)) break;
- vlc_mutex_lock(&hls->lock);
- segment_t *segment = segment_GetSegment(hls, client->segment);
- assert(segment);
- vlc_mutex_unlock(&hls->lock);
-
- if (Download(client->s, hls, segment, &client->current) != VLC_SUCCESS)
- {
- if (!p_sys->b_live)
- {
- p_sys->b_error = true;
- break;
- }
- }
-
- /* download succeeded */
- /* determine next segment to download */
- vlc_mutex_lock(&client->lock_wait);
- if (client->seek >= 0)
- {
- client->segment = client->seek;
- client->seek = -1;
- }
- else if (client->segment < count)
- client->segment++;
- vlc_cond_signal(&client->wait);
- vlc_mutex_unlock(&client->lock_wait);
-
- /* FIXME: Reread the m3u8 index file */
+ /* reload the m3u8 index file */
if (p_sys->b_live)
{
double wait = 1;
mtime_t now = mdate();
if (now >= p_sys->playlist.wakeup)
{
-#if 0
- /** FIXME: Implement m3u8 playlist reloading */
- if (!hls_ReloadPlaylist(client->s))
+ if (hls_ReloadPlaylist(client->s) != VLC_SUCCESS)
{
/* No change in playlist, then backoff */
p_sys->playlist.tries++;
else if (p_sys->playlist.tries == 2) wait = 1;
else if (p_sys->playlist.tries >= 3) wait = 3;
}
-#endif
+ else p_sys->playlist.tries = 0;
+
/* determine next time to update playlist */
p_sys->playlist.last = now;
- p_sys->playlist.wakeup = now + ((mtime_t)(hls->duration * wait) * (mtime_t)1000000);
+ p_sys->playlist.wakeup = now + ((mtime_t)(hls->duration * wait)
+ * (mtime_t)1000000);
}
+
+ if (!vlc_object_alive(p_this)) break;
+ }
+
+ vlc_mutex_lock(&hls->lock);
+ segment_t *segment = segment_GetSegment(hls, p_sys->download.segment);
+ assert(segment);
+ vlc_mutex_unlock(&hls->lock);
+
+ if (Download(client->s, hls, segment, &p_sys->download.current) != VLC_SUCCESS)
+ {
+ if (!vlc_object_alive(p_this)) break;
+
+ if (!p_sys->b_live)
+ {
+ p_sys->b_error = true;
+ break;
+ }
+ }
+
+ /* download succeeded */
+ /* determine next segment to download */
+ vlc_mutex_lock(&p_sys->download.lock_wait);
+ if (p_sys->download.seek >= 0)
+ {
+ p_sys->download.segment = p_sys->download.seek;
+ p_sys->download.seek = -1;
}
+ else if (p_sys->download.segment < count)
+ p_sys->download.segment++;
+ vlc_cond_signal(&p_sys->download.wait);
+ vlc_mutex_unlock(&p_sys->download.lock_wait);
}
vlc_restorecancel(canc);
free(p_sys);
return VLC_ENOMEM;
}
- char *psz_path = strrchr(psz_uri, '/');
- if (psz_path) *psz_path = '\0';
vlc_UrlParse(&p_sys->m3u8, psz_uri, 0);
free(psz_uri);
((mtime_t)hls->duration * UINT64_C(1000000));
}
- p_sys->thread->hls_stream = p_sys->hls_stream;
- p_sys->thread->current = current;
+ p_sys->download.current = current;
p_sys->playback.current = current;
- p_sys->thread->segment = p_sys->playback.segment;
- p_sys->thread->seek = -1;
+ p_sys->download.segment = p_sys->playback.segment;
+ p_sys->download.seek = -1;
p_sys->playback.segment = 0; /* reset to first segment */
p_sys->thread->s = s;
- vlc_mutex_init(&p_sys->thread->lock_wait);
- vlc_cond_init(&p_sys->thread->wait);
+ vlc_mutex_init(&p_sys->download.lock_wait);
+ vlc_cond_init(&p_sys->download.wait);
if (vlc_thread_create(p_sys->thread, "HTTP Live Streaming client",
hls_Thread, VLC_THREAD_PRIORITY_INPUT))
/* */
if (p_sys->thread)
{
- vlc_mutex_lock(&p_sys->thread->lock_wait);
+ vlc_mutex_lock(&p_sys->download.lock_wait);
vlc_object_kill(p_sys->thread);
- vlc_cond_signal(&p_sys->thread->wait);
- vlc_mutex_unlock(&p_sys->thread->lock_wait);
+ vlc_cond_signal(&p_sys->download.wait);
+ vlc_mutex_unlock(&p_sys->download.lock_wait);
/* */
vlc_thread_join(p_sys->thread);
- vlc_mutex_destroy(&p_sys->thread->lock_wait);
- vlc_cond_destroy(&p_sys->thread->wait);
+ vlc_mutex_destroy(&p_sys->download.lock_wait);
+ vlc_cond_destroy(&p_sys->download.wait);
vlc_object_release(p_sys->thread);
}
/* This segment is ready? */
if (segment->data != NULL)
{
+ p_sys->b_cache = hls->b_cache;
vlc_mutex_unlock(&hls->lock);
- return segment;
+ goto check;
}
}
vlc_mutex_unlock(&hls->lock);
break;
}
- vlc_mutex_lock(&p_sys->thread->lock_wait);
- int i_segment = p_sys->thread->segment;
- vlc_mutex_unlock(&p_sys->thread->lock_wait);
+ vlc_mutex_lock(&p_sys->download.lock_wait);
+ int i_segment = p_sys->download.segment;
+ vlc_mutex_unlock(&p_sys->download.lock_wait);
/* This segment is ready? */
if ((segment->data != NULL) &&
(p_sys->playback.segment < i_segment))
{
p_sys->playback.current = i_stream;
+ p_sys->b_cache = hls->b_cache;
vlc_mutex_unlock(&hls->lock);
- return segment;
+ goto check;
}
vlc_mutex_unlock(&hls->lock);
}
/* */
return NULL;
+
+check:
+ /* sanity check */
+ if (p_sys->download.segment - p_sys->playback.segment == 0)
+ msg_Err(s, "playback will stall");
+ else if (p_sys->download.segment - p_sys->playback.segment < 3)
+ msg_Warn(s, "playback in danger of stalling");
+ return segment;
}
static ssize_t hls_Read(stream_t *s, uint8_t *p_read, unsigned int i_read)
vlc_mutex_lock(&segment->lock);
if (segment->data->i_buffer == 0)
{
- if (!p_sys->b_cache)
+ if (!p_sys->b_cache || p_sys->b_live)
{
block_Release(segment->data);
segment->data = NULL;
vlc_mutex_unlock(&segment->lock);
/* signal download thread */
- vlc_mutex_lock(&p_sys->thread->lock_wait);
- vlc_cond_signal(&p_sys->thread->wait);
- vlc_mutex_unlock(&p_sys->thread->lock_wait);
+ vlc_mutex_lock(&p_sys->download.lock_wait);
+ vlc_cond_signal(&p_sys->download.wait);
+ vlc_mutex_unlock(&p_sys->download.lock_wait);
continue;
}
if (segment->size == segment->data->i_buffer)
msg_Info(s, "playing segment %d from stream %d",
- p_sys->playback.segment, p_sys->playback.current);
+ segment->sequence, p_sys->playback.current);
ssize_t len = -1;
if (i_read <= segment->data->i_buffer)
{
stream_sys_t *p_sys = s->p_sys;
- if ((p_sys->hls_stream == NULL) ||
- (p_sys->thread == NULL))
+ if (p_sys->hls_stream == NULL)
return false;
hls_stream_t *hls = hls_Get(p_sys->hls_stream, p_sys->playback.current);
int count = vlc_array_count(hls->segments);
vlc_mutex_unlock(&hls->lock);
- vlc_mutex_lock(&p_sys->thread->lock_wait);
- bool may_seek = (p_sys->thread->segment < (count - 2));
- vlc_mutex_unlock(&p_sys->thread->lock_wait);
+ vlc_mutex_lock(&p_sys->download.lock_wait);
+ bool may_seek = (p_sys->download.segment < (count - 2));
+ vlc_mutex_unlock(&p_sys->download.lock_wait);
return may_seek;
}
return true;
vlc_mutex_unlock(&segment->lock);
/* start download at current playback segment */
- if (p_sys->thread)
- {
- vlc_mutex_unlock(&hls->lock);
-
- /* Wake up download thread */
- vlc_mutex_lock(&p_sys->thread->lock_wait);
- p_sys->thread->seek = p_sys->playback.segment;
- vlc_cond_signal(&p_sys->thread->wait);
- vlc_mutex_unlock(&p_sys->thread->lock_wait);
-
- /* Wait for download to be finished */
- vlc_mutex_lock(&p_sys->thread->lock_wait);
- msg_Info(s, "seek to segment %d", p_sys->playback.segment);
- while (((p_sys->thread->seek != -1) ||
- (p_sys->thread->segment - p_sys->playback.segment < 3)) &&
- (p_sys->thread->segment < (count - 6)))
- {
- vlc_cond_wait(&p_sys->thread->wait, &p_sys->thread->lock_wait);
- if (!vlc_object_alive(s) || s->b_error) break;
- }
- vlc_mutex_unlock(&p_sys->thread->lock_wait);
+ vlc_mutex_unlock(&hls->lock);
- return VLC_SUCCESS;
+ /* Wake up download thread */
+ vlc_mutex_lock(&p_sys->download.lock_wait);
+ p_sys->download.seek = p_sys->playback.segment;
+ vlc_cond_signal(&p_sys->download.wait);
+ vlc_mutex_unlock(&p_sys->download.lock_wait);
+
+ /* Wait for download to be finished */
+ vlc_mutex_lock(&p_sys->download.lock_wait);
+ msg_Info(s, "seek to segment %d", p_sys->playback.segment);
+ while (((p_sys->download.seek != -1) ||
+ (p_sys->download.segment - p_sys->playback.segment < 3)) &&
+ (p_sys->download.segment < (count - 6)))
+ {
+ vlc_cond_wait(&p_sys->download.wait, &p_sys->download.lock_wait);
+ if (!vlc_object_alive(s) || s->b_error) break;
}
+ vlc_mutex_unlock(&p_sys->download.lock_wait);
+
+ return VLC_SUCCESS;
}
vlc_mutex_unlock(&hls->lock);