From: Anton Baskanov baskanov@gmail.com
Signed-off-by: Anton Baskanov baskanov@gmail.com --- dlls/quartz/tests/mpegaudio.c | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-)
diff --git a/dlls/quartz/tests/mpegaudio.c b/dlls/quartz/tests/mpegaudio.c index 75b85ee8716..48d879cf0f3 100644 --- a/dlls/quartz/tests/mpegaudio.c +++ b/dlls/quartz/tests/mpegaudio.c @@ -120,9 +120,9 @@ static const MPEGLAYER3WAVEFORMAT mp3_format1 =
static const AM_MEDIA_TYPE mp3_mt1 = { - /* MEDIATYPE_Audio, MEDIASUBTYPE_MPEG1AudioPayload, FORMAT_WaveFormatEx */ + /* MEDIATYPE_Audio, MEDIASUBTYPE_MP3, FORMAT_WaveFormatEx */ .majortype = {0x73647561, 0x0000, 0x0010, {0x80, 0x00, 0x00, 0xaa, 0x00, 0x38, 0x9b, 0x71}}, - .subtype = {0x00000050, 0x0000, 0x0010, {0x80, 0x00, 0x00, 0xaa, 0x00, 0x38, 0x9b, 0x71}}, + .subtype = {0x00000055, 0x0000, 0x0010, {0x80, 0x00, 0x00, 0xaa, 0x00, 0x38, 0x9b, 0x71}}, .bFixedSizeSamples = TRUE, .lSampleSize = 1, .formattype = {0x05589f81, 0xc356, 0x11ce, {0xbf, 0x01, 0x00, 0xaa, 0x00, 0x55, 0x59, 0x5a}}, @@ -741,12 +741,12 @@ static void test_media_types(void)
mt = mp2_mt; mt.subtype = MEDIASUBTYPE_MPEG1Packet; - hr = IPin_QueryAccept(pin, &mp2_mt); + hr = IPin_QueryAccept(pin, &mt); ok(hr == S_OK, "Got hr %#lx.\n", hr);
mt = mp2_mt; mt.subtype = MEDIASUBTYPE_MPEG1Payload; - hr = IPin_QueryAccept(pin, &mp2_mt); + hr = IPin_QueryAccept(pin, &mt); ok(hr == S_OK, "Got hr %#lx.\n", hr);
mt = mp2_mt;
From: Anton Baskanov baskanov@gmail.com
Signed-off-by: Anton Baskanov baskanov@gmail.com --- dlls/quartz/tests/mpegaudio.c | 131 +++++++++++++++++++++++++++++++++- 1 file changed, 130 insertions(+), 1 deletion(-)
diff --git a/dlls/quartz/tests/mpegaudio.c b/dlls/quartz/tests/mpegaudio.c index 48d879cf0f3..48f5517336c 100644 --- a/dlls/quartz/tests/mpegaudio.c +++ b/dlls/quartz/tests/mpegaudio.c @@ -795,7 +795,7 @@ struct testfilter struct strmbase_source source; struct strmbase_sink sink; const AM_MEDIA_TYPE *mt; - unsigned int got_sample; + unsigned int got_sample, got_new_segment, got_eos, got_begin_flush, got_end_flush; REFERENCE_TIME expected_start_time; REFERENCE_TIME expected_stop_time; }; @@ -898,12 +898,48 @@ static HRESULT WINAPI testsink_Receive(struct strmbase_sink *iface, IMediaSample return S_OK; }
+static HRESULT testsink_new_segment(struct strmbase_sink *iface, + REFERENCE_TIME start, REFERENCE_TIME stop, double rate) +{ + struct testfilter *filter = impl_from_strmbase_filter(iface->pin.filter); + ++filter->got_new_segment; + ok(start == 10000, "Got start %s.\n", wine_dbgstr_longlong(start)); + ok(stop == 20000, "Got stop %s.\n", wine_dbgstr_longlong(stop)); + ok(rate == 1.0, "Got rate %.16e.\n", rate); + return S_OK; +} + +static HRESULT testsink_eos(struct strmbase_sink *iface) +{ + struct testfilter *filter = impl_from_strmbase_filter(iface->pin.filter); + ++filter->got_eos; + return S_OK; +} + +static HRESULT testsink_begin_flush(struct strmbase_sink *iface) +{ + struct testfilter *filter = impl_from_strmbase_filter(iface->pin.filter); + ++filter->got_begin_flush; + return S_OK; +} + +static HRESULT testsink_end_flush(struct strmbase_sink *iface) +{ + struct testfilter *filter = impl_from_strmbase_filter(iface->pin.filter); + ++filter->got_end_flush; + return S_OK; +} + static const struct strmbase_sink_ops testsink_ops = { .base.pin_query_interface = testsink_query_interface, .base.pin_get_media_type = testsink_get_media_type, .sink_connect = testsink_connect, .pfnReceive = testsink_Receive, + .sink_new_segment = testsink_new_segment, + .sink_eos = testsink_eos, + .sink_begin_flush = testsink_begin_flush, + .sink_end_flush = testsink_end_flush, };
static void testfilter_init(struct testfilter *filter) @@ -1175,6 +1211,98 @@ static void test_sample_processing(IMediaControl *control, IMemInputPin *input, IMemAllocator_Release(allocator); }
+static void test_streaming_events(IMediaControl *control, IPin *sink, + IMemInputPin *input, struct testfilter *testsink) +{ + REFERENCE_TIME start, stop; + IMemAllocator *allocator; + IMediaSample *sample; + HRESULT hr; + BYTE *data; + + hr = IMediaControl_Pause(control); + ok(hr == S_OK, "Got hr %#lx.\n", hr); + + hr = IMemInputPin_GetAllocator(input, &allocator); + ok(hr == S_OK, "Got hr %#lx.\n", hr); + hr = IMemAllocator_Commit(allocator); + ok(hr == S_OK, "Got hr %#lx.\n", hr); + hr = IMemAllocator_GetBuffer(allocator, &sample, NULL, NULL, 0); + ok(hr == S_OK, "Got hr %#lx.\n", hr); + hr = IMediaSample_GetPointer(sample, &data); + ok(hr == S_OK, "Got hr %#lx.\n", hr); + memset(data, 0, 48); + data[0] = 0xff; + data[1] = 0xff; + data[2] = 0x18; + data[3] = 0xc4; + hr = IMediaSample_SetActualDataLength(sample, 48); + ok(hr == S_OK, "Got hr %#lx.\n", hr); + start = 0; + stop = 120000; + hr = IMediaSample_SetTime(sample, &start, &stop); + ok(hr == S_OK, "Got hr %#lx.\n", hr); + + ok(!testsink->got_new_segment, "Got %u calls to IPin::NewSegment().\n", testsink->got_new_segment); + hr = IPin_NewSegment(sink, 10000, 20000, 1.0); + ok(hr == S_OK, "Got hr %#lx.\n", hr); + ok(testsink->got_new_segment == 1, "Got %u calls to IPin::NewSegment().\n", testsink->got_new_segment); + + ok(!testsink->got_eos, "Got %u calls to IPin::EndOfStream().\n", testsink->got_eos); + hr = IPin_EndOfStream(sink); + ok(hr == S_OK, "Got hr %#lx.\n", hr); + ok(!testsink->got_sample, "Got %u calls to Receive().\n", testsink->got_sample); + ok(testsink->got_eos == 1, "Got %u calls to IPin::EndOfStream().\n", testsink->got_eos); + testsink->got_eos = 0; + + hr = IPin_EndOfStream(sink); + ok(hr == S_OK, "Got hr %#lx.\n", hr); + ok(testsink->got_eos == 1, "Got %u calls to IPin::EndOfStream().\n", testsink->got_eos); + + testsink->expected_start_time = 0; + testsink->expected_stop_time = 120000; + hr = IMemInputPin_Receive(input, sample); + ok(hr == S_OK, "Got hr %#lx.\n", hr); + hr = IMemInputPin_Receive(input, sample); + ok(hr == S_OK, "Got hr %#lx.\n", hr); + hr = IMemInputPin_Receive(input, sample); + ok(hr == S_OK, "Got hr %#lx.\n", hr); + ok(testsink->got_sample >= 1, "Got %u calls to Receive().\n", testsink->got_sample); + testsink->got_sample = 0; + + ok(!testsink->got_begin_flush, "Got %u calls to IPin::BeginFlush().\n", testsink->got_begin_flush); + hr = IPin_BeginFlush(sink); + ok(hr == S_OK, "Got hr %#lx.\n", hr); + ok(testsink->got_begin_flush == 1, "Got %u calls to IPin::BeginFlush().\n", testsink->got_begin_flush); + + hr = IMemInputPin_Receive(input, sample); + ok(hr == S_FALSE, "Got hr %#lx.\n", hr); + + hr = IPin_EndOfStream(sink); + ok(hr == S_FALSE, "Got hr %#lx.\n", hr); + + ok(!testsink->got_end_flush, "Got %u calls to IPin::EndFlush().\n", testsink->got_end_flush); + hr = IPin_EndFlush(sink); + ok(hr == S_OK, "Got hr %#lx.\n", hr); + ok(testsink->got_end_flush == 1, "Got %u calls to IPin::EndFlush().\n", testsink->got_end_flush); + + testsink->expected_start_time = 0; + testsink->expected_stop_time = 120000; + hr = IMemInputPin_Receive(input, sample); + ok(hr == S_OK, "Got hr %#lx.\n", hr); + hr = IMemInputPin_Receive(input, sample); + ok(hr == S_OK, "Got hr %#lx.\n", hr); + hr = IMemInputPin_Receive(input, sample); + ok(hr == S_OK, "Got hr %#lx.\n", hr); + ok(testsink->got_sample >= 1, "Got %u calls to Receive().\n", testsink->got_sample); + testsink->got_sample = 0; + + hr = IMediaControl_Stop(control); + ok(hr == S_OK, "Got hr %#lx.\n", hr); + IMediaSample_Release(sample); + IMemAllocator_Release(allocator); +} + static void test_connect_pin(void) { IBaseFilter *filter = create_mpeg_audio_codec(); @@ -1369,6 +1497,7 @@ static void test_connect_pin(void) ok(hr == S_OK, "Got hr %#lx.\n", hr);
test_sample_processing(control, meminput, &testsink); + test_streaming_events(control, sink, meminput, &testsink);
hr = IFilterGraph2_Disconnect(graph, source); ok(hr == S_OK, "Got hr %#lx.\n", hr);
From: Anton Baskanov baskanov@gmail.com
Signed-off-by: Anton Baskanov baskanov@gmail.com --- dlls/quartz/tests/mpegaudio.c | 4 ++-- dlls/winegstreamer/Makefile.in | 2 +- dlls/winegstreamer/quartz_transform.c | 22 ++++++++++++++++++++++ 3 files changed, 25 insertions(+), 3 deletions(-)
diff --git a/dlls/quartz/tests/mpegaudio.c b/dlls/quartz/tests/mpegaudio.c index 48f5517336c..48322d019a8 100644 --- a/dlls/quartz/tests/mpegaudio.c +++ b/dlls/quartz/tests/mpegaudio.c @@ -253,8 +253,8 @@ static void test_interfaces(void) IBaseFilter_FindPin(filter, L"Out", &pin);
check_interface(pin, &IID_IPin, TRUE); - todo_wine check_interface(pin, &IID_IMediaPosition, TRUE); - todo_wine check_interface(pin, &IID_IMediaSeeking, TRUE); + check_interface(pin, &IID_IMediaPosition, TRUE); + check_interface(pin, &IID_IMediaSeeking, TRUE); todo_wine check_interface(pin, &IID_IQualityControl, TRUE); check_interface(pin, &IID_IUnknown, TRUE);
diff --git a/dlls/winegstreamer/Makefile.in b/dlls/winegstreamer/Makefile.in index 00b7c72a5f3..e4c2636d02d 100644 --- a/dlls/winegstreamer/Makefile.in +++ b/dlls/winegstreamer/Makefile.in @@ -1,7 +1,7 @@ MODULE = winegstreamer.dll UNIXLIB = winegstreamer.so IMPORTLIB = winegstreamer -IMPORTS = strmbase ole32 msdmo +IMPORTS = strmbase ole32 oleaut32 msdmo DELAYIMPORTS = mfplat EXTRAINCL = $(GSTREAMER_CFLAGS) EXTRALIBS = $(GSTREAMER_LIBS) $(PTHREAD_LIBS) diff --git a/dlls/winegstreamer/quartz_transform.c b/dlls/winegstreamer/quartz_transform.c index 447f331d474..7fa18aa7673 100644 --- a/dlls/winegstreamer/quartz_transform.c +++ b/dlls/winegstreamer/quartz_transform.c @@ -33,6 +33,7 @@ struct transform
struct strmbase_sink sink; struct strmbase_source source; + struct strmbase_passthrough passthrough;
struct wg_transform *transform;
@@ -66,6 +67,7 @@ static void transform_destroy(struct strmbase_filter *iface) { struct transform *filter = impl_from_strmbase_filter(iface);
+ strmbase_passthrough_cleanup(&filter->passthrough); strmbase_source_cleanup(&filter->source); strmbase_sink_cleanup(&filter->sink); strmbase_filter_cleanup(&filter->filter); @@ -409,6 +411,21 @@ static HRESULT transform_source_get_media_type(struct strmbase_pin *pin, unsigne return filter->ops->source_get_media_type(filter, index, mt); }
+static HRESULT transform_source_query_interface(struct strmbase_pin *pin, REFIID iid, void **out) +{ + struct transform *filter = impl_from_strmbase_filter(pin->filter); + + if (IsEqualGUID(iid, &IID_IMediaPosition)) + *out = &filter->passthrough.IMediaPosition_iface; + else if (IsEqualGUID(iid, &IID_IMediaSeeking)) + *out = &filter->passthrough.IMediaSeeking_iface; + else + return E_NOINTERFACE; + + IUnknown_AddRef((IUnknown *)*out); + return S_OK; +} + static HRESULT WINAPI transform_source_DecideBufferSize(struct strmbase_source *pin, IMemAllocator *allocator, ALLOCATOR_PROPERTIES *props) { struct transform *filter = impl_from_strmbase_filter(pin->pin.filter); @@ -420,6 +437,7 @@ static const struct strmbase_source_ops source_ops = { .base.pin_query_accept = transform_source_query_accept, .base.pin_get_media_type = transform_source_get_media_type, + .base.pin_query_interface = transform_source_query_interface, .pfnAttemptConnection = BaseOutputPinImpl_AttemptConnection, .pfnDecideAllocator = BaseOutputPinImpl_DecideAllocator, .pfnDecideBufferSize = transform_source_DecideBufferSize, @@ -437,6 +455,10 @@ static HRESULT transform_create(IUnknown *outer, const CLSID *clsid, const struc strmbase_sink_init(&object->sink, &object->filter, L"In", &sink_ops, NULL); strmbase_source_init(&object->source, &object->filter, L"Out", &source_ops);
+ strmbase_passthrough_init(&object->passthrough, (IUnknown *)&object->source.pin.IPin_iface); + ISeekingPassThru_Init(&object->passthrough.ISeekingPassThru_iface, FALSE, + &object->sink.pin.IPin_iface); + object->ops = ops;
*out = object;
From: Anton Baskanov baskanov@gmail.com
Signed-off-by: Anton Baskanov baskanov@gmail.com --- dlls/quartz/tests/mpegaudio.c | 4 +- dlls/winegstreamer/quartz_transform.c | 127 ++++++++++++++++++++++++++ 2 files changed, 129 insertions(+), 2 deletions(-)
diff --git a/dlls/quartz/tests/mpegaudio.c b/dlls/quartz/tests/mpegaudio.c index 48322d019a8..d3d5c3d76f0 100644 --- a/dlls/quartz/tests/mpegaudio.c +++ b/dlls/quartz/tests/mpegaudio.c @@ -242,7 +242,7 @@ static void test_interfaces(void)
check_interface(pin, &IID_IMemInputPin, TRUE); check_interface(pin, &IID_IPin, TRUE); - todo_wine check_interface(pin, &IID_IQualityControl, TRUE); + check_interface(pin, &IID_IQualityControl, TRUE); check_interface(pin, &IID_IUnknown, TRUE);
check_interface(pin, &IID_IMediaPosition, FALSE); @@ -255,7 +255,7 @@ static void test_interfaces(void) check_interface(pin, &IID_IPin, TRUE); check_interface(pin, &IID_IMediaPosition, TRUE); check_interface(pin, &IID_IMediaSeeking, TRUE); - todo_wine check_interface(pin, &IID_IQualityControl, TRUE); + check_interface(pin, &IID_IQualityControl, TRUE); check_interface(pin, &IID_IUnknown, TRUE);
check_interface(pin, &IID_IAsyncReader, FALSE); diff --git a/dlls/winegstreamer/quartz_transform.c b/dlls/winegstreamer/quartz_transform.c index 7fa18aa7673..7986997abef 100644 --- a/dlls/winegstreamer/quartz_transform.c +++ b/dlls/winegstreamer/quartz_transform.c @@ -35,6 +35,10 @@ struct transform struct strmbase_source source; struct strmbase_passthrough passthrough;
+ IQualityControl sink_IQualityControl_iface; + IQualityControl source_IQualityControl_iface; + IQualityControl *qc_sink; + struct wg_transform *transform;
const struct transform_ops *ops; @@ -272,6 +276,8 @@ static HRESULT transform_sink_query_interface(struct strmbase_pin *pin, REFIID i
if (IsEqualGUID(iid, &IID_IMemInputPin)) *out = &filter->sink.IMemInputPin_iface; + else if (IsEqualGUID(iid, &IID_IQualityControl)) + *out = &filter->sink_IQualityControl_iface; else return E_NOINTERFACE;
@@ -419,6 +425,8 @@ static HRESULT transform_source_query_interface(struct strmbase_pin *pin, REFIID *out = &filter->passthrough.IMediaPosition_iface; else if (IsEqualGUID(iid, &IID_IMediaSeeking)) *out = &filter->passthrough.IMediaSeeking_iface; + else if (IsEqualGUID(iid, &IID_IQualityControl)) + *out = &filter->source_IQualityControl_iface; else return E_NOINTERFACE;
@@ -443,6 +451,122 @@ static const struct strmbase_source_ops source_ops = .pfnDecideBufferSize = transform_source_DecideBufferSize, };
+static struct transform *impl_from_sink_IQualityControl(IQualityControl *iface) +{ + return CONTAINING_RECORD(iface, struct transform, sink_IQualityControl_iface); +} + +static HRESULT WINAPI sink_quality_control_QueryInterface(IQualityControl *iface, REFIID iid, void **out) +{ + struct transform *filter = impl_from_sink_IQualityControl(iface); + return IPin_QueryInterface(&filter->source.pin.IPin_iface, iid, out); +} + +static ULONG WINAPI sink_quality_control_AddRef(IQualityControl *iface) +{ + struct transform *filter = impl_from_sink_IQualityControl(iface); + return IPin_AddRef(&filter->source.pin.IPin_iface); +} + +static ULONG WINAPI sink_quality_control_Release(IQualityControl *iface) +{ + struct transform *filter = impl_from_sink_IQualityControl(iface); + return IPin_Release(&filter->source.pin.IPin_iface); +} + +static HRESULT WINAPI sink_quality_control_Notify(IQualityControl *iface, IBaseFilter *sender, Quality q) +{ + struct transform *filter = impl_from_sink_IQualityControl(iface); + + TRACE("filter %p, sender %p, type %#x, proportion %ld, late %s, timestamp %s.\n", + filter, sender, q.Type, q.Proportion, debugstr_time(q.Late), debugstr_time(q.TimeStamp)); + + return S_OK; +} + +static HRESULT WINAPI sink_quality_control_SetSink(IQualityControl *iface, IQualityControl *sink) +{ + struct transform *filter = impl_from_sink_IQualityControl(iface); + + TRACE("filter %p, sink %p.\n", filter, sink); + + filter->qc_sink = sink; + + return S_OK; +} + +static const IQualityControlVtbl sink_quality_control_vtbl = +{ + sink_quality_control_QueryInterface, + sink_quality_control_AddRef, + sink_quality_control_Release, + sink_quality_control_Notify, + sink_quality_control_SetSink, +}; + +static struct transform *impl_from_source_IQualityControl(IQualityControl *iface) +{ + return CONTAINING_RECORD(iface, struct transform, source_IQualityControl_iface); +} + +static HRESULT WINAPI source_quality_control_QueryInterface(IQualityControl *iface, REFIID iid, void **out) +{ + struct transform *filter = impl_from_source_IQualityControl(iface); + return IPin_QueryInterface(&filter->source.pin.IPin_iface, iid, out); +} + +static ULONG WINAPI source_quality_control_AddRef(IQualityControl *iface) +{ + struct transform *filter = impl_from_source_IQualityControl(iface); + return IPin_AddRef(&filter->source.pin.IPin_iface); +} + +static ULONG WINAPI source_quality_control_Release(IQualityControl *iface) +{ + struct transform *filter = impl_from_source_IQualityControl(iface); + return IPin_Release(&filter->source.pin.IPin_iface); +} + +static HRESULT WINAPI source_quality_control_Notify(IQualityControl *iface, IBaseFilter *sender, Quality q) +{ + struct transform *filter = impl_from_source_IQualityControl(iface); + IQualityControl *peer; + HRESULT hr = VFW_E_NOT_FOUND; + + TRACE("filter %p, sender %p, type %#x, proportion %ld, late %s, timestamp %s.\n", + filter, sender, q.Type, q.Proportion, debugstr_time(q.Late), debugstr_time(q.TimeStamp)); + + if (filter->qc_sink) + return IQualityControl_Notify(filter->qc_sink, &filter->filter.IBaseFilter_iface, q); + + if (filter->sink.pin.peer + && SUCCEEDED(IPin_QueryInterface(filter->sink.pin.peer, &IID_IQualityControl, (void **)&peer))) + { + hr = IQualityControl_Notify(peer, &filter->filter.IBaseFilter_iface, q); + IQualityControl_Release(peer); + } + + return hr; +} + +static HRESULT WINAPI source_quality_control_SetSink(IQualityControl *iface, IQualityControl *sink) +{ + struct transform *filter = impl_from_source_IQualityControl(iface); + + TRACE("filter %p, sink %p.\n", filter, sink); + + return S_OK; +} + +static const IQualityControlVtbl source_quality_control_vtbl = +{ + source_quality_control_QueryInterface, + source_quality_control_AddRef, + source_quality_control_Release, + source_quality_control_Notify, + source_quality_control_SetSink, +}; + static HRESULT transform_create(IUnknown *outer, const CLSID *clsid, const struct transform_ops *ops, struct transform **out) { struct transform *object; @@ -459,6 +583,9 @@ static HRESULT transform_create(IUnknown *outer, const CLSID *clsid, const struc ISeekingPassThru_Init(&object->passthrough.ISeekingPassThru_iface, FALSE, &object->sink.pin.IPin_iface);
+ object->sink_IQualityControl_iface.lpVtbl = &sink_quality_control_vtbl; + object->source_IQualityControl_iface.lpVtbl = &source_quality_control_vtbl; + object->ops = ops;
*out = object;
From: Anton Baskanov baskanov@gmail.com
Signed-off-by: Anton Baskanov baskanov@gmail.com --- 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. */
This merge request was approved by Zebediah Figura.