1 /*
2  *  Copyright (C) 2005-2018 Team Kodi
3  *  This file is part of Kodi - https://kodi.tv
4  *
5  *  SPDX-License-Identifier: LGPL-2.1-or-later
6  *  See LICENSES/README.md for more information.
7  */
8 
9 #include "VAAPI.h"
10 
11 #include "DVDVideoCodec.h"
12 #include "ServiceBroker.h"
13 #include "cores/VideoPlayer/DVDCodecs/DVDCodecUtils.h"
14 #include "cores/VideoPlayer/DVDCodecs/DVDFactoryCodec.h"
15 #include "cores/VideoPlayer/Interface/TimingConstants.h"
16 #include "cores/VideoPlayer/Process/ProcessInfo.h"
17 #include "settings/AdvancedSettings.h"
18 #include "settings/Settings.h"
19 #include "settings/SettingsComponent.h"
20 #include "settings/lib/Setting.h"
21 #include "threads/SingleLock.h"
22 #include "utils/MemUtils.h"
23 #include "utils/StringUtils.h"
24 #include "utils/XTimeUtils.h"
25 #include "utils/log.h"
26 #include "windowing/GraphicContext.h"
27 
28 #include <drm_fourcc.h>
29 #include <va/va_drm.h>
30 #include <va/va_drmcommon.h>
31 
32 extern "C" {
33 #include <libavutil/avutil.h>
34 #include <libavutil/hwcontext.h>
35 #include <libavutil/hwcontext_vaapi.h>
36 #include <libavutil/opt.h>
37 #include <libavfilter/buffersink.h>
38 #include <libavfilter/buffersrc.h>
39 }
40 
41 #include "system_egl.h"
42 
43 #include <EGL/eglext.h>
44 #include <va/va_vpp.h>
45 #include <xf86drm.h>
46 
47 #if VA_CHECK_VERSION(1, 0, 0)
48 # include <va/va_str.h>
49 #endif
50 
51 using namespace VAAPI;
52 #define NUM_RENDER_PICS 7
53 
54 constexpr auto SETTING_VIDEOPLAYER_USEVAAPI = "videoplayer.usevaapi";
55 constexpr auto SETTING_VIDEOPLAYER_USEVAAPIHEVC = "videoplayer.usevaapihevc";
56 constexpr auto SETTING_VIDEOPLAYER_USEVAAPIMPEG2 = "videoplayer.usevaapimpeg2";
57 constexpr auto SETTING_VIDEOPLAYER_USEVAAPIMPEG4 = "videoplayer.usevaapimpeg4";
58 constexpr auto SETTING_VIDEOPLAYER_USEVAAPIVC1 = "videoplayer.usevaapivc1";
59 constexpr auto SETTING_VIDEOPLAYER_USEVAAPIVP8 = "videoplayer.usevaapivp8";
60 constexpr auto SETTING_VIDEOPLAYER_USEVAAPIVP9 = "videoplayer.usevaapivp9";
61 constexpr auto SETTING_VIDEOPLAYER_PREFERVAAPIRENDER = "videoplayer.prefervaapirender";
62 
VaErrorCallback(void * user_context,const char * message)63 void VAAPI::VaErrorCallback(void *user_context, const char *message)
64 {
65   std::string str{message};
66   CLog::Log(LOGERROR, "libva error: {}", StringUtils::TrimRight(str));
67 }
68 
VaInfoCallback(void * user_context,const char * message)69 void VAAPI::VaInfoCallback(void *user_context, const char *message)
70 {
71   std::string str{message};
72   CLog::Log(LOGDEBUG, "libva info: {}", StringUtils::TrimRight(str));
73 }
74 
75 //-----------------------------------------------------------------------------
76 //-----------------------------------------------------------------------------
77 
78 CVAAPIContext *CVAAPIContext::m_context = 0;
79 CCriticalSection CVAAPIContext::m_section;
80 
CVAAPIContext()81 CVAAPIContext::CVAAPIContext()
82 {
83   m_context = 0;
84   m_refCount = 0;
85   m_profiles = NULL;
86   m_display = NULL;
87 }
88 
Release(CDecoder * decoder)89 void CVAAPIContext::Release(CDecoder *decoder)
90 {
91   CSingleLock lock(m_section);
92 
93   auto it = find(m_decoders.begin(), m_decoders.end(), decoder);
94   if (it != m_decoders.end())
95     m_decoders.erase(it);
96 
97   m_refCount--;
98   if (m_refCount <= 0)
99   {
100     Close();
101     delete this;
102     m_context = 0;
103   }
104 }
105 
Close()106 void CVAAPIContext::Close()
107 {
108   CLog::Log(LOGINFO, "VAAPI::Close - closing decoder context");
109   if (m_renderNodeFD >= 0)
110   {
111     close(m_renderNodeFD);
112   }
113 
114   DestroyContext();
115 }
116 
EnsureContext(CVAAPIContext ** ctx,CDecoder * decoder)117 bool CVAAPIContext::EnsureContext(CVAAPIContext **ctx, CDecoder *decoder)
118 {
119   CSingleLock lock(m_section);
120 
121   if (m_context)
122   {
123     m_context->m_refCount++;
124     *ctx = m_context;
125     if (!m_context->IsValidDecoder(decoder))
126       m_context->m_decoders.push_back(decoder);
127     return true;
128   }
129 
130   m_context = new CVAAPIContext();
131   *ctx = m_context;
132   {
133     CSingleLock gLock(CServiceBroker::GetWinSystem()->GetGfxContext());
134     if (!m_context->CreateContext())
135     {
136       delete m_context;
137       m_context = 0;
138       *ctx = NULL;
139       return false;
140     }
141   }
142 
143   m_context->m_refCount++;
144 
145   if (!m_context->IsValidDecoder(decoder))
146     m_context->m_decoders.push_back(decoder);
147   *ctx = m_context;
148   return true;
149 }
150 
SetValidDRMVaDisplayFromRenderNode()151 void CVAAPIContext::SetValidDRMVaDisplayFromRenderNode()
152 {
153   int const buf_size{128};
154   char name[buf_size];
155   int fd{-1};
156 
157   // 128 is the start of the NUM in renderD<NUM>
158   for (int i = 128; i < (128 + 16); i++)
159   {
160     snprintf(name, buf_size, "/dev/dri/renderD%u", i);
161 
162     fd = open(name, O_RDWR);
163 
164     if (fd < 0)
165     {
166       continue;
167     }
168 
169     auto display = vaGetDisplayDRM(fd);
170 
171     if (display != nullptr)
172     {
173       m_renderNodeFD = fd;
174       m_display = display;
175       return;
176     }
177     close(fd);
178   }
179 
180   CLog::Log(LOGERROR, "Failed to find any open render nodes in /dev/dri/renderD<num>");
181 }
182 
SetVaDisplayForSystem()183 void CVAAPIContext::SetVaDisplayForSystem()
184 {
185   m_display = CDecoder::m_pWinSystem->GetVADisplay();
186 
187   // Fallback to DRM
188   if (!m_display)
189   {
190     // Render nodes depends on kernel >= 3.15
191     SetValidDRMVaDisplayFromRenderNode();
192   }
193 }
194 
CreateContext()195 bool CVAAPIContext::CreateContext()
196 {
197   SetVaDisplayForSystem();
198 
199   if (m_display == nullptr)
200   {
201     CLog::Log(LOGERROR, "Failed to find any VaDisplays for this system");
202     return false;
203   }
204 
205 #if VA_CHECK_VERSION(1, 0, 0)
206   vaSetErrorCallback(m_display, VaErrorCallback, nullptr);
207   vaSetInfoCallback(m_display, VaInfoCallback, nullptr);
208 #endif
209 
210   int major_version, minor_version;
211   if (!CheckSuccess(vaInitialize(m_display, &major_version, &minor_version), "vaInitialize"))
212   {
213     vaTerminate(m_display);
214     m_display = NULL;
215     return false;
216   }
217 
218   CLog::Log(LOGDEBUG, LOGVIDEO, "VAAPI - initialize version %d.%d", major_version, minor_version);
219   CLog::Log(LOGDEBUG, LOGVIDEO, "VAAPI - driver in use: %s", vaQueryVendorString(m_display));
220 
221   QueryCaps();
222   if (!m_profileCount)
223     return false;
224 
225   return true;
226 }
227 
DestroyContext()228 void CVAAPIContext::DestroyContext()
229 {
230   delete[] m_profiles;
231   if (m_display)
232   {
233     if (CheckSuccess(vaTerminate(m_display), "vaTerminate"))
234     {
235       m_display = NULL;
236     }
237     else
238     {
239 #if VA_CHECK_VERSION(1, 0, 0)
240       vaSetErrorCallback(m_display, nullptr, nullptr);
241       vaSetInfoCallback(m_display, nullptr, nullptr);
242 #endif
243     }
244   }
245 }
246 
QueryCaps()247 void CVAAPIContext::QueryCaps()
248 {
249   m_profileCount = 0;
250 
251   int max_profiles = vaMaxNumProfiles(m_display);
252   m_profiles = new VAProfile[max_profiles];
253 
254   if (!CheckSuccess(vaQueryConfigProfiles(m_display, m_profiles, &m_profileCount), "vaQueryConfigProfiles"))
255     return;
256 
257   for(int i = 0; i < m_profileCount; i++)
258   {
259 #if VA_CHECK_VERSION(1, 0, 0)
260     CLog::Log(LOGDEBUG, LOGVIDEO, "VAAPI - profile %s", vaProfileStr(m_profiles[i]));
261 #else
262     CLog::Log(LOGDEBUG, LOGVIDEO, "VAAPI - profile %d", m_profiles[i]);
263 #endif
264   }
265 }
266 
GetAttrib(VAProfile profile)267 VAConfigAttrib CVAAPIContext::GetAttrib(VAProfile profile)
268 {
269   CSingleLock lock(m_section);
270 
271   VAConfigAttrib attrib;
272   attrib.type = VAConfigAttribRTFormat;
273   CheckSuccess(vaGetConfigAttributes(m_display, profile, VAEntrypointVLD, &attrib, 1), "vaGetConfigAttributes");
274 
275   return attrib;
276 }
277 
SupportsProfile(VAProfile profile)278 bool CVAAPIContext::SupportsProfile(VAProfile profile)
279 {
280   CSingleLock lock(m_section);
281 
282   for (int i=0; i<m_profileCount; i++)
283   {
284     if (m_profiles[i] == profile)
285       return true;
286   }
287   return false;
288 }
289 
CreateConfig(VAProfile profile,VAConfigAttrib attrib)290 VAConfigID CVAAPIContext::CreateConfig(VAProfile profile, VAConfigAttrib attrib)
291 {
292   CSingleLock lock(m_section);
293 
294   VAConfigID config = VA_INVALID_ID;
295   CheckSuccess(vaCreateConfig(m_display, profile, VAEntrypointVLD, &attrib, 1, &config), "vaCreateConfig");
296 
297   return config;
298 }
299 
CheckSuccess(VAStatus status,const std::string & function)300 bool CVAAPIContext::CheckSuccess(VAStatus status, const std::string& function)
301 {
302   if (status != VA_STATUS_SUCCESS)
303   {
304     CLog::Log(LOGERROR, "VAAPI/context {} error: {} ({})", function, vaErrorStr(status), status);
305     return false;
306   }
307   return true;
308 }
309 
GetDisplay()310 VADisplay CVAAPIContext::GetDisplay()
311 {
312   return m_display;
313 }
314 
IsValidDecoder(CDecoder * decoder)315 bool CVAAPIContext::IsValidDecoder(CDecoder *decoder)
316 {
317   auto it = find(m_decoders.begin(), m_decoders.end(), decoder);
318   if (it != m_decoders.end())
319     return true;
320 
321   return false;
322 }
323 
FFReleaseBuffer(void * opaque,uint8_t * data)324 void CVAAPIContext::FFReleaseBuffer(void *opaque, uint8_t *data)
325 {
326   CDecoder *va = static_cast<CDecoder*>(opaque);
327   if (m_context && m_context->IsValidDecoder(va))
328   {
329     va->FFReleaseBuffer(data);
330   }
331 }
332 
333 //-----------------------------------------------------------------------------
334 // VAAPI Video Surface states
335 //-----------------------------------------------------------------------------
336 
337 #define SURFACE_USED_FOR_REFERENCE 0x01
338 #define SURFACE_USED_FOR_RENDER    0x02
339 
AddSurface(VASurfaceID surf)340 void CVideoSurfaces::AddSurface(VASurfaceID surf)
341 {
342   CSingleLock lock(m_section);
343   m_state[surf] = 0;
344   m_freeSurfaces.push_back(surf);
345 }
346 
ClearReference(VASurfaceID surf)347 void CVideoSurfaces::ClearReference(VASurfaceID surf)
348 {
349   CSingleLock lock(m_section);
350   if (m_state.find(surf) == m_state.end())
351   {
352     CLog::Log(LOGWARNING, "CVideoSurfaces::ClearReference - surface invalid");
353     return;
354   }
355   m_state[surf] &= ~SURFACE_USED_FOR_REFERENCE;
356   if (m_state[surf] == 0)
357   {
358     m_freeSurfaces.push_back(surf);
359   }
360 }
361 
MarkRender(VASurfaceID surf)362 bool CVideoSurfaces::MarkRender(VASurfaceID surf)
363 {
364   CSingleLock lock(m_section);
365   if (m_state.find(surf) == m_state.end())
366   {
367     CLog::Log(LOGWARNING, "CVideoSurfaces::MarkRender - surface invalid");
368     return false;
369   }
370   auto it = std::find(m_freeSurfaces.begin(), m_freeSurfaces.end(), surf);
371   if (it != m_freeSurfaces.end())
372   {
373     m_freeSurfaces.erase(it);
374   }
375   m_state[surf] |= SURFACE_USED_FOR_RENDER;
376   return true;
377 }
378 
ClearRender(VASurfaceID surf)379 void CVideoSurfaces::ClearRender(VASurfaceID surf)
380 {
381   CSingleLock lock(m_section);
382   if (m_state.find(surf) == m_state.end())
383   {
384     CLog::Log(LOGWARNING, "CVideoSurfaces::ClearRender - surface invalid");
385     return;
386   }
387   m_state[surf] &= ~SURFACE_USED_FOR_RENDER;
388   if (m_state[surf] == 0)
389   {
390     m_freeSurfaces.push_back(surf);
391   }
392 }
393 
IsValid(VASurfaceID surf)394 bool CVideoSurfaces::IsValid(VASurfaceID surf)
395 {
396   CSingleLock lock(m_section);
397   if (m_state.find(surf) != m_state.end())
398     return true;
399   else
400     return false;
401 }
402 
GetFree(VASurfaceID surf)403 VASurfaceID CVideoSurfaces::GetFree(VASurfaceID surf)
404 {
405   CSingleLock lock(m_section);
406   if (m_state.find(surf) != m_state.end())
407   {
408     auto it = std::find(m_freeSurfaces.begin(), m_freeSurfaces.end(), surf);
409     if (it == m_freeSurfaces.end())
410     {
411       CLog::Log(LOGWARNING, "CVideoSurfaces::GetFree - surface not free");
412     }
413     else
414     {
415       m_freeSurfaces.erase(it);
416       m_state[surf] = SURFACE_USED_FOR_REFERENCE;
417       return surf;
418     }
419   }
420 
421   if (!m_freeSurfaces.empty())
422   {
423     VASurfaceID freeSurf = m_freeSurfaces.front();
424     m_freeSurfaces.pop_front();
425     m_state[freeSurf] = SURFACE_USED_FOR_REFERENCE;
426     return freeSurf;
427   }
428 
429   return VA_INVALID_SURFACE;
430 }
431 
GetAtIndex(int idx)432 VASurfaceID CVideoSurfaces::GetAtIndex(int idx)
433 {
434   if ((size_t) idx >= m_state.size())
435     return VA_INVALID_SURFACE;
436 
437   auto it = m_state.begin();
438   for(int i = 0; i < idx; i++)
439     ++it;
440   return it->first;
441 }
442 
RemoveNext(bool skiprender)443 VASurfaceID CVideoSurfaces::RemoveNext(bool skiprender)
444 {
445   CSingleLock lock(m_section);
446   VASurfaceID surf;
447   for(auto it = m_state.begin(); it != m_state.end(); ++it)
448   {
449     if (skiprender && it->second & SURFACE_USED_FOR_RENDER)
450       continue;
451     surf = it->first;
452     m_state.erase(surf);
453 
454     auto it2 = std::find(m_freeSurfaces.begin(), m_freeSurfaces.end(), surf);
455     if (it2 != m_freeSurfaces.end())
456       m_freeSurfaces.erase(it2);
457     return surf;
458   }
459   return VA_INVALID_SURFACE;
460 }
461 
Reset()462 void CVideoSurfaces::Reset()
463 {
464   CSingleLock lock(m_section);
465   m_freeSurfaces.clear();
466   m_state.clear();
467 }
468 
Size()469 int CVideoSurfaces::Size()
470 {
471   CSingleLock lock(m_section);
472   return m_state.size();
473 }
474 
HasFree()475 bool CVideoSurfaces::HasFree()
476 {
477   CSingleLock lock(m_section);
478   return !m_freeSurfaces.empty();
479 }
480 
NumFree()481 int CVideoSurfaces::NumFree()
482 {
483   CSingleLock lock(m_section);
484   return m_freeSurfaces.size();
485 }
486 
HasRefs()487 bool CVideoSurfaces::HasRefs()
488 {
489   CSingleLock lock(m_section);
490   for (const auto &i : m_state)
491   {
492     if (i.second & SURFACE_USED_FOR_REFERENCE)
493     return true;
494   }
495   return false;
496 }
497 
498 //-----------------------------------------------------------------------------
499 // VAAPI
500 //-----------------------------------------------------------------------------
501 
502 bool CDecoder::m_capGeneral = false;
503 bool CDecoder::m_capDeepColor = false;
504 IVaapiWinSystem* CDecoder::m_pWinSystem = nullptr;
505 
CDecoder(CProcessInfo & processInfo)506 CDecoder::CDecoder(CProcessInfo& processInfo) :
507   m_vaapiOutput(*this, &m_inMsgEvent),
508   m_processInfo(processInfo)
509 {
510   m_vaapiConfig.videoSurfaces = &m_videoSurfaces;
511 
512   m_vaapiConfigured = false;
513   m_DisplayState = VAAPI_OPEN;
514   m_vaapiConfig.context = 0;
515   m_vaapiConfig.configId = VA_INVALID_ID;
516   m_vaapiConfig.processInfo = &m_processInfo;
517   m_avctx = nullptr;
518   m_getBufferError = 0;
519 }
520 
~CDecoder()521 CDecoder::~CDecoder()
522 {
523   Close();
524 }
525 
Open(AVCodecContext * avctx,AVCodecContext * mainctx,const enum AVPixelFormat fmt)526 bool CDecoder::Open(AVCodecContext* avctx, AVCodecContext* mainctx, const enum AVPixelFormat fmt)
527 {
528   if (!m_capGeneral)
529     return false;
530 
531   // check if user wants to decode this format with VAAPI
532   std::map<AVCodecID, std::string> settings_map = {
533     { AV_CODEC_ID_H263, SETTING_VIDEOPLAYER_USEVAAPIMPEG4 },
534     { AV_CODEC_ID_MPEG4, SETTING_VIDEOPLAYER_USEVAAPIMPEG4 },
535     { AV_CODEC_ID_WMV3, SETTING_VIDEOPLAYER_USEVAAPIVC1 },
536     { AV_CODEC_ID_VC1, SETTING_VIDEOPLAYER_USEVAAPIVC1 },
537     { AV_CODEC_ID_MPEG2VIDEO, SETTING_VIDEOPLAYER_USEVAAPIMPEG2 },
538     { AV_CODEC_ID_VP8, SETTING_VIDEOPLAYER_USEVAAPIVP8 },
539     { AV_CODEC_ID_VP9, SETTING_VIDEOPLAYER_USEVAAPIVP9 },
540     { AV_CODEC_ID_HEVC, SETTING_VIDEOPLAYER_USEVAAPIHEVC },
541   };
542 
543   auto entry = settings_map.find(avctx->codec_id);
544   if (entry != settings_map.end())
545   {
546     auto settingsComponent = CServiceBroker::GetSettingsComponent();
547     if (!settingsComponent)
548       return false;
549 
550     auto settings = settingsComponent->GetSettings();
551     if (!settings)
552       return false;
553 
554     auto setting = settings->GetSetting(entry->second);
555     if (!setting)
556     {
557       CLog::Log(LOGERROR, "Failed to load setting for: {}", entry->second);
558       return false;
559     }
560 
561     bool enabled = settings->GetBool(entry->second) && setting->IsVisible();
562     if (!enabled)
563       return false;
564   }
565 
566   CLog::Log(LOGDEBUG, LOGVIDEO, "VAAPI - open decoder");
567 
568   if (!CVAAPIContext::EnsureContext(&m_vaapiConfig.context, this))
569     return false;
570 
571   if(avctx->coded_width  == 0 ||
572      avctx->coded_height == 0)
573   {
574     CLog::Log(LOGWARNING,"VAAPI::Open: no width/height available, can't init");
575     return false;
576   }
577 
578   m_vaapiConfig.driverIsMesa = StringUtils::StartsWith(vaQueryVendorString(m_vaapiConfig.context->GetDisplay()), "Mesa");
579   m_vaapiConfig.vidWidth = avctx->width;
580   m_vaapiConfig.vidHeight = avctx->height;
581   m_vaapiConfig.outWidth = avctx->width;
582   m_vaapiConfig.outHeight = avctx->height;
583   m_vaapiConfig.surfaceWidth = avctx->coded_width;
584   m_vaapiConfig.surfaceHeight = avctx->coded_height;
585   m_vaapiConfig.aspect = avctx->sample_aspect_ratio;
586   m_DisplayState = VAAPI_OPEN;
587   m_vaapiConfigured = false;
588   m_presentPicture = nullptr;
589   m_getBufferError = 0;
590 
591   VAProfile profile;
592   switch (avctx->codec_id)
593   {
594     case AV_CODEC_ID_MPEG2VIDEO:
595       profile = VAProfileMPEG2Main;
596       if (!m_vaapiConfig.context->SupportsProfile(profile))
597         return false;
598       break;
599     case AV_CODEC_ID_MPEG4:
600     case AV_CODEC_ID_H263:
601       profile = VAProfileMPEG4AdvancedSimple;
602       if (!m_vaapiConfig.context->SupportsProfile(profile))
603         return false;
604       break;
605     case AV_CODEC_ID_H264:
606     {
607       if (avctx->profile == FF_PROFILE_H264_CONSTRAINED_BASELINE)
608       {
609         profile = VAProfileH264ConstrainedBaseline;
610         if (!m_vaapiConfig.context->SupportsProfile(profile))
611           return false;
612       }
613       else
614       {
615         if(avctx->profile == FF_PROFILE_H264_MAIN)
616         {
617           profile = VAProfileH264Main;
618           if (m_vaapiConfig.context->SupportsProfile(profile))
619             break;
620         }
621         profile = VAProfileH264High;
622         if (!m_vaapiConfig.context->SupportsProfile(profile))
623           return false;
624       }
625       break;
626     }
627     case AV_CODEC_ID_HEVC:
628     {
629       if (avctx->profile == FF_PROFILE_HEVC_MAIN_10)
630       {
631         if (!m_capDeepColor)
632           return false;
633 
634         profile = VAProfileHEVCMain10;
635       }
636       else if (avctx->profile == FF_PROFILE_HEVC_MAIN)
637         profile = VAProfileHEVCMain;
638       else
639         profile = VAProfileNone;
640       if (!m_vaapiConfig.context->SupportsProfile(profile))
641         return false;
642       break;
643     }
644     case AV_CODEC_ID_VP8:
645     {
646       profile = VAProfileVP8Version0_3;
647       if (!m_vaapiConfig.context->SupportsProfile(profile))
648         return false;
649       break;
650     }
651     case AV_CODEC_ID_VP9:
652     {
653       if (avctx->profile == FF_PROFILE_VP9_0)
654         profile = VAProfileVP9Profile0;
655       else if (avctx->profile == FF_PROFILE_VP9_2)
656         profile = VAProfileVP9Profile2;
657       else
658         profile = VAProfileNone;
659       if (!m_vaapiConfig.context->SupportsProfile(profile))
660         return false;
661       break;
662     }
663     case AV_CODEC_ID_WMV3:
664       profile = VAProfileVC1Main;
665       if (!m_vaapiConfig.context->SupportsProfile(profile))
666         return false;
667       break;
668     case AV_CODEC_ID_VC1:
669       profile = VAProfileVC1Advanced;
670       if (!m_vaapiConfig.context->SupportsProfile(profile))
671         return false;
672       break;
673     default:
674       return false;
675   }
676 
677   m_vaapiConfig.profile = profile;
678   m_vaapiConfig.attrib = m_vaapiConfig.context->GetAttrib(profile);
679   if ((m_vaapiConfig.attrib.value & (VA_RT_FORMAT_YUV420 | VA_RT_FORMAT_YUV420_10BPP)) == 0)
680   {
681     CLog::Log(LOGERROR, "VAAPI - invalid yuv format %x", m_vaapiConfig.attrib.value);
682     return false;
683   }
684 
685   if (avctx->codec_id == AV_CODEC_ID_H264)
686   {
687     m_vaapiConfig.maxReferences = avctx->refs;
688     if (m_vaapiConfig.maxReferences > 16)
689       m_vaapiConfig.maxReferences = 16;
690     if (m_vaapiConfig.maxReferences < 5)
691       m_vaapiConfig.maxReferences = 5;
692   }
693   else if (avctx->codec_id == AV_CODEC_ID_HEVC)
694     m_vaapiConfig.maxReferences = 16;
695   else if (avctx->codec_id == AV_CODEC_ID_VP9)
696     m_vaapiConfig.maxReferences = 8;
697   else
698     m_vaapiConfig.maxReferences = 2;
699 
700   // add an extra surface for safety, some faulty material
701   // make ffmpeg require more buffers
702   m_vaapiConfig.maxReferences += 6;
703 
704   if (!ConfigVAAPI())
705   {
706     return false;
707   }
708 
709   AVBufferRef *deviceRef =  av_hwdevice_ctx_alloc(AV_HWDEVICE_TYPE_VAAPI);
710   AVHWDeviceContext *deviceCtx = (AVHWDeviceContext*)deviceRef->data;
711   AVVAAPIDeviceContext *vaapiDeviceCtx = (AVVAAPIDeviceContext*)deviceCtx->hwctx;
712   AVBufferRef *framesRef = av_hwframe_ctx_alloc(deviceRef);
713   AVHWFramesContext *framesCtx = (AVHWFramesContext*)framesRef->data;
714   AVVAAPIFramesContext *vaapiFramesCtx = (AVVAAPIFramesContext*)framesCtx->hwctx;
715 
716   vaapiDeviceCtx->display = m_vaapiConfig.dpy;
717   vaapiDeviceCtx->driver_quirks = AV_VAAPI_DRIVER_QUIRK_RENDER_PARAM_BUFFERS;
718   vaapiFramesCtx->nb_attributes = 0;
719   vaapiFramesCtx->nb_surfaces = m_videoSurfaces.Size();
720   VASurfaceID *surfaceIds = (VASurfaceID*)av_malloc(vaapiFramesCtx->nb_surfaces *  sizeof(VASurfaceID));
721   for (int i=0; i<vaapiFramesCtx->nb_surfaces; ++i)
722     surfaceIds[i] = m_videoSurfaces.GetAtIndex(i);
723   vaapiFramesCtx->surface_ids = surfaceIds;
724   framesCtx->format = AV_PIX_FMT_VAAPI;
725   framesCtx->width  = avctx->coded_width;
726   framesCtx->height = avctx->coded_height;
727 
728   avctx->hw_frames_ctx = framesRef;
729   avctx->get_buffer2 = CDecoder::FFGetBuffer;
730   avctx->slice_flags = SLICE_FLAG_CODED_ORDER|SLICE_FLAG_ALLOW_FIELD;
731 
732   m_avctx = mainctx;
733   return true;
734 }
735 
Close()736 void CDecoder::Close()
737 {
738   CLog::Log(LOGINFO, "VAAPI::%s", __FUNCTION__);
739 
740   CSingleLock lock(m_DecoderSection);
741 
742   FiniVAAPIOutput();
743 
744   if (m_vaapiConfig.context)
745     m_vaapiConfig.context->Release(this);
746   m_vaapiConfig.context = 0;
747 }
748 
Release()749 long CDecoder::Release()
750 {
751   // if ffmpeg holds any references, flush buffers
752   if (m_avctx && m_videoSurfaces.HasRefs())
753   {
754     avcodec_flush_buffers(m_avctx);
755   }
756 
757   if (m_presentPicture)
758   {
759     m_presentPicture->Release();
760     m_presentPicture = nullptr;
761   }
762   // check if we should do some pre-cleanup here
763   // a second decoder might need resources
764   if (m_vaapiConfigured == true)
765   {
766     CSingleLock lock(m_DecoderSection);
767     CLog::Log(LOGDEBUG, LOGVIDEO, "VAAPI::Release pre-cleanup");
768 
769     CSingleLock lock1(CServiceBroker::GetWinSystem()->GetGfxContext());
770     Message *reply;
771     if (m_vaapiOutput.m_controlPort.SendOutMessageSync(COutputControlProtocol::PRECLEANUP,
772                                                    &reply,
773                                                    2000))
774     {
775       bool success = reply->signal == COutputControlProtocol::ACC ? true : false;
776       reply->Release();
777       if (!success)
778       {
779         CLog::Log(LOGERROR, "VAAPI::%s - pre-cleanup returned error", __FUNCTION__);
780         m_DisplayState = VAAPI_ERROR;
781       }
782     }
783     else
784     {
785       CLog::Log(LOGERROR, "VAAPI::%s - pre-cleanup timed out", __FUNCTION__);
786       m_DisplayState = VAAPI_ERROR;
787     }
788 
789     VASurfaceID surf;
790     while((surf = m_videoSurfaces.RemoveNext(true)) != VA_INVALID_SURFACE)
791     {
792       CheckSuccess(vaDestroySurfaces(m_vaapiConfig.dpy, &surf, 1), "vaDestroySurfaces");
793     }
794   }
795   return IHardwareDecoder::Release();
796 }
797 
ReleasePicReference()798 long CDecoder::ReleasePicReference()
799 {
800   return IHardwareDecoder::Release();
801 }
802 
FFGetBuffer(AVCodecContext * avctx,AVFrame * pic,int flags)803 int CDecoder::FFGetBuffer(AVCodecContext *avctx, AVFrame *pic, int flags)
804 {
805   ICallbackHWAccel* cb = static_cast<ICallbackHWAccel*>(avctx->opaque);
806   CDecoder* va = static_cast<CDecoder*>(cb->GetHWAccel());
807 
808   // while we are waiting to recover we can't do anything
809   CSingleLock lock(va->m_DecoderSection);
810 
811   if(va->m_DisplayState != VAAPI_OPEN)
812   {
813     CLog::Log(LOGWARNING, "VAAPI::FFGetBuffer - returning due to awaiting recovery");
814     return -1;
815   }
816 
817   VASurfaceID surf = (VASurfaceID)(uintptr_t)pic->data[3];
818   surf = va->m_videoSurfaces.GetFree(surf != 0 ? surf : VA_INVALID_SURFACE);
819 
820   if (surf == VA_INVALID_SURFACE)
821   {
822     uint16_t decoded, processed, render;
823     bool vpp;
824     va->m_bufferStats.Get(decoded, processed, render, vpp);
825     CLog::Log(LOGWARNING, "VAAPI::FFGetBuffer - no surface available - dec: %d, render: %d",
826                          decoded, render);
827     va->m_getBufferError++;
828     return -1;
829   }
830 
831   va->m_getBufferError = 0;
832 
833   pic->data[1] = pic->data[2] = NULL;
834   pic->data[0] = (uint8_t*)(uintptr_t)surf;
835   pic->data[3] = (uint8_t*)(uintptr_t)surf;
836   pic->linesize[0] = pic->linesize[1] =  pic->linesize[2] = 0;
837   AVBufferRef *buffer = av_buffer_create(pic->data[3], 0, CVAAPIContext::FFReleaseBuffer, va, 0);
838   if (!buffer)
839   {
840     CLog::Log(LOGERROR, "VAAPI::%s - error creating buffer", __FUNCTION__);
841     return -1;
842   }
843   pic->buf[0] = buffer;
844 
845   pic->reordered_opaque = avctx->reordered_opaque;
846   va->Acquire();
847   return 0;
848 }
849 
FFReleaseBuffer(uint8_t * data)850 void CDecoder::FFReleaseBuffer(uint8_t *data)
851 {
852   {
853     VASurfaceID surf;
854 
855     CSingleLock lock(m_DecoderSection);
856 
857     surf = (VASurfaceID)(uintptr_t)data;
858     m_videoSurfaces.ClearReference(surf);
859   }
860 
861   IHardwareDecoder::Release();
862 }
863 
SetCodecControl(int flags)864 void CDecoder::SetCodecControl(int flags)
865 {
866   m_codecControl = flags & (DVD_CODEC_CTRL_DRAIN | DVD_CODEC_CTRL_HURRY);
867 }
868 
Decode(AVCodecContext * avctx,AVFrame * pFrame)869 CDVDVideoCodec::VCReturn CDecoder::Decode(AVCodecContext* avctx, AVFrame* pFrame)
870 {
871   CDVDVideoCodec::VCReturn result = Check(avctx);
872   if (result != CDVDVideoCodec::VC_NOBUFFER && result != CDVDVideoCodec::VC_NONE)
873     return result;
874 
875   CSingleLock lock(m_DecoderSection);
876 
877   if (!m_vaapiConfigured)
878     return CDVDVideoCodec::VC_ERROR;
879 
880   if (pFrame)
881   { // we have a new frame from decoder
882 
883     VASurfaceID surf = (VASurfaceID)(uintptr_t)pFrame->data[3];
884     // ffmpeg vc-1 decoder does not flush, make sure the data buffer is still valid
885     if (!m_videoSurfaces.IsValid(surf))
886     {
887       CLog::Log(LOGWARNING, "VAAPI::Decode - ignoring invalid buffer");
888       return CDVDVideoCodec::VC_BUFFER;
889     }
890     m_videoSurfaces.MarkRender(surf);
891 
892     // send frame to output for processing
893     CVaapiDecodedPicture *pic = new CVaapiDecodedPicture();
894     static_cast<ICallbackHWAccel*>(avctx->opaque)->GetPictureCommon(&(pic->DVDPic));
895     m_codecControl = pic->DVDPic.iFlags & (DVD_CODEC_CTRL_HURRY | DVD_CODEC_CTRL_NO_POSTPROC);
896     pic->videoSurface = surf;
897     m_bufferStats.IncDecoded();
898     CPayloadWrap<CVaapiDecodedPicture> *payload = new CPayloadWrap<CVaapiDecodedPicture>(pic);
899     m_vaapiOutput.m_dataPort.SendOutMessage(COutputDataProtocol::NEWFRAME, payload);
900   }
901 
902   uint16_t decoded, processed, render;
903   bool vpp;
904   Message *msg;
905   while (m_vaapiOutput.m_controlPort.ReceiveInMessage(&msg))
906   {
907     if (msg->signal == COutputControlProtocol::ERROR)
908     {
909       m_DisplayState = VAAPI_ERROR;
910       msg->Release();
911       return CDVDVideoCodec::VC_ERROR;
912     }
913     msg->Release();
914   }
915 
916   bool drain = (m_codecControl & DVD_CODEC_CTRL_DRAIN);
917 
918   m_bufferStats.Get(decoded, processed, render, vpp);
919   // if all pics are drained, break the loop by setting VC_EOF
920   if (drain && decoded <= 0 && processed <= 0 && render <= 0)
921     return CDVDVideoCodec::VC_EOF;
922 
923   while (true)
924   {
925     // first fill the buffers to keep vaapi busy
926     if (!drain && decoded < 2 && processed < 3 && m_videoSurfaces.HasFree())
927     {
928       return CDVDVideoCodec::VC_BUFFER;
929     }
930     else if (m_vaapiOutput.m_dataPort.ReceiveInMessage(&msg))
931     {
932       if (msg->signal == COutputDataProtocol::PICTURE)
933       {
934         if (m_presentPicture)
935         {
936           m_presentPicture->Release();
937           m_presentPicture = nullptr;
938         }
939 
940         m_presentPicture = *(CVaapiRenderPicture**)msg->data;
941         m_bufferStats.DecRender();
942         m_bufferStats.SetParams(0, m_codecControl);
943         msg->Release();
944         return CDVDVideoCodec::VC_PICTURE;
945       }
946       msg->Release();
947     }
948     else if (m_vaapiOutput.m_controlPort.ReceiveInMessage(&msg))
949     {
950       if (msg->signal == COutputControlProtocol::STATS)
951       {
952         msg->Release();
953         m_bufferStats.Get(decoded, processed, render, vpp);
954         if (!drain && decoded < 2 && processed < 3)
955         {
956           return CDVDVideoCodec::VC_BUFFER;
957         }
958       }
959       else
960       {
961         msg->Release();
962         m_DisplayState = VAAPI_ERROR;
963         return CDVDVideoCodec::VC_ERROR;
964       }
965     }
966 
967     if (!m_inMsgEvent.WaitMSec(2000))
968       break;
969   }
970 
971   CLog::Log(LOGERROR, "VAAPI::%s - timed out waiting for output message - decoded: %d, proc: %d, has free surface: %s",
972                       __FUNCTION__, decoded, processed, m_videoSurfaces.HasFree() ? "yes" : "no");
973   m_DisplayState = VAAPI_ERROR;
974 
975   return CDVDVideoCodec::VC_ERROR;
976 }
977 
Check(AVCodecContext * avctx)978 CDVDVideoCodec::VCReturn CDecoder::Check(AVCodecContext* avctx)
979 {
980   EDisplayState state;
981 
982   { CSingleLock lock(m_DecoderSection);
983     state = m_DisplayState;
984   }
985 
986   if (state == VAAPI_LOST)
987   {
988     CLog::Log(LOGDEBUG, LOGVIDEO, "VAAPI::Check waiting for display reset event");
989     if (!m_DisplayEvent.WaitMSec(4000))
990     {
991       CLog::Log(LOGERROR, "VAAPI::Check - device didn't reset in reasonable time");
992       state = VAAPI_RESET;
993     }
994     else
995     {
996       CSingleLock lock(m_DecoderSection);
997       state = m_DisplayState;
998     }
999   }
1000   if (state == VAAPI_RESET || state == VAAPI_ERROR)
1001   {
1002     CSingleLock lock(m_DecoderSection);
1003 
1004     avcodec_flush_buffers(avctx);
1005     FiniVAAPIOutput();
1006     if (m_vaapiConfig.context)
1007       m_vaapiConfig.context->Release(this);
1008     m_vaapiConfig.context = 0;
1009 
1010     if (CVAAPIContext::EnsureContext(&m_vaapiConfig.context, this) && ConfigVAAPI())
1011     {
1012       m_DisplayState = VAAPI_OPEN;
1013     }
1014 
1015     if (state == VAAPI_RESET)
1016       return CDVDVideoCodec::VC_FLUSHED;
1017     else
1018       return CDVDVideoCodec::VC_ERROR;
1019   }
1020 
1021   if (m_getBufferError > 0 && m_getBufferError < 5)
1022   {
1023     // if there is no other error, sleep for a short while
1024     // in order not to drain player's message queue
1025     KODI::TIME::Sleep(10);
1026 
1027     return CDVDVideoCodec::VC_NOBUFFER;
1028   }
1029 
1030   return CDVDVideoCodec::VC_NONE;
1031 }
1032 
GetPicture(AVCodecContext * avctx,VideoPicture * picture)1033 bool CDecoder::GetPicture(AVCodecContext* avctx, VideoPicture* picture)
1034 {
1035   if (picture->videoBuffer)
1036   {
1037     picture->videoBuffer->Release();
1038     picture->videoBuffer = nullptr;
1039   }
1040 
1041   CSingleLock lock(m_DecoderSection);
1042 
1043   if (m_DisplayState != VAAPI_OPEN)
1044     return false;
1045 
1046   picture->SetParams(m_presentPicture->DVDPic);
1047   picture->videoBuffer = m_presentPicture;
1048   m_presentPicture = nullptr;
1049 
1050   return true;
1051 }
1052 
Reset()1053 void CDecoder::Reset()
1054 {
1055   CSingleLock lock(m_DecoderSection);
1056 
1057   if (m_presentPicture)
1058   {
1059     m_presentPicture->Release();
1060     m_presentPicture = nullptr;
1061   }
1062 
1063   if (!m_vaapiConfigured)
1064     return;
1065 
1066   Message *reply;
1067   if (m_vaapiOutput.m_controlPort.SendOutMessageSync(COutputControlProtocol::FLUSH,
1068                                                  &reply,
1069                                                  2000))
1070   {
1071     bool success = reply->signal == COutputControlProtocol::ACC ? true : false;
1072     reply->Release();
1073     if (!success)
1074     {
1075       CLog::Log(LOGERROR, "VAAPI::%s - flush returned error", __FUNCTION__);
1076       m_DisplayState = VAAPI_ERROR;
1077     }
1078     else
1079     {
1080       m_bufferStats.Reset();
1081       m_getBufferError = 0;
1082     }
1083   }
1084   else
1085   {
1086     CLog::Log(LOGERROR, "VAAPI::%s - flush timed out", __FUNCTION__);
1087     m_DisplayState = VAAPI_ERROR;
1088   }
1089 }
1090 
CanSkipDeint()1091 bool CDecoder::CanSkipDeint()
1092 {
1093   return m_bufferStats.CanSkipDeint();
1094 }
1095 
CheckSuccess(VAStatus status,const std::string & function)1096 bool CDecoder::CheckSuccess(VAStatus status, const std::string& function)
1097 {
1098   if (status != VA_STATUS_SUCCESS)
1099   {
1100     CLog::Log(LOGERROR, "VAAPI/decoder {} error: {} ({})", function, vaErrorStr(status), status);
1101     m_ErrorCount++;
1102 
1103     if(m_DisplayState == VAAPI_OPEN)
1104     {
1105       if (m_ErrorCount > 2)
1106         m_DisplayState = VAAPI_ERROR;
1107     }
1108     return false;
1109   }
1110   m_ErrorCount = 0;
1111   return true;
1112 }
1113 
ConfigVAAPI()1114 bool CDecoder::ConfigVAAPI()
1115 {
1116   m_vaapiConfig.dpy = m_vaapiConfig.context->GetDisplay();
1117   m_vaapiConfig.attrib = m_vaapiConfig.context->GetAttrib(m_vaapiConfig.profile);
1118   if ((m_vaapiConfig.attrib.value & (VA_RT_FORMAT_YUV420 | VA_RT_FORMAT_YUV420_10BPP)) == 0)
1119   {
1120     CLog::Log(LOGERROR, "VAAPI - invalid yuv format %x", m_vaapiConfig.attrib.value);
1121     return false;
1122   }
1123 
1124   m_vaapiConfig.configId = m_vaapiConfig.context->CreateConfig(m_vaapiConfig.profile,
1125                                                                m_vaapiConfig.attrib);
1126   if (m_vaapiConfig.configId == VA_INVALID_ID)
1127     return false;
1128 
1129   // create surfaces
1130   unsigned int format = VA_RT_FORMAT_YUV420;
1131   std::int32_t pixelFormat = VA_FOURCC_NV12;
1132 
1133   if (m_vaapiConfig.profile == VAProfileHEVCMain10)
1134   {
1135     format = VA_RT_FORMAT_YUV420_10BPP;
1136     pixelFormat = VA_FOURCC_P010;
1137   }
1138 
1139   VASurfaceAttrib attribs[1], *attrib;
1140   attrib = attribs;
1141   attrib->flags = VA_SURFACE_ATTRIB_SETTABLE;
1142   attrib->type = VASurfaceAttribPixelFormat;
1143   attrib->value.type = VAGenericValueTypeInteger;
1144   attrib->value.value.i = pixelFormat;
1145 
1146   VASurfaceID surfaces[32];
1147   int nb_surfaces = m_vaapiConfig.maxReferences;
1148   if (!CheckSuccess(
1149       vaCreateSurfaces(m_vaapiConfig.dpy, format, m_vaapiConfig.surfaceWidth,
1150           m_vaapiConfig.surfaceHeight, surfaces,
1151           nb_surfaces, attribs, 1), "vaCreateSurfaces"))
1152   {
1153     return false;
1154   }
1155   for (int i=0; i<nb_surfaces; i++)
1156   {
1157     m_videoSurfaces.AddSurface(surfaces[i]);
1158   }
1159 
1160   // initialize output
1161   CSingleLock lock(CServiceBroker::GetWinSystem()->GetGfxContext());
1162   m_vaapiConfig.stats = &m_bufferStats;
1163   m_bufferStats.Reset();
1164   m_vaapiOutput.Start();
1165   Message *reply;
1166   if (m_vaapiOutput.m_controlPort.SendOutMessageSync(COutputControlProtocol::INIT,
1167                                                      &reply,
1168                                                      2000,
1169                                                      &m_vaapiConfig,
1170                                                      sizeof(m_vaapiConfig)))
1171   {
1172     bool success = reply->signal == COutputControlProtocol::ACC ? true : false;
1173     if (!success)
1174     {
1175       reply->Release();
1176       CLog::Log(LOGERROR, "VAAPI::%s - vaapi output returned error", __FUNCTION__);
1177       m_vaapiOutput.Dispose();
1178       return false;
1179     }
1180     reply->Release();
1181   }
1182   else
1183   {
1184     CLog::Log(LOGERROR, "VAAPI::%s - failed to init output", __FUNCTION__);
1185     m_vaapiOutput.Dispose();
1186     return false;
1187   }
1188 
1189   m_inMsgEvent.Reset();
1190   m_vaapiConfigured = true;
1191   m_ErrorCount = 0;
1192 
1193   return true;
1194 }
1195 
FiniVAAPIOutput()1196 void CDecoder::FiniVAAPIOutput()
1197 {
1198   if (!m_vaapiConfigured)
1199     return;
1200 
1201   // uninit output
1202   m_vaapiOutput.Dispose();
1203   m_vaapiConfigured = false;
1204 
1205   // destroy surfaces
1206   CLog::Log(LOGDEBUG, LOGVIDEO, "VAAPI::FiniVAAPIOutput destroying %d video surfaces", m_videoSurfaces.Size());
1207   VASurfaceID surf;
1208   while((surf = m_videoSurfaces.RemoveNext()) != VA_INVALID_SURFACE)
1209   {
1210     CheckSuccess(vaDestroySurfaces(m_vaapiConfig.dpy, &surf, 1), "vaDestroySurfaces");
1211   }
1212   m_videoSurfaces.Reset();
1213 
1214   // destroy vaapi config
1215   if (m_vaapiConfig.configId != VA_INVALID_ID)
1216     CheckSuccess(vaDestroyConfig(m_vaapiConfig.dpy, m_vaapiConfig.configId), "vaDestroyConfig");
1217   m_vaapiConfig.configId = VA_INVALID_ID;
1218 }
1219 
ReturnRenderPicture(CVaapiRenderPicture * renderPic)1220 void CDecoder::ReturnRenderPicture(CVaapiRenderPicture *renderPic)
1221 {
1222   m_vaapiOutput.m_dataPort.SendOutMessage(COutputDataProtocol::RETURNPIC, &renderPic, sizeof(renderPic));
1223 }
1224 
Create(CDVDStreamInfo & hint,CProcessInfo & processInfo,AVPixelFormat fmt)1225 IHardwareDecoder* CDecoder::Create(CDVDStreamInfo &hint, CProcessInfo &processInfo, AVPixelFormat fmt)
1226 {
1227   if (fmt == AV_PIX_FMT_VAAPI_VLD && CServiceBroker::GetSettingsComponent()->GetSettings()->GetBool(SETTING_VIDEOPLAYER_USEVAAPI))
1228     return new VAAPI::CDecoder(processInfo);
1229 
1230   return nullptr;
1231 }
1232 
Register(IVaapiWinSystem * winSystem,bool deepColor)1233 void CDecoder::Register(IVaapiWinSystem *winSystem, bool deepColor)
1234 {
1235   m_pWinSystem = winSystem;
1236 
1237   CVaapiConfig config;
1238   if (!CVAAPIContext::EnsureContext(&config.context, nullptr))
1239     return;
1240 
1241   m_capGeneral = true;
1242   m_capDeepColor = deepColor;
1243   CDVDFactoryCodec::RegisterHWAccel("vaapi", CDecoder::Create);
1244   config.context->Release(nullptr);
1245 
1246   auto settingsComponent = CServiceBroker::GetSettingsComponent();
1247   if (!settingsComponent)
1248     return;
1249 
1250   auto settings = settingsComponent->GetSettings();
1251   if (!settings)
1252     return;
1253 
1254   constexpr std::array<const char*, 8> vaapiSettings = {
1255       SETTING_VIDEOPLAYER_USEVAAPI,     SETTING_VIDEOPLAYER_USEVAAPIMPEG4,
1256       SETTING_VIDEOPLAYER_USEVAAPIVC1,  SETTING_VIDEOPLAYER_USEVAAPIMPEG2,
1257       SETTING_VIDEOPLAYER_USEVAAPIVP8,  SETTING_VIDEOPLAYER_USEVAAPIVP9,
1258       SETTING_VIDEOPLAYER_USEVAAPIHEVC, SETTING_VIDEOPLAYER_PREFERVAAPIRENDER};
1259 
1260   for (const auto vaapiSetting : vaapiSettings)
1261   {
1262     auto setting = settings->GetSetting(vaapiSetting);
1263     if (!setting)
1264     {
1265       CLog::Log(LOGERROR, "Failed to load setting for: {}", vaapiSetting);
1266       continue;
1267     }
1268 
1269     setting->SetVisible(true);
1270   }
1271 }
1272 
1273 //-----------------------------------------------------------------------------
1274 // Buffer Pool
1275 //-----------------------------------------------------------------------------
1276 
1277 /**
1278  * Buffer pool holds allocated vaapi and gl resources
1279  * Embedded in COutput
1280  */
1281 class VAAPI::CVaapiBufferPool : public IVideoBufferPool
1282 {
1283 public:
1284   explicit CVaapiBufferPool(CDecoder &decoder);
1285   ~CVaapiBufferPool() override;
1286   CVideoBuffer* Get() override;
1287   void Return(int id) override;
1288   CVaapiRenderPicture* GetVaapi();
1289   bool HasFree();
1290   void QueueReturnPicture(CVaapiRenderPicture *pic);
1291   CVaapiRenderPicture* ProcessSyncPicture();
1292   void Init();
1293   void DeleteTextures(bool precleanup);
1294 
1295   std::deque<CVaapiProcessedPicture> processedPics;
1296   std::deque<CVaapiProcessedPicture> processedPicsAway;
1297   std::deque<CVaapiDecodedPicture> decodedPics;
1298   int procPicId;
1299 
1300 protected:
1301   std::vector<CVaapiRenderPicture*> allRenderPics;
1302   std::deque<int> usedRenderPics;
1303   std::deque<int> freeRenderPics;
1304   std::deque<int> syncRenderPics;
1305 
1306   CDecoder &m_vaapi;
1307 };
1308 
CVaapiBufferPool(CDecoder & decoder)1309 CVaapiBufferPool::CVaapiBufferPool(CDecoder &decoder)
1310   : m_vaapi(decoder)
1311 {
1312   CVaapiRenderPicture *pic;
1313   for (unsigned int i = 0; i < NUM_RENDER_PICS; i++)
1314   {
1315     pic = new CVaapiRenderPicture(i);
1316     allRenderPics.push_back(pic);
1317     freeRenderPics.push_back(i);
1318   }
1319 }
1320 
~CVaapiBufferPool()1321 CVaapiBufferPool::~CVaapiBufferPool()
1322 {
1323   CVaapiRenderPicture *pic;
1324   for (unsigned int i = 0; i < NUM_RENDER_PICS; i++)
1325   {
1326     pic = allRenderPics[i];
1327     delete pic;
1328   }
1329   allRenderPics.clear();
1330 }
1331 
Get()1332 CVideoBuffer* CVaapiBufferPool::Get()
1333 {
1334   if (freeRenderPics.empty())
1335     return nullptr;
1336 
1337   int idx = freeRenderPics.front();
1338   freeRenderPics.pop_front();
1339   usedRenderPics.push_back(idx);
1340 
1341   CVideoBuffer *retPic = allRenderPics[idx];
1342   retPic->Acquire(GetPtr());
1343 
1344   m_vaapi.Acquire();
1345 
1346   return retPic;
1347 }
1348 
Return(int id)1349 void CVaapiBufferPool::Return(int id)
1350 {
1351   CVaapiRenderPicture *pic = allRenderPics[id];
1352 
1353   m_vaapi.ReturnRenderPicture(pic);
1354   m_vaapi.ReleasePicReference();
1355 }
1356 
GetVaapi()1357 CVaapiRenderPicture* CVaapiBufferPool::GetVaapi()
1358 {
1359   return dynamic_cast<CVaapiRenderPicture*>(Get());
1360 }
1361 
HasFree()1362 bool CVaapiBufferPool::HasFree()
1363 {
1364   return !freeRenderPics.empty();
1365 }
1366 
QueueReturnPicture(CVaapiRenderPicture * pic)1367 void CVaapiBufferPool::QueueReturnPicture(CVaapiRenderPicture *pic)
1368 {
1369   std::deque<int>::iterator it;
1370   for (it = usedRenderPics.begin(); it != usedRenderPics.end(); ++it)
1371   {
1372     if (allRenderPics[*it] == pic)
1373     {
1374       break;
1375     }
1376   }
1377 
1378   if (it == usedRenderPics.end())
1379   {
1380     CLog::Log(LOGWARNING, "CVaapiRenderPicture::QueueReturnPicture - pic not found");
1381     return;
1382   }
1383 
1384   // check if already queued
1385   auto it2 = find(syncRenderPics.begin(), syncRenderPics.end(), *it);
1386   if (it2 == syncRenderPics.end())
1387   {
1388     syncRenderPics.push_back(*it);
1389   }
1390 }
1391 
ProcessSyncPicture()1392 CVaapiRenderPicture* CVaapiBufferPool::ProcessSyncPicture()
1393 {
1394   CVaapiRenderPicture *retPic = nullptr;
1395 
1396   for (auto it = syncRenderPics.begin(); it != syncRenderPics.end(); ++it)
1397   {
1398     retPic = allRenderPics[*it];
1399 
1400     freeRenderPics.push_back(*it);
1401 
1402     auto it2 = find(usedRenderPics.begin(), usedRenderPics.end(),*it);
1403     if (it2 == usedRenderPics.end())
1404     {
1405       CLog::Log(LOGERROR, "CVaapiRenderPicture::ProcessSyncPicture - pic not found in queue");
1406     }
1407     else
1408     {
1409       usedRenderPics.erase(it2);
1410     }
1411     it = syncRenderPics.erase(it);
1412 
1413     if (!retPic->valid)
1414     {
1415       CLog::Log(LOGDEBUG, LOGVIDEO, "CVaapiRenderPicture::%s - return of invalid render pic", __FUNCTION__);
1416       retPic = nullptr;
1417     }
1418     break;
1419   }
1420   return retPic;
1421 }
1422 
Init()1423 void CVaapiBufferPool::Init()
1424 {
1425   for (auto &pic : allRenderPics)
1426   {
1427     pic->avFrame = av_frame_alloc();
1428     pic->valid = false;
1429   }
1430   procPicId = 0;
1431 }
1432 
DeleteTextures(bool precleanup)1433 void CVaapiBufferPool::DeleteTextures(bool precleanup)
1434 {
1435   for (auto &pic : allRenderPics)
1436   {
1437     if (precleanup && pic->valid)
1438       continue;
1439 
1440     av_frame_free(&pic->avFrame);
1441     pic->valid = false;
1442   }
1443 }
1444 
GetPlanes(uint8_t * (& planes)[YuvImage::MAX_PLANES])1445 void CVaapiRenderPicture::GetPlanes(uint8_t*(&planes)[YuvImage::MAX_PLANES])
1446 {
1447   planes[0] = avFrame->data[0];
1448   planes[1] = avFrame->data[1];
1449   planes[2] = avFrame->data[2];
1450 }
1451 
GetStrides(int (& strides)[YuvImage::MAX_PLANES])1452 void CVaapiRenderPicture::GetStrides(int(&strides)[YuvImage::MAX_PLANES])
1453 {
1454   strides[0] = avFrame->linesize[0];
1455   strides[1] = avFrame->linesize[1];
1456   strides[2] = avFrame->linesize[2];
1457 }
1458 
1459 //-----------------------------------------------------------------------------
1460 // Output
1461 //-----------------------------------------------------------------------------
COutput(CDecoder & decoder,CEvent * inMsgEvent)1462 COutput::COutput(CDecoder &decoder, CEvent *inMsgEvent) :
1463   CThread("Vaapi-Output"),
1464   m_controlPort("OutputControlPort", inMsgEvent, &m_outMsgEvent),
1465   m_dataPort("OutputDataPort", inMsgEvent, &m_outMsgEvent),
1466   m_vaapi(decoder)
1467 {
1468   m_inMsgEvent = inMsgEvent;
1469   m_bufferPool = std::make_shared<CVaapiBufferPool>(decoder);
1470 }
1471 
Start()1472 void COutput::Start()
1473 {
1474   Create();
1475 }
1476 
~COutput()1477 COutput::~COutput()
1478 {
1479   Dispose();
1480 }
1481 
Dispose()1482 void COutput::Dispose()
1483 {
1484   CSingleLock lock(CServiceBroker::GetWinSystem()->GetGfxContext());
1485   m_bStop = true;
1486   m_outMsgEvent.Set();
1487   StopThread();
1488   m_controlPort.Purge();
1489   m_dataPort.Purge();
1490 }
1491 
OnStartup()1492 void COutput::OnStartup()
1493 {
1494   CLog::Log(LOGINFO, "COutput::OnStartup: Output Thread created");
1495 }
1496 
OnExit()1497 void COutput::OnExit()
1498 {
1499   CLog::Log(LOGINFO, "COutput::OnExit: Output Thread terminated");
1500 }
1501 
1502 enum OUTPUT_STATES
1503 {
1504   O_TOP = 0,                      // 0
1505   O_TOP_ERROR,                    // 1
1506   O_TOP_UNCONFIGURED,             // 2
1507   O_TOP_CONFIGURED,               // 3
1508   O_TOP_CONFIGURED_IDLE,          // 4
1509   O_TOP_CONFIGURED_WORK,          // 5
1510   O_TOP_CONFIGURED_STEP1,         // 6
1511   O_TOP_CONFIGURED_STEP2,         // 7
1512   O_TOP_CONFIGURED_OUTPUT,        // 8
1513 };
1514 
1515 int VAAPI_OUTPUT_parentStates[] = {
1516     -1,
1517     0, //TOP_ERROR
1518     0, //TOP_UNCONFIGURED
1519     0, //TOP_CONFIGURED
1520     3, //TOP_CONFIGURED_IDLE
1521     3, //TOP_CONFIGURED_WORK
1522     3, //TOP_CONFIGURED_STEP1
1523     3, //TOP_CONFIGURED_STEP2
1524     3, //TOP_CONFIGURED_OUTPUT
1525 };
1526 
StateMachine(int signal,Protocol * port,Message * msg)1527 void COutput::StateMachine(int signal, Protocol *port, Message *msg)
1528 {
1529   for (int state = m_state; ; state = VAAPI_OUTPUT_parentStates[state])
1530   {
1531     switch (state)
1532     {
1533     case O_TOP: // TOP
1534       if (port == &m_controlPort)
1535       {
1536         switch (signal)
1537         {
1538         case COutputControlProtocol::FLUSH:
1539           msg->Reply(COutputControlProtocol::ACC);
1540           return;
1541         case COutputControlProtocol::PRECLEANUP:
1542           msg->Reply(COutputControlProtocol::ACC);
1543           return;
1544         default:
1545           break;
1546         }
1547       }
1548       else if (port == &m_dataPort)
1549       {
1550         switch (signal)
1551         {
1552         case COutputDataProtocol::RETURNPIC:
1553           CVaapiRenderPicture *pic;
1554           pic = *((CVaapiRenderPicture**)msg->data);
1555           QueueReturnPicture(pic);
1556           return;
1557         case COutputDataProtocol::RETURNPROCPIC:
1558           int id;
1559           id = *((int*)msg->data);
1560           ProcessReturnProcPicture(id);
1561           return;
1562         default:
1563           break;
1564         }
1565       }
1566       {
1567         std::string portName = port == NULL ? "timer" : port->portName;
1568         CLog::Log(LOGWARNING, "COutput::%s - signal: %d form port: %s not handled for state: %d", __FUNCTION__, signal, portName.c_str(), m_state);
1569       }
1570       return;
1571 
1572     case O_TOP_ERROR:
1573       break;
1574 
1575     case O_TOP_UNCONFIGURED:
1576       if (port == &m_controlPort)
1577       {
1578         switch (signal)
1579         {
1580         case COutputControlProtocol::INIT:
1581           CVaapiConfig *data;
1582           data = reinterpret_cast<CVaapiConfig*>(msg->data);
1583           if (data)
1584           {
1585             m_config = *data;
1586           }
1587           Init();
1588 
1589           // set initial number of
1590           EnsureBufferPool();
1591           m_state = O_TOP_CONFIGURED_IDLE;
1592           msg->Reply(COutputControlProtocol::ACC);
1593           return;
1594         default:
1595           break;
1596         }
1597       }
1598       break;
1599 
1600     case O_TOP_CONFIGURED:
1601       if (port == &m_controlPort)
1602       {
1603         switch (signal)
1604         {
1605         case COutputControlProtocol::FLUSH:
1606           Flush();
1607           msg->Reply(COutputControlProtocol::ACC);
1608           m_state = O_TOP_CONFIGURED_IDLE;
1609           return;
1610         case COutputControlProtocol::PRECLEANUP:
1611           Flush();
1612           ReleaseBufferPool(true);
1613           msg->Reply(COutputControlProtocol::ACC);
1614           m_state = O_TOP_UNCONFIGURED;
1615           m_extTimeout = 10000;
1616           return;
1617         default:
1618           break;
1619         }
1620       }
1621       else if (port == &m_dataPort)
1622       {
1623         switch (signal)
1624         {
1625         case COutputDataProtocol::NEWFRAME:
1626           CPayloadWrap<CVaapiDecodedPicture> *payload;
1627           payload = dynamic_cast<CPayloadWrap<CVaapiDecodedPicture>*>(msg->payloadObj.get());
1628           if (payload)
1629           {
1630             m_bufferPool->decodedPics.push_back(*(payload->GetPlayload()));
1631             m_extTimeout = 0;
1632           }
1633           return;
1634         case COutputDataProtocol::RETURNPIC:
1635           CVaapiRenderPicture *pic;
1636           pic = *((CVaapiRenderPicture**)msg->data);
1637           QueueReturnPicture(pic);
1638           m_controlPort.SendInMessage(COutputControlProtocol::STATS);
1639           m_extTimeout = 0;
1640           return;
1641         case COutputDataProtocol::RETURNPROCPIC:
1642           int id;
1643           id = *((int*)msg->data);
1644           ProcessReturnProcPicture(id);
1645           m_extTimeout = 0;
1646           return;
1647         default:
1648           break;
1649         }
1650       }
1651       break;
1652 
1653     case O_TOP_CONFIGURED_IDLE:
1654       if (port == NULL) // timeout
1655       {
1656         switch (signal)
1657         {
1658         case COutputControlProtocol::TIMEOUT:
1659           ProcessSyncPicture();
1660           m_extTimeout = 100;
1661           if (HasWork())
1662           {
1663             m_state = O_TOP_CONFIGURED_WORK;
1664             m_extTimeout = 0;
1665           }
1666           return;
1667         default:
1668           break;
1669         }
1670       }
1671       break;
1672 
1673     case O_TOP_CONFIGURED_WORK:
1674       if (port == NULL) // timeout
1675       {
1676         switch (signal)
1677         {
1678         case COutputControlProtocol::TIMEOUT:
1679           if (PreferPP())
1680           {
1681             m_currentPicture = m_bufferPool->decodedPics.front();
1682             m_bufferPool->decodedPics.pop_front();
1683             InitCycle();
1684             m_state = O_TOP_CONFIGURED_STEP1;
1685             m_extTimeout = 0;
1686             return;
1687           }
1688           else if (m_bufferPool->HasFree() &&
1689                    !m_bufferPool->processedPics.empty())
1690           {
1691             m_state = O_TOP_CONFIGURED_OUTPUT;
1692             m_extTimeout = 0;
1693             return;
1694           }
1695           else
1696             m_state = O_TOP_CONFIGURED_IDLE;
1697           m_extTimeout = 100;
1698           return;
1699         default:
1700           break;
1701         }
1702       }
1703       break;
1704 
1705     case O_TOP_CONFIGURED_STEP1:
1706       if (port == NULL) // timeout
1707       {
1708         switch (signal)
1709         {
1710         case COutputControlProtocol::TIMEOUT:
1711         {
1712           if (!m_pp->AddPicture(m_currentPicture))
1713           {
1714             m_state = O_TOP_ERROR;
1715             return;
1716           }
1717           CVaapiProcessedPicture outPic;
1718           if (m_pp->Filter(outPic))
1719           {
1720             m_config.stats->IncProcessed();
1721             m_bufferPool->processedPics.push_back(outPic);
1722             m_state = O_TOP_CONFIGURED_STEP2;
1723           }
1724           else
1725           {
1726             m_state = O_TOP_CONFIGURED_IDLE;
1727           }
1728           m_config.stats->DecDecoded();
1729           m_controlPort.SendInMessage(COutputControlProtocol::STATS);
1730           m_extTimeout = 0;
1731           return;
1732         }
1733         default:
1734           break;
1735         }
1736       }
1737       break;
1738 
1739     case O_TOP_CONFIGURED_STEP2:
1740       if (port == NULL) // timeout
1741       {
1742         switch (signal)
1743         {
1744         case COutputControlProtocol::TIMEOUT:
1745         {
1746           CVaapiProcessedPicture outPic;
1747           if (m_pp->Filter(outPic))
1748           {
1749             m_bufferPool->processedPics.push_back(outPic);
1750             m_config.stats->IncProcessed();
1751             m_extTimeout = 0;
1752             return;
1753           }
1754           m_state = O_TOP_CONFIGURED_IDLE;
1755           m_extTimeout = 0;
1756           return;
1757         }
1758         default:
1759           break;
1760         }
1761       }
1762       break;
1763 
1764     case O_TOP_CONFIGURED_OUTPUT:
1765       if (port == NULL) // timeout
1766       {
1767         switch (signal)
1768         {
1769         case COutputControlProtocol::TIMEOUT:
1770           if (!m_bufferPool->processedPics.empty())
1771           {
1772             CVaapiRenderPicture *outPic;
1773             CVaapiProcessedPicture procPic;
1774             procPic = m_bufferPool->processedPics.front();
1775             m_bufferPool->processedPics.pop_front();
1776             outPic = ProcessPicture(procPic);
1777             if (outPic)
1778             {
1779               m_config.stats->IncRender();
1780               m_dataPort.SendInMessage(COutputDataProtocol::PICTURE, &outPic, sizeof(outPic));
1781             }
1782             m_config.stats->DecProcessed();
1783           }
1784           m_state = O_TOP_CONFIGURED_IDLE;
1785           m_extTimeout = 0;
1786           return;
1787         default:
1788           break;
1789         }
1790       }
1791       break;
1792 
1793     default: // we are in no state, should not happen
1794       CLog::Log(LOGERROR, "COutput::%s - no valid state: %d", __FUNCTION__, m_state);
1795       return;
1796     }
1797   } // for
1798 }
1799 
Process()1800 void COutput::Process()
1801 {
1802   Message *msg = NULL;
1803   Protocol *port = NULL;
1804   bool gotMsg;
1805 
1806   m_state = O_TOP_UNCONFIGURED;
1807   m_extTimeout = 1000;
1808   m_bStateMachineSelfTrigger = false;
1809 
1810   while (!m_bStop)
1811   {
1812     gotMsg = false;
1813 
1814     if (m_bStateMachineSelfTrigger)
1815     {
1816       m_bStateMachineSelfTrigger = false;
1817       // self trigger state machine
1818       StateMachine(msg->signal, port, msg);
1819       if (!m_bStateMachineSelfTrigger)
1820       {
1821         msg->Release();
1822         msg = NULL;
1823       }
1824       continue;
1825     }
1826     // check control port
1827     else if (m_controlPort.ReceiveOutMessage(&msg))
1828     {
1829       gotMsg = true;
1830       port = &m_controlPort;
1831     }
1832     // check data port
1833     else if (m_dataPort.ReceiveOutMessage(&msg))
1834     {
1835       gotMsg = true;
1836       port = &m_dataPort;
1837     }
1838     if (gotMsg)
1839     {
1840       StateMachine(msg->signal, port, msg);
1841       if (!m_bStateMachineSelfTrigger)
1842       {
1843         msg->Release();
1844         msg = NULL;
1845       }
1846       continue;
1847     }
1848 
1849     // wait for message
1850     else if (m_outMsgEvent.WaitMSec(m_extTimeout))
1851     {
1852       continue;
1853     }
1854     // time out
1855     else
1856     {
1857       msg = m_controlPort.GetMessage();
1858       msg->signal = COutputControlProtocol::TIMEOUT;
1859       port = 0;
1860       // signal timeout to state machine
1861       StateMachine(msg->signal, port, msg);
1862       if (!m_bStateMachineSelfTrigger)
1863       {
1864         msg->Release();
1865         msg = NULL;
1866       }
1867     }
1868   }
1869   Flush();
1870   Uninit();
1871 }
1872 
Init()1873 bool COutput::Init()
1874 {
1875   m_diMethods.numDiMethods = 0;
1876 
1877   m_pp = new CFFmpegPostproc();
1878   m_pp->PreInit(m_config, &m_diMethods);
1879   delete m_pp;
1880 
1881   m_pp = new CVppPostproc();
1882   m_pp->PreInit(m_config, &m_diMethods);
1883   delete m_pp;
1884 
1885   m_pp = nullptr;
1886 
1887   std::list<EINTERLACEMETHOD> deintMethods;
1888   deintMethods.assign(m_diMethods.diMethods, m_diMethods.diMethods + m_diMethods.numDiMethods);
1889   m_config.processInfo->UpdateDeinterlacingMethods(deintMethods);
1890   m_config.processInfo->SetDeinterlacingMethodDefault(EINTERLACEMETHOD::VS_INTERLACEMETHOD_VAAPI_BOB);
1891 
1892   m_seenInterlaced = false;
1893 
1894   return true;
1895 }
1896 
Uninit()1897 bool COutput::Uninit()
1898 {
1899   ProcessSyncPicture();
1900   ReleaseBufferPool();
1901   if (m_pp)
1902   {
1903     std::shared_ptr<CPostproc> pp(m_pp);
1904     m_discardedPostprocs.push_back(pp);
1905     m_pp->Discard(this, &COutput::ReadyForDisposal);
1906     m_pp = nullptr;
1907   }
1908 
1909   if (!m_discardedPostprocs.empty())
1910   {
1911     CLog::Log(LOGERROR, "VAAPI::COutput::Uninit - not all CPostprcs released");
1912   }
1913   return true;
1914 }
1915 
Flush()1916 void COutput::Flush()
1917 {
1918   Message *msg;
1919   while (m_dataPort.ReceiveOutMessage(&msg))
1920   {
1921     if (msg->signal == COutputDataProtocol::NEWFRAME)
1922     {
1923       CPayloadWrap<CVaapiDecodedPicture> *payload;
1924       payload = dynamic_cast<CPayloadWrap<CVaapiDecodedPicture>*>(msg->payloadObj.get());
1925       if (payload)
1926       {
1927         CVaapiDecodedPicture pic = *(payload->GetPlayload());
1928         m_config.videoSurfaces->ClearRender(pic.videoSurface);
1929       }
1930     }
1931     else if (msg->signal == COutputDataProtocol::RETURNPIC)
1932     {
1933       CVaapiRenderPicture *pic;
1934       pic = *((CVaapiRenderPicture**)msg->data);
1935       QueueReturnPicture(pic);
1936     }
1937     msg->Release();
1938   }
1939 
1940   while (m_dataPort.ReceiveInMessage(&msg))
1941   {
1942     if (msg->signal == COutputDataProtocol::PICTURE)
1943     {
1944       CVaapiRenderPicture *pic;
1945       pic = *((CVaapiRenderPicture**)msg->data);
1946       pic->Release();
1947     }
1948     msg->Release();
1949   }
1950 
1951   for (unsigned int i = 0; i < m_bufferPool->decodedPics.size(); i++)
1952   {
1953     m_config.videoSurfaces->ClearRender(m_bufferPool->decodedPics[i].videoSurface);
1954   }
1955   m_bufferPool->decodedPics.clear();
1956 
1957   for (unsigned int i = 0; i < m_bufferPool->processedPics.size(); i++)
1958   {
1959     ReleaseProcessedPicture(m_bufferPool->processedPics[i]);
1960   }
1961   m_bufferPool->processedPics.clear();
1962 
1963   if (m_pp)
1964     m_pp->Flush();
1965 }
1966 
HasWork()1967 bool COutput::HasWork()
1968 {
1969   // send a pic to renderer
1970   if (m_bufferPool->HasFree() && !m_bufferPool->processedPics.empty())
1971     return true;
1972 
1973   bool ppWantsPic = true;
1974   if (m_pp)
1975     ppWantsPic = m_pp->WantsPic();
1976 
1977   if (!m_bufferPool->decodedPics.empty() && m_bufferPool->processedPics.size() < 4 && ppWantsPic)
1978     return true;
1979 
1980   return false;
1981 }
1982 
PreferPP()1983 bool COutput::PreferPP()
1984 {
1985   if (!m_bufferPool->decodedPics.empty())
1986   {
1987     if (!m_pp)
1988       return true;
1989 
1990     if (!m_pp->WantsPic())
1991       return false;
1992 
1993     if (!m_pp->DoesSync() && m_bufferPool->processedPics.size() < 4)
1994       return true;
1995 
1996     if (!m_bufferPool->HasFree() || m_bufferPool->processedPics.empty())
1997       return true;
1998   }
1999 
2000   return false;
2001 }
2002 
InitCycle()2003 void COutput::InitCycle()
2004 {
2005   uint64_t latency;
2006   int flags;
2007   m_config.stats->GetParams(latency, flags);
2008 
2009   m_config.stats->SetCanSkipDeint(false);
2010 
2011   EINTERLACEMETHOD method = m_config.processInfo->GetVideoSettings().m_InterlaceMethod;
2012   bool interlaced = m_currentPicture.DVDPic.iFlags & DVP_FLAG_INTERLACED;
2013   // Remember whether any interlaced frames were encountered already.
2014   // If this is the case, the deinterlace method will never automatically be switched to NONE again in
2015   // order to not change deint methods every few frames in PAFF streams.
2016   m_seenInterlaced = m_seenInterlaced || interlaced;
2017 
2018   if (!(flags & DVD_CODEC_CTRL_NO_POSTPROC) &&
2019       m_seenInterlaced &&
2020       method != VS_INTERLACEMETHOD_NONE)
2021   {
2022     if (!m_config.processInfo->Supports(method))
2023       method = VS_INTERLACEMETHOD_VAAPI_BOB;
2024 
2025     if (m_pp && !m_pp->UpdateDeintMethod(method))
2026     {
2027       CLog::Log(LOGDEBUG, LOGVIDEO, "VAAPI output: Current postproc does not want new deinterlace mode, removing");
2028       std::shared_ptr<CPostproc> pp(m_pp);
2029       m_discardedPostprocs.push_back(pp);
2030       m_pp->Discard(this, &COutput::ReadyForDisposal);
2031       m_pp = nullptr;
2032       m_config.processInfo->SetVideoDeintMethod("unknown");
2033     }
2034     if (!m_pp)
2035     {
2036       if (method == VS_INTERLACEMETHOD_DEINTERLACE ||
2037           method == VS_INTERLACEMETHOD_RENDER_BOB)
2038       {
2039         CLog::Log(LOGDEBUG, LOGVIDEO, "VAAPI output: Initializing ffmpeg postproc");
2040         m_pp = new CFFmpegPostproc();
2041         m_config.stats->SetVpp(false);
2042       }
2043       else
2044       {
2045         CLog::Log(LOGDEBUG, LOGVIDEO, "VAAPI output: Initializing vaapi postproc");
2046         m_pp = new CVppPostproc();
2047         m_config.stats->SetVpp(true);
2048       }
2049       if (m_pp->PreInit(m_config))
2050       {
2051         m_pp->Init(method);
2052       }
2053       else
2054       {
2055         CLog::Log(LOGERROR, "VAAPI output: Postproc preinit failed");
2056         delete m_pp;
2057         m_pp = nullptr;
2058       }
2059     }
2060   }
2061   // progressive
2062   else
2063   {
2064     method = VS_INTERLACEMETHOD_NONE;
2065 
2066     if (m_pp && !m_pp->UpdateDeintMethod(method))
2067     {
2068       CLog::Log(LOGDEBUG, LOGVIDEO, "VAAPI output: Current postproc does not want new deinterlace mode, removing");
2069       std::shared_ptr<CPostproc> pp(m_pp);
2070       m_discardedPostprocs.push_back(pp);
2071       m_pp->Discard(this, &COutput::ReadyForDisposal);
2072       m_pp = nullptr;
2073       m_config.processInfo->SetVideoDeintMethod("unknown");
2074     }
2075     if (!m_pp)
2076     {
2077       const bool preferVaapiRender = CServiceBroker::GetSettingsComponent()->GetSettings()->GetBool(SETTING_VIDEOPLAYER_PREFERVAAPIRENDER);
2078       // For 1080p/i or below, always use CVppPostproc even when not deinterlacing
2079       // Reason is: mesa cannot dynamically switch surfaces between use for VAAPI post-processing
2080       // and use for direct export, so we run into trouble if we or the user want to switch
2081       // deinterlacing on/off mid-stream.
2082       // See also: https://bugs.freedesktop.org/show_bug.cgi?id=105145
2083       const bool alwaysInsertVpp = m_config.driverIsMesa &&
2084                                    ((m_config.vidWidth * m_config.vidHeight) <= (1920 * 1080)) &&
2085                                    interlaced;
2086 
2087       m_config.stats->SetVpp(false);
2088       if (!preferVaapiRender)
2089       {
2090         CLog::Log(LOGDEBUG, LOGVIDEO, "VAAPI output: Initializing ffmpeg postproc");
2091         m_pp = new CFFmpegPostproc();
2092       }
2093       else if (alwaysInsertVpp)
2094       {
2095         CLog::Log(LOGDEBUG, LOGVIDEO, "VAAPI output: Initializing vaapi postproc");
2096         m_pp = new CVppPostproc();
2097         m_config.stats->SetVpp(true);
2098       }
2099       else
2100       {
2101         CLog::Log(LOGDEBUG, LOGVIDEO, "VAAPI output: Initializing skip postproc");
2102         m_pp = new CSkipPostproc();
2103       }
2104       if (m_pp->PreInit(m_config))
2105       {
2106         m_pp->Init(method);
2107       }
2108       else
2109       {
2110         CLog::Log(LOGERROR, "VAAPI output: Postproc preinit failed");
2111         delete m_pp;
2112         m_pp = nullptr;
2113       }
2114     }
2115   }
2116   if (!m_pp) // fallback
2117   {
2118     CLog::Log(LOGDEBUG, LOGVIDEO, "VAAPI output: Initializing skip postproc as fallback");
2119     m_pp = new CSkipPostproc();
2120     m_config.stats->SetVpp(false);
2121     if (m_pp->PreInit(m_config))
2122       m_pp->Init(method);
2123   }
2124 }
2125 
ProcessPicture(CVaapiProcessedPicture & pic)2126 CVaapiRenderPicture* COutput::ProcessPicture(CVaapiProcessedPicture &pic)
2127 {
2128   CVaapiRenderPicture *retPic;
2129   retPic = m_bufferPool->GetVaapi();
2130   retPic->DVDPic.SetParams(pic.DVDPic);
2131 
2132   if (!pic.source)
2133   {
2134     CLog::Log(LOGERROR, "VAAPI::ProcessPicture - pic has no source");
2135     retPic->Release();
2136     return nullptr;
2137   }
2138 
2139   if (pic.source->UseVideoSurface())
2140   {
2141     vaSyncSurface(m_config.dpy, pic.videoSurface);
2142     pic.id = m_bufferPool->procPicId++;
2143     m_bufferPool->processedPicsAway.push_back(pic);
2144     retPic->procPic = pic;
2145     retPic->vadsp = m_config.dpy;
2146   }
2147   else
2148   {
2149     av_frame_move_ref(retPic->avFrame, pic.frame);
2150     pic.source->ClearRef(pic);
2151     retPic->procPic.videoSurface = VA_INVALID_ID;
2152   }
2153 
2154   retPic->DVDPic.dts = DVD_NOPTS_VALUE;
2155   retPic->DVDPic.iWidth = m_config.vidWidth;
2156   retPic->DVDPic.iHeight = m_config.vidHeight;
2157 
2158   retPic->valid = true;
2159 
2160   return retPic;
2161 }
2162 
ReleaseProcessedPicture(CVaapiProcessedPicture & pic)2163 void COutput::ReleaseProcessedPicture(CVaapiProcessedPicture &pic)
2164 {
2165   if (!pic.source)
2166   {
2167     return;
2168   }
2169   pic.source->ClearRef(pic);
2170   pic.source = nullptr;
2171 }
2172 
QueueReturnPicture(CVaapiRenderPicture * pic)2173 void COutput::QueueReturnPicture(CVaapiRenderPicture *pic)
2174 {
2175   m_bufferPool->QueueReturnPicture(pic);
2176   ProcessSyncPicture();
2177 }
2178 
ProcessSyncPicture()2179 void COutput::ProcessSyncPicture()
2180 {
2181   CVaapiRenderPicture *pic;
2182 
2183   pic = m_bufferPool->ProcessSyncPicture();
2184 
2185   if (pic)
2186   {
2187     ProcessReturnPicture(pic);
2188   }
2189 }
2190 
ProcessReturnPicture(CVaapiRenderPicture * pic)2191 void COutput::ProcessReturnPicture(CVaapiRenderPicture *pic)
2192 {
2193   if (pic->avFrame)
2194     av_frame_unref(pic->avFrame);
2195 
2196   ProcessReturnProcPicture(pic->procPic.id);
2197   pic->valid = false;
2198 }
2199 
ProcessReturnProcPicture(int id)2200 void COutput::ProcessReturnProcPicture(int id)
2201 {
2202   for (auto it=m_bufferPool->processedPicsAway.begin(); it!=m_bufferPool->processedPicsAway.end(); ++it)
2203   {
2204     if (it->id == id)
2205     {
2206       ReleaseProcessedPicture(*it);
2207       m_bufferPool->processedPicsAway.erase(it);
2208       break;
2209     }
2210   }
2211 }
2212 
EnsureBufferPool()2213 void COutput::EnsureBufferPool()
2214 {
2215   m_bufferPool->Init();
2216 }
2217 
ReleaseBufferPool(bool precleanup)2218 void COutput::ReleaseBufferPool(bool precleanup)
2219 {
2220   ProcessSyncPicture();
2221 
2222   m_bufferPool->DeleteTextures(precleanup);
2223 
2224   for (unsigned int i = 0; i < m_bufferPool->decodedPics.size(); i++)
2225   {
2226     m_config.videoSurfaces->ClearRender(m_bufferPool->decodedPics[i].videoSurface);
2227   }
2228   m_bufferPool->decodedPics.clear();
2229 
2230   for (unsigned int i = 0; i < m_bufferPool->processedPics.size(); i++)
2231   {
2232     ReleaseProcessedPicture(m_bufferPool->processedPics[i]);
2233   }
2234   m_bufferPool->processedPics.clear();
2235 }
2236 
ReadyForDisposal(CPostproc * pp)2237 void COutput::ReadyForDisposal(CPostproc *pp)
2238 {
2239   for (auto it = m_discardedPostprocs.begin(); it != m_discardedPostprocs.end(); ++it)
2240   {
2241     if ((*it).get() == pp)
2242     {
2243       m_discardedPostprocs.erase(it);
2244       break;
2245     }
2246   }
2247 }
2248 
2249 //-----------------------------------------------------------------------------
2250 // Postprocessing
2251 //-----------------------------------------------------------------------------
2252 
PreInit(CVaapiConfig & config,SDiMethods * methods)2253 bool CSkipPostproc::PreInit(CVaapiConfig &config, SDiMethods *methods)
2254 {
2255   m_config = config;
2256   return true;
2257 }
2258 
Init(EINTERLACEMETHOD method)2259 bool CSkipPostproc::Init(EINTERLACEMETHOD method)
2260 {
2261   m_config.processInfo->SetVideoDeintMethod("none");
2262   return true;
2263 }
2264 
AddPicture(CVaapiDecodedPicture & inPic)2265 bool CSkipPostproc::AddPicture(CVaapiDecodedPicture &inPic)
2266 {
2267   m_pic = inPic;
2268   m_step = 0;
2269   return true;
2270 }
2271 
Filter(CVaapiProcessedPicture & outPic)2272 bool CSkipPostproc::Filter(CVaapiProcessedPicture &outPic)
2273 {
2274   if (m_step > 0)
2275     return false;
2276   outPic.DVDPic.SetParams(m_pic.DVDPic);
2277   outPic.videoSurface = m_pic.videoSurface;
2278   m_refsToSurfaces++;
2279   outPic.source = this;
2280   outPic.DVDPic.iFlags &= ~(DVP_FLAG_TOP_FIELD_FIRST |
2281                             DVP_FLAG_REPEAT_TOP_FIELD |
2282                             DVP_FLAG_INTERLACED);
2283   m_step++;
2284   return true;
2285 }
2286 
ClearRef(CVaapiProcessedPicture & pic)2287 void CSkipPostproc::ClearRef(CVaapiProcessedPicture &pic)
2288 {
2289   m_config.videoSurfaces->ClearRender(pic.videoSurface);
2290   m_refsToSurfaces--;
2291 
2292   if (m_pOut && m_refsToSurfaces <= 0)
2293     (m_pOut->*m_cbDispose)(this);
2294 }
2295 
Flush()2296 void CSkipPostproc::Flush()
2297 {
2298 
2299 }
2300 
UpdateDeintMethod(EINTERLACEMETHOD method)2301 bool CSkipPostproc::UpdateDeintMethod(EINTERLACEMETHOD method)
2302 {
2303   if (method == VS_INTERLACEMETHOD_NONE)
2304     return true;
2305 
2306   return false;
2307 }
2308 
DoesSync()2309 bool CSkipPostproc::DoesSync()
2310 {
2311   return false;
2312 }
2313 
UseVideoSurface()2314 bool CSkipPostproc::UseVideoSurface()
2315 {
2316   return true;
2317 }
2318 
Discard(COutput * output,ReadyToDispose cb)2319 void CSkipPostproc::Discard(COutput *output, ReadyToDispose cb)
2320 {
2321   m_pOut = output;
2322   m_cbDispose = cb;
2323   if (m_refsToSurfaces <= 0)
2324     (m_pOut->*m_cbDispose)(this);
2325 }
2326 
2327 //-----------------------------------------------------------------------------
2328 // VPP Postprocessing
2329 //-----------------------------------------------------------------------------
2330 
CVppPostproc()2331 CVppPostproc::CVppPostproc()
2332 {
2333   m_contextId = VA_INVALID_ID;
2334   m_configId = VA_INVALID_ID;
2335   m_filter = VA_INVALID_ID;
2336 }
2337 
~CVppPostproc()2338 CVppPostproc::~CVppPostproc()
2339 {
2340   Dispose();
2341 }
2342 
PreInit(CVaapiConfig & config,SDiMethods * methods)2343 bool CVppPostproc::PreInit(CVaapiConfig &config, SDiMethods *methods)
2344 {
2345   m_config = config;
2346 
2347   // create config
2348   if (!CheckSuccess(
2349       vaCreateConfig(m_config.dpy, VAProfileNone, VAEntrypointVideoProc, NULL, 0, &m_configId),
2350       "vaCreateConfig"))
2351   {
2352     CLog::Log(LOGDEBUG, LOGVIDEO, "CVppPostproc::PreInit  - VPP init failed in vaCreateConfig");
2353 
2354     return false;
2355   }
2356 
2357   VASurfaceAttrib attribs[1], *attrib;
2358   attrib = attribs;
2359   attrib->flags = VA_SURFACE_ATTRIB_SETTABLE;
2360   attrib->type = VASurfaceAttribPixelFormat;
2361   attrib->value.type = VAGenericValueTypeInteger;
2362   attrib->value.value.i = VA_FOURCC_NV12;
2363 
2364   // create surfaces
2365   VASurfaceID surfaces[32];
2366   unsigned int format = VA_RT_FORMAT_YUV420;
2367   if (m_config.profile == VAProfileHEVCMain10)
2368   {
2369     format = VA_RT_FORMAT_YUV420_10BPP;
2370     attrib->value.value.i = VA_FOURCC_P010;
2371   }
2372   int nb_surfaces = NUM_RENDER_PICS;
2373   if (!CheckSuccess(
2374       vaCreateSurfaces(m_config.dpy, format, m_config.surfaceWidth, m_config.surfaceHeight,
2375           surfaces, nb_surfaces,
2376           attribs, 1), "vaCreateSurfaces"))
2377   {
2378     CLog::Log(LOGDEBUG, LOGVIDEO, "CVppPostproc::PreInit  - VPP init failed in vaCreateSurfaces");
2379 
2380     return false;
2381   }
2382   for (int i=0; i<nb_surfaces; i++)
2383   {
2384     m_videoSurfaces.AddSurface(surfaces[i]);
2385   }
2386 
2387   // create vaapi decoder context
2388   if (!CheckSuccess(
2389       vaCreateContext(m_config.dpy, m_configId, m_config.surfaceWidth, m_config.surfaceHeight, 0,
2390           surfaces,
2391           nb_surfaces, &m_contextId), "vaCreateContext"))
2392   {
2393     m_contextId = VA_INVALID_ID;
2394     CLog::Log(LOGDEBUG, LOGVIDEO, "CVppPostproc::PreInit  - VPP init failed in vaCreateContext");
2395 
2396     return false;
2397   }
2398 
2399   VAProcFilterType filters[VAProcFilterCount];
2400   unsigned int numFilters = VAProcFilterCount;
2401   VAProcFilterCapDeinterlacing deinterlacingCaps[VAProcDeinterlacingCount];
2402   unsigned int numDeinterlacingCaps = VAProcDeinterlacingCount;
2403 
2404   if (!CheckSuccess(vaQueryVideoProcFilters(m_config.dpy, m_contextId, filters, &numFilters),
2405       "vaQueryVideoProcFilters"))
2406   {
2407     CLog::Log(LOGDEBUG, LOGVIDEO, "CVppPostproc::PreInit  - VPP init failed in vaQueryVideoProcFilters");
2408 
2409     return false;
2410   }
2411 
2412   if (!CheckSuccess(vaQueryVideoProcFilterCaps(m_config.dpy, m_contextId, VAProcFilterDeinterlacing,
2413       deinterlacingCaps,
2414       &numDeinterlacingCaps), "vaQueryVideoProcFilterCaps"))
2415   {
2416     CLog::Log(LOGDEBUG, LOGVIDEO, "CVppPostproc::PreInit  - VPP init failed in vaQueryVideoProcFilterCaps");
2417 
2418     return false;
2419   }
2420 
2421   if (methods)
2422   {
2423     for (unsigned int i = 0; i < numFilters; i++)
2424     {
2425       if (filters[i] == VAProcFilterDeinterlacing)
2426       {
2427         for (unsigned int j = 0; j < numDeinterlacingCaps; j++)
2428         {
2429           if (deinterlacingCaps[j].type == VAProcDeinterlacingBob)
2430           {
2431             methods->diMethods[methods->numDiMethods++] = VS_INTERLACEMETHOD_VAAPI_BOB;
2432           }
2433           else if (deinterlacingCaps[j].type == VAProcDeinterlacingMotionAdaptive)
2434           {
2435             methods->diMethods[methods->numDiMethods++] = VS_INTERLACEMETHOD_VAAPI_MADI;
2436           }
2437           else if (deinterlacingCaps[j].type == VAProcDeinterlacingMotionCompensated)
2438           {
2439             methods->diMethods[methods->numDiMethods++] = VS_INTERLACEMETHOD_VAAPI_MACI;
2440           }
2441         }
2442       }
2443     }
2444   }
2445   return true;
2446 }
2447 
Init(EINTERLACEMETHOD method)2448 bool CVppPostproc::Init(EINTERLACEMETHOD method)
2449 {
2450   m_forwardRefs = 0;
2451   m_backwardRefs = 0;
2452   m_currentIdx = 0;
2453   m_frameCount = 0;
2454   m_vppMethod = VS_INTERLACEMETHOD_AUTO;
2455 
2456   return UpdateDeintMethod(method);
2457 }
2458 
2459 
UpdateDeintMethod(EINTERLACEMETHOD method)2460 bool CVppPostproc::UpdateDeintMethod(EINTERLACEMETHOD method)
2461 {
2462   if (method == m_vppMethod)
2463   {
2464     return true;
2465   }
2466 
2467   m_vppMethod = method;
2468   m_forwardRefs = 0;
2469   m_backwardRefs = 0;
2470 
2471   if (m_filter != VA_INVALID_ID)
2472   {
2473     CheckSuccess(vaDestroyBuffer(m_config.dpy, m_filter), "vaDestroyBuffer");
2474     m_filter = VA_INVALID_ID;
2475   }
2476 
2477   VAProcDeinterlacingType vppMethod;
2478   switch (method)
2479   {
2480   case VS_INTERLACEMETHOD_VAAPI_BOB:
2481     vppMethod = VAProcDeinterlacingBob;
2482     m_config.processInfo->SetVideoDeintMethod("vaapi-bob");
2483     break;
2484   case VS_INTERLACEMETHOD_VAAPI_MADI:
2485     vppMethod = VAProcDeinterlacingMotionAdaptive;
2486     m_config.processInfo->SetVideoDeintMethod("vaapi-madi");
2487     break;
2488   case VS_INTERLACEMETHOD_VAAPI_MACI:
2489     vppMethod = VAProcDeinterlacingMotionCompensated;
2490     m_config.processInfo->SetVideoDeintMethod("vaapi-mcdi");
2491     break;
2492   case VS_INTERLACEMETHOD_NONE:
2493     // Early exit, filter parameter buffer not needed then
2494     m_config.processInfo->SetVideoDeintMethod("vaapi-none");
2495     return true;
2496   default:
2497     m_config.processInfo->SetVideoDeintMethod("unknown");
2498     return false;
2499   }
2500 
2501   VAProcFilterParameterBufferDeinterlacing filterparams;
2502   filterparams.type = VAProcFilterDeinterlacing;
2503   filterparams.algorithm = vppMethod;
2504   filterparams.flags = 0;
2505 
2506   if (!CheckSuccess(vaCreateBuffer(m_config.dpy, m_contextId, VAProcFilterParameterBufferType,
2507       sizeof(filterparams), 1,
2508       &filterparams, &m_filter), "vaCreateBuffer"))
2509   {
2510     m_filter = VA_INVALID_ID;
2511     return false;
2512   }
2513 
2514   VAProcPipelineCaps pplCaps;
2515   if (!CheckSuccess(vaQueryVideoProcPipelineCaps(m_config.dpy, m_contextId, &m_filter, 1, &pplCaps),
2516       "vaQueryVideoProcPipelineCaps"))
2517   {
2518     return false;
2519   }
2520 
2521   m_forwardRefs = pplCaps.num_forward_references;
2522   m_backwardRefs = pplCaps.num_backward_references;
2523 
2524   return true;
2525 }
2526 
Dispose()2527 void CVppPostproc::Dispose()
2528 {
2529   // make sure surfaces are idle
2530   for (int i=0; i<m_videoSurfaces.Size(); i++)
2531   {
2532     CheckSuccess(vaSyncSurface(m_config.dpy, m_videoSurfaces.GetAtIndex(i)), "vaSyncSurface");
2533   }
2534 
2535   if (m_filter != VA_INVALID_ID)
2536   {
2537     CheckSuccess(vaDestroyBuffer(m_config.dpy, m_filter), "vaDestroyBuffer");
2538     m_filter = VA_INVALID_ID;
2539   }
2540   if (m_contextId != VA_INVALID_ID)
2541   {
2542     CheckSuccess(vaDestroyContext(m_config.dpy, m_contextId), "vaDestroyContext");
2543     m_contextId = VA_INVALID_ID;
2544   }
2545   VASurfaceID surf;
2546   while((surf = m_videoSurfaces.RemoveNext()) != VA_INVALID_SURFACE)
2547   {
2548     CheckSuccess(vaDestroySurfaces(m_config.dpy, &surf, 1), "vaDestroySurface");
2549   }
2550   m_videoSurfaces.Reset();
2551 
2552   if (m_configId != VA_INVALID_ID)
2553   {
2554     CheckSuccess(vaDestroyConfig(m_config.dpy, m_configId), "vaDestroyConfig");
2555     m_configId = VA_INVALID_ID;
2556   }
2557 
2558   // release all decoded pictures
2559   Flush();
2560 }
2561 
AddPicture(CVaapiDecodedPicture & pic)2562 bool CVppPostproc::AddPicture(CVaapiDecodedPicture &pic)
2563 {
2564   pic.index = m_frameCount;
2565   m_decodedPics.push_front(pic);
2566   m_frameCount++;
2567   m_step = 0;
2568   m_config.stats->SetCanSkipDeint(true);
2569   return true;
2570 }
2571 
Filter(CVaapiProcessedPicture & outPic)2572 bool CVppPostproc::Filter(CVaapiProcessedPicture &outPic)
2573 {
2574   if (m_step>1)
2575   {
2576     Advance();
2577     return false;
2578   }
2579 
2580   // we need a free render target
2581   VASurfaceID surf = m_videoSurfaces.GetFree(VA_INVALID_SURFACE);
2582   if (surf == VA_INVALID_SURFACE)
2583   {
2584     CLog::Log(LOGERROR, "VAAPI - VPP - no free render target");
2585     return false;
2586   }
2587   // clear reference in case we return false
2588   m_videoSurfaces.ClearReference(surf);
2589 
2590   // move window of frames we are looking at to account for backward (=future) refs
2591   const auto currentIdx = m_currentIdx - m_backwardRefs;
2592 
2593   // make sure we have all needed forward refs
2594   if ((currentIdx - m_forwardRefs) < m_decodedPics.back().index)
2595   {
2596     Advance();
2597     return false;
2598   }
2599 
2600   auto it = std::find_if(m_decodedPics.begin(), m_decodedPics.end(),
2601                          [currentIdx](const CVaapiDecodedPicture &picture){
2602                            return picture.index == currentIdx;
2603                          });
2604   if (it==m_decodedPics.end())
2605   {
2606     return false;
2607   }
2608   outPic.DVDPic.SetParams(it->DVDPic);
2609 
2610   // skip deinterlacing cycle if requested
2611   if ((m_step == 1) &&
2612       ((outPic.DVDPic.iFlags & DVD_CODEC_CTRL_SKIPDEINT) || !(outPic.DVDPic.iFlags & DVP_FLAG_INTERLACED) || (m_vppMethod == VS_INTERLACEMETHOD_NONE)))
2613   {
2614     Advance();
2615     return false;
2616   }
2617 
2618   // vpp deinterlacing
2619   VAProcFilterParameterBufferDeinterlacing *filterParams;
2620   VABufferID pipelineBuf;
2621   VAProcPipelineParameterBuffer *pipelineParams;
2622   VARectangle inputRegion;
2623   VARectangle outputRegion;
2624 
2625   if (!CheckSuccess(vaBeginPicture(m_config.dpy, m_contextId, surf), "vaBeginPicture"))
2626   {
2627     return false;
2628   }
2629 
2630   if (!CheckSuccess(vaCreateBuffer(m_config.dpy, m_contextId, VAProcPipelineParameterBufferType,
2631           sizeof(VAProcPipelineParameterBuffer), 1, NULL, &pipelineBuf), "vaCreateBuffer"))
2632   {
2633     return false;
2634   }
2635   if (!CheckSuccess(vaMapBuffer(m_config.dpy, pipelineBuf, (void**) &pipelineParams),
2636       "vaMapBuffer"))
2637   {
2638     return false;
2639   }
2640   memset(pipelineParams, 0, sizeof(VAProcPipelineParameterBuffer));
2641 
2642   inputRegion.x = outputRegion.x = 0;
2643   inputRegion.y = outputRegion.y = 0;
2644   inputRegion.width = outputRegion.width = m_config.surfaceWidth;
2645   inputRegion.height = outputRegion.height = m_config.surfaceHeight;
2646 
2647   pipelineParams->output_region = &outputRegion;
2648   pipelineParams->surface_region = &inputRegion;
2649   pipelineParams->output_background_color = 0xff000000;
2650   pipelineParams->filter_flags = 0;
2651 
2652   VASurfaceID forwardRefs[32];
2653   VASurfaceID backwardRefs[32];
2654   pipelineParams->forward_references = forwardRefs;
2655   pipelineParams->backward_references = backwardRefs;
2656   pipelineParams->num_forward_references = 0;
2657   pipelineParams->num_backward_references = 0;
2658 
2659   int maxPic = currentIdx + m_backwardRefs;
2660   int minPic = currentIdx - m_forwardRefs;
2661   int curPic = currentIdx;
2662 
2663   // deinterlace flag
2664   if (m_vppMethod != VS_INTERLACEMETHOD_NONE)
2665   {
2666     unsigned int flags = 0;
2667 
2668     if (it->DVDPic.iFlags & DVP_FLAG_INTERLACED)
2669     {
2670       if (it->DVDPic.iFlags & DVP_FLAG_TOP_FIELD_FIRST)
2671         flags = 0;
2672       else
2673         flags = VA_DEINTERLACING_BOTTOM_FIELD_FIRST | VA_DEINTERLACING_BOTTOM_FIELD;
2674 
2675       if (m_step)
2676       {
2677         if (flags & VA_DEINTERLACING_BOTTOM_FIELD)
2678           flags &= ~VA_DEINTERLACING_BOTTOM_FIELD;
2679         else
2680           flags |= VA_DEINTERLACING_BOTTOM_FIELD;
2681       }
2682     }
2683     if (!CheckSuccess(vaMapBuffer(m_config.dpy, m_filter, (void**) &filterParams), "vaMapBuffer"))
2684     {
2685       return false;
2686     }
2687     filterParams->flags = flags;
2688     if (!CheckSuccess(vaUnmapBuffer(m_config.dpy, m_filter), "vaUnmapBuffer"))
2689     {
2690       return false;
2691     }
2692 
2693     pipelineParams->filters = &m_filter;
2694     pipelineParams->num_filters = 1;
2695   }
2696   else
2697   {
2698     pipelineParams->num_filters = 0;
2699   }
2700 
2701   // references
2702   double ptsLast = DVD_NOPTS_VALUE;
2703   double pts = DVD_NOPTS_VALUE;
2704 
2705   pipelineParams->surface = VA_INVALID_SURFACE;
2706   for (const auto &picture : m_decodedPics)
2707   {
2708     if (picture.index >= minPic && picture.index <= maxPic)
2709     {
2710       if (picture.index > curPic)
2711       {
2712         backwardRefs[(picture.index - curPic) - 1] = picture.videoSurface;
2713         pipelineParams->num_backward_references++;
2714       }
2715       else if (picture.index == curPic)
2716       {
2717         pipelineParams->surface = picture.videoSurface;
2718         pts = picture.DVDPic.pts;
2719       }
2720       if (picture.index < curPic)
2721       {
2722         forwardRefs[(curPic - picture.index) - 1] = picture.videoSurface;
2723         pipelineParams->num_forward_references++;
2724         if (picture.index == curPic - 1)
2725           ptsLast = picture.DVDPic.pts;
2726       }
2727     }
2728   }
2729 
2730   // set pts for 2nd frame
2731   if (m_step && pts != DVD_NOPTS_VALUE && ptsLast != DVD_NOPTS_VALUE)
2732     outPic.DVDPic.pts += (pts-ptsLast)/2;
2733 
2734   if (pipelineParams->surface == VA_INVALID_SURFACE)
2735     return false;
2736 
2737   if (!CheckSuccess(vaUnmapBuffer(m_config.dpy, pipelineBuf), "vaUnmmapBuffer"))
2738   {
2739     return false;
2740   }
2741 
2742   if (!CheckSuccess(vaRenderPicture(m_config.dpy, m_contextId, &pipelineBuf, 1), "vaRenderPicture"))
2743   {
2744     return false;
2745   }
2746 
2747   if (!CheckSuccess(vaEndPicture(m_config.dpy, m_contextId), "vaEndPicture"))
2748   {
2749     return false;
2750   }
2751 
2752   if (!CheckSuccess(vaDestroyBuffer(m_config.dpy, pipelineBuf), "vaDestroyBuffer"))
2753   {
2754     return false;
2755   }
2756 
2757   m_step++;
2758   outPic.videoSurface = m_videoSurfaces.GetFree(surf);
2759   outPic.source = this;
2760   outPic.DVDPic.iFlags &= ~(DVP_FLAG_TOP_FIELD_FIRST |
2761                             DVP_FLAG_REPEAT_TOP_FIELD |
2762                             DVP_FLAG_INTERLACED);
2763 
2764   return true;
2765 }
2766 
Advance()2767 void CVppPostproc::Advance()
2768 {
2769   m_currentIdx++;
2770 
2771   // release all unneeded refs
2772   auto it = m_decodedPics.begin();
2773   while (it != m_decodedPics.end())
2774   {
2775     if (it->index < m_currentIdx - m_forwardRefs - m_backwardRefs)
2776     {
2777       m_config.videoSurfaces->ClearRender(it->videoSurface);
2778       it = m_decodedPics.erase(it);
2779     }
2780     else
2781       ++it;
2782   }
2783 }
2784 
ClearRef(CVaapiProcessedPicture & pic)2785 void CVppPostproc::ClearRef(CVaapiProcessedPicture &pic)
2786 {
2787   m_videoSurfaces.ClearReference(pic.videoSurface);
2788 
2789   if (m_pOut && !m_videoSurfaces.HasRefs())
2790     (m_pOut->*m_cbDispose)(this);
2791 }
2792 
Flush()2793 void CVppPostproc::Flush()
2794 {
2795   // release all decoded pictures
2796   auto it = m_decodedPics.begin();
2797   while (it != m_decodedPics.end())
2798   {
2799     m_config.videoSurfaces->ClearRender(it->videoSurface);
2800     it = m_decodedPics.erase(it);
2801   }
2802 }
2803 
DoesSync()2804 bool CVppPostproc::DoesSync()
2805 {
2806   return false;
2807 }
2808 
WantsPic()2809 bool CVppPostproc::WantsPic()
2810 {
2811   // need at least 2 for deinterlacing
2812   if (m_videoSurfaces.NumFree() > 1)
2813     return true;
2814 
2815   return false;
2816 }
2817 
UseVideoSurface()2818 bool CVppPostproc::UseVideoSurface()
2819 {
2820   return true;
2821 }
2822 
Discard(COutput * output,ReadyToDispose cb)2823 void CVppPostproc::Discard(COutput *output, ReadyToDispose cb)
2824 {
2825   m_pOut = output;
2826   m_cbDispose = cb;
2827   if (!m_videoSurfaces.HasRefs())
2828     (m_pOut->*m_cbDispose)(this);
2829 }
2830 
CheckSuccess(VAStatus status,const std::string & function)2831 bool CVppPostproc::CheckSuccess(VAStatus status, const std::string& function)
2832 {
2833   if (status != VA_STATUS_SUCCESS)
2834   {
2835     CLog::Log(LOGERROR, "VAAPI/vpp {} error: {} ({})", function, vaErrorStr(status), status);
2836     return false;
2837   }
2838   return true;
2839 }
2840 
2841 //-----------------------------------------------------------------------------
2842 // FFmpeg Postprocessing
2843 //-----------------------------------------------------------------------------
2844 
2845 #define CACHED_BUFFER_SIZE 4096
2846 
CFFmpegPostproc()2847 CFFmpegPostproc::CFFmpegPostproc()
2848 {
2849   m_cache = NULL;
2850   m_pFilterFrameIn = NULL;
2851   m_pFilterFrameOut = NULL;
2852   m_pFilterGraph = NULL;
2853   m_DVDPic.pts = DVD_NOPTS_VALUE;
2854   m_frametime = 0;
2855   m_lastOutPts = DVD_NOPTS_VALUE;
2856 }
2857 
~CFFmpegPostproc()2858 CFFmpegPostproc::~CFFmpegPostproc()
2859 {
2860   Close();
2861   KODI::MEMORY::AlignedFree(m_cache);
2862   m_dllSSE4.Unload();
2863   av_frame_free(&m_pFilterFrameIn);
2864   av_frame_free(&m_pFilterFrameOut);
2865 }
2866 
PreInit(CVaapiConfig & config,SDiMethods * methods)2867 bool CFFmpegPostproc::PreInit(CVaapiConfig &config, SDiMethods *methods)
2868 {
2869   m_config = config;
2870   bool use_filter = true;
2871 
2872   // copying large surfaces via sse4 is a bit slow
2873   // we just return false here as the primary use case the
2874   // sse4 copy method is deinterlacing of max 1080i content
2875   if (m_config.vidWidth > 1920 || m_config.vidHeight > 1088)
2876     return false;
2877 
2878   VAImage image;
2879   image.image_id = VA_INVALID_ID;
2880   VASurfaceID surface = config.videoSurfaces->GetAtIndex(0);
2881   VAStatus status = vaDeriveImage(config.dpy, surface, &image);
2882   if (status != VA_STATUS_SUCCESS)
2883   {
2884     CLog::Log(LOGINFO, "VAAPI::SupportsFilter vaDeriveImage not supported by driver - ffmpeg postprocessing and CPU-copy rendering will not be available");
2885     use_filter = false;
2886   }
2887   if (use_filter && (image.format.fourcc != VA_FOURCC_NV12))
2888   {
2889     CLog::Log(LOGWARNING,"VAAPI::SupportsFilter image format not NV12");
2890     use_filter = false;
2891   }
2892   if (use_filter && ((image.pitches[0] % 64) || (image.pitches[1] % 64)))
2893   {
2894     CLog::Log(LOGWARNING,"VAAPI::SupportsFilter patches no multiple of 64");
2895     use_filter = false;
2896   }
2897   if (image.image_id != VA_INVALID_ID)
2898     CheckSuccess(vaDestroyImage(config.dpy, image.image_id), "vaDestroyImage");
2899 
2900   if (use_filter && !m_dllSSE4.Load())
2901   {
2902     CLog::Log(LOGERROR,"VAAPI::SupportsFilter failed loading sse4 lib");
2903     use_filter = false;
2904   }
2905 
2906   if (use_filter)
2907   {
2908     m_cache = static_cast<uint8_t*>(KODI::MEMORY::AlignedMalloc(CACHED_BUFFER_SIZE, 64));
2909     if (methods)
2910     {
2911       methods->diMethods[methods->numDiMethods++] = VS_INTERLACEMETHOD_DEINTERLACE;
2912       methods->diMethods[methods->numDiMethods++] = VS_INTERLACEMETHOD_RENDER_BOB;
2913     }
2914   }
2915   return use_filter;
2916 }
2917 
Init(EINTERLACEMETHOD method)2918 bool CFFmpegPostproc::Init(EINTERLACEMETHOD method)
2919 {
2920   if (!(m_pFilterGraph = avfilter_graph_alloc()))
2921   {
2922     CLog::Log(LOGERROR, "VAAPI::CFFmpegPostproc::Init - unable to alloc filter graph");
2923     return false;
2924   }
2925 
2926   const AVFilter* srcFilter = avfilter_get_by_name("buffer");
2927   const AVFilter* outFilter = avfilter_get_by_name("buffersink");
2928 
2929   std::string args = StringUtils::Format("%d:%d:%d:%d:%d:%d:%d",
2930                                         m_config.vidWidth,
2931                                         m_config.vidHeight,
2932                                         AV_PIX_FMT_NV12,
2933                                         1,
2934                                         1,
2935                                         (m_config.aspect.num != 0) ? m_config.aspect.num : 1,
2936                                         (m_config.aspect.num != 0) ? m_config.aspect.den : 1);
2937 
2938   if (avfilter_graph_create_filter(&m_pFilterIn, srcFilter, "src", args.c_str(), NULL, m_pFilterGraph) < 0)
2939   {
2940     CLog::Log(LOGERROR, "VAAPI::CFFmpegPostproc::Init - avfilter_graph_create_filter: src");
2941     return false;
2942   }
2943 
2944   if (avfilter_graph_create_filter(&m_pFilterOut, outFilter, "out", NULL, NULL, m_pFilterGraph) < 0)
2945   {
2946     CLog::Log(LOGERROR, "CFFmpegPostproc::Init  - avfilter_graph_create_filter: out");
2947     return false;
2948   }
2949 
2950   enum AVPixelFormat pix_fmts[] = { AV_PIX_FMT_NV12, AV_PIX_FMT_NONE };
2951   if (av_opt_set_int_list(m_pFilterOut, "pix_fmts", pix_fmts,  AV_PIX_FMT_NONE, AV_OPT_SEARCH_CHILDREN) < 0)
2952   {
2953     CLog::Log(LOGERROR, "VAAPI::CFFmpegPostproc::Init  - failed settings pix formats");
2954     return false;
2955   }
2956 
2957   AVFilterInOut* outputs = avfilter_inout_alloc();
2958   AVFilterInOut* inputs  = avfilter_inout_alloc();
2959 
2960   outputs->name    = av_strdup("in");
2961   outputs->filter_ctx = m_pFilterIn;
2962   outputs->pad_idx = 0;
2963   outputs->next    = NULL;
2964 
2965   inputs->name    = av_strdup("out");
2966   inputs->filter_ctx = m_pFilterOut;
2967   inputs->pad_idx = 0;
2968   inputs->next    = NULL;
2969 
2970   if (method == VS_INTERLACEMETHOD_DEINTERLACE)
2971   {
2972     std::string filter;
2973 
2974     filter = "yadif=1:-1";
2975 
2976     if (avfilter_graph_parse_ptr(m_pFilterGraph, filter.c_str(), &inputs, &outputs, NULL) < 0)
2977     {
2978       CLog::Log(LOGERROR, "VAAPI::CFFmpegPostproc::Init  - avfilter_graph_parse");
2979       avfilter_inout_free(&outputs);
2980       avfilter_inout_free(&inputs);
2981       return false;
2982     }
2983 
2984     avfilter_inout_free(&outputs);
2985     avfilter_inout_free(&inputs);
2986 
2987     if (avfilter_graph_config(m_pFilterGraph, NULL) < 0)
2988     {
2989       CLog::Log(LOGERROR, "VAAPI::CFFmpegPostproc::Init  - avfilter_graph_config");
2990       return false;
2991     }
2992 
2993     m_config.processInfo->SetVideoDeintMethod("yadif");
2994   }
2995   else if (method == VS_INTERLACEMETHOD_RENDER_BOB ||
2996            method == VS_INTERLACEMETHOD_NONE)
2997   {
2998     CLog::Log(LOGDEBUG, LOGVIDEO, "VAAPI::CFFmpegPostproc::Init  - skip deinterlacing");
2999     avfilter_inout_free(&outputs);
3000     avfilter_inout_free(&inputs);
3001     m_config.processInfo->SetVideoDeintMethod("none");
3002   }
3003   else
3004   {
3005     avfilter_inout_free(&outputs);
3006     avfilter_inout_free(&inputs);
3007     m_config.processInfo->SetVideoDeintMethod("unknown");
3008     return false;
3009   }
3010   m_diMethod = method;
3011 
3012   m_pFilterFrameIn = av_frame_alloc();
3013   m_pFilterFrameOut = av_frame_alloc();
3014   return true;
3015 }
3016 
AddPicture(CVaapiDecodedPicture & inPic)3017 bool CFFmpegPostproc::AddPicture(CVaapiDecodedPicture &inPic)
3018 {
3019   VASurfaceID surf = inPic.videoSurface;
3020   VAImage image;
3021   uint8_t *buf;
3022   if (m_DVDPic.pts != DVD_NOPTS_VALUE && inPic.DVDPic.pts != DVD_NOPTS_VALUE)
3023   {
3024     m_frametime = inPic.DVDPic.pts - m_DVDPic.pts;
3025   }
3026   m_DVDPic.SetParams(inPic.DVDPic);
3027   bool result = false;
3028 
3029   if (!CheckSuccess(vaSyncSurface(m_config.dpy, surf), "vaSyncSurface"))
3030     goto error;
3031 
3032   if (!CheckSuccess(vaDeriveImage(m_config.dpy, surf, &image), "vaDeriveImage"))
3033     goto error;
3034 
3035   if (!CheckSuccess(vaMapBuffer(m_config.dpy, image.buf, (void**) &buf), "vaMapBuffer"))
3036     goto error;
3037 
3038   m_pFilterFrameIn->format = AV_PIX_FMT_NV12;
3039   m_pFilterFrameIn->width = m_config.vidWidth;
3040   m_pFilterFrameIn->height = m_config.vidHeight;
3041   m_pFilterFrameIn->linesize[0] = image.pitches[0];
3042   m_pFilterFrameIn->linesize[1] = image.pitches[1];
3043   m_pFilterFrameIn->interlaced_frame = (inPic.DVDPic.iFlags & DVP_FLAG_INTERLACED) ? 1 : 0;
3044   m_pFilterFrameIn->top_field_first = (inPic.DVDPic.iFlags & DVP_FLAG_TOP_FIELD_FIRST) ? 1 : 0;
3045 
3046   if (inPic.DVDPic.pts == DVD_NOPTS_VALUE)
3047     m_pFilterFrameIn->pts = AV_NOPTS_VALUE;
3048   else
3049     m_pFilterFrameIn->pts = (inPic.DVDPic.pts / DVD_TIME_BASE) * AV_TIME_BASE;
3050 
3051   m_pFilterFrameIn->pkt_dts = m_pFilterFrameIn->pts;
3052   m_pFilterFrameIn->best_effort_timestamp = m_pFilterFrameIn->pts;
3053 
3054   av_frame_get_buffer(m_pFilterFrameIn, 64);
3055 
3056   uint8_t *src, *dst;
3057   src = buf + image.offsets[0];
3058   dst = m_pFilterFrameIn->data[0];
3059   m_dllSSE4.copy_frame(src, dst, m_cache, m_config.vidWidth, m_config.vidHeight, image.pitches[0]);
3060 
3061   src = buf + image.offsets[1];
3062   dst = m_pFilterFrameIn->data[1];
3063   m_dllSSE4.copy_frame(src, dst, m_cache, image.width, image.height/2, image.pitches[1]);
3064 
3065   m_pFilterFrameIn->linesize[0] = image.pitches[0];
3066   m_pFilterFrameIn->linesize[1] = image.pitches[1];
3067   m_pFilterFrameIn->data[2] = NULL;
3068   m_pFilterFrameIn->data[3] = NULL;
3069   m_pFilterFrameIn->pkt_size = image.data_size;
3070 
3071   CheckSuccess(vaUnmapBuffer(m_config.dpy, image.buf), "vaUnmapBuffer");
3072   CheckSuccess(vaDestroyImage(m_config.dpy, image.image_id), "vaDestroyImage");
3073 
3074   if (m_diMethod == VS_INTERLACEMETHOD_DEINTERLACE)
3075   {
3076     if (av_buffersrc_add_frame(m_pFilterIn, m_pFilterFrameIn) < 0)
3077     {
3078       CLog::Log(LOGERROR, "CFFmpegPostproc::AddPicture - av_buffersrc_add_frame");
3079       goto error;
3080     }
3081   }
3082   else if (m_diMethod == VS_INTERLACEMETHOD_RENDER_BOB ||
3083            m_diMethod == VS_INTERLACEMETHOD_NONE)
3084   {
3085     av_frame_move_ref(m_pFilterFrameOut, m_pFilterFrameIn);
3086     m_step = 0;
3087   }
3088   av_frame_unref(m_pFilterFrameIn);
3089 
3090   result = true;
3091 
3092 error:
3093   m_config.videoSurfaces->ClearRender(surf);
3094   return result;
3095 }
3096 
Filter(CVaapiProcessedPicture & outPic)3097 bool CFFmpegPostproc::Filter(CVaapiProcessedPicture &outPic)
3098 {
3099   outPic.DVDPic.SetParams(m_DVDPic);
3100   if (m_diMethod == VS_INTERLACEMETHOD_DEINTERLACE)
3101   {
3102     int result;
3103     result = av_buffersink_get_frame(m_pFilterOut, m_pFilterFrameOut);
3104 
3105     if(result  == AVERROR(EAGAIN) || result == AVERROR_EOF)
3106       return false;
3107     else if(result < 0)
3108     {
3109       CLog::Log(LOGERROR, "CFFmpegPostproc::Filter - av_buffersink_get_frame");
3110       return false;
3111     }
3112     outPic.DVDPic.iFlags &= ~(DVP_FLAG_TOP_FIELD_FIRST |
3113                               DVP_FLAG_REPEAT_TOP_FIELD |
3114                               DVP_FLAG_INTERLACED);
3115   }
3116   else if (m_diMethod == VS_INTERLACEMETHOD_RENDER_BOB ||
3117            m_diMethod == VS_INTERLACEMETHOD_NONE)
3118   {
3119     if (m_step > 0)
3120       return false;
3121   }
3122 
3123   m_step++;
3124   outPic.frame = av_frame_clone(m_pFilterFrameOut);
3125   av_frame_unref(m_pFilterFrameOut);
3126 
3127   outPic.source = this;
3128   m_refsToPics++;
3129 
3130   int64_t bpts = outPic.frame->best_effort_timestamp;
3131   if(bpts != AV_NOPTS_VALUE)
3132   {
3133     outPic.DVDPic.pts = (double)bpts * DVD_TIME_BASE / AV_TIME_BASE;
3134   }
3135   else
3136     outPic.DVDPic.pts = DVD_NOPTS_VALUE;
3137 
3138   double pts = outPic.DVDPic.pts;
3139   if (m_lastOutPts != DVD_NOPTS_VALUE && m_lastOutPts == pts)
3140   {
3141     outPic.DVDPic.pts += m_frametime/2;
3142   }
3143   m_lastOutPts = pts;
3144 
3145   return true;
3146 }
3147 
ClearRef(CVaapiProcessedPicture & pic)3148 void CFFmpegPostproc::ClearRef(CVaapiProcessedPicture &pic)
3149 {
3150   av_frame_free(&pic.frame);
3151   m_refsToPics--;
3152 
3153   if (m_pOut && m_refsToPics <= 0 && m_cbDispose)
3154     (m_pOut->*m_cbDispose)(this);
3155 }
3156 
Close()3157 void CFFmpegPostproc::Close()
3158 {
3159   if (m_pFilterGraph)
3160   {
3161     avfilter_graph_free(&m_pFilterGraph);
3162   }
3163 }
3164 
Flush()3165 void CFFmpegPostproc::Flush()
3166 {
3167   Close();
3168   Init(m_diMethod);
3169   m_DVDPic.pts = DVD_NOPTS_VALUE;
3170   m_frametime = 0;
3171   m_lastOutPts = DVD_NOPTS_VALUE;
3172 }
3173 
UpdateDeintMethod(EINTERLACEMETHOD method)3174 bool CFFmpegPostproc::UpdateDeintMethod(EINTERLACEMETHOD method)
3175 {
3176   /// \todo switching between certain methods could be done without deinit/init
3177   return (m_diMethod == method);
3178 }
3179 
DoesSync()3180 bool CFFmpegPostproc::DoesSync()
3181 {
3182   return true;
3183 }
3184 
UseVideoSurface()3185 bool CFFmpegPostproc::UseVideoSurface()
3186 {
3187   return false;
3188 }
3189 
Discard(COutput * output,ReadyToDispose cb)3190 void CFFmpegPostproc::Discard(COutput *output, ReadyToDispose cb)
3191 {
3192   m_pOut = output;
3193   m_cbDispose = cb;
3194   if (m_refsToPics <= 0)
3195     (m_pOut->*m_cbDispose)(this);
3196 }
3197 
CheckSuccess(VAStatus status,const std::string & function)3198 bool CFFmpegPostproc::CheckSuccess(VAStatus status, const std::string& function)
3199 {
3200   if (status != VA_STATUS_SUCCESS)
3201   {
3202     CLog::Log(LOGERROR, "VAAPI/ffpp error: {} ({})", function, vaErrorStr(status), status);
3203     return false;
3204   }
3205   return true;
3206 }
3207