This is similar to https://gitlab.winehq.org/wine/wine/-/merge_requests/2684, https://gitlab.winehq.org/wine/wine/-/merge_requests/3004 or https://gitlab.winehq.org/wine/wine/-/merge_requests/3139 but it validates the session transform node behavior with tests.
The tests are added after the changes because they otherwise don't pass and making them pass would be unnecessarily complicated.
I also have some local tests for MF_TOPONODE_WORKQUEUE_ID attributes and how it is supposed to behave. It basically creates new serialized work queues for every source node and assign them to every node downstream. Any sample request or processing operation is done in the associated queue.
When joining streams, queues are assigned downstream one after another and the last assigned queue is used when requesting samples upstream, but when samples are received and processed downstream it looks like the current queue of the source node is used for every downstream operations.
The request behavior seems to be the same when work queues are used, with round robin input requests, and single ProcessInput call followed by ProcessOutput loop until it fails.
This is yet not optimally efficient, and could be improved, for the following reasons:
1) All session operations are serialized together, even unrelated streams, and ProcessInput / ProcessOutput calls may be costly and stalling the pipeline. I believe that native probably allows parallel processing of unrelated stream requests, this needs to be confirmed.
2) MFT_MESSAGE_COMMAND_DRAIN message use isn't ideal, the message forces the transform to process all queued input synchronously, which can take a long time. I haven't checked exactly what native does but I believe it instead uses MFT_MESSAGE_NOTIFY_END_OF_STREAM messages, which would allow us to notify and drain the GStreamer decoder asynchronously.
3) MFT_MESSAGE_COMMAND_DRAIN also doesn't distinguish between input streams and needs to be sent globally. It's unclear how it should be used when multiple input streams are involved, and when one stream ends its segment then start a new segment while other streams don't have yet reached EOS. MFT_MESSAGE_NOTIFY_END_OF_STREAM messages have a stream ID parameter and would be more appropriate to handle separate input streams independently.
-- v2: mf/tests: Add more media session transform tests with multiple inputs. mf/tests: Add more media session transform tests with multiple outputs. mf/tests: Add some media session transform call pattern tests. mf/session: Increase the stream request count when requests are already queued. mf/session: Request more samples from upstream when necessary. mf/session: Push transform input samples one by one to ProcessInput. mf/session: Use helpers to push and pop samples for transform streams. mf/session: Flush requests in transform_node_deliver_samples when drained. mf/session: Use a helper to deliver transform node requested samples. mf/session: Use local variables to access transform node streams.
From: Rémi Bernon rbernon@codeweavers.com
--- dlls/mf/session.c | 48 +++++++++++++++++++++++++++++++---------------- 1 file changed, 32 insertions(+), 16 deletions(-)
diff --git a/dlls/mf/session.c b/dlls/mf/session.c index 555a71dcdf7..c629399d10e 100644 --- a/dlls/mf/session.c +++ b/dlls/mf/session.c @@ -3128,6 +3128,8 @@ static HRESULT transform_node_pull_samples(const struct media_session *session, /* Collect returned samples for all streams. */ for (i = 0; i < node->u.transform.output_count; ++i) { + struct transform_stream *stream = &node->u.transform.outputs[i]; + if (buffers[i].pEvents) IMFCollection_Release(buffers[i].pEvents);
@@ -3137,7 +3139,7 @@ static HRESULT transform_node_pull_samples(const struct media_session *session, IMFQualityManager_NotifyProcessOutput(session->quality_manager, node->node, i, buffers[i].pSample);
queued_sample = transform_create_sample(buffers[i].pSample); - list_add_tail(&node->u.transform.outputs[i].samples, &queued_sample->entry); + list_add_tail(&stream->samples, &queued_sample->entry); }
if (buffers[i].pSample) @@ -3189,17 +3191,21 @@ static void session_deliver_sample_to_node(struct media_session *session, IMFTop } break; case MF_TOPOLOGY_TRANSFORM_NODE: + { + struct transform_stream *input_stream = &topo_node->u.transform.inputs[input];
transform_node_pull_samples(session, topo_node);
sample_entry = transform_create_sample(sample); - list_add_tail(&topo_node->u.transform.inputs[input].samples, &sample_entry->entry); + list_add_tail(&input_stream->samples, &sample_entry->entry);
for (i = 0; i < topo_node->u.transform.input_count; ++i) { + struct transform_stream *stream = &topo_node->u.transform.inputs[i]; + stream_id = transform_node_get_stream_id(topo_node, FALSE, i); - LIST_FOR_EACH_ENTRY_SAFE(sample_entry, sample_entry2, &topo_node->u.transform.inputs[i].samples, - struct sample, entry) + + LIST_FOR_EACH_ENTRY_SAFE(sample_entry, sample_entry2, &stream->samples, struct sample, entry) { if (sample_entry->sample) { @@ -3212,7 +3218,7 @@ static void session_deliver_sample_to_node(struct media_session *session, IMFTop } else { - transform_stream_drop_samples(&topo_node->u.transform.inputs[i]); + transform_stream_drop_samples(stream); drain = TRUE; } } @@ -3231,28 +3237,32 @@ static void session_deliver_sample_to_node(struct media_session *session, IMFTop { for (i = 0; i < topo_node->u.transform.output_count; ++i) { - if ((sample_entry = transform_create_sample(NULL))) - list_add_tail(&topo_node->u.transform.outputs[i].samples, &sample_entry->entry); + struct transform_stream *stream = &topo_node->u.transform.outputs[i]; + + if ((sample_entry = transform_create_sample(NULL))) + list_add_tail(&stream->samples, &sample_entry->entry); } }
/* Push down all available output. */ for (i = 0; i < topo_node->u.transform.output_count; ++i) { + struct transform_stream *stream = &topo_node->u.transform.outputs[i]; + if (FAILED(IMFTopologyNode_GetOutput(node, i, &downstream_node, &downstream_input))) { WARN("Failed to get connected node for output %u.\n", i); continue; }
- LIST_FOR_EACH_ENTRY_SAFE(sample_entry, sample_entry2, &topo_node->u.transform.outputs[i].samples, + LIST_FOR_EACH_ENTRY_SAFE(sample_entry, sample_entry2, &stream->samples, struct sample, entry) { - if (!topo_node->u.transform.outputs[i].requests) + if (!stream->requests) break;
session_deliver_sample_to_node(session, downstream_node, downstream_input, sample_entry->sample); - topo_node->u.transform.outputs[i].requests--; + stream->requests--;
transform_release_sample(sample_entry); } @@ -3261,6 +3271,7 @@ static void session_deliver_sample_to_node(struct media_session *session, IMFTop }
break; + } case MF_TOPOLOGY_TEE_NODE: FIXME("Unhandled downstream node type %d.\n", node_type); break; @@ -3293,20 +3304,22 @@ static void session_deliver_pending_samples(struct media_session *session, IMFTo /* Push down all available output. */ for (i = 0; i < topo_node->u.transform.output_count; ++i) { + struct transform_stream *stream = &topo_node->u.transform.outputs[i]; + if (FAILED(IMFTopologyNode_GetOutput(node, i, &downstream_node, &downstream_input))) { WARN("Failed to get connected node for output %u.\n", i); continue; }
- LIST_FOR_EACH_ENTRY_SAFE(sample_entry, sample_entry2, &topo_node->u.transform.outputs[i].samples, + LIST_FOR_EACH_ENTRY_SAFE(sample_entry, sample_entry2, &stream->samples, struct sample, entry) { - if (!topo_node->u.transform.outputs[i].requests) + if (!stream->requests) break;
session_deliver_sample_to_node(session, downstream_node, downstream_input, sample_entry->sample); - topo_node->u.transform.outputs[i].requests--; + stream->requests--;
transform_release_sample(sample_entry); } @@ -3342,14 +3355,16 @@ static HRESULT session_request_sample_from_node(struct media_session *session, I WARN("Sample request failed, hr %#lx.\n", hr); break; case MF_TOPOLOGY_TRANSFORM_NODE: + { + struct transform_stream *stream = &topo_node->u.transform.outputs[output];
- if (list_empty(&topo_node->u.transform.outputs[output].samples)) + if (list_empty(&stream->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++; + stream->requests++; IMFTopologyNode_Release(upstream_node); } } @@ -3357,7 +3372,7 @@ static HRESULT session_request_sample_from_node(struct media_session *session, I { if (SUCCEEDED(hr = IMFTopologyNode_GetOutput(node, output, &downstream_node, &downstream_input))) { - sample = LIST_ENTRY(list_head(&topo_node->u.transform.outputs[output].samples), struct sample, entry); + sample = LIST_ENTRY(list_head(&stream->samples), struct sample, entry); session_deliver_sample_to_node(session, downstream_node, downstream_input, sample->sample); transform_release_sample(sample); IMFTopologyNode_Release(downstream_node); @@ -3365,6 +3380,7 @@ static HRESULT session_request_sample_from_node(struct media_session *session, I }
break; + } case MF_TOPOLOGY_TEE_NODE: FIXME("Unhandled upstream node type %d.\n", node_type); default:
From: Rémi Bernon rbernon@codeweavers.com
--- dlls/mf/session.c | 98 +++++++++++++++++++---------------------------- 1 file changed, 39 insertions(+), 59 deletions(-)
diff --git a/dlls/mf/session.c b/dlls/mf/session.c index c629399d10e..254fea6dfe4 100644 --- a/dlls/mf/session.c +++ b/dlls/mf/session.c @@ -3151,12 +3151,47 @@ static HRESULT transform_node_pull_samples(const struct media_session *session, return hr; }
+static void session_deliver_sample_to_node(struct media_session *session, IMFTopologyNode *node, unsigned int input, + IMFSample *sample); + +static void transform_node_deliver_samples(struct media_session *session, struct topo_node *topo_node) +{ + IMFTopologyNode *up_node = topo_node->node, *down_node; + struct sample *sample, *next; + DWORD output, input; + HRESULT hr; + + /* Push down all available output. */ + for (output = 0; output < topo_node->u.transform.output_count; ++output) + { + struct transform_stream *stream = &topo_node->u.transform.outputs[output]; + + if (FAILED(hr = IMFTopologyNode_GetOutput(up_node, output, &down_node, &input))) + { + WARN("Failed to node %p/%lu output, hr %#lx.\n", up_node, output, hr); + continue; + } + + LIST_FOR_EACH_ENTRY_SAFE(sample, next, &stream->samples, struct sample, entry) + { + if (!stream->requests) + break; + + session_deliver_sample_to_node(session, down_node, input, sample->sample); + stream->requests--; + + transform_release_sample(sample); + } + + IMFTopologyNode_Release(down_node); + } +} + static void session_deliver_sample_to_node(struct media_session *session, IMFTopologyNode *node, unsigned int input, IMFSample *sample) { struct sample *sample_entry, *sample_entry2; - DWORD stream_id, downstream_input; - IMFTopologyNode *downstream_node; + DWORD stream_id; struct topo_node *topo_node; MF_TOPOLOGY_TYPE node_type; BOOL drain = FALSE; @@ -3244,32 +3279,7 @@ static void session_deliver_sample_to_node(struct media_session *session, IMFTop } }
- /* Push down all available output. */ - for (i = 0; i < topo_node->u.transform.output_count; ++i) - { - struct transform_stream *stream = &topo_node->u.transform.outputs[i]; - - if (FAILED(IMFTopologyNode_GetOutput(node, i, &downstream_node, &downstream_input))) - { - WARN("Failed to get connected node for output %u.\n", i); - continue; - } - - LIST_FOR_EACH_ENTRY_SAFE(sample_entry, sample_entry2, &stream->samples, - struct sample, entry) - { - if (!stream->requests) - break; - - session_deliver_sample_to_node(session, downstream_node, downstream_input, sample_entry->sample); - stream->requests--; - - transform_release_sample(sample_entry); - } - - IMFTopologyNode_Release(downstream_node); - } - + transform_node_deliver_samples(session, topo_node); break; } case MF_TOPOLOGY_TEE_NODE: @@ -3282,13 +3292,9 @@ static void session_deliver_sample_to_node(struct media_session *session, IMFTop
static void session_deliver_pending_samples(struct media_session *session, IMFTopologyNode *node) { - struct sample *sample_entry, *sample_entry2; - IMFTopologyNode *downstream_node; struct topo_node *topo_node; MF_TOPOLOGY_TYPE node_type; - DWORD downstream_input; TOPOID node_id; - unsigned int i;
IMFTopologyNode_GetNodeType(node, &node_type); IMFTopologyNode_GetTopoNodeID(node, &node_id); @@ -3298,34 +3304,8 @@ static void session_deliver_pending_samples(struct media_session *session, IMFTo switch (node_type) { case MF_TOPOLOGY_TRANSFORM_NODE: - transform_node_pull_samples(session, topo_node); - - /* Push down all available output. */ - for (i = 0; i < topo_node->u.transform.output_count; ++i) - { - struct transform_stream *stream = &topo_node->u.transform.outputs[i]; - - if (FAILED(IMFTopologyNode_GetOutput(node, i, &downstream_node, &downstream_input))) - { - WARN("Failed to get connected node for output %u.\n", i); - continue; - } - - LIST_FOR_EACH_ENTRY_SAFE(sample_entry, sample_entry2, &stream->samples, - struct sample, entry) - { - if (!stream->requests) - break; - - session_deliver_sample_to_node(session, downstream_node, downstream_input, sample_entry->sample); - stream->requests--; - - transform_release_sample(sample_entry); - } - - IMFTopologyNode_Release(downstream_node); - } + transform_node_deliver_samples(session, topo_node); break; default: FIXME("Unexpected node type %u.\n", node_type);
From: Rémi Bernon rbernon@codeweavers.com
--- dlls/mf/session.c | 44 +++++++++++++++++++++++--------------------- 1 file changed, 23 insertions(+), 21 deletions(-)
diff --git a/dlls/mf/session.c b/dlls/mf/session.c index 254fea6dfe4..81198349f66 100644 --- a/dlls/mf/session.c +++ b/dlls/mf/session.c @@ -155,6 +155,7 @@ struct transform_stream struct list samples; unsigned int requests; unsigned int min_buffer_size; + BOOL stream_ended; };
enum topo_node_flags @@ -3151,11 +3152,23 @@ static HRESULT transform_node_pull_samples(const struct media_session *session, return hr; }
+static BOOL transform_node_all_streams_ended(struct topo_node *topo_node) +{ + UINT i; + + for (i = 0; i < topo_node->u.transform.input_count; i++) + if (!topo_node->u.transform.inputs[i].stream_ended) + return FALSE; + + return TRUE; +} + static void session_deliver_sample_to_node(struct media_session *session, IMFTopologyNode *node, unsigned int input, IMFSample *sample);
static void transform_node_deliver_samples(struct media_session *session, struct topo_node *topo_node) { + BOOL draining = transform_node_all_streams_ended(topo_node); IMFTopologyNode *up_node = topo_node->node, *down_node; struct sample *sample, *next; DWORD output, input; @@ -3183,6 +3196,12 @@ static void transform_node_deliver_samples(struct media_session *session, struct transform_release_sample(sample); }
+ while (stream->requests && draining) + { + session_deliver_sample_to_node(session, down_node, input, NULL); + stream->requests--; + } + IMFTopologyNode_Release(down_node); } } @@ -3194,7 +3213,6 @@ static void session_deliver_sample_to_node(struct media_session *session, IMFTop DWORD stream_id; struct topo_node *topo_node; MF_TOPOLOGY_TYPE node_type; - BOOL drain = FALSE; TOPOID node_id; unsigned int i; HRESULT hr; @@ -3242,7 +3260,9 @@ static void session_deliver_sample_to_node(struct media_session *session, IMFTop
LIST_FOR_EACH_ENTRY_SAFE(sample_entry, sample_entry2, &stream->samples, struct sample, entry) { - if (sample_entry->sample) + if ((stream->stream_ended = !sample_entry->sample)) + transform_stream_drop_samples(stream); + else { if ((hr = IMFTransform_ProcessInput(topo_node->object.transform, stream_id, sample_entry->sample, 0)) == MF_E_NOTACCEPTING) @@ -3251,34 +3271,16 @@ static void session_deliver_sample_to_node(struct media_session *session, IMFTop WARN("Failed to process input for stream %u/%lu, hr %#lx.\n", i, stream_id, hr); transform_release_sample(sample_entry); } - else - { - transform_stream_drop_samples(stream); - drain = TRUE; - } } }
- if (drain) + if (transform_node_all_streams_ended(topo_node)) { if (FAILED(hr = IMFTransform_ProcessMessage(topo_node->object.transform, MFT_MESSAGE_COMMAND_DRAIN, 0))) WARN("Drain command failed for transform, hr %#lx.\n", hr); }
transform_node_pull_samples(session, topo_node); - - /* Remaining unprocessed input has been discarded, now queue markers for every output. */ - if (drain) - { - for (i = 0; i < topo_node->u.transform.output_count; ++i) - { - struct transform_stream *stream = &topo_node->u.transform.outputs[i]; - - if ((sample_entry = transform_create_sample(NULL))) - list_add_tail(&stream->samples, &sample_entry->entry); - } - } - transform_node_deliver_samples(session, topo_node); break; }
From: Rémi Bernon rbernon@codeweavers.com
--- dlls/mf/session.c | 80 ++++++++++++++++++++++++++--------------------- 1 file changed, 44 insertions(+), 36 deletions(-)
diff --git a/dlls/mf/session.c b/dlls/mf/session.c index 81198349f66..268a930e21d 100644 --- a/dlls/mf/session.c +++ b/dlls/mf/session.c @@ -681,12 +681,41 @@ static void transform_release_sample(struct sample *sample) free(sample); }
+static HRESULT transform_stream_push_sample(struct transform_stream *stream, IMFSample *sample) +{ + struct sample *entry; + + if (!(entry = calloc(1, sizeof(*entry)))) + return E_OUTOFMEMORY; + + entry->sample = sample; + IMFSample_AddRef(entry->sample); + + list_add_tail(&stream->samples, &entry->entry); + return S_OK; +} + +static HRESULT transform_stream_pop_sample(struct transform_stream *stream, IMFSample **sample) +{ + struct sample *entry; + struct list *ptr; + + if (!(ptr = list_head(&stream->samples))) + return E_FAIL; + + entry = LIST_ENTRY(ptr, struct sample, entry); + list_remove(&entry->entry); + *sample = entry->sample; + free(entry); + return S_OK; +} + static void transform_stream_drop_samples(struct transform_stream *stream) { - struct sample *sample, *sample2; + IMFSample *sample;
- LIST_FOR_EACH_ENTRY_SAFE(sample, sample2, &stream->samples, struct sample, entry) - transform_release_sample(sample); + while (SUCCEEDED(transform_stream_pop_sample(stream, &sample))) + IMFSample_Release(sample); }
static void release_topo_node(struct topo_node *node) @@ -3035,20 +3064,6 @@ static void session_set_sink_stream_state(struct media_session *session, IMFStre } }
-static struct sample *transform_create_sample(IMFSample *sample) -{ - struct sample *sample_entry = calloc(1, sizeof(*sample_entry)); - - if (sample_entry) - { - sample_entry->sample = sample; - if (sample_entry->sample) - IMFSample_AddRef(sample_entry->sample); - } - - return sample_entry; -} - static HRESULT transform_get_external_output_sample(const struct media_session *session, struct topo_node *transform, unsigned int output_index, const MFT_OUTPUT_STREAM_INFO *stream_info, IMFSample **sample) { @@ -3097,7 +3112,6 @@ static HRESULT transform_node_pull_samples(const struct media_session *session, { MFT_OUTPUT_STREAM_INFO stream_info; MFT_OUTPUT_DATA_BUFFER *buffers; - struct sample *queued_sample; HRESULT hr = E_UNEXPECTED; DWORD status = 0; unsigned int i; @@ -3138,9 +3152,8 @@ static HRESULT transform_node_pull_samples(const struct media_session *session, { if (session->quality_manager) IMFQualityManager_NotifyProcessOutput(session->quality_manager, node->node, i, buffers[i].pSample); - - queued_sample = transform_create_sample(buffers[i].pSample); - list_add_tail(&stream->samples, &queued_sample->entry); + if (FAILED(hr = transform_stream_push_sample(stream, buffers[i].pSample))) + WARN("Failed to queue output sample, hr %#lx\n", hr); }
if (buffers[i].pSample) @@ -3170,8 +3183,8 @@ static void transform_node_deliver_samples(struct media_session *session, struct { BOOL draining = transform_node_all_streams_ended(topo_node); IMFTopologyNode *up_node = topo_node->node, *down_node; - struct sample *sample, *next; DWORD output, input; + IMFSample *sample; HRESULT hr;
/* Push down all available output. */ @@ -3185,15 +3198,11 @@ static void transform_node_deliver_samples(struct media_session *session, struct continue; }
- LIST_FOR_EACH_ENTRY_SAFE(sample, next, &stream->samples, struct sample, entry) + while (stream->requests && SUCCEEDED(hr = transform_stream_pop_sample(stream, &sample))) { - if (!stream->requests) - break; - - session_deliver_sample_to_node(session, down_node, input, sample->sample); + session_deliver_sample_to_node(session, down_node, input, sample); stream->requests--; - - transform_release_sample(sample); + IMFSample_Release(sample); }
while (stream->requests && draining) @@ -3249,8 +3258,7 @@ static void session_deliver_sample_to_node(struct media_session *session, IMFTop
transform_node_pull_samples(session, topo_node);
- sample_entry = transform_create_sample(sample); - list_add_tail(&input_stream->samples, &sample_entry->entry); + transform_stream_push_sample(input_stream, sample);
for (i = 0; i < topo_node->u.transform.input_count; ++i) { @@ -3321,7 +3329,7 @@ static HRESULT session_request_sample_from_node(struct media_session *session, I DWORD downstream_input, upstream_output; struct topo_node *topo_node; MF_TOPOLOGY_TYPE node_type; - struct sample *sample; + IMFSample *sample; TOPOID node_id; HRESULT hr;
@@ -3340,7 +3348,7 @@ static HRESULT session_request_sample_from_node(struct media_session *session, I { struct transform_stream *stream = &topo_node->u.transform.outputs[output];
- if (list_empty(&stream->samples)) + if (FAILED(hr = transform_stream_pop_sample(stream, &sample))) { /* Forward request to upstream node. */ if (SUCCEEDED(hr = IMFTopologyNode_GetInput(node, 0 /* FIXME */, &upstream_node, &upstream_output))) @@ -3354,11 +3362,11 @@ static HRESULT session_request_sample_from_node(struct media_session *session, I { if (SUCCEEDED(hr = IMFTopologyNode_GetOutput(node, output, &downstream_node, &downstream_input))) { - sample = LIST_ENTRY(list_head(&stream->samples), struct sample, entry); - session_deliver_sample_to_node(session, downstream_node, downstream_input, sample->sample); - transform_release_sample(sample); + session_deliver_sample_to_node(session, downstream_node, downstream_input, sample); IMFTopologyNode_Release(downstream_node); } + + IMFSample_Release(sample); }
break;
From: Rémi Bernon rbernon@codeweavers.com
--- dlls/mf/session.c | 71 ++++++++++++++++------------------------------- 1 file changed, 24 insertions(+), 47 deletions(-)
diff --git a/dlls/mf/session.c b/dlls/mf/session.c index 268a930e21d..863ff81b29f 100644 --- a/dlls/mf/session.c +++ b/dlls/mf/session.c @@ -673,14 +673,6 @@ static void session_set_caps(struct media_session *session, DWORD caps) IMFMediaEvent_Release(event); }
-static void transform_release_sample(struct sample *sample) -{ - list_remove(&sample->entry); - if (sample->sample) - IMFSample_Release(sample->sample); - free(sample); -} - static HRESULT transform_stream_push_sample(struct transform_stream *stream, IMFSample *sample) { struct sample *entry; @@ -3176,6 +3168,29 @@ static BOOL transform_node_all_streams_ended(struct topo_node *topo_node) return TRUE; }
+static void transform_node_push_sample(const struct media_session *session, struct topo_node *topo_node, + UINT input, IMFSample *sample) +{ + struct transform_stream *stream = &topo_node->u.transform.inputs[input]; + HRESULT hr; + + if ((stream->stream_ended = !sample)) + { + if (!transform_node_all_streams_ended(topo_node)) + return; + + if (FAILED(hr = IMFTransform_ProcessMessage(topo_node->object.transform, MFT_MESSAGE_COMMAND_DRAIN, 0))) + WARN("Drain command failed for transform, hr %#lx.\n", hr); + } + else + { + UINT id = transform_node_get_stream_id(topo_node, FALSE, input); + if (FAILED(hr = IMFTransform_ProcessInput(topo_node->object.transform, id, sample, 0)) + && FAILED(hr = transform_stream_push_sample(stream, sample))) + WARN("Failed to queue delayed input sample, hr %#lx\n", hr); + } +} + static void session_deliver_sample_to_node(struct media_session *session, IMFTopologyNode *node, unsigned int input, IMFSample *sample);
@@ -3218,12 +3233,9 @@ static void transform_node_deliver_samples(struct media_session *session, struct static void session_deliver_sample_to_node(struct media_session *session, IMFTopologyNode *node, unsigned int input, IMFSample *sample) { - struct sample *sample_entry, *sample_entry2; - DWORD stream_id; struct topo_node *topo_node; MF_TOPOLOGY_TYPE node_type; TOPOID node_id; - unsigned int i; HRESULT hr;
if (session->quality_manager) @@ -3253,45 +3265,10 @@ static void session_deliver_sample_to_node(struct media_session *session, IMFTop } break; case MF_TOPOLOGY_TRANSFORM_NODE: - { - struct transform_stream *input_stream = &topo_node->u.transform.inputs[input]; - - transform_node_pull_samples(session, topo_node); - - transform_stream_push_sample(input_stream, sample); - - for (i = 0; i < topo_node->u.transform.input_count; ++i) - { - struct transform_stream *stream = &topo_node->u.transform.inputs[i]; - - stream_id = transform_node_get_stream_id(topo_node, FALSE, i); - - LIST_FOR_EACH_ENTRY_SAFE(sample_entry, sample_entry2, &stream->samples, struct sample, entry) - { - if ((stream->stream_ended = !sample_entry->sample)) - transform_stream_drop_samples(stream); - else - { - if ((hr = IMFTransform_ProcessInput(topo_node->object.transform, stream_id, - sample_entry->sample, 0)) == MF_E_NOTACCEPTING) - 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); - } - } - } - - if (transform_node_all_streams_ended(topo_node)) - { - if (FAILED(hr = IMFTransform_ProcessMessage(topo_node->object.transform, MFT_MESSAGE_COMMAND_DRAIN, 0))) - WARN("Drain command failed for transform, hr %#lx.\n", hr); - } - + transform_node_push_sample(session, topo_node, input, sample); 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", node_type); break;
From: Rémi Bernon rbernon@codeweavers.com
--- dlls/mf/session.c | 48 +++++++++++++++++++++++++++++++++++++++++++---- 1 file changed, 44 insertions(+), 4 deletions(-)
diff --git a/dlls/mf/session.c b/dlls/mf/session.c index 863ff81b29f..72d4a0824d5 100644 --- a/dlls/mf/session.c +++ b/dlls/mf/session.c @@ -200,6 +200,7 @@ struct topo_node struct transform_stream *inputs; DWORD *input_map; unsigned int input_count; + unsigned int next_input;
struct transform_stream *outputs; DWORD *output_map; @@ -693,7 +694,7 @@ static HRESULT transform_stream_pop_sample(struct transform_stream *stream, IMFS struct list *ptr;
if (!(ptr = list_head(&stream->samples))) - return E_FAIL; + return MF_E_TRANSFORM_NEED_MORE_INPUT;
entry = LIST_ENTRY(ptr, struct sample, entry); list_remove(&entry->entry); @@ -3168,6 +3169,17 @@ static BOOL transform_node_all_streams_ended(struct topo_node *topo_node) return TRUE; }
+static BOOL transform_node_has_requests(struct topo_node *topo_node) +{ + UINT i; + + for (i = 0; i < topo_node->u.transform.output_count; i++) + if (topo_node->u.transform.outputs[i].requests) + return TRUE; + + return FALSE; +} + static void transform_node_push_sample(const struct media_session *session, struct topo_node *topo_node, UINT input, IMFSample *sample) { @@ -3200,10 +3212,10 @@ static void transform_node_deliver_samples(struct media_session *session, struct IMFTopologyNode *up_node = topo_node->node, *down_node; DWORD output, input; IMFSample *sample; - HRESULT hr; + HRESULT hr = S_OK;
/* Push down all available output. */ - for (output = 0; output < topo_node->u.transform.output_count; ++output) + for (output = 0; SUCCEEDED(hr) && output < topo_node->u.transform.output_count; ++output) { struct transform_stream *stream = &topo_node->u.transform.outputs[output];
@@ -3213,8 +3225,17 @@ static void transform_node_deliver_samples(struct media_session *session, struct continue; }
- while (stream->requests && SUCCEEDED(hr = transform_stream_pop_sample(stream, &sample))) + while (stream->requests) { + if (FAILED(hr = transform_stream_pop_sample(stream, &sample))) + { + /* try getting more samples by calling IMFTransform_ProcessOutput */ + if (FAILED(hr = transform_node_pull_samples(session, topo_node))) + break; + if (FAILED(hr = transform_stream_pop_sample(stream, &sample))) + break; + } + session_deliver_sample_to_node(session, down_node, input, sample); stream->requests--; IMFSample_Release(sample); @@ -3228,6 +3249,25 @@ static void transform_node_deliver_samples(struct media_session *session, struct
IMFTopologyNode_Release(down_node); } + + if (hr == MF_E_TRANSFORM_NEED_MORE_INPUT && transform_node_has_requests(topo_node)) + { + struct transform_stream *stream; + + input = topo_node->u.transform.next_input++ % topo_node->u.transform.input_count; + stream = &topo_node->u.transform.inputs[input]; + + if (SUCCEEDED(transform_stream_pop_sample(stream, &sample))) + session_deliver_sample_to_node(session, topo_node->node, input, sample); + else if (FAILED(hr = IMFTopologyNode_GetInput(topo_node->node, input, &up_node, &output))) + WARN("Failed to get node %p/%lu input, hr %#lx\n", topo_node->node, input, hr); + 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); + IMFTopologyNode_Release(up_node); + } + } }
static void session_deliver_sample_to_node(struct media_session *session, IMFTopologyNode *node, unsigned int input,
From: Rémi Bernon rbernon@codeweavers.com
Better matching native behavior. --- dlls/mf/session.c | 38 ++++++++++++++++++++------------------ 1 file changed, 20 insertions(+), 18 deletions(-)
diff --git a/dlls/mf/session.c b/dlls/mf/session.c index 72d4a0824d5..16330674df2 100644 --- a/dlls/mf/session.c +++ b/dlls/mf/session.c @@ -3342,13 +3342,13 @@ 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 *down_node; struct topo_node *topo_node; MF_TOPOLOGY_TYPE node_type; + HRESULT hr = S_OK; IMFSample *sample; TOPOID node_id; - HRESULT hr; + DWORD input;
IMFTopologyNode_GetNodeType(node, &node_type); IMFTopologyNode_GetTopoNodeID(node, &node_id); @@ -3365,27 +3365,29 @@ static HRESULT session_request_sample_from_node(struct media_session *session, I { struct transform_stream *stream = &topo_node->u.transform.outputs[output];
- if (FAILED(hr = transform_stream_pop_sample(stream, &sample))) + if (FAILED(hr = IMFTopologyNode_GetOutput(node, output, &down_node, &input))) { - /* 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))) - stream->requests++; - IMFTopologyNode_Release(upstream_node); - } + WARN("Failed to node %p/%lu output, hr %#lx.\n", node, output, hr); + break; } - else - { - if (SUCCEEDED(hr = IMFTopologyNode_GetOutput(node, output, &downstream_node, &downstream_input))) - { - session_deliver_sample_to_node(session, downstream_node, downstream_input, sample); - IMFTopologyNode_Release(downstream_node); - }
+ if (SUCCEEDED(transform_stream_pop_sample(stream, &sample))) + { + session_deliver_sample_to_node(session, down_node, input, sample); IMFSample_Release(sample); } + else if (transform_node_has_requests(topo_node)) + { + /* there's already requests pending, just increase the counter */ + stream->requests++; + } + else + { + stream->requests++; + transform_node_deliver_samples(session, topo_node); + }
+ IMFTopologyNode_Release(down_node); break; } case MF_TOPOLOGY_TEE_NODE:
From: Rémi Bernon rbernon@codeweavers.com
--- dlls/mf/tests/mf.c | 766 +++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 766 insertions(+)
diff --git a/dlls/mf/tests/mf.c b/dlls/mf/tests/mf.c index fa538e8ad9f..cbf373646c0 100644 --- a/dlls/mf/tests/mf.c +++ b/dlls/mf/tests/mf.c @@ -7050,6 +7050,771 @@ static void test_mpeg4_media_sink(void) IMFMediaType_Release(audio_type); }
+struct test_transform +{ + IMFTransform IMFTransform_iface; + LONG refcount; + + CRITICAL_SECTION cs; + CONDITION_VARIABLE results_cond; + BOOL shutdown; + + UINT input_count; + IMFMediaType *available_input_types[4]; + IMFMediaType *input_types[4]; + IMFSample *input_samples[4]; + HRESULT input_results[4]; + HANDLE process_input_event[4]; + + UINT output_count; + IMFMediaType *available_output_types[4]; + IMFMediaType *output_types[4]; + IMFSample *output_samples[4]; + HRESULT output_results[4]; + HANDLE process_output_event[4]; +}; + +static HRESULT WINAPI test_transform_QueryInterface(IMFTransform *iface, REFIID iid, void **out) +{ + struct test_transform *impl = CONTAINING_RECORD(iface, struct test_transform, IMFTransform_iface); + + if (IsEqualGUID(iid, &IID_IUnknown) + || IsEqualGUID(iid, &IID_IMFTransform)) + *out = &impl->IMFTransform_iface; + else + { + *out = NULL; + return E_NOINTERFACE; + } + + IUnknown_AddRef((IUnknown *)*out); + return S_OK; +} + +static ULONG WINAPI test_transform_AddRef(IMFTransform *iface) +{ + struct test_transform *impl = CONTAINING_RECORD(iface, struct test_transform, IMFTransform_iface); + ULONG refcount = InterlockedIncrement(&impl->refcount); + return refcount; +} + +static ULONG WINAPI test_transform_Release(IMFTransform *iface) +{ + struct test_transform *impl = CONTAINING_RECORD(iface, struct test_transform, IMFTransform_iface); + ULONG refcount = InterlockedDecrement(&impl->refcount); + UINT i; + + if (!refcount) + { + for (i = 0; i < impl->input_count; i++) + CloseHandle(impl->process_input_event[i]); + for (i = 0; i < impl->output_count; i++) + CloseHandle(impl->process_output_event[i]); + DeleteCriticalSection(&impl->cs); + free(impl); + } + + return refcount; +} + +static HRESULT WINAPI test_transform_GetStreamLimits(IMFTransform *iface, DWORD *input_minimum, + DWORD *input_maximum, DWORD *output_minimum, DWORD *output_maximum) +{ + return E_NOTIMPL; +} + +static HRESULT WINAPI test_transform_GetStreamCount(IMFTransform *iface, DWORD *inputs, DWORD *outputs) +{ + struct test_transform *impl = CONTAINING_RECORD(iface, struct test_transform, IMFTransform_iface); + *inputs = impl->input_count; + *outputs = impl->output_count; + return S_OK; +} + +static HRESULT WINAPI test_transform_GetStreamIDs(IMFTransform *iface, DWORD input_size, DWORD *inputs, + DWORD output_size, DWORD *outputs) +{ + struct test_transform *impl = CONTAINING_RECORD(iface, struct test_transform, IMFTransform_iface); + UINT i; + + for (i = 0; i < impl->input_count; ++i) + inputs[i] = i; + for (i = 0; i < impl->output_count; ++i) + outputs[i] = i; + + return S_OK; +} + +static HRESULT WINAPI test_transform_GetInputStreamInfo(IMFTransform *iface, DWORD id, MFT_INPUT_STREAM_INFO *info) +{ + info->cbAlignment = 0; + info->cbSize = 0x1000; + info->dwFlags = 0; + info->cbMaxLookahead = 0; + info->hnsMaxLatency = 0; + return S_OK; +} + +static HRESULT WINAPI test_transform_GetOutputStreamInfo(IMFTransform *iface, DWORD id, MFT_OUTPUT_STREAM_INFO *info) +{ + info->cbAlignment = 0; + info->cbSize = 0x1000; + info->dwFlags = MFT_OUTPUT_STREAM_PROVIDES_SAMPLES; + return S_OK; +} + +static HRESULT WINAPI test_transform_GetAttributes(IMFTransform *iface, IMFAttributes **attributes) +{ + return E_NOTIMPL; +} + +static HRESULT WINAPI test_transform_GetInputStreamAttributes(IMFTransform *iface, DWORD id, IMFAttributes **attributes) +{ + return E_NOTIMPL; +} + +static HRESULT WINAPI test_transform_GetOutputStreamAttributes(IMFTransform *iface, DWORD id, IMFAttributes **attributes) +{ + return E_NOTIMPL; +} + +static HRESULT WINAPI test_transform_DeleteInputStream(IMFTransform *iface, DWORD id) +{ + return E_NOTIMPL; +} + +static HRESULT WINAPI test_transform_AddInputStreams(IMFTransform *iface, DWORD streams, DWORD *ids) +{ + return E_NOTIMPL; +} + +static HRESULT WINAPI test_transform_GetInputAvailableType(IMFTransform *iface, DWORD id, DWORD index, + IMFMediaType **type) +{ + struct test_transform *impl = CONTAINING_RECORD(iface, struct test_transform, IMFTransform_iface); + HRESULT hr; + + EnterCriticalSection(&impl->cs); + if (impl->shutdown) + hr = MF_E_SHUTDOWN; + else if (index >= impl->input_count) + hr = MF_E_NO_MORE_TYPES; + else + { + IMFMediaType_AddRef((*type = impl->available_input_types[index])); + hr = S_OK; + } + LeaveCriticalSection(&impl->cs); + return hr; +} + +static HRESULT WINAPI test_transform_GetOutputAvailableType(IMFTransform *iface, DWORD id, + DWORD index, IMFMediaType **type) +{ + struct test_transform *impl = CONTAINING_RECORD(iface, struct test_transform, IMFTransform_iface); + HRESULT hr; + + EnterCriticalSection(&impl->cs); + if (impl->shutdown) + hr = MF_E_SHUTDOWN; + else if (index >= impl->output_count) + hr = MF_E_NO_MORE_TYPES; + else + { + IMFMediaType_AddRef((*type = impl->available_output_types[index])); + hr = S_OK; + } + LeaveCriticalSection(&impl->cs); + return hr; +} + +static HRESULT WINAPI test_transform_SetInputType(IMFTransform *iface, DWORD id, IMFMediaType *type, DWORD flags) +{ + struct test_transform *impl = CONTAINING_RECORD(iface, struct test_transform, IMFTransform_iface); + HRESULT hr; + + EnterCriticalSection(&impl->cs); + if (impl->shutdown) + hr = MF_E_SHUTDOWN; + else if (id >= impl->input_count) + hr = MF_E_INVALIDSTREAMNUMBER; + else + { + IMFMediaType_AddRef((impl->input_types[id] = type)); + hr = S_OK; + } + LeaveCriticalSection(&impl->cs); + return hr; +} + +static HRESULT WINAPI test_transform_SetOutputType(IMFTransform *iface, DWORD id, IMFMediaType *type, DWORD flags) +{ + struct test_transform *impl = CONTAINING_RECORD(iface, struct test_transform, IMFTransform_iface); + HRESULT hr; + + EnterCriticalSection(&impl->cs); + if (impl->shutdown) + hr = MF_E_SHUTDOWN; + else if (id >= impl->output_count) + hr = MF_E_INVALIDSTREAMNUMBER; + else + { + IMFMediaType_AddRef((impl->output_types[id] = type)); + hr = S_OK; + } + LeaveCriticalSection(&impl->cs); + return hr; +} + +static HRESULT WINAPI test_transform_GetInputCurrentType(IMFTransform *iface, DWORD id, IMFMediaType **type) +{ + struct test_transform *impl = CONTAINING_RECORD(iface, struct test_transform, IMFTransform_iface); + HRESULT hr; + + EnterCriticalSection(&impl->cs); + if (impl->shutdown) + hr = MF_E_SHUTDOWN; + else if (id >= impl->input_count) + hr = MF_E_INVALIDSTREAMNUMBER; + else if (!impl->input_types[id]) + hr = MF_E_TRANSFORM_TYPE_NOT_SET; + else + { + IMFMediaType_AddRef((*type = impl->input_types[id])); + hr = S_OK; + } + LeaveCriticalSection(&impl->cs); + return hr; +} + +static HRESULT WINAPI test_transform_GetOutputCurrentType(IMFTransform *iface, DWORD id, IMFMediaType **type) +{ + struct test_transform *impl = CONTAINING_RECORD(iface, struct test_transform, IMFTransform_iface); + HRESULT hr; + + EnterCriticalSection(&impl->cs); + if (impl->shutdown) + hr = MF_E_SHUTDOWN; + else if (id >= impl->output_count) + hr = MF_E_INVALIDSTREAMNUMBER; + else if (!impl->output_types[id]) + hr = MF_E_TRANSFORM_TYPE_NOT_SET; + else + { + IMFMediaType_AddRef((*type = impl->output_types[id])); + hr = S_OK; + } + LeaveCriticalSection(&impl->cs); + return hr; +} + +static HRESULT WINAPI test_transform_GetInputStatus(IMFTransform *iface, DWORD id, DWORD *flags) +{ + return E_NOTIMPL; +} + +static HRESULT WINAPI test_transform_GetOutputStatus(IMFTransform *iface, DWORD *flags) +{ + return E_NOTIMPL; +} + +static HRESULT WINAPI test_transform_SetOutputBounds(IMFTransform *iface, LONGLONG lower, LONGLONG upper) +{ + return E_NOTIMPL; +} + +static HRESULT WINAPI test_transform_ProcessEvent(IMFTransform *iface, DWORD id, IMFMediaEvent *event) +{ + return E_NOTIMPL; +} + +static HRESULT WINAPI test_transform_ProcessMessage(IMFTransform *iface, MFT_MESSAGE_TYPE message, ULONG_PTR param) +{ + return S_OK; +} + +static HRESULT WINAPI test_transform_ProcessInput(IMFTransform *iface, DWORD id, IMFSample *sample, DWORD flags) +{ + struct test_transform *impl = CONTAINING_RECORD(iface, struct test_transform, IMFTransform_iface); + IMFSample *previous; + HRESULT hr; + + EnterCriticalSection(&impl->cs); + + if (impl->shutdown) + hr = MF_E_SHUTDOWN; + else + { + if ((previous = impl->input_samples[id])) + { + /* samples should be received again after MF_E_NOTACCEPTING */ + if (impl->input_results[id] == MF_E_NOTACCEPTING) + ok(previous == sample, "got different sample\n"); + IMFSample_Release(previous); + } + + impl->input_results[id] = E_PENDING; + IMFSample_AddRef((impl->input_samples[id] = sample)); + SetEvent(impl->process_input_event[id]); + + while ((hr = impl->input_results[id]) == E_PENDING && !impl->shutdown) + SleepConditionVariableCS(&impl->results_cond, &impl->cs, INFINITE); + } + + LeaveCriticalSection(&impl->cs); + return hr; +} + +static HRESULT WINAPI test_transform_ProcessOutput(IMFTransform *iface, DWORD flags, DWORD count, + MFT_OUTPUT_DATA_BUFFER *samples, DWORD *status) +{ + struct test_transform *impl = CONTAINING_RECORD(iface, struct test_transform, IMFTransform_iface); + HRESULT hr, result; + UINT i; + + EnterCriticalSection(&impl->cs); + + if (impl->shutdown) + hr = MF_E_SHUTDOWN; + else + { + for (i = 0; i < count; ++i) + { + UINT id = samples[i].dwStreamID; + ok(!samples[i].pEvents, "got pEvents\n"); + ok(!samples[i].pSample, "got pSample\n"); + ok(!samples[i].dwStatus, "got dwStatus\n"); + ok(id == i, "got dwStreamID %#lx\n", samples[i].dwStreamID); + + impl->output_results[id] = E_PENDING; + SetEvent(impl->process_output_event[id]); + } + + for (i = 0, hr = MF_E_TRANSFORM_NEED_MORE_INPUT; i < count; ++i) + { + UINT id = samples[i].dwStreamID; + while ((result = impl->output_results[id]) == E_PENDING && !impl->shutdown) + SleepConditionVariableCS(&impl->results_cond, &impl->cs, INFINITE); + samples[i].pSample = impl->output_samples[id]; + impl->output_samples[id] = NULL; + if (FAILED(result)) + samples[i].dwStatus = MFT_OUTPUT_DATA_BUFFER_NO_SAMPLE; + else + hr = S_OK; + } + + *status = 0; + } + + LeaveCriticalSection(&impl->cs); + return hr; +} + +static const IMFTransformVtbl test_transform_vtbl = +{ + test_transform_QueryInterface, + test_transform_AddRef, + test_transform_Release, + test_transform_GetStreamLimits, + test_transform_GetStreamCount, + test_transform_GetStreamIDs, + test_transform_GetInputStreamInfo, + test_transform_GetOutputStreamInfo, + test_transform_GetAttributes, + test_transform_GetInputStreamAttributes, + test_transform_GetOutputStreamAttributes, + test_transform_DeleteInputStream, + test_transform_AddInputStreams, + test_transform_GetInputAvailableType, + test_transform_GetOutputAvailableType, + test_transform_SetInputType, + test_transform_SetOutputType, + test_transform_GetInputCurrentType, + test_transform_GetOutputCurrentType, + test_transform_GetInputStatus, + test_transform_GetOutputStatus, + test_transform_SetOutputBounds, + test_transform_ProcessEvent, + test_transform_ProcessMessage, + test_transform_ProcessInput, + test_transform_ProcessOutput, +}; + +HRESULT test_transform_create(UINT input_count, IMFMediaType **input_types, + UINT output_count, IMFMediaType **output_types, IMFTransform **out) +{ + struct test_transform *impl; + UINT i; + + if (!(impl = calloc(1, sizeof(*impl)))) + return E_OUTOFMEMORY; + impl->IMFTransform_iface.lpVtbl = &test_transform_vtbl; + impl->refcount = 1; + + impl->input_count = input_count; + impl->output_count = output_count; + + for (i = 0; i < input_count; i++) + { + IMFMediaType_AddRef((impl->available_input_types[i] = input_types[i])); + impl->process_input_event[i] = CreateEventW(NULL, FALSE, FALSE, NULL); + } + for (i = 0; i < output_count; i++) + { + IMFMediaType_AddRef((impl->available_output_types[i] = output_types[i])); + impl->process_output_event[i] = CreateEventW(NULL, FALSE, FALSE, NULL); + } + + InitializeCriticalSection(&impl->cs); + + *out = &impl->IMFTransform_iface; + return S_OK; +} + +static DWORD test_transform_wait_process_input(IMFTransform *iface, DWORD timeout) +{ + struct test_transform *impl = CONTAINING_RECORD(iface, struct test_transform, IMFTransform_iface); + return WaitForMultipleObjects(impl->input_count, impl->process_input_event, FALSE, timeout); +} + +static void test_transform_get_input(IMFTransform *iface, UINT index, IMFSample **sample) +{ + struct test_transform *impl = CONTAINING_RECORD(iface, struct test_transform, IMFTransform_iface); + + EnterCriticalSection(&impl->cs); + impl->input_results[index] = sample ? S_OK : MF_E_NOTACCEPTING; + if (sample) + { + *sample = impl->input_samples[index]; + impl->input_samples[index] = NULL; + } + LeaveCriticalSection(&impl->cs); + WakeAllConditionVariable(&impl->results_cond); +} + +static DWORD test_transform_wait_process_output(IMFTransform *iface, DWORD timeout) +{ + struct test_transform *impl = CONTAINING_RECORD(iface, struct test_transform, IMFTransform_iface); + return WaitForMultipleObjects(impl->output_count, impl->process_output_event, FALSE, timeout); +} + +static void test_transform_set_output(IMFTransform *iface, UINT index, IMFSample *sample) +{ + struct test_transform *impl = CONTAINING_RECORD(iface, struct test_transform, IMFTransform_iface); + + EnterCriticalSection(&impl->cs); + impl->output_results[index] = sample ? S_OK : MF_E_TRANSFORM_NEED_MORE_INPUT; + if ((impl->output_samples[index] = sample)) + IMFSample_AddRef(sample); + LeaveCriticalSection(&impl->cs); + WakeAllConditionVariable(&impl->results_cond); +} + +static void test_transform_shutdown(IMFTransform *iface) +{ + struct test_transform *impl = CONTAINING_RECORD(iface, struct test_transform, IMFTransform_iface); + UINT i; + + EnterCriticalSection(&impl->cs); + impl->shutdown = TRUE; + for (i = 0; i < impl->input_count; i++) + { + impl->input_results[i] = MF_E_SHUTDOWN; + if (impl->input_samples[i]) + IMFSample_Release(impl->input_samples[i]); + impl->input_samples[i] = NULL; + } + for (i = 0; i < impl->output_count; i++) + { + impl->output_results[i] = MF_E_SHUTDOWN; + if (impl->output_samples[i]) + IMFSample_Release(impl->output_samples[i]); + impl->output_samples[i] = NULL; + } + LeaveCriticalSection(&impl->cs); + WakeAllConditionVariable(&impl->results_cond); +} + +static IMFTopologyNode *test_transform_create_source_node(IMFTopology *topology, + IMFMediaSource *source, UINT index, IMFMediaType **media_type) +{ + IMFPresentationDescriptor *pd; + IMFMediaTypeHandler *handler; + IMFStreamDescriptor *sd; + IMFTopologyNode *node; + BOOL selected; + HRESULT hr; + + /* create source node */ + hr = MFCreateTopologyNode(MF_TOPOLOGY_SOURCESTREAM_NODE, &node); + ok(hr == S_OK, "got hr %#lx\n", hr); + hr = IMFTopology_AddNode(topology, node); + ok(hr == S_OK, "got hr %#lx\n", hr); + + hr = IMFMediaSource_CreatePresentationDescriptor(source, &pd); + ok(hr == S_OK, "got hr %#lx\n", hr); + hr = IMFPresentationDescriptor_SelectStream(pd, index); + ok(hr == S_OK, "got hr %#lx\n", hr); + hr = IMFPresentationDescriptor_GetStreamDescriptorByIndex(pd, index, &selected, &sd); + ok(hr == S_OK, "got hr %#lx\n", hr); + + hr = IMFStreamDescriptor_GetMediaTypeHandler(sd, &handler); + ok(hr == S_OK, "got hr %#lx\n", hr); + hr = IMFMediaTypeHandler_GetMediaTypeByIndex(handler, 0, media_type); + ok(hr == S_OK, "got hr %#lx\n", hr); + IMFMediaTypeHandler_Release(handler); + + hr = IMFTopologyNode_SetUnknown(node, &MF_TOPONODE_SOURCE, (IUnknown *)source); + ok(hr == S_OK, "got hr %#lx\n", hr); + hr = IMFTopologyNode_SetUnknown(node, &MF_TOPONODE_STREAM_DESCRIPTOR, (IUnknown *)sd); + ok(hr == S_OK, "got hr %#lx\n", hr); + IMFStreamDescriptor_Release(sd); + hr = IMFTopologyNode_SetUnknown(node, &MF_TOPONODE_PRESENTATION_DESCRIPTOR, (IUnknown *)pd); + ok(hr == S_OK, "got hr %#lx\n", hr); + IMFPresentationDescriptor_Release(pd); + + return node; +} + +static IMFTopologyNode *test_transform_create_transform_node(IMFTopology *topology, + UINT input_count, IMFMediaType **input_types, UINT output_count, IMFMediaType **output_types, + IMFTransform **transform) +{ + IMFTopologyNode *node; + HRESULT hr; + + hr = MFCreateTopologyNode(MF_TOPOLOGY_TRANSFORM_NODE, &node); + ok(hr == S_OK, "got hr %#lx\n", hr); + hr = IMFTopology_AddNode(topology, node); + ok(hr == S_OK, "got hr %#lx\n", hr); + hr = test_transform_create(input_count, input_types, output_count, output_types, transform); + ok(hr == S_OK, "got hr %#lx\n", hr); + hr = IMFTopologyNode_SetObject(node, (IUnknown *)*transform); + ok(hr == S_OK, "got hr %#lx\n", hr); + hr = IMFTopologyNode_SetUINT32(node, &MF_TOPONODE_CONNECT_METHOD, MF_CONNECT_DIRECT); + ok(hr == S_OK, "got hr %#lx\n", hr); + + return node; +} + +static IMFTopologyNode *test_transform_create_output_node(IMFTopology *topology, + IMFMediaType *media_type, IMFSampleGrabberSinkCallback *callback) +{ + IMFStreamSink *stream_sink; + IMFMediaSink *media_sink; + IMFTopologyNode *node; + IMFActivate *activate; + HRESULT hr; + + /* create output node */ + hr = MFCreateTopologyNode(MF_TOPOLOGY_OUTPUT_NODE, &node); + ok(hr == S_OK, "got hr %#lx\n", hr); + hr = IMFTopology_AddNode(topology, node); + ok(hr == S_OK, "got hr %#lx\n", hr); + + hr = MFCreateSampleGrabberSinkActivate(media_type, callback, &activate); + ok(hr == S_OK, "got hr %#lx\n", hr); + hr = IMFActivate_SetUINT32(activate, &MF_SAMPLEGRABBERSINK_IGNORE_CLOCK, 1); + ok(hr == S_OK, "got hr %#lx\n", hr); + hr = IMFActivate_ActivateObject(activate, &IID_IMFMediaSink, (void **)&media_sink); + ok(hr == S_OK, "got hr %#lx\n", hr); + IMFActivate_Release(activate); + + hr = IMFMediaSink_GetStreamSinkByIndex(media_sink, 0, &stream_sink); + ok(hr == S_OK, "got hr %#lx\n", hr); + IMFMediaSink_Release(media_sink); + hr = IMFTopologyNode_SetObject(node, (IUnknown *)stream_sink); + ok(hr == S_OK, "got hr %#lx\n", hr); + IMFStreamSink_Release(stream_sink); + hr = IMFTopologyNode_SetUINT32(node, &MF_TOPONODE_CONNECT_METHOD, MF_CONNECT_DIRECT); + ok(hr == S_OK, "got hr %#lx\n", hr); + + return node; +} + +static void test_media_session_transform_simple(void) +{ + IMFAsyncCallback *media_event_callback; + IMFSampleGrabberSinkCallback *grabber_callback; + IMFTopologyNode *up_node, *node; + IMFMediaType *media_type; + IMFMediaSession *session; + LONGLONG time, duration; + IMFTransform *transform; + IMFMediaSource *source; + UINT32 i; + IMFTopology *topology; + PROPVARIANT propvar; + IMFSample *sample; + HRESULT hr; + DWORD res; + + hr = MFStartup(MF_VERSION, MFSTARTUP_FULL); + ok(hr == S_OK, "got hr %#lx\n", hr); + + if (!(source = create_media_source(L"test.mp4", L"video/mp4"))) + { + win_skip("MP4 media source is not supported, skipping tests.\n"); + goto done; + } + + grabber_callback = create_test_grabber_callback(); + media_event_callback = create_test_callback(TRUE); + + hr = MFCreateMediaSession(NULL, &session); + ok(hr == S_OK, "got hr %#lx\n", hr); + + + /* create the topology with 1 source -> MFT -> 1 output */ + + hr = MFCreateTopology(&topology); + ok(hr == S_OK, "got hr %#lx\n", hr); + + up_node = test_transform_create_source_node(topology, source, 0, &media_type); + + node = test_transform_create_transform_node(topology, 1, &media_type, 1, &media_type, &transform); + hr = IMFTopologyNode_ConnectOutput(up_node, 0, node, 0); + ok(hr == S_OK, "got hr %#lx\n", hr); + IMFTopologyNode_Release(up_node); + up_node = node; + + node = test_transform_create_output_node(topology, media_type, grabber_callback); + IMFMediaType_Release(media_type); + hr = IMFTopologyNode_ConnectOutput(up_node, 0, node, 0); + ok(hr == S_OK, "got hr %#lx\n", hr); + IMFTopologyNode_Release(up_node); + IMFTopologyNode_Release(node); + IMFMediaSource_Release(source); + + + hr = IMFMediaSession_SetTopology(session, 0, topology); + ok(hr == S_OK, "got hr %#lx\n", hr); + + hr = wait_media_event(session, media_event_callback, MESessionTopologySet, 1000, &propvar); + ok(hr == S_OK, "got hr %#lx\n", hr); + ok(propvar.vt == VT_UNKNOWN, "got vt %u\n", propvar.vt); + ok(propvar.punkVal != (IUnknown *)topology, "got punkVal %p\n", propvar.punkVal); + IMFTopology_Release(topology); + IMFTopology_AddRef((topology = (IMFTopology *)propvar.punkVal)); + PropVariantClear(&propvar); + + hr = wait_media_event(session, media_event_callback, MESessionTopologyStatus, 1000, &propvar); + ok(hr == S_OK, "got hr %#lx\n", hr); + ok(propvar.vt == VT_UNKNOWN, "got vt %u\n", propvar.vt); + ok(propvar.punkVal == (IUnknown *)topology, "got punkVal %p\n", propvar.punkVal); + PropVariantClear(&propvar); + + hr = IMFMediaSession_Start(session, &GUID_NULL, &propvar); + ok(hr == S_OK, "got hr %#lx\n", hr); + + + /* starts with a ProcessOutput call */ + + res = test_transform_wait_process_input(transform, 0); + ok(res == WAIT_TIMEOUT, "got res %#lx\n", res); + res = test_transform_wait_process_output(transform, 1000); + ok(res == 0, "got res %#lx\n", res); + + /* ProcessOutput call loops until it fails */ + + for (i = 0, time = 0, duration = 1000; i < 20; i++) + { + IMFMediaBuffer *buffer; + + winetest_push_context("%u", i); + + hr = MFCreateSample(&sample); + ok(hr == S_OK, "got hr %#lx\n", hr); + hr = MFCreateMemoryBuffer(0x1000, &buffer); + ok(hr == S_OK, "got hr %#lx\n", hr); + hr = IMFSample_AddBuffer(sample, buffer); + ok(hr == S_OK, "got hr %#lx\n", hr); + IMFMediaBuffer_Release(buffer); + + hr = IMFSample_SetSampleTime(sample, time + duration); + ok(hr == S_OK, "got hr %#lx\n", hr); + hr = IMFSample_SetSampleDuration(sample, duration); + ok(hr == S_OK, "got hr %#lx\n", hr); + + test_transform_set_output(transform, 0, sample); + + IMFSample_Release(sample); + time += duration; + + res = test_transform_wait_process_output(transform, 1000); + ok(res == 0, "got res %#lx\n", res); + + winetest_pop_context(); + } + + res = test_transform_wait_process_input(transform, 0); + ok(res == WAIT_TIMEOUT, "got res %#lx\n", res); + res = test_transform_wait_process_output(transform, 0); + ok(res == WAIT_TIMEOUT, "got res %#lx\n", res); + test_transform_set_output(transform, 0, NULL); + + for (i = 0; i < 10; i++) + { + winetest_push_context("%u", i); + + /* a single ProcessInput call */ + res = test_transform_wait_process_input(transform, 1000); + ok(res == 0, "got res %#lx\n", res); + res = test_transform_wait_process_input(transform, 0); + ok(res == WAIT_TIMEOUT, "got res %#lx\n", res); + res = test_transform_wait_process_output(transform, 0); + ok(res == WAIT_TIMEOUT, "got res %#lx\n", res); + /* take the sample and return S_OK */ + test_transform_get_input(transform, 0, &sample); + + /* ProcessOutput call loop */ + res = test_transform_wait_process_output(transform, 1000); + ok(res == 0, "got res %#lx\n", res); + test_transform_set_output(transform, 0, sample); + + res = test_transform_wait_process_output(transform, 1000); + ok(res == 0, "got res %#lx\n", res); + test_transform_set_output(transform, 0, NULL); + + IMFSample_Release(sample); + + + /* returning MF_E_NOTACCEPTING doesn't change the pattern */ + res = test_transform_wait_process_input(transform, 1000); + ok(res == 0, "got res %#lx\n", res); + res = test_transform_wait_process_input(transform, 0); + ok(res == WAIT_TIMEOUT, "got res %#lx\n", res); + res = test_transform_wait_process_output(transform, 0); + ok(res == WAIT_TIMEOUT, "got res %#lx\n", res); + /* release the sample and return MF_E_NOTACCEPTING */ + test_transform_get_input(transform, 0, NULL); + + /* ProcessOutput call loop */ + res = test_transform_wait_process_output(transform, 1000); + ok(res == 0, "got res %#lx\n", res); + test_transform_set_output(transform, 0, NULL); + /* needs to fail at least twice */ + res = test_transform_wait_process_output(transform, 1000); + ok(res == 0, "got res %#lx\n", res); + test_transform_set_output(transform, 0, NULL); + + winetest_pop_context(); + } + + test_transform_shutdown(transform); + + IMFTransform_Release(transform); + hr = IMFMediaSession_Shutdown(session); + ok(hr == S_OK, "got hr %#lx\n", hr); + IMFMediaSession_Release(session); + + IMFAsyncCallback_Release(media_event_callback); + +done: + hr = MFShutdown(); + ok(hr == S_OK, "got hr %#lx\n", hr); +} + START_TEST(mf) { init_functions(); @@ -7084,4 +7849,5 @@ START_TEST(mf) test_MFGetTopoNodeCurrentType(); test_MFRequireProtectedEnvironment(); test_mpeg4_media_sink(); + test_media_session_transform_simple(); }
From: Rémi Bernon rbernon@codeweavers.com
--- dlls/mf/tests/mf.c | 208 +++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 208 insertions(+)
diff --git a/dlls/mf/tests/mf.c b/dlls/mf/tests/mf.c index cbf373646c0..5c5e3326fa5 100644 --- a/dlls/mf/tests/mf.c +++ b/dlls/mf/tests/mf.c @@ -7815,6 +7815,213 @@ done: ok(hr == S_OK, "got hr %#lx\n", hr); }
+static void test_media_session_transform_multiple_outputs(void) +{ + IMFAsyncCallback *media_event_callback; + IMFSampleGrabberSinkCallback *grabber_callback; + IMFTopologyNode *up_node, *node; + IMFMediaType *media_types[2]; + IMFMediaSession *session; + LONGLONG time, duration; + IMFTransform *transform; + IMFMediaSource *source; + UINT32 i; + IMFTopology *topology; + PROPVARIANT propvar; + IMFSample *sample; + HRESULT hr; + DWORD res; + + hr = MFStartup(MF_VERSION, MFSTARTUP_FULL); + ok(hr == S_OK, "got hr %#lx\n", hr); + + if (!(source = create_media_source(L"test.mp4", L"video/mp4"))) + { + win_skip("MP4 media source is not supported, skipping tests.\n"); + goto done; + } + + grabber_callback = create_test_grabber_callback(); + media_event_callback = create_test_callback(TRUE); + + hr = MFCreateMediaSession(NULL, &session); + ok(hr == S_OK, "got hr %#lx\n", hr); + + + /* create the topology with 1 source -> MFT -> 2 output */ + hr = MFCreateTopology(&topology); + ok(hr == S_OK, "got hr %#lx\n", hr); + + up_node = test_transform_create_source_node(topology, source, 0, &media_types[0]); + IMFMediaType_AddRef((media_types[1] = media_types[0])); + + node = test_transform_create_transform_node(topology, 1, media_types, 2, media_types, &transform); + hr = IMFTopologyNode_ConnectOutput(up_node, 0, node, 0); + ok(hr == S_OK, "got hr %#lx\n", hr); + IMFTopologyNode_Release(up_node); + up_node = node; + + node = test_transform_create_output_node(topology, media_types[0], grabber_callback); + IMFMediaType_Release(media_types[0]); + hr = IMFTopologyNode_ConnectOutput(up_node, 0, node, 0); + ok(hr == S_OK, "got hr %#lx\n", hr); + IMFTopologyNode_Release(node); + + node = test_transform_create_output_node(topology, media_types[1], grabber_callback); + IMFMediaType_Release(media_types[1]); + hr = IMFTopologyNode_ConnectOutput(up_node, 1, node, 0); + ok(hr == S_OK, "got hr %#lx\n", hr); + IMFTopologyNode_Release(node); + + IMFTopologyNode_Release(up_node); + IMFMediaSource_Release(source); + + + hr = IMFMediaSession_SetTopology(session, 0, topology); + ok(hr == S_OK, "got hr %#lx\n", hr); + + hr = wait_media_event(session, media_event_callback, MESessionTopologySet, 1000, &propvar); + ok(hr == S_OK, "got hr %#lx\n", hr); + ok(propvar.vt == VT_UNKNOWN, "got vt %u\n", propvar.vt); + ok(propvar.punkVal != (IUnknown *)topology, "got punkVal %p\n", propvar.punkVal); + IMFTopology_Release(topology); + IMFTopology_AddRef((topology = (IMFTopology *)propvar.punkVal)); + PropVariantClear(&propvar); + + hr = wait_media_event(session, media_event_callback, MESessionTopologyStatus, 1000, &propvar); + ok(hr == S_OK, "got hr %#lx\n", hr); + ok(propvar.vt == VT_UNKNOWN, "got vt %u\n", propvar.vt); + ok(propvar.punkVal == (IUnknown *)topology, "got punkVal %p\n", propvar.punkVal); + PropVariantClear(&propvar); + + hr = IMFMediaSession_Start(session, &GUID_NULL, &propvar); + ok(hr == S_OK, "got hr %#lx\n", hr); + + + /* starts with a ProcessOutput call for every stream */ + + res = test_transform_wait_process_input(transform, 0); + ok(res == WAIT_TIMEOUT, "got res %#lx\n", res); + res = test_transform_wait_process_output(transform, 1000); + ok(res == 0, "got res %#lx\n", res); + res = test_transform_wait_process_output(transform, 1000); + ok(res == 1, "got res %#lx\n", res); + + /* ProcessOutput call loops until it fails */ + + for (i = 0, time = 0, duration = 1000; i < 20; i++) + { + IMFMediaBuffer *buffer; + + winetest_push_context("%u", i); + + hr = MFCreateSample(&sample); + ok(hr == S_OK, "got hr %#lx\n", hr); + hr = MFCreateMemoryBuffer(0x1000, &buffer); + ok(hr == S_OK, "got hr %#lx\n", hr); + hr = IMFSample_AddBuffer(sample, buffer); + ok(hr == S_OK, "got hr %#lx\n", hr); + IMFMediaBuffer_Release(buffer); + + hr = IMFSample_SetSampleTime(sample, time + duration); + ok(hr == S_OK, "got hr %#lx\n", hr); + hr = IMFSample_SetSampleDuration(sample, duration); + ok(hr == S_OK, "got hr %#lx\n", hr); + + test_transform_set_output(transform, 0, sample); + test_transform_set_output(transform, 1, sample); + IMFSample_Release(sample); + time += duration; + + res = test_transform_wait_process_output(transform, 1000); + ok(res == 0, "got res %#lx\n", res); + res = test_transform_wait_process_output(transform, 1000); + ok(res == 1, "got res %#lx\n", res); + + winetest_pop_context(); + } + + res = test_transform_wait_process_input(transform, 0); + ok(res == WAIT_TIMEOUT, "got res %#lx\n", res); + res = test_transform_wait_process_output(transform, 0); + ok(res == WAIT_TIMEOUT, "got res %#lx\n", res); + test_transform_set_output(transform, 0, NULL); + test_transform_set_output(transform, 1, NULL); + + for (i = 0; i < 10; i++) + { + winetest_push_context("%u", i); + + /* a single ProcessInput call */ + res = test_transform_wait_process_input(transform, 1000); + ok(res == 0, "got res %#lx\n", res); + res = test_transform_wait_process_input(transform, 0); + ok(res == WAIT_TIMEOUT, "got res %#lx\n", res); + res = test_transform_wait_process_output(transform, 0); + ok(res == WAIT_TIMEOUT, "got res %#lx\n", res); + /* take the sample and return S_OK */ + test_transform_get_input(transform, 0, &sample); + + /* ProcessOutput call loop */ + res = test_transform_wait_process_output(transform, 1000); + ok(res == 0, "got res %#lx\n", res); + res = test_transform_wait_process_output(transform, 1000); + ok(res == 1, "got res %#lx\n", res); + test_transform_set_output(transform, 0, NULL); + test_transform_set_output(transform, 1, sample); + + res = test_transform_wait_process_output(transform, 1000); + ok(res == 0, "got res %#lx\n", res); + res = test_transform_wait_process_output(transform, 1000); + ok(res == 1, "got res %#lx\n", res); + test_transform_set_output(transform, 0, NULL); + test_transform_set_output(transform, 1, NULL); + + IMFSample_Release(sample); + + + /* returning MF_E_NOTACCEPTING doesn't change the pattern */ + res = test_transform_wait_process_input(transform, 1000); + ok(res == 0, "got res %#lx\n", res); + res = test_transform_wait_process_input(transform, 0); + ok(res == WAIT_TIMEOUT, "got res %#lx\n", res); + res = test_transform_wait_process_output(transform, 0); + ok(res == WAIT_TIMEOUT, "got res %#lx\n", res); + /* release the sample and return MF_E_NOTACCEPTING */ + test_transform_get_input(transform, 0, NULL); + + /* ProcessOutput call loop */ + res = test_transform_wait_process_output(transform, 1000); + ok(res == 0, "got res %#lx\n", res); + res = test_transform_wait_process_output(transform, 1000); + ok(res == 1, "got res %#lx\n", res); + test_transform_set_output(transform, 0, NULL); + test_transform_set_output(transform, 1, NULL); + /* needs to fail at least twice */ + res = test_transform_wait_process_output(transform, 1000); + ok(res == 0, "got res %#lx\n", res); + res = test_transform_wait_process_output(transform, 1000); + ok(res == 1, "got res %#lx\n", res); + test_transform_set_output(transform, 0, NULL); + test_transform_set_output(transform, 1, NULL); + + winetest_pop_context(); + } + + test_transform_shutdown(transform); + + IMFTransform_Release(transform); + hr = IMFMediaSession_Shutdown(session); + ok(hr == S_OK, "got hr %#lx\n", hr); + IMFMediaSession_Release(session); + + IMFAsyncCallback_Release(media_event_callback); + +done: + hr = MFShutdown(); + ok(hr == S_OK, "got hr %#lx\n", hr); +} + START_TEST(mf) { init_functions(); @@ -7850,4 +8057,5 @@ START_TEST(mf) test_MFRequireProtectedEnvironment(); test_mpeg4_media_sink(); test_media_session_transform_simple(); + test_media_session_transform_multiple_outputs(); }
From: Rémi Bernon rbernon@codeweavers.com
--- dlls/mf/tests/mf.c | 190 +++++++++++++++++++++++++++++ dlls/mf/tests/multiple-streams.mp4 | Bin 0 -> 20789 bytes dlls/mf/tests/resource.rc | 15 +++ 3 files changed, 205 insertions(+) create mode 100644 dlls/mf/tests/multiple-streams.mp4
diff --git a/dlls/mf/tests/mf.c b/dlls/mf/tests/mf.c index 5c5e3326fa5..b775253a1c5 100644 --- a/dlls/mf/tests/mf.c +++ b/dlls/mf/tests/mf.c @@ -8022,6 +8022,195 @@ done: ok(hr == S_OK, "got hr %#lx\n", hr); }
+static void test_media_session_transform_multiple_sources(void) +{ + IMFAsyncCallback *media_event_callback; + IMFSampleGrabberSinkCallback *grabber_callback; + IMFTopologyNode *up_nodes[2], *node; + IMFMediaType *media_types[2]; + UINT32 i; + IMFMediaSession *session; + LONGLONG time, duration; + IMFTransform *transform; + IMFMediaSource *source; + IMFTopology *topology; + PROPVARIANT propvar; + DWORD res, stream; + IMFSample *sample; + HRESULT hr; + + hr = MFStartup(MF_VERSION, MFSTARTUP_FULL); + ok(hr == S_OK, "got hr %#lx\n", hr); + + if (!(source = create_media_source(L"multiple-streams.mp4", L"video/mp4"))) + { + win_skip("MP4 media source is not supported, skipping tests.\n"); + goto done; + } + + grabber_callback = create_test_grabber_callback(); + media_event_callback = create_test_callback(TRUE); + + hr = MFCreateMediaSession(NULL, &session); + ok(hr == S_OK, "got hr %#lx\n", hr); + + + /* create the topology with 2 sources -> MFT -> 1 output */ + hr = MFCreateTopology(&topology); + ok(hr == S_OK, "got hr %#lx\n", hr); + + up_nodes[0] = test_transform_create_source_node(topology, source, 0, &media_types[0]); + up_nodes[1] = test_transform_create_source_node(topology, source, 1, &media_types[1]); + + node = test_transform_create_transform_node(topology, 2, media_types, 1, media_types, &transform); + hr = IMFTopologyNode_ConnectOutput(up_nodes[0], 0, node, 0); + ok(hr == S_OK, "got hr %#lx\n", hr); + IMFTopologyNode_Release(up_nodes[0]); + hr = IMFTopologyNode_ConnectOutput(up_nodes[1], 0, node, 1); + ok(hr == S_OK, "got hr %#lx\n", hr); + IMFTopologyNode_Release(up_nodes[1]); + up_nodes[0] = node; + + node = test_transform_create_output_node(topology, media_types[0], grabber_callback); + IMFMediaType_Release(media_types[0]); + hr = IMFTopologyNode_ConnectOutput(up_nodes[0], 0, node, 0); + ok(hr == S_OK, "got hr %#lx\n", hr); + IMFTopologyNode_Release(up_nodes[0]); + IMFTopologyNode_Release(node); + IMFMediaSource_Release(source); + + + hr = IMFMediaSession_SetTopology(session, 0, topology); + ok(hr == S_OK, "got hr %#lx\n", hr); + + hr = wait_media_event(session, media_event_callback, MESessionTopologySet, 1000, &propvar); + ok(hr == S_OK, "got hr %#lx\n", hr); + ok(propvar.vt == VT_UNKNOWN, "got vt %u\n", propvar.vt); + ok(propvar.punkVal != (IUnknown *)topology, "got punkVal %p\n", propvar.punkVal); + IMFTopology_Release(topology); + IMFTopology_AddRef((topology = (IMFTopology *)propvar.punkVal)); + PropVariantClear(&propvar); + + hr = wait_media_event(session, media_event_callback, MESessionTopologyStatus, 1000, &propvar); + ok(hr == S_OK, "got hr %#lx\n", hr); + ok(propvar.vt == VT_UNKNOWN, "got vt %u\n", propvar.vt); + ok(propvar.punkVal == (IUnknown *)topology, "got punkVal %p\n", propvar.punkVal); + PropVariantClear(&propvar); + + hr = IMFMediaSession_Start(session, &GUID_NULL, &propvar); + ok(hr == S_OK, "got hr %#lx\n", hr); + + + /* starts with a ProcessOutput call */ + + res = test_transform_wait_process_input(transform, 0); + ok(res == WAIT_TIMEOUT, "got res %#lx\n", res); + res = test_transform_wait_process_output(transform, 1000); + ok(res == 0, "got res %#lx\n", res); + + /* ProcessOutput call loops until it fails */ + + for (i = 0, time = 0, duration = 1000; i < 20; i++) + { + IMFMediaBuffer *buffer; + + winetest_push_context("%u", i); + + hr = MFCreateSample(&sample); + ok(hr == S_OK, "got hr %#lx\n", hr); + hr = MFCreateMemoryBuffer(0x1000, &buffer); + ok(hr == S_OK, "got hr %#lx\n", hr); + hr = IMFSample_AddBuffer(sample, buffer); + ok(hr == S_OK, "got hr %#lx\n", hr); + IMFMediaBuffer_Release(buffer); + + hr = IMFSample_SetSampleTime(sample, time + duration); + ok(hr == S_OK, "got hr %#lx\n", hr); + hr = IMFSample_SetSampleDuration(sample, duration); + ok(hr == S_OK, "got hr %#lx\n", hr); + + test_transform_set_output(transform, 0, sample); + + IMFSample_Release(sample); + time += duration; + + res = test_transform_wait_process_output(transform, 1000); + ok(res == 0, "got res %#lx\n", res); + + winetest_pop_context(); + } + + res = test_transform_wait_process_input(transform, 0); + ok(res == WAIT_TIMEOUT, "got res %#lx\n", res); + res = test_transform_wait_process_output(transform, 0); + ok(res == WAIT_TIMEOUT, "got res %#lx\n", res); + test_transform_set_output(transform, 0, NULL); + + for (i = 0; i < 10; i++) + { + winetest_push_context("%u", i); + + /* a single ProcessInput call for stream 0 or 1, depending on the media source */ + res = test_transform_wait_process_input(transform, 1000); + ok(res == 0 || res == 1, "got res %#lx\n", res); + stream = res; + res = test_transform_wait_process_input(transform, 0); + ok(res == WAIT_TIMEOUT, "got res %#lx\n", res); + res = test_transform_wait_process_output(transform, 0); + ok(res == WAIT_TIMEOUT, "got res %#lx\n", res); + /* take the sample and return S_OK */ + test_transform_get_input(transform, stream, &sample); + + /* ProcessOutput call loop */ + res = test_transform_wait_process_output(transform, 1000); + ok(res == 0, "got res %#lx\n", res); + test_transform_set_output(transform, 0, sample); + + res = test_transform_wait_process_output(transform, 1000); + ok(res == 0, "got res %#lx\n", res); + test_transform_set_output(transform, 0, NULL); + + IMFSample_Release(sample); + + + /* a single ProcessInput call for stream 0 or 1, depending on the media source */ + res = test_transform_wait_process_input(transform, 1000); + ok(res == 0 || res == 1, "got res %#lx\n", res); + stream = res; + res = test_transform_wait_process_input(transform, 0); + ok(res == WAIT_TIMEOUT, "got res %#lx\n", res); + res = test_transform_wait_process_output(transform, 0); + ok(res == WAIT_TIMEOUT, "got res %#lx\n", res); + /* release the sample and return MF_E_NOTACCEPTING */ + test_transform_get_input(transform, stream, NULL); + + /* ProcessOutput call loop */ + res = test_transform_wait_process_output(transform, 1000); + ok(res == 0, "got res %#lx\n", res); + test_transform_set_output(transform, 0, NULL); + + /* needs to fail at least twice */ + res = test_transform_wait_process_output(transform, 1000); + ok(res == 0, "got res %#lx\n", res); + test_transform_set_output(transform, 0, NULL); + + winetest_pop_context(); + } + + test_transform_shutdown(transform); + + IMFTransform_Release(transform); + hr = IMFMediaSession_Shutdown(session); + ok(hr == S_OK, "got hr %#lx\n", hr); + IMFMediaSession_Release(session); + + IMFAsyncCallback_Release(media_event_callback); + +done: + hr = MFShutdown(); + ok(hr == S_OK, "got hr %#lx\n", hr); +} + START_TEST(mf) { init_functions(); @@ -8058,4 +8247,5 @@ START_TEST(mf) test_mpeg4_media_sink(); test_media_session_transform_simple(); test_media_session_transform_multiple_outputs(); + test_media_session_transform_multiple_sources(); } diff --git a/dlls/mf/tests/multiple-streams.mp4 b/dlls/mf/tests/multiple-streams.mp4 new file mode 100644 index 0000000000000000000000000000000000000000..ffb333bd89812b050005af0684cccd4d90c65643 GIT binary patch literal 20789 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 W0QddhvkWA$$(6$Yge!?)uKa&B-o<YK
literal 0 HcmV?d00001
diff --git a/dlls/mf/tests/resource.rc b/dlls/mf/tests/resource.rc index d9595c68980..9f9a0d8a4e5 100644 --- a/dlls/mf/tests/resource.rc +++ b/dlls/mf/tests/resource.rc @@ -133,3 +133,18 @@ rgb24frame.bmp RCDATA rgb24frame.bmp */ /* @makedep: test.mp4 */ test.mp4 RCDATA test.mp4 + +/* Generated with: +gst-launch-1.0 \ + audiotestsrc num-buffers=60 ! audio/x-raw,format=F32LE,channels=1 ! \ + taginject tags="language-code=eng" ! \ + audioconvert ! audioresample ! avenc_aac ! mux. \ + \ + audiotestsrc num-buffers=60 ! audio/x-raw,format=S16LE,layout=interleaved ! \ + taginject tags="language-code=fre" ! \ + audioconvert ! audioresample ! lamemp3enc ! mux. \ + \ + mp4mux name=mux ! filesink location=dlls/mf/tests/multiple-streams.mp4 +*/ +/* @makedep: multiple-streams.mp4 */ +multiple-streams.mp4 RCDATA multiple-streams.mp4
Hi,
It looks like your patch introduced the new failures shown below. Please investigate and fix them before resubmitting your patch. If they are not new, fixing them anyway would help a lot. Otherwise please ask for the known failures list to be updated.
The tests also ran into some preexisting test failures. If you know how to fix them that would be helpful. See the TestBot job for the details:
The full results can be found at: https://testbot.winehq.org/JobDetails.pl?Key=134524
Your paranoid android.
=== w7u_2qxl (32 bit report) ===
mf: mf.c:7352: Test failed: got different sample mf.c:7352: Test failed: got different sample mf.c:7352: Test failed: got different sample mf.c:7352: Test failed: got different sample mf.c:7352: Test failed: got different sample mf.c:7352: Test failed: got different sample mf.c:7352: Test failed: got different sample mf.c:7352: Test failed: got different sample mf.c:7352: Test failed: got different sample mf.c:7352: Test failed: got different sample mf.c:7352: Test failed: got different sample mf.c:7352: Test failed: got different sample mf.c:7352: Test failed: got different sample mf.c:7352: Test failed: got different sample mf.c:7352: Test failed: got different sample mf.c:7352: Test failed: got different sample mf.c:7352: Test failed: got different sample mf.c:7352: Test failed: got different sample
=== w7u_adm (32 bit report) ===
mf: mf.c:7352: Test failed: got different sample mf.c:7352: Test failed: got different sample mf.c:7352: Test failed: got different sample mf.c:7352: Test failed: got different sample mf.c:7352: Test failed: got different sample mf.c:7352: Test failed: got different sample mf.c:7352: Test failed: got different sample mf.c:7352: Test failed: got different sample mf.c:7352: Test failed: got different sample mf.c:7352: Test failed: got different sample mf.c:7352: Test failed: got different sample mf.c:7352: Test failed: got different sample mf.c:7352: Test failed: got different sample mf.c:7352: Test failed: got different sample mf.c:7352: Test failed: got different sample mf.c:7352: Test failed: got different sample mf.c:7352: Test failed: got different sample mf.c:7352: Test failed: got different sample
=== w7u_el (32 bit report) ===
mf: mf.c:7352: Test failed: got different sample mf.c:7352: Test failed: got different sample mf.c:7352: Test failed: got different sample mf.c:7352: Test failed: got different sample mf.c:7352: Test failed: got different sample mf.c:7352: Test failed: got different sample mf.c:7352: Test failed: got different sample mf.c:7352: Test failed: got different sample mf.c:7352: Test failed: got different sample mf.c:7352: Test failed: got different sample mf.c:7352: Test failed: got different sample mf.c:7352: Test failed: got different sample mf.c:7352: Test failed: got different sample mf.c:7352: Test failed: got different sample mf.c:7352: Test failed: got different sample mf.c:7352: Test failed: got different sample mf.c:7352: Test failed: got different sample mf.c:7352: Test failed: got different sample
=== w7pro64 (64 bit report) ===
mf: mf.c:7352: Test failed: got different sample mf.c:7352: Test failed: got different sample mf.c:7352: Test failed: got different sample mf.c:7352: Test failed: got different sample mf.c:7352: Test failed: got different sample mf.c:7352: Test failed: got different sample mf.c:7352: Test failed: got different sample mf.c:7352: Test failed: got different sample mf.c:7352: Test failed: got different sample mf.c:7352: Test failed: got different sample mf.c:7352: Test failed: got different sample mf.c:7352: Test failed: got different sample mf.c:7352: Test failed: got different sample mf.c:7352: Test failed: got different sample mf.c:7352: Test failed: got different sample mf.c:7352: Test failed: got different sample mf.c:7352: Test failed: got different sample mf.c:7352: Test failed: got different sample
=== w7u_el (32 bit report) ===
mf: transform.c:4402: Test failed: GetElementCount returned 0x78 transform.c:4404: Test failed: sample 97: got 1 buffers transform.c:4404: Test failed: sample 97: got sample time 14800000 transform.c:4404: Test failed: sample 97: got sample duration 400000 transform.c:1090: Test failed: sample 97: buffer 0: got unexpected buffer 0abc:transform: unhandled exception c0000005 at 0042B382
=== w7pro64 (64 bit report) ===
mf: transform.c:4402: Test failed: GetElementCount returned 0x78 transform.c:4404: Test failed: sample 97: got 1 buffers transform.c:4404: Test failed: sample 97: got sample time 14800000 transform.c:4404: Test failed: sample 97: got sample duration 400000 transform.c:1090: Test failed: sample 97: buffer 0: got unexpected buffer 0720:transform: unhandled exception c0000005 at 0000000000424EC0
v2: Added a test to check what happens to samples refused by ProcessInput, restored the input stream sample queue, which should ever contain at most a single sample (hence no need to insert it at the head of the queue if it gets refused again).