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"
38 #include "wine/unicode.h"
39 #include "wine/debug.h"
46 WINE_DEFAULT_DEBUG_CHANNEL(quartz
);
48 typedef struct AVISplitterImpl
51 IMediaSample
* pCurrentSample
;
52 RIFFCHUNK CurrentChunk
;
53 LONGLONG CurrentChunkOffset
; /* in media time */
55 AVIMAINHEADER AviHeader
;
58 static HRESULT
AVISplitter_NextChunk(LONGLONG
* pllCurrentChunkOffset
, RIFFCHUNK
* pCurrentChunk
, const REFERENCE_TIME
* tStart
, const REFERENCE_TIME
* tStop
, const BYTE
* pbSrcStream
, int inner
)
61 *pllCurrentChunkOffset
+= MEDIATIME_FROM_BYTES(sizeof(RIFFLIST
));
63 *pllCurrentChunkOffset
+= MEDIATIME_FROM_BYTES(sizeof(RIFFCHUNK
) + RIFFROUND(pCurrentChunk
->cb
));
65 if (*pllCurrentChunkOffset
>= *tStop
)
66 return S_FALSE
; /* no more data - we couldn't even get the next chunk header! */
67 else if (*pllCurrentChunkOffset
+ MEDIATIME_FROM_BYTES(sizeof(RIFFCHUNK
)) >= *tStop
)
69 memcpy(pCurrentChunk
, pbSrcStream
+ (DWORD
)BYTES_FROM_MEDIATIME(*pllCurrentChunkOffset
- *tStart
), (DWORD
)BYTES_FROM_MEDIATIME(*tStop
- *pllCurrentChunkOffset
));
70 return S_FALSE
; /* no more data */
73 memcpy(pCurrentChunk
, pbSrcStream
+ (DWORD
)BYTES_FROM_MEDIATIME(*pllCurrentChunkOffset
- *tStart
), sizeof(RIFFCHUNK
));
78 static HRESULT
AVISplitter_Sample(LPVOID iface
, IMediaSample
* pSample
)
80 AVISplitterImpl
*This
= (AVISplitterImpl
*)iface
;
81 LPBYTE pbSrcStream
= NULL
;
83 REFERENCE_TIME tStart
, tStop
;
85 BOOL bMoreData
= TRUE
;
87 hr
= IMediaSample_GetPointer(pSample
, &pbSrcStream
);
89 hr
= IMediaSample_GetTime(pSample
, &tStart
, &tStop
);
91 cbSrcStream
= IMediaSample_GetActualDataLength(pSample
);
93 /* trace removed for performance reasons */
94 /* TRACE("(%p)\n", pSample); */
96 assert(BYTES_FROM_MEDIATIME(tStop
- tStart
) == cbSrcStream
);
98 if (This
->CurrentChunkOffset
<= tStart
&& This
->CurrentChunkOffset
+ MEDIATIME_FROM_BYTES(sizeof(RIFFCHUNK
)) > tStart
)
100 DWORD offset
= (DWORD
)BYTES_FROM_MEDIATIME(tStart
- This
->CurrentChunkOffset
);
101 assert(offset
<= sizeof(RIFFCHUNK
));
102 memcpy((BYTE
*)&This
->CurrentChunk
+ offset
, pbSrcStream
, sizeof(RIFFCHUNK
) - offset
);
104 else if (This
->CurrentChunkOffset
> tStart
)
106 DWORD offset
= (DWORD
)BYTES_FROM_MEDIATIME(This
->CurrentChunkOffset
- tStart
);
107 if (offset
>= (DWORD
)cbSrcStream
)
109 FIXME("large offset\n");
114 memcpy(&This
->CurrentChunk
, pbSrcStream
+ offset
, sizeof(RIFFCHUNK
));
117 assert(This
->CurrentChunkOffset
+ MEDIATIME_FROM_BYTES(sizeof(RIFFCHUNK
)) < tStop
);
123 long chunk_remaining_bytes
= 0;
126 Parser_OutputPin
* pOutputPin
;
127 BOOL bSyncPoint
= TRUE
;
129 if (This
->CurrentChunkOffset
>= tStart
)
130 offset_src
= (long)BYTES_FROM_MEDIATIME(This
->CurrentChunkOffset
- tStart
) + sizeof(RIFFCHUNK
);
134 switch (This
->CurrentChunk
.fcc
)
137 case aviFCC('i','d','x','1'): /* Index is not handled */
138 /* silently ignore */
139 if (S_FALSE
== AVISplitter_NextChunk(&This
->CurrentChunkOffset
, &This
->CurrentChunk
, &tStart
, &tStop
, pbSrcStream
, FALSE
))
143 /* We only handle the 'rec ' list which contains the stream data */
144 if ((*(DWORD
*)(pbSrcStream
+ BYTES_FROM_MEDIATIME(This
->CurrentChunkOffset
-tStart
) + sizeof(RIFFCHUNK
))) == aviFCC('r','e','c',' '))
146 /* FIXME: We only advanced to the first chunk inside the list without keeping track that we are in it.
147 * This is not clean and the parser should be improved for that but it is enough for most AVI files. */
148 if (S_FALSE
== AVISplitter_NextChunk(&This
->CurrentChunkOffset
, &This
->CurrentChunk
, &tStart
, &tStop
, pbSrcStream
, TRUE
))
153 This
->CurrentChunk
= *(RIFFCHUNK
*) (pbSrcStream
+ BYTES_FROM_MEDIATIME(This
->CurrentChunkOffset
-tStart
));
154 offset_src
= (long)BYTES_FROM_MEDIATIME(This
->CurrentChunkOffset
- tStart
) + sizeof(RIFFCHUNK
);
157 else if (S_FALSE
== AVISplitter_NextChunk(&This
->CurrentChunkOffset
, &This
->CurrentChunk
, &tStart
, &tStop
, pbSrcStream
, FALSE
))
162 #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 */
163 switch (TWOCCFromFOURCC(This
->CurrentChunk
.fcc
))
165 case cktypeDIBcompressed
:
169 /* FIXME: check that pin is of type video */
171 case cktypeWAVEbytes
:
172 /* FIXME: check that pin is of type audio */
174 case cktypePALchange
:
175 FIXME("handle palette change\n");
178 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
));
179 if (S_FALSE
== AVISplitter_NextChunk(&This
->CurrentChunkOffset
, &This
->CurrentChunk
, &tStart
, &tStop
, pbSrcStream
, FALSE
))
186 streamId
= StreamFromFOURCC(This
->CurrentChunk
.fcc
);
188 if (streamId
> This
->Parser
.cStreams
)
190 ERR("Corrupted AVI file (contains stream id %d, but supposed to only have %ld streams)\n", streamId
, This
->Parser
.cStreams
);
195 pOutputPin
= (Parser_OutputPin
*)This
->Parser
.ppPins
[streamId
+ 1];
197 if (!This
->pCurrentSample
)
199 /* cache media sample until it is ready to be despatched
200 * (i.e. we reach the end of the chunk) */
201 hr
= OutputPin_GetDeliveryBuffer(&pOutputPin
->pin
, &This
->pCurrentSample
, NULL
, NULL
, 0);
205 hr
= IMediaSample_SetActualDataLength(This
->pCurrentSample
, 0);
210 TRACE("Skipping sending sample for stream %02d due to error (%lx)\n", streamId
, hr
);
211 This
->pCurrentSample
= NULL
;
212 if (S_FALSE
== AVISplitter_NextChunk(&This
->CurrentChunkOffset
, &This
->CurrentChunk
, &tStart
, &tStop
, pbSrcStream
, FALSE
))
218 hr
= IMediaSample_GetPointer(This
->pCurrentSample
, &pbDstStream
);
222 cbDstStream
= IMediaSample_GetSize(This
->pCurrentSample
);
224 chunk_remaining_bytes
= (long)BYTES_FROM_MEDIATIME(This
->CurrentChunkOffset
+ MEDIATIME_FROM_BYTES(This
->CurrentChunk
.cb
+ sizeof(RIFFCHUNK
)) - tStart
) - offset_src
;
226 assert(chunk_remaining_bytes
>= 0);
227 assert(chunk_remaining_bytes
<= cbDstStream
- IMediaSample_GetActualDataLength(This
->pCurrentSample
));
229 /* trace removed for performance reasons */
230 /* TRACE("chunk_remaining_bytes: 0x%lx, cbSrcStream: 0x%lx, offset_src: 0x%lx\n", chunk_remaining_bytes, cbSrcStream, offset_src); */
233 if (chunk_remaining_bytes
<= cbSrcStream
- offset_src
)
237 memcpy(pbDstStream
+ IMediaSample_GetActualDataLength(This
->pCurrentSample
), pbSrcStream
+ offset_src
, chunk_remaining_bytes
);
238 hr
= IMediaSample_SetActualDataLength(This
->pCurrentSample
, chunk_remaining_bytes
+ IMediaSample_GetActualDataLength(This
->pCurrentSample
));
244 REFERENCE_TIME tAviStart
, tAviStop
;
247 if (pOutputPin
->dwSamplesProcessed
== 0)
248 IMediaSample_SetDiscontinuity(This
->pCurrentSample
, TRUE
);
250 IMediaSample_SetSyncPoint(This
->pCurrentSample
, bSyncPoint
);
252 pOutputPin
->dwSamplesProcessed
++;
254 if (pOutputPin
->dwSampleSize
)
255 tAviStart
= (LONGLONG
)ceil(10000000.0 * (float)(pOutputPin
->dwSamplesProcessed
- 1) * (float)IMediaSample_GetActualDataLength(This
->pCurrentSample
) / ((float)pOutputPin
->dwSampleSize
* pOutputPin
->fSamplesPerSec
));
257 tAviStart
= (LONGLONG
)ceil(10000000.0 * (float)(pOutputPin
->dwSamplesProcessed
- 1) / (float)pOutputPin
->fSamplesPerSec
);
258 if (pOutputPin
->dwSampleSize
)
259 tAviStop
= (LONGLONG
)ceil(10000000.0 * (float)pOutputPin
->dwSamplesProcessed
* (float)IMediaSample_GetActualDataLength(This
->pCurrentSample
) / ((float)pOutputPin
->dwSampleSize
* pOutputPin
->fSamplesPerSec
));
261 tAviStop
= (LONGLONG
)ceil(10000000.0 * (float)pOutputPin
->dwSamplesProcessed
/ (float)pOutputPin
->fSamplesPerSec
);
263 IMediaSample_SetTime(This
->pCurrentSample
, &tAviStart
, &tAviStop
);
265 hr
= OutputPin_SendSample(&pOutputPin
->pin
, This
->pCurrentSample
);
266 if (hr
!= S_OK
&& hr
!= VFW_E_NOT_CONNECTED
)
267 ERR("Error sending sample (%lx)\n", hr
);
270 if (This
->pCurrentSample
)
271 IMediaSample_Release(This
->pCurrentSample
);
273 This
->pCurrentSample
= NULL
;
275 if (S_FALSE
== AVISplitter_NextChunk(&This
->CurrentChunkOffset
, &This
->CurrentChunk
, &tStart
, &tStop
, pbSrcStream
, FALSE
))
282 memcpy(pbDstStream
+ IMediaSample_GetActualDataLength(This
->pCurrentSample
), pbSrcStream
+ offset_src
, cbSrcStream
- offset_src
);
283 IMediaSample_SetActualDataLength(This
->pCurrentSample
, cbSrcStream
- offset_src
+ IMediaSample_GetActualDataLength(This
->pCurrentSample
));
290 if (tStop
>= This
->EndOfFile
)
294 TRACE("End of file reached\n");
296 for (i
= 0; i
< This
->Parser
.cStreams
; i
++)
301 TRACE("Send End Of Stream to output pin %d\n", i
);
303 hr
= IPin_ConnectedTo(This
->Parser
.ppPins
[i
+1], &ppin
);
306 hr
= IPin_EndOfStream(ppin
);
316 /* Force the pullpin thread to stop */
323 static HRESULT
AVISplitter_QueryAccept(LPVOID iface
, const AM_MEDIA_TYPE
* pmt
)
325 if (IsEqualIID(&pmt
->majortype
, &MEDIATYPE_Stream
) && IsEqualIID(&pmt
->subtype
, &MEDIASUBTYPE_Avi
))
330 static HRESULT
AVISplitter_ProcessStreamList(AVISplitterImpl
* This
, const BYTE
* pData
, DWORD cb
)
333 const RIFFCHUNK
* pChunk
;
336 float fSamplesPerSec
= 0.0f
;
337 DWORD dwSampleSize
= 0;
339 ALLOCATOR_PROPERTIES props
;
340 static const WCHAR wszStreamTemplate
[] = {'S','t','r','e','a','m',' ','%','0','2','d',0};
344 props
.cbBuffer
= 0x20000;
347 ZeroMemory(&amt
, sizeof(amt
));
348 piOutput
.dir
= PINDIR_OUTPUT
;
349 piOutput
.pFilter
= (IBaseFilter
*)This
;
350 wsprintfW(piOutput
.achName
, wszStreamTemplate
, This
->Parser
.cStreams
);
352 for (pChunk
= (const RIFFCHUNK
*)pData
;
353 ((const BYTE
*)pChunk
>= pData
) && ((const BYTE
*)pChunk
+ sizeof(RIFFCHUNK
) < pData
+ cb
) && (pChunk
->cb
> 0);
354 pChunk
= (const RIFFCHUNK
*)((const BYTE
*)pChunk
+ sizeof(RIFFCHUNK
) + pChunk
->cb
)
359 case ckidSTREAMHEADER
:
361 const AVISTREAMHEADER
* pStrHdr
= (const AVISTREAMHEADER
*)pChunk
;
362 TRACE("processing stream header\n");
364 fSamplesPerSec
= (float)pStrHdr
->dwRate
/ (float)pStrHdr
->dwScale
;
366 switch (pStrHdr
->fccType
)
368 case streamtypeVIDEO
:
369 memcpy(&amt
.formattype
, &FORMAT_VideoInfo
, sizeof(GUID
));
373 case streamtypeAUDIO
:
374 memcpy(&amt
.formattype
, &FORMAT_WaveFormatEx
, sizeof(GUID
));
377 memcpy(&amt
.formattype
, &FORMAT_None
, sizeof(GUID
));
379 memcpy(&amt
.majortype
, &MEDIATYPE_Video
, sizeof(GUID
));
380 amt
.majortype
.Data1
= pStrHdr
->fccType
;
381 memcpy(&amt
.subtype
, &MEDIATYPE_Video
, sizeof(GUID
));
382 amt
.subtype
.Data1
= pStrHdr
->fccHandler
;
383 TRACE("Subtype FCC: %.04s\n", (LPCSTR
)&pStrHdr
->fccHandler
);
384 amt
.lSampleSize
= pStrHdr
->dwSampleSize
;
385 amt
.bFixedSizeSamples
= (amt
.lSampleSize
!= 0);
387 /* FIXME: Is this right? */
388 if (!amt
.lSampleSize
)
394 amt
.bTemporalCompression
= IsEqualGUID(&amt
.majortype
, &MEDIATYPE_Video
); /* FIXME? */
395 dwSampleSize
= pStrHdr
->dwSampleSize
;
396 dwLength
= pStrHdr
->dwLength
;
398 dwLength
= This
->AviHeader
.dwTotalFrames
;
400 if (pStrHdr
->dwSuggestedBufferSize
)
401 props
.cbBuffer
= pStrHdr
->dwSuggestedBufferSize
;
405 case ckidSTREAMFORMAT
:
406 TRACE("processing stream format data\n");
407 if (IsEqualIID(&amt
.formattype
, &FORMAT_VideoInfo
))
409 VIDEOINFOHEADER
* pvi
;
410 /* biCompression member appears to override the value in the stream header.
411 * i.e. the stream header can say something completely contradictory to what
412 * is in the BITMAPINFOHEADER! */
413 if (pChunk
->cb
< sizeof(BITMAPINFOHEADER
))
415 ERR("Not enough bytes for BITMAPINFOHEADER\n");
418 amt
.cbFormat
= sizeof(VIDEOINFOHEADER
) - sizeof(BITMAPINFOHEADER
) + pChunk
->cb
;
419 amt
.pbFormat
= CoTaskMemAlloc(amt
.cbFormat
);
420 ZeroMemory(amt
.pbFormat
, amt
.cbFormat
);
421 pvi
= (VIDEOINFOHEADER
*)amt
.pbFormat
;
422 pvi
->AvgTimePerFrame
= (LONGLONG
)(10000000.0 / fSamplesPerSec
);
423 CopyMemory(&pvi
->bmiHeader
, (const BYTE
*)(pChunk
+ 1), pChunk
->cb
);
424 if (pvi
->bmiHeader
.biCompression
)
425 amt
.subtype
.Data1
= pvi
->bmiHeader
.biCompression
;
429 amt
.cbFormat
= pChunk
->cb
;
430 amt
.pbFormat
= CoTaskMemAlloc(amt
.cbFormat
);
431 CopyMemory(amt
.pbFormat
, (const BYTE
*)(pChunk
+ 1), amt
.cbFormat
);
435 TRACE("processing stream name\n");
436 /* FIXME: this doesn't exactly match native version (we omit the "##)" prefix), but hey... */
437 MultiByteToWideChar(CP_ACP
, 0, (LPCSTR
)(pChunk
+ 1), pChunk
->cb
, piOutput
.achName
, sizeof(piOutput
.achName
) / sizeof(piOutput
.achName
[0]));
439 case ckidSTREAMHANDLERDATA
:
440 FIXME("process stream handler data\n");
443 TRACE("JUNK chunk ignored\n");
446 FIXME("unknown chunk type \"%.04s\" ignored\n", (LPCSTR
)&pChunk
->fcc
);
450 if (IsEqualGUID(&amt
.formattype
, &FORMAT_WaveFormatEx
))
452 memcpy(&amt
.subtype
, &MEDIATYPE_Video
, sizeof(GUID
));
453 amt
.subtype
.Data1
= ((WAVEFORMATEX
*)amt
.pbFormat
)->wFormatTag
;
456 dump_AM_MEDIA_TYPE(&amt
);
457 TRACE("fSamplesPerSec = %f\n", (double)fSamplesPerSec
);
458 TRACE("dwSampleSize = %lx\n", dwSampleSize
);
459 TRACE("dwLength = %lx\n", dwLength
);
461 hr
= Parser_AddPin(&(This
->Parser
), &piOutput
, &props
, &amt
, fSamplesPerSec
, dwSampleSize
, dwLength
);
466 /* FIXME: fix leaks on failure here */
467 static HRESULT
AVISplitter_InputPin_PreConnect(IPin
* iface
, IPin
* pConnectPin
)
469 PullPin
*This
= (PullPin
*)iface
;
472 LONGLONG pos
= 0; /* in bytes */
474 RIFFCHUNK
* pCurrentChunk
;
475 AVISplitterImpl
* pAviSplit
= (AVISplitterImpl
*)This
->pin
.pinInfo
.pFilter
;
477 hr
= IAsyncReader_SyncRead(This
->pReader
, pos
, sizeof(list
), (BYTE
*)&list
);
480 if (list
.fcc
!= ckidRIFF
)
482 ERR("Input stream not a RIFF file\n");
485 if (list
.cb
> 1 * 1024 * 1024 * 1024) /* cannot be more than 1Gb in size */
487 ERR("Input stream violates RIFF spec\n");
490 if (list
.fccListType
!= ckidAVI
)
492 ERR("Input stream not an AVI RIFF file\n");
496 hr
= IAsyncReader_SyncRead(This
->pReader
, pos
, sizeof(list
), (BYTE
*)&list
);
497 if (list
.fcc
!= ckidLIST
)
499 ERR("Expected LIST chunk, but got %.04s\n", (LPSTR
)&list
.fcc
);
502 if (list
.fccListType
!= ckidHEADERLIST
)
504 ERR("Header list expected. Got: %.04s\n", (LPSTR
)&list
.fccListType
);
508 pBuffer
= HeapAlloc(GetProcessHeap(), 0, list
.cb
- sizeof(RIFFLIST
) + sizeof(RIFFCHUNK
));
509 hr
= IAsyncReader_SyncRead(This
->pReader
, pos
+ sizeof(list
), list
.cb
- sizeof(RIFFLIST
) + sizeof(RIFFCHUNK
), pBuffer
);
511 pAviSplit
->AviHeader
.cb
= 0;
513 for (pCurrentChunk
= (RIFFCHUNK
*)pBuffer
; (BYTE
*)pCurrentChunk
+ sizeof(*pCurrentChunk
) < pBuffer
+ list
.cb
; pCurrentChunk
= (RIFFCHUNK
*)(((BYTE
*)pCurrentChunk
) + sizeof(*pCurrentChunk
) + pCurrentChunk
->cb
))
517 switch (pCurrentChunk
->fcc
)
519 case ckidMAINAVIHEADER
:
520 /* AVIMAINHEADER includes the structure that is pCurrentChunk at the moment */
521 memcpy(&pAviSplit
->AviHeader
, pCurrentChunk
, sizeof(pAviSplit
->AviHeader
));
524 pList
= (RIFFLIST
*)pCurrentChunk
;
525 switch (pList
->fccListType
)
528 hr
= AVISplitter_ProcessStreamList(pAviSplit
, (BYTE
*)pCurrentChunk
+ sizeof(RIFFLIST
), pCurrentChunk
->cb
+ sizeof(RIFFCHUNK
) - sizeof(RIFFLIST
));
531 FIXME("process ODML header\n");
539 FIXME("unrecognised header list type: %.04s\n", (LPSTR
)&pCurrentChunk
->fcc
);
542 HeapFree(GetProcessHeap(), 0, pBuffer
);
544 if (pAviSplit
->AviHeader
.cb
!= sizeof(pAviSplit
->AviHeader
) - sizeof(RIFFCHUNK
))
546 ERR("Avi Header wrong size!\n");
550 pos
+= sizeof(RIFFCHUNK
) + list
.cb
;
551 hr
= IAsyncReader_SyncRead(This
->pReader
, pos
, sizeof(list
), (BYTE
*)&list
);
553 if (list
.fcc
== ckidJUNK
)
555 pos
+= sizeof(RIFFCHUNK
) + list
.cb
;
556 hr
= IAsyncReader_SyncRead(This
->pReader
, pos
, sizeof(list
), (BYTE
*)&list
);
559 if (list
.fcc
!= ckidLIST
)
561 ERR("Expected LIST, but got %.04s\n", (LPSTR
)&list
.fcc
);
564 if (list
.fccListType
!= ckidAVIMOVIE
)
566 ERR("Expected AVI movie list, but got %.04s\n", (LPSTR
)&list
.fccListType
);
572 pAviSplit
->CurrentChunkOffset
= MEDIATIME_FROM_BYTES(pos
+ sizeof(RIFFLIST
));
573 pAviSplit
->EndOfFile
= MEDIATIME_FROM_BYTES(pos
+ list
.cb
+ sizeof(RIFFLIST
));
574 hr
= IAsyncReader_SyncRead(This
->pReader
, BYTES_FROM_MEDIATIME(pAviSplit
->CurrentChunkOffset
), sizeof(pAviSplit
->CurrentChunk
), (BYTE
*)&pAviSplit
->CurrentChunk
);
580 TRACE("AVI File ok\n");
585 HRESULT
AVISplitter_create(IUnknown
* pUnkOuter
, LPVOID
* ppv
)
588 AVISplitterImpl
* This
;
590 TRACE("(%p, %p)\n", pUnkOuter
, ppv
);
595 return CLASS_E_NOAGGREGATION
;
597 /* Note: This memory is managed by the transform filter once created */
598 This
= CoTaskMemAlloc(sizeof(AVISplitterImpl
));
600 This
->pCurrentSample
= NULL
;
602 hr
= Parser_Create(&(This
->Parser
), &CLSID_AviSplitter
, AVISplitter_Sample
, AVISplitter_QueryAccept
, AVISplitter_InputPin_PreConnect
);