4 * Copyright 2003 Robert Shearman
5 * Copyright 2004-2005 Christian Costa
7 * This library is free software; you can redistribute it and/or
8 * modify it under the terms of the GNU Lesser General Public
9 * License as published by the Free Software Foundation; either
10 * version 2.1 of the License, or (at your option) any later version.
12 * This library is distributed in the hope that it will be useful,
13 * but WITHOUT ANY WARRANTY; without even the implied warranty of
14 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
15 * Lesser General Public License for more details.
17 * You should have received a copy of the GNU Lesser General Public
18 * License along with this library; if not, write to the Free Software
19 * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA
22 * - we don't do anything with indices yet (we could use them when seeking)
23 * - we don't support multiple RIFF sections (i.e. large AVI files > 2Gb)
26 #include "quartz_private.h"
27 #include "control_private.h"
37 #include "wine/unicode.h"
38 #include "wine/debug.h"
45 WINE_DEFAULT_DEBUG_CHANNEL(quartz
);
47 typedef struct StreamData
54 typedef struct AVISplitterImpl
57 IMediaSample
* pCurrentSample
;
58 RIFFCHUNK CurrentChunk
;
59 LONGLONG CurrentChunkOffset
; /* in media time */
61 AVIMAINHEADER AviHeader
;
65 static HRESULT
AVISplitter_NextChunk(LONGLONG
* pllCurrentChunkOffset
, RIFFCHUNK
* pCurrentChunk
, const REFERENCE_TIME
* tStart
, const REFERENCE_TIME
* tStop
, const BYTE
* pbSrcStream
, int inner
)
68 *pllCurrentChunkOffset
+= MEDIATIME_FROM_BYTES(sizeof(RIFFLIST
));
70 *pllCurrentChunkOffset
+= MEDIATIME_FROM_BYTES(sizeof(RIFFCHUNK
) + RIFFROUND(pCurrentChunk
->cb
));
72 if (*pllCurrentChunkOffset
>= *tStop
)
73 return S_FALSE
; /* no more data - we couldn't even get the next chunk header! */
74 else if (*pllCurrentChunkOffset
+ MEDIATIME_FROM_BYTES(sizeof(RIFFCHUNK
)) >= *tStop
)
76 memcpy(pCurrentChunk
, pbSrcStream
+ (DWORD
)BYTES_FROM_MEDIATIME(*pllCurrentChunkOffset
- *tStart
), (DWORD
)BYTES_FROM_MEDIATIME(*tStop
- *pllCurrentChunkOffset
));
77 return S_FALSE
; /* no more data */
80 memcpy(pCurrentChunk
, pbSrcStream
+ (DWORD
)BYTES_FROM_MEDIATIME(*pllCurrentChunkOffset
- *tStart
), sizeof(RIFFCHUNK
));
85 static HRESULT
AVISplitter_Sample(LPVOID iface
, IMediaSample
* pSample
)
87 AVISplitterImpl
*This
= (AVISplitterImpl
*)iface
;
88 LPBYTE pbSrcStream
= NULL
;
90 REFERENCE_TIME tStart
, tStop
;
92 BOOL bMoreData
= TRUE
;
94 hr
= IMediaSample_GetPointer(pSample
, &pbSrcStream
);
96 hr
= IMediaSample_GetTime(pSample
, &tStart
, &tStop
);
98 cbSrcStream
= IMediaSample_GetActualDataLength(pSample
);
100 /* trace removed for performance reasons */
101 /* TRACE("(%p)\n", pSample); */
103 assert(BYTES_FROM_MEDIATIME(tStop
- tStart
) == cbSrcStream
);
105 if (This
->CurrentChunkOffset
<= tStart
&& This
->CurrentChunkOffset
+ MEDIATIME_FROM_BYTES(sizeof(RIFFCHUNK
)) > tStart
)
107 DWORD offset
= (DWORD
)BYTES_FROM_MEDIATIME(tStart
- This
->CurrentChunkOffset
);
108 assert(offset
<= sizeof(RIFFCHUNK
));
109 memcpy((BYTE
*)&This
->CurrentChunk
+ offset
, pbSrcStream
, sizeof(RIFFCHUNK
) - offset
);
111 else if (This
->CurrentChunkOffset
> tStart
)
113 DWORD offset
= (DWORD
)BYTES_FROM_MEDIATIME(This
->CurrentChunkOffset
- tStart
);
114 if (offset
>= (DWORD
)cbSrcStream
)
116 FIXME("large offset\n");
121 memcpy(&This
->CurrentChunk
, pbSrcStream
+ offset
, sizeof(RIFFCHUNK
));
124 assert(This
->CurrentChunkOffset
+ MEDIATIME_FROM_BYTES(sizeof(RIFFCHUNK
)) < tStop
);
130 long chunk_remaining_bytes
= 0;
133 Parser_OutputPin
* pOutputPin
;
134 BOOL bSyncPoint
= TRUE
;
136 if (This
->CurrentChunkOffset
>= tStart
)
137 offset_src
= (long)BYTES_FROM_MEDIATIME(This
->CurrentChunkOffset
- tStart
) + sizeof(RIFFCHUNK
);
141 switch (This
->CurrentChunk
.fcc
)
144 case aviFCC('i','d','x','1'): /* Index is not handled */
145 /* silently ignore */
146 if (S_FALSE
== AVISplitter_NextChunk(&This
->CurrentChunkOffset
, &This
->CurrentChunk
, &tStart
, &tStop
, pbSrcStream
, FALSE
))
150 /* We only handle the 'rec ' list which contains the stream data */
151 if ((*(DWORD
*)(pbSrcStream
+ BYTES_FROM_MEDIATIME(This
->CurrentChunkOffset
-tStart
) + sizeof(RIFFCHUNK
))) == aviFCC('r','e','c',' '))
153 /* FIXME: We only advanced to the first chunk inside the list without keeping track that we are in it.
154 * This is not clean and the parser should be improved for that but it is enough for most AVI files. */
155 if (S_FALSE
== AVISplitter_NextChunk(&This
->CurrentChunkOffset
, &This
->CurrentChunk
, &tStart
, &tStop
, pbSrcStream
, TRUE
))
160 This
->CurrentChunk
= *(RIFFCHUNK
*) (pbSrcStream
+ BYTES_FROM_MEDIATIME(This
->CurrentChunkOffset
-tStart
));
161 offset_src
= (long)BYTES_FROM_MEDIATIME(This
->CurrentChunkOffset
- tStart
) + sizeof(RIFFCHUNK
);
164 else if (S_FALSE
== AVISplitter_NextChunk(&This
->CurrentChunkOffset
, &This
->CurrentChunk
, &tStart
, &tStop
, pbSrcStream
, FALSE
))
169 #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 */
170 switch (TWOCCFromFOURCC(This
->CurrentChunk
.fcc
))
172 case cktypeDIBcompressed
:
176 /* FIXME: check that pin is of type video */
178 case cktypeWAVEbytes
:
179 /* FIXME: check that pin is of type audio */
181 case cktypePALchange
:
182 FIXME("handle palette change\n");
185 FIXME("Skipping unknown chunk type: %s at file offset 0x%x\n", debugstr_an((LPSTR
)&This
->CurrentChunk
.fcc
, 4), (DWORD
)BYTES_FROM_MEDIATIME(This
->CurrentChunkOffset
));
186 if (S_FALSE
== AVISplitter_NextChunk(&This
->CurrentChunkOffset
, &This
->CurrentChunk
, &tStart
, &tStop
, pbSrcStream
, FALSE
))
193 streamId
= StreamFromFOURCC(This
->CurrentChunk
.fcc
);
195 if (streamId
> This
->Parser
.cStreams
)
197 ERR("Corrupted AVI file (contains stream id %d, but supposed to only have %d streams)\n", streamId
, This
->Parser
.cStreams
);
202 pOutputPin
= (Parser_OutputPin
*)This
->Parser
.ppPins
[streamId
+ 1];
204 if (!This
->pCurrentSample
)
206 /* cache media sample until it is ready to be despatched
207 * (i.e. we reach the end of the chunk) */
208 hr
= OutputPin_GetDeliveryBuffer(&pOutputPin
->pin
, &This
->pCurrentSample
, NULL
, NULL
, 0);
212 hr
= IMediaSample_SetActualDataLength(This
->pCurrentSample
, 0);
217 TRACE("Skipping sending sample for stream %02d due to error (%x)\n", streamId
, hr
);
218 This
->pCurrentSample
= NULL
;
219 if (S_FALSE
== AVISplitter_NextChunk(&This
->CurrentChunkOffset
, &This
->CurrentChunk
, &tStart
, &tStop
, pbSrcStream
, FALSE
))
225 hr
= IMediaSample_GetPointer(This
->pCurrentSample
, &pbDstStream
);
229 cbDstStream
= IMediaSample_GetSize(This
->pCurrentSample
);
231 chunk_remaining_bytes
= (long)BYTES_FROM_MEDIATIME(This
->CurrentChunkOffset
+ MEDIATIME_FROM_BYTES(This
->CurrentChunk
.cb
+ sizeof(RIFFCHUNK
)) - tStart
) - offset_src
;
233 assert(chunk_remaining_bytes
>= 0);
234 assert(chunk_remaining_bytes
<= cbDstStream
- IMediaSample_GetActualDataLength(This
->pCurrentSample
));
236 /* trace removed for performance reasons */
237 /* TRACE("chunk_remaining_bytes: 0x%x, cbSrcStream: 0x%x, offset_src: 0x%x\n", chunk_remaining_bytes, cbSrcStream, offset_src); */
240 if (chunk_remaining_bytes
<= cbSrcStream
- offset_src
)
244 memcpy(pbDstStream
+ IMediaSample_GetActualDataLength(This
->pCurrentSample
), pbSrcStream
+ offset_src
, chunk_remaining_bytes
);
245 hr
= IMediaSample_SetActualDataLength(This
->pCurrentSample
, chunk_remaining_bytes
+ IMediaSample_GetActualDataLength(This
->pCurrentSample
));
251 REFERENCE_TIME tAviStart
, tAviStop
;
252 StreamData
*stream
= This
->streams
+ streamId
;
255 if (pOutputPin
->dwSamplesProcessed
== 0)
256 IMediaSample_SetDiscontinuity(This
->pCurrentSample
, TRUE
);
258 IMediaSample_SetSyncPoint(This
->pCurrentSample
, bSyncPoint
);
260 pOutputPin
->dwSamplesProcessed
++;
262 if (stream
->dwSampleSize
)
263 tAviStart
= (LONGLONG
)ceil(10000000.0 * (float)(pOutputPin
->dwSamplesProcessed
- 1) * (float)IMediaSample_GetActualDataLength(This
->pCurrentSample
) / ((float)stream
->dwSampleSize
* stream
->fSamplesPerSec
));
265 tAviStart
= (LONGLONG
)ceil(10000000.0 * (float)(pOutputPin
->dwSamplesProcessed
- 1) / (float)stream
->fSamplesPerSec
);
266 if (stream
->dwSampleSize
)
267 tAviStop
= (LONGLONG
)ceil(10000000.0 * (float)pOutputPin
->dwSamplesProcessed
* (float)IMediaSample_GetActualDataLength(This
->pCurrentSample
) / ((float)stream
->dwSampleSize
* stream
->fSamplesPerSec
));
269 tAviStop
= (LONGLONG
)ceil(10000000.0 * (float)pOutputPin
->dwSamplesProcessed
/ (float)stream
->fSamplesPerSec
);
271 IMediaSample_SetTime(This
->pCurrentSample
, &tAviStart
, &tAviStop
);
273 hr
= OutputPin_SendSample(&pOutputPin
->pin
, This
->pCurrentSample
);
274 if (hr
!= S_OK
&& hr
!= VFW_E_NOT_CONNECTED
)
275 ERR("Error sending sample (%x)\n", hr
);
278 if (This
->pCurrentSample
)
279 IMediaSample_Release(This
->pCurrentSample
);
281 This
->pCurrentSample
= NULL
;
283 if (S_FALSE
== AVISplitter_NextChunk(&This
->CurrentChunkOffset
, &This
->CurrentChunk
, &tStart
, &tStop
, pbSrcStream
, FALSE
))
290 memcpy(pbDstStream
+ IMediaSample_GetActualDataLength(This
->pCurrentSample
), pbSrcStream
+ offset_src
, cbSrcStream
- offset_src
);
291 IMediaSample_SetActualDataLength(This
->pCurrentSample
, cbSrcStream
- offset_src
+ IMediaSample_GetActualDataLength(This
->pCurrentSample
));
298 if (tStop
>= This
->EndOfFile
)
302 TRACE("End of file reached\n");
304 for (i
= 0; i
< This
->Parser
.cStreams
; i
++)
309 TRACE("Send End Of Stream to output pin %d\n", i
);
311 hr
= IPin_ConnectedTo(This
->Parser
.ppPins
[i
+1], &ppin
);
314 hr
= IPin_EndOfStream(ppin
);
324 /* Force the pullpin thread to stop */
331 static HRESULT
AVISplitter_QueryAccept(LPVOID iface
, const AM_MEDIA_TYPE
* pmt
)
333 if (IsEqualIID(&pmt
->majortype
, &MEDIATYPE_Stream
) && IsEqualIID(&pmt
->subtype
, &MEDIASUBTYPE_Avi
))
338 static HRESULT
AVISplitter_ProcessStreamList(AVISplitterImpl
* This
, const BYTE
* pData
, DWORD cb
)
341 const RIFFCHUNK
* pChunk
;
344 float fSamplesPerSec
= 0.0f
;
345 DWORD dwSampleSize
= 0;
347 ALLOCATOR_PROPERTIES props
;
348 static const WCHAR wszStreamTemplate
[] = {'S','t','r','e','a','m',' ','%','0','2','d',0};
353 props
.cbBuffer
= 0x20000;
356 ZeroMemory(&amt
, sizeof(amt
));
357 piOutput
.dir
= PINDIR_OUTPUT
;
358 piOutput
.pFilter
= (IBaseFilter
*)This
;
359 wsprintfW(piOutput
.achName
, wszStreamTemplate
, This
->Parser
.cStreams
);
361 for (pChunk
= (const RIFFCHUNK
*)pData
;
362 ((const BYTE
*)pChunk
>= pData
) && ((const BYTE
*)pChunk
+ sizeof(RIFFCHUNK
) < pData
+ cb
) && (pChunk
->cb
> 0);
363 pChunk
= (const RIFFCHUNK
*)((const BYTE
*)pChunk
+ sizeof(RIFFCHUNK
) + pChunk
->cb
)
368 case ckidSTREAMHEADER
:
370 const AVISTREAMHEADER
* pStrHdr
= (const AVISTREAMHEADER
*)pChunk
;
371 TRACE("processing stream header\n");
373 fSamplesPerSec
= (float)pStrHdr
->dwRate
/ (float)pStrHdr
->dwScale
;
375 switch (pStrHdr
->fccType
)
377 case streamtypeVIDEO
:
378 amt
.formattype
= FORMAT_VideoInfo
;
382 case streamtypeAUDIO
:
383 amt
.formattype
= FORMAT_WaveFormatEx
;
386 amt
.formattype
= FORMAT_None
;
388 amt
.majortype
= MEDIATYPE_Video
;
389 amt
.majortype
.Data1
= pStrHdr
->fccType
;
390 amt
.subtype
= MEDIATYPE_Video
;
391 amt
.subtype
.Data1
= pStrHdr
->fccHandler
;
392 TRACE("Subtype FCC: %.04s\n", (LPCSTR
)&pStrHdr
->fccHandler
);
393 amt
.lSampleSize
= pStrHdr
->dwSampleSize
;
394 amt
.bFixedSizeSamples
= (amt
.lSampleSize
!= 0);
396 /* FIXME: Is this right? */
397 if (!amt
.lSampleSize
)
403 amt
.bTemporalCompression
= IsEqualGUID(&amt
.majortype
, &MEDIATYPE_Video
); /* FIXME? */
404 dwSampleSize
= pStrHdr
->dwSampleSize
;
405 dwLength
= pStrHdr
->dwLength
;
407 dwLength
= This
->AviHeader
.dwTotalFrames
;
409 if (pStrHdr
->dwSuggestedBufferSize
)
410 props
.cbBuffer
= pStrHdr
->dwSuggestedBufferSize
;
414 case ckidSTREAMFORMAT
:
415 TRACE("processing stream format data\n");
416 if (IsEqualIID(&amt
.formattype
, &FORMAT_VideoInfo
))
418 VIDEOINFOHEADER
* pvi
;
419 /* biCompression member appears to override the value in the stream header.
420 * i.e. the stream header can say something completely contradictory to what
421 * is in the BITMAPINFOHEADER! */
422 if (pChunk
->cb
< sizeof(BITMAPINFOHEADER
))
424 ERR("Not enough bytes for BITMAPINFOHEADER\n");
427 amt
.cbFormat
= sizeof(VIDEOINFOHEADER
) - sizeof(BITMAPINFOHEADER
) + pChunk
->cb
;
428 amt
.pbFormat
= CoTaskMemAlloc(amt
.cbFormat
);
429 ZeroMemory(amt
.pbFormat
, amt
.cbFormat
);
430 pvi
= (VIDEOINFOHEADER
*)amt
.pbFormat
;
431 pvi
->AvgTimePerFrame
= (LONGLONG
)(10000000.0 / fSamplesPerSec
);
432 CopyMemory(&pvi
->bmiHeader
, (const BYTE
*)(pChunk
+ 1), pChunk
->cb
);
433 if (pvi
->bmiHeader
.biCompression
)
434 amt
.subtype
.Data1
= pvi
->bmiHeader
.biCompression
;
438 amt
.cbFormat
= pChunk
->cb
;
439 amt
.pbFormat
= CoTaskMemAlloc(amt
.cbFormat
);
440 CopyMemory(amt
.pbFormat
, (const BYTE
*)(pChunk
+ 1), amt
.cbFormat
);
444 TRACE("processing stream name\n");
445 /* FIXME: this doesn't exactly match native version (we omit the "##)" prefix), but hey... */
446 MultiByteToWideChar(CP_ACP
, 0, (LPCSTR
)(pChunk
+ 1), pChunk
->cb
, piOutput
.achName
, sizeof(piOutput
.achName
) / sizeof(piOutput
.achName
[0]));
448 case ckidSTREAMHANDLERDATA
:
449 FIXME("process stream handler data\n");
452 TRACE("JUNK chunk ignored\n");
455 FIXME("unknown chunk type \"%.04s\" ignored\n", (LPCSTR
)&pChunk
->fcc
);
459 if (IsEqualGUID(&amt
.formattype
, &FORMAT_WaveFormatEx
))
461 amt
.subtype
= MEDIATYPE_Video
;
462 amt
.subtype
.Data1
= ((WAVEFORMATEX
*)amt
.pbFormat
)->wFormatTag
;
465 dump_AM_MEDIA_TYPE(&amt
);
466 TRACE("fSamplesPerSec = %f\n", (double)fSamplesPerSec
);
467 TRACE("dwSampleSize = %x\n", dwSampleSize
);
468 TRACE("dwLength = %x\n", dwLength
);
469 This
->streams
= CoTaskMemRealloc(This
->streams
, sizeof(StreamData
) * (This
->Parser
.cStreams
+1));
470 stream
= This
->streams
+ This
->Parser
.cStreams
;
471 stream
->fSamplesPerSec
= fSamplesPerSec
;
472 stream
->dwSampleSize
= dwSampleSize
;
473 stream
->dwLength
= dwLength
; /* TODO: Use this for mediaseeking */
475 hr
= Parser_AddPin(&(This
->Parser
), &piOutput
, &props
, &amt
);
480 /* FIXME: fix leaks on failure here */
481 static HRESULT
AVISplitter_InputPin_PreConnect(IPin
* iface
, IPin
* pConnectPin
)
483 PullPin
*This
= (PullPin
*)iface
;
486 LONGLONG pos
= 0; /* in bytes */
488 RIFFCHUNK
* pCurrentChunk
;
489 AVISplitterImpl
* pAviSplit
= (AVISplitterImpl
*)This
->pin
.pinInfo
.pFilter
;
491 hr
= IAsyncReader_SyncRead(This
->pReader
, pos
, sizeof(list
), (BYTE
*)&list
);
494 if (list
.fcc
!= ckidRIFF
)
496 ERR("Input stream not a RIFF file\n");
499 if (list
.fccListType
!= ckidAVI
)
501 ERR("Input stream not an AVI RIFF file\n");
505 hr
= IAsyncReader_SyncRead(This
->pReader
, pos
, sizeof(list
), (BYTE
*)&list
);
506 if (list
.fcc
!= ckidLIST
)
508 ERR("Expected LIST chunk, but got %.04s\n", (LPSTR
)&list
.fcc
);
511 if (list
.fccListType
!= ckidHEADERLIST
)
513 ERR("Header list expected. Got: %.04s\n", (LPSTR
)&list
.fccListType
);
517 pBuffer
= HeapAlloc(GetProcessHeap(), 0, list
.cb
- sizeof(RIFFLIST
) + sizeof(RIFFCHUNK
));
518 hr
= IAsyncReader_SyncRead(This
->pReader
, pos
+ sizeof(list
), list
.cb
- sizeof(RIFFLIST
) + sizeof(RIFFCHUNK
), pBuffer
);
520 pAviSplit
->AviHeader
.cb
= 0;
522 for (pCurrentChunk
= (RIFFCHUNK
*)pBuffer
; (BYTE
*)pCurrentChunk
+ sizeof(*pCurrentChunk
) < pBuffer
+ list
.cb
; pCurrentChunk
= (RIFFCHUNK
*)(((BYTE
*)pCurrentChunk
) + sizeof(*pCurrentChunk
) + pCurrentChunk
->cb
))
526 switch (pCurrentChunk
->fcc
)
528 case ckidMAINAVIHEADER
:
529 /* AVIMAINHEADER includes the structure that is pCurrentChunk at the moment */
530 memcpy(&pAviSplit
->AviHeader
, pCurrentChunk
, sizeof(pAviSplit
->AviHeader
));
533 pList
= (RIFFLIST
*)pCurrentChunk
;
534 switch (pList
->fccListType
)
537 hr
= AVISplitter_ProcessStreamList(pAviSplit
, (BYTE
*)pCurrentChunk
+ sizeof(RIFFLIST
), pCurrentChunk
->cb
+ sizeof(RIFFCHUNK
) - sizeof(RIFFLIST
));
540 FIXME("process ODML header\n");
548 FIXME("unrecognised header list type: %.04s\n", (LPSTR
)&pCurrentChunk
->fcc
);
551 HeapFree(GetProcessHeap(), 0, pBuffer
);
553 if (pAviSplit
->AviHeader
.cb
!= sizeof(pAviSplit
->AviHeader
) - sizeof(RIFFCHUNK
))
555 ERR("Avi Header wrong size!\n");
559 pos
+= sizeof(RIFFCHUNK
) + list
.cb
;
560 hr
= IAsyncReader_SyncRead(This
->pReader
, pos
, sizeof(list
), (BYTE
*)&list
);
562 while (list
.fcc
== ckidJUNK
|| (list
.fcc
== ckidLIST
&& list
.fccListType
== ckidINFO
))
564 pos
+= sizeof(RIFFCHUNK
) + list
.cb
;
565 hr
= IAsyncReader_SyncRead(This
->pReader
, pos
, sizeof(list
), (BYTE
*)&list
);
568 if (list
.fcc
!= ckidLIST
)
570 ERR("Expected LIST, but got %.04s\n", (LPSTR
)&list
.fcc
);
573 if (list
.fccListType
!= ckidAVIMOVIE
)
575 ERR("Expected AVI movie list, but got %.04s\n", (LPSTR
)&list
.fccListType
);
581 pAviSplit
->CurrentChunkOffset
= MEDIATIME_FROM_BYTES(pos
+ sizeof(RIFFLIST
));
582 pAviSplit
->EndOfFile
= MEDIATIME_FROM_BYTES(pos
+ list
.cb
+ sizeof(RIFFLIST
));
583 hr
= IAsyncReader_SyncRead(This
->pReader
, BYTES_FROM_MEDIATIME(pAviSplit
->CurrentChunkOffset
), sizeof(pAviSplit
->CurrentChunk
), (BYTE
*)&pAviSplit
->CurrentChunk
);
589 TRACE("AVI File ok\n");
594 static HRESULT
AVISplitter_Cleanup(LPVOID iface
)
596 AVISplitterImpl
*This
= (AVISplitterImpl
*)iface
;
598 TRACE("(%p)->()\n", This
);
600 if (This
->pCurrentSample
)
601 IMediaSample_Release(This
->pCurrentSample
);
602 This
->pCurrentSample
= NULL
;
607 HRESULT
AVISplitter_create(IUnknown
* pUnkOuter
, LPVOID
* ppv
)
610 AVISplitterImpl
* This
;
612 TRACE("(%p, %p)\n", pUnkOuter
, ppv
);
617 return CLASS_E_NOAGGREGATION
;
619 /* Note: This memory is managed by the transform filter once created */
620 This
= CoTaskMemAlloc(sizeof(AVISplitterImpl
));
622 This
->pCurrentSample
= NULL
;
623 This
->streams
= NULL
;
625 hr
= Parser_Create(&(This
->Parser
), &CLSID_AviSplitter
, AVISplitter_Sample
, AVISplitter_QueryAccept
, AVISplitter_InputPin_PreConnect
, AVISplitter_Cleanup
);