From e6ddf6fa378fc8e41535fb3d06daab9587fded2a Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?Michael=20M=C3=BCller?= <michael@fds-team.de>
Date: Sun, 22 Feb 2015 01:25:20 +0100
Subject: dxva2: Initial implementation of MPEG2 decoder using vaapi backend.

---
 configure.ac                |  16 +
 dlls/dxva2/Makefile.in      |  14 +-
 dlls/dxva2/backend.idl      |  96 ++++++
 dlls/dxva2/dxva2_private.h  | 115 ++++++-
 dlls/dxva2/genericdecoder.c | 432 +++++++++++++++++++++++++
 dlls/dxva2/main.c           |  70 +++-
 dlls/dxva2/tests/dxva2.c    |  23 +-
 dlls/dxva2/vaapi-mpeg2.c    | 753 +++++++++++++++++++++++++++++++++++++++++++
 dlls/dxva2/vaapi.c          | 767 ++++++++++++++++++++++++++++++++++++++++++++
 dlls/dxva2/videoservices.c  |  74 ++---
 10 files changed, 2293 insertions(+), 67 deletions(-)
 create mode 100644 dlls/dxva2/backend.idl
 create mode 100644 dlls/dxva2/genericdecoder.c
 create mode 100644 dlls/dxva2/vaapi-mpeg2.c
 create mode 100644 dlls/dxva2/vaapi.c

