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