1 /*
2  *  Copyright (C) 2005-2018 Team Kodi
3  *  This file is part of Kodi - https://kodi.tv
4  *
5  *  SPDX-License-Identifier: GPL-2.0-or-later
6  *  See LICENSES/README.md for more information.
7  */
8 
9 #pragma once
10 
11 /**
12  * design goals:
13  * - improve performance
14  *   max out hw resources: e.g. make 1080p60 play on ION2
15  *   allow advanced de-interlacing on ION
16  *
17  * - add vdpau/opengl interop
18  *
19  * - remove tight dependency to render thread
20  *   prior design needed to hijack render thread in order to do
21  *   gl interop functions. In particular this was a problem for
22  *   init and clear down. Introduction of GL_NV_vdpau_interop
23  *   increased the need to be independent from render thread
24  *
25  * - move to an actor based design in order to reduce the number
26  *   of locks needed.
27  */
28 
29 #include "cores/VideoPlayer/Buffers/VideoBuffer.h"
30 #include "cores/VideoPlayer/DVDCodecs/Video/DVDVideoCodec.h"
31 #include "cores/VideoSettings.h"
32 #include "guilib/DispResource.h"
33 #include "threads/CriticalSection.h"
34 #include "threads/Event.h"
35 #include "threads/SharedSection.h"
36 #include "threads/Thread.h"
37 #include "utils/ActorProtocol.h"
38 #include "utils/Geometry.h"
39 
40 #include <deque>
41 #include <list>
42 #include <map>
43 #include <utility>
44 #include <vector>
45 
46 #include <X11/Xlib.h>
47 #include <X11/Xutil.h>
48 
49 extern "C" {
50 #include <libavutil/avutil.h>
51 #include <libavcodec/vdpau.h>
52 }
53 
54 class CProcessInfo;
55 
56 namespace VDPAU
57 {
58 
59 /**
60  * VDPAU interface to driver
61  */
62 
63 struct VDPAU_procs
64 {
65   VdpGetProcAddress*vdp_get_proc_address;
66   VdpDeviceDestroy*                    vdp_device_destroy;
67 
68   VdpVideoSurfaceCreate* vdp_video_surface_create;
69   VdpVideoSurfaceDestroy* vdp_video_surface_destroy;
70   VdpVideoSurfacePutBitsYCbCr* vdp_video_surface_put_bits_y_cb_cr;
71   VdpVideoSurfaceGetBitsYCbCr* vdp_video_surface_get_bits_y_cb_cr;
72 
73   VdpOutputSurfacePutBitsYCbCr* vdp_output_surface_put_bits_y_cb_cr;
74   VdpOutputSurfacePutBitsNative* vdp_output_surface_put_bits_native;
75   VdpOutputSurfaceCreate* vdp_output_surface_create;
76   VdpOutputSurfaceDestroy* vdp_output_surface_destroy;
77   VdpOutputSurfaceGetBitsNative* vdp_output_surface_get_bits_native;
78   VdpOutputSurfaceRenderOutputSurface* vdp_output_surface_render_output_surface;
79   VdpOutputSurfacePutBitsIndexed* vdp_output_surface_put_bits_indexed;
80 
81   VdpVideoMixerCreate* vdp_video_mixer_create;
82   VdpVideoMixerSetFeatureEnables* vdp_video_mixer_set_feature_enables;
83   VdpVideoMixerQueryParameterSupport* vdp_video_mixer_query_parameter_support;
84   VdpVideoMixerQueryFeatureSupport* vdp_video_mixer_query_feature_support;
85   VdpVideoMixerDestroy* vdp_video_mixer_destroy;
86   VdpVideoMixerRender* vdp_video_mixer_render;
87   VdpVideoMixerSetAttributeValues* vdp_video_mixer_set_attribute_values;
88 
89   VdpGenerateCSCMatrix*  vdp_generate_csc_matrix;
90 
91   VdpGetErrorString* vdp_get_error_string;
92 
93   VdpDecoderCreate* vdp_decoder_create;
94   VdpDecoderDestroy* vdp_decoder_destroy;
95   VdpDecoderRender* vdp_decoder_render;
96   VdpDecoderQueryCapabilities* vdp_decoder_query_caps;
97 
98   VdpPreemptionCallbackRegister* vdp_preemption_callback_register;
99 };
100 
101 //-----------------------------------------------------------------------------
102 // VDPAU data structs
103 //-----------------------------------------------------------------------------
104 
105 class CDecoder;
106 
107 /**
108  * Buffer statistics used to control number of frames in queue
109  */
110 
111 class CVdpauBufferStats
112 {
113 public:
114   uint16_t decodedPics;
115   uint16_t processedPics;
116   uint16_t renderPics;
117   uint64_t latency; // time decoder has waited for a frame, ideally there is no latency
118   int codecFlags;
119   bool canSkipDeint;
120   bool draining;
121 
IncDecoded()122   void IncDecoded() { CSingleLock l(m_sec); decodedPics++;}
DecDecoded()123   void DecDecoded() { CSingleLock l(m_sec); decodedPics--;}
IncProcessed()124   void IncProcessed() { CSingleLock l(m_sec); processedPics++;}
DecProcessed()125   void DecProcessed() { CSingleLock l(m_sec); processedPics--;}
IncRender()126   void IncRender() { CSingleLock l(m_sec); renderPics++;}
DecRender()127   void DecRender() { CSingleLock l(m_sec); renderPics--;}
Reset()128   void Reset() { CSingleLock l(m_sec); decodedPics=0; processedPics=0;renderPics=0;latency=0;}
Get(uint16_t & decoded,uint16_t & processed,uint16_t & render)129   void Get(uint16_t &decoded, uint16_t &processed, uint16_t &render) {CSingleLock l(m_sec); decoded = decodedPics, processed=processedPics, render=renderPics;}
SetParams(uint64_t time,int flags)130   void SetParams(uint64_t time, int flags) { CSingleLock l(m_sec); latency = time; codecFlags = flags; }
GetParams(uint64_t & lat,int & flags)131   void GetParams(uint64_t &lat, int &flags) { CSingleLock l(m_sec); lat = latency; flags = codecFlags; }
SetCanSkipDeint(bool canSkip)132   void SetCanSkipDeint(bool canSkip) { CSingleLock l(m_sec); canSkipDeint = canSkip; }
CanSkipDeint()133   bool CanSkipDeint() { CSingleLock l(m_sec); if (canSkipDeint) return true; else return false;}
SetDraining(bool drain)134   void SetDraining(bool drain) { CSingleLock l(m_sec); draining = drain; }
IsDraining()135   bool IsDraining() { CSingleLock l(m_sec); if (draining) return true; else return false;}
136 private:
137   CCriticalSection m_sec;
138 };
139 
140 /**
141  *  CVdpauConfig holds all configuration parameters needed by vdpau
142  *  The structure is sent to the internal classes CMixer and COutput
143  *  for init.
144  */
145 
146 class CVideoSurfaces;
147 class CVDPAUContext;
148 
149 struct CVdpauConfig
150 {
151   int surfaceWidth;
152   int surfaceHeight;
153   int vidWidth;
154   int vidHeight;
155   int outWidth;
156   int outHeight;
157   VdpDecoder vdpDecoder;
158   VdpChromaType vdpChromaType;
159   CVdpauBufferStats *stats;
160   CDecoder *vdpau;
161   int upscale;
162   CVideoSurfaces *videoSurfaces;
163   int numRenderBuffers;
164   uint32_t maxReferences;
165   bool useInteropYuv;
166   CVDPAUContext *context;
167   CProcessInfo *processInfo;
168   int resetCounter;
169   uint64_t timeOpened;
170 };
171 
172 /**
173  * Holds a decoded frame
174  * Input to COutput for further processing
175  */
176 struct CVdpauDecodedPicture
177 {
178   CVdpauDecodedPicture() = default;
CVdpauDecodedPictureCVdpauDecodedPicture179   CVdpauDecodedPicture(const CVdpauDecodedPicture &rhs)
180   {
181     *this = rhs;
182   }
183   CVdpauDecodedPicture& operator=(const CVdpauDecodedPicture& rhs)
184   {
185     DVDPic.SetParams(rhs.DVDPic);
186     videoSurface = rhs.videoSurface;
187     isYuv = rhs.isYuv;
188     return *this;
189   };
190   VideoPicture DVDPic;
191   VdpVideoSurface videoSurface;
192   bool isYuv;
193 };
194 
195 /**
196  * Frame after having been processed by vdpau mixer
197  */
198 struct CVdpauProcessedPicture
199 {
200   CVdpauProcessedPicture() = default;
CVdpauProcessedPictureCVdpauProcessedPicture201   CVdpauProcessedPicture(const CVdpauProcessedPicture& rhs)
202   {
203     *this = rhs;
204   }
205   CVdpauProcessedPicture& operator=(const CVdpauProcessedPicture& rhs)
206   {
207     DVDPic.SetParams(rhs.DVDPic);
208     videoSurface = rhs.videoSurface;
209     outputSurface = rhs.outputSurface;
210     crop = rhs.crop;
211     isYuv = rhs.isYuv;
212     id = rhs.id;
213     return *this;
214   };
215 
216   VideoPicture DVDPic;
217   VdpVideoSurface videoSurface = VDP_INVALID_HANDLE;
218   VdpOutputSurface outputSurface = VDP_INVALID_HANDLE;
219   bool crop;
220   bool isYuv;
221   int id = 0;
222 };
223 
224 class CVdpauRenderPicture : public CVideoBuffer
225 {
226 public:
CVdpauRenderPicture(int id)227   explicit CVdpauRenderPicture(int id) : CVideoBuffer(id) { }
228   VideoPicture DVDPic;
229   CVdpauProcessedPicture procPic;
230   int width;
231   int height;
232   CRect crop;
233   void *device;
234   void *procFunc;
235   int64_t ident;
236 };
237 
238 //-----------------------------------------------------------------------------
239 // Mixer
240 //-----------------------------------------------------------------------------
241 
242 class CMixerControlProtocol : public Actor::Protocol
243 {
244 public:
CMixerControlProtocol(std::string name,CEvent * inEvent,CEvent * outEvent)245   CMixerControlProtocol(std::string name, CEvent* inEvent, CEvent* outEvent)
246     : Protocol(std::move(name), inEvent, outEvent){};
247   enum OutSignal
248   {
249     INIT = 0,
250     FLUSH,
251     TIMEOUT,
252   };
253   enum InSignal
254   {
255     ACC,
256     ERROR,
257   };
258 };
259 
260 class CMixerDataProtocol : public Actor::Protocol
261 {
262 public:
CMixerDataProtocol(std::string name,CEvent * inEvent,CEvent * outEvent)263   CMixerDataProtocol(std::string name, CEvent* inEvent, CEvent* outEvent)
264     : Protocol(std::move(name), inEvent, outEvent){};
265   enum OutSignal
266   {
267     FRAME,
268     BUFFER,
269   };
270   enum InSignal
271   {
272     PICTURE,
273   };
274 };
275 
276 /**
277  * Embeds the vdpau video mixer
278  * Embedded by COutput class, gets decoded frames from COutput, processes
279  * them in mixer ands sends processed frames back to COutput
280  */
281 class CMixer : private CThread
282 {
283 public:
284   explicit CMixer(CEvent *inMsgEvent);
285   ~CMixer() override;
286   void Start();
287   void Dispose();
288   bool IsActive();
289   CMixerControlProtocol m_controlPort;
290   CMixerDataProtocol m_dataPort;
291 protected:
292   void OnStartup() override;
293   void OnExit() override;
294   void Process() override;
295   void StateMachine(int signal, Actor::Protocol *port, Actor::Message *msg);
296   void Init();
297   void Uninit();
298   void Flush();
299   void CreateVdpauMixer();
300   void ProcessPicture();
301   void InitCycle();
302   void FiniCycle();
303   void CheckFeatures();
304   void SetPostProcFeatures(bool postProcEnabled);
305   void PostProcOff();
306   void InitCSCMatrix(int Width);
307   bool GenerateStudioCSCMatrix(VdpColorStandard colorStandard, VdpCSCMatrix &studioCSCMatrix);
308   void SetColor();
309   void SetNoiseReduction();
310   void SetSharpness();
311   void SetDeintSkipChroma();
312   void SetDeinterlacing();
313   void SetHWUpscaling();
314   void DisableHQScaling();
315   std::string GetDeintStrFromInterlaceMethod(EINTERLACEMETHOD method);
316   bool CheckStatus(VdpStatus vdp_st, int line);
317   CEvent m_outMsgEvent;
318   CEvent *m_inMsgEvent;
319   int m_state;
320   bool m_bStateMachineSelfTrigger;
321 
322   // extended state variables for state machine
323   int m_extTimeout;
324   bool m_vdpError;
325   CVdpauConfig m_config;
326   VdpVideoMixer m_videoMixer;
327   VdpProcamp m_Procamp;
328   VdpCSCMatrix  m_CSCMatrix;
329   bool m_PostProc;
330   float m_Brightness;
331   float m_Contrast;
332   float m_NoiseReduction;
333   float m_Sharpness;
334   int m_Deint;
335   int m_Upscale;
336   bool m_SeenInterlaceFlag;
337   unsigned int m_ColorMatrix       : 4;
338   VdpVideoMixerPictureStructure m_mixerfield;
339   int m_mixerstep;
340   int m_mixersteps;
341   CVdpauProcessedPicture m_processPicture;
342   std::queue<VdpOutputSurface> m_outputSurfaces;
343   std::queue<CVdpauDecodedPicture> m_decodedPics;
344   std::deque<CVdpauDecodedPicture> m_mixerInput;
345 };
346 
347 //-----------------------------------------------------------------------------
348 // Output
349 //-----------------------------------------------------------------------------
350 
351 class COutputControlProtocol : public Actor::Protocol
352 {
353 public:
COutputControlProtocol(std::string name,CEvent * inEvent,CEvent * outEvent)354   COutputControlProtocol(std::string name, CEvent* inEvent, CEvent* outEvent)
355     : Actor::Protocol(std::move(name), inEvent, outEvent){};
356   enum OutSignal
357   {
358     INIT,
359     FLUSH,
360     PRECLEANUP,
361     TIMEOUT,
362   };
363   enum InSignal
364   {
365     ACC,
366     ERROR,
367     STATS,
368   };
369 };
370 
371 class COutputDataProtocol : public Actor::Protocol
372 {
373 public:
COutputDataProtocol(std::string name,CEvent * inEvent,CEvent * outEvent)374   COutputDataProtocol(std::string name, CEvent* inEvent, CEvent* outEvent)
375     : Actor::Protocol(std::move(name), inEvent, outEvent){};
376   enum OutSignal
377   {
378     NEWFRAME = 0,
379     RETURNPIC,
380   };
381   enum InSignal
382   {
383     PICTURE,
384   };
385 };
386 
387 /**
388  * COutput is embedded in CDecoder and embeds CMixer
389  * The class has its own OpenGl context which is shared with render thread
390  * COutput generated ready to render textures and passes them back to
391  * CDecoder
392  */
393 class CVdpauBufferPool;
394 
395 class COutput : private CThread
396 {
397 public:
398   COutput(CDecoder &decoder, CEvent *inMsgEvent);
399   ~COutput() override;
400   void Start();
401   void Dispose();
402   COutputControlProtocol m_controlPort;
403   COutputDataProtocol m_dataPort;
404 protected:
405   void OnStartup() override;
406   void OnExit() override;
407   void Process() override;
408   void StateMachine(int signal, Actor::Protocol *port, Actor::Message *msg);
409   bool HasWork();
410   CVdpauRenderPicture *ProcessMixerPicture();
411   void QueueReturnPicture(CVdpauRenderPicture *pic);
412   void ProcessReturnPicture(CVdpauRenderPicture *pic);
413   void ProcessSyncPicture();
414   bool Init();
415   bool Uninit();
416   void Flush();
417   bool EnsureBufferPool();
418   void ReleaseBufferPool();
419   void PreCleanup();
420   void InitMixer();
421   bool CheckStatus(VdpStatus vdp_st, int line);
422   CEvent m_outMsgEvent;
423   CEvent *m_inMsgEvent;
424   int m_state;
425   bool m_bStateMachineSelfTrigger;
426   CDecoder &m_vdpau;
427 
428   // extended state variables for state machine
429   int m_extTimeout;
430   bool m_vdpError;
431   CVdpauConfig m_config;
432   std::shared_ptr<CVdpauBufferPool> m_bufferPool;
433   CMixer m_mixer;
434 };
435 
436 //-----------------------------------------------------------------------------
437 // VDPAU Video Surface states
438 //-----------------------------------------------------------------------------
439 
440 class CVideoSurfaces
441 {
442 public:
443   void AddSurface(VdpVideoSurface surf);
444   void ClearReference(VdpVideoSurface surf);
445   bool MarkRender(VdpVideoSurface surf);
446   void ClearRender(VdpVideoSurface surf);
447   bool IsValid(VdpVideoSurface surf);
448   VdpVideoSurface GetFree(VdpVideoSurface surf);
449   VdpVideoSurface RemoveNext(bool skiprender = false);
450   void Reset();
451   int Size();
452   bool HasRefs();
453 protected:
454   std::map<VdpVideoSurface, int> m_state;
455   std::list<VdpVideoSurface> m_freeSurfaces;
456   CCriticalSection m_section;
457 };
458 
459 //-----------------------------------------------------------------------------
460 // VDPAU decoder
461 //-----------------------------------------------------------------------------
462 
463 class CVDPAUContext
464 {
465 public:
466   static bool EnsureContext(CVDPAUContext **ctx);
467   void Release();
468   VDPAU_procs& GetProcs();
469   VdpDevice GetDevice();
470   bool Supports(VdpVideoMixerFeature feature);
471   VdpVideoMixerFeature* GetFeatures();
472   int GetFeatureCount();
473 private:
474   CVDPAUContext();
475   void Close();
476   bool LoadSymbols();
477   bool CreateContext();
478   void DestroyContext();
479   void QueryProcs();
480   void SpewHardwareAvailable();
481   static CVDPAUContext *m_context;
482   static CCriticalSection m_section;
483   static Display *m_display;
484   int m_refCount;
485   VdpVideoMixerFeature m_vdpFeatures[14];
486   int m_featureCount;
487   static void *m_dlHandle;
488   VdpDevice m_vdpDevice;
489   VDPAU_procs m_vdpProcs;
490   VdpStatus (*dl_vdp_device_create_x11)(Display* display, int screen, VdpDevice* device, VdpGetProcAddress **get_proc_address);
491 };
492 
493 /**
494  *  VDPAU main class
495  */
496 class CDecoder
497  : public IHardwareDecoder
498  , public IDispResource
499 {
500    friend class CVdpauBufferPool;
501 
502 public:
503 
504   struct Desc
505   {
506     const char *name;
507     uint32_t id;
508     uint32_t aux; /* optional extra parameter... */
509   };
510 
511   explicit CDecoder(CProcessInfo& processInfo);
512   ~CDecoder() override;
513 
514   bool Open (AVCodecContext* avctx, AVCodecContext* mainctx, const enum AVPixelFormat) override;
515   CDVDVideoCodec::VCReturn Decode (AVCodecContext* avctx, AVFrame* frame) override;
516   bool GetPicture(AVCodecContext* avctx, VideoPicture* picture) override;
517   void Reset() override;
518   virtual void Close();
519   long Release() override;
520   bool CanSkipDeint() override;
GetAllowedReferences()521   unsigned GetAllowedReferences() override { return 5; }
522 
523   CDVDVideoCodec::VCReturn Check(AVCodecContext* avctx) override;
Name()524   const std::string Name() override { return "vdpau"; }
525   void SetCodecControl(int flags) override;
526 
527   bool Supports(VdpVideoMixerFeature feature);
528   static bool IsVDPAUFormat(AVPixelFormat fmt);
529 
530   static void FFReleaseBuffer(void *opaque, uint8_t *data);
531   static int FFGetBuffer(AVCodecContext *avctx, AVFrame *pic, int flags);
532   static int Render(struct AVCodecContext *s, struct AVFrame *src,
533                     const VdpPictureInfo *info, uint32_t buffers_used,
534                     const VdpBitstreamBuffer *buffers);
535 
536   void OnLostDisplay() override;
537   void OnResetDisplay() override;
538 
539   static IHardwareDecoder* Create(CDVDStreamInfo &hint, CProcessInfo &processInfo, AVPixelFormat fmt);
540   static void Register();
541 
542 protected:
543   void SetWidthHeight(int width, int height);
544   bool ConfigVDPAU(AVCodecContext *avctx, int ref_frames);
545   bool CheckStatus(VdpStatus vdp_st, int line);
546   void FiniVDPAUOutput();
547   void ReturnRenderPicture(CVdpauRenderPicture *renderPic);
548   long ReleasePicReference();
549 
550   static void ReadFormatOf( AVCodecID codec
551                           , VdpDecoderProfile &decoder_profile
552                           , VdpChromaType &chroma_type);
553 
554   // OnLostDevice triggers transition from all states to LOST
555   // internal errors trigger transition from OPEN to RESET
556   // OnResetDevice triggers transition from LOST to RESET
557   enum EDisplayState
558   { VDPAU_OPEN
559   , VDPAU_RESET
560   , VDPAU_LOST
561   , VDPAU_ERROR
562   } m_DisplayState;
563   CCriticalSection m_DecoderSection;
564   CEvent m_DisplayEvent;
565   int m_ErrorCount;
566 
567   bool m_vdpauConfigured;
568   CVdpauConfig m_vdpauConfig;
569   CVideoSurfaces m_videoSurfaces;
570   AVVDPAUContext m_hwContext;
571   AVCodecContext* m_avctx = nullptr;
572 
573   COutput m_vdpauOutput;
574   CVdpauBufferStats m_bufferStats;
575   CEvent m_inMsgEvent;
576   CVdpauRenderPicture *m_presentPicture = nullptr;
577 
578   int m_codecControl;
579   CProcessInfo& m_processInfo;
580 
581   static bool m_capGeneral;
582 };
583 
584 }
585