Merge pull request #5101 from FernetMenta/ffmpeg-threads
[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 #define NUM_CROP_PIX 3
43
44 #define ARSIZE(x) (sizeof(x) / sizeof((x)[0]))
45
46 // settings codecs mapping
47 DVDCodecAvailableType g_vdpau_available[] = {
48   { AV_CODEC_ID_H263, "videoplayer.usevdpaumpeg4" },
49   { AV_CODEC_ID_MPEG4, "videoplayer.usevdpaumpeg4" },
50   { AV_CODEC_ID_WMV3, "videoplayer.usevdpauvc1" },
51   { AV_CODEC_ID_VC1, "videoplayer.usevdpauvc1" },
52   { AV_CODEC_ID_MPEG2VIDEO, "videoplayer.usevdpaumpeg2" },
53 };
54 const size_t settings_count = sizeof(g_vdpau_available) / sizeof(DVDCodecAvailableType);
55
56 CDecoder::Desc decoder_profiles[] = {
57 {"MPEG1",        VDP_DECODER_PROFILE_MPEG1},
58 {"MPEG2_SIMPLE", VDP_DECODER_PROFILE_MPEG2_SIMPLE},
59 {"MPEG2_MAIN",   VDP_DECODER_PROFILE_MPEG2_MAIN},
60 {"H264_BASELINE",VDP_DECODER_PROFILE_H264_BASELINE},
61 {"H264_MAIN",    VDP_DECODER_PROFILE_H264_MAIN},
62 {"H264_HIGH",    VDP_DECODER_PROFILE_H264_HIGH},
63 {"VC1_SIMPLE",   VDP_DECODER_PROFILE_VC1_SIMPLE},
64 {"VC1_MAIN",     VDP_DECODER_PROFILE_VC1_MAIN},
65 {"VC1_ADVANCED", VDP_DECODER_PROFILE_VC1_ADVANCED},
66 {"MPEG4_PART2_ASP", VDP_DECODER_PROFILE_MPEG4_PART2_ASP},
67 };
68 const size_t decoder_profile_count = sizeof(decoder_profiles)/sizeof(CDecoder::Desc);
69
70 static struct SInterlaceMapping
71 {
72   const EINTERLACEMETHOD     method;
73   const VdpVideoMixerFeature feature;
74 } g_interlace_mapping[] = 
75 { {VS_INTERLACEMETHOD_VDPAU_TEMPORAL             , VDP_VIDEO_MIXER_FEATURE_DEINTERLACE_TEMPORAL}
76 , {VS_INTERLACEMETHOD_VDPAU_TEMPORAL_HALF        , VDP_VIDEO_MIXER_FEATURE_DEINTERLACE_TEMPORAL}
77 , {VS_INTERLACEMETHOD_VDPAU_TEMPORAL_SPATIAL     , VDP_VIDEO_MIXER_FEATURE_DEINTERLACE_TEMPORAL_SPATIAL}
78 , {VS_INTERLACEMETHOD_VDPAU_TEMPORAL_SPATIAL_HALF, VDP_VIDEO_MIXER_FEATURE_DEINTERLACE_TEMPORAL_SPATIAL}
79 , {VS_INTERLACEMETHOD_VDPAU_INVERSE_TELECINE     , VDP_VIDEO_MIXER_FEATURE_INVERSE_TELECINE}
80 , {VS_INTERLACEMETHOD_NONE                       , (VdpVideoMixerFeature)-1}
81 };
82
83 static float studioCSCKCoeffs601[3] = {0.299, 0.587, 0.114}; //BT601 {Kr, Kg, Kb}
84 static float studioCSCKCoeffs709[3] = {0.2126, 0.7152, 0.0722}; //BT709 {Kr, Kg, Kb}
85
86 //-----------------------------------------------------------------------------
87 //-----------------------------------------------------------------------------
88
89 CVDPAUContext *CVDPAUContext::m_context = 0;
90 CCriticalSection CVDPAUContext::m_section;
91 Display *CVDPAUContext::m_display = 0;
92 void *CVDPAUContext::m_dlHandle = 0;
93
94 CVDPAUContext::CVDPAUContext()
95 {
96   m_context = 0;
97   m_refCount = 0;
98 }
99
100 void CVDPAUContext::Release()
101 {
102   CSingleLock lock(m_section);
103
104   m_refCount--;
105   if (m_refCount <= 0)
106   {
107     Close();
108     delete this;
109     m_context = 0;
110   }
111 }
112
113 void CVDPAUContext::Close()
114 {
115   CLog::Log(LOGNOTICE, "VDPAU::Close - closing decoder context");
116   DestroyContext();
117 }
118
119 bool CVDPAUContext::EnsureContext(CVDPAUContext **ctx)
120 {
121   CSingleLock lock(m_section);
122
123   if (m_context)
124   {
125     m_context->m_refCount++;
126     *ctx = m_context;
127     return true;
128   }
129
130   m_context = new CVDPAUContext();
131   *ctx = m_context;
132   {
133     CSingleLock gLock(g_graphicsContext);
134     if (!m_context->LoadSymbols() || !m_context->CreateContext())
135     {
136       delete m_context;
137       m_context = 0;
138       *ctx = NULL;
139       return false;
140     }
141   }
142
143   m_context->m_refCount++;
144
145   *ctx = m_context;
146   return true;
147 }
148
149 VDPAU_procs& CVDPAUContext::GetProcs()
150 {
151   return m_vdpProcs;
152 }
153
154 VdpVideoMixerFeature* CVDPAUContext::GetFeatures()
155 {
156   return m_vdpFeatures;
157 }
158
159 int CVDPAUContext::GetFeatureCount()
160 {
161   return m_featureCount;
162 }
163
164 bool CVDPAUContext::LoadSymbols()
165 {
166   if (!m_dlHandle)
167   {
168     m_dlHandle  = dlopen("libvdpau.so.1", RTLD_LAZY);
169     if (!m_dlHandle)
170     {
171       const char* error = dlerror();
172       if (!error)
173         error = "dlerror() returned NULL";
174
175       CLog::Log(LOGERROR,"VDPAU::LoadSymbols: Unable to get handle to lib: %s", error);
176       return false;
177     }
178   }
179
180   char* error;
181   (void)dlerror();
182   dl_vdp_device_create_x11 = (VdpStatus (*)(Display*, int, VdpDevice*, VdpStatus (**)(VdpDevice, VdpFuncId, void**)))dlsym(m_dlHandle, (const char*)"vdp_device_create_x11");
183   error = dlerror();
184   if (error)
185   {
186     CLog::Log(LOGERROR,"(VDPAU) - %s in %s",error,__FUNCTION__);
187     m_vdpDevice = VDP_INVALID_HANDLE;
188     return false;
189   }
190   return true;
191 }
192
193 bool CVDPAUContext::CreateContext()
194 {
195   CLog::Log(LOGNOTICE,"VDPAU::CreateContext - creating decoder context");
196
197   int mScreen;
198   { CSingleLock lock(g_graphicsContext);
199     if (!m_display)
200       m_display = XOpenDisplay(NULL);
201     mScreen = g_Windowing.GetCurrentScreen();
202   }
203
204   VdpStatus vdp_st;
205   // Create Device
206   vdp_st = dl_vdp_device_create_x11(m_display,
207                                     mScreen,
208                                    &m_vdpDevice,
209                                    &m_vdpProcs.vdp_get_proc_address);
210
211   CLog::Log(LOGNOTICE,"vdp_device = 0x%08x vdp_st = 0x%08x",m_vdpDevice,vdp_st);
212   if (vdp_st != VDP_STATUS_OK)
213   {
214     CLog::Log(LOGERROR,"(VDPAU) unable to init VDPAU - vdp_st = 0x%x.  Falling back.",vdp_st);
215     m_vdpDevice = VDP_INVALID_HANDLE;
216     return false;
217   }
218
219   QueryProcs();
220   SpewHardwareAvailable();
221   return true;
222 }
223
224 void CVDPAUContext::QueryProcs()
225 {
226   VdpStatus vdp_st;
227
228 #define VDP_PROC(id, proc) \
229   do { \
230     vdp_st = m_vdpProcs.vdp_get_proc_address(m_vdpDevice, id, (void**)&proc); \
231     if (vdp_st != VDP_STATUS_OK) \
232     { \
233       CLog::Log(LOGERROR, "CVDPAUContext::GetProcs - failed to get proc id"); \
234     } \
235   } while(0);
236
237   VDP_PROC(VDP_FUNC_ID_GET_ERROR_STRING                    , m_vdpProcs.vdp_get_error_string);
238   VDP_PROC(VDP_FUNC_ID_DEVICE_DESTROY                      , m_vdpProcs.vdp_device_destroy);
239   VDP_PROC(VDP_FUNC_ID_GENERATE_CSC_MATRIX                 , m_vdpProcs.vdp_generate_csc_matrix);
240   VDP_PROC(VDP_FUNC_ID_VIDEO_SURFACE_CREATE                , m_vdpProcs.vdp_video_surface_create);
241   VDP_PROC(VDP_FUNC_ID_VIDEO_SURFACE_DESTROY               , m_vdpProcs.vdp_video_surface_destroy);
242   VDP_PROC(VDP_FUNC_ID_VIDEO_SURFACE_PUT_BITS_Y_CB_CR      , m_vdpProcs.vdp_video_surface_put_bits_y_cb_cr);
243   VDP_PROC(VDP_FUNC_ID_VIDEO_SURFACE_GET_BITS_Y_CB_CR      , m_vdpProcs.vdp_video_surface_get_bits_y_cb_cr);
244   VDP_PROC(VDP_FUNC_ID_OUTPUT_SURFACE_PUT_BITS_Y_CB_CR     , m_vdpProcs.vdp_output_surface_put_bits_y_cb_cr);
245   VDP_PROC(VDP_FUNC_ID_OUTPUT_SURFACE_PUT_BITS_NATIVE      , m_vdpProcs.vdp_output_surface_put_bits_native);
246   VDP_PROC(VDP_FUNC_ID_OUTPUT_SURFACE_CREATE               , m_vdpProcs.vdp_output_surface_create);
247   VDP_PROC(VDP_FUNC_ID_OUTPUT_SURFACE_DESTROY              , m_vdpProcs.vdp_output_surface_destroy);
248   VDP_PROC(VDP_FUNC_ID_OUTPUT_SURFACE_GET_BITS_NATIVE      , m_vdpProcs.vdp_output_surface_get_bits_native);
249   VDP_PROC(VDP_FUNC_ID_OUTPUT_SURFACE_RENDER_OUTPUT_SURFACE, m_vdpProcs.vdp_output_surface_render_output_surface);
250   VDP_PROC(VDP_FUNC_ID_OUTPUT_SURFACE_PUT_BITS_INDEXED     , m_vdpProcs.vdp_output_surface_put_bits_indexed);
251   VDP_PROC(VDP_FUNC_ID_VIDEO_MIXER_CREATE                  , m_vdpProcs.vdp_video_mixer_create);
252   VDP_PROC(VDP_FUNC_ID_VIDEO_MIXER_SET_FEATURE_ENABLES     , m_vdpProcs.vdp_video_mixer_set_feature_enables);
253   VDP_PROC(VDP_FUNC_ID_VIDEO_MIXER_DESTROY                 , m_vdpProcs.vdp_video_mixer_destroy);
254   VDP_PROC(VDP_FUNC_ID_VIDEO_MIXER_RENDER                  , m_vdpProcs.vdp_video_mixer_render);
255   VDP_PROC(VDP_FUNC_ID_VIDEO_MIXER_SET_ATTRIBUTE_VALUES    , m_vdpProcs.vdp_video_mixer_set_attribute_values);
256   VDP_PROC(VDP_FUNC_ID_VIDEO_MIXER_QUERY_PARAMETER_SUPPORT , m_vdpProcs.vdp_video_mixer_query_parameter_support);
257   VDP_PROC(VDP_FUNC_ID_VIDEO_MIXER_QUERY_FEATURE_SUPPORT   , m_vdpProcs.vdp_video_mixer_query_feature_support);
258   VDP_PROC(VDP_FUNC_ID_DECODER_CREATE                      , m_vdpProcs.vdp_decoder_create);
259   VDP_PROC(VDP_FUNC_ID_DECODER_DESTROY                     , m_vdpProcs.vdp_decoder_destroy);
260   VDP_PROC(VDP_FUNC_ID_DECODER_RENDER                      , m_vdpProcs.vdp_decoder_render);
261   VDP_PROC(VDP_FUNC_ID_DECODER_QUERY_CAPABILITIES          , m_vdpProcs.vdp_decoder_query_caps);
262 #undef VDP_PROC
263 }
264
265 VdpDevice CVDPAUContext::GetDevice()
266 {
267   return m_vdpDevice;
268 }
269
270 void CVDPAUContext::DestroyContext()
271 {
272   if (!m_vdpProcs.vdp_device_destroy)
273     return;
274
275   m_vdpProcs.vdp_device_destroy(m_vdpDevice);
276   m_vdpDevice = VDP_INVALID_HANDLE;
277 }
278
279 void CVDPAUContext::SpewHardwareAvailable()  //CopyrighVDPAUt (c) 2008 Wladimir J. van der Laan  -- VDPInfo
280 {
281   VdpStatus rv;
282   CLog::Log(LOGNOTICE,"VDPAU Decoder capabilities:");
283   CLog::Log(LOGNOTICE,"name          level macbs width height");
284   CLog::Log(LOGNOTICE,"------------------------------------");
285   for(unsigned int x=0; x<decoder_profile_count; ++x)
286   {
287     VdpBool is_supported = false;
288     uint32_t max_level, max_macroblocks, max_width, max_height;
289     rv = m_vdpProcs.vdp_decoder_query_caps(m_vdpDevice, decoder_profiles[x].id,
290                                 &is_supported, &max_level, &max_macroblocks, &max_width, &max_height);
291     if(rv == VDP_STATUS_OK && is_supported)
292     {
293       CLog::Log(LOGNOTICE,"%-16s %2i %5i %5i %5i\n", decoder_profiles[x].name,
294                 max_level, max_macroblocks, max_width, max_height);
295     }
296   }
297   CLog::Log(LOGNOTICE,"------------------------------------");
298   m_featureCount = 0;
299 #define CHECK_SUPPORT(feature)  \
300   do { \
301     VdpBool supported; \
302     if(m_vdpProcs.vdp_video_mixer_query_feature_support(m_vdpDevice, feature, &supported) == VDP_STATUS_OK && supported) { \
303       CLog::Log(LOGNOTICE, "Mixer feature: "#feature);  \
304       m_vdpFeatures[m_featureCount++] = feature; \
305     } \
306   } while(false)
307
308   CHECK_SUPPORT(VDP_VIDEO_MIXER_FEATURE_NOISE_REDUCTION);
309   CHECK_SUPPORT(VDP_VIDEO_MIXER_FEATURE_SHARPNESS);
310   CHECK_SUPPORT(VDP_VIDEO_MIXER_FEATURE_DEINTERLACE_TEMPORAL);
311   CHECK_SUPPORT(VDP_VIDEO_MIXER_FEATURE_DEINTERLACE_TEMPORAL_SPATIAL);
312   CHECK_SUPPORT(VDP_VIDEO_MIXER_FEATURE_INVERSE_TELECINE);
313 #ifdef VDP_VIDEO_MIXER_FEATURE_HIGH_QUALITY_SCALING_L1
314   CHECK_SUPPORT(VDP_VIDEO_MIXER_FEATURE_HIGH_QUALITY_SCALING_L1);
315   CHECK_SUPPORT(VDP_VIDEO_MIXER_FEATURE_HIGH_QUALITY_SCALING_L2);
316   CHECK_SUPPORT(VDP_VIDEO_MIXER_FEATURE_HIGH_QUALITY_SCALING_L3);
317   CHECK_SUPPORT(VDP_VIDEO_MIXER_FEATURE_HIGH_QUALITY_SCALING_L4);
318   CHECK_SUPPORT(VDP_VIDEO_MIXER_FEATURE_HIGH_QUALITY_SCALING_L5);
319   CHECK_SUPPORT(VDP_VIDEO_MIXER_FEATURE_HIGH_QUALITY_SCALING_L6);
320   CHECK_SUPPORT(VDP_VIDEO_MIXER_FEATURE_HIGH_QUALITY_SCALING_L7);
321   CHECK_SUPPORT(VDP_VIDEO_MIXER_FEATURE_HIGH_QUALITY_SCALING_L8);
322   CHECK_SUPPORT(VDP_VIDEO_MIXER_FEATURE_HIGH_QUALITY_SCALING_L9);
323 #endif
324 #undef CHECK_SUPPORT
325 }
326
327 bool CVDPAUContext::Supports(VdpVideoMixerFeature feature)
328 {
329   for(int i = 0; i < m_featureCount; i++)
330   {
331     if(m_vdpFeatures[i] == feature)
332       return true;
333   }
334   return false;
335 }
336
337 //-----------------------------------------------------------------------------
338 // VDPAU Video Surface states
339 //-----------------------------------------------------------------------------
340
341 #define SURFACE_USED_FOR_REFERENCE 0x01
342 #define SURFACE_USED_FOR_RENDER    0x02
343
344 void CVideoSurfaces::AddSurface(VdpVideoSurface surf)
345 {
346   CSingleLock lock(m_section);
347   m_state[surf] = SURFACE_USED_FOR_REFERENCE;
348 }
349
350 void CVideoSurfaces::ClearReference(VdpVideoSurface surf)
351 {
352   CSingleLock lock(m_section);
353   if (m_state.find(surf) == m_state.end())
354   {
355     CLog::Log(LOGWARNING, "CVideoSurfaces::ClearReference - surface invalid");
356     return;
357   }
358   m_state[surf] &= ~SURFACE_USED_FOR_REFERENCE;
359   if (m_state[surf] == 0)
360   {
361     m_freeSurfaces.push_back(surf);
362   }
363 }
364
365 bool CVideoSurfaces::MarkRender(VdpVideoSurface surf)
366 {
367   CSingleLock lock(m_section);
368   if (m_state.find(surf) == m_state.end())
369   {
370     CLog::Log(LOGWARNING, "CVideoSurfaces::MarkRender - surface invalid");
371     return false;
372   }
373   std::list<VdpVideoSurface>::iterator it;
374   it = std::find(m_freeSurfaces.begin(), m_freeSurfaces.end(), surf);
375   if (it != m_freeSurfaces.end())
376   {
377     m_freeSurfaces.erase(it);
378   }
379   m_state[surf] |= SURFACE_USED_FOR_RENDER;
380   return true;
381 }
382
383 void CVideoSurfaces::ClearRender(VdpVideoSurface surf)
384 {
385   CSingleLock lock(m_section);
386   if (m_state.find(surf) == m_state.end())
387   {
388     CLog::Log(LOGWARNING, "CVideoSurfaces::ClearRender - surface invalid");
389     return;
390   }
391   m_state[surf] &= ~SURFACE_USED_FOR_RENDER;
392   if (m_state[surf] == 0)
393   {
394     m_freeSurfaces.push_back(surf);
395   }
396 }
397
398 bool CVideoSurfaces::IsValid(VdpVideoSurface surf)
399 {
400   CSingleLock lock(m_section);
401   if (m_state.find(surf) != m_state.end())
402     return true;
403   else
404     return false;
405 }
406
407 VdpVideoSurface CVideoSurfaces::GetFree(VdpVideoSurface surf)
408 {
409   CSingleLock lock(m_section);
410   if (m_state.find(surf) != m_state.end())
411   {
412     std::list<VdpVideoSurface>::iterator it;
413     it = std::find(m_freeSurfaces.begin(), m_freeSurfaces.end(), surf);
414     if (it == m_freeSurfaces.end())
415     {
416       CLog::Log(LOGWARNING, "CVideoSurfaces::GetFree - surface not free");
417     }
418     else
419     {
420       m_freeSurfaces.erase(it);
421       m_state[surf] = SURFACE_USED_FOR_REFERENCE;
422       return surf;
423     }
424   }
425
426   if (!m_freeSurfaces.empty())
427   {
428     VdpVideoSurface freeSurf = m_freeSurfaces.front();
429     m_freeSurfaces.pop_front();
430     m_state[freeSurf] = SURFACE_USED_FOR_REFERENCE;
431     return freeSurf;
432   }
433
434   return VDP_INVALID_HANDLE;
435 }
436
437 VdpVideoSurface CVideoSurfaces::GetAtIndex(int idx)
438 {
439   if (idx >= m_state.size())
440     return VDP_INVALID_HANDLE;
441
442   std::map<VdpVideoSurface, int>::iterator it = m_state.begin();
443   for(int i = 0; i < idx; i++)
444     ++it;
445   return it->first;
446 }
447
448 VdpVideoSurface CVideoSurfaces::RemoveNext(bool skiprender)
449 {
450   CSingleLock lock(m_section);
451   VdpVideoSurface surf;
452   std::map<VdpVideoSurface, int>::iterator it;
453   for(it = m_state.begin(); it != m_state.end(); ++it)
454   {
455     if (skiprender && it->second & SURFACE_USED_FOR_RENDER)
456       continue;
457     surf = it->first;
458     m_state.erase(surf);
459
460     std::list<VdpVideoSurface>::iterator it2;
461     it2 = std::find(m_freeSurfaces.begin(), m_freeSurfaces.end(), surf);
462     if (it2 != m_freeSurfaces.end())
463       m_freeSurfaces.erase(it2);
464     return surf;
465   }
466   return VDP_INVALID_HANDLE;
467 }
468
469 void CVideoSurfaces::Reset()
470 {
471   CSingleLock lock(m_section);
472   m_freeSurfaces.clear();
473   m_state.clear();
474 }
475
476 int CVideoSurfaces::Size()
477 {
478   CSingleLock lock(m_section);
479   return m_state.size();
480 }
481
482 //-----------------------------------------------------------------------------
483 // CVDPAU
484 //-----------------------------------------------------------------------------
485
486 CDecoder::CDecoder() : m_vdpauOutput(&m_inMsgEvent)
487 {
488   m_vdpauConfig.videoSurfaces = &m_videoSurfaces;
489
490   m_vdpauConfigured = false;
491   m_hwContext.bitstream_buffers_allocated = 0;
492   m_DisplayState = VDPAU_OPEN;
493   m_vdpauConfig.context = 0;
494 }
495
496 bool CDecoder::Open(AVCodecContext* avctx, const enum PixelFormat fmt, unsigned int surfaces)
497 {
498   // check if user wants to decode this format with VDPAU
499   std::string gpuvendor = g_Windowing.GetRenderVendor();
500   std::transform(gpuvendor.begin(), gpuvendor.end(), gpuvendor.begin(), ::tolower);
501   // nvidia is whitelisted despite for mpeg-4 we need to query user settings
502   if ((gpuvendor.compare(0, 6, "nvidia") != 0)  || (avctx->codec_id == AV_CODEC_ID_MPEG4) || (avctx->codec_id == AV_CODEC_ID_H263))
503   {
504     if (CDVDVideoCodec::IsCodecDisabled(g_vdpau_available, settings_count, avctx->codec_id))
505       return false;
506   }
507
508 #ifndef GL_NV_vdpau_interop
509   CLog::Log(LOGNOTICE, "VDPAU: compilation without required extension GL_NV_vdpau_interop");
510   return false;
511 #endif
512   if (!g_Windowing.IsExtSupported("GL_NV_vdpau_interop"))
513   {
514     CLog::Log(LOGNOTICE, "VDPAU::Open: required extension GL_NV_vdpau_interop not found");
515     return false;
516   }
517
518   if(avctx->coded_width  == 0
519   || avctx->coded_height == 0)
520   {
521     CLog::Log(LOGWARNING,"VDPAU::Open: no width/height available, can't init");
522     return false;
523   }
524   m_vdpauConfig.numRenderBuffers = surfaces;
525   m_decoderThread = CThread::GetCurrentThreadId();
526
527   if (!CVDPAUContext::EnsureContext(&m_vdpauConfig.context))
528     return false;
529
530   m_DisplayState = VDPAU_OPEN;
531   m_vdpauConfigured = false;
532
533   if (!m_dllAvUtil.Load())
534     return false;
535
536   m_presentPicture = 0;
537
538   {
539     VdpDecoderProfile profile = 0;
540
541     // convert FFMPEG codec ID to VDPAU profile.
542     ReadFormatOf(avctx->codec_id, profile, m_vdpauConfig.vdpChromaType);
543     if(profile)
544     {
545       VdpStatus vdp_st;
546       VdpBool is_supported = false;
547       uint32_t max_level, max_macroblocks, max_width, max_height;
548
549       // query device capabilities to ensure that VDPAU can handle the requested codec
550       vdp_st = m_vdpauConfig.context->GetProcs().vdp_decoder_query_caps(m_vdpauConfig.context->GetDevice(),
551                profile, &is_supported, &max_level, &max_macroblocks, &max_width, &max_height);
552
553       // test to make sure there is a possibility the codec will work
554       if (CheckStatus(vdp_st, __LINE__))
555       {
556         CLog::Log(LOGERROR, "VDPAU::Open: error %s(%d) checking for decoder support", m_vdpauConfig.context->GetProcs().vdp_get_error_string(vdp_st), vdp_st);
557         return false;
558       }
559
560       if (max_width < avctx->coded_width || max_height < avctx->coded_height)
561       {
562         CLog::Log(LOGWARNING,"VDPAU::Open: requested picture dimensions (%i, %i) exceed hardware capabilities ( %i, %i).",
563                               avctx->coded_width, avctx->coded_height, max_width, max_height);
564         return false;
565       }
566
567       if (!CDVDCodecUtils::IsVP3CompatibleWidth(avctx->coded_width))
568         CLog::Log(LOGWARNING,"VDPAU::Open width %i might not be supported because of hardware bug", avctx->width);
569    
570       // attempt to create a decoder with this width/height, some sizes are not supported by hw
571       vdp_st = m_vdpauConfig.context->GetProcs().vdp_decoder_create(m_vdpauConfig.context->GetDevice(), profile, avctx->coded_width, avctx->coded_height, 5, &m_vdpauConfig.vdpDecoder);
572
573       if (CheckStatus(vdp_st, __LINE__))
574       {
575         CLog::Log(LOGERROR, "VDPAU::Open: error: %s(%d) checking for decoder support", m_vdpauConfig.context->GetProcs().vdp_get_error_string(vdp_st), vdp_st);
576         return false;
577       }
578
579       m_vdpauConfig.context->GetProcs().vdp_decoder_destroy(m_vdpauConfig.vdpDecoder);
580       CheckStatus(vdp_st, __LINE__);
581
582       // finally setup ffmpeg
583       memset(&m_hwContext, 0, sizeof(AVVDPAUContext));
584       m_hwContext.render = CDecoder::Render;
585       m_hwContext.bitstream_buffers_allocated = 0;
586       avctx->get_buffer      = CDecoder::FFGetBuffer;
587       avctx->reget_buffer    = CDecoder::FFGetBuffer;
588       avctx->release_buffer  = CDecoder::FFReleaseBuffer;
589       avctx->draw_horiz_band = CDecoder::FFDrawSlice;
590       avctx->slice_flags=SLICE_FLAG_CODED_ORDER|SLICE_FLAG_ALLOW_FIELD;
591       avctx->hwaccel_context = &m_hwContext;
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   m_processPicture.crop = false;
2417   if (m_mixerInput[1].DVDPic.format == RENDER_FMT_VDPAU)
2418   {
2419     m_processPicture.outputSurface = m_outputSurfaces.front();
2420     m_mixerInput[1].DVDPic.iWidth = m_config.outWidth;
2421     m_mixerInput[1].DVDPic.iHeight = m_config.outHeight;
2422     if (m_SeenInterlaceFlag)
2423     {
2424       double ratio = (double)m_mixerInput[1].DVDPic.iDisplayHeight / m_mixerInput[1].DVDPic.iHeight;
2425       m_mixerInput[1].DVDPic.iDisplayHeight = lrint(ratio*(m_mixerInput[1].DVDPic.iHeight-NUM_CROP_PIX*2));
2426       m_processPicture.crop = true;
2427     }
2428   }
2429   else
2430   {
2431     m_mixerInput[1].DVDPic.iWidth = m_config.vidWidth;
2432     m_mixerInput[1].DVDPic.iHeight = m_config.vidHeight;
2433   }
2434
2435   m_processPicture.DVDPic = m_mixerInput[1].DVDPic;
2436   m_processPicture.videoSurface = m_mixerInput[1].videoSurface;
2437 }
2438
2439 void CMixer::FiniCycle()
2440 {
2441   // Keep video surfaces for one 2 cycles longer than used
2442   // by mixer. This avoids blocking in decoder.
2443   // NVidia recommends num_ref + 5
2444   while (m_mixerInput.size() > 5)
2445   {
2446     CVdpauDecodedPicture &tmp = m_mixerInput.back();
2447     if (m_processPicture.DVDPic.format != RENDER_FMT_VDPAU_420)
2448     {
2449       m_config.videoSurfaces->ClearRender(tmp.videoSurface);
2450     }
2451     m_mixerInput.pop_back();
2452   }
2453 }
2454
2455 void CMixer::ProcessPicture()
2456 {
2457   if (m_processPicture.DVDPic.format == RENDER_FMT_VDPAU_420)
2458     return;
2459
2460   VdpStatus vdp_st;
2461
2462   if (m_mixerstep == 1)
2463   {
2464     if(m_mixerfield == VDP_VIDEO_MIXER_PICTURE_STRUCTURE_TOP_FIELD)
2465       m_mixerfield = VDP_VIDEO_MIXER_PICTURE_STRUCTURE_BOTTOM_FIELD;
2466     else
2467       m_mixerfield = VDP_VIDEO_MIXER_PICTURE_STRUCTURE_TOP_FIELD;
2468   }
2469
2470   VdpVideoSurface past_surfaces[4] = { VDP_INVALID_HANDLE, VDP_INVALID_HANDLE, VDP_INVALID_HANDLE, VDP_INVALID_HANDLE };
2471   VdpVideoSurface futu_surfaces[2] = { VDP_INVALID_HANDLE, VDP_INVALID_HANDLE };
2472   uint32_t pastCount = 4;
2473   uint32_t futuCount = 2;
2474
2475   if(m_mixerfield == VDP_VIDEO_MIXER_PICTURE_STRUCTURE_FRAME)
2476   {
2477     // use only 2 past 1 future for progressive/weave
2478     // (only used for postproc anyway eg noise reduction)
2479     if (m_mixerInput.size() > 3)
2480       past_surfaces[1] = m_mixerInput[3].videoSurface;
2481     if (m_mixerInput.size() > 2)
2482       past_surfaces[0] = m_mixerInput[2].videoSurface;
2483     futu_surfaces[0] = m_mixerInput[0].videoSurface;
2484     pastCount = 2;
2485     futuCount = 1;
2486   }
2487   else
2488   {
2489     if(m_mixerstep == 0)
2490     { // first field
2491       if (m_mixerInput.size() > 3)
2492       {
2493         past_surfaces[3] = m_mixerInput[3].videoSurface;
2494         past_surfaces[2] = m_mixerInput[3].videoSurface;
2495       }
2496       if (m_mixerInput.size() > 2)
2497       {
2498         past_surfaces[1] = m_mixerInput[2].videoSurface;
2499         past_surfaces[0] = m_mixerInput[2].videoSurface;
2500       }
2501       futu_surfaces[0] = m_mixerInput[1].videoSurface;
2502       futu_surfaces[1] = m_mixerInput[0].videoSurface;
2503     }
2504     else
2505     { // second field
2506       if (m_mixerInput.size() > 3)
2507       {
2508         past_surfaces[3] = m_mixerInput[3].videoSurface;
2509       }
2510       if (m_mixerInput.size() > 2)
2511       {
2512         past_surfaces[2] = m_mixerInput[2].videoSurface;
2513         past_surfaces[1] = m_mixerInput[2].videoSurface;
2514       }
2515       past_surfaces[0] = m_mixerInput[1].videoSurface;
2516       futu_surfaces[0] = m_mixerInput[0].videoSurface;
2517       futu_surfaces[1] = m_mixerInput[0].videoSurface;
2518
2519       if (m_mixerInput[0].DVDPic.pts != DVD_NOPTS_VALUE &&
2520           m_mixerInput[1].DVDPic.pts != DVD_NOPTS_VALUE)
2521       {
2522         m_processPicture.DVDPic.pts = m_mixerInput[1].DVDPic.pts +
2523                                      (m_mixerInput[0].DVDPic.pts -
2524                                       m_mixerInput[1].DVDPic.pts) / 2;
2525       }
2526       else
2527         m_processPicture.DVDPic.pts = DVD_NOPTS_VALUE;
2528       m_processPicture.DVDPic.dts = DVD_NOPTS_VALUE;
2529     }
2530     m_processPicture.DVDPic.iRepeatPicture = 0.0;
2531   } // interlaced
2532
2533   VdpRect sourceRect;
2534   sourceRect.x0 = 0;
2535   sourceRect.y0 = 0;
2536   sourceRect.x1 = m_config.vidWidth;
2537   sourceRect.y1 = m_config.vidHeight;
2538
2539   VdpRect destRect;
2540   destRect.x0 = 0;
2541   destRect.y0 = 0;
2542   destRect.x1 = m_config.outWidth;
2543   destRect.y1 = m_config.outHeight;
2544
2545   // start vdpau video mixer
2546   vdp_st = m_config.context->GetProcs().vdp_video_mixer_render(m_videoMixer,
2547                                 VDP_INVALID_HANDLE,
2548                                 0,
2549                                 m_mixerfield,
2550                                 pastCount,
2551                                 past_surfaces,
2552                                 m_mixerInput[1].videoSurface,
2553                                 futuCount,
2554                                 futu_surfaces,
2555                                 &sourceRect,
2556                                 m_processPicture.outputSurface,
2557                                 &destRect,
2558                                 &destRect,
2559                                 0,
2560                                 NULL);
2561   CheckStatus(vdp_st, __LINE__);
2562 }
2563
2564
2565 bool CMixer::CheckStatus(VdpStatus vdp_st, int line)
2566 {
2567   if (vdp_st != VDP_STATUS_OK)
2568   {
2569     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);
2570     m_vdpError = true;
2571     return true;
2572   }
2573   return false;
2574 }
2575
2576 //-----------------------------------------------------------------------------
2577 // Buffer Pool
2578 //-----------------------------------------------------------------------------
2579
2580 VdpauBufferPool::VdpauBufferPool()
2581 {
2582   CVdpauRenderPicture *pic;
2583   for (unsigned int i = 0; i < NUM_RENDER_PICS; i++)
2584   {
2585     pic = new CVdpauRenderPicture(renderPicSec);
2586     allRenderPics.push_back(pic);
2587   }
2588 }
2589
2590 VdpauBufferPool::~VdpauBufferPool()
2591 {
2592   CVdpauRenderPicture *pic;
2593   for (unsigned int i = 0; i < NUM_RENDER_PICS; i++)
2594   {
2595     pic = allRenderPics[i];
2596     delete pic;
2597   }
2598   allRenderPics.clear();
2599 }
2600
2601 //-----------------------------------------------------------------------------
2602 // Output
2603 //-----------------------------------------------------------------------------
2604 COutput::COutput(CEvent *inMsgEvent) :
2605   CThread("Vdpau Output"),
2606   m_controlPort("OutputControlPort", inMsgEvent, &m_outMsgEvent),
2607   m_dataPort("OutputDataPort", inMsgEvent, &m_outMsgEvent),
2608   m_mixer(&m_outMsgEvent)
2609 {
2610   m_inMsgEvent = inMsgEvent;
2611
2612   for (unsigned int i = 0; i < m_bufferPool.allRenderPics.size(); ++i)
2613   {
2614     m_bufferPool.freeRenderPics.push_back(i);
2615   }
2616 }
2617
2618 void COutput::Start()
2619 {
2620   Create();
2621 }
2622
2623 COutput::~COutput()
2624 {
2625   Dispose();
2626
2627   m_bufferPool.freeRenderPics.clear();
2628   m_bufferPool.usedRenderPics.clear();
2629 }
2630
2631 void COutput::Dispose()
2632 {
2633   CSingleLock lock(g_graphicsContext);
2634   m_bStop = true;
2635   m_outMsgEvent.Set();
2636   StopThread();
2637   m_controlPort.Purge();
2638   m_dataPort.Purge();
2639 }
2640
2641 void COutput::OnStartup()
2642 {
2643   CLog::Log(LOGNOTICE, "COutput::OnStartup: Output Thread created");
2644 }
2645
2646 void COutput::OnExit()
2647 {
2648   CLog::Log(LOGNOTICE, "COutput::OnExit: Output Thread terminated");
2649 }
2650
2651 enum OUTPUT_STATES
2652 {
2653   O_TOP = 0,                      // 0
2654   O_TOP_ERROR,                    // 1
2655   O_TOP_UNCONFIGURED,             // 2
2656   O_TOP_CONFIGURED,               // 3
2657   O_TOP_CONFIGURED_IDLE,          // 4
2658   O_TOP_CONFIGURED_WORK,          // 5
2659 };
2660
2661 int VDPAU_OUTPUT_parentStates[] = {
2662     -1,
2663     0, //TOP_ERROR
2664     0, //TOP_UNCONFIGURED
2665     0, //TOP_CONFIGURED
2666     3, //TOP_CONFIGURED_IDLE
2667     3, //TOP_CONFIGURED_WORK
2668 };
2669
2670 void COutput::StateMachine(int signal, Protocol *port, Message *msg)
2671 {
2672   for (int state = m_state; ; state = VDPAU_OUTPUT_parentStates[state])
2673   {
2674     switch (state)
2675     {
2676     case O_TOP: // TOP
2677       if (port == &m_controlPort)
2678       {
2679         switch (signal)
2680         {
2681         case COutputControlProtocol::FLUSH:
2682           msg->Reply(COutputControlProtocol::ACC);
2683           return;
2684         case COutputControlProtocol::PRECLEANUP:
2685           msg->Reply(COutputControlProtocol::ACC);
2686           return;
2687         default:
2688           break;
2689         }
2690       }
2691       else if (port == &m_dataPort)
2692       {
2693         switch (signal)
2694         {
2695         case COutputDataProtocol::RETURNPIC:
2696           CVdpauRenderPicture *pic;
2697           pic = *((CVdpauRenderPicture**)msg->data);
2698           QueueReturnPicture(pic);
2699           return;
2700         default:
2701           break;
2702         }
2703       }
2704       {
2705         std::string portName = port == NULL ? "timer" : port->portName;
2706         CLog::Log(LOGWARNING, "COutput::%s - signal: %d form port: %s not handled for state: %d", __FUNCTION__, signal, portName.c_str(), m_state);
2707       }
2708       return;
2709
2710     case O_TOP_ERROR:
2711       break;
2712
2713     case O_TOP_UNCONFIGURED:
2714       if (port == &m_controlPort)
2715       {
2716         switch (signal)
2717         {
2718         case COutputControlProtocol::INIT:
2719           CVdpauConfig *data;
2720           data = (CVdpauConfig*)msg->data;
2721           if (data)
2722           {
2723             m_config = *data;
2724           }
2725           Init();
2726           Message *reply;
2727           if (m_mixer.m_controlPort.SendOutMessageSync(CMixerControlProtocol::INIT,
2728                                      &reply, 1000, &m_config, sizeof(m_config)))
2729           {
2730             if (reply->signal != CMixerControlProtocol::ACC)
2731               m_vdpError = true;
2732             reply->Release();
2733           }
2734
2735           // set initial number of
2736           m_bufferPool.numOutputSurfaces = 4;
2737           EnsureBufferPool();
2738           if (!m_vdpError)
2739           {
2740             m_state = O_TOP_CONFIGURED_IDLE;
2741             msg->Reply(COutputControlProtocol::ACC, &m_config, sizeof(m_config));
2742           }
2743           else
2744           {
2745             m_state = O_TOP_ERROR;
2746             msg->Reply(COutputControlProtocol::ERROR);
2747           }
2748           return;
2749         default:
2750           break;
2751         }
2752       }
2753       break;
2754
2755     case O_TOP_CONFIGURED:
2756       if (port == &m_controlPort)
2757       {
2758         switch (signal)
2759         {
2760         case COutputControlProtocol::FLUSH:
2761           Flush();
2762           msg->Reply(COutputControlProtocol::ACC);
2763           return;
2764         case COutputControlProtocol::PRECLEANUP:
2765           Flush();
2766           PreCleanup();
2767           msg->Reply(COutputControlProtocol::ACC);
2768           return;
2769         default:
2770           break;
2771         }
2772       }
2773       else if (port == &m_dataPort)
2774       {
2775         switch (signal)
2776         {
2777         case COutputDataProtocol::NEWFRAME:
2778           CVdpauDecodedPicture *frame;
2779           frame = (CVdpauDecodedPicture*)msg->data;
2780           if (frame)
2781           {
2782             m_mixer.m_dataPort.SendOutMessage(CMixerDataProtocol::FRAME,
2783                                                frame,sizeof(CVdpauDecodedPicture));
2784           }
2785           return;
2786         case COutputDataProtocol::RETURNPIC:
2787           CVdpauRenderPicture *pic;
2788           pic = *((CVdpauRenderPicture**)msg->data);
2789           QueueReturnPicture(pic);
2790           m_controlPort.SendInMessage(COutputControlProtocol::STATS);
2791           m_state = O_TOP_CONFIGURED_WORK;
2792           m_extTimeout = 0;
2793           return;
2794         default:
2795           break;
2796         }
2797       }
2798       else if (port == &m_mixer.m_dataPort)
2799       {
2800         switch (signal)
2801         {
2802         case CMixerDataProtocol::PICTURE:
2803           CVdpauProcessedPicture *pic;
2804           pic = (CVdpauProcessedPicture*)msg->data;
2805           m_bufferPool.processedPics.push(*pic);
2806           m_state = O_TOP_CONFIGURED_WORK;
2807           m_extTimeout = 0;
2808           return;
2809         default:
2810           break;
2811         }
2812       }
2813       break;
2814
2815     case O_TOP_CONFIGURED_IDLE:
2816       if (port == NULL) // timeout
2817       {
2818         switch (signal)
2819         {
2820         case COutputControlProtocol::TIMEOUT:
2821           if (ProcessSyncPicture())
2822             m_extTimeout = 10;
2823           else
2824             m_extTimeout = 100;
2825           if (HasWork())
2826           {
2827             m_state = O_TOP_CONFIGURED_WORK;
2828             m_extTimeout = 0;
2829           }
2830           return;
2831         default:
2832           break;
2833         }
2834       }
2835       break;
2836
2837     case O_TOP_CONFIGURED_WORK:
2838       if (port == NULL) // timeout
2839       {
2840         switch (signal)
2841         {
2842         case COutputControlProtocol::TIMEOUT:
2843           if (HasWork())
2844           {
2845             CVdpauRenderPicture *pic;
2846             pic = ProcessMixerPicture();
2847             if (pic)
2848             {
2849               m_config.stats->DecProcessed();
2850               m_config.stats->IncRender();
2851               m_dataPort.SendInMessage(COutputDataProtocol::PICTURE, &pic, sizeof(pic));
2852             }
2853             m_extTimeout = 1;
2854           }
2855           else
2856           {
2857             m_state = O_TOP_CONFIGURED_IDLE;
2858             m_extTimeout = 0;
2859           }
2860           return;
2861         default:
2862           break;
2863         }
2864       }
2865       break;
2866
2867     default: // we are in no state, should not happen
2868       CLog::Log(LOGERROR, "COutput::%s - no valid state: %d", __FUNCTION__, m_state);
2869       return;
2870     }
2871   } // for
2872 }
2873
2874 void COutput::Process()
2875 {
2876   Message *msg = NULL;
2877   Protocol *port = NULL;
2878   bool gotMsg;
2879
2880   m_state = O_TOP_UNCONFIGURED;
2881   m_extTimeout = 1000;
2882   m_bStateMachineSelfTrigger = false;
2883
2884   while (!m_bStop)
2885   {
2886     gotMsg = false;
2887
2888     if (m_bStateMachineSelfTrigger)
2889     {
2890       m_bStateMachineSelfTrigger = false;
2891       // self trigger state machine
2892       StateMachine(msg->signal, port, msg);
2893       if (!m_bStateMachineSelfTrigger)
2894       {
2895         msg->Release();
2896         msg = NULL;
2897       }
2898       continue;
2899     }
2900     // check control port
2901     else if (m_controlPort.ReceiveOutMessage(&msg))
2902     {
2903       gotMsg = true;
2904       port = &m_controlPort;
2905     }
2906     // check data port
2907     else if (m_dataPort.ReceiveOutMessage(&msg))
2908     {
2909       gotMsg = true;
2910       port = &m_dataPort;
2911     }
2912     // check mixer data port
2913     else if (m_mixer.m_dataPort.ReceiveInMessage(&msg))
2914     {
2915       gotMsg = true;
2916       port = &m_mixer.m_dataPort;
2917     }
2918     if (gotMsg)
2919     {
2920       StateMachine(msg->signal, port, msg);
2921       if (!m_bStateMachineSelfTrigger)
2922       {
2923         msg->Release();
2924         msg = NULL;
2925       }
2926       continue;
2927     }
2928
2929     // wait for message
2930     else if (m_outMsgEvent.WaitMSec(m_extTimeout))
2931     {
2932       continue;
2933     }
2934     // time out
2935     else
2936     {
2937       msg = m_controlPort.GetMessage();
2938       msg->signal = COutputControlProtocol::TIMEOUT;
2939       port = 0;
2940       // signal timeout to state machine
2941       StateMachine(msg->signal, port, msg);
2942       if (!m_bStateMachineSelfTrigger)
2943       {
2944         msg->Release();
2945         msg = NULL;
2946       }
2947     }
2948   }
2949   Flush();
2950   Uninit();
2951 }
2952
2953 bool COutput::Init()
2954 {
2955   if (!CreateGlxContext())
2956     return false;
2957
2958   if (!GLInit())
2959     return false;
2960
2961   m_mixer.Start();
2962   m_vdpError = false;
2963
2964   return true;
2965 }
2966
2967 bool COutput::Uninit()
2968 {
2969   m_mixer.Dispose();
2970   glFlush();
2971   while(ProcessSyncPicture())
2972   {
2973     Sleep(10);
2974   }
2975   GLUnmapSurfaces();
2976   ReleaseBufferPool();
2977   DestroyGlxContext();
2978   return true;
2979 }
2980
2981 void COutput::Flush()
2982 {
2983   if (m_mixer.IsActive())
2984   {
2985     Message *reply;
2986     if (m_mixer.m_controlPort.SendOutMessageSync(CMixerControlProtocol::FLUSH,
2987                                                  &reply,
2988                                                  2000))
2989     {
2990       reply->Release();
2991     }
2992     else
2993       CLog::Log(LOGERROR, "Coutput::%s - failed to flush mixer", __FUNCTION__);
2994   }
2995
2996   Message *msg;
2997   while (m_mixer.m_dataPort.ReceiveInMessage(&msg))
2998   {
2999     if (msg->signal == CMixerDataProtocol::PICTURE)
3000     {
3001       CVdpauProcessedPicture pic = *(CVdpauProcessedPicture*)msg->data;
3002       m_bufferPool.processedPics.push(pic);
3003     }
3004     msg->Release();
3005   }
3006
3007   while (m_dataPort.ReceiveOutMessage(&msg))
3008   {
3009     if (msg->signal == COutputDataProtocol::NEWFRAME)
3010     {
3011       CVdpauDecodedPicture pic = *(CVdpauDecodedPicture*)msg->data;
3012       m_config.videoSurfaces->ClearRender(pic.videoSurface);
3013     }
3014     else if (msg->signal == COutputDataProtocol::RETURNPIC)
3015     {
3016       CVdpauRenderPicture *pic;
3017       pic = *((CVdpauRenderPicture**)msg->data);
3018       QueueReturnPicture(pic);
3019     }
3020     msg->Release();
3021   }
3022
3023   while (m_dataPort.ReceiveInMessage(&msg))
3024   {
3025     if (msg->signal == COutputDataProtocol::PICTURE)
3026     {
3027       CVdpauRenderPicture *pic;
3028       pic = *((CVdpauRenderPicture**)msg->data);
3029       QueueReturnPicture(pic);
3030     }
3031   }
3032
3033   // reset used render flag which was cleared on mixer flush
3034   std::deque<int>::iterator it;
3035   for (it = m_bufferPool.usedRenderPics.begin(); it != m_bufferPool.usedRenderPics.end(); ++it)
3036   {
3037     CVdpauRenderPicture *pic = m_bufferPool.allRenderPics[*it];
3038     if (pic->DVDPic.format == RENDER_FMT_VDPAU_420)
3039     {
3040       std::map<VdpVideoSurface, VdpauBufferPool::GLVideoSurface>::iterator it2;
3041       it2 = m_bufferPool.glVideoSurfaceMap.find(pic->sourceIdx);
3042       if (it2 == m_bufferPool.glVideoSurfaceMap.end())
3043       {
3044         CLog::Log(LOGDEBUG, "COutput::Flush - gl surface not found");
3045         continue;
3046       }
3047       m_config.videoSurfaces->MarkRender(it2->second.sourceVuv);
3048     }
3049   }
3050
3051   // clear processed pics
3052   while(!m_bufferPool.processedPics.empty())
3053   {
3054     CVdpauProcessedPicture procPic = m_bufferPool.processedPics.front();
3055     if (procPic.DVDPic.format == RENDER_FMT_VDPAU)
3056     {
3057       m_mixer.m_dataPort.SendOutMessage(CMixerDataProtocol::BUFFER, &procPic.outputSurface, sizeof(procPic.outputSurface));
3058     }
3059     else if (procPic.DVDPic.format == RENDER_FMT_VDPAU_420)
3060     {
3061       m_config.videoSurfaces->ClearRender(procPic.videoSurface);
3062     }
3063     m_bufferPool.processedPics.pop();
3064   }
3065 }
3066
3067 bool COutput::HasWork()
3068 {
3069   if (!m_bufferPool.processedPics.empty() && !m_bufferPool.freeRenderPics.empty())
3070     return true;
3071   return false;
3072 }
3073
3074 CVdpauRenderPicture* COutput::ProcessMixerPicture()
3075 {
3076   CVdpauRenderPicture *retPic = NULL;
3077
3078   if (!m_bufferPool.processedPics.empty() && !m_bufferPool.freeRenderPics.empty())
3079   {
3080     int idx = m_bufferPool.freeRenderPics.front();
3081     retPic = m_bufferPool.allRenderPics[idx];
3082     m_bufferPool.freeRenderPics.pop_front();
3083     m_bufferPool.usedRenderPics.push_back(idx);
3084     CVdpauProcessedPicture procPic = m_bufferPool.processedPics.front();
3085     m_bufferPool.processedPics.pop();
3086
3087     retPic->DVDPic = procPic.DVDPic;
3088     retPic->valid = true;
3089     if (retPic->DVDPic.format == RENDER_FMT_VDPAU)
3090     {
3091       m_config.useInteropYuv = false;
3092       m_bufferPool.numOutputSurfaces = NUM_RENDER_PICS;
3093       EnsureBufferPool();
3094       GLMapSurface(false, procPic.outputSurface);
3095       retPic->sourceIdx = procPic.outputSurface;
3096       retPic->texture[0] = m_bufferPool.glOutputSurfaceMap[procPic.outputSurface].texture[0];
3097       retPic->texWidth = m_config.outWidth;
3098       retPic->texHeight = m_config.outHeight;
3099       retPic->crop.x1 = 0;
3100       retPic->crop.y1 = procPic.crop ? NUM_CROP_PIX : 0;
3101       retPic->crop.x2 = m_config.outWidth;
3102       retPic->crop.y2 = m_config.outHeight - retPic->crop.y1;
3103     }
3104     else
3105     {
3106       m_config.useInteropYuv = true;
3107       GLMapSurface(true, procPic.videoSurface);
3108       retPic->sourceIdx = procPic.videoSurface;
3109       for (unsigned int i=0; i<4; ++i)
3110         retPic->texture[i] = m_bufferPool.glVideoSurfaceMap[procPic.videoSurface].texture[i];
3111       retPic->texWidth = m_config.surfaceWidth;
3112       retPic->texHeight = m_config.surfaceHeight;
3113       retPic->crop.x1 = 0;
3114       retPic->crop.y1 = 0;
3115       retPic->crop.x2 = m_config.surfaceWidth - m_config.vidWidth;
3116       retPic->crop.y2 = m_config.surfaceHeight - m_config.vidHeight;
3117     }
3118   }
3119   return retPic;
3120 }
3121
3122 void COutput::QueueReturnPicture(CVdpauRenderPicture *pic)
3123 {
3124   std::deque<int>::iterator it;
3125   for (it = m_bufferPool.usedRenderPics.begin(); it != m_bufferPool.usedRenderPics.end(); ++it)
3126   {
3127     if (m_bufferPool.allRenderPics[*it] == pic)
3128     {
3129       break;
3130     }
3131   }
3132
3133   if (it == m_bufferPool.usedRenderPics.end())
3134   {
3135     CLog::Log(LOGWARNING, "COutput::QueueReturnPicture - pic not found");
3136     return;
3137   }
3138
3139   // check if already queued
3140   std::deque<int>::iterator it2 = find(m_bufferPool.syncRenderPics.begin(),
3141                                        m_bufferPool.syncRenderPics.end(),
3142                                        *it);
3143   if (it2 == m_bufferPool.syncRenderPics.end())
3144   {
3145     m_bufferPool.syncRenderPics.push_back(*it);
3146   }
3147
3148   ProcessSyncPicture();
3149 }
3150
3151 bool COutput::ProcessSyncPicture()
3152 {
3153   CVdpauRenderPicture *pic;
3154   bool busy = false;
3155
3156   std::deque<int>::iterator it;
3157   for (it = m_bufferPool.syncRenderPics.begin(); it != m_bufferPool.syncRenderPics.end(); )
3158   {
3159     pic = m_bufferPool.allRenderPics[*it];
3160
3161 #ifdef GL_ARB_sync
3162     if (pic->usefence)
3163     {
3164       if (glIsSync(pic->fence))
3165       {
3166         GLint state;
3167         GLsizei length;
3168         glGetSynciv(pic->fence, GL_SYNC_STATUS, 1, &length, &state);
3169         if(state == GL_SIGNALED)
3170         {
3171           glDeleteSync(pic->fence);
3172           pic->fence = None;
3173         }
3174         else
3175         {
3176           busy = true;
3177           ++it;
3178           continue;
3179         }
3180       }
3181     }
3182 #endif
3183
3184     m_bufferPool.freeRenderPics.push_back(*it);
3185
3186     std::deque<int>::iterator it2 = find(m_bufferPool.usedRenderPics.begin(),
3187                                          m_bufferPool.usedRenderPics.end(),
3188                                          *it);
3189     if (it2 == m_bufferPool.usedRenderPics.end())
3190     {
3191       CLog::Log(LOGERROR, "COutput::ProcessSyncPicture - pic not found in queue");
3192     }
3193     else
3194     {
3195       m_bufferPool.usedRenderPics.erase(it2);
3196     }
3197     it = m_bufferPool.syncRenderPics.erase(it);
3198
3199     if (pic->valid)
3200     {
3201       ProcessReturnPicture(pic);
3202     }
3203     else
3204     {
3205       CLog::Log(LOGDEBUG, "COutput::%s - return of invalid render pic", __FUNCTION__);
3206     }
3207   }
3208   return busy;
3209 }
3210
3211 void COutput::ProcessReturnPicture(CVdpauRenderPicture *pic)
3212 {
3213   if (pic->DVDPic.format == RENDER_FMT_VDPAU_420)
3214   {
3215     std::map<VdpVideoSurface, VdpauBufferPool::GLVideoSurface>::iterator it;
3216     it = m_bufferPool.glVideoSurfaceMap.find(pic->sourceIdx);
3217     if (it == m_bufferPool.glVideoSurfaceMap.end())
3218     {
3219       CLog::Log(LOGDEBUG, "COutput::ProcessReturnPicture - gl surface not found");
3220       return;
3221     }
3222 #ifdef GL_NV_vdpau_interop
3223     glVDPAUUnmapSurfacesNV(1, &(it->second.glVdpauSurface));
3224 #endif
3225     VdpVideoSurface surf = it->second.sourceVuv;
3226     m_config.videoSurfaces->ClearRender(surf);
3227   }
3228   else if (pic->DVDPic.format == RENDER_FMT_VDPAU)
3229   {
3230     std::map<VdpOutputSurface, VdpauBufferPool::GLVideoSurface>::iterator it;
3231     it = m_bufferPool.glOutputSurfaceMap.find(pic->sourceIdx);
3232     if (it == m_bufferPool.glOutputSurfaceMap.end())
3233     {
3234       CLog::Log(LOGDEBUG, "COutput::ProcessReturnPicture - gl surface not found");
3235       return;
3236     }
3237 #ifdef GL_NV_vdpau_interop
3238     glVDPAUUnmapSurfacesNV(1, &(it->second.glVdpauSurface));
3239 #endif
3240     VdpOutputSurface outSurf = it->second.sourceRgb;
3241     m_mixer.m_dataPort.SendOutMessage(CMixerDataProtocol::BUFFER, &outSurf, sizeof(outSurf));
3242   }
3243 }
3244
3245 bool COutput::EnsureBufferPool()
3246 {
3247   VdpStatus vdp_st;
3248
3249   // Creation of outputSurfaces
3250   VdpOutputSurface outputSurface;
3251   for (int i = m_bufferPool.outputSurfaces.size(); i < m_bufferPool.numOutputSurfaces; i++)
3252   {
3253     vdp_st = m_config.context->GetProcs().vdp_output_surface_create(m_config.context->GetDevice(),
3254                                       VDP_RGBA_FORMAT_B8G8R8A8,
3255                                       m_config.outWidth,
3256                                       m_config.outHeight,
3257                                       &outputSurface);
3258     if (CheckStatus(vdp_st, __LINE__))
3259       return false;
3260     m_bufferPool.outputSurfaces.push_back(outputSurface);
3261
3262     m_mixer.m_dataPort.SendOutMessage(CMixerDataProtocol::BUFFER,
3263                                       &outputSurface,
3264                                       sizeof(VdpOutputSurface));
3265     CLog::Log(LOGNOTICE, "VDPAU::COutput::InitBufferPool - Output Surface created");
3266   }
3267   return true;
3268 }
3269
3270 void COutput::ReleaseBufferPool()
3271 {
3272   VdpStatus vdp_st;
3273
3274   CSingleLock lock(m_bufferPool.renderPicSec);
3275
3276   // release all output surfaces
3277   for (unsigned int i = 0; i < m_bufferPool.outputSurfaces.size(); ++i)
3278   {
3279     if (m_bufferPool.outputSurfaces[i] == VDP_INVALID_HANDLE)
3280       continue;
3281     vdp_st = m_config.context->GetProcs().vdp_output_surface_destroy(m_bufferPool.outputSurfaces[i]);
3282     CheckStatus(vdp_st, __LINE__);
3283   }
3284   m_bufferPool.outputSurfaces.clear();
3285
3286   // wait for all fences
3287   XbmcThreads::EndTime timeout(1000);
3288   for (unsigned int i = 0; i < m_bufferPool.allRenderPics.size(); i++)
3289   {
3290     CVdpauRenderPicture *pic = m_bufferPool.allRenderPics[i];
3291     if (pic->usefence)
3292     {
3293 #ifdef GL_ARB_sync
3294       while (glIsSync(pic->fence))
3295       {
3296         GLint state;
3297         GLsizei length;
3298         glGetSynciv(pic->fence, GL_SYNC_STATUS, 1, &length, &state);
3299         if(state == GL_SIGNALED || timeout.IsTimePast())
3300         {
3301           glDeleteSync(pic->fence);
3302         }
3303         else
3304         {
3305           Sleep(5);
3306         }
3307       }
3308       pic->fence = None;
3309 #endif
3310     }
3311   }
3312   if (timeout.IsTimePast())
3313   {
3314     CLog::Log(LOGERROR, "COutput::%s - timeout waiting for fence", __FUNCTION__);
3315   }
3316   ProcessSyncPicture();
3317
3318   // invalidate all used render pictures
3319   for (unsigned int i = 0; i < m_bufferPool.usedRenderPics.size(); ++i)
3320   {
3321     CVdpauRenderPicture *pic = m_bufferPool.allRenderPics[m_bufferPool.usedRenderPics[i]];
3322     pic->valid = false;
3323   }
3324 }
3325
3326 void COutput::PreCleanup()
3327 {
3328
3329   VdpStatus vdp_st;
3330
3331   m_mixer.Dispose();
3332   ProcessSyncPicture();
3333
3334   CSingleLock lock(m_bufferPool.renderPicSec);
3335   for (unsigned int i = 0; i < m_bufferPool.outputSurfaces.size(); ++i)
3336   {
3337     if (m_bufferPool.outputSurfaces[i] == VDP_INVALID_HANDLE)
3338       continue;
3339
3340     // check if output surface is in use
3341     bool used = false;
3342     std::deque<int>::iterator it;
3343     CVdpauRenderPicture *pic;
3344     for (it = m_bufferPool.usedRenderPics.begin(); it != m_bufferPool.usedRenderPics.end(); ++it)
3345     {
3346       pic = m_bufferPool.allRenderPics[*it];
3347       if ((pic->sourceIdx == m_bufferPool.outputSurfaces[i]) && pic->valid)
3348       {
3349         used = true;
3350         break;
3351       }
3352     }
3353     if (used)
3354       continue;
3355
3356 #ifdef GL_NV_vdpau_interop
3357     // unmap surface
3358     std::map<VdpOutputSurface, VdpauBufferPool::GLVideoSurface>::iterator it_map;
3359     it_map = m_bufferPool.glOutputSurfaceMap.find(m_bufferPool.outputSurfaces[i]);
3360     if (it_map == m_bufferPool.glOutputSurfaceMap.end())
3361     {
3362       CLog::Log(LOGERROR, "%s - could not find gl surface", __FUNCTION__);
3363       continue;
3364     }
3365     glVDPAUUnregisterSurfaceNV(it_map->second.glVdpauSurface);
3366     glDeleteTextures(1, it_map->second.texture);
3367     m_bufferPool.glOutputSurfaceMap.erase(it_map);
3368 #endif
3369
3370     vdp_st = m_config.context->GetProcs().vdp_output_surface_destroy(m_bufferPool.outputSurfaces[i]);
3371     CheckStatus(vdp_st, __LINE__);
3372
3373     m_bufferPool.outputSurfaces[i] = VDP_INVALID_HANDLE;
3374
3375     CLog::Log(LOGDEBUG, "VDPAU::PreCleanup - released output surface");
3376   }
3377
3378 }
3379
3380 void COutput::InitMixer()
3381 {
3382   for (unsigned int i = 0; i < m_bufferPool.outputSurfaces.size(); ++i)
3383   {
3384     m_mixer.m_dataPort.SendOutMessage(CMixerDataProtocol::BUFFER,
3385                                       &m_bufferPool.outputSurfaces[i],
3386                                       sizeof(VdpOutputSurface));
3387   }
3388 }
3389
3390 bool COutput::GLInit()
3391 {
3392 #ifdef GL_NV_vdpau_interop
3393   glVDPAUInitNV = NULL;
3394   glVDPAUFiniNV = NULL;
3395   glVDPAURegisterOutputSurfaceNV = NULL;
3396   glVDPAURegisterVideoSurfaceNV = NULL;
3397   glVDPAUIsSurfaceNV = NULL;
3398   glVDPAUUnregisterSurfaceNV = NULL;
3399   glVDPAUSurfaceAccessNV = NULL;
3400   glVDPAUMapSurfacesNV = NULL;
3401   glVDPAUUnmapSurfacesNV = NULL;
3402   glVDPAUGetSurfaceivNV = NULL;
3403 #endif
3404
3405 #ifdef GL_NV_vdpau_interop
3406     if (!glVDPAUInitNV)
3407       glVDPAUInitNV    = (PFNGLVDPAUINITNVPROC)glXGetProcAddress((GLubyte *) "glVDPAUInitNV");
3408     if (!glVDPAUFiniNV)
3409       glVDPAUFiniNV = (PFNGLVDPAUFININVPROC)glXGetProcAddress((GLubyte *) "glVDPAUFiniNV");
3410     if (!glVDPAURegisterOutputSurfaceNV)
3411       glVDPAURegisterOutputSurfaceNV    = (PFNGLVDPAUREGISTEROUTPUTSURFACENVPROC)glXGetProcAddress((GLubyte *) "glVDPAURegisterOutputSurfaceNV");
3412     if (!glVDPAURegisterVideoSurfaceNV)
3413       glVDPAURegisterVideoSurfaceNV    = (PFNGLVDPAUREGISTERVIDEOSURFACENVPROC)glXGetProcAddress((GLubyte *) "glVDPAURegisterVideoSurfaceNV");
3414     if (!glVDPAUIsSurfaceNV)
3415       glVDPAUIsSurfaceNV    = (PFNGLVDPAUISSURFACENVPROC)glXGetProcAddress((GLubyte *) "glVDPAUIsSurfaceNV");
3416     if (!glVDPAUUnregisterSurfaceNV)
3417       glVDPAUUnregisterSurfaceNV = (PFNGLVDPAUUNREGISTERSURFACENVPROC)glXGetProcAddress((GLubyte *) "glVDPAUUnregisterSurfaceNV");
3418     if (!glVDPAUSurfaceAccessNV)
3419       glVDPAUSurfaceAccessNV    = (PFNGLVDPAUSURFACEACCESSNVPROC)glXGetProcAddress((GLubyte *) "glVDPAUSurfaceAccessNV");
3420     if (!glVDPAUMapSurfacesNV)
3421       glVDPAUMapSurfacesNV = (PFNGLVDPAUMAPSURFACESNVPROC)glXGetProcAddress((GLubyte *) "glVDPAUMapSurfacesNV");
3422     if (!glVDPAUUnmapSurfacesNV)
3423       glVDPAUUnmapSurfacesNV = (PFNGLVDPAUUNMAPSURFACESNVPROC)glXGetProcAddress((GLubyte *) "glVDPAUUnmapSurfacesNV");
3424     if (!glVDPAUGetSurfaceivNV)
3425       glVDPAUGetSurfaceivNV = (PFNGLVDPAUGETSURFACEIVNVPROC)glXGetProcAddress((GLubyte *) "glVDPAUGetSurfaceivNV");
3426
3427   while (glGetError() != GL_NO_ERROR);
3428   glVDPAUInitNV(reinterpret_cast<void*>(m_config.context->GetDevice()), reinterpret_cast<void*>(m_config.context->GetProcs().vdp_get_proc_address));
3429   if (glGetError() != GL_NO_ERROR)
3430   {
3431     CLog::Log(LOGERROR, "VDPAU::COutput - GLInitInterop glVDPAUInitNV failed");
3432     m_vdpError = true;
3433     return false;
3434   }
3435   CLog::Log(LOGNOTICE, "VDPAU::COutput: vdpau gl interop initialized");
3436 #endif
3437
3438 #ifdef GL_ARB_sync
3439   bool hasfence = glewIsSupported("GL_ARB_sync");
3440   for (unsigned int i = 0; i < m_bufferPool.allRenderPics.size(); i++)
3441   {
3442     m_bufferPool.allRenderPics[i]->usefence = hasfence;
3443   }
3444 #endif
3445
3446   return true;
3447 }
3448
3449 void COutput::GLMapSurface(bool yuv, uint32_t source)
3450 {
3451 #ifdef GL_NV_vdpau_interop
3452
3453   if (yuv)
3454   {
3455     std::map<VdpVideoSurface, VdpauBufferPool::GLVideoSurface>::iterator it;
3456     it = m_bufferPool.glVideoSurfaceMap.find(source);
3457     if (it == m_bufferPool.glVideoSurfaceMap.end())
3458     {
3459       VdpauBufferPool::GLVideoSurface glSurface;
3460       VdpVideoSurface surf = source;
3461
3462       if (surf == VDP_INVALID_HANDLE)
3463         return;
3464
3465       glSurface.sourceVuv = surf;
3466       while (glGetError() != GL_NO_ERROR) ;
3467       glGenTextures(4, glSurface.texture);
3468       if (glGetError() != GL_NO_ERROR)
3469       {
3470         CLog::Log(LOGERROR, "VDPAU::COutput error creating texture");
3471         m_vdpError = true;
3472       }
3473       glSurface.glVdpauSurface = glVDPAURegisterVideoSurfaceNV(reinterpret_cast<void*>(surf),
3474                                                     GL_TEXTURE_2D, 4, glSurface.texture);
3475
3476       if (glGetError() != GL_NO_ERROR)
3477       {
3478         CLog::Log(LOGERROR, "VDPAU::COutput error register video surface");
3479         m_vdpError = true;
3480       }
3481       glVDPAUSurfaceAccessNV(glSurface.glVdpauSurface, GL_READ_ONLY);
3482       if (glGetError() != GL_NO_ERROR)
3483       {
3484         CLog::Log(LOGERROR, "VDPAU::COutput error setting access");
3485         m_vdpError = true;
3486       }
3487       m_bufferPool.glVideoSurfaceMap[surf] = glSurface;
3488
3489       CLog::Log(LOGNOTICE, "VDPAU::COutput registered surface");
3490     }
3491
3492     while (glGetError() != GL_NO_ERROR) ;
3493     glVDPAUMapSurfacesNV(1, &m_bufferPool.glVideoSurfaceMap[source].glVdpauSurface);
3494     if (glGetError() != GL_NO_ERROR)
3495     {
3496       CLog::Log(LOGERROR, "VDPAU::COutput error mapping surface");
3497       m_vdpError = true;
3498     }
3499
3500     if (m_vdpError)
3501       return;
3502   }
3503   else
3504   {
3505     std::map<VdpOutputSurface, VdpauBufferPool::GLVideoSurface>::iterator it;
3506     it = m_bufferPool.glOutputSurfaceMap.find(source);
3507     if (it == m_bufferPool.glOutputSurfaceMap.end())
3508     {
3509       unsigned int idx = 0;
3510       for (idx = 0; idx<m_bufferPool.outputSurfaces.size(); idx++)
3511       {
3512         if (m_bufferPool.outputSurfaces[idx] == source)
3513           break;
3514       }
3515
3516       VdpauBufferPool::GLVideoSurface glSurface;
3517       glSurface.sourceRgb = m_bufferPool.outputSurfaces[idx];
3518       glGenTextures(1, glSurface.texture);
3519       glSurface.glVdpauSurface = glVDPAURegisterOutputSurfaceNV(reinterpret_cast<void*>(m_bufferPool.outputSurfaces[idx]),
3520                                                GL_TEXTURE_2D, 1, glSurface.texture);
3521       if (glGetError() != GL_NO_ERROR)
3522       {
3523         CLog::Log(LOGERROR, "VDPAU::COutput error register output surface");
3524         m_vdpError = true;
3525       }
3526       glVDPAUSurfaceAccessNV(glSurface.glVdpauSurface, GL_READ_ONLY);
3527       if (glGetError() != GL_NO_ERROR)
3528       {
3529         CLog::Log(LOGERROR, "VDPAU::COutput error setting access");
3530         m_vdpError = true;
3531       }
3532       m_bufferPool.glOutputSurfaceMap[source] = glSurface;
3533       CLog::Log(LOGNOTICE, "VDPAU::COutput registered output surfaces");
3534     }
3535
3536     while (glGetError() != GL_NO_ERROR) ;
3537     glVDPAUMapSurfacesNV(1, &m_bufferPool.glOutputSurfaceMap[source].glVdpauSurface);
3538     if (glGetError() != GL_NO_ERROR)
3539     {
3540       CLog::Log(LOGERROR, "VDPAU::COutput error mapping surface");
3541       m_vdpError = true;
3542     }
3543
3544     if (m_vdpError)
3545       return;
3546   }
3547 #endif
3548 }
3549
3550 void COutput::GLUnmapSurfaces()
3551 {
3552 #ifdef GL_NV_vdpau_interop
3553
3554   {
3555     std::map<VdpVideoSurface, VdpauBufferPool::GLVideoSurface>::iterator it;
3556     for (it = m_bufferPool.glVideoSurfaceMap.begin(); it != m_bufferPool.glVideoSurfaceMap.end(); ++it)
3557     {
3558       glVDPAUUnregisterSurfaceNV(it->second.glVdpauSurface);
3559       glDeleteTextures(4, it->second.texture);
3560     }
3561     m_bufferPool.glVideoSurfaceMap.clear();
3562   }
3563
3564   std::map<VdpOutputSurface, VdpauBufferPool::GLVideoSurface>::iterator it;
3565   for (it = m_bufferPool.glOutputSurfaceMap.begin(); it != m_bufferPool.glOutputSurfaceMap.end(); ++it)
3566   {
3567     glVDPAUUnregisterSurfaceNV(it->second.glVdpauSurface);
3568     glDeleteTextures(1, it->second.texture);
3569   }
3570   m_bufferPool.glOutputSurfaceMap.clear();
3571
3572   glVDPAUFiniNV();
3573
3574   CLog::Log(LOGNOTICE, "VDPAU::COutput: vdpau gl interop finished");
3575
3576 #endif
3577 }
3578
3579 bool COutput::CheckStatus(VdpStatus vdp_st, int line)
3580 {
3581   if (vdp_st != VDP_STATUS_OK)
3582   {
3583     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);
3584     m_vdpError = true;
3585     return true;
3586   }
3587   return false;
3588 }
3589
3590 bool COutput::CreateGlxContext()
3591 {
3592   GLXContext   glContext;
3593
3594   m_Display = g_Windowing.GetDisplay();
3595   glContext = g_Windowing.GetGlxContext();
3596   m_Window = g_Windowing.GetWindow();
3597
3598   // Get our window attribs.
3599   XWindowAttributes wndattribs;
3600   XGetWindowAttributes(m_Display, m_Window, &wndattribs);
3601
3602   // Get visual Info
3603   XVisualInfo visInfo;
3604   visInfo.visualid = wndattribs.visual->visualid;
3605   int nvisuals = 0;
3606   XVisualInfo* visuals = XGetVisualInfo(m_Display, VisualIDMask, &visInfo, &nvisuals);
3607   if (nvisuals != 1)
3608   {
3609     CLog::Log(LOGERROR, "VDPAU::COutput::CreateGlxContext - could not find visual");
3610     return false;
3611   }
3612   visInfo = visuals[0];
3613   XFree(visuals);
3614
3615   m_pixmap = XCreatePixmap(m_Display,
3616                            m_Window,
3617                            192,
3618                            108,
3619                            visInfo.depth);
3620   if (!m_pixmap)
3621   {
3622     CLog::Log(LOGERROR, "VDPAU::COutput::CreateGlxContext - Unable to create XPixmap");
3623     return false;
3624   }
3625
3626   // create gl pixmap
3627   m_glPixmap = glXCreateGLXPixmap(m_Display, &visInfo, m_pixmap);
3628
3629   if (!m_glPixmap)
3630   {
3631     CLog::Log(LOGINFO, "VDPAU::COutput::CreateGlxContext - Could not create glPixmap");
3632     return false;
3633   }
3634
3635   m_glContext = glXCreateContext(m_Display, &visInfo, glContext, True);
3636
3637   if (!glXMakeCurrent(m_Display, m_glPixmap, m_glContext))
3638   {
3639     CLog::Log(LOGINFO, "VDPAU::COutput::CreateGlxContext - Could not make Pixmap current");
3640     return false;
3641   }
3642
3643   CLog::Log(LOGNOTICE, "VDPAU::COutput::CreateGlxContext - created context");
3644   return true;
3645 }
3646
3647 bool COutput::DestroyGlxContext()
3648 {
3649   if (m_glContext)
3650   {
3651     glXMakeCurrent(m_Display, None, NULL);
3652     glXDestroyContext(m_Display, m_glContext);
3653   }
3654   m_glContext = 0;
3655
3656   if (m_glPixmap)
3657     glXDestroyPixmap(m_Display, m_glPixmap);
3658   m_glPixmap = 0;
3659
3660   if (m_pixmap)
3661     XFreePixmap(m_Display, m_pixmap);
3662   m_pixmap = 0;
3663
3664   return true;
3665 }
3666
3667 #endif