From fb0f6b4be46032e2e38a804822eaccb772e5d9bb Mon Sep 17 00:00:00 2001 From: Anton Baskanov Date: Tue, 17 May 2022 12:04:19 +0700 Subject: [PATCH] quartz/tests: Add tests for MPEG audio decoder quality control. Signed-off-by: Anton Baskanov --- dlls/quartz/tests/mpegaudio.c | 223 ++++++++++++++++++++++++++++++++++ 1 file changed, 223 insertions(+) diff --git a/dlls/quartz/tests/mpegaudio.c b/dlls/quartz/tests/mpegaudio.c index d3d5c3d76f0..804cbbcc3d1 100644 --- a/dlls/quartz/tests/mpegaudio.c +++ b/dlls/quartz/tests/mpegaudio.c @@ -789,11 +789,118 @@ static void test_media_types(void) ok(!ref, "Got outstanding refcount %ld.\n", ref); } +struct testqc +{ + IQualityControl IQualityControl_iface; + IUnknown IUnknown_inner; + IUnknown *outer_unk; + LONG refcount; + IBaseFilter *notify_sender; + Quality notify_quality; + HRESULT notify_hr; +}; + +static struct testqc *impl_from_IQualityControl(IQualityControl *iface) +{ + return CONTAINING_RECORD(iface, struct testqc, IQualityControl_iface); +} + +static HRESULT WINAPI testqc_QueryInterface(IQualityControl *iface, REFIID iid, void **out) +{ + struct testqc *qc = impl_from_IQualityControl(iface); + return IUnknown_QueryInterface(qc->outer_unk, iid, out); +} + +static ULONG WINAPI testqc_AddRef(IQualityControl *iface) +{ + struct testqc *qc = impl_from_IQualityControl(iface); + return IUnknown_AddRef(qc->outer_unk); +} + +static ULONG WINAPI testqc_Release(IQualityControl *iface) +{ + struct testqc *qc = impl_from_IQualityControl(iface); + return IUnknown_Release(qc->outer_unk); +} + +static HRESULT WINAPI testqc_Notify(IQualityControl *iface, IBaseFilter *sender, Quality q) +{ + struct testqc *qc = impl_from_IQualityControl(iface); + + qc->notify_sender = sender; + qc->notify_quality = q; + + return qc->notify_hr; +} + +static HRESULT WINAPI testqc_SetSink(IQualityControl *iface, IQualityControl *sink) +{ + ok(0, "Unexpected call.\n"); + return E_NOTIMPL; +} + +static const IQualityControlVtbl testqc_vtbl = +{ + testqc_QueryInterface, + testqc_AddRef, + testqc_Release, + testqc_Notify, + testqc_SetSink, +}; + +static struct testqc *impl_from_qc_IUnknown(IUnknown *iface) +{ + return CONTAINING_RECORD(iface, struct testqc, IUnknown_inner); +} + +static HRESULT WINAPI testqc_inner_QueryInterface(IUnknown *iface, REFIID iid, void **out) +{ + struct testqc *qc = impl_from_qc_IUnknown(iface); + + if (IsEqualIID(iid, &IID_IUnknown)) + *out = iface; + else if (IsEqualIID(iid, &IID_IQualityControl)) + *out = &qc->IQualityControl_iface; + else + return E_NOINTERFACE; + + IUnknown_AddRef((IUnknown *)*out); + return S_OK; +} + +static ULONG WINAPI testqc_inner_AddRef(IUnknown *iface) +{ + struct testqc *qc = impl_from_qc_IUnknown(iface); + return InterlockedIncrement(&qc->refcount); +} + +static ULONG WINAPI testqc_inner_Release(IUnknown *iface) +{ + struct testqc *qc = impl_from_qc_IUnknown(iface); + return InterlockedDecrement(&qc->refcount); +} + +static const IUnknownVtbl testqc_inner_vtbl = +{ + testqc_inner_QueryInterface, + testqc_inner_AddRef, + testqc_inner_Release, +}; + +static void testqc_init(struct testqc *qc, IUnknown *outer) +{ + memset(qc, 0, sizeof(*qc)); + qc->IQualityControl_iface.lpVtbl = &testqc_vtbl; + qc->IUnknown_inner.lpVtbl = &testqc_inner_vtbl; + qc->outer_unk = outer ? outer : &qc->IUnknown_inner; +} + struct testfilter { struct strmbase_filter filter; struct strmbase_source source; struct strmbase_sink sink; + struct testqc *qc; const AM_MEDIA_TYPE *mt; unsigned int got_sample, got_new_segment, got_eos, got_begin_flush, got_end_flush; REFERENCE_TIME expected_start_time; @@ -827,6 +934,19 @@ static const struct strmbase_filter_ops testfilter_ops = .filter_destroy = testfilter_destroy, }; +static HRESULT testsource_query_interface(struct strmbase_pin *iface, REFIID iid, void **out) +{ + struct testfilter *filter = impl_from_strmbase_filter(iface->filter); + + if (IsEqualGUID(iid, &IID_IQualityControl) && filter->qc) + *out = &filter->qc->IQualityControl_iface; + else + return E_NOINTERFACE; + + IUnknown_AddRef((IUnknown *)*out); + return S_OK; +} + static HRESULT WINAPI testsource_DecideAllocator(struct strmbase_source *iface, IMemInputPin *peer, IMemAllocator **allocator) { @@ -835,6 +955,7 @@ static HRESULT WINAPI testsource_DecideAllocator(struct strmbase_source *iface, static const struct strmbase_source_ops testsource_ops = { + .base.pin_query_interface = testsource_query_interface, .pfnAttemptConnection = BaseOutputPinImpl_AttemptConnection, .pfnDecideAllocator = testsource_DecideAllocator, }; @@ -1108,6 +1229,107 @@ static void test_source_allocator(IFilterGraph2 *graph, IMediaControl *control, IFilterGraph2_Disconnect(graph, &testsource->source.pin.IPin_iface); } +static void test_quality_control(IFilterGraph2 *graph, IBaseFilter *filter, + IPin *sink, IPin *source, struct testfilter *testsource, struct testfilter *testsink) +{ + struct testqc testsource_qc; + IQualityControl *source_qc; + IQualityControl *sink_qc; + Quality quality = {0}; + struct testqc qc; + HRESULT hr; + + testqc_init(&testsource_qc, testsource->filter.outer_unk); + testqc_init(&qc, NULL); + + hr = IPin_QueryInterface(sink, &IID_IQualityControl, (void **)&sink_qc); + ok(hr == S_OK, "Got hr %#lx.\n", hr); + hr = IPin_QueryInterface(source, &IID_IQualityControl, (void **)&source_qc); + ok(hr == S_OK, "Got hr %#lx.\n", hr); + + hr = IQualityControl_Notify(sink_qc, &testsink->filter.IBaseFilter_iface, quality); + ok(hr == S_OK, "Got hr %#lx.\n", hr); + + hr = IQualityControl_Notify(source_qc, &testsink->filter.IBaseFilter_iface, quality); + ok(hr == VFW_E_NOT_FOUND, "Got hr %#lx.\n", hr); + + hr = IQualityControl_SetSink(sink_qc, &qc.IQualityControl_iface); + ok(hr == S_OK, "Got hr %#lx.\n", hr); + qc.notify_sender = (IBaseFilter *)0xdeadbeef; + memset(&qc.notify_quality, 0xaa, sizeof(qc.notify_quality)); + hr = IQualityControl_Notify(source_qc, &testsink->filter.IBaseFilter_iface, quality); + ok(hr == S_OK, "Got hr %#lx.\n", hr); + ok(qc.notify_sender == filter, "Got sender %p.\n", qc.notify_sender); + ok(!memcmp(&qc.notify_quality, &quality, sizeof(quality)), "Quality didn't match.\n"); + hr = IQualityControl_SetSink(sink_qc, NULL); + ok(hr == S_OK, "Got hr %#lx.\n", hr); + + hr = IQualityControl_SetSink(source_qc, &qc.IQualityControl_iface); + ok(hr == S_OK, "Got hr %#lx.\n", hr); + qc.notify_sender = (IBaseFilter *)0xdeadbeef; + hr = IQualityControl_Notify(source_qc, &testsink->filter.IBaseFilter_iface, quality); + ok(hr == VFW_E_NOT_FOUND, "Got hr %#lx.\n", hr); + ok(qc.notify_sender == (IBaseFilter *)0xdeadbeef, "Got sender %p.\n", qc.notify_sender); + hr = IQualityControl_SetSink(source_qc, NULL); + ok(hr == S_OK, "Got hr %#lx.\n", hr); + + hr = IFilterGraph2_ConnectDirect(graph, &testsource->source.pin.IPin_iface, sink, &mp2_mt); + ok(hr == S_OK, "Got hr %#lx.\n", hr); + + hr = IQualityControl_Notify(source_qc, &testsink->filter.IBaseFilter_iface, quality); + ok(hr == VFW_E_NOT_FOUND, "Got hr %#lx.\n", hr); + + testsource->qc = &testsource_qc; + + qc.notify_sender = (IBaseFilter *)0xdeadbeef; + hr = IQualityControl_Notify(sink_qc, &testsink->filter.IBaseFilter_iface, quality); + ok(hr == S_OK, "Got hr %#lx.\n", hr); + ok(qc.notify_sender == (IBaseFilter *)0xdeadbeef, "Got sender %p.\n", qc.notify_sender); + + testsource_qc.notify_sender = (IBaseFilter *)0xdeadbeef; + memset(&testsource_qc.notify_quality, 0xaa, sizeof(testsource_qc.notify_quality)); + hr = IQualityControl_Notify(source_qc, &testsink->filter.IBaseFilter_iface, quality); + ok(hr == S_OK, "Got hr %#lx.\n", hr); + ok(testsource_qc.notify_sender == filter, "Got sender %p.\n", testsource_qc.notify_sender); + ok(!memcmp(&testsource_qc.notify_quality, &quality, sizeof(quality)), "Quality didn't match.\n"); + + testsource_qc.notify_hr = E_FAIL; + hr = IQualityControl_Notify(source_qc, &testsink->filter.IBaseFilter_iface, quality); + ok(hr == E_FAIL, "Got hr %#lx.\n", hr); + testsource_qc.notify_hr = S_OK; + + hr = IQualityControl_SetSink(sink_qc, &qc.IQualityControl_iface); + ok(hr == S_OK, "Got hr %#lx.\n", hr); + qc.notify_sender = (IBaseFilter *)0xdeadbeef; + memset(&qc.notify_quality, 0xaa, sizeof(qc.notify_quality)); + hr = IQualityControl_Notify(source_qc, &testsink->filter.IBaseFilter_iface, quality); + ok(hr == S_OK, "Got hr %#lx.\n", hr); + ok(qc.notify_sender == filter, "Got sender %p.\n", qc.notify_sender); + ok(!memcmp(&qc.notify_quality, &quality, sizeof(quality)), "Quality didn't match.\n"); + hr = IQualityControl_SetSink(sink_qc, NULL); + ok(hr == S_OK, "Got hr %#lx.\n", hr); + + hr = IQualityControl_SetSink(source_qc, &qc.IQualityControl_iface); + ok(hr == S_OK, "Got hr %#lx.\n", hr); + qc.notify_sender = (IBaseFilter *)0xdeadbeef; + hr = IQualityControl_Notify(source_qc, &testsink->filter.IBaseFilter_iface, quality); + ok(hr == S_OK, "Got hr %#lx.\n", hr); + ok(qc.notify_sender == (IBaseFilter *)0xdeadbeef, "Got sender %p.\n", qc.notify_sender); + hr = IQualityControl_SetSink(source_qc, NULL); + ok(hr == S_OK, "Got hr %#lx.\n", hr); + + IFilterGraph2_Disconnect(graph, sink); + IFilterGraph2_Disconnect(graph, &testsource->source.pin.IPin_iface); + + hr = IQualityControl_Notify(source_qc, &testsink->filter.IBaseFilter_iface, quality); + ok(hr == VFW_E_NOT_FOUND, "Got hr %#lx.\n", hr); + + IQualityControl_Release(source_qc); + IQualityControl_Release(sink_qc); + + testsource->qc = NULL; +} + static void test_sample_processing(IMediaControl *control, IMemInputPin *input, struct testfilter *sink) { REFERENCE_TIME start, stop; @@ -1333,6 +1555,7 @@ static void test_connect_pin(void) IFilterGraph2_QueryInterface(graph, &IID_IMediaControl, (void **)&control); test_source_allocator(graph, control, sink, source, &testsource, &testsink); + test_quality_control(graph, filter, sink, source, &testsource, &testsink); /* Test sink connection. */