[PATCH v2 0/2] MR10606: mf/session: Support asynchronous transforms.
-- v2: mf/session: Support asynchronous transforms. mf/test: Add asynchronous transform tests. https://gitlab.winehq.org/wine/wine/-/merge_requests/10606
From: Conor McCarthy <cmccarthy@codeweavers.com> --- dlls/mf/tests/mf.c | 398 +++++++++++++++++++++++++++++++++++++++++++-- 1 file changed, 383 insertions(+), 15 deletions(-) diff --git a/dlls/mf/tests/mf.c b/dlls/mf/tests/mf.c index 571c8458e5c..bd02d771d0a 100644 --- a/dlls/mf/tests/mf.c +++ b/dlls/mf/tests/mf.c @@ -10224,6 +10224,8 @@ static void test_media_session_thinning(void) struct test_transform { IMFTransform IMFTransform_iface; + IMFMediaEventGenerator IMFMediaEventGenerator_iface; + IMFShutdown IMFShutdown_iface; LONG refcount; const MFT_OUTPUT_STREAM_INFO *output_stream_info; @@ -10239,13 +10241,50 @@ struct test_transform IMFSample *output; HANDLE flush_event; + + IMFAttributes *attributes; + IMFMediaEventQueue *event_queue; + BOOL async; + BOOL streaming; + BOOL pending_input; + BOOL drain; + BOOL is_shut_down; }; +#define test_transform_check_unlocked(a) test_transform_check_unlocked_(__LINE__, a) +static void test_transform_check_unlocked_(int line, struct test_transform *transform) +{ + UINT32 unlock; + HRESULT hr; + + if (!transform->async) + return; + hr = IMFAttributes_GetUINT32(transform->attributes, &MF_TRANSFORM_ASYNC_UNLOCK, &unlock); + ok_(__FILE__, line)(hr == S_OK, "Unexpected hr %#lx.\n", hr); + todo_wine + ok_(__FILE__, line)(unlock, "Transform is locked.\n"); +} + static struct test_transform *test_transform_from_IMFTransform(IMFTransform *iface) { return CONTAINING_RECORD(iface, struct test_transform, IMFTransform_iface); } +static struct test_transform *test_transform_from_IMFMediaEventGenerator(IMFMediaEventGenerator *iface) +{ + return CONTAINING_RECORD(iface, struct test_transform, IMFMediaEventGenerator_iface); +} + +static struct test_transform *test_transform_from_IMFShutdown(IMFShutdown *iface) +{ + return CONTAINING_RECORD(iface, struct test_transform, IMFShutdown_iface); +} + +static HRESULT test_transform_queue_event(struct test_transform *transform, MediaEventType type) +{ + return IMFMediaEventQueue_QueueEventParamVar(transform->event_queue, type, &GUID_NULL, S_OK, NULL); +} + static HRESULT WINAPI test_transform_QueryInterface(IMFTransform *iface, REFIID iid, void **out) { struct test_transform *transform = test_transform_from_IMFTransform(iface); @@ -10253,13 +10292,24 @@ static HRESULT WINAPI test_transform_QueryInterface(IMFTransform *iface, REFIID if (IsEqualGUID(iid, &IID_IUnknown) || IsEqualGUID(iid, &IID_IMFTransform)) { - IMFTransform_AddRef(&transform->IMFTransform_iface); *out = &transform->IMFTransform_iface; - return S_OK; + } + else if (transform->async && IsEqualIID(iid, &IID_IMFMediaEventGenerator)) + { + *out = &transform->IMFMediaEventGenerator_iface; + } + else if (transform->async && IsEqualIID(iid, &IID_IMFShutdown)) + { + *out = &transform->IMFShutdown_iface; + } + else + { + *out = NULL; + return E_NOINTERFACE; } - *out = NULL; - return E_NOINTERFACE; + IUnknown_AddRef((IUnknown *)*out); + return S_OK; } static ULONG WINAPI test_transform_AddRef(IMFTransform *iface) @@ -10281,6 +10331,13 @@ static ULONG WINAPI test_transform_Release(IMFTransform *iface) if (transform->output_type) IMFMediaType_Release(transform->output_type); CloseHandle(transform->flush_event); + if (transform->async) + { + if (!transform->is_shut_down) + IMFShutdown_Shutdown(&transform->IMFShutdown_iface); + IMFAttributes_Release(transform->attributes); + IMFMediaEventQueue_Release(transform->event_queue); + } free(transform); } @@ -10322,6 +10379,8 @@ static HRESULT WINAPI test_transform_GetOutputStreamInfo(IMFTransform *iface, DW { struct test_transform *transform = test_transform_from_IMFTransform(iface); + test_transform_check_unlocked(transform); + ok(!!transform->output_stream_info, "Unexpected %s iface %p call.\n", __func__, iface); if (!transform->output_stream_info) return E_NOTIMPL; @@ -10332,7 +10391,14 @@ static HRESULT WINAPI test_transform_GetOutputStreamInfo(IMFTransform *iface, DW static HRESULT WINAPI test_transform_GetAttributes(IMFTransform *iface, IMFAttributes **attributes) { - return E_NOTIMPL; + struct test_transform *transform = test_transform_from_IMFTransform(iface); + + if (!transform->async) + return E_NOTIMPL; + + *attributes = transform->attributes; + IMFAttributes_AddRef(*attributes); + return S_OK; } static HRESULT WINAPI test_transform_GetInputStreamAttributes(IMFTransform *iface, DWORD id, IMFAttributes **attributes) @@ -10378,6 +10444,8 @@ static HRESULT WINAPI test_transform_GetOutputAvailableType(IMFTransform *iface, { struct test_transform *transform = test_transform_from_IMFTransform(iface); + test_transform_check_unlocked(transform); + if (index >= transform->output_count) { *type = NULL; @@ -10392,6 +10460,9 @@ static HRESULT WINAPI test_transform_GetOutputAvailableType(IMFTransform *iface, static HRESULT WINAPI test_transform_SetInputType(IMFTransform *iface, DWORD id, IMFMediaType *type, DWORD flags) { struct test_transform *transform = test_transform_from_IMFTransform(iface); + + test_transform_check_unlocked(transform); + if (flags & MFT_SET_TYPE_TEST_ONLY) return S_OK; if (transform->input_type) @@ -10404,6 +10475,9 @@ static HRESULT WINAPI test_transform_SetInputType(IMFTransform *iface, DWORD id, static HRESULT WINAPI test_transform_SetOutputType(IMFTransform *iface, DWORD id, IMFMediaType *type, DWORD flags) { struct test_transform *transform = test_transform_from_IMFTransform(iface); + + test_transform_check_unlocked(transform); + if (flags & MFT_SET_TYPE_TEST_ONLY) return S_OK; if (transform->output_type) @@ -10416,6 +10490,9 @@ static HRESULT WINAPI test_transform_SetOutputType(IMFTransform *iface, DWORD id static HRESULT WINAPI test_transform_GetInputCurrentType(IMFTransform *iface, DWORD id, IMFMediaType **type) { struct test_transform *transform = test_transform_from_IMFTransform(iface); + + test_transform_check_unlocked(transform); + if (!(*type = transform->input_type)) return MF_E_TRANSFORM_TYPE_NOT_SET; IMFMediaType_AddRef(*type); @@ -10425,6 +10502,9 @@ static HRESULT WINAPI test_transform_GetInputCurrentType(IMFTransform *iface, DW static HRESULT WINAPI test_transform_GetOutputCurrentType(IMFTransform *iface, DWORD id, IMFMediaType **type) { struct test_transform *transform = test_transform_from_IMFTransform(iface); + + test_transform_check_unlocked(transform); + if (!(*type = transform->output_type)) return MF_E_TRANSFORM_TYPE_NOT_SET; IMFMediaType_AddRef(*type); @@ -10458,11 +10538,15 @@ static HRESULT WINAPI test_transform_ProcessEvent(IMFTransform *iface, DWORD id, DEFINE_EXPECT(test_transform_ProcessMessage_BEGIN_STREAMING); DEFINE_EXPECT(test_transform_ProcessMessage_START_OF_STREAM); DEFINE_EXPECT(test_transform_ProcessMessage_FLUSH); +DEFINE_EXPECT(test_transform_ProcessInput); +DEFINE_EXPECT(test_transform_ProcessOutput); static HRESULT WINAPI test_transform_ProcessMessage(IMFTransform *iface, MFT_MESSAGE_TYPE message, ULONG_PTR param) { struct test_transform *transform = test_transform_from_IMFTransform(iface); + test_transform_check_unlocked(transform); + switch (message) { case MFT_MESSAGE_NOTIFY_BEGIN_STREAMING: @@ -10473,31 +10557,59 @@ static HRESULT WINAPI test_transform_ProcessMessage(IMFTransform *iface, MFT_MES case MFT_MESSAGE_NOTIFY_START_OF_STREAM: CHECK_EXPECT(test_transform_ProcessMessage_START_OF_STREAM); add_object_state(&actual_object_state_record, MFT_START); + if (transform->async) + { + transform->streaming = TRUE; + transform->pending_input = TRUE; + SET_EXPECT(test_transform_ProcessInput); + return test_transform_queue_event(transform, METransformNeedInput); + } return S_OK; case MFT_MESSAGE_COMMAND_FLUSH: + if (transform->output) + { + IMFSample_Release(transform->output); + transform->output = NULL; + } SetEvent(transform->flush_event); CHECK_EXPECT2(test_transform_ProcessMessage_FLUSH); add_object_state(&actual_object_state_record, MFT_FLUSH); return S_OK; + case MFT_MESSAGE_COMMAND_DRAIN: + if (transform->async) + { + if (!transform->pending_input && !transform->output) + return test_transform_queue_event(transform, METransformDrainComplete); + transform->drain = TRUE; + return S_OK; + } + break; + + case MFT_MESSAGE_NOTIFY_END_STREAMING: + if (transform->async) + return S_OK; + break; + default: - ok(0, "Unexpected %s call %#x.\n", __func__, message); - return E_NOTIMPL; + break; } -} -DEFINE_EXPECT(test_transform_ProcessInput); -DEFINE_EXPECT(test_transform_ProcessOutput); + ok(0, "Unexpected %s call %#x.\n", __func__, message); + return E_NOTIMPL; +} static HRESULT WINAPI test_transform_ProcessInput(IMFTransform *iface, DWORD id, IMFSample *sample, DWORD flags) { struct test_transform *transform = test_transform_from_IMFTransform(iface); HRESULT hr; + test_transform_check_unlocked(transform); + if (expect_test_transform_ProcessInput) { - if (transform->output) + if (transform->output || (transform->async && !transform->pending_input)) { hr = MF_E_NOTACCEPTING; } @@ -10505,6 +10617,12 @@ static HRESULT WINAPI test_transform_ProcessInput(IMFTransform *iface, DWORD id, { IMFSample_AddRef(transform->output = sample); hr = S_OK; + + if (transform->async) + { + transform->pending_input = FALSE; + hr = test_transform_queue_event(transform, METransformHaveOutput); + } } } else @@ -10524,6 +10642,8 @@ static HRESULT WINAPI test_transform_ProcessOutput(IMFTransform *iface, DWORD fl struct test_transform *transform = test_transform_from_IMFTransform(iface); HRESULT hr; + test_transform_check_unlocked(transform); + if (expect_test_transform_ProcessOutput) { if (transform->output) @@ -10532,10 +10652,25 @@ static HRESULT WINAPI test_transform_ProcessOutput(IMFTransform *iface, DWORD fl data->pSample = transform->output; transform->output = NULL; hr = S_OK; + + if (transform->async) + { + if (transform->drain) + { + hr = test_transform_queue_event(transform, METransformDrainComplete); + transform->drain = FALSE; + } + else if (transform->streaming) + { + transform->pending_input = TRUE; + SET_EXPECT(test_transform_ProcessInput); + hr = test_transform_queue_event(transform, METransformNeedInput); + } + } } else { - hr = MF_E_TRANSFORM_NEED_MORE_INPUT; + hr = transform->async ? E_UNEXPECTED : MF_E_TRANSFORM_NEED_MORE_INPUT; } } else @@ -10579,14 +10714,125 @@ static const IMFTransformVtbl test_transform_vtbl = test_transform_ProcessOutput, }; +static HRESULT WINAPI test_transform_events_QueryInterface(IMFMediaEventGenerator *iface, REFIID iid, void **out) +{ + struct test_transform *transform = test_transform_from_IMFMediaEventGenerator(iface); + return IMFTransform_QueryInterface(&transform->IMFTransform_iface, iid, out); +} + +static ULONG WINAPI test_transform_events_AddRef(IMFMediaEventGenerator *iface) +{ + struct test_transform *transform = test_transform_from_IMFMediaEventGenerator(iface); + return IMFTransform_AddRef(&transform->IMFTransform_iface); +} + +static ULONG WINAPI test_transform_events_Release(IMFMediaEventGenerator *iface) +{ + struct test_transform *transform = test_transform_from_IMFMediaEventGenerator(iface); + return IMFTransform_Release(&transform->IMFTransform_iface); +} + +static HRESULT WINAPI test_transform_events_GetEvent(IMFMediaEventGenerator *iface, DWORD flags, IMFMediaEvent **event) +{ + struct test_transform *transform = test_transform_from_IMFMediaEventGenerator(iface); + return IMFMediaEventQueue_GetEvent(transform->event_queue, flags, event); +} + +static HRESULT WINAPI test_transform_events_BeginGetEvent(IMFMediaEventGenerator *iface, IMFAsyncCallback *callback, + IUnknown *state) +{ + struct test_transform *transform = test_transform_from_IMFMediaEventGenerator(iface); + return IMFMediaEventQueue_BeginGetEvent(transform->event_queue, callback, state); +} + +static HRESULT WINAPI test_transform_events_EndGetEvent(IMFMediaEventGenerator *iface, IMFAsyncResult *result, + IMFMediaEvent **event) +{ + struct test_transform *transform = test_transform_from_IMFMediaEventGenerator(iface); + return IMFMediaEventQueue_EndGetEvent(transform->event_queue, result, event); +} + +static HRESULT WINAPI test_transform_events_QueueEvent(IMFMediaEventGenerator *iface, MediaEventType event_type, + REFGUID ext_type, HRESULT hr, const PROPVARIANT *value) +{ + struct test_transform *transform = test_transform_from_IMFMediaEventGenerator(iface); + return IMFMediaEventQueue_QueueEventParamVar(transform->event_queue, event_type, ext_type, hr, value); +} + +static const IMFMediaEventGeneratorVtbl test_transform_events_vtbl = +{ + test_transform_events_QueryInterface, + test_transform_events_AddRef, + test_transform_events_Release, + test_transform_events_GetEvent, + test_transform_events_BeginGetEvent, + test_transform_events_EndGetEvent, + test_transform_events_QueueEvent, +}; + +static HRESULT WINAPI test_transform_shutdown_QueryInterface(IMFShutdown *iface, REFIID iid, void **out) +{ + struct test_transform *transform = test_transform_from_IMFShutdown(iface); + return IMFTransform_QueryInterface(&transform->IMFTransform_iface, iid, out); +} + +static ULONG WINAPI test_transform_shutdown_AddRef(IMFShutdown *iface) +{ + struct test_transform *transform = test_transform_from_IMFShutdown(iface); + return IMFTransform_AddRef(&transform->IMFTransform_iface); +} + +static ULONG WINAPI test_transform_shutdown_Release(IMFShutdown *iface) +{ + struct test_transform *transform = test_transform_from_IMFShutdown(iface); + return IMFTransform_Release(&transform->IMFTransform_iface); +} + +static HRESULT WINAPI test_transform_shutdown_Shutdown(IMFShutdown *iface) +{ + struct test_transform *transform = test_transform_from_IMFShutdown(iface); + + IMFMediaEventQueue_Shutdown(transform->event_queue); + transform->is_shut_down = TRUE; + + return S_OK; +} + +static HRESULT WINAPI test_transform_shutdown_GetShutdownStatus(IMFShutdown *iface, MFSHUTDOWN_STATUS *status) +{ + struct test_transform *transform = test_transform_from_IMFShutdown(iface); + HRESULT hr = S_OK; + + if (!status) + return E_INVALIDARG; + + if (transform->is_shut_down) + *status = MFSHUTDOWN_COMPLETED; + else + hr = MF_E_INVALIDREQUEST; + + return hr; +} + +static const IMFShutdownVtbl test_transform_shutdown_vtbl = +{ + test_transform_shutdown_QueryInterface, + test_transform_shutdown_AddRef, + test_transform_shutdown_Release, + test_transform_shutdown_Shutdown, + test_transform_shutdown_GetShutdownStatus, +}; + static HRESULT WINAPI test_transform_create(UINT input_count, IMFMediaType **input_types, - UINT output_count, IMFMediaType **output_types, BOOL d3d_aware, IMFTransform **out) + UINT output_count, IMFMediaType **output_types, BOOL d3d_aware, BOOL async, IMFTransform **out) { struct test_transform *transform; if (!(transform = calloc(1, sizeof(*transform)))) return E_OUTOFMEMORY; transform->IMFTransform_iface.lpVtbl = &test_transform_vtbl; + transform->IMFMediaEventGenerator_iface.lpVtbl = async ? &test_transform_events_vtbl : NULL; + transform->IMFShutdown_iface.lpVtbl = async ? &test_transform_shutdown_vtbl : NULL; transform->refcount = 1; transform->input_count = input_count; @@ -10600,6 +10846,20 @@ static HRESULT WINAPI test_transform_create(UINT input_count, IMFMediaType **inp transform->flush_event = CreateEventW(NULL, FALSE, FALSE, NULL); ok(!!transform->flush_event, "CreateEventW failed, error %lu\n", GetLastError()); + if ((transform->async = async)) + { + HRESULT hr = MFCreateAttributes(&transform->attributes, 3); + ok(hr == S_OK, "Unexpected hr %#lx.\n", hr); + hr = IMFAttributes_SetUINT32(transform->attributes, &MF_TRANSFORM_ASYNC, TRUE); + ok(hr == S_OK, "Unexpected hr %#lx.\n", hr); + hr = IMFAttributes_SetUINT32(transform->attributes, &MF_TRANSFORM_ASYNC_UNLOCK, FALSE); + ok(hr == S_OK, "Unexpected hr %#lx.\n", hr); + hr = IMFAttributes_SetUINT32(transform->attributes, &MFT_SUPPORT_DYNAMIC_FORMAT_CHANGE, TRUE); + ok(hr == S_OK, "Unexpected hr %#lx.\n", hr); + hr = MFCreateEventQueue(&transform->event_queue); + ok(hr == S_OK, "Unexpected hr %#lx.\n", hr); + } + *out = &transform->IMFTransform_iface; return S_OK; } @@ -10661,7 +10921,7 @@ static void test_media_session_seek(void) hr = IMFMediaType_SetUINT32(type, &MF_MT_ALL_SAMPLES_INDEPENDENT, TRUE); ok(hr == S_OK, "Unexpected hr %#lx.\n", hr); mft = NULL; - hr = test_transform_create(1, &type, 1, &type, FALSE, &mft); + hr = test_transform_create(1, &type, 1, &type, FALSE, FALSE, &mft); ok(hr == S_OK, "Unexpected hr %#lx.\n", hr); test_transform_set_output_stream_info(mft, &output_stream_info); @@ -10968,7 +11228,7 @@ static void test_media_session_scrubbing(void) hr = IMFMediaType_SetUINT64(type, &MF_MT_FRAME_SIZE, (UINT64)640 << 32 | 480); ok(hr == S_OK, "Unexpected hr %#lx.\n", hr); mft = NULL; - hr = test_transform_create(1, &type, 1, &type, FALSE, &mft); + hr = test_transform_create(1, &type, 1, &type, FALSE, FALSE, &mft); ok(hr == S_OK, "Unexpected hr %#lx.\n", hr); transform = test_transform_from_IMFTransform(mft); test_transform_set_output_stream_info(mft, &output_stream_info); @@ -11554,6 +11814,113 @@ done: ok(hr == S_OK, "Unexpected hr %#lx.\n", hr); } +static void test_async_transform(void) +{ + media_type_desc video_nv12_desc = + { + ATTR_GUID(MF_MT_MAJOR_TYPE, MFMediaType_Video), + ATTR_GUID(MF_MT_SUBTYPE, MFVideoFormat_NV12), + }; + MFT_OUTPUT_STREAM_INFO output_stream_info = {0}; + struct test_grabber_callback *grabber_callback; + struct test_transform *transform; + IMFActivate *sink_activate; + IMFAsyncCallback *callback; + IMFMediaType *output_type; + PROPVARIANT propvar = {0}; + IMFMediaSession *session; + IMFTransform *mft = NULL; + IMFMediaSource *source; + IMFTopology *topology; + HRESULT hr; + DWORD ret; + UINT i; + + hr = MFStartup(MF_VERSION, MFSTARTUP_FULL); + ok(hr == S_OK, "Failed to start up, hr %#lx.\n", hr); + + if (!(source = create_media_source(L"test.mp4", L"video/mp4"))) + { + todo_wine /* Gitlab CI Debian runner */ + win_skip("MP4 media source is not supported, skipping tests.\n"); + MFShutdown(); + return; + } + + grabber_callback = impl_from_IMFSampleGrabberSinkCallback(create_test_grabber_callback()); + grabber_callback->ready_event = CreateEventW(NULL, FALSE, FALSE, NULL); + ok(!!grabber_callback->ready_event, "CreateEventW failed, error %lu\n", GetLastError()); + + hr = MFCreateMediaType(&output_type); + ok(hr == S_OK, "Unexpected hr %#lx.\n", hr); + init_media_type(output_type, video_nv12_desc, -1); + hr = MFCreateSampleGrabberSinkActivate(output_type, &grabber_callback->IMFSampleGrabberSinkCallback_iface, &sink_activate); + ok(hr == S_OK, "Failed to create grabber sink, hr %#lx.\n", hr); + + memset(&actual_object_state_record, 0, sizeof(actual_object_state_record)); + SET_EXPECT(test_transform_ProcessMessage_START_OF_STREAM); + SET_EXPECT(test_transform_ProcessMessage_BEGIN_STREAMING); + SET_EXPECT(test_transform_ProcessOutput); + SET_EXPECT(test_transform_ProcessMessage_FLUSH); + + hr = test_transform_create(1, &output_type, 1, &output_type, FALSE, TRUE, &mft); + ok(hr == S_OK, "Unexpected hr %#lx.\n", hr); + test_transform_set_output_stream_info(mft, &output_stream_info); + + hr = MFCreateMediaSession(NULL, &session); + ok(hr == S_OK, "Unexpected hr %#lx.\n", hr); + + topology = create_test_topology_unk(source, (IUnknown *)sink_activate, (IUnknown *)mft, NULL); + hr = IMFMediaSession_SetTopology(session, 0, topology); + ok(hr == S_OK, "Unexpected hr %#lx.\n", hr); + IMFTopology_Release(topology); + IMFActivate_Release(sink_activate); + + callback = create_test_callback(TRUE); + propvar.vt = VT_EMPTY; + hr = IMFMediaSession_Start(session, &GUID_NULL, &propvar); + ok(hr == S_OK, "Unexpected hr %#lx.\n", hr); + hr = wait_media_event(session, callback, MESessionStarted, 1000, &propvar); + + /* Check for repeated sample delivery */ + for (i = 0, ret = WAIT_OBJECT_0; i < 10 && ret == WAIT_OBJECT_0; ++i) + { + ret = WaitForSingleObject(grabber_callback->ready_event, 1000); + todo_wine + ok(ret == WAIT_OBJECT_0, "WaitForSingleObject returned %lu\n", ret); + } + + transform = test_transform_from_IMFTransform(mft); + ok(transform->streaming, "Transform is not streaming.\n"); + + hr = IMFMediaSession_Stop(session); + ok(hr == S_OK, "Unexpected hr %#lx.\n", hr); + hr = IMFMediaSession_Close(session); + ok(hr == S_OK, "Unexpected hr %#lx.\n", hr); + hr = wait_media_event(session, callback, MESessionClosed, 1000, &propvar); + ok(hr == S_OK, "Unexpected hr %#lx.\n", hr); + + ok(!transform->is_shut_down, "Transform was shut down.\n"); + + hr = IMFMediaSource_Shutdown(source); + ok(hr == S_OK, "Unexpected hr %#lx.\n", hr); + hr = IMFMediaSession_Shutdown(session); + ok(hr == S_OK, "Unexpected hr %#lx.\n", hr); + + todo_wine + ok(transform->is_shut_down, "Transform was not shut down.\n"); + + IMFTransform_Release(mft); + IMFMediaType_Release(output_type); + IMFAsyncCallback_Release(callback); + IMFMediaSession_Release(session); + IMFSampleGrabberSinkCallback_Release(&grabber_callback->IMFSampleGrabberSinkCallback_iface); + IMFMediaSource_Release(source); + + hr = MFShutdown(); + ok(hr == S_OK, "Unexpected hr %#lx.\n", hr); +} + START_TEST(mf) { init_functions(); @@ -11599,4 +11966,5 @@ START_TEST(mf) test_media_session_sample_request(); test_media_session_invalid_topology(); test_media_session_sink_shutdown(); + test_async_transform(); } -- GitLab https://gitlab.winehq.org/wine/wine/-/merge_requests/10606
From: Conor McCarthy <cmccarthy@codeweavers.com> --- dlls/mf/mf_private.h | 1 + dlls/mf/session.c | 122 +++++++++++++++++++++++++++++++++++--- dlls/mf/tests/mf.c | 3 - dlls/mf/topology_loader.c | 32 ++++++++++ 4 files changed, 147 insertions(+), 11 deletions(-) diff --git a/dlls/mf/mf_private.h b/dlls/mf/mf_private.h index 0658db1de15..ad8d730db18 100644 --- a/dlls/mf/mf_private.h +++ b/dlls/mf/mf_private.h @@ -127,6 +127,7 @@ extern HRESULT topology_node_get_type_handler(IMFTopologyNode *node, DWORD strea extern HRESULT topology_node_init_media_type(IMFTopologyNode *node, DWORD stream, BOOL output, IMFMediaType **type); extern BOOL topology_node_is_d3d_aware(IMFTopologyNode *node); extern HRESULT topology_node_set_device_manager(IMFTopologyNode *node, IUnknown *device_manager); +extern IMFAttributes *topology_node_transform_async_get_attributes(IMFTopologyNode *node); extern HRESULT stream_sink_get_device_manager(IMFStreamSink *stream_sink, IUnknown **device_manager); extern HRESULT enum_audio_capture_sources(IMFAttributes *attributes, IMFActivate ***sources, UINT32 *ret_count); diff --git a/dlls/mf/session.c b/dlls/mf/session.c index bf01477316b..fc7be775d3a 100644 --- a/dlls/mf/session.c +++ b/dlls/mf/session.c @@ -227,6 +227,9 @@ struct topo_node struct transform_stream *outputs; DWORD *output_map; unsigned int output_count; + + BOOL async; + IMFMediaEventGenerator *event_source; } transform; } u; }; @@ -800,6 +803,8 @@ static void release_topo_node(struct topo_node *node) free(node->u.transform.outputs); free(node->u.transform.input_map); free(node->u.transform.output_map); + if (node->u.transform.event_source) + IMFMediaEventGenerator_Release(node->u.transform.event_source); break; case MF_TOPOLOGY_OUTPUT_NODE: if (node->u.sink.allocator) @@ -824,17 +829,18 @@ static void release_topo_node(struct topo_node *node) static void topology_shutdown(IMFTopology *topology) { IMFStreamSink *stream_sink; + IMFAttributes *attributes; IMFTopologyNode *node; IMFActivate *activate; IMFMediaSink *sink; WORD idx = 0; HRESULT hr; - /* FIXME: should handle async MFTs, but these are not supported by the rest of the pipeline currently. */ - while (SUCCEEDED(IMFTopology_GetNode(topology, idx++, &node))) { - if (topology_node_get_type(node) == MF_TOPOLOGY_OUTPUT_NODE) + MF_TOPOLOGY_TYPE node_type = topology_node_get_type(node); + + if (node_type == MF_TOPOLOGY_OUTPUT_NODE) { /* MF_TOPONODE_NOSHUTDOWN_ON_REMOVE is ignored, at least for sinks. */ @@ -856,6 +862,21 @@ static void topology_shutdown(IMFTopology *topology) IMFStreamSink_Release(stream_sink); } } + else if (node_type == MF_TOPOLOGY_TRANSFORM_NODE + && (attributes = topology_node_transform_async_get_attributes(node))) + { + IMFShutdown *shutdown; + if (SUCCEEDED(hr = topology_node_get_object(node, &IID_IMFShutdown, (void **)&shutdown))) + { + IMFShutdown_Shutdown(shutdown); + IMFShutdown_Release(shutdown); + } + else + { + WARN("Failed to get shutdown interface, hr %#lx.\n", hr); + } + IMFAttributes_Release(attributes); + } IMFTopologyNode_Release(node); } @@ -1754,6 +1775,7 @@ static HRESULT session_set_transform_stream_info(struct topo_node *node) struct transform_stream *streams; UINT32 bytes_per_second, value; unsigned int block_alignment; + IMFAttributes *attributes; IMFMediaType *media_type; HRESULT hr; @@ -1783,6 +1805,17 @@ static HRESULT session_set_transform_stream_info(struct topo_node *node) node->u.transform.inputs = streams; node->u.transform.input_count = input_count; + if ((attributes = topology_node_transform_async_get_attributes(node->node))) + { + IMFAttributes_SetUINT32(attributes, &MF_TRANSFORM_ASYNC_UNLOCK, TRUE); + IMFAttributes_Release(attributes); + + node->u.transform.async = TRUE; + if (FAILED(hr = IMFTransform_QueryInterface(node->object.transform, &IID_IMFMediaEventGenerator, + (void **)&node->u.transform.event_source))) + WARN("Failed to get event source, hr %#lx.\n", hr); + } + streams = calloc(output_count, sizeof(*streams)); for (i = 0; i < output_count; ++i) { @@ -3234,7 +3267,12 @@ static void session_set_source_object_state(struct media_session *session, IUnkn LIST_FOR_EACH_ENTRY(node, &session->presentation.nodes, struct topo_node, entry) { if (node->type == MF_TOPOLOGY_TRANSFORM_NODE) + { + if (node->u.transform.async && FAILED(hr = IMFMediaEventGenerator_BeginGetEvent(node->u.transform.event_source, + &session->events_callback, (IUnknown *)node->u.transform.event_source))) + WARN("Failed to subscribe to transform events, hr %#lx.\n", hr); IMFTransform_ProcessMessage(node->object.transform, MFT_MESSAGE_NOTIFY_START_OF_STREAM, 0); + } } } @@ -3873,7 +3911,8 @@ static HRESULT transform_node_handle_format_change(struct media_session *session IMFAttributes_Release(attributes); } - if (!support_dynamic_format_change) + /* all async MFTs must support dynamic format change, but async is checked here in case a bugged MFT doesn't set the flag */ + if (!support_dynamic_format_change && !topo_node->u.transform.async) { if (SUCCEEDED(hr = IMFTransform_ProcessMessage(transform, MFT_MESSAGE_COMMAND_DRAIN, id))) { @@ -3986,6 +4025,8 @@ static void transform_node_deliver_samples(struct media_session *session, struct if (FAILED(hr = transform_stream_pop_event(stream, &event))) { + if (topo_node->u.transform.async) + break; /* try getting more samples by calling IMFTransform_ProcessOutput */ if (FAILED(hr = transform_node_pull_samples(session, topo_node))) break; @@ -4007,7 +4048,7 @@ static void transform_node_deliver_samples(struct media_session *session, struct } } - if (hr == MF_E_TRANSFORM_NEED_MORE_INPUT && transform_node_has_requests(topo_node)) + if (!topo_node->u.transform.async && hr == MF_E_TRANSFORM_NEED_MORE_INPUT && transform_node_has_requests(topo_node)) { struct transform_stream *stream; @@ -4062,8 +4103,11 @@ static void session_deliver_sample_to_node(struct media_session *session, struct case MF_TOPOLOGY_TRANSFORM_NODE: if (FAILED(hr = transform_node_push_sample(session, topo_node, input, sample))) WARN("Failed to push or queue sample to transform, hr %#lx\n", hr); - transform_node_pull_samples(session, topo_node); - transform_node_deliver_samples(session, topo_node); + if (!topo_node->u.transform.async) + { + transform_node_pull_samples(session, topo_node); + transform_node_deliver_samples(session, topo_node); + } break; case MF_TOPOLOGY_TEE_NODE: FIXME("Unhandled downstream node type %d.\n", topo_node->type); @@ -4116,7 +4160,7 @@ static HRESULT session_request_sample_from_node(struct media_session *session, s ERR("Failed to handle stream event, hr %#lx\n", hr); IMFMediaEvent_Release(event); } - else if (transform_node_has_requests(topo_node)) + else if (transform_node_has_requests(topo_node) || topo_node->u.transform.async) { /* there's already requests pending, just increase the counter */ stream->requests++; @@ -4379,6 +4423,20 @@ static void session_sink_stream_scrub_complete(struct media_session *session, IM } } +static struct topo_node *session_get_async_transform_node(const struct media_session *session, + IMFMediaEventGenerator *event_source) +{ + struct topo_node *node; + + LIST_FOR_EACH_ENTRY(node, &session->presentation.nodes, struct topo_node, entry) + { + if (node->type == MF_TOPOLOGY_TRANSFORM_NODE && node->u.transform.event_source == event_source) + return node; + } + FIXME("Failed to get transform.\n"); + return NULL; +} + static HRESULT WINAPI session_events_callback_Invoke(IMFAsyncCallback *iface, IMFAsyncResult *result) { struct media_session *session = impl_from_events_callback_IMFAsyncCallback(iface); @@ -4593,6 +4651,54 @@ static HRESULT WINAPI session_events_callback_Invoke(IMFAsyncCallback *iface, IM break; + case METransformNeedInput: + { + struct topo_node *up_node, *topo_node = session_get_async_transform_node(session, event_source); + struct transform_stream *stream; + IMFMediaEvent *stream_event; + UINT32 input = 0; + BOOL is_sample; + DWORD output; + + if (FAILED(hr = IMFMediaEvent_GetUINT32(event, &MF_EVENT_MFT_INPUT_STREAM_ID, &input))) + WARN("Failed to get input id, hr %#lx.\n", hr); + + stream = &topo_node->u.transform.inputs[input]; + + for (is_sample = FALSE; !is_sample && SUCCEEDED(hr); ) + { + if (SUCCEEDED(hr = transform_stream_pop_event(stream, &stream_event))) + { + MediaEventType event_type; + if (FAILED(hr = transform_stream_handle_event(session, stream, topo_node, input, stream_event))) + ERR("Failed to handle stream event, hr %#lx\n", hr); + is_sample = SUCCEEDED(hr = IMFMediaEvent_GetType(stream_event, &event_type)) && event_type == MEMediaSample; + IMFMediaEvent_Release(stream_event); + } + } + + if (is_sample) + break; + + if (!(up_node = session_get_topo_node_input(session, topo_node, input, &output))) + WARN("Failed to get node %p/%u input\n", topo_node, input); + else if (FAILED(hr = session_request_sample_from_node(session, up_node, output))) + WARN("Failed to request sample from upstream node %p/%lu, hr %#lx\n", up_node, output, hr); + break; + } + + case METransformHaveOutput: + { + struct topo_node *topo_node = session_get_async_transform_node(session, event_source); + + if (topo_node) + { + transform_node_pull_samples(session, topo_node); + transform_node_deliver_samples(session, topo_node); + } + break; + } + case MEError: /* Wine-specific extension */ EnterCriticalSection(&session->cs); diff --git a/dlls/mf/tests/mf.c b/dlls/mf/tests/mf.c index bd02d771d0a..00406196e05 100644 --- a/dlls/mf/tests/mf.c +++ b/dlls/mf/tests/mf.c @@ -10261,7 +10261,6 @@ static void test_transform_check_unlocked_(int line, struct test_transform *tran return; hr = IMFAttributes_GetUINT32(transform->attributes, &MF_TRANSFORM_ASYNC_UNLOCK, &unlock); ok_(__FILE__, line)(hr == S_OK, "Unexpected hr %#lx.\n", hr); - todo_wine ok_(__FILE__, line)(unlock, "Transform is locked.\n"); } @@ -11886,7 +11885,6 @@ static void test_async_transform(void) for (i = 0, ret = WAIT_OBJECT_0; i < 10 && ret == WAIT_OBJECT_0; ++i) { ret = WaitForSingleObject(grabber_callback->ready_event, 1000); - todo_wine ok(ret == WAIT_OBJECT_0, "WaitForSingleObject returned %lu\n", ret); } @@ -11907,7 +11905,6 @@ static void test_async_transform(void) hr = IMFMediaSession_Shutdown(session); ok(hr == S_OK, "Unexpected hr %#lx.\n", hr); - todo_wine ok(transform->is_shut_down, "Transform was not shut down.\n"); IMFTransform_Release(mft); diff --git a/dlls/mf/topology_loader.c b/dlls/mf/topology_loader.c index 08557d354a8..05d00c3782c 100644 --- a/dlls/mf/topology_loader.c +++ b/dlls/mf/topology_loader.c @@ -806,6 +806,32 @@ BOOL topology_node_is_d3d_aware(IMFTopologyNode *node) return d3d_aware || d3d11_aware; } +IMFAttributes *topology_node_transform_async_get_attributes(IMFTopologyNode *node) +{ + IMFAttributes *attributes; + + if (SUCCEEDED(topology_node_get_object_attributes(node, &attributes))) + { + UINT32 async; + if (SUCCEEDED(IMFAttributes_GetUINT32(attributes, &MF_TRANSFORM_ASYNC, &async)) && async) + return attributes; + IMFAttributes_Release(attributes); + } + + return NULL; +} + +static void topology_node_transform_unlock_async(IMFTopologyNode *node) +{ + IMFAttributes *attributes; + + if ((attributes = topology_node_transform_async_get_attributes(node))) + { + IMFAttributes_SetUINT32(attributes, &MF_TRANSFORM_ASYNC_UNLOCK, TRUE); + IMFAttributes_Release(attributes); + } +} + static HRESULT topology_loader_create_copier(IMFTopologyNode *upstream_node, DWORD upstream_output, IMFTopologyNode *downstream_node, unsigned int downstream_input, IMFTransform **copier) { @@ -1061,6 +1087,12 @@ static HRESULT WINAPI topology_loader_Load(IMFTopoLoader *iface, IMFTopology *in for (i = 0; SUCCEEDED(IMFTopology_GetNode(input_topology, i, &node)); i++) { + MF_TOPOLOGY_TYPE type; + + /* unlock transforms now to enable querying of types */ + if (SUCCEEDED(IMFTopologyNode_GetNodeType(node, &type)) && type == MF_TOPOLOGY_TRANSFORM_NODE) + topology_node_transform_unlock_async(node); + hr = topology_node_list_branches(node, &branches); IMFTopologyNode_Release(node); if (FAILED(hr)) -- GitLab https://gitlab.winehq.org/wine/wine/-/merge_requests/10606
Rebased, and added todos to the test commit. Not sure how those ended up missing. -- https://gitlab.winehq.org/wine/wine/-/merge_requests/10606#note_138299
participants (2)
-
Conor McCarthy -
Conor McCarthy (@cmccarthy)