The test has a little inspiration from the SDK samples.
-- v2: mfmediaengine: Add support for inserting video effects. mfmediaengine/tests: Add tests for video effects. mf: Don't make stream sink shutdown dependent on IMFActivate presence in node. mf: Don't leak sink in session_get_renderer_node_service(). mf: Clear queued topologies on session shutdown.
From: Bernhard Kölbl besentv@gmail.com
This prevents media engine from leaking one reference to itself, due to sharing its refcount with the sample grabber sink calback, which was in turned gets refrenced through nodes in the queued topoligies.
Signed-off-by: Bernhard Kölbl besentv@gmail.com --- dlls/mf/session.c | 1 + 1 file changed, 1 insertion(+)
diff --git a/dlls/mf/session.c b/dlls/mf/session.c index 8b29c8487fb..3641cc74b83 100644 --- a/dlls/mf/session.c +++ b/dlls/mf/session.c @@ -2225,6 +2225,7 @@ static HRESULT WINAPI mfsession_Shutdown(IMFMediaSession *iface) IMFPresentationClock_Release(session->clock); session->clock = NULL; session_clear_presentation(session); + session_clear_queued_topologies(session); session_submit_simple_command(session, SESSION_CMD_SHUTDOWN); } LeaveCriticalSection(&session->cs);
From: Bernhard Kölbl besentv@gmail.com
Signed-off-by: Bernhard Kölbl besentv@gmail.com --- dlls/mf/session.c | 1 + 1 file changed, 1 insertion(+)
diff --git a/dlls/mf/session.c b/dlls/mf/session.c index 3641cc74b83..555a71dcdf7 100644 --- a/dlls/mf/session.c +++ b/dlls/mf/session.c @@ -2397,6 +2397,7 @@ static HRESULT session_get_renderer_node_service(struct media_session *session, if (FAILED(hr = MFGetService((IUnknown *)sink, service, riid, obj))) WARN("Failed to get service from renderer node, %#lx.\n", hr); } + IMFMediaSink_Release(sink); } IMFStreamSink_Release(stream_sink); }
From: Bernhard Kölbl besentv@gmail.com
Nodes only hold a ref to an activate object when they are a sink anyway. See session_bind_output_nodes() for reference.
Signed-off-by: Bernhard Kölbl besentv@gmail.com --- dlls/mf/session.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/dlls/mf/session.c b/dlls/mf/session.c index 555a71dcdf7..efe568f987f 100644 --- a/dlls/mf/session.c +++ b/dlls/mf/session.c @@ -762,7 +762,7 @@ static void session_shutdown_current_topology(struct media_session *session) WARN("Failed to shut down activation object for the sink, hr %#lx.\n", hr); IMFActivate_Release(activate); } - else if (SUCCEEDED(topology_node_get_object(node, &IID_IMFStreamSink, (void **)&stream_sink))) + if (SUCCEEDED(topology_node_get_object(node, &IID_IMFStreamSink, (void **)&stream_sink))) { if (SUCCEEDED(IMFStreamSink_GetMediaSink(stream_sink, &sink))) {
From: Bernhard Kölbl besentv@gmail.com
Signed-off-by: Bernhard Kölbl besentv@gmail.com --- dlls/mfmediaengine/tests/mfmediaengine.c | 777 ++++++++++++++++++ dlls/mfmediaengine/tests/resource.rc | 4 + .../tests/rgb32frame_static_colors.bmp | Bin 0 -> 16438 bytes 3 files changed, 781 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..c4d444b1aa2 100644 --- a/dlls/mfmediaengine/tests/mfmediaengine.c +++ b/dlls/mfmediaengine/tests/mfmediaengine.c @@ -1326,6 +1326,782 @@ 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, "Unexpected hr %#lx.\n", hr); + EXPECT_REF(&video_effect3->IMFTransform_iface, 2); + + if (FAILED(hr)) + goto done; + + 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); + ok(!ref, "Unexpected ref %lu.\n", ref); + + ID3D11Texture2D_Release(texture); + ID3D11Device_Release(device); + + ref = IMFTransform_Release(&video_effect3->IMFTransform_iface); + ok(!ref, "Unexpected ref %lu.\n", ref); + ref = IMFTransform_Release(&video_effect2->IMFTransform_iface); + ok(!ref, "Unexpected ref %lu.\n", ref); + ref = IMFTransform_Release(&video_effect->IMFTransform_iface); + 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 +2133,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 | 112 ++++++++++++++++++++++- dlls/mfmediaengine/tests/mfmediaengine.c | 7 +- 2 files changed, 113 insertions(+), 6 deletions(-)
diff --git a/dlls/mfmediaengine/main.c b/dlls/mfmediaengine/main.c index b3ac236c78e..2f20e568128 100644 --- a/dlls/mfmediaengine/main.c +++ b/dlls/mfmediaengine/main.c @@ -113,6 +113,12 @@ struct rect float left, top, right, bottom; };
+struct video_effect +{ + IUnknown *object; + BOOL optional; +}; + struct media_engine { IMFMediaEngineEx IMFMediaEngineEx_iface; @@ -146,6 +152,11 @@ struct media_engine IMFPresentationDescriptor *pd; } presentation; struct + { + struct video_effect *video_effects; + DWORD video_effects_size; + } effects; + struct { LONGLONG pts; SIZE size; @@ -1018,6 +1029,43 @@ 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 ***nodes) +{ + HRESULT hr = S_OK; + UINT i; + + if (!engine->effects.video_effects_size) + return S_OK; + + if(!(*nodes = calloc(engine->effects.video_effects_size, sizeof(**nodes)))) + return E_OUTOFMEMORY; + + for (i = 0; i < engine->effects.video_effects_size; ++i) + { + IMFTopologyNode *node = NULL; + + if (FAILED(hr = MFCreateTopologyNode(MF_TOPOLOGY_TRANSFORM_NODE, &node))) + goto failed; + + IMFTopologyNode_SetObject(node, (IUnknown *)engine->effects.video_effects[i].object); + IMFTopologyNode_SetUINT32(node, &MF_TOPONODE_NOSHUTDOWN_ON_REMOVE, FALSE); + + if (engine->effects.video_effects[i].optional) + IMFTopologyNode_SetUINT32(node, &MF_TOPONODE_CONNECT_METHOD, MF_CONNECT_AS_OPTIONAL); + + (*nodes)[i] = node; + } + + return S_OK; + +failed: + for (i = 0; i < engine->effects.video_effects_size; ++i) + if ((*nodes)[i]) IMFTopologyNode_Release((*nodes)[i]); + + *nodes = NULL; + return hr; +} + static HRESULT media_engine_create_audio_renderer(struct media_engine *engine, IMFTopologyNode **node) { unsigned int category, role; @@ -1105,6 +1153,18 @@ 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) +{ + UINT i; + + for (i = 0; i < engine->effects.video_effects_size; ++i) + IUnknown_Release(engine->effects.video_effects[i].object); + + free(engine->effects.video_effects); + engine->effects.video_effects = NULL; + engine->effects.video_effects_size = 0; +} + static HRESULT media_engine_create_topology(struct media_engine *engine, IMFMediaSource *source) { IMFStreamDescriptor *sd_audio = NULL, *sd_video = NULL; @@ -1190,6 +1250,7 @@ static HRESULT media_engine_create_topology(struct media_engine *engine, IMFMedi { IMFTopologyNode *sar_node = NULL, *audio_src = NULL; IMFTopologyNode *grabber_node = NULL, *video_src = NULL; + IMFTopologyNode **video_effects = NULL;
if (engine->flags & MF_MEDIA_ENGINE_REAL_TIME_MODE) IMFTopology_SetUINT32(topology, &MF_LOW_LATENCY, TRUE); @@ -1223,16 +1284,35 @@ static HRESULT media_engine_create_topology(struct media_engine *engine, IMFMedi if (FAILED(hr = media_engine_create_video_renderer(engine, &grabber_node))) WARN("Failed to create video grabber node, hr %#lx.\n", hr);
+ if (FAILED(media_engine_create_video_effects(engine, &video_effects))) + WARN("Failed to create video effect nodes, hr %#lx.\n", hr); + if (grabber_node && video_src) { IMFTopology_AddNode(topology, video_src); IMFTopology_AddNode(topology, grabber_node); - IMFTopologyNode_ConnectOutput(video_src, 0, grabber_node, 0); + + for (i = 0; video_effects && i < engine->effects.video_effects_size; ++i) + { + IMFTopology_AddNode(topology, video_effects[i]); + if (i > 0) + IMFTopologyNode_ConnectOutput(video_effects[i - 1], 0, video_effects[i], 0); + } + + if (video_effects) + { + IMFTopologyNode_ConnectOutput(video_src, 0, video_effects[0], 0); + IMFTopologyNode_ConnectOutput(video_effects[engine->effects.video_effects_size - 1], 0, grabber_node, 0); + } + else + IMFTopologyNode_ConnectOutput(video_src, 0, grabber_node, 0); }
if (SUCCEEDED(hr)) IMFTopologyNode_GetTopoNodeID(video_src, &engine->video_frame.node_id);
+ for (i = 0; video_effects && i < engine->effects.video_effects_size; ++i) + IMFTopologyNode_Release(video_effects[i]); if (grabber_node) IMFTopologyNode_Release(grabber_node); if (video_src) @@ -1382,6 +1462,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) @@ -2589,9 +2670,34 @@ static HRESULT WINAPI media_engine_IsProtected(IMFMediaEngineEx *iface, BOOL *pr
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); + struct video_effect *tmp; + HRESULT hr = S_OK;
- return E_NOTIMPL; + TRACE("%p, %p, %d.\n", iface, effect, is_optional); + + EnterCriticalSection(&engine->cs); + + if (engine->flags & FLAGS_ENGINE_SHUT_DOWN) + hr = MF_E_SHUTDOWN; + else if (!(tmp = realloc(engine->effects.video_effects, + sizeof(*engine->effects.video_effects) * (engine->effects.video_effects_size + 1)))) + { + hr = MF_E_INVALIDREQUEST; + } + else + { + tmp[engine->effects.video_effects_size].object = effect; + IUnknown_AddRef(tmp[engine->effects.video_effects_size].object); + tmp[engine->effects.video_effects_size].optional = is_optional; + + engine->effects.video_effects = tmp; + engine->effects.video_effects_size++; + } + + 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 c4d444b1aa2..26ec957253c 100644 --- a/dlls/mfmediaengine/tests/mfmediaengine.c +++ b/dlls/mfmediaengine/tests/mfmediaengine.c @@ -1376,6 +1376,7 @@ 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; }
@@ -2013,15 +2014,15 @@ 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, "Unexpected hr %#lx.\n", hr); + ok(hr == S_OK, "Unexpected hr %#lx.\n", hr); EXPECT_REF(&video_effect3->IMFTransform_iface, 2);
if (FAILED(hr))
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=133351
Your paranoid android.
=== w8 (32 bit report) ===
mfmediaengine: mfmediaengine.c:2025: Test failed: Unexpected hr 0xc00d36b2. mfmediaengine.c:2026: Test failed: Unexpected refcount 1, expected 2.
=== w8adm (32 bit report) ===
mfmediaengine: mfmediaengine.c:2025: Test failed: Unexpected hr 0xc00d36b2. mfmediaengine.c:2026: Test failed: Unexpected refcount 1, expected 2.
=== w864 (32 bit report) ===
mfmediaengine: mfmediaengine.c:2025: Test failed: Unexpected hr 0xc00d36b2. mfmediaengine.c:2026: Test failed: Unexpected refcount 1, expected 2.
=== w1064v1507 (32 bit report) ===
mfmediaengine: mfmediaengine.c:2025: Test failed: Unexpected hr 0xc00d36b2. mfmediaengine.c:2026: Test failed: Unexpected refcount 1, expected 2.
=== w864 (64 bit report) ===
mfmediaengine: mfmediaengine.c:2025: Test failed: Unexpected hr 0xc00d36b2. mfmediaengine.c:2026: Test failed: Unexpected refcount 1, expected 2.
=== w1064v1507 (64 bit report) ===
mfmediaengine: mfmediaengine.c:2025: Test failed: Unexpected hr 0xc00d36b2. mfmediaengine.c:2026: Test failed: Unexpected refcount 1, expected 2.
=== debian11 (32 bit report) ===
user32: msg.c:6897: Test failed: SetFocus(hwnd) on a button: 4: the msg 0x0007 was expected, but got msg 0x0005 instead msg.c:6897: Test failed: SetFocus(hwnd) on a button: 5: the msg 0x0138 was expected, but got msg 0x030f instead msg.c:6897: Test failed: SetFocus(hwnd) on a button: 6: the msg 0x0111 was expected, but got msg 0x001c instead msg.c:6897: Test failed: SetFocus(hwnd) on a button: 8: the msg 0x8000 was expected, but got msg 0x0086 instead msg.c:6897: Test failed: SetFocus(hwnd) on a button: 9: the msg sequence is not complete: expected 0000 - actual 0006
Please split mf.dll changes to separate MR, they are not related to the Media Engine.
On Fri Jun 2 19:20:19 2023 +0000, Nikolay Sivov wrote:
Please split mf.dll changes to separate MR, they are not related to the Media Engine.
Sure: https://gitlab.winehq.org/wine/wine/-/merge_requests/2958
Nikolay Sivov (@nsivov) commented about dlls/mf/session.c:
WARN("Failed to shut down activation object for the sink, hr %#lx.\n", hr); IMFActivate_Release(activate); }
else if (SUCCEEDED(topology_node_get_object(node, &IID_IMFStreamSink, (void **)&stream_sink)))
if (SUCCEEDED(topology_node_get_object(node, &IID_IMFStreamSink, (void **)&stream_sink))) { if (SUCCEEDED(IMFStreamSink_GetMediaSink(stream_sink, &sink))) {
Could you explain what does this fix?
Nikolay Sivov (@nsivov) commented about dlls/mfmediaengine/main.c:
- if (!engine->effects.video_effects_size)
return S_OK;
- if(!(*nodes = calloc(engine->effects.video_effects_size, sizeof(**nodes))))
return E_OUTOFMEMORY;
- for (i = 0; i < engine->effects.video_effects_size; ++i)
- {
IMFTopologyNode *node = NULL;
if (FAILED(hr = MFCreateTopologyNode(MF_TOPOLOGY_TRANSFORM_NODE, &node)))
goto failed;
IMFTopologyNode_SetObject(node, (IUnknown *)engine->effects.video_effects[i].object);
IMFTopologyNode_SetUINT32(node, &MF_TOPONODE_NOSHUTDOWN_ON_REMOVE, FALSE);
How did you verify this one?
Nikolay Sivov (@nsivov) commented about dlls/mfmediaengine/main.c:
- if (engine->flags & FLAGS_ENGINE_SHUT_DOWN)
hr = MF_E_SHUTDOWN;
- else if (!(tmp = realloc(engine->effects.video_effects,
sizeof(*engine->effects.video_effects) * (engine->effects.video_effects_size + 1))))
- {
hr = MF_E_INVALIDREQUEST;
- }
- else
- {
tmp[engine->effects.video_effects_size].object = effect;
IUnknown_AddRef(tmp[engine->effects.video_effects_size].object);
tmp[engine->effects.video_effects_size].optional = is_optional;
engine->effects.video_effects = tmp;
engine->effects.video_effects_size++;
Could you use mf_array_reserve()?
Nikolay Sivov (@nsivov) commented about dlls/mfmediaengine/main.c:
- FIXME("%p, %p, %d stub.\n", iface, effect, is_optional);
- struct media_engine *engine = impl_from_IMFMediaEngineEx(iface);
- struct video_effect *tmp;
- HRESULT hr = S_OK;
- return E_NOTIMPL;
- TRACE("%p, %p, %d.\n", iface, effect, is_optional);
- EnterCriticalSection(&engine->cs);
- if (engine->flags & FLAGS_ENGINE_SHUT_DOWN)
hr = MF_E_SHUTDOWN;
- else if (!(tmp = realloc(engine->effects.video_effects,
sizeof(*engine->effects.video_effects) * (engine->effects.video_effects_size + 1))))
- {
hr = MF_E_INVALIDREQUEST;
Is that E_OUTOFMEMORY?
On Mon Jun 5 12:24:41 2023 +0000, Nikolay Sivov wrote:
Could you explain what does this fix?
Sure: In `session_bind_output_nodes`, we put the ActivateObject for the MediaSink as Unknown and the MeidaSink-object as object. Now when this code you mentioned is called, if we have an activation object stored in the node as Iunknown, the code guarded by `else if` is never called even though we have an MediaSink object in the node. (So this code is only working with activation objects)
On Mon Jun 5 12:24:43 2023 +0000, Nikolay Sivov wrote:
Is that E_OUTOFMEMORY?
Yeah, maybe worth changing. I put `MF_E_INVALIDREQUEST` as the function sometimes returns it for whatever reason.
On Mon Jun 5 12:24:42 2023 +0000, Nikolay Sivov wrote:
How did you verify this one?
I put `MF_TOPONODE_NOSHUTDOWN_ON_REMOVE` because the other nodes use it as well, thought it's more of a Wine implementation detail.
On Tue Jun 6 09:18:15 2023 +0000, Bernhard Kölbl wrote:
Sure: In `session_bind_output_nodes`, we put the ActivateObject for the MediaSink as Unknown and the MeidaSink-object as object in the node. Now when this code you mentioned is called, if we have an activation object stored in the node as Iunknown, the code guarded by `else if` is never called even though we have an MediaSink object in the node. (So this code is only working without activation objects)
Ok, I see. Does it shutdown both through activation object and through the sink on Windows?
On Tue Jun 6 10:38:11 2023 +0000, Nikolay Sivov wrote:
Ok, I see. Does it shutdown both through activation object and through the sink on Windows?
Looking at https://testbot.winehq.org/JobDetails.pl?Key=133508, it seems like Windows only shuts down the Sink, not the activation object, so that needs to be moved into MediaEngine.