quartz: Call EnumMatchingFilters() once in autoplug().

Instead of iterating through types one by one and calling EnumMatchingFilters()
on each one, build a list of all media types exposed by the pin and call the
function once.

This avoids trying to pointless autoplug the same filter multiple times, which
speeds up autoplugging greatly for cases when a given filter connects less than
instantaneously. The most prominent example of such a filter is the AVI
decompressor, which has to call ICLocate() on connection. ICLocate() is not a
fast API on Wine, and it is even slower on Windows; this reduces the number of
times we try to call it greatly.
This commit is contained in:
Zebediah Figura 2023-07-22 19:52:12 -05:00 committed by Alexandre Julliard
parent ce3ccccb2c
commit e6cc4ae4b4
2 changed files with 128 additions and 75 deletions

View file

@ -1195,16 +1195,79 @@ static HRESULT autoplug_through_filter(struct filter_graph *graph, IPin *source,
return VFW_E_CANNOT_CONNECT;
}
static HRESULT get_autoplug_types(IPin *source, unsigned int *ret_count, GUID **ret_types)
{
unsigned int i, mt_count = 0, mt_capacity = 16;
AM_MEDIA_TYPE **mts = NULL;
IEnumMediaTypes *enummt;
GUID *types = NULL;
HRESULT hr;
if (FAILED(hr = IPin_EnumMediaTypes(source, &enummt)))
{
ERR("Failed to enumerate media types, hr %#lx.\n", hr);
return hr;
}
for (;;)
{
ULONG count;
if (!(mts = realloc(mts, mt_capacity * sizeof(*mts))))
{
hr = E_OUTOFMEMORY;
goto out;
}
if (FAILED(hr = IEnumMediaTypes_Next(enummt, mt_capacity - mt_count, mts + mt_count, &count)))
{
ERR("Failed to get media types, hr %#lx.\n", hr);
goto out;
}
mt_count += count;
if (hr == S_FALSE)
break;
mt_capacity *= 2;
}
if (!(types = malloc(mt_count * 2 * sizeof(*types))))
{
hr = E_OUTOFMEMORY;
goto out;
}
for (i = 0; i < mt_count; ++i)
{
types[i * 2] = mts[i]->majortype;
types[i * 2 + 1] = mts[i]->subtype;
DeleteMediaType(mts[i]);
}
*ret_count = mt_count;
*ret_types = types;
hr = S_OK;
out:
free(mts);
IEnumMediaTypes_Release(enummt);
return hr;
}
/* Common helper for IGraphBuilder::Connect() and IGraphBuilder::Render(), which
* share most of the same code. Render() calls this with a NULL sink. */
static HRESULT autoplug(struct filter_graph *graph, IPin *source, IPin *sink,
BOOL render_to_existing, unsigned int recursion_depth)
{
IAMGraphBuilderCallback *callback = NULL;
IEnumMediaTypes *enummt;
struct filter *graph_filter;
IEnumMoniker *enummoniker;
unsigned int type_count;
IFilterMapper2 *mapper;
struct filter *filter;
AM_MEDIA_TYPE *mt;
IBaseFilter *filter;
GUID *types = NULL;
IMoniker *moniker;
HRESULT hr;
TRACE("Trying to autoplug %p to %p, recursion depth %u.\n", source, sink, recursion_depth);
@ -1228,16 +1291,16 @@ static HRESULT autoplug(struct filter_graph *graph, IPin *source, IPin *sink,
}
/* Always prefer filters in the graph. */
LIST_FOR_EACH_ENTRY(filter, &graph->filters, struct filter, entry)
LIST_FOR_EACH_ENTRY(graph_filter, &graph->filters, struct filter, entry)
{
if (SUCCEEDED(hr = autoplug_through_filter(graph, source, filter->filter,
if (SUCCEEDED(hr = autoplug_through_filter(graph, source, graph_filter->filter,
sink, render_to_existing, recursion_depth)))
return hr;
}
IUnknown_QueryInterface(graph->punkFilterMapper2, &IID_IFilterMapper2, (void **)&mapper);
if (FAILED(hr = IPin_EnumMediaTypes(source, &enummt)))
if (FAILED(hr = get_autoplug_types(source, &type_count, &types)))
{
IFilterMapper2_Release(mapper);
return hr;
@ -1246,85 +1309,75 @@ static HRESULT autoplug(struct filter_graph *graph, IPin *source, IPin *sink,
if (graph->pSite)
IUnknown_QueryInterface(graph->pSite, &IID_IAMGraphBuilderCallback, (void **)&callback);
while (IEnumMediaTypes_Next(enummt, 1, &mt, NULL) == S_OK)
if (FAILED(hr = IFilterMapper2_EnumMatchingFilters(mapper, &enummoniker,
0, FALSE, MERIT_UNLIKELY, TRUE, type_count, types, NULL, NULL, FALSE,
render_to_existing, 0, NULL, NULL, NULL)))
goto out;
while (IEnumMoniker_Next(enummoniker, 1, &moniker, NULL) == S_OK)
{
GUID types[2] = {mt->majortype, mt->subtype};
IEnumMoniker *enummoniker;
IBaseFilter *filter;
IMoniker *moniker;
IPropertyBag *bag;
VARIANT var;
DeleteMediaType(mt);
if (FAILED(hr = IFilterMapper2_EnumMatchingFilters(mapper, &enummoniker,
0, FALSE, MERIT_UNLIKELY, TRUE, 1, types, NULL, NULL, FALSE,
render_to_existing, 0, NULL, NULL, NULL)))
goto out;
while (IEnumMoniker_Next(enummoniker, 1, &moniker, NULL) == S_OK)
VariantInit(&var);
IMoniker_BindToStorage(moniker, NULL, NULL, &IID_IPropertyBag, (void **)&bag);
hr = IPropertyBag_Read(bag, L"FriendlyName", &var, NULL);
IPropertyBag_Release(bag);
if (FAILED(hr))
{
IPropertyBag *bag;
VARIANT var;
VariantInit(&var);
IMoniker_BindToStorage(moniker, NULL, NULL, &IID_IPropertyBag, (void **)&bag);
hr = IPropertyBag_Read(bag, L"FriendlyName", &var, NULL);
IPropertyBag_Release(bag);
if (FAILED(hr))
{
IMoniker_Release(moniker);
continue;
}
if (callback && FAILED(hr = IAMGraphBuilderCallback_SelectedFilter(callback, moniker)))
{
TRACE("Filter rejected by IAMGraphBuilderCallback::SelectedFilter(), hr %#lx.\n", hr);
IMoniker_Release(moniker);
continue;
}
hr = create_filter(graph, moniker, &filter);
IMoniker_Release(moniker);
if (FAILED(hr))
{
ERR("Failed to create filter for %s, hr %#lx.\n", debugstr_w(V_BSTR(&var)), hr);
VariantClear(&var);
continue;
}
if (callback && FAILED(hr = IAMGraphBuilderCallback_CreatedFilter(callback, filter)))
{
TRACE("Filter rejected by IAMGraphBuilderCallback::CreatedFilter(), hr %#lx.\n", hr);
IBaseFilter_Release(filter);
continue;
}
hr = IFilterGraph2_AddFilter(&graph->IFilterGraph2_iface, filter, V_BSTR(&var));
VariantClear(&var);
if (FAILED(hr))
{
ERR("Failed to add filter, hr %#lx.\n", hr);
IBaseFilter_Release(filter);
continue;
}
hr = autoplug_through_filter(graph, source, filter, sink, render_to_existing, recursion_depth);
if (SUCCEEDED(hr))
{
IBaseFilter_Release(filter);
goto out;
}
IFilterGraph2_RemoveFilter(&graph->IFilterGraph2_iface, filter);
IBaseFilter_Release(filter);
continue;
}
IEnumMoniker_Release(enummoniker);
if (callback && FAILED(hr = IAMGraphBuilderCallback_SelectedFilter(callback, moniker)))
{
TRACE("Filter rejected by IAMGraphBuilderCallback::SelectedFilter(), hr %#lx.\n", hr);
IMoniker_Release(moniker);
continue;
}
hr = create_filter(graph, moniker, &filter);
IMoniker_Release(moniker);
if (FAILED(hr))
{
ERR("Failed to create filter for %s, hr %#lx.\n", debugstr_w(V_BSTR(&var)), hr);
VariantClear(&var);
continue;
}
if (callback && FAILED(hr = IAMGraphBuilderCallback_CreatedFilter(callback, filter)))
{
TRACE("Filter rejected by IAMGraphBuilderCallback::CreatedFilter(), hr %#lx.\n", hr);
IBaseFilter_Release(filter);
continue;
}
hr = IFilterGraph2_AddFilter(&graph->IFilterGraph2_iface, filter, V_BSTR(&var));
VariantClear(&var);
if (FAILED(hr))
{
ERR("Failed to add filter, hr %#lx.\n", hr);
IBaseFilter_Release(filter);
continue;
}
hr = autoplug_through_filter(graph, source, filter, sink, render_to_existing, recursion_depth);
if (SUCCEEDED(hr))
{
IBaseFilter_Release(filter);
goto out;
}
IFilterGraph2_RemoveFilter(&graph->IFilterGraph2_iface, filter);
IBaseFilter_Release(filter);
}
IEnumMoniker_Release(enummoniker);
hr = VFW_E_CANNOT_CONNECT;
out:
free(types);
if (callback) IAMGraphBuilderCallback_Release(callback);
IEnumMediaTypes_Release(enummt);
IFilterMapper2_Release(mapper);
return hr;
}

View file

@ -5344,7 +5344,7 @@ static void test_autoplug_uyvy(void)
* failure to decode up to missing audio hardware, even though we're not
* trying to render audio. */
hr = IFilterGraph2_Render(graph, &source_pin.IPin_iface);
todo_wine ok(hr == S_OK || hr == VFW_E_NO_AUDIO_HARDWARE, "Got hr %#lx.\n", hr);
ok(hr == S_OK || hr == VFW_E_NO_AUDIO_HARDWARE, "Got hr %#lx.\n", hr);
ref = IFilterGraph2_Release(graph);
ok(!ref, "Got outstanding refcount %ld.\n", ref);