From 2c9cb01379b47d5c2dc9583a995a09f5447029d8 Mon Sep 17 00:00:00 2001 From: Maarten Lankhorst Date: Wed, 18 May 2005 15:34:04 +0000 Subject: [PATCH] Add pin functions from quartz. --- dlls/qcap/Makefile.in | 1 + dlls/qcap/pin.c | 421 ++++++++++++++++++++++++++++++++++++++++++ dlls/qcap/pin.h | 80 ++++++++ 3 files changed, 502 insertions(+) create mode 100644 dlls/qcap/pin.c create mode 100644 dlls/qcap/pin.h diff --git a/dlls/qcap/Makefile.in b/dlls/qcap/Makefile.in index 9910055210b..be9e0571d2d 100644 --- a/dlls/qcap/Makefile.in +++ b/dlls/qcap/Makefile.in @@ -12,6 +12,7 @@ C_SRCS = \ dllsetup.c \ enummedia.c \ enumpins.c \ + pin.c \ qcap_main.c RC_SRCS = version.rc diff --git a/dlls/qcap/pin.c b/dlls/qcap/pin.c new file mode 100644 index 00000000000..45221dd87d8 --- /dev/null +++ b/dlls/qcap/pin.c @@ -0,0 +1,421 @@ +/* + * 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 + +#define COBJMACROS + +#include "windef.h" +#include "winbase.h" +#include "wtypes.h" +#include "wingdi.h" +#include "winuser.h" +#include "dshow.h" + +#include "qcap_main.h" +#include "pin.h" + +#include "wine/debug.h" +#include "wine/unicode.h" +#include "uuids.h" +#include "vfwmsgs.h" +#include + +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 --git a/dlls/qcap/pin.h b/dlls/qcap/pin.h new file mode 100644 index 00000000000..bca610eb27f --- /dev/null +++ b/dlls/qcap/pin.h @@ -0,0 +1,80 @@ +/* + * 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);