quartz/filtergraph: Maintain a topologically sorted list of filters.

Signed-off-by: Zebediah Figura <z.figura12@gmail.com>
Signed-off-by: Alexandre Julliard <julliard@winehq.org>
This commit is contained in:
Zebediah Figura 2020-02-19 20:44:56 -06:00 committed by Alexandre Julliard
parent 7a392c55d6
commit 98a0c1dac1
1 changed files with 85 additions and 2 deletions

View File

@ -18,6 +18,7 @@
* Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA
*/
#include <assert.h>
#include <stdarg.h>
#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);