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