2 * Implements AVI Parser(Splitter).
4 * Copyright (C) Hidenori TAKESHIMA <hidenori@a2.ctktv.ne.jp>
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.
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.
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
36 #include "wine/debug.h"
37 WINE_DEFAULT_DEBUG_CHANNEL(quartz);
39 #include "quartz_private.h"
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[] =
49 static const WCHAR QUARTZ_AVIParserOutPin_Basename[] =
50 { 'S','t','r','e','a','m',0 };
52 #define WINE_QUARTZ_AVIPINNAME_MAX 64
54 /****************************************************************************
60 typedef struct CAVIParseImpl CAVIParseImpl;
61 typedef struct CAVIParseStream CAVIParseStream;
66 CAVIParseStream* pStreamsBuf;
68 AVIINDEXENTRY* pIndexEntriesBuf;
69 WCHAR wchWork[ WINE_QUARTZ_AVIPINNAME_MAX ];
72 struct CAVIParseStream
78 AVIINDEXENTRY* pIndexEntries;
81 REFERENCE_TIME rtInternal;
85 static HRESULT CAVIParseImpl_ParseStreamList(
86 CParserImpl* pImpl, CAVIParseImpl* This, ULONG nStreamIndex,
87 LONGLONG llOfsTop, DWORD dwListLen, CAVIParseStream* pStream )
93 TRACE("search strh\n");
94 hr = RIFF_SearchChunk(
96 llOfsTop, PARSER_strh,
97 &llOfs, &dwChunkLength );
100 TRACE("strh has been detected\n");
101 if ( dwChunkLength < sizeof(AVIStreamHeader) )
104 hr = IAsyncReader_SyncRead( pImpl->m_pReader,
105 llOfs, sizeof(AVIStreamHeader), (BYTE*)&pStream->strh );
112 TRACE("search strf\n");
113 hr = RIFF_SearchChunk(
115 llOfsTop, PARSER_strf,
116 &llOfs, &dwChunkLength );
117 if ( hr == S_OK && dwChunkLength > 0 )
119 TRACE("strf has been detected\n");
120 pStream->cbFmt = dwChunkLength;
121 pStream->pFmtBuf = (BYTE*)QUARTZ_AllocMem( dwChunkLength );
122 if ( pStream->pFmtBuf == NULL )
125 hr = IAsyncReader_SyncRead( pImpl->m_pReader,
126 llOfs, dwChunkLength, pStream->pFmtBuf );
131 TRACE("search indx\n");
132 hr = RIFF_SearchChunk(
134 llOfsTop, PARSER_indx,
135 &llOfs, &dwChunkLength );
140 FIXME( "'indx' has been detected - not implemented now!\n" );
148 static HRESULT CAVIParseImpl_InitParser( CParserImpl* pImpl, ULONG* pcStreams )
150 CAVIParseImpl* This = NULL;
160 AVIINDEXENTRY* pEntriesBuf = NULL;
164 TRACE("(%p,%p)\n",pImpl,pcStreams);
166 hr = IAsyncReader_SyncRead( pImpl->m_pReader, 0, 12, riffhdr );
171 if ( memcmp( &riffhdr[0], "RIFF", 4 ) != 0 ||
172 memcmp( &riffhdr[8], "AVI ", 4 ) != 0 )
177 This = (CAVIParseImpl*)QUARTZ_AllocMem( sizeof(CAVIParseImpl) );
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;
186 hr = RIFF_SearchList(
187 pImpl, (DWORD)0xffffffff,
188 PARSER_RIFF_OfsFirst, PARSER_hdrl,
189 &llOfs_hdrl, &dwLen_hdrl );
196 TRACE("read avih\n");
197 hr = RIFF_SearchChunk(
199 llOfs_hdrl, PARSER_avih,
200 &llOfs, &dwChunkLength );
206 if ( dwChunkLength > sizeof(MainAVIHeader) )
207 dwChunkLength = sizeof(MainAVIHeader);
208 hr = IAsyncReader_SyncRead( pImpl->m_pReader, llOfs, dwChunkLength, (BYTE*)&(This->avih) );
213 if ( This->avih.dwStreams == 0 )
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 );
225 for ( nIndex = 0; nIndex < This->avih.dwStreams; nIndex++ )
227 TRACE("search strl for stream %lu\n",nIndex);
228 hr = RIFF_SearchList(
230 dwLen_hdrl, llOfs, PARSER_strl,
231 &llOfs, &dwChunkLength );
238 hr = CAVIParseImpl_ParseStreamList(
240 llOfs, dwChunkLength, &This->pStreamsBuf[nIndex] );
246 llOfs += dwChunkLength;
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 );
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 );
271 pEntriesBuf = (AVIINDEXENTRY*)QUARTZ_AllocMem(
272 sizeof(AVIINDEXENTRY) * This->cIndexEntries );
273 if ( pEntriesBuf == NULL )
274 return E_OUTOFMEMORY;
276 for ( nIndex = 0; nIndex < This->avih.dwStreams; nIndex++ )
278 cEntriesCur = cEntries;
279 dwChunkId = (((nIndex%10)+'0')<<8) | ((nIndex/10)+'0');
280 for ( i = 0; i < This->cIndexEntries; i++ )
282 if ( (This->pIndexEntriesBuf[i].ckid & 0xffff) == dwChunkId )
283 memcpy( &pEntriesBuf[cEntries++], &This->pIndexEntriesBuf[i], sizeof(AVIINDEXENTRY) );
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);
292 QUARTZ_FreeMem(This->pIndexEntriesBuf);
293 This->pIndexEntriesBuf = pEntriesBuf;
295 This->avih.dwSuggestedBufferSize = 0;
296 for ( i = 0; i < This->cIndexEntries; i++ )
298 if ( This->avih.dwSuggestedBufferSize < This->pIndexEntriesBuf[i].dwChunkLength )
299 This->avih.dwSuggestedBufferSize = This->pIndexEntriesBuf[i].dwChunkLength;
307 if ( This->avih.dwStreams > 100 )
310 *pcStreams = This->avih.dwStreams;
315 static HRESULT CAVIParseImpl_UninitParser( CParserImpl* pImpl )
317 CAVIParseImpl* This = (CAVIParseImpl*)pImpl->m_pUserData;
320 TRACE("(%p)\n",This);
326 if ( This->pStreamsBuf != NULL )
328 for ( nIndex = 0; nIndex < This->avih.dwStreams; nIndex++ )
330 /* release this stream */
331 if ( This->pStreamsBuf[nIndex].pFmtBuf != NULL )
332 QUARTZ_FreeMem(This->pStreamsBuf[nIndex].pFmtBuf);
334 QUARTZ_FreeMem( This->pStreamsBuf );
335 This->pStreamsBuf = NULL;
338 if ( This->pIndexEntriesBuf != NULL )
340 QUARTZ_FreeMem( This->pIndexEntriesBuf );
341 This->pIndexEntriesBuf = NULL;
344 QUARTZ_FreeMem( This );
345 pImpl->m_pUserData = NULL;
350 static LPCWSTR CAVIParseImpl_GetOutPinName( CParserImpl* pImpl, ULONG nStreamIndex )
352 CAVIParseImpl* This = (CAVIParseImpl*)pImpl->m_pUserData;
355 TRACE("(%p,%lu)\n",This,nStreamIndex);
357 if ( This == NULL || nStreamIndex >= This->avih.dwStreams )
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;
366 return This->wchWork;
369 static HRESULT CAVIParseImpl_GetStreamType( CParserImpl* pImpl, ULONG nStreamIndex, AM_MEDIA_TYPE* pmt )
371 CAVIParseImpl* This = (CAVIParseImpl*)pImpl->m_pUserData;
372 VIDEOINFOHEADER* pvi;
373 BITMAPINFOHEADER* pbi;
379 TRACE("(%p,%lu,%p)\n",This,nStreamIndex,pmt);
383 if ( nStreamIndex >= This->avih.dwStreams )
386 cbFmt = This->pStreamsBuf[nStreamIndex].cbFmt;
388 ZeroMemory( pmt, sizeof(AM_MEDIA_TYPE) );
389 switch ( This->pStreamsBuf[nStreamIndex].strh.fccType )
392 pbi = (BITMAPINFOHEADER*)This->pStreamsBuf[nStreamIndex].pFmtBuf;
393 if ( pbi == NULL || cbFmt < sizeof(BITMAPINFOHEADER) )
396 memcpy( &pmt->majortype, &MEDIATYPE_Video, sizeof(GUID) );
397 hr = QUARTZ_MediaSubType_FromBitmap( &pmt->subtype, pbi );
401 QUARTZ_MediaSubType_FromFourCC( &pmt->subtype, (DWORD)pbi->biCompression );
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) );
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;
415 memcpy( &pvi->bmiHeader, pbi, cbFmt );
418 pwfx = (WAVEFORMATEX*)This->pStreamsBuf[nStreamIndex].pFmtBuf;
419 if ( pwfx == NULL || cbFmt < (sizeof(WAVEFORMATEX)-2) )
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) );
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 );
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) );
448 pmt->pbFormat = NULL;
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) );
460 pmt->pbFormat = NULL;
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) );
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) );
483 pmt->pbFormat = NULL;
488 static HRESULT CAVIParseImpl_CheckStreamType( CParserImpl* pImpl, ULONG nStreamIndex, const AM_MEDIA_TYPE* pmt )
490 CAVIParseImpl* This = (CAVIParseImpl*)pImpl->m_pUserData;
493 VIDEOINFOHEADER* pvi;
494 VIDEOINFOHEADER* pviCheck;
496 WAVEFORMATEX* pwfxCheck;
498 TRACE("(%p,%lu,%p)\n",This,nStreamIndex,pmt);
500 hr = CAVIParseImpl_GetStreamType( pImpl, nStreamIndex, &mt );
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 ) )
513 TRACE("check format\n");
515 switch ( This->pStreamsBuf[nStreamIndex].strh.fccType )
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) )
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 )
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) )
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 )
551 QUARTZ_MediaType_Free( &mt );
558 static HRESULT CAVIParseImpl_GetAllocProp( CParserImpl* pImpl, ALLOCATOR_PROPERTIES* pReqProp )
560 CAVIParseImpl* This = (CAVIParseImpl*)pImpl->m_pUserData;
562 TRACE("(%p,%p)\n",This,pReqProp);
566 ZeroMemory( pReqProp, sizeof(ALLOCATOR_PROPERTIES) );
567 pReqProp->cBuffers = This->avih.dwStreams;
568 pReqProp->cbBuffer = This->avih.dwSuggestedBufferSize;
573 static HRESULT CAVIParseImpl_GetNextRequest( CParserImpl* pImpl, ULONG* pnStreamIndex, LONGLONG* pllStart, LONG* plLength, REFERENCE_TIME* prtStart, REFERENCE_TIME* prtStop )
575 CAVIParseImpl* This = (CAVIParseImpl*)pImpl->m_pUserData;
576 REFERENCE_TIME rtNext;
579 CAVIParseStream* pStream;
580 const WAVEFORMATEX* pwfx;
585 TRACE("(%p)\n",This);
587 nIndexNext = This->avih.dwStreams;
588 rtNext = ((REFERENCE_TIME)0x7fffffff<<32)|((REFERENCE_TIME)0xffffffff);
589 for ( nIndex = 0; nIndex < This->avih.dwStreams; nIndex++ )
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 )
596 rtNext = This->pStreamsBuf[nIndex].rtCur;
599 if ( nIndexNext >= This->avih.dwStreams )
602 if ( This->pIndexEntriesBuf != NULL )
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;
611 switch ( pStream->strh.fccType )
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 */
621 pwfx = (const WAVEFORMATEX*)pStream->pFmtBuf;
622 if ( pwfx != NULL && pStream->cbFmt >= (sizeof(WAVEFORMATEX)-2) )
624 pStream->rtInternal += (REFERENCE_TIME)*plLength;
625 rtNext = pStream->rtInternal * (REFERENCE_TIME)QUARTZ_TIMEUNITS / (REFERENCE_TIME)pwfx->nAvgBytesPerSec;
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);
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);
640 pStream->cIndexCur ++;
641 pStream->rtCur = rtNext;
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));
658 static HRESULT CAVIParseImpl_ProcessSample( CParserImpl* pImpl, ULONG nStreamIndex, LONGLONG llStart, LONG lLength, IMediaSample* pSample )
660 CAVIParseImpl* This = (CAVIParseImpl*)pImpl->m_pUserData;
662 TRACE("(%p,%lu,%ld,%ld,%p)\n",This,nStreamIndex,(long)llStart,lLength,pSample);
673 static const struct ParserHandlers CAVIParseImpl_Handlers =
675 CAVIParseImpl_InitParser,
676 CAVIParseImpl_UninitParser,
677 CAVIParseImpl_GetOutPinName,
678 CAVIParseImpl_GetStreamType,
679 CAVIParseImpl_CheckStreamType,
680 CAVIParseImpl_GetAllocProp,
681 CAVIParseImpl_GetNextRequest,
682 CAVIParseImpl_ProcessSample,
684 /* for IQualityControl */
685 NULL, /* pQualityNotify */
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 */
700 HRESULT QUARTZ_CreateAVISplitter(IUnknown* punkOuter,void** ppobj)
702 return QUARTZ_CreateParser(
705 QUARTZ_AVIParser_Name,
706 QUARTZ_AVIParserInPin_Name,
707 &CAVIParseImpl_Handlers );