From 98a0c1dac1ee111c6e4ecbd621b44cd4d1556ef7 Mon Sep 17 00:00:00 2001 From: Zebediah Figura Date: Wed, 19 Feb 2020 20:44:56 -0600 Subject: [PATCH] quartz/filtergraph: Maintain a topologically sorted list of filters. Signed-off-by: Zebediah Figura Signed-off-by: Alexandre Julliard --- dlls/quartz/filtergraph.c | 87 ++++++++++++++++++++++++++++++++++++++- 1 file changed, 85 insertions(+), 2 deletions(-) diff --git a/dlls/quartz/filtergraph.c b/dlls/quartz/filtergraph.c index 46ae113a4bd..9e568224807 100644 --- a/dlls/quartz/filtergraph.c +++ b/dlls/quartz/filtergraph.c @@ -18,6 +18,7 @@ * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA */ +#include #include #define COBJMACROS @@ -150,9 +151,10 @@ typedef struct _ITF_CACHE_ENTRY { struct filter { - struct list entry; + struct list entry, sorted_entry; IBaseFilter *filter; WCHAR *name; + BOOL sorting; }; typedef struct _IFilterGraphImpl { @@ -183,8 +185,18 @@ typedef struct _IFilterGraphImpl { IUnknown *outer_unk; LONG ref; IUnknown *punkFilterMapper2; - struct list filters; + /* 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; unsigned int name_index; IReferenceClock *refClock; @@ -617,6 +629,8 @@ 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; ++graph->version; return duplicate_name ? VFW_S_DUPLICATE_NAME : hr; @@ -694,6 +708,7 @@ static HRESULT WINAPI FilterGraph2_RemoveFilter(IFilterGraph2 *iface, IBaseFilte IBaseFilter_SetSyncSource(pFilter, NULL); IBaseFilter_Release(pFilter); list_remove(&entry->entry); + list_remove(&entry->sorted_entry); CoTaskMemFree(entry->name); heap_free(entry); This->version++; @@ -818,6 +833,70 @@ out: #endif } +static struct filter *find_sorted_filter(IFilterGraphImpl *graph, IBaseFilter *iface) +{ + struct filter *filter; + + LIST_FOR_EACH_ENTRY(filter, &graph->sorted_filters, struct filter, sorted_entry) + { + if (filter->filter == iface) + return filter; + } + + return NULL; +} + +static void sort_filter_recurse(IFilterGraphImpl *graph, struct filter *filter, struct list *sorted) +{ + struct filter *peer_filter; + IEnumPins *enumpins; + PIN_DIRECTION dir; + IPin *pin, *peer; + PIN_INFO info; + + TRACE("Sorting filter %p.\n", filter->filter); + + /* Cyclic connections should be caught by CheckCircularConnection(). */ + assert(!filter->sorting); + + filter->sorting = TRUE; + + IBaseFilter_EnumPins(filter->filter, &enumpins); + while (IEnumPins_Next(enumpins, 1, &pin, NULL) == S_OK) + { + IPin_QueryDirection(pin, &dir); + + if (dir == PINDIR_INPUT && IPin_ConnectedTo(pin, &peer) == S_OK) + { + 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); + IBaseFilter_Release(info.pFilter); + IPin_Release(peer); + } + IPin_Release(pin); + } + IEnumPins_Release(enumpins); + + filter->sorting = FALSE; + + list_remove(&filter->sorted_entry); + list_add_head(sorted, &filter->sorted_entry); +} + +static void sort_filters(IFilterGraphImpl *graph) +{ + struct list sorted = LIST_INIT(sorted), *cursor; + + while ((cursor = list_head(&graph->sorted_filters))) + { + struct filter *filter = LIST_ENTRY(cursor, struct filter, sorted_entry); + sort_filter_recurse(graph, filter, &sorted); + } + + list_move_tail(&graph->sorted_filters, &sorted); +} /* NOTE: despite the implication, it doesn't matter which * way round you put in the input and output pins */ @@ -869,6 +948,9 @@ static HRESULT WINAPI FilterGraph2_ConnectDirect(IFilterGraph2 *iface, IPin *ppi } } + if (SUCCEEDED(hr)) + sort_filters(This); + return hr; } @@ -5702,6 +5784,7 @@ static HRESULT filter_graph_common_create(IUnknown *outer, void **out, BOOL thre 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);