📄 ctlutil.cpp
字号:
BOOL bStream
) :
CUnknown(NAME("DeferredCommand"), pUnk),
m_pQueue(pQ),
m_pUnk(pUnkExecutor),
m_iid(iid),
m_dispidMethod(dispidMethod),
m_wFlags(wFlags),
m_DispParams(nArgs, pDispParams, phr),
m_pvarResult(pvarResult),
m_bStream(bStream),
m_hrResult(E_ABORT)
{
// convert REFTIME to REFERENCE_TIME
COARefTime convertor(time);
m_time = convertor;
// no check of time validity - it's ok to queue a command that's
// already late
// check iid is supportable on pUnk by QueryInterface for it
IUnknown * pInterface;
HRESULT hr = m_pUnk->QueryInterface(GetIID(), (void**) &pInterface);
if (FAILED(hr)) {
*phr = hr;
return;
}
pInterface->Release();
// !!! check dispidMethod and param/return types using typelib
ITypeInfo *pti;
hr = m_Dispatch.GetTypeInfo(*iid, 0, 0, &pti);
if (FAILED(hr)) {
*phr = hr;
return;
}
// !!! some sort of ITypeInfo validity check here
pti->Release();
// Fix up the dispid for put and get
if (wFlags == DISPATCH_PROPERTYPUT) {
m_DispParams.cNamedArgs = 1;
m_DispId = DISPID_PROPERTYPUT;
m_DispParams.rgdispidNamedArgs = &m_DispId;
}
// all checks ok - add to queue
hr = pQ->Insert(this);
if (FAILED(hr)) {
*phr = hr;
}
}
// refcounts are held by caller of InvokeAt... and by list. So if
// we get here, we can't be on the list
#if 0
CDeferredCommand::~CDeferredCommand()
{
// this assert is invalid since if the queue is deleted while we are
// still on the queue, we will have been removed by the queue and this
// m_pQueue will not have been modified.
// ASSERT(m_pQueue == NULL);
// we don't hold a ref count on pUnk, which is the object that should
// execute the command.
// This is because there would otherwise be a circular refcount problem
// since pUnk probably owns the CmdQueue object that has a refcount
// on us.
// The lifetime of pUnk is guaranteed by it being part of, or lifetime
// controlled by, our parent object. As long as we are on the list, pUnk
// must be valid. Once we are off the list, we do not use pUnk.
}
#endif
// overriden to publicise our interfaces
STDMETHODIMP
CDeferredCommand::NonDelegatingQueryInterface(REFIID riid, void **ppv)
{
ValidateReadWritePtr(ppv,sizeof(PVOID));
if (riid == IID_IDeferredCommand) {
return GetInterface( (IDeferredCommand *) this, ppv);
} else {
return CUnknown::NonDelegatingQueryInterface(riid, ppv);
}
}
// remove from q. this will reduce the refcount by one (since the q
// holds a count) but can't make us go away since he must have a
// refcount in order to call this method.
STDMETHODIMP
CDeferredCommand::Cancel()
{
if (m_pQueue == NULL) {
return VFW_E_ALREADY_CANCELLED;
}
HRESULT hr = m_pQueue->Remove(this);
if (FAILED(hr)) {
return hr;
}
m_pQueue = NULL;
return S_OK;
}
STDMETHODIMP
CDeferredCommand::Confidence(LONG* pConfidence)
{
return E_NOTIMPL;
}
STDMETHODIMP
CDeferredCommand::GetHResult(HRESULT * phrResult)
{
CheckPointer(phrResult,E_POINTER);
ValidateReadWritePtr(phrResult,sizeof(HRESULT));
if (m_pQueue != NULL) {
return E_ABORT;
}
*phrResult = m_hrResult;
return S_OK;
}
// set the time to be a new time (checking that it is valid) and
// then requeue
STDMETHODIMP
CDeferredCommand::Postpone(REFTIME newtime)
{
// check that this time is not past
// convert REFTIME to REFERENCE_TIME
COARefTime convertor(newtime);
// check that the time has not passed
if (m_pQueue->CheckTime(convertor, IsStreamTime())) {
return VFW_E_TIME_ALREADY_PASSED;
}
// extract from list
HRESULT hr = m_pQueue->Remove(this);
if (FAILED(hr)) {
return hr;
}
// change time
m_time = convertor;
// requeue
hr = m_pQueue->Insert(this);
return hr;
}
HRESULT
CDeferredCommand::Invoke()
{
// check that we are still outstanding
if (m_pQueue == NULL) {
return VFW_E_ALREADY_CANCELLED;
}
// get the type info
ITypeInfo* pti;
HRESULT hr = m_Dispatch.GetTypeInfo(GetIID(), 0, 0, &pti);
if (FAILED(hr)) {
return hr;
}
// qi for the expected interface and then invoke it. Note that we have to
// treat the returned interface as IUnknown since we don't know its type.
IUnknown* pInterface;
hr = m_pUnk->QueryInterface(GetIID(), (void**) &pInterface);
if (FAILED(hr)) {
pti->Release();
return hr;
}
EXCEPINFO expinfo;
UINT uArgErr;
m_hrResult = pti->Invoke(
pInterface,
GetMethod(),
GetFlags(),
GetParams(),
GetResult(),
&expinfo,
&uArgErr);
// release the interface we QI'd for
pInterface->Release();
pti->Release();
// remove from list whether or not successful
// or we loop indefinitely
hr = m_pQueue->Remove(this);
m_pQueue = NULL;
return hr;
}
// --- CCmdQueue methods ----------
CCmdQueue::CCmdQueue() :
m_listPresentation(NAME("Presentation time command list")),
m_listStream(NAME("Stream time command list")),
m_evDue(TRUE), // manual reset
m_dwAdvise(0),
m_pClock(NULL),
m_bRunning(FALSE)
{
}
CCmdQueue::~CCmdQueue()
{
// empty all our lists
// we hold a refcount on each, so traverse and Release each
// entry then RemoveAll to empty the list
POSITION pos = m_listPresentation.GetHeadPosition();
while(pos) {
CDeferredCommand* pCmd = m_listPresentation.GetNext(pos);
pCmd->Release();
}
m_listPresentation.RemoveAll();
pos = m_listStream.GetHeadPosition();
while(pos) {
CDeferredCommand* pCmd = m_listStream.GetNext(pos);
pCmd->Release();
}
m_listStream.RemoveAll();
if (m_pClock) {
if (m_dwAdvise) {
m_pClock->Unadvise(m_dwAdvise);
m_dwAdvise = 0;
}
m_pClock->Release();
}
}
// returns a new CDeferredCommand object that will be initialised with
// the parameters and will be added to the queue during construction.
// returns S_OK if successfully created otherwise an error and
// no object has been queued.
HRESULT
CCmdQueue::New(
CDeferredCommand **ppCmd,
LPUNKNOWN pUnk, // this object will execute command
REFTIME time,
GUID* iid,
long dispidMethod,
short wFlags,
long cArgs,
VARIANT* pDispParams,
VARIANT* pvarResult,
short* puArgErr,
BOOL bStream
)
{
CAutoLock lock(&m_Lock);
HRESULT hr = S_OK;
*ppCmd = NULL;
CDeferredCommand* pCmd;
pCmd = new CDeferredCommand(
this,
NULL, // not aggregated
&hr,
pUnk, // this guy will execute
time,
iid,
dispidMethod,
wFlags,
cArgs,
pDispParams,
pvarResult,
puArgErr,
bStream);
if (pCmd == NULL) {
hr = E_OUTOFMEMORY;
} else {
*ppCmd = pCmd;
}
return hr;
}
HRESULT
CCmdQueue::Insert(CDeferredCommand* pCmd)
{
CAutoLock lock(&m_Lock);
// addref the item
pCmd->AddRef();
CGenericList<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->Get(pos)->GetTime() <= pCmd->GetTime())) {
pList->GetNext(pos);
}
// now at end of list or in front of items that come later
if (!pos) {
pList->AddTail(pCmd);
} else {
pList->AddBefore(pos, pCmd);
}
SetTimeAdvise();
return S_OK;
}
HRESULT
CCmdQueue::Remove(CDeferredCommand* pCmd)
{
CAutoLock lock(&m_Lock);
HRESULT hr = S_OK;
CGenericList<CDeferredCommand> * pList;
if (pCmd->IsStreamTime()) {
pList = &m_listStream;
} else {
pList = &m_listPresentation;
}
POSITION pos = pList->GetHeadPosition();
// traverse the list
while (pos && (pList->Get(pos) != pCmd)) {
pList->GetNext(pos);
}
// did we drop off the end?
if (!pos) {
hr = VFW_E_NOT_FOUND;
} else {
// found it - now take off list
pList->Remove(pos);
// Insert did an AddRef, so release it
pCmd->Release();
// check that timer request is still for earliest time
SetTimeAdvise();
}
return hr;
}
// set the clock used for timing
HRESULT
CCmdQueue::SetSyncSource(IReferenceClock* pClock)
{
CAutoLock lock(&m_Lock);
// addref the new clock first in case they are the same
if (pClock) {
pClock->AddRef();
}
// kill any advise on the old clock
if (m_pClock) {
if (m_dwAdvise) {
m_pClock->Unadvise(m_dwAdvise);
m_dwAdvise = 0;
}
m_pClock->Release();
}
m_pClock = pClock;
// set up a new advise
SetTimeAdvise();
return S_OK;
}
// set up a timer event with the reference clock
void
CCmdQueue::SetTimeAdvise(void)
{
// make sure we have a clock to use
if (!m_pClock) {
return;
}
// reset the event whenever we are requesting a new signal
m_evDue.Reset();
// time 0 is earliest
CRefTime current;
// find the earliest presentation time
if (m_listPresentation.GetCount() > 0) {
POSITION pos = m_listPresentation.GetHeadPosition();
current = m_listPresentation.Get(pos)->GetTime();
}
// if we're running, check the stream times too
if (m_bRunning) {
CRefTime t;
if (m_listStream.GetCount() > 0) {
POSITION pos = m_listStream.GetHeadPosition();
t = m_listStream.Get(pos)->GetTime();
// add on stream time offset to get presentation time
t += m_StreamTimeOffset;
// is this earlier?
if ((current == TimeZero) || (t < current)) {
current = t;
}
}
}
// need to change?
if ((current > TimeZero) && (current != m_tCurrentAdvise)) {
if (m_dwAdvise) {
m_pClock->Unadvise(m_dwAdvise);
// reset the event whenever we are requesting a new signal
m_evDue.Reset();
}
// ask for time advice - the first two params are either
// stream time offset and stream time or
// presentation time and 0. we always use the latter
HRESULT hr = m_pClock->AdviseTime(
(REFERENCE_TIME)current,
TimeZero,
(HEVENT) HANDLE(m_evDue),
&m_dwAdvise);
ASSERT(SUCCEEDED(hr));
m_tCurrentAdvise = current;
}
}
// switch to run mode. Streamtime to Presentation time mapping known.
HRESULT
CCmdQueue::Run(REFERENCE_TIME tStreamTimeOffset)
{
CAutoLock lock(&m_Lock);
m_StreamTimeOffset = tStreamTimeOffset;
m_bRunning = TRUE;
// ensure advise is accurate
SetTimeAdvise();
return S_OK;
}
// switch to Stopped or Paused mode. Time mapping not known.
HRESULT
CCmdQueue::EndRun()
{
CAutoLock lock(&m_Lock);
m_bRunning = FALSE;
// check timer setting - stream times
SetTimeAdvise();
return S_OK;
}
// return a pointer to the next due command. Blocks for msTimeout
// milliseconds until there is a due command.
// Stream-time commands will only become due between Run and Endrun calls.
// The command remains queued until invoked or cancelled.
// Returns E_ABORT if timeout occurs, otherwise S_OK (or other error).
//
// returns an AddRef'd object
HRESULT
CCmdQueue::GetDueCommand(CDeferredCommand ** ppCmd, long msTimeout)
{
// loop until we timeout or find a due command
for (;;) {
{
CAutoLock lock(&m_Lock);
// find the earliest command
CDeferredCommand * pCmd = NULL;
// check the presentation time and the
// stream time list to find the earliest
if (m_listPresentation.GetCount() > 0) {
POSITION pos = m_listPresentation.GetHeadPosition();
pCmd = m_listPresentation.Get(pos);
}
if (m_bRunning && (m_listStream.GetCount() > 0)) {
POSITION pos = m_listStream.GetHeadPosition();
CDeferredCommand* pStrm = m_listStream.Get(pos);
CRefTime t = pStrm->GetTime() + m_StreamTimeOffset;
if (!pCmd || (t < pCmd->GetTime())) {
pCmd = pStrm;
}
}
// if we have found one, is it due?
if (pCmd) {
if (CheckTime(pCmd->GetTime(), pCmd->IsStreamTime())) {
// yes it's due - addref it
pCmd->AddRef();
*ppCmd = pCmd;
return S_OK;
}
}
}
// block until the advise is signalled
if (WaitForSingleObject(m_evDue, msTimeout) != WAIT_OBJECT_0) {
return E_ABORT;
}
}
}
// return a pointer to a command that will be due for a given time.
// Pass in a stream time here. The stream time offset will be passed
// in via the Run method.
// Commands remain queued until invoked or cancelled.
// This method will not block. It will report E_ABORT if there are no
// commands due yet.
//
// returns an AddRef'd object
HRESULT
CCmdQueue::GetCommandDueFor(REFERENCE_TIME rtStream, CDeferredCommand**ppCmd)
{
CAutoLock lock(&m_Lock);
CRefTime tStream(rtStream);
// find the earliest stream and presentation time commands
CDeferredCommand* pStream = NULL;
if (m_listStream.GetCount() > 0) {
POSITION pos = m_listStream.GetHeadPosition();
pStream = m_listStream.Get(pos);
}
CDeferredCommand* pPresent = NULL;
if (m_listPresentation.GetCount() > 0) {
POSITION pos = m_listPresentation.GetHeadPosition();
pPresent = m_listPresentation.Get(pos);
}
// is there a presentation time that has passed already
if (pPresent && CheckTime(pPresent->GetTime(), FALSE)) {
pPresent->AddRef();
*ppCmd = pPresent;
return S_OK;
}
// is there a stream time command due before this stream time
if (pStream && (pStream->GetTime() <= tStream)) {
pPresent->AddRef();
*ppCmd = pStream;
return S_OK;
}
// if we are running, we can map presentation times to
// stream time. In this case, is there a presentation time command
// that will be due before this stream time is presented?
if (m_bRunning && pPresent) {
// this stream time will appear at...
tStream += m_StreamTimeOffset;
// due before that?
if (pPresent->GetTime() <= tStream) {
*ppCmd = pPresent;
return S_OK;
}
}
// no commands due yet
return VFW_E_NOT_FOUND;
}
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -