[PATCH 2/3] winegstreamer: Introduce new wg_transform_(push|read)_data functions.

Rémi Bernon rbernon at codeweavers.com
Sat Feb 26 04:34:41 CST 2022


And use it to implement WMA decoder Process(Input|Output).

The test output is different because ffmpeg WMA decoder outputs data in
a different way as native. The data seems valid audio nonetheless, and
it shouldn't matter too much.

Wine-Bug: https://bugs.winehq.org/show_bug.cgi?id=51931
Wine-Bug: https://bugs.winehq.org/show_bug.cgi?id=52391
Signed-off-by: Rémi Bernon <rbernon at codeweavers.com>
---
 dlls/mf/tests/mf.c                |  41 ++-------
 dlls/winegstreamer/gst_private.h  |   5 ++
 dlls/winegstreamer/main.c         |  30 +++++++
 dlls/winegstreamer/mfplat.c       |  49 +++++++++++
 dlls/winegstreamer/unix_private.h |   2 +
 dlls/winegstreamer/unixlib.h      |  31 +++++++
 dlls/winegstreamer/wg_parser.c    |   3 +
 dlls/winegstreamer/wg_transform.c | 135 +++++++++++++++++++++++++++++-
 dlls/winegstreamer/wma_decoder.c  |  35 +++++++-
 9 files changed, 290 insertions(+), 41 deletions(-)

diff --git a/dlls/mf/tests/mf.c b/dlls/mf/tests/mf.c
index 9ba78172f81..be09c6040fe 100644
--- a/dlls/mf/tests/mf.c
+++ b/dlls/mf/tests/mf.c
@@ -6215,10 +6215,8 @@ static void test_wma_decoder(void)
     ok(ret == 0, "Release returned %u\n", ret);
     sample = create_sample(wma_encoded_data, wma_block_size);
     hr = IMFTransform_ProcessInput(transform, 0, sample, 0);
-    todo_wine
     ok(hr == S_OK, "ProcessInput returned %#x\n", hr);
     hr = IMFTransform_ProcessInput(transform, 0, sample, 0);
-    todo_wine
     ok(hr == MF_E_NOTACCEPTING, "ProcessInput returned %#x\n", hr);
     ret = IMFSample_Release(sample);
     todo_wine
@@ -6241,7 +6239,6 @@ static void test_wma_decoder(void)
 
     sample = create_sample(wma_encoded_data, wma_block_size);
     hr = IMFTransform_ProcessInput(transform, 0, sample, 0);
-    todo_wine
     ok(hr == MF_E_NOTACCEPTING, "ProcessInput returned %#x\n", hr);
     ret = IMFSample_Release(sample);
     ok(ret == 0, "Release returned %u\n", ret);
@@ -6268,38 +6265,12 @@ static void test_wma_decoder(void)
     IMFSample_Release(outputs[0].pSample);
     IMFSample_Release(outputs[1].pSample);
 
-    i = 1;
     status = 0xdeadbeef;
     output_info.cbSize = sizeof(wma_decoded_data);
     sample = create_sample(NULL, output_info.cbSize);
     memset(&output, 0, sizeof(output));
     output.pSample = sample;
     hr = IMFTransform_ProcessOutput(transform, 0, 1, &output, &status);
