quartz: Combine IGraphBuilder::Render() and IGraphBuilder::Connect() into a single helper.
Signed-off-by: Zebediah Figura <z.figura12@gmail.com> Signed-off-by: Alexandre Julliard <julliard@winehq.org>
This commit is contained in:
parent
2ddc3e4758
commit
c603a5bfbc
|
@ -207,7 +207,6 @@ typedef struct _IFilterGraphImpl {
|
|||
int nItfCacheEntries;
|
||||
BOOL defaultclock;
|
||||
GUID timeformatseek;
|
||||
LONG recursioncount;
|
||||
IUnknown *pSite;
|
||||
LONG version;
|
||||
|
||||
|
@ -1059,27 +1058,6 @@ static HRESULT WINAPI FilterGraph2_SetDefaultSyncSource(IFilterGraph2 *iface)
|
|||
return hr;
|
||||
}
|
||||
|
||||
static HRESULT GetFilterInfo(IMoniker* pMoniker, VARIANT* pvar)
|
||||
{
|
||||
IPropertyBag * pPropBagCat = NULL;
|
||||
HRESULT hr;
|
||||
|
||||
VariantInit(pvar);
|
||||
|
||||
hr = IMoniker_BindToStorage(pMoniker, NULL, NULL, &IID_IPropertyBag, (LPVOID*)&pPropBagCat);
|
||||
|
||||
if (SUCCEEDED(hr))
|
||||
hr = IPropertyBag_Read(pPropBagCat, L"FriendlyName", pvar, NULL);
|
||||
|
||||
if (SUCCEEDED(hr))
|
||||
TRACE("Moniker = %s\n", debugstr_w(V_BSTR(pvar)));
|
||||
|
||||
if (pPropBagCat)
|
||||
IPropertyBag_Release(pPropBagCat);
|
||||
|
||||
return hr;
|
||||
}
|
||||
|
||||
struct filter_create_params
|
||||
{
|
||||
HRESULT hr;
|
||||
|
@ -1141,621 +1119,292 @@ static HRESULT create_filter(IFilterGraphImpl *graph, IMoniker *moniker, IBaseFi
|
|||
return IMoniker_BindToObject(moniker, NULL, NULL, &IID_IBaseFilter, (void **)filter);
|
||||
}
|
||||
|
||||
/* Attempt to connect one of the output pins on filter to sink. Helper for
|
||||
* FilterGraph2_Connect(). */
|
||||
static HRESULT connect_output_pin(IFilterGraphImpl *graph, IBaseFilter *filter, IPin *sink)
|
||||
static HRESULT autoplug(IFilterGraphImpl *graph, IPin *source, IPin *sink, unsigned int recursion_depth);
|
||||
|
||||
static HRESULT autoplug_through_sink(IFilterGraphImpl *graph, IPin *source,
|
||||
IBaseFilter *filter, IPin *middle_sink, IPin *sink, unsigned int recursion_depth)
|
||||
{
|
||||
IEnumPins *enumpins;
|
||||
BOOL any = FALSE, all = TRUE;
|
||||
IPin *middle_source, *peer;
|
||||
IEnumPins *source_enum;
|
||||
PIN_DIRECTION dir;
|
||||
PIN_INFO info;
|
||||
HRESULT hr;
|
||||
IPin *pin;
|
||||
|
||||
hr = IBaseFilter_EnumPins(filter, &enumpins);
|
||||
if (FAILED(hr))
|
||||
return hr;
|
||||
TRACE("Trying to autoplug %p to %p through %p.\n", source, sink, middle_sink);
|
||||
|
||||
while (IEnumPins_Next(enumpins, 1, &pin, NULL) == S_OK)
|
||||
IPin_QueryDirection(middle_sink, &dir);
|
||||
if (dir != PINDIR_INPUT)
|
||||
return E_FAIL;
|
||||
|
||||
if (IPin_ConnectedTo(middle_sink, &peer) == S_OK)
|
||||
{
|
||||
IPin_QueryPinInfo(pin, &info);
|
||||
IPin_Release(peer);
|
||||
return E_FAIL;
|
||||
}
|
||||
|
||||
if (FAILED(hr = IFilterGraph2_ConnectDirect(&graph->IFilterGraph2_iface, source, middle_sink, NULL)))
|
||||
return E_FAIL;
|
||||
|
||||
if (FAILED(hr = IBaseFilter_EnumPins(filter, &source_enum)))
|
||||
goto err;
|
||||
|
||||
while (IEnumPins_Next(source_enum, 1, &middle_source, NULL) == S_OK)
|
||||
{
|
||||
IPin_QueryPinInfo(middle_source, &info);
|
||||
IBaseFilter_Release(info.pFilter);
|
||||
if (info.dir == PINDIR_OUTPUT)
|
||||
if (info.dir != PINDIR_OUTPUT)
|
||||
{
|
||||
IPin_Release(middle_source);
|
||||
continue;
|
||||
}
|
||||
if (info.achName[0] == '~')
|
||||
{
|
||||
TRACE("Skipping non-rendered pin %s.\n", debugstr_w(info.achName));
|
||||
IPin_Release(pin);
|
||||
IPin_Release(middle_source);
|
||||
continue;
|
||||
}
|
||||
if (IPin_ConnectedTo(middle_source, &peer) == S_OK)
|
||||
{
|
||||
IPin_Release(peer);
|
||||
IPin_Release(middle_source);
|
||||
continue;
|
||||
}
|
||||
|
||||
if (SUCCEEDED(IFilterGraph2_Connect(&graph->IFilterGraph2_iface, pin, sink)))
|
||||
hr = autoplug(graph, middle_source, sink, recursion_depth + 1);
|
||||
IPin_Release(middle_source);
|
||||
if (SUCCEEDED(hr) && sink)
|
||||
{
|
||||
IPin_Release(pin);
|
||||
IEnumPins_Release(enumpins);
|
||||
IEnumPins_Release(source_enum);
|
||||
return hr;
|
||||
}
|
||||
if (SUCCEEDED(hr))
|
||||
any = TRUE;
|
||||
if (hr != S_OK)
|
||||
all = FALSE;
|
||||
}
|
||||
IEnumPins_Release(source_enum);
|
||||
|
||||
if (!sink)
|
||||
{
|
||||
if (all)
|
||||
return S_OK;
|
||||
}
|
||||
}
|
||||
IPin_Release(pin);
|
||||
if (any)
|
||||
return VFW_S_PARTIAL_RENDER;
|
||||
}
|
||||
|
||||
IEnumPins_Release(enumpins);
|
||||
err:
|
||||
IFilterGraph2_Disconnect(&graph->IFilterGraph2_iface, source);
|
||||
IFilterGraph2_Disconnect(&graph->IFilterGraph2_iface, middle_sink);
|
||||
return E_FAIL;
|
||||
}
|
||||
|
||||
static HRESULT autoplug_through_filter(IFilterGraphImpl *graph, IPin *source,
|
||||
IBaseFilter *filter, IPin *sink, unsigned int recursion_depth)
|
||||
{
|
||||
IEnumPins *sink_enum;
|
||||
IPin *filter_sink;
|
||||
HRESULT hr;
|
||||
|
||||
TRACE("Trying to autoplug %p to %p through %p.\n", source, sink, filter);
|
||||
|
||||
if (FAILED(hr = IBaseFilter_EnumPins(filter, &sink_enum)))
|
||||
return hr;
|
||||
|
||||
while (IEnumPins_Next(sink_enum, 1, &filter_sink, NULL) == S_OK)
|
||||
{
|
||||
hr = autoplug_through_sink(graph, source, filter, filter_sink, sink, recursion_depth);
|
||||
IPin_Release(filter_sink);
|
||||
if (SUCCEEDED(hr))
|
||||
{
|
||||
IEnumPins_Release(sink_enum);
|
||||
return hr;
|
||||
}
|
||||
}
|
||||
IEnumPins_Release(sink_enum);
|
||||
return VFW_E_CANNOT_CONNECT;
|
||||
}
|
||||
|
||||
/*** IGraphBuilder methods ***/
|
||||
static HRESULT WINAPI FilterGraph2_Connect(IFilterGraph2 *iface, IPin *ppinOut, IPin *ppinIn)
|
||||
/* 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(IFilterGraphImpl *graph, IPin *source, IPin *sink, unsigned int recursion_depth)
|
||||
{
|
||||
IFilterGraphImpl *This = impl_from_IFilterGraph2(iface);
|
||||
IAMGraphBuilderCallback *callback = NULL;
|
||||
IEnumMediaTypes *enummt;
|
||||
IFilterMapper2 *mapper;
|
||||
struct filter *filter;
|
||||
AM_MEDIA_TYPE *mt;
|
||||
HRESULT hr;
|
||||
IPin *pin;
|
||||
AM_MEDIA_TYPE* mt = NULL;
|
||||
IEnumMediaTypes* penummt = NULL;
|
||||
ULONG nbmt;
|
||||
IEnumPins* penumpins;
|
||||
IEnumMoniker* pEnumMoniker;
|
||||
GUID tab[2];
|
||||
IMoniker* pMoniker;
|
||||
PIN_INFO PinInfo;
|
||||
|
||||
TRACE("Trying to autoplug %p to %p, recursion depth %u.\n", source, sink, recursion_depth);
|
||||
|
||||
if (recursion_depth >= 5)
|
||||
{
|
||||
WARN("Recursion depth has reached 5; aborting.\n");
|
||||
return VFW_E_CANNOT_CONNECT;
|
||||
}
|
||||
|
||||
if (sink)
|
||||
{
|
||||
/* Try to connect directly to this sink. */
|
||||
hr = IFilterGraph2_ConnectDirect(&graph->IFilterGraph2_iface, source, sink, NULL);
|
||||
|
||||
/* If direct connection succeeded, we should propagate that return value.
|
||||
* If it returned VFW_E_NOT_CONNECTED or VFW_E_NO_AUDIO_HARDWARE, then don't
|
||||
* even bother trying intermediate filters, since they won't succeed. */
|
||||
if (SUCCEEDED(hr) || hr == VFW_E_NOT_CONNECTED || hr == VFW_E_NO_AUDIO_HARDWARE)
|
||||
return hr;
|
||||
}
|
||||
|
||||
/* Always prefer filters in the graph. */
|
||||
LIST_FOR_EACH_ENTRY(filter, &graph->filters, struct filter, entry)
|
||||
{
|
||||
if (SUCCEEDED(hr = autoplug_through_filter(graph, source, filter->filter, sink, recursion_depth)))
|
||||
return hr;
|
||||
}
|
||||
|
||||
IUnknown_QueryInterface(graph->punkFilterMapper2, &IID_IFilterMapper2, (void **)&mapper);
|
||||
|
||||
if (FAILED(hr = IPin_EnumMediaTypes(source, &enummt)))
|
||||
{
|
||||
IFilterMapper2_Release(mapper);
|
||||
return hr;
|
||||
}
|
||||
|
||||
if (graph->pSite)
|
||||
IUnknown_QueryInterface(graph->pSite, &IID_IAMGraphBuilderCallback, (void **)&callback);
|
||||
|
||||
while (IEnumMediaTypes_Next(enummt, 1, &mt, NULL) == S_OK)
|
||||
{
|
||||
GUID types[2] = {mt->majortype, mt->subtype};
|
||||
IEnumMoniker *enummoniker;
|
||||
IBaseFilter *filter;
|
||||
IMoniker *moniker;
|
||||
|
||||
DeleteMediaType(mt);
|
||||
|
||||
if (FAILED(hr = IFilterMapper2_EnumMatchingFilters(mapper, &enummoniker, 0, FALSE,
|
||||
MERIT_UNLIKELY, TRUE, 1, types, NULL, NULL, FALSE, FALSE, 0, NULL, NULL, NULL)))
|
||||
goto out;
|
||||
|
||||
while (IEnumMoniker_Next(enummoniker, 1, &moniker, NULL) == S_OK)
|
||||
{
|
||||
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 %#x.\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 %#x.\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 %#x.\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 %#x.\n", hr);
|
||||
IBaseFilter_Release(filter);
|
||||
continue;
|
||||
}
|
||||
|
||||
hr = autoplug_through_filter(graph, source, filter, sink, 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:
|
||||
if (callback) IAMGraphBuilderCallback_Release(callback);
|
||||
IEnumMediaTypes_Release(enummt);
|
||||
IFilterMapper2_Release(mapper);
|
||||
return hr;
|
||||
}
|
||||
|
||||
static HRESULT WINAPI FilterGraph2_Connect(IFilterGraph2 *iface, IPin *source, IPin *sink)
|
||||
{
|
||||
IFilterGraphImpl *graph = impl_from_IFilterGraph2(iface);
|
||||
PIN_DIRECTION dir;
|
||||
IFilterMapper2 *pFilterMapper2 = NULL;
|
||||
HRESULT hr;
|
||||
|
||||
TRACE("(%p/%p)->(%p, %p)\n", This, iface, ppinOut, ppinIn);
|
||||
TRACE("graph %p, source %p, sink %p.\n", graph, source, sink);
|
||||
|
||||
if(!ppinOut || !ppinIn)
|
||||
if (!source || !sink)
|
||||
return E_POINTER;
|
||||
|
||||
if (TRACE_ON(quartz))
|
||||
{
|
||||
hr = IPin_QueryPinInfo(ppinIn, &PinInfo);
|
||||
if (FAILED(hr))
|
||||
if (FAILED(hr = IPin_QueryDirection(source, &dir)))
|
||||
return hr;
|
||||
|
||||
TRACE("Filter owning ppinIn(%p) => %p\n", ppinIn, PinInfo.pFilter);
|
||||
IBaseFilter_Release(PinInfo.pFilter);
|
||||
|
||||
hr = IPin_QueryPinInfo(ppinOut, &PinInfo);
|
||||
if (FAILED(hr))
|
||||
return hr;
|
||||
|
||||
TRACE("Filter owning ppinOut(%p) => %p\n", ppinOut, PinInfo.pFilter);
|
||||
IBaseFilter_Release(PinInfo.pFilter);
|
||||
}
|
||||
|
||||
EnterCriticalSection(&This->cs);
|
||||
++This->recursioncount;
|
||||
if (This->recursioncount >= 5)
|
||||
{
|
||||
WARN("Recursion count has reached %d\n", This->recursioncount);
|
||||
hr = VFW_E_CANNOT_CONNECT;
|
||||
goto out;
|
||||
}
|
||||
|
||||
hr = IPin_QueryDirection(ppinOut, &dir);
|
||||
if (FAILED(hr))
|
||||
goto out;
|
||||
|
||||
if (dir == PINDIR_INPUT)
|
||||
{
|
||||
IPin *temp;
|
||||
|
||||
TRACE("Directions seem backwards, swapping pins\n");
|
||||
|
||||
temp = ppinIn;
|
||||
ppinIn = ppinOut;
|
||||
ppinOut = temp;
|
||||
temp = sink;
|
||||
sink = source;
|
||||
source = temp;
|
||||
}
|
||||
|
||||
/* Try direct connection first */
|
||||
hr = IFilterGraph2_ConnectDirect(iface, ppinOut, ppinIn, NULL);
|
||||
EnterCriticalSection(&graph->cs);
|
||||
|
||||
/* If direct connection succeeded, we should propagate that return value.
|
||||
* If it returned VFW_E_NOT_CONNECTED or VFW_E_NO_AUDIO_HARDWARE, then don't
|
||||
* even bother trying intermediate filters, since they won't succeed. */
|
||||
if (SUCCEEDED(hr) || hr == VFW_E_NOT_CONNECTED || hr == VFW_E_NO_AUDIO_HARDWARE)
|
||||
goto out;
|
||||
hr = autoplug(graph, source, sink, 0);
|
||||
|
||||
TRACE("Direct connection failed, trying to render using extra filters\n");
|
||||
LeaveCriticalSection(&graph->cs);
|
||||
|
||||
LIST_FOR_EACH_ENTRY(filter, &This->filters, struct filter, entry)
|
||||
{
|
||||
hr = IBaseFilter_EnumPins(filter->filter, &penumpins);
|
||||
if (FAILED(hr))
|
||||
goto out;
|
||||
|
||||
while (IEnumPins_Next(penumpins, 1, &pin, NULL) == S_OK)
|
||||
{
|
||||
IPin_QueryDirection(pin, &dir);
|
||||
if (dir == PINDIR_INPUT && SUCCEEDED(IFilterGraph2_ConnectDirect(iface,
|
||||
ppinOut, pin, NULL)))
|
||||
{
|
||||
if (SUCCEEDED(hr = connect_output_pin(This, filter->filter, ppinIn)))
|
||||
{
|
||||
IPin_Release(pin);
|
||||
IEnumPins_Release(penumpins);
|
||||
goto out;
|
||||
}
|
||||
|
||||
IFilterGraph2_Disconnect(iface, pin);
|
||||
IFilterGraph2_Disconnect(iface, ppinOut);
|
||||
}
|
||||
IPin_Release(pin);
|
||||
}
|
||||
|
||||
IEnumPins_Release(penumpins);
|
||||
}
|
||||
|
||||
/* Find the appropriate transform filter than can transform the minor media type of output pin of the upstream
|
||||
* filter to the minor mediatype of input pin of the renderer */
|
||||
hr = IPin_EnumMediaTypes(ppinOut, &penummt);
|
||||
if (FAILED(hr))
|
||||
{
|
||||
WARN("EnumMediaTypes (%x)\n", hr);
|
||||
goto out;
|
||||
}
|
||||
|
||||
hr = IEnumMediaTypes_Next(penummt, 1, &mt, &nbmt);
|
||||
if (FAILED(hr)) {
|
||||
WARN("IEnumMediaTypes_Next (%x)\n", hr);
|
||||
goto out;
|
||||
}
|
||||
|
||||
if (!nbmt)
|
||||
{
|
||||
WARN("No media type found!\n");
|
||||
hr = VFW_E_INVALIDMEDIATYPE;
|
||||
goto out;
|
||||
}
|
||||
TRACE("MajorType %s\n", debugstr_guid(&mt->majortype));
|
||||
TRACE("SubType %s\n", debugstr_guid(&mt->subtype));
|
||||
|
||||
hr = IUnknown_QueryInterface(This->punkFilterMapper2, &IID_IFilterMapper2, (void**)&pFilterMapper2);
|
||||
if (FAILED(hr)) {
|
||||
WARN("Unable to get IFilterMapper2 (%x)\n", hr);
|
||||
goto out;
|
||||
}
|
||||
|
||||
/* Try to find a suitable filter that can connect to the pin to render */
|
||||
tab[0] = mt->majortype;
|
||||
tab[1] = mt->subtype;
|
||||
hr = IFilterMapper2_EnumMatchingFilters(pFilterMapper2, &pEnumMoniker, 0, FALSE, MERIT_UNLIKELY, TRUE, 1, tab, NULL, NULL, FALSE, FALSE, 0, NULL, NULL, NULL);
|
||||
if (FAILED(hr)) {
|
||||
WARN("Unable to enum filters (%x)\n", hr);
|
||||
goto out;
|
||||
}
|
||||
|
||||
hr = VFW_E_CANNOT_RENDER;
|
||||
while (IEnumMoniker_Next(pEnumMoniker, 1, &pMoniker, NULL) == S_OK)
|
||||
{
|
||||
VARIANT var;
|
||||
IBaseFilter* pfilter = NULL;
|
||||
IAMGraphBuilderCallback *callback = NULL;
|
||||
|
||||
hr = GetFilterInfo(pMoniker, &var);
|
||||
if (FAILED(hr)) {
|
||||
WARN("Unable to retrieve filter info (%x)\n", hr);
|
||||
goto error;
|
||||
}
|
||||
|
||||
hr = create_filter(This, pMoniker, &pfilter);
|
||||
IMoniker_Release(pMoniker);
|
||||
if (FAILED(hr)) {
|
||||
WARN("Unable to create filter (%x), trying next one\n", hr);
|
||||
goto error;
|
||||
}
|
||||
|
||||
if (This->pSite)
|
||||
{
|
||||
IUnknown_QueryInterface(This->pSite, &IID_IAMGraphBuilderCallback, (LPVOID*)&callback);
|
||||
if (callback)
|
||||
{
|
||||
HRESULT rc;
|
||||
rc = IAMGraphBuilderCallback_SelectedFilter(callback, pMoniker);
|
||||
if (FAILED(rc))
|
||||
{
|
||||
TRACE("Filter rejected by IAMGraphBuilderCallback_SelectedFilter\n");
|
||||
IAMGraphBuilderCallback_Release(callback);
|
||||
goto error;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (callback)
|
||||
{
|
||||
HRESULT rc;
|
||||
rc = IAMGraphBuilderCallback_CreatedFilter(callback, pfilter);
|
||||
IAMGraphBuilderCallback_Release(callback);
|
||||
if (FAILED(rc))
|
||||
{
|
||||
IBaseFilter_Release(pfilter);
|
||||
pfilter = NULL;
|
||||
TRACE("Filter rejected by IAMGraphBuilderCallback_CreatedFilter\n");
|
||||
goto error;
|
||||
}
|
||||
}
|
||||
|
||||
hr = IFilterGraph2_AddFilter(iface, pfilter, V_BSTR(&var));
|
||||
if (FAILED(hr)) {
|
||||
WARN("Unable to add filter (%x)\n", hr);
|
||||
IBaseFilter_Release(pfilter);
|
||||
pfilter = NULL;
|
||||
goto error;
|
||||
}
|
||||
|
||||
VariantClear(&var);
|
||||
|
||||
hr = IBaseFilter_EnumPins(pfilter, &penumpins);
|
||||
if (FAILED(hr)) {
|
||||
WARN("Enumpins (%x)\n", hr);
|
||||
goto error;
|
||||
}
|
||||
|
||||
while (IEnumPins_Next(penumpins, 1, &pin, NULL) == S_OK)
|
||||
{
|
||||
if (SUCCEEDED(IFilterGraph2_ConnectDirect(iface, ppinOut, pin, NULL)))
|
||||
{
|
||||
if (SUCCEEDED(hr = connect_output_pin(This, pfilter, ppinIn)))
|
||||
{
|
||||
IPin_Release(pin);
|
||||
IEnumPins_Release(penumpins);
|
||||
IBaseFilter_Release(pfilter);
|
||||
IEnumMoniker_Release(pEnumMoniker);
|
||||
goto out;
|
||||
}
|
||||
IFilterGraph2_Disconnect(iface, pin);
|
||||
IFilterGraph2_Disconnect(iface, ppinOut);
|
||||
}
|
||||
IPin_Release(pin);
|
||||
}
|
||||
IEnumPins_Release(penumpins);
|
||||
|
||||
error:
|
||||
VariantClear(&var);
|
||||
if (pfilter) {
|
||||
IFilterGraph2_RemoveFilter(iface, pfilter);
|
||||
IBaseFilter_Release(pfilter);
|
||||
}
|
||||
}
|
||||
|
||||
if (FAILED(hr))
|
||||
hr = VFW_E_CANNOT_CONNECT;
|
||||
|
||||
IEnumMoniker_Release(pEnumMoniker);
|
||||
|
||||
out:
|
||||
if (pFilterMapper2)
|
||||
IFilterMapper2_Release(pFilterMapper2);
|
||||
if (penummt)
|
||||
IEnumMediaTypes_Release(penummt);
|
||||
if (mt)
|
||||
DeleteMediaType(mt);
|
||||
--This->recursioncount;
|
||||
LeaveCriticalSection(&This->cs);
|
||||
TRACE("--> %08x\n", hr);
|
||||
TRACE("Returning %#x.\n", hr);
|
||||
return hr;
|
||||
}
|
||||
|
||||
/* Render all output pins of the given filter. Helper for FilterGraph2_Render(). */
|
||||
static HRESULT render_output_pins(IFilterGraphImpl *graph, IBaseFilter *filter)
|
||||
static HRESULT WINAPI FilterGraph2_Render(IFilterGraph2 *iface, IPin *source)
|
||||
{
|
||||
BOOL renderany = FALSE;
|
||||
BOOL renderall = TRUE;
|
||||
IEnumPins *enumpins;
|
||||
IPin *pin, *peer;
|
||||
PIN_INFO info;
|
||||
|
||||
IBaseFilter_EnumPins(filter, &enumpins);
|
||||
while (IEnumPins_Next(enumpins, 1, &pin, NULL) == S_OK)
|
||||
{
|
||||
IPin_QueryPinInfo(pin, &info);
|
||||
IBaseFilter_Release(info.pFilter);
|
||||
if (info.dir == PINDIR_OUTPUT)
|
||||
{
|
||||
if (info.achName[0] == '~')
|
||||
{
|
||||
TRACE("Skipping non-rendered pin %s.\n", debugstr_w(info.achName));
|
||||
IPin_Release(pin);
|
||||
continue;
|
||||
}
|
||||
|
||||
if (IPin_ConnectedTo(pin, &peer) == VFW_E_NOT_CONNECTED)
|
||||
{
|
||||
HRESULT hr;
|
||||
hr = IFilterGraph2_Render(&graph->IFilterGraph2_iface, pin);
|
||||
if (SUCCEEDED(hr))
|
||||
renderany = TRUE;
|
||||
else
|
||||
renderall = FALSE;
|
||||
}
|
||||
else
|
||||
IPin_Release(peer);
|
||||
}
|
||||
|
||||
IPin_Release(pin);
|
||||
}
|
||||
|
||||
IEnumPins_Release(enumpins);
|
||||
|
||||
if (renderall)
|
||||
return S_OK;
|
||||
|
||||
if (renderany)
|
||||
return VFW_S_PARTIAL_RENDER;
|
||||
|
||||
return VFW_E_CANNOT_RENDER;
|
||||
}
|
||||
|
||||
/* Ogg hates me if I create a direct rendering method
|
||||
*
|
||||
* It can only connect to a pin properly once, so use a recursive method that does
|
||||
*
|
||||
* +----+ --- (PIN 1) (Render is called on this pin)
|
||||
* | |
|
||||
* +----+ --- (PIN 2)
|
||||
*
|
||||
* Enumerate possible renderers that EXACTLY match the requested type
|
||||
*
|
||||
* If none is available, try to add intermediate filters that can connect to the input pin
|
||||
* then call Render on that intermediate pin's output pins
|
||||
* if it succeeds: Render returns success, if it doesn't, the intermediate filter is removed,
|
||||
* and another filter that can connect to the input pin is tried
|
||||
* if we run out of filters that can, give up and return VFW_E_CANNOT_RENDER
|
||||
* It's recursive, but fun!
|
||||
*/
|
||||
|
||||
static HRESULT WINAPI FilterGraph2_Render(IFilterGraph2 *iface, IPin *ppinOut)
|
||||
{
|
||||
IFilterGraphImpl *This = impl_from_IFilterGraph2(iface);
|
||||
IEnumMediaTypes* penummt;
|
||||
struct filter *filter;
|
||||
AM_MEDIA_TYPE* mt;
|
||||
ULONG nbmt;
|
||||
IFilterGraphImpl *graph = impl_from_IFilterGraph2(iface);
|
||||
HRESULT hr;
|
||||
|
||||
IEnumMoniker* pEnumMoniker;
|
||||
GUID tab[4];
|
||||
ULONG nb;
|
||||
IMoniker* pMoniker;
|
||||
IFilterMapper2 *pFilterMapper2 = NULL;
|
||||
TRACE("graph %p, source %p.\n", graph, source);
|
||||
|
||||
TRACE("(%p/%p)->(%p)\n", This, iface, ppinOut);
|
||||
|
||||
if (TRACE_ON(quartz))
|
||||
{
|
||||
PIN_INFO PinInfo;
|
||||
|
||||
hr = IPin_QueryPinInfo(ppinOut, &PinInfo);
|
||||
if (FAILED(hr))
|
||||
return hr;
|
||||
|
||||
TRACE("Filter owning pin => %p\n", PinInfo.pFilter);
|
||||
IBaseFilter_Release(PinInfo.pFilter);
|
||||
}
|
||||
|
||||
/* Try to find out if there is a renderer for the specified subtype already, and use that
|
||||
*/
|
||||
EnterCriticalSection(&This->cs);
|
||||
LIST_FOR_EACH_ENTRY(filter, &This->filters, struct filter, entry)
|
||||
{
|
||||
IEnumPins *enumpins = NULL;
|
||||
IPin *pin = NULL;
|
||||
|
||||
hr = IBaseFilter_EnumPins(filter->filter, &enumpins);
|
||||
|
||||
if (FAILED(hr) || !enumpins)
|
||||
continue;
|
||||
|
||||
IEnumPins_Reset(enumpins);
|
||||
while (IEnumPins_Next(enumpins, 1, &pin, NULL) == S_OK)
|
||||
{
|
||||
IPin *to = NULL;
|
||||
PIN_DIRECTION dir = PINDIR_OUTPUT;
|
||||
|
||||
IPin_QueryDirection(pin, &dir);
|
||||
if (dir != PINDIR_INPUT)
|
||||
{
|
||||
IPin_Release(pin);
|
||||
continue;
|
||||
}
|
||||
IPin_ConnectedTo(pin, &to);
|
||||
|
||||
if (to == NULL)
|
||||
{
|
||||
hr = FilterGraph2_ConnectDirect(iface, ppinOut, pin, NULL);
|
||||
if (SUCCEEDED(hr))
|
||||
{
|
||||
TRACE("Connected successfully %p/%p, %08x look if we should render more!\n", ppinOut, pin, hr);
|
||||
IPin_Release(pin);
|
||||
|
||||
hr = render_output_pins(This, filter->filter);
|
||||
if (FAILED(hr))
|
||||
{
|
||||
IPin_Disconnect(ppinOut);
|
||||
IPin_Disconnect(pin);
|
||||
continue;
|
||||
}
|
||||
IEnumPins_Release(enumpins);
|
||||
LeaveCriticalSection(&This->cs);
|
||||
return hr;
|
||||
}
|
||||
WARN("Could not connect!\n");
|
||||
}
|
||||
else
|
||||
IPin_Release(to);
|
||||
|
||||
IPin_Release(pin);
|
||||
}
|
||||
IEnumPins_Release(enumpins);
|
||||
}
|
||||
|
||||
LeaveCriticalSection(&This->cs);
|
||||
|
||||
hr = IPin_EnumMediaTypes(ppinOut, &penummt);
|
||||
if (FAILED(hr)) {
|
||||
WARN("EnumMediaTypes (%x)\n", hr);
|
||||
return hr;
|
||||
}
|
||||
|
||||
IEnumMediaTypes_Reset(penummt);
|
||||
|
||||
/* Looks like no existing renderer of the kind exists
|
||||
* Try adding new ones
|
||||
*/
|
||||
tab[0] = tab[1] = GUID_NULL;
|
||||
while (SUCCEEDED(hr))
|
||||
{
|
||||
hr = IEnumMediaTypes_Next(penummt, 1, &mt, &nbmt);
|
||||
if (FAILED(hr)) {
|
||||
WARN("IEnumMediaTypes_Next (%x)\n", hr);
|
||||
break;
|
||||
}
|
||||
if (!nbmt)
|
||||
{
|
||||
EnterCriticalSection(&graph->cs);
|
||||
hr = autoplug(graph, source, NULL, 0);
|
||||
LeaveCriticalSection(&graph->cs);
|
||||
if (hr == VFW_E_CANNOT_CONNECT)
|
||||
hr = VFW_E_CANNOT_RENDER;
|
||||
break;
|
||||
}
|
||||
else
|
||||
{
|
||||
TRACE("MajorType %s\n", debugstr_guid(&mt->majortype));
|
||||
TRACE("SubType %s\n", debugstr_guid(&mt->subtype));
|
||||
|
||||
/* Only enumerate once, this doesn't account for all previous ones, but this should be enough nonetheless */
|
||||
if (IsEqualIID(&tab[0], &mt->majortype) && IsEqualIID(&tab[1], &mt->subtype))
|
||||
{
|
||||
DeleteMediaType(mt);
|
||||
continue;
|
||||
}
|
||||
|
||||
if (pFilterMapper2 == NULL)
|
||||
{
|
||||
hr = IUnknown_QueryInterface(This->punkFilterMapper2, &IID_IFilterMapper2, (void**)&pFilterMapper2);
|
||||
if (FAILED(hr))
|
||||
{
|
||||
WARN("Unable to query IFilterMapper2 (%x)\n", hr);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
/* Try to find a suitable renderer with the same media type */
|
||||
tab[0] = mt->majortype;
|
||||
tab[1] = mt->subtype;
|
||||
hr = IFilterMapper2_EnumMatchingFilters(pFilterMapper2, &pEnumMoniker, 0, FALSE, MERIT_UNLIKELY, TRUE, 1, tab, NULL, NULL, FALSE, FALSE, 0, NULL, NULL, NULL);
|
||||
if (FAILED(hr))
|
||||
{
|
||||
WARN("Unable to enum filters (%x)\n", hr);
|
||||
break;
|
||||
}
|
||||
}
|
||||
hr = E_FAIL;
|
||||
|
||||
while (IEnumMoniker_Next(pEnumMoniker, 1, &pMoniker, &nb) == S_OK)
|
||||
{
|
||||
VARIANT var;
|
||||
IPin* ppinfilter;
|
||||
IBaseFilter* pfilter = NULL;
|
||||
IEnumPins* penumpins = NULL;
|
||||
ULONG pin;
|
||||
|
||||
hr = GetFilterInfo(pMoniker, &var);
|
||||
if (FAILED(hr)) {
|
||||
WARN("Unable to retrieve filter info (%x)\n", hr);
|
||||
goto error;
|
||||
}
|
||||
|
||||
hr = create_filter(This, pMoniker, &pfilter);
|
||||
IMoniker_Release(pMoniker);
|
||||
if (FAILED(hr))
|
||||
{
|
||||
WARN("Unable to create filter (%x), trying next one\n", hr);
|
||||
goto error;
|
||||
}
|
||||
|
||||
hr = IFilterGraph2_AddFilter(iface, pfilter, V_BSTR(&var));
|
||||
if (FAILED(hr)) {
|
||||
WARN("Unable to add filter (%x)\n", hr);
|
||||
IBaseFilter_Release(pfilter);
|
||||
pfilter = NULL;
|
||||
goto error;
|
||||
}
|
||||
|
||||
hr = IBaseFilter_EnumPins(pfilter, &penumpins);
|
||||
if (FAILED(hr)) {
|
||||
WARN("Splitter Enumpins (%x)\n", hr);
|
||||
goto error;
|
||||
}
|
||||
|
||||
while ((hr = IEnumPins_Next(penumpins, 1, &ppinfilter, &pin)) == S_OK)
|
||||
{
|
||||
PIN_DIRECTION dir;
|
||||
|
||||
if (pin == 0) {
|
||||
WARN("No Pin\n");
|
||||
hr = E_FAIL;
|
||||
goto error;
|
||||
}
|
||||
|
||||
hr = IPin_QueryDirection(ppinfilter, &dir);
|
||||
if (FAILED(hr)) {
|
||||
IPin_Release(ppinfilter);
|
||||
WARN("QueryDirection failed (%x)\n", hr);
|
||||
goto error;
|
||||
}
|
||||
if (dir != PINDIR_INPUT) {
|
||||
IPin_Release(ppinfilter);
|
||||
continue; /* Wrong direction */
|
||||
}
|
||||
|
||||
/* Connect the pin to the "Renderer" */
|
||||
hr = IFilterGraph2_ConnectDirect(iface, ppinOut, ppinfilter, NULL);
|
||||
IPin_Release(ppinfilter);
|
||||
|
||||
if (FAILED(hr)) {
|
||||
WARN("Unable to connect %s to renderer (%x)\n", debugstr_w(V_BSTR(&var)), hr);
|
||||
goto error;
|
||||
}
|
||||
TRACE("Connected, recursing %s\n", debugstr_w(V_BSTR(&var)));
|
||||
|
||||
VariantClear(&var);
|
||||
|
||||
hr = render_output_pins(This, pfilter);
|
||||
if (FAILED(hr)) {
|
||||
WARN("Unable to connect recursively (%x)\n", hr);
|
||||
goto error;
|
||||
}
|
||||
IBaseFilter_Release(pfilter);
|
||||
break;
|
||||
}
|
||||
if (SUCCEEDED(hr)) {
|
||||
IEnumPins_Release(penumpins);
|
||||
break; /* out of IEnumMoniker_Next loop */
|
||||
}
|
||||
|
||||
/* IEnumPins_Next failed, all other failure case caught by goto error */
|
||||
WARN("IEnumPins_Next (%x)\n", hr);
|
||||
/* goto error */
|
||||
|
||||
error:
|
||||
VariantClear(&var);
|
||||
if (penumpins)
|
||||
IEnumPins_Release(penumpins);
|
||||
if (pfilter) {
|
||||
IFilterGraph2_RemoveFilter(iface, pfilter);
|
||||
IBaseFilter_Release(pfilter);
|
||||
}
|
||||
if (SUCCEEDED(hr)) DebugBreak();
|
||||
}
|
||||
|
||||
IEnumMoniker_Release(pEnumMoniker);
|
||||
if (nbmt)
|
||||
DeleteMediaType(mt);
|
||||
if (SUCCEEDED(hr))
|
||||
break;
|
||||
hr = S_OK;
|
||||
}
|
||||
|
||||
if (pFilterMapper2)
|
||||
IFilterMapper2_Release(pFilterMapper2);
|
||||
|
||||
IEnumMediaTypes_Release(penummt);
|
||||
TRACE("Returning %#x.\n", hr);
|
||||
return hr;
|
||||
}
|
||||
|
||||
|
@ -5755,7 +5404,6 @@ static HRESULT filter_graph_common_create(IUnknown *outer, IUnknown **out, BOOL
|
|||
memcpy(&fimpl->timeformatseek, &TIME_FORMAT_MEDIA_TIME, sizeof(GUID));
|
||||
fimpl->stream_start = fimpl->stream_elapsed = 0;
|
||||
fimpl->punkFilterMapper2 = NULL;
|
||||
fimpl->recursioncount = 0;
|
||||
fimpl->version = 0;
|
||||
fimpl->current_pos = 0;
|
||||
|
||||
|
|
|
@ -2110,7 +2110,7 @@ static void test_graph_builder_connect(void)
|
|||
{
|
||||
static const GUID parser1_clsid = {0x12345678};
|
||||
static const GUID parser2_clsid = {0x87654321};
|
||||
AM_MEDIA_TYPE source_type = {{0}}, sink_type = {{0}}, parser3_type = {{0}};
|
||||
AM_MEDIA_TYPE source_types[2] = {{{0}}}, sink_type = {{0}}, parser3_type = {{0}};
|
||||
struct testpin source_pin, sink_pin, sink2_pin, parser1_pins[3], parser2_pins[2], parser3_pins[2];
|
||||
struct testfilter source, sink, sink2, parser1, parser2, parser3;
|
||||
struct testfilter_cf parser1_cf = { {&testfilter_cf_vtbl}, &parser1 };
|
||||
|
@ -2125,10 +2125,11 @@ static void test_graph_builder_connect(void)
|
|||
HRESULT hr;
|
||||
ULONG ref;
|
||||
|
||||
memset(&source_type.majortype, 0xcc, sizeof(GUID));
|
||||
memset(&source_types[0].majortype, 0xcc, sizeof(GUID));
|
||||
memset(&source_types[1].majortype, 0xdd, sizeof(GUID));
|
||||
memset(&sink_type.majortype, 0x66, sizeof(GUID));
|
||||
testsource_init(&source_pin, &source_type, 1);
|
||||
source_pin.request_mt = &source_type;
|
||||
testsource_init(&source_pin, source_types, 2);
|
||||
source_pin.request_mt = &source_types[1];
|
||||
testfilter_init(&source, &source_pin, 1);
|
||||
testsink_init(&sink_pin);
|
||||
testfilter_init(&sink, &sink_pin, 1);
|
||||
|
@ -2398,7 +2399,7 @@ todo_wine
|
|||
regpins[1].cInstances = 1;
|
||||
regpins[1].nMediaTypes = 1;
|
||||
regpins[1].lpMediaType = ®types;
|
||||
regtypes.clsMajorType = &source_type.majortype;
|
||||
regtypes.clsMajorType = &source_types[1].majortype;
|
||||
regtypes.clsMinorType = &MEDIASUBTYPE_NULL;
|
||||
hr = IFilterMapper2_RegisterFilter(mapper, &parser1_clsid, L"test", NULL, NULL, NULL, ®filter);
|
||||
if (hr == E_ACCESSDENIED)
|
||||
|
|
Loading…
Reference in New Issue