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