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