mirror of
https://github.com/libretro/pcsx2.git
synced 2024-11-30 21:00:42 +00:00
ab9bdb009b
Update it to the version found at https://github.com/Microsoft/Windows-classic-samples , which is in an MIT licensed repo, and add the LICENSE file (edited to remove the SIL Open Font LICENSE part since that doesn't apply). Some modifications have been made to reduce the diff/stop git complaining (not including any file that wasn't in the previous version and removing the related header includes in streams.h, and fixing some but not all of the whitespace issues).
479 lines
20 KiB
C++
479 lines
20 KiB
C++
//------------------------------------------------------------------------------
|
|
// 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__
|
|
|