quartz: Move IMediaSeeking from the parser pin to the parser filter.
[wine/wine64.git] / dlls / quartz / avisplit.c
blob140b0dfe29c179972d329d15f71b81a8a032d9bf
1 /*
2 * AVI Splitter Filter
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
21 /* FIXME:
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"
28 #include "pin.h"
30 #include "uuids.h"
31 #include "aviriff.h"
32 #include "vfwmsgs.h"
33 #include "amvideo.h"
35 #include "fourcc.h"
37 #include "wine/unicode.h"
38 #include "wine/debug.h"
40 #include <math.h>
41 #include <assert.h>
43 #include "parser.h"
45 WINE_DEFAULT_DEBUG_CHANNEL(quartz);
47 typedef struct StreamData
49 DWORD dwSampleSize;
50 FLOAT fSamplesPerSec;
51 DWORD dwLength;
52 } StreamData;
54 typedef struct AVISplitterImpl
56 ParserImpl Parser;
57 IMediaSample * pCurrentSample;
58 RIFFCHUNK CurrentChunk;
59 LONGLONG CurrentChunkOffset; /* in media time */
60 LONGLONG EndOfFile;
61 AVIMAINHEADER AviHeader;
62 StreamData *streams;
63 } AVISplitterImpl;
65 static HRESULT AVISplitter_NextChunk(LONGLONG * pllCurrentChunkOffset, RIFFCHUNK * pCurrentChunk, const REFERENCE_TIME * tStart, const REFERENCE_TIME * tStop, const BYTE * pbSrcStream, int inner)
67 if (inner)
68 *pllCurrentChunkOffset += MEDIATIME_FROM_BYTES(sizeof(RIFFLIST));
69 else
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 */
79 else
80 memcpy(pCurrentChunk, pbSrcStream + (DWORD)BYTES_FROM_MEDIATIME(*pllCurrentChunkOffset - *tStart), sizeof(RIFFCHUNK));
82 return S_OK;
85 static HRESULT AVISplitter_Sample(LPVOID iface, IMediaSample * pSample)
87 AVISplitterImpl *This = (AVISplitterImpl *)iface;
88 LPBYTE pbSrcStream = NULL;
89 long cbSrcStream = 0;
90 REFERENCE_TIME tStart, tStop;
91 HRESULT hr;
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");
117 hr = S_OK;
118 goto skip;
121 memcpy(&This->CurrentChunk, pbSrcStream + offset, sizeof(RIFFCHUNK));
124 assert(This->CurrentChunkOffset + MEDIATIME_FROM_BYTES(sizeof(RIFFCHUNK)) < tStop);
126 while (bMoreData)
128 BYTE * pbDstStream;
129 long cbDstStream;
130 long chunk_remaining_bytes = 0;
131 long offset_src;
132 WORD streamId;
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);
138 else
139 offset_src = 0;
141 switch (This->CurrentChunk.fcc)
143 case ckidJUNK:
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))
147 bMoreData = FALSE;
148 continue;
149 case ckidLIST:
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))
157 bMoreData = FALSE;
158 continue;
160 This->CurrentChunk = *(RIFFCHUNK*) (pbSrcStream + BYTES_FROM_MEDIATIME(This->CurrentChunkOffset-tStart));
161 offset_src = (long)BYTES_FROM_MEDIATIME(This->CurrentChunkOffset - tStart) + sizeof(RIFFCHUNK);
162 break;
164 else if (S_FALSE == AVISplitter_NextChunk(&This->CurrentChunkOffset, &This->CurrentChunk, &tStart, &tStop, pbSrcStream, FALSE))
165 bMoreData = FALSE;
166 continue;
167 default:
168 break;
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:
173 bSyncPoint = FALSE;
174 /* fall-through */
175 case cktypeDIBbits:
176 /* FIXME: check that pin is of type video */
177 break;
178 case cktypeWAVEbytes:
179 /* FIXME: check that pin is of type audio */
180 break;
181 case cktypePALchange:
182 FIXME("handle palette change\n");
183 break;
184 default:
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))
187 bMoreData = FALSE;
188 continue;
190 #endif
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);
198 hr = E_FAIL;
199 break;
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);
210 if (SUCCEEDED(hr))
212 hr = IMediaSample_SetActualDataLength(This->pCurrentSample, 0);
213 assert(hr == S_OK);
215 else
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))
220 bMoreData = FALSE;
221 continue;
225 hr = IMediaSample_GetPointer(This->pCurrentSample, &pbDstStream);
227 if (SUCCEEDED(hr))
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)
242 if (SUCCEEDED(hr))
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));
246 assert(hr == S_OK);
249 if (SUCCEEDED(hr))
251 REFERENCE_TIME tAviStart, tAviStop;
252 StreamData *stream = This->streams + streamId;
254 /* FIXME: hack */
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));
264 else
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));
268 else
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))
284 bMoreData = FALSE;
286 else
288 if (SUCCEEDED(hr))
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));
293 bMoreData = FALSE;
297 skip:
298 if (tStop >= This->EndOfFile)
300 int i;
302 TRACE("End of file reached\n");
304 for (i = 0; i < This->Parser.cStreams; i++)
306 IPin* ppin;
307 HRESULT hr;
309 TRACE("Send End Of Stream to output pin %d\n", i);
311 hr = IPin_ConnectedTo(This->Parser.ppPins[i+1], &ppin);
312 if (SUCCEEDED(hr))
314 hr = IPin_EndOfStream(ppin);
315 IPin_Release(ppin);
317 if (FAILED(hr))
319 ERR("%x\n", hr);
320 break;
324 /* Force the pullpin thread to stop */
325 hr = S_FALSE;
328 return hr;
331 static HRESULT AVISplitter_QueryAccept(LPVOID iface, const AM_MEDIA_TYPE * pmt)
333 if (IsEqualIID(&pmt->majortype, &MEDIATYPE_Stream) && IsEqualIID(&pmt->subtype, &MEDIASUBTYPE_Avi))
334 return S_OK;
335 return S_FALSE;
338 static HRESULT AVISplitter_ProcessStreamList(AVISplitterImpl * This, const BYTE * pData, DWORD cb)
340 PIN_INFO piOutput;
341 const RIFFCHUNK * pChunk;
342 HRESULT hr;
343 AM_MEDIA_TYPE amt;
344 float fSamplesPerSec = 0.0f;
345 DWORD dwSampleSize = 0;
346 DWORD dwLength = 0;
347 ALLOCATOR_PROPERTIES props;
348 static const WCHAR wszStreamTemplate[] = {'S','t','r','e','a','m',' ','%','0','2','d',0};
349 StreamData *stream;
351 props.cbAlign = 1;
352 props.cbPrefix = 0;
353 props.cbBuffer = 0x20000;
354 props.cBuffers = 2;
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)
366 switch (pChunk->fcc)
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;
379 amt.pbFormat = NULL;
380 amt.cbFormat = 0;
381 break;
382 case streamtypeAUDIO:
383 amt.formattype = FORMAT_WaveFormatEx;
384 break;
385 default:
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)
399 amt.lSampleSize = 1;
400 dwSampleSize = 1;
403 amt.bTemporalCompression = IsEqualGUID(&amt.majortype, &MEDIATYPE_Video); /* FIXME? */
404 dwSampleSize = pStrHdr->dwSampleSize;
405 dwLength = pStrHdr->dwLength;
406 if (!dwLength)
407 dwLength = This->AviHeader.dwTotalFrames;
409 if (pStrHdr->dwSuggestedBufferSize)
410 props.cbBuffer = pStrHdr->dwSuggestedBufferSize;
412 break;
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");
425 return E_FAIL;
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;
436 else
438 amt.cbFormat = pChunk->cb;
439 amt.pbFormat = CoTaskMemAlloc(amt.cbFormat);
440 CopyMemory(amt.pbFormat, (const BYTE *)(pChunk + 1), amt.cbFormat);
442 break;
443 case ckidSTREAMNAME:
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]));
447 break;
448 case ckidSTREAMHANDLERDATA:
449 FIXME("process stream handler data\n");
450 break;
451 case ckidJUNK:
452 TRACE("JUNK chunk ignored\n");
453 break;
454 default:
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);
477 return hr;
480 /* FIXME: fix leaks on failure here */
481 static HRESULT AVISplitter_InputPin_PreConnect(IPin * iface, IPin * pConnectPin)
483 PullPin *This = (PullPin *)iface;
484 HRESULT hr;
485 RIFFLIST list;
486 LONGLONG pos = 0; /* in bytes */
487 BYTE * pBuffer;
488 RIFFCHUNK * pCurrentChunk;
489 AVISplitterImpl * pAviSplit = (AVISplitterImpl *)This->pin.pinInfo.pFilter;
491 hr = IAsyncReader_SyncRead(This->pReader, pos, sizeof(list), (BYTE *)&list);
492 pos += sizeof(list);
494 if (list.fcc != ckidRIFF)
496 ERR("Input stream not a RIFF file\n");
497 return E_FAIL;
499 if (list.fccListType != ckidAVI)
501 ERR("Input stream not an AVI RIFF file\n");
502 return E_FAIL;
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);
509 return E_FAIL;
511 if (list.fccListType != ckidHEADERLIST)
513 ERR("Header list expected. Got: %.04s\n", (LPSTR)&list.fccListType);
514 return E_FAIL;
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))
524 RIFFLIST * pList;
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));
531 break;
532 case ckidLIST:
533 pList = (RIFFLIST *)pCurrentChunk;
534 switch (pList->fccListType)
536 case ckidSTREAMLIST:
537 hr = AVISplitter_ProcessStreamList(pAviSplit, (BYTE *)pCurrentChunk + sizeof(RIFFLIST), pCurrentChunk->cb + sizeof(RIFFCHUNK) - sizeof(RIFFLIST));
538 break;
539 case ckidODML:
540 FIXME("process ODML header\n");
541 break;
543 break;
544 case ckidJUNK:
545 /* ignore */
546 break;
547 default:
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");
556 return E_FAIL;
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);
571 return E_FAIL;
573 if (list.fccListType != ckidAVIMOVIE)
575 ERR("Expected AVI movie list, but got %.04s\n", (LPSTR)&list.fccListType);
576 return E_FAIL;
579 if (hr == S_OK)
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);
586 if (hr != S_OK)
587 return E_FAIL;
589 TRACE("AVI File ok\n");
591 return hr;
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;
604 return S_OK;
607 HRESULT AVISplitter_create(IUnknown * pUnkOuter, LPVOID * ppv)
609 HRESULT hr;
610 AVISplitterImpl * This;
612 TRACE("(%p, %p)\n", pUnkOuter, ppv);
614 *ppv = NULL;
616 if (pUnkOuter)
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);
627 if (FAILED(hr))
628 return hr;
630 *ppv = (LPVOID)This;
632 return hr;