X-Git-Url: https://git.sesse.net/?a=blobdiff_plain;f=modules%2Fstream_filter%2Fhttplive.c;h=299c23b1100b983d21f6ff3c825332de4786fbb6;hb=90df055fda7d7f2edbccf4a8b3e3ab32b67c2396;hp=cc3fe48c755b392037599427fd59332efe132d1a;hpb=fba893a1a379627546a2adccc75d77e409c694dc;p=vlc diff --git a/modules/stream_filter/httplive.c b/modules/stream_filter/httplive.c index cc3fe48c75..299c23b110 100644 --- a/modules/stream_filter/httplive.c +++ b/modules/stream_filter/httplive.c @@ -92,6 +92,8 @@ struct stream_sys_t vlc_thread_t reload; /* HLS m3u8 reload thread */ vlc_thread_t thread; /* HLS segment download thread */ + block_t *peeked; + /* */ vlc_array_t *hls_stream; /* bandwidth adaptation */ uint64_t bandwidth; /* measured bandwidth (bits per second) */ @@ -148,6 +150,8 @@ static void* hls_Reload(void *); static segment_t *segment_GetSegment(hls_stream_t *hls, int wanted); static void segment_Free(segment_t *segment); +static char *ConstructUrl(vlc_url_t *url); + /**************************************************************************** * ****************************************************************************/ @@ -233,6 +237,34 @@ static void hls_Free(hls_stream_t *hls) hls = NULL; } +static hls_stream_t *hls_Copy(hls_stream_t *src, const bool b_cp_segments) +{ + assert(src); + assert(!b_cp_segments); /* FIXME: copying segments is not implemented */ + + hls_stream_t *dst = (hls_stream_t *)malloc(sizeof(hls_stream_t)); + if (dst == NULL) return NULL; + + dst->id = src->id; + dst->bandwidth = src->bandwidth; + dst->duration = src->duration; + dst->size = src->size; + dst->sequence = src->sequence; + dst->version = src->version; + dst->b_cache = src->b_cache; + char *uri = ConstructUrl(&src->url); + if (uri == NULL) + { + free(dst); + return NULL; + } + vlc_UrlParse(&dst->url, uri, 0); + if (!b_cp_segments) + dst->segments = vlc_array_new(); + vlc_mutex_init(&dst->lock); + return dst; +} + static hls_stream_t *hls_Get(vlc_array_t *hls_stream, const int wanted) { int count = vlc_array_count(hls_stream); @@ -356,7 +388,7 @@ static int ChooseSegment(stream_t *s, const int current) hls_stream_t *hls = hls_Get(p_sys->hls_stream, current); if (hls == NULL) return 0; - /* Choose a segment to start which is no closer then + /* Choose a segment to start which is no closer than * 3 times the target duration from the end of the playlist. */ int wanted = 0; @@ -372,7 +404,7 @@ static int ChooseSegment(stream_t *s, const int current) if (segment->duration > hls->duration) { - msg_Err(s, "EXTINF:%d duration is larger then EXT-X-TARGETDURATION:%d", + msg_Err(s, "EXTINF:%d duration is larger than EXT-X-TARGETDURATION:%d", segment->duration, hls->duration); } @@ -624,7 +656,7 @@ static int parse_StreamInformation(stream_t *s, vlc_array_t **hls_stream, return VLC_EGENERIC; } - msg_Info(s, "bandwidth adaption detected (program-id=%d, bandwidth=%"PRIu64").", id, bw); + msg_Info(s, "bandwidth adaptation detected (program-id=%d, bandwidth=%"PRIu64").", id, bw); char *psz_uri = relative_URI(s, uri, NULL); @@ -963,17 +995,43 @@ static int get_HTTPLiveMetaPlaylist(stream_t *s, vlc_array_t **streams) { stream_sys_t *p_sys = s->p_sys; assert(*streams); + int err = VLC_EGENERIC; - /* Download new playlist file from server */ - uint8_t *buffer = NULL; - ssize_t len = read_M3U8_from_url(s, &p_sys->m3u8, &buffer); - if (len < 0) - return VLC_EGENERIC; + /* Duplicate HLS stream META information */ + for (int i = 0; i < vlc_array_count(p_sys->hls_stream); i++) + { + hls_stream_t *src, *dst; + src = (hls_stream_t *)vlc_array_item_at_index(p_sys->hls_stream, i); + if (src == NULL) + return VLC_EGENERIC; - /* Parse HLS m3u8 content. */ - int err = parse_M3U8(s, *streams, buffer, len); - free(buffer); + dst = hls_Copy(src, false); + if (dst == NULL) + return VLC_ENOMEM; + vlc_array_append(*streams, dst); + } + + /* Download new playlist file from server */ + for (int i = 0; i < vlc_array_count(*streams); i++) + { + hls_stream_t *hls; + hls = (hls_stream_t *)vlc_array_item_at_index(*streams, i); + if (hls == NULL) + return VLC_EGENERIC; + + /* Download playlist file from server */ + uint8_t *buf = NULL; + ssize_t len = read_M3U8_from_url(s, &hls->url, &buf); + if (len < 0) + err = VLC_EGENERIC; + else + { + /* Parse HLS m3u8 content. */ + err = parse_M3U8(s, *streams, buf, len); + free(buf); + } + } return err; } @@ -984,6 +1042,7 @@ static int hls_UpdatePlaylist(stream_t *s, hls_stream_t *hls_new, hls_stream_t * 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); @@ -993,23 +1052,34 @@ static int hls_UpdatePlaylist(stream_t *s, hls_stream_t *hls_new, hls_stream_t * segment_t *segment = segment_Find(*hls, p->sequence); if (segment) { + assert(p->url.psz_path); + assert(segment->url.psz_path); + /* 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 found with different content - resetting"); - 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", p->url.psz_path); - msg_Err(s, " old=%s", segment->url.psz_path); + msg_Warn(s, "existing segment found with different content - resetting"); + msg_Warn(s, "- sequence: new=%d, old=%d", p->sequence, segment->sequence); + msg_Warn(s, "- duration: new=%d, old=%d", p->duration, segment->duration); + msg_Warn(s, "- file: new=%s", p->url.psz_path); + msg_Warn(s, " old=%s", segment->url.psz_path); /* Resetting content */ + char *psz_url = ConstructUrl(&p->url); + if (psz_url == NULL) + { + msg_Err(s, "Failed updating segment %d - skipping it", p->sequence); + segment_Free(p); + continue; + } segment->sequence = p->sequence; segment->duration = p->duration; vlc_UrlClean(&segment->url); - vlc_UrlParse(&segment->url, p->url.psz_buffer, 0); + vlc_UrlParse(&segment->url, psz_url, 0); segment_Free(p); + free(psz_url); } } else @@ -1031,6 +1101,7 @@ static int hls_UpdatePlaylist(stream_t *s, hls_stream_t *hls_new, hls_stream_t * return VLC_SUCCESS; fail_and_unlock: + assert(0); vlc_mutex_unlock(&(*hls)->lock); return VLC_EGENERIC; } @@ -1079,7 +1150,6 @@ static int hls_ReloadPlaylist(stream_t *s) msg_Info(s, "failed updating HLS stream (id=%d, bandwidth=%"PRIu64")", hls_new->id, hls_new->bandwidth); } - vlc_array_destroy(hls_streams); return VLC_SUCCESS; } @@ -1139,7 +1209,7 @@ static int Download(stream_t *s, hls_stream_t *hls, segment_t *segment, int *cur int estimated = (int)(size / p_sys->bandwidth); if (estimated > segment->duration) { - msg_Warn(s,"downloading of segment %d takes %ds, which is longer then its playback (%ds)", + msg_Warn(s,"downloading of segment %d takes %ds, which is longer than its playback (%ds)", segment->sequence, estimated, segment->duration); } } @@ -1147,6 +1217,8 @@ static int Download(stream_t *s, hls_stream_t *hls, segment_t *segment, int *cur mtime_t start = mdate(); if (hls_Download(s, segment) != VLC_SUCCESS) { + msg_Err(s, "downloaded segment %d from stream %d failed", + segment->sequence, *cur_stream); vlc_mutex_unlock(&segment->lock); return VLC_EGENERIC; } @@ -1266,9 +1338,9 @@ static void* hls_Reload(void *p_this) int canc = vlc_savecancel(); + double wait = 0.5; while (vlc_object_alive(s)) { - double wait = 1; mtime_t now = mdate(); if (now >= p_sys->playlist.wakeup) { @@ -1279,9 +1351,20 @@ static void* hls_Reload(void *p_this) p_sys->playlist.tries++; if (p_sys->playlist.tries == 1) wait = 0.5; else if (p_sys->playlist.tries == 2) wait = 1; - else if (p_sys->playlist.tries >= 3) wait = 3; + else if (p_sys->playlist.tries >= 3) wait = 2; + + /* Can we afford to backoff? */ + if (p_sys->download.segment - p_sys->playback.segment < 3) + { + p_sys->playlist.tries = 0; + wait = 0.5; + } + } + else + { + p_sys->playlist.tries = 0; + wait = 0.5; } - else p_sys->playlist.tries = 0; hls_stream_t *hls = hls_Get(p_sys->hls_stream, p_sys->download.stream); assert(hls); @@ -1393,14 +1476,18 @@ static int hls_Download(stream_t *s, segment_t *segment) if (size > segment->size) { msg_Dbg(s, "size changed %"PRIu64, segment->size); - segment->data = block_Realloc(segment->data, 0, size); - if (segment->data == NULL) + block_t *p_block = block_Realloc(segment->data, 0, size); + if (p_block == NULL) { stream_Delete(p_ts); + block_Release(segment->data); + segment->data = NULL; return VLC_ENOMEM; } + segment->data = p_block; segment->size = size; assert(segment->data->i_buffer == segment->size); + p_block = NULL; } length = stream_Read(p_ts, segment->data->p_buffer + curlen, segment->size - curlen); if (length <= 0) @@ -1569,7 +1656,7 @@ static int Open(vlc_object_t *p_this) if (p_sys->b_live && (p_sys->playback.segment < 0)) { - msg_Warn(s, "less data then 3 times 'target duration' available for live playback, playback may stall"); + msg_Warn(s, "less data than 3 times 'target duration' available for live playback, playback may stall"); } if (Prefetch(s, ¤t) != VLC_SUCCESS) @@ -1662,6 +1749,8 @@ static void Close(vlc_object_t *p_this) /* */ vlc_UrlClean(&p_sys->m3u8); + if (p_sys->peeked) + block_Release (p_sys->peeked); free(p_sys); } @@ -1850,10 +1939,9 @@ static int Read(stream_t *s, void *buffer, unsigned int i_read) static int Peek(stream_t *s, const uint8_t **pp_peek, unsigned int i_peek) { stream_sys_t *p_sys = s->p_sys; - size_t curlen = 0; segment_t *segment; + unsigned int len = i_peek; -again: segment = GetSegment(s); if (segment == NULL) { @@ -1864,29 +1952,77 @@ again: vlc_mutex_lock(&segment->lock); - /* remember segment to peek */ - int peek_segment = p_sys->playback.segment; - do + size_t i_buff = segment->data->i_buffer; + uint8_t *p_buff = segment->data->p_buffer; + + if (i_peek < i_buff) { - if (i_peek < segment->data->i_buffer) - { - *pp_peek = segment->data->p_buffer; - curlen += i_peek; - } - else + *pp_peek = p_buff; + vlc_mutex_unlock(&segment->lock); + return i_peek; + } + + else /* This will seldom be run */ + { + /* remember segment to read */ + int peek_segment = p_sys->playback.segment; + size_t curlen = 0; + segment_t *nsegment; + p_sys->playback.segment++; + block_t *peeked = p_sys->peeked; + + if (peeked == NULL) + peeked = block_Alloc (i_peek); + else if (peeked->i_buffer < i_peek) + peeked = block_Realloc (peeked, 0, i_peek); + if (peeked == NULL) + return 0; + + memcpy(peeked->p_buffer, p_buff, i_buff); + curlen = i_buff; + len -= i_buff; + vlc_mutex_unlock(&segment->lock); + + i_buff = peeked->i_buffer; + p_buff = peeked->p_buffer; + *pp_peek = p_buff; + + while ((curlen < i_peek) && vlc_object_alive(s)) { - p_sys->playback.segment++; - vlc_mutex_unlock(&segment->lock); - goto again; - } - } while ((curlen < i_peek) && vlc_object_alive(s)); + nsegment = GetSegment(s); + if (nsegment == NULL) + { + msg_Err(s, "segment %d should have been available (stream %d)", + p_sys->playback.segment, p_sys->playback.stream); + /* restore segment to read */ + p_sys->playback.segment = peek_segment; + return curlen; /* eof? */ + } - /* restore segment to read */ - p_sys->playback.segment = peek_segment; + vlc_mutex_lock(&nsegment->lock); - vlc_mutex_unlock(&segment->lock); + if (len < nsegment->data->i_buffer) + { + memcpy(p_buff + curlen, nsegment->data->p_buffer, len); + curlen += len; + } + else + { + size_t i_nbuff = nsegment->data->i_buffer; + memcpy(p_buff + curlen, nsegment->data->p_buffer, i_nbuff); + curlen += i_nbuff; + len -= i_nbuff; + + p_sys->playback.segment++; + } + + vlc_mutex_unlock(&nsegment->lock); + } - return curlen; + /* restore segment to read */ + p_sys->playback.segment = peek_segment; + return curlen; + } } static bool hls_MaySeek(stream_t *s) @@ -2037,7 +2173,6 @@ static int Control(stream_t *s, int i_query, va_list args) switch (i_query) { case STREAM_CAN_SEEK: - case STREAM_CAN_FASTSEEK: *(va_arg (args, bool *)) = hls_MaySeek(s); break; case STREAM_GET_POSITION: