[QUARTZ] Some more filtergraph methods implementations (RenderFile,
Render, Connect & AddSourceFilter)
Christian Costa
titan.costa at wanadoo.fr
Mon Nov 15 18:30:48 CST 2004
Hi,
Here is a patch that enables to build a filtergraph from a file.
The implementation is generic and use the filtermapper to find
registered filters.
Actually, this is enough to play an AVI file, although there is
certainly more work to do...
Changelog:
Implemented IGraphBuilder methods: Connect, Render, RenderFile &
AddSourceFilter.
Fixed filter name generation in IGraphBuilder::AddFilter.
Christian Costa titan.costa at wanadoo.fr
-------------- next part --------------
Index: filtergraph.c
===================================================================
RCS file: /home/wine/wine/dlls/quartz/filtergraph.c,v
retrieving revision 1.17
diff -u -r1.17 filtergraph.c
--- filtergraph.c 18 Oct 2004 21:21:19 -0000 1.17
+++ filtergraph.c 15 Nov 2004 21:59:49 -0000
@@ -31,12 +31,15 @@
#include "winuser.h"
#include "dshow.h"
#include "wine/debug.h"
+#include "quartz_private.h"
+#define COM_NO_WINDOWS_H
+#include "ole2.h"
+#include "olectl.h"
#include "strmif.h"
#include "vfwmsgs.h"
#include "evcode.h"
#include "wine/unicode.h"
-#include "quartz_private.h"
WINE_DEFAULT_DEBUG_CHANNEL(quartz);
@@ -163,6 +166,7 @@
/* IVideoFrameStep */
ULONG ref;
+ IFilterMapper2 * pFilterMapper2;
IBaseFilter ** ppFiltersInGraph;
LPWSTR * pFilterNames;
int nFilters;
@@ -238,6 +242,7 @@
ref = --This->ref;
if (ref == 0) {
+ IFilterMapper2_Release(This->pFilterMapper2);
CloseHandle(This->hEventCompletion);
EventsQueue_Destroy(&This->evqueue);
HeapFree(GetProcessHeap(), 0, This->ppFiltersInGraph);
@@ -316,7 +321,7 @@
/* Check if the generated name already exists */
for(i = 0; i < This->nFilters; i++)
- if (!strcmpW(This->pFilterNames[i], pName))
+ if (!strcmpW(This->pFilterNames[i], wszFilterName))
break;
/* Compute next index and exit if generated name is suitable */
@@ -335,7 +340,6 @@
else
memcpy(wszFilterName, pName, (strlenW(pName) + 1) * sizeof(WCHAR));
-
if (This->nFilters + 1 > This->filterCapacity)
{
int newCapacity = 2*This->filterCapacity;
@@ -485,23 +489,348 @@
return S_OK;
}
+static HRESULT GetFilterInfo(IMoniker* pMoniker, GUID* pclsid, VARIANT* pvar)
+{
+ static const WCHAR wszClsidName[] = {'C','L','S','I','D',0};
+ static const WCHAR wszFriendlyName[] = {'F','r','i','e','n','d','l','y','N','a','m','e',0};
+ IPropertyBag * pPropBagCat = NULL;
+ HRESULT hr;
+
+ VariantInit(pvar);
+ V_VT(pvar) = VT_BSTR;
+
+ hr = IMoniker_BindToStorage(pMoniker, NULL, NULL, &IID_IPropertyBag, (LPVOID*)&pPropBagCat);
+
+ if (SUCCEEDED(hr))
+ hr = IPropertyBag_Read(pPropBagCat, wszClsidName, pvar, NULL);
+
+ if (SUCCEEDED(hr))
+ hr = CLSIDFromString(V_UNION(pvar, bstrVal), pclsid);
+
+ if (SUCCEEDED(hr))
+ hr = IPropertyBag_Read(pPropBagCat, wszFriendlyName, pvar, NULL);
+
+ if (SUCCEEDED(hr))
+ TRACE("Moniker = %s - %s\n", debugstr_guid(pclsid), debugstr_w(V_UNION(pvar, bstrVal)));
+
+ if (pPropBagCat)
+ IPropertyBag_Release(pPropBagCat);
+
+ return hr;
+}
+
+static HRESULT GetInternalConnections(IBaseFilter* pfilter, IPin* poutputpin, IPin*** pppins, ULONG* pnb)
+{
+ HRESULT hr;
+ ULONG nb = 0;
+
+ TRACE("\n");
+ hr = IPin_QueryInternalConnections(poutputpin, NULL, &nb);
+ if (hr == S_OK) {
+ /* Rendered input */
+ } else if (hr == S_FALSE) {
+ *pppins = CoTaskMemAlloc(sizeof(IPin*)*nb);
+ hr = IPin_QueryInternalConnections(poutputpin, *pppins, &nb);
+ if (hr != S_OK) {
+ ERR("Error (%lx)\n", hr);
+ }
+ } else if (hr == E_NOTIMPL) {
+ /* Input connected to all outputs */
+ IEnumPins* penumpins;
+ IPin* ppin;
+ int i = 0;
+ TRACE("E_NOTIMPL\n");
+ hr = IBaseFilter_EnumPins(pfilter, &penumpins);
+ if (FAILED(hr)) {
+ ERR("filter Enumpins failed (%lx)\n", hr);
+ return hr;
+ }
+ i = 0;
+ /* Count output pins */
+ while(IEnumPins_Next(penumpins, 1, &ppin, &nb) == S_OK) {
+ PIN_DIRECTION pindir;
+ IPin_QueryDirection(ppin, &pindir);
+ if (pindir == PINDIR_OUTPUT)
+ i++;
+ else
+ IPin_Release(ppin);
+ }
+ *pppins = CoTaskMemAlloc(sizeof(IPin*)*i);
+ /* Retreive output pins */
+ IEnumPins_Reset(penumpins);
+ i = 0;
+ while(IEnumPins_Next(penumpins, 1, &ppin, &nb) == S_OK) {
+ PIN_DIRECTION pindir;
+ IPin_QueryDirection(ppin, &pindir);
+ if (pindir == PINDIR_OUTPUT)
+ (*pppins)[i++] = ppin;
+ else
+ IPin_Release(ppin);
+ }
+ nb = i;
+ if (FAILED(hr)) {
+ ERR("Next failed (%lx)\n", hr);
+ return hr;
+ }
+ IEnumPins_Release(penumpins);
+ } else if (FAILED(hr)) {
+ ERR("Cannot get internal connection (%lx)\n", hr);
+ return hr;
+ }
+
+ *pnb = nb;
+ return S_OK;
+}
+
/*** IGraphBuilder methods ***/
static HRESULT WINAPI Graphbuilder_Connect(IGraphBuilder *iface,
IPin *ppinOut,
IPin *ppinIn) {
ICOM_THIS_MULTI(IFilterGraphImpl, IGraphBuilder_vtbl, iface);
+ HRESULT hr;
+ AM_MEDIA_TYPE* mt;
+ IEnumMediaTypes* penummt;
+ ULONG nbmt;
+ IEnumPins* penumpins;
+ IEnumMoniker* pEnumMoniker;
+ GUID tab[2];
+ ULONG nb;
+ IMoniker* pMoniker;
+ ULONG pin;
+
+ TRACE("(%p/%p)->(%p, %p)\n", This, iface, ppinOut, ppinIn);
+
+ /* Try direct connection first */
+ TRACE("Try direct connection first\n");
+ hr = IPin_Connect(ppinOut, ppinIn, NULL);
+ if (SUCCEEDED(hr)) {
+ TRACE("Direct connection successfull\n");
+ return S_OK;
+ }
+ TRACE("Direct connection failed, trying to insert other filters\n");
- TRACE("(%p/%p)->(%p, %p): stub !!!\n", This, iface, ppinOut, ppinIn);
+ /* 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)) {
+ ERR("EnumMediaTypes (%lx)\n", hr);
+ return hr;
+ }
+
+ hr = IEnumMediaTypes_Next(penummt, 1, &mt, &nbmt);
+ if (FAILED(hr)) {
+ ERR("IEnumMediaTypes_Next (%lx)\n", hr);
+ return hr;
+ }
+
+ if (!nbmt) {
+ ERR("No media type found!\n");
+ return S_OK;
+ }
+ TRACE("MajorType %s\n", debugstr_guid(&mt->majortype));
+ TRACE("SubType %s\n", debugstr_guid(&mt->subtype));
+
+ /* 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(This->pFilterMapper2, &pEnumMoniker, 0, FALSE, 0, TRUE, 1, tab, NULL, NULL, FALSE, FALSE, 0, NULL, NULL, NULL);
+ if (FAILED(hr)) {
+ ERR("Unable to enum filters (%lx)\n", hr);
+ return hr;
+ }
+
+ while(IEnumMoniker_Next(pEnumMoniker, 1, &pMoniker, &nb) == S_OK)
+ {
+ VARIANT var;
+ GUID clsid;
+ IPin** ppins;
+ IPin* ppinfilter;
+ IBaseFilter* pfilter = NULL;
+
+ hr = GetFilterInfo(pMoniker, &clsid, &var);
+ IMoniker_Release(pMoniker);
+ if (FAILED(hr)) {
+ ERR("Unable to retreive filter info (%lx)\n", hr);
+ goto error;
+ }
+
+ hr = CoCreateInstance(&clsid, NULL, CLSCTX_INPROC_SERVER, &IID_IBaseFilter, (LPVOID*)&pfilter);
+ if (FAILED(hr)) {
+ ERR("Unable to create filter (%lx), trying next one\n", hr);
+ goto error;
+ }
+
+ hr = IGraphBuilder_AddFilter(iface, pfilter, NULL);
+ if (FAILED(hr)) {
+ ERR("Unable to add filter (%lx)\n", hr);
+ IBaseFilter_Release(pfilter);
+ pfilter = NULL;
+ goto error;
+ }
+
+ hr = IBaseFilter_EnumPins(pfilter, &penumpins);
+ if (FAILED(hr)) {
+ ERR("Enumpins (%lx)\n", hr);
+ goto error;
+ }
+ hr = IEnumPins_Next(penumpins, 1, &ppinfilter, &pin);
+ if (FAILED(hr)) {
+ ERR("Next (%lx)\n", hr);
+ goto error;
+ }
+ if (pin == 0) {
+ ERR("No Pin\n");
+ goto error;
+ }
+ IEnumPins_Release(penumpins);
+ hr = IPin_Connect(ppinOut, ppinfilter, NULL);
+ if (FAILED(hr)) {
+ TRACE("Cannot connect to filter (%lx), trying next one\n", hr);
+ goto error;
+ }
+ TRACE("Successfully connected to filter, follow chain...\n");
+
+ /* Render all output pins of the filter by calling IGraphBuilder_Render on each of them */
+ hr = GetInternalConnections(pfilter, ppinfilter, &ppins, &nb);
+
+ if (SUCCEEDED(hr)) {
+ int i;
+ TRACE("pins to consider: %ld\n", nb);
+ for(i = 0; i < nb; i++) {
+ TRACE("Processing pin %d\n", i);
+ hr = IGraphBuilder_Connect(iface, ppins[0], ppinIn);
+ if (FAILED(hr)) {
+ TRACE("Cannot render pin %p (%lx)\n", ppinfilter, hr);
+ return hr;
+ }
+ }
+ CoTaskMemFree(ppins);
+ }
+ break;
+
+error:
+ if (pfilter) {
+ IGraphBuilder_RemoveFilter(iface, pfilter);
+ IBaseFilter_Release(pfilter);
+ }
+ }
+
+ IEnumMediaTypes_Release(penummt);
+ DeleteMediaType(mt);
+
return S_OK;
}
static HRESULT WINAPI Graphbuilder_Render(IGraphBuilder *iface,
IPin *ppinOut) {
ICOM_THIS_MULTI(IFilterGraphImpl, IGraphBuilder_vtbl, iface);
+ IEnumMediaTypes* penummt;
+ AM_MEDIA_TYPE* mt;
+ ULONG nbmt;
+ HRESULT hr;
+
+ IEnumMoniker* pEnumMoniker;
+ GUID tab[2];
+ ULONG nb;
+ IMoniker* pMoniker;
+
+ TRACE("(%p/%p)->(%p)\n", This, iface, ppinOut);
+
+ hr = IPin_EnumMediaTypes(ppinOut, &penummt);
+ if (FAILED(hr)) {
+ ERR("EnumMediaTypes (%lx)\n", hr);
+ return hr;
+ }
- TRACE("(%p/%p)->(%p): stub !!!\n", This, iface, ppinOut);
+ while(1)
+ {
+ hr = IEnumMediaTypes_Next(penummt, 1, &mt, &nbmt);
+ if (FAILED(hr)) {
+ ERR("IEnumMediaTypes_Next (%lx)\n", hr);
+ return hr;
+ }
+ if (!nbmt)
+ break;
+ TRACE("MajorType %s\n", debugstr_guid(&mt->majortype));
+ TRACE("SubType %s\n", debugstr_guid(&mt->subtype));
+ /* Try to find a suitable renderer with the same media type */
+ tab[0] = mt->majortype;
+ tab[1] = GUID_NULL;
+ hr = IFilterMapper2_EnumMatchingFilters(This->pFilterMapper2, &pEnumMoniker, 0, FALSE, 0, TRUE, 1, tab, NULL, NULL, TRUE, FALSE, 0, NULL, NULL, NULL);
+ if (FAILED(hr)) {
+ ERR("Unable to enum filters (%lx)\n", hr);
+ return hr;
+ }
+
+ while(IEnumMoniker_Next(pEnumMoniker, 1, &pMoniker, &nb) == S_OK)
+ {
+ VARIANT var;
+ GUID clsid;
+ IPin* ppinfilter;
+ IBaseFilter* pfilter = NULL;
+ IEnumPins* penumpins;
+ ULONG pin;
+
+ hr = GetFilterInfo(pMoniker, &clsid, &var);
+ IMoniker_Release(pMoniker);
+ if (FAILED(hr)) {
+ ERR("Unable to retreive filter info (%lx)\n", hr);
+ goto error;
+ }
+
+ hr = CoCreateInstance(&clsid, NULL, CLSCTX_INPROC_SERVER, &IID_IBaseFilter, (LPVOID*)&pfilter);
+ if (FAILED(hr)) {
+ ERR("Unable to create filter (%lx), trying next one\n", hr);
+ goto error;
+ }
+
+ hr = IGraphBuilder_AddFilter(iface, pfilter, NULL);
+ if (FAILED(hr)) {
+ ERR("Unable to add filter (%lx)\n", hr);
+ IBaseFilter_Release(pfilter);
+ pfilter = NULL;
+ goto error;
+ }
+
+ hr = IBaseFilter_EnumPins(pfilter, &penumpins);
+ if (FAILED(hr)) {
+ ERR("Splitter Enumpins (%lx)\n", hr);
+ goto error;
+ }
+ hr = IEnumPins_Next(penumpins, 1, &ppinfilter, &pin);
+ if (FAILED(hr)) {
+ ERR("Next (%lx)\n", hr);
+ goto error;
+ }
+ if (pin == 0) {
+ ERR("No Pin\n");
+ goto error;
+ }
+ IEnumPins_Release(penumpins);
+
+ /* Connect the pin to render to the renderer */
+ hr = IGraphBuilder_Connect(iface, ppinOut, ppinfilter);
+ if (FAILED(hr)) {
+ TRACE("Unable to connect to renderer (%lx)\n", hr);
+ goto error;
+ }
+ break;
+
+error:
+ if (pfilter) {
+ IGraphBuilder_RemoveFilter(iface, pfilter);
+ IBaseFilter_Release(pfilter);
+ }
+ }
+
+ DeleteMediaType(mt);
+ break;
+ }
+
+ IEnumMediaTypes_Release(penummt);
+
return S_OK;
}
@@ -509,9 +838,134 @@
LPCWSTR lpcwstrFile,
LPCWSTR lpcwstrPlayList) {
ICOM_THIS_MULTI(IFilterGraphImpl, IGraphBuilder_vtbl, iface);
+ static const WCHAR string[] = {'R','e','a','d','e','r',0};
+ IBaseFilter* preader = NULL;
+ IBaseFilter* psplitter;
+ IPin* ppinreader;
+ IPin* ppinsplitter;
+ IEnumPins* penumpins;
+ ULONG pin;
+ HRESULT hr;
+ IEnumMoniker* pEnumMoniker;
+ GUID tab[2];
+ IPin** ppins;
+ ULONG nb;
+ IMoniker* pMoniker;
+ IFileSourceFilter* pfile = NULL;
+ AM_MEDIA_TYPE mt;
+ WCHAR* filename;
+
+ TRACE("(%p/%p)->(%s, %s)\n", This, iface, debugstr_w(lpcwstrFile), debugstr_w(lpcwstrPlayList));
+
+ hr = IGraphBuilder_AddSourceFilter(iface, lpcwstrFile, string, &preader);
- TRACE("(%p/%p)->(%s (%p), %s (%p)): stub !!!\n", This, iface, debugstr_w(lpcwstrFile), lpcwstrFile, debugstr_w(lpcwstrPlayList), lpcwstrPlayList);
+ /* Retreive file media type */
+ if (SUCCEEDED(hr))
+ hr = IBaseFilter_QueryInterface(preader, &IID_IFileSourceFilter, (LPVOID*)&pfile);
+ if (SUCCEEDED(hr)) {
+ hr = IFileSourceFilter_GetCurFile(pfile, &filename, &mt);
+ IFileSourceFilter_Release(pfile);
+ }
+
+ if (SUCCEEDED(hr)) {
+ tab[0] = mt.majortype;
+ tab[1] = mt.subtype;
+ hr = IFilterMapper2_EnumMatchingFilters(This->pFilterMapper2, &pEnumMoniker, 0, FALSE, 0, TRUE, 1, tab, NULL, NULL, FALSE, FALSE, 0, NULL, NULL, NULL);
+ } else {
+ if (preader) {
+ IGraphBuilder_RemoveFilter(iface, preader);
+ IBaseFilter_Release(preader);
+ }
+ return hr;
+ }
+
+ while(IEnumMoniker_Next(pEnumMoniker, 1, &pMoniker, &nb) == S_OK)
+ {
+ VARIANT var;
+ GUID clsid;
+
+ hr = GetFilterInfo(pMoniker, &clsid, &var);
+ IMoniker_Release(pMoniker);
+ if (FAILED(hr)) {
+ ERR("Unable to retreive filter info (%lx)\n", hr);
+ continue;
+ }
+
+ hr = CoCreateInstance(&clsid, NULL, CLSCTX_INPROC_SERVER, &IID_IBaseFilter, (LPVOID*)&psplitter);
+ if (FAILED(hr)) {
+ ERR("Unable to create filter (%lx), trying next one\n", hr);
+ continue;
+ }
+ hr = IGraphBuilder_AddFilter(iface, psplitter, NULL);
+ if (FAILED(hr)) {
+ ERR("Unable add filter (%lx)\n", hr);
+ return hr;
+ }
+
+ /* Connect file source and splitter filters together */
+ /* Make the splitter analyze incoming data */
+ hr = IBaseFilter_EnumPins(preader, &penumpins);
+ if (FAILED(hr)) {
+ ERR("Enumpins (%lx)\n", hr);
+ return hr;
+ }
+ hr = IEnumPins_Next(penumpins, 1, &ppinreader, &pin);
+ if (FAILED(hr)) {
+ ERR("Next (%lx)\n", hr);
+ return hr;
+ }
+ if (pin == 0) {
+ ERR("No Pin\n");
+ return E_FAIL;
+ }
+ IEnumPins_Release(penumpins);
+
+ hr = IBaseFilter_EnumPins(psplitter, &penumpins);
+ if (FAILED(hr)) {
+ ERR("Splitter Enumpins (%lx)\n", hr);
+ return hr;
+ }
+ hr = IEnumPins_Next(penumpins, 1, &ppinsplitter, &pin);
+ if (FAILED(hr)) {
+ ERR("Next (%lx)\n", hr);
+ return hr;
+ }
+ if (pin == 0) {
+ ERR("No Pin\n");
+ return E_FAIL;
+ }
+ IEnumPins_Release(penumpins);
+
+ hr = IPin_Connect(ppinreader, ppinsplitter, NULL);
+ if (FAILED(hr)) {
+ IBaseFilter_Release(ppinsplitter);
+ ppinsplitter = NULL;
+ TRACE("Cannot connect to filter (%lx), trying next one\n", hr);
+ break;
+ }
+ TRACE("Successfully connected to filter\n");
+ break;
+ }
+
+ /* Render all output pin of the splitter by calling IGraphBuilder_Render on each of them */
+ hr = GetInternalConnections(psplitter, ppinsplitter, &ppins, &nb);
+
+ if (SUCCEEDED(hr)) {
+ int i;
+ TRACE("pins to consider: %ld\n", nb);
+ for(i = 0; i < nb; i++) {
+ TRACE("Processing pin %d\n", i);
+ hr = IGraphBuilder_Render(iface, ppins[i]);
+ if (FAILED(hr)) {
+ ERR("Cannot render pin %p (%lx)\n", ppins[i], hr);
+ /* FIXME: We should clean created things properly */
+ break;
+ }
+ }
+ }
+ CoTaskMemFree(ppins);
+
return S_OK;
}
@@ -520,9 +974,61 @@
LPCWSTR lpcwstrFilterName,
IBaseFilter **ppFilter) {
ICOM_THIS_MULTI(IFilterGraphImpl, IGraphBuilder_vtbl, iface);
+ HRESULT hr;
+ IBaseFilter* preader;
+ IFileSourceFilter* pfile = NULL;
+ AM_MEDIA_TYPE mt;
+ WCHAR* filename;
+
+ TRACE("(%p/%p)->(%s, %s, %p)\n", This, iface, debugstr_w(lpcwstrFileName), debugstr_w(lpcwstrFilterName), ppFilter);
+
+ /* Instantiate a file source filter */
+ hr = CoCreateInstance(&CLSID_AsyncReader, NULL, CLSCTX_INPROC_SERVER, &IID_IBaseFilter, (LPVOID*)&preader);
+ if (FAILED(hr)) {
+ ERR("Unable to create file source filter (%lx)\n", hr);
+ return hr;
+ }
- TRACE("(%p/%p)->(%s (%p), %s (%p), %p): stub !!!\n", This, iface, debugstr_w(lpcwstrFileName), lpcwstrFileName, debugstr_w(lpcwstrFilterName), lpcwstrFilterName, ppFilter);
+ hr = IGraphBuilder_AddFilter(iface, preader, lpcwstrFilterName);
+ if (FAILED(hr)) {
+ ERR("Unable add filter (%lx)\n", hr);
+ IBaseFilter_Release(preader);
+ return hr;
+ }
+ hr = IBaseFilter_QueryInterface(preader, &IID_IFileSourceFilter, (LPVOID*)&pfile);
+ if (FAILED(hr)) {
+ ERR("Unable to get IFileSourceInterface (%lx)\n", hr);
+ goto error;
+ }
+
+ /* Load the file in the file source filter */
+ hr = IFileSourceFilter_Load(pfile, lpcwstrFileName, NULL);
+ if (FAILED(hr)) {
+ ERR("Load (%lx)\n", hr);
+ goto error;
+ }
+
+ IFileSourceFilter_GetCurFile(pfile, &filename, &mt);
+ if (FAILED(hr)) {
+ ERR("GetCurFile (%lx)\n", hr);
+ goto error;
+ }
+ TRACE("File %s\n", debugstr_w(filename));
+ TRACE("MajorType %s\n", debugstr_guid(&mt.majortype));
+ TRACE("SubType %s\n", debugstr_guid(&mt.subtype));
+
+ if (ppFilter)
+ *ppFilter = preader;
+
+ return S_OK;
+
+error:
+ if (pfile)
+ IFileSourceFilter_Release(pfile);
+ IGraphBuilder_RemoveFilter(iface, preader);
+ IBaseFilter_Release(preader);
+
return S_OK;
}
@@ -2413,6 +2919,7 @@
/* This is the only function that actually creates a FilterGraph class... */
HRESULT FILTERGRAPH_create(IUnknown *pUnkOuter, LPVOID *ppObj) {
IFilterGraphImpl *fimpl;
+ HRESULT hr;
TRACE("(%p,%p)\n", pUnkOuter, ppObj);
@@ -2443,6 +2950,12 @@
fimpl->nRenderers = 0;
fimpl->EcCompleteCount = 0;
EventsQueue_Init(&fimpl->evqueue);
+
+ hr = CoCreateInstance(&CLSID_FilterMapper, NULL, CLSCTX_INPROC_SERVER, &IID_IFilterMapper2, (LPVOID*)&fimpl->pFilterMapper2);
+ if (FAILED(hr)) {
+ ERR("Unable to create filter mapper (%lx)\n", hr);
+ return hr;
+ }
*ppObj = fimpl;
return S_OK;
More information about the wine-patches
mailing list