/* * Implements IBaseFilter for parsers. (internal) * * 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 * * FIXME - handle errors/flushing correctly. * FIXME - handle seeking. */ #include "config.h" #include "windef.h" #include "winbase.h" #include "wingdi.h" #include "winuser.h" #include "mmsystem.h" #include "winerror.h" #include "strmif.h" #include "control.h" #include "vfwmsgs.h" #include "evcode.h" #include "uuids.h" #include "wine/debug.h" WINE_DEFAULT_DEBUG_CHANNEL(quartz); #include "quartz_private.h" #include "parser.h" #include "mtype.h" #include "memalloc.h" #define QUARTZ_MSG_EXITTHREAD (WM_APP+2) #define QUARTZ_MSG_SEEK (WM_APP+3) HRESULT CParserOutPinImpl_InitIMediaSeeking( CParserOutPinImpl* This ); void CParserOutPinImpl_UninitIMediaSeeking( CParserOutPinImpl* This ); HRESULT CParserOutPinImpl_InitIMediaPosition( CParserOutPinImpl* This ); void CParserOutPinImpl_UninitIMediaPosition( CParserOutPinImpl* This ); /*************************************************************************** * * CParserImpl internal thread * */ static void CParserImplThread_ClearAllRequests( CParserImpl* This ) { ULONG nIndex; TRACE("(%p)\n",This); for ( nIndex = 0; nIndex < This->m_cOutStreams; nIndex++ ) { This->m_ppOutPins[nIndex]->m_bReqUsed = FALSE; This->m_ppOutPins[nIndex]->m_pReqSample = NULL; } } static void CParserImplThread_ReleaseAllRequests( CParserImpl* This ) { ULONG nIndex; TRACE("(%p)\n",This); for ( nIndex = 0; nIndex < This->m_cOutStreams; nIndex++ ) { if ( This->m_ppOutPins[nIndex]->m_bReqUsed ) { if ( This->m_ppOutPins[nIndex]->m_pReqSample != NULL ) { IMediaSample_Release(This->m_ppOutPins[nIndex]->m_pReqSample); This->m_ppOutPins[nIndex]->m_pReqSample = NULL; } This->m_ppOutPins[nIndex]->m_bReqUsed = FALSE; } } } static BOOL CParserImplThread_HasPendingSamples( CParserImpl* This ) { ULONG nIndex; for ( nIndex = 0; nIndex < This->m_cOutStreams; nIndex++ ) { if ( This->m_ppOutPins[nIndex]->m_bReqUsed && This->m_ppOutPins[nIndex]->m_pReqSample != NULL ) return TRUE; } return FALSE; } static HRESULT CParserImplThread_FlushAllPendingSamples( CParserImpl* This ) { HRESULT hr; IMediaSample* pSample; DWORD_PTR dwContext; TRACE("(%p)\n",This); /* remove all samples from queue. */ hr = IAsyncReader_BeginFlush(This->m_pReader); if ( FAILED(hr) ) return hr; IAsyncReader_EndFlush(This->m_pReader); /* remove all processed samples from queue. */ while ( 1 ) { hr = IAsyncReader_WaitForNext(This->m_pReader,0,&pSample,&dwContext); if ( hr != S_OK ) break; } CParserImplThread_ReleaseAllRequests(This); return NOERROR; } static HRESULT CParserImplThread_SendEndOfStream( CParserImpl* This ) { ULONG nIndex; HRESULT hr; HRESULT hrRet; CParserOutPinImpl* pOutPin; TRACE("(%p)\n",This); if ( This->m_bSendEOS ) return NOERROR; This->m_bSendEOS = TRUE; hrRet = S_OK; for ( nIndex = 0; nIndex < This->m_cOutStreams; nIndex++ ) { pOutPin = This->m_ppOutPins[nIndex]; hr = CPinBaseImpl_SendEndOfStream(&pOutPin->pin); if ( FAILED(hr) ) { if ( SUCCEEDED(hrRet) ) hrRet = hr; } else { if ( hr != S_OK && hrRet == S_OK ) hrRet = hr; } } return hrRet; } static HRESULT CParserImplThread_SendFlush( CParserImpl* This ) { ULONG nIndex; HRESULT hr; HRESULT hrRet; CParserOutPinImpl* pOutPin; TRACE("(%p)\n",This); hrRet = S_OK; for ( nIndex = 0; nIndex < This->m_cOutStreams; nIndex++ ) { pOutPin = This->m_ppOutPins[nIndex]; hr = CPinBaseImpl_SendBeginFlush(&pOutPin->pin); if ( FAILED(hr) ) { if ( SUCCEEDED(hrRet) ) hrRet = hr; } else { if ( hr != S_OK && hrRet == S_OK ) hrRet = hr; hr = CPinBaseImpl_SendEndFlush(&pOutPin->pin); if ( FAILED(hr) ) hrRet = hr; } } return hrRet; } static void CParserImplThread_ErrorAbort( CParserImpl* This, HRESULT hr ) { CBaseFilterImpl_MediaEventNotify( &This->basefilter,EC_ERRORABORT,(LONG_PTR)hr,(LONG_PTR)0); CParserImplThread_SendEndOfStream(This); } static HRESULT CParserImplThread_ProcessNextSample( CParserImpl* This ) { IMediaSample* pSample; DWORD_PTR dwContext; ULONG nIndex; HRESULT hr; CParserOutPinImpl* pOutPin; MSG msg; while ( 1 ) { if ( PeekMessageA( &msg, (HWND)NULL, 0, 0, PM_REMOVE ) ) { hr = NOERROR; switch ( msg.message ) { case QUARTZ_MSG_EXITTHREAD: TRACE("(%p) EndThread\n",This); CParserImplThread_FlushAllPendingSamples(This); CParserImplThread_ClearAllRequests(This); CParserImplThread_SendFlush(This); CParserImplThread_SendEndOfStream(This); TRACE("(%p) exit thread\n",This); return S_FALSE; case QUARTZ_MSG_SEEK: FIXME("(%p) Seek\n",This); CParserImplThread_FlushAllPendingSamples(This); hr = CParserImplThread_SendFlush(This); CParserImplThread_SendEndOfStream(This); /* FIXME - process seeking. */ /* FIXME - Send NewSegment. */ break; default: FIXME( "invalid message %04u\n", (unsigned)msg.message ); hr = E_FAIL; CParserImplThread_ErrorAbort(This,hr); } return hr; } hr = IAsyncReader_WaitForNext(This->m_pReader,PARSER_POLL_INTERVAL,&pSample,&dwContext); nIndex = (ULONG)dwContext; if ( hr != VFW_E_TIMEOUT ) break; } if ( FAILED(hr) ) { CParserImplThread_ErrorAbort(This,hr); return hr; } pOutPin = This->m_ppOutPins[nIndex]; if ( pOutPin != NULL && pOutPin->m_bReqUsed ) { if ( This->m_pHandler->pProcessSample != NULL ) hr = This->m_pHandler->pProcessSample(This,nIndex,pOutPin->m_llReqStart,pOutPin->m_lReqLength,pOutPin->m_pReqSample); if ( SUCCEEDED(hr) ) { if ( pOutPin->m_pOutPinAllocator != NULL && pOutPin->m_pOutPinAllocator != This->m_pAllocator ) { /* if pin has its own allocator, sample must be copied */ hr = IMemAllocator_GetBuffer( This->m_pAllocator, &pSample, NULL, NULL, 0 ); if ( SUCCEEDED(hr) ) { hr = QUARTZ_IMediaSample_Copy( pSample, pOutPin->m_pReqSample, TRUE ); if ( SUCCEEDED(hr) ) hr = CPinBaseImpl_SendSample(&pOutPin->pin,pSample); IMediaSample_Release(pSample); } } else { hr = CPinBaseImpl_SendSample(&pOutPin->pin,pOutPin->m_pReqSample); } } if ( FAILED(hr) ) CParserImplThread_ErrorAbort(This,hr); IMediaSample_Release(pOutPin->m_pReqSample); pOutPin->m_pReqSample = NULL; pOutPin->m_bReqUsed = FALSE; } if ( SUCCEEDED(hr) ) hr = NOERROR; TRACE("return %08lx\n",hr); return hr; } static DWORD WINAPI CParserImplThread_Entry( LPVOID pv ) { CParserImpl* This = (CParserImpl*)pv; BOOL bReqNext; ULONG nIndex = 0; HRESULT hr; REFERENCE_TIME rtSampleTimeStart, rtSampleTimeEnd; LONGLONG llReqStart; LONG lReqLength; REFERENCE_TIME rtReqStart, rtReqStop; IMediaSample* pSample; MSG msg; /* initialize the message queue. */ PeekMessageA( &msg, (HWND)NULL, 0, 0, PM_NOREMOVE ); CParserImplThread_ClearAllRequests(This); /* resume the owner thread. */ SetEvent( This->m_hEventInit ); TRACE( "Enter message loop.\n" ); bReqNext = TRUE; while ( 1 ) { if ( bReqNext ) { /* Get the next request. */ hr = This->m_pHandler->pGetNextRequest( This, &nIndex, &llReqStart, &lReqLength, &rtReqStart, &rtReqStop ); if ( FAILED(hr) ) { CParserImplThread_ErrorAbort(This,hr); break; } if ( hr != S_OK ) { /* Flush pending samples. */ hr = S_OK; while ( CParserImplThread_HasPendingSamples(This) ) { hr = CParserImplThread_ProcessNextSample(This); if ( hr != S_OK ) break; } if ( hr != S_OK ) { /* notification is already sent */ break; } /* Send End Of Stream. */ hr = CParserImplThread_SendEndOfStream(This); if ( hr != S_OK ) { /* notification is already sent */ break; } /* Blocking... */ hr = CParserImplThread_ProcessNextSample(This); if ( hr != S_OK ) { /* notification is already sent */ break; } continue; } if ( This->m_ppOutPins[nIndex]->pin.pPinConnectedTo == NULL ) continue; rtSampleTimeStart = This->basefilter.rtStart + llReqStart * QUARTZ_TIMEUNITS; rtSampleTimeEnd = (llReqStart + lReqLength) * QUARTZ_TIMEUNITS; bReqNext = FALSE; } if ( !This->m_ppOutPins[nIndex]->m_bReqUsed ) { hr = IMemAllocator_GetBuffer( This->m_pAllocator, &pSample, NULL, NULL, 0 ); if ( FAILED(hr) ) { CParserImplThread_ErrorAbort(This,hr); break; } hr = IMediaSample_SetTime(pSample,&rtSampleTimeStart,&rtSampleTimeEnd); if ( SUCCEEDED(hr) ) hr = IAsyncReader_Request(This->m_pReader,pSample,nIndex); if ( FAILED(hr) ) { CParserImplThread_ErrorAbort(This,hr); break; } This->m_ppOutPins[nIndex]->m_bReqUsed = TRUE; This->m_ppOutPins[nIndex]->m_pReqSample = pSample; This->m_ppOutPins[nIndex]->m_llReqStart = llReqStart; This->m_ppOutPins[nIndex]->m_lReqLength = lReqLength; This->m_ppOutPins[nIndex]->m_rtReqStart = rtSampleTimeStart; This->m_ppOutPins[nIndex]->m_rtReqStop = rtSampleTimeEnd; bReqNext = TRUE; continue; } hr = CParserImplThread_ProcessNextSample(This); if ( hr != S_OK ) { /* notification is already sent */ break; } } return 0; } /*************************************************************************** * * CParserImpl internal methods * */ static void CParserImpl_SetAsyncReader( CParserImpl* This, IAsyncReader* pReader ) { if ( This->m_pReader != NULL ) { IAsyncReader_Release( This->m_pReader ); This->m_pReader = NULL; } if ( pReader != NULL ) { This->m_pReader = pReader; IAsyncReader_AddRef(This->m_pReader); } } static void CParserImpl_ReleaseOutPins( CParserImpl* This ) { ULONG nIndex; if ( This->m_ppOutPins != NULL ) { for ( nIndex = 0; nIndex < This->m_cOutStreams; nIndex++ ) { if ( This->m_ppOutPins[nIndex] != NULL ) { IUnknown_Release(This->m_ppOutPins[nIndex]->unk.punkControl); This->m_ppOutPins[nIndex] = NULL; } } QUARTZ_FreeMem(This->m_ppOutPins); This->m_ppOutPins = NULL; } This->m_cOutStreams = 0; } static BOOL CParserImpl_OutPinsAreConnected( CParserImpl* This ) { QUARTZ_CompListItem* pItem; IPin* pPin; IPin* pPinPeer; HRESULT hr; QUARTZ_CompList_Lock( This->basefilter.pOutPins ); pItem = QUARTZ_CompList_GetFirst( This->basefilter.pOutPins ); while ( pItem != NULL ) { if ( pItem == NULL ) break; pPin = (IPin*)QUARTZ_CompList_GetItemPtr(pItem); pPinPeer = NULL; hr = IPin_ConnectedTo(pPin,&pPinPeer); if ( hr == S_OK && pPinPeer != NULL ) { IPin_Release(pPinPeer); return TRUE; } pItem = QUARTZ_CompList_GetNext( This->basefilter.pOutPins, pItem ); } QUARTZ_CompList_Unlock( This->basefilter.pOutPins ); return FALSE; } static void CParserImpl_ReleaseListOfOutPins( CParserImpl* This ) { QUARTZ_CompListItem* pItem; QUARTZ_CompList_Lock( This->basefilter.pOutPins ); while ( 1 ) { pItem = QUARTZ_CompList_GetFirst( This->basefilter.pOutPins ); if ( pItem == NULL ) break; QUARTZ_CompList_RemoveComp( This->basefilter.pOutPins, QUARTZ_CompList_GetItemPtr(pItem) ); } QUARTZ_CompList_Unlock( This->basefilter.pOutPins ); } static HRESULT CParserImpl_BeginThread( CParserImpl* This ) { DWORD dwRes; HANDLE hEvents[2]; if ( This->m_hEventInit != (HANDLE)NULL && This->m_hThread != (HANDLE)NULL ) return NOERROR; This->m_hEventInit = CreateEventA(NULL,TRUE,FALSE,NULL); if ( This->m_hEventInit == (HANDLE)NULL ) return E_OUTOFMEMORY; /* create the processing thread. */ This->m_hThread = CreateThread( NULL, 0, CParserImplThread_Entry, (LPVOID)This, 0, &This->m_dwThreadId ); if ( This->m_hThread == (HANDLE)NULL ) return E_FAIL; hEvents[0] = This->m_hEventInit; hEvents[1] = This->m_hThread; dwRes = WaitForMultipleObjects(2,hEvents,FALSE,INFINITE); if ( dwRes != WAIT_OBJECT_0 ) return E_FAIL; return NOERROR; } static void CParserImpl_EndThread( CParserImpl* This ) { TRACE("(%p)\n",This); if ( This->m_hThread != (HANDLE)NULL ) { if ( PostThreadMessageA( This->m_dwThreadId, QUARTZ_MSG_EXITTHREAD, 0, 0 ) ) { WaitForSingleObject( This->m_hThread, INFINITE ); } CloseHandle( This->m_hThread ); This->m_hThread = (HANDLE)NULL; This->m_dwThreadId = 0; } if ( This->m_hEventInit != (HANDLE)NULL ) { CloseHandle( This->m_hEventInit ); This->m_hEventInit = (HANDLE)NULL; } } static HRESULT CParserImpl_MemCommit( CParserImpl* This ) { HRESULT hr; ULONG nIndex; IMemAllocator* pAlloc; TRACE("(%p)\n",This); if ( This->m_pAllocator == NULL ) return E_UNEXPECTED; hr = IMemAllocator_Commit( This->m_pAllocator ); if ( FAILED(hr) ) return hr; if ( This->m_ppOutPins != NULL && This->m_cOutStreams > 0 ) { for ( nIndex = 0; nIndex < This->m_cOutStreams; nIndex++ ) { pAlloc = This->m_ppOutPins[nIndex]->m_pOutPinAllocator; if ( pAlloc != NULL && pAlloc != This->m_pAllocator ) { hr = IMemAllocator_Commit( pAlloc ); if ( FAILED(hr) ) return hr; } } } return NOERROR; } static void CParserImpl_MemDecommit( CParserImpl* This ) { ULONG nIndex; IMemAllocator* pAlloc; TRACE("(%p)\n",This); if ( This->m_pAllocator != NULL ) IMemAllocator_Decommit( This->m_pAllocator ); if ( This->m_ppOutPins != NULL && This->m_cOutStreams > 0 ) { for ( nIndex = 0; nIndex < This->m_cOutStreams; nIndex++ ) { pAlloc = This->m_ppOutPins[nIndex]->m_pOutPinAllocator; if ( pAlloc != NULL ) IMemAllocator_Decommit( pAlloc ); } } } /*************************************************************************** * * CParserImpl methods * */ static HRESULT CParserImpl_OnActive( CBaseFilterImpl* pImpl ) { CParserImpl_THIS(pImpl,basefilter); TRACE( "(%p)\n", This ); if ( !CParserImpl_OutPinsAreConnected(This) ) return NOERROR; return NOERROR; } static HRESULT CParserImpl_OnInactive( CBaseFilterImpl* pImpl ) { CParserImpl_THIS(pImpl,basefilter); HRESULT hr; TRACE( "(%p)\n", This ); if ( !CParserImpl_OutPinsAreConnected(This) ) return NOERROR; hr = CParserImpl_MemCommit(This); if ( FAILED(hr) ) return hr; hr = CParserImpl_BeginThread(This); if ( FAILED(hr) ) { CParserImpl_EndThread(This); return hr; } return NOERROR; } static HRESULT CParserImpl_OnStop( CBaseFilterImpl* pImpl ) { CParserImpl_THIS(pImpl,basefilter); FIXME( "(%p)\n", This ); CParserImpl_EndThread(This); CParserImpl_MemDecommit(This); This->m_bSendEOS = FALSE; /* FIXME - reset streams. */ return NOERROR; } static const CBaseFilterHandlers filterhandlers = { CParserImpl_OnActive, /* pOnActive */ CParserImpl_OnInactive, /* pOnInactive */ CParserImpl_OnStop, /* pOnStop */ }; /*************************************************************************** * * CParserInPinImpl methods * */ static HRESULT CParserInPinImpl_OnPreConnect( CPinBaseImpl* pImpl, IPin* pPin ) { CParserInPinImpl_THIS(pImpl,pin); HRESULT hr; ULONG nIndex; IUnknown* punk; IAsyncReader* pReader = NULL; LPCWSTR pwszOutPinName; IMemAllocator* pAllocActual; AM_MEDIA_TYPE* pmt; TRACE("(%p,%p)\n",This,pPin); if ( This->pParser->m_pHandler->pInitParser == NULL || This->pParser->m_pHandler->pUninitParser == NULL || This->pParser->m_pHandler->pGetOutPinName == NULL || This->pParser->m_pHandler->pGetStreamType == NULL || This->pParser->m_pHandler->pCheckStreamType == NULL || This->pParser->m_pHandler->pGetAllocProp == NULL || This->pParser->m_pHandler->pGetNextRequest == NULL ) { FIXME("this parser is not implemented.\n"); return E_NOTIMPL; } /* at first, release all output pins. */ if ( CParserImpl_OutPinsAreConnected(This->pParser) ) return E_FAIL; CParserImpl_ReleaseListOfOutPins(This->pParser); CParserImpl_ReleaseOutPins(This->pParser); CParserImpl_SetAsyncReader( This->pParser, NULL ); hr = IPin_QueryInterface( pPin, &IID_IAsyncReader, (void**)&pReader ); if ( FAILED(hr) ) return hr; CParserImpl_SetAsyncReader( This->pParser, pReader ); IAsyncReader_Release(pReader); /* initialize parser. */ hr = This->pParser->m_pHandler->pInitParser(This->pParser,&This->pParser->m_cOutStreams); if ( FAILED(hr) ) return hr; This->pParser->m_ppOutPins = (CParserOutPinImpl**)QUARTZ_AllocMem( sizeof(CParserOutPinImpl*) * This->pParser->m_cOutStreams ); if ( This->pParser->m_ppOutPins == NULL ) return E_OUTOFMEMORY; for ( nIndex = 0; nIndex < This->pParser->m_cOutStreams; nIndex++ ) This->pParser->m_ppOutPins[nIndex] = NULL; /* create and initialize an allocator. */ hr = This->pParser->m_pHandler->pGetAllocProp(This->pParser,&This->pParser->m_propAlloc); if ( FAILED(hr) ) return hr; if ( This->pParser->m_propAlloc.cbAlign == 0 ) This->pParser->m_propAlloc.cbAlign = 1; if ( This->pParser->m_pAllocator == NULL ) { hr = QUARTZ_CreateMemoryAllocator(NULL,(void**)&punk); if ( FAILED(hr) ) return hr; hr = IUnknown_QueryInterface( punk, &IID_IMemAllocator, (void**)&This->pParser->m_pAllocator ); IUnknown_Release(punk); if ( FAILED(hr) ) return hr; } pAllocActual = NULL; hr = IAsyncReader_RequestAllocator(pReader,This->pParser->m_pAllocator,&This->pParser->m_propAlloc,&pAllocActual); if ( FAILED(hr) ) return hr; IMemAllocator_Release(This->pParser->m_pAllocator); This->pParser->m_pAllocator = pAllocActual; /* create output pins. */ for ( nIndex = 0; nIndex < This->pParser->m_cOutStreams; nIndex++ ) { pwszOutPinName = This->pParser->m_pHandler->pGetOutPinName(This->pParser,nIndex); if ( pwszOutPinName == NULL ) return E_FAIL; hr = QUARTZ_CreateParserOutPin( This->pParser, &This->pParser->m_csParser, &This->pParser->m_ppOutPins[nIndex], nIndex, pwszOutPinName ); if ( SUCCEEDED(hr) ) hr = QUARTZ_CompList_AddTailComp( This->pParser->basefilter.pOutPins, (IUnknown*)&(This->pParser->m_ppOutPins[nIndex]->pin), NULL, 0 ); if ( FAILED(hr) ) return hr; pmt = &This->pParser->m_ppOutPins[nIndex]->m_mtOut; QUARTZ_MediaType_Free( pmt ); ZeroMemory( pmt, sizeof(AM_MEDIA_TYPE) ); hr = This->pParser->m_pHandler->pGetStreamType(This->pParser,nIndex,pmt); if ( FAILED(hr) ) { ZeroMemory( pmt, sizeof(AM_MEDIA_TYPE) ); return hr; } This->pParser->m_ppOutPins[nIndex]->pin.cAcceptTypes = 1; This->pParser->m_ppOutPins[nIndex]->pin.pmtAcceptTypes = pmt; } return NOERROR; } static HRESULT CParserInPinImpl_OnDisconnect( CPinBaseImpl* pImpl ) { CParserInPinImpl_THIS(pImpl,pin); CParserImpl_OnInactive(&This->pParser->basefilter); CParserImpl_OnStop(&This->pParser->basefilter); if ( This->pParser->m_pHandler->pUninitParser != NULL ) This->pParser->m_pHandler->pUninitParser(This->pParser); CParserImpl_SetAsyncReader( This->pParser, NULL ); if ( This->pParser->m_pAllocator != NULL ) { IMemAllocator_Decommit(This->pParser->m_pAllocator); IMemAllocator_Release(This->pParser->m_pAllocator); This->pParser->m_pAllocator = NULL; } return NOERROR; } static HRESULT CParserInPinImpl_CheckMediaType( CPinBaseImpl* pImpl, const AM_MEDIA_TYPE* pmt ) { CParserInPinImpl_THIS(pImpl,pin); TRACE("(%p,%p)\n",This,pmt); if ( !IsEqualGUID( &pmt->majortype, &MEDIATYPE_Stream ) ) return E_FAIL; return NOERROR; } static const CBasePinHandlers inputpinhandlers = { CParserInPinImpl_OnPreConnect, /* pOnPreConnect */ NULL, /* pOnPostConnect */ CParserInPinImpl_OnDisconnect, /* pOnDisconnect */ CParserInPinImpl_CheckMediaType, /* pCheckMediaType */ NULL, /* pQualityNotify */ NULL, /* pReceive */ NULL, /* pReceiveCanBlock */ NULL, /* pEndOfStream */ NULL, /* pBeginFlush */ NULL, /* pEndFlush */ NULL, /* pNewSegment */ }; /*************************************************************************** * * CParserOutPinImpl methods * */ static HRESULT CParserOutPinImpl_OnPostConnect( CPinBaseImpl* pImpl, IPin* pPin ) { CParserOutPinImpl_THIS(pImpl,pin); ALLOCATOR_PROPERTIES propReq; ALLOCATOR_PROPERTIES propActual; IMemAllocator* pAllocator; HRESULT hr; BOOL bNewAllocator = FALSE; TRACE("(%p,%p)\n",This,pPin); if ( This->pin.pMemInputPinConnectedTo == NULL ) return E_UNEXPECTED; if ( This->m_pOutPinAllocator != NULL ) { IMemAllocator_Release(This->m_pOutPinAllocator); This->m_pOutPinAllocator = NULL; } /* try to use This->pParser->m_pAllocator. */ ZeroMemory( &propReq, sizeof(ALLOCATOR_PROPERTIES) ); hr = IMemInputPin_GetAllocatorRequirements( This->pin.pMemInputPinConnectedTo, &propReq ); if ( propReq.cbAlign != 0 ) { if ( This->pParser->m_propAlloc.cbAlign != ( This->pParser->m_propAlloc.cbAlign / propReq.cbAlign * propReq.cbAlign ) ) bNewAllocator = TRUE; } if ( propReq.cbPrefix != 0 ) bNewAllocator = TRUE; if ( !bNewAllocator ) { hr = IMemInputPin_NotifyAllocator( This->pin.pMemInputPinConnectedTo, This->pParser->m_pAllocator, FALSE ); if ( hr == NOERROR ) { This->m_pOutPinAllocator = This->pParser->m_pAllocator; IMemAllocator_AddRef(This->m_pOutPinAllocator); return NOERROR; } } hr = IMemInputPin_GetAllocator( This->pin.pMemInputPinConnectedTo, &pAllocator ); if ( FAILED(hr) ) return hr; hr = IMemAllocator_SetProperties( pAllocator, &This->pParser->m_propAlloc, &propActual ); if ( SUCCEEDED(hr) ) hr = IMemInputPin_NotifyAllocator( This->pin.pMemInputPinConnectedTo, pAllocator, FALSE ); if ( FAILED(hr) ) { IMemAllocator_Release(pAllocator); return hr; } This->m_pOutPinAllocator = pAllocator; return NOERROR; } static HRESULT CParserOutPinImpl_OnDisconnect( CPinBaseImpl* pImpl ) { CParserOutPinImpl_THIS(pImpl,pin); if ( This->m_pOutPinAllocator != NULL ) { IMemAllocator_Release(This->m_pOutPinAllocator); This->m_pOutPinAllocator = NULL; } return NOERROR; } static HRESULT CParserOutPinImpl_CheckMediaType( CPinBaseImpl* pImpl, const AM_MEDIA_TYPE* pmt ) { CParserOutPinImpl_THIS(pImpl,pin); HRESULT hr; TRACE("(%p,%p)\n",This,pmt); if ( pmt == NULL ) return E_POINTER; if ( This->pParser->m_pHandler->pCheckStreamType == NULL ) return E_NOTIMPL; hr = This->pParser->m_pHandler->pCheckStreamType( This->pParser, This->nStreamIndex, pmt ); if ( FAILED(hr) ) return hr; return NOERROR; } static const CBasePinHandlers outputpinhandlers = { NULL, /* pOnPreConnect */ CParserOutPinImpl_OnPostConnect, /* pOnPostConnect */ CParserOutPinImpl_OnDisconnect, /* pOnDisconnect */ CParserOutPinImpl_CheckMediaType, /* pCheckMediaType */ NULL, /* pQualityNotify */ OutputPinSync_Receive, /* pReceive */ OutputPinSync_ReceiveCanBlock, /* pReceiveCanBlock */ OutputPinSync_EndOfStream, /* pEndOfStream */ OutputPinSync_BeginFlush, /* pBeginFlush */ OutputPinSync_EndFlush, /* pEndFlush */ OutputPinSync_NewSegment, /* pNewSegment */ }; /*************************************************************************** * * new/delete CParserImpl * */ /* can I use offsetof safely? - FIXME? */ static QUARTZ_IFEntry FilterIFEntries[] = { { &IID_IPersist, offsetof(CParserImpl,basefilter)-offsetof(CParserImpl,unk) }, { &IID_IMediaFilter, offsetof(CParserImpl,basefilter)-offsetof(CParserImpl,unk) }, { &IID_IBaseFilter, offsetof(CParserImpl,basefilter)-offsetof(CParserImpl,unk) }, }; static void QUARTZ_DestroyParser(IUnknown* punk) { CParserImpl_THIS(punk,unk); TRACE( "(%p)\n", This ); if ( This->m_pInPin != NULL ) CParserInPinImpl_OnDisconnect(&This->m_pInPin->pin); CParserImpl_SetAsyncReader( This, NULL ); if ( This->m_pAllocator != NULL ) { IMemAllocator_Release(This->m_pAllocator); This->m_pAllocator = NULL; } if ( This->m_pInPin != NULL ) { IUnknown_Release(This->m_pInPin->unk.punkControl); This->m_pInPin = NULL; } CParserImpl_ReleaseOutPins( This ); DeleteCriticalSection( &This->m_csParser ); CBaseFilterImpl_UninitIBaseFilter(&This->basefilter); } HRESULT QUARTZ_CreateParser( IUnknown* punkOuter,void** ppobj, const CLSID* pclsidParser, LPCWSTR pwszParserName, LPCWSTR pwszInPinName, const ParserHandlers* pHandler ) { CParserImpl* This = NULL; HRESULT hr; TRACE("(%p,%p)\n",punkOuter,ppobj); This = (CParserImpl*) QUARTZ_AllocObj( sizeof(CParserImpl) ); if ( This == NULL ) return E_OUTOFMEMORY; ZeroMemory( This, sizeof(CParserImpl) ); This->m_pInPin = NULL; This->m_cOutStreams = 0; This->m_ppOutPins = NULL; This->m_pReader = NULL; This->m_pAllocator = NULL; ZeroMemory( &This->m_propAlloc, sizeof(ALLOCATOR_PROPERTIES) ); This->m_hEventInit = (HANDLE)NULL; This->m_hThread = (HANDLE)NULL; This->m_dwThreadId = 0; This->m_bSendEOS = FALSE; This->m_pHandler = pHandler; This->m_pUserData = NULL; QUARTZ_IUnkInit( &This->unk, punkOuter ); hr = CBaseFilterImpl_InitIBaseFilter( &This->basefilter, This->unk.punkControl, pclsidParser, pwszParserName, &filterhandlers ); if ( SUCCEEDED(hr) ) { /* construct this class. */ hr = S_OK; if ( FAILED(hr) ) { CBaseFilterImpl_UninitIBaseFilter(&This->basefilter); } } if ( FAILED(hr) ) { QUARTZ_FreeObj(This); return hr; } This->unk.pEntries = FilterIFEntries; This->unk.dwEntries = sizeof(FilterIFEntries)/sizeof(FilterIFEntries[0]); This->unk.pOnFinalRelease = QUARTZ_DestroyParser; InitializeCriticalSection( &This->m_csParser ); /* create the input pin. */ hr = QUARTZ_CreateParserInPin( This, &This->m_csParser, &This->m_pInPin, pwszInPinName ); if ( SUCCEEDED(hr) ) hr = QUARTZ_CompList_AddComp( This->basefilter.pInPins, (IUnknown*)&(This->m_pInPin->pin), NULL, 0 ); if ( FAILED(hr) ) { IUnknown_Release( This->unk.punkControl ); return hr; } *ppobj = (void*)&(This->unk); return S_OK; } /*************************************************************************** * * new/delete CParserInPinImpl * */ /* can I use offsetof safely? - FIXME? */ static QUARTZ_IFEntry InPinIFEntries[] = { { &IID_IPin, offsetof(CParserInPinImpl,pin)-offsetof(CParserInPinImpl,unk) }, { &IID_IMemInputPin, offsetof(CParserInPinImpl,meminput)-offsetof(CParserInPinImpl,unk) }, }; static void QUARTZ_DestroyParserInPin(IUnknown* punk) { CParserInPinImpl_THIS(punk,unk); TRACE( "(%p)\n", This ); CPinBaseImpl_UninitIPin( &This->pin ); CMemInputPinBaseImpl_UninitIMemInputPin( &This->meminput ); } HRESULT QUARTZ_CreateParserInPin( CParserImpl* pFilter, CRITICAL_SECTION* pcsPin, CParserInPinImpl** ppPin, LPCWSTR pwszPinName ) { CParserInPinImpl* This = NULL; HRESULT hr; TRACE("(%p,%p,%p)\n",pFilter,pcsPin,ppPin); This = (CParserInPinImpl*) QUARTZ_AllocObj( sizeof(CParserInPinImpl) ); if ( This == NULL ) return E_OUTOFMEMORY; QUARTZ_IUnkInit( &This->unk, NULL ); This->pParser = pFilter; hr = CPinBaseImpl_InitIPin( &This->pin, This->unk.punkControl, pcsPin, NULL, &pFilter->basefilter, pwszPinName, FALSE, &inputpinhandlers ); if ( SUCCEEDED(hr) ) { hr = CMemInputPinBaseImpl_InitIMemInputPin( &This->meminput, This->unk.punkControl, &This->pin ); if ( FAILED(hr) ) { CPinBaseImpl_UninitIPin( &This->pin ); } } if ( FAILED(hr) ) { QUARTZ_FreeObj(This); return hr; } This->unk.pEntries = InPinIFEntries; This->unk.dwEntries = sizeof(InPinIFEntries)/sizeof(InPinIFEntries[0]); This->unk.pOnFinalRelease = QUARTZ_DestroyParserInPin; *ppPin = This; TRACE("returned successfully.\n"); return S_OK; } /*************************************************************************** * * new/delete CParserOutPinImpl * */ /* can I use offsetof safely? - FIXME? */ static QUARTZ_IFEntry OutPinIFEntries[] = { { &IID_IPin, offsetof(CParserOutPinImpl,pin)-offsetof(CParserOutPinImpl,unk) }, { &IID_IQualityControl, offsetof(CParserOutPinImpl,qcontrol)-offsetof(CParserOutPinImpl,unk) }, { &IID_IMediaSeeking, offsetof(CParserOutPinImpl,mediaseeking)-offsetof(CParserOutPinImpl,unk) }, { &IID_IMediaPosition, offsetof(CParserOutPinImpl,mediaposition)-offsetof(CParserOutPinImpl,unk) }, }; static void QUARTZ_DestroyParserOutPin(IUnknown* punk) { CParserOutPinImpl_THIS(punk,unk); TRACE( "(%p)\n", This ); QUARTZ_MediaType_Free( &This->m_mtOut ); if ( This->m_pOutPinAllocator != NULL ) IMemAllocator_Release(This->m_pOutPinAllocator); CParserOutPinImpl_UninitIMediaPosition(This); CParserOutPinImpl_UninitIMediaSeeking(This); CQualityControlPassThruImpl_UninitIQualityControl( &This->qcontrol ); CPinBaseImpl_UninitIPin( &This->pin ); } HRESULT QUARTZ_CreateParserOutPin( CParserImpl* pFilter, CRITICAL_SECTION* pcsPin, CParserOutPinImpl** ppPin, ULONG nStreamIndex, LPCWSTR pwszPinName ) { CParserOutPinImpl* This = NULL; HRESULT hr; TRACE("(%p,%p,%p)\n",pFilter,pcsPin,ppPin); This = (CParserOutPinImpl*) QUARTZ_AllocObj( sizeof(CParserOutPinImpl) ); if ( This == NULL ) return E_OUTOFMEMORY; QUARTZ_IUnkInit( &This->unk, NULL ); This->pParser = pFilter; This->nStreamIndex = nStreamIndex; ZeroMemory( &This->m_mtOut, sizeof(AM_MEDIA_TYPE) ); This->m_pOutPinAllocator = NULL; This->m_pUserData = NULL; This->m_bReqUsed = FALSE; This->m_pReqSample = NULL; This->m_llReqStart = 0; This->m_lReqLength = 0; This->m_rtReqStart = 0; This->m_rtReqStop = 0; hr = CPinBaseImpl_InitIPin( &This->pin, This->unk.punkControl, pcsPin, NULL, &pFilter->basefilter, pwszPinName, TRUE, &outputpinhandlers ); if ( SUCCEEDED(hr) ) { hr = CQualityControlPassThruImpl_InitIQualityControl( &This->qcontrol, This->unk.punkControl, &This->pin ); if ( SUCCEEDED(hr) ) { hr = CParserOutPinImpl_InitIMediaSeeking(This); if ( SUCCEEDED(hr) ) { hr = CParserOutPinImpl_InitIMediaPosition(This); if ( FAILED(hr) ) { CParserOutPinImpl_UninitIMediaSeeking(This); } } if ( FAILED(hr) ) { CQualityControlPassThruImpl_UninitIQualityControl( &This->qcontrol ); } } if ( FAILED(hr) ) { CPinBaseImpl_UninitIPin( &This->pin ); } } if ( FAILED(hr) ) { QUARTZ_FreeObj(This); return hr; } This->unk.pEntries = OutPinIFEntries; This->unk.dwEntries = sizeof(OutPinIFEntries)/sizeof(OutPinIFEntries[0]); This->unk.pOnFinalRelease = QUARTZ_DestroyParserOutPin; *ppPin = This; TRACE("returned successfully.\n"); return S_OK; } /*************************************************************************** * * IMediaSeeking for CParserOutPinImpl * */ static HRESULT WINAPI IMediaSeeking_fnQueryInterface(IMediaSeeking* iface,REFIID riid,void** ppobj) { CParserOutPinImpl_THIS(iface,mediaseeking); TRACE("(%p)->()\n",This); return IUnknown_QueryInterface(This->unk.punkControl,riid,ppobj); } static ULONG WINAPI IMediaSeeking_fnAddRef(IMediaSeeking* iface) { CParserOutPinImpl_THIS(iface,mediaseeking); TRACE("(%p)->()\n",This); return IUnknown_AddRef(This->unk.punkControl); } static ULONG WINAPI IMediaSeeking_fnRelease(IMediaSeeking* iface) { CParserOutPinImpl_THIS(iface,mediaseeking); TRACE("(%p)->()\n",This); return IUnknown_Release(This->unk.punkControl); } static HRESULT WINAPI IMediaSeeking_fnGetCapabilities(IMediaSeeking* iface,DWORD* pdwCaps) { CParserOutPinImpl_THIS(iface,mediaseeking); FIXME("(%p)->() stub!\n",This); return E_NOTIMPL; } static HRESULT WINAPI IMediaSeeking_fnCheckCapabilities(IMediaSeeking* iface,DWORD* pdwCaps) { CParserOutPinImpl_THIS(iface,mediaseeking); FIXME("(%p)->() stub!\n",This); return E_NOTIMPL; } static HRESULT WINAPI IMediaSeeking_fnIsFormatSupported(IMediaSeeking* iface,const GUID* pidFormat) { CParserOutPinImpl_THIS(iface,mediaseeking); FIXME("(%p)->() stub!\n",This); return E_NOTIMPL; } static HRESULT WINAPI IMediaSeeking_fnQueryPreferredFormat(IMediaSeeking* iface,GUID* pidFormat) { CParserOutPinImpl_THIS(iface,mediaseeking); FIXME("(%p)->() stub!\n",This); return E_NOTIMPL; } static HRESULT WINAPI IMediaSeeking_fnGetTimeFormat(IMediaSeeking* iface,GUID* pidFormat) { CParserOutPinImpl_THIS(iface,mediaseeking); FIXME("(%p)->() stub!\n",This); return E_NOTIMPL; } static HRESULT WINAPI IMediaSeeking_fnIsUsingTimeFormat(IMediaSeeking* iface,const GUID* pidFormat) { CParserOutPinImpl_THIS(iface,mediaseeking); FIXME("(%p)->() stub!\n",This); return E_NOTIMPL; } static HRESULT WINAPI IMediaSeeking_fnSetTimeFormat(IMediaSeeking* iface,const GUID* pidFormat) { CParserOutPinImpl_THIS(iface,mediaseeking); FIXME("(%p)->() stub!\n",This); return E_NOTIMPL; } static HRESULT WINAPI IMediaSeeking_fnGetDuration(IMediaSeeking* iface,LONGLONG* pllDuration) { CParserOutPinImpl_THIS(iface,mediaseeking); /* the following line may produce too many FIXMEs... */ FIXME("(%p)->() stub!\n",This); return E_NOTIMPL; } static HRESULT WINAPI IMediaSeeking_fnGetStopPosition(IMediaSeeking* iface,LONGLONG* pllPos) { CParserOutPinImpl_THIS(iface,mediaseeking); FIXME("(%p)->() stub!\n",This); return E_NOTIMPL; } static HRESULT WINAPI IMediaSeeking_fnGetCurrentPosition(IMediaSeeking* iface,LONGLONG* pllPos) { CParserOutPinImpl_THIS(iface,mediaseeking); FIXME("(%p)->() stub!\n",This); return E_NOTIMPL; } static HRESULT WINAPI IMediaSeeking_fnConvertTimeFormat(IMediaSeeking* iface,LONGLONG* pllOut,const GUID* pidFmtOut,LONGLONG llIn,const GUID* pidFmtIn) { CParserOutPinImpl_THIS(iface,mediaseeking); FIXME("(%p)->() stub!\n",This); return E_NOTIMPL; } static HRESULT WINAPI IMediaSeeking_fnSetPositions(IMediaSeeking* iface,LONGLONG* pllCur,DWORD dwCurFlags,LONGLONG* pllStop,DWORD dwStopFlags) { CParserOutPinImpl_THIS(iface,mediaseeking); FIXME("(%p)->() stub!\n",This); return E_NOTIMPL; } static HRESULT WINAPI IMediaSeeking_fnGetPositions(IMediaSeeking* iface,LONGLONG* pllCur,LONGLONG* pllStop) { CParserOutPinImpl_THIS(iface,mediaseeking); FIXME("(%p)->() stub!\n",This); return E_NOTIMPL; } static HRESULT WINAPI IMediaSeeking_fnGetAvailable(IMediaSeeking* iface,LONGLONG* pllFirst,LONGLONG* pllLast) { CParserOutPinImpl_THIS(iface,mediaseeking); FIXME("(%p)->() stub!\n",This); return E_NOTIMPL; } static HRESULT WINAPI IMediaSeeking_fnSetRate(IMediaSeeking* iface,double dblRate) { CParserOutPinImpl_THIS(iface,mediaseeking); FIXME("(%p)->() stub!\n",This); return E_NOTIMPL; } static HRESULT WINAPI IMediaSeeking_fnGetRate(IMediaSeeking* iface,double* pdblRate) { CParserOutPinImpl_THIS(iface,mediaseeking); FIXME("(%p)->() stub!\n",This); return E_NOTIMPL; } static HRESULT WINAPI IMediaSeeking_fnGetPreroll(IMediaSeeking* iface,LONGLONG* pllPreroll) { CParserOutPinImpl_THIS(iface,mediaseeking); FIXME("(%p)->() stub!\n",This); return E_NOTIMPL; } static ICOM_VTABLE(IMediaSeeking) imediaseeking = { ICOM_MSVTABLE_COMPAT_DummyRTTIVALUE /* IUnknown fields */ IMediaSeeking_fnQueryInterface, IMediaSeeking_fnAddRef, IMediaSeeking_fnRelease, /* IMediaSeeking fields */ IMediaSeeking_fnGetCapabilities, IMediaSeeking_fnCheckCapabilities, IMediaSeeking_fnIsFormatSupported, IMediaSeeking_fnQueryPreferredFormat, IMediaSeeking_fnGetTimeFormat, IMediaSeeking_fnIsUsingTimeFormat, IMediaSeeking_fnSetTimeFormat, IMediaSeeking_fnGetDuration, IMediaSeeking_fnGetStopPosition, IMediaSeeking_fnGetCurrentPosition, IMediaSeeking_fnConvertTimeFormat, IMediaSeeking_fnSetPositions, IMediaSeeking_fnGetPositions, IMediaSeeking_fnGetAvailable, IMediaSeeking_fnSetRate, IMediaSeeking_fnGetRate, IMediaSeeking_fnGetPreroll, }; HRESULT CParserOutPinImpl_InitIMediaSeeking( CParserOutPinImpl* This ) { TRACE("(%p)\n",This); ICOM_VTBL(&This->mediaseeking) = &imediaseeking; return NOERROR; } void CParserOutPinImpl_UninitIMediaSeeking( CParserOutPinImpl* This ) { TRACE("(%p)\n",This); } /*************************************************************************** * * IMediaPosition for CParserOutPinImpl * */ static HRESULT WINAPI IMediaPosition_fnQueryInterface(IMediaPosition* iface,REFIID riid,void** ppobj) { CParserOutPinImpl_THIS(iface,mediaposition); TRACE("(%p)->()\n",This); return IUnknown_QueryInterface(This->unk.punkControl,riid,ppobj); } static ULONG WINAPI IMediaPosition_fnAddRef(IMediaPosition* iface) { CParserOutPinImpl_THIS(iface,mediaposition); TRACE("(%p)->()\n",This); return IUnknown_AddRef(This->unk.punkControl); } static ULONG WINAPI IMediaPosition_fnRelease(IMediaPosition* iface) { CParserOutPinImpl_THIS(iface,mediaposition); TRACE("(%p)->()\n",This); return IUnknown_Release(This->unk.punkControl); } static HRESULT WINAPI IMediaPosition_fnGetTypeInfoCount(IMediaPosition* iface,UINT* pcTypeInfo) { CParserOutPinImpl_THIS(iface,mediaposition); FIXME("(%p)->() stub!\n",This); return E_NOTIMPL; } static HRESULT WINAPI IMediaPosition_fnGetTypeInfo(IMediaPosition* iface,UINT iTypeInfo, LCID lcid, ITypeInfo** ppobj) { CParserOutPinImpl_THIS(iface,mediaposition); FIXME("(%p)->() stub!\n",This); return E_NOTIMPL; } static HRESULT WINAPI IMediaPosition_fnGetIDsOfNames(IMediaPosition* iface,REFIID riid, LPOLESTR* ppwszName, UINT cNames, LCID lcid, DISPID* pDispId) { CParserOutPinImpl_THIS(iface,mediaposition); FIXME("(%p)->() stub!\n",This); return E_NOTIMPL; } static HRESULT WINAPI IMediaPosition_fnInvoke(IMediaPosition* iface,DISPID DispId, REFIID riid, LCID lcid, WORD wFlags, DISPPARAMS* pDispParams, VARIANT* pVarRes, EXCEPINFO* pExcepInfo, UINT* puArgErr) { CParserOutPinImpl_THIS(iface,mediaposition); FIXME("(%p)->() stub!\n",This); return E_NOTIMPL; } static HRESULT WINAPI IMediaPosition_fnget_Duration(IMediaPosition* iface,REFTIME* prefTime) { CParserOutPinImpl_THIS(iface,mediaposition); FIXME("(%p)->() stub!\n",This); return E_NOTIMPL; } static HRESULT WINAPI IMediaPosition_fnput_CurrentPosition(IMediaPosition* iface,REFTIME refTime) { CParserOutPinImpl_THIS(iface,mediaposition); FIXME("(%p)->() stub!\n",This); return E_NOTIMPL; } static HRESULT WINAPI IMediaPosition_fnget_CurrentPosition(IMediaPosition* iface,REFTIME* prefTime) { CParserOutPinImpl_THIS(iface,mediaposition); FIXME("(%p)->() stub!\n",This); return E_NOTIMPL; } static HRESULT WINAPI IMediaPosition_fnget_StopTime(IMediaPosition* iface,REFTIME* prefTime) { CParserOutPinImpl_THIS(iface,mediaposition); FIXME("(%p)->() stub!\n",This); return E_NOTIMPL; } static HRESULT WINAPI IMediaPosition_fnput_StopTime(IMediaPosition* iface,REFTIME refTime) { CParserOutPinImpl_THIS(iface,mediaposition); FIXME("(%p)->() stub!\n",This); return E_NOTIMPL; } static HRESULT WINAPI IMediaPosition_fnget_PrerollTime(IMediaPosition* iface,REFTIME* prefTime) { CParserOutPinImpl_THIS(iface,mediaposition); FIXME("(%p)->() stub!\n",This); return E_NOTIMPL; } static HRESULT WINAPI IMediaPosition_fnput_PrerollTime(IMediaPosition* iface,REFTIME refTime) { CParserOutPinImpl_THIS(iface,mediaposition); FIXME("(%p)->() stub!\n",This); return E_NOTIMPL; } static HRESULT WINAPI IMediaPosition_fnput_Rate(IMediaPosition* iface,double dblRate) { CParserOutPinImpl_THIS(iface,mediaposition); return IMediaSeeking_SetRate(CParserOutPinImpl_IMediaSeeking(This),dblRate); } static HRESULT WINAPI IMediaPosition_fnget_Rate(IMediaPosition* iface,double* pdblRate) { CParserOutPinImpl_THIS(iface,mediaposition); return IMediaSeeking_GetRate(CParserOutPinImpl_IMediaSeeking(This),pdblRate); } static HRESULT WINAPI IMediaPosition_fnCanSeekForward(IMediaPosition* iface,LONG* pCanSeek) { CParserOutPinImpl_THIS(iface,mediaposition); FIXME("(%p)->() stub!\n",This); return E_NOTIMPL; } static HRESULT WINAPI IMediaPosition_fnCanSeekBackward(IMediaPosition* iface,LONG* pCanSeek) { CParserOutPinImpl_THIS(iface,mediaposition); FIXME("(%p)->() stub!\n",This); return E_NOTIMPL; } static ICOM_VTABLE(IMediaPosition) imediaposition = { ICOM_MSVTABLE_COMPAT_DummyRTTIVALUE /* IUnknown fields */ IMediaPosition_fnQueryInterface, IMediaPosition_fnAddRef, IMediaPosition_fnRelease, /* IDispatch fields */ IMediaPosition_fnGetTypeInfoCount, IMediaPosition_fnGetTypeInfo, IMediaPosition_fnGetIDsOfNames, IMediaPosition_fnInvoke, /* IMediaPosition fields */ IMediaPosition_fnget_Duration, IMediaPosition_fnput_CurrentPosition, IMediaPosition_fnget_CurrentPosition, IMediaPosition_fnget_StopTime, IMediaPosition_fnput_StopTime, IMediaPosition_fnget_PrerollTime, IMediaPosition_fnput_PrerollTime, IMediaPosition_fnput_Rate, IMediaPosition_fnget_Rate, IMediaPosition_fnCanSeekForward, IMediaPosition_fnCanSeekBackward, }; HRESULT CParserOutPinImpl_InitIMediaPosition( CParserOutPinImpl* This ) { TRACE("(%p)\n",This); ICOM_VTBL(&This->mediaposition) = &imediaposition; return NOERROR; } void CParserOutPinImpl_UninitIMediaPosition( CParserOutPinImpl* This ) { TRACE("(%p)\n",This); }