diff --git a/dlls/evr/mixer.c b/dlls/evr/mixer.c index 3101d17ffdb..f9f1f4b0c34 100644 --- a/dlls/evr/mixer.c +++ b/dlls/evr/mixer.c @@ -21,17 +21,30 @@ #include "wine/debug.h" #include "evr.h" #include "d3d9.h" +#include "mferror.h" #include "evr_classes.h" WINE_DEFAULT_DEBUG_CHANNEL(evr); +#define MAX_MIXER_INPUT_STREAMS 16 + +struct input_stream +{ + unsigned int id; +}; + struct video_mixer { IMFTransform IMFTransform_iface; IMFVideoDeviceID IMFVideoDeviceID_iface; IMFTopologyServiceLookupClient IMFTopologyServiceLookupClient_iface; LONG refcount; + + struct input_stream inputs[MAX_MIXER_INPUT_STREAMS]; + unsigned int input_ids[MAX_MIXER_INPUT_STREAMS]; + unsigned int input_count; + CRITICAL_SECTION cs; }; static struct video_mixer *impl_from_IMFTransform(IMFTransform *iface) @@ -49,6 +62,21 @@ static struct video_mixer *impl_from_IMFTopologyServiceLookupClient(IMFTopologyS return CONTAINING_RECORD(iface, struct video_mixer, IMFTopologyServiceLookupClient_iface); } +static int video_mixer_compare_input_id(const void *a, const void *b) +{ + const unsigned int *key = a; + const struct input_stream *input = b; + + if (*key > input->id) return 1; + if (*key < input->id) return -1; + return 0; +} + +static struct input_stream * video_mixer_get_input(const struct video_mixer *mixer, unsigned int id) +{ + return bsearch(&id, mixer->inputs, mixer->input_count, sizeof(*mixer->inputs), video_mixer_compare_input_id); +} + static HRESULT WINAPI video_mixer_transform_QueryInterface(IMFTransform *iface, REFIID riid, void **obj) { struct video_mixer *mixer = impl_from_IMFTransform(iface); @@ -97,7 +125,10 @@ static ULONG WINAPI video_mixer_transform_Release(IMFTransform *iface) TRACE("%p, refcount %u.\n", iface, refcount); if (!refcount) + { + DeleteCriticalSection(&mixer->cs); free(mixer); + } return refcount; } @@ -105,38 +136,81 @@ static ULONG WINAPI video_mixer_transform_Release(IMFTransform *iface) static HRESULT WINAPI video_mixer_transform_GetStreamLimits(IMFTransform *iface, DWORD *input_minimum, DWORD *input_maximum, DWORD *output_minimum, DWORD *output_maximum) { - FIXME("%p, %p, %p, %p, %p.\n", iface, input_minimum, input_maximum, output_minimum, output_maximum); + TRACE("%p, %p, %p, %p, %p.\n", iface, input_minimum, input_maximum, output_minimum, output_maximum); - return E_NOTIMPL; + *input_minimum = 1; + *input_maximum = 16; + *output_minimum = 1; + *output_maximum = 1; + + return S_OK; } static HRESULT WINAPI video_mixer_transform_GetStreamCount(IMFTransform *iface, DWORD *inputs, DWORD *outputs) { - FIXME("%p, %p, %p.\n", iface, inputs, outputs); + struct video_mixer *mixer = impl_from_IMFTransform(iface); - return E_NOTIMPL; + TRACE("%p, %p, %p.\n", iface, inputs, outputs); + + EnterCriticalSection(&mixer->cs); + if (inputs) *inputs = mixer->input_count; + if (outputs) *outputs = 1; + LeaveCriticalSection(&mixer->cs); + + return S_OK; } static HRESULT WINAPI video_mixer_transform_GetStreamIDs(IMFTransform *iface, DWORD input_size, DWORD *inputs, DWORD output_size, DWORD *outputs) { - FIXME("%p, %u, %p, %u, %p.\n", iface, input_size, inputs, output_size, outputs); + struct video_mixer *mixer = impl_from_IMFTransform(iface); + HRESULT hr = S_OK; - return E_NOTIMPL; + TRACE("%p, %u, %p, %u, %p.\n", iface, input_size, inputs, output_size, outputs); + + EnterCriticalSection(&mixer->cs); + if (mixer->input_count > input_size || !output_size) + hr = MF_E_BUFFERTOOSMALL; + else if (inputs) + memcpy(inputs, mixer->input_ids, mixer->input_count * sizeof(*inputs)); + if (outputs) *outputs = 0; + LeaveCriticalSection(&mixer->cs); + + return hr; } static HRESULT WINAPI video_mixer_transform_GetInputStreamInfo(IMFTransform *iface, DWORD id, MFT_INPUT_STREAM_INFO *info) { - FIXME("%p, %u, %p.\n", iface, id, info); + struct video_mixer *mixer = impl_from_IMFTransform(iface); + struct input_stream *input; + HRESULT hr = S_OK; - return E_NOTIMPL; + TRACE("%p, %u, %p.\n", iface, id, info); + + EnterCriticalSection(&mixer->cs); + if (!(input = video_mixer_get_input(mixer, id))) + hr = MF_E_INVALIDSTREAMNUMBER; + else + { + memset(info, 0, sizeof(*info)); + if (id) + info->dwFlags |= MFT_INPUT_STREAM_REMOVABLE | MFT_INPUT_STREAM_OPTIONAL; + } + LeaveCriticalSection(&mixer->cs); + + return hr; } static HRESULT WINAPI video_mixer_transform_GetOutputStreamInfo(IMFTransform *iface, DWORD id, MFT_OUTPUT_STREAM_INFO *info) { - FIXME("%p, %u, %p.\n", iface, id, info); + TRACE("%p, %u, %p.\n", iface, id, info); - return E_NOTIMPL; + if (id) + return MF_E_INVALIDSTREAMNUMBER; + + memset(info, 0, sizeof(*info)); + + return S_OK; } static HRESULT WINAPI video_mixer_transform_GetAttributes(IMFTransform *iface, IMFAttributes **attributes) @@ -157,23 +231,93 @@ static HRESULT WINAPI video_mixer_transform_GetInputStreamAttributes(IMFTransfor static HRESULT WINAPI video_mixer_transform_GetOutputStreamAttributes(IMFTransform *iface, DWORD id, IMFAttributes **attributes) { - FIXME("%p, %u, %p.\n", iface, id, attributes); + TRACE("%p, %u, %p.\n", iface, id, attributes); return E_NOTIMPL; } static HRESULT WINAPI video_mixer_transform_DeleteInputStream(IMFTransform *iface, DWORD id) { - FIXME("%p, %u.\n", iface, id); + struct video_mixer *mixer = impl_from_IMFTransform(iface); + struct input_stream *input; + HRESULT hr = S_OK; + unsigned int idx; - return E_NOTIMPL; + TRACE("%p, %u.\n", iface, id); + + EnterCriticalSection(&mixer->cs); + + /* Can't delete reference stream. */ + if (!id || !(input = video_mixer_get_input(mixer, id))) + hr = MF_E_INVALIDSTREAMNUMBER; + else + { + mixer->input_count--; + idx = input - mixer->inputs; + if (idx < mixer->input_count) + { + memmove(&mixer->inputs[idx], &mixer->inputs[idx + 1], (mixer->input_count - idx) * sizeof(*mixer->inputs)); + memmove(&mixer->input_ids[idx], &mixer->input_ids[idx + 1], (mixer->input_count - idx) * + sizeof(*mixer->input_ids)); + } + } + + LeaveCriticalSection(&mixer->cs); + + return hr; } -static HRESULT WINAPI video_mixer_transform_AddInputStreams(IMFTransform *iface, DWORD streams, DWORD *ids) +static int video_mixer_add_input_sort_compare(const void *a, const void *b) { - FIXME("%p, %u, %p.\n", iface, streams, ids); + const struct input_stream *left = a, *right = b; + return left->id != right->id ? (left->id < right->id ? -1 : 1) : 0; +}; - return E_NOTIMPL; +static HRESULT WINAPI video_mixer_transform_AddInputStreams(IMFTransform *iface, DWORD count, DWORD *ids) +{ + struct video_mixer *mixer = impl_from_IMFTransform(iface); + struct input_stream inputs[MAX_MIXER_INPUT_STREAMS] = { {0} }; + unsigned int i, len; + HRESULT hr = S_OK; + + TRACE("%p, %u, %p.\n", iface, count, ids); + + if (!ids) + return E_POINTER; + + EnterCriticalSection(&mixer->cs); + if (count > ARRAY_SIZE(mixer->inputs) - mixer->input_count) + hr = E_INVALIDARG; + else + { + /* Test for collisions. */ + memcpy(inputs, mixer->inputs, mixer->input_count * sizeof(*inputs)); + for (i = 0; i < count; ++i) + inputs[i + mixer->input_count].id = ids[i]; + + len = mixer->input_count + count; + + qsort(inputs, len, sizeof(*inputs), video_mixer_add_input_sort_compare); + + for (i = 1; i < len; ++i) + { + if (inputs[i - 1].id == inputs[i].id) + { + hr = E_INVALIDARG; + break; + } + } + + if (SUCCEEDED(hr)) + { + memcpy(&mixer->input_ids[mixer->input_count], ids, count * sizeof(*ids)); + memcpy(mixer->inputs, inputs, len * sizeof(*inputs)); + mixer->input_count += count; + } + } + LeaveCriticalSection(&mixer->cs); + + return hr; } static HRESULT WINAPI video_mixer_transform_GetInputAvailableType(IMFTransform *iface, DWORD id, DWORD index, @@ -407,6 +551,8 @@ HRESULT evr_mixer_create(IUnknown *outer, void **out) object->IMFVideoDeviceID_iface.lpVtbl = &video_mixer_device_id_vtbl; object->IMFTopologyServiceLookupClient_iface.lpVtbl = &video_mixer_service_client_vtbl; object->refcount = 1; + object->input_count = 1; + InitializeCriticalSection(&object->cs); *out = &object->IMFTransform_iface; diff --git a/dlls/evr/tests/evr.c b/dlls/evr/tests/evr.c index 56fb1680b07..db20fa76643 100644 --- a/dlls/evr/tests/evr.c +++ b/dlls/evr/tests/evr.c @@ -25,6 +25,7 @@ #include "evr.h" #include "initguid.h" #include "dxva2api.h" +#include "mferror.h" static const WCHAR sink_id[] = {'E','V','R',' ','I','n','p','u','t','0',0}; @@ -340,8 +341,16 @@ static void test_pin_info(void) static void test_default_mixer(void) { + DWORD input_min, input_max, output_min, output_max; + MFT_OUTPUT_STREAM_INFO output_info; + MFT_INPUT_STREAM_INFO input_info; + DWORD input_count, output_count; IMFVideoDeviceID *deviceid; + DWORD input_id, output_id; + IMFAttributes *attributes, *attributes2; IMFTransform *transform; + unsigned int i; + DWORD ids[16]; IUnknown *unk; HRESULT hr; IID iid; @@ -365,6 +374,111 @@ static void test_default_mixer(void) IMFVideoDeviceID_Release(deviceid); + /* Stream configuration. */ + input_count = output_count = 0; + hr = IMFTransform_GetStreamCount(transform, &input_count, &output_count); + ok(hr == S_OK, "Failed to get stream count, hr %#x.\n", hr); + ok(input_count == 1 && output_count == 1, "Unexpected stream count %u/%u.\n", input_count, output_count); + + hr = IMFTransform_GetStreamLimits(transform, &input_min, &input_max, &output_min, &output_max); + ok(hr == S_OK, "Failed to get stream limits, hr %#x.\n", hr); + ok(input_min == 1 && input_max == 16 && output_min == 1 && output_max == 1, "Unexpected stream limits %u/%u, %u/%u.\n", + input_min, input_max, output_min, output_max); + + hr = IMFTransform_GetInputStreamInfo(transform, 1, &input_info); + ok(hr == MF_E_INVALIDSTREAMNUMBER, "Unexpected hr %#x.\n", hr); + + hr = IMFTransform_GetOutputStreamInfo(transform, 1, &output_info); + ok(hr == MF_E_INVALIDSTREAMNUMBER, "Unexpected hr %#x.\n", hr); + + memset(&input_info, 0xcc, sizeof(input_info)); + hr = IMFTransform_GetInputStreamInfo(transform, 0, &input_info); + ok(hr == S_OK, "Failed to get input info, hr %#x.\n", hr); + + memset(&output_info, 0xcc, sizeof(output_info)); + hr = IMFTransform_GetOutputStreamInfo(transform, 0, &output_info); + ok(hr == S_OK, "Failed to get input info, hr %#x.\n", hr); + ok(!(output_info.dwFlags & (MFT_OUTPUT_STREAM_PROVIDES_SAMPLES | MFT_OUTPUT_STREAM_CAN_PROVIDE_SAMPLES)), + "Unexpected output flags %#x.\n", output_info.dwFlags); + + hr = IMFTransform_GetStreamIDs(transform, 1, &input_id, 1, &output_id); + ok(hr == S_OK, "Failed to get input info, hr %#x.\n", hr); + ok(input_id == 0 && output_id == 0, "Unexpected stream ids.\n"); + + hr = IMFTransform_GetInputStreamAttributes(transform, 1, &attributes); +todo_wine + ok(hr == MF_E_INVALIDSTREAMNUMBER, "Unexpected hr %#x.\n", hr); + + hr = IMFTransform_GetOutputStreamAttributes(transform, 1, &attributes); + ok(hr == E_NOTIMPL, "Unexpected hr %#x.\n", hr); + + hr = IMFTransform_GetOutputStreamAttributes(transform, 0, &attributes); + ok(hr == E_NOTIMPL, "Unexpected hr %#x.\n", hr); + + hr = IMFTransform_AddInputStreams(transform, 16, NULL); + ok(hr == E_POINTER, "Unexpected hr %#x.\n", hr); + + hr = IMFTransform_AddInputStreams(transform, 16, ids); + ok(hr == E_INVALIDARG, "Unexpected hr %#x.\n", hr); + + memset(ids, 0, sizeof(ids)); + hr = IMFTransform_AddInputStreams(transform, 15, ids); + ok(hr == E_INVALIDARG, "Unexpected hr %#x.\n", hr); + + for (i = 0; i < ARRAY_SIZE(ids); ++i) + ids[i] = i + 1; + + hr = IMFTransform_AddInputStreams(transform, 15, ids); + ok(hr == S_OK, "Unexpected hr %#x.\n", hr); + + input_count = output_count = 0; + hr = IMFTransform_GetStreamCount(transform, &input_count, &output_count); + ok(hr == S_OK, "Failed to get stream count, hr %#x.\n", hr); + ok(input_count == 16 && output_count == 1, "Unexpected stream count %u/%u.\n", input_count, output_count); + + memset(&input_info, 0, sizeof(input_info)); + hr = IMFTransform_GetInputStreamInfo(transform, 1, &input_info); + ok(hr == S_OK, "Failed to get input info, hr %#x.\n", hr); + ok((input_info.dwFlags & (MFT_INPUT_STREAM_REMOVABLE | MFT_INPUT_STREAM_OPTIONAL)) == + (MFT_INPUT_STREAM_REMOVABLE | MFT_INPUT_STREAM_OPTIONAL), "Unexpected flags %#x.\n", input_info.dwFlags); + + attributes = NULL; + hr = IMFTransform_GetInputStreamAttributes(transform, 0, &attributes); +todo_wine { + ok(hr == S_OK, "Unexpected hr %#x.\n", hr); + ok(!!attributes, "Unexpected attributes.\n"); +} + attributes2 = NULL; + hr = IMFTransform_GetInputStreamAttributes(transform, 0, &attributes2); +todo_wine + ok(hr == S_OK, "Unexpected hr %#x.\n", hr); + ok(attributes == attributes2, "Unexpected instance.\n"); + + if (attributes2) + IMFAttributes_Release(attributes2); + if (attributes) + IMFAttributes_Release(attributes); + + attributes = NULL; + hr = IMFTransform_GetInputStreamAttributes(transform, 1, &attributes); +todo_wine { + ok(hr == S_OK, "Unexpected hr %#x.\n", hr); + ok(!!attributes, "Unexpected attributes.\n"); +} + if (attributes) + IMFAttributes_Release(attributes); + + hr = IMFTransform_DeleteInputStream(transform, 0); + ok(hr == MF_E_INVALIDSTREAMNUMBER, "Unexpected hr %#x.\n", hr); + + hr = IMFTransform_DeleteInputStream(transform, 1); + ok(hr == S_OK, "Unexpected hr %#x.\n", hr); + + input_count = output_count = 0; + hr = IMFTransform_GetStreamCount(transform, &input_count, &output_count); + ok(hr == S_OK, "Failed to get stream count, hr %#x.\n", hr); + ok(input_count == 15 && output_count == 1, "Unexpected stream count %u/%u.\n", input_count, output_count); + IMFTransform_Release(transform); hr = MFCreateVideoMixer(NULL, &IID_IMFTransform, &IID_IMFTransform, (void **)&transform);