2 * Generic Implementation of IPin Interface
4 * Copyright 2003 Robert Shearman
6 * This library is free software; you can redistribute it and/or
7 * modify it under the terms of the GNU Lesser General Public
8 * License as published by the Free Software Foundation; either
9 * version 2.1 of the License, or (at your option) any later version.
11 * This library is distributed in the hope that it will be useful,
12 * but WITHOUT ANY WARRANTY; without even the implied warranty of
13 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
14 * Lesser General Public License for more details.
16 * You should have received a copy of the GNU Lesser General Public
17 * License along with this library; if not, write to the Free Software
18 * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
21 #include "quartz_private.h"
24 #include "wine/debug.h"
25 #include "wine/unicode.h"
30 WINE_DEFAULT_DEBUG_CHANNEL(quartz);
32 static const struct IPinVtbl InputPin_Vtbl;
33 static const struct IPinVtbl OutputPin_Vtbl;
34 static const struct IMemInputPinVtbl MemInputPin_Vtbl;
36 #define _IMemInputPin_Offset ((int)(&(((InputPin*)0)->lpVtblMemInput)))
37 #define ICOM_THIS_From_IMemInputPin(impl, iface) impl* This = (impl*)(((char*)iface)-_IMemInputPin_Offset);
39 static void Copy_PinInfo(PIN_INFO * pDest, const PIN_INFO * pSrc)
41 /* Tempting to just do a memcpy, but the name field is
42 128 characters long! We will probably never exceed 10
43 most of the time, so we are better off copying
44 each field manually */
45 strcpyW(pDest->achName, pSrc->achName);
46 pDest->dir = pSrc->dir;
47 pDest->pFilter = pSrc->pFilter;
48 IBaseFilter_AddRef(pDest->pFilter);
51 /* Internal function called as a helper to IPin_Connect */
52 /* specific AM_MEDIA_TYPE - it cannot be NULL */
53 /* NOTE: not part of standard interface */
54 static HRESULT OutputPin_ConnectSpecific(IPin * iface, IPin * pReceivePin, const AM_MEDIA_TYPE * pmt)
57 ICOM_THIS(OutputPin, iface);
59 TRACE("(%p, %p)\n", pReceivePin, pmt);
60 dump_AM_MEDIA_TYPE(pmt);
62 hr = IPin_ReceiveConnection(pReceivePin, iface, pmt);
66 This->pin.pConnectedTo = pReceivePin;
67 IPin_AddRef(pReceivePin);
68 CopyMediaType(&This->pin.mtCurrent, pmt);
71 This->pin.pConnectedTo = NULL;
73 TRACE("-- %lx\n", hr);
77 HRESULT InputPin_Construct(const PIN_INFO * pPinInfo, SAMPLEPROC pSampleProc, LPVOID pUserData, QUERYACCEPTPROC pQueryAccept, LPCRITICAL_SECTION pCritSec, IPin ** ppPin)
83 if (pPinInfo->dir != PINDIR_INPUT)
85 ERR("Pin direction(%x) != PINDIR_INPUT\n", pPinInfo->dir);
89 pPinImpl = CoTaskMemAlloc(sizeof(*pPinImpl));
94 if (SUCCEEDED(InputPin_Init(pPinInfo, pSampleProc, pUserData, pQueryAccept, pCritSec, pPinImpl)))
96 pPinImpl->pin.lpVtbl = &InputPin_Vtbl;
97 pPinImpl->lpVtblMemInput = &MemInputPin_Vtbl;
99 *ppPin = (IPin *)(&pPinImpl->pin.lpVtbl);
105 /* Note that we don't init the vtables here (like C++ constructor) */
106 HRESULT InputPin_Init(const PIN_INFO * pPinInfo, SAMPLEPROC pSampleProc, LPVOID pUserData, QUERYACCEPTPROC pQueryAccept, LPCRITICAL_SECTION pCritSec, InputPin * pPinImpl)
108 /* Common attributes */
109 pPinImpl->pin.refCount = 1;
110 pPinImpl->pin.pConnectedTo = NULL;
111 pPinImpl->pin.pQueryAccept = pQueryAccept;
112 pPinImpl->pin.pUserData = pUserData;
113 pPinImpl->pin.pCritSec = pCritSec;
114 InitializeCriticalSection(pPinImpl->pin.pCritSec);
115 Copy_PinInfo(&pPinImpl->pin.pinInfo, pPinInfo);
117 /* Input pin attributes */
118 pPinImpl->pSampleProc = pSampleProc;
119 pPinImpl->pAllocator = NULL;
120 pPinImpl->tStart = 0;
127 HRESULT OutputPin_Init(const PIN_INFO * pPinInfo, LPVOID pUserData, QUERYACCEPTPROC pQueryAccept, LPCRITICAL_SECTION pCritSec, OutputPin * pPinImpl)
129 /* Common attributes */
130 pPinImpl->pin.lpVtbl = &OutputPin_Vtbl;
131 pPinImpl->pin.refCount = 1;
132 pPinImpl->pin.pConnectedTo = NULL;
133 pPinImpl->pin.pQueryAccept = pQueryAccept;
134 pPinImpl->pin.pUserData = pUserData;
135 pPinImpl->pin.pCritSec = pCritSec;
136 InitializeCriticalSection(pPinImpl->pin.pCritSec);
137 Copy_PinInfo(&pPinImpl->pin.pinInfo, pPinInfo);
139 /* Output pin attributes */
140 pPinImpl->pMemInputPin = NULL;
141 pPinImpl->pConnectSpecific = OutputPin_ConnectSpecific;
146 HRESULT OutputPin_Construct(const PIN_INFO * pPinInfo, LPVOID pUserData, QUERYACCEPTPROC pQueryAccept, LPCRITICAL_SECTION pCritSec, IPin ** ppPin)
148 OutputPin * pPinImpl;
152 if (pPinInfo->dir != PINDIR_OUTPUT)
154 ERR("Pin direction(%x) != PINDIR_OUTPUT\n", pPinInfo->dir);
158 pPinImpl = CoTaskMemAlloc(sizeof(*pPinImpl));
161 return E_OUTOFMEMORY;
163 if (SUCCEEDED(OutputPin_Init(pPinInfo, pUserData, pQueryAccept, pCritSec, pPinImpl)))
165 pPinImpl->pin.lpVtbl = &OutputPin_Vtbl;
167 *ppPin = (IPin *)(&pPinImpl->pin.lpVtbl);
173 /*** Common pin functions ***/
175 ULONG WINAPI IPinImpl_AddRef(IPin * iface)
177 ICOM_THIS(IPinImpl, iface);
181 return InterlockedIncrement(&This->refCount);
184 HRESULT WINAPI IPinImpl_Disconnect(IPin * iface)
187 ICOM_THIS(IPinImpl, iface);
191 EnterCriticalSection(This->pCritSec);
193 if (This->pConnectedTo)
195 IPin_Release(This->pConnectedTo);
196 This->pConnectedTo = NULL;
202 LeaveCriticalSection(This->pCritSec);
207 HRESULT WINAPI IPinImpl_ConnectedTo(IPin * iface, IPin ** ppPin)
210 ICOM_THIS(IPinImpl, iface);
212 /* TRACE("(%p)\n", ppPin);*/
214 EnterCriticalSection(This->pCritSec);
216 if (!This->pConnectedTo)
219 hr = VFW_E_NOT_CONNECTED;
223 *ppPin = This->pConnectedTo;
227 LeaveCriticalSection(This->pCritSec);
232 HRESULT WINAPI IPinImpl_ConnectionMediaType(IPin * iface, AM_MEDIA_TYPE * pmt)
235 ICOM_THIS(IPinImpl, iface);
237 TRACE("(%p)\n", pmt);
239 EnterCriticalSection(This->pCritSec);
241 if (This->pConnectedTo)
243 CopyMediaType(pmt, &This->mtCurrent);
248 ZeroMemory(pmt, sizeof(*pmt));
249 hr = VFW_E_NOT_CONNECTED;
252 LeaveCriticalSection(This->pCritSec);
257 HRESULT WINAPI IPinImpl_QueryPinInfo(IPin * iface, PIN_INFO * pInfo)
259 ICOM_THIS(IPinImpl, iface);
261 TRACE("(%p)\n", pInfo);
263 Copy_PinInfo(pInfo, &This->pinInfo);
268 HRESULT WINAPI IPinImpl_QueryDirection(IPin * iface, PIN_DIRECTION * pPinDir)
270 ICOM_THIS(IPinImpl, iface);
272 TRACE("(%p)\n", pPinDir);
274 *pPinDir = This->pinInfo.dir;
279 HRESULT WINAPI IPinImpl_QueryId(IPin * iface, LPWSTR * Id)
281 ICOM_THIS(IPinImpl, iface);
285 *Id = CoTaskMemAlloc((strlenW(This->pinInfo.achName) + 1) * sizeof(WCHAR));
287 return E_OUTOFMEMORY;
289 strcpyW(*Id, This->pinInfo.achName);
294 HRESULT WINAPI IPinImpl_QueryAccept(IPin * iface, const AM_MEDIA_TYPE * pmt)
296 ICOM_THIS(IPinImpl, iface);
298 TRACE("(%p)\n", pmt);
300 return (This->pQueryAccept(This->pUserData, pmt) == S_OK ? S_OK : S_FALSE);
303 HRESULT WINAPI IPinImpl_EnumMediaTypes(IPin * iface, IEnumMediaTypes ** ppEnum)
305 ENUMMEDIADETAILS emd;
307 TRACE("(%p)\n", ppEnum);
309 /* override this method to allow enumeration of your types */
311 emd.pMediaTypes = NULL;
313 return IEnumMediaTypesImpl_Construct(&emd, ppEnum);
316 HRESULT WINAPI IPinImpl_QueryInternalConnections(IPin * iface, IPin ** apPin, ULONG * cPin)
318 TRACE("(%p, %p)\n", apPin, cPin);
320 return E_NOTIMPL; /* to tell caller that all input pins connected to all output pins */
323 /*** IPin implementation for an input pin ***/
325 HRESULT WINAPI InputPin_QueryInterface(IPin * iface, REFIID riid, LPVOID * ppv)
327 ICOM_THIS(InputPin, iface);
329 TRACE("(%s, %p)\n", qzdebugstr_guid(riid), ppv);
333 if (IsEqualIID(riid, &IID_IUnknown))
334 *ppv = (LPVOID)iface;
335 else if (IsEqualIID(riid, &IID_IPin))
336 *ppv = (LPVOID)iface;
337 else if (IsEqualIID(riid, &IID_IMemInputPin))
338 *ppv = (LPVOID)&This->lpVtblMemInput;
342 IUnknown_AddRef((IUnknown *)(*ppv));
346 FIXME("No interface for %s!\n", qzdebugstr_guid(riid));
348 return E_NOINTERFACE;
351 ULONG WINAPI InputPin_Release(IPin * iface)
353 ICOM_THIS(InputPin, iface);
357 if (!InterlockedDecrement(&This->pin.refCount))
359 if (This->pAllocator)
360 IMemAllocator_Release(This->pAllocator);
365 return This->pin.refCount;
368 HRESULT WINAPI InputPin_Connect(IPin * iface, IPin * pConnector, const AM_MEDIA_TYPE * pmt)
370 ERR("Outgoing connection on an input pin! (%p, %p)\n", pConnector, pmt);
376 HRESULT WINAPI InputPin_ReceiveConnection(IPin * iface, IPin * pReceivePin, const AM_MEDIA_TYPE * pmt)
378 PIN_DIRECTION pindirReceive;
379 ICOM_THIS(InputPin, iface);
382 TRACE("(%p, %p)\n", pReceivePin, pmt);
383 dump_AM_MEDIA_TYPE(pmt);
385 EnterCriticalSection(This->pin.pCritSec);
387 if (This->pin.pConnectedTo)
388 hr = VFW_E_ALREADY_CONNECTED;
390 if (SUCCEEDED(hr) && This->pin.pQueryAccept(This->pin.pUserData, pmt) != S_OK)
391 hr = VFW_E_TYPE_NOT_ACCEPTED; /* FIXME: shouldn't we just map common errors onto
392 * VFW_E_TYPE_NOT_ACCEPTED and pass the value on otherwise? */
396 IPin_QueryDirection(pReceivePin, &pindirReceive);
398 if (pindirReceive != PINDIR_OUTPUT)
400 ERR("Can't connect from non-output pin\n");
401 hr = VFW_E_INVALID_DIRECTION;
407 CopyMediaType(&This->pin.mtCurrent, pmt);
408 This->pin.pConnectedTo = pReceivePin;
409 IPin_AddRef(pReceivePin);
412 LeaveCriticalSection(This->pin.pCritSec);
417 HRESULT WINAPI InputPin_EndOfStream(IPin * iface)
424 HRESULT WINAPI InputPin_BeginFlush(IPin * iface)
430 HRESULT WINAPI InputPin_EndFlush(IPin * iface)
436 HRESULT WINAPI InputPin_NewSegment(IPin * iface, REFERENCE_TIME tStart, REFERENCE_TIME tStop, double dRate)
438 ICOM_THIS(InputPin, iface);
440 TRACE("(%lx%08lx, %lx%08lx, %e)\n", (ULONG)(tStart >> 32), (ULONG)tStart, (ULONG)(tStop >> 32), (ULONG)tStop, dRate);
442 This->tStart = tStart;
449 static const IPinVtbl InputPin_Vtbl =
451 ICOM_MSVTABLE_COMPAT_DummyRTTIVALUE
452 InputPin_QueryInterface,
456 InputPin_ReceiveConnection,
458 IPinImpl_ConnectedTo,
459 IPinImpl_ConnectionMediaType,
460 IPinImpl_QueryPinInfo,
461 IPinImpl_QueryDirection,
463 IPinImpl_QueryAccept,
464 IPinImpl_EnumMediaTypes,
465 IPinImpl_QueryInternalConnections,
466 InputPin_EndOfStream,
472 /*** IMemInputPin implementation ***/
474 HRESULT WINAPI MemInputPin_QueryInterface(IMemInputPin * iface, REFIID riid, LPVOID * ppv)
476 ICOM_THIS_From_IMemInputPin(InputPin, iface);
478 return IPin_QueryInterface((IPin *)&This->pin, riid, ppv);
481 ULONG WINAPI MemInputPin_AddRef(IMemInputPin * iface)
483 ICOM_THIS_From_IMemInputPin(InputPin, iface);
485 return IPin_AddRef((IPin *)&This->pin);
488 ULONG WINAPI MemInputPin_Release(IMemInputPin * iface)
490 ICOM_THIS_From_IMemInputPin(InputPin, iface);
492 return IPin_Release((IPin *)&This->pin);
495 HRESULT WINAPI MemInputPin_GetAllocator(IMemInputPin * iface, IMemAllocator ** ppAllocator)
497 ICOM_THIS_From_IMemInputPin(InputPin, iface);
499 TRACE("MemInputPin_GetAllocator()\n");
501 *ppAllocator = This->pAllocator;
503 IMemAllocator_AddRef(*ppAllocator);
505 return *ppAllocator ? S_OK : VFW_E_NO_ALLOCATOR;
508 HRESULT WINAPI MemInputPin_NotifyAllocator(IMemInputPin * iface, IMemAllocator * pAllocator, BOOL bReadOnly)
510 ICOM_THIS_From_IMemInputPin(InputPin, iface);
512 TRACE("MemInputPin_NotifyAllocator()\n");
514 if (This->pAllocator)
515 IMemAllocator_Release(This->pAllocator);
516 This->pAllocator = pAllocator;
517 if (This->pAllocator)
518 IMemAllocator_AddRef(This->pAllocator);
523 HRESULT WINAPI MemInputPin_GetAllocatorRequirements(IMemInputPin * iface, ALLOCATOR_PROPERTIES * pProps)
525 TRACE("(%p)\n", pProps);
527 /* override this method if you have any specific requirements */
532 HRESULT WINAPI MemInputPin_Receive(IMemInputPin * iface, IMediaSample * pSample)
534 ICOM_THIS_From_IMemInputPin(InputPin, iface);
536 /* this trace commented out for performance reasons */
537 /* TRACE("(%p)\n", pSample);*/
539 return This->pSampleProc(This->pin.pUserData, pSample);
542 HRESULT WINAPI MemInputPin_ReceiveMultiple(IMemInputPin * iface, IMediaSample ** pSamples, long nSamples, long *nSamplesProcessed)
545 TRACE("(%p, %ld, %p)\n", pSamples, nSamples, nSamplesProcessed);
547 for (*nSamplesProcessed = 0; *nSamplesProcessed < nSamples; (*nSamplesProcessed)++)
549 hr = IMemInputPin_Receive(iface, pSamples[*nSamplesProcessed]);
557 HRESULT WINAPI MemInputPin_ReceiveCanBlock(IMemInputPin * iface)
561 /* FIXME: we should check whether any output pins will block */
566 static const IMemInputPinVtbl MemInputPin_Vtbl =
568 ICOM_MSVTABLE_COMPAT_DummyRTTIVALUE
569 MemInputPin_QueryInterface,
572 MemInputPin_GetAllocator,
573 MemInputPin_NotifyAllocator,
574 MemInputPin_GetAllocatorRequirements,
576 MemInputPin_ReceiveMultiple,
577 MemInputPin_ReceiveCanBlock
580 HRESULT WINAPI OutputPin_QueryInterface(IPin * iface, REFIID riid, LPVOID * ppv)
582 TRACE("(%s, %p)\n", qzdebugstr_guid(riid), ppv);
586 if (IsEqualIID(riid, &IID_IUnknown))
587 *ppv = (LPVOID)iface;
588 else if (IsEqualIID(riid, &IID_IPin))
589 *ppv = (LPVOID)iface;
593 IUnknown_AddRef((IUnknown *)(*ppv));
597 FIXME("No interface for %s!\n", qzdebugstr_guid(riid));
599 return E_NOINTERFACE;
602 ULONG WINAPI OutputPin_Release(IPin * iface)
604 ICOM_THIS(OutputPin, iface);
608 if (!InterlockedDecrement(&This->pin.refCount))
613 return This->pin.refCount;
616 HRESULT WINAPI OutputPin_Connect(IPin * iface, IPin * pReceivePin, const AM_MEDIA_TYPE * pmt)
619 ICOM_THIS(OutputPin, iface);
621 TRACE("(%p, %p)\n", pReceivePin, pmt);
622 dump_AM_MEDIA_TYPE(pmt);
624 /* If we try to connect to ourself, we will definitely deadlock.
625 * There are other cases where we could deadlock too, but this
626 * catches the obvious case */
627 assert(pReceivePin != iface);
629 EnterCriticalSection(This->pin.pCritSec);
631 /* get the IMemInputPin interface we will use to deliver samples to the
633 hr = IPin_QueryInterface(pReceivePin, &IID_IMemInputPin, (LPVOID *)&This->pMemInputPin);
635 /* if we have been a specific type to connect with, then we can either connect
636 * with that or fail. We cannot choose different AM_MEDIA_TYPE */
639 if (pmt && !IsEqualGUID(&pmt->majortype, &GUID_NULL) && !IsEqualGUID(&pmt->subtype, &GUID_NULL))
640 hr = This->pConnectSpecific(iface, pReceivePin, pmt);
643 /* negotiate media type */
645 IEnumMediaTypes * pEnumCandidates;
646 AM_MEDIA_TYPE * pmtCandidate; /* Candidate media type */
648 if (SUCCEEDED(hr = IPin_EnumMediaTypes(iface, &pEnumCandidates)))
650 hr = VFW_E_NO_ACCEPTABLE_TYPES; /* Assume the worst, but set to S_OK if connected successfully */
652 /* try this filter's media types first */
653 while (S_OK == IEnumMediaTypes_Next(pEnumCandidates, 1, &pmtCandidate, NULL))
655 if (( !pmt || CompareMediaTypes(pmt, pmtCandidate, TRUE) ) &&
656 (This->pConnectSpecific(iface, pReceivePin, pmtCandidate) == S_OK))
659 CoTaskMemFree(pmtCandidate);
662 CoTaskMemFree(pmtCandidate);
664 IEnumMediaTypes_Release(pEnumCandidates);
667 /* then try receiver filter's media types */
668 if (hr != S_OK && SUCCEEDED(hr = IPin_EnumMediaTypes(pReceivePin, &pEnumCandidates))) /* if we haven't already connected successfully */
670 while (S_OK == IEnumMediaTypes_Next(pEnumCandidates, 1, &pmtCandidate, NULL))
672 if (( !pmt || CompareMediaTypes(pmt, pmtCandidate, TRUE) ) &&
673 (This->pConnectSpecific(iface, pReceivePin, pmtCandidate) == S_OK))
676 CoTaskMemFree(pmtCandidate);
679 CoTaskMemFree(pmtCandidate);
681 IEnumMediaTypes_Release(pEnumCandidates);
683 } /* if negotiate media type */
686 LeaveCriticalSection(This->pin.pCritSec);
691 HRESULT WINAPI OutputPin_ReceiveConnection(IPin * iface, IPin * pReceivePin, const AM_MEDIA_TYPE * pmt)
693 ERR("Incoming connection on an output pin! (%p, %p)\n", pReceivePin, pmt);
698 HRESULT WINAPI OutputPin_Disconnect(IPin * iface)
701 ICOM_THIS(OutputPin, iface);
705 EnterCriticalSection(This->pin.pCritSec);
707 if (This->pMemInputPin)
709 IMemInputPin_Release(This->pMemInputPin);
710 This->pMemInputPin = NULL;
712 if (This->pin.pConnectedTo)
714 IPin_Release(This->pin.pConnectedTo);
715 This->pin.pConnectedTo = NULL;
721 LeaveCriticalSection(This->pin.pCritSec);
726 HRESULT WINAPI OutputPin_EndOfStream(IPin * iface)
730 /* not supposed to do anything in an output pin */
735 HRESULT WINAPI OutputPin_BeginFlush(IPin * iface)
739 /* not supposed to do anything in an output pin */
744 HRESULT WINAPI OutputPin_EndFlush(IPin * iface)
748 /* not supposed to do anything in an output pin */
753 HRESULT WINAPI OutputPin_NewSegment(IPin * iface, REFERENCE_TIME tStart, REFERENCE_TIME tStop, double dRate)
755 TRACE("(%lx%08lx, %lx%08lx, %e)\n", (ULONG)(tStart >> 32), (ULONG)tStart, (ULONG)(tStop >> 32), (ULONG)tStop, dRate);
757 /* not supposed to do anything in an output pin */
762 static const IPinVtbl OutputPin_Vtbl =
764 ICOM_MSVTABLE_COMPAT_DummyRTTIVALUE
765 OutputPin_QueryInterface,
769 OutputPin_ReceiveConnection,
770 OutputPin_Disconnect,
771 IPinImpl_ConnectedTo,
772 IPinImpl_ConnectionMediaType,
773 IPinImpl_QueryPinInfo,
774 IPinImpl_QueryDirection,
776 IPinImpl_QueryAccept,
777 IPinImpl_EnumMediaTypes,
778 IPinImpl_QueryInternalConnections,
779 OutputPin_EndOfStream,
780 OutputPin_BeginFlush,