Signed-off-by: Zebediah Figura z.figura12@gmail.com --- dlls/qcap/qcap_private.h | 3 +- dlls/qcap/v4l.c | 96 ++++----------------------------------- dlls/qcap/vfwcapture.c | 98 ++++++++++++++++++++++++++++++++++++++-- 3 files changed, 103 insertions(+), 94 deletions(-)
diff --git a/dlls/qcap/qcap_private.h b/dlls/qcap/qcap_private.h index 9df432f8d05..d715219c645 100644 --- a/dlls/qcap/qcap_private.h +++ b/dlls/qcap/qcap_private.h @@ -54,9 +54,8 @@ struct video_capture_funcs LONG *min, LONG *max, LONG *step, LONG *default_value, LONG *flags); HRESULT (*get_prop)(struct video_capture_device *device, VideoProcAmpProperty property, LONG *value, LONG *flags); HRESULT (*set_prop)(struct video_capture_device *device, VideoProcAmpProperty property, LONG value, LONG flags); + BOOL (*read_frame)(struct video_capture_device *device, BYTE *data); void (*init_stream)(struct video_capture_device *device); - void (*start_stream)(struct video_capture_device *device); - void (*stop_stream)(struct video_capture_device *device); void (*cleanup_stream)(struct video_capture_device *device); };
diff --git a/dlls/qcap/v4l.c b/dlls/qcap/v4l.c index a007adce492..e0095379b34 100644 --- a/dlls/qcap/v4l.c +++ b/dlls/qcap/v4l.c @@ -99,11 +99,6 @@ struct video_capture_device
struct strmbase_source *pin; int fd, mmap; - - HANDLE thread; - FILTER_STATE state; - CONDITION_VARIABLE state_cv; - CRITICAL_SECTION state_cs; };
static int xioctl(int fd, int request, void * arg) @@ -119,8 +114,6 @@ static int xioctl(int fd, int request, void * arg)
static void v4l_device_destroy(struct video_capture_device *device) { - device->state_cs.DebugInfo->Spare[0] = 0; - DeleteCriticalSection(&device->state_cs); if (device->fd != -1) video_close(device->fd); if (device->caps_count) @@ -330,62 +323,19 @@ static void reverse_image(struct video_capture_device *device, LPBYTE output, co } }
-static DWORD WINAPI ReadThread(void *arg) +static BOOL v4l_device_read_frame(struct video_capture_device *device, BYTE *data) { - struct video_capture_device *device = arg; - HRESULT hr; - IMediaSample *pSample = NULL; - unsigned char *pTarget; - - for (;;) + while (video_read(device->fd, device->image_data, device->image_size) < 0) { - EnterCriticalSection(&device->state_cs); - - while (device->state == State_Paused) - SleepConditionVariableCS(&device->state_cv, &device->state_cs, INFINITE); - - if (device->state == State_Stopped) + if (errno != EAGAIN) { - LeaveCriticalSection(&device->state_cs); - break; - } - - LeaveCriticalSection(&device->state_cs); - - hr = BaseOutputPinImpl_GetDeliveryBuffer(device->pin, &pSample, NULL, NULL, 0); - if (SUCCEEDED(hr)) - { - int len; - - IMediaSample_SetActualDataLength(pSample, device->image_size); - - len = IMediaSample_GetActualDataLength(pSample); - TRACE("Data length: %d KB\n", len / 1024); - - IMediaSample_GetPointer(pSample, &pTarget); - - while (video_read(device->fd, device->image_data, device->image_size) == -1) - { - if (errno != EAGAIN) - { - ERR("Failed to read frame: %s\n", strerror(errno)); - break; - } - } - - reverse_image(device, pTarget, device->image_data); - hr = IMemInputPin_Receive(device->pin->pMemInputPin, pSample); - IMediaSample_Release(pSample); - } - - if (FAILED(hr) && hr != VFW_E_NOT_CONNECTED) - { - TRACE("Return %x, stop IFilterGraph\n", hr); - break; + ERR("Failed to read frame: %s\n", strerror(errno)); + return FALSE; } }
- return 0; + reverse_image(device, data, device->image_data); + return TRUE; }
static void v4l_device_init_stream(struct video_capture_device *device) @@ -407,37 +357,12 @@ static void v4l_device_init_stream(struct video_capture_device *device) if (FAILED(hr = IMemAllocator_Commit(device->pin->pAllocator))) ERR("Failed to commit allocator, hr %#x.\n", hr); } - - device->state = State_Paused; - device->thread = CreateThread(NULL, 0, ReadThread, device, 0, NULL); -} - -static void v4l_device_start_stream(struct video_capture_device *device) -{ - EnterCriticalSection(&device->state_cs); - device->state = State_Running; - LeaveCriticalSection(&device->state_cs); -} - -static void v4l_device_stop_stream(struct video_capture_device *device) -{ - EnterCriticalSection(&device->state_cs); - device->state = State_Paused; - LeaveCriticalSection(&device->state_cs); }
static void v4l_device_cleanup_stream(struct video_capture_device *device) { 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; - hr = IMemAllocator_Decommit(device->pin->pAllocator); if (hr != S_OK && hr != VFW_E_NOT_COMMITTED) ERR("Failed to decommit allocator, hr %#x.\n", hr); @@ -638,10 +563,6 @@ struct video_capture_device *v4l_device_create(struct strmbase_source *pin, USHO }
device->pin = pin; - device->state = State_Stopped; - InitializeConditionVariable(&device->state_cv); - InitializeCriticalSection(&device->state_cs); - device->state_cs.DebugInfo->Spare[0] = (DWORD_PTR)(__FILE__ ": video_capture_device.state_cs");
TRACE("Format: %d bpp - %dx%d.\n", device->current_caps->video_info.bmiHeader.biBitCount, device->current_caps->video_info.bmiHeader.biWidth, @@ -667,9 +588,8 @@ const struct video_capture_funcs v4l_funcs = .get_prop_range = v4l_device_get_prop_range, .get_prop = v4l_device_get_prop, .set_prop = v4l_device_set_prop, + .read_frame = v4l_device_read_frame, .init_stream = v4l_device_init_stream, - .start_stream = v4l_device_start_stream, - .stop_stream = v4l_device_stop_stream, .cleanup_stream = v4l_device_cleanup_stream, };
diff --git a/dlls/qcap/vfwcapture.c b/dlls/qcap/vfwcapture.c index 9ff96f13f91..9fb694ba678 100644 --- a/dlls/qcap/vfwcapture.c +++ b/dlls/qcap/vfwcapture.c @@ -34,10 +34,19 @@ struct vfw_capture IPersistPropertyBag IPersistPropertyBag_iface; BOOL init;
- struct video_capture_device *device; - struct strmbase_source source; IKsPropertySet IKsPropertySet_iface; + + struct video_capture_device *device; + + /* FIXME: It would be nice to avoid duplicating this variable with strmbase. + * However, synchronization is tricky; we need access to be protected by a + * separate lock. */ + FILTER_STATE state; + CONDITION_VARIABLE state_cv; + CRITICAL_SECTION state_cs; + + HANDLE thread; };
static inline struct vfw_capture *impl_from_strmbase_filter(struct strmbase_filter *iface) @@ -96,6 +105,8 @@ static void vfw_capture_destroy(struct strmbase_filter *iface) IPin_Disconnect(filter->source.pin.peer); IPin_Disconnect(&filter->source.pin.IPin_iface); } + filter->state_cs.DebugInfo->Spare[0] = 0; + DeleteCriticalSection(&filter->state_cs); strmbase_source_cleanup(&filter->source); strmbase_filter_cleanup(&filter->filter); CoTaskMemFree(filter); @@ -121,11 +132,71 @@ static HRESULT vfw_capture_query_interface(struct strmbase_filter *iface, REFIID return S_OK; }
+static DWORD WINAPI stream_thread(void *arg) +{ + struct vfw_capture *filter = arg; + const VIDEOINFOHEADER *format = (const VIDEOINFOHEADER *)filter->source.pin.mt.pbFormat; + const unsigned int image_size = format->bmiHeader.biWidth + * format->bmiHeader.biHeight * format->bmiHeader.biBitCount / 8; + + for (;;) + { + IMediaSample *sample; + HRESULT hr; + BYTE *data; + + EnterCriticalSection(&filter->state_cs); + + while (filter->state == State_Paused) + SleepConditionVariableCS(&filter->state_cv, &filter->state_cs, INFINITE); + + if (filter->state == State_Stopped) + { + LeaveCriticalSection(&filter->state_cs); + break; + } + + LeaveCriticalSection(&filter->state_cs); + + if (FAILED(hr = BaseOutputPinImpl_GetDeliveryBuffer(&filter->source, &sample, NULL, NULL, 0))) + { + ERR("Failed to get sample, hr %#x.\n", hr); + break; + } + + IMediaSample_SetActualDataLength(sample, image_size); + IMediaSample_GetPointer(sample, &data); + + if (!capture_funcs->read_frame(filter->device, data)) + { + IMediaSample_Release(sample); + break; + } + + hr = IMemInputPin_Receive(filter->source.pMemInputPin, sample); + IMediaSample_Release(sample); + if (FAILED(hr)) + { + ERR("IMemInputPin::Receive() returned %#x.\n", hr); + break; + } + } + + return 0; +} + static HRESULT vfw_capture_init_stream(struct strmbase_filter *iface) { struct vfw_capture *filter = impl_from_strmbase_filter(iface);
capture_funcs->init_stream(filter->device); + + EnterCriticalSection(&filter->state_cs); + filter->state = State_Paused; + LeaveCriticalSection(&filter->state_cs); + + filter->thread = CreateThread(NULL, 0, stream_thread, filter, 0, NULL); + return S_OK; }
@@ -133,7 +204,10 @@ static HRESULT vfw_capture_start_stream(struct strmbase_filter *iface, REFERENCE { struct vfw_capture *filter = impl_from_strmbase_filter(iface);
- capture_funcs->start_stream(filter->device); + EnterCriticalSection(&filter->state_cs); + filter->state = State_Running; + LeaveCriticalSection(&filter->state_cs); + WakeConditionVariable(&filter->state_cv); return S_OK; }
@@ -141,7 +215,9 @@ static HRESULT vfw_capture_stop_stream(struct strmbase_filter *iface) { struct vfw_capture *filter = impl_from_strmbase_filter(iface);
- capture_funcs->stop_stream(filter->device); + EnterCriticalSection(&filter->state_cs); + filter->state = State_Paused; + LeaveCriticalSection(&filter->state_cs); return S_OK; }
@@ -149,6 +225,15 @@ static HRESULT vfw_capture_cleanup_stream(struct strmbase_filter *iface) { struct vfw_capture *filter = impl_from_strmbase_filter(iface);
+ EnterCriticalSection(&filter->state_cs); + filter->state = State_Stopped; + LeaveCriticalSection(&filter->state_cs); + WakeConditionVariable(&filter->state_cv); + + WaitForSingleObject(filter->thread, INFINITE); + CloseHandle(filter->thread); + filter->thread = NULL; + capture_funcs->cleanup_stream(filter->device); return S_OK; } @@ -709,6 +794,11 @@ HRESULT vfw_capture_create(IUnknown *outer, IUnknown **out)
object->IKsPropertySet_iface.lpVtbl = &IKsPropertySet_VTable;
+ object->state = State_Stopped; + InitializeConditionVariable(&object->state_cv); + InitializeCriticalSection(&object->state_cs); + object->state_cs.DebugInfo->Spare[0] = (DWORD_PTR)(__FILE__ ": vfw_capture.state_cs"); + TRACE("Created VFW capture filter %p.\n", object); ObjectRefCount(TRUE); *out = &object->filter.IUnknown_inner;