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.
From: Shaun Ren sren@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 {
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.
This merge request was closed by Shaun Ren.