📄 ctlutil.cpp
字号:
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 + -