From: Conor McCarthy <cmccarthy@codeweavers.com> --- dlls/mf/mf_private.h | 1 + dlls/mf/session.c | 122 +++++++++++++++++++++++++++++++++++--- dlls/mf/tests/mf.c | 1 - dlls/mf/topology_loader.c | 32 ++++++++++ 4 files changed, 147 insertions(+), 9 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 db837c369d7..927ab072bc6 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) @@ -825,6 +830,7 @@ static void session_shutdown_current_topology(struct media_session *session) { unsigned int shutdown, force_shutdown; IMFStreamSink *stream_sink; + IMFAttributes *attributes; IMFTopology *topology; IMFTopologyNode *node; IMFActivate *activate; @@ -835,11 +841,11 @@ static void session_shutdown_current_topology(struct media_session *session) topology = session->presentation.current_topology; force_shutdown = session->state == SESSION_STATE_SHUT_DOWN; - /* 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) { shutdown = 1; IMFTopologyNode_GetUINT32(node, &MF_TOPONODE_NOSHUTDOWN_ON_REMOVE, &shutdown); @@ -865,6 +871,21 @@ static void session_shutdown_current_topology(struct media_session *session) } } } + 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); } @@ -1752,6 +1773,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; @@ -1781,6 +1803,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) { @@ -3233,7 +3266,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); + } } } @@ -3859,7 +3897,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))) { @@ -3972,6 +4011,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; @@ -3993,7 +4034,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; @@ -4048,8 +4089,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); @@ -4102,7 +4146,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++; @@ -4365,6 +4409,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); @@ -4579,6 +4637,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 1c0b8424576..af2f6f86da0 100644 --- a/dlls/mf/tests/mf.c +++ b/dlls/mf/tests/mf.c @@ -10227,7 +10227,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"); } diff --git a/dlls/mf/topology_loader.c b/dlls/mf/topology_loader.c index 9cf4ee0b28c..b18119b7216 100644 --- a/dlls/mf/topology_loader.c +++ b/dlls/mf/topology_loader.c @@ -804,6 +804,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) { @@ -1059,6 +1085,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