diff --git a/configure.ac b/configure.ac
index 3473566..ff3b189 100644
--- a/configure.ac
+++ b/configure.ac
@@ -100,6 +100,8 @@ AC_ARG_WITH(xxf86vm,   AS_HELP_STRING([--without-xxf86vm],[do not use XFree vide
             [if test "x$withval" = "xno"; then ac_cv_header_X11_extensions_xf86vmode_h=no; ac_cv_header_X11_extensions_xf86vmproto_h=no; fi])
 AC_ARG_WITH(zlib,      AS_HELP_STRING([--without-zlib],[do not use Zlib (data compression)]),
             [if test "x$withval" = "xno"; then ac_cv_header_zlib_h=no; fi])
+AC_ARG_WITH(va,        AS_HELP_STRING([--without-va],[do not use VA (GPU video acceleration)]),
+            [if test "x$withval" = "xno"; then ac_cv_header_va_va_x11_h=no; fi])
 
 AC_ARG_WITH(wine-tools,AS_HELP_STRING([--with-wine-tools=DIR],[use Wine tools from directory DIR]))
 AC_ARG_WITH(wine64,    AS_HELP_STRING([--with-wine64=DIR],[use the 64-bit Wine in DIR for a Wow64 build]))
@@ -1261,6 +1263,20 @@ This probably prevents linking to OpenGL. Try deleting the file and restarting c
     WINE_WARNING_WITH(opengl,[test -n "$opengl_msg"],[$opengl_msg
 OpenGL and Direct3D won't be supported.])
 
+    dnl **** Check for libva ****
+    AC_CHECK_HEADERS([va/va_x11.h])
+    if test "$ac_cv_header_va_va_x11_h" = "yes"
+    then
+        WINE_CHECK_SONAME(va,vaQuerySurfaceAttributes,,,[$X_LIBS])
+        WINE_CHECK_SONAME(va-x11,vaGetDisplay,,,[$X_LIBS -lva])
+        if test "x$ac_cv_lib_soname_va" != "x" -a "x$ac_cv_lib_soname_va_x11" != "x"
+        then
+            AC_DEFINE(HAVE_VAAPI, 1, [Define if you have the libva development files])
+        fi
+    fi
+    WINE_NOTICE_WITH(va,[test "x$ac_cv_lib_soname_va" = "x" -o "x$ac_cv_lib_soname_va_x11" = "x"],
+                     [libva ${notice_platform}development files not found, GPU video acceleration won't be supported.])
+
     CPPFLAGS="$ac_save_CPPFLAGS"
 else
     X_CFLAGS=""
diff --git a/dlls/dxva2/Makefile.in b/dlls/dxva2/Makefile.in
index 4af9dc0..afdefc3 100644
--- a/dlls/dxva2/Makefile.in
+++ b/dlls/dxva2/Makefile.in
@@ -1,8 +1,14 @@
 MODULE = dxva2.dll
-IMPORTS = ole32
+IMPORTS = ole32 advapi32
+
+IDL_SRCS = \
+	backend.idl
 
 C_SRCS = \
-	main.c \
-	videoservices.c \
 	devicemanager.c \
-	softwareprocessor.c
+	genericdecoder.c \
+	main.c \
+	softwareprocessor.c \
+	vaapi.c \
+	vaapi-mpeg2.c \
+	videoservices.c
diff --git a/dlls/dxva2/backend.idl b/dlls/dxva2/backend.idl
new file mode 100644
index 0000000..8d48835
--- /dev/null
+++ b/dlls/dxva2/backend.idl
@@ -0,0 +1,96 @@
+/*
+ * Copyright (C) 2015 Michael Müller for Pipelight
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2.1 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA
+ */
+
+#pragma makedep header
+
+import "unknwn.idl";
+import "dxva2api.idl";
+
+typedef struct _WineVideoImage
+{
+    void *buffer;
+    UINT width;
+    UINT height;
+    D3DFORMAT format;
+    UINT planeCount;
+    UINT *pitches;
+    UINT *offsets;
+} WineVideoImage;
+
+[
+    object,
+    local,
+    uuid(4C22E900-B16F-11E4-AB27-0800200C9A66)
+]
+interface IWineVideoDecoder : IUnknown
+{
+    HRESULT LockBuffer(
+        [in] UINT type,
+        [out] void **buffer,
+        [out] UINT *size);
+
+    HRESULT UnlockBuffer(
+        [in] UINT type);
+
+    HRESULT ExecuteBuffers(
+        [in] DXVA2_DecodeBufferDesc *pictureParam,
+        [in] DXVA2_DecodeBufferDesc *qMatrix,
+        [in] DXVA2_DecodeBufferDesc *sliceInfo,
+        [in] DXVA2_DecodeBufferDesc *bitStream);
+
+    HRESULT BeginFrame(
+        [in] UINT surfaceIndex);
+
+    HRESULT EndFrame();
+
+    HRESULT LockImage(
+        [in] WineVideoImage *image);
+
+    HRESULT UnlockImage();
+}
+
+[
+    object,
+    local,
+    uuid(21E19BB0-B16E-11E4-AB27-0800200C9A66)
+]
+interface IWineVideoService : IUnknown
+{
+    HRESULT GetDecoderDeviceGuids(
+        [out] UINT *count,
+        [out] GUID **guids);
+
+    HRESULT GetDecoderRenderTargets(
+        [in] REFGUID guid,
+        [out] UINT *count,
+        [out] D3DFORMAT **formats);
+
+    HRESULT GetDecoderConfigurations(
+        [in] REFGUID guid,
+        [in] const DXVA2_VideoDesc *videoDesc,
+        [in] IUnknown *reserved,
+        [out] UINT *count,
+        [out] DXVA2_ConfigPictureDecode **configs);
+
+    HRESULT CreateVideoDecoder(
+        [in] REFGUID guid,
+        [in] const DXVA2_VideoDesc *videoDesc,
+        [in] DXVA2_ConfigPictureDecode *config,
+        [in] UINT numSurfaces,
+        [out] IWineVideoDecoder **decoder);
+}
diff --git a/dlls/dxva2/dxva2_private.h b/dlls/dxva2/dxva2_private.h
index f0068b6..f518637 100644
--- a/dlls/dxva2/dxva2_private.h
+++ b/dlls/dxva2/dxva2_private.h
@@ -1,5 +1,5 @@
 /*
- * Copyright 2014 Michael Müller for Pipelight
+ * Copyright 2014-2015 Michael Müller for Pipelight
  *
  * This library is free software; you can redistribute it and/or
  * modify it under the terms of the GNU Lesser General Public
@@ -17,9 +17,122 @@
  */
 
 #include "dxva2api.h"
+#include "d3d9.h"
+#include "backend.h"
+#ifdef HAVE_VAAPI
+# undef Status
+# include <va/va_x11.h>
+#endif
 
 extern HRESULT videoservice_create( IDirect3DDevice9 *device, REFIID riid, void **ppv ) DECLSPEC_HIDDEN;
 extern HRESULT devicemanager_create( UINT *resetToken, void **ppv ) DECLSPEC_HIDDEN;
 extern HRESULT processor_software_create( IDirectXVideoProcessorService *processor_service, IDirect3DDevice9 *device,
                                           const DXVA2_VideoDesc *pVideoDesc, D3DFORMAT RenderTargetFormat,
                                           UINT MaxNumSubStreams, IDirectXVideoProcessor **processor ) DECLSPEC_HIDDEN;
+extern HRESULT genericdecoder_create( IDirectXVideoDecoderService *videodecoder, const DXVA2_VideoDesc *videoDesc,
+                                      DXVA2_ConfigPictureDecode *config, IDirect3DSurface9 **decoderRenderTargets,
+                                      UINT numSurfaces, IWineVideoDecoder *backend, IDirectXVideoDecoder **decode ) DECLSPEC_HIDDEN;
+
+/*****************************************************************************
+ * vaapi backend
+ */
+
+/* public */
+extern BOOL config_vaapi_enabled DECLSPEC_HIDDEN;
+extern IWineVideoService *vaapi_videoservice_create( void ) DECLSPEC_HIDDEN;
+
+/* internal */
+#ifdef HAVE_VAAPI
+
+#define MAKE_FUNCPTR(f) extern typeof(f) * p##f DECLSPEC_HIDDEN
+MAKE_FUNCPTR(XCloseDisplay);
+MAKE_FUNCPTR(XOpenDisplay);
+MAKE_FUNCPTR(vaBeginPicture);
+MAKE_FUNCPTR(vaCreateBuffer);
+MAKE_FUNCPTR(vaCreateConfig);
+MAKE_FUNCPTR(vaCreateContext);
+MAKE_FUNCPTR(vaCreateImage);
+MAKE_FUNCPTR(vaCreateSurfaces);
+MAKE_FUNCPTR(vaDestroyBuffer);
+MAKE_FUNCPTR(vaDestroyConfig);
+MAKE_FUNCPTR(vaDestroyContext);
+MAKE_FUNCPTR(vaDestroyImage);
+MAKE_FUNCPTR(vaDestroySurfaces);
+MAKE_FUNCPTR(vaEndPicture);
+MAKE_FUNCPTR(vaErrorStr);
+MAKE_FUNCPTR(vaGetConfigAttributes);
+MAKE_FUNCPTR(vaGetDisplay);
+MAKE_FUNCPTR(vaGetImage);
+MAKE_FUNCPTR(vaInitialize);
+MAKE_FUNCPTR(vaMapBuffer);
+MAKE_FUNCPTR(vaMaxNumEntrypoints);
+MAKE_FUNCPTR(vaMaxNumProfiles);
+MAKE_FUNCPTR(vaQueryConfigEntrypoints);
+MAKE_FUNCPTR(vaQueryConfigProfiles);
+MAKE_FUNCPTR(vaQuerySurfaceAttributes);
+MAKE_FUNCPTR(vaRenderPicture);
+MAKE_FUNCPTR(vaSyncSurface);
+MAKE_FUNCPTR(vaTerminate);
+MAKE_FUNCPTR(vaUnmapBuffer);
+#undef MAKE_FUNCPTR
+
+struct vaapi_profile
+{
+    const int profile;
+    const int entryPoint;
+    const GUID* guid;
+    BOOL supported;
+};
+
+struct vaapi_format
+{
+    const D3DFORMAT d3dformat;
+    const unsigned int vafourcc;
+    const unsigned int vaformat;
+    const BOOL fallback;
+    const unsigned int bits;
+};
+
+typedef struct
+{
+    IWineVideoService IWineVideoService_iface;
+    LONG refCount;
+
+    /* libraries */
+    void *va_handle;
+    void *va_x11_handle;
+    void *x11_handle;
+
+    /* display */
+    Display *x11_display;
+    VADisplay va_display;
+} WineVideoServiceImpl;
+
+static inline WineVideoServiceImpl *impl_from_IWineVideoService( IWineVideoService *iface )
+{
+    return CONTAINING_RECORD(iface, WineVideoServiceImpl, IWineVideoService_iface);
+}
+
+static inline VADisplay IWineVideoService_VADisplay( IWineVideoService *iface )
+{
+    WineVideoServiceImpl *This = impl_from_IWineVideoService(iface);
+    return This->va_display;
+}
+
+extern void vaapi_lock( void ) DECLSPEC_HIDDEN;
+extern void vaapi_unlock( void ) DECLSPEC_HIDDEN;
+
+extern struct vaapi_profile *vaapi_lookup_profile( int profile, int entryPoint ) DECLSPEC_HIDDEN;
+extern struct vaapi_profile *vaapi_lookup_guid( const GUID *guid ) DECLSPEC_HIDDEN;
+extern struct vaapi_format *vaapi_lookup_d3dformat( D3DFORMAT d3dformat ) DECLSPEC_HIDDEN;
+
+extern BOOL vaapi_is_format_supported( VADisplay va_display, struct vaapi_profile *profile,
+                                       struct vaapi_format *format ) DECLSPEC_HIDDEN;
+extern BOOL vaapi_create_surfaces( VADisplay va_display, const struct vaapi_format *format, UINT width, UINT height,
+                                   VAImage *vaImage, UINT numSurfaces, VASurfaceID **surfaceList ) DECLSPEC_HIDDEN;
+
+extern HRESULT vaapi_mpeg2decoder_create( IWineVideoService *backend, const DXVA2_VideoDesc *videoDesc,
+                                          DXVA2_ConfigPictureDecode *config, UINT numSurfaces,
+                                          IWineVideoDecoder **decoder ) DECLSPEC_HIDDEN;
+
+#endif /* HAVE_VAAPI */
diff --git a/dlls/dxva2/genericdecoder.c b/dlls/dxva2/genericdecoder.c
new file mode 100644
index 0000000..3903d6b
--- /dev/null
+++ b/dlls/dxva2/genericdecoder.c
@@ -0,0 +1,432 @@
+/*
+ * Copyright 2014-2015 Michael Müller for Pipelight
+ * Copyright 2014-2015 Sebastian Lackner for Pipelight
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2.1 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA
+ */
+
+#include <stdarg.h>
+#include "windef.h"
+#include "winbase.h"
+
+#include "wine/debug.h"
+
+#define COBJMACROS
+#include "d3d9.h"
+#include "dxva2api.h"
+#include "dxva.h"
+#include "dxva2_private.h"
+
+WINE_DEFAULT_DEBUG_CHANNEL(dxva2);
+
+static void convert_nv12_nv12(WineVideoImage *image, D3DSURFACE_DESC *d3ddesc, D3DLOCKED_RECT *d3drect)
+{
+    unsigned int y, height, width;
+    char *src, *dst;
+
+    height = min(d3ddesc->Height, image->height);
+    width  = min(d3ddesc->Width,image->width);
+
+    src = (char *)image->buffer + image->offsets[0];
+    dst = d3drect->pBits;
+
+    for (y = 0; y < height; y++)
+    {
+        memcpy(dst, src, width);
+        src += image->pitches[0];
+        dst += d3drect->Pitch;
+    }
+
+    src = (char *)image->buffer + image->offsets[1];
+    dst = (char *)d3drect->pBits + d3drect->Pitch * d3ddesc->Height;
+
+    for (y = 0; y < height / 2; y++)
+    {
+        memcpy(dst, src, width);
+        src += image->pitches[1];
+        dst += d3drect->Pitch;
+    }
+}
+
+struct image_converter
+{
+    D3DFORMAT src;
+    D3DFORMAT dst;
+    void (*func)(WineVideoImage *image, D3DSURFACE_DESC *d3ddesc, D3DLOCKED_RECT *d3drect);
+};
+
+static struct image_converter image_converters[] =
+{
+    {MAKEFOURCC('N','V','1','2'), MAKEFOURCC('N','V','1','2'), convert_nv12_nv12}
+};
+
+static struct image_converter *get_image_converter(WineVideoImage *imageInfo, D3DSURFACE_DESC *d3ddesc)
+{
+    unsigned int i;
+    for (i = 0; i < sizeof(image_converters) / sizeof(image_converters[0]); i++)
+    {
+        if (image_converters[i].src == imageInfo->format && image_converters[i].dst == d3ddesc->Format)
+            return &image_converters[i];
+    }
+    return NULL;
+}
+
+typedef struct
+{
+    IDirectXVideoDecoder IDirectXVideoDecoder_iface;
+
+    LONG refCount;
+    IDirectXVideoDecoderService *videodecoder;
+    IWineVideoDecoder *backend;
+
+    /* video attributes */
+    UINT width;
+    UINT height;
+
+    /* surfaces used by this decoder */
+    UINT surfaceCount;
+    IDirect3DSurface9 **surfaces;
+    int currentSurface;
+} DirectXVideoDecoderGenericImpl;
+
+static inline DirectXVideoDecoderGenericImpl *impl_from_IDirectXVideoDecoderGeneric( IDirectXVideoDecoder *iface )
+{
+    return CONTAINING_RECORD(iface, DirectXVideoDecoderGenericImpl, IDirectXVideoDecoder_iface);
+}
+
+/*****************************************************************************
+ * IDirectXVideoDecoderGeneric interface
+ */
+
+static HRESULT WINAPI DirectXVideoDecoderGeneric_QueryInterface( IDirectXVideoDecoder *iface, REFIID riid, LPVOID *ppv )
+{
+    DirectXVideoDecoderGenericImpl *This = impl_from_IDirectXVideoDecoderGeneric(iface);
+    TRACE("(%p/%p)->(%s, %p)\n", iface, This, debugstr_guid(riid), ppv);
+
+    *ppv = NULL;
+
+    if (IsEqualIID(riid, &IID_IUnknown) || IsEqualIID(riid, &IID_IDirectXVideoDecoder))
+        *ppv = (LPVOID)iface;
+
+    if (*ppv)
+    {
+        IUnknown_AddRef((IUnknown *)(*ppv));
+        return S_OK;
+    }
+
+    FIXME("No interface for %s\n", debugstr_guid(riid));
+    return E_NOINTERFACE;
+}
+
+static ULONG WINAPI DirectXVideoDecoderGeneric_AddRef( IDirectXVideoDecoder *iface )
+{
+    DirectXVideoDecoderGenericImpl *This = impl_from_IDirectXVideoDecoderGeneric(iface);
+    ULONG refCount = InterlockedIncrement(&This->refCount);
+
+    TRACE("(%p)->() AddRef from %d\n", This, refCount - 1);
+
+    return refCount;
+}
+
+static ULONG WINAPI DirectXVideoDecoderGeneric_Release( IDirectXVideoDecoder *iface )
+{
+    DirectXVideoDecoderGenericImpl *This = impl_from_IDirectXVideoDecoderGeneric(iface);
+    ULONG refCount = InterlockedDecrement(&This->refCount);
+    unsigned int i;
+
+    TRACE("(%p)->() Release from %d\n", This, refCount + 1);
+
+    if (!refCount)
+    {
+        TRACE("Destroying\n");
+
+        if (This->currentSurface != -1)
+            ERR("decoder destroyed while decoding frame\n");
+
+        for (i = 0; i < This->surfaceCount; i++)
+            IDirect3DSurface9_Release(This->surfaces[i]);
+        HeapFree(GetProcessHeap(), 0, This->surfaces);
+
+        IDirectXVideoDecoderService_Release(This->videodecoder);
+        IWineVideoDecoder_Release(This->backend);
+        CoTaskMemFree(This);
+    }
+
+    return refCount;
+}
+
+static HRESULT WINAPI DirectXVideoDecoderGeneric_GetVideoDecoderService( IDirectXVideoDecoder *iface, IDirectXVideoDecoderService **ppService )
+{
+    DirectXVideoDecoderGenericImpl *This = impl_from_IDirectXVideoDecoderGeneric(iface);
+
+    TRACE("(%p)->(%p)\n", This, ppService);
+
+    if (!ppService)
+        return E_INVALIDARG;
+
+    IDirectXVideoDecoderService_AddRef(This->videodecoder);
+    *ppService = This->videodecoder;
+
+    return S_OK;
+}
+
+static HRESULT WINAPI DirectXVideoDecoderGeneric_GetCreationParameters( IDirectXVideoDecoder *iface, GUID *pDeviceGuid, DXVA2_VideoDesc *pVideoDesc,
+                                                                        DXVA2_ConfigPictureDecode *pConfig, IDirect3DSurface9 ***pDecoderRenderTargets,
+                                                                        UINT *pNumSurfaces )
+{
+    DirectXVideoDecoderGenericImpl *This = impl_from_IDirectXVideoDecoderGeneric(iface);
+
+    FIXME("(%p)->(%p, %p, %p, %p, %p)\n", This, pDeviceGuid, pVideoDesc, pConfig, pDecoderRenderTargets, pNumSurfaces);
+
+    return E_NOTIMPL;
+}
+
+static HRESULT WINAPI DirectXVideoDecoderGeneric_GetBuffer( IDirectXVideoDecoder *iface, UINT BufferType, void **ppBuffer, UINT *pBufferSize )
+{
+    DirectXVideoDecoderGenericImpl *This = impl_from_IDirectXVideoDecoderGeneric(iface);
+
+    TRACE("(%p)->(%u, %p, %p)\n", This, BufferType, ppBuffer, pBufferSize);
+
+    if (!ppBuffer || !pBufferSize)
+        return E_INVALIDARG;
+
+    return IWineVideoDecoder_LockBuffer(This->backend, BufferType, ppBuffer, pBufferSize);
+}
+
+static HRESULT WINAPI DirectXVideoDecoderGeneric_ReleaseBuffer( IDirectXVideoDecoder *iface, UINT BufferType )
+{
+    DirectXVideoDecoderGenericImpl *This = impl_from_IDirectXVideoDecoderGeneric(iface);
+
+    TRACE("(%p)->(%u)\n", This, BufferType);
+
+    return IWineVideoDecoder_UnlockBuffer(This->backend, BufferType);
+}
+
+static HRESULT WINAPI DirectXVideoDecoderGeneric_BeginFrame( IDirectXVideoDecoder *iface, IDirect3DSurface9 *pRenderTarget, void *pvPVPData )
+{
+    DirectXVideoDecoderGenericImpl *This = impl_from_IDirectXVideoDecoderGeneric(iface);
+    unsigned int i;
+    HRESULT hr;
+
+    TRACE("(%p)->(%p, %p)\n", This, pRenderTarget, pvPVPData);
+
+    if (!pRenderTarget)
+        return E_INVALIDARG;
+
+    if (This->currentSurface >= 0)
+    {
+        ERR("previous frame was not finished properly, resetting decoder\n");
+        IWineVideoDecoder_EndFrame(This->backend);
+        This->currentSurface = -1;
+    }
+
+    for (i = 0; i < This->surfaceCount; i++)
+    {
+        if (This->surfaces[i] == pRenderTarget)
+            break;
+    }
+    if (i >= This->surfaceCount)
+    {
+        ERR("render target %p is not in the list of surfaces\n", pRenderTarget);
+        return E_INVALIDARG;
+    }
+
+    hr = IWineVideoDecoder_BeginFrame(This->backend, i);
+    if (FAILED(hr))
+    {
+        FIXME("Failed to begin frame!\n");
+        return hr;
+    }
+
+    /* set the new current frame */
+    This->currentSurface = i;
+    return S_OK;
+}
+
+static HRESULT WINAPI DirectXVideoDecoderGeneric_EndFrame( IDirectXVideoDecoder *iface, HANDLE *pHandleComplete )
+{
+    DirectXVideoDecoderGenericImpl *This = impl_from_IDirectXVideoDecoderGeneric(iface);
+    struct image_converter *converter;
+    D3DSURFACE_DESC d3ddesc;
+    D3DLOCKED_RECT d3drect;
+    WineVideoImage imageInfo;
+    int last_surface;
+    HRESULT hr;
+
+    TRACE("(%p)->(%p)\n", This, pHandleComplete);
+
+    last_surface = This->currentSurface;
+    This->currentSurface = -1;
+
+    if (last_surface < 0)
+        WARN("called without frame context\n");
+
+    hr = IWineVideoDecoder_EndFrame(This->backend);
+    if (FAILED(hr))
+    {
+        FIXME("Failed to end frame!\n");
+        return hr;
+    }
+
+    hr = IWineVideoDecoder_LockImage(This->backend, &imageInfo);
+    if (SUCCEEDED(hr))
+    {
+        hr = IDirect3DSurface9_GetDesc(This->surfaces[last_surface], &d3ddesc);
+        if (SUCCEEDED(hr))
+        {
+            hr = IDirect3DSurface9_LockRect(This->surfaces[last_surface], &d3drect, NULL, D3DLOCK_DISCARD);
+            if (SUCCEEDED(hr))
+            {
+                converter = get_image_converter(&imageInfo, &d3ddesc);
+                if (converter)
+                {
+                    converter->func(&imageInfo, &d3ddesc, &d3drect);
+                }
+                else
+                {
+                    FIXME("could not find any suiteable converter\n");
+                    hr = E_FAIL;
+                }
+                IDirect3DSurface9_UnlockRect(This->surfaces[last_surface]);
+            }
+        }
+        IWineVideoDecoder_UnlockImage(This->backend);
+    }
+
+    return hr;
+}
+
+static HRESULT WINAPI DirectXVideoDecoderGeneric_Execute( IDirectXVideoDecoder *iface, const DXVA2_DecodeExecuteParams *pExecuteParams )
+{
+    DirectXVideoDecoderGenericImpl *This = impl_from_IDirectXVideoDecoderGeneric(iface);
+    DXVA2_DecodeBufferDesc *pictureParam = NULL, *qMatrix = NULL, *sliceInfo = NULL, *bitStream = NULL;
+    unsigned int i;
+
+    TRACE("(%p)->(%p)\n", This, pExecuteParams);
+
+    if (!pExecuteParams || !pExecuteParams->pCompressedBuffers)
+        return E_INVALIDARG;
+
+    for (i = 0; i < pExecuteParams->NumCompBuffers; i++)
+    {
+        switch (pExecuteParams->pCompressedBuffers[i].CompressedBufferType)
+        {
+            case DXVA2_PictureParametersBufferType:
+                if (!pictureParam)
+                {
+                    pictureParam = &pExecuteParams->pCompressedBuffers[i];
+                    continue;
+                }
+                break;
+
+            case DXVA2_InverseQuantizationMatrixBufferType:
+                if (!qMatrix)
+                {
+                    qMatrix = &pExecuteParams->pCompressedBuffers[i];
+                    continue;
+                }
+                break;
+
+            case DXVA2_SliceControlBufferType:
+                if (!sliceInfo)
+                {
+                    sliceInfo = &pExecuteParams->pCompressedBuffers[i];
+                    continue;
+                }
+                break;
+
+            case DXVA2_BitStreamDateBufferType:
+                if (!bitStream)
+                {
+                    bitStream = &pExecuteParams->pCompressedBuffers[i];
+                    continue;
+                }
+                break;
+
+            default:
+                FIXME("ignoring unsupported buffer type 0x%x\n",
+                      pExecuteParams->pCompressedBuffers[i].CompressedBufferType);
+                continue;
+        }
+
+        ERR("buffer type 0x%x specified multiple times\n",
+            pExecuteParams->pCompressedBuffers[i].CompressedBufferType);
+        return E_FAIL;
+    }
+
+    return IWineVideoDecoder_ExecuteBuffers(This->backend, pictureParam, qMatrix, sliceInfo, bitStream);
+}
+
+static const IDirectXVideoDecoderVtbl DirectXVideoDecoderGeneric_VTable =
+{
+    DirectXVideoDecoderGeneric_QueryInterface,
+    DirectXVideoDecoderGeneric_AddRef,
+    DirectXVideoDecoderGeneric_Release,
+    DirectXVideoDecoderGeneric_GetVideoDecoderService,
+    DirectXVideoDecoderGeneric_GetCreationParameters,
+    DirectXVideoDecoderGeneric_GetBuffer,
+    DirectXVideoDecoderGeneric_ReleaseBuffer,
+    DirectXVideoDecoderGeneric_BeginFrame,
+    DirectXVideoDecoderGeneric_EndFrame,
+    DirectXVideoDecoderGeneric_Execute
+};
+
+HRESULT genericdecoder_create( IDirectXVideoDecoderService *videodecoder, const DXVA2_VideoDesc *videoDesc,
+                               DXVA2_ConfigPictureDecode *config, IDirect3DSurface9 **decoderRenderTargets,
+                               UINT numSurfaces, IWineVideoDecoder *backend, IDirectXVideoDecoder **decoder )
+{
+    DirectXVideoDecoderGenericImpl *genericdecoder;
+    unsigned int i;
+
+    if (!videoDesc || !config || !decoderRenderTargets || !decoder || !backend)
+        return E_INVALIDARG;
+
+    *decoder = NULL;
+
+    genericdecoder = CoTaskMemAlloc(sizeof(*genericdecoder));
+    if (!genericdecoder)
+        return E_OUTOFMEMORY;
+
+    genericdecoder->IDirectXVideoDecoder_iface.lpVtbl = &DirectXVideoDecoderGeneric_VTable;
+    genericdecoder->refCount             = 1;
+    genericdecoder->videodecoder         = videodecoder;
+    genericdecoder->backend              = backend;
+
+    genericdecoder->width                = videoDesc->SampleWidth;
+    genericdecoder->height               = videoDesc->SampleHeight;
+
+    genericdecoder->surfaceCount         = numSurfaces;
+    genericdecoder->surfaces             = NULL;
+    genericdecoder->currentSurface       = -1;
+
+    genericdecoder->surfaces = HeapAlloc(GetProcessHeap(), 0, sizeof(IDirect3DSurface9*) * numSurfaces);
+    if (!genericdecoder->surfaces)
+    {
+        CoTaskMemFree(genericdecoder);
+        return E_FAIL;
+    }
+
+    for (i = 0; i < numSurfaces; i++)
+    {
+        genericdecoder->surfaces[i] = decoderRenderTargets[i];
+        IDirect3DSurface9_AddRef(genericdecoder->surfaces[i]);
+    }
+
+    IDirectXVideoDecoderService_AddRef(videodecoder);
+    IWineVideoDecoder_AddRef(backend);
+
+    *decoder = &genericdecoder->IDirectXVideoDecoder_iface;
+    return S_OK;
+}
diff --git a/dlls/dxva2/main.c b/dlls/dxva2/main.c
index 355e11f..758c146 100644
--- a/dlls/dxva2/main.c
+++ b/dlls/dxva2/main.c
@@ -32,6 +32,8 @@
 
 WINE_DEFAULT_DEBUG_CHANNEL(dxva2);
 
+BOOL config_vaapi_enabled = FALSE;
+
 BOOL WINAPI CapabilitiesRequestAndCapabilitiesReply( HMONITOR monitor, LPSTR buffer, DWORD length )
 {
     FIXME("(%p, %p, %d): stub\n", monitor, buffer, length);
@@ -323,16 +325,76 @@ BOOL WINAPI SetVCPFeature( HMONITOR monitor, BYTE vcpCode, DWORD value )
     return FALSE;
 }
 
-BOOL WINAPI DllMain (HINSTANCE hinstDLL, DWORD fdwReason, LPVOID lpvReserved)
+static BOOL get_app_key( HKEY *defkey, HKEY *appkey )
+{
+    char buffer[MAX_PATH+16];
+    DWORD len;
+
+    *appkey = 0;
+
+    /* @@ Wine registry key: HKCU\Software\Wine\DXVA2 */
+    if (RegOpenKeyA(HKEY_CURRENT_USER, "Software\\Wine\\DXVA2", defkey))
+        *defkey = 0;
+
+    len = GetModuleFileNameA(0, buffer, MAX_PATH);
+    if (len && len < MAX_PATH)
+    {
+        HKEY tmpkey;
+
+        /* @@ Wine registry key: HKCU\Software\Wine\AppDefaults\app.exe\DXVA2 */
+        if (!RegOpenKeyA(HKEY_CURRENT_USER, "Software\\Wine\\AppDefaults", &tmpkey))
+        {
+            char *p, *appname = buffer;
+            if ((p = strrchr(appname, '/'))) appname = p + 1;
+            if ((p = strrchr(appname, '\\'))) appname = p + 1;
+            strcat(appname, "\\DXVA2");
+
+            if (RegOpenKeyA(tmpkey, appname, appkey)) *appkey = 0;
+            RegCloseKey(tmpkey);
+        }
+    }
+
+    return *defkey || *appkey;
+}
+
+static BOOL get_config_key( HKEY defkey, HKEY appkey, const char *name, char *buffer, DWORD size )
+{
+    if (appkey && !RegQueryValueExA( appkey, name, 0, NULL, (LPBYTE)buffer, &size ))
+        return TRUE;
+
+    if (defkey && !RegQueryValueExA( defkey, name, 0, NULL, (LPBYTE)buffer, &size ))
+        return TRUE;
+
+    return FALSE;
+}
+
+static void dxva2_init( void )
+{
+    HKEY defkey, appkey;
+    char buffer[MAX_PATH];
+
+    if (!get_app_key(&defkey, &appkey))
+        return;
+
+    if (get_config_key(defkey, appkey, "backend", buffer, sizeof(buffer)))
+        config_vaapi_enabled = !strcmp(buffer, "va");
+
+    if (defkey) RegCloseKey(defkey);
+    if (appkey) RegCloseKey(appkey);
+}
+
+BOOL WINAPI DllMain(HINSTANCE hinstDLL, DWORD fdwReason, LPVOID lpvReserved)
 {
     TRACE("%p,%x,%p\n", hinstDLL, fdwReason, lpvReserved);
 
-    switch (fdwReason) {
-        case DLL_WINE_PREATTACH:
-            return FALSE;  /* prefer native version */
+    switch (fdwReason)
+    {
         case DLL_PROCESS_ATTACH:
+            dxva2_init();
             DisableThreadLibraryCalls(hinstDLL);
             break;
+        case DLL_PROCESS_DETACH:
+            break;
     }
 
     return TRUE;
diff --git a/dlls/dxva2/tests/dxva2.c b/dlls/dxva2/tests/dxva2.c
index dcbb990..c28be4d 100644
--- a/dlls/dxva2/tests/dxva2.c
+++ b/dlls/dxva2/tests/dxva2.c
@@ -1,7 +1,7 @@
 /*
  * Unit tests for dxva2 functions
  *
- * Copyright 2015 Michael Müller
+ * Copyright (c) 2015 Michael Müller
  *
  * This library is free software; you can redistribute it and/or
  * modify it under the terms of the GNU Lesser General Public
@@ -85,9 +85,9 @@ struct decoder_test decoder_tests[] =
     {&DXVA2_ModeH264_E, 720, 576, 17, FALSE},
 };
 
-static const char* convert_decoderguid_to_str(const GUID *guid)
+static inline const char* convert_decoderguid_to_str(const GUID *guid)
 {
-    unsigned int i;
+    int i;
 
     for (i = 0; i < sizeof(decoder_names) / sizeof(decoder_names[0]); i++)
     {
@@ -192,7 +192,7 @@ static void test_decoder_resolution(IDirectXVideoDecoderService *service, REFGUI
     DXVA2_ConfigPictureDecode config;
     IDirect3DSurface9 **surfaces;
     IDirectXVideoDecoder *decoder;
-    unsigned int i;
+    UINT i;
 
     trace("Analysing buffer sizes for: %s (%u x %u)\n", convert_decoderguid_to_str(guid), width, height);
 
@@ -222,7 +222,7 @@ static void test_decoder_resolution(IDirectXVideoDecoderService *service, REFGUI
     ok(hr == D3DERR_INVALIDCALL, "Expected D3DERR_INVALIDCALL, got %x\n", hr);
 
     hr = IDirectXVideoDecoderService_GetDecoderConfigurations(service, guid, &desc, NULL, &count, &configs);
-    ok(!hr, "Failed to get decoder configuration for: %s\n", convert_decoderguid_to_str(guid));
+    ok(!hr, "Failed to get decoder congiruation for: %s\n", convert_decoderguid_to_str(guid));
     if (hr) return;
 
     if (!count)
@@ -308,10 +308,10 @@ static void test_decoder(IDirectXVideoDecoderService *service, REFGUID const gui
 static void test_decoder_service(HWND focus_window)
 {
     IDirectXVideoDecoderService *service;
-    unsigned int i, j;
     HRESULT hr;
     UINT count;
     GUID *guids;
+    int i, j;
 
     if (!create_video_service(focus_window, &IID_IDirectXVideoDecoderService, (void**)&service))
     {
@@ -320,11 +320,8 @@ static void test_decoder_service(HWND focus_window)
     }
 
     hr = IDirectXVideoDecoderService_GetDecoderDeviceGuids(service, &count, &guids);
-    if (hr)
-    {
-        skip("Failed to get decoder guids: %x\n", hr);
-        return;
-    }
+    ok(!hr, "Failed to get decoder guids: %x\n", hr);
+    if (hr) return;
 
     for (i = 0; i < count; i++)
     {
@@ -347,8 +344,8 @@ static void test_decoder_service(HWND focus_window)
             continue;
         }
 
-        test_decoder(service, decoder_tests[i].guid, decoder_tests[i].width,
-                     decoder_tests[i].height, decoder_tests[i].surface_count);
+        test_decoder(service, decoder_tests[i].guid, decoder_tests[i].width, decoder_tests[i].height,
+                     decoder_tests[i].surface_count);
     }
 
     IDirectXVideoDecoderService_Release(service);
diff --git a/dlls/dxva2/vaapi-mpeg2.c b/dlls/dxva2/vaapi-mpeg2.c
new file mode 100644
index 0000000..7b7f61d
--- /dev/null
+++ b/dlls/dxva2/vaapi-mpeg2.c
@@ -0,0 +1,753 @@
+/*
+ * Copyright 2014-2015 Michael Müller for Pipelight
+ * Copyright 2014-2015 Sebastian Lackner for Pipelight
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2.1 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA
+ */
+
+#include "config.h"
+#include <stdarg.h>
+#include <assert.h>
+#include "windef.h"
+#include "winbase.h"
+
+#include "wine/debug.h"
+
+#define COBJMACROS
+#include "d3d9.h"
+#include "dxva2api.h"
+#include "dxva.h"
+#include "dxva2_private.h"
+
+WINE_DEFAULT_DEBUG_CHANNEL(dxva2);
+
+#ifdef HAVE_VAAPI
+
+#define MAX_SLICES 1024
+
+static inline UINT estimate_maximum_slice_size( UINT width, UINT height )
+{
+    UINT size = width * (height + 8);
+    return max(size, 1241600);
+}
+
+typedef struct
+{
+    IWineVideoDecoder IWineVideoDecoder_iface;
+    LONG refCount;
+    IWineVideoService *service;
+
+    /* video attributes */
+    UINT width;
+    UINT height;
+    D3DFORMAT format;
+    DWORD maxSliceSize;
+    VAImage vaImage;
+
+    /* surfaces used by this decoder */
+    UINT surfaceCount;
+    VASurfaceID *surfaces;
+    UINT currentSurface;
+
+    /* configuration and context of the decoder */
+    VAConfigID config;
+    VAContextID context;
+
+    /* slice buffer id */
+    VABufferID vaBitstream;
+    DXVA_PictureParameters d3dPictureParam;
+    DXVA_QmatrixData d3dQMatrix;
+    DXVA_SliceInfo d3dSliceInfo[MAX_SLICES];
+} WineVideoDecoderMPEG2Impl;
+
+/* caller has to hold the vaapi_lock */
+static HRESULT process_picture_parameters( WineVideoDecoderMPEG2Impl *This, const DXVA2_DecodeBufferDesc *desc )
+{
+    VADisplay va_display = IWineVideoService_VADisplay(This->service);
+    VAPictureParameterBufferMPEG2 params;
+    VABufferID vaPictureParam;
+    VAStatus status;
+
+    if (desc->DataSize != sizeof(This->d3dPictureParam))
+        FIXME("unexpected picture parameter buffer size %u\n", desc->DataSize);
+
+    memset(&params, 0, sizeof(params));
+
+    params.horizontal_size = This->width;
+    params.vertical_size   = This->height;
+
+    /* forward reference */
+    if (This->d3dPictureParam.wForwardRefPictureIndex < This->surfaceCount)
+        params.forward_reference_picture = This->surfaces[This->d3dPictureParam.wForwardRefPictureIndex];
+    else if (This->d3dPictureParam.wForwardRefPictureIndex == 0xFFFF)
+        params.forward_reference_picture = VA_INVALID_ID;
+    else
+    {
+        FIXME("invalid forward surface reference index\n");
+        params.forward_reference_picture = VA_INVALID_ID;
+    }
+
+    /* backward reference */
+    if (This->d3dPictureParam.wBackwardRefPictureIndex < This->surfaceCount)
+        params.backward_reference_picture = This->surfaces[This->d3dPictureParam.wBackwardRefPictureIndex];
+    else if (This->d3dPictureParam.wBackwardRefPictureIndex == 0xFFFF)
+        params.backward_reference_picture = VA_INVALID_ID;
+    else
+    {
+        FIXME("invalid backward surface reference index\n");
+        params.backward_reference_picture = VA_INVALID_ID;
+    }
+
+    /* picture_coding_type can have the following values :
+     *   1 - I Frame - not references to other images
+     *   2 - P Frame - reference to previous image
+     *   3 - B Frame - reference to previous and next image */
+    if (This->d3dPictureParam.bPicIntra)
+        params.picture_coding_type = 1;
+    else if (This->d3dPictureParam.bPicBackwardPrediction)
+        params.picture_coding_type = 3;
+    else
+        params.picture_coding_type = 2;
+
+    params.f_code = This->d3dPictureParam.wBitstreamFcodes;
+
+    /* see http://msdn.microsoft.com/en-us/library/windows/hardware/ff564012(v=vs.85).aspx */
+    params.picture_coding_extension.value = 0;
+    params.picture_coding_extension.bits.intra_dc_precision         = (This->d3dPictureParam.wBitstreamPCEelements >> 14) & 3;
+    params.picture_coding_extension.bits.picture_structure          = (This->d3dPictureParam.wBitstreamPCEelements >> 12) & 3;
+    params.picture_coding_extension.bits.top_field_first            = (This->d3dPictureParam.wBitstreamPCEelements >> 11) & 1;
+    params.picture_coding_extension.bits.frame_pred_frame_dct       = (This->d3dPictureParam.wBitstreamPCEelements >> 10) & 1;
+    params.picture_coding_extension.bits.concealment_motion_vectors = (This->d3dPictureParam.wBitstreamPCEelements >> 9) & 1;
+    params.picture_coding_extension.bits.q_scale_type               = (This->d3dPictureParam.wBitstreamPCEelements >> 8) & 1;
+    params.picture_coding_extension.bits.intra_vlc_format           = (This->d3dPictureParam.wBitstreamPCEelements >> 7) & 1;
+    params.picture_coding_extension.bits.alternate_scan             = (This->d3dPictureParam.wBitstreamPCEelements >> 6) & 1;
+    params.picture_coding_extension.bits.repeat_first_field         = (This->d3dPictureParam.wBitstreamPCEelements >> 5) & 1;
+    params.picture_coding_extension.bits.progressive_frame          = (This->d3dPictureParam.wBitstreamPCEelements >> 3) & 1;
+    params.picture_coding_extension.bits.is_first_field             = !This->d3dPictureParam.bSecondField;
+
+    status = pvaCreateBuffer(va_display, This->context, VAPictureParameterBufferType,
+                             sizeof(params), 1, &params, &vaPictureParam);
+    if (status != VA_STATUS_SUCCESS)
+    {
+        ERR("failed to create picture parameter buffer: %s (0x%x)\n", pvaErrorStr(status), status);
+        return E_FAIL;
+    }
+
+    /* vaRenderPicture is supposed to destroy the buffer */
+    status = pvaRenderPicture(va_display, This->context, &vaPictureParam, 1);
+    if (status == VA_STATUS_SUCCESS)
+        return S_OK;
+
+    ERR("failed to process picture parameter buffer: %s (0x%x)\n", pvaErrorStr(status), status);
+    pvaDestroyBuffer(va_display, vaPictureParam);
+    return E_FAIL;
+}
+
+/* caller has to hold the vaapi_lock */
+static HRESULT process_quantization_matrix( WineVideoDecoderMPEG2Impl *This, const DXVA2_DecodeBufferDesc *desc )
+{
+    VADisplay va_display = IWineVideoService_VADisplay(This->service);
+    VAIQMatrixBufferMPEG2 matrix;
+    VABufferID vaIQMatrix;
+    VAStatus status;
+    unsigned int i;
+
+    if (desc->DataSize != sizeof(This->d3dQMatrix))
+        FIXME("unexpected quantization matrix buffer size %u\n", desc->DataSize);
+
+    memset(&matrix, 0, sizeof(matrix));
+
+    matrix.load_intra_quantiser_matrix            = This->d3dQMatrix.bNewQmatrix[0];
+    matrix.load_non_intra_quantiser_matrix        = This->d3dQMatrix.bNewQmatrix[1];
+    matrix.load_chroma_intra_quantiser_matrix     = This->d3dQMatrix.bNewQmatrix[2];
+    matrix.load_chroma_non_intra_quantiser_matrix = This->d3dQMatrix.bNewQmatrix[3];
+
+    /* In theory we could just copy the matrix, but
+     * "The matrix consists of [...] unsigned words (in which only the lower 8 bits of each word are used [...])"
+     * (see: http://msdn.microsoft.com/en-us/library/windows/hardware/ff564034(v=vs.85).aspx) */
+    for (i = 0; i < 64; i++)
+    {
+        matrix.intra_quantiser_matrix[i]            = This->d3dQMatrix.Qmatrix[0][i];
+        matrix.non_intra_quantiser_matrix[i]        = This->d3dQMatrix.Qmatrix[1][i];
+        matrix.chroma_intra_quantiser_matrix[i]     = This->d3dQMatrix.Qmatrix[2][i];
+        matrix.chroma_non_intra_quantiser_matrix[i] = This->d3dQMatrix.Qmatrix[3][i];
+    }
+
+    status = pvaCreateBuffer(va_display, This->context, VAIQMatrixBufferType,
+                             sizeof(matrix), 1, &matrix, &vaIQMatrix);
+    if (status != VA_STATUS_SUCCESS)
+    {
+        ERR("failed to create quantization matrix buffer: %s (0x%x)\n", pvaErrorStr(status), status);
+        return E_FAIL;
+    }
+
+    /* vaRenderPicture is supposed to destroy the buffer */
+    status = pvaRenderPicture(va_display, This->context, &vaIQMatrix, 1);
+    if (status == VA_STATUS_SUCCESS)
+        return S_OK;
+
+    ERR("failed to process quantization matrix buffer: %s (0x%x)\n", pvaErrorStr(status), status);
+    pvaDestroyBuffer(va_display, vaIQMatrix);
+    return E_FAIL;
+}
+
+/* caller has to hold the vaapi_lock */
+static HRESULT process_slice_control_buffer( WineVideoDecoderMPEG2Impl *This, const DXVA2_DecodeBufferDesc *desc )
+{
+    VADisplay va_display = IWineVideoService_VADisplay(This->service);
+    VASliceParameterBufferMPEG2 *slice = NULL;
+    VABufferID vaSliceInfo;
+    VAStatus status;
+    unsigned int sliceCount, i;
+
+    /* check for valid parameters */
+    if (!desc->DataSize || desc->DataSize % sizeof(DXVA_SliceInfo) != 0)
+    {
+        ERR("slice control buffer size is invalid (got %u, expected multiple of %lu)\n",
+            desc->DataSize, (SIZE_T)sizeof(DXVA_SliceInfo));
+        return E_FAIL;
+    }
+
+    sliceCount = desc->DataSize / sizeof(DXVA_SliceInfo);
+
+    status = pvaCreateBuffer(va_display, This->context, VASliceParameterBufferType,
+                             sizeof(VASliceParameterBufferMPEG2), sliceCount, NULL, &vaSliceInfo);
+    if (status != VA_STATUS_SUCCESS)
+    {
+        ERR("failed to create slice control buffer: %s (0x%x)\n", pvaErrorStr(status), status);
+        return E_FAIL;
+    }
+
+    status = pvaMapBuffer(va_display, vaSliceInfo, (void **)&slice);
+    if (status != VA_STATUS_SUCCESS)
+    {
+        ERR("failed to map slice control buffer: %s (0x%x)\n", pvaErrorStr(status), status);
+        goto err;
+    }
+
+    for (i = 0; i < sliceCount; i++)
+    {
+        slice[i].slice_data_size    = This->d3dSliceInfo[i].dwSliceBitsInBuffer / 8;
+        slice[i].slice_data_offset  = This->d3dSliceInfo[i].dwSliceDataLocation;
+
+        switch (This->d3dSliceInfo[i].wBadSliceChopping)
+        {
+            case 0: slice[i].slice_data_flag = VA_SLICE_DATA_FLAG_ALL; break;
+            case 1: slice[i].slice_data_flag = VA_SLICE_DATA_FLAG_BEGIN; break;
+            case 2: slice[i].slice_data_flag = VA_SLICE_DATA_FLAG_END; break;
+            case 3: slice[i].slice_data_flag = VA_SLICE_DATA_FLAG_MIDDLE; break;
+            default:
+            {
+                ERR("invalid wBadSliceChopping value %d - assuming VA_SLICE_DATA_FLAG_ALL\n",
+                    This->d3dSliceInfo[i].wBadSliceChopping);
+                slice[i].slice_data_flag = VA_SLICE_DATA_FLAG_ALL;
+                break;
+            }
+        }
+
+        slice[i].macroblock_offset         = This->d3dSliceInfo[i].wMBbitOffset;
+        slice[i].slice_horizontal_position = This->d3dSliceInfo[i].wHorizontalPosition;
+        slice[i].slice_vertical_position   = This->d3dSliceInfo[i].wVerticalPosition;
+        slice[i].quantiser_scale_code      = This->d3dSliceInfo[i].wQuantizerScaleCode;
+        slice[i].intra_slice_flag          = (This->d3dSliceInfo[i].wMBbitOffset > 38); /* magic */
+    }
+
+    if (pvaUnmapBuffer(va_display, vaSliceInfo) != VA_STATUS_SUCCESS)
+        goto err;
+
+    /* vaRenderPicture is supposed to destroy the buffer */
+    status = pvaRenderPicture(va_display, This->context, &vaSliceInfo, 1);
+    if (status == VA_STATUS_SUCCESS)
+        return S_OK;
+
+    ERR("failed to process slice control buffer: %s (0x%x)\n", pvaErrorStr(status), status);
+
+err:
+    pvaDestroyBuffer(va_display, vaSliceInfo);
+    return E_FAIL;
+}
+
+/* caller has to hold the vaapi_lock */
+static HRESULT process_data_buffer( WineVideoDecoderMPEG2Impl *This, const DXVA2_DecodeBufferDesc *desc )
+{
+    VADisplay va_display = IWineVideoService_VADisplay(This->service);
+    VAStatus status;
+    HRESULT hr = E_FAIL;
+
+    if (This->vaBitstream == VA_INVALID_ID)
+        return E_FAIL;
+
+    /* vaRenderPicture is supposed to destroy the buffer */
+    status = pvaRenderPicture(va_display, This->context, &This->vaBitstream, 1);
+    if (status != VA_STATUS_SUCCESS)
+    {
+        ERR("failed to process slice buffer: %s (0x%x)\n", pvaErrorStr(status), status);
+        pvaDestroyBuffer(va_display, This->vaBitstream);
+    }
+    else hr = S_OK;
+
+    This->vaBitstream = VA_INVALID_ID;
+    return hr;
+}
+
+static inline WineVideoDecoderMPEG2Impl *impl_from_IWineVideoDecoder( IWineVideoDecoder *iface )
+{
+    return CONTAINING_RECORD(iface, WineVideoDecoderMPEG2Impl, IWineVideoDecoder_iface);
+}
+
+/*****************************************************************************
+ * IWineVideoDecoder interface
+ */
+
+static HRESULT WINAPI WineVideoDecoderMPEG2_QueryInterface( IWineVideoDecoder *iface, REFIID riid, void **ppv )
+{
+    WineVideoDecoderMPEG2Impl *This = impl_from_IWineVideoDecoder(iface);
+    TRACE("(%p/%p)->(%s, %p)\n", iface, This, debugstr_guid(riid), ppv);
+
+    *ppv = NULL;
+
+    if (IsEqualIID(riid, &IID_IUnknown) || IsEqualIID(riid, &IID_IWineVideoDecoder))
+        *ppv = &This->IWineVideoDecoder_iface;
+
+    if (*ppv)
+    {
+        IUnknown_AddRef((IUnknown *)(*ppv));
+        return S_OK;
+    }
+
+    FIXME("No interface for %s\n", debugstr_guid(riid));
+    return E_NOINTERFACE;
+}
+
+static ULONG WINAPI WineVideoDecoderMPEG2_AddRef( IWineVideoDecoder *iface )
+{
+    WineVideoDecoderMPEG2Impl *This = impl_from_IWineVideoDecoder(iface);
+    ULONG refCount = InterlockedIncrement(&This->refCount);
+
+    TRACE("(%p)->() AddRef from %d\n", This, refCount - 1);
+
+    return refCount;
+}
+
+static ULONG WINAPI WineVideoDecoderMPEG2_Release( IWineVideoDecoder *iface )
+{
+    WineVideoDecoderMPEG2Impl *This = impl_from_IWineVideoDecoder(iface);
+    ULONG refCount = InterlockedDecrement(&This->refCount);
+
+    TRACE("(%p)->() Release from %d\n", This, refCount + 1);
+
+    if (!refCount)
+    {
+        VADisplay va_display = IWineVideoService_VADisplay(This->service);
+        TRACE("Destroying\n");
+
+        vaapi_lock();
+
+        if (This->vaBitstream != VA_INVALID_ID)
+            pvaDestroyBuffer(va_display, This->vaBitstream);
+
+        pvaDestroySurfaces(va_display, This->surfaces, This->surfaceCount);
+        HeapFree(GetProcessHeap(), 0, This->surfaces);
+
+        pvaDestroyImage(va_display, This->vaImage.image_id);
+
+        pvaDestroyContext(va_display, This->context);
+        pvaDestroyConfig(va_display, This->config);
+
+        vaapi_unlock();
+
+        IWineVideoService_Release(This->service);
+        CoTaskMemFree(This);
+    }
+
+    return refCount;
+}
+
+static HRESULT WINAPI WineVideoDecoderMPEG2_LockBuffer( IWineVideoDecoder *iface, UINT type, void **buffer,
+                                                        UINT *size )
+{
+    WineVideoDecoderMPEG2Impl *This = impl_from_IWineVideoDecoder(iface);
+    VADisplay va_display = IWineVideoService_VADisplay(This->service);
+    VAStatus status;
+    HRESULT hr = E_FAIL;
+    void *buf;
+
+    TRACE("(%p, %u, %p, %p)\n", This, type, buffer, size);
+
+    if (type == DXVA2_PictureParametersBufferType)
+    {
+        *buffer = &This->d3dPictureParam;
+        *size = sizeof(This->d3dPictureParam);
+        return S_OK;
+    }
+    else if(type == DXVA2_InverseQuantizationMatrixBufferType)
+    {
+        *buffer = &This->d3dQMatrix;
+        *size = sizeof(This->d3dQMatrix);
+        return S_OK;
+    }
+    else if (type == DXVA2_SliceControlBufferType)
+    {
+        *buffer = &This->d3dSliceInfo;
+        *size = sizeof(This->d3dSliceInfo);
+        return S_OK;
+    }
+    else if (type != DXVA2_BitStreamDateBufferType)
+        return E_INVALIDARG;
+
+    vaapi_lock();
+
+    /* reuse existing vaSlice buffer if any */
+    if (This->vaBitstream == VA_INVALID_ID)
+    {
+        status = pvaCreateBuffer(va_display, This->context, VASliceDataBufferType,
+                                 This->maxSliceSize, 1, NULL, &This->vaBitstream);
+        if (status != VA_STATUS_SUCCESS)
+        {
+            ERR("failed to create slice buffer: %s (0x%x)\n", pvaErrorStr(status), status);
+            goto out;
+        }
+    }
+
+    status = pvaMapBuffer(va_display, This->vaBitstream, &buf);
+    if (status != VA_STATUS_SUCCESS)
+    {
+        ERR("failed to map slice buffer: %s (0x%x)\n", pvaErrorStr(status), status);
+    }
+    else
+    {
+        *buffer = buf;
+        *size   = This->maxSliceSize;
+        hr = S_OK;
+    }
+
+out:
+    vaapi_unlock();
+    return hr;
+}
+
+static HRESULT WINAPI WineVideoDecoderMPEG2_UnlockBuffer( IWineVideoDecoder *iface, UINT type )
+{
+    WineVideoDecoderMPEG2Impl *This = impl_from_IWineVideoDecoder(iface);
+    VADisplay va_display = IWineVideoService_VADisplay(This->service);
+    VAStatus status;
+    HRESULT hr = E_FAIL;
+
+    TRACE("(%p, %u,)\n", This, type);
+
+    if (type == DXVA2_PictureParametersBufferType ||
+        type == DXVA2_InverseQuantizationMatrixBufferType ||
+        type == DXVA2_SliceControlBufferType)
+    {
+        return S_OK;
+    }
+    else if (type != DXVA2_BitStreamDateBufferType)
+        return E_INVALIDARG;
+
+    vaapi_lock();
+
+    if (This->vaBitstream == VA_INVALID_ID)
+    {
+        ERR("no slice buffer allocated\n");
+        goto out;
+    }
+
+    status = pvaUnmapBuffer(va_display, This->vaBitstream);
+    if (status != VA_STATUS_SUCCESS)
+    {
+        ERR("failed to unmap slice buffer: %s (0x%x)\n", pvaErrorStr(status), status);
+    }
+    else hr = S_OK;
+
+out:
+    vaapi_unlock();
+    return hr;
+}
+
+static HRESULT WINAPI WineVideoDecoderMPEG2_ExecuteBuffer( IWineVideoDecoder *iface, DXVA2_DecodeBufferDesc *pictureParam,
+        DXVA2_DecodeBufferDesc *qMatrix, DXVA2_DecodeBufferDesc *sliceInfo, DXVA2_DecodeBufferDesc *bitStream )
+{
+    WineVideoDecoderMPEG2Impl *This = impl_from_IWineVideoDecoder(iface);
+    HRESULT hr;
+
+    TRACE("(%p, %p, %p, %p, %p)\n", This, pictureParam, qMatrix, sliceInfo, bitStream);
+
+    if (!pictureParam || !qMatrix || !sliceInfo || !bitStream)
+    {
+        FIXME("not enough buffers to decode picture\n");
+        return E_FAIL;
+    }
+
+    vaapi_lock();
+
+    hr = process_picture_parameters(This, pictureParam);
+
+    if (SUCCEEDED(hr))
+        hr = process_quantization_matrix(This, qMatrix);
+
+    if (SUCCEEDED(hr))
+        hr = process_slice_control_buffer(This, sliceInfo);
+
+    if (SUCCEEDED(hr))
+        hr = process_data_buffer(This, bitStream);
+
+    vaapi_unlock();
+    return hr;
+}
+
+static HRESULT WINAPI WineVideoDecoderMPEG2_BeginFrame( IWineVideoDecoder *iface, UINT surfaceIndex )
+{
+    WineVideoDecoderMPEG2Impl *This = impl_from_IWineVideoDecoder(iface);
+    VADisplay va_display = IWineVideoService_VADisplay(This->service);
+    VAStatus status;
+    HRESULT hr = E_FAIL;
+
+    TRACE("(%p, %d)\n", This, surfaceIndex);
+
+    if (surfaceIndex >= This->surfaceCount)
+        return E_INVALIDARG;
+
+    vaapi_lock();
+
+    status = pvaBeginPicture(va_display, This->context, This->surfaces[surfaceIndex]);
+    if (status != VA_STATUS_SUCCESS)
+    {
+        ERR("failed to begin picture: %s (0x%x)\n", pvaErrorStr(status), status);
+    }
+    else
+    {
+        This->currentSurface = surfaceIndex;
+        hr = S_OK;
+    }
+
+    vaapi_unlock();
+    return hr;
+}
+
+static HRESULT WINAPI WineVideoDecoderMPEG2_EndFrame( IWineVideoDecoder *iface )
+{
+    WineVideoDecoderMPEG2Impl *This = impl_from_IWineVideoDecoder(iface);
+    VADisplay va_display = IWineVideoService_VADisplay(This->service);
+    VAStatus status;
+    HRESULT hr = E_FAIL;
+
+    TRACE("(%p)\n", This);
+
+    vaapi_lock();
+
+    status = pvaEndPicture(va_display, This->context);
+    if (status != VA_STATUS_SUCCESS)
+    {
+        ERR("ending picture failed: %s (0x%x)\n", pvaErrorStr(status), status);
+    }
+    else hr = S_OK;
+
+    vaapi_unlock();
+    return hr;
+}
+
+static HRESULT WINAPI WineVideoDecoderMPEG2_LockImage( IWineVideoDecoder *iface, WineVideoImage *image )
+{
+    WineVideoDecoderMPEG2Impl *This = impl_from_IWineVideoDecoder(iface);
+    VADisplay va_display = IWineVideoService_VADisplay(This->service);
+    VAStatus status;
+    HRESULT hr = E_FAIL;
+
+    TRACE("(%p, %p)\n", This, image);
+
+    vaapi_lock();
+
+    pvaSyncSurface(va_display, This->surfaces[This->currentSurface]);
+
+    status = pvaGetImage(va_display, This->surfaces[This->currentSurface], 0, 0,
+                         This->width, This->height, This->vaImage.image_id);
+    if (status != VA_STATUS_SUCCESS)
+    {
+        ERR("failed to get image: %s (0x%x)\n", pvaErrorStr(status), status);
+        goto out;
+    }
+
+    status = pvaMapBuffer(va_display, This->vaImage.buf, &image->buffer);
+    if (status != VA_STATUS_SUCCESS)
+    {
+        ERR("failed to map image buffer: %s (0x%x)\n", pvaErrorStr(status), status);
+    }
+    else
+    {
+        image->format     = This->format;
+        image->width      = This->vaImage.width;
+        image->height     = This->vaImage.height;
+        image->planeCount = This->vaImage.num_planes;
+        image->offsets    = This->vaImage.offsets;
+        image->pitches    = This->vaImage.pitches;
+        hr = S_OK;
+    }
+
+out:
+    vaapi_unlock();
+    return hr;
+}
+
+static HRESULT WINAPI WineVideoDecoderMPEG2_UnlockImage( IWineVideoDecoder *iface )
+{
+    WineVideoDecoderMPEG2Impl *This = impl_from_IWineVideoDecoder(iface);
+    VADisplay va_display = IWineVideoService_VADisplay(This->service);
+    VAStatus status;
+    HRESULT hr = E_FAIL;
+
+    TRACE("(%p)\n", This);
+
+    vaapi_lock();
+
+    status = pvaUnmapBuffer(va_display, This->vaImage.buf);
+    if (status != VA_STATUS_SUCCESS)
+    {
+        ERR("failed to unmap image buffer: %s (0x%x)\n", pvaErrorStr(status), status);
+    }
+    else hr = S_OK;
+
+    vaapi_unlock();
+    return hr;
+}
+
+static const IWineVideoDecoderVtbl WineVideoDecoderMPEG2_VTable =
+{
+    WineVideoDecoderMPEG2_QueryInterface,
+    WineVideoDecoderMPEG2_AddRef,
+    WineVideoDecoderMPEG2_Release,
+    WineVideoDecoderMPEG2_LockBuffer,
+    WineVideoDecoderMPEG2_UnlockBuffer,
+    WineVideoDecoderMPEG2_ExecuteBuffer,
+    WineVideoDecoderMPEG2_BeginFrame,
+    WineVideoDecoderMPEG2_EndFrame,
+    WineVideoDecoderMPEG2_LockImage,
+    WineVideoDecoderMPEG2_UnlockImage
+};
+
+HRESULT vaapi_mpeg2decoder_create( IWineVideoService *service, const DXVA2_VideoDesc *videoDesc,
+                                   DXVA2_ConfigPictureDecode *config, UINT numSurfaces, IWineVideoDecoder **decoder )
+{
+    struct vaapi_format *format;
+    struct vaapi_profile *profile;
+    WineVideoDecoderMPEG2Impl *mpeg2decoder;
+    VAConfigAttrib codecAttrib;
+    VADisplay va_display;
+    VAStatus status;
+
+    if (!service || !videoDesc || !config || !decoder)
+        return E_INVALIDARG;
+
+    va_display = IWineVideoService_VADisplay(service);
+    *decoder = NULL;
+
+    /* MPEG2 B frames can reference up to 2 previously decoded frames */
+    if (numSurfaces < 3)
+        WARN("decoder initialized with less than 3 frames\n");
+
+    format  = vaapi_lookup_d3dformat(videoDesc->Format);
+    profile = vaapi_lookup_guid(&DXVA2_ModeMPEG2_VLD);
+    if (!format || !profile)
+        return E_INVALIDARG;
+
+    if (!vaapi_is_format_supported(va_display, profile, format))
+        return E_INVALIDARG;
+
+    if (videoDesc->InputSampleFreq.Numerator * videoDesc->OutputFrameFreq.Denominator !=
+        videoDesc->OutputFrameFreq.Numerator * videoDesc->InputSampleFreq.Denominator)
+    {
+        FIXME("changing the framerate is not supported\n");
+        return E_INVALIDARG;
+    }
+
+    mpeg2decoder = CoTaskMemAlloc(sizeof(*mpeg2decoder));
+    if (!mpeg2decoder)
+        return E_OUTOFMEMORY;
+
+    memset(mpeg2decoder, 0, sizeof(*mpeg2decoder));
+
+    mpeg2decoder->IWineVideoDecoder_iface.lpVtbl = &WineVideoDecoderMPEG2_VTable;
+    mpeg2decoder->refCount          = 1;
+    mpeg2decoder->service           = service;
+
+    mpeg2decoder->width             = videoDesc->SampleWidth;
+    mpeg2decoder->height            = videoDesc->SampleHeight;
+    mpeg2decoder->format            = videoDesc->Format;
+    mpeg2decoder->maxSliceSize      = estimate_maximum_slice_size(videoDesc->SampleWidth, videoDesc->SampleHeight);
+    memset(&mpeg2decoder->vaImage, 0, sizeof(mpeg2decoder->vaImage));
+    mpeg2decoder->vaImage.image_id  = VA_INVALID_ID;
+
+    mpeg2decoder->surfaceCount      = numSurfaces;
+    mpeg2decoder->surfaces          = NULL;
+    mpeg2decoder->currentSurface    = 0;
+
+    mpeg2decoder->config            = 0;
+    mpeg2decoder->context           = 0;
+
+    mpeg2decoder->vaBitstream       = VA_INVALID_ID;
+
+    vaapi_lock();
+
+    codecAttrib.type  = VAConfigAttribRTFormat;
+    codecAttrib.value = format->vaformat;
+
+    status = pvaCreateConfig(va_display, profile->profile, profile->entryPoint,
+                             &codecAttrib, 1, &mpeg2decoder->config);
+    if (status != VA_STATUS_SUCCESS)
+    {
+        ERR("failed to create decoder config: %s (0x%x)\n", pvaErrorStr(status), status);
+        goto err;
+    }
+
+    if (!vaapi_create_surfaces(va_display, format, mpeg2decoder->width, mpeg2decoder->height,
+                               &mpeg2decoder->vaImage, numSurfaces, &mpeg2decoder->surfaces))
+    {
+        ERR("Failed to create image or surfaces\n");
+        goto err;
+    }
+
+    status = pvaCreateContext(va_display, mpeg2decoder->config, mpeg2decoder->width, mpeg2decoder->height,
+                              VA_PROGRESSIVE, mpeg2decoder->surfaces, numSurfaces, &mpeg2decoder->context);
+    if (status != VA_STATUS_SUCCESS)
+    {
+        ERR("failed to create context: %s (0x%x)\n", pvaErrorStr(status), status);
+        goto err;
+    }
+
+    vaapi_unlock();
+
+    IWineVideoService_AddRef(service);
+
+    *decoder = &mpeg2decoder->IWineVideoDecoder_iface;
+    return S_OK;
+
+err:
+     if (mpeg2decoder->surfaces)
+    {
+        pvaDestroySurfaces(va_display, mpeg2decoder->surfaces, mpeg2decoder->surfaceCount);
+        HeapFree(GetProcessHeap(), 0, mpeg2decoder->surfaces);
+    }
+
+    if (mpeg2decoder->vaImage.image_id != VA_INVALID_ID)
+        pvaDestroyImage(va_display, mpeg2decoder->vaImage.image_id);
+
+    if (mpeg2decoder->config)
+        pvaDestroyConfig(va_display, mpeg2decoder->config);
+
+    vaapi_unlock();
+    CoTaskMemFree(mpeg2decoder);
+    return E_FAIL;
+}
+
+#endif /* HAVE_VAAPI */
diff --git a/dlls/dxva2/vaapi.c b/dlls/dxva2/vaapi.c
new file mode 100644
index 0000000..80e63bf
--- /dev/null
+++ b/dlls/dxva2/vaapi.c
@@ -0,0 +1,767 @@
+/*
+ * Copyright 2014-2015 Michael Müller for Pipelight
+ * Copyright 2015 Sebastian Lackner for Pipelight
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2.1 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA
+ */
+
+#include "config.h"
+#include "wine/port.h"
+#include <stdarg.h>
+#include <assert.h>
+#include "windef.h"
+#include "winbase.h"
+
+#include "wine/library.h"
+#include "wine/debug.h"
+
+#define COBJMACROS
+#include "d3d9.h"
+#include "dxva2api.h"
+#include "dxva.h"
+#include "dxva2_private.h"
+
+WINE_DEFAULT_DEBUG_CHANNEL(dxva2);
+
+#ifdef HAVE_VAAPI
+
+#define MAKE_FUNCPTR(f) typeof(f) * p##f = NULL
+MAKE_FUNCPTR(XCloseDisplay);
+MAKE_FUNCPTR(XOpenDisplay);
+MAKE_FUNCPTR(vaBeginPicture);
+MAKE_FUNCPTR(vaCreateBuffer);
+MAKE_FUNCPTR(vaCreateConfig);
+MAKE_FUNCPTR(vaCreateContext);
+MAKE_FUNCPTR(vaCreateImage);
+MAKE_FUNCPTR(vaCreateSurfaces);
+MAKE_FUNCPTR(vaDestroyBuffer);
+MAKE_FUNCPTR(vaDestroyConfig);
+MAKE_FUNCPTR(vaDestroyContext);
+MAKE_FUNCPTR(vaDestroyImage);
+MAKE_FUNCPTR(vaDestroySurfaces);
+MAKE_FUNCPTR(vaEndPicture);
+MAKE_FUNCPTR(vaErrorStr);
+MAKE_FUNCPTR(vaGetConfigAttributes);
+MAKE_FUNCPTR(vaGetDisplay);
+MAKE_FUNCPTR(vaGetImage);
+MAKE_FUNCPTR(vaInitialize);
+MAKE_FUNCPTR(vaMapBuffer);
+MAKE_FUNCPTR(vaMaxNumEntrypoints);
+MAKE_FUNCPTR(vaMaxNumProfiles);
+MAKE_FUNCPTR(vaQueryConfigEntrypoints);
+MAKE_FUNCPTR(vaQueryConfigProfiles);
+MAKE_FUNCPTR(vaQuerySurfaceAttributes);
+MAKE_FUNCPTR(vaRenderPicture);
+MAKE_FUNCPTR(vaSyncSurface);
+MAKE_FUNCPTR(vaTerminate);
+MAKE_FUNCPTR(vaUnmapBuffer);
+#undef MAKE_FUNCPTR
+
+#define LOAD_FUNCPTR(f) \
+    if(!(p##f = wine_dlsym(handle, #f, NULL, 0))) \
+    { \
+        WARN("Can't find symbol %s.\n", #f); \
+        goto error; \
+    }
+
+static void *load_libva( void )
+{
+    void *handle = wine_dlopen(SONAME_LIBVA, RTLD_NOW, NULL, 0);
+    if (!handle)
+    {
+        FIXME("Wine cannot find the %s library.\n", SONAME_LIBVA);
+        return FALSE;
+    }
+
+    LOAD_FUNCPTR(vaBeginPicture);
+    LOAD_FUNCPTR(vaCreateBuffer);
+    LOAD_FUNCPTR(vaCreateConfig);
+    LOAD_FUNCPTR(vaCreateContext);
+    LOAD_FUNCPTR(vaCreateImage);
+    LOAD_FUNCPTR(vaCreateSurfaces);
+    LOAD_FUNCPTR(vaDestroyBuffer);
+    LOAD_FUNCPTR(vaDestroyConfig);
+    LOAD_FUNCPTR(vaDestroyContext);
+    LOAD_FUNCPTR(vaDestroyImage);
+    LOAD_FUNCPTR(vaDestroySurfaces);
+    LOAD_FUNCPTR(vaEndPicture);
+    LOAD_FUNCPTR(vaErrorStr);
+    LOAD_FUNCPTR(vaGetConfigAttributes);
+    LOAD_FUNCPTR(vaGetImage);
+    LOAD_FUNCPTR(vaInitialize);
+    LOAD_FUNCPTR(vaMapBuffer);
+    LOAD_FUNCPTR(vaMaxNumEntrypoints);
+    LOAD_FUNCPTR(vaMaxNumProfiles);
+    LOAD_FUNCPTR(vaQueryConfigEntrypoints);
+    LOAD_FUNCPTR(vaQueryConfigProfiles);
+    LOAD_FUNCPTR(vaQuerySurfaceAttributes);
+    LOAD_FUNCPTR(vaRenderPicture);
+    LOAD_FUNCPTR(vaSyncSurface);
+    LOAD_FUNCPTR(vaTerminate);
+    LOAD_FUNCPTR(vaUnmapBuffer);
+    return handle;
+
+error:
+    wine_dlclose(handle, NULL, 0);
+    return NULL;
+}
+
+static void *load_libva_x11( void )
+{
+    void *handle = wine_dlopen(SONAME_LIBVA_X11, RTLD_NOW, NULL, 0);
+    if (!handle)
+    {
+        FIXME("Wine cannot find the %s library.\n", SONAME_LIBVA_X11);
+        return FALSE;
+    }
+
+    LOAD_FUNCPTR(vaGetDisplay);
+    return handle;
+
+error:
+    wine_dlclose(handle, NULL, 0);
+    return NULL;
+}
+
+static void *load_libx11( void )
+{
+    void *handle = wine_dlopen(SONAME_LIBX11, RTLD_NOW, NULL, 0);
+    if (!handle)
+    {
+        FIXME("Wine cannot find the %s library.\n", SONAME_LIBX11);
+        return FALSE;
+    }
+
+    LOAD_FUNCPTR(XCloseDisplay);
+    LOAD_FUNCPTR(XOpenDisplay);
+    return handle;
+
+error:
+    wine_dlclose(handle, NULL, 0);
+    return NULL;
+}
+
+#undef LOAD_FUNCPTR
+
+static CRITICAL_SECTION vaapi_section;
+static CRITICAL_SECTION_DEBUG vaapi_section_debug =
+{
+    0, 0, &vaapi_section,
+    {&vaapi_section_debug.ProcessLocksList, &vaapi_section_debug.ProcessLocksList},
+      0, 0, {(DWORD_PTR)(__FILE__ ": vaapi_section")}
+};
+static CRITICAL_SECTION vaapi_section = {&vaapi_section_debug, -1, 0, 0, 0, 0};
+
+static WineVideoServiceImpl *vaapi_videoservice = NULL;
+
+void vaapi_lock( void )
+{
+    EnterCriticalSection(&vaapi_section);
+}
+
+void vaapi_unlock( void )
+{
+    LeaveCriticalSection(&vaapi_section);
+}
+
+static struct vaapi_profile profile_table[] =
+{
+    /* MPEG2 */
+    {VAProfileMPEG2Main,            VAEntrypointVLD,    &DXVA2_ModeMPEG2_VLD},
+    {VAProfileMPEG2Main,            VAEntrypointMoComp, &DXVA2_ModeMPEG2_MoComp},
+    {VAProfileMPEG2Main,            VAEntrypointIDCT,   &DXVA2_ModeMPEG2_IDCT},
+
+    /* VC1 */
+    {VAProfileVC1Advanced,          VAEntrypointVLD,    &DXVA2_ModeVC1_VLD},
+    {VAProfileVC1Advanced,          VAEntrypointIDCT,   &DXVA2_ModeVC1_IDCT},
+    {VAProfileVC1Advanced,          VAEntrypointMoComp, &DXVA2_ModeVC1_MoComp},
+
+    /* H264 */
+    {VAProfileH264High,             VAEntrypointVLD,    &DXVA2_ModeH264_E},
+    {VAProfileH264High,             VAEntrypointIDCT,   &DXVA2_ModeH264_C},
+    {VAProfileH264High,             VAEntrypointMoComp, &DXVA2_ModeH264_A},
+
+    /* TODO: MPEG4, ... */
+};
+
+struct vaapi_profile *vaapi_lookup_profile( int profile, int entryPoint )
+{
+    unsigned int i;
+    for (i = 0; i < sizeof(profile_table)/sizeof(profile_table[0]); i++)
+    {
+        if (profile_table[i].profile == profile && profile_table[i].entryPoint == entryPoint)
+            return &profile_table[i];
+    }
+    return NULL;
+}
+
+struct vaapi_profile *vaapi_lookup_guid( const GUID *guid )
+{
+    unsigned int i;
+    for (i = 0; i < sizeof(profile_table)/sizeof(profile_table[0]); i++)
+    {
+        if (IsEqualGUID(profile_table[i].guid, guid))
+            return &profile_table[i];
+    }
+    return NULL;
+}
+
+static struct vaapi_format format_table[] =
+{
+    {MAKEFOURCC('N','V','1','1'), VA_FOURCC_NV11, VA_RT_FORMAT_YUV420, FALSE, 12},
+    {MAKEFOURCC('N','V','1','2'), VA_FOURCC_NV12, VA_RT_FORMAT_YUV420, TRUE , 12},
+    {MAKEFOURCC('A','I','4','4'), VA_FOURCC_AI44, 0,                   FALSE,  0}, /* TODO */
+    {0,                           VA_FOURCC_RGBA, VA_RT_FORMAT_RGB32,  FALSE, 32}, /* TODO */
+    {0,                           VA_FOURCC_RGBX, VA_RT_FORMAT_RGB32,  FALSE, 32}, /* TODO */
+    {0,                           VA_FOURCC_BGRA, VA_RT_FORMAT_RGB32,  FALSE, 32}, /* TODO */
+    {0,                           VA_FOURCC_BGRX, VA_RT_FORMAT_RGB32,  FALSE, 32}, /* TODO */
+    {D3DFMT_A8R8G8B8,             VA_FOURCC_ARGB, VA_RT_FORMAT_RGB32,  FALSE, 32},
+    {D3DFMT_X8R8G8B8,             VA_FOURCC_XRGB, VA_RT_FORMAT_RGB32,  FALSE, 32},
+    {D3DFMT_UYVY,                 VA_FOURCC_UYVY, VA_RT_FORMAT_YUV422, FALSE, 16},
+    {D3DFMT_YUY2,                 VA_FOURCC_YUY2, VA_RT_FORMAT_YUV422, FALSE, 16},
+    {MAKEFOURCC('A','Y','U','V'), VA_FOURCC_AYUV, VA_RT_FORMAT_YUV444, FALSE, 32},
+    {MAKEFOURCC('Y','V','1','2'), VA_FOURCC_YV12, VA_RT_FORMAT_YUV420, FALSE, 12},
+    {0,                           VA_FOURCC_P208, 0,                   FALSE,  0}, /* TODO */
+    {MAKEFOURCC('I','Y','U','V'), VA_FOURCC_IYUV, VA_RT_FORMAT_YUV411, FALSE, 12},
+    {MAKEFOURCC('Y','V','2','4'), VA_FOURCC_YV24, VA_RT_FORMAT_YUV444, FALSE, 24}, /* TODO */
+    {0,                           VA_FOURCC_YV32, 0,                   FALSE, 32}, /* TODO */
+    {0,                           VA_FOURCC_Y800, 0,                   FALSE,  8}, /* TODO */
+    {MAKEFOURCC('I','M','C','3'), VA_FOURCC_IMC3, 0,                   FALSE, 12},
+    {0,                           VA_FOURCC_411P, 0,                   FALSE, 12}, /* TODO */
+    {0,                           VA_FOURCC_422H, VA_RT_FORMAT_YUV422, FALSE, 16}, /* TODO */
+    {0,                           VA_FOURCC_422V, VA_RT_FORMAT_YUV422, FALSE, 16}, /* TODO */
+    {0,                           VA_FOURCC_444P, 0,                   FALSE, 24}, /* TODO */
+    {0,                           VA_FOURCC_RGBP, VA_RT_FORMAT_RGBP,   FALSE, 16}, /* TODO */
+    {0,                           VA_FOURCC_BGRP, VA_RT_FORMAT_RGBP,   FALSE, 16}, /* TODO */
+    {0,                           VA_FOURCC_411R, 0,                   FALSE, 12}, /* TODO */
+};
+
+struct vaapi_format *vaapi_lookup_d3dformat( D3DFORMAT d3dformat )
+{
+    unsigned int i;
+    for (i = 0; i < sizeof(format_table)/sizeof(format_table[0]); i++)
+    {
+        if (format_table[i].d3dformat == d3dformat)
+            return &format_table[i];
+    }
+    return NULL;
+}
+
+
+/* caller has to hold the vaapi_lock */
+static BOOL enumerate_vaprofiles( VADisplay va_display )
+{
+    int numProfiles, numEntryPoints;
+    VAEntrypoint *entryPoints = NULL;
+    VAProfile *profiles;
+    unsigned int i, j;
+
+    for (i = 0; i < sizeof(profile_table)/sizeof(profile_table[0]); i++)
+        profile_table[i].supported = FALSE;
+
+    numProfiles    = pvaMaxNumProfiles(va_display);
+    numEntryPoints = pvaMaxNumEntrypoints(va_display);
+
+    if (!numProfiles || !numEntryPoints)
+        return FALSE;
+
+    profiles = HeapAlloc(GetProcessHeap(), 0, sizeof(VAProfile) * numProfiles);
+    if (!profiles)
+        return FALSE;
+
+    entryPoints = HeapAlloc(GetProcessHeap(), 0, sizeof(VAEntrypoint) * numEntryPoints);
+    if (!entryPoints)
+    {
+        HeapFree(GetProcessHeap(), 0, profiles);
+        return FALSE;
+    }
+
+    if (pvaQueryConfigProfiles(va_display, profiles, &numProfiles) != VA_STATUS_SUCCESS)
+    {
+        HeapFree(GetProcessHeap(), 0, profiles);
+        HeapFree(GetProcessHeap(), 0, entryPoints);
+        return FALSE;
+    }
+
+    for (i = 0; i < numProfiles; i++)
+    {
+        if (pvaQueryConfigEntrypoints(va_display, profiles[i], entryPoints, &numEntryPoints) != VA_STATUS_SUCCESS)
+            continue;
+
+        for (j = 0; j < numEntryPoints; j++)
+        {
+            struct vaapi_profile *profile = vaapi_lookup_profile(profiles[i], entryPoints[j]);
+            if (profile)
+                profile->supported = TRUE;
+            else
+                WARN("missing decoder translation for format %d/%d\n", profiles[i], entryPoints[i]);
+        }
+    }
+
+    HeapFree(GetProcessHeap(), 0, profiles);
+    HeapFree(GetProcessHeap(), 0, entryPoints);
+    return TRUE;
+}
+
+/* caller has to hold the vaapi_lock */
+BOOL vaapi_is_format_supported( VADisplay va_display, struct vaapi_profile *profile, struct vaapi_format *format )
+{
+    unsigned int numSurfaceAttribs = 0;
+    VASurfaceAttrib *surfaceAttribs;
+    VAConfigAttrib attrib;
+    VAConfigID config;
+    VAStatus status;
+    unsigned int i;
+    BOOL ret = FALSE;
+
+    attrib.type  = VAConfigAttribRTFormat;
+    attrib.value = format->vaformat;
+    if (pvaCreateConfig(va_display, profile->profile, profile->entryPoint, &attrib, 1, &config) != VA_STATUS_SUCCESS)
+        return FALSE;
+
+    status = pvaQuerySurfaceAttributes(va_display, config, NULL, &numSurfaceAttribs);
+    if (status == VA_STATUS_ERROR_UNIMPLEMENTED ||
+        status == VA_STATUS_ERROR_INVALID_PARAMETER)
+    {
+        pvaDestroyConfig(va_display, config);
+        return format->fallback;
+    }
+
+    if (status == VA_STATUS_SUCCESS && numSurfaceAttribs != 0)
+    {
+        surfaceAttribs = HeapAlloc(GetProcessHeap(), 0, sizeof(VASurfaceAttrib) * numSurfaceAttribs);
+        if (surfaceAttribs)
+        {
+            if (pvaQuerySurfaceAttributes(va_display, config, surfaceAttribs, &numSurfaceAttribs) == VA_STATUS_SUCCESS)
+            {
+                for (i = 0; i < numSurfaceAttribs; i++)
+                {
+                    if (surfaceAttribs[i].type == VASurfaceAttribPixelFormat &&
+                        surfaceAttribs[i].value.value.i == format->vafourcc)
+                    {
+                        ret = TRUE;
+                        break;
+                    }
+                }
+            }
+            HeapFree(GetProcessHeap(), 0, surfaceAttribs);
+        }
+    }
+
+    pvaDestroyConfig(va_display, config);
+    return ret;
+}
+
+/* caller has to hold the vaapi_lock */
+BOOL vaapi_create_surfaces( VADisplay va_display, const struct vaapi_format *format, unsigned int width,
+                            unsigned int height, VAImage *vaImage, UINT numSurfaces, VASurfaceID **surfaceList )
+{
+    VASurfaceAttrib surfaceAttrib;
+    VAImageFormat imageFormat;
+    VASurfaceID *surfaces;
+    VAStatus status;
+
+    memset(&imageFormat, 0, sizeof(imageFormat));
+    imageFormat.fourcc         = format->vafourcc;
+    imageFormat.byte_order     = VA_LSB_FIRST;
+    imageFormat.bits_per_pixel = format->bits;
+
+    status = pvaCreateImage(va_display, &imageFormat, width, height, vaImage);
+    if (status != VA_STATUS_SUCCESS)
+    {
+        ERR("failed to create image: %s (0x%x)\n", pvaErrorStr(status), status);
+        return FALSE;
+    }
+
+    surfaces = HeapAlloc(GetProcessHeap(), 0, sizeof(VASurfaceID) * numSurfaces);
+    if (!surfaces)
+    {
+        pvaDestroyImage(va_display, vaImage->image_id);
+        vaImage->image_id = VA_INVALID_ID;
+        return FALSE;
+    }
+
+    /* TODO: round width/height values ? */
+    surfaceAttrib.type  = VASurfaceAttribPixelFormat;
+    surfaceAttrib.flags = VA_SURFACE_ATTRIB_SETTABLE;
+    surfaceAttrib.value.value.i = format->vafourcc;
+
+    TRACE("format=%x width=%u height=%u num=%u\n", format->d3dformat, width, height, numSurfaces);
+    status = pvaCreateSurfaces(va_display, format->vaformat, width, height,
+                               surfaces, numSurfaces, &surfaceAttrib, 1);
+    if (status != VA_STATUS_SUCCESS)
+    {
+        /* The vdpau backend doesn't seem to allow setting the surface picture type.
+         * We can only hope that it matches the expected one. */
+        status = pvaCreateSurfaces(va_display, format->vaformat, width, height,
+                                   surfaces, numSurfaces, NULL, 0);
+    }
+
+    if (status == VA_STATUS_SUCCESS)
+    {
+        *surfaceList = surfaces;
+        return TRUE;
+    }
+
+    ERR("failed to create surfaces: %s (0x%x)\n", pvaErrorStr(status), status);
+    HeapFree(GetProcessHeap(), 0, surfaces);
+    pvaDestroyImage(va_display, vaImage->image_id);
+    vaImage->image_id = VA_INVALID_ID;
+    return FALSE;
+}
+
+/*****************************************************************************
+ * IWineVideoService vaapi interface
+ */
+
+static HRESULT WINAPI WineVideoService_QueryInterface( IWineVideoService *iface, REFIID riid, LPVOID *ppv )
+{
+    WineVideoServiceImpl *This = impl_from_IWineVideoService(iface);
+    TRACE("(%p/%p)->(%s, %p)\n", iface, This, debugstr_guid(riid), ppv);
+
+    *ppv = NULL;
+
+    if (IsEqualIID(riid, &IID_IUnknown) || IsEqualIID(riid, &IID_IWineVideoService))
+        *ppv = &This->IWineVideoService_iface;
+
+    if (*ppv)
+    {
+        IUnknown_AddRef((IUnknown *)(*ppv));
+        return S_OK;
+    }
+
+    FIXME("No interface for %s\n", debugstr_guid(riid));
+    return E_NOINTERFACE;
+}
+
+static ULONG WINAPI WineVideoService_AddRef( IWineVideoService *iface )
+{
+    WineVideoServiceImpl *This = impl_from_IWineVideoService(iface);
+    ULONG refCount = InterlockedIncrement(&This->refCount);
+
+    TRACE("(%p)->() AddRef from %d\n", This, refCount - 1);
+
+    return refCount;
+}
+
+static ULONG WINAPI WineVideoService_Release( IWineVideoService *iface )
+{
+    WineVideoServiceImpl *This = impl_from_IWineVideoService(iface);
+    ULONG refCount;
+
+    vaapi_lock();
+    refCount = InterlockedDecrement(&This->refCount);
+    if (!refCount)
+    {
+        assert(vaapi_videoservice == This);
+        vaapi_videoservice = NULL;
+    }
+    vaapi_unlock();
+
+    TRACE("(%p)->() Release from %d\n", This, refCount + 1);
+
+    if (!refCount)
+    {
+        TRACE("Destroying\n");
+
+        vaapi_lock();
+        pvaTerminate(This->va_display);
+        vaapi_unlock();
+
+        pXCloseDisplay(This->x11_display);
+
+        wine_dlclose(This->x11_handle, NULL, 0);
+        wine_dlclose(This->va_x11_handle, NULL, 0);
+        wine_dlclose(This->va_handle, NULL, 0);
+
+        CoTaskMemFree(This);
+    }
+
+    return refCount;
+}
+
+static HRESULT WINAPI WineVideoService_GetDecoderDeviceGuids(IWineVideoService *iface, UINT *count, GUID **guids)
+{
+    WineVideoServiceImpl *This = impl_from_IWineVideoService(iface);
+    unsigned int i, numProfiles = 0;
+    GUID *buf;
+
+    TRACE("(%p, %p, %p)\n", This, count, guids);
+
+    buf = CoTaskMemAlloc(sizeof(GUID) * (sizeof(profile_table)/sizeof(profile_table[0])));
+    if (!buf)
+        return E_OUTOFMEMORY;
+
+    for (i = 0; i < sizeof(profile_table)/sizeof(profile_table[0]); i++)
+    {
+        if (profile_table[i].supported)
+            memcpy(&buf[numProfiles++], profile_table[i].guid, sizeof(GUID));
+    }
+
+    if (!numProfiles)
+    {
+        CoTaskMemFree(buf);
+        return E_FAIL;
+    }
+
+    *count = numProfiles;
+    *guids = buf;
+    return S_OK;
+}
+
+static HRESULT WINAPI WineVideoService_GetDecoderRenderTargets( IWineVideoService *iface, REFGUID guid, UINT *count,
+                                                                D3DFORMAT **formats )
+{
+    WineVideoServiceImpl *This = impl_from_IWineVideoService(iface);
+    unsigned int numFormats = 0, i;
+    struct vaapi_profile *profile;
+    VAConfigAttrib attrib;
+    VAStatus status;
+    D3DFORMAT *buf;
+
+    TRACE("(%p, %s, %p, %p)\n", This, debugstr_guid(guid), count, formats);
+
+    profile = vaapi_lookup_guid(guid);
+    if (!profile || !profile->supported)
+        return D3DERR_INVALIDCALL;
+
+    buf = CoTaskMemAlloc(sizeof(D3DFORMAT) * (sizeof(format_table)/sizeof(format_table[0])));
+    if (!buf)
+        return E_OUTOFMEMORY;
+
+    vaapi_lock();
+
+    attrib.type  = VAConfigAttribRTFormat;
+    attrib.value = 0;
+
+    status = pvaGetConfigAttributes(This->va_display, profile->profile, profile->entryPoint, &attrib, 1);
+    if (status != VA_STATUS_SUCCESS)
+    {
+        ERR("failed to get config attributes: %s (0x%x)\n", pvaErrorStr(status), status);
+    }
+    else
+    {
+        for (i = 0; i < sizeof(format_table)/sizeof(format_table[0]); i++)
+        {
+            /* skip TODOs */
+            if (!format_table[i].d3dformat || !format_table[i].vafourcc || !format_table[i].vaformat)
+                continue;
+            /* check if the main format is supported (i.e. RGB, YUV420, ...) */
+            if (!(attrib.value & format_table[i].vaformat))
+                continue;
+            if (vaapi_is_format_supported(This->va_display, profile, &format_table[i]))
+                buf[numFormats++] = format_table[i].d3dformat;
+        }
+    }
+
+    vaapi_unlock();
+
+    if (!numFormats)
+    {
+        CoTaskMemFree(buf);
+        return E_FAIL;
+    }
+
+    *count   = numFormats;
+    *formats = buf;
+    return S_OK;
+}
+
+static BOOL is_h264_codec( REFGUID guid )
+{
+    return (IsEqualGUID(guid, &DXVA2_ModeH264_A) ||
+            IsEqualGUID(guid, &DXVA2_ModeH264_B) ||
+            IsEqualGUID(guid, &DXVA2_ModeH264_C) ||
+            IsEqualGUID(guid, &DXVA2_ModeH264_D) ||
+            IsEqualGUID(guid, &DXVA2_ModeH264_E) ||
+            IsEqualGUID(guid, &DXVA2_ModeH264_F));
+}
+
+static HRESULT WINAPI WineVideoService_GetDecoderConfigurations( IWineVideoService *iface, REFGUID guid,
+                                                                 const DXVA2_VideoDesc *videoDesc, IUnknown *reserved,
+                                                                 UINT *count, DXVA2_ConfigPictureDecode **configs )
+{
+    WineVideoServiceImpl *This = impl_from_IWineVideoService(iface);
+    DXVA2_ConfigPictureDecode *config;
+    const struct vaapi_profile *profile;
+
+    FIXME("(%p/%p)->(%s, %p, %p, %p, %p): semi-stub\n",
+          iface, This, debugstr_guid(guid), videoDesc, reserved, count, configs);
+
+    if (!guid || !videoDesc || !count || !configs)
+        return E_INVALIDARG;
+
+    profile = vaapi_lookup_guid(guid);
+    if (!profile || !profile->supported)
+        return D3DERR_INVALIDCALL;
+
+    config = CoTaskMemAlloc(sizeof(*config));
+    if (!config)
+        return E_OUTOFMEMORY;
+
+    /* TODO: Query decoder instead of using hardcoded values */
+    memcpy(&config->guidConfigBitstreamEncryption, &DXVA_NoEncrypt, sizeof(GUID));
+    memcpy(&config->guidConfigMBcontrolEncryption, &DXVA_NoEncrypt, sizeof(GUID));
+    memcpy(&config->guidConfigResidDiffEncryption, &DXVA_NoEncrypt, sizeof(GUID));
+
+    config->ConfigBitstreamRaw             = 1;
+    config->ConfigMBcontrolRasterOrder     = is_h264_codec(guid) ? 0 : 1;
+    config->ConfigResidDiffHost            = 0; /* FIXME */
+    config->ConfigSpatialResid8            = 0; /* FIXME */
+    config->ConfigResid8Subtraction        = 0; /* FIXME */
+    config->ConfigSpatialHost8or9Clipping  = 0; /* FIXME */
+    config->ConfigSpatialResidInterleaved  = 0; /* FIXME */
+    config->ConfigIntraResidUnsigned       = 0; /* FIXME */
+    config->ConfigResidDiffAccelerator     = 0;
+    config->ConfigHostInverseScan          = 0;
+    config->ConfigSpecificIDCT             = 1;
+    config->Config4GroupedCoefs            = 0;
+    config->ConfigDecoderSpecific          = 0;
+
+    if (IsEqualGUID(guid, &DXVA2_ModeMPEG2_VLD))
+        config->ConfigMinRenderTargetBuffCount = 3;
+    else if(is_h264_codec(guid))
+        config->ConfigMinRenderTargetBuffCount = 16;
+    else
+    {
+        FIXME("ConfigMinRenderTargetBuffCount unknown for codec %s, falling back to 16\n", debugstr_guid(guid));
+        config->ConfigMinRenderTargetBuffCount = 16;
+    }
+
+    *count   = 1;
+    *configs = config;
+    return S_OK;
+}
+
+static HRESULT WINAPI WineVideoService_CreateVideoDecoder( IWineVideoService *iface, REFGUID guid, const DXVA2_VideoDesc *videoDesc,
+                                                           DXVA2_ConfigPictureDecode *config, UINT numSurfaces, IWineVideoDecoder **decoder )
+{
+    WineVideoServiceImpl *This = impl_from_IWineVideoService(iface);
+
+    FIXME("(%p/%p)->(%s, %p, %p, %u, %p): semi-stub\n",
+          iface, This, debugstr_guid(guid), videoDesc, config, numSurfaces, decoder);
+
+    if (IsEqualGUID(guid, &DXVA2_ModeMPEG2_VLD))
+        return vaapi_mpeg2decoder_create(iface, videoDesc, config, numSurfaces, decoder);
+
+    return E_FAIL;
+}
+
+static const IWineVideoServiceVtbl WineVideoService_VTable =
+{
+    WineVideoService_QueryInterface,
+    WineVideoService_AddRef,
+    WineVideoService_Release,
+    WineVideoService_GetDecoderDeviceGuids,
+    WineVideoService_GetDecoderRenderTargets,
+    WineVideoService_GetDecoderConfigurations,
+    WineVideoService_CreateVideoDecoder
+};
+
+IWineVideoService *vaapi_videoservice_create( void )
+{
+    WineVideoServiceImpl *videoservice;
+    int major, minor;
+
+    if (!config_vaapi_enabled)
+    {
+        FIXME("Vaapi backend disabled via registry\n");
+        return NULL;
+    }
+
+    vaapi_lock();
+
+    /* existing vaapi videoservice backend, increment refcount */
+    videoservice = vaapi_videoservice;
+    if (videoservice)
+    {
+        WineVideoService_AddRef(&videoservice->IWineVideoService_iface);
+        vaapi_unlock();
+        return &videoservice->IWineVideoService_iface;
+    }
+
+    videoservice = CoTaskMemAlloc(sizeof(*videoservice));
+    if (!videoservice)
+        goto err;
+
+    videoservice->IWineVideoService_iface.lpVtbl = &WineVideoService_VTable;
+    videoservice->refCount      = 1;
+
+    videoservice->va_handle     = NULL;
+    videoservice->va_x11_handle = NULL;
+    videoservice->x11_handle    = NULL;
+
+    videoservice->x11_display   = NULL;
+    videoservice->va_display    = NULL;
+
+    videoservice->va_handle = load_libva();
+    if (!videoservice->va_handle)
+        goto err;
+
+    videoservice->va_x11_handle = load_libva_x11();
+    if (!videoservice->va_x11_handle)
+        goto err;
+
+    videoservice->x11_handle = load_libx11();
+    if (!videoservice->x11_handle)
+        goto err;
+
+    videoservice->x11_display = pXOpenDisplay(NULL);
+    if (!videoservice->x11_display)
+        goto err;
+
+    videoservice->va_display = pvaGetDisplay(videoservice->x11_display);
+    if (!videoservice->va_display)
+        goto err;
+
+    if (pvaInitialize(videoservice->va_display, &major, &minor) != VA_STATUS_SUCCESS)
+        goto err;
+
+    if (!enumerate_vaprofiles(videoservice->va_display))
+        goto err;
+
+    /* remember the interface for future requests */
+    vaapi_videoservice = videoservice;
+    vaapi_unlock();
+    return &videoservice->IWineVideoService_iface;
+
+err:
+    if (videoservice->va_display)
+        pvaTerminate(videoservice->va_display);
+
+    vaapi_unlock();
+
+    if (videoservice->x11_display)
+        pXCloseDisplay(videoservice->x11_display);
+    if (videoservice->x11_handle)
+        wine_dlclose(videoservice->x11_handle, NULL, 0);
+    if (videoservice->va_x11_handle)
+        wine_dlclose(videoservice->va_x11_handle, NULL, 0);
+    if (videoservice->va_handle)
+        wine_dlclose(videoservice->va_handle, NULL, 0);
+
+    CoTaskMemFree(videoservice);
+    return NULL;
+}
+
+#else
+
+IWineVideoService *vaapi_videoservice_create( void )
+{
+    FIXME("Wine compiled without vaapi support, GPU decoding not available.\n");
+    return NULL;
+}
+
+#endif /* HAVE_VAAPI */
diff --git a/dlls/dxva2/videoservices.c b/dlls/dxva2/videoservices.c
index 46e431a..84222dc 100644
--- a/dlls/dxva2/videoservices.c
+++ b/dlls/dxva2/videoservices.c
@@ -1,5 +1,5 @@
 /*
- * Copyright 2014 Michael Müller for Pipelight
+ * Copyright 2014-2015 Michael Müller for Pipelight
  *
  * This library is free software; you can redistribute it and/or
  * modify it under the terms of the GNU Lesser General Public
@@ -37,18 +37,9 @@ typedef struct
 
     LONG refCount;
     IDirect3DDevice9 *device;
+    IWineVideoService *backend;
 } DirectXVideoAccelerationServiceImpl;
 
-static BOOL is_h264_codec( REFGUID guid )
-{
-    return (IsEqualGUID(guid, &DXVA2_ModeH264_A) ||
-            IsEqualGUID(guid, &DXVA2_ModeH264_B) ||
-            IsEqualGUID(guid, &DXVA2_ModeH264_C) ||
-            IsEqualGUID(guid, &DXVA2_ModeH264_D) ||
-            IsEqualGUID(guid, &DXVA2_ModeH264_E) ||
-            IsEqualGUID(guid, &DXVA2_ModeH264_F));
-}
-
 static inline DirectXVideoAccelerationServiceImpl *impl_from_IDirectXVideoAccelerationService( IDirectXVideoAccelerationService *iface )
 {
     return CONTAINING_RECORD(iface, DirectXVideoAccelerationServiceImpl, IDirectXVideoAccelerationService_iface);
@@ -115,6 +106,7 @@ static ULONG WINAPI DirectXVideoAccelerationService_Release( IDirectXVideoAccele
     {
         TRACE("Destroying\n");
         IDirect3DDevice9_Release(This->device);
+        IWineVideoService_Release(This->backend);
         CoTaskMemFree(This);
     }
 
@@ -200,6 +192,8 @@ static HRESULT WINAPI DirectXVideoDecoderService_CreateVideoDecoder( IDirectXVid
         IDirectXVideoDecoder **ppDecode )
 {
     DirectXVideoAccelerationServiceImpl *This = impl_from_IDirectXVideoDecoderService(iface);
+    IWineVideoDecoder *decoder_backend;
+    HRESULT hr;
 
     FIXME("(%p/%p)->(%s, %p, %p, %p, %u, %p): stub\n",
         iface, This, debugstr_guid(guid), pVideoDesc, pConfig, ppDecoderRenderTargets, NumSurfaces, ppDecode);
@@ -207,14 +201,20 @@ static HRESULT WINAPI DirectXVideoDecoderService_CreateVideoDecoder( IDirectXVid
     if (!guid || !pVideoDesc || !pConfig || !ppDecoderRenderTargets || !NumSurfaces || !ppDecode)
         return E_INVALIDARG;
 
-    return E_NOTIMPL;
+    hr = IWineVideoService_CreateVideoDecoder(This->backend, guid, pVideoDesc, pConfig, NumSurfaces, &decoder_backend);
+    if (SUCCEEDED(hr))
+    {
+        hr = genericdecoder_create(iface, pVideoDesc, pConfig, ppDecoderRenderTargets, NumSurfaces, decoder_backend, ppDecode);
+        IWineVideoDecoder_Release(decoder_backend);
+    }
+
+    return hr;
 }
 
 static HRESULT WINAPI DirectXVideoDecoderService_GetDecoderConfigurations( IDirectXVideoDecoderService *iface, REFGUID guid,
         const DXVA2_VideoDesc *pVideoDesc, IUnknown *pReserved, UINT *pCount, DXVA2_ConfigPictureDecode **ppConfigs )
 {
     DirectXVideoAccelerationServiceImpl *This = impl_from_IDirectXVideoDecoderService(iface);
-    DXVA2_ConfigPictureDecode *config;
 
     FIXME("(%p/%p)->(%s, %p, %p, %p, %p): semi-stub\n",
         iface, This, debugstr_guid(guid), pVideoDesc, pReserved, pCount, ppConfigs);
@@ -222,46 +222,19 @@ static HRESULT WINAPI DirectXVideoDecoderService_GetDecoderConfigurations( IDire
     if (!guid || !pVideoDesc || !pCount || !ppConfigs)
         return E_INVALIDARG;
 
-    config = CoTaskMemAlloc(sizeof(*config));
-    if (!config)
-        return E_OUTOFMEMORY;
-
-    /* TODO: Query decoder instead of using hardcoded values */
-
-    memcpy(&config->guidConfigBitstreamEncryption, &DXVA_NoEncrypt, sizeof(GUID));
-    memcpy(&config->guidConfigMBcontrolEncryption, &DXVA_NoEncrypt, sizeof(GUID));
-    memcpy(&config->guidConfigResidDiffEncryption, &DXVA_NoEncrypt, sizeof(GUID));
-
-    config->ConfigBitstreamRaw             = 1;
-    config->ConfigMBcontrolRasterOrder     = is_h264_codec(guid) ? 0 : 1;
-    config->ConfigResidDiffHost            = 0; /* FIXME */
-    config->ConfigSpatialResid8            = 0; /* FIXME */
-    config->ConfigResid8Subtraction        = 0; /* FIXME */
-    config->ConfigSpatialHost8or9Clipping  = 0; /* FIXME */
-    config->ConfigSpatialResidInterleaved  = 0; /* FIXME */
-    config->ConfigIntraResidUnsigned       = 0; /* FIXME */
-    config->ConfigResidDiffAccelerator     = 0;
-    config->ConfigHostInverseScan          = 0;
-    config->ConfigSpecificIDCT             = 1;
-    config->Config4GroupedCoefs            = 0;
-    config->ConfigMinRenderTargetBuffCount = 3;
-    config->ConfigDecoderSpecific          = 0;
-
-    *pCount    = 1;
-    *ppConfigs = config;
-    return S_OK;
+    return IWineVideoService_GetDecoderConfigurations(This->backend, guid, pVideoDesc, pReserved, pCount, ppConfigs);
 }
 
 static HRESULT WINAPI DirectXVideoDecoderService_GetDecoderDeviceGuids( IDirectXVideoDecoderService *iface, UINT *count, GUID **pGuids )
 {
     DirectXVideoAccelerationServiceImpl *This = impl_from_IDirectXVideoDecoderService(iface);
 
-    FIXME("(%p/%p)->(%p, %p): stub\n", iface, This, count, pGuids);
+    FIXME("(%p/%p)->(%p, %p): semi-stub\n", iface, This, count, pGuids);
 
     if (!count || !pGuids)
         return E_INVALIDARG;
 
-    return E_NOTIMPL;
+    return IWineVideoService_GetDecoderDeviceGuids(This->backend, count, pGuids);
 }
 
 static HRESULT WINAPI DirectXVideoDecoderService_GetDecoderRenderTargets( IDirectXVideoDecoderService *iface, REFGUID guid,
@@ -269,12 +242,12 @@ static HRESULT WINAPI DirectXVideoDecoderService_GetDecoderRenderTargets( IDirec
 {
     DirectXVideoAccelerationServiceImpl *This = impl_from_IDirectXVideoDecoderService(iface);
 
-    FIXME("(%p/%p)->(%s, %p, %p): stub\n", iface, This, debugstr_guid(guid), pCount, pFormats);
+    FIXME("(%p/%p)->(%s, %p, %p): semi-stub\n", iface, This, debugstr_guid(guid), pCount, pFormats);
 
     if (!guid || !pCount || !pFormats)
         return E_INVALIDARG;
 
-    return E_NOTIMPL;
+    return IWineVideoService_GetDecoderRenderTargets(This->backend, guid, pCount, pFormats);
 }
 
 static const IDirectXVideoDecoderServiceVtbl DirectXVideoDecoderService_VTable =
@@ -444,6 +417,7 @@ static const IDirectXVideoProcessorServiceVtbl DirectXVideoProcessorService_VTab
 
 HRESULT videoservice_create( IDirect3DDevice9 *device, REFIID riid, void **ppv )
 {
+    IWineVideoService *backend;
     DirectXVideoAccelerationServiceImpl *videoservice;
 
     if (!device || !riid || !ppv)
@@ -451,15 +425,23 @@ HRESULT videoservice_create( IDirect3DDevice9 *device, REFIID riid, void **ppv )
 
     *ppv = NULL;
 
+    backend = vaapi_videoservice_create();
+    if (!backend)
+        return E_NOINTERFACE;
+
     videoservice = CoTaskMemAlloc(sizeof(DirectXVideoAccelerationServiceImpl));
     if (!videoservice)
+    {
+        IWineVideoService_Release(videoservice->backend);
         return E_OUTOFMEMORY;
+    }
 
     videoservice->IDirectXVideoAccelerationService_iface.lpVtbl = &DirectXVideoAccelerationService_VTable;
     videoservice->IDirectXVideoDecoderService_iface.lpVtbl      = &DirectXVideoDecoderService_VTable;
     videoservice->IDirectXVideoProcessorService_iface.lpVtbl    = &DirectXVideoProcessorService_VTable;
     videoservice->refCount = 1;
     videoservice->device = device;
+    videoservice->backend = backend;
 
     if (IsEqualIID(riid, &IID_IUnknown))
         *ppv = &videoservice->IDirectXVideoAccelerationService_iface;
@@ -471,6 +453,8 @@ HRESULT videoservice_create( IDirect3DDevice9 *device, REFIID riid, void **ppv )
         *ppv = &videoservice->IDirectXVideoProcessorService_iface;
     else
     {
+        FIXME("Service %s not implemented\n", debugstr_guid(riid));
+        IWineVideoService_Release(videoservice->backend);
         CoTaskMemFree(videoservice);
         return E_NOINTERFACE;
     }
-- 
2.6.2

