Checking that WMA decoder ignores any incorrectly sized input sample.
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@codeweavers.com ---
With this series WMA decoding should work again, the last patch is required by FAudio which calls ProcessMessage.
It's not completely clear if we really need to support any message, and although we should maybe support draining at some point but I don't think it's required at the moment.
dlls/mf/tests/mf.c | 25 +++++++++++++----------- dlls/winegstreamer/wma_decoder.c | 33 ++++++++++++++++++++++++++++++++ 2 files changed, 47 insertions(+), 11 deletions(-)
diff --git a/dlls/mf/tests/mf.c b/dlls/mf/tests/mf.c index e61442bb512..9ba78172f81 100644 --- a/dlls/mf/tests/mf.c +++ b/dlls/mf/tests/mf.c @@ -6031,6 +6031,7 @@ static void test_wma_decoder(void) MFT_REGISTER_TYPE_INFO input_type = {MFMediaType_Audio, MFAudioFormat_WMAudioV8}; MFT_REGISTER_TYPE_INFO output_type = {MFMediaType_Audio, MFAudioFormat_Float}; MFT_OUTPUT_STREAM_INFO output_info; + MFT_OUTPUT_DATA_BUFFER outputs[2]; MFT_INPUT_STREAM_INFO input_info; MFT_OUTPUT_DATA_BUFFER output; const BYTE *wma_encoded_data; @@ -6204,13 +6205,11 @@ 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, wma_block_size - wma_block_size / 2); + 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); @@ -6231,16 +6230,13 @@ static void test_wma_decoder(void) status = 0xdeadbeef; memset(&output, 0, sizeof(output)); 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.dwStreamID == 0, "got dwStreamID %u\n", output.dwStreamID); ok(!output.pSample, "got pSample %p\n", output.pSample); - todo_wine ok(output.dwStatus == MFT_OUTPUT_DATA_BUFFER_NO_SAMPLE || broken(output.dwStatus == (MFT_OUTPUT_DATA_BUFFER_INCOMPLETE|MFT_OUTPUT_DATA_BUFFER_NO_SAMPLE)) /* Win7 */, "got dwStatus %#x\n", output.dwStatus); ok(!output.pEvents, "got pEvents %p\n", output.pEvents); - todo_wine ok(status == 0, "got status %#x\n", status);
sample = create_sample(wma_encoded_data, wma_block_size); @@ -6253,16 +6249,25 @@ static void test_wma_decoder(void) status = 0xdeadbeef; memset(&output, 0, sizeof(output)); 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, "got pSample %p\n", output.pSample); - todo_wine ok(output.dwStatus == MFT_OUTPUT_DATA_BUFFER_NO_SAMPLE || broken(output.dwStatus == (MFT_OUTPUT_DATA_BUFFER_INCOMPLETE|MFT_OUTPUT_DATA_BUFFER_NO_SAMPLE)) /* Win7 */, "got dwStatus %#x\n", output.dwStatus); - todo_wine ok(status == 0, "got status %#x\n", status);
+ status = 0xdeadbeef; + memset(&output, 0, sizeof(output)); + output_info.cbSize = sizeof(wma_decoded_data); + sample = create_sample(NULL, output_info.cbSize); + outputs[0].pSample = sample; + sample = create_sample(NULL, output_info.cbSize); + outputs[1].pSample = sample; + hr = IMFTransform_ProcessOutput(transform, 0, 2, outputs, &status); + ok(hr == E_INVALIDARG, "ProcessOutput returned %#x\n", hr); + IMFSample_Release(outputs[0].pSample); + IMFSample_Release(outputs[1].pSample); + i = 1; status = 0xdeadbeef; output_info.cbSize = sizeof(wma_decoded_data); @@ -6333,7 +6338,6 @@ static void test_wma_decoder(void) 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); - todo_wine ok(status == 0, "got status %#x\n", status); ret = IMFSample_Release(sample); ok(ret == 0, "Release returned %u\n", ret); @@ -6349,7 +6353,6 @@ static void test_wma_decoder(void) ok(output.dwStatus == 0 || broken(output.dwStatus == (MFT_OUTPUT_DATA_BUFFER_INCOMPLETE|7)) /* Win7 */, "got dwStatus %#x\n", output.dwStatus); - todo_wine ok(status == 0, "got status %#x\n", status); check_sample(sample, NULL, 0, NULL); ret = IMFSample_Release(sample); diff --git a/dlls/winegstreamer/wma_decoder.c b/dlls/winegstreamer/wma_decoder.c index 6c198706944..a35da1cdc70 100644 --- a/dlls/winegstreamer/wma_decoder.c +++ b/dlls/winegstreamer/wma_decoder.c @@ -520,14 +520,47 @@ 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); + MFT_INPUT_STREAM_INFO info; + HRESULT hr; + FIXME("iface %p, id %lu, sample %p, flags %#lx stub!\n", iface, id, sample, flags); + + if (!decoder->wg_transform) + return MF_E_TRANSFORM_TYPE_NOT_SET; + + if (FAILED(hr = IMFTransform_GetInputStreamInfo(iface, 0, &info))) + return hr; + return E_NOTIMPL; }
static HRESULT WINAPI transform_ProcessOutput(IMFTransform *iface, DWORD flags, DWORD count, MFT_OUTPUT_DATA_BUFFER *samples, DWORD *status) { + struct wma_decoder *decoder = impl_from_IMFTransform(iface); + MFT_OUTPUT_STREAM_INFO info; + HRESULT hr; + FIXME("iface %p, flags %#lx, count %lu, samples %p, status %p stub!\n", iface, flags, count, samples, status); + + if (count > 1) + return E_INVALIDARG; + + if (!decoder->wg_transform) + return MF_E_TRANSFORM_TYPE_NOT_SET; + + if (FAILED(hr = IMFTransform_GetOutputStreamInfo(iface, 0, &info))) + return hr; + + *status = 0; + samples[0].dwStatus = 0; + if (!samples[0].pSample) + { + samples[0].dwStatus = MFT_OUTPUT_DATA_BUFFER_NO_SAMPLE; + return MF_E_TRANSFORM_NEED_MORE_INPUT; + } + return E_NOTIMPL; }
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@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, ¶ms))) + 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, ¶ms))) + 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, ¤t_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 =
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@codeweavers.com --- dlls/winegstreamer/wma_decoder.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/dlls/winegstreamer/wma_decoder.c b/dlls/winegstreamer/wma_decoder.c index 19574c52735..872a1c52354 100644 --- a/dlls/winegstreamer/wma_decoder.c +++ b/dlls/winegstreamer/wma_decoder.c @@ -515,7 +515,7 @@ static HRESULT WINAPI transform_ProcessEvent(IMFTransform *iface, DWORD id, IMFM static HRESULT WINAPI transform_ProcessMessage(IMFTransform *iface, MFT_MESSAGE_TYPE message, ULONG_PTR param) { FIXME("iface %p, message %#x, param %p stub!\n", iface, message, (void *)param); - return E_NOTIMPL; + return S_OK; }
static HRESULT WINAPI transform_ProcessInput(IMFTransform *iface, DWORD id, IMFSample *sample, DWORD flags)
Hi,
While running your changed tests, I think I found new failures. Being a bot and all I'm not very good at pattern recognition, so I might be wrong, but could you please double-check?
Full results can be found at: https://testbot.winehq.org/JobDetails.pl?Key=109308
Your paranoid android.
=== debian11 (32 bit report) ===
mf: mf.c:6208: Test failed: ProcessInput returned 0x80004001 mf.c:6213: Test failed: ProcessInput returned 0x80004001
=== debian11 (32 bit Arabic:Morocco report) ===
mf: mf.c:6208: Test failed: ProcessInput returned 0x80004001 mf.c:6213: Test failed: ProcessInput returned 0x80004001
=== debian11 (32 bit German report) ===
mf: mf.c:6208: Test failed: ProcessInput returned 0x80004001 mf.c:6213: Test failed: ProcessInput returned 0x80004001
=== debian11 (32 bit French report) ===
mf: mf.c:6208: Test failed: ProcessInput returned 0x80004001 mf.c:6213: Test failed: ProcessInput returned 0x80004001
=== debian11 (32 bit Hebrew:Israel report) ===
mf: mf.c:6208: Test failed: ProcessInput returned 0x80004001 mf.c:6213: Test failed: ProcessInput returned 0x80004001
=== debian11 (32 bit Hindi:India report) ===
mf: mf.c:6208: Test failed: ProcessInput returned 0x80004001 mf.c:6213: Test failed: ProcessInput returned 0x80004001
=== debian11 (32 bit Japanese:Japan report) ===
mf: mf.c:6208: Test failed: ProcessInput returned 0x80004001 mf.c:6213: Test failed: ProcessInput returned 0x80004001
=== debian11 (32 bit Chinese:China report) ===
mf: mf.c:6208: Test failed: ProcessInput returned 0x80004001 mf.c:6213: Test failed: ProcessInput returned 0x80004001
=== debian11 (32 bit WoW report) ===
mf: mf.c:6208: Test failed: ProcessInput returned 0x80004001 mf.c:6213: Test failed: ProcessInput returned 0x80004001
=== debian11 (64 bit WoW report) ===
mf: mf.c:6208: Test failed: ProcessInput returned 0x80004001 mf.c:6213: Test failed: ProcessInput returned 0x80004001
On 2/26/22 11:40, Marvin wrote:
=== debian11 (64 bit WoW report) ===
mf: mf.c:6208: Test failed: ProcessInput returned 0x80004001 mf.c:6213: Test failed: ProcessInput returned 0x80004001
I'll fix these, but also I found that there's going to be an issue with the queue:
Call of Duty Black Ops III really doesn't care about the return status of ProcessInput, and apparently expects a stream change result after a certain number of ProcessInput / ProcessOutput calls.
Adding a queue in front introduces too much decoupling, and we either:
1) don't accept buffers when there's already one in the queue, like here, and as the game doesn't even bother pushing the sample again we then miss some,
2) or, if we queue more buffers, accept too many input buffers and the game is confused that it pushed too many samples before receiving its stream change result,
It is possible, and I was doing this locally with an earlier version using a queue, to put the queue in the middle of the pipeline, after the h264 decoder element.
It helps this particular case as pushing a buffer also parses it and triggers the caps change, or output buffer, but it then causes some problems I need to resolve first.
More specifically, if we block on the output end, waiting for ProcessOutput like here, we won't process queries that could be triggered concurrently by a gst_pad_push, causing a deadlock.