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
32 #include "qcap_main.h"
35 #include "wine/debug.h"
36 #include "wine/unicode.h"
41 WINE_DEFAULT_DEBUG_CHANNEL(qcap
);
43 #define ALIGNDOWN(value,boundary) ((value) & ~(boundary-1))
44 #define ALIGNUP(value,boundary) (ALIGNDOWN(value - 1, boundary) + boundary)
46 static void Copy_PinInfo(PIN_INFO
* pDest
, const PIN_INFO
* pSrc
)
48 /* Tempting to just do a memcpy, but the name field is
49 128 characters long! We will probably never exceed 10
50 most of the time, so we are better off copying
51 each field manually */
52 strcpyW(pDest
->achName
, pSrc
->achName
);
53 pDest
->dir
= pSrc
->dir
;
54 pDest
->pFilter
= pSrc
->pFilter
;
57 HRESULT WINAPI
IPinImpl_ConnectedTo(IPin
* iface
, IPin
** ppPin
)
60 IPinImpl
*This
= (IPinImpl
*)iface
;
62 /* TRACE("(%p)\n", ppPin);*/
64 EnterCriticalSection(This
->pCritSec
);
66 if (This
->pConnectedTo
)
68 *ppPin
= This
->pConnectedTo
;
73 hr
= VFW_E_NOT_CONNECTED
;
75 LeaveCriticalSection(This
->pCritSec
);
80 HRESULT WINAPI
IPinImpl_ConnectionMediaType(IPin
* iface
, AM_MEDIA_TYPE
* pmt
)
83 IPinImpl
*This
= (IPinImpl
*)iface
;
85 TRACE("(%p/%p)->(%p)\n", This
, iface
, pmt
);
87 EnterCriticalSection(This
->pCritSec
);
89 if (This
->pConnectedTo
)
91 CopyMediaType(pmt
, &This
->mtCurrent
);
96 ZeroMemory(pmt
, sizeof(*pmt
));
97 hr
= VFW_E_NOT_CONNECTED
;
100 LeaveCriticalSection(This
->pCritSec
);
105 HRESULT WINAPI
IPinImpl_QueryPinInfo(IPin
* iface
, PIN_INFO
* pInfo
)
107 IPinImpl
*This
= (IPinImpl
*)iface
;
109 TRACE("(%p/%p)->(%p)\n", This
, iface
, pInfo
);
111 Copy_PinInfo(pInfo
, &This
->pinInfo
);
112 IBaseFilter_AddRef(pInfo
->pFilter
);
117 HRESULT WINAPI
IPinImpl_QueryDirection(IPin
* iface
, PIN_DIRECTION
* pPinDir
)
119 IPinImpl
*This
= (IPinImpl
*)iface
;
121 TRACE("(%p/%p)->(%p)\n", This
, iface
, pPinDir
);
123 *pPinDir
= This
->pinInfo
.dir
;
128 HRESULT WINAPI
IPinImpl_QueryId(IPin
* iface
, LPWSTR
* Id
)
130 IPinImpl
*This
= (IPinImpl
*)iface
;
132 TRACE("(%p/%p)->(%p)\n", This
, iface
, Id
);
134 *Id
= CoTaskMemAlloc((strlenW(This
->pinInfo
.achName
) + 1) * sizeof(WCHAR
));
136 return E_OUTOFMEMORY
;
138 strcpyW(*Id
, This
->pinInfo
.achName
);
143 HRESULT WINAPI
IPinImpl_QueryAccept(IPin
* iface
, const AM_MEDIA_TYPE
* pmt
)
145 IPinImpl
*This
= (IPinImpl
*)iface
;
147 TRACE("(%p/%p)->(%p)\n", This
, iface
, pmt
);
149 if (!This
->fnQueryAccept
) return S_OK
;
151 return (This
->fnQueryAccept(This
->pUserData
, pmt
) == S_OK
? S_OK
: S_FALSE
);
154 HRESULT WINAPI
IPinImpl_QueryInternalConnections(IPin
* iface
, IPin
** apPin
, ULONG
* cPin
)
156 IPinImpl
*This
= (IPinImpl
*)iface
;
158 TRACE("(%p/%p)->(%p, %p)\n", This
, iface
, apPin
, cPin
);
160 return E_NOTIMPL
; /* to tell caller that all input pins connected to all output pins */
163 /* Function called as a helper to IPin_Connect */
164 /* specific AM_MEDIA_TYPE - it cannot be NULL */
165 /* NOTE: not part of standard interface */
166 static HRESULT
OutputPin_ConnectSpecific(IPin
* iface
, IPin
* pReceivePin
, const AM_MEDIA_TYPE
* pmt
)
168 OutputPin
*This
= (OutputPin
*)iface
;
170 IMemAllocator
* pMemAlloc
= NULL
;
171 ALLOCATOR_PROPERTIES actual
; /* FIXME: should we put the actual props back in to This? */
173 TRACE("(%p, %p)\n", pReceivePin
, pmt
);
174 dump_AM_MEDIA_TYPE(pmt
);
176 /* FIXME: call queryacceptproc */
178 This
->pin
.pConnectedTo
= pReceivePin
;
179 IPin_AddRef(pReceivePin
);
180 CopyMediaType(&This
->pin
.mtCurrent
, pmt
);
182 hr
= IPin_ReceiveConnection(pReceivePin
, iface
, pmt
);
184 /* get the IMemInputPin interface we will use to deliver samples to the
188 hr
= IPin_QueryInterface(pReceivePin
, &IID_IMemInputPin
, (LPVOID
)&This
->pMemInputPin
);
191 hr
= IMemInputPin_GetAllocator(This
->pMemInputPin
, &pMemAlloc
);
193 if (hr
== VFW_E_NO_ALLOCATOR
)
195 /* Input pin provides no allocator, use standard memory allocator */
196 hr
= CoCreateInstance(&CLSID_MemoryAllocator
, NULL
, CLSCTX_INPROC_SERVER
, &IID_IMemAllocator
, (LPVOID
*)&pMemAlloc
);
200 hr
= IMemInputPin_NotifyAllocator(This
->pMemInputPin
, pMemAlloc
, FALSE
);
205 hr
= IMemAllocator_SetProperties(pMemAlloc
, &This
->allocProps
, &actual
);
208 IMemAllocator_Release(pMemAlloc
);
210 /* break connection if we couldn't get the allocator */
212 IPin_Disconnect(pReceivePin
);
217 IPin_Release(This
->pin
.pConnectedTo
);
218 This
->pin
.pConnectedTo
= NULL
;
219 DeleteMediaType(&This
->pin
.mtCurrent
);
222 TRACE(" -- %lx\n", hr
);
226 HRESULT
OutputPin_Init(const PIN_INFO
* pPinInfo
, ALLOCATOR_PROPERTIES
* props
, LPVOID pUserData
, QUERYACCEPTPROC pQueryAccept
, LPCRITICAL_SECTION pCritSec
, OutputPin
* pPinImpl
)
230 /* Common attributes */
231 pPinImpl
->pin
.refCount
= 1;
232 pPinImpl
->pin
.pConnectedTo
= NULL
;
233 pPinImpl
->pin
.fnQueryAccept
= pQueryAccept
;
234 pPinImpl
->pin
.pUserData
= pUserData
;
235 pPinImpl
->pin
.pCritSec
= pCritSec
;
236 Copy_PinInfo(&pPinImpl
->pin
.pinInfo
, pPinInfo
);
238 /* Output pin attributes */
239 pPinImpl
->pMemInputPin
= NULL
;
240 pPinImpl
->pConnectSpecific
= OutputPin_ConnectSpecific
;
243 memcpy(&pPinImpl
->allocProps
, props
, sizeof(pPinImpl
->allocProps
));
244 if (pPinImpl
->allocProps
.cbAlign
== 0)
245 pPinImpl
->allocProps
.cbAlign
= 1;
248 ZeroMemory(&pPinImpl
->allocProps
, sizeof(pPinImpl
->allocProps
));
253 HRESULT WINAPI
OutputPin_Connect(IPin
* iface
, IPin
* pReceivePin
, const AM_MEDIA_TYPE
* pmt
)
256 OutputPin
*This
= (OutputPin
*)iface
;
258 TRACE("(%p/%p)->(%p, %p)\n", This
, iface
, pReceivePin
, pmt
);
259 dump_AM_MEDIA_TYPE(pmt
);
261 /* If we try to connect to ourself, we will definitely deadlock.
262 * There are other cases where we could deadlock too, but this
263 * catches the obvious case */
264 assert(pReceivePin
!= iface
);
266 EnterCriticalSection(This
->pin
.pCritSec
);
268 /* if we have been a specific type to connect with, then we can either connect
269 * with that or fail. We cannot choose different AM_MEDIA_TYPE */
270 if (pmt
&& !IsEqualGUID(&pmt
->majortype
, &GUID_NULL
) && !IsEqualGUID(&pmt
->subtype
, &GUID_NULL
))
271 hr
= This
->pConnectSpecific(iface
, pReceivePin
, pmt
);
274 /* negotiate media type */
276 IEnumMediaTypes
* pEnumCandidates
;
277 AM_MEDIA_TYPE
* pmtCandidate
; /* Candidate media type */
279 if (SUCCEEDED(hr
= IPin_EnumMediaTypes(iface
, &pEnumCandidates
)))
281 hr
= VFW_E_NO_ACCEPTABLE_TYPES
; /* Assume the worst, but set to S_OK if connected successfully */
283 /* try this filter's media types first */
284 while (S_OK
== IEnumMediaTypes_Next(pEnumCandidates
, 1, &pmtCandidate
, NULL
))
286 if (( !pmt
|| CompareMediaTypes(pmt
, pmtCandidate
, TRUE
) ) &&
287 (This
->pConnectSpecific(iface
, pReceivePin
, pmtCandidate
) == S_OK
))
291 DeleteMediaType(pmtCandidate
);
294 DeleteMediaType(pmtCandidate
);
296 IEnumMediaTypes_Release(pEnumCandidates
);
299 /* then try receiver filter's media types */
300 if (hr
!= S_OK
&& SUCCEEDED(hr
= IPin_EnumMediaTypes(pReceivePin
, &pEnumCandidates
))) /* if we haven't already connected successfully */
302 hr
= VFW_E_NO_ACCEPTABLE_TYPES
; /* Assume the worst, but set to S_OK if connected successfully */
304 while (S_OK
== IEnumMediaTypes_Next(pEnumCandidates
, 1, &pmtCandidate
, NULL
))
306 if (( !pmt
|| CompareMediaTypes(pmt
, pmtCandidate
, TRUE
) ) &&
307 (This
->pConnectSpecific(iface
, pReceivePin
, pmtCandidate
) == S_OK
))
310 DeleteMediaType(pmtCandidate
);
313 DeleteMediaType(pmtCandidate
);
315 IEnumMediaTypes_Release(pEnumCandidates
);
317 } /* if negotiate media type */
319 LeaveCriticalSection(This
->pin
.pCritSec
);
321 TRACE(" -- %lx\n", hr
);
325 HRESULT WINAPI
OutputPin_ReceiveConnection(IPin
* iface
, IPin
* pReceivePin
, const AM_MEDIA_TYPE
* pmt
)
327 ERR("Incoming connection on an output pin! (%p, %p)\n", pReceivePin
, pmt
);
332 HRESULT WINAPI
OutputPin_Disconnect(IPin
* iface
)
335 OutputPin
*This
= (OutputPin
*)iface
;
339 EnterCriticalSection(This
->pin
.pCritSec
);
341 if (This
->pMemInputPin
)
343 IMemInputPin_Release(This
->pMemInputPin
);
344 This
->pMemInputPin
= NULL
;
346 if (This
->pin
.pConnectedTo
)
348 IPin_Release(This
->pin
.pConnectedTo
);
349 This
->pin
.pConnectedTo
= NULL
;
355 LeaveCriticalSection(This
->pin
.pCritSec
);
360 HRESULT
OutputPin_GetDeliveryBuffer(OutputPin
* This
, IMediaSample
** ppSample
, const REFERENCE_TIME
* tStart
, const REFERENCE_TIME
* tStop
, DWORD dwFlags
)
364 TRACE("(%p, %p, %p, %lx)\n", ppSample
, tStart
, tStop
, dwFlags
);
366 EnterCriticalSection(This
->pin
.pCritSec
);
368 if (!This
->pin
.pConnectedTo
)
369 hr
= VFW_E_NOT_CONNECTED
;
372 IMemAllocator
* pAlloc
= NULL
;
374 hr
= IMemInputPin_GetAllocator(This
->pMemInputPin
, &pAlloc
);
377 hr
= IMemAllocator_GetBuffer(pAlloc
, ppSample
, (REFERENCE_TIME
*)tStart
, (REFERENCE_TIME
*)tStop
, dwFlags
);
380 hr
= IMediaSample_SetTime(*ppSample
, (REFERENCE_TIME
*)tStart
, (REFERENCE_TIME
*)tStop
);
383 IMemAllocator_Release(pAlloc
);
386 LeaveCriticalSection(This
->pin
.pCritSec
);
391 HRESULT
OutputPin_SendSample(OutputPin
* This
, IMediaSample
* pSample
)
394 IMemInputPin
* pMemConnected
= NULL
;
396 EnterCriticalSection(This
->pin
.pCritSec
);
398 if (!This
->pin
.pConnectedTo
|| !This
->pMemInputPin
)
399 hr
= VFW_E_NOT_CONNECTED
;
402 /* we don't have the lock held when using This->pMemInputPin,
403 * so we need to AddRef it to stop it being deleted while we are
405 pMemConnected
= This
->pMemInputPin
;
406 IMemInputPin_AddRef(pMemConnected
);
409 LeaveCriticalSection(This
->pin
.pCritSec
);
413 /* NOTE: if we are in a critical section when Receive is called
414 * then it causes some problems (most notably with the native Video
415 * Renderer) if we are re-entered for whatever reason */
416 hr
= IMemInputPin_Receive(pMemConnected
, pSample
);
417 IMemInputPin_Release(pMemConnected
);