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

Rémi Bernon rbernon at codeweavers.com
Thu Mar 3 05:30:11 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                |  50 ++++---------
 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      |  30 ++++++++
 dlls/winegstreamer/wg_parser.c    |   3 +
 dlls/winegstreamer/wg_transform.c | 119 +++++++++++++++++++++++++++++-
 dlls/winegstreamer/wma_decoder.c  |  33 ++++++++-
 9 files changed, 278 insertions(+), 43 deletions(-)

diff --git a/dlls/mf/tests/mf.c b/dlls/mf/tests/mf.c
index 0ab46445d9a..6462594dae2 100644
--- a/dlls/mf/tests/mf.c
+++ b/dlls/mf/tests/mf.c
@@ -6205,22 +6205,18 @@ static void test_wma_decoder(void)
 
     sample = create_sample(wma_encoded_data, wma_block_size / 2);
     hr = IMFTransform_ProcessInput(transform, 0, sample, 0);
-    todo_wine
     ok(hr == S_OK, "ProcessInput returned %#x\n", hr);
     ret = IMFSample_Release(sample);
     ok(ret == 0, "Release returned %u\n", ret);
     sample = create_sample(wma_encoded_data, wma_block_size + 1);
     hr = IMFTransform_ProcessInput(transform, 0, sample, 0);
-    todo_wine
     ok(hr == S_OK, "ProcessInput returned %#x\n", hr);
     ret = IMFSample_Release(sample);
     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
@@ -6243,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);
@@ -6270,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);
 
@@ -6321,8 +6290,20 @@ static void test_wma_decoder(void)
         }
         else
         {
-            check_sample(sample, wma_decoded_data, sizeof(wma_decoded_data) / 2, NULL);
-            i += sizeof(wma_decoded_data) / 2;
+            DWORD length;
+
+            /* FFmpeg doesn't seem to decode WMA buffers in the same way as native */
+
+            hr = IMFSample_GetTotalLength(sample, &length);
+            ok(hr == S_OK, "GetTotalLength returned %#x\n", hr);
+            todo_wine
+            ok(length == sizeof(wma_decoded_data) / 2, "got length %u\n", length);
+
+            if (length == sizeof(wma_decoded_data) / 2)
+            {
+                check_sample(sample, wma_decoded_data, sizeof(wma_decoded_data) / 2, NULL);
+                i += sizeof(wma_decoded_data) / 2;
+            }
         }
         ret = IMFSample_Release(sample);
         ok(ret == 0, "Release returned %u\n", ret);
@@ -6336,7 +6317,6 @@ static void test_wma_decoder(void)
     todo_wine
     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);
@@ -6349,7 +6329,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 ||
@@ -6362,7 +6341,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..82e926d673d 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;
+    if (FAILED(hr = IMFMediaBuffer_Lock(mf_sample->media_buffer, &buffer, &max_length, &current_length)))
+        goto out;
+
+    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:
+    if (mf_sample->media_buffer)
+        IMFMediaBuffer_Release(mf_sample->media_buffer);
+    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..e1c0ec3f3b7 100644
--- a/dlls/winegstreamer/unixlib.h
+++ b/dlls/winegstreamer/unixlib.h
@@ -103,6 +103,19 @@ struct wg_format
     } u;
 };
 
+enum wg_sample_flag
+{
+    WG_SAMPLE_FLAG_INCOMPLETE = 1,
+};
+
+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 +229,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 +271,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..6e272634653 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"
 
@@ -48,15 +48,24 @@ struct wg_transform
     GstPad *my_src, *my_sink;
     GstPad *their_sink, *their_src;
     GstSegment segment;
+    GstBuffer *input;
+
+    pthread_mutex_t mutex;
+    GstBuffer *output;
 };
 
 static GstFlowReturn transform_sink_chain_cb(GstPad *pad, GstObject *parent, GstBuffer *buffer)
 {
     struct wg_transform *transform = gst_pad_get_element_private(pad);
 
-    GST_INFO("transform %p, buffer %p.", transform, buffer);
+    GST_LOG("transform %p, buffer %p.", transform, buffer);
 
-    gst_buffer_unref(buffer);
+    pthread_mutex_lock(&transform->mutex);
+    if (transform->output)
+        transform->output = gst_buffer_append(transform->output, buffer);
+    else
+        transform->output = buffer;
+    pthread_mutex_unlock(&transform->mutex);
 
     return GST_FLOW_OK;
 }
@@ -65,12 +74,18 @@ NTSTATUS wg_transform_destroy(void *args)
 {
     struct wg_transform *transform = args;
 
+    if (transform->input)
+        gst_buffer_unref(transform->input);
+    if (transform->output)
+        gst_buffer_unref(transform->output);
+
     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_mutex_destroy(&transform->mutex);
     free(transform);
 
     return STATUS_SUCCESS;
@@ -278,6 +293,8 @@ NTSTATUS wg_transform_create(void *args)
     gst_caps_unref(sink_caps);
     gst_caps_unref(src_caps);
 
+    pthread_mutex_init(&transform->mutex, NULL);
+
     GST_INFO("Created winegstreamer transform %p.", transform);
     params->transform = transform;
     return STATUS_SUCCESS;
@@ -304,3 +321,99 @@ 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;
+    GstBuffer *buffer;
+
+    if (transform->input)
+    {
+        GST_INFO("Refusing %u bytes, a buffer is already queued", sample->size);
+        params->result = MF_E_NOTACCEPTING;
+        return STATUS_SUCCESS;
+    }
+
+    if (!(buffer = gst_buffer_new_and_alloc(sample->size)))
+    {
+        GST_ERROR("Failed to allocate input buffer");
+        return STATUS_NO_MEMORY;
+    }
+    gst_buffer_fill(buffer, 0, sample->data, sample->size);
+    transform->input = buffer;
+
+    GST_INFO("Copied %u bytes from sample %p to input buffer", sample->size, sample);
+    params->result = S_OK;
+    return STATUS_SUCCESS;
+}
+
+static NTSTATUS read_transform_output_data(struct wg_transform *transform,
+        struct wg_sample *sample)
+{
+    GstBuffer *buffer = transform->output;
+    GstMapInfo info;
+
+    if (!gst_buffer_map(buffer, &info, GST_MAP_READ))
+    {
+        GST_ERROR("Failed to map buffer %p", buffer);
+        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(buffer, &info);
+    gst_buffer_resize(buffer, sample->size, -1);
+
+    if (info.size <= sample->size)
+    {
+        gst_buffer_unref(transform->output);
+        transform->output = NULL;
+    }
+
+    GST_INFO("Copied %u bytes, sample %p, flags %#x", sample->size, sample, 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;
+    GstFlowReturn ret;
+    NTSTATUS status;
+
+    if (!transform->input)
+        GST_DEBUG("Not input buffer queued");
+    else if ((ret = gst_pad_push(transform->my_src, transform->input)))
+    {
+        GST_ERROR("Failed to push transform input, error %d", ret);
+        return STATUS_UNSUCCESSFUL;
+    }
+    transform->input = NULL;
+
+    sample->size = 0;
+    pthread_mutex_lock(&transform->mutex);
+    if (transform->output)
+    {
+        params->result = S_OK;
+        status = read_transform_output_data(transform, sample);
+    }
+    else
+    {
+        params->result = MF_E_TRANSFORM_NEED_MORE_INPUT;
+        status = STATUS_SUCCESS;
+        GST_INFO("Cannot read %u bytes, no output available", 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..6ee43e19414 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,17 @@ 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;
+
+    /* 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 +551,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 +573,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