I thought I'd move it around a little and put it in qcap, where I
*think* it should reside, I know CaptureGraphBuilder has to, so I moved
it to
I had to copy some files from quartz for it though
media.c is enummedia.c in quartz
pin.c is a diet version of quartz' pin.c
regsvr.c is meant to register the vfwcapture and the capturegraphbuilder
class, also copied from quartz
qcap_main.c is basically a modified version of quartz' main.c
vfwcapture related files:
vfwcapture.c - front-end com interface
v4l.c - Does the magic between /dev/video and vfwcapture
null.c - NULL renderer, in case v4l isn't available the functions get
enabled
capture.h - headers that are used for null.c/v4l.c/vfwcapture.c
Current todo list:
- Implement YUV formats (If anyone has some documentation on it I would
be grateful, if you want to write the renderers yourself (Should be
easy) I would be eternal grateful)
- Add resizing (Help me!!! Fix my Resize function (24 bits to 24 bits)
and send the results to me), currently resizing (Clipping and flipping
image horizontal) should crash when format < 320x240
- Find a way for devenum to keep /dev/video* apart (I have a vague idea
on how to do it)
- Fix Capture_SetMediaType function to try to adjust size
I'll do the last thing of my TODO list myself, but if you're interested
in helping you're free to do so :)
If your webcam isn't detected in msn messenger there could be several
reasons:
1. Make sure you use the BUILTIN devenum,quartz and qcap
2. Make sure you regsvr32'd the *BUILTIN* qcap and quartz (regsvr32 qcap)
3. Delete HKEY_CURRENT_USER/Software/Microsoft/ActiveMovie
4. Check permissions for /dev/video*
5. Adjust #define VIDEODEVICE (in dlls/qcap/vfwcapture.c) to get the
right video device..
6. Try starting up msn with WINEDEBUG=-all,+qcap_v4l and check for
output, especially the ERR lines
You could get something like:
err:qcap_v4l:Capture_Initialise /dev/video1: (Some explanation why the
initialisation failed here)
in that case it should be obvious why it wouldn't work and how you can
fix it :)
If it can't render your webcam, write a renderer for it and give me a
copy of it.
If it your image gets clipped, dig into msdn and write a proper resize
function and pass me a copy.
If you're too lazy to apply my quartz fixes patch, native quartz will
work too.. (I tested it).. but I offer no guarantees on it..
If something else happens, tell me the results, and I'll look at it..
(If not covered by the above ;))
At this point, it is not recommended to use this in msn but you're free
to try, you will need a native msvfw32.dll, because a function msn uses
is not implemented in the builtin
This patch will only work for wine-cvs, since it uses some patches that
are for now only in the wine cvs tree
Devices I tested this on:
A creative usb webcam (zc030x driver), images looked distorted because
of driver issues, not because of bugs on my side
bttv, worked perfect, because for some reason it initialised with a
resolution of 320x240, so I didn't need to touch it
Index: dlls/quartz/enummedia.c
===================================================================
RCS file: /home/wine/wine/dlls/quartz/enummedia.c,v
retrieving revision 1.5
diff -u -p -r1.5 enummedia.c
--- dlls/quartz/enummedia.c 6 Jan 2005 19:36:47 -0000 1.5
+++ dlls/quartz/enummedia.c 1 May 2005 00:10:56 -0000
@@ -27,6 +27,7 @@ WINE_DEFAULT_DEBUG_CHANNEL(quartz);
HRESULT CopyMediaType(AM_MEDIA_TYPE * pDest, const AM_MEDIA_TYPE *pSrc)
{
memcpy(pDest, pSrc, sizeof(AM_MEDIA_TYPE));
+ if (!pSrc->pbFormat) return S_OK;
if (!(pDest->pbFormat = CoTaskMemAlloc(pSrc->cbFormat)))
return E_OUTOFMEMORY;
memcpy(pDest->pbFormat, pSrc->pbFormat, pSrc->cbFormat);
@@ -45,6 +46,7 @@ void DeleteMediaType(AM_MEDIA_TYPE * pMe
IUnknown_Release(pMediaType->pUnk);
pMediaType->pUnk = NULL;
}
+ CoTaskMemFree(pMediaType);
}
BOOL CompareMediaTypes(const AM_MEDIA_TYPE * pmt1, const AM_MEDIA_TYPE * pmt2, BOOL bWildcards)
@@ -90,7 +92,11 @@ HRESULT IEnumMediaTypesImpl_Construct(co
pEnumMediaTypes->enumMediaDetails.cMediaTypes = pDetails->cMediaTypes;
pEnumMediaTypes->enumMediaDetails.pMediaTypes = CoTaskMemAlloc(sizeof(AM_MEDIA_TYPE) * pDetails->cMediaTypes);
for (i = 0; i < pDetails->cMediaTypes; i++)
- pEnumMediaTypes->enumMediaDetails.pMediaTypes[i] = pDetails->pMediaTypes[i];
+ if (FAILED(CopyMediaType(&pEnumMediaTypes->enumMediaDetails.pMediaTypes[i], &pDetails->pMediaTypes[i]))) {
+ while (--i > 0) CoTaskMemFree(pEnumMediaTypes->enumMediaDetails.pMediaTypes[i].pbFormat);
+ CoTaskMemFree(pEnumMediaTypes->enumMediaDetails.pMediaTypes);
+ return E_OUTOFMEMORY;
+ }
*ppEnum = (IEnumMediaTypes *)(&pEnumMediaTypes->lpVtbl);
return S_OK;
}
@@ -136,12 +142,14 @@ static ULONG WINAPI IEnumMediaTypesImpl_
if (!refCount)
{
+ int i;
+ for (i = 0; i < This->enumMediaDetails.cMediaTypes; i++)
+ if (This->enumMediaDetails.pMediaTypes[i].pbFormat)
+ CoTaskMemFree(This->enumMediaDetails.pMediaTypes[i].pbFormat);
CoTaskMemFree(This->enumMediaDetails.pMediaTypes);
CoTaskMemFree(This);
- return 0;
}
- else
- return refCount;
+ return refCount;
}
static HRESULT WINAPI IEnumMediaTypesImpl_Next(IEnumMediaTypes * iface, ULONG cMediaTypes, AM_MEDIA_TYPE ** ppMediaTypes, ULONG * pcFetched)
@@ -159,7 +167,13 @@ static HRESULT WINAPI IEnumMediaTypesImp
ULONG i;
*ppMediaTypes = CoTaskMemAlloc(sizeof(AM_MEDIA_TYPE) * cFetched);
for (i = 0; i < cFetched; i++)
- (*ppMediaTypes)[i] = This->enumMediaDetails.pMediaTypes[This->uIndex + i];
+ if (FAILED(CopyMediaType(&(*ppMediaTypes)[i], &This->enumMediaDetails.pMediaTypes[This->uIndex + i]))) {
+ while (--i)
+ CoTaskMemFree((*ppMediaTypes)[i].pbFormat);
+ CoTaskMemFree(*ppMediaTypes);
+ *ppMediaTypes = NULL;
+ return E_OUTOFMEMORY;
+ }
}
if ((cMediaTypes != 1) || pcFetched)
Index: dlls/quartz/filtergraph.c
===================================================================
RCS file: /home/wine/wine/dlls/quartz/filtergraph.c,v
retrieving revision 1.27
diff -u -p -r1.27 filtergraph.c
--- dlls/quartz/filtergraph.c 24 Mar 2005 21:01:37 -0000 1.27
+++ dlls/quartz/filtergraph.c 1 May 2005 00:10:58 -0000
@@ -245,6 +245,9 @@ static ULONG Filtergraph_Release(IFilter
TRACE("(%p)->(): new ref = %ld\n", This, ref);
if (ref == 0) {
+ int i;
+ for (i = 0; i < This->nFilters; i++)
+ IBaseFilter_Release(This->ppFiltersInGraph[i]);
IFilterMapper2_Release(This->pFilterMapper2);
CloseHandle(This->hEventCompletion);
EventsQueue_Destroy(&This->evqueue);
@@ -556,8 +559,7 @@ static HRESULT GetInternalConnections(IB
IPin_QueryDirection(ppin, &pindir);
if (pindir == PINDIR_OUTPUT)
i++;
- else
- IPin_Release(ppin);
+ IPin_Release(ppin);
}
*pppins = CoTaskMemAlloc(sizeof(IPin*)*i);
/* Retrieve output pins */
@@ -605,10 +607,8 @@ static HRESULT WINAPI Graphbuilder_Conne
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 successful\n");
return S_OK;
}
TRACE("Direct connection failed, trying to insert other filters\n");
@@ -648,7 +648,7 @@ static HRESULT WINAPI Graphbuilder_Conne
VARIANT var;
GUID clsid;
IPin** ppins;
- IPin* ppinfilter;
+ IPin* ppinfilter = NULL;
IBaseFilter* pfilter = NULL;
hr = GetFilterInfo(pMoniker, &clsid, &var);
@@ -677,6 +677,7 @@ static HRESULT WINAPI Graphbuilder_Conne
ERR("Enumpins (%lx)\n", hr);
goto error;
}
+
hr = IEnumPins_Next(penumpins, 1, &ppinfilter, &pin);
if (FAILED(hr)) {
ERR("Next (%lx)\n", hr);
@@ -703,17 +704,22 @@ static HRESULT WINAPI Graphbuilder_Conne
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);
+ hr = IGraphBuilder_Connect(iface, ppins[i], ppinIn);
if (FAILED(hr)) {
- TRACE("Cannot render pin %p (%lx)\n", ppinfilter, hr);
- return hr;
+ TRACE("Cannot render pin %p (%lx)\n", ppinfilter, hr);
}
+ IPin_Release(ppins[i]);
+ if (SUCCEEDED(hr)) break;
}
+ while (++i < nb) IPin_Release(ppins[i]);
CoTaskMemFree(ppins);
+ IBaseFilter_Release(pfilter);
+ IPin_Release(ppinfilter);
+ break;
}
- break;
error:
+ if (ppinfilter) IPin_Release(ppinfilter);
if (pfilter) {
IGraphBuilder_RemoveFilter(iface, pfilter);
IBaseFilter_Release(pfilter);
@@ -1167,7 +1173,9 @@ static HRESULT WINAPI Mediacontrol_Invok
return S_OK;
}
-static HRESULT ExploreGraph(IFilterGraphImpl* pGraph, IPin* pOutputPin, REFERENCE_TIME tStart)
+typedef HRESULT(WINAPI *fnFoundFilter)(IBaseFilter *);
+
+static HRESULT ExploreGraph(IFilterGraphImpl* pGraph, IPin* pOutputPin, fnFoundFilter FoundFilter)
{
HRESULT hr;
IPin* pInputPin;
@@ -1176,7 +1184,8 @@ static HRESULT ExploreGraph(IFilterGraph
ULONG i;
PIN_INFO PinInfo;
- TRACE("%p %p %lld\n", pGraph, pOutputPin, tStart);
+ TRACE("%p %p\n", pGraph, pOutputPin);
+ PinInfo.pFilter = NULL;
hr = IPin_ConnectedTo(pOutputPin, &pInputPin);
@@ -1201,20 +1210,33 @@ static HRESULT ExploreGraph(IFilterGraph
/* Explore the graph downstream from this pin
* FIXME: We should prevent exploring from a pin more than once. This can happens when
* several input pins are connected to the same output (a MUX for instance). */
- ExploreGraph(pGraph, ppPins[i], tStart);
+ ExploreGraph(pGraph, ppPins[i], FoundFilter);
+ IPin_Release(ppPins[i]);
}
CoTaskMemFree(ppPins);
}
- TRACE("Run filter %p\n", PinInfo.pFilter);
- IBaseFilter_Run(PinInfo.pFilter, tStart);
+ TRACE("Doing stuff with filter %p\n", PinInfo.pFilter);
+ FoundFilter(PinInfo.pFilter);
}
+ if (PinInfo.pFilter) IBaseFilter_Release(PinInfo.pFilter);
return hr;
}
-/*** IMediaControl methods ***/
-static HRESULT WINAPI Mediacontrol_Run(IMediaControl *iface) {
+static HRESULT WINAPI SendRun(IBaseFilter *pFilter) {
+ return IBaseFilter_Run(pFilter, 0);
+}
+
+static HRESULT WINAPI SendPause(IBaseFilter *pFilter) {
+ return IBaseFilter_Pause(pFilter);
+}
+
+static HRESULT WINAPI SendStop(IBaseFilter *pFilter) {
+ return IBaseFilter_Stop(pFilter);
+}
+
+static HRESULT WINAPI SendTheFilterAMessage(IMediaControl *iface, fnFoundFilter FoundFilter) {
ICOM_THIS_MULTI(IFilterGraphImpl, IMediaControl_vtbl, iface);
int i;
IBaseFilter* pfilter;
@@ -1223,19 +1245,10 @@ static HRESULT WINAPI Mediacontrol_Run(I
IPin* pPin;
LONG dummy;
PIN_DIRECTION dir;
-
TRACE("(%p/%p)->()\n", This, iface);
- EnterCriticalSection(&This->cs);
-
- if (This->state == State_Running)
- {
- LeaveCriticalSection(&This->cs);
- return S_OK;
- }
-
- /* Explorer the graph from source filters to renderers, determine renderers number and
- * run filters from renderers to source filters */
+ /* Explorer the graph from source filters to renderers, determine renderers
+ * number and run filters from renderers to source filters */
This->nRenderers = 0;
ResetEvent(This->hEventCompletion);
@@ -1253,6 +1266,7 @@ static HRESULT WINAPI Mediacontrol_Run(I
while(IEnumPins_Next(pEnum, 1, &pPin, &dummy) == S_OK)
{
IPin_QueryDirection(pPin, &dir);
+ IPin_Release(pPin);
if (dir == PINDIR_INPUT)
{
source = FALSE;
@@ -1266,34 +1280,56 @@ static HRESULT WINAPI Mediacontrol_Run(I
while(IEnumPins_Next(pEnum, 1, &pPin, &dummy) == S_OK)
{
/* Explore the graph downstream from this pin */
- ExploreGraph(This, pPin, 0);
+ ExploreGraph(This, pPin, FoundFilter);
+ IPin_Release(pPin);
}
- IBaseFilter_Run(pfilter, 0);
+ FoundFilter(pfilter);
}
IEnumPins_Release(pEnum);
}
- This->state = State_Running;
+ return S_FALSE;
+}
+
+/*** IMediaControl methods ***/
+static HRESULT WINAPI Mediacontrol_Run(IMediaControl *iface) {
+ ICOM_THIS_MULTI(IFilterGraphImpl, IMediaControl_vtbl, iface);
+ TRACE("(%p/%p)->()\n", This, iface);
+
+ if (This->state == State_Running) return S_OK;
+ EnterCriticalSection(&This->cs);
+ SendTheFilterAMessage(iface, SendRun);
+ This->state = State_Running;
LeaveCriticalSection(&This->cs);
-
return S_FALSE;
}
static HRESULT WINAPI Mediacontrol_Pause(IMediaControl *iface) {
ICOM_THIS_MULTI(IFilterGraphImpl, IMediaControl_vtbl, iface);
+ TRACE("(%p/%p)->()\n", This, iface);
- TRACE("(%p/%p)->(): stub !!!\n", This, iface);
+ if (This->state == State_Paused) return S_OK;
- return S_OK;
+ EnterCriticalSection(&This->cs);
+ SendTheFilterAMessage(iface, SendPause);
+ This->state = State_Paused;
+ LeaveCriticalSection(&This->cs);
+ return S_FALSE;
}
static HRESULT WINAPI Mediacontrol_Stop(IMediaControl *iface) {
ICOM_THIS_MULTI(IFilterGraphImpl, IMediaControl_vtbl, iface);
+ TRACE("(%p/%p)->()\n", This, iface);
- TRACE("(%p/%p)->(): stub !!!\n", This, iface);
+ if (This->state == State_Stopped) return S_OK;
- return S_OK;
+ EnterCriticalSection(&This->cs);
+ if (This->state == State_Running) SendTheFilterAMessage(iface, SendPause);
+ SendTheFilterAMessage(iface, SendStop);
+ This->state = State_Stopped;
+ LeaveCriticalSection(&This->cs);
+ return S_FALSE;
}
static HRESULT WINAPI Mediacontrol_GetState(IMediaControl *iface,
Index: dlls/quartz/pin.c
===================================================================
RCS file: /home/wine/wine/dlls/quartz/pin.c,v
retrieving revision 1.13
diff -u -p -r1.13 pin.c
--- dlls/quartz/pin.c 2 Mar 2005 10:12:12 -0000 1.13
+++ dlls/quartz/pin.c 1 May 2005 00:10:59 -0000
@@ -49,7 +49,6 @@ static void Copy_PinInfo(PIN_INFO * pDes
strcpyW(pDest->achName, pSrc->achName);
pDest->dir = pSrc->dir;
pDest->pFilter = pSrc->pFilter;
- IBaseFilter_AddRef(pDest->pFilter);
}
/* Function called as a helper to IPin_Connect */
@@ -311,6 +310,7 @@ HRESULT WINAPI IPinImpl_QueryPinInfo(IPi
TRACE("(%p/%p)->(%p)\n", This, iface, pInfo);
Copy_PinInfo(pInfo, &This->pinInfo);
+ IBaseFilter_AddRef(pInfo->pFilter);
return S_OK;
}
diff -Nru wine-old/dlls/qcap/capturegraph.c wine-new/dlls/qcap/capturegraph.c
--- wine-old/dlls/qcap/capturegraph.c 1970-01-01 01:00:00.000000000 +0100
+++ wine-new/dlls/qcap/capturegraph.c 2005-04-29 00:31:34.000000000 +0200
@@ -0,0 +1,196 @@
+/* Capture Graph Builder, Minimal edition..
+ * Copyright 2005 Maarten Lankhorst
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2.1 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ *
+ * TODO: Implement CaptureGraphBuilder2, and make all calls from
+ * CaptureGraphBuilder forward to it..
+ */
+
+#include "config.h"
+
+#define NONAMELESSSTRUCT
+#define NONAMELESSUNION
+#include "qcap_private.h"
+
+#include "uuids.h"
+#include "mmreg.h"
+#include "vfwmsgs.h"
+#include "amvideo.h"
+#include "windef.h"
+#include "winbase.h"
+#include "dshow.h"
+#include "evcode.h"
+#include "strmif.h"
+#include "ddraw.h"
+
+#include "wine/unicode.h"
+#include "wine/debug.h"
+
+WINE_DEFAULT_DEBUG_CHANNEL(qcap);
+
+static const ICaptureGraphBuilderVtbl Builder_Vtbl;
+
+typedef struct CaptureGraphImpl
+{
+ const ICaptureGraphBuilderVtbl * lpVtbl;
+ IGraphBuilder *mygraph;
+
+ ULONG refCount;
+ CRITICAL_SECTION csFilter;
+} CaptureGraphImpl;
+
+HRESULT CaptureGraphBuilder_create(IUnknown * pUnkOuter, LPVOID * ppv)
+{
+ CaptureGraphImpl * pCapture;
+ TRACE("(%p, %p)\n", pUnkOuter, ppv);
+ *ppv = NULL;
+ if (pUnkOuter) return CLASS_E_NOAGGREGATION;
+ pCapture = CoTaskMemAlloc(sizeof(CaptureGraphImpl));
+ pCapture->lpVtbl = &Builder_Vtbl;
+ pCapture->refCount = 1;
+ pCapture->mygraph = NULL;
+ InitializeCriticalSection(&pCapture->csFilter);
+ *ppv = (LPVOID)pCapture;
+ return S_OK;
+}
+
+static HRESULT WINAPI CaptureGraphBuilder_QueryInterface(ICaptureGraphBuilder * iface, REFIID riid, LPVOID * ppv)
+{
+ struct CaptureGraphImpl *This = (CaptureGraphImpl *)iface;
+ TRACE("(%p/%p)->(%s, %p)\n", This, iface, qcdebugstr_guid(riid), ppv);
+ *ppv = NULL;
+ if (IsEqualIID(riid, &IID_IUnknown))
+ *ppv = (LPVOID)This;
+ else if (IsEqualIID(riid, &IID_ICaptureGraphBuilder))
+ *ppv = (LPVOID)This;
+
+ if (*ppv)
+ {
+ IUnknown_AddRef((IUnknown *)(*ppv));
+ return S_OK;
+ }
+
+ FIXME("No interface for %s!\n", qcdebugstr_guid(riid));
+ return E_NOINTERFACE;
+}
+
+static ULONG WINAPI CaptureGraphBuilder_AddRef(ICaptureGraphBuilder * iface)
+{
+ CaptureGraphImpl *This = (CaptureGraphImpl *)iface;
+ ULONG refCount = InterlockedIncrement(&This->refCount);
+
+ TRACE("(%p/%p)->() AddRef from %ld\n", This, iface, refCount - 1);
+ return refCount;
+}
+
+static ULONG WINAPI CaptureGraphBuilder_Release(ICaptureGraphBuilder * iface)
+{
+ CaptureGraphImpl *This = (CaptureGraphImpl *)iface;
+ ULONG refCount = InterlockedDecrement(&This->refCount);
+
+ TRACE("(%p/%p)->() Release from %ld\n", This, iface, refCount + 1);
+
+ if (!refCount) {
+ TRACE("Destroying everything..\n");
+ DeleteCriticalSection(&This->csFilter);
+ if (This->mygraph != NULL)
+ IGraphBuilder_Release((IGraphBuilder *)This->mygraph);
+ CoTaskMemFree(This);
+ return 0;
+ }
+ else return refCount;
+}
+
+static HRESULT WINAPI CaptureGraphBuilder_SetFilterGraph(ICaptureGraphBuilder * iface, IGraphBuilder *pfg)
+{
+/* The graph builder will automatically create a filter graph if you don't call this method. If you call this method after the graph builder has created its own filter graph, the call will fail. */
+ struct CaptureGraphImpl *This = (CaptureGraphImpl *)iface;
+ if (This->mygraph != NULL) return E_NOTIMPL;
+ This->mygraph = pfg;
+ IGraphBuilder_AddRef((IGraphBuilder *)This->mygraph);
+ TRACE("%p: %p\n", iface, pfg);
+ return S_OK;
+}
+
+static HRESULT WINAPI CaptureGraphBuilder_GetFilterGraph(ICaptureGraphBuilder * iface, IGraphBuilder **pfg)
+{
+ struct CaptureGraphImpl *This = (CaptureGraphImpl *)iface;
+ FIXME("%p: Make our own filtergraph if we haven't got one already - stub\n", iface);
+ if (This->mygraph == NULL) return E_NOTIMPL;
+
+ *pfg = This->mygraph;
+ IGraphBuilder_AddRef((IGraphBuilder *)This->mygraph);
+
+ TRACE("%p: %p\n", iface, *pfg);
+ return S_OK;
+}
+
+static HRESULT WINAPI CaptureGraphBuilder_SetOutputFileName(ICaptureGraphBuilder * iface, const GUID *pType, LPCOLESTR lpstrFile,IBaseFilter **ppf, IFileSinkFilter **ppSink)
+{
+ FIXME("%p: %p, %p, %p, %p - stub\n", iface, pType, lpstrFile, *ppf, *ppSink);
+ return E_NOTIMPL;
+}
+
+static HRESULT WINAPI CaptureGraphBuilder_FindInterface(ICaptureGraphBuilder * iface, const GUID *pCategory, IBaseFilter *pf, REFIID riid, void **ppint)
+{
+ struct CaptureGraphImpl *This = (CaptureGraphImpl *)iface;
+ FIXME("%p: %s %p %s %p - unwanted partial stub!\n", This, qcdebugstr_guid(pCategory), pf, qcdebugstr_guid(riid), *ppint);
+ return IBaseFilter_QueryInterface(pf, riid, ppint);
+ /* Looks for the specified interface on the filter, upstream and
+ * downstream from the filter, and, optionally, only on the output
+ * pin of the given category.
+ */
+}
+
+static HRESULT WINAPI CaptureGraphBuilder_RenderStream(ICaptureGraphBuilder * iface, const GUID *pCategory, IUnknown *pSource, IBaseFilter *pfCompressor, IBaseFilter *pfRenderer)
+{
+ FIXME("%p: stub\n", iface);
+ return E_NOTIMPL;
+}
+
+static HRESULT WINAPI CaptureGraphBuilder_ControlStream(ICaptureGraphBuilder * iface, const GUID *pCategory, IBaseFilter *pFilter, REFERENCE_TIME *pstart, REFERENCE_TIME *pstop, WORD wStartCookie, WORD wStopCookie)
+{
+ FIXME("%p: stub\n", iface);
+ return E_NOTIMPL;
+}
+
+static HRESULT WINAPI CaptureGraphBuilder_AllocCapFile(ICaptureGraphBuilder * iface, LPCOLESTR lpstr, DWORDLONG dwlSize)
+{
+ FIXME("%p: stub\n", iface);
+ return E_NOTIMPL;
+}
+
+static HRESULT WINAPI CaptureGraphBuilder_CopyCaptureFile(ICaptureGraphBuilder * iface, LPOLESTR lpwstrOld, LPOLESTR lpwstrNew, int fAllowEscAbort, IAMCopyCaptureFileProgress *pCallback)
+{
+ FIXME("%p: stub\n", iface);
+ return E_NOTIMPL;
+}
+
+static const ICaptureGraphBuilderVtbl Builder_Vtbl =
+{
+ CaptureGraphBuilder_QueryInterface,
+ CaptureGraphBuilder_AddRef,
+ CaptureGraphBuilder_Release,
+ CaptureGraphBuilder_SetFilterGraph,
+ CaptureGraphBuilder_GetFilterGraph,
+ CaptureGraphBuilder_SetOutputFileName,
+ CaptureGraphBuilder_FindInterface,
+ CaptureGraphBuilder_RenderStream,
+ CaptureGraphBuilder_ControlStream,
+ CaptureGraphBuilder_AllocCapFile,
+ CaptureGraphBuilder_CopyCaptureFile
+};
+
diff -Nru wine-old/dlls/qcap/capture.h wine-new/dlls/qcap/capture.h
--- wine-old/dlls/qcap/capture.h 1970-01-01 01:00:00.000000000 +0100
+++ wine-new/dlls/qcap/capture.h 2005-05-01 01:51:50.000000000 +0200
@@ -0,0 +1,35 @@
+/* DirectShow private capture header (QCAP.DLL)
+ *
+ * Copyright 2005 Maarten Lankhorst
+ *
+ * This file contains the (internal) driver registration functions,
+ * driver enumeration APIs and DirectDraw creation functions.
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2.1 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ */
+
+#ifndef __CAPTURE_H__
+#define __CAPTURE_H__
+
+HRESULT Capture_Initialise(void ** pointer, IPin *pOut, USHORT card);
+HRESULT Capture_GetMediaType(void * pBox, AM_MEDIA_TYPE ** mT);
+HRESULT Capture_SetMediaType(void * pBox, AM_MEDIA_TYPE * mT);
+HRESULT Capture_Run(void * pBox, FILTER_STATE *state);
+HRESULT Capture_Stop(void * pBox, FILTER_STATE *state);
+HRESULT Capture_Pause(void * pBox, FILTER_STATE *state);
+HRESULT Capture_Destroy(void * pBox);
+
+#endif /* __CAPTURE_H__ */
+
diff -Nru wine-old/dlls/qcap/enumpins.c wine-new/dlls/qcap/enumpins.c
--- wine-old/dlls/qcap/enumpins.c 1970-01-01 01:00:00.000000000 +0100
+++ wine-new/dlls/qcap/enumpins.c 2005-04-29 13:40:15.000000000 +0200
@@ -0,0 +1,183 @@
+/*
+ * Generic Implementation of IPin Interface
+ *
+ * Copyright 2003 Robert Shearman
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2.1 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ */
+
+#include "qcap_private.h"
+#include "pin.h"
+
+#include "wine/debug.h"
+#include "wine/unicode.h"
+#include "uuids.h"
+#include "vfwmsgs.h"
+#include <assert.h>
+
+WINE_DEFAULT_DEBUG_CHANNEL(qcap);
+
+typedef struct IEnumPinsImpl
+{
+ const IEnumPinsVtbl * lpVtbl;
+ ULONG refCount;
+ ENUMPINDETAILS enumPinDetails;
+ ULONG uIndex;
+} IEnumPinsImpl;
+
+static const struct IEnumPinsVtbl IEnumPinsImpl_Vtbl;
+
+HRESULT IEnumPinsImpl_Construct(const ENUMPINDETAILS * pDetails, IEnumPins ** ppEnum)
+{
+ IEnumPinsImpl * pEnumPins = CoTaskMemAlloc(sizeof(IEnumPinsImpl));
+ TRACE("()\n");
+ if (!pEnumPins)
+ {
+ *ppEnum = NULL;
+ return E_OUTOFMEMORY;
+ }
+ pEnumPins->lpVtbl = &IEnumPinsImpl_Vtbl;
+ pEnumPins->refCount = 1;
+ pEnumPins->uIndex = 0;
+ CopyMemory(&pEnumPins->enumPinDetails, pDetails, sizeof(ENUMPINDETAILS));
+ *ppEnum = (IEnumPins *)(&pEnumPins->lpVtbl);
+ return S_OK;
+}
+
+static HRESULT WINAPI IEnumPinsImpl_QueryInterface(IEnumPins * iface, REFIID riid, LPVOID * ppv)
+{
+ TRACE("(%s, %p)\n", qcdebugstr_guid(riid), ppv);
+
+ *ppv = NULL;
+
+ if (IsEqualIID(riid, &IID_IUnknown))
+ *ppv = (LPVOID)iface;
+ else if (IsEqualIID(riid, &IID_IEnumPins))
+ *ppv = (LPVOID)iface;
+
+ if (*ppv)
+ {
+ IUnknown_AddRef((IUnknown *)(*ppv));
+ return S_OK;
+ }
+
+ FIXME("No interface for %s!\n", qcdebugstr_guid(riid));
+
+ return E_NOINTERFACE;
+}
+static ULONG WINAPI IEnumPinsImpl_AddRef(IEnumPins * iface)
+{
+ IEnumPinsImpl *This = (IEnumPinsImpl *)iface;
+ ULONG refCount = InterlockedIncrement(&This->refCount);
+
+ TRACE("() -> %lu\n", refCount);
+
+ return refCount;
+}
+
+static ULONG WINAPI IEnumPinsImpl_Release(IEnumPins * iface)
+{
+ IEnumPinsImpl *This = (IEnumPinsImpl *)iface;
+ ULONG refCount = InterlockedDecrement(&This->refCount);
+
+ TRACE("() -> %lu\n", refCount);
+
+ if (!refCount)
+ {
+ CoTaskMemFree(This);
+ return 0;
+ }
+ else
+ return refCount;
+}
+
+static HRESULT WINAPI IEnumPinsImpl_Next(IEnumPins * iface, ULONG cPins, IPin ** ppPins, ULONG * pcFetched)
+{
+ ULONG cFetched;
+ IEnumPinsImpl *This = (IEnumPinsImpl *)iface;
+
+ TRACE("()\n");
+
+ cFetched = min(This->enumPinDetails.cPins, This->uIndex + cPins) - This->uIndex;
+
+ TRACE("(%lu, %p, %p)\n", cPins, ppPins, pcFetched);
+
+ if (cFetched > 0)
+ {
+ ULONG i;
+ for (i = 0; i < cFetched; i++) {
+ IPin_AddRef(This->enumPinDetails.ppPins[This->uIndex + i]);
+ ppPins[i] = This->enumPinDetails.ppPins[This->uIndex + i];
+ }
+ }
+
+ if ((cPins != 1) || pcFetched)
+ *pcFetched = cFetched;
+
+ This->uIndex += cFetched;
+
+ if (cFetched != cPins)
+ return S_FALSE;
+ return S_OK;
+}
+
+static HRESULT WINAPI IEnumPinsImpl_Skip(IEnumPins * iface, ULONG cPins)
+{
+ IEnumPinsImpl *This = (IEnumPinsImpl *)iface;
+
+ TRACE("(%lu)\n", cPins);
+
+ if (This->uIndex + cPins < This->enumPinDetails.cPins)
+ {
+ This->uIndex += cPins;
+ return S_OK;
+ }
+ return S_FALSE;
+}
+
+static HRESULT WINAPI IEnumPinsImpl_Reset(IEnumPins * iface)
+{
+ IEnumPinsImpl *This = (IEnumPinsImpl *)iface;
+
+ TRACE("IEnumPinsImpl::Reset()\n");
+
+ This->uIndex = 0;
+ return S_OK;
+}
+
+static HRESULT WINAPI IEnumPinsImpl_Clone(IEnumPins * iface, IEnumPins ** ppEnum)
+{
+ HRESULT hr;
+ IEnumPinsImpl *This = (IEnumPinsImpl *)iface;
+
+ TRACE("(%p)\n", ppEnum);
+
+ hr = IEnumPinsImpl_Construct(&This->enumPinDetails, ppEnum);
+ if (FAILED(hr))
+ return hr;
+ return IEnumPins_Skip(*ppEnum, This->uIndex);
+}
+
+static const IEnumPinsVtbl IEnumPinsImpl_Vtbl =
+{
+ IEnumPinsImpl_QueryInterface,
+ IEnumPinsImpl_AddRef,
+ IEnumPinsImpl_Release,
+ IEnumPinsImpl_Next,
+ IEnumPinsImpl_Skip,
+ IEnumPinsImpl_Reset,
+ IEnumPinsImpl_Clone
+};
+
diff -Nru wine-old/dlls/qcap/Makefile.in wine-new/dlls/qcap/Makefile.in
--- wine-old/dlls/qcap/Makefile.in 2004-05-14 23:37:32.000000000 +0200
+++ wine-new/dlls/qcap/Makefile.in 2005-05-01 01:56:42.000000000 +0200
@@ -3,8 +3,19 @@
SRCDIR = @srcdir@
VPATH = @srcdir@
MODULE = qcap.dll
+IMPORTS = ole32 kernel32 advapi32 user32
+EXTRALIBS = -lstrmiids -luuid $(LIBUNICODE)
-C_SRCS = qcap_main.c
+C_SRCS = \
+ capturegraph.c \
+ enumpins.c \
+ media.c \
+ null.c \
+ pin.c \
+ qcap_main.c \
+ regsvr.c \
+ v4l.c \
+ vfwcapture.c
RC_SRCS = version.rc
diff -Nru wine-old/dlls/qcap/media.c wine-new/dlls/qcap/media.c
--- wine-old/dlls/qcap/media.c 1970-01-01 01:00:00.000000000 +0100
+++ wine-new/dlls/qcap/media.c 2005-05-01 01:48:09.000000000 +0200
@@ -0,0 +1,229 @@
+/*
+ * Implementation of IEnumMediaTypes Interface
+ *
+ * Copyright 2003 Robert Shearman
+ * Copyright 2005 Maarten Lankhorst
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2.1 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ */
+
+#include "qcap_private.h"
+
+#include "wine/debug.h"
+
+WINE_DEFAULT_DEBUG_CHANNEL(qcap);
+
+HRESULT CopyMediaType(AM_MEDIA_TYPE * pDest, const AM_MEDIA_TYPE *pSrc)
+{
+ memcpy(pDest, pSrc, sizeof(AM_MEDIA_TYPE));
+ if (!pDest->pbFormat) return S_OK;
+ if (!(pDest->pbFormat = CoTaskMemAlloc(pSrc->cbFormat)))
+ return E_OUTOFMEMORY;
+ memcpy(pDest->pbFormat, pSrc->pbFormat, pSrc->cbFormat);
+ return S_OK;
+}
+
+void DeleteMediaType(AM_MEDIA_TYPE * pMediaType)
+{
+ if (pMediaType->pbFormat)
+ {
+ CoTaskMemFree(pMediaType->pbFormat);
+ pMediaType->pbFormat = NULL;
+ }
+ if (pMediaType->pUnk)
+ {
+ IUnknown_Release(pMediaType->pUnk);
+ pMediaType->pUnk = NULL;
+ }
+ CoTaskMemFree(pMediaType);
+}
+
+BOOL CompareMediaTypes(const AM_MEDIA_TYPE * pmt1, const AM_MEDIA_TYPE * pmt2, BOOL bWildcards)
+{
+ TRACE("pmt1: ");
+ dump_AM_MEDIA_TYPE(pmt1);
+ TRACE("pmt2: ");
+ dump_AM_MEDIA_TYPE(pmt2);
+ return (((bWildcards && (IsEqualGUID(&pmt1->majortype, &GUID_NULL) || IsEqualGUID(&pmt2->majortype, &GUID_NULL))) || IsEqualGUID(&pmt1->majortype, &pmt2->majortype)) &&
+ ((bWildcards && (IsEqualGUID(&pmt1->subtype, &GUID_NULL) || IsEqualGUID(&pmt2->subtype, &GUID_NULL))) || IsEqualGUID(&pmt1->subtype, &pmt2->subtype)));
+}
+
+void dump_AM_MEDIA_TYPE(const AM_MEDIA_TYPE * pmt)
+{
+ if (!pmt)
+ return;
+ TRACE("\t%s\n\t%s\n\t...\n\t%s\n", qcdebugstr_guid(&pmt->majortype), qcdebugstr_guid(&pmt->subtype), qcdebugstr_guid(&pmt->formattype));
+}
+
+typedef struct IEnumMediaTypesImpl
+{
+ const IEnumMediaTypesVtbl * lpVtbl;
+ ULONG refCount;
+ ENUMMEDIADETAILS enumMediaDetails;
+ ULONG uIndex;
+} IEnumMediaTypesImpl;
+
+static const struct IEnumMediaTypesVtbl IEnumMediaTypesImpl_Vtbl;
+
+HRESULT IEnumMediaTypesImpl_Construct(const ENUMMEDIADETAILS * pDetails, IEnumMediaTypes ** ppEnum)
+{
+ ULONG i;
+ IEnumMediaTypesImpl * pEnumMediaTypes = CoTaskMemAlloc(sizeof(IEnumMediaTypesImpl));
+
+ if (!pEnumMediaTypes)
+ {
+ *ppEnum = NULL;
+ return E_OUTOFMEMORY;
+ }
+ pEnumMediaTypes->lpVtbl = &IEnumMediaTypesImpl_Vtbl;
+ pEnumMediaTypes->refCount = 1;
+ pEnumMediaTypes->uIndex = 0;
+ pEnumMediaTypes->enumMediaDetails.cMediaTypes = pDetails->cMediaTypes;
+ pEnumMediaTypes->enumMediaDetails.pMediaTypes = CoTaskMemAlloc(sizeof(AM_MEDIA_TYPE) * pDetails->cMediaTypes);
+ for (i = 0; i < pDetails->cMediaTypes; i++)
+ CopyMediaType(&pEnumMediaTypes->enumMediaDetails.pMediaTypes[i], &pDetails->pMediaTypes[i]);
+ *ppEnum = (IEnumMediaTypes *)(&pEnumMediaTypes->lpVtbl);
+ return S_OK;
+}
+
+static HRESULT WINAPI IEnumMediaTypesImpl_QueryInterface(IEnumMediaTypes * iface, REFIID riid, LPVOID * ppv)
+{
+ TRACE("(%s, %p)\n", qcdebugstr_guid(riid), ppv);
+
+ *ppv = NULL;
+
+ if (IsEqualIID(riid, &IID_IUnknown))
+ *ppv = (LPVOID)iface;
+ else if (IsEqualIID(riid, &IID_IEnumMediaTypes))
+ *ppv = (LPVOID)iface;
+
+ if (*ppv)
+ {
+ IUnknown_AddRef((IUnknown *)(*ppv));
+ return S_OK;
+ }
+
+ FIXME("No interface for %s!\n", qcdebugstr_guid(riid));
+
+ return E_NOINTERFACE;
+}
+
+static ULONG WINAPI IEnumMediaTypesImpl_AddRef(IEnumMediaTypes * iface)
+{
+ IEnumMediaTypesImpl *This = (IEnumMediaTypesImpl *)iface;
+ ULONG refCount = InterlockedIncrement(&This->refCount);
+
+ TRACE("()\n");
+
+ return refCount;
+}
+
+static ULONG WINAPI IEnumMediaTypesImpl_Release(IEnumMediaTypes * iface)
+{
+ IEnumMediaTypesImpl *This = (IEnumMediaTypesImpl *)iface;
+ ULONG refCount = InterlockedDecrement(&This->refCount);
+
+ TRACE("()\n");
+
+ if (!refCount)
+ {
+ int i;
+ for (i = 0; i < This->enumMediaDetails.cMediaTypes; i++)
+ if (This->enumMediaDetails.pMediaTypes[i].pbFormat)
+ CoTaskMemFree(This->enumMediaDetails.pMediaTypes[i].pbFormat);
+ CoTaskMemFree(This->enumMediaDetails.pMediaTypes);
+ CoTaskMemFree(This);
+ return 0;
+ }
+ else
+ return refCount;
+}
+
+static HRESULT WINAPI IEnumMediaTypesImpl_Next(IEnumMediaTypes * iface, ULONG cMediaTypes, AM_MEDIA_TYPE ** ppMediaTypes, ULONG * pcFetched)
+{
+ ULONG cFetched;
+ IEnumMediaTypesImpl *This = (IEnumMediaTypesImpl *)iface;
+
+ cFetched = min(This->enumMediaDetails.cMediaTypes, This->uIndex + cMediaTypes) - This->uIndex;
+
+ TRACE("(%lu, %p, %p)\n", cMediaTypes, ppMediaTypes, pcFetched);
+ TRACE("Next uIndex: %lu, cFetched: %lu\n", This->uIndex, cFetched);
+
+ if (cFetched > 0)
+ {
+ ULONG i;
+ *ppMediaTypes = CoTaskMemAlloc(sizeof(AM_MEDIA_TYPE) * cFetched);
+ for (i = 0; i < cFetched; i++)
+ CopyMediaType(&(*ppMediaTypes)[i], &This->enumMediaDetails.pMediaTypes[This->uIndex + i]);
+ }
+
+ if ((cMediaTypes != 1) || pcFetched)
+ *pcFetched = cFetched;
+
+ This->uIndex += cFetched;
+
+ if (cFetched != cMediaTypes)
+ return S_FALSE;
+ return S_OK;
+}
+
+static HRESULT WINAPI IEnumMediaTypesImpl_Skip(IEnumMediaTypes * iface, ULONG cMediaTypes)
+{
+ IEnumMediaTypesImpl *This = (IEnumMediaTypesImpl *)iface;
+
+ TRACE("(%lu)\n", cMediaTypes);
+
+ if (This->uIndex + cMediaTypes < This->enumMediaDetails.cMediaTypes)
+ {
+ This->uIndex += cMediaTypes;
+ return S_OK;
+ }
+ return S_FALSE;
+}
+
+static HRESULT WINAPI IEnumMediaTypesImpl_Reset(IEnumMediaTypes * iface)
+{
+ IEnumMediaTypesImpl *This = (IEnumMediaTypesImpl *)iface;
+
+ TRACE("()\n");
+
+ This->uIndex = 0;
+ return S_OK;
+}
+
+static HRESULT WINAPI IEnumMediaTypesImpl_Clone(IEnumMediaTypes * iface, IEnumMediaTypes ** ppEnum)
+{
+ HRESULT hr;
+ IEnumMediaTypesImpl *This = (IEnumMediaTypesImpl *)iface;
+
+ TRACE("(%p)\n", ppEnum);
+ hr = IEnumMediaTypesImpl_Construct(&This->enumMediaDetails, ppEnum);
+
+ if (FAILED(hr))
+ return hr;
+ return IEnumMediaTypes_Skip(*ppEnum, This->uIndex);
+}
+
+static const IEnumMediaTypesVtbl IEnumMediaTypesImpl_Vtbl =
+{
+ IEnumMediaTypesImpl_QueryInterface,
+ IEnumMediaTypesImpl_AddRef,
+ IEnumMediaTypesImpl_Release,
+ IEnumMediaTypesImpl_Next,
+ IEnumMediaTypesImpl_Skip,
+ IEnumMediaTypesImpl_Reset,
+ IEnumMediaTypesImpl_Clone
+};
+
diff -Nru wine-old/dlls/qcap/null.c wine-new/dlls/qcap/null.c
--- wine-old/dlls/qcap/null.c 1970-01-01 01:00:00.000000000 +0100
+++ wine-new/dlls/qcap/null.c 2005-05-01 01:49:06.000000000 +0200
@@ -0,0 +1,84 @@
+/* DirectShow capture services (QCAP.DLL)
+ *
+ * Copyright 2005 Maarten Lankhorst
+ *
+ * This file contains the part of the vfw capture interface that
+ * does the actual capturing stuff required for capturing
+ * and setting/getting media format..
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2.1 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ *
+ * Why on earth am I writing a copyright notice for a NULL renderer???
+ */
+
+#include "config.h"
+#ifndef HAVE_LINUX_VIDEODEV_H
+
+#define NONAMELESSSTRUCT
+#define NONAMELESSUNION
+#include "qcap_private.h"
+#include "pin.h"
+
+#include "uuids.h"
+#include "mmreg.h"
+#include "vfwmsgs.h"
+#include "amvideo.h"
+#include "windef.h"
+#include "winbase.h"
+#include "dshow.h"
+#include "strmif.h"
+#include "ddraw.h"
+
+#include "wine/unicode.h"
+#include "wine/debug.h"
+
+HRESULT Capture_Initialise(void ** pointer, IPin *pOut, USHORT card)
+{
+ ERR("No DirectShow Video for Wine Capture support compiled\n");
+ return E_FAIL;
+}
+
+HRESULT Capture_GetMediaType(void * pBox, AM_MEDIA_TYPE ** mT)
+{
+ return E_FAIL;
+}
+
+HRESULT Capture_SetMediaType(void * pBox, AM_MEDIA_TYPE * mT)
+{
+ return E_FAIL;
+}
+
+HRESULT Capture_Run(void * pBox, FILTER_STATE *state)
+{
+ return E_FAIL;
+}
+
+HRESULT Capture_Stop(void * pBox, FILTER_STATE *state)
+{
+ return E_FAIL;
+}
+
+HRESULT Capture_Pause(void * pBox, FILTER_STATE *state)
+{
+ return E_FAIL;
+}
+
+HRESULT Capture_Destroy(void * pBox)
+{
+ return E_FAIL;
+}
+
+#endif
+
diff -Nru wine-old/dlls/qcap/pin.c wine-new/dlls/qcap/pin.c
--- wine-old/dlls/qcap/pin.c 1970-01-01 01:00:00.000000000 +0100
+++ wine-new/dlls/qcap/pin.c 2005-04-30 18:44:10.000000000 +0200
@@ -0,0 +1,411 @@
+/*
+ * Generic Implementation of IPin Interface
+ *
+ * Copyright 2003 Robert Shearman
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2.1 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ */
+
+#include "qcap_private.h"
+#include "pin.h"
+
+#include "wine/debug.h"
+#include "wine/unicode.h"
+#include "uuids.h"
+#include "vfwmsgs.h"
+#include <assert.h>
+
+WINE_DEFAULT_DEBUG_CHANNEL(qcap);
+
+#define ALIGNDOWN(value,boundary) ((value) & ~(boundary-1))
+#define ALIGNUP(value,boundary) (ALIGNDOWN(value - 1, boundary) + boundary)
+
+static void Copy_PinInfo(PIN_INFO * pDest, const PIN_INFO * pSrc)
+{
+ /* Tempting to just do a memcpy, but the name field is
+ 128 characters long! We will probably never exceed 10
+ most of the time, so we are better off copying
+ each field manually */
+ strcpyW(pDest->achName, pSrc->achName);
+ pDest->dir = pSrc->dir;
+ pDest->pFilter = pSrc->pFilter;
+}
+
+HRESULT WINAPI IPinImpl_ConnectedTo(IPin * iface, IPin ** ppPin)
+{
+ HRESULT hr;
+ IPinImpl *This = (IPinImpl *)iface;
+
+/* TRACE("(%p)\n", ppPin);*/
+
+ EnterCriticalSection(This->pCritSec);
+ {
+ if (This->pConnectedTo)
+ {
+ *ppPin = This->pConnectedTo;
+ IPin_AddRef(*ppPin);
+ hr = S_OK;
+ }
+ else
+ hr = VFW_E_NOT_CONNECTED;
+ }
+ LeaveCriticalSection(This->pCritSec);
+
+ return hr;
+}
+
+HRESULT WINAPI IPinImpl_ConnectionMediaType(IPin * iface, AM_MEDIA_TYPE * pmt)
+{
+ HRESULT hr;
+ IPinImpl *This = (IPinImpl *)iface;
+
+ TRACE("(%p/%p)->(%p)\n", This, iface, pmt);
+
+ EnterCriticalSection(This->pCritSec);
+ {
+ if (This->pConnectedTo)
+ {
+ CopyMediaType(pmt, &This->mtCurrent);
+ hr = S_OK;
+ }
+ else
+ {
+ ZeroMemory(pmt, sizeof(*pmt));
+ hr = VFW_E_NOT_CONNECTED;
+ }
+ }
+ LeaveCriticalSection(This->pCritSec);
+
+ return hr;
+}
+
+HRESULT WINAPI IPinImpl_QueryPinInfo(IPin * iface, PIN_INFO * pInfo)
+{
+ IPinImpl *This = (IPinImpl *)iface;
+
+ TRACE("(%p/%p)->(%p)\n", This, iface, pInfo);
+
+ Copy_PinInfo(pInfo, &This->pinInfo);
+ IBaseFilter_AddRef(pInfo->pFilter);
+
+ return S_OK;
+}
+
+HRESULT WINAPI IPinImpl_QueryDirection(IPin * iface, PIN_DIRECTION * pPinDir)
+{
+ IPinImpl *This = (IPinImpl *)iface;
+
+ TRACE("(%p/%p)->(%p)\n", This, iface, pPinDir);
+
+ *pPinDir = This->pinInfo.dir;
+
+ return S_OK;
+}
+
+HRESULT WINAPI IPinImpl_QueryId(IPin * iface, LPWSTR * Id)
+{
+ IPinImpl *This = (IPinImpl *)iface;
+
+ TRACE("(%p/%p)->(%p)\n", This, iface, Id);
+
+ *Id = CoTaskMemAlloc((strlenW(This->pinInfo.achName) + 1) * sizeof(WCHAR));
+ if (!Id)
+ return E_OUTOFMEMORY;
+
+ strcpyW(*Id, This->pinInfo.achName);
+
+ return S_OK;
+}
+
+HRESULT WINAPI IPinImpl_QueryAccept(IPin * iface, const AM_MEDIA_TYPE * pmt)
+{
+ IPinImpl *This = (IPinImpl *)iface;
+
+ TRACE("(%p/%p)->(%p)\n", This, iface, pmt);
+
+ if (!This->fnQueryAccept) return S_OK;
+
+ return (This->fnQueryAccept(This->pUserData, pmt) == S_OK ? S_OK : S_FALSE);
+}
+
+HRESULT WINAPI IPinImpl_QueryInternalConnections(IPin * iface, IPin ** apPin, ULONG * cPin)
+{
+ IPinImpl *This = (IPinImpl *)iface;
+
+ TRACE("(%p/%p)->(%p, %p)\n", This, iface, apPin, cPin);
+
+ return E_NOTIMPL; /* to tell caller that all input pins connected to all output pins */
+}
+
+/* Function called as a helper to IPin_Connect */
+/* specific AM_MEDIA_TYPE - it cannot be NULL */
+/* NOTE: not part of standard interface */
+static HRESULT OutputPin_ConnectSpecific(IPin * iface, IPin * pReceivePin, const AM_MEDIA_TYPE * pmt)
+{
+ OutputPin *This = (OutputPin *)iface;
+ HRESULT hr;
+ IMemAllocator * pMemAlloc = NULL;
+ ALLOCATOR_PROPERTIES actual; /* FIXME: should we put the actual props back in to This? */
+
+ TRACE("(%p, %p)\n", pReceivePin, pmt);
+ dump_AM_MEDIA_TYPE(pmt);
+
+ /* FIXME: call queryacceptproc */
+
+ This->pin.pConnectedTo = pReceivePin;
+ IPin_AddRef(pReceivePin);
+ CopyMediaType(&This->pin.mtCurrent, pmt);
+
+ hr = IPin_ReceiveConnection(pReceivePin, iface, pmt);
+
+ /* get the IMemInputPin interface we will use to deliver samples to the
+ * connected pin */
+ if (SUCCEEDED(hr))
+ {
+ hr = IPin_QueryInterface(pReceivePin, &IID_IMemInputPin, (LPVOID)&This->pMemInputPin);
+
+ if (SUCCEEDED(hr))
+ hr = IMemInputPin_GetAllocator(This->pMemInputPin, &pMemAlloc);
+
+ if (hr == VFW_E_NO_ALLOCATOR)
+ {
+ /* Input pin provides no allocator, use standard memory allocator */
+ hr = CoCreateInstance(&CLSID_MemoryAllocator, NULL, CLSCTX_INPROC_SERVER, &IID_IMemAllocator, (LPVOID*)&pMemAlloc);
+
+ if (SUCCEEDED(hr))
+ {
+ hr = IMemInputPin_NotifyAllocator(This->pMemInputPin, pMemAlloc, FALSE);
+ }
+ }
+
+ if (SUCCEEDED(hr))
+ hr = IMemAllocator_SetProperties(pMemAlloc, &This->allocProps, &actual);
+
+ if (pMemAlloc)
+ IMemAllocator_Release(pMemAlloc);
+
+ /* break connection if we couldn't get the allocator */
+ if (FAILED(hr))
+ IPin_Disconnect(pReceivePin);
+ }
+
+ if (FAILED(hr))
+ {
+ IPin_Release(This->pin.pConnectedTo);
+ This->pin.pConnectedTo = NULL;
+ DeleteMediaType(&This->pin.mtCurrent);
+ }
+
+ TRACE(" -- %lx\n", hr);
+ return hr;
+}
+
+HRESULT OutputPin_Init(const PIN_INFO * pPinInfo, ALLOCATOR_PROPERTIES * props, LPVOID pUserData, QUERYACCEPTPROC pQueryAccept, LPCRITICAL_SECTION pCritSec, OutputPin * pPinImpl)
+{
+ TRACE("\n");
+
+ /* Common attributes */
+ pPinImpl->pin.refCount = 1;
+ pPinImpl->pin.pConnectedTo = NULL;
+ pPinImpl->pin.fnQueryAccept = pQueryAccept;
+ pPinImpl->pin.pUserData = pUserData;
+ pPinImpl->pin.pCritSec = pCritSec;
+ Copy_PinInfo(&pPinImpl->pin.pinInfo, pPinInfo);
+
+ /* Output pin attributes */
+ pPinImpl->pMemInputPin = NULL;
+ pPinImpl->pConnectSpecific = OutputPin_ConnectSpecific;
+ if (props)
+ {
+ memcpy(&pPinImpl->allocProps, props, sizeof(pPinImpl->allocProps));
+ if (pPinImpl->allocProps.cbAlign == 0)
+ pPinImpl->allocProps.cbAlign = 1;
+ }
+ else
+ ZeroMemory(&pPinImpl->allocProps, sizeof(pPinImpl->allocProps));
+
+ return S_OK;
+}
+
+HRESULT WINAPI OutputPin_Connect(IPin * iface, IPin * pReceivePin, const AM_MEDIA_TYPE * pmt)
+{
+ HRESULT hr;
+ OutputPin *This = (OutputPin *)iface;
+
+ TRACE("(%p/%p)->(%p, %p)\n", This, iface, pReceivePin, pmt);
+ dump_AM_MEDIA_TYPE(pmt);
+
+ /* If we try to connect to ourself, we will definitely deadlock.
+ * There are other cases where we could deadlock too, but this
+ * catches the obvious case */
+ assert(pReceivePin != iface);
+
+ EnterCriticalSection(This->pin.pCritSec);
+ {
+ /* if we have been a specific type to connect with, then we can either connect
+ * with that or fail. We cannot choose different AM_MEDIA_TYPE */
+ if (pmt && !IsEqualGUID(&pmt->majortype, &GUID_NULL) && !IsEqualGUID(&pmt->subtype, &GUID_NULL))
+ hr = This->pConnectSpecific(iface, pReceivePin, pmt);
+ else
+ {
+ /* negotiate media type */
+
+ IEnumMediaTypes * pEnumCandidates;
+ AM_MEDIA_TYPE * pmtCandidate; /* Candidate media type */
+
+ if (SUCCEEDED(hr = IPin_EnumMediaTypes(iface, &pEnumCandidates)))
+ {
+ hr = VFW_E_NO_ACCEPTABLE_TYPES; /* Assume the worst, but set to S_OK if connected successfully */
+
+ /* try this filter's media types first */
+ while (S_OK == IEnumMediaTypes_Next(pEnumCandidates, 1, &pmtCandidate, NULL))
+ {
+ if (( !pmt || CompareMediaTypes(pmt, pmtCandidate, TRUE) ) &&
+ (This->pConnectSpecific(iface, pReceivePin, pmtCandidate) == S_OK))
+ {
+ hr = S_OK;
+ TRACE("o_o\n");
+ DeleteMediaType(pmtCandidate);
+ break;
+ }
+ DeleteMediaType(pmtCandidate);
+ }
+ IEnumMediaTypes_Release(pEnumCandidates);
+ }
+
+ /* then try receiver filter's media types */
+ if (hr != S_OK && SUCCEEDED(hr = IPin_EnumMediaTypes(pReceivePin, &pEnumCandidates))) /* if we haven't already connected successfully */
+ {
+ hr = VFW_E_NO_ACCEPTABLE_TYPES; /* Assume the worst, but set to S_OK if connected successfully */
+
+ while (S_OK == IEnumMediaTypes_Next(pEnumCandidates, 1, &pmtCandidate, NULL))
+ {
+ if (( !pmt || CompareMediaTypes(pmt, pmtCandidate, TRUE) ) &&
+ (This->pConnectSpecific(iface, pReceivePin, pmtCandidate) == S_OK))
+ {
+ hr = S_OK;
+ DeleteMediaType(pmtCandidate);
+ break;
+ }
+ DeleteMediaType(pmtCandidate);
+ } /* while */
+ IEnumMediaTypes_Release(pEnumCandidates);
+ } /* if not found */
+ } /* if negotiate media type */
+ } /* if succeeded */
+ LeaveCriticalSection(This->pin.pCritSec);
+
+ TRACE(" -- %lx\n", hr);
+ return hr;
+}
+
+HRESULT WINAPI OutputPin_ReceiveConnection(IPin * iface, IPin * pReceivePin, const AM_MEDIA_TYPE * pmt)
+{
+ ERR("Incoming connection on an output pin! (%p, %p)\n", pReceivePin, pmt);
+
+ return E_UNEXPECTED;
+}
+
+HRESULT WINAPI OutputPin_Disconnect(IPin * iface)
+{
+ HRESULT hr;
+ OutputPin *This = (OutputPin *)iface;
+
+ TRACE("()\n");
+
+ EnterCriticalSection(This->pin.pCritSec);
+ {
+ if (This->pMemInputPin)
+ {
+ IMemInputPin_Release(This->pMemInputPin);
+ This->pMemInputPin = NULL;
+ }
+ if (This->pin.pConnectedTo)
+ {
+ IPin_Release(This->pin.pConnectedTo);
+ This->pin.pConnectedTo = NULL;
+ hr = S_OK;
+ }
+ else
+ hr = S_FALSE;
+ }
+ LeaveCriticalSection(This->pin.pCritSec);
+
+ return hr;
+}
+
+HRESULT OutputPin_GetDeliveryBuffer(OutputPin * This, IMediaSample ** ppSample, const REFERENCE_TIME * tStart, const REFERENCE_TIME * tStop, DWORD dwFlags)
+{
+ HRESULT hr;
+
+ TRACE("(%p, %p, %p, %lx)\n", ppSample, tStart, tStop, dwFlags);
+
+ EnterCriticalSection(This->pin.pCritSec);
+ {
+ if (!This->pin.pConnectedTo)
+ hr = VFW_E_NOT_CONNECTED;
+ else
+ {
+ IMemAllocator * pAlloc = NULL;
+
+ hr = IMemInputPin_GetAllocator(This->pMemInputPin, &pAlloc);
+
+ if (SUCCEEDED(hr))
+ hr = IMemAllocator_GetBuffer(pAlloc, ppSample, (REFERENCE_TIME *)tStart, (REFERENCE_TIME *)tStop, dwFlags);
+
+ if (SUCCEEDED(hr))
+ hr = IMediaSample_SetTime(*ppSample, (REFERENCE_TIME *)tStart, (REFERENCE_TIME *)tStop);
+
+ if (pAlloc)
+ IMemAllocator_Release(pAlloc);
+ }
+ }
+ LeaveCriticalSection(This->pin.pCritSec);
+
+ return hr;
+}
+
+HRESULT OutputPin_SendSample(OutputPin * This, IMediaSample * pSample)
+{
+ HRESULT hr = S_OK;
+ IMemInputPin * pMemConnected = NULL;
+
+ EnterCriticalSection(This->pin.pCritSec);
+ {
+ if (!This->pin.pConnectedTo || !This->pMemInputPin)
+ hr = VFW_E_NOT_CONNECTED;
+ else
+ {
+ /* we don't have the lock held when using This->pMemInputPin,
+ * so we need to AddRef it to stop it being deleted while we are
+ * using it. */
+ pMemConnected = This->pMemInputPin;
+ IMemInputPin_AddRef(pMemConnected);
+ }
+ }
+ LeaveCriticalSection(This->pin.pCritSec);
+
+ if (SUCCEEDED(hr))
+ {
+ /* NOTE: if we are in a critical section when Receive is called
+ * then it causes some problems (most notably with the native Video
+ * Renderer) if we are re-entered for whatever reason */
+ hr = IMemInputPin_Receive(pMemConnected, pSample);
+ IMemInputPin_Release(pMemConnected);
+ }
+
+ return hr;
+}
+
diff -Nru wine-old/dlls/qcap/pin.h wine-new/dlls/qcap/pin.h
--- wine-old/dlls/qcap/pin.h 1970-01-01 01:00:00.000000000 +0100
+++ wine-new/dlls/qcap/pin.h 2005-04-29 14:01:43.000000000 +0200
@@ -0,0 +1,81 @@
+/*
+ * IPin function declarations to allow inheritance
+ *
+ * Copyright 2003 Robert Shearman
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2.1 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ */
+
+/* This function will process incoming samples to the pin.
+ * Any return value valid in IMemInputPin::Receive is allowed here
+ */
+typedef HRESULT (* SAMPLEPROC)(LPVOID userdata, IMediaSample * pSample);
+
+/* This function will determine whether a type is supported or not.
+ * It is allowed to return any error value (within reason), as opposed
+ * to IPin::QueryAccept which is only allowed to return S_OK or S_FALSE.
+ */
+typedef HRESULT (* QUERYACCEPTPROC)(LPVOID userdata, const AM_MEDIA_TYPE * pmt);
+
+/* This function is called prior to finalizing a connection with
+ * another pin and can be used to get things from the other pin
+ * like IMemInput interfaces.
+ */
+typedef HRESULT (* PRECONNECTPROC)(IPin * iface, IPin * pConnectPin);
+
+typedef struct IPinImpl
+{
+ const struct IPinVtbl * lpVtbl;
+ ULONG refCount;
+ LPCRITICAL_SECTION pCritSec;
+ PIN_INFO pinInfo;
+ IPin * pConnectedTo;
+ AM_MEDIA_TYPE mtCurrent;
+ ENUMMEDIADETAILS enumMediaDetails;
+ QUERYACCEPTPROC fnQueryAccept;
+ LPVOID pUserData;
+} IPinImpl;
+
+typedef struct OutputPin
+{
+ /* inheritance C style! */
+ IPinImpl pin;
+
+ IMemInputPin * pMemInputPin;
+ HRESULT (* pConnectSpecific)(IPin * iface, IPin * pReceiver, const AM_MEDIA_TYPE * pmt);
+ ALLOCATOR_PROPERTIES allocProps;
+} OutputPin;
+
+/*** Initializers ***/
+HRESULT OutputPin_Init(const PIN_INFO * pPinInfo, ALLOCATOR_PROPERTIES *props, LPVOID pUserData, QUERYACCEPTPROC pQueryAccept, LPCRITICAL_SECTION pCritSec, OutputPin * pPinImpl);
+
+/* Common */
+HRESULT WINAPI IPinImpl_ConnectedTo(IPin * iface, IPin ** ppPin);
+HRESULT WINAPI IPinImpl_ConnectionMediaType(IPin * iface, AM_MEDIA_TYPE * pmt);
+HRESULT WINAPI IPinImpl_QueryPinInfo(IPin * iface, PIN_INFO * pInfo);
+HRESULT WINAPI IPinImpl_QueryDirection(IPin * iface, PIN_DIRECTION * pPinDir);
+HRESULT WINAPI IPinImpl_QueryId(IPin * iface, LPWSTR * Id);
+HRESULT WINAPI IPinImpl_QueryAccept(IPin * iface, const AM_MEDIA_TYPE * pmt);
+HRESULT WINAPI IPinImpl_EnumMediaTypes(IPin * iface, IEnumMediaTypes ** ppEnum);
+HRESULT WINAPI IPinImpl_QueryInternalConnections(IPin * iface, IPin ** apPin, ULONG * cPin);
+
+/* Output Pin */
+HRESULT WINAPI OutputPin_Connect(IPin * iface, IPin * pReceivePin, const AM_MEDIA_TYPE * pmt);
+HRESULT WINAPI OutputPin_Disconnect(IPin * iface);
+HRESULT WINAPI OutputPin_ReceiveConnection(IPin * iface, IPin * pReceivePin, const AM_MEDIA_TYPE * pmt);
+
+HRESULT OutputPin_GetDeliveryBuffer(OutputPin * This, IMediaSample ** ppSample, const REFERENCE_TIME * tStart, const REFERENCE_TIME * tStop, DWORD dwFlags);
+HRESULT OutputPin_SendSample(OutputPin * This, IMediaSample * pSample);
+
diff -Nru wine-old/dlls/qcap/qcap_main.c wine-new/dlls/qcap/qcap_main.c
--- wine-old/dlls/qcap/qcap_main.c 2004-05-21 22:54:48.000000000 +0200
+++ wine-new/dlls/qcap/qcap_main.c 2005-05-01 01:53:20.000000000 +0200
@@ -1,7 +1,10 @@
-/*
- * Qcap implementation
+/* DirectShow Capture Base Functions (QCAP.DLL)
*
- * Copyright (C) 2003 Dominik Strasser
+ * Copyright 2002 Lionel Ulmer
+ * Copyright 2005 Maarten Lankhorst
+ *
+ * This file contains the (internal) driver registration functions,
+ * driver enumeration APIs and DirectDraw creation functions.
*
* This library is free software; you can redistribute it and/or
* modify it under the terms of the GNU Lesser General Public
@@ -18,26 +21,206 @@
* Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
*/
+#include "config.h"
#include "wine/debug.h"
-#include "winerror.h"
+#include "qcap_private.h"
WINE_DEFAULT_DEBUG_CHANNEL(qcap);
+static DWORD dll_ref = 0;
+
+/* For the moment, do nothing here. */
+BOOL WINAPI DllMain(HINSTANCE hInstDLL, DWORD fdwReason, LPVOID lpv)
+{
+ switch(fdwReason) {
+ case DLL_PROCESS_ATTACH:
+ DisableThreadLibraryCalls(hInstDLL);
+ break;
+ case DLL_PROCESS_DETACH:
+ break;
+ }
+ return TRUE;
+}
+
+/******************************************************************************
+ * DirectShow ClassFactory
+ */
+typedef struct {
+ IClassFactory ITF_IClassFactory;
+
+ DWORD ref;
+ HRESULT (*pfnCreateInstance)(IUnknown *pUnkOuter, LPVOID *ppObj);
+} IClassFactoryImpl;
+
+struct object_creation_info
+{
+ const CLSID *clsid;
+ HRESULT (*pfnCreateInstance)(IUnknown *pUnkOuter, LPVOID *ppObj);
+};
+
+static const struct object_creation_info object_creation[] =
+{
+ { &CLSID_VfwCapture, &VfwCapture_create },
+ { &CLSID_CaptureGraphBuilder, &CaptureGraphBuilder_create }
+};
+
+static HRESULT WINAPI
+DSCF_QueryInterface(LPCLASSFACTORY iface,REFIID riid,LPVOID *ppobj)
+{
+ IClassFactoryImpl *This = (IClassFactoryImpl *)iface;
+
+ if (IsEqualGUID(riid, &IID_IUnknown)
+ || IsEqualGUID(riid, &IID_IClassFactory))
+ {
+ IClassFactory_AddRef(iface);
+ *ppobj = This;
+ return S_OK;
+ }
+
+ WARN("(%p)->(%s,%p),not found\n",This,debugstr_guid(riid),ppobj);
+ return E_NOINTERFACE;
+}
+
+static ULONG WINAPI DSCF_AddRef(LPCLASSFACTORY iface)
+{
+ IClassFactoryImpl *This = (IClassFactoryImpl *)iface;
+ return InterlockedIncrement(&This->ref);
+}
+
+static ULONG WINAPI DSCF_Release(LPCLASSFACTORY iface)
+{
+ IClassFactoryImpl *This = (IClassFactoryImpl *)iface;
+
+ ULONG ref = InterlockedDecrement(&This->ref);
+
+ if (ref == 0)
+ HeapFree(GetProcessHeap(), 0, This);
+
+ return ref;
+}
+
+
+static HRESULT WINAPI DSCF_CreateInstance(LPCLASSFACTORY iface, LPUNKNOWN pOuter,
+ REFIID riid, LPVOID *ppobj)
+{
+ IClassFactoryImpl *This = (IClassFactoryImpl *)iface;
+ HRESULT hres;
+ LPUNKNOWN punk;
+
+ TRACE("(%p)->(%p,%s,%p)\n",This,pOuter,debugstr_guid(riid),ppobj);
+
+ *ppobj = NULL;
+ hres = This->pfnCreateInstance(pOuter, (LPVOID *) &punk);
+ if (SUCCEEDED(hres)) {
+ hres = IUnknown_QueryInterface(punk, riid, ppobj);
+ IUnknown_Release(punk);
+ }
+ return hres;
+}
+
+static HRESULT WINAPI DSCF_LockServer(LPCLASSFACTORY iface,BOOL dolock)
+{
+ IClassFactoryImpl *This = (IClassFactoryImpl *)iface;
+ FIXME("(%p)->(%d),stub!\n",This,dolock);
+ return S_OK;
+}
+
+static IClassFactoryVtbl DSCF_Vtbl =
+{
+ DSCF_QueryInterface,
+ DSCF_AddRef,
+ DSCF_Release,
+ DSCF_CreateInstance,
+ DSCF_LockServer
+};
+
+/*******************************************************************************
+ * DllGetClassObject [QCAP.@]
+ * Retrieves class object from a DLL object
+ *
+ * NOTES
+ * Docs say returns STDAPI
+ *
+ * PARAMS
+ * rclsid [I] CLSID for the class object
+ * riid [I] Reference to identifier of interface for class object
+ * ppv [O] Address of variable to receive interface pointer for riid
+ *
+ * RETURNS
+ * Success: S_OK
+ * Failure: CLASS_E_CLASSNOTAVAILABLE, E_OUTOFMEMORY, E_INVALIDARG,
+ * E_UNEXPECTED
+ */
+DWORD WINAPI QCAP_DllGetClassObject(REFCLSID rclsid, REFIID riid, LPVOID *ppv)
+{
+ unsigned int i;
+ IClassFactoryImpl *factory;
+
+ TRACE("(%s,%s,%p)\n", debugstr_guid(rclsid), debugstr_guid(riid), ppv);
+
+ if ( !IsEqualGUID( &IID_IClassFactory, riid )
+ && ! IsEqualGUID( &IID_IUnknown, riid) )
+ return E_NOINTERFACE;
+
+ for (i=0; i < sizeof(object_creation)/sizeof(object_creation[0]); i++)
+ {
+ if (IsEqualGUID(object_creation[i].clsid, rclsid))
+ break;
+ }
+
+ if (i == sizeof(object_creation)/sizeof(object_creation[0]))
+ {
+ FIXME("%s: no class found.\n", debugstr_guid(rclsid));
+ return CLASS_E_CLASSNOTAVAILABLE;
+ }
+
+ factory = HeapAlloc(GetProcessHeap(), 0, sizeof(*factory));
+ if (factory == NULL) return E_OUTOFMEMORY;
+
+ factory->ITF_IClassFactory.lpVtbl = &DSCF_Vtbl;
+ factory->ref = 1;
+
+ factory->pfnCreateInstance = object_creation[i].pfnCreateInstance;
+
+ *ppv = &(factory->ITF_IClassFactory);
+ return S_OK;
+}
+
/***********************************************************************
- * DllRegisterServer (QCAP.@)
+ * DllCanUnloadNow (QCAP.@)
*/
-HRESULT WINAPI QCAP_DllRegisterServer()
+HRESULT WINAPI QCAP_DllCanUnloadNow()
{
- FIXME("(): stub\n");
- return 0;
+ return dll_ref != 0 ? S_FALSE : S_OK;
}
+
+#define OUR_GUID_ENTRY(name, l, w1, w2, b1, b2, b3, b4, b5, b6, b7, b8) \
+ { { l, w1, w2, { b1, b2, b3, b4, b5, b6, b7, b8 } } , #name },
+
+static struct {
+ const GUID riid;
+ const char *name;
+} InterfaceDesc[] =
+{
+#include "uuids.h"
+ { { 0, 0, 0, {0, 0, 0, 0, 0, 0, 0, 0} }, NULL }
+};
+
/***********************************************************************
- * DllGetClassObject (QCAP.@)
+ * qcdebugstr_guid (internal)
+ *
+ * Gives a text version of QCap Guids
*/
-HRESULT WINAPI QCAP_DllGetClassObject(REFCLSID rclsid, REFIID iid, LPVOID *ppv)
+const char * qcdebugstr_guid( const GUID * id )
{
- FIXME("\n\tCLSID:\t%s,\n\tIID:\t%s\n",debugstr_guid(rclsid),debugstr_guid(iid));
- return CLASS_E_CLASSNOTAVAILABLE;
+ int i;
+ char * name = NULL;
+
+ for (i=0;InterfaceDesc[i].name && !name;i++) {
+ if (IsEqualGUID(&InterfaceDesc[i].riid, id)) return InterfaceDesc[i].name;
+ }
+ return debugstr_guid(id);
}
+
diff -Nru wine-old/dlls/qcap/qcap_private.h wine-new/dlls/qcap/qcap_private.h
--- wine-old/dlls/qcap/qcap_private.h 1970-01-01 01:00:00.000000000 +0100
+++ wine-new/dlls/qcap/qcap_private.h 2005-04-29 00:27:34.000000000 +0200
@@ -0,0 +1,64 @@
+/* Quartz Capture private interfaces (QCAP.DLL)
+ *
+ * Copyright 2005 Maarten Lankhorst
+ *
+ * This file contains the (internal) driver registration functions,
+ * driver enumeration APIs and DirectDraw creation functions.
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2.1 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ */
+
+#ifndef __QCAP_PRIVATE_INCLUDED__
+#define __QCAP_PRIVATE_INCLUDED__
+
+#include <stdarg.h>
+
+#define COBJMACROS
+
+#include "windef.h"
+#include "winbase.h"
+#include "wtypes.h"
+#include "wingdi.h"
+#include "winuser.h"
+#include "dshow.h"
+
+#define ICOM_THIS_MULTI(impl,field,iface) impl* const This=(impl*)((char*)(iface) - offsetof(impl,field))
+
+HRESULT VfwCapture_create(IUnknown * pUnkOuter, LPVOID * ppv);
+HRESULT CaptureGraphBuilder_create(IUnknown * pUnkOuter, LPVOID * ppv);
+
+typedef struct tagENUMPINDETAILS
+{
+ ULONG cPins;
+ IPin ** ppPins;
+} ENUMPINDETAILS;
+
+typedef struct tagENUMEDIADETAILS
+{
+ ULONG cMediaTypes;
+ AM_MEDIA_TYPE * pMediaTypes;
+} ENUMMEDIADETAILS;
+
+HRESULT IEnumPinsImpl_Construct(const ENUMPINDETAILS * pDetails, IEnumPins ** ppEnum);
+HRESULT IEnumMediaTypesImpl_Construct(const ENUMMEDIADETAILS * pDetails, IEnumMediaTypes ** ppEnum);
+
+extern const char * qcdebugstr_guid(const GUID * id);
+
+HRESULT CopyMediaType(AM_MEDIA_TYPE * pDest, const AM_MEDIA_TYPE *pSrc);
+void DeleteMediaType(AM_MEDIA_TYPE * pmt);
+BOOL CompareMediaTypes(const AM_MEDIA_TYPE * pmt1, const AM_MEDIA_TYPE * pmt2, BOOL bWildcards);
+void dump_AM_MEDIA_TYPE(const AM_MEDIA_TYPE * pmt);
+
+#endif /* __QCAP_PRIVATE_INCLUDED__ */
diff -Nru wine-old/dlls/qcap/qcap.spec wine-new/dlls/qcap/qcap.spec
--- wine-old/dlls/qcap/qcap.spec 2003-07-30 05:48:55.000000000 +0200
+++ wine-new/dlls/qcap/qcap.spec 2005-04-28 23:07:08.000000000 +0200
@@ -1,4 +1,4 @@
-@ stub DllCanUnloadNow
+@ stdcall -private DllCanUnloadNow() QCAP_DllCanUnloadNow
@ stdcall -private DllGetClassObject(ptr ptr ptr) QCAP_DllGetClassObject
@ stdcall -private DllRegisterServer() QCAP_DllRegisterServer
-@ stub DllUnregisterServer
+@ stdcall -private DllUnregisterServer() QCAP_DllUnregisterServer
diff -Nru wine-old/dlls/qcap/regsvr.c wine-new/dlls/qcap/regsvr.c
--- wine-old/dlls/qcap/regsvr.c 1970-01-01 01:00:00.000000000 +0100
+++ wine-new/dlls/qcap/regsvr.c 2005-04-29 00:28:39.000000000 +0200
@@ -0,0 +1,940 @@
+/*
+ * self-registerable dll functions for qcap.dll
+ *
+ * Copyright (C) 2003 John K. Hohm
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2.1 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ */
+
+#define NONAMELESSUNION
+#define NONAMELESSSTRUCT
+#define COBJMACROS
+#define COM_NO_WINDOWS_H
+#include <stdarg.h>
+#include <string.h>
+
+#include "windef.h"
+#include "winbase.h"
+#include "winuser.h"
+#include "winreg.h"
+#include "winerror.h"
+
+#include "ole2.h"
+#include "uuids.h"
+#include "strmif.h"
+
+#include "wine/debug.h"
+
+WINE_DEFAULT_DEBUG_CHANNEL(qcap);
+
+/*
+ * Near the bottom of this file are the exported DllRegisterServer and
+ * DllUnregisterServer, which make all this worthwhile.
+ */
+
+/***********************************************************************
+ * interface for self-registering
+ */
+struct regsvr_interface
+{
+ IID const *iid; /* NULL for end of list */
+ LPCSTR name; /* can be NULL to omit */
+ IID const *base_iid; /* can be NULL to omit */
+ int num_methods; /* can be <0 to omit */
+ CLSID const *ps_clsid; /* can be NULL to omit */
+ CLSID const *ps_clsid32; /* can be NULL to omit */
+};
+
+static HRESULT register_interfaces(struct regsvr_interface const *list);
+static HRESULT unregister_interfaces(struct regsvr_interface const *list);
+
+struct regsvr_coclass
+{
+ CLSID const *clsid; /* NULL for end of list */
+ LPCSTR name; /* can be NULL to omit */
+ LPCSTR ips; /* can be NULL to omit */
+ LPCSTR ips32; /* can be NULL to omit */
+ LPCSTR ips32_tmodel; /* can be NULL to omit */
+ LPCSTR progid; /* can be NULL to omit */
+ LPCSTR viprogid; /* can be NULL to omit */
+ LPCSTR progid_extra; /* can be NULL to omit */
+};
+
+static HRESULT register_coclasses(struct regsvr_coclass const *list);
+static HRESULT unregister_coclasses(struct regsvr_coclass const *list);
+
+struct regsvr_mediatype_parsing
+{
+ CLSID const *majortype; /* NULL for end of list */
+ CLSID const *subtype;
+ LPCSTR line[11]; /* NULL for end of list */
+};
+
+static HRESULT register_mediatypes_parsing(struct regsvr_mediatype_parsing const *list);
+static HRESULT unregister_mediatypes_parsing(struct regsvr_mediatype_parsing const *list);
+
+struct regsvr_mediatype_extension
+{
+ CLSID const *majortype; /* NULL for end of list */
+ CLSID const *subtype;
+ LPCSTR extension;
+};
+
+struct mediatype
+{
+ CLSID const *majortype; /* NULL for end of list */
+ CLSID const *subtype;
+ DWORD fourcc;
+};
+
+struct pin
+{
+ DWORD flags; /* 0xFFFFFFFF for end of list */
+ struct mediatype mediatypes[11];
+};
+
+struct regsvr_filter
+{
+ CLSID const *clsid; /* NULL for end of list */
+ CLSID const *category;
+ WCHAR name[50];
+ DWORD merit;
+ struct pin pins[11];
+};
+
+static HRESULT register_mediatypes_extension(struct regsvr_mediatype_extension const *list);
+static HRESULT unregister_mediatypes_extension(struct regsvr_mediatype_extension const *list);
+
+static HRESULT register_filters(struct regsvr_filter const *list);
+static HRESULT unregister_filters(struct regsvr_filter const *list);
+
+/***********************************************************************
+ * static string constants
+ */
+static WCHAR const interface_keyname[10] = {
+ 'I', 'n', 't', 'e', 'r', 'f', 'a', 'c', 'e', 0 };
+static WCHAR const base_ifa_keyname[14] = {
+ 'B', 'a', 's', 'e', 'I', 'n', 't', 'e', 'r', 'f', 'a', 'c',
+ 'e', 0 };
+static WCHAR const num_methods_keyname[11] = {
+ 'N', 'u', 'm', 'M', 'e', 't', 'h', 'o', 'd', 's', 0 };
+static WCHAR const ps_clsid_keyname[15] = {
+ 'P', 'r', 'o', 'x', 'y', 'S', 't', 'u', 'b', 'C', 'l', 's',
+ 'i', 'd', 0 };
+static WCHAR const ps_clsid32_keyname[17] = {
+ 'P', 'r', 'o', 'x', 'y', 'S', 't', 'u', 'b', 'C', 'l', 's',
+ 'i', 'd', '3', '2', 0 };
+static WCHAR const clsid_keyname[6] = {
+ 'C', 'L', 'S', 'I', 'D', 0 };
+static WCHAR const curver_keyname[7] = {
+ 'C', 'u', 'r', 'V', 'e', 'r', 0 };
+static WCHAR const ips_keyname[13] = {
+ 'I', 'n', 'P', 'r', 'o', 'c', 'S', 'e', 'r', 'v', 'e', 'r',
+ 0 };
+static WCHAR const ips32_keyname[15] = {
+ 'I', 'n', 'P', 'r', 'o', 'c', 'S', 'e', 'r', 'v', 'e', 'r',
+ '3', '2', 0 };
+static WCHAR const progid_keyname[7] = {
+ 'P', 'r', 'o', 'g', 'I', 'D', 0 };
+static WCHAR const viprogid_keyname[25] = {
+ 'V', 'e', 'r', 's', 'i', 'o', 'n', 'I', 'n', 'd', 'e', 'p',
+ 'e', 'n', 'd', 'e', 'n', 't', 'P', 'r', 'o', 'g', 'I', 'D',
+ 0 };
+static char const tmodel_valuename[] = "ThreadingModel";
+static WCHAR const mediatype_name[11] = {
+ 'M', 'e', 'd', 'i', 'a', ' ', 'T', 'y', 'p', 'e', 0 };
+static WCHAR const subtype_valuename[8] = {
+ 'S', 'u', 'b', 't', 'y', 'p', 'e', 0 };
+static WCHAR const sourcefilter_valuename[14] = {
+ 'S', 'o', 'u', 'r', 'c', 'e', ' ', 'F', 'i', 'l', 't', 'e', 'r', 0 };
+static WCHAR const extensions_keyname[11] = {
+ 'E', 'x', 't', 'e', 'n', 's', 'i', 'o', 'n', 's', 0 };
+
+/***********************************************************************
+ * static helper functions
+ */
+static LONG register_key_guid(HKEY base, WCHAR const *name, GUID const *guid);
+static LONG register_key_defvalueW(HKEY base, WCHAR const *name,
+ WCHAR const *value);
+static LONG register_key_defvalueA(HKEY base, WCHAR const *name,
+ char const *value);
+static LONG register_progid(WCHAR const *clsid,
+ char const *progid, char const *curver_progid,
+ char const *name, char const *extra);
+static LONG recursive_delete_key(HKEY key);
+static LONG recursive_delete_keyA(HKEY base, char const *name);
+static LONG recursive_delete_keyW(HKEY base, WCHAR const *name);
+
+/***********************************************************************
+ * register_interfaces
+ */
+static HRESULT register_interfaces(struct regsvr_interface const *list)
+{
+ LONG res = ERROR_SUCCESS;
+ HKEY interface_key;
+
+ res = RegCreateKeyExW(HKEY_CLASSES_ROOT, interface_keyname, 0, NULL, 0,
+ KEY_READ | KEY_WRITE, NULL, &interface_key, NULL);
+ if (res != ERROR_SUCCESS) goto error_return;
+
+ for (; res == ERROR_SUCCESS && list->iid; ++list) {
+ WCHAR buf[39];
+ HKEY iid_key;
+
+ StringFromGUID2(list->iid, buf, 39);
+ res = RegCreateKeyExW(interface_key, buf, 0, NULL, 0,
+ KEY_READ | KEY_WRITE, NULL, &iid_key, NULL);
+ if (res != ERROR_SUCCESS) goto error_close_interface_key;
+
+ if (list->name) {
+ res = RegSetValueExA(iid_key, NULL, 0, REG_SZ,
+ (CONST BYTE*)(list->name),
+ strlen(list->name) + 1);
+ if (res != ERROR_SUCCESS) goto error_close_iid_key;
+ }
+
+ if (list->base_iid) {
+ register_key_guid(iid_key, base_ifa_keyname, list->base_iid);
+ if (res != ERROR_SUCCESS) goto error_close_iid_key;
+ }
+
+ if (0 <= list->num_methods) {
+ static WCHAR const fmt[3] = { '%', 'd', 0 };
+ HKEY key;
+
+ res = RegCreateKeyExW(iid_key, num_methods_keyname, 0, NULL, 0,
+ KEY_READ | KEY_WRITE, NULL, &key, NULL);
+ if (res != ERROR_SUCCESS) goto error_close_iid_key;
+
+ wsprintfW(buf, fmt, list->num_methods);
+ res = RegSetValueExW(key, NULL, 0, REG_SZ,
+ (CONST BYTE*)buf,
+ (lstrlenW(buf) + 1) * sizeof(WCHAR));
+ RegCloseKey(key);
+
+ if (res != ERROR_SUCCESS) goto error_close_iid_key;
+ }
+
+ if (list->ps_clsid) {
+ register_key_guid(iid_key, ps_clsid_keyname, list->ps_clsid);
+ if (res != ERROR_SUCCESS) goto error_close_iid_key;
+ }
+
+ if (list->ps_clsid32) {
+ register_key_guid(iid_key, ps_clsid32_keyname, list->ps_clsid32);
+ if (res != ERROR_SUCCESS) goto error_close_iid_key;
+ }
+
+ error_close_iid_key:
+ RegCloseKey(iid_key);
+ }
+
+error_close_interface_key:
+ RegCloseKey(interface_key);
+error_return:
+ return res != ERROR_SUCCESS ? HRESULT_FROM_WIN32(res) : S_OK;
+}
+
+/***********************************************************************
+ * unregister_interfaces
+ */
+static HRESULT unregister_interfaces(struct regsvr_interface const *list)
+{
+ LONG res = ERROR_SUCCESS;
+ HKEY interface_key;
+
+ res = RegOpenKeyExW(HKEY_CLASSES_ROOT, interface_keyname, 0,
+ KEY_READ | KEY_WRITE, &interface_key);
+ if (res == ERROR_FILE_NOT_FOUND) return S_OK;
+ if (res != ERROR_SUCCESS) goto error_return;
+
+ for (; res == ERROR_SUCCESS && list->iid; ++list) {
+ WCHAR buf[39];
+
+ StringFromGUID2(list->iid, buf, 39);
+ res = recursive_delete_keyW(interface_key, buf);
+ }
+
+ RegCloseKey(interface_key);
+error_return:
+ return res != ERROR_SUCCESS ? HRESULT_FROM_WIN32(res) : S_OK;
+}
+
+/***********************************************************************
+ * register_coclasses
+ */
+static HRESULT register_coclasses(struct regsvr_coclass const *list)
+{
+ LONG res = ERROR_SUCCESS;
+ HKEY coclass_key;
+
+ res = RegCreateKeyExW(HKEY_CLASSES_ROOT, clsid_keyname, 0, NULL, 0,
+ KEY_READ | KEY_WRITE, NULL, &coclass_key, NULL);
+ if (res != ERROR_SUCCESS) goto error_return;
+
+ for (; res == ERROR_SUCCESS && list->clsid; ++list) {
+ WCHAR buf[39];
+ HKEY clsid_key;
+
+ StringFromGUID2(list->clsid, buf, 39);
+ res = RegCreateKeyExW(coclass_key, buf, 0, NULL, 0,
+ KEY_READ | KEY_WRITE, NULL, &clsid_key, NULL);
+ if (res != ERROR_SUCCESS) goto error_close_coclass_key;
+
+ if (list->name) {
+ res = RegSetValueExA(clsid_key, NULL, 0, REG_SZ,
+ (CONST BYTE*)(list->name),
+ strlen(list->name) + 1);
+ if (res != ERROR_SUCCESS) goto error_close_clsid_key;
+ }
+
+ if (list->ips) {
+ res = register_key_defvalueA(clsid_key, ips_keyname, list->ips);
+ if (res != ERROR_SUCCESS) goto error_close_clsid_key;
+ }
+
+ if (list->ips32) {
+ HKEY ips32_key;
+
+ res = RegCreateKeyExW(clsid_key, ips32_keyname, 0, NULL, 0,
+ KEY_READ | KEY_WRITE, NULL,
+ &ips32_key, NULL);
+ if (res != ERROR_SUCCESS) goto error_close_clsid_key;
+
+ res = RegSetValueExA(ips32_key, NULL, 0, REG_SZ,
+ (CONST BYTE*)list->ips32,
+ lstrlenA(list->ips32) + 1);
+ if (res == ERROR_SUCCESS && list->ips32_tmodel)
+ res = RegSetValueExA(ips32_key, tmodel_valuename, 0, REG_SZ,
+ (CONST BYTE*)list->ips32_tmodel,
+ strlen(list->ips32_tmodel) + 1);
+ RegCloseKey(ips32_key);
+ if (res != ERROR_SUCCESS) goto error_close_clsid_key;
+ }
+
+ if (list->progid) {
+ res = register_key_defvalueA(clsid_key, progid_keyname,
+ list->progid);
+ if (res != ERROR_SUCCESS) goto error_close_clsid_key;
+
+ res = register_progid(buf, list->progid, NULL,
+ list->name, list->progid_extra);
+ if (res != ERROR_SUCCESS) goto error_close_clsid_key;
+ }
+
+ if (list->viprogid) {
+ res = register_key_defvalueA(clsid_key, viprogid_keyname,
+ list->viprogid);
+ if (res != ERROR_SUCCESS) goto error_close_clsid_key;
+
+ res = register_progid(buf, list->viprogid, list->progid,
+ list->name, list->progid_extra);
+ if (res != ERROR_SUCCESS) goto error_close_clsid_key;
+ }
+
+ error_close_clsid_key:
+ RegCloseKey(clsid_key);
+ }
+
+error_close_coclass_key:
+ RegCloseKey(coclass_key);
+error_return:
+ return res != ERROR_SUCCESS ? HRESULT_FROM_WIN32(res) : S_OK;
+}
+
+/***********************************************************************
+ * unregister_coclasses
+ */
+static HRESULT unregister_coclasses(struct regsvr_coclass const *list)
+{
+ LONG res = ERROR_SUCCESS;
+ HKEY coclass_key;
+
+ res = RegOpenKeyExW(HKEY_CLASSES_ROOT, clsid_keyname, 0,
+ KEY_READ | KEY_WRITE, &coclass_key);
+ if (res == ERROR_FILE_NOT_FOUND) return S_OK;
+ if (res != ERROR_SUCCESS) goto error_return;
+
+ for (; res == ERROR_SUCCESS && list->clsid; ++list) {
+ WCHAR buf[39];
+
+ StringFromGUID2(list->clsid, buf, 39);
+ res = recursive_delete_keyW(coclass_key, buf);
+ if (res != ERROR_SUCCESS) goto error_close_coclass_key;
+
+ if (list->progid) {
+ res = recursive_delete_keyA(HKEY_CLASSES_ROOT, list->progid);
+ if (res != ERROR_SUCCESS) goto error_close_coclass_key;
+ }
+
+ if (list->viprogid) {
+ res = recursive_delete_keyA(HKEY_CLASSES_ROOT, list->viprogid);
+ if (res != ERROR_SUCCESS) goto error_close_coclass_key;
+ }
+ }
+
+error_close_coclass_key:
+ RegCloseKey(coclass_key);
+error_return:
+ return res != ERROR_SUCCESS ? HRESULT_FROM_WIN32(res) : S_OK;
+}
+
+/***********************************************************************
+ * register_mediatypes_parsing
+ */
+static HRESULT register_mediatypes_parsing(struct regsvr_mediatype_parsing const *list)
+{
+ LONG res = ERROR_SUCCESS;
+ HKEY mediatype_key;
+ WCHAR buf[39];
+ int i;
+
+ res = RegCreateKeyExW(HKEY_CLASSES_ROOT, mediatype_name, 0, NULL, 0,
+ KEY_READ | KEY_WRITE, NULL, &mediatype_key, NULL);
+ if (res != ERROR_SUCCESS) return HRESULT_FROM_WIN32(res);
+
+ for (; res == ERROR_SUCCESS && list->majortype; ++list) {
+ HKEY majortype_key = NULL;
+ HKEY subtype_key = NULL;
+
+ StringFromGUID2(list->majortype, buf, 39);
+ res = RegCreateKeyExW(mediatype_key, buf, 0, NULL, 0,
+ KEY_READ | KEY_WRITE, NULL, &majortype_key, NULL);
+ if (res != ERROR_SUCCESS) goto error_close_keys;
+
+ StringFromGUID2(list->subtype, buf, 39);
+ res = RegCreateKeyExW(majortype_key, buf, 0, NULL, 0,
+ KEY_READ | KEY_WRITE, NULL, &subtype_key, NULL);
+ if (res != ERROR_SUCCESS) goto error_close_keys;
+
+ StringFromGUID2(&CLSID_AsyncReader, buf, 39);
+ res = RegSetValueExW(subtype_key, sourcefilter_valuename, 0, REG_SZ, (CONST BYTE*)buf,
+ (lstrlenW(buf) + 1) * sizeof(WCHAR));
+ if (res != ERROR_SUCCESS) goto error_close_keys;
+
+ for(i = 0; list->line[i]; i++) {
+ char buffer[3];
+ wsprintfA(buffer, "%d", i);
+ res = RegSetValueExA(subtype_key, buffer, 0, REG_SZ, (CONST BYTE*)list->line[i],
+ lstrlenA(list->line[i]));
+ if (res != ERROR_SUCCESS) goto error_close_keys;
+ }
+
+error_close_keys:
+ if (majortype_key)
+ RegCloseKey(majortype_key);
+ if (subtype_key)
+ RegCloseKey(subtype_key);
+ }
+
+ RegCloseKey(mediatype_key);
+
+ return res != ERROR_SUCCESS ? HRESULT_FROM_WIN32(res) : S_OK;
+}
+
+/***********************************************************************
+ * register_mediatypes_extension
+ */
+static HRESULT register_mediatypes_extension(struct regsvr_mediatype_extension const *list)
+{
+ LONG res = ERROR_SUCCESS;
+ HKEY mediatype_key;
+ HKEY extensions_root_key = NULL;
+ WCHAR buf[39];
+
+ res = RegCreateKeyExW(HKEY_CLASSES_ROOT, mediatype_name, 0, NULL, 0,
+ KEY_READ | KEY_WRITE, NULL, &mediatype_key, NULL);
+ if (res != ERROR_SUCCESS) return HRESULT_FROM_WIN32(res);
+
+ res = RegCreateKeyExW(mediatype_key, extensions_keyname, 0, NULL, 0,
+ KEY_READ | KEY_WRITE, NULL, &extensions_root_key, NULL);
+ if (res != ERROR_SUCCESS) goto error_return;
+
+ for (; res == ERROR_SUCCESS && list->majortype; ++list) {
+ HKEY extension_key;
+
+ res = RegCreateKeyExA(extensions_root_key, list->extension, 0, NULL, 0,
+ KEY_READ | KEY_WRITE, NULL, &extension_key, NULL);
+ if (res != ERROR_SUCCESS) break;
+
+ StringFromGUID2(list->majortype, buf, 39);
+ res = RegSetValueExW(extension_key, mediatype_name, 0, REG_SZ, (CONST BYTE*)buf,
+ (lstrlenW(buf) + 1) * sizeof(WCHAR));
+ if (res != ERROR_SUCCESS) goto error_close_key;
+
+ StringFromGUID2(list->subtype, buf, 39);
+ res = RegSetValueExW(extension_key, subtype_valuename, 0, REG_SZ, (CONST BYTE*)buf,
+ (lstrlenW(buf) + 1) * sizeof(WCHAR));
+ if (res != ERROR_SUCCESS) goto error_close_key;
+
+ StringFromGUID2(&CLSID_AsyncReader, buf, 39);
+ res = RegSetValueExW(extension_key, sourcefilter_valuename, 0, REG_SZ, (CONST BYTE*)buf,
+ (lstrlenW(buf) + 1) * sizeof(WCHAR));
+ if (res != ERROR_SUCCESS) goto error_close_key;
+
+error_close_key:
+ RegCloseKey(extension_key);
+ }
+
+error_return:
+ RegCloseKey(mediatype_key);
+ if (extensions_root_key)
+ RegCloseKey(extensions_root_key);
+
+ return res != ERROR_SUCCESS ? HRESULT_FROM_WIN32(res) : S_OK;
+}
+
+/***********************************************************************
+ * unregister_mediatypes_parsing
+ */
+static HRESULT unregister_mediatypes_parsing(struct regsvr_mediatype_parsing const *list)
+{
+ LONG res;
+ HKEY mediatype_key;
+ HKEY majortype_key;
+ WCHAR buf[39];
+
+ res = RegOpenKeyExW(HKEY_CLASSES_ROOT, mediatype_name, 0,
+ KEY_READ | KEY_WRITE, &mediatype_key);
+ if (res == ERROR_FILE_NOT_FOUND) return S_OK;
+ if (res != ERROR_SUCCESS) return HRESULT_FROM_WIN32(res);
+
+ for (; res == ERROR_SUCCESS && list->majortype; ++list) {
+ StringFromGUID2(list->majortype, buf, 39);
+ res = RegOpenKeyExW(mediatype_key, buf, 0,
+ KEY_READ | KEY_WRITE, &majortype_key);
+ if (res == ERROR_FILE_NOT_FOUND) {
+ res = ERROR_SUCCESS;
+ continue;
+ }
+ if (res != ERROR_SUCCESS) break;
+
+ StringFromGUID2(list->subtype, buf, 39);
+ res = recursive_delete_keyW(majortype_key, buf);
+ if (res == ERROR_FILE_NOT_FOUND) res = ERROR_SUCCESS;
+
+ /* Removed majortype key if there is no more subtype key */
+ res = RegDeleteKeyW(majortype_key, 0);
+ if (res == ERROR_ACCESS_DENIED) res = ERROR_SUCCESS;
+
+ RegCloseKey(majortype_key);
+ }
+
+ RegCloseKey(mediatype_key);
+
+ return res != ERROR_SUCCESS ? HRESULT_FROM_WIN32(res) : S_OK;
+}
+
+/***********************************************************************
+ * unregister_mediatypes_extension
+ */
+static HRESULT unregister_mediatypes_extension(struct regsvr_mediatype_extension const *list)
+{
+ LONG res;
+ HKEY mediatype_key;
+ HKEY extensions_root_key = NULL;
+
+ res = RegOpenKeyExW(HKEY_CLASSES_ROOT, mediatype_name, 0,
+ KEY_READ | KEY_WRITE, &mediatype_key);
+ if (res == ERROR_FILE_NOT_FOUND) return S_OK;
+ if (res != ERROR_SUCCESS) return HRESULT_FROM_WIN32(res);
+
+ res = RegOpenKeyExW(mediatype_key, extensions_keyname, 0,
+ KEY_READ | KEY_WRITE, &extensions_root_key);
+ if (res == ERROR_FILE_NOT_FOUND)
+ res = ERROR_SUCCESS;
+ else if (res == ERROR_SUCCESS)
+ for (; res == ERROR_SUCCESS && list->majortype; ++list) {
+ res = recursive_delete_keyA(extensions_root_key, list->extension);
+ if (res == ERROR_FILE_NOT_FOUND) res = ERROR_SUCCESS;
+ }
+
+ RegCloseKey(mediatype_key);
+ if (extensions_root_key)
+ RegCloseKey(extensions_root_key);
+
+ return res != ERROR_SUCCESS ? HRESULT_FROM_WIN32(res) : S_OK;
+}
+
+/***********************************************************************
+ * register_filters
+ */
+static HRESULT register_filters(struct regsvr_filter const *list)
+{
+ HRESULT hr;
+ IFilterMapper2* pFM2 = NULL;
+
+ CoInitialize(NULL);
+ hr = CoCreateInstance(&CLSID_FilterMapper2, NULL, CLSCTX_INPROC_SERVER, &IID_IFilterMapper2, (LPVOID*)&pFM2);
+
+ if (SUCCEEDED(hr)) {
+ for (; SUCCEEDED(hr) && list->clsid; ++list) {
+ REGFILTER2 rf2;
+ REGFILTERPINS2* prfp2;
+ int i;
+
+ for (i = 0; list->pins[i].flags != 0xFFFFFFFF; i++) ;
+ rf2.dwVersion = 2;
+ rf2.dwMerit = list->merit;
+ rf2.u.s1.cPins2 = i;
+ rf2.u.s1.rgPins2 = prfp2 = (REGFILTERPINS2*) CoTaskMemAlloc(i*sizeof(REGFILTERPINS2));
+ if (!prfp2) {
+ hr = E_OUTOFMEMORY;
+ break;
+ }
+ for (i = 0; list->pins[i].flags != 0xFFFFFFFF; i++) {
+ REGPINTYPES* lpMediatype;
+ CLSID* lpClsid;
+ int j, nbmt;
+
+ for (nbmt = 0; list->pins[i].mediatypes[nbmt].majortype; nbmt++) ;
+ /* Allocate a single buffer for regpintypes struct and clsids */
+ lpMediatype = (REGPINTYPES*) CoTaskMemAlloc(nbmt*(sizeof(REGPINTYPES) + 2*sizeof(CLSID)));
+ if (!lpMediatype) {
+ hr = E_OUTOFMEMORY;
+ break;
+ }
+ lpClsid = (CLSID*) (lpMediatype + nbmt);
+ for (j = 0; j < nbmt; j++) {
+ (lpMediatype + j)->clsMajorType = lpClsid + j*2;
+ memcpy(lpClsid + j*2, list->pins[i].mediatypes[j].majortype, sizeof(CLSID));
+ (lpMediatype + j)->clsMinorType = lpClsid + j*2 + 1;
+ if (list->pins[i].mediatypes[j].subtype)
+ memcpy(lpClsid + j*2 + 1, list->pins[i].mediatypes[j].subtype, sizeof(CLSID));
+ else {
+ /* Subtype are often a combination of major type + fourcc/tag */
+ memcpy(lpClsid + j*2 + 1, list->pins[i].mediatypes[j].majortype, sizeof(CLSID));
+ *(DWORD*)(lpClsid + j*2 + 1) = list->pins[i].mediatypes[j].fourcc;
+ }
+ }
+ prfp2[i].dwFlags = list->pins[i].flags;
+ prfp2[i].cInstances = 0;
+ prfp2[i].nMediaTypes = j;
+ prfp2[i].lpMediaType = lpMediatype;
+ prfp2[i].nMediums = 0;
+ prfp2[i].lpMedium = NULL;
+ prfp2[i].clsPinCategory = NULL;
+ }
+
+ if (FAILED(hr)) {
+ ERR("failed to register with hresult 0x%lx\n", hr);
+ CoTaskMemFree(prfp2);
+ break;
+ }
+
+ hr = IFilterMapper2_RegisterFilter(pFM2, list->clsid, list->name, NULL, list->category, NULL, &rf2);
+
+ while (i) {
+ CoTaskMemFree((REGPINTYPES*)prfp2[i-1].lpMediaType);
+ i--;
+ }
+ CoTaskMemFree(prfp2);
+ }
+ }
+
+ if (pFM2)
+ IFilterMapper2_Release(pFM2);
+
+ CoUninitialize();
+
+ return hr;
+}
+
+/***********************************************************************
+ * unregister_filters
+ */
+static HRESULT unregister_filters(struct regsvr_filter const *list)
+{
+ HRESULT hr;
+ IFilterMapper2* pFM2;
+
+ CoInitialize(NULL);
+
+ hr = CoCreateInstance(&CLSID_FilterMapper2, NULL, CLSCTX_INPROC_SERVER, &IID_IFilterMapper2, (LPVOID*)&pFM2);
+
+ if (SUCCEEDED(hr)) {
+ for (; SUCCEEDED(hr) && list->clsid; ++list)
+ hr = IFilterMapper2_UnregisterFilter(pFM2, list->category, NULL, list->clsid);
+ IFilterMapper2_Release(pFM2);
+ }
+
+ CoUninitialize();
+
+ return hr;
+}
+
+/***********************************************************************
+ * regsvr_key_guid
+ */
+static LONG register_key_guid(HKEY base, WCHAR const *name, GUID const *guid)
+{
+ WCHAR buf[39];
+
+ StringFromGUID2(guid, buf, 39);
+ return register_key_defvalueW(base, name, buf);
+}
+
+/***********************************************************************
+ * regsvr_key_defvalueW
+ */
+static LONG register_key_defvalueW(
+ HKEY base,
+ WCHAR const *name,
+ WCHAR const *value)
+{
+ LONG res;
+ HKEY key;
+
+ res = RegCreateKeyExW(base, name, 0, NULL, 0,
+ KEY_READ | KEY_WRITE, NULL, &key, NULL);
+ if (res != ERROR_SUCCESS) return res;
+ res = RegSetValueExW(key, NULL, 0, REG_SZ, (CONST BYTE*)value,
+ (lstrlenW(value) + 1) * sizeof(WCHAR));
+ RegCloseKey(key);
+ return res;
+}
+
+/***********************************************************************
+ * regsvr_key_defvalueA
+ */
+static LONG register_key_defvalueA(
+ HKEY base,
+ WCHAR const *name,
+ char const *value)
+{
+ LONG res;
+ HKEY key;
+
+ res = RegCreateKeyExW(base, name, 0, NULL, 0,
+ KEY_READ | KEY_WRITE, NULL, &key, NULL);
+ if (res != ERROR_SUCCESS) return res;
+ res = RegSetValueExA(key, NULL, 0, REG_SZ, (CONST BYTE*)value,
+ lstrlenA(value) + 1);
+ RegCloseKey(key);
+ return res;
+}
+
+/***********************************************************************
+ * regsvr_progid
+ */
+static LONG register_progid(
+ WCHAR const *clsid,
+ char const *progid,
+ char const *curver_progid,
+ char const *name,
+ char const *extra)
+{
+ LONG res;
+ HKEY progid_key;
+
+ res = RegCreateKeyExA(HKEY_CLASSES_ROOT, progid, 0,
+ NULL, 0, KEY_READ | KEY_WRITE, NULL,
+ &progid_key, NULL);
+ if (res != ERROR_SUCCESS) return res;
+
+ if (name) {
+ res = RegSetValueExA(progid_key, NULL, 0, REG_SZ,
+ (CONST BYTE*)name, strlen(name) + 1);
+ if (res != ERROR_SUCCESS) goto error_close_progid_key;
+ }
+
+ if (clsid) {
+ res = register_key_defvalueW(progid_key, clsid_keyname, clsid);
+ if (res != ERROR_SUCCESS) goto error_close_progid_key;
+ }
+
+ if (curver_progid) {
+ res = register_key_defvalueA(progid_key, curver_keyname,
+ curver_progid);
+ if (res != ERROR_SUCCESS) goto error_close_progid_key;
+ }
+
+ if (extra) {
+ HKEY extra_key;
+
+ res = RegCreateKeyExA(progid_key, extra, 0,
+ NULL, 0, KEY_READ | KEY_WRITE, NULL,
+ &extra_key, NULL);
+ if (res == ERROR_SUCCESS)
+ RegCloseKey(extra_key);
+ }
+
+error_close_progid_key:
+ RegCloseKey(progid_key);
+ return res;
+}
+
+/***********************************************************************
+ * recursive_delete_key
+ */
+static LONG recursive_delete_key(HKEY key)
+{
+ LONG res;
+ WCHAR subkey_name[MAX_PATH];
+ DWORD cName;
+ HKEY subkey;
+
+ for (;;) {
+ cName = sizeof(subkey_name) / sizeof(WCHAR);
+ res = RegEnumKeyExW(key, 0, subkey_name, &cName,
+ NULL, NULL, NULL, NULL);
+ if (res != ERROR_SUCCESS && res != ERROR_MORE_DATA) {
+ res = ERROR_SUCCESS; /* presumably we're done enumerating */
+ break;
+ }
+ res = RegOpenKeyExW(key, subkey_name, 0,
+ KEY_READ | KEY_WRITE, &subkey);
+ if (res == ERROR_FILE_NOT_FOUND) continue;
+ if (res != ERROR_SUCCESS) break;
+
+ res = recursive_delete_key(subkey);
+ RegCloseKey(subkey);
+ if (res != ERROR_SUCCESS) break;
+ }
+
+ if (res == ERROR_SUCCESS) res = RegDeleteKeyW(key, 0);
+ return res;
+}
+
+/***********************************************************************
+ * recursive_delete_keyA
+ */
+static LONG recursive_delete_keyA(HKEY base, char const *name)
+{
+ LONG res;
+ HKEY key;
+
+ res = RegOpenKeyExA(base, name, 0, KEY_READ | KEY_WRITE, &key);
+ if (res == ERROR_FILE_NOT_FOUND) return ERROR_SUCCESS;
+ if (res != ERROR_SUCCESS) return res;
+ res = recursive_delete_key(key);
+ RegCloseKey(key);
+ return res;
+}
+
+/***********************************************************************
+ * recursive_delete_keyW
+ */
+static LONG recursive_delete_keyW(HKEY base, WCHAR const *name)
+{
+ LONG res;
+ HKEY key;
+
+ res = RegOpenKeyExW(base, name, 0, KEY_READ | KEY_WRITE, &key);
+ if (res == ERROR_FILE_NOT_FOUND) return ERROR_SUCCESS;
+ if (res != ERROR_SUCCESS) return res;
+ res = recursive_delete_key(key);
+ RegCloseKey(key);
+ return res;
+}
+
+/***********************************************************************
+ * coclass list
+ */
+static struct regsvr_coclass const coclass_list[] = {
+ { &CLSID_VfwCapture,
+ "Video input for wine",
+ NULL,
+ "qcap.dll",
+ "Both"
+ },
+ { &CLSID_CaptureGraphBuilder,
+ "Capture Graph Builder",
+ NULL,
+ "qcap.dll",
+ "Both"
+ },
+ { NULL } /* list terminator */
+};
+
+/***********************************************************************
+ * interface list
+ */
+
+static struct regsvr_interface const interface_list[] = {
+ { NULL } /* list terminator */
+};
+
+/***********************************************************************
+ * mediatype list
+ */
+
+static struct regsvr_mediatype_parsing const mediatype_parsing_list[] = {
+ { NULL } /* list terminator */
+};
+
+/***********************************************************************
+ * mediatype list
+ */
+
+static struct regsvr_mediatype_extension const mediatype_extension_list[] = {
+ { NULL } /* list terminator */
+};
+
+/***********************************************************************
+ * filter list
+ */
+
+static struct regsvr_filter const filter_list[] = {
+ { &CLSID_VfwCapture,
+ &CLSID_VideoInputDeviceCategory,
+ { 'V','i','d','e','o',' ','4',' ','w','i','n','e',0 },
+ 0x400000,
+ { { 0xFFFFFFFF } }
+ },
+ { NULL } /* list terminator */
+};
+
+/***********************************************************************
+ * DllRegisterServer (QCAP.@)
+ */
+HRESULT WINAPI QCAP_DllRegisterServer(void)
+{
+ HRESULT hr;
+
+ TRACE("\n");
+
+ hr = register_coclasses(coclass_list);
+ if (SUCCEEDED(hr))
+ hr = register_interfaces(interface_list);
+ if (SUCCEEDED(hr))
+ hr = register_mediatypes_parsing(mediatype_parsing_list);
+ if (SUCCEEDED(hr))
+ hr = register_mediatypes_extension(mediatype_extension_list);
+ if (SUCCEEDED(hr))
+ hr = register_filters(filter_list);
+ return hr;
+}
+
+/***********************************************************************
+ * DllUnregisterServer (QCAP.@)
+ */
+HRESULT WINAPI QCAP_DllUnregisterServer(void)
+{
+ HRESULT hr;
+
+ TRACE("\n");
+
+ hr = unregister_filters(filter_list);
+ if (SUCCEEDED(hr))
+ hr = unregister_coclasses(coclass_list);
+ if (SUCCEEDED(hr))
+ hr = unregister_interfaces(interface_list);
+ if (SUCCEEDED(hr))
+ hr = unregister_mediatypes_parsing(mediatype_parsing_list);
+ if (SUCCEEDED(hr))
+ hr = unregister_mediatypes_extension(mediatype_extension_list);
+ return hr;
+}
diff -Nru wine-old/dlls/qcap/v4l.c wine-new/dlls/qcap/v4l.c
--- wine-old/dlls/qcap/v4l.c 1970-01-01 01:00:00.000000000 +0100
+++ wine-new/dlls/qcap/v4l.c 2005-05-01 02:08:03.000000000 +0200
@@ -0,0 +1,667 @@
+/* DirectShow capture services (QCAP.DLL)
+ *
+ * Copyright 2005 Maarten Lankhorst
+ *
+ * This file contains the part of the vfw capture interface that
+ * does the actual Video4Linux(1/2) stuff required for capturing
+ * and setting/getting media format..
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2.1 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ */
+
+#include "config.h"
+#ifdef HAVE_LINUX_VIDEODEV_H
+
+#define NONAMELESSSTRUCT
+#define NONAMELESSUNION
+#include "qcap_private.h"
+#include "pin.h"
+
+#include "uuids.h"
+#include "mmreg.h"
+#include "vfwmsgs.h"
+#include "amvideo.h"
+#include "windef.h"
+#include "winbase.h"
+#include "dshow.h"
+#include "strmif.h"
+#include "ddraw.h"
+
+#include "wine/unicode.h"
+#include "wine/debug.h"
+
+#include <stdio.h>
+#include <sys/ioctl.h>
+#include <sys/stat.h>
+#include <sys/mman.h>
+#include <sys/errno.h>
+#include <sys/time.h>
+#include <asm/types.h>
+#include <linux/videodev.h>
+#include <fcntl.h>
+#include <unistd.h>
+#include "winnls.h"
+#include "capture.h"
+
+WINE_DEFAULT_DEBUG_CHANNEL(qcap_v4l);
+
+/* NOTE: Currently the V4L2 is unsupported and I'm not working on it,
+ * I have undefined HAVE_V4L2 so that I could leave the code in place
+ * without having to update it..
+ */
+#undef HAVE_V4L2
+
+struct CaptureBox;
+
+typedef void (* Renderer)(struct CaptureBox *, LPBYTE bufferin, LPBYTE stream);
+
+typedef struct CaptureBox {
+/* Dunno what to put in here? */
+ UINT width, height, bitDepth, fps;
+ UINT outputwidth, outputheight;
+
+ CRITICAL_SECTION CritSect;
+
+ IPin *pOut;
+ int fd, isV4l2, mmap;
+ int iscommitted, stopped;
+
+/* mmap (V4l1) */
+ struct video_mmap *grab_buf;
+ struct video_mbuf gb_buffers;
+ void *pmap;
+ int buffers, palette;
+
+/* read (V4l1) */
+ int imagesize;
+ char * grab_data;
+
+ int curframe;
+
+ HANDLE thread;
+ Renderer renderer;
+} CaptureBox;
+
+struct renderlist {
+ int depth;
+ char* name;
+ Renderer renderer;
+};
+
+static void renderer_RGB24(CaptureBox *capBox, LPBYTE bufferin, LPBYTE stream);
+static void renderer_RGB32(CaptureBox *capBox, LPBYTE bufferin, LPBYTE stream);
+
+static const struct renderlist renderlist_V4l[] = {
+ { 0, "NULL renderer", NULL },
+ { 8, "Gray scales", NULL }, /* 1, Intentional not suported */
+ { 0, "High 240 cube (BT848)", NULL }, /* 2, Intentional not supported */
+ { 16, "16 bit RGB (565)", NULL }, /* 3, Intentional not supported */
+ { 24, "24 bit RGB values", renderer_RGB24 }, /* 4, Supported, and tested */
+ { 32, "32 bit RGB values", renderer_RGB32 }, /* 5, Supported, and tested */
+ { 16, "15 bit RGB (555)", NULL }, /* 6, Intentional not supported */
+ { 16, "YUV 422 (Not P)", NULL }, /* 7, Should be supported */
+ { 16, "YUYV (Not P)", NULL }, /* 8, Should be supported */
+ { 16, "UYVY (Not P)", NULL }, /* 9, Should be supported */
+ { 12, "YUV 420 (Not P)", NULL }, /* 10, Should be supported */
+ { 0, "Raw capturing (BT848)", NULL }, /* 11, Intentional not supported */
+ { 16, "YUV 422 (Planar)", NULL }, /* 12, Should be supported */
+ { 12, "YUV 411 (Planar)", NULL }, /* 13, Should be supported */
+ { 12, "YUV 420 (Planar)", NULL }, /* 14, Should be supported */
+ { 10, "YUV 410 (Planar)", NULL }, /* 15, Should be supported */
+ { 0, NULL, NULL },
+};
+
+const int fallback_V4l[] = { 4, 5, 7, 8, 9, 12, 13, 14, 15, 10 };
+/* Fallback: First try raw formats, then yuv, then yuv with a color channel missing */
+
+static int xioctl(int fd, int request, void * arg)
+{
+ int r;
+
+ do r = ioctl (fd, request, arg);
+ while (-1 == r && EINTR == errno);
+
+ return r;
+}
+
+HRESULT Capture_Initialise(void ** pointer, IPin * pOut, USHORT card)
+{
+ CaptureBox * capBox = CoTaskMemAlloc(sizeof(CaptureBox));
+ char device[128];
+ struct stat st;
+#ifdef HAVE_V4L2
+ struct v4l2_capability caps;
+#endif
+ struct video_capability capa;
+ struct video_picture pict;
+ struct video_window window;
+
+ /* Because I don't want to change every return failure,
+ I'll let VfwCapture clean up if *pointer != NULL
+ and the call failed..
+ */
+
+ *pointer = NULL; /* Until we're succesful */
+
+ if (!capBox) {
+ ERR("Out of memory\n");
+ return E_OUTOFMEMORY;
+ }
+
+ sprintf(device, "/dev/video%i", card);
+
+ FIXME("(): Allow use of more then 1 video adapter, like I did in avicap\n");
+
+ if (stat (device, &st) == -1) {
+ ERR("%s: %s\n", device, strerror(errno));
+ CoTaskMemFree(capBox);
+ return E_FAIL;
+ }
+
+ if (!S_ISCHR (st.st_mode)) {
+ ERR("%s: Not a device\n", device);
+ CoTaskMemFree(capBox);
+ return E_FAIL;
+ }
+
+ capBox->fd = open(device, O_RDWR | O_NONBLOCK);
+ if (capBox->fd == -1) {
+ ERR("%s: Failed to open: %s\n", device, strerror(errno));
+ CoTaskMemFree(capBox);
+ return E_FAIL;
+ }
+
+#ifdef HAVE_V4L2
+ if (xioctl(capBox->fd, VIDIOC_QUERYCAP, &caps) >= 0) {
+ capBox->isV4l2 = 1;
+/* There are 5 reasons I don't add V4l2 support
+ * 1. Webcams don't use it
+ * 2. It is more complicated then V4l and more complicated then it needs to be
+ * 3. V4l2 devices can fall back to V4l
+ * 4. No one really misses it if I leave it out..
+ * 5. (MAIN REASON) *I* don't miss it, if you do, write V4l2 support yourself!
+ */
+ ERR("Tinkerer detected? Thou shalt not define HAVE_V4L2\n");
+ CoTaskMemFree(capBox);
+ close(capBox->fd);
+ return E_FAIL;
+ } else
+#endif /* HAVE_V4L2 */
+ {
+ capBox->isV4l2 = 0;
+ memset(&capa, 0, sizeof(capa));
+
+ if (xioctl(capBox->fd, VIDIOCGCAP, &capa) == -1) {
+ if (errno != EINVAL && errno != 515) ERR("%s: Querying failed: %s\n", device, strerror(errno));
+ else ERR("%s: Querying failed: Not a V4L compatible device", device);
+ close(capBox->fd);
+ CoTaskMemFree(capBox);
+ return E_FAIL;
+ }
+
+ if (!(capa.type & VID_TYPE_CAPTURE)) {
+ ERR("%s: This is not a video capture device\n", device);
+ close(capBox->fd);
+ CoTaskMemFree(capBox);
+ return E_FAIL;
+ }
+ TRACE("Amount of inputs on %s: %d\n", capa.name, capa.channels);
+
+ if (xioctl(capBox->fd, VIDIOCGPICT, &pict) == -1) {
+ ERR("%s: Acquiring picture properties failed, this shouldn't happen..\n", device);
+ close(capBox->fd);
+ CoTaskMemFree(capBox);
+ return E_FAIL;
+ }
+
+ TRACE("%s: Suggested picture depth: %d, suggested picture palette: %d\n", device, pict.depth, pict.palette);
+ TRACE("%s: Hue %d, Color %d, Contrast %d\n", device, pict.hue,pict.colour,pict.contrast);
+/* This will most likely fail, and if it does we will fall back to 32 bits,
+ of which each first bit will be nothing but rubble, waste of bandwidth */
+ TRACE("%s: Suggested format: \"%s\"\n", device, renderlist_V4l[pict.palette].name);
+ if (!renderlist_V4l[pict.palette].renderer)
+ {
+ int palet = pict.palette, formatenum;
+ WARN("No renderer available for \"%s\", trying to fall back to defaults\n", renderlist_V4l[pict.palette].name);
+ capBox->renderer = NULL;
+ for (formatenum = 0; formatenum < (sizeof(fallback_V4l) / sizeof(int)); formatenum++) {
+ int currentrender = fallback_V4l[formatenum];
+ if (renderlist_V4l[currentrender].renderer == NULL) continue;
+ pict.depth = renderlist_V4l[currentrender].depth;
+ pict.palette = currentrender;
+ if (xioctl(capBox->fd, VIDIOCSPICT, &pict) == -1) {
+ TRACE("%s: Could not render with \"%s\"\n (%d)", device, renderlist_V4l[currentrender].name, currentrender);
+ continue;
+ }
+ TRACE("%s: Found a suitable renderer: \"%s\" (%d)\n", device, renderlist_V4l[currentrender].name, currentrender);
+ capBox->renderer = renderlist_V4l[currentrender].renderer;
+ break;
+ }
+ if (!capBox->renderer) {
+ close(capBox->fd);
+ CoTaskMemFree(capBox);
+ ERR("%s: This device wants to use \"%s\", but this format isn't available, and it didn't accept one of our other formats, GIVING UP!\n\n", device, renderlist_V4l[palet].name);
+ return E_FAIL;
+ }
+ } else {
+ TRACE("Using the suggested format\n");
+ capBox->renderer = renderlist_V4l[pict.palette].renderer;
+ }
+
+ memset(&window, 0, sizeof(window));
+ if (xioctl(capBox->fd, VIDIOCGWIN, &window) == -1) {
+ ERR("%s: Getting resolution failed.. (%s), giving up\n", device, strerror(errno));
+ close(capBox->fd);
+ CoTaskMemFree(capBox);
+ return E_FAIL;
+ }
+
+ capBox->height = window.height;
+ capBox->width = window.width;
+ capBox->bitDepth = 24;
+/* Try mmap */
+
+ if (xioctl(capBox->fd, VIDIOCGMBUF, &capBox->gb_buffers) != -1 && capBox->gb_buffers.frames) {
+ capBox->mmap = 1;
+ capBox->buffers = capBox->gb_buffers.frames;
+ if (capBox->gb_buffers.frames > 2) {
+ TRACE("%s: %d buffers granted, but only using 2 anyway\n", device, capBox->gb_buffers.frames);
+ capBox->buffers = 2;
+ } else if (capBox->gb_buffers.frames == 1) {
+ TRACE("%s: Only 1 buffer granted, which is below the tested 2\n", device);
+ } else {
+ TRACE("%s: Using %d buffers\n", device, capBox->gb_buffers.frames);
+ }
+ capBox->pmap = mmap(0, capBox->gb_buffers.size, PROT_READ|PROT_WRITE, MAP_SHARED, capBox->fd, 0);
+ if (capBox->pmap != MAP_FAILED) {
+ int i;
+ capBox->grab_buf = CoTaskMemAlloc(sizeof(struct video_mmap) * capBox->buffers);
+ if(!capBox->grab_buf) {
+ ERR("Out of memory?\n");
+ munmap(capBox->pmap, capBox->gb_buffers.size);
+ close(capBox->fd);
+ CoTaskMemFree(capBox);
+ return E_OUTOFMEMORY;
+ }
+
+ /* Setup mmap capture buffers. */
+ for (i = 0; i < capBox->buffers; i++) {
+ capBox->grab_buf[i].format = pict.palette;
+ capBox->grab_buf[i].frame = i;
+ capBox->grab_buf[i].width = capBox->width;
+ capBox->grab_buf[i].height = capBox->height;
+ if (xioctl(capBox->fd, VIDIOCMCAPTURE, &capBox->grab_buf[i]) == -1)
+ {
+ ERR("%s: Error setting up capture IOCTL: %s\n", device, strerror(errno));
+ Capture_Destroy(capBox);
+ return E_FAIL;
+ }
+ }
+ } else capBox->mmap = 0;
+ }
+ if (!capBox->mmap) {
+ capBox->buffers = 1;
+ capBox->imagesize = renderlist_V4l[pict.palette].depth * capBox->height * capBox->width / 8;
+ capBox->grab_data = CoTaskMemAlloc(capBox->imagesize);
+ if (!capBox->grab_data)
+ {
+ ERR("%s: Out of memory?\n", device);
+ close(capBox->fd);
+ CoTaskMemFree(capBox);
+ return E_OUTOFMEMORY;
+ }
+ }
+ }
+
+ capBox->pOut = pOut;
+ capBox->fps = 3;
+ capBox->stopped = 0;
+ capBox->curframe = 0;
+ InitializeCriticalSection(&capBox->CritSect);
+ *pointer = capBox;
+
+/* I had to set output to 320x240 for pin connection, the builtin filtergraph reconnect doesn't work
+ * we will try to change to this format later anyway, in Capture_SetFormat */
+ capBox->outputwidth = 320;
+ capBox->outputheight = 240;
+ capBox->iscommitted = 0;
+ TRACE("Using format: %d bits - %d x %d\n", capBox->bitDepth, capBox->width, capBox->height);
+ return S_OK;
+}
+
+HRESULT Capture_Destroy(void * pBox)
+{
+ CaptureBox *capBox = (CaptureBox *)pBox;
+/* Destroy file handlers etc? This function can't fail.. */
+#ifdef HAVE_V4L2
+ if (capBox->isV4l2) {
+/* We should destroy V4l2 stuff here but we don't use it */
+ } else
+#endif
+ if (capBox->mmap) {
+ munmap(capBox->pmap, capBox->gb_buffers.size);
+ CoTaskMemFree(capBox->grab_buf);
+ } else CoTaskMemFree(capBox->grab_data);
+ close(capBox->fd);
+ DeleteCriticalSection(&capBox->CritSect);
+ return S_OK;
+}
+
+HRESULT Capture_SetMediaType(void * pBox, AM_MEDIA_TYPE * mT)
+{
+ CaptureBox *capBox = (CaptureBox *)pBox;
+ TRACE("%p\n", capBox);
+ if (((VIDEOINFOHEADER *)mT->pbFormat)->bmiHeader.biBitCount != 24 ||
+ ((VIDEOINFOHEADER *)mT->pbFormat)->bmiHeader.biCompression != BI_RGB)
+ {
+ ERR("Invalid media type suggested\n");
+ return VFW_E_INVALIDMEDIATYPE;
+ }
+#ifdef HAVE_V4L2
+ if (capBox->isV4l2) {
+/* We should do V4l2 resizing here but V4l2 is not really supported.. */
+ } else
+#endif
+ {
+ struct video_window window;
+
+ }
+
+ capBox->outputwidth = ((VIDEOINFOHEADER *)mT->pbFormat)->bmiHeader.biWidth;
+ capBox->outputheight = ((VIDEOINFOHEADER *)mT->pbFormat)->bmiHeader.biHeight;
+
+ FIXME("%p -> (%p) - %d %d\n", capBox, mT, capBox->outputwidth, capBox->outputheight);
+ return S_OK;
+}
+
+HRESULT Capture_GetMediaType(void * pBox, AM_MEDIA_TYPE ** mT)
+{
+ CaptureBox *capBox = (CaptureBox *)pBox;
+ TRACE("%p\n", capBox);
+ VIDEOINFOHEADER *vi;
+ mT[0] = CoTaskMemAlloc(sizeof(AM_MEDIA_TYPE));
+ if (!mT[0]) return E_OUTOFMEMORY;
+ vi = CoTaskMemAlloc(sizeof(VIDEOINFOHEADER));
+ mT[0]->cbFormat = sizeof(VIDEOINFOHEADER);
+ if (!vi)
+ {
+ CoTaskMemFree(mT[0]);
+ return E_OUTOFMEMORY;
+ }
+ memcpy(&mT[0]->majortype, &MEDIATYPE_Video, sizeof(GUID));
+ memcpy(&mT[0]->subtype, &MEDIASUBTYPE_RGB24, sizeof(GUID));
+ memcpy(&mT[0]->formattype, &FORMAT_VideoInfo, sizeof(GUID));
+ mT[0]->bFixedSizeSamples = TRUE;
+ mT[0]->bTemporalCompression = FALSE;
+ mT[0]->pUnk = NULL;
+ mT[0]->lSampleSize = capBox->outputwidth * capBox->outputheight * capBox->bitDepth / 8;
+ TRACE("Output format: %d x %d - %d bits = %lu KB\n", capBox->outputwidth, capBox->outputheight, capBox->bitDepth, mT[0]->lSampleSize/1024);
+ vi->rcSource.left = 0; vi->rcSource.top = 0;
+ vi->rcTarget.left = 0; vi->rcTarget.top = 0;
+ vi->rcSource.right = capBox->width; vi->rcSource.bottom = capBox->height;
+ vi->rcTarget.right = capBox->outputwidth; vi->rcTarget.bottom = capBox->outputheight;
+ vi->dwBitRate = capBox->fps * mT[0]->lSampleSize;
+ vi->dwBitErrorRate = 0;
+ vi->AvgTimePerFrame = (LONGLONG)10000000.0 / (LONGLONG)capBox->fps;
+ vi->bmiHeader.biSize = 40;
+ vi->bmiHeader.biWidth = capBox->outputwidth;
+ vi->bmiHeader.biHeight = capBox->outputheight;
+ vi->bmiHeader.biPlanes = 1;
+ vi->bmiHeader.biBitCount = 24;
+ vi->bmiHeader.biCompression = BI_RGB;
+ vi->bmiHeader.biSizeImage = mT[0]->lSampleSize;
+ vi->bmiHeader.biClrUsed = vi->bmiHeader.biClrImportant = 0;
+ vi->bmiHeader.biXPelsPerMeter = 100;
+ vi->bmiHeader.biYPelsPerMeter = 100;
+ mT[0]->pbFormat = (void *)vi;
+ dump_AM_MEDIA_TYPE(mT[0]);
+ return S_OK;
+}
+
+static void renderer_RGB24(CaptureBox *capBox, LPBYTE bufferin, LPBYTE stream)
+{
+ int size = capBox->height * capBox->width * 3;
+ TRACE("%p\n", capBox);
+ memcpy(bufferin, stream, size);
+}
+
+static void renderer_RGB32(CaptureBox *capBox, LPBYTE bufferin, LPBYTE stream)
+{
+ int size = capBox->height * capBox->width * 4;
+ int pointer = 0, offset = 1;
+ TRACE("()\n");
+
+ /* I guess it's easier - but slower to do this per byte.. */
+
+ while (pointer + offset <= size) {
+ bufferin[pointer] = stream[pointer + offset];
+ pointer++;
+ bufferin[pointer] = stream[pointer + offset];
+ pointer++;
+ bufferin[pointer] = stream[pointer + offset];
+ pointer++;
+ offset++;
+ }
+}
+
+#define CHECKRANGE(a) (a > 255 ? 255 : (a < 0 ? 0 : a))
+
+static void Resize(CaptureBox * capBox, LPBYTE output, LPBYTE input)
+{
+/* outputheight < height */
+/* outputwidth < width */
+ int depth = capBox->bitDepth / 8;
+ int inoffset = 0, outoffset = (capBox->outputheight-1) * capBox->outputwidth * depth;
+ int ow = capBox->outputwidth * depth;
+ while (outoffset) {
+ outoffset -= ow;
+ int x;
+ for (x = 0; x < ow; x++)
+ output[outoffset + x] = input[inoffset + x];
+ inoffset += capBox->width * depth;
+ }
+}
+
+static void Capture_GetFrame(CaptureBox * capBox, LPBYTE * pInput)
+{
+#ifdef HAVE_V4L2
+ if (capBox->isV4l2) {
+ } else
+#endif
+ if (capBox->mmap) {
+ if (xioctl(capBox->fd, VIDIOCSYNC, &capBox->grab_buf[capBox->curframe]) == -1)
+ TRACE("Syncing ioctl failed: %s\n", strerror(errno));
+ *pInput = ((char *)capBox->pmap) + capBox->gb_buffers.offsets[capBox->curframe];
+ } else {
+ int retval;
+ while ((retval = read(capBox->fd, capBox->grab_data, capBox->imagesize)) == -1)
+ if (errno != EAGAIN) break;
+ if (retval == -1)
+ ERR("Error occured while reading from device: %s\n", strerror(errno));
+ *pInput = capBox->grab_data;
+ }
+}
+
+static void Capture_FreeFrame(CaptureBox * capBox)
+{
+#ifdef HAVE_V4L2
+ if (capBox->isV4l2) {
+ } else
+#endif
+ if (capBox->mmap) {
+ if (xioctl(capBox->fd, VIDIOCMCAPTURE, &capBox->grab_buf[capBox->curframe]) == -1)
+ TRACE("Freeing frame for capture failed: %s\n", strerror(errno));
+ }
+ if (++capBox->curframe == capBox->buffers) capBox->curframe = 0;
+}
+
+static DWORD WINAPI ReadThread(LPVOID lParam) {
+ CaptureBox * capBox = lParam;
+ HRESULT hr;
+ IMediaSample *pSample = NULL;
+ unsigned long framecount = 0;
+ LPBYTE pTarget, pInput, pOutput;
+ pOutput = CoTaskMemAlloc(capBox->width * capBox->height * 3);
+
+ while (1) {
+ EnterCriticalSection(&capBox->CritSect);
+ if (capBox->stopped) break;
+ hr = OutputPin_GetDeliveryBuffer((OutputPin *)capBox->pOut, &pSample, NULL, NULL, 0);
+ if (SUCCEEDED(hr)) {
+ IMediaSample_SetActualDataLength(pSample, capBox->outputheight * capBox->outputwidth * capBox->bitDepth / 8);
+ IMediaSample_GetPointer(pSample, &pTarget);
+ /* TODO: Check return values.. */
+ Capture_GetFrame(capBox, &pInput);
+ capBox->renderer(capBox, pOutput, pInput);
+ Resize(capBox, pTarget, pOutput);
+ hr = OutputPin_SendSample((OutputPin *)capBox->pOut, pSample);
+ FIXME("%p -> Frame %lu: %lx\n", capBox, ++framecount, hr);
+ IMediaSample_Release(pSample);
+ Capture_FreeFrame(capBox);
+ }
+ LeaveCriticalSection(&capBox->CritSect);
+ if (FAILED(hr) && hr != VFW_E_NOT_CONNECTED) {
+ ERR("Received error: %lx\n", hr);
+ }
+ }
+ LeaveCriticalSection(&capBox->CritSect);
+ CoTaskMemFree(pOutput);
+
+ return 0x0;
+}
+
+HRESULT Capture_Run(void * pBox, FILTER_STATE *state)
+{
+ CaptureBox *capBox = (CaptureBox *)pBox;
+ HANDLE thread;
+ HRESULT hr;
+
+ TRACE("%p -> (%p)\n", capBox, state);
+
+ if (*state == State_Running) return S_OK;
+
+ EnterCriticalSection(&capBox->CritSect);
+
+ capBox->stopped = 0;
+
+ if (*state == State_Stopped)
+ {
+ *state = State_Running;
+ if (!capBox->iscommitted++) {
+ IMemAllocator * pAlloc = NULL;
+ ALLOCATOR_PROPERTIES ap, actual;
+ ap.cBuffers = 3;
+ ap.cbBuffer = capBox->outputwidth * capBox->outputheight * capBox->bitDepth / 8;
+ ap.cbAlign = 1;
+ ap.cbPrefix = 0;
+
+ hr = IMemInputPin_GetAllocator(((OutputPin *)capBox->pOut)->pMemInputPin, &pAlloc);
+
+ if (SUCCEEDED(hr))
+ hr = IMemAllocator_SetProperties(pAlloc, &ap, &actual);
+
+ if (SUCCEEDED(hr))
+ hr = IMemAllocator_Commit(pAlloc);
+
+ if (pAlloc)
+ IMemAllocator_Release(pAlloc);
+
+ TRACE("Committing allocator: %lx\n", hr);
+ }
+
+ thread = CreateThread(NULL, 0, ReadThread, capBox, 0, NULL);
+ if (thread) {
+ capBox->thread = thread;
+ SetThreadPriority(thread, THREAD_PRIORITY_IDLE);
+ LeaveCriticalSection(&capBox->CritSect);
+ return S_OK;
+ }
+ ERR("Creating thread failed.. %lx\n", GetLastError());
+ LeaveCriticalSection(&capBox->CritSect);
+ return E_FAIL;
+ }
+
+ ResumeThread(capBox->thread);
+ *state = State_Running;
+ LeaveCriticalSection(&capBox->CritSect);
+ return S_OK;
+}
+
+HRESULT Capture_Pause(void * pBox, FILTER_STATE *state)
+{
+ CaptureBox *capBox = (CaptureBox *)pBox;
+ TRACE("%p -> (%p)\n", capBox, state);
+ if (*state == State_Paused) return S_OK;
+ if (*state == State_Stopped) Capture_Run(pBox, state);
+ EnterCriticalSection(&capBox->CritSect);
+ *state = State_Paused;
+ SuspendThread(capBox->thread);
+ LeaveCriticalSection(&capBox->CritSect);
+ return S_OK;
+}
+
+HRESULT Capture_Stop(void * pBox, FILTER_STATE *state)
+{
+ CaptureBox *capBox = (CaptureBox *)pBox;
+ TRACE("%p -> (%p)\n", capBox, state);
+ if (*state == State_Stopped) return S_OK;
+
+ EnterCriticalSection(&capBox->CritSect);
+
+ if (capBox->thread) {
+ if (*state == State_Paused)
+ ResumeThread(capBox->thread);
+ *state = State_Stopped;
+ capBox->stopped = 1;
+ capBox->thread = 0;
+ if (capBox->iscommitted) {
+ HRESULT hr;
+ IMemInputPin *pMem = NULL;
+ IMemAllocator * pAlloc = NULL;
+ IPin *pConnect = NULL;
+
+ capBox->iscommitted = 0;
+
+ hr = IPin_ConnectedTo(capBox->pOut, &pConnect);
+
+ if (SUCCEEDED(hr))
+ hr = IPin_QueryInterface(pConnect, &IID_IMemInputPin, (void **) &pMem);
+
+ if (SUCCEEDED(hr))
+ hr = IMemInputPin_GetAllocator(pMem, &pAlloc);
+
+ if (SUCCEEDED(hr))
+ hr = IMemAllocator_Decommit(pAlloc);
+
+ if (pAlloc)
+ IMemAllocator_Release(pAlloc);
+
+ if (pMem)
+ IMemInputPin_Release(pMem);
+
+ if (pConnect)
+ IPin_Release(pConnect);
+
+ if (hr != S_OK && hr != VFW_E_NOT_COMMITTED)
+ ERR("Decommitting allocator: %lx\n", hr);
+ }
+ }
+
+ LeaveCriticalSection(&capBox->CritSect);
+ return S_OK;
+}
+
+#endif /* HAVE_LINUX_VIDEODEV_H */
+
diff -Nru wine-old/dlls/qcap/vfwcapture.c wine-new/dlls/qcap/vfwcapture.c
--- wine-old/dlls/qcap/vfwcapture.c 1970-01-01 01:00:00.000000000 +0100
+++ wine-new/dlls/qcap/vfwcapture.c 2005-05-01 01:59:57.000000000 +0200
@@ -0,0 +1,614 @@
+/* Video For Windows Steering structure
+ *
+ * Copyright 2005 Maarten Lankhorst
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2.1 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ *
+ * Ugh, Now I can't tell what's worser, COM or the bugs in filtergraph, pins, enummedia and devenum..
+ * This file is only compiled if we found a working videodev.h
+ * If it doesn't exist, null.c will be compiled instead
+ */
+
+/* /dev/videoX where X = videodevice, will look at a more legal way later..
+ * So here we use /dev/video0 */
+#define VIDEODEVICE 0
+
+#define NONAMELESSSTRUCT
+#define NONAMELESSUNION
+#include "config.h"
+#include "qcap_private.h"
+#include "wine/unicode.h"
+#include "wine/debug.h"
+
+WINE_DEFAULT_DEBUG_CHANNEL(qcap);
+
+#include "pin.h"
+#include "capture.h"
+#include "uuids.h"
+#include "mmreg.h"
+#include "vfwmsgs.h"
+#include "amvideo.h"
+#include "windef.h"
+#include "winbase.h"
+#include "dshow.h"
+#include "strmif.h"
+#include "ddraw.h"
+
+static const IBaseFilterVtbl VfwCapture_Vtbl;
+static const IAMStreamConfigVtbl IAMStreamConfig_VTable;
+static const IPinVtbl VfwPin_Vtbl;
+
+static HRESULT VfwPin_Construct(IBaseFilter * pBaseFilter, LPCRITICAL_SECTION pCritSec, IPin ** ppPin);
+static const WCHAR wszOutputPinName[] = { 'O','u','t','p','u','t',0 };
+
+typedef struct VfwCapture
+{
+ const struct IBaseFilterVtbl * lpVtbl;
+ const struct IAMStreamConfigVtbl * IAMStreamConfig_vtbl;
+
+ LPVOID myCap;
+ ULONG refCount;
+ FILTER_INFO filterInfo;
+ FILTER_STATE state;
+ CRITICAL_SECTION csFilter;
+
+ IPin * pOutputPin;
+} VfwCapture;
+
+/* VfwPin implementation */
+typedef struct VfwPinImpl
+{
+ OutputPin pin;
+
+ LPVOID myCap;
+ IKsPropertySetVtbl * KSP_VT;
+} VfwPinImpl;
+
+HRESULT VfwCapture_create(IUnknown * pUnkOuter, LPVOID * ppv)
+{
+ VfwCapture *pVfwCapture;
+ HRESULT hr;
+
+ if (pUnkOuter)
+ return CLASS_E_NOAGGREGATION;
+
+ pVfwCapture = CoTaskMemAlloc(sizeof(VfwCapture));
+
+ if (!pVfwCapture)
+ return E_OUTOFMEMORY;
+
+ pVfwCapture->lpVtbl = &VfwCapture_Vtbl;
+ pVfwCapture->IAMStreamConfig_vtbl = &IAMStreamConfig_VTable;
+ pVfwCapture->refCount = 1;
+ pVfwCapture->filterInfo.achName[0] = '\0';
+ pVfwCapture->filterInfo.pGraph = NULL;
+ pVfwCapture->state = State_Stopped;
+ InitializeCriticalSection(&pVfwCapture->csFilter);
+ hr = VfwPin_Construct((IBaseFilter *)&pVfwCapture->lpVtbl, &pVfwCapture->csFilter, &pVfwCapture->pOutputPin);
+ if (!SUCCEEDED(hr))
+ {
+ CoTaskMemFree(pVfwCapture);
+ return E_OUTOFMEMORY;
+ } else {
+ hr = Capture_Initialise(&pVfwCapture->myCap, pVfwCapture->pOutputPin, VIDEODEVICE);
+ ((VfwPinImpl *)pVfwCapture->pOutputPin)->myCap = pVfwCapture->myCap;
+ if (FAILED(hr)) {
+ if (pVfwCapture->myCap)
+ CoTaskMemFree(pVfwCapture->myCap);
+ IPin_Release((IPin *)pVfwCapture->pOutputPin);
+ CoTaskMemFree(pVfwCapture);
+ return hr;
+ }
+ }
+ *ppv = (LPVOID)pVfwCapture;
+ TRACE("-- created at %p\n", pVfwCapture);
+
+ return S_OK;
+}
+
+static HRESULT WINAPI VfwCapture_QueryInterface(IBaseFilter * iface, REFIID riid, LPVOID * ppv)
+{
+ VfwCapture *This = (VfwCapture *)iface;
+ TRACE("(%s, %p)\n", qcdebugstr_guid(riid), ppv);
+ *ppv = NULL;
+
+ if (IsEqualIID(riid, &IID_IUnknown))
+ *ppv = (LPVOID)This;
+ else if (IsEqualIID(riid, &IID_IPersist))
+ *ppv = (LPVOID)This;
+ else if (IsEqualIID(riid, &IID_IMediaFilter))
+ *ppv = (LPVOID)This;
+ else if (IsEqualIID(riid, &IID_IBaseFilter))
+ *ppv = (LPVOID)This;
+ else if (IsEqualIID(riid, &IID_IAMStreamConfig))
+ *ppv = (LPVOID)&(This->IAMStreamConfig_vtbl);
+
+ if (*ppv)
+ {
+ TRACE("Returning %s interface\n", qcdebugstr_guid(riid));
+ IUnknown_AddRef((IUnknown *)(*ppv));
+ return S_OK;
+ }
+
+ FIXME("No interface for %s!\n", qcdebugstr_guid(riid));
+ return E_NOINTERFACE;
+}
+
+static ULONG WINAPI VfwCapture_AddRef(IBaseFilter * iface)
+{
+ VfwCapture *This = (VfwCapture *)iface;
+ ULONG refCount = InterlockedIncrement(&This->refCount);
+
+ TRACE("(%p/%p)->() New refcount: %ld\n", This, iface, refCount);
+
+ return refCount;
+}
+
+static ULONG WINAPI VfwCapture_Release(IBaseFilter * iface)
+{
+ VfwCapture *This = (VfwCapture *)iface;
+ ULONG refCount = InterlockedDecrement(&This->refCount);
+ TRACE("(%p/%p)->() New refcount: %ld\n", This, iface, refCount);
+
+ if (!refCount)
+ {
+ TRACE("(): Destroying everything!\n");
+ if (This->state != State_Stopped)
+ Capture_Stop(This->myCap, &This->state);
+ Capture_Destroy(This->myCap);
+ CoTaskMemFree(This->myCap);
+ if (((IPinImpl *)This->pOutputPin)->pConnectedTo != NULL)
+ {
+ IPin_Disconnect(((IPinImpl *)This->pOutputPin)->pConnectedTo);
+ IPin_Disconnect(This->pOutputPin);
+ }
+ IPin_Release(This->pOutputPin);
+ DeleteCriticalSection(&This->csFilter);
+ This->lpVtbl = NULL;
+ CoTaskMemFree(This);
+ }
+ return refCount;
+}
+
+/** IPersist methods **/
+
+static HRESULT WINAPI VfwCapture_GetClassID(IBaseFilter * iface, CLSID * pClsid)
+{
+ TRACE("(%p)\n", pClsid);
+ *pClsid = CLSID_VfwCapture;
+ return S_OK;
+}
+
+/** IMediaFilter methods **/
+
+static HRESULT WINAPI VfwCapture_Stop(IBaseFilter * iface)
+{
+ VfwCapture *This = (VfwCapture *)iface;
+ TRACE("()\n");
+ return Capture_Stop(This->myCap, &This->state);
+}
+
+static HRESULT WINAPI VfwCapture_Pause(IBaseFilter * iface)
+{
+ VfwCapture *This = (VfwCapture *)iface;
+ TRACE("()\n");
+ return Capture_Pause(This->myCap, &This->state);
+}
+
+static HRESULT WINAPI VfwCapture_Run(IBaseFilter * iface, REFERENCE_TIME tStart)
+{
+ VfwCapture *This = (VfwCapture *)iface;
+ TRACE("(%lx%08lx)\n", (ULONG)(tStart >> 32), (ULONG)tStart);
+ return Capture_Run(This->myCap, &This->state);
+}
+
+static HRESULT WINAPI VfwCapture_GetState(IBaseFilter * iface, DWORD dwMilliSecsTimeout, FILTER_STATE *pState)
+{
+ VfwCapture *This = (VfwCapture *)iface;
+ TRACE("(%lu, %p)\n", dwMilliSecsTimeout, pState);
+ *pState = This->state;
+ return S_OK;
+}
+
+static HRESULT WINAPI VfwCapture_SetSyncSource(IBaseFilter * iface, IReferenceClock *pClock)
+{
+ TRACE("(%p)\n", pClock);
+ return S_OK;
+}
+
+static HRESULT WINAPI VfwCapture_GetSyncSource(IBaseFilter * iface, IReferenceClock **ppClock)
+{
+ TRACE("(%p)\n", ppClock);
+ return S_OK;
+}
+
+/** IBaseFilter methods **/
+
+static HRESULT WINAPI VfwCapture_EnumPins(IBaseFilter * iface, IEnumPins **ppEnum)
+{
+ ENUMPINDETAILS epd;
+ VfwCapture *This = (VfwCapture *)iface;
+
+ TRACE("(%p)\n", ppEnum);
+
+ epd.cPins = 1;
+ epd.ppPins = &This->pOutputPin;
+ return IEnumPinsImpl_Construct(&epd, ppEnum);
+}
+
+static HRESULT WINAPI VfwCapture_FindPin(IBaseFilter * iface, LPCWSTR Id, IPin **ppPin)
+{
+ FIXME("(%s, %p) - stub\n", debugstr_w(Id), ppPin);
+ return E_NOTIMPL;
+}
+
+static HRESULT WINAPI VfwCapture_QueryFilterInfo(IBaseFilter * iface, FILTER_INFO *pInfo)
+{
+ VfwCapture *This = (VfwCapture *)iface;
+
+ TRACE("(%p)\n", pInfo);
+
+ strcpyW(pInfo->achName, This->filterInfo.achName);
+ pInfo->pGraph = This->filterInfo.pGraph;
+
+ if (pInfo->pGraph)
+ IFilterGraph_AddRef(pInfo->pGraph);
+ return S_OK;
+}
+
+static HRESULT WINAPI VfwCapture_JoinFilterGraph(IBaseFilter * iface, IFilterGraph *pGraph, LPCWSTR pName)
+{
+ VfwCapture *This = (VfwCapture *)iface;
+
+ TRACE("(%p, %s)\n", pGraph, debugstr_w(pName));
+
+ if (pName)
+ strcpyW(This->filterInfo.achName, pName);
+ else
+ *This->filterInfo.achName = 0;
+ This->filterInfo.pGraph = pGraph; /* NOTE: do NOT increase ref. count */
+
+ return S_OK;
+}
+
+static HRESULT WINAPI VfwCapture_QueryVendorInfo(IBaseFilter * iface, LPWSTR *pVendorInfo)
+{
+ FIXME("(%p) - stub\n", pVendorInfo);
+ return E_NOTIMPL;
+}
+
+static const IBaseFilterVtbl VfwCapture_Vtbl =
+{
+ VfwCapture_QueryInterface,
+ VfwCapture_AddRef,
+ VfwCapture_Release,
+ VfwCapture_GetClassID,
+ VfwCapture_Stop,
+ VfwCapture_Pause,
+ VfwCapture_Run,
+ VfwCapture_GetState,
+ VfwCapture_SetSyncSource,
+ VfwCapture_GetSyncSource,
+ VfwCapture_EnumPins,
+ VfwCapture_FindPin,
+ VfwCapture_QueryFilterInfo,
+ VfwCapture_JoinFilterGraph,
+ VfwCapture_QueryVendorInfo
+};
+
+/* AMStreamConfig interface, we only need to implement {G,S}etFormat */
+static HRESULT WINAPI AMStreamConfig_QueryInterface(IAMStreamConfig * iface, REFIID riid, LPVOID * ppv)
+{
+ if (IsEqualIID(riid, &IID_IUnknown) ||
+ IsEqualIID(riid, &IID_IAMStreamConfig))
+ {
+ *ppv = (LPVOID)iface;
+ return S_OK;
+ }
+
+ FIXME("No interface for iid %s\n", qcdebugstr_guid(riid));
+ return E_NOINTERFACE;
+}
+
+static ULONG WINAPI AMStreamConfig_AddRef(IAMStreamConfig * iface)
+{
+ ICOM_THIS_MULTI(VfwCapture, IAMStreamConfig_vtbl, iface);
+ TRACE("%p --> Forwarding to VfwCapture (%p)\n", iface, This);
+ return IUnknown_AddRef((IUnknown *)This);
+}
+
+static ULONG WINAPI AMStreamConfig_Release(IAMStreamConfig * iface)
+{
+ ICOM_THIS_MULTI(VfwCapture, IAMStreamConfig_vtbl, iface);
+ TRACE("%p --> Forwarding to VfwCapture (%p)\n", iface, This);
+ return IUnknown_Release((IUnknown *)This);
+}
+
+static HRESULT WINAPI AMStreamConfig_SetFormat(IAMStreamConfig *iface,
+ AM_MEDIA_TYPE *pmt) {
+ HRESULT hr;
+ ICOM_THIS_MULTI(VfwCapture, IAMStreamConfig_vtbl, iface);
+
+ TRACE("(%p): %p->%p\n", iface, pmt, pmt->pbFormat);
+
+ if (This->state != State_Stopped)
+ {
+ TRACE("Returning not stopped error\n");
+ return VFW_E_NOT_STOPPED;
+ }
+
+ dump_AM_MEDIA_TYPE(pmt);
+
+ if (((IPinImpl *)This->pOutputPin)->pConnectedTo != NULL) {
+ hr = IPin_QueryAccept(((IPinImpl *)This->pOutputPin)->pConnectedTo, pmt);
+ TRACE("Would accept: %ld\n", hr);
+ if (hr == S_FALSE)
+ return VFW_E_INVALIDMEDIATYPE;
+ }
+
+ hr = Capture_SetMediaType(This->myCap, pmt);
+ if (SUCCEEDED(hr) && This->filterInfo.pGraph != NULL &&
+ ((IPinImpl *)This->pOutputPin)->pConnectedTo != NULL) {
+ hr = IFilterGraph_Reconnect(This->filterInfo.pGraph, This->pOutputPin);
+ if (SUCCEEDED(hr))
+ TRACE("Reconnection completed, with new media format..\n");
+ }
+ TRACE("Returning: %ld\n", hr);
+ return hr;
+}
+
+static HRESULT WINAPI AMStreamConfig_GetFormat(IAMStreamConfig *iface,
+ AM_MEDIA_TYPE **pmt) {
+ ICOM_THIS_MULTI(VfwCapture, IAMStreamConfig_vtbl, iface);
+
+ TRACE("%p -> (%p)\n", iface, pmt);
+ return Capture_GetMediaType(This->myCap, pmt);
+}
+
+static HRESULT WINAPI AMStreamConfig_GetNumberOfCapabilities(IAMStreamConfig *iface, int *piCount, int *piSize)
+{
+ FIXME("%p: %p %p - stub, intentional\n", iface, piCount, piSize);
+ return E_NOTIMPL; /* Not implemented for this interface */
+}
+
+static HRESULT WINAPI AMStreamConfig_GetStreamCaps(IAMStreamConfig *iface,
+int iIndex, AM_MEDIA_TYPE **pmt, BYTE *pSCC)
+{
+ FIXME("%p: %d %p %p - stub, intentional\n", iface, iIndex, pmt, pSCC);
+ return E_NOTIMPL; /* Not implemented for this interface */
+}
+
+static const IAMStreamConfigVtbl IAMStreamConfig_VTable =
+{
+ AMStreamConfig_QueryInterface,
+ AMStreamConfig_AddRef,
+ AMStreamConfig_Release,
+ AMStreamConfig_SetFormat,
+ AMStreamConfig_GetFormat,
+ AMStreamConfig_GetNumberOfCapabilities,
+ AMStreamConfig_GetStreamCaps
+};
+
+/* IKsPropertySet interface */
+static HRESULT WINAPI KSP_QueryInterface(IKsPropertySet * iface, REFIID riid, LPVOID * ppv)
+{
+ if (IsEqualIID(riid, &IID_IUnknown) ||
+ IsEqualIID(riid, &IID_IKsPropertySet))
+ {
+ *ppv = (LPVOID)iface;
+ return S_OK;
+ }
+
+ FIXME("No interface for iid %s\n", qcdebugstr_guid(riid));
+ return E_NOINTERFACE;
+}
+
+static ULONG WINAPI KSP_AddRef(IKsPropertySet * iface)
+{
+ ICOM_THIS_MULTI(VfwPinImpl, KSP_VT, iface);
+ TRACE("%p --> Forwarding to VfwPin (%p)\n", iface, This);
+ return IUnknown_AddRef((IUnknown *)This);
+}
+
+static ULONG WINAPI KSP_Release(IKsPropertySet * iface)
+{
+ ICOM_THIS_MULTI(VfwPinImpl, KSP_VT, iface);
+ TRACE("%p --> Forwarding to VfwPin (%p)\n", iface, This);
+ return IUnknown_Release((IUnknown *)This);
+}
+
+static HRESULT WINAPI KSP_Set(IKsPropertySet * iface, REFGUID guidPropSet, DWORD dwPropID, LPVOID pInstanceData, DWORD cbInstanceData, LPVOID pPropData, DWORD cbPropData)
+{
+ FIXME("%p: stub\n", iface);
+ return E_NOTIMPL;
+}
+
+static HRESULT WINAPI KSP_Get(IKsPropertySet * iface, REFGUID guidPropSet, DWORD dwPropID, LPVOID pInstanceData, DWORD cbInstanceData, LPVOID pPropData, DWORD cbPropData, DWORD *pcbReturned)
+{
+ TRACE("()\n");
+ if (!IsEqualIID(guidPropSet, &ROPSETID_Pin))
+ return E_PROP_SET_UNSUPPORTED;
+ if (pPropData == NULL && pcbReturned == NULL)
+ return E_POINTER;
+ if (pcbReturned)
+ *pcbReturned = sizeof(GUID);
+ if (pPropData == NULL)
+ return S_OK;
+ if (cbPropData < sizeof(GUID))
+ return E_UNEXPECTED;
+ *(GUID *)pPropData = PIN_CATEGORY_PREVIEW;
+ FIXME("() Not adding a pin with PIN_CATEGORY_CAPTURE for a reason..\n");
+ return S_OK;
+}
+
+static HRESULT WINAPI KSP_QuerySupported(IKsPropertySet * iface, REFGUID guidPropSet, DWORD dwPropID, DWORD *pTypeSupport)
+{
+ FIXME("%p: stub\n", iface);
+ return E_NOTIMPL;
+}
+
+static IKsPropertySetVtbl KSP_VTable =
+{
+ KSP_QueryInterface,
+ KSP_AddRef,
+ KSP_Release,
+ KSP_Set,
+ KSP_Get,
+ KSP_QuerySupported
+};
+
+static HRESULT VfwPin_Construct(IBaseFilter * pBaseFilter, LPCRITICAL_SECTION pCritSec, IPin ** ppPin)
+{
+ ALLOCATOR_PROPERTIES ap;
+ VfwPinImpl * pPinImpl;
+ PIN_INFO piOutput;
+
+ pPinImpl = CoTaskMemAlloc(sizeof(*pPinImpl));
+ if (!pPinImpl)
+ return E_OUTOFMEMORY;
+
+ ap.cBuffers = 3;
+ ap.cbBuffer = 230400;
+ ap.cbAlign = 1;
+ ap.cbPrefix = 0;
+
+ piOutput.dir = PINDIR_OUTPUT;
+ piOutput.pFilter = pBaseFilter;
+ strcpyW(piOutput.achName, wszOutputPinName);
+
+ if (SUCCEEDED(OutputPin_Init(&piOutput, &ap, pBaseFilter, NULL, pCritSec, &pPinImpl->pin)))
+ {
+ pPinImpl->KSP_VT = &KSP_VTable;
+ pPinImpl->pin.pin.lpVtbl = &VfwPin_Vtbl;
+ *ppPin = (IPin *)(&pPinImpl->pin.pin.lpVtbl);
+ return S_OK;
+ }
+ return E_FAIL;
+}
+
+static HRESULT WINAPI VfwPin_QueryInterface(IPin * iface, REFIID riid, LPVOID * ppv)
+{
+ VfwPinImpl *This = (VfwPinImpl *)iface;
+ TRACE("%s %p\n", qcdebugstr_guid(riid), ppv);
+ *ppv = NULL;
+ if (IsEqualIID(riid, &IID_IUnknown))
+ *ppv = (LPVOID)This;
+ else if (IsEqualIID(riid, &IID_IPin))
+ *ppv = (LPVOID)This;
+ else if (IsEqualIID(riid, &IID_IKsPropertySet))
+ *ppv = (LPVOID)&(This->KSP_VT);
+
+ if (*ppv)
+ {
+ IUnknown_AddRef((IUnknown *)(*ppv));
+ return S_OK;
+ }
+
+ FIXME("No interface for %s!\n", qcdebugstr_guid(riid));
+ return E_NOINTERFACE;
+}
+
+static ULONG WINAPI VfwPin_AddRef(IPin * iface)
+{
+ VfwPinImpl *This = (VfwPinImpl *)iface;
+ ULONG refCount = InterlockedIncrement(&This->pin.pin.refCount);
+
+ TRACE("() -> new refcount: %lu\n", refCount);
+
+ return refCount;
+}
+
+static ULONG WINAPI VfwPin_Release(IPin * iface)
+{
+ VfwPinImpl *This = (VfwPinImpl *)iface;
+ ULONG refCount = InterlockedDecrement(&This->pin.pin.refCount);
+ TRACE("() -> new refcount: %lu\n", refCount);
+
+ if (!refCount)
+ {
+ CoTaskMemFree(This);
+ return 0;
+ }
+ return refCount;
+}
+
+static HRESULT WINAPI VfwPin_EnumMediaTypes(IPin * iface, IEnumMediaTypes ** ppEnum)
+{
+ ENUMMEDIADETAILS emd;
+ AM_MEDIA_TYPE *pmt;
+ HRESULT hr;
+
+ VfwPinImpl *This = (VfwPinImpl *)iface;
+ emd.cMediaTypes = 1;
+ hr = Capture_GetMediaType(This->myCap, &pmt);
+ emd.pMediaTypes = pmt;
+ if (SUCCEEDED(hr)) hr = IEnumMediaTypesImpl_Construct(&emd, ppEnum);
+ TRACE("%p -- %lx\n", This, hr);
+ DeleteMediaType(pmt);
+ return hr;
+}
+
+static HRESULT WINAPI VfwPin_QueryInternalConnections(IPin * iface, IPin ** apPin, ULONG * cPin)
+{
+ TRACE("(%p)->(%p, %p)\n", iface, apPin, cPin);
+ return E_NOTIMPL;
+}
+
+static HRESULT WINAPI VfwPin_EndOfStream(IPin * iface)
+{
+ TRACE("()\n");
+ return E_UNEXPECTED;
+}
+
+static HRESULT WINAPI VfwPin_BeginFlush(IPin * iface)
+{
+ TRACE("(%p)->()\n", iface);
+ return E_UNEXPECTED;
+}
+
+static HRESULT WINAPI VfwPin_EndFlush(IPin * iface)
+{
+ TRACE("(%p)->()\n", iface);
+ return E_UNEXPECTED;
+}
+
+static HRESULT WINAPI VfwPin_NewSegment(IPin * iface, REFERENCE_TIME tStart, REFERENCE_TIME tStop, double dRate)
+{
+ TRACE("(%p)->(%lx%08lx, %lx%08lx, %e)\n", iface, (ULONG)(tStart >> 32), (ULONG)tStart, (ULONG)(tStop >> 32), (ULONG)tStop, dRate);
+ return E_UNEXPECTED;
+}
+
+static const IPinVtbl VfwPin_Vtbl =
+{
+ VfwPin_QueryInterface,
+ VfwPin_AddRef,
+ VfwPin_Release,
+ OutputPin_Connect,
+ OutputPin_ReceiveConnection,
+ OutputPin_Disconnect,
+ IPinImpl_ConnectedTo,
+ IPinImpl_ConnectionMediaType,
+ IPinImpl_QueryPinInfo,
+ IPinImpl_QueryDirection,
+ IPinImpl_QueryId,
+ IPinImpl_QueryAccept,
+ VfwPin_EnumMediaTypes,
+ VfwPin_QueryInternalConnections,
+ VfwPin_EndOfStream,
+ VfwPin_BeginFlush,
+ VfwPin_EndFlush,
+ VfwPin_NewSegment
+};
+