diff --git a/configure b/configure index a097955e905..add494cf8e2 100755 --- a/configure +++ b/configure @@ -1522,6 +1522,7 @@ enable_prntvpt enable_propsys enable_psapi enable_pstorec +enable_qasf enable_qcap enable_qedit enable_qmgr @@ -20842,6 +20843,8 @@ wine_fn_config_makefile dlls/psapi enable_psapi wine_fn_config_makefile dlls/psapi/tests enable_tests wine_fn_config_makefile dlls/pstorec enable_pstorec wine_fn_config_makefile dlls/pstorec/tests enable_tests +wine_fn_config_makefile dlls/qasf enable_qasf +wine_fn_config_makefile dlls/qasf/tests enable_tests wine_fn_config_makefile dlls/qcap enable_qcap wine_fn_config_makefile dlls/qcap/tests enable_tests wine_fn_config_makefile dlls/qedit enable_qedit diff --git a/configure.ac b/configure.ac index 649bce5f86c..47d2b750c01 100644 --- a/configure.ac +++ b/configure.ac @@ -3580,6 +3580,8 @@ WINE_CONFIG_MAKEFILE(dlls/psapi) WINE_CONFIG_MAKEFILE(dlls/psapi/tests) WINE_CONFIG_MAKEFILE(dlls/pstorec) WINE_CONFIG_MAKEFILE(dlls/pstorec/tests) +WINE_CONFIG_MAKEFILE(dlls/qasf) +WINE_CONFIG_MAKEFILE(dlls/qasf/tests) WINE_CONFIG_MAKEFILE(dlls/qcap) WINE_CONFIG_MAKEFILE(dlls/qcap/tests) WINE_CONFIG_MAKEFILE(dlls/qedit) diff --git a/dlls/qasf/Makefile.in b/dlls/qasf/Makefile.in new file mode 100644 index 00000000000..7ca314e20ba --- /dev/null +++ b/dlls/qasf/Makefile.in @@ -0,0 +1,11 @@ +MODULE = qasf.dll +IMPORTS = strmbase dmoguids strmiids uuid + +EXTRADLLFLAGS = -mno-cygwin + +C_SRCS = \ + dmowrapper.c \ + qasf_main.c + +IDL_SRCS = \ + qasf_classes.idl diff --git a/dlls/qasf/dmowrapper.c b/dlls/qasf/dmowrapper.c new file mode 100644 index 00000000000..c7c5d3df9ea --- /dev/null +++ b/dlls/qasf/dmowrapper.c @@ -0,0 +1,68 @@ +/* + * DMO wrapper filter + * + * Copyright (C) 2019 Zebediah Figura + * + * 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 + */ + +#include "qasf_private.h" + +WINE_DEFAULT_DEBUG_CHANNEL(qasf); + +struct dmo_wrapper +{ + struct strmbase_filter filter; +}; + +static inline struct dmo_wrapper *impl_from_strmbase_filter(struct strmbase_filter *iface) +{ + return CONTAINING_RECORD(iface, struct dmo_wrapper, filter); +} + +static struct strmbase_pin *dmo_wrapper_get_pin(struct strmbase_filter *iface, unsigned int index) +{ + return NULL; +} + +static void dmo_wrapper_destroy(struct strmbase_filter *iface) +{ + struct dmo_wrapper *filter = impl_from_strmbase_filter(iface); + + strmbase_filter_cleanup(&filter->filter); + free(filter); +} + +static struct strmbase_filter_ops filter_ops = +{ + .filter_get_pin = dmo_wrapper_get_pin, + .filter_destroy = dmo_wrapper_destroy, +}; + +HRESULT dmo_wrapper_create(IUnknown *outer, IUnknown **out) +{ + struct dmo_wrapper *object; + + if (!(object = calloc(sizeof(*object), 1))) + return E_OUTOFMEMORY; + + /* Always pass NULL as the outer object; see test_aggregation(). */ + strmbase_filter_init(&object->filter, NULL, &CLSID_DMOWrapperFilter, &filter_ops); + + TRACE("Created DMO wrapper %p.\n", object); + *out = &object->filter.IUnknown_inner; + + return S_OK; +} diff --git a/dlls/qasf/qasf.spec b/dlls/qasf/qasf.spec new file mode 100644 index 00000000000..b16365d0c9f --- /dev/null +++ b/dlls/qasf/qasf.spec @@ -0,0 +1,4 @@ +@ stdcall -private DllCanUnloadNow() +@ stdcall -private DllGetClassObject(ptr ptr ptr) +@ stdcall -private DllRegisterServer() +@ stdcall -private DllUnregisterServer() diff --git a/dlls/qasf/qasf_classes.idl b/dlls/qasf/qasf_classes.idl new file mode 100644 index 00000000000..0acf3806910 --- /dev/null +++ b/dlls/qasf/qasf_classes.idl @@ -0,0 +1,27 @@ +/* + * Copyright (C) 2019 Zebediah Figura + * + * 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 + */ + +#pragma makedep register + +#include "dmodshow.idl" + +[ + threading(both), + uuid(94297043-bd82-4dfd-b0de-8177739c6d20), +] +coclass DMOWrapperFilter {} diff --git a/dlls/qasf/qasf_main.c b/dlls/qasf/qasf_main.c new file mode 100644 index 00000000000..5df23649f0d --- /dev/null +++ b/dlls/qasf/qasf_main.c @@ -0,0 +1,137 @@ +/* + * DirectShow ASF filters + * + * Copyright (C) 2019 Zebediah Figura + * + * 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 + */ + +#include "qasf_private.h" + +WINE_DEFAULT_DEBUG_CHANNEL(qasf); + +static HINSTANCE qasf_instance; + +struct class_factory +{ + IClassFactory IClassFactory_iface; + HRESULT (*create_instance)(IUnknown *outer, IUnknown **out); +}; + +static struct class_factory *impl_from_IClassFactory(IClassFactory *iface) +{ + return CONTAINING_RECORD(iface, struct class_factory, IClassFactory_iface); +} + +static HRESULT WINAPI class_factory_QueryInterface(IClassFactory *iface, REFIID iid, void **out) +{ + TRACE("iface %p, iid %s, out %p.\n", iface, debugstr_guid(iid), out); + + if (IsEqualGUID(iid, &IID_IUnknown) || IsEqualGUID(iid, &IID_IClassFactory)) + { + IClassFactory_AddRef(iface); + *out = iface; + return S_OK; + } + + *out = NULL; + WARN("%s not implemented, returning E_NOINTERFACE\n", debugstr_guid(iid)); + return E_NOINTERFACE; +} + +static ULONG WINAPI class_factory_AddRef(IClassFactory *iface) +{ + return 2; +} + +static ULONG WINAPI class_factory_Release(IClassFactory *iface) +{ + return 1; +} + +static HRESULT WINAPI class_factory_CreateInstance(IClassFactory *iface, + IUnknown *outer, REFIID iid, void **out) +{ + struct class_factory *factory = impl_from_IClassFactory(iface); + IUnknown *unk; + HRESULT hr; + + TRACE("iface %p, outer %p, iid %s, out %p.\n", iface, outer, debugstr_guid(iid), out); + + *out = NULL; + + if (outer && !IsEqualGUID(iid, &IID_IUnknown)) + return E_NOINTERFACE; + + if (SUCCEEDED(hr = factory->create_instance(outer, &unk))) + { + hr = IUnknown_QueryInterface(unk, iid, out); + IUnknown_Release(unk); + } + return hr; +} + +static HRESULT WINAPI class_factory_LockServer(IClassFactory *iface, BOOL lock) +{ + FIXME("lock %d, stub!\n", lock); + return S_OK; +} + +static const IClassFactoryVtbl class_factory_vtbl = +{ + class_factory_QueryInterface, + class_factory_AddRef, + class_factory_Release, + class_factory_CreateInstance, + class_factory_LockServer, +}; + +static struct class_factory dmo_wrapper_cf = {{&class_factory_vtbl}, dmo_wrapper_create}; + +BOOL WINAPI DllMain(HINSTANCE instance, DWORD reason, void *reserved) +{ + if (reason == DLL_PROCESS_ATTACH) + { + DisableThreadLibraryCalls(instance); + qasf_instance = instance; + } + return TRUE; +} + +HRESULT WINAPI DllGetClassObject(REFCLSID clsid, REFIID iid, void **out) +{ + TRACE("clsid %s, iid %s, out %p.\n", debugstr_guid(clsid), debugstr_guid(iid), out); + + if (IsEqualGUID(clsid, &CLSID_DMOWrapperFilter)) + return IClassFactory_QueryInterface(&dmo_wrapper_cf.IClassFactory_iface, iid, out); + + FIXME("%s not available, returning CLASS_E_CLASSNOTAVAILABLE.\n", debugstr_guid(clsid)); + return CLASS_E_CLASSNOTAVAILABLE; +} + +HRESULT WINAPI DllCanUnloadNow(void) +{ + return S_FALSE; +} + +HRESULT WINAPI DllRegisterServer(void) +{ + return __wine_register_resources(qasf_instance); +} + +HRESULT WINAPI DllUnregisterServer(void) +{ + return __wine_unregister_resources(qasf_instance); +} diff --git a/dlls/qasf/qasf_private.h b/dlls/qasf/qasf_private.h new file mode 100644 index 00000000000..bdb2e433b2f --- /dev/null +++ b/dlls/qasf/qasf_private.h @@ -0,0 +1,34 @@ +/* + * DirectShow ASF filters + * + * Copyright (C) 2019 Zebediah Figura + * + * 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 + */ + +#ifndef QASF_PRIVATE_H +#define QASF_PRIVATE_H + +#define COBJMACROS +#include "dshow.h" +#include "dmo.h" +#include "dmodshow.h" +#include "rpcproxy.h" +#include "wine/debug.h" +#include "wine/strmbase.h" + +HRESULT dmo_wrapper_create(IUnknown *outer, IUnknown **out) DECLSPEC_HIDDEN; + +#endif /* QASF_PRIVATE_H */ diff --git a/dlls/qasf/tests/Makefile.in b/dlls/qasf/tests/Makefile.in new file mode 100644 index 00000000000..dd04f4b438f --- /dev/null +++ b/dlls/qasf/tests/Makefile.in @@ -0,0 +1,5 @@ +TESTDLL = qasf.dll +IMPORTS = dmoguids strmiids uuid ole32 + +C_SRCS = \ + dmowrapper.c diff --git a/dlls/qasf/tests/dmowrapper.c b/dlls/qasf/tests/dmowrapper.c new file mode 100644 index 00000000000..397980b3760 --- /dev/null +++ b/dlls/qasf/tests/dmowrapper.c @@ -0,0 +1,182 @@ +/* + * DMO wrapper filter unit tests + * + * Copyright (C) 2019 Zebediah Figura + * + * 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 "dmo.h" +#include "dmodshow.h" +#include "wine/test.h" + +static const GUID test_iid = {0x33333333}; + +static IBaseFilter *create_dmo_wrapper(void) +{ + IBaseFilter *filter = NULL; + HRESULT hr = CoCreateInstance(&CLSID_DMOWrapperFilter, NULL, + CLSCTX_INPROC_SERVER, &IID_IBaseFilter, (void **)&filter); + ok(hr == S_OK, "Got hr %#x.\n", hr); + return filter; +} + +static ULONG get_refcount(void *iface) +{ + IUnknown *unknown = iface; + IUnknown_AddRef(unknown); + return IUnknown_Release(unknown); +} + +#define check_interface(a, b, c) check_interface_(__LINE__, a, b, c) +static void check_interface_(unsigned int line, void *iface_ptr, REFIID iid, BOOL supported) +{ + IUnknown *iface = iface_ptr; + HRESULT hr, expected_hr; + IUnknown *unk; + + expected_hr = supported ? S_OK : E_NOINTERFACE; + + hr = IUnknown_QueryInterface(iface, iid, (void **)&unk); + ok_(__FILE__, line)(hr == expected_hr, "Got hr %#x, expected %#x.\n", hr, expected_hr); + if (SUCCEEDED(hr)) + IUnknown_Release(unk); +} + +static void test_interfaces(void) +{ + IBaseFilter *filter = create_dmo_wrapper(); + + check_interface(filter, &IID_IBaseFilter, TRUE); + todo_wine check_interface(filter, &IID_IDMOWrapperFilter, TRUE); + check_interface(filter, &IID_IMediaFilter, TRUE); + check_interface(filter, &IID_IPersist, TRUE); + todo_wine check_interface(filter, &IID_IPersistStream, TRUE); + check_interface(filter, &IID_IUnknown, TRUE); + + check_interface(filter, &IID_IAMFilterMiscFlags, FALSE); + check_interface(filter, &IID_IBasicAudio, FALSE); + check_interface(filter, &IID_IBasicVideo, FALSE); + check_interface(filter, &IID_IKsPropertySet, FALSE); + check_interface(filter, &IID_IMediaPosition, FALSE); + check_interface(filter, &IID_IMediaSeeking, FALSE); + check_interface(filter, &IID_IPersistPropertyBag, FALSE); + check_interface(filter, &IID_IPin, FALSE); + check_interface(filter, &IID_IQualityControl, FALSE); + check_interface(filter, &IID_IQualProp, FALSE); + check_interface(filter, &IID_IReferenceClock, FALSE); + check_interface(filter, &IID_IVideoWindow, FALSE); + + IBaseFilter_Release(filter); +} + +static HRESULT WINAPI outer_QueryInterface(IUnknown *iface, REFIID iid, void **out) +{ + ok(0, "Unexpected call.\n"); + return E_NOINTERFACE; +} + +static ULONG WINAPI outer_AddRef(IUnknown *iface) +{ + ok(0, "Unexpected call.\n"); + return 2; +} + +static ULONG WINAPI outer_Release(IUnknown *iface) +{ + ok(0, "Unexpected call.\n"); + return 1; +} + +static const IUnknownVtbl outer_vtbl = +{ + outer_QueryInterface, + outer_AddRef, + outer_Release, +}; + +static IUnknown test_outer = {&outer_vtbl}; + +static void test_aggregation(void) +{ + IBaseFilter *filter, *filter2; + IUnknown *unk, *unk2; + HRESULT hr; + ULONG ref; + + /* The DMO wrapper filter pretends to support aggregation, but doesn't + * actually aggregate anything. */ + + filter = (IBaseFilter *)0xdeadbeef; + hr = CoCreateInstance(&CLSID_DMOWrapperFilter, &test_outer, CLSCTX_INPROC_SERVER, + &IID_IBaseFilter, (void **)&filter); + ok(hr == E_NOINTERFACE, "Got hr %#x.\n", hr); + ok(!filter, "Got interface %p.\n", filter); + + hr = CoCreateInstance(&CLSID_DMOWrapperFilter, &test_outer, CLSCTX_INPROC_SERVER, + &IID_IUnknown, (void **)&unk); + ok(hr == S_OK, "Got hr %#x.\n", hr); + ok(unk != &test_outer, "Returned IUnknown should not be outer IUnknown.\n"); + ref = get_refcount(unk); + ok(ref == 1, "Got unexpected refcount %d.\n", ref); + + ref = IUnknown_AddRef(unk); + ok(ref == 2, "Got unexpected refcount %d.\n", ref); + + ref = IUnknown_Release(unk); + ok(ref == 1, "Got unexpected refcount %d.\n", ref); + + hr = IUnknown_QueryInterface(unk, &IID_IUnknown, (void **)&unk2); + ok(hr == S_OK, "Got hr %#x.\n", hr); + ok(unk2 == unk, "Got unexpected IUnknown %p.\n", unk2); + IUnknown_Release(unk2); + + hr = IUnknown_QueryInterface(unk, &IID_IBaseFilter, (void **)&filter); + ok(hr == S_OK, "Got hr %#x.\n", hr); + + hr = IBaseFilter_QueryInterface(filter, &IID_IUnknown, (void **)&unk2); + ok(hr == S_OK, "Got hr %#x.\n", hr); + ok(unk2 == unk, "Got unexpected IUnknown %p.\n", unk2); + IUnknown_Release(unk2); + + hr = IBaseFilter_QueryInterface(filter, &IID_IBaseFilter, (void **)&filter2); + ok(hr == S_OK, "Got hr %#x.\n", hr); + ok(filter2 == filter, "Got unexpected IBaseFilter %p.\n", filter2); + IBaseFilter_Release(filter2); + + hr = IUnknown_QueryInterface(unk, &test_iid, (void **)&unk2); + ok(hr == E_NOINTERFACE, "Got hr %#x.\n", hr); + ok(!unk2, "Got unexpected IUnknown %p.\n", unk2); + + hr = IBaseFilter_QueryInterface(filter, &test_iid, (void **)&unk2); + ok(hr == E_NOINTERFACE, "Got hr %#x.\n", hr); + ok(!unk2, "Got unexpected IUnknown %p.\n", unk2); + + IBaseFilter_Release(filter); + ref = IUnknown_Release(unk); + ok(!ref, "Got unexpected refcount %d.\n", ref); +} + +START_TEST(dmowrapper) +{ + CoInitializeEx(NULL, COINIT_MULTITHREADED); + + test_interfaces(); + test_aggregation(); + + CoUninitialize(); +}