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