The test has a little inspiration from the SDK samples.
-- v9: mfmediaengine: Add support for inserting video effects.
From: Bernhard Kölbl besentv@gmail.com
Signed-off-by: Bernhard Kölbl besentv@gmail.com --- dlls/mfmediaengine/tests/mfmediaengine.c | 617 +++++++++++++++++++++++ 1 file changed, 617 insertions(+)
diff --git a/dlls/mfmediaengine/tests/mfmediaengine.c b/dlls/mfmediaengine/tests/mfmediaengine.c index 655b19af050..980f672dd2c 100644 --- a/dlls/mfmediaengine/tests/mfmediaengine.c +++ b/dlls/mfmediaengine/tests/mfmediaengine.c @@ -1326,6 +1326,622 @@ done: CloseHandle(notify.ready_event); }
+struct passthrough_mft +{ + IMFTransform IMFTransform_iface; + LONG refcount; + + IMFMediaType *media_type_in, *media_type_out; + IMFSample *sample_buf; + LONG processing_delta; + DWORD frame_size; + BOOL is_receiver; + + CRITICAL_SECTION cs; +}; + +const GUID *media_subtypes[] = +{ + &MFVideoFormat_I420, + /* Add more formats if needed. */ +}; + +static struct passthrough_mft *impl_from_IMFTransform(IMFTransform *iface) +{ + return CONTAINING_RECORD(iface, struct passthrough_mft, IMFTransform_iface); +} + +static HRESULT WINAPI passthrough_mft_QueryInterface(IMFTransform *iface, REFIID iid, void **out) +{ + struct passthrough_mft *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 passthrough_mft_AddRef(IMFTransform *iface) +{ + struct passthrough_mft *impl = impl_from_IMFTransform(iface); + ULONG refcount = InterlockedIncrement(&impl->refcount); + return refcount; +} + +static ULONG WINAPI passthrough_mft_Release(IMFTransform *iface) +{ + struct passthrough_mft *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 passthrough_mft_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 passthrough_mft_GetStreamCount(IMFTransform *iface, DWORD *inputs, DWORD *outputs) +{ + *inputs = *outputs = 1; + return S_OK; +} + +static HRESULT WINAPI passthrough_mft_GetStreamIDs(IMFTransform *iface, DWORD input_size, DWORD *inputs, + DWORD output_size, DWORD *outputs) +{ + return E_NOTIMPL; +} + +static HRESULT WINAPI passthrough_mft_GetInputStreamInfo(IMFTransform *iface, DWORD id, MFT_INPUT_STREAM_INFO *info) +{ + return E_NOTIMPL; +} + +static HRESULT WINAPI passthrough_mft_GetOutputStreamInfo(IMFTransform *iface, DWORD id, MFT_OUTPUT_STREAM_INFO *info) +{ + struct passthrough_mft *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 passthrough_mft_GetAttributes(IMFTransform *iface, IMFAttributes **attributes) +{ + return E_NOTIMPL; +} + +static HRESULT WINAPI passthrough_mft_GetInputStreamAttributes(IMFTransform *iface, DWORD id, IMFAttributes **attributes) +{ + return E_NOTIMPL; +} + +static HRESULT WINAPI passthrough_mft_GetOutputStreamAttributes(IMFTransform *iface, DWORD id, IMFAttributes **attributes) +{ + return E_NOTIMPL; +} + +static HRESULT WINAPI passthrough_mft_DeleteInputStream(IMFTransform *iface, DWORD id) +{ + return E_NOTIMPL; +} + +static HRESULT WINAPI passthrough_mft_AddInputStreams(IMFTransform *iface, DWORD streams, DWORD *ids) +{ + return E_NOTIMPL; +} + +static HRESULT WINAPI passthrough_mft_GetInputAvailableType(IMFTransform *iface, DWORD id, DWORD index, + IMFMediaType **type) +{ + struct passthrough_mft *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 passthrough_mft_GetOutputAvailableType(IMFTransform *iface, DWORD id, DWORD index, + IMFMediaType **type) +{ + struct passthrough_mft *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 passthrough_mft_SetInputType(IMFTransform *iface, DWORD id, IMFMediaType *type, DWORD flags) +{ + struct passthrough_mft *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; + + width = propvar.uhVal.HighPart; + 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 passthrough_mft_SetOutputType(IMFTransform *iface, DWORD id, IMFMediaType *type, DWORD flags) +{ + struct passthrough_mft *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 passthrough_mft_GetInputCurrentType(IMFTransform *iface, DWORD id, IMFMediaType **type) +{ + struct passthrough_mft *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 passthrough_mft_GetOutputCurrentType(IMFTransform *iface, DWORD id, IMFMediaType **type) +{ + struct passthrough_mft *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 passthrough_mft_GetInputStatus(IMFTransform *iface, DWORD id, DWORD *flags) +{ + return E_NOTIMPL; +} + +static HRESULT WINAPI passthrough_mft_GetOutputStatus(IMFTransform *iface, DWORD *flags) +{ + return E_NOTIMPL; +} + +static HRESULT WINAPI passthrough_mft_SetOutputBounds(IMFTransform *iface, LONGLONG lower, LONGLONG upper) +{ + return E_NOTIMPL; +} + +static HRESULT WINAPI passthrough_mft_ProcessEvent(IMFTransform *iface, DWORD id, IMFMediaEvent *event) +{ + return E_NOTIMPL; +} + +static HRESULT WINAPI passthrough_mft_ProcessMessage(IMFTransform *iface, MFT_MESSAGE_TYPE message, ULONG_PTR param) +{ + if (message == MFT_MESSAGE_COMMAND_FLUSH) + return E_NOTIMPL; + + return S_OK; +} + +static HRESULT WINAPI passthrough_mft_ProcessInput(IMFTransform *iface, DWORD id, IMFSample *sample, DWORD flags) +{ + struct passthrough_mft *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 WINAPI passthrough_mft_ProcessOutput(IMFTransform *iface, DWORD flags, DWORD count, + MFT_OUTPUT_DATA_BUFFER *samples, DWORD *status) +{ + struct passthrough_mft *impl = impl_from_IMFTransform(iface); + LONGLONG duration = 0, time = 0; + IMFMediaBuffer *buffer = NULL; + HRESULT hr = S_OK; + UINT32 val = 41; + + if (count != 1) return E_INVALIDARG; + + EnterCriticalSection(&impl->cs); + if (!impl->sample_buf) + { + hr = MF_E_TRANSFORM_NEED_MORE_INPUT; + goto done; + } + + hr = IMFSample_GetUINT32(impl->sample_buf, &MF_SESSION_GLOBAL_TIME, &val); + if (impl->is_receiver) + { + ok(hr == S_OK, "Unexpected hr %#lx.\n", hr); + ok(val == 42, "Got unexpexted value %u.\n", val); + } + else + { + ok(hr == MF_E_ATTRIBUTENOTFOUND, "Unexpected hr %#lx.\n", hr); + ok(val == 41, "Got unexpexted value %u.\n", val); + + IMFSample_SetUINT32(samples[0].pSample, &MF_SESSION_GLOBAL_TIME, 42); + } + + 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 (SUCCEEDED(hr = IMFSample_ConvertToContiguousBuffer(samples[0].pSample, &buffer))) + { + if (FAILED(hr = IMFSample_CopyToBuffer(impl->sample_buf, buffer))) + 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 (buffer) IMFMediaBuffer_Release(buffer); + + LeaveCriticalSection(&impl->cs); + + return hr; +} + +static const IMFTransformVtbl passthrough_mft_vtbl = +{ + passthrough_mft_QueryInterface, + passthrough_mft_AddRef, + passthrough_mft_Release, + passthrough_mft_GetStreamLimits, + passthrough_mft_GetStreamCount, + passthrough_mft_GetStreamIDs, + passthrough_mft_GetInputStreamInfo, + passthrough_mft_GetOutputStreamInfo, + passthrough_mft_GetAttributes, + passthrough_mft_GetInputStreamAttributes, + passthrough_mft_GetOutputStreamAttributes, + passthrough_mft_DeleteInputStream, + passthrough_mft_AddInputStreams, + passthrough_mft_GetInputAvailableType, + passthrough_mft_GetOutputAvailableType, + passthrough_mft_SetInputType, + passthrough_mft_SetOutputType, + passthrough_mft_GetInputCurrentType, + passthrough_mft_GetOutputCurrentType, + passthrough_mft_GetInputStatus, + passthrough_mft_GetOutputStatus, + passthrough_mft_SetOutputBounds, + passthrough_mft_ProcessEvent, + passthrough_mft_ProcessMessage, + passthrough_mft_ProcessInput, + passthrough_mft_ProcessOutput, +}; + +HRESULT passthrough_mft_create(BOOL is_receiver, struct passthrough_mft **out) +{ + struct passthrough_mft *impl; + + if (!(impl = calloc(1, sizeof(*impl)))) + return E_OUTOFMEMORY; + + impl->IMFTransform_iface.lpVtbl = &passthrough_mft_vtbl; + impl->is_receiver = is_receiver; + impl->refcount = 1; + + 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 passthrough_mft *video_effect, *video_effect2; + IMFMediaEngineEx *media_engine_ex; + IMFDXGIDeviceManager *manager; + D3D11_TEXTURE2D_DESC desc; + ID3D11Texture2D *texture; + 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 = passthrough_mft_create(FALSE, &video_effect); + ok(hr == S_OK, "Unexpected hr %#lx.\n", hr); + + video_effect2 = NULL; + hr = passthrough_mft_create(TRUE, &video_effect2); + 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); + todo_wine 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); + todo_wine EXPECT_REF(&video_effect2->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); + + /* Wait for MediaEngine to be ready. */ + res = WaitForSingleObject(notify.ready_event, 5000); + ok(!res, "Unexpected res %#lx.\n", res); + + /* Wait for another update. This makes MediaEngine shutdown more consistent on Windows. */ + res = WaitForSingleObject(notify.ready_event, 500); + /* Timeupdates are missing in Wine. */ + 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, &dst_rect, NULL); + ok(hr == S_OK, "Unexpected hr %#lx.\n", hr); + + 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); + +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_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); +} + START_TEST(mfmediaengine) { HRESULT hr; @@ -1357,6 +1973,7 @@ START_TEST(mfmediaengine) test_SetSourceFromByteStream(); test_audio_configuration(); test_TransferVideoFrame(); + test_video_effect();
IMFMediaEngineClassFactory_Release(factory);
From: Bernhard Kölbl besentv@gmail.com
--- dlls/mfmediaengine/main.c | 110 ++++++++++++++++++++++- dlls/mfmediaengine/tests/mfmediaengine.c | 20 ++--- 2 files changed, 114 insertions(+), 16 deletions(-)
diff --git a/dlls/mfmediaengine/main.c b/dlls/mfmediaengine/main.c index b3ac236c78e..3aeee7cd744 100644 --- a/dlls/mfmediaengine/main.c +++ b/dlls/mfmediaengine/main.c @@ -113,6 +113,19 @@ struct rect float left, top, right, bottom; };
+struct effect +{ + IUnknown *object; + BOOL optional; +}; + +struct effects +{ + struct effect *effects; + size_t count; + size_t capacity; +}; + struct media_engine { IMFMediaEngineEx IMFMediaEngineEx_iface; @@ -145,6 +158,7 @@ struct media_engine IMFMediaSource *source; IMFPresentationDescriptor *pd; } presentation; + struct effects video_effects; struct { LONGLONG pts; @@ -1018,6 +1032,46 @@ static HRESULT media_engine_create_source_node(IMFMediaSource *source, IMFPresen return S_OK; }
+static HRESULT media_engine_create_effects(struct effect *effects, size_t count, + IMFTopologyNode *src, IMFTopologyNode *sink, IMFTopology *topology) +{ + IMFTopologyNode *last = src; + HRESULT hr = S_OK; + size_t i; + + 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); + break; + } + + 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); + + if (SUCCEEDED(hr)) + 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,20 @@ static void media_engine_clear_presentation(struct media_engine *engine) memset(&engine->presentation, 0, sizeof(engine->presentation)); }
+static void media_engine_clear_effects(struct effects *effects) +{ + size_t i; + + for (i = 0; i < effects->count; ++i) + { + if (effects->effects[i].object) + IUnknown_Release(effects->effects[i].object); + } + + free(effects->effects); + memset(effects, 0, sizeof(*effects)); +} + static HRESULT media_engine_create_topology(struct media_engine *engine, IMFMediaSource *source) { IMFStreamDescriptor *sd_audio = NULL, *sd_video = NULL; @@ -1227,7 +1295,10 @@ 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_effects(engine->video_effects.effects, engine->video_effects.count, + video_src, grabber_node, topology))) + WARN("Failed to create video effect nodes, hr %#lx.\n", hr); }
if (SUCCEEDED(hr)) @@ -1382,6 +1453,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->video_effects); media_engine_release_video_frame_resources(engine); media_engine_clear_presentation(engine); if (engine->device_manager) @@ -2587,11 +2659,43 @@ static HRESULT WINAPI media_engine_IsProtected(IMFMediaEngineEx *iface, BOOL *pr return E_NOTIMPL; }
+static HRESULT media_engine_insert_effect(struct media_engine *engine, struct effects *effects, IUnknown *object, BOOL is_optional) +{ + HRESULT hr = S_OK; + + if (engine->flags & FLAGS_ENGINE_SHUT_DOWN) + hr = MF_E_SHUTDOWN; + else if (!mf_array_reserve((void **)&effects->effects, &effects->capacity, effects->count + 1, sizeof(*effects->effects))) + { + hr = E_OUTOFMEMORY; + } + else + { + effects->effects[effects->count].object = object; + if (object) + { + IUnknown_AddRef(effects->effects[effects->count].object); + } + effects->effects[effects->count].optional = is_optional; + + effects->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, &engine->video_effects, 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 980f672dd2c..36730d7f75f 100644 --- a/dlls/mfmediaengine/tests/mfmediaengine.c +++ b/dlls/mfmediaengine/tests/mfmediaengine.c @@ -1893,17 +1893,12 @@ 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); - todo_wine EXPECT_REF(&video_effect->IMFTransform_iface, 2); + 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); - todo_wine EXPECT_REF(&video_effect2->IMFTransform_iface, 2); - - if (FAILED(hr)) - { - goto done; - } + ok(hr == S_OK, "Unexpected hr %#lx.\n", hr); + EXPECT_REF(&video_effect2->IMFTransform_iface, 2);
hr = IMFMediaEngineEx_SetSourceFromByteStream(media_engine_ex, stream, url); ok(hr == S_OK, "Unexpected hr %#lx.\n", hr); @@ -1925,19 +1920,18 @@ static void test_video_effect(void) 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);
-done: IMFMediaEngineEx_Shutdown(media_engine_ex);
ref = IMFMediaEngineEx_Release(media_engine_ex); - ok(!ref, "Unexpected ref %lu.\n", ref); + todo_wine ok(!ref, "Unexpected ref %lu.\n", ref);
ID3D11Texture2D_Release(texture); ID3D11Device_Release(device);
ref = IMFTransform_Release(&video_effect2->IMFTransform_iface); - ok(!ref, "Unexpected ref %lu.\n", ref); + todo_wine ok(!ref, "Unexpected ref %lu.\n", ref); ref = IMFTransform_Release(&video_effect->IMFTransform_iface); - ok(!ref, "Unexpected ref %lu.\n", ref); + todo_wine ok(!ref, "Unexpected ref %lu.\n", ref);
CloseHandle(notify.ready_event); }
On Wed Jun 21 21:52:44 2023 +0000, Bernhard Kölbl wrote:
changed this line in [version 8 of the diff](/wine/wine/-/merge_requests/2778/diffs?diff_id=53209&start_sha=51f8d51dd2bee8ab13eecb0e5ee84045a52153b7#f56f5661ad29be925a16bb2ec6a9a6bb11da77b4_2075_1926)
So in the new version I tried to get rid of many unneeded test parts.
With current tests, how do we know that transforms are used at all?
On Tue Jun 27 10:53:51 2023 +0000, Nikolay Sivov wrote:
With current tests, how do we know that transforms are used at all?
Fair point, how about changing `processing_delta` to just count up and check if the value is !0 ?
On Tue Jun 27 10:53:51 2023 +0000, Bernhard Kölbl wrote:
Fair point, how about changing `processing_delta` to just count up and check if the value is !0 ?
Maybe, it could also use a better name.
Nikolay Sivov (@nsivov) commented about dlls/mfmediaengine/tests/mfmediaengine.c:
- {
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 (SUCCEEDED(hr = IMFSample_ConvertToContiguousBuffer(samples[0].pSample, &buffer)))
- {
if (FAILED(hr = IMFSample_CopyToBuffer(impl->sample_buf, buffer)))
goto done;
- }
Instead of all that, could you simply return original sample object?
See attached diff, on top of current version of this MR. It removes everything unnecessary I could find.
[0001-mfmediaengine-tests-Simply-passthrough-mft.txt](/uploads/a658d3f7519f675b7e63d1e16de3aee1/0001-mfmediaengine-tests-Simply-passthrough-mft.txt)
On Tue Jun 27 16:17:58 2023 +0000, Nikolay Sivov wrote:
See attached diff, on top of current version of this MR. It removes everything unnecessary I could find. [0001-mfmediaengine-tests-Simply-passthrough-mft.txt](/uploads/a658d3f7519f675b7e63d1e16de3aee1/0001-mfmediaengine-tests-Simply-passthrough-mft.txt)
You removed `IMFSample_SetUINT32(samples[0].pSample, &MF_SESSION_GLOBAL_TIME, 42);` etc., that looks like a mistake?
On Tue Jun 27 16:17:57 2023 +0000, Bernhard Kölbl wrote:
You removed `IMFSample_SetUINT32(samples[0].pSample, &MF_SESSION_GLOBAL_TIME, 42);` etc., that looks like a mistake?
What is it for?
On Tue Jun 27 17:38:38 2023 +0000, Nikolay Sivov wrote:
What is it for?
Making sure the MFTs are inserted in order.
On Tue Jun 27 18:12:49 2023 +0000, Bernhard Kölbl wrote:
Making sure the MFTs are inserted in order.
With my patch sample objects are reused, you can have some integer id per mft instance, then set attribute as id+1 on ProcessOutput(), and on ProcessInput() check for id == attrvalue. For the fact that transforms are called at all, it's enough to accumulate some stats for a number of ever returned samples. After transfer call just compare them to zero. For attribute guid please use something more neutral, for example IMFSample or IUnknown uuid.
On Tue Jun 27 18:27:27 2023 +0000, Nikolay Sivov wrote:
With my patch sample objects are reused, you can have some integer id per mft instance, then set attribute as id+1 on ProcessOutput(), and on ProcessInput() check for id == attrvalue. For the fact that transforms are called at all, it's enough to accumulate some stats for a number of ever returned samples. After transfer call just compare them to zero. For attribute guid please use something more neutral, for example IMFSample or IUnknown uuid.
Yeah instead of the id thingy I put the `is_receiver` variable since we only have 2 mfts anyway.