mirror of
https://github.com/PCSX2/pcsx2.git
synced 2025-04-02 10:52:54 -04:00
Update it to the version found at https://github.com/Microsoft/Windows-classic-samples , which is in an MIT licensed repo, and add the LICENSE file (edited to remove the SIL Open Font LICENSE part since that doesn't apply). Some modifications have been made to reduce the diff/stop git complaining (not including any file that wasn't in the previous version and removing the related header includes in streams.h, and fixing some but not all of the whitespace issues).
2541 lines
52 KiB
C++
2541 lines
52 KiB
C++
//------------------------------------------------------------------------------
|
|
// 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;
|
|
}
|
|
|