| //------------------------------------------------------------------------------ | |
| // 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 <streams.h> | |
| #include <limits.h> | |
| #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(__out UINT * pctinfo) | |
| { | |
| CheckPointer(pctinfo,E_POINTER); | |
| ValidateReadWritePtr(pctinfo,sizeof(UINT *)); | |
| *pctinfo = 1; | |
| return S_OK; | |
| } | |
| typedef HRESULT (STDAPICALLTYPE *LPLOADTYPELIB)( | |
| const OLECHAR FAR *szFile, | |
| __deref_out ITypeLib FAR* FAR* pptlib); | |
| typedef HRESULT (STDAPICALLTYPE *LPLOADREGTYPELIB)(REFGUID rguid, | |
| WORD wVerMajor, | |
| WORD wVerMinor, | |
| LCID lcid, | |
| __deref_out ITypeLib FAR* FAR* pptlib); | |
| // attempt to find our type library | |
| STDMETHODIMP | |
| CBaseDispatch::GetTypeInfo( | |
| REFIID riid, | |
| UINT itinfo, | |
| LCID lcid, | |
| __deref_out 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, | |
| __in_ecount(cNames) LPOLESTR * rgszNames, | |
| UINT cNames, | |
| LCID lcid, | |
| __out_ecount(cNames) 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, __deref_out 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(__out UINT * pctinfo) | |
| { | |
| return m_basedisp.GetTypeInfoCount(pctinfo); | |
| } | |
| // attempt to find our type library | |
| STDMETHODIMP | |
| CMediaControl::GetTypeInfo( | |
| UINT itinfo, | |
| LCID lcid, | |
| __deref_out ITypeInfo ** pptinfo) | |
| { | |
| return m_basedisp.GetTypeInfo( | |
| IID_IMediaControl, | |
| itinfo, | |
| lcid, | |
| pptinfo); | |
| } | |
| STDMETHODIMP | |
| CMediaControl::GetIDsOfNames( | |
| REFIID riid, | |
| __in_ecount(cNames) LPOLESTR * rgszNames, | |
| UINT cNames, | |
| LCID lcid, | |
| __out_ecount(cNames) DISPID * rgdispid) | |
| { | |
| return m_basedisp.GetIDsOfNames( | |
| IID_IMediaControl, | |
| rgszNames, | |
| cNames, | |
| lcid, | |
| rgdispid); | |
| } | |
| STDMETHODIMP | |
| CMediaControl::Invoke( | |
| DISPID dispidMember, | |
| REFIID riid, | |
| LCID lcid, | |
| WORD wFlags, | |
| __in DISPPARAMS * pdispparams, | |
| __out_opt VARIANT * pvarResult, | |
| __out_opt EXCEPINFO * pexcepinfo, | |
| __out_opt 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(__in_opt LPCTSTR name,__in_opt LPUNKNOWN pUnk) : | |
| CUnknown(name, pUnk) | |
| { | |
| } | |
| // expose our interfaces IMediaEvent and IUnknown | |
| STDMETHODIMP | |
| CMediaEvent::NonDelegatingQueryInterface(REFIID riid, __deref_out 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(__out UINT * pctinfo) | |
| { | |
| return m_basedisp.GetTypeInfoCount(pctinfo); | |
| } | |
| // attempt to find our type library | |
| STDMETHODIMP | |
| CMediaEvent::GetTypeInfo( | |
| UINT itinfo, | |
| LCID lcid, | |
| __deref_out ITypeInfo ** pptinfo) | |
| { | |
| return m_basedisp.GetTypeInfo( | |
| IID_IMediaEvent, | |
| itinfo, | |
| lcid, | |
| pptinfo); | |
| } | |
| STDMETHODIMP | |
| CMediaEvent::GetIDsOfNames( | |
| REFIID riid, | |
| __in_ecount(cNames) LPOLESTR * rgszNames, | |
| UINT cNames, | |
| LCID lcid, | |
| __out_ecount(cNames) DISPID * rgdispid) | |
| { | |
| return m_basedisp.GetIDsOfNames( | |
| IID_IMediaEvent, | |
| rgszNames, | |
| cNames, | |
| lcid, | |
| rgdispid); | |
| } | |
| STDMETHODIMP | |
| CMediaEvent::Invoke( | |
| DISPID dispidMember, | |
| REFIID riid, | |
| LCID lcid, | |
| WORD wFlags, | |
| __in DISPPARAMS * pdispparams, | |
| __out_opt VARIANT * pvarResult, | |
| __out_opt EXCEPINFO * pexcepinfo, | |
| __out_opt 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(__in_opt LPCTSTR name,__in_opt LPUNKNOWN pUnk) : | |
| CUnknown(name, pUnk) | |
| { | |
| } | |
| CMediaPosition::CMediaPosition(__in_opt LPCTSTR name, | |
| __in_opt LPUNKNOWN pUnk, | |
| __inout HRESULT * phr) : | |
| CUnknown(name, pUnk) | |
| { | |
| UNREFERENCED_PARAMETER(phr); | |
| } | |
| // expose our interfaces IMediaPosition and IUnknown | |
| STDMETHODIMP | |
| CMediaPosition::NonDelegatingQueryInterface(REFIID riid, __deref_out 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(__out UINT * pctinfo) | |
| { | |
| return m_basedisp.GetTypeInfoCount(pctinfo); | |
| } | |
| // attempt to find our type library | |
| STDMETHODIMP | |
| CMediaPosition::GetTypeInfo( | |
| UINT itinfo, | |
| LCID lcid, | |
| __deref_out ITypeInfo ** pptinfo) | |
| { | |
| return m_basedisp.GetTypeInfo( | |
| IID_IMediaPosition, | |
| itinfo, | |
| lcid, | |
| pptinfo); | |
| } | |
| STDMETHODIMP | |
| CMediaPosition::GetIDsOfNames( | |
| REFIID riid, | |
| __in_ecount(cNames) LPOLESTR * rgszNames, | |
| UINT cNames, | |
| LCID lcid, | |
| __out_ecount(cNames) DISPID * rgdispid) | |
| { | |
| return m_basedisp.GetIDsOfNames( | |
| IID_IMediaPosition, | |
| rgszNames, | |
| cNames, | |
| lcid, | |
| rgdispid); | |
| } | |
| STDMETHODIMP | |
| CMediaPosition::Invoke( | |
| DISPID dispidMember, | |
| REFIID riid, | |
| LCID lcid, | |
| WORD wFlags, | |
| __in DISPPARAMS * pdispparams, | |
| __out_opt VARIANT * pvarResult, | |
| __out_opt EXCEPINFO * pexcepinfo, | |
| __out_opt 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(__in_opt LPCTSTR pName, | |
| __in_opt LPUNKNOWN pUnk, | |
| __inout 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,__deref_out void **ppv) | |
| { | |
| CheckPointer(ppv,E_POINTER); | |
| *ppv = NULL; | |
| if (riid == IID_IMediaSeeking) { | |
| return GetInterface( static_cast<IMediaSeeking *>(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(__deref_out 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(__out DWORD * pCaps) | |
| { | |
| IMediaSeeking* pMS; | |
| HRESULT hr = GetPeerSeeking(&pMS); | |
| if (FAILED(hr)) { | |
| return hr; | |
| } | |
| hr = pMS->GetCapabilities(pCaps); | |
| pMS->Release(); | |
| return hr; | |
| } | |
| STDMETHODIMP | |
| CPosPassThru::CheckCapabilities(__inout 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(__out 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(__out 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(__out LONGLONG * pTarget, | |
| __in_opt const GUID * pTargetFormat, | |
| LONGLONG Source, | |
| __in_opt 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( __inout_opt LONGLONG * pCurrent, | |
| DWORD CurrentFlags, | |
| __inout_opt 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(__out_opt LONGLONG *pCurrent, __out_opt 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)( __out 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(__out 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(__out LONGLONG *pStop) | |
| { | |
| return GetSeekingLongLong( &IMediaSeeking::GetStopPosition, pStop );; | |
| } | |
| STDMETHODIMP | |
| CPosPassThru::GetDuration(__out LONGLONG *pDuration) | |
| { | |
| return GetSeekingLongLong( &IMediaSeeking::GetDuration, pDuration );; | |
| } | |
| STDMETHODIMP | |
| CPosPassThru::GetPreroll(__out LONGLONG *pllPreroll) | |
| { | |
| return GetSeekingLongLong( &IMediaSeeking::GetPreroll, pllPreroll );; | |
| } | |
| STDMETHODIMP | |
| CPosPassThru::GetAvailable( __out_opt LONGLONG *pEarliest, __out_opt 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(__out 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(__out 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(__out 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(__out 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(__out 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(__out 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(__out LONG *pCanSeekForward) | |
| { | |
| IMediaPosition* pMP; | |
| HRESULT hr = GetPeer(&pMP); | |
| if (FAILED(hr)) { | |
| return hr; | |
| } | |
| hr = pMP->CanSeekForward(pCanSeekForward); | |
| pMP->Release(); | |
| return hr; | |
| } | |
| STDMETHODIMP | |
| CPosPassThru::CanSeekBackward(__out 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(__in_opt LPCTSTR pName, | |
| __in_opt LPUNKNOWN pUnk, | |
| __inout 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(__out LONGLONG *pStartTime, __out_opt 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( | |
| __in_opt LPCTSTR pName, | |
| __in_opt LPUNKNOWN pUnk, | |
| __inout HRESULT* phr, | |
| __in 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, __deref_out void **ppv) | |
| { | |
| if(riid == IID_IMediaSeeking) { | |
| CheckPointer(ppv, E_POINTER); | |
| return GetInterface(static_cast<IMediaSeeking *>(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(__out 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(__out GUID *pFormat) | |
| { | |
| CheckPointer(pFormat, E_POINTER); | |
| *pFormat = TIME_FORMAT_MEDIA_TIME; | |
| return S_OK; | |
| } | |
| HRESULT CSourceSeeking::GetDuration(__out LONGLONG *pDuration) | |
| { | |
| CheckPointer(pDuration, E_POINTER); | |
| CAutoLock lock(m_pLock); | |
| *pDuration = m_rtDuration; | |
| return S_OK; | |
| } | |
| HRESULT CSourceSeeking::GetStopPosition(__out LONGLONG *pStop) | |
| { | |
| CheckPointer(pStop, E_POINTER); | |
| CAutoLock lock(m_pLock); | |
| *pStop = m_rtStop; | |
| return S_OK; | |
| } | |
| HRESULT CSourceSeeking::GetCurrentPosition(__out LONGLONG *pCurrent) | |
| { | |
| // GetCurrentPosition is typically supported only in renderers and | |
| // not in source filters. | |
| return E_NOTIMPL; | |
| } | |
| HRESULT CSourceSeeking::GetCapabilities( __out DWORD * pCapabilities ) | |
| { | |
| CheckPointer(pCapabilities, E_POINTER); | |
| *pCapabilities = m_dwSeekingCaps; | |
| return S_OK; | |
| } | |
| HRESULT CSourceSeeking::CheckCapabilities( __inout 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( __out LONGLONG * pTarget, | |
| __in_opt const GUID * pTargetFormat, | |
| LONGLONG Source, | |
| __in_opt 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( __inout_opt LONGLONG * pCurrent, | |
| DWORD CurrentFlags, | |
| __inout_opt 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( __out_opt LONGLONG * pCurrent, __out_opt LONGLONG * pStop ) | |
| { | |
| if(pCurrent) { | |
| *pCurrent = m_rtStart; | |
| } | |
| if(pStop) { | |
| *pStop = m_rtStop; | |
| } | |
| return S_OK;; | |
| } | |
| HRESULT CSourceSeeking::GetAvailable( __out_opt LONGLONG * pEarliest, __out_opt 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( __out double * pdRate) | |
| { | |
| CheckPointer(pdRate, E_POINTER); | |
| CAutoLock lock(m_pLock); | |
| *pdRate = m_dRateSeeking; | |
| return S_OK; | |
| } | |
| HRESULT CSourceSeeking::GetPreroll(__out LONGLONG *pPreroll) | |
| { | |
| CheckPointer(pPreroll, E_POINTER); | |
| *pPreroll = 0; | |
| return S_OK; | |
| } | |
| // --- CSourcePosition implementation ---------- | |
| CSourcePosition::CSourcePosition(__in_opt LPCTSTR pName, | |
| __in_opt LPUNKNOWN pUnk, | |
| __inout HRESULT* phr, | |
| __in 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(__out 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(__out 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(__out 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(__out 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(__out LONG *pCanSeekForward) | |
| { | |
| CheckPointer(pCanSeekForward,E_POINTER); | |
| *pCanSeekForward = OATRUE; | |
| return S_OK; | |
| } | |
| // By default we can seek backwards | |
| STDMETHODIMP | |
| CSourcePosition::CanSeekBackward(__out LONG *pCanSeekBackward) | |
| { | |
| CheckPointer(pCanSeekBackward,E_POINTER); | |
| *pCanSeekBackward = OATRUE; | |
| return S_OK; | |
| } | |
| // --- Implementation of CBasicAudio class ---------- | |
| CBasicAudio::CBasicAudio(__in_opt LPCTSTR pName,__in_opt LPUNKNOWN punk) : | |
| CUnknown(pName, punk) | |
| { | |
| } | |
| // overriden to publicise our interfaces | |
| STDMETHODIMP | |
| CBasicAudio::NonDelegatingQueryInterface(REFIID riid, __deref_out void **ppv) | |
| { | |
| ValidateReadWritePtr(ppv,sizeof(PVOID)); | |
| if (riid == IID_IBasicAudio) { | |
| return GetInterface( (IBasicAudio *) this, ppv); | |
| } else { | |
| return CUnknown::NonDelegatingQueryInterface(riid, ppv); | |
| } | |
| } | |
| STDMETHODIMP | |
| CBasicAudio::GetTypeInfoCount(__out UINT * pctinfo) | |
| { | |
| return m_basedisp.GetTypeInfoCount(pctinfo); | |
| } | |
| STDMETHODIMP | |
| CBasicAudio::GetTypeInfo( | |
| UINT itinfo, | |
| LCID lcid, | |
| __deref_out ITypeInfo ** pptinfo) | |
| { | |
| return m_basedisp.GetTypeInfo( | |
| IID_IBasicAudio, | |
| itinfo, | |
| lcid, | |
| pptinfo); | |
| } | |
| STDMETHODIMP | |
| CBasicAudio::GetIDsOfNames( | |
| REFIID riid, | |
| __in_ecount(cNames) LPOLESTR * rgszNames, | |
| UINT cNames, | |
| LCID lcid, | |
| __out_ecount(cNames) DISPID * rgdispid) | |
| { | |
| return m_basedisp.GetIDsOfNames( | |
| IID_IBasicAudio, | |
| rgszNames, | |
| cNames, | |
| lcid, | |
| rgdispid); | |
| } | |
| STDMETHODIMP | |
| CBasicAudio::Invoke( | |
| DISPID dispidMember, | |
| REFIID riid, | |
| LCID lcid, | |
| WORD wFlags, | |
| __in DISPPARAMS * pdispparams, | |
| __out_opt VARIANT * pvarResult, | |
| __out_opt EXCEPINFO * pexcepinfo, | |
| __out_opt 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(__in_opt LPCTSTR pName,__in_opt LPUNKNOWN punk) : | |
| CUnknown(pName, punk) | |
| { | |
| } | |
| // overriden to publicise our interfaces | |
| STDMETHODIMP | |
| CBaseVideoWindow::NonDelegatingQueryInterface(REFIID riid, __deref_out void **ppv) | |
| { | |
| ValidateReadWritePtr(ppv,sizeof(PVOID)); | |
| if (riid == IID_IVideoWindow) { | |
| return GetInterface( (IVideoWindow *) this, ppv); | |
| } else { | |
| return CUnknown::NonDelegatingQueryInterface(riid, ppv); | |
| } | |
| } | |
| STDMETHODIMP | |
| CBaseVideoWindow::GetTypeInfoCount(__out UINT * pctinfo) | |
| { | |
| return m_basedisp.GetTypeInfoCount(pctinfo); | |
| } | |
| STDMETHODIMP | |
| CBaseVideoWindow::GetTypeInfo( | |
| UINT itinfo, | |
| LCID lcid, | |
| __deref_out ITypeInfo ** pptinfo) | |
| { | |
| return m_basedisp.GetTypeInfo( | |
| IID_IVideoWindow, | |
| itinfo, | |
| lcid, | |
| pptinfo); | |
| } | |
| STDMETHODIMP | |
| CBaseVideoWindow::GetIDsOfNames( | |
| REFIID riid, | |
| __in_ecount(cNames) LPOLESTR * rgszNames, | |
| UINT cNames, | |
| LCID lcid, | |
| __out_ecount(cNames) DISPID * rgdispid) | |
| { | |
| return m_basedisp.GetIDsOfNames( | |
| IID_IVideoWindow, | |
| rgszNames, | |
| cNames, | |
| lcid, | |
| rgdispid); | |
| } | |
| STDMETHODIMP | |
| CBaseVideoWindow::Invoke( | |
| DISPID dispidMember, | |
| REFIID riid, | |
| LCID lcid, | |
| WORD wFlags, | |
| __in DISPPARAMS * pdispparams, | |
| __out_opt VARIANT * pvarResult, | |
| __out_opt EXCEPINFO * pexcepinfo, | |
| __out_opt 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(__in_opt LPCTSTR pName,__in_opt LPUNKNOWN punk) : | |
| CUnknown(pName, punk) | |
| { | |
| } | |
| // overriden to publicise our interfaces | |
| STDMETHODIMP | |
| CBaseBasicVideo::NonDelegatingQueryInterface(REFIID riid, __deref_out void **ppv) | |
| { | |
| ValidateReadWritePtr(ppv,sizeof(PVOID)); | |
| if (riid == IID_IBasicVideo || riid == IID_IBasicVideo2) { | |
| return GetInterface( static_cast<IBasicVideo2 *>(this), ppv); | |
| } else { | |
| return CUnknown::NonDelegatingQueryInterface(riid, ppv); | |
| } | |
| } | |
| STDMETHODIMP | |
| CBaseBasicVideo::GetTypeInfoCount(__out UINT * pctinfo) | |
| { | |
| return m_basedisp.GetTypeInfoCount(pctinfo); | |
| } | |
| STDMETHODIMP | |
| CBaseBasicVideo::GetTypeInfo( | |
| UINT itinfo, | |
| LCID lcid, | |
| __deref_out ITypeInfo ** pptinfo) | |
| { | |
| return m_basedisp.GetTypeInfo( | |
| IID_IBasicVideo, | |
| itinfo, | |
| lcid, | |
| pptinfo); | |
| } | |
| STDMETHODIMP | |
| CBaseBasicVideo::GetIDsOfNames( | |
| REFIID riid, | |
| __in_ecount(cNames) LPOLESTR * rgszNames, | |
| UINT cNames, | |
| LCID lcid, | |
| __out_ecount(cNames) DISPID * rgdispid) | |
| { | |
| return m_basedisp.GetIDsOfNames( | |
| IID_IBasicVideo, | |
| rgszNames, | |
| cNames, | |
| lcid, | |
| rgdispid); | |
| } | |
| STDMETHODIMP | |
| CBaseBasicVideo::Invoke( | |
| DISPID dispidMember, | |
| REFIID riid, | |
| LCID lcid, | |
| WORD wFlags, | |
| __in DISPPARAMS * pdispparams, | |
| __out_opt VARIANT * pvarResult, | |
| __out_opt EXCEPINFO * pexcepinfo, | |
| __out_opt 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, __in_ecount(nArgs) VARIANT* pArgs, __inout_opt 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++) { | |
| // Why aren't we using VariantCopy? | |
| 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 ((PVOID)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; | |
| } | |
| } | |
| } | |
| 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: | |
| // Explicitly cast BSTR to PVOID to tell code scanning tools we really mean to test the pointer | |
| if ((PVOID)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( | |
| __inout CCmdQueue * pQ, | |
| __in_opt LPUNKNOWN pUnk, | |
| __inout HRESULT * phr, | |
| __in LPUNKNOWN pUnkExecutor, | |
| REFTIME time, | |
| __in GUID* iid, | |
| long dispidMethod, | |
| short wFlags, | |
| long nArgs, | |
| __in_ecount(nArgs) VARIANT* pDispParams, | |
| __out VARIANT* pvarResult, | |
| __out 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, __out 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(__out LONG* pConfidence) | |
| { | |
| return E_NOTIMPL; | |
| } | |
| STDMETHODIMP | |
| CDeferredCommand::GetHResult(__out 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(__inout_opt HRESULT *phr) : | |
| m_listPresentation(NAME("Presentation time command list")), | |
| m_listStream(NAME("Stream time command list")), | |
| m_evDue(TRUE, phr), // 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( | |
| __out CDeferredCommand **ppCmd, | |
| __in LPUNKNOWN pUnk, // this object will execute command | |
| REFTIME time, | |
| __in GUID* iid, | |
| long dispidMethod, | |
| short wFlags, | |
| long cArgs, | |
| __in_ecount(cArgs) VARIANT* pDispParams, | |
| __out VARIANT* pvarResult, | |
| __out 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(__in CDeferredCommand* pCmd) | |
| { | |
| CAutoLock lock(&m_Lock); | |
| // addref the item | |
| pCmd->AddRef(); | |
| CGenericList<CDeferredCommand> * 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->GetValid(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(__in CDeferredCommand* pCmd) | |
| { | |
| CAutoLock lock(&m_Lock); | |
| HRESULT hr = S_OK; | |
| CGenericList<CDeferredCommand> * pList; | |
| if (pCmd->IsStreamTime()) { | |
| pList = &m_listStream; | |
| } else { | |
| pList = &m_listPresentation; | |
| } | |
| POSITION pos = pList->GetHeadPosition(); | |
| // traverse the list | |
| while (pos && (pList->GetValid(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(__in_opt 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 | |
| POSITION pos = m_listPresentation.GetHeadPosition(); | |
| if (pos != NULL) { | |
| current = m_listPresentation.GetValid(pos)->GetTime(); | |
| } | |
| // if we're running, check the stream times too | |
| if (m_bRunning) { | |
| CRefTime t; | |
| pos = m_listStream.GetHeadPosition(); | |
| if (NULL != pos) { | |
| t = m_listStream.GetValid(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(__out 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 | |
| POSITION pos = m_listPresentation.GetHeadPosition(); | |
| if (NULL != pos) { | |
| pCmd = m_listPresentation.GetValid(pos); | |
| } | |
| if (m_bRunning) { | |
| pos = m_listStream.GetHeadPosition(); | |
| if (NULL != pos) { | |
| CDeferredCommand* pStrm = m_listStream.GetValid(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, __out CDeferredCommand**ppCmd) | |
| { | |
| CAutoLock lock(&m_Lock); | |
| CRefTime tStream(rtStream); | |
| // find the earliest stream and presentation time commands | |
| CDeferredCommand* pStream = NULL; | |
| POSITION pos = m_listStream.GetHeadPosition(); | |
| if (NULL != pos) { | |
| pStream = m_listStream.GetValid(pos); | |
| } | |
| CDeferredCommand* pPresent = NULL; | |
| pos = m_listPresentation.GetHeadPosition(); | |
| if (NULL != pos) { | |
| pPresent = m_listPresentation.GetValid(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)) { | |
| pStream->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; | |
| } | |