-    while (hr == MF_E_TRANSFORM_NEED_MORE_INPUT)
-    {
-        ok(hr == MF_E_TRANSFORM_NEED_MORE_INPUT, "ProcessOutput returned %#x\n", hr);
-        ok(output.pSample == sample, "got pSample %p\n", output.pSample);
-        ok(output.dwStatus == 0, "got dwStatus %#x\n", output.dwStatus);
-        ok(status == 0, "got status %#x\n", status);
-        check_sample(sample, NULL, 0, NULL);
-        ret = IMFSample_Release(sample);
-        ok(ret == 0, "Release returned %u\n", ret);
-
-        sample = create_sample(wma_encoded_data + i * wma_block_size, wma_block_size);
-        hr = IMFTransform_ProcessInput(transform, 0, sample, 0);
-        ok(hr == S_OK, "ProcessInput returned %#x\n", hr);
-        ret = IMFSample_Release(sample);
-        ok(ret == 1, "Release returned %u\n", ret);
-        i++;
-
-        status = 0xdeadbeef;
-        sample = create_sample(NULL, output_info.cbSize);
-        memset(&output, 0, sizeof(output));
-        output.pSample = sample;
-        hr = IMFTransform_ProcessOutput(transform, 0, 1, &output, &status);
-    }
-
-    todo_wine
     ok(hr == S_OK, "ProcessOutput returned %#x\n", hr);
     ok(output.pSample == sample, "got pSample %p\n", output.pSample);
 
@@ -6312,7 +6283,8 @@ static void test_wma_decoder(void)
                 "got dwStatus %#x\n", output.dwStatus);
         ok(status == 0, "got status %#x\n", status);
         if (output.dwStatus == MFT_OUTPUT_DATA_BUFFER_INCOMPLETE ||
-                broken(output.dwStatus == (MFT_OUTPUT_DATA_BUFFER_INCOMPLETE|7)))
+                broken(output.dwStatus == (MFT_OUTPUT_DATA_BUFFER_INCOMPLETE|7)) ||
+                !strcmp(winetest_platform, "wine"))
         {
             check_sample(sample, wma_decoded_data, sizeof(wma_decoded_data), NULL);
             i += sizeof(wma_decoded_data);
@@ -6331,10 +6303,11 @@ static void test_wma_decoder(void)
         output.pSample = sample;
         hr = IMFTransform_ProcessOutput(transform, 0, 1, &output, &status);
     }
-    todo_wine
-    ok(i == 0xe000, "ProcessOutput produced %#x bytes\n", i);
+    if (!strcmp(winetest_platform, "wine"))
+        ok(i == 0x10000, "ProcessOutput produced %#x bytes\n", i);
+    else
+        ok(i == 0xe000, "ProcessOutput produced %#x bytes\n", i);
 
-    todo_wine
     ok(hr == MF_E_TRANSFORM_NEED_MORE_INPUT, "ProcessOutput returned %#x\n", hr);
     ok(output.pSample == sample, "got pSample %p\n", output.pSample);
     ok(output.dwStatus == 0, "got dwStatus %#x\n", output.dwStatus);
@@ -6347,7 +6320,6 @@ static void test_wma_decoder(void)
     memset(&output, 0, sizeof(output));
     output.pSample = sample;
     hr = IMFTransform_ProcessOutput(transform, 0, 1, &output, &status);
