| //------------------------------------------------------------------------------ | |
| // File: RenBase.h | |
| // | |
| // Desc: DirectShow base classes - defines a generic ActiveX base renderer | |
| // class. | |
| // | |
| // Copyright (c) 1992-2001 Microsoft Corporation. All rights reserved. | |
| //------------------------------------------------------------------------------ | |
| #ifndef __RENBASE__ | |
| #define __RENBASE__ | |
| // Forward class declarations | |
| class CBaseRenderer; | |
| class CBaseVideoRenderer; | |
| class CRendererInputPin; | |
| // This is our input pin class that channels calls to the renderer | |
| class CRendererInputPin : public CBaseInputPin | |
| { | |
| protected: | |
| CBaseRenderer *m_pRenderer; | |
| public: | |
| CRendererInputPin(__inout CBaseRenderer *pRenderer, | |
| __inout HRESULT *phr, | |
| __in_opt LPCWSTR Name); | |
| // Overriden from the base pin classes | |
| HRESULT BreakConnect(); | |
| HRESULT CompleteConnect(IPin *pReceivePin); | |
| HRESULT SetMediaType(const CMediaType *pmt); | |
| HRESULT CheckMediaType(const CMediaType *pmt); | |
| HRESULT Active(); | |
| HRESULT Inactive(); | |
| // Add rendering behaviour to interface functions | |
| STDMETHODIMP QueryId(__deref_out LPWSTR *Id); | |
| STDMETHODIMP EndOfStream(); | |
| STDMETHODIMP BeginFlush(); | |
| STDMETHODIMP EndFlush(); | |
| STDMETHODIMP Receive(IMediaSample *pMediaSample); | |
| // Helper | |
| IMemAllocator inline *Allocator() const | |
| { | |
| return m_pAllocator; | |
| } | |
| }; | |
| // Main renderer class that handles synchronisation and state changes | |
| class CBaseRenderer : public CBaseFilter | |
| { | |
| protected: | |
| friend class CRendererInputPin; | |
| 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 | |
| CRendererPosPassThru *m_pPosition; // Media seeking pass by object | |
| 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. | |
| public: | |
| CBaseRenderer(REFCLSID RenderClass, // CLSID for this renderer | |
| __in_opt LPCTSTR pName, // Debug ONLY description | |
| __inout_opt LPUNKNOWN pUnk, // Aggregated owner object | |
| __inout HRESULT *phr); // General OLE return code | |
| ~CBaseRenderer(); | |
| // Overriden to say what interfaces we support and where | |
| virtual HRESULT GetMediaPositionInterface(REFIID riid, __deref_out void **ppv); | |
| STDMETHODIMP NonDelegatingQueryInterface(REFIID, __deref_out void **); | |
| virtual HRESULT SourceThreadCanWait(BOOL bCanWait); | |
| #ifdef DEBUG | |
| // Debug only dump of the renderer state | |
| void DisplayRendererState(); | |
| #endif | |
| virtual HRESULT WaitForRenderTime(); | |
| virtual HRESULT CompleteStateChange(FILTER_STATE OldState); | |
| // Return internal information about this filter | |
| BOOL IsEndOfStream() { return m_bEOS; }; | |
| BOOL IsEndOfStreamDelivered() { return m_bEOSDelivered; }; | |
| BOOL IsStreaming() { return m_bStreaming; }; | |
| void SetAbortSignal(BOOL bAbort) { m_bAbort = bAbort; }; | |
| virtual void OnReceiveFirstSample(IMediaSample *pMediaSample) { }; | |
| 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(); }; | |
| virtual int GetPinCount(); | |
| virtual CBasePin *GetPin(int n); | |
| FILTER_STATE GetRealState(); | |
| void SendRepaint(); | |
| void SendNotifyWindow(IPin *pPin,HWND hwnd); | |
| BOOL OnDisplayChange(); | |
| void SetRepaintStatus(BOOL bRepaint); | |
| // Override the filter and pin interface functions | |
| STDMETHODIMP Stop(); | |
| STDMETHODIMP Pause(); | |
| STDMETHODIMP Run(REFERENCE_TIME StartTime); | |
| STDMETHODIMP GetState(DWORD dwMSecs, __out FILTER_STATE *State); | |
| STDMETHODIMP FindPin(LPCWSTR Id, __deref_out IPin **ppPin); | |
| // These are available for a quality management implementation | |
| 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() { }; | |
| #ifdef PERF | |
| REFERENCE_TIME m_trRenderStart; // Just before we started drawing | |
| // Set in OnRenderStart, Used in OnRenderEnd | |
| int m_idBaseStamp; // MSR_id for frame time stamp | |
| int m_idBaseRenderTime; // MSR_id for true wait time | |
| int m_idBaseAccuracy; // MSR_id for time frame is late (int) | |
| #endif | |
| // Quality management implementation for scheduling rendering | |
| virtual BOOL ScheduleSample(IMediaSample *pMediaSample); | |
| virtual HRESULT GetSampleTimes(IMediaSample *pMediaSample, | |
| __out REFERENCE_TIME *pStartTime, | |
| __out REFERENCE_TIME *pEndTime); | |
| virtual HRESULT ShouldDrawSampleNow(IMediaSample *pMediaSample, | |
| __out REFERENCE_TIME *ptrStart, | |
| __out REFERENCE_TIME *ptrEnd); | |
| // Lots of end of stream complexities | |
| void TimerCallback(); | |
| void ResetEndOfStreamTimer(); | |
| HRESULT NotifyEndOfStream(); | |
| virtual HRESULT SendEndOfStream(); | |
| virtual HRESULT ResetEndOfStream(); | |
| virtual HRESULT EndOfStream(); | |
| // Rendering is based around the clock | |
| void SignalTimerFired(); | |
| virtual HRESULT CancelNotification(); | |
| virtual HRESULT ClearPendingSample(); | |
| // Called when the filter changes state | |
| virtual HRESULT Active(); | |
| virtual HRESULT Inactive(); | |
| virtual HRESULT StartStreaming(); | |
| virtual HRESULT StopStreaming(); | |
| virtual HRESULT BeginFlush(); | |
| virtual HRESULT EndFlush(); | |
| // Deal with connections and type changes | |
| virtual HRESULT BreakConnect(); | |
| virtual HRESULT SetMediaType(const CMediaType *pmt); | |
| virtual HRESULT CompleteConnect(IPin *pReceivePin); | |
| // These look after the handling of data samples | |
| virtual HRESULT PrepareReceive(IMediaSample *pMediaSample); | |
| virtual HRESULT Receive(IMediaSample *pMediaSample); | |
| virtual BOOL HaveCurrentSample(); | |
| virtual IMediaSample *GetCurrentSample(); | |
| virtual HRESULT Render(IMediaSample *pMediaSample); | |
| // Derived classes MUST override these | |
| virtual HRESULT DoRenderSample(IMediaSample *pMediaSample) PURE; | |
| virtual HRESULT CheckMediaType(const CMediaType *) PURE; | |
| // Helper | |
| void WaitForReceiveToComplete(); | |
| }; | |
| // CBaseVideoRenderer is a renderer class (see its ancestor class) and | |
| // it handles scheduling of media samples so that they are drawn at the | |
| // correct time by the reference clock. It implements a degradation | |
| // strategy. Possible degradation modes are: | |
| // Drop frames here (only useful if the drawing takes significant time) | |
| // Signal supplier (upstream) to drop some frame(s) - i.e. one-off skip. | |
| // Signal supplier to change the frame rate - i.e. ongoing skipping. | |
| // Or any combination of the above. | |
| // In order to determine what's useful to try we need to know what's going | |
| // on. This is done by timing various operations (including the supplier). | |
| // This timing is done by using timeGetTime as it is accurate enough and | |
| // usually cheaper than calling the reference clock. It also tells the | |
| // truth if there is an audio break and the reference clock stops. | |
| // We provide a number of public entry points (named OnXxxStart, OnXxxEnd) | |
| // which the rest of the renderer calls at significant moments. These do | |
| // the timing. | |
| // the number of frames that the sliding averages are averaged over. | |
| // the rule is (1024*NewObservation + (AVGPERIOD-1) * PreviousAverage)/AVGPERIOD | |
| #define AVGPERIOD 4 | |
| #define DO_MOVING_AVG(avg,obs) (avg = (1024*obs + (AVGPERIOD-1)*avg)/AVGPERIOD) | |
| // Spot the bug in this macro - I can't. but it doesn't work! | |
| class CBaseVideoRenderer : public CBaseRenderer, // Base renderer class | |
| public IQualProp, // Property page guff | |
| public IQualityControl // Allow throttling | |
| { | |
| protected: | |
| // Hungarian: | |
| // tFoo is the time Foo in mSec (beware m_tStart from filter.h) | |
| // trBar is the time Bar by the reference clock | |
| //****************************************************************** | |
| // State variables to control synchronisation | |
| //****************************************************************** | |
| // Control of sending Quality messages. We need to know whether | |
| // we are in trouble (e.g. frames being dropped) and where the time | |
| // is being spent. | |
| // When we drop a frame we play the next one early. | |
| // The frame after that is likely to wait before drawing and counting this | |
| // wait as spare time is unfair, so we count it as a zero wait. | |
| // We therefore need to know whether we are playing frames early or not. | |
| int m_nNormal; // The number of consecutive frames | |
| // drawn at their normal time (not early) | |
| // -1 means we just dropped a frame. | |
| #ifdef PERF | |
| BOOL m_bDrawLateFrames; // Don't drop any frames (debug and I'm | |
| // not keen on people using it!) | |
| #endif | |
| BOOL m_bSupplierHandlingQuality;// The response to Quality messages says | |
| // our supplier is handling things. | |
| // We will allow things to go extra late | |
| // before dropping frames. We will play | |
| // very early after he has dropped one. | |
| // Control of scheduling, frame dropping etc. | |
| // We need to know where the time is being spent so as to tell whether | |
| // we should be taking action here, signalling supplier or what. | |
| // The variables are initialised to a mode of NOT dropping frames. | |
| // They will tell the truth after a few frames. | |
| // We typically record a start time for an event, later we get the time | |
| // again and subtract to get the elapsed time, and we average this over | |
| // a few frames. The average is used to tell what mode we are in. | |
| // Although these are reference times (64 bit) they are all DIFFERENCES | |
| // between times which are small. An int will go up to 214 secs before | |
| // overflow. Avoiding 64 bit multiplications and divisions seems | |
| // worth while. | |
| // Audio-video throttling. If the user has turned up audio quality | |
| // very high (in principle it could be any other stream, not just audio) | |
| // then we can receive cries for help via the graph manager. In this case | |
| // we put in a wait for some time after rendering each frame. | |
| int m_trThrottle; | |
| // The time taken to render (i.e. BitBlt) frames controls which component | |
| // needs to degrade. If the blt is expensive, the renderer degrades. | |
| // If the blt is cheap it's done anyway and the supplier degrades. | |
| int m_trRenderAvg; // Time frames are taking to blt | |
| int m_trRenderLast; // Time for last frame blt | |
| int m_tRenderStart; // Just before we started drawing (mSec) | |
| // derived from timeGetTime. | |
| // When frames are dropped we will play the next frame as early as we can. | |
| // If it was a false alarm and the machine is fast we slide gently back to | |
| // normal timing. To do this, we record the offset showing just how early | |
| // we really are. This will normally be negative meaning early or zero. | |
| int m_trEarliness; | |
| // Target provides slow long-term feedback to try to reduce the | |
| // average sync offset to zero. Whenever a frame is actually rendered | |
| // early we add a msec or two, whenever late we take off a few. | |
| // We add or take off 1/32 of the error time. | |
| // Eventually we should be hovering around zero. For a really bad case | |
| // where we were (say) 300mSec off, it might take 100 odd frames to | |
| // settle down. The rate of change of this is intended to be slower | |
| // than any other mechanism in Quartz, thereby avoiding hunting. | |
| int m_trTarget; | |
| // The proportion of time spent waiting for the right moment to blt | |
| // controls whether we bother to drop a frame or whether we reckon that | |
| // we're doing well enough that we can stand a one-frame glitch. | |
| int m_trWaitAvg; // Average of last few wait times | |
| // (actually we just average how early | |
| // we were). Negative here means LATE. | |
| // The average inter-frame time. | |
| // This is used to calculate the proportion of the time used by the | |
| // three operations (supplying us, waiting, rendering) | |
| int m_trFrameAvg; // Average inter-frame time | |
| int m_trDuration; // duration of last frame. | |
| #ifdef PERF | |
| // Performance logging identifiers | |
| int m_idTimeStamp; // MSR_id for frame time stamp | |
| int m_idEarliness; // MSR_id for earliness fudge | |
| int m_idTarget; // MSR_id for Target fudge | |
| int m_idWaitReal; // MSR_id for true wait time | |
| int m_idWait; // MSR_id for wait time recorded | |
| int m_idFrameAccuracy; // MSR_id for time frame is late (int) | |
| int m_idRenderAvg; // MSR_id for Render time recorded (int) | |
| int m_idSchLateTime; // MSR_id for lateness at scheduler | |
| int m_idQualityRate; // MSR_id for Quality rate requested | |
| int m_idQualityTime; // MSR_id for Quality time requested | |
| int m_idDecision; // MSR_id for decision code | |
| int m_idDuration; // MSR_id for duration of a frame | |
| int m_idThrottle; // MSR_id for audio-video throttling | |
| //int m_idDebug; // MSR_id for trace style debugging | |
| //int m_idSendQuality; // MSR_id for timing the notifications per se | |
| #endif // PERF | |
| REFERENCE_TIME m_trRememberStampForPerf; // original time stamp of frame | |
| // with no earliness fudges etc. | |
| #ifdef PERF | |
| REFERENCE_TIME m_trRememberFrameForPerf; // time when previous frame rendered | |
| // debug... | |
| int m_idFrameAvg; | |
| int m_idWaitAvg; | |
| #endif | |
| // PROPERTY PAGE | |
| // This has edit fields that show the user what's happening | |
| // These member variables hold these counts. | |
| int m_cFramesDropped; // cumulative frames dropped IN THE RENDERER | |
| int m_cFramesDrawn; // Frames since streaming started seen BY THE | |
| // RENDERER (some may be dropped upstream) | |
| // Next two support average sync offset and standard deviation of sync offset. | |
| LONGLONG m_iTotAcc; // Sum of accuracies in mSec | |
| LONGLONG m_iSumSqAcc; // Sum of squares of (accuracies in mSec) | |
| // Next two allow jitter calculation. Jitter is std deviation of frame time. | |
| REFERENCE_TIME m_trLastDraw; // Time of prev frame (for inter-frame times) | |
| LONGLONG m_iSumSqFrameTime; // Sum of squares of (inter-frame time in mSec) | |
| LONGLONG m_iSumFrameTime; // Sum of inter-frame times in mSec | |
| // To get performance statistics on frame rate, jitter etc, we need | |
| // to record the lateness and inter-frame time. What we actually need are the | |
| // data above (sum, sum of squares and number of entries for each) but the data | |
| // is generated just ahead of time and only later do we discover whether the | |
| // frame was actually drawn or not. So we have to hang on to the data | |
| int m_trLate; // hold onto frame lateness | |
| int m_trFrame; // hold onto inter-frame time | |
| int m_tStreamingStart; // if streaming then time streaming started | |
| // else time of last streaming session | |
| // used for property page statistics | |
| #ifdef PERF | |
| LONGLONG m_llTimeOffset; // timeGetTime()*10000+m_llTimeOffset==ref time | |
| #endif | |
| public: | |
| CBaseVideoRenderer(REFCLSID RenderClass, // CLSID for this renderer | |
| __in_opt LPCTSTR pName, // Debug ONLY description | |
| __inout_opt LPUNKNOWN pUnk, // Aggregated owner object | |
| __inout HRESULT *phr); // General OLE return code | |
| ~CBaseVideoRenderer(); | |
| // IQualityControl methods - Notify allows audio-video throttling | |
| STDMETHODIMP SetSink( IQualityControl * piqc); | |
| STDMETHODIMP Notify( IBaseFilter * pSelf, Quality q); | |
| // These provide a full video quality management implementation | |
| void OnRenderStart(IMediaSample *pMediaSample); | |
| void OnRenderEnd(IMediaSample *pMediaSample); | |
| void OnWaitStart(); | |
| void OnWaitEnd(); | |
| HRESULT OnStartStreaming(); | |
| HRESULT OnStopStreaming(); | |
| void ThrottleWait(); | |
| // Handle the statistics gathering for our quality management | |
| void PreparePerformanceData(int trLate, int trFrame); | |
| virtual void RecordFrameLateness(int trLate, int trFrame); | |
| virtual void OnDirectRender(IMediaSample *pMediaSample); | |
| virtual HRESULT ResetStreamingTimes(); | |
| BOOL ScheduleSample(IMediaSample *pMediaSample); | |
| HRESULT ShouldDrawSampleNow(IMediaSample *pMediaSample, | |
| __inout REFERENCE_TIME *ptrStart, | |
| __inout REFERENCE_TIME *ptrEnd); | |
| virtual HRESULT SendQuality(REFERENCE_TIME trLate, REFERENCE_TIME trRealStream); | |
| STDMETHODIMP JoinFilterGraph(__inout_opt IFilterGraph * pGraph, __in_opt LPCWSTR pName); | |
| // | |
| // Do estimates for standard deviations for per-frame | |
| // statistics | |
| // | |
| // *piResult = (llSumSq - iTot * iTot / m_cFramesDrawn - 1) / | |
| // (m_cFramesDrawn - 2) | |
| // or 0 if m_cFramesDrawn <= 3 | |
| // | |
| HRESULT GetStdDev( | |
| int nSamples, | |
| __out int *piResult, | |
| LONGLONG llSumSq, | |
| LONGLONG iTot | |
| ); | |
| public: | |
| // IQualProp property page support | |
| STDMETHODIMP get_FramesDroppedInRenderer(__out int *cFramesDropped); | |
| STDMETHODIMP get_FramesDrawn(__out int *pcFramesDrawn); | |
| STDMETHODIMP get_AvgFrameRate(__out int *piAvgFrameRate); | |
| STDMETHODIMP get_Jitter(__out int *piJitter); | |
| STDMETHODIMP get_AvgSyncOffset(__out int *piAvg); | |
| STDMETHODIMP get_DevSyncOffset(__out int *piDev); | |
| // Implement an IUnknown interface and expose IQualProp | |
| DECLARE_IUNKNOWN | |
| STDMETHODIMP NonDelegatingQueryInterface(REFIID riid,__deref_out VOID **ppv); | |
| }; | |
| #endif // __RENBASE__ | |