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 +}; +