diff --git a/dlls/quartz/tests/filtergraph.c b/dlls/quartz/tests/filtergraph.c index aec93926eba..d7ae766f117 100644 --- a/dlls/quartz/tests/filtergraph.c +++ b/dlls/quartz/tests/filtergraph.c @@ -46,31 +46,35 @@ static const WCHAR mpegfile[] = {'t','e','s','t','.','m','p','g',0}; static const WCHAR mp3file[] = {'t','e','s','t','.','m','p','3',0}; static const WCHAR wavefile[] = {'t','e','s','t','.','w','a','v',0}; -static WCHAR *load_resource(const WCHAR *name) +static WCHAR *create_file(const WCHAR *name, const char *data, DWORD size) { static WCHAR pathW[MAX_PATH]; DWORD written; HANDLE file; - HRSRC res; - void *ptr; GetTempPathW(ARRAY_SIZE(pathW), pathW); lstrcatW(pathW, name); - file = CreateFileW(pathW, GENERIC_READ|GENERIC_WRITE, 0, NULL, CREATE_ALWAYS, 0, 0); - ok(file != INVALID_HANDLE_VALUE, "file creation failed, at %s, error %d\n", wine_dbgstr_w(pathW), - GetLastError()); - - res = FindResourceW(NULL, name, (LPCWSTR)RT_RCDATA); - ok( res != 0, "couldn't find resource\n" ); - ptr = LockResource( LoadResource( GetModuleHandleA(NULL), res )); - WriteFile( file, ptr, SizeofResource( GetModuleHandleA(NULL), res ), &written, NULL ); - ok( written == SizeofResource( GetModuleHandleA(NULL), res ), "couldn't write resource\n" ); - CloseHandle( file ); + ok(file != INVALID_HANDLE_VALUE, "Failed to create file %s, error %u.\n", + wine_dbgstr_w(pathW), GetLastError()); + WriteFile(file, data, size, &written, NULL); + ok(written = size, "Failed to write file data, error %u.\n", GetLastError()); + CloseHandle(file); return pathW; } +static WCHAR *load_resource(const WCHAR *name) +{ + HRSRC res; + void *ptr; + + res = FindResourceW(NULL, name, (const WCHAR *)RT_RCDATA); + ok(!!res, "Failed to find resource %s, error %u.\n", wine_dbgstr_w(name), GetLastError()); + ptr = LockResource(LoadResource(GetModuleHandleA(NULL), res)); + return create_file(name, ptr, SizeofResource(GetModuleHandleA(NULL), res)); +} + static IFilterGraph2 *create_graph(void) { IFilterGraph2 *ret; @@ -1175,6 +1179,9 @@ struct testfilter double seek_rate; IReferenceClock IReferenceClock_iface; + + IFileSourceFilter IFileSourceFilter_iface; + WCHAR filename[MAX_PATH]; }; static inline struct testfilter *impl_from_IEnumPins(IEnumPins *iface) @@ -1267,30 +1274,31 @@ static HRESULT WINAPI testfilter_QueryInterface(IBaseFilter *iface, REFIID iid, || IsEqualGUID(iid, &IID_IBaseFilter)) { *out = &filter->IBaseFilter_iface; - IBaseFilter_AddRef(*out); - return S_OK; } else if (IsEqualGUID(iid, &IID_IAMFilterMiscFlags) && filter->IAMFilterMiscFlags_iface.lpVtbl) { *out = &filter->IAMFilterMiscFlags_iface; - IAMFilterMiscFlags_AddRef(*out); - return S_OK; } else if (IsEqualGUID(iid, &IID_IMediaSeeking) && filter->IMediaSeeking_iface.lpVtbl) { *out = &filter->IMediaSeeking_iface; - IMediaSeeking_AddRef(*out); - return S_OK; } else if (IsEqualGUID(iid, &IID_IReferenceClock) && filter->IReferenceClock_iface.lpVtbl) { *out = &filter->IReferenceClock_iface; - IMediaSeeking_AddRef(*out); - return S_OK; + } + else if (IsEqualGUID(iid, &IID_IFileSourceFilter) && filter->IFileSourceFilter_iface.lpVtbl) + { + *out = &filter->IFileSourceFilter_iface; + } + else + { + *out = NULL; + return E_NOINTERFACE; } - *out = NULL; - return E_NOINTERFACE; + IUnknown_AddRef((IUnknown *)*out); + return S_OK; } static ULONG WINAPI testfilter_AddRef(IBaseFilter *iface) @@ -1742,6 +1750,56 @@ static const IReferenceClockVtbl testclock_vtbl = testclock_Unadvise, }; +static struct testfilter *impl_from_IFileSourceFilter(IFileSourceFilter *iface) +{ + return CONTAINING_RECORD(iface, struct testfilter, IFileSourceFilter_iface); +} + +static HRESULT WINAPI testfilesource_QueryInterface(IFileSourceFilter *iface, REFIID iid, void **out) +{ + struct testfilter *filter = impl_from_IFileSourceFilter(iface); + return IBaseFilter_QueryInterface(&filter->IBaseFilter_iface, iid, out); +} + +static ULONG WINAPI testfilesource_AddRef(IFileSourceFilter *iface) +{ + struct testfilter *filter = impl_from_IFileSourceFilter(iface); + return InterlockedIncrement(&filter->ref); +} + +static ULONG WINAPI testfilesource_Release(IFileSourceFilter *iface) +{ + struct testfilter *filter = impl_from_IFileSourceFilter(iface); + return InterlockedDecrement(&filter->ref); +} + +static HRESULT WINAPI testfilesource_Load(IFileSourceFilter *iface, + const WCHAR *filename, const AM_MEDIA_TYPE *mt) +{ + struct testfilter *filter = impl_from_IFileSourceFilter(iface); + if (winetest_debug > 1) trace("%p->Load()\n", iface); + + wcscpy(filter->filename, filename); + + return S_OK; +} + +static HRESULT WINAPI testfilesource_GetCurFile(IFileSourceFilter *iface, + WCHAR **filename, AM_MEDIA_TYPE *mt) +{ + ok(0, "Unexpected call.\n"); + return E_NOTIMPL; +} + +static const IFileSourceFilterVtbl testfilesource_vtbl = +{ + testfilesource_QueryInterface, + testfilesource_AddRef, + testfilesource_Release, + testfilesource_Load, + testfilesource_GetCurFile, +}; + struct testfilter_cf { IClassFactory IClassFactory_iface; @@ -3827,6 +3885,137 @@ static void test_default_sync_source(void) ok(source.ref == 1, "Got outstanding refcount %d.\n", source.ref); } +static void test_add_source_filter(void) +{ + static const char bogus_data[20] = {0xde, 0xad, 0xbe, 0xef}; + static const char midi_data[20] = {'M','T','h','d'}; + static const WCHAR testW[] = {'t','e','s','t',0}; + + IFilterGraph2 *graph = create_graph(); + IFileSourceFilter *filesource; + IBaseFilter *filter, *filter2; + FILTER_INFO filter_info; + const WCHAR *filename; + WCHAR *ret_filename; + AM_MEDIA_TYPE mt; + CLSID clsid; + HRESULT hr; + ULONG ref; + BOOL ret; + HKEY key; + + /* Test a file which should be registered by extension. */ + + filename = create_file(mp3file, midi_data, sizeof(midi_data)); + hr = IFilterGraph2_AddSourceFilter(graph, filename, testW, &filter); + ok(hr == S_OK, "Got hr %#x.\n", hr); + + hr = IBaseFilter_GetClassID(filter, &clsid); + ok(hr == S_OK, "Got hr %#x.\n", hr); + ok(IsEqualGUID(&clsid, &CLSID_AsyncReader), "Got filter %s.\n", wine_dbgstr_guid(&clsid)); + hr = IBaseFilter_QueryFilterInfo(filter, &filter_info); + ok(hr == S_OK, "Got hr %#x.\n", hr); + ok(!wcscmp(filter_info.achName, testW), "Got unexpected name %s.\n", wine_dbgstr_w(filter_info.achName)); + IFilterGraph_Release(filter_info.pGraph); + + hr = IBaseFilter_QueryInterface(filter, &IID_IFileSourceFilter, (void **)&filesource); + ok(hr == S_OK, "Got hr %#x.\n", hr); + hr = IFileSourceFilter_GetCurFile(filesource, &ret_filename, &mt); + ok(hr == S_OK, "Got hr %#x.\n", hr); + ok(!wcscmp(ret_filename, filename), "Expected filename %s, got %s.\n", + wine_dbgstr_w(filename), wine_dbgstr_w(ret_filename)); + ok(IsEqualGUID(&mt.majortype, &MEDIATYPE_Stream), "Got major type %s.\n", wine_dbgstr_guid(&mt.majortype)); + ok(IsEqualGUID(&mt.subtype, &MEDIASUBTYPE_MPEG1Audio), "Got subtype %s.\n", wine_dbgstr_guid(&mt.subtype)); + IFileSourceFilter_Release(filesource); + + hr = IFilterGraph2_AddSourceFilter(graph, filename, testW, &filter2); + ok(hr == S_OK, "Got hr %#x.\n", hr); + ok(filter2 != filter, "Filters shouldn't match.\n"); + hr = IFilterGraph2_RemoveFilter(graph, filter2); + ok(hr == S_OK, "Got hr %#x.\n", hr); + ref = IBaseFilter_Release(filter2); + ok(!ref, "Got outstanding refcount %d.\n", ref); + + hr = IFilterGraph2_RemoveFilter(graph, filter); + ok(hr == S_OK, "Got hr %#x.\n", hr); + ref = IBaseFilter_Release(filter); + ok(!ref, "Got outstanding refcount %d.\n", ref); + ret = DeleteFileW(filename); + ok(ret, "Failed to delete %s, error %u.\n", wine_dbgstr_w(filename), GetLastError()); + + /* Test a file which should be registered by signature. */ + + filename = create_file(avifile, midi_data, sizeof(midi_data)); + hr = IFilterGraph2_AddSourceFilter(graph, filename, NULL, &filter); + ok(hr == S_OK, "Got hr %#x.\n", hr); + + hr = IBaseFilter_GetClassID(filter, &clsid); + ok(hr == S_OK, "Got hr %#x.\n", hr); + ok(IsEqualGUID(&clsid, &CLSID_AsyncReader), "Got filter %s.\n", wine_dbgstr_guid(&clsid)); + hr = IBaseFilter_QueryFilterInfo(filter, &filter_info); + ok(hr == S_OK, "Got hr %#x.\n", hr); + todo_wine ok(!wcscmp(filter_info.achName, filename), "Got unexpected name %s.\n", wine_dbgstr_w(filter_info.achName)); + IFilterGraph_Release(filter_info.pGraph); + + hr = IBaseFilter_QueryInterface(filter, &IID_IFileSourceFilter, (void **)&filesource); + ok(hr == S_OK, "Got hr %#x.\n", hr); + hr = IFileSourceFilter_GetCurFile(filesource, &ret_filename, &mt); + ok(hr == S_OK, "Got hr %#x.\n", hr); + ok(!wcscmp(ret_filename, filename), "Expected filename %s, got %s.\n", + wine_dbgstr_w(filename), wine_dbgstr_w(ret_filename)); + ok(IsEqualGUID(&mt.majortype, &MEDIATYPE_Stream), "Got major type %s.\n", wine_dbgstr_guid(&mt.majortype)); + ok(IsEqualGUID(&mt.subtype, &MEDIATYPE_Midi), "Got subtype %s.\n", wine_dbgstr_guid(&mt.subtype)); + IFileSourceFilter_Release(filesource); + + hr = IFilterGraph2_RemoveFilter(graph, filter); + ok(hr == S_OK, "Got hr %#x.\n", hr); + ref = IBaseFilter_Release(filter); + ok(!ref, "Got outstanding refcount %d.\n", ref); + ret = DeleteFileW(filename); + ok(ret, "Failed to delete %s, error %u.\n", wine_dbgstr_w(filename), GetLastError()); + + if (!RegCreateKeyA(HKEY_CLASSES_ROOT, "Media Type\\{abbccdde-0000-0000-0000-000000000000}" + "\\{bccddeef-0000-0000-0000-000000000000}", &key)) + { + static const GUID testfilter_clsid = {0x12345678}; + struct testpin testfilter_pin; + struct testfilter testfilter; + struct testfilter_cf cf = {{&testfilter_cf_vtbl}, &testfilter}; + DWORD cookie; + + testsource_init(&testfilter_pin, NULL, 0); + testfilter_init(&testfilter, &testfilter_pin, 1); + testfilter.IFileSourceFilter_iface.lpVtbl = &testfilesource_vtbl; + + CoRegisterClassObject(&testfilter_clsid, (IUnknown *)&cf.IClassFactory_iface, + CLSCTX_INPROC_SERVER, REGCLS_MULTIPLEUSE, &cookie); + RegSetValueExA(key, "0", 0, REG_SZ, (const BYTE *)"0,4,,deadbeef", 14); + RegSetValueExA(key, "Source Filter", 0, REG_SZ, (const BYTE *)"{12345678-0000-0000-0000-000000000000}", 39); + + filename = create_file(avifile, bogus_data, sizeof(bogus_data)); + hr = IFilterGraph2_AddSourceFilter(graph, filename, NULL, &filter); + ok(hr == S_OK, "Got hr %#x.\n", hr); + ok(filter == &testfilter.IBaseFilter_iface, "Got unexpected filter %p.\n", filter); + + hr = IFilterGraph2_RemoveFilter(graph, filter); + ok(hr == S_OK, "Got hr %#x.\n", hr); + IBaseFilter_Release(filter); + ref = IBaseFilter_Release(&testfilter.IBaseFilter_iface); + ok(!ref, "Got outstanding refcount %d.\n", ref); + ret = DeleteFileW(filename); + ok(ret, "Failed to delete %s, error %u.\n", wine_dbgstr_w(filename), GetLastError()); + RegDeleteKeyA(HKEY_CLASSES_ROOT, "Media Type\\{abbccdde-0000-0000-0000-000000000000}" + "\\{bccddeef-0000-0000-0000-000000000000}"); + RegDeleteKeyA(HKEY_CLASSES_ROOT, "Media Type\\{abbccdde-0000-0000-0000-000000000000}"); + CoRevokeClassObject(cookie); + } + else + skip("Not enough permission to register media types.\n"); + + ref = IFilterGraph2_Release(graph); + ok(!ref, "Got outstanding refcount %d.\n", ref); +} + START_TEST(filtergraph) { CoInitializeEx(NULL, COINIT_MULTITHREADED); @@ -3848,6 +4037,7 @@ START_TEST(filtergraph) test_ec_complete(); test_graph_seeking(); test_default_sync_source(); + test_add_source_filter(); CoUninitialize(); test_render_with_multithread();