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 * - we don't do anything with indices yet (we could use them when seeking)
22 * - we don't support multiple RIFF sections (i.e. large AVI files > 2Gb)
25 #include "quartz_private.h"
26 #include "control_private.h"
37 #include "wine/unicode.h"
38 #include "wine/debug.h"
43 WINE_DEFAULT_DEBUG_CHANNEL(quartz
);
45 static const WCHAR wcsInputPinName
[] = {'i','n','p','u','t',' ','p','i','n',0};
46 static const struct IBaseFilterVtbl AVISplitter_Vtbl
;
47 static const struct IMediaSeekingVtbl AVISplitter_Seeking_Vtbl
;
48 static const struct IPinVtbl AVISplitter_OutputPin_Vtbl
;
49 static const struct IPinVtbl AVISplitter_InputPin_Vtbl
;
51 static HRESULT
AVISplitter_Sample(LPVOID iface
, IMediaSample
* pSample
);
52 static HRESULT
AVISplitter_OutputPin_QueryAccept(LPVOID iface
, const AM_MEDIA_TYPE
* pmt
);
53 static HRESULT
AVISplitter_QueryAccept(LPVOID iface
, const AM_MEDIA_TYPE
* pmt
);
54 static HRESULT
AVISplitter_InputPin_PreConnect(IPin
* iface
, IPin
* pConnectPin
);
55 static HRESULT
AVISplitter_ChangeStart(LPVOID iface
);
56 static HRESULT
AVISplitter_ChangeStop(LPVOID iface
);
57 static HRESULT
AVISplitter_ChangeRate(LPVOID iface
);
59 static HRESULT
AVISplitter_InputPin_Construct(const PIN_INFO
* pPinInfo
, SAMPLEPROC pSampleProc
, LPVOID pUserData
, QUERYACCEPTPROC pQueryAccept
, LPCRITICAL_SECTION pCritSec
, IPin
** ppPin
);
61 typedef struct AVISplitter
63 const IBaseFilterVtbl
* lpVtbl
;
66 CRITICAL_SECTION csFilter
;
68 REFERENCE_TIME rtStreamStart
;
69 IReferenceClock
* pClock
;
70 FILTER_INFO filterInfo
;
75 IMediaSample
* pCurrentSample
;
76 RIFFCHUNK CurrentChunk
;
77 LONGLONG CurrentChunkOffset
; /* in media time */
79 AVIMAINHEADER AviHeader
;
82 typedef struct AVISplitter_OutputPin
88 DWORD dwSamplesProcessed
;
91 MediaSeekingImpl mediaSeeking
;
92 } AVISplitter_OutputPin
;
95 #define _IMediaSeeking_Offset ((int)(&(((AVISplitter_OutputPin*)0)->mediaSeeking)))
96 #define ICOM_THIS_From_IMediaSeeking(impl, iface) impl* This = (impl*)(((char*)iface)-_IMediaSeeking_Offset);
98 HRESULT
AVISplitter_create(IUnknown
* pUnkOuter
, LPVOID
* ppv
)
102 AVISplitter
* pAviSplit
;
104 TRACE("(%p, %p)\n", pUnkOuter
, ppv
);
109 return CLASS_E_NOAGGREGATION
;
111 pAviSplit
= CoTaskMemAlloc(sizeof(AVISplitter
));
113 pAviSplit
->lpVtbl
= &AVISplitter_Vtbl
;
114 pAviSplit
->refCount
= 1;
115 InitializeCriticalSection(&pAviSplit
->csFilter
);
116 pAviSplit
->state
= State_Stopped
;
117 pAviSplit
->pClock
= NULL
;
118 pAviSplit
->pCurrentSample
= NULL
;
119 ZeroMemory(&pAviSplit
->filterInfo
, sizeof(FILTER_INFO
));
121 pAviSplit
->cStreams
= 0;
122 pAviSplit
->ppPins
= CoTaskMemAlloc(1 * sizeof(IPin
*));
124 /* construct input pin */
125 piInput
.dir
= PINDIR_INPUT
;
126 piInput
.pFilter
= (IBaseFilter
*)pAviSplit
;
127 strncpyW(piInput
.achName
, wcsInputPinName
, sizeof(piInput
.achName
) / sizeof(piInput
.achName
[0]));
129 hr
= AVISplitter_InputPin_Construct(&piInput
, AVISplitter_Sample
, (LPVOID
)pAviSplit
, AVISplitter_QueryAccept
, &pAviSplit
->csFilter
, (IPin
**)&pAviSplit
->pInputPin
);
133 pAviSplit
->ppPins
[0] = (IPin
*)pAviSplit
->pInputPin
;
134 pAviSplit
->pInputPin
->fnPreConnect
= AVISplitter_InputPin_PreConnect
;
135 *ppv
= (LPVOID
)pAviSplit
;
139 CoTaskMemFree(pAviSplit
->ppPins
);
140 DeleteCriticalSection(&pAviSplit
->csFilter
);
141 CoTaskMemFree(pAviSplit
);
147 static HRESULT
AVISplitter_OutputPin_Init(const PIN_INFO
* pPinInfo
, ALLOCATOR_PROPERTIES
* props
, LPVOID pUserData
, QUERYACCEPTPROC pQueryAccept
, const AM_MEDIA_TYPE
* pmt
, float fSamplesPerSec
, LPCRITICAL_SECTION pCritSec
, AVISplitter_OutputPin
* pPinImpl
)
149 pPinImpl
->pmt
= CoTaskMemAlloc(sizeof(AM_MEDIA_TYPE
));
150 CopyMediaType(pPinImpl
->pmt
, pmt
);
151 pPinImpl
->dwSamplesProcessed
= 0;
152 pPinImpl
->dwSampleSize
= 0;
153 pPinImpl
->fSamplesPerSec
= fSamplesPerSec
;
155 MediaSeekingImpl_Init((LPVOID
)pPinInfo
->pFilter
, AVISplitter_ChangeStop
, AVISplitter_ChangeStart
, AVISplitter_ChangeRate
, &pPinImpl
->mediaSeeking
);
156 pPinImpl
->mediaSeeking
.lpVtbl
= &AVISplitter_Seeking_Vtbl
;
158 return OutputPin_Init(pPinInfo
, props
, pUserData
, pQueryAccept
, pCritSec
, &pPinImpl
->pin
);
161 static HRESULT
AVISplitter_OutputPin_Construct(const PIN_INFO
* pPinInfo
, ALLOCATOR_PROPERTIES
* props
, LPVOID pUserData
, QUERYACCEPTPROC pQueryAccept
, const AM_MEDIA_TYPE
* pmt
, float fSamplesPerSec
, LPCRITICAL_SECTION pCritSec
, IPin
** ppPin
)
163 AVISplitter_OutputPin
* pPinImpl
;
167 assert(pPinInfo
->dir
== PINDIR_OUTPUT
);
169 pPinImpl
= CoTaskMemAlloc(sizeof(AVISplitter_OutputPin
));
172 return E_OUTOFMEMORY
;
174 if (SUCCEEDED(AVISplitter_OutputPin_Init(pPinInfo
, props
, pUserData
, pQueryAccept
, pmt
, fSamplesPerSec
, pCritSec
, pPinImpl
)))
176 pPinImpl
->pin
.pin
.lpVtbl
= &AVISplitter_OutputPin_Vtbl
;
178 *ppPin
= (IPin
*)pPinImpl
;
184 static HRESULT WINAPI
AVISplitter_QueryInterface(IBaseFilter
* iface
, REFIID riid
, LPVOID
* ppv
)
186 AVISplitter
*This
= (AVISplitter
*)iface
;
187 TRACE("(%s, %p)\n", qzdebugstr_guid(riid
), ppv
);
191 if (IsEqualIID(riid
, &IID_IUnknown
))
193 else if (IsEqualIID(riid
, &IID_IPersist
))
195 else if (IsEqualIID(riid
, &IID_IMediaFilter
))
197 else if (IsEqualIID(riid
, &IID_IBaseFilter
))
202 IUnknown_AddRef((IUnknown
*)(*ppv
));
206 FIXME("No interface for %s!\n", qzdebugstr_guid(riid
));
208 return E_NOINTERFACE
;
211 static ULONG WINAPI
AVISplitter_AddRef(IBaseFilter
* iface
)
213 AVISplitter
*This
= (AVISplitter
*)iface
;
215 TRACE("(%p/%p)->() AddRef from %ld\n", This
, iface
, This
->refCount
);
217 return InterlockedIncrement(&This
->refCount
);
220 static ULONG WINAPI
AVISplitter_Release(IBaseFilter
* iface
)
222 AVISplitter
*This
= (AVISplitter
*)iface
;
224 TRACE("(%p/%p)->() Release from %ld\n", This
, iface
, This
->refCount
);
226 if (!InterlockedDecrement(&This
->refCount
))
230 DeleteCriticalSection(&This
->csFilter
);
232 IReferenceClock_Release(This
->pClock
);
234 for (i
= 0; i
< This
->cStreams
+ 1; i
++)
235 IPin_Release(This
->ppPins
[i
]);
237 HeapFree(GetProcessHeap(), 0, This
->ppPins
);
240 TRACE("Destroying AVI splitter\n");
246 return This
->refCount
;
249 /** IPersist methods **/
251 static HRESULT WINAPI
AVISplitter_GetClassID(IBaseFilter
* iface
, CLSID
* pClsid
)
253 TRACE("(%p)\n", pClsid
);
255 *pClsid
= CLSID_AviSplitter
;
260 /** IMediaFilter methods **/
262 static HRESULT WINAPI
AVISplitter_Stop(IBaseFilter
* iface
)
265 AVISplitter
*This
= (AVISplitter
*)iface
;
269 EnterCriticalSection(&This
->csFilter
);
271 hr
= PullPin_StopProcessing(This
->pInputPin
);
272 This
->state
= State_Stopped
;
274 LeaveCriticalSection(&This
->csFilter
);
279 static HRESULT WINAPI
AVISplitter_Pause(IBaseFilter
* iface
)
283 AVISplitter
*This
= (AVISplitter
*)iface
;
287 EnterCriticalSection(&This
->csFilter
);
289 bInit
= (This
->state
== State_Stopped
);
290 This
->state
= State_Paused
;
292 LeaveCriticalSection(&This
->csFilter
);
298 hr
= PullPin_Seek(This
->pInputPin
, This
->CurrentChunkOffset
, This
->EndOfFile
);
301 hr
= PullPin_InitProcessing(This
->pInputPin
);
305 for (i
= 1; i
< This
->cStreams
+ 1; i
++)
307 AVISplitter_OutputPin
* StreamPin
= (AVISplitter_OutputPin
*)This
->ppPins
[i
];
308 OutputPin_DeliverNewSegment((OutputPin
*)This
->ppPins
[i
], 0, (LONGLONG
)ceil(10000000.0 * (float)StreamPin
->dwLength
/ StreamPin
->fSamplesPerSec
), 1.0);
309 StreamPin
->mediaSeeking
.llDuration
= (LONGLONG
)ceil(10000000.0 * (float)StreamPin
->dwLength
/ StreamPin
->fSamplesPerSec
);
310 StreamPin
->mediaSeeking
.llStop
= (LONGLONG
)ceil(10000000.0 * (float)StreamPin
->dwLength
/ StreamPin
->fSamplesPerSec
);
311 OutputPin_CommitAllocator((OutputPin
*)This
->ppPins
[i
]);
314 /* FIXME: this is a little hacky: we have to deliver (at least?) one sample
315 * to each renderer before they will complete their transitions. We should probably
316 * seek through the stream for the first of each, rather than do it this way which is
317 * probably a bit prone to deadlocking */
318 hr
= PullPin_StartProcessing(This
->pInputPin
);
321 /* FIXME: else pause thread */
326 static HRESULT WINAPI
AVISplitter_Run(IBaseFilter
* iface
, REFERENCE_TIME tStart
)
329 AVISplitter
*This
= (AVISplitter
*)iface
;
332 TRACE("(%s)\n", wine_dbgstr_longlong(tStart
));
334 EnterCriticalSection(&This
->csFilter
);
336 This
->rtStreamStart
= tStart
;
337 This
->state
= State_Running
;
339 hr
= PullPin_InitProcessing(This
->pInputPin
);
343 for (i
= 1; i
< This
->cStreams
+ 1; i
++)
345 OutputPin_CommitAllocator((OutputPin
*)This
->ppPins
[i
]);
347 hr
= PullPin_StartProcessing(This
->pInputPin
);
350 LeaveCriticalSection(&This
->csFilter
);
355 static HRESULT WINAPI
AVISplitter_GetState(IBaseFilter
* iface
, DWORD dwMilliSecsTimeout
, FILTER_STATE
*pState
)
357 AVISplitter
*This
= (AVISplitter
*)iface
;
359 TRACE("(%ld, %p)\n", dwMilliSecsTimeout
, pState
);
361 EnterCriticalSection(&This
->csFilter
);
363 *pState
= This
->state
;
365 LeaveCriticalSection(&This
->csFilter
);
367 /* FIXME: this is a little bit unsafe, but I don't see that we can do this
368 * while in the critical section. Maybe we could copy the pointer and addref in the
369 * critical section and then release after this.
371 if (This
->pInputPin
&& (PullPin_WaitForStateChange(This
->pInputPin
, dwMilliSecsTimeout
) == S_FALSE
))
372 return VFW_S_STATE_INTERMEDIATE
;
377 static HRESULT WINAPI
AVISplitter_SetSyncSource(IBaseFilter
* iface
, IReferenceClock
*pClock
)
379 AVISplitter
*This
= (AVISplitter
*)iface
;
381 TRACE("(%p)\n", pClock
);
383 EnterCriticalSection(&This
->csFilter
);
386 IReferenceClock_Release(This
->pClock
);
387 This
->pClock
= pClock
;
389 IReferenceClock_AddRef(This
->pClock
);
391 LeaveCriticalSection(&This
->csFilter
);
396 static HRESULT WINAPI
AVISplitter_GetSyncSource(IBaseFilter
* iface
, IReferenceClock
**ppClock
)
398 AVISplitter
*This
= (AVISplitter
*)iface
;
400 TRACE("(%p)\n", ppClock
);
402 EnterCriticalSection(&This
->csFilter
);
404 *ppClock
= This
->pClock
;
406 IReferenceClock_AddRef(This
->pClock
);
408 LeaveCriticalSection(&This
->csFilter
);
413 /** IBaseFilter implementation **/
415 static HRESULT WINAPI
AVISplitter_EnumPins(IBaseFilter
* iface
, IEnumPins
**ppEnum
)
418 AVISplitter
*This
= (AVISplitter
*)iface
;
420 TRACE("(%p)\n", ppEnum
);
422 epd
.cPins
= This
->cStreams
+ 1; /* +1 for input pin */
423 epd
.ppPins
= This
->ppPins
;
424 return IEnumPinsImpl_Construct(&epd
, ppEnum
);
427 static HRESULT WINAPI
AVISplitter_FindPin(IBaseFilter
* iface
, LPCWSTR Id
, IPin
**ppPin
)
429 FIXME("AVISplitter::FindPin(...)\n");
431 /* FIXME: critical section */
436 static HRESULT WINAPI
AVISplitter_QueryFilterInfo(IBaseFilter
* iface
, FILTER_INFO
*pInfo
)
438 AVISplitter
*This
= (AVISplitter
*)iface
;
440 TRACE("(%p)\n", pInfo
);
442 strcpyW(pInfo
->achName
, This
->filterInfo
.achName
);
443 pInfo
->pGraph
= This
->filterInfo
.pGraph
;
446 IFilterGraph_AddRef(pInfo
->pGraph
);
451 static HRESULT WINAPI
AVISplitter_JoinFilterGraph(IBaseFilter
* iface
, IFilterGraph
*pGraph
, LPCWSTR pName
)
454 AVISplitter
*This
= (AVISplitter
*)iface
;
456 TRACE("(%p, %s)\n", pGraph
, debugstr_w(pName
));
458 EnterCriticalSection(&This
->csFilter
);
461 strcpyW(This
->filterInfo
.achName
, pName
);
463 *This
->filterInfo
.achName
= '\0';
464 This
->filterInfo
.pGraph
= pGraph
; /* NOTE: do NOT increase ref. count */
466 LeaveCriticalSection(&This
->csFilter
);
471 static HRESULT WINAPI
AVISplitter_QueryVendorInfo(IBaseFilter
* iface
, LPWSTR
*pVendorInfo
)
473 TRACE("(%p)\n", pVendorInfo
);
477 static const IBaseFilterVtbl AVISplitter_Vtbl
=
479 AVISplitter_QueryInterface
,
482 AVISplitter_GetClassID
,
486 AVISplitter_GetState
,
487 AVISplitter_SetSyncSource
,
488 AVISplitter_GetSyncSource
,
489 AVISplitter_EnumPins
,
491 AVISplitter_QueryFilterInfo
,
492 AVISplitter_JoinFilterGraph
,
493 AVISplitter_QueryVendorInfo
496 static HRESULT
AVISplitter_NextChunk(LONGLONG
* pllCurrentChunkOffset
, RIFFCHUNK
* pCurrentChunk
, const REFERENCE_TIME
* tStart
, const REFERENCE_TIME
* tStop
, const BYTE
* pbSrcStream
)
498 *pllCurrentChunkOffset
+= MEDIATIME_FROM_BYTES(sizeof(RIFFCHUNK
) + RIFFROUND(pCurrentChunk
->cb
));
500 if (*pllCurrentChunkOffset
> *tStop
)
501 return S_FALSE
; /* no more data - we couldn't even get the next chunk header! */
502 else if (*pllCurrentChunkOffset
+ MEDIATIME_FROM_BYTES(sizeof(RIFFCHUNK
)) >= *tStop
)
504 memcpy(pCurrentChunk
, pbSrcStream
+ (DWORD
)BYTES_FROM_MEDIATIME(*pllCurrentChunkOffset
- *tStart
), (DWORD
)BYTES_FROM_MEDIATIME(*tStop
- *pllCurrentChunkOffset
));
505 return S_FALSE
; /* no more data */
508 memcpy(pCurrentChunk
, pbSrcStream
+ (DWORD
)BYTES_FROM_MEDIATIME(*pllCurrentChunkOffset
- *tStart
), sizeof(RIFFCHUNK
));
513 static HRESULT
AVISplitter_Sample(LPVOID iface
, IMediaSample
* pSample
)
515 AVISplitter
*This
= (AVISplitter
*)iface
;
516 LPBYTE pbSrcStream
= NULL
;
517 long cbSrcStream
= 0;
518 REFERENCE_TIME tStart
, tStop
;
520 BOOL bMoreData
= TRUE
;
522 hr
= IMediaSample_GetPointer(pSample
, &pbSrcStream
);
524 hr
= IMediaSample_GetTime(pSample
, &tStart
, &tStop
);
526 cbSrcStream
= IMediaSample_GetActualDataLength(pSample
);
528 /* trace removed for performance reasons */
529 /* TRACE("(%p)\n", pSample); */
531 assert(BYTES_FROM_MEDIATIME(tStop
- tStart
) == cbSrcStream
);
533 if (This
->CurrentChunkOffset
<= tStart
&& This
->CurrentChunkOffset
+ MEDIATIME_FROM_BYTES(sizeof(RIFFCHUNK
)) > tStart
)
535 DWORD offset
= (DWORD
)BYTES_FROM_MEDIATIME(tStart
- This
->CurrentChunkOffset
);
536 assert(offset
<= sizeof(RIFFCHUNK
));
537 memcpy((BYTE
*)&This
->CurrentChunk
+ offset
, pbSrcStream
, sizeof(RIFFCHUNK
) - offset
);
539 else if (This
->CurrentChunkOffset
> tStart
)
541 DWORD offset
= (DWORD
)BYTES_FROM_MEDIATIME(This
->CurrentChunkOffset
- tStart
);
542 if (offset
>= (DWORD
)cbSrcStream
)
544 FIXME("large offset\n");
548 memcpy(&This
->CurrentChunk
, pbSrcStream
+ offset
, sizeof(RIFFCHUNK
));
551 assert(This
->CurrentChunkOffset
+ MEDIATIME_FROM_BYTES(sizeof(RIFFCHUNK
)) < tStop
);
557 long chunk_remaining_bytes
= 0;
560 AVISplitter_OutputPin
* pOutputPin
;
561 BOOL bSyncPoint
= TRUE
;
563 if (This
->CurrentChunkOffset
>= tStart
)
564 offset_src
= (long)BYTES_FROM_MEDIATIME(This
->CurrentChunkOffset
- tStart
) + sizeof(RIFFCHUNK
);
568 switch (This
->CurrentChunk
.fcc
)
571 case aviFCC('i','d','x','1'): /* Index is not handled */
572 /* silently ignore */
573 if (S_FALSE
== AVISplitter_NextChunk(&This
->CurrentChunkOffset
, &This
->CurrentChunk
, &tStart
, &tStop
, pbSrcStream
))
577 /* We only handle the 'rec ' list which contains the stream data */
578 if ((*(DWORD
*)(pbSrcStream
+ BYTES_FROM_MEDIATIME(This
->CurrentChunkOffset
-tStart
) + sizeof(RIFFCHUNK
))) == aviFCC('r','e','c',' '))
580 /* FIXME: We only advanced to the first chunk inside the list without keeping track that we are in it.
581 * This is not clean and the parser should be improved for that but it is enough for most AVI files. */
582 This
->CurrentChunkOffset
= MEDIATIME_FROM_BYTES(BYTES_FROM_MEDIATIME(This
->CurrentChunkOffset
) + sizeof(RIFFLIST
));
583 This
->CurrentChunk
= *(RIFFCHUNK
*) (pbSrcStream
+ BYTES_FROM_MEDIATIME(This
->CurrentChunkOffset
-tStart
));
584 offset_src
= (long)BYTES_FROM_MEDIATIME(This
->CurrentChunkOffset
- tStart
) + sizeof(RIFFCHUNK
);
587 else if (S_FALSE
== AVISplitter_NextChunk(&This
->CurrentChunkOffset
, &This
->CurrentChunk
, &tStart
, &tStop
, pbSrcStream
))
592 #if 0 /* According to the AVI specs, a stream data chunk should be ABXX where AB is the stream number and X means don't care */
593 switch (TWOCCFromFOURCC(This
->CurrentChunk
.fcc
))
595 case cktypeDIBcompressed
:
599 /* FIXME: check that pin is of type video */
601 case cktypeWAVEbytes
:
602 /* FIXME: check that pin is of type audio */
604 case cktypePALchange
:
605 FIXME("handle palette change\n");
608 FIXME("Skipping unknown chunk type: %s at file offset 0x%lx\n", debugstr_an((LPSTR
)&This
->CurrentChunk
.fcc
, 4), (DWORD
)BYTES_FROM_MEDIATIME(This
->CurrentChunkOffset
));
609 if (S_FALSE
== AVISplitter_NextChunk(&This
->CurrentChunkOffset
, &This
->CurrentChunk
, &tStart
, &tStop
, pbSrcStream
))
616 streamId
= StreamFromFOURCC(This
->CurrentChunk
.fcc
);
618 if (streamId
> This
->cStreams
)
620 ERR("Corrupted AVI file (contains stream id %d, but supposed to only have %ld streams)\n", streamId
, This
->cStreams
);
624 pOutputPin
= (AVISplitter_OutputPin
*)This
->ppPins
[streamId
+ 1];
626 if (!This
->pCurrentSample
)
628 /* cache media sample until it is ready to be despatched
629 * (i.e. we reach the end of the chunk) */
630 hr
= OutputPin_GetDeliveryBuffer(&pOutputPin
->pin
, &This
->pCurrentSample
, NULL
, NULL
, 0);
634 hr
= IMediaSample_SetActualDataLength(This
->pCurrentSample
, 0);
639 TRACE("Skipping sending sample for stream %02d due to error (%lx)\n", streamId
, hr
);
640 This
->pCurrentSample
= NULL
;
641 if (S_FALSE
== AVISplitter_NextChunk(&This
->CurrentChunkOffset
, &This
->CurrentChunk
, &tStart
, &tStop
, pbSrcStream
))
647 hr
= IMediaSample_GetPointer(This
->pCurrentSample
, &pbDstStream
);
651 cbDstStream
= IMediaSample_GetSize(This
->pCurrentSample
);
653 chunk_remaining_bytes
= (long)BYTES_FROM_MEDIATIME(This
->CurrentChunkOffset
+ MEDIATIME_FROM_BYTES(This
->CurrentChunk
.cb
+ sizeof(RIFFCHUNK
)) - tStart
) - offset_src
;
655 assert(chunk_remaining_bytes
>= 0);
656 assert(chunk_remaining_bytes
<= cbDstStream
- IMediaSample_GetActualDataLength(This
->pCurrentSample
));
658 /* trace removed for performance reasons */
659 /* TRACE("chunk_remaining_bytes: 0x%lx, cbSrcStream: 0x%lx, offset_src: 0x%lx\n", chunk_remaining_bytes, cbSrcStream, offset_src); */
662 if (chunk_remaining_bytes
<= cbSrcStream
- offset_src
)
666 memcpy(pbDstStream
+ IMediaSample_GetActualDataLength(This
->pCurrentSample
), pbSrcStream
+ offset_src
, chunk_remaining_bytes
);
667 hr
= IMediaSample_SetActualDataLength(This
->pCurrentSample
, chunk_remaining_bytes
+ IMediaSample_GetActualDataLength(This
->pCurrentSample
));
673 REFERENCE_TIME tAviStart
, tAviStop
;
676 if (pOutputPin
->dwSamplesProcessed
== 0)
677 IMediaSample_SetDiscontinuity(This
->pCurrentSample
, TRUE
);
679 IMediaSample_SetSyncPoint(This
->pCurrentSample
, bSyncPoint
);
681 pOutputPin
->dwSamplesProcessed
++;
683 if (pOutputPin
->dwSampleSize
)
684 tAviStart
= (LONGLONG
)ceil(10000000.0 * (float)(pOutputPin
->dwSamplesProcessed
- 1) * (float)IMediaSample_GetActualDataLength(This
->pCurrentSample
) / ((float)pOutputPin
->dwSampleSize
* pOutputPin
->fSamplesPerSec
));
686 tAviStart
= (LONGLONG
)ceil(10000000.0 * (float)(pOutputPin
->dwSamplesProcessed
- 1) / (float)pOutputPin
->fSamplesPerSec
);
687 if (pOutputPin
->dwSampleSize
)
688 tAviStop
= (LONGLONG
)ceil(10000000.0 * (float)pOutputPin
->dwSamplesProcessed
* (float)IMediaSample_GetActualDataLength(This
->pCurrentSample
) / ((float)pOutputPin
->dwSampleSize
* pOutputPin
->fSamplesPerSec
));
690 tAviStop
= (LONGLONG
)ceil(10000000.0 * (float)pOutputPin
->dwSamplesProcessed
/ (float)pOutputPin
->fSamplesPerSec
);
692 IMediaSample_SetTime(This
->pCurrentSample
, &tAviStart
, &tAviStop
);
694 hr
= OutputPin_SendSample(&pOutputPin
->pin
, This
->pCurrentSample
);
695 if (hr
!= S_OK
&& hr
!= VFW_E_NOT_CONNECTED
)
696 ERR("Error sending sample (%lx)\n", hr
);
699 if (This
->pCurrentSample
)
700 IMediaSample_Release(This
->pCurrentSample
);
702 This
->pCurrentSample
= NULL
;
704 if (S_FALSE
== AVISplitter_NextChunk(&This
->CurrentChunkOffset
, &This
->CurrentChunk
, &tStart
, &tStop
, pbSrcStream
))
711 memcpy(pbDstStream
+ IMediaSample_GetActualDataLength(This
->pCurrentSample
), pbSrcStream
+ offset_src
, cbSrcStream
- offset_src
);
712 IMediaSample_SetActualDataLength(This
->pCurrentSample
, cbSrcStream
- offset_src
+ IMediaSample_GetActualDataLength(This
->pCurrentSample
));
720 static HRESULT
AVISplitter_QueryAccept(LPVOID iface
, const AM_MEDIA_TYPE
* pmt
)
722 if (IsEqualIID(&pmt
->majortype
, &MEDIATYPE_Stream
) && IsEqualIID(&pmt
->subtype
, &MEDIASUBTYPE_Avi
))
727 static HRESULT
AVISplitter_ProcessStreamList(AVISplitter
* This
, const BYTE
* pData
, DWORD cb
)
730 const RIFFCHUNK
* pChunk
;
734 float fSamplesPerSec
= 0.0f
;
735 DWORD dwSampleSize
= 0;
737 ALLOCATOR_PROPERTIES props
;
738 static const WCHAR wszStreamTemplate
[] = {'S','t','r','e','a','m',' ','%','0','2','d',0};
742 props
.cbBuffer
= 0x20000;
745 ZeroMemory(&amt
, sizeof(amt
));
746 piOutput
.dir
= PINDIR_OUTPUT
;
747 piOutput
.pFilter
= (IBaseFilter
*)This
;
748 wsprintfW(piOutput
.achName
, wszStreamTemplate
, This
->cStreams
);
750 for (pChunk
= (const RIFFCHUNK
*)pData
;
751 ((const BYTE
*)pChunk
>= pData
) && ((const BYTE
*)pChunk
+ sizeof(RIFFCHUNK
) < pData
+ cb
) && (pChunk
->cb
> 0);
752 pChunk
= (const RIFFCHUNK
*)((const BYTE
*)pChunk
+ sizeof(RIFFCHUNK
) + pChunk
->cb
)
757 case ckidSTREAMHEADER
:
759 const AVISTREAMHEADER
* pStrHdr
= (const AVISTREAMHEADER
*)pChunk
;
760 TRACE("processing stream header\n");
762 fSamplesPerSec
= (float)pStrHdr
->dwRate
/ (float)pStrHdr
->dwScale
;
764 switch (pStrHdr
->fccType
)
766 case streamtypeVIDEO
:
767 memcpy(&amt
.formattype
, &FORMAT_VideoInfo
, sizeof(GUID
));
771 case streamtypeAUDIO
:
772 memcpy(&amt
.formattype
, &FORMAT_WaveFormatEx
, sizeof(GUID
));
775 memcpy(&amt
.formattype
, &FORMAT_None
, sizeof(GUID
));
777 memcpy(&amt
.majortype
, &MEDIATYPE_Video
, sizeof(GUID
));
778 amt
.majortype
.Data1
= pStrHdr
->fccType
;
779 memcpy(&amt
.subtype
, &MEDIATYPE_Video
, sizeof(GUID
));
780 amt
.subtype
.Data1
= pStrHdr
->fccHandler
;
781 TRACE("Subtype FCC: %.04s\n", (LPCSTR
)&pStrHdr
->fccHandler
);
782 amt
.lSampleSize
= pStrHdr
->dwSampleSize
;
783 amt
.bFixedSizeSamples
= (amt
.lSampleSize
!= 0);
785 /* FIXME: Is this right? */
786 if (!amt
.lSampleSize
)
792 amt
.bTemporalCompression
= IsEqualGUID(&amt
.majortype
, &MEDIATYPE_Video
); /* FIXME? */
793 dwSampleSize
= pStrHdr
->dwSampleSize
;
794 dwLength
= pStrHdr
->dwLength
;
796 dwLength
= This
->AviHeader
.dwTotalFrames
;
798 if (pStrHdr
->dwSuggestedBufferSize
)
799 props
.cbBuffer
= pStrHdr
->dwSuggestedBufferSize
;
803 case ckidSTREAMFORMAT
:
804 TRACE("processing stream format data\n");
805 if (IsEqualIID(&amt
.formattype
, &FORMAT_VideoInfo
))
807 VIDEOINFOHEADER
* pvi
;
808 /* biCompression member appears to override the value in the stream header.
809 * i.e. the stream header can say something completely contradictory to what
810 * is in the BITMAPINFOHEADER! */
811 if (pChunk
->cb
< sizeof(BITMAPINFOHEADER
))
813 ERR("Not enough bytes for BITMAPINFOHEADER\n");
816 amt
.cbFormat
= sizeof(VIDEOINFOHEADER
) - sizeof(BITMAPINFOHEADER
) + pChunk
->cb
;
817 amt
.pbFormat
= CoTaskMemAlloc(amt
.cbFormat
);
818 ZeroMemory(amt
.pbFormat
, amt
.cbFormat
);
819 pvi
= (VIDEOINFOHEADER
*)amt
.pbFormat
;
820 pvi
->AvgTimePerFrame
= (LONGLONG
)(10000000.0 / fSamplesPerSec
);
821 CopyMemory(&pvi
->bmiHeader
, (const BYTE
*)(pChunk
+ 1), pChunk
->cb
);
822 if (pvi
->bmiHeader
.biCompression
)
823 amt
.subtype
.Data1
= pvi
->bmiHeader
.biCompression
;
827 amt
.cbFormat
= pChunk
->cb
;
828 amt
.pbFormat
= CoTaskMemAlloc(amt
.cbFormat
);
829 CopyMemory(amt
.pbFormat
, (const BYTE
*)(pChunk
+ 1), amt
.cbFormat
);
833 TRACE("processing stream name\n");
834 /* FIXME: this doesn't exactly match native version (we omit the "##)" prefix), but hey... */
835 MultiByteToWideChar(CP_ACP
, 0, (LPCSTR
)(pChunk
+ 1), pChunk
->cb
, piOutput
.achName
, sizeof(piOutput
.achName
) / sizeof(piOutput
.achName
[0]));
837 case ckidSTREAMHANDLERDATA
:
838 FIXME("process stream handler data\n");
841 TRACE("JUNK chunk ignored\n");
844 FIXME("unknown chunk type \"%.04s\" ignored\n", (LPCSTR
)&pChunk
->fcc
);
848 if (IsEqualGUID(&amt
.formattype
, &FORMAT_WaveFormatEx
))
850 memcpy(&amt
.subtype
, &MEDIATYPE_Video
, sizeof(GUID
));
851 amt
.subtype
.Data1
= ((WAVEFORMATEX
*)amt
.pbFormat
)->wFormatTag
;
854 dump_AM_MEDIA_TYPE(&amt
);
855 TRACE("fSamplesPerSec = %f\n", (double)fSamplesPerSec
);
856 TRACE("dwSampleSize = %lx\n", dwSampleSize
);
857 TRACE("dwLength = %lx\n", dwLength
);
859 ppOldPins
= This
->ppPins
;
861 This
->ppPins
= HeapAlloc(GetProcessHeap(), 0, (This
->cStreams
+ 2) * sizeof(IPin
*));
862 memcpy(This
->ppPins
, ppOldPins
, (This
->cStreams
+ 1) * sizeof(IPin
*));
864 hr
= AVISplitter_OutputPin_Construct(&piOutput
, &props
, NULL
, AVISplitter_OutputPin_QueryAccept
, &amt
, fSamplesPerSec
, &This
->csFilter
, This
->ppPins
+ This
->cStreams
+ 1);
868 ((AVISplitter_OutputPin
*)(This
->ppPins
[This
->cStreams
+ 1]))->dwSampleSize
= dwSampleSize
;
869 ((AVISplitter_OutputPin
*)(This
->ppPins
[This
->cStreams
+ 1]))->dwLength
= dwLength
;
870 ((AVISplitter_OutputPin
*)(This
->ppPins
[This
->cStreams
+ 1]))->pin
.pin
.pUserData
= (LPVOID
)This
->ppPins
[This
->cStreams
+ 1];
872 HeapFree(GetProcessHeap(), 0, ppOldPins
);
876 HeapFree(GetProcessHeap(), 0, This
->ppPins
);
877 This
->ppPins
= ppOldPins
;
878 ERR("Failed with error %lx\n", hr
);
884 static HRESULT
AVISplitter_RemoveOutputPins(AVISplitter
* This
)
886 /* NOTE: should be in critical section when calling this function */
889 IPin
** ppOldPins
= This
->ppPins
;
891 /* reduce the pin array down to 1 (just our input pin) */
892 This
->ppPins
= HeapAlloc(GetProcessHeap(), 0, sizeof(IPin
*) * 1);
893 memcpy(This
->ppPins
, ppOldPins
, sizeof(IPin
*) * 1);
895 for (i
= 0; i
< This
->cStreams
; i
++)
897 OutputPin_DeliverDisconnect((OutputPin
*)ppOldPins
[i
+ 1]);
898 IPin_Release(ppOldPins
[i
+ 1]);
902 HeapFree(GetProcessHeap(), 0, ppOldPins
);
907 /* FIXME: fix leaks on failure here */
908 static HRESULT
AVISplitter_InputPin_PreConnect(IPin
* iface
, IPin
* pConnectPin
)
910 PullPin
*This
= (PullPin
*)iface
;
913 LONGLONG pos
= 0; /* in bytes */
915 RIFFCHUNK
* pCurrentChunk
;
916 AVISplitter
* pAviSplit
= (AVISplitter
*)This
->pin
.pinInfo
.pFilter
;
918 hr
= IAsyncReader_SyncRead(This
->pReader
, pos
, sizeof(list
), (BYTE
*)&list
);
921 if (list
.fcc
!= ckidRIFF
)
923 ERR("Input stream not a RIFF file\n");
926 if (list
.cb
> 1 * 1024 * 1024 * 1024) /* cannot be more than 1Gb in size */
928 ERR("Input stream violates RIFF spec\n");
931 if (list
.fccListType
!= ckidAVI
)
933 ERR("Input stream not an AVI RIFF file\n");
937 hr
= IAsyncReader_SyncRead(This
->pReader
, pos
, sizeof(list
), (BYTE
*)&list
);
938 if (list
.fcc
!= ckidLIST
)
940 ERR("Expected LIST chunk, but got %.04s\n", (LPSTR
)&list
.fcc
);
943 if (list
.fccListType
!= ckidHEADERLIST
)
945 ERR("Header list expected. Got: %.04s\n", (LPSTR
)&list
.fccListType
);
949 pBuffer
= HeapAlloc(GetProcessHeap(), 0, list
.cb
- sizeof(RIFFLIST
) + sizeof(RIFFCHUNK
));
950 hr
= IAsyncReader_SyncRead(This
->pReader
, pos
+ sizeof(list
), list
.cb
- sizeof(RIFFLIST
) + sizeof(RIFFCHUNK
), pBuffer
);
952 pAviSplit
->AviHeader
.cb
= 0;
954 for (pCurrentChunk
= (RIFFCHUNK
*)pBuffer
; (BYTE
*)pCurrentChunk
+ sizeof(*pCurrentChunk
) < pBuffer
+ list
.cb
; pCurrentChunk
= (RIFFCHUNK
*)(((BYTE
*)pCurrentChunk
) + sizeof(*pCurrentChunk
) + pCurrentChunk
->cb
))
958 switch (pCurrentChunk
->fcc
)
960 case ckidMAINAVIHEADER
:
961 /* AVIMAINHEADER includes the structure that is pCurrentChunk at the moment */
962 memcpy(&pAviSplit
->AviHeader
, pCurrentChunk
, sizeof(pAviSplit
->AviHeader
));
965 pList
= (RIFFLIST
*)pCurrentChunk
;
966 switch (pList
->fccListType
)
969 hr
= AVISplitter_ProcessStreamList(pAviSplit
, (BYTE
*)pCurrentChunk
+ sizeof(RIFFLIST
), pCurrentChunk
->cb
+ sizeof(RIFFCHUNK
) - sizeof(RIFFLIST
));
972 FIXME("process ODML header\n");
980 FIXME("unrecognised header list type: %.04s\n", (LPSTR
)&pCurrentChunk
->fcc
);
983 HeapFree(GetProcessHeap(), 0, pBuffer
);
985 if (pAviSplit
->AviHeader
.cb
!= sizeof(pAviSplit
->AviHeader
) - sizeof(RIFFCHUNK
))
987 ERR("Avi Header wrong size!\n");
991 pos
+= sizeof(RIFFCHUNK
) + list
.cb
;
992 hr
= IAsyncReader_SyncRead(This
->pReader
, pos
, sizeof(list
), (BYTE
*)&list
);
994 if (list
.fcc
== ckidJUNK
)
996 pos
+= sizeof(RIFFCHUNK
) + list
.cb
;
997 hr
= IAsyncReader_SyncRead(This
->pReader
, pos
, sizeof(list
), (BYTE
*)&list
);
1000 if (list
.fcc
!= ckidLIST
)
1002 ERR("Expected LIST, but got %.04s\n", (LPSTR
)&list
.fcc
);
1005 if (list
.fccListType
!= ckidAVIMOVIE
)
1007 ERR("Expected AVI movie list, but got %.04s\n", (LPSTR
)&list
.fccListType
);
1013 pAviSplit
->CurrentChunkOffset
= MEDIATIME_FROM_BYTES(pos
+ sizeof(RIFFLIST
));
1014 pAviSplit
->EndOfFile
= MEDIATIME_FROM_BYTES(pos
+ list
.cb
+ sizeof(RIFFLIST
));
1015 hr
= IAsyncReader_SyncRead(This
->pReader
, BYTES_FROM_MEDIATIME(pAviSplit
->CurrentChunkOffset
), sizeof(pAviSplit
->CurrentChunk
), (BYTE
*)&pAviSplit
->CurrentChunk
);
1021 TRACE("AVI File ok\n");
1026 static HRESULT
AVISplitter_ChangeStart(LPVOID iface
)
1028 FIXME("(%p)\n", iface
);
1032 static HRESULT
AVISplitter_ChangeStop(LPVOID iface
)
1034 FIXME("(%p)\n", iface
);
1038 static HRESULT
AVISplitter_ChangeRate(LPVOID iface
)
1040 FIXME("(%p)\n", iface
);
1045 static HRESULT WINAPI
AVISplitter_Seeking_QueryInterface(IMediaSeeking
* iface
, REFIID riid
, LPVOID
* ppv
)
1047 ICOM_THIS_From_IMediaSeeking(AVISplitter_OutputPin
, iface
);
1049 return IUnknown_QueryInterface((IUnknown
*)This
, riid
, ppv
);
1052 static ULONG WINAPI
AVISplitter_Seeking_AddRef(IMediaSeeking
* iface
)
1054 ICOM_THIS_From_IMediaSeeking(AVISplitter_OutputPin
, iface
);
1056 return IUnknown_AddRef((IUnknown
*)This
);
1059 static ULONG WINAPI
AVISplitter_Seeking_Release(IMediaSeeking
* iface
)
1061 ICOM_THIS_From_IMediaSeeking(AVISplitter_OutputPin
, iface
);
1063 return IUnknown_Release((IUnknown
*)This
);
1066 static const IMediaSeekingVtbl AVISplitter_Seeking_Vtbl
=
1068 AVISplitter_Seeking_QueryInterface
,
1069 AVISplitter_Seeking_AddRef
,
1070 AVISplitter_Seeking_Release
,
1071 MediaSeekingImpl_GetCapabilities
,
1072 MediaSeekingImpl_CheckCapabilities
,
1073 MediaSeekingImpl_IsFormatSupported
,
1074 MediaSeekingImpl_QueryPreferredFormat
,
1075 MediaSeekingImpl_GetTimeFormat
,
1076 MediaSeekingImpl_IsUsingTimeFormat
,
1077 MediaSeekingImpl_SetTimeFormat
,
1078 MediaSeekingImpl_GetDuration
,
1079 MediaSeekingImpl_GetStopPosition
,
1080 MediaSeekingImpl_GetCurrentPosition
,
1081 MediaSeekingImpl_ConvertTimeFormat
,
1082 MediaSeekingImpl_SetPositions
,
1083 MediaSeekingImpl_GetPositions
,
1084 MediaSeekingImpl_GetAvailable
,
1085 MediaSeekingImpl_SetRate
,
1086 MediaSeekingImpl_GetRate
,
1087 MediaSeekingImpl_GetPreroll
1090 HRESULT WINAPI
AVISplitter_OutputPin_QueryInterface(IPin
* iface
, REFIID riid
, LPVOID
* ppv
)
1092 AVISplitter_OutputPin
*This
= (AVISplitter_OutputPin
*)iface
;
1094 TRACE("(%s, %p)\n", qzdebugstr_guid(riid
), ppv
);
1098 if (IsEqualIID(riid
, &IID_IUnknown
))
1099 *ppv
= (LPVOID
)iface
;
1100 else if (IsEqualIID(riid
, &IID_IPin
))
1101 *ppv
= (LPVOID
)iface
;
1102 else if (IsEqualIID(riid
, &IID_IMediaSeeking
))
1103 *ppv
= (LPVOID
)&This
->mediaSeeking
;
1107 IUnknown_AddRef((IUnknown
*)(*ppv
));
1111 FIXME("No interface for %s!\n", qzdebugstr_guid(riid
));
1113 return E_NOINTERFACE
;
1116 static ULONG WINAPI
AVISplitter_OutputPin_Release(IPin
* iface
)
1118 AVISplitter_OutputPin
*This
= (AVISplitter_OutputPin
*)iface
;
1122 if (!InterlockedDecrement(&This
->pin
.pin
.refCount
))
1124 DeleteMediaType(This
->pmt
);
1125 CoTaskMemFree(This
->pmt
);
1126 DeleteMediaType(&This
->pin
.pin
.mtCurrent
);
1127 CoTaskMemFree(This
);
1130 return This
->pin
.pin
.refCount
;
1133 static HRESULT WINAPI
AVISplitter_OutputPin_EnumMediaTypes(IPin
* iface
, IEnumMediaTypes
** ppEnum
)
1135 ENUMMEDIADETAILS emd
;
1136 AVISplitter_OutputPin
*This
= (AVISplitter_OutputPin
*)iface
;
1138 TRACE("(%p)\n", ppEnum
);
1140 /* override this method to allow enumeration of your types */
1141 emd
.cMediaTypes
= 1;
1142 emd
.pMediaTypes
= This
->pmt
;
1144 return IEnumMediaTypesImpl_Construct(&emd
, ppEnum
);
1147 static HRESULT
AVISplitter_OutputPin_QueryAccept(LPVOID iface
, const AM_MEDIA_TYPE
* pmt
)
1149 AVISplitter_OutputPin
*This
= (AVISplitter_OutputPin
*)iface
;
1152 dump_AM_MEDIA_TYPE(pmt
);
1154 return (memcmp(This
->pmt
, pmt
, sizeof(AM_MEDIA_TYPE
)) == 0);
1157 static const IPinVtbl AVISplitter_OutputPin_Vtbl
=
1159 AVISplitter_OutputPin_QueryInterface
,
1161 AVISplitter_OutputPin_Release
,
1163 OutputPin_ReceiveConnection
,
1164 OutputPin_Disconnect
,
1165 IPinImpl_ConnectedTo
,
1166 IPinImpl_ConnectionMediaType
,
1167 IPinImpl_QueryPinInfo
,
1168 IPinImpl_QueryDirection
,
1170 IPinImpl_QueryAccept
,
1171 AVISplitter_OutputPin_EnumMediaTypes
,
1172 IPinImpl_QueryInternalConnections
,
1173 OutputPin_EndOfStream
,
1174 OutputPin_BeginFlush
,
1176 OutputPin_NewSegment
1179 static HRESULT
AVISplitter_InputPin_Construct(const PIN_INFO
* pPinInfo
, SAMPLEPROC pSampleProc
, LPVOID pUserData
, QUERYACCEPTPROC pQueryAccept
, LPCRITICAL_SECTION pCritSec
, IPin
** ppPin
)
1185 if (pPinInfo
->dir
!= PINDIR_INPUT
)
1187 ERR("Pin direction(%x) != PINDIR_INPUT\n", pPinInfo
->dir
);
1188 return E_INVALIDARG
;
1191 pPinImpl
= CoTaskMemAlloc(sizeof(*pPinImpl
));
1194 return E_OUTOFMEMORY
;
1196 if (SUCCEEDED(PullPin_Init(pPinInfo
, pSampleProc
, pUserData
, pQueryAccept
, pCritSec
, pPinImpl
)))
1198 pPinImpl
->pin
.lpVtbl
= &AVISplitter_InputPin_Vtbl
;
1200 *ppPin
= (IPin
*)(&pPinImpl
->pin
.lpVtbl
);
1206 static HRESULT WINAPI
AVISplitter_InputPin_Disconnect(IPin
* iface
)
1209 IPinImpl
*This
= (IPinImpl
*)iface
;
1213 EnterCriticalSection(This
->pCritSec
);
1215 if (This
->pConnectedTo
)
1219 hr
= IBaseFilter_GetState(This
->pinInfo
.pFilter
, 0, &state
);
1221 if (SUCCEEDED(hr
) && (state
== State_Stopped
))
1223 IPin_Release(This
->pConnectedTo
);
1224 This
->pConnectedTo
= NULL
;
1225 hr
= AVISplitter_RemoveOutputPins((AVISplitter
*)This
->pinInfo
.pFilter
);
1228 hr
= VFW_E_NOT_STOPPED
;
1233 LeaveCriticalSection(This
->pCritSec
);
1238 static const IPinVtbl AVISplitter_InputPin_Vtbl
=
1240 PullPin_QueryInterface
,
1244 PullPin_ReceiveConnection
,
1245 AVISplitter_InputPin_Disconnect
,
1246 IPinImpl_ConnectedTo
,
1247 IPinImpl_ConnectionMediaType
,
1248 IPinImpl_QueryPinInfo
,
1249 IPinImpl_QueryDirection
,
1251 IPinImpl_QueryAccept
,
1252 IPinImpl_EnumMediaTypes
,
1253 IPinImpl_QueryInternalConnections
,
1254 PullPin_EndOfStream
,