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 =