The test has a little inspiration from the SDK samples.
-- v7: mfmediaengine: Add support for inserting video effects. mfmediaengine/tests: Add tests for video effects.
From: Bernhard Kölbl besentv@gmail.com
Signed-off-by: Bernhard Kölbl besentv@gmail.com --- dlls/mfmediaengine/tests/mfmediaengine.c | 779 ++++++++++++++++++ dlls/mfmediaengine/tests/resource.rc | 4 + .../tests/rgb32frame_static_colors.bmp | Bin 0 -> 16438 bytes 3 files changed, 783 insertions(+) create mode 100644 dlls/mfmediaengine/tests/rgb32frame_static_colors.bmp
diff --git a/dlls/mfmediaengine/tests/mfmediaengine.c b/dlls/mfmediaengine/tests/mfmediaengine.c index 655b19af050..c61267133b6 100644 --- a/dlls/mfmediaengine/tests/mfmediaengine.c +++ b/dlls/mfmediaengine/tests/mfmediaengine.c @@ -1326,6 +1326,784 @@ done: CloseHandle(notify.ready_event); }
+#define EFFECT_PAINT_TOP 0x00000001 +#define EFFECT_PAINT_RIGHT 0x00000002 +#define EFFECT_PAINT_BOTTOM 0x00000003 +#define EFFECT_PAINT_LEFT 0x00000004 + +struct static_color_effect +{ + IMFTransform IMFTransform_iface; + LONG refcount; + + IMFMediaType *media_type_in, *media_type_out; + DWORD frame_size, frame_width, frame_height; + LONG processing_delta, paint_area; + IMFSample *sample_buf; + BYTE u, v; + + CRITICAL_SECTION cs; +}; + +const GUID *media_subtypes[] = +{ + &MFVideoFormat_I420, + /* Add more formats if needed. */ +}; + +static struct static_color_effect *impl_from_IMFTransform(IMFTransform *iface) +{ + return CONTAINING_RECORD(iface, struct static_color_effect, IMFTransform_iface); +} + +static HRESULT WINAPI static_color_effect_QueryInterface(IMFTransform *iface, REFIID iid, void **out) +{ + struct static_color_effect *impl = impl_from_IMFTransform(iface); + + if (IsEqualGUID(iid, &IID_IUnknown) || + IsEqualGUID(iid, &IID_IMFTransform)) + { + *out = &impl->IMFTransform_iface; + IUnknown_AddRef((IUnknown *)*out); + return S_OK; + } + + *out = NULL; + return E_NOINTERFACE; +} + +static ULONG WINAPI static_color_effect_AddRef(IMFTransform *iface) +{ + struct static_color_effect *impl = impl_from_IMFTransform(iface); + ULONG refcount = InterlockedIncrement(&impl->refcount); + return refcount; +} + +static ULONG WINAPI static_color_effect_Release(IMFTransform *iface) +{ + struct static_color_effect *impl = impl_from_IMFTransform(iface); + ULONG refcount = InterlockedDecrement(&impl->refcount); + + if (!refcount) + { + if (impl->media_type_out) IMFMediaType_Release(impl->media_type_out); + if (impl->media_type_in) IMFMediaType_Release(impl->media_type_in); + DeleteCriticalSection(&impl->cs); + free(impl); + } + + return refcount; +} + +static HRESULT WINAPI static_color_effect_GetStreamLimits(IMFTransform *iface, DWORD *input_minimum, + DWORD *input_maximum, DWORD *output_minimum, DWORD *output_maximum) +{ + *input_minimum = *input_maximum = *output_minimum = *output_maximum = 1; + return S_OK; +} + +static HRESULT WINAPI static_color_effect_GetStreamCount(IMFTransform *iface, DWORD *inputs, DWORD *outputs) +{ + *inputs = *outputs = 1; + return S_OK; +} + +static HRESULT WINAPI static_color_effect_GetStreamIDs(IMFTransform *iface, DWORD input_size, DWORD *inputs, + DWORD output_size, DWORD *outputs) +{ + return E_NOTIMPL; +} + +static HRESULT WINAPI static_color_effect_GetInputStreamInfo(IMFTransform *iface, DWORD id, MFT_INPUT_STREAM_INFO *info) +{ + return E_NOTIMPL; +} + +static HRESULT WINAPI static_color_effect_GetOutputStreamInfo(IMFTransform *iface, DWORD id, MFT_OUTPUT_STREAM_INFO *info) +{ + struct static_color_effect *impl = impl_from_IMFTransform(iface); + + if (id) return MF_E_INVALIDSTREAMNUMBER; + + info->dwFlags = + MFT_OUTPUT_STREAM_WHOLE_SAMPLES | + MFT_OUTPUT_STREAM_FIXED_SAMPLE_SIZE | + MFT_OUTPUT_STREAM_SINGLE_SAMPLE_PER_BUFFER; + + info->cbAlignment = 0; + info->cbSize = impl->frame_size; + + return S_OK; +} + +static HRESULT WINAPI static_color_effect_GetAttributes(IMFTransform *iface, IMFAttributes **attributes) +{ + return E_NOTIMPL; +} + +static HRESULT WINAPI static_color_effect_GetInputStreamAttributes(IMFTransform *iface, DWORD id, IMFAttributes **attributes) +{ + return E_NOTIMPL; +} + +static HRESULT WINAPI static_color_effect_GetOutputStreamAttributes(IMFTransform *iface, DWORD id, IMFAttributes **attributes) +{ + return E_NOTIMPL; +} + +static HRESULT WINAPI static_color_effect_DeleteInputStream(IMFTransform *iface, DWORD id) +{ + return E_NOTIMPL; +} + +static HRESULT WINAPI static_color_effect_AddInputStreams(IMFTransform *iface, DWORD streams, DWORD *ids) +{ + return E_NOTIMPL; +} + +static HRESULT WINAPI static_color_effect_GetInputAvailableType(IMFTransform *iface, DWORD id, DWORD index, + IMFMediaType **type) +{ + struct static_color_effect *impl = impl_from_IMFTransform(iface); + IMFMediaType *mt; + + if (id) return MF_E_INVALIDSTREAMNUMBER; + + EnterCriticalSection(&impl->cs); + + if (impl->media_type_in) + { + if (index > 0) + goto no_more_types; + + *type = impl->media_type_in; + IMFMediaType_AddRef(*type); + } + else + { + if (index >= ARRAY_SIZE(media_subtypes)) + goto no_more_types; + + MFCreateMediaType(&mt); + IMFMediaType_SetGUID(mt, &MF_MT_MAJOR_TYPE, &MFMediaType_Video); + IMFMediaType_SetGUID(mt, &MF_MT_SUBTYPE, media_subtypes[index]); + *type = mt; + } + LeaveCriticalSection(&impl->cs); + + return S_OK; + +no_more_types: + LeaveCriticalSection(&impl->cs); + + return MF_E_NO_MORE_TYPES; +} + +static HRESULT WINAPI static_color_effect_GetOutputAvailableType(IMFTransform *iface, DWORD id, DWORD index, + IMFMediaType **type) +{ + struct static_color_effect *impl = impl_from_IMFTransform(iface); + IMFMediaType *mt; + + if (id) return MF_E_INVALIDSTREAMNUMBER; + + EnterCriticalSection(&impl->cs); + + if (impl->media_type_out) + { + if (index > 0) + goto no_more_types; + + *type = impl->media_type_out; + IMFMediaType_AddRef(*type); + } + else + { + if (index >= ARRAY_SIZE(media_subtypes)) + goto no_more_types; + + MFCreateMediaType(&mt); + IMFMediaType_SetGUID(mt, &MF_MT_MAJOR_TYPE, &MFMediaType_Video); + IMFMediaType_SetGUID(mt, &MF_MT_SUBTYPE, media_subtypes[index]); + *type = mt; + } + + LeaveCriticalSection(&impl->cs); + + return S_OK; + +no_more_types: + LeaveCriticalSection(&impl->cs); + + return MF_E_NO_MORE_TYPES; +} + +static HRESULT WINAPI static_color_effect_SetInputType(IMFTransform *iface, DWORD id, IMFMediaType *type, DWORD flags) +{ + struct static_color_effect *impl = impl_from_IMFTransform(iface); + UINT32 height, width, i; + PROPVARIANT propvar; + HRESULT hr; + GUID value; + + if (id) return MF_E_INVALIDSTREAMNUMBER; + + EnterCriticalSection(&impl->cs); + + if (FAILED(hr = IMFMediaType_GetGUID(type, &MF_MT_SUBTYPE, &value))) + goto done; + + for (hr = MF_E_INVALIDMEDIATYPE, i = 0; i < ARRAY_SIZE(media_subtypes); ++i) + { + if (IsEqualGUID(&value, media_subtypes[i])) + { + hr = S_OK; + break; + } + } + + if (FAILED(hr)) + goto done; + + if (FAILED(hr = IMFMediaType_GetItem(type, &MF_MT_FRAME_SIZE, &propvar))) + goto done; + + impl->frame_width = width = propvar.uhVal.HighPart; + impl->frame_height = height = propvar.uhVal.LowPart; + + if (impl->media_type_in) IMFMediaType_Release(impl->media_type_in); + impl->media_type_in = type; + IMFMediaType_AddRef(impl->media_type_in); + + /* Idk why, but Windows apparently expects this function to also set an output type... */ + if (impl->media_type_out) IMFMediaType_Release(impl->media_type_out); + impl->media_type_out = type; + IMFMediaType_AddRef(impl->media_type_out); + + if (IsEqualGUID(&value, &MFVideoFormat_I420)) + { + impl->frame_size = width * (height + (height / 2)); /* 12bpp */ + } + else + { + impl->frame_size = 0; + } + +done: + LeaveCriticalSection(&impl->cs); + + return hr; +} + +static HRESULT WINAPI static_color_effect_SetOutputType(IMFTransform *iface, DWORD id, IMFMediaType *type, DWORD flags) +{ + struct static_color_effect *impl = impl_from_IMFTransform(iface); + + if (id) return MF_E_INVALIDSTREAMNUMBER; + + EnterCriticalSection(&impl->cs); + + if (impl->media_type_out) + IMFMediaType_Release(impl->media_type_out); + + impl->media_type_out = type; + IMFMediaType_AddRef(impl->media_type_out); + + LeaveCriticalSection(&impl->cs); + + return S_OK; +} + +static HRESULT WINAPI static_color_effect_GetInputCurrentType(IMFTransform *iface, DWORD id, IMFMediaType **type) +{ + struct static_color_effect *impl = impl_from_IMFTransform(iface); + HRESULT hr; + + if (id) return MF_E_INVALIDSTREAMNUMBER; + + EnterCriticalSection(&impl->cs); + + if (!impl->media_type_in) + { + hr = MF_E_TRANSFORM_TYPE_NOT_SET; + } + else + { + *type = impl->media_type_in; + IMFMediaType_AddRef(*type); + hr = S_OK; + } + + LeaveCriticalSection(&impl->cs); + + return hr; +} + +static HRESULT WINAPI static_color_effect_GetOutputCurrentType(IMFTransform *iface, DWORD id, IMFMediaType **type) +{ + struct static_color_effect *impl = impl_from_IMFTransform(iface); + HRESULT hr; + + if (id) return MF_E_INVALIDSTREAMNUMBER; + + EnterCriticalSection(&impl->cs); + + if (!impl->media_type_out) + { + hr = MF_E_TRANSFORM_TYPE_NOT_SET; + } + else + { + *type = impl->media_type_out; + IMFMediaType_AddRef(*type); + hr = S_OK; + } + + LeaveCriticalSection(&impl->cs); + + return hr; +} + +static HRESULT WINAPI static_color_effect_GetInputStatus(IMFTransform *iface, DWORD id, DWORD *flags) +{ + return E_NOTIMPL; +} + +static HRESULT WINAPI static_color_effect_GetOutputStatus(IMFTransform *iface, DWORD *flags) +{ + return E_NOTIMPL; +} + +static HRESULT WINAPI static_color_effect_SetOutputBounds(IMFTransform *iface, LONGLONG lower, LONGLONG upper) +{ + return E_NOTIMPL; +} + +static HRESULT WINAPI static_color_effect_ProcessEvent(IMFTransform *iface, DWORD id, IMFMediaEvent *event) +{ + return E_NOTIMPL; +} + +static HRESULT WINAPI static_color_effect_ProcessMessage(IMFTransform *iface, MFT_MESSAGE_TYPE message, ULONG_PTR param) +{ + struct static_color_effect *impl = impl_from_IMFTransform(iface); + HRESULT hr = S_OK; + + EnterCriticalSection(&impl->cs); + + switch (message) + { + case MFT_MESSAGE_COMMAND_FLUSH: + hr = E_NOTIMPL; + break; + + default: + break; + } + + LeaveCriticalSection(&impl->cs); + + return hr; +} + +static HRESULT WINAPI static_color_effect_ProcessInput(IMFTransform *iface, DWORD id, IMFSample *sample, DWORD flags) +{ + struct static_color_effect *impl = impl_from_IMFTransform(iface); + DWORD buf_count = 0; + HRESULT hr = S_OK; + + if (id) return MF_E_INVALIDSTREAMNUMBER; + + EnterCriticalSection(&impl->cs); + if (impl->sample_buf) + { + hr = MF_E_NOTACCEPTING; + goto done; + } + + if (FAILED(hr = IMFSample_GetBufferCount(sample, &buf_count))) + goto done; + + if (!buf_count) + { + hr = E_FAIL; + goto done; + } + else if (buf_count > 1) + { + hr = MF_E_SAMPLE_HAS_TOO_MANY_BUFFERS; + } + + IMFSample_AddRef((impl->sample_buf = sample)); + impl->processing_delta++; + +done: + LeaveCriticalSection(&impl->cs); + + return hr; +} + +static HRESULT static_color_effect_convert_frame(struct static_color_effect *impl, GUID *subtype, IMFMediaBuffer *in, IMFMediaBuffer *out) +{ + DWORD len = 0, max = 0, i, height, width, x_start, x_end, y_start, y_end; + IMF2DBuffer *src_2d = NULL, *dst_2d = NULL; + BYTE *src, *dst; + HRESULT hr; + + if (FAILED(hr = IMFMediaBuffer_Lock(in, &src, NULL, NULL))) + goto done; + + if (FAILED(hr = IMFMediaBuffer_Lock(out, &dst, &max, &len))) + { + IMFMediaBuffer_Unlock(in); + goto done; + } + + y_start = 0; + x_start = 0; + height = y_end = impl->frame_height; + width = x_end = impl->frame_width; + + /* Note: The area begins in the bottom left corner. */ + if (impl->paint_area == EFFECT_PAINT_BOTTOM) + { + y_end = height / 2; + } + else if (impl->paint_area == EFFECT_PAINT_LEFT) + { + x_end = width / 2; + } + else if (impl->paint_area == EFFECT_PAINT_RIGHT) + { + x_start = width / 2; + } + else if (impl->paint_area == EFFECT_PAINT_TOP) + { + y_start = height / 2; + } + + if (IsEqualGUID(subtype, &MFVideoFormat_I420)) + { + /* Y plane */ + for (i = 0; i < height; ++i) + { + memcpy(dst, src, width); + + if (i >= y_start && i < y_end) + memset(dst + x_start, 0x80, x_end - x_start); + + dst += width; + src += width; + } + + /* Half all values because U and V plane are half the size. */ + x_start = x_start / 2; + y_start = y_start / 2; + x_end = x_end / 2; + y_end = y_end / 2; + height = height / 2; + width = width / 2; + + /* U plane */ + for (i = 0; i < height; ++i) + { + memcpy(dst, src, width); + + if (i >= y_start && i < y_end) + memset(dst + x_start, impl->u, x_end - x_start); + + dst += width; + src += width; + } + + /* V plane */ + for (i = 0; i < height; ++i) + { + memcpy(dst, src, width); + + if (i >= y_start && i < y_end) + memset(dst + x_start, impl->v, x_end - x_start); + + dst += width; + src += width; + } + } + else hr = E_FAIL; + + if (SUCCEEDED(hr)) + hr = IMFMediaBuffer_SetCurrentLength(out, max); + + IMFMediaBuffer_Unlock(out); + IMFMediaBuffer_Unlock(in); + +done: + if (dst_2d) IMF2DBuffer_Release(dst_2d); + if (src_2d) IMF2DBuffer_Release(src_2d); + return hr; +} + +static HRESULT WINAPI static_color_effect_ProcessOutput(IMFTransform *iface, DWORD flags, DWORD count, + MFT_OUTPUT_DATA_BUFFER *samples, DWORD *status) +{ + struct static_color_effect *impl = impl_from_IMFTransform(iface); + IMFMediaBuffer *in_buf = NULL, *out_buf = NULL; + LONGLONG duration = 0, time = 0; + HRESULT hr = S_OK; + GUID subtype; + + if (count != 1) return E_INVALIDARG; + + EnterCriticalSection(&impl->cs); + + if (!impl->sample_buf) + { + hr = MF_E_TRANSFORM_NEED_MORE_INPUT; + goto done; + } + + if (FAILED(hr = IMFMediaType_GetGUID(impl->media_type_in, &MF_MT_SUBTYPE, &subtype))) + goto done; + + if (SUCCEEDED(hr = IMFSample_GetSampleDuration(impl->sample_buf, &duration))) + { + if (FAILED(hr = IMFSample_SetSampleDuration(samples[0].pSample, duration))) + goto done; + } + + if (SUCCEEDED(hr = IMFSample_GetSampleTime(impl->sample_buf, &time))) + { + if (FAILED(hr = IMFSample_SetSampleTime(samples[0].pSample, time))) + goto done; + } + + if (FAILED(hr = IMFSample_ConvertToContiguousBuffer(impl->sample_buf, &in_buf))) + goto done; + + if (FAILED(hr = IMFSample_ConvertToContiguousBuffer(samples[0].pSample, &out_buf))) + goto done; + + if (FAILED(hr = static_color_effect_convert_frame(impl, &subtype, in_buf, out_buf))) + goto done; + + *status = samples[0].dwStatus = 0; + impl->processing_delta--; +done: + if (impl->sample_buf) IMFSample_Release(impl->sample_buf); + impl->sample_buf = NULL; + if (out_buf) IMFMediaBuffer_Release(out_buf); + if (in_buf) IMFMediaBuffer_Release(in_buf); + + LeaveCriticalSection(&impl->cs); + + return hr; +} + +static const IMFTransformVtbl static_color_effect_vtbl = +{ + static_color_effect_QueryInterface, + static_color_effect_AddRef, + static_color_effect_Release, + static_color_effect_GetStreamLimits, + static_color_effect_GetStreamCount, + static_color_effect_GetStreamIDs, + static_color_effect_GetInputStreamInfo, + static_color_effect_GetOutputStreamInfo, + static_color_effect_GetAttributes, + static_color_effect_GetInputStreamAttributes, + static_color_effect_GetOutputStreamAttributes, + static_color_effect_DeleteInputStream, + static_color_effect_AddInputStreams, + static_color_effect_GetInputAvailableType, + static_color_effect_GetOutputAvailableType, + static_color_effect_SetInputType, + static_color_effect_SetOutputType, + static_color_effect_GetInputCurrentType, + static_color_effect_GetOutputCurrentType, + static_color_effect_GetInputStatus, + static_color_effect_GetOutputStatus, + static_color_effect_SetOutputBounds, + static_color_effect_ProcessEvent, + static_color_effect_ProcessMessage, + static_color_effect_ProcessInput, + static_color_effect_ProcessOutput, +}; + +HRESULT static_color_effect_create(BYTE u, BYTE v, LONG area, struct static_color_effect **out) +{ + struct static_color_effect *impl; + + if (!(impl = calloc(1, sizeof(*impl)))) + return E_OUTOFMEMORY; + + impl->IMFTransform_iface.lpVtbl = &static_color_effect_vtbl; + impl->refcount = 1; + impl->paint_area = area; + impl->u = u; + impl->v = v; + + InitializeCriticalSection(&impl->cs); + + *out = impl; + return S_OK; +} + +static void test_video_effect(void) +{ + struct test_transfer_notify notify = {{&test_transfer_notify_vtbl}}; + WCHAR url[] = { L"i420-64x64.avi" }; + struct static_color_effect *video_effect, *video_effect2, *video_effect3; + ID3D11Texture2D *texture, *rb_texture; + D3D11_MAPPED_SUBRESOURCE map_desc; + IMFMediaEngineEx *media_engine_ex; + IMFDXGIDeviceManager *manager; + ID3D11DeviceContext *context; + D3D11_TEXTURE2D_DESC desc; + IMFByteStream *stream; + ID3D11Device *device; + RECT dst_rect; + UINT token; + HRESULT hr; + DWORD res; + ULONG ref; + + stream = load_resource(L"i420-64x64.avi", L"video/avi"); + + notify.ready_event = CreateEventW(NULL, FALSE, FALSE, NULL); + ok(!!notify.ready_event, "CreateEventW failed, error %lu\n", GetLastError()); + + if (!(device = create_d3d11_device())) + { + skip("Failed to create a D3D11 device, skipping tests.\n"); + return; + } + + hr = pMFCreateDXGIDeviceManager(&token, &manager); + ok(hr == S_OK, "Unexpected hr %#lx.\n", hr); + hr = IMFDXGIDeviceManager_ResetDevice(manager, (IUnknown *)device, token); + ok(hr == S_OK, "Unexpected hr %#lx.\n", hr); + + media_engine_ex = create_media_engine_ex(¬ify.IMFMediaEngineNotify_iface, + manager, DXGI_FORMAT_B8G8R8X8_UNORM); + + IMFDXGIDeviceManager_Release(manager); + + if (!(notify.media_engine = media_engine_ex)) + return; + + memset(&desc, 0, sizeof(desc)); + desc.Width = 64; + desc.Height = 64; + desc.ArraySize = 1; + desc.Format = DXGI_FORMAT_B8G8R8X8_UNORM; + desc.BindFlags = D3D11_BIND_RENDER_TARGET; + desc.SampleDesc.Count = 1; + hr = ID3D11Device_CreateTexture2D(device, &desc, NULL, &texture); + ok(hr == S_OK, "Unexpected hr %#lx.\n", hr); + + video_effect = NULL; + hr = static_color_effect_create(0x00, 0x00, EFFECT_PAINT_TOP, &video_effect); + ok(hr == S_OK, "Unexpected hr %#lx.\n", hr); + + video_effect2 = NULL; + hr = static_color_effect_create(0xff, 0xff, EFFECT_PAINT_RIGHT, &video_effect2); + ok(hr == S_OK, "Unexpected hr %#lx.\n", hr); + + video_effect3 = NULL; + hr = static_color_effect_create(0x00, 0xff, EFFECT_PAINT_BOTTOM, &video_effect3); + ok(hr == S_OK, "Unexpected hr %#lx.\n", hr); + + hr = IMFMediaEngineEx_InsertVideoEffect(media_engine_ex, (IUnknown *)&video_effect->IMFTransform_iface, FALSE); + todo_wine ok(hr == S_OK, "Unexpected hr %#lx.\n", hr); + EXPECT_REF(&video_effect->IMFTransform_iface, 2); + + hr = IMFMediaEngineEx_InsertVideoEffect(media_engine_ex, (IUnknown *)&video_effect2->IMFTransform_iface, FALSE); + todo_wine ok(hr == S_OK, "Unexpected hr %#lx.\n", hr); + EXPECT_REF(&video_effect2->IMFTransform_iface, 2); + + hr = IMFMediaEngineEx_InsertVideoEffect(media_engine_ex, (IUnknown *)&video_effect3->IMFTransform_iface, FALSE); + todo_wine ok(hr == S_OK || broken(hr == MF_E_INVALIDREQUEST), "Unexpected hr %#lx.\n", hr); + + if (FAILED(hr)) /* Win8 to Win10v1507 and Wine */ + { + goto done; + } + EXPECT_REF(&video_effect3->IMFTransform_iface, 2); + + hr = IMFMediaEngineEx_SetSourceFromByteStream(media_engine_ex, stream, url); + ok(hr == S_OK, "Unexpected hr %#lx.\n", hr); + IMFByteStream_Release(stream); + + res = WaitForSingleObject(notify.ready_event, 5000); + ok(!res, "Unexpected res %#lx.\n", res); + + if (FAILED(notify.error)) + { + win_skip("Media engine reported error %#lx, skipping tests.\n", notify.error); + goto done; + } + + /* FIXME: Wine first video frame is often full of garbage, wait for another update */ + res = WaitForSingleObject(notify.ready_event, 500); + /* It's also missing the MF_MEDIA_ENGINE_EVENT_TIMEUPDATE notifications */ + todo_wine + ok(!res, "Unexpected res %#lx.\n", res); + + SetRect(&dst_rect, 0, 0, desc.Width, desc.Height); + hr = IMFMediaEngineEx_TransferVideoFrame(notify.media_engine, (IUnknown *)texture, NULL, NULL, NULL); + ok(hr == S_OK || broken(hr == E_POINTER) /* w1064v1507 */, "Unexpected hr %#lx.\n", hr); + + ID3D11Texture2D_GetDesc(texture, &desc); + desc.Usage = D3D11_USAGE_STAGING; + desc.BindFlags = 0; + desc.CPUAccessFlags = D3D11_CPU_ACCESS_READ; + desc.MiscFlags = 0; + hr = ID3D11Device_CreateTexture2D(device, &desc, NULL, &rb_texture); + ok(hr == S_OK, "Unexpected hr %#lx.\n", hr); + + ID3D11Device_GetImmediateContext(device, &context); + ID3D11DeviceContext_CopySubresourceRegion(context, (ID3D11Resource *)rb_texture, + 0, 0, 0, 0, (ID3D11Resource *)texture, 0, NULL); + + memset(&map_desc, 0, sizeof(map_desc)); + hr = ID3D11DeviceContext_Map(context, (ID3D11Resource *)rb_texture, 0, D3D11_MAP_READ, 0, &map_desc); + ok(hr == S_OK, "Unexpected hr %#lx.\n", hr); + ok(!!map_desc.pData, "got pData %p\n", map_desc.pData); + ok(map_desc.DepthPitch == 16384, "got DepthPitch %u\n", map_desc.DepthPitch); + ok(map_desc.RowPitch == desc.Width * 4, "got RowPitch %u\n", map_desc.RowPitch); + check_rgb32_data(L"rgb32frame_static_colors.bmp", map_desc.pData, map_desc.RowPitch * desc.Height, &dst_rect); + ID3D11DeviceContext_Unmap(context, (ID3D11Resource *)rb_texture, 0); + + ok(video_effect->processing_delta == 0, "Unexpected delta %lu.\n", video_effect->processing_delta); + ok(video_effect2->processing_delta == 0, "Unexpected delta %lu.\n", video_effect2->processing_delta); + ok(video_effect3->processing_delta == 0, "Unexpected delta %lu.\n", video_effect3->processing_delta); + + ID3D11DeviceContext_Release(context); + ID3D11Texture2D_Release(rb_texture); + +done: + IMFMediaEngineEx_Shutdown(media_engine_ex); + + ref = IMFMediaEngineEx_Release(media_engine_ex); + todo_wine ok(!ref, "Unexpected ref %lu.\n", ref); + + ID3D11Texture2D_Release(texture); + ID3D11Device_Release(device); + + ref = IMFTransform_Release(&video_effect3->IMFTransform_iface); + todo_wine ok(!ref, "Unexpected ref %lu.\n", ref); + ref = IMFTransform_Release(&video_effect2->IMFTransform_iface); + todo_wine ok(!ref, "Unexpected ref %lu.\n", ref); + ref = IMFTransform_Release(&video_effect->IMFTransform_iface); + todo_wine ok(!ref, "Unexpected ref %lu.\n", ref); + + CloseHandle(notify.ready_event); +} + +#undef EFFECT_PAINT_TOP +#undef EFFECT_PAINT_RIGHT +#undef EFFECT_PAINT_BOTTOM +#undef EFFECT_PAINT_LEFT + START_TEST(mfmediaengine) { HRESULT hr; @@ -1357,6 +2135,7 @@ START_TEST(mfmediaengine) test_SetSourceFromByteStream(); test_audio_configuration(); test_TransferVideoFrame(); + test_video_effect();
IMFMediaEngineClassFactory_Release(factory);
diff --git a/dlls/mfmediaengine/tests/resource.rc b/dlls/mfmediaengine/tests/resource.rc index 50152586758..141418ac928 100644 --- a/dlls/mfmediaengine/tests/resource.rc +++ b/dlls/mfmediaengine/tests/resource.rc @@ -30,3 +30,7 @@ i420-64x64.avi RCDATA i420-64x64.avi /* Generated from running the tests on Windows */ /* @makedep: rgb32frame.bmp */ rgb32frame.bmp RCDATA rgb32frame.bmp + +/* Generated from running the tests on Windows */ +/* @makedep: rgb32frame_static_colors.bmp */ +rgb32frame_static_colors.bmp RCDATA rgb32frame_static_colors.bmp diff --git a/dlls/mfmediaengine/tests/rgb32frame_static_colors.bmp b/dlls/mfmediaengine/tests/rgb32frame_static_colors.bmp new file mode 100644 index 0000000000000000000000000000000000000000..71a38a5b80a6f67e8a039b9e7ff23a2d0fb861cf GIT binary patch literal 16438 zcmeI&u?@m75Cu@f9!Qi7fKo^4vjBV0qwJKOvc=&<G)(Y8dO>H=S$5t(u3(ynu^rFQ z*ITdgQ>S+A-}*Bi>-REOHRTQ%V1NMz7+`<_1{h#~0R|XgfB^;=V1NMz7+`<_1{h#~ z0S5lvz-z0Psz=rNo~^cBDpk{S?T4>uwaR^#W$Ayu=;ses?)0sk-}_J6-}mM}cLVY- z|MKtmKWQM>{mei5zy6=h{=T>T=Wam$<zN2Ie@O$m?q~kf|MmZ5_V>NzKX(K2FaPpy N{!1Fjb-&-3|K38dTF?Lh
literal 0 HcmV?d00001
From: Bernhard Kölbl besentv@gmail.com
--- dlls/mfmediaengine/main.c | 114 ++++++++++++++++++++++- dlls/mfmediaengine/tests/mfmediaengine.c | 9 +- 2 files changed, 116 insertions(+), 7 deletions(-)
diff --git a/dlls/mfmediaengine/main.c b/dlls/mfmediaengine/main.c index b3ac236c78e..a1bc50609b9 100644 --- a/dlls/mfmediaengine/main.c +++ b/dlls/mfmediaengine/main.c @@ -113,6 +113,12 @@ struct rect float left, top, right, bottom; };
+struct effect +{ + IUnknown *object; + BOOL optional; +}; + struct media_engine { IMFMediaEngineEx IMFMediaEngineEx_iface; @@ -146,6 +152,12 @@ struct media_engine IMFPresentationDescriptor *pd; } presentation; struct + { + struct effect *effects; + size_t count; + size_t capacity; + } video_effects; + struct { LONGLONG pts; SIZE size; @@ -1018,6 +1030,48 @@ static HRESULT media_engine_create_source_node(IMFMediaSource *source, IMFPresen return S_OK; }
+static HRESULT media_engine_create_video_effects(struct media_engine *engine, + IMFTopologyNode *src, IMFTopologyNode *sink, IMFTopology *topology) +{ + struct effect *effects = NULL; + IMFTopologyNode *last = src; + size_t i, count = 0; + HRESULT hr; + + effects = engine->video_effects.effects; + count = engine->video_effects.count; + + IMFTopologyNode_AddRef(last); + + for (i = 0; i < count; ++i) + { + IMFTopologyNode *node = NULL; + + if (FAILED(hr = MFCreateTopologyNode(MF_TOPOLOGY_TRANSFORM_NODE, &node))) + { + WARN("Failed to create transform node, hr %#lx", hr); + continue; + } + + IMFTopologyNode_SetObject(node, (IUnknown *)effects[i].object); + IMFTopologyNode_SetUINT32(node, &MF_TOPONODE_NOSHUTDOWN_ON_REMOVE, FALSE); + + if (effects[i].optional) + IMFTopologyNode_SetUINT32(node, &MF_TOPONODE_CONNECT_METHOD, MF_CONNECT_AS_OPTIONAL); + + IMFTopology_AddNode(topology, node); + IMFTopologyNode_ConnectOutput(last, 0, node, 0); + + IMFTopologyNode_Release(last); + last = node; + } + + IMFTopologyNode_Release(last); + + hr = IMFTopologyNode_ConnectOutput(last, 0, sink, 0); + return hr; +} + static HRESULT media_engine_create_audio_renderer(struct media_engine *engine, IMFTopologyNode **node) { unsigned int category, role; @@ -1105,6 +1159,19 @@ static void media_engine_clear_presentation(struct media_engine *engine) memset(&engine->presentation, 0, sizeof(engine->presentation)); }
+static void media_engine_clear_effects(struct media_engine *engine) +{ + size_t i; + + for (i = 0; i < engine->video_effects.count; ++i) + IUnknown_Release(engine->video_effects.effects[i].object); + + free(engine->video_effects.effects); + engine->video_effects.effects = NULL; + engine->video_effects.capacity = 0; + engine->video_effects.count = 0; +} + static HRESULT media_engine_create_topology(struct media_engine *engine, IMFMediaSource *source) { IMFStreamDescriptor *sd_audio = NULL, *sd_video = NULL; @@ -1227,7 +1294,9 @@ static HRESULT media_engine_create_topology(struct media_engine *engine, IMFMedi { IMFTopology_AddNode(topology, video_src); IMFTopology_AddNode(topology, grabber_node); - IMFTopologyNode_ConnectOutput(video_src, 0, grabber_node, 0); + + if (FAILED(hr = media_engine_create_video_effects(engine, video_src, grabber_node, topology))) + WARN("Failed to create video effect nodes, hr %#lx.\n", hr); }
if (SUCCEEDED(hr)) @@ -1382,6 +1451,7 @@ static void free_media_engine(struct media_engine *engine) IMFAttributes_Release(engine->attributes); if (engine->resolver) IMFSourceResolver_Release(engine->resolver); + media_engine_clear_effects(engine); media_engine_release_video_frame_resources(engine); media_engine_clear_presentation(engine); if (engine->device_manager) @@ -2587,11 +2657,49 @@ static HRESULT WINAPI media_engine_IsProtected(IMFMediaEngineEx *iface, BOOL *pr return E_NOTIMPL; }
+static HRESULT media_engine_insert_effect(struct media_engine *engine, IUnknown *effect, BOOL is_optional) +{ + size_t *count, *capacity; + struct effect **effects; + HRESULT hr = S_OK; + + capacity = &engine->video_effects.capacity; + effects = &engine->video_effects.effects; + count = &engine->video_effects.count; + + if (engine->flags & FLAGS_ENGINE_SHUT_DOWN) + hr = MF_E_SHUTDOWN; + else if (!mf_array_reserve((void **)effects, capacity, *count + 1, sizeof(**effects))) + { + hr = E_OUTOFMEMORY; + } + else + { + if (effect) + { + (*effects)[*count].object = effect; + IUnknown_AddRef((*effects)[*count].object); + } + (*effects)[*count].optional = is_optional; + + (*count)++; + } + + return hr; +} + static HRESULT WINAPI media_engine_InsertVideoEffect(IMFMediaEngineEx *iface, IUnknown *effect, BOOL is_optional) { - FIXME("%p, %p, %d stub.\n", iface, effect, is_optional); + struct media_engine *engine = impl_from_IMFMediaEngineEx(iface); + HRESULT hr = S_OK;
- return E_NOTIMPL; + TRACE("%p, %p, %d.\n", iface, effect, is_optional); + + EnterCriticalSection(&engine->cs); + hr = media_engine_insert_effect(engine, effect, is_optional); + LeaveCriticalSection(&engine->cs); + + return hr; }
static HRESULT WINAPI media_engine_InsertAudioEffect(IMFMediaEngineEx *iface, IUnknown *effect, BOOL is_optional) diff --git a/dlls/mfmediaengine/tests/mfmediaengine.c b/dlls/mfmediaengine/tests/mfmediaengine.c index c61267133b6..826d1028362 100644 --- a/dlls/mfmediaengine/tests/mfmediaengine.c +++ b/dlls/mfmediaengine/tests/mfmediaengine.c @@ -2013,18 +2013,19 @@ static void test_video_effect(void) ok(hr == S_OK, "Unexpected hr %#lx.\n", hr);
hr = IMFMediaEngineEx_InsertVideoEffect(media_engine_ex, (IUnknown *)&video_effect->IMFTransform_iface, FALSE); - todo_wine ok(hr == S_OK, "Unexpected hr %#lx.\n", hr); + ok(hr == S_OK, "Unexpected hr %#lx.\n", hr); EXPECT_REF(&video_effect->IMFTransform_iface, 2);
hr = IMFMediaEngineEx_InsertVideoEffect(media_engine_ex, (IUnknown *)&video_effect2->IMFTransform_iface, FALSE); - todo_wine ok(hr == S_OK, "Unexpected hr %#lx.\n", hr); + ok(hr == S_OK, "Unexpected hr %#lx.\n", hr); EXPECT_REF(&video_effect2->IMFTransform_iface, 2);
hr = IMFMediaEngineEx_InsertVideoEffect(media_engine_ex, (IUnknown *)&video_effect3->IMFTransform_iface, FALSE); - todo_wine ok(hr == S_OK || broken(hr == MF_E_INVALIDREQUEST), "Unexpected hr %#lx.\n", hr); + ok(hr == S_OK || broken(hr == MF_E_INVALIDREQUEST), "Unexpected hr %#lx.\n", hr);
- if (FAILED(hr)) /* Win8 to Win10v1507 and Wine */ + if (FAILED(hr)) { + win_skip("Inserting more than two effects fails on Win8 to Win10v1507 %#lx, skipping tests.\n", hr); goto done; } EXPECT_REF(&video_effect3->IMFTransform_iface, 2);
Hi,
It looks like your patch introduced the new failures shown below. Please investigate and fix them before resubmitting your patch. If they are not new, fixing them anyway would help a lot. Otherwise please ask for the known failures list to be updated.
The tests also ran into some preexisting test failures. If you know how to fix them that would be helpful. See the TestBot job for the details:
The full results can be found at: https://testbot.winehq.org/JobDetails.pl?Key=133991
Your paranoid android.
=== w864 (32 bit report) ===
mfmediaengine: mfmediaengine.c:2088: Test failed: Unexpected ref 2. mfmediaengine.c:2096: Test failed: Unexpected ref 1. mfmediaengine.c:2098: Test failed: Unexpected ref 1.
v4: - Immediately add created nodes to toplogy. - Nullcheck for AddRef on nodes.
Nikolay Sivov (@nsivov) commented about dlls/mfmediaengine/tests/mfmediaengine.c:
hr = IMFMediaEngineEx_InsertVideoEffect(media_engine_ex, (IUnknown *)&video_effect2->IMFTransform_iface, FALSE);
- todo_wine ok(hr == S_OK, "Unexpected hr %#lx.\n", hr);
ok(hr == S_OK, "Unexpected hr %#lx.\n", hr); EXPECT_REF(&video_effect2->IMFTransform_iface, 2);
hr = IMFMediaEngineEx_InsertVideoEffect(media_engine_ex, (IUnknown *)&video_effect3->IMFTransform_iface, FALSE);
- todo_wine ok(hr == S_OK || broken(hr == MF_E_INVALIDREQUEST), "Unexpected hr %#lx.\n", hr);
- ok(hr == S_OK || broken(hr == MF_E_INVALIDREQUEST), "Unexpected hr %#lx.\n", hr);
- if (FAILED(hr)) /* Win8 to Win10v1507 and Wine */
- if (FAILED(hr)) {
}win_skip("Inserting more than two effects fails on Win8 to Win10v1507 %#lx, skipping tests.\n", hr); goto done;
If it's a matter of 2 or 3 and not about more than 1, let's use 2, and remove this skip.
Please apply this one for implementation. Tests will need more work, but I'm fine with dropping them for now.
[0001-mfmediaengine-Add-video-effects-to-the-pipeline.txt](/uploads/7fc6d835de7447be380b2722f2f78e3d/0001-mfmediaengine-Add-video-effects-to-the-pipeline.txt)
On Wed Jun 21 09:35:46 2023 +0000, Nikolay Sivov wrote:
Please apply this one for implementation. Tests will need more work, but I'm fine with dropping them for now. [0001-mfmediaengine-Add-video-effects-to-the-pipeline.txt](/uploads/7fc6d835de7447be380b2722f2f78e3d/0001-mfmediaengine-Add-video-effects-to-the-pipeline.txt)
This is a little unexpected, what's the matter with the tests?
Nikolay Sivov (@nsivov) commented about dlls/mfmediaengine/tests/mfmediaengine.c:
goto done;
- if (FAILED(hr = IMFMediaType_GetItem(type, &MF_MT_FRAME_SIZE, &propvar)))
goto done;
- impl->frame_width = width = propvar.uhVal.HighPart;
- impl->frame_height = height = propvar.uhVal.LowPart;
- if (impl->media_type_in) IMFMediaType_Release(impl->media_type_in);
- impl->media_type_in = type;
- IMFMediaType_AddRef(impl->media_type_in);
- /* Idk why, but Windows apparently expects this function to also set an output type... */
- if (impl->media_type_out) IMFMediaType_Release(impl->media_type_out);
- impl->media_type_out = type;
- IMFMediaType_AddRef(impl->media_type_out);
This one is a strange requirement.
Nikolay Sivov (@nsivov) commented about dlls/mfmediaengine/tests/mfmediaengine.c:
- hr = IMFMediaEngineEx_InsertVideoEffect(media_engine_ex, (IUnknown *)&video_effect->IMFTransform_iface, FALSE);
- todo_wine ok(hr == S_OK, "Unexpected hr %#lx.\n", hr);
- EXPECT_REF(&video_effect->IMFTransform_iface, 2);
- hr = IMFMediaEngineEx_InsertVideoEffect(media_engine_ex, (IUnknown *)&video_effect2->IMFTransform_iface, FALSE);
- todo_wine ok(hr == S_OK, "Unexpected hr %#lx.\n", hr);
- EXPECT_REF(&video_effect2->IMFTransform_iface, 2);
- hr = IMFMediaEngineEx_InsertVideoEffect(media_engine_ex, (IUnknown *)&video_effect3->IMFTransform_iface, FALSE);
- todo_wine ok(hr == S_OK || broken(hr == MF_E_INVALIDREQUEST), "Unexpected hr %#lx.\n", hr);
- if (FAILED(hr)) /* Win8 to Win10v1507 and Wine */
- {
goto done;
- }
- EXPECT_REF(&video_effect3->IMFTransform_iface, 2);
This part that I mentioned before might as well go away, if two effects are always supported.
Nikolay Sivov (@nsivov) commented about dlls/mfmediaengine/tests/mfmediaengine.c:
goto done;
- }
- EXPECT_REF(&video_effect3->IMFTransform_iface, 2);
- hr = IMFMediaEngineEx_SetSourceFromByteStream(media_engine_ex, stream, url);
- ok(hr == S_OK, "Unexpected hr %#lx.\n", hr);
- IMFByteStream_Release(stream);
- res = WaitForSingleObject(notify.ready_event, 5000);
- ok(!res, "Unexpected res %#lx.\n", res);
- if (FAILED(notify.error))
- {
win_skip("Media engine reported error %#lx, skipping tests.\n", notify.error);
goto done;
- }
What does this mean?
Nikolay Sivov (@nsivov) commented about dlls/mfmediaengine/tests/mfmediaengine.c:
- IMFByteStream_Release(stream);
- res = WaitForSingleObject(notify.ready_event, 5000);
- ok(!res, "Unexpected res %#lx.\n", res);
- if (FAILED(notify.error))
- {
win_skip("Media engine reported error %#lx, skipping tests.\n", notify.error);
goto done;
- }
- /* FIXME: Wine first video frame is often full of garbage, wait for another update */
- res = WaitForSingleObject(notify.ready_event, 500);
- /* It's also missing the MF_MEDIA_ENGINE_EVENT_TIMEUPDATE notifications */
- todo_wine
- ok(!res, "Unexpected res %#lx.\n", res);
What is this about? What kind of garbage?
Nikolay Sivov (@nsivov) commented about dlls/mfmediaengine/tests/mfmediaengine.c:
- if (FAILED(notify.error))
- {
win_skip("Media engine reported error %#lx, skipping tests.\n", notify.error);
goto done;
- }
- /* FIXME: Wine first video frame is often full of garbage, wait for another update */
- res = WaitForSingleObject(notify.ready_event, 500);
- /* It's also missing the MF_MEDIA_ENGINE_EVENT_TIMEUPDATE notifications */
- todo_wine
- ok(!res, "Unexpected res %#lx.\n", res);
- SetRect(&dst_rect, 0, 0, desc.Width, desc.Height);
- hr = IMFMediaEngineEx_TransferVideoFrame(notify.media_engine, (IUnknown *)texture, NULL, NULL, NULL);
- ok(hr == S_OK || broken(hr == E_POINTER) /* w1064v1507 */, "Unexpected hr %#lx.\n", hr);
Any way to avoid this E_POINTER?
Nikolay Sivov (@nsivov) commented about dlls/mfmediaengine/tests/mfmediaengine.c:
+done:
- IMFMediaEngineEx_Shutdown(media_engine_ex);
- ref = IMFMediaEngineEx_Release(media_engine_ex);
- todo_wine ok(!ref, "Unexpected ref %lu.\n", ref);
- ID3D11Texture2D_Release(texture);
- ID3D11Device_Release(device);
- ref = IMFTransform_Release(&video_effect3->IMFTransform_iface);
- todo_wine ok(!ref, "Unexpected ref %lu.\n", ref);
- ref = IMFTransform_Release(&video_effect2->IMFTransform_iface);
- todo_wine ok(!ref, "Unexpected ref %lu.\n", ref);
- ref = IMFTransform_Release(&video_effect->IMFTransform_iface);
- todo_wine ok(!ref, "Unexpected ref %lu.\n", ref);
Why are these not released? I know we have an issue with sinks/IMFActivate, but what's wrong with transforms?
Nikolay Sivov (@nsivov) commented about dlls/mfmediaengine/tests/mfmediaengine.c:
- desc.MiscFlags = 0;
- hr = ID3D11Device_CreateTexture2D(device, &desc, NULL, &rb_texture);
- ok(hr == S_OK, "Unexpected hr %#lx.\n", hr);
- ID3D11Device_GetImmediateContext(device, &context);
- ID3D11DeviceContext_CopySubresourceRegion(context, (ID3D11Resource *)rb_texture,
0, 0, 0, 0, (ID3D11Resource *)texture, 0, NULL);
- memset(&map_desc, 0, sizeof(map_desc));
- hr = ID3D11DeviceContext_Map(context, (ID3D11Resource *)rb_texture, 0, D3D11_MAP_READ, 0, &map_desc);
- ok(hr == S_OK, "Unexpected hr %#lx.\n", hr);
- ok(!!map_desc.pData, "got pData %p\n", map_desc.pData);
- ok(map_desc.DepthPitch == 16384, "got DepthPitch %u\n", map_desc.DepthPitch);
- ok(map_desc.RowPitch == desc.Width * 4, "got RowPitch %u\n", map_desc.RowPitch);
- check_rgb32_data(L"rgb32frame_static_colors.bmp", map_desc.pData, map_desc.RowPitch * desc.Height, &dst_rect);
- ID3D11DeviceContext_Unmap(context, (ID3D11Resource *)rb_texture, 0);
Used format and input data adds a lot of bitmap copying, handling of planar format, etc, when all you need to know is that transforms are called in order. We might even use passthrough transform that does not do anything at all, but sets some attribute on output sample that following transform can check.
So my concern about this test is that it adds are lot of lines of noise that we don't need to test what needs to be tested, leaves unexplained skips and broken() cases, as we all as some todos on cleanup.
On Wed Jun 21 10:14:20 2023 +0000, Nikolay Sivov wrote:
This one is a strange requirement.
I did quite a bunch of fiddling around with this, and only this turned out to work.
What makes it strange to you, how does it usually work?
On Wed Jun 21 10:14:21 2023 +0000, Nikolay Sivov wrote:
This part that I mentioned before might as well go away, if two effects are always supported.
Sure
On Wed Jun 21 10:14:21 2023 +0000, Nikolay Sivov wrote:
What does this mean?
This means media engine wasn't able to bind the source, either because of topo or session issues, I'd say.
On Wed Jun 21 10:31:55 2023 +0000, Bernhard Kölbl wrote:
I did quite a bunch of fiddling around with this, and only this turned out to work. What makes it strange to you, how does it usually work?
Normally you set input type, then output type separately, it's documented like that for transforms. That's what happens in color converter in winegstreamer. If setting types separately work with the session on Windows, there is something else going on.
On Wed Jun 21 10:33:00 2023 +0000, Bernhard Kölbl wrote:
This means media engine wasn't able to bind the source, either because of topo or session issues, I'd say.
Does it actually happen?
On Wed Jun 21 10:14:23 2023 +0000, Nikolay Sivov wrote:
Why are these not released? I know we have an issue with sinks/IMFActivate, but what's wrong with transforms?
They are not released because session holds a ref to media engine via the Samplegrabber Sink Callback shared ref, so because of the ActivateObject issues, we never release media engine and thus never the MFTs ... When you apply the ActivateObject patch from the other Mr, this releases fine
On Wed Jun 21 10:14:23 2023 +0000, Nikolay Sivov wrote:
Used format and input data adds a lot of bitmap copying, handling of planar format, etc, when all you need to know is that transforms are called in order. We might even use passthrough transform that does not do anything at all, but sets some attribute on output sample that following transform can check. So my concern about this test is that it adds are lot of lines of noise that we don't need to test what needs to be tested, leaves unexplained skips and broken() cases, as we all as some todos on cleanup.
will do
On Wed Jun 21 10:37:47 2023 +0000, Nikolay Sivov wrote:
Normally you set input type, then output type separately, it's documented like that for transforms. That's what happens in color converter in winegstreamer. If setting types separately work with the session on Windows, there is something else going on.
I'm honestly out of ideas of what might be happening here, but it definitely didn't work like the documentation said.
On Wed Jun 21 10:14:23 2023 +0000, Nikolay Sivov wrote:
Any way to avoid this E_POINTER?
Ahh `dst_rect` isn't set.