Signed-off-by: Anton Baskanov baskanov@gmail.com --- dlls/amstream/tests/amstream.c | 285 ++++++++++++++++++++++++++++++++- 1 file changed, 280 insertions(+), 5 deletions(-)
diff --git a/dlls/amstream/tests/amstream.c b/dlls/amstream/tests/amstream.c index adfec169df9..39d65ebaaad 100644 --- a/dlls/amstream/tests/amstream.c +++ b/dlls/amstream/tests/amstream.c @@ -1166,7 +1166,6 @@ static ULONG WINAPI testsource_seeking_Release(IMediaSeeking *iface)
static HRESULT WINAPI testsource_seeking_GetCapabilities(IMediaSeeking *iface, DWORD *capabilities) { - ok(0, "Unexpected call.\n"); return E_NOTIMPL; }
@@ -1200,7 +1199,6 @@ static HRESULT WINAPI testsource_seeking_GetTimeFormat(IMediaSeeking *iface, GUI
static HRESULT WINAPI testsource_seeking_IsUsingTimeFormat(IMediaSeeking *iface, const GUID *format) { - ok(0, "Unexpected call.\n"); return E_NOTIMPL; }
@@ -2033,12 +2031,18 @@ static void test_pin_info(void) struct graph { IFilterGraph2 IFilterGraph2_iface; + IMediaEventSink IMediaEventSink_iface; IUnknown *inner_unk; IFilterGraph2 *inner; + IMediaEventSink *inner_event_sink; LONG refcount; unsigned int got_add_filter; IBaseFilter *filter; WCHAR filter_name[128]; + unsigned int got_notify; + LONG event_code; + LONG_PTR event_param1; + LONG_PTR event_param2; };
static struct graph *impl_from_IFilterGraph2(IFilterGraph2 *iface) @@ -2046,6 +2050,11 @@ static struct graph *impl_from_IFilterGraph2(IFilterGraph2 *iface) return CONTAINING_RECORD(iface, struct graph, IFilterGraph2_iface); }
+static struct graph *impl_from_IMediaEventSink(IMediaEventSink *iface) +{ + return CONTAINING_RECORD(iface, struct graph, IMediaEventSink_iface); +} + static HRESULT WINAPI graph_QueryInterface(IFilterGraph2 *iface, REFIID iid, void **out) { struct graph *graph = impl_from_IFilterGraph2(iface); @@ -2059,9 +2068,13 @@ static HRESULT WINAPI graph_QueryInterface(IFilterGraph2 *iface, REFIID iid, voi IFilterGraph2_AddRef(iface); return S_OK; } - else if (IsEqualGUID(iid, &IID_IMediaSeeking) - || IsEqualGUID(iid, &IID_IMediaControl) - || IsEqualGUID(iid, &IID_IMediaEventEx)) + else if (IsEqualGUID(iid, &IID_IMediaEventSink)) + { + *out = &graph->IMediaEventSink_iface; + IFilterGraph2_AddRef(iface); + return S_OK; + } + else { return IUnknown_QueryInterface(graph->inner_unk, iid, out); } @@ -2223,24 +2236,65 @@ static const IFilterGraph2Vtbl graph_vtbl = graph_RenderEx, };
+static HRESULT WINAPI event_sink_QueryInterface(IMediaEventSink *iface, REFIID iid, void **out) +{ + struct graph *graph = impl_from_IMediaEventSink(iface); + return IFilterGraph2_QueryInterface(&graph->IFilterGraph2_iface, iid, out); +} + +static ULONG WINAPI event_sink_AddRef(IMediaEventSink *iface) +{ + struct graph *graph = impl_from_IMediaEventSink(iface); + return IFilterGraph2_AddRef(&graph->IFilterGraph2_iface); +} + +static ULONG WINAPI event_sink_Release(IMediaEventSink *iface) +{ + struct graph *graph = impl_from_IMediaEventSink(iface); + return IFilterGraph2_Release(&graph->IFilterGraph2_iface); +} + +static HRESULT WINAPI event_sink_Notify(IMediaEventSink *iface, + LONG code, LONG_PTR param1, LONG_PTR param2) +{ + struct graph *graph = impl_from_IMediaEventSink(iface); + ++graph->got_notify; + graph->event_code = code; + graph->event_param1 = param1; + graph->event_param2 = param2; + return IMediaEventSink_Notify(graph->inner_event_sink, code, param1, param2); +} + +static const IMediaEventSinkVtbl event_sink_vtbl = +{ + event_sink_QueryInterface, + event_sink_AddRef, + event_sink_Release, + event_sink_Notify, +}; + static void graph_init(struct graph *graph) { HRESULT hr;
memset(graph, 0, sizeof(*graph)); graph->IFilterGraph2_iface.lpVtbl = &graph_vtbl; + graph->IMediaEventSink_iface.lpVtbl = &event_sink_vtbl; graph->refcount = 1; hr = CoCreateInstance(&CLSID_FilterGraph, (IUnknown *)&graph->IFilterGraph2_iface, CLSCTX_INPROC_SERVER, &IID_IUnknown, (void **)&graph->inner_unk); ok(hr == S_OK, "Got hr %#x.\n", hr); hr = IUnknown_QueryInterface(graph->inner_unk, &IID_IFilterGraph2, (void **)&graph->inner); ok(hr == S_OK, "Got hr %#x.\n", hr); + hr = IUnknown_QueryInterface(graph->inner_unk, &IID_IMediaEventSink, (void **)&graph->inner_event_sink); + ok(hr == S_OK, "Got hr %#x.\n", hr); }
static void graph_destroy(struct graph *graph) { ULONG ref;
+ IMediaEventSink_Release(graph->inner_event_sink); IFilterGraph2_Release(graph->inner); ref = IUnknown_Release(graph->inner_unk); ok(!ref, "Got outstanding refcount %d.\n", ref); @@ -6851,6 +6905,226 @@ static void test_mediastreamfilter_wait_until(void) ok(!ref, "Got outstanding refcount %d.\n", ref); }
+static void test_mediastreamfilter_end_of_stream(void) +{ + IAMMultiMediaStream *mmstream = create_ammultimediastream(); + struct testfilter source1, source2; + IMediaStream *stream1, *stream2; + IMediaControl *media_control; + IMediaStreamFilter *filter; + struct graph graph; + IPin *pin1, *pin2; + HRESULT hr; + ULONG ref; + + graph_init(&graph); + hr = IFilterGraph2_QueryInterface(&graph.IFilterGraph2_iface, &IID_IMediaControl, (void **)&media_control); + ok(hr == S_OK, "Got hr %#x.\n", hr); + + hr = IAMMultiMediaStream_Initialize(mmstream, STREAMTYPE_READ, 0, (IGraphBuilder *)&graph.IFilterGraph2_iface); + ok(hr == S_OK, "Got hr %#x.\n", hr); + hr = IAMMultiMediaStream_AddMediaStream(mmstream, NULL, &MSPID_PrimaryAudio, 0, &stream1); + ok(hr == S_OK, "Got hr %#x.\n", hr); + hr = IAMMultiMediaStream_AddMediaStream(mmstream, NULL, &MSPID_PrimaryVideo, 0, &stream2); + ok(hr == S_OK, "Got hr %#x.\n", hr); + hr = IMediaStream_QueryInterface(stream1, &IID_IPin, (void **)&pin1); + ok(hr == S_OK, "Got hr %#x.\n", hr); + hr = IMediaStream_QueryInterface(stream2, &IID_IPin, (void **)&pin2); + ok(hr == S_OK, "Got hr %#x.\n", hr); + hr = IAMMultiMediaStream_GetFilter(mmstream, &filter); + ok(hr == S_OK, "Got hr %#x.\n", hr); + ok(filter != NULL, "Expected non-null filter\n"); + testfilter_init(&source1); + testfilter_init(&source2); + hr = IFilterGraph2_AddFilter(&graph.IFilterGraph2_iface, &source1.filter.IBaseFilter_iface, NULL); + ok(hr == S_OK, "Got hr %#x.\n", hr); + hr = IFilterGraph2_AddFilter(&graph.IFilterGraph2_iface, &source2.filter.IBaseFilter_iface, NULL); + ok(hr == S_OK, "Got hr %#x.\n", hr); + + hr = IPin_Connect(&source1.source.pin.IPin_iface, pin1, &audio_mt); + ok(hr == S_OK, "Got hr %#x.\n", hr); + hr = IPin_Connect(&source2.source.pin.IPin_iface, pin2, &rgb32_mt); + ok(hr == S_OK, "Got hr %#x.\n", hr); + + /* Initially, EC_COMPLETE notifications are disabled. */ + hr = IAMMultiMediaStream_SetState(mmstream, STREAMSTATE_RUN); + ok(hr == S_OK, "Got hr %#x.\n", hr); + + graph.got_notify = 0; + + hr = IMediaStreamFilter_EndOfStream(filter); + todo_wine ok(hr == S_OK, "Got hr %#x.\n", hr); + hr = IMediaStreamFilter_EndOfStream(filter); + todo_wine ok(hr == S_OK, "Got hr %#x.\n", hr); + + ok(graph.got_notify == 0, "Got %d calls to IMediaEventSink::Notify().\n", graph.got_notify); + + hr = IAMMultiMediaStream_SetState(mmstream, STREAMSTATE_STOP); + ok(hr == S_OK, "Got hr %#x.\n", hr); + + /* Unsuccsessful call to SupportSeeking does not enable EC_COMPLETE notifications. */ + hr = IMediaStreamFilter_SupportSeeking(filter, TRUE); + ok(hr == E_NOINTERFACE, "Got hr %#x.\n", hr); + + hr = IAMMultiMediaStream_SetState(mmstream, STREAMSTATE_RUN); + ok(hr == S_OK, "Got hr %#x.\n", hr); + + graph.got_notify = 0; + + hr = IMediaStreamFilter_EndOfStream(filter); + todo_wine ok(hr == S_OK, "Got hr %#x.\n", hr); + hr = IMediaStreamFilter_EndOfStream(filter); + todo_wine ok(hr == S_OK, "Got hr %#x.\n", hr); + + ok(graph.got_notify == 0, "Got %d calls to IMediaEventSink::Notify().\n", graph.got_notify); + + hr = IAMMultiMediaStream_SetState(mmstream, STREAMSTATE_STOP); + ok(hr == S_OK, "Got hr %#x.\n", hr); + + /* Successful call to SupportSeeking enables EC_COMPLETE notifications. */ + source1.IMediaSeeking_iface.lpVtbl = &testsource_seeking_vtbl; + + hr = IMediaStreamFilter_SupportSeeking(filter, TRUE); + ok(hr == S_OK, "Got hr %#x.\n", hr); + + hr = IAMMultiMediaStream_SetState(mmstream, STREAMSTATE_RUN); + ok(hr == S_OK, "Got hr %#x.\n", hr); + + graph.got_notify = 0; + + hr = IMediaStreamFilter_EndOfStream(filter); + todo_wine ok(hr == S_OK, "Got hr %#x.\n", hr); + + ok(graph.got_notify == 0, "Got %d calls to IMediaEventSink::Notify().\n", graph.got_notify); + + hr = IMediaStreamFilter_EndOfStream(filter); + todo_wine ok(hr == S_OK, "Got hr %#x.\n", hr); + + todo_wine ok(graph.got_notify == 1, "Got %d calls to IMediaEventSink::Notify().\n", graph.got_notify); + + hr = IAMMultiMediaStream_SetState(mmstream, STREAMSTATE_STOP); + ok(hr == S_OK, "Got hr %#x.\n", hr); + + /* EC_COMPLETE is sent on paused->running state transition + * if EndOfStream has been called for all streams. */ + graph.got_notify = 0; + + hr = IMediaStreamFilter_EndOfStream(filter); + todo_wine ok(hr == S_OK, "Got hr %#x.\n", hr); + hr = IMediaStreamFilter_EndOfStream(filter); + todo_wine ok(hr == S_OK, "Got hr %#x.\n", hr); + + ok(graph.got_notify == 0, "Got %d calls to IMediaEventSink::Notify().\n", graph.got_notify); + + hr = IMediaControl_Pause(media_control); + ok(hr == S_OK, "Got hr %#x.\n", hr); + + ok(graph.got_notify == 0, "Got %d calls to IMediaEventSink::Notify().\n", graph.got_notify); + + hr = IMediaControl_Run(media_control); + ok(hr == S_OK, "Got hr %#x.\n", hr); + + todo_wine ok(graph.got_notify == 1, "Got %d calls to IMediaEventSink::Notify().\n", graph.got_notify); + + hr = IMediaControl_Stop(media_control); + ok(hr == S_OK, "Got hr %#x.\n", hr); + + /* EndOfStream count is reset on paused->stopped state transition. */ + hr = IAMMultiMediaStream_SetState(mmstream, STREAMSTATE_RUN); + ok(hr == S_OK, "Got hr %#x.\n", hr); + + graph.got_notify = 0; + + hr = IMediaStreamFilter_EndOfStream(filter); + todo_wine ok(hr == S_OK, "Got hr %#x.\n", hr); + + hr = IAMMultiMediaStream_SetState(mmstream, STREAMSTATE_STOP); + ok(hr == S_OK, "Got hr %#x.\n", hr); + hr = IAMMultiMediaStream_SetState(mmstream, STREAMSTATE_RUN); + ok(hr == S_OK, "Got hr %#x.\n", hr); + + hr = IMediaStreamFilter_EndOfStream(filter); + todo_wine ok(hr == S_OK, "Got hr %#x.\n", hr); + + ok(graph.got_notify == 0, "Got %d calls to IMediaEventSink::Notify().\n", graph.got_notify); + + hr = IAMMultiMediaStream_SetState(mmstream, STREAMSTATE_STOP); + ok(hr == S_OK, "Got hr %#x.\n", hr); + + /* EOS count is not reset on running->paused state transition. */ + hr = IMediaControl_Run(media_control); + ok(hr == S_OK, "Got hr %#x.\n", hr); + + graph.got_notify = 0; + + hr = IMediaStreamFilter_EndOfStream(filter); + todo_wine ok(hr == S_OK, "Got hr %#x.\n", hr); + + hr = IMediaControl_Pause(media_control); + ok(hr == S_OK, "Got hr %#x.\n", hr); + hr = IMediaControl_Run(media_control); + ok(hr == S_OK, "Got hr %#x.\n", hr); + + hr = IMediaStreamFilter_EndOfStream(filter); + todo_wine ok(hr == S_OK, "Got hr %#x.\n", hr); + + todo_wine ok(graph.got_notify == 1, "Got %d calls to IMediaEventSink::Notify().\n", graph.got_notify); + + hr = IMediaControl_Stop(media_control); + ok(hr == S_OK, "Got hr %#x.\n", hr); + + /* Flush with cancel_eos=TRUE decrements EOS count. */ + hr = IAMMultiMediaStream_SetState(mmstream, STREAMSTATE_RUN); + ok(hr == S_OK, "Got hr %#x.\n", hr); + + graph.got_notify = 0; + + hr = IMediaStreamFilter_EndOfStream(filter); + todo_wine ok(hr == S_OK, "Got hr %#x.\n", hr); + + hr = IMediaStreamFilter_Flush(filter, TRUE); + + hr = IMediaStreamFilter_EndOfStream(filter); + todo_wine ok(hr == S_OK, "Got hr %#x.\n", hr); + + ok(graph.got_notify == 0, "Got %d calls to IMediaEventSink::Notify().\n", graph.got_notify); + + hr = IAMMultiMediaStream_SetState(mmstream, STREAMSTATE_STOP); + ok(hr == S_OK, "Got hr %#x.\n", hr); + + /* Flush with cancel_eos=FALSE does not decrement EOS count. */ + hr = IAMMultiMediaStream_SetState(mmstream, STREAMSTATE_RUN); + ok(hr == S_OK, "Got hr %#x.\n", hr); + + graph.got_notify = 0; + + hr = IMediaStreamFilter_EndOfStream(filter); + todo_wine ok(hr == S_OK, "Got hr %#x.\n", hr); + + hr = IMediaStreamFilter_Flush(filter, FALSE); + + hr = IMediaStreamFilter_EndOfStream(filter); + todo_wine ok(hr == S_OK, "Got hr %#x.\n", hr); + + todo_wine ok(graph.got_notify == 1, "Got %d calls to IMediaEventSink::Notify().\n", graph.got_notify); + + hr = IAMMultiMediaStream_SetState(mmstream, STREAMSTATE_STOP); + ok(hr == S_OK, "Got hr %#x.\n", hr); + + ref = IAMMultiMediaStream_Release(mmstream); + ok(!ref, "Got outstanding refcount %d.\n", ref); + IMediaControl_Release(media_control); + graph_destroy(&graph); + ref = IMediaStreamFilter_Release(filter); + ok(!ref, "Got outstanding refcount %d.\n", ref); + IPin_Release(pin1); + ref = IMediaStream_Release(stream1); + ok(!ref, "Got outstanding refcount %d.\n", ref); + IPin_Release(pin2); + ref = IMediaStream_Release(stream2); + ok(!ref, "Got outstanding refcount %d.\n", ref); +} + static void test_ddrawstream_getsetdirectdraw(void) { IAMMultiMediaStream *mmstream = create_ammultimediastream(); @@ -8758,6 +9032,7 @@ START_TEST(amstream) test_mediastreamfilter_get_current_stream_time(); test_mediastreamfilter_reference_time_to_stream_time(); test_mediastreamfilter_wait_until(); + test_mediastreamfilter_end_of_stream();
CoUninitialize(); }