vdpau: Improve detection of supported files.
[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
40 using namespace VDPAU;
41 #define NUM_RENDER_PICS 7
42
43 #define ARSIZE(x) (sizeof(x) / sizeof((x)[0]))
44
45 // settings codecs mapping
46 DVDCodecAvailableType g_vdpau_available[] = {
47   { AV_CODEC_ID_H263, "videoplayer.usevdpaumpeg4" },
48   { AV_CODEC_ID_MPEG4, "videoplayer.usevdpaumpeg4" },
49   { AV_CODEC_ID_WMV3, "videoplayer.usevdpauvc1" },
50   { AV_CODEC_ID_VC1, "videoplayer.usevdpauvc1" },
51   { AV_CODEC_ID_MPEG2VIDEO, "videoplayer.usevdpaumpeg2" },
52 };
53 const size_t settings_count = sizeof(g_vdpau_available) / sizeof(DVDCodecAvailableType);
54
55 CDecoder::Desc decoder_profiles[] = {
56 {"MPEG1",        VDP_DECODER_PROFILE_MPEG1},
57 {"MPEG2_SIMPLE", VDP_DECODER_PROFILE_MPEG2_SIMPLE},
58 {"MPEG2_MAIN",   VDP_DECODER_PROFILE_MPEG2_MAIN},
59 {"H264_BASELINE",VDP_DECODER_PROFILE_H264_BASELINE},
60 {"H264_MAIN",    VDP_DECODER_PROFILE_H264_MAIN},
61 {"H264_HIGH",    VDP_DECODER_PROFILE_H264_HIGH},
62 {"VC1_SIMPLE",   VDP_DECODER_PROFILE_VC1_SIMPLE},
63 {"VC1_MAIN",     VDP_DECODER_PROFILE_VC1_MAIN},
64 {"VC1_ADVANCED", VDP_DECODER_PROFILE_VC1_ADVANCED},
65 {"MPEG4_PART2_ASP", VDP_DECODER_PROFILE_MPEG4_PART2_ASP},
66 };
67 const size_t decoder_profile_count = sizeof(decoder_profiles)/sizeof(CDecoder::Desc);
68
69 static struct SInterlaceMapping
70 {
71   const EINTERLACEMETHOD     method;
72   const VdpVideoMixerFeature feature;
73 } g_interlace_mapping[] = 
74 { {VS_INTERLACEMETHOD_VDPAU_TEMPORAL             , VDP_VIDEO_MIXER_FEATURE_DEINTERLACE_TEMPORAL}
75 , {VS_INTERLACEMETHOD_VDPAU_TEMPORAL_HALF        , VDP_VIDEO_MIXER_FEATURE_DEINTERLACE_TEMPORAL}
76 , {VS_INTERLACEMETHOD_VDPAU_TEMPORAL_SPATIAL     , VDP_VIDEO_MIXER_FEATURE_DEINTERLACE_TEMPORAL_SPATIAL}
77 , {VS_INTERLACEMETHOD_VDPAU_TEMPORAL_SPATIAL_HALF, VDP_VIDEO_MIXER_FEATURE_DEINTERLACE_TEMPORAL_SPATIAL}
78 , {VS_INTERLACEMETHOD_VDPAU_INVERSE_TELECINE     , VDP_VIDEO_MIXER_FEATURE_INVERSE_TELECINE}
79 , {VS_INTERLACEMETHOD_NONE                       , (VdpVideoMixerFeature)-1}
80 };
81
82 static float studioCSCKCoeffs601[3] = {0.299, 0.587, 0.114}; //BT601 {Kr, Kg, Kb}
83 static float studioCSCKCoeffs709[3] = {0.2126, 0.7152, 0.0722}; //BT709 {Kr, Kg, Kb}
84
85 //-----------------------------------------------------------------------------
86 //-----------------------------------------------------------------------------
87
88 CVDPAUContext *CVDPAUContext::m_context = 0;
89 CCriticalSection CVDPAUContext::m_section;
90 Display *CVDPAUContext::m_display = 0;
91 void *CVDPAUContext::m_dlHandle = 0;
92
93 CVDPAUContext::CVDPAUContext()
94 {
95   m_context = 0;
96   m_refCount = 0;
97 }
98
99 void CVDPAUContext::Release()
100 {
101   CSingleLock lock(m_section);
102
103   m_refCount--;
104   if (m_refCount <= 0)
105   {
106     Close();
107     delete this;
108     m_context = 0;
109   }
110 }
111
112 void CVDPAUContext::Close()
113 {
114   CLog::Log(LOGNOTICE, "VDPAU::Close - closing decoder context");
115   DestroyContext();
116 }
117
118 bool CVDPAUContext::EnsureContext(CVDPAUContext **ctx)
119 {
120   CSingleLock lock(m_section);
121
122   if (m_context)
123   {
124     m_context->m_refCount++;
125     *ctx = m_context;
126     return true;
127   }
128
129   m_context = new CVDPAUContext();
130   *ctx = m_context;
131   {
132     CSingleLock gLock(g_graphicsContext);
133     if (!m_context->LoadSymbols() || !m_context->CreateContext())
134     {
135       delete m_context;
136       m_context = 0;
137       return false;
138     }
139   }
140
141   m_context->m_refCount++;
142
143   *ctx = m_context;
144   return true;
145 }
146
147 VDPAU_procs& CVDPAUContext::GetProcs()
148 {
149   return m_vdpProcs;
150 }
151
152 VdpVideoMixerFeature* CVDPAUContext::GetFeatures()
153 {
154   return m_vdpFeatures;
155 }
156
157 int CVDPAUContext::GetFeatureCount()
158 {
159   return m_featureCount;
160 }
161
162 bool CVDPAUContext::LoadSymbols()
163 {
164   if (!m_dlHandle)
165   {
166     m_dlHandle  = dlopen("libvdpau.so.1", RTLD_LAZY);
167     if (!m_dlHandle)
168     {
169       const char* error = dlerror();
170       if (!error)
171         error = "dlerror() returned NULL";
172
173       CLog::Log(LOGERROR,"VDPAU::LoadSymbols: Unable to get handle to lib: %s", error);
174       return false;
175     }
176   }
177
178   char* error;
179   (void)dlerror();
180   dl_vdp_device_create_x11 = (VdpStatus (*)(Display*, int, VdpDevice*, VdpStatus (**)(VdpDevice, VdpFuncId, void**)))dlsym(m_dlHandle, (const char*)"vdp_device_create_x11");
181   error = dlerror();
182   if (error)
183   {
184     CLog::Log(LOGERROR,"(VDPAU) - %s in %s",error,__FUNCTION__);
185     m_vdpDevice = VDP_INVALID_HANDLE;
186     return false;
187   }
188   return true;
189 }
190
191 bool CVDPAUContext::CreateContext()
192 {
193   CLog::Log(LOGNOTICE,"VDPAU::CreateContext - creating decoder context");
194
195   int mScreen;
196   { CSingleLock lock(g_graphicsContext);
197     if (!m_display)
198       m_display = XOpenDisplay(NULL);
199     mScreen = g_Windowing.GetCurrentScreen();
200   }
201
202   VdpStatus vdp_st;
203   // Create Device
204   vdp_st = dl_vdp_device_create_x11(m_display,
205                                     mScreen,
206                                    &m_vdpDevice,
207                                    &m_vdpProcs.vdp_get_proc_address);
208
209   CLog::Log(LOGNOTICE,"vdp_device = 0x%08x vdp_st = 0x%08x",m_vdpDevice,vdp_st);
210   if (vdp_st != VDP_STATUS_OK)
211   {
212     CLog::Log(LOGERROR,"(VDPAU) unable to init VDPAU - vdp_st = 0x%x.  Falling back.",vdp_st);
213     m_vdpDevice = VDP_INVALID_HANDLE;
214     return false;
215   }
216
217   QueryProcs();
218   SpewHardwareAvailable();
219   return true;
220 }
221
222 void CVDPAUContext::QueryProcs()
223 {
224   VdpStatus vdp_st;
225
226 #define VDP_PROC(id, proc) \
227   do { \
228     vdp_st = m_vdpProcs.vdp_get_proc_address(m_vdpDevice, id, (void**)&proc); \
229     if (vdp_st != VDP_STATUS_OK) \
230     { \
231       CLog::Log(LOGERROR, "CVDPAUContext::GetProcs - failed to get proc id"); \
232     } \
233   } while(0);
234
235   VDP_PROC(VDP_FUNC_ID_GET_ERROR_STRING                    , m_vdpProcs.vdp_get_error_string);
236   VDP_PROC(VDP_FUNC_ID_DEVICE_DESTROY                      , m_vdpProcs.vdp_device_destroy);
237   VDP_PROC(VDP_FUNC_ID_GENERATE_CSC_MATRIX                 , m_vdpProcs.vdp_generate_csc_matrix);
238   VDP_PROC(VDP_FUNC_ID_VIDEO_SURFACE_CREATE                , m_vdpProcs.vdp_video_surface_create);
239   VDP_PROC(VDP_FUNC_ID_VIDEO_SURFACE_DESTROY               , m_vdpProcs.vdp_video_surface_destroy);
240   VDP_PROC(VDP_FUNC_ID_VIDEO_SURFACE_PUT_BITS_Y_CB_CR      , m_vdpProcs.vdp_video_surface_put_bits_y_cb_cr);
241   VDP_PROC(VDP_FUNC_ID_VIDEO_SURFACE_GET_BITS_Y_CB_CR      , m_vdpProcs.vdp_video_surface_get_bits_y_cb_cr);
242   VDP_PROC(VDP_FUNC_ID_OUTPUT_SURFACE_PUT_BITS_Y_CB_CR     , m_vdpProcs.vdp_output_surface_put_bits_y_cb_cr);
243   VDP_PROC(VDP_FUNC_ID_OUTPUT_SURFACE_PUT_BITS_NATIVE      , m_vdpProcs.vdp_output_surface_put_bits_native);
244   VDP_PROC(VDP_FUNC_ID_OUTPUT_SURFACE_CREATE               , m_vdpProcs.vdp_output_surface_create);
245   VDP_PROC(VDP_FUNC_ID_OUTPUT_SURFACE_DESTROY              , m_vdpProcs.vdp_output_surface_destroy);
246   VDP_PROC(VDP_FUNC_ID_OUTPUT_SURFACE_GET_BITS_NATIVE      , m_vdpProcs.vdp_output_surface_get_bits_native);
247   VDP_PROC(VDP_FUNC_ID_OUTPUT_SURFACE_RENDER_OUTPUT_SURFACE, m_vdpProcs.vdp_output_surface_render_output_surface);
248   VDP_PROC(VDP_FUNC_ID_OUTPUT_SURFACE_PUT_BITS_INDEXED     , m_vdpProcs.vdp_output_surface_put_bits_indexed);
249   VDP_PROC(VDP_FUNC_ID_VIDEO_MIXER_CREATE                  , m_vdpProcs.vdp_video_mixer_create);
250   VDP_PROC(VDP_FUNC_ID_VIDEO_MIXER_SET_FEATURE_ENABLES     , m_vdpProcs.vdp_video_mixer_set_feature_enables);
251   VDP_PROC(VDP_FUNC_ID_VIDEO_MIXER_DESTROY                 , m_vdpProcs.vdp_video_mixer_destroy);
252   VDP_PROC(VDP_FUNC_ID_VIDEO_MIXER_RENDER                  , m_vdpProcs.vdp_video_mixer_render);
253   VDP_PROC(VDP_FUNC_ID_VIDEO_MIXER_SET_ATTRIBUTE_VALUES    , m_vdpProcs.vdp_video_mixer_set_attribute_values);
254   VDP_PROC(VDP_FUNC_ID_VIDEO_MIXER_QUERY_PARAMETER_SUPPORT , m_vdpProcs.vdp_video_mixer_query_parameter_support);
255   VDP_PROC(VDP_FUNC_ID_VIDEO_MIXER_QUERY_FEATURE_SUPPORT   , m_vdpProcs.vdp_video_mixer_query_feature_support);
256   VDP_PROC(VDP_FUNC_ID_DECODER_CREATE                      , m_vdpProcs.vdp_decoder_create);
257   VDP_PROC(VDP_FUNC_ID_DECODER_DESTROY                     , m_vdpProcs.vdp_decoder_destroy);
258   VDP_PROC(VDP_FUNC_ID_DECODER_RENDER                      , m_vdpProcs.vdp_decoder_render);
259   VDP_PROC(VDP_FUNC_ID_DECODER_QUERY_CAPABILITIES          , m_vdpProcs.vdp_decoder_query_caps);
260 #undef VDP_PROC
261 }
262
263 VdpDevice CVDPAUContext::GetDevice()
264 {
265   return m_vdpDevice;
266 }
267
268 void CVDPAUContext::DestroyContext()
269 {
270   if (!m_vdpProcs.vdp_device_destroy)
271     return;
272
273   m_vdpProcs.vdp_device_destroy(m_vdpDevice);
274   m_vdpDevice = VDP_INVALID_HANDLE;
275 }
276
277 void CVDPAUContext::SpewHardwareAvailable()  //CopyrighVDPAUt (c) 2008 Wladimir J. van der Laan  -- VDPInfo
278 {
279   VdpStatus rv;
280   CLog::Log(LOGNOTICE,"VDPAU Decoder capabilities:");
281   CLog::Log(LOGNOTICE,"name          level macbs width height");
282   CLog::Log(LOGNOTICE,"------------------------------------");
283   for(unsigned int x=0; x<decoder_profile_count; ++x)
284   {
285     VdpBool is_supported = false;
286     uint32_t max_level, max_macroblocks, max_width, max_height;
287     rv = m_vdpProcs.vdp_decoder_query_caps(m_vdpDevice, decoder_profiles[x].id,
288                                 &is_supported, &max_level, &max_macroblocks, &max_width, &max_height);
289     if(rv == VDP_STATUS_OK && is_supported)
290     {
291       CLog::Log(LOGNOTICE,"%-16s %2i %5i %5i %5i\n", decoder_profiles[x].name,
292                 max_level, max_macroblocks, max_width, max_height);
293     }
294   }
295   CLog::Log(LOGNOTICE,"------------------------------------");
296   m_featureCount = 0;
297 #define CHECK_SUPPORT(feature)  \
298   do { \
299     VdpBool supported; \
300     if(m_vdpProcs.vdp_video_mixer_query_feature_support(m_vdpDevice, feature, &supported) == VDP_STATUS_OK && supported) { \
301       CLog::Log(LOGNOTICE, "Mixer feature: "#feature);  \
302       m_vdpFeatures[m_featureCount++] = feature; \
303     } \
304   } while(false)
305
306   CHECK_SUPPORT(VDP_VIDEO_MIXER_FEATURE_NOISE_REDUCTION);
307   CHECK_SUPPORT(VDP_VIDEO_MIXER_FEATURE_SHARPNESS);
308   CHECK_SUPPORT(VDP_VIDEO_MIXER_FEATURE_DEINTERLACE_TEMPORAL);
309   CHECK_SUPPORT(VDP_VIDEO_MIXER_FEATURE_DEINTERLACE_TEMPORAL_SPATIAL);
310   CHECK_SUPPORT(VDP_VIDEO_MIXER_FEATURE_INVERSE_TELECINE);
311 #ifdef VDP_VIDEO_MIXER_FEATURE_HIGH_QUALITY_SCALING_L1
312   CHECK_SUPPORT(VDP_VIDEO_MIXER_FEATURE_HIGH_QUALITY_SCALING_L1);
313   CHECK_SUPPORT(VDP_VIDEO_MIXER_FEATURE_HIGH_QUALITY_SCALING_L2);
314   CHECK_SUPPORT(VDP_VIDEO_MIXER_FEATURE_HIGH_QUALITY_SCALING_L3);
315   CHECK_SUPPORT(VDP_VIDEO_MIXER_FEATURE_HIGH_QUALITY_SCALING_L4);
316   CHECK_SUPPORT(VDP_VIDEO_MIXER_FEATURE_HIGH_QUALITY_SCALING_L5);
317   CHECK_SUPPORT(VDP_VIDEO_MIXER_FEATURE_HIGH_QUALITY_SCALING_L6);
318   CHECK_SUPPORT(VDP_VIDEO_MIXER_FEATURE_HIGH_QUALITY_SCALING_L7);
319   CHECK_SUPPORT(VDP_VIDEO_MIXER_FEATURE_HIGH_QUALITY_SCALING_L8);
320   CHECK_SUPPORT(VDP_VIDEO_MIXER_FEATURE_HIGH_QUALITY_SCALING_L9);
321 #endif
322 #undef CHECK_SUPPORT
323 }
324
325 bool CVDPAUContext::Supports(VdpVideoMixerFeature feature)
326 {
327   for(int i = 0; i < m_featureCount; i++)
328   {
329     if(m_vdpFeatures[i] == feature)
330       return true;
331   }
332   return false;
333 }
334
335 //-----------------------------------------------------------------------------
336 // VDPAU Video Surface states
337 //-----------------------------------------------------------------------------
338
339 #define SURFACE_USED_FOR_REFERENCE 0x01
340 #define SURFACE_USED_FOR_RENDER    0x02
341
342 void CVideoSurfaces::AddSurface(VdpVideoSurface surf)
343 {
344   CSingleLock lock(m_section);
345   m_state[surf] = SURFACE_USED_FOR_REFERENCE;
346 }
347
348 void CVideoSurfaces::ClearReference(VdpVideoSurface surf)
349 {
350   CSingleLock lock(m_section);
351   if (m_state.find(surf) == m_state.end())
352   {
353     CLog::Log(LOGWARNING, "CVideoSurfaces::ClearReference - surface invalid");
354     return;
355   }
356   m_state[surf] &= ~SURFACE_USED_FOR_REFERENCE;
357   if (m_state[surf] == 0)
358   {
359     m_freeSurfaces.push_back(surf);
360   }
361 }
362
363 bool CVideoSurfaces::MarkRender(VdpVideoSurface surf)
364 {
365   CSingleLock lock(m_section);
366   if (m_state.find(surf) == m_state.end())
367   {
368     CLog::Log(LOGWARNING, "CVideoSurfaces::MarkRender - surface invalid");
369     return false;
370   }
371   std::list<VdpVideoSurface>::iterator it;
372   it = std::find(m_freeSurfaces.begin(), m_freeSurfaces.end(), surf);
373   if (it != m_freeSurfaces.end())
374   {
375     m_freeSurfaces.erase(it);
376   }
377   m_state[surf] |= SURFACE_USED_FOR_RENDER;
378   return true;
379 }
380
381 void CVideoSurfaces::ClearRender(VdpVideoSurface surf)
382 {
383   CSingleLock lock(m_section);
384   if (m_state.find(surf) == m_state.end())
385   {
386     CLog::Log(LOGWARNING, "CVideoSurfaces::ClearRender - surface invalid");
387     return;
388   }
389   m_state[surf] &= ~SURFACE_USED_FOR_RENDER;
390   if (m_state[surf] == 0)
391   {
392     m_freeSurfaces.push_back(surf);
393   }
394 }
395
396 bool CVideoSurfaces::IsValid(VdpVideoSurface surf)
397 {
398   CSingleLock lock(m_section);
399   if (m_state.find(surf) != m_state.end())
400     return true;
401   else
402     return false;
403 }
404
405 VdpVideoSurface CVideoSurfaces::GetFree(VdpVideoSurface surf)
406 {
407   CSingleLock lock(m_section);
408   if (m_state.find(surf) != m_state.end())
409   {
410     std::list<VdpVideoSurface>::iterator it;
411     it = std::find(m_freeSurfaces.begin(), m_freeSurfaces.end(), surf);
412     if (it == m_freeSurfaces.end())
413     {
414       CLog::Log(LOGWARNING, "CVideoSurfaces::GetFree - surface not free");
415     }
416     else
417     {
418       m_freeSurfaces.erase(it);
419       m_state[surf] = SURFACE_USED_FOR_REFERENCE;
420       return surf;
421     }
422   }
423
424   if (!m_freeSurfaces.empty())
425   {
426     VdpVideoSurface freeSurf = m_freeSurfaces.front();
427     m_freeSurfaces.pop_front();
428     m_state[freeSurf] = SURFACE_USED_FOR_REFERENCE;
429     return freeSurf;
430   }
431
432   return VDP_INVALID_HANDLE;
433 }
434
435 VdpVideoSurface CVideoSurfaces::GetAtIndex(int idx)
436 {
437   if (idx >= m_state.size())
438     return VDP_INVALID_HANDLE;
439
440   std::map<VdpVideoSurface, int>::iterator it = m_state.begin();
441   for(int i = 0; i < idx; i++)
442     ++it;
443   return it->first;
444 }
445
446 VdpVideoSurface CVideoSurfaces::RemoveNext(bool skiprender)
447 {
448   CSingleLock lock(m_section);
449   VdpVideoSurface surf;
450   std::map<VdpVideoSurface, int>::iterator it;
451   for(it = m_state.begin(); it != m_state.end(); ++it)
452   {
453     if (skiprender && it->second & SURFACE_USED_FOR_RENDER)
454       continue;
455     surf = it->first;
456     m_state.erase(surf);
457
458     std::list<VdpVideoSurface>::iterator it2;
459     it2 = std::find(m_freeSurfaces.begin(), m_freeSurfaces.end(), surf);
460     if (it2 != m_freeSurfaces.end())
461       m_freeSurfaces.erase(it2);
462     return surf;
463   }
464   return VDP_INVALID_HANDLE;
465 }
466
467 void CVideoSurfaces::Reset()
468 {
469   CSingleLock lock(m_section);
470   m_freeSurfaces.clear();
471   m_state.clear();
472 }
473
474 int CVideoSurfaces::Size()
475 {
476   CSingleLock lock(m_section);
477   return m_state.size();
478 }
479
480 //-----------------------------------------------------------------------------
481 // CVDPAU
482 //-----------------------------------------------------------------------------
483
484 CDecoder::CDecoder() : m_vdpauOutput(&m_inMsgEvent)
485 {
486   m_vdpauConfig.videoSurfaces = &m_videoSurfaces;
487
488   m_vdpauConfigured = false;
489   m_hwContext.bitstream_buffers_allocated = 0;
490   m_DisplayState = VDPAU_OPEN;
491   m_vdpauConfig.context = 0;
492 }
493
494 bool CDecoder::Open(AVCodecContext* avctx, const enum PixelFormat fmt, unsigned int surfaces)
495 {
496   // check if user wants to decode this format with VDPAU
497   std::string gpuvendor = g_Windowing.GetRenderVendor();
498   std::transform(gpuvendor.begin(), gpuvendor.end(), gpuvendor.begin(), ::tolower);
499   // nvidia is whitelisted despite for mpeg-4 we need to query user settings
500   if ((gpuvendor.compare(0, 6, "nvidia") != 0)  || (avctx->codec_id == AV_CODEC_ID_MPEG4) || (avctx->codec_id == AV_CODEC_ID_H263))
501   {
502     if (CDVDVideoCodec::IsCodecDisabled(g_vdpau_available, settings_count, avctx->codec_id))
503       return false;
504   }
505
506 #ifndef GL_NV_vdpau_interop
507   CLog::Log(LOGNOTICE, "VDPAU: compilation without required extension GL_NV_vdpau_interop");
508   return false;
509 #endif
510   if (!g_Windowing.IsExtSupported("GL_NV_vdpau_interop"))
511   {
512     CLog::Log(LOGNOTICE, "VDPAU: required extension GL_NV_vdpau_interop not found");
513     return false;
514   }
515
516   if(avctx->coded_width  == 0
517   || avctx->coded_height == 0)
518   {
519     CLog::Log(LOGWARNING,"(VDPAU) no width/height available, can't init");
520     return false;
521   }
522   m_vdpauConfig.numRenderBuffers = surfaces;
523   m_decoderThread = CThread::GetCurrentThreadId();
524
525   if (!CVDPAUContext::EnsureContext(&m_vdpauConfig.context))
526     return false;
527
528   m_DisplayState = VDPAU_OPEN;
529   m_vdpauConfigured = false;
530
531   if (!m_dllAvUtil.Load())
532     return false;
533
534   m_presentPicture = 0;
535
536   {
537     VdpDecoderProfile profile = 0;
538     /* Change FFMPEG Codec ID to VDPAU Profile. */
539         ReadFormatOf(avctx->codec_id, profile, m_vdpauConfig.vdpChromaType);
540     if(profile)
541     {
542       VdpStatus vdp_st;
543       VdpBool is_supported = false;
544       uint32_t max_level, max_macroblocks, max_width, max_height;
545       /* Query Device Capabilities to Ensure that VDPAU Can handle the Requested Codec */
546       vdp_st = m_vdpauConfig.context->GetProcs().vdp_decoder_query_caps(m_vdpauConfig.context->GetDevice(),
547        profile, &is_supported, &max_level, &max_macroblocks, &max_width, &max_height);
548       /* Test to make Sure there is a Possibility the Codec will Work */
549       if(CheckStatus(vdp_st, __LINE__))
550       {
551         CLog::Log(LOGERROR, " (VDPAU) Error: %s(%d) checking for decoder support\n", m_vdpauConfig.context->GetProcs().vdp_get_error_string(vdp_st), vdp_st);
552         return false;
553       }
554       if(max_width < avctx->coded_width || max_height < avctx->coded_height)
555       {
556         CLog::Log(LOGWARNING,"(VDPAU) Requested Picture Dimensions (%i, %i) exceed hardware capabilities ( %i, %i).", 
557                 avctx->coded_width, avctx->coded_height, max_width, max_height);
558         return false;
559       }
560       if (!CDVDCodecUtils::IsVP3CompatibleWidth(avctx->coded_width))
561         CLog::Log(LOGWARNING,"(VDPAU) width %i might not be supported because of hardware bug", avctx->width);
562    
563       /* attempt to create a decoder with this width/height, some sizes are not supported by hw */
564       vdp_st = m_vdpauConfig.context->GetProcs().vdp_decoder_create(m_vdpauConfig.context->GetDevice(), profile, avctx->coded_width, avctx->coded_height, 5, &m_vdpauConfig.vdpDecoder);
565
566       if(CheckStatus(vdp_st, __LINE__))
567       {
568         CLog::Log(LOGERROR, " (VDPAU) Error: %s(%d) checking for decoder support\n", m_vdpauConfig.context->GetProcs().vdp_get_error_string(vdp_st), vdp_st);
569         return false;
570       }
571
572       m_vdpauConfig.context->GetProcs().vdp_decoder_destroy(m_vdpauConfig.vdpDecoder);
573       CheckStatus(vdp_st, __LINE__);
574
575       /* finally setup ffmpeg */
576       memset(&m_hwContext, 0, sizeof(AVVDPAUContext));
577       m_hwContext.render = CDecoder::Render;
578       m_hwContext.bitstream_buffers_allocated = 0;
579       avctx->get_buffer      = CDecoder::FFGetBuffer;
580       avctx->reget_buffer    = CDecoder::FFGetBuffer;
581       avctx->release_buffer  = CDecoder::FFReleaseBuffer;
582       avctx->draw_horiz_band = CDecoder::FFDrawSlice;
583       avctx->slice_flags=SLICE_FLAG_CODED_ORDER|SLICE_FLAG_ALLOW_FIELD;
584       avctx->hwaccel_context = &m_hwContext;
585       avctx->thread_count    = 1;
586
587       g_Windowing.Register(this);
588       return true;
589     }
590   }
591   return false;
592 }
593
594 CDecoder::~CDecoder()
595 {
596   Close();
597 }
598
599 void CDecoder::Close()
600 {
601   CLog::Log(LOGNOTICE, " (VDPAU) %s", __FUNCTION__);
602
603   g_Windowing.Unregister(this);
604
605   CSingleLock lock(m_DecoderSection);
606
607   FiniVDPAUOutput();
608   m_vdpauOutput.Dispose();
609
610   if (m_hwContext.bitstream_buffers_allocated)
611   {
612     m_dllAvUtil.av_freep(&m_hwContext.bitstream_buffers);
613   }
614
615   m_dllAvUtil.Unload();
616
617   if (m_vdpauConfig.context)
618     m_vdpauConfig.context->Release();
619   m_vdpauConfig.context = 0;
620 }
621
622 long CDecoder::Release()
623 {
624   // check if we should do some pre-cleanup here
625   // a second decoder might need resources
626   if (m_vdpauConfigured == true)
627   {
628     CSingleLock lock(m_DecoderSection);
629     CLog::Log(LOGNOTICE,"CVDPAU::Release pre-cleanup");
630
631     Message *reply;
632     if (m_vdpauOutput.m_controlPort.SendOutMessageSync(COutputControlProtocol::PRECLEANUP,
633                                                    &reply,
634                                                    2000))
635     {
636       bool success = reply->signal == COutputControlProtocol::ACC ? true : false;
637       reply->Release();
638       if (!success)
639       {
640         CLog::Log(LOGERROR, "VDPAU::%s - pre-cleanup returned error", __FUNCTION__);
641         m_DisplayState = VDPAU_ERROR;
642       }
643     }
644     else
645     {
646       CLog::Log(LOGERROR, "VDPAU::%s - pre-cleanup timed out", __FUNCTION__);
647       m_DisplayState = VDPAU_ERROR;
648     }
649
650     VdpVideoSurface surf;
651     while((surf = m_videoSurfaces.RemoveNext(true)) != VDP_INVALID_HANDLE)
652     {
653       m_vdpauConfig.context->GetProcs().vdp_video_surface_destroy(surf);
654     }
655   }
656   return IHardwareDecoder::Release();
657 }
658
659 long CDecoder::ReleasePicReference()
660 {
661   return IHardwareDecoder::Release();
662 }
663
664 void CDecoder::SetWidthHeight(int width, int height)
665 {
666   m_vdpauConfig.upscale = g_advancedSettings.m_videoVDPAUScaling;
667
668   //pick the smallest dimensions, so we downscale with vdpau and upscale with opengl when appropriate
669   //this requires the least amount of gpu memory bandwidth
670   if (g_graphicsContext.GetWidth() < width || g_graphicsContext.GetHeight() < height || m_vdpauConfig.upscale >= 0)
671   {
672     //scale width to desktop size if the aspect ratio is the same or bigger than the desktop
673     if ((double)height * g_graphicsContext.GetWidth() / width <= (double)g_graphicsContext.GetHeight())
674     {
675       m_vdpauConfig.outWidth = g_graphicsContext.GetWidth();
676       m_vdpauConfig.outHeight = MathUtils::round_int((double)height * g_graphicsContext.GetWidth() / width);
677     }
678     else //scale height to the desktop size if the aspect ratio is smaller than the desktop
679     {
680       m_vdpauConfig.outHeight = g_graphicsContext.GetHeight();
681       m_vdpauConfig.outWidth = MathUtils::round_int((double)width * g_graphicsContext.GetHeight() / height);
682     }
683   }
684   else
685   { //let opengl scale
686     m_vdpauConfig.outWidth = width;
687     m_vdpauConfig.outHeight = height;
688   }
689   CLog::Log(LOGDEBUG, "CVDPAU::SetWidthHeight Setting OutWidth: %i OutHeight: %i", m_vdpauConfig.outWidth, m_vdpauConfig.outHeight);
690 }
691
692 void CDecoder::OnLostDevice()
693 {
694   CLog::Log(LOGNOTICE,"CVDPAU::OnLostDevice event");
695
696   int count = g_graphicsContext.exit();
697
698   CSingleLock lock(m_DecoderSection);
699   FiniVDPAUOutput();
700   if (m_vdpauConfig.context)
701     m_vdpauConfig.context->Release();
702   m_vdpauConfig.context = 0;
703
704   m_DisplayState = VDPAU_LOST;
705   lock.Leave();
706   m_DisplayEvent.Reset();
707
708   g_graphicsContext.restore(count);
709 }
710
711 void CDecoder::OnResetDevice()
712 {
713   CLog::Log(LOGNOTICE,"CVDPAU::OnResetDevice event");
714
715   int count = g_graphicsContext.exit();
716
717   CSingleLock lock(m_DecoderSection);
718   if (m_DisplayState == VDPAU_LOST)
719   {
720     m_DisplayState = VDPAU_RESET;
721     lock.Leave();
722     m_DisplayEvent.Set();
723   }
724
725   g_graphicsContext.restore(count);
726 }
727
728 int CDecoder::Check(AVCodecContext* avctx)
729 {
730   EDisplayState state;
731
732   { CSingleLock lock(m_DecoderSection);
733     state = m_DisplayState;
734   }
735
736   if (state == VDPAU_LOST)
737   {
738     CLog::Log(LOGNOTICE,"CVDPAU::Check waiting for display reset event");
739     if (!m_DisplayEvent.WaitMSec(4000))
740     {
741       CLog::Log(LOGERROR, "CVDPAU::Check - device didn't reset in reasonable time");
742       state = VDPAU_RESET;
743     }
744     else
745     {
746       CSingleLock lock(m_DecoderSection);
747       state = m_DisplayState;
748     }
749   }
750   if (state == VDPAU_RESET || state == VDPAU_ERROR)
751   {
752     CSingleLock lock(m_DecoderSection);
753
754     FiniVDPAUOutput();
755     if (m_vdpauConfig.context)
756       m_vdpauConfig.context->Release();
757     m_vdpauConfig.context = 0;
758
759     if (CVDPAUContext::EnsureContext(&m_vdpauConfig.context))
760     {
761       m_DisplayState = VDPAU_OPEN;
762       m_vdpauConfigured = false;
763     }
764
765     if (state == VDPAU_RESET)
766       return VC_FLUSHED;
767     else
768       return VC_ERROR;
769   }
770   return 0;
771 }
772
773 bool CDecoder::IsVDPAUFormat(PixelFormat format)
774 {
775   if (format == AV_PIX_FMT_VDPAU)
776     return true;
777   else
778     return false;
779 }
780
781 bool CDecoder::Supports(VdpVideoMixerFeature feature)
782 {
783   return m_vdpauConfig.context->Supports(feature);
784 }
785
786 bool CDecoder::Supports(EINTERLACEMETHOD method)
787 {
788   if(method == VS_INTERLACEMETHOD_VDPAU_BOB
789   || method == VS_INTERLACEMETHOD_AUTO)
790     return true;
791
792   if (method == VS_INTERLACEMETHOD_RENDER_BOB)
793     return true;
794
795   if (method == VS_INTERLACEMETHOD_VDPAU_INVERSE_TELECINE)
796     return false;
797
798   for(SInterlaceMapping* p = g_interlace_mapping; p->method != VS_INTERLACEMETHOD_NONE; p++)
799   {
800     if(p->method == method)
801       return Supports(p->feature);
802   }
803   return false;
804 }
805
806 EINTERLACEMETHOD CDecoder::AutoInterlaceMethod()
807 {
808   return VS_INTERLACEMETHOD_RENDER_BOB;
809 }
810
811 void CDecoder::FiniVDPAUOutput()
812 {
813   if (!m_vdpauConfigured)
814     return;
815
816   CLog::Log(LOGNOTICE, " (VDPAU) %s", __FUNCTION__);
817
818   // uninit output
819   m_vdpauOutput.Dispose();
820   m_vdpauConfigured = false;
821
822   VdpStatus vdp_st;
823
824   vdp_st = m_vdpauConfig.context->GetProcs().vdp_decoder_destroy(m_vdpauConfig.vdpDecoder);
825   if (CheckStatus(vdp_st, __LINE__))
826     return;
827   m_vdpauConfig.vdpDecoder = VDP_INVALID_HANDLE;
828
829   CLog::Log(LOGDEBUG, "CVDPAU::FiniVDPAUOutput destroying %d video surfaces", m_videoSurfaces.Size());
830
831   VdpVideoSurface surf;
832   while((surf = m_videoSurfaces.RemoveNext()) != VDP_INVALID_HANDLE)
833   {
834     m_vdpauConfig.context->GetProcs().vdp_video_surface_destroy(surf);
835     if (CheckStatus(vdp_st, __LINE__))
836       return;
837   }
838   m_videoSurfaces.Reset();
839 }
840
841 void CDecoder::ReadFormatOf( AVCodecID codec
842                            , VdpDecoderProfile &vdp_decoder_profile
843                            , VdpChromaType     &vdp_chroma_type)
844 {
845   switch (codec)
846   {
847     case AV_CODEC_ID_MPEG1VIDEO:
848       vdp_decoder_profile = VDP_DECODER_PROFILE_MPEG1;
849       vdp_chroma_type     = VDP_CHROMA_TYPE_420;
850       break;
851     case AV_CODEC_ID_MPEG2VIDEO:
852       vdp_decoder_profile = VDP_DECODER_PROFILE_MPEG2_MAIN;
853       vdp_chroma_type     = VDP_CHROMA_TYPE_420;
854       break;
855     case AV_CODEC_ID_H264:
856       vdp_decoder_profile = VDP_DECODER_PROFILE_H264_HIGH;
857       vdp_chroma_type     = VDP_CHROMA_TYPE_420;
858       break;
859     case AV_CODEC_ID_WMV3:
860       vdp_decoder_profile = VDP_DECODER_PROFILE_VC1_MAIN;
861       vdp_chroma_type     = VDP_CHROMA_TYPE_420;
862       break;
863     case AV_CODEC_ID_VC1:
864       vdp_decoder_profile = VDP_DECODER_PROFILE_VC1_ADVANCED;
865       vdp_chroma_type     = VDP_CHROMA_TYPE_420;
866       break;
867     case AV_CODEC_ID_MPEG4:
868       vdp_decoder_profile = VDP_DECODER_PROFILE_MPEG4_PART2_ASP;
869       vdp_chroma_type     = VDP_CHROMA_TYPE_420;
870       break;
871     default:
872       vdp_decoder_profile = 0;
873       vdp_chroma_type     = 0;
874       break;
875   }
876 }
877
878 bool CDecoder::ConfigVDPAU(AVCodecContext* avctx, int ref_frames)
879 {
880   FiniVDPAUOutput();
881
882   VdpStatus vdp_st;
883   VdpDecoderProfile vdp_decoder_profile;
884
885   m_vdpauConfig.vidWidth = avctx->width;
886   m_vdpauConfig.vidHeight = avctx->height;
887   m_vdpauConfig.surfaceWidth = avctx->coded_width;
888   m_vdpauConfig.surfaceHeight = avctx->coded_height;
889
890   SetWidthHeight(avctx->width,avctx->height);
891
892   CLog::Log(LOGNOTICE, " (VDPAU) screenWidth:%i vidWidth:%i surfaceWidth:%i",m_vdpauConfig.outWidth,m_vdpauConfig.vidWidth,m_vdpauConfig.surfaceWidth);
893   CLog::Log(LOGNOTICE, " (VDPAU) screenHeight:%i vidHeight:%i surfaceHeight:%i",m_vdpauConfig.outHeight,m_vdpauConfig.vidHeight,m_vdpauConfig.surfaceHeight);
894
895   ReadFormatOf(avctx->codec_id, vdp_decoder_profile, m_vdpauConfig.vdpChromaType);
896
897   if(avctx->codec_id == AV_CODEC_ID_H264)
898   {
899     m_vdpauConfig.maxReferences = ref_frames;
900     if (m_vdpauConfig.maxReferences > 16) m_vdpauConfig.maxReferences = 16;
901     if (m_vdpauConfig.maxReferences < 5)  m_vdpauConfig.maxReferences = 5;
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   return true;
947 }
948
949 int CDecoder::FFGetBuffer(AVCodecContext *avctx, AVFrame *pic)
950 {
951   //CLog::Log(LOGNOTICE,"%s",__FUNCTION__);
952   CDVDVideoCodecFFmpeg* ctx        = (CDVDVideoCodecFFmpeg*)avctx->opaque;
953   CDecoder*             vdp        = (CDecoder*)ctx->GetHardware();
954
955   // while we are waiting to recover we can't do anything
956   CSingleLock lock(vdp->m_DecoderSection);
957
958   if(vdp->m_DisplayState != VDPAU_OPEN)
959   {
960     CLog::Log(LOGWARNING, "CVDPAU::FFGetBuffer - returning due to awaiting recovery");
961     return -1;
962   }
963
964   VdpVideoSurface surf = (VdpVideoSurface)(uintptr_t)pic->data[3];
965   surf = vdp->m_videoSurfaces.GetFree(surf != 0 ? surf : VDP_INVALID_HANDLE);
966
967   VdpStatus vdp_st = VDP_STATUS_ERROR;
968   if (surf == VDP_INVALID_HANDLE)
969   {
970     // create a new surface
971     VdpDecoderProfile profile;
972     ReadFormatOf(avctx->codec_id, profile, vdp->m_vdpauConfig.vdpChromaType);
973
974     vdp_st = vdp->m_vdpauConfig.context->GetProcs().vdp_video_surface_create(vdp->m_vdpauConfig.context->GetDevice(),
975                                          vdp->m_vdpauConfig.vdpChromaType,
976                                          avctx->coded_width,
977                                          avctx->coded_height,
978                                          &surf);
979     vdp->CheckStatus(vdp_st, __LINE__);
980     if (vdp_st != VDP_STATUS_OK)
981     {
982       CLog::Log(LOGERROR, "CVDPAU::FFGetBuffer - No Video surface available could be created");
983       return -1;
984     }
985     vdp->m_videoSurfaces.AddSurface(surf);
986   }
987
988   pic->data[1] = pic->data[2] = NULL;
989   pic->data[0] = (uint8_t*)(uintptr_t)surf;
990   pic->data[3] = (uint8_t*)(uintptr_t)surf;
991
992   pic->linesize[0] = pic->linesize[1] =  pic->linesize[2] = 0;
993
994   pic->type= FF_BUFFER_TYPE_USER;
995
996   pic->reordered_opaque= avctx->reordered_opaque;
997   return 0;
998 }
999
1000 void CDecoder::FFReleaseBuffer(AVCodecContext *avctx, AVFrame *pic)
1001 {
1002   CDVDVideoCodecFFmpeg* ctx        = (CDVDVideoCodecFFmpeg*)avctx->opaque;
1003   CDecoder*             vdp        = (CDecoder*)ctx->GetHardware();
1004
1005   VdpVideoSurface surf;
1006   unsigned int i;
1007
1008   CSingleLock lock(vdp->m_DecoderSection);
1009
1010   surf = (VdpVideoSurface)(uintptr_t)pic->data[3];
1011
1012   vdp->m_videoSurfaces.ClearReference(surf);
1013
1014   for(i=0; i<4; i++)
1015     pic->data[i]= NULL;
1016 }
1017
1018 VdpStatus CDecoder::Render( VdpDecoder decoder, VdpVideoSurface target,
1019                             VdpPictureInfo const *picture_info,
1020                             uint32_t bitstream_buffer_count,
1021                             VdpBitstreamBuffer const * bitstream_buffers)
1022 {
1023   return VDP_STATUS_OK;
1024 }
1025
1026 void CDecoder::FFDrawSlice(struct AVCodecContext *s,
1027                                            const AVFrame *src, int offset[4],
1028                                            int y, int type, int height)
1029 {
1030   CDVDVideoCodecFFmpeg* ctx = (CDVDVideoCodecFFmpeg*)s->opaque;
1031   CDecoder*               vdp = (CDecoder*)ctx->GetHardware();
1032
1033   // while we are waiting to recover we can't do anything
1034   CSingleLock lock(vdp->m_DecoderSection);
1035
1036   if(vdp->m_DisplayState != VDPAU_OPEN)
1037     return;
1038
1039   if(src->linesize[0] || src->linesize[1] || src->linesize[2]
1040   || offset[0] || offset[1] || offset[2])
1041   {
1042     CLog::Log(LOGERROR, "CVDPAU::FFDrawSlice - invalid linesizes or offsets provided");
1043     return;
1044   }
1045
1046   VdpStatus vdp_st;
1047   VdpVideoSurface surf = (VdpVideoSurface)(uintptr_t)src->data[3];
1048
1049   // ffmpeg vc-1 decoder does not flush, make sure the data buffer is still valid
1050   if (!vdp->m_videoSurfaces.IsValid(surf))
1051   {
1052     CLog::Log(LOGWARNING, "CVDPAU::FFDrawSlice - ignoring invalid buffer");
1053     return;
1054   }
1055
1056   uint32_t max_refs = 0;
1057   if(s->codec_id == AV_CODEC_ID_H264)
1058     max_refs = vdp->m_hwContext.info.h264.num_ref_frames;
1059
1060   if(vdp->m_vdpauConfig.vdpDecoder == VDP_INVALID_HANDLE
1061   || vdp->m_vdpauConfigured == false
1062   || vdp->m_vdpauConfig.maxReferences < max_refs)
1063   {
1064     if(!vdp->ConfigVDPAU(s, max_refs))
1065       return;
1066   }
1067
1068   uint64_t startTime = CurrentHostCounter();
1069   uint16_t decoded, processed, rend;
1070   vdp->m_bufferStats.Get(decoded, processed, rend);
1071   vdp_st = vdp->m_vdpauConfig.context->GetProcs().vdp_decoder_render(vdp->m_vdpauConfig.vdpDecoder,
1072                                    surf,
1073                                    (VdpPictureInfo const *)&(vdp->m_hwContext.info),
1074                                    vdp->m_hwContext.bitstream_buffers_used,
1075                                    vdp->m_hwContext.bitstream_buffers);
1076   vdp->CheckStatus(vdp_st, __LINE__);
1077   uint64_t diff = CurrentHostCounter() - startTime;
1078   if (diff*1000/CurrentHostFrequency() > 30)
1079     CLog::Log(LOGDEBUG, "CVDPAU::DrawSlice - VdpDecoderRender long decoding: %d ms, dec: %d, proc: %d, rend: %d", (int)((diff*1000)/CurrentHostFrequency()), decoded, processed, rend);
1080 }
1081
1082
1083 int CDecoder::Decode(AVCodecContext *avctx, AVFrame *pFrame)
1084 {
1085   int result = Check(avctx);
1086   if (result)
1087     return result;
1088
1089   CSingleLock lock(m_DecoderSection);
1090
1091   if (!m_vdpauConfigured)
1092     return VC_ERROR;
1093
1094   if(pFrame)
1095   { // we have a new frame from decoder
1096
1097     VdpVideoSurface surf = (VdpVideoSurface)(uintptr_t)pFrame->data[3];
1098     // ffmpeg vc-1 decoder does not flush, make sure the data buffer is still valid
1099     if (!m_videoSurfaces.IsValid(surf))
1100     {
1101       CLog::Log(LOGWARNING, "CVDPAU::Decode - ignoring invalid buffer");
1102       return VC_BUFFER;
1103     }
1104     m_videoSurfaces.MarkRender(surf);
1105
1106     // send frame to output for processing
1107     CVdpauDecodedPicture pic;
1108     memset(&pic.DVDPic, 0, sizeof(pic.DVDPic));
1109     ((CDVDVideoCodecFFmpeg*)avctx->opaque)->GetPictureCommon(&pic.DVDPic);
1110     pic.videoSurface = surf;
1111     pic.DVDPic.color_matrix = avctx->colorspace;
1112     m_bufferStats.IncDecoded();
1113     m_vdpauOutput.m_dataPort.SendOutMessage(COutputDataProtocol::NEWFRAME, &pic, sizeof(pic));
1114
1115     //TODO
1116     // m_codecControl = pic.DVDPic.iFlags & (DVP_FLAG_DRAIN | DVP_FLAG_NO_POSTPROC);
1117   }
1118
1119   int retval = 0;
1120   uint16_t decoded, processed, render;
1121   Message *msg;
1122   while (m_vdpauOutput.m_controlPort.ReceiveInMessage(&msg))
1123   {
1124     if (msg->signal == COutputControlProtocol::ERROR)
1125     {
1126       m_DisplayState = VDPAU_ERROR;
1127       retval |= VC_ERROR;
1128     }
1129     msg->Release();
1130   }
1131
1132   m_bufferStats.Get(decoded, processed, render);
1133
1134   uint64_t startTime = CurrentHostCounter();
1135   while (!retval)
1136   {
1137     // first fill the buffers to keep vdpau busy
1138     // mixer will run with decoded >= 2. output is limited by number of output surfaces
1139     // In case mixer is bypassed we limit by looking at processed
1140     if (decoded < 3 && processed < 3)
1141     {
1142       retval |= VC_BUFFER;
1143     }
1144     else if (m_vdpauOutput.m_dataPort.ReceiveInMessage(&msg))
1145     {
1146       if (msg->signal == COutputDataProtocol::PICTURE)
1147       {
1148         if (m_presentPicture)
1149         {
1150           m_presentPicture->ReturnUnused();
1151           m_presentPicture = 0;
1152         }
1153
1154         m_presentPicture = *(CVdpauRenderPicture**)msg->data;
1155         m_presentPicture->vdpau = this;
1156         m_bufferStats.DecRender();
1157         m_bufferStats.Get(decoded, processed, render);
1158         retval |= VC_PICTURE;
1159         msg->Release();
1160         break;
1161       }
1162       msg->Release();
1163     }
1164     else if (m_vdpauOutput.m_controlPort.ReceiveInMessage(&msg))
1165     {
1166       if (msg->signal == COutputControlProtocol::STATS)
1167       {
1168         m_bufferStats.Get(decoded, processed, render);
1169       }
1170       else
1171       {
1172         m_DisplayState = VDPAU_ERROR;
1173         retval |= VC_ERROR;
1174       }
1175       msg->Release();
1176     }
1177
1178     if (decoded < 3 && processed < 3)
1179     {
1180       retval |= VC_BUFFER;
1181     }
1182
1183     if (!retval && !m_inMsgEvent.WaitMSec(2000))
1184       break;
1185   }
1186   uint64_t diff = CurrentHostCounter() - startTime;
1187   if (retval & VC_PICTURE)
1188   {
1189     m_bufferStats.SetParams(diff, m_codecControl);
1190   }
1191   if (diff*1000/CurrentHostFrequency() > 50)
1192     CLog::Log(LOGDEBUG,"CVDPAU::Decode long wait: %d", (int)((diff*1000)/CurrentHostFrequency()));
1193
1194   if (!retval)
1195   {
1196     CLog::Log(LOGERROR, "VDPAU::%s - timed out waiting for output message", __FUNCTION__);
1197     m_DisplayState = VDPAU_ERROR;
1198     retval |= VC_ERROR;
1199   }
1200
1201   return retval;
1202 }
1203
1204 bool CDecoder::GetPicture(AVCodecContext* avctx, AVFrame* frame, DVDVideoPicture* picture)
1205 {
1206   CSingleLock lock(m_DecoderSection);
1207
1208   if (m_DisplayState != VDPAU_OPEN)
1209     return false;
1210
1211   *picture = m_presentPicture->DVDPic;
1212   picture->vdpau = m_presentPicture;
1213
1214   return true;
1215 }
1216
1217 void CDecoder::Reset()
1218 {
1219   CSingleLock lock(m_DecoderSection);
1220
1221   if (!m_vdpauConfigured)
1222     return;
1223
1224   Message *reply;
1225   if (m_vdpauOutput.m_controlPort.SendOutMessageSync(COutputControlProtocol::FLUSH,
1226                                                  &reply,
1227                                                  2000))
1228   {
1229     bool success = reply->signal == COutputControlProtocol::ACC ? true : false;
1230     reply->Release();
1231     if (!success)
1232     {
1233       CLog::Log(LOGERROR, "VDPAU::%s - flush returned error", __FUNCTION__);
1234       m_DisplayState = VDPAU_ERROR;
1235     }
1236     else
1237       m_bufferStats.Reset();
1238   }
1239   else
1240   {
1241     CLog::Log(LOGERROR, "VDPAU::%s - flush timed out", __FUNCTION__);
1242     m_DisplayState = VDPAU_ERROR;
1243   }
1244 }
1245
1246 bool CDecoder::CanSkipDeint()
1247 {
1248   return m_bufferStats.CanSkipDeint();
1249 }
1250
1251 void CDecoder::ReturnRenderPicture(CVdpauRenderPicture *renderPic)
1252 {
1253   m_vdpauOutput.m_dataPort.SendOutMessage(COutputDataProtocol::RETURNPIC, &renderPic, sizeof(renderPic));
1254 }
1255
1256 bool CDecoder::CheckStatus(VdpStatus vdp_st, int line)
1257 {
1258   if (vdp_st != VDP_STATUS_OK)
1259   {
1260     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);
1261
1262     if(m_DisplayState == VDPAU_OPEN)
1263     {
1264       if (vdp_st == VDP_STATUS_DISPLAY_PREEMPTED)
1265       {
1266         m_DisplayEvent.Reset();
1267         m_DisplayState = VDPAU_LOST;
1268       }
1269       else
1270         m_DisplayState = VDPAU_ERROR;
1271     }
1272
1273     return true;
1274   }
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 = VDP_STATUS_ERROR;
1704   vdp_st = m_config.context->GetProcs().vdp_video_mixer_create(m_config.context->GetDevice(),
1705                                 m_config.context->GetFeatureCount(),
1706                                 m_config.context->GetFeatures(),
1707                                 ARSIZE(parameters),
1708                                 parameters,
1709                                 parameter_values,
1710                                 &m_videoMixer);
1711   CheckStatus(vdp_st, __LINE__);
1712
1713 }
1714
1715 void CMixer::InitCSCMatrix(int Width)
1716 {
1717   m_Procamp.struct_version = VDP_PROCAMP_VERSION;
1718   m_Procamp.brightness     = 0.0;
1719   m_Procamp.contrast       = 1.0;
1720   m_Procamp.saturation     = 1.0;
1721   m_Procamp.hue            = 0;
1722 }
1723
1724 void CMixer::CheckFeatures()
1725 {
1726   if (m_Upscale != m_config.upscale)
1727   {
1728     SetHWUpscaling();
1729     m_Upscale = m_config.upscale;
1730   }
1731   if (m_Brightness != CMediaSettings::Get().GetCurrentVideoSettings().m_Brightness ||
1732       m_Contrast   != CMediaSettings::Get().GetCurrentVideoSettings().m_Contrast ||
1733       m_ColorMatrix != m_mixerInput[1].DVDPic.color_matrix)
1734   {
1735     SetColor();
1736     m_Brightness = CMediaSettings::Get().GetCurrentVideoSettings().m_Brightness;
1737     m_Contrast = CMediaSettings::Get().GetCurrentVideoSettings().m_Contrast;
1738     m_ColorMatrix = m_mixerInput[1].DVDPic.color_matrix;
1739   }
1740   if (m_NoiseReduction != CMediaSettings::Get().GetCurrentVideoSettings().m_NoiseReduction)
1741   {
1742     m_NoiseReduction = CMediaSettings::Get().GetCurrentVideoSettings().m_NoiseReduction;
1743     SetNoiseReduction();
1744   }
1745   if (m_Sharpness != CMediaSettings::Get().GetCurrentVideoSettings().m_Sharpness)
1746   {
1747     m_Sharpness = CMediaSettings::Get().GetCurrentVideoSettings().m_Sharpness;
1748     SetSharpness();
1749   }
1750   if (m_DeintMode != CMediaSettings::Get().GetCurrentVideoSettings().m_DeinterlaceMode ||
1751       m_Deint     != CMediaSettings::Get().GetCurrentVideoSettings().m_InterlaceMethod)
1752   {
1753     m_DeintMode = CMediaSettings::Get().GetCurrentVideoSettings().m_DeinterlaceMode;
1754     m_Deint     = CMediaSettings::Get().GetCurrentVideoSettings().m_InterlaceMethod;
1755     SetDeinterlacing();
1756   }
1757 }
1758
1759 void CMixer::SetPostProcFeatures(bool postProcEnabled)
1760 {
1761   if (m_PostProc != postProcEnabled)
1762   {
1763     if (postProcEnabled)
1764     {
1765       SetNoiseReduction();
1766       SetSharpness();
1767       SetDeinterlacing();
1768       SetHWUpscaling();
1769     }
1770     else
1771       PostProcOff();
1772     m_PostProc = postProcEnabled;
1773   }
1774 }
1775
1776 void CMixer::PostProcOff()
1777 {
1778   VdpStatus vdp_st;
1779
1780   if (m_videoMixer == VDP_INVALID_HANDLE)
1781     return;
1782
1783   VdpVideoMixerFeature feature[] = { VDP_VIDEO_MIXER_FEATURE_DEINTERLACE_TEMPORAL,
1784                                      VDP_VIDEO_MIXER_FEATURE_DEINTERLACE_TEMPORAL_SPATIAL,
1785                                      VDP_VIDEO_MIXER_FEATURE_INVERSE_TELECINE};
1786
1787   VdpBool enabled[]={0,0,0};
1788   vdp_st = m_config.context->GetProcs().vdp_video_mixer_set_feature_enables(m_videoMixer, ARSIZE(feature), feature, enabled);
1789   CheckStatus(vdp_st, __LINE__);
1790
1791   if(m_config.vdpau->Supports(VDP_VIDEO_MIXER_FEATURE_NOISE_REDUCTION))
1792   {
1793     VdpVideoMixerFeature feature[] = { VDP_VIDEO_MIXER_FEATURE_NOISE_REDUCTION};
1794
1795     VdpBool enabled[]={0};
1796     vdp_st = m_config.context->GetProcs().vdp_video_mixer_set_feature_enables(m_videoMixer, ARSIZE(feature), feature, enabled);
1797     CheckStatus(vdp_st, __LINE__);
1798   }
1799
1800   if(m_config.vdpau->Supports(VDP_VIDEO_MIXER_FEATURE_SHARPNESS))
1801   {
1802     VdpVideoMixerFeature feature[] = { VDP_VIDEO_MIXER_FEATURE_SHARPNESS};
1803
1804     VdpBool enabled[]={0};
1805     vdp_st = m_config.context->GetProcs().vdp_video_mixer_set_feature_enables(m_videoMixer, ARSIZE(feature), feature, enabled);
1806     CheckStatus(vdp_st, __LINE__);
1807   }
1808
1809   DisableHQScaling();
1810 }
1811
1812 bool CMixer::GenerateStudioCSCMatrix(VdpColorStandard colorStandard, VdpCSCMatrix &studioCSCMatrix)
1813 {
1814    // instead use studioCSCKCoeffs601[3], studioCSCKCoeffs709[3] to generate float[3][4] matrix (float studioCSC[3][4])
1815    // m00 = mRY = red: luma factor (contrast factor) (1.0)
1816    // m10 = mGY = green: luma factor (contrast factor) (1.0)
1817    // m20 = mBY = blue: luma factor (contrast factor) (1.0)
1818    //
1819    // m01 = mRB = red: blue color diff coeff (0.0)
1820    // m11 = mGB = green: blue color diff coeff (-2Kb(1-Kb)/(Kg))
1821    // m21 = mBB = blue: blue color diff coeff ((1-Kb)/0.5)
1822    //
1823    // m02 = mRR = red: red color diff coeff ((1-Kr)/0.5)
1824    // m12 = mGR = green: red color diff coeff (-2Kr(1-Kr)/(Kg))
1825    // m22 = mBR = blue: red color diff coeff (0.0)
1826    //
1827    // m03 = mRC = red: colour zero offset (brightness factor) (-(1-Kr)/0.5 * (128/255))
1828    // m13 = mGC = green: colour zero offset (brightness factor) ((256/255) * (Kb(1-Kb) + Kr(1-Kr)) / Kg)
1829    // m23 = mBC = blue: colour zero offset (brightness factor) (-(1-Kb)/0.5 * (128/255))
1830
1831    // columns
1832    int Y = 0;
1833    int Cb = 1;
1834    int Cr = 2;
1835    int C = 3;
1836    // rows
1837    int R = 0;
1838    int G = 1;
1839    int B = 2;
1840    // colour standard coefficients for red, geen, blue
1841    double Kr, Kg, Kb;
1842    // colour diff zero position (use standard 8-bit coding precision)
1843    double CDZ = 128; //256*0.5
1844    // range excursion (use standard 8-bit coding precision)
1845    double EXC = 255; //256-1
1846
1847    if (colorStandard == VDP_COLOR_STANDARD_ITUR_BT_601)
1848    {
1849       Kr = studioCSCKCoeffs601[0];
1850       Kg = studioCSCKCoeffs601[1];
1851       Kb = studioCSCKCoeffs601[2];
1852    }
1853    else // assume VDP_COLOR_STANDARD_ITUR_BT_709
1854    {
1855       Kr = studioCSCKCoeffs709[0];
1856       Kg = studioCSCKCoeffs709[1];
1857       Kb = studioCSCKCoeffs709[2];
1858    }
1859    // we keep luma unscaled to retain the levels present in source so that 16-235 luma is converted to RGB 16-235
1860    studioCSCMatrix[R][Y] = 1.0;
1861    studioCSCMatrix[G][Y] = 1.0;
1862    studioCSCMatrix[B][Y] = 1.0;
1863
1864    studioCSCMatrix[R][Cb] = 0.0;
1865    studioCSCMatrix[G][Cb] = (double)-2 * Kb * (1 - Kb) / Kg;
1866    studioCSCMatrix[B][Cb] = (double)(1 - Kb) / 0.5;
1867
1868    studioCSCMatrix[R][Cr] = (double)(1 - Kr) / 0.5;
1869    studioCSCMatrix[G][Cr] = (double)-2 * Kr * (1 - Kr) / Kg;
1870    studioCSCMatrix[B][Cr] = 0.0;
1871
1872    studioCSCMatrix[R][C] = (double)-1 * studioCSCMatrix[R][Cr] * CDZ/EXC;
1873    studioCSCMatrix[G][C] = (double)-1 * (studioCSCMatrix[G][Cb] + studioCSCMatrix[G][Cr]) * CDZ/EXC;
1874    studioCSCMatrix[B][C] = (double)-1 * studioCSCMatrix[B][Cb] * CDZ/EXC;
1875
1876    return true;
1877 }
1878
1879 void CMixer::SetColor()
1880 {
1881   VdpStatus vdp_st;
1882
1883   if (m_Brightness != CMediaSettings::Get().GetCurrentVideoSettings().m_Brightness)
1884     m_Procamp.brightness = (float)((CMediaSettings::Get().GetCurrentVideoSettings().m_Brightness)-50) / 100;
1885   if (m_Contrast != CMediaSettings::Get().GetCurrentVideoSettings().m_Contrast)
1886     m_Procamp.contrast = (float)((CMediaSettings::Get().GetCurrentVideoSettings().m_Contrast)+50) / 100;
1887
1888   VdpColorStandard colorStandard;
1889   switch(m_mixerInput[1].DVDPic.color_matrix)
1890   {
1891     case AVCOL_SPC_BT709:
1892       colorStandard = VDP_COLOR_STANDARD_ITUR_BT_709;
1893       break;
1894     case AVCOL_SPC_BT470BG:
1895     case AVCOL_SPC_SMPTE170M:
1896       colorStandard = VDP_COLOR_STANDARD_ITUR_BT_601;
1897       break;
1898     case AVCOL_SPC_SMPTE240M:
1899       colorStandard = VDP_COLOR_STANDARD_SMPTE_240M;
1900       break;
1901     case AVCOL_SPC_FCC:
1902     case AVCOL_SPC_UNSPECIFIED:
1903     case AVCOL_SPC_RGB:
1904     default:
1905       if(m_config.surfaceWidth > 1000)
1906         colorStandard = VDP_COLOR_STANDARD_ITUR_BT_709;
1907       else
1908         colorStandard = VDP_COLOR_STANDARD_ITUR_BT_601;
1909   }
1910
1911   VdpVideoMixerAttribute attributes[] = { VDP_VIDEO_MIXER_ATTRIBUTE_CSC_MATRIX };
1912   if (CSettings::Get().GetBool("videoscreen.limitedrange"))
1913   {
1914     float studioCSC[3][4];
1915     GenerateStudioCSCMatrix(colorStandard, studioCSC);
1916     void const * pm_CSCMatix[] = { &studioCSC };
1917     vdp_st = m_config.context->GetProcs().vdp_video_mixer_set_attribute_values(m_videoMixer, ARSIZE(attributes), attributes, pm_CSCMatix);
1918   }
1919   else
1920   {
1921     vdp_st = m_config.context->GetProcs().vdp_generate_csc_matrix(&m_Procamp, colorStandard, &m_CSCMatrix);
1922     void const * pm_CSCMatix[] = { &m_CSCMatrix };
1923     vdp_st = m_config.context->GetProcs().vdp_video_mixer_set_attribute_values(m_videoMixer, ARSIZE(attributes), attributes, pm_CSCMatix);
1924   }
1925
1926   CheckStatus(vdp_st, __LINE__);
1927 }
1928
1929 void CMixer::SetNoiseReduction()
1930 {
1931   if(!m_config.vdpau->Supports(VDP_VIDEO_MIXER_FEATURE_NOISE_REDUCTION))
1932     return;
1933
1934   VdpVideoMixerFeature feature[] = { VDP_VIDEO_MIXER_FEATURE_NOISE_REDUCTION };
1935   VdpVideoMixerAttribute attributes[] = { VDP_VIDEO_MIXER_ATTRIBUTE_NOISE_REDUCTION_LEVEL };
1936   VdpStatus vdp_st;
1937
1938   if (!CMediaSettings::Get().GetCurrentVideoSettings().m_NoiseReduction)
1939   {
1940     VdpBool enabled[]= {0};
1941     vdp_st = m_config.context->GetProcs().vdp_video_mixer_set_feature_enables(m_videoMixer, ARSIZE(feature), feature, enabled);
1942     CheckStatus(vdp_st, __LINE__);
1943     return;
1944   }
1945   VdpBool enabled[]={1};
1946   vdp_st = m_config.context->GetProcs().vdp_video_mixer_set_feature_enables(m_videoMixer, ARSIZE(feature), feature, enabled);
1947   CheckStatus(vdp_st, __LINE__);
1948   void* nr[] = { &CMediaSettings::Get().GetCurrentVideoSettings().m_NoiseReduction };
1949   CLog::Log(LOGNOTICE,"Setting Noise Reduction to %f",CMediaSettings::Get().GetCurrentVideoSettings().m_NoiseReduction);
1950   vdp_st = m_config.context->GetProcs().vdp_video_mixer_set_attribute_values(m_videoMixer, ARSIZE(attributes), attributes, nr);
1951   CheckStatus(vdp_st, __LINE__);
1952 }
1953
1954 void CMixer::SetSharpness()
1955 {
1956   if(!m_config.vdpau->Supports(VDP_VIDEO_MIXER_FEATURE_SHARPNESS))
1957     return;
1958
1959   VdpVideoMixerFeature feature[] = { VDP_VIDEO_MIXER_FEATURE_SHARPNESS };
1960   VdpVideoMixerAttribute attributes[] = { VDP_VIDEO_MIXER_ATTRIBUTE_SHARPNESS_LEVEL };
1961   VdpStatus vdp_st;
1962
1963   if (!CMediaSettings::Get().GetCurrentVideoSettings().m_Sharpness)
1964   {
1965     VdpBool enabled[]={0};
1966     vdp_st = m_config.context->GetProcs().vdp_video_mixer_set_feature_enables(m_videoMixer, ARSIZE(feature), feature, enabled);
1967     CheckStatus(vdp_st, __LINE__);
1968     return;
1969   }
1970   VdpBool enabled[]={1};
1971   vdp_st = m_config.context->GetProcs().vdp_video_mixer_set_feature_enables(m_videoMixer, ARSIZE(feature), feature, enabled);
1972   CheckStatus(vdp_st, __LINE__);
1973   void* sh[] = { &CMediaSettings::Get().GetCurrentVideoSettings().m_Sharpness };
1974   CLog::Log(LOGNOTICE,"Setting Sharpness to %f",CMediaSettings::Get().GetCurrentVideoSettings().m_Sharpness);
1975   vdp_st = m_config.context->GetProcs().vdp_video_mixer_set_attribute_values(m_videoMixer, ARSIZE(attributes), attributes, sh);
1976   CheckStatus(vdp_st, __LINE__);
1977 }
1978
1979 EINTERLACEMETHOD CMixer::GetDeinterlacingMethod(bool log /* = false */)
1980 {
1981   EINTERLACEMETHOD method = CMediaSettings::Get().GetCurrentVideoSettings().m_InterlaceMethod;
1982   if (method == VS_INTERLACEMETHOD_AUTO)
1983   {
1984     int deint = -1;
1985 //    if (m_config.outHeight >= 720)
1986 //      deint = g_advancedSettings.m_videoVDPAUdeintHD;
1987 //    else
1988 //      deint = g_advancedSettings.m_videoVDPAUdeintSD;
1989
1990     if (deint != -1)
1991     {
1992       if (m_config.vdpau->Supports(EINTERLACEMETHOD(deint)))
1993       {
1994         method = EINTERLACEMETHOD(deint);
1995         if (log)
1996           CLog::Log(LOGNOTICE, "CVDPAU::GetDeinterlacingMethod: set de-interlacing to %d",  deint);
1997       }
1998       else
1999       {
2000         if (log)
2001           CLog::Log(LOGWARNING, "CVDPAU::GetDeinterlacingMethod: method for de-interlacing (advanced settings) not supported");
2002       }
2003     }
2004   }
2005   return method;
2006 }
2007
2008 void CMixer::SetDeinterlacing()
2009 {
2010   VdpStatus vdp_st;
2011
2012   if (m_videoMixer == VDP_INVALID_HANDLE)
2013     return;
2014
2015   EDEINTERLACEMODE   mode = CMediaSettings::Get().GetCurrentVideoSettings().m_DeinterlaceMode;
2016   EINTERLACEMETHOD method = GetDeinterlacingMethod(true);
2017
2018   VdpVideoMixerFeature feature[] = { VDP_VIDEO_MIXER_FEATURE_DEINTERLACE_TEMPORAL,
2019                                      VDP_VIDEO_MIXER_FEATURE_DEINTERLACE_TEMPORAL_SPATIAL,
2020                                      VDP_VIDEO_MIXER_FEATURE_INVERSE_TELECINE };
2021
2022   if (mode == VS_DEINTERLACEMODE_OFF)
2023   {
2024     VdpBool enabled[] = {0,0,0};
2025     vdp_st = m_config.context->GetProcs().vdp_video_mixer_set_feature_enables(m_videoMixer, ARSIZE(feature), feature, enabled);
2026   }
2027   else
2028   {
2029     if (method == VS_INTERLACEMETHOD_AUTO)
2030     {
2031       VdpBool enabled[] = {1,0,0};
2032       if (g_advancedSettings.m_videoVDPAUtelecine)
2033         enabled[2] = 1;
2034       vdp_st = m_config.context->GetProcs().vdp_video_mixer_set_feature_enables(m_videoMixer, ARSIZE(feature), feature, enabled);
2035     }
2036     else if (method == VS_INTERLACEMETHOD_VDPAU_TEMPORAL
2037          ||  method == VS_INTERLACEMETHOD_VDPAU_TEMPORAL_HALF)
2038     {
2039       VdpBool enabled[] = {1,0,0};
2040       if (g_advancedSettings.m_videoVDPAUtelecine)
2041         enabled[2] = 1;
2042       vdp_st = m_config.context->GetProcs().vdp_video_mixer_set_feature_enables(m_videoMixer, ARSIZE(feature), feature, enabled);
2043     }
2044     else if (method == VS_INTERLACEMETHOD_VDPAU_TEMPORAL_SPATIAL
2045          ||  method == VS_INTERLACEMETHOD_VDPAU_TEMPORAL_SPATIAL_HALF)
2046     {
2047       VdpBool enabled[] = {1,1,0};
2048       if (g_advancedSettings.m_videoVDPAUtelecine)
2049         enabled[2] = 1;
2050       vdp_st = m_config.context->GetProcs().vdp_video_mixer_set_feature_enables(m_videoMixer, ARSIZE(feature), feature, enabled);
2051     }
2052     else
2053     {
2054       VdpBool enabled[]={0,0,0};
2055       vdp_st = m_config.context->GetProcs().vdp_video_mixer_set_feature_enables(m_videoMixer, ARSIZE(feature), feature, enabled);
2056     }
2057   }
2058   CheckStatus(vdp_st, __LINE__);
2059
2060   SetDeintSkipChroma();
2061
2062   m_config.useInteropYuv = !CSettings::Get().GetBool("videoplayer.usevdpaumixer");
2063 }
2064
2065 void CMixer::SetDeintSkipChroma()
2066 {
2067   VdpVideoMixerAttribute attribute[] = { VDP_VIDEO_MIXER_ATTRIBUTE_SKIP_CHROMA_DEINTERLACE};
2068   VdpStatus vdp_st;
2069
2070   uint8_t val;
2071   if (g_advancedSettings.m_videoVDPAUdeintSkipChromaHD && m_config.outHeight >= 720)
2072     val = 1;
2073   else
2074     val = 0;
2075
2076   void const *values[]={&val};
2077   vdp_st = m_config.context->GetProcs().vdp_video_mixer_set_attribute_values(m_videoMixer, ARSIZE(attribute), attribute, values);
2078
2079   CheckStatus(vdp_st, __LINE__);
2080 }
2081
2082 void CMixer::SetHWUpscaling()
2083 {
2084 #ifdef VDP_VIDEO_MIXER_FEATURE_HIGH_QUALITY_SCALING_L1
2085
2086   VdpStatus vdp_st;
2087   VdpBool enabled[]={1};
2088   switch (m_config.upscale)
2089   {
2090     case 9:
2091        if (m_config.vdpau->Supports(VDP_VIDEO_MIXER_FEATURE_HIGH_QUALITY_SCALING_L9))
2092        {
2093           VdpVideoMixerFeature feature[] = { VDP_VIDEO_MIXER_FEATURE_HIGH_QUALITY_SCALING_L9 };
2094           vdp_st = m_config.context->GetProcs().vdp_video_mixer_set_feature_enables(m_videoMixer, ARSIZE(feature), feature, enabled);
2095           break;
2096        }
2097     case 8:
2098        if (m_config.vdpau->Supports(VDP_VIDEO_MIXER_FEATURE_HIGH_QUALITY_SCALING_L8))
2099        {
2100           VdpVideoMixerFeature feature[] = { VDP_VIDEO_MIXER_FEATURE_HIGH_QUALITY_SCALING_L8 };
2101           vdp_st = m_config.context->GetProcs().vdp_video_mixer_set_feature_enables(m_videoMixer, ARSIZE(feature), feature, enabled);
2102           break;
2103        }
2104     case 7:
2105        if (m_config.vdpau->Supports(VDP_VIDEO_MIXER_FEATURE_HIGH_QUALITY_SCALING_L7))
2106        {
2107           VdpVideoMixerFeature feature[] = { VDP_VIDEO_MIXER_FEATURE_HIGH_QUALITY_SCALING_L7 };
2108           vdp_st = m_config.context->GetProcs().vdp_video_mixer_set_feature_enables(m_videoMixer, ARSIZE(feature), feature, enabled);
2109           break;
2110        }
2111     case 6:
2112        if (m_config.vdpau->Supports(VDP_VIDEO_MIXER_FEATURE_HIGH_QUALITY_SCALING_L6))
2113        {
2114           VdpVideoMixerFeature feature[] = { VDP_VIDEO_MIXER_FEATURE_HIGH_QUALITY_SCALING_L6 };
2115           vdp_st = m_config.context->GetProcs().vdp_video_mixer_set_feature_enables(m_videoMixer, ARSIZE(feature), feature, enabled);
2116           break;
2117        }
2118     case 5:
2119        if (m_config.vdpau->Supports(VDP_VIDEO_MIXER_FEATURE_HIGH_QUALITY_SCALING_L5))
2120        {
2121           VdpVideoMixerFeature feature[] = { VDP_VIDEO_MIXER_FEATURE_HIGH_QUALITY_SCALING_L5 };
2122           vdp_st = m_config.context->GetProcs().vdp_video_mixer_set_feature_enables(m_videoMixer, ARSIZE(feature), feature, enabled);
2123           break;
2124        }
2125     case 4:
2126        if (m_config.vdpau->Supports(VDP_VIDEO_MIXER_FEATURE_HIGH_QUALITY_SCALING_L4))
2127        {
2128           VdpVideoMixerFeature feature[] = { VDP_VIDEO_MIXER_FEATURE_HIGH_QUALITY_SCALING_L4 };
2129           vdp_st = m_config.context->GetProcs().vdp_video_mixer_set_feature_enables(m_videoMixer, ARSIZE(feature), feature, enabled);
2130           break;
2131        }
2132     case 3:
2133        if (m_config.vdpau->Supports(VDP_VIDEO_MIXER_FEATURE_HIGH_QUALITY_SCALING_L3))
2134        {
2135           VdpVideoMixerFeature feature[] = { VDP_VIDEO_MIXER_FEATURE_HIGH_QUALITY_SCALING_L3 };
2136           vdp_st = m_config.context->GetProcs().vdp_video_mixer_set_feature_enables(m_videoMixer, ARSIZE(feature), feature, enabled);
2137           break;
2138        }
2139     case 2:
2140        if (m_config.vdpau->Supports(VDP_VIDEO_MIXER_FEATURE_HIGH_QUALITY_SCALING_L2))
2141        {
2142           VdpVideoMixerFeature feature[] = { VDP_VIDEO_MIXER_FEATURE_HIGH_QUALITY_SCALING_L2 };
2143           vdp_st = m_config.context->GetProcs().vdp_video_mixer_set_feature_enables(m_videoMixer, ARSIZE(feature), feature, enabled);
2144           break;
2145        }
2146     case 1:
2147        if (m_config.vdpau->Supports(VDP_VIDEO_MIXER_FEATURE_HIGH_QUALITY_SCALING_L1))
2148        {
2149           VdpVideoMixerFeature feature[] = { VDP_VIDEO_MIXER_FEATURE_HIGH_QUALITY_SCALING_L1 };
2150           vdp_st = m_config.context->GetProcs().vdp_video_mixer_set_feature_enables(m_videoMixer, ARSIZE(feature), feature, enabled);
2151           break;
2152        }
2153     default:
2154        DisableHQScaling();
2155        return;
2156   }
2157   CheckStatus(vdp_st, __LINE__);
2158 #endif
2159 }
2160
2161 void CMixer::DisableHQScaling()
2162 {
2163   VdpStatus vdp_st;
2164
2165   if (m_videoMixer == VDP_INVALID_HANDLE)
2166     return;
2167
2168   if(m_config.vdpau->Supports(VDP_VIDEO_MIXER_FEATURE_HIGH_QUALITY_SCALING_L1))
2169   {
2170     VdpVideoMixerFeature feature[] = { VDP_VIDEO_MIXER_FEATURE_HIGH_QUALITY_SCALING_L1 };
2171     VdpBool enabled[]={0};
2172     vdp_st = m_config.context->GetProcs().vdp_video_mixer_set_feature_enables(m_videoMixer, ARSIZE(feature), feature, enabled);
2173     CheckStatus(vdp_st, __LINE__);
2174   }
2175
2176   if(m_config.vdpau->Supports(VDP_VIDEO_MIXER_FEATURE_HIGH_QUALITY_SCALING_L2))
2177   {
2178     VdpVideoMixerFeature feature[] = { VDP_VIDEO_MIXER_FEATURE_HIGH_QUALITY_SCALING_L2 };
2179     VdpBool enabled[]={0};
2180     vdp_st = m_config.context->GetProcs().vdp_video_mixer_set_feature_enables(m_videoMixer, ARSIZE(feature), feature, enabled);
2181     CheckStatus(vdp_st, __LINE__);
2182   }
2183
2184   if(m_config.vdpau->Supports(VDP_VIDEO_MIXER_FEATURE_HIGH_QUALITY_SCALING_L3))
2185   {
2186     VdpVideoMixerFeature feature[] = { VDP_VIDEO_MIXER_FEATURE_HIGH_QUALITY_SCALING_L3 };
2187     VdpBool enabled[]={0};
2188     vdp_st = m_config.context->GetProcs().vdp_video_mixer_set_feature_enables(m_videoMixer, ARSIZE(feature), feature, enabled);
2189     CheckStatus(vdp_st, __LINE__);
2190   }
2191
2192   if(m_config.vdpau->Supports(VDP_VIDEO_MIXER_FEATURE_HIGH_QUALITY_SCALING_L4))
2193   {
2194     VdpVideoMixerFeature feature[] = { VDP_VIDEO_MIXER_FEATURE_HIGH_QUALITY_SCALING_L4 };
2195     VdpBool enabled[]={0};
2196     vdp_st = m_config.context->GetProcs().vdp_video_mixer_set_feature_enables(m_videoMixer, ARSIZE(feature), feature, enabled);
2197     CheckStatus(vdp_st, __LINE__);
2198   }
2199
2200   if(m_config.vdpau->Supports(VDP_VIDEO_MIXER_FEATURE_HIGH_QUALITY_SCALING_L5))
2201   {
2202     VdpVideoMixerFeature feature[] = { VDP_VIDEO_MIXER_FEATURE_HIGH_QUALITY_SCALING_L5 };
2203     VdpBool enabled[]={0};
2204     vdp_st = m_config.context->GetProcs().vdp_video_mixer_set_feature_enables(m_videoMixer, ARSIZE(feature), feature, enabled);
2205     CheckStatus(vdp_st, __LINE__);
2206   }
2207
2208   if(m_config.vdpau->Supports(VDP_VIDEO_MIXER_FEATURE_HIGH_QUALITY_SCALING_L6))
2209   {
2210     VdpVideoMixerFeature feature[] = { VDP_VIDEO_MIXER_FEATURE_HIGH_QUALITY_SCALING_L6 };
2211     VdpBool enabled[]={0};
2212     vdp_st = m_config.context->GetProcs().vdp_video_mixer_set_feature_enables(m_videoMixer, ARSIZE(feature), feature, enabled);
2213     CheckStatus(vdp_st, __LINE__);
2214   }
2215
2216   if(m_config.vdpau->Supports(VDP_VIDEO_MIXER_FEATURE_HIGH_QUALITY_SCALING_L7))
2217   {
2218     VdpVideoMixerFeature feature[] = { VDP_VIDEO_MIXER_FEATURE_HIGH_QUALITY_SCALING_L7 };
2219     VdpBool enabled[]={0};
2220     vdp_st = m_config.context->GetProcs().vdp_video_mixer_set_feature_enables(m_videoMixer, ARSIZE(feature), feature, enabled);
2221     CheckStatus(vdp_st, __LINE__);
2222   }
2223
2224   if(m_config.vdpau->Supports(VDP_VIDEO_MIXER_FEATURE_HIGH_QUALITY_SCALING_L8))
2225   {
2226     VdpVideoMixerFeature feature[] = { VDP_VIDEO_MIXER_FEATURE_HIGH_QUALITY_SCALING_L8 };
2227     VdpBool enabled[]={0};
2228     vdp_st = m_config.context->GetProcs().vdp_video_mixer_set_feature_enables(m_videoMixer, ARSIZE(feature), feature, enabled);
2229     CheckStatus(vdp_st, __LINE__);
2230   }
2231
2232   if(m_config.vdpau->Supports(VDP_VIDEO_MIXER_FEATURE_HIGH_QUALITY_SCALING_L9))
2233   {
2234     VdpVideoMixerFeature feature[] = { VDP_VIDEO_MIXER_FEATURE_HIGH_QUALITY_SCALING_L9 };
2235     VdpBool enabled[]={0};
2236     vdp_st = m_config.context->GetProcs().vdp_video_mixer_set_feature_enables(m_videoMixer, ARSIZE(feature), feature, enabled);
2237     CheckStatus(vdp_st, __LINE__);
2238   }
2239 }
2240
2241 void CMixer::Init()
2242 {
2243   m_Brightness = 0.0;
2244   m_Contrast = 0.0;
2245   m_NoiseReduction = 0.0;
2246   m_Sharpness = 0.0;
2247   m_DeintMode = 0;
2248   m_Deint = 0;
2249   m_Upscale = 0;
2250   m_SeenInterlaceFlag = false;
2251   m_ColorMatrix = 0;
2252   m_PostProc = false;
2253   m_vdpError = false;
2254
2255   m_config.upscale = g_advancedSettings.m_videoVDPAUScaling;
2256   m_config.useInteropYuv = !CSettings::Get().GetBool("videoplayer.usevdpaumixer");
2257
2258   CreateVdpauMixer();
2259 }
2260
2261 void CMixer::Uninit()
2262 {
2263   Flush();
2264   while (!m_outputSurfaces.empty())
2265   {
2266     m_outputSurfaces.pop();
2267   }
2268   m_config.context->GetProcs().vdp_video_mixer_destroy(m_videoMixer);
2269 }
2270
2271 void CMixer::Flush()
2272 {
2273   while (!m_mixerInput.empty())
2274   {
2275     CVdpauDecodedPicture pic = m_mixerInput.back();
2276     m_mixerInput.pop_back();
2277     m_config.videoSurfaces->ClearRender(pic.videoSurface);
2278   }
2279   while (!m_decodedPics.empty())
2280   {
2281     CVdpauDecodedPicture pic = m_decodedPics.front();
2282     m_decodedPics.pop();
2283     m_config.videoSurfaces->ClearRender(pic.videoSurface);
2284   }
2285   Message *msg;
2286   while (m_dataPort.ReceiveOutMessage(&msg))
2287   {
2288     if (msg->signal == CMixerDataProtocol::FRAME)
2289     {
2290       CVdpauDecodedPicture pic = *(CVdpauDecodedPicture*)msg->data;
2291       m_config.videoSurfaces->ClearRender(pic.videoSurface);
2292     }
2293     else if (msg->signal == CMixerDataProtocol::BUFFER)
2294     {
2295       VdpOutputSurface *surf;
2296       surf = (VdpOutputSurface*)msg->data;
2297       m_outputSurfaces.push(*surf);
2298     }
2299     msg->Release();
2300   }
2301 }
2302
2303 void CMixer::InitCycle()
2304 {
2305   CheckFeatures();
2306   int flags;
2307   uint64_t latency;
2308   m_config.stats->GetParams(latency, flags);
2309   // TODO
2310   if (0) //flags & DVP_FLAG_NO_POSTPROC)
2311     SetPostProcFeatures(false);
2312   else
2313     SetPostProcFeatures(true);
2314
2315   m_config.stats->SetCanSkipDeint(false);
2316
2317   EDEINTERLACEMODE   mode = CMediaSettings::Get().GetCurrentVideoSettings().m_DeinterlaceMode;
2318   EINTERLACEMETHOD method = GetDeinterlacingMethod();
2319   bool interlaced = m_mixerInput[1].DVDPic.iFlags & DVP_FLAG_INTERLACED;
2320   m_SeenInterlaceFlag |= interlaced;
2321
2322   // TODO
2323   if (//!(flags & DVP_FLAG_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       // TODO
2346       if (0) //m_mixerInput[1].DVDPic.iFlags & DVP_FLAG_DROPDEINT)
2347       {
2348         m_mixersteps = 1;
2349       }
2350
2351       if(m_mixerInput[1].DVDPic.iFlags & DVP_FLAG_TOP_FIELD_FIRST)
2352         m_mixerfield = VDP_VIDEO_MIXER_PICTURE_STRUCTURE_TOP_FIELD;
2353       else
2354         m_mixerfield = VDP_VIDEO_MIXER_PICTURE_STRUCTURE_BOTTOM_FIELD;
2355
2356       m_mixerInput[1].DVDPic.format = RENDER_FMT_VDPAU;
2357       m_mixerInput[1].DVDPic.iFlags &= ~(DVP_FLAG_TOP_FIELD_FIRST |
2358                                         DVP_FLAG_REPEAT_TOP_FIELD |
2359                                         DVP_FLAG_INTERLACED);
2360       m_config.useInteropYuv = false;
2361     }
2362     else if (method == VS_INTERLACEMETHOD_RENDER_BOB)
2363     {
2364       m_mixersteps = 1;
2365       m_mixerfield = VDP_VIDEO_MIXER_PICTURE_STRUCTURE_FRAME;
2366       m_mixerInput[1].DVDPic.format = RENDER_FMT_VDPAU_420;
2367       m_config.useInteropYuv = true;
2368     }
2369     else
2370     {
2371       CLog::Log(LOGERROR, "CMixer::%s - interlace method: %d not supported, setting to AUTO", __FUNCTION__, method);
2372       m_mixersteps = 1;
2373       m_mixerfield = VDP_VIDEO_MIXER_PICTURE_STRUCTURE_FRAME;
2374       m_mixerInput[1].DVDPic.format = RENDER_FMT_VDPAU;
2375       m_mixerInput[1].DVDPic.iFlags &= ~(DVP_FLAG_TOP_FIELD_FIRST |
2376                                         DVP_FLAG_REPEAT_TOP_FIELD |
2377                                         DVP_FLAG_INTERLACED);
2378
2379       CMediaSettings::Get().GetCurrentVideoSettings().m_InterlaceMethod = VS_INTERLACEMETHOD_AUTO;
2380     }
2381   }
2382   else
2383   {
2384     m_mixersteps = 1;
2385     m_mixerfield = VDP_VIDEO_MIXER_PICTURE_STRUCTURE_FRAME;
2386
2387     if (m_config.useInteropYuv)
2388       m_mixerInput[1].DVDPic.format = RENDER_FMT_VDPAU_420;
2389     else
2390     {
2391       m_mixerInput[1].DVDPic.format = RENDER_FMT_VDPAU;
2392       m_mixerInput[1].DVDPic.iFlags &= ~(DVP_FLAG_TOP_FIELD_FIRST |
2393                                         DVP_FLAG_REPEAT_TOP_FIELD |
2394                                         DVP_FLAG_INTERLACED);
2395     }
2396   }
2397   m_mixerstep = 0;
2398
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.iHeight -= 6;
2408       m_mixerInput[1].DVDPic.iDisplayHeight = lrint(ratio*m_mixerInput[1].DVDPic.iHeight);
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   GLUnmapSurfaces();
2953   ReleaseBufferPool();
2954   DestroyGlxContext();
2955   return true;
2956 }
2957
2958 void COutput::Flush()
2959 {
2960   if (m_mixer.IsActive())
2961   {
2962     Message *reply;
2963     if (m_mixer.m_controlPort.SendOutMessageSync(CMixerControlProtocol::FLUSH,
2964                                                  &reply,
2965                                                  2000))
2966     {
2967       reply->Release();
2968     }
2969     else
2970       CLog::Log(LOGERROR, "Coutput::%s - failed to flush mixer", __FUNCTION__);
2971   }
2972
2973   Message *msg;
2974   while (m_mixer.m_dataPort.ReceiveInMessage(&msg))
2975   {
2976     if (msg->signal == CMixerDataProtocol::PICTURE)
2977     {
2978       CVdpauProcessedPicture pic = *(CVdpauProcessedPicture*)msg->data;
2979       m_bufferPool.processedPics.push(pic);
2980     }
2981     msg->Release();
2982   }
2983
2984   while (m_dataPort.ReceiveOutMessage(&msg))
2985   {
2986     if (msg->signal == COutputDataProtocol::NEWFRAME)
2987     {
2988       CVdpauDecodedPicture pic = *(CVdpauDecodedPicture*)msg->data;
2989       m_config.videoSurfaces->ClearRender(pic.videoSurface);
2990     }
2991     else if (msg->signal == COutputDataProtocol::RETURNPIC)
2992     {
2993       CVdpauRenderPicture *pic;
2994       pic = *((CVdpauRenderPicture**)msg->data);
2995       QueueReturnPicture(pic);
2996     }
2997     msg->Release();
2998   }
2999
3000   while (m_dataPort.ReceiveInMessage(&msg))
3001   {
3002     if (msg->signal == COutputDataProtocol::PICTURE)
3003     {
3004       CVdpauRenderPicture *pic;
3005       pic = *((CVdpauRenderPicture**)msg->data);
3006       QueueReturnPicture(pic);
3007     }
3008   }
3009
3010   // reset used render flag which was cleared on mixer flush
3011   std::deque<int>::iterator it;
3012   for (it = m_bufferPool.usedRenderPics.begin(); it != m_bufferPool.usedRenderPics.end(); ++it)
3013   {
3014     CVdpauRenderPicture *pic = m_bufferPool.allRenderPics[*it];
3015     if (pic->DVDPic.format == RENDER_FMT_VDPAU_420)
3016     {
3017       std::map<VdpVideoSurface, VdpauBufferPool::GLVideoSurface>::iterator it2;
3018       it2 = m_bufferPool.glVideoSurfaceMap.find(pic->sourceIdx);
3019       if (it2 == m_bufferPool.glVideoSurfaceMap.end())
3020       {
3021         CLog::Log(LOGDEBUG, "COutput::Flush - gl surface not found");
3022         continue;
3023       }
3024       m_config.videoSurfaces->MarkRender(it2->second.sourceVuv);
3025     }
3026   }
3027
3028   // clear processed pics
3029   while(!m_bufferPool.processedPics.empty())
3030   {
3031     CVdpauProcessedPicture procPic = m_bufferPool.processedPics.front();
3032     if (procPic.DVDPic.format == RENDER_FMT_VDPAU)
3033     {
3034       m_mixer.m_dataPort.SendOutMessage(CMixerDataProtocol::BUFFER, &procPic.outputSurface, sizeof(procPic.outputSurface));
3035     }
3036     else if (procPic.DVDPic.format == RENDER_FMT_VDPAU_420)
3037     {
3038       m_config.videoSurfaces->ClearRender(procPic.videoSurface);
3039     }
3040     m_bufferPool.processedPics.pop();
3041   }
3042 }
3043
3044 bool COutput::HasWork()
3045 {
3046   if (!m_bufferPool.processedPics.empty() && !m_bufferPool.freeRenderPics.empty())
3047     return true;
3048   return false;
3049 }
3050
3051 CVdpauRenderPicture* COutput::ProcessMixerPicture()
3052 {
3053   CVdpauRenderPicture *retPic = NULL;
3054
3055   if (!m_bufferPool.processedPics.empty() && !m_bufferPool.freeRenderPics.empty())
3056   {
3057     int idx = m_bufferPool.freeRenderPics.front();
3058     retPic = m_bufferPool.allRenderPics[idx];
3059     m_bufferPool.freeRenderPics.pop_front();
3060     m_bufferPool.usedRenderPics.push_back(idx);
3061     CVdpauProcessedPicture procPic = m_bufferPool.processedPics.front();
3062     m_bufferPool.processedPics.pop();
3063
3064     retPic->DVDPic = procPic.DVDPic;
3065     retPic->valid = true;
3066     if (retPic->DVDPic.format == RENDER_FMT_VDPAU)
3067     {
3068       m_config.useInteropYuv = false;
3069       m_bufferPool.numOutputSurfaces = NUM_RENDER_PICS;
3070       EnsureBufferPool();
3071       GLMapSurface(false, procPic.outputSurface);
3072       retPic->sourceIdx = procPic.outputSurface;
3073       retPic->texture[0] = m_bufferPool.glOutputSurfaceMap[procPic.outputSurface].texture[0];
3074       retPic->texWidth = m_config.outWidth;
3075       retPic->texHeight = m_config.outHeight;
3076       retPic->crop.x1 = 0;
3077       retPic->crop.y1 = (m_config.outHeight - retPic->DVDPic.iHeight) / 2;
3078       retPic->crop.x2 = m_config.outWidth;
3079       retPic->crop.y2 = m_config.outHeight - retPic->crop.y1;
3080     }
3081     else
3082     {
3083       m_config.useInteropYuv = true;
3084       GLMapSurface(true, procPic.videoSurface);
3085       retPic->sourceIdx = procPic.videoSurface;
3086       for (unsigned int i=0; i<4; ++i)
3087         retPic->texture[i] = m_bufferPool.glVideoSurfaceMap[procPic.videoSurface].texture[i];
3088       retPic->texWidth = m_config.surfaceWidth;
3089       retPic->texHeight = m_config.surfaceHeight;
3090       retPic->crop.x1 = 0;
3091       retPic->crop.y1 = 0;
3092       retPic->crop.x2 = m_config.surfaceWidth - m_config.vidWidth;
3093       retPic->crop.y2 = m_config.surfaceHeight - m_config.vidHeight;
3094     }
3095   }
3096   return retPic;
3097 }
3098
3099 void COutput::QueueReturnPicture(CVdpauRenderPicture *pic)
3100 {
3101   std::deque<int>::iterator it;
3102   for (it = m_bufferPool.usedRenderPics.begin(); it != m_bufferPool.usedRenderPics.end(); ++it)
3103   {
3104     if (m_bufferPool.allRenderPics[*it] == pic)
3105     {
3106       break;
3107     }
3108   }
3109
3110   if (it == m_bufferPool.usedRenderPics.end())
3111   {
3112     CLog::Log(LOGWARNING, "COutput::QueueReturnPicture - pic not found");
3113     return;
3114   }
3115
3116   // check if already queued
3117   std::deque<int>::iterator it2 = find(m_bufferPool.syncRenderPics.begin(),
3118                                        m_bufferPool.syncRenderPics.end(),
3119                                        *it);
3120   if (it2 == m_bufferPool.syncRenderPics.end())
3121   {
3122     m_bufferPool.syncRenderPics.push_back(*it);
3123   }
3124
3125   ProcessSyncPicture();
3126 }
3127
3128 bool COutput::ProcessSyncPicture()
3129 {
3130   CVdpauRenderPicture *pic;
3131   bool busy = false;
3132
3133   std::deque<int>::iterator it;
3134   for (it = m_bufferPool.syncRenderPics.begin(); it != m_bufferPool.syncRenderPics.end(); )
3135   {
3136     pic = m_bufferPool.allRenderPics[*it];
3137
3138 #ifdef GL_ARB_sync
3139     if (pic->usefence)
3140     {
3141       if (glIsSync(pic->fence))
3142       {
3143         GLint state;
3144         GLsizei length;
3145         glGetSynciv(pic->fence, GL_SYNC_STATUS, 1, &length, &state);
3146         if(state == GL_SIGNALED)
3147         {
3148           glDeleteSync(pic->fence);
3149           pic->fence = None;
3150         }
3151         else
3152         {
3153           busy = true;
3154           ++it;
3155           continue;
3156         }
3157       }
3158     }
3159 #endif
3160
3161     m_bufferPool.freeRenderPics.push_back(*it);
3162
3163     std::deque<int>::iterator it2 = find(m_bufferPool.usedRenderPics.begin(),
3164                                          m_bufferPool.usedRenderPics.end(),
3165                                          *it);
3166     if (it2 == m_bufferPool.usedRenderPics.end())
3167     {
3168       CLog::Log(LOGERROR, "COutput::ProcessSyncPicture - pic not found in queue");
3169     }
3170     else
3171     {
3172       m_bufferPool.usedRenderPics.erase(it2);
3173     }
3174     it = m_bufferPool.syncRenderPics.erase(it);
3175
3176     if (pic->valid)
3177     {
3178       ProcessReturnPicture(pic);
3179     }
3180     else
3181     {
3182       CLog::Log(LOGDEBUG, "COutput::%s - return of invalid render pic", __FUNCTION__);
3183     }
3184   }
3185   return busy;
3186 }
3187
3188 void COutput::ProcessReturnPicture(CVdpauRenderPicture *pic)
3189 {
3190   if (pic->DVDPic.format == RENDER_FMT_VDPAU_420)
3191   {
3192     std::map<VdpVideoSurface, VdpauBufferPool::GLVideoSurface>::iterator it;
3193     it = m_bufferPool.glVideoSurfaceMap.find(pic->sourceIdx);
3194     if (it == m_bufferPool.glVideoSurfaceMap.end())
3195     {
3196       CLog::Log(LOGDEBUG, "COutput::ProcessReturnPicture - gl surface not found");
3197       return;
3198     }
3199 #ifdef GL_NV_vdpau_interop
3200     glVDPAUUnmapSurfacesNV(1, &(it->second.glVdpauSurface));
3201 #endif
3202     VdpVideoSurface surf = it->second.sourceVuv;
3203     m_config.videoSurfaces->ClearRender(surf);
3204   }
3205   else if (pic->DVDPic.format == RENDER_FMT_VDPAU)
3206   {
3207     std::map<VdpOutputSurface, VdpauBufferPool::GLVideoSurface>::iterator it;
3208     it = m_bufferPool.glOutputSurfaceMap.find(pic->sourceIdx);
3209     if (it == m_bufferPool.glOutputSurfaceMap.end())
3210     {
3211       CLog::Log(LOGDEBUG, "COutput::ProcessReturnPicture - gl surface not found");
3212       return;
3213     }
3214 #ifdef GL_NV_vdpau_interop
3215     glVDPAUUnmapSurfacesNV(1, &(it->second.glVdpauSurface));
3216 #endif
3217     VdpOutputSurface outSurf = it->second.sourceRgb;
3218     m_mixer.m_dataPort.SendOutMessage(CMixerDataProtocol::BUFFER, &outSurf, sizeof(outSurf));
3219   }
3220 }
3221
3222 bool COutput::EnsureBufferPool()
3223 {
3224   VdpStatus vdp_st;
3225
3226   // Creation of outputSurfaces
3227   VdpOutputSurface outputSurface;
3228   for (int i = m_bufferPool.outputSurfaces.size(); i < m_bufferPool.numOutputSurfaces; i++)
3229   {
3230     vdp_st = m_config.context->GetProcs().vdp_output_surface_create(m_config.context->GetDevice(),
3231                                       VDP_RGBA_FORMAT_B8G8R8A8,
3232                                       m_config.outWidth,
3233                                       m_config.outHeight,
3234                                       &outputSurface);
3235     if (CheckStatus(vdp_st, __LINE__))
3236       return false;
3237     m_bufferPool.outputSurfaces.push_back(outputSurface);
3238
3239     m_mixer.m_dataPort.SendOutMessage(CMixerDataProtocol::BUFFER,
3240                                       &outputSurface,
3241                                       sizeof(VdpOutputSurface));
3242     CLog::Log(LOGNOTICE, "VDPAU::COutput::InitBufferPool - Output Surface created");
3243   }
3244   return true;
3245 }
3246
3247 void COutput::ReleaseBufferPool()
3248 {
3249   VdpStatus vdp_st;
3250
3251   CSingleLock lock(m_bufferPool.renderPicSec);
3252
3253   // release all output surfaces
3254   for (unsigned int i = 0; i < m_bufferPool.outputSurfaces.size(); ++i)
3255   {
3256     if (m_bufferPool.outputSurfaces[i] == VDP_INVALID_HANDLE)
3257       continue;
3258     vdp_st = m_config.context->GetProcs().vdp_output_surface_destroy(m_bufferPool.outputSurfaces[i]);
3259     CheckStatus(vdp_st, __LINE__);
3260   }
3261   m_bufferPool.outputSurfaces.clear();
3262
3263   // wait for all fences
3264   XbmcThreads::EndTime timeout(1000);
3265   for (unsigned int i = 0; i < m_bufferPool.allRenderPics.size(); i++)
3266   {
3267     CVdpauRenderPicture *pic = m_bufferPool.allRenderPics[i];
3268     if (pic->usefence)
3269     {
3270 #ifdef GL_ARB_sync
3271       while (glIsSync(pic->fence))
3272       {
3273         GLint state;
3274         GLsizei length;
3275         glGetSynciv(pic->fence, GL_SYNC_STATUS, 1, &length, &state);
3276         if(state == GL_SIGNALED || timeout.IsTimePast())
3277         {
3278           glDeleteSync(pic->fence);
3279         }
3280         else
3281         {
3282           Sleep(5);
3283         }
3284       }
3285       pic->fence = None;
3286 #endif
3287     }
3288   }
3289   if (timeout.IsTimePast())
3290   {
3291     CLog::Log(LOGERROR, "COutput::%s - timeout waiting for fence", __FUNCTION__);
3292   }
3293   ProcessSyncPicture();
3294
3295   // invalidate all used render pictures
3296   for (unsigned int i = 0; i < m_bufferPool.usedRenderPics.size(); ++i)
3297   {
3298     CVdpauRenderPicture *pic = m_bufferPool.allRenderPics[m_bufferPool.usedRenderPics[i]];
3299     pic->valid = false;
3300   }
3301 }
3302
3303 void COutput::PreCleanup()
3304 {
3305
3306   VdpStatus vdp_st;
3307
3308   m_mixer.Dispose();
3309   ProcessSyncPicture();
3310
3311   CSingleLock lock(m_bufferPool.renderPicSec);
3312   for (unsigned int i = 0; i < m_bufferPool.outputSurfaces.size(); ++i)
3313   {
3314     if (m_bufferPool.outputSurfaces[i] == VDP_INVALID_HANDLE)
3315       continue;
3316
3317     // check if output surface is in use
3318     bool used = false;
3319     std::deque<int>::iterator it;
3320     CVdpauRenderPicture *pic;
3321     for (it = m_bufferPool.usedRenderPics.begin(); it != m_bufferPool.usedRenderPics.end(); ++it)
3322     {
3323       pic = m_bufferPool.allRenderPics[*it];
3324       if ((pic->sourceIdx == m_bufferPool.outputSurfaces[i]) && pic->valid)
3325       {
3326         used = true;
3327         break;
3328       }
3329     }
3330     if (used)
3331       continue;
3332
3333 #ifdef GL_NV_vdpau_interop
3334     // unmap surface
3335     std::map<VdpOutputSurface, VdpauBufferPool::GLVideoSurface>::iterator it_map;
3336     it_map = m_bufferPool.glOutputSurfaceMap.find(m_bufferPool.outputSurfaces[i]);
3337     if (it_map == m_bufferPool.glOutputSurfaceMap.end())
3338     {
3339       CLog::Log(LOGERROR, "%s - could not find gl surface", __FUNCTION__);
3340       continue;
3341     }
3342     glVDPAUUnregisterSurfaceNV(it_map->second.glVdpauSurface);
3343     glDeleteTextures(1, it_map->second.texture);
3344     m_bufferPool.glOutputSurfaceMap.erase(it_map);
3345 #endif
3346
3347     vdp_st = m_config.context->GetProcs().vdp_output_surface_destroy(m_bufferPool.outputSurfaces[i]);
3348     CheckStatus(vdp_st, __LINE__);
3349
3350     m_bufferPool.outputSurfaces[i] = VDP_INVALID_HANDLE;
3351
3352     CLog::Log(LOGDEBUG, "VDPAU::PreCleanup - released output surface");
3353   }
3354
3355 }
3356
3357 void COutput::InitMixer()
3358 {
3359   for (unsigned int i = 0; i < m_bufferPool.outputSurfaces.size(); ++i)
3360   {
3361     m_mixer.m_dataPort.SendOutMessage(CMixerDataProtocol::BUFFER,
3362                                       &m_bufferPool.outputSurfaces[i],
3363                                       sizeof(VdpOutputSurface));
3364   }
3365 }
3366
3367 bool COutput::GLInit()
3368 {
3369 #ifdef GL_NV_vdpau_interop
3370   glVDPAUInitNV = NULL;
3371   glVDPAUFiniNV = NULL;
3372   glVDPAURegisterOutputSurfaceNV = NULL;
3373   glVDPAURegisterVideoSurfaceNV = NULL;
3374   glVDPAUIsSurfaceNV = NULL;
3375   glVDPAUUnregisterSurfaceNV = NULL;
3376   glVDPAUSurfaceAccessNV = NULL;
3377   glVDPAUMapSurfacesNV = NULL;
3378   glVDPAUUnmapSurfacesNV = NULL;
3379   glVDPAUGetSurfaceivNV = NULL;
3380 #endif
3381
3382 #ifdef GL_NV_vdpau_interop
3383     if (!glVDPAUInitNV)
3384       glVDPAUInitNV    = (PFNGLVDPAUINITNVPROC)glXGetProcAddress((GLubyte *) "glVDPAUInitNV");
3385     if (!glVDPAUFiniNV)
3386       glVDPAUFiniNV = (PFNGLVDPAUFININVPROC)glXGetProcAddress((GLubyte *) "glVDPAUFiniNV");
3387     if (!glVDPAURegisterOutputSurfaceNV)
3388       glVDPAURegisterOutputSurfaceNV    = (PFNGLVDPAUREGISTEROUTPUTSURFACENVPROC)glXGetProcAddress((GLubyte *) "glVDPAURegisterOutputSurfaceNV");
3389     if (!glVDPAURegisterVideoSurfaceNV)
3390       glVDPAURegisterVideoSurfaceNV    = (PFNGLVDPAUREGISTERVIDEOSURFACENVPROC)glXGetProcAddress((GLubyte *) "glVDPAURegisterVideoSurfaceNV");
3391     if (!glVDPAUIsSurfaceNV)
3392       glVDPAUIsSurfaceNV    = (PFNGLVDPAUISSURFACENVPROC)glXGetProcAddress((GLubyte *) "glVDPAUIsSurfaceNV");
3393     if (!glVDPAUUnregisterSurfaceNV)
3394       glVDPAUUnregisterSurfaceNV = (PFNGLVDPAUUNREGISTERSURFACENVPROC)glXGetProcAddress((GLubyte *) "glVDPAUUnregisterSurfaceNV");
3395     if (!glVDPAUSurfaceAccessNV)
3396       glVDPAUSurfaceAccessNV    = (PFNGLVDPAUSURFACEACCESSNVPROC)glXGetProcAddress((GLubyte *) "glVDPAUSurfaceAccessNV");
3397     if (!glVDPAUMapSurfacesNV)
3398       glVDPAUMapSurfacesNV = (PFNGLVDPAUMAPSURFACESNVPROC)glXGetProcAddress((GLubyte *) "glVDPAUMapSurfacesNV");
3399     if (!glVDPAUUnmapSurfacesNV)
3400       glVDPAUUnmapSurfacesNV = (PFNGLVDPAUUNMAPSURFACESNVPROC)glXGetProcAddress((GLubyte *) "glVDPAUUnmapSurfacesNV");
3401     if (!glVDPAUGetSurfaceivNV)
3402       glVDPAUGetSurfaceivNV = (PFNGLVDPAUGETSURFACEIVNVPROC)glXGetProcAddress((GLubyte *) "glVDPAUGetSurfaceivNV");
3403
3404   while (glGetError() != GL_NO_ERROR);
3405   glVDPAUInitNV(reinterpret_cast<void*>(m_config.context->GetDevice()), reinterpret_cast<void*>(m_config.context->GetProcs().vdp_get_proc_address));
3406   if (glGetError() != GL_NO_ERROR)
3407   {
3408     CLog::Log(LOGERROR, "VDPAU::COutput - GLInitInterop glVDPAUInitNV failed");
3409     m_vdpError = true;
3410     return false;
3411   }
3412   CLog::Log(LOGNOTICE, "VDPAU::COutput: vdpau gl interop initialized");
3413 #endif
3414
3415 #ifdef GL_ARB_sync
3416   bool hasfence = glewIsSupported("GL_ARB_sync");
3417   for (unsigned int i = 0; i < m_bufferPool.allRenderPics.size(); i++)
3418   {
3419     m_bufferPool.allRenderPics[i]->usefence = hasfence;
3420   }
3421 #endif
3422
3423   return true;
3424 }
3425
3426 void COutput::GLMapSurface(bool yuv, uint32_t source)
3427 {
3428 #ifdef GL_NV_vdpau_interop
3429
3430   if (yuv)
3431   {
3432     std::map<VdpVideoSurface, VdpauBufferPool::GLVideoSurface>::iterator it;
3433     it = m_bufferPool.glVideoSurfaceMap.find(source);
3434     if (it == m_bufferPool.glVideoSurfaceMap.end())
3435     {
3436       VdpauBufferPool::GLVideoSurface glSurface;
3437       VdpVideoSurface surf = source;
3438
3439       if (surf == VDP_INVALID_HANDLE)
3440         return;
3441
3442       glSurface.sourceVuv = surf;
3443       while (glGetError() != GL_NO_ERROR) ;
3444       glGenTextures(4, glSurface.texture);
3445       if (glGetError() != GL_NO_ERROR)
3446       {
3447         CLog::Log(LOGERROR, "VDPAU::COutput error creating texture");
3448         m_vdpError = true;
3449       }
3450       glSurface.glVdpauSurface = glVDPAURegisterVideoSurfaceNV(reinterpret_cast<void*>(surf),
3451                                                     GL_TEXTURE_2D, 4, glSurface.texture);
3452
3453       if (glGetError() != GL_NO_ERROR)
3454       {
3455         CLog::Log(LOGERROR, "VDPAU::COutput error register video surface");
3456         m_vdpError = true;
3457       }
3458       glVDPAUSurfaceAccessNV(glSurface.glVdpauSurface, GL_READ_ONLY);
3459       if (glGetError() != GL_NO_ERROR)
3460       {
3461         CLog::Log(LOGERROR, "VDPAU::COutput error setting access");
3462         m_vdpError = true;
3463       }
3464       m_bufferPool.glVideoSurfaceMap[surf] = glSurface;
3465
3466       CLog::Log(LOGNOTICE, "VDPAU::COutput registered surface");
3467     }
3468
3469     while (glGetError() != GL_NO_ERROR) ;
3470     glVDPAUMapSurfacesNV(1, &m_bufferPool.glVideoSurfaceMap[source].glVdpauSurface);
3471     if (glGetError() != GL_NO_ERROR)
3472     {
3473       CLog::Log(LOGERROR, "VDPAU::COutput error mapping surface");
3474       m_vdpError = true;
3475     }
3476
3477     if (m_vdpError)
3478       return;
3479   }
3480   else
3481   {
3482     std::map<VdpOutputSurface, VdpauBufferPool::GLVideoSurface>::iterator it;
3483     it = m_bufferPool.glOutputSurfaceMap.find(source);
3484     if (it == m_bufferPool.glOutputSurfaceMap.end())
3485     {
3486       unsigned int idx = 0;
3487       for (idx = 0; idx<m_bufferPool.outputSurfaces.size(); idx++)
3488       {
3489         if (m_bufferPool.outputSurfaces[idx] == source)
3490           break;
3491       }
3492
3493       VdpauBufferPool::GLVideoSurface glSurface;
3494       glSurface.sourceRgb = m_bufferPool.outputSurfaces[idx];
3495       glGenTextures(1, glSurface.texture);
3496       glSurface.glVdpauSurface = glVDPAURegisterOutputSurfaceNV(reinterpret_cast<void*>(m_bufferPool.outputSurfaces[idx]),
3497                                                GL_TEXTURE_2D, 1, glSurface.texture);
3498       if (glGetError() != GL_NO_ERROR)
3499       {
3500         CLog::Log(LOGERROR, "VDPAU::COutput error register output surface");
3501         m_vdpError = true;
3502       }
3503       glVDPAUSurfaceAccessNV(glSurface.glVdpauSurface, GL_READ_ONLY);
3504       if (glGetError() != GL_NO_ERROR)
3505       {
3506         CLog::Log(LOGERROR, "VDPAU::COutput error setting access");
3507         m_vdpError = true;
3508       }
3509       m_bufferPool.glOutputSurfaceMap[source] = glSurface;
3510       CLog::Log(LOGNOTICE, "VDPAU::COutput registered output surfaces");
3511     }
3512
3513     while (glGetError() != GL_NO_ERROR) ;
3514     glVDPAUMapSurfacesNV(1, &m_bufferPool.glOutputSurfaceMap[source].glVdpauSurface);
3515     if (glGetError() != GL_NO_ERROR)
3516     {
3517       CLog::Log(LOGERROR, "VDPAU::COutput error mapping surface");
3518       m_vdpError = true;
3519     }
3520
3521     if (m_vdpError)
3522       return;
3523   }
3524 #endif
3525 }
3526
3527 void COutput::GLUnmapSurfaces()
3528 {
3529 #ifdef GL_NV_vdpau_interop
3530
3531   {
3532     std::map<VdpVideoSurface, VdpauBufferPool::GLVideoSurface>::iterator it;
3533     for (it = m_bufferPool.glVideoSurfaceMap.begin(); it != m_bufferPool.glVideoSurfaceMap.end(); ++it)
3534     {
3535       glVDPAUUnregisterSurfaceNV(it->second.glVdpauSurface);
3536       glDeleteTextures(4, it->second.texture);
3537     }
3538     m_bufferPool.glVideoSurfaceMap.clear();
3539   }
3540
3541   std::map<VdpOutputSurface, VdpauBufferPool::GLVideoSurface>::iterator it;
3542   for (it = m_bufferPool.glOutputSurfaceMap.begin(); it != m_bufferPool.glOutputSurfaceMap.end(); ++it)
3543   {
3544     glVDPAUUnregisterSurfaceNV(it->second.glVdpauSurface);
3545     glDeleteTextures(1, it->second.texture);
3546   }
3547   m_bufferPool.glOutputSurfaceMap.clear();
3548
3549   glVDPAUFiniNV();
3550
3551   CLog::Log(LOGNOTICE, "VDPAU::COutput: vdpau gl interop finished");
3552
3553 #endif
3554 }
3555
3556 bool COutput::CheckStatus(VdpStatus vdp_st, int line)
3557 {
3558   if (vdp_st != VDP_STATUS_OK)
3559   {
3560     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);
3561     m_vdpError = true;
3562     return true;
3563   }
3564   return false;
3565 }
3566
3567 bool COutput::CreateGlxContext()
3568 {
3569   GLXContext   glContext;
3570
3571   m_Display = g_Windowing.GetDisplay();
3572   glContext = g_Windowing.GetGlxContext();
3573   m_Window = g_Windowing.GetWindow();
3574
3575   // Get our window attribs.
3576   XWindowAttributes wndattribs;
3577   XGetWindowAttributes(m_Display, m_Window, &wndattribs);
3578
3579   // Get visual Info
3580   XVisualInfo visInfo;
3581   visInfo.visualid = wndattribs.visual->visualid;
3582   int nvisuals = 0;
3583   XVisualInfo* visuals = XGetVisualInfo(m_Display, VisualIDMask, &visInfo, &nvisuals);
3584   if (nvisuals != 1)
3585   {
3586     CLog::Log(LOGERROR, "VDPAU::COutput::CreateGlxContext - could not find visual");
3587     return false;
3588   }
3589   visInfo = visuals[0];
3590   XFree(visuals);
3591
3592   m_pixmap = XCreatePixmap(m_Display,
3593                            m_Window,
3594                            192,
3595                            108,
3596                            visInfo.depth);
3597   if (!m_pixmap)
3598   {
3599     CLog::Log(LOGERROR, "VDPAU::COutput::CreateGlxContext - Unable to create XPixmap");
3600     return false;
3601   }
3602
3603   // create gl pixmap
3604   m_glPixmap = glXCreateGLXPixmap(m_Display, &visInfo, m_pixmap);
3605
3606   if (!m_glPixmap)
3607   {
3608     CLog::Log(LOGINFO, "VDPAU::COutput::CreateGlxContext - Could not create glPixmap");
3609     return false;
3610   }
3611
3612   m_glContext = glXCreateContext(m_Display, &visInfo, glContext, True);
3613
3614   if (!glXMakeCurrent(m_Display, m_glPixmap, m_glContext))
3615   {
3616     CLog::Log(LOGINFO, "VDPAU::COutput::CreateGlxContext - Could not make Pixmap current");
3617     return false;
3618   }
3619
3620   CLog::Log(LOGNOTICE, "VDPAU::COutput::CreateGlxContext - created context");
3621   return true;
3622 }
3623
3624 bool COutput::DestroyGlxContext()
3625 {
3626   if (m_glContext)
3627   {
3628     glXMakeCurrent(m_Display, None, NULL);
3629     glXDestroyContext(m_Display, m_glContext);
3630   }
3631   m_glContext = 0;
3632
3633   if (m_glPixmap)
3634     glXDestroyPixmap(m_Display, m_glPixmap);
3635   m_glPixmap = 0;
3636
3637   if (m_pixmap)
3638     XFreePixmap(m_Display, m_pixmap);
3639   m_pixmap = 0;
3640
3641   return true;
3642 }
3643
3644 #endif