[PATCH 0/3] MR10709: colorcnv/tests: Add initial tests for the color conversion media object.
This MR add tests for the color conversion DMO which is currently only stubbed under Wine. -- https://gitlab.winehq.org/wine/wine/-/merge_requests/10709
From: Brendan McGrath <bmcgrath@codeweavers.com> --- configure.ac | 1 + dlls/colorcnv/tests/Makefile.in | 5 +++++ dlls/colorcnv/tests/colorcnv.c | 23 +++++++++++++++++++++++ 3 files changed, 29 insertions(+) create mode 100644 dlls/colorcnv/tests/Makefile.in create mode 100644 dlls/colorcnv/tests/colorcnv.c diff --git a/configure.ac b/configure.ac index 3e617aaad4f..88d9878d3db 100644 --- a/configure.ac +++ b/configure.ac @@ -2509,6 +2509,7 @@ WINE_CONFIG_MAKEFILE(dlls/cldapi) WINE_CONFIG_MAKEFILE(dlls/clusapi) WINE_CONFIG_MAKEFILE(dlls/cng.sys) WINE_CONFIG_MAKEFILE(dlls/colorcnv) +WINE_CONFIG_MAKEFILE(dlls/colorcnv/tests) WINE_CONFIG_MAKEFILE(dlls/combase) WINE_CONFIG_MAKEFILE(dlls/combase/tests) WINE_CONFIG_MAKEFILE(dlls/comcat) diff --git a/dlls/colorcnv/tests/Makefile.in b/dlls/colorcnv/tests/Makefile.in new file mode 100644 index 00000000000..a4ee918ef95 --- /dev/null +++ b/dlls/colorcnv/tests/Makefile.in @@ -0,0 +1,5 @@ +TESTDLL = colorcnv.dll +IMPORTS = + +SOURCES = \ + colorcnv.c diff --git a/dlls/colorcnv/tests/colorcnv.c b/dlls/colorcnv/tests/colorcnv.c new file mode 100644 index 00000000000..991f047bdef --- /dev/null +++ b/dlls/colorcnv/tests/colorcnv.c @@ -0,0 +1,23 @@ +/* + * Copyright 2026 Brendan McGrath 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 "wine/test.h" + +START_TEST(colorcnv) +{ +} -- GitLab https://gitlab.winehq.org/wine/wine/-/merge_requests/10709
From: Brendan McGrath <bmcgrath@codeweavers.com> --- dlls/colorcnv/tests/Makefile.in | 2 +- dlls/colorcnv/tests/colorcnv.c | 232 ++++++++++++++++++++++++++++++++ 2 files changed, 233 insertions(+), 1 deletion(-) diff --git a/dlls/colorcnv/tests/Makefile.in b/dlls/colorcnv/tests/Makefile.in index a4ee918ef95..2ec3b1231f4 100644 --- a/dlls/colorcnv/tests/Makefile.in +++ b/dlls/colorcnv/tests/Makefile.in @@ -1,5 +1,5 @@ TESTDLL = colorcnv.dll -IMPORTS = +IMPORTS = ole32 msdmo dmoguids wmcodecdspuuid strmiids uuid SOURCES = \ colorcnv.c diff --git a/dlls/colorcnv/tests/colorcnv.c b/dlls/colorcnv/tests/colorcnv.c index 991f047bdef..c42fd6766ff 100644 --- a/dlls/colorcnv/tests/colorcnv.c +++ b/dlls/colorcnv/tests/colorcnv.c @@ -18,6 +18,238 @@ #include "wine/test.h" +#define COBJMACROS +#include <mediaerr.h> +#include <mediaobj.h> +#include <uuids.h> +#include <wmcodecdsp.h> + +#include <amvideo.h> +#include <dmort.h> + +static void test_media_object_media_types(void) +{ + static const GUID *input_types[] = + { + &MEDIASUBTYPE_YV12, + &MEDIASUBTYPE_YUY2, + &MEDIASUBTYPE_UYVY, + &MEDIASUBTYPE_AYUV, + &MEDIASUBTYPE_NV12, + &MEDIASUBTYPE_RGB32, + &MEDIASUBTYPE_RGB565, + &MEDIASUBTYPE_I420, + &MEDIASUBTYPE_IYUV, + &MEDIASUBTYPE_YVYU, + &MEDIASUBTYPE_RGB24, + &MEDIASUBTYPE_RGB555, + &MEDIASUBTYPE_RGB8, + &MEDIASUBTYPE_V216, + &MEDIASUBTYPE_V410, + &MEDIASUBTYPE_NV11, + &MEDIASUBTYPE_Y41P, + &MEDIASUBTYPE_Y41T, + &MEDIASUBTYPE_Y42T, + &MEDIASUBTYPE_YVU9, + }; + + static const struct + { + const GUID *guid; + ULONG cbFormat; + ULONG lSampleSize; + } + output_types[] = + { + {&MEDIASUBTYPE_YV12, sizeof(VIDEOINFOHEADER), 13824}, + {&MEDIASUBTYPE_YUY2, sizeof(VIDEOINFOHEADER), 18432}, + {&MEDIASUBTYPE_UYVY, sizeof(VIDEOINFOHEADER), 18432}, + {&MEDIASUBTYPE_AYUV, sizeof(VIDEOINFOHEADER), 36864}, + {&MEDIASUBTYPE_NV12, sizeof(VIDEOINFOHEADER), 13824}, + {&MEDIASUBTYPE_RGB32, sizeof(VIDEOINFOHEADER), 36864}, + {&MEDIASUBTYPE_RGB565, 100, 18432}, + {&MEDIASUBTYPE_I420, sizeof(VIDEOINFOHEADER), 13824}, + {&MEDIASUBTYPE_IYUV, sizeof(VIDEOINFOHEADER), 13824}, + {&MEDIASUBTYPE_YVYU, sizeof(VIDEOINFOHEADER), 18432}, + {&MEDIASUBTYPE_RGB24, sizeof(VIDEOINFOHEADER), 27648}, + {&MEDIASUBTYPE_RGB555, 100, 18432}, + {&MEDIASUBTYPE_RGB8, 1112, 9216}, + {&MEDIASUBTYPE_V216, sizeof(VIDEOINFOHEADER), 36864}, + {&MEDIASUBTYPE_V410, sizeof(VIDEOINFOHEADER), 36864}, + {&MEDIASUBTYPE_NV11, sizeof(VIDEOINFOHEADER), 13824}, + }; + + DWORD num_in_streams, num_out_streams; + VIDEOINFOHEADER video_info = {0}; + IMediaObject *media_object; + DMO_MEDIA_TYPE mt = {0}; + ULONG refcount; + HRESULT hr; + int i; + + hr = CoInitialize(NULL); + ok(hr == S_OK, "CoInitialize failed, hr %#lx.\n", hr); + + hr = CoCreateInstance(&CLSID_CColorConvertDMO, NULL, CLSCTX_INPROC_SERVER, &IID_IMediaObject, + (void **)&media_object); + ok(hr == S_OK, "Failed to create media object, hr %#lx.\n", hr); + + hr = IMediaObject_GetStreamCount(media_object, &num_in_streams, &num_out_streams); + todo_wine + ok(hr == S_OK, "Got hr %#lx.\n", hr); + todo_wine + ok(num_in_streams == 1, "Got num_in_streams %lu.\n", num_in_streams); + todo_wine + ok(num_out_streams == 1, "Got num_out_streams %lu.\n", num_in_streams); + + hr = IMediaObject_GetInputCurrentType(media_object, 0, &mt); + todo_wine + ok(hr == DMO_E_TYPE_NOT_SET, "Got hr %#lx.\n", hr); + + for (i = 0; i < ARRAY_SIZE(input_types); i++) + { + winetest_push_context("%d", i); + hr = IMediaObject_GetInputType(media_object, 0, i, &mt); + todo_wine + ok(hr == S_OK, "Got hr %#lx.\n", hr); + todo_wine + ok(IsEqualGUID(&mt.majortype, &MEDIATYPE_Video), "Unexpected major type %s.\n", + wine_dbgstr_guid(&mt.majortype)); + todo_wine + ok(IsEqualGUID(&mt.subtype, input_types[i]), "Unexpected subtype %s.\n", wine_dbgstr_guid(&mt.subtype)); + todo_wine + ok(mt.bFixedSizeSamples, "bFixedSizeSamples should be true.\n"); + ok(!mt.bTemporalCompression, "bTemporalCompression should be false.\n"); + ok(mt.lSampleSize == 0, "Unexpected lSampleSize %lu.\n", mt.lSampleSize); + ok(IsEqualGUID(&mt.formattype, &GUID_NULL), "Unexpected format type %s.\n", wine_dbgstr_guid(&mt.formattype)); + ok(mt.pUnk == NULL, "Unexpected pUnk value %p.\n", mt.pUnk); + ok(mt.cbFormat == 0, "Unexpected cbFormat value %lu.\n", mt.cbFormat); + ok(mt.pbFormat == NULL, "Unexpected pbFormat vlaue %p.\n", mt.pbFormat); + winetest_pop_context(); + } + + hr = IMediaObject_GetInputType(media_object, 0, i, &mt); + todo_wine + ok(hr == DMO_E_NO_MORE_ITEMS, "Got hr %#lx.\n", hr); + + for (i = 0; i < ARRAY_SIZE(output_types); i++) + { + winetest_push_context("%d", i); + hr = IMediaObject_GetOutputType(media_object, 0, i, &mt); + todo_wine + ok(hr == S_OK, "Got hr %#lx.\n", hr); + todo_wine + ok(IsEqualGUID(&mt.majortype, &MEDIATYPE_Video), "Unexpected major type %s.\n", + wine_dbgstr_guid(&mt.majortype)); + todo_wine + ok(IsEqualGUID(&mt.subtype, output_types[i].guid), "Unexpected sub type %s.\n", wine_dbgstr_guid(&mt.subtype)); + todo_wine + ok(mt.bFixedSizeSamples, "bFixedSizeSamples should be true.\n"); + ok(!mt.bTemporalCompression, "bTemporalCompression should be false.\n"); + ok(mt.lSampleSize == 0, "Unexpected lSampleSize %lu.\n", mt.lSampleSize); + ok(IsEqualGUID(&mt.formattype, &GUID_NULL), "Unexpected format type %s.\n", wine_dbgstr_guid(&mt.formattype)); + ok(mt.pUnk == NULL, "Unexpected pUnk value %p.\n", mt.pUnk); + ok(mt.cbFormat == 0, "Unexpected cbFormat value %lu.\n", mt.cbFormat); + ok(mt.pbFormat == NULL, "Unexpected pbFormat value %p.\n", mt.pbFormat); + winetest_pop_context(); + } + + hr = IMediaObject_GetOutputType(media_object, 0, i, &mt); + todo_wine + ok(hr == DMO_E_NO_MORE_ITEMS, "Got hr %#lx.\n", hr); + + mt.majortype = MEDIATYPE_Video; + mt.subtype = MEDIASUBTYPE_RGB24; + mt.bFixedSizeSamples = TRUE; + mt.lSampleSize = 96 * 96 * 3; + hr = IMediaObject_SetInputType(media_object, 0, &mt, 0); + todo_wine + ok(hr == DMO_E_TYPE_NOT_ACCEPTED, "Got hr %#lx.\n", hr); + + /* MS documentation for BITMAPINFOHEADER states biSizeImage can be zero + * for uncompressed RGB bitmaps; but this DMO rejects it with E_INVALIDARG + */ + mt.formattype = FORMAT_VideoInfo; + mt.cbFormat = sizeof(video_info); + mt.pbFormat = (BYTE *)&video_info; + video_info.bmiHeader.biSize = sizeof(video_info.bmiHeader); + video_info.bmiHeader.biWidth = 96; + video_info.bmiHeader.biHeight = 96; + video_info.bmiHeader.biPlanes = 1; + video_info.bmiHeader.biBitCount = 24; + video_info.bmiHeader.biCompression = BI_RGB; + hr = IMediaObject_SetInputType(media_object, 0, &mt, 0); + todo_wine + ok(hr == E_INVALIDARG, "Got hr %#lx.\n", hr); + + video_info.bmiHeader.biSizeImage = 96 * 96 * 3; + hr = IMediaObject_SetInputType(media_object, 0, &mt, 0); + todo_wine + ok(hr == S_OK, "Got hr %#lx.\n", hr); + + memset(&mt, 0, sizeof(mt)); + for (i = 0; i < ARRAY_SIZE(output_types); i++) + { + winetest_push_context("%d", i); + hr = IMediaObject_GetOutputType(media_object, 0, i, &mt); + todo_wine + ok(hr == S_OK, "Got hr %#lx.\n", hr); + todo_wine + ok(IsEqualGUID(&mt.majortype, &MEDIATYPE_Video), "Unexpected major type %s.\n", + wine_dbgstr_guid(&mt.majortype)); + todo_wine + ok(IsEqualGUID(&mt.subtype, output_types[i].guid), "Unexpected sub type %s.\n", wine_dbgstr_guid(&mt.subtype)); + todo_wine + ok(mt.bFixedSizeSamples, "bFixedSizeSamples should be true.\n"); + ok(!mt.bTemporalCompression, "bTemporalCompression should be false.\n"); + todo_wine + ok(mt.lSampleSize == output_types[i].lSampleSize, "Unexpected lSampleSize %lu.\n", mt.lSampleSize); + todo_wine + ok(IsEqualGUID(&mt.formattype, &FORMAT_VideoInfo), "Unexpected format type %s.\n", + wine_dbgstr_guid(&mt.formattype)); + ok(mt.pUnk == NULL, "Unexpected pUnk value %p.\n", mt.pUnk); + todo_wine + ok(mt.cbFormat == output_types[i].cbFormat, "Unexpected cbFormat value %lu.\n", mt.cbFormat); + todo_wine + ok(mt.pbFormat != NULL, "pbFormat should not be NULL.\n"); + MoFreeMediaType(&mt); + winetest_pop_context(); + } + + mt.majortype = MEDIATYPE_Video; + mt.subtype = MEDIASUBTYPE_RGB32; + mt.bFixedSizeSamples = TRUE; + mt.lSampleSize = 96 * 96 * 4; + hr = IMediaObject_SetOutputType(media_object, 0, &mt, 0); + todo_wine + ok(hr == DMO_E_TYPE_NOT_ACCEPTED, "Got hr %#lx.\n", hr); + + memset(&video_info, 0, sizeof(video_info)); + mt.formattype = FORMAT_VideoInfo; + mt.cbFormat = sizeof(video_info); + mt.pbFormat = (BYTE *)&video_info; + video_info.bmiHeader.biSize = sizeof(video_info.bmiHeader); + video_info.bmiHeader.biWidth = 96; + video_info.bmiHeader.biHeight = 96; + video_info.bmiHeader.biPlanes = 1; + video_info.bmiHeader.biBitCount = 32; + video_info.bmiHeader.biCompression = BI_RGB; + hr = IMediaObject_SetOutputType(media_object, 0, &mt, 0); + todo_wine + ok(hr == E_INVALIDARG, "Got hr %#lx.\n", hr); + + video_info.bmiHeader.biSizeImage = 96 * 96 * 4; + hr = IMediaObject_SetInputType(media_object, 0, &mt, 0); + todo_wine + ok(hr == S_OK, "Got hr %#lx.\n", hr); + + refcount = IMediaObject_Release(media_object); + ok(refcount == 0, "Unexpected refcount %lu.\n", refcount); + + CoUninitialize(); +} + START_TEST(colorcnv) { + test_media_object_media_types(); } -- GitLab https://gitlab.winehq.org/wine/wine/-/merge_requests/10709
From: Brendan McGrath <bmcgrath@codeweavers.com> --- dlls/colorcnv/tests/Makefile.in | 3 +- dlls/colorcnv/tests/colorcnv.c | 269 ++++++++++++++++++++++++ dlls/colorcnv/tests/resource.rc | 28 +++ dlls/colorcnv/tests/rgb24frame_flip.bmp | Bin 0 -> 27702 bytes dlls/colorcnv/tests/rgb32frame.bmp | Bin 0 -> 36918 bytes 5 files changed, 299 insertions(+), 1 deletion(-) create mode 100644 dlls/colorcnv/tests/resource.rc create mode 100644 dlls/colorcnv/tests/rgb24frame_flip.bmp create mode 100755 dlls/colorcnv/tests/rgb32frame.bmp diff --git a/dlls/colorcnv/tests/Makefile.in b/dlls/colorcnv/tests/Makefile.in index 2ec3b1231f4..ec48b88392f 100644 --- a/dlls/colorcnv/tests/Makefile.in +++ b/dlls/colorcnv/tests/Makefile.in @@ -2,4 +2,5 @@ TESTDLL = colorcnv.dll IMPORTS = ole32 msdmo dmoguids wmcodecdspuuid strmiids uuid SOURCES = \ - colorcnv.c + colorcnv.c \ + resource.rc diff --git a/dlls/colorcnv/tests/colorcnv.c b/dlls/colorcnv/tests/colorcnv.c index c42fd6766ff..6030a4100fc 100644 --- a/dlls/colorcnv/tests/colorcnv.c +++ b/dlls/colorcnv/tests/colorcnv.c @@ -27,6 +27,189 @@ #include <amvideo.h> #include <dmort.h> +#pragma pack(push, 2) +struct bmp_header +{ + char magic[2]; + DWORD length; + DWORD reserved; + DWORD offset; +}; +#pragma pack(pop) + +struct media_buffer +{ + IMediaBuffer IMediaBuffer_iface; + LONG refcount; + + BYTE *ptr; + DWORD length; + DWORD max_length; +}; + +static struct media_buffer *media_buffer_from_IMediaBuffer(IMediaBuffer *iface) +{ + return CONTAINING_RECORD(iface, struct media_buffer, IMediaBuffer_iface); +} + +static WINAPI HRESULT media_buffer_QueryInterface(IMediaBuffer *iface, REFIID riid, void **obj) +{ + if (IsEqualGUID(riid, &IID_IMediaBuffer) || + IsEqualGUID(riid, &IID_IUnknown)) + { + *obj = iface; + } + else + { + *obj = NULL; + return E_NOINTERFACE; + } + + IUnknown_AddRef((IUnknown *)*obj); + return S_OK; +} + +static WINAPI ULONG media_buffer_AddRef(IMediaBuffer *iface) +{ + struct media_buffer *media_buffer = media_buffer_from_IMediaBuffer(iface); + ULONG refcount; + + refcount = InterlockedIncrement(&media_buffer->refcount); + + return refcount; +} + +static WINAPI ULONG media_buffer_Release(IMediaBuffer *iface) +{ + struct media_buffer *media_buffer = media_buffer_from_IMediaBuffer(iface); + ULONG refcount; + + refcount = InterlockedDecrement(&media_buffer->refcount); + + if (!refcount) + free(media_buffer); + + return refcount; +} + +static WINAPI HRESULT media_buffer_SetLength(IMediaBuffer *iface, DWORD length) +{ + struct media_buffer *media_buffer = media_buffer_from_IMediaBuffer(iface); + + media_buffer->length = length; + + return S_OK; +} + +static WINAPI HRESULT media_buffer_GetMaxLength(IMediaBuffer *iface, DWORD *max_length) +{ + struct media_buffer *media_buffer = media_buffer_from_IMediaBuffer(iface); + + *max_length = media_buffer->max_length; + + return S_OK; +} + +static WINAPI HRESULT media_buffer_GetBufferAndLength(IMediaBuffer *iface, BYTE **ptr, DWORD *length) +{ + struct media_buffer *media_buffer = media_buffer_from_IMediaBuffer(iface); + + if (ptr) + *ptr = media_buffer->ptr; + if (length) + *length = media_buffer->length; + + return S_OK; +} + +static IMediaBufferVtbl media_buffer_vtbl = +{ + media_buffer_QueryInterface, + media_buffer_AddRef, + media_buffer_Release, + media_buffer_SetLength, + media_buffer_GetMaxLength, + media_buffer_GetBufferAndLength, +}; + +static struct media_buffer *create_media_buffer(BYTE *ptr, DWORD length, DWORD max_length) +{ + struct media_buffer *media_buffer; + + media_buffer = calloc(1, sizeof(*media_buffer)); + media_buffer->IMediaBuffer_iface.lpVtbl = &media_buffer_vtbl; + media_buffer->refcount = 1; + + media_buffer->ptr = ptr; + media_buffer->length = length; + media_buffer->max_length = max_length; + + return media_buffer; +} + +static struct media_buffer *resource_as_media_buffer(const WCHAR *resource_name) +{ + struct bmp_header *header; + HRSRC resource; + BYTE *data; + DWORD size; + + resource = FindResourceW(NULL, L"rgb24frame_flip.bmp", (const WCHAR *)RT_RCDATA); + header = (struct bmp_header *)LockResource(LoadResource(GetModuleHandleW(NULL), resource)); + data = (BYTE *)header + header->offset; + size = header->length - header->offset; + + return create_media_buffer(data, size, size); +} + +static void write_bitmap_to_file(const BITMAPINFOHEADER *bitmap_header, const BYTE *data, HANDLE output) +{ + struct bmp_header header = + { + .magic = "BM", + .length = sizeof(header) + sizeof(*bitmap_header) + bitmap_header->biSizeImage, + .offset = sizeof(header) + sizeof(*bitmap_header), + }; + DWORD written; + BOOL ret; + + ret = WriteFile(output, &header, sizeof(header), &written, NULL); + ok(ret, "WriteFile failed, error %lu\n", GetLastError()); + ok(written == sizeof(header), "written %lu bytes\n", written); + ret = WriteFile(output, bitmap_header, sizeof(*bitmap_header), &written, NULL); + ok(ret, "WriteFile failed, error %lu\n", GetLastError()); + ok(written == sizeof(*bitmap_header), "written %lu bytes\n", written); + ret = WriteFile(output, data, bitmap_header->biSizeImage, &written, NULL); + ok(ret, "WriteFile failed, error %lu\n", GetLastError()); + ok(written == bitmap_header->biSizeImage, "written %lu bytes\n", written); +} + +static HANDLE open_temp_file(const WCHAR *output_filename) +{ + WCHAR path[MAX_PATH]; + HANDLE output; + + GetTempPathW(ARRAY_SIZE(path), path); + lstrcatW(path, output_filename); + + output = CreateFileW(path, GENERIC_WRITE, 0, NULL, CREATE_ALWAYS, 0, 0); + ok(output != INVALID_HANDLE_VALUE, "CreateFileW failed, error %lu\n", GetLastError()); + + trace("created %s\n", debugstr_w(path)); + + return output; +} + +static void write_bitmap(const BITMAPINFOHEADER *bitmap_header, const BYTE *data, const WCHAR *output_filename) +{ + HANDLE output; + + output = open_temp_file(output_filename); + write_bitmap_to_file(bitmap_header, data, output); + + CloseHandle(output); +} + static void test_media_object_media_types(void) { static const GUID *input_types[] = @@ -239,10 +422,95 @@ static void test_media_object_media_types(void) ok(hr == E_INVALIDARG, "Got hr %#lx.\n", hr); video_info.bmiHeader.biSizeImage = 96 * 96 * 4; + hr = IMediaObject_SetOutputType(media_object, 0, &mt, 0); + todo_wine + ok(hr == S_OK, "Got hr %#lx.\n", hr); + + refcount = IMediaObject_Release(media_object); + ok(refcount == 0, "Unexpected refcount %lu.\n", refcount); + + CoUninitialize(); +} + +void test_media_object_color_conversion(void) +{ + struct media_buffer *input_buffer, *output_buffer; + const struct bmp_header *expect_rgb32_header; + DMO_OUTPUT_DATA_BUFFER output_data; + VIDEOINFOHEADER video_info = {0}; + const BYTE *expect_rgb32_data; + BYTE rgb32_data[96 * 96 * 4]; + IMediaObject *media_object; + DMO_MEDIA_TYPE mt = {0}; + DWORD status, diff; + HRSRC resource; + ULONG refcount; + HRESULT hr; + + hr = CoInitialize(NULL); + ok(hr == S_OK, "CoInitialize failed, hr %#lx.\n", hr); + + hr = CoCreateInstance(&CLSID_CColorConvertDMO, NULL, CLSCTX_INPROC_SERVER, &IID_IMediaObject, + (void **)&media_object); + ok(hr == S_OK, "Failed to create media object, hr %#lx.\n", hr); + + mt.majortype = MEDIATYPE_Video; + mt.subtype = MEDIASUBTYPE_RGB24; + mt.bFixedSizeSamples = TRUE; + mt.lSampleSize = 96 * 96 * 3; + mt.formattype = FORMAT_VideoInfo; + mt.cbFormat = sizeof(video_info); + mt.pbFormat = (BYTE *)&video_info; + video_info.bmiHeader.biSize = sizeof(video_info.bmiHeader); + video_info.bmiHeader.biWidth = 96; + video_info.bmiHeader.biHeight = 96; + video_info.bmiHeader.biPlanes = 1; + video_info.bmiHeader.biBitCount = 24; + video_info.bmiHeader.biCompression = BI_RGB; + video_info.bmiHeader.biSizeImage = 96 * 96 * 3; hr = IMediaObject_SetInputType(media_object, 0, &mt, 0); todo_wine ok(hr == S_OK, "Got hr %#lx.\n", hr); + mt.subtype = MEDIASUBTYPE_RGB32; + mt.lSampleSize = 96 * 96 * 4; + video_info.bmiHeader.biHeight = -96; + video_info.bmiHeader.biBitCount = 32; + video_info.bmiHeader.biSizeImage = 96 * 96 * 4; + hr = IMediaObject_SetOutputType(media_object, 0, &mt, 0); + todo_wine + ok(hr == S_OK, "Got hr %#lx.\n", hr); + + input_buffer = resource_as_media_buffer(L"rgb24frame_flip.bmp"); + hr = IMediaObject_ProcessInput(media_object, 0, &input_buffer->IMediaBuffer_iface, 0, 0, 0); + todo_wine + ok(hr == S_OK, "Got hr %#lx.\n", hr); + + output_buffer = create_media_buffer(rgb32_data, 0, sizeof(rgb32_data)); + output_data.pBuffer = &output_buffer->IMediaBuffer_iface; + hr = IMediaObject_ProcessOutput(media_object, 0, 1, &output_data, &status); + todo_wine + ok(hr == S_OK, "Got hr %#lx.\n", hr); + + write_bitmap(&video_info.bmiHeader, rgb32_data, L"rgb32frame.bmp"); + + resource = FindResourceW(NULL, L"rgb32frame.bmp", (const WCHAR *)RT_RCDATA); + expect_rgb32_header = ((const struct bmp_header *)LockResource(LoadResource(GetModuleHandleW(NULL), resource))); + expect_rgb32_data = (const BYTE *)expect_rgb32_header + expect_rgb32_header->offset; + + diff = 0; + for (unsigned int i = 0; i < mt.lSampleSize; ++i) + diff += abs((int)output_buffer->ptr[i] - (int)expect_rgb32_data[i]); + diff = diff * 100 / 256 / mt.lSampleSize; + todo_wine + ok(diff == 0, "Got %lu%% difference.\n", diff); + + refcount = IMediaBuffer_Release(&input_buffer->IMediaBuffer_iface); + ok(refcount == 0, "Unexpected refcount %lu.\n", refcount); + + refcount = IMediaBuffer_Release(&output_buffer->IMediaBuffer_iface); + ok(refcount == 0, "Unexpected refcount %lu.\n", refcount); + refcount = IMediaObject_Release(media_object); ok(refcount == 0, "Unexpected refcount %lu.\n", refcount); @@ -252,4 +520,5 @@ static void test_media_object_media_types(void) START_TEST(colorcnv) { test_media_object_media_types(); + test_media_object_color_conversion(); } diff --git a/dlls/colorcnv/tests/resource.rc b/dlls/colorcnv/tests/resource.rc new file mode 100644 index 00000000000..123ddb5cdbf --- /dev/null +++ b/dlls/colorcnv/tests/resource.rc @@ -0,0 +1,28 @@ +/* + * Copyright 2026 Brendan McGrath 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 + */ + +/* Generated with: */ +/* ffmpeg -f lavfi -i pal100bars=size=96x96 \ + * -vf transpose -pix_fmt bgr24 -frames:v 1 \ + * rgb24frame_flip.bmp */ +/* @makedep: rgb24frame_flip.bmp */ +rgb24frame_flip.bmp RCDATA rgb24frame_flip.bmp + +/* Generated by running the media object color conversion test on Windows */ +/* @makedep: rgb32frame.bmp */ +rgb32frame.bmp RCDATA rgb32frame.bmp diff --git a/dlls/colorcnv/tests/rgb24frame_flip.bmp b/dlls/colorcnv/tests/rgb24frame_flip.bmp new file mode 100644 index 0000000000000000000000000000000000000000..7795fcf3989b8bf6594935c15eedf22829f14229 GIT binary patch literal 27702 zcmZ?rHOpZD12YB&1`P%Vh6E^PWRL)hGeG4boKbQ#1V%$(Gz3ONU^E0qLtr!nMnhmU z1V%$(Gz3ONfSw`nkAYznjfTKz2#kinXb6mkz-S1JhQMeDjE2By2#kinXb8|D1Q;0p zj-t^J7!85Z5Eu=C(GVC7fzc2c4S~@R7!85Z5Eu;sI)uPqhJT}IGz3ONU^E0qLtr!n zMnhmU1V%$(Gz3ONU^E0qLx2t;FuMA1Gz3ONU^E0qLtr!nMnhmU1V%$(Gz3ONU^E0q zLx4UZ@c%!<C>jlc(GVC7fzc2c4S~@R7!85Z5Eu=C(GVC7fzc44LkKYZ{X2?ALtr!n zMnhmU1V%$(Gz3ONU^E0qLtr!nMnhmU1n3X~|3|@S2#kinXb6mkz-S1JhQMeDjE2By Q2#kinXb6mk09`@=09!zwZ~y=R literal 0 HcmV?d00001 diff --git a/dlls/colorcnv/tests/rgb32frame.bmp b/dlls/colorcnv/tests/rgb32frame.bmp new file mode 100755 index 0000000000000000000000000000000000000000..7a4298bf8e1804b7e84072280290ddd0684cfa25 GIT binary patch literal 36918 zcmZ?rHJiWy24)Nl3>pj!3<(Sj3=97M|If&v02XI}%0oC1^8Y9p4S~@R7!85Z5Eu=C z(GVC7fzc2c4S~@R7!85Z5Eu=C(GVC7fzc2c)*-;~_wWBvJQ@b0X<#%BjHZFnG%%V5 zM$^D(8W>FjqiJ9?4UDFN(KIlc21e7sXc`#4Y2g2VhX12@Gz>=5z-Ss6O#`E8U^ESk zrh(BkFq#HN)4*sN7)=ACX<#%BjHZFnG%$SA0K-3q|D$*`3`WzyXc`zz1EXnRG!2ZV zfzdQDng&MGz-Ss6O#`E8U^ESkrh(BkFnrU%Uxt7GNAYMFjHZFnG%%V5M$^D(8W>Fj zqiJ9?4UDFN(KIlc21e7sXc`zz1EXnR_@)5{hQI$u@n{%~rh(BkFq#HN)4*sN7)=AC zX<#%BjHZFnG%%V5M$^D(8W>FjqiJCHrh$J94F5;*Xc&y9fzdQDng&MGz-Ss6O#`E8 zU^ESkrh(BkFq#HN)4*sN7)=ACX<+!K0R{$!|D$*`3`WzyXc`zz1EXnRG!2ZVfzdQD cng&MGz-Ss6O#`E8U^ESkrh(BkFnrSh02ft&82|tP literal 0 HcmV?d00001 -- GitLab https://gitlab.winehq.org/wine/wine/-/merge_requests/10709
I wasn't 100% sure the right place to put these tests, but given Windows lists `Colorcnv.dll` as the DLL under their documentation for `CLSID_CColorConvertDMO`, I decided this is probably the right place. -- https://gitlab.winehq.org/wine/wine/-/merge_requests/10709#note_137185
I'm not sure how extensively you intend to test it but imo it might be better to use mf/tests/transform.c for that, it already has plenty of utility helpers to check transforms and dmo behavior. Also, is there any specific reason to implement the dmo side? I've used it to more gradually introduce some change in the backend in https://gitlab.winehq.org/wine/wine/-/merge_requests/10687. Let me know if it conflicts with your plans. -- https://gitlab.winehq.org/wine/wine/-/merge_requests/10709#note_137188
I'm not sure how extensively you intend to test it but imo it might be better to use mf/tests/transform.c for that, it already has plenty of utility helpers to check transforms and dmo behavior.
I did see some other DMO tests under `mf/tests`, but it didn't feel right to me to put DMO tests there given DMO and MF are different technologies. But I guess they do have a lot of overlap, so the tests (and implementation) for one can be leveraged for the other. I'm happy to move it there if you think that's the right place.
Also, is there any specific reason to implement the dmo side?
I'm implementing the Color Space Converter direct show filter: https://learn.microsoft.com/en-us/windows/win32/directshow/color-space-conve... The implementation I currently have on my local copies the `IMediaSample` data to a `IMFMediaBuffer` and uses the `IMFTransform` interface of the MF color converter, but I thought it might be better to use `IMediaBuffer` to wrap the `IMediaSample` and hence use the `IMediaObject` interface. So not really a big deal if I do use the `IMFTransform` interface.
Let me know if it conflicts with your plans.
Not at all. I've got options: 1. Implement and use the `IMediaObject` interface in winegstreamer; 2. Stick with the existing `IMFTransform` interface; or 3. Wait for your MR to be merged and use the `IMediaObject` interface there I think for now I might go with option 2. The only benefit to using the `IMediaObject` interface was less data copies; but this is an older API, so I don't think I need to worry about 4k/60fps video. -- https://gitlab.winehq.org/wine/wine/-/merge_requests/10709#note_137191
I did see some other DMO tests under `mf/tests`, but it didn't feel right to me to put DMO tests there given DMO and MF are different technologies. But I guess they do have a lot of overlap, so the tests (and implementation) for one can be leveraged for the other. I'm happy to move it there if you think that's the right place.
We have already several DMO tests for the decoders there, it may seem that they are different APIs but for DMO/transforms it's actually much more intertwined, and some objects which implement both have a behavior that pretty much depends on that dual nature (and mixing calls between both interfaces is even possible). Also, for the purpose of running tests (locally for instance), it's much much simpler to have a single executable to run to cover as much surface as possible, rather than having to list every possible module out there. MF tests already span across 4-5 different modules, I think it's better to avoid adding tests in every separate component module or they will likely be overlooked.
The implementation I currently have on my local copies the `IMediaSample` data to a `IMFMediaBuffer` and uses the `IMFTransform` interface of the MF color converter, but I thought it might be better to use `IMediaBuffer` to wrap the `IMediaSample` and hence use the `IMediaObject` interface. So not really a big deal if I do use the `IMFTransform` interface.
There are MF functions for interop but usually it's the other way around (and that's because dual MF transforms are basically implemented by wrapping the DMO calls). I'm not sure how easier or more difficult it is to wrap `IMediaSample` / `IMediaBuffer`, but I feel that because they are from the same era it might be easier to go this way rather than through MF?
I think for now I might go with option 2. The only benefit to using the `IMediaObject` interface was less data copies; but this is an older API, so I don't think I need to worry about 4k/60fps video.
It depends on how much data copy we mean. We should be able to avoid copying the actual data in all cases, or we will likely have some issues anyway. These games usually don't have so large videos but they may still suffer from playback issues if video conversion is too slow. And regardless, we should simply avoid copying frame data around unnecessarily. Fwiw we have / had (that should improve with new wow64 mode) plenty of playback problems with games using wmvcore, because color conversion performance in Gstreamer 32bit ORC was terrible. -- https://gitlab.winehq.org/wine/wine/-/merge_requests/10709#note_137194
participants (3)
-
Brendan McGrath -
Brendan McGrath (@redmcg) -
Rémi Bernon (@rbernon)