[PATCH 1/5] amstream/tests: Refactor test graph to support multiple interfaces.
Signed-off-by: Anton Baskanov <baskanov(a)gmail.com> --- dlls/amstream/tests/amstream.c | 96 +++++++++++++++++++++++----------- 1 file changed, 65 insertions(+), 31 deletions(-) diff --git a/dlls/amstream/tests/amstream.c b/dlls/amstream/tests/amstream.c index f422a6a8db9..adfec169df9 100644 --- a/dlls/amstream/tests/amstream.c +++ b/dlls/amstream/tests/amstream.c @@ -2030,15 +2030,25 @@ static void test_pin_info(void) ok(!ref, "Got outstanding refcount %d.\n", ref); } -static IUnknown *graph_inner_unk; -static IFilterGraph2 *graph_inner; -static LONG graph_refcount = 1; -static unsigned int got_add_filter; -static IBaseFilter *graph_filter; -static WCHAR graph_filter_name[128]; +struct graph +{ + IFilterGraph2 IFilterGraph2_iface; + IUnknown *inner_unk; + IFilterGraph2 *inner; + LONG refcount; + unsigned int got_add_filter; + IBaseFilter *filter; + WCHAR filter_name[128]; +}; + +static struct graph *impl_from_IFilterGraph2(IFilterGraph2 *iface) +{ + return CONTAINING_RECORD(iface, struct graph, IFilterGraph2_iface); +} static HRESULT WINAPI graph_QueryInterface(IFilterGraph2 *iface, REFIID iid, void **out) { + struct graph *graph = impl_from_IFilterGraph2(iface); if (winetest_debug > 1) trace("QueryInterface(%s)\n", wine_dbgstr_guid(iid)); if (IsEqualGUID(iid, &IID_IFilterGraph2) || IsEqualGUID(iid, &IID_IGraphBuilder) @@ -2053,31 +2063,34 @@ static HRESULT WINAPI graph_QueryInterface(IFilterGraph2 *iface, REFIID iid, voi || IsEqualGUID(iid, &IID_IMediaControl) || IsEqualGUID(iid, &IID_IMediaEventEx)) { - return IUnknown_QueryInterface(graph_inner_unk, iid, out); + return IUnknown_QueryInterface(graph->inner_unk, iid, out); } return E_NOINTERFACE; } static ULONG WINAPI graph_AddRef(IFilterGraph2 *iface) { - return InterlockedIncrement(&graph_refcount); + struct graph *graph = impl_from_IFilterGraph2(iface); + return InterlockedIncrement(&graph->refcount); } static ULONG WINAPI graph_Release(IFilterGraph2 *iface) { - return InterlockedDecrement(&graph_refcount); + struct graph *graph = impl_from_IFilterGraph2(iface); + return InterlockedDecrement(&graph->refcount); } static HRESULT WINAPI graph_AddFilter(IFilterGraph2 *iface, IBaseFilter *filter, const WCHAR *name) { + struct graph *graph = impl_from_IFilterGraph2(iface); if (winetest_debug > 1) trace("AddFilter(%p, %s)\n", filter, wine_dbgstr_w(name)); - ++got_add_filter; - graph_filter = filter; + ++graph->got_add_filter; + graph->filter = filter; if (name) - wcscpy(graph_filter_name, name); + wcscpy(graph->filter_name, name); else - graph_filter_name[0] = 0; - return IFilterGraph2_AddFilter(graph_inner, filter, name); + graph->filter_name[0] = 0; + return IFilterGraph2_AddFilter(graph->inner, filter, name); } static HRESULT WINAPI graph_RemoveFilter(IFilterGraph2 *iface, IBaseFilter *filter) @@ -2088,8 +2101,9 @@ static HRESULT WINAPI graph_RemoveFilter(IFilterGraph2 *iface, IBaseFilter *filt static HRESULT WINAPI graph_EnumFilters(IFilterGraph2 *iface, IEnumFilters **enumfilters) { + struct graph *graph = impl_from_IFilterGraph2(iface); if (winetest_debug > 1) trace("EnumFilters()\n"); - return IFilterGraph2_EnumFilters(graph_inner, enumfilters); + return IFilterGraph2_EnumFilters(graph->inner, enumfilters); } static HRESULT WINAPI graph_FindFilterByName(IFilterGraph2 *iface, const WCHAR *name, IBaseFilter **filter) @@ -2209,13 +2223,36 @@ static const IFilterGraph2Vtbl graph_vtbl = graph_RenderEx, }; +static void graph_init(struct graph *graph) +{ + HRESULT hr; + + memset(graph, 0, sizeof(*graph)); + graph->IFilterGraph2_iface.lpVtbl = &graph_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); +} + +static void graph_destroy(struct graph *graph) +{ + ULONG ref; + + IFilterGraph2_Release(graph->inner); + ref = IUnknown_Release(graph->inner_unk); + ok(!ref, "Got outstanding refcount %d.\n", ref); +} + static void test_initialize(void) { IAMMultiMediaStream *mmstream = create_ammultimediastream(); - IFilterGraph2 graph = {&graph_vtbl}; IMediaStreamFilter *filter; IGraphBuilder *ret_graph; IMediaStream *stream; + struct graph graph; STREAM_TYPE type; HRESULT hr; ULONG ref; @@ -2350,9 +2387,7 @@ static void test_initialize(void) mmstream = create_ammultimediastream(); - CoCreateInstance(&CLSID_FilterGraph, (IUnknown *)&graph, CLSCTX_INPROC_SERVER, - &IID_IUnknown, (void **)&graph_inner_unk); - IUnknown_QueryInterface(graph_inner_unk, &IID_IFilterGraph2, (void **)&graph_inner); + graph_init(&graph); ret_graph = (IGraphBuilder *)0xdeadbeef; hr = IAMMultiMediaStream_GetFilterGraph(mmstream, &ret_graph); @@ -2363,27 +2398,27 @@ static void test_initialize(void) ok(hr == S_OK, "Got hr %#x.\n", hr); ok(!!filter, "Expected a non-NULL filter."); - got_add_filter = 0; - hr = IAMMultiMediaStream_Initialize(mmstream, STREAMTYPE_READ, 0, (IGraphBuilder *)&graph); + graph.got_add_filter = 0; + hr = IAMMultiMediaStream_Initialize(mmstream, STREAMTYPE_READ, 0, (IGraphBuilder *)&graph.IFilterGraph2_iface); ok(hr == S_OK, "Got hr %#x.\n", hr); - ok(got_add_filter == 1, "Got %d calls to IGraphBuilder::AddFilter().\n", got_add_filter); - ok(graph_filter == (IBaseFilter *)filter, "Got filter %p.\n", filter); - ok(!wcscmp(graph_filter_name, L"MediaStreamFilter"), "Got unexpected name %s.\n", wine_dbgstr_w(graph_filter_name)); + ok(graph.got_add_filter == 1, "Got %d calls to IGraphBuilder::AddFilter().\n", graph.got_add_filter); + ok(graph.filter == (IBaseFilter *)filter, "Got filter %p.\n", filter); + ok(!wcscmp(graph.filter_name, L"MediaStreamFilter"), "Got unexpected name %s.\n", wine_dbgstr_w(graph.filter_name)); hr = IAMMultiMediaStream_GetFilterGraph(mmstream, &ret_graph); ok(hr == S_OK, "Got hr %#x.\n", hr); ok(ret_graph == (IGraphBuilder *)&graph, "Got unexpected graph %p.\n", ret_graph); IGraphBuilder_Release(ret_graph); - got_add_filter = 0; + graph.got_add_filter = 0; hr = IAMMultiMediaStream_AddMediaStream(mmstream, NULL, &MSPID_PrimaryAudio, 0, NULL); ok(hr == S_OK, "Got hr %#x.\n", hr); - ok(!got_add_filter, "Got %d calls to IGraphBuilder::AddFilter().\n", got_add_filter); + ok(!graph.got_add_filter, "Got %d calls to IGraphBuilder::AddFilter().\n", graph.got_add_filter); - got_add_filter = 0; + graph.got_add_filter = 0; hr = IAMMultiMediaStream_AddMediaStream(mmstream, NULL, &MSPID_PrimaryVideo, 0, NULL); ok(hr == S_OK, "Got hr %#x.\n", hr); - ok(!got_add_filter, "Got %d calls to IGraphBuilder::AddFilter().\n", got_add_filter); + ok(!graph.got_add_filter, "Got %d calls to IGraphBuilder::AddFilter().\n", graph.got_add_filter); hr = IAMMultiMediaStream_Initialize(mmstream, STREAMTYPE_READ, 0, (IGraphBuilder *)&graph); ok(hr == E_INVALIDARG, "Got hr %#x.\n", hr); @@ -2397,9 +2432,8 @@ static void test_initialize(void) IMediaStreamFilter_Release(filter); ref = IAMMultiMediaStream_Release(mmstream); ok(!ref, "Got outstanding refcount %d.\n", ref); - IFilterGraph2_Release(graph_inner); - ok(graph_refcount == 1, "Got outstanding refcount %d.\n", graph_refcount); - IUnknown_Release(graph_inner_unk); + + graph_destroy(&graph); } static IAMMultiMediaStream *mmstream_mmstream; -- 2.25.1
Signed-off-by: Anton Baskanov <baskanov(a)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(); } -- 2.25.1
Signed-off-by: Zebediah Figura <z.figura12(a)gmail.com>
On 4/6/21 1:04 PM, Anton Baskanov wrote:
+ /* 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); +
It may be worth expanding this to check whether e.g. {EOS, EOS, flush, EOS} generates a notification.
On среда, 7 апреля 2021 г. 22:43:02 +07 you wrote:
On 4/6/21 1:04 PM, Anton Baskanov wrote:
+ /* 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); +
It may be worth expanding this to check whether e.g. {EOS, EOS, flush, EOS} generates a notification.
I've added the test and it passes both in Windows and Wine. I'll send it in the next batch so that it applies cleanly if you don't mind.
Signed-off-by: Anton Baskanov <baskanov(a)gmail.com> --- This is required to avoid deadlocks, e.g. when the filter is stopped while EndOfStream is being called. --- dlls/amstream/filter.c | 88 +++++++++++++++++++++++++++++++----------- 1 file changed, 65 insertions(+), 23 deletions(-) diff --git a/dlls/amstream/filter.c b/dlls/amstream/filter.c index 3c0436a294f..6fac4b9b4b7 100644 --- a/dlls/amstream/filter.c +++ b/dlls/amstream/filter.c @@ -167,7 +167,9 @@ struct filter IMediaSeeking IMediaSeeking_iface; LONG refcount; CRITICAL_SECTION cs; + CRITICAL_SECTION stream_cs; + /* Protected either by cs or stream_cs, lock both when modifying. */ IReferenceClock *clock; WCHAR name[128]; IFilterGraph *graph; @@ -176,6 +178,8 @@ struct filter IAMMediaStream *seekable_stream; FILTER_STATE state; REFERENCE_TIME start_time; + + /* Protected by stream_cs. */ struct list free_events; struct list used_events; }; @@ -253,6 +257,7 @@ static ULONG WINAPI filter_Release(IMediaStreamFilter *iface) heap_free(filter->streams); if (filter->clock) IReferenceClock_Release(filter->clock); + DeleteCriticalSection(&filter->stream_cs); DeleteCriticalSection(&filter->cs); heap_free(filter); } @@ -266,28 +271,28 @@ static HRESULT WINAPI filter_GetClassID(IMediaStreamFilter *iface, CLSID *clsid) return S_OK; } -static void set_state(struct filter *filter, FILTER_STATE state) -{ - if (filter->state != state) - { - ULONG i; - - for (i = 0; i < filter->nb_streams; ++i) - IAMMediaStream_SetState(filter->streams[i], state); - filter->state = state; - } -} - static HRESULT WINAPI filter_Stop(IMediaStreamFilter *iface) { struct filter *filter = impl_from_IMediaStreamFilter(iface); struct event *event; + ULONG i; TRACE("iface %p.\n", iface); EnterCriticalSection(&filter->cs); - set_state(filter, State_Stopped); + if (filter->state == State_Stopped) + { + LeaveCriticalSection(&filter->cs); + return S_OK; + } + + for (i = 0; i < filter->nb_streams; ++i) + IAMMediaStream_SetState(filter->streams[i], State_Stopped); + + EnterCriticalSection(&filter->stream_cs); + + filter->state = State_Stopped; LIST_FOR_EACH_ENTRY(event, &filter->used_events, struct event, entry) { @@ -299,6 +304,7 @@ static HRESULT WINAPI filter_Stop(IMediaStreamFilter *iface) } } + LeaveCriticalSection(&filter->stream_cs); LeaveCriticalSection(&filter->cs); return S_OK; @@ -307,12 +313,24 @@ static HRESULT WINAPI filter_Stop(IMediaStreamFilter *iface) static HRESULT WINAPI filter_Pause(IMediaStreamFilter *iface) { struct filter *filter = impl_from_IMediaStreamFilter(iface); + ULONG i; TRACE("iface %p.\n", iface); EnterCriticalSection(&filter->cs); - set_state(filter, State_Paused); + if (filter->state == State_Paused) + { + LeaveCriticalSection(&filter->cs); + return S_OK; + } + + EnterCriticalSection(&filter->stream_cs); + filter->state = State_Paused; + LeaveCriticalSection(&filter->stream_cs); + + for (i = 0; i < filter->nb_streams; ++i) + IAMMediaStream_SetState(filter->streams[i], State_Paused); LeaveCriticalSection(&filter->cs); @@ -322,13 +340,27 @@ static HRESULT WINAPI filter_Pause(IMediaStreamFilter *iface) static HRESULT WINAPI filter_Run(IMediaStreamFilter *iface, REFERENCE_TIME start) { struct filter *filter = impl_from_IMediaStreamFilter(iface); + ULONG i; TRACE("iface %p, start %s.\n", iface, wine_dbgstr_longlong(start)); EnterCriticalSection(&filter->cs); + if (filter->state == State_Running) + { + LeaveCriticalSection(&filter->cs); + return S_OK; + } + + EnterCriticalSection(&filter->stream_cs); + + filter->state = State_Running; filter->start_time = start; - set_state(filter, State_Running); + + LeaveCriticalSection(&filter->stream_cs); + + for (i = 0; i < filter->nb_streams; ++i) + IAMMediaStream_SetState(filter->streams[i], State_Running); LeaveCriticalSection(&filter->cs); @@ -360,6 +392,7 @@ static HRESULT WINAPI filter_SetSyncSource(IMediaStreamFilter *iface, IReference TRACE("iface %p, clock %p.\n", iface, clock); EnterCriticalSection(&filter->cs); + EnterCriticalSection(&filter->stream_cs); if (clock) IReferenceClock_AddRef(clock); @@ -367,6 +400,7 @@ static HRESULT WINAPI filter_SetSyncSource(IMediaStreamFilter *iface, IReference IReferenceClock_Release(filter->clock); filter->clock = clock; + LeaveCriticalSection(&filter->stream_cs); LeaveCriticalSection(&filter->cs); return S_OK; @@ -491,6 +525,7 @@ static HRESULT WINAPI filter_JoinFilterGraph(IMediaStreamFilter *iface, TRACE("iface %p, graph %p, name.%s.\n", iface, graph, debugstr_w(name)); EnterCriticalSection(&filter->cs); + EnterCriticalSection(&filter->stream_cs); if (name) wcsncpy(filter->name, name, ARRAY_SIZE(filter->name)); @@ -498,6 +533,7 @@ static HRESULT WINAPI filter_JoinFilterGraph(IMediaStreamFilter *iface, filter->name[0] = 0; filter->graph = graph; + LeaveCriticalSection(&filter->stream_cs); LeaveCriticalSection(&filter->cs); return S_OK; @@ -624,6 +660,8 @@ static HRESULT WINAPI filter_SupportSeeking(IMediaStreamFilter *iface, BOOL rend return HRESULT_FROM_WIN32(ERROR_ALREADY_INITIALIZED); } + EnterCriticalSection(&filter->stream_cs); + for (i = 0; i < filter->nb_streams; ++i) { IMediaSeeking *seeking = get_seeking(filter->streams[i]); @@ -636,6 +674,7 @@ static HRESULT WINAPI filter_SupportSeeking(IMediaStreamFilter *iface, BOOL rend { filter->seekable_stream = filter->streams[i]; IMediaSeeking_Release(seeking); + LeaveCriticalSection(&filter->stream_cs); LeaveCriticalSection(&filter->cs); return S_OK; } @@ -643,6 +682,7 @@ static HRESULT WINAPI filter_SupportSeeking(IMediaStreamFilter *iface, BOOL rend IMediaSeeking_Release(seeking); } + LeaveCriticalSection(&filter->stream_cs); LeaveCriticalSection(&filter->cs); return E_NOINTERFACE; } @@ -704,11 +744,11 @@ static HRESULT WINAPI filter_WaitUntil(IMediaStreamFilter *iface, REFERENCE_TIME TRACE("filter %p, time %s.\n", iface, wine_dbgstr_longlong(time)); - EnterCriticalSection(&filter->cs); + EnterCriticalSection(&filter->stream_cs); if (!filter->clock) { - LeaveCriticalSection(&filter->cs); + LeaveCriticalSection(&filter->stream_cs); return E_FAIL; } @@ -729,23 +769,23 @@ static HRESULT WINAPI filter_WaitUntil(IMediaStreamFilter *iface, REFERENCE_TIME if (FAILED(hr)) { list_add_tail(&filter->free_events, entry); - LeaveCriticalSection(&filter->cs); + LeaveCriticalSection(&filter->stream_cs); return hr; } event->interrupted = FALSE; list_add_tail(&filter->used_events, entry); - LeaveCriticalSection(&filter->cs); + LeaveCriticalSection(&filter->stream_cs); WaitForSingleObject(event->event, INFINITE); - EnterCriticalSection(&filter->cs); + EnterCriticalSection(&filter->stream_cs); hr = event->interrupted ? S_FALSE : S_OK; list_remove(entry); list_add_tail(&filter->free_events, entry); - LeaveCriticalSection(&filter->cs); + LeaveCriticalSection(&filter->stream_cs); return hr; } @@ -757,7 +797,7 @@ static HRESULT WINAPI filter_Flush(IMediaStreamFilter *iface, BOOL cancel_eos) TRACE("filter %p, cancel_eos %d.\n", iface, cancel_eos); - EnterCriticalSection(&filter->cs); + EnterCriticalSection(&filter->stream_cs); LIST_FOR_EACH_ENTRY(event, &filter->used_events, struct event, entry) { @@ -769,7 +809,7 @@ static HRESULT WINAPI filter_Flush(IMediaStreamFilter *iface, BOOL cancel_eos) } } - LeaveCriticalSection(&filter->cs); + LeaveCriticalSection(&filter->stream_cs); return S_OK; } @@ -1064,6 +1104,8 @@ HRESULT filter_create(IUnknown *outer, void **out) list_init(&object->used_events); InitializeCriticalSection(&object->cs); object->cs.DebugInfo->Spare[0] = (DWORD_PTR)(__FILE__ ": MediaStreamFilter.cs"); + InitializeCriticalSection(&object->stream_cs); + object->stream_cs.DebugInfo->Spare[0] = (DWORD_PTR)(__FILE__ ": MediaStreamFilter.stream_cs"); TRACE("Created media stream filter %p.\n", object); *out = &object->IMediaStreamFilter_iface; -- 2.25.1
On 4/6/21 1:04 PM, Anton Baskanov wrote:
Signed-off-by: Anton Baskanov <baskanov(a)gmail.com> --- This is required to avoid deadlocks, e.g. when the filter is stopped while EndOfStream is being called.
So, if I understand correctly, the race is: Main thread Streaming thread ------------------------------------------------ IMediaControl::Stop() IPin::EndOfStream() lock graph->cs IMediaStreamFilter::EndOfStream() stop parser filter wait for streaming thread try to grab graph->cs Shouldn't patch 0005 be enough to avoid this? We don't hold filter->cs, or any amstream-specific locks, while waiting for streaming threads to stop.
On среда, 7 апреля 2021 г. 22:27:33 +07 you wrote:
On 4/6/21 1:04 PM, Anton Baskanov wrote:
Signed-off-by: Anton Baskanov <baskanov(a)gmail.com> --- This is required to avoid deadlocks, e.g. when the filter is stopped while EndOfStream is being called.
So, if I understand correctly, the race is:
Main thread Streaming thread ------------------------------------------------ IMediaControl::Stop() IPin::EndOfStream() lock graph->cs IMediaStreamFilter::EndOfStream() stop parser filter wait for streaming thread try to grab graph->cs
Shouldn't patch 0005 be enough to avoid this? We don't hold filter->cs, or any amstream-specific locks, while waiting for streaming threads to stop.
Actually, the deadlock is: Main thread Streaming thread ------------------------------------------------ IMediaStreamFilter::Stop() IPin::EndOfStream() lock filter->cs lock stream->cs IAMMediaStream::SetState() IMediaStreamFilter::EndOfStream() try to lock stream->cs try to lock filter->cs In theory, it should be possible to move SetState() calls out of the filter critical section, but there are other possible deadlocks, like: Main thread Streaming thread ------------------------------------------------ IMediaStreamFilter::GetStopPosition() IPin::EndOfStream() lock filter->cs lock stream->cs get_seeking() IPin_ConnectedTo() IMediaStreamFilter::EndOfStream() try to lock stream->cs try to lock filter->cs
On 4/8/21 1:14 AM, Anton Baskanov wrote:
On среда, 7 апреля 2021 г. 22:27:33 +07 you wrote:
On 4/6/21 1:04 PM, Anton Baskanov wrote:
Signed-off-by: Anton Baskanov <baskanov(a)gmail.com> --- This is required to avoid deadlocks, e.g. when the filter is stopped while EndOfStream is being called.
So, if I understand correctly, the race is:
Main thread Streaming thread ------------------------------------------------ IMediaControl::Stop() IPin::EndOfStream() lock graph->cs IMediaStreamFilter::EndOfStream() stop parser filter wait for streaming thread try to grab graph->cs
Shouldn't patch 0005 be enough to avoid this? We don't hold filter->cs, or any amstream-specific locks, while waiting for streaming threads to stop.
Actually, the deadlock is:
Main thread Streaming thread ------------------------------------------------ IMediaStreamFilter::Stop() IPin::EndOfStream() lock filter->cs lock stream->cs IAMMediaStream::SetState() IMediaStreamFilter::EndOfStream() try to lock stream->cs try to lock filter->cs
In theory, it should be possible to move SetState() calls out of the filter critical section, but there are other possible deadlocks, like:
Main thread Streaming thread ------------------------------------------------ IMediaStreamFilter::GetStopPosition() IPin::EndOfStream() lock filter->cs lock stream->cs get_seeking() IPin_ConnectedTo() IMediaStreamFilter::EndOfStream() try to lock stream->cs try to lock filter->cs
Ah. Yeah, lock ordering would be a problem. Fortunately it only seems to be a problem there. Could we instead call IMediaStreamFilter::EndOfStream() not inside stream->cs? That solution seems potentially simpler, judging by your not-yet-submitted commit 4ad440dde6. It should probably be accompanied by a comment; probably we should add a similar comment to IStreamSample::GetSampleTimes().
On четверг, 8 апреля 2021 г. 22:12:35 +07 you wrote:
On 4/8/21 1:14 AM, Anton Baskanov wrote:
On ÃÂÃÂõôð, 7 ðÿÃÂõûà2021 ó. 22:27:33 +07 you wrote:
On 4/6/21 1:04 PM, Anton Baskanov wrote:
Signed-off-by: Anton Baskanov <baskanov(a)gmail.com> --- This is required to avoid deadlocks, e.g. when the filter is stopped while EndOfStream is being called.
So, if I understand correctly, the race is:
Main thread Streaming thread ------------------------------------------------ IMediaControl::Stop() IPin::EndOfStream() lock graph->cs IMediaStreamFilter::EndOfStream() stop parser filter wait for streaming thread try to grab graph->cs
Shouldn't patch 0005 be enough to avoid this? We don't hold filter->cs, or any amstream-specific locks, while waiting for streaming threads to stop.> Actually, the deadlock is:
Main thread Streaming thread ------------------------------------------------ IMediaStreamFilter::Stop() IPin::EndOfStream() lock filter->cs lock stream->cs IAMMediaStream::SetState() IMediaStreamFilter::EndOfStream() try to lock stream->cs try to lock filter->cs
In theory, it should be possible to move SetState() calls out of the filter critical section, but there are other possible deadlocks, like:
Main thread Streaming thread ------------------------------------------------ IMediaStreamFilter::GetStopPosition() IPin::EndOfStream() lock filter->cs lock stream->cs get_seeking() IPin_ConnectedTo() IMediaStreamFilter::EndOfStream() try to lock stream->cs try to lock filter->cs
Ah. Yeah, lock ordering would be a problem. Fortunately it only seems to be a problem there.
Could we instead call IMediaStreamFilter::EndOfStream() not inside stream->cs? That solution seems potentially simpler, judging by your not-yet-submitted commit 4ad440dde6. It should probably be accompanied by a comment; probably we should add a similar comment to IStreamSample::GetSampleTimes().
Looks like this should work. There will be a race condition where IMediaStreamFilter::Flush is called before MediaStreamFilter::EndOfStream. AFAICS this should not cause any issues other than making eos_count negative briefly.
On 4/9/21 2:02 PM, Anton Baskanov wrote:
On четверг, 8 апреля 2021 г. 22:12:35 +07 you wrote:
On 4/8/21 1:14 AM, Anton Baskanov wrote:
On ÃÂÃÂõôð, 7 ðÿÃÂõûà2021 ó. 22:27:33 +07 you wrote:
On 4/6/21 1:04 PM, Anton Baskanov wrote:
Signed-off-by: Anton Baskanov <baskanov(a)gmail.com> --- This is required to avoid deadlocks, e.g. when the filter is stopped while EndOfStream is being called.
So, if I understand correctly, the race is:
Main thread Streaming thread ------------------------------------------------ IMediaControl::Stop() IPin::EndOfStream() lock graph->cs IMediaStreamFilter::EndOfStream() stop parser filter wait for streaming thread try to grab graph->cs
Shouldn't patch 0005 be enough to avoid this? We don't hold filter->cs, or any amstream-specific locks, while waiting for streaming threads to stop.> Actually, the deadlock is:
Main thread Streaming thread ------------------------------------------------ IMediaStreamFilter::Stop() IPin::EndOfStream() lock filter->cs lock stream->cs IAMMediaStream::SetState() IMediaStreamFilter::EndOfStream() try to lock stream->cs try to lock filter->cs
In theory, it should be possible to move SetState() calls out of the filter critical section, but there are other possible deadlocks, like:
Main thread Streaming thread ------------------------------------------------ IMediaStreamFilter::GetStopPosition() IPin::EndOfStream() lock filter->cs lock stream->cs get_seeking() IPin_ConnectedTo() IMediaStreamFilter::EndOfStream() try to lock stream->cs try to lock filter->cs
Ah. Yeah, lock ordering would be a problem. Fortunately it only seems to be a problem there.
Could we instead call IMediaStreamFilter::EndOfStream() not inside stream->cs? That solution seems potentially simpler, judging by your not-yet-submitted commit 4ad440dde6. It should probably be accompanied by a comment; probably we should add a similar comment to IStreamSample::GetSampleTimes().
Looks like this should work. There will be a race condition where IMediaStreamFilter::Flush is called before MediaStreamFilter::EndOfStream. AFAICS this should not cause any issues other than making eos_count negative briefly.
There is a race there, yeah, but I don't think it's meaningful. Nice job anticipating my "is it valid to decrement the counter past zero" question in 203580, by the way :D
Signed-off-by: Anton Baskanov <baskanov(a)gmail.com> --- dlls/amstream/filter.c | 39 +++++++++++++++++++++++++++++++-- dlls/amstream/tests/amstream.c | 40 +++++++++++++++++----------------- 2 files changed, 57 insertions(+), 22 deletions(-) diff --git a/dlls/amstream/filter.c b/dlls/amstream/filter.c index 6fac4b9b4b7..2575f82aeef 100644 --- a/dlls/amstream/filter.c +++ b/dlls/amstream/filter.c @@ -182,6 +182,7 @@ struct filter /* Protected by stream_cs. */ struct list free_events; struct list used_events; + ULONG eos_count; }; struct event @@ -271,6 +272,22 @@ static HRESULT WINAPI filter_GetClassID(IMediaStreamFilter *iface, CLSID *clsid) return S_OK; } +static void send_ec_complete(struct filter *filter) +{ + IMediaEventSink *event_sink; + + if (!filter->graph) + return; + + if (FAILED(IFilterGraph_QueryInterface(filter->graph, &IID_IMediaEventSink, (void **)&event_sink))) + return; + + IMediaEventSink_Notify(event_sink, EC_COMPLETE, S_OK, + (LONG_PTR)&filter->IMediaStreamFilter_iface); + + IMediaEventSink_Release(event_sink); +} + static HRESULT WINAPI filter_Stop(IMediaStreamFilter *iface) { struct filter *filter = impl_from_IMediaStreamFilter(iface); @@ -293,6 +310,7 @@ static HRESULT WINAPI filter_Stop(IMediaStreamFilter *iface) EnterCriticalSection(&filter->stream_cs); filter->state = State_Stopped; + filter->eos_count = 0; LIST_FOR_EACH_ENTRY(event, &filter->used_events, struct event, entry) { @@ -354,6 +372,9 @@ static HRESULT WINAPI filter_Run(IMediaStreamFilter *iface, REFERENCE_TIME start EnterCriticalSection(&filter->stream_cs); + if (filter->seekable_stream && filter->eos_count == filter->nb_streams) + send_ec_complete(filter); + filter->state = State_Running; filter->start_time = start; @@ -809,6 +830,9 @@ static HRESULT WINAPI filter_Flush(IMediaStreamFilter *iface, BOOL cancel_eos) } } + if (cancel_eos) + --filter->eos_count; + LeaveCriticalSection(&filter->stream_cs); return S_OK; @@ -816,9 +840,20 @@ static HRESULT WINAPI filter_Flush(IMediaStreamFilter *iface, BOOL cancel_eos) static HRESULT WINAPI filter_EndOfStream(IMediaStreamFilter *iface) { - FIXME("(%p)->(): Stub!\n", iface); + struct filter *filter = impl_from_IMediaStreamFilter(iface); - return E_NOTIMPL; + TRACE("filter %p.\n", filter); + + EnterCriticalSection(&filter->stream_cs); + + ++filter->eos_count; + if (filter->state == State_Running && filter->seekable_stream && + filter->eos_count == filter->nb_streams) + send_ec_complete(filter); + + LeaveCriticalSection(&filter->stream_cs); + + return S_OK; } static const IMediaStreamFilterVtbl filter_vtbl = diff --git a/dlls/amstream/tests/amstream.c b/dlls/amstream/tests/amstream.c index 39d65ebaaad..5bdbd8b0bd0 100644 --- a/dlls/amstream/tests/amstream.c +++ b/dlls/amstream/tests/amstream.c @@ -6953,9 +6953,9 @@ static void test_mediastreamfilter_end_of_stream(void) graph.got_notify = 0; hr = IMediaStreamFilter_EndOfStream(filter); - todo_wine ok(hr == S_OK, "Got hr %#x.\n", hr); + 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(hr == S_OK, "Got hr %#x.\n", hr); ok(graph.got_notify == 0, "Got %d calls to IMediaEventSink::Notify().\n", graph.got_notify); @@ -6972,9 +6972,9 @@ static void test_mediastreamfilter_end_of_stream(void) graph.got_notify = 0; hr = IMediaStreamFilter_EndOfStream(filter); - todo_wine ok(hr == S_OK, "Got hr %#x.\n", hr); + 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(hr == S_OK, "Got hr %#x.\n", hr); ok(graph.got_notify == 0, "Got %d calls to IMediaEventSink::Notify().\n", graph.got_notify); @@ -6993,14 +6993,14 @@ static void test_mediastreamfilter_end_of_stream(void) graph.got_notify = 0; hr = IMediaStreamFilter_EndOfStream(filter); - todo_wine ok(hr == S_OK, "Got hr %#x.\n", hr); + 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); + 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); + 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); @@ -7010,9 +7010,9 @@ static void test_mediastreamfilter_end_of_stream(void) graph.got_notify = 0; hr = IMediaStreamFilter_EndOfStream(filter); - todo_wine ok(hr == S_OK, "Got hr %#x.\n", hr); + 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(hr == S_OK, "Got hr %#x.\n", hr); ok(graph.got_notify == 0, "Got %d calls to IMediaEventSink::Notify().\n", graph.got_notify); @@ -7024,7 +7024,7 @@ static void test_mediastreamfilter_end_of_stream(void) 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); + 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); @@ -7036,7 +7036,7 @@ static void test_mediastreamfilter_end_of_stream(void) graph.got_notify = 0; hr = IMediaStreamFilter_EndOfStream(filter); - todo_wine ok(hr == S_OK, "Got hr %#x.\n", hr); + ok(hr == S_OK, "Got hr %#x.\n", hr); hr = IAMMultiMediaStream_SetState(mmstream, STREAMSTATE_STOP); ok(hr == S_OK, "Got hr %#x.\n", hr); @@ -7044,7 +7044,7 @@ static void test_mediastreamfilter_end_of_stream(void) 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(hr == S_OK, "Got hr %#x.\n", hr); ok(graph.got_notify == 0, "Got %d calls to IMediaEventSink::Notify().\n", graph.got_notify); @@ -7058,7 +7058,7 @@ static void test_mediastreamfilter_end_of_stream(void) graph.got_notify = 0; hr = IMediaStreamFilter_EndOfStream(filter); - todo_wine ok(hr == S_OK, "Got hr %#x.\n", hr); + ok(hr == S_OK, "Got hr %#x.\n", hr); hr = IMediaControl_Pause(media_control); ok(hr == S_OK, "Got hr %#x.\n", hr); @@ -7066,9 +7066,9 @@ static void test_mediastreamfilter_end_of_stream(void) 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(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); + 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); @@ -7080,12 +7080,12 @@ static void test_mediastreamfilter_end_of_stream(void) graph.got_notify = 0; hr = IMediaStreamFilter_EndOfStream(filter); - todo_wine ok(hr == S_OK, "Got hr %#x.\n", hr); + 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(hr == S_OK, "Got hr %#x.\n", hr); ok(graph.got_notify == 0, "Got %d calls to IMediaEventSink::Notify().\n", graph.got_notify); @@ -7099,14 +7099,14 @@ static void test_mediastreamfilter_end_of_stream(void) graph.got_notify = 0; hr = IMediaStreamFilter_EndOfStream(filter); - todo_wine ok(hr == S_OK, "Got hr %#x.\n", hr); + 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); + 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); + 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); -- 2.25.1
Signed-off-by: Anton Baskanov <baskanov(a)gmail.com> --- This is required to avoid deadlocks, e.g. when MediaStreamFilter sends EC_COMPLETE while the graph is being stopped. --- dlls/quartz/filtergraph.c | 26 +++++++++++++++++++------- 1 file changed, 19 insertions(+), 7 deletions(-) diff --git a/dlls/quartz/filtergraph.c b/dlls/quartz/filtergraph.c index a8c6cc3391c..1c8bdca99e7 100644 --- a/dlls/quartz/filtergraph.c +++ b/dlls/quartz/filtergraph.c @@ -104,6 +104,7 @@ struct filter_graph IReferenceClock *refClock; IBaseFilter *refClockProvider; + CRITICAL_SECTION event_cs; struct list media_events; HANDLE media_event_handle; HWND media_event_window; @@ -116,6 +117,8 @@ struct filter_graph int HandleEcComplete; int HandleEcRepaint; int HandleEcClockChanged; + unsigned int got_ec_complete : 1; + unsigned int media_events_disabled : 1; CRITICAL_SECTION cs; ITF_CACHE_ENTRY ItfCacheEntries[MAX_ITF_CACHE_ENTRIES]; @@ -135,8 +138,6 @@ struct filter_graph LONGLONG current_pos; unsigned int needs_async_run : 1; - unsigned int got_ec_complete : 1; - unsigned int media_events_disabled : 1; }; struct enum_filters @@ -467,6 +468,7 @@ static ULONG WINAPI FilterGraphInner_Release(IUnknown *iface) CloseHandle(This->message_thread); CloseHandle(This->message_thread_ret); } + DeleteCriticalSection(&This->event_cs); DeleteCriticalSection(&This->cs); free(This); @@ -1695,8 +1697,10 @@ static HRESULT graph_start(struct filter_graph *graph, REFERENCE_TIME stream_sta struct filter *filter; HRESULT hr = S_OK; + EnterCriticalSection(&graph->event_cs); graph->EcCompleteCount = 0; update_render_count(graph); + LeaveCriticalSection(&graph->event_cs); LIST_FOR_EACH_ENTRY_SAFE(event, next, &graph->media_events, struct media_event, entry) { @@ -1813,7 +1817,10 @@ static HRESULT WINAPI MediaControl_Run(IMediaControl *iface) } sort_filters(graph); + + EnterCriticalSection(&graph->event_cs); update_render_count(graph); + LeaveCriticalSection(&graph->event_cs); if (graph->state == State_Stopped) { @@ -4752,12 +4759,12 @@ static HRESULT WINAPI MediaEvent_GetEvent(IMediaEventEx *iface, LONG *code, if (WaitForSingleObject(graph->media_event_handle, timeout)) return E_ABORT; - EnterCriticalSection(&graph->cs); + EnterCriticalSection(&graph->event_cs); if (!(entry = list_head(&graph->media_events))) { ResetEvent(graph->media_event_handle); - LeaveCriticalSection(&graph->cs); + LeaveCriticalSection(&graph->event_cs); return E_ABORT; } event = LIST_ENTRY(entry, struct media_event, entry); @@ -4767,7 +4774,7 @@ static HRESULT WINAPI MediaEvent_GetEvent(IMediaEventEx *iface, LONG *code, *param2 = event->param2; free(event); - LeaveCriticalSection(&graph->cs); + LeaveCriticalSection(&graph->event_cs); return S_OK; } @@ -5015,7 +5022,10 @@ static HRESULT WINAPI MediaFilter_Pause(IMediaFilter *iface) } sort_filters(graph); + + EnterCriticalSection(&graph->event_cs); update_render_count(graph); + LeaveCriticalSection(&graph->event_cs); if (graph->defaultclock && !graph->refClock) IFilterGraph2_SetDefaultSyncSource(&graph->IFilterGraph2_iface); @@ -5261,7 +5271,7 @@ static HRESULT WINAPI MediaEventSink_Notify(IMediaEventSink *iface, LONG code, TRACE("graph %p, code %#x, param1 %#Ix, param2 %#Ix.\n", graph, code, param1, param2); - EnterCriticalSection(&graph->cs); + EnterCriticalSection(&graph->event_cs); if (code == EC_COMPLETE && graph->HandleEcComplete) { @@ -5285,7 +5295,7 @@ static HRESULT WINAPI MediaEventSink_Notify(IMediaEventSink *iface, LONG code, queue_media_event(graph, code, param1, param2); } - LeaveCriticalSection(&graph->cs); + LeaveCriticalSection(&graph->event_cs); return S_OK; } @@ -5591,6 +5601,8 @@ static HRESULT filter_graph_common_create(IUnknown *outer, IUnknown **out, BOOL InitializeCriticalSection(&object->cs); object->cs.DebugInfo->Spare[0] = (DWORD_PTR)(__FILE__ ": filter_graph.cs"); + InitializeCriticalSection(&object->event_cs); + object->event_cs.DebugInfo->Spare[0] = (DWORD_PTR)(__FILE__ ": filter_graph.event_cs"); object->defaultclock = TRUE; -- 2.25.1
On 4/6/21 1:04 PM, Anton Baskanov wrote:
Signed-off-by: Anton Baskanov <baskanov(a)gmail.com> --- This is required to avoid deadlocks, e.g. when MediaStreamFilter sends EC_COMPLETE while the graph is being stopped.
Can you please add a comment to this effect to the code?
--- dlls/quartz/filtergraph.c | 26 +++++++++++++++++++------- 1 file changed, 19 insertions(+), 7 deletions(-)
diff --git a/dlls/quartz/filtergraph.c b/dlls/quartz/filtergraph.c index a8c6cc3391c..1c8bdca99e7 100644 --- a/dlls/quartz/filtergraph.c +++ b/dlls/quartz/filtergraph.c @@ -104,6 +104,7 @@ struct filter_graph IReferenceClock *refClock; IBaseFilter *refClockProvider;
+ CRITICAL_SECTION event_cs; struct list media_events; HANDLE media_event_handle; HWND media_event_window; @@ -116,6 +117,8 @@ struct filter_graph int HandleEcComplete; int HandleEcRepaint; int HandleEcClockChanged; + unsigned int got_ec_complete : 1; + unsigned int media_events_disabled : 1;
CRITICAL_SECTION cs; ITF_CACHE_ENTRY ItfCacheEntries[MAX_ITF_CACHE_ENTRIES]; @@ -135,8 +138,6 @@ struct filter_graph LONGLONG current_pos;
unsigned int needs_async_run : 1; - unsigned int got_ec_complete : 1; - unsigned int media_events_disabled : 1; };
struct enum_filters @@ -467,6 +468,7 @@ static ULONG WINAPI FilterGraphInner_Release(IUnknown *iface) CloseHandle(This->message_thread); CloseHandle(This->message_thread_ret); } + DeleteCriticalSection(&This->event_cs); DeleteCriticalSection(&This->cs); free(This);
@@ -1695,8 +1697,10 @@ static HRESULT graph_start(struct filter_graph *graph, REFERENCE_TIME stream_sta struct filter *filter; HRESULT hr = S_OK;
+ EnterCriticalSection(&graph->event_cs); graph->EcCompleteCount = 0; update_render_count(graph); + LeaveCriticalSection(&graph->event_cs);
LIST_FOR_EACH_ENTRY_SAFE(event, next, &graph->media_events, struct media_event, entry) { @@ -1813,7 +1817,10 @@ static HRESULT WINAPI MediaControl_Run(IMediaControl *iface) }
sort_filters(graph); + + EnterCriticalSection(&graph->event_cs); update_render_count(graph); + LeaveCriticalSection(&graph->event_cs);
if (graph->state == State_Stopped) { @@ -4752,12 +4759,12 @@ static HRESULT WINAPI MediaEvent_GetEvent(IMediaEventEx *iface, LONG *code, if (WaitForSingleObject(graph->media_event_handle, timeout)) return E_ABORT;
- EnterCriticalSection(&graph->cs); + EnterCriticalSection(&graph->event_cs);
if (!(entry = list_head(&graph->media_events))) { ResetEvent(graph->media_event_handle); - LeaveCriticalSection(&graph->cs); + LeaveCriticalSection(&graph->event_cs); return E_ABORT; } event = LIST_ENTRY(entry, struct media_event, entry); @@ -4767,7 +4774,7 @@ static HRESULT WINAPI MediaEvent_GetEvent(IMediaEventEx *iface, LONG *code, *param2 = event->param2; free(event);
- LeaveCriticalSection(&graph->cs); + LeaveCriticalSection(&graph->event_cs); return S_OK; }
@@ -5015,7 +5022,10 @@ static HRESULT WINAPI MediaFilter_Pause(IMediaFilter *iface) }
sort_filters(graph); + + EnterCriticalSection(&graph->event_cs); update_render_count(graph); + LeaveCriticalSection(&graph->event_cs);
if (graph->defaultclock && !graph->refClock) IFilterGraph2_SetDefaultSyncSource(&graph->IFilterGraph2_iface); @@ -5261,7 +5271,7 @@ static HRESULT WINAPI MediaEventSink_Notify(IMediaEventSink *iface, LONG code,
TRACE("graph %p, code %#x, param1 %#Ix, param2 %#Ix.\n", graph, code, param1, param2);
- EnterCriticalSection(&graph->cs); + EnterCriticalSection(&graph->event_cs);
if (code == EC_COMPLETE && graph->HandleEcComplete) { @@ -5285,7 +5295,7 @@ static HRESULT WINAPI MediaEventSink_Notify(IMediaEventSink *iface, LONG code, queue_media_event(graph, code, param1, param2); }
- LeaveCriticalSection(&graph->cs); + LeaveCriticalSection(&graph->event_cs); return S_OK; }
@@ -5591,6 +5601,8 @@ static HRESULT filter_graph_common_create(IUnknown *outer, IUnknown **out, BOOL
InitializeCriticalSection(&object->cs); object->cs.DebugInfo->Spare[0] = (DWORD_PTR)(__FILE__ ": filter_graph.cs"); + InitializeCriticalSection(&object->event_cs); + object->event_cs.DebugInfo->Spare[0] = (DWORD_PTR)(__FILE__ ": filter_graph.event_cs");
object->defaultclock = TRUE;
participants (2)
-
Anton Baskanov -
Zebediah Figura (she/her)