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