//------------------------------------------------------------------------------ // File: CtlUtil.cpp // // Desc: DirectShow base classes. // // Copyright (c) 1992-2001 Microsoft Corporation. All rights reserved. //------------------------------------------------------------------------------ // Base classes implementing IDispatch parsing for the basic control dual // interfaces. Derive from these and implement just the custom method and // property methods. We also implement CPosPassThru that can be used by // renderers and transforms to pass by IMediaPosition and IMediaSeeking #include #include #include "seekpt.h" // 'bool' non standard reserved word #pragma warning(disable:4237) // --- CBaseDispatch implementation ---------- CBaseDispatch::~CBaseDispatch() { if (m_pti) { m_pti->Release(); } } // return 1 if we support GetTypeInfo STDMETHODIMP CBaseDispatch::GetTypeInfoCount(UINT * pctinfo) { CheckPointer(pctinfo,E_POINTER); ValidateReadWritePtr(pctinfo,sizeof(UINT *)); *pctinfo = 1; return S_OK; } typedef HRESULT (STDAPICALLTYPE *LPLOADTYPELIB)( const OLECHAR FAR *szFile, ITypeLib FAR* FAR* pptlib); typedef HRESULT (STDAPICALLTYPE *LPLOADREGTYPELIB)(REFGUID rguid, WORD wVerMajor, WORD wVerMinor, LCID lcid, ITypeLib FAR* FAR* pptlib); // attempt to find our type library STDMETHODIMP CBaseDispatch::GetTypeInfo( REFIID riid, UINT itinfo, LCID lcid, ITypeInfo ** pptinfo) { CheckPointer(pptinfo,E_POINTER); ValidateReadWritePtr(pptinfo,sizeof(ITypeInfo *)); HRESULT hr; *pptinfo = NULL; // we only support one type element if (0 != itinfo) { return TYPE_E_ELEMENTNOTFOUND; } if (NULL == pptinfo) { return E_POINTER; } // always look for neutral if (NULL == m_pti) { LPLOADTYPELIB lpfnLoadTypeLib; LPLOADREGTYPELIB lpfnLoadRegTypeLib; ITypeLib *ptlib; HINSTANCE hInst; static const char szTypeLib[] = "LoadTypeLib"; static const char szRegTypeLib[] = "LoadRegTypeLib"; static const WCHAR szControl[] = L"control.tlb"; // // Try to get the Ole32Aut.dll module handle. // hInst = LoadOLEAut32(); if (hInst == NULL) { DWORD dwError = GetLastError(); return AmHresultFromWin32(dwError); } lpfnLoadRegTypeLib = (LPLOADREGTYPELIB)GetProcAddress(hInst, szRegTypeLib); if (lpfnLoadRegTypeLib == NULL) { DWORD dwError = GetLastError(); return AmHresultFromWin32(dwError); } hr = (*lpfnLoadRegTypeLib)(LIBID_QuartzTypeLib, 1, 0, // version 1.0 lcid, &ptlib); if (FAILED(hr)) { // attempt to load directly - this will fill the // registry in if it finds it lpfnLoadTypeLib = (LPLOADTYPELIB)GetProcAddress(hInst, szTypeLib); if (lpfnLoadTypeLib == NULL) { DWORD dwError = GetLastError(); return AmHresultFromWin32(dwError); } hr = (*lpfnLoadTypeLib)(szControl, &ptlib); if (FAILED(hr)) { return hr; } } hr = ptlib->GetTypeInfoOfGuid( riid, &m_pti); ptlib->Release(); if (FAILED(hr)) { return hr; } } *pptinfo = m_pti; m_pti->AddRef(); return S_OK; } STDMETHODIMP CBaseDispatch::GetIDsOfNames( REFIID riid, OLECHAR ** rgszNames, UINT cNames, LCID lcid, DISPID * rgdispid) { // although the IDispatch riid is dead, we use this to pass from // the interface implementation class to us the iid we are talking about. ITypeInfo * pti; HRESULT hr = GetTypeInfo(riid, 0, lcid, &pti); if (SUCCEEDED(hr)) { hr = pti->GetIDsOfNames(rgszNames, cNames, rgdispid); pti->Release(); } return hr; } // --- CMediaControl implementation --------- CMediaControl::CMediaControl(const TCHAR * name,LPUNKNOWN pUnk) : CUnknown(name, pUnk) { } // expose our interfaces IMediaControl and IUnknown STDMETHODIMP CMediaControl::NonDelegatingQueryInterface(REFIID riid, void **ppv) { ValidateReadWritePtr(ppv,sizeof(PVOID)); if (riid == IID_IMediaControl) { return GetInterface( (IMediaControl *) this, ppv); } else { return CUnknown::NonDelegatingQueryInterface(riid, ppv); } } // return 1 if we support GetTypeInfo STDMETHODIMP CMediaControl::GetTypeInfoCount(UINT * pctinfo) { return m_basedisp.GetTypeInfoCount(pctinfo); } // attempt to find our type library STDMETHODIMP CMediaControl::GetTypeInfo( UINT itinfo, LCID lcid, ITypeInfo ** pptinfo) { return m_basedisp.GetTypeInfo( IID_IMediaControl, itinfo, lcid, pptinfo); } STDMETHODIMP CMediaControl::GetIDsOfNames( REFIID riid, OLECHAR ** rgszNames, UINT cNames, LCID lcid, DISPID * rgdispid) { return m_basedisp.GetIDsOfNames( IID_IMediaControl, rgszNames, cNames, lcid, rgdispid); } STDMETHODIMP CMediaControl::Invoke( DISPID dispidMember, REFIID riid, LCID lcid, WORD wFlags, DISPPARAMS * pdispparams, VARIANT * pvarResult, EXCEPINFO * pexcepinfo, UINT * puArgErr) { // this parameter is a dead leftover from an earlier interface if (IID_NULL != riid) { return DISP_E_UNKNOWNINTERFACE; } ITypeInfo * pti; HRESULT hr = GetTypeInfo(0, lcid, &pti); if (FAILED(hr)) { return hr; } hr = pti->Invoke( (IMediaControl *)this, dispidMember, wFlags, pdispparams, pvarResult, pexcepinfo, puArgErr); pti->Release(); return hr; } // --- CMediaEvent implementation ---------- CMediaEvent::CMediaEvent(const TCHAR * name,LPUNKNOWN pUnk) : CUnknown(name, pUnk) { } // expose our interfaces IMediaEvent and IUnknown STDMETHODIMP CMediaEvent::NonDelegatingQueryInterface(REFIID riid, void **ppv) { ValidateReadWritePtr(ppv,sizeof(PVOID)); if (riid == IID_IMediaEvent || riid == IID_IMediaEventEx) { return GetInterface( (IMediaEventEx *) this, ppv); } else { return CUnknown::NonDelegatingQueryInterface(riid, ppv); } } // return 1 if we support GetTypeInfo STDMETHODIMP CMediaEvent::GetTypeInfoCount(UINT * pctinfo) { return m_basedisp.GetTypeInfoCount(pctinfo); } // attempt to find our type library STDMETHODIMP CMediaEvent::GetTypeInfo( UINT itinfo, LCID lcid, ITypeInfo ** pptinfo) { return m_basedisp.GetTypeInfo( IID_IMediaEvent, itinfo, lcid, pptinfo); } STDMETHODIMP CMediaEvent::GetIDsOfNames( REFIID riid, OLECHAR ** rgszNames, UINT cNames, LCID lcid, DISPID * rgdispid) { return m_basedisp.GetIDsOfNames( IID_IMediaEvent, rgszNames, cNames, lcid, rgdispid); } STDMETHODIMP CMediaEvent::Invoke( DISPID dispidMember, REFIID riid, LCID lcid, WORD wFlags, DISPPARAMS * pdispparams, VARIANT * pvarResult, EXCEPINFO * pexcepinfo, UINT * puArgErr) { // this parameter is a dead leftover from an earlier interface if (IID_NULL != riid) { return DISP_E_UNKNOWNINTERFACE; } ITypeInfo * pti; HRESULT hr = GetTypeInfo(0, lcid, &pti); if (FAILED(hr)) { return hr; } hr = pti->Invoke( (IMediaEvent *)this, dispidMember, wFlags, pdispparams, pvarResult, pexcepinfo, puArgErr); pti->Release(); return hr; } // --- CMediaPosition implementation ---------- CMediaPosition::CMediaPosition(const TCHAR * name,LPUNKNOWN pUnk) : CUnknown(name, pUnk) { } CMediaPosition::CMediaPosition(const TCHAR * name, LPUNKNOWN pUnk, HRESULT * phr) : CUnknown(name, pUnk) { UNREFERENCED_PARAMETER(phr); } // expose our interfaces IMediaPosition and IUnknown STDMETHODIMP CMediaPosition::NonDelegatingQueryInterface(REFIID riid, void **ppv) { ValidateReadWritePtr(ppv,sizeof(PVOID)); if (riid == IID_IMediaPosition) { return GetInterface( (IMediaPosition *) this, ppv); } else { return CUnknown::NonDelegatingQueryInterface(riid, ppv); } } // return 1 if we support GetTypeInfo STDMETHODIMP CMediaPosition::GetTypeInfoCount(UINT * pctinfo) { return m_basedisp.GetTypeInfoCount(pctinfo); } // attempt to find our type library STDMETHODIMP CMediaPosition::GetTypeInfo( UINT itinfo, LCID lcid, ITypeInfo ** pptinfo) { return m_basedisp.GetTypeInfo( IID_IMediaPosition, itinfo, lcid, pptinfo); } STDMETHODIMP CMediaPosition::GetIDsOfNames( REFIID riid, OLECHAR ** rgszNames, UINT cNames, LCID lcid, DISPID * rgdispid) { return m_basedisp.GetIDsOfNames( IID_IMediaPosition, rgszNames, cNames, lcid, rgdispid); } STDMETHODIMP CMediaPosition::Invoke( DISPID dispidMember, REFIID riid, LCID lcid, WORD wFlags, DISPPARAMS * pdispparams, VARIANT * pvarResult, EXCEPINFO * pexcepinfo, UINT * puArgErr) { // this parameter is a dead leftover from an earlier interface if (IID_NULL != riid) { return DISP_E_UNKNOWNINTERFACE; } ITypeInfo * pti; HRESULT hr = GetTypeInfo(0, lcid, &pti); if (FAILED(hr)) { return hr; } hr = pti->Invoke( (IMediaPosition *)this, dispidMember, wFlags, pdispparams, pvarResult, pexcepinfo, puArgErr); pti->Release(); return hr; } // --- IMediaPosition and IMediaSeeking pass through class ---------- CPosPassThru::CPosPassThru(const TCHAR *pName, LPUNKNOWN pUnk, HRESULT *phr, IPin *pPin) : CMediaPosition(pName,pUnk), m_pPin(pPin) { if (pPin == NULL) { *phr = E_POINTER; return; } } // Expose our IMediaSeeking and IMediaPosition interfaces STDMETHODIMP CPosPassThru::NonDelegatingQueryInterface(REFIID riid,void **ppv) { CheckPointer(ppv,E_POINTER); *ppv = NULL; if (riid == IID_IMediaSeeking) { return GetInterface( static_cast(this), ppv); } return CMediaPosition::NonDelegatingQueryInterface(riid,ppv); } // Return the IMediaPosition interface from our peer HRESULT CPosPassThru::GetPeer(IMediaPosition ** ppMP) { *ppMP = NULL; IPin *pConnected; HRESULT hr = m_pPin->ConnectedTo(&pConnected); if (FAILED(hr)) { return E_NOTIMPL; } IMediaPosition * pMP; hr = pConnected->QueryInterface(IID_IMediaPosition, (void **) &pMP); pConnected->Release(); if (FAILED(hr)) { return E_NOTIMPL; } *ppMP = pMP; return S_OK; } // Return the IMediaSeeking interface from our peer HRESULT CPosPassThru::GetPeerSeeking(IMediaSeeking ** ppMS) { *ppMS = NULL; IPin *pConnected; HRESULT hr = m_pPin->ConnectedTo(&pConnected); if (FAILED(hr)) { return E_NOTIMPL; } IMediaSeeking * pMS; hr = pConnected->QueryInterface(IID_IMediaSeeking, (void **) &pMS); pConnected->Release(); if (FAILED(hr)) { return E_NOTIMPL; } *ppMS = pMS; return S_OK; } // --- IMediaSeeking methods ---------- STDMETHODIMP CPosPassThru::GetCapabilities(DWORD * pCaps) { IMediaSeeking* pMS; HRESULT hr = GetPeerSeeking(&pMS); if (FAILED(hr)) { return hr; } hr = pMS->GetCapabilities(pCaps); pMS->Release(); return hr; } STDMETHODIMP CPosPassThru::CheckCapabilities(DWORD * pCaps) { IMediaSeeking* pMS; HRESULT hr = GetPeerSeeking(&pMS); if (FAILED(hr)) { return hr; } hr = pMS->CheckCapabilities(pCaps); pMS->Release(); return hr; } STDMETHODIMP CPosPassThru::IsFormatSupported(const GUID * pFormat) { IMediaSeeking* pMS; HRESULT hr = GetPeerSeeking(&pMS); if (FAILED(hr)) { return hr; } hr = pMS->IsFormatSupported(pFormat); pMS->Release(); return hr; } STDMETHODIMP CPosPassThru::QueryPreferredFormat(GUID *pFormat) { IMediaSeeking* pMS; HRESULT hr = GetPeerSeeking(&pMS); if (FAILED(hr)) { return hr; } hr = pMS->QueryPreferredFormat(pFormat); pMS->Release(); return hr; } STDMETHODIMP CPosPassThru::SetTimeFormat(const GUID * pFormat) { IMediaSeeking* pMS; HRESULT hr = GetPeerSeeking(&pMS); if (FAILED(hr)) { return hr; } hr = pMS->SetTimeFormat(pFormat); pMS->Release(); return hr; } STDMETHODIMP CPosPassThru::GetTimeFormat(GUID *pFormat) { IMediaSeeking* pMS; HRESULT hr = GetPeerSeeking(&pMS); if (FAILED(hr)) { return hr; } hr = pMS->GetTimeFormat(pFormat); pMS->Release(); return hr; } STDMETHODIMP CPosPassThru::IsUsingTimeFormat(const GUID * pFormat) { IMediaSeeking* pMS; HRESULT hr = GetPeerSeeking(&pMS); if (FAILED(hr)) { return hr; } hr = pMS->IsUsingTimeFormat(pFormat); pMS->Release(); return hr; } STDMETHODIMP CPosPassThru::ConvertTimeFormat(LONGLONG * pTarget, const GUID * pTargetFormat, LONGLONG Source, const GUID * pSourceFormat ) { IMediaSeeking* pMS; HRESULT hr = GetPeerSeeking(&pMS); if (FAILED(hr)) { return hr; } hr = pMS->ConvertTimeFormat(pTarget, pTargetFormat, Source, pSourceFormat ); pMS->Release(); return hr; } STDMETHODIMP CPosPassThru::SetPositions( LONGLONG * pCurrent, DWORD CurrentFlags , LONGLONG * pStop, DWORD StopFlags ) { IMediaSeeking* pMS; HRESULT hr = GetPeerSeeking(&pMS); if (FAILED(hr)) { return hr; } hr = pMS->SetPositions(pCurrent, CurrentFlags, pStop, StopFlags ); pMS->Release(); return hr; } STDMETHODIMP CPosPassThru::GetPositions(LONGLONG *pCurrent, LONGLONG * pStop) { IMediaSeeking* pMS; HRESULT hr = GetPeerSeeking(&pMS); if (FAILED(hr)) { return hr; } hr = pMS->GetPositions(pCurrent,pStop); pMS->Release(); return hr; } HRESULT CPosPassThru::GetSeekingLongLong ( HRESULT (__stdcall IMediaSeeking::*pMethod)( LONGLONG * ) , LONGLONG * pll ) { IMediaSeeking* pMS; HRESULT hr = GetPeerSeeking(&pMS); if (SUCCEEDED(hr)) { hr = (pMS->*pMethod)(pll); pMS->Release(); } return hr; } // If we don't have a current position then ask upstream STDMETHODIMP CPosPassThru::GetCurrentPosition(LONGLONG *pCurrent) { // Can we report the current position HRESULT hr = GetMediaTime(pCurrent,NULL); if (SUCCEEDED(hr)) hr = NOERROR; else hr = GetSeekingLongLong( &IMediaSeeking::GetCurrentPosition, pCurrent ); return hr; } STDMETHODIMP CPosPassThru::GetStopPosition(LONGLONG *pStop) { return GetSeekingLongLong( &IMediaSeeking::GetStopPosition, pStop );; } STDMETHODIMP CPosPassThru::GetDuration(LONGLONG *pDuration) { return GetSeekingLongLong( &IMediaSeeking::GetDuration, pDuration );; } STDMETHODIMP CPosPassThru::GetPreroll(LONGLONG *pllPreroll) { return GetSeekingLongLong( &IMediaSeeking::GetPreroll, pllPreroll );; } STDMETHODIMP CPosPassThru::GetAvailable( LONGLONG *pEarliest, LONGLONG *pLatest ) { IMediaSeeking* pMS; HRESULT hr = GetPeerSeeking(&pMS); if (FAILED(hr)) { return hr; } hr = pMS->GetAvailable( pEarliest, pLatest ); pMS->Release(); return hr; } STDMETHODIMP CPosPassThru::GetRate(double * pdRate) { IMediaSeeking* pMS; HRESULT hr = GetPeerSeeking(&pMS); if (FAILED(hr)) { return hr; } hr = pMS->GetRate(pdRate); pMS->Release(); return hr; } STDMETHODIMP CPosPassThru::SetRate(double dRate) { if (0.0 == dRate) { return E_INVALIDARG; } IMediaSeeking* pMS; HRESULT hr = GetPeerSeeking(&pMS); if (FAILED(hr)) { return hr; } hr = pMS->SetRate(dRate); pMS->Release(); return hr; } // --- IMediaPosition methods ---------- STDMETHODIMP CPosPassThru::get_Duration(REFTIME * plength) { IMediaPosition* pMP; HRESULT hr = GetPeer(&pMP); if (FAILED(hr)) { return hr; } hr = pMP->get_Duration(plength); pMP->Release(); return hr; } STDMETHODIMP CPosPassThru::get_CurrentPosition(REFTIME * pllTime) { IMediaPosition* pMP; HRESULT hr = GetPeer(&pMP); if (FAILED(hr)) { return hr; } hr = pMP->get_CurrentPosition(pllTime); pMP->Release(); return hr; } STDMETHODIMP CPosPassThru::put_CurrentPosition(REFTIME llTime) { IMediaPosition* pMP; HRESULT hr = GetPeer(&pMP); if (FAILED(hr)) { return hr; } hr = pMP->put_CurrentPosition(llTime); pMP->Release(); return hr; } STDMETHODIMP CPosPassThru::get_StopTime(REFTIME * pllTime) { IMediaPosition* pMP; HRESULT hr = GetPeer(&pMP); if (FAILED(hr)) { return hr; } hr = pMP->get_StopTime(pllTime); pMP->Release(); return hr; } STDMETHODIMP CPosPassThru::put_StopTime(REFTIME llTime) { IMediaPosition* pMP; HRESULT hr = GetPeer(&pMP); if (FAILED(hr)) { return hr; } hr = pMP->put_StopTime(llTime); pMP->Release(); return hr; } STDMETHODIMP CPosPassThru::get_PrerollTime(REFTIME * pllTime) { IMediaPosition* pMP; HRESULT hr = GetPeer(&pMP); if (FAILED(hr)) { return hr; } hr = pMP->get_PrerollTime(pllTime); pMP->Release(); return hr; } STDMETHODIMP CPosPassThru::put_PrerollTime(REFTIME llTime) { IMediaPosition* pMP; HRESULT hr = GetPeer(&pMP); if (FAILED(hr)) { return hr; } hr = pMP->put_PrerollTime(llTime); pMP->Release(); return hr; } STDMETHODIMP CPosPassThru::get_Rate(double * pdRate) { IMediaPosition* pMP; HRESULT hr = GetPeer(&pMP); if (FAILED(hr)) { return hr; } hr = pMP->get_Rate(pdRate); pMP->Release(); return hr; } STDMETHODIMP CPosPassThru::put_Rate(double dRate) { if (0.0 == dRate) { return E_INVALIDARG; } IMediaPosition* pMP; HRESULT hr = GetPeer(&pMP); if (FAILED(hr)) { return hr; } hr = pMP->put_Rate(dRate); pMP->Release(); return hr; } STDMETHODIMP CPosPassThru::CanSeekForward(LONG *pCanSeekForward) { IMediaPosition* pMP; HRESULT hr = GetPeer(&pMP); if (FAILED(hr)) { return hr; } hr = pMP->CanSeekForward(pCanSeekForward); pMP->Release(); return hr; } STDMETHODIMP CPosPassThru::CanSeekBackward(LONG *pCanSeekBackward) { IMediaPosition* pMP; HRESULT hr = GetPeer(&pMP); if (FAILED(hr)) { return hr; } hr = pMP->CanSeekBackward(pCanSeekBackward); pMP->Release(); return hr; } // --- Implements the CRendererPosPassThru class ---------- // Media times (eg current frame, field, sample etc) are passed through the // filtergraph in media samples. When a renderer gets a sample with media // times in it, it will call one of the RegisterMediaTime methods we expose // (one takes an IMediaSample, the other takes the media times direct). We // store the media times internally and return them in GetCurrentPosition. CRendererPosPassThru::CRendererPosPassThru(const TCHAR *pName, LPUNKNOWN pUnk, HRESULT *phr, IPin *pPin) : CPosPassThru(pName,pUnk,phr,pPin), m_StartMedia(0), m_EndMedia(0), m_bReset(TRUE) { } // Sets the media times the object should report HRESULT CRendererPosPassThru::RegisterMediaTime(IMediaSample *pMediaSample) { ASSERT(pMediaSample); LONGLONG StartMedia; LONGLONG EndMedia; CAutoLock cAutoLock(&m_PositionLock); // Get the media times from the sample HRESULT hr = pMediaSample->GetTime(&StartMedia,&EndMedia); if (FAILED(hr)) { ASSERT(hr == VFW_E_SAMPLE_TIME_NOT_SET); return hr; } m_StartMedia = StartMedia; m_EndMedia = EndMedia; m_bReset = FALSE; return NOERROR; } // Sets the media times the object should report HRESULT CRendererPosPassThru::RegisterMediaTime(LONGLONG StartTime,LONGLONG EndTime) { CAutoLock cAutoLock(&m_PositionLock); m_StartMedia = StartTime; m_EndMedia = EndTime; m_bReset = FALSE; return NOERROR; } // Return the current media times registered in the object HRESULT CRendererPosPassThru::GetMediaTime(LONGLONG *pStartTime,LONGLONG *pEndTime) { ASSERT(pStartTime); CAutoLock cAutoLock(&m_PositionLock); if (m_bReset == TRUE) { return E_FAIL; } // We don't have to return the end time HRESULT hr = ConvertTimeFormat( pStartTime, 0, m_StartMedia, &TIME_FORMAT_MEDIA_TIME ); if (pEndTime && SUCCEEDED(hr)) { hr = ConvertTimeFormat( pEndTime, 0, m_EndMedia, &TIME_FORMAT_MEDIA_TIME ); } return hr; } // Resets the media times we hold HRESULT CRendererPosPassThru::ResetMediaTime() { CAutoLock cAutoLock(&m_PositionLock); m_StartMedia = 0; m_EndMedia = 0; m_bReset = TRUE; return NOERROR; } // Intended to be called by the owing filter during EOS processing so // that the media times can be adjusted to the stop time. This ensures // that the GetCurrentPosition will actully get to the stop position. HRESULT CRendererPosPassThru::EOS() { HRESULT hr; if ( m_bReset == TRUE ) hr = E_FAIL; else { LONGLONG llStop; if SUCCEEDED(hr=GetStopPosition(&llStop)) { CAutoLock cAutoLock(&m_PositionLock); m_StartMedia = m_EndMedia = llStop; } } return hr; } // -- CSourceSeeking implementation ------------ CSourceSeeking::CSourceSeeking( const TCHAR * pName, LPUNKNOWN pUnk, HRESULT* phr, CCritSec * pLock) : CUnknown(pName, pUnk), m_pLock(pLock), m_rtStart((long)0) { m_rtStop = _I64_MAX / 2; m_rtDuration = m_rtStop; m_dRateSeeking = 1.0; m_dwSeekingCaps = AM_SEEKING_CanSeekForwards | AM_SEEKING_CanSeekBackwards | AM_SEEKING_CanSeekAbsolute | AM_SEEKING_CanGetStopPos | AM_SEEKING_CanGetDuration; } HRESULT CSourceSeeking::NonDelegatingQueryInterface(REFIID riid, void **ppv) { if(riid == IID_IMediaSeeking) { CheckPointer(ppv, E_POINTER); return GetInterface(static_cast(this), ppv); } else { return CUnknown::NonDelegatingQueryInterface(riid, ppv); } } HRESULT CSourceSeeking::IsFormatSupported(const GUID * pFormat) { CheckPointer(pFormat, E_POINTER); // only seeking in time (REFERENCE_TIME units) is supported return *pFormat == TIME_FORMAT_MEDIA_TIME ? S_OK : S_FALSE; } HRESULT CSourceSeeking::QueryPreferredFormat(GUID *pFormat) { CheckPointer(pFormat, E_POINTER); *pFormat = TIME_FORMAT_MEDIA_TIME; return S_OK; } HRESULT CSourceSeeking::SetTimeFormat(const GUID * pFormat) { CheckPointer(pFormat, E_POINTER); // nothing to set; just check that it's TIME_FORMAT_TIME return *pFormat == TIME_FORMAT_MEDIA_TIME ? S_OK : E_INVALIDARG; } HRESULT CSourceSeeking::IsUsingTimeFormat(const GUID * pFormat) { CheckPointer(pFormat, E_POINTER); return *pFormat == TIME_FORMAT_MEDIA_TIME ? S_OK : S_FALSE; } HRESULT CSourceSeeking::GetTimeFormat(GUID *pFormat) { CheckPointer(pFormat, E_POINTER); *pFormat = TIME_FORMAT_MEDIA_TIME; return S_OK; } HRESULT CSourceSeeking::GetDuration(LONGLONG *pDuration) { CheckPointer(pDuration, E_POINTER); CAutoLock lock(m_pLock); *pDuration = m_rtDuration; return S_OK; } HRESULT CSourceSeeking::GetStopPosition(LONGLONG *pStop) { CheckPointer(pStop, E_POINTER); CAutoLock lock(m_pLock); *pStop = m_rtStop; return S_OK; } HRESULT CSourceSeeking::GetCurrentPosition(LONGLONG *pCurrent) { // GetCurrentPosition is typically supported only in renderers and // not in source filters. return E_NOTIMPL; } HRESULT CSourceSeeking::GetCapabilities( DWORD * pCapabilities ) { CheckPointer(pCapabilities, E_POINTER); *pCapabilities = m_dwSeekingCaps; return S_OK; } HRESULT CSourceSeeking::CheckCapabilities( DWORD * pCapabilities ) { CheckPointer(pCapabilities, E_POINTER); // make sure all requested capabilities are in our mask return (~m_dwSeekingCaps & *pCapabilities) ? S_FALSE : S_OK; } HRESULT CSourceSeeking::ConvertTimeFormat( LONGLONG * pTarget, const GUID * pTargetFormat, LONGLONG Source, const GUID * pSourceFormat ) { CheckPointer(pTarget, E_POINTER); // format guids can be null to indicate current format // since we only support TIME_FORMAT_MEDIA_TIME, we don't really // offer any conversions. if(pTargetFormat == 0 || *pTargetFormat == TIME_FORMAT_MEDIA_TIME) { if(pSourceFormat == 0 || *pSourceFormat == TIME_FORMAT_MEDIA_TIME) { *pTarget = Source; return S_OK; } } return E_INVALIDARG; } HRESULT CSourceSeeking::SetPositions( LONGLONG * pCurrent, DWORD CurrentFlags , LONGLONG * pStop, DWORD StopFlags ) { DWORD StopPosBits = StopFlags & AM_SEEKING_PositioningBitsMask; DWORD StartPosBits = CurrentFlags & AM_SEEKING_PositioningBitsMask; if(StopFlags) { CheckPointer(pStop, E_POINTER); // accept only relative, incremental, or absolute positioning if(StopPosBits != StopFlags) { return E_INVALIDARG; } } if(CurrentFlags) { CheckPointer(pCurrent, E_POINTER); if(StartPosBits != AM_SEEKING_AbsolutePositioning && StartPosBits != AM_SEEKING_RelativePositioning) { return E_INVALIDARG; } } // scope for autolock { CAutoLock lock(m_pLock); // set start position if(StartPosBits == AM_SEEKING_AbsolutePositioning) { m_rtStart = *pCurrent; } else if(StartPosBits == AM_SEEKING_RelativePositioning) { m_rtStart += *pCurrent; } // set stop position if(StopPosBits == AM_SEEKING_AbsolutePositioning) { m_rtStop = *pStop; } else if(StopPosBits == AM_SEEKING_IncrementalPositioning) { m_rtStop = m_rtStart + *pStop; } else if(StopPosBits == AM_SEEKING_RelativePositioning) { m_rtStop = m_rtStop + *pStop; } } HRESULT hr = S_OK; if(SUCCEEDED(hr) && StopPosBits) { hr = ChangeStop(); } if(StartPosBits) { hr = ChangeStart(); } return hr; } HRESULT CSourceSeeking::GetPositions( LONGLONG * pCurrent, LONGLONG * pStop ) { if(pCurrent) { *pCurrent = m_rtStart; } if(pStop) { *pStop = m_rtStop; } return S_OK;; } HRESULT CSourceSeeking::GetAvailable( LONGLONG * pEarliest, LONGLONG * pLatest ) { if(pEarliest) { *pEarliest = 0; } if(pLatest) { CAutoLock lock(m_pLock); *pLatest = m_rtDuration; } return S_OK; } HRESULT CSourceSeeking::SetRate( double dRate) { { CAutoLock lock(m_pLock); m_dRateSeeking = dRate; } return ChangeRate(); } HRESULT CSourceSeeking::GetRate( double * pdRate) { CheckPointer(pdRate, E_POINTER); CAutoLock lock(m_pLock); *pdRate = m_dRateSeeking; return S_OK; } HRESULT CSourceSeeking::GetPreroll(LONGLONG *pPreroll) { CheckPointer(pPreroll, E_POINTER); *pPreroll = 0; return S_OK; } // --- CSourcePosition implementation ---------- CSourcePosition::CSourcePosition(const TCHAR * pName, LPUNKNOWN pUnk, HRESULT* phr, CCritSec * pLock) : CMediaPosition(pName, pUnk), m_pLock(pLock), m_Start(CRefTime((LONGLONG)0)) { m_Stop = _I64_MAX; m_Rate = 1.0; } STDMETHODIMP CSourcePosition::get_Duration(REFTIME * plength) { CheckPointer(plength,E_POINTER); ValidateReadWritePtr(plength,sizeof(REFTIME)); CAutoLock lock(m_pLock); *plength = m_Duration; return S_OK; } STDMETHODIMP CSourcePosition::put_CurrentPosition(REFTIME llTime) { m_pLock->Lock(); m_Start = llTime; m_pLock->Unlock(); return ChangeStart(); } STDMETHODIMP CSourcePosition::get_StopTime(REFTIME * pllTime) { CheckPointer(pllTime,E_POINTER); ValidateReadWritePtr(pllTime,sizeof(REFTIME)); CAutoLock lock(m_pLock); *pllTime = m_Stop; return S_OK; } STDMETHODIMP CSourcePosition::put_StopTime(REFTIME llTime) { m_pLock->Lock(); m_Stop = llTime; m_pLock->Unlock(); return ChangeStop(); } STDMETHODIMP CSourcePosition::get_PrerollTime(REFTIME * pllTime) { CheckPointer(pllTime,E_POINTER); ValidateReadWritePtr(pllTime,sizeof(REFTIME)); return E_NOTIMPL; } STDMETHODIMP CSourcePosition::put_PrerollTime(REFTIME llTime) { return E_NOTIMPL; } STDMETHODIMP CSourcePosition::get_Rate(double * pdRate) { CheckPointer(pdRate,E_POINTER); ValidateReadWritePtr(pdRate,sizeof(double)); CAutoLock lock(m_pLock); *pdRate = m_Rate; return S_OK; } STDMETHODIMP CSourcePosition::put_Rate(double dRate) { m_pLock->Lock(); m_Rate = dRate; m_pLock->Unlock(); return ChangeRate(); } // By default we can seek forwards STDMETHODIMP CSourcePosition::CanSeekForward(LONG *pCanSeekForward) { CheckPointer(pCanSeekForward,E_POINTER); *pCanSeekForward = OATRUE; return S_OK; } // By default we can seek backwards STDMETHODIMP CSourcePosition::CanSeekBackward(LONG *pCanSeekBackward) { CheckPointer(pCanSeekBackward,E_POINTER); *pCanSeekBackward = OATRUE; return S_OK; } // --- Implementation of CBasicAudio class ---------- CBasicAudio::CBasicAudio(const TCHAR * pName,LPUNKNOWN punk) : CUnknown(pName, punk) { } // overriden to publicise our interfaces STDMETHODIMP CBasicAudio::NonDelegatingQueryInterface(REFIID riid, void **ppv) { ValidateReadWritePtr(ppv,sizeof(PVOID)); if (riid == IID_IBasicAudio) { return GetInterface( (IBasicAudio *) this, ppv); } else { return CUnknown::NonDelegatingQueryInterface(riid, ppv); } } STDMETHODIMP CBasicAudio::GetTypeInfoCount(UINT * pctinfo) { return m_basedisp.GetTypeInfoCount(pctinfo); } STDMETHODIMP CBasicAudio::GetTypeInfo( UINT itinfo, LCID lcid, ITypeInfo ** pptinfo) { return m_basedisp.GetTypeInfo( IID_IBasicAudio, itinfo, lcid, pptinfo); } STDMETHODIMP CBasicAudio::GetIDsOfNames( REFIID riid, OLECHAR ** rgszNames, UINT cNames, LCID lcid, DISPID * rgdispid) { return m_basedisp.GetIDsOfNames( IID_IBasicAudio, rgszNames, cNames, lcid, rgdispid); } STDMETHODIMP CBasicAudio::Invoke( DISPID dispidMember, REFIID riid, LCID lcid, WORD wFlags, DISPPARAMS * pdispparams, VARIANT * pvarResult, EXCEPINFO * pexcepinfo, UINT * puArgErr) { // this parameter is a dead leftover from an earlier interface if (IID_NULL != riid) { return DISP_E_UNKNOWNINTERFACE; } ITypeInfo * pti; HRESULT hr = GetTypeInfo(0, lcid, &pti); if (FAILED(hr)) { return hr; } hr = pti->Invoke( (IBasicAudio *)this, dispidMember, wFlags, pdispparams, pvarResult, pexcepinfo, puArgErr); pti->Release(); return hr; } // --- IVideoWindow implementation ---------- CBaseVideoWindow::CBaseVideoWindow(const TCHAR * pName,LPUNKNOWN punk) : CUnknown(pName, punk) { } // overriden to publicise our interfaces STDMETHODIMP CBaseVideoWindow::NonDelegatingQueryInterface(REFIID riid, void **ppv) { ValidateReadWritePtr(ppv,sizeof(PVOID)); if (riid == IID_IVideoWindow) { return GetInterface( (IVideoWindow *) this, ppv); } else { return CUnknown::NonDelegatingQueryInterface(riid, ppv); } } STDMETHODIMP CBaseVideoWindow::GetTypeInfoCount(UINT * pctinfo) { return m_basedisp.GetTypeInfoCount(pctinfo); } STDMETHODIMP CBaseVideoWindow::GetTypeInfo( UINT itinfo, LCID lcid, ITypeInfo ** pptinfo) { return m_basedisp.GetTypeInfo( IID_IVideoWindow, itinfo, lcid, pptinfo); } STDMETHODIMP CBaseVideoWindow::GetIDsOfNames( REFIID riid, OLECHAR ** rgszNames, UINT cNames, LCID lcid, DISPID * rgdispid) { return m_basedisp.GetIDsOfNames( IID_IVideoWindow, rgszNames, cNames, lcid, rgdispid); } STDMETHODIMP CBaseVideoWindow::Invoke( DISPID dispidMember, REFIID riid, LCID lcid, WORD wFlags, DISPPARAMS * pdispparams, VARIANT * pvarResult, EXCEPINFO * pexcepinfo, UINT * puArgErr) { // this parameter is a dead leftover from an earlier interface if (IID_NULL != riid) { return DISP_E_UNKNOWNINTERFACE; } ITypeInfo * pti; HRESULT hr = GetTypeInfo(0, lcid, &pti); if (FAILED(hr)) { return hr; } hr = pti->Invoke( (IVideoWindow *)this, dispidMember, wFlags, pdispparams, pvarResult, pexcepinfo, puArgErr); pti->Release(); return hr; } // --- IBasicVideo implementation ---------- CBaseBasicVideo::CBaseBasicVideo(const TCHAR * pName,LPUNKNOWN punk) : CUnknown(pName, punk) { } // overriden to publicise our interfaces STDMETHODIMP CBaseBasicVideo::NonDelegatingQueryInterface(REFIID riid, void **ppv) { ValidateReadWritePtr(ppv,sizeof(PVOID)); if (riid == IID_IBasicVideo || riid == IID_IBasicVideo2) { return GetInterface( static_cast(this), ppv); } else { return CUnknown::NonDelegatingQueryInterface(riid, ppv); } } STDMETHODIMP CBaseBasicVideo::GetTypeInfoCount(UINT * pctinfo) { return m_basedisp.GetTypeInfoCount(pctinfo); } STDMETHODIMP CBaseBasicVideo::GetTypeInfo( UINT itinfo, LCID lcid, ITypeInfo ** pptinfo) { return m_basedisp.GetTypeInfo( IID_IBasicVideo, itinfo, lcid, pptinfo); } STDMETHODIMP CBaseBasicVideo::GetIDsOfNames( REFIID riid, OLECHAR ** rgszNames, UINT cNames, LCID lcid, DISPID * rgdispid) { return m_basedisp.GetIDsOfNames( IID_IBasicVideo, rgszNames, cNames, lcid, rgdispid); } STDMETHODIMP CBaseBasicVideo::Invoke( DISPID dispidMember, REFIID riid, LCID lcid, WORD wFlags, DISPPARAMS * pdispparams, VARIANT * pvarResult, EXCEPINFO * pexcepinfo, UINT * puArgErr) { // this parameter is a dead leftover from an earlier interface if (IID_NULL != riid) { return DISP_E_UNKNOWNINTERFACE; } ITypeInfo * pti; HRESULT hr = GetTypeInfo(0, lcid, &pti); if (FAILED(hr)) { return hr; } hr = pti->Invoke( (IBasicVideo *)this, dispidMember, wFlags, pdispparams, pvarResult, pexcepinfo, puArgErr); pti->Release(); return hr; } // --- Implementation of Deferred Commands ---------- CDispParams::CDispParams(UINT nArgs, VARIANT* pArgs, HRESULT *phr) { cNamedArgs = 0; rgdispidNamedArgs = NULL; cArgs = nArgs; if (cArgs) { rgvarg = new VARIANT[cArgs]; if (NULL == rgvarg) { cArgs = 0; if (phr) { *phr = E_OUTOFMEMORY; } return; } for (UINT i = 0; i < cArgs; i++) { VARIANT * pDest = &rgvarg[i]; VARIANT * pSrc = &pArgs[i]; pDest->vt = pSrc->vt; switch(pDest->vt) { case VT_I4: pDest->lVal = pSrc->lVal; break; case VT_UI1: pDest->bVal = pSrc->bVal; break; case VT_I2: pDest->iVal = pSrc->iVal; break; case VT_R4: pDest->fltVal = pSrc->fltVal; break; case VT_R8: pDest->dblVal = pSrc->dblVal; break; case VT_BOOL: pDest->boolVal = pSrc->boolVal; break; case VT_ERROR: pDest->scode = pSrc->scode; break; case VT_CY: pDest->cyVal = pSrc->cyVal; break; case VT_DATE: pDest->date = pSrc->date; break; case VT_BSTR: if (pSrc->bstrVal == NULL) { pDest->bstrVal = NULL; } else { // a BSTR is a WORD followed by a UNICODE string. // the pointer points just after the WORD WORD len = * (WORD*) (pSrc->bstrVal - (sizeof(WORD) / sizeof(OLECHAR))); OLECHAR* pch = new OLECHAR[len + (sizeof(WORD)/sizeof(OLECHAR))]; if (pch) { WORD *pui = (WORD*)pch; *pui = len; pDest->bstrVal = pch + (sizeof(WORD)/sizeof(OLECHAR)); CopyMemory(pDest->bstrVal, pSrc->bstrVal, len*sizeof(OLECHAR)); } else { cArgs = i; if (phr) { *phr = E_OUTOFMEMORY; } } } pDest->bstrVal = pSrc->bstrVal; break; case VT_UNKNOWN: pDest->punkVal = pSrc->punkVal; pDest->punkVal->AddRef(); break; case VT_DISPATCH: pDest->pdispVal = pSrc->pdispVal; pDest->pdispVal->AddRef(); break; default: // a type we haven't got round to adding yet! ASSERT(0); break; } } } else { rgvarg = NULL; } } CDispParams::~CDispParams() { for (UINT i = 0; i < cArgs; i++) { switch(rgvarg[i].vt) { case VT_BSTR: if (rgvarg[i].bstrVal != NULL) { OLECHAR * pch = rgvarg[i].bstrVal - (sizeof(WORD)/sizeof(OLECHAR)); delete pch; } break; case VT_UNKNOWN: rgvarg[i].punkVal->Release(); break; case VT_DISPATCH: rgvarg[i].pdispVal->Release(); break; } } delete[] rgvarg; } // lifetime is controlled by refcounts (see defer.h) CDeferredCommand::CDeferredCommand( CCmdQueue * pQ, LPUNKNOWN pUnk, HRESULT * phr, LPUNKNOWN pUnkExecutor, REFTIME time, GUID* iid, long dispidMethod, short wFlags, long nArgs, VARIANT* pDispParams, VARIANT* pvarResult, short* puArgErr, BOOL bStream ) : CUnknown(NAME("DeferredCommand"), pUnk), m_pQueue(pQ), m_pUnk(pUnkExecutor), m_iid(iid), m_dispidMethod(dispidMethod), m_wFlags(wFlags), m_DispParams(nArgs, pDispParams, phr), m_pvarResult(pvarResult), m_bStream(bStream), m_hrResult(E_ABORT) { // convert REFTIME to REFERENCE_TIME COARefTime convertor(time); m_time = convertor; // no check of time validity - it's ok to queue a command that's // already late // check iid is supportable on pUnk by QueryInterface for it IUnknown * pInterface; HRESULT hr = m_pUnk->QueryInterface(GetIID(), (void**) &pInterface); if (FAILED(hr)) { *phr = hr; return; } pInterface->Release(); // !!! check dispidMethod and param/return types using typelib ITypeInfo *pti; hr = m_Dispatch.GetTypeInfo(*iid, 0, 0, &pti); if (FAILED(hr)) { *phr = hr; return; } // !!! some sort of ITypeInfo validity check here pti->Release(); // Fix up the dispid for put and get if (wFlags == DISPATCH_PROPERTYPUT) { m_DispParams.cNamedArgs = 1; m_DispId = DISPID_PROPERTYPUT; m_DispParams.rgdispidNamedArgs = &m_DispId; } // all checks ok - add to queue hr = pQ->Insert(this); if (FAILED(hr)) { *phr = hr; } } // refcounts are held by caller of InvokeAt... and by list. So if // we get here, we can't be on the list #if 0 CDeferredCommand::~CDeferredCommand() { // this assert is invalid since if the queue is deleted while we are // still on the queue, we will have been removed by the queue and this // m_pQueue will not have been modified. // ASSERT(m_pQueue == NULL); // we don't hold a ref count on pUnk, which is the object that should // execute the command. // This is because there would otherwise be a circular refcount problem // since pUnk probably owns the CmdQueue object that has a refcount // on us. // The lifetime of pUnk is guaranteed by it being part of, or lifetime // controlled by, our parent object. As long as we are on the list, pUnk // must be valid. Once we are off the list, we do not use pUnk. } #endif // overriden to publicise our interfaces STDMETHODIMP CDeferredCommand::NonDelegatingQueryInterface(REFIID riid, void **ppv) { ValidateReadWritePtr(ppv,sizeof(PVOID)); if (riid == IID_IDeferredCommand) { return GetInterface( (IDeferredCommand *) this, ppv); } else { return CUnknown::NonDelegatingQueryInterface(riid, ppv); } } // remove from q. this will reduce the refcount by one (since the q // holds a count) but can't make us go away since he must have a // refcount in order to call this method. STDMETHODIMP CDeferredCommand::Cancel() { if (m_pQueue == NULL) { return VFW_E_ALREADY_CANCELLED; } HRESULT hr = m_pQueue->Remove(this); if (FAILED(hr)) { return hr; } m_pQueue = NULL; return S_OK; } STDMETHODIMP CDeferredCommand::Confidence(LONG* pConfidence) { return E_NOTIMPL; } STDMETHODIMP CDeferredCommand::GetHResult(HRESULT * phrResult) { CheckPointer(phrResult,E_POINTER); ValidateReadWritePtr(phrResult,sizeof(HRESULT)); if (m_pQueue != NULL) { return E_ABORT; } *phrResult = m_hrResult; return S_OK; } // set the time to be a new time (checking that it is valid) and // then requeue STDMETHODIMP CDeferredCommand::Postpone(REFTIME newtime) { // check that this time is not past // convert REFTIME to REFERENCE_TIME COARefTime convertor(newtime); // check that the time has not passed if (m_pQueue->CheckTime(convertor, IsStreamTime())) { return VFW_E_TIME_ALREADY_PASSED; } // extract from list HRESULT hr = m_pQueue->Remove(this); if (FAILED(hr)) { return hr; } // change time m_time = convertor; // requeue hr = m_pQueue->Insert(this); return hr; } HRESULT CDeferredCommand::Invoke() { // check that we are still outstanding if (m_pQueue == NULL) { return VFW_E_ALREADY_CANCELLED; } // get the type info ITypeInfo* pti; HRESULT hr = m_Dispatch.GetTypeInfo(GetIID(), 0, 0, &pti); if (FAILED(hr)) { return hr; } // qi for the expected interface and then invoke it. Note that we have to // treat the returned interface as IUnknown since we don't know its type. IUnknown* pInterface; hr = m_pUnk->QueryInterface(GetIID(), (void**) &pInterface); if (FAILED(hr)) { pti->Release(); return hr; } EXCEPINFO expinfo; UINT uArgErr; m_hrResult = pti->Invoke( pInterface, GetMethod(), GetFlags(), GetParams(), GetResult(), &expinfo, &uArgErr); // release the interface we QI'd for pInterface->Release(); pti->Release(); // remove from list whether or not successful // or we loop indefinitely hr = m_pQueue->Remove(this); m_pQueue = NULL; return hr; } // --- CCmdQueue methods ---------- CCmdQueue::CCmdQueue() : m_listPresentation(NAME("Presentation time command list")), m_listStream(NAME("Stream time command list")), m_evDue(TRUE), // manual reset m_dwAdvise(0), m_pClock(NULL), m_bRunning(FALSE) { } CCmdQueue::~CCmdQueue() { // empty all our lists // we hold a refcount on each, so traverse and Release each // entry then RemoveAll to empty the list POSITION pos = m_listPresentation.GetHeadPosition(); while(pos) { CDeferredCommand* pCmd = m_listPresentation.GetNext(pos); pCmd->Release(); } m_listPresentation.RemoveAll(); pos = m_listStream.GetHeadPosition(); while(pos) { CDeferredCommand* pCmd = m_listStream.GetNext(pos); pCmd->Release(); } m_listStream.RemoveAll(); if (m_pClock) { if (m_dwAdvise) { m_pClock->Unadvise(m_dwAdvise); m_dwAdvise = 0; } m_pClock->Release(); } } // returns a new CDeferredCommand object that will be initialised with // the parameters and will be added to the queue during construction. // returns S_OK if successfully created otherwise an error and // no object has been queued. HRESULT CCmdQueue::New( CDeferredCommand **ppCmd, LPUNKNOWN pUnk, // this object will execute command REFTIME time, GUID* iid, long dispidMethod, short wFlags, long cArgs, VARIANT* pDispParams, VARIANT* pvarResult, short* puArgErr, BOOL bStream ) { CAutoLock lock(&m_Lock); HRESULT hr = S_OK; *ppCmd = NULL; CDeferredCommand* pCmd; pCmd = new CDeferredCommand( this, NULL, // not aggregated &hr, pUnk, // this guy will execute time, iid, dispidMethod, wFlags, cArgs, pDispParams, pvarResult, puArgErr, bStream); if (pCmd == NULL) { hr = E_OUTOFMEMORY; } else { *ppCmd = pCmd; } return hr; } HRESULT CCmdQueue::Insert(CDeferredCommand* pCmd) { CAutoLock lock(&m_Lock); // addref the item pCmd->AddRef(); CGenericList * pList; if (pCmd->IsStreamTime()) { pList = &m_listStream; } else { pList = &m_listPresentation; } POSITION pos = pList->GetHeadPosition(); // seek past all items that are before us while (pos && (pList->Get(pos)->GetTime() <= pCmd->GetTime())) { pList->GetNext(pos); } // now at end of list or in front of items that come later if (!pos) { pList->AddTail(pCmd); } else { pList->AddBefore(pos, pCmd); } SetTimeAdvise(); return S_OK; } HRESULT CCmdQueue::Remove(CDeferredCommand* pCmd) { CAutoLock lock(&m_Lock); HRESULT hr = S_OK; CGenericList * pList; if (pCmd->IsStreamTime()) { pList = &m_listStream; } else { pList = &m_listPresentation; } POSITION pos = pList->GetHeadPosition(); // traverse the list while (pos && (pList->Get(pos) != pCmd)) { pList->GetNext(pos); } // did we drop off the end? if (!pos) { hr = VFW_E_NOT_FOUND; } else { // found it - now take off list pList->Remove(pos); // Insert did an AddRef, so release it pCmd->Release(); // check that timer request is still for earliest time SetTimeAdvise(); } return hr; } // set the clock used for timing HRESULT CCmdQueue::SetSyncSource(IReferenceClock* pClock) { CAutoLock lock(&m_Lock); // addref the new clock first in case they are the same if (pClock) { pClock->AddRef(); } // kill any advise on the old clock if (m_pClock) { if (m_dwAdvise) { m_pClock->Unadvise(m_dwAdvise); m_dwAdvise = 0; } m_pClock->Release(); } m_pClock = pClock; // set up a new advise SetTimeAdvise(); return S_OK; } // set up a timer event with the reference clock void CCmdQueue::SetTimeAdvise(void) { // make sure we have a clock to use if (!m_pClock) { return; } // reset the event whenever we are requesting a new signal m_evDue.Reset(); // time 0 is earliest CRefTime current; // find the earliest presentation time if (m_listPresentation.GetCount() > 0) { POSITION pos = m_listPresentation.GetHeadPosition(); current = m_listPresentation.Get(pos)->GetTime(); } // if we're running, check the stream times too if (m_bRunning) { CRefTime t; if (m_listStream.GetCount() > 0) { POSITION pos = m_listStream.GetHeadPosition(); t = m_listStream.Get(pos)->GetTime(); // add on stream time offset to get presentation time t += m_StreamTimeOffset; // is this earlier? if ((current == TimeZero) || (t < current)) { current = t; } } } // need to change? if ((current > TimeZero) && (current != m_tCurrentAdvise)) { if (m_dwAdvise) { m_pClock->Unadvise(m_dwAdvise); // reset the event whenever we are requesting a new signal m_evDue.Reset(); } // ask for time advice - the first two params are either // stream time offset and stream time or // presentation time and 0. we always use the latter HRESULT hr = m_pClock->AdviseTime( (REFERENCE_TIME)current, TimeZero, (HEVENT) HANDLE(m_evDue), &m_dwAdvise); ASSERT(SUCCEEDED(hr)); m_tCurrentAdvise = current; } } // switch to run mode. Streamtime to Presentation time mapping known. HRESULT CCmdQueue::Run(REFERENCE_TIME tStreamTimeOffset) { CAutoLock lock(&m_Lock); m_StreamTimeOffset = tStreamTimeOffset; m_bRunning = TRUE; // ensure advise is accurate SetTimeAdvise(); return S_OK; } // switch to Stopped or Paused mode. Time mapping not known. HRESULT CCmdQueue::EndRun() { CAutoLock lock(&m_Lock); m_bRunning = FALSE; // check timer setting - stream times SetTimeAdvise(); return S_OK; } // return a pointer to the next due command. Blocks for msTimeout // milliseconds until there is a due command. // Stream-time commands will only become due between Run and Endrun calls. // The command remains queued until invoked or cancelled. // Returns E_ABORT if timeout occurs, otherwise S_OK (or other error). // // returns an AddRef'd object HRESULT CCmdQueue::GetDueCommand(CDeferredCommand ** ppCmd, long msTimeout) { // loop until we timeout or find a due command for (;;) { { CAutoLock lock(&m_Lock); // find the earliest command CDeferredCommand * pCmd = NULL; // check the presentation time and the // stream time list to find the earliest if (m_listPresentation.GetCount() > 0) { POSITION pos = m_listPresentation.GetHeadPosition(); pCmd = m_listPresentation.Get(pos); } if (m_bRunning && (m_listStream.GetCount() > 0)) { POSITION pos = m_listStream.GetHeadPosition(); CDeferredCommand* pStrm = m_listStream.Get(pos); CRefTime t = pStrm->GetTime() + m_StreamTimeOffset; if (!pCmd || (t < pCmd->GetTime())) { pCmd = pStrm; } } // if we have found one, is it due? if (pCmd) { if (CheckTime(pCmd->GetTime(), pCmd->IsStreamTime())) { // yes it's due - addref it pCmd->AddRef(); *ppCmd = pCmd; return S_OK; } } } // block until the advise is signalled if (WaitForSingleObject(m_evDue, msTimeout) != WAIT_OBJECT_0) { return E_ABORT; } } } // return a pointer to a command that will be due for a given time. // Pass in a stream time here. The stream time offset will be passed // in via the Run method. // Commands remain queued until invoked or cancelled. // This method will not block. It will report E_ABORT if there are no // commands due yet. // // returns an AddRef'd object HRESULT CCmdQueue::GetCommandDueFor(REFERENCE_TIME rtStream, CDeferredCommand**ppCmd) { CAutoLock lock(&m_Lock); CRefTime tStream(rtStream); // find the earliest stream and presentation time commands CDeferredCommand* pStream = NULL; if (m_listStream.GetCount() > 0) { POSITION pos = m_listStream.GetHeadPosition(); pStream = m_listStream.Get(pos); } CDeferredCommand* pPresent = NULL; if (m_listPresentation.GetCount() > 0) { POSITION pos = m_listPresentation.GetHeadPosition(); pPresent = m_listPresentation.Get(pos); } // is there a presentation time that has passed already if (pPresent && CheckTime(pPresent->GetTime(), FALSE)) { pPresent->AddRef(); *ppCmd = pPresent; return S_OK; } // is there a stream time command due before this stream time if (pStream && (pStream->GetTime() <= tStream)) { pPresent->AddRef(); *ppCmd = pStream; return S_OK; } // if we are running, we can map presentation times to // stream time. In this case, is there a presentation time command // that will be due before this stream time is presented? if (m_bRunning && pPresent) { // this stream time will appear at... tStream += m_StreamTimeOffset; // due before that? if (pPresent->GetTime() <= tStream) { *ppCmd = pPresent; return S_OK; } } // no commands due yet return VFW_E_NOT_FOUND; }