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