Fixed crashing in stopping parser.
[wine] / dlls / quartz / aviparse.c
1 /*
2  * Implements AVI Parser(Splitter).
3  *
4  *      FIXME - no seeking
5  *
6  * Copyright (C) Hidenori TAKESHIMA <hidenori@a2.ctktv.ne.jp>
7  *
8  * This library is free software; you can redistribute it and/or
9  * modify it under the terms of the GNU Lesser General Public
10  * License as published by the Free Software Foundation; either
11  * version 2.1 of the License, or (at your option) any later version.
12  *
13  * This library is distributed in the hope that it will be useful,
14  * but WITHOUT ANY WARRANTY; without even the implied warranty of
15  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
16  * Lesser General Public License for more details.
17  *
18  * You should have received a copy of the GNU Lesser General Public
19  * License along with this library; if not, write to the Free Software
20  * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
21  */
22
23 #include "config.h"
24
25 #include "windef.h"
26 #include "winbase.h"
27 #include "wingdi.h"
28 #include "winuser.h"
29 #include "mmsystem.h"
30 #include "vfw.h"
31 #include "winerror.h"
32 #include "strmif.h"
33 #include "control.h"
34 #include "vfwmsgs.h"
35 #include "amvideo.h"
36 #include "uuids.h"
37
38 #include "wine/debug.h"
39 WINE_DEFAULT_DEBUG_CHANNEL(quartz);
40
41 #include "quartz_private.h"
42 #include "parser.h"
43 #include "mtype.h"
44
45
46
47 static const WCHAR QUARTZ_AVIParser_Name[] =
48 { 'A','V','I',' ','S','p','l','i','t','t','e','r',0 };
49 static const WCHAR QUARTZ_AVIParserInPin_Name[] =
50 { 'I','n',0 };
51 static const WCHAR QUARTZ_AVIParserOutPin_Basename[] =
52 { 'S','t','r','e','a','m',0 };
53
54 #define WINE_QUARTZ_AVIPINNAME_MAX      64
55
56 /****************************************************************************
57  *
58  *      CAVIParseImpl
59  */
60
61
62 typedef struct CAVIParseImpl CAVIParseImpl;
63 typedef struct CAVIParseStream CAVIParseStream;
64
65 struct CAVIParseImpl
66 {
67         MainAVIHeader   avih;
68         CAVIParseStream*        pStreamsBuf;
69         DWORD   cIndexEntries;
70         AVIINDEXENTRY*  pIndexEntriesBuf;
71         WCHAR   wchWork[ WINE_QUARTZ_AVIPINNAME_MAX ];
72 };
73
74 struct CAVIParseStream
75 {
76         AVIStreamHeader strh;
77         DWORD   cbFmt;
78         BYTE*   pFmtBuf;
79         DWORD   cIndexEntries;
80         AVIINDEXENTRY*  pIndexEntries;
81         DWORD   cIndexCur;
82         REFERENCE_TIME  rtCur;
83         REFERENCE_TIME  rtInternal;
84         BOOL    bDataDiscontinuity;
85 };
86
87
88 static HRESULT CAVIParseImpl_ParseStreamList(
89         CParserImpl* pImpl, CAVIParseImpl* This, ULONG nStreamIndex,
90         LONGLONG llOfsTop, DWORD dwListLen, CAVIParseStream* pStream )
91 {
92         HRESULT hr;
93         LONGLONG        llOfs;
94         DWORD   dwChunkLength;
95
96         TRACE("search strh\n");
97         hr = RIFF_SearchChunk(
98                 pImpl, dwListLen,
99                 llOfsTop, PARSER_strh,
100                 &llOfs, &dwChunkLength );
101         if ( hr == S_OK )
102         {
103                 TRACE("strh has been detected\n");
104                 if ( dwChunkLength < sizeof(AVIStreamHeader) )
105                         hr = E_FAIL;
106                 else
107                         hr = IAsyncReader_SyncRead( pImpl->m_pReader,
108                                 llOfs, sizeof(AVIStreamHeader), (BYTE*)&pStream->strh );
109         }
110         if ( FAILED(hr) )
111                 return hr;
112         if ( hr != S_OK )
113                 return E_FAIL;
114
115         TRACE("search strf\n");
116         hr = RIFF_SearchChunk(
117                 pImpl, dwListLen,
118                 llOfsTop, PARSER_strf,
119                 &llOfs, &dwChunkLength );
120         if ( hr == S_OK && dwChunkLength > 0 )
121         {
122                 TRACE("strf has been detected\n");
123                 pStream->cbFmt = dwChunkLength;
124                 pStream->pFmtBuf = (BYTE*)QUARTZ_AllocMem( dwChunkLength );
125                 if ( pStream->pFmtBuf == NULL )
126                         hr = E_OUTOFMEMORY;
127                 else
128                         hr = IAsyncReader_SyncRead( pImpl->m_pReader,
129                                 llOfs, dwChunkLength, pStream->pFmtBuf );
130         }
131         if ( FAILED(hr) )
132                 return hr;
133
134         TRACE("search indx\n");
135         hr = RIFF_SearchChunk(
136                 pImpl, dwListLen,
137                 llOfsTop, PARSER_indx,
138                 &llOfs, &dwChunkLength );
139         if ( FAILED(hr) )
140                 return hr;
141         if ( hr == S_OK )
142         {
143                 FIXME( "'indx' has been detected - not implemented now!\n" );
144                 return E_FAIL;
145         }
146
147         return NOERROR;
148 }
149
150
151 static HRESULT CAVIParseImpl_InitParser( CParserImpl* pImpl, ULONG* pcStreams )
152 {
153         CAVIParseImpl*  This = NULL;
154         BYTE    riffhdr[12];
155         ULONG   i;
156         ULONG   nIndex;
157         HRESULT hr;
158         LONGLONG        llOfs_hdrl;
159         DWORD   dwLen_hdrl;
160         LONGLONG        llOfs;
161         DWORD   dwChunkId;
162         DWORD   dwChunkLength;
163         AVIINDEXENTRY*  pEntriesBuf = NULL;
164         ULONG   cEntries;
165         ULONG   cEntriesCur;
166
167         TRACE("(%p,%p)\n",pImpl,pcStreams);
168
169         hr = IAsyncReader_SyncRead( pImpl->m_pReader, 0, 12, riffhdr );
170         if ( FAILED(hr) )
171                 return hr;
172         if ( hr != S_OK )
173                 return E_FAIL;
174         if ( memcmp( &riffhdr[0], "RIFF", 4 ) != 0 ||
175                  memcmp( &riffhdr[8], "AVI ", 4 ) != 0 )
176                 return E_FAIL;
177
178         TRACE("it's AVI\n");
179
180         This = (CAVIParseImpl*)QUARTZ_AllocMem( sizeof(CAVIParseImpl) );
181         if ( This == NULL )
182                 return E_OUTOFMEMORY;
183         pImpl->m_pUserData = This;
184         ZeroMemory( This, sizeof(CAVIParseImpl) );
185         This->pStreamsBuf = NULL;
186         This->cIndexEntries = 0;
187         This->pIndexEntriesBuf = 0;
188
189         hr = RIFF_SearchList(
190                 pImpl, (DWORD)0xffffffff,
191                 PARSER_RIFF_OfsFirst, PARSER_hdrl,
192                 &llOfs_hdrl, &dwLen_hdrl );
193         if ( FAILED(hr) )
194                 return hr;
195         if ( hr != S_OK )
196                 return E_FAIL;
197
198         /* read 'avih' */
199         TRACE("read avih\n");
200         hr = RIFF_SearchChunk(
201                 pImpl, dwLen_hdrl,
202                 llOfs_hdrl, PARSER_avih,
203                 &llOfs, &dwChunkLength );
204         if ( FAILED(hr) )
205                 return hr;
206         if ( hr != S_OK )
207                 return E_FAIL;
208
209         if ( dwChunkLength > sizeof(MainAVIHeader) )
210                 dwChunkLength = sizeof(MainAVIHeader);
211         hr = IAsyncReader_SyncRead( pImpl->m_pReader, llOfs, dwChunkLength, (BYTE*)&(This->avih) );
212         if ( FAILED(hr) )
213                 return hr;
214         if ( hr != S_OK )
215                 return E_FAIL;
216         if ( This->avih.dwStreams == 0 )
217                 return E_FAIL;
218
219         /* initialize streams. */
220         This->pStreamsBuf = (CAVIParseStream*)QUARTZ_AllocMem(
221                 sizeof(CAVIParseStream) * This->avih.dwStreams );
222         if ( This->pStreamsBuf == NULL )
223                 return E_OUTOFMEMORY;
224         ZeroMemory( This->pStreamsBuf,
225                 sizeof(CAVIParseStream) * This->avih.dwStreams );
226
227         llOfs = llOfs_hdrl;
228         for ( nIndex = 0; nIndex < This->avih.dwStreams; nIndex++ )
229         {
230                 TRACE("search strl for stream %lu\n",nIndex);
231                 hr = RIFF_SearchList(
232                         pImpl,
233                         dwLen_hdrl, llOfs, PARSER_strl,
234                         &llOfs, &dwChunkLength );
235                 if ( FAILED(hr) )
236                         return hr;
237                 if ( hr != S_OK )
238                         return E_FAIL;
239
240                 /* read 'strl'. */
241                 hr = CAVIParseImpl_ParseStreamList(
242                         pImpl, This, nIndex,
243                         llOfs, dwChunkLength, &This->pStreamsBuf[nIndex] );
244
245                 if ( FAILED(hr) )
246                         return hr;
247                 if ( hr != S_OK )
248                         return E_FAIL;
249                 llOfs += dwChunkLength;
250         }
251
252         /* initialize idx1. */
253         TRACE("search idx1\n");
254         hr = RIFF_SearchChunk(
255                 pImpl, (DWORD)0xffffffff,
256                 PARSER_RIFF_OfsFirst, PARSER_idx1,
257                 &llOfs, &dwChunkLength );
258         if ( FAILED(hr) )
259                 return hr;
260         if ( hr == S_OK )
261         {
262                 /* read idx1. */
263                 This->cIndexEntries = dwChunkLength / sizeof(AVIINDEXENTRY);
264                 This->pIndexEntriesBuf = (AVIINDEXENTRY*)QUARTZ_AllocMem(
265                         sizeof(AVIINDEXENTRY) * This->cIndexEntries );
266                 if ( This->pIndexEntriesBuf == NULL )
267                         return E_OUTOFMEMORY;
268                 hr = IAsyncReader_SyncRead( pImpl->m_pReader, llOfs, sizeof(AVIINDEXENTRY) * This->cIndexEntries, (BYTE*)This->pIndexEntriesBuf );
269                 if ( FAILED(hr) )
270                         return hr;
271                 if ( hr != S_OK )
272                         return E_FAIL;
273
274                 pEntriesBuf = (AVIINDEXENTRY*)QUARTZ_AllocMem(
275                         sizeof(AVIINDEXENTRY) * This->cIndexEntries );
276                 if ( pEntriesBuf == NULL )
277                         return E_OUTOFMEMORY;
278                 cEntries = 0;
279                 for ( nIndex = 0; nIndex < This->avih.dwStreams; nIndex++ )
280                 {
281                         cEntriesCur = cEntries;
282                         dwChunkId = (((nIndex%10)+'0')<<8) | ((nIndex/10)+'0');
283                         for ( i = 0; i < This->cIndexEntries; i++ )
284                         {
285                                 if ( (This->pIndexEntriesBuf[i].ckid & 0xffff) == dwChunkId )
286                                         memcpy( &pEntriesBuf[cEntries++], &This->pIndexEntriesBuf[i], sizeof(AVIINDEXENTRY) );
287                         }
288                         This->pStreamsBuf[nIndex].pIndexEntries = &pEntriesBuf[cEntriesCur];
289                         This->pStreamsBuf[nIndex].cIndexEntries = cEntries - cEntriesCur;
290                         This->pStreamsBuf[nIndex].cIndexCur = 0;
291                         This->pStreamsBuf[nIndex].rtCur = 0;
292                         This->pStreamsBuf[nIndex].rtInternal = 0;
293                         TRACE("stream %lu - %lu entries\n",nIndex,This->pStreamsBuf[nIndex].cIndexEntries);
294                         This->pStreamsBuf[nIndex].bDataDiscontinuity = TRUE;
295                 }
296                 QUARTZ_FreeMem(This->pIndexEntriesBuf);
297                 This->pIndexEntriesBuf = pEntriesBuf;
298
299                 This->avih.dwSuggestedBufferSize = 0;
300                 for ( i = 0; i < This->cIndexEntries; i++ )
301                 {
302                         if ( This->avih.dwSuggestedBufferSize < This->pIndexEntriesBuf[i].dwChunkLength )
303                                 This->avih.dwSuggestedBufferSize = This->pIndexEntriesBuf[i].dwChunkLength;
304                 }
305         }
306         else
307         {
308                 return E_FAIL;
309         }
310
311         if ( This->avih.dwStreams > 100 )
312                 return E_FAIL;
313
314         *pcStreams = This->avih.dwStreams;
315
316         return NOERROR;
317 }
318
319 static HRESULT CAVIParseImpl_UninitParser( CParserImpl* pImpl )
320 {
321         CAVIParseImpl*  This = (CAVIParseImpl*)pImpl->m_pUserData;
322         ULONG   nIndex;
323
324         TRACE("(%p)\n",This);
325
326         if ( This == NULL )
327                 return NOERROR;
328
329         /* destruct */
330         if ( This->pStreamsBuf != NULL )
331         {
332                 for ( nIndex = 0; nIndex < This->avih.dwStreams; nIndex++ )
333                 {
334                         /* release this stream */
335                         if ( This->pStreamsBuf[nIndex].pFmtBuf != NULL )
336                                 QUARTZ_FreeMem(This->pStreamsBuf[nIndex].pFmtBuf);
337                 }
338                 QUARTZ_FreeMem( This->pStreamsBuf );
339                 This->pStreamsBuf = NULL;
340         }
341
342         if ( This->pIndexEntriesBuf != NULL )
343         {
344                 QUARTZ_FreeMem( This->pIndexEntriesBuf );
345                 This->pIndexEntriesBuf = NULL;
346         }
347
348         QUARTZ_FreeMem( This );
349         pImpl->m_pUserData = NULL;
350
351         return NOERROR;
352 }
353
354 static LPCWSTR CAVIParseImpl_GetOutPinName( CParserImpl* pImpl, ULONG nStreamIndex )
355 {
356         CAVIParseImpl*  This = (CAVIParseImpl*)pImpl->m_pUserData;
357         int wlen;
358
359         TRACE("(%p,%lu)\n",This,nStreamIndex);
360
361         if ( This == NULL || nStreamIndex >= This->avih.dwStreams )
362                 return NULL;
363
364         wlen = lstrlenW(QUARTZ_AVIParserOutPin_Basename);
365         memcpy( This->wchWork, QUARTZ_AVIParserOutPin_Basename, sizeof(WCHAR)*wlen );
366         This->wchWork[ wlen ] = (nStreamIndex/10) + '0';
367         This->wchWork[ wlen+1 ] = (nStreamIndex%10) + '0';
368         This->wchWork[ wlen+2 ] = 0;
369
370         return This->wchWork;
371 }
372
373 static HRESULT CAVIParseImpl_GetStreamType( CParserImpl* pImpl, ULONG nStreamIndex, AM_MEDIA_TYPE* pmt )
374 {
375         CAVIParseImpl*  This = (CAVIParseImpl*)pImpl->m_pUserData;
376         VIDEOINFOHEADER*        pvi;
377         BITMAPINFOHEADER*       pbi;
378         WAVEFORMATEX*   pwfx;
379         DWORD   cbFmt;
380         DWORD   cb;
381         HRESULT hr;
382
383         TRACE("(%p,%lu,%p)\n",This,nStreamIndex,pmt);
384
385         if ( This == NULL )
386                 return E_UNEXPECTED;
387         if ( nStreamIndex >= This->avih.dwStreams )
388                 return E_INVALIDARG;
389
390         cbFmt = This->pStreamsBuf[nStreamIndex].cbFmt;
391
392         ZeroMemory( pmt, sizeof(AM_MEDIA_TYPE) );
393         switch ( This->pStreamsBuf[nStreamIndex].strh.fccType )
394         {
395         case PARSER_vids:
396                 pbi = (BITMAPINFOHEADER*)This->pStreamsBuf[nStreamIndex].pFmtBuf;
397                 if ( pbi == NULL || cbFmt < sizeof(BITMAPINFOHEADER) )
398                         goto unknown_format;
399
400                 memcpy( &pmt->majortype, &MEDIATYPE_Video, sizeof(GUID) );
401                 hr = QUARTZ_MediaSubType_FromBitmap( &pmt->subtype, pbi );
402                 if ( FAILED(hr) )
403                         goto unknown_format;
404                 if ( hr != S_OK )
405                         QUARTZ_MediaSubType_FromFourCC( &pmt->subtype, (DWORD)pbi->biCompression );
406
407                 pmt->bFixedSizeSamples = QUARTZ_BitmapHasFixedSample( pbi ) ? 1 : 0;
408                 pmt->bTemporalCompression = 0; /* FIXME - 1 if inter-frame compression is used */
409                 pmt->lSampleSize = ( pbi->biCompression == 0 ) ? DIBSIZE(*pbi) : pbi->biSizeImage;
410                 memcpy( &pmt->formattype, &FORMAT_VideoInfo, sizeof(GUID) );
411
412                 cb = sizeof(VIDEOINFOHEADER) + cbFmt;
413                 pmt->pbFormat = (BYTE*)CoTaskMemAlloc( cb );
414                 if ( pmt->pbFormat == NULL )
415                         return E_OUTOFMEMORY;
416                 ZeroMemory( pmt->pbFormat, cb );
417                 pvi = (VIDEOINFOHEADER*)pmt->pbFormat;
418                 pmt->cbFormat = cb;
419                 memcpy( &pvi->bmiHeader, pbi, cbFmt );
420                 break;
421         case PARSER_auds:
422                 pwfx = (WAVEFORMATEX*)This->pStreamsBuf[nStreamIndex].pFmtBuf;
423                 if ( pwfx == NULL || cbFmt < (sizeof(WAVEFORMATEX)-2) )
424                         goto unknown_format;
425
426                 memcpy( &pmt->majortype, &MEDIATYPE_Audio, sizeof(GUID) );
427                 QUARTZ_MediaSubType_FromFourCC( &pmt->subtype, (DWORD)pwfx->wFormatTag );
428                 pmt->bFixedSizeSamples = 1;
429                 pmt->bTemporalCompression = 0;
430                 pmt->lSampleSize = pwfx->nBlockAlign;
431                 memcpy( &pmt->formattype, &FORMAT_WaveFormatEx, sizeof(GUID) );
432                 pmt->pUnk = NULL;
433
434                 cb = ( cbFmt < sizeof(WAVEFORMATEX) ) ? sizeof(WAVEFORMATEX) : cbFmt;
435                 pmt->pbFormat = (BYTE*)CoTaskMemAlloc( cb );
436                 if ( pmt->pbFormat == NULL )
437                         return E_OUTOFMEMORY;
438                 ZeroMemory( pmt->pbFormat, cb );
439                 pmt->cbFormat = cbFmt;
440                 memcpy( pmt->pbFormat, pwfx, cbFmt );
441                 break;
442         case PARSER_mids:
443                 /* FIXME? */
444                 memcpy( &pmt->majortype, &MEDIATYPE_Midi, sizeof(GUID) );
445                 memcpy( &pmt->subtype, &MEDIASUBTYPE_NULL, sizeof(GUID) );
446                 pmt->bFixedSizeSamples = 0;
447                 pmt->bTemporalCompression = 0;
448                 pmt->lSampleSize = 1;
449                 memcpy( &pmt->formattype, &FORMAT_None, sizeof(GUID) );
450                 pmt->pUnk = NULL;
451                 pmt->cbFormat = 0;
452                 pmt->pbFormat = NULL;
453                 break;
454         case PARSER_txts:
455                 /* FIXME? */
456                 memcpy( &pmt->majortype, &MEDIATYPE_Text, sizeof(GUID) );
457                 memcpy( &pmt->subtype, &MEDIASUBTYPE_NULL, sizeof(GUID) );
458                 pmt->bFixedSizeSamples = 0;
459                 pmt->bTemporalCompression = 0;
460                 pmt->lSampleSize = 1;
461                 memcpy( &pmt->formattype, &FORMAT_None, sizeof(GUID) );
462                 pmt->pUnk = NULL;
463                 pmt->cbFormat = 0;
464                 pmt->pbFormat = NULL;
465                 break;
466         default:
467                 goto unknown_format;
468         }
469
470         return NOERROR;
471
472 unknown_format:;
473         FIXME( "(%p) unsupported stream type %c%c%c%c\n",This,
474                         (int)((This->pStreamsBuf[nStreamIndex].strh.fccType>> 0)&0xff),
475                         (int)((This->pStreamsBuf[nStreamIndex].strh.fccType>> 8)&0xff),
476                         (int)((This->pStreamsBuf[nStreamIndex].strh.fccType>>16)&0xff),
477                         (int)((This->pStreamsBuf[nStreamIndex].strh.fccType>>24)&0xff) );
478
479         memcpy( &pmt->majortype, &MEDIATYPE_NULL, sizeof(GUID) );
480         memcpy( &pmt->subtype, &MEDIASUBTYPE_NULL, sizeof(GUID) );
481         pmt->bFixedSizeSamples = 0;
482         pmt->bTemporalCompression = 0;
483         pmt->lSampleSize = 1;
484         memcpy( &pmt->formattype, &FORMAT_None, sizeof(GUID) );
485         pmt->pUnk = NULL;
486         pmt->cbFormat = 0;
487         pmt->pbFormat = NULL;
488
489         return NOERROR;
490 }
491
492 static HRESULT CAVIParseImpl_CheckStreamType( CParserImpl* pImpl, ULONG nStreamIndex, const AM_MEDIA_TYPE* pmt )
493 {
494         CAVIParseImpl*  This = (CAVIParseImpl*)pImpl->m_pUserData;
495         HRESULT hr;
496         AM_MEDIA_TYPE   mt;
497         VIDEOINFOHEADER*        pvi;
498         VIDEOINFOHEADER*        pviCheck;
499         WAVEFORMATEX*   pwfx;
500         WAVEFORMATEX*   pwfxCheck;
501
502         TRACE("(%p,%lu,%p)\n",This,nStreamIndex,pmt);
503
504         hr = CAVIParseImpl_GetStreamType( pImpl, nStreamIndex, &mt );
505         if ( FAILED(hr) )
506                 return hr;
507
508         TRACE("check GUIDs - %s,%s\n",debugstr_guid(&pmt->majortype),debugstr_guid(&pmt->subtype));
509         if ( !IsEqualGUID( &pmt->majortype, &mt.majortype ) ||
510                  !IsEqualGUID( &pmt->subtype, &mt.subtype ) ||
511                  !IsEqualGUID( &pmt->formattype, &mt.formattype ) )
512         {
513                 hr = E_FAIL;
514                 goto end;
515         }
516
517         TRACE("check format\n");
518         hr = S_OK;
519         switch ( This->pStreamsBuf[nStreamIndex].strh.fccType )
520         {
521         case PARSER_vids:
522                 TRACE("check vids\n");
523                 pvi = (VIDEOINFOHEADER*)mt.pbFormat;
524                 pviCheck = (VIDEOINFOHEADER*)pmt->pbFormat;
525                 if ( pvi == NULL || pviCheck == NULL || pmt->cbFormat < sizeof(VIDEOINFOHEADER) )
526                         hr = E_FAIL;
527                 if ( pvi->bmiHeader.biWidth != pviCheck->bmiHeader.biWidth ||
528                          pvi->bmiHeader.biHeight != pviCheck->bmiHeader.biHeight ||
529                          pvi->bmiHeader.biPlanes != pviCheck->bmiHeader.biPlanes ||
530                          pvi->bmiHeader.biBitCount != pviCheck->bmiHeader.biBitCount ||
531                          pvi->bmiHeader.biCompression != pviCheck->bmiHeader.biCompression ||
532                          pvi->bmiHeader.biClrUsed != pviCheck->bmiHeader.biClrUsed )
533                         hr = E_FAIL;
534                 break;
535         case PARSER_auds:
536                 TRACE("check auds\n");
537                 pwfx = (WAVEFORMATEX*)mt.pbFormat;
538                 pwfxCheck = (WAVEFORMATEX*)pmt->pbFormat;
539                 if ( pwfx == NULL || pwfxCheck == NULL || pmt->cbFormat < (sizeof(WAVEFORMATEX)-2) )
540                         hr = E_FAIL;
541                 if ( pwfx->wFormatTag != pwfxCheck->wFormatTag ||
542                          pwfx->nBlockAlign != pwfxCheck->nBlockAlign ||
543                          pwfx->wBitsPerSample != pwfxCheck->wBitsPerSample ||
544                          pwfx->nChannels != pwfxCheck->nChannels ||
545                          pwfx->nSamplesPerSec != pwfxCheck->nSamplesPerSec )
546                         hr = E_FAIL;
547                 break;
548         case PARSER_mids:
549         case PARSER_txts:
550                 break;
551         default:
552                 break;
553         }
554 end:
555         QUARTZ_MediaType_Free( &mt );
556
557         TRACE("%08lx\n",hr);
558
559         return hr;
560 }
561
562 static HRESULT CAVIParseImpl_GetAllocProp( CParserImpl* pImpl, ALLOCATOR_PROPERTIES* pReqProp )
563 {
564         CAVIParseImpl*  This = (CAVIParseImpl*)pImpl->m_pUserData;
565
566         TRACE("(%p,%p)\n",This,pReqProp);
567         if ( This == NULL )
568                 return E_UNEXPECTED;
569
570         ZeroMemory( pReqProp, sizeof(ALLOCATOR_PROPERTIES) );
571         pReqProp->cBuffers = This->avih.dwStreams;
572         pReqProp->cbBuffer = This->avih.dwSuggestedBufferSize;
573
574         return NOERROR;
575 }
576
577 static HRESULT CAVIParseImpl_GetNextRequest( CParserImpl* pImpl, ULONG* pnStreamIndex, LONGLONG* pllStart, LONG* plLength, REFERENCE_TIME* prtStart, REFERENCE_TIME* prtStop, DWORD* pdwSampleFlags )
578 {
579         CAVIParseImpl*  This = (CAVIParseImpl*)pImpl->m_pUserData;
580         REFERENCE_TIME  rtNext;
581         DWORD   nIndexNext;
582         DWORD   nIndex;
583         CAVIParseStream*        pStream;
584         const WAVEFORMATEX*     pwfx;
585
586         TRACE("(%p)\n",This);
587
588         if ( This == NULL )
589                 return E_UNEXPECTED;
590         *pdwSampleFlags = AM_SAMPLE_SPLICEPOINT;
591
592         nIndexNext = This->avih.dwStreams;
593         rtNext = ((REFERENCE_TIME)0x7fffffff<<32)|((REFERENCE_TIME)0xffffffff);
594         for ( nIndex = 0; nIndex < This->avih.dwStreams; nIndex++ )
595         {
596                 TRACE("stream %lu - %lu,%lu\n",nIndex,(unsigned long)(This->pStreamsBuf[nIndex].rtCur*1000/QUARTZ_TIMEUNITS),This->pStreamsBuf[nIndex].cIndexCur);
597                 if ( rtNext > This->pStreamsBuf[nIndex].rtCur &&
598                          This->pStreamsBuf[nIndex].cIndexCur < This->pStreamsBuf[nIndex].cIndexEntries )
599                 {
600                         nIndexNext = nIndex;
601                         rtNext = This->pStreamsBuf[nIndex].rtCur;
602                 }
603         }
604         if ( nIndexNext >= This->avih.dwStreams )
605                 return S_FALSE;
606
607         if ( This->pIndexEntriesBuf != NULL )
608         {
609                 pStream = &This->pStreamsBuf[nIndexNext];
610                 *pnStreamIndex = nIndexNext;
611                 *pllStart = (LONGLONG)pStream->pIndexEntries[pStream->cIndexCur].dwChunkOffset + 8;
612                 *plLength = (LONG)pStream->pIndexEntries[pStream->cIndexCur].dwChunkLength;
613                 *prtStart = rtNext;
614                 *prtStop = rtNext;
615                 /* FIXME - is this frame keyframe?? */
616                 *pdwSampleFlags = AM_SAMPLE_SPLICEPOINT;
617                 if ( pStream->bDataDiscontinuity )
618                 {
619                         *pdwSampleFlags |= AM_SAMPLE_DATADISCONTINUITY;
620                         pStream->bDataDiscontinuity = FALSE;
621                 }
622
623                 switch ( pStream->strh.fccType )
624                 {
625                 case PARSER_vids:
626                         TRACE("vids\n");
627                         pStream->rtInternal ++;
628                         rtNext = pStream->rtInternal * (REFERENCE_TIME)QUARTZ_TIMEUNITS * (REFERENCE_TIME)pStream->strh.dwScale / (REFERENCE_TIME)pStream->strh.dwRate;
629                         /* FIXME - handle AVIPALCHANGE */
630                         break;
631                 case PARSER_auds:
632                         TRACE("auds\n");
633                         pwfx = (const WAVEFORMATEX*)pStream->pFmtBuf;
634                         if ( pwfx != NULL && pStream->cbFmt >= (sizeof(WAVEFORMATEX)-2) )
635                         {
636                                 pStream->rtInternal += (REFERENCE_TIME)*plLength;
637                                 rtNext = pStream->rtInternal * (REFERENCE_TIME)QUARTZ_TIMEUNITS / (REFERENCE_TIME)pwfx->nAvgBytesPerSec;
638                         }
639                         else
640                         {
641                                 pStream->rtInternal += (REFERENCE_TIME)(*plLength);
642                                 rtNext = pStream->rtInternal * (REFERENCE_TIME)QUARTZ_TIMEUNITS * (REFERENCE_TIME)pStream->strh.dwScale / ((REFERENCE_TIME)pStream->strh.dwSampleSize * (REFERENCE_TIME)pStream->strh.dwRate);
643                         }
644                         break;
645                 case PARSER_mids:
646                 case PARSER_txts:
647                 default:
648                         pStream->rtInternal += (REFERENCE_TIME)(*plLength);
649                         rtNext = pStream->rtInternal * (REFERENCE_TIME)QUARTZ_TIMEUNITS * (REFERENCE_TIME)pStream->strh.dwScale / ((REFERENCE_TIME)pStream->strh.dwSampleSize * (REFERENCE_TIME)pStream->strh.dwRate);
650                         break;
651                 }
652                 pStream->cIndexCur ++;
653                 pStream->rtCur = rtNext;
654                 *prtStop = rtNext;
655         }
656         else
657         {
658                 ERR( "no idx1\n" );
659                 return E_NOTIMPL;
660         }
661
662         TRACE("return %lu / %ld-%ld / %lu-%lu\n",
663                 *pnStreamIndex,(long)*pllStart,*plLength,
664                 (unsigned long)((*prtStart)*1000/QUARTZ_TIMEUNITS),
665                 (unsigned long)((*prtStop)*1000/QUARTZ_TIMEUNITS));
666
667         return NOERROR;
668 }
669
670 static HRESULT CAVIParseImpl_ProcessSample( CParserImpl* pImpl, ULONG nStreamIndex, LONGLONG llStart, LONG lLength, IMediaSample* pSample )
671 {
672         CAVIParseImpl*  This = (CAVIParseImpl*)pImpl->m_pUserData;
673
674         TRACE("(%p,%lu,%ld,%ld,%p)\n",This,nStreamIndex,(long)llStart,lLength,pSample);
675
676         if ( This == NULL )
677                 return E_UNEXPECTED;
678
679         return NOERROR;
680 }
681
682
683
684
685 static const struct ParserHandlers CAVIParseImpl_Handlers =
686 {
687         CAVIParseImpl_InitParser,
688         CAVIParseImpl_UninitParser,
689         CAVIParseImpl_GetOutPinName,
690         CAVIParseImpl_GetStreamType,
691         CAVIParseImpl_CheckStreamType,
692         CAVIParseImpl_GetAllocProp,
693         CAVIParseImpl_GetNextRequest,
694         CAVIParseImpl_ProcessSample,
695
696         /* for IQualityControl */
697         NULL, /* pQualityNotify */
698
699         /* for seeking */
700         NULL, /* pGetSeekingCaps */
701         NULL, /* pIsTimeFormatSupported */
702         NULL, /* pGetCurPos */
703         NULL, /* pSetCurPos */
704         NULL, /* pGetDuration */
705         NULL, /* pGetStopPos */
706         NULL, /* pSetStopPos */
707         NULL, /* pGetPreroll */
708 };
709
710 HRESULT QUARTZ_CreateAVISplitter(IUnknown* punkOuter,void** ppobj)
711 {
712         return QUARTZ_CreateParser(
713                 punkOuter,ppobj,
714                 &CLSID_AviSplitter,
715                 QUARTZ_AVIParser_Name,
716                 QUARTZ_AVIParserInPin_Name,
717                 &CAVIParseImpl_Handlers );
718 }
719
720