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