[PATCH 3/4] quartz/filtergraph: Always sort filter list before use.

Anton Baskanov baskanov at gmail.com
Mon Jun 8 13:00:54 CDT 2020


Some applications (e.g. Earth 2150) call IPin::Connect directly instead of IFilterGraph::ConnectDirect.

Signed-off-by: Anton Baskanov <baskanov at gmail.com>
---
 dlls/quartz/filtergraph.c       | 68 +++++++++++++++++----------------
 dlls/quartz/tests/filtergraph.c | 11 ++++--
 2 files changed, 44 insertions(+), 35 deletions(-)

diff --git a/dlls/quartz/filtergraph.c b/dlls/quartz/filtergraph.c
index 76d64e4ca4..bfb6ea78d3 100644
--- a/dlls/quartz/filtergraph.c
+++ b/dlls/quartz/filtergraph.c
@@ -187,17 +187,7 @@ typedef struct _IFilterGraphImpl {
     LONG ref;
     IUnknown *punkFilterMapper2;
 
-    /* We keep two lists of filters, one unsorted and one topologically sorted.
-     * The former is necessary for functions like IGraphBuilder::Connect() and
-     * IGraphBuilder::Render() that iterate through the filter list but may
-     * add to it while doing so; the latter is for functions like
-     * IMediaControl::Run() that should propagate messages to all filters
-     * (including unconnected ones) but must do so in topological order from
-     * sinks to sources. We can easily guarantee that the loop in Connect() will
-     * touch each filter exactly once so long as we aren't reordering it, but
-     * using the sorted filters list there would be hard. This seems to be the
-     * easiest and clearest solution. */
-    struct list filters, sorted_filters;
+    struct list filters;
     unsigned int name_index;
 
     IReferenceClock *refClock;
@@ -680,7 +670,6 @@ static HRESULT WINAPI FilterGraph2_AddFilter(IFilterGraph2 *iface,
     IBaseFilter_AddRef(entry->filter = filter);
 
     list_add_head(&graph->filters, &entry->entry);
-    list_add_head(&graph->sorted_filters, &entry->sorted_entry);
     entry->sorting = FALSE;
     entry->seeking = NULL;
     ++graph->version;
@@ -762,7 +751,6 @@ static HRESULT WINAPI FilterGraph2_RemoveFilter(IFilterGraph2 *iface, IBaseFilte
                 if (entry->seeking)
                     IMediaSeeking_Release(entry->seeking);
                 list_remove(&entry->entry);
-                list_remove(&entry->sorted_entry);
                 CoTaskMemFree(entry->name);
                 heap_free(entry);
                 This->version++;
@@ -887,11 +875,11 @@ out:
 #endif
 }
 
-static struct filter *find_sorted_filter(IFilterGraphImpl *graph, IBaseFilter *iface)
+static struct filter *find_sorted_filter(struct list *sorted_filters, IBaseFilter *iface)
 {
     struct filter *filter;
 
-    LIST_FOR_EACH_ENTRY(filter, &graph->sorted_filters, struct filter, sorted_entry)
+    LIST_FOR_EACH_ENTRY(filter, sorted_filters, struct filter, sorted_entry)
     {
         if (filter->filter == iface)
             return filter;
@@ -900,7 +888,7 @@ static struct filter *find_sorted_filter(IFilterGraphImpl *graph, IBaseFilter *i
     return NULL;
 }
 
-static void sort_filter_recurse(IFilterGraphImpl *graph, struct filter *filter, struct list *sorted)
+static void sort_filter_recurse(struct list *sorted_filters, struct filter *filter, struct list *sorted)
 {
     struct filter *peer_filter;
     IEnumPins *enumpins;
@@ -924,8 +912,8 @@ static void sort_filter_recurse(IFilterGraphImpl *graph, struct filter *filter,
         {
             IPin_QueryPinInfo(peer, &info);
             /* Note that the filter may have already been sorted. */
-            if ((peer_filter = find_sorted_filter(graph, info.pFilter)))
-                sort_filter_recurse(graph, peer_filter, sorted);
+            if ((peer_filter = find_sorted_filter(sorted_filters, info.pFilter)))
+                sort_filter_recurse(sorted_filters, peer_filter, sorted);
             IBaseFilter_Release(info.pFilter);
             IPin_Release(peer);
         }
@@ -939,17 +927,25 @@ static void sort_filter_recurse(IFilterGraphImpl *graph, struct filter *filter,
     list_add_head(sorted, &filter->sorted_entry);
 }
 
-static void sort_filters(IFilterGraphImpl *graph)
+static void sort_filters(IFilterGraphImpl *graph, struct list *sorted_filters)
 {
     struct list sorted = LIST_INIT(sorted), *cursor;
+    struct filter *filter;
+
+    list_init(sorted_filters);
+
+    LIST_FOR_EACH_ENTRY(filter, &graph->filters, struct filter, entry)
+    {
+        list_add_tail(sorted_filters, &filter->sorted_entry);
+    }
 
-    while ((cursor = list_head(&graph->sorted_filters)))
+    while ((cursor = list_head(sorted_filters)))
     {
-        struct filter *filter = LIST_ENTRY(cursor, struct filter, sorted_entry);
-        sort_filter_recurse(graph, filter, &sorted);
+        filter = LIST_ENTRY(cursor, struct filter, sorted_entry);
+        sort_filter_recurse(sorted_filters, filter, &sorted);
     }
 
-    list_move_tail(&graph->sorted_filters, &sorted);
+    list_move_tail(sorted_filters, &sorted);
 }
 
 /* NOTE: despite the implication, it doesn't matter which
@@ -1002,9 +998,6 @@ static HRESULT WINAPI FilterGraph2_ConnectDirect(IFilterGraph2 *iface, IPin *ppi
         }
     }
 
-    if (SUCCEEDED(hr))
-        sort_filters(This);
-
     return hr;
 }
 
@@ -5120,6 +5113,7 @@ static HRESULT WINAPI MediaFilter_Stop(IMediaFilter *iface)
 {
     IFilterGraphImpl *graph = impl_from_IMediaFilter(iface);
     HRESULT hr = S_OK, filter_hr;
+    struct list sorted_filters;
     struct filter *filter;
 
     TRACE("graph %p.\n", graph);
@@ -5132,9 +5126,11 @@ static HRESULT WINAPI MediaFilter_Stop(IMediaFilter *iface)
         return S_OK;
     }
 
+    sort_filters(graph, &sorted_filters);
+
     if (graph->state == State_Running)
     {
-        LIST_FOR_EACH_ENTRY(filter, &graph->sorted_filters, struct filter, sorted_entry)
+        LIST_FOR_EACH_ENTRY(filter, &sorted_filters, struct filter, sorted_entry)
         {
             filter_hr = IBaseFilter_Pause(filter->filter);
             if (hr == S_OK)
@@ -5142,7 +5138,7 @@ static HRESULT WINAPI MediaFilter_Stop(IMediaFilter *iface)
         }
     }
 
-    LIST_FOR_EACH_ENTRY(filter, &graph->sorted_filters, struct filter, sorted_entry)
+    LIST_FOR_EACH_ENTRY(filter, &sorted_filters, struct filter, sorted_entry)
     {
         filter_hr = IBaseFilter_Stop(filter->filter);
         if (hr == S_OK)
@@ -5163,6 +5159,7 @@ static HRESULT WINAPI MediaFilter_Pause(IMediaFilter *iface)
 {
     IFilterGraphImpl *graph = impl_from_IMediaFilter(iface);
     HRESULT hr = S_OK, filter_hr;
+    struct list sorted_filters;
     struct filter *filter;
 
     TRACE("graph %p.\n", graph);
@@ -5175,6 +5172,8 @@ static HRESULT WINAPI MediaFilter_Pause(IMediaFilter *iface)
         return S_OK;
     }
 
+    sort_filters(graph, &sorted_filters);
+
     if (graph->defaultclock && !graph->refClock)
         IFilterGraph2_SetDefaultSyncSource(&graph->IFilterGraph2_iface);
 
@@ -5186,7 +5185,7 @@ static HRESULT WINAPI MediaFilter_Pause(IMediaFilter *iface)
         graph->current_pos += graph->stream_elapsed;
     }
 
-    LIST_FOR_EACH_ENTRY(filter, &graph->sorted_filters, struct filter, sorted_entry)
+    LIST_FOR_EACH_ENTRY(filter, &sorted_filters, struct filter, sorted_entry)
     {
         filter_hr = IBaseFilter_Pause(filter->filter);
         if (hr == S_OK)
@@ -5204,6 +5203,7 @@ static HRESULT WINAPI MediaFilter_Run(IMediaFilter *iface, REFERENCE_TIME start)
     IFilterGraphImpl *graph = impl_from_IMediaFilter(iface);
     REFERENCE_TIME stream_start = start;
     HRESULT hr = S_OK, filter_hr;
+    struct list sorted_filters;
     struct filter *filter;
 
     TRACE("graph %p, start %s.\n", graph, debugstr_time(start));
@@ -5218,6 +5218,8 @@ static HRESULT WINAPI MediaFilter_Run(IMediaFilter *iface, REFERENCE_TIME start)
     graph->EcCompleteCount = 0;
     graph->nRenderers = 0;
 
+    sort_filters(graph, &sorted_filters);
+
     if (graph->defaultclock && !graph->refClock)
         IFilterGraph2_SetDefaultSyncSource(&graph->IFilterGraph2_iface);
 
@@ -5229,7 +5231,7 @@ static HRESULT WINAPI MediaFilter_Run(IMediaFilter *iface, REFERENCE_TIME start)
             stream_start += 500000;
     }
 
-    LIST_FOR_EACH_ENTRY(filter, &graph->sorted_filters, struct filter, sorted_entry)
+    LIST_FOR_EACH_ENTRY(filter, &sorted_filters, struct filter, sorted_entry)
     {
         filter_hr = IBaseFilter_Run(filter->filter, stream_start);
         if (hr == S_OK)
@@ -5249,6 +5251,7 @@ static HRESULT WINAPI MediaFilter_GetState(IMediaFilter *iface, DWORD timeout, F
     IFilterGraphImpl *graph = impl_from_IMediaFilter(iface);
     DWORD end = GetTickCount() + timeout;
     HRESULT hr = S_OK, filter_hr;
+    struct list sorted_filters;
     struct filter *filter;
 
     TRACE("graph %p, timeout %u, state %p.\n", graph, timeout, state);
@@ -5258,9 +5261,11 @@ static HRESULT WINAPI MediaFilter_GetState(IMediaFilter *iface, DWORD timeout, F
 
     EnterCriticalSection(&graph->cs);
 
+    sort_filters(graph, &sorted_filters);
+
     *state = graph->state;
 
-    LIST_FOR_EACH_ENTRY(filter, &graph->sorted_filters, struct filter, sorted_entry)
+    LIST_FOR_EACH_ENTRY(filter, &sorted_filters, struct filter, sorted_entry)
     {
         FILTER_STATE filter_state;
         int wait;
@@ -5681,7 +5686,6 @@ static HRESULT filter_graph_common_create(IUnknown *outer, IUnknown **out, BOOL
     fimpl->IGraphVersion_iface.lpVtbl = &IGraphVersion_VTable;
     fimpl->ref = 1;
     list_init(&fimpl->filters);
-    list_init(&fimpl->sorted_filters);
     fimpl->name_index = 1;
     fimpl->refClock = NULL;
     fimpl->hEventCompletion = CreateEventW(0, TRUE, FALSE, 0);
diff --git a/dlls/quartz/tests/filtergraph.c b/dlls/quartz/tests/filtergraph.c
index e0883ed258..3a14a1a6e9 100644
--- a/dlls/quartz/tests/filtergraph.c
+++ b/dlls/quartz/tests/filtergraph.c
@@ -3165,7 +3165,9 @@ static void test_filter_state(void)
     IFilterGraph2_AddFilter(graph, &source.IBaseFilter_iface, NULL);
     IFilterGraph2_AddFilter(graph, &sink.IBaseFilter_iface, NULL);
     IFilterGraph2_AddFilter(graph, &dummy.IBaseFilter_iface, NULL);
-    IFilterGraph2_ConnectDirect(graph, &source_pin.IPin_iface, &sink_pin.IPin_iface, NULL);
+    /* Using IPin::Connect instead of IFilterGraph2::ConnectDirect to show that */
+    /* FilterGraph does not rely on ::ConnectDirect to track filter connections. */
+    IPin_Connect(&source_pin.IPin_iface, &sink_pin.IPin_iface, NULL);
 
     check_filter_state(graph, State_Stopped);
 
@@ -3240,9 +3242,12 @@ static void test_filter_state(void)
     IFilterGraph2_QueryInterface(graph, &IID_IMediaFilter, (void **)&filter);
     IFilterGraph2_QueryInterface(graph, &IID_IMediaControl, (void **)&control);
 
-    IFilterGraph2_AddFilter(graph, &source.IBaseFilter_iface, NULL);
+    /* Add the filters in reverse order this time. */
     IFilterGraph2_AddFilter(graph, &sink.IBaseFilter_iface, NULL);
-    IFilterGraph2_ConnectDirect(graph, &source_pin.IPin_iface, &sink_pin.IPin_iface, NULL);
+    IFilterGraph2_AddFilter(graph, &source.IBaseFilter_iface, NULL);
+    /* Using IPin::Connect instead of IFilterGraph2::ConnectDirect to show that */
+    /* FilterGraph does not rely on ::ConnectDirect to track filter connections. */
+    IPin_Connect(&source_pin.IPin_iface, &sink_pin.IPin_iface, NULL);
 
     hr = IMediaFilter_Pause(filter);
     ok(hr == S_OK, "Got hr %#x.\n", hr);
-- 
2.17.1




More information about the wine-devel mailing list