-- v2: qasf: Implement ASF Reader filter stream stubs. qasf: Create and open a WMReader in the ASF Reader filter. qasf: Rename variables and functions. qasf: Constify filter_ops. qasf/tests: Add more ASF Reader filter tests.
From: Rémi Bernon rbernon@codeweavers.com
Signed-off-by: Rémi Bernon rbernon@codeweavers.com --- dlls/qasf/tests/Makefile.in | 4 +- dlls/qasf/tests/asfreader.c | 234 +++++++++++++++++++++++++++++++++++- dlls/qasf/tests/resource.rc | 25 ++++ dlls/qasf/tests/test.wmv | Bin 0 -> 45663 bytes 4 files changed, 258 insertions(+), 5 deletions(-) create mode 100644 dlls/qasf/tests/resource.rc create mode 100644 dlls/qasf/tests/test.wmv
diff --git a/dlls/qasf/tests/Makefile.in b/dlls/qasf/tests/Makefile.in index 4b8d40671fb..e68b97c0408 100644 --- a/dlls/qasf/tests/Makefile.in +++ b/dlls/qasf/tests/Makefile.in @@ -1,6 +1,8 @@ TESTDLL = qasf.dll -IMPORTS = strmbase dmoguids strmiids uuid msdmo ole32 +IMPORTS = strmbase dmoguids strmiids uuid wmvcore msdmo ole32
C_SRCS = \ asfreader.c \ dmowrapper.c + +RC_SRCS = resource.rc diff --git a/dlls/qasf/tests/asfreader.c b/dlls/qasf/tests/asfreader.c index f4f8b9e2e19..a2b56ee29de 100644 --- a/dlls/qasf/tests/asfreader.c +++ b/dlls/qasf/tests/asfreader.c @@ -22,17 +22,19 @@ #define COBJMACROS
#include "dshow.h" +#include "mediaobj.h" +#include "propsys.h" +#include "dvdmedia.h" #include "wine/strmbase.h" #include "wine/test.h"
#include "initguid.h" +#include "wmsdk.h" +#include "wmcodecdsp.h"
+DEFINE_GUID(WMMEDIASUBTYPE_WMV1,0x31564d57,0x0000,0x0010,0x80,0x00,0x00,0xaa,0x00,0x38,0x9b,0x71); static const GUID testguid = {0x22222222, 0x2222, 0x2222, {0x22, 0x22, 0x22, 0x22, 0x22, 0x22, 0x22, 0x22}};
-DEFINE_GUID(IID_IWMHeaderInfo, 0x96406bda, 0x2b2b, 0x11d3, 0xb3, 0x6b, 0x00, 0xc0, 0x4f, 0x61, 0x08, 0xff); -DEFINE_GUID(IID_IWMReaderAdvanced, 0x96406bea, 0x2b2b, 0x11d3, 0xb3, 0x6b, 0x00, 0xc0, 0x4f, 0x61, 0x08, 0xff); -DEFINE_GUID(IID_IWMReaderAdvanced2, 0xae14a945, 0xb90c, 0x4d0d, 0x91, 0x27, 0x80, 0xd6, 0x65, 0xf7, 0xd7, 0x3e); - static IBaseFilter *create_asf_reader(void) { IBaseFilter *filter = NULL; @@ -45,6 +47,31 @@ static IBaseFilter *create_asf_reader(void) return filter; }
+static WCHAR *load_resource(const WCHAR *name) +{ + static WCHAR pathW[MAX_PATH]; + DWORD written; + HANDLE file; + HRSRC res; + void *ptr; + + GetTempPathW(ARRAY_SIZE(pathW), pathW); + wcscat(pathW, name); + + file = CreateFileW(pathW, GENERIC_READ|GENERIC_WRITE, 0, NULL, CREATE_ALWAYS, 0, 0); + ok(file != INVALID_HANDLE_VALUE, "Failed to create file %s, error %lu.\n", + wine_dbgstr_w(pathW), GetLastError()); + + res = FindResourceW(NULL, name, (LPCWSTR)RT_RCDATA); + ok(!!res, "Failed to load resource, error %lu.\n", GetLastError()); + ptr = LockResource(LoadResource(GetModuleHandleA(NULL), res)); + WriteFile(file, ptr, SizeofResource( GetModuleHandleA(NULL), res), &written, NULL); + ok(written == SizeofResource(GetModuleHandleA(NULL), res), "Failed to write resource.\n"); + CloseHandle(file); + + return pathW; +} + static ULONG get_refcount(void *iface) { IUnknown *unknown = iface; @@ -183,8 +210,147 @@ static void test_aggregation(void) ok(outer_ref == 1, "Got unexpected refcount %ld.\n", outer_ref); }
+static inline BOOL compare_media_types(const AM_MEDIA_TYPE *a, const AM_MEDIA_TYPE *b) +{ + return !memcmp(a, b, offsetof(AM_MEDIA_TYPE, pbFormat)) + && !memcmp(a->pbFormat, b->pbFormat, a->cbFormat); +} + +static inline void check_pin(IPin *pin, IBaseFilter *expect_filter, PIN_DIRECTION expect_dir, + const WCHAR *expect_name, const WCHAR *expect_id, AM_MEDIA_TYPE *expect_mt, UINT expect_mt_count) +{ + IEnumMediaTypes *enum_mt; + AM_MEDIA_TYPE *mt; + PIN_INFO info; + HRESULT hr; + WCHAR *id; + UINT i; + + hr = IPin_QueryPinInfo(pin, &info); + ok(hr == S_OK, "Got hr %#lx.\n", hr); + ok(info.pFilter == expect_filter, "Got filter %p\n", info.pFilter); + ok(info.dir == expect_dir, "Got dir %u\n", info.dir); + todo_wine + ok(!wcscmp(info.achName, expect_name), "Got name %s\n", wine_dbgstr_w(info.achName)); + IBaseFilter_Release(info.pFilter); + + hr = IPin_QueryId(pin, &id); + ok(hr == S_OK, "Got hr %#lx.\n", hr); + todo_wine + ok(!wcscmp(id, expect_id), "Got id %s\n", wine_dbgstr_w(id)); + CoTaskMemFree(id); + + hr = IPin_EnumMediaTypes(pin, &enum_mt); + todo_wine + ok(hr == S_OK, "Got hr %#lx.\n", hr); + if (hr != S_OK) goto skip_enum_mt; + hr = IEnumMediaTypes_Next(enum_mt, 1, &mt, NULL); + for (i = 0; hr == S_OK; hr = IEnumMediaTypes_Next(enum_mt, 1, &mt, NULL), i++) + { + if (i < expect_mt_count) + { + todo_wine + ok(compare_media_types(mt, expect_mt + i), "Media type %u didn't match.\n", i); + } + FreeMediaType(mt); + CoTaskMemFree(mt); + } + todo_wine + ok(i == expect_mt_count, "Got %u types\n", i); + ok(hr == S_FALSE, "Got hr %#lx.\n", hr); + IEnumMediaTypes_Release(enum_mt); + +skip_enum_mt: + check_interface(pin, &IID_IPin, TRUE); + check_interface(pin, &IID_IUnknown, TRUE); + todo_wine + check_interface(pin, &IID_IMediaSeeking, TRUE); + todo_wine + check_interface(pin, &IID_IQualityControl, TRUE); + check_interface(pin, &IID_IServiceProvider, FALSE); + check_interface(pin, &IID_IWMStreamConfig, FALSE); +} + static void test_filesourcefilter(void) { + static const MSAUDIO1WAVEFORMAT msaudio1_format = + { + .wfx.wFormatTag = WAVE_FORMAT_MSAUDIO1, + .wfx.nChannels = 1, + .wfx.nSamplesPerSec = 44100, + .wfx.nBlockAlign = 743, + .wfx.nAvgBytesPerSec = 16000, + .wfx.wBitsPerSample = 16, + .wfx.cbSize = sizeof(MSAUDIO1WAVEFORMAT) - sizeof(WAVEFORMATEX), + .wEncodeOptions = 1, + }; + static const VIDEOINFOHEADER2 wmv1_info2 = + { + .rcSource = {0, 0, 64, 48}, + .rcTarget = {0, 0, 64, 48}, + .dwBitRate = 189464, + .dwPictAspectRatioX = 64, + .dwPictAspectRatioY = 48, + .bmiHeader = + { + .biSize = sizeof(BITMAPINFOHEADER), + .biWidth = 64, + .biHeight = 48, + .biPlanes = 1, + .biBitCount = 24, + .biCompression = MAKEFOURCC('W','M','V','1'), + .biSizeImage = 64 * 48 * 3, + }, + }; + static const VIDEOINFOHEADER wmv1_info = + { + .rcSource = {0, 0, 64, 48}, + .rcTarget = {0, 0, 64, 48}, + .dwBitRate = 189464, + .bmiHeader = + { + .biSize = sizeof(BITMAPINFOHEADER), + .biWidth = 64, + .biHeight = 48, + .biPlanes = 1, + .biBitCount = 24, + .biCompression = MAKEFOURCC('W','M','V','1'), + .biSizeImage = 64 * 48 * 3, + }, + }; + AM_MEDIA_TYPE audio_mt[] = + { + { + .majortype = MEDIATYPE_Audio, + .subtype = MEDIASUBTYPE_MSAUDIO1, + .bFixedSizeSamples = TRUE, + .lSampleSize = 743, + .formattype = FORMAT_WaveFormatEx, + .cbFormat = sizeof(MSAUDIO1WAVEFORMAT), + .pbFormat = (BYTE *)&msaudio1_format, + }, + }; + AM_MEDIA_TYPE video_mt[] = + { + { + .majortype = MEDIATYPE_Video, + .subtype = WMMEDIASUBTYPE_WMV1, + .formattype = FORMAT_VideoInfo2, + .bTemporalCompression = TRUE, + .cbFormat = sizeof(VIDEOINFOHEADER2), + .pbFormat = (BYTE *)&wmv1_info2, + }, + { + .majortype = MEDIATYPE_Video, + .subtype = WMMEDIASUBTYPE_WMV1, + .formattype = FORMAT_VideoInfo, + .bTemporalCompression = TRUE, + .cbFormat = sizeof(VIDEOINFOHEADER), + .pbFormat = (BYTE *)&wmv1_info, + }, + }; + + const WCHAR *filename = load_resource(L"test.wmv"); IBaseFilter *filter = create_asf_reader(); IFileSourceFilter *filesource; IFilterGraph2 *graph; @@ -289,6 +455,66 @@ static void test_filesourcefilter(void) ok(type.pbFormat == ptr, "Got format block %p.\n", type.pbFormat); CoTaskMemFree(olepath);
+ hr = IFileSourceFilter_Load(filesource, filename, NULL); + ok(hr == E_FAIL, "Got hr %#lx.\n", hr); + + ref = IFilterGraph2_Release(graph); + ok(!ref, "Got outstanding refcount %ld.\n", ref); + IBaseFilter_Release(filter); + ref = IFileSourceFilter_Release(filesource); + ok(!ref, "Got outstanding refcount %ld.\n", ref); + + + filter = create_asf_reader(); + hr = IBaseFilter_QueryInterface(filter, &IID_IFileSourceFilter, (void **)&filesource); + ok(hr == S_OK, "Got hr %#lx.\n", hr); + hr = IFileSourceFilter_Load(filesource, filename, NULL); + ok(hr == S_OK, "Got hr %#lx.\n", hr); + + hr = CoCreateInstance(&CLSID_FilterGraph, NULL, CLSCTX_INPROC_SERVER, + &IID_IFilterGraph2, (void **)&graph); + ok(hr == S_OK, "Got hr %#lx.\n", hr); + hr = IFilterGraph2_AddFilter(graph, filter, NULL); + ok(hr == S_OK, "Got hr %#lx.\n", hr); + + olepath = (void *)0xdeadbeef; + memset(&type, 0x22, sizeof(type)); + hr = IFileSourceFilter_GetCurFile(filesource, &olepath, &type); + ok(hr == S_OK, "Got hr %#lx.\n", hr); + ok(!wcscmp(olepath, filename), "Got file %s.\n", wine_dbgstr_w(olepath)); + ok(IsEqualGUID(&type.majortype, &MEDIATYPE_NULL), "Got majortype %s.\n", + wine_dbgstr_guid(&type.majortype)); + ok(IsEqualGUID(&type.subtype, &MEDIASUBTYPE_NULL), "Got subtype %s.\n", + wine_dbgstr_guid(&type.subtype)); + ok(type.bFixedSizeSamples == 0x22222222, "Got fixed size %d.\n", type.bFixedSizeSamples); + ok(type.bTemporalCompression == 0x22222222, "Got temporal compression %d.\n", type.bTemporalCompression); + ok(!type.lSampleSize, "Got sample size %lu.\n", type.lSampleSize); + ok(IsEqualGUID(&type.formattype, &testguid), "Got format type %s.\n", wine_dbgstr_guid(&type.formattype)); + ok(!type.pUnk, "Got pUnk %p.\n", type.pUnk); + ok(!type.cbFormat, "Got format size %lu.\n", type.cbFormat); + ok(type.pbFormat == ptr, "Got format block %p.\n", type.pbFormat); + CoTaskMemFree(olepath); + + hr = IBaseFilter_EnumPins(filter, &enumpins); + ok(hr == S_OK, "Got hr %#lx.\n", hr); + + hr = IEnumPins_Next(enumpins, 1, pins, NULL); + todo_wine + ok(hr == S_OK, "Got hr %#lx.\n", hr); + if (hr != S_OK) goto skip_pins; + check_pin(pins[0], filter, PINDIR_OUTPUT, L"Raw Video 0", L"Raw Video 0", video_mt, ARRAY_SIZE(video_mt)); + IPin_Release(pins[0]); + + hr = IEnumPins_Next(enumpins, 1, pins, NULL); + ok(hr == S_OK, "Got hr %#lx.\n", hr); + check_pin(pins[0], filter, PINDIR_OUTPUT, L"Raw Audio 1", L"Raw Audio 1", audio_mt, ARRAY_SIZE(audio_mt)); + IPin_Release(pins[0]); + + hr = IEnumPins_Next(enumpins, 1, pins, NULL); +skip_pins: + ok(hr == S_FALSE, "Got hr %#lx.\n", hr); + IEnumPins_Release(enumpins); + ref = IFilterGraph2_Release(graph); ok(!ref, "Got outstanding refcount %ld.\n", ref); IBaseFilter_Release(filter); diff --git a/dlls/qasf/tests/resource.rc b/dlls/qasf/tests/resource.rc new file mode 100644 index 00000000000..2d5184b3f73 --- /dev/null +++ b/dlls/qasf/tests/resource.rc @@ -0,0 +1,25 @@ +/* + * Resource file for qasf tests. + * + * Copyright 2022 Rémi Bernon for CodeWeavers + * + * 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 "windef.h" + +/* ffmpeg -f lavfi -i smptebars -f lavfi -i "sine=frequency=700" -t 2.0 -f asf -vcodec wmv1 -vf scale=64x48 -acodec wmav1 test.wmv */ +/* @makedep: test.wmv */ +test.wmv RCDATA test.wmv diff --git a/dlls/qasf/tests/test.wmv b/dlls/qasf/tests/test.wmv new file mode 100644 index 0000000000000000000000000000000000000000..fa953771cd31213ec24bef9000127125047e0623 GIT binary patch literal 45663 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
literal 0 HcmV?d00001
From: Rémi Bernon rbernon@codeweavers.com
Signed-off-by: Rémi Bernon rbernon@codeweavers.com --- dlls/qasf/asfreader.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/dlls/qasf/asfreader.c b/dlls/qasf/asfreader.c index a037728079f..ba782f19ad9 100644 --- a/dlls/qasf/asfreader.c +++ b/dlls/qasf/asfreader.c @@ -66,7 +66,7 @@ static HRESULT asf_reader_query_interface(struct strmbase_filter *iface, REFIID return E_NOINTERFACE; }
-static struct strmbase_filter_ops filter_ops = +static const struct strmbase_filter_ops filter_ops = { .filter_get_pin = asf_reader_get_pin, .filter_destroy = asf_reader_destroy,
From: Rémi Bernon rbernon@codeweavers.com
* filesourcefilter_ prefix to file_source_.
* type to media_type.
* filename to file_name.
* impl_reader_from_strmbase_filter to impl_from_strmbase_filter.
Signed-off-by: Rémi Bernon rbernon@codeweavers.com --- dlls/qasf/asfreader.c | 76 +++++++++++++++++++++---------------------- 1 file changed, 38 insertions(+), 38 deletions(-)
diff --git a/dlls/qasf/asfreader.c b/dlls/qasf/asfreader.c index ba782f19ad9..a657b719456 100644 --- a/dlls/qasf/asfreader.c +++ b/dlls/qasf/asfreader.c @@ -27,11 +27,11 @@ struct asf_reader struct strmbase_filter filter; IFileSourceFilter IFileSourceFilter_iface;
- AM_MEDIA_TYPE type; - WCHAR *filename; + AM_MEDIA_TYPE media_type; + WCHAR *file_name; };
-static inline struct asf_reader *impl_reader_from_strmbase_filter(struct strmbase_filter *iface) +static inline struct asf_reader *impl_from_strmbase_filter(struct strmbase_filter *iface) { return CONTAINING_RECORD(iface, struct asf_reader, filter); } @@ -43,10 +43,10 @@ static struct strmbase_pin *asf_reader_get_pin(struct strmbase_filter *iface, un
static void asf_reader_destroy(struct strmbase_filter *iface) { - struct asf_reader *filter = impl_reader_from_strmbase_filter(iface); + struct asf_reader *filter = impl_from_strmbase_filter(iface);
- free(filter->filename); - FreeMediaType(&filter->type); + free(filter->file_name); + FreeMediaType(&filter->media_type);
strmbase_filter_cleanup(&filter->filter); free(filter); @@ -54,7 +54,7 @@ static void asf_reader_destroy(struct strmbase_filter *iface)
static HRESULT asf_reader_query_interface(struct strmbase_filter *iface, REFIID iid, void **out) { - struct asf_reader *filter = impl_reader_from_strmbase_filter(iface); + struct asf_reader *filter = impl_from_strmbase_filter(iface);
if (IsEqualGUID(iid, &IID_IFileSourceFilter)) { @@ -78,84 +78,84 @@ static inline struct asf_reader *impl_from_IFileSourceFilter(IFileSourceFilter * return CONTAINING_RECORD(iface, struct asf_reader, IFileSourceFilter_iface); }
-static HRESULT WINAPI filesourcefilter_QueryInterface(IFileSourceFilter *iface, REFIID iid, void **out) +static HRESULT WINAPI file_source_QueryInterface(IFileSourceFilter *iface, REFIID iid, void **out) { struct asf_reader *filter = impl_from_IFileSourceFilter(iface);
return IBaseFilter_QueryInterface(&filter->filter.IBaseFilter_iface, iid, out); }
-static ULONG WINAPI filesourcefilter_AddRef(IFileSourceFilter *iface) +static ULONG WINAPI file_source_AddRef(IFileSourceFilter *iface) { struct asf_reader *filter = impl_from_IFileSourceFilter(iface);
return IBaseFilter_AddRef(&filter->filter.IBaseFilter_iface); }
-static ULONG WINAPI filesourcefilter_Release(IFileSourceFilter *iface) +static ULONG WINAPI file_source_Release(IFileSourceFilter *iface) { struct asf_reader *filter = impl_from_IFileSourceFilter(iface);
return IBaseFilter_Release(&filter->filter.IBaseFilter_iface); }
-static HRESULT WINAPI filesourcefilter_Load(IFileSourceFilter *iface, LPCOLESTR filename, const AM_MEDIA_TYPE *type) +static HRESULT WINAPI file_source_Load(IFileSourceFilter *iface, LPCOLESTR file_name, const AM_MEDIA_TYPE *media_type) { struct asf_reader *filter = impl_from_IFileSourceFilter(iface);
- TRACE("filter %p, filename %s, type %p.\n", filter, debugstr_w(filename), type); - strmbase_dump_media_type(type); + TRACE("filter %p, file_name %s, media_type %p.\n", filter, debugstr_w(file_name), media_type); + strmbase_dump_media_type(media_type);
- if (!filename) + if (!file_name) return E_POINTER;
- if (filter->filename) + if (filter->file_name) return E_FAIL;
- if (!(filter->filename = wcsdup(filename))) + if (!(filter->file_name = wcsdup(file_name))) return E_OUTOFMEMORY;
- if (type) - CopyMediaType(&filter->type, type); + if (media_type) + CopyMediaType(&filter->media_type, media_type);
return S_OK; }
-static HRESULT WINAPI filesourcefilter_GetCurFile(IFileSourceFilter *iface, LPOLESTR *filename, AM_MEDIA_TYPE *type) +static HRESULT WINAPI file_source_GetCurFile(IFileSourceFilter *iface, LPOLESTR *file_name, AM_MEDIA_TYPE *media_type) { struct asf_reader *filter = impl_from_IFileSourceFilter(iface);
- TRACE("filter %p, filename %p, type %p.\n", filter, filename, type); + TRACE("filter %p, file_name %p, media_type %p.\n", filter, file_name, media_type);
- if (!filename) + if (!file_name) return E_POINTER; - *filename = NULL; + *file_name = NULL;
- if (type) + if (media_type) { - type->majortype = filter->type.majortype; - type->subtype = filter->type.subtype; - type->lSampleSize = filter->type.lSampleSize; - type->pUnk = filter->type.pUnk; - type->cbFormat = filter->type.cbFormat; + media_type->majortype = filter->media_type.majortype; + media_type->subtype = filter->media_type.subtype; + media_type->lSampleSize = filter->media_type.lSampleSize; + media_type->pUnk = filter->media_type.pUnk; + media_type->cbFormat = filter->media_type.cbFormat; }
- if (filter->filename) + if (filter->file_name) { - *filename = CoTaskMemAlloc((wcslen(filter->filename) + 1) * sizeof(WCHAR)); - wcscpy(*filename, filter->filename); + *file_name = CoTaskMemAlloc((wcslen(filter->file_name) + 1) * sizeof(WCHAR)); + wcscpy(*file_name, filter->file_name); }
return S_OK; }
-static const IFileSourceFilterVtbl filesourcefilter_vtbl = +static const IFileSourceFilterVtbl file_source_vtbl = { - filesourcefilter_QueryInterface, - filesourcefilter_AddRef, - filesourcefilter_Release, - filesourcefilter_Load, - filesourcefilter_GetCurFile, + file_source_QueryInterface, + file_source_AddRef, + file_source_Release, + file_source_Load, + file_source_GetCurFile, };
HRESULT asf_reader_create(IUnknown *outer, IUnknown **out) @@ -166,7 +166,7 @@ HRESULT asf_reader_create(IUnknown *outer, IUnknown **out) return E_OUTOFMEMORY;
strmbase_filter_init(&object->filter, outer, &CLSID_WMAsfReader, &filter_ops); - object->IFileSourceFilter_iface.lpVtbl = &filesourcefilter_vtbl; + object->IFileSourceFilter_iface.lpVtbl = &file_source_vtbl;
TRACE("Created WM ASF reader %p.\n", object); *out = &object->filter.IUnknown_inner;
From: Rémi Bernon rbernon@codeweavers.com
Signed-off-by: Rémi Bernon rbernon@codeweavers.com --- dlls/qasf/Makefile.in | 2 +- dlls/qasf/asfreader.c | 124 ++++++++++++++++++++++++++++++++++++++++++ 2 files changed, 125 insertions(+), 1 deletion(-)
diff --git a/dlls/qasf/Makefile.in b/dlls/qasf/Makefile.in index ee8fd0451e1..05c947b6255 100644 --- a/dlls/qasf/Makefile.in +++ b/dlls/qasf/Makefile.in @@ -1,5 +1,5 @@ MODULE = qasf.dll -IMPORTS = strmbase dmoguids strmiids uuid ole32 oleaut32 +IMPORTS = strmbase dmoguids strmiids uuid ole32 oleaut32 wmvcore
C_SRCS = \ asfreader.c \ diff --git a/dlls/qasf/asfreader.c b/dlls/qasf/asfreader.c index a657b719456..c38943ec247 100644 --- a/dlls/qasf/asfreader.c +++ b/dlls/qasf/asfreader.c @@ -20,6 +20,11 @@
#include "qasf_private.h"
+#include "mediaobj.h" +#include "propsys.h" +#include "initguid.h" +#include "wmsdkidl.h" + WINE_DEFAULT_DEBUG_CHANNEL(quartz);
struct asf_reader @@ -29,6 +34,9 @@ struct asf_reader
AM_MEDIA_TYPE media_type; WCHAR *file_name; + + IWMReaderCallback *callback; + IWMReader *reader; };
static inline struct asf_reader *impl_from_strmbase_filter(struct strmbase_filter *iface) @@ -47,6 +55,8 @@ static void asf_reader_destroy(struct strmbase_filter *iface)
free(filter->file_name); FreeMediaType(&filter->media_type); + IWMReaderCallback_Release(filter->callback); + IWMReader_Release(filter->reader);
strmbase_filter_cleanup(&filter->filter); free(filter); @@ -102,6 +112,7 @@ static ULONG WINAPI file_source_Release(IFileSourceFilter *iface) static HRESULT WINAPI file_source_Load(IFileSourceFilter *iface, LPCOLESTR file_name, const AM_MEDIA_TYPE *media_type) { struct asf_reader *filter = impl_from_IFileSourceFilter(iface); + HRESULT hr;
TRACE("filter %p, file_name %s, media_type %p.\n", filter, debugstr_w(file_name), media_type); strmbase_dump_media_type(media_type); @@ -118,6 +129,9 @@ static HRESULT WINAPI file_source_Load(IFileSourceFilter *iface, LPCOLESTR file_ if (media_type) CopyMediaType(&filter->media_type, media_type);
+ if (FAILED(hr = IWMReader_Open(filter->reader, filter->file_name, filter->callback, NULL))) + WARN("Failed to open WMReader, hr %#lx\n", hr); + return S_OK; }
@@ -158,13 +172,123 @@ static const IFileSourceFilterVtbl file_source_vtbl = file_source_GetCurFile, };
+struct asf_callback +{ + IWMReaderCallback IWMReaderCallback_iface; + LONG ref; + + struct asf_reader *filter; +}; + +static inline struct asf_callback *impl_from_IWMReaderCallback(IWMReaderCallback *iface) +{ + return CONTAINING_RECORD(iface, struct asf_callback, IWMReaderCallback_iface); +} + +static HRESULT WINAPI reader_callback_QueryInterface(IWMReaderCallback *iface, const IID *iid, void **out) +{ + struct asf_callback *impl = impl_from_IWMReaderCallback(iface); + + TRACE("iface %p, iid %s, out %p.\n", iface, debugstr_guid(iid), out); + + if (IsEqualGUID(iid, &IID_IUnknown) + || IsEqualGUID(iid, &IID_IWMStatusCallback) + || IsEqualGUID(iid, &IID_IWMReaderCallback)) + *out = &impl->IWMReaderCallback_iface; + else + { + *out = NULL; + WARN("%s not implemented, returning E_NOINTERFACE.\n", debugstr_guid(iid)); + return E_NOINTERFACE; + } + + IUnknown_AddRef((IUnknown *)*out); + return S_OK; +} + +static ULONG WINAPI reader_callback_AddRef(IWMReaderCallback *iface) +{ + struct asf_callback *impl = impl_from_IWMReaderCallback(iface); + ULONG ref = InterlockedIncrement(&impl->ref); + + TRACE("iface %p increasing ref to %lu.\n", impl, ref); + + return ref; +} + +static ULONG WINAPI reader_callback_Release(IWMReaderCallback *iface) +{ + struct asf_callback *impl = impl_from_IWMReaderCallback(iface); + ULONG ref = InterlockedDecrement(&impl->ref); + + TRACE("iface %p decreasing ref to %lu.\n", impl, ref); + + if (!ref) + free(impl); + + return ref; +} + +static HRESULT WINAPI reader_callback_OnStatus(IWMReaderCallback *iface, WMT_STATUS status, HRESULT hr, + WMT_ATTR_DATATYPE type, BYTE *value, void *context) +{ + FIXME("iface %p, status %d, hr %#lx, type %d, value %p, context %p stub!\n", + iface, status, hr, type, value, context); + return E_NOTIMPL; +} + +static HRESULT WINAPI reader_callback_OnSample(IWMReaderCallback *iface, DWORD output, QWORD time, + QWORD duration, DWORD flags, INSSBuffer *sample, void *context) +{ + FIXME("iface %p, output %lu, time %I64u, duration %I64u, flags %#lx, sample %p, context %p stub!\n", + iface, output, time, duration, flags, sample, context); + return E_NOTIMPL; +} + +static const IWMReaderCallbackVtbl reader_callback_vtbl = +{ + reader_callback_QueryInterface, + reader_callback_AddRef, + reader_callback_Release, + reader_callback_OnStatus, + reader_callback_OnSample, +}; + +static HRESULT asf_callback_create(struct asf_reader *filter, IWMReaderCallback **out) +{ + struct asf_callback *impl; + + if (!(impl = calloc(1, sizeof(*impl)))) + return E_OUTOFMEMORY; + + impl->IWMReaderCallback_iface.lpVtbl = &reader_callback_vtbl; + impl->filter = filter; + impl->ref = 1; + + *out = &impl->IWMReaderCallback_iface; + return S_OK; +} + HRESULT asf_reader_create(IUnknown *outer, IUnknown **out) { struct asf_reader *object; + HRESULT hr;
if (!(object = calloc(1, sizeof(*object)))) return E_OUTOFMEMORY;
+ if (FAILED(hr = WMCreateReader(NULL, 0, &object->reader))) + { + free(object); + return hr; + } + if (FAILED(hr = asf_callback_create(object, &object->callback))) + { + IWMReader_Release(object->reader); + free(object); + return hr; + } + strmbase_filter_init(&object->filter, outer, &CLSID_WMAsfReader, &filter_ops); object->IFileSourceFilter_iface.lpVtbl = &file_source_vtbl;
From: Rémi Bernon rbernon@codeweavers.com
Signed-off-by: Rémi Bernon rbernon@codeweavers.com --- dlls/qasf/asfreader.c | 97 +++++++++++++++++++++++++++++++++++-- dlls/qasf/tests/asfreader.c | 6 --- 2 files changed, 94 insertions(+), 9 deletions(-)
diff --git a/dlls/qasf/asfreader.c b/dlls/qasf/asfreader.c index c38943ec247..a570687f9d1 100644 --- a/dlls/qasf/asfreader.c +++ b/dlls/qasf/asfreader.c @@ -27,6 +27,12 @@
WINE_DEFAULT_DEBUG_CHANNEL(quartz);
+struct asf_stream +{ + struct strmbase_source source; + DWORD index; +}; + struct asf_reader { struct strmbase_filter filter; @@ -37,8 +43,16 @@ struct asf_reader
IWMReaderCallback *callback; IWMReader *reader; + + UINT stream_count; + struct asf_stream streams[16]; };
+static inline struct asf_stream *impl_from_strmbase_pin(struct strmbase_pin *iface) +{ + return CONTAINING_RECORD(iface, struct asf_stream, source.pin); +} + static inline struct asf_reader *impl_from_strmbase_filter(struct strmbase_filter *iface) { return CONTAINING_RECORD(iface, struct asf_reader, filter); @@ -46,12 +60,31 @@ static inline struct asf_reader *impl_from_strmbase_filter(struct strmbase_filte
static struct strmbase_pin *asf_reader_get_pin(struct strmbase_filter *iface, unsigned int index) { - return NULL; + struct asf_reader *filter = impl_from_strmbase_filter(iface); + struct strmbase_pin *pin = NULL; + + TRACE("iface %p, index %u.\n", iface, index); + + EnterCriticalSection(&filter->filter.filter_cs); + if (index < filter->stream_count) + pin = &filter->streams[index].source.pin; + LeaveCriticalSection(&filter->filter.filter_cs); + + return pin; }
static void asf_reader_destroy(struct strmbase_filter *iface) { struct asf_reader *filter = impl_from_strmbase_filter(iface); + struct strmbase_source *source; + + while (filter->stream_count--) + { + source = &filter->streams[filter->stream_count].source; + if (source->pin.peer) IPin_Disconnect(source->pin.peer); + IPin_Disconnect(&source->pin.IPin_iface); + strmbase_source_cleanup(source); + }
free(filter->file_name); FreeMediaType(&filter->media_type); @@ -83,6 +116,20 @@ static const struct strmbase_filter_ops filter_ops = .filter_query_interface = asf_reader_query_interface, };
+static HRESULT WINAPI asf_reader_DecideBufferSize(struct strmbase_source *iface, + IMemAllocator *allocator, ALLOCATOR_PROPERTIES *req_props) +{ + FIXME("iface %p, allocator %p, req_props %p stub!\n", iface, allocator, req_props); + return E_NOTIMPL; +} + +static const struct strmbase_source_ops source_ops = +{ + .pfnDecideAllocator = BaseOutputPinImpl_DecideAllocator, + .pfnAttemptConnection = BaseOutputPinImpl_AttemptConnection, + .pfnDecideBufferSize = asf_reader_DecideBufferSize, +}; + static inline struct asf_reader *impl_from_IFileSourceFilter(IFileSourceFilter *iface) { return CONTAINING_RECORD(iface, struct asf_reader, IFileSourceFilter_iface); @@ -232,9 +279,51 @@ static ULONG WINAPI reader_callback_Release(IWMReaderCallback *iface) static HRESULT WINAPI reader_callback_OnStatus(IWMReaderCallback *iface, WMT_STATUS status, HRESULT hr, WMT_ATTR_DATATYPE type, BYTE *value, void *context) { - FIXME("iface %p, status %d, hr %#lx, type %d, value %p, context %p stub!\n", + struct asf_reader *filter = impl_from_IWMReaderCallback(iface)->filter; + DWORD i, stream_count; + WCHAR name[MAX_PATH]; + + TRACE("iface %p, status %d, hr %#lx, type %d, value %p, context %p.\n", iface, status, hr, type, value, context); - return E_NOTIMPL; + + switch (status) + { + case WMT_OPENED: + if (FAILED(hr)) + { + WARN("Failed to open WMReader, hr %#lx\n", hr); + break; + } + if (FAILED(hr = IWMReader_GetOutputCount(filter->reader, &stream_count))) + { + WARN("Failed to get WMReader output count, hr %#lx\n", hr); + break; + } + if (stream_count > ARRAY_SIZE(filter->streams)) + { + FIXME("Found %lu streams, not supported!\n", stream_count); + stream_count = ARRAY_SIZE(filter->streams); + } + + EnterCriticalSection(&filter->filter.filter_cs); + for (i = 0; i < stream_count; ++i) + { + struct asf_stream *stream = filter->streams + i; + swprintf(name, ARRAY_SIZE(name), L"Raw Stream %u", stream->index); + strmbase_source_init(&stream->source, &filter->filter, name, &source_ops); + } + filter->stream_count = stream_count; + LeaveCriticalSection(&filter->filter.filter_cs); + + BaseFilterImpl_IncrementPinVersion(&filter->filter); + break; + + default: + WARN("Ignoring status %#x\n", status); + break; + } + + return S_OK; }
static HRESULT WINAPI reader_callback_OnSample(IWMReaderCallback *iface, DWORD output, QWORD time, @@ -273,6 +362,7 @@ HRESULT asf_reader_create(IUnknown *outer, IUnknown **out) { struct asf_reader *object; HRESULT hr; + int i;
if (!(object = calloc(1, sizeof(*object)))) return E_OUTOFMEMORY; @@ -289,6 +379,7 @@ HRESULT asf_reader_create(IUnknown *outer, IUnknown **out) return hr; }
+ for (i = 0; i < ARRAY_SIZE(object->streams); ++i) object->streams[i].index = i; strmbase_filter_init(&object->filter, outer, &CLSID_WMAsfReader, &filter_ops); object->IFileSourceFilter_iface.lpVtbl = &file_source_vtbl;
diff --git a/dlls/qasf/tests/asfreader.c b/dlls/qasf/tests/asfreader.c index a2b56ee29de..cda7eca602d 100644 --- a/dlls/qasf/tests/asfreader.c +++ b/dlls/qasf/tests/asfreader.c @@ -241,9 +241,7 @@ static inline void check_pin(IPin *pin, IBaseFilter *expect_filter, PIN_DIRECTIO CoTaskMemFree(id);
hr = IPin_EnumMediaTypes(pin, &enum_mt); - todo_wine ok(hr == S_OK, "Got hr %#lx.\n", hr); - if (hr != S_OK) goto skip_enum_mt; hr = IEnumMediaTypes_Next(enum_mt, 1, &mt, NULL); for (i = 0; hr == S_OK; hr = IEnumMediaTypes_Next(enum_mt, 1, &mt, NULL), i++) { @@ -260,7 +258,6 @@ static inline void check_pin(IPin *pin, IBaseFilter *expect_filter, PIN_DIRECTIO ok(hr == S_FALSE, "Got hr %#lx.\n", hr); IEnumMediaTypes_Release(enum_mt);
-skip_enum_mt: check_interface(pin, &IID_IPin, TRUE); check_interface(pin, &IID_IUnknown, TRUE); todo_wine @@ -499,9 +496,7 @@ static void test_filesourcefilter(void) ok(hr == S_OK, "Got hr %#lx.\n", hr);
hr = IEnumPins_Next(enumpins, 1, pins, NULL); - todo_wine ok(hr == S_OK, "Got hr %#lx.\n", hr); - if (hr != S_OK) goto skip_pins; check_pin(pins[0], filter, PINDIR_OUTPUT, L"Raw Video 0", L"Raw Video 0", video_mt, ARRAY_SIZE(video_mt)); IPin_Release(pins[0]);
@@ -511,7 +506,6 @@ static void test_filesourcefilter(void) IPin_Release(pins[0]);
hr = IEnumPins_Next(enumpins, 1, pins, NULL); -skip_pins: ok(hr == S_FALSE, "Got hr %#lx.\n", hr); IEnumPins_Release(enumpins);
On Tue May 24 16:09:54 2022 +0000, **** wrote:
Zebediah Figura replied on the mailing list:
On 5/24/22 01:12, Rémi Bernon (@rbernon) wrote: > On Mon May 23 23:15:33 2022 +0000, **** wrote: >> Zebediah Figura replied on the mailing list: >> \`\`\` >> On 5/23/22 07:07, Rémi Bernon wrote: >>> +static HRESULT asf_callback_create(struct asf_reader *filter, >> IWMReaderCallback **out) >>> +{ >>> + struct asf_callback *impl; >>> + >>> + if (!(impl = calloc(1, sizeof(*impl)))) >>> + return E_OUTOFMEMORY; >>> + >>> + impl->IWMReaderCallback_iface.lpVtbl = &reader_callback_vtbl; >>> + impl->filter = filter; >>> + impl->ref = 1; >>> + >>> + *out = &impl->IWMReaderCallback_iface; >>> + return S_OK; >>> +} >> Can we put the IWMReaderCallback interface inside of struct asf_reader, >> instead of making this a separate object? >> \`\`\` > > No, the reader will hold a ref on it and it would prevent filter destruction otherwise. > If it's the same object, the reader shouldn't be holding a reference. In fact I think they should be able to share the same refcount.
If it's the same object, the reader shouldn't be holding a reference. In fact I think they should be able to share the same refcount.
The reader has no way to know that and it adds a ref to the callback interface as it should. I don't think we should decref ourselves after calling IWMReader_Open, that sounds pretty ugly.
On 5/24/22 11:25, Rémi Bernon (@rbernon) wrote:
On Tue May 24 16:09:54 2022 +0000, **** wrote:
Zebediah Figura replied on the mailing list:
On 5/24/22 01:12, Rémi Bernon (@rbernon) wrote: > On Mon May 23 23:15:33 2022 +0000, **** wrote: >> Zebediah Figura replied on the mailing list: >> \`\`\` >> On 5/23/22 07:07, Rémi Bernon wrote: >>> +static HRESULT asf_callback_create(struct asf_reader *filter, >> IWMReaderCallback **out) >>> +{ >>> + struct asf_callback *impl; >>> + >>> + if (!(impl = calloc(1, sizeof(*impl)))) >>> + return E_OUTOFMEMORY; >>> + >>> + impl->IWMReaderCallback_iface.lpVtbl = &reader_callback_vtbl; >>> + impl->filter = filter; >>> + impl->ref = 1; >>> + >>> + *out = &impl->IWMReaderCallback_iface; >>> + return S_OK; >>> +} >> Can we put the IWMReaderCallback interface inside of struct asf_reader, >> instead of making this a separate object? >> \`\`\` > > No, the reader will hold a ref on it and it would prevent filter destruction otherwise. > If it's the same object, the reader shouldn't be holding a reference. In fact I think they should be able to share the same refcount.
If it's the same object, the reader shouldn't be holding a reference. In fact I think they should be able to share the same refcount.
The reader has no way to know that and it adds a ref to the callback interface as it should. I don't think we should decref ourselves after calling IWMReader_Open, that sounds pretty ugly.
Oops, you meant the wmvcore reader, not the qasf reader.
Making it a separate allocation doesn't really help the problem, though. We're still holding a pointer to the reader without holding a reference, which is probably workable in practice but makes me worried.
If we want to be fully safe, I think the right thing to do is to have a separate refcount for the callback, close the WM reader when the filter's refcount reaches zero, and destroy the filter when both refcounts reach zero. This is kind of similar to what's done here, but with the callback holding an "internal" reference count to the filter—except that you don't need them to be separate objects.
(It's also not clear that using the asynchronous reader is worth the trouble, but for the sake of progress I'll set that aside...)
On Tue May 24 16:40:21 2022 +0000, **** wrote:
Zebediah Figura replied on the mailing list:
On 5/24/22 11:25, Rémi Bernon (@rbernon) wrote: > On Tue May 24 16:09:54 2022 +0000, **** wrote: >> Zebediah Figura replied on the mailing list: >> \`\`\` >> On 5/24/22 01:12, Rémi Bernon (@rbernon) wrote: >>> On Mon May 23 23:15:33 2022 +0000, **** wrote: >>>> Zebediah Figura replied on the mailing list: >>>> \`\`\` >>>> On 5/23/22 07:07, Rémi Bernon wrote: >>>>> +static HRESULT asf_callback_create(struct asf_reader *filter, >>>> IWMReaderCallback **out) >>>>> +{ >>>>> + struct asf_callback *impl; >>>>> + >>>>> + if (!(impl = calloc(1, sizeof(*impl)))) >>>>> + return E_OUTOFMEMORY; >>>>> + >>>>> + impl->IWMReaderCallback_iface.lpVtbl = &reader_callback_vtbl; >>>>> + impl->filter = filter; >>>>> + impl->ref = 1; >>>>> + >>>>> + *out = &impl->IWMReaderCallback_iface; >>>>> + return S_OK; >>>>> +} >>>> Can we put the IWMReaderCallback interface inside of struct asf_reader, >>>> instead of making this a separate object? >>>> \`\`\` >>> >>> No, the reader will hold a ref on it and it would prevent filter >> destruction otherwise. >>> >> If it's the same object, the reader shouldn't be holding a reference. In >> fact I think they should be able to share the same refcount. >> \`\`\` >> If it's the same object, the reader shouldn't be holding a reference. In >> fact I think they should be able to share the same refcount. > > The reader has no way to know that and it adds a ref to the callback interface as it should. I don't think we should decref ourselves after calling IWMReader_Open, that sounds pretty ugly. > Oops, you meant the wmvcore reader, not the qasf reader. Making it a separate allocation doesn't really help the problem, though. We're still holding a pointer to the reader without holding a reference, which is probably workable in practice but makes me worried. If we want to be fully safe, I think the right thing to do is to have a separate refcount for the callback, close the WM reader when the filter's refcount reaches zero, and destroy the filter when both refcounts reach zero. This is kind of similar to what's done here, but with the callback holding an "internal" reference count to the filter—except that you don't need them to be separate objects. (It's also not clear that using the asynchronous reader is worth the trouble, but for the sake of progress I'll set that aside...)
Regarding the refcount issue it looks like calling `IWMReader_Close` should be enough to release the callback. As there's no guarantee that it does we could also clear the filter pointer when it is destroyed and leave the callback to be released eventually later.
Using the sync reader we would just re-implement the async reader in qasf reader and add another read thread implementation which feels pretty redundant.
On 5/24/22 11:57, Rémi Bernon (@rbernon) wrote:
Regarding the refcount issue it looks like calling `IWMReader_Close` should be enough to release the callback. As there's no guarantee that it does we could also clear the filter pointer when it is destroyed and leave the callback to be released eventually later.
Using the sync reader we would just re-implement the async reader in qasf reader and add another read thread implementation which feels pretty redundant.
Right, I think it's a reasonable enough place to start for now. I've resent the merge request with some minor stylistic tweaks.
On Tue May 24 16:57:51 2022 +0000, Rémi Bernon wrote:
Regarding the refcount issue it looks like calling `IWMReader_Close` should be enough to release the callback. As there's no guarantee that it does we could also clear the filter pointer when it is destroyed and leave the callback to be released eventually later. Using the sync reader we would just re-implement the async reader in qasf reader and add another read thread implementation which feels pretty redundant.
FWIW I can see that there're tests to validate that `IWMReader_Release` doesn't leave any outstanding ref and that `IWMReader_Close` also releases the provided callback.
This merge request was closed by Rémi Bernon.