[PATCH 8/8] quartz: Run the graph asynchronously if necessary.
Zebediah Figura
z.figura12 at gmail.com
Tue Jul 14 19:56:26 CDT 2020
Signed-off-by: Zebediah Figura <z.figura12 at gmail.com>
---
dlls/quartz/filtergraph.c | 236 +++++++++++++++++++++++++-----
dlls/quartz/tests/filtergraph.c | 12 +-
dlls/quartz/tests/videorenderer.c | 6 +-
dlls/quartz/tests/vmr9.c | 6 +-
4 files changed, 208 insertions(+), 52 deletions(-)
diff --git a/dlls/quartz/filtergraph.c b/dlls/quartz/filtergraph.c
index 273cd021109..22d7f7b4450 100644
--- a/dlls/quartz/filtergraph.c
+++ b/dlls/quartz/filtergraph.c
@@ -191,6 +191,9 @@ struct filter_graph
struct list filters;
unsigned int name_index;
+ OAFilterState state;
+ TP_WORK *async_run_work;
+
IReferenceClock *refClock;
IBaseFilter *refClockProvider;
EventsQueue evqueue;
@@ -202,7 +205,6 @@ struct filter_graph
int HandleEcComplete;
int HandleEcRepaint;
int HandleEcClockChanged;
- OAFilterState state;
CRITICAL_SECTION cs;
ITF_CACHE_ENTRY ItfCacheEntries[MAX_ITF_CACHE_ENTRIES];
int nItfCacheEntries;
@@ -219,6 +221,8 @@ struct filter_graph
REFERENCE_TIME stream_start, stream_elapsed;
LONGLONG current_pos;
+
+ unsigned int needs_async_run : 1;
};
struct enum_filters
@@ -1719,13 +1723,179 @@ static HRESULT WINAPI MediaControl_Invoke(IMediaControl *iface, DISPID dispIdMem
return S_OK;
}
+static void update_render_count(struct filter_graph *graph)
+{
+ /* Some filters (e.g. MediaStreamFilter) can become renderers when they are
+ * already in the graph. */
+ struct filter *filter;
+ graph->nRenderers = 0;
+ LIST_FOR_EACH_ENTRY(filter, &graph->filters, struct filter, entry)
+ {
+ if (is_renderer(filter))
+ ++graph->nRenderers;
+ }
+}
+
+/* Perform the paused -> running transition. The caller must hold graph->cs. */
+static HRESULT graph_start(struct filter_graph *graph, REFERENCE_TIME stream_start)
+{
+ struct filter *filter;
+ HRESULT hr = S_OK;
+
+ graph->EcCompleteCount = 0;
+ update_render_count(graph);
+
+ if (graph->defaultclock && !graph->refClock)
+ IFilterGraph2_SetDefaultSyncSource(&graph->IFilterGraph2_iface);
+
+ if (!stream_start && graph->refClock)
+ {
+ IReferenceClock_GetTime(graph->refClock, &graph->stream_start);
+ stream_start = graph->stream_start - graph->stream_elapsed;
+ /* Delay presentation time by 200 ms, to give filters time to
+ * initialize. */
+ stream_start += 200 * 10000;
+ }
+
+ LIST_FOR_EACH_ENTRY(filter, &graph->filters, struct filter, entry)
+ {
+ HRESULT filter_hr = IBaseFilter_Run(filter->filter, stream_start);
+ if (hr == S_OK)
+ hr = filter_hr;
+ TRACE("Filter %p returned %#x.\n", filter->filter, filter_hr);
+ }
+
+ if (FAILED(hr))
+ WARN("Failed to start stream, hr %#x.\n", hr);
+
+ return hr;
+}
+
+static void CALLBACK async_run_cb(TP_CALLBACK_INSTANCE *instance, void *context, TP_WORK *work)
+{
+ struct filter_graph *graph = context;
+ struct filter *filter;
+ FILTER_STATE state;
+ HRESULT hr;
+
+ TRACE("Performing asynchronous state change.\n");
+
+ /* We can't just call GetState(), since that will return State_Running and
+ * VFW_S_STATE_INTERMEDIATE regardless of whether we're done pausing yet.
+ * Instead replicate it here. */
+
+ for (;;)
+ {
+ IBaseFilter *async_filter = NULL;
+
+ hr = S_OK;
+
+ EnterCriticalSection(&graph->cs);
+
+ if (!graph->needs_async_run)
+ break;
+
+ LIST_FOR_EACH_ENTRY(filter, &graph->filters, struct filter, entry)
+ {
+ hr = IBaseFilter_GetState(filter->filter, 0, &state);
+
+ if (hr == VFW_S_STATE_INTERMEDIATE)
+ async_filter = filter->filter;
+
+ if (SUCCEEDED(hr) && state != State_Paused)
+ ERR("Filter %p reported incorrect state %u.\n", filter->filter, state);
+
+ if (hr != S_OK)
+ break;
+ }
+
+ if (hr != VFW_S_STATE_INTERMEDIATE)
+ break;
+
+ LeaveCriticalSection(&graph->cs);
+
+ IBaseFilter_GetState(async_filter, 10, &state);
+ }
+
+ if (hr == S_OK && graph->needs_async_run)
+ {
+ sort_filters(graph);
+ graph_start(graph, 0);
+ graph->needs_async_run = 0;
+ }
+
+ LeaveCriticalSection(&graph->cs);
+ IUnknown_Release(graph->outer_unk);
+}
+
static HRESULT WINAPI MediaControl_Run(IMediaControl *iface)
{
struct filter_graph *graph = impl_from_IMediaControl(iface);
+ BOOL need_async_run = TRUE;
+ struct filter *filter;
+ FILTER_STATE state;
+ HRESULT hr = S_OK;
TRACE("graph %p.\n", graph);
- return IMediaFilter_Run(&graph->IMediaFilter_iface, 0);
+ EnterCriticalSection(&graph->cs);
+
+ if (graph->state == State_Running)
+ {
+ LeaveCriticalSection(&graph->cs);
+ return S_OK;
+ }
+
+ sort_filters(graph);
+ update_render_count(graph);
+
+ if (graph->state == State_Stopped)
+ {
+ if (graph->defaultclock && !graph->refClock)
+ IFilterGraph2_SetDefaultSyncSource(&graph->IFilterGraph2_iface);
+
+ LIST_FOR_EACH_ENTRY(filter, &graph->filters, struct filter, entry)
+ {
+ HRESULT filter_hr = IBaseFilter_Pause(filter->filter);
+ if (hr == S_OK)
+ hr = filter_hr;
+ TRACE("Filter %p returned %#x.\n", filter->filter, filter_hr);
+
+ /* If a filter returns VFW_S_CANT_CUE, we shouldn't wait for a
+ * paused state. */
+ filter_hr = IBaseFilter_GetState(filter->filter, 0, &state);
+ if (filter_hr != S_OK && filter_hr != VFW_S_STATE_INTERMEDIATE)
+ need_async_run = FALSE;
+ }
+
+ if (FAILED(hr))
+ {
+ LeaveCriticalSection(&graph->cs);
+ WARN("Failed to pause, hr %#x.\n", hr);
+ return hr;
+ }
+ }
+
+ graph->state = State_Running;
+
+ if (SUCCEEDED(hr))
+ {
+ if (hr != S_OK && need_async_run)
+ {
+ if (!graph->async_run_work)
+ graph->async_run_work = CreateThreadpoolWork(async_run_cb, graph, NULL);
+ graph->needs_async_run = 1;
+ IUnknown_AddRef(graph->outer_unk);
+ SubmitThreadpoolWork(graph->async_run_work);
+ }
+ else
+ {
+ graph_start(graph, 0);
+ }
+ }
+
+ LeaveCriticalSection(&graph->cs);
+ return hr;
}
static HRESULT WINAPI MediaControl_Pause(IMediaControl *iface)
@@ -4778,6 +4948,7 @@ static HRESULT WINAPI MediaFilter_Stop(IMediaFilter *iface)
struct filter_graph *graph = impl_from_IMediaFilter(iface);
HRESULT hr = S_OK, filter_hr;
struct filter *filter;
+ TP_WORK *work;
TRACE("graph %p.\n", graph);
@@ -4809,26 +4980,20 @@ static HRESULT WINAPI MediaFilter_Stop(IMediaFilter *iface)
}
graph->state = State_Stopped;
+ graph->needs_async_run = 0;
+ work = graph->async_run_work;
/* Update the current position, probably to synchronize multiple streams. */
IMediaSeeking_SetPositions(&graph->IMediaSeeking_iface, &graph->current_pos,
AM_SEEKING_AbsolutePositioning, NULL, AM_SEEKING_NoPositioning);
LeaveCriticalSection(&graph->cs);
- return hr;
-}
-static void update_render_count(struct filter_graph *graph)
-{
- /* Some filters (e.g. MediaStreamFilter) can become renderers when they are
- * already in the graph. */
- struct filter *filter;
- graph->nRenderers = 0;
- LIST_FOR_EACH_ENTRY(filter, &graph->filters, struct filter, entry)
- {
- if (is_renderer(filter))
- ++graph->nRenderers;
- }
+ /* Don't cancel the callback; it's holding a reference to the graph. */
+ if (work)
+ WaitForThreadpoolWorkCallbacks(work, FALSE);
+
+ return hr;
}
static HRESULT WINAPI MediaFilter_Pause(IMediaFilter *iface)
@@ -4836,6 +5001,7 @@ static HRESULT WINAPI MediaFilter_Pause(IMediaFilter *iface)
struct filter_graph *graph = impl_from_IMediaFilter(iface);
HRESULT hr = S_OK, filter_hr;
struct filter *filter;
+ TP_WORK *work;
TRACE("graph %p.\n", graph);
@@ -4869,17 +5035,22 @@ static HRESULT WINAPI MediaFilter_Pause(IMediaFilter *iface)
}
graph->state = State_Paused;
+ graph->needs_async_run = 0;
+ work = graph->async_run_work;
LeaveCriticalSection(&graph->cs);
+
+ /* Don't cancel the callback; it's holding a reference to the graph. */
+ if (work)
+ WaitForThreadpoolWorkCallbacks(work, FALSE);
+
return hr;
}
static HRESULT WINAPI MediaFilter_Run(IMediaFilter *iface, REFERENCE_TIME start)
{
struct filter_graph *graph = impl_from_IMediaFilter(iface);
- REFERENCE_TIME stream_start = start;
- HRESULT hr = S_OK, filter_hr;
- struct filter *filter;
+ HRESULT hr;
TRACE("graph %p, start %s.\n", graph, debugstr_time(start));
@@ -4890,31 +5061,13 @@ static HRESULT WINAPI MediaFilter_Run(IMediaFilter *iface, REFERENCE_TIME start)
LeaveCriticalSection(&graph->cs);
return S_OK;
}
- graph->EcCompleteCount = 0;
sort_filters(graph);
- update_render_count(graph);
-
- if (graph->defaultclock && !graph->refClock)
- IFilterGraph2_SetDefaultSyncSource(&graph->IFilterGraph2_iface);
-
- if (!start && graph->refClock)
- {
- IReferenceClock_GetTime(graph->refClock, &graph->stream_start);
- stream_start = graph->stream_start - graph->stream_elapsed;
- /* Delay presentation time by 200 ms, to give filters time to
- * initialize. */
- stream_start += 200 * 10000;
- }
- LIST_FOR_EACH_ENTRY(filter, &graph->filters, struct filter, entry)
- {
- filter_hr = IBaseFilter_Run(filter->filter, stream_start);
- if (hr == S_OK)
- hr = filter_hr;
- }
+ hr = graph_start(graph, start);
graph->state = State_Running;
+ graph->needs_async_run = 0;
LeaveCriticalSection(&graph->cs);
return hr;
@@ -4924,6 +5077,7 @@ static HRESULT WINAPI MediaFilter_GetState(IMediaFilter *iface, DWORD timeout, F
{
struct filter_graph *graph = impl_from_IMediaFilter(iface);
DWORD end = GetTickCount() + timeout;
+ FILTER_STATE expect_state;
HRESULT hr;
TRACE("graph %p, timeout %u, state %p.\n", graph, timeout, state);
@@ -4939,6 +5093,7 @@ static HRESULT WINAPI MediaFilter_GetState(IMediaFilter *iface, DWORD timeout, F
EnterCriticalSection(&graph->cs);
*state = graph->state;
+ expect_state = graph->needs_async_run ? State_Paused : graph->state;
for (;;)
{
@@ -4970,8 +5125,9 @@ static HRESULT WINAPI MediaFilter_GetState(IMediaFilter *iface, DWORD timeout, F
else if (filter_state != graph->state && filter_state != State_Paused)
hr = E_FAIL;
- if (filter_state != graph->state)
- ERR("Filter %p reported incorrect state %u.\n", filter->filter, filter_state);
+ if (filter_state != expect_state)
+ ERR("Filter %p reported incorrect state %u (expected %u).\n",
+ filter->filter, filter_state, expect_state);
}
LeaveCriticalSection(&graph->cs);
diff --git a/dlls/quartz/tests/filtergraph.c b/dlls/quartz/tests/filtergraph.c
index 99120133bb6..0c627aaeafb 100644
--- a/dlls/quartz/tests/filtergraph.c
+++ b/dlls/quartz/tests/filtergraph.c
@@ -3497,8 +3497,8 @@ todo_wine
hr = IMediaControl_GetState(control, 0, &state);
ok(hr == VFW_S_STATE_INTERMEDIATE, "Got hr %#x.\n", hr);
ok(state == State_Running, "Got state %u.\n", state);
- todo_wine ok(sink.state == State_Paused, "Got state %u.\n", sink.state);
- todo_wine ok(source.state == State_Paused, "Got state %u.\n", source.state);
+ ok(sink.state == State_Paused, "Got state %u.\n", sink.state);
+ ok(source.state == State_Paused, "Got state %u.\n", source.state);
hr = IMediaControl_Run(control);
todo_wine ok(hr == S_FALSE, "Got hr %#x.\n", hr);
@@ -3506,16 +3506,16 @@ todo_wine
hr = IMediaControl_GetState(control, 0, &state);
ok(hr == VFW_S_STATE_INTERMEDIATE, "Got hr %#x.\n", hr);
ok(state == State_Running, "Got state %u.\n", state);
- todo_wine ok(sink.state == State_Paused, "Got state %u.\n", sink.state);
- todo_wine ok(source.state == State_Paused, "Got state %u.\n", source.state);
+ ok(sink.state == State_Paused, "Got state %u.\n", sink.state);
+ ok(source.state == State_Paused, "Got state %u.\n", source.state);
sink.state_hr = sink.GetState_hr = S_OK;
while ((hr = IMediaControl_GetState(control, INFINITE, &state)) == VFW_S_STATE_INTERMEDIATE)
{
ok(state == State_Running, "Got state %u.\n", state);
- todo_wine ok(sink.state == State_Paused, "Got state %u.\n", sink.state);
- todo_wine ok(source.state == State_Paused, "Got state %u.\n", source.state);
+ ok(sink.state == State_Paused, "Got state %u.\n", sink.state);
+ ok(source.state == State_Paused, "Got state %u.\n", source.state);
Sleep(10);
}
ok(hr == S_OK, "Got hr %#x.\n", hr);
diff --git a/dlls/quartz/tests/videorenderer.c b/dlls/quartz/tests/videorenderer.c
index b157700a92b..4ae320e0225 100644
--- a/dlls/quartz/tests/videorenderer.c
+++ b/dlls/quartz/tests/videorenderer.c
@@ -1172,7 +1172,7 @@ static void test_eos(IPin *pin, IMemInputPin *input, IFilterGraph2 *graph)
* done rendering. */
hr = IMediaControl_Run(control);
- todo_wine ok(hr == S_FALSE, "Got hr %#x.\n", hr);
+ ok(hr == S_FALSE, "Got hr %#x.\n", hr);
hr = join_thread(send_frame(input));
ok(hr == S_OK, "Got hr %#x.\n", hr);
hr = IMediaControl_GetState(control, 1000, &state);
@@ -1195,7 +1195,7 @@ static void test_eos(IPin *pin, IMemInputPin *input, IFilterGraph2 *graph)
/* Test sending EOS while flushing. */
hr = IMediaControl_Run(control);
- todo_wine ok(hr == S_FALSE, "Got hr %#x.\n", hr);
+ ok(hr == S_FALSE, "Got hr %#x.\n", hr);
hr = join_thread(send_frame(input));
ok(hr == S_OK, "Got hr %#x.\n", hr);
@@ -1214,7 +1214,7 @@ static void test_eos(IPin *pin, IMemInputPin *input, IFilterGraph2 *graph)
/* Test sending EOS and then flushing or stopping. */
hr = IMediaControl_Run(control);
- todo_wine ok(hr == S_FALSE, "Got hr %#x.\n", hr);
+ ok(hr == S_FALSE, "Got hr %#x.\n", hr);
hr = join_thread(send_frame(input));
ok(hr == S_OK, "Got hr %#x.\n", hr);
hr = IMediaControl_GetState(control, 1000, &state);
diff --git a/dlls/quartz/tests/vmr9.c b/dlls/quartz/tests/vmr9.c
index e953c05dd1e..cd4df58f791 100644
--- a/dlls/quartz/tests/vmr9.c
+++ b/dlls/quartz/tests/vmr9.c
@@ -1308,7 +1308,7 @@ static void test_eos(IPin *pin, IMemInputPin *input, IMediaControl *control)
commit_allocator(input);
hr = IMediaControl_Run(control);
- todo_wine ok(hr == S_FALSE, "Got hr %#x.\n", hr);
+ ok(hr == S_FALSE, "Got hr %#x.\n", hr);
hr = join_thread(send_frame(input));
ok(hr == S_OK, "Got hr %#x.\n", hr);
hr = IMediaControl_GetState(control, 1000, &state);
@@ -1332,7 +1332,7 @@ static void test_eos(IPin *pin, IMemInputPin *input, IMediaControl *control)
commit_allocator(input);
hr = IMediaControl_Run(control);
- todo_wine ok(hr == S_FALSE, "Got hr %#x.\n", hr);
+ ok(hr == S_FALSE, "Got hr %#x.\n", hr);
hr = join_thread(send_frame(input));
ok(hr == S_OK, "Got hr %#x.\n", hr);
@@ -1352,7 +1352,7 @@ static void test_eos(IPin *pin, IMemInputPin *input, IMediaControl *control)
commit_allocator(input);
hr = IMediaControl_Run(control);
- todo_wine ok(hr == S_FALSE, "Got hr %#x.\n", hr);
+ ok(hr == S_FALSE, "Got hr %#x.\n", hr);
hr = join_thread(send_frame(input));
ok(hr == S_OK, "Got hr %#x.\n", hr);
hr = IMediaControl_GetState(control, 1000, &state);
--
2.27.0
More information about the wine-devel
mailing list