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 ICOM_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 ICOM_THIS(AVISplitter
, iface
);
215 return InterlockedIncrement(&This
->refCount
);
218 static ULONG WINAPI
AVISplitter_Release(IBaseFilter
* iface
)
220 ICOM_THIS(AVISplitter
, iface
);
222 if (!InterlockedDecrement(&This
->refCount
))
226 DeleteCriticalSection(&This
->csFilter
);
227 IReferenceClock_Release(This
->pClock
);
229 for (i
= 0; i
< This
->cStreams
+ 1; i
++)
230 IPin_Release(This
->ppPins
[i
]);
232 HeapFree(GetProcessHeap(), 0, This
->ppPins
);
235 TRACE("Destroying AVI splitter\n");
241 return This
->refCount
;
244 /** IPersist methods **/
246 static HRESULT WINAPI
AVISplitter_GetClassID(IBaseFilter
* iface
, CLSID
* pClsid
)
248 TRACE("(%p)\n", pClsid
);
250 *pClsid
= CLSID_AviSplitter
;
255 /** IMediaFilter methods **/
257 static HRESULT WINAPI
AVISplitter_Stop(IBaseFilter
* iface
)
260 ICOM_THIS(AVISplitter
, iface
);
264 EnterCriticalSection(&This
->csFilter
);
266 hr
= PullPin_StopProcessing(This
->pInputPin
);
268 This
->state
= State_Stopped
;
270 LeaveCriticalSection(&This
->csFilter
);
275 static HRESULT WINAPI
AVISplitter_Pause(IBaseFilter
* iface
)
279 ICOM_THIS(AVISplitter
, iface
);
283 EnterCriticalSection(&This
->csFilter
);
285 bInit
= (This
->state
== State_Stopped
);
286 This
->state
= State_Paused
;
288 LeaveCriticalSection(&This
->csFilter
);
294 hr
= PullPin_Seek(This
->pInputPin
, This
->CurrentChunkOffset
, This
->EndOfFile
);
297 hr
= PullPin_InitProcessing(This
->pInputPin
);
301 for (i
= 1; i
< This
->cStreams
+ 1; i
++)
303 OutputPin_DeliverNewSegment((OutputPin
*)This
->ppPins
[i
], 0, (LONGLONG
)ceil(10000000.0 * (float)((AVISplitter_OutputPin
*)This
->ppPins
[i
])->dwLength
/ ((AVISplitter_OutputPin
*)This
->ppPins
[i
])->fSamplesPerSec
), 1.0);
304 ((AVISplitter_OutputPin
*)This
->ppPins
[i
])->mediaSeeking
.llDuration
= (LONGLONG
)ceil(10000000.0 * (float)((AVISplitter_OutputPin
*)This
->ppPins
[i
])->dwLength
/ ((AVISplitter_OutputPin
*)This
->ppPins
[i
])->fSamplesPerSec
);
305 ((AVISplitter_OutputPin
*)This
->ppPins
[i
])->mediaSeeking
.llStop
= (LONGLONG
)ceil(10000000.0 * (float)((AVISplitter_OutputPin
*)This
->ppPins
[i
])->dwLength
/ ((AVISplitter_OutputPin
*)This
->ppPins
[i
])->fSamplesPerSec
);
306 OutputPin_CommitAllocator((OutputPin
*)This
->ppPins
[i
]);
309 /* FIXME: this is a little hacky: we have to deliver (at least?) one sample
310 * to each renderer before they will complete their transitions. We should probably
311 * seek through the stream for the first of each, rather than do it this way which is
312 * probably a bit prone to deadlocking */
313 hr
= PullPin_StartProcessing(This
->pInputPin
);
316 /* FIXME: else pause thread */
321 static HRESULT WINAPI
AVISplitter_Run(IBaseFilter
* iface
, REFERENCE_TIME tStart
)
324 ICOM_THIS(AVISplitter
, iface
);
326 TRACE("(%s)\n", wine_dbgstr_longlong(tStart
));
328 EnterCriticalSection(&This
->csFilter
);
330 This
->rtStreamStart
= tStart
;
332 This
->state
= State_Running
;
334 LeaveCriticalSection(&This
->csFilter
);
339 static HRESULT WINAPI
AVISplitter_GetState(IBaseFilter
* iface
, DWORD dwMilliSecsTimeout
, FILTER_STATE
*pState
)
341 ICOM_THIS(AVISplitter
, iface
);
343 TRACE("(%ld, %p)\n", dwMilliSecsTimeout
, pState
);
345 EnterCriticalSection(&This
->csFilter
);
347 *pState
= This
->state
;
349 LeaveCriticalSection(&This
->csFilter
);
351 /* FIXME: this is a little bit unsafe, but I don't see that we can do this
352 * while in the critical section. Maybe we could copy the pointer and addref in the
353 * critical section and then release after this.
355 if (This
->pInputPin
&& (PullPin_WaitForStateChange(This
->pInputPin
, dwMilliSecsTimeout
) == S_FALSE
))
356 return VFW_S_STATE_INTERMEDIATE
;
361 static HRESULT WINAPI
AVISplitter_SetSyncSource(IBaseFilter
* iface
, IReferenceClock
*pClock
)
363 ICOM_THIS(AVISplitter
, iface
);
365 TRACE("(%p)\n", pClock
);
367 EnterCriticalSection(&This
->csFilter
);
370 IReferenceClock_Release(This
->pClock
);
371 This
->pClock
= pClock
;
373 IReferenceClock_AddRef(This
->pClock
);
375 LeaveCriticalSection(&This
->csFilter
);
380 static HRESULT WINAPI
AVISplitter_GetSyncSource(IBaseFilter
* iface
, IReferenceClock
**ppClock
)
382 ICOM_THIS(AVISplitter
, iface
);
384 TRACE("(%p)\n", ppClock
);
386 EnterCriticalSection(&This
->csFilter
);
388 *ppClock
= This
->pClock
;
389 IReferenceClock_AddRef(This
->pClock
);
391 LeaveCriticalSection(&This
->csFilter
);
396 /** IBaseFilter implementation **/
398 static HRESULT WINAPI
AVISplitter_EnumPins(IBaseFilter
* iface
, IEnumPins
**ppEnum
)
401 ICOM_THIS(AVISplitter
, iface
);
403 TRACE("(%p)\n", ppEnum
);
405 epd
.cPins
= This
->cStreams
+ 1; /* +1 for input pin */
406 epd
.ppPins
= This
->ppPins
;
407 return IEnumPinsImpl_Construct(&epd
, ppEnum
);
410 static HRESULT WINAPI
AVISplitter_FindPin(IBaseFilter
* iface
, LPCWSTR Id
, IPin
**ppPin
)
412 FIXME("AVISplitter::FindPin(...)\n");
414 /* FIXME: critical section */
419 static HRESULT WINAPI
AVISplitter_QueryFilterInfo(IBaseFilter
* iface
, FILTER_INFO
*pInfo
)
421 ICOM_THIS(AVISplitter
, iface
);
423 TRACE("(%p)\n", pInfo
);
425 strcpyW(pInfo
->achName
, This
->filterInfo
.achName
);
426 pInfo
->pGraph
= This
->filterInfo
.pGraph
;
429 IFilterGraph_AddRef(pInfo
->pGraph
);
434 static HRESULT WINAPI
AVISplitter_JoinFilterGraph(IBaseFilter
* iface
, IFilterGraph
*pGraph
, LPCWSTR pName
)
437 ICOM_THIS(AVISplitter
, iface
);
439 TRACE("(%p, %s)\n", pGraph
, debugstr_w(pName
));
441 EnterCriticalSection(&This
->csFilter
);
444 strcpyW(This
->filterInfo
.achName
, pName
);
446 *This
->filterInfo
.achName
= '\0';
447 This
->filterInfo
.pGraph
= pGraph
; /* NOTE: do NOT increase ref. count */
449 LeaveCriticalSection(&This
->csFilter
);
454 static HRESULT WINAPI
AVISplitter_QueryVendorInfo(IBaseFilter
* iface
, LPWSTR
*pVendorInfo
)
456 TRACE("(%p)\n", pVendorInfo
);
460 static const IBaseFilterVtbl AVISplitter_Vtbl
=
462 ICOM_MSVTABLE_COMPAT_DummyRTTIVALUE
463 AVISplitter_QueryInterface
,
466 AVISplitter_GetClassID
,
470 AVISplitter_GetState
,
471 AVISplitter_SetSyncSource
,
472 AVISplitter_GetSyncSource
,
473 AVISplitter_EnumPins
,
475 AVISplitter_QueryFilterInfo
,
476 AVISplitter_JoinFilterGraph
,
477 AVISplitter_QueryVendorInfo
480 static HRESULT
AVISplitter_NextChunk(LONGLONG
* pllCurrentChunkOffset
, RIFFCHUNK
* pCurrentChunk
, const REFERENCE_TIME
* tStart
, const REFERENCE_TIME
* tStop
, const BYTE
* pbSrcStream
)
482 *pllCurrentChunkOffset
+= MEDIATIME_FROM_BYTES(sizeof(RIFFCHUNK
) + RIFFROUND(pCurrentChunk
->cb
));
484 if (*pllCurrentChunkOffset
> *tStop
)
485 return S_FALSE
; /* no more data - we couldn't even get the next chunk header! */
486 else if (*pllCurrentChunkOffset
+ MEDIATIME_FROM_BYTES(sizeof(RIFFCHUNK
)) >= *tStop
)
488 memcpy(pCurrentChunk
, pbSrcStream
+ (DWORD
)BYTES_FROM_MEDIATIME(*pllCurrentChunkOffset
- *tStart
), (DWORD
)BYTES_FROM_MEDIATIME(*tStop
- *pllCurrentChunkOffset
));
489 return S_FALSE
; /* no more data */
492 memcpy(pCurrentChunk
, pbSrcStream
+ (DWORD
)BYTES_FROM_MEDIATIME(*pllCurrentChunkOffset
- *tStart
), sizeof(RIFFCHUNK
));
497 static HRESULT
AVISplitter_Sample(LPVOID iface
, IMediaSample
* pSample
)
499 ICOM_THIS(AVISplitter
, iface
);
500 LPBYTE pbSrcStream
= NULL
;
501 long cbSrcStream
= 0;
502 REFERENCE_TIME tStart
, tStop
;
504 BOOL bMoreData
= TRUE
;
506 hr
= IMediaSample_GetPointer(pSample
, &pbSrcStream
);
508 hr
= IMediaSample_GetTime(pSample
, &tStart
, &tStop
);
510 cbSrcStream
= IMediaSample_GetActualDataLength(pSample
);
512 /* trace removed for performance reasons */
513 /* TRACE("(%p)\n", pSample); */
515 assert(BYTES_FROM_MEDIATIME(tStop
- tStart
) == cbSrcStream
);
517 if (This
->CurrentChunkOffset
<= tStart
&& This
->CurrentChunkOffset
+ MEDIATIME_FROM_BYTES(sizeof(RIFFCHUNK
)) > tStart
)
519 DWORD offset
= (DWORD
)BYTES_FROM_MEDIATIME(tStart
- This
->CurrentChunkOffset
);
520 assert(offset
<= sizeof(RIFFCHUNK
));
521 memcpy((BYTE
*)&This
->CurrentChunk
+ offset
, pbSrcStream
, sizeof(RIFFCHUNK
) - offset
);
523 else if (This
->CurrentChunkOffset
> tStart
)
525 DWORD offset
= (DWORD
)BYTES_FROM_MEDIATIME(This
->CurrentChunkOffset
- tStart
);
526 if (offset
>= (DWORD
)cbSrcStream
)
528 FIXME("large offset\n");
532 memcpy(&This
->CurrentChunk
, pbSrcStream
+ offset
, sizeof(RIFFCHUNK
));
535 assert(This
->CurrentChunkOffset
+ MEDIATIME_FROM_BYTES(sizeof(RIFFCHUNK
)) < tStop
);
541 long chunk_remaining_bytes
= 0;
544 AVISplitter_OutputPin
* pOutputPin
;
545 BOOL bSyncPoint
= TRUE
;
547 if (This
->CurrentChunkOffset
>= tStart
)
548 offset_src
= (long)BYTES_FROM_MEDIATIME(This
->CurrentChunkOffset
- tStart
) + sizeof(RIFFCHUNK
);
552 switch (TWOCCFromFOURCC(This
->CurrentChunk
.fcc
))
554 case cktypeDIBcompressed
:
558 /* FIXME: check that pin is of type video */
560 case cktypeWAVEbytes
:
561 /* FIXME: check that pin is of type audio */
563 case cktypePALchange
:
564 FIXME("handle palette change\n");
567 /* silently ignore */
568 if (S_FALSE
== AVISplitter_NextChunk(&This
->CurrentChunkOffset
, &This
->CurrentChunk
, &tStart
, &tStop
, pbSrcStream
))
572 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
));
573 if (S_FALSE
== AVISplitter_NextChunk(&This
->CurrentChunkOffset
, &This
->CurrentChunk
, &tStart
, &tStop
, pbSrcStream
))
578 streamId
= StreamFromFOURCC(This
->CurrentChunk
.fcc
);
580 if (streamId
> This
->cStreams
)
582 ERR("Corrupted AVI file (contains stream id %d, but supposed to only have %ld streams)\n", streamId
, This
->cStreams
);
586 pOutputPin
= (AVISplitter_OutputPin
*)This
->ppPins
[streamId
+ 1];
588 if (!This
->pCurrentSample
)
590 /* cache media sample until it is ready to be despatched
591 * (i.e. we reach the end of the chunk) */
592 hr
= OutputPin_GetDeliveryBuffer(&pOutputPin
->pin
, &This
->pCurrentSample
, NULL
, NULL
, 0);
596 hr
= IMediaSample_SetActualDataLength(This
->pCurrentSample
, 0);
601 TRACE("Skipping sending sample for stream %02d due to error (%lx)\n", streamId
, hr
);
602 This
->pCurrentSample
= NULL
;
603 if (S_FALSE
== AVISplitter_NextChunk(&This
->CurrentChunkOffset
, &This
->CurrentChunk
, &tStart
, &tStop
, pbSrcStream
))
609 hr
= IMediaSample_GetPointer(This
->pCurrentSample
, &pbDstStream
);
613 cbDstStream
= IMediaSample_GetSize(This
->pCurrentSample
);
615 chunk_remaining_bytes
= (long)BYTES_FROM_MEDIATIME(This
->CurrentChunkOffset
+ MEDIATIME_FROM_BYTES(This
->CurrentChunk
.cb
+ sizeof(RIFFCHUNK
)) - tStart
) - offset_src
;
617 assert(chunk_remaining_bytes
>= 0);
618 assert(chunk_remaining_bytes
<= cbDstStream
- IMediaSample_GetActualDataLength(This
->pCurrentSample
));
620 /* trace removed for performance reasons */
621 /* TRACE("chunk_remaining_bytes: 0x%lx, cbSrcStream: 0x%lx, offset_src: 0x%lx\n", chunk_remaining_bytes, cbSrcStream, offset_src); */
624 if (chunk_remaining_bytes
<= cbSrcStream
- offset_src
)
628 memcpy(pbDstStream
+ IMediaSample_GetActualDataLength(This
->pCurrentSample
), pbSrcStream
+ offset_src
, chunk_remaining_bytes
);
629 hr
= IMediaSample_SetActualDataLength(This
->pCurrentSample
, chunk_remaining_bytes
+ IMediaSample_GetActualDataLength(This
->pCurrentSample
));
635 REFERENCE_TIME tAviStart
, tAviStop
;
638 if (pOutputPin
->dwSamplesProcessed
== 0)
639 IMediaSample_SetDiscontinuity(This
->pCurrentSample
, TRUE
);
641 IMediaSample_SetSyncPoint(This
->pCurrentSample
, bSyncPoint
);
643 pOutputPin
->dwSamplesProcessed
++;
645 if (pOutputPin
->dwSampleSize
)
646 tAviStart
= (LONGLONG
)ceil(10000000.0 * (float)(pOutputPin
->dwSamplesProcessed
- 1) * (float)IMediaSample_GetActualDataLength(This
->pCurrentSample
) / ((float)pOutputPin
->dwSampleSize
* pOutputPin
->fSamplesPerSec
));
648 tAviStart
= (LONGLONG
)ceil(10000000.0 * (float)(pOutputPin
->dwSamplesProcessed
- 1) / (float)pOutputPin
->fSamplesPerSec
);
649 if (pOutputPin
->dwSampleSize
)
650 tAviStop
= (LONGLONG
)ceil(10000000.0 * (float)pOutputPin
->dwSamplesProcessed
* (float)IMediaSample_GetActualDataLength(This
->pCurrentSample
) / ((float)pOutputPin
->dwSampleSize
* pOutputPin
->fSamplesPerSec
));
652 tAviStop
= (LONGLONG
)ceil(10000000.0 * (float)pOutputPin
->dwSamplesProcessed
/ (float)pOutputPin
->fSamplesPerSec
);
654 IMediaSample_SetTime(This
->pCurrentSample
, &tAviStart
, &tAviStop
);
657 hr
= OutputPin_SendSample(&pOutputPin
->pin
, This
->pCurrentSample
);
658 if (hr
!= S_OK
&& hr
!= VFW_E_NOT_CONNECTED
)
659 ERR("Error sending sample (%lx)\n", hr
);
662 if (This
->pCurrentSample
)
663 IMediaSample_Release(This
->pCurrentSample
);
665 This
->pCurrentSample
= NULL
;
667 if (S_FALSE
== AVISplitter_NextChunk(&This
->CurrentChunkOffset
, &This
->CurrentChunk
, &tStart
, &tStop
, pbSrcStream
))
674 memcpy(pbDstStream
+ IMediaSample_GetActualDataLength(This
->pCurrentSample
), pbSrcStream
+ offset_src
, cbSrcStream
- offset_src
);
675 IMediaSample_SetActualDataLength(This
->pCurrentSample
, cbSrcStream
- offset_src
+ IMediaSample_GetActualDataLength(This
->pCurrentSample
));
683 static HRESULT
AVISplitter_QueryAccept(LPVOID iface
, const AM_MEDIA_TYPE
* pmt
)
685 if (IsEqualIID(&pmt
->majortype
, &MEDIATYPE_Stream
) && IsEqualIID(&pmt
->subtype
, &MEDIASUBTYPE_Avi
))
690 static HRESULT
AVISplitter_ProcessStreamList(AVISplitter
* This
, const BYTE
* pData
, DWORD cb
)
697 float fSamplesPerSec
= 0.0f
;
698 DWORD dwSampleSize
= 0;
700 ALLOCATOR_PROPERTIES props
;
701 static const WCHAR wszStreamTemplate
[] = {'S','t','r','e','a','m',' ','%','0','2','d',0};
705 props
.cbBuffer
= 0x20000;
708 ZeroMemory(&amt
, sizeof(amt
));
709 piOutput
.dir
= PINDIR_OUTPUT
;
710 piOutput
.pFilter
= (IBaseFilter
*)This
;
711 wsprintfW(piOutput
.achName
, wszStreamTemplate
, This
->cStreams
);
713 for (pChunk
= (RIFFCHUNK
*)pData
; ((BYTE
*)pChunk
>= pData
) && ((BYTE
*)pChunk
+ sizeof(RIFFCHUNK
) < pData
+ cb
) && (pChunk
->cb
> 0); pChunk
= (RIFFCHUNK
*)((BYTE
*)pChunk
+ sizeof(RIFFCHUNK
) + pChunk
->cb
))
717 case ckidSTREAMHEADER
:
719 const AVISTREAMHEADER
* pStrHdr
= (const AVISTREAMHEADER
*)pChunk
;
720 TRACE("processing stream header\n");
722 fSamplesPerSec
= (float)pStrHdr
->dwRate
/ (float)pStrHdr
->dwScale
;
724 switch (pStrHdr
->fccType
)
726 case streamtypeVIDEO
:
727 memcpy(&amt
.formattype
, &FORMAT_VideoInfo
, sizeof(GUID
));
731 case streamtypeAUDIO
:
732 memcpy(&amt
.formattype
, &FORMAT_WaveFormatEx
, sizeof(GUID
));
735 memcpy(&amt
.formattype
, &FORMAT_None
, sizeof(GUID
));
737 memcpy(&amt
.majortype
, &MEDIATYPE_Video
, sizeof(GUID
));
738 amt
.majortype
.Data1
= pStrHdr
->fccType
;
739 memcpy(&amt
.subtype
, &MEDIATYPE_Video
, sizeof(GUID
));
740 amt
.subtype
.Data1
= pStrHdr
->fccHandler
;
741 TRACE("Subtype FCC: %.04s\n", (LPSTR
)&pStrHdr
->fccHandler
);
742 amt
.lSampleSize
= pStrHdr
->dwSampleSize
;
743 amt
.bFixedSizeSamples
= (amt
.lSampleSize
!= 0);
745 /* FIXME: Is this right? */
746 if (!amt
.lSampleSize
)
752 amt
.bTemporalCompression
= IsEqualGUID(&amt
.majortype
, &MEDIATYPE_Video
); /* FIXME? */
753 dwSampleSize
= pStrHdr
->dwSampleSize
;
754 dwLength
= pStrHdr
->dwLength
;
756 dwLength
= This
->AviHeader
.dwTotalFrames
;
758 if (pStrHdr
->dwSuggestedBufferSize
)
759 props
.cbBuffer
= pStrHdr
->dwSuggestedBufferSize
;
763 case ckidSTREAMFORMAT
:
764 TRACE("processing stream format data\n");
765 if (IsEqualIID(&amt
.formattype
, &FORMAT_VideoInfo
))
767 VIDEOINFOHEADER
* pvi
;
768 /* biCompression member appears to override the value in the stream header.
769 * i.e. the stream header can say something completely contradictory to what
770 * is in the BITMAPINFOHEADER! */
771 if (pChunk
->cb
< sizeof(BITMAPINFOHEADER
))
773 ERR("Not enough bytes for BITMAPINFOHEADER\n");
776 amt
.cbFormat
= sizeof(VIDEOINFOHEADER
) - sizeof(BITMAPINFOHEADER
) + pChunk
->cb
;
777 amt
.pbFormat
= CoTaskMemAlloc(amt
.cbFormat
);
778 ZeroMemory(amt
.pbFormat
, amt
.cbFormat
);
779 pvi
= (VIDEOINFOHEADER
*)amt
.pbFormat
;
780 pvi
->AvgTimePerFrame
= (LONGLONG
)(10000000.0 / fSamplesPerSec
);
781 CopyMemory(&pvi
->bmiHeader
, (const BYTE
*)(pChunk
+ 1), pChunk
->cb
);
782 if (pvi
->bmiHeader
.biCompression
)
783 amt
.subtype
.Data1
= pvi
->bmiHeader
.biCompression
;
787 amt
.cbFormat
= pChunk
->cb
;
788 amt
.pbFormat
= CoTaskMemAlloc(amt
.cbFormat
);
789 CopyMemory(amt
.pbFormat
, (const BYTE
*)(pChunk
+ 1), amt
.cbFormat
);
793 TRACE("processing stream name\n");
794 /* FIXME: this doesn't exactly match native version (we omit the "##)" prefix), but hey... */
795 MultiByteToWideChar(CP_ACP
, 0, (LPCSTR
)(pChunk
+ 1), pChunk
->cb
, piOutput
.achName
, sizeof(piOutput
.achName
) / sizeof(piOutput
.achName
[0]));
797 case ckidSTREAMHANDLERDATA
:
798 FIXME("process stream handler data\n");
801 TRACE("JUNK chunk ignored\n");
804 FIXME("unknown chunk type \"%.04s\" ignored\n", (LPSTR
)&pChunk
->fcc
);
808 if (IsEqualGUID(&amt
.formattype
, &FORMAT_WaveFormatEx
))
810 memcpy(&amt
.subtype
, &MEDIATYPE_Video
, sizeof(GUID
));
811 amt
.subtype
.Data1
= ((WAVEFORMATEX
*)amt
.pbFormat
)->wFormatTag
;
814 dump_AM_MEDIA_TYPE(&amt
);
815 FIXME("fSamplesPerSec = %f\n", (double)fSamplesPerSec
);
816 FIXME("dwSampleSize = %lx\n", dwSampleSize
);
817 FIXME("dwLength = %lx\n", dwLength
);
819 ppOldPins
= This
->ppPins
;
821 This
->ppPins
= HeapAlloc(GetProcessHeap(), 0, (This
->cStreams
+ 2) * sizeof(IPin
*));
822 memcpy(This
->ppPins
, ppOldPins
, (This
->cStreams
+ 1) * sizeof(IPin
*));
824 hr
= AVISplitter_OutputPin_Construct(&piOutput
, &props
, NULL
, AVISplitter_OutputPin_QueryAccept
, &amt
, fSamplesPerSec
, &This
->csFilter
, This
->ppPins
+ This
->cStreams
+ 1);
828 ((AVISplitter_OutputPin
*)(This
->ppPins
[This
->cStreams
+ 1]))->dwSampleSize
= dwSampleSize
;
829 ((AVISplitter_OutputPin
*)(This
->ppPins
[This
->cStreams
+ 1]))->dwLength
= dwLength
;
830 ((AVISplitter_OutputPin
*)(This
->ppPins
[This
->cStreams
+ 1]))->pin
.pin
.pUserData
= (LPVOID
)This
->ppPins
[This
->cStreams
+ 1];
832 HeapFree(GetProcessHeap(), 0, ppOldPins
);
836 HeapFree(GetProcessHeap(), 0, This
->ppPins
);
837 This
->ppPins
= ppOldPins
;
838 ERR("Failed with error %lx\n", hr
);
844 static HRESULT
AVISplitter_RemoveOutputPins(AVISplitter
* This
)
846 /* NOTE: should be in critical section when calling this function */
849 IPin
** ppOldPins
= This
->ppPins
;
851 /* reduce the pin array down to 1 (just our input pin) */
852 This
->ppPins
= HeapAlloc(GetProcessHeap(), 0, sizeof(IPin
*) * 1);
853 memcpy(This
->ppPins
, ppOldPins
, sizeof(IPin
*) * 1);
855 for (i
= 0; i
< This
->cStreams
; i
++)
857 OutputPin_DeliverDisconnect((OutputPin
*)ppOldPins
[i
+ 1]);
858 IPin_Release(ppOldPins
[i
+ 1]);
862 HeapFree(GetProcessHeap(), 0, ppOldPins
);
867 /* FIXME: fix leaks on failure here */
868 static HRESULT
AVISplitter_InputPin_PreConnect(IPin
* iface
, IPin
* pConnectPin
)
870 ICOM_THIS(PullPin
, iface
);
873 LONGLONG pos
= 0; /* in bytes */
875 RIFFCHUNK
* pCurrentChunk
;
876 AVISplitter
* pAviSplit
= (AVISplitter
*)This
->pin
.pinInfo
.pFilter
;
878 hr
= IAsyncReader_SyncRead(This
->pReader
, pos
, sizeof(list
), (BYTE
*)&list
);
881 if (list
.fcc
!= ckidRIFF
)
883 ERR("Input stream not a RIFF file\n");
886 if (list
.cb
> 1 * 1024 * 1024 * 1024) /* cannot be more than 1Gb in size */
888 ERR("Input stream violates RIFF spec\n");
891 if (list
.fccListType
!= ckidAVI
)
893 ERR("Input stream not an AVI RIFF file\n");
897 hr
= IAsyncReader_SyncRead(This
->pReader
, pos
, sizeof(list
), (BYTE
*)&list
);
898 if (list
.fcc
!= ckidLIST
)
900 ERR("Expected LIST chunk, but got %.04s\n", (LPSTR
)&list
.fcc
);
903 if (list
.fccListType
!= ckidHEADERLIST
)
905 ERR("Header list expected. Got: %.04s\n", (LPSTR
)&list
.fccListType
);
909 pBuffer
= HeapAlloc(GetProcessHeap(), 0, list
.cb
- sizeof(RIFFLIST
) + sizeof(RIFFCHUNK
));
910 hr
= IAsyncReader_SyncRead(This
->pReader
, pos
+ sizeof(list
), list
.cb
- sizeof(RIFFLIST
) + sizeof(RIFFCHUNK
), pBuffer
);
912 pAviSplit
->AviHeader
.cb
= 0;
914 for (pCurrentChunk
= (RIFFCHUNK
*)pBuffer
; (BYTE
*)pCurrentChunk
+ sizeof(*pCurrentChunk
) < pBuffer
+ list
.cb
; pCurrentChunk
= (RIFFCHUNK
*)(((BYTE
*)pCurrentChunk
) + sizeof(*pCurrentChunk
) + pCurrentChunk
->cb
))
918 switch (pCurrentChunk
->fcc
)
920 case ckidMAINAVIHEADER
:
921 /* AVIMAINHEADER includes the structure that is pCurrentChunk at the moment */
922 memcpy(&pAviSplit
->AviHeader
, pCurrentChunk
, sizeof(pAviSplit
->AviHeader
));
925 pList
= (RIFFLIST
*)pCurrentChunk
;
926 switch (pList
->fccListType
)
929 hr
= AVISplitter_ProcessStreamList(pAviSplit
, (BYTE
*)pCurrentChunk
+ sizeof(RIFFLIST
), pCurrentChunk
->cb
+ sizeof(RIFFCHUNK
) - sizeof(RIFFLIST
));
932 FIXME("process ODML header\n");
940 FIXME("unrecognised header list type: %.04s\n", (LPSTR
)&pCurrentChunk
->fcc
);
943 HeapFree(GetProcessHeap(), 0, pBuffer
);
945 if (pAviSplit
->AviHeader
.cb
!= sizeof(pAviSplit
->AviHeader
) - sizeof(RIFFCHUNK
))
947 ERR("Avi Header wrong size!\n");
951 pos
+= sizeof(RIFFCHUNK
) + list
.cb
;
952 hr
= IAsyncReader_SyncRead(This
->pReader
, pos
, sizeof(list
), (BYTE
*)&list
);
954 if (list
.fcc
== ckidJUNK
)
956 pos
+= sizeof(RIFFCHUNK
) + list
.cb
;
957 hr
= IAsyncReader_SyncRead(This
->pReader
, pos
, sizeof(list
), (BYTE
*)&list
);
960 if (list
.fcc
!= ckidLIST
)
962 ERR("Expected LIST, but got %.04s\n", (LPSTR
)&list
.fcc
);
965 if (list
.fccListType
!= ckidAVIMOVIE
)
967 ERR("Expected AVI movie list, but got %.04s\n", (LPSTR
)&list
.fccListType
);
973 pAviSplit
->CurrentChunkOffset
= MEDIATIME_FROM_BYTES(pos
+ sizeof(RIFFLIST
));
974 pAviSplit
->EndOfFile
= MEDIATIME_FROM_BYTES(pos
+ list
.cb
+ sizeof(RIFFLIST
));
975 hr
= IAsyncReader_SyncRead(This
->pReader
, BYTES_FROM_MEDIATIME(pAviSplit
->CurrentChunkOffset
), sizeof(pAviSplit
->CurrentChunk
), (BYTE
*)&pAviSplit
->CurrentChunk
);
981 TRACE("AVI File ok\n");
986 static HRESULT
AVISplitter_ChangeStart(LPVOID iface
)
988 FIXME("(%p)\n", iface
);
992 static HRESULT
AVISplitter_ChangeStop(LPVOID iface
)
994 FIXME("(%p)\n", iface
);
998 static HRESULT
AVISplitter_ChangeRate(LPVOID iface
)
1000 FIXME("(%p)\n", iface
);
1005 static HRESULT WINAPI
AVISplitter_Seeking_QueryInterface(IMediaSeeking
* iface
, REFIID riid
, LPVOID
* ppv
)
1007 ICOM_THIS_From_IMediaSeeking(AVISplitter_OutputPin
, iface
);
1009 return IUnknown_QueryInterface((IUnknown
*)This
, riid
, ppv
);
1012 static ULONG WINAPI
AVISplitter_Seeking_AddRef(IMediaSeeking
* iface
)
1014 ICOM_THIS_From_IMediaSeeking(AVISplitter_OutputPin
, iface
);
1016 return IUnknown_AddRef((IUnknown
*)This
);
1019 static ULONG WINAPI
AVISplitter_Seeking_Release(IMediaSeeking
* iface
)
1021 ICOM_THIS_From_IMediaSeeking(AVISplitter_OutputPin
, iface
);
1023 return IUnknown_Release((IUnknown
*)This
);
1026 static const IMediaSeekingVtbl AVISplitter_Seeking_Vtbl
=
1028 ICOM_MSVTABLE_COMPAT_DummyRTTIVALUE
1029 AVISplitter_Seeking_QueryInterface
,
1030 AVISplitter_Seeking_AddRef
,
1031 AVISplitter_Seeking_Release
,
1032 MediaSeekingImpl_GetCapabilities
,
1033 MediaSeekingImpl_CheckCapabilities
,
1034 MediaSeekingImpl_IsFormatSupported
,
1035 MediaSeekingImpl_QueryPreferredFormat
,
1036 MediaSeekingImpl_GetTimeFormat
,
1037 MediaSeekingImpl_IsUsingTimeFormat
,
1038 MediaSeekingImpl_SetTimeFormat
,
1039 MediaSeekingImpl_GetDuration
,
1040 MediaSeekingImpl_GetStopPosition
,
1041 MediaSeekingImpl_GetCurrentPosition
,
1042 MediaSeekingImpl_ConvertTimeFormat
,
1043 MediaSeekingImpl_SetPositions
,
1044 MediaSeekingImpl_GetPositions
,
1045 MediaSeekingImpl_GetAvailable
,
1046 MediaSeekingImpl_SetRate
,
1047 MediaSeekingImpl_GetRate
,
1048 MediaSeekingImpl_GetPreroll
1051 HRESULT WINAPI
AVISplitter_OutputPin_QueryInterface(IPin
* iface
, REFIID riid
, LPVOID
* ppv
)
1053 ICOM_THIS(AVISplitter_OutputPin
, iface
);
1055 TRACE("(%s, %p)\n", qzdebugstr_guid(riid
), ppv
);
1059 if (IsEqualIID(riid
, &IID_IUnknown
))
1060 *ppv
= (LPVOID
)iface
;
1061 else if (IsEqualIID(riid
, &IID_IPin
))
1062 *ppv
= (LPVOID
)iface
;
1063 else if (IsEqualIID(riid
, &IID_IMediaSeeking
))
1064 *ppv
= (LPVOID
)&This
->mediaSeeking
;
1068 IUnknown_AddRef((IUnknown
*)(*ppv
));
1072 FIXME("No interface for %s!\n", qzdebugstr_guid(riid
));
1074 return E_NOINTERFACE
;
1077 static ULONG WINAPI
AVISplitter_OutputPin_Release(IPin
* iface
)
1079 ICOM_THIS(AVISplitter_OutputPin
, iface
);
1083 if (!InterlockedDecrement(&This
->pin
.pin
.refCount
))
1085 DeleteMediaType(This
->pmt
);
1086 CoTaskMemFree(This
->pmt
);
1087 DeleteMediaType(&This
->pin
.pin
.mtCurrent
);
1088 CoTaskMemFree(This
);
1091 return This
->pin
.pin
.refCount
;
1094 static HRESULT WINAPI
AVISplitter_OutputPin_EnumMediaTypes(IPin
* iface
, IEnumMediaTypes
** ppEnum
)
1096 ENUMMEDIADETAILS emd
;
1097 ICOM_THIS(AVISplitter_OutputPin
, iface
);
1099 TRACE("(%p)\n", ppEnum
);
1101 /* override this method to allow enumeration of your types */
1102 emd
.cMediaTypes
= 1;
1103 emd
.pMediaTypes
= This
->pmt
;
1105 return IEnumMediaTypesImpl_Construct(&emd
, ppEnum
);
1108 static HRESULT
AVISplitter_OutputPin_QueryAccept(LPVOID iface
, const AM_MEDIA_TYPE
* pmt
)
1110 ICOM_THIS(AVISplitter_OutputPin
, iface
);
1113 dump_AM_MEDIA_TYPE(pmt
);
1115 return (memcmp(This
->pmt
, pmt
, sizeof(AM_MEDIA_TYPE
)) == 0);
1118 static const IPinVtbl AVISplitter_OutputPin_Vtbl
=
1120 ICOM_MSVTABLE_COMPAT_DummyRTTIVALUE
1121 AVISplitter_OutputPin_QueryInterface
,
1123 AVISplitter_OutputPin_Release
,
1125 OutputPin_ReceiveConnection
,
1126 OutputPin_Disconnect
,
1127 IPinImpl_ConnectedTo
,
1128 IPinImpl_ConnectionMediaType
,
1129 IPinImpl_QueryPinInfo
,
1130 IPinImpl_QueryDirection
,
1132 IPinImpl_QueryAccept
,
1133 AVISplitter_OutputPin_EnumMediaTypes
,
1134 IPinImpl_QueryInternalConnections
,
1135 OutputPin_EndOfStream
,
1136 OutputPin_BeginFlush
,
1138 OutputPin_NewSegment
1141 static HRESULT
AVISplitter_InputPin_Construct(const PIN_INFO
* pPinInfo
, SAMPLEPROC pSampleProc
, LPVOID pUserData
, QUERYACCEPTPROC pQueryAccept
, LPCRITICAL_SECTION pCritSec
, IPin
** ppPin
)
1147 if (pPinInfo
->dir
!= PINDIR_INPUT
)
1149 ERR("Pin direction(%x) != PINDIR_INPUT\n", pPinInfo
->dir
);
1150 return E_INVALIDARG
;
1153 pPinImpl
= CoTaskMemAlloc(sizeof(*pPinImpl
));
1156 return E_OUTOFMEMORY
;
1158 if (SUCCEEDED(PullPin_Init(pPinInfo
, pSampleProc
, pUserData
, pQueryAccept
, pCritSec
, pPinImpl
)))
1160 pPinImpl
->pin
.lpVtbl
= &AVISplitter_InputPin_Vtbl
;
1162 *ppPin
= (IPin
*)(&pPinImpl
->pin
.lpVtbl
);
1168 static HRESULT WINAPI
AVISplitter_InputPin_Disconnect(IPin
* iface
)
1171 ICOM_THIS(IPinImpl
, iface
);
1175 EnterCriticalSection(This
->pCritSec
);
1177 if (This
->pConnectedTo
)
1181 hr
= IBaseFilter_GetState(This
->pinInfo
.pFilter
, 0, &state
);
1183 if (SUCCEEDED(hr
) && (state
== State_Stopped
))
1185 IPin_Release(This
->pConnectedTo
);
1186 This
->pConnectedTo
= NULL
;
1187 hr
= AVISplitter_RemoveOutputPins((AVISplitter
*)This
->pinInfo
.pFilter
);
1190 hr
= VFW_E_NOT_STOPPED
;
1195 LeaveCriticalSection(This
->pCritSec
);
1200 static const IPinVtbl AVISplitter_InputPin_Vtbl
=
1202 ICOM_MSVTABLE_COMPAT_DummyRTTIVALUE
1203 PullPin_QueryInterface
,
1207 PullPin_ReceiveConnection
,
1208 AVISplitter_InputPin_Disconnect
,
1209 IPinImpl_ConnectedTo
,
1210 IPinImpl_ConnectionMediaType
,
1211 IPinImpl_QueryPinInfo
,
1212 IPinImpl_QueryDirection
,
1214 IPinImpl_QueryAccept
,
1215 IPinImpl_EnumMediaTypes
,
1216 IPinImpl_QueryInternalConnections
,
1217 PullPin_EndOfStream
,