[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
Mon Aug 26 15:43:07 CEST 2013


vlc | branch: master | Avishay Spitzer <savishay at gmail.com> | Mon Aug 19 03:08:33 2013 -0400| [50d7d0c9c470ccec4d48212221aaecd822928c96] | committer: Ilkka Ollakka

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>

> http://git.videolan.org/gitweb.cgi/vlc.git/?a=commit;h=50d7d0c9c470ccec4d48212221aaecd822928c96
---

 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 cc1c0b7..6577a81 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 */
@@ -1672,6 +1678,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);
@@ -2029,6 +2040,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)
     {
@@ -2056,6 +2070,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++)
@@ -2096,6 +2113,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++)
     {
@@ -2284,13 +2304,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