When an upstream node has not given enough samples to the transform, we need to request more samples from it to avoid stalling the pipeline.
This fixes an issue where the intro audio of the game Airborne Kingdom stops playing after a few seconds.
-- v3: mf: Request more samples for transforms when needed.
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 b2371763150..0c07d1bee75 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) @@ -1504,6 +1507,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) @@ -3114,6 +3119,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) { @@ -3138,6 +3146,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) { @@ -3146,7 +3178,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; @@ -3194,7 +3226,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); @@ -3246,9 +3281,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); @@ -3311,8 +3352,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; @@ -3334,13 +3375,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 {
v3: - Rather than checking whether `ProcessOutput() == MF_E_TRANSFORM_NEED_MORE_INPUT` after `ProcessInput()`, instead check if there are still pending requests, to decide if requesting more samples upstream is necessary. If the transform requires multiple inputs to generate an output sample, and multiple samples are requested from downstream, it is possible for the pipeline to stall. - Reset `input_full` whenever `ProcessOutput()` succeeds.
This merge request was closed by Shaun Ren.