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
);
267 This
->state
= State_Stopped
;
269 LeaveCriticalSection(&This
->csFilter
);
274 static HRESULT WINAPI
AVISplitter_Pause(IBaseFilter
* iface
)
278 ICOM_THIS(AVISplitter
, iface
);
282 EnterCriticalSection(&This
->csFilter
);
284 bInit
= (This
->state
== State_Stopped
);
285 This
->state
= State_Paused
;
287 LeaveCriticalSection(&This
->csFilter
);
293 hr
= PullPin_Seek(This
->pInputPin
, This
->CurrentChunkOffset
, This
->EndOfFile
);
296 hr
= PullPin_InitProcessing(This
->pInputPin
);
300 for (i
= 1; i
< This
->cStreams
+ 1; i
++)
302 AVISplitter_OutputPin
* StreamPin
= (AVISplitter_OutputPin
*)This
->ppPins
[i
];
303 OutputPin_DeliverNewSegment((OutputPin
*)This
->ppPins
[i
], 0, (LONGLONG
)ceil(10000000.0 * (float)StreamPin
->dwLength
/ StreamPin
->fSamplesPerSec
), 1.0);
304 StreamPin
->mediaSeeking
.llDuration
= (LONGLONG
)ceil(10000000.0 * (float)StreamPin
->dwLength
/ StreamPin
->fSamplesPerSec
);
305 StreamPin
->mediaSeeking
.llStop
= (LONGLONG
)ceil(10000000.0 * (float)StreamPin
->dwLength
/ StreamPin
->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
;
331 This
->state
= State_Running
;
333 LeaveCriticalSection(&This
->csFilter
);
338 static HRESULT WINAPI
AVISplitter_GetState(IBaseFilter
* iface
, DWORD dwMilliSecsTimeout
, FILTER_STATE
*pState
)
340 ICOM_THIS(AVISplitter
, iface
);
342 TRACE("(%ld, %p)\n", dwMilliSecsTimeout
, pState
);
344 EnterCriticalSection(&This
->csFilter
);
346 *pState
= This
->state
;
348 LeaveCriticalSection(&This
->csFilter
);
350 /* FIXME: this is a little bit unsafe, but I don't see that we can do this
351 * while in the critical section. Maybe we could copy the pointer and addref in the
352 * critical section and then release after this.
354 if (This
->pInputPin
&& (PullPin_WaitForStateChange(This
->pInputPin
, dwMilliSecsTimeout
) == S_FALSE
))
355 return VFW_S_STATE_INTERMEDIATE
;
360 static HRESULT WINAPI
AVISplitter_SetSyncSource(IBaseFilter
* iface
, IReferenceClock
*pClock
)
362 ICOM_THIS(AVISplitter
, iface
);
364 TRACE("(%p)\n", pClock
);
366 EnterCriticalSection(&This
->csFilter
);
369 IReferenceClock_Release(This
->pClock
);
370 This
->pClock
= pClock
;
372 IReferenceClock_AddRef(This
->pClock
);
374 LeaveCriticalSection(&This
->csFilter
);
379 static HRESULT WINAPI
AVISplitter_GetSyncSource(IBaseFilter
* iface
, IReferenceClock
**ppClock
)
381 ICOM_THIS(AVISplitter
, iface
);
383 TRACE("(%p)\n", ppClock
);
385 EnterCriticalSection(&This
->csFilter
);
387 *ppClock
= This
->pClock
;
388 IReferenceClock_AddRef(This
->pClock
);
390 LeaveCriticalSection(&This
->csFilter
);
395 /** IBaseFilter implementation **/
397 static HRESULT WINAPI
AVISplitter_EnumPins(IBaseFilter
* iface
, IEnumPins
**ppEnum
)
400 ICOM_THIS(AVISplitter
, iface
);
402 TRACE("(%p)\n", ppEnum
);
404 epd
.cPins
= This
->cStreams
+ 1; /* +1 for input pin */
405 epd
.ppPins
= This
->ppPins
;
406 return IEnumPinsImpl_Construct(&epd
, ppEnum
);
409 static HRESULT WINAPI
AVISplitter_FindPin(IBaseFilter
* iface
, LPCWSTR Id
, IPin
**ppPin
)
411 FIXME("AVISplitter::FindPin(...)\n");
413 /* FIXME: critical section */
418 static HRESULT WINAPI
AVISplitter_QueryFilterInfo(IBaseFilter
* iface
, FILTER_INFO
*pInfo
)
420 ICOM_THIS(AVISplitter
, iface
);
422 TRACE("(%p)\n", pInfo
);
424 strcpyW(pInfo
->achName
, This
->filterInfo
.achName
);
425 pInfo
->pGraph
= This
->filterInfo
.pGraph
;
428 IFilterGraph_AddRef(pInfo
->pGraph
);
433 static HRESULT WINAPI
AVISplitter_JoinFilterGraph(IBaseFilter
* iface
, IFilterGraph
*pGraph
, LPCWSTR pName
)
436 ICOM_THIS(AVISplitter
, iface
);
438 TRACE("(%p, %s)\n", pGraph
, debugstr_w(pName
));
440 EnterCriticalSection(&This
->csFilter
);
443 strcpyW(This
->filterInfo
.achName
, pName
);
445 *This
->filterInfo
.achName
= '\0';
446 This
->filterInfo
.pGraph
= pGraph
; /* NOTE: do NOT increase ref. count */
448 LeaveCriticalSection(&This
->csFilter
);
453 static HRESULT WINAPI
AVISplitter_QueryVendorInfo(IBaseFilter
* iface
, LPWSTR
*pVendorInfo
)
455 TRACE("(%p)\n", pVendorInfo
);
459 static const IBaseFilterVtbl AVISplitter_Vtbl
=
461 ICOM_MSVTABLE_COMPAT_DummyRTTIVALUE
462 AVISplitter_QueryInterface
,
465 AVISplitter_GetClassID
,
469 AVISplitter_GetState
,
470 AVISplitter_SetSyncSource
,
471 AVISplitter_GetSyncSource
,
472 AVISplitter_EnumPins
,
474 AVISplitter_QueryFilterInfo
,
475 AVISplitter_JoinFilterGraph
,
476 AVISplitter_QueryVendorInfo
479 static HRESULT
AVISplitter_NextChunk(LONGLONG
* pllCurrentChunkOffset
, RIFFCHUNK
* pCurrentChunk
, const REFERENCE_TIME
* tStart
, const REFERENCE_TIME
* tStop
, const BYTE
* pbSrcStream
)
481 *pllCurrentChunkOffset
+= MEDIATIME_FROM_BYTES(sizeof(RIFFCHUNK
) + RIFFROUND(pCurrentChunk
->cb
));
483 if (*pllCurrentChunkOffset
> *tStop
)
484 return S_FALSE
; /* no more data - we couldn't even get the next chunk header! */
485 else if (*pllCurrentChunkOffset
+ MEDIATIME_FROM_BYTES(sizeof(RIFFCHUNK
)) >= *tStop
)
487 memcpy(pCurrentChunk
, pbSrcStream
+ (DWORD
)BYTES_FROM_MEDIATIME(*pllCurrentChunkOffset
- *tStart
), (DWORD
)BYTES_FROM_MEDIATIME(*tStop
- *pllCurrentChunkOffset
));
488 return S_FALSE
; /* no more data */
491 memcpy(pCurrentChunk
, pbSrcStream
+ (DWORD
)BYTES_FROM_MEDIATIME(*pllCurrentChunkOffset
- *tStart
), sizeof(RIFFCHUNK
));
496 static HRESULT
AVISplitter_Sample(LPVOID iface
, IMediaSample
* pSample
)
498 ICOM_THIS(AVISplitter
, iface
);
499 LPBYTE pbSrcStream
= NULL
;
500 long cbSrcStream
= 0;
501 REFERENCE_TIME tStart
, tStop
;
503 BOOL bMoreData
= TRUE
;
505 hr
= IMediaSample_GetPointer(pSample
, &pbSrcStream
);
507 hr
= IMediaSample_GetTime(pSample
, &tStart
, &tStop
);
509 cbSrcStream
= IMediaSample_GetActualDataLength(pSample
);
511 /* trace removed for performance reasons */
512 /* TRACE("(%p)\n", pSample); */
514 assert(BYTES_FROM_MEDIATIME(tStop
- tStart
) == cbSrcStream
);
516 if (This
->CurrentChunkOffset
<= tStart
&& This
->CurrentChunkOffset
+ MEDIATIME_FROM_BYTES(sizeof(RIFFCHUNK
)) > tStart
)
518 DWORD offset
= (DWORD
)BYTES_FROM_MEDIATIME(tStart
- This
->CurrentChunkOffset
);
519 assert(offset
<= sizeof(RIFFCHUNK
));
520 memcpy((BYTE
*)&This
->CurrentChunk
+ offset
, pbSrcStream
, sizeof(RIFFCHUNK
) - offset
);
522 else if (This
->CurrentChunkOffset
> tStart
)
524 DWORD offset
= (DWORD
)BYTES_FROM_MEDIATIME(This
->CurrentChunkOffset
- tStart
);
525 if (offset
>= (DWORD
)cbSrcStream
)
527 FIXME("large offset\n");
531 memcpy(&This
->CurrentChunk
, pbSrcStream
+ offset
, sizeof(RIFFCHUNK
));
534 assert(This
->CurrentChunkOffset
+ MEDIATIME_FROM_BYTES(sizeof(RIFFCHUNK
)) < tStop
);
540 long chunk_remaining_bytes
= 0;
543 AVISplitter_OutputPin
* pOutputPin
;
544 BOOL bSyncPoint
= TRUE
;
546 if (This
->CurrentChunkOffset
>= tStart
)
547 offset_src
= (long)BYTES_FROM_MEDIATIME(This
->CurrentChunkOffset
- tStart
) + sizeof(RIFFCHUNK
);
551 switch (This
->CurrentChunk
.fcc
)
554 case aviFCC('i','d','x','1'): /* Index is not handled */
555 /* silently ignore */
556 if (S_FALSE
== AVISplitter_NextChunk(&This
->CurrentChunkOffset
, &This
->CurrentChunk
, &tStart
, &tStop
, pbSrcStream
))
560 /* We only handle the 'rec ' list which contains the stream data */
561 if ((*(DWORD
*)(pbSrcStream
+ BYTES_FROM_MEDIATIME(This
->CurrentChunkOffset
-tStart
) + sizeof(RIFFCHUNK
))) == aviFCC('r','e','c',' '))
563 /* FIXME: We only advanced to the first chunk inside the list without keeping track that we are in it.
564 * This is not clean and the parser should be improved for that but it is enough for most AVI files. */
565 This
->CurrentChunkOffset
= MEDIATIME_FROM_BYTES(BYTES_FROM_MEDIATIME(This
->CurrentChunkOffset
) + sizeof(RIFFLIST
));
566 This
->CurrentChunk
= *(RIFFCHUNK
*) (pbSrcStream
+ BYTES_FROM_MEDIATIME(This
->CurrentChunkOffset
-tStart
));
569 else if (S_FALSE
== AVISplitter_NextChunk(&This
->CurrentChunkOffset
, &This
->CurrentChunk
, &tStart
, &tStop
, pbSrcStream
))
574 #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 */
575 switch (TWOCCFromFOURCC(This
->CurrentChunk
.fcc
))
577 case cktypeDIBcompressed
:
581 /* FIXME: check that pin is of type video */
583 case cktypeWAVEbytes
:
584 /* FIXME: check that pin is of type audio */
586 case cktypePALchange
:
587 FIXME("handle palette change\n");
590 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
));
591 if (S_FALSE
== AVISplitter_NextChunk(&This
->CurrentChunkOffset
, &This
->CurrentChunk
, &tStart
, &tStop
, pbSrcStream
))
598 streamId
= StreamFromFOURCC(This
->CurrentChunk
.fcc
);
600 if (streamId
> This
->cStreams
)
602 ERR("Corrupted AVI file (contains stream id %d, but supposed to only have %ld streams)\n", streamId
, This
->cStreams
);
606 pOutputPin
= (AVISplitter_OutputPin
*)This
->ppPins
[streamId
+ 1];
608 if (!This
->pCurrentSample
)
610 /* cache media sample until it is ready to be despatched
611 * (i.e. we reach the end of the chunk) */
612 hr
= OutputPin_GetDeliveryBuffer(&pOutputPin
->pin
, &This
->pCurrentSample
, NULL
, NULL
, 0);
616 hr
= IMediaSample_SetActualDataLength(This
->pCurrentSample
, 0);
621 TRACE("Skipping sending sample for stream %02d due to error (%lx)\n", streamId
, hr
);
622 This
->pCurrentSample
= NULL
;
623 if (S_FALSE
== AVISplitter_NextChunk(&This
->CurrentChunkOffset
, &This
->CurrentChunk
, &tStart
, &tStop
, pbSrcStream
))
629 hr
= IMediaSample_GetPointer(This
->pCurrentSample
, &pbDstStream
);
633 cbDstStream
= IMediaSample_GetSize(This
->pCurrentSample
);
635 chunk_remaining_bytes
= (long)BYTES_FROM_MEDIATIME(This
->CurrentChunkOffset
+ MEDIATIME_FROM_BYTES(This
->CurrentChunk
.cb
+ sizeof(RIFFCHUNK
)) - tStart
) - offset_src
;
637 assert(chunk_remaining_bytes
>= 0);
638 assert(chunk_remaining_bytes
<= cbDstStream
- IMediaSample_GetActualDataLength(This
->pCurrentSample
));
640 /* trace removed for performance reasons */
641 /* TRACE("chunk_remaining_bytes: 0x%lx, cbSrcStream: 0x%lx, offset_src: 0x%lx\n", chunk_remaining_bytes, cbSrcStream, offset_src); */
644 if (chunk_remaining_bytes
<= cbSrcStream
- offset_src
)
648 memcpy(pbDstStream
+ IMediaSample_GetActualDataLength(This
->pCurrentSample
), pbSrcStream
+ offset_src
, chunk_remaining_bytes
);
649 hr
= IMediaSample_SetActualDataLength(This
->pCurrentSample
, chunk_remaining_bytes
+ IMediaSample_GetActualDataLength(This
->pCurrentSample
));
655 REFERENCE_TIME tAviStart
, tAviStop
;
658 if (pOutputPin
->dwSamplesProcessed
== 0)
659 IMediaSample_SetDiscontinuity(This
->pCurrentSample
, TRUE
);
661 IMediaSample_SetSyncPoint(This
->pCurrentSample
, bSyncPoint
);
663 pOutputPin
->dwSamplesProcessed
++;
665 if (pOutputPin
->dwSampleSize
)
666 tAviStart
= (LONGLONG
)ceil(10000000.0 * (float)(pOutputPin
->dwSamplesProcessed
- 1) * (float)IMediaSample_GetActualDataLength(This
->pCurrentSample
) / ((float)pOutputPin
->dwSampleSize
* pOutputPin
->fSamplesPerSec
));
668 tAviStart
= (LONGLONG
)ceil(10000000.0 * (float)(pOutputPin
->dwSamplesProcessed
- 1) / (float)pOutputPin
->fSamplesPerSec
);
669 if (pOutputPin
->dwSampleSize
)
670 tAviStop
= (LONGLONG
)ceil(10000000.0 * (float)pOutputPin
->dwSamplesProcessed
* (float)IMediaSample_GetActualDataLength(This
->pCurrentSample
) / ((float)pOutputPin
->dwSampleSize
* pOutputPin
->fSamplesPerSec
));
672 tAviStop
= (LONGLONG
)ceil(10000000.0 * (float)pOutputPin
->dwSamplesProcessed
/ (float)pOutputPin
->fSamplesPerSec
);
674 IMediaSample_SetTime(This
->pCurrentSample
, &tAviStart
, &tAviStop
);
676 hr
= OutputPin_SendSample(&pOutputPin
->pin
, This
->pCurrentSample
);
677 if (hr
!= S_OK
&& hr
!= VFW_E_NOT_CONNECTED
)
678 ERR("Error sending sample (%lx)\n", hr
);
681 /* If we have a sample that has not been delivered, release it */
682 if (FAILED(hr
) && This
->pCurrentSample
)
683 IMediaSample_Release(This
->pCurrentSample
);
685 This
->pCurrentSample
= NULL
;
687 if (S_FALSE
== AVISplitter_NextChunk(&This
->CurrentChunkOffset
, &This
->CurrentChunk
, &tStart
, &tStop
, pbSrcStream
))
694 memcpy(pbDstStream
+ IMediaSample_GetActualDataLength(This
->pCurrentSample
), pbSrcStream
+ offset_src
, cbSrcStream
- offset_src
);
695 IMediaSample_SetActualDataLength(This
->pCurrentSample
, cbSrcStream
- offset_src
+ IMediaSample_GetActualDataLength(This
->pCurrentSample
));
703 static HRESULT
AVISplitter_QueryAccept(LPVOID iface
, const AM_MEDIA_TYPE
* pmt
)
705 if (IsEqualIID(&pmt
->majortype
, &MEDIATYPE_Stream
) && IsEqualIID(&pmt
->subtype
, &MEDIASUBTYPE_Avi
))
710 static HRESULT
AVISplitter_ProcessStreamList(AVISplitter
* This
, const BYTE
* pData
, DWORD cb
)
717 float fSamplesPerSec
= 0.0f
;
718 DWORD dwSampleSize
= 0;
720 ALLOCATOR_PROPERTIES props
;
721 static const WCHAR wszStreamTemplate
[] = {'S','t','r','e','a','m',' ','%','0','2','d',0};
725 props
.cbBuffer
= 0x20000;
728 ZeroMemory(&amt
, sizeof(amt
));
729 piOutput
.dir
= PINDIR_OUTPUT
;
730 piOutput
.pFilter
= (IBaseFilter
*)This
;
731 wsprintfW(piOutput
.achName
, wszStreamTemplate
, This
->cStreams
);
733 for (pChunk
= (RIFFCHUNK
*)pData
; ((BYTE
*)pChunk
>= pData
) && ((BYTE
*)pChunk
+ sizeof(RIFFCHUNK
) < pData
+ cb
) && (pChunk
->cb
> 0); pChunk
= (RIFFCHUNK
*)((BYTE
*)pChunk
+ sizeof(RIFFCHUNK
) + pChunk
->cb
))
737 case ckidSTREAMHEADER
:
739 const AVISTREAMHEADER
* pStrHdr
= (const AVISTREAMHEADER
*)pChunk
;
740 TRACE("processing stream header\n");
742 fSamplesPerSec
= (float)pStrHdr
->dwRate
/ (float)pStrHdr
->dwScale
;
744 switch (pStrHdr
->fccType
)
746 case streamtypeVIDEO
:
747 memcpy(&amt
.formattype
, &FORMAT_VideoInfo
, sizeof(GUID
));
751 case streamtypeAUDIO
:
752 memcpy(&amt
.formattype
, &FORMAT_WaveFormatEx
, sizeof(GUID
));
755 memcpy(&amt
.formattype
, &FORMAT_None
, sizeof(GUID
));
757 memcpy(&amt
.majortype
, &MEDIATYPE_Video
, sizeof(GUID
));
758 amt
.majortype
.Data1
= pStrHdr
->fccType
;
759 memcpy(&amt
.subtype
, &MEDIATYPE_Video
, sizeof(GUID
));
760 amt
.subtype
.Data1
= pStrHdr
->fccHandler
;
761 TRACE("Subtype FCC: %.04s\n", (LPSTR
)&pStrHdr
->fccHandler
);
762 amt
.lSampleSize
= pStrHdr
->dwSampleSize
;
763 amt
.bFixedSizeSamples
= (amt
.lSampleSize
!= 0);
765 /* FIXME: Is this right? */
766 if (!amt
.lSampleSize
)
772 amt
.bTemporalCompression
= IsEqualGUID(&amt
.majortype
, &MEDIATYPE_Video
); /* FIXME? */
773 dwSampleSize
= pStrHdr
->dwSampleSize
;
774 dwLength
= pStrHdr
->dwLength
;
776 dwLength
= This
->AviHeader
.dwTotalFrames
;
778 if (pStrHdr
->dwSuggestedBufferSize
)
779 props
.cbBuffer
= pStrHdr
->dwSuggestedBufferSize
;
783 case ckidSTREAMFORMAT
:
784 TRACE("processing stream format data\n");
785 if (IsEqualIID(&amt
.formattype
, &FORMAT_VideoInfo
))
787 VIDEOINFOHEADER
* pvi
;
788 /* biCompression member appears to override the value in the stream header.
789 * i.e. the stream header can say something completely contradictory to what
790 * is in the BITMAPINFOHEADER! */
791 if (pChunk
->cb
< sizeof(BITMAPINFOHEADER
))
793 ERR("Not enough bytes for BITMAPINFOHEADER\n");
796 amt
.cbFormat
= sizeof(VIDEOINFOHEADER
) - sizeof(BITMAPINFOHEADER
) + pChunk
->cb
;
797 amt
.pbFormat
= CoTaskMemAlloc(amt
.cbFormat
);
798 ZeroMemory(amt
.pbFormat
, amt
.cbFormat
);
799 pvi
= (VIDEOINFOHEADER
*)amt
.pbFormat
;
800 pvi
->AvgTimePerFrame
= (LONGLONG
)(10000000.0 / fSamplesPerSec
);
801 CopyMemory(&pvi
->bmiHeader
, (const BYTE
*)(pChunk
+ 1), pChunk
->cb
);
802 if (pvi
->bmiHeader
.biCompression
)
803 amt
.subtype
.Data1
= pvi
->bmiHeader
.biCompression
;
807 amt
.cbFormat
= pChunk
->cb
;
808 amt
.pbFormat
= CoTaskMemAlloc(amt
.cbFormat
);
809 CopyMemory(amt
.pbFormat
, (const BYTE
*)(pChunk
+ 1), amt
.cbFormat
);
813 TRACE("processing stream name\n");
814 /* FIXME: this doesn't exactly match native version (we omit the "##)" prefix), but hey... */
815 MultiByteToWideChar(CP_ACP
, 0, (LPCSTR
)(pChunk
+ 1), pChunk
->cb
, piOutput
.achName
, sizeof(piOutput
.achName
) / sizeof(piOutput
.achName
[0]));
817 case ckidSTREAMHANDLERDATA
:
818 FIXME("process stream handler data\n");
821 TRACE("JUNK chunk ignored\n");
824 FIXME("unknown chunk type \"%.04s\" ignored\n", (LPSTR
)&pChunk
->fcc
);
828 if (IsEqualGUID(&amt
.formattype
, &FORMAT_WaveFormatEx
))
830 memcpy(&amt
.subtype
, &MEDIATYPE_Video
, sizeof(GUID
));
831 amt
.subtype
.Data1
= ((WAVEFORMATEX
*)amt
.pbFormat
)->wFormatTag
;
834 dump_AM_MEDIA_TYPE(&amt
);
835 FIXME("fSamplesPerSec = %f\n", (double)fSamplesPerSec
);
836 FIXME("dwSampleSize = %lx\n", dwSampleSize
);
837 FIXME("dwLength = %lx\n", dwLength
);
839 ppOldPins
= This
->ppPins
;
841 This
->ppPins
= HeapAlloc(GetProcessHeap(), 0, (This
->cStreams
+ 2) * sizeof(IPin
*));
842 memcpy(This
->ppPins
, ppOldPins
, (This
->cStreams
+ 1) * sizeof(IPin
*));
844 hr
= AVISplitter_OutputPin_Construct(&piOutput
, &props
, NULL
, AVISplitter_OutputPin_QueryAccept
, &amt
, fSamplesPerSec
, &This
->csFilter
, This
->ppPins
+ This
->cStreams
+ 1);
848 ((AVISplitter_OutputPin
*)(This
->ppPins
[This
->cStreams
+ 1]))->dwSampleSize
= dwSampleSize
;
849 ((AVISplitter_OutputPin
*)(This
->ppPins
[This
->cStreams
+ 1]))->dwLength
= dwLength
;
850 ((AVISplitter_OutputPin
*)(This
->ppPins
[This
->cStreams
+ 1]))->pin
.pin
.pUserData
= (LPVOID
)This
->ppPins
[This
->cStreams
+ 1];
852 HeapFree(GetProcessHeap(), 0, ppOldPins
);
856 HeapFree(GetProcessHeap(), 0, This
->ppPins
);
857 This
->ppPins
= ppOldPins
;
858 ERR("Failed with error %lx\n", hr
);
864 static HRESULT
AVISplitter_RemoveOutputPins(AVISplitter
* This
)
866 /* NOTE: should be in critical section when calling this function */
869 IPin
** ppOldPins
= This
->ppPins
;
871 /* reduce the pin array down to 1 (just our input pin) */
872 This
->ppPins
= HeapAlloc(GetProcessHeap(), 0, sizeof(IPin
*) * 1);
873 memcpy(This
->ppPins
, ppOldPins
, sizeof(IPin
*) * 1);
875 for (i
= 0; i
< This
->cStreams
; i
++)
877 OutputPin_DeliverDisconnect((OutputPin
*)ppOldPins
[i
+ 1]);
878 IPin_Release(ppOldPins
[i
+ 1]);
882 HeapFree(GetProcessHeap(), 0, ppOldPins
);
887 /* FIXME: fix leaks on failure here */
888 static HRESULT
AVISplitter_InputPin_PreConnect(IPin
* iface
, IPin
* pConnectPin
)
890 ICOM_THIS(PullPin
, iface
);
893 LONGLONG pos
= 0; /* in bytes */
895 RIFFCHUNK
* pCurrentChunk
;
896 AVISplitter
* pAviSplit
= (AVISplitter
*)This
->pin
.pinInfo
.pFilter
;
898 hr
= IAsyncReader_SyncRead(This
->pReader
, pos
, sizeof(list
), (BYTE
*)&list
);
901 if (list
.fcc
!= ckidRIFF
)
903 ERR("Input stream not a RIFF file\n");
906 if (list
.cb
> 1 * 1024 * 1024 * 1024) /* cannot be more than 1Gb in size */
908 ERR("Input stream violates RIFF spec\n");
911 if (list
.fccListType
!= ckidAVI
)
913 ERR("Input stream not an AVI RIFF file\n");
917 hr
= IAsyncReader_SyncRead(This
->pReader
, pos
, sizeof(list
), (BYTE
*)&list
);
918 if (list
.fcc
!= ckidLIST
)
920 ERR("Expected LIST chunk, but got %.04s\n", (LPSTR
)&list
.fcc
);
923 if (list
.fccListType
!= ckidHEADERLIST
)
925 ERR("Header list expected. Got: %.04s\n", (LPSTR
)&list
.fccListType
);
929 pBuffer
= HeapAlloc(GetProcessHeap(), 0, list
.cb
- sizeof(RIFFLIST
) + sizeof(RIFFCHUNK
));
930 hr
= IAsyncReader_SyncRead(This
->pReader
, pos
+ sizeof(list
), list
.cb
- sizeof(RIFFLIST
) + sizeof(RIFFCHUNK
), pBuffer
);
932 pAviSplit
->AviHeader
.cb
= 0;
934 for (pCurrentChunk
= (RIFFCHUNK
*)pBuffer
; (BYTE
*)pCurrentChunk
+ sizeof(*pCurrentChunk
) < pBuffer
+ list
.cb
; pCurrentChunk
= (RIFFCHUNK
*)(((BYTE
*)pCurrentChunk
) + sizeof(*pCurrentChunk
) + pCurrentChunk
->cb
))
938 switch (pCurrentChunk
->fcc
)
940 case ckidMAINAVIHEADER
:
941 /* AVIMAINHEADER includes the structure that is pCurrentChunk at the moment */
942 memcpy(&pAviSplit
->AviHeader
, pCurrentChunk
, sizeof(pAviSplit
->AviHeader
));
945 pList
= (RIFFLIST
*)pCurrentChunk
;
946 switch (pList
->fccListType
)
949 hr
= AVISplitter_ProcessStreamList(pAviSplit
, (BYTE
*)pCurrentChunk
+ sizeof(RIFFLIST
), pCurrentChunk
->cb
+ sizeof(RIFFCHUNK
) - sizeof(RIFFLIST
));
952 FIXME("process ODML header\n");
960 FIXME("unrecognised header list type: %.04s\n", (LPSTR
)&pCurrentChunk
->fcc
);
963 HeapFree(GetProcessHeap(), 0, pBuffer
);
965 if (pAviSplit
->AviHeader
.cb
!= sizeof(pAviSplit
->AviHeader
) - sizeof(RIFFCHUNK
))
967 ERR("Avi Header wrong size!\n");
971 pos
+= sizeof(RIFFCHUNK
) + list
.cb
;
972 hr
= IAsyncReader_SyncRead(This
->pReader
, pos
, sizeof(list
), (BYTE
*)&list
);
974 if (list
.fcc
== ckidJUNK
)
976 pos
+= sizeof(RIFFCHUNK
) + list
.cb
;
977 hr
= IAsyncReader_SyncRead(This
->pReader
, pos
, sizeof(list
), (BYTE
*)&list
);
980 if (list
.fcc
!= ckidLIST
)
982 ERR("Expected LIST, but got %.04s\n", (LPSTR
)&list
.fcc
);
985 if (list
.fccListType
!= ckidAVIMOVIE
)
987 ERR("Expected AVI movie list, but got %.04s\n", (LPSTR
)&list
.fccListType
);
993 pAviSplit
->CurrentChunkOffset
= MEDIATIME_FROM_BYTES(pos
+ sizeof(RIFFLIST
));
994 pAviSplit
->EndOfFile
= MEDIATIME_FROM_BYTES(pos
+ list
.cb
+ sizeof(RIFFLIST
));
995 hr
= IAsyncReader_SyncRead(This
->pReader
, BYTES_FROM_MEDIATIME(pAviSplit
->CurrentChunkOffset
), sizeof(pAviSplit
->CurrentChunk
), (BYTE
*)&pAviSplit
->CurrentChunk
);
1001 TRACE("AVI File ok\n");
1006 static HRESULT
AVISplitter_ChangeStart(LPVOID iface
)
1008 FIXME("(%p)\n", iface
);
1012 static HRESULT
AVISplitter_ChangeStop(LPVOID iface
)
1014 FIXME("(%p)\n", iface
);
1018 static HRESULT
AVISplitter_ChangeRate(LPVOID iface
)
1020 FIXME("(%p)\n", iface
);
1025 static HRESULT WINAPI
AVISplitter_Seeking_QueryInterface(IMediaSeeking
* iface
, REFIID riid
, LPVOID
* ppv
)
1027 ICOM_THIS_From_IMediaSeeking(AVISplitter_OutputPin
, iface
);
1029 return IUnknown_QueryInterface((IUnknown
*)This
, riid
, ppv
);
1032 static ULONG WINAPI
AVISplitter_Seeking_AddRef(IMediaSeeking
* iface
)
1034 ICOM_THIS_From_IMediaSeeking(AVISplitter_OutputPin
, iface
);
1036 return IUnknown_AddRef((IUnknown
*)This
);
1039 static ULONG WINAPI
AVISplitter_Seeking_Release(IMediaSeeking
* iface
)
1041 ICOM_THIS_From_IMediaSeeking(AVISplitter_OutputPin
, iface
);
1043 return IUnknown_Release((IUnknown
*)This
);
1046 static const IMediaSeekingVtbl AVISplitter_Seeking_Vtbl
=
1048 ICOM_MSVTABLE_COMPAT_DummyRTTIVALUE
1049 AVISplitter_Seeking_QueryInterface
,
1050 AVISplitter_Seeking_AddRef
,
1051 AVISplitter_Seeking_Release
,
1052 MediaSeekingImpl_GetCapabilities
,
1053 MediaSeekingImpl_CheckCapabilities
,
1054 MediaSeekingImpl_IsFormatSupported
,
1055 MediaSeekingImpl_QueryPreferredFormat
,
1056 MediaSeekingImpl_GetTimeFormat
,
1057 MediaSeekingImpl_IsUsingTimeFormat
,
1058 MediaSeekingImpl_SetTimeFormat
,
1059 MediaSeekingImpl_GetDuration
,
1060 MediaSeekingImpl_GetStopPosition
,
1061 MediaSeekingImpl_GetCurrentPosition
,
1062 MediaSeekingImpl_ConvertTimeFormat
,
1063 MediaSeekingImpl_SetPositions
,
1064 MediaSeekingImpl_GetPositions
,
1065 MediaSeekingImpl_GetAvailable
,
1066 MediaSeekingImpl_SetRate
,
1067 MediaSeekingImpl_GetRate
,
1068 MediaSeekingImpl_GetPreroll
1071 HRESULT WINAPI
AVISplitter_OutputPin_QueryInterface(IPin
* iface
, REFIID riid
, LPVOID
* ppv
)
1073 ICOM_THIS(AVISplitter_OutputPin
, iface
);
1075 TRACE("(%s, %p)\n", qzdebugstr_guid(riid
), ppv
);
1079 if (IsEqualIID(riid
, &IID_IUnknown
))
1080 *ppv
= (LPVOID
)iface
;
1081 else if (IsEqualIID(riid
, &IID_IPin
))
1082 *ppv
= (LPVOID
)iface
;
1083 else if (IsEqualIID(riid
, &IID_IMediaSeeking
))
1084 *ppv
= (LPVOID
)&This
->mediaSeeking
;
1088 IUnknown_AddRef((IUnknown
*)(*ppv
));
1092 FIXME("No interface for %s!\n", qzdebugstr_guid(riid
));
1094 return E_NOINTERFACE
;
1097 static ULONG WINAPI
AVISplitter_OutputPin_Release(IPin
* iface
)
1099 ICOM_THIS(AVISplitter_OutputPin
, iface
);
1103 if (!InterlockedDecrement(&This
->pin
.pin
.refCount
))
1105 DeleteMediaType(This
->pmt
);
1106 CoTaskMemFree(This
->pmt
);
1107 DeleteMediaType(&This
->pin
.pin
.mtCurrent
);
1108 CoTaskMemFree(This
);
1111 return This
->pin
.pin
.refCount
;
1114 static HRESULT WINAPI
AVISplitter_OutputPin_EnumMediaTypes(IPin
* iface
, IEnumMediaTypes
** ppEnum
)
1116 ENUMMEDIADETAILS emd
;
1117 ICOM_THIS(AVISplitter_OutputPin
, iface
);
1119 TRACE("(%p)\n", ppEnum
);
1121 /* override this method to allow enumeration of your types */
1122 emd
.cMediaTypes
= 1;
1123 emd
.pMediaTypes
= This
->pmt
;
1125 return IEnumMediaTypesImpl_Construct(&emd
, ppEnum
);
1128 static HRESULT
AVISplitter_OutputPin_QueryAccept(LPVOID iface
, const AM_MEDIA_TYPE
* pmt
)
1130 ICOM_THIS(AVISplitter_OutputPin
, iface
);
1133 dump_AM_MEDIA_TYPE(pmt
);
1135 return (memcmp(This
->pmt
, pmt
, sizeof(AM_MEDIA_TYPE
)) == 0);
1138 static const IPinVtbl AVISplitter_OutputPin_Vtbl
=
1140 ICOM_MSVTABLE_COMPAT_DummyRTTIVALUE
1141 AVISplitter_OutputPin_QueryInterface
,
1143 AVISplitter_OutputPin_Release
,
1145 OutputPin_ReceiveConnection
,
1146 OutputPin_Disconnect
,
1147 IPinImpl_ConnectedTo
,
1148 IPinImpl_ConnectionMediaType
,
1149 IPinImpl_QueryPinInfo
,
1150 IPinImpl_QueryDirection
,
1152 IPinImpl_QueryAccept
,
1153 AVISplitter_OutputPin_EnumMediaTypes
,
1154 IPinImpl_QueryInternalConnections
,
1155 OutputPin_EndOfStream
,
1156 OutputPin_BeginFlush
,
1158 OutputPin_NewSegment
1161 static HRESULT
AVISplitter_InputPin_Construct(const PIN_INFO
* pPinInfo
, SAMPLEPROC pSampleProc
, LPVOID pUserData
, QUERYACCEPTPROC pQueryAccept
, LPCRITICAL_SECTION pCritSec
, IPin
** ppPin
)
1167 if (pPinInfo
->dir
!= PINDIR_INPUT
)
1169 ERR("Pin direction(%x) != PINDIR_INPUT\n", pPinInfo
->dir
);
1170 return E_INVALIDARG
;
1173 pPinImpl
= CoTaskMemAlloc(sizeof(*pPinImpl
));
1176 return E_OUTOFMEMORY
;
1178 if (SUCCEEDED(PullPin_Init(pPinInfo
, pSampleProc
, pUserData
, pQueryAccept
, pCritSec
, pPinImpl
)))
1180 pPinImpl
->pin
.lpVtbl
= &AVISplitter_InputPin_Vtbl
;
1182 *ppPin
= (IPin
*)(&pPinImpl
->pin
.lpVtbl
);
1188 static HRESULT WINAPI
AVISplitter_InputPin_Disconnect(IPin
* iface
)
1191 ICOM_THIS(IPinImpl
, iface
);
1195 EnterCriticalSection(This
->pCritSec
);
1197 if (This
->pConnectedTo
)
1201 hr
= IBaseFilter_GetState(This
->pinInfo
.pFilter
, 0, &state
);
1203 if (SUCCEEDED(hr
) && (state
== State_Stopped
))
1205 IPin_Release(This
->pConnectedTo
);
1206 This
->pConnectedTo
= NULL
;
1207 hr
= AVISplitter_RemoveOutputPins((AVISplitter
*)This
->pinInfo
.pFilter
);
1210 hr
= VFW_E_NOT_STOPPED
;
1215 LeaveCriticalSection(This
->pCritSec
);
1220 static const IPinVtbl AVISplitter_InputPin_Vtbl
=
1222 ICOM_MSVTABLE_COMPAT_DummyRTTIVALUE
1223 PullPin_QueryInterface
,
1227 PullPin_ReceiveConnection
,
1228 AVISplitter_InputPin_Disconnect
,
1229 IPinImpl_ConnectedTo
,
1230 IPinImpl_ConnectionMediaType
,
1231 IPinImpl_QueryPinInfo
,
1232 IPinImpl_QueryDirection
,
1234 IPinImpl_QueryAccept
,
1235 IPinImpl_EnumMediaTypes
,
1236 IPinImpl_QueryInternalConnections
,
1237 PullPin_EndOfStream
,