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