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