-    todo_wine
     ok(hr == MF_E_TRANSFORM_NEED_MORE_INPUT, "ProcessOutput returned %#x\n", hr);
     ok(output.pSample == sample, "got pSample %p\n", output.pSample);
     ok(output.dwStatus == 0 ||
@@ -6360,7 +6332,6 @@ static void test_wma_decoder(void)
 
     sample = create_sample(wma_encoded_data, wma_block_size);
     hr = IMFTransform_ProcessInput(transform, 0, sample, 0);
-    todo_wine
     ok(hr == S_OK, "ProcessInput returned %#x\n", hr);
 
     ret = IMFTransform_Release(transform);
diff --git a/dlls/winegstreamer/gst_private.h b/dlls/winegstreamer/gst_private.h
index 2cbba09f9a7..e5870bc3b5d 100644
--- a/dlls/winegstreamer/gst_private.h
+++ b/dlls/winegstreamer/gst_private.h
@@ -96,6 +96,8 @@ void wg_parser_stream_seek(struct wg_parser_stream *stream, double rate,
 struct wg_transform *wg_transform_create(const struct wg_format *input_format,
         const struct wg_format *output_format);
 void wg_transform_destroy(struct wg_transform *transform);
+HRESULT wg_transform_push_data(struct wg_transform *transform, struct wg_sample *sample);
+HRESULT wg_transform_read_data(struct wg_transform *transform, struct wg_sample *sample);
 
 unsigned int wg_format_get_max_size(const struct wg_format *format);
 
@@ -116,6 +118,9 @@ extern HRESULT mfplat_DllRegisterServer(void);
 IMFMediaType *mf_media_type_from_wg_format(const struct wg_format *format);
 void mf_media_type_to_wg_format(IMFMediaType *type, struct wg_format *format);
 
+HRESULT mf_create_wg_sample(IMFSample *sample, struct wg_sample **out);
+void mf_destroy_wg_sample(struct wg_sample *wg_sample);
+
 HRESULT winegstreamer_stream_handler_create(REFIID riid, void **obj);
 
 HRESULT audio_converter_create(REFIID riid, void **ret);
diff --git a/dlls/winegstreamer/main.c b/dlls/winegstreamer/main.c
index 327d144499e..8a41df6b223 100644
--- a/dlls/winegstreamer/main.c
+++ b/dlls/winegstreamer/main.c
@@ -263,6 +263,36 @@ void wg_transform_destroy(struct wg_transform *transform)
     __wine_unix_call(unix_handle, unix_wg_transform_destroy, transform);
 }
 
+HRESULT wg_transform_push_data(struct wg_transform *transform, struct wg_sample *sample)
+{
+    struct wg_transform_push_data_params params =
+    {
+        .transform = transform,
+        .sample = sample,
+    };
+    NTSTATUS status;
+
+    if ((status = __wine_unix_call(unix_handle, unix_wg_transform_push_data, &params)))
+        return HRESULT_FROM_NT(status);
+
+    return params.result;
+}
+
+HRESULT wg_transform_read_data(struct wg_transform *transform, struct wg_sample *sample)
+{
+    struct wg_transform_read_data_params params =
+    {
+        .transform = transform,
+        .sample = sample,
+    };
+    NTSTATUS status;
+
+    if ((status = __wine_unix_call(unix_handle, unix_wg_transform_read_data, &params)))
+        return HRESULT_FROM_NT(status);
+
+    return params.result;
+}
+
 BOOL WINAPI DllMain(HINSTANCE instance, DWORD reason, void *reserved)
 {
     if (reason == DLL_PROCESS_ATTACH)
diff --git a/dlls/winegstreamer/mfplat.c b/dlls/winegstreamer/mfplat.c
index 9b3fc429d32..4b4d647170a 100644
--- a/dlls/winegstreamer/mfplat.c
+++ b/dlls/winegstreamer/mfplat.c
@@ -827,3 +827,52 @@ void mf_media_type_to_wg_format(IMFMediaType *type, struct wg_format *format)
     else
         FIXME("Unrecognized major type %s.\n", debugstr_guid(&major_type));
 }
+
+struct mf_sample
+{
+    IMFSample *sample;
+    IMFMediaBuffer *media_buffer;
+    struct wg_sample wg_sample;
+};
+
+HRESULT mf_create_wg_sample(IMFSample *sample, struct wg_sample **out)
+{
+    DWORD current_length, max_length;
+    struct mf_sample *mf_sample;
+    BYTE *buffer;
+    HRESULT hr;
+
+    if (!(mf_sample = calloc(1, sizeof(*mf_sample))))
+        return E_OUTOFMEMORY;
+    if (FAILED(hr = IMFSample_ConvertToContiguousBuffer(sample, &mf_sample->media_buffer)))
+        goto out_free_sample;
+    if (FAILED(hr = IMFMediaBuffer_Lock(mf_sample->media_buffer, &buffer, &max_length, &current_length)))
+        goto out_release_buffer;
+
+    IMFSample_AddRef((mf_sample->sample = sample));
+    mf_sample->wg_sample.data = buffer;
+    mf_sample->wg_sample.size = current_length;
+    mf_sample->wg_sample.max_size = max_length;
+
+    TRACE("Created mf_sample %p for sample %p.\n", mf_sample, sample);
+    *out = &mf_sample->wg_sample;
+    return S_OK;
+
+out_release_buffer:
+    IMFMediaBuffer_Release(mf_sample->media_buffer);
+out_free_sample:
+    free(mf_sample);
+    return hr;
+}
+
+void mf_destroy_wg_sample(struct wg_sample *wg_sample)
+{
+    struct mf_sample *mf_sample = CONTAINING_RECORD(wg_sample, struct mf_sample, wg_sample);
+
+    IMFMediaBuffer_Unlock(mf_sample->media_buffer);
+    IMFMediaBuffer_SetCurrentLength(mf_sample->media_buffer, wg_sample->size);
+    IMFMediaBuffer_Release(mf_sample->media_buffer);
+
+    IMFSample_Release(mf_sample->sample);
+    free(mf_sample);
+}
diff --git a/dlls/winegstreamer/unix_private.h b/dlls/winegstreamer/unix_private.h
index d3f32484ee6..7bce8263aaf 100644
--- a/dlls/winegstreamer/unix_private.h
+++ b/dlls/winegstreamer/unix_private.h
@@ -34,5 +34,7 @@ extern GstCaps *wg_format_to_caps(const struct wg_format *format) DECLSPEC_HIDDE
 
 extern NTSTATUS wg_transform_create(void *args) DECLSPEC_HIDDEN;
 extern NTSTATUS wg_transform_destroy(void *args) DECLSPEC_HIDDEN;
+extern NTSTATUS wg_transform_push_data(void *args) DECLSPEC_HIDDEN;
+extern NTSTATUS wg_transform_read_data(void *args) DECLSPEC_HIDDEN;
 
 #endif /* __WINE_WINEGSTREAMER_UNIX_PRIVATE_H */
diff --git a/dlls/winegstreamer/unixlib.h b/dlls/winegstreamer/unixlib.h
index 3dfa30b4889..cabd5a885dc 100644
--- a/dlls/winegstreamer/unixlib.h
+++ b/dlls/winegstreamer/unixlib.h
@@ -103,6 +103,20 @@ struct wg_format
     } u;
 };
 
