2 * Implements AVI Parser(Splitter).
4 * hidenori@a2.ctktv.ne.jp
22 #include "debugtools.h"
23 DEFAULT_DEBUG_CHANNEL(quartz);
25 #include "quartz_private.h"
31 static const WCHAR QUARTZ_AVIParser_Name[] =
32 { 'A','V','I',' ','S','p','l','i','t','t','e','r',0 };
33 static const WCHAR QUARTZ_AVIParserInPin_Name[] =
35 static const WCHAR QUARTZ_AVIParserOutPin_Basename[] =
36 { 'S','t','r','e','a','m',0 };
38 #define WINE_QUARTZ_AVIPINNAME_MAX 64
40 /****************************************************************************
46 typedef struct CAVIParseImpl CAVIParseImpl;
47 typedef struct CAVIParseStream CAVIParseStream;
52 CAVIParseStream* pStreamsBuf;
54 AVIINDEXENTRY* pIndexEntriesBuf;
55 WCHAR wchWork[ WINE_QUARTZ_AVIPINNAME_MAX ];
58 struct CAVIParseStream
64 AVIINDEXENTRY* pIndexEntries;
67 REFERENCE_TIME rtInternal;
71 static HRESULT CAVIParseImpl_ParseStreamList(
72 CParserImpl* pImpl, CAVIParseImpl* This, ULONG nStreamIndex,
73 LONGLONG llOfsTop, DWORD dwListLen, CAVIParseStream* pStream )
79 TRACE("search strh\n");
80 hr = RIFF_SearchChunk(
82 llOfsTop, PARSER_strh,
83 &llOfs, &dwChunkLength );
86 TRACE("strh has been detected\n");
87 if ( dwChunkLength < sizeof(AVIStreamHeader) )
90 hr = IAsyncReader_SyncRead( pImpl->m_pReader,
91 llOfs, sizeof(AVIStreamHeader), (BYTE*)&pStream->strh );
98 TRACE("search strf\n");
99 hr = RIFF_SearchChunk(
101 llOfsTop, PARSER_strf,
102 &llOfs, &dwChunkLength );
103 if ( hr == S_OK && dwChunkLength > 0 )
105 TRACE("strf has been detected\n");
106 pStream->cbFmt = dwChunkLength;
107 pStream->pFmtBuf = (BYTE*)QUARTZ_AllocMem( dwChunkLength );
108 if ( pStream->pFmtBuf == NULL )
111 hr = IAsyncReader_SyncRead( pImpl->m_pReader,
112 llOfs, dwChunkLength, pStream->pFmtBuf );
117 TRACE("search indx\n");
118 hr = RIFF_SearchChunk(
120 llOfsTop, PARSER_indx,
121 &llOfs, &dwChunkLength );
126 FIXME( "'indx' has been detected - not implemented now!\n" );
134 static HRESULT CAVIParseImpl_InitParser( CParserImpl* pImpl, ULONG* pcStreams )
136 CAVIParseImpl* This = NULL;
146 AVIINDEXENTRY* pEntriesBuf = NULL;
150 TRACE("(%p,%p)\n",pImpl,pcStreams);
152 hr = IAsyncReader_SyncRead( pImpl->m_pReader, 0, 12, riffhdr );
157 if ( memcmp( &riffhdr[0], "RIFF", 4 ) != 0 ||
158 memcmp( &riffhdr[8], "AVI ", 4 ) != 0 )
163 This = (CAVIParseImpl*)QUARTZ_AllocMem( sizeof(CAVIParseImpl) );
165 return E_OUTOFMEMORY;
166 pImpl->m_pUserData = This;
167 ZeroMemory( This, sizeof(CAVIParseImpl) );
168 This->pStreamsBuf = NULL;
169 This->cIndexEntries = 0;
170 This->pIndexEntriesBuf = 0;
172 hr = RIFF_SearchList(
173 pImpl, (DWORD)0xffffffff,
174 PARSER_RIFF_OfsFirst, PARSER_hdrl,
175 &llOfs_hdrl, &dwLen_hdrl );
182 TRACE("read avih\n");
183 hr = RIFF_SearchChunk(
185 llOfs_hdrl, PARSER_avih,
186 &llOfs, &dwChunkLength );
192 if ( dwChunkLength > sizeof(MainAVIHeader) )
193 dwChunkLength = sizeof(MainAVIHeader);
194 hr = IAsyncReader_SyncRead( pImpl->m_pReader, llOfs, dwChunkLength, (BYTE*)&(This->avih) );
199 if ( This->avih.dwStreams == 0 )
202 /* initialize streams. */
203 This->pStreamsBuf = (CAVIParseStream*)QUARTZ_AllocMem(
204 sizeof(CAVIParseStream) * This->avih.dwStreams );
205 if ( This->pStreamsBuf == NULL )
206 return E_OUTOFMEMORY;
207 ZeroMemory( This->pStreamsBuf,
208 sizeof(CAVIParseStream) * This->avih.dwStreams );
211 for ( nIndex = 0; nIndex < This->avih.dwStreams; nIndex++ )
213 TRACE("search strl for stream %lu\n",nIndex);
214 hr = RIFF_SearchList(
216 dwLen_hdrl, llOfs, PARSER_strl,
217 &llOfs, &dwChunkLength );
224 hr = CAVIParseImpl_ParseStreamList(
226 llOfs, dwChunkLength, &This->pStreamsBuf[nIndex] );
232 llOfs += dwChunkLength;
235 /* initialize idx1. */
236 TRACE("search idx1\n");
237 hr = RIFF_SearchChunk(
238 pImpl, (DWORD)0xffffffff,
239 PARSER_RIFF_OfsFirst, PARSER_idx1,
240 &llOfs, &dwChunkLength );
246 This->cIndexEntries = dwChunkLength / sizeof(AVIINDEXENTRY);
247 This->pIndexEntriesBuf = (AVIINDEXENTRY*)QUARTZ_AllocMem(
248 sizeof(AVIINDEXENTRY) * This->cIndexEntries );
249 if ( This->pIndexEntriesBuf == NULL )
250 return E_OUTOFMEMORY;
251 hr = IAsyncReader_SyncRead( pImpl->m_pReader, llOfs, sizeof(AVIINDEXENTRY) * This->cIndexEntries, (BYTE*)This->pIndexEntriesBuf );
257 pEntriesBuf = (AVIINDEXENTRY*)QUARTZ_AllocMem(
258 sizeof(AVIINDEXENTRY) * This->cIndexEntries );
259 if ( pEntriesBuf == NULL )
260 return E_OUTOFMEMORY;
262 for ( nIndex = 0; nIndex < This->avih.dwStreams; nIndex++ )
264 cEntriesCur = cEntries;
265 dwChunkId = (((nIndex%10)+'0')<<8) | ((nIndex/10)+'0');
266 for ( i = 0; i < This->cIndexEntries; i++ )
268 if ( (This->pIndexEntriesBuf[i].ckid & 0xffff) == dwChunkId )
269 memcpy( &pEntriesBuf[cEntries++], &This->pIndexEntriesBuf[i], sizeof(AVIINDEXENTRY) );
271 This->pStreamsBuf[nIndex].pIndexEntries = &pEntriesBuf[cEntriesCur];
272 This->pStreamsBuf[nIndex].cIndexEntries = cEntries - cEntriesCur;
273 This->pStreamsBuf[nIndex].cIndexCur = 0;
274 This->pStreamsBuf[nIndex].rtCur = 0;
275 This->pStreamsBuf[nIndex].rtInternal = 0;
276 TRACE("stream %lu - %lu entries\n",nIndex,This->pStreamsBuf[nIndex].cIndexEntries);
278 QUARTZ_FreeMem(This->pIndexEntriesBuf);
279 This->pIndexEntriesBuf = pEntriesBuf;
281 This->avih.dwSuggestedBufferSize = 0;
282 for ( i = 0; i < This->cIndexEntries; i++ )
284 if ( This->avih.dwSuggestedBufferSize < This->pIndexEntriesBuf[i].dwChunkLength )
285 This->avih.dwSuggestedBufferSize = This->pIndexEntriesBuf[i].dwChunkLength;
293 if ( This->avih.dwStreams > 100 )
296 *pcStreams = This->avih.dwStreams;
301 static HRESULT CAVIParseImpl_UninitParser( CParserImpl* pImpl )
303 CAVIParseImpl* This = (CAVIParseImpl*)pImpl->m_pUserData;
306 TRACE("(%p)\n",This);
312 if ( This->pStreamsBuf != NULL )
314 for ( nIndex = 0; nIndex < This->avih.dwStreams; nIndex++ )
316 /* release this stream */
317 if ( This->pStreamsBuf[nIndex].pFmtBuf != NULL )
318 QUARTZ_FreeMem(This->pStreamsBuf[nIndex].pFmtBuf);
320 QUARTZ_FreeMem( This->pStreamsBuf );
321 This->pStreamsBuf = NULL;
324 if ( This->pIndexEntriesBuf != NULL )
326 QUARTZ_FreeMem( This->pIndexEntriesBuf );
327 This->pIndexEntriesBuf = NULL;
330 QUARTZ_FreeMem( This );
331 pImpl->m_pUserData = NULL;
336 static LPCWSTR CAVIParseImpl_GetOutPinName( CParserImpl* pImpl, ULONG nStreamIndex )
338 CAVIParseImpl* This = (CAVIParseImpl*)pImpl->m_pUserData;
341 TRACE("(%p,%lu)\n",This,nStreamIndex);
343 if ( This == NULL || nStreamIndex >= This->avih.dwStreams )
346 wlen = lstrlenW(QUARTZ_AVIParserOutPin_Basename);
347 memcpy( This->wchWork, QUARTZ_AVIParserOutPin_Basename, sizeof(WCHAR)*wlen );
348 This->wchWork[ wlen ] = (nStreamIndex/10) + '0';
349 This->wchWork[ wlen+1 ] = (nStreamIndex%10) + '0';
350 This->wchWork[ wlen+2 ] = 0;
352 return This->wchWork;
355 static HRESULT CAVIParseImpl_GetStreamType( CParserImpl* pImpl, ULONG nStreamIndex, AM_MEDIA_TYPE* pmt )
357 CAVIParseImpl* This = (CAVIParseImpl*)pImpl->m_pUserData;
358 VIDEOINFOHEADER* pvi;
359 BITMAPINFOHEADER* pbi;
365 TRACE("(%p,%lu,%p)\n",This,nStreamIndex,pmt);
369 if ( nStreamIndex >= This->avih.dwStreams )
372 cbFmt = This->pStreamsBuf[nStreamIndex].cbFmt;
374 ZeroMemory( pmt, sizeof(AM_MEDIA_TYPE) );
375 switch ( This->pStreamsBuf[nStreamIndex].strh.fccType )
378 pbi = (BITMAPINFOHEADER*)This->pStreamsBuf[nStreamIndex].pFmtBuf;
379 if ( pbi == NULL || cbFmt < sizeof(BITMAPINFOHEADER) )
382 memcpy( &pmt->majortype, &MEDIATYPE_Video, sizeof(GUID) );
383 hr = QUARTZ_MediaSubType_FromBitmap( &pmt->subtype, pbi );
387 QUARTZ_MediaSubType_FromFourCC( &pmt->subtype, (DWORD)pbi->biCompression );
389 pmt->bFixedSizeSamples = QUARTZ_BitmapHasFixedSample( pbi ) ? 1 : 0;
390 pmt->bTemporalCompression = 0; /* FIXME - 1 if inter-frame compression is used */
391 pmt->lSampleSize = ( pbi->biCompression == 0 ) ? DIBSIZE(*pbi) : pbi->biSizeImage;
392 memcpy( &pmt->formattype, &FORMAT_VideoInfo, sizeof(GUID) );
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;
401 memcpy( &pvi->bmiHeader, pbi, cbFmt );
404 pwfx = (WAVEFORMATEX*)This->pStreamsBuf[nStreamIndex].pFmtBuf;
405 if ( pwfx == NULL || cbFmt < (sizeof(WAVEFORMATEX)-2) )
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) );
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 );
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) );
434 pmt->pbFormat = NULL;
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) );
446 pmt->pbFormat = NULL;
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) );
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) );
469 pmt->pbFormat = NULL;
474 static HRESULT CAVIParseImpl_CheckStreamType( CParserImpl* pImpl, ULONG nStreamIndex, const AM_MEDIA_TYPE* pmt )
476 CAVIParseImpl* This = (CAVIParseImpl*)pImpl->m_pUserData;
479 VIDEOINFOHEADER* pvi;
480 VIDEOINFOHEADER* pviCheck;
482 WAVEFORMATEX* pwfxCheck;
484 TRACE("(%p,%lu,%p)\n",This,nStreamIndex,pmt);
486 hr = CAVIParseImpl_GetStreamType( pImpl, nStreamIndex, &mt );
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 ) )
499 TRACE("check format\n");
501 switch ( This->pStreamsBuf[nStreamIndex].strh.fccType )
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) )
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 )
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) )
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 )
537 QUARTZ_MediaType_Free( &mt );
544 static HRESULT CAVIParseImpl_GetAllocProp( CParserImpl* pImpl, ALLOCATOR_PROPERTIES* pReqProp )
546 CAVIParseImpl* This = (CAVIParseImpl*)pImpl->m_pUserData;
548 TRACE("(%p,%p)\n",This,pReqProp);
552 ZeroMemory( pReqProp, sizeof(ALLOCATOR_PROPERTIES) );
553 pReqProp->cBuffers = This->avih.dwStreams;
554 pReqProp->cbBuffer = This->avih.dwSuggestedBufferSize;
559 static HRESULT CAVIParseImpl_GetNextRequest( CParserImpl* pImpl, ULONG* pnStreamIndex, LONGLONG* pllStart, LONG* plLength, REFERENCE_TIME* prtStart, REFERENCE_TIME* prtStop )
561 CAVIParseImpl* This = (CAVIParseImpl*)pImpl->m_pUserData;
562 REFERENCE_TIME rtNext;
565 CAVIParseStream* pStream;
566 const WAVEFORMATEX* pwfx;
571 TRACE("(%p)\n",This);
573 nIndexNext = This->avih.dwStreams;
574 rtNext = ((REFERENCE_TIME)0x7fffffff<<32)|((REFERENCE_TIME)0xffffffff);
575 for ( nIndex = 0; nIndex < This->avih.dwStreams; nIndex++ )
577 TRACE("stream %lu - %lu,%lu\n",nIndex,(unsigned long)(This->pStreamsBuf[nIndex].rtCur*1000/QUARTZ_TIMEUNITS),This->pStreamsBuf[nIndex].cIndexCur);
578 if ( rtNext > This->pStreamsBuf[nIndex].rtCur &&
579 This->pStreamsBuf[nIndex].cIndexCur < This->pStreamsBuf[nIndex].cIndexEntries )
582 rtNext = This->pStreamsBuf[nIndex].rtCur;
585 if ( nIndexNext >= This->avih.dwStreams )
588 if ( This->pIndexEntriesBuf != NULL )
590 pStream = &This->pStreamsBuf[nIndexNext];
591 *pnStreamIndex = nIndexNext;
592 *pllStart = (LONGLONG)pStream->pIndexEntries[pStream->cIndexCur].dwChunkOffset + 8;
593 *plLength = (LONG)pStream->pIndexEntries[pStream->cIndexCur].dwChunkLength;
597 switch ( pStream->strh.fccType )
601 pStream->rtInternal ++;
602 rtNext = pStream->rtInternal * (REFERENCE_TIME)QUARTZ_TIMEUNITS * (REFERENCE_TIME)pStream->strh.dwScale / (REFERENCE_TIME)pStream->strh.dwRate;
603 /* FIXME - handle AVIPALCHANGE */
607 pwfx = (const WAVEFORMATEX*)pStream->pFmtBuf;
608 if ( pwfx != NULL && pStream->cbFmt >= (sizeof(WAVEFORMATEX)-2) )
610 pStream->rtInternal += (REFERENCE_TIME)*plLength;
611 rtNext = pStream->rtInternal * (REFERENCE_TIME)QUARTZ_TIMEUNITS / (REFERENCE_TIME)pwfx->nAvgBytesPerSec;
615 pStream->rtInternal += (REFERENCE_TIME)(*plLength);
616 rtNext = pStream->rtInternal * (REFERENCE_TIME)QUARTZ_TIMEUNITS * (REFERENCE_TIME)pStream->strh.dwScale / ((REFERENCE_TIME)pStream->strh.dwSampleSize * (REFERENCE_TIME)pStream->strh.dwRate);
622 pStream->rtInternal += (REFERENCE_TIME)(*plLength);
623 rtNext = pStream->rtInternal * (REFERENCE_TIME)QUARTZ_TIMEUNITS * (REFERENCE_TIME)pStream->strh.dwScale / ((REFERENCE_TIME)pStream->strh.dwSampleSize * (REFERENCE_TIME)pStream->strh.dwRate);
626 pStream->cIndexCur ++;
627 pStream->rtCur = rtNext;
636 TRACE("return %lu / %ld-%ld / %lu-%lu\n",
637 *pnStreamIndex,(long)*pllStart,*plLength,
638 (unsigned long)((*prtStart)*1000/QUARTZ_TIMEUNITS),
639 (unsigned long)((*prtStop)*1000/QUARTZ_TIMEUNITS));
644 static HRESULT CAVIParseImpl_ProcessSample( CParserImpl* pImpl, ULONG nStreamIndex, LONGLONG llStart, LONG lLength, IMediaSample* pSample )
646 CAVIParseImpl* This = (CAVIParseImpl*)pImpl->m_pUserData;
648 TRACE("(%p,%lu,%ld,%ld,%p)\n",This,nStreamIndex,(long)llStart,lLength,pSample);
659 static const struct ParserHandlers CAVIParseImpl_Handlers =
661 CAVIParseImpl_InitParser,
662 CAVIParseImpl_UninitParser,
663 CAVIParseImpl_GetOutPinName,
664 CAVIParseImpl_GetStreamType,
665 CAVIParseImpl_CheckStreamType,
666 CAVIParseImpl_GetAllocProp,
667 CAVIParseImpl_GetNextRequest,
668 CAVIParseImpl_ProcessSample,
670 /* for IQualityControl */
671 NULL, /* pQualityNotify */
674 NULL, /* pGetSeekingCaps */
675 NULL, /* pIsTimeFormatSupported */
676 NULL, /* pGetCurPos */
677 NULL, /* pSetCurPos */
678 NULL, /* pGetDuration */
679 NULL, /* pSetDuration */
680 NULL, /* pGetStopPos */
681 NULL, /* pSetStopPos */
682 NULL, /* pGetPreroll */
683 NULL, /* pSetPreroll */
686 HRESULT QUARTZ_CreateAVISplitter(IUnknown* punkOuter,void** ppobj)
688 return QUARTZ_CreateParser(
691 QUARTZ_AVIParser_Name,
692 QUARTZ_AVIParserInPin_Name,
693 &CAVIParseImpl_Handlers );