Commit 651e4002 authored by Rémi Bernon's avatar Rémi Bernon Committed by Alexandre Julliard

winegstreamer: Introduce new wg_transform_(push|read)_data functions.

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=52391Signed-off-by: 's avatarRémi Bernon <rbernon@codeweavers.com> Signed-off-by: 's avatarZebediah Figura <zfigura@codeweavers.com> Signed-off-by: 's avatarNikolay Sivov <nsivov@codeweavers.com> Signed-off-by: 's avatarAlexandre Julliard <julliard@winehq.org>
parent befeea42
......@@ -6213,22 +6213,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
......@@ -6251,7 +6247,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);
......@@ -6278,38 +6273,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);
......@@ -6329,8 +6298,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);
......@@ -6344,7 +6325,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);
......@@ -6357,7 +6337,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 ||
......@@ -6370,7 +6349,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);
......
......@@ -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);
......
......@@ -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)
......
......@@ -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, &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);
}
......@@ -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 */
......@@ -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 */
......@@ -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),
};
......@@ -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;
}
......@@ -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 =
......
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment