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