//------------------------------------------------------------------------------ | |
// 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; | |
} | |