DirectShow学习(八): CBaseRender类及相应Pin类的源代码分析

时间:2022-06-15 00:03:10

DirectShow学习(八): CBaseRender类及相应Pin类的源代码分析
1.      
CRendererInputPin[renbase.h/renbase.cpp]
派生自CBaseInputPin<?xml:namespace prefix = o ns = "urn:schemas-microsoft-com:office:office" />

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)析构函数中调用StopStreamingClearPendingSample;并删除m_pPositionm_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();
}

 
前一篇: (七) CTransInPlaceFilter及相关联Pin类的源代码解析