From: Anton Baskanov baskanov@gmail.com
--- dlls/amstream/tests/amstream.c | 295 +++++++++++++++++++++++++++++++++ 1 file changed, 295 insertions(+)
diff --git a/dlls/amstream/tests/amstream.c b/dlls/amstream/tests/amstream.c index f1e17ef49cf..abf0428140b 100644 --- a/dlls/amstream/tests/amstream.c +++ b/dlls/amstream/tests/amstream.c @@ -386,6 +386,7 @@ static void test_interfaces(void) check_interface(stream, &IID_IMediaStreamFilter, FALSE); check_interface(stream, &IID_IMultiMediaStream, FALSE); check_interface(stream, &IID_IPersist, FALSE); + check_interface(stream, &IID_IQualityControl, FALSE);
IMediaStream_Release(stream);
@@ -408,6 +409,7 @@ static void test_interfaces(void) check_interface(stream, &IID_IMediaStreamFilter, FALSE); check_interface(stream, &IID_IMultiMediaStream, FALSE); check_interface(stream, &IID_IPersist, FALSE); + check_interface(stream, &IID_IQualityControl, FALSE);
IMediaStream_Release(stream);
@@ -1017,10 +1019,13 @@ struct testfilter struct strmbase_filter filter; struct strmbase_source source; IMediaSeeking IMediaSeeking_iface; + IQualityControl IQualityControl_iface; LONGLONG current_position; LONGLONG stop_position; const AM_MEDIA_TYPE *preferred_mt; HANDLE wait_state_event; + IBaseFilter *qc_notify_sender; + Quality qc_notify_quality; HRESULT get_duration_hr; HRESULT get_stop_position_hr; HRESULT set_positions_hr; @@ -1028,6 +1033,7 @@ struct testfilter HRESULT cleanup_stream_hr; HRESULT wait_state_hr; HRESULT is_format_supported_hr; + HRESULT qc_notify_hr; };
static inline struct testfilter *impl_from_BaseFilter(struct strmbase_filter *iface) @@ -1123,6 +1129,8 @@ static HRESULT testsource_query_interface(struct strmbase_pin *iface, REFIID iid
if (IsEqualGUID(iid, &IID_IMediaSeeking) && filter->IMediaSeeking_iface.lpVtbl) *out = &filter->IMediaSeeking_iface; + else if (IsEqualGUID(iid, &IID_IQualityControl) && filter->IQualityControl_iface.lpVtbl) + *out = &filter->IQualityControl_iface; else return E_NOINTERFACE;
@@ -1358,6 +1366,54 @@ static const IMediaSeekingVtbl testsource_seeking_vtbl = testsource_seeking_GetPreroll, };
+static inline struct testfilter *impl_from_IQualityControl(IQualityControl *iface) +{ + return CONTAINING_RECORD(iface, struct testfilter, IQualityControl_iface); +} + +static HRESULT WINAPI testsource_qc_QueryInterface(IQualityControl *iface, REFIID iid, void **out) +{ + struct testfilter *filter = impl_from_IQualityControl(iface); + return IBaseFilter_QueryInterface(&filter->filter.IBaseFilter_iface, iid, out); +} + +static ULONG WINAPI testsource_qc_AddRef(IQualityControl *iface) +{ + struct testfilter *filter = impl_from_IQualityControl(iface); + return IBaseFilter_AddRef(&filter->filter.IBaseFilter_iface); +} + +static ULONG WINAPI testsource_qc_Release(IQualityControl *iface) +{ + struct testfilter *filter = impl_from_IQualityControl(iface); + return IBaseFilter_Release(&filter->filter.IBaseFilter_iface); +} + +static HRESULT WINAPI testsource_qc_Notify(IQualityControl *iface, IBaseFilter *sender, Quality q) +{ + struct testfilter *filter = impl_from_IQualityControl(iface); + + filter->qc_notify_sender = sender; + filter->qc_notify_quality = q; + + return filter->qc_notify_hr; +} + +static HRESULT WINAPI testsource_qc_SetSink(IQualityControl *iface, IQualityControl *sink) +{ + ok(0, "Unexpected call.\n"); + return E_NOTIMPL; +} + +static const IQualityControlVtbl testsource_qc_vtbl = +{ + testsource_qc_QueryInterface, + testsource_qc_AddRef, + testsource_qc_Release, + testsource_qc_Notify, + testsource_qc_SetSink, +}; + #define check_get_stream(a,b,c,d) check_get_stream_(__LINE__,a,b,c,d) static void check_get_stream_(int line, IAMMultiMediaStream *mmstream, IMediaStreamFilter *filter, const GUID *mspid, IMediaStream *expect) @@ -8190,6 +8246,244 @@ static void test_ddrawstream_create_sample(void) desc.ddckCKSrcBlt.dwColorSpaceHighValue); }
+static void test_ddrawstream_qc(void) +{ + static const BYTE test_data[] = { 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11 }; + IAMMultiMediaStream *mmstream = create_ammultimediastream(); + struct advise_time_cookie cookie = { 0 }; + IDirectDrawStreamSample *stream_sample; + IDirectDrawMediaStream *ddraw_stream; + IMediaFilter *graph_media_filter; + STREAM_TIME filter_start_time; + IMemInputPin *mem_input_pin; + IMediaStreamFilter *filter; + struct testfilter source; + struct testclock clock; + STREAM_TIME start_time; + STREAM_TIME end_time; + IGraphBuilder *graph; + IMediaStream *stream; + VIDEOINFO video_info; + AM_MEDIA_TYPE mt; + HANDLE thread; + HRESULT hr; + ULONG ref; + IPin *pin; + + hr = IAMMultiMediaStream_Initialize(mmstream, STREAMTYPE_READ, 0, NULL); + ok(hr == S_OK, "Got hr %#lx.\n", hr); + hr = IAMMultiMediaStream_GetFilter(mmstream, &filter); + ok(hr == S_OK, "Got hr %#lx.\n", hr); + ok(!!filter, "Expected non-null filter.\n"); + hr = IAMMultiMediaStream_AddMediaStream(mmstream, NULL, &MSPID_PrimaryVideo, 0, &stream); + ok(hr == S_OK, "Got hr %#lx.\n", hr); + hr = IMediaStream_QueryInterface(stream, &IID_IDirectDrawMediaStream, (void **)&ddraw_stream); + ok(hr == S_OK, "Got hr %#lx.\n", hr); + hr = IMediaStream_QueryInterface(stream, &IID_IPin, (void **)&pin); + ok(hr == S_OK, "Got hr %#lx.\n", hr); + hr = IMediaStream_QueryInterface(stream, &IID_IMemInputPin, (void **)&mem_input_pin); + ok(hr == S_OK, "Got hr %#lx.\n", hr); + hr = IAMMultiMediaStream_GetFilterGraph(mmstream, &graph); + ok(hr == S_OK, "Got hr %#lx.\n", hr); + ok(graph != NULL, "Expected non-NULL graph.\n"); + hr = IGraphBuilder_QueryInterface(graph, &IID_IMediaFilter, (void **)&graph_media_filter); + ok(hr == S_OK, "Got hr %#lx.\n", hr); + testfilter_init(&source); + source.IQualityControl_iface.lpVtbl = &testsource_qc_vtbl; + hr = IGraphBuilder_AddFilter(graph, &source.filter.IBaseFilter_iface, NULL); + ok(hr == S_OK, "Got hr %#lx.\n", hr); + testclock_init(&clock); + cookie.advise_time_called_event = CreateEventW(NULL, FALSE, FALSE, NULL); + ok(cookie.advise_time_called_event != NULL, "Expected non-NULL event."); + + video_info = rgb32_video_info; + video_info.bmiHeader.biWidth = 3; + video_info.bmiHeader.biHeight = 1; + mt = rgb32_mt; + mt.pbFormat = (BYTE *)&video_info; + hr = IGraphBuilder_ConnectDirect(graph, &source.source.pin.IPin_iface, pin, &mt); + ok(hr == S_OK, "Got hr %#lx.\n", hr); + + hr = IDirectDrawMediaStream_CreateSample(ddraw_stream, NULL, NULL, 0, &stream_sample); + ok(hr == S_OK, "Got hr %#lx.\n", hr); + + /* There are no quality control messages without a sync source. */ + hr = IMediaFilter_SetSyncSource(graph_media_filter, NULL); + ok(hr == S_OK, "Got hr %#lx.\n", hr); + + hr = IAMMultiMediaStream_SetState(mmstream, STREAMSTATE_RUN); + ok(hr == S_OK, "Got hr %#lx.\n", hr); + + source.qc_notify_sender = (IBaseFilter *)0xdeadbeef; + source.qc_notify_quality.Type = Flood; + source.qc_notify_quality.Proportion = 0xdeadbeef; + source.qc_notify_quality.Late = 0xdeadbeef; + source.qc_notify_quality.TimeStamp = 0xdeadbeef; + + thread = ammediastream_async_receive_time(&source, + 11111111, 11111111, test_data, sizeof(test_data)); + ok(WaitForSingleObject(thread, 100) == WAIT_TIMEOUT, "Receive returned prematurely.\n"); + + hr = IDirectDrawStreamSample_Update(stream_sample, 0, NULL, NULL, 0); + ok(hr == S_OK, "Got hr %#lx.\n", hr); + + ok(!WaitForSingleObject(thread, 2000), "Wait timed out.\n"); + CloseHandle(thread); + + ok(source.qc_notify_sender == (IBaseFilter *)0xdeadbeef, "Got sender %p.\n", + source.qc_notify_sender); + ok(source.qc_notify_quality.Type == Flood, "Got type %d.\n", + source.qc_notify_quality.Type); + ok(source.qc_notify_quality.Proportion == 0xdeadbeef, "Got proportion %ld.\n", + source.qc_notify_quality.Proportion); + ok(source.qc_notify_quality.Late == 0xdeadbeef, + "Got late %s.\n", wine_dbgstr_longlong(source.qc_notify_quality.Late)); + ok(source.qc_notify_quality.TimeStamp == 0xdeadbeef, "Got time stamp %s.\n", + wine_dbgstr_longlong(source.qc_notify_quality.TimeStamp)); + + hr = IAMMultiMediaStream_SetState(mmstream, STREAMSTATE_STOP); + ok(hr == S_OK, "Got hr %#lx.\n", hr); + + hr = IMediaFilter_SetSyncSource(graph_media_filter, &clock.IReferenceClock_iface); + ok(hr == S_OK, "Got hr %#lx.\n", hr); + + clock.time = 12345678; + + hr = IAMMultiMediaStream_SetState(mmstream, STREAMSTATE_RUN); + ok(hr == S_OK, "Got hr %#lx.\n", hr); + + hr = IMediaStreamFilter_GetCurrentStreamTime(filter, &filter_start_time); + ok(hr == S_OK, "Got hr %#lx.\n", hr); + + /* Quality control message is sent on update completion, q.Late is computed + * as a difference between sample start time and update completion time. */ + + /* Test Update() before Reveive(). */ + source.qc_notify_sender = (IBaseFilter *)0xdeadbeef; + source.qc_notify_quality.Type = Flood; + source.qc_notify_quality.Proportion = 0xdeadbeef; + source.qc_notify_quality.Late = 0xdeadbeef; + source.qc_notify_quality.TimeStamp = 0xdeadbeef; + + clock.time = 12345678 - filter_start_time + 11111111; + + hr = IDirectDrawStreamSample_Update(stream_sample, SSUPDATE_ASYNC, NULL, NULL, 0); + ok(hr == MS_S_PENDING, "Got hr %#lx.\n", hr); + + clock.advise_time_cookie = &cookie; + clock.time = 12345678 - filter_start_time + 11111111 + 100000; + + start_time = 11111111 + 200000; + end_time = 11111111 + 200000; + thread = ammediastream_async_receive_time(&source, + start_time, end_time, test_data, sizeof(test_data)); + ok(!WaitForSingleObject(cookie.advise_time_called_event, 2000), "Wait timed out.\n"); + + ok(source.qc_notify_sender == (IBaseFilter *)0xdeadbeef, "Got sender %p.\n", + source.qc_notify_sender); + ok(source.qc_notify_quality.Type == Flood, "Got type %d.\n", + source.qc_notify_quality.Type); + ok(source.qc_notify_quality.Proportion == 0xdeadbeef, "Got proportion %ld.\n", + source.qc_notify_quality.Proportion); + ok(source.qc_notify_quality.Late == 0xdeadbeef, + "Got late %s.\n", wine_dbgstr_longlong(source.qc_notify_quality.Late)); + ok(source.qc_notify_quality.TimeStamp == 0xdeadbeef, "Got time stamp %s.\n", + wine_dbgstr_longlong(source.qc_notify_quality.TimeStamp)); + + clock.time = 12345678 - filter_start_time + 11111111 + 200000; + + SetEvent(cookie.event); + + ok(!WaitForSingleObject(thread, 2000), "Wait timed out.\n"); + CloseHandle(thread); + + hr = IDirectDrawStreamSample_CompletionStatus(stream_sample, 0, 0); + ok(hr == S_OK, "Got hr %#lx.\n", hr); + + todo_wine ok(source.qc_notify_sender == (IBaseFilter *)filter, "Got sender %p.\n", + source.qc_notify_sender); + todo_wine ok(source.qc_notify_quality.Type == Famine, "Got type %d.\n", + source.qc_notify_quality.Type); + todo_wine ok(source.qc_notify_quality.Proportion == 1000, "Got proportion %ld.\n", + source.qc_notify_quality.Proportion); + todo_wine ok(source.qc_notify_quality.Late == 0, "Got late %s.\n", + wine_dbgstr_longlong(source.qc_notify_quality.Late)); + todo_wine ok(source.qc_notify_quality.TimeStamp == start_time, "Got time stamp %s.\n", + wine_dbgstr_longlong(source.qc_notify_quality.TimeStamp)); + + /* Test Update() after Reveive(). */ + source.qc_notify_sender = (IBaseFilter *)0xdeadbeef; + source.qc_notify_quality.Type = Flood; + source.qc_notify_quality.Proportion = 0xdeadbeef; + source.qc_notify_quality.Late = 0xdeadbeef; + source.qc_notify_quality.TimeStamp = 0xdeadbeef; + + clock.advise_time_cookie = &cookie; + clock.time = 12345678 - filter_start_time + 11111111 + 300000; + + start_time = 11111111 + 400000; + end_time = 11111111 + 400000; + thread = ammediastream_async_receive_time(&source, + start_time, end_time, test_data, sizeof(test_data)); + ok(!WaitForSingleObject(cookie.advise_time_called_event, 2000), "Wait timed out.\n"); + + clock.time = 12345678 - filter_start_time + 11111111 + 400000; + SetEvent(cookie.event); + + ok(WaitForSingleObject(thread, 100) == WAIT_TIMEOUT, "Receive returned prematurely.\n"); + + ok(source.qc_notify_sender == (IBaseFilter *)0xdeadbeef, "Got sender %p.\n", + source.qc_notify_sender); + ok(source.qc_notify_quality.Type == Flood, "Got type %d.\n", + source.qc_notify_quality.Type); + ok(source.qc_notify_quality.Proportion == 0xdeadbeef, "Got proportion %ld.\n", + source.qc_notify_quality.Proportion); + ok(source.qc_notify_quality.Late == 0xdeadbeef, + "Got late %s.\n", wine_dbgstr_longlong(source.qc_notify_quality.Late)); + ok(source.qc_notify_quality.TimeStamp == 0xdeadbeef, "Got time stamp %s.\n", + wine_dbgstr_longlong(source.qc_notify_quality.TimeStamp)); + + clock.time = 12345678 - filter_start_time + 11111111 + 500000; + + hr = IDirectDrawStreamSample_Update(stream_sample, 0, NULL, NULL, 0); + ok(hr == S_OK, "Got hr %#lx.\n", hr); + + ok(!WaitForSingleObject(thread, 2000), "Wait timed out.\n"); + CloseHandle(thread); + + todo_wine ok(source.qc_notify_sender == (IBaseFilter *)filter, "Got sender %p.\n", + source.qc_notify_sender); + todo_wine ok(source.qc_notify_quality.Type == Famine, "Got type %d.\n", + source.qc_notify_quality.Type); + todo_wine ok(source.qc_notify_quality.Proportion == 1000, "Got proportion %ld.\n", + source.qc_notify_quality.Proportion); + todo_wine ok(source.qc_notify_quality.Late == 100000, "Got late %s.\n", + wine_dbgstr_longlong(source.qc_notify_quality.Late)); + todo_wine ok(source.qc_notify_quality.TimeStamp == start_time, "Got time stamp %s.\n", + wine_dbgstr_longlong(source.qc_notify_quality.TimeStamp)); + + hr = IAMMultiMediaStream_SetState(mmstream, STREAMSTATE_STOP); + ok(hr == S_OK, "Got hr %#lx.\n", hr); + IGraphBuilder_Disconnect(graph, pin); + IGraphBuilder_Disconnect(graph, &source.source.pin.IPin_iface); + + CloseHandle(cookie.advise_time_called_event); + ref = IDirectDrawStreamSample_Release(stream_sample); + ok(!ref, "Got outstanding refcount %ld.\n", ref); + ref = IAMMultiMediaStream_Release(mmstream); + ok(!ref, "Got outstanding refcount %ld.\n", ref); + IMediaFilter_Release(graph_media_filter); + ref = IGraphBuilder_Release(graph); + ok(!ref, "Got outstanding refcount %ld.\n", ref); + ref = IMediaStreamFilter_Release(filter); + ok(!ref, "Got outstanding refcount %ld.\n", ref); + IPin_Release(pin); + IMemInputPin_Release(mem_input_pin); + IDirectDrawMediaStream_Release(ddraw_stream); + ref = IMediaStream_Release(stream); + ok(!ref, "Got outstanding refcount %ld.\n", ref); +} + static void test_ddrawstreamsample_get_media_stream(void) { IAMMultiMediaStream *mmstream = create_ammultimediastream(); @@ -9516,6 +9810,7 @@ START_TEST(amstream) test_ddrawstream_begin_flush_end_flush(); test_ddrawstream_new_segment(); test_ddrawstream_get_time_per_frame(); + test_ddrawstream_qc();
test_ddrawstreamsample_get_media_stream(); test_ddrawstreamsample_update();