Merge pull request #9388 from bkuhls/jarvis_pr9231
[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_dataPort)
1462       {
1463         switch (signal)
1464         {
1465         case CMixerDataProtocol::FRAME:
1466           CVdpauDecodedPicture *frame;
1467           frame = (CVdpauDecodedPicture*)msg->data;
1468           if (frame)
1469           {
1470             m_decodedPics.push(*frame);
1471           }
1472           m_extTimeout = 0;
1473           return;
1474         case CMixerDataProtocol::BUFFER:
1475           VdpOutputSurface *surf;
1476           surf = (VdpOutputSurface*)msg->data;
1477           if (surf)
1478           {
1479             m_outputSurfaces.push(*surf);
1480           }
1481           m_extTimeout = 0;
1482           return;
1483         default:
1484           break;
1485         }
1486       }
1487       break;
1488
1489     case M_TOP_CONFIGURED_WAIT1:
1490       if (port == NULL) // timeout
1491       {
1492         switch (signal)
1493         {
1494         case CMixerControlProtocol::TIMEOUT:
1495           if (!m_decodedPics.empty() && !m_outputSurfaces.empty())
1496           {
1497             m_state = M_TOP_CONFIGURED_STEP1;
1498             m_bStateMachineSelfTrigger = true;
1499           }
1500           else
1501           {
1502             m_extTimeout = 100;
1503           }
1504           return;
1505         default:
1506           break;
1507         }
1508       }
1509       break;
1510
1511     case M_TOP_CONFIGURED_STEP1:
1512       if (port == NULL) // timeout
1513       {
1514         switch (signal)
1515         {
1516         case CMixerControlProtocol::TIMEOUT:
1517           m_mixerInput.push_front(m_decodedPics.front());
1518           m_decodedPics.pop();
1519           if (m_mixerInput.size() < 2)
1520           {
1521             m_state = M_TOP_CONFIGURED_WAIT1;
1522             m_extTimeout = 0;
1523             return;
1524           }
1525           InitCycle();
1526           ProcessPicture();
1527           if (m_vdpError)
1528           {
1529             m_state = M_TOP_CONFIGURED_WAIT1;
1530             m_extTimeout = 1000;
1531             return;
1532           }
1533           if (m_processPicture.DVDPic.format != RENDER_FMT_VDPAU_420)
1534             m_outputSurfaces.pop();
1535           m_config.stats->IncProcessed();
1536           m_config.stats->DecDecoded();
1537           m_dataPort.SendInMessage(CMixerDataProtocol::PICTURE,&m_processPicture,sizeof(m_processPicture));
1538           if (m_mixersteps > 1)
1539           {
1540             m_state = M_TOP_CONFIGURED_WAIT2;
1541             m_extTimeout = 0;
1542           }
1543           else
1544           {
1545             FiniCycle();
1546             m_state = M_TOP_CONFIGURED_WAIT1;
1547             m_extTimeout = 0;
1548           }
1549           return;
1550         default:
1551           break;
1552         }
1553       }
1554       break;
1555
1556     case M_TOP_CONFIGURED_WAIT2:
1557       if (port == NULL) // timeout
1558       {
1559         switch (signal)
1560         {
1561         case CMixerControlProtocol::TIMEOUT:
1562           if (!m_outputSurfaces.empty())
1563           {
1564             m_state = M_TOP_CONFIGURED_STEP2;
1565             m_bStateMachineSelfTrigger = true;
1566           }
1567           else
1568           {
1569             m_extTimeout = 100;
1570           }
1571           return;
1572         default:
1573           break;
1574         }
1575       }
1576       break;
1577
1578     case M_TOP_CONFIGURED_STEP2:
1579        if (port == NULL) // timeout
1580        {
1581          switch (signal)
1582          {
1583          case CMixerControlProtocol::TIMEOUT:
1584            m_processPicture.outputSurface = m_outputSurfaces.front();
1585            m_mixerstep = 1;
1586            ProcessPicture();
1587            if (m_vdpError)
1588            {
1589              m_state = M_TOP_CONFIGURED_WAIT1;
1590              m_extTimeout = 1000;
1591              return;
1592            }
1593            if (m_processPicture.DVDPic.format != RENDER_FMT_VDPAU_420)
1594              m_outputSurfaces.pop();
1595            m_config.stats->IncProcessed();
1596            m_dataPort.SendInMessage(CMixerDataProtocol::PICTURE,&m_processPicture,sizeof(m_processPicture));
1597            FiniCycle();
1598            m_state = M_TOP_CONFIGURED_WAIT1;
1599            m_extTimeout = 0;
1600            return;
1601          default:
1602            break;
1603          }
1604        }
1605        break;
1606
1607     default: // we are in no state, should not happen
1608       CLog::Log(LOGERROR, "CMixer::%s - no valid state: %d", __FUNCTION__, m_state);
1609       return;
1610     }
1611   } // for
1612 }
1613
1614 void CMixer::Process()
1615 {
1616   Message *msg = NULL;
1617   Protocol *port = NULL;
1618   bool gotMsg;
1619
1620   m_state = M_TOP_UNCONFIGURED;
1621   m_extTimeout = 1000;
1622   m_bStateMachineSelfTrigger = false;
1623
1624   while (!m_bStop)
1625   {
1626     gotMsg = false;
1627
1628     if (m_bStateMachineSelfTrigger)
1629     {
1630       m_bStateMachineSelfTrigger = false;
1631       // self trigger state machine
1632       StateMachine(msg->signal, port, msg);
1633       if (!m_bStateMachineSelfTrigger)
1634       {
1635         msg->Release();
1636         msg = NULL;
1637       }
1638       continue;
1639     }
1640     // check control port
1641     else if (m_controlPort.ReceiveOutMessage(&msg))
1642     {
1643       gotMsg = true;
1644       port = &m_controlPort;
1645     }
1646     // check data port
1647     else if (m_dataPort.ReceiveOutMessage(&msg))
1648     {
1649       gotMsg = true;
1650       port = &m_dataPort;
1651     }
1652
1653     if (gotMsg)
1654     {
1655       StateMachine(msg->signal, port, msg);
1656       if (!m_bStateMachineSelfTrigger)
1657       {
1658         msg->Release();
1659         msg = NULL;
1660       }
1661       continue;
1662     }
1663
1664     // wait for message
1665     else if (m_outMsgEvent.WaitMSec(m_extTimeout))
1666     {
1667       continue;
1668     }
1669     // time out
1670     else
1671     {
1672       msg = m_controlPort.GetMessage();
1673       msg->signal = CMixerControlProtocol::TIMEOUT;
1674       port = 0;
1675       // signal timeout to state machine
1676       StateMachine(msg->signal, port, msg);
1677       if (!m_bStateMachineSelfTrigger)
1678       {
1679         msg->Release();
1680         msg = NULL;
1681       }
1682     }
1683   }
1684   Uninit();
1685 }
1686
1687 void CMixer::CreateVdpauMixer()
1688 {
1689   CLog::Log(LOGNOTICE, " (VDPAU) Creating the video mixer");
1690
1691   InitCSCMatrix(m_config.vidWidth);
1692
1693   VdpVideoMixerParameter parameters[] = {
1694     VDP_VIDEO_MIXER_PARAMETER_VIDEO_SURFACE_WIDTH,
1695     VDP_VIDEO_MIXER_PARAMETER_VIDEO_SURFACE_HEIGHT,
1696     VDP_VIDEO_MIXER_PARAMETER_CHROMA_TYPE};
1697
1698   void const * parameter_values[] = {
1699     &m_config.surfaceWidth,
1700     &m_config.surfaceHeight,
1701     &m_config.vdpChromaType};
1702
1703   VdpStatus vdp_st = m_config.context->GetProcs().vdp_video_mixer_create(m_config.context->GetDevice(),
1704                                 m_config.context->GetFeatureCount(),
1705                                 m_config.context->GetFeatures(),
1706                                 ARSIZE(parameters),
1707                                 parameters,
1708                                 parameter_values,
1709                                 &m_videoMixer);
1710   CheckStatus(vdp_st, __LINE__);
1711
1712 }
1713
1714 void CMixer::InitCSCMatrix(int Width)
1715 {
1716   m_Procamp.struct_version = VDP_PROCAMP_VERSION;
1717   m_Procamp.brightness     = 0.0;
1718   m_Procamp.contrast       = 1.0;
1719   m_Procamp.saturation     = 1.0;
1720   m_Procamp.hue            = 0;
1721 }
1722
1723 void CMixer::CheckFeatures()
1724 {
1725   if (m_Upscale != m_config.upscale)
1726   {
1727     SetHWUpscaling();
1728     m_Upscale = m_config.upscale;
1729   }
1730   if (m_Brightness != CMediaSettings::GetInstance().GetCurrentVideoSettings().m_Brightness ||
1731       m_Contrast   != CMediaSettings::GetInstance().GetCurrentVideoSettings().m_Contrast ||
1732       m_ColorMatrix != m_mixerInput[1].DVDPic.color_matrix)
1733   {
1734     SetColor();
1735     m_Brightness = CMediaSettings::GetInstance().GetCurrentVideoSettings().m_Brightness;
1736     m_Contrast = CMediaSettings::GetInstance().GetCurrentVideoSettings().m_Contrast;
1737     m_ColorMatrix = m_mixerInput[1].DVDPic.color_matrix;
1738   }
1739   if (m_NoiseReduction != CMediaSettings::GetInstance().GetCurrentVideoSettings().m_NoiseReduction)
1740   {
1741     m_NoiseReduction = CMediaSettings::GetInstance().GetCurrentVideoSettings().m_NoiseReduction;
1742     SetNoiseReduction();
1743   }
1744   if (m_Sharpness != CMediaSettings::GetInstance().GetCurrentVideoSettings().m_Sharpness)
1745   {
1746     m_Sharpness = CMediaSettings::GetInstance().GetCurrentVideoSettings().m_Sharpness;
1747     SetSharpness();
1748   }
1749   if (m_DeintMode != CMediaSettings::GetInstance().GetCurrentVideoSettings().m_DeinterlaceMode ||
1750       m_Deint     != CMediaSettings::GetInstance().GetCurrentVideoSettings().m_InterlaceMethod)
1751   {
1752     m_DeintMode = CMediaSettings::GetInstance().GetCurrentVideoSettings().m_DeinterlaceMode;
1753     m_Deint     = CMediaSettings::GetInstance().GetCurrentVideoSettings().m_InterlaceMethod;
1754     SetDeinterlacing();
1755   }
1756 }
1757
1758 void CMixer::SetPostProcFeatures(bool postProcEnabled)
1759 {
1760   if (m_PostProc != postProcEnabled)
1761   {
1762     if (postProcEnabled)
1763     {
1764       SetNoiseReduction();
1765       SetSharpness();
1766       SetDeinterlacing();
1767       SetHWUpscaling();
1768     }
1769     else
1770       PostProcOff();
1771     m_PostProc = postProcEnabled;
1772   }
1773 }
1774
1775 void CMixer::PostProcOff()
1776 {
1777   VdpStatus vdp_st;
1778
1779   if (m_videoMixer == VDP_INVALID_HANDLE)
1780     return;
1781
1782   VdpVideoMixerFeature feature[] = { VDP_VIDEO_MIXER_FEATURE_DEINTERLACE_TEMPORAL,
1783                                      VDP_VIDEO_MIXER_FEATURE_DEINTERLACE_TEMPORAL_SPATIAL,
1784                                      VDP_VIDEO_MIXER_FEATURE_INVERSE_TELECINE};
1785
1786   VdpBool enabled[]={0,0,0};
1787   vdp_st = m_config.context->GetProcs().vdp_video_mixer_set_feature_enables(m_videoMixer, ARSIZE(feature), feature, enabled);
1788   CheckStatus(vdp_st, __LINE__);
1789
1790   if(m_config.vdpau->Supports(VDP_VIDEO_MIXER_FEATURE_NOISE_REDUCTION))
1791   {
1792     VdpVideoMixerFeature feature[] = { VDP_VIDEO_MIXER_FEATURE_NOISE_REDUCTION};
1793
1794     VdpBool enabled[]={0};
1795     vdp_st = m_config.context->GetProcs().vdp_video_mixer_set_feature_enables(m_videoMixer, ARSIZE(feature), feature, enabled);
1796     CheckStatus(vdp_st, __LINE__);
1797   }
1798
1799   if(m_config.vdpau->Supports(VDP_VIDEO_MIXER_FEATURE_SHARPNESS))
1800   {
1801     VdpVideoMixerFeature feature[] = { VDP_VIDEO_MIXER_FEATURE_SHARPNESS};
1802
1803     VdpBool enabled[]={0};
1804     vdp_st = m_config.context->GetProcs().vdp_video_mixer_set_feature_enables(m_videoMixer, ARSIZE(feature), feature, enabled);
1805     CheckStatus(vdp_st, __LINE__);
1806   }
1807
1808   DisableHQScaling();
1809 }
1810
1811 bool CMixer::GenerateStudioCSCMatrix(VdpColorStandard colorStandard, VdpCSCMatrix &studioCSCMatrix)
1812 {
1813    // instead use studioCSCKCoeffs601[3], studioCSCKCoeffs709[3] to generate float[3][4] matrix (float studioCSC[3][4])
1814    // m00 = mRY = red: luma factor (contrast factor) (1.0)
1815    // m10 = mGY = green: luma factor (contrast factor) (1.0)
1816    // m20 = mBY = blue: luma factor (contrast factor) (1.0)
1817    //
1818    // m01 = mRB = red: blue color diff coeff (0.0)
1819    // m11 = mGB = green: blue color diff coeff (-2Kb(1-Kb)/(Kg))
1820    // m21 = mBB = blue: blue color diff coeff ((1-Kb)/0.5)
1821    //
1822    // m02 = mRR = red: red color diff coeff ((1-Kr)/0.5)
1823    // m12 = mGR = green: red color diff coeff (-2Kr(1-Kr)/(Kg))
1824    // m22 = mBR = blue: red color diff coeff (0.0)
1825    //
1826    // m03 = mRC = red: colour zero offset (brightness factor) (-(1-Kr)/0.5 * (128/255))
1827    // m13 = mGC = green: colour zero offset (brightness factor) ((256/255) * (Kb(1-Kb) + Kr(1-Kr)) / Kg)
1828    // m23 = mBC = blue: colour zero offset (brightness factor) (-(1-Kb)/0.5 * (128/255))
1829
1830    // columns
1831    int Y = 0;
1832    int Cb = 1;
1833    int Cr = 2;
1834    int C = 3;
1835    // rows
1836    int R = 0;
1837    int G = 1;
1838    int B = 2;
1839    // colour standard coefficients for red, geen, blue
1840    double Kr, Kg, Kb;
1841    // colour diff zero position (use standard 8-bit coding precision)
1842    double CDZ = 128; //256*0.5
1843    // range excursion (use standard 8-bit coding precision)
1844    double EXC = 255; //256-1
1845
1846    if (colorStandard == VDP_COLOR_STANDARD_ITUR_BT_601)
1847    {
1848       Kr = studioCSCKCoeffs601[0];
1849       Kg = studioCSCKCoeffs601[1];
1850       Kb = studioCSCKCoeffs601[2];
1851    }
1852    else // assume VDP_COLOR_STANDARD_ITUR_BT_709
1853    {
1854       Kr = studioCSCKCoeffs709[0];
1855       Kg = studioCSCKCoeffs709[1];
1856       Kb = studioCSCKCoeffs709[2];
1857    }
1858    // we keep luma unscaled to retain the levels present in source so that 16-235 luma is converted to RGB 16-235
1859    studioCSCMatrix[R][Y] = 1.0;
1860    studioCSCMatrix[G][Y] = 1.0;
1861    studioCSCMatrix[B][Y] = 1.0;
1862
1863    studioCSCMatrix[R][Cb] = 0.0;
1864    studioCSCMatrix[G][Cb] = (double)-2 * Kb * (1 - Kb) / Kg;
1865    studioCSCMatrix[B][Cb] = (double)(1 - Kb) / 0.5;
1866
1867    studioCSCMatrix[R][Cr] = (double)(1 - Kr) / 0.5;
1868    studioCSCMatrix[G][Cr] = (double)-2 * Kr * (1 - Kr) / Kg;
1869    studioCSCMatrix[B][Cr] = 0.0;
1870
1871    studioCSCMatrix[R][C] = (double)-1 * studioCSCMatrix[R][Cr] * CDZ/EXC;
1872    studioCSCMatrix[G][C] = (double)-1 * (studioCSCMatrix[G][Cb] + studioCSCMatrix[G][Cr]) * CDZ/EXC;
1873    studioCSCMatrix[B][C] = (double)-1 * studioCSCMatrix[B][Cb] * CDZ/EXC;
1874
1875    return true;
1876 }
1877
1878 void CMixer::SetColor()
1879 {
1880   VdpStatus vdp_st;
1881
1882   if (m_Brightness != CMediaSettings::GetInstance().GetCurrentVideoSettings().m_Brightness)
1883     m_Procamp.brightness = (float)((CMediaSettings::GetInstance().GetCurrentVideoSettings().m_Brightness)-50) / 100;
1884   if (m_Contrast != CMediaSettings::GetInstance().GetCurrentVideoSettings().m_Contrast)
1885     m_Procamp.contrast = (float)((CMediaSettings::GetInstance().GetCurrentVideoSettings().m_Contrast)+50) / 100;
1886
1887   VdpColorStandard colorStandard;
1888   switch(m_mixerInput[1].DVDPic.color_matrix)
1889   {
1890     case AVCOL_SPC_BT709:
1891       colorStandard = VDP_COLOR_STANDARD_ITUR_BT_709;
1892       break;
1893     case AVCOL_SPC_BT470BG:
1894     case AVCOL_SPC_SMPTE170M:
1895       colorStandard = VDP_COLOR_STANDARD_ITUR_BT_601;
1896       break;
1897     case AVCOL_SPC_SMPTE240M:
1898       colorStandard = VDP_COLOR_STANDARD_SMPTE_240M;
1899       break;
1900     case AVCOL_SPC_FCC:
1901     case AVCOL_SPC_UNSPECIFIED:
1902     case AVCOL_SPC_RGB:
1903     default:
1904       if(m_config.surfaceWidth > 1000)
1905         colorStandard = VDP_COLOR_STANDARD_ITUR_BT_709;
1906       else
1907         colorStandard = VDP_COLOR_STANDARD_ITUR_BT_601;
1908   }
1909
1910   VdpVideoMixerAttribute attributes[] = { VDP_VIDEO_MIXER_ATTRIBUTE_CSC_MATRIX };
1911   if (CSettings::GetInstance().GetBool(CSettings::SETTING_VIDEOSCREEN_LIMITEDRANGE))
1912   {
1913     float studioCSC[3][4];
1914     GenerateStudioCSCMatrix(colorStandard, studioCSC);
1915     void const * pm_CSCMatix[] = { &studioCSC };
1916     vdp_st = m_config.context->GetProcs().vdp_video_mixer_set_attribute_values(m_videoMixer, ARSIZE(attributes), attributes, pm_CSCMatix);
1917   }
1918   else
1919   {
1920     vdp_st = m_config.context->GetProcs().vdp_generate_csc_matrix(&m_Procamp, colorStandard, &m_CSCMatrix);
1921     if(vdp_st != VDP_STATUS_ERROR)
1922     {
1923       void const * pm_CSCMatix[] = { &m_CSCMatrix };
1924       vdp_st = m_config.context->GetProcs().vdp_video_mixer_set_attribute_values(m_videoMixer, ARSIZE(attributes), attributes, pm_CSCMatix);
1925     }
1926   }
1927
1928   CheckStatus(vdp_st, __LINE__);
1929 }
1930
1931 void CMixer::SetNoiseReduction()
1932 {
1933   if(!m_config.vdpau->Supports(VDP_VIDEO_MIXER_FEATURE_NOISE_REDUCTION))
1934     return;
1935
1936   VdpVideoMixerFeature feature[] = { VDP_VIDEO_MIXER_FEATURE_NOISE_REDUCTION };
1937   VdpVideoMixerAttribute attributes[] = { VDP_VIDEO_MIXER_ATTRIBUTE_NOISE_REDUCTION_LEVEL };
1938   VdpStatus vdp_st;
1939
1940   if (!CMediaSettings::GetInstance().GetCurrentVideoSettings().m_NoiseReduction)
1941   {
1942     VdpBool enabled[]= {0};
1943     vdp_st = m_config.context->GetProcs().vdp_video_mixer_set_feature_enables(m_videoMixer, ARSIZE(feature), feature, enabled);
1944     CheckStatus(vdp_st, __LINE__);
1945     return;
1946   }
1947   VdpBool enabled[]={1};
1948   vdp_st = m_config.context->GetProcs().vdp_video_mixer_set_feature_enables(m_videoMixer, ARSIZE(feature), feature, enabled);
1949   CheckStatus(vdp_st, __LINE__);
1950   void* nr[] = { &CMediaSettings::GetInstance().GetCurrentVideoSettings().m_NoiseReduction };
1951   CLog::Log(LOGNOTICE,"Setting Noise Reduction to %f",CMediaSettings::GetInstance().GetCurrentVideoSettings().m_NoiseReduction);
1952   vdp_st = m_config.context->GetProcs().vdp_video_mixer_set_attribute_values(m_videoMixer, ARSIZE(attributes), attributes, nr);
1953   CheckStatus(vdp_st, __LINE__);
1954 }
1955
1956 void CMixer::SetSharpness()
1957 {
1958   if(!m_config.vdpau->Supports(VDP_VIDEO_MIXER_FEATURE_SHARPNESS))
1959     return;
1960
1961   VdpVideoMixerFeature feature[] = { VDP_VIDEO_MIXER_FEATURE_SHARPNESS };
1962   VdpVideoMixerAttribute attributes[] = { VDP_VIDEO_MIXER_ATTRIBUTE_SHARPNESS_LEVEL };
1963   VdpStatus vdp_st;
1964
1965   if (!CMediaSettings::GetInstance().GetCurrentVideoSettings().m_Sharpness)
1966   {
1967     VdpBool enabled[]={0};
1968     vdp_st = m_config.context->GetProcs().vdp_video_mixer_set_feature_enables(m_videoMixer, ARSIZE(feature), feature, enabled);
1969     CheckStatus(vdp_st, __LINE__);
1970     return;
1971   }
1972   VdpBool enabled[]={1};
1973   vdp_st = m_config.context->GetProcs().vdp_video_mixer_set_feature_enables(m_videoMixer, ARSIZE(feature), feature, enabled);
1974   CheckStatus(vdp_st, __LINE__);
1975   void* sh[] = { &CMediaSettings::GetInstance().GetCurrentVideoSettings().m_Sharpness };
1976   CLog::Log(LOGNOTICE,"Setting Sharpness to %f",CMediaSettings::GetInstance().GetCurrentVideoSettings().m_Sharpness);
1977   vdp_st = m_config.context->GetProcs().vdp_video_mixer_set_attribute_values(m_videoMixer, ARSIZE(attributes), attributes, sh);
1978   CheckStatus(vdp_st, __LINE__);
1979 }
1980
1981 EINTERLACEMETHOD CMixer::GetDeinterlacingMethod(bool log /* = false */)
1982 {
1983   EINTERLACEMETHOD method = CMediaSettings::GetInstance().GetCurrentVideoSettings().m_InterlaceMethod;
1984   if (method == VS_INTERLACEMETHOD_AUTO)
1985   {
1986     int deint = -1;
1987 //    if (m_config.outHeight >= 720)
1988 //      deint = g_advancedSettings.m_videoVDPAUdeintHD;
1989 //    else
1990 //      deint = g_advancedSettings.m_videoVDPAUdeintSD;
1991
1992     if (deint != -1)
1993     {
1994       if (m_config.vdpau->Supports(EINTERLACEMETHOD(deint)))
1995       {
1996         method = EINTERLACEMETHOD(deint);
1997         if (log)
1998           CLog::Log(LOGNOTICE, "CVDPAU::GetDeinterlacingMethod: set de-interlacing to %d",  deint);
1999       }
2000       else
2001       {
2002         if (log)
2003           CLog::Log(LOGWARNING, "CVDPAU::GetDeinterlacingMethod: method for de-interlacing (advanced settings) not supported");
2004       }
2005     }
2006   }
2007   return method;
2008 }
2009
2010 void CMixer::SetDeinterlacing()
2011 {
2012   VdpStatus vdp_st;
2013
2014   if (m_videoMixer == VDP_INVALID_HANDLE)
2015     return;
2016
2017   EDEINTERLACEMODE   mode = CMediaSettings::GetInstance().GetCurrentVideoSettings().m_DeinterlaceMode;
2018   EINTERLACEMETHOD method = GetDeinterlacingMethod(true);
2019
2020   VdpVideoMixerFeature feature[] = { VDP_VIDEO_MIXER_FEATURE_DEINTERLACE_TEMPORAL,
2021                                      VDP_VIDEO_MIXER_FEATURE_DEINTERLACE_TEMPORAL_SPATIAL,
2022                                      VDP_VIDEO_MIXER_FEATURE_INVERSE_TELECINE };
2023
2024   if (mode == VS_DEINTERLACEMODE_OFF)
2025   {
2026     VdpBool enabled[] = {0,0,0};
2027     vdp_st = m_config.context->GetProcs().vdp_video_mixer_set_feature_enables(m_videoMixer, ARSIZE(feature), feature, enabled);
2028   }
2029   else
2030   {
2031     if (method == VS_INTERLACEMETHOD_AUTO)
2032     {
2033       VdpBool enabled[] = {1,0,0};
2034       if (g_advancedSettings.m_videoVDPAUtelecine)
2035         enabled[2] = 1;
2036       vdp_st = m_config.context->GetProcs().vdp_video_mixer_set_feature_enables(m_videoMixer, ARSIZE(feature), feature, enabled);
2037     }
2038     else if (method == VS_INTERLACEMETHOD_VDPAU_TEMPORAL
2039          ||  method == VS_INTERLACEMETHOD_VDPAU_TEMPORAL_HALF)
2040     {
2041       VdpBool enabled[] = {1,0,0};
2042       if (g_advancedSettings.m_videoVDPAUtelecine)
2043         enabled[2] = 1;
2044       vdp_st = m_config.context->GetProcs().vdp_video_mixer_set_feature_enables(m_videoMixer, ARSIZE(feature), feature, enabled);
2045     }
2046     else if (method == VS_INTERLACEMETHOD_VDPAU_TEMPORAL_SPATIAL
2047          ||  method == VS_INTERLACEMETHOD_VDPAU_TEMPORAL_SPATIAL_HALF)
2048     {
2049       VdpBool enabled[] = {1,1,0};
2050       if (g_advancedSettings.m_videoVDPAUtelecine)
2051         enabled[2] = 1;
2052       vdp_st = m_config.context->GetProcs().vdp_video_mixer_set_feature_enables(m_videoMixer, ARSIZE(feature), feature, enabled);
2053     }
2054     else
2055     {
2056       VdpBool enabled[]={0,0,0};
2057       vdp_st = m_config.context->GetProcs().vdp_video_mixer_set_feature_enables(m_videoMixer, ARSIZE(feature), feature, enabled);
2058     }
2059   }
2060   CheckStatus(vdp_st, __LINE__);
2061
2062   SetDeintSkipChroma();
2063
2064   m_config.useInteropYuv = !CSettings::GetInstance().GetBool(CSettings::SETTING_VIDEOPLAYER_USEVDPAUMIXER);
2065 }
2066
2067 void CMixer::SetDeintSkipChroma()
2068 {
2069   VdpVideoMixerAttribute attribute[] = { VDP_VIDEO_MIXER_ATTRIBUTE_SKIP_CHROMA_DEINTERLACE};
2070   VdpStatus vdp_st;
2071
2072   uint8_t val;
2073   if (g_advancedSettings.m_videoVDPAUdeintSkipChromaHD && m_config.outHeight >= 720)
2074     val = 1;
2075   else
2076     val = 0;
2077
2078   void const *values[]={&val};
2079   vdp_st = m_config.context->GetProcs().vdp_video_mixer_set_attribute_values(m_videoMixer, ARSIZE(attribute), attribute, values);
2080
2081   CheckStatus(vdp_st, __LINE__);
2082 }
2083
2084 void CMixer::SetHWUpscaling()
2085 {
2086 #ifdef VDP_VIDEO_MIXER_FEATURE_HIGH_QUALITY_SCALING_L1
2087
2088   VdpStatus vdp_st;
2089   VdpBool enabled[]={1};
2090   switch (m_config.upscale)
2091   {
2092     case 9:
2093        if (m_config.vdpau->Supports(VDP_VIDEO_MIXER_FEATURE_HIGH_QUALITY_SCALING_L9))
2094        {
2095           VdpVideoMixerFeature feature[] = { VDP_VIDEO_MIXER_FEATURE_HIGH_QUALITY_SCALING_L9 };
2096           vdp_st = m_config.context->GetProcs().vdp_video_mixer_set_feature_enables(m_videoMixer, ARSIZE(feature), feature, enabled);
2097           break;
2098        }
2099     case 8:
2100        if (m_config.vdpau->Supports(VDP_VIDEO_MIXER_FEATURE_HIGH_QUALITY_SCALING_L8))
2101        {
2102           VdpVideoMixerFeature feature[] = { VDP_VIDEO_MIXER_FEATURE_HIGH_QUALITY_SCALING_L8 };
2103           vdp_st = m_config.context->GetProcs().vdp_video_mixer_set_feature_enables(m_videoMixer, ARSIZE(feature), feature, enabled);
2104           break;
2105        }
2106     case 7:
2107        if (m_config.vdpau->Supports(VDP_VIDEO_MIXER_FEATURE_HIGH_QUALITY_SCALING_L7))
2108        {
2109           VdpVideoMixerFeature feature[] = { VDP_VIDEO_MIXER_FEATURE_HIGH_QUALITY_SCALING_L7 };
2110           vdp_st = m_config.context->GetProcs().vdp_video_mixer_set_feature_enables(m_videoMixer, ARSIZE(feature), feature, enabled);
2111           break;
2112        }
2113     case 6:
2114        if (m_config.vdpau->Supports(VDP_VIDEO_MIXER_FEATURE_HIGH_QUALITY_SCALING_L6))
2115        {
2116           VdpVideoMixerFeature feature[] = { VDP_VIDEO_MIXER_FEATURE_HIGH_QUALITY_SCALING_L6 };
2117           vdp_st = m_config.context->GetProcs().vdp_video_mixer_set_feature_enables(m_videoMixer, ARSIZE(feature), feature, enabled);
2118           break;
2119        }
2120     case 5:
2121        if (m_config.vdpau->Supports(VDP_VIDEO_MIXER_FEATURE_HIGH_QUALITY_SCALING_L5))
2122        {
2123           VdpVideoMixerFeature feature[] = { VDP_VIDEO_MIXER_FEATURE_HIGH_QUALITY_SCALING_L5 };
2124           vdp_st = m_config.context->GetProcs().vdp_video_mixer_set_feature_enables(m_videoMixer, ARSIZE(feature), feature, enabled);
2125           break;
2126        }
2127     case 4:
2128        if (m_config.vdpau->Supports(VDP_VIDEO_MIXER_FEATURE_HIGH_QUALITY_SCALING_L4))
2129        {
2130           VdpVideoMixerFeature feature[] = { VDP_VIDEO_MIXER_FEATURE_HIGH_QUALITY_SCALING_L4 };
2131           vdp_st = m_config.context->GetProcs().vdp_video_mixer_set_feature_enables(m_videoMixer, ARSIZE(feature), feature, enabled);
2132           break;
2133        }
2134     case 3:
2135        if (m_config.vdpau->Supports(VDP_VIDEO_MIXER_FEATURE_HIGH_QUALITY_SCALING_L3))
2136        {
2137           VdpVideoMixerFeature feature[] = { VDP_VIDEO_MIXER_FEATURE_HIGH_QUALITY_SCALING_L3 };
2138           vdp_st = m_config.context->GetProcs().vdp_video_mixer_set_feature_enables(m_videoMixer, ARSIZE(feature), feature, enabled);
2139           break;
2140        }
2141     case 2:
2142        if (m_config.vdpau->Supports(VDP_VIDEO_MIXER_FEATURE_HIGH_QUALITY_SCALING_L2))
2143        {
2144           VdpVideoMixerFeature feature[] = { VDP_VIDEO_MIXER_FEATURE_HIGH_QUALITY_SCALING_L2 };
2145           vdp_st = m_config.context->GetProcs().vdp_video_mixer_set_feature_enables(m_videoMixer, ARSIZE(feature), feature, enabled);
2146           break;
2147        }
2148     case 1:
2149        if (m_config.vdpau->Supports(VDP_VIDEO_MIXER_FEATURE_HIGH_QUALITY_SCALING_L1))
2150        {
2151           VdpVideoMixerFeature feature[] = { VDP_VIDEO_MIXER_FEATURE_HIGH_QUALITY_SCALING_L1 };
2152           vdp_st = m_config.context->GetProcs().vdp_video_mixer_set_feature_enables(m_videoMixer, ARSIZE(feature), feature, enabled);
2153           break;
2154        }
2155     default:
2156        DisableHQScaling();
2157        return;
2158   }
2159   CheckStatus(vdp_st, __LINE__);
2160 #endif
2161 }
2162
2163 void CMixer::DisableHQScaling()
2164 {
2165   VdpStatus vdp_st;
2166
2167   if (m_videoMixer == VDP_INVALID_HANDLE)
2168     return;
2169
2170   if(m_config.vdpau->Supports(VDP_VIDEO_MIXER_FEATURE_HIGH_QUALITY_SCALING_L1))
2171   {
2172     VdpVideoMixerFeature feature[] = { VDP_VIDEO_MIXER_FEATURE_HIGH_QUALITY_SCALING_L1 };
2173     VdpBool enabled[]={0};
2174     vdp_st = m_config.context->GetProcs().vdp_video_mixer_set_feature_enables(m_videoMixer, ARSIZE(feature), feature, enabled);
2175     CheckStatus(vdp_st, __LINE__);
2176   }
2177
2178   if(m_config.vdpau->Supports(VDP_VIDEO_MIXER_FEATURE_HIGH_QUALITY_SCALING_L2))
2179   {
2180     VdpVideoMixerFeature feature[] = { VDP_VIDEO_MIXER_FEATURE_HIGH_QUALITY_SCALING_L2 };
2181     VdpBool enabled[]={0};
2182     vdp_st = m_config.context->GetProcs().vdp_video_mixer_set_feature_enables(m_videoMixer, ARSIZE(feature), feature, enabled);
2183     CheckStatus(vdp_st, __LINE__);
2184   }
2185
2186   if(m_config.vdpau->Supports(VDP_VIDEO_MIXER_FEATURE_HIGH_QUALITY_SCALING_L3))
2187   {
2188     VdpVideoMixerFeature feature[] = { VDP_VIDEO_MIXER_FEATURE_HIGH_QUALITY_SCALING_L3 };
2189     VdpBool enabled[]={0};
2190     vdp_st = m_config.context->GetProcs().vdp_video_mixer_set_feature_enables(m_videoMixer, ARSIZE(feature), feature, enabled);
2191     CheckStatus(vdp_st, __LINE__);
2192   }
2193
2194   if(m_config.vdpau->Supports(VDP_VIDEO_MIXER_FEATURE_HIGH_QUALITY_SCALING_L4))
2195   {
2196     VdpVideoMixerFeature feature[] = { VDP_VIDEO_MIXER_FEATURE_HIGH_QUALITY_SCALING_L4 };
2197     VdpBool enabled[]={0};
2198     vdp_st = m_config.context->GetProcs().vdp_video_mixer_set_feature_enables(m_videoMixer, ARSIZE(feature), feature, enabled);
2199     CheckStatus(vdp_st, __LINE__);
2200   }
2201
2202   if(m_config.vdpau->Supports(VDP_VIDEO_MIXER_FEATURE_HIGH_QUALITY_SCALING_L5))
2203   {
2204     VdpVideoMixerFeature feature[] = { VDP_VIDEO_MIXER_FEATURE_HIGH_QUALITY_SCALING_L5 };
2205     VdpBool enabled[]={0};
2206     vdp_st = m_config.context->GetProcs().vdp_video_mixer_set_feature_enables(m_videoMixer, ARSIZE(feature), feature, enabled);
2207     CheckStatus(vdp_st, __LINE__);
2208   }
2209
2210   if(m_config.vdpau->Supports(VDP_VIDEO_MIXER_FEATURE_HIGH_QUALITY_SCALING_L6))
2211   {
2212     VdpVideoMixerFeature feature[] = { VDP_VIDEO_MIXER_FEATURE_HIGH_QUALITY_SCALING_L6 };
2213     VdpBool enabled[]={0};
2214     vdp_st = m_config.context->GetProcs().vdp_video_mixer_set_feature_enables(m_videoMixer, ARSIZE(feature), feature, enabled);
2215     CheckStatus(vdp_st, __LINE__);
2216   }
2217
2218   if(m_config.vdpau->Supports(VDP_VIDEO_MIXER_FEATURE_HIGH_QUALITY_SCALING_L7))
2219   {
2220     VdpVideoMixerFeature feature[] = { VDP_VIDEO_MIXER_FEATURE_HIGH_QUALITY_SCALING_L7 };
2221     VdpBool enabled[]={0};
2222     vdp_st = m_config.context->GetProcs().vdp_video_mixer_set_feature_enables(m_videoMixer, ARSIZE(feature), feature, enabled);
2223     CheckStatus(vdp_st, __LINE__);
2224   }
2225
2226   if(m_config.vdpau->Supports(VDP_VIDEO_MIXER_FEATURE_HIGH_QUALITY_SCALING_L8))
2227   {
2228     VdpVideoMixerFeature feature[] = { VDP_VIDEO_MIXER_FEATURE_HIGH_QUALITY_SCALING_L8 };
2229     VdpBool enabled[]={0};
2230     vdp_st = m_config.context->GetProcs().vdp_video_mixer_set_feature_enables(m_videoMixer, ARSIZE(feature), feature, enabled);
2231     CheckStatus(vdp_st, __LINE__);
2232   }
2233
2234   if(m_config.vdpau->Supports(VDP_VIDEO_MIXER_FEATURE_HIGH_QUALITY_SCALING_L9))
2235   {
2236     VdpVideoMixerFeature feature[] = { VDP_VIDEO_MIXER_FEATURE_HIGH_QUALITY_SCALING_L9 };
2237     VdpBool enabled[]={0};
2238     vdp_st = m_config.context->GetProcs().vdp_video_mixer_set_feature_enables(m_videoMixer, ARSIZE(feature), feature, enabled);
2239     CheckStatus(vdp_st, __LINE__);
2240   }
2241 }
2242
2243 void CMixer::Init()
2244 {
2245   m_Brightness = 0.0;
2246   m_Contrast = 0.0;
2247   m_NoiseReduction = 0.0;
2248   m_Sharpness = 0.0;
2249   m_DeintMode = 0;
2250   m_Deint = 0;
2251   m_Upscale = 0;
2252   m_SeenInterlaceFlag = false;
2253   m_ColorMatrix = 0;
2254   m_PostProc = false;
2255   m_vdpError = false;
2256
2257   m_config.upscale = g_advancedSettings.m_videoVDPAUScaling;
2258   m_config.useInteropYuv = !CSettings::GetInstance().GetBool(CSettings::SETTING_VIDEOPLAYER_USEVDPAUMIXER);
2259
2260   CreateVdpauMixer();
2261 }
2262
2263 void CMixer::Uninit()
2264 {
2265   Flush();
2266   while (!m_outputSurfaces.empty())
2267   {
2268     m_outputSurfaces.pop();
2269   }
2270   m_config.context->GetProcs().vdp_video_mixer_destroy(m_videoMixer);
2271 }
2272
2273 void CMixer::Flush()
2274 {
2275   while (!m_mixerInput.empty())
2276   {
2277     CVdpauDecodedPicture pic = m_mixerInput.back();
2278     m_mixerInput.pop_back();
2279     m_config.videoSurfaces->ClearRender(pic.videoSurface);
2280   }
2281   while (!m_decodedPics.empty())
2282   {
2283     CVdpauDecodedPicture pic = m_decodedPics.front();
2284     m_decodedPics.pop();
2285     m_config.videoSurfaces->ClearRender(pic.videoSurface);
2286   }
2287   Message *msg;
2288   while (m_dataPort.ReceiveOutMessage(&msg))
2289   {
2290     if (msg->signal == CMixerDataProtocol::FRAME)
2291     {
2292       CVdpauDecodedPicture pic = *(CVdpauDecodedPicture*)msg->data;
2293       m_config.videoSurfaces->ClearRender(pic.videoSurface);
2294     }
2295     else if (msg->signal == CMixerDataProtocol::BUFFER)
2296     {
2297       VdpOutputSurface *surf;
2298       surf = (VdpOutputSurface*)msg->data;
2299       m_outputSurfaces.push(*surf);
2300     }
2301     msg->Release();
2302   }
2303 }
2304
2305 void CMixer::InitCycle()
2306 {
2307   CheckFeatures();
2308   int flags;
2309   uint64_t latency;
2310   m_config.stats->GetParams(latency, flags);
2311   if (flags & DVD_CODEC_CTRL_NO_POSTPROC)
2312     SetPostProcFeatures(false);
2313   else
2314     SetPostProcFeatures(true);
2315
2316   m_config.stats->SetCanSkipDeint(false);
2317
2318   EDEINTERLACEMODE   mode = CMediaSettings::GetInstance().GetCurrentVideoSettings().m_DeinterlaceMode;
2319   EINTERLACEMETHOD method = GetDeinterlacingMethod();
2320   bool interlaced = m_mixerInput[1].DVDPic.iFlags & DVP_FLAG_INTERLACED;
2321   m_SeenInterlaceFlag |= interlaced;
2322
2323   if (!(flags & DVD_CODEC_CTRL_NO_POSTPROC) &&
2324       (mode == VS_DEINTERLACEMODE_FORCE ||
2325       (mode == VS_DEINTERLACEMODE_AUTO && interlaced)))
2326   {
2327     if((method == VS_INTERLACEMETHOD_AUTO && interlaced)
2328       ||  method == VS_INTERLACEMETHOD_VDPAU_BOB
2329       ||  method == VS_INTERLACEMETHOD_VDPAU_TEMPORAL
2330       ||  method == VS_INTERLACEMETHOD_VDPAU_TEMPORAL_HALF
2331       ||  method == VS_INTERLACEMETHOD_VDPAU_TEMPORAL_SPATIAL
2332       ||  method == VS_INTERLACEMETHOD_VDPAU_TEMPORAL_SPATIAL_HALF
2333       ||  method == VS_INTERLACEMETHOD_VDPAU_INVERSE_TELECINE )
2334     {
2335       if(method == VS_INTERLACEMETHOD_VDPAU_TEMPORAL_HALF
2336         || method == VS_INTERLACEMETHOD_VDPAU_TEMPORAL_SPATIAL_HALF
2337         || !g_graphicsContext.IsFullScreenVideo())
2338         m_mixersteps = 1;
2339       else
2340       {
2341         m_mixersteps = 2;
2342         m_config.stats->SetCanSkipDeint(true);
2343       }
2344
2345       if (m_mixerInput[1].DVDPic.iFlags & DVD_CODEC_CTRL_SKIPDEINT)
2346       {
2347         m_mixersteps = 1;
2348       }
2349
2350       if(m_mixerInput[1].DVDPic.iFlags & DVP_FLAG_TOP_FIELD_FIRST)
2351         m_mixerfield = VDP_VIDEO_MIXER_PICTURE_STRUCTURE_TOP_FIELD;
2352       else
2353         m_mixerfield = VDP_VIDEO_MIXER_PICTURE_STRUCTURE_BOTTOM_FIELD;
2354
2355       m_mixerInput[1].DVDPic.format = RENDER_FMT_VDPAU;
2356       m_mixerInput[1].DVDPic.iFlags &= ~(DVP_FLAG_TOP_FIELD_FIRST |
2357                                         DVP_FLAG_REPEAT_TOP_FIELD |
2358                                         DVP_FLAG_INTERLACED);
2359       m_config.useInteropYuv = false;
2360     }
2361     else if (method == VS_INTERLACEMETHOD_RENDER_BOB)
2362     {
2363       m_mixersteps = 1;
2364       m_mixerfield = VDP_VIDEO_MIXER_PICTURE_STRUCTURE_FRAME;
2365       m_mixerInput[1].DVDPic.format = RENDER_FMT_VDPAU_420;
2366       m_config.useInteropYuv = true;
2367     }
2368     else
2369     {
2370       CLog::Log(LOGERROR, "CMixer::%s - interlace method: %d not supported, setting to AUTO", __FUNCTION__, method);
2371       m_mixersteps = 1;
2372       m_mixerfield = VDP_VIDEO_MIXER_PICTURE_STRUCTURE_FRAME;
2373       m_mixerInput[1].DVDPic.format = RENDER_FMT_VDPAU;
2374       m_mixerInput[1].DVDPic.iFlags &= ~(DVP_FLAG_TOP_FIELD_FIRST |
2375                                         DVP_FLAG_REPEAT_TOP_FIELD |
2376                                         DVP_FLAG_INTERLACED);
2377
2378       CMediaSettings::GetInstance().GetCurrentVideoSettings().m_InterlaceMethod = VS_INTERLACEMETHOD_AUTO;
2379     }
2380   }
2381   else
2382   {
2383     m_mixersteps = 1;
2384     m_mixerfield = VDP_VIDEO_MIXER_PICTURE_STRUCTURE_FRAME;
2385
2386     if (m_config.useInteropYuv)
2387       m_mixerInput[1].DVDPic.format = RENDER_FMT_VDPAU_420;
2388     else
2389     {
2390       m_mixerInput[1].DVDPic.format = RENDER_FMT_VDPAU;
2391       m_mixerInput[1].DVDPic.iFlags &= ~(DVP_FLAG_TOP_FIELD_FIRST |
2392                                         DVP_FLAG_REPEAT_TOP_FIELD |
2393                                         DVP_FLAG_INTERLACED);
2394     }
2395   }
2396   m_mixerstep = 0;
2397
2398   m_processPicture.crop = false;
2399   if (m_mixerInput[1].DVDPic.format == RENDER_FMT_VDPAU)
2400   {
2401     m_processPicture.outputSurface = m_outputSurfaces.front();
2402     m_mixerInput[1].DVDPic.iWidth = m_config.outWidth;
2403     m_mixerInput[1].DVDPic.iHeight = m_config.outHeight;
2404     if (m_SeenInterlaceFlag)
2405     {
2406       double ratio = (double)m_mixerInput[1].DVDPic.iDisplayHeight / m_mixerInput[1].DVDPic.iHeight;
2407       m_mixerInput[1].DVDPic.iDisplayHeight = lrint(ratio*(m_mixerInput[1].DVDPic.iHeight-NUM_CROP_PIX*2));
2408       m_processPicture.crop = true;
2409     }
2410   }
2411   else
2412   {
2413     m_mixerInput[1].DVDPic.iWidth = m_config.vidWidth;
2414     m_mixerInput[1].DVDPic.iHeight = m_config.vidHeight;
2415   }
2416
2417   m_processPicture.DVDPic = m_mixerInput[1].DVDPic;
2418   m_processPicture.videoSurface = m_mixerInput[1].videoSurface;
2419 }
2420
2421 void CMixer::FiniCycle()
2422 {
2423   // Keep video surfaces for one 2 cycles longer than used
2424   // by mixer. This avoids blocking in decoder.
2425   // NVidia recommends num_ref + 5
2426   while (m_mixerInput.size() > 5)
2427   {
2428     CVdpauDecodedPicture &tmp = m_mixerInput.back();
2429     if (m_processPicture.DVDPic.format != RENDER_FMT_VDPAU_420)
2430     {
2431       m_config.videoSurfaces->ClearRender(tmp.videoSurface);
2432     }
2433     m_mixerInput.pop_back();
2434   }
2435 }
2436
2437 void CMixer::ProcessPicture()
2438 {
2439   if (m_processPicture.DVDPic.format == RENDER_FMT_VDPAU_420)
2440     return;
2441
2442   VdpStatus vdp_st;
2443
2444   if (m_mixerstep == 1)
2445   {
2446     if(m_mixerfield == VDP_VIDEO_MIXER_PICTURE_STRUCTURE_TOP_FIELD)
2447       m_mixerfield = VDP_VIDEO_MIXER_PICTURE_STRUCTURE_BOTTOM_FIELD;
2448     else
2449       m_mixerfield = VDP_VIDEO_MIXER_PICTURE_STRUCTURE_TOP_FIELD;
2450   }
2451
2452   VdpVideoSurface past_surfaces[4] = { VDP_INVALID_HANDLE, VDP_INVALID_HANDLE, VDP_INVALID_HANDLE, VDP_INVALID_HANDLE };
2453   VdpVideoSurface futu_surfaces[2] = { VDP_INVALID_HANDLE, VDP_INVALID_HANDLE };
2454   uint32_t pastCount = 4;
2455   uint32_t futuCount = 2;
2456
2457   if(m_mixerfield == VDP_VIDEO_MIXER_PICTURE_STRUCTURE_FRAME)
2458   {
2459     // use only 2 past 1 future for progressive/weave
2460     // (only used for postproc anyway eg noise reduction)
2461     if (m_mixerInput.size() > 3)
2462       past_surfaces[1] = m_mixerInput[3].videoSurface;
2463     if (m_mixerInput.size() > 2)
2464       past_surfaces[0] = m_mixerInput[2].videoSurface;
2465     futu_surfaces[0] = m_mixerInput[0].videoSurface;
2466     pastCount = 2;
2467     futuCount = 1;
2468   }
2469   else
2470   {
2471     if(m_mixerstep == 0)
2472     { // first field
2473       if (m_mixerInput.size() > 3)
2474       {
2475         past_surfaces[3] = m_mixerInput[3].videoSurface;
2476         past_surfaces[2] = m_mixerInput[3].videoSurface;
2477       }
2478       if (m_mixerInput.size() > 2)
2479       {
2480         past_surfaces[1] = m_mixerInput[2].videoSurface;
2481         past_surfaces[0] = m_mixerInput[2].videoSurface;
2482       }
2483       futu_surfaces[0] = m_mixerInput[1].videoSurface;
2484       futu_surfaces[1] = m_mixerInput[0].videoSurface;
2485     }
2486     else
2487     { // second field
2488       if (m_mixerInput.size() > 3)
2489       {
2490         past_surfaces[3] = m_mixerInput[3].videoSurface;
2491       }
2492       if (m_mixerInput.size() > 2)
2493       {
2494         past_surfaces[2] = m_mixerInput[2].videoSurface;
2495         past_surfaces[1] = m_mixerInput[2].videoSurface;
2496       }
2497       past_surfaces[0] = m_mixerInput[1].videoSurface;
2498       futu_surfaces[0] = m_mixerInput[0].videoSurface;
2499       futu_surfaces[1] = m_mixerInput[0].videoSurface;
2500
2501       if (m_mixerInput[0].DVDPic.pts != DVD_NOPTS_VALUE &&
2502           m_mixerInput[1].DVDPic.pts != DVD_NOPTS_VALUE)
2503       {
2504         m_processPicture.DVDPic.pts = m_mixerInput[1].DVDPic.pts +
2505                                      (m_mixerInput[0].DVDPic.pts -
2506                                       m_mixerInput[1].DVDPic.pts) / 2;
2507       }
2508       else
2509         m_processPicture.DVDPic.pts = DVD_NOPTS_VALUE;
2510       m_processPicture.DVDPic.dts = DVD_NOPTS_VALUE;
2511     }
2512     m_processPicture.DVDPic.iRepeatPicture = 0.0;
2513   } // interlaced
2514
2515   VdpRect sourceRect;
2516   sourceRect.x0 = 0;
2517   sourceRect.y0 = 0;
2518   sourceRect.x1 = m_config.vidWidth;
2519   sourceRect.y1 = m_config.vidHeight;
2520
2521   VdpRect destRect;
2522   destRect.x0 = 0;
2523   destRect.y0 = 0;
2524   destRect.x1 = m_config.outWidth;
2525   destRect.y1 = m_config.outHeight;
2526
2527   // start vdpau video mixer
2528   vdp_st = m_config.context->GetProcs().vdp_video_mixer_render(m_videoMixer,
2529                                 VDP_INVALID_HANDLE,
2530                                 0,
2531                                 m_mixerfield,
2532                                 pastCount,
2533                                 past_surfaces,
2534                                 m_mixerInput[1].videoSurface,
2535                                 futuCount,
2536                                 futu_surfaces,
2537                                 &sourceRect,
2538                                 m_processPicture.outputSurface,
2539                                 &destRect,
2540                                 &destRect,
2541                                 0,
2542                                 NULL);
2543   CheckStatus(vdp_st, __LINE__);
2544 }
2545
2546
2547 bool CMixer::CheckStatus(VdpStatus vdp_st, int line)
2548 {
2549   if (vdp_st != VDP_STATUS_OK)
2550   {
2551     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);
2552     m_vdpError = true;
2553     return true;
2554   }
2555   return false;
2556 }
2557
2558 //-----------------------------------------------------------------------------
2559 // Buffer Pool
2560 //-----------------------------------------------------------------------------
2561
2562 VdpauBufferPool::VdpauBufferPool()
2563 {
2564   CVdpauRenderPicture *pic;
2565   for (unsigned int i = 0; i < NUM_RENDER_PICS; i++)
2566   {
2567     pic = new CVdpauRenderPicture(renderPicSec);
2568     allRenderPics.push_back(pic);
2569   }
2570 }
2571
2572 VdpauBufferPool::~VdpauBufferPool()
2573 {
2574   CVdpauRenderPicture *pic;
2575   for (unsigned int i = 0; i < NUM_RENDER_PICS; i++)
2576   {
2577     pic = allRenderPics[i];
2578     delete pic;
2579   }
2580   allRenderPics.clear();
2581 }
2582
2583 //-----------------------------------------------------------------------------
2584 // Output
2585 //-----------------------------------------------------------------------------
2586 COutput::COutput(CEvent *inMsgEvent) :
2587   CThread("Vdpau Output"),
2588   m_controlPort("OutputControlPort", inMsgEvent, &m_outMsgEvent),
2589   m_dataPort("OutputDataPort", inMsgEvent, &m_outMsgEvent),
2590   m_mixer(&m_outMsgEvent)
2591 {
2592   m_inMsgEvent = inMsgEvent;
2593
2594   for (unsigned int i = 0; i < m_bufferPool.allRenderPics.size(); ++i)
2595   {
2596     m_bufferPool.freeRenderPics.push_back(i);
2597   }
2598 }
2599
2600 void COutput::Start()
2601 {
2602   Create();
2603 }
2604
2605 COutput::~COutput()
2606 {
2607   Dispose();
2608
2609   m_bufferPool.freeRenderPics.clear();
2610   m_bufferPool.usedRenderPics.clear();
2611 }
2612
2613 void COutput::Dispose()
2614 {
2615   CSingleLock lock(g_graphicsContext);
2616   m_bStop = true;
2617   m_outMsgEvent.Set();
2618   StopThread();
2619   m_controlPort.Purge();
2620   m_dataPort.Purge();
2621 }
2622
2623 void COutput::OnStartup()
2624 {
2625   CLog::Log(LOGNOTICE, "COutput::OnStartup: Output Thread created");
2626 }
2627
2628 void COutput::OnExit()
2629 {
2630   CLog::Log(LOGNOTICE, "COutput::OnExit: Output Thread terminated");
2631 }
2632
2633 enum OUTPUT_STATES
2634 {
2635   O_TOP = 0,                      // 0
2636   O_TOP_ERROR,                    // 1
2637   O_TOP_UNCONFIGURED,             // 2
2638   O_TOP_CONFIGURED,               // 3
2639   O_TOP_CONFIGURED_IDLE,          // 4
2640   O_TOP_CONFIGURED_WORK,          // 5
2641 };
2642
2643 int VDPAU_OUTPUT_parentStates[] = {
2644     -1,
2645     0, //TOP_ERROR
2646     0, //TOP_UNCONFIGURED
2647     0, //TOP_CONFIGURED
2648     3, //TOP_CONFIGURED_IDLE
2649     3, //TOP_CONFIGURED_WORK
2650 };
2651
2652 void COutput::StateMachine(int signal, Protocol *port, Message *msg)
2653 {
2654   for (int state = m_state; ; state = VDPAU_OUTPUT_parentStates[state])
2655   {
2656     switch (state)
2657     {
2658     case O_TOP: // TOP
2659       if (port == &m_controlPort)
2660       {
2661         switch (signal)
2662         {
2663         case COutputControlProtocol::FLUSH:
2664           msg->Reply(COutputControlProtocol::ACC);
2665           return;
2666         case COutputControlProtocol::PRECLEANUP:
2667           msg->Reply(COutputControlProtocol::ACC);
2668           return;
2669         default:
2670           break;
2671         }
2672       }
2673       else if (port == &m_dataPort)
2674       {
2675         switch (signal)
2676         {
2677         case COutputDataProtocol::RETURNPIC:
2678           CVdpauRenderPicture *pic;
2679           pic = *((CVdpauRenderPicture**)msg->data);
2680           QueueReturnPicture(pic);
2681           return;
2682         default:
2683           break;
2684         }
2685       }
2686       {
2687         std::string portName = port == NULL ? "timer" : port->portName;
2688         CLog::Log(LOGWARNING, "COutput::%s - signal: %d form port: %s not handled for state: %d", __FUNCTION__, signal, portName.c_str(), m_state);
2689       }
2690       return;
2691
2692     case O_TOP_ERROR:
2693       break;
2694
2695     case O_TOP_UNCONFIGURED:
2696       if (port == &m_controlPort)
2697       {
2698         switch (signal)
2699         {
2700         case COutputControlProtocol::INIT:
2701           CVdpauConfig *data;
2702           data = (CVdpauConfig*)msg->data;
2703           if (data)
2704           {
2705             m_config = *data;
2706           }
2707           Init();
2708           Message *reply;
2709           if (m_mixer.m_controlPort.SendOutMessageSync(CMixerControlProtocol::INIT,
2710                                      &reply, 1000, &m_config, sizeof(m_config)))
2711           {
2712             if (reply->signal != CMixerControlProtocol::ACC)
2713               m_vdpError = true;
2714             reply->Release();
2715           }
2716
2717           // set initial number of
2718           m_bufferPool.numOutputSurfaces = 4;
2719           EnsureBufferPool();
2720           if (!m_vdpError)
2721           {
2722             m_state = O_TOP_CONFIGURED_IDLE;
2723             msg->Reply(COutputControlProtocol::ACC, &m_config, sizeof(m_config));
2724           }
2725           else
2726           {
2727             m_state = O_TOP_ERROR;
2728             msg->Reply(COutputControlProtocol::ERROR);
2729           }
2730           return;
2731         default:
2732           break;
2733         }
2734       }
2735       break;
2736
2737     case O_TOP_CONFIGURED:
2738       if (port == &m_controlPort)
2739       {
2740         switch (signal)
2741         {
2742         case COutputControlProtocol::FLUSH:
2743           Flush();
2744           msg->Reply(COutputControlProtocol::ACC);
2745           return;
2746         case COutputControlProtocol::PRECLEANUP:
2747           Flush();
2748           PreCleanup();
2749           msg->Reply(COutputControlProtocol::ACC);
2750           return;
2751         default:
2752           break;
2753         }
2754       }
2755       else if (port == &m_dataPort)
2756       {
2757         switch (signal)
2758         {
2759         case COutputDataProtocol::NEWFRAME:
2760           CVdpauDecodedPicture *frame;
2761           frame = (CVdpauDecodedPicture*)msg->data;
2762           if (frame)
2763           {
2764             m_mixer.m_dataPort.SendOutMessage(CMixerDataProtocol::FRAME,
2765                                                frame,sizeof(CVdpauDecodedPicture));
2766           }
2767           return;
2768         case COutputDataProtocol::RETURNPIC:
2769           CVdpauRenderPicture *pic;
2770           pic = *((CVdpauRenderPicture**)msg->data);
2771           QueueReturnPicture(pic);
2772           m_controlPort.SendInMessage(COutputControlProtocol::STATS);
2773           m_state = O_TOP_CONFIGURED_WORK;
2774           m_extTimeout = 0;
2775           return;
2776         default:
2777           break;
2778         }
2779       }
2780       else if (port == &m_mixer.m_dataPort)
2781       {
2782         switch (signal)
2783         {
2784         case CMixerDataProtocol::PICTURE:
2785           CVdpauProcessedPicture *pic;
2786           pic = (CVdpauProcessedPicture*)msg->data;
2787           m_bufferPool.processedPics.push(*pic);
2788           m_state = O_TOP_CONFIGURED_WORK;
2789           m_extTimeout = 0;
2790           return;
2791         default:
2792           break;
2793         }
2794       }
2795       break;
2796
2797     case O_TOP_CONFIGURED_IDLE:
2798       if (port == NULL) // timeout
2799       {
2800         switch (signal)
2801         {
2802         case COutputControlProtocol::TIMEOUT:
2803           if (ProcessSyncPicture())
2804             m_extTimeout = 10;
2805           else
2806             m_extTimeout = 100;
2807           if (HasWork())
2808           {
2809             m_state = O_TOP_CONFIGURED_WORK;
2810             m_extTimeout = 0;
2811           }
2812           return;
2813         default:
2814           break;
2815         }
2816       }
2817       break;
2818
2819     case O_TOP_CONFIGURED_WORK:
2820       if (port == NULL) // timeout
2821       {
2822         switch (signal)
2823         {
2824         case COutputControlProtocol::TIMEOUT:
2825           if (HasWork())
2826           {
2827             CVdpauRenderPicture *pic;
2828             pic = ProcessMixerPicture();
2829             if (pic)
2830             {
2831               m_config.stats->DecProcessed();
2832               m_config.stats->IncRender();
2833               m_dataPort.SendInMessage(COutputDataProtocol::PICTURE, &pic, sizeof(pic));
2834             }
2835             m_extTimeout = 1;
2836           }
2837           else
2838           {
2839             m_state = O_TOP_CONFIGURED_IDLE;
2840             m_extTimeout = 0;
2841           }
2842           return;
2843         default:
2844           break;
2845         }
2846       }
2847       break;
2848
2849     default: // we are in no state, should not happen
2850       CLog::Log(LOGERROR, "COutput::%s - no valid state: %d", __FUNCTION__, m_state);
2851       return;
2852     }
2853   } // for
2854 }
2855
2856 void COutput::Process()
2857 {
2858   Message *msg = NULL;
2859   Protocol *port = NULL;
2860   bool gotMsg;
2861
2862   m_state = O_TOP_UNCONFIGURED;
2863   m_extTimeout = 1000;
2864   m_bStateMachineSelfTrigger = false;
2865
2866   while (!m_bStop)
2867   {
2868     gotMsg = false;
2869
2870     if (m_bStateMachineSelfTrigger)
2871     {
2872       m_bStateMachineSelfTrigger = false;
2873       // self trigger state machine
2874       StateMachine(msg->signal, port, msg);
2875       if (!m_bStateMachineSelfTrigger)
2876       {
2877         msg->Release();
2878         msg = NULL;
2879       }
2880       continue;
2881     }
2882     // check control port
2883     else if (m_controlPort.ReceiveOutMessage(&msg))
2884     {
2885       gotMsg = true;
2886       port = &m_controlPort;
2887     }
2888     // check data port
2889     else if (m_dataPort.ReceiveOutMessage(&msg))
2890     {
2891       gotMsg = true;
2892       port = &m_dataPort;
2893     }
2894     // check mixer data port
2895     else if (m_mixer.m_dataPort.ReceiveInMessage(&msg))
2896     {
2897       gotMsg = true;
2898       port = &m_mixer.m_dataPort;
2899     }
2900     if (gotMsg)
2901     {
2902       StateMachine(msg->signal, port, msg);
2903       if (!m_bStateMachineSelfTrigger)
2904       {
2905         msg->Release();
2906         msg = NULL;
2907       }
2908       continue;
2909     }
2910
2911     // wait for message
2912     else if (m_outMsgEvent.WaitMSec(m_extTimeout))
2913     {
2914       continue;
2915     }
2916     // time out
2917     else
2918     {
2919       msg = m_controlPort.GetMessage();
2920       msg->signal = COutputControlProtocol::TIMEOUT;
2921       port = 0;
2922       // signal timeout to state machine
2923       StateMachine(msg->signal, port, msg);
2924       if (!m_bStateMachineSelfTrigger)
2925       {
2926         msg->Release();
2927         msg = NULL;
2928       }
2929     }
2930   }
2931   Flush();
2932   Uninit();
2933 }
2934
2935 bool COutput::Init()
2936 {
2937   if (!CreateGlxContext())
2938     return false;
2939
2940   if (!GLInit())
2941     return false;
2942
2943   m_mixer.Start();
2944   m_vdpError = false;
2945
2946   return true;
2947 }
2948
2949 bool COutput::Uninit()
2950 {
2951   m_mixer.Dispose();
2952   glFlush();
2953   while(ProcessSyncPicture())
2954   {
2955     Sleep(10);
2956   }
2957   GLUnmapSurfaces();
2958   ReleaseBufferPool();
2959   DestroyGlxContext();
2960   return true;
2961 }
2962
2963 void COutput::Flush()
2964 {
2965   if (m_mixer.IsActive())
2966   {
2967     Message *reply;
2968     if (m_mixer.m_controlPort.SendOutMessageSync(CMixerControlProtocol::FLUSH,
2969                                                  &reply,
2970                                                  2000))
2971     {
2972       reply->Release();
2973     }
2974     else
2975       CLog::Log(LOGERROR, "Coutput::%s - failed to flush mixer", __FUNCTION__);
2976   }
2977
2978   Message *msg;
2979   while (m_mixer.m_dataPort.ReceiveInMessage(&msg))
2980   {
2981     if (msg->signal == CMixerDataProtocol::PICTURE)
2982     {
2983       CVdpauProcessedPicture pic = *(CVdpauProcessedPicture*)msg->data;
2984       m_bufferPool.processedPics.push(pic);
2985     }
2986     msg->Release();
2987   }
2988
2989   while (m_dataPort.ReceiveOutMessage(&msg))
2990   {
2991     if (msg->signal == COutputDataProtocol::NEWFRAME)
2992     {
2993       CVdpauDecodedPicture pic = *(CVdpauDecodedPicture*)msg->data;
2994       m_config.videoSurfaces->ClearRender(pic.videoSurface);
2995     }
2996     else if (msg->signal == COutputDataProtocol::RETURNPIC)
2997     {
2998       CVdpauRenderPicture *pic;
2999       pic = *((CVdpauRenderPicture**)msg->data);
3000       QueueReturnPicture(pic);
3001     }
3002     msg->Release();
3003   }
3004
3005   while (m_dataPort.ReceiveInMessage(&msg))
3006   {
3007     if (msg->signal == COutputDataProtocol::PICTURE)
3008     {
3009       CVdpauRenderPicture *pic;
3010       pic = *((CVdpauRenderPicture**)msg->data);
3011       QueueReturnPicture(pic);
3012     }
3013   }
3014
3015   // reset used render flag which was cleared on mixer flush
3016   std::deque<int>::iterator it;
3017   for (it = m_bufferPool.usedRenderPics.begin(); it != m_bufferPool.usedRenderPics.end(); ++it)
3018   {
3019     CVdpauRenderPicture *pic = m_bufferPool.allRenderPics[*it];
3020     if (pic->DVDPic.format == RENDER_FMT_VDPAU_420)
3021     {
3022       std::map<VdpVideoSurface, VdpauBufferPool::GLVideoSurface>::iterator it2;
3023       it2 = m_bufferPool.glVideoSurfaceMap.find(pic->sourceIdx);
3024       if (it2 == m_bufferPool.glVideoSurfaceMap.end())
3025       {
3026         if (g_advancedSettings.CanLogComponent(LOGVIDEO))
3027           CLog::Log(LOGDEBUG, "COutput::Flush - gl surface not found");
3028
3029         continue;
3030       }
3031       m_config.videoSurfaces->MarkRender(it2->second.sourceVuv);
3032     }
3033   }
3034
3035   // clear processed pics
3036   while(!m_bufferPool.processedPics.empty())
3037   {
3038     CVdpauProcessedPicture procPic = m_bufferPool.processedPics.front();
3039     if (procPic.DVDPic.format == RENDER_FMT_VDPAU)
3040     {
3041       m_mixer.m_dataPort.SendOutMessage(CMixerDataProtocol::BUFFER, &procPic.outputSurface, sizeof(procPic.outputSurface));
3042     }
3043     else if (procPic.DVDPic.format == RENDER_FMT_VDPAU_420)
3044     {
3045       m_config.videoSurfaces->ClearRender(procPic.videoSurface);
3046     }
3047     m_bufferPool.processedPics.pop();
3048   }
3049 }
3050
3051 bool COutput::HasWork()
3052 {
3053   if (!m_bufferPool.processedPics.empty() && !m_bufferPool.freeRenderPics.empty())
3054     return true;
3055   return false;
3056 }
3057
3058 CVdpauRenderPicture* COutput::ProcessMixerPicture()
3059 {
3060   CVdpauRenderPicture *retPic = NULL;
3061
3062   if (!m_bufferPool.processedPics.empty() && !m_bufferPool.freeRenderPics.empty())
3063   {
3064     int idx = m_bufferPool.freeRenderPics.front();
3065     retPic = m_bufferPool.allRenderPics[idx];
3066     m_bufferPool.freeRenderPics.pop_front();
3067     m_bufferPool.usedRenderPics.push_back(idx);
3068     CVdpauProcessedPicture procPic = m_bufferPool.processedPics.front();
3069     m_bufferPool.processedPics.pop();
3070
3071     retPic->DVDPic = procPic.DVDPic;
3072     retPic->valid = true;
3073     if (retPic->DVDPic.format == RENDER_FMT_VDPAU)
3074     {
3075       m_config.useInteropYuv = false;
3076       m_bufferPool.numOutputSurfaces = NUM_RENDER_PICS;
3077       EnsureBufferPool();
3078       GLMapSurface(false, procPic.outputSurface);
3079       retPic->sourceIdx = procPic.outputSurface;
3080       retPic->texture[0] = m_bufferPool.glOutputSurfaceMap[procPic.outputSurface].texture[0];
3081       retPic->texWidth = m_config.outWidth;
3082       retPic->texHeight = m_config.outHeight;
3083       retPic->crop.x1 = 0;
3084       retPic->crop.y1 = procPic.crop ? NUM_CROP_PIX : 0;
3085       retPic->crop.x2 = m_config.outWidth;
3086       retPic->crop.y2 = m_config.outHeight - retPic->crop.y1;
3087     }
3088     else
3089     {
3090       m_config.useInteropYuv = true;
3091       GLMapSurface(true, procPic.videoSurface);
3092       retPic->sourceIdx = procPic.videoSurface;
3093       for (unsigned int i=0; i<4; ++i)
3094         retPic->texture[i] = m_bufferPool.glVideoSurfaceMap[procPic.videoSurface].texture[i];
3095       retPic->texWidth = m_config.surfaceWidth;
3096       retPic->texHeight = m_config.surfaceHeight;
3097       retPic->crop.x1 = 0;
3098       retPic->crop.y1 = 0;
3099       retPic->crop.x2 = m_config.surfaceWidth - m_config.vidWidth;
3100       retPic->crop.y2 = m_config.surfaceHeight - m_config.vidHeight;
3101     }
3102   }
3103   return retPic;
3104 }
3105
3106 void COutput::QueueReturnPicture(CVdpauRenderPicture *pic)
3107 {
3108   std::deque<int>::iterator it;
3109   for (it = m_bufferPool.usedRenderPics.begin(); it != m_bufferPool.usedRenderPics.end(); ++it)
3110   {
3111     if (m_bufferPool.allRenderPics[*it] == pic)
3112     {
3113       break;
3114     }
3115   }
3116
3117   if (it == m_bufferPool.usedRenderPics.end())
3118   {
3119     CLog::Log(LOGWARNING, "COutput::QueueReturnPicture - pic not found");
3120     return;
3121   }
3122
3123   // check if already queued
3124   std::deque<int>::iterator it2 = find(m_bufferPool.syncRenderPics.begin(),
3125                                        m_bufferPool.syncRenderPics.end(),
3126                                        *it);
3127   if (it2 == m_bufferPool.syncRenderPics.end())
3128   {
3129     m_bufferPool.syncRenderPics.push_back(*it);
3130   }
3131
3132   ProcessSyncPicture();
3133 }
3134
3135 bool COutput::ProcessSyncPicture()
3136 {
3137   CVdpauRenderPicture *pic;
3138   bool busy = false;
3139
3140   std::deque<int>::iterator it;
3141   for (it = m_bufferPool.syncRenderPics.begin(); it != m_bufferPool.syncRenderPics.end(); )
3142   {
3143     pic = m_bufferPool.allRenderPics[*it];
3144
3145 #ifdef GL_ARB_sync
3146     if (pic->usefence)
3147     {
3148       if (glIsSync(pic->fence))
3149       {
3150         GLint state;
3151         GLsizei length;
3152         glGetSynciv(pic->fence, GL_SYNC_STATUS, 1, &length, &state);
3153         if(state == GL_SIGNALED)
3154         {
3155           glDeleteSync(pic->fence);
3156           pic->fence = None;
3157         }
3158         else
3159         {
3160           busy = true;
3161           ++it;
3162           continue;
3163         }
3164       }
3165     }
3166 #endif
3167
3168     m_bufferPool.freeRenderPics.push_back(*it);
3169
3170     std::deque<int>::iterator it2 = find(m_bufferPool.usedRenderPics.begin(),
3171                                          m_bufferPool.usedRenderPics.end(),
3172                                          *it);
3173     if (it2 == m_bufferPool.usedRenderPics.end())
3174     {
3175       CLog::Log(LOGERROR, "COutput::ProcessSyncPicture - pic not found in queue");
3176     }
3177     else
3178     {
3179       m_bufferPool.usedRenderPics.erase(it2);
3180     }
3181     it = m_bufferPool.syncRenderPics.erase(it);
3182
3183     if (pic->valid)
3184     {
3185       ProcessReturnPicture(pic);
3186     }
3187     else
3188     {
3189       if (g_advancedSettings.CanLogComponent(LOGVIDEO))
3190         CLog::Log(LOGDEBUG, "COutput::%s - return of invalid render pic", __FUNCTION__);
3191     }
3192   }
3193   return busy;
3194 }
3195
3196 void COutput::ProcessReturnPicture(CVdpauRenderPicture *pic)
3197 {
3198   if (pic->DVDPic.format == RENDER_FMT_VDPAU_420)
3199   {
3200     std::map<VdpVideoSurface, VdpauBufferPool::GLVideoSurface>::iterator it;
3201     it = m_bufferPool.glVideoSurfaceMap.find(pic->sourceIdx);
3202     if (it == m_bufferPool.glVideoSurfaceMap.end())
3203     {
3204       if (g_advancedSettings.CanLogComponent(LOGVIDEO))
3205         CLog::Log(LOGDEBUG, "COutput::ProcessReturnPicture - gl surface not found");
3206
3207       return;
3208     }
3209 #ifdef GL_NV_vdpau_interop
3210     glVDPAUUnmapSurfacesNV(1, &(it->second.glVdpauSurface));
3211 #endif
3212     VdpVideoSurface surf = it->second.sourceVuv;
3213     m_config.videoSurfaces->ClearRender(surf);
3214   }
3215   else if (pic->DVDPic.format == RENDER_FMT_VDPAU)
3216   {
3217     std::map<VdpOutputSurface, VdpauBufferPool::GLVideoSurface>::iterator it;
3218     it = m_bufferPool.glOutputSurfaceMap.find(pic->sourceIdx);
3219     if (it == m_bufferPool.glOutputSurfaceMap.end())
3220     {
3221       if (g_advancedSettings.CanLogComponent(LOGVIDEO))
3222         CLog::Log(LOGDEBUG, "COutput::ProcessReturnPicture - gl surface not found");
3223
3224       return;
3225     }
3226 #ifdef GL_NV_vdpau_interop
3227     glVDPAUUnmapSurfacesNV(1, &(it->second.glVdpauSurface));
3228 #endif
3229     VdpOutputSurface outSurf = it->second.sourceRgb;
3230     m_mixer.m_dataPort.SendOutMessage(CMixerDataProtocol::BUFFER, &outSurf, sizeof(outSurf));
3231   }
3232 }
3233
3234 bool COutput::EnsureBufferPool()
3235 {
3236   VdpStatus vdp_st;
3237
3238   // Creation of outputSurfaces
3239   VdpOutputSurface outputSurface;
3240   for (int i = m_bufferPool.outputSurfaces.size(); i < m_bufferPool.numOutputSurfaces; i++)
3241   {
3242     vdp_st = m_config.context->GetProcs().vdp_output_surface_create(m_config.context->GetDevice(),
3243                                       VDP_RGBA_FORMAT_B8G8R8A8,
3244                                       m_config.outWidth,
3245                                       m_config.outHeight,
3246                                       &outputSurface);
3247     if (CheckStatus(vdp_st, __LINE__))
3248       return false;
3249     m_bufferPool.outputSurfaces.push_back(outputSurface);
3250
3251     m_mixer.m_dataPort.SendOutMessage(CMixerDataProtocol::BUFFER,
3252                                       &outputSurface,
3253                                       sizeof(VdpOutputSurface));
3254     CLog::Log(LOGNOTICE, "VDPAU::COutput::InitBufferPool - Output Surface created");
3255   }
3256   return true;
3257 }
3258
3259 void COutput::ReleaseBufferPool()
3260 {
3261   VdpStatus vdp_st;
3262
3263   CSingleLock lock(m_bufferPool.renderPicSec);
3264
3265   // release all output surfaces
3266   for (unsigned int i = 0; i < m_bufferPool.outputSurfaces.size(); ++i)
3267   {
3268     if (m_bufferPool.outputSurfaces[i] == VDP_INVALID_HANDLE)
3269       continue;
3270     vdp_st = m_config.context->GetProcs().vdp_output_surface_destroy(m_bufferPool.outputSurfaces[i]);
3271     CheckStatus(vdp_st, __LINE__);
3272   }
3273   m_bufferPool.outputSurfaces.clear();
3274
3275   // wait for all fences
3276   XbmcThreads::EndTime timeout(1000);
3277   for (unsigned int i = 0; i < m_bufferPool.allRenderPics.size(); i++)
3278   {
3279     CVdpauRenderPicture *pic = m_bufferPool.allRenderPics[i];
3280     if (pic->usefence)
3281     {
3282 #ifdef GL_ARB_sync
3283       while (glIsSync(pic->fence))
3284       {
3285         GLint state;
3286         GLsizei length;
3287         glGetSynciv(pic->fence, GL_SYNC_STATUS, 1, &length, &state);
3288         if(state == GL_SIGNALED || timeout.IsTimePast())
3289         {
3290           glDeleteSync(pic->fence);
3291         }
3292         else
3293         {
3294           Sleep(5);
3295         }
3296       }
3297       pic->fence = None;
3298 #endif
3299     }
3300   }
3301   if (timeout.IsTimePast())
3302   {
3303     CLog::Log(LOGERROR, "COutput::%s - timeout waiting for fence", __FUNCTION__);
3304   }
3305   ProcessSyncPicture();
3306
3307   // invalidate all used render pictures
3308   for (unsigned int i = 0; i < m_bufferPool.usedRenderPics.size(); ++i)
3309   {
3310     CVdpauRenderPicture *pic = m_bufferPool.allRenderPics[m_bufferPool.usedRenderPics[i]];
3311     pic->valid = false;
3312   }
3313 }
3314
3315 void COutput::PreCleanup()
3316 {
3317
3318   VdpStatus vdp_st;
3319
3320   m_mixer.Dispose();
3321   ProcessSyncPicture();
3322
3323   CSingleLock lock(m_bufferPool.renderPicSec);
3324   for (unsigned int i = 0; i < m_bufferPool.outputSurfaces.size(); ++i)
3325   {
3326     if (m_bufferPool.outputSurfaces[i] == VDP_INVALID_HANDLE)
3327       continue;
3328
3329     // check if output surface is in use
3330     bool used = false;
3331     std::deque<int>::iterator it;
3332     CVdpauRenderPicture *pic;
3333     for (it = m_bufferPool.usedRenderPics.begin(); it != m_bufferPool.usedRenderPics.end(); ++it)
3334     {
3335       pic = m_bufferPool.allRenderPics[*it];
3336       if ((pic->sourceIdx == m_bufferPool.outputSurfaces[i]) && pic->valid)
3337       {
3338         used = true;
3339         break;
3340       }
3341     }
3342     if (used)
3343       continue;
3344
3345 #ifdef GL_NV_vdpau_interop
3346     // unmap surface
3347     std::map<VdpOutputSurface, VdpauBufferPool::GLVideoSurface>::iterator it_map;
3348     it_map = m_bufferPool.glOutputSurfaceMap.find(m_bufferPool.outputSurfaces[i]);
3349     if (it_map == m_bufferPool.glOutputSurfaceMap.end())
3350     {
3351       CLog::Log(LOGERROR, "%s - could not find gl surface", __FUNCTION__);
3352       continue;
3353     }
3354     glVDPAUUnregisterSurfaceNV(it_map->second.glVdpauSurface);
3355     glDeleteTextures(1, it_map->second.texture);
3356     m_bufferPool.glOutputSurfaceMap.erase(it_map);
3357 #endif
3358
3359     vdp_st = m_config.context->GetProcs().vdp_output_surface_destroy(m_bufferPool.outputSurfaces[i]);
3360     CheckStatus(vdp_st, __LINE__);
3361
3362     m_bufferPool.outputSurfaces[i] = VDP_INVALID_HANDLE;
3363     if (g_advancedSettings.CanLogComponent(LOGVIDEO))
3364       CLog::Log(LOGDEBUG, "VDPAU::PreCleanup - released output surface");
3365   }
3366
3367 }
3368
3369 void COutput::InitMixer()
3370 {
3371   for (unsigned int i = 0; i < m_bufferPool.outputSurfaces.size(); ++i)
3372   {
3373     m_mixer.m_dataPort.SendOutMessage(CMixerDataProtocol::BUFFER,
3374                                       &m_bufferPool.outputSurfaces[i],
3375                                       sizeof(VdpOutputSurface));
3376   }
3377 }
3378
3379 bool COutput::GLInit()
3380 {
3381 #ifdef GL_NV_vdpau_interop
3382   glVDPAUInitNV = NULL;
3383   glVDPAUFiniNV = NULL;
3384   glVDPAURegisterOutputSurfaceNV = NULL;
3385   glVDPAURegisterVideoSurfaceNV = NULL;
3386   glVDPAUIsSurfaceNV = NULL;
3387   glVDPAUUnregisterSurfaceNV = NULL;
3388   glVDPAUSurfaceAccessNV = NULL;
3389   glVDPAUMapSurfacesNV = NULL;
3390   glVDPAUUnmapSurfacesNV = NULL;
3391   glVDPAUGetSurfaceivNV = NULL;
3392 #endif
3393
3394 #ifdef GL_NV_vdpau_interop
3395     if (!glVDPAUInitNV)
3396       glVDPAUInitNV    = (PFNGLVDPAUINITNVPROC)glXGetProcAddress((GLubyte *) "glVDPAUInitNV");
3397     if (!glVDPAUFiniNV)
3398       glVDPAUFiniNV = (PFNGLVDPAUFININVPROC)glXGetProcAddress((GLubyte *) "glVDPAUFiniNV");
3399     if (!glVDPAURegisterOutputSurfaceNV)
3400       glVDPAURegisterOutputSurfaceNV    = (PFNGLVDPAUREGISTEROUTPUTSURFACENVPROC)glXGetProcAddress((GLubyte *) "glVDPAURegisterOutputSurfaceNV");
3401     if (!glVDPAURegisterVideoSurfaceNV)
3402       glVDPAURegisterVideoSurfaceNV    = (PFNGLVDPAUREGISTERVIDEOSURFACENVPROC)glXGetProcAddress((GLubyte *) "glVDPAURegisterVideoSurfaceNV");
3403     if (!glVDPAUIsSurfaceNV)
3404       glVDPAUIsSurfaceNV    = (PFNGLVDPAUISSURFACENVPROC)glXGetProcAddress((GLubyte *) "glVDPAUIsSurfaceNV");
3405     if (!glVDPAUUnregisterSurfaceNV)
3406       glVDPAUUnregisterSurfaceNV = (PFNGLVDPAUUNREGISTERSURFACENVPROC)glXGetProcAddress((GLubyte *) "glVDPAUUnregisterSurfaceNV");
3407     if (!glVDPAUSurfaceAccessNV)
3408       glVDPAUSurfaceAccessNV    = (PFNGLVDPAUSURFACEACCESSNVPROC)glXGetProcAddress((GLubyte *) "glVDPAUSurfaceAccessNV");
3409     if (!glVDPAUMapSurfacesNV)
3410       glVDPAUMapSurfacesNV = (PFNGLVDPAUMAPSURFACESNVPROC)glXGetProcAddress((GLubyte *) "glVDPAUMapSurfacesNV");
3411     if (!glVDPAUUnmapSurfacesNV)
3412       glVDPAUUnmapSurfacesNV = (PFNGLVDPAUUNMAPSURFACESNVPROC)glXGetProcAddress((GLubyte *) "glVDPAUUnmapSurfacesNV");
3413     if (!glVDPAUGetSurfaceivNV)
3414       glVDPAUGetSurfaceivNV = (PFNGLVDPAUGETSURFACEIVNVPROC)glXGetProcAddress((GLubyte *) "glVDPAUGetSurfaceivNV");
3415
3416   while (glGetError() != GL_NO_ERROR);
3417   glVDPAUInitNV(reinterpret_cast<void*>(m_config.context->GetDevice()), reinterpret_cast<void*>(m_config.context->GetProcs().vdp_get_proc_address));
3418   if (glGetError() != GL_NO_ERROR)
3419   {
3420     CLog::Log(LOGERROR, "VDPAU::COutput - GLInitInterop glVDPAUInitNV failed");
3421     m_vdpError = true;
3422     return false;
3423   }
3424   CLog::Log(LOGNOTICE, "VDPAU::COutput: vdpau gl interop initialized");
3425 #endif
3426
3427 #ifdef GL_ARB_sync
3428   bool hasfence = glewIsSupported("GL_ARB_sync");
3429   for (unsigned int i = 0; i < m_bufferPool.allRenderPics.size(); i++)
3430   {
3431     m_bufferPool.allRenderPics[i]->usefence = hasfence;
3432   }
3433 #endif
3434
3435   return true;
3436 }
3437
3438 void COutput::GLMapSurface(bool yuv, uint32_t source)
3439 {
3440 #ifdef GL_NV_vdpau_interop
3441
3442   if (yuv)
3443   {
3444     std::map<VdpVideoSurface, VdpauBufferPool::GLVideoSurface>::iterator it;
3445     it = m_bufferPool.glVideoSurfaceMap.find(source);
3446     if (it == m_bufferPool.glVideoSurfaceMap.end())
3447     {
3448       VdpauBufferPool::GLVideoSurface glSurface;
3449       VdpVideoSurface surf = source;
3450
3451       if (surf == VDP_INVALID_HANDLE)
3452         return;
3453
3454       glSurface.sourceVuv = surf;
3455       while (glGetError() != GL_NO_ERROR) ;
3456       glGenTextures(4, glSurface.texture);
3457       if (glGetError() != GL_NO_ERROR)
3458       {
3459         CLog::Log(LOGERROR, "VDPAU::COutput error creating texture");
3460         m_vdpError = true;
3461       }
3462       glSurface.glVdpauSurface = glVDPAURegisterVideoSurfaceNV(reinterpret_cast<void*>(surf),
3463                                                     GL_TEXTURE_2D, 4, glSurface.texture);
3464
3465       if (glGetError() != GL_NO_ERROR)
3466       {
3467         CLog::Log(LOGERROR, "VDPAU::COutput error register video surface");
3468         m_vdpError = true;
3469       }
3470       glVDPAUSurfaceAccessNV(glSurface.glVdpauSurface, GL_READ_ONLY);
3471       if (glGetError() != GL_NO_ERROR)
3472       {
3473         CLog::Log(LOGERROR, "VDPAU::COutput error setting access");
3474         m_vdpError = true;
3475       }
3476       m_bufferPool.glVideoSurfaceMap[surf] = glSurface;
3477
3478       CLog::Log(LOGNOTICE, "VDPAU::COutput registered surface");
3479     }
3480
3481     while (glGetError() != GL_NO_ERROR) ;
3482     glVDPAUMapSurfacesNV(1, &m_bufferPool.glVideoSurfaceMap[source].glVdpauSurface);
3483     if (glGetError() != GL_NO_ERROR)
3484     {
3485       CLog::Log(LOGERROR, "VDPAU::COutput error mapping surface");
3486       m_vdpError = true;
3487     }
3488
3489     if (m_vdpError)
3490       return;
3491   }
3492   else
3493   {
3494     std::map<VdpOutputSurface, VdpauBufferPool::GLVideoSurface>::iterator it;
3495     it = m_bufferPool.glOutputSurfaceMap.find(source);
3496     if (it == m_bufferPool.glOutputSurfaceMap.end())
3497     {
3498       unsigned int idx = 0;
3499       for (idx = 0; idx<m_bufferPool.outputSurfaces.size(); idx++)
3500       {
3501         if (m_bufferPool.outputSurfaces[idx] == source)
3502           break;
3503       }
3504
3505       VdpauBufferPool::GLVideoSurface glSurface;
3506       glSurface.sourceRgb = m_bufferPool.outputSurfaces[idx];
3507       glGenTextures(1, glSurface.texture);
3508       glSurface.glVdpauSurface = glVDPAURegisterOutputSurfaceNV(reinterpret_cast<void*>(m_bufferPool.outputSurfaces[idx]),
3509                                                GL_TEXTURE_2D, 1, glSurface.texture);
3510       if (glGetError() != GL_NO_ERROR)
3511       {
3512         CLog::Log(LOGERROR, "VDPAU::COutput error register output surface");
3513         m_vdpError = true;
3514       }
3515       glVDPAUSurfaceAccessNV(glSurface.glVdpauSurface, GL_READ_ONLY);
3516       if (glGetError() != GL_NO_ERROR)
3517       {
3518         CLog::Log(LOGERROR, "VDPAU::COutput error setting access");
3519         m_vdpError = true;
3520       }
3521       m_bufferPool.glOutputSurfaceMap[source] = glSurface;
3522       CLog::Log(LOGNOTICE, "VDPAU::COutput registered output surfaces");
3523     }
3524
3525     while (glGetError() != GL_NO_ERROR) ;
3526     glVDPAUMapSurfacesNV(1, &m_bufferPool.glOutputSurfaceMap[source].glVdpauSurface);
3527     if (glGetError() != GL_NO_ERROR)
3528     {
3529       CLog::Log(LOGERROR, "VDPAU::COutput error mapping surface");
3530       m_vdpError = true;
3531     }
3532
3533     if (m_vdpError)
3534       return;
3535   }
3536 #endif
3537 }
3538
3539 void COutput::GLUnmapSurfaces()
3540 {
3541 #ifdef GL_NV_vdpau_interop
3542
3543   {
3544     std::map<VdpVideoSurface, VdpauBufferPool::GLVideoSurface>::iterator it;
3545     for (it = m_bufferPool.glVideoSurfaceMap.begin(); it != m_bufferPool.glVideoSurfaceMap.end(); ++it)
3546     {
3547       glVDPAUUnregisterSurfaceNV(it->second.glVdpauSurface);
3548       glDeleteTextures(4, it->second.texture);
3549     }
3550     m_bufferPool.glVideoSurfaceMap.clear();
3551   }
3552
3553   std::map<VdpOutputSurface, VdpauBufferPool::GLVideoSurface>::iterator it;
3554   for (it = m_bufferPool.glOutputSurfaceMap.begin(); it != m_bufferPool.glOutputSurfaceMap.end(); ++it)
3555   {
3556     glVDPAUUnregisterSurfaceNV(it->second.glVdpauSurface);
3557     glDeleteTextures(1, it->second.texture);
3558   }
3559   m_bufferPool.glOutputSurfaceMap.clear();
3560
3561   glVDPAUFiniNV();
3562
3563   CLog::Log(LOGNOTICE, "VDPAU::COutput: vdpau gl interop finished");
3564
3565 #endif
3566 }
3567
3568 bool COutput::CheckStatus(VdpStatus vdp_st, int line)
3569 {
3570   if (vdp_st != VDP_STATUS_OK)
3571   {
3572     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);
3573     m_vdpError = true;
3574     return true;
3575   }
3576   return false;
3577 }
3578
3579 bool COutput::CreateGlxContext()
3580 {
3581   GLXContext   glContext;
3582
3583   m_Display = g_Windowing.GetDisplay();
3584   glContext = g_Windowing.GetGlxContext();
3585   m_Window = g_Windowing.GetWindow();
3586
3587   // Get our window attribs.
3588   XWindowAttributes wndattribs;
3589   XGetWindowAttributes(m_Display, m_Window, &wndattribs);
3590
3591   // Get visual Info
3592   XVisualInfo visInfo;
3593   visInfo.visualid = wndattribs.visual->visualid;
3594   int nvisuals = 0;
3595   XVisualInfo* visuals = XGetVisualInfo(m_Display, VisualIDMask, &visInfo, &nvisuals);
3596   if (nvisuals != 1)
3597   {
3598     CLog::Log(LOGERROR, "VDPAU::COutput::CreateGlxContext - could not find visual");
3599     return false;
3600   }
3601   visInfo = visuals[0];
3602   XFree(visuals);
3603
3604   m_pixmap = XCreatePixmap(m_Display,
3605                            m_Window,
3606                            192,
3607                            108,
3608                            visInfo.depth);
3609   if (!m_pixmap)
3610   {
3611     CLog::Log(LOGERROR, "VDPAU::COutput::CreateGlxContext - Unable to create XPixmap");
3612     return false;
3613   }
3614
3615   // create gl pixmap
3616   m_glPixmap = glXCreateGLXPixmap(m_Display, &visInfo, m_pixmap);
3617
3618   if (!m_glPixmap)
3619   {
3620     CLog::Log(LOGINFO, "VDPAU::COutput::CreateGlxContext - Could not create glPixmap");
3621     return false;
3622   }
3623
3624   m_glContext = glXCreateContext(m_Display, &visInfo, glContext, True);
3625
3626   if (!glXMakeCurrent(m_Display, m_glPixmap, m_glContext))
3627   {
3628     CLog::Log(LOGINFO, "VDPAU::COutput::CreateGlxContext - Could not make Pixmap current");
3629     return false;
3630   }
3631
3632   CLog::Log(LOGNOTICE, "VDPAU::COutput::CreateGlxContext - created context");
3633   return true;
3634 }
3635
3636 bool COutput::DestroyGlxContext()
3637 {
3638   if (m_glContext)
3639   {
3640     glXMakeCurrent(m_Display, None, NULL);
3641     glXDestroyContext(m_Display, m_glContext);
3642   }
3643   m_glContext = 0;
3644
3645   if (m_glPixmap)
3646     glXDestroyPixmap(m_Display, m_glPixmap);
3647   m_glPixmap = 0;
3648
3649   if (m_pixmap)
3650     XFreePixmap(m_Display, m_pixmap);
3651   m_pixmap = 0;
3652
3653   return true;
3654 }
3655
3656 #endif