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