[PATCH 4/4] qcap: Use a condition variable to synchronize with the video capture thread.

Zebediah Figura z.figura12 at gmail.com
Sun Jun 28 18:13:41 CDT 2020


This fixes a hang that can be triggered by calling Pause() followed by Stop().

Signed-off-by: Zebediah Figura <z.figura12 at gmail.com>
---
 dlls/qcap/v4l.c | 53 +++++++++++++++++++++++++++++++------------------
 1 file changed, 34 insertions(+), 19 deletions(-)

diff --git a/dlls/qcap/v4l.c b/dlls/qcap/v4l.c
index 2cf50b0c28..54502cfa1c 100644
--- a/dlls/qcap/v4l.c
+++ b/dlls/qcap/v4l.c
@@ -98,9 +98,11 @@ struct v4l_device
 
     struct strmbase_source *pin;
     int fd, mmap;
-    FILTER_STATE state;
 
-    HANDLE thread, run_event;
+    HANDLE thread;
+    FILTER_STATE state;
+    CONDITION_VARIABLE state_cv;
+    CRITICAL_SECTION state_cs;
 };
 
 static inline struct v4l_device *v4l_device(struct video_capture_device *iface)
@@ -123,6 +125,8 @@ static void v4l_device_destroy(struct video_capture_device *iface)
 {
     struct v4l_device *device = v4l_device(iface);
 
+    device->state_cs.DebugInfo->Spare[0] = 0;
+    DeleteCriticalSection(&device->state_cs);
     if (device->fd != -1)
         video_close(device->fd);
     if (device->caps_count)
@@ -320,19 +324,18 @@ static void reverse_image(struct v4l_device *device, LPBYTE output, const BYTE *
     }
 }
 
-static DWORD WINAPI ReadThread(LPVOID lParam)
+static DWORD WINAPI ReadThread(void *arg)
 {
-    struct v4l_device *capBox = lParam;
+    struct v4l_device *device = arg;
     HRESULT hr;
     IMediaSample *pSample = NULL;
-    ULONG framecount = 0;
     unsigned char *pTarget, *image_data;
     unsigned int image_size;
     UINT width, height, depth;
 
-    width = capBox->current_caps->video_info.bmiHeader.biWidth;
-    height = capBox->current_caps->video_info.bmiHeader.biHeight;
-    depth = capBox->current_caps->video_info.bmiHeader.biBitCount / 8;
+    width = device->current_caps->video_info.bmiHeader.biWidth;
+    height = device->current_caps->video_info.bmiHeader.biHeight;
+    depth = device->current_caps->video_info.bmiHeader.biBitCount / 8;
     image_size = width * height * depth;
     if (!(image_data = heap_alloc(image_size)))
     {
@@ -340,12 +343,17 @@ static DWORD WINAPI ReadThread(LPVOID lParam)
         return 0;
     }
 
-    while (capBox->state != State_Stopped)
+    EnterCriticalSection(&device->state_cs);
+
+    for (;;)
     {
-        if (capBox->state == State_Paused)
-            WaitForSingleObject(capBox->run_event, INFINITE);
+        while (device->state == State_Paused)
+            SleepConditionVariableCS(&device->state_cv, &device->state_cs, INFINITE);
+
+        if (device->state == State_Stopped)
+            break;
 
-        hr = BaseOutputPinImpl_GetDeliveryBuffer(capBox->pin, &pSample, NULL, NULL, 0);
+        hr = BaseOutputPinImpl_GetDeliveryBuffer(device->pin, &pSample, NULL, NULL, 0);
         if (SUCCEEDED(hr))
         {
             int len;
@@ -358,7 +366,7 @@ static DWORD WINAPI ReadThread(LPVOID lParam)
 
             IMediaSample_GetPointer(pSample, &pTarget);
 
-            while (video_read(capBox->fd, image_data, image_size) == -1)
+            while (video_read(device->fd, image_data, image_size) == -1)
             {
                 if (errno != EAGAIN)
                 {
@@ -367,9 +375,8 @@ static DWORD WINAPI ReadThread(LPVOID lParam)
                 }
             }
 
-            reverse_image(capBox, pTarget, image_data);
-            hr = IMemInputPin_Receive(capBox->pin->pMemInputPin, pSample);
-            TRACE("%p -> Frame %u: %x\n", capBox, ++framecount, hr);
+            reverse_image(device, pTarget, image_data);
+            hr = IMemInputPin_Receive(device->pin->pMemInputPin, pSample);
             IMediaSample_Release(pSample);
         }
         if (FAILED(hr) && hr != VFW_E_NOT_CONNECTED)
@@ -379,6 +386,7 @@ static DWORD WINAPI ReadThread(LPVOID lParam)
         }
     }
 
+    LeaveCriticalSection(&device->state_cs);
     heap_free(image_data);
     return 0;
 }
@@ -412,15 +420,17 @@ static void v4l_device_init_stream(struct video_capture_device *iface)
 static void v4l_device_start_stream(struct video_capture_device *iface)
 {
     struct v4l_device *device = v4l_device(iface);
+    EnterCriticalSection(&device->state_cs);
     device->state = State_Running;
-    SetEvent(device->run_event);
+    LeaveCriticalSection(&device->state_cs);
 }
 
 static void v4l_device_stop_stream(struct video_capture_device *iface)
 {
     struct v4l_device *device = v4l_device(iface);
+    EnterCriticalSection(&device->state_cs);
     device->state = State_Paused;
-    ResetEvent(device->run_event);
+    LeaveCriticalSection(&device->state_cs);
 }
 
 static void v4l_device_cleanup_stream(struct video_capture_device *iface)
@@ -428,7 +438,10 @@ static void v4l_device_cleanup_stream(struct video_capture_device *iface)
     struct v4l_device *device = v4l_device(iface);
     HRESULT hr;
 
+    EnterCriticalSection(&device->state_cs);
     device->state = State_Stopped;
+    LeaveCriticalSection(&device->state_cs);
+    WakeConditionVariable(&device->state_cv);
     WaitForSingleObject(device->thread, INFINITE);
     CloseHandle(device->thread);
     device->thread = NULL;
@@ -656,7 +669,9 @@ struct video_capture_device *v4l_device_create(struct strmbase_source *pin, USHO
     device->d.ops = &v4l_device_ops;
     device->pin = pin;
     device->state = State_Stopped;
-    device->run_event = CreateEventW(NULL, TRUE, FALSE, NULL);
+    InitializeConditionVariable(&device->state_cv);
+    InitializeCriticalSection(&device->state_cs);
+    device->state_cs.DebugInfo->Spare[0] = (DWORD_PTR)(__FILE__ ": v4l_device.state_cs");
 
     TRACE("Format: %d bpp - %dx%d.\n", device->current_caps->video_info.bmiHeader.biBitCount,
             device->current_caps->video_info.bmiHeader.biWidth,
-- 
2.27.0




More information about the wine-devel mailing list