[PATCH 0/14] MR11679: quartz: Implement color converter.
This MR implements the DirectShow Color Space Converter filter. -- https://gitlab.winehq.org/wine/wine/-/merge_requests/11679
From: Brendan McGrath <bmcgrath@codeweavers.com> --- dlls/quartz/Makefile.in | 1 + dlls/quartz/colorconv.c | 83 +++++++++++++++++++++++++++++++++++ dlls/quartz/main.c | 44 +++++++++++++++++++ dlls/quartz/quartz_private.h | 1 + dlls/quartz/quartz_strmif.idl | 7 +++ dlls/quartz/tests/colorconv.c | 32 +------------- 6 files changed, 137 insertions(+), 31 deletions(-) create mode 100644 dlls/quartz/colorconv.c diff --git a/dlls/quartz/Makefile.in b/dlls/quartz/Makefile.in index a61bbc2b5fa..1e38b24ef73 100644 --- a/dlls/quartz/Makefile.in +++ b/dlls/quartz/Makefile.in @@ -9,6 +9,7 @@ VER_OLESELFREGISTER = 1 SOURCES = \ acmwrapper.c \ avidec.c \ + colorconv.c \ control_tlb.idl \ decoder.c \ dsoundrender.c \ diff --git a/dlls/quartz/colorconv.c b/dlls/quartz/colorconv.c new file mode 100644 index 00000000000..934462a345d --- /dev/null +++ b/dlls/quartz/colorconv.c @@ -0,0 +1,83 @@ +/* + * Color converter + * + * Copyright 2026 Brendan McGrath + * + * 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 "quartz_private.h" + +#include "vfw.h" + +#include "wine/debug.h" + +WINE_DEFAULT_DEBUG_CHANNEL(quartz); + +struct color_converter +{ + struct strmbase_filter filter; +}; + +static struct color_converter *impl_from_strmbase_filter(struct strmbase_filter *iface) +{ + return CONTAINING_RECORD(iface, struct color_converter, filter); +} + +static struct strmbase_pin *color_get_pin(struct strmbase_filter *iface, unsigned int index) +{ + return NULL; +} + +static void color_destroy(struct strmbase_filter *iface) +{ + struct color_converter *filter = impl_from_strmbase_filter(iface); + + strmbase_filter_cleanup(&filter->filter); + free(filter); +} + +static HRESULT color_init_stream(struct strmbase_filter *iface) +{ + return S_OK; +} + +static HRESULT color_cleanup_stream(struct strmbase_filter *iface) +{ + return S_OK; +} + +static const struct strmbase_filter_ops filter_ops = +{ + .filter_get_pin = color_get_pin, + .filter_destroy = color_destroy, + .filter_init_stream = color_init_stream, + .filter_cleanup_stream = color_cleanup_stream, +}; + +HRESULT color_create(IUnknown *outer, IUnknown **out) +{ + struct color_converter *object; + + if (!(object = calloc(1, sizeof(*object)))) + return E_OUTOFMEMORY; + + strmbase_filter_init(&object->filter, outer, &CLSID_Colour, &filter_ops); + + TRACE("Created Color Converter %p.\n", object); + *out = &object->filter.IUnknown_inner; + + return S_OK; +} diff --git a/dlls/quartz/main.c b/dlls/quartz/main.c index d8da7bde5fa..d38ace1eb20 100644 --- a/dlls/quartz/main.c +++ b/dlls/quartz/main.c @@ -93,6 +93,7 @@ static const struct object_creation_info object_creation[] = { &CLSID_AudioRender, dsound_render_create }, { &CLSID_AVIDec, avi_dec_create }, { &CLSID_AviSplitter, avi_splitter_create }, + { &CLSID_Colour, color_create }, { &CLSID_CMpegAudioCodec, mpeg_audio_codec_create }, { &CLSID_CMpegVideoCodec, mpeg_video_codec_create }, { &CLSID_DSoundRender, dsound_render_create }, @@ -353,6 +354,44 @@ HRESULT WINAPI DllRegisterServer(void) .rgPins2 = acm_wrapper_pins, }; + static const REGPINTYPES color_inputs[] = + { + {&MEDIATYPE_Video, &MEDIASUBTYPE_RGB8}, + {&MEDIATYPE_Video, &MEDIASUBTYPE_RGB555}, + {&MEDIATYPE_Video, &MEDIASUBTYPE_RGB565}, + {&MEDIATYPE_Video, &MEDIASUBTYPE_RGB24}, + {&MEDIATYPE_Video, &MEDIASUBTYPE_RGB32}, + {&MEDIATYPE_Video, &MEDIASUBTYPE_ARGB32}, + }; + static const REGPINTYPES color_outputs[] = + { + {&MEDIATYPE_Video, &MEDIASUBTYPE_RGB8}, + {&MEDIATYPE_Video, &MEDIASUBTYPE_RGB555}, + {&MEDIATYPE_Video, &MEDIASUBTYPE_RGB565}, + {&MEDIATYPE_Video, &MEDIASUBTYPE_RGB24}, + {&MEDIATYPE_Video, &MEDIASUBTYPE_RGB32}, + {&MEDIATYPE_Video, &MEDIASUBTYPE_ARGB32}, + }; + static const REGFILTERPINS2 color_pins[] = + { + { + .nMediaTypes = ARRAY_SIZE(color_inputs), + .lpMediaType = color_inputs, + }, + { + .nMediaTypes = ARRAY_SIZE(color_outputs), + .lpMediaType = color_outputs, + .dwFlags = REG_PINFLAG_B_OUTPUT, + }, + }; + static const REGFILTER2 color_reg = + { + .dwVersion = 2, + .dwMerit = MERIT_UNLIKELY + 1, + .cPins2 = ARRAY_SIZE(color_pins), + .rgPins2 = color_pins, + }; + static const REGPINTYPES mpeg_splitter_inputs[] = { {&MEDIATYPE_Stream, &MEDIASUBTYPE_MPEG1Audio}, @@ -542,6 +581,9 @@ HRESULT WINAPI DllRegisterServer(void) if (FAILED(hr = IFilterMapper2_RegisterFilter(mapper, &CLSID_ACMWrapper, L"ACM Wrapper", NULL, &CLSID_LegacyAmFilterCategory, NULL, &acm_wrapper_reg))) goto done; + if (FAILED(hr = IFilterMapper2_RegisterFilter(mapper, &CLSID_Colour, L"Color Space Converter", NULL, + &CLSID_LegacyAmFilterCategory, NULL, &color_reg))) + goto done; if (FAILED(hr = IFilterMapper2_RegisterFilter(mapper, &CLSID_AviSplitter, L"AVI Splitter", NULL, NULL, NULL, &avi_splitter_reg))) goto done; @@ -589,6 +631,8 @@ HRESULT WINAPI DllUnregisterServer(void) goto done; if (FAILED(hr = IFilterMapper2_UnregisterFilter(mapper, &CLSID_LegacyAmFilterCategory, NULL, &CLSID_ACMWrapper))) goto done; + if (FAILED(hr = IFilterMapper2_UnregisterFilter(mapper, &CLSID_LegacyAmFilterCategory, NULL, &CLSID_Colour))) + goto done; if (FAILED(hr = IFilterMapper2_UnregisterFilter(mapper, NULL, NULL, &CLSID_AviSplitter))) goto done; if (FAILED(hr = IFilterMapper2_UnregisterFilter(mapper, NULL, NULL, &CLSID_MPEG1Splitter))) diff --git a/dlls/quartz/quartz_private.h b/dlls/quartz/quartz_private.h index e683dc51cdc..7c8e296d43c 100644 --- a/dlls/quartz/quartz_private.h +++ b/dlls/quartz/quartz_private.h @@ -54,6 +54,7 @@ HRESULT acm_wrapper_create(IUnknown *outer, IUnknown **out); HRESULT async_reader_create(IUnknown *outer, IUnknown **out); HRESULT avi_dec_create(IUnknown *outer, IUnknown **out); HRESULT avi_splitter_create(IUnknown *outer, IUnknown **out); +HRESULT color_create(IUnknown *outer, IUnknown **out); HRESULT dsound_render_create(IUnknown *outer, IUnknown **out); HRESULT filter_graph_create(IUnknown *outer, IUnknown **out); HRESULT filter_graph_no_thread_create(IUnknown *outer, IUnknown **out); diff --git a/dlls/quartz/quartz_strmif.idl b/dlls/quartz/quartz_strmif.idl index ad63352ff07..e9469e1be80 100644 --- a/dlls/quartz/quartz_strmif.idl +++ b/dlls/quartz/quartz_strmif.idl @@ -91,6 +91,13 @@ coclass AsyncReader { interface IBaseFilter; } ] coclass AVIDec { interface IBaseFilter; } +[ + helpstring("Color Space Converter"), + threading(both), + uuid(1643e180-90f5-11ce-97d5-00aa0055595a) +] +coclass Colour { interface IBaseFilter; } + [ helpstring("DirectSound Audio Renderer"), threading(both), diff --git a/dlls/quartz/tests/colorconv.c b/dlls/quartz/tests/colorconv.c index cb8a2ee4fac..f1ceba3f61c 100644 --- a/dlls/quartz/tests/colorconv.c +++ b/dlls/quartz/tests/colorconv.c @@ -806,12 +806,7 @@ static void test_registration(void) int i, j; hr = create_color_conv_property_bag(&property_bag); - - if (hr != S_OK) - { - skip("Skipping registration tests.\n"); - return; - } + ok(hr == S_OK, "Got hr %#lx.\n", hr); VariantInit(&var); hr = IPropertyBag_Read(property_bag, L"FilterData", &var, NULL); @@ -885,12 +880,8 @@ static void test_interfaces(void) IPin *pin; hr = create_color_conv(&filter); - todo_wine ok(hr == S_OK, "Got hr %#lx.\n", hr); - if (hr != S_OK) - return; - check_interface(filter, &IID_IBaseFilter, TRUE); check_interface(filter, &IID_IMediaFilter, TRUE); check_interface(filter, &IID_IPersist, TRUE); @@ -1052,10 +1043,7 @@ static void test_enum_pins(void) HRESULT hr; hr = create_color_conv(&filter); - todo_wine ok(hr == S_OK, "Got hr %#lx.\n", hr); - if (hr != S_OK) - return; refcount = get_refcount(filter); ok(refcount == 1, "Got refcount %ld.\n", refcount); @@ -1181,10 +1169,7 @@ static void test_find_pin(void) HRESULT hr; hr = create_color_conv(&filter); - todo_wine ok(hr == S_OK, "Got hr %#lx.\n", hr); - if (hr != S_OK) - return; hr = IBaseFilter_EnumPins(filter, &enum_pins); ok(hr == S_OK, "Got hr %#lx.\n", hr); @@ -1227,10 +1212,7 @@ static void test_pin_info(void) IPin *pin; hr = create_color_conv(&filter); - todo_wine ok(hr == S_OK, "Got hr %#lx.\n", hr); - if (hr != S_OK) - return; hr = IBaseFilter_FindPin(filter, L"In", &pin); todo_wine @@ -1316,10 +1298,7 @@ static void test_media_types(void) int i; hr = create_color_conv(&filter); - todo_wine ok(hr == S_OK, "Got hr %#lx.\n", hr); - if (hr != S_OK) - return; hr = create_filter_graph(&graph); ok(hr == S_OK, "Got hr %#lx.\n", hr); @@ -1486,10 +1465,7 @@ static void test_enum_media_types(void) IPin *pin; hr = create_color_conv(&filter); - todo_wine ok(hr == S_OK, "Got hr %#lx.\n", hr); - if (hr != S_OK) - return; hr = IBaseFilter_FindPin(filter, L"In", &pin); todo_wine @@ -1570,10 +1546,7 @@ static void test_unconnected_filter_state(void) ULONG ref; hr = create_color_conv(&filter); - todo_wine ok(hr == S_OK, "Got hr %#lx.\n", hr); - if (hr != S_OK) - return; hr = IBaseFilter_GetState(filter, 0, &state); ok(hr == S_OK, "Got hr %#lx.\n", hr); @@ -2311,10 +2284,7 @@ static void test_connect_pin(void) HRESULT hr; hr = create_color_conv(&filter); - todo_wine ok(hr == S_OK, "Got hr %#lx.\n", hr); - if (hr != S_OK) - return; hr = create_filter_graph(&graph); ok(hr == S_OK, "Got hr %#lx.\n", hr); -- GitLab https://gitlab.winehq.org/wine/wine/-/merge_requests/11679
From: Brendan McGrath <bmcgrath@codeweavers.com> --- dlls/quartz/colorconv.c | 84 +++++++++++++++++++++++++++++++++++ dlls/quartz/tests/colorconv.c | 72 +++++++++--------------------- 2 files changed, 104 insertions(+), 52 deletions(-) diff --git a/dlls/quartz/colorconv.c b/dlls/quartz/colorconv.c index 934462a345d..35909502bde 100644 --- a/dlls/quartz/colorconv.c +++ b/dlls/quartz/colorconv.c @@ -29,6 +29,10 @@ WINE_DEFAULT_DEBUG_CHANNEL(quartz); struct color_converter { struct strmbase_filter filter; + + struct strmbase_source source; + + struct strmbase_sink sink; }; static struct color_converter *impl_from_strmbase_filter(struct strmbase_filter *iface) @@ -36,8 +40,46 @@ static struct color_converter *impl_from_strmbase_filter(struct strmbase_filter return CONTAINING_RECORD(iface, struct color_converter, filter); } +static HRESULT color_sink_connect(struct strmbase_sink *iface, IPin *peer, const AM_MEDIA_TYPE *mt) +{ + FIXME("stub\n"); + return VFW_E_TYPE_NOT_ACCEPTED; +} + +static const struct strmbase_sink_ops sink_ops = +{ + .sink_connect = color_sink_connect, +}; + +static HRESULT WINAPI color_source_DecideBufferSize( + struct strmbase_source *iface, IMemAllocator *alloc, ALLOCATOR_PROPERTIES *props) +{ + ALLOCATOR_PROPERTIES actual; + + if (!props->cbAlign) + props->cbAlign = 1; + + if (!props->cBuffers) + props->cBuffers = 1; + + return IMemAllocator_SetProperties(alloc, props, &actual); +} + +static const struct strmbase_source_ops source_ops = +{ + .pfnAttemptConnection = BaseOutputPinImpl_AttemptConnection, + .pfnDecideAllocator = BaseOutputPinImpl_DecideAllocator, + .pfnDecideBufferSize = color_source_DecideBufferSize, +}; + static struct strmbase_pin *color_get_pin(struct strmbase_filter *iface, unsigned int index) { + struct color_converter *filter = impl_from_strmbase_filter(iface); + + if (index == 0) + return &filter->sink.pin; + else if (index == 1) + return &filter->source.pin; return NULL; } @@ -45,17 +87,44 @@ static void color_destroy(struct strmbase_filter *iface) { struct color_converter *filter = impl_from_strmbase_filter(iface); + if (filter->sink.pin.peer) + IPin_Disconnect(filter->sink.pin.peer); + IPin_Disconnect(&filter->sink.pin.IPin_iface); + + if (filter->source.pin.peer) + IPin_Disconnect(filter->source.pin.peer); + IPin_Disconnect(&filter->source.pin.IPin_iface); + + strmbase_sink_cleanup(&filter->sink); + strmbase_source_cleanup(&filter->source); strmbase_filter_cleanup(&filter->filter); + free(filter); } static HRESULT color_init_stream(struct strmbase_filter *iface) { + struct color_converter *filter = impl_from_strmbase_filter(iface); + HRESULT hr; + + if (!filter->source.pin.peer) + return S_OK; + + if (FAILED(hr = IMemAllocator_Commit(filter->source.pAllocator))) + ERR("Failed to commit allocator, hr %#lx.\n", hr); + return S_OK; } static HRESULT color_cleanup_stream(struct strmbase_filter *iface) { + struct color_converter *filter = impl_from_strmbase_filter(iface); + + if (!filter->source.pin.peer) + return S_OK; + + IMemAllocator_Decommit(filter->source.pAllocator); + return S_OK; } @@ -70,12 +139,27 @@ static const struct strmbase_filter_ops filter_ops = HRESULT color_create(IUnknown *outer, IUnknown **out) { struct color_converter *object; + IMemAllocator *allocator; + HRESULT hr; + + if (FAILED(hr = CoCreateInstance( + &CLSID_MemoryAllocator, NULL, CLSCTX_INPROC_SERVER, &IID_IMemAllocator, (void **)&allocator))) + return hr; if (!(object = calloc(1, sizeof(*object)))) + { + IMemAllocator_Release(allocator); return E_OUTOFMEMORY; + } strmbase_filter_init(&object->filter, outer, &CLSID_Colour, &filter_ops); + strmbase_sink_init(&object->sink, &object->filter, L"In", &sink_ops, allocator); + wcscpy(object->sink.pin.name, L"Input"); + + strmbase_source_init(&object->source, &object->filter, L"Out", &source_ops); + wcscpy(object->source.pin.name, L"XForm Out"); + TRACE("Created Color Converter %p.\n", object); *out = &object->filter.IUnknown_inner; diff --git a/dlls/quartz/tests/colorconv.c b/dlls/quartz/tests/colorconv.c index f1ceba3f61c..6ccb6f0b769 100644 --- a/dlls/quartz/tests/colorconv.c +++ b/dlls/quartz/tests/colorconv.c @@ -900,43 +900,35 @@ static void test_interfaces(void) check_interface(filter, &IID_IVideoWindow, FALSE); hr = IBaseFilter_FindPin(filter, L"In", &pin); - todo_wine ok(hr == S_OK, "Got hr %#lx.\n", hr); - if (hr == S_OK) - { - todo_wine - check_interface(pin, &IID_IMemInputPin, TRUE); - check_interface(pin, &IID_IPin, TRUE); - todo_wine - check_interface(pin, &IID_IQualityControl, TRUE); - check_interface(pin, &IID_IUnknown, TRUE); + todo_wine + check_interface(pin, &IID_IMemInputPin, TRUE); + check_interface(pin, &IID_IPin, TRUE); + todo_wine + check_interface(pin, &IID_IQualityControl, TRUE); + check_interface(pin, &IID_IUnknown, TRUE); - check_interface(pin, &IID_IMediaPosition, FALSE); - check_interface(pin, &IID_IMediaSeeking, FALSE); + check_interface(pin, &IID_IMediaPosition, FALSE); + check_interface(pin, &IID_IMediaSeeking, FALSE); - IPin_Release(pin); - } + IPin_Release(pin); hr = IBaseFilter_FindPin(filter, L"Out", &pin); - todo_wine ok(hr == S_OK, "Got hr %#lx.\n", hr); - if (hr == S_OK) - { - check_interface(pin, &IID_IPin, TRUE); - todo_wine - check_interface(pin, &IID_IMediaPosition, TRUE); - todo_wine - check_interface(pin, &IID_IMediaSeeking, TRUE); - todo_wine - check_interface(pin, &IID_IQualityControl, TRUE); - check_interface(pin, &IID_IUnknown, TRUE); + check_interface(pin, &IID_IPin, TRUE); + todo_wine + check_interface(pin, &IID_IMediaPosition, TRUE); + todo_wine + check_interface(pin, &IID_IMediaSeeking, TRUE); + todo_wine + check_interface(pin, &IID_IQualityControl, TRUE); + check_interface(pin, &IID_IUnknown, TRUE); - check_interface(pin, &IID_IAsyncReader, FALSE); + check_interface(pin, &IID_IAsyncReader, FALSE); - IPin_Release(pin); - } + IPin_Release(pin); refcount = IBaseFilter_Release(filter); ok(refcount == 0, "Got refcount %lu.\n", refcount); @@ -1062,10 +1054,7 @@ static void test_enum_pins(void) ok(hr == E_POINTER, "Got hr %#lx.\n", hr); hr = IEnumPins_Next(enum1, 1, pins, NULL); - todo_wine ok(hr == S_OK, "Got hr %#lx.\n", hr); - if (hr != S_OK) - goto skip_test; refcount = get_refcount(filter); ok(refcount == 3, "Got refcount %ld.\n", refcount); @@ -1153,8 +1142,6 @@ static void test_enum_pins(void) IPin_Release(pins[0]); IEnumPins_Release(enum2); - -skip_test: IEnumPins_Release(enum1); refcount = IBaseFilter_Release(filter); ok(refcount == 0, "Got refcount %ld.\n", refcount); @@ -1175,10 +1162,7 @@ static void test_find_pin(void) ok(hr == S_OK, "Got hr %#lx.\n", hr); hr = IBaseFilter_FindPin(filter, L"In", &pin); - todo_wine ok(hr == S_OK, "Got hr %#lx.\n", hr); - if (hr != S_OK) - goto skip_test; hr = IEnumPins_Next(enum_pins, 1, &pin2, NULL); ok(hr == S_OK, "Got hr %#lx.\n", hr); @@ -1194,7 +1178,6 @@ static void test_find_pin(void) IPin_Release(pin2); IPin_Release(pin); -skip_test: IEnumPins_Release(enum_pins); refcount = IBaseFilter_Release(filter); ok(refcount == 0, "Got refcount %ld.\n", refcount); @@ -1215,10 +1198,7 @@ static void test_pin_info(void) ok(hr == S_OK, "Got hr %#lx.\n", hr); hr = IBaseFilter_FindPin(filter, L"In", &pin); - todo_wine ok(hr == S_OK, "Got hr %#lx.\n", hr); - if (hr != S_OK) - goto skip_test; refcount = get_refcount(filter); ok(refcount == 2, "Got refcount %ld.\n", refcount); @@ -1278,7 +1258,6 @@ static void test_pin_info(void) IPin_Release(pin); -skip_test: refcount = IBaseFilter_Release(filter); ok(refcount == 0, "Got refcount %ld.\n", refcount); } @@ -1307,10 +1286,7 @@ static void test_media_types(void) ok(hr == S_OK, "Got hr %#lx.\n", hr); hr = IBaseFilter_FindPin(filter, L"Out", &source); - todo_wine ok(hr == S_OK, "Got hr %#lx.\n", hr); - if (hr != S_OK) - goto skip_test; hr = IBaseFilter_FindPin(filter, L"In", &sink); ok(hr == S_OK, "Got hr %#lx.\n", hr); @@ -1444,7 +1420,6 @@ static void test_media_types(void) refcount = IBaseFilter_Release(&peer->filter.IBaseFilter_iface); ok(refcount == 0, "Got refcount %lu.\n", refcount); -skip_test: hr = IFilterGraph_RemoveFilter(graph, filter); ok(hr == S_OK, "Got hr %#lx.\n", hr); @@ -1468,10 +1443,7 @@ static void test_enum_media_types(void) ok(hr == S_OK, "Got hr %#lx.\n", hr); hr = IBaseFilter_FindPin(filter, L"In", &pin); - todo_wine ok(hr == S_OK, "Got hr %#lx.\n", hr); - if (hr != S_OK) - goto skip_test; hr = IPin_EnumMediaTypes(pin, &enum1); ok(hr == S_OK, "Got hr %#lx.\n", hr); @@ -1533,7 +1505,6 @@ static void test_enum_media_types(void) IEnumMediaTypes_Release(enum2); IPin_Release(pin); -skip_test: ref = IBaseFilter_Release(filter); ok(!ref, "Got outstanding refcount %ld.\n", ref); } @@ -2296,10 +2267,7 @@ static void test_connect_pin(void) ok(hr == S_OK, "Got hr %#lx.\n", hr); hr = IBaseFilter_FindPin(filter, L"In", &sink); - todo_wine ok(hr == S_OK, "Got hr %#lx.\n", hr); - if (hr != S_OK) - goto skip_test; hr = IBaseFilter_FindPin(filter, L"Out", &source); ok(hr == S_OK, "Got hr %#lx.\n", hr); @@ -2450,7 +2418,7 @@ skip_connection_test: refcount = IMemAllocator_Release(&sink_allocator->IMemAllocator_iface); ok(refcount == 0, "Got refcount %lu.\n", refcount); -skip_test: + IMediaControl_Release(control); hr = IFilterGraph_RemoveFilter(graph, filter); -- GitLab https://gitlab.winehq.org/wine/wine/-/merge_requests/11679
From: Brendan McGrath <bmcgrath@codeweavers.com> --- dlls/quartz/colorconv.c | 19 +++++++++++++++++++ dlls/quartz/tests/colorconv.c | 1 - 2 files changed, 19 insertions(+), 1 deletion(-) diff --git a/dlls/quartz/colorconv.c b/dlls/quartz/colorconv.c index 35909502bde..42a3976daac 100644 --- a/dlls/quartz/colorconv.c +++ b/dlls/quartz/colorconv.c @@ -31,6 +31,7 @@ struct color_converter struct strmbase_filter filter; struct strmbase_source source; + struct strmbase_passthrough passthrough; struct strmbase_sink sink; }; @@ -65,8 +66,22 @@ static HRESULT WINAPI color_source_DecideBufferSize( return IMemAllocator_SetProperties(alloc, props, &actual); } +static HRESULT color_source_query_interface(struct strmbase_pin *iface, REFIID iid, void **out) +{ + struct color_converter *filter = impl_from_strmbase_filter(iface->filter); + + if (IsEqualGUID(iid, &IID_IMediaSeeking)) + *out = &filter->passthrough.IMediaSeeking_iface; + else + return E_NOINTERFACE; + + IUnknown_AddRef((IUnknown *)*out); + return S_OK; +} + static const struct strmbase_source_ops source_ops = { + .base.pin_query_interface = color_source_query_interface, .pfnAttemptConnection = BaseOutputPinImpl_AttemptConnection, .pfnDecideAllocator = BaseOutputPinImpl_DecideAllocator, .pfnDecideBufferSize = color_source_DecideBufferSize, @@ -97,6 +112,7 @@ static void color_destroy(struct strmbase_filter *iface) strmbase_sink_cleanup(&filter->sink); strmbase_source_cleanup(&filter->source); + strmbase_passthrough_cleanup(&filter->passthrough); strmbase_filter_cleanup(&filter->filter); free(filter); @@ -160,6 +176,9 @@ HRESULT color_create(IUnknown *outer, IUnknown **out) strmbase_source_init(&object->source, &object->filter, L"Out", &source_ops); wcscpy(object->source.pin.name, L"XForm Out"); + strmbase_passthrough_init(&object->passthrough, (IUnknown *)&object->source.pin.IPin_iface); + ISeekingPassThru_Init(&object->passthrough.ISeekingPassThru_iface, FALSE, &object->sink.pin.IPin_iface); + TRACE("Created Color Converter %p.\n", object); *out = &object->filter.IUnknown_inner; diff --git a/dlls/quartz/tests/colorconv.c b/dlls/quartz/tests/colorconv.c index 6ccb6f0b769..4f03f7228df 100644 --- a/dlls/quartz/tests/colorconv.c +++ b/dlls/quartz/tests/colorconv.c @@ -920,7 +920,6 @@ static void test_interfaces(void) check_interface(pin, &IID_IPin, TRUE); todo_wine check_interface(pin, &IID_IMediaPosition, TRUE); - todo_wine check_interface(pin, &IID_IMediaSeeking, TRUE); todo_wine check_interface(pin, &IID_IQualityControl, TRUE); -- GitLab https://gitlab.winehq.org/wine/wine/-/merge_requests/11679
From: Brendan McGrath <bmcgrath@codeweavers.com> --- dlls/quartz/colorconv.c | 14 ++++++++++++++ dlls/quartz/tests/colorconv.c | 2 -- 2 files changed, 14 insertions(+), 2 deletions(-) diff --git a/dlls/quartz/colorconv.c b/dlls/quartz/colorconv.c index 42a3976daac..97d22b50bb0 100644 --- a/dlls/quartz/colorconv.c +++ b/dlls/quartz/colorconv.c @@ -41,6 +41,19 @@ static struct color_converter *impl_from_strmbase_filter(struct strmbase_filter return CONTAINING_RECORD(iface, struct color_converter, filter); } +static HRESULT color_sink_query_interface(struct strmbase_pin *iface, REFIID iid, void **out) +{ + struct color_converter *filter = impl_from_strmbase_filter(iface->filter); + + if (IsEqualGUID(iid, &IID_IMemInputPin)) + *out = &filter->sink.IMemInputPin_iface; + else + return E_NOINTERFACE; + + IUnknown_AddRef((IUnknown *)*out); + return S_OK; +} + static HRESULT color_sink_connect(struct strmbase_sink *iface, IPin *peer, const AM_MEDIA_TYPE *mt) { FIXME("stub\n"); @@ -49,6 +62,7 @@ static HRESULT color_sink_connect(struct strmbase_sink *iface, IPin *peer, const static const struct strmbase_sink_ops sink_ops = { + .base.pin_query_interface = color_sink_query_interface, .sink_connect = color_sink_connect, }; diff --git a/dlls/quartz/tests/colorconv.c b/dlls/quartz/tests/colorconv.c index 4f03f7228df..58f2d4884e2 100644 --- a/dlls/quartz/tests/colorconv.c +++ b/dlls/quartz/tests/colorconv.c @@ -902,7 +902,6 @@ static void test_interfaces(void) hr = IBaseFilter_FindPin(filter, L"In", &pin); ok(hr == S_OK, "Got hr %#lx.\n", hr); - todo_wine check_interface(pin, &IID_IMemInputPin, TRUE); check_interface(pin, &IID_IPin, TRUE); todo_wine @@ -2273,7 +2272,6 @@ static void test_connect_pin(void) meminput = NULL; hr = IPin_QueryInterface(sink, &IID_IMemInputPin, (void **)&meminput); - todo_wine ok(hr == S_OK, "Got hr %#lx.\n", hr); sink_allocator = create_mem_allocator(); -- GitLab https://gitlab.winehq.org/wine/wine/-/merge_requests/11679
From: Brendan McGrath <bmcgrath@codeweavers.com> --- dlls/quartz/colorconv.c | 52 +++++++++++++++++++++++++++++++++++++++++ 1 file changed, 52 insertions(+) diff --git a/dlls/quartz/colorconv.c b/dlls/quartz/colorconv.c index 97d22b50bb0..f35a99378a5 100644 --- a/dlls/quartz/colorconv.c +++ b/dlls/quartz/colorconv.c @@ -36,6 +36,49 @@ struct color_converter struct strmbase_sink sink; }; +struct subtype +{ + const GUID *guid; + DWORD compression; + WORD bitcount; + ULONG cbFormat; +}; + +static const struct subtype subtypes[] = +{ + { &MEDIASUBTYPE_ARGB32, BI_RGB, 32, sizeof(VIDEOINFOHEADER) }, + { &MEDIASUBTYPE_RGB32, BI_RGB, 32, sizeof(VIDEOINFOHEADER) }, + { &MEDIASUBTYPE_RGB24, BI_RGB, 24, sizeof(VIDEOINFOHEADER) }, + { &MEDIASUBTYPE_RGB565, BI_BITFIELDS, 16, sizeof(VIDEOINFOHEADER) + sizeof(DWORD[3]) /* dwBitMasks */ }, + { &MEDIASUBTYPE_RGB555, BI_BITFIELDS, 16, sizeof(VIDEOINFOHEADER) + sizeof(DWORD[3]) /* dwBitMasks */ }, + { &MEDIASUBTYPE_RGB8, BI_RGB, 8, sizeof(VIDEOINFOHEADER) + sizeof(RGBQUAD[256]) /* bmiColors */ }, +}; + +static const struct subtype *get_subtype(const AM_MEDIA_TYPE *mt) +{ + const struct subtype *subtype = NULL; + VIDEOINFOHEADER *video_info; + int i; + + if (!IsEqualGUID(&mt->majortype, &MEDIATYPE_Video) || !IsEqualGUID(&mt->formattype, &FORMAT_VideoInfo)) + return NULL; + + for (i = 0; i < ARRAY_SIZE(subtypes); i++) + { + if (IsEqualGUID(&mt->subtype, subtypes[i].guid)) + { + subtype = subtypes + i; + break; + } + } + + if (!subtype || mt->cbFormat < subtype->cbFormat || !(video_info = (VIDEOINFOHEADER *)mt->pbFormat) || + video_info->bmiHeader.biSize != sizeof(video_info->bmiHeader)) + return NULL; + + return subtype; +} + static struct color_converter *impl_from_strmbase_filter(struct strmbase_filter *iface) { return CONTAINING_RECORD(iface, struct color_converter, filter); @@ -54,6 +97,14 @@ static HRESULT color_sink_query_interface(struct strmbase_pin *iface, REFIID iid return S_OK; } +static HRESULT color_sink_query_accept(struct strmbase_pin *iface, const AM_MEDIA_TYPE *mt) +{ + if (get_subtype(mt)) + return S_OK; + else + return S_FALSE; +} + static HRESULT color_sink_connect(struct strmbase_sink *iface, IPin *peer, const AM_MEDIA_TYPE *mt) { FIXME("stub\n"); @@ -63,6 +114,7 @@ static HRESULT color_sink_connect(struct strmbase_sink *iface, IPin *peer, const static const struct strmbase_sink_ops sink_ops = { .base.pin_query_interface = color_sink_query_interface, + .base.pin_query_accept = color_sink_query_accept, .sink_connect = color_sink_connect, }; -- GitLab https://gitlab.winehq.org/wine/wine/-/merge_requests/11679
From: Brendan McGrath <bmcgrath@codeweavers.com> --- dlls/quartz/colorconv.c | 9 ++++++++- dlls/quartz/tests/colorconv.c | 13 +------------ 2 files changed, 9 insertions(+), 13 deletions(-) diff --git a/dlls/quartz/colorconv.c b/dlls/quartz/colorconv.c index f35a99378a5..f91a6eb9f0b 100644 --- a/dlls/quartz/colorconv.c +++ b/dlls/quartz/colorconv.c @@ -107,8 +107,15 @@ static HRESULT color_sink_query_accept(struct strmbase_pin *iface, const AM_MEDI static HRESULT color_sink_connect(struct strmbase_sink *iface, IPin *peer, const AM_MEDIA_TYPE *mt) { + if (!get_subtype(mt)) + { + TRACE("Connection refused\n"); + return VFW_E_TYPE_NOT_ACCEPTED; + } + + /* TODO: Set up color conversion */ FIXME("stub\n"); - return VFW_E_TYPE_NOT_ACCEPTED; + return S_OK; } static const struct strmbase_sink_ops sink_ops = diff --git a/dlls/quartz/tests/colorconv.c b/dlls/quartz/tests/colorconv.c index 58f2d4884e2..97ff64713a3 100644 --- a/dlls/quartz/tests/colorconv.c +++ b/dlls/quartz/tests/colorconv.c @@ -1310,11 +1310,9 @@ static void test_media_types(void) ok(hr == S_OK, "Got hr %#lx.\n", hr); hr = IPin_ReceiveConnection(sink, &peer->source.pin.IPin_iface, &req_mt); - todo_wine ok(hr == S_OK, "Got hr %#lx.\n", hr); hr = IPin_Disconnect(sink); - todo_wine ok(hr == S_OK, "Got hr %#lx.\n", hr); /* Test negative height */ @@ -1324,11 +1322,9 @@ static void test_media_types(void) ok(hr == S_OK, "Got hr %#lx.\n", hr); hr = IPin_ReceiveConnection(sink, &peer->source.pin.IPin_iface, &req_mt); - todo_wine ok(hr == S_OK, "Got hr %#lx.\n", hr); hr = IPin_Disconnect(sink); - todo_wine ok(hr == S_OK, "Got hr %#lx.\n", hr); winetest_pop_context(); @@ -1350,7 +1346,6 @@ static void test_media_types(void) video_info.rcTarget.bottom = 220; hr = IPin_ReceiveConnection(sink, &peer->source.pin.IPin_iface, &req_mt); - todo_wine ok(hr == S_OK, "Got hr %#lx.\n", hr); hr = IPin_EnumMediaTypes(source, &enum_types); @@ -1403,7 +1398,6 @@ static void test_media_types(void) IEnumMediaTypes_Release(enum_types); hr = IPin_Disconnect(sink); - todo_wine ok(hr == S_OK, "Got hr %#lx.\n", hr); /* The previously accepted media type is no longer accepted after disconnect */ @@ -2322,10 +2316,7 @@ static void test_connect_pin(void) ok(hr == S_OK, "Got hr %#lx.\n", hr); hr = IFilterGraph_ConnectDirect(graph, &testsource->source.pin.IPin_iface, sink, &req_mt); - todo_wine ok(hr == S_OK, "Got hr %#lx.\n", hr); - if (hr != S_OK) - goto skip_connection_test; hr = IPin_ConnectedTo(sink, &peer); ok(hr == S_OK, "Got hr %#lx.\n", hr); @@ -2394,9 +2385,7 @@ static void test_connect_pin(void) hr = IFilterGraph_Disconnect(graph, &testsource->source.pin.IPin_iface); ok(hr == S_OK, "Got hr %#lx.\n", hr); -skip_connection_test: - if (meminput) - IMemInputPin_Release(meminput); + IMemInputPin_Release(meminput); IPin_Release(sink); IPin_Release(source); -- GitLab https://gitlab.winehq.org/wine/wine/-/merge_requests/11679
From: Brendan McGrath <bmcgrath@codeweavers.com> --- dlls/quartz/colorconv.c | 52 +++++++++++++++++++++++++++++++++++ dlls/quartz/tests/colorconv.c | 2 -- 2 files changed, 52 insertions(+), 2 deletions(-) diff --git a/dlls/quartz/colorconv.c b/dlls/quartz/colorconv.c index f91a6eb9f0b..e60f34fa5a4 100644 --- a/dlls/quartz/colorconv.c +++ b/dlls/quartz/colorconv.c @@ -152,9 +152,61 @@ static HRESULT color_source_query_interface(struct strmbase_pin *iface, REFIID i return S_OK; } +static HRESULT color_source_get_media_type(struct strmbase_pin *iface, unsigned int index, AM_MEDIA_TYPE *mt) +{ + struct color_converter *filter = impl_from_strmbase_filter(iface->filter); + const VIDEOINFOHEADER *sink_format; + const struct subtype *subtype; + VIDEOINFO *format; + + if (!filter->sink.pin.peer || index >= ARRAY_SIZE(subtypes)) + return VFW_S_NO_MORE_ITEMS; + + subtype = subtypes + index; + sink_format = (VIDEOINFOHEADER *)filter->sink.pin.mt.pbFormat; + + memset(mt, 0, sizeof(AM_MEDIA_TYPE)); + + if (!(format = CoTaskMemAlloc(mt->cbFormat = subtype->cbFormat))) + return E_OUTOFMEMORY; + + memset(format, 0, mt->cbFormat); + + format->rcSource = sink_format->rcSource; + format->rcTarget = sink_format->rcTarget; + format->dwBitRate = sink_format->dwBitRate; + format->dwBitErrorRate = sink_format->dwBitErrorRate; + format->AvgTimePerFrame = sink_format->AvgTimePerFrame; + + format->bmiHeader.biSize = sizeof(BITMAPINFOHEADER); + format->bmiHeader.biWidth = sink_format->bmiHeader.biWidth; + format->bmiHeader.biHeight = sink_format->bmiHeader.biHeight; + format->bmiHeader.biPlanes = sink_format->bmiHeader.biPlanes; + format->bmiHeader.biBitCount = subtype->bitcount; + format->bmiHeader.biCompression = subtype->compression; + format->bmiHeader.biSizeImage = format->bmiHeader.biHeight * format->bmiHeader.biWidth * (subtype->bitcount / 8); + + if (IsEqualGUID(subtype->guid, &MEDIASUBTYPE_RGB565)) + { + format->dwBitMasks[iRED] = 0xf800; + format->dwBitMasks[iGREEN] = 0x07e0; + format->dwBitMasks[iBLUE] = 0x001f; + } + + mt->majortype = MEDIATYPE_Video; + mt->subtype = *subtype->guid; + mt->bFixedSizeSamples = TRUE; + mt->lSampleSize = format->bmiHeader.biSizeImage; + mt->formattype = FORMAT_VideoInfo; + mt->pbFormat = (BYTE *)format; + + return S_OK; +} + static const struct strmbase_source_ops source_ops = { .base.pin_query_interface = color_source_query_interface, + .base.pin_get_media_type = color_source_get_media_type, .pfnAttemptConnection = BaseOutputPinImpl_AttemptConnection, .pfnDecideAllocator = BaseOutputPinImpl_DecideAllocator, .pfnDecideBufferSize = color_source_DecideBufferSize, diff --git a/dlls/quartz/tests/colorconv.c b/dlls/quartz/tests/colorconv.c index 97ff64713a3..7dd01b9313f 100644 --- a/dlls/quartz/tests/colorconv.c +++ b/dlls/quartz/tests/colorconv.c @@ -1352,9 +1352,7 @@ static void test_media_types(void) ok(hr == S_OK, "Got hr %#lx.\n", hr); hr = IEnumMediaTypes_Next(enum_types, ARRAY_SIZE(subtypes), media_types, &num_types); - todo_wine ok(hr == S_OK, "Got hr %#lx.\n", hr); - todo_wine ok(num_types == 6, "Got num_types %lu.\n", num_types); mt = req_mt; -- GitLab https://gitlab.winehq.org/wine/wine/-/merge_requests/11679
From: Brendan McGrath <bmcgrath@codeweavers.com> --- dlls/quartz/colorconv.c | 11 +++++++++++ dlls/quartz/tests/colorconv.c | 1 - 2 files changed, 11 insertions(+), 1 deletion(-) diff --git a/dlls/quartz/colorconv.c b/dlls/quartz/colorconv.c index e60f34fa5a4..286e446e938 100644 --- a/dlls/quartz/colorconv.c +++ b/dlls/quartz/colorconv.c @@ -152,6 +152,16 @@ static HRESULT color_source_query_interface(struct strmbase_pin *iface, REFIID i return S_OK; } +static HRESULT color_source_query_accept(struct strmbase_pin *iface, const AM_MEDIA_TYPE *mt) +{ + struct color_converter *filter = impl_from_strmbase_filter(iface->filter); + + if (!filter->sink.pin.peer) + return S_FALSE; + + return get_subtype(mt) ? S_OK : S_FALSE; +} + static HRESULT color_source_get_media_type(struct strmbase_pin *iface, unsigned int index, AM_MEDIA_TYPE *mt) { struct color_converter *filter = impl_from_strmbase_filter(iface->filter); @@ -206,6 +216,7 @@ static HRESULT color_source_get_media_type(struct strmbase_pin *iface, unsigned static const struct strmbase_source_ops source_ops = { .base.pin_query_interface = color_source_query_interface, + .base.pin_query_accept = color_source_query_accept, .base.pin_get_media_type = color_source_get_media_type, .pfnAttemptConnection = BaseOutputPinImpl_AttemptConnection, .pfnDecideAllocator = BaseOutputPinImpl_DecideAllocator, diff --git a/dlls/quartz/tests/colorconv.c b/dlls/quartz/tests/colorconv.c index 7dd01b9313f..4b058091a3a 100644 --- a/dlls/quartz/tests/colorconv.c +++ b/dlls/quartz/tests/colorconv.c @@ -1400,7 +1400,6 @@ static void test_media_types(void) /* The previously accepted media type is no longer accepted after disconnect */ hr = IPin_QueryAccept(source, &req_mt); - todo_wine ok(hr == S_FALSE, "Got hr %#lx.\n", hr); FreeMediaType(&req_mt); -- GitLab https://gitlab.winehq.org/wine/wine/-/merge_requests/11679
From: Brendan McGrath <bmcgrath@codeweavers.com> --- dlls/quartz/colorconv.c | 11 +++++++++++ 1 file changed, 11 insertions(+) diff --git a/dlls/quartz/colorconv.c b/dlls/quartz/colorconv.c index 286e446e938..1a91400fa23 100644 --- a/dlls/quartz/colorconv.c +++ b/dlls/quartz/colorconv.c @@ -128,7 +128,10 @@ static const struct strmbase_sink_ops sink_ops = static HRESULT WINAPI color_source_DecideBufferSize( struct strmbase_source *iface, IMemAllocator *alloc, ALLOCATOR_PROPERTIES *props) { + const struct subtype *subtype; ALLOCATOR_PROPERTIES actual; + BITMAPINFOHEADER *header; + long min_image_size; if (!props->cbAlign) props->cbAlign = 1; @@ -136,6 +139,14 @@ static HRESULT WINAPI color_source_DecideBufferSize( if (!props->cBuffers) props->cBuffers = 1; + subtype = get_subtype(&iface->pin.mt); + + header = &((VIDEOINFOHEADER *)iface->pin.mt.pbFormat)->bmiHeader; + min_image_size = header->biWidth * header->biHeight * (subtype->bitcount / 8); + + if (props->cbBuffer < min_image_size) + props->cbBuffer = min_image_size; + return IMemAllocator_SetProperties(alloc, props, &actual); } -- GitLab https://gitlab.winehq.org/wine/wine/-/merge_requests/11679
From: Brendan McGrath <bmcgrath@codeweavers.com> --- dlls/quartz/Makefile.in | 2 +- dlls/quartz/colorconv.c | 71 +++++++++++++++++++++++++++++------ dlls/quartz/tests/colorconv.c | 6 +++ 3 files changed, 67 insertions(+), 12 deletions(-) diff --git a/dlls/quartz/Makefile.in b/dlls/quartz/Makefile.in index 1e38b24ef73..98aac7c5553 100644 --- a/dlls/quartz/Makefile.in +++ b/dlls/quartz/Makefile.in @@ -1,6 +1,6 @@ MODULE = quartz.dll IMPORTLIB = quartz -IMPORTS = strmiids dxguid strmbase uuid dsound msacm32 msvfw32 ole32 oleaut32 rpcrt4 user32 gdi32 advapi32 winmm msvcrt +IMPORTS = strmiids dxguid strmbase uuid dsound msacm32 msvfw32 ole32 oleaut32 rpcrt4 user32 gdi32 advapi32 winmm msvcrt dmoguids wmcodecdspuuid DELAYIMPORTS = ddraw VER_PRODUCTVERSION = 6,5,1,902 diff --git a/dlls/quartz/colorconv.c b/dlls/quartz/colorconv.c index 1a91400fa23..6d768c81897 100644 --- a/dlls/quartz/colorconv.c +++ b/dlls/quartz/colorconv.c @@ -20,7 +20,9 @@ #include "quartz_private.h" +#include "mediaobj.h" #include "vfw.h" +#include "wmcodecdsp.h" #include "wine/debug.h" @@ -34,6 +36,8 @@ struct color_converter struct strmbase_passthrough passthrough; struct strmbase_sink sink; + + IMediaObject *dmo; }; struct subtype @@ -79,6 +83,34 @@ static const struct subtype *get_subtype(const AM_MEDIA_TYPE *mt) return subtype; } +static LONG calculate_stride(const BITMAPINFOHEADER *bmi_header) +{ + LONG stride = (bmi_header->biWidth * (bmi_header->biBitCount / 8) + 3) & ~3; + if (bmi_header->biHeight >= 0) + return stride; + else + return -stride; +} + +static void populate_output_dmo_mt( + const AM_MEDIA_TYPE *input_mt, const AM_MEDIA_TYPE *output_mt, DMO_MEDIA_TYPE *dmo_mt) +{ + const VIDEOINFOHEADER *input_video_info; + VIDEOINFOHEADER *video_info; + + input_video_info = (VIDEOINFOHEADER *)input_mt->pbFormat; + + video_info = calloc(1, sizeof(*video_info)); + memcpy(video_info, output_mt->pbFormat, sizeof(*video_info)); + video_info->bmiHeader.biWidth = input_video_info->bmiHeader.biWidth; + video_info->bmiHeader.biHeight = input_video_info->bmiHeader.biHeight; + video_info->bmiHeader.biSizeImage = abs(calculate_stride(&video_info->bmiHeader)) * video_info->bmiHeader.biHeight; + + memcpy(dmo_mt, output_mt, sizeof(*dmo_mt)); + dmo_mt->pbFormat = (BYTE *)video_info; + dmo_mt->lSampleSize = video_info->bmiHeader.biSizeImage; +} + static struct color_converter *impl_from_strmbase_filter(struct strmbase_filter *iface) { return CONTAINING_RECORD(iface, struct color_converter, filter); @@ -107,15 +139,15 @@ static HRESULT color_sink_query_accept(struct strmbase_pin *iface, const AM_MEDI static HRESULT color_sink_connect(struct strmbase_sink *iface, IPin *peer, const AM_MEDIA_TYPE *mt) { - if (!get_subtype(mt)) - { - TRACE("Connection refused\n"); - return VFW_E_TYPE_NOT_ACCEPTED; - } + struct color_converter *filter = impl_from_strmbase_filter(iface->pin.filter); + HRESULT hr = VFW_E_TYPE_NOT_ACCEPTED; - /* TODO: Set up color conversion */ - FIXME("stub\n"); - return S_OK; + if (get_subtype(mt)) + hr = IMediaObject_SetInputType(filter->dmo, 0, mt, 0); + + TRACE("Returning %#lx.\n", hr); + + return hr; } static const struct strmbase_sink_ops sink_ops = @@ -128,10 +160,17 @@ static const struct strmbase_sink_ops sink_ops = static HRESULT WINAPI color_source_DecideBufferSize( struct strmbase_source *iface, IMemAllocator *alloc, ALLOCATOR_PROPERTIES *props) { + struct color_converter *filter = impl_from_strmbase_filter(iface->pin.filter); const struct subtype *subtype; ALLOCATOR_PROPERTIES actual; BITMAPINFOHEADER *header; + DMO_MEDIA_TYPE dmo_mt; long min_image_size; + HRESULT hr; + + populate_output_dmo_mt(&filter->sink.pin.mt, &iface->pin.mt, &dmo_mt); + if (FAILED(hr = IMediaObject_SetOutputType(filter->dmo, 0, &dmo_mt, 0))) + return hr; if (!props->cbAlign) props->cbAlign = 1; @@ -262,6 +301,8 @@ static void color_destroy(struct strmbase_filter *iface) strmbase_passthrough_cleanup(&filter->passthrough); strmbase_filter_cleanup(&filter->filter); + IMediaObject_Release(filter->dmo); + free(filter); } @@ -326,8 +367,16 @@ HRESULT color_create(IUnknown *outer, IUnknown **out) strmbase_passthrough_init(&object->passthrough, (IUnknown *)&object->source.pin.IPin_iface); ISeekingPassThru_Init(&object->passthrough.ISeekingPassThru_iface, FALSE, &object->sink.pin.IPin_iface); - TRACE("Created Color Converter %p.\n", object); - *out = &object->filter.IUnknown_inner; + if (SUCCEEDED(hr = CoCreateInstance(&CLSID_CColorConvertDMO, NULL, CLSCTX_INPROC_SERVER, &IID_IMediaObject, + (void **)&object->dmo))) + { + TRACE("Created Color Converter %p.\n", object); + *out = &object->filter.IUnknown_inner; + } + else + { + ERR("Failed to create color conversion transform %#lx.\n", hr); + } - return S_OK; + return hr; } diff --git a/dlls/quartz/tests/colorconv.c b/dlls/quartz/tests/colorconv.c index 4b058091a3a..4807c7601f8 100644 --- a/dlls/quartz/tests/colorconv.c +++ b/dlls/quartz/tests/colorconv.c @@ -1310,9 +1310,12 @@ static void test_media_types(void) ok(hr == S_OK, "Got hr %#lx.\n", hr); hr = IPin_ReceiveConnection(sink, &peer->source.pin.IPin_iface, &req_mt); + /* DMO color converter does not support ARGB32 */ + todo_wine_if(i == 0) ok(hr == S_OK, "Got hr %#lx.\n", hr); hr = IPin_Disconnect(sink); + todo_wine_if(i == 0) ok(hr == S_OK, "Got hr %#lx.\n", hr); /* Test negative height */ @@ -1322,9 +1325,12 @@ static void test_media_types(void) ok(hr == S_OK, "Got hr %#lx.\n", hr); hr = IPin_ReceiveConnection(sink, &peer->source.pin.IPin_iface, &req_mt); + /* DMO color converter does not support ARGB32 */ + todo_wine_if(i == 0) ok(hr == S_OK, "Got hr %#lx.\n", hr); hr = IPin_Disconnect(sink); + todo_wine_if(i == 0) ok(hr == S_OK, "Got hr %#lx.\n", hr); winetest_pop_context(); -- GitLab https://gitlab.winehq.org/wine/wine/-/merge_requests/11679
From: Brendan McGrath <bmcgrath@codeweavers.com> --- dlls/quartz/Makefile.in | 2 +- dlls/quartz/colorconv.c | 372 +++++++++++++++++++++++++++++++++- dlls/quartz/tests/colorconv.c | 15 -- 3 files changed, 366 insertions(+), 23 deletions(-) diff --git a/dlls/quartz/Makefile.in b/dlls/quartz/Makefile.in index 98aac7c5553..54d559a12d8 100644 --- a/dlls/quartz/Makefile.in +++ b/dlls/quartz/Makefile.in @@ -1,6 +1,6 @@ MODULE = quartz.dll IMPORTLIB = quartz -IMPORTS = strmiids dxguid strmbase uuid dsound msacm32 msvfw32 ole32 oleaut32 rpcrt4 user32 gdi32 advapi32 winmm msvcrt dmoguids wmcodecdspuuid +IMPORTS = mfplat strmiids dxguid strmbase uuid dsound msacm32 msvfw32 ole32 oleaut32 rpcrt4 user32 gdi32 advapi32 winmm msvcrt dmoguids wmcodecdspuuid DELAYIMPORTS = ddraw VER_PRODUCTVERSION = 6,5,1,902 diff --git a/dlls/quartz/colorconv.c b/dlls/quartz/colorconv.c index 6d768c81897..22b4703c819 100644 --- a/dlls/quartz/colorconv.c +++ b/dlls/quartz/colorconv.c @@ -21,6 +21,7 @@ #include "quartz_private.h" #include "mediaobj.h" +#include "mfapi.h" #include "vfw.h" #include "wmcodecdsp.h" @@ -38,8 +39,129 @@ struct color_converter struct strmbase_sink sink; IMediaObject *dmo; + LONG sample_output_stride; + LONG dmo_output_stride; + ULONG dmo_output_sample_size; }; +struct buffer_for_sample +{ + IMediaBuffer IMediaBuffer_iface; + LONG refcount; + + IMediaSample *sample; +}; + +struct buffer +{ + IMediaBuffer IMediaBuffer_iface; + LONG refcount; + + BYTE *data; + DWORD length; + DWORD max_length; +}; + +static struct buffer *buffer_from_IMediaBuffer(IMediaBuffer *iface) +{ + return CONTAINING_RECORD(iface, struct buffer, IMediaBuffer_iface); +} + +static HRESULT WINAPI buffer_QueryInterface(IMediaBuffer *iface, REFIID iid, void **out) +{ + if (IsEqualGUID(iid, &IID_IUnknown) || IsEqualGUID(iid, &IID_IMediaBuffer)) + { + *out = iface; + } + else + { + *out = NULL; + return E_NOINTERFACE; + } + + IUnknown_AddRef((IUnknown *)(*out)); + return S_OK; +} + +static ULONG WINAPI buffer_AddRef(IMediaBuffer *iface) +{ + struct buffer *buffer = buffer_from_IMediaBuffer(iface); + ULONG refcount; + + refcount = InterlockedIncrement(&buffer->refcount); + + return refcount; +} + +static ULONG WINAPI buffer_Release(IMediaBuffer *iface) +{ + struct buffer *buffer = buffer_from_IMediaBuffer(iface); + ULONG refcount; + + refcount = InterlockedDecrement(&buffer->refcount); + + if (!refcount) + { + free(buffer->data); + free(buffer); + } + + return refcount; +} + +static HRESULT WINAPI buffer_SetLength(IMediaBuffer *iface, DWORD length) +{ + struct buffer *buffer = buffer_from_IMediaBuffer(iface); + + buffer->length = length; + + return S_OK; +} + +static HRESULT WINAPI buffer_GetMaxLength(IMediaBuffer *iface, DWORD *max_length) +{ + struct buffer *buffer = buffer_from_IMediaBuffer(iface); + + *max_length = buffer->max_length; + + return S_OK; +} + +static HRESULT WINAPI buffer_GetBufferAndLength(IMediaBuffer *iface, BYTE **data, DWORD *length) +{ + struct buffer *buffer = buffer_from_IMediaBuffer(iface); + + *length = buffer->length; + if (data) + *data = buffer->data; + + return S_OK; +} + +static IMediaBufferVtbl buffer_vtbl = +{ + buffer_QueryInterface, + buffer_AddRef, + buffer_Release, + buffer_SetLength, + buffer_GetMaxLength, + buffer_GetBufferAndLength, +}; + +static struct buffer *create_buffer(DWORD max_length) +{ + struct buffer *buffer; + + buffer = calloc(1, sizeof(*buffer)); + buffer->IMediaBuffer_iface.lpVtbl = &buffer_vtbl; + buffer->refcount = 1; + + buffer->data = malloc(max_length); + buffer->max_length = max_length; + + return buffer; +} + struct subtype { const GUID *guid; @@ -111,6 +233,110 @@ static void populate_output_dmo_mt( dmo_mt->lSampleSize = video_info->bmiHeader.biSizeImage; } +static struct buffer_for_sample *buffer_for_sample_from_IMediaBuffer(IMediaBuffer *iface) +{ + return CONTAINING_RECORD(iface, struct buffer_for_sample, IMediaBuffer_iface); +} + +static HRESULT WINAPI buffer_for_sample_QueryInterface(IMediaBuffer *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_IMediaBuffer)) + { + *out = 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 buffer_for_sample_AddRef(IMediaBuffer *iface) +{ + struct buffer_for_sample *buffer = buffer_for_sample_from_IMediaBuffer(iface); + ULONG refcount; + + refcount = InterlockedIncrement(&buffer->refcount); + + return refcount; +} + +static ULONG WINAPI buffer_for_sample_Release(IMediaBuffer *iface) +{ + struct buffer_for_sample *buffer = buffer_for_sample_from_IMediaBuffer(iface); + ULONG refcount; + + refcount = InterlockedDecrement(&buffer->refcount); + + if (!refcount) + { + IMediaSample_Release(buffer->sample); + free(buffer); + } + + return refcount; +} + +static HRESULT WINAPI buffer_for_sample_SetLength(IMediaBuffer *iface, DWORD len) +{ + struct buffer_for_sample *buffer = buffer_for_sample_from_IMediaBuffer(iface); + + TRACE("iface %p, len %lu.\n", iface, len); + + return IMediaSample_SetActualDataLength(buffer->sample, len); +} + +static HRESULT WINAPI buffer_for_sample_GetMaxLength(IMediaBuffer *iface, DWORD *len) +{ + struct buffer_for_sample *buffer = buffer_for_sample_from_IMediaBuffer(iface); + + TRACE("iface %p, len %p.\n", iface, len); + + *len = IMediaSample_GetSize(buffer->sample); + return S_OK; +} + +static HRESULT WINAPI buffer_for_sample_GetBufferAndLength(IMediaBuffer *iface, BYTE **data, DWORD *len) +{ + struct buffer_for_sample *buffer = buffer_for_sample_from_IMediaBuffer(iface); + + TRACE("iface %p, data %p, len %p.\n", iface, data, len); + + *len = IMediaSample_GetActualDataLength(buffer->sample); + if (data) + return IMediaSample_GetPointer(buffer->sample, data); + return S_OK; +} + +static const IMediaBufferVtbl buffer_for_sample_vtbl = +{ + buffer_for_sample_QueryInterface, + buffer_for_sample_AddRef, + buffer_for_sample_Release, + buffer_for_sample_SetLength, + buffer_for_sample_GetMaxLength, + buffer_for_sample_GetBufferAndLength, +}; + +static struct buffer_for_sample *create_buffer_for_sample(IMediaSample *sample) +{ + struct buffer_for_sample *buffer; + + buffer = calloc(1, sizeof(*buffer)); + buffer->IMediaBuffer_iface.lpVtbl = &buffer_for_sample_vtbl; + buffer->refcount = 1; + + IMediaSample_AddRef(buffer->sample = sample); + + return buffer; +} + static struct color_converter *impl_from_strmbase_filter(struct strmbase_filter *iface) { return CONTAINING_RECORD(iface, struct color_converter, filter); @@ -150,13 +376,6 @@ static HRESULT color_sink_connect(struct strmbase_sink *iface, IPin *peer, const return hr; } -static const struct strmbase_sink_ops sink_ops = -{ - .base.pin_query_interface = color_sink_query_interface, - .base.pin_query_accept = color_sink_query_accept, - .sink_connect = color_sink_connect, -}; - static HRESULT WINAPI color_source_DecideBufferSize( struct strmbase_source *iface, IMemAllocator *alloc, ALLOCATOR_PROPERTIES *props) { @@ -172,6 +391,10 @@ static HRESULT WINAPI color_source_DecideBufferSize( if (FAILED(hr = IMediaObject_SetOutputType(filter->dmo, 0, &dmo_mt, 0))) return hr; + filter->sample_output_stride = calculate_stride(&((VIDEOINFOHEADER *)iface->pin.mt.pbFormat)->bmiHeader); + filter->dmo_output_stride = calculate_stride(&((VIDEOINFOHEADER *)dmo_mt.pbFormat)->bmiHeader); + filter->dmo_output_sample_size = dmo_mt.lSampleSize; + if (!props->cbAlign) props->cbAlign = 1; @@ -189,6 +412,141 @@ static HRESULT WINAPI color_source_DecideBufferSize( return IMemAllocator_SetProperties(alloc, props, &actual); } +static HRESULT WINAPI color_sink_Receive(struct strmbase_sink *iface, IMediaSample *src_sample) +{ + struct color_converter *filter = impl_from_strmbase_filter(iface->pin.filter); + BITMAPINFOHEADER *input_bmi_header, *output_bmi_header; + struct buffer_for_sample *src_buffer; + BYTE *dst_buff, *src_buff, *dest; + DMO_OUTPUT_DATA_BUFFER output; + const struct subtype *subtype; + struct buffer *dst_buffer; + BITMAPINFOHEADER *header; + IMediaSample *dst_sample; + DWORD flags = 0, status; + long output_image_size; + LONGLONG start, stop; + LONG dst_size; + UINT32 *data; + HRESULT hr; + int i; + + /* We do not expect pin connection state to change while the filter is + * running. This guarantee is necessary, since otherwise we would have to + * take the filter lock, and we can't take the filter lock from a streaming + * thread. */ + if (!filter->source.pMemInputPin) + { + WARN("Source is not connected, returning VFW_E_NOT_CONNECTED.\n"); + return VFW_E_NOT_CONNECTED; + } + + if (filter->filter.state == State_Stopped) + return VFW_E_WRONG_STATE; + + if (filter->sink.flushing) + return S_FALSE; + + hr = IMediaSample_GetPointer(src_sample, &src_buff); + if (FAILED(hr)) + { + ERR("Failed to get input buffer pointer, hr %#lx.\n", hr); + return hr; + } + + if (FAILED(hr = IMemAllocator_GetBuffer(filter->source.pAllocator, &dst_sample, NULL, NULL, 0))) + { + ERR("Failed to get sample, hr %#lx.\n", hr); + return hr; + } + + hr = IMediaSample_GetPointer(dst_sample, &dst_buff); + if (FAILED(hr)) + { + ERR("Failed to get output buffer pointer, hr %#lx.\n", hr); + IMediaSample_Release(dst_sample); + return hr; + } + + subtype = get_subtype(&filter->source.pin.mt); + header = &((VIDEOINFOHEADER *)filter->sink.pin.mt.pbFormat)->bmiHeader; + output_image_size = header->biWidth * header->biHeight * (subtype->bitcount / 8); + dst_size = IMediaSample_GetSize(dst_sample); + if (dst_size < output_image_size) + { + ERR("Sample size is too small (%ld < %lu).\n", dst_size, output_image_size); + IMediaSample_Release(dst_sample); + return E_FAIL; + } + + if (IMediaSample_IsPreroll(src_sample) == S_OK) + flags |= ICDECOMPRESS_PREROLL; + hr = IMediaSample_GetTime(src_sample, &start, &stop); + + /* perform color conversion */ + src_buffer = create_buffer_for_sample(src_sample); + hr = IMediaObject_ProcessInput(filter->dmo, 0, &src_buffer->IMediaBuffer_iface, + hr == S_OK ? DMO_INPUT_DATA_BUFFERF_TIME & DMO_INPUT_DATA_BUFFERF_TIMELENGTH : 0, start, stop - start); + IMediaBuffer_Release(&src_buffer->IMediaBuffer_iface); + + input_bmi_header = &((VIDEOINFOHEADER *)filter->sink.pin.mt.pbFormat)->bmiHeader; + output_bmi_header = &((VIDEOINFOHEADER *)filter->source.pin.mt.pbFormat)->bmiHeader; + dst_buffer = create_buffer(filter->dmo_output_sample_size); + memset(&output, 0, sizeof(output)); + output.pBuffer = &dst_buffer->IMediaBuffer_iface; + hr = IMediaObject_ProcessOutput(filter->dmo, 0, 1, &output, &status); + if (input_bmi_header->biBitCount < output_bmi_header->biBitCount && output_bmi_header->biBitCount == 32) + { + /* Fix the value of the alpha channel. DMO uses 0xff, whilst quartz uses 0x00. */ + data = (UINT32 *)dst_buffer->data; + + for (i = 0; i < input_bmi_header->biHeight * input_bmi_header->biWidth; i++) + *data++ &= 0xffffff; + } + IMediaSample_GetPointer(dst_sample, &dest); + if (filter->sample_output_stride < 0) + dest += -filter->sample_output_stride * (input_bmi_header->biHeight - 1); + + MFCopyImage(dest, filter->sample_output_stride, dst_buffer->data, filter->dmo_output_stride, + input_bmi_header->biWidth * (output_bmi_header->biBitCount / 8), input_bmi_header->biHeight); + IMediaBuffer_Release(output.pBuffer); + + /* Drop sample if it's intended to be dropped */ + if (flags & ICDECOMPRESS_HURRYUP) + { + IMediaSample_Release(dst_sample); + return S_OK; + } + + IMediaSample_SetActualDataLength(dst_sample, output_image_size); + + IMediaSample_SetPreroll(dst_sample, (IMediaSample_IsPreroll(src_sample) == S_OK)); + IMediaSample_SetDiscontinuity(dst_sample, (IMediaSample_IsDiscontinuity(src_sample) == S_OK)); + IMediaSample_SetSyncPoint(dst_sample, TRUE); + + if (hr == S_OK) + IMediaSample_SetTime(dst_sample, &start, &stop); + else if (hr == VFW_S_NO_STOP_TIME) + IMediaSample_SetTime(dst_sample, &start, NULL); + else + IMediaSample_SetTime(dst_sample, NULL, NULL); + + hr = IMemInputPin_Receive(filter->source.pMemInputPin, dst_sample); + if (hr != S_OK && hr != VFW_E_NOT_CONNECTED) + ERR("Failed to send sample, hr %#lx.\n", hr); + + IMediaSample_Release(dst_sample); + return hr; +} + +static const struct strmbase_sink_ops sink_ops = +{ + .base.pin_query_interface = color_sink_query_interface, + .base.pin_query_accept = color_sink_query_accept, + .pfnReceive = color_sink_Receive, + .sink_connect = color_sink_connect, +}; + static HRESULT color_source_query_interface(struct strmbase_pin *iface, REFIID iid, void **out) { struct color_converter *filter = impl_from_strmbase_filter(iface->filter); diff --git a/dlls/quartz/tests/colorconv.c b/dlls/quartz/tests/colorconv.c index 4807c7601f8..494b5f757c8 100644 --- a/dlls/quartz/tests/colorconv.c +++ b/dlls/quartz/tests/colorconv.c @@ -1915,13 +1915,7 @@ static void test_sample_processing( sink_allocator->expect_get_media_type = TRUE; sink_allocator->media_type_checked = FALSE; hr = IMemInputPin_Receive(input, sample); - todo_wine ok(hr == S_OK, "Got hr %#lx.\n", hr); - if (hr != S_OK) - { - winetest_pop_context(); - continue; - } ok(sink_allocator->sample_refcount == 1, "Got sample refcount %ld.\n", sink_allocator->sample_refcount); todo_wine @@ -2020,7 +2014,6 @@ static void test_sample_processing( sink_allocator->expect_get_media_type = TRUE; sink_allocator->media_type_checked = FALSE; hr = IMemInputPin_Receive(input, sample); - todo_wine ok(hr == S_OK, "Got hr %#lx.\n", hr); todo_wine ok(sink_allocator->media_type_checked, "Expected media type to have been checked.\n"); @@ -2032,10 +2025,7 @@ static void test_sample_processing( sample = testsink->sample; testsink->sample = NULL; - todo_wine ok(sample != NULL, "Expected out peer sample.\n"); - if (sample == NULL) - goto skip_test; hr = IMediaSample_GetPointer(sample, &buff); ok(hr == S_OK, "Get hr %#lx.\n", hr); @@ -2126,7 +2116,6 @@ static void test_sample_processing( IMediaSample_Release(sample); ok(sink_allocator->sample_refcount == 0, "Got sample refcount %ld.\n", sink_allocator->sample_refcount); -skip_test: hr = IMemAllocator_Decommit(allocator); ok(hr == S_OK, "Got hr %#lx.\n", hr); @@ -2185,9 +2174,7 @@ static void test_streaming_events(IMediaControl *control, IPin *sink, IMemInputP sink_allocator->media_type_checked = FALSE; sink_allocator->expect_get_media_type = TRUE; hr = IMemInputPin_Receive(input, sample); - todo_wine ok(hr == S_OK, "Got hr %#lx.\n", hr); - todo_wine ok(testsink->sample != NULL, "Expected to receive sample.\n"); todo_wine ok(sink_allocator->media_type_checked, "Expected media type to have been checked.\n"); @@ -2216,9 +2203,7 @@ static void test_streaming_events(IMediaControl *control, IPin *sink, IMemInputP sink_allocator->expect_get_media_type = TRUE; sink_allocator->expect_get_buffer = TRUE; hr = IMemInputPin_Receive(input, sample); - todo_wine ok(hr == S_OK, "Got hr %#lx.\n", hr); - todo_wine ok(testsink->sample != NULL, "Expected to receive sample.\n"); todo_wine ok(sink_allocator->media_type_checked, "Expected media type to have been checked.\n"); -- GitLab https://gitlab.winehq.org/wine/wine/-/merge_requests/11679
From: Brendan McGrath <bmcgrath@codeweavers.com> --- dlls/quartz/colorconv.c | 27 +++++++++++++++++++++++++++ dlls/quartz/tests/colorconv.c | 7 ------- 2 files changed, 27 insertions(+), 7 deletions(-) diff --git a/dlls/quartz/colorconv.c b/dlls/quartz/colorconv.c index 22b4703c819..22cb3d9da2c 100644 --- a/dlls/quartz/colorconv.c +++ b/dlls/quartz/colorconv.c @@ -426,6 +426,7 @@ static HRESULT WINAPI color_sink_Receive(struct strmbase_sink *iface, IMediaSamp DWORD flags = 0, status; long output_image_size; LONGLONG start, stop; + AM_MEDIA_TYPE *mt; LONG dst_size; UINT32 *data; HRESULT hr; @@ -460,6 +461,32 @@ static HRESULT WINAPI color_sink_Receive(struct strmbase_sink *iface, IMediaSamp return hr; } + /* Handle dynamic format change. */ + if ((hr = IMediaSample_GetMediaType(dst_sample, &mt)) == S_OK) + { + if (memcmp(mt, &filter->source.pin.mt, offsetof(AM_MEDIA_TYPE, pbFormat)) || + memcmp(mt->pbFormat, filter->source.pin.mt.pbFormat, mt->cbFormat)) + { + DMO_MEDIA_TYPE dmo_mt; + + populate_output_dmo_mt(&filter->sink.pin.mt, mt, &dmo_mt); + if (FAILED(hr = IMediaObject_SetOutputType(filter->dmo, 0, &dmo_mt, 0))) + WARN("Failed to update media type, hr %#lx.\n", hr); + + filter->sample_output_stride = calculate_stride(&((VIDEOINFOHEADER *)mt->pbFormat)->bmiHeader); + filter->dmo_output_stride = calculate_stride(&((VIDEOINFOHEADER *)dmo_mt.pbFormat)->bmiHeader); + filter->dmo_output_sample_size = dmo_mt.lSampleSize; + + FreeMediaType(&filter->source.pin.mt); + filter->source.pin.mt = *mt; + CoTaskMemFree(mt); + } + } + else if (hr != S_FALSE) + { + ERR("Failed to get media type, hr %#lx.\n", hr); + } + hr = IMediaSample_GetPointer(dst_sample, &dst_buff); if (FAILED(hr)) { diff --git a/dlls/quartz/tests/colorconv.c b/dlls/quartz/tests/colorconv.c index 494b5f757c8..b85a4da1c5e 100644 --- a/dlls/quartz/tests/colorconv.c +++ b/dlls/quartz/tests/colorconv.c @@ -1918,7 +1918,6 @@ static void test_sample_processing( ok(hr == S_OK, "Got hr %#lx.\n", hr); ok(sink_allocator->sample_refcount == 1, "Got sample refcount %ld.\n", sink_allocator->sample_refcount); - todo_wine ok(sink_allocator->media_type_checked, "Expected media type to have been checked.\n"); ok(testsink->sample != NULL, "Expected out peer sample.\n"); sink_allocator->expect_get_buffer = FALSE; @@ -2015,7 +2014,6 @@ static void test_sample_processing( sink_allocator->media_type_checked = FALSE; hr = IMemInputPin_Receive(input, sample); ok(hr == S_OK, "Got hr %#lx.\n", hr); - todo_wine ok(sink_allocator->media_type_checked, "Expected media type to have been checked.\n"); sink_allocator->expect_get_buffer = FALSE; sink_allocator->expect_get_media_type = FALSE; @@ -2084,7 +2082,6 @@ static void test_sample_processing( sink_allocator->media_type_checked = FALSE; hr = IMemInputPin_Receive(input, sample); ok(hr == S_OK, "Got hr %#lx.\n", hr); - todo_wine ok(sink_allocator->media_type_checked, "Expected media type to have been checked.\n"); sink_allocator->expect_get_buffer = FALSE; sink_allocator->expect_get_media_type = FALSE; @@ -2109,7 +2106,6 @@ static void test_sample_processing( for (unsigned int i = 0; i < image_size; ++i) diff += abs((int)buff[i] - (int)rgb32_image->data[i]); diff = diff * 100 / 256 / image_size; - todo_wine ok(diff == 0, "Got %I64u%% difference.\n", diff); free(rgb32_image); @@ -2146,7 +2142,6 @@ static void test_streaming_events(IMediaControl *control, IPin *sink, IMemInputP ok(hr == S_OK, "Got hr %#lx.\n", hr); sink_allocator->expect_get_buffer = FALSE; sink_allocator->expect_get_media_type = FALSE; - todo_wine ok(sink_allocator->media_type_checked, "Expected media type to have been checked.\n"); hr = IMediaSample_GetPointer(sample, &data); ok(hr == S_OK, "Got hr %#lx.\n", hr); @@ -2176,7 +2171,6 @@ static void test_streaming_events(IMediaControl *control, IPin *sink, IMemInputP hr = IMemInputPin_Receive(input, sample); ok(hr == S_OK, "Got hr %#lx.\n", hr); ok(testsink->sample != NULL, "Expected to receive sample.\n"); - todo_wine ok(sink_allocator->media_type_checked, "Expected media type to have been checked.\n"); sink_allocator->expect_get_media_type = FALSE; if (testsink->sample) @@ -2205,7 +2199,6 @@ static void test_streaming_events(IMediaControl *control, IPin *sink, IMemInputP hr = IMemInputPin_Receive(input, sample); ok(hr == S_OK, "Got hr %#lx.\n", hr); ok(testsink->sample != NULL, "Expected to receive sample.\n"); - todo_wine ok(sink_allocator->media_type_checked, "Expected media type to have been checked.\n"); sink_allocator->expect_get_buffer = FALSE; sink_allocator->expect_get_media_type = FALSE; -- GitLab https://gitlab.winehq.org/wine/wine/-/merge_requests/11679
From: Brendan McGrath <bmcgrath@codeweavers.com> --- dlls/quartz/colorconv.c | 9 +++++++++ 1 file changed, 9 insertions(+) diff --git a/dlls/quartz/colorconv.c b/dlls/quartz/colorconv.c index 22cb3d9da2c..b810e16a542 100644 --- a/dlls/quartz/colorconv.c +++ b/dlls/quartz/colorconv.c @@ -376,6 +376,14 @@ static HRESULT color_sink_connect(struct strmbase_sink *iface, IPin *peer, const return hr; } +static HRESULT color_sink_end_flush(struct strmbase_sink *iface) +{ + struct color_converter *filter = impl_from_strmbase_filter(iface->pin.filter); + if (filter->source.pin.peer) + return IPin_EndFlush(filter->source.pin.peer); + return S_OK; +} + static HRESULT WINAPI color_source_DecideBufferSize( struct strmbase_source *iface, IMemAllocator *alloc, ALLOCATOR_PROPERTIES *props) { @@ -572,6 +580,7 @@ static const struct strmbase_sink_ops sink_ops = .base.pin_query_accept = color_sink_query_accept, .pfnReceive = color_sink_Receive, .sink_connect = color_sink_connect, + .sink_end_flush = color_sink_end_flush, }; static HRESULT color_source_query_interface(struct strmbase_pin *iface, REFIID iid, void **out) -- GitLab https://gitlab.winehq.org/wine/wine/-/merge_requests/11679
From: Brendan McGrath <bmcgrath@codeweavers.com> --- dlls/quartz/colorconv.c | 11 +++++++++++ dlls/quartz/tests/colorconv.c | 1 - 2 files changed, 11 insertions(+), 1 deletion(-) diff --git a/dlls/quartz/colorconv.c b/dlls/quartz/colorconv.c index b810e16a542..6eacf78a88d 100644 --- a/dlls/quartz/colorconv.c +++ b/dlls/quartz/colorconv.c @@ -574,6 +574,16 @@ static HRESULT WINAPI color_sink_Receive(struct strmbase_sink *iface, IMediaSamp return hr; } +HRESULT color_sink_can_block(struct strmbase_sink *iface) +{ + struct color_converter *filter = impl_from_strmbase_filter(iface->pin.filter); + + if (!filter->source.pMemInputPin) + return VFW_E_NOT_CONNECTED; + + return IMemInputPin_ReceiveCanBlock(filter->source.pMemInputPin); +} + static const struct strmbase_sink_ops sink_ops = { .base.pin_query_interface = color_sink_query_interface, @@ -581,6 +591,7 @@ static const struct strmbase_sink_ops sink_ops = .pfnReceive = color_sink_Receive, .sink_connect = color_sink_connect, .sink_end_flush = color_sink_end_flush, + .sink_receive_can_block = color_sink_can_block, }; static HRESULT color_source_query_interface(struct strmbase_pin *iface, REFIID iid, void **out) diff --git a/dlls/quartz/tests/colorconv.c b/dlls/quartz/tests/colorconv.c index b85a4da1c5e..18a609bd9cd 100644 --- a/dlls/quartz/tests/colorconv.c +++ b/dlls/quartz/tests/colorconv.c @@ -1831,7 +1831,6 @@ static void test_sample_processing( testsink->can_block = S_FALSE; hr = IMemInputPin_ReceiveCanBlock(input); - todo_wine ok(hr == S_FALSE, "Got hr %#lx.\n", hr); sink_allocator = mem_allocator_from_IMemAllocator(testsink->sink.pAllocator); -- GitLab https://gitlab.winehq.org/wine/wine/-/merge_requests/11679
2/14: ``` + if (FAILED(hr = CoCreateInstance( + &CLSID_MemoryAllocator, NULL, CLSCTX_INPROC_SERVER, &IID_IMemAllocator, (void **)&allocator))) + return hr; ``` Eh, what's up with the indentation here? 6/14: ``` static HRESULT color_sink_connect(struct strmbase_sink *iface, IPin *peer, const AM_MEDIA_TYPE *mt) { + if (!get_subtype(mt)) + { + TRACE("Connection refused\n"); + return VFW_E_TYPE_NOT_ACCEPTED; + } + + /* TODO: Set up color conversion */ FIXME("stub\n"); - return VFW_E_TYPE_NOT_ACCEPTED; + return S_OK; } ``` You don't need the media type check; strmbase calls query_accept() before reaching this callback. Since you're not doing anything else either, you can just leave out the callback entirely. 9/14: ``` + header = &((VIDEOINFOHEADER *)iface->pin.mt.pbFormat)->bmiHeader; ``` Generally you want to check formattype before doing this, especially since FORMAT_VideoInfo2 is a thing. 10/14: ``` + video_info = calloc(1, sizeof(*video_info)); + memcpy(video_info, output_mt->pbFormat, sizeof(*video_info)); + video_info->bmiHeader.biWidth = input_video_info->bmiHeader.biWidth; + video_info->bmiHeader.biHeight = input_video_info->bmiHeader.biHeight; + video_info->bmiHeader.biSizeImage = abs(calculate_stride(&video_info->bmiHeader)) * video_info->bmiHeader.biHeight; + + memcpy(dmo_mt, output_mt, sizeof(*dmo_mt)); + dmo_mt->pbFormat = (BYTE *)video_info; + dmo_mt->lSampleSize = video_info->bmiHeader.biSizeImage; ``` Why do you need to do these? Does the converter ignore width/height mismatch? I don't see tests for that, unless I'm blind. (Also, that biSizeImage calculation is rather awkward. I think it would make more sense to just multiply width * abs(height) * depth.) Also, I'd recommend adding a FIXME for nontrivial rcSource/rcTarget handling. I've seen applications depend on that. 11/14: ``` -IMPORTS = strmiids dxguid strmbase uuid dsound msacm32 msvfw32 ole32 oleaut32 rpcrt4 user32 gdi32 advapi32 winmm msvcrt dmoguids wmcodecdspuuid +IMPORTS = mfplat strmiids dxguid strmbase uuid dsound msacm32 msvfw32 ole32 oleaut32 rpcrt4 user32 gdi32 advapi32 winmm msvcrt dmoguids wmcodecdspuuid ``` mfplat should be a delay-import. I'm not convinced you really need it, though. The only thing you're using it for is MFCopyImage(), and this is probably something we should be avoiding in the first place; this should be zero-copy if at all possible. ``` + output_image_size = header->biWidth * header->biHeight * (subtype->bitcount / 8); ``` Not header->biSizeImage? Also I don't think that handles negative biHeight. ``` + if (IMediaSample_IsPreroll(src_sample) == S_OK) + flags |= ICDECOMPRESS_PREROLL; ``` That looks left over, as does the other VFW flag code. ``` + hr = IMediaSample_GetTime(src_sample, &start, &stop); + + /* perform color conversion */ + src_buffer = create_buffer_for_sample(src_sample); + hr = IMediaObject_ProcessInput(filter->dmo, 0, &src_buffer->IMediaBuffer_iface, + hr == S_OK ? DMO_INPUT_DATA_BUFFERF_TIME & DMO_INPUT_DATA_BUFFERF_TIMELENGTH : 0, start, stop - start); ``` That's hard to read, doesn't correctly handle VFW_S_NO_STOP_TIME, and looks like you typed & when you meant |. ``` + if (hr == S_OK) + IMediaSample_SetTime(dst_sample, &start, &stop); + else if (hr == VFW_S_NO_STOP_TIME) + IMediaSample_SetTime(dst_sample, &start, NULL); + else + IMediaSample_SetTime(dst_sample, NULL, NULL); ``` You've overwritten the hr you're trying to use here. You might as well just set this earlier. ``` + if (input_bmi_header->biBitCount < output_bmi_header->biBitCount && output_bmi_header->biBitCount == 32) + { + /* Fix the value of the alpha channel. DMO uses 0xff, whilst quartz uses 0x00. */ + data = (UINT32 *)dst_buffer->data; + + for (i = 0; i < input_bmi_header->biHeight * input_bmi_header->biWidth; i++) + *data++ &= 0xffffff; + } ``` Shouldn't this check for RGB32 specifically? This would be wrong for ARGB32. Also, does this not affect ARGB32 -> RGB32? But anyway, this is really not ideal for performance, and maybe it means we shouldn't be using colorcnv. 12/14: ``` + if (memcmp(mt, &filter->source.pin.mt, offsetof(AM_MEDIA_TYPE, pbFormat)) || + memcmp(mt->pbFormat, filter->source.pin.mt.pbFormat, mt->cbFormat)) ``` || at the beginning of the line. ``` + if (FAILED(hr = IMediaObject_SetOutputType(filter->dmo, 0, &dmo_mt, 0))) + WARN("Failed to update media type, hr %#lx.\n", hr); ``` I would make this an ERR; this shouldn't happen. 13/14: ``` +static HRESULT color_sink_end_flush(struct strmbase_sink *iface) +{ + struct color_converter *filter = impl_from_strmbase_filter(iface->pin.filter); + if (filter->source.pin.peer) + return IPin_EndFlush(filter->source.pin.peer); + return S_OK; +} ``` strmbase should already do this for you. 14/14: ``` +HRESULT color_sink_can_block(struct strmbase_sink *iface) ``` Missing static. Also, I would name it consistently with the method name, color_sink_receive_can_block(). -- https://gitlab.winehq.org/wine/wine/-/merge_requests/11679#note_149712
On Mon Aug 24 21:23:21 2026 +0000, Elizabeth Figura wrote:
2/14: ``` + if (FAILED(hr = CoCreateInstance( + &CLSID_MemoryAllocator, NULL, CLSCTX_INPROC_SERVER, &IID_IMemAllocator, (void **)&allocator))) + return hr; ``` Eh, what's up with the indentation here? 6/14: ``` static HRESULT color_sink_connect(struct strmbase_sink *iface, IPin *peer, const AM_MEDIA_TYPE *mt) { + if (!get_subtype(mt)) + { + TRACE("Connection refused\n"); + return VFW_E_TYPE_NOT_ACCEPTED; + } + + /* TODO: Set up color conversion */ FIXME("stub\n"); - return VFW_E_TYPE_NOT_ACCEPTED; + return S_OK; } ``` You don't need the media type check; strmbase calls query_accept() before reaching this callback. Since you're not doing anything else either, you can just leave out the callback entirely. 9/14: ``` + header = &((VIDEOINFOHEADER *)iface->pin.mt.pbFormat)->bmiHeader; ``` Generally you want to check formattype before doing this, especially since FORMAT_VideoInfo2 is a thing. 10/14: ``` + video_info = calloc(1, sizeof(*video_info)); + memcpy(video_info, output_mt->pbFormat, sizeof(*video_info)); + video_info->bmiHeader.biWidth = input_video_info->bmiHeader.biWidth; + video_info->bmiHeader.biHeight = input_video_info->bmiHeader.biHeight; + video_info->bmiHeader.biSizeImage = abs(calculate_stride(&video_info->bmiHeader)) * video_info->bmiHeader.biHeight; + + memcpy(dmo_mt, output_mt, sizeof(*dmo_mt)); + dmo_mt->pbFormat = (BYTE *)video_info; + dmo_mt->lSampleSize = video_info->bmiHeader.biSizeImage; ``` Why do you need to do these? Does the converter ignore width/height mismatch? I don't see tests for that, unless I'm blind. (Also, that biSizeImage calculation is rather awkward. I think it would make more sense to just multiply width * abs(height) * depth.) Also, I'd recommend adding a FIXME for nontrivial rcSource/rcTarget handling. I've seen applications depend on that. 11/14: ``` -IMPORTS = strmiids dxguid strmbase uuid dsound msacm32 msvfw32 ole32 oleaut32 rpcrt4 user32 gdi32 advapi32 winmm msvcrt dmoguids wmcodecdspuuid +IMPORTS = mfplat strmiids dxguid strmbase uuid dsound msacm32 msvfw32 ole32 oleaut32 rpcrt4 user32 gdi32 advapi32 winmm msvcrt dmoguids wmcodecdspuuid ``` mfplat should be a delay-import. I'm not convinced you really need it, though. The only thing you're using it for is MFCopyImage(), and this is probably something we should be avoiding in the first place; this should be zero-copy if at all possible. ``` + output_image_size = header->biWidth * header->biHeight * (subtype->bitcount / 8); ``` Not header->biSizeImage? Also I don't think that handles negative biHeight. ``` + if (IMediaSample_IsPreroll(src_sample) == S_OK) + flags |= ICDECOMPRESS_PREROLL; ``` That looks left over, as does the other VFW flag code. ``` + hr = IMediaSample_GetTime(src_sample, &start, &stop); + + /* perform color conversion */ + src_buffer = create_buffer_for_sample(src_sample); + hr = IMediaObject_ProcessInput(filter->dmo, 0, &src_buffer->IMediaBuffer_iface, + hr == S_OK ? DMO_INPUT_DATA_BUFFERF_TIME & DMO_INPUT_DATA_BUFFERF_TIMELENGTH : 0, start, stop - start); ``` That's hard to read, doesn't correctly handle VFW_S_NO_STOP_TIME, and looks like you typed & when you meant |. ``` + if (hr == S_OK) + IMediaSample_SetTime(dst_sample, &start, &stop); + else if (hr == VFW_S_NO_STOP_TIME) + IMediaSample_SetTime(dst_sample, &start, NULL); + else + IMediaSample_SetTime(dst_sample, NULL, NULL); ``` You've overwritten the hr you're trying to use here. You might as well just set this earlier. ``` + if (input_bmi_header->biBitCount < output_bmi_header->biBitCount && output_bmi_header->biBitCount == 32) + { + /* Fix the value of the alpha channel. DMO uses 0xff, whilst quartz uses 0x00. */ + data = (UINT32 *)dst_buffer->data; + + for (i = 0; i < input_bmi_header->biHeight * input_bmi_header->biWidth; i++) + *data++ &= 0xffffff; + } ``` Shouldn't this check for RGB32 specifically? This would be wrong for ARGB32. Also, does this not affect ARGB32 -> RGB32? But anyway, this is really not ideal for performance, and maybe it means we shouldn't be using colorcnv. 12/14: ``` + if (memcmp(mt, &filter->source.pin.mt, offsetof(AM_MEDIA_TYPE, pbFormat)) || + memcmp(mt->pbFormat, filter->source.pin.mt.pbFormat, mt->cbFormat)) ``` || at the beginning of the line. ``` + if (FAILED(hr = IMediaObject_SetOutputType(filter->dmo, 0, &dmo_mt, 0))) + WARN("Failed to update media type, hr %#lx.\n", hr); ``` I would make this an ERR; this shouldn't happen. 13/14: ``` +static HRESULT color_sink_end_flush(struct strmbase_sink *iface) +{ + struct color_converter *filter = impl_from_strmbase_filter(iface->pin.filter); + if (filter->source.pin.peer) + return IPin_EndFlush(filter->source.pin.peer); + return S_OK; +} ``` strmbase should already do this for you. 14/14: ``` +HRESULT color_sink_can_block(struct strmbase_sink *iface) ``` Missing static. Also, I would name it consistently with the method name, color_sink_receive_can_block(). Thanks for the review Zeb. Given `colorcnv` doesn't support `ARGB32` and the potential performance issues (for example image copies and fixing the alpha channel) I will look in to using FFmpeg directly.
2/14
Eh, what's up with the indentation here?
I'll change that to make it one line. But that was what `clang-format` produced.
6/14
You don't need the media type check; strmbase calls query_accept() before reaching this callback
I implement the QueryAccept in 5/14, but that doesn't fix any of the tests, where as this change fixes several. Does that mean something is not working as intended?
9/14
Generally you want to check formattype before doing this, especially since FORMAT_VideoInfo2 is a thing.
I think because `get_subtype` rejects anything but for `FORMAT_VideoInfo`, it is safe to assume `pin.mt` is `FORMAT_VideoInfo` in this scenario
10/14
Does the converter ignore width/height mismatch?
I think I discovered that with local testing. But I'll look to replace this with FFmpeg anyway.
biSizeImage calculation is rather awkward
`calculate_stride` here does have the advantage of handling misaligned widths.
I'd recommend adding a FIXME for nontrivial rcSource/rcTarget handling
OK, will do.
11/14
this should be zero-copy if at all possible
Agree. I will look in to FFmpeg.
Not header->biSizeImage? Also I don't think that handles negative biHeight
Agreed. That looks wrong. I will fix.
That looks left over, as does the other VFW flag code
Yes, you're right. I'll fix that up.
That's hard to read, doesn't correctly handle VFW_S_NO_STOP_TIME, and looks like you typed & when you meant |.
Again you're right. This will probably go if I use FFmpeg direct, but otherwise I'll fix.
You've overwritten the hr you're trying to use here. You might as well just set this earlier
Correct again. I'll fix.
this is really not ideal for performance, and maybe it means we shouldn't be using colorcnv
Agree - I'll look in to using FFmpeg direct.
12/14
|| at the beginning of the line.
OK, I'll fix
I would make this an ERR; this shouldn't happen.
This will go if I use FFmpeg direct, but otherwise I'll fix.
13/14
strmbase should already do this for you
OK, I'll remove that commit
14/14
Missing static. Also, I would name it consistently with the method name, color_sink_receive_can_block().
Thanks for catching. I will fix both. -- https://gitlab.winehq.org/wine/wine/-/merge_requests/11679#note_149789
participants (3)
-
Brendan McGrath -
Brendan McGrath (@redmcg) -
Elizabeth Figura (@zfigura)