summaryrefslogtreecommitdiffstats
path: root/thirdparties/win32/include/directshow/renbase.h
blob: 8634c6bee905153d43a6aeb3d44eb54de7feed3a (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
476
477
478
//------------------------------------------------------------------------------
// 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__

OpenPOWER on IntegriCloud