Signed-off-by: Anton Baskanov baskanov@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;
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(); }
Signed-off-by: Zebediah Figura z.figura12@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@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;
On 4/6/21 1:04 PM, Anton Baskanov wrote:
Signed-off-by: Anton Baskanov baskanov@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@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@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@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@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@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);
Signed-off-by: Anton Baskanov baskanov@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;
On 4/6/21 1:04 PM, Anton Baskanov wrote:
Signed-off-by: Anton Baskanov baskanov@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->cs); free(This);DeleteCriticalSection(&This->event_cs);
@@ -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;