[PATCH v4 2/3] winegstreamer: Introduce new wg_transform_(push|read)_data functions.
Rémi Bernon
rbernon at codeweavers.com
Mon Mar 14 08:43:22 CDT 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>
Signed-off-by: Zebediah Figura <zfigura 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 de72ad5a6e7..166a357409a 100644
--- a/dlls/mf/tests/mf.c
+++ b/dlls/mf/tests/mf.c
@@ -6209,22 +6209,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 %#lx\n", hr);
ret = IMFSample_Release(sample);
ok(ret == 0, "Release returned %lu\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 %#lx\n", hr);
ret = IMFSample_Release(sample);
ok(ret == 0, "Release returned %lu\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 %#lx\n", hr);
hr = IMFTransform_ProcessInput(transform, 0, sample, 0);
- todo_wine
ok(hr == MF_E_NOTACCEPTING, "ProcessInput returned %#lx\n", hr);
ret = IMFSample_Release(sample);
todo_wine
@@ -6247,7 +6243,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 %#lx\n", hr);
ret = IMFSample_Release(sample);
ok(ret == 0, "Release returned %lu\n", ret);
@@ -6274,38 +6269,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 %#lx\n", hr);
- ok(output.pSample == sample, "got pSample %p\n", output.pSample);
- ok(output.dwStatus == 0, "got dwStatus %#lx\n", output.dwStatus);
- ok(status == 0, "got status %#lx\n", status);
- check_sample(sample, NULL, 0, NULL);
- ret = IMFSample_Release(sample);
- ok(ret == 0, "Release returned %lu\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 %#lx\n", hr);
- ret = IMFSample_Release(sample);
- ok(ret == 1, "Release returned %lu\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 %#lx\n", hr);
ok(output.pSample == sample, "got pSample %p\n", output.pSample);
@@ -6325,8 +6294,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 %#lx\n", hr);
+ todo_wine
+ ok(length == sizeof(wma_decoded_data) / 2, "got length %lu\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 %lu\n", ret);
@@ -6340,7 +6321,6 @@ static void test_wma_decoder(void)
todo_wine
ok(i == 0xe000, "ProcessOutput produced %#lx bytes\n", i);
- todo_wine
ok(hr == MF_E_TRANSFORM_NEED_MORE_INPUT, "ProcessOutput returned %#lx\n", hr);
ok(output.pSample == sample, "got pSample %p\n", output.pSample);
ok(output.dwStatus == 0, "got dwStatus %#lx\n", output.dwStatus);
@@ -6353,7 +6333,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 %#lx\n", hr);
ok(output.pSample == sample, "got pSample %p\n", output.pSample);
ok(output.dwStatus == 0 ||
@@ -6366,7 +6345,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 %#lx\n", hr);
ret = IMFTransform_Release(transform);
diff --git a/dlls/winegstreamer/gst_private.h b/dlls/winegstreamer/gst_private.h
index 34b3e0c1047..f5da807a2ab 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 h264_decoder_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 7eb673b61ae..ed460144e8b 100644
--- a/dlls/winegstreamer/mfplat.c
+++ b/dlls/winegstreamer/mfplat.c
@@ -854,3 +854,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, ¤t_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.35.1
More information about the wine-devel
mailing list