📄 renbaseasync.cpp
字号:
CAutoLock cSampleLock(&m_RendererLock);
ASSERT(IsActive() == TRUE);
ASSERT(m_pInputPin->IsFlushing() == FALSE);
ASSERT(m_pInputPin->IsConnected() == TRUE);
ASSERT(m_pMediaSample == NULL);
// check that no data is being sent after the source signalled an end of stream
if (m_bEOS || m_bAbort) {
Ready();
return E_UNEXPECTED;
}
// Store the media times from this sample
if (m_pPosition) m_pPosition->RegisterMediaTime(pMediaSample);
//
// Queue the received sample. Note that we AddRef pMediaSample since we're
// returning without using it immediately.
//
CRendererSample *pRendererSample = new CRendererSample(pMediaSample);
if (!pRendererSample)
{
return E_OUTOFMEMORY;
}
m_RendererQueue.Insert (pRendererSample);
//
// If we're streaming, set an event for proper scheduling by the worker thread.
//
if (m_bStreaming)
{
m_ScheduleEvent.Set();
}
// Store the sample end time for EC_COMPLETE handling
m_SignalTime = m_pInputPin->SampleProps()->tStop;
if (m_bStreaming == FALSE) {
SetRepaintStatus(TRUE);
}
return NOERROR;
}
//
// Called by the source filter when we have a sample to render. We just prepare for
// receiving the sample, and return immediately. The sample is going to be processed
// by the renderer thread (scheduled and rendered).
//
HRESULT CBaseRendererAsync::Receive(IMediaSample *pSample)
{
ASSERT(pSample);
// It may return VFW_E_SAMPLE_REJECTED code to say don't bother
HRESULT hr = PrepareReceive(pSample);
if (FAILED(hr)) {
if (hr == VFW_E_SAMPLE_REJECTED) {
return NOERROR;
}
return hr;
}
Ready(); // signal we have a sample now
return NOERROR;
}
BOOL CBaseRendererAsync::HaveCurrentSample()
{
CAutoLock cRendererLock(&m_RendererLock);
CRendererSample *pRendererSample;
return ((m_RendererQueue.Peek (&pRendererSample) == S_OK) ? TRUE : FALSE);
}
BOOL CBaseRendererAsync::ScheduleSample(IMediaSample *pMediaSample)
{
// We override ShouldDrawSampleNow to add quality management
return CBaseRenderer::ScheduleSample(pMediaSample);
}
//
// Verifies if there's any sample in our renderer sample queue, and if
// there's a sample which is not scheduled, schedule it.
//
HRESULT CBaseRendererAsync::ScheduleQueuedSample (void)
{
CAutoLock cSampleLock(&m_RendererLock); // make sure we don't try to schedule the same sample twice
CRendererSample *pRendererSample;
HRESULT hr = m_RendererQueue.Peek (&pRendererSample);
if (hr == S_OK && !pRendererSample->IsScheduled())
{
DWORD dwAdvise = m_dwAdvise;
if (ScheduleSample (pRendererSample->MediaSample()))
{
pRendererSample->SetScheduled(TRUE);
m_pScheduledSample = pRendererSample;
}
else
{
// could not schedule this sample, remove it from the queue and move on...
hr = m_RendererQueue.Remove (&pRendererSample);
if (hr != S_OK || !pRendererSample)
{
return hr;
}
delete pRendererSample; // we don't need the queued sample anymore
}
}
return hr;
}
//
// Waits for the correct presentation time and then renders
// the sample.
//
HRESULT CBaseRendererAsync::ProcessQueuedSample (void)
{
CRendererSample *pRendererSample;
IMediaSample *pMediaSample;
CAutoLock cSampleLock(&m_RendererLock);
HRESULT hr = m_RendererQueue.Remove (&pRendererSample);
if (hr != S_OK || !pRendererSample)
{
return hr;
}
pMediaSample = pRendererSample->MediaSample();
ASSERT (pMediaSample);
if (!pMediaSample)
return E_FAIL;
//
// BEWARE we sometimes keep the sample even after rendering it,
// such as when we go into a stopped state (we keep it
// to refresh the device with) so we must AddRef it to keep it safely. If
// we start flushing the source thread is released and any sample waiting
// will be released otherwise GetBuffer may never return (see BeginFlush)
//
m_pMediaSample = pMediaSample;
m_pMediaSample->AddRef();
delete pRendererSample; // we don't need the queued sample anymore
PrepareRender();
// since we gave away the filter wide lock, the state of the filter could
// have changed to Stopped
if (m_State == State_Stopped)
return NOERROR;
// Deal with this sample
if (m_bFirstSample)
{
OnReceiveFirstSample(m_pMediaSample);
}
else
{
Render(m_pMediaSample);
}
m_bFirstSample = FALSE;
ClearPendingSample();
SendEndOfStream();
CancelNotification();
return NOERROR;
}
//
// This is called when we start running so that we can schedule any pending
// image we have with the clock and display any timing information. If we
// don't have any sample but we have queued an EOS flag then we send it. If
// we do have a sample then we wait until that has been rendered before we
// signal the filter graph otherwise we may change state before it's done
//
HRESULT CBaseRendererAsync::StartStreaming()
{
CAutoLock cRendererLock(&m_RendererLock);
CRendererSample *pRendererSample;
if (m_bStreaming == TRUE) {
return NOERROR;
}
// Reset the streaming times ready for running
m_bStreaming = TRUE;
timeBeginPeriod(1);
OnStartStreaming();
// There should be no outstanding advise
ASSERT(WAIT_TIMEOUT == WaitForSingleObject((HANDLE)m_RenderEvent,0));
ASSERT(CancelNotification() == S_FALSE);
//
// If we have an EOS and no data then deliver it now
//
if (m_RendererQueue.Peek(&pRendererSample) != S_OK)
{
return SendEndOfStream();
}
//
// Set an event for the worker thread to start scheduling
//
m_ScheduleEvent.Set();
return NOERROR;
}
//
// This is called when we stop streaming so that we can set our internal flag
// indicating we are not to schedule any more samples arriving. The state
// change methods in the filter implementation take care of cancelling any
// clock advise link we have set up and clearing any pending samples we have
//
HRESULT CBaseRendererAsync::StopStreaming()
{
CAutoLock cRendererLock(&m_RendererLock);
m_bEOSDelivered = FALSE;
if (m_bStreaming == TRUE) {
m_bStreaming = FALSE;
OnStopStreaming();
timeEndPeriod(1);
}
return NOERROR;
}
//
// When a video window detects a display change (WM_DISPLAYCHANGE message) it
// can send an EC_DISPLAY_CHANGED event code along with the renderer pin. The
// filtergraph will stop everyone and reconnect our input pin. As we're then
// reconnected we can accept the media type that matches the new display mode
// since we may no longer be able to draw the current image type efficiently
//
BOOL CBaseRendererAsync::OnDisplayChange()
{
BOOL bOk = CBaseRenderer::OnDisplayChange();
m_RendererQueue.Flush();
return bOk;
}
//
// Worker thread related functionality
//
HRESULT CBaseRendererAsync::RunThread()
{
DbgLog((LOG_TRACE, 2, TEXT("CBaseRenderer::RunThread")));
return CallWorker(CMD_RUN);
}
HRESULT CBaseRendererAsync::StopThread()
{
DbgLog((LOG_TRACE, 2, TEXT("CBaseRenderer::StopThread")));
return CallWorker(CMD_STOP);
}
HRESULT CBaseRendererAsync::KillThread()
{
// !!! CAutoLock lock(&m_AccessLock);
DbgLog((LOG_TRACE, 2, TEXT("CBaseRendererAsync:KillThread")));
HRESULT hr = CallWorker(CMD_EXIT);
if (FAILED(hr))
{
return hr;
}
// wait for thread completion and then close
// handle (and clear so we can start another later)
Close();
return NOERROR;
}
//
// This thread is running the whole time the graph is active. It needs to monitor the
// passing of stream time and wake up when it wants to render an object.
//
DWORD CBaseRendererAsync::ThreadProc()
{
BOOL bExit = FALSE;
DbgLog((LOG_TRACE, 2, TEXT("CBaseRendererAsync::ThreadProc - Starting thread = 0x%x"), m_hThread));
while (!bExit)
{
Command req = GetRequest();
switch (req)
{
case CMD_EXIT:
DbgLog((LOG_TRACE, 2, TEXT("CBaseRendererAsync::ThreadProc - CMD_EXIT received")));
m_RendererQueue.Flush();
bExit = TRUE;
Reply(NOERROR);
break;
case CMD_STOP:
DbgLog((LOG_TRACE, 2, TEXT("CBaseRendererAsync::ThreadProc - CMD_STOP received")));
m_RendererQueue.Flush();
Reply(NOERROR);
break;
case CMD_RUN:
HANDLE events[4];
BOOL bRunning = TRUE;
DbgLog((LOG_TRACE, 2, TEXT("CBaseRendererAsync::ThreadProc - CMD_RUN received")));
Reply(NOERROR);
DWORD dwCurrentPriority = CeGetThreadPriority(GetCurrentThread());
if (dwCurrentPriority != s_dwVideoThreadPriority)
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -