/* * Implements AVI Parser(Splitter). * * Copyright (C) Hidenori TAKESHIMA * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public * License as published by the Free Software Foundation; either * version 2.1 of the License, or (at your option) any later version. * * This library is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public * License along with this library; if not, write to the Free Software * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */ #include "config.h" #include "windef.h" #include "winbase.h" #include "wingdi.h" #include "winuser.h" #include "mmsystem.h" #include "vfw.h" #include "winerror.h" #include "strmif.h" #include "control.h" #include "vfwmsgs.h" #include "amvideo.h" #include "uuids.h" #include "wine/debug.h" WINE_DEFAULT_DEBUG_CHANNEL(quartz); #include "quartz_private.h" #include "parser.h" #include "mtype.h" static const WCHAR QUARTZ_AVIParser_Name[] = { 'A','V','I',' ','S','p','l','i','t','t','e','r',0 }; static const WCHAR QUARTZ_AVIParserInPin_Name[] = { 'I','n',0 }; static const WCHAR QUARTZ_AVIParserOutPin_Basename[] = { 'S','t','r','e','a','m',0 }; #define WINE_QUARTZ_AVIPINNAME_MAX 64 /**************************************************************************** * * CAVIParseImpl */ typedef struct CAVIParseImpl CAVIParseImpl; typedef struct CAVIParseStream CAVIParseStream; struct CAVIParseImpl { MainAVIHeader avih; CAVIParseStream* pStreamsBuf; DWORD cIndexEntries; AVIINDEXENTRY* pIndexEntriesBuf; WCHAR wchWork[ WINE_QUARTZ_AVIPINNAME_MAX ]; }; struct CAVIParseStream { AVIStreamHeader strh; DWORD cbFmt; BYTE* pFmtBuf; DWORD cIndexEntries; AVIINDEXENTRY* pIndexEntries; DWORD cIndexCur; REFERENCE_TIME rtCur; REFERENCE_TIME rtInternal; }; static HRESULT CAVIParseImpl_ParseStreamList( CParserImpl* pImpl, CAVIParseImpl* This, ULONG nStreamIndex, LONGLONG llOfsTop, DWORD dwListLen, CAVIParseStream* pStream ) { HRESULT hr; LONGLONG llOfs; DWORD dwChunkLength; TRACE("search strh\n"); hr = RIFF_SearchChunk( pImpl, dwListLen, llOfsTop, PARSER_strh, &llOfs, &dwChunkLength ); if ( hr == S_OK ) { TRACE("strh has been detected\n"); if ( dwChunkLength < sizeof(AVIStreamHeader) ) hr = E_FAIL; else hr = IAsyncReader_SyncRead( pImpl->m_pReader, llOfs, sizeof(AVIStreamHeader), (BYTE*)&pStream->strh ); } if ( FAILED(hr) ) return hr; if ( hr != S_OK ) return E_FAIL; TRACE("search strf\n"); hr = RIFF_SearchChunk( pImpl, dwListLen, llOfsTop, PARSER_strf, &llOfs, &dwChunkLength ); if ( hr == S_OK && dwChunkLength > 0 ) { TRACE("strf has been detected\n"); pStream->cbFmt = dwChunkLength; pStream->pFmtBuf = (BYTE*)QUARTZ_AllocMem( dwChunkLength ); if ( pStream->pFmtBuf == NULL ) hr = E_OUTOFMEMORY; else hr = IAsyncReader_SyncRead( pImpl->m_pReader, llOfs, dwChunkLength, pStream->pFmtBuf ); } if ( FAILED(hr) ) return hr; TRACE("search indx\n"); hr = RIFF_SearchChunk( pImpl, dwListLen, llOfsTop, PARSER_indx, &llOfs, &dwChunkLength ); if ( FAILED(hr) ) return hr; if ( hr == S_OK ) { FIXME( "'indx' has been detected - not implemented now!\n" ); return E_FAIL; } return NOERROR; } static HRESULT CAVIParseImpl_InitParser( CParserImpl* pImpl, ULONG* pcStreams ) { CAVIParseImpl* This = NULL; BYTE riffhdr[12]; ULONG i; ULONG nIndex; HRESULT hr; LONGLONG llOfs_hdrl; DWORD dwLen_hdrl; LONGLONG llOfs; DWORD dwChunkId; DWORD dwChunkLength; AVIINDEXENTRY* pEntriesBuf = NULL; ULONG cEntries; ULONG cEntriesCur; TRACE("(%p,%p)\n",pImpl,pcStreams); hr = IAsyncReader_SyncRead( pImpl->m_pReader, 0, 12, riffhdr ); if ( FAILED(hr) ) return hr; if ( hr != S_OK ) return E_FAIL; if ( memcmp( &riffhdr[0], "RIFF", 4 ) != 0 || memcmp( &riffhdr[8], "AVI ", 4 ) != 0 ) return E_FAIL; TRACE("it's AVI\n"); This = (CAVIParseImpl*)QUARTZ_AllocMem( sizeof(CAVIParseImpl) ); if ( This == NULL ) return E_OUTOFMEMORY; pImpl->m_pUserData = This; ZeroMemory( This, sizeof(CAVIParseImpl) ); This->pStreamsBuf = NULL; This->cIndexEntries = 0; This->pIndexEntriesBuf = 0; hr = RIFF_SearchList( pImpl, (DWORD)0xffffffff, PARSER_RIFF_OfsFirst, PARSER_hdrl, &llOfs_hdrl, &dwLen_hdrl ); if ( FAILED(hr) ) return hr; if ( hr != S_OK ) return E_FAIL; /* read 'avih' */ TRACE("read avih\n"); hr = RIFF_SearchChunk( pImpl, dwLen_hdrl, llOfs_hdrl, PARSER_avih, &llOfs, &dwChunkLength ); if ( FAILED(hr) ) return hr; if ( hr != S_OK ) return E_FAIL; if ( dwChunkLength > sizeof(MainAVIHeader) ) dwChunkLength = sizeof(MainAVIHeader); hr = IAsyncReader_SyncRead( pImpl->m_pReader, llOfs, dwChunkLength, (BYTE*)&(This->avih) ); if ( FAILED(hr) ) return hr; if ( hr != S_OK ) return E_FAIL; if ( This->avih.dwStreams == 0 ) return E_FAIL; /* initialize streams. */ This->pStreamsBuf = (CAVIParseStream*)QUARTZ_AllocMem( sizeof(CAVIParseStream) * This->avih.dwStreams ); if ( This->pStreamsBuf == NULL ) return E_OUTOFMEMORY; ZeroMemory( This->pStreamsBuf, sizeof(CAVIParseStream) * This->avih.dwStreams ); llOfs = llOfs_hdrl; for ( nIndex = 0; nIndex < This->avih.dwStreams; nIndex++ ) { TRACE("search strl for stream %lu\n",nIndex); hr = RIFF_SearchList( pImpl, dwLen_hdrl, llOfs, PARSER_strl, &llOfs, &dwChunkLength ); if ( FAILED(hr) ) return hr; if ( hr != S_OK ) return E_FAIL; /* read 'strl'. */ hr = CAVIParseImpl_ParseStreamList( pImpl, This, nIndex, llOfs, dwChunkLength, &This->pStreamsBuf[nIndex] ); if ( FAILED(hr) ) return hr; if ( hr != S_OK ) return E_FAIL; llOfs += dwChunkLength; } /* initialize idx1. */ TRACE("search idx1\n"); hr = RIFF_SearchChunk( pImpl, (DWORD)0xffffffff, PARSER_RIFF_OfsFirst, PARSER_idx1, &llOfs, &dwChunkLength ); if ( FAILED(hr) ) return hr; if ( hr == S_OK ) { /* read idx1. */ This->cIndexEntries = dwChunkLength / sizeof(AVIINDEXENTRY); This->pIndexEntriesBuf = (AVIINDEXENTRY*)QUARTZ_AllocMem( sizeof(AVIINDEXENTRY) * This->cIndexEntries ); if ( This->pIndexEntriesBuf == NULL ) return E_OUTOFMEMORY; hr = IAsyncReader_SyncRead( pImpl->m_pReader, llOfs, sizeof(AVIINDEXENTRY) * This->cIndexEntries, (BYTE*)This->pIndexEntriesBuf ); if ( FAILED(hr) ) return hr; if ( hr != S_OK ) return E_FAIL; pEntriesBuf = (AVIINDEXENTRY*)QUARTZ_AllocMem( sizeof(AVIINDEXENTRY) * This->cIndexEntries ); if ( pEntriesBuf == NULL ) return E_OUTOFMEMORY; cEntries = 0; for ( nIndex = 0; nIndex < This->avih.dwStreams; nIndex++ ) { cEntriesCur = cEntries; dwChunkId = (((nIndex%10)+'0')<<8) | ((nIndex/10)+'0'); for ( i = 0; i < This->cIndexEntries; i++ ) { if ( (This->pIndexEntriesBuf[i].ckid & 0xffff) == dwChunkId ) memcpy( &pEntriesBuf[cEntries++], &This->pIndexEntriesBuf[i], sizeof(AVIINDEXENTRY) ); } This->pStreamsBuf[nIndex].pIndexEntries = &pEntriesBuf[cEntriesCur]; This->pStreamsBuf[nIndex].cIndexEntries = cEntries - cEntriesCur; This->pStreamsBuf[nIndex].cIndexCur = 0; This->pStreamsBuf[nIndex].rtCur = 0; This->pStreamsBuf[nIndex].rtInternal = 0; TRACE("stream %lu - %lu entries\n",nIndex,This->pStreamsBuf[nIndex].cIndexEntries); } QUARTZ_FreeMem(This->pIndexEntriesBuf); This->pIndexEntriesBuf = pEntriesBuf; This->avih.dwSuggestedBufferSize = 0; for ( i = 0; i < This->cIndexEntries; i++ ) { if ( This->avih.dwSuggestedBufferSize < This->pIndexEntriesBuf[i].dwChunkLength ) This->avih.dwSuggestedBufferSize = This->pIndexEntriesBuf[i].dwChunkLength; } } else { return E_FAIL; } if ( This->avih.dwStreams > 100 ) return E_FAIL; *pcStreams = This->avih.dwStreams; return NOERROR; } static HRESULT CAVIParseImpl_UninitParser( CParserImpl* pImpl ) { CAVIParseImpl* This = (CAVIParseImpl*)pImpl->m_pUserData; ULONG nIndex; TRACE("(%p)\n",This); if ( This == NULL ) return NOERROR; /* destruct */ if ( This->pStreamsBuf != NULL ) { for ( nIndex = 0; nIndex < This->avih.dwStreams; nIndex++ ) { /* release this stream */ if ( This->pStreamsBuf[nIndex].pFmtBuf != NULL ) QUARTZ_FreeMem(This->pStreamsBuf[nIndex].pFmtBuf); } QUARTZ_FreeMem( This->pStreamsBuf ); This->pStreamsBuf = NULL; } if ( This->pIndexEntriesBuf != NULL ) { QUARTZ_FreeMem( This->pIndexEntriesBuf ); This->pIndexEntriesBuf = NULL; } QUARTZ_FreeMem( This ); pImpl->m_pUserData = NULL; return NOERROR; } static LPCWSTR CAVIParseImpl_GetOutPinName( CParserImpl* pImpl, ULONG nStreamIndex ) { CAVIParseImpl* This = (CAVIParseImpl*)pImpl->m_pUserData; int wlen; TRACE("(%p,%lu)\n",This,nStreamIndex); if ( This == NULL || nStreamIndex >= This->avih.dwStreams ) return NULL; wlen = lstrlenW(QUARTZ_AVIParserOutPin_Basename); memcpy( This->wchWork, QUARTZ_AVIParserOutPin_Basename, sizeof(WCHAR)*wlen ); This->wchWork[ wlen ] = (nStreamIndex/10) + '0'; This->wchWork[ wlen+1 ] = (nStreamIndex%10) + '0'; This->wchWork[ wlen+2 ] = 0; return This->wchWork; } static HRESULT CAVIParseImpl_GetStreamType( CParserImpl* pImpl, ULONG nStreamIndex, AM_MEDIA_TYPE* pmt ) { CAVIParseImpl* This = (CAVIParseImpl*)pImpl->m_pUserData; VIDEOINFOHEADER* pvi; BITMAPINFOHEADER* pbi; WAVEFORMATEX* pwfx; DWORD cbFmt; DWORD cb; HRESULT hr; TRACE("(%p,%lu,%p)\n",This,nStreamIndex,pmt); if ( This == NULL ) return E_UNEXPECTED; if ( nStreamIndex >= This->avih.dwStreams ) return E_INVALIDARG; cbFmt = This->pStreamsBuf[nStreamIndex].cbFmt; ZeroMemory( pmt, sizeof(AM_MEDIA_TYPE) ); switch ( This->pStreamsBuf[nStreamIndex].strh.fccType ) { case PARSER_vids: pbi = (BITMAPINFOHEADER*)This->pStreamsBuf[nStreamIndex].pFmtBuf; if ( pbi == NULL || cbFmt < sizeof(BITMAPINFOHEADER) ) goto unknown_format; memcpy( &pmt->majortype, &MEDIATYPE_Video, sizeof(GUID) ); hr = QUARTZ_MediaSubType_FromBitmap( &pmt->subtype, pbi ); if ( FAILED(hr) ) goto unknown_format; if ( hr != S_OK ) QUARTZ_MediaSubType_FromFourCC( &pmt->subtype, (DWORD)pbi->biCompression ); pmt->bFixedSizeSamples = QUARTZ_BitmapHasFixedSample( pbi ) ? 1 : 0; pmt->bTemporalCompression = 0; /* FIXME - 1 if inter-frame compression is used */ pmt->lSampleSize = ( pbi->biCompression == 0 ) ? DIBSIZE(*pbi) : pbi->biSizeImage; memcpy( &pmt->formattype, &FORMAT_VideoInfo, sizeof(GUID) ); cb = sizeof(VIDEOINFOHEADER) + cbFmt; pmt->pbFormat = (BYTE*)CoTaskMemAlloc( cb ); if ( pmt->pbFormat == NULL ) return E_OUTOFMEMORY; ZeroMemory( pmt->pbFormat, cb ); pvi = (VIDEOINFOHEADER*)pmt->pbFormat; pmt->cbFormat = cb; memcpy( &pvi->bmiHeader, pbi, cbFmt ); break; case PARSER_auds: pwfx = (WAVEFORMATEX*)This->pStreamsBuf[nStreamIndex].pFmtBuf; if ( pwfx == NULL || cbFmt < (sizeof(WAVEFORMATEX)-2) ) goto unknown_format; memcpy( &pmt->majortype, &MEDIATYPE_Audio, sizeof(GUID) ); QUARTZ_MediaSubType_FromFourCC( &pmt->subtype, (DWORD)pwfx->wFormatTag ); pmt->bFixedSizeSamples = 1; pmt->bTemporalCompression = 0; pmt->lSampleSize = pwfx->nBlockAlign; memcpy( &pmt->formattype, &FORMAT_WaveFormatEx, sizeof(GUID) ); pmt->pUnk = NULL; cb = ( cbFmt < sizeof(WAVEFORMATEX) ) ? sizeof(WAVEFORMATEX) : cbFmt; pmt->pbFormat = (BYTE*)CoTaskMemAlloc( cb ); if ( pmt->pbFormat == NULL ) return E_OUTOFMEMORY; ZeroMemory( pmt->pbFormat, cb ); pmt->cbFormat = cbFmt; memcpy( pmt->pbFormat, pwfx, cbFmt ); break; case PARSER_mids: /* FIXME? */ memcpy( &pmt->majortype, &MEDIATYPE_Midi, sizeof(GUID) ); memcpy( &pmt->subtype, &MEDIASUBTYPE_NULL, sizeof(GUID) ); pmt->bFixedSizeSamples = 0; pmt->bTemporalCompression = 0; pmt->lSampleSize = 1; memcpy( &pmt->formattype, &FORMAT_None, sizeof(GUID) ); pmt->pUnk = NULL; pmt->cbFormat = 0; pmt->pbFormat = NULL; break; case PARSER_txts: /* FIXME? */ memcpy( &pmt->majortype, &MEDIATYPE_Text, sizeof(GUID) ); memcpy( &pmt->subtype, &MEDIASUBTYPE_NULL, sizeof(GUID) ); pmt->bFixedSizeSamples = 0; pmt->bTemporalCompression = 0; pmt->lSampleSize = 1; memcpy( &pmt->formattype, &FORMAT_None, sizeof(GUID) ); pmt->pUnk = NULL; pmt->cbFormat = 0; pmt->pbFormat = NULL; break; default: goto unknown_format; } return NOERROR; unknown_format:; FIXME( "(%p) unsupported stream type %c%c%c%c\n",This, (int)((This->pStreamsBuf[nStreamIndex].strh.fccType>> 0)&0xff), (int)((This->pStreamsBuf[nStreamIndex].strh.fccType>> 8)&0xff), (int)((This->pStreamsBuf[nStreamIndex].strh.fccType>>16)&0xff), (int)((This->pStreamsBuf[nStreamIndex].strh.fccType>>24)&0xff) ); memcpy( &pmt->majortype, &MEDIATYPE_NULL, sizeof(GUID) ); memcpy( &pmt->subtype, &MEDIASUBTYPE_NULL, sizeof(GUID) ); pmt->bFixedSizeSamples = 0; pmt->bTemporalCompression = 0; pmt->lSampleSize = 1; memcpy( &pmt->formattype, &FORMAT_None, sizeof(GUID) ); pmt->pUnk = NULL; pmt->cbFormat = 0; pmt->pbFormat = NULL; return NOERROR; } static HRESULT CAVIParseImpl_CheckStreamType( CParserImpl* pImpl, ULONG nStreamIndex, const AM_MEDIA_TYPE* pmt ) { CAVIParseImpl* This = (CAVIParseImpl*)pImpl->m_pUserData; HRESULT hr; AM_MEDIA_TYPE mt; VIDEOINFOHEADER* pvi; VIDEOINFOHEADER* pviCheck; WAVEFORMATEX* pwfx; WAVEFORMATEX* pwfxCheck; TRACE("(%p,%lu,%p)\n",This,nStreamIndex,pmt); hr = CAVIParseImpl_GetStreamType( pImpl, nStreamIndex, &mt ); if ( FAILED(hr) ) return hr; TRACE("check GUIDs - %s,%s\n",debugstr_guid(&pmt->majortype),debugstr_guid(&pmt->subtype)); if ( !IsEqualGUID( &pmt->majortype, &mt.majortype ) || !IsEqualGUID( &pmt->subtype, &mt.subtype ) || !IsEqualGUID( &pmt->formattype, &mt.formattype ) ) { hr = E_FAIL; goto end; } TRACE("check format\n"); hr = S_OK; switch ( This->pStreamsBuf[nStreamIndex].strh.fccType ) { case PARSER_vids: TRACE("check vids\n"); pvi = (VIDEOINFOHEADER*)mt.pbFormat; pviCheck = (VIDEOINFOHEADER*)pmt->pbFormat; if ( pvi == NULL || pviCheck == NULL || pmt->cbFormat < sizeof(VIDEOINFOHEADER) ) hr = E_FAIL; if ( pvi->bmiHeader.biWidth != pviCheck->bmiHeader.biWidth || pvi->bmiHeader.biHeight != pviCheck->bmiHeader.biHeight || pvi->bmiHeader.biPlanes != pviCheck->bmiHeader.biPlanes || pvi->bmiHeader.biBitCount != pviCheck->bmiHeader.biBitCount || pvi->bmiHeader.biCompression != pviCheck->bmiHeader.biCompression || pvi->bmiHeader.biClrUsed != pviCheck->bmiHeader.biClrUsed ) hr = E_FAIL; break; case PARSER_auds: TRACE("check auds\n"); pwfx = (WAVEFORMATEX*)mt.pbFormat; pwfxCheck = (WAVEFORMATEX*)pmt->pbFormat; if ( pwfx == NULL || pwfxCheck == NULL || pmt->cbFormat < (sizeof(WAVEFORMATEX)-2) ) hr = E_FAIL; if ( pwfx->wFormatTag != pwfxCheck->wFormatTag || pwfx->nBlockAlign != pwfxCheck->nBlockAlign || pwfx->wBitsPerSample != pwfxCheck->wBitsPerSample || pwfx->nChannels != pwfxCheck->nChannels || pwfx->nSamplesPerSec != pwfxCheck->nSamplesPerSec ) hr = E_FAIL; break; case PARSER_mids: case PARSER_txts: break; default: break; } end: QUARTZ_MediaType_Free( &mt ); TRACE("%08lx\n",hr); return hr; } static HRESULT CAVIParseImpl_GetAllocProp( CParserImpl* pImpl, ALLOCATOR_PROPERTIES* pReqProp ) { CAVIParseImpl* This = (CAVIParseImpl*)pImpl->m_pUserData; TRACE("(%p,%p)\n",This,pReqProp); if ( This == NULL ) return E_UNEXPECTED; ZeroMemory( pReqProp, sizeof(ALLOCATOR_PROPERTIES) ); pReqProp->cBuffers = This->avih.dwStreams; pReqProp->cbBuffer = This->avih.dwSuggestedBufferSize; return NOERROR; } static HRESULT CAVIParseImpl_GetNextRequest( CParserImpl* pImpl, ULONG* pnStreamIndex, LONGLONG* pllStart, LONG* plLength, REFERENCE_TIME* prtStart, REFERENCE_TIME* prtStop ) { CAVIParseImpl* This = (CAVIParseImpl*)pImpl->m_pUserData; REFERENCE_TIME rtNext; DWORD nIndexNext; DWORD nIndex; CAVIParseStream* pStream; const WAVEFORMATEX* pwfx; if ( This == NULL ) return E_UNEXPECTED; TRACE("(%p)\n",This); nIndexNext = This->avih.dwStreams; rtNext = ((REFERENCE_TIME)0x7fffffff<<32)|((REFERENCE_TIME)0xffffffff); for ( nIndex = 0; nIndex < This->avih.dwStreams; nIndex++ ) { TRACE("stream %lu - %lu,%lu\n",nIndex,(unsigned long)(This->pStreamsBuf[nIndex].rtCur*1000/QUARTZ_TIMEUNITS),This->pStreamsBuf[nIndex].cIndexCur); if ( rtNext > This->pStreamsBuf[nIndex].rtCur && This->pStreamsBuf[nIndex].cIndexCur < This->pStreamsBuf[nIndex].cIndexEntries ) { nIndexNext = nIndex; rtNext = This->pStreamsBuf[nIndex].rtCur; } } if ( nIndexNext >= This->avih.dwStreams ) return S_FALSE; if ( This->pIndexEntriesBuf != NULL ) { pStream = &This->pStreamsBuf[nIndexNext]; *pnStreamIndex = nIndexNext; *pllStart = (LONGLONG)pStream->pIndexEntries[pStream->cIndexCur].dwChunkOffset + 8; *plLength = (LONG)pStream->pIndexEntries[pStream->cIndexCur].dwChunkLength; *prtStart = rtNext; *prtStop = rtNext; switch ( pStream->strh.fccType ) { case PARSER_vids: TRACE("vids\n"); pStream->rtInternal ++; rtNext = pStream->rtInternal * (REFERENCE_TIME)QUARTZ_TIMEUNITS * (REFERENCE_TIME)pStream->strh.dwScale / (REFERENCE_TIME)pStream->strh.dwRate; /* FIXME - handle AVIPALCHANGE */ break; case PARSER_auds: TRACE("auds\n"); pwfx = (const WAVEFORMATEX*)pStream->pFmtBuf; if ( pwfx != NULL && pStream->cbFmt >= (sizeof(WAVEFORMATEX)-2) ) { pStream->rtInternal += (REFERENCE_TIME)*plLength; rtNext = pStream->rtInternal * (REFERENCE_TIME)QUARTZ_TIMEUNITS / (REFERENCE_TIME)pwfx->nAvgBytesPerSec; } else { pStream->rtInternal += (REFERENCE_TIME)(*plLength); rtNext = pStream->rtInternal * (REFERENCE_TIME)QUARTZ_TIMEUNITS * (REFERENCE_TIME)pStream->strh.dwScale / ((REFERENCE_TIME)pStream->strh.dwSampleSize * (REFERENCE_TIME)pStream->strh.dwRate); } break; case PARSER_mids: case PARSER_txts: default: pStream->rtInternal += (REFERENCE_TIME)(*plLength); rtNext = pStream->rtInternal * (REFERENCE_TIME)QUARTZ_TIMEUNITS * (REFERENCE_TIME)pStream->strh.dwScale / ((REFERENCE_TIME)pStream->strh.dwSampleSize * (REFERENCE_TIME)pStream->strh.dwRate); break; } pStream->cIndexCur ++; pStream->rtCur = rtNext; *prtStop = rtNext; } else { ERR( "no idx1\n" ); return E_NOTIMPL; } TRACE("return %lu / %ld-%ld / %lu-%lu\n", *pnStreamIndex,(long)*pllStart,*plLength, (unsigned long)((*prtStart)*1000/QUARTZ_TIMEUNITS), (unsigned long)((*prtStop)*1000/QUARTZ_TIMEUNITS)); return NOERROR; } static HRESULT CAVIParseImpl_ProcessSample( CParserImpl* pImpl, ULONG nStreamIndex, LONGLONG llStart, LONG lLength, IMediaSample* pSample ) { CAVIParseImpl* This = (CAVIParseImpl*)pImpl->m_pUserData; TRACE("(%p,%lu,%ld,%ld,%p)\n",This,nStreamIndex,(long)llStart,lLength,pSample); if ( This == NULL ) return E_UNEXPECTED; return NOERROR; } static const struct ParserHandlers CAVIParseImpl_Handlers = { CAVIParseImpl_InitParser, CAVIParseImpl_UninitParser, CAVIParseImpl_GetOutPinName, CAVIParseImpl_GetStreamType, CAVIParseImpl_CheckStreamType, CAVIParseImpl_GetAllocProp, CAVIParseImpl_GetNextRequest, CAVIParseImpl_ProcessSample, /* for IQualityControl */ NULL, /* pQualityNotify */ /* for seeking */ NULL, /* pGetSeekingCaps */ NULL, /* pIsTimeFormatSupported */ NULL, /* pGetCurPos */ NULL, /* pSetCurPos */ NULL, /* pGetDuration */ NULL, /* pSetDuration */ NULL, /* pGetStopPos */ NULL, /* pSetStopPos */ NULL, /* pGetPreroll */ NULL, /* pSetPreroll */ }; HRESULT QUARTZ_CreateAVISplitter(IUnknown* punkOuter,void** ppobj) { return QUARTZ_CreateParser( punkOuter,ppobj, &CLSID_AviSplitter, QUARTZ_AVIParser_Name, QUARTZ_AVIParserInPin_Name, &CAVIParseImpl_Handlers ); }