From: Damjan Jovanovic Subject: [PATCH v2] qcap: implement and test VfwPin_CheckMediaType() Message-Id: Date: Wed, 24 Apr 2019 08:18:16 +0200 VfwPin_CheckMediaType() always return a hardcoded E_NOTIMPL, preventing any media format from being negotiated, and limiting video capture devices from connecting to many other filters. Implement it and test. In try 2, instead of the high-level test of connecting to a NullRenderer, more elaborate lower-level testing is done: we test that the device can accept all of its own media types, and prove that AM_MEDIA_TYPEs of NULL, GUID_NULL, FORMAT_Empty, and a NULL pbFormat, all fail. The implementation matches results obtained by testing against 2 webcams on Windows. Also coding style was cleaned up. Signed-off-by: Damjan Jovanovic --- dlls/qcap/capture.h | 1 + dlls/qcap/tests/Makefile.in | 3 +- dlls/qcap/tests/videocapture.c | 149 +++++++++++++++++++++++++++++++++ dlls/qcap/v4l.c | 50 +++++++++-- dlls/qcap/vfwcapture.c | 4 +- 5 files changed, 198 insertions(+), 9 deletions(-) create mode 100644 dlls/qcap/tests/videocapture.c diff --git a/dlls/qcap/capture.h b/dlls/qcap/capture.h index 65ed2dfc27..aed88893e5 100644 --- a/dlls/qcap/capture.h +++ b/dlls/qcap/capture.h @@ -25,6 +25,7 @@ typedef struct _Capture Capture; Capture *qcap_driver_init(IPin*,USHORT) DECLSPEC_HIDDEN; HRESULT qcap_driver_destroy(Capture*) DECLSPEC_HIDDEN; +HRESULT qcap_driver_check_format(Capture*,const AM_MEDIA_TYPE*) DECLSPEC_HIDDEN; HRESULT qcap_driver_set_format(Capture*,AM_MEDIA_TYPE*) DECLSPEC_HIDDEN; HRESULT qcap_driver_get_format(const Capture*,AM_MEDIA_TYPE**) DECLSPEC_HIDDEN; HRESULT qcap_driver_get_prop_range(Capture*,VideoProcAmpProperty,LONG*,LONG*,LONG*,LONG*,LONG*) DECLSPEC_HIDDEN; diff --git a/dlls/qcap/tests/Makefile.in b/dlls/qcap/tests/Makefile.in index 2b988476ad..a5b9d0e474 100644 --- a/dlls/qcap/tests/Makefile.in +++ b/dlls/qcap/tests/Makefile.in @@ -5,4 +5,5 @@ C_SRCS = \ audiorecord.c \ avico.c \ qcap.c \ - smartteefilter.c + smartteefilter.c \ + videocapture.c diff --git a/dlls/qcap/tests/videocapture.c b/dlls/qcap/tests/videocapture.c new file mode 100644 index 0000000000..a9a5de0523 --- /dev/null +++ b/dlls/qcap/tests/videocapture.c @@ -0,0 +1,149 @@ +/* + * Video capture device tests. + * + * Copyright 2019 Damjan Jovanovic + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA + */ + +#define COBJMACROS +#include "dshow.h" +#include "wine/test.h" + + +static void test_pin_media_types(IPin *pin) +{ + IEnumMediaTypes *enum_media_types; + AM_MEDIA_TYPE *media_type; + AM_MEDIA_TYPE empty_media_type; + HRESULT hr; + + hr = IPin_EnumMediaTypes(pin, &enum_media_types); + ok(hr == S_OK, "Got hr %#x.\n", hr); + + while ((hr = IEnumMediaTypes_Next(enum_media_types, 1, &media_type, NULL)) == S_OK) + { + hr = IPin_QueryAccept(pin, media_type); + ok(hr == S_OK, "Got hr %#x.\n", hr); + CoTaskMemFree(media_type); + } + IEnumMediaTypes_Release(enum_media_types); + + hr = IPin_QueryAccept(pin, NULL); + todo_wine ok(hr == E_POINTER, "Got hr %#x.\n", hr); + + memset(&empty_media_type, 0, sizeof(empty_media_type)); + hr = IPin_QueryAccept(pin, &empty_media_type); + ok(hr != S_OK, "Got hr %#x.\n", hr); + + empty_media_type.majortype = MEDIATYPE_Video; + hr = IPin_QueryAccept(pin, &empty_media_type); + ok(hr != S_OK, "Got hr %#x.\n", hr); + + empty_media_type.formattype = FORMAT_VideoInfo; + hr = IPin_QueryAccept(pin, &empty_media_type); + ok(hr != S_OK, "Got hr %#x.\n", hr); + + empty_media_type.formattype = FORMAT_None; + hr = IPin_QueryAccept(pin, &empty_media_type); + ok(hr != S_OK, "Got hr %#x.\n", hr); +} + +static void test_capture(IBaseFilter *capture_device) +{ + IEnumPins *enum_pins; + IPin *pin; + HRESULT hr; + + hr = IBaseFilter_EnumPins(capture_device, &enum_pins); + ok(hr == S_OK, "Got hr %#x.\n", hr); + + while ((hr = IEnumPins_Next(enum_pins, 1, &pin, NULL)) == S_OK) + { + PIN_DIRECTION pin_direction; + IPin_QueryDirection(pin, &pin_direction); + if (pin_direction == PINDIR_OUTPUT) + test_pin_media_types(pin); + IPin_Release(pin); + } + + IEnumPins_Release(enum_pins); +} + +static void test_property_bag(IMoniker *moniker) +{ + IPropertyBag *property_bag = NULL; + HRESULT hr; + + hr = IMoniker_BindToStorage(moniker, NULL, NULL, &IID_IPropertyBag, (void**)&property_bag); + ok(hr == S_OK, "Got hr=%#x.\n", hr); + + /* On Windows, all properties can fail to be read, so don't bother testing */ + + if (property_bag != NULL) + IPropertyBag_Release(property_bag); +} + +START_TEST(videocapture) +{ + ICreateDevEnum *dev_enum = NULL; + IEnumMoniker *class_enum = NULL; + IMoniker *moniker = NULL; + HRESULT hr; + + CoInitialize(NULL); + + hr = CoCreateInstance(&CLSID_SystemDeviceEnum, NULL, CLSCTX_INPROC, + &IID_ICreateDevEnum, (void **)&dev_enum); + ok(hr == S_OK, "Got hr=%#x.\n", hr); + + hr = ICreateDevEnum_CreateClassEnumerator(dev_enum, &CLSID_VideoInputDeviceCategory, &class_enum, 0); + if (hr == S_FALSE) + { + skip("No video capture devices present.\n"); + goto end; + } + ok(hr == S_OK, "Got hr=%#x.\n", hr); + + while (IEnumMoniker_Next(class_enum, 1, &moniker, NULL) == S_OK) + { + WCHAR *name; + IBaseFilter *capture_device = NULL; + + hr = IMoniker_GetDisplayName(moniker, NULL, NULL, &name); + ok(hr == S_OK, "Got hr=%#x.\n", hr); + trace("Testing device %s.\n", wine_dbgstr_w(name)); + CoTaskMemFree(name); + + hr = IMoniker_BindToObject(moniker, NULL, NULL, &IID_IBaseFilter, (void**)&capture_device); + if (hr == S_OK) + { + test_property_bag(moniker); + test_capture(capture_device); + IBaseFilter_Release(capture_device); + } + else + skip("Failed to open capture device, hr=%#x.\n", hr); + + IMoniker_Release(moniker); + } + +end: + if (dev_enum != NULL) + ICreateDevEnum_Release(dev_enum); + if (class_enum != NULL) + IEnumMoniker_Release(class_enum); + CoUninitialize(); +} diff --git a/dlls/qcap/v4l.c b/dlls/qcap/v4l.c index dcc5925164..a5766c2a81 100644 --- a/dlls/qcap/v4l.c +++ b/dlls/qcap/v4l.c @@ -131,20 +131,58 @@ HRESULT qcap_driver_destroy(Capture *capBox) return S_OK; } +HRESULT qcap_driver_check_format(Capture *device, const AM_MEDIA_TYPE *mt) +{ + HRESULT hr; + TRACE("(%p)->(AM_MEDIA_TYPE(%p))\n", device, mt); + dump_AM_MEDIA_TYPE(mt); + + if (mt == NULL) + { + hr = E_POINTER; + goto end; + } + + if (!IsEqualGUID(&mt->majortype, &MEDIATYPE_Video)) + { + hr = S_FALSE; + goto end; + } + + if (IsEqualGUID(&mt->formattype, &FORMAT_VideoInfo) && + mt->pbFormat != NULL && + mt->cbFormat >= sizeof(VIDEOINFOHEADER)) + { + VIDEOINFOHEADER *vih = (VIDEOINFOHEADER *)mt->pbFormat; + if (vih->bmiHeader.biBitCount == 24 && vih->bmiHeader.biCompression == BI_RGB) + hr = S_OK; + else + { + FIXME("Unsupported compression %#x, bpp %u.\n", vih->bmiHeader.biCompression, + vih->bmiHeader.biBitCount); + hr = S_FALSE; + } + } + else + hr = VFW_E_INVALIDMEDIATYPE; + +end: + TRACE("returning %#x\n", hr); + return hr; +} + HRESULT qcap_driver_set_format(Capture *device, AM_MEDIA_TYPE *mt) { struct v4l2_format format = {0}; int newheight, newwidth; VIDEOINFOHEADER *vih; int fd = device->fd; + HRESULT hr; + hr = qcap_driver_check_format(device, mt); + if (FAILED(hr)) + return hr; vih = (VIDEOINFOHEADER *)mt->pbFormat; - if (vih->bmiHeader.biBitCount != 24 || vih->bmiHeader.biCompression != BI_RGB) - { - FIXME("Unsupported compression %#x, bpp %u.\n", vih->bmiHeader.biCompression, - vih->bmiHeader.biBitCount); - return VFW_E_INVALIDMEDIATYPE; - } newwidth = vih->bmiHeader.biWidth; newheight = vih->bmiHeader.biHeight; diff --git a/dlls/qcap/vfwcapture.c b/dlls/qcap/vfwcapture.c index 6297a80e61..06dbe5eae8 100644 --- a/dlls/qcap/vfwcapture.c +++ b/dlls/qcap/vfwcapture.c @@ -660,8 +660,8 @@ static inline VfwPinImpl *impl_from_BasePin(BasePin *pin) static HRESULT WINAPI VfwPin_CheckMediaType(BasePin *pin, const AM_MEDIA_TYPE *amt) { - FIXME("(%p) stub\n", pin); - return E_NOTIMPL; + VfwPinImpl *This = impl_from_BasePin(pin); + return qcap_driver_check_format(This->parent->driver_info, amt); } static HRESULT WINAPI VfwPin_GetMediaType(BasePin *pin, int iPosition, AM_MEDIA_TYPE *pmt)