From e6cc4ae4b488ab1870681ed1747a185e4302aca2 Mon Sep 17 00:00:00 2001 From: Zebediah Figura Date: Sat, 22 Jul 2023 19:52:12 -0500 Subject: [PATCH] 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. --- dlls/quartz/filtergraph.c | 201 ++++++++++++++++++++------------ dlls/quartz/tests/filtergraph.c | 2 +- 2 files changed, 128 insertions(+), 75 deletions(-) diff --git a/dlls/quartz/filtergraph.c b/dlls/quartz/filtergraph.c index 8c8f89ce2ac..e0e126d8ef0 100644 --- a/dlls/quartz/filtergraph.c +++ b/dlls/quartz/filtergraph.c @@ -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; } diff --git a/dlls/quartz/tests/filtergraph.c b/dlls/quartz/tests/filtergraph.c index 391f9777175..00232ea7a52 100644 --- a/dlls/quartz/tests/filtergraph.c +++ b/dlls/quartz/tests/filtergraph.c @@ -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);