DirectShow学习(八): CBaseRender类及相应Pin类的源代码分析
1. CRendererInputPin类[renbase.h/renbase.cpp]
派生自CBaseInputPin。
a) 成员变量:
CBaseRenderer *m_pRenderer;
b) IPin接口和继承的函数
HRESULT
BreakConnect();
{
HRESULT hr = m_pRenderer->
BreakConnect();
return CBaseInputPin::
BreakConnect();
}
HRESULT CompleteConnect(IPin *pReceivePin);
{
HRESULT hr = m_pRenderer->CompleteConnect(pReceivePin);
return CBaseInputPin::CompleteConnect(pReceivePin);
}
HRESULT SetMediaType(const CMediaType *pmt);
{
HRESULT hr = CBaseInputPin::SetMediaType(pmt);
return m_pRenderer->SetMediaType(pmt);
}
HRESULT CheckMediaType(const CMediaType *pmt);
{ return m_pRenderer->CheckMediaType(pmt); }
HRESULT Active();{ return m_pRenderer->Active();}
HRESULT Inactive();
{
ASSERT(CritCheckIn(&m_pRenderer->m_InterfaceLock));
m_bRunTimeError = FALSE;
return m_pRenderer->Inactive();
}
STDMETHODIMP QueryId(LPWSTR *Id);
{
*Id = (LPWSTR)CoTaskMemAlloc(8);
lstrcpyW(*Id, L"In");
}
STDMETHODIMP EndOfStream();
{
CAutoLock cRendererLock(&m_pRenderer->m_InterfaceLock);
CAutoLock cSampleLock(&m_pRenderer->m_RendererLock);
HRESULT hr = CheckStreaming();
hr = m_pRenderer->EndOfStream();
hr = CBaseInputPin::EndOfStream();
}
STDMETHODIMP BeginFlush();
{
HRESULT hr = m_pRenderer->EndFlush();
hr = CBaseInputPin::EndFlush();
}
STDMETHODIMP EndFlush();
{
CAutoLock cRendererLock(&m_pRenderer->m_InterfaceLock);
CAutoLock cSampleLock(&m_pRenderer->m_RendererLock);
CBaseInputPin::BeginFlush();
m_pRenderer->BeginFlush();
return m_pRenderer->ResetEndOfStream();
}
STDMETHODIMP Receive(IMediaSample *pMediaSample);
{
HRESULT hr = m_pRenderer->Receive(pSample);
if (FAILED(hr)) {
ASSERT(CritCheckOut(&m_pRenderer->m_RendererLock));
CAutoLock cRendererLock(&m_pRenderer->m_InterfaceLock);
if (!IsStopped() && !IsFlushing() && !m_pRenderer->m_bAbort && !m_bRunTimeError)
{
m_pRenderer->NotifyEvent(EC_ERRORABORT,hr,0);
CAutoLock alRendererLock(&m_pRenderer->m_RendererLock);
if (m_pRenderer->IsStreaming() && !m_pRenderer->IsEndOfStreamDelivered())
{ m_pRenderer->NotifyEndOfStream();}
m_bRunTimeError = TRUE;
}
}
}
2. CBaseRender类[renbase.h/renbase.cpp]
派生自CBaseFilter。
a) 成员变量
CRendererPosPassThru *m_pPosition; // Media seeking pass by object
类CRendererPosPassThru派生自CPosPassThru,后者继承自接口IMediaSeeking和类CMediaPosition。而类CMediaPosition实现了接口IMediaPosition。具体实现见ctlutil.h
CAMEvent m_RenderEvent; // Used to signal timer events
CAMEvent m_ThreadSignal; // Signalled to release worker thread
CAMEvent m_evComplete; // Signalled when state complete
BOOL m_bAbort; // Stop us from rendering more data
BOOL m_bStreaming; // Are we currently streaming
DWORD_PTR m_dwAdvise; // Timer advise cookie
IMediaSample *m_pMediaSample; // Current image media sample
BOOL m_bEOS; // Any more samples in the stream
BOOL m_bEOSDelivered; // Have we delivered an EC_COMPLETE
CRendererInputPin *m_pInputPin; // Our renderer input pin object
CCritSec m_InterfaceLock; // Critical section for interfaces
CCritSec m_RendererLock; // Controls access to internals
IQualityControl * m_pQSink; // QualityControl sink
BOOL m_bRepaintStatus; // Can we signal an EC_REPAINT
// Avoid some deadlocks by tracking filter during stop
volatile BOOL m_bInReceive; // Inside Receive between PrepareReceive
// And actually processing the sample
REFERENCE_TIME m_SignalTime; // Time when we signal EC_COMPLETE
UINT m_EndOfStreamTimer; // Used to signal end of stream
CCritSec m_ObjectCreationLock; // This lock protects the creation and
// of m_pPosition and m_pInputPin. It ensures that two threads cannot create
// either object simultaneously.
构造函数中,对各成员初始化,m_evComplete(TRUE), m_bAbort(FALSE), m_pPosition(NULL), m_ThreadSignal(TRUE), m_bStreaming(FALSE), m_bEOS(FALSE), m_bEOSDelivered(FALSE), m_pMediaSample(NULL), m_dwAdvise(0), m_pQSink(NULL), m_pInputPin(NULL), m_bRepaintStatus(TRUE), m_SignalTime(0), m_bInReceive(FALSE), m_EndOfStreamTimer(0);析构函数中调用StopStreaming和ClearPendingSample;并删除m_pPosition和m_pInputPin。
b) 新增加的virtual函数
// Overriden to say what interfaces we support and where
virtual HRESULT GetMediaPositionInterface(REFIID riid,void **ppv);
{
CAutoLock cObjectCreationLock(&m_ObjectCreationLock);
if (m_pPosition) {
return m_pPosition->NonDelegatingQueryInterface(riid,ppv);
}
m_pPosition = new CRendererPosPassThru(NAME("Renderer CPosPassThru"),
CBaseFilter::GetOwner(),(HRESULT *) &hr, GetPin(0));
return GetMediaPositionInterface(riid,ppv);
}
virtual HRESULT SourceThreadCanWait(BOOL bCanWait);
{
if (bCanWait == TRUE) m_ThreadSignal.Reset();
else m_ThreadSignal.Set();
}
virtual HRESULT WaitForRenderTime();
{
HANDLE WaitObjects[] = { m_ThreadSignal, m_RenderEvent };
OnWaitStart();
while (Result == WAIT_TIMEOUT) {
Result = WaitForMultipleObjects(2,WaitObjects,FALSE,RENDER_TIMEOUT);}
OnWaitEnd();
if (Result == WAIT_OBJECT_0) {return VFW_E_STATE_CHANGED;}
SignalTimerFired();
}
virtual HRESULT CompleteStateChange(FILTER_STATE OldState);
{
if (m_pInputPin->IsConnected() == FALSE) {Ready();return S_OK;}
if (IsEndOfStream() == TRUE) {Ready();return S_OK;}
if (HaveCurrentSample() == TRUE) {
if (OldState != State_Stopped) { Ready();return S_OK;}
}
NotReady();
}
virtual void OnReceiveFirstSample(IMediaSample *pMediaSample) {};
virtual void OnRenderStart(IMediaSample *pMediaSample){}
virtual void OnRenderEnd(IMediaSample *pMediaSample){}
virtual HRESULT OnStartStreaming() { return NOERROR; };
virtual HRESULT OnStopStreaming() { return NOERROR; };
virtual void OnWaitStart() {};
virtual void OnWaitEnd() {};
virtual void PrepareRender() {};
// Quality management implementation for scheduling rendering
virtual BOOL ScheduleSample(IMediaSample *pMediaSample);
{
REFERENCE_TIME StartSample, EndSample;
if (pMediaSample == NULL) {return FALSE;}
HRESULT hr = GetSampleTimes(pMediaSample, &StartSample, &EndSample);
if (FAILED(hr)) {return FALSE;}
if (hr == S_OK) {EXECUTE_ASSERT(SetEvent((HANDLE) m_RenderEvent));
return TRUE;}
ASSERT(WAIT_TIMEOUT == WaitForSingleObject((HANDLE)m_RenderEvent,0));
hr = m_pClock->AdviseTime(
(REFERENCE_TIME) m_tStart, // Start run time
StartSample, // Stream time
(HEVENT)(HANDLE) m_RenderEvent, // Render notification
&m_dwAdvise); // Advise cookie
if (SUCCEEDED(hr)) {return TRUE;}
return FALSE;
}
virtual HRESULT GetSampleTimes(IMediaSample *pMediaSample,
REFERENCE_TIME *pStartTime,
REFERENCE_TIME *pEndTime);
{
if (SUCCEEDED(pMediaSample->GetTime(pStartTime, pEndTime))) {
if (*pEndTime < *pStartTime) {return VFW_E_START_TIME_AFTER_END;}
} else {return S_OK;}
if (m_pClock == NULL) {return S_OK;}
return ShouldDrawSampleNow(pMediaSample,pStartTime,pEndTime);
}
virtual HRESULT ShouldDrawSampleNow(IMediaSample *pMediaSample,
REFERENCE_TIME *ptrStart,
REFERENCE_TIME *ptrEnd);{ return S_FALSE;}
virtual HRESULT SendEndOfStream();
{
ASSERT(CritCheckIn(&m_RendererLock));
if (m_bEOS == FALSE || m_bEOSDelivered || m_EndOfStreamTimer) {
return NOERROR;}
if (m_pClock == NULL) {return NotifyEndOfStream();}
REFERENCE_TIME Signal = m_tStart + m_SignalTime;
REFERENCE_TIME CurrentTime;
m_pClock->GetTime(&CurrentTime);
LONG Delay = LONG((Signal - CurrentTime) / 10000);
if (Delay < TIMEOUT_DELIVERYWAIT) {return NotifyEndOfStream();}
m_EndOfStreamTimer = CompatibleTimeSetEvent((UINT) Delay, // Period of timer
TIMEOUT_RESOLUTION, // Timer resolution
EndOfStreamTimer, // Callback function
DWORD_PTR(this), // Used information
TIME_ONESHOT); // Type of callback
if (m_EndOfStreamTimer == 0) {return NotifyEndOfStream();}
}
virtual HRESULT ResetEndOfStream();
{
ResetEndOfStreamTimer();
CAutoLock cRendererLock(&m_RendererLock);
m_bEOS = FALSE;
m_bEOSDelivered = FALSE;
m_SignalTime = 0;
}
virtual HRESULT EndOfStream();
{
m_bEOS = TRUE;
Ready();
if (m_pMediaSample) { return NOERROR;}
if (m_bStreaming) { SendEndOfStream();}
}
virtual HRESULT CancelNotification();
{
ASSERT(m_dwAdvise == 0 || m_pClock);
DWORD_PTR dwAdvise = m_dwAdvise;
if (m_dwAdvise) {
m_pClock->Unadvise(m_dwAdvise);
SignalTimerFired();}
m_RenderEvent.Reset();
return (dwAdvise ? S_OK : S_FALSE);
}
virtual HRESULT ClearPendingSample();
{
CAutoLock cRendererLock(&m_RendererLock);
if (m_pMediaSample) {
m_pMediaSample->Release();
m_pMediaSample = NULL;}
}
virtual HRESULT PrepareReceive(IMediaSample *pMediaSample);
{
CAutoLock cInterfaceLock(&m_InterfaceLock);
m_bInReceive = TRUE;
HRESULT hr = m_pInputPin->CBaseInputPin::Receive(pMediaSample);
if (hr != NOERROR) {m_bInReceive = FALSE; return E_FAIL;}
if (m_pInputPin->SampleProps()->pMediaType) {
hr = m_pInputPin->SetMediaType(
(CMediaType *)m_pInputPin->SampleProps()->pMediaType);
if (FAILED(hr)) {
m_bInReceive = FALSE;
return hr;
}
}
CAutoLock cSampleLock(&m_RendererLock);
if (m_pMediaSample || m_bEOS || m_bAbort) {
Ready();m_bInReceive = FALSE; return E_UNEXPECTED;}
if (m_pPosition) m_pPosition->RegisterMediaTime(pMediaSample);
if ((m_bStreaming == TRUE) && (ScheduleSample(pMediaSample) == FALSE)) {
ASSERT(WAIT_TIMEOUT == WaitForSingleObject((HANDLE)m_RenderEvent,0));
ASSERT(CancelNotification() == S_FALSE);
m_bInReceive = FALSE;
return VFW_E_SAMPLE_REJECTED;
}
m_SignalTime = m_pInputPin->SampleProps()->tStop;
m_pMediaSample = pMediaSample;
m_pMediaSample->AddRef();
if (m_bStreaming == FALSE) {SetRepaintStatus(TRUE);}
}
virtual BOOL HaveCurrentSample();
{
// Checks if there is a sample waiting at the renderer
CAutoLock cRendererLock(&m_RendererLock);
return (m_pMediaSample == NULL ? FALSE : TRUE);
}
virtual IMediaSample *GetCurrentSample();
{
CAutoLock cRendererLock(&m_RendererLock);
if (m_pMediaSample) {m_pMediaSample->AddRef();}
return m_pMediaSample;
}
virtual HRESULT Render(IMediaSample *pMediaSample);
{
if (pMediaSample == NULL) {return S_FALSE;}
if (m_bStreaming == FALSE) {return S_FALSE;}
OnRenderStart(pMediaSample);
DoRenderSample(pMediaSample);
OnRenderEnd(pMediaSample);
}
virtual HRESULT DoRenderSample(IMediaSample *pMediaSample) PURE;
c) IBaseFilter接口函数以及继承函数
virtual int GetPinCount();{1}
virtual CBasePin *GetPin(int n);
{
CAutoLock cObjectCreationLock(&m_ObjectCreationLock);
if (m_pInputPin == NULL) {
m_pInputPin = new CRendererInputPin(this,&hr,L"In");}
return m_pInputPin;
}
STDMETHODIMP Stop();
{
CAutoLock cRendererLock(&m_InterfaceLock);
if (m_State == State_Stopped) { return NOERROR;}
if (m_pInputPin->IsConnected() == FALSE) {
m_State = State_Stopped;
return NOERROR;}
CBaseFilter::Stop();
if (m_pInputPin->Allocator()) { m_pInputPin->Allocator()->Decommit();}
SetRepaintStatus(TRUE);
StopStreaming();
SourceThreadCanWait(FALSE);
ResetEndOfStream();
CancelNotification();
Ready();
WaitForReceiveToComplete();
m_bAbort = FALSE;
}
STDMETHODIMP Pause();
{
CAutoLock cRendererLock(&m_InterfaceLock);
FILTER_STATE OldState = m_State;
if (m_State == State_Paused) { return CompleteStateChange(State_Paused);}
if (m_pInputPin->IsConnected() == FALSE) {
m_State = State_Paused;
return CompleteStateChange(State_Paused);
}
HRESULT hr = CBaseFilter::Pause();
SetRepaintStatus(TRUE);
StopStreaming();
SourceThreadCanWait(TRUE);
CancelNotification();
ResetEndOfStreamTimer();
if (m_pInputPin->Allocator()) { m_pInputPin->Allocator()->Commit();}
if (OldState == State_Stopped) {
m_bAbort = FALSE;
ClearPendingSample();}
return CompleteStateChange(OldState);
}
STDMETHODIMP Run(REFERENCE_TIME StartTime);
{
CAutoLock cRendererLock(&m_InterfaceLock);
FILTER_STATE OldState = m_State;
if (m_pInputPin->IsConnected() == FALSE) {
NotifyEvent(EC_COMPLETE,S_OK,(LONG_PTR)(IBaseFilter *)this);
m_State = State_Running;
return NOERROR;}
Ready();
HRESULT hr = CBaseFilter::Run(StartTime);
SourceThreadCanWait(TRUE);
SetRepaintStatus(FALSE);
if (m_pInputPin->Allocator()) { m_pInputPin->Allocator()->Commit();}
if (OldState == State_Stopped) {
m_bAbort = FALSE;
ClearPendingSample();}
return StartStreaming ();
}
STDMETHODIMP GetState(DWORD dwMSecs,FILTER_STATE *State);
{
CheckPointer(State,E_POINTER);
if (WaitDispatchingMessages(m_evComplete, dwMSecs) == WAIT_TIMEOUT) {
*State = m_State;
return VFW_S_STATE_INTERMEDIATE;}
*State = m_State;
}
STDMETHODIMP FindPin(LPCWSTR Id, IPin **ppPin);
{
if (0==lstrcmpW(Id,L"In")) { *ppPin = GetPin(0); (*ppPin)->AddRef();}
}
virtual HRESULT Active();{return NOERROR;}
virtual HRESULT Inactive();
{
if (m_pPosition) { m_pPosition->ResetMediaTime();}
ClearPendingSample();
}
virtual HRESULT StartStreaming();
{
CAutoLock cRendererLock(&m_RendererLock);
if (m_bStreaming == TRUE) {return NOERROR;}
m_bStreaming = TRUE;
timeBeginPeriod(1);
OnStartStreaming();
ASSERT(WAIT_TIMEOUT == WaitForSingleObject((HANDLE)m_RenderEvent,0));
ASSERT(CancelNotification() == S_FALSE);
if (m_pMediaSample == NULL) {return SendEndOfStream();}
if (!ScheduleSample(m_pMediaSample)) m_RenderEvent.Set();
}
virtual HRESULT StopStreaming();
{
CAutoLock cRendererLock(&m_RendererLock);
m_bEOSDelivered = FALSE;
if (m_bStreaming == TRUE) {
m_bStreaming = FALSE;
OnStopStreaming();
timeEndPeriod(1);}
}
virtual HRESULT BeginFlush();
{
if (m_State == State_Paused) { NotReady();}
SourceThreadCanWait(FALSE);
CancelNotification();
ClearPendingSample();
WaitForReceiveToComplete();
}
virtual HRESULT EndFlush();
{
if (m_pPosition) m_pPosition->ResetMediaTime();
SourceThreadCanWait(TRUE);
}
virtual HRESULT
BreakConnect();
{
if (m_pQSink) {m_pQSink->Release();m_pQSink = NULL;}
if (m_pInputPin->IsConnected() == FALSE) {return S_FALSE;}
if (m_State != State_Stopped && !m_pInputPin->CanReconnectWhenActive()) {
return VFW_E_NOT_STOPPED;}
SetRepaintStatus(FALSE);
ResetEndOfStream();
ClearPendingSample();
m_bAbort = FALSE;
if (State_Running == m_State) {StopStreaming();}
}
virtual HRESULT SetMediaType(const CMediaType *pmt);{ return NOERROR;}
virtual HRESULT CompleteConnect(IPin *pReceivePin);
{
ASSERT(CritCheckIn(&m_InterfaceLock));
m_bAbort = FALSE;
if (State_Running == GetRealState()) {
HRESULT hr = StartStreaming();
SetRepaintStatus(FALSE);
} else { SetRepaintStatus(TRUE);}
}
virtual HRESULT Receive(IMediaSample *pMediaSample);
{
HRESULT hr = PrepareReceive(pSample);
if (m_State == State_Paused) {
PrepareRender();
m_bInReceive = FALSE;
{
// We must hold both these locks
CAutoLock cRendererLock(&m_InterfaceLock);
if (m_State == State_Stopped) return NOERROR;
m_bInReceive = TRUE;
CAutoLock cSampleLock(&m_RendererLock);
OnReceiveFirstSample(pSample);
}
Ready();
}
hr = WaitForRenderTime();
if (FAILED(hr)) {m_bInReceive = FALSE; return NOERROR;}
PrepareRender();
m_bInReceive = FALSE;
CAutoLock cRendererLock(&m_InterfaceLock);
if (m_State == State_Stopped) return NOERROR;
CAutoLock cSampleLock(&m_RendererLock);
Render(m_pMediaSample);
ClearPendingSample();
SendEndOfStream();
CancelNotification();
}
virtual HRESULT CheckMediaType(const CMediaType *) PURE;
d) 其他函数
BOOL IsEndOfStream() { return m_bEOS; };
BOOL IsEndOfStreamDelivered() { return m_bEOSDelivered; };
BOOL IsStreaming() { return m_bStreaming; };
void SetAbortSignal(BOOL bAbort) { m_bAbort = bAbort; };
CAMEvent *GetRenderEvent() { return &m_RenderEvent; };
// Permit access to the transition state
void Ready() { m_evComplete.Set(); };
void NotReady() { m_evComplete.Reset(); };
BOOL CheckReady() { return m_evComplete.Check(); };
FILTER_STATE GetRealState(){ return m_State; };
void SendRepaint();
{
CAutoLock cSampleLock(&m_RendererLock);
if (m_bAbort == FALSE) {
if (m_pInputPin->IsConnected() == TRUE) {
if (m_pInputPin->IsFlushing() == FALSE) {
if (IsEndOfStream() == FALSE) {
if (m_bRepaintStatus == TRUE) {
IPin *pPin = (IPin *) m_pInputPin;
NotifyEvent(EC_REPAINT,(LONG_PTR) pPin,0);
SetRepaintStatus(FALSE);
}}}}}}
void SendNotifyWindow(IPin *pPin,HWND hwnd);
{
IMediaEventSink *pSink;
HRESULT hr = pPin->QueryInterface(IID_IMediaEventSink,(void **)&pSink);
if (SUCCEEDED(hr)) {
pSink->Notify(EC_NOTIFY_WINDOW,LONG_PTR(hwnd),0);
pSink->Release();}
NotifyEvent(EC_NOTIFY_WINDOW,LONG_PTR(hwnd),0);
}
BOOL OnDisplayChange();
{
CAutoLock cSampleLock(&m_RendererLock);
if (m_pInputPin->IsConnected() == FALSE) {return FALSE;}
IPin *pPin = (IPin *) m_pInputPin;
m_pInputPin->AddRef();
NotifyEvent(EC_DISPLAY_CHANGED,(LONG_PTR) pPin,0);
SetAbortSignal(TRUE);
ClearPendingSample();
m_pInputPin->Release();
}
void SetRepaintStatus(BOOL
bRepaint);
{
CAutoLock cSampleLock(&m_RendererLock);
m_bRepaintStatus =
bRepaint;
}
void TimerCallback();
{
CAutoLock cRendererLock(&m_RendererLock);
if (m_EndOfStreamTimer) {
m_EndOfStreamTimer = 0;
SendEndOfStream();}
}
void ResetEndOfStreamTimer();
{
ASSERT(CritCheckOut(&m_RendererLock));
if (m_EndOfStreamTimer) {
timeKillEvent(m_EndOfStreamTimer);
m_EndOfStreamTimer = 0;}
}
HRESULT NotifyEndOfStream();
{
CAutoLock cRendererLock(&m_RendererLock);
if (m_bStreaming == FALSE) {return NOERROR;}
m_EndOfStreamTimer = 0;
if (m_pPosition) m_pPosition->EOS();
m_bEOSDelivered = TRUE;
return NotifyEvent(EC_COMPLETE,S_OK,(LONG_PTR)(IBaseFilter *)this);
}
void SignalTimerFired();{m_dwAdvise = 0;}
void WaitForReceiveToComplete();
{
for (;;) {
if (!m_bInReceive) {
break;}
MSG msg;
// Receive all interthread sendmessages
PeekMessage(&msg, NULL, WM_NULL, WM_NULL, PM_NOREMOVE);
Sleep(1);
}
if (HIWORD(GetQueueStatus(QS_POSTMESSAGE)) & QS_POSTMESSAGE) {
PostThreadMessage(GetCurrentThreadId(), WM_NULL, 0, 0);
}
}
STDMETHODIMP NonDelegatingQueryInterface(REFIID riid,void **ppv)
{
if (riid == IID_IMediaPosition || riid == IID_IMediaSeeking) {
return GetMediaPositionInterface(riid,ppv);
} else { return CBaseFilter::NonDelegatingQueryInterface(riid,ppv); }
}
e) 友元函数
friend void CALLBACK EndOfStreamTimer(UINT uID, // Timer identifier
UINT uMsg, // Not currently used
DWORD_PTR dwUser, // User information
DWORD_PTR dw1, // Windows reserved
DWORD_PTR dw2); // Is also reserved
{
CBaseRenderer *pRenderer = (CBaseRenderer *) dwUser;
pRenderer->TimerCallback();
}