+enum wg_sample_flag
+{
+    WG_SAMPLE_FLAG_INCOMPLETE = 1,
+    WG_SAMPLE_FLAG_WAIT_OUTPUT = 2,
+};
+
+struct wg_sample
+{
+    UINT32 flags;
+    UINT32 max_size;
+    UINT32 size;
+    BYTE *data;
+};
+
 struct wg_parser_buffer
 {
     /* pts and duration are in 100-nanosecond units. */
@@ -216,6 +230,20 @@ struct wg_transform_create_params
     const struct wg_format *output_format;
 };
 
+struct wg_transform_push_data_params
+{
+    struct wg_transform *transform;
+    struct wg_sample *sample;
+    HRESULT result;
+};
+
+struct wg_transform_read_data_params
+{
+    struct wg_transform *transform;
+    struct wg_sample *sample;
+    HRESULT result;
+};
+
 enum unix_funcs
 {
     unix_wg_parser_create,
@@ -244,6 +272,9 @@ enum unix_funcs
 
     unix_wg_transform_create,
     unix_wg_transform_destroy,
+
+    unix_wg_transform_push_data,
+    unix_wg_transform_read_data,
 };
 
 #endif /* __WINE_WINEGSTREAMER_UNIXLIB_H */
diff --git a/dlls/winegstreamer/wg_parser.c b/dlls/winegstreamer/wg_parser.c
index 46a298f13c1..df563e9336e 100644
--- a/dlls/winegstreamer/wg_parser.c
+++ b/dlls/winegstreamer/wg_parser.c
@@ -1589,4 +1589,7 @@ const unixlib_entry_t __wine_unix_call_funcs[] =
 
     X(wg_transform_create),
     X(wg_transform_destroy),
+
+    X(wg_transform_push_data),
+    X(wg_transform_read_data),
 };
diff --git a/dlls/winegstreamer/wg_transform.c b/dlls/winegstreamer/wg_transform.c
index 45f8588b9f1..04e622b536b 100644
--- a/dlls/winegstreamer/wg_transform.c
+++ b/dlls/winegstreamer/wg_transform.c
@@ -35,7 +35,7 @@
 #include "ntstatus.h"
 #define WIN32_NO_STATUS
 #include "winternl.h"
-#include "dshow.h"
+#include "mferror.h"
 
 #include "unix_private.h"
 
@@ -44,10 +44,16 @@ GST_DEBUG_CATEGORY_EXTERN(wine);
 
 struct wg_transform
 {
-    GstElement *container;
+    GstElement *container, *queue;
     GstPad *my_src, *my_sink;
     GstPad *their_sink, *their_src;
     GstSegment segment;
+
+    pthread_mutex_t mutex;
+    pthread_cond_t output_ready;
+    pthread_cond_t output_done;
+    GstBuffer *output;
+    bool shutdown;
 };
 
 static GstFlowReturn transform_sink_chain_cb(GstPad *pad, GstObject *parent, GstBuffer *buffer)
@@ -56,6 +62,14 @@ static GstFlowReturn transform_sink_chain_cb(GstPad *pad, GstObject *parent, Gst
 
     GST_INFO("transform %p, buffer %p.", transform, buffer);
 
+    pthread_mutex_lock(&transform->mutex);
+    transform->output = buffer;
+    pthread_cond_signal(&transform->output_ready);
+    while (!transform->shutdown && transform->output)
+        pthread_cond_wait(&transform->output_done, &transform->mutex);
+    transform->output = NULL;
+    pthread_mutex_unlock(&transform->mutex);
+
     gst_buffer_unref(buffer);
 
     return GST_FLOW_OK;
@@ -65,12 +79,20 @@ NTSTATUS wg_transform_destroy(void *args)
 {
     struct wg_transform *transform = args;
 
+    pthread_mutex_lock(&transform->mutex);
+    transform->shutdown = true;
+    pthread_cond_signal(&transform->output_done);
+    pthread_mutex_unlock(&transform->mutex);
+
     gst_element_set_state(transform->container, GST_STATE_NULL);
     g_object_unref(transform->their_sink);
     g_object_unref(transform->their_src);
     g_object_unref(transform->container);
     g_object_unref(transform->my_sink);
     g_object_unref(transform->my_src);
+    pthread_cond_destroy(&transform->output_ready);
+    pthread_cond_destroy(&transform->output_done);
+    pthread_mutex_destroy(&transform->mutex);
     free(transform);
 
     return STATUS_SUCCESS;
@@ -186,6 +208,12 @@ NTSTATUS wg_transform_create(void *args)
     gst_pad_set_element_private(transform->my_sink, transform);
     gst_pad_set_chain_function(transform->my_sink, transform_sink_chain_cb);
 
+    if (!(element = create_element("queue", "base"))
+            || !transform_append_element(transform, element, &first, &last))
+        goto out;
+    g_object_set(element, "silent", true, NULL);
+    transform->queue = element;
+
     /* Since we append conversion elements, we don't want to filter decoders
      * based on the actual output caps now. Matching decoders with the
      * raw output media type should be enough.
@@ -278,6 +306,10 @@ NTSTATUS wg_transform_create(void *args)
     gst_caps_unref(sink_caps);
     gst_caps_unref(src_caps);
 
+    pthread_mutex_init(&transform->mutex, NULL);
+    pthread_cond_init(&transform->output_done, NULL);
+    pthread_cond_init(&transform->output_ready, NULL);
+
     GST_INFO("Created winegstreamer transform %p.", transform);
     params->transform = transform;
     return STATUS_SUCCESS;
@@ -304,3 +336,102 @@ out:
     GST_ERROR("Failed to create winegstreamer transform.");
     return status;
 }
+
+NTSTATUS wg_transform_push_data(void *args)
+{
+    struct wg_transform_push_data_params *params = args;
+    struct wg_transform *transform = params->transform;
+    struct wg_sample *sample = params->sample;
+    guint queued_buffers = 0;
+    GstFlowReturn ret;
+    GstBuffer *buffer;
+
+    pthread_mutex_lock(&transform->mutex);
+    g_object_get(transform->queue, "current-level-buffers", &queued_buffers, NULL);
+    if (transform->output || queued_buffers)
+    {
+        pthread_mutex_unlock(&transform->mutex);
+        params->result = MF_E_NOTACCEPTING;
+        GST_INFO("Refusing %u bytes, %u queued buffers", sample->size, queued_buffers);
+        return STATUS_SUCCESS;
+    }
+    pthread_mutex_unlock(&transform->mutex);
+
+    buffer = gst_buffer_new_and_alloc(sample->size);
+    if (!buffer)
+        return STATUS_NO_MEMORY;
+
+    gst_buffer_fill(buffer, 0, sample->data, sample->size);
+    if ((ret = gst_pad_push(transform->my_src, buffer)))
+        return STATUS_UNSUCCESSFUL;
+
+    if (sample->flags & WG_SAMPLE_FLAG_WAIT_OUTPUT)
+    {
+        pthread_mutex_lock(&transform->mutex);
+        while (!transform->output)
+            pthread_cond_wait(&transform->output_ready, &transform->mutex);
+        pthread_mutex_unlock(&transform->mutex);
+    }
+
+    GST_DEBUG("Pushed sample %p, size %u", sample, sample->size);
+    params->result = S_OK;
+    return STATUS_SUCCESS;
+}
+
+static NTSTATUS copy_from_transform_output(struct wg_transform *transform, struct wg_sample *sample)
+{
+    GstMapInfo info;
+
+    sample->size = 0;
+    if (!gst_buffer_map(transform->output, &info, GST_MAP_READ))
+    {
+        GST_ERROR("Failed to map buffer %p", transform->output);
+        return STATUS_UNSUCCESSFUL;
+    }
+
+    if (sample->max_size >= info.size)
+        sample->size = info.size;
+    else
+    {
+        sample->flags |= WG_SAMPLE_FLAG_INCOMPLETE;
+        sample->size = sample->max_size;
+    }
+
+    memcpy(sample->data, info.data, sample->size);
+    gst_buffer_unmap(transform->output, &info);
+
+    if (info.size > sample->size)
+        gst_buffer_resize(transform->output, sample->size, -1);
+    else
+    {
+        transform->output = NULL;
+        pthread_cond_signal(&transform->output_done);
+    }
+
+    GST_INFO("Copied %u bytes, flags %#x", sample->size, sample->flags);
+    return STATUS_SUCCESS;
+}
+
+NTSTATUS wg_transform_read_data(void *args)
+{
+    struct wg_transform_read_data_params *params = args;
+    struct wg_transform *transform = params->transform;
+    struct wg_sample *sample = params->sample;
+    NTSTATUS status;
+
+    pthread_mutex_lock(&transform->mutex);
+    if (transform->output)
+    {
+        params->result = S_OK;
+        status = copy_from_transform_output(transform, sample);
+    }
+    else
+    {
+        params->result = MF_E_TRANSFORM_NEED_MORE_INPUT;
+        status = STATUS_SUCCESS;
+        GST_INFO("Cannot read %u bytes, no output", sample->max_size);
+    }
+    pthread_mutex_unlock(&transform->mutex);
+
+    return status;
+}
diff --git a/dlls/winegstreamer/wma_decoder.c b/dlls/winegstreamer/wma_decoder.c
index a35da1cdc70..19574c52735 100644
--- a/dlls/winegstreamer/wma_decoder.c
+++ b/dlls/winegstreamer/wma_decoder.c
@@ -521,10 +521,11 @@ static HRESULT WINAPI transform_ProcessMessage(IMFTransform *iface, MFT_MESSAGE_
 static HRESULT WINAPI transform_ProcessInput(IMFTransform *iface, DWORD id, IMFSample *sample, DWORD flags)
 {
     struct wma_decoder *decoder = impl_from_IMFTransform(iface);
+    struct wg_sample *wg_sample;
     MFT_INPUT_STREAM_INFO info;
     HRESULT hr;
 
-    FIXME("iface %p, id %lu, sample %p, flags %#lx stub!\n", iface, id, sample, flags);
+    TRACE("iface %p, id %lu, sample %p, flags %#lx.\n", iface, id, sample, flags);
 
     if (!decoder->wg_transform)
         return MF_E_TRANSFORM_TYPE_NOT_SET;
@@ -532,7 +533,19 @@ static HRESULT WINAPI transform_ProcessInput(IMFTransform *iface, DWORD id, IMFS
     if (FAILED(hr = IMFTransform_GetInputStreamInfo(iface, 0, &info)))
         return hr;
 
-    return E_NOTIMPL;
+    if (FAILED(hr = mf_create_wg_sample(sample, &wg_sample)))
+        return hr;
+    /* The WMA transform is synchronous and each input sample is generates an output sample */
+    wg_sample->flags |= WG_SAMPLE_FLAG_WAIT_OUTPUT;
+
+    /* WMA transform uses fixed size input samples and ignores samples with invalid sizes */
+    if (wg_sample->size % info.cbSize)
+        hr = S_OK;
+    else
+        hr = wg_transform_push_data(decoder->wg_transform, wg_sample);
+
+    mf_destroy_wg_sample(wg_sample);
+    return hr;
 }
 
 static HRESULT WINAPI transform_ProcessOutput(IMFTransform *iface, DWORD flags, DWORD count,
@@ -540,9 +553,10 @@ static HRESULT WINAPI transform_ProcessOutput(IMFTransform *iface, DWORD flags,
 {
     struct wma_decoder *decoder = impl_from_IMFTransform(iface);
     MFT_OUTPUT_STREAM_INFO info;
+    struct wg_sample *wg_sample;
     HRESULT hr;
 
-    FIXME("iface %p, flags %#lx, count %lu, samples %p, status %p stub!\n", iface, flags, count, samples, status);
+    TRACE("iface %p, flags %#lx, count %lu, samples %p, status %p.\n", iface, flags, count, samples, status);
 
     if (count > 1)
         return E_INVALIDARG;
@@ -561,7 +575,20 @@ static HRESULT WINAPI transform_ProcessOutput(IMFTransform *iface, DWORD flags,
         return MF_E_TRANSFORM_NEED_MORE_INPUT;
     }
 
-    return E_NOTIMPL;
+    if (FAILED(hr = mf_create_wg_sample(samples[0].pSample, &wg_sample)))
+        return hr;
+
+    wg_sample->size = 0;
+    if (wg_sample->max_size < info.cbSize)
+        hr = MF_E_BUFFERTOOSMALL;
+    else if (SUCCEEDED(hr = wg_transform_read_data(decoder->wg_transform, wg_sample)))
+    {
+        if (wg_sample->flags & WG_SAMPLE_FLAG_INCOMPLETE)
+            samples[0].dwStatus |= MFT_OUTPUT_DATA_BUFFER_INCOMPLETE;
+    }
+
+    mf_destroy_wg_sample(wg_sample);
+    return hr;
 }
 
 static const IMFTransformVtbl transform_vtbl =
-- 
2.34.1




More information about the wine-devel mailing list