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