[vlc-commits] Bug fix: HLS module does not block until data is available. As a result live streams may end unexpectedly in case of short playlists or slow connections. Problem was fixed by blocking on a condition variable in the "read" function in case data is not available until Download thread signals ( when new data is available) or a timeout of 10 seconds is reached. Blocking is done with a timed wait in order to avoid deadlocks since the thread that calls read is also responsible for calling close.
Avishay Spitzer
git at videolan.org
Tue Aug 27 14:41:57 CEST 2013
vlc/vlc-2.1 | branch: master | Avishay Spitzer <savishay at gmail.com> | Mon Aug 19 03:08:33 2013 -0400| [4b12eb0542373e465ae752cd0e8c5d6845e5e4af] | committer: Jean-Baptiste Kempf
Bug fix: HLS module does not block until data is available. As a result live streams may end unexpectedly in case of short playlists or slow connections. Problem was fixed by blocking on a condition variable in the "read" function in case data is not available until Download thread signals (when new data is available) or a timeout of 10 seconds is reached. Blocking is done with a timed wait in order to avoid deadlocks since the thread that calls read is also responsible for calling close.
Signed-off-by: Ilkka Ollakka <ileoo at videolan.org>
(cherry picked from commit 50d7d0c9c470ccec4d48212221aaecd822928c96)
Signed-off-by: Jean-Baptiste Kempf <jb at videolan.org>
> http://git.videolan.org/gitweb.cgi/vlc/vlc-2.1.git/?a=commit;h=4b12eb0542373e465ae752cd0e8c5d6845e5e4af
---
modules/stream_filter/httplive.c | 74 ++++++++++++++++++++++++++++++++++----
1 file changed, 68 insertions(+), 6 deletions(-)
diff --git a/modules/stream_filter/httplive.c b/modules/stream_filter/httplive.c
index 3424c13..ae191f4 100644
--- a/modules/stream_filter/httplive.c
+++ b/modules/stream_filter/httplive.c
@@ -135,6 +135,12 @@ struct stream_sys_t
int tries; /* times it was not changed */
} playlist;
+ struct hls_read_s
+ {
+ vlc_mutex_t lock_wait; /* used by read condition variable */
+ vlc_cond_t wait; /* some condition to wait on during read */
+ } read;
+
/* state */
bool b_cache; /* can cache files */
bool b_meta; /* meta playlist */
@@ -1674,6 +1680,11 @@ static void* hls_Thread(void *p_this)
p_sys->download.segment++;
vlc_cond_signal(&p_sys->download.wait);
vlc_mutex_unlock(&p_sys->download.lock_wait);
+
+ // In case of a successful download signal the read thread that data is available
+ vlc_mutex_lock(&p_sys->read.lock_wait);
+ vlc_cond_signal(&p_sys->read.wait);
+ vlc_mutex_unlock(&p_sys->read.lock_wait);
}
vlc_restorecancel(canc);
@@ -2031,6 +2042,9 @@ static int Open(vlc_object_t *p_this)
vlc_mutex_init(&p_sys->download.lock_wait);
vlc_cond_init(&p_sys->download.wait);
+ vlc_mutex_init(&p_sys->read.lock_wait);
+ vlc_cond_init(&p_sys->read.wait);
+
/* Initialize HLS live stream */
if (p_sys->b_live)
{
@@ -2058,6 +2072,9 @@ fail_thread:
vlc_mutex_destroy(&p_sys->download.lock_wait);
vlc_cond_destroy(&p_sys->download.wait);
+ vlc_mutex_destroy(&p_sys->read.lock_wait);
+ vlc_cond_destroy(&p_sys->read.wait);
+
fail:
/* Free hls streams */
for (int i = 0; i < vlc_array_count(p_sys->hls_stream); i++)
@@ -2098,6 +2115,9 @@ static void Close(vlc_object_t *p_this)
vlc_mutex_destroy(&p_sys->download.lock_wait);
vlc_cond_destroy(&p_sys->download.wait);
+ vlc_mutex_destroy(&p_sys->read.lock_wait);
+ vlc_cond_destroy(&p_sys->read.wait);
+
/* Free hls streams */
for (int i = 0; i < vlc_array_count(p_sys->hls_stream); i++)
{
@@ -2286,13 +2306,55 @@ static int Read(stream_t *s, void *buffer, unsigned int i_read)
assert(p_sys->hls_stream);
- if (p_sys->b_error)
- return 0;
+ while (length == 0)
+ {
+ // In case an error occurred or the stream was closed return 0
+ if (p_sys->b_error || !vlc_object_alive(s))
+ return 0;
- /* NOTE: buffer might be NULL if caller wants to skip data */
- length = hls_Read(s, (uint8_t*) buffer, i_read);
- if (length < 0)
- return 0;
+ // Lock the mutex before trying to read to avoid a race condition with the download thread
+ vlc_mutex_lock(&p_sys->read.lock_wait);
+
+ /* NOTE: buffer might be NULL if caller wants to skip data */
+ length = hls_Read(s, (uint8_t*) buffer, i_read);
+
+ // An error has occurred in hls_Read
+ if (length < 0)
+ {
+ vlc_mutex_unlock(&p_sys->read.lock_wait);
+
+ return 0;
+ }
+
+ // There is no data available yet for the demuxer so we need to wait until reload and
+ // download operation are over.
+ // Download thread will signal once download is finished.
+ // A timed wait is used to avoid deadlock in case data never arrives since the thread
+ // running this read operation is also responsible for closing the stream
+ if (length == 0)
+ {
+ mtime_t start = mdate();
+
+ // Wait for 10 seconds
+ mtime_t timeout_limit = start + (10 * UINT64_C(1000000));
+
+ int res = vlc_cond_timedwait(&p_sys->read.wait, &p_sys->read.lock_wait, timeout_limit);
+
+ // Error - reached a timeout of 10 seconds without data arriving - kill the stream
+ if (res == ETIMEDOUT)
+ {
+ msg_Info(s, "timeout limit reached!");
+
+ vlc_mutex_unlock(&p_sys->read.lock_wait);
+
+ return 0;
+ }
+ else if (res == EINVAL)
+ return 0; // Error - lock is not locked so we can just return
+ }
+
+ vlc_mutex_unlock(&p_sys->read.lock_wait);
+ }
p_sys->playback.offset += length;
return length;
More information about the vlc-commits
mailing list