📄 renbase.cpp
字号:
// Constructor must be passed the base renderer object
CRendererInputPin::CRendererInputPin(CBaseRenderer *pRenderer,
HRESULT *phr,
LPCWSTR pPinName) :
CBaseInputPin(NAME("Renderer pin"),
pRenderer,
&pRenderer->m_InterfaceLock,
(HRESULT *) phr,
pPinName)
{
m_pRenderer = pRenderer;
ASSERT(m_pRenderer);
}
// Signals end of data stream on the input pin
STDMETHODIMP CRendererInputPin::EndOfStream()
{
CAutoLock cRendererLock(&m_pRenderer->m_InterfaceLock);
CAutoLock cSampleLock(&m_pRenderer->m_RendererLock);
// Make sure we're streaming ok
HRESULT hr = CheckStreaming();
if (hr != NOERROR) {
return hr;
}
// Pass it onto the renderer
hr = m_pRenderer->EndOfStream();
if (SUCCEEDED(hr)) {
hr = CBaseInputPin::EndOfStream();
}
return hr;
}
// Signals start of flushing on the input pin - we just have the interface lock.
// BeginFlush will do the renderer lock. ResetEndOfStream can't have the renderer lock
// because we call timeKillEvent, our timer callback method
// has to take the renderer lock to serialise our state. Therefore holding a
// renderer lock when calling timeKillEvent could cause a deadlock condition
STDMETHODIMP CRendererInputPin::BeginFlush()
{
CAutoLock cRendererLock(&m_pRenderer->m_InterfaceLock);
CBaseInputPin::BeginFlush();
m_pRenderer->BeginFlush();
return m_pRenderer->ResetEndOfStream();
}
// Signals end of flushing on the input pin
STDMETHODIMP CRendererInputPin::EndFlush()
{
CAutoLock cRendererLock(&m_pRenderer->m_InterfaceLock);
HRESULT hr = m_pRenderer->EndFlush();
if (SUCCEEDED(hr)) {
hr = CBaseInputPin::EndFlush();
}
return hr;
}
// Pass the sample straight through to the renderer object
STDMETHODIMP CRendererInputPin::Receive(IMediaSample *pSample)
{
return m_pRenderer->Receive(pSample);
}
// Let the renderer object know that a new segment is starting
STDMETHODIMP CRendererInputPin::NewSegment(
REFERENCE_TIME tStart,
REFERENCE_TIME tStop,
double dRate)
{
HRESULT hr = CBaseInputPin::NewSegment (tStart, tStop, dRate);
if (SUCCEEDED(hr))
{
m_pRenderer->OnNewSegment (tStart, tStop, dRate);
}
return hr;
}
// Called when the input pin is disconnected
HRESULT CRendererInputPin::BreakConnect()
{
HRESULT hr = m_pRenderer->BreakConnect();
if (FAILED(hr)) {
return hr;
}
return CBaseInputPin::BreakConnect();
}
// Called when the input pin is connected
HRESULT CRendererInputPin::CompleteConnect(IPin *pReceivePin)
{
HRESULT hr = m_pRenderer->CompleteConnect(pReceivePin);
if (FAILED(hr)) {
return hr;
}
return CBaseInputPin::CompleteConnect(pReceivePin);
}
// Give the pin id of our one and only pin
STDMETHODIMP CRendererInputPin::QueryId(LPWSTR *Id)
{
CheckPointer(Id,E_POINTER);
*Id = (LPWSTR)CoTaskMemAlloc(8);
if (*Id == NULL) {
return E_OUTOFMEMORY;
}
lstrcpyW(*Id, L"In");
return NOERROR;
}
// Will the filter accept this media type
HRESULT CRendererInputPin::CheckMediaType(const CMediaType *pmt)
{
return m_pRenderer->CheckMediaType(pmt);
}
// Called when we go paused or running
HRESULT CRendererInputPin::Active()
{
return m_pRenderer->Active();
}
// Called when we go into a stopped state
HRESULT CRendererInputPin::Inactive()
{
return m_pRenderer->Inactive();
}
// Tell derived classes about the media type agreed
HRESULT CRendererInputPin::SetMediaType(const CMediaType *pmt)
{
HRESULT hr = CBaseInputPin::SetMediaType(pmt);
if (FAILED(hr)) {
return hr;
}
return m_pRenderer->SetMediaType(pmt);
}
CBaseVideoRenderer::CBaseVideoRenderer(
REFCLSID RenderClass, // CLSID for this renderer
TCHAR *pName, // Debug ONLY description
LPUNKNOWN pUnk, // Aggregated owner object
HRESULT *phr) : // General OLE return code
CBaseRenderer(RenderClass,pName,pUnk,phr)
{
} // Constructor
// Destructor is just a placeholder
CBaseVideoRenderer::~CBaseVideoRenderer()
{
ASSERT(m_dwAdvise == 0);
}
// Overidden to return our IQualProp interface
STDMETHODIMP
CBaseVideoRenderer::NonDelegatingQueryInterface(REFIID riid,VOID **ppv)
{
// We return IQualProp and delegate everything else
if (riid == IID_IQualProp) {
return CVideoRendererQuality::NonDelegatingQueryInterface (riid, ppv);
} else if (riid == IID_IQualityControl) {
return GetInterface((IBaseFilter *) this, ppv);
} else {
return CBaseRenderer::NonDelegatingQueryInterface(riid,ppv);
}
}
// Override JoinFilterGraph so that, just before leaving
// the graph we can send an EC_WINDOW_DESTROYED event
STDMETHODIMP
CBaseVideoRenderer::JoinFilterGraph(IFilterGraph *pGraph,LPCWSTR pName)
{
// Since we send EC_ACTIVATE, we also need to ensure
// we send EC_WINDOW_DESTROYED or the resource manager may be
// holding us as a focus object
if (!pGraph && m_pGraph) {
// We were in a graph and now we're not
// Do this properly in case we are aggregated
IBaseFilter* pFilter;
QueryInterface(IID_IBaseFilter,(void **) &pFilter);
NotifyEvent(EC_WINDOW_DESTROYED, (LPARAM) pFilter, 0);
pFilter->Release();
}
return CBaseFilter::JoinFilterGraph(pGraph, pName);
}
// The timing functions in this class are called by the window object and by
// the renderer's allocator.
// The windows object calls timing functions as it receives media sample
// images for drawing using GDI.
// The allocator calls timing functions when it starts passing DCI/DirectDraw
// surfaces which are not rendered in the same way; The decompressor writes
// directly to the surface with no separate rendering, so those code paths
// call direct into us. Since we only ever hand out DCI/DirectDraw surfaces
// when we have allocated one and only one image we know there cannot be any
// conflict between the two.
//
// We use timeGetTime to return the timing counts we use (since it's relative
// performance we are interested in rather than absolute compared to a clock)
// The window object sets the accuracy of the system clock (normally 1ms) by
// calling timeBeginPeriod/timeEndPeriod when it changes streaming states
//
// IQualityControl methods
//
STDMETHODIMP CBaseVideoRenderer::SetSink( IQualityControl * piqc)
{
m_pQSink = piqc;
return NOERROR;
} // SetSink
STDMETHODIMP CBaseVideoRenderer::Notify( IBaseFilter * pSelf, Quality q)
{
return CVideoRendererQuality::NotifyQuality(q);
} // Notify
void CBaseVideoRenderer::PrepareRender()
{
if (!m_bFirstFrameRendered)
{
NotifyEvent( EC_VIDEOFRAMEREADY, NULL, NULL );
m_bFirstFrameRendered = TRUE;
}
}
// Send a message to indicate what our supplier should do about quality.
HRESULT CBaseVideoRenderer::SendQuality(REFERENCE_TIME trLate,
REFERENCE_TIME trRealStream)
{
Quality q;
CVideoRendererQuality::GetQuality(trLate, trRealStream, &q);
// A specific sink interface may be set through IPin
if (m_pQSink==NULL) {
// Get our input pin's peer. We send quality management messages
// to any nominated receiver of these things (set in the IPin
// interface), or else to our source filter.
IQualityControl *pQC = NULL;
IPin *pOutputPin = m_pInputPin->GetConnected();
ASSERT(pOutputPin != NULL);
// And get an AddRef'd quality control interface
HRESULT hr = pOutputPin->QueryInterface(IID_IQualityControl,(void**) &pQC);
if (SUCCEEDED(hr)) {
m_pQSink = pQC;
}
}
if (m_pQSink) {
return m_pQSink->Notify(this,q);
}
return S_FALSE;
} // SendQuality
// We are called with a valid IMediaSample image to decide whether this is to
// be drawn or not. There must be a reference clock in operation.
// Return S_OK if it is to be drawn Now (as soon as possible)
// Return S_FALSE if it is to be drawn when it's due
// Return an error if we want to drop it
// m_nNormal=-1 indicates that we dropped the previous frame and so this
// one should be drawn early. Respect it and update it.
// Use current stream time plus a number of heuristics (detailed below)
// to make the decision
HRESULT CBaseVideoRenderer::ShouldDrawSampleNow(IMediaSample *pMediaSample,
REFERENCE_TIME *ptrStart,
REFERENCE_TIME *ptrEnd)
{
REFERENCE_TIME trRealStream; // the real time now expressed as stream time.
REFERENCE_TIME trLate;
m_pClock->GetTime(&trRealStream);
trRealStream -= m_tStart; // convert to stream time (this is a reftime)
HRESULT hr = ComputeLateness (*ptrStart, trRealStream, &trLate);
if (SUCCEEDED(hr))
{
hr = SendQuality (trLate, trRealStream);
BOOL bSupplierHandlingQuality = (hr == S_OK);
hr = CVideoRendererQuality::ShouldDrawSampleNow(pMediaSample, ptrStart, ptrEnd, trRealStream, bSupplierHandlingQuality);
}
return hr;
} // ShouldDrawSampleNow
// NOTE we're called by both the window thread and the source filter thread
// so we have to be protected by a critical section (locked before called)
// Also, when the window thread gets signalled to render an image, it always
// does so regardless of how late it is. All the degradation is done when we
// are scheduling the next sample to be drawn. Hence when we start an advise
// link to draw a sample, that sample's time will always become the last one
// drawn - unless of course we stop streaming in which case we cancel links
BOOL CBaseVideoRenderer::ScheduleSample(IMediaSample *pMediaSample)
{
// We override ShouldDrawSampleNow to add quality management
BOOL bDrawImage = CBaseRenderer::ScheduleSample(pMediaSample);
if (bDrawImage == FALSE) {
CVideoRendererQuality::FrameDropped (pMediaSample);
}
return bDrawImage;
}
// Implementation of IQualProp interface needed to support the property page
// This is how the property page gets the data out of the scheduler. We are
// passed into the constructor the owning object in the COM sense, this will
// either be the video renderer or an external IUnknown if we're aggregated.
// We initialise our CUnknown base class with this interface pointer. Then
// all we have to do is to override NonDelegatingQueryInterface to expose
// our IQualProp interface. The AddRef and Release are handled automatically
// by the base class and will be passed on to the appropriate outer object
STDMETHODIMP CBaseVideoRenderer::get_FramesDroppedInRenderer(int *pcFramesDropped)
{
CAutoLock cVideoLock(&m_InterfaceLock);
return CVideoRendererQuality::get_FramesDroppedInRenderer (pcFramesDropped);
} // get_FramesDroppedInRenderer
// Set *pcFramesDrawn to the number of frames drawn since
// streaming started.
STDMETHODIMP CBaseVideoRenderer::get_FramesDrawn( int *pcFramesDrawn)
{
CAutoLock cVideoLock(&m_InterfaceLock);
return CVideoRendererQuality::get_FramesDrawn (pcFramesDrawn);
} // get_FramesDrawn
// Set iAvgFrameRate to the frames per hundred secs since
// streaming started. 0 otherwise.
STDMETHODIMP CBaseVideoRenderer::get_AvgFrameRate( int *piAvgFrameRate)
{
CAutoLock cVideoLock(&m_InterfaceLock);
return CVideoRendererQuality::get_AvgFrameRate (piAvgFrameRate, m_bStreaming);
} // get_AvgFrameRate
// Set *piAvg to the average sync offset since streaming started
// in mSec. The sync offset is the time in mSec between when the frame
// should have been drawn and when the frame was actually drawn.
STDMETHODIMP CBaseVideoRenderer::get_AvgSyncOffset( int *piAvg)
{
CAutoLock cVideoLock(&m_InterfaceLock);
return CVideoRendererQuality::get_
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -