[PATCH 0/1] MR2684: mf: Request more samples for transforms when needed.
This avoids stalling the pipeline when upstream nodes have not given enough samples for processing. If there are multiple upstream nodes, request samples in a round-robin fashion. This fixes an issue where the intro audio of the game Airborne Kingdom stops playing after a few seconds. -- https://gitlab.winehq.org/wine/wine/-/merge_requests/2684
From: Shaun Ren <sren(a)codeweavers.com> If there are multiple upstream nodes, request samples in a round-robin fashion. --- dlls/mf/session.c | 57 ++++++++++++++++++++++++++++++++++++++--------- 1 file changed, 47 insertions(+), 10 deletions(-) diff --git a/dlls/mf/session.c b/dlls/mf/session.c index 7d6a65d3f55..c3096a47f55 100644 --- a/dlls/mf/session.c +++ b/dlls/mf/session.c @@ -198,6 +198,8 @@ struct topo_node { struct transform_stream *inputs; DWORD *input_map; + BOOL *input_full; + unsigned int input_next; unsigned int input_count; struct transform_stream *outputs; @@ -707,6 +709,7 @@ 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); + free(node->u.transform.input_full); break; case MF_TOPOLOGY_OUTPUT_NODE: if (node->u.sink.allocator) @@ -1512,6 +1515,8 @@ static HRESULT session_set_transform_stream_info(struct topo_node *node) { node->u.transform.input_map = input_map; node->u.transform.output_map = output_map; + node->u.transform.input_full = calloc(input_count, sizeof(BOOL)); + node->u.transform.input_next = 0; streams = calloc(input_count, sizeof(*streams)); for (i = 0; i < input_count; ++i) @@ -3122,6 +3127,9 @@ static HRESULT transform_node_pull_samples(const struct media_session *session, if (SUCCEEDED(hr)) hr = IMFTransform_ProcessOutput(node->object.transform, 0, node->u.transform.output_count, buffers, &status); + if (SUCCEEDED(hr)) + memset(node->u.transform.input_full, 0, sizeof(BOOL) * node->u.transform.input_count); + /* Collect returned samples for all streams. */ for (i = 0; i < node->u.transform.output_count; ++i) { @@ -3146,6 +3154,30 @@ static HRESULT transform_node_pull_samples(const struct media_session *session, return hr; } +static HRESULT transform_node_request_sample(struct media_session *session, struct topo_node *node) +{ + unsigned int count = node->u.transform.input_count; + IMFTopologyNode *upstream_node; + DWORD upstream_output; + unsigned int i, cur; + HRESULT hr; + + /* In case of multiple inputs, request samples from upstream in a round-robin fashion. */ + for (i = 0; i < count && node->u.transform.input_full[(node->u.transform.input_next + i) % count]; ++i) ; + if (i == count) + return MF_E_NOTACCEPTING; + + cur = (node->u.transform.input_next + i) % count; + if (SUCCEEDED(hr = IMFTopologyNode_GetInput(node->node, cur, &upstream_node, &upstream_output))) + { + if (SUCCEEDED(hr = session_request_sample_from_node(session, upstream_node, upstream_output))) + node->u.transform.input_next = (cur + 1) % count; + IMFTopologyNode_Release(upstream_node); + } + + return hr; +} + static void session_deliver_sample_to_node(struct media_session *session, IMFTopologyNode *node, unsigned int input, IMFSample *sample) { @@ -3154,7 +3186,7 @@ static void session_deliver_sample_to_node(struct media_session *session, IMFTop IMFTopologyNode *downstream_node; struct topo_node *topo_node; MF_TOPOLOGY_TYPE node_type; - BOOL drain = FALSE; + BOOL drain = FALSE, request = FALSE; TOPOID node_id; unsigned int i; HRESULT hr; @@ -3202,7 +3234,10 @@ static void session_deliver_sample_to_node(struct media_session *session, IMFTop { if ((hr = IMFTransform_ProcessInput(topo_node->object.transform, stream_id, sample_entry->sample, 0)) == MF_E_NOTACCEPTING) + { + topo_node->u.transform.input_full[i] = TRUE; break; + } if (FAILED(hr)) WARN("Failed to process input for stream %u/%lu, hr %#lx.\n", i, stream_id, hr); transform_release_sample(sample_entry); @@ -3254,9 +3289,15 @@ static void session_deliver_sample_to_node(struct media_session *session, IMFTop transform_release_sample(sample_entry); } + if (topo_node->u.transform.outputs[i].requests > 0) + request = TRUE; + IMFTopologyNode_Release(downstream_node); } + if (request && !drain) + transform_node_request_sample(session, topo_node); + break; case MF_TOPOLOGY_TEE_NODE: FIXME("Unhandled downstream node type %d.\n", node_type); @@ -3319,8 +3360,8 @@ static void session_deliver_pending_samples(struct media_session *session, IMFTo static HRESULT session_request_sample_from_node(struct media_session *session, IMFTopologyNode *node, DWORD output) { - IMFTopologyNode *downstream_node, *upstream_node; - DWORD downstream_input, upstream_output; + IMFTopologyNode *downstream_node; + DWORD downstream_input; struct topo_node *topo_node; MF_TOPOLOGY_TYPE node_type; struct sample *sample; @@ -3342,13 +3383,9 @@ static HRESULT session_request_sample_from_node(struct media_session *session, I if (list_empty(&topo_node->u.transform.outputs[output].samples)) { - /* Forward request to upstream node. */ - if (SUCCEEDED(hr = IMFTopologyNode_GetInput(node, 0 /* FIXME */, &upstream_node, &upstream_output))) - { - if (SUCCEEDED(hr = session_request_sample_from_node(session, upstream_node, upstream_output))) - topo_node->u.transform.outputs[output].requests++; - IMFTopologyNode_Release(upstream_node); - } + /* Forward request to upstream nodes. */ + if (SUCCEEDED(hr = transform_node_request_sample(session, topo_node))) + topo_node->u.transform.outputs[output].requests++; } else { -- GitLab https://gitlab.winehq.org/wine/wine/-/merge_requests/2684
Fwiw although it's probably a valid approach I think this can be solved differently, using some async MFT wrapper like I'm proposing in https://gitlab.winehq.org/wine/wine/-/merge_requests/3139. The async wrapper will be required to avoid stalling the pipeline with decoder MFT and heavy decoding work, like large videos. -- https://gitlab.winehq.org/wine/wine/-/merge_requests/2684#note_36587
This merge request was closed by Shaun Ren. -- https://gitlab.winehq.org/wine/wine/-/merge_requests/2684
participants (3)
-
Rémi Bernon -
Shaun Ren -
Shaun Ren (@shaunren)