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