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