1 /*
2  *  Copyright (C) 2005-2018 Team Kodi
3  *  This file is part of Kodi - https://kodi.tv
4  *
5  *  SPDX-License-Identifier: GPL-2.0-or-later
6  *  See LICENSES/README.md for more information.
7  */
8 
9 #include "VDPAU.h"
10 
11 #include "Application.h"
12 #include "DVDCodecs/DVDCodecUtils.h"
13 #include "ServiceBroker.h"
14 #include "cores/VideoPlayer/DVDCodecs/DVDFactoryCodec.h"
15 #include "cores/VideoPlayer/Interface/TimingConstants.h"
16 #include "cores/VideoPlayer/Process/ProcessInfo.h"
17 #include "cores/VideoPlayer/VideoRenderers/RenderFlags.h"
18 #include "cores/VideoPlayer/VideoRenderers/RenderManager.h"
19 #include "guilib/TextureManager.h"
20 #include "rendering/RenderSystem.h"
21 #include "settings/AdvancedSettings.h"
22 #include "settings/Settings.h"
23 #include "settings/SettingsComponent.h"
24 #include "settings/lib/Setting.h"
25 #include "utils/MathUtils.h"
26 #include "utils/TimeUtils.h"
27 #include "utils/log.h"
28 #include "windowing/GraphicContext.h"
29 #include "windowing/X11/WinSystemX11.h"
30 
31 #include <dlfcn.h>
32 
33 using namespace Actor;
34 using namespace VDPAU;
35 #define NUM_RENDER_PICS 7
36 #define NUM_CROP_PIX 3
37 
38 #define ARSIZE(x) (sizeof(x) / sizeof((x)[0]))
39 
40 CDecoder::Desc decoder_profiles[] = {
41 {"MPEG1",        VDP_DECODER_PROFILE_MPEG1},
42 {"MPEG2_SIMPLE", VDP_DECODER_PROFILE_MPEG2_SIMPLE},
43 {"MPEG2_MAIN",   VDP_DECODER_PROFILE_MPEG2_MAIN},
44 {"H264_BASELINE",VDP_DECODER_PROFILE_H264_BASELINE},
45 {"H264_MAIN",    VDP_DECODER_PROFILE_H264_MAIN},
46 {"H264_HIGH",    VDP_DECODER_PROFILE_H264_HIGH},
47 {"VC1_SIMPLE",   VDP_DECODER_PROFILE_VC1_SIMPLE},
48 {"VC1_MAIN",     VDP_DECODER_PROFILE_VC1_MAIN},
49 {"VC1_ADVANCED", VDP_DECODER_PROFILE_VC1_ADVANCED},
50 {"MPEG4_PART2_ASP", VDP_DECODER_PROFILE_MPEG4_PART2_ASP},
51 #ifdef VDP_DECODER_PROFILE_HEVC_MAIN
52 {"HEVC_MAIN", VDP_DECODER_PROFILE_HEVC_MAIN},
53 #endif
54 #ifdef VDP_DECODER_PROFILE_VP9_PROFILE_0
55 {"VP9_PROFILE_0", VDP_DECODER_PROFILE_VP9_PROFILE_0},
56 #endif
57 };
58 
59 static struct SInterlaceMapping
60 {
61   const EINTERLACEMETHOD     method;
62   const VdpVideoMixerFeature feature;
63 } g_interlace_mapping[] =
64 { {VS_INTERLACEMETHOD_VDPAU_TEMPORAL             , VDP_VIDEO_MIXER_FEATURE_DEINTERLACE_TEMPORAL}
65 , {VS_INTERLACEMETHOD_VDPAU_TEMPORAL_HALF        , VDP_VIDEO_MIXER_FEATURE_DEINTERLACE_TEMPORAL}
66 , {VS_INTERLACEMETHOD_VDPAU_TEMPORAL_SPATIAL     , VDP_VIDEO_MIXER_FEATURE_DEINTERLACE_TEMPORAL_SPATIAL}
67 , {VS_INTERLACEMETHOD_VDPAU_TEMPORAL_SPATIAL_HALF, VDP_VIDEO_MIXER_FEATURE_DEINTERLACE_TEMPORAL_SPATIAL}
68 , {VS_INTERLACEMETHOD_VDPAU_INVERSE_TELECINE     , VDP_VIDEO_MIXER_FEATURE_INVERSE_TELECINE}
69 , {VS_INTERLACEMETHOD_NONE                       , (VdpVideoMixerFeature)-1}
70 };
71 
72 static float studioCSCKCoeffs601[3] = {0.299, 0.587, 0.114}; //BT601 {Kr, Kg, Kb}
73 static float studioCSCKCoeffs709[3] = {0.2126, 0.7152, 0.0722}; //BT709 {Kr, Kg, Kb}
74 
75 //-----------------------------------------------------------------------------
76 //-----------------------------------------------------------------------------
77 
78 CVDPAUContext *CVDPAUContext::m_context = 0;
79 CCriticalSection CVDPAUContext::m_section;
80 Display *CVDPAUContext::m_display = 0;
81 void *CVDPAUContext::m_dlHandle = 0;
82 
CVDPAUContext()83 CVDPAUContext::CVDPAUContext()
84 {
85   m_context = 0;
86   m_refCount = 0;
87 }
88 
Release()89 void CVDPAUContext::Release()
90 {
91   CSingleLock lock(m_section);
92 
93   m_refCount--;
94   if (m_refCount <= 0)
95   {
96     Close();
97     delete this;
98     m_context = 0;
99   }
100 }
101 
Close()102 void CVDPAUContext::Close()
103 {
104   CLog::Log(LOGINFO, "VDPAU::Close - closing decoder context");
105   DestroyContext();
106 }
107 
EnsureContext(CVDPAUContext ** ctx)108 bool CVDPAUContext::EnsureContext(CVDPAUContext **ctx)
109 {
110   CSingleLock lock(m_section);
111 
112   if (m_context)
113   {
114     m_context->m_refCount++;
115     *ctx = m_context;
116     return true;
117   }
118 
119   m_context = new CVDPAUContext();
120   *ctx = m_context;
121   {
122     CSingleLock gLock(CServiceBroker::GetWinSystem()->GetGfxContext());
123     if (!m_context->LoadSymbols() || !m_context->CreateContext())
124     {
125       delete m_context;
126       m_context = 0;
127       *ctx = NULL;
128       return false;
129     }
130   }
131 
132   m_context->m_refCount++;
133 
134   *ctx = m_context;
135   return true;
136 }
137 
GetProcs()138 VDPAU_procs& CVDPAUContext::GetProcs()
139 {
140   return m_vdpProcs;
141 }
142 
GetFeatures()143 VdpVideoMixerFeature* CVDPAUContext::GetFeatures()
144 {
145   return m_vdpFeatures;
146 }
147 
GetFeatureCount()148 int CVDPAUContext::GetFeatureCount()
149 {
150   return m_featureCount;
151 }
152 
LoadSymbols()153 bool CVDPAUContext::LoadSymbols()
154 {
155   if (!m_dlHandle)
156   {
157     m_dlHandle  = dlopen("libvdpau.so.1", RTLD_LAZY);
158     if (!m_dlHandle)
159     {
160       const char* error = dlerror();
161       if (!error)
162         error = "dlerror() returned NULL";
163 
164       CLog::Log(LOGERROR,"VDPAU::LoadSymbols: Unable to get handle to lib: %s", error);
165       return false;
166     }
167   }
168 
169   char* error;
170   (void)dlerror();
171   dl_vdp_device_create_x11 = (VdpStatus (*)(Display*, int, VdpDevice*, VdpStatus (**)(VdpDevice, VdpFuncId, void**)))dlsym(m_dlHandle, (const char*)"vdp_device_create_x11");
172   error = dlerror();
173   if (error)
174   {
175     CLog::Log(LOGERROR,"(VDPAU) - %s in %s",error,__FUNCTION__);
176     m_vdpDevice = VDP_INVALID_HANDLE;
177     return false;
178   }
179   return true;
180 }
181 
CreateContext()182 bool CVDPAUContext::CreateContext()
183 {
184   CLog::Log(LOGINFO, "VDPAU::CreateContext - creating decoder context");
185 
186   int screen;
187   { CSingleLock lock(CServiceBroker::GetWinSystem()->GetGfxContext());
188 
189     if (!m_display)
190       m_display = XOpenDisplay(NULL);
191 
192     if (!m_display)
193       return false;
194 
195     screen = static_cast<KODI::WINDOWING::X11::CWinSystemX11*>(CServiceBroker::GetWinSystem())->GetScreen();
196   }
197 
198   VdpStatus vdp_st;
199   // Create Device
200   vdp_st = dl_vdp_device_create_x11(m_display,
201                                     screen,
202                                    &m_vdpDevice,
203                                    &m_vdpProcs.vdp_get_proc_address);
204 
205   CLog::Log(LOGINFO, "vdp_device = 0x%08x vdp_st = 0x%08x", m_vdpDevice, vdp_st);
206   if (vdp_st != VDP_STATUS_OK)
207   {
208     CLog::Log(LOGERROR,"(VDPAU) unable to init VDPAU - vdp_st = 0x%x.  Falling back.",vdp_st);
209     m_vdpDevice = VDP_INVALID_HANDLE;
210     return false;
211   }
212 
213   QueryProcs();
214   SpewHardwareAvailable();
215   return true;
216 }
217 
QueryProcs()218 void CVDPAUContext::QueryProcs()
219 {
220   VdpStatus vdp_st;
221 
222 #define VDP_PROC(id, proc) \
223   do { \
224     vdp_st = m_vdpProcs.vdp_get_proc_address(m_vdpDevice, id, (void**)&proc); \
225     if (vdp_st != VDP_STATUS_OK) \
226     { \
227       CLog::Log(LOGERROR, "CVDPAUContext::GetProcs - failed to get proc id"); \
228     } \
229   } while(0);
230 
231   VDP_PROC(VDP_FUNC_ID_GET_ERROR_STRING                    , m_vdpProcs.vdp_get_error_string);
232   VDP_PROC(VDP_FUNC_ID_DEVICE_DESTROY                      , m_vdpProcs.vdp_device_destroy);
233   VDP_PROC(VDP_FUNC_ID_GENERATE_CSC_MATRIX                 , m_vdpProcs.vdp_generate_csc_matrix);
234   VDP_PROC(VDP_FUNC_ID_VIDEO_SURFACE_CREATE                , m_vdpProcs.vdp_video_surface_create);
235   VDP_PROC(VDP_FUNC_ID_VIDEO_SURFACE_DESTROY               , m_vdpProcs.vdp_video_surface_destroy);
236   VDP_PROC(VDP_FUNC_ID_VIDEO_SURFACE_PUT_BITS_Y_CB_CR      , m_vdpProcs.vdp_video_surface_put_bits_y_cb_cr);
237   VDP_PROC(VDP_FUNC_ID_VIDEO_SURFACE_GET_BITS_Y_CB_CR      , m_vdpProcs.vdp_video_surface_get_bits_y_cb_cr);
238   VDP_PROC(VDP_FUNC_ID_OUTPUT_SURFACE_PUT_BITS_Y_CB_CR     , m_vdpProcs.vdp_output_surface_put_bits_y_cb_cr);
239   VDP_PROC(VDP_FUNC_ID_OUTPUT_SURFACE_PUT_BITS_NATIVE      , m_vdpProcs.vdp_output_surface_put_bits_native);
240   VDP_PROC(VDP_FUNC_ID_OUTPUT_SURFACE_CREATE               , m_vdpProcs.vdp_output_surface_create);
241   VDP_PROC(VDP_FUNC_ID_OUTPUT_SURFACE_DESTROY              , m_vdpProcs.vdp_output_surface_destroy);
242   VDP_PROC(VDP_FUNC_ID_OUTPUT_SURFACE_GET_BITS_NATIVE      , m_vdpProcs.vdp_output_surface_get_bits_native);
243   VDP_PROC(VDP_FUNC_ID_OUTPUT_SURFACE_RENDER_OUTPUT_SURFACE, m_vdpProcs.vdp_output_surface_render_output_surface);
244   VDP_PROC(VDP_FUNC_ID_OUTPUT_SURFACE_PUT_BITS_INDEXED     , m_vdpProcs.vdp_output_surface_put_bits_indexed);
245   VDP_PROC(VDP_FUNC_ID_VIDEO_MIXER_CREATE                  , m_vdpProcs.vdp_video_mixer_create);
246   VDP_PROC(VDP_FUNC_ID_VIDEO_MIXER_SET_FEATURE_ENABLES     , m_vdpProcs.vdp_video_mixer_set_feature_enables);
247   VDP_PROC(VDP_FUNC_ID_VIDEO_MIXER_DESTROY                 , m_vdpProcs.vdp_video_mixer_destroy);
248   VDP_PROC(VDP_FUNC_ID_VIDEO_MIXER_RENDER                  , m_vdpProcs.vdp_video_mixer_render);
249   VDP_PROC(VDP_FUNC_ID_VIDEO_MIXER_SET_ATTRIBUTE_VALUES    , m_vdpProcs.vdp_video_mixer_set_attribute_values);
250   VDP_PROC(VDP_FUNC_ID_VIDEO_MIXER_QUERY_PARAMETER_SUPPORT , m_vdpProcs.vdp_video_mixer_query_parameter_support);
251   VDP_PROC(VDP_FUNC_ID_VIDEO_MIXER_QUERY_FEATURE_SUPPORT   , m_vdpProcs.vdp_video_mixer_query_feature_support);
252   VDP_PROC(VDP_FUNC_ID_DECODER_CREATE                      , m_vdpProcs.vdp_decoder_create);
253   VDP_PROC(VDP_FUNC_ID_DECODER_DESTROY                     , m_vdpProcs.vdp_decoder_destroy);
254   VDP_PROC(VDP_FUNC_ID_DECODER_RENDER                      , m_vdpProcs.vdp_decoder_render);
255   VDP_PROC(VDP_FUNC_ID_DECODER_QUERY_CAPABILITIES          , m_vdpProcs.vdp_decoder_query_caps);
256 #undef VDP_PROC
257 }
258 
GetDevice()259 VdpDevice CVDPAUContext::GetDevice()
260 {
261   return m_vdpDevice;
262 }
263 
DestroyContext()264 void CVDPAUContext::DestroyContext()
265 {
266   if (!m_vdpProcs.vdp_device_destroy)
267     return;
268 
269   m_vdpProcs.vdp_device_destroy(m_vdpDevice);
270   m_vdpDevice = VDP_INVALID_HANDLE;
271 }
272 
SpewHardwareAvailable()273 void CVDPAUContext::SpewHardwareAvailable()  //Copyright (c) 2008 Wladimir J. van der Laan  -- VDPInfo
274 {
275   VdpStatus rv;
276   CLog::Log(LOGINFO, "VDPAU Decoder capabilities:");
277   CLog::Log(LOGINFO, "name          level macbs width height");
278   CLog::Log(LOGINFO, "------------------------------------");
279   for(const CDecoder::Desc& decoder_profile : decoder_profiles)
280   {
281     VdpBool is_supported = false;
282     uint32_t max_level, max_macroblocks, max_width, max_height;
283     rv = m_vdpProcs.vdp_decoder_query_caps(m_vdpDevice, decoder_profile.id,
284                                 &is_supported, &max_level, &max_macroblocks, &max_width, &max_height);
285     if(rv == VDP_STATUS_OK && is_supported)
286     {
287       CLog::Log(LOGINFO, "%-16s %2i %5i %5i %5i", decoder_profile.name, max_level, max_macroblocks,
288                 max_width, max_height);
289     }
290   }
291   CLog::Log(LOGINFO, "------------------------------------");
292   m_featureCount = 0;
293 #define CHECK_SUPPORT(feature) \
294   do \
295   { \
296     VdpBool supported; \
297     if (m_vdpProcs.vdp_video_mixer_query_feature_support(m_vdpDevice, feature, &supported) == \
298             VDP_STATUS_OK && \
299         supported) \
300     { \
301       CLog::Log(LOGINFO, "Mixer feature: " #feature); \
302       m_vdpFeatures[m_featureCount++] = feature; \
303     } \
304   } while (false)
305 
306   CHECK_SUPPORT(VDP_VIDEO_MIXER_FEATURE_NOISE_REDUCTION);
307   CHECK_SUPPORT(VDP_VIDEO_MIXER_FEATURE_SHARPNESS);
308   CHECK_SUPPORT(VDP_VIDEO_MIXER_FEATURE_DEINTERLACE_TEMPORAL);
309   CHECK_SUPPORT(VDP_VIDEO_MIXER_FEATURE_DEINTERLACE_TEMPORAL_SPATIAL);
310   CHECK_SUPPORT(VDP_VIDEO_MIXER_FEATURE_INVERSE_TELECINE);
311 #ifdef VDP_VIDEO_MIXER_FEATURE_HIGH_QUALITY_SCALING_L1
312   CHECK_SUPPORT(VDP_VIDEO_MIXER_FEATURE_HIGH_QUALITY_SCALING_L1);
313   CHECK_SUPPORT(VDP_VIDEO_MIXER_FEATURE_HIGH_QUALITY_SCALING_L2);
314   CHECK_SUPPORT(VDP_VIDEO_MIXER_FEATURE_HIGH_QUALITY_SCALING_L3);
315   CHECK_SUPPORT(VDP_VIDEO_MIXER_FEATURE_HIGH_QUALITY_SCALING_L4);
316   CHECK_SUPPORT(VDP_VIDEO_MIXER_FEATURE_HIGH_QUALITY_SCALING_L5);
317   CHECK_SUPPORT(VDP_VIDEO_MIXER_FEATURE_HIGH_QUALITY_SCALING_L6);
318   CHECK_SUPPORT(VDP_VIDEO_MIXER_FEATURE_HIGH_QUALITY_SCALING_L7);
319   CHECK_SUPPORT(VDP_VIDEO_MIXER_FEATURE_HIGH_QUALITY_SCALING_L8);
320   CHECK_SUPPORT(VDP_VIDEO_MIXER_FEATURE_HIGH_QUALITY_SCALING_L9);
321 #endif
322 #undef CHECK_SUPPORT
323 }
324 
Supports(VdpVideoMixerFeature feature)325 bool CVDPAUContext::Supports(VdpVideoMixerFeature feature)
326 {
327   for(int i = 0; i < m_featureCount; i++)
328   {
329     if(m_vdpFeatures[i] == feature)
330       return true;
331   }
332   return false;
333 }
334 
335 //-----------------------------------------------------------------------------
336 // VDPAU Video Surface states
337 //-----------------------------------------------------------------------------
338 
339 #define SURFACE_USED_FOR_REFERENCE 0x01
340 #define SURFACE_USED_FOR_RENDER    0x02
341 
AddSurface(VdpVideoSurface surf)342 void CVideoSurfaces::AddSurface(VdpVideoSurface surf)
343 {
344   CSingleLock lock(m_section);
345   m_state[surf] = SURFACE_USED_FOR_REFERENCE;
346 }
347 
ClearReference(VdpVideoSurface surf)348 void CVideoSurfaces::ClearReference(VdpVideoSurface surf)
349 {
350   CSingleLock lock(m_section);
351   if (m_state.find(surf) == m_state.end())
352   {
353     CLog::Log(LOGWARNING, "CVideoSurfaces::ClearReference - surface invalid");
354     return;
355   }
356   m_state[surf] &= ~SURFACE_USED_FOR_REFERENCE;
357   if (m_state[surf] == 0)
358   {
359     m_freeSurfaces.push_back(surf);
360   }
361 }
362 
MarkRender(VdpVideoSurface surf)363 bool CVideoSurfaces::MarkRender(VdpVideoSurface surf)
364 {
365   CSingleLock lock(m_section);
366   if (m_state.find(surf) == m_state.end())
367   {
368     CLog::Log(LOGWARNING, "CVideoSurfaces::MarkRender - surface invalid");
369     return false;
370   }
371   std::list<VdpVideoSurface>::iterator it;
372   it = std::find(m_freeSurfaces.begin(), m_freeSurfaces.end(), surf);
373   if (it != m_freeSurfaces.end())
374   {
375     m_freeSurfaces.erase(it);
376   }
377   m_state[surf] |= SURFACE_USED_FOR_RENDER;
378   return true;
379 }
380 
ClearRender(VdpVideoSurface surf)381 void CVideoSurfaces::ClearRender(VdpVideoSurface surf)
382 {
383   CSingleLock lock(m_section);
384   if (m_state.find(surf) == m_state.end())
385   {
386     CLog::Log(LOGWARNING, "CVideoSurfaces::ClearRender - surface invalid");
387     return;
388   }
389   m_state[surf] &= ~SURFACE_USED_FOR_RENDER;
390   if (m_state[surf] == 0)
391   {
392     m_freeSurfaces.push_back(surf);
393   }
394 }
395 
IsValid(VdpVideoSurface surf)396 bool CVideoSurfaces::IsValid(VdpVideoSurface surf)
397 {
398   CSingleLock lock(m_section);
399   if (m_state.find(surf) != m_state.end())
400     return true;
401   else
402     return false;
403 }
404 
GetFree(VdpVideoSurface surf)405 VdpVideoSurface CVideoSurfaces::GetFree(VdpVideoSurface surf)
406 {
407   CSingleLock lock(m_section);
408   if (m_state.find(surf) != m_state.end())
409   {
410     std::list<VdpVideoSurface>::iterator it;
411     it = std::find(m_freeSurfaces.begin(), m_freeSurfaces.end(), surf);
412     if (it == m_freeSurfaces.end())
413     {
414       CLog::Log(LOGWARNING, "CVideoSurfaces::GetFree - surface not free");
415     }
416     else
417     {
418       m_freeSurfaces.erase(it);
419       m_state[surf] = SURFACE_USED_FOR_REFERENCE;
420       return surf;
421     }
422   }
423 
424   if (!m_freeSurfaces.empty())
425   {
426     VdpVideoSurface freeSurf = m_freeSurfaces.front();
427     m_freeSurfaces.pop_front();
428     m_state[freeSurf] = SURFACE_USED_FOR_REFERENCE;
429     return freeSurf;
430   }
431 
432   return VDP_INVALID_HANDLE;
433 }
434 
RemoveNext(bool skiprender)435 VdpVideoSurface CVideoSurfaces::RemoveNext(bool skiprender)
436 {
437   CSingleLock lock(m_section);
438   VdpVideoSurface surf;
439   std::map<VdpVideoSurface, int>::iterator it;
440   for(it = m_state.begin(); it != m_state.end(); ++it)
441   {
442     if (skiprender && it->second & SURFACE_USED_FOR_RENDER)
443       continue;
444     surf = it->first;
445     m_state.erase(surf);
446 
447     std::list<VdpVideoSurface>::iterator it2;
448     it2 = std::find(m_freeSurfaces.begin(), m_freeSurfaces.end(), surf);
449     if (it2 != m_freeSurfaces.end())
450       m_freeSurfaces.erase(it2);
451     return surf;
452   }
453   return VDP_INVALID_HANDLE;
454 }
455 
Reset()456 void CVideoSurfaces::Reset()
457 {
458   CSingleLock lock(m_section);
459   m_freeSurfaces.clear();
460   m_state.clear();
461 }
462 
Size()463 int CVideoSurfaces::Size()
464 {
465   CSingleLock lock(m_section);
466   return m_state.size();
467 }
468 
HasRefs()469 bool CVideoSurfaces::HasRefs()
470 {
471   CSingleLock lock(m_section);
472   for (const auto &i : m_state)
473   {
474     if (i.second & SURFACE_USED_FOR_REFERENCE)
475     return true;
476   }
477   return false;
478 }
479 
480 //-----------------------------------------------------------------------------
481 // CVDPAU
482 //-----------------------------------------------------------------------------
483 
484 bool CDecoder::m_capGeneral = false;
485 
CDecoder(CProcessInfo & processInfo)486 CDecoder::CDecoder(CProcessInfo& processInfo) :
487     m_vdpauOutput(*this, &m_inMsgEvent), m_processInfo(processInfo)
488 {
489   m_vdpauConfig.videoSurfaces = &m_videoSurfaces;
490 
491   m_vdpauConfigured = false;
492   m_DisplayState = VDPAU_OPEN;
493   m_vdpauConfig.context = 0;
494   m_vdpauConfig.processInfo = &m_processInfo;
495   m_vdpauConfig.resetCounter = 0;
496 }
497 
Open(AVCodecContext * avctx,AVCodecContext * mainctx,const enum AVPixelFormat fmt)498 bool CDecoder::Open(AVCodecContext* avctx, AVCodecContext* mainctx, const enum AVPixelFormat fmt)
499 {
500   // this could be done better by querying actual hw capabilities
501   // but since vdpau will be dropped anyway in v19, this should do
502   if (avctx->sw_pix_fmt != AV_PIX_FMT_YUV420P &&
503       avctx->sw_pix_fmt != AV_PIX_FMT_YUVJ420P)
504     return false;
505 
506   // check if user wants to decode this format with VDPAU
507   std::string gpuvendor = CServiceBroker::GetRenderSystem()->GetRenderVendor();
508   std::transform(gpuvendor.begin(), gpuvendor.end(), gpuvendor.begin(), ::tolower);
509   // nvidia is whitelisted despite for mpeg-4 we need to query user settings
510   if ((gpuvendor.compare(0, 6, "nvidia") != 0)  || (avctx->codec_id == AV_CODEC_ID_MPEG4) || (avctx->codec_id == AV_CODEC_ID_H263))
511   {
512     std::map<AVCodecID, std::string> settings_map = {
513       { AV_CODEC_ID_H263, CSettings::SETTING_VIDEOPLAYER_USEVDPAUMPEG4 },
514       { AV_CODEC_ID_MPEG4, CSettings::SETTING_VIDEOPLAYER_USEVDPAUMPEG4 },
515       { AV_CODEC_ID_WMV3, CSettings::SETTING_VIDEOPLAYER_USEVDPAUVC1 },
516       { AV_CODEC_ID_VC1, CSettings::SETTING_VIDEOPLAYER_USEVDPAUVC1 },
517       { AV_CODEC_ID_MPEG2VIDEO, CSettings::SETTING_VIDEOPLAYER_USEVDPAUMPEG2 },
518     };
519 
520     auto settingsComponent = CServiceBroker::GetSettingsComponent();
521     if (!settingsComponent)
522       return false;
523 
524     auto settings = settingsComponent->GetSettings();
525     if (!settings)
526       return false;
527 
528     auto entry = settings_map.find(avctx->codec_id);
529     if (entry != settings_map.end())
530     {
531       auto setting = settings->GetSetting(entry->second);
532       if (!setting)
533       {
534         CLog::Log(LOGERROR, "Failed to load setting for: {}", entry->second);
535         return false;
536       }
537 
538       bool enabled = settings->GetBool(entry->second) && setting->IsVisible();
539       if (!enabled)
540         return false;
541     }
542   }
543 
544   if (!CServiceBroker::GetRenderSystem()->IsExtSupported("GL_NV_vdpau_interop"))
545   {
546     CLog::Log(LOGINFO, "VDPAU::Open: required extension GL_NV_vdpau_interop not found");
547     return false;
548   }
549 
550   if (avctx->coded_width  == 0 ||
551      avctx->coded_height == 0)
552   {
553     CLog::Log(LOGWARNING,"VDPAU::Open: no width/height available, can't init");
554     return false;
555   }
556   m_vdpauConfig.numRenderBuffers = 5;
557   m_vdpauConfig.timeOpened = CurrentHostCounter();
558 
559   if (!CVDPAUContext::EnsureContext(&m_vdpauConfig.context))
560     return false;
561 
562   m_DisplayState = VDPAU_OPEN;
563   m_vdpauConfigured = false;
564 
565   m_presentPicture = 0;
566 
567   {
568     VdpDecoderProfile profile = 0;
569 
570     // convert FFMPEG codec ID to VDPAU profile.
571     ReadFormatOf(avctx->codec_id, profile, m_vdpauConfig.vdpChromaType);
572     if(profile)
573     {
574       VdpStatus vdp_st;
575       VdpBool is_supported = false;
576       uint32_t max_level, max_macroblocks, max_width, max_height;
577 
578       // query device capabilities to ensure that VDPAU can handle the requested codec
579       vdp_st = m_vdpauConfig.context->GetProcs().vdp_decoder_query_caps(m_vdpauConfig.context->GetDevice(),
580                profile, &is_supported, &max_level, &max_macroblocks, &max_width, &max_height);
581 
582       // test to make sure there is a possibility the codec will work
583       if (CheckStatus(vdp_st, __LINE__))
584       {
585         CLog::Log(LOGERROR, "VDPAU::Open: error %s(%d) checking for decoder support", m_vdpauConfig.context->GetProcs().vdp_get_error_string(vdp_st), vdp_st);
586         return false;
587       }
588 
589       if (max_width < (uint32_t) avctx->coded_width || max_height < (uint32_t) avctx->coded_height)
590       {
591         CLog::Log(LOGWARNING,"VDPAU::Open: requested picture dimensions (%i, %i) exceed hardware capabilities ( %i, %i).",
592 	                      avctx->coded_width, avctx->coded_height, max_width, max_height);
593         return false;
594       }
595 
596       if (!CDVDCodecUtils::IsVP3CompatibleWidth(avctx->coded_width))
597         CLog::Log(LOGWARNING,"VDPAU::Open width %i might not be supported because of hardware bug", avctx->width);
598 
599       // attempt to create a decoder with this width/height, some sizes are not supported by hw
600       vdp_st = m_vdpauConfig.context->GetProcs().vdp_decoder_create(m_vdpauConfig.context->GetDevice(), profile, avctx->coded_width, avctx->coded_height, 5, &m_vdpauConfig.vdpDecoder);
601 
602       if (CheckStatus(vdp_st, __LINE__))
603       {
604         CLog::Log(LOGERROR, "VDPAU::Open: error: %s(%d) checking for decoder support", m_vdpauConfig.context->GetProcs().vdp_get_error_string(vdp_st), vdp_st);
605         return false;
606       }
607 
608       m_vdpauConfig.context->GetProcs().vdp_decoder_destroy(m_vdpauConfig.vdpDecoder);
609       CheckStatus(vdp_st, __LINE__);
610 
611       // finally setup ffmpeg
612       memset(&m_hwContext, 0, sizeof(AVVDPAUContext));
613       m_hwContext.render2 = CDecoder::Render;
614       avctx->get_buffer2 = CDecoder::FFGetBuffer;
615       avctx->slice_flags = SLICE_FLAG_CODED_ORDER|SLICE_FLAG_ALLOW_FIELD;
616       avctx->hwaccel_context = &m_hwContext;
617 
618       CServiceBroker::GetWinSystem()->Register(this);
619       m_avctx = mainctx;
620       return true;
621     }
622   }
623   return false;
624 }
625 
~CDecoder()626 CDecoder::~CDecoder()
627 {
628   Close();
629 }
630 
Close()631 void CDecoder::Close()
632 {
633   CLog::Log(LOGINFO, " (VDPAU) %s", __FUNCTION__);
634 
635   CServiceBroker::GetWinSystem()->Unregister(this);
636 
637   CSingleLock lock(m_DecoderSection);
638 
639   FiniVDPAUOutput();
640   m_vdpauOutput.Dispose();
641 
642   if (m_vdpauConfig.context)
643     m_vdpauConfig.context->Release();
644   m_vdpauConfig.context = 0;
645 }
646 
Release()647 long CDecoder::Release()
648 {
649   // if ffmpeg holds any references, flush buffers
650   if (m_avctx && m_videoSurfaces.HasRefs())
651   {
652     avcodec_flush_buffers(m_avctx);
653   }
654 
655   // check if we should do some pre-cleanup here
656   // a second decoder might need resources
657   if (m_vdpauConfigured == true)
658   {
659     CSingleLock lock(m_DecoderSection);
660     CLog::Log(LOGINFO, "CVDPAU::Release pre-cleanup");
661 
662     Message *reply;
663     if (m_vdpauOutput.m_controlPort.SendOutMessageSync(COutputControlProtocol::PRECLEANUP,
664                                                    &reply,
665                                                    2000))
666     {
667       bool success = reply->signal == COutputControlProtocol::ACC ? true : false;
668       reply->Release();
669       if (!success)
670       {
671         CLog::Log(LOGERROR, "VDPAU::%s - pre-cleanup returned error", __FUNCTION__);
672         m_DisplayState = VDPAU_ERROR;
673       }
674     }
675     else
676     {
677       CLog::Log(LOGERROR, "VDPAU::%s - pre-cleanup timed out", __FUNCTION__);
678       m_DisplayState = VDPAU_ERROR;
679     }
680 
681     VdpVideoSurface surf;
682     while((surf = m_videoSurfaces.RemoveNext(true)) != VDP_INVALID_HANDLE)
683     {
684       m_vdpauConfig.context->GetProcs().vdp_video_surface_destroy(surf);
685     }
686   }
687   return IHardwareDecoder::Release();
688 }
689 
ReleasePicReference()690 long CDecoder::ReleasePicReference()
691 {
692   return IHardwareDecoder::Release();
693 }
694 
SetWidthHeight(int width,int height)695 void CDecoder::SetWidthHeight(int width, int height)
696 {
697   m_vdpauConfig.upscale = CServiceBroker::GetSettingsComponent()->GetAdvancedSettings()->m_videoVDPAUScaling;
698 
699   //pick the smallest dimensions, so we downscale with vdpau and upscale with opengl when appropriate
700   //this requires the least amount of gpu memory bandwidth
701   if (CServiceBroker::GetWinSystem()->GetGfxContext().GetWidth() < width || CServiceBroker::GetWinSystem()->GetGfxContext().GetHeight() < height || m_vdpauConfig.upscale >= 0)
702   {
703     //scale width to desktop size if the aspect ratio is the same or bigger than the desktop
704     if ((double)height * CServiceBroker::GetWinSystem()->GetGfxContext().GetWidth() / width <= (double)CServiceBroker::GetWinSystem()->GetGfxContext().GetHeight())
705     {
706       m_vdpauConfig.outWidth = CServiceBroker::GetWinSystem()->GetGfxContext().GetWidth();
707       m_vdpauConfig.outHeight = MathUtils::round_int((double)height * CServiceBroker::GetWinSystem()->GetGfxContext().GetWidth() / width);
708     }
709     else //scale height to the desktop size if the aspect ratio is smaller than the desktop
710     {
711       m_vdpauConfig.outHeight = CServiceBroker::GetWinSystem()->GetGfxContext().GetHeight();
712       m_vdpauConfig.outWidth = MathUtils::round_int((double)width * CServiceBroker::GetWinSystem()->GetGfxContext().GetHeight() / height);
713     }
714   }
715   else
716   { //let opengl scale
717     m_vdpauConfig.outWidth = width;
718     m_vdpauConfig.outHeight = height;
719   }
720   CLog::Log(LOGDEBUG, LOGVIDEO, "CVDPAU::SetWidthHeight Setting OutWidth: %i OutHeight: %i", m_vdpauConfig.outWidth, m_vdpauConfig.outHeight);
721 }
722 
OnLostDisplay()723 void CDecoder::OnLostDisplay()
724 {
725   CLog::Log(LOGINFO, "CVDPAU::OnLostDevice event");
726 
727   int count = CServiceBroker::GetWinSystem()->GetGfxContext().exit();
728 
729   CSingleLock lock(m_DecoderSection);
730   FiniVDPAUOutput();
731   if (m_vdpauConfig.context)
732     m_vdpauConfig.context->Release();
733   m_vdpauConfig.context = 0;
734 
735   m_DisplayState = VDPAU_LOST;
736   lock.Leave();
737   m_DisplayEvent.Reset();
738 
739   CServiceBroker::GetWinSystem()->GetGfxContext().restore(count);
740 }
741 
OnResetDisplay()742 void CDecoder::OnResetDisplay()
743 {
744   CLog::Log(LOGINFO, "CVDPAU::OnResetDevice event");
745 
746   int count = CServiceBroker::GetWinSystem()->GetGfxContext().exit();
747 
748   CSingleLock lock(m_DecoderSection);
749   if (m_DisplayState == VDPAU_LOST)
750   {
751     m_DisplayState = VDPAU_RESET;
752     lock.Leave();
753     m_DisplayEvent.Set();
754   }
755 
756   CServiceBroker::GetWinSystem()->GetGfxContext().restore(count);
757 }
758 
Check(AVCodecContext * avctx)759 CDVDVideoCodec::VCReturn CDecoder::Check(AVCodecContext* avctx)
760 {
761   EDisplayState state;
762 
763   { CSingleLock lock(m_DecoderSection);
764     state = m_DisplayState;
765   }
766 
767   if (state == VDPAU_LOST)
768   {
769     CLog::Log(LOGINFO, "CVDPAU::Check waiting for display reset event");
770     if (!m_DisplayEvent.WaitMSec(4000))
771     {
772       CLog::Log(LOGERROR, "CVDPAU::Check - device didn't reset in reasonable time");
773       state = VDPAU_RESET;
774     }
775     else
776     {
777       CSingleLock lock(m_DecoderSection);
778       state = m_DisplayState;
779     }
780   }
781   if (state == VDPAU_RESET || state == VDPAU_ERROR)
782   {
783     CSingleLock lock(m_DecoderSection);
784 
785     avcodec_flush_buffers(avctx);
786     FiniVDPAUOutput();
787     if (m_vdpauConfig.context)
788       m_vdpauConfig.context->Release();
789     m_vdpauConfig.context = 0;
790 
791     if (CVDPAUContext::EnsureContext(&m_vdpauConfig.context))
792     {
793       m_DisplayState = VDPAU_OPEN;
794       m_vdpauConfigured = false;
795     }
796 
797     if (state == VDPAU_RESET)
798       return CDVDVideoCodec::VC_FLUSHED;
799     else
800       return CDVDVideoCodec::VC_ERROR;
801   }
802   return CDVDVideoCodec::VC_NONE;
803 }
804 
IsVDPAUFormat(AVPixelFormat format)805 bool CDecoder::IsVDPAUFormat(AVPixelFormat format)
806 {
807   if (format == AV_PIX_FMT_VDPAU)
808     return true;
809   else
810     return false;
811 }
812 
Supports(VdpVideoMixerFeature feature)813 bool CDecoder::Supports(VdpVideoMixerFeature feature)
814 {
815   return m_vdpauConfig.context->Supports(feature);
816 }
817 
FiniVDPAUOutput()818 void CDecoder::FiniVDPAUOutput()
819 {
820   if (!m_vdpauConfigured)
821     return;
822 
823   CLog::Log(LOGINFO, " (VDPAU) %s", __FUNCTION__);
824 
825   // uninit output
826   m_vdpauOutput.Dispose();
827   m_vdpauConfigured = false;
828 
829   VdpStatus vdp_st;
830 
831   vdp_st = m_vdpauConfig.context->GetProcs().vdp_decoder_destroy(m_vdpauConfig.vdpDecoder);
832   if (CheckStatus(vdp_st, __LINE__))
833     return;
834   m_vdpauConfig.vdpDecoder = VDP_INVALID_HANDLE;
835 
836   CLog::Log(LOGDEBUG, LOGVIDEO, "CVDPAU::FiniVDPAUOutput destroying %d video surfaces", m_videoSurfaces.Size());
837 
838   VdpVideoSurface surf;
839   while((surf = m_videoSurfaces.RemoveNext()) != VDP_INVALID_HANDLE)
840   {
841     m_vdpauConfig.context->GetProcs().vdp_video_surface_destroy(surf);
842     if (CheckStatus(vdp_st, __LINE__))
843       return;
844   }
845   m_videoSurfaces.Reset();
846 }
847 
ReadFormatOf(AVCodecID codec,VdpDecoderProfile & vdp_decoder_profile,VdpChromaType & vdp_chroma_type)848 void CDecoder::ReadFormatOf( AVCodecID codec
849                            , VdpDecoderProfile &vdp_decoder_profile
850                            , VdpChromaType     &vdp_chroma_type)
851 {
852   switch (codec)
853   {
854     case AV_CODEC_ID_MPEG1VIDEO:
855       vdp_decoder_profile = VDP_DECODER_PROFILE_MPEG1;
856       vdp_chroma_type     = VDP_CHROMA_TYPE_420;
857       break;
858     case AV_CODEC_ID_MPEG2VIDEO:
859       vdp_decoder_profile = VDP_DECODER_PROFILE_MPEG2_MAIN;
860       vdp_chroma_type     = VDP_CHROMA_TYPE_420;
861       break;
862     case AV_CODEC_ID_H264:
863       vdp_decoder_profile = VDP_DECODER_PROFILE_H264_HIGH;
864       vdp_chroma_type     = VDP_CHROMA_TYPE_420;
865       break;
866 #ifdef VDP_DECODER_PROFILE_HEVC_MAIN
867     case AV_CODEC_ID_HEVC:
868       vdp_decoder_profile = VDP_DECODER_PROFILE_HEVC_MAIN;
869       vdp_chroma_type     = VDP_CHROMA_TYPE_420;
870       break;
871 #endif
872 #ifdef VDP_DECODER_PROFILE_VP9_PROFILE_0
873     case AV_CODEC_ID_VP9:
874     vdp_decoder_profile = VDP_DECODER_PROFILE_VP9_PROFILE_0;
875     vdp_chroma_type = VDP_CHROMA_TYPE_420;
876     break;
877 #endif
878     case AV_CODEC_ID_WMV3:
879       vdp_decoder_profile = VDP_DECODER_PROFILE_VC1_MAIN;
880       vdp_chroma_type     = VDP_CHROMA_TYPE_420;
881       break;
882     case AV_CODEC_ID_VC1:
883       vdp_decoder_profile = VDP_DECODER_PROFILE_VC1_ADVANCED;
884       vdp_chroma_type     = VDP_CHROMA_TYPE_420;
885       break;
886     case AV_CODEC_ID_MPEG4:
887       vdp_decoder_profile = VDP_DECODER_PROFILE_MPEG4_PART2_ASP;
888       vdp_chroma_type     = VDP_CHROMA_TYPE_420;
889       break;
890     default:
891       vdp_decoder_profile = 0;
892       vdp_chroma_type     = 0;
893       break;
894   }
895 }
896 
ConfigVDPAU(AVCodecContext * avctx,int ref_frames)897 bool CDecoder::ConfigVDPAU(AVCodecContext* avctx, int ref_frames)
898 {
899   FiniVDPAUOutput();
900 
901   VdpStatus vdp_st;
902   VdpDecoderProfile vdp_decoder_profile;
903 
904   m_vdpauConfig.vidWidth = avctx->width;
905   m_vdpauConfig.vidHeight = avctx->height;
906   m_vdpauConfig.surfaceWidth = avctx->coded_width;
907   m_vdpauConfig.surfaceHeight = avctx->coded_height;
908 
909   SetWidthHeight(avctx->width,avctx->height);
910 
911   CLog::Log(LOGINFO, " (VDPAU) screenWidth:%i vidWidth:%i surfaceWidth:%i", m_vdpauConfig.outWidth,
912             m_vdpauConfig.vidWidth, m_vdpauConfig.surfaceWidth);
913   CLog::Log(LOGINFO, " (VDPAU) screenHeight:%i vidHeight:%i surfaceHeight:%i",
914             m_vdpauConfig.outHeight, m_vdpauConfig.vidHeight, m_vdpauConfig.surfaceHeight);
915 
916   ReadFormatOf(avctx->codec_id, vdp_decoder_profile, m_vdpauConfig.vdpChromaType);
917 
918   if (avctx->codec_id == AV_CODEC_ID_H264)
919   {
920     m_vdpauConfig.maxReferences = ref_frames;
921     if (m_vdpauConfig.maxReferences > 16) m_vdpauConfig.maxReferences = 16;
922     if (m_vdpauConfig.maxReferences < 5)  m_vdpauConfig.maxReferences = 5;
923   }
924   else if (avctx->codec_id == AV_CODEC_ID_HEVC)
925   {
926     // The DPB works quite differently in hevc and there isn't  a per-file max
927     // reference number, so we force the maximum number (source: upstream ffmpeg)
928     m_vdpauConfig.maxReferences = 16;
929   }
930   else if (avctx->codec_id == AV_CODEC_ID_VP9)
931   {
932     if (avctx->profile != FF_PROFILE_VP9_0)
933       return false;
934 
935     m_vdpauConfig.maxReferences = 8;
936   }
937   else
938     m_vdpauConfig.maxReferences = 2;
939 
940   vdp_st = m_vdpauConfig.context->GetProcs().vdp_decoder_create(m_vdpauConfig.context->GetDevice(),
941                               vdp_decoder_profile,
942                               m_vdpauConfig.surfaceWidth,
943                               m_vdpauConfig.surfaceHeight,
944                               m_vdpauConfig.maxReferences,
945                               &m_vdpauConfig.vdpDecoder);
946   if (CheckStatus(vdp_st, __LINE__))
947     return false;
948 
949   // initialize output
950   CSingleLock lock(CServiceBroker::GetWinSystem()->GetGfxContext());
951   m_vdpauConfig.stats = &m_bufferStats;
952   m_vdpauConfig.vdpau = this;
953   m_bufferStats.Reset();
954   m_vdpauOutput.Start();
955   Message *reply;
956   if (m_vdpauOutput.m_controlPort.SendOutMessageSync(COutputControlProtocol::INIT,
957                                                  &reply,
958                                                  2000,
959                                                  &m_vdpauConfig,
960                                                  sizeof(m_vdpauConfig)))
961   {
962     bool success = reply->signal == COutputControlProtocol::ACC ? true : false;
963     reply->Release();
964     if (!success)
965     {
966       CLog::Log(LOGERROR, "VDPAU::%s - vdpau output returned error", __FUNCTION__);
967       m_vdpauOutput.Dispose();
968       return false;
969     }
970   }
971   else
972   {
973     CLog::Log(LOGERROR, "VDPAU::%s - failed to init output", __FUNCTION__);
974     m_vdpauOutput.Dispose();
975     return false;
976   }
977 
978   m_inMsgEvent.Reset();
979   m_vdpauConfigured = true;
980   m_ErrorCount = 0;
981   m_vdpauConfig.resetCounter++;
982   return true;
983 }
984 
FFGetBuffer(AVCodecContext * avctx,AVFrame * pic,int flags)985 int CDecoder::FFGetBuffer(AVCodecContext *avctx, AVFrame *pic, int flags)
986 {
987   ICallbackHWAccel* cb = static_cast<ICallbackHWAccel*>(avctx->opaque);
988   CDecoder* vdp = static_cast<CDecoder*>(cb->GetHWAccel());
989 
990   // while we are waiting to recover we can't do anything
991   CSingleLock lock(vdp->m_DecoderSection);
992 
993   if(vdp->m_DisplayState != VDPAU_OPEN)
994   {
995     CLog::Log(LOGWARNING, "CVDPAU::FFGetBuffer - returning due to awaiting recovery");
996     return -1;
997   }
998 
999   VdpVideoSurface surf = (VdpVideoSurface)(uintptr_t)pic->data[3];
1000   surf = vdp->m_videoSurfaces.GetFree(surf != 0 ? surf : VDP_INVALID_HANDLE);
1001 
1002   VdpStatus vdp_st = VDP_STATUS_ERROR;
1003   if (surf == VDP_INVALID_HANDLE)
1004   {
1005     // create a new surface
1006     VdpDecoderProfile profile;
1007     ReadFormatOf(avctx->codec_id, profile, vdp->m_vdpauConfig.vdpChromaType);
1008 
1009     vdp_st = vdp->m_vdpauConfig.context->GetProcs().vdp_video_surface_create(vdp->m_vdpauConfig.context->GetDevice(),
1010                                          vdp->m_vdpauConfig.vdpChromaType,
1011                                          avctx->coded_width,
1012                                          avctx->coded_height,
1013                                          &surf);
1014     vdp->CheckStatus(vdp_st, __LINE__);
1015     if (vdp_st != VDP_STATUS_OK)
1016     {
1017       CLog::Log(LOGERROR, "CVDPAU::FFGetBuffer - No Video surface available could be created");
1018       return -1;
1019     }
1020     vdp->m_videoSurfaces.AddSurface(surf);
1021   }
1022 
1023   pic->data[1] = pic->data[2] = NULL;
1024   pic->data[0] = (uint8_t*)(uintptr_t)surf;
1025   pic->data[3] = (uint8_t*)(uintptr_t)surf;
1026   pic->linesize[0] = pic->linesize[1] =  pic->linesize[2] = 0;
1027   AVBufferRef *buffer = av_buffer_create(pic->data[3], 0, FFReleaseBuffer, vdp, 0);
1028   if (!buffer)
1029   {
1030     CLog::Log(LOGERROR, "CVDPAU::%s - error creating buffer", __FUNCTION__);
1031     return -1;
1032   }
1033   pic->buf[0] = buffer;
1034 
1035   pic->reordered_opaque= avctx->reordered_opaque;
1036   return 0;
1037 }
1038 
FFReleaseBuffer(void * opaque,uint8_t * data)1039 void CDecoder::FFReleaseBuffer(void *opaque, uint8_t *data)
1040 {
1041   CDecoder *vdp = static_cast<CDecoder*>(opaque);
1042 
1043   VdpVideoSurface surf;
1044 
1045   CSingleLock lock(vdp->m_DecoderSection);
1046 
1047   surf = (VdpVideoSurface)(uintptr_t)data;
1048 
1049   vdp->m_videoSurfaces.ClearReference(surf);
1050 }
1051 
Render(struct AVCodecContext * s,struct AVFrame * src,const VdpPictureInfo * info,uint32_t buffers_used,const VdpBitstreamBuffer * buffers)1052 int CDecoder::Render(struct AVCodecContext *s, struct AVFrame *src,
1053                      const VdpPictureInfo *info, uint32_t buffers_used,
1054                      const VdpBitstreamBuffer *buffers)
1055 {
1056   ICallbackHWAccel* ctx = static_cast<ICallbackHWAccel*>(s->opaque);
1057   CDecoder* vdp = static_cast<CDecoder*>(ctx->GetHWAccel());
1058 
1059   // while we are waiting to recover we can't do anything
1060   CSingleLock lock(vdp->m_DecoderSection);
1061 
1062   if(vdp->m_DisplayState != VDPAU_OPEN)
1063     return -1;
1064 
1065   if(src->linesize[0] || src->linesize[1] || src->linesize[2])
1066   {
1067     CLog::Log(LOGERROR, "CVDPAU::FFDrawSlice - invalid linesizes or offsets provided");
1068     return -1;
1069   }
1070 
1071   VdpStatus vdp_st;
1072   VdpVideoSurface surf = (VdpVideoSurface)(uintptr_t)src->data[3];
1073 
1074   // ffmpeg vc-1 decoder does not flush, make sure the data buffer is still valid
1075   if (!vdp->m_videoSurfaces.IsValid(surf))
1076   {
1077     CLog::Log(LOGWARNING, "CVDPAU::FFDrawSlice - ignoring invalid buffer");
1078     return -1;
1079   }
1080 
1081   uint32_t max_refs = 0;
1082   if(s->codec_id == AV_CODEC_ID_H264)
1083     max_refs = s->refs;
1084 
1085   if(vdp->m_vdpauConfig.vdpDecoder == VDP_INVALID_HANDLE
1086   || vdp->m_vdpauConfigured == false
1087   || vdp->m_vdpauConfig.maxReferences < max_refs)
1088   {
1089     if(!vdp->ConfigVDPAU(s, max_refs))
1090       return -1;
1091   }
1092 
1093   uint64_t startTime = CurrentHostCounter();
1094   uint16_t decoded, processed, rend;
1095   vdp->m_bufferStats.Get(decoded, processed, rend);
1096   vdp_st = vdp->m_vdpauConfig.context->GetProcs().vdp_decoder_render(vdp->m_vdpauConfig.vdpDecoder,
1097                                                                      surf, info, buffers_used, buffers);
1098   if (vdp->CheckStatus(vdp_st, __LINE__))
1099     return -1;
1100 
1101   uint64_t diff = CurrentHostCounter() - startTime;
1102   if (diff*1000/CurrentHostFrequency() > 30)
1103     CLog::Log(LOGDEBUG, LOGVIDEO, "CVDPAU::DrawSlice - VdpDecoderRender long decoding: %d ms, dec: %d, proc: %d, rend: %d", (int)((diff*1000)/CurrentHostFrequency()), decoded, processed, rend);
1104 
1105   return 0;
1106 }
1107 
SetCodecControl(int flags)1108 void CDecoder::SetCodecControl(int flags)
1109 {
1110   m_codecControl = flags & (DVD_CODEC_CTRL_DRAIN | DVD_CODEC_CTRL_HURRY);
1111   if (m_codecControl & DVD_CODEC_CTRL_DRAIN)
1112     m_bufferStats.SetDraining(true);
1113   else
1114     m_bufferStats.SetDraining(false);
1115 }
1116 
Decode(AVCodecContext * avctx,AVFrame * pFrame)1117 CDVDVideoCodec::VCReturn CDecoder::Decode(AVCodecContext *avctx, AVFrame *pFrame)
1118 {
1119   CDVDVideoCodec::VCReturn result = Check(avctx);
1120   if (result != CDVDVideoCodec::VC_NONE)
1121     return result;
1122 
1123   CSingleLock lock(m_DecoderSection);
1124 
1125   if (!m_vdpauConfigured)
1126     return CDVDVideoCodec::VC_ERROR;
1127 
1128   if(pFrame)
1129   { // we have a new frame from decoder
1130 
1131     VdpVideoSurface surf = (VdpVideoSurface)(uintptr_t)pFrame->data[3];
1132     // ffmpeg vc-1 decoder does not flush, make sure the data buffer is still valid
1133     if (!m_videoSurfaces.IsValid(surf))
1134     {
1135       CLog::Log(LOGWARNING, "CVDPAU::Decode - ignoring invalid buffer");
1136       return CDVDVideoCodec::VC_BUFFER;
1137     }
1138     m_videoSurfaces.MarkRender(surf);
1139 
1140     // send frame to output for processing
1141     CVdpauDecodedPicture *pic = new CVdpauDecodedPicture();
1142     static_cast<ICallbackHWAccel*>(avctx->opaque)->GetPictureCommon(&(pic->DVDPic));
1143     m_codecControl = pic->DVDPic.iFlags & (DVD_CODEC_CTRL_HURRY | DVD_CODEC_CTRL_NO_POSTPROC);
1144     pic->videoSurface = surf;
1145     pic->DVDPic.color_space = avctx->colorspace;
1146     m_bufferStats.IncDecoded();
1147     CPayloadWrap<CVdpauDecodedPicture> *payload = new CPayloadWrap<CVdpauDecodedPicture>(pic);
1148     m_vdpauOutput.m_dataPort.SendOutMessage(COutputDataProtocol::NEWFRAME, payload);
1149   }
1150 
1151   uint16_t decoded, processed, render;
1152   Message *msg;
1153   while (m_vdpauOutput.m_controlPort.ReceiveInMessage(&msg))
1154   {
1155     if (msg->signal == COutputControlProtocol::ERROR)
1156     {
1157       m_DisplayState = VDPAU_ERROR;
1158       msg->Release();
1159       return CDVDVideoCodec::VC_BUFFER;
1160     }
1161     msg->Release();
1162   }
1163 
1164   bool drain = (m_codecControl & DVD_CODEC_CTRL_DRAIN);
1165 
1166   m_bufferStats.Get(decoded, processed, render);
1167   // if all pics are drained, break the loop by setting VC_EOF
1168   if (drain && decoded <= 0 && processed <= 0 && render <= 0)
1169     return CDVDVideoCodec::VC_EOF;
1170 
1171   uint64_t startTime = CurrentHostCounter();
1172   while (true)
1173   {
1174     // first fill the buffers to keep vdpau busy
1175     // mixer will run with decoded >= 2. output is limited by number of output surfaces
1176     // In case mixer is bypassed we limit by looking at processed
1177     if (!drain && decoded < 3 && processed < 3)
1178     {
1179       return CDVDVideoCodec::VC_BUFFER;
1180     }
1181     else if (m_vdpauOutput.m_dataPort.ReceiveInMessage(&msg))
1182     {
1183       if (msg->signal == COutputDataProtocol::PICTURE)
1184       {
1185         if (m_presentPicture)
1186         {
1187           m_presentPicture->Release();
1188           m_presentPicture = nullptr;
1189         }
1190 
1191         m_presentPicture = *(CVdpauRenderPicture**)msg->data;
1192         m_bufferStats.DecRender();
1193         msg->Release();
1194         uint64_t diff = CurrentHostCounter() - startTime;
1195         m_bufferStats.SetParams(diff, m_codecControl);
1196         return CDVDVideoCodec::VC_PICTURE;
1197       }
1198       msg->Release();
1199     }
1200     else if (m_vdpauOutput.m_controlPort.ReceiveInMessage(&msg))
1201     {
1202       if (msg->signal == COutputControlProtocol::STATS)
1203       {
1204         msg->Release();
1205         m_bufferStats.Get(decoded, processed, render);
1206         if (!drain && decoded < 3 && processed < 3)
1207         {
1208           return CDVDVideoCodec::VC_BUFFER;
1209         }
1210       }
1211       else
1212       {
1213         m_DisplayState = VDPAU_ERROR;
1214         msg->Release();
1215         return CDVDVideoCodec::VC_ERROR;
1216       }
1217     }
1218 
1219     if (!m_inMsgEvent.WaitMSec(2000))
1220       break;
1221   }
1222 
1223   CLog::Log(LOGERROR, "VDPAU::%s - timed out waiting for output message", __FUNCTION__);
1224   m_DisplayState = VDPAU_ERROR;
1225 
1226   return CDVDVideoCodec::VC_ERROR;
1227 }
1228 
GetPicture(AVCodecContext * avctx,VideoPicture * picture)1229 bool CDecoder::GetPicture(AVCodecContext* avctx, VideoPicture* picture)
1230 {
1231   if (picture->videoBuffer)
1232   {
1233     picture->videoBuffer->Release();
1234     picture->videoBuffer = nullptr;
1235   }
1236 
1237   CSingleLock lock(m_DecoderSection);
1238 
1239   if (m_DisplayState != VDPAU_OPEN)
1240     return false;
1241 
1242   picture->SetParams(m_presentPicture->DVDPic);
1243   picture->videoBuffer = m_presentPicture;
1244   m_presentPicture = nullptr;
1245 
1246   return true;
1247 }
1248 
Reset()1249 void CDecoder::Reset()
1250 {
1251   CSingleLock lock(m_DecoderSection);
1252 
1253   if (m_presentPicture)
1254   {
1255     m_presentPicture->Release();
1256     m_presentPicture = nullptr;
1257   }
1258 
1259   if (!m_vdpauConfigured)
1260     return;
1261 
1262   Message *reply;
1263   if (m_vdpauOutput.m_controlPort.SendOutMessageSync(COutputControlProtocol::FLUSH,
1264                                                  &reply,
1265                                                  2000))
1266   {
1267     bool success = reply->signal == COutputControlProtocol::ACC ? true : false;
1268     reply->Release();
1269     if (!success)
1270     {
1271       CLog::Log(LOGERROR, "VDPAU::%s - flush returned error", __FUNCTION__);
1272       m_DisplayState = VDPAU_ERROR;
1273     }
1274     else
1275       m_bufferStats.Reset();
1276   }
1277   else
1278   {
1279     CLog::Log(LOGERROR, "VDPAU::%s - flush timed out", __FUNCTION__);
1280     m_DisplayState = VDPAU_ERROR;
1281   }
1282 }
1283 
CanSkipDeint()1284 bool CDecoder::CanSkipDeint()
1285 {
1286   return m_bufferStats.CanSkipDeint();
1287 }
1288 
ReturnRenderPicture(CVdpauRenderPicture * renderPic)1289 void CDecoder::ReturnRenderPicture(CVdpauRenderPicture *renderPic)
1290 {
1291   m_vdpauOutput.m_dataPort.SendOutMessage(COutputDataProtocol::RETURNPIC, &renderPic, sizeof(renderPic));
1292 }
1293 
CheckStatus(VdpStatus vdp_st,int line)1294 bool CDecoder::CheckStatus(VdpStatus vdp_st, int line)
1295 {
1296   if (vdp_st != VDP_STATUS_OK)
1297   {
1298     CLog::Log(LOGERROR, " (VDPAU) Error: %s(%d) at %s:%d",
1299               m_vdpauConfig.context->GetProcs().vdp_get_error_string(vdp_st), vdp_st, __FILE__,
1300               line);
1301 
1302     m_ErrorCount++;
1303 
1304     if(m_DisplayState == VDPAU_OPEN)
1305     {
1306       if (vdp_st == VDP_STATUS_DISPLAY_PREEMPTED)
1307       {
1308         m_DisplayEvent.Reset();
1309         m_DisplayState = VDPAU_LOST;
1310       }
1311       else if (m_ErrorCount > 2)
1312         m_DisplayState = VDPAU_ERROR;
1313     }
1314 
1315     return true;
1316   }
1317   m_ErrorCount = 0;
1318   return false;
1319 }
1320 
Create(CDVDStreamInfo & hint,CProcessInfo & processInfo,AVPixelFormat fmt)1321 IHardwareDecoder* CDecoder::Create(CDVDStreamInfo &hint, CProcessInfo &processInfo, AVPixelFormat fmt)
1322  {
1323    if (CDecoder::IsVDPAUFormat(fmt) && CServiceBroker::GetSettingsComponent()->GetSettings()->GetBool(CSettings::SETTING_VIDEOPLAYER_USEVDPAU))
1324      return new VDPAU::CDecoder(processInfo);
1325 
1326    return nullptr;
1327  }
1328 
Register()1329 void CDecoder::Register()
1330 {
1331   CVDPAUContext *context;
1332   if (!CVDPAUContext::EnsureContext(&context))
1333     return;
1334 
1335   context->Release();
1336 
1337   m_capGeneral = true;
1338 
1339   CDVDFactoryCodec::RegisterHWAccel("vdpau", CDecoder::Create);
1340 
1341   std::string gpuvendor = CServiceBroker::GetRenderSystem()->GetRenderVendor();
1342   std::transform(gpuvendor.begin(), gpuvendor.end(), gpuvendor.begin(), ::tolower);
1343   bool isNvidia = (gpuvendor.compare(0, 6, "nvidia") == 0);
1344 
1345   auto settingsComponent = CServiceBroker::GetSettingsComponent();
1346   if (!settingsComponent)
1347     return;
1348 
1349   auto settings = settingsComponent->GetSettings();
1350   if (!settings)
1351     return;
1352 
1353   auto setting = settings->GetSetting(CSettings::SETTING_VIDEOPLAYER_USEVDPAU);
1354   if (!setting)
1355     CLog::Log(LOGERROR, "Failed to load setting for: {}", CSettings::SETTING_VIDEOPLAYER_USEVDPAU);
1356   else
1357     setting->SetVisible(true);
1358 
1359   if (!isNvidia)
1360   {
1361     constexpr std::array<const char*, 4> vdpauSettings = {
1362         CSettings::SETTING_VIDEOPLAYER_USEVDPAUMPEG4, CSettings::SETTING_VIDEOPLAYER_USEVDPAUVC1,
1363         CSettings::SETTING_VIDEOPLAYER_USEVDPAUMPEG2, CSettings::SETTING_VIDEOPLAYER_USEVDPAUMIXER};
1364 
1365     for (const auto& vdpauSetting : vdpauSettings)
1366     {
1367       setting = settings->GetSetting(vdpauSetting);
1368       if (!setting)
1369       {
1370         CLog::Log(LOGERROR, "Failed to load setting for: {}", vdpauSetting);
1371         continue;
1372       }
1373 
1374       setting->SetVisible(true);
1375     }
1376   }
1377 }
1378 
1379 //-----------------------------------------------------------------------------
1380 // BufferPool
1381 //-----------------------------------------------------------------------------
1382 
1383 class VDPAU::CVdpauBufferPool : public IVideoBufferPool
1384 {
1385 public:
1386   explicit CVdpauBufferPool(CDecoder &decoder);
1387   ~CVdpauBufferPool() override;
1388   CVideoBuffer* Get() override;
1389   void Return(int id) override;
1390   CVdpauRenderPicture* GetVdpau();
1391   bool HasFree();
1392   void QueueReturnPicture(CVdpauRenderPicture *pic);
1393   CVdpauRenderPicture* ProcessSyncPicture();
1394   void InvalidateUsed();
1395 
1396   unsigned short numOutputSurfaces;
1397   std::vector<VdpOutputSurface> outputSurfaces;
1398   std::queue<CVdpauProcessedPicture> processedPics;
1399   std::deque<CVdpauProcessedPicture> processedPicsAway;
1400 
1401   int procPicId = 0;
1402 
1403 protected:
1404   std::vector<CVdpauRenderPicture*> allRenderPics;
1405   std::deque<int> usedRenderPics;
1406   std::deque<int> freeRenderPics;
1407   std::deque<int> syncRenderPics;
1408 
1409   CDecoder &m_vdpau;
1410 };
1411 
CVdpauBufferPool(CDecoder & decoder)1412 CVdpauBufferPool::CVdpauBufferPool(CDecoder &decoder)
1413   : m_vdpau(decoder)
1414 {
1415   CVdpauRenderPicture *pic;
1416   for (unsigned int i = 0; i < NUM_RENDER_PICS; i++)
1417   {
1418     pic = new CVdpauRenderPicture(i);
1419     allRenderPics.push_back(pic);
1420     freeRenderPics.push_back(i);
1421   }
1422 }
1423 
~CVdpauBufferPool()1424 CVdpauBufferPool::~CVdpauBufferPool()
1425 {
1426   CVdpauRenderPicture *pic;
1427   for (unsigned int i = 0; i < NUM_RENDER_PICS; i++)
1428   {
1429     pic = allRenderPics[i];
1430     delete pic;
1431   }
1432   allRenderPics.clear();
1433 }
1434 
Get()1435 CVideoBuffer* CVdpauBufferPool::Get()
1436 {
1437   if (freeRenderPics.empty())
1438     return nullptr;
1439 
1440   int idx = freeRenderPics.front();
1441   freeRenderPics.pop_front();
1442   usedRenderPics.push_back(idx);
1443 
1444   CVideoBuffer *retPic = allRenderPics[idx];
1445   retPic->Acquire(GetPtr());
1446 
1447   m_vdpau.Acquire();
1448 
1449   return retPic;
1450 }
1451 
Return(int id)1452 void CVdpauBufferPool::Return(int id)
1453 {
1454   CVdpauRenderPicture *pic = allRenderPics[id];
1455 
1456   m_vdpau.ReturnRenderPicture(pic);
1457   m_vdpau.ReleasePicReference();
1458 }
1459 
GetVdpau()1460 CVdpauRenderPicture* CVdpauBufferPool::GetVdpau()
1461 {
1462   return dynamic_cast<CVdpauRenderPicture*>(Get());
1463 }
1464 
HasFree()1465 bool CVdpauBufferPool::HasFree()
1466 {
1467   return !freeRenderPics.empty();
1468 }
1469 
QueueReturnPicture(CVdpauRenderPicture * pic)1470 void CVdpauBufferPool::QueueReturnPicture(CVdpauRenderPicture *pic)
1471 {
1472   std::deque<int>::iterator it;
1473   for (it = usedRenderPics.begin(); it != usedRenderPics.end(); ++it)
1474   {
1475     if (allRenderPics[*it] == pic)
1476     {
1477       break;
1478     }
1479   }
1480 
1481   if (it == usedRenderPics.end())
1482   {
1483     CLog::Log(LOGWARNING, "COutput::QueueReturnPicture - pic not found");
1484     return;
1485   }
1486 
1487   // check if already queued
1488   std::deque<int>::iterator it2 = find(syncRenderPics.begin(),
1489                                        syncRenderPics.end(),
1490                                        *it);
1491   if (it2 == syncRenderPics.end())
1492   {
1493     syncRenderPics.push_back(*it);
1494   }
1495 }
1496 
ProcessSyncPicture()1497 CVdpauRenderPicture* CVdpauBufferPool::ProcessSyncPicture()
1498 {
1499   CVdpauRenderPicture *retPic = nullptr;
1500 
1501   std::deque<int>::iterator it;
1502   for (it = syncRenderPics.begin(); it != syncRenderPics.end(); )
1503   {
1504     retPic = allRenderPics[*it];
1505 
1506     freeRenderPics.push_back(*it);
1507 
1508     std::deque<int>::iterator it2 = find(usedRenderPics.begin(),
1509                                          usedRenderPics.end(),
1510                                          *it);
1511     if (it2 == usedRenderPics.end())
1512     {
1513       CLog::Log(LOGERROR, "COutput::ProcessSyncPicture - pic not found in queue");
1514     }
1515     else
1516     {
1517       usedRenderPics.erase(it2);
1518     }
1519     it = syncRenderPics.erase(it);
1520 
1521     break;
1522   }
1523   return retPic;
1524 }
1525 
InvalidateUsed()1526 void CVdpauBufferPool::InvalidateUsed()
1527 {
1528   std::deque<int>::iterator it;
1529   for (it = usedRenderPics.begin(); it != usedRenderPics.end(); ++it)
1530   {
1531     allRenderPics[*it]->procPic.outputSurface = VDP_INVALID_HANDLE;
1532     allRenderPics[*it]->procPic.videoSurface = VDP_INVALID_HANDLE;
1533   }
1534 }
1535 
1536 //-----------------------------------------------------------------------------
1537 // Mixer
1538 //-----------------------------------------------------------------------------
CMixer(CEvent * inMsgEvent)1539 CMixer::CMixer(CEvent *inMsgEvent) :
1540   CThread("Vdpau Mixer"),
1541   m_controlPort("ControlPort", inMsgEvent, &m_outMsgEvent),
1542   m_dataPort("DataPort", inMsgEvent, &m_outMsgEvent)
1543 {
1544   m_inMsgEvent = inMsgEvent;
1545 }
1546 
~CMixer()1547 CMixer::~CMixer()
1548 {
1549   Dispose();
1550 }
1551 
Start()1552 void CMixer::Start()
1553 {
1554   Create();
1555 }
1556 
Dispose()1557 void CMixer::Dispose()
1558 {
1559   m_bStop = true;
1560   m_outMsgEvent.Set();
1561   StopThread();
1562 
1563   m_controlPort.Purge();
1564   m_dataPort.Purge();
1565 }
1566 
IsActive()1567 bool CMixer::IsActive()
1568 {
1569   return IsRunning();
1570 }
1571 
OnStartup()1572 void CMixer::OnStartup()
1573 {
1574   CLog::Log(LOGINFO, "CMixer::OnStartup: Output Thread created");
1575 }
1576 
OnExit()1577 void CMixer::OnExit()
1578 {
1579   CLog::Log(LOGINFO, "CMixer::OnExit: Output Thread terminated");
1580 }
1581 
1582 enum MIXER_STATES
1583 {
1584   M_TOP = 0,                      // 0
1585   M_TOP_ERROR,                    // 1
1586   M_TOP_UNCONFIGURED,             // 2
1587   M_TOP_CONFIGURED,               // 3
1588   M_TOP_CONFIGURED_WAIT1,         // 4
1589   M_TOP_CONFIGURED_STEP1,         // 5
1590   M_TOP_CONFIGURED_WAIT2,         // 6
1591   M_TOP_CONFIGURED_STEP2,         // 7
1592 };
1593 
1594 int MIXER_parentStates[] = {
1595     -1,
1596     0, //TOP_ERROR
1597     0, //TOP_UNCONFIGURED
1598     0, //TOP_CONFIGURED
1599     3, //TOP_CONFIGURED_WAIT1
1600     3, //TOP_CONFIGURED_STEP1
1601     3, //TOP_CONFIGURED_WAIT2
1602     3, //TOP_CONFIGURED_STEP2
1603 };
1604 
StateMachine(int signal,Protocol * port,Message * msg)1605 void CMixer::StateMachine(int signal, Protocol *port, Message *msg)
1606 {
1607   for (int state = m_state; ; state = MIXER_parentStates[state])
1608   {
1609     switch (state)
1610     {
1611     case M_TOP: // TOP
1612       if (port == &m_controlPort)
1613       {
1614         switch (signal)
1615         {
1616         case CMixerControlProtocol::FLUSH:
1617           Flush();
1618           msg->Reply(CMixerControlProtocol::ACC);
1619           return;
1620         default:
1621           break;
1622         }
1623       }
1624       {
1625         std::string portName = port == NULL ? "timer" : port->portName;
1626         CLog::Log(LOGWARNING, "CMixer::%s - signal: %d form port: %s not handled for state: %d", __FUNCTION__, signal, portName.c_str(), m_state);
1627       }
1628       return;
1629 
1630     case M_TOP_ERROR: // TOP
1631       break;
1632 
1633     case M_TOP_UNCONFIGURED:
1634       if (port == &m_controlPort)
1635       {
1636         switch (signal)
1637         {
1638         case CMixerControlProtocol::INIT:
1639           CVdpauConfig *data;
1640           data = (CVdpauConfig*)msg->data;
1641           if (data)
1642           {
1643             m_config = *data;
1644           }
1645           Init();
1646           if (!m_vdpError)
1647           {
1648             m_state = M_TOP_CONFIGURED_WAIT1;
1649             msg->Reply(CMixerControlProtocol::ACC);
1650           }
1651           else
1652           {
1653             msg->Reply(CMixerControlProtocol::ERROR);
1654           }
1655           return;
1656         default:
1657           break;
1658         }
1659       }
1660       break;
1661 
1662     case M_TOP_CONFIGURED:
1663       if (port == &m_controlPort)
1664       {
1665         switch (signal)
1666         {
1667         case CMixerControlProtocol::FLUSH:
1668           Flush();
1669           msg->Reply(CMixerControlProtocol::ACC);
1670           m_state = M_TOP_CONFIGURED_WAIT1;
1671           return;
1672         default:
1673           break;
1674         }
1675       }
1676       else if (port == &m_dataPort)
1677       {
1678         switch (signal)
1679         {
1680         case CMixerDataProtocol::FRAME:
1681           CPayloadWrap<CVdpauDecodedPicture> *payload;
1682           payload = dynamic_cast<CPayloadWrap<CVdpauDecodedPicture>*>(msg->payloadObj.get());
1683           if (payload)
1684           {
1685             m_decodedPics.push(*(payload->GetPlayload()));
1686           }
1687           m_extTimeout = 0;
1688           return;
1689         case CMixerDataProtocol::BUFFER:
1690           VdpOutputSurface *surf;
1691           surf = (VdpOutputSurface*)msg->data;
1692           if (surf)
1693           {
1694             m_outputSurfaces.push(*surf);
1695           }
1696           m_extTimeout = 0;
1697           return;
1698         default:
1699           break;
1700         }
1701       }
1702       break;
1703 
1704     case M_TOP_CONFIGURED_WAIT1:
1705       if (port == NULL) // timeout
1706       {
1707         switch (signal)
1708         {
1709         case CMixerControlProtocol::TIMEOUT:
1710           if (!m_decodedPics.empty() && !m_outputSurfaces.empty())
1711           {
1712             m_state = M_TOP_CONFIGURED_STEP1;
1713             m_bStateMachineSelfTrigger = true;
1714           }
1715           else if (!m_outputSurfaces.empty() &&
1716                    m_config.stats->IsDraining() &&
1717                    m_mixerInput.size() >= 1)
1718           {
1719             CVdpauDecodedPicture pic;
1720             pic.DVDPic.SetParams(m_mixerInput[0].DVDPic);
1721             pic.videoSurface = VDP_INVALID_HANDLE;
1722             m_decodedPics.push(pic);
1723             m_state = M_TOP_CONFIGURED_STEP1;
1724             m_bStateMachineSelfTrigger = true;
1725           }
1726           else
1727           {
1728             m_extTimeout = 100;
1729           }
1730           return;
1731         default:
1732           break;
1733         }
1734       }
1735       break;
1736 
1737     case M_TOP_CONFIGURED_STEP1:
1738       if (port == NULL) // timeout
1739       {
1740         switch (signal)
1741         {
1742         case CMixerControlProtocol::TIMEOUT:
1743           m_mixerInput.push_front(m_decodedPics.front());
1744           m_decodedPics.pop();
1745           if (m_mixerInput.size() < 2)
1746           {
1747             m_state = M_TOP_CONFIGURED_WAIT1;
1748             m_extTimeout = 0;
1749             return;
1750           }
1751           InitCycle();
1752           ProcessPicture();
1753           if (m_vdpError)
1754           {
1755             m_state = M_TOP_CONFIGURED_WAIT1;
1756             m_extTimeout = 1000;
1757             return;
1758           }
1759           if (!m_processPicture.isYuv)
1760             m_outputSurfaces.pop();
1761           m_config.stats->IncProcessed();
1762           m_config.stats->DecDecoded();
1763           m_dataPort.SendInMessage(CMixerDataProtocol::PICTURE,&m_processPicture,sizeof(m_processPicture));
1764           if (m_mixersteps > 1)
1765           {
1766             m_state = M_TOP_CONFIGURED_WAIT2;
1767             m_extTimeout = 0;
1768           }
1769           else
1770           {
1771             FiniCycle();
1772             m_state = M_TOP_CONFIGURED_WAIT1;
1773             m_extTimeout = 0;
1774           }
1775           return;
1776         default:
1777           break;
1778         }
1779       }
1780       break;
1781 
1782     case M_TOP_CONFIGURED_WAIT2:
1783       if (port == NULL) // timeout
1784       {
1785         switch (signal)
1786         {
1787         case CMixerControlProtocol::TIMEOUT:
1788           if (!m_outputSurfaces.empty())
1789           {
1790             m_state = M_TOP_CONFIGURED_STEP2;
1791             m_bStateMachineSelfTrigger = true;
1792           }
1793           else
1794           {
1795             m_extTimeout = 100;
1796           }
1797           return;
1798         default:
1799           break;
1800         }
1801       }
1802       break;
1803 
1804     case M_TOP_CONFIGURED_STEP2:
1805        if (port == NULL) // timeout
1806        {
1807          switch (signal)
1808          {
1809          case CMixerControlProtocol::TIMEOUT:
1810            m_processPicture.outputSurface = m_outputSurfaces.front();
1811            m_mixerstep = 1;
1812            ProcessPicture();
1813            if (m_vdpError)
1814            {
1815              m_state = M_TOP_CONFIGURED_WAIT1;
1816              m_extTimeout = 1000;
1817              return;
1818            }
1819            if (!m_processPicture.isYuv)
1820              m_outputSurfaces.pop();
1821            m_config.stats->IncProcessed();
1822            m_dataPort.SendInMessage(CMixerDataProtocol::PICTURE,&m_processPicture,sizeof(m_processPicture));
1823            FiniCycle();
1824            m_state = M_TOP_CONFIGURED_WAIT1;
1825            m_extTimeout = 0;
1826            return;
1827          default:
1828            break;
1829          }
1830        }
1831        break;
1832 
1833     default: // we are in no state, should not happen
1834       CLog::Log(LOGERROR, "CMixer::%s - no valid state: %d", __FUNCTION__, m_state);
1835       return;
1836     }
1837   } // for
1838 }
1839 
Process()1840 void CMixer::Process()
1841 {
1842   Message *msg = NULL;
1843   Protocol *port = NULL;
1844   bool gotMsg;
1845 
1846   m_state = M_TOP_UNCONFIGURED;
1847   m_extTimeout = 1000;
1848   m_bStateMachineSelfTrigger = false;
1849 
1850   while (!m_bStop)
1851   {
1852     gotMsg = false;
1853 
1854     if (m_bStateMachineSelfTrigger)
1855     {
1856       m_bStateMachineSelfTrigger = false;
1857       // self trigger state machine
1858       StateMachine(msg->signal, port, msg);
1859       if (!m_bStateMachineSelfTrigger)
1860       {
1861         msg->Release();
1862         msg = NULL;
1863       }
1864       continue;
1865     }
1866     // check control port
1867     else if (m_controlPort.ReceiveOutMessage(&msg))
1868     {
1869       gotMsg = true;
1870       port = &m_controlPort;
1871     }
1872     // check data port
1873     else if (m_dataPort.ReceiveOutMessage(&msg))
1874     {
1875       gotMsg = true;
1876       port = &m_dataPort;
1877     }
1878 
1879     if (gotMsg)
1880     {
1881       StateMachine(msg->signal, port, msg);
1882       if (!m_bStateMachineSelfTrigger)
1883       {
1884         msg->Release();
1885         msg = NULL;
1886       }
1887       continue;
1888     }
1889 
1890     // wait for message
1891     else if (m_outMsgEvent.WaitMSec(m_extTimeout))
1892     {
1893       continue;
1894     }
1895     // time out
1896     else
1897     {
1898       msg = m_controlPort.GetMessage();
1899       msg->signal = CMixerControlProtocol::TIMEOUT;
1900       port = 0;
1901       // signal timeout to state machine
1902       StateMachine(msg->signal, port, msg);
1903       if (!m_bStateMachineSelfTrigger)
1904       {
1905         msg->Release();
1906         msg = NULL;
1907       }
1908     }
1909   }
1910   Uninit();
1911 }
1912 
CreateVdpauMixer()1913 void CMixer::CreateVdpauMixer()
1914 {
1915   CLog::Log(LOGINFO, " (VDPAU) Creating the video mixer");
1916 
1917   InitCSCMatrix(m_config.vidWidth);
1918 
1919   VdpVideoMixerParameter parameters[] = {
1920     VDP_VIDEO_MIXER_PARAMETER_VIDEO_SURFACE_WIDTH,
1921     VDP_VIDEO_MIXER_PARAMETER_VIDEO_SURFACE_HEIGHT,
1922     VDP_VIDEO_MIXER_PARAMETER_CHROMA_TYPE};
1923 
1924   void const * parameter_values[] = {
1925     &m_config.surfaceWidth,
1926     &m_config.surfaceHeight,
1927     &m_config.vdpChromaType};
1928 
1929   VdpStatus vdp_st = m_config.context->GetProcs().vdp_video_mixer_create(m_config.context->GetDevice(),
1930                                 m_config.context->GetFeatureCount(),
1931                                 m_config.context->GetFeatures(),
1932                                 ARSIZE(parameters),
1933                                 parameters,
1934                                 parameter_values,
1935                                 &m_videoMixer);
1936   CheckStatus(vdp_st, __LINE__);
1937 
1938 }
1939 
InitCSCMatrix(int Width)1940 void CMixer::InitCSCMatrix(int Width)
1941 {
1942   m_Procamp.struct_version = VDP_PROCAMP_VERSION;
1943   m_Procamp.brightness     = 0.0;
1944   m_Procamp.contrast       = 1.0;
1945   m_Procamp.saturation     = 1.0;
1946   m_Procamp.hue            = 0;
1947 }
1948 
CheckFeatures()1949 void CMixer::CheckFeatures()
1950 {
1951   if (m_Upscale != m_config.upscale)
1952   {
1953     SetHWUpscaling();
1954     m_Upscale = m_config.upscale;
1955   }
1956   if (m_Brightness != m_config.processInfo->GetVideoSettings().m_Brightness ||
1957       m_Contrast   != m_config.processInfo->GetVideoSettings().m_Contrast ||
1958       m_ColorMatrix != m_mixerInput[1].DVDPic.color_space)
1959   {
1960     SetColor();
1961     m_Brightness = m_config.processInfo->GetVideoSettings().m_Brightness;
1962     m_Contrast = m_config.processInfo->GetVideoSettings().m_Contrast;
1963     m_ColorMatrix = m_mixerInput[1].DVDPic.color_space;
1964   }
1965   if (m_NoiseReduction != m_config.processInfo->GetVideoSettings().m_NoiseReduction)
1966   {
1967     m_NoiseReduction = m_config.processInfo->GetVideoSettings().m_NoiseReduction;
1968     SetNoiseReduction();
1969   }
1970   if (m_Sharpness != m_config.processInfo->GetVideoSettings().m_Sharpness)
1971   {
1972     m_Sharpness = m_config.processInfo->GetVideoSettings().m_Sharpness;
1973     SetSharpness();
1974   }
1975   if (m_Deint != m_config.processInfo->GetVideoSettings().m_InterlaceMethod)
1976   {
1977     m_Deint = m_config.processInfo->GetVideoSettings().m_InterlaceMethod;
1978     SetDeinterlacing();
1979   }
1980 }
1981 
SetPostProcFeatures(bool postProcEnabled)1982 void CMixer::SetPostProcFeatures(bool postProcEnabled)
1983 {
1984   if (m_PostProc != postProcEnabled)
1985   {
1986     if (postProcEnabled)
1987     {
1988       SetNoiseReduction();
1989       SetSharpness();
1990       SetDeinterlacing();
1991       SetHWUpscaling();
1992     }
1993     else
1994       PostProcOff();
1995     m_PostProc = postProcEnabled;
1996   }
1997 }
1998 
PostProcOff()1999 void CMixer::PostProcOff()
2000 {
2001   VdpStatus vdp_st;
2002 
2003   if (m_videoMixer == VDP_INVALID_HANDLE)
2004     return;
2005 
2006   VdpVideoMixerFeature feature[] = { VDP_VIDEO_MIXER_FEATURE_DEINTERLACE_TEMPORAL,
2007                                      VDP_VIDEO_MIXER_FEATURE_DEINTERLACE_TEMPORAL_SPATIAL,
2008                                      VDP_VIDEO_MIXER_FEATURE_INVERSE_TELECINE};
2009 
2010   VdpBool enabled[]={0,0,0};
2011   vdp_st = m_config.context->GetProcs().vdp_video_mixer_set_feature_enables(m_videoMixer, ARSIZE(feature), feature, enabled);
2012   CheckStatus(vdp_st, __LINE__);
2013 
2014   if(m_config.vdpau->Supports(VDP_VIDEO_MIXER_FEATURE_NOISE_REDUCTION))
2015   {
2016     VdpVideoMixerFeature feature[] = { VDP_VIDEO_MIXER_FEATURE_NOISE_REDUCTION};
2017 
2018     VdpBool enabled[]={0};
2019     vdp_st = m_config.context->GetProcs().vdp_video_mixer_set_feature_enables(m_videoMixer, ARSIZE(feature), feature, enabled);
2020     CheckStatus(vdp_st, __LINE__);
2021   }
2022 
2023   if(m_config.vdpau->Supports(VDP_VIDEO_MIXER_FEATURE_SHARPNESS))
2024   {
2025     VdpVideoMixerFeature feature[] = { VDP_VIDEO_MIXER_FEATURE_SHARPNESS};
2026 
2027     VdpBool enabled[]={0};
2028     vdp_st = m_config.context->GetProcs().vdp_video_mixer_set_feature_enables(m_videoMixer, ARSIZE(feature), feature, enabled);
2029     CheckStatus(vdp_st, __LINE__);
2030   }
2031 
2032   DisableHQScaling();
2033 }
2034 
GenerateStudioCSCMatrix(VdpColorStandard colorStandard,VdpCSCMatrix & studioCSCMatrix)2035 bool CMixer::GenerateStudioCSCMatrix(VdpColorStandard colorStandard, VdpCSCMatrix &studioCSCMatrix)
2036 {
2037    // instead use studioCSCKCoeffs601[3], studioCSCKCoeffs709[3] to generate float[3][4] matrix (float studioCSC[3][4])
2038    // m00 = mRY = red: luma factor (contrast factor) (1.0)
2039    // m10 = mGY = green: luma factor (contrast factor) (1.0)
2040    // m20 = mBY = blue: luma factor (contrast factor) (1.0)
2041    //
2042    // m01 = mRB = red: blue color diff coeff (0.0)
2043    // m11 = mGB = green: blue color diff coeff (-2Kb(1-Kb)/(Kg))
2044    // m21 = mBB = blue: blue color diff coeff ((1-Kb)/0.5)
2045    //
2046    // m02 = mRR = red: red color diff coeff ((1-Kr)/0.5)
2047    // m12 = mGR = green: red color diff coeff (-2Kr(1-Kr)/(Kg))
2048    // m22 = mBR = blue: red color diff coeff (0.0)
2049    //
2050    // m03 = mRC = red: colour zero offset (brightness factor) (-(1-Kr)/0.5 * (128/255))
2051    // m13 = mGC = green: colour zero offset (brightness factor) ((256/255) * (Kb(1-Kb) + Kr(1-Kr)) / Kg)
2052    // m23 = mBC = blue: colour zero offset (brightness factor) (-(1-Kb)/0.5 * (128/255))
2053 
2054    // columns
2055    int Y = 0;
2056    int Cb = 1;
2057    int Cr = 2;
2058    int C = 3;
2059    // rows
2060    int R = 0;
2061    int G = 1;
2062    int B = 2;
2063    // colour standard coefficients for red, geen, blue
2064    double Kr, Kg, Kb;
2065    // colour diff zero position (use standard 8-bit coding precision)
2066    double CDZ = 128; //256*0.5
2067    // range excursion (use standard 8-bit coding precision)
2068    double EXC = 255; //256-1
2069 
2070    if (colorStandard == VDP_COLOR_STANDARD_ITUR_BT_601)
2071    {
2072       Kr = studioCSCKCoeffs601[0];
2073       Kg = studioCSCKCoeffs601[1];
2074       Kb = studioCSCKCoeffs601[2];
2075    }
2076    else // assume VDP_COLOR_STANDARD_ITUR_BT_709
2077    {
2078       Kr = studioCSCKCoeffs709[0];
2079       Kg = studioCSCKCoeffs709[1];
2080       Kb = studioCSCKCoeffs709[2];
2081    }
2082    // we keep luma unscaled to retain the levels present in source so that 16-235 luma is converted to RGB 16-235
2083    studioCSCMatrix[R][Y] = 1.0;
2084    studioCSCMatrix[G][Y] = 1.0;
2085    studioCSCMatrix[B][Y] = 1.0;
2086 
2087    studioCSCMatrix[R][Cb] = 0.0;
2088    studioCSCMatrix[G][Cb] = (double)-2 * Kb * (1 - Kb) / Kg;
2089    studioCSCMatrix[B][Cb] = (double)(1 - Kb) / 0.5;
2090 
2091    studioCSCMatrix[R][Cr] = (double)(1 - Kr) / 0.5;
2092    studioCSCMatrix[G][Cr] = (double)-2 * Kr * (1 - Kr) / Kg;
2093    studioCSCMatrix[B][Cr] = 0.0;
2094 
2095    studioCSCMatrix[R][C] = (double)-1 * studioCSCMatrix[R][Cr] * CDZ/EXC;
2096    studioCSCMatrix[G][C] = (double)-1 * (studioCSCMatrix[G][Cb] + studioCSCMatrix[G][Cr]) * CDZ/EXC;
2097    studioCSCMatrix[B][C] = (double)-1 * studioCSCMatrix[B][Cb] * CDZ/EXC;
2098 
2099    return true;
2100 }
2101 
SetColor()2102 void CMixer::SetColor()
2103 {
2104   VdpStatus vdp_st;
2105 
2106   if (m_Brightness != m_config.processInfo->GetVideoSettings().m_Brightness)
2107     m_Procamp.brightness = (float)((m_config.processInfo->GetVideoSettings().m_Brightness)-50) / 100;
2108   if (m_Contrast != m_config.processInfo->GetVideoSettings().m_Contrast)
2109     m_Procamp.contrast = (float)((m_config.processInfo->GetVideoSettings().m_Contrast)+50) / 100;
2110 
2111   VdpColorStandard colorStandard;
2112   switch(m_mixerInput[1].DVDPic.color_space)
2113   {
2114     case AVCOL_SPC_BT709:
2115       colorStandard = VDP_COLOR_STANDARD_ITUR_BT_709;
2116       break;
2117     case AVCOL_SPC_BT470BG:
2118     case AVCOL_SPC_SMPTE170M:
2119       colorStandard = VDP_COLOR_STANDARD_ITUR_BT_601;
2120       break;
2121     case AVCOL_SPC_SMPTE240M:
2122       colorStandard = VDP_COLOR_STANDARD_SMPTE_240M;
2123       break;
2124     case AVCOL_SPC_FCC:
2125     case AVCOL_SPC_UNSPECIFIED:
2126     case AVCOL_SPC_RGB:
2127     default:
2128       if(m_config.surfaceWidth > 1000)
2129         colorStandard = VDP_COLOR_STANDARD_ITUR_BT_709;
2130       else
2131         colorStandard = VDP_COLOR_STANDARD_ITUR_BT_601;
2132   }
2133 
2134   VdpVideoMixerAttribute attributes[] = { VDP_VIDEO_MIXER_ATTRIBUTE_CSC_MATRIX };
2135   if (CServiceBroker::GetSettingsComponent()->GetSettings()->GetBool(CSettings::SETTING_VIDEOSCREEN_LIMITEDRANGE))
2136   {
2137     float studioCSC[3][4];
2138     GenerateStudioCSCMatrix(colorStandard, studioCSC);
2139     void const * pm_CSCMatrix[] = { &studioCSC };
2140     vdp_st = m_config.context->GetProcs().vdp_video_mixer_set_attribute_values(m_videoMixer, ARSIZE(attributes), attributes, pm_CSCMatrix);
2141   }
2142   else
2143   {
2144     vdp_st = m_config.context->GetProcs().vdp_generate_csc_matrix(&m_Procamp, colorStandard, &m_CSCMatrix);
2145     if(vdp_st != VDP_STATUS_ERROR)
2146     {
2147       void const * pm_CSCMatrix[] = { &m_CSCMatrix };
2148       vdp_st = m_config.context->GetProcs().vdp_video_mixer_set_attribute_values(m_videoMixer, ARSIZE(attributes), attributes, pm_CSCMatrix);
2149     }
2150   }
2151 
2152   CheckStatus(vdp_st, __LINE__);
2153 }
2154 
SetNoiseReduction()2155 void CMixer::SetNoiseReduction()
2156 {
2157   if(!m_config.vdpau->Supports(VDP_VIDEO_MIXER_FEATURE_NOISE_REDUCTION))
2158     return;
2159 
2160   VdpVideoMixerFeature feature[] = { VDP_VIDEO_MIXER_FEATURE_NOISE_REDUCTION };
2161   VdpVideoMixerAttribute attributes[] = { VDP_VIDEO_MIXER_ATTRIBUTE_NOISE_REDUCTION_LEVEL };
2162   VdpStatus vdp_st;
2163 
2164   if (!m_config.processInfo->GetVideoSettings().m_NoiseReduction)
2165   {
2166     VdpBool enabled[]= {0};
2167     vdp_st = m_config.context->GetProcs().vdp_video_mixer_set_feature_enables(m_videoMixer, ARSIZE(feature), feature, enabled);
2168     CheckStatus(vdp_st, __LINE__);
2169     return;
2170   }
2171   VdpBool enabled[]={1};
2172   vdp_st = m_config.context->GetProcs().vdp_video_mixer_set_feature_enables(m_videoMixer, ARSIZE(feature), feature, enabled);
2173   CheckStatus(vdp_st, __LINE__);
2174   float noiseReduction = m_config.processInfo->GetVideoSettings().m_NoiseReduction;
2175   void* nr[] = { &noiseReduction };
2176   CLog::Log(LOGINFO, "Setting Noise Reduction to %f",
2177             m_config.processInfo->GetVideoSettings().m_NoiseReduction);
2178   vdp_st = m_config.context->GetProcs().vdp_video_mixer_set_attribute_values(m_videoMixer, ARSIZE(attributes), attributes, nr);
2179   CheckStatus(vdp_st, __LINE__);
2180 }
2181 
SetSharpness()2182 void CMixer::SetSharpness()
2183 {
2184   if(!m_config.vdpau->Supports(VDP_VIDEO_MIXER_FEATURE_SHARPNESS))
2185     return;
2186 
2187   VdpVideoMixerFeature feature[] = { VDP_VIDEO_MIXER_FEATURE_SHARPNESS };
2188   VdpVideoMixerAttribute attributes[] = { VDP_VIDEO_MIXER_ATTRIBUTE_SHARPNESS_LEVEL };
2189   VdpStatus vdp_st;
2190 
2191   if (!m_config.processInfo->GetVideoSettings().m_Sharpness)
2192   {
2193     VdpBool enabled[]={0};
2194     vdp_st = m_config.context->GetProcs().vdp_video_mixer_set_feature_enables(m_videoMixer, ARSIZE(feature), feature, enabled);
2195     CheckStatus(vdp_st, __LINE__);
2196     return;
2197   }
2198   VdpBool enabled[]={1};
2199   vdp_st = m_config.context->GetProcs().vdp_video_mixer_set_feature_enables(m_videoMixer, ARSIZE(feature), feature, enabled);
2200   CheckStatus(vdp_st, __LINE__);
2201   float sharpness = m_config.processInfo->GetVideoSettings().m_Sharpness;
2202   void* sh[] = { &sharpness };
2203   CLog::Log(LOGINFO, "Setting Sharpness to %f",
2204             m_config.processInfo->GetVideoSettings().m_Sharpness);
2205   vdp_st = m_config.context->GetProcs().vdp_video_mixer_set_attribute_values(m_videoMixer, ARSIZE(attributes), attributes, sh);
2206   CheckStatus(vdp_st, __LINE__);
2207 }
2208 
SetDeinterlacing()2209 void CMixer::SetDeinterlacing()
2210 {
2211   VdpStatus vdp_st;
2212 
2213   if (m_videoMixer == VDP_INVALID_HANDLE)
2214     return;
2215 
2216   EINTERLACEMETHOD method = m_config.processInfo->GetVideoSettings().m_InterlaceMethod;
2217 
2218   VdpVideoMixerFeature feature[] = { VDP_VIDEO_MIXER_FEATURE_DEINTERLACE_TEMPORAL,
2219                                      VDP_VIDEO_MIXER_FEATURE_DEINTERLACE_TEMPORAL_SPATIAL,
2220                                      VDP_VIDEO_MIXER_FEATURE_INVERSE_TELECINE };
2221 
2222   if (method == VS_INTERLACEMETHOD_NONE)
2223   {
2224     VdpBool enabled[] = {0,0,0};
2225     vdp_st = m_config.context->GetProcs().vdp_video_mixer_set_feature_enables(m_videoMixer, ARSIZE(feature), feature, enabled);
2226   }
2227   else
2228   {
2229     // fall back path if called with non supported method
2230     if (!m_config.processInfo->Supports(method))
2231     {
2232       method = VS_INTERLACEMETHOD_VDPAU_TEMPORAL;
2233       m_Deint = VS_INTERLACEMETHOD_VDPAU_TEMPORAL;
2234     }
2235 
2236     if (method == VS_INTERLACEMETHOD_VDPAU_TEMPORAL
2237     ||  method == VS_INTERLACEMETHOD_VDPAU_TEMPORAL_HALF)
2238     {
2239       VdpBool enabled[] = {1,0,0};
2240       if (CServiceBroker::GetSettingsComponent()->GetAdvancedSettings()->m_videoVDPAUtelecine)
2241         enabled[2] = 1;
2242       vdp_st = m_config.context->GetProcs().vdp_video_mixer_set_feature_enables(m_videoMixer, ARSIZE(feature), feature, enabled);
2243     }
2244     else if (method == VS_INTERLACEMETHOD_VDPAU_TEMPORAL_SPATIAL
2245          ||  method == VS_INTERLACEMETHOD_VDPAU_TEMPORAL_SPATIAL_HALF)
2246     {
2247       VdpBool enabled[] = {1,1,0};
2248       if (CServiceBroker::GetSettingsComponent()->GetAdvancedSettings()->m_videoVDPAUtelecine)
2249         enabled[2] = 1;
2250       vdp_st = m_config.context->GetProcs().vdp_video_mixer_set_feature_enables(m_videoMixer, ARSIZE(feature), feature, enabled);
2251     }
2252     else
2253     {
2254       VdpBool enabled[]={0,0,0};
2255       vdp_st = m_config.context->GetProcs().vdp_video_mixer_set_feature_enables(m_videoMixer, ARSIZE(feature), feature, enabled);
2256     }
2257   }
2258   CheckStatus(vdp_st, __LINE__);
2259 
2260   SetDeintSkipChroma();
2261 
2262   m_config.useInteropYuv = !CServiceBroker::GetSettingsComponent()->GetSettings()->GetBool(CSettings::SETTING_VIDEOPLAYER_USEVDPAUMIXER);
2263 
2264   std::string deintStr = GetDeintStrFromInterlaceMethod(method);
2265   // update deinterlacing method used in processInfo (none if progressive)
2266   m_config.processInfo->SetVideoDeintMethod(deintStr);
2267 }
2268 
SetDeintSkipChroma()2269 void CMixer::SetDeintSkipChroma()
2270 {
2271   VdpVideoMixerAttribute attribute[] = { VDP_VIDEO_MIXER_ATTRIBUTE_SKIP_CHROMA_DEINTERLACE};
2272   VdpStatus vdp_st;
2273 
2274   uint8_t val;
2275   if (CServiceBroker::GetSettingsComponent()->GetAdvancedSettings()->m_videoVDPAUdeintSkipChromaHD && m_config.outHeight >= 720)
2276     val = 1;
2277   else
2278     val = 0;
2279 
2280   void const *values[]={&val};
2281   vdp_st = m_config.context->GetProcs().vdp_video_mixer_set_attribute_values(m_videoMixer, ARSIZE(attribute), attribute, values);
2282 
2283   CheckStatus(vdp_st, __LINE__);
2284 }
2285 
SetHWUpscaling()2286 void CMixer::SetHWUpscaling()
2287 {
2288 #ifdef VDP_VIDEO_MIXER_FEATURE_HIGH_QUALITY_SCALING_L1
2289 
2290   VdpStatus vdp_st;
2291   VdpBool enabled[]={1};
2292   switch (m_config.upscale)
2293   {
2294     case 9:
2295        if (m_config.vdpau->Supports(VDP_VIDEO_MIXER_FEATURE_HIGH_QUALITY_SCALING_L9))
2296        {
2297           VdpVideoMixerFeature feature[] = { VDP_VIDEO_MIXER_FEATURE_HIGH_QUALITY_SCALING_L9 };
2298           vdp_st = m_config.context->GetProcs().vdp_video_mixer_set_feature_enables(m_videoMixer, ARSIZE(feature), feature, enabled);
2299           break;
2300        }
2301     case 8:
2302        if (m_config.vdpau->Supports(VDP_VIDEO_MIXER_FEATURE_HIGH_QUALITY_SCALING_L8))
2303        {
2304           VdpVideoMixerFeature feature[] = { VDP_VIDEO_MIXER_FEATURE_HIGH_QUALITY_SCALING_L8 };
2305           vdp_st = m_config.context->GetProcs().vdp_video_mixer_set_feature_enables(m_videoMixer, ARSIZE(feature), feature, enabled);
2306           break;
2307        }
2308     case 7:
2309        if (m_config.vdpau->Supports(VDP_VIDEO_MIXER_FEATURE_HIGH_QUALITY_SCALING_L7))
2310        {
2311           VdpVideoMixerFeature feature[] = { VDP_VIDEO_MIXER_FEATURE_HIGH_QUALITY_SCALING_L7 };
2312           vdp_st = m_config.context->GetProcs().vdp_video_mixer_set_feature_enables(m_videoMixer, ARSIZE(feature), feature, enabled);
2313           break;
2314        }
2315     case 6:
2316        if (m_config.vdpau->Supports(VDP_VIDEO_MIXER_FEATURE_HIGH_QUALITY_SCALING_L6))
2317        {
2318           VdpVideoMixerFeature feature[] = { VDP_VIDEO_MIXER_FEATURE_HIGH_QUALITY_SCALING_L6 };
2319           vdp_st = m_config.context->GetProcs().vdp_video_mixer_set_feature_enables(m_videoMixer, ARSIZE(feature), feature, enabled);
2320           break;
2321        }
2322     case 5:
2323        if (m_config.vdpau->Supports(VDP_VIDEO_MIXER_FEATURE_HIGH_QUALITY_SCALING_L5))
2324        {
2325           VdpVideoMixerFeature feature[] = { VDP_VIDEO_MIXER_FEATURE_HIGH_QUALITY_SCALING_L5 };
2326           vdp_st = m_config.context->GetProcs().vdp_video_mixer_set_feature_enables(m_videoMixer, ARSIZE(feature), feature, enabled);
2327           break;
2328        }
2329     case 4:
2330        if (m_config.vdpau->Supports(VDP_VIDEO_MIXER_FEATURE_HIGH_QUALITY_SCALING_L4))
2331        {
2332           VdpVideoMixerFeature feature[] = { VDP_VIDEO_MIXER_FEATURE_HIGH_QUALITY_SCALING_L4 };
2333           vdp_st = m_config.context->GetProcs().vdp_video_mixer_set_feature_enables(m_videoMixer, ARSIZE(feature), feature, enabled);
2334           break;
2335        }
2336     case 3:
2337        if (m_config.vdpau->Supports(VDP_VIDEO_MIXER_FEATURE_HIGH_QUALITY_SCALING_L3))
2338        {
2339           VdpVideoMixerFeature feature[] = { VDP_VIDEO_MIXER_FEATURE_HIGH_QUALITY_SCALING_L3 };
2340           vdp_st = m_config.context->GetProcs().vdp_video_mixer_set_feature_enables(m_videoMixer, ARSIZE(feature), feature, enabled);
2341           break;
2342        }
2343     case 2:
2344        if (m_config.vdpau->Supports(VDP_VIDEO_MIXER_FEATURE_HIGH_QUALITY_SCALING_L2))
2345        {
2346           VdpVideoMixerFeature feature[] = { VDP_VIDEO_MIXER_FEATURE_HIGH_QUALITY_SCALING_L2 };
2347           vdp_st = m_config.context->GetProcs().vdp_video_mixer_set_feature_enables(m_videoMixer, ARSIZE(feature), feature, enabled);
2348           break;
2349        }
2350     case 1:
2351        if (m_config.vdpau->Supports(VDP_VIDEO_MIXER_FEATURE_HIGH_QUALITY_SCALING_L1))
2352        {
2353           VdpVideoMixerFeature feature[] = { VDP_VIDEO_MIXER_FEATURE_HIGH_QUALITY_SCALING_L1 };
2354           vdp_st = m_config.context->GetProcs().vdp_video_mixer_set_feature_enables(m_videoMixer, ARSIZE(feature), feature, enabled);
2355           break;
2356        }
2357     default:
2358        DisableHQScaling();
2359        return;
2360   }
2361   CheckStatus(vdp_st, __LINE__);
2362 #endif
2363 }
2364 
DisableHQScaling()2365 void CMixer::DisableHQScaling()
2366 {
2367   VdpStatus vdp_st;
2368 
2369   if (m_videoMixer == VDP_INVALID_HANDLE)
2370     return;
2371 
2372   if(m_config.vdpau->Supports(VDP_VIDEO_MIXER_FEATURE_HIGH_QUALITY_SCALING_L1))
2373   {
2374     VdpVideoMixerFeature feature[] = { VDP_VIDEO_MIXER_FEATURE_HIGH_QUALITY_SCALING_L1 };
2375     VdpBool enabled[]={0};
2376     vdp_st = m_config.context->GetProcs().vdp_video_mixer_set_feature_enables(m_videoMixer, ARSIZE(feature), feature, enabled);
2377     CheckStatus(vdp_st, __LINE__);
2378   }
2379 
2380   if(m_config.vdpau->Supports(VDP_VIDEO_MIXER_FEATURE_HIGH_QUALITY_SCALING_L2))
2381   {
2382     VdpVideoMixerFeature feature[] = { VDP_VIDEO_MIXER_FEATURE_HIGH_QUALITY_SCALING_L2 };
2383     VdpBool enabled[]={0};
2384     vdp_st = m_config.context->GetProcs().vdp_video_mixer_set_feature_enables(m_videoMixer, ARSIZE(feature), feature, enabled);
2385     CheckStatus(vdp_st, __LINE__);
2386   }
2387 
2388   if(m_config.vdpau->Supports(VDP_VIDEO_MIXER_FEATURE_HIGH_QUALITY_SCALING_L3))
2389   {
2390     VdpVideoMixerFeature feature[] = { VDP_VIDEO_MIXER_FEATURE_HIGH_QUALITY_SCALING_L3 };
2391     VdpBool enabled[]={0};
2392     vdp_st = m_config.context->GetProcs().vdp_video_mixer_set_feature_enables(m_videoMixer, ARSIZE(feature), feature, enabled);
2393     CheckStatus(vdp_st, __LINE__);
2394   }
2395 
2396   if(m_config.vdpau->Supports(VDP_VIDEO_MIXER_FEATURE_HIGH_QUALITY_SCALING_L4))
2397   {
2398     VdpVideoMixerFeature feature[] = { VDP_VIDEO_MIXER_FEATURE_HIGH_QUALITY_SCALING_L4 };
2399     VdpBool enabled[]={0};
2400     vdp_st = m_config.context->GetProcs().vdp_video_mixer_set_feature_enables(m_videoMixer, ARSIZE(feature), feature, enabled);
2401     CheckStatus(vdp_st, __LINE__);
2402   }
2403 
2404   if(m_config.vdpau->Supports(VDP_VIDEO_MIXER_FEATURE_HIGH_QUALITY_SCALING_L5))
2405   {
2406     VdpVideoMixerFeature feature[] = { VDP_VIDEO_MIXER_FEATURE_HIGH_QUALITY_SCALING_L5 };
2407     VdpBool enabled[]={0};
2408     vdp_st = m_config.context->GetProcs().vdp_video_mixer_set_feature_enables(m_videoMixer, ARSIZE(feature), feature, enabled);
2409     CheckStatus(vdp_st, __LINE__);
2410   }
2411 
2412   if(m_config.vdpau->Supports(VDP_VIDEO_MIXER_FEATURE_HIGH_QUALITY_SCALING_L6))
2413   {
2414     VdpVideoMixerFeature feature[] = { VDP_VIDEO_MIXER_FEATURE_HIGH_QUALITY_SCALING_L6 };
2415     VdpBool enabled[]={0};
2416     vdp_st = m_config.context->GetProcs().vdp_video_mixer_set_feature_enables(m_videoMixer, ARSIZE(feature), feature, enabled);
2417     CheckStatus(vdp_st, __LINE__);
2418   }
2419 
2420   if(m_config.vdpau->Supports(VDP_VIDEO_MIXER_FEATURE_HIGH_QUALITY_SCALING_L7))
2421   {
2422     VdpVideoMixerFeature feature[] = { VDP_VIDEO_MIXER_FEATURE_HIGH_QUALITY_SCALING_L7 };
2423     VdpBool enabled[]={0};
2424     vdp_st = m_config.context->GetProcs().vdp_video_mixer_set_feature_enables(m_videoMixer, ARSIZE(feature), feature, enabled);
2425     CheckStatus(vdp_st, __LINE__);
2426   }
2427 
2428   if(m_config.vdpau->Supports(VDP_VIDEO_MIXER_FEATURE_HIGH_QUALITY_SCALING_L8))
2429   {
2430     VdpVideoMixerFeature feature[] = { VDP_VIDEO_MIXER_FEATURE_HIGH_QUALITY_SCALING_L8 };
2431     VdpBool enabled[]={0};
2432     vdp_st = m_config.context->GetProcs().vdp_video_mixer_set_feature_enables(m_videoMixer, ARSIZE(feature), feature, enabled);
2433     CheckStatus(vdp_st, __LINE__);
2434   }
2435 
2436   if(m_config.vdpau->Supports(VDP_VIDEO_MIXER_FEATURE_HIGH_QUALITY_SCALING_L9))
2437   {
2438     VdpVideoMixerFeature feature[] = { VDP_VIDEO_MIXER_FEATURE_HIGH_QUALITY_SCALING_L9 };
2439     VdpBool enabled[]={0};
2440     vdp_st = m_config.context->GetProcs().vdp_video_mixer_set_feature_enables(m_videoMixer, ARSIZE(feature), feature, enabled);
2441     CheckStatus(vdp_st, __LINE__);
2442   }
2443 }
2444 
Init()2445 void CMixer::Init()
2446 {
2447   m_Brightness = 0.0;
2448   m_Contrast = 0.0;
2449   m_NoiseReduction = 0.0;
2450   m_Sharpness = 0.0;
2451   m_Deint = 0;
2452   m_Upscale = 0;
2453   m_SeenInterlaceFlag = false;
2454   m_ColorMatrix = 0;
2455   m_PostProc = false;
2456   m_vdpError = false;
2457 
2458   m_config.upscale = CServiceBroker::GetSettingsComponent()->GetAdvancedSettings()->m_videoVDPAUScaling;
2459   m_config.useInteropYuv = !CServiceBroker::GetSettingsComponent()->GetSettings()->GetBool(CSettings::SETTING_VIDEOPLAYER_USEVDPAUMIXER);
2460 
2461   CreateVdpauMixer();
2462 
2463   // update deinterlacing methods in processInfo
2464   std::list<EINTERLACEMETHOD> deintMethods;
2465   deintMethods.push_back(VS_INTERLACEMETHOD_NONE);
2466   for(SInterlaceMapping* p = g_interlace_mapping; p->method != VS_INTERLACEMETHOD_NONE; p++)
2467   {
2468     if (p->method == VS_INTERLACEMETHOD_VDPAU_INVERSE_TELECINE)
2469       continue;
2470 
2471     if (m_config.vdpau->Supports(p->feature))
2472       deintMethods.push_back(p->method);
2473   }
2474   deintMethods.push_back(VS_INTERLACEMETHOD_VDPAU_BOB);
2475   deintMethods.push_back(VS_INTERLACEMETHOD_RENDER_BOB);
2476   m_config.processInfo->UpdateDeinterlacingMethods(deintMethods);
2477   m_config.processInfo->SetDeinterlacingMethodDefault(EINTERLACEMETHOD::VS_INTERLACEMETHOD_VDPAU_TEMPORAL);
2478 }
2479 
Uninit()2480 void CMixer::Uninit()
2481 {
2482   Flush();
2483   while (!m_outputSurfaces.empty())
2484   {
2485     m_outputSurfaces.pop();
2486   }
2487   m_config.context->GetProcs().vdp_video_mixer_destroy(m_videoMixer);
2488 }
2489 
Flush()2490 void CMixer::Flush()
2491 {
2492   while (!m_mixerInput.empty())
2493   {
2494     CVdpauDecodedPicture pic = m_mixerInput.back();
2495     m_mixerInput.pop_back();
2496     m_config.videoSurfaces->ClearRender(pic.videoSurface);
2497   }
2498   while (!m_decodedPics.empty())
2499   {
2500     CVdpauDecodedPicture pic = m_decodedPics.front();
2501     m_decodedPics.pop();
2502     m_config.videoSurfaces->ClearRender(pic.videoSurface);
2503   }
2504   Message *msg;
2505   while (m_dataPort.ReceiveOutMessage(&msg))
2506   {
2507     if (msg->signal == CMixerDataProtocol::FRAME)
2508     {
2509       CPayloadWrap<CVdpauDecodedPicture> *payload;
2510       payload = dynamic_cast<CPayloadWrap<CVdpauDecodedPicture>*>(msg->payloadObj.get());
2511       if (payload)
2512       {
2513         CVdpauDecodedPicture pic = *(payload->GetPlayload());
2514         m_config.videoSurfaces->ClearRender(pic.videoSurface);
2515       }
2516     }
2517     else if (msg->signal == CMixerDataProtocol::BUFFER)
2518     {
2519       VdpOutputSurface *surf;
2520       surf = (VdpOutputSurface*)msg->data;
2521       m_outputSurfaces.push(*surf);
2522     }
2523     msg->Release();
2524   }
2525 }
2526 
GetDeintStrFromInterlaceMethod(EINTERLACEMETHOD method)2527 std::string CMixer::GetDeintStrFromInterlaceMethod(EINTERLACEMETHOD method)
2528 {
2529   switch (method)
2530   {
2531     case VS_INTERLACEMETHOD_NONE:
2532       return "none";
2533     case VS_INTERLACEMETHOD_VDPAU_BOB:
2534       return "vdpau-bob";
2535     case VS_INTERLACEMETHOD_VDPAU_TEMPORAL:
2536       return "vdpau-temp";
2537     case VS_INTERLACEMETHOD_VDPAU_TEMPORAL_HALF:
2538       return "vdpau-temp-half";
2539     case VS_INTERLACEMETHOD_VDPAU_TEMPORAL_SPATIAL:
2540       return "vdpau-temp-spat";
2541     case VS_INTERLACEMETHOD_VDPAU_TEMPORAL_SPATIAL_HALF:
2542       return "vdpau-temp-spat-half";
2543     case VS_INTERLACEMETHOD_RENDER_BOB:
2544       return "bob";
2545     default:
2546       return "unknown";
2547   }
2548 }
2549 
InitCycle()2550 void CMixer::InitCycle()
2551 {
2552   CheckFeatures();
2553   int flags;
2554   uint64_t latency;
2555   m_config.stats->GetParams(latency, flags);
2556   if (flags & DVD_CODEC_CTRL_NO_POSTPROC)
2557     SetPostProcFeatures(false);
2558   else
2559     SetPostProcFeatures(true);
2560 
2561   m_config.stats->SetCanSkipDeint(false);
2562 
2563   EINTERLACEMETHOD method = m_config.processInfo->GetVideoSettings().m_InterlaceMethod;
2564   bool interlaced = m_mixerInput[1].DVDPic.iFlags & DVP_FLAG_INTERLACED;
2565   m_SeenInterlaceFlag |= interlaced;
2566 
2567   if (!(flags & DVD_CODEC_CTRL_NO_POSTPROC) &&
2568       interlaced &&
2569       method != VS_INTERLACEMETHOD_NONE)
2570   {
2571     if (!m_config.processInfo->Supports(method))
2572       method = VS_INTERLACEMETHOD_VDPAU_TEMPORAL;
2573 
2574     if (method == VS_INTERLACEMETHOD_VDPAU_BOB ||
2575         method == VS_INTERLACEMETHOD_VDPAU_TEMPORAL ||
2576         method == VS_INTERLACEMETHOD_VDPAU_TEMPORAL_HALF ||
2577         method == VS_INTERLACEMETHOD_VDPAU_TEMPORAL_SPATIAL ||
2578         method == VS_INTERLACEMETHOD_VDPAU_TEMPORAL_SPATIAL_HALF)
2579     {
2580       if(method == VS_INTERLACEMETHOD_VDPAU_TEMPORAL_HALF ||
2581          method == VS_INTERLACEMETHOD_VDPAU_TEMPORAL_SPATIAL_HALF ||
2582          !CServiceBroker::GetWinSystem()->GetGfxContext().IsFullScreenVideo())
2583         m_mixersteps = 1;
2584       else
2585       {
2586         m_mixersteps = 2;
2587         m_config.stats->SetCanSkipDeint(true);
2588       }
2589 
2590       if (m_mixerInput[1].DVDPic.iFlags & DVD_CODEC_CTRL_SKIPDEINT)
2591       {
2592         m_mixersteps = 1;
2593       }
2594 
2595       if(m_mixerInput[1].DVDPic.iFlags & DVP_FLAG_TOP_FIELD_FIRST)
2596         m_mixerfield = VDP_VIDEO_MIXER_PICTURE_STRUCTURE_TOP_FIELD;
2597       else
2598         m_mixerfield = VDP_VIDEO_MIXER_PICTURE_STRUCTURE_BOTTOM_FIELD;
2599 
2600       m_mixerInput[1].DVDPic.iFlags &= ~(DVP_FLAG_TOP_FIELD_FIRST |
2601                                         DVP_FLAG_REPEAT_TOP_FIELD |
2602                                         DVP_FLAG_INTERLACED);
2603       m_mixerInput[1].isYuv = false;
2604       m_config.useInteropYuv = false;
2605     }
2606     else if (method == VS_INTERLACEMETHOD_RENDER_BOB)
2607     {
2608       m_mixersteps = 1;
2609       m_mixerfield = VDP_VIDEO_MIXER_PICTURE_STRUCTURE_FRAME;
2610       m_mixerInput[1].isYuv = true;
2611       m_config.useInteropYuv = true;
2612     }
2613   }
2614   else
2615   {
2616     m_mixersteps = 1;
2617     m_mixerfield = VDP_VIDEO_MIXER_PICTURE_STRUCTURE_FRAME;
2618 
2619     if (m_config.useInteropYuv)
2620       m_mixerInput[1].isYuv = true;
2621     else
2622     {
2623       m_mixerInput[1].DVDPic.iFlags &= ~(DVP_FLAG_TOP_FIELD_FIRST |
2624                                         DVP_FLAG_REPEAT_TOP_FIELD |
2625                                         DVP_FLAG_INTERLACED);
2626       m_mixerInput[1].isYuv = false;
2627     }
2628   }
2629   m_mixerstep = 0;
2630   //m_mixerInput[1].DVDPic.format = RENDER_FMT_VDPAU;
2631 
2632   m_processPicture.crop = false;
2633   if (!m_mixerInput[1].isYuv)
2634   {
2635     m_processPicture.outputSurface = m_outputSurfaces.front();
2636     m_mixerInput[1].DVDPic.iWidth = m_config.outWidth;
2637     m_mixerInput[1].DVDPic.iHeight = m_config.outHeight;
2638     if (m_SeenInterlaceFlag)
2639     {
2640       double ratio = (double)m_mixerInput[1].DVDPic.iDisplayHeight / m_mixerInput[1].DVDPic.iHeight;
2641       m_mixerInput[1].DVDPic.iDisplayHeight = lrint(ratio*(m_mixerInput[1].DVDPic.iHeight-NUM_CROP_PIX*2));
2642       m_processPicture.crop = true;
2643     }
2644   }
2645   else
2646   {
2647     m_mixerInput[1].DVDPic.iWidth = m_config.vidWidth;
2648     m_mixerInput[1].DVDPic.iHeight = m_config.vidHeight;
2649   }
2650 
2651   m_processPicture.isYuv = m_mixerInput[1].isYuv;
2652   m_processPicture.DVDPic.SetParams(m_mixerInput[1].DVDPic);
2653   m_processPicture.videoSurface = m_mixerInput[1].videoSurface;
2654 }
2655 
FiniCycle()2656 void CMixer::FiniCycle()
2657 {
2658   // Keep video surfaces for one 2 cycles longer than used
2659   // by mixer. This avoids blocking in decoder.
2660   // NVidia recommends num_ref + 5
2661   size_t surfToKeep = 5;
2662 
2663   if (m_mixerInput.size() > 0 &&
2664       (m_mixerInput[0].videoSurface == VDP_INVALID_HANDLE))
2665     surfToKeep = 1;
2666 
2667   while (m_mixerInput.size() > surfToKeep)
2668   {
2669     CVdpauDecodedPicture &tmp = m_mixerInput.back();
2670     if (!m_processPicture.isYuv)
2671     {
2672       m_config.videoSurfaces->ClearRender(tmp.videoSurface);
2673     }
2674     m_mixerInput.pop_back();
2675   }
2676 
2677   if (surfToKeep == 1)
2678     m_mixerInput.clear();
2679 }
2680 
ProcessPicture()2681 void CMixer::ProcessPicture()
2682 {
2683   if (m_processPicture.isYuv)
2684     return;
2685 
2686   VdpStatus vdp_st;
2687 
2688   if (m_mixerstep == 1)
2689   {
2690     if(m_mixerfield == VDP_VIDEO_MIXER_PICTURE_STRUCTURE_TOP_FIELD)
2691       m_mixerfield = VDP_VIDEO_MIXER_PICTURE_STRUCTURE_BOTTOM_FIELD;
2692     else
2693       m_mixerfield = VDP_VIDEO_MIXER_PICTURE_STRUCTURE_TOP_FIELD;
2694   }
2695 
2696   VdpVideoSurface past_surfaces[4] = { VDP_INVALID_HANDLE, VDP_INVALID_HANDLE, VDP_INVALID_HANDLE, VDP_INVALID_HANDLE };
2697   VdpVideoSurface futu_surfaces[2] = { VDP_INVALID_HANDLE, VDP_INVALID_HANDLE };
2698   uint32_t pastCount = 4;
2699   uint32_t futuCount = 2;
2700 
2701   if(m_mixerfield == VDP_VIDEO_MIXER_PICTURE_STRUCTURE_FRAME)
2702   {
2703     // use only 2 past 1 future for progressive/weave
2704     // (only used for postproc anyway eg noise reduction)
2705     if (m_mixerInput.size() > 3)
2706       past_surfaces[1] = m_mixerInput[3].videoSurface;
2707     if (m_mixerInput.size() > 2)
2708       past_surfaces[0] = m_mixerInput[2].videoSurface;
2709     if (m_mixerInput.size() > 1)
2710       futu_surfaces[0] = m_mixerInput[0].videoSurface;
2711     pastCount = 2;
2712     futuCount = 1;
2713   }
2714   else
2715   {
2716     if(m_mixerstep == 0)
2717     { // first field
2718       if (m_mixerInput.size() > 3)
2719       {
2720         past_surfaces[3] = m_mixerInput[3].videoSurface;
2721         past_surfaces[2] = m_mixerInput[3].videoSurface;
2722       }
2723       if (m_mixerInput.size() > 2)
2724       {
2725         past_surfaces[1] = m_mixerInput[2].videoSurface;
2726         past_surfaces[0] = m_mixerInput[2].videoSurface;
2727       }
2728       futu_surfaces[0] = m_mixerInput[1].videoSurface;
2729       futu_surfaces[1] = m_mixerInput[0].videoSurface;
2730     }
2731     else
2732     { // second field
2733       if (m_mixerInput.size() > 3)
2734       {
2735         past_surfaces[3] = m_mixerInput[3].videoSurface;
2736       }
2737       if (m_mixerInput.size() > 2)
2738       {
2739         past_surfaces[2] = m_mixerInput[2].videoSurface;
2740         past_surfaces[1] = m_mixerInput[2].videoSurface;
2741       }
2742       past_surfaces[0] = m_mixerInput[1].videoSurface;
2743       futu_surfaces[0] = m_mixerInput[0].videoSurface;
2744       futu_surfaces[1] = m_mixerInput[0].videoSurface;
2745 
2746       if (m_mixerInput[0].DVDPic.pts != DVD_NOPTS_VALUE &&
2747           m_mixerInput[1].DVDPic.pts != DVD_NOPTS_VALUE)
2748       {
2749         m_processPicture.DVDPic.pts = m_mixerInput[1].DVDPic.pts +
2750                                      (m_mixerInput[0].DVDPic.pts -
2751                                       m_mixerInput[1].DVDPic.pts) / 2;
2752       }
2753       else
2754         m_processPicture.DVDPic.pts = DVD_NOPTS_VALUE;
2755       m_processPicture.DVDPic.dts = DVD_NOPTS_VALUE;
2756     }
2757     m_processPicture.DVDPic.iRepeatPicture = 0.0;
2758   } // interlaced
2759 
2760   VdpRect sourceRect;
2761   sourceRect.x0 = 0;
2762   sourceRect.y0 = 0;
2763   sourceRect.x1 = m_config.vidWidth;
2764   sourceRect.y1 = m_config.vidHeight;
2765 
2766   VdpRect destRect;
2767   destRect.x0 = 0;
2768   destRect.y0 = 0;
2769   destRect.x1 = m_config.outWidth;
2770   destRect.y1 = m_config.outHeight;
2771 
2772   // start vdpau video mixer
2773   vdp_st = m_config.context->GetProcs().vdp_video_mixer_render(m_videoMixer,
2774                                 VDP_INVALID_HANDLE,
2775                                 0,
2776                                 m_mixerfield,
2777                                 pastCount,
2778                                 past_surfaces,
2779                                 m_mixerInput[1].videoSurface,
2780                                 futuCount,
2781                                 futu_surfaces,
2782                                 &sourceRect,
2783                                 m_processPicture.outputSurface,
2784                                 &destRect,
2785                                 &destRect,
2786                                 0,
2787                                 NULL);
2788   CheckStatus(vdp_st, __LINE__);
2789 }
2790 
2791 
CheckStatus(VdpStatus vdp_st,int line)2792 bool CMixer::CheckStatus(VdpStatus vdp_st, int line)
2793 {
2794   if (vdp_st != VDP_STATUS_OK)
2795   {
2796     CLog::Log(LOGERROR, " (VDPAU) Error: %s(%d) at %s:%d",
2797               m_config.context->GetProcs().vdp_get_error_string(vdp_st), vdp_st, __FILE__, line);
2798     m_vdpError = true;
2799     return true;
2800   }
2801   return false;
2802 }
2803 
2804 //-----------------------------------------------------------------------------
2805 // Output
2806 //-----------------------------------------------------------------------------
COutput(CDecoder & decoder,CEvent * inMsgEvent)2807 COutput::COutput(CDecoder &decoder, CEvent *inMsgEvent) :
2808   CThread("Vdpau Output"),
2809   m_controlPort("OutputControlPort", inMsgEvent, &m_outMsgEvent),
2810   m_dataPort("OutputDataPort", inMsgEvent, &m_outMsgEvent),
2811   m_vdpau(decoder),
2812   m_mixer(&m_outMsgEvent)
2813 {
2814   m_inMsgEvent = inMsgEvent;
2815   m_bufferPool = std::make_shared<CVdpauBufferPool>(decoder);
2816 }
2817 
Start()2818 void COutput::Start()
2819 {
2820   Create();
2821 }
2822 
~COutput()2823 COutput::~COutput()
2824 {
2825   Dispose();
2826 }
2827 
Dispose()2828 void COutput::Dispose()
2829 {
2830   CSingleLock lock(CServiceBroker::GetWinSystem()->GetGfxContext());
2831   m_bStop = true;
2832   m_outMsgEvent.Set();
2833   StopThread();
2834   m_controlPort.Purge();
2835   m_dataPort.Purge();
2836 }
2837 
OnStartup()2838 void COutput::OnStartup()
2839 {
2840   CLog::Log(LOGINFO, "COutput::OnStartup: Output Thread created");
2841 }
2842 
OnExit()2843 void COutput::OnExit()
2844 {
2845   CLog::Log(LOGINFO, "COutput::OnExit: Output Thread terminated");
2846 }
2847 
2848 enum OUTPUT_STATES
2849 {
2850   O_TOP = 0,                      // 0
2851   O_TOP_ERROR,                    // 1
2852   O_TOP_UNCONFIGURED,             // 2
2853   O_TOP_CONFIGURED,               // 3
2854   O_TOP_CONFIGURED_IDLE,          // 4
2855   O_TOP_CONFIGURED_WORK,          // 5
2856 };
2857 
2858 int VDPAU_OUTPUT_parentStates[] = {
2859     -1,
2860     0, //TOP_ERROR
2861     0, //TOP_UNCONFIGURED
2862     0, //TOP_CONFIGURED
2863     3, //TOP_CONFIGURED_IDLE
2864     3, //TOP_CONFIGURED_WORK
2865 };
2866 
StateMachine(int signal,Protocol * port,Message * msg)2867 void COutput::StateMachine(int signal, Protocol *port, Message *msg)
2868 {
2869   for (int state = m_state; ; state = VDPAU_OUTPUT_parentStates[state])
2870   {
2871     switch (state)
2872     {
2873     case O_TOP: // TOP
2874       if (port == &m_controlPort)
2875       {
2876         switch (signal)
2877         {
2878         case COutputControlProtocol::FLUSH:
2879           msg->Reply(COutputControlProtocol::ACC);
2880           return;
2881         case COutputControlProtocol::PRECLEANUP:
2882           msg->Reply(COutputControlProtocol::ACC);
2883           return;
2884         default:
2885           break;
2886         }
2887       }
2888       else if (port == &m_dataPort)
2889       {
2890         switch (signal)
2891         {
2892         case COutputDataProtocol::RETURNPIC:
2893           CVdpauRenderPicture *pic;
2894           pic = *((CVdpauRenderPicture**)msg->data);
2895           QueueReturnPicture(pic);
2896           return;
2897         default:
2898           break;
2899         }
2900       }
2901       {
2902         std::string portName = port == NULL ? "timer" : port->portName;
2903         CLog::Log(LOGWARNING, "COutput::%s - signal: %d form port: %s not handled for state: %d", __FUNCTION__, signal, portName.c_str(), m_state);
2904       }
2905       return;
2906 
2907     case O_TOP_ERROR:
2908       break;
2909 
2910     case O_TOP_UNCONFIGURED:
2911       if (port == &m_controlPort)
2912       {
2913         switch (signal)
2914         {
2915         case COutputControlProtocol::INIT:
2916           CVdpauConfig *data;
2917           data = (CVdpauConfig*)msg->data;
2918           if (data)
2919           {
2920             m_config = *data;
2921           }
2922           Init();
2923           Message *reply;
2924           if (m_mixer.m_controlPort.SendOutMessageSync(CMixerControlProtocol::INIT,
2925                                      &reply, 1000, &m_config, sizeof(m_config)))
2926           {
2927             if (reply->signal != CMixerControlProtocol::ACC)
2928               m_vdpError = true;
2929             reply->Release();
2930           }
2931 
2932           // set initial number of
2933           m_bufferPool->numOutputSurfaces = 4;
2934           EnsureBufferPool();
2935           if (!m_vdpError)
2936           {
2937             m_state = O_TOP_CONFIGURED_IDLE;
2938             msg->Reply(COutputControlProtocol::ACC, &m_config, sizeof(m_config));
2939           }
2940           else
2941           {
2942             m_state = O_TOP_ERROR;
2943             msg->Reply(COutputControlProtocol::ERROR);
2944           }
2945           return;
2946         default:
2947           break;
2948         }
2949       }
2950       break;
2951 
2952     case O_TOP_CONFIGURED:
2953       if (port == &m_controlPort)
2954       {
2955         switch (signal)
2956         {
2957         case COutputControlProtocol::FLUSH:
2958           Flush();
2959           msg->Reply(COutputControlProtocol::ACC);
2960           return;
2961         case COutputControlProtocol::PRECLEANUP:
2962           Flush();
2963           PreCleanup();
2964           msg->Reply(COutputControlProtocol::ACC);
2965           return;
2966         default:
2967           break;
2968         }
2969       }
2970       else if (port == &m_dataPort)
2971       {
2972         switch (signal)
2973         {
2974         case COutputDataProtocol::NEWFRAME:
2975           CPayloadWrap<CVdpauDecodedPicture> *payload;
2976           payload = dynamic_cast<CPayloadWrap<CVdpauDecodedPicture>*>(msg->payloadObj.release());
2977           if (payload)
2978           {
2979             m_mixer.m_dataPort.SendOutMessage(CMixerDataProtocol::FRAME, payload);
2980           }
2981           return;
2982         case COutputDataProtocol::RETURNPIC:
2983           CVdpauRenderPicture *pic;
2984           pic = *((CVdpauRenderPicture**)msg->data);
2985           QueueReturnPicture(pic);
2986           m_controlPort.SendInMessage(COutputControlProtocol::STATS);
2987           m_state = O_TOP_CONFIGURED_WORK;
2988           m_extTimeout = 0;
2989           return;
2990         default:
2991           break;
2992         }
2993       }
2994       else if (port == &m_mixer.m_dataPort)
2995       {
2996         switch (signal)
2997         {
2998         case CMixerDataProtocol::PICTURE:
2999           CVdpauProcessedPicture *pic;
3000           pic = (CVdpauProcessedPicture*)msg->data;
3001           m_bufferPool->processedPics.push(*pic);
3002           m_state = O_TOP_CONFIGURED_WORK;
3003           m_extTimeout = 0;
3004           return;
3005         default:
3006           break;
3007         }
3008       }
3009       break;
3010 
3011     case O_TOP_CONFIGURED_IDLE:
3012       if (port == NULL) // timeout
3013       {
3014         switch (signal)
3015         {
3016         case COutputControlProtocol::TIMEOUT:
3017           m_extTimeout = 100;
3018           if (HasWork())
3019           {
3020             m_state = O_TOP_CONFIGURED_WORK;
3021             m_extTimeout = 0;
3022           }
3023           return;
3024         default:
3025           break;
3026         }
3027       }
3028       break;
3029 
3030     case O_TOP_CONFIGURED_WORK:
3031       if (port == NULL) // timeout
3032       {
3033         switch (signal)
3034         {
3035         case COutputControlProtocol::TIMEOUT:
3036           if (HasWork())
3037           {
3038             CVdpauRenderPicture *pic;
3039             pic = ProcessMixerPicture();
3040             if (pic)
3041             {
3042               m_config.stats->DecProcessed();
3043               m_config.stats->IncRender();
3044               m_dataPort.SendInMessage(COutputDataProtocol::PICTURE, &pic, sizeof(pic));
3045             }
3046             m_extTimeout = 1;
3047           }
3048           else
3049           {
3050             m_state = O_TOP_CONFIGURED_IDLE;
3051             m_extTimeout = 0;
3052           }
3053           return;
3054         default:
3055           break;
3056         }
3057       }
3058       break;
3059 
3060     default: // we are in no state, should not happen
3061       CLog::Log(LOGERROR, "COutput::%s - no valid state: %d", __FUNCTION__, m_state);
3062       return;
3063     }
3064   } // for
3065 }
3066 
Process()3067 void COutput::Process()
3068 {
3069   Message *msg = NULL;
3070   Protocol *port = NULL;
3071   bool gotMsg;
3072 
3073   m_state = O_TOP_UNCONFIGURED;
3074   m_extTimeout = 1000;
3075   m_bStateMachineSelfTrigger = false;
3076 
3077   while (!m_bStop)
3078   {
3079     gotMsg = false;
3080 
3081     if (m_bStateMachineSelfTrigger)
3082     {
3083       m_bStateMachineSelfTrigger = false;
3084       // self trigger state machine
3085       StateMachine(msg->signal, port, msg);
3086       if (!m_bStateMachineSelfTrigger)
3087       {
3088         msg->Release();
3089         msg = NULL;
3090       }
3091       continue;
3092     }
3093     // check control port
3094     else if (m_controlPort.ReceiveOutMessage(&msg))
3095     {
3096       gotMsg = true;
3097       port = &m_controlPort;
3098     }
3099     // check data port
3100     else if (m_dataPort.ReceiveOutMessage(&msg))
3101     {
3102       gotMsg = true;
3103       port = &m_dataPort;
3104     }
3105     // check mixer data port
3106     else if (m_mixer.m_dataPort.ReceiveInMessage(&msg))
3107     {
3108       gotMsg = true;
3109       port = &m_mixer.m_dataPort;
3110     }
3111     if (gotMsg)
3112     {
3113       StateMachine(msg->signal, port, msg);
3114       if (!m_bStateMachineSelfTrigger)
3115       {
3116         msg->Release();
3117         msg = NULL;
3118       }
3119       continue;
3120     }
3121 
3122     // wait for message
3123     else if (m_outMsgEvent.WaitMSec(m_extTimeout))
3124     {
3125       continue;
3126     }
3127     // time out
3128     else
3129     {
3130       msg = m_controlPort.GetMessage();
3131       msg->signal = COutputControlProtocol::TIMEOUT;
3132       port = 0;
3133       // signal timeout to state machine
3134       StateMachine(msg->signal, port, msg);
3135       if (!m_bStateMachineSelfTrigger)
3136       {
3137         msg->Release();
3138         msg = NULL;
3139       }
3140     }
3141   }
3142   Flush();
3143   Uninit();
3144 }
3145 
Init()3146 bool COutput::Init()
3147 {
3148   m_mixer.Start();
3149   m_vdpError = false;
3150 
3151   return true;
3152 }
3153 
Uninit()3154 bool COutput::Uninit()
3155 {
3156   m_mixer.Dispose();
3157   ProcessSyncPicture();
3158   ReleaseBufferPool();
3159   return true;
3160 }
3161 
Flush()3162 void COutput::Flush()
3163 {
3164   if (m_mixer.IsActive())
3165   {
3166     Message *reply;
3167     if (m_mixer.m_controlPort.SendOutMessageSync(CMixerControlProtocol::FLUSH,
3168                                                  &reply,
3169                                                  2000))
3170     {
3171       reply->Release();
3172     }
3173     else
3174       CLog::Log(LOGERROR, "Coutput::%s - failed to flush mixer", __FUNCTION__);
3175   }
3176 
3177   Message *msg;
3178 
3179   while (m_dataPort.ReceiveInMessage(&msg))
3180   {
3181     if (msg->signal == COutputDataProtocol::PICTURE)
3182     {
3183       CVdpauRenderPicture *pic;
3184       pic = *((CVdpauRenderPicture**)msg->data);
3185       pic->Release();
3186     }
3187     msg->Release();
3188   }
3189 
3190   while (m_dataPort.ReceiveOutMessage(&msg))
3191   {
3192     if (msg->signal == COutputDataProtocol::NEWFRAME)
3193     {
3194       CPayloadWrap<CVdpauDecodedPicture> *payload;
3195       payload = dynamic_cast<CPayloadWrap<CVdpauDecodedPicture>*>(msg->payloadObj.get());
3196       if (payload)
3197       {
3198         CVdpauDecodedPicture pic = *(payload->GetPlayload());
3199         m_config.videoSurfaces->ClearRender(pic.videoSurface);
3200       }
3201     }
3202     else if (msg->signal == COutputDataProtocol::RETURNPIC)
3203     {
3204       CVdpauRenderPicture *pic;
3205       pic = *((CVdpauRenderPicture**)msg->data);
3206       QueueReturnPicture(pic);
3207     }
3208     msg->Release();
3209   }
3210 
3211   while (m_mixer.m_dataPort.ReceiveInMessage(&msg))
3212   {
3213     if (msg->signal == CMixerDataProtocol::PICTURE)
3214     {
3215       CVdpauProcessedPicture pic = *reinterpret_cast<CVdpauProcessedPicture*>(msg->data);
3216       m_bufferPool->processedPics.push(pic);
3217     }
3218     msg->Release();
3219   }
3220 
3221   // reset used render flag which was cleared on mixer flush
3222   for (auto &awayPic : m_bufferPool->processedPicsAway)
3223   {
3224     if (awayPic.isYuv)
3225     {
3226       m_config.videoSurfaces->MarkRender(awayPic.videoSurface);
3227     }
3228   }
3229 
3230   // clear processed pics
3231   while(!m_bufferPool->processedPics.empty())
3232   {
3233     CVdpauProcessedPicture procPic = m_bufferPool->processedPics.front();
3234     if (!procPic.isYuv)
3235     {
3236       m_mixer.m_dataPort.SendOutMessage(CMixerDataProtocol::BUFFER, &procPic.outputSurface, sizeof(procPic.outputSurface));
3237     }
3238     else if (procPic.isYuv)
3239     {
3240       m_config.videoSurfaces->ClearRender(procPic.videoSurface);
3241     }
3242     m_bufferPool->processedPics.pop();
3243   }
3244 }
3245 
HasWork()3246 bool COutput::HasWork()
3247 {
3248   if (!m_bufferPool->processedPics.empty() && m_bufferPool->HasFree())
3249     return true;
3250   return false;
3251 }
3252 
ProcessMixerPicture()3253 CVdpauRenderPicture* COutput::ProcessMixerPicture()
3254 {
3255   CVdpauRenderPicture *retPic = NULL;
3256 
3257   if (!m_bufferPool->processedPics.empty() && m_bufferPool->HasFree())
3258   {
3259     retPic = m_bufferPool->GetVdpau();
3260     CVdpauProcessedPicture procPic = m_bufferPool->processedPics.front();
3261     procPic.id = m_bufferPool->procPicId++;
3262     m_bufferPool->processedPics.pop();
3263     m_bufferPool->processedPicsAway.push_back(procPic);
3264     retPic->procPic = procPic;
3265     retPic->device = reinterpret_cast<void*>(m_config.context->GetDevice());
3266     retPic->procFunc = reinterpret_cast<void*>(m_config.context->GetProcs().vdp_get_proc_address);
3267     retPic->ident = m_config.timeOpened + m_config.resetCounter;
3268 
3269     retPic->DVDPic.SetParams(procPic.DVDPic);
3270     if (!procPic.isYuv)
3271     {
3272       m_config.useInteropYuv = false;
3273       m_bufferPool->numOutputSurfaces = NUM_RENDER_PICS;
3274       EnsureBufferPool();
3275       retPic->width = m_config.outWidth;
3276       retPic->height = m_config.outHeight;
3277       retPic->crop.x1 = 0;
3278       retPic->crop.y1 = procPic.crop ? NUM_CROP_PIX : 0;
3279       retPic->crop.x2 = m_config.outWidth;
3280       retPic->crop.y2 = m_config.outHeight - retPic->crop.y1;
3281     }
3282     else
3283     {
3284       m_config.useInteropYuv = true;
3285       retPic->width = m_config.surfaceWidth;
3286       retPic->height = m_config.surfaceHeight;
3287       retPic->crop.x1 = 0;
3288       retPic->crop.y1 = 0;
3289       retPic->crop.x2 = m_config.surfaceWidth - m_config.vidWidth;
3290       retPic->crop.y2 = m_config.surfaceHeight - m_config.vidHeight;
3291     }
3292   }
3293   return retPic;
3294 }
3295 
QueueReturnPicture(CVdpauRenderPicture * pic)3296 void COutput::QueueReturnPicture(CVdpauRenderPicture *pic)
3297 {
3298   m_bufferPool->QueueReturnPicture(pic);
3299   ProcessSyncPicture();
3300 }
3301 
ProcessSyncPicture()3302 void COutput::ProcessSyncPicture()
3303 {
3304   CVdpauRenderPicture *pic;
3305 
3306   pic = m_bufferPool->ProcessSyncPicture();
3307 
3308   while (pic != nullptr)
3309   {
3310     ProcessReturnPicture(pic);
3311     pic = m_bufferPool->ProcessSyncPicture();
3312   }
3313 }
3314 
ProcessReturnPicture(CVdpauRenderPicture * pic)3315 void COutput::ProcessReturnPicture(CVdpauRenderPicture *pic)
3316 {
3317   for (auto it=m_bufferPool->processedPicsAway.begin(); it!=m_bufferPool->processedPicsAway.end(); ++it)
3318   {
3319     if (it->id == pic->procPic.id)
3320     {
3321       if (pic->procPic.isYuv)
3322       {
3323         VdpVideoSurface surf = pic->procPic.videoSurface;
3324         if (surf != VDP_INVALID_HANDLE)
3325           m_config.videoSurfaces->ClearRender(surf);
3326       }
3327       else
3328       {
3329         VdpOutputSurface outSurf = pic->procPic.outputSurface;
3330         if (outSurf != VDP_INVALID_HANDLE)
3331           m_mixer.m_dataPort.SendOutMessage(CMixerDataProtocol::BUFFER, &outSurf, sizeof(outSurf));
3332       }
3333       m_bufferPool->processedPicsAway.erase(it);
3334       break;
3335     }
3336   }
3337 }
3338 
EnsureBufferPool()3339 bool COutput::EnsureBufferPool()
3340 {
3341   VdpStatus vdp_st;
3342 
3343   // Creation of outputSurfaces
3344   VdpOutputSurface outputSurface;
3345   for (int i = m_bufferPool->outputSurfaces.size(); i < m_bufferPool->numOutputSurfaces; i++)
3346   {
3347     vdp_st = m_config.context->GetProcs().vdp_output_surface_create(m_config.context->GetDevice(),
3348                                       VDP_RGBA_FORMAT_B8G8R8A8,
3349                                       m_config.outWidth,
3350                                       m_config.outHeight,
3351                                       &outputSurface);
3352     if (CheckStatus(vdp_st, __LINE__))
3353       return false;
3354     m_bufferPool->outputSurfaces.push_back(outputSurface);
3355 
3356     m_mixer.m_dataPort.SendOutMessage(CMixerDataProtocol::BUFFER,
3357                                       &outputSurface,
3358                                       sizeof(VdpOutputSurface));
3359     CLog::Log(LOGINFO, "VDPAU::COutput::InitBufferPool - Output Surface created");
3360   }
3361   return true;
3362 }
3363 
ReleaseBufferPool()3364 void COutput::ReleaseBufferPool()
3365 {
3366   VdpStatus vdp_st;
3367 
3368   // release all output surfaces
3369   m_bufferPool->InvalidateUsed();
3370   for (unsigned int i = 0; i < m_bufferPool->outputSurfaces.size(); ++i)
3371   {
3372     if (m_bufferPool->outputSurfaces[i] == VDP_INVALID_HANDLE)
3373       continue;
3374     vdp_st = m_config.context->GetProcs().vdp_output_surface_destroy(m_bufferPool->outputSurfaces[i]);
3375     CheckStatus(vdp_st, __LINE__);
3376   }
3377   m_bufferPool->outputSurfaces.clear();
3378 
3379   ProcessSyncPicture();
3380 }
3381 
PreCleanup()3382 void COutput::PreCleanup()
3383 {
3384 
3385   VdpStatus vdp_st;
3386 
3387   m_mixer.Dispose();
3388   ProcessSyncPicture();
3389 
3390   for (unsigned int i = 0; i < m_bufferPool->outputSurfaces.size(); ++i)
3391   {
3392     if (m_bufferPool->outputSurfaces[i] == VDP_INVALID_HANDLE)
3393       continue;
3394 
3395     // check if output surface is in use
3396     bool used = false;
3397     for (auto &picAway : m_bufferPool->processedPicsAway)
3398     {
3399       if (picAway.outputSurface == m_bufferPool->outputSurfaces[i])
3400       {
3401         used = true;
3402         break;
3403       }
3404     }
3405     if (used)
3406       continue;
3407 
3408     vdp_st = m_config.context->GetProcs().vdp_output_surface_destroy(m_bufferPool->outputSurfaces[i]);
3409     CheckStatus(vdp_st, __LINE__);
3410 
3411     m_bufferPool->outputSurfaces[i] = VDP_INVALID_HANDLE;
3412     CLog::Log(LOGDEBUG, LOGVIDEO, "VDPAU::PreCleanup - released output surface");
3413   }
3414 
3415 }
3416 
InitMixer()3417 void COutput::InitMixer()
3418 {
3419   for (unsigned int i = 0; i < m_bufferPool->outputSurfaces.size(); ++i)
3420   {
3421     m_mixer.m_dataPort.SendOutMessage(CMixerDataProtocol::BUFFER,
3422                                       &m_bufferPool->outputSurfaces[i],
3423                                       sizeof(VdpOutputSurface));
3424   }
3425 }
3426 
CheckStatus(VdpStatus vdp_st,int line)3427 bool COutput::CheckStatus(VdpStatus vdp_st, int line)
3428 {
3429   if (vdp_st != VDP_STATUS_OK)
3430   {
3431     CLog::Log(LOGERROR, " (VDPAU) Error: %s(%d) at %s:%d",
3432               m_config.context->GetProcs().vdp_get_error_string(vdp_st), vdp_st, __FILE__, line);
3433     m_vdpError = true;
3434     return true;
3435   }
3436   return false;
3437 }
3438