This is the first part of a series implementing an asynchronous MFT wrapper around the synchronous session transforms. The idea is to use the async MFT behavior for all the session transforms.
I believe this is generally useful, and goes in the direction of support native async MFT, although there is none at the moment, and it will also improve the behavior of the session with decoder transforms. These MFT do not generate a sample for each input samples, and may require more input to be requested. Using the async MFT behavior, we would generate METransformInputNeeded events when appropriate, and the session would react to them and request more samples from upstream.
Similarly, producing output will trigger METransformOutputAvailable events, and the session can react to these events and push the samples downstream if there were requested, or queue them.
Moreover, for very large video decoding or video processing, the synchronous transform may be performing slowly, and this currently blocks the session callbacks, causing stutter in the playback. Using the async MFT, we can process samples asynchronously, in the multi threaded queue.
The complete async_transform implementation can be seen in the https://gitlab.winehq.org/rbernon/wine/-/commits/mr/mf-async-transform branch. I also have a local tree with fully working decoder MFTs, but it requires more changes elsewhere and some media source refactor.
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 | 137 +++++++++++++++++++++++----------------------- 1 file changed, 70 insertions(+), 67 deletions(-)
diff --git a/dlls/mf/session.c b/dlls/mf/session.c index c629399d10e..3a2add63782 100644 --- a/dlls/mf/session.c +++ b/dlls/mf/session.c @@ -672,20 +672,50 @@ static void session_set_caps(struct media_session *session, DWORD caps) IMFMediaEvent_Release(event); }
-static void transform_release_sample(struct sample *sample) +static void transform_stream_push_sample(struct transform_stream *stream, IMFSample *sample, BOOL head) { - list_remove(&sample->entry); - if (sample->sample) - IMFSample_Release(sample->sample); - free(sample); + struct sample *entry; + + if (!(entry = calloc(1, sizeof(*entry)))) + { + ERR("Failed to allocate sample entry.\n"); + return; + } + + entry->sample = sample; + if (entry->sample) + IMFSample_AddRef(entry->sample); + + if (head) + list_add_head(&stream->samples, &entry->entry); + else + list_add_tail(&stream->samples, &entry->entry); +} + +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_PENDING; + + 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))) + { + if (sample) + IMFSample_Release(sample); + } }
static void release_topo_node(struct topo_node *node) @@ -3034,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) { @@ -3096,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; @@ -3137,9 +3152,7 @@ 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); + transform_stream_push_sample(stream, buffers[i].pSample, FALSE); }
if (buffers[i].pSample) @@ -3154,7 +3167,6 @@ static HRESULT transform_node_pull_samples(const struct media_session *session, 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; struct topo_node *topo_node; @@ -3193,34 +3205,35 @@ static void session_deliver_sample_to_node(struct media_session *session, IMFTop case MF_TOPOLOGY_TRANSFORM_NODE: { struct transform_stream *input_stream = &topo_node->u.transform.inputs[input]; + transform_stream_push_sample(input_stream, sample, FALSE);
transform_node_pull_samples(session, topo_node);
- sample_entry = transform_create_sample(sample); - 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, &stream->samples, struct sample, entry) + while (SUCCEEDED(hr = transform_stream_pop_sample(stream, &sample))) { - if (sample_entry->sample) - { - 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); - } - else + if (!sample) { transform_stream_drop_samples(stream); drain = TRUE; + break; + } + + hr = IMFTransform_ProcessInput(topo_node->object.transform, stream_id, sample, 0); + if (hr == MF_E_NOTACCEPTING) + { + transform_stream_push_sample(stream, sample, TRUE); + IMFSample_Release(sample); + break; } + + if (FAILED(hr)) + WARN("Failed to process input for stream %u/%lu, hr %#lx.\n", i, stream_id, hr); + IMFSample_Release(sample); } }
@@ -3238,9 +3251,7 @@ static void session_deliver_sample_to_node(struct media_session *session, IMFTop 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_stream_push_sample(stream, NULL, FALSE); } }
@@ -3255,16 +3266,12 @@ static void session_deliver_sample_to_node(struct media_session *session, IMFTop continue; }
- LIST_FOR_EACH_ENTRY_SAFE(sample_entry, sample_entry2, &stream->samples, - struct sample, entry) + while (stream->requests && SUCCEEDED(transform_stream_pop_sample(stream, &sample))) { - if (!stream->requests) - break; - - session_deliver_sample_to_node(session, downstream_node, downstream_input, sample_entry->sample); + session_deliver_sample_to_node(session, downstream_node, downstream_input, sample); stream->requests--; - - transform_release_sample(sample_entry); + if (sample) + IMFSample_Release(sample); }
IMFTopologyNode_Release(downstream_node); @@ -3282,11 +3289,11 @@ 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; + IMFSample *sample; TOPOID node_id; unsigned int i;
@@ -3312,16 +3319,12 @@ static void session_deliver_pending_samples(struct media_session *session, IMFTo continue; }
- LIST_FOR_EACH_ENTRY_SAFE(sample_entry, sample_entry2, &stream->samples, - struct sample, entry) + while (stream->requests && SUCCEEDED(transform_stream_pop_sample(stream, &sample))) { - if (!stream->requests) - break; - - session_deliver_sample_to_node(session, downstream_node, downstream_input, sample_entry->sample); + session_deliver_sample_to_node(session, downstream_node, downstream_input, sample); stream->requests--; - - transform_release_sample(sample_entry); + if (sample) + IMFSample_Release(sample); }
IMFTopologyNode_Release(downstream_node); @@ -3339,7 +3342,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;
@@ -3358,7 +3361,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(transform_stream_pop_sample(stream, &sample))) { /* Forward request to upstream node. */ if (SUCCEEDED(hr = IMFTopologyNode_GetInput(node, 0 /* FIXME */, &upstream_node, &upstream_output))) @@ -3372,9 +3375,9 @@ 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); + if (sample) + IMFSample_Release(sample); IMFTopologyNode_Release(downstream_node); } }
From: Rémi Bernon rbernon@codeweavers.com
--- dlls/mf/session.c | 85 ++++++++++++++++++++++++++--------------------- 1 file changed, 48 insertions(+), 37 deletions(-)
diff --git a/dlls/mf/session.c b/dlls/mf/session.c index 3a2add63782..10fcbd80850 100644 --- a/dlls/mf/session.c +++ b/dlls/mf/session.c @@ -3164,14 +3164,58 @@ static HRESULT transform_node_pull_samples(const struct media_session *session, return hr; }
+static HRESULT transform_node_push_samples(const struct media_session *session, struct topo_node *node) +{ + BOOL drain = FALSE; + HRESULT hr = S_OK; + IMFSample *sample; + UINT i, id; + + for (i = 0; i < node->u.transform.input_count; ++i) + { + struct transform_stream *stream = &node->u.transform.inputs[i]; + + id = transform_node_get_stream_id(node, FALSE, i); + while (SUCCEEDED(hr = transform_stream_pop_sample(stream, &sample))) + { + if (!sample) + { + transform_stream_drop_samples(stream); + drain = TRUE; + break; + } + + hr = IMFTransform_ProcessInput(node->object.transform, id, sample, 0); + if (hr == MF_E_NOTACCEPTING) + { + transform_stream_push_sample(stream, sample, TRUE); + IMFSample_Release(sample); + break; + } + + if (FAILED(hr)) + WARN("Failed to process input for stream %u/%u, hr %#lx.\n", i, id, hr); + IMFSample_Release(sample); + } + } + + if (drain) + { + if (FAILED(hr = IMFTransform_ProcessMessage(node->object.transform, MFT_MESSAGE_COMMAND_DRAIN, 0))) + WARN("Drain command failed for transform, hr %#lx.\n", hr); + return MF_E_END_OF_STREAM; + } + + return hr; +} + static void session_deliver_sample_to_node(struct media_session *session, IMFTopologyNode *node, unsigned int input, IMFSample *sample) { - DWORD stream_id, downstream_input; + DWORD downstream_input; IMFTopologyNode *downstream_node; struct topo_node *topo_node; MF_TOPOLOGY_TYPE node_type; - BOOL drain = FALSE; TOPOID node_id; unsigned int i; HRESULT hr; @@ -3208,45 +3252,12 @@ static void session_deliver_sample_to_node(struct media_session *session, IMFTop transform_stream_push_sample(input_stream, sample, FALSE);
transform_node_pull_samples(session, topo_node); - - 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); - while (SUCCEEDED(hr = transform_stream_pop_sample(stream, &sample))) - { - if (!sample) - { - transform_stream_drop_samples(stream); - drain = TRUE; - break; - } - - hr = IMFTransform_ProcessInput(topo_node->object.transform, stream_id, sample, 0); - if (hr == MF_E_NOTACCEPTING) - { - transform_stream_push_sample(stream, sample, TRUE); - IMFSample_Release(sample); - break; - } - - if (FAILED(hr)) - WARN("Failed to process input for stream %u/%lu, hr %#lx.\n", i, stream_id, hr); - IMFSample_Release(sample); - } - } - - if (drain) - { - if (FAILED(hr = IMFTransform_ProcessMessage(topo_node->object.transform, MFT_MESSAGE_COMMAND_DRAIN, 0))) - WARN("Drain command failed for transform, hr %#lx.\n", hr); - } + hr = transform_node_push_samples(session, topo_node);
transform_node_pull_samples(session, topo_node);
/* Remaining unprocessed input has been discarded, now queue markers for every output. */ - if (drain) + if (hr == MF_E_END_OF_STREAM) { for (i = 0; i < topo_node->u.transform.output_count; ++i) {
From: Rémi Bernon rbernon@codeweavers.com
--- dlls/mf/Makefile.in | 1 + dlls/mf/async_transform.c | 346 ++++++++++++++++++++++++++++++++++++++ dlls/mf/mf_private.h | 1 + dlls/mf/session.c | 16 +- 4 files changed, 360 insertions(+), 4 deletions(-) create mode 100644 dlls/mf/async_transform.c
diff --git a/dlls/mf/Makefile.in b/dlls/mf/Makefile.in index d5478cf4853..9a236ce6948 100644 --- a/dlls/mf/Makefile.in +++ b/dlls/mf/Makefile.in @@ -6,6 +6,7 @@ DELAYIMPORTS = evr user32 EXTRADLLFLAGS = -Wb,--prefer-native
C_SRCS = \ + async_transform.c \ clock.c \ copier.c \ evr.c \ diff --git a/dlls/mf/async_transform.c b/dlls/mf/async_transform.c new file mode 100644 index 00000000000..0c746a6da83 --- /dev/null +++ b/dlls/mf/async_transform.c @@ -0,0 +1,346 @@ +/* + * Copyright 2023 Rémi Bernon for CodeWeavers + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA + */ + +#include <stddef.h> +#include <stdarg.h> + +#define COBJMACROS +#include "windef.h" +#include "winbase.h" + +#include "mf_private.h" + +WINE_DEFAULT_DEBUG_CHANNEL(mfplat); + +struct async_transform +{ + IMFTransform IMFTransform_iface; + LONG refcount; + + IMFTransform *transform; +}; + +static struct async_transform *impl_from_IMFTransform(IMFTransform *iface) +{ + return CONTAINING_RECORD(iface, struct async_transform, IMFTransform_iface); +} + +static HRESULT WINAPI transform_QueryInterface(IMFTransform *iface, REFIID iid, void **out) +{ + struct async_transform *impl = impl_from_IMFTransform(iface); + + TRACE("iface %p, iid %s, out %p.\n", iface, debugstr_guid(iid), out); + + if (IsEqualGUID(iid, &IID_IUnknown) + || IsEqualGUID(iid, &IID_IMFTransform)) + *out = &impl->IMFTransform_iface; + else + { + *out = NULL; + WARN("%s not implemented, returning E_NOINTERFACE.\n", debugstr_guid(iid)); + return E_NOINTERFACE; + } + + IUnknown_AddRef((IUnknown *)*out); + return S_OK; +} + +static ULONG WINAPI transform_AddRef(IMFTransform *iface) +{ + struct async_transform *impl = impl_from_IMFTransform(iface); + ULONG refcount = InterlockedIncrement(&impl->refcount); + TRACE("iface %p increasing refcount to %lu.\n", impl, refcount); + return refcount; +} + +static ULONG WINAPI transform_Release(IMFTransform *iface) +{ + struct async_transform *impl = impl_from_IMFTransform(iface); + ULONG refcount = InterlockedDecrement(&impl->refcount); + + TRACE("iface %p decreasing refcount to %lu.\n", impl, refcount); + + if (!refcount) + { + IMFTransform_Release(impl->transform); + free(impl); + } + + return refcount; +} + +static HRESULT WINAPI transform_GetStreamLimits(IMFTransform *iface, DWORD *input_minimum, + DWORD *input_maximum, DWORD *output_minimum, DWORD *output_maximum) +{ + struct async_transform *impl = impl_from_IMFTransform(iface); + + TRACE("iface %p, input_minimum %p, input_maximum %p, output_minimum %p, output_maximum %p.\n", + iface, input_minimum, input_maximum, output_minimum, output_maximum); + + return IMFTransform_GetStreamLimits(impl->transform, input_minimum, input_maximum, output_minimum, output_maximum); +} + +static HRESULT WINAPI transform_GetStreamCount(IMFTransform *iface, DWORD *inputs, DWORD *outputs) +{ + struct async_transform *impl = impl_from_IMFTransform(iface); + + TRACE("iface %p, inputs %p, outputs %p.\n", iface, inputs, outputs); + + return IMFTransform_GetStreamCount(impl->transform, inputs, outputs); +} + +static HRESULT WINAPI transform_GetStreamIDs(IMFTransform *iface, DWORD input_size, DWORD *inputs, + DWORD output_size, DWORD *outputs) +{ + struct async_transform *impl = impl_from_IMFTransform(iface); + + TRACE("iface %p, input_size %lu, inputs %p, output_size %lu, outputs %p.\n", iface, + input_size, inputs, output_size, outputs); + + return IMFTransform_GetStreamIDs(impl->transform, input_size, inputs, output_size, outputs); +} + +static HRESULT WINAPI transform_GetInputStreamInfo(IMFTransform *iface, DWORD id, MFT_INPUT_STREAM_INFO *info) +{ + struct async_transform *impl = impl_from_IMFTransform(iface); + + TRACE("iface %p, id %#lx, info %p.\n", iface, id, info); + + return IMFTransform_GetInputStreamInfo(impl->transform, id, info); +} + +static HRESULT WINAPI transform_GetOutputStreamInfo(IMFTransform *iface, DWORD id, MFT_OUTPUT_STREAM_INFO *info) +{ + struct async_transform *impl = impl_from_IMFTransform(iface); + + TRACE("iface %p, id %#lx, info %p.\n", iface, id, info); + + return IMFTransform_GetOutputStreamInfo(impl->transform, id, info); +} + +static HRESULT WINAPI transform_GetAttributes(IMFTransform *iface, IMFAttributes **attributes) +{ + struct async_transform *impl = impl_from_IMFTransform(iface); + + TRACE("iface %p, attributes %p.\n", iface, attributes); + + return IMFTransform_GetAttributes(impl->transform, attributes); +} + +static HRESULT WINAPI transform_GetInputStreamAttributes(IMFTransform *iface, DWORD id, IMFAttributes **attributes) +{ + struct async_transform *impl = impl_from_IMFTransform(iface); + + TRACE("iface %p, id %#lx, attributes %p.\n", iface, id, attributes); + + return IMFTransform_GetInputStreamAttributes(impl->transform, id, attributes); +} + +static HRESULT WINAPI transform_GetOutputStreamAttributes(IMFTransform *iface, DWORD id, IMFAttributes **attributes) +{ + struct async_transform *impl = impl_from_IMFTransform(iface); + + TRACE("iface %p, id %#lx, attributes %p.\n", iface, id, attributes); + + return IMFTransform_GetOutputStreamAttributes(impl->transform, id, attributes); +} + +static HRESULT WINAPI transform_DeleteInputStream(IMFTransform *iface, DWORD id) +{ + struct async_transform *impl = impl_from_IMFTransform(iface); + + TRACE("iface %p, id %#lx.\n", iface, id); + + return IMFTransform_DeleteInputStream(impl->transform, id); +} + +static HRESULT WINAPI transform_AddInputStreams(IMFTransform *iface, DWORD streams, DWORD *ids) +{ + struct async_transform *impl = impl_from_IMFTransform(iface); + + TRACE("iface %p, streams %lu, ids %p.\n", iface, streams, ids); + + return IMFTransform_AddInputStreams(impl->transform, streams, ids); +} + +static HRESULT WINAPI transform_GetInputAvailableType(IMFTransform *iface, DWORD id, DWORD index, + IMFMediaType **type) +{ + struct async_transform *impl = impl_from_IMFTransform(iface); + + TRACE("iface %p, id %#lx, index %#lx, type %p.\n", iface, id, index, type); + + return IMFTransform_GetInputAvailableType(impl->transform, id, index, type); +} + +static HRESULT WINAPI transform_GetOutputAvailableType(IMFTransform *iface, DWORD id, + DWORD index, IMFMediaType **type) +{ + struct async_transform *impl = impl_from_IMFTransform(iface); + + TRACE("iface %p, id %#lx, index %#lx, type %p.\n", iface, id, index, type); + + return IMFTransform_GetOutputAvailableType(impl->transform, id, index, type); +} + +static HRESULT WINAPI transform_SetInputType(IMFTransform *iface, DWORD id, IMFMediaType *type, DWORD flags) +{ + struct async_transform *impl = impl_from_IMFTransform(iface); + + TRACE("iface %p, id %#lx, type %p, flags %#lx.\n", iface, id, type, flags); + + return IMFTransform_SetInputType(impl->transform, id, type, flags); +} + +static HRESULT WINAPI transform_SetOutputType(IMFTransform *iface, DWORD id, IMFMediaType *type, DWORD flags) +{ + struct async_transform *impl = impl_from_IMFTransform(iface); + + TRACE("iface %p, id %#lx, type %p, flags %#lx.\n", iface, id, type, flags); + + return IMFTransform_SetOutputType(impl->transform, id, type, flags); +} + +static HRESULT WINAPI transform_GetInputCurrentType(IMFTransform *iface, DWORD id, IMFMediaType **type) +{ + struct async_transform *impl = impl_from_IMFTransform(iface); + + TRACE("iface %p, id %#lx, type %p.\n", iface, id, type); + + return IMFTransform_GetInputCurrentType(impl->transform, id, type); +} + +static HRESULT WINAPI transform_GetOutputCurrentType(IMFTransform *iface, DWORD id, IMFMediaType **type) +{ + struct async_transform *impl = impl_from_IMFTransform(iface); + + TRACE("iface %p, id %#lx, type %p.\n", iface, id, type); + + return IMFTransform_GetOutputCurrentType(impl->transform, id, type); +} + +static HRESULT WINAPI transform_GetInputStatus(IMFTransform *iface, DWORD id, DWORD *flags) +{ + struct async_transform *impl = impl_from_IMFTransform(iface); + + TRACE("iface %p, id %#lx, flags %p.\n", iface, id, flags); + + return IMFTransform_GetInputStatus(impl->transform, id, flags); +} + +static HRESULT WINAPI transform_GetOutputStatus(IMFTransform *iface, DWORD *flags) +{ + struct async_transform *impl = impl_from_IMFTransform(iface); + + TRACE("iface %p, flags %p.\n", iface, flags); + + return IMFTransform_GetOutputStatus(impl->transform, flags); +} + +static HRESULT WINAPI transform_SetOutputBounds(IMFTransform *iface, LONGLONG lower, LONGLONG upper) +{ + struct async_transform *impl = impl_from_IMFTransform(iface); + + TRACE("iface %p, lower %I64d, upper %I64d.\n", iface, lower, upper); + + return IMFTransform_SetOutputBounds(impl->transform, lower, upper); +} + +static HRESULT WINAPI transform_ProcessEvent(IMFTransform *iface, DWORD id, IMFMediaEvent *event) +{ + struct async_transform *impl = impl_from_IMFTransform(iface); + + TRACE("iface %p, id %#lx, event %p.\n", iface, id, event); + + return IMFTransform_ProcessEvent(impl->transform, id, event); +} + +static HRESULT WINAPI transform_ProcessMessage(IMFTransform *iface, MFT_MESSAGE_TYPE message, ULONG_PTR param) +{ + struct async_transform *impl = impl_from_IMFTransform(iface); + + TRACE("iface %p, message %#x, param %Ix.\n", iface, message, param); + + return IMFTransform_ProcessMessage(impl->transform, message, param); +} + +static HRESULT WINAPI transform_ProcessInput(IMFTransform *iface, DWORD id, IMFSample *sample, DWORD flags) +{ + struct async_transform *impl = impl_from_IMFTransform(iface); + + TRACE("iface %p, id %#lx, sample %p, flags %#lx.\n", iface, id, sample, flags); + + return IMFTransform_ProcessInput(impl->transform, id, sample, flags); +} + +static HRESULT WINAPI transform_ProcessOutput(IMFTransform *iface, DWORD flags, DWORD count, + MFT_OUTPUT_DATA_BUFFER *samples, DWORD *status) +{ + struct async_transform *impl = impl_from_IMFTransform(iface); + + TRACE("iface %p, flags %#lx, count %lu, samples %p, status %p.\n", iface, flags, count, samples, status); + + return IMFTransform_ProcessOutput(impl->transform, flags, count, samples, status); +} + +static const IMFTransformVtbl transform_vtbl = +{ + transform_QueryInterface, + transform_AddRef, + transform_Release, + transform_GetStreamLimits, + transform_GetStreamCount, + transform_GetStreamIDs, + transform_GetInputStreamInfo, + transform_GetOutputStreamInfo, + transform_GetAttributes, + transform_GetInputStreamAttributes, + transform_GetOutputStreamAttributes, + transform_DeleteInputStream, + transform_AddInputStreams, + transform_GetInputAvailableType, + transform_GetOutputAvailableType, + transform_SetInputType, + transform_SetOutputType, + transform_GetInputCurrentType, + transform_GetOutputCurrentType, + transform_GetInputStatus, + transform_GetOutputStatus, + transform_SetOutputBounds, + transform_ProcessEvent, + transform_ProcessMessage, + transform_ProcessInput, + transform_ProcessOutput, +}; + +HRESULT async_transform_create(IMFTransform *transform, IMFTransform **out) +{ + struct async_transform *impl; + + if (!(impl = calloc(1, sizeof(*impl)))) + return E_OUTOFMEMORY; + impl->IMFTransform_iface.lpVtbl = &transform_vtbl; + impl->refcount = 1; + + impl->transform = transform; + IMFTransform_AddRef(impl->transform); + + *out = &impl->IMFTransform_iface; + TRACE("Created async transform %p for transform %p\n", *out, transform); + return S_OK; +} diff --git a/dlls/mf/mf_private.h b/dlls/mf/mf_private.h index 69923154b79..1bed3671871 100644 --- a/dlls/mf/mf_private.h +++ b/dlls/mf/mf_private.h @@ -118,3 +118,4 @@ extern BOOL mf_is_sar_sink(IMFMediaSink *sink) DECLSPEC_HIDDEN; extern HRESULT topology_node_get_object(IMFTopologyNode *node, REFIID riid, void **obj) DECLSPEC_HIDDEN; extern HRESULT topology_node_get_type_handler(IMFTopologyNode *node, DWORD stream, BOOL output, IMFMediaTypeHandler **handler) DECLSPEC_HIDDEN; extern HRESULT topology_node_init_media_type(IMFTopologyNode *node, DWORD stream, BOOL output, IMFMediaType **type) DECLSPEC_HIDDEN; +extern HRESULT async_transform_create(IMFTransform *transform, IMFTransform **out) DECLSPEC_HIDDEN; diff --git a/dlls/mf/session.c b/dlls/mf/session.c index 10fcbd80850..7d74b665205 100644 --- a/dlls/mf/session.c +++ b/dlls/mf/session.c @@ -1708,15 +1708,23 @@ static HRESULT session_append_node(struct media_session *session, IMFTopologyNod
break; case MF_TOPOLOGY_TRANSFORM_NODE: + { + IMFTransform *transform;
- if (SUCCEEDED(hr = topology_node_get_object(node, &IID_IMFTransform, (void **)&topo_node->object.transform))) + if (FAILED(hr = topology_node_get_object(node, &IID_IMFTransform, (void **)&transform))) + WARN("Failed to get IMFTransform for MFT node, hr %#lx.\n", hr); + else { - hr = session_set_transform_stream_info(topo_node); + /* FIXME handle already async transforms */ + if (FAILED(hr = async_transform_create(transform, &topo_node->object.transform))) + WARN("Failed to create async IMFTransform wrapper, hr %#lx.\n", hr); + else + hr = session_set_transform_stream_info(topo_node); + IMFTransform_Release(transform); } - else - WARN("Failed to get IMFTransform for MFT node, hr %#lx.\n", hr);
break; + } case MF_TOPOLOGY_TEE_NODE: FIXME("Unsupported node type %d.\n", topo_node->type);
From: Rémi Bernon rbernon@codeweavers.com
--- dlls/mf/async_transform.c | 144 ++++++++++++++++++++++++++++++++------ 1 file changed, 121 insertions(+), 23 deletions(-)
diff --git a/dlls/mf/async_transform.c b/dlls/mf/async_transform.c index 0c746a6da83..13ea6f26760 100644 --- a/dlls/mf/async_transform.c +++ b/dlls/mf/async_transform.c @@ -32,6 +32,7 @@ struct async_transform IMFTransform IMFTransform_iface; LONG refcount;
+ CRITICAL_SECTION cs; IMFTransform *transform; };
@@ -78,6 +79,8 @@ static ULONG WINAPI transform_Release(IMFTransform *iface) if (!refcount) { IMFTransform_Release(impl->transform); + impl->cs.DebugInfo->Spare[0] = 0; + DeleteCriticalSection(&impl->cs); free(impl); }
@@ -88,214 +91,306 @@ static HRESULT WINAPI transform_GetStreamLimits(IMFTransform *iface, DWORD *inpu DWORD *input_maximum, DWORD *output_minimum, DWORD *output_maximum) { struct async_transform *impl = impl_from_IMFTransform(iface); + HRESULT hr;
TRACE("iface %p, input_minimum %p, input_maximum %p, output_minimum %p, output_maximum %p.\n", iface, input_minimum, input_maximum, output_minimum, output_maximum);
- return IMFTransform_GetStreamLimits(impl->transform, input_minimum, input_maximum, output_minimum, output_maximum); + EnterCriticalSection(&impl->cs); + hr = IMFTransform_GetStreamLimits(impl->transform, input_minimum, input_maximum, output_minimum, output_maximum); + LeaveCriticalSection(&impl->cs); + return hr; }
static HRESULT WINAPI transform_GetStreamCount(IMFTransform *iface, DWORD *inputs, DWORD *outputs) { struct async_transform *impl = impl_from_IMFTransform(iface); + HRESULT hr;
TRACE("iface %p, inputs %p, outputs %p.\n", iface, inputs, outputs);
- return IMFTransform_GetStreamCount(impl->transform, inputs, outputs); + EnterCriticalSection(&impl->cs); + hr = IMFTransform_GetStreamCount(impl->transform, inputs, outputs); + LeaveCriticalSection(&impl->cs); + return hr; }
static HRESULT WINAPI transform_GetStreamIDs(IMFTransform *iface, DWORD input_size, DWORD *inputs, DWORD output_size, DWORD *outputs) { struct async_transform *impl = impl_from_IMFTransform(iface); + HRESULT hr;
TRACE("iface %p, input_size %lu, inputs %p, output_size %lu, outputs %p.\n", iface, input_size, inputs, output_size, outputs);
- return IMFTransform_GetStreamIDs(impl->transform, input_size, inputs, output_size, outputs); + EnterCriticalSection(&impl->cs); + hr = IMFTransform_GetStreamIDs(impl->transform, input_size, inputs, output_size, outputs); + LeaveCriticalSection(&impl->cs); + return hr; }
static HRESULT WINAPI transform_GetInputStreamInfo(IMFTransform *iface, DWORD id, MFT_INPUT_STREAM_INFO *info) { struct async_transform *impl = impl_from_IMFTransform(iface); + HRESULT hr;
TRACE("iface %p, id %#lx, info %p.\n", iface, id, info);
- return IMFTransform_GetInputStreamInfo(impl->transform, id, info); + EnterCriticalSection(&impl->cs); + hr = IMFTransform_GetInputStreamInfo(impl->transform, id, info); + LeaveCriticalSection(&impl->cs); + return hr; }
static HRESULT WINAPI transform_GetOutputStreamInfo(IMFTransform *iface, DWORD id, MFT_OUTPUT_STREAM_INFO *info) { struct async_transform *impl = impl_from_IMFTransform(iface); + HRESULT hr;
TRACE("iface %p, id %#lx, info %p.\n", iface, id, info);
- return IMFTransform_GetOutputStreamInfo(impl->transform, id, info); + EnterCriticalSection(&impl->cs); + hr = IMFTransform_GetOutputStreamInfo(impl->transform, id, info); + LeaveCriticalSection(&impl->cs); + return hr; }
static HRESULT WINAPI transform_GetAttributes(IMFTransform *iface, IMFAttributes **attributes) { struct async_transform *impl = impl_from_IMFTransform(iface); + HRESULT hr;
TRACE("iface %p, attributes %p.\n", iface, attributes);
- return IMFTransform_GetAttributes(impl->transform, attributes); + EnterCriticalSection(&impl->cs); + hr = IMFTransform_GetAttributes(impl->transform, attributes); + LeaveCriticalSection(&impl->cs); + return hr; }
static HRESULT WINAPI transform_GetInputStreamAttributes(IMFTransform *iface, DWORD id, IMFAttributes **attributes) { struct async_transform *impl = impl_from_IMFTransform(iface); + HRESULT hr;
TRACE("iface %p, id %#lx, attributes %p.\n", iface, id, attributes);
- return IMFTransform_GetInputStreamAttributes(impl->transform, id, attributes); + EnterCriticalSection(&impl->cs); + hr = IMFTransform_GetInputStreamAttributes(impl->transform, id, attributes); + LeaveCriticalSection(&impl->cs); + return hr; }
static HRESULT WINAPI transform_GetOutputStreamAttributes(IMFTransform *iface, DWORD id, IMFAttributes **attributes) { struct async_transform *impl = impl_from_IMFTransform(iface); + HRESULT hr;
TRACE("iface %p, id %#lx, attributes %p.\n", iface, id, attributes);
- return IMFTransform_GetOutputStreamAttributes(impl->transform, id, attributes); + EnterCriticalSection(&impl->cs); + hr = IMFTransform_GetOutputStreamAttributes(impl->transform, id, attributes); + LeaveCriticalSection(&impl->cs); + return hr; }
static HRESULT WINAPI transform_DeleteInputStream(IMFTransform *iface, DWORD id) { struct async_transform *impl = impl_from_IMFTransform(iface); + HRESULT hr;
TRACE("iface %p, id %#lx.\n", iface, id);
- return IMFTransform_DeleteInputStream(impl->transform, id); + EnterCriticalSection(&impl->cs); + hr = IMFTransform_DeleteInputStream(impl->transform, id); + LeaveCriticalSection(&impl->cs); + return hr; }
static HRESULT WINAPI transform_AddInputStreams(IMFTransform *iface, DWORD streams, DWORD *ids) { struct async_transform *impl = impl_from_IMFTransform(iface); + HRESULT hr;
TRACE("iface %p, streams %lu, ids %p.\n", iface, streams, ids);
- return IMFTransform_AddInputStreams(impl->transform, streams, ids); + EnterCriticalSection(&impl->cs); + hr = IMFTransform_AddInputStreams(impl->transform, streams, ids); + LeaveCriticalSection(&impl->cs); + return hr; }
static HRESULT WINAPI transform_GetInputAvailableType(IMFTransform *iface, DWORD id, DWORD index, IMFMediaType **type) { struct async_transform *impl = impl_from_IMFTransform(iface); + HRESULT hr;
TRACE("iface %p, id %#lx, index %#lx, type %p.\n", iface, id, index, type);
- return IMFTransform_GetInputAvailableType(impl->transform, id, index, type); + EnterCriticalSection(&impl->cs); + hr = IMFTransform_GetInputAvailableType(impl->transform, id, index, type); + LeaveCriticalSection(&impl->cs); + return hr; }
static HRESULT WINAPI transform_GetOutputAvailableType(IMFTransform *iface, DWORD id, DWORD index, IMFMediaType **type) { struct async_transform *impl = impl_from_IMFTransform(iface); + HRESULT hr;
TRACE("iface %p, id %#lx, index %#lx, type %p.\n", iface, id, index, type);
- return IMFTransform_GetOutputAvailableType(impl->transform, id, index, type); + EnterCriticalSection(&impl->cs); + hr = IMFTransform_GetOutputAvailableType(impl->transform, id, index, type); + LeaveCriticalSection(&impl->cs); + return hr; }
static HRESULT WINAPI transform_SetInputType(IMFTransform *iface, DWORD id, IMFMediaType *type, DWORD flags) { struct async_transform *impl = impl_from_IMFTransform(iface); + HRESULT hr;
TRACE("iface %p, id %#lx, type %p, flags %#lx.\n", iface, id, type, flags);
- return IMFTransform_SetInputType(impl->transform, id, type, flags); + EnterCriticalSection(&impl->cs); + hr = IMFTransform_SetInputType(impl->transform, id, type, flags); + LeaveCriticalSection(&impl->cs); + return hr; }
static HRESULT WINAPI transform_SetOutputType(IMFTransform *iface, DWORD id, IMFMediaType *type, DWORD flags) { struct async_transform *impl = impl_from_IMFTransform(iface); + HRESULT hr;
TRACE("iface %p, id %#lx, type %p, flags %#lx.\n", iface, id, type, flags);
- return IMFTransform_SetOutputType(impl->transform, id, type, flags); + EnterCriticalSection(&impl->cs); + hr = IMFTransform_SetOutputType(impl->transform, id, type, flags); + LeaveCriticalSection(&impl->cs); + return hr; }
static HRESULT WINAPI transform_GetInputCurrentType(IMFTransform *iface, DWORD id, IMFMediaType **type) { struct async_transform *impl = impl_from_IMFTransform(iface); + HRESULT hr;
TRACE("iface %p, id %#lx, type %p.\n", iface, id, type);
- return IMFTransform_GetInputCurrentType(impl->transform, id, type); + EnterCriticalSection(&impl->cs); + hr = IMFTransform_GetInputCurrentType(impl->transform, id, type); + LeaveCriticalSection(&impl->cs); + return hr; }
static HRESULT WINAPI transform_GetOutputCurrentType(IMFTransform *iface, DWORD id, IMFMediaType **type) { struct async_transform *impl = impl_from_IMFTransform(iface); + HRESULT hr;
TRACE("iface %p, id %#lx, type %p.\n", iface, id, type);
- return IMFTransform_GetOutputCurrentType(impl->transform, id, type); + EnterCriticalSection(&impl->cs); + hr = IMFTransform_GetOutputCurrentType(impl->transform, id, type); + LeaveCriticalSection(&impl->cs); + return hr; }
static HRESULT WINAPI transform_GetInputStatus(IMFTransform *iface, DWORD id, DWORD *flags) { struct async_transform *impl = impl_from_IMFTransform(iface); + HRESULT hr;
TRACE("iface %p, id %#lx, flags %p.\n", iface, id, flags);
- return IMFTransform_GetInputStatus(impl->transform, id, flags); + EnterCriticalSection(&impl->cs); + hr = IMFTransform_GetInputStatus(impl->transform, id, flags); + LeaveCriticalSection(&impl->cs); + return hr; }
static HRESULT WINAPI transform_GetOutputStatus(IMFTransform *iface, DWORD *flags) { struct async_transform *impl = impl_from_IMFTransform(iface); + HRESULT hr;
TRACE("iface %p, flags %p.\n", iface, flags);
- return IMFTransform_GetOutputStatus(impl->transform, flags); + EnterCriticalSection(&impl->cs); + hr = IMFTransform_GetOutputStatus(impl->transform, flags); + LeaveCriticalSection(&impl->cs); + return hr; }
static HRESULT WINAPI transform_SetOutputBounds(IMFTransform *iface, LONGLONG lower, LONGLONG upper) { struct async_transform *impl = impl_from_IMFTransform(iface); + HRESULT hr;
TRACE("iface %p, lower %I64d, upper %I64d.\n", iface, lower, upper);
- return IMFTransform_SetOutputBounds(impl->transform, lower, upper); + EnterCriticalSection(&impl->cs); + hr = IMFTransform_SetOutputBounds(impl->transform, lower, upper); + LeaveCriticalSection(&impl->cs); + return hr; }
static HRESULT WINAPI transform_ProcessEvent(IMFTransform *iface, DWORD id, IMFMediaEvent *event) { struct async_transform *impl = impl_from_IMFTransform(iface); + HRESULT hr;
TRACE("iface %p, id %#lx, event %p.\n", iface, id, event);
- return IMFTransform_ProcessEvent(impl->transform, id, event); + EnterCriticalSection(&impl->cs); + hr = IMFTransform_ProcessEvent(impl->transform, id, event); + LeaveCriticalSection(&impl->cs); + return hr; }
static HRESULT WINAPI transform_ProcessMessage(IMFTransform *iface, MFT_MESSAGE_TYPE message, ULONG_PTR param) { struct async_transform *impl = impl_from_IMFTransform(iface); + HRESULT hr;
TRACE("iface %p, message %#x, param %Ix.\n", iface, message, param);
- return IMFTransform_ProcessMessage(impl->transform, message, param); + EnterCriticalSection(&impl->cs); + hr = IMFTransform_ProcessMessage(impl->transform, message, param); + LeaveCriticalSection(&impl->cs); + return hr; }
static HRESULT WINAPI transform_ProcessInput(IMFTransform *iface, DWORD id, IMFSample *sample, DWORD flags) { struct async_transform *impl = impl_from_IMFTransform(iface); + HRESULT hr;
TRACE("iface %p, id %#lx, sample %p, flags %#lx.\n", iface, id, sample, flags);
- return IMFTransform_ProcessInput(impl->transform, id, sample, flags); + EnterCriticalSection(&impl->cs); + hr = IMFTransform_ProcessInput(impl->transform, id, sample, flags); + LeaveCriticalSection(&impl->cs); + return hr; }
static HRESULT WINAPI transform_ProcessOutput(IMFTransform *iface, DWORD flags, DWORD count, MFT_OUTPUT_DATA_BUFFER *samples, DWORD *status) { struct async_transform *impl = impl_from_IMFTransform(iface); + HRESULT hr;
TRACE("iface %p, flags %#lx, count %lu, samples %p, status %p.\n", iface, flags, count, samples, status);
- return IMFTransform_ProcessOutput(impl->transform, flags, count, samples, status); + EnterCriticalSection(&impl->cs); + hr = IMFTransform_ProcessOutput(impl->transform, flags, count, samples, status); + LeaveCriticalSection(&impl->cs); + return hr; }
static const IMFTransformVtbl transform_vtbl = @@ -337,6 +432,9 @@ HRESULT async_transform_create(IMFTransform *transform, IMFTransform **out) impl->IMFTransform_iface.lpVtbl = &transform_vtbl; impl->refcount = 1;
+ InitializeCriticalSection(&impl->cs); + impl->cs.DebugInfo->Spare[0] = (DWORD_PTR)(__FILE__ ": cs"); + impl->transform = transform; IMFTransform_AddRef(impl->transform);
From: Rémi Bernon rbernon@codeweavers.com
--- dlls/mf/async_transform.c | 197 +++++++++++++++++++++++++++++++++----- dlls/mf/session.c | 24 ++++- 2 files changed, 192 insertions(+), 29 deletions(-)
diff --git a/dlls/mf/async_transform.c b/dlls/mf/async_transform.c index 13ea6f26760..db8c299c0a0 100644 --- a/dlls/mf/async_transform.c +++ b/dlls/mf/async_transform.c @@ -30,6 +30,7 @@ WINE_DEFAULT_DEBUG_CHANNEL(mfplat); struct async_transform { IMFTransform IMFTransform_iface; + IMFShutdown IMFShutdown_iface; LONG refcount;
CRITICAL_SECTION cs; @@ -50,6 +51,8 @@ static HRESULT WINAPI transform_QueryInterface(IMFTransform *iface, REFIID iid, if (IsEqualGUID(iid, &IID_IUnknown) || IsEqualGUID(iid, &IID_IMFTransform)) *out = &impl->IMFTransform_iface; + else if (IsEqualGUID(iid, &IID_IMFShutdown)) + *out = &impl->IMFShutdown_iface; else { *out = NULL; @@ -78,7 +81,7 @@ static ULONG WINAPI transform_Release(IMFTransform *iface)
if (!refcount) { - IMFTransform_Release(impl->transform); + IMFShutdown_Shutdown(&impl->IMFShutdown_iface); impl->cs.DebugInfo->Spare[0] = 0; DeleteCriticalSection(&impl->cs); free(impl); @@ -97,7 +100,10 @@ static HRESULT WINAPI transform_GetStreamLimits(IMFTransform *iface, DWORD *inpu iface, input_minimum, input_maximum, output_minimum, output_maximum);
EnterCriticalSection(&impl->cs); - hr = IMFTransform_GetStreamLimits(impl->transform, input_minimum, input_maximum, output_minimum, output_maximum); + if (!impl->transform) + hr = MF_E_SHUTDOWN; + else + hr = IMFTransform_GetStreamLimits(impl->transform, input_minimum, input_maximum, output_minimum, output_maximum); LeaveCriticalSection(&impl->cs); return hr; } @@ -110,7 +116,10 @@ static HRESULT WINAPI transform_GetStreamCount(IMFTransform *iface, DWORD *input TRACE("iface %p, inputs %p, outputs %p.\n", iface, inputs, outputs);
EnterCriticalSection(&impl->cs); - hr = IMFTransform_GetStreamCount(impl->transform, inputs, outputs); + if (!impl->transform) + hr = MF_E_SHUTDOWN; + else + hr = IMFTransform_GetStreamCount(impl->transform, inputs, outputs); LeaveCriticalSection(&impl->cs); return hr; } @@ -125,7 +134,10 @@ static HRESULT WINAPI transform_GetStreamIDs(IMFTransform *iface, DWORD input_si input_size, inputs, output_size, outputs);
EnterCriticalSection(&impl->cs); - hr = IMFTransform_GetStreamIDs(impl->transform, input_size, inputs, output_size, outputs); + if (!impl->transform) + hr = MF_E_SHUTDOWN; + else + hr = IMFTransform_GetStreamIDs(impl->transform, input_size, inputs, output_size, outputs); LeaveCriticalSection(&impl->cs); return hr; } @@ -138,7 +150,10 @@ static HRESULT WINAPI transform_GetInputStreamInfo(IMFTransform *iface, DWORD id TRACE("iface %p, id %#lx, info %p.\n", iface, id, info);
EnterCriticalSection(&impl->cs); - hr = IMFTransform_GetInputStreamInfo(impl->transform, id, info); + if (!impl->transform) + hr = MF_E_SHUTDOWN; + else + hr = IMFTransform_GetInputStreamInfo(impl->transform, id, info); LeaveCriticalSection(&impl->cs); return hr; } @@ -151,7 +166,10 @@ static HRESULT WINAPI transform_GetOutputStreamInfo(IMFTransform *iface, DWORD i TRACE("iface %p, id %#lx, info %p.\n", iface, id, info);
EnterCriticalSection(&impl->cs); - hr = IMFTransform_GetOutputStreamInfo(impl->transform, id, info); + if (!impl->transform) + hr = MF_E_SHUTDOWN; + else + hr = IMFTransform_GetOutputStreamInfo(impl->transform, id, info); LeaveCriticalSection(&impl->cs); return hr; } @@ -164,7 +182,10 @@ static HRESULT WINAPI transform_GetAttributes(IMFTransform *iface, IMFAttributes TRACE("iface %p, attributes %p.\n", iface, attributes);
EnterCriticalSection(&impl->cs); - hr = IMFTransform_GetAttributes(impl->transform, attributes); + if (!impl->transform) + hr = MF_E_SHUTDOWN; + else + hr = IMFTransform_GetAttributes(impl->transform, attributes); LeaveCriticalSection(&impl->cs); return hr; } @@ -177,7 +198,10 @@ static HRESULT WINAPI transform_GetInputStreamAttributes(IMFTransform *iface, DW TRACE("iface %p, id %#lx, attributes %p.\n", iface, id, attributes);
EnterCriticalSection(&impl->cs); - hr = IMFTransform_GetInputStreamAttributes(impl->transform, id, attributes); + if (!impl->transform) + hr = MF_E_SHUTDOWN; + else + hr = IMFTransform_GetInputStreamAttributes(impl->transform, id, attributes); LeaveCriticalSection(&impl->cs); return hr; } @@ -190,7 +214,10 @@ static HRESULT WINAPI transform_GetOutputStreamAttributes(IMFTransform *iface, D TRACE("iface %p, id %#lx, attributes %p.\n", iface, id, attributes);
EnterCriticalSection(&impl->cs); - hr = IMFTransform_GetOutputStreamAttributes(impl->transform, id, attributes); + if (!impl->transform) + hr = MF_E_SHUTDOWN; + else + hr = IMFTransform_GetOutputStreamAttributes(impl->transform, id, attributes); LeaveCriticalSection(&impl->cs); return hr; } @@ -203,7 +230,10 @@ static HRESULT WINAPI transform_DeleteInputStream(IMFTransform *iface, DWORD id) TRACE("iface %p, id %#lx.\n", iface, id);
EnterCriticalSection(&impl->cs); - hr = IMFTransform_DeleteInputStream(impl->transform, id); + if (!impl->transform) + hr = MF_E_SHUTDOWN; + else + hr = IMFTransform_DeleteInputStream(impl->transform, id); LeaveCriticalSection(&impl->cs); return hr; } @@ -216,7 +246,10 @@ static HRESULT WINAPI transform_AddInputStreams(IMFTransform *iface, DWORD strea TRACE("iface %p, streams %lu, ids %p.\n", iface, streams, ids);
EnterCriticalSection(&impl->cs); - hr = IMFTransform_AddInputStreams(impl->transform, streams, ids); + if (!impl->transform) + hr = MF_E_SHUTDOWN; + else + hr = IMFTransform_AddInputStreams(impl->transform, streams, ids); LeaveCriticalSection(&impl->cs); return hr; } @@ -230,7 +263,10 @@ static HRESULT WINAPI transform_GetInputAvailableType(IMFTransform *iface, DWORD TRACE("iface %p, id %#lx, index %#lx, type %p.\n", iface, id, index, type);
EnterCriticalSection(&impl->cs); - hr = IMFTransform_GetInputAvailableType(impl->transform, id, index, type); + if (!impl->transform) + hr = MF_E_SHUTDOWN; + else + hr = IMFTransform_GetInputAvailableType(impl->transform, id, index, type); LeaveCriticalSection(&impl->cs); return hr; } @@ -244,7 +280,10 @@ static HRESULT WINAPI transform_GetOutputAvailableType(IMFTransform *iface, DWOR TRACE("iface %p, id %#lx, index %#lx, type %p.\n", iface, id, index, type);
EnterCriticalSection(&impl->cs); - hr = IMFTransform_GetOutputAvailableType(impl->transform, id, index, type); + if (!impl->transform) + hr = MF_E_SHUTDOWN; + else + hr = IMFTransform_GetOutputAvailableType(impl->transform, id, index, type); LeaveCriticalSection(&impl->cs); return hr; } @@ -257,7 +296,10 @@ static HRESULT WINAPI transform_SetInputType(IMFTransform *iface, DWORD id, IMFM TRACE("iface %p, id %#lx, type %p, flags %#lx.\n", iface, id, type, flags);
EnterCriticalSection(&impl->cs); - hr = IMFTransform_SetInputType(impl->transform, id, type, flags); + if (!impl->transform) + hr = MF_E_SHUTDOWN; + else + hr = IMFTransform_SetInputType(impl->transform, id, type, flags); LeaveCriticalSection(&impl->cs); return hr; } @@ -270,7 +312,10 @@ static HRESULT WINAPI transform_SetOutputType(IMFTransform *iface, DWORD id, IMF TRACE("iface %p, id %#lx, type %p, flags %#lx.\n", iface, id, type, flags);
EnterCriticalSection(&impl->cs); - hr = IMFTransform_SetOutputType(impl->transform, id, type, flags); + if (!impl->transform) + hr = MF_E_SHUTDOWN; + else + hr = IMFTransform_SetOutputType(impl->transform, id, type, flags); LeaveCriticalSection(&impl->cs); return hr; } @@ -283,7 +328,10 @@ static HRESULT WINAPI transform_GetInputCurrentType(IMFTransform *iface, DWORD i TRACE("iface %p, id %#lx, type %p.\n", iface, id, type);
EnterCriticalSection(&impl->cs); - hr = IMFTransform_GetInputCurrentType(impl->transform, id, type); + if (!impl->transform) + hr = MF_E_SHUTDOWN; + else + hr = IMFTransform_GetInputCurrentType(impl->transform, id, type); LeaveCriticalSection(&impl->cs); return hr; } @@ -296,7 +344,10 @@ static HRESULT WINAPI transform_GetOutputCurrentType(IMFTransform *iface, DWORD TRACE("iface %p, id %#lx, type %p.\n", iface, id, type);
EnterCriticalSection(&impl->cs); - hr = IMFTransform_GetOutputCurrentType(impl->transform, id, type); + if (!impl->transform) + hr = MF_E_SHUTDOWN; + else + hr = IMFTransform_GetOutputCurrentType(impl->transform, id, type); LeaveCriticalSection(&impl->cs); return hr; } @@ -309,7 +360,10 @@ static HRESULT WINAPI transform_GetInputStatus(IMFTransform *iface, DWORD id, DW TRACE("iface %p, id %#lx, flags %p.\n", iface, id, flags);
EnterCriticalSection(&impl->cs); - hr = IMFTransform_GetInputStatus(impl->transform, id, flags); + if (!impl->transform) + hr = MF_E_SHUTDOWN; + else + hr = IMFTransform_GetInputStatus(impl->transform, id, flags); LeaveCriticalSection(&impl->cs); return hr; } @@ -322,7 +376,10 @@ static HRESULT WINAPI transform_GetOutputStatus(IMFTransform *iface, DWORD *flag TRACE("iface %p, flags %p.\n", iface, flags);
EnterCriticalSection(&impl->cs); - hr = IMFTransform_GetOutputStatus(impl->transform, flags); + if (!impl->transform) + hr = MF_E_SHUTDOWN; + else + hr = IMFTransform_GetOutputStatus(impl->transform, flags); LeaveCriticalSection(&impl->cs); return hr; } @@ -335,7 +392,10 @@ static HRESULT WINAPI transform_SetOutputBounds(IMFTransform *iface, LONGLONG lo TRACE("iface %p, lower %I64d, upper %I64d.\n", iface, lower, upper);
EnterCriticalSection(&impl->cs); - hr = IMFTransform_SetOutputBounds(impl->transform, lower, upper); + if (!impl->transform) + hr = MF_E_SHUTDOWN; + else + hr = IMFTransform_SetOutputBounds(impl->transform, lower, upper); LeaveCriticalSection(&impl->cs); return hr; } @@ -348,7 +408,10 @@ static HRESULT WINAPI transform_ProcessEvent(IMFTransform *iface, DWORD id, IMFM TRACE("iface %p, id %#lx, event %p.\n", iface, id, event);
EnterCriticalSection(&impl->cs); - hr = IMFTransform_ProcessEvent(impl->transform, id, event); + if (!impl->transform) + hr = MF_E_SHUTDOWN; + else + hr = IMFTransform_ProcessEvent(impl->transform, id, event); LeaveCriticalSection(&impl->cs); return hr; } @@ -361,7 +424,10 @@ static HRESULT WINAPI transform_ProcessMessage(IMFTransform *iface, MFT_MESSAGE_ TRACE("iface %p, message %#x, param %Ix.\n", iface, message, param);
EnterCriticalSection(&impl->cs); - hr = IMFTransform_ProcessMessage(impl->transform, message, param); + if (!impl->transform) + hr = MF_E_SHUTDOWN; + else + hr = IMFTransform_ProcessMessage(impl->transform, message, param); LeaveCriticalSection(&impl->cs); return hr; } @@ -374,7 +440,10 @@ static HRESULT WINAPI transform_ProcessInput(IMFTransform *iface, DWORD id, IMFS TRACE("iface %p, id %#lx, sample %p, flags %#lx.\n", iface, id, sample, flags);
EnterCriticalSection(&impl->cs); - hr = IMFTransform_ProcessInput(impl->transform, id, sample, flags); + if (!impl->transform) + hr = MF_E_SHUTDOWN; + else + hr = IMFTransform_ProcessInput(impl->transform, id, sample, flags); LeaveCriticalSection(&impl->cs); return hr; } @@ -388,7 +457,10 @@ static HRESULT WINAPI transform_ProcessOutput(IMFTransform *iface, DWORD flags, TRACE("iface %p, flags %#lx, count %lu, samples %p, status %p.\n", iface, flags, count, samples, status);
EnterCriticalSection(&impl->cs); - hr = IMFTransform_ProcessOutput(impl->transform, flags, count, samples, status); + if (!impl->transform) + hr = MF_E_SHUTDOWN; + else + hr = IMFTransform_ProcessOutput(impl->transform, flags, count, samples, status); LeaveCriticalSection(&impl->cs); return hr; } @@ -423,6 +495,82 @@ static const IMFTransformVtbl transform_vtbl = transform_ProcessOutput, };
+static struct async_transform *impl_from_IMFShutdown(IMFShutdown *iface) +{ + return CONTAINING_RECORD(iface, struct async_transform, IMFShutdown_iface); +} + +static HRESULT WINAPI shutdown_QueryInterface(IMFShutdown *iface, REFIID iid, void **out) +{ + struct async_transform *impl = impl_from_IMFShutdown(iface); + return IMFTransform_QueryInterface(&impl->IMFTransform_iface, iid, out); +} + +static ULONG WINAPI shutdown_AddRef(IMFShutdown *iface) +{ + struct async_transform *impl = impl_from_IMFShutdown(iface); + return IMFTransform_AddRef(&impl->IMFTransform_iface); +} + +static ULONG WINAPI shutdown_Release(IMFShutdown *iface) +{ + struct async_transform *impl = impl_from_IMFShutdown(iface); + return IMFTransform_Release(&impl->IMFTransform_iface); +} + +static HRESULT WINAPI shutdown_Shutdown(IMFShutdown *iface) +{ + struct async_transform *impl = impl_from_IMFShutdown(iface); + HRESULT hr; + + TRACE("iface %p.\n", iface); + + EnterCriticalSection(&impl->cs); + if (!impl->transform) + hr = MF_E_SHUTDOWN; + else + { + IMFTransform_Release(impl->transform); + impl->transform = NULL; + hr = S_OK; + } + LeaveCriticalSection(&impl->cs); + + return hr; +} + +static HRESULT WINAPI shutdown_GetShutdownStatus(IMFShutdown *iface, MFSHUTDOWN_STATUS *status) +{ + struct async_transform *impl = impl_from_IMFShutdown(iface); + HRESULT hr; + + TRACE("iface %p, status %p.\n", iface, status); + + if (!status) + return E_INVALIDARG; + + EnterCriticalSection(&impl->cs); + if (impl->transform) + hr = MF_E_INVALIDREQUEST; + else + { + *status = MFSHUTDOWN_COMPLETED; + hr = S_OK; + } + LeaveCriticalSection(&impl->cs); + + return hr; +} + +static const IMFShutdownVtbl shutdown_vtbl = +{ + shutdown_QueryInterface, + shutdown_AddRef, + shutdown_Release, + shutdown_Shutdown, + shutdown_GetShutdownStatus, +}; + HRESULT async_transform_create(IMFTransform *transform, IMFTransform **out) { struct async_transform *impl; @@ -430,6 +578,7 @@ HRESULT async_transform_create(IMFTransform *transform, IMFTransform **out) if (!(impl = calloc(1, sizeof(*impl)))) return E_OUTOFMEMORY; impl->IMFTransform_iface.lpVtbl = &transform_vtbl; + impl->IMFShutdown_iface.lpVtbl = &shutdown_vtbl; impl->refcount = 1;
InitializeCriticalSection(&impl->cs); diff --git a/dlls/mf/session.c b/dlls/mf/session.c index 7d74b665205..808ca5306ce 100644 --- a/dlls/mf/session.c +++ b/dlls/mf/session.c @@ -760,7 +760,8 @@ static void release_topo_node(struct topo_node *node)
static void session_shutdown_current_topology(struct media_session *session) { - unsigned int shutdown, force_shutdown; + unsigned int needs_shutdown, force_shutdown; + struct topo_node *topo_node; MF_TOPOLOGY_TYPE node_type; IMFStreamSink *stream_sink; IMFTopology *topology; @@ -773,17 +774,30 @@ static void session_shutdown_current_topology(struct media_session *session) topology = session->presentation.current_topology; force_shutdown = session->state == SESSION_STATE_SHUT_DOWN;
- /* FIXME: should handle async MFTs, but these are not supported by the rest of the pipeline currently. */ + LIST_FOR_EACH_ENTRY(topo_node, &session->presentation.nodes, struct topo_node, entry) + { + IMFShutdown *shutdown; + + if (topo_node->type != MF_TOPOLOGY_TRANSFORM_NODE) + continue; + + if (SUCCEEDED(IMFTransform_QueryInterface(topo_node->object.transform, &IID_IMFShutdown, (void **)&shutdown))) + { + if (FAILED(IMFShutdown_Shutdown(shutdown))) + WARN("Failed to shutdown async transform\n"); + IMFShutdown_Release(shutdown); + } + }
while (SUCCEEDED(IMFTopology_GetNode(topology, idx++, &node))) { if (SUCCEEDED(IMFTopologyNode_GetNodeType(node, &node_type)) && node_type == MF_TOPOLOGY_OUTPUT_NODE) { - shutdown = 1; - IMFTopologyNode_GetUINT32(node, &MF_TOPONODE_NOSHUTDOWN_ON_REMOVE, &shutdown); + if (FAILED(IMFTopologyNode_GetUINT32(node, &MF_TOPONODE_NOSHUTDOWN_ON_REMOVE, &needs_shutdown))) + needs_shutdown = 1;
- if (force_shutdown || shutdown) + if (force_shutdown || needs_shutdown) { if (SUCCEEDED(IMFTopologyNode_GetUnknown(node, &_MF_TOPONODE_IMFActivate, &IID_IMFActivate, (void **)&activate)))
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=134067
Your paranoid android.
=== debian11 (32 bit report) ===
user32: win.c:10983: Test failed: GetForegroundWindow() = 016E0070
Having support for asynchronous transforms would be nice, but my concerns are about compatibility:
- there is a documented way to assign queues to branches with MF_TOPONODE_WORKQUEUE_ID. I imagine that having async processing on the same branches will violate that; - quality manager has callbacks for ProcessInput()/ProcessOutput() that are visible to applications, I believe those are blocking and again might differ if we introduce additional queues; - async transforms require additional resolution steps to enable them, and additional flag to enumerate. Is there an evidence that the loader enumerates async transforms automatically and sets them up? - resolved topology contains references to transform objects, which in this case will be wrappers. This is clearly visible to client application;
Having support for asynchronous transforms would be nice, but my concerns are about compatibility:
- there is a documented way to assign queues to branches with MF_TOPONODE_WORKQUEUE_ID. I imagine that having async processing on the same branches will violate that;
As far as I can see this only applies to source nodes, although maybe it applies to all the downstream chain? I'm also not sure to understand how it matters what happens internally. Decoders might already create internal threads to process frames in parallel. What matters is that the samples are then delivered to the client from the proper queues.
- quality manager has callbacks for ProcessInput()/ProcessOutput() that are visible to applications, I believe those are blocking and again might differ if we introduce additional queues;
Although it might be interesting to check how true async MFT behave if they are even supported, I think that here again we can simply consider an async MFT wrapper will behave as a synchronous one from the session perspective, except for the notifications.
The session will still call ProcessInput / ProcessOutput, and notify the quality manager about it synchronously. If this blocks, then the session will not call ProcessOutput again until it returns. The async MFT wrapper might still continue to process some frames in background, but will eventually becomes idle as the session will not be pushing input either.
- async transforms require additional resolution steps to enable them, and additional flag to enumerate. Is there an evidence that the loader enumerates async transforms automatically and sets them up?
I don't know, I haven't done any test with actual async MFT. I don't even know if they are supported, and none of the builtin MFT I have seen so far are supporting the async mode.
- resolved topology contains references to transform objects, which in this case will be wrappers. This is clearly visible to client application;
The wrapper is only kept in the session topo_node, the topology still has the original transform object.
This is indeed not how native behaves. Probably better to consider https://gitlab.winehq.org/wine/wine/-/merge_requests/3245 for now, as builtin MFT definitely don't implement async MFT behavior (it looks like the session tries to query some internal IID on the transforms, but it's not clear how to leverage that).
This merge request was closed by Rémi Bernon.