Merge pull request #5101 from FernetMenta/ffmpeg-threads
[vuplus_xbmc] / xbmc / cores / dvdplayer / DVDCodecs / Video / VDPAU.cpp
index f830f61..73c4fa4 100644 (file)
 #include "settings/MediaSettings.h"
 #include "Application.h"
 #include "utils/MathUtils.h"
+#include "utils/TimeUtils.h"
 #include "DVDCodecs/DVDCodecUtils.h"
+#include "cores/VideoRenderers/RenderFlags.h"
+
+using namespace VDPAU;
+#define NUM_RENDER_PICS 7
+#define NUM_CROP_PIX 3
 
 #define ARSIZE(x) (sizeof(x) / sizeof((x)[0]))
 
-CVDPAU::Desc decoder_profiles[] = {
+// settings codecs mapping
+DVDCodecAvailableType g_vdpau_available[] = {
+  { AV_CODEC_ID_H263, "videoplayer.usevdpaumpeg4" },
+  { AV_CODEC_ID_MPEG4, "videoplayer.usevdpaumpeg4" },
+  { AV_CODEC_ID_WMV3, "videoplayer.usevdpauvc1" },
+  { AV_CODEC_ID_VC1, "videoplayer.usevdpauvc1" },
+  { AV_CODEC_ID_MPEG2VIDEO, "videoplayer.usevdpaumpeg2" },
+};
+const size_t settings_count = sizeof(g_vdpau_available) / sizeof(DVDCodecAvailableType);
+
+CDecoder::Desc decoder_profiles[] = {
 {"MPEG1",        VDP_DECODER_PROFILE_MPEG1},
 {"MPEG2_SIMPLE", VDP_DECODER_PROFILE_MPEG2_SIMPLE},
 {"MPEG2_MAIN",   VDP_DECODER_PROFILE_MPEG2_MAIN},
@@ -47,18 +63,9 @@ CVDPAU::Desc decoder_profiles[] = {
 {"VC1_SIMPLE",   VDP_DECODER_PROFILE_VC1_SIMPLE},
 {"VC1_MAIN",     VDP_DECODER_PROFILE_VC1_MAIN},
 {"VC1_ADVANCED", VDP_DECODER_PROFILE_VC1_ADVANCED},
-#ifdef VDP_DECODER_PROFILE_MPEG4_PART2_ASP
 {"MPEG4_PART2_ASP", VDP_DECODER_PROFILE_MPEG4_PART2_ASP},
-#endif
-};
-const size_t decoder_profile_count = sizeof(decoder_profiles)/sizeof(CVDPAU::Desc);
-
-static float studioCSC[3][4] =
-{
-    { 1.0f,        0.0f, 1.57480000f,-0.78740000f},
-    { 1.0f,-0.18737736f,-0.46813736f, 0.32775736f},
-    { 1.0f, 1.85556000f,        0.0f,-0.92780000f}
 };
+const size_t decoder_profile_count = sizeof(decoder_profiles)/sizeof(CDecoder::Desc);
 
 static struct SInterlaceMapping
 {
@@ -69,867 +76,777 @@ static struct SInterlaceMapping
 , {VS_INTERLACEMETHOD_VDPAU_TEMPORAL_HALF        , VDP_VIDEO_MIXER_FEATURE_DEINTERLACE_TEMPORAL}
 , {VS_INTERLACEMETHOD_VDPAU_TEMPORAL_SPATIAL     , VDP_VIDEO_MIXER_FEATURE_DEINTERLACE_TEMPORAL_SPATIAL}
 , {VS_INTERLACEMETHOD_VDPAU_TEMPORAL_SPATIAL_HALF, VDP_VIDEO_MIXER_FEATURE_DEINTERLACE_TEMPORAL_SPATIAL}
-, {VS_INTERLACEMETHOD_VDPAU_INVERSE_TELECINE     , VDP_VIDEO_MIXER_FEATURE_INVERSE_TELECINE} 
+, {VS_INTERLACEMETHOD_VDPAU_INVERSE_TELECINE     , VDP_VIDEO_MIXER_FEATURE_INVERSE_TELECINE}
 , {VS_INTERLACEMETHOD_NONE                       , (VdpVideoMixerFeature)-1}
 };
 
-//since libvdpau 0.4, vdp_device_create_x11() installs a callback on the Display*,
-//if we unload libvdpau with dlclose(), we segfault on XCloseDisplay,
-//so we just keep a static handle to libvdpau around
-void* CVDPAU::dl_handle;
+static float studioCSCKCoeffs601[3] = {0.299, 0.587, 0.114}; //BT601 {Kr, Kg, Kb}
+static float studioCSCKCoeffs709[3] = {0.2126, 0.7152, 0.0722}; //BT709 {Kr, Kg, Kb}
 
-CVDPAU::CVDPAU()
-{
-  glXBindTexImageEXT = NULL;
-  glXReleaseTexImageEXT = NULL;
-  vdp_device = VDP_INVALID_HANDLE;
-  surfaceNum      = presentSurfaceNum = 0;
-  picAge.b_age    = picAge.ip_age[0] = picAge.ip_age[1] = 256*256*256*64;
-  vdpauConfigured = false;
-  m_DisplayState = VDPAU_OPEN;
-  m_mixerfield = VDP_VIDEO_MIXER_PICTURE_STRUCTURE_FRAME;
-  m_mixerstep  = 0;
+//-----------------------------------------------------------------------------
+//-----------------------------------------------------------------------------
 
-  m_glPixmap = 0;
-  m_Pixmap = 0;
-  if (!glXBindTexImageEXT)
-    glXBindTexImageEXT    = (PFNGLXBINDTEXIMAGEEXTPROC)glXGetProcAddress((GLubyte *) "glXBindTexImageEXT");
-  if (!glXReleaseTexImageEXT)
-    glXReleaseTexImageEXT = (PFNGLXRELEASETEXIMAGEEXTPROC)glXGetProcAddress((GLubyte *) "glXReleaseTexImageEXT");
-
-  totalAvailableOutputSurfaces = 0;
-  outputSurface = presentSurface = VDP_INVALID_HANDLE;
-  vdp_flip_target = VDP_INVALID_HANDLE;
-  vdp_flip_queue = VDP_INVALID_HANDLE;
-  vid_width = vid_height = OutWidth = OutHeight = 0;
-  surface_width = surface_height = 0;
-  
-  memset(&decoder, 0, sizeof(decoder));
-  memset(&outRect, 0, sizeof(outRect));
-  memset(&outRectVid, 0, sizeof(outRectVid));
-
-  m_Display = NULL;
-
-  tmpBrightness  = 0;
-  tmpContrast    = 0;
-  tmpDeintMode   = 0;
-  tmpDeintGUI    = 0;
-  tmpDeint       = 0;
-  max_references = 0;
-
-  for (int i = 0; i < NUM_OUTPUT_SURFACES; i++)
-    outputSurfaces[i] = VDP_INVALID_HANDLE;
-
-  videoMixer = VDP_INVALID_HANDLE;
-  m_BlackBar = NULL;
-
-  memset(m_features, 0, sizeof(m_features));
-  m_feature_count = 0;
-  m_vdpauOutputMethod = OUTPUT_NONE;
-
-  upScale = g_advancedSettings.m_videoVDPAUScaling;
-
-  vdp_video_mixer_set_attribute_values = NULL;
-  vdp_generate_csc_matrix = NULL;
-  vdp_presentation_queue_target_destroy = NULL;
-  vdp_presentation_queue_create = NULL;
-  vdp_presentation_queue_destroy = NULL;
-  vdp_presentation_queue_display = NULL;
-  vdp_presentation_queue_block_until_surface_idle = NULL;
-  vdp_presentation_queue_target_create_x11 = NULL;
-  vdp_presentation_queue_query_surface_status = NULL;
-  vdp_presentation_queue_get_time = NULL;
-  vdp_get_error_string = NULL;
-  vdp_decoder_create = NULL;
-  vdp_decoder_destroy = NULL;
-  vdp_decoder_render = NULL;
-  vdp_decoder_query_caps = NULL;
-  vdp_preemption_callback_register = NULL;
-  dl_vdp_device_create_x11 = NULL;
-  dl_vdp_get_proc_address = NULL;
-  dl_vdp_preemption_callback_register = NULL;
-  past[0] = NULL;
-  past[1] = NULL;
-  current = NULL;
-  future = NULL;
-  tmpNoiseReduction = 0.0f;
-  tmpSharpness = 0.0f;
-  vdp_get_proc_address = NULL;
-  vdp_device_destroy = NULL;
-  vdp_video_surface_create = NULL;
-  vdp_video_surface_destroy = NULL;
-  vdp_video_surface_put_bits_y_cb_cr = NULL;
-  vdp_video_surface_get_bits_y_cb_cr = NULL;
-  vdp_output_surface_put_bits_y_cb_cr = NULL;
-  vdp_output_surface_put_bits_native = NULL;
-  vdp_output_surface_create = NULL;
-  vdp_output_surface_destroy = NULL;
-  vdp_output_surface_get_bits_native = NULL;
-  vdp_output_surface_render_output_surface = NULL;
-  vdp_output_surface_put_bits_indexed = NULL;
-  vdp_video_mixer_create = NULL;
-  vdp_video_mixer_set_feature_enables = NULL;
-  vdp_video_mixer_query_parameter_support = NULL;
-  vdp_video_mixer_query_feature_support = NULL;
-  vdp_video_mixer_destroy = NULL;
-  vdp_video_mixer_render = NULL;
-  m_hwContext.bitstream_buffers_allocated = 0;
-}
+CVDPAUContext *CVDPAUContext::m_context = 0;
+CCriticalSection CVDPAUContext::m_section;
+Display *CVDPAUContext::m_display = 0;
+void *CVDPAUContext::m_dlHandle = 0;
 
-bool CVDPAU::Open(AVCodecContext* avctx, const enum PixelFormat, unsigned int surfaces)
+CVDPAUContext::CVDPAUContext()
 {
-  if(avctx->coded_width  == 0
-  || avctx->coded_height == 0)
-  {
-    CLog::Log(LOGWARNING,"(VDPAU) no width/height available, can't init");
-    return false;
-  }
+  m_context = 0;
+  m_refCount = 0;
+}
 
-  if ((avctx->codec_id == AV_CODEC_ID_MPEG4) && !g_advancedSettings.m_videoAllowMpeg4VDPAU)
-    return false;
+void CVDPAUContext::Release()
+{
+  CSingleLock lock(m_section);
 
-  if (!dl_handle)
+  m_refCount--;
+  if (m_refCount <= 0)
   {
-    dl_handle  = dlopen("libvdpau.so.1", RTLD_LAZY);
-    if (!dl_handle)
-    {
-      const char* error = dlerror();
-      if (!error)
-        error = "dlerror() returned NULL";
-
-      CLog::Log(LOGNOTICE,"(VDPAU) Unable to get handle to libvdpau: %s", error);
-      //g_application.m_guiDialogKaiToast.QueueNotification(CGUIDialogKaiToast::Error, "VDPAU", error, 10000);
-
-      return false;
-    }
+    Close();
+    delete this;
+    m_context = 0;
   }
+}
 
-  if (!m_dllAvUtil.Load())
-    return false;
+void CVDPAUContext::Close()
+{
+  CLog::Log(LOGNOTICE, "VDPAU::Close - closing decoder context");
+  DestroyContext();
+}
 
-  InitVDPAUProcs();
+bool CVDPAUContext::EnsureContext(CVDPAUContext **ctx)
+{
+  CSingleLock lock(m_section);
 
-  if (vdp_device != VDP_INVALID_HANDLE)
+  if (m_context)
   {
-    SpewHardwareAvailable();
+    m_context->m_refCount++;
+    *ctx = m_context;
+    return true;
+  }
 
-    VdpDecoderProfile profile = 0;
-    if(avctx->codec_id == AV_CODEC_ID_H264)
-      profile = VDP_DECODER_PROFILE_H264_HIGH;
-#ifdef VDP_DECODER_PROFILE_MPEG4_PART2_ASP
-    else if(avctx->codec_id == AV_CODEC_ID_MPEG4)
-      profile = VDP_DECODER_PROFILE_MPEG4_PART2_ASP;
-#endif
-    if(profile)
+  m_context = new CVDPAUContext();
+  *ctx = m_context;
+  {
+    CSingleLock gLock(g_graphicsContext);
+    if (!m_context->LoadSymbols() || !m_context->CreateContext())
     {
-      if (!CDVDCodecUtils::IsVP3CompatibleWidth(avctx->coded_width))
-        CLog::Log(LOGWARNING,"(VDPAU) width %i might not be supported because of hardware bug", avctx->width);
-   
-      /* attempt to create a decoder with this width/height, some sizes are not supported by hw */
-      VdpStatus vdp_st;
-      vdp_st = vdp_decoder_create(vdp_device, profile, avctx->coded_width, avctx->coded_height, 5, &decoder);
-
-      if(vdp_st != VDP_STATUS_OK)
-      {
-        CLog::Log(LOGERROR, " (VDPAU) Error: %s(%d) checking for decoder support\n", vdp_get_error_string(vdp_st), vdp_st);
-        FiniVDPAUProcs();
-        return false;
-      }
-
-      vdp_decoder_destroy(decoder);
-      CheckStatus(vdp_st, __LINE__);
+      delete m_context;
+      m_context = 0;
+      *ctx = NULL;
+      return false;
     }
+  }
 
-    InitCSCMatrix(avctx->coded_height);
-
-    /* finally setup ffmpeg */
-    memset(&m_hwContext, 0, sizeof(AVVDPAUContext));
-    m_hwContext.render = CVDPAU::Render;
-    m_hwContext.bitstream_buffers_allocated = 0;
-    avctx->get_buffer      = CVDPAU::FFGetBuffer;
-    avctx->release_buffer  = CVDPAU::FFReleaseBuffer;
-    avctx->draw_horiz_band = CVDPAU::FFDrawSlice;
-    avctx->slice_flags=SLICE_FLAG_CODED_ORDER|SLICE_FLAG_ALLOW_FIELD;
-    avctx->hwaccel_context = &m_hwContext;
-    avctx->thread_count    = 1;
+  m_context->m_refCount++;
 
-    g_Windowing.Register(this);
-    return true;
-  }
-  return false;
+  *ctx = m_context;
+  return true;
 }
 
-CVDPAU::~CVDPAU()
+VDPAU_procs& CVDPAUContext::GetProcs()
 {
-  Close();
+  return m_vdpProcs;
 }
 
-void CVDPAU::Close()
+VdpVideoMixerFeature* CVDPAUContext::GetFeatures()
 {
-  CLog::Log(LOGNOTICE, " (VDPAU) %s", __FUNCTION__);
+  return m_vdpFeatures;
+}
 
-  FiniVDPAUOutput();
-  FiniVDPAUProcs();
+int CVDPAUContext::GetFeatureCount()
+{
+  return m_featureCount;
+}
 
-  while (!m_videoSurfaces.empty())
+bool CVDPAUContext::LoadSymbols()
+{
+  if (!m_dlHandle)
   {
-    vdpau_render_state *render = m_videoSurfaces.back();
-    m_videoSurfaces.pop_back();
-    if (render->bitstream_buffers_allocated)
-      m_dllAvUtil.av_freep(&render->bitstream_buffers);
-    render->bitstream_buffers_allocated = 0;
-    free(render);
+    m_dlHandle  = dlopen("libvdpau.so.1", RTLD_LAZY);
+    if (!m_dlHandle)
+    {
+      const char* error = dlerror();
+      if (!error)
+        error = "dlerror() returned NULL";
+
+      CLog::Log(LOGERROR,"VDPAU::LoadSymbols: Unable to get handle to lib: %s", error);
+      return false;
+    }
   }
 
-  if (m_hwContext.bitstream_buffers_allocated)
+  char* error;
+  (void)dlerror();
+  dl_vdp_device_create_x11 = (VdpStatus (*)(Display*, int, VdpDevice*, VdpStatus (**)(VdpDevice, VdpFuncId, void**)))dlsym(m_dlHandle, (const char*)"vdp_device_create_x11");
+  error = dlerror();
+  if (error)
   {
-    m_dllAvUtil.av_freep(&m_hwContext.bitstream_buffers);
+    CLog::Log(LOGERROR,"(VDPAU) - %s in %s",error,__FUNCTION__);
+    m_vdpDevice = VDP_INVALID_HANDLE;
+    return false;
   }
-
-  g_Windowing.Unregister(this);
-  m_dllAvUtil.Unload();
+  return true;
 }
 
-bool CVDPAU::MakePixmapGL()
-{
-  int num=0;
-  int fbConfigIndex = 0;
-
-  int doubleVisAttributes[] = {
-    GLX_RENDER_TYPE, GLX_RGBA_BIT,
-    GLX_RED_SIZE, 8,
-    GLX_GREEN_SIZE, 8,
-    GLX_BLUE_SIZE, 8,
-    GLX_ALPHA_SIZE, 8,
-    GLX_DEPTH_SIZE, 8,
-    GLX_DRAWABLE_TYPE, GLX_PIXMAP_BIT,
-    GLX_BIND_TO_TEXTURE_RGBA_EXT, True,
-    GLX_DOUBLEBUFFER, True,
-    GLX_Y_INVERTED_EXT, True,
-    GLX_X_RENDERABLE, True,
-    None
-  };
-
-  int pixmapAttribs[] = {
-    GLX_TEXTURE_TARGET_EXT, GLX_TEXTURE_2D_EXT,
-    GLX_TEXTURE_FORMAT_EXT, GLX_TEXTURE_FORMAT_RGBA_EXT,
-    None
-  };
-
-  GLXFBConfig *fbConfigs;
-  fbConfigs = glXChooseFBConfig(m_Display, DefaultScreen(m_Display), doubleVisAttributes, &num);
-  if (fbConfigs==NULL)
-  {
-    CLog::Log(LOGERROR, "GLX Error: MakePixmap: No compatible framebuffers found");
-    return false;
+bool CVDPAUContext::CreateContext()
+{
+  CLog::Log(LOGNOTICE,"VDPAU::CreateContext - creating decoder context");
+
+  int mScreen;
+  { CSingleLock lock(g_graphicsContext);
+    if (!m_display)
+      m_display = XOpenDisplay(NULL);
+    mScreen = g_Windowing.GetCurrentScreen();
   }
-  CLog::Log(LOGDEBUG, "Found %d fbconfigs.", num);
-  fbConfigIndex = 0;
-  CLog::Log(LOGDEBUG, "Using fbconfig index %d.", fbConfigIndex);
 
-  m_glPixmap = glXCreatePixmap(m_Display, fbConfigs[fbConfigIndex], m_Pixmap, pixmapAttribs);
+  VdpStatus vdp_st;
+  // Create Device
+  vdp_st = dl_vdp_device_create_x11(m_display,
+                                    mScreen,
+                                   &m_vdpDevice,
+                                   &m_vdpProcs.vdp_get_proc_address);
 
-  if (!m_glPixmap)
+  CLog::Log(LOGNOTICE,"vdp_device = 0x%08x vdp_st = 0x%08x",m_vdpDevice,vdp_st);
+  if (vdp_st != VDP_STATUS_OK)
   {
-    CLog::Log(LOGINFO, "GLX Error: Could not create Pixmap");
-    XFree(fbConfigs);
+    CLog::Log(LOGERROR,"(VDPAU) unable to init VDPAU - vdp_st = 0x%x.  Falling back.",vdp_st);
+    m_vdpDevice = VDP_INVALID_HANDLE;
     return false;
   }
-  XFree(fbConfigs);
 
+  QueryProcs();
+  SpewHardwareAvailable();
   return true;
 }
 
-bool CVDPAU::MakePixmap(int width, int height)
+void CVDPAUContext::QueryProcs()
 {
-  //pick the smallest dimensions, so we downscale with vdpau and upscale with opengl when appropriate
-  //this requires the least amount of gpu memory bandwidth
-  if (g_graphicsContext.GetWidth() < width || g_graphicsContext.GetHeight() < height || upScale)
-  {
-    //scale width to desktop size if the aspect ratio is the same or bigger than the desktop
-    if ((double)height * g_graphicsContext.GetWidth() / width <= (double)g_graphicsContext.GetHeight())
-    {
-      OutWidth = g_graphicsContext.GetWidth();
-      OutHeight = MathUtils::round_int((double)height * g_graphicsContext.GetWidth() / width);
-    }
-    else //scale height to the desktop size if the aspect ratio is smaller than the desktop
-    {
-      OutHeight = g_graphicsContext.GetHeight();
-      OutWidth = MathUtils::round_int((double)width * g_graphicsContext.GetHeight() / height);
-    }
-  }
-  else
-  { //let opengl scale
-    OutWidth = width;
-    OutHeight = height;
-  }
-
-  CLog::Log(LOGNOTICE,"Creating %ix%i pixmap", OutWidth, OutHeight);
-
-    // Get our window attribs.
-  XWindowAttributes wndattribs;
-  XGetWindowAttributes(m_Display, DefaultRootWindow(m_Display), &wndattribs); // returns a status but I don't know what success is
-
-  m_Pixmap = XCreatePixmap(m_Display,
-                           DefaultRootWindow(m_Display),
-                           OutWidth,
-                           OutHeight,
-                           wndattribs.depth);
-  if (!m_Pixmap)
-  {
-    CLog::Log(LOGERROR, "GLX Error: MakePixmap: Unable to create XPixmap");
-    return false;
-  }
+  VdpStatus vdp_st;
 
-  XGCValues values = {};
-  GC xgc;
-  values.foreground = BlackPixel (m_Display, DefaultScreen (m_Display));
-  xgc = XCreateGC(m_Display, m_Pixmap, GCForeground, &values);
-  XFillRectangle(m_Display, m_Pixmap, xgc, 0, 0, OutWidth, OutHeight);
-  XFreeGC(m_Display, xgc);
+#define VDP_PROC(id, proc) \
+  do { \
+    vdp_st = m_vdpProcs.vdp_get_proc_address(m_vdpDevice, id, (void**)&proc); \
+    if (vdp_st != VDP_STATUS_OK) \
+    { \
+      CLog::Log(LOGERROR, "CVDPAUContext::GetProcs - failed to get proc id"); \
+    } \
+  } while(0);
 
-  if(!MakePixmapGL())
-    return false;
+  VDP_PROC(VDP_FUNC_ID_GET_ERROR_STRING                    , m_vdpProcs.vdp_get_error_string);
+  VDP_PROC(VDP_FUNC_ID_DEVICE_DESTROY                      , m_vdpProcs.vdp_device_destroy);
+  VDP_PROC(VDP_FUNC_ID_GENERATE_CSC_MATRIX                 , m_vdpProcs.vdp_generate_csc_matrix);
+  VDP_PROC(VDP_FUNC_ID_VIDEO_SURFACE_CREATE                , m_vdpProcs.vdp_video_surface_create);
+  VDP_PROC(VDP_FUNC_ID_VIDEO_SURFACE_DESTROY               , m_vdpProcs.vdp_video_surface_destroy);
+  VDP_PROC(VDP_FUNC_ID_VIDEO_SURFACE_PUT_BITS_Y_CB_CR      , m_vdpProcs.vdp_video_surface_put_bits_y_cb_cr);
+  VDP_PROC(VDP_FUNC_ID_VIDEO_SURFACE_GET_BITS_Y_CB_CR      , m_vdpProcs.vdp_video_surface_get_bits_y_cb_cr);
+  VDP_PROC(VDP_FUNC_ID_OUTPUT_SURFACE_PUT_BITS_Y_CB_CR     , m_vdpProcs.vdp_output_surface_put_bits_y_cb_cr);
+  VDP_PROC(VDP_FUNC_ID_OUTPUT_SURFACE_PUT_BITS_NATIVE      , m_vdpProcs.vdp_output_surface_put_bits_native);
+  VDP_PROC(VDP_FUNC_ID_OUTPUT_SURFACE_CREATE               , m_vdpProcs.vdp_output_surface_create);
+  VDP_PROC(VDP_FUNC_ID_OUTPUT_SURFACE_DESTROY              , m_vdpProcs.vdp_output_surface_destroy);
+  VDP_PROC(VDP_FUNC_ID_OUTPUT_SURFACE_GET_BITS_NATIVE      , m_vdpProcs.vdp_output_surface_get_bits_native);
+  VDP_PROC(VDP_FUNC_ID_OUTPUT_SURFACE_RENDER_OUTPUT_SURFACE, m_vdpProcs.vdp_output_surface_render_output_surface);
+  VDP_PROC(VDP_FUNC_ID_OUTPUT_SURFACE_PUT_BITS_INDEXED     , m_vdpProcs.vdp_output_surface_put_bits_indexed);
+  VDP_PROC(VDP_FUNC_ID_VIDEO_MIXER_CREATE                  , m_vdpProcs.vdp_video_mixer_create);
+  VDP_PROC(VDP_FUNC_ID_VIDEO_MIXER_SET_FEATURE_ENABLES     , m_vdpProcs.vdp_video_mixer_set_feature_enables);
+  VDP_PROC(VDP_FUNC_ID_VIDEO_MIXER_DESTROY                 , m_vdpProcs.vdp_video_mixer_destroy);
+  VDP_PROC(VDP_FUNC_ID_VIDEO_MIXER_RENDER                  , m_vdpProcs.vdp_video_mixer_render);
+  VDP_PROC(VDP_FUNC_ID_VIDEO_MIXER_SET_ATTRIBUTE_VALUES    , m_vdpProcs.vdp_video_mixer_set_attribute_values);
+  VDP_PROC(VDP_FUNC_ID_VIDEO_MIXER_QUERY_PARAMETER_SUPPORT , m_vdpProcs.vdp_video_mixer_query_parameter_support);
+  VDP_PROC(VDP_FUNC_ID_VIDEO_MIXER_QUERY_FEATURE_SUPPORT   , m_vdpProcs.vdp_video_mixer_query_feature_support);
+  VDP_PROC(VDP_FUNC_ID_DECODER_CREATE                      , m_vdpProcs.vdp_decoder_create);
+  VDP_PROC(VDP_FUNC_ID_DECODER_DESTROY                     , m_vdpProcs.vdp_decoder_destroy);
+  VDP_PROC(VDP_FUNC_ID_DECODER_RENDER                      , m_vdpProcs.vdp_decoder_render);
+  VDP_PROC(VDP_FUNC_ID_DECODER_QUERY_CAPABILITIES          , m_vdpProcs.vdp_decoder_query_caps);
+#undef VDP_PROC
+}
 
-  return true;
+VdpDevice CVDPAUContext::GetDevice()
+{
+  return m_vdpDevice;
 }
 
-void CVDPAU::BindPixmap()
+void CVDPAUContext::DestroyContext()
 {
-  CSharedLock lock(m_DecoderSection);
+  if (!m_vdpProcs.vdp_device_destroy)
+    return;
 
-  { CSharedLock dLock(m_DisplaySection);
-    if (m_DisplayState != VDPAU_OPEN)
-      return;
-  }
+  m_vdpProcs.vdp_device_destroy(m_vdpDevice);
+  m_vdpDevice = VDP_INVALID_HANDLE;
+}
 
-  if (m_glPixmap)
+void CVDPAUContext::SpewHardwareAvailable()  //CopyrighVDPAUt (c) 2008 Wladimir J. van der Laan  -- VDPInfo
+{
+  VdpStatus rv;
+  CLog::Log(LOGNOTICE,"VDPAU Decoder capabilities:");
+  CLog::Log(LOGNOTICE,"name          level macbs width height");
+  CLog::Log(LOGNOTICE,"------------------------------------");
+  for(unsigned int x=0; x<decoder_profile_count; ++x)
   {
-    if(presentSurface != VDP_INVALID_HANDLE)
+    VdpBool is_supported = false;
+    uint32_t max_level, max_macroblocks, max_width, max_height;
+    rv = m_vdpProcs.vdp_decoder_query_caps(m_vdpDevice, decoder_profiles[x].id,
+                                &is_supported, &max_level, &max_macroblocks, &max_width, &max_height);
+    if(rv == VDP_STATUS_OK && is_supported)
     {
-      VdpPresentationQueueStatus status;
-      VdpTime time;
-      VdpStatus vdp_st;
-      vdp_st = vdp_presentation_queue_query_surface_status(
-                    vdp_flip_queue, presentSurface, &status, &time);
-      CheckStatus(vdp_st, __LINE__);
-      while(status != VDP_PRESENTATION_QUEUE_STATUS_VISIBLE && vdp_st == VDP_STATUS_OK)
-      {
-        Sleep(1);
-        vdp_st = vdp_presentation_queue_query_surface_status(
-                      vdp_flip_queue, presentSurface, &status, &time);
-        CheckStatus(vdp_st, __LINE__);
-      }
+      CLog::Log(LOGNOTICE,"%-16s %2i %5i %5i %5i\n", decoder_profiles[x].name,
+                max_level, max_macroblocks, max_width, max_height);
     }
-    
-    glXBindTexImageEXT(m_Display, m_glPixmap, GLX_FRONT_LEFT_EXT, NULL);
   }
-  else CLog::Log(LOGERROR,"(VDPAU) BindPixmap called without valid pixmap");
+  CLog::Log(LOGNOTICE,"------------------------------------");
+  m_featureCount = 0;
+#define CHECK_SUPPORT(feature)  \
+  do { \
+    VdpBool supported; \
+    if(m_vdpProcs.vdp_video_mixer_query_feature_support(m_vdpDevice, feature, &supported) == VDP_STATUS_OK && supported) { \
+      CLog::Log(LOGNOTICE, "Mixer feature: "#feature);  \
+      m_vdpFeatures[m_featureCount++] = feature; \
+    } \
+  } while(false)
+
+  CHECK_SUPPORT(VDP_VIDEO_MIXER_FEATURE_NOISE_REDUCTION);
+  CHECK_SUPPORT(VDP_VIDEO_MIXER_FEATURE_SHARPNESS);
+  CHECK_SUPPORT(VDP_VIDEO_MIXER_FEATURE_DEINTERLACE_TEMPORAL);
+  CHECK_SUPPORT(VDP_VIDEO_MIXER_FEATURE_DEINTERLACE_TEMPORAL_SPATIAL);
+  CHECK_SUPPORT(VDP_VIDEO_MIXER_FEATURE_INVERSE_TELECINE);
+#ifdef VDP_VIDEO_MIXER_FEATURE_HIGH_QUALITY_SCALING_L1
+  CHECK_SUPPORT(VDP_VIDEO_MIXER_FEATURE_HIGH_QUALITY_SCALING_L1);
+  CHECK_SUPPORT(VDP_VIDEO_MIXER_FEATURE_HIGH_QUALITY_SCALING_L2);
+  CHECK_SUPPORT(VDP_VIDEO_MIXER_FEATURE_HIGH_QUALITY_SCALING_L3);
+  CHECK_SUPPORT(VDP_VIDEO_MIXER_FEATURE_HIGH_QUALITY_SCALING_L4);
+  CHECK_SUPPORT(VDP_VIDEO_MIXER_FEATURE_HIGH_QUALITY_SCALING_L5);
+  CHECK_SUPPORT(VDP_VIDEO_MIXER_FEATURE_HIGH_QUALITY_SCALING_L6);
+  CHECK_SUPPORT(VDP_VIDEO_MIXER_FEATURE_HIGH_QUALITY_SCALING_L7);
+  CHECK_SUPPORT(VDP_VIDEO_MIXER_FEATURE_HIGH_QUALITY_SCALING_L8);
+  CHECK_SUPPORT(VDP_VIDEO_MIXER_FEATURE_HIGH_QUALITY_SCALING_L9);
+#endif
+#undef CHECK_SUPPORT
 }
 
-void CVDPAU::ReleasePixmap()
+bool CVDPAUContext::Supports(VdpVideoMixerFeature feature)
 {
-  CSharedLock lock(m_DecoderSection);
-
-  { CSharedLock dLock(m_DisplaySection);
-    if (m_DisplayState != VDPAU_OPEN)
-      return;
-  }
-
-  if (m_glPixmap)
+  for(int i = 0; i < m_featureCount; i++)
   {
-    glXReleaseTexImageEXT(m_Display, m_glPixmap, GLX_FRONT_LEFT_EXT);
+    if(m_vdpFeatures[i] == feature)
+      return true;
   }
-  else CLog::Log(LOGERROR,"(VDPAU) ReleasePixmap called without valid pixmap");
+  return false;
 }
 
-void CVDPAU::OnLostDevice()
-{
-  CLog::Log(LOGNOTICE,"CVDPAU::OnLostDevice event");
+//-----------------------------------------------------------------------------
+// VDPAU Video Surface states
+//-----------------------------------------------------------------------------
 
-  CExclusiveLock lock(m_DecoderSection);
-  FiniVDPAUOutput();
-  FiniVDPAUProcs();
+#define SURFACE_USED_FOR_REFERENCE 0x01
+#define SURFACE_USED_FOR_RENDER    0x02
 
-  m_DisplayState = VDPAU_LOST;
-  m_DisplayEvent.Reset();
+void CVideoSurfaces::AddSurface(VdpVideoSurface surf)
+{
+  CSingleLock lock(m_section);
+  m_state[surf] = SURFACE_USED_FOR_REFERENCE;
 }
 
-void CVDPAU::OnResetDevice()
+void CVideoSurfaces::ClearReference(VdpVideoSurface surf)
 {
-  CLog::Log(LOGNOTICE,"CVDPAU::OnResetDevice event");
-
-  CExclusiveLock lock(m_DisplaySection);
-  if (m_DisplayState == VDPAU_LOST)
+  CSingleLock lock(m_section);
+  if (m_state.find(surf) == m_state.end())
   {
-    m_DisplayState = VDPAU_RESET;
-    m_DisplayEvent.Set();
+    CLog::Log(LOGWARNING, "CVideoSurfaces::ClearReference - surface invalid");
+    return;
+  }
+  m_state[surf] &= ~SURFACE_USED_FOR_REFERENCE;
+  if (m_state[surf] == 0)
+  {
+    m_freeSurfaces.push_back(surf);
   }
 }
 
-int CVDPAU::Check(AVCodecContext* avctx)
+bool CVideoSurfaces::MarkRender(VdpVideoSurface surf)
 {
-  EDisplayState state;
+  CSingleLock lock(m_section);
+  if (m_state.find(surf) == m_state.end())
+  {
+    CLog::Log(LOGWARNING, "CVideoSurfaces::MarkRender - surface invalid");
+    return false;
+  }
+  std::list<VdpVideoSurface>::iterator it;
+  it = std::find(m_freeSurfaces.begin(), m_freeSurfaces.end(), surf);
+  if (it != m_freeSurfaces.end())
+  {
+    m_freeSurfaces.erase(it);
+  }
+  m_state[surf] |= SURFACE_USED_FOR_RENDER;
+  return true;
+}
 
-  { CSharedLock lock(m_DisplaySection);
-    state = m_DisplayState;
+void CVideoSurfaces::ClearRender(VdpVideoSurface surf)
+{
+  CSingleLock lock(m_section);
+  if (m_state.find(surf) == m_state.end())
+  {
+    CLog::Log(LOGWARNING, "CVideoSurfaces::ClearRender - surface invalid");
+    return;
+  }
+  m_state[surf] &= ~SURFACE_USED_FOR_RENDER;
+  if (m_state[surf] == 0)
+  {
+    m_freeSurfaces.push_back(surf);
   }
+}
 
-  if (state == VDPAU_LOST)
+bool CVideoSurfaces::IsValid(VdpVideoSurface surf)
+{
+  CSingleLock lock(m_section);
+  if (m_state.find(surf) != m_state.end())
+    return true;
+  else
+    return false;
+}
+
+VdpVideoSurface CVideoSurfaces::GetFree(VdpVideoSurface surf)
+{
+  CSingleLock lock(m_section);
+  if (m_state.find(surf) != m_state.end())
   {
-    CLog::Log(LOGNOTICE,"CVDPAU::Check waiting for display reset event");
-    if (!m_DisplayEvent.WaitMSec(2000))
+    std::list<VdpVideoSurface>::iterator it;
+    it = std::find(m_freeSurfaces.begin(), m_freeSurfaces.end(), surf);
+    if (it == m_freeSurfaces.end())
     {
-      CLog::Log(LOGERROR, "CVDPAU::Check - device didn't reset in reasonable time");
-      state = VDPAU_RESET;
+      CLog::Log(LOGWARNING, "CVideoSurfaces::GetFree - surface not free");
     }
     else
     {
-      CSharedLock lock(m_DisplaySection);
-      state = m_DisplayState;
+      m_freeSurfaces.erase(it);
+      m_state[surf] = SURFACE_USED_FOR_REFERENCE;
+      return surf;
     }
   }
-  if (state == VDPAU_RESET || state == VDPAU_ERROR)
+
+  if (!m_freeSurfaces.empty())
   {
-    CLog::Log(LOGNOTICE,"Attempting recovery");
+    VdpVideoSurface freeSurf = m_freeSurfaces.front();
+    m_freeSurfaces.pop_front();
+    m_state[freeSurf] = SURFACE_USED_FOR_REFERENCE;
+    return freeSurf;
+  }
 
-    CSingleLock gLock(g_graphicsContext);
-    CExclusiveLock lock(m_DecoderSection);
+  return VDP_INVALID_HANDLE;
+}
 
-    FiniVDPAUOutput();
-    FiniVDPAUProcs();
+VdpVideoSurface CVideoSurfaces::GetAtIndex(int idx)
+{
+  if (idx >= m_state.size())
+    return VDP_INVALID_HANDLE;
 
-    InitVDPAUProcs();
+  std::map<VdpVideoSurface, int>::iterator it = m_state.begin();
+  for(int i = 0; i < idx; i++)
+    ++it;
+  return it->first;
+}
 
-    if (state == VDPAU_RESET)
-      return VC_FLUSHED;
-    else
-      return VC_ERROR;
-  }
-  return 0;
+VdpVideoSurface CVideoSurfaces::RemoveNext(bool skiprender)
+{
+  CSingleLock lock(m_section);
+  VdpVideoSurface surf;
+  std::map<VdpVideoSurface, int>::iterator it;
+  for(it = m_state.begin(); it != m_state.end(); ++it)
+  {
+    if (skiprender && it->second & SURFACE_USED_FOR_RENDER)
+      continue;
+    surf = it->first;
+    m_state.erase(surf);
+
+    std::list<VdpVideoSurface>::iterator it2;
+    it2 = std::find(m_freeSurfaces.begin(), m_freeSurfaces.end(), surf);
+    if (it2 != m_freeSurfaces.end())
+      m_freeSurfaces.erase(it2);
+    return surf;
+  }
+  return VDP_INVALID_HANDLE;
 }
 
-bool CVDPAU::IsVDPAUFormat(PixelFormat format)
+void CVideoSurfaces::Reset()
 {
-  if (format == AV_PIX_FMT_VDPAU)
-    return true;
-  else
-    return false;
+  CSingleLock lock(m_section);
+  m_freeSurfaces.clear();
+  m_state.clear();
 }
 
-void CVDPAU::CheckFeatures()
-{
-  if (videoMixer == VDP_INVALID_HANDLE)
-  {
-    CLog::Log(LOGNOTICE, " (VDPAU) Creating the video mixer");
-    // Creation of VideoMixer.
-    VdpVideoMixerParameter parameters[] = {
-      VDP_VIDEO_MIXER_PARAMETER_VIDEO_SURFACE_WIDTH,
-      VDP_VIDEO_MIXER_PARAMETER_VIDEO_SURFACE_HEIGHT,
-      VDP_VIDEO_MIXER_PARAMETER_CHROMA_TYPE
-    };
-
-    void const * parameter_values[] = {
-      &surface_width,
-      &surface_height,
-      &vdp_chroma_type
-    };
-
-    tmpBrightness = 0;
-    tmpContrast = 0;
-    tmpNoiseReduction = 0;
-    tmpSharpness = 0;
-
-    VdpStatus vdp_st = vdp_video_mixer_create(vdp_device,
-                                    m_feature_count,
-                                    m_features,
-                                    ARSIZE(parameters),
-                                    parameters,
-                                    parameter_values,
-                                    &videoMixer);
-    CheckStatus(vdp_st, __LINE__);
+int CVideoSurfaces::Size()
+{
+  CSingleLock lock(m_section);
+  return m_state.size();
+}
 
-    SetHWUpscaling();
-  }
+//-----------------------------------------------------------------------------
+// CVDPAU
+//-----------------------------------------------------------------------------
 
-  if (tmpBrightness != CMediaSettings::Get().GetCurrentVideoSettings().m_Brightness ||
-      tmpContrast   != CMediaSettings::Get().GetCurrentVideoSettings().m_Contrast)
-  {
-    SetColor();
-    tmpBrightness = CMediaSettings::Get().GetCurrentVideoSettings().m_Brightness;
-    tmpContrast = CMediaSettings::Get().GetCurrentVideoSettings().m_Contrast;
-  }
-  if (tmpNoiseReduction != CMediaSettings::Get().GetCurrentVideoSettings().m_NoiseReduction)
-  {
-    tmpNoiseReduction = CMediaSettings::Get().GetCurrentVideoSettings().m_NoiseReduction;
-    SetNoiseReduction();
-  }
-  if (tmpSharpness != CMediaSettings::Get().GetCurrentVideoSettings().m_Sharpness)
-  {
-    tmpSharpness = CMediaSettings::Get().GetCurrentVideoSettings().m_Sharpness;
-    SetSharpness();
-  }
-  if (  tmpDeintMode != CMediaSettings::Get().GetCurrentVideoSettings().m_DeinterlaceMode ||
-        tmpDeintGUI  != CMediaSettings::Get().GetCurrentVideoSettings().m_InterlaceMethod ||
-       (tmpDeintGUI == VS_INTERLACEMETHOD_AUTO && tmpDeint != AutoInterlaceMethod()))
-  {
-    tmpDeintMode = CMediaSettings::Get().GetCurrentVideoSettings().m_DeinterlaceMode;
-    tmpDeintGUI  = CMediaSettings::Get().GetCurrentVideoSettings().m_InterlaceMethod;
-    if (tmpDeintGUI == VS_INTERLACEMETHOD_AUTO)
-      tmpDeint = AutoInterlaceMethod();
-    else
-      tmpDeint = tmpDeintGUI;
+CDecoder::CDecoder() : m_vdpauOutput(&m_inMsgEvent)
+{
+  m_vdpauConfig.videoSurfaces = &m_videoSurfaces;
 
-    SetDeinterlacing();
-  }
+  m_vdpauConfigured = false;
+  m_hwContext.bitstream_buffers_allocated = 0;
+  m_DisplayState = VDPAU_OPEN;
+  m_vdpauConfig.context = 0;
 }
 
-bool CVDPAU::Supports(VdpVideoMixerFeature feature)
+bool CDecoder::Open(AVCodecContext* avctx, const enum PixelFormat fmt, unsigned int surfaces)
 {
-  for(int i = 0; i < m_feature_count; i++)
+  // check if user wants to decode this format with VDPAU
+  std::string gpuvendor = g_Windowing.GetRenderVendor();
+  std::transform(gpuvendor.begin(), gpuvendor.end(), gpuvendor.begin(), ::tolower);
+  // nvidia is whitelisted despite for mpeg-4 we need to query user settings
+  if ((gpuvendor.compare(0, 6, "nvidia") != 0)  || (avctx->codec_id == AV_CODEC_ID_MPEG4) || (avctx->codec_id == AV_CODEC_ID_H263))
   {
-    if(m_features[i] == feature)
-      return true;
+    if (CDVDVideoCodec::IsCodecDisabled(g_vdpau_available, settings_count, avctx->codec_id))
+      return false;
   }
-  return false;
-}
 
-bool CVDPAU::Supports(EINTERLACEMETHOD method)
-{
-  if(method == VS_INTERLACEMETHOD_VDPAU_BOB
-  || method == VS_INTERLACEMETHOD_AUTO
-  || method == VS_INTERLACEMETHOD_AUTO_ION)
-    return true;
+#ifndef GL_NV_vdpau_interop
+  CLog::Log(LOGNOTICE, "VDPAU: compilation without required extension GL_NV_vdpau_interop");
+  return false;
+#endif
+  if (!g_Windowing.IsExtSupported("GL_NV_vdpau_interop"))
+  {
+    CLog::Log(LOGNOTICE, "VDPAU::Open: required extension GL_NV_vdpau_interop not found");
+    return false;
+  }
 
-  for(SInterlaceMapping* p = g_interlace_mapping; p->method != VS_INTERLACEMETHOD_NONE; p++)
+  if(avctx->coded_width  == 0
+  || avctx->coded_height == 0)
   {
-    if(p->method == method)
-      return Supports(p->feature);
+    CLog::Log(LOGWARNING,"VDPAU::Open: no width/height available, can't init");
+    return false;
   }
-  return false;
-}
+  m_vdpauConfig.numRenderBuffers = surfaces;
+  m_decoderThread = CThread::GetCurrentThreadId();
 
-EINTERLACEMETHOD CVDPAU::AutoInterlaceMethod()
-{
-  return VS_INTERLACEMETHOD_VDPAU_TEMPORAL;
-}
+  if (!CVDPAUContext::EnsureContext(&m_vdpauConfig.context))
+    return false;
 
-void CVDPAU::SetColor()
-{
-  VdpStatus vdp_st;
+  m_DisplayState = VDPAU_OPEN;
+  m_vdpauConfigured = false;
 
-  if (tmpBrightness != CMediaSettings::Get().GetCurrentVideoSettings().m_Brightness)
-    m_Procamp.brightness = (float)((CMediaSettings::Get().GetCurrentVideoSettings().m_Brightness)-50) / 100;
-  if (tmpContrast != CMediaSettings::Get().GetCurrentVideoSettings().m_Contrast)
-    m_Procamp.contrast = (float)((CMediaSettings::Get().GetCurrentVideoSettings().m_Contrast)+50) / 100;
+  if (!m_dllAvUtil.Load())
+    return false;
 
-  if(vid_height >= 600 || vid_width > 1024)
-    vdp_st = vdp_generate_csc_matrix(&m_Procamp, VDP_COLOR_STANDARD_ITUR_BT_709, &m_CSCMatrix);
-  else
-    vdp_st = vdp_generate_csc_matrix(&m_Procamp, VDP_COLOR_STANDARD_ITUR_BT_601, &m_CSCMatrix);
+  m_presentPicture = 0;
 
-  VdpVideoMixerAttribute attributes[] = { VDP_VIDEO_MIXER_ATTRIBUTE_CSC_MATRIX };
-  if (CSettings::Get().GetBool("videoscreen.limitedrange"))
-  {
-    void const * pm_CSCMatix[] = { &studioCSC };
-    vdp_st = vdp_video_mixer_set_attribute_values(videoMixer, ARSIZE(attributes), attributes, pm_CSCMatix);
-  }
-  else
   {
-    void const * pm_CSCMatix[] = { &m_CSCMatrix };
-    vdp_st = vdp_video_mixer_set_attribute_values(videoMixer, ARSIZE(attributes), attributes, pm_CSCMatix);
-  }
-  CheckStatus(vdp_st, __LINE__);
-}
+    VdpDecoderProfile profile = 0;
 
-void CVDPAU::SetNoiseReduction()
-{
-  if(!Supports(VDP_VIDEO_MIXER_FEATURE_NOISE_REDUCTION))
-    return;
+    // convert FFMPEG codec ID to VDPAU profile.
+    ReadFormatOf(avctx->codec_id, profile, m_vdpauConfig.vdpChromaType);
+    if(profile)
+    {
+      VdpStatus vdp_st;
+      VdpBool is_supported = false;
+      uint32_t max_level, max_macroblocks, max_width, max_height;
 
-  VdpVideoMixerFeature feature[] = { VDP_VIDEO_MIXER_FEATURE_NOISE_REDUCTION };
-  VdpVideoMixerAttribute attributes[] = { VDP_VIDEO_MIXER_ATTRIBUTE_NOISE_REDUCTION_LEVEL };
-  VdpStatus vdp_st;
+      // query device capabilities to ensure that VDPAU can handle the requested codec
+      vdp_st = m_vdpauConfig.context->GetProcs().vdp_decoder_query_caps(m_vdpauConfig.context->GetDevice(),
+               profile, &is_supported, &max_level, &max_macroblocks, &max_width, &max_height);
 
-  if (!CMediaSettings::Get().GetCurrentVideoSettings().m_NoiseReduction)
-  {
-    VdpBool enabled[]= {0};
-    vdp_st = vdp_video_mixer_set_feature_enables(videoMixer, ARSIZE(feature), feature, enabled);
-    CheckStatus(vdp_st, __LINE__);
-    return;
-  }
-  VdpBool enabled[]={1};
-  vdp_st = vdp_video_mixer_set_feature_enables(videoMixer, ARSIZE(feature), feature, enabled);
-  CheckStatus(vdp_st, __LINE__);
-  void* nr[] = { &CMediaSettings::Get().GetCurrentVideoSettings().m_NoiseReduction };
-  CLog::Log(LOGNOTICE,"Setting Noise Reduction to %f",CMediaSettings::Get().GetCurrentVideoSettings().m_NoiseReduction);
-  vdp_st = vdp_video_mixer_set_attribute_values(videoMixer, ARSIZE(attributes), attributes, nr);
-  CheckStatus(vdp_st, __LINE__);
-}
+      // test to make sure there is a possibility the codec will work
+      if (CheckStatus(vdp_st, __LINE__))
+      {
+        CLog::Log(LOGERROR, "VDPAU::Open: error %s(%d) checking for decoder support", m_vdpauConfig.context->GetProcs().vdp_get_error_string(vdp_st), vdp_st);
+        return false;
+      }
 
-void CVDPAU::SetSharpness()
-{
-  if(!Supports(VDP_VIDEO_MIXER_FEATURE_SHARPNESS))
-    return;
-  
-  VdpVideoMixerFeature feature[] = { VDP_VIDEO_MIXER_FEATURE_SHARPNESS };
-  VdpVideoMixerAttribute attributes[] = { VDP_VIDEO_MIXER_ATTRIBUTE_SHARPNESS_LEVEL };
-  VdpStatus vdp_st;
+      if (max_width < avctx->coded_width || max_height < avctx->coded_height)
+      {
+        CLog::Log(LOGWARNING,"VDPAU::Open: requested picture dimensions (%i, %i) exceed hardware capabilities ( %i, %i).",
+                             avctx->coded_width, avctx->coded_height, max_width, max_height);
+        return false;
+      }
 
-  if (!CMediaSettings::Get().GetCurrentVideoSettings().m_Sharpness)
-  {
-    VdpBool enabled[]={0};
-    vdp_st = vdp_video_mixer_set_feature_enables(videoMixer, ARSIZE(feature), feature, enabled);
-    CheckStatus(vdp_st, __LINE__);
-    return;
+      if (!CDVDCodecUtils::IsVP3CompatibleWidth(avctx->coded_width))
+        CLog::Log(LOGWARNING,"VDPAU::Open width %i might not be supported because of hardware bug", avctx->width);
+   
+      // attempt to create a decoder with this width/height, some sizes are not supported by hw
+      vdp_st = m_vdpauConfig.context->GetProcs().vdp_decoder_create(m_vdpauConfig.context->GetDevice(), profile, avctx->coded_width, avctx->coded_height, 5, &m_vdpauConfig.vdpDecoder);
+
+      if (CheckStatus(vdp_st, __LINE__))
+      {
+        CLog::Log(LOGERROR, "VDPAU::Open: error: %s(%d) checking for decoder support", m_vdpauConfig.context->GetProcs().vdp_get_error_string(vdp_st), vdp_st);
+        return false;
+      }
+
+      m_vdpauConfig.context->GetProcs().vdp_decoder_destroy(m_vdpauConfig.vdpDecoder);
+      CheckStatus(vdp_st, __LINE__);
+
+      // finally setup ffmpeg
+      memset(&m_hwContext, 0, sizeof(AVVDPAUContext));
+      m_hwContext.render = CDecoder::Render;
+      m_hwContext.bitstream_buffers_allocated = 0;
+      avctx->get_buffer      = CDecoder::FFGetBuffer;
+      avctx->reget_buffer    = CDecoder::FFGetBuffer;
+      avctx->release_buffer  = CDecoder::FFReleaseBuffer;
+      avctx->draw_horiz_band = CDecoder::FFDrawSlice;
+      avctx->slice_flags=SLICE_FLAG_CODED_ORDER|SLICE_FLAG_ALLOW_FIELD;
+      avctx->hwaccel_context = &m_hwContext;
+
+      g_Windowing.Register(this);
+      return true;
+    }
   }
-  VdpBool enabled[]={1};
-  vdp_st = vdp_video_mixer_set_feature_enables(videoMixer, ARSIZE(feature), feature, enabled);
-  CheckStatus(vdp_st, __LINE__);
-  void* sh[] = { &CMediaSettings::Get().GetCurrentVideoSettings().m_Sharpness };
-  CLog::Log(LOGNOTICE,"Setting Sharpness to %f",CMediaSettings::Get().GetCurrentVideoSettings().m_Sharpness);
-  vdp_st = vdp_video_mixer_set_attribute_values(videoMixer, ARSIZE(attributes), attributes, sh);
-  CheckStatus(vdp_st, __LINE__);
+  return false;
 }
 
-void CVDPAU::SetHWUpscaling()
+CDecoder::~CDecoder()
 {
-#ifdef VDP_VIDEO_MIXER_FEATURE_HIGH_QUALITY_SCALING_L1
-  if(!Supports(VDP_VIDEO_MIXER_FEATURE_HIGH_QUALITY_SCALING_L1) || !upScale)
-    return;
-
-  VdpVideoMixerFeature feature[] = { VDP_VIDEO_MIXER_FEATURE_HIGH_QUALITY_SCALING_L1 };
-  VdpStatus vdp_st;
-  VdpBool enabled[]={1};
-  vdp_st = vdp_video_mixer_set_feature_enables(videoMixer, ARSIZE(feature), feature, enabled);
-  CheckStatus(vdp_st, __LINE__);
-#endif
+  Close();
 }
 
-void CVDPAU::SetDeinterlacing()
+void CDecoder::Close()
 {
-  VdpStatus vdp_st;
-  EDEINTERLACEMODE   mode = CMediaSettings::Get().GetCurrentVideoSettings().m_DeinterlaceMode;
-  EINTERLACEMETHOD method = CMediaSettings::Get().GetCurrentVideoSettings().m_InterlaceMethod;
-  if (method == VS_INTERLACEMETHOD_AUTO)
-    method = AutoInterlaceMethod();
+  CLog::Log(LOGNOTICE, " (VDPAU) %s", __FUNCTION__);
 
-  VdpVideoMixerFeature feature[] = { VDP_VIDEO_MIXER_FEATURE_DEINTERLACE_TEMPORAL,
-                                     VDP_VIDEO_MIXER_FEATURE_DEINTERLACE_TEMPORAL_SPATIAL,
-                                     VDP_VIDEO_MIXER_FEATURE_INVERSE_TELECINE };
-  if (mode == VS_DEINTERLACEMODE_OFF)
+  g_Windowing.Unregister(this);
+
+  CSingleLock lock(m_DecoderSection);
+
+  FiniVDPAUOutput();
+  m_vdpauOutput.Dispose();
+
+  if (m_hwContext.bitstream_buffers_allocated)
   {
-    VdpBool enabled[]={0,0,0};
-    vdp_st = vdp_video_mixer_set_feature_enables(videoMixer, ARSIZE(feature), feature, enabled);
+    m_dllAvUtil.av_freep(&m_hwContext.bitstream_buffers);
   }
-  else
+
+  m_dllAvUtil.Unload();
+
+  if (m_vdpauConfig.context)
+    m_vdpauConfig.context->Release();
+  m_vdpauConfig.context = 0;
+}
+
+long CDecoder::Release()
+{
+  // check if we should do some pre-cleanup here
+  // a second decoder might need resources
+  if (m_vdpauConfigured == true)
   {
-    if (method == VS_INTERLACEMETHOD_AUTO_ION)
+    CSingleLock lock(m_DecoderSection);
+    CLog::Log(LOGNOTICE,"CVDPAU::Release pre-cleanup");
+
+    Message *reply;
+    if (m_vdpauOutput.m_controlPort.SendOutMessageSync(COutputControlProtocol::PRECLEANUP,
+                                                   &reply,
+                                                   2000))
     {
-      if (vid_height <= 576)
-      {
-        VdpBool enabled[]={1,1,0};
-        vdp_st = vdp_video_mixer_set_feature_enables(videoMixer, ARSIZE(feature), feature, enabled);
-      }
-      else if (vid_height > 576)
+      bool success = reply->signal == COutputControlProtocol::ACC ? true : false;
+      reply->Release();
+      if (!success)
       {
-        VdpBool enabled[]={1,0,0};
-        vdp_st = vdp_video_mixer_set_feature_enables(videoMixer, ARSIZE(feature), feature, enabled);
+        CLog::Log(LOGERROR, "VDPAU::%s - pre-cleanup returned error", __FUNCTION__);
+        m_DisplayState = VDPAU_ERROR;
       }
     }
-    else if (method == VS_INTERLACEMETHOD_VDPAU_TEMPORAL
-         ||  method == VS_INTERLACEMETHOD_VDPAU_TEMPORAL_HALF)
+    else
     {
-      VdpBool enabled[]={1,0,0};
-      vdp_st = vdp_video_mixer_set_feature_enables(videoMixer, ARSIZE(feature), feature, enabled);
+      CLog::Log(LOGERROR, "VDPAU::%s - pre-cleanup timed out", __FUNCTION__);
+      m_DisplayState = VDPAU_ERROR;
     }
-    else if (method == VS_INTERLACEMETHOD_VDPAU_TEMPORAL_SPATIAL
-         ||  method == VS_INTERLACEMETHOD_VDPAU_TEMPORAL_SPATIAL_HALF)
+
+    VdpVideoSurface surf;
+    while((surf = m_videoSurfaces.RemoveNext(true)) != VDP_INVALID_HANDLE)
     {
-      VdpBool enabled[]={1,1,0};
-      vdp_st = vdp_video_mixer_set_feature_enables(videoMixer, ARSIZE(feature), feature, enabled);
+      m_vdpauConfig.context->GetProcs().vdp_video_surface_destroy(surf);
     }
-    else if (method == VS_INTERLACEMETHOD_VDPAU_INVERSE_TELECINE)
+  }
+  return IHardwareDecoder::Release();
+}
+
+long CDecoder::ReleasePicReference()
+{
+  return IHardwareDecoder::Release();
+}
+
+void CDecoder::SetWidthHeight(int width, int height)
+{
+  m_vdpauConfig.upscale = g_advancedSettings.m_videoVDPAUScaling;
+
+  //pick the smallest dimensions, so we downscale with vdpau and upscale with opengl when appropriate
+  //this requires the least amount of gpu memory bandwidth
+  if (g_graphicsContext.GetWidth() < width || g_graphicsContext.GetHeight() < height || m_vdpauConfig.upscale >= 0)
+  {
+    //scale width to desktop size if the aspect ratio is the same or bigger than the desktop
+    if ((double)height * g_graphicsContext.GetWidth() / width <= (double)g_graphicsContext.GetHeight())
     {
-      VdpBool enabled[]={1,0,1};
-      vdp_st = vdp_video_mixer_set_feature_enables(videoMixer, ARSIZE(feature), feature, enabled);
+      m_vdpauConfig.outWidth = g_graphicsContext.GetWidth();
+      m_vdpauConfig.outHeight = MathUtils::round_int((double)height * g_graphicsContext.GetWidth() / width);
     }
-    else
+    else //scale height to the desktop size if the aspect ratio is smaller than the desktop
     {
-      VdpBool enabled[]={0,0,0};
-      vdp_st = vdp_video_mixer_set_feature_enables(videoMixer, ARSIZE(feature), feature, enabled);
+      m_vdpauConfig.outHeight = g_graphicsContext.GetHeight();
+      m_vdpauConfig.outWidth = MathUtils::round_int((double)width * g_graphicsContext.GetHeight() / height);
     }
   }
-
-  CheckStatus(vdp_st, __LINE__);
+  else
+  { //let opengl scale
+    m_vdpauConfig.outWidth = width;
+    m_vdpauConfig.outHeight = height;
+  }
+  CLog::Log(LOGDEBUG, "CVDPAU::SetWidthHeight Setting OutWidth: %i OutHeight: %i", m_vdpauConfig.outWidth, m_vdpauConfig.outHeight);
 }
 
-void CVDPAU::InitVDPAUProcs()
+void CDecoder::OnLostDevice()
 {
-  char* error;
+  CLog::Log(LOGNOTICE,"CVDPAU::OnLostDevice event");
 
-  (void)dlerror();
-  dl_vdp_device_create_x11 = (VdpStatus (*)(Display*, int, VdpDevice*, VdpStatus (**)(VdpDevice, VdpFuncId, void**)))dlsym(dl_handle, (const char*)"vdp_device_create_x11");
-  error = dlerror();
-  if (error)
-  {
-    CLog::Log(LOGERROR,"(VDPAU) - %s in %s",error,__FUNCTION__);
-    vdp_device = VDP_INVALID_HANDLE;
+  int count = g_graphicsContext.exit();
 
-    //g_application.m_guiDialogKaiToast.QueueNotification(CGUIDialogKaiToast::Error, "VDPAU", error, 10000);
+  CSingleLock lock(m_DecoderSection);
+  FiniVDPAUOutput();
+  if (m_vdpauConfig.context)
+    m_vdpauConfig.context->Release();
+  m_vdpauConfig.context = 0;
 
-    return;
-  }
+  m_DisplayState = VDPAU_LOST;
+  lock.Leave();
+  m_DisplayEvent.Reset();
 
-  if (dl_vdp_device_create_x11)
-  {
-    CSingleLock lock(g_graphicsContext);
-    m_Display = g_Windowing.GetDisplay();
-  }
-  else
+  g_graphicsContext.restore(count);
+}
+
+void CDecoder::OnResetDevice()
+{
+  CLog::Log(LOGNOTICE,"CVDPAU::OnResetDevice event");
+
+  int count = g_graphicsContext.exit();
+
+  CSingleLock lock(m_DecoderSection);
+  if (m_DisplayState == VDPAU_LOST)
   {
-    CLog::Log(LOGERROR,"(VDPAU) - Unable to get dl_vdp_device_create_x11 in %s", __FUNCTION__);
-    vdp_device = VDP_INVALID_HANDLE;
-    return;
+    m_DisplayState = VDPAU_RESET;
+    lock.Leave();
+    m_DisplayEvent.Set();
   }
 
-  int mScreen = DefaultScreen(m_Display);
-  VdpStatus vdp_st;
+  g_graphicsContext.restore(count);
+}
 
-  // Create Device
-  // tested on 64bit Ubuntu 11.10 and it deadlocked without this
-  XLockDisplay(m_Display);
-  vdp_st = dl_vdp_device_create_x11(m_Display, //x_display,
-                                 mScreen, //x_screen,
-                                 &vdp_device,
-                                 &vdp_get_proc_address);
-  XUnlockDisplay(m_Display);
-
-  CLog::Log(LOGNOTICE,"vdp_device = 0x%08x vdp_st = 0x%08x",vdp_device,vdp_st);
-  if (vdp_st != VDP_STATUS_OK)
+int CDecoder::Check(AVCodecContext* avctx)
+{
+  EDisplayState state;
+
+  { CSingleLock lock(m_DecoderSection);
+    state = m_DisplayState;
+  }
+
+  if (state == VDPAU_LOST)
   {
-    CLog::Log(LOGERROR,"(VDPAU) unable to init VDPAU - vdp_st = 0x%x.  Falling back.",vdp_st);
-    vdp_device = VDP_INVALID_HANDLE;
-    return;
+    CLog::Log(LOGNOTICE,"CVDPAU::Check waiting for display reset event");
+    if (!m_DisplayEvent.WaitMSec(4000))
+    {
+      CLog::Log(LOGERROR, "CVDPAU::Check - device didn't reset in reasonable time");
+      state = VDPAU_RESET;
+    }
+    else
+    {
+      CSingleLock lock(m_DecoderSection);
+      state = m_DisplayState;
+    }
   }
+  if (state == VDPAU_RESET || state == VDPAU_ERROR)
+  {
+    CSingleLock lock(m_DecoderSection);
 
-#define VDP_PROC(id, proc) \
-  do { \
-    vdp_st = vdp_get_proc_address(vdp_device, id, (void**)&proc); \
-    CheckStatus(vdp_st, __LINE__); \
-  } while(0);
+    FiniVDPAUOutput();
+    if (m_vdpauConfig.context)
+      m_vdpauConfig.context->Release();
+    m_vdpauConfig.context = 0;
 
-  VDP_PROC(VDP_FUNC_ID_GET_ERROR_STRING                    , vdp_get_error_string);
-  VDP_PROC(VDP_FUNC_ID_DEVICE_DESTROY                      , vdp_device_destroy);
-  VDP_PROC(VDP_FUNC_ID_GENERATE_CSC_MATRIX                 , vdp_generate_csc_matrix);
-  VDP_PROC(VDP_FUNC_ID_VIDEO_SURFACE_CREATE                , vdp_video_surface_create);
-  VDP_PROC(VDP_FUNC_ID_VIDEO_SURFACE_DESTROY               , vdp_video_surface_destroy);
-  VDP_PROC(VDP_FUNC_ID_VIDEO_SURFACE_PUT_BITS_Y_CB_CR      , vdp_video_surface_put_bits_y_cb_cr);
-  VDP_PROC(VDP_FUNC_ID_VIDEO_SURFACE_GET_BITS_Y_CB_CR      , vdp_video_surface_get_bits_y_cb_cr);
-  VDP_PROC(VDP_FUNC_ID_OUTPUT_SURFACE_PUT_BITS_Y_CB_CR     , vdp_output_surface_put_bits_y_cb_cr);
-  VDP_PROC(VDP_FUNC_ID_OUTPUT_SURFACE_PUT_BITS_NATIVE      , vdp_output_surface_put_bits_native);
-  VDP_PROC(VDP_FUNC_ID_OUTPUT_SURFACE_CREATE               , vdp_output_surface_create);
-  VDP_PROC(VDP_FUNC_ID_OUTPUT_SURFACE_DESTROY              , vdp_output_surface_destroy);
-  VDP_PROC(VDP_FUNC_ID_OUTPUT_SURFACE_GET_BITS_NATIVE      , vdp_output_surface_get_bits_native);
-  VDP_PROC(VDP_FUNC_ID_OUTPUT_SURFACE_RENDER_OUTPUT_SURFACE, vdp_output_surface_render_output_surface);
-  VDP_PROC(VDP_FUNC_ID_OUTPUT_SURFACE_PUT_BITS_INDEXED     , vdp_output_surface_put_bits_indexed);  
-  VDP_PROC(VDP_FUNC_ID_VIDEO_MIXER_CREATE                  , vdp_video_mixer_create);
-  VDP_PROC(VDP_FUNC_ID_VIDEO_MIXER_SET_FEATURE_ENABLES     , vdp_video_mixer_set_feature_enables);
-  VDP_PROC(VDP_FUNC_ID_VIDEO_MIXER_DESTROY                 , vdp_video_mixer_destroy);
-  VDP_PROC(VDP_FUNC_ID_VIDEO_MIXER_RENDER                  , vdp_video_mixer_render);
-  VDP_PROC(VDP_FUNC_ID_VIDEO_MIXER_SET_ATTRIBUTE_VALUES    , vdp_video_mixer_set_attribute_values);
-  VDP_PROC(VDP_FUNC_ID_VIDEO_MIXER_QUERY_PARAMETER_SUPPORT , vdp_video_mixer_query_parameter_support);
-  VDP_PROC(VDP_FUNC_ID_VIDEO_MIXER_QUERY_FEATURE_SUPPORT   , vdp_video_mixer_query_feature_support);
-  VDP_PROC(VDP_FUNC_ID_DECODER_CREATE                      , vdp_decoder_create);
-  VDP_PROC(VDP_FUNC_ID_DECODER_DESTROY                     , vdp_decoder_destroy);
-  VDP_PROC(VDP_FUNC_ID_DECODER_RENDER                      , vdp_decoder_render);
-  VDP_PROC(VDP_FUNC_ID_DECODER_QUERY_CAPABILITIES          , vdp_decoder_query_caps);
-  VDP_PROC(VDP_FUNC_ID_PREEMPTION_CALLBACK_REGISTER        , vdp_preemption_callback_register);
-  VDP_PROC(VDP_FUNC_ID_PRESENTATION_QUEUE_TARGET_DESTROY          , vdp_presentation_queue_target_destroy);
-  VDP_PROC(VDP_FUNC_ID_PRESENTATION_QUEUE_CREATE                  , vdp_presentation_queue_create);
-  VDP_PROC(VDP_FUNC_ID_PRESENTATION_QUEUE_DESTROY                 , vdp_presentation_queue_destroy);
-  VDP_PROC(VDP_FUNC_ID_PRESENTATION_QUEUE_DISPLAY                 , vdp_presentation_queue_display);
-  VDP_PROC(VDP_FUNC_ID_PRESENTATION_QUEUE_BLOCK_UNTIL_SURFACE_IDLE, vdp_presentation_queue_block_until_surface_idle);
-  VDP_PROC(VDP_FUNC_ID_PRESENTATION_QUEUE_TARGET_CREATE_X11       , vdp_presentation_queue_target_create_x11);
-  VDP_PROC(VDP_FUNC_ID_PRESENTATION_QUEUE_QUERY_SURFACE_STATUS    , vdp_presentation_queue_query_surface_status);
-  VDP_PROC(VDP_FUNC_ID_PRESENTATION_QUEUE_GET_TIME                , vdp_presentation_queue_get_time);
-  
-#undef VDP_PROC
+    if (CVDPAUContext::EnsureContext(&m_vdpauConfig.context))
+    {
+      m_DisplayState = VDPAU_OPEN;
+      m_vdpauConfigured = false;
+    }
 
-  // set all vdpau resources to invalid
-  vdp_flip_target = VDP_INVALID_HANDLE;
-  vdp_flip_queue = VDP_INVALID_HANDLE;
-  videoMixer = VDP_INVALID_HANDLE;
-  totalAvailableOutputSurfaces = 0;
-  presentSurface = VDP_INVALID_HANDLE;
-  outputSurface = VDP_INVALID_HANDLE;
-  for (int i = 0; i < NUM_OUTPUT_SURFACES; i++)
-    outputSurfaces[i] = VDP_INVALID_HANDLE;
+    if (state == VDPAU_RESET)
+      return VC_FLUSHED;
+    else
+      return VC_ERROR;
+  }
+  return 0;
+}
 
-  m_vdpauOutputMethod = OUTPUT_NONE;
+bool CDecoder::IsVDPAUFormat(PixelFormat format)
+{
+  if (format == AV_PIX_FMT_VDPAU)
+    return true;
+  else
+    return false;
+}
 
-  CExclusiveLock lock(m_DisplaySection);
-  m_DisplayState = VDPAU_OPEN;
-  vdpauConfigured = false;
+bool CDecoder::Supports(VdpVideoMixerFeature feature)
+{
+  return m_vdpauConfig.context->Supports(feature);
 }
 
-void CVDPAU::FiniVDPAUProcs()
+bool CDecoder::Supports(EINTERLACEMETHOD method)
 {
-  if (vdp_device == VDP_INVALID_HANDLE) return;
+  if(method == VS_INTERLACEMETHOD_VDPAU_BOB
+  || method == VS_INTERLACEMETHOD_AUTO)
+    return true;
 
-  VdpStatus vdp_st;
-  vdp_st = vdp_device_destroy(vdp_device);
-  CheckStatus(vdp_st, __LINE__);
-  vdp_device = VDP_INVALID_HANDLE;
-  vdpauConfigured = false;
+  if (method == VS_INTERLACEMETHOD_RENDER_BOB)
+    return true;
+
+  if (method == VS_INTERLACEMETHOD_VDPAU_INVERSE_TELECINE)
+    return false;
+
+  for(SInterlaceMapping* p = g_interlace_mapping; p->method != VS_INTERLACEMETHOD_NONE; p++)
+  {
+    if(p->method == method)
+      return Supports(p->feature);
+  }
+  return false;
 }
 
-void CVDPAU::InitCSCMatrix(int Height)
+EINTERLACEMETHOD CDecoder::AutoInterlaceMethod()
 {
-  VdpStatus vdp_st;
-  m_Procamp.struct_version = VDP_PROCAMP_VERSION;
-  m_Procamp.brightness     = 0.0;
-  m_Procamp.contrast       = 1.0;
-  m_Procamp.saturation     = 1.0;
-  m_Procamp.hue            = 0;
-  vdp_st = vdp_generate_csc_matrix(&m_Procamp,
-                                   (Height < 720)? VDP_COLOR_STANDARD_ITUR_BT_601 : VDP_COLOR_STANDARD_ITUR_BT_709,
-                                   &m_CSCMatrix);
-  CheckStatus(vdp_st, __LINE__);
+  return VS_INTERLACEMETHOD_RENDER_BOB;
 }
 
-void CVDPAU::FiniVDPAUOutput()
+void CDecoder::FiniVDPAUOutput()
 {
-  FiniOutputMethod();
-
-  if (vdp_device == VDP_INVALID_HANDLE || !vdpauConfigured) return;
+  if (!m_vdpauConfigured)
+    return;
 
   CLog::Log(LOGNOTICE, " (VDPAU) %s", __FUNCTION__);
 
+  // uninit output
+  m_vdpauOutput.Dispose();
+  m_vdpauConfigured = false;
+
   VdpStatus vdp_st;
 
-  vdp_st = vdp_decoder_destroy(decoder);
+  vdp_st = m_vdpauConfig.context->GetProcs().vdp_decoder_destroy(m_vdpauConfig.vdpDecoder);
   if (CheckStatus(vdp_st, __LINE__))
     return;
-  decoder = VDP_INVALID_HANDLE;
+  m_vdpauConfig.vdpDecoder = VDP_INVALID_HANDLE;
 
-  for (unsigned int i = 0; i < m_videoSurfaces.size(); ++i)
+  CLog::Log(LOGDEBUG, "CVDPAU::FiniVDPAUOutput destroying %d video surfaces", m_videoSurfaces.Size());
+
+  VdpVideoSurface surf;
+  while((surf = m_videoSurfaces.RemoveNext()) != VDP_INVALID_HANDLE)
   {
-    vdpau_render_state *render = m_videoSurfaces[i];
-    if (render->surface != VDP_INVALID_HANDLE)
-    {
-      vdp_st = vdp_video_surface_destroy(render->surface);
-      render->surface = VDP_INVALID_HANDLE;
-    }
+    m_vdpauConfig.context->GetProcs().vdp_video_surface_destroy(surf);
     if (CheckStatus(vdp_st, __LINE__))
       return;
   }
+  m_videoSurfaces.Reset();
 }
 
-
-void CVDPAU::ReadFormatOf( AVCodecID codec
-                         , VdpDecoderProfile &vdp_decoder_profile
-                         , VdpChromaType     &vdp_chroma_type)
+void CDecoder::ReadFormatOf( AVCodecID codec
+                           , VdpDecoderProfile &vdp_decoder_profile
+                           , VdpChromaType     &vdp_chroma_type)
 {
   switch (codec)
   {
@@ -941,7 +858,7 @@ void CVDPAU::ReadFormatOf( AVCodecID codec
       vdp_decoder_profile = VDP_DECODER_PROFILE_MPEG2_MAIN;
       vdp_chroma_type     = VDP_CHROMA_TYPE_420;
       break;
-    case  AV_CODEC_ID_H264:
+    case AV_CODEC_ID_H264:
       vdp_decoder_profile = VDP_DECODER_PROFILE_H264_HIGH;
       vdp_chroma_type     = VDP_CHROMA_TYPE_420;
       break;
@@ -953,12 +870,10 @@ void CVDPAU::ReadFormatOf( AVCodecID codec
       vdp_decoder_profile = VDP_DECODER_PROFILE_VC1_ADVANCED;
       vdp_chroma_type     = VDP_CHROMA_TYPE_420;
       break;
-#if (defined VDP_DECODER_PROFILE_MPEG4_PART2_ASP)
     case AV_CODEC_ID_MPEG4:
       vdp_decoder_profile = VDP_DECODER_PROFILE_MPEG4_PART2_ASP;
       vdp_chroma_type     = VDP_CHROMA_TYPE_420;
       break;
-#endif
     default:
       vdp_decoder_profile = 0;
       vdp_chroma_type     = 0;
@@ -966,751 +881,2787 @@ void CVDPAU::ReadFormatOf( AVCodecID codec
   }
 }
 
-
-bool CVDPAU::ConfigVDPAU(AVCodecContext* avctx, int ref_frames)
+bool CDecoder::ConfigVDPAU(AVCodecContext* avctx, int ref_frames)
 {
   FiniVDPAUOutput();
 
   VdpStatus vdp_st;
   VdpDecoderProfile vdp_decoder_profile;
-  vid_width = avctx->width;
-  vid_height = avctx->height;
-  surface_width = avctx->coded_width;
-  surface_height = avctx->coded_height;
 
-  past[1] = past[0] = current = future = NULL;
-  CLog::Log(LOGNOTICE, " (VDPAU) screenWidth:%i vidWidth:%i surfaceWidth:%i",OutWidth,vid_width,surface_width);
-  CLog::Log(LOGNOTICE, " (VDPAU) screenHeight:%i vidHeight:%i surfaceHeight:%i",OutHeight,vid_height,surface_height);
-  ReadFormatOf(avctx->codec_id, vdp_decoder_profile, vdp_chroma_type);
+  m_vdpauConfig.vidWidth = avctx->width;
+  m_vdpauConfig.vidHeight = avctx->height;
+  m_vdpauConfig.surfaceWidth = avctx->coded_width;
+  m_vdpauConfig.surfaceHeight = avctx->coded_height;
+
+  SetWidthHeight(avctx->width,avctx->height);
+
+  CLog::Log(LOGNOTICE, " (VDPAU) screenWidth:%i vidWidth:%i surfaceWidth:%i",m_vdpauConfig.outWidth,m_vdpauConfig.vidWidth,m_vdpauConfig.surfaceWidth);
+  CLog::Log(LOGNOTICE, " (VDPAU) screenHeight:%i vidHeight:%i surfaceHeight:%i",m_vdpauConfig.outHeight,m_vdpauConfig.vidHeight,m_vdpauConfig.surfaceHeight);
+
+  ReadFormatOf(avctx->codec_id, vdp_decoder_profile, m_vdpauConfig.vdpChromaType);
 
   if(avctx->codec_id == AV_CODEC_ID_H264)
   {
-     max_references = ref_frames;
-     if (max_references > 16) max_references = 16;
-     if (max_references < 5)  max_references = 5;
+    m_vdpauConfig.maxReferences = ref_frames;
+    if (m_vdpauConfig.maxReferences > 16) m_vdpauConfig.maxReferences = 16;
+    if (m_vdpauConfig.maxReferences < 5)  m_vdpauConfig.maxReferences = 5;
   }
   else
-    max_references = 2;
+    m_vdpauConfig.maxReferences = 2;
 
-  vdp_st = vdp_decoder_create(vdp_device,
+  vdp_st = m_vdpauConfig.context->GetProcs().vdp_decoder_create(m_vdpauConfig.context->GetDevice(),
                               vdp_decoder_profile,
-                              surface_width,
-                              surface_height,
-                              max_references,
-                              &decoder);
+                              m_vdpauConfig.surfaceWidth,
+                              m_vdpauConfig.surfaceHeight,
+                              m_vdpauConfig.maxReferences,
+                              &m_vdpauConfig.vdpDecoder);
   if (CheckStatus(vdp_st, __LINE__))
     return false;
 
-  m_vdpauOutputMethod = OUTPUT_NONE;
+  // initialize output
+  CSingleLock lock(g_graphicsContext);
+  m_vdpauConfig.stats = &m_bufferStats;
+  m_vdpauConfig.vdpau = this;
+  m_bufferStats.Reset();
+  m_vdpauOutput.Start();
+  Message *reply;
+  if (m_vdpauOutput.m_controlPort.SendOutMessageSync(COutputControlProtocol::INIT,
+                                                 &reply,
+                                                 2000,
+                                                 &m_vdpauConfig,
+                                                 sizeof(m_vdpauConfig)))
+  {
+    bool success = reply->signal == COutputControlProtocol::ACC ? true : false;
+    reply->Release();
+    if (!success)
+    {
+      CLog::Log(LOGERROR, "VDPAU::%s - vdpau output returned error", __FUNCTION__);
+      m_vdpauOutput.Dispose();
+      return false;
+    }
+  }
+  else
+  {
+    CLog::Log(LOGERROR, "VDPAU::%s - failed to init output", __FUNCTION__);
+    m_vdpauOutput.Dispose();
+    return false;
+  }
 
-  vdpauConfigured = true;
+  m_inMsgEvent.Reset();
+  m_vdpauConfigured = true;
+  m_ErrorCount = 0;
   return true;
 }
 
-bool CVDPAU::ConfigOutputMethod(AVCodecContext *avctx, AVFrame *pFrame)
+int CDecoder::FFGetBuffer(AVCodecContext *avctx, AVFrame *pic)
 {
-  VdpStatus vdp_st;
+  //CLog::Log(LOGNOTICE,"%s",__FUNCTION__);
+  CDVDVideoCodecFFmpeg* ctx        = (CDVDVideoCodecFFmpeg*)avctx->opaque;
+  CDecoder*             vdp        = (CDecoder*)ctx->GetHardware();
 
-  if (m_vdpauOutputMethod == OUTPUT_PIXMAP)
-    return true;
+  // while we are waiting to recover we can't do anything
+  CSingleLock lock(vdp->m_DecoderSection);
 
-  FiniOutputMethod();
+  if(vdp->m_DisplayState != VDPAU_OPEN)
+  {
+    CLog::Log(LOGWARNING, "CVDPAU::FFGetBuffer - returning due to awaiting recovery");
+    return -1;
+  }
 
-  MakePixmap(avctx->width,avctx->height);
+  VdpVideoSurface surf = (VdpVideoSurface)(uintptr_t)pic->data[3];
+  surf = vdp->m_videoSurfaces.GetFree(surf != 0 ? surf : VDP_INVALID_HANDLE);
 
-  vdp_st = vdp_presentation_queue_target_create_x11(vdp_device,
-                                                    m_Pixmap, //x_window,
-                                                    &vdp_flip_target);
-  if (CheckStatus(vdp_st, __LINE__))
-    return false;
+  VdpStatus vdp_st = VDP_STATUS_ERROR;
+  if (surf == VDP_INVALID_HANDLE)
+  {
+    // create a new surface
+    VdpDecoderProfile profile;
+    ReadFormatOf(avctx->codec_id, profile, vdp->m_vdpauConfig.vdpChromaType);
 
-  vdp_st = vdp_presentation_queue_create(vdp_device,
-                                         vdp_flip_target,
-                                         &vdp_flip_queue);
-  if (CheckStatus(vdp_st, __LINE__))
-    return false;
+    vdp_st = vdp->m_vdpauConfig.context->GetProcs().vdp_video_surface_create(vdp->m_vdpauConfig.context->GetDevice(),
+                                         vdp->m_vdpauConfig.vdpChromaType,
+                                         avctx->coded_width,
+                                         avctx->coded_height,
+                                         &surf);
+    vdp->CheckStatus(vdp_st, __LINE__);
+    if (vdp_st != VDP_STATUS_OK)
+    {
+      CLog::Log(LOGERROR, "CVDPAU::FFGetBuffer - No Video surface available could be created");
+      return -1;
+    }
+    vdp->m_videoSurfaces.AddSurface(surf);
+  }
 
-  totalAvailableOutputSurfaces = 0;
+  pic->data[1] = pic->data[2] = NULL;
+  pic->data[0] = (uint8_t*)(uintptr_t)surf;
+  pic->data[3] = (uint8_t*)(uintptr_t)surf;
 
-  int tmpMaxOutputSurfaces = NUM_OUTPUT_SURFACES;
-  if (vid_width == FULLHD_WIDTH)
-    tmpMaxOutputSurfaces = NUM_OUTPUT_SURFACES_FOR_FULLHD;
+  pic->linesize[0] = pic->linesize[1] =  pic->linesize[2] = 0;
 
-  // Creation of outputSurfaces
-  for (int i = 0; i < NUM_OUTPUT_SURFACES && i < tmpMaxOutputSurfaces; i++)
-  {
-    vdp_st = vdp_output_surface_create(vdp_device,
-                                       VDP_RGBA_FORMAT_B8G8R8A8,
-                                       OutWidth,
-                                       OutHeight,
-                                       &outputSurfaces[i]);
-    if (CheckStatus(vdp_st, __LINE__))
-      return false;
-    totalAvailableOutputSurfaces++;
-  }
-  CLog::Log(LOGNOTICE, " (VDPAU) Total Output Surfaces Available: %i of a max (tmp: %i const: %i)",
-                       totalAvailableOutputSurfaces,
-                       tmpMaxOutputSurfaces,
-                       NUM_OUTPUT_SURFACES);
+  pic->type= FF_BUFFER_TYPE_USER;
+
+  pic->reordered_opaque= avctx->reordered_opaque;
+  return 0;
+}
+
+void CDecoder::FFReleaseBuffer(AVCodecContext *avctx, AVFrame *pic)
+{
+  CDVDVideoCodecFFmpeg* ctx        = (CDVDVideoCodecFFmpeg*)avctx->opaque;
+  CDecoder*             vdp        = (CDecoder*)ctx->GetHardware();
 
-  // create 3 pitches of black lines needed for clipping top
-  // and bottom lines when de-interlacing
-  m_BlackBar = new uint32_t[3*OutWidth];
-  memset(m_BlackBar, 0, 3*OutWidth*sizeof(uint32_t));
+  VdpVideoSurface surf;
+  unsigned int i;
 
-  surfaceNum = presentSurfaceNum = 0;
-  outputSurface = presentSurface = VDP_INVALID_HANDLE;
-  videoMixer = VDP_INVALID_HANDLE;
+  CSingleLock lock(vdp->m_DecoderSection);
 
-  m_vdpauOutputMethod = OUTPUT_PIXMAP;
+  surf = (VdpVideoSurface)(uintptr_t)pic->data[3];
 
-  return true;
+  vdp->m_videoSurfaces.ClearReference(surf);
+
+  for(i=0; i<4; i++)
+    pic->data[i]= NULL;
 }
 
-bool CVDPAU::FiniOutputMethod()
+VdpStatus CDecoder::Render( VdpDecoder decoder, VdpVideoSurface target,
+                            VdpPictureInfo const *picture_info,
+                            uint32_t bitstream_buffer_count,
+                            VdpBitstreamBuffer const * bitstream_buffers)
 {
-  VdpStatus vdp_st;
+  return VDP_STATUS_OK;
+}
+
+void CDecoder::FFDrawSlice(struct AVCodecContext *s,
+                                           const AVFrame *src, int offset[4],
+                                           int y, int type, int height)
+{
+  CDVDVideoCodecFFmpeg* ctx = (CDVDVideoCodecFFmpeg*)s->opaque;
+  CDecoder*               vdp = (CDecoder*)ctx->GetHardware();
+
+  // while we are waiting to recover we can't do anything
+  CSingleLock lock(vdp->m_DecoderSection);
+
+  if(vdp->m_DisplayState != VDPAU_OPEN)
+    return;
 
-  if (vdp_flip_queue != VDP_INVALID_HANDLE)
+  if(src->linesize[0] || src->linesize[1] || src->linesize[2]
+  || offset[0] || offset[1] || offset[2])
   {
-    vdp_st = vdp_presentation_queue_destroy(vdp_flip_queue);
-    vdp_flip_queue = VDP_INVALID_HANDLE;
-    CheckStatus(vdp_st, __LINE__);
+    CLog::Log(LOGERROR, "CVDPAU::FFDrawSlice - invalid linesizes or offsets provided");
+    return;
   }
 
-  if (vdp_flip_target != VDP_INVALID_HANDLE)
+  VdpStatus vdp_st;
+  VdpVideoSurface surf = (VdpVideoSurface)(uintptr_t)src->data[3];
+
+  // ffmpeg vc-1 decoder does not flush, make sure the data buffer is still valid
+  if (!vdp->m_videoSurfaces.IsValid(surf))
   {
-    vdp_st = vdp_presentation_queue_target_destroy(vdp_flip_target);
-    vdp_flip_target = VDP_INVALID_HANDLE;
-    CheckStatus(vdp_st, __LINE__);
+    CLog::Log(LOGWARNING, "CVDPAU::FFDrawSlice - ignoring invalid buffer");
+    return;
   }
 
-  if (m_glPixmap)
+  uint32_t max_refs = 0;
+  if(s->codec_id == AV_CODEC_ID_H264)
+    max_refs = vdp->m_hwContext.info.h264.num_ref_frames;
+
+  if(vdp->m_vdpauConfig.vdpDecoder == VDP_INVALID_HANDLE
+  || vdp->m_vdpauConfigured == false
+  || vdp->m_vdpauConfig.maxReferences < max_refs)
   {
-    CLog::Log(LOGDEBUG, "GLX: Destroying glPixmap");
-    glXDestroyPixmap(m_Display, m_glPixmap);
-    m_glPixmap = None;
+    if(!vdp->ConfigVDPAU(s, max_refs))
+      return;
+  }
+
+  uint64_t startTime = CurrentHostCounter();
+  uint16_t decoded, processed, rend;
+  vdp->m_bufferStats.Get(decoded, processed, rend);
+  vdp_st = vdp->m_vdpauConfig.context->GetProcs().vdp_decoder_render(vdp->m_vdpauConfig.vdpDecoder,
+                                   surf,
+                                   (VdpPictureInfo const *)&(vdp->m_hwContext.info),
+                                   vdp->m_hwContext.bitstream_buffers_used,
+                                   vdp->m_hwContext.bitstream_buffers);
+  if (vdp->CheckStatus(vdp_st, __LINE__))
+    vdp->m_DecoderError = true;
+  else
+    vdp->m_DecoderError = false;
+
+  uint64_t diff = CurrentHostCounter() - startTime;
+  if (diff*1000/CurrentHostFrequency() > 30)
+    CLog::Log(LOGDEBUG, "CVDPAU::DrawSlice - VdpDecoderRender long decoding: %d ms, dec: %d, proc: %d, rend: %d", (int)((diff*1000)/CurrentHostFrequency()), decoded, processed, rend);
+}
+
+
+int CDecoder::Decode(AVCodecContext *avctx, AVFrame *pFrame)
+{
+  int result = Check(avctx);
+  if (result)
+    return result;
+
+  CSingleLock lock(m_DecoderSection);
+
+  if (m_DecoderError && pFrame)
+    return VC_ERROR;
+
+  if (!m_vdpauConfigured)
+    return VC_ERROR;
+
+  if(pFrame)
+  { // we have a new frame from decoder
+
+    VdpVideoSurface surf = (VdpVideoSurface)(uintptr_t)pFrame->data[3];
+    // ffmpeg vc-1 decoder does not flush, make sure the data buffer is still valid
+    if (!m_videoSurfaces.IsValid(surf))
+    {
+      CLog::Log(LOGWARNING, "CVDPAU::Decode - ignoring invalid buffer");
+      return VC_BUFFER;
+    }
+    m_videoSurfaces.MarkRender(surf);
+
+    // send frame to output for processing
+    CVdpauDecodedPicture pic;
+    memset(&pic.DVDPic, 0, sizeof(pic.DVDPic));
+    ((CDVDVideoCodecFFmpeg*)avctx->opaque)->GetPictureCommon(&pic.DVDPic);
+    pic.videoSurface = surf;
+    pic.DVDPic.color_matrix = avctx->colorspace;
+    m_bufferStats.IncDecoded();
+    m_vdpauOutput.m_dataPort.SendOutMessage(COutputDataProtocol::NEWFRAME, &pic, sizeof(pic));
+
+    //TODO
+    // m_codecControl = pic.DVDPic.iFlags & (DVP_FLAG_DRAIN | DVP_FLAG_NO_POSTPROC);
   }
 
-  if (m_Pixmap)
+  int retval = 0;
+  uint16_t decoded, processed, render;
+  Message *msg;
+  while (m_vdpauOutput.m_controlPort.ReceiveInMessage(&msg))
   {
-    CLog::Log(LOGDEBUG, "GLX: Destroying XPixmap");
-    XFreePixmap(m_Display, m_Pixmap);
-    m_Pixmap = None;
+    if (msg->signal == COutputControlProtocol::ERROR)
+    {
+      m_DisplayState = VDPAU_ERROR;
+      retval |= VC_ERROR;
+    }
+    msg->Release();
   }
 
-  outputSurface = presentSurface = VDP_INVALID_HANDLE;
+  m_bufferStats.Get(decoded, processed, render);
 
-  for (int i = 0; i < totalAvailableOutputSurfaces; i++)
+  uint64_t startTime = CurrentHostCounter();
+  while (!retval)
   {
-    if (outputSurfaces[i] == VDP_INVALID_HANDLE)
-       continue;
-    vdp_st = vdp_output_surface_destroy(outputSurfaces[i]);
-    outputSurfaces[i] = VDP_INVALID_HANDLE;
-    CheckStatus(vdp_st, __LINE__);
-  }
+    // first fill the buffers to keep vdpau busy
+    // mixer will run with decoded >= 2. output is limited by number of output surfaces
+    // In case mixer is bypassed we limit by looking at processed
+    if (decoded < 3 && processed < 3)
+    {
+      retval |= VC_BUFFER;
+    }
+    else if (m_vdpauOutput.m_dataPort.ReceiveInMessage(&msg))
+    {
+      if (msg->signal == COutputDataProtocol::PICTURE)
+      {
+        if (m_presentPicture)
+        {
+          m_presentPicture->ReturnUnused();
+          m_presentPicture = 0;
+        }
+
+        m_presentPicture = *(CVdpauRenderPicture**)msg->data;
+        m_presentPicture->vdpau = this;
+        m_bufferStats.DecRender();
+        m_bufferStats.Get(decoded, processed, render);
+        retval |= VC_PICTURE;
+        msg->Release();
+        break;
+      }
+      msg->Release();
+    }
+    else if (m_vdpauOutput.m_controlPort.ReceiveInMessage(&msg))
+    {
+      if (msg->signal == COutputControlProtocol::STATS)
+      {
+        m_bufferStats.Get(decoded, processed, render);
+      }
+      else
+      {
+        m_DisplayState = VDPAU_ERROR;
+        retval |= VC_ERROR;
+      }
+      msg->Release();
+    }
 
-  if (videoMixer != VDP_INVALID_HANDLE)
+    if (decoded < 3 && processed < 3)
+    {
+      retval |= VC_BUFFER;
+    }
+
+    if (!retval && !m_inMsgEvent.WaitMSec(2000))
+      break;
+  }
+  uint64_t diff = CurrentHostCounter() - startTime;
+  if (retval & VC_PICTURE)
   {
-    vdp_st = vdp_video_mixer_destroy(videoMixer);
-    videoMixer = VDP_INVALID_HANDLE;
-    CheckStatus(vdp_st, __LINE__);
+    m_bufferStats.SetParams(diff, m_codecControl);
   }
+  if (diff*1000/CurrentHostFrequency() > 50)
+    CLog::Log(LOGDEBUG,"CVDPAU::Decode long wait: %d", (int)((diff*1000)/CurrentHostFrequency()));
 
-  if (m_BlackBar)
+  if (!retval)
   {
-    delete [] m_BlackBar;
-    m_BlackBar = NULL;
+    CLog::Log(LOGERROR, "VDPAU::%s - timed out waiting for output message", __FUNCTION__);
+    m_DisplayState = VDPAU_ERROR;
+    retval |= VC_ERROR;
   }
 
-  while (!m_DVDVideoPics.empty())
-    m_DVDVideoPics.pop();
+  return retval;
+}
+
+bool CDecoder::GetPicture(AVCodecContext* avctx, AVFrame* frame, DVDVideoPicture* picture)
+{
+  CSingleLock lock(m_DecoderSection);
+
+  if (m_DisplayState != VDPAU_OPEN)
+    return false;
+
+  *picture = m_presentPicture->DVDPic;
+  picture->vdpau = m_presentPicture;
 
   return true;
 }
 
-void CVDPAU::SpewHardwareAvailable()  //Copyright (c) 2008 Wladimir J. van der Laan  -- VDPInfo
+void CDecoder::Reset()
 {
-  VdpStatus rv;
-  CLog::Log(LOGNOTICE,"VDPAU Decoder capabilities:");
-  CLog::Log(LOGNOTICE,"name          level macbs width height");
-  CLog::Log(LOGNOTICE,"------------------------------------");
-  for(unsigned int x=0; x<decoder_profile_count; ++x)
+  CSingleLock lock(m_DecoderSection);
+
+  if (!m_vdpauConfigured)
+    return;
+
+  Message *reply;
+  if (m_vdpauOutput.m_controlPort.SendOutMessageSync(COutputControlProtocol::FLUSH,
+                                                 &reply,
+                                                 2000))
   {
-    VdpBool is_supported = false;
-    uint32_t max_level, max_macroblocks, max_width, max_height;
-    rv = vdp_decoder_query_caps(vdp_device, decoder_profiles[x].id,
-                                &is_supported, &max_level, &max_macroblocks, &max_width, &max_height);
-    if(rv == VDP_STATUS_OK && is_supported)
+    bool success = reply->signal == COutputControlProtocol::ACC ? true : false;
+    reply->Release();
+    if (!success)
     {
-      CLog::Log(LOGNOTICE,"%-16s %2i %5i %5i %5i\n", decoder_profiles[x].name,
-                max_level, max_macroblocks, max_width, max_height);
+      CLog::Log(LOGERROR, "VDPAU::%s - flush returned error", __FUNCTION__);
+      m_DisplayState = VDPAU_ERROR;
     }
+    else
+      m_bufferStats.Reset();
   }
-  CLog::Log(LOGNOTICE,"------------------------------------");
-  m_feature_count = 0;
-#define CHECK_SUPPORT(feature)  \
-  do { \
-    VdpBool supported; \
-    if(vdp_video_mixer_query_feature_support(vdp_device, feature, &supported) == VDP_STATUS_OK && supported) { \
-      CLog::Log(LOGNOTICE, "Mixer feature: "#feature);  \
-      m_features[m_feature_count++] = feature; \
-    } \
-  } while(false)
+  else
+  {
+    CLog::Log(LOGERROR, "VDPAU::%s - flush timed out", __FUNCTION__);
+    m_DisplayState = VDPAU_ERROR;
+  }
+}
 
-  CHECK_SUPPORT(VDP_VIDEO_MIXER_FEATURE_NOISE_REDUCTION);
-  CHECK_SUPPORT(VDP_VIDEO_MIXER_FEATURE_SHARPNESS);
-  CHECK_SUPPORT(VDP_VIDEO_MIXER_FEATURE_DEINTERLACE_TEMPORAL);
-  CHECK_SUPPORT(VDP_VIDEO_MIXER_FEATURE_DEINTERLACE_TEMPORAL_SPATIAL);
-  CHECK_SUPPORT(VDP_VIDEO_MIXER_FEATURE_INVERSE_TELECINE);
-#ifdef VDP_VIDEO_MIXER_FEATURE_HIGH_QUALITY_SCALING_L1
-  CHECK_SUPPORT(VDP_VIDEO_MIXER_FEATURE_HIGH_QUALITY_SCALING_L1);
-  CHECK_SUPPORT(VDP_VIDEO_MIXER_FEATURE_HIGH_QUALITY_SCALING_L2);
-  CHECK_SUPPORT(VDP_VIDEO_MIXER_FEATURE_HIGH_QUALITY_SCALING_L3);
-  CHECK_SUPPORT(VDP_VIDEO_MIXER_FEATURE_HIGH_QUALITY_SCALING_L4);
-  CHECK_SUPPORT(VDP_VIDEO_MIXER_FEATURE_HIGH_QUALITY_SCALING_L5);
-  CHECK_SUPPORT(VDP_VIDEO_MIXER_FEATURE_HIGH_QUALITY_SCALING_L6);
-  CHECK_SUPPORT(VDP_VIDEO_MIXER_FEATURE_HIGH_QUALITY_SCALING_L7);
-  CHECK_SUPPORT(VDP_VIDEO_MIXER_FEATURE_HIGH_QUALITY_SCALING_L8);
-  CHECK_SUPPORT(VDP_VIDEO_MIXER_FEATURE_HIGH_QUALITY_SCALING_L9);
-#endif
-#undef CHECK_SUPPORT
+bool CDecoder::CanSkipDeint()
+{
+  return m_bufferStats.CanSkipDeint();
+}
 
+void CDecoder::ReturnRenderPicture(CVdpauRenderPicture *renderPic)
+{
+  m_vdpauOutput.m_dataPort.SendOutMessage(COutputDataProtocol::RETURNPIC, &renderPic, sizeof(renderPic));
 }
 
-bool CVDPAU::IsSurfaceValid(vdpau_render_state *render)
+bool CDecoder::CheckStatus(VdpStatus vdp_st, int line)
 {
-  // find render state in queue
-  bool found(false);
-  unsigned int i;
-  for(i = 0; i < m_videoSurfaces.size(); ++i)
+  if (vdp_st != VDP_STATUS_OK)
   {
-    if(m_videoSurfaces[i] == render)
+    CLog::Log(LOGERROR, " (VDPAU) Error: %s(%d) at %s:%d\n", m_vdpauConfig.context->GetProcs().vdp_get_error_string(vdp_st), vdp_st, __FILE__, line);
+
+    m_ErrorCount++;
+
+    if(m_DisplayState == VDPAU_OPEN)
     {
-      found = true;
-      break;
+      if (vdp_st == VDP_STATUS_DISPLAY_PREEMPTED)
+      {
+        m_DisplayEvent.Reset();
+        m_DisplayState = VDPAU_LOST;
+      }
+      else if (m_ErrorCount > 2)
+        m_DisplayState = VDPAU_ERROR;
     }
+
+    return true;
   }
-  if (!found)
-  {
-    CLog::Log(LOGERROR,"%s - video surface not found", __FUNCTION__);
-    return false;
+  m_ErrorCount = 0;
+  return false;
+}
+
+//-----------------------------------------------------------------------------
+// RenderPicture
+//-----------------------------------------------------------------------------
+
+CVdpauRenderPicture* CVdpauRenderPicture::Acquire()
+{
+  CSingleLock lock(renderPicSection);
+
+  if (refCount == 0)
+    vdpau->Acquire();
+
+  refCount++;
+  return this;
+}
+
+long CVdpauRenderPicture::Release()
+{
+  CSingleLock lock(renderPicSection);
+
+  refCount--;
+  if (refCount > 0)
+    return refCount;
+
+  lock.Leave();
+  vdpau->ReturnRenderPicture(this);
+  vdpau->ReleasePicReference();
+
+  return refCount;
+}
+
+void CVdpauRenderPicture::ReturnUnused()
+{
+  { CSingleLock lock(renderPicSection);
+    if (refCount > 0)
+      return;
   }
-  if (m_videoSurfaces[i]->surface == VDP_INVALID_HANDLE)
+  if (vdpau)
+    vdpau->ReturnRenderPicture(this);
+}
+
+void CVdpauRenderPicture::Sync()
+{
+#ifdef GL_ARB_sync
+  CSingleLock lock(renderPicSection);
+  if (usefence)
   {
-    m_videoSurfaces[i]->state = 0;
-    return false;
+    if(glIsSync(fence))
+    {
+      glDeleteSync(fence);
+      fence = None;
+    }
+    fence = glFenceSync(GL_SYNC_GPU_COMMANDS_COMPLETE, 0);
   }
+#endif
+}
 
-  return true;
+//-----------------------------------------------------------------------------
+// Mixer
+//-----------------------------------------------------------------------------
+CMixer::CMixer(CEvent *inMsgEvent) :
+  CThread("Vdpau Mixer"),
+  m_controlPort("ControlPort", inMsgEvent, &m_outMsgEvent),
+  m_dataPort("DataPort", inMsgEvent, &m_outMsgEvent)
+{
+  m_inMsgEvent = inMsgEvent;
 }
 
-int CVDPAU::FFGetBuffer(AVCodecContext *avctx, AVFrame *pic)
+CMixer::~CMixer()
 {
-  //CLog::Log(LOGNOTICE,"%s",__FUNCTION__);
-  CDVDVideoCodecFFmpeg* ctx        = (CDVDVideoCodecFFmpeg*)avctx->opaque;
-  CVDPAU*               vdp        = (CVDPAU*)ctx->GetHardware();
-  struct pictureAge*    pA         = &vdp->picAge;
+  Dispose();
+}
 
-  // while we are waiting to recover we can't do anything
-  CSharedLock lock(vdp->m_DecoderSection);
+void CMixer::Start()
+{
+  Create();
+}
+
+void CMixer::Dispose()
+{
+  m_bStop = true;
+  m_outMsgEvent.Set();
+  StopThread();
+
+  m_controlPort.Purge();
+  m_dataPort.Purge();
+}
+
+bool CMixer::IsActive()
+{
+  return IsRunning();
+}
+
+void CMixer::OnStartup()
+{
+  CLog::Log(LOGNOTICE, "CMixer::OnStartup: Output Thread created");
+}
+
+void CMixer::OnExit()
+{
+  CLog::Log(LOGNOTICE, "CMixer::OnExit: Output Thread terminated");
+}
 
-  { CSharedLock dLock(vdp->m_DisplaySection);
-    if(vdp->m_DisplayState != VDPAU_OPEN)
+enum MIXER_STATES
+{
+  M_TOP = 0,                      // 0
+  M_TOP_ERROR,                    // 1
+  M_TOP_UNCONFIGURED,             // 2
+  M_TOP_CONFIGURED,               // 3
+  M_TOP_CONFIGURED_WAIT1,         // 4
+  M_TOP_CONFIGURED_STEP1,         // 5
+  M_TOP_CONFIGURED_WAIT2,         // 6
+  M_TOP_CONFIGURED_STEP2,         // 7
+};
+
+int MIXER_parentStates[] = {
+    -1,
+    0, //TOP_ERROR
+    0, //TOP_UNCONFIGURED
+    0, //TOP_CONFIGURED
+    3, //TOP_CONFIGURED_WAIT1
+    3, //TOP_CONFIGURED_STEP1
+    3, //TOP_CONFIGURED_WAIT2
+    3, //TOP_CONFIGURED_STEP2
+};
+
+void CMixer::StateMachine(int signal, Protocol *port, Message *msg)
+{
+  for (int state = m_state; ; state = MIXER_parentStates[state])
+  {
+    switch (state)
+    {
+    case M_TOP: // TOP
+      if (port == &m_controlPort)
+      {
+        switch (signal)
+        {
+        case CMixerControlProtocol::FLUSH:
+          Flush();
+          msg->Reply(CMixerControlProtocol::ACC);
+          return;
+        default:
+          break;
+        }
+      }
+      {
+        std::string portName = port == NULL ? "timer" : port->portName;
+        CLog::Log(LOGWARNING, "CMixer::%s - signal: %d form port: %s not handled for state: %d", __FUNCTION__, signal, portName.c_str(), m_state);
+      }
+      return;
+
+    case M_TOP_ERROR: // TOP
+      break;
+
+    case M_TOP_UNCONFIGURED:
+      if (port == &m_controlPort)
+      {
+        switch (signal)
+        {
+        case CMixerControlProtocol::INIT:
+          CVdpauConfig *data;
+          data = (CVdpauConfig*)msg->data;
+          if (data)
+          {
+            m_config = *data;
+          }
+          Init();
+          if (!m_vdpError)
+          {
+            m_state = M_TOP_CONFIGURED_WAIT1;
+            msg->Reply(CMixerControlProtocol::ACC);
+          }
+          else
+          {
+            msg->Reply(CMixerControlProtocol::ERROR);
+          }
+          return;
+        default:
+          break;
+        }
+      }
+      break;
+
+    case M_TOP_CONFIGURED:
+      if (port == &m_dataPort)
+      {
+        switch (signal)
+        {
+        case CMixerDataProtocol::FRAME:
+          CVdpauDecodedPicture *frame;
+          frame = (CVdpauDecodedPicture*)msg->data;
+          if (frame)
+          {
+            m_decodedPics.push(*frame);
+          }
+          m_extTimeout = 0;
+          return;
+        case CMixerDataProtocol::BUFFER:
+          VdpOutputSurface *surf;
+          surf = (VdpOutputSurface*)msg->data;
+          if (surf)
+          {
+            m_outputSurfaces.push(*surf);
+          }
+          m_extTimeout = 0;
+          return;
+        default:
+          break;
+        }
+      }
+      break;
+
+    case M_TOP_CONFIGURED_WAIT1:
+      if (port == NULL) // timeout
+      {
+        switch (signal)
+        {
+        case CMixerControlProtocol::TIMEOUT:
+          if (!m_decodedPics.empty() && !m_outputSurfaces.empty())
+          {
+            m_state = M_TOP_CONFIGURED_STEP1;
+            m_bStateMachineSelfTrigger = true;
+          }
+          else
+          {
+            m_extTimeout = 100;
+          }
+          return;
+        default:
+          break;
+        }
+      }
+      break;
+
+    case M_TOP_CONFIGURED_STEP1:
+      if (port == NULL) // timeout
+      {
+        switch (signal)
+        {
+        case CMixerControlProtocol::TIMEOUT:
+          m_mixerInput.push_front(m_decodedPics.front());
+          m_decodedPics.pop();
+          if (m_mixerInput.size() < 2)
+          {
+            m_state = M_TOP_CONFIGURED_WAIT1;
+            m_extTimeout = 0;
+            return;
+          }
+          InitCycle();
+          ProcessPicture();
+          if (m_vdpError)
+          {
+            m_state = M_TOP_CONFIGURED_WAIT1;
+            m_extTimeout = 1000;
+            return;
+          }
+          if (m_processPicture.DVDPic.format != RENDER_FMT_VDPAU_420)
+            m_outputSurfaces.pop();
+          m_config.stats->IncProcessed();
+          m_config.stats->DecDecoded();
+          m_dataPort.SendInMessage(CMixerDataProtocol::PICTURE,&m_processPicture,sizeof(m_processPicture));
+          if (m_mixersteps > 1)
+          {
+            m_state = M_TOP_CONFIGURED_WAIT2;
+            m_extTimeout = 0;
+          }
+          else
+          {
+            FiniCycle();
+            m_state = M_TOP_CONFIGURED_WAIT1;
+            m_extTimeout = 0;
+          }
+          return;
+        default:
+          break;
+        }
+      }
+      break;
+
+    case M_TOP_CONFIGURED_WAIT2:
+      if (port == NULL) // timeout
+      {
+        switch (signal)
+        {
+        case CMixerControlProtocol::TIMEOUT:
+          if (!m_outputSurfaces.empty())
+          {
+            m_state = M_TOP_CONFIGURED_STEP2;
+            m_bStateMachineSelfTrigger = true;
+          }
+          else
+          {
+            m_extTimeout = 100;
+          }
+          return;
+        default:
+          break;
+        }
+      }
+      break;
+
+    case M_TOP_CONFIGURED_STEP2:
+       if (port == NULL) // timeout
+       {
+         switch (signal)
+         {
+         case CMixerControlProtocol::TIMEOUT:
+           m_processPicture.outputSurface = m_outputSurfaces.front();
+           m_mixerstep = 1;
+           ProcessPicture();
+           if (m_vdpError)
+           {
+             m_state = M_TOP_CONFIGURED_WAIT1;
+             m_extTimeout = 1000;
+             return;
+           }
+           if (m_processPicture.DVDPic.format != RENDER_FMT_VDPAU_420)
+             m_outputSurfaces.pop();
+           m_config.stats->IncProcessed();
+           m_dataPort.SendInMessage(CMixerDataProtocol::PICTURE,&m_processPicture,sizeof(m_processPicture));
+           FiniCycle();
+           m_state = M_TOP_CONFIGURED_WAIT1;
+           m_extTimeout = 0;
+           return;
+         default:
+           break;
+         }
+       }
+       break;
+
+    default: // we are in no state, should not happen
+      CLog::Log(LOGERROR, "CMixer::%s - no valid state: %d", __FUNCTION__, m_state);
+      return;
+    }
+  } // for
+}
+
+void CMixer::Process()
+{
+  Message *msg = NULL;
+  Protocol *port = NULL;
+  bool gotMsg;
+
+  m_state = M_TOP_UNCONFIGURED;
+  m_extTimeout = 1000;
+  m_bStateMachineSelfTrigger = false;
+
+  while (!m_bStop)
+  {
+    gotMsg = false;
+
+    if (m_bStateMachineSelfTrigger)
+    {
+      m_bStateMachineSelfTrigger = false;
+      // self trigger state machine
+      StateMachine(msg->signal, port, msg);
+      if (!m_bStateMachineSelfTrigger)
+      {
+        msg->Release();
+        msg = NULL;
+      }
+      continue;
+    }
+    // check control port
+    else if (m_controlPort.ReceiveOutMessage(&msg))
+    {
+      gotMsg = true;
+      port = &m_controlPort;
+    }
+    // check data port
+    else if (m_dataPort.ReceiveOutMessage(&msg))
+    {
+      gotMsg = true;
+      port = &m_dataPort;
+    }
+
+    if (gotMsg)
+    {
+      StateMachine(msg->signal, port, msg);
+      if (!m_bStateMachineSelfTrigger)
+      {
+        msg->Release();
+        msg = NULL;
+      }
+      continue;
+    }
+
+    // wait for message
+    else if (m_outMsgEvent.WaitMSec(m_extTimeout))
+    {
+      continue;
+    }
+    // time out
+    else
+    {
+      msg = m_controlPort.GetMessage();
+      msg->signal = CMixerControlProtocol::TIMEOUT;
+      port = 0;
+      // signal timeout to state machine
+      StateMachine(msg->signal, port, msg);
+      if (!m_bStateMachineSelfTrigger)
+      {
+        msg->Release();
+        msg = NULL;
+      }
+    }
+  }
+  Uninit();
+}
+
+void CMixer::CreateVdpauMixer()
+{
+  CLog::Log(LOGNOTICE, " (VDPAU) Creating the video mixer");
+
+  InitCSCMatrix(m_config.vidWidth);
+
+  VdpVideoMixerParameter parameters[] = {
+    VDP_VIDEO_MIXER_PARAMETER_VIDEO_SURFACE_WIDTH,
+    VDP_VIDEO_MIXER_PARAMETER_VIDEO_SURFACE_HEIGHT,
+    VDP_VIDEO_MIXER_PARAMETER_CHROMA_TYPE};
+
+  void const * parameter_values[] = {
+    &m_config.surfaceWidth,
+    &m_config.surfaceHeight,
+    &m_config.vdpChromaType};
+
+  VdpStatus vdp_st = VDP_STATUS_ERROR;
+  vdp_st = m_config.context->GetProcs().vdp_video_mixer_create(m_config.context->GetDevice(),
+                                m_config.context->GetFeatureCount(),
+                                m_config.context->GetFeatures(),
+                                ARSIZE(parameters),
+                                parameters,
+                                parameter_values,
+                                &m_videoMixer);
+  CheckStatus(vdp_st, __LINE__);
+
+}
+
+void CMixer::InitCSCMatrix(int Width)
+{
+  m_Procamp.struct_version = VDP_PROCAMP_VERSION;
+  m_Procamp.brightness     = 0.0;
+  m_Procamp.contrast       = 1.0;
+  m_Procamp.saturation     = 1.0;
+  m_Procamp.hue            = 0;
+}
+
+void CMixer::CheckFeatures()
+{
+  if (m_Upscale != m_config.upscale)
+  {
+    SetHWUpscaling();
+    m_Upscale = m_config.upscale;
+  }
+  if (m_Brightness != CMediaSettings::Get().GetCurrentVideoSettings().m_Brightness ||
+      m_Contrast   != CMediaSettings::Get().GetCurrentVideoSettings().m_Contrast ||
+      m_ColorMatrix != m_mixerInput[1].DVDPic.color_matrix)
+  {
+    SetColor();
+    m_Brightness = CMediaSettings::Get().GetCurrentVideoSettings().m_Brightness;
+    m_Contrast = CMediaSettings::Get().GetCurrentVideoSettings().m_Contrast;
+    m_ColorMatrix = m_mixerInput[1].DVDPic.color_matrix;
+  }
+  if (m_NoiseReduction != CMediaSettings::Get().GetCurrentVideoSettings().m_NoiseReduction)
+  {
+    m_NoiseReduction = CMediaSettings::Get().GetCurrentVideoSettings().m_NoiseReduction;
+    SetNoiseReduction();
+  }
+  if (m_Sharpness != CMediaSettings::Get().GetCurrentVideoSettings().m_Sharpness)
+  {
+    m_Sharpness = CMediaSettings::Get().GetCurrentVideoSettings().m_Sharpness;
+    SetSharpness();
+  }
+  if (m_DeintMode != CMediaSettings::Get().GetCurrentVideoSettings().m_DeinterlaceMode ||
+      m_Deint     != CMediaSettings::Get().GetCurrentVideoSettings().m_InterlaceMethod)
+  {
+    m_DeintMode = CMediaSettings::Get().GetCurrentVideoSettings().m_DeinterlaceMode;
+    m_Deint     = CMediaSettings::Get().GetCurrentVideoSettings().m_InterlaceMethod;
+    SetDeinterlacing();
+  }
+}
+
+void CMixer::SetPostProcFeatures(bool postProcEnabled)
+{
+  if (m_PostProc != postProcEnabled)
+  {
+    if (postProcEnabled)
+    {
+      SetNoiseReduction();
+      SetSharpness();
+      SetDeinterlacing();
+      SetHWUpscaling();
+    }
+    else
+      PostProcOff();
+    m_PostProc = postProcEnabled;
+  }
+}
+
+void CMixer::PostProcOff()
+{
+  VdpStatus vdp_st;
+
+  if (m_videoMixer == VDP_INVALID_HANDLE)
+    return;
+
+  VdpVideoMixerFeature feature[] = { VDP_VIDEO_MIXER_FEATURE_DEINTERLACE_TEMPORAL,
+                                     VDP_VIDEO_MIXER_FEATURE_DEINTERLACE_TEMPORAL_SPATIAL,
+                                     VDP_VIDEO_MIXER_FEATURE_INVERSE_TELECINE};
+
+  VdpBool enabled[]={0,0,0};
+  vdp_st = m_config.context->GetProcs().vdp_video_mixer_set_feature_enables(m_videoMixer, ARSIZE(feature), feature, enabled);
+  CheckStatus(vdp_st, __LINE__);
+
+  if(m_config.vdpau->Supports(VDP_VIDEO_MIXER_FEATURE_NOISE_REDUCTION))
+  {
+    VdpVideoMixerFeature feature[] = { VDP_VIDEO_MIXER_FEATURE_NOISE_REDUCTION};
+
+    VdpBool enabled[]={0};
+    vdp_st = m_config.context->GetProcs().vdp_video_mixer_set_feature_enables(m_videoMixer, ARSIZE(feature), feature, enabled);
+    CheckStatus(vdp_st, __LINE__);
+  }
+
+  if(m_config.vdpau->Supports(VDP_VIDEO_MIXER_FEATURE_SHARPNESS))
+  {
+    VdpVideoMixerFeature feature[] = { VDP_VIDEO_MIXER_FEATURE_SHARPNESS};
+
+    VdpBool enabled[]={0};
+    vdp_st = m_config.context->GetProcs().vdp_video_mixer_set_feature_enables(m_videoMixer, ARSIZE(feature), feature, enabled);
+    CheckStatus(vdp_st, __LINE__);
+  }
+
+  DisableHQScaling();
+}
+
+bool CMixer::GenerateStudioCSCMatrix(VdpColorStandard colorStandard, VdpCSCMatrix &studioCSCMatrix)
+{
+   // instead use studioCSCKCoeffs601[3], studioCSCKCoeffs709[3] to generate float[3][4] matrix (float studioCSC[3][4])
+   // m00 = mRY = red: luma factor (contrast factor) (1.0)
+   // m10 = mGY = green: luma factor (contrast factor) (1.0)
+   // m20 = mBY = blue: luma factor (contrast factor) (1.0)
+   //
+   // m01 = mRB = red: blue color diff coeff (0.0)
+   // m11 = mGB = green: blue color diff coeff (-2Kb(1-Kb)/(Kg))
+   // m21 = mBB = blue: blue color diff coeff ((1-Kb)/0.5)
+   //
+   // m02 = mRR = red: red color diff coeff ((1-Kr)/0.5)
+   // m12 = mGR = green: red color diff coeff (-2Kr(1-Kr)/(Kg))
+   // m22 = mBR = blue: red color diff coeff (0.0)
+   //
+   // m03 = mRC = red: colour zero offset (brightness factor) (-(1-Kr)/0.5 * (128/255))
+   // m13 = mGC = green: colour zero offset (brightness factor) ((256/255) * (Kb(1-Kb) + Kr(1-Kr)) / Kg)
+   // m23 = mBC = blue: colour zero offset (brightness factor) (-(1-Kb)/0.5 * (128/255))
+
+   // columns
+   int Y = 0;
+   int Cb = 1;
+   int Cr = 2;
+   int C = 3;
+   // rows
+   int R = 0;
+   int G = 1;
+   int B = 2;
+   // colour standard coefficients for red, geen, blue
+   double Kr, Kg, Kb;
+   // colour diff zero position (use standard 8-bit coding precision)
+   double CDZ = 128; //256*0.5
+   // range excursion (use standard 8-bit coding precision)
+   double EXC = 255; //256-1
+
+   if (colorStandard == VDP_COLOR_STANDARD_ITUR_BT_601)
+   {
+      Kr = studioCSCKCoeffs601[0];
+      Kg = studioCSCKCoeffs601[1];
+      Kb = studioCSCKCoeffs601[2];
+   }
+   else // assume VDP_COLOR_STANDARD_ITUR_BT_709
+   {
+      Kr = studioCSCKCoeffs709[0];
+      Kg = studioCSCKCoeffs709[1];
+      Kb = studioCSCKCoeffs709[2];
+   }
+   // we keep luma unscaled to retain the levels present in source so that 16-235 luma is converted to RGB 16-235
+   studioCSCMatrix[R][Y] = 1.0;
+   studioCSCMatrix[G][Y] = 1.0;
+   studioCSCMatrix[B][Y] = 1.0;
+
+   studioCSCMatrix[R][Cb] = 0.0;
+   studioCSCMatrix[G][Cb] = (double)-2 * Kb * (1 - Kb) / Kg;
+   studioCSCMatrix[B][Cb] = (double)(1 - Kb) / 0.5;
+
+   studioCSCMatrix[R][Cr] = (double)(1 - Kr) / 0.5;
+   studioCSCMatrix[G][Cr] = (double)-2 * Kr * (1 - Kr) / Kg;
+   studioCSCMatrix[B][Cr] = 0.0;
+
+   studioCSCMatrix[R][C] = (double)-1 * studioCSCMatrix[R][Cr] * CDZ/EXC;
+   studioCSCMatrix[G][C] = (double)-1 * (studioCSCMatrix[G][Cb] + studioCSCMatrix[G][Cr]) * CDZ/EXC;
+   studioCSCMatrix[B][C] = (double)-1 * studioCSCMatrix[B][Cb] * CDZ/EXC;
+
+   return true;
+}
+
+void CMixer::SetColor()
+{
+  VdpStatus vdp_st;
+
+  if (m_Brightness != CMediaSettings::Get().GetCurrentVideoSettings().m_Brightness)
+    m_Procamp.brightness = (float)((CMediaSettings::Get().GetCurrentVideoSettings().m_Brightness)-50) / 100;
+  if (m_Contrast != CMediaSettings::Get().GetCurrentVideoSettings().m_Contrast)
+    m_Procamp.contrast = (float)((CMediaSettings::Get().GetCurrentVideoSettings().m_Contrast)+50) / 100;
+
+  VdpColorStandard colorStandard;
+  switch(m_mixerInput[1].DVDPic.color_matrix)
+  {
+    case AVCOL_SPC_BT709:
+      colorStandard = VDP_COLOR_STANDARD_ITUR_BT_709;
+      break;
+    case AVCOL_SPC_BT470BG:
+    case AVCOL_SPC_SMPTE170M:
+      colorStandard = VDP_COLOR_STANDARD_ITUR_BT_601;
+      break;
+    case AVCOL_SPC_SMPTE240M:
+      colorStandard = VDP_COLOR_STANDARD_SMPTE_240M;
+      break;
+    case AVCOL_SPC_FCC:
+    case AVCOL_SPC_UNSPECIFIED:
+    case AVCOL_SPC_RGB:
+    default:
+      if(m_config.surfaceWidth > 1000)
+        colorStandard = VDP_COLOR_STANDARD_ITUR_BT_709;
+      else
+        colorStandard = VDP_COLOR_STANDARD_ITUR_BT_601;
+  }
+
+  VdpVideoMixerAttribute attributes[] = { VDP_VIDEO_MIXER_ATTRIBUTE_CSC_MATRIX };
+  if (CSettings::Get().GetBool("videoscreen.limitedrange"))
+  {
+    float studioCSC[3][4];
+    GenerateStudioCSCMatrix(colorStandard, studioCSC);
+    void const * pm_CSCMatix[] = { &studioCSC };
+    vdp_st = m_config.context->GetProcs().vdp_video_mixer_set_attribute_values(m_videoMixer, ARSIZE(attributes), attributes, pm_CSCMatix);
+  }
+  else
+  {
+    vdp_st = m_config.context->GetProcs().vdp_generate_csc_matrix(&m_Procamp, colorStandard, &m_CSCMatrix);
+    void const * pm_CSCMatix[] = { &m_CSCMatrix };
+    vdp_st = m_config.context->GetProcs().vdp_video_mixer_set_attribute_values(m_videoMixer, ARSIZE(attributes), attributes, pm_CSCMatix);
+  }
+
+  CheckStatus(vdp_st, __LINE__);
+}
+
+void CMixer::SetNoiseReduction()
+{
+  if(!m_config.vdpau->Supports(VDP_VIDEO_MIXER_FEATURE_NOISE_REDUCTION))
+    return;
+
+  VdpVideoMixerFeature feature[] = { VDP_VIDEO_MIXER_FEATURE_NOISE_REDUCTION };
+  VdpVideoMixerAttribute attributes[] = { VDP_VIDEO_MIXER_ATTRIBUTE_NOISE_REDUCTION_LEVEL };
+  VdpStatus vdp_st;
+
+  if (!CMediaSettings::Get().GetCurrentVideoSettings().m_NoiseReduction)
+  {
+    VdpBool enabled[]= {0};
+    vdp_st = m_config.context->GetProcs().vdp_video_mixer_set_feature_enables(m_videoMixer, ARSIZE(feature), feature, enabled);
+    CheckStatus(vdp_st, __LINE__);
+    return;
+  }
+  VdpBool enabled[]={1};
+  vdp_st = m_config.context->GetProcs().vdp_video_mixer_set_feature_enables(m_videoMixer, ARSIZE(feature), feature, enabled);
+  CheckStatus(vdp_st, __LINE__);
+  void* nr[] = { &CMediaSettings::Get().GetCurrentVideoSettings().m_NoiseReduction };
+  CLog::Log(LOGNOTICE,"Setting Noise Reduction to %f",CMediaSettings::Get().GetCurrentVideoSettings().m_NoiseReduction);
+  vdp_st = m_config.context->GetProcs().vdp_video_mixer_set_attribute_values(m_videoMixer, ARSIZE(attributes), attributes, nr);
+  CheckStatus(vdp_st, __LINE__);
+}
+
+void CMixer::SetSharpness()
+{
+  if(!m_config.vdpau->Supports(VDP_VIDEO_MIXER_FEATURE_SHARPNESS))
+    return;
+
+  VdpVideoMixerFeature feature[] = { VDP_VIDEO_MIXER_FEATURE_SHARPNESS };
+  VdpVideoMixerAttribute attributes[] = { VDP_VIDEO_MIXER_ATTRIBUTE_SHARPNESS_LEVEL };
+  VdpStatus vdp_st;
+
+  if (!CMediaSettings::Get().GetCurrentVideoSettings().m_Sharpness)
+  {
+    VdpBool enabled[]={0};
+    vdp_st = m_config.context->GetProcs().vdp_video_mixer_set_feature_enables(m_videoMixer, ARSIZE(feature), feature, enabled);
+    CheckStatus(vdp_st, __LINE__);
+    return;
+  }
+  VdpBool enabled[]={1};
+  vdp_st = m_config.context->GetProcs().vdp_video_mixer_set_feature_enables(m_videoMixer, ARSIZE(feature), feature, enabled);
+  CheckStatus(vdp_st, __LINE__);
+  void* sh[] = { &CMediaSettings::Get().GetCurrentVideoSettings().m_Sharpness };
+  CLog::Log(LOGNOTICE,"Setting Sharpness to %f",CMediaSettings::Get().GetCurrentVideoSettings().m_Sharpness);
+  vdp_st = m_config.context->GetProcs().vdp_video_mixer_set_attribute_values(m_videoMixer, ARSIZE(attributes), attributes, sh);
+  CheckStatus(vdp_st, __LINE__);
+}
+
+EINTERLACEMETHOD CMixer::GetDeinterlacingMethod(bool log /* = false */)
+{
+  EINTERLACEMETHOD method = CMediaSettings::Get().GetCurrentVideoSettings().m_InterlaceMethod;
+  if (method == VS_INTERLACEMETHOD_AUTO)
+  {
+    int deint = -1;
+//    if (m_config.outHeight >= 720)
+//      deint = g_advancedSettings.m_videoVDPAUdeintHD;
+//    else
+//      deint = g_advancedSettings.m_videoVDPAUdeintSD;
+
+    if (deint != -1)
+    {
+      if (m_config.vdpau->Supports(EINTERLACEMETHOD(deint)))
+      {
+        method = EINTERLACEMETHOD(deint);
+        if (log)
+          CLog::Log(LOGNOTICE, "CVDPAU::GetDeinterlacingMethod: set de-interlacing to %d",  deint);
+      }
+      else
+      {
+        if (log)
+          CLog::Log(LOGWARNING, "CVDPAU::GetDeinterlacingMethod: method for de-interlacing (advanced settings) not supported");
+      }
+    }
+  }
+  return method;
+}
+
+void CMixer::SetDeinterlacing()
+{
+  VdpStatus vdp_st;
+
+  if (m_videoMixer == VDP_INVALID_HANDLE)
+    return;
+
+  EDEINTERLACEMODE   mode = CMediaSettings::Get().GetCurrentVideoSettings().m_DeinterlaceMode;
+  EINTERLACEMETHOD method = GetDeinterlacingMethod(true);
+
+  VdpVideoMixerFeature feature[] = { VDP_VIDEO_MIXER_FEATURE_DEINTERLACE_TEMPORAL,
+                                     VDP_VIDEO_MIXER_FEATURE_DEINTERLACE_TEMPORAL_SPATIAL,
+                                     VDP_VIDEO_MIXER_FEATURE_INVERSE_TELECINE };
+
+  if (mode == VS_DEINTERLACEMODE_OFF)
+  {
+    VdpBool enabled[] = {0,0,0};
+    vdp_st = m_config.context->GetProcs().vdp_video_mixer_set_feature_enables(m_videoMixer, ARSIZE(feature), feature, enabled);
+  }
+  else
+  {
+    if (method == VS_INTERLACEMETHOD_AUTO)
+    {
+      VdpBool enabled[] = {1,0,0};
+      if (g_advancedSettings.m_videoVDPAUtelecine)
+        enabled[2] = 1;
+      vdp_st = m_config.context->GetProcs().vdp_video_mixer_set_feature_enables(m_videoMixer, ARSIZE(feature), feature, enabled);
+    }
+    else if (method == VS_INTERLACEMETHOD_VDPAU_TEMPORAL
+         ||  method == VS_INTERLACEMETHOD_VDPAU_TEMPORAL_HALF)
+    {
+      VdpBool enabled[] = {1,0,0};
+      if (g_advancedSettings.m_videoVDPAUtelecine)
+        enabled[2] = 1;
+      vdp_st = m_config.context->GetProcs().vdp_video_mixer_set_feature_enables(m_videoMixer, ARSIZE(feature), feature, enabled);
+    }
+    else if (method == VS_INTERLACEMETHOD_VDPAU_TEMPORAL_SPATIAL
+         ||  method == VS_INTERLACEMETHOD_VDPAU_TEMPORAL_SPATIAL_HALF)
+    {
+      VdpBool enabled[] = {1,1,0};
+      if (g_advancedSettings.m_videoVDPAUtelecine)
+        enabled[2] = 1;
+      vdp_st = m_config.context->GetProcs().vdp_video_mixer_set_feature_enables(m_videoMixer, ARSIZE(feature), feature, enabled);
+    }
+    else
+    {
+      VdpBool enabled[]={0,0,0};
+      vdp_st = m_config.context->GetProcs().vdp_video_mixer_set_feature_enables(m_videoMixer, ARSIZE(feature), feature, enabled);
+    }
+  }
+  CheckStatus(vdp_st, __LINE__);
+
+  SetDeintSkipChroma();
+
+  m_config.useInteropYuv = !CSettings::Get().GetBool("videoplayer.usevdpaumixer");
+}
+
+void CMixer::SetDeintSkipChroma()
+{
+  VdpVideoMixerAttribute attribute[] = { VDP_VIDEO_MIXER_ATTRIBUTE_SKIP_CHROMA_DEINTERLACE};
+  VdpStatus vdp_st;
+
+  uint8_t val;
+  if (g_advancedSettings.m_videoVDPAUdeintSkipChromaHD && m_config.outHeight >= 720)
+    val = 1;
+  else
+    val = 0;
+
+  void const *values[]={&val};
+  vdp_st = m_config.context->GetProcs().vdp_video_mixer_set_attribute_values(m_videoMixer, ARSIZE(attribute), attribute, values);
+
+  CheckStatus(vdp_st, __LINE__);
+}
+
+void CMixer::SetHWUpscaling()
+{
+#ifdef VDP_VIDEO_MIXER_FEATURE_HIGH_QUALITY_SCALING_L1
+
+  VdpStatus vdp_st;
+  VdpBool enabled[]={1};
+  switch (m_config.upscale)
+  {
+    case 9:
+       if (m_config.vdpau->Supports(VDP_VIDEO_MIXER_FEATURE_HIGH_QUALITY_SCALING_L9))
+       {
+          VdpVideoMixerFeature feature[] = { VDP_VIDEO_MIXER_FEATURE_HIGH_QUALITY_SCALING_L9 };
+          vdp_st = m_config.context->GetProcs().vdp_video_mixer_set_feature_enables(m_videoMixer, ARSIZE(feature), feature, enabled);
+          break;
+       }
+    case 8:
+       if (m_config.vdpau->Supports(VDP_VIDEO_MIXER_FEATURE_HIGH_QUALITY_SCALING_L8))
+       {
+          VdpVideoMixerFeature feature[] = { VDP_VIDEO_MIXER_FEATURE_HIGH_QUALITY_SCALING_L8 };
+          vdp_st = m_config.context->GetProcs().vdp_video_mixer_set_feature_enables(m_videoMixer, ARSIZE(feature), feature, enabled);
+          break;
+       }
+    case 7:
+       if (m_config.vdpau->Supports(VDP_VIDEO_MIXER_FEATURE_HIGH_QUALITY_SCALING_L7))
+       {
+          VdpVideoMixerFeature feature[] = { VDP_VIDEO_MIXER_FEATURE_HIGH_QUALITY_SCALING_L7 };
+          vdp_st = m_config.context->GetProcs().vdp_video_mixer_set_feature_enables(m_videoMixer, ARSIZE(feature), feature, enabled);
+          break;
+       }
+    case 6:
+       if (m_config.vdpau->Supports(VDP_VIDEO_MIXER_FEATURE_HIGH_QUALITY_SCALING_L6))
+       {
+          VdpVideoMixerFeature feature[] = { VDP_VIDEO_MIXER_FEATURE_HIGH_QUALITY_SCALING_L6 };
+          vdp_st = m_config.context->GetProcs().vdp_video_mixer_set_feature_enables(m_videoMixer, ARSIZE(feature), feature, enabled);
+          break;
+       }
+    case 5:
+       if (m_config.vdpau->Supports(VDP_VIDEO_MIXER_FEATURE_HIGH_QUALITY_SCALING_L5))
+       {
+          VdpVideoMixerFeature feature[] = { VDP_VIDEO_MIXER_FEATURE_HIGH_QUALITY_SCALING_L5 };
+          vdp_st = m_config.context->GetProcs().vdp_video_mixer_set_feature_enables(m_videoMixer, ARSIZE(feature), feature, enabled);
+          break;
+       }
+    case 4:
+       if (m_config.vdpau->Supports(VDP_VIDEO_MIXER_FEATURE_HIGH_QUALITY_SCALING_L4))
+       {
+          VdpVideoMixerFeature feature[] = { VDP_VIDEO_MIXER_FEATURE_HIGH_QUALITY_SCALING_L4 };
+          vdp_st = m_config.context->GetProcs().vdp_video_mixer_set_feature_enables(m_videoMixer, ARSIZE(feature), feature, enabled);
+          break;
+       }
+    case 3:
+       if (m_config.vdpau->Supports(VDP_VIDEO_MIXER_FEATURE_HIGH_QUALITY_SCALING_L3))
+       {
+          VdpVideoMixerFeature feature[] = { VDP_VIDEO_MIXER_FEATURE_HIGH_QUALITY_SCALING_L3 };
+          vdp_st = m_config.context->GetProcs().vdp_video_mixer_set_feature_enables(m_videoMixer, ARSIZE(feature), feature, enabled);
+          break;
+       }
+    case 2:
+       if (m_config.vdpau->Supports(VDP_VIDEO_MIXER_FEATURE_HIGH_QUALITY_SCALING_L2))
+       {
+          VdpVideoMixerFeature feature[] = { VDP_VIDEO_MIXER_FEATURE_HIGH_QUALITY_SCALING_L2 };
+          vdp_st = m_config.context->GetProcs().vdp_video_mixer_set_feature_enables(m_videoMixer, ARSIZE(feature), feature, enabled);
+          break;
+       }
+    case 1:
+       if (m_config.vdpau->Supports(VDP_VIDEO_MIXER_FEATURE_HIGH_QUALITY_SCALING_L1))
+       {
+          VdpVideoMixerFeature feature[] = { VDP_VIDEO_MIXER_FEATURE_HIGH_QUALITY_SCALING_L1 };
+          vdp_st = m_config.context->GetProcs().vdp_video_mixer_set_feature_enables(m_videoMixer, ARSIZE(feature), feature, enabled);
+          break;
+       }
+    default:
+       DisableHQScaling();
+       return;
+  }
+  CheckStatus(vdp_st, __LINE__);
+#endif
+}
+
+void CMixer::DisableHQScaling()
+{
+  VdpStatus vdp_st;
+
+  if (m_videoMixer == VDP_INVALID_HANDLE)
+    return;
+
+  if(m_config.vdpau->Supports(VDP_VIDEO_MIXER_FEATURE_HIGH_QUALITY_SCALING_L1))
+  {
+    VdpVideoMixerFeature feature[] = { VDP_VIDEO_MIXER_FEATURE_HIGH_QUALITY_SCALING_L1 };
+    VdpBool enabled[]={0};
+    vdp_st = m_config.context->GetProcs().vdp_video_mixer_set_feature_enables(m_videoMixer, ARSIZE(feature), feature, enabled);
+    CheckStatus(vdp_st, __LINE__);
+  }
+
+  if(m_config.vdpau->Supports(VDP_VIDEO_MIXER_FEATURE_HIGH_QUALITY_SCALING_L2))
+  {
+    VdpVideoMixerFeature feature[] = { VDP_VIDEO_MIXER_FEATURE_HIGH_QUALITY_SCALING_L2 };
+    VdpBool enabled[]={0};
+    vdp_st = m_config.context->GetProcs().vdp_video_mixer_set_feature_enables(m_videoMixer, ARSIZE(feature), feature, enabled);
+    CheckStatus(vdp_st, __LINE__);
+  }
+
+  if(m_config.vdpau->Supports(VDP_VIDEO_MIXER_FEATURE_HIGH_QUALITY_SCALING_L3))
+  {
+    VdpVideoMixerFeature feature[] = { VDP_VIDEO_MIXER_FEATURE_HIGH_QUALITY_SCALING_L3 };
+    VdpBool enabled[]={0};
+    vdp_st = m_config.context->GetProcs().vdp_video_mixer_set_feature_enables(m_videoMixer, ARSIZE(feature), feature, enabled);
+    CheckStatus(vdp_st, __LINE__);
+  }
+
+  if(m_config.vdpau->Supports(VDP_VIDEO_MIXER_FEATURE_HIGH_QUALITY_SCALING_L4))
+  {
+    VdpVideoMixerFeature feature[] = { VDP_VIDEO_MIXER_FEATURE_HIGH_QUALITY_SCALING_L4 };
+    VdpBool enabled[]={0};
+    vdp_st = m_config.context->GetProcs().vdp_video_mixer_set_feature_enables(m_videoMixer, ARSIZE(feature), feature, enabled);
+    CheckStatus(vdp_st, __LINE__);
+  }
+
+  if(m_config.vdpau->Supports(VDP_VIDEO_MIXER_FEATURE_HIGH_QUALITY_SCALING_L5))
+  {
+    VdpVideoMixerFeature feature[] = { VDP_VIDEO_MIXER_FEATURE_HIGH_QUALITY_SCALING_L5 };
+    VdpBool enabled[]={0};
+    vdp_st = m_config.context->GetProcs().vdp_video_mixer_set_feature_enables(m_videoMixer, ARSIZE(feature), feature, enabled);
+    CheckStatus(vdp_st, __LINE__);
+  }
+
+  if(m_config.vdpau->Supports(VDP_VIDEO_MIXER_FEATURE_HIGH_QUALITY_SCALING_L6))
+  {
+    VdpVideoMixerFeature feature[] = { VDP_VIDEO_MIXER_FEATURE_HIGH_QUALITY_SCALING_L6 };
+    VdpBool enabled[]={0};
+    vdp_st = m_config.context->GetProcs().vdp_video_mixer_set_feature_enables(m_videoMixer, ARSIZE(feature), feature, enabled);
+    CheckStatus(vdp_st, __LINE__);
+  }
+
+  if(m_config.vdpau->Supports(VDP_VIDEO_MIXER_FEATURE_HIGH_QUALITY_SCALING_L7))
+  {
+    VdpVideoMixerFeature feature[] = { VDP_VIDEO_MIXER_FEATURE_HIGH_QUALITY_SCALING_L7 };
+    VdpBool enabled[]={0};
+    vdp_st = m_config.context->GetProcs().vdp_video_mixer_set_feature_enables(m_videoMixer, ARSIZE(feature), feature, enabled);
+    CheckStatus(vdp_st, __LINE__);
+  }
+
+  if(m_config.vdpau->Supports(VDP_VIDEO_MIXER_FEATURE_HIGH_QUALITY_SCALING_L8))
+  {
+    VdpVideoMixerFeature feature[] = { VDP_VIDEO_MIXER_FEATURE_HIGH_QUALITY_SCALING_L8 };
+    VdpBool enabled[]={0};
+    vdp_st = m_config.context->GetProcs().vdp_video_mixer_set_feature_enables(m_videoMixer, ARSIZE(feature), feature, enabled);
+    CheckStatus(vdp_st, __LINE__);
+  }
+
+  if(m_config.vdpau->Supports(VDP_VIDEO_MIXER_FEATURE_HIGH_QUALITY_SCALING_L9))
+  {
+    VdpVideoMixerFeature feature[] = { VDP_VIDEO_MIXER_FEATURE_HIGH_QUALITY_SCALING_L9 };
+    VdpBool enabled[]={0};
+    vdp_st = m_config.context->GetProcs().vdp_video_mixer_set_feature_enables(m_videoMixer, ARSIZE(feature), feature, enabled);
+    CheckStatus(vdp_st, __LINE__);
+  }
+}
+
+void CMixer::Init()
+{
+  m_Brightness = 0.0;
+  m_Contrast = 0.0;
+  m_NoiseReduction = 0.0;
+  m_Sharpness = 0.0;
+  m_DeintMode = 0;
+  m_Deint = 0;
+  m_Upscale = 0;
+  m_SeenInterlaceFlag = false;
+  m_ColorMatrix = 0;
+  m_PostProc = false;
+  m_vdpError = false;
+
+  m_config.upscale = g_advancedSettings.m_videoVDPAUScaling;
+  m_config.useInteropYuv = !CSettings::Get().GetBool("videoplayer.usevdpaumixer");
+
+  CreateVdpauMixer();
+}
+
+void CMixer::Uninit()
+{
+  Flush();
+  while (!m_outputSurfaces.empty())
+  {
+    m_outputSurfaces.pop();
+  }
+  m_config.context->GetProcs().vdp_video_mixer_destroy(m_videoMixer);
+}
+
+void CMixer::Flush()
+{
+  while (!m_mixerInput.empty())
+  {
+    CVdpauDecodedPicture pic = m_mixerInput.back();
+    m_mixerInput.pop_back();
+    m_config.videoSurfaces->ClearRender(pic.videoSurface);
+  }
+  while (!m_decodedPics.empty())
+  {
+    CVdpauDecodedPicture pic = m_decodedPics.front();
+    m_decodedPics.pop();
+    m_config.videoSurfaces->ClearRender(pic.videoSurface);
+  }
+  Message *msg;
+  while (m_dataPort.ReceiveOutMessage(&msg))
+  {
+    if (msg->signal == CMixerDataProtocol::FRAME)
+    {
+      CVdpauDecodedPicture pic = *(CVdpauDecodedPicture*)msg->data;
+      m_config.videoSurfaces->ClearRender(pic.videoSurface);
+    }
+    else if (msg->signal == CMixerDataProtocol::BUFFER)
+    {
+      VdpOutputSurface *surf;
+      surf = (VdpOutputSurface*)msg->data;
+      m_outputSurfaces.push(*surf);
+    }
+    msg->Release();
+  }
+}
+
+void CMixer::InitCycle()
+{
+  CheckFeatures();
+  int flags;
+  uint64_t latency;
+  m_config.stats->GetParams(latency, flags);
+  // TODO
+  if (0) //flags & DVP_FLAG_NO_POSTPROC)
+    SetPostProcFeatures(false);
+  else
+    SetPostProcFeatures(true);
+
+  m_config.stats->SetCanSkipDeint(false);
+
+  EDEINTERLACEMODE   mode = CMediaSettings::Get().GetCurrentVideoSettings().m_DeinterlaceMode;
+  EINTERLACEMETHOD method = GetDeinterlacingMethod();
+  bool interlaced = m_mixerInput[1].DVDPic.iFlags & DVP_FLAG_INTERLACED;
+  m_SeenInterlaceFlag |= interlaced;
+
+  // TODO
+  if (//!(flags & DVP_FLAG_NO_POSTPROC) &&
+      (mode == VS_DEINTERLACEMODE_FORCE ||
+      (mode == VS_DEINTERLACEMODE_AUTO && interlaced)))
+  {
+    if((method == VS_INTERLACEMETHOD_AUTO && interlaced)
+      ||  method == VS_INTERLACEMETHOD_VDPAU_BOB
+      ||  method == VS_INTERLACEMETHOD_VDPAU_TEMPORAL
+      ||  method == VS_INTERLACEMETHOD_VDPAU_TEMPORAL_HALF
+      ||  method == VS_INTERLACEMETHOD_VDPAU_TEMPORAL_SPATIAL
+      ||  method == VS_INTERLACEMETHOD_VDPAU_TEMPORAL_SPATIAL_HALF
+      ||  method == VS_INTERLACEMETHOD_VDPAU_INVERSE_TELECINE )
+    {
+      if(method == VS_INTERLACEMETHOD_VDPAU_TEMPORAL_HALF
+        || method == VS_INTERLACEMETHOD_VDPAU_TEMPORAL_SPATIAL_HALF
+        || !g_graphicsContext.IsFullScreenVideo())
+        m_mixersteps = 1;
+      else
+      {
+        m_mixersteps = 2;
+        m_config.stats->SetCanSkipDeint(true);
+      }
+
+      // TODO
+      if (0) //m_mixerInput[1].DVDPic.iFlags & DVP_FLAG_DROPDEINT)
+      {
+        m_mixersteps = 1;
+      }
+
+      if(m_mixerInput[1].DVDPic.iFlags & DVP_FLAG_TOP_FIELD_FIRST)
+        m_mixerfield = VDP_VIDEO_MIXER_PICTURE_STRUCTURE_TOP_FIELD;
+      else
+        m_mixerfield = VDP_VIDEO_MIXER_PICTURE_STRUCTURE_BOTTOM_FIELD;
+
+      m_mixerInput[1].DVDPic.format = RENDER_FMT_VDPAU;
+      m_mixerInput[1].DVDPic.iFlags &= ~(DVP_FLAG_TOP_FIELD_FIRST |
+                                        DVP_FLAG_REPEAT_TOP_FIELD |
+                                        DVP_FLAG_INTERLACED);
+      m_config.useInteropYuv = false;
+    }
+    else if (method == VS_INTERLACEMETHOD_RENDER_BOB)
+    {
+      m_mixersteps = 1;
+      m_mixerfield = VDP_VIDEO_MIXER_PICTURE_STRUCTURE_FRAME;
+      m_mixerInput[1].DVDPic.format = RENDER_FMT_VDPAU_420;
+      m_config.useInteropYuv = true;
+    }
+    else
+    {
+      CLog::Log(LOGERROR, "CMixer::%s - interlace method: %d not supported, setting to AUTO", __FUNCTION__, method);
+      m_mixersteps = 1;
+      m_mixerfield = VDP_VIDEO_MIXER_PICTURE_STRUCTURE_FRAME;
+      m_mixerInput[1].DVDPic.format = RENDER_FMT_VDPAU;
+      m_mixerInput[1].DVDPic.iFlags &= ~(DVP_FLAG_TOP_FIELD_FIRST |
+                                        DVP_FLAG_REPEAT_TOP_FIELD |
+                                        DVP_FLAG_INTERLACED);
+
+      CMediaSettings::Get().GetCurrentVideoSettings().m_InterlaceMethod = VS_INTERLACEMETHOD_AUTO;
+    }
+  }
+  else
+  {
+    m_mixersteps = 1;
+    m_mixerfield = VDP_VIDEO_MIXER_PICTURE_STRUCTURE_FRAME;
+
+    if (m_config.useInteropYuv)
+      m_mixerInput[1].DVDPic.format = RENDER_FMT_VDPAU_420;
+    else
+    {
+      m_mixerInput[1].DVDPic.format = RENDER_FMT_VDPAU;
+      m_mixerInput[1].DVDPic.iFlags &= ~(DVP_FLAG_TOP_FIELD_FIRST |
+                                        DVP_FLAG_REPEAT_TOP_FIELD |
+                                        DVP_FLAG_INTERLACED);
+    }
+  }
+  m_mixerstep = 0;
+
+  m_processPicture.crop = false;
+  if (m_mixerInput[1].DVDPic.format == RENDER_FMT_VDPAU)
+  {
+    m_processPicture.outputSurface = m_outputSurfaces.front();
+    m_mixerInput[1].DVDPic.iWidth = m_config.outWidth;
+    m_mixerInput[1].DVDPic.iHeight = m_config.outHeight;
+    if (m_SeenInterlaceFlag)
+    {
+      double ratio = (double)m_mixerInput[1].DVDPic.iDisplayHeight / m_mixerInput[1].DVDPic.iHeight;
+      m_mixerInput[1].DVDPic.iDisplayHeight = lrint(ratio*(m_mixerInput[1].DVDPic.iHeight-NUM_CROP_PIX*2));
+      m_processPicture.crop = true;
+    }
+  }
+  else
+  {
+    m_mixerInput[1].DVDPic.iWidth = m_config.vidWidth;
+    m_mixerInput[1].DVDPic.iHeight = m_config.vidHeight;
+  }
+
+  m_processPicture.DVDPic = m_mixerInput[1].DVDPic;
+  m_processPicture.videoSurface = m_mixerInput[1].videoSurface;
+}
+
+void CMixer::FiniCycle()
+{
+  // Keep video surfaces for one 2 cycles longer than used
+  // by mixer. This avoids blocking in decoder.
+  // NVidia recommends num_ref + 5
+  while (m_mixerInput.size() > 5)
+  {
+    CVdpauDecodedPicture &tmp = m_mixerInput.back();
+    if (m_processPicture.DVDPic.format != RENDER_FMT_VDPAU_420)
+    {
+      m_config.videoSurfaces->ClearRender(tmp.videoSurface);
+    }
+    m_mixerInput.pop_back();
+  }
+}
+
+void CMixer::ProcessPicture()
+{
+  if (m_processPicture.DVDPic.format == RENDER_FMT_VDPAU_420)
+    return;
+
+  VdpStatus vdp_st;
+
+  if (m_mixerstep == 1)
+  {
+    if(m_mixerfield == VDP_VIDEO_MIXER_PICTURE_STRUCTURE_TOP_FIELD)
+      m_mixerfield = VDP_VIDEO_MIXER_PICTURE_STRUCTURE_BOTTOM_FIELD;
+    else
+      m_mixerfield = VDP_VIDEO_MIXER_PICTURE_STRUCTURE_TOP_FIELD;
+  }
+
+  VdpVideoSurface past_surfaces[4] = { VDP_INVALID_HANDLE, VDP_INVALID_HANDLE, VDP_INVALID_HANDLE, VDP_INVALID_HANDLE };
+  VdpVideoSurface futu_surfaces[2] = { VDP_INVALID_HANDLE, VDP_INVALID_HANDLE };
+  uint32_t pastCount = 4;
+  uint32_t futuCount = 2;
+
+  if(m_mixerfield == VDP_VIDEO_MIXER_PICTURE_STRUCTURE_FRAME)
+  {
+    // use only 2 past 1 future for progressive/weave
+    // (only used for postproc anyway eg noise reduction)
+    if (m_mixerInput.size() > 3)
+      past_surfaces[1] = m_mixerInput[3].videoSurface;
+    if (m_mixerInput.size() > 2)
+      past_surfaces[0] = m_mixerInput[2].videoSurface;
+    futu_surfaces[0] = m_mixerInput[0].videoSurface;
+    pastCount = 2;
+    futuCount = 1;
+  }
+  else
+  {
+    if(m_mixerstep == 0)
+    { // first field
+      if (m_mixerInput.size() > 3)
+      {
+        past_surfaces[3] = m_mixerInput[3].videoSurface;
+        past_surfaces[2] = m_mixerInput[3].videoSurface;
+      }
+      if (m_mixerInput.size() > 2)
+      {
+        past_surfaces[1] = m_mixerInput[2].videoSurface;
+        past_surfaces[0] = m_mixerInput[2].videoSurface;
+      }
+      futu_surfaces[0] = m_mixerInput[1].videoSurface;
+      futu_surfaces[1] = m_mixerInput[0].videoSurface;
+    }
+    else
+    { // second field
+      if (m_mixerInput.size() > 3)
+      {
+        past_surfaces[3] = m_mixerInput[3].videoSurface;
+      }
+      if (m_mixerInput.size() > 2)
+      {
+        past_surfaces[2] = m_mixerInput[2].videoSurface;
+        past_surfaces[1] = m_mixerInput[2].videoSurface;
+      }
+      past_surfaces[0] = m_mixerInput[1].videoSurface;
+      futu_surfaces[0] = m_mixerInput[0].videoSurface;
+      futu_surfaces[1] = m_mixerInput[0].videoSurface;
+
+      if (m_mixerInput[0].DVDPic.pts != DVD_NOPTS_VALUE &&
+          m_mixerInput[1].DVDPic.pts != DVD_NOPTS_VALUE)
+      {
+        m_processPicture.DVDPic.pts = m_mixerInput[1].DVDPic.pts +
+                                     (m_mixerInput[0].DVDPic.pts -
+                                      m_mixerInput[1].DVDPic.pts) / 2;
+      }
+      else
+        m_processPicture.DVDPic.pts = DVD_NOPTS_VALUE;
+      m_processPicture.DVDPic.dts = DVD_NOPTS_VALUE;
+    }
+    m_processPicture.DVDPic.iRepeatPicture = 0.0;
+  } // interlaced
+
+  VdpRect sourceRect;
+  sourceRect.x0 = 0;
+  sourceRect.y0 = 0;
+  sourceRect.x1 = m_config.vidWidth;
+  sourceRect.y1 = m_config.vidHeight;
+
+  VdpRect destRect;
+  destRect.x0 = 0;
+  destRect.y0 = 0;
+  destRect.x1 = m_config.outWidth;
+  destRect.y1 = m_config.outHeight;
+
+  // start vdpau video mixer
+  vdp_st = m_config.context->GetProcs().vdp_video_mixer_render(m_videoMixer,
+                                VDP_INVALID_HANDLE,
+                                0,
+                                m_mixerfield,
+                                pastCount,
+                                past_surfaces,
+                                m_mixerInput[1].videoSurface,
+                                futuCount,
+                                futu_surfaces,
+                                &sourceRect,
+                                m_processPicture.outputSurface,
+                                &destRect,
+                                &destRect,
+                                0,
+                                NULL);
+  CheckStatus(vdp_st, __LINE__);
+}
+
+
+bool CMixer::CheckStatus(VdpStatus vdp_st, int line)
+{
+  if (vdp_st != VDP_STATUS_OK)
+  {
+    CLog::Log(LOGERROR, " (VDPAU) Error: %s(%d) at %s:%d\n", m_config.context->GetProcs().vdp_get_error_string(vdp_st), vdp_st, __FILE__, line);
+    m_vdpError = true;
+    return true;
+  }
+  return false;
+}
+
+//-----------------------------------------------------------------------------
+// Buffer Pool
+//-----------------------------------------------------------------------------
+
+VdpauBufferPool::VdpauBufferPool()
+{
+  CVdpauRenderPicture *pic;
+  for (unsigned int i = 0; i < NUM_RENDER_PICS; i++)
+  {
+    pic = new CVdpauRenderPicture(renderPicSec);
+    allRenderPics.push_back(pic);
+  }
+}
+
+VdpauBufferPool::~VdpauBufferPool()
+{
+  CVdpauRenderPicture *pic;
+  for (unsigned int i = 0; i < NUM_RENDER_PICS; i++)
+  {
+    pic = allRenderPics[i];
+    delete pic;
+  }
+  allRenderPics.clear();
+}
+
+//-----------------------------------------------------------------------------
+// Output
+//-----------------------------------------------------------------------------
+COutput::COutput(CEvent *inMsgEvent) :
+  CThread("Vdpau Output"),
+  m_controlPort("OutputControlPort", inMsgEvent, &m_outMsgEvent),
+  m_dataPort("OutputDataPort", inMsgEvent, &m_outMsgEvent),
+  m_mixer(&m_outMsgEvent)
+{
+  m_inMsgEvent = inMsgEvent;
+
+  for (unsigned int i = 0; i < m_bufferPool.allRenderPics.size(); ++i)
+  {
+    m_bufferPool.freeRenderPics.push_back(i);
+  }
+}
+
+void COutput::Start()
+{
+  Create();
+}
+
+COutput::~COutput()
+{
+  Dispose();
+
+  m_bufferPool.freeRenderPics.clear();
+  m_bufferPool.usedRenderPics.clear();
+}
+
+void COutput::Dispose()
+{
+  CSingleLock lock(g_graphicsContext);
+  m_bStop = true;
+  m_outMsgEvent.Set();
+  StopThread();
+  m_controlPort.Purge();
+  m_dataPort.Purge();
+}
+
+void COutput::OnStartup()
+{
+  CLog::Log(LOGNOTICE, "COutput::OnStartup: Output Thread created");
+}
+
+void COutput::OnExit()
+{
+  CLog::Log(LOGNOTICE, "COutput::OnExit: Output Thread terminated");
+}
+
+enum OUTPUT_STATES
+{
+  O_TOP = 0,                      // 0
+  O_TOP_ERROR,                    // 1
+  O_TOP_UNCONFIGURED,             // 2
+  O_TOP_CONFIGURED,               // 3
+  O_TOP_CONFIGURED_IDLE,          // 4
+  O_TOP_CONFIGURED_WORK,          // 5
+};
+
+int VDPAU_OUTPUT_parentStates[] = {
+    -1,
+    0, //TOP_ERROR
+    0, //TOP_UNCONFIGURED
+    0, //TOP_CONFIGURED
+    3, //TOP_CONFIGURED_IDLE
+    3, //TOP_CONFIGURED_WORK
+};
+
+void COutput::StateMachine(int signal, Protocol *port, Message *msg)
+{
+  for (int state = m_state; ; state = VDPAU_OUTPUT_parentStates[state])
+  {
+    switch (state)
+    {
+    case O_TOP: // TOP
+      if (port == &m_controlPort)
+      {
+        switch (signal)
+        {
+        case COutputControlProtocol::FLUSH:
+          msg->Reply(COutputControlProtocol::ACC);
+          return;
+        case COutputControlProtocol::PRECLEANUP:
+          msg->Reply(COutputControlProtocol::ACC);
+          return;
+        default:
+          break;
+        }
+      }
+      else if (port == &m_dataPort)
+      {
+        switch (signal)
+        {
+        case COutputDataProtocol::RETURNPIC:
+          CVdpauRenderPicture *pic;
+          pic = *((CVdpauRenderPicture**)msg->data);
+          QueueReturnPicture(pic);
+          return;
+        default:
+          break;
+        }
+      }
+      {
+        std::string portName = port == NULL ? "timer" : port->portName;
+        CLog::Log(LOGWARNING, "COutput::%s - signal: %d form port: %s not handled for state: %d", __FUNCTION__, signal, portName.c_str(), m_state);
+      }
+      return;
+
+    case O_TOP_ERROR:
+      break;
+
+    case O_TOP_UNCONFIGURED:
+      if (port == &m_controlPort)
+      {
+        switch (signal)
+        {
+        case COutputControlProtocol::INIT:
+          CVdpauConfig *data;
+          data = (CVdpauConfig*)msg->data;
+          if (data)
+          {
+            m_config = *data;
+          }
+          Init();
+          Message *reply;
+          if (m_mixer.m_controlPort.SendOutMessageSync(CMixerControlProtocol::INIT,
+                                     &reply, 1000, &m_config, sizeof(m_config)))
+          {
+            if (reply->signal != CMixerControlProtocol::ACC)
+              m_vdpError = true;
+            reply->Release();
+          }
+
+          // set initial number of
+          m_bufferPool.numOutputSurfaces = 4;
+          EnsureBufferPool();
+          if (!m_vdpError)
+          {
+            m_state = O_TOP_CONFIGURED_IDLE;
+            msg->Reply(COutputControlProtocol::ACC, &m_config, sizeof(m_config));
+          }
+          else
+          {
+            m_state = O_TOP_ERROR;
+            msg->Reply(COutputControlProtocol::ERROR);
+          }
+          return;
+        default:
+          break;
+        }
+      }
+      break;
+
+    case O_TOP_CONFIGURED:
+      if (port == &m_controlPort)
+      {
+        switch (signal)
+        {
+        case COutputControlProtocol::FLUSH:
+          Flush();
+          msg->Reply(COutputControlProtocol::ACC);
+          return;
+        case COutputControlProtocol::PRECLEANUP:
+          Flush();
+          PreCleanup();
+          msg->Reply(COutputControlProtocol::ACC);
+          return;
+        default:
+          break;
+        }
+      }
+      else if (port == &m_dataPort)
+      {
+        switch (signal)
+        {
+        case COutputDataProtocol::NEWFRAME:
+          CVdpauDecodedPicture *frame;
+          frame = (CVdpauDecodedPicture*)msg->data;
+          if (frame)
+          {
+            m_mixer.m_dataPort.SendOutMessage(CMixerDataProtocol::FRAME,
+                                               frame,sizeof(CVdpauDecodedPicture));
+          }
+          return;
+        case COutputDataProtocol::RETURNPIC:
+          CVdpauRenderPicture *pic;
+          pic = *((CVdpauRenderPicture**)msg->data);
+          QueueReturnPicture(pic);
+          m_controlPort.SendInMessage(COutputControlProtocol::STATS);
+          m_state = O_TOP_CONFIGURED_WORK;
+          m_extTimeout = 0;
+          return;
+        default:
+          break;
+        }
+      }
+      else if (port == &m_mixer.m_dataPort)
+      {
+        switch (signal)
+        {
+        case CMixerDataProtocol::PICTURE:
+          CVdpauProcessedPicture *pic;
+          pic = (CVdpauProcessedPicture*)msg->data;
+          m_bufferPool.processedPics.push(*pic);
+          m_state = O_TOP_CONFIGURED_WORK;
+          m_extTimeout = 0;
+          return;
+        default:
+          break;
+        }
+      }
+      break;
+
+    case O_TOP_CONFIGURED_IDLE:
+      if (port == NULL) // timeout
+      {
+        switch (signal)
+        {
+        case COutputControlProtocol::TIMEOUT:
+          if (ProcessSyncPicture())
+            m_extTimeout = 10;
+          else
+            m_extTimeout = 100;
+          if (HasWork())
+          {
+            m_state = O_TOP_CONFIGURED_WORK;
+            m_extTimeout = 0;
+          }
+          return;
+        default:
+          break;
+        }
+      }
+      break;
+
+    case O_TOP_CONFIGURED_WORK:
+      if (port == NULL) // timeout
+      {
+        switch (signal)
+        {
+        case COutputControlProtocol::TIMEOUT:
+          if (HasWork())
+          {
+            CVdpauRenderPicture *pic;
+            pic = ProcessMixerPicture();
+            if (pic)
+            {
+              m_config.stats->DecProcessed();
+              m_config.stats->IncRender();
+              m_dataPort.SendInMessage(COutputDataProtocol::PICTURE, &pic, sizeof(pic));
+            }
+            m_extTimeout = 1;
+          }
+          else
+          {
+            m_state = O_TOP_CONFIGURED_IDLE;
+            m_extTimeout = 0;
+          }
+          return;
+        default:
+          break;
+        }
+      }
+      break;
+
+    default: // we are in no state, should not happen
+      CLog::Log(LOGERROR, "COutput::%s - no valid state: %d", __FUNCTION__, m_state);
+      return;
+    }
+  } // for
+}
+
+void COutput::Process()
+{
+  Message *msg = NULL;
+  Protocol *port = NULL;
+  bool gotMsg;
+
+  m_state = O_TOP_UNCONFIGURED;
+  m_extTimeout = 1000;
+  m_bStateMachineSelfTrigger = false;
+
+  while (!m_bStop)
+  {
+    gotMsg = false;
+
+    if (m_bStateMachineSelfTrigger)
+    {
+      m_bStateMachineSelfTrigger = false;
+      // self trigger state machine
+      StateMachine(msg->signal, port, msg);
+      if (!m_bStateMachineSelfTrigger)
+      {
+        msg->Release();
+        msg = NULL;
+      }
+      continue;
+    }
+    // check control port
+    else if (m_controlPort.ReceiveOutMessage(&msg))
+    {
+      gotMsg = true;
+      port = &m_controlPort;
+    }
+    // check data port
+    else if (m_dataPort.ReceiveOutMessage(&msg))
+    {
+      gotMsg = true;
+      port = &m_dataPort;
+    }
+    // check mixer data port
+    else if (m_mixer.m_dataPort.ReceiveInMessage(&msg))
+    {
+      gotMsg = true;
+      port = &m_mixer.m_dataPort;
+    }
+    if (gotMsg)
+    {
+      StateMachine(msg->signal, port, msg);
+      if (!m_bStateMachineSelfTrigger)
+      {
+        msg->Release();
+        msg = NULL;
+      }
+      continue;
+    }
+
+    // wait for message
+    else if (m_outMsgEvent.WaitMSec(m_extTimeout))
+    {
+      continue;
+    }
+    // time out
+    else
+    {
+      msg = m_controlPort.GetMessage();
+      msg->signal = COutputControlProtocol::TIMEOUT;
+      port = 0;
+      // signal timeout to state machine
+      StateMachine(msg->signal, port, msg);
+      if (!m_bStateMachineSelfTrigger)
+      {
+        msg->Release();
+        msg = NULL;
+      }
+    }
+  }
+  Flush();
+  Uninit();
+}
+
+bool COutput::Init()
+{
+  if (!CreateGlxContext())
+    return false;
+
+  if (!GLInit())
+    return false;
+
+  m_mixer.Start();
+  m_vdpError = false;
+
+  return true;
+}
+
+bool COutput::Uninit()
+{
+  m_mixer.Dispose();
+  glFlush();
+  while(ProcessSyncPicture())
+  {
+    Sleep(10);
+  }
+  GLUnmapSurfaces();
+  ReleaseBufferPool();
+  DestroyGlxContext();
+  return true;
+}
+
+void COutput::Flush()
+{
+  if (m_mixer.IsActive())
+  {
+    Message *reply;
+    if (m_mixer.m_controlPort.SendOutMessageSync(CMixerControlProtocol::FLUSH,
+                                                 &reply,
+                                                 2000))
+    {
+      reply->Release();
+    }
+    else
+      CLog::Log(LOGERROR, "Coutput::%s - failed to flush mixer", __FUNCTION__);
+  }
+
+  Message *msg;
+  while (m_mixer.m_dataPort.ReceiveInMessage(&msg))
+  {
+    if (msg->signal == CMixerDataProtocol::PICTURE)
+    {
+      CVdpauProcessedPicture pic = *(CVdpauProcessedPicture*)msg->data;
+      m_bufferPool.processedPics.push(pic);
+    }
+    msg->Release();
+  }
+
+  while (m_dataPort.ReceiveOutMessage(&msg))
+  {
+    if (msg->signal == COutputDataProtocol::NEWFRAME)
+    {
+      CVdpauDecodedPicture pic = *(CVdpauDecodedPicture*)msg->data;
+      m_config.videoSurfaces->ClearRender(pic.videoSurface);
+    }
+    else if (msg->signal == COutputDataProtocol::RETURNPIC)
+    {
+      CVdpauRenderPicture *pic;
+      pic = *((CVdpauRenderPicture**)msg->data);
+      QueueReturnPicture(pic);
+    }
+    msg->Release();
+  }
+
+  while (m_dataPort.ReceiveInMessage(&msg))
+  {
+    if (msg->signal == COutputDataProtocol::PICTURE)
+    {
+      CVdpauRenderPicture *pic;
+      pic = *((CVdpauRenderPicture**)msg->data);
+      QueueReturnPicture(pic);
+    }
+  }
+
+  // reset used render flag which was cleared on mixer flush
+  std::deque<int>::iterator it;
+  for (it = m_bufferPool.usedRenderPics.begin(); it != m_bufferPool.usedRenderPics.end(); ++it)
+  {
+    CVdpauRenderPicture *pic = m_bufferPool.allRenderPics[*it];
+    if (pic->DVDPic.format == RENDER_FMT_VDPAU_420)
+    {
+      std::map<VdpVideoSurface, VdpauBufferPool::GLVideoSurface>::iterator it2;
+      it2 = m_bufferPool.glVideoSurfaceMap.find(pic->sourceIdx);
+      if (it2 == m_bufferPool.glVideoSurfaceMap.end())
+      {
+        CLog::Log(LOGDEBUG, "COutput::Flush - gl surface not found");
+        continue;
+      }
+      m_config.videoSurfaces->MarkRender(it2->second.sourceVuv);
+    }
+  }
+
+  // clear processed pics
+  while(!m_bufferPool.processedPics.empty())
+  {
+    CVdpauProcessedPicture procPic = m_bufferPool.processedPics.front();
+    if (procPic.DVDPic.format == RENDER_FMT_VDPAU)
+    {
+      m_mixer.m_dataPort.SendOutMessage(CMixerDataProtocol::BUFFER, &procPic.outputSurface, sizeof(procPic.outputSurface));
+    }
+    else if (procPic.DVDPic.format == RENDER_FMT_VDPAU_420)
+    {
+      m_config.videoSurfaces->ClearRender(procPic.videoSurface);
+    }
+    m_bufferPool.processedPics.pop();
+  }
+}
+
+bool COutput::HasWork()
+{
+  if (!m_bufferPool.processedPics.empty() && !m_bufferPool.freeRenderPics.empty())
+    return true;
+  return false;
+}
+
+CVdpauRenderPicture* COutput::ProcessMixerPicture()
+{
+  CVdpauRenderPicture *retPic = NULL;
+
+  if (!m_bufferPool.processedPics.empty() && !m_bufferPool.freeRenderPics.empty())
+  {
+    int idx = m_bufferPool.freeRenderPics.front();
+    retPic = m_bufferPool.allRenderPics[idx];
+    m_bufferPool.freeRenderPics.pop_front();
+    m_bufferPool.usedRenderPics.push_back(idx);
+    CVdpauProcessedPicture procPic = m_bufferPool.processedPics.front();
+    m_bufferPool.processedPics.pop();
+
+    retPic->DVDPic = procPic.DVDPic;
+    retPic->valid = true;
+    if (retPic->DVDPic.format == RENDER_FMT_VDPAU)
+    {
+      m_config.useInteropYuv = false;
+      m_bufferPool.numOutputSurfaces = NUM_RENDER_PICS;
+      EnsureBufferPool();
+      GLMapSurface(false, procPic.outputSurface);
+      retPic->sourceIdx = procPic.outputSurface;
+      retPic->texture[0] = m_bufferPool.glOutputSurfaceMap[procPic.outputSurface].texture[0];
+      retPic->texWidth = m_config.outWidth;
+      retPic->texHeight = m_config.outHeight;
+      retPic->crop.x1 = 0;
+      retPic->crop.y1 = procPic.crop ? NUM_CROP_PIX : 0;
+      retPic->crop.x2 = m_config.outWidth;
+      retPic->crop.y2 = m_config.outHeight - retPic->crop.y1;
+    }
+    else
+    {
+      m_config.useInteropYuv = true;
+      GLMapSurface(true, procPic.videoSurface);
+      retPic->sourceIdx = procPic.videoSurface;
+      for (unsigned int i=0; i<4; ++i)
+        retPic->texture[i] = m_bufferPool.glVideoSurfaceMap[procPic.videoSurface].texture[i];
+      retPic->texWidth = m_config.surfaceWidth;
+      retPic->texHeight = m_config.surfaceHeight;
+      retPic->crop.x1 = 0;
+      retPic->crop.y1 = 0;
+      retPic->crop.x2 = m_config.surfaceWidth - m_config.vidWidth;
+      retPic->crop.y2 = m_config.surfaceHeight - m_config.vidHeight;
+    }
+  }
+  return retPic;
+}
+
+void COutput::QueueReturnPicture(CVdpauRenderPicture *pic)
+{
+  std::deque<int>::iterator it;
+  for (it = m_bufferPool.usedRenderPics.begin(); it != m_bufferPool.usedRenderPics.end(); ++it)
+  {
+    if (m_bufferPool.allRenderPics[*it] == pic)
+    {
+      break;
+    }
+  }
+
+  if (it == m_bufferPool.usedRenderPics.end())
+  {
+    CLog::Log(LOGWARNING, "COutput::QueueReturnPicture - pic not found");
+    return;
+  }
+
+  // check if already queued
+  std::deque<int>::iterator it2 = find(m_bufferPool.syncRenderPics.begin(),
+                                       m_bufferPool.syncRenderPics.end(),
+                                       *it);
+  if (it2 == m_bufferPool.syncRenderPics.end())
+  {
+    m_bufferPool.syncRenderPics.push_back(*it);
+  }
+
+  ProcessSyncPicture();
+}
+
+bool COutput::ProcessSyncPicture()
+{
+  CVdpauRenderPicture *pic;
+  bool busy = false;
+
+  std::deque<int>::iterator it;
+  for (it = m_bufferPool.syncRenderPics.begin(); it != m_bufferPool.syncRenderPics.end(); )
+  {
+    pic = m_bufferPool.allRenderPics[*it];
+
+#ifdef GL_ARB_sync
+    if (pic->usefence)
+    {
+      if (glIsSync(pic->fence))
+      {
+        GLint state;
+        GLsizei length;
+        glGetSynciv(pic->fence, GL_SYNC_STATUS, 1, &length, &state);
+        if(state == GL_SIGNALED)
+        {
+          glDeleteSync(pic->fence);
+          pic->fence = None;
+        }
+        else
+        {
+          busy = true;
+          ++it;
+          continue;
+        }
+      }
+    }
+#endif
+
+    m_bufferPool.freeRenderPics.push_back(*it);
+
+    std::deque<int>::iterator it2 = find(m_bufferPool.usedRenderPics.begin(),
+                                         m_bufferPool.usedRenderPics.end(),
+                                         *it);
+    if (it2 == m_bufferPool.usedRenderPics.end())
+    {
+      CLog::Log(LOGERROR, "COutput::ProcessSyncPicture - pic not found in queue");
+    }
+    else
     {
-      CLog::Log(LOGWARNING, "CVDPAU::FFGetBuffer - returning due to awaiting recovery");
-      return -1;
+      m_bufferPool.usedRenderPics.erase(it2);
     }
-  }
+    it = m_bufferPool.syncRenderPics.erase(it);
 
-  vdpau_render_state * render = NULL;
-
-  // find unused surface
-  for(unsigned int i = 0; i < vdp->m_videoSurfaces.size(); i++)
-  {
-    if(!(vdp->m_videoSurfaces[i]->state & (FF_VDPAU_STATE_USED_FOR_REFERENCE | FF_VDPAU_STATE_USED_FOR_RENDER)))
+    if (pic->valid)
     {
-      render = vdp->m_videoSurfaces[i];
-      render->state = 0;
-      break;
+      ProcessReturnPicture(pic);
+    }
+    else
+    {
+      CLog::Log(LOGDEBUG, "COutput::%s - return of invalid render pic", __FUNCTION__);
     }
   }
+  return busy;
+}
 
-  VdpStatus vdp_st = VDP_STATUS_ERROR;
-  if (render == NULL)
+void COutput::ProcessReturnPicture(CVdpauRenderPicture *pic)
+{
+  if (pic->DVDPic.format == RENDER_FMT_VDPAU_420)
   {
-    // create a new surface
-    VdpDecoderProfile profile;
-    ReadFormatOf(avctx->codec_id, profile, vdp->vdp_chroma_type);
-    render = (vdpau_render_state*)calloc(sizeof(vdpau_render_state), 1);
-    if (render == NULL)
+    std::map<VdpVideoSurface, VdpauBufferPool::GLVideoSurface>::iterator it;
+    it = m_bufferPool.glVideoSurfaceMap.find(pic->sourceIdx);
+    if (it == m_bufferPool.glVideoSurfaceMap.end())
     {
-      CLog::Log(LOGWARNING, "CVDPAU::FFGetBuffer - calloc failed");
-      return -1;
+      CLog::Log(LOGDEBUG, "COutput::ProcessReturnPicture - gl surface not found");
+      return;
     }
-    render->surface = VDP_INVALID_HANDLE;
-    vdp->m_videoSurfaces.push_back(render);
+#ifdef GL_NV_vdpau_interop
+    glVDPAUUnmapSurfacesNV(1, &(it->second.glVdpauSurface));
+#endif
+    VdpVideoSurface surf = it->second.sourceVuv;
+    m_config.videoSurfaces->ClearRender(surf);
   }
-
-  if (render->surface == VDP_INVALID_HANDLE)
+  else if (pic->DVDPic.format == RENDER_FMT_VDPAU)
   {
-    vdp_st = vdp->vdp_video_surface_create(vdp->vdp_device,
-                                         vdp->vdp_chroma_type,
-                                         avctx->coded_width,
-                                         avctx->coded_height,
-                                         &render->surface);
-    vdp->CheckStatus(vdp_st, __LINE__);
-    if (vdp_st != VDP_STATUS_OK)
+    std::map<VdpOutputSurface, VdpauBufferPool::GLVideoSurface>::iterator it;
+    it = m_bufferPool.glOutputSurfaceMap.find(pic->sourceIdx);
+    if (it == m_bufferPool.glOutputSurfaceMap.end())
     {
-      free(render);
-      CLog::Log(LOGERROR, "CVDPAU::FFGetBuffer - No Video surface available could be created");
-      return -1;
+      CLog::Log(LOGDEBUG, "COutput::ProcessReturnPicture - gl surface not found");
+      return;
     }
+#ifdef GL_NV_vdpau_interop
+    glVDPAUUnmapSurfacesNV(1, &(it->second.glVdpauSurface));
+#endif
+    VdpOutputSurface outSurf = it->second.sourceRgb;
+    m_mixer.m_dataPort.SendOutMessage(CMixerDataProtocol::BUFFER, &outSurf, sizeof(outSurf));
   }
+}
 
-  pic->data[1] = pic->data[2] = NULL;
-  pic->data[0] = (uint8_t*)render;
-  pic->data[3] = (uint8_t*)(uintptr_t)render->surface;
+bool COutput::EnsureBufferPool()
+{
+  VdpStatus vdp_st;
 
-  pic->linesize[0] = pic->linesize[1] =  pic->linesize[2] = 0;
+  // Creation of outputSurfaces
+  VdpOutputSurface outputSurface;
+  for (int i = m_bufferPool.outputSurfaces.size(); i < m_bufferPool.numOutputSurfaces; i++)
+  {
+    vdp_st = m_config.context->GetProcs().vdp_output_surface_create(m_config.context->GetDevice(),
+                                      VDP_RGBA_FORMAT_B8G8R8A8,
+                                      m_config.outWidth,
+                                      m_config.outHeight,
+                                      &outputSurface);
+    if (CheckStatus(vdp_st, __LINE__))
+      return false;
+    m_bufferPool.outputSurfaces.push_back(outputSurface);
 
-  if(pic->reference)
-  {
-    pA->ip_age[0]= pA->ip_age[1]+1;
-    pA->ip_age[1]= 1;
-    pA->b_age++;
+    m_mixer.m_dataPort.SendOutMessage(CMixerDataProtocol::BUFFER,
+                                      &outputSurface,
+                                      sizeof(VdpOutputSurface));
+    CLog::Log(LOGNOTICE, "VDPAU::COutput::InitBufferPool - Output Surface created");
   }
-  else
-  {
-    pA->ip_age[0]++;
-    pA->ip_age[1]++;
-    pA->b_age = 1;
-  }
-  pic->type= FF_BUFFER_TYPE_USER;
-
-  render->state |= FF_VDPAU_STATE_USED_FOR_REFERENCE;
-  pic->reordered_opaque= avctx->reordered_opaque;
-  return 0;
+  return true;
 }
 
-void CVDPAU::FFReleaseBuffer(AVCodecContext *avctx, AVFrame *pic)
+void COutput::ReleaseBufferPool()
 {
-  //CLog::Log(LOGNOTICE,"%s",__FUNCTION__);
-  CDVDVideoCodecFFmpeg* ctx        = (CDVDVideoCodecFFmpeg*)avctx->opaque;
-  CVDPAU*               vdp        = (CVDPAU*)ctx->GetHardware();
-  vdpau_render_state  * render;
-  unsigned int i;
+  VdpStatus vdp_st;
 
-  CSharedLock lock(vdp->m_DecoderSection);
+  CSingleLock lock(m_bufferPool.renderPicSec);
 
-  render=(vdpau_render_state*)pic->data[0];
-  if(!render)
+  // release all output surfaces
+  for (unsigned int i = 0; i < m_bufferPool.outputSurfaces.size(); ++i)
   {
-    CLog::Log(LOGERROR, "CVDPAU::FFReleaseBuffer - invalid context handle provided");
-    return;
+    if (m_bufferPool.outputSurfaces[i] == VDP_INVALID_HANDLE)
+      continue;
+    vdp_st = m_config.context->GetProcs().vdp_output_surface_destroy(m_bufferPool.outputSurfaces[i]);
+    CheckStatus(vdp_st, __LINE__);
   }
+  m_bufferPool.outputSurfaces.clear();
 
-  for(i=0; i<4; i++)
-    pic->data[i]= NULL;
-
-  // find render state in queue
-  if (!vdp->IsSurfaceValid(render))
+  // wait for all fences
+  XbmcThreads::EndTime timeout(1000);
+  for (unsigned int i = 0; i < m_bufferPool.allRenderPics.size(); i++)
   {
-    CLog::Log(LOGDEBUG, "CVDPAU::FFReleaseBuffer - ignoring invalid buffer");
-    return;
+    CVdpauRenderPicture *pic = m_bufferPool.allRenderPics[i];
+    if (pic->usefence)
+    {
+#ifdef GL_ARB_sync
+      while (glIsSync(pic->fence))
+      {
+        GLint state;
+        GLsizei length;
+        glGetSynciv(pic->fence, GL_SYNC_STATUS, 1, &length, &state);
+        if(state == GL_SIGNALED || timeout.IsTimePast())
+        {
+          glDeleteSync(pic->fence);
+        }
+        else
+        {
+          Sleep(5);
+        }
+      }
+      pic->fence = None;
+#endif
+    }
   }
+  if (timeout.IsTimePast())
+  {
+    CLog::Log(LOGERROR, "COutput::%s - timeout waiting for fence", __FUNCTION__);
+  }
+  ProcessSyncPicture();
 
-  render->state &= ~FF_VDPAU_STATE_USED_FOR_REFERENCE;
-}
-
-VdpStatus CVDPAU::Render(VdpDecoder decoder, VdpVideoSurface target,
-                         VdpPictureInfo const *picture_info,
-                         uint32_t bitstream_buffer_count,
-                         VdpBitstreamBuffer const * bitstream_buffers)
-{
-  return VDP_STATUS_OK;
+  // invalidate all used render pictures
+  for (unsigned int i = 0; i < m_bufferPool.usedRenderPics.size(); ++i)
+  {
+    CVdpauRenderPicture *pic = m_bufferPool.allRenderPics[m_bufferPool.usedRenderPics[i]];
+    pic->valid = false;
+  }
 }
 
-void CVDPAU::FFDrawSlice(struct AVCodecContext *s,
-                                           const AVFrame *src, int offset[4],
-                                           int y, int type, int height)
+void COutput::PreCleanup()
 {
-  CDVDVideoCodecFFmpeg* ctx = (CDVDVideoCodecFFmpeg*)s->opaque;
-  CVDPAU*               vdp = (CVDPAU*)ctx->GetHardware();
-
-  // while we are waiting to recover we can't do anything
-  CSharedLock lock(vdp->m_DecoderSection);
 
-  { CSharedLock dLock(vdp->m_DisplaySection);
-    if(vdp->m_DisplayState != VDPAU_OPEN)
-      return;
-  }
+  VdpStatus vdp_st;
 
+  m_mixer.Dispose();
+  ProcessSyncPicture();
 
-  if(src->linesize[0] || src->linesize[1] || src->linesize[2]
-  || offset[0] || offset[1] || offset[2])
+  CSingleLock lock(m_bufferPool.renderPicSec);
+  for (unsigned int i = 0; i < m_bufferPool.outputSurfaces.size(); ++i)
   {
-    CLog::Log(LOGERROR, "CVDPAU::FFDrawSlice - invalid linesizes or offsets provided");
-    return;
-  }
+    if (m_bufferPool.outputSurfaces[i] == VDP_INVALID_HANDLE)
+      continue;
 
-  VdpStatus vdp_st;
-  vdpau_render_state * render;
+    // check if output surface is in use
+    bool used = false;
+    std::deque<int>::iterator it;
+    CVdpauRenderPicture *pic;
+    for (it = m_bufferPool.usedRenderPics.begin(); it != m_bufferPool.usedRenderPics.end(); ++it)
+    {
+      pic = m_bufferPool.allRenderPics[*it];
+      if ((pic->sourceIdx == m_bufferPool.outputSurfaces[i]) && pic->valid)
+      {
+        used = true;
+        break;
+      }
+    }
+    if (used)
+      continue;
+
+#ifdef GL_NV_vdpau_interop
+    // unmap surface
+    std::map<VdpOutputSurface, VdpauBufferPool::GLVideoSurface>::iterator it_map;
+    it_map = m_bufferPool.glOutputSurfaceMap.find(m_bufferPool.outputSurfaces[i]);
+    if (it_map == m_bufferPool.glOutputSurfaceMap.end())
+    {
+      CLog::Log(LOGERROR, "%s - could not find gl surface", __FUNCTION__);
+      continue;
+    }
+    glVDPAUUnregisterSurfaceNV(it_map->second.glVdpauSurface);
+    glDeleteTextures(1, it_map->second.texture);
+    m_bufferPool.glOutputSurfaceMap.erase(it_map);
+#endif
 
-  render = (vdpau_render_state*)src->data[0];
-  if(!render)
-  {
-    CLog::Log(LOGERROR, "CVDPAU::FFDrawSlice - invalid context handle provided");
-    return;
-  }
+    vdp_st = m_config.context->GetProcs().vdp_output_surface_destroy(m_bufferPool.outputSurfaces[i]);
+    CheckStatus(vdp_st, __LINE__);
 
-  // ffmpeg vc-1 decoder does not flush, make sure the data buffer is still valid
-  if (!vdp->IsSurfaceValid(render))
-  {
-    CLog::Log(LOGWARNING, "CVDPAU::FFDrawSlice - ignoring invalid buffer");
-    return;
+    m_bufferPool.outputSurfaces[i] = VDP_INVALID_HANDLE;
+
+    CLog::Log(LOGDEBUG, "VDPAU::PreCleanup - released output surface");
   }
 
-  uint32_t max_refs = 0;
-  if(s->codec_id == AV_CODEC_ID_H264)
-    max_refs = vdp->m_hwContext.info.h264.num_ref_frames;
+}
 
-  if(vdp->decoder == VDP_INVALID_HANDLE
-  || vdp->vdpauConfigured == false
-  || vdp->max_references < max_refs)
+void COutput::InitMixer()
+{
+  for (unsigned int i = 0; i < m_bufferPool.outputSurfaces.size(); ++i)
   {
-    if(!vdp->ConfigVDPAU(s, max_refs))
-      return;
+    m_mixer.m_dataPort.SendOutMessage(CMixerDataProtocol::BUFFER,
+                                      &m_bufferPool.outputSurfaces[i],
+                                      sizeof(VdpOutputSurface));
   }
-
-  vdp_st = vdp->vdp_decoder_render(vdp->decoder,
-                                   render->surface,
-                                   (VdpPictureInfo const *)&(vdp->m_hwContext.info),
-                                   vdp->m_hwContext.bitstream_buffers_used,
-                                   vdp->m_hwContext.bitstream_buffers);
-  vdp->CheckStatus(vdp_st, __LINE__);
 }
 
-int CVDPAU::Decode(AVCodecContext *avctx, AVFrame *pFrame)
+bool COutput::GLInit()
 {
-  //CLog::Log(LOGNOTICE,"%s",__FUNCTION__);
-  VdpStatus vdp_st;
-  VdpTime time;
-
-  int result = Check(avctx);
-  if (result)
-    return result;
-
-  CSharedLock lock(m_DecoderSection);
-
-  if (!vdpauConfigured)
-    return VC_ERROR;
-
-  // configure vdpau output
-  if (!ConfigOutputMethod(avctx, pFrame))
-    return VC_FLUSHED;
-
-  outputSurface = outputSurfaces[surfaceNum];
+#ifdef GL_NV_vdpau_interop
+  glVDPAUInitNV = NULL;
+  glVDPAUFiniNV = NULL;
+  glVDPAURegisterOutputSurfaceNV = NULL;
+  glVDPAURegisterVideoSurfaceNV = NULL;
+  glVDPAUIsSurfaceNV = NULL;
+  glVDPAUUnregisterSurfaceNV = NULL;
+  glVDPAUSurfaceAccessNV = NULL;
+  glVDPAUMapSurfacesNV = NULL;
+  glVDPAUUnmapSurfacesNV = NULL;
+  glVDPAUGetSurfaceivNV = NULL;
+#endif
 
-  CheckFeatures();
+#ifdef GL_NV_vdpau_interop
+    if (!glVDPAUInitNV)
+      glVDPAUInitNV    = (PFNGLVDPAUINITNVPROC)glXGetProcAddress((GLubyte *) "glVDPAUInitNV");
+    if (!glVDPAUFiniNV)
+      glVDPAUFiniNV = (PFNGLVDPAUFININVPROC)glXGetProcAddress((GLubyte *) "glVDPAUFiniNV");
+    if (!glVDPAURegisterOutputSurfaceNV)
+      glVDPAURegisterOutputSurfaceNV    = (PFNGLVDPAUREGISTEROUTPUTSURFACENVPROC)glXGetProcAddress((GLubyte *) "glVDPAURegisterOutputSurfaceNV");
+    if (!glVDPAURegisterVideoSurfaceNV)
+      glVDPAURegisterVideoSurfaceNV    = (PFNGLVDPAUREGISTERVIDEOSURFACENVPROC)glXGetProcAddress((GLubyte *) "glVDPAURegisterVideoSurfaceNV");
+    if (!glVDPAUIsSurfaceNV)
+      glVDPAUIsSurfaceNV    = (PFNGLVDPAUISSURFACENVPROC)glXGetProcAddress((GLubyte *) "glVDPAUIsSurfaceNV");
+    if (!glVDPAUUnregisterSurfaceNV)
+      glVDPAUUnregisterSurfaceNV = (PFNGLVDPAUUNREGISTERSURFACENVPROC)glXGetProcAddress((GLubyte *) "glVDPAUUnregisterSurfaceNV");
+    if (!glVDPAUSurfaceAccessNV)
+      glVDPAUSurfaceAccessNV    = (PFNGLVDPAUSURFACEACCESSNVPROC)glXGetProcAddress((GLubyte *) "glVDPAUSurfaceAccessNV");
+    if (!glVDPAUMapSurfacesNV)
+      glVDPAUMapSurfacesNV = (PFNGLVDPAUMAPSURFACESNVPROC)glXGetProcAddress((GLubyte *) "glVDPAUMapSurfacesNV");
+    if (!glVDPAUUnmapSurfacesNV)
+      glVDPAUUnmapSurfacesNV = (PFNGLVDPAUUNMAPSURFACESNVPROC)glXGetProcAddress((GLubyte *) "glVDPAUUnmapSurfacesNV");
+    if (!glVDPAUGetSurfaceivNV)
+      glVDPAUGetSurfaceivNV = (PFNGLVDPAUGETSURFACEIVNVPROC)glXGetProcAddress((GLubyte *) "glVDPAUGetSurfaceivNV");
+
+  while (glGetError() != GL_NO_ERROR);
+  glVDPAUInitNV(reinterpret_cast<void*>(m_config.context->GetDevice()), reinterpret_cast<void*>(m_config.context->GetProcs().vdp_get_proc_address));
+  if (glGetError() != GL_NO_ERROR)
+  {
+    CLog::Log(LOGERROR, "VDPAU::COutput - GLInitInterop glVDPAUInitNV failed");
+    m_vdpError = true;
+    return false;
+  }
+  CLog::Log(LOGNOTICE, "VDPAU::COutput: vdpau gl interop initialized");
+#endif
 
-  if (( (int)outRectVid.x1 != OutWidth ) ||
-      ( (int)outRectVid.y1 != OutHeight ))
+#ifdef GL_ARB_sync
+  bool hasfence = glewIsSupported("GL_ARB_sync");
+  for (unsigned int i = 0; i < m_bufferPool.allRenderPics.size(); i++)
   {
-    outRectVid.x0 = 0;
-    outRectVid.y0 = 0;
-    outRectVid.x1 = OutWidth;
-    outRectVid.y1 = OutHeight;
+    m_bufferPool.allRenderPics[i]->usefence = hasfence;
   }
+#endif
 
-  EDEINTERLACEMODE   mode = CMediaSettings::Get().GetCurrentVideoSettings().m_DeinterlaceMode;
-  EINTERLACEMETHOD method = CMediaSettings::Get().GetCurrentVideoSettings().m_InterlaceMethod;
-  if (method == VS_INTERLACEMETHOD_AUTO)
-    method = AutoInterlaceMethod();
-
-  if(pFrame)
-  { // we have a new frame from decoder
+  return true;
+}
 
-    vdpau_render_state * render = (vdpau_render_state*)pFrame->data[2];
-    if(!render) // old style ffmpeg gave data on plane 0
-      render = (vdpau_render_state*)pFrame->data[0];
-    if(!render)
-      return VC_ERROR;
+void COutput::GLMapSurface(bool yuv, uint32_t source)
+{
+#ifdef GL_NV_vdpau_interop
 
-    // ffmpeg vc-1 decoder does not flush, make sure the data buffer is still valid
-    if (!IsSurfaceValid(render))
+  if (yuv)
+  {
+    std::map<VdpVideoSurface, VdpauBufferPool::GLVideoSurface>::iterator it;
+    it = m_bufferPool.glVideoSurfaceMap.find(source);
+    if (it == m_bufferPool.glVideoSurfaceMap.end())
     {
-      CLog::Log(LOGWARNING, "CVDPAU::Decode - ignoring invalid buffer");
-      return VC_BUFFER;
-    }
+      VdpauBufferPool::GLVideoSurface glSurface;
+      VdpVideoSurface surf = source;
+
+      if (surf == VDP_INVALID_HANDLE)
+        return;
 
-    render->state |= FF_VDPAU_STATE_USED_FOR_RENDER;
+      glSurface.sourceVuv = surf;
+      while (glGetError() != GL_NO_ERROR) ;
+      glGenTextures(4, glSurface.texture);
+      if (glGetError() != GL_NO_ERROR)
+      {
+        CLog::Log(LOGERROR, "VDPAU::COutput error creating texture");
+        m_vdpError = true;
+      }
+      glSurface.glVdpauSurface = glVDPAURegisterVideoSurfaceNV(reinterpret_cast<void*>(surf),
+                                                    GL_TEXTURE_2D, 4, glSurface.texture);
 
-    ClearUsedForRender(&past[0]);
-    past[0] = past[1];
-    past[1] = current;
-    current = future;
-    future = render;
+      if (glGetError() != GL_NO_ERROR)
+      {
+        CLog::Log(LOGERROR, "VDPAU::COutput error register video surface");
+        m_vdpError = true;
+      }
+      glVDPAUSurfaceAccessNV(glSurface.glVdpauSurface, GL_READ_ONLY);
+      if (glGetError() != GL_NO_ERROR)
+      {
+        CLog::Log(LOGERROR, "VDPAU::COutput error setting access");
+        m_vdpError = true;
+      }
+      m_bufferPool.glVideoSurfaceMap[surf] = glSurface;
 
-    DVDVideoPicture DVDPic;
-    memset(&DVDPic, 0, sizeof(DVDVideoPicture));
-    ((CDVDVideoCodecFFmpeg*)avctx->opaque)->GetPictureCommon(&DVDPic);
-    m_DVDVideoPics.push(DVDPic);
+      CLog::Log(LOGNOTICE, "VDPAU::COutput registered surface");
+    }
 
-    int pics = m_DVDVideoPics.size();
-    if (pics < 2)
-        return VC_BUFFER;
-    else if (pics > 2)
+    while (glGetError() != GL_NO_ERROR) ;
+    glVDPAUMapSurfacesNV(1, &m_bufferPool.glVideoSurfaceMap[source].glVdpauSurface);
+    if (glGetError() != GL_NO_ERROR)
     {
-      // this should not normally happen
-      CLog::Log(LOGERROR, "CVDPAU::Decode - invalid number of pictures in queue");
-      while (pics-- != 2)
-        m_DVDVideoPics.pop();
+      CLog::Log(LOGERROR, "VDPAU::COutput error mapping surface");
+      m_vdpError = true;
     }
 
-    if (mode == VS_DEINTERLACEMODE_FORCE
-    || (mode == VS_DEINTERLACEMODE_AUTO && m_DVDVideoPics.front().iFlags & DVP_FLAG_INTERLACED))
+    if (m_vdpError)
+      return;
+  }
+  else
+  {
+    std::map<VdpOutputSurface, VdpauBufferPool::GLVideoSurface>::iterator it;
+    it = m_bufferPool.glOutputSurfaceMap.find(source);
+    if (it == m_bufferPool.glOutputSurfaceMap.end())
     {
-      if((method == VS_INTERLACEMETHOD_AUTO_ION
-      ||  method == VS_INTERLACEMETHOD_VDPAU_BOB
-      ||  method == VS_INTERLACEMETHOD_VDPAU_TEMPORAL
-      ||  method == VS_INTERLACEMETHOD_VDPAU_TEMPORAL_HALF
-      ||  method == VS_INTERLACEMETHOD_VDPAU_TEMPORAL_SPATIAL
-      ||  method == VS_INTERLACEMETHOD_VDPAU_TEMPORAL_SPATIAL_HALF
-      ||  method == VS_INTERLACEMETHOD_VDPAU_INVERSE_TELECINE ))
+      unsigned int idx = 0;
+      for (idx = 0; idx<m_bufferPool.outputSurfaces.size(); idx++)
       {
-        if((method == VS_INTERLACEMETHOD_AUTO_ION && vid_height > 576)
-        || method == VS_INTERLACEMETHOD_VDPAU_TEMPORAL_HALF
-        || method == VS_INTERLACEMETHOD_VDPAU_TEMPORAL_SPATIAL_HALF
-        || avctx->skip_frame == AVDISCARD_NONREF)
-          m_mixerstep = 0;
-        else
-          m_mixerstep = 1;
+        if (m_bufferPool.outputSurfaces[idx] == source)
+          break;
+      }
 
-        if(m_DVDVideoPics.front().iFlags & DVP_FLAG_TOP_FIELD_FIRST)
-          m_mixerfield = VDP_VIDEO_MIXER_PICTURE_STRUCTURE_TOP_FIELD;
-        else
-          m_mixerfield = VDP_VIDEO_MIXER_PICTURE_STRUCTURE_BOTTOM_FIELD;
+      VdpauBufferPool::GLVideoSurface glSurface;
+      glSurface.sourceRgb = m_bufferPool.outputSurfaces[idx];
+      glGenTextures(1, glSurface.texture);
+      glSurface.glVdpauSurface = glVDPAURegisterOutputSurfaceNV(reinterpret_cast<void*>(m_bufferPool.outputSurfaces[idx]),
+                                               GL_TEXTURE_2D, 1, glSurface.texture);
+      if (glGetError() != GL_NO_ERROR)
+      {
+        CLog::Log(LOGERROR, "VDPAU::COutput error register output surface");
+        m_vdpError = true;
       }
-      else
+      glVDPAUSurfaceAccessNV(glSurface.glVdpauSurface, GL_READ_ONLY);
+      if (glGetError() != GL_NO_ERROR)
       {
-        m_mixerstep  = 0;
-        m_mixerfield = VDP_VIDEO_MIXER_PICTURE_STRUCTURE_FRAME;
+        CLog::Log(LOGERROR, "VDPAU::COutput error setting access");
+        m_vdpError = true;
       }
+      m_bufferPool.glOutputSurfaceMap[source] = glSurface;
+      CLog::Log(LOGNOTICE, "VDPAU::COutput registered output surfaces");
     }
-    else
+
+    while (glGetError() != GL_NO_ERROR) ;
+    glVDPAUMapSurfacesNV(1, &m_bufferPool.glOutputSurfaceMap[source].glVdpauSurface);
+    if (glGetError() != GL_NO_ERROR)
     {
-      m_mixerstep  = 0;
-      m_mixerfield = VDP_VIDEO_MIXER_PICTURE_STRUCTURE_FRAME;
+      CLog::Log(LOGERROR, "VDPAU::COutput error mapping surface");
+      m_vdpError = true;
     }
 
+    if (m_vdpError)
+      return;
   }
-  else if(m_mixerstep == 1)
-  { // no new frame given, output second field of old frame
+#endif
+}
 
-    if(avctx->skip_frame == AVDISCARD_NONREF)
-    {
-      ClearUsedForRender(&past[1]);
-      m_DVDVideoPics.pop();
-      return VC_BUFFER;
-    }
+void COutput::GLUnmapSurfaces()
+{
+#ifdef GL_NV_vdpau_interop
 
-    m_mixerstep = 2;
-    if(m_mixerfield == VDP_VIDEO_MIXER_PICTURE_STRUCTURE_TOP_FIELD)
-      m_mixerfield = VDP_VIDEO_MIXER_PICTURE_STRUCTURE_BOTTOM_FIELD;
-    else
-      m_mixerfield = VDP_VIDEO_MIXER_PICTURE_STRUCTURE_TOP_FIELD;
-  }
-  else
   {
-    CLog::Log(LOGERROR, "CVDPAU::Decode - invalid mixer state reached");
-    return VC_BUFFER;
+    std::map<VdpVideoSurface, VdpauBufferPool::GLVideoSurface>::iterator it;
+    for (it = m_bufferPool.glVideoSurfaceMap.begin(); it != m_bufferPool.glVideoSurfaceMap.end(); ++it)
+    {
+      glVDPAUUnregisterSurfaceNV(it->second.glVdpauSurface);
+      glDeleteTextures(4, it->second.texture);
+    }
+    m_bufferPool.glVideoSurfaceMap.clear();
   }
 
-  VdpVideoSurface past_surfaces[2] = { VDP_INVALID_HANDLE, VDP_INVALID_HANDLE };
-  VdpVideoSurface futu_surfaces[1] = { VDP_INVALID_HANDLE };
-
-  if(m_mixerfield == VDP_VIDEO_MIXER_PICTURE_STRUCTURE_FRAME)
+  std::map<VdpOutputSurface, VdpauBufferPool::GLVideoSurface>::iterator it;
+  for (it = m_bufferPool.glOutputSurfaceMap.begin(); it != m_bufferPool.glOutputSurfaceMap.end(); ++it)
   {
-    if (past[0])
-      past_surfaces[1] = past[0]->surface;
-    if (past[1])
-      past_surfaces[0] = past[1]->surface;
-    futu_surfaces[0] = future->surface;
+    glVDPAUUnregisterSurfaceNV(it->second.glVdpauSurface);
+    glDeleteTextures(1, it->second.texture);
   }
-  else
-  {
-    if(m_mixerstep == 1)
-    { // first field
-      if (past[1])
-      {
-        past_surfaces[1] = past[1]->surface;
-        past_surfaces[0] = past[1]->surface;
-      }
-      futu_surfaces[0] = current->surface;
-    }
-    else
-    { // second field
-      if (past[1])
-        past_surfaces[1] = past[1]->surface;
-      past_surfaces[0] = current->surface;
-      futu_surfaces[0] = future->surface;
-    }
-  }
-
-  vdp_presentation_queue_block_until_surface_idle(vdp_flip_queue,outputSurface,&time);
-
-  VdpRect sourceRect = {0,0,vid_width, vid_height};
-
-  vdp_st = vdp_video_mixer_render(videoMixer,
-                                  VDP_INVALID_HANDLE,
-                                  0, 
-                                  m_mixerfield,
-                                  2,
-                                  past_surfaces,
-                                  current->surface,
-                                  1,
-                                  futu_surfaces,
-                                  &sourceRect,
-                                  outputSurface,
-                                  &(outRectVid),
-                                  &(outRectVid),
-                                  0,
-                                  NULL);
-  CheckStatus(vdp_st, __LINE__);
+  m_bufferPool.glOutputSurfaceMap.clear();
 
-  surfaceNum++;
-  if (surfaceNum >= totalAvailableOutputSurfaces) surfaceNum = 0;
+  glVDPAUFiniNV();
 
-  if(m_mixerfield == VDP_VIDEO_MIXER_PICTURE_STRUCTURE_FRAME)
-  {
-    ClearUsedForRender(&past[0]);
-    return VC_BUFFER | VC_PICTURE;
-  }
-  else
-  {
-    // in order to clip top and bottom lines when de-interlacing
-    // we black those lines as a work around for not working
-    // background colour using the mixer
-    // pixel perfect is preferred over overscanning or zooming
-
-    VdpRect clipRect = outRectVid;
-    clipRect.y1 = clipRect.y0 + 2;
-    uint32_t *data[] = {m_BlackBar};
-    uint32_t pitches[] = {outRectVid.x1};
-    vdp_st = vdp_output_surface_put_bits_native(outputSurface,
-                                        (void**)data,
-                                        pitches,
-                                        &clipRect);
-    CheckStatus(vdp_st, __LINE__);
+  CLog::Log(LOGNOTICE, "VDPAU::COutput: vdpau gl interop finished");
 
-    clipRect = outRectVid;
-    clipRect.y0 = clipRect.y1 - 2;
-    vdp_st = vdp_output_surface_put_bits_native(outputSurface,
-                                        (void**)data,
-                                        pitches,
-                                        &clipRect);
-    CheckStatus(vdp_st, __LINE__);
+#endif
+}
 
-    if(m_mixerstep == 1)
-      return VC_PICTURE;
-    else
-    {
-      ClearUsedForRender(&past[1]);
-      return VC_BUFFER | VC_PICTURE;
-    }
+bool COutput::CheckStatus(VdpStatus vdp_st, int line)
+{
+  if (vdp_st != VDP_STATUS_OK)
+  {
+    CLog::Log(LOGERROR, " (VDPAU) Error: %s(%d) at %s:%d\n", m_config.context->GetProcs().vdp_get_error_string(vdp_st), vdp_st, __FILE__, line);
+    m_vdpError = true;
+    return true;
   }
+  return false;
 }
 
-bool CVDPAU::GetPicture(AVCodecContext* avctx, AVFrame* frame, DVDVideoPicture* picture)
+bool COutput::CreateGlxContext()
 {
-  CSharedLock lock(m_DecoderSection);
-
-  { CSharedLock dLock(m_DisplaySection);
-    if (m_DisplayState != VDPAU_OPEN)
-      return false;
-  }
+  GLXContext   glContext;
 
-  *picture = m_DVDVideoPics.front();
-  // if this is the first field of an interlaced frame, we'll need
-  // this same picture for the second field later
-  if (m_mixerstep != 1)
-    m_DVDVideoPics.pop();
+  m_Display = g_Windowing.GetDisplay();
+  glContext = g_Windowing.GetGlxContext();
+  m_Window = g_Windowing.GetWindow();
 
-  picture->format = RENDER_FMT_VDPAU;
-  picture->iFlags &= DVP_FLAG_DROPPED;
-  picture->iWidth = OutWidth;
-  picture->iHeight = OutHeight;
-  picture->vdpau = this;
+  // Get our window attribs.
+  XWindowAttributes wndattribs;
+  XGetWindowAttributes(m_Display, m_Window, &wndattribs);
 
-  if(m_mixerstep)
+  // Get visual Info
+  XVisualInfo visInfo;
+  visInfo.visualid = wndattribs.visual->visualid;
+  int nvisuals = 0;
+  XVisualInfo* visuals = XGetVisualInfo(m_Display, VisualIDMask, &visInfo, &nvisuals);
+  if (nvisuals != 1)
   {
-    picture->iRepeatPicture = -0.5;
-    if(m_mixerstep > 1)
-    {
-      picture->dts = DVD_NOPTS_VALUE;
-      picture->pts = DVD_NOPTS_VALUE;
-    }
+    CLog::Log(LOGERROR, "VDPAU::COutput::CreateGlxContext - could not find visual");
+    return false;
   }
-  return true;
-}
+  visInfo = visuals[0];
+  XFree(visuals);
 
-void CVDPAU::Reset()
-{
-  // invalidate surfaces and picture queue when seeking
-  ClearUsedForRender(&past[0]);
-  ClearUsedForRender(&past[1]);
-  ClearUsedForRender(&current);
-  ClearUsedForRender(&future);
+  m_pixmap = XCreatePixmap(m_Display,
+                           m_Window,
+                           192,
+                           108,
+                           visInfo.depth);
+  if (!m_pixmap)
+  {
+    CLog::Log(LOGERROR, "VDPAU::COutput::CreateGlxContext - Unable to create XPixmap");
+    return false;
+  }
 
-  while (!m_DVDVideoPics.empty())
-    m_DVDVideoPics.pop();
-}
+  // create gl pixmap
+  m_glPixmap = glXCreateGLXPixmap(m_Display, &visInfo, m_pixmap);
 
-void CVDPAU::Present()
-{
-  //CLog::Log(LOGNOTICE,"%s",__FUNCTION__);
-  VdpStatus vdp_st;
+  if (!m_glPixmap)
+  {
+    CLog::Log(LOGINFO, "VDPAU::COutput::CreateGlxContext - Could not create glPixmap");
+    return false;
+  }
 
-  CSharedLock lock(m_DecoderSection);
+  m_glContext = glXCreateContext(m_Display, &visInfo, glContext, True);
 
-  { CSharedLock dLock(m_DisplaySection);
-    if (m_DisplayState != VDPAU_OPEN)
-      return;
+  if (!glXMakeCurrent(m_Display, m_glPixmap, m_glContext))
+  {
+    CLog::Log(LOGINFO, "VDPAU::COutput::CreateGlxContext - Could not make Pixmap current");
+    return false;
   }
 
-  presentSurface = outputSurface;
-
-  vdp_st = vdp_presentation_queue_display(vdp_flip_queue,
-                                          presentSurface,
-                                          0,
-                                          0,
-                                          0);
-  CheckStatus(vdp_st, __LINE__);
+  CLog::Log(LOGNOTICE, "VDPAU::COutput::CreateGlxContext - created context");
+  return true;
 }
 
-bool CVDPAU::CheckStatus(VdpStatus vdp_st, int line)
+bool COutput::DestroyGlxContext()
 {
-  if (vdp_st != VDP_STATUS_OK)
+  if (m_glContext)
   {
-    CLog::Log(LOGERROR, " (VDPAU) Error: %s(%d) at %s:%d\n", vdp_get_error_string(vdp_st), vdp_st, __FILE__, line);
+    glXMakeCurrent(m_Display, None, NULL);
+    glXDestroyContext(m_Display, m_glContext);
+  }
+  m_glContext = 0;
 
-    CExclusiveLock lock(m_DisplaySection);
+  if (m_glPixmap)
+    glXDestroyPixmap(m_Display, m_glPixmap);
+  m_glPixmap = 0;
 
-    if(m_DisplayState == VDPAU_OPEN)
-    {
-      if (vdp_st == VDP_STATUS_DISPLAY_PREEMPTED)
-      {
-        m_DisplayEvent.Reset();
-        m_DisplayState = VDPAU_LOST;
-      }
-      else
-        m_DisplayState = VDPAU_ERROR;
-    }
+  if (m_pixmap)
+    XFreePixmap(m_Display, m_pixmap);
+  m_pixmap = 0;
 
-    return true;
-  }
-  return false;
+  return true;
 }
 
 #endif