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 return InterlockedIncrement(&This
->refCount
);
218 static ULONG WINAPI
AVISplitter_Release(IBaseFilter
* iface
)
220 AVISplitter
*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 AVISplitter
*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 AVISplitter
*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 AVISplitter
*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 AVISplitter
*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 AVISplitter
*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 AVISplitter
*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 AVISplitter
*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 AVISplitter
*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 AVISplitter
*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 AVISplitter_QueryInterface
,
464 AVISplitter_GetClassID
,
468 AVISplitter_GetState
,
469 AVISplitter_SetSyncSource
,
470 AVISplitter_GetSyncSource
,
471 AVISplitter_EnumPins
,
473 AVISplitter_QueryFilterInfo
,
474 AVISplitter_JoinFilterGraph
,
475 AVISplitter_QueryVendorInfo
478 static HRESULT
AVISplitter_NextChunk(LONGLONG
* pllCurrentChunkOffset
, RIFFCHUNK
* pCurrentChunk
, const REFERENCE_TIME
* tStart
, const REFERENCE_TIME
* tStop
, const BYTE
* pbSrcStream
)
480 *pllCurrentChunkOffset
+= MEDIATIME_FROM_BYTES(sizeof(RIFFCHUNK
) + RIFFROUND(pCurrentChunk
->cb
));
482 if (*pllCurrentChunkOffset
> *tStop
)
483 return S_FALSE
; /* no more data - we couldn't even get the next chunk header! */
484 else if (*pllCurrentChunkOffset
+ MEDIATIME_FROM_BYTES(sizeof(RIFFCHUNK
)) >= *tStop
)
486 memcpy(pCurrentChunk
, pbSrcStream
+ (DWORD
)BYTES_FROM_MEDIATIME(*pllCurrentChunkOffset
- *tStart
), (DWORD
)BYTES_FROM_MEDIATIME(*tStop
- *pllCurrentChunkOffset
));
487 return S_FALSE
; /* no more data */
490 memcpy(pCurrentChunk
, pbSrcStream
+ (DWORD
)BYTES_FROM_MEDIATIME(*pllCurrentChunkOffset
- *tStart
), sizeof(RIFFCHUNK
));
495 static HRESULT
AVISplitter_Sample(LPVOID iface
, IMediaSample
* pSample
)
497 AVISplitter
*This
= (AVISplitter
*)iface
;
498 LPBYTE pbSrcStream
= NULL
;
499 long cbSrcStream
= 0;
500 REFERENCE_TIME tStart
, tStop
;
502 BOOL bMoreData
= TRUE
;
504 hr
= IMediaSample_GetPointer(pSample
, &pbSrcStream
);
506 hr
= IMediaSample_GetTime(pSample
, &tStart
, &tStop
);
508 cbSrcStream
= IMediaSample_GetActualDataLength(pSample
);
510 /* trace removed for performance reasons */
511 /* TRACE("(%p)\n", pSample); */
513 assert(BYTES_FROM_MEDIATIME(tStop
- tStart
) == cbSrcStream
);
515 if (This
->CurrentChunkOffset
<= tStart
&& This
->CurrentChunkOffset
+ MEDIATIME_FROM_BYTES(sizeof(RIFFCHUNK
)) > tStart
)
517 DWORD offset
= (DWORD
)BYTES_FROM_MEDIATIME(tStart
- This
->CurrentChunkOffset
);
518 assert(offset
<= sizeof(RIFFCHUNK
));
519 memcpy((BYTE
*)&This
->CurrentChunk
+ offset
, pbSrcStream
, sizeof(RIFFCHUNK
) - offset
);
521 else if (This
->CurrentChunkOffset
> tStart
)
523 DWORD offset
= (DWORD
)BYTES_FROM_MEDIATIME(This
->CurrentChunkOffset
- tStart
);
524 if (offset
>= (DWORD
)cbSrcStream
)
526 FIXME("large offset\n");
530 memcpy(&This
->CurrentChunk
, pbSrcStream
+ offset
, sizeof(RIFFCHUNK
));
533 assert(This
->CurrentChunkOffset
+ MEDIATIME_FROM_BYTES(sizeof(RIFFCHUNK
)) < tStop
);
539 long chunk_remaining_bytes
= 0;
542 AVISplitter_OutputPin
* pOutputPin
;
543 BOOL bSyncPoint
= TRUE
;
545 if (This
->CurrentChunkOffset
>= tStart
)
546 offset_src
= (long)BYTES_FROM_MEDIATIME(This
->CurrentChunkOffset
- tStart
) + sizeof(RIFFCHUNK
);
550 switch (This
->CurrentChunk
.fcc
)
553 case aviFCC('i','d','x','1'): /* Index is not handled */
554 /* silently ignore */
555 if (S_FALSE
== AVISplitter_NextChunk(&This
->CurrentChunkOffset
, &This
->CurrentChunk
, &tStart
, &tStop
, pbSrcStream
))
559 /* We only handle the 'rec ' list which contains the stream data */
560 if ((*(DWORD
*)(pbSrcStream
+ BYTES_FROM_MEDIATIME(This
->CurrentChunkOffset
-tStart
) + sizeof(RIFFCHUNK
))) == aviFCC('r','e','c',' '))
562 /* FIXME: We only advanced to the first chunk inside the list without keeping track that we are in it.
563 * This is not clean and the parser should be improved for that but it is enough for most AVI files. */
564 This
->CurrentChunkOffset
= MEDIATIME_FROM_BYTES(BYTES_FROM_MEDIATIME(This
->CurrentChunkOffset
) + sizeof(RIFFLIST
));
565 This
->CurrentChunk
= *(RIFFCHUNK
*) (pbSrcStream
+ BYTES_FROM_MEDIATIME(This
->CurrentChunkOffset
-tStart
));
568 else if (S_FALSE
== AVISplitter_NextChunk(&This
->CurrentChunkOffset
, &This
->CurrentChunk
, &tStart
, &tStop
, pbSrcStream
))
573 #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 */
574 switch (TWOCCFromFOURCC(This
->CurrentChunk
.fcc
))
576 case cktypeDIBcompressed
:
580 /* FIXME: check that pin is of type video */
582 case cktypeWAVEbytes
:
583 /* FIXME: check that pin is of type audio */
585 case cktypePALchange
:
586 FIXME("handle palette change\n");
589 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
));
590 if (S_FALSE
== AVISplitter_NextChunk(&This
->CurrentChunkOffset
, &This
->CurrentChunk
, &tStart
, &tStop
, pbSrcStream
))
597 streamId
= StreamFromFOURCC(This
->CurrentChunk
.fcc
);
599 if (streamId
> This
->cStreams
)
601 ERR("Corrupted AVI file (contains stream id %d, but supposed to only have %ld streams)\n", streamId
, This
->cStreams
);
605 pOutputPin
= (AVISplitter_OutputPin
*)This
->ppPins
[streamId
+ 1];
607 if (!This
->pCurrentSample
)
609 /* cache media sample until it is ready to be despatched
610 * (i.e. we reach the end of the chunk) */
611 hr
= OutputPin_GetDeliveryBuffer(&pOutputPin
->pin
, &This
->pCurrentSample
, NULL
, NULL
, 0);
615 hr
= IMediaSample_SetActualDataLength(This
->pCurrentSample
, 0);
620 TRACE("Skipping sending sample for stream %02d due to error (%lx)\n", streamId
, hr
);
621 This
->pCurrentSample
= NULL
;
622 if (S_FALSE
== AVISplitter_NextChunk(&This
->CurrentChunkOffset
, &This
->CurrentChunk
, &tStart
, &tStop
, pbSrcStream
))
628 hr
= IMediaSample_GetPointer(This
->pCurrentSample
, &pbDstStream
);
632 cbDstStream
= IMediaSample_GetSize(This
->pCurrentSample
);
634 chunk_remaining_bytes
= (long)BYTES_FROM_MEDIATIME(This
->CurrentChunkOffset
+ MEDIATIME_FROM_BYTES(This
->CurrentChunk
.cb
+ sizeof(RIFFCHUNK
)) - tStart
) - offset_src
;
636 assert(chunk_remaining_bytes
>= 0);
637 assert(chunk_remaining_bytes
<= cbDstStream
- IMediaSample_GetActualDataLength(This
->pCurrentSample
));
639 /* trace removed for performance reasons */
640 /* TRACE("chunk_remaining_bytes: 0x%lx, cbSrcStream: 0x%lx, offset_src: 0x%lx\n", chunk_remaining_bytes, cbSrcStream, offset_src); */
643 if (chunk_remaining_bytes
<= cbSrcStream
- offset_src
)
647 memcpy(pbDstStream
+ IMediaSample_GetActualDataLength(This
->pCurrentSample
), pbSrcStream
+ offset_src
, chunk_remaining_bytes
);
648 hr
= IMediaSample_SetActualDataLength(This
->pCurrentSample
, chunk_remaining_bytes
+ IMediaSample_GetActualDataLength(This
->pCurrentSample
));
654 REFERENCE_TIME tAviStart
, tAviStop
;
657 if (pOutputPin
->dwSamplesProcessed
== 0)
658 IMediaSample_SetDiscontinuity(This
->pCurrentSample
, TRUE
);
660 IMediaSample_SetSyncPoint(This
->pCurrentSample
, bSyncPoint
);
662 pOutputPin
->dwSamplesProcessed
++;
664 if (pOutputPin
->dwSampleSize
)
665 tAviStart
= (LONGLONG
)ceil(10000000.0 * (float)(pOutputPin
->dwSamplesProcessed
- 1) * (float)IMediaSample_GetActualDataLength(This
->pCurrentSample
) / ((float)pOutputPin
->dwSampleSize
* pOutputPin
->fSamplesPerSec
));
667 tAviStart
= (LONGLONG
)ceil(10000000.0 * (float)(pOutputPin
->dwSamplesProcessed
- 1) / (float)pOutputPin
->fSamplesPerSec
);
668 if (pOutputPin
->dwSampleSize
)
669 tAviStop
= (LONGLONG
)ceil(10000000.0 * (float)pOutputPin
->dwSamplesProcessed
* (float)IMediaSample_GetActualDataLength(This
->pCurrentSample
) / ((float)pOutputPin
->dwSampleSize
* pOutputPin
->fSamplesPerSec
));
671 tAviStop
= (LONGLONG
)ceil(10000000.0 * (float)pOutputPin
->dwSamplesProcessed
/ (float)pOutputPin
->fSamplesPerSec
);
673 IMediaSample_SetTime(This
->pCurrentSample
, &tAviStart
, &tAviStop
);
675 hr
= OutputPin_SendSample(&pOutputPin
->pin
, This
->pCurrentSample
);
676 if (hr
!= S_OK
&& hr
!= VFW_E_NOT_CONNECTED
)
677 ERR("Error sending sample (%lx)\n", hr
);
680 /* If we have a sample that has not been delivered, release it */
681 if (FAILED(hr
) && This
->pCurrentSample
)
682 IMediaSample_Release(This
->pCurrentSample
);
684 This
->pCurrentSample
= NULL
;
686 if (S_FALSE
== AVISplitter_NextChunk(&This
->CurrentChunkOffset
, &This
->CurrentChunk
, &tStart
, &tStop
, pbSrcStream
))
693 memcpy(pbDstStream
+ IMediaSample_GetActualDataLength(This
->pCurrentSample
), pbSrcStream
+ offset_src
, cbSrcStream
- offset_src
);
694 IMediaSample_SetActualDataLength(This
->pCurrentSample
, cbSrcStream
- offset_src
+ IMediaSample_GetActualDataLength(This
->pCurrentSample
));
702 static HRESULT
AVISplitter_QueryAccept(LPVOID iface
, const AM_MEDIA_TYPE
* pmt
)
704 if (IsEqualIID(&pmt
->majortype
, &MEDIATYPE_Stream
) && IsEqualIID(&pmt
->subtype
, &MEDIASUBTYPE_Avi
))
709 static HRESULT
AVISplitter_ProcessStreamList(AVISplitter
* This
, const BYTE
* pData
, DWORD cb
)
716 float fSamplesPerSec
= 0.0f
;
717 DWORD dwSampleSize
= 0;
719 ALLOCATOR_PROPERTIES props
;
720 static const WCHAR wszStreamTemplate
[] = {'S','t','r','e','a','m',' ','%','0','2','d',0};
724 props
.cbBuffer
= 0x20000;
727 ZeroMemory(&amt
, sizeof(amt
));
728 piOutput
.dir
= PINDIR_OUTPUT
;
729 piOutput
.pFilter
= (IBaseFilter
*)This
;
730 wsprintfW(piOutput
.achName
, wszStreamTemplate
, This
->cStreams
);
732 for (pChunk
= (RIFFCHUNK
*)pData
; ((BYTE
*)pChunk
>= pData
) && ((BYTE
*)pChunk
+ sizeof(RIFFCHUNK
) < pData
+ cb
) && (pChunk
->cb
> 0); pChunk
= (RIFFCHUNK
*)((BYTE
*)pChunk
+ sizeof(RIFFCHUNK
) + pChunk
->cb
))
736 case ckidSTREAMHEADER
:
738 const AVISTREAMHEADER
* pStrHdr
= (const AVISTREAMHEADER
*)pChunk
;
739 TRACE("processing stream header\n");
741 fSamplesPerSec
= (float)pStrHdr
->dwRate
/ (float)pStrHdr
->dwScale
;
743 switch (pStrHdr
->fccType
)
745 case streamtypeVIDEO
:
746 memcpy(&amt
.formattype
, &FORMAT_VideoInfo
, sizeof(GUID
));
750 case streamtypeAUDIO
:
751 memcpy(&amt
.formattype
, &FORMAT_WaveFormatEx
, sizeof(GUID
));
754 memcpy(&amt
.formattype
, &FORMAT_None
, sizeof(GUID
));
756 memcpy(&amt
.majortype
, &MEDIATYPE_Video
, sizeof(GUID
));
757 amt
.majortype
.Data1
= pStrHdr
->fccType
;
758 memcpy(&amt
.subtype
, &MEDIATYPE_Video
, sizeof(GUID
));
759 amt
.subtype
.Data1
= pStrHdr
->fccHandler
;
760 TRACE("Subtype FCC: %.04s\n", (LPSTR
)&pStrHdr
->fccHandler
);
761 amt
.lSampleSize
= pStrHdr
->dwSampleSize
;
762 amt
.bFixedSizeSamples
= (amt
.lSampleSize
!= 0);
764 /* FIXME: Is this right? */
765 if (!amt
.lSampleSize
)
771 amt
.bTemporalCompression
= IsEqualGUID(&amt
.majortype
, &MEDIATYPE_Video
); /* FIXME? */
772 dwSampleSize
= pStrHdr
->dwSampleSize
;
773 dwLength
= pStrHdr
->dwLength
;
775 dwLength
= This
->AviHeader
.dwTotalFrames
;
777 if (pStrHdr
->dwSuggestedBufferSize
)
778 props
.cbBuffer
= pStrHdr
->dwSuggestedBufferSize
;
782 case ckidSTREAMFORMAT
:
783 TRACE("processing stream format data\n");
784 if (IsEqualIID(&amt
.formattype
, &FORMAT_VideoInfo
))
786 VIDEOINFOHEADER
* pvi
;
787 /* biCompression member appears to override the value in the stream header.
788 * i.e. the stream header can say something completely contradictory to what
789 * is in the BITMAPINFOHEADER! */
790 if (pChunk
->cb
< sizeof(BITMAPINFOHEADER
))
792 ERR("Not enough bytes for BITMAPINFOHEADER\n");
795 amt
.cbFormat
= sizeof(VIDEOINFOHEADER
) - sizeof(BITMAPINFOHEADER
) + pChunk
->cb
;
796 amt
.pbFormat
= CoTaskMemAlloc(amt
.cbFormat
);
797 ZeroMemory(amt
.pbFormat
, amt
.cbFormat
);
798 pvi
= (VIDEOINFOHEADER
*)amt
.pbFormat
;
799 pvi
->AvgTimePerFrame
= (LONGLONG
)(10000000.0 / fSamplesPerSec
);
800 CopyMemory(&pvi
->bmiHeader
, (const BYTE
*)(pChunk
+ 1), pChunk
->cb
);
801 if (pvi
->bmiHeader
.biCompression
)
802 amt
.subtype
.Data1
= pvi
->bmiHeader
.biCompression
;
806 amt
.cbFormat
= pChunk
->cb
;
807 amt
.pbFormat
= CoTaskMemAlloc(amt
.cbFormat
);
808 CopyMemory(amt
.pbFormat
, (const BYTE
*)(pChunk
+ 1), amt
.cbFormat
);
812 TRACE("processing stream name\n");
813 /* FIXME: this doesn't exactly match native version (we omit the "##)" prefix), but hey... */
814 MultiByteToWideChar(CP_ACP
, 0, (LPCSTR
)(pChunk
+ 1), pChunk
->cb
, piOutput
.achName
, sizeof(piOutput
.achName
) / sizeof(piOutput
.achName
[0]));
816 case ckidSTREAMHANDLERDATA
:
817 FIXME("process stream handler data\n");
820 TRACE("JUNK chunk ignored\n");
823 FIXME("unknown chunk type \"%.04s\" ignored\n", (LPSTR
)&pChunk
->fcc
);
827 if (IsEqualGUID(&amt
.formattype
, &FORMAT_WaveFormatEx
))
829 memcpy(&amt
.subtype
, &MEDIATYPE_Video
, sizeof(GUID
));
830 amt
.subtype
.Data1
= ((WAVEFORMATEX
*)amt
.pbFormat
)->wFormatTag
;
833 dump_AM_MEDIA_TYPE(&amt
);
834 FIXME("fSamplesPerSec = %f\n", (double)fSamplesPerSec
);
835 FIXME("dwSampleSize = %lx\n", dwSampleSize
);
836 FIXME("dwLength = %lx\n", dwLength
);
838 ppOldPins
= This
->ppPins
;
840 This
->ppPins
= HeapAlloc(GetProcessHeap(), 0, (This
->cStreams
+ 2) * sizeof(IPin
*));
841 memcpy(This
->ppPins
, ppOldPins
, (This
->cStreams
+ 1) * sizeof(IPin
*));
843 hr
= AVISplitter_OutputPin_Construct(&piOutput
, &props
, NULL
, AVISplitter_OutputPin_QueryAccept
, &amt
, fSamplesPerSec
, &This
->csFilter
, This
->ppPins
+ This
->cStreams
+ 1);
847 ((AVISplitter_OutputPin
*)(This
->ppPins
[This
->cStreams
+ 1]))->dwSampleSize
= dwSampleSize
;
848 ((AVISplitter_OutputPin
*)(This
->ppPins
[This
->cStreams
+ 1]))->dwLength
= dwLength
;
849 ((AVISplitter_OutputPin
*)(This
->ppPins
[This
->cStreams
+ 1]))->pin
.pin
.pUserData
= (LPVOID
)This
->ppPins
[This
->cStreams
+ 1];
851 HeapFree(GetProcessHeap(), 0, ppOldPins
);
855 HeapFree(GetProcessHeap(), 0, This
->ppPins
);
856 This
->ppPins
= ppOldPins
;
857 ERR("Failed with error %lx\n", hr
);
863 static HRESULT
AVISplitter_RemoveOutputPins(AVISplitter
* This
)
865 /* NOTE: should be in critical section when calling this function */
868 IPin
** ppOldPins
= This
->ppPins
;
870 /* reduce the pin array down to 1 (just our input pin) */
871 This
->ppPins
= HeapAlloc(GetProcessHeap(), 0, sizeof(IPin
*) * 1);
872 memcpy(This
->ppPins
, ppOldPins
, sizeof(IPin
*) * 1);
874 for (i
= 0; i
< This
->cStreams
; i
++)
876 OutputPin_DeliverDisconnect((OutputPin
*)ppOldPins
[i
+ 1]);
877 IPin_Release(ppOldPins
[i
+ 1]);
881 HeapFree(GetProcessHeap(), 0, ppOldPins
);
886 /* FIXME: fix leaks on failure here */
887 static HRESULT
AVISplitter_InputPin_PreConnect(IPin
* iface
, IPin
* pConnectPin
)
889 PullPin
*This
= (PullPin
*)iface
;
892 LONGLONG pos
= 0; /* in bytes */
894 RIFFCHUNK
* pCurrentChunk
;
895 AVISplitter
* pAviSplit
= (AVISplitter
*)This
->pin
.pinInfo
.pFilter
;
897 hr
= IAsyncReader_SyncRead(This
->pReader
, pos
, sizeof(list
), (BYTE
*)&list
);
900 if (list
.fcc
!= ckidRIFF
)
902 ERR("Input stream not a RIFF file\n");
905 if (list
.cb
> 1 * 1024 * 1024 * 1024) /* cannot be more than 1Gb in size */
907 ERR("Input stream violates RIFF spec\n");
910 if (list
.fccListType
!= ckidAVI
)
912 ERR("Input stream not an AVI RIFF file\n");
916 hr
= IAsyncReader_SyncRead(This
->pReader
, pos
, sizeof(list
), (BYTE
*)&list
);
917 if (list
.fcc
!= ckidLIST
)
919 ERR("Expected LIST chunk, but got %.04s\n", (LPSTR
)&list
.fcc
);
922 if (list
.fccListType
!= ckidHEADERLIST
)
924 ERR("Header list expected. Got: %.04s\n", (LPSTR
)&list
.fccListType
);
928 pBuffer
= HeapAlloc(GetProcessHeap(), 0, list
.cb
- sizeof(RIFFLIST
) + sizeof(RIFFCHUNK
));
929 hr
= IAsyncReader_SyncRead(This
->pReader
, pos
+ sizeof(list
), list
.cb
- sizeof(RIFFLIST
) + sizeof(RIFFCHUNK
), pBuffer
);
931 pAviSplit
->AviHeader
.cb
= 0;
933 for (pCurrentChunk
= (RIFFCHUNK
*)pBuffer
; (BYTE
*)pCurrentChunk
+ sizeof(*pCurrentChunk
) < pBuffer
+ list
.cb
; pCurrentChunk
= (RIFFCHUNK
*)(((BYTE
*)pCurrentChunk
) + sizeof(*pCurrentChunk
) + pCurrentChunk
->cb
))
937 switch (pCurrentChunk
->fcc
)
939 case ckidMAINAVIHEADER
:
940 /* AVIMAINHEADER includes the structure that is pCurrentChunk at the moment */
941 memcpy(&pAviSplit
->AviHeader
, pCurrentChunk
, sizeof(pAviSplit
->AviHeader
));
944 pList
= (RIFFLIST
*)pCurrentChunk
;
945 switch (pList
->fccListType
)
948 hr
= AVISplitter_ProcessStreamList(pAviSplit
, (BYTE
*)pCurrentChunk
+ sizeof(RIFFLIST
), pCurrentChunk
->cb
+ sizeof(RIFFCHUNK
) - sizeof(RIFFLIST
));
951 FIXME("process ODML header\n");
959 FIXME("unrecognised header list type: %.04s\n", (LPSTR
)&pCurrentChunk
->fcc
);
962 HeapFree(GetProcessHeap(), 0, pBuffer
);
964 if (pAviSplit
->AviHeader
.cb
!= sizeof(pAviSplit
->AviHeader
) - sizeof(RIFFCHUNK
))
966 ERR("Avi Header wrong size!\n");
970 pos
+= sizeof(RIFFCHUNK
) + list
.cb
;
971 hr
= IAsyncReader_SyncRead(This
->pReader
, pos
, sizeof(list
), (BYTE
*)&list
);
973 if (list
.fcc
== ckidJUNK
)
975 pos
+= sizeof(RIFFCHUNK
) + list
.cb
;
976 hr
= IAsyncReader_SyncRead(This
->pReader
, pos
, sizeof(list
), (BYTE
*)&list
);
979 if (list
.fcc
!= ckidLIST
)
981 ERR("Expected LIST, but got %.04s\n", (LPSTR
)&list
.fcc
);
984 if (list
.fccListType
!= ckidAVIMOVIE
)
986 ERR("Expected AVI movie list, but got %.04s\n", (LPSTR
)&list
.fccListType
);
992 pAviSplit
->CurrentChunkOffset
= MEDIATIME_FROM_BYTES(pos
+ sizeof(RIFFLIST
));
993 pAviSplit
->EndOfFile
= MEDIATIME_FROM_BYTES(pos
+ list
.cb
+ sizeof(RIFFLIST
));
994 hr
= IAsyncReader_SyncRead(This
->pReader
, BYTES_FROM_MEDIATIME(pAviSplit
->CurrentChunkOffset
), sizeof(pAviSplit
->CurrentChunk
), (BYTE
*)&pAviSplit
->CurrentChunk
);
1000 TRACE("AVI File ok\n");
1005 static HRESULT
AVISplitter_ChangeStart(LPVOID iface
)
1007 FIXME("(%p)\n", iface
);
1011 static HRESULT
AVISplitter_ChangeStop(LPVOID iface
)
1013 FIXME("(%p)\n", iface
);
1017 static HRESULT
AVISplitter_ChangeRate(LPVOID iface
)
1019 FIXME("(%p)\n", iface
);
1024 static HRESULT WINAPI
AVISplitter_Seeking_QueryInterface(IMediaSeeking
* iface
, REFIID riid
, LPVOID
* ppv
)
1026 ICOM_THIS_From_IMediaSeeking(AVISplitter_OutputPin
, iface
);
1028 return IUnknown_QueryInterface((IUnknown
*)This
, riid
, ppv
);
1031 static ULONG WINAPI
AVISplitter_Seeking_AddRef(IMediaSeeking
* iface
)
1033 ICOM_THIS_From_IMediaSeeking(AVISplitter_OutputPin
, iface
);
1035 return IUnknown_AddRef((IUnknown
*)This
);
1038 static ULONG WINAPI
AVISplitter_Seeking_Release(IMediaSeeking
* iface
)
1040 ICOM_THIS_From_IMediaSeeking(AVISplitter_OutputPin
, iface
);
1042 return IUnknown_Release((IUnknown
*)This
);
1045 static const IMediaSeekingVtbl AVISplitter_Seeking_Vtbl
=
1047 AVISplitter_Seeking_QueryInterface
,
1048 AVISplitter_Seeking_AddRef
,
1049 AVISplitter_Seeking_Release
,
1050 MediaSeekingImpl_GetCapabilities
,
1051 MediaSeekingImpl_CheckCapabilities
,
1052 MediaSeekingImpl_IsFormatSupported
,
1053 MediaSeekingImpl_QueryPreferredFormat
,
1054 MediaSeekingImpl_GetTimeFormat
,
1055 MediaSeekingImpl_IsUsingTimeFormat
,
1056 MediaSeekingImpl_SetTimeFormat
,
1057 MediaSeekingImpl_GetDuration
,
1058 MediaSeekingImpl_GetStopPosition
,
1059 MediaSeekingImpl_GetCurrentPosition
,
1060 MediaSeekingImpl_ConvertTimeFormat
,
1061 MediaSeekingImpl_SetPositions
,
1062 MediaSeekingImpl_GetPositions
,
1063 MediaSeekingImpl_GetAvailable
,
1064 MediaSeekingImpl_SetRate
,
1065 MediaSeekingImpl_GetRate
,
1066 MediaSeekingImpl_GetPreroll
1069 HRESULT WINAPI
AVISplitter_OutputPin_QueryInterface(IPin
* iface
, REFIID riid
, LPVOID
* ppv
)
1071 AVISplitter_OutputPin
*This
= (AVISplitter_OutputPin
*)iface
;
1073 TRACE("(%s, %p)\n", qzdebugstr_guid(riid
), ppv
);
1077 if (IsEqualIID(riid
, &IID_IUnknown
))
1078 *ppv
= (LPVOID
)iface
;
1079 else if (IsEqualIID(riid
, &IID_IPin
))
1080 *ppv
= (LPVOID
)iface
;
1081 else if (IsEqualIID(riid
, &IID_IMediaSeeking
))
1082 *ppv
= (LPVOID
)&This
->mediaSeeking
;
1086 IUnknown_AddRef((IUnknown
*)(*ppv
));
1090 FIXME("No interface for %s!\n", qzdebugstr_guid(riid
));
1092 return E_NOINTERFACE
;
1095 static ULONG WINAPI
AVISplitter_OutputPin_Release(IPin
* iface
)
1097 AVISplitter_OutputPin
*This
= (AVISplitter_OutputPin
*)iface
;
1101 if (!InterlockedDecrement(&This
->pin
.pin
.refCount
))
1103 DeleteMediaType(This
->pmt
);
1104 CoTaskMemFree(This
->pmt
);
1105 DeleteMediaType(&This
->pin
.pin
.mtCurrent
);
1106 CoTaskMemFree(This
);
1109 return This
->pin
.pin
.refCount
;
1112 static HRESULT WINAPI
AVISplitter_OutputPin_EnumMediaTypes(IPin
* iface
, IEnumMediaTypes
** ppEnum
)
1114 ENUMMEDIADETAILS emd
;
1115 AVISplitter_OutputPin
*This
= (AVISplitter_OutputPin
*)iface
;
1117 TRACE("(%p)\n", ppEnum
);
1119 /* override this method to allow enumeration of your types */
1120 emd
.cMediaTypes
= 1;
1121 emd
.pMediaTypes
= This
->pmt
;
1123 return IEnumMediaTypesImpl_Construct(&emd
, ppEnum
);
1126 static HRESULT
AVISplitter_OutputPin_QueryAccept(LPVOID iface
, const AM_MEDIA_TYPE
* pmt
)
1128 AVISplitter_OutputPin
*This
= (AVISplitter_OutputPin
*)iface
;
1131 dump_AM_MEDIA_TYPE(pmt
);
1133 return (memcmp(This
->pmt
, pmt
, sizeof(AM_MEDIA_TYPE
)) == 0);
1136 static const IPinVtbl AVISplitter_OutputPin_Vtbl
=
1138 AVISplitter_OutputPin_QueryInterface
,
1140 AVISplitter_OutputPin_Release
,
1142 OutputPin_ReceiveConnection
,
1143 OutputPin_Disconnect
,
1144 IPinImpl_ConnectedTo
,
1145 IPinImpl_ConnectionMediaType
,
1146 IPinImpl_QueryPinInfo
,
1147 IPinImpl_QueryDirection
,
1149 IPinImpl_QueryAccept
,
1150 AVISplitter_OutputPin_EnumMediaTypes
,
1151 IPinImpl_QueryInternalConnections
,
1152 OutputPin_EndOfStream
,
1153 OutputPin_BeginFlush
,
1155 OutputPin_NewSegment
1158 static HRESULT
AVISplitter_InputPin_Construct(const PIN_INFO
* pPinInfo
, SAMPLEPROC pSampleProc
, LPVOID pUserData
, QUERYACCEPTPROC pQueryAccept
, LPCRITICAL_SECTION pCritSec
, IPin
** ppPin
)
1164 if (pPinInfo
->dir
!= PINDIR_INPUT
)
1166 ERR("Pin direction(%x) != PINDIR_INPUT\n", pPinInfo
->dir
);
1167 return E_INVALIDARG
;
1170 pPinImpl
= CoTaskMemAlloc(sizeof(*pPinImpl
));
1173 return E_OUTOFMEMORY
;
1175 if (SUCCEEDED(PullPin_Init(pPinInfo
, pSampleProc
, pUserData
, pQueryAccept
, pCritSec
, pPinImpl
)))
1177 pPinImpl
->pin
.lpVtbl
= &AVISplitter_InputPin_Vtbl
;
1179 *ppPin
= (IPin
*)(&pPinImpl
->pin
.lpVtbl
);
1185 static HRESULT WINAPI
AVISplitter_InputPin_Disconnect(IPin
* iface
)
1188 IPinImpl
*This
= (IPinImpl
*)iface
;
1192 EnterCriticalSection(This
->pCritSec
);
1194 if (This
->pConnectedTo
)
1198 hr
= IBaseFilter_GetState(This
->pinInfo
.pFilter
, 0, &state
);
1200 if (SUCCEEDED(hr
) && (state
== State_Stopped
))
1202 IPin_Release(This
->pConnectedTo
);
1203 This
->pConnectedTo
= NULL
;
1204 hr
= AVISplitter_RemoveOutputPins((AVISplitter
*)This
->pinInfo
.pFilter
);
1207 hr
= VFW_E_NOT_STOPPED
;
1212 LeaveCriticalSection(This
->pCritSec
);
1217 static const IPinVtbl AVISplitter_InputPin_Vtbl
=
1219 PullPin_QueryInterface
,
1223 PullPin_ReceiveConnection
,
1224 AVISplitter_InputPin_Disconnect
,
1225 IPinImpl_ConnectedTo
,
1226 IPinImpl_ConnectionMediaType
,
1227 IPinImpl_QueryPinInfo
,
1228 IPinImpl_QueryDirection
,
1230 IPinImpl_QueryAccept
,
1231 IPinImpl_EnumMediaTypes
,
1232 IPinImpl_QueryInternalConnections
,
1233 PullPin_EndOfStream
,