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