Welcome to mirror list, hosted at ThFree Co, Russian Federation.

SyncRenderer.h « VideoRenderers « renderer « filters « src - github.com/mpc-hc/mpc-hc.git - Unnamed repository; edit this file 'description' to name the repository.
summaryrefslogtreecommitdiff
blob: 44778e1772c2b1fd8062122f2feeb93a3e72a6ee (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
479
480
481
482
483
484
485
486
487
488
489
490
491
492
493
494
495
496
497
498
499
500
501
502
503
504
505
506
507
508
509
510
511
512
513
514
515
516
517
518
519
520
521
522
523
524
525
526
527
528
529
530
531
532
533
534
535
536
537
538
539
540
541
542
543
544
545
546
547
548
549
550
551
552
553
554
555
556
557
558
559
560
561
562
563
564
565
566
567
568
569
570
571
572
573
574
575
576
577
578
579
580
581
582
583
584
585
586
587
588
589
590
591
592
593
594
595
596
597
598
599
600
601
602
603
604
605
606
607
608
609
610
611
612
613
614
615
616
617
618
619
620
621
622
623
624
625
626
627
628
629
630
631
632
633
634
635
636
637
638
639
640
641
642
643
644
645
646
647
648
649
650
651
652
653
654
655
656
657
658
659
660
661
662
663
664
665
666
667
668
669
670
671
672
673
674
675
676
677
678
679
680
681
/*
 * (C) 2010-2015 see Authors.txt
 *
 * This file is part of MPC-HC.
 *
 * MPC-HC is free software; you can redistribute it and/or modify
 * it under the terms of the GNU General Public License as published by
 * the Free Software Foundation; either version 3 of the License, or
 * (at your option) any later version.
 *
 * MPC-HC is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 * GNU General Public License for more details.
 *
 * You should have received a copy of the GNU General Public License
 * along with this program.  If not, see <http://www.gnu.org/licenses/>.
 *
 */

#pragma once

#include "../../../SubPic/ISubPic.h"
#include "RenderersSettings.h"
#include "SyncAllocatorPresenter.h"
#include "AllocatorCommon.h"
#include <dxva2api.h>
#include "../../../DSUtil/WinapiFunc.h"

#define VMRBITMAP_UPDATE 0x80000000
#define MAX_PICTURE_SLOTS (60 + 2) // Last 2 for pixels shader!
#define NB_JITTER 126
#include "AsyncCallback.h"

extern bool g_bNoDuration; // Defined in MainFrm.cpp
extern bool g_bExternalSubtitleTime;

class CFocusThread;

// Possible messages to the PowerStrip API. PowerStrip is used to control
// the display frequency in one of the video - display synchronization modes.
// Powerstrip can also through a CGenlock object give very accurate timing data
// (given) that the gfx board is supported by PS.
#define UM_SETCUSTOMTIMING     (WM_USER + 200)
#define UM_SETREFRESHRATE      (WM_USER + 201)
#define UM_SETPOLARITY         (WM_USER + 202)
#define UM_REMOTECONTROL       (WM_USER + 210)
#define UM_SETGAMMARAMP        (WM_USER + 203)
#define UM_CREATERESOLUTION    (WM_USER + 204)
#define UM_GETTIMING           (WM_USER + 205)
#define UM_SETCUSTOMTIMINGFAST (WM_USER + 211) // Sets timing without writing to file. Faster

#define PositiveHorizontalPolarity  0x00
#define PositiveVerticalPolarity    0x00
#define NegativeHorizontalPolarity  0x02
#define NegativeVerticalPolarity    0x04
#define HideTrayIcon    0x00
#define ShowTrayIcon    0x01
#define ClosePowerStrip 0x63

#define HACTIVE     0
#define HFRONTPORCH 1
#define HSYNCWIDTH  2
#define HBACKPORCH  3
#define VACTIVE     4
#define VFRONTPORCH 5
#define VSYNCWIDTH  6
#define VBACKPORCH  7
#define PIXELCLOCK  8
#define UNKNOWN     9

// Guid to tag IMFSample with a group id
static const GUID GUID_GROUP_ID = { 0x309e32cc, 0x9b23, 0x4c6c, { 0x86, 0x63, 0xcd, 0xd9, 0xad, 0x49, 0x7f, 0x8a } };
// Guid to tag IMFSample with DirectX surface index
static const GUID GUID_SURFACE_INDEX = { 0x30c8e9f6, 0x415, 0x4b81, { 0xa3, 0x15, 0x1, 0xa, 0xc6, 0xa9, 0xda, 0x19 } };

namespace GothSync
{
    enum EVR_STATS_MSG {
        MSG_MIXERIN,
        MSG_MIXEROUT,
        MSG_ERROR
    };

#pragma pack(push, 1)

    template<int texcoords>
    struct MYD3DVERTEX {
        float x, y, z, rhw;
        struct {
            float u, v;
        } t[texcoords];
    };

    template<>
    struct MYD3DVERTEX<0> {
        float x, y, z, rhw;
        DWORD Diffuse;
    };

#pragma pack(pop)

    class CGenlock;
    class CSyncRenderer;

    // Base allocator-presenter
    class CBaseAP:
        public CSubPicAllocatorPresenterImpl
    {
    protected:
        CRenderersSettings::CAdvRendererSettings m_LastRendererSettings;

        HMODULE m_hDWMAPI;
        HRESULT(__stdcall* m_pDwmIsCompositionEnabled)(__out BOOL* pfEnabled);
        HRESULT(__stdcall* m_pDwmEnableComposition)(UINT uCompositionAction);
        HMODULE m_hD3D9;
        HRESULT(__stdcall* m_pDirect3DCreate9Ex)(UINT SDKVersion, IDirect3D9Ex**);

        CCritSec m_allocatorLock;
        CComPtr<IDirect3D9Ex> m_pD3DEx;
        CComPtr<IDirect3D9> m_pD3D;
        CComPtr<IDirect3DDevice9Ex> m_pD3DDevEx;
        CComPtr<IDirect3DDevice9> m_pD3DDev;

        CComPtr<IDirect3DTexture9> m_pVideoTexture[MAX_PICTURE_SLOTS];
        CComPtr<IDirect3DSurface9> m_pVideoSurface[MAX_PICTURE_SLOTS];
        CComPtr<IDirect3DTexture9> m_pOSDTexture;
        CComPtr<IDirect3DSurface9> m_pOSDSurface;
        CComPtr<ID3DXLine> m_pLine;
        CComPtr<ID3DXFont> m_pFont;
        CComPtr<ID3DXSprite> m_pSprite;
        CSyncRenderer* m_pOuterEVR;

        class CExternalPixelShader
        {
        public:
            CComPtr<IDirect3DPixelShader9> m_pPixelShader;
            CStringA m_SourceData;
            CStringA m_SourceTarget;
            HRESULT Compile(CPixelShaderCompiler* pCompiler) {
                HRESULT hr = pCompiler->CompileShader(m_SourceData, "main", m_SourceTarget, 0, &m_pPixelShader);
                if (FAILED(hr)) {
                    return hr;
                }
                return S_OK;
            }
        };

        CAutoPtr<CPixelShaderCompiler> m_pPSC;
        CAtlList<CExternalPixelShader> m_pPixelShaders;
        CAtlList<CExternalPixelShader> m_pPixelShadersScreenSpace;
        CComPtr<IDirect3DPixelShader9> m_pResizerPixelShader[4]; // bl, bc1, bc2_1, bc2_2
        CComPtr<IDirect3DTexture9> m_pScreenSizeTemporaryTexture[2];

        D3DFORMAT m_SurfaceType;
        D3DFORMAT m_BackbufferType;
        D3DFORMAT m_DisplayType;
        D3DTEXTUREFILTERTYPE m_filter;
        D3DCAPS9 m_caps;
        D3DPRESENT_PARAMETERS m_pp;

        bool SettingsNeedResetDevice();
        void SendResetRequest();
        virtual HRESULT CreateDXDevice(CString& _Error);
        virtual HRESULT ResetDXDevice(CString& _Error);
        virtual HRESULT AllocSurfaces(D3DFORMAT Format = D3DFMT_A8R8G8B8);
        virtual void DeleteSurfaces();

        LONGLONG m_LastAdapterCheck;
        UINT m_CurrentAdapter;

        float m_bicubicA;
        HRESULT InitResizers(float bicubicA, bool bNeedScreenSizeTexture);

        // Functions to trace timing performance
        void SyncStats(LONGLONG syncTime);
        void SyncOffsetStats(LONGLONG syncOffset);
        void InitStats();
        void DrawStats();

        template<int texcoords>
        void AdjustQuad(MYD3DVERTEX<texcoords>* v, double dx, double dy);
        template<int texcoords>
        HRESULT TextureBlt(IDirect3DDevice9* pD3DDev, MYD3DVERTEX<texcoords> v[4], D3DTEXTUREFILTERTYPE filter);
        MFOffset GetOffset(float v);
        MFVideoArea GetArea(float x, float y, DWORD width, DWORD height);
        bool ClipToSurface(IDirect3DSurface9* pSurface, CRect& s, CRect& d);

        HRESULT DrawRectBase(IDirect3DDevice9* pD3DDev, MYD3DVERTEX<0> v[4]);
        HRESULT DrawRect(DWORD _Color, DWORD _Alpha, const CRect& _Rect);
        HRESULT TextureCopy(IDirect3DTexture9* pTexture);
        HRESULT TextureResize(IDirect3DTexture9* pTexture, const Vector dst[4], D3DTEXTUREFILTERTYPE filter, const CRect& SrcRect);
        HRESULT TextureResizeBilinear(IDirect3DTexture9* pTexture, const Vector dst[4], const CRect& SrcRect);
        HRESULT TextureResizeBicubic1pass(IDirect3DTexture9* pTexture, const Vector dst[4], const CRect& SrcRect);
        HRESULT TextureResizeBicubic2pass(IDirect3DTexture9* pTexture, const Vector dst[4], const CRect& SrcRect);

        typedef HRESULT(WINAPI* D3DXLoadSurfaceFromMemoryPtr)(
            LPDIRECT3DSURFACE9 pDestSurface,
            CONST PALETTEENTRY* pDestPalette,
            CONST RECT* pDestRect,
            LPCVOID pSrcMemory,
            D3DFORMAT SrcFormat,
            UINT SrcPitch,
            CONST PALETTEENTRY* pSrcPalette,
            CONST RECT* pSrcRect,
            DWORD Filter,
            D3DCOLOR ColorKey);

        typedef HRESULT(WINAPI* D3DXCreateLinePtr)
        (LPDIRECT3DDEVICE9 pDevice,
         LPD3DXLINE* ppLine);

        typedef HRESULT(WINAPI* D3DXCreateFontPtr)(
            LPDIRECT3DDEVICE9 pDevice,
            int Height,
            UINT Width,
            UINT Weight,
            UINT MipLevels,
            bool Italic,
            DWORD CharSet,
            DWORD OutputPrecision,
            DWORD Quality,
            DWORD PitchAndFamily,
            LPCWSTR pFaceName,
            LPD3DXFONT* ppFont);

        HRESULT AlphaBlt(RECT* pSrc, const RECT* pDst, IDirect3DTexture9* pTexture);

        virtual void OnResetDevice() {};

        int m_nTearingPos;
        VMR9AlphaBitmap m_VMR9AlphaBitmap;
        CAutoVectorPtr<BYTE> m_VMR9AlphaBitmapData;
        CRect m_VMR9AlphaBitmapRect;
        int m_VMR9AlphaBitmapWidthBytes;

        D3DXLoadSurfaceFromMemoryPtr m_pD3DXLoadSurfaceFromMemory;
        D3DXCreateLinePtr m_pD3DXCreateLine;
        D3DXCreateFontPtr m_pD3DXCreateFont;
        HRESULT(__stdcall* m_pD3DXCreateSprite)(LPDIRECT3DDEVICE9 pDevice, LPD3DXSPRITE* ppSprite);

        int m_nDXSurface;   // Total number of DX Surfaces
        int m_nVMR9Surfaces;
        int m_iVMR9Surface;
        int m_nCurSurface; // Surface currently displayed
        long m_nUsedBuffer;

        LONG m_lNextSampleWait; // Waiting time for next sample in EVR
        bool m_bSnapToVSync;    // True if framerate is low enough so that snap to vsync makes sense

        UINT m_uScanLineEnteringPaint;      // The active scan line when entering Paint()
        REFERENCE_TIME m_llEstVBlankTime;   // Next vblank start time in reference clock "coordinates"

        double m_fAvrFps;       // Estimate the true FPS as given by the distance between vsyncs when a frame has been presented
        double m_fJitterStdDev; // VSync estimate std dev
        double m_fJitterMean;   // Means time between two syncpulses when a frame has been presented (i.e. when Paint() has been called

        double m_fSyncOffsetAvr;    // Means time between the call of Paint() and vsync. To avoid tearing this should be several ms at least
        double m_fSyncOffsetStdDev; // The std dev of the above

        bool m_bHighColorResolution;
        bool m_bCompositionEnabled;
        bool m_bDesktopCompositionDisabled;
        bool m_bIsFullscreen;
        bool m_bNeedCheckSample;
        DWORD m_dMainThreadId;

        CSize m_ScreenSize;

        // Display and frame rates and cycles
        double m_dDetectedScanlineTime; // Time for one (horizontal) scan line. Extracted at stream start and used to calculate vsync time
        double m_dD3DRefreshCycle;      // Display refresh cycle ms
        double m_dEstRefreshCycle;      // As estimated from scan lines
        double m_dFrameCycle;           // Average sample time, extracted from the samples themselves
        // double m_fps is defined in ISubPic.h
        double m_dOptimumDisplayCycle;  // The display cycle that is closest to the frame rate. A multiple of the actual display cycle
        double m_dCycleDifference;      // Difference in video and display cycle time relative to the video cycle time

        UINT m_pcFramesDropped;
        UINT m_pcFramesDuplicated;
        UINT m_pcFramesDrawn;

        LONGLONG m_pllJitter [NB_JITTER];     // Vertical sync time stats
        LONGLONG m_pllSyncOffset [NB_JITTER]; // Sync offset time stats
        int m_nNextJitter;
        int m_nNextSyncOffset;
        LONGLONG m_JitterStdDev;

        LONGLONG m_llLastSyncTime;

        LONGLONG m_MaxJitter;
        LONGLONG m_MinJitter;
        LONGLONG m_MaxSyncOffset;
        LONGLONG m_MinSyncOffset;
        UINT m_uSyncGlitches;

        LONGLONG m_llSampleTime, m_llLastSampleTime; // Present time for the current sample
        LONGLONG m_llHysteresis;
        LONG m_lShiftToNearest, m_lShiftToNearestPrev;
        bool m_bVideoSlowerThanDisplay;

        REFERENCE_TIME m_rtTimePerFrame;
        bool m_bInterlaced;
        double m_TextScale;
        CString  m_strStatsMsg[10];

        CGenlock* m_pGenlock;                 // The video - display synchronizer class
        CComPtr<IReferenceClock> m_pRefClock; // The reference clock. Used in Paint()
        CComPtr<IAMAudioRendererStats> m_pAudioStats; // Audio statistics from audio renderer. To check so that audio is in sync
        DWORD m_lAudioLag;                    // Time difference between audio and video when the audio renderer is matching rate to the external reference clock
        long m_lAudioLagMin, m_lAudioLagMax;  // The accumulated difference between the audio renderer and the master clock
        DWORD m_lAudioSlaveMode;              // To check whether the audio renderer matches rate with SyncClock (returns the value 4 if it does)

        double GetRefreshRate();              // Get the best estimate of the display refresh rate in Hz
        double GetDisplayCycle();             // Get the best estimate of the display cycle time in milliseconds
        double GetCycleDifference();          // Get the difference in video and display cycle times.
        void EstimateRefreshTimings();        // Estimate the times for one scan line and one frame respectively from the actual refresh data
        bool ExtractInterlaced(const AM_MEDIA_TYPE* pmt);

        CFocusThread* m_FocusThread;
        HWND          m_hFocusWindow;

    public:
        CBaseAP(HWND hWnd, bool bFullscreen, HRESULT& hr, CString& _Error);
        ~CBaseAP();

        CCritSec m_VMR9AlphaBitmapLock;
        void UpdateAlphaBitmap();
        void ResetStats();

        // ISubPicAllocatorPresenter
        STDMETHODIMP CreateRenderer(IUnknown** ppRenderer);
        STDMETHODIMP_(bool) Paint(bool bAll);
        STDMETHODIMP GetDIB(BYTE* lpDib, DWORD* size);
        STDMETHODIMP SetPixelShader(LPCSTR pSrcData, LPCSTR pTarget);
        STDMETHODIMP SetPixelShader2(LPCSTR pSrcData, LPCSTR pTarget, bool bScreenSpace);
        STDMETHODIMP_(bool) ResetDevice();
        STDMETHODIMP_(bool) DisplayChange();
    };

    class CSyncAP:
        public CBaseAP,
        public IMFGetService,
        public IMFTopologyServiceLookupClient,
        public IMFVideoDeviceID,
        public IMFVideoPresenter,
        public IDirect3DDeviceManager9,
        public IMFAsyncCallback,
        public IQualProp,
        public IMFRateSupport,
        public IMFVideoDisplayControl,
        public IEVRTrustedVideoPlugin,
        public ISyncClockAdviser,
        public ID3DFullscreenControl
    {
    public:
        CSyncAP(HWND hWnd, bool bFullscreen, HRESULT& hr, CString& _Error);
        ~CSyncAP();

        DECLARE_IUNKNOWN;
        STDMETHODIMP NonDelegatingQueryInterface(REFIID riid, void** ppv);

        STDMETHODIMP CreateRenderer(IUnknown** ppRenderer);
        STDMETHODIMP_(bool) Paint(bool bAll);
        STDMETHODIMP GetNativeVideoSize(LONG* lpWidth, LONG* lpHeight, LONG* lpARWidth, LONG* lpARHeight);
        STDMETHODIMP InitializeDevice(AM_MEDIA_TYPE* pMediaType);
        STDMETHODIMP_(bool) ResetDevice();

        // ISubPicAllocatorPresenter2
        STDMETHODIMP_(bool) IsRendering() {
            return (m_nRenderState == Started);
        }

        // IMFClockStateSink
        STDMETHODIMP OnClockStart(MFTIME hnsSystemTime, LONGLONG llClockStartOffset);
        STDMETHODIMP OnClockStop(MFTIME hnsSystemTime);
        STDMETHODIMP OnClockPause(MFTIME hnsSystemTime);
        STDMETHODIMP OnClockRestart(MFTIME hnsSystemTime);
        STDMETHODIMP OnClockSetRate(MFTIME hnsSystemTime, float flRate);

        // IBaseFilter delegate
        bool GetState(DWORD dwMilliSecsTimeout, FILTER_STATE* State, HRESULT& _ReturnValue);

        // IQualProp (EVR statistics window). These are incompletely implemented currently
        STDMETHODIMP get_FramesDroppedInRenderer(int* pcFrames);
        STDMETHODIMP get_FramesDrawn(int* pcFramesDrawn);
        STDMETHODIMP get_AvgFrameRate(int* piAvgFrameRate);
        STDMETHODIMP get_Jitter(int* iJitter);
        STDMETHODIMP get_AvgSyncOffset(int* piAvg);
        STDMETHODIMP get_DevSyncOffset(int* piDev);

        // IMFRateSupport
        STDMETHODIMP GetSlowestRate(MFRATE_DIRECTION eDirection, BOOL fThin, float* pflRate);
        STDMETHODIMP GetFastestRate(MFRATE_DIRECTION eDirection, BOOL fThin, float* pflRate);
        STDMETHODIMP IsRateSupported(BOOL fThin, float flRate, float* pflNearestSupportedRate);
        float GetMaxRate(BOOL bThin);

        // IMFVideoPresenter
        STDMETHODIMP ProcessMessage(MFVP_MESSAGE_TYPE eMessage, ULONG_PTR ulParam);
        STDMETHODIMP GetCurrentMediaType(__deref_out  IMFVideoMediaType** ppMediaType);

        // IMFTopologyServiceLookupClient
        STDMETHODIMP InitServicePointers(__in  IMFTopologyServiceLookup* pLookup);
        STDMETHODIMP ReleaseServicePointers();

        // IMFVideoDeviceID
        STDMETHODIMP GetDeviceID(__out  IID* pDeviceID);

        // IMFGetService
        STDMETHODIMP GetService(__RPC__in REFGUID guidService, __RPC__in REFIID riid, __RPC__deref_out_opt LPVOID* ppvObject);

        // IMFAsyncCallback
        STDMETHODIMP GetParameters(__RPC__out DWORD* pdwFlags, /* [out] */ __RPC__out DWORD* pdwQueue);
        STDMETHODIMP Invoke(__RPC__in_opt IMFAsyncResult* pAsyncResult);

        // IMFVideoDisplayControl
        STDMETHODIMP GetNativeVideoSize(SIZE* pszVideo, SIZE* pszARVideo);
        STDMETHODIMP GetIdealVideoSize(SIZE* pszMin, SIZE* pszMax);
        STDMETHODIMP SetVideoPosition(const MFVideoNormalizedRect* pnrcSource, const LPRECT prcDest);
        STDMETHODIMP GetVideoPosition(MFVideoNormalizedRect* pnrcSource, LPRECT prcDest);
        STDMETHODIMP SetAspectRatioMode(DWORD dwAspectRatioMode);
        STDMETHODIMP GetAspectRatioMode(DWORD* pdwAspectRatioMode);
        STDMETHODIMP SetVideoWindow(HWND hwndVideo);
        STDMETHODIMP GetVideoWindow(HWND* phwndVideo);
        STDMETHODIMP RepaintVideo();
        STDMETHODIMP GetCurrentImage(BITMAPINFOHEADER* pBih, BYTE** pDib, DWORD* pcbDib, LONGLONG* pTimeStamp);
        STDMETHODIMP SetBorderColor(COLORREF Clr);
        STDMETHODIMP GetBorderColor(COLORREF* pClr);
        STDMETHODIMP SetRenderingPrefs(DWORD dwRenderFlags);
        STDMETHODIMP GetRenderingPrefs(DWORD* pdwRenderFlags);
        STDMETHODIMP SetFullscreen(BOOL fFullscreen);
        STDMETHODIMP GetFullscreen(BOOL* pfFullscreen);

        // IEVRTrustedVideoPlugin
        STDMETHODIMP IsInTrustedVideoMode(BOOL* pYes);
        STDMETHODIMP CanConstrict(BOOL* pYes);
        STDMETHODIMP SetConstriction(DWORD dwKPix);
        STDMETHODIMP DisableImageExport(BOOL bDisable);

        // IDirect3DDeviceManager9
        STDMETHODIMP ResetDevice(IDirect3DDevice9* pDevice, UINT resetToken);
        STDMETHODIMP OpenDeviceHandle(HANDLE* phDevice);
        STDMETHODIMP CloseDeviceHandle(HANDLE hDevice);
        STDMETHODIMP TestDevice(HANDLE hDevice);
        STDMETHODIMP LockDevice(HANDLE hDevice, IDirect3DDevice9** ppDevice, BOOL fBlock);
        STDMETHODIMP UnlockDevice(HANDLE hDevice, BOOL fSaveState);
        STDMETHODIMP GetVideoService(HANDLE hDevice, REFIID riid, void** ppService);

        // ID3DFullscreenControl
        STDMETHODIMP SetD3DFullscreen(bool fEnabled);
        STDMETHODIMP GetD3DFullscreen(bool* pfEnabled);

    protected:
        STDMETHODIMP_(bool) Paint(IMFSample* pMFSample);
        void OnResetDevice();
        MFCLOCK_STATE m_LastClockState;

    private:
        enum RENDER_STATE {
            Started  = State_Running,
            Stopped  = State_Stopped,
            Paused   = State_Paused,
            Shutdown = State_Running + 1
        };

        CComPtr<IMFClock> m_pClock;
        CComPtr<IDirect3DDeviceManager9> m_pD3DManager;
        CComPtr<IMFTransform> m_pMixer;
        CComPtr<IMediaEventSink> m_pSink;
        CComPtr<IMFMediaType> m_pMediaType;
        MFVideoAspectRatioMode m_dwVideoAspectRatioMode;
        MFVideoRenderPrefs m_dwVideoRenderPrefs;
        COLORREF m_BorderColor;

        HANDLE m_hEvtQuit;  // Stop rendering thread event
        bool m_bEvtQuit;
        HANDLE m_hEvtFlush; // Discard all buffers
        bool m_bEvtFlush;
        HANDLE m_hEvtSkip;  // Skip frame
        bool m_bEvtSkip;

        bool m_bUseInternalTimer;
        INT32 m_LastSetOutputRange;
        bool m_bPendingRenegotiate;
        bool m_bPendingMediaFinished;
        bool m_bPrerolled;  // true if first sample has been displayed.

        HANDLE m_hRenderThread;
        HANDLE m_hMixerThread;
        RENDER_STATE m_nRenderState;
        bool m_bStepping;

        CCritSec m_SampleQueueLock;
        CCritSec m_ImageProcessingLock;

        UINT32                    m_nCurrentGroupId;
        CInterfaceList<IMFSample> m_FreeSamples;
        CInterfaceList<IMFSample> m_ScheduledSamples;
        CComPtr<IMFSample>        m_pCurrentlyDisplayedSample;
        UINT m_nResetToken;
        int m_nStepCount;

        bool GetSampleFromMixer();
        void MixerThread();
        static DWORD WINAPI MixerThreadStatic(LPVOID lpParam);
        void RenderThread();
        static DWORD WINAPI RenderThreadStatic(LPVOID lpParam);

        void StartWorkerThreads();
        void StopWorkerThreads();
        HRESULT CheckShutdown() const;
        void CompleteFrameStep(bool bCancel);

        void RemoveAllSamples();
        STDMETHODIMP AdviseSyncClock(ISyncClock* sC);
        HRESULT BeginStreaming();
        HRESULT GetFreeSample(IMFSample** ppSample);
        HRESULT GetScheduledSample(IMFSample** ppSample, int& count);
        void AddToFreeList(IMFSample* pSample, bool bTail);
        void AddToScheduledList(IMFSample* pSample, bool bSorted);
        void FlushSamples();

        HRESULT TrackSample(IMFSample* pSample);

        // Callback when a video sample is released.
        HRESULT OnSampleFree(IMFAsyncResult* pResult);
        AsyncCallback<CSyncAP> m_SampleFreeCallback;

        LONGLONG GetMediaTypeMerit(IMFMediaType* pMediaType);
        HRESULT RenegotiateMediaType();
        HRESULT IsMediaTypeSupported(IMFMediaType* pMixerType);
        HRESULT CreateOptimalOutputType(IMFMediaType* pMixerProposedType, IMFMediaType* pMixerInputType, IMFMediaType** ppType);
        HRESULT SetMediaType(IMFMediaType* pType);

        const WinapiFunc<decltype(DXVA2CreateDirect3DDeviceManager9)> fnDXVA2CreateDirect3DDeviceManager9;
        const WinapiFunc<decltype(MFCreateDXSurfaceBuffer)> fnMFCreateDXSurfaceBuffer;
        const WinapiFunc<decltype(MFCreateVideoSampleFromSurface)> fnMFCreateVideoSampleFromSurface;
        const WinapiFunc<decltype(MFCreateMediaType)> fnMFCreateMediaType;

        const WinapiFunc<decltype(AvSetMmThreadCharacteristicsW)> fnAvSetMmThreadCharacteristicsW;
        const WinapiFunc<decltype(AvSetMmThreadPriority)> fnAvSetMmThreadPriority;
        const WinapiFunc<decltype(AvRevertMmThreadCharacteristics)> fnAvRevertMmThreadCharacteristics;
    };

    class CSyncRenderer:
        public CUnknown,
        public IVMRffdshow9,
        public IVMRMixerBitmap9,
        public IBaseFilter
    {
        CComPtr<IUnknown> m_pEVR;
        IBaseFilter* m_pEVRBase;
        VMR9AlphaBitmap* m_pVMR9AlphaBitmap;
        CSyncAP* m_pAllocatorPresenter;

    public:
        CSyncRenderer(const TCHAR* pName, LPUNKNOWN pUnk, HRESULT& hr, VMR9AlphaBitmap* pVMR9AlphaBitmap, CSyncAP* pAllocatorPresenter);
        ~CSyncRenderer();

        // IBaseFilter
        virtual HRESULT STDMETHODCALLTYPE EnumPins(__out IEnumPins** ppEnum);
        virtual HRESULT STDMETHODCALLTYPE FindPin(LPCWSTR Id, __out IPin** ppPin);
        virtual HRESULT STDMETHODCALLTYPE QueryFilterInfo(__out FILTER_INFO* pInfo);
        virtual HRESULT STDMETHODCALLTYPE JoinFilterGraph(__in_opt IFilterGraph* pGraph, __in_opt LPCWSTR pName);
        virtual HRESULT STDMETHODCALLTYPE QueryVendorInfo(__out LPWSTR* pVendorInfo);
        virtual HRESULT STDMETHODCALLTYPE Stop();
        virtual HRESULT STDMETHODCALLTYPE Pause();
        virtual HRESULT STDMETHODCALLTYPE Run(REFERENCE_TIME tStart);
        virtual HRESULT STDMETHODCALLTYPE GetState(DWORD dwMilliSecsTimeout, __out FILTER_STATE* State);
        virtual HRESULT STDMETHODCALLTYPE SetSyncSource(__in_opt  IReferenceClock* pClock);
        virtual HRESULT STDMETHODCALLTYPE GetSyncSource(__deref_out_opt  IReferenceClock** pClock);
        virtual HRESULT STDMETHODCALLTYPE GetClassID(__RPC__out CLSID* pClassID);

        // IVMRffdshow9
        virtual HRESULT STDMETHODCALLTYPE support_ffdshow();

        // IVMRMixerBitmap9
        STDMETHODIMP GetAlphaBitmapParameters(VMR9AlphaBitmap* pBmpParms);
        STDMETHODIMP SetAlphaBitmap(const VMR9AlphaBitmap*  pBmpParms);
        STDMETHODIMP UpdateAlphaBitmapParameters(const VMR9AlphaBitmap* pBmpParms);

        DECLARE_IUNKNOWN;
        virtual HRESULT STDMETHODCALLTYPE NonDelegatingQueryInterface(REFIID riid, void** ppvObject);
    };

    class CGenlock
    {
    public:
        class MovingAverage
        {
        public:
            MovingAverage(size_t size)
                : fifoSize(size)
                , fifo(fifoSize)
                , oldestSample(0)
                , sum(0.0) {
            }

            double Average(double sample) {
                sum = sum + sample - fifo[oldestSample];
                fifo[oldestSample] = sample;
                oldestSample++;
                if (oldestSample == fifoSize) {
                    oldestSample = 0;
                }
                return sum / fifoSize;
            }

        private:
            size_t fifoSize;
            std::vector<double> fifo;
            size_t oldestSample;
            double sum;
        };

        CGenlock(double target, double limit, int rowD, int colD, double clockD, UINT mon);
        CGenlock(const CGenlock&) = delete;
        ~CGenlock();

        CGenlock& operator=(const CGenlock&) = delete;

        BOOL PowerstripRunning();   // TRUE if PowerStrip is running
        HRESULT GetTiming();        // Get the string representing the display's current timing parameters
        HRESULT ResetTiming();      // Reset timing to what was last registered by GetTiming()
        HRESULT ResetClock();       // Reset reference clock speed to nominal
        HRESULT SetTargetSyncOffset(double targetD);
        HRESULT GetTargetSyncOffset(double* targetD);
        HRESULT SetControlLimit(double cL);
        HRESULT GetControlLimit(double* cL);
        HRESULT SetDisplayResolution(UINT columns, UINT lines);
        HRESULT AdviseSyncClock(ISyncClock* sC);
        HRESULT SetMonitor(UINT mon); // Set the number of the monitor to synchronize
        HRESULT ResetStats();         // Reset timing statistics

        HRESULT ControlDisplay(double syncOffset, double frameCycle); // Adjust the frequency of the display if needed
        HRESULT ControlClock(double syncOffset, double frameCycle);   // Adjust the frequency of the clock if needed
        HRESULT UpdateStats(double syncOffset, double frameCycle);    // Don't adjust anything, just update the syncOffset stats

        bool powerstripTimingExists;        // true if display timing has been got through Powerstrip
        bool liveSource;                    // true if live source -> display sync is the only option
        int adjDelta;                       // -1 for display slower in relation to video, 0 for keep, 1 for faster
        int lineDelta;                      // The number of rows added or subtracted when adjusting display fps
        int columnDelta;                    // The number of colums added or subtracted when adjusting display fps
        double cycleDelta;                  // Adjustment factor for cycle time as fraction of nominal value
        UINT displayAdjustmentsMade;        // The number of adjustments made to display refresh rate
        UINT clockAdjustmentsMade;          // The number of adjustments made to clock frequency

        UINT totalLines, totalColumns;      // Including the porches and sync widths
        UINT visibleLines, visibleColumns;  // The nominal resolution
        MovingAverage syncOffsetFifo;
        MovingAverage frameCycleFifo;
        double minSyncOffset, maxSyncOffset;
        double syncOffsetAvg; // Average of the above
        double minFrameCycle, maxFrameCycle;
        double frameCycleAvg;

        UINT pixelClock;                // In pixels/s
        double displayFreqCruise;       // Nominal display frequency in frames/s
        double displayFreqSlower;
        double displayFreqFaster;
        double curDisplayFreq;          // Current (adjusted) display frequency
        double controlLimit;            // How much the sync offset is allowed to drift from target sync offset
        WPARAM monitor;                 // The monitor to be controlled. 0-based.
        CComPtr<ISyncClock> syncClock;  // Interface to an adjustable reference clock

    private:
        HWND psWnd; // PowerStrip window
        const static int TIMING_PARAM_CNT = 10;
        const static int MAX_LOADSTRING = 100;
        std::array<UINT, TIMING_PARAM_CNT> displayTiming;       // Display timing parameters
        std::array<UINT, TIMING_PARAM_CNT> displayTimingSave;   // So that we can reset the display at exit
        TCHAR faster[MAX_LOADSTRING];                           // String corresponding to faster display frequency
        TCHAR cruise[MAX_LOADSTRING];                           // String corresponding to nominal display frequency
        TCHAR slower[MAX_LOADSTRING];                           // String corresponding to slower display frequency
        TCHAR savedTiming[MAX_LOADSTRING];                      // String version of saved timing (to be restored upon exit)
        double lowSyncOffset;       // The closest we want to let the scheduled render time to get to the next vsync. In % of the frame time
        double targetSyncOffset;    // Where we want the scheduled render time to be in relation to the next vsync
        double highSyncOffset;      // The furthers we want to let the scheduled render time to get to the next vsync
        CCritSec csGenlockLock;
    };
}