DXVA: Prototype 73/26273/2
authorgunsoo83.kim <gunsoo83.kim@samsung.com>
Wed, 20 Aug 2014 07:11:01 +0000 (16:11 +0900)
committerSeokYeon Hwang <syeon.hwang@samsung.com>
Fri, 22 Aug 2014 06:14:10 +0000 (23:14 -0700)
Prototype implementation of DXVA plugin.

Change-Id: I994740402456f4b30125546ef6e257fae664d0e6
Signed-off-by: gunsoo83.kim <gunsoo83.kim@samsung.com>
tizen/src/hw/pci/Makefile.objs
tizen/src/hw/pci/maru_brillcodec_device.c
tizen/src/hw/pci/maru_dxva2_plugin.c [new file with mode: 0644]

index c33630e..46a584f 100644 (file)
@@ -10,6 +10,7 @@ obj-y += maru_camera_linux_pci.o
 LIBS += -lv4l2 -lv4lconvert
 endif
 ifdef CONFIG_WIN32
+obj-y += maru_dxva2_plugin.o
 obj-y += maru_camera_win32_pci.o
 LIBS += -lole32 -loleaut32 -luuid -lstrmiids
 endif
@@ -23,3 +24,4 @@ endif
 
 maru_brillcodec.o-cflags := $(LIBAV_CFLAGS)
 maru_brillcodec_device.o-cflags := $(LIBAV_CFLAGS)
+maru_dxva2_plugin.o-cflags := $(LIBAV_CFLAGS)
index 3b0040e..21ee881 100644 (file)
  *
  */
 
+//FIXME
+//#define CONFIG_VAAPI
+//#define CONFIG_DXVA2
+
+#ifdef CONFIG_VAAPI
 #include "dlfcn.h"
+#endif
 
 #include "qemu/main-loop.h"
 #include "hw/pci/pci.h"
@@ -47,6 +53,10 @@ MULTI_DEBUG_CHANNEL(qemu, brillcodec);
 #define CODEC_REG_SIZE              (256)
 #define DEFAULT_WORKER_THREAD_CNT   8
 
+#ifdef CONFIG_DXVA2
+extern CodecPlugin dxva_plugin;
+#endif
+
 enum codec_io_cmd {
     CODEC_CMD_API_INDEX             = 0x28,
     CODEC_CMD_CONTEXT_INDEX         = 0x2C,
@@ -244,9 +254,6 @@ static const MemoryRegionOps brillcodec_mmio_ops = {
     .endianness = DEVICE_LITTLE_ENDIAN,
 };
 
-//FIXME
-#define CONFIG_VAAPI
-
 #ifdef CONFIG_VAAPI
 static CodecPlugin *vaapi_probe(void) {
     // FIXME: probing...
@@ -263,6 +270,16 @@ static CodecPlugin *vaapi_probe(void) {
     return plugin;
 }
 #endif
+#ifdef CONFIG_DXVA2
+static CodecPlugin *dxva2_probe(void) {
+    CodecPlugin *plugin = NULL;
+
+    plugin = &dxva_plugin;
+
+    return plugin;
+}
+#endif
+
 
 static int brillcodec_initfn(PCIDevice *dev)
 {
@@ -297,6 +314,11 @@ static int brillcodec_initfn(PCIDevice *dev)
         INFO("VA-API extension is enabled.\n");
     }
 #endif
+#ifdef CONFIG_DXVA2
+    if ((s->hwaccel_plugin = dxva2_probe())) {
+        INFO("DXVA2 extension is enabled.\n");
+    }
+#endif
 
     return 0;
 }
diff --git a/tizen/src/hw/pci/maru_dxva2_plugin.c b/tizen/src/hw/pci/maru_dxva2_plugin.c
new file mode 100644 (file)
index 0000000..4cc938a
--- /dev/null
@@ -0,0 +1,1009 @@
+/*
+ * DXVA2 Module for Decoder Device
+ *
+ * Copyright (c) 2014 Samsung Electronics Co., Ltd All Rights Reserved
+ *
+ * Contact:
+ *  GunSoo Kim <gunsoo83.kim@samsung.com>
+ *  SangHo Park <sangho1206.park@samsung.com>
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version 2
+ * of the License, or (at your option) any later version.
+ *
+ * This program 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 General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston,
+ * MA 02110-1301, USA.
+ *
+ * Contributors:
+ * - S-Core Co., Ltd
+ *
+ */
+
+#ifndef _WIN32_WINNT
+#define _WIN32_WINNT 0x600
+#else
+# if _WIN32_WINNT < 0x600
+/* dxva2 needs Vista support */
+#  undef _WIN32_WINNT
+#  define _WIN32_WINNT 0x600
+# endif
+#endif
+
+#define DXVA2API_USE_BITFIELDS
+#define COBJMACROS
+
+#ifndef INITGUID
+#define INITGUID
+#endif
+
+#include "glib.h"
+#include "libavcodec/avcodec.h"
+#include "libavcodec/dxva2.h"
+#include "libavutil/pixdesc.h"
+#include "libavutil/imgutils.h"
+
+#include <assert.h>
+#include <windows.h>
+#include <windowsx.h>
+#include <ole2.h>
+#include <commctrl.h>
+#include <shlwapi.h>
+#include <d3d9.h>
+#include <dxva2api.h>
+
+#include <initguid.h> /* must be last included to not redefine existing GUIDs */
+
+/* dxva2api.h GUIDs: http://msdn.microsoft.com/en-us/library/windows/desktop/ms697067(v=vs100).aspx
+ * assume that they are declared in dxva2api.h */
+#define MS_GUID(name, l, w1, w2, b1, b2, b3, b4, b5, b6, b7, b8)
+
+#ifdef __MINGW32__
+# include <_mingw.h>
+
+# if !defined(__MINGW64_VERSION_MAJOR)
+#  undef MS_GUID
+#  define MS_GUID DEFINE_GUID /* dxva2api.h fails to declare those, redefine as static */
+#  define DXVA2_E_NEW_VIDEO_DEVICE MAKE_HRESULT(1, 4, 4097)
+# else
+#  include <dxva.h>
+# endif
+
+#endif /* __MINGW32__ */
+
+#include "maru_brillcodec_plugin.h"
+#include "debug_ch.h"
+
+/* define debug channel */
+MULTI_DEBUG_CHANNEL(qemu, dxva2_plugin);
+
+
+MS_GUID(IID_IDirectXVideoDecoderService, 0xfc51a551, 0xd5e7, 0x11d9, 0xaf,0x55,0x00,0x05,0x4e,0x43,0xff,0x02);
+MS_GUID(IID_IDirectXVideoAccelerationService, 0xfc51a550, 0xd5e7, 0x11d9, 0xaf,0x55,0x00,0x05,0x4e,0x43,0xff,0x02);
+
+MS_GUID    (DXVA_NoEncrypt,                         0x1b81bed0, 0xa0c7, 0x11d3, 0xb9, 0x84, 0x00, 0xc0, 0x4f, 0x2e, 0x73, 0xc5);
+
+/* Codec capabilities GUID, sorted by codec */
+MS_GUID    (DXVA2_ModeMPEG2_MoComp,                 0xe6a9f44b, 0x61b0, 0x4563, 0x9e, 0xa4, 0x63, 0xd2, 0xa3, 0xc6, 0xfe, 0x66);
+MS_GUID    (DXVA2_ModeMPEG2_IDCT,                   0xbf22ad00, 0x03ea, 0x4690, 0x80, 0x77, 0x47, 0x33, 0x46, 0x20, 0x9b, 0x7e);
+MS_GUID    (DXVA2_ModeMPEG2_VLD,                    0xee27417f, 0x5e28, 0x4e65, 0xbe, 0xea, 0x1d, 0x26, 0xb5, 0x08, 0xad, 0xc9);
+DEFINE_GUID(DXVA2_ModeMPEG2and1_VLD,                0x86695f12, 0x340e, 0x4f04, 0x9f, 0xd3, 0x92, 0x53, 0xdd, 0x32, 0x74, 0x60);
+DEFINE_GUID(DXVA2_ModeMPEG1_VLD,                    0x6f3ec719, 0x3735, 0x42cc, 0x80, 0x63, 0x65, 0xcc, 0x3c, 0xb3, 0x66, 0x16);
+
+MS_GUID    (DXVA2_ModeH264_A,                       0x1b81be64, 0xa0c7, 0x11d3, 0xb9, 0x84, 0x00, 0xc0, 0x4f, 0x2e, 0x73, 0xc5);
+MS_GUID    (DXVA2_ModeH264_B,                       0x1b81be65, 0xa0c7, 0x11d3, 0xb9, 0x84, 0x00, 0xc0, 0x4f, 0x2e, 0x73, 0xc5);
+MS_GUID    (DXVA2_ModeH264_C,                       0x1b81be66, 0xa0c7, 0x11d3, 0xb9, 0x84, 0x00, 0xc0, 0x4f, 0x2e, 0x73, 0xc5);
+MS_GUID    (DXVA2_ModeH264_D,                       0x1b81be67, 0xa0c7, 0x11d3, 0xb9, 0x84, 0x00, 0xc0, 0x4f, 0x2e, 0x73, 0xc5);
+MS_GUID    (DXVA2_ModeH264_E,                       0x1b81be68, 0xa0c7, 0x11d3, 0xb9, 0x84, 0x00, 0xc0, 0x4f, 0x2e, 0x73, 0xc5);
+MS_GUID    (DXVA2_ModeH264_F,                       0x1b81be69, 0xa0c7, 0x11d3, 0xb9, 0x84, 0x00, 0xc0, 0x4f, 0x2e, 0x73, 0xc5);
+DEFINE_GUID(DXVA_ModeH264_VLD_Multiview,            0x9901CCD3, 0xca12, 0x4b7e, 0x86, 0x7a, 0xe2, 0x22, 0x3d, 0x92, 0x55, 0xc3); // MVC
+DEFINE_GUID(DXVA_ModeH264_VLD_WithFMOASO_NoFGT,     0xd5f04ff9, 0x3418, 0x45d8, 0x95, 0x61, 0x32, 0xa7, 0x6a, 0xae, 0x2d, 0xdd);
+DEFINE_GUID(DXVADDI_Intel_ModeH264_A,               0x604F8E64, 0x4951, 0x4c54, 0x88, 0xFE, 0xAB, 0xD2, 0x5C, 0x15, 0xB3, 0xD6);
+DEFINE_GUID(DXVADDI_Intel_ModeH264_C,               0x604F8E66, 0x4951, 0x4c54, 0x88, 0xFE, 0xAB, 0xD2, 0x5C, 0x15, 0xB3, 0xD6);
+DEFINE_GUID(DXVADDI_Intel_ModeH264_E,               0x604F8E68, 0x4951, 0x4c54, 0x88, 0xFE, 0xAB, 0xD2, 0x5C, 0x15, 0xB3, 0xD6); // DXVA_Intel_H264_NoFGT_ClearVideo
+DEFINE_GUID(DXVA_ModeH264_VLD_NoFGT_Flash,          0x4245F676, 0x2BBC, 0x4166, 0xa0, 0xBB, 0x54, 0xE7, 0xB8, 0x49, 0xC3, 0x80);
+
+MS_GUID    (DXVA2_ModeWMV8_A,                       0x1b81be80, 0xa0c7, 0x11d3, 0xb9, 0x84, 0x00, 0xc0, 0x4f, 0x2e, 0x73, 0xc5);
+MS_GUID    (DXVA2_ModeWMV8_B,                       0x1b81be81, 0xa0c7, 0x11d3, 0xb9, 0x84, 0x00, 0xc0, 0x4f, 0x2e, 0x73, 0xc5);
+
+MS_GUID    (DXVA2_ModeWMV9_A,                       0x1b81be90, 0xa0c7, 0x11d3, 0xb9, 0x84, 0x00, 0xc0, 0x4f, 0x2e, 0x73, 0xc5);
+MS_GUID    (DXVA2_ModeWMV9_B,                       0x1b81be91, 0xa0c7, 0x11d3, 0xb9, 0x84, 0x00, 0xc0, 0x4f, 0x2e, 0x73, 0xc5);
+MS_GUID    (DXVA2_ModeWMV9_C,                       0x1b81be94, 0xa0c7, 0x11d3, 0xb9, 0x84, 0x00, 0xc0, 0x4f, 0x2e, 0x73, 0xc5);
+
+MS_GUID    (DXVA2_ModeVC1_A,                        0x1b81beA0, 0xa0c7, 0x11d3, 0xb9, 0x84, 0x00, 0xc0, 0x4f, 0x2e, 0x73, 0xc5);
+MS_GUID    (DXVA2_ModeVC1_B,                        0x1b81beA1, 0xa0c7, 0x11d3, 0xb9, 0x84, 0x00, 0xc0, 0x4f, 0x2e, 0x73, 0xc5);
+MS_GUID    (DXVA2_ModeVC1_C,                        0x1b81beA2, 0xa0c7, 0x11d3, 0xb9, 0x84, 0x00, 0xc0, 0x4f, 0x2e, 0x73, 0xc5);
+MS_GUID    (DXVA2_ModeVC1_D,                        0x1b81beA3, 0xa0c7, 0x11d3, 0xb9, 0x84, 0x00, 0xc0, 0x4f, 0x2e, 0x73, 0xc5);
+DEFINE_GUID(DXVA2_ModeVC1_D2010,                    0x1b81beA4, 0xa0c7, 0x11d3, 0xb9, 0x84, 0x00, 0xc0, 0x4f, 0x2e, 0x73, 0xc5); // August 2010 update
+DEFINE_GUID(DXVA_Intel_VC1_ClearVideo,              0xBCC5DB6D, 0xA2B6, 0x4AF0, 0xAC, 0xE4, 0xAD, 0xB1, 0xF7, 0x87, 0xBC, 0x89);
+DEFINE_GUID(DXVA_Intel_VC1_ClearVideo_2,            0xE07EC519, 0xE651, 0x4CD6, 0xAC, 0x84, 0x13, 0x70, 0xCC, 0xEE, 0xC8, 0x51);
+
+DEFINE_GUID(DXVA_nVidia_MPEG4_ASP,                  0x9947EC6F, 0x689B, 0x11DC, 0xA3, 0x20, 0x00, 0x19, 0xDB, 0xBC, 0x41, 0x84);
+DEFINE_GUID(DXVA_ModeMPEG4pt2_VLD_Simple,           0xefd64d74, 0xc9e8, 0x41d7, 0xa5, 0xe9, 0xe9, 0xb0, 0xe3, 0x9f, 0xa3, 0x19);
+DEFINE_GUID(DXVA_ModeMPEG4pt2_VLD_AdvSimple_NoGMC,  0xed418a9f, 0x010d, 0x4eda, 0x9a, 0xe3, 0x9a, 0x65, 0x35, 0x8d, 0x8d, 0x2e);
+DEFINE_GUID(DXVA_ModeMPEG4pt2_VLD_AdvSimple_GMC,    0xab998b5b, 0x4258, 0x44a9, 0x9f, 0xeb, 0x94, 0xe5, 0x97, 0xa6, 0xba, 0xae);
+DEFINE_GUID(DXVA_ModeMPEG4pt2_VLD_AdvSimple_Avivo,  0x7C74ADC6, 0xe2ba, 0x4ade, 0x86, 0xde, 0x30, 0xbe, 0xab, 0xb4, 0x0c, 0xc1);
+
+/* */
+typedef struct {
+    const char   *name;
+    const GUID   *guid;
+    int          codec;
+} dxva2_mode_t;
+/* XXX Prefered modes must come first */
+static const dxva2_mode_t dxva2_modes[] = {
+    /* MPEG-1/2 */
+    { "MPEG-2 variable-length decoder",                                               &DXVA2_ModeMPEG2_VLD,                   AV_CODEC_ID_MPEG2VIDEO },
+    { "MPEG-2 & MPEG-1 variable-length decoder",                                      &DXVA2_ModeMPEG2and1_VLD,               AV_CODEC_ID_MPEG2VIDEO },
+    { "MPEG-2 motion compensation",                                                   &DXVA2_ModeMPEG2_MoComp,                0 },
+    { "MPEG-2 inverse discrete cosine transform",                                     &DXVA2_ModeMPEG2_IDCT,                  0 },
+
+    { "MPEG-1 variable-length decoder",                                               &DXVA2_ModeMPEG1_VLD,                   0 },
+
+    /* H.264 */
+    { "H.264 variable-length decoder, film grain technology",                         &DXVA2_ModeH264_F,                      AV_CODEC_ID_H264 },
+    { "H.264 variable-length decoder, no film grain technology (Intel ClearVideo)",   &DXVADDI_Intel_ModeH264_E,              AV_CODEC_ID_H264 },
+    { "H.264 variable-length decoder, no film grain technology",                      &DXVA2_ModeH264_E,                      AV_CODEC_ID_H264 },
+    { "H.264 variable-length decoder, no film grain technology, FMO/ASO",             &DXVA_ModeH264_VLD_WithFMOASO_NoFGT,    AV_CODEC_ID_H264 },
+    { "H.264 variable-length decoder, no film grain technology, Flash",               &DXVA_ModeH264_VLD_NoFGT_Flash,         AV_CODEC_ID_H264 },
+
+    { "H.264 inverse discrete cosine transform, film grain technology",               &DXVA2_ModeH264_D,                      0 },
+    { "H.264 inverse discrete cosine transform, no film grain technology",            &DXVA2_ModeH264_C,                      0 },
+    { "H.264 inverse discrete cosine transform, no film grain technology (Intel)",    &DXVADDI_Intel_ModeH264_C,              0 },
+
+    { "H.264 motion compensation, film grain technology",                             &DXVA2_ModeH264_B,                      0 },
+    { "H.264 motion compensation, no film grain technology",                          &DXVA2_ModeH264_A,                      0 },
+    { "H.264 motion compensation, no film grain technology (Intel)",                  &DXVADDI_Intel_ModeH264_A,              0 },
+
+    /* WMV */
+    { "Windows Media Video 8 motion compensation",                                    &DXVA2_ModeWMV8_B,                      0 },
+    { "Windows Media Video 8 post processing",                                        &DXVA2_ModeWMV8_A,                      0 },
+
+    { "Windows Media Video 9 IDCT",                                                   &DXVA2_ModeWMV9_C,                      0 },
+    { "Windows Media Video 9 motion compensation",                                    &DXVA2_ModeWMV9_B,                      0 },
+    { "Windows Media Video 9 post processing",                                        &DXVA2_ModeWMV9_A,                      0 },
+
+    /* VC-1 */
+    { "VC-1 variable-length decoder",                                                 &DXVA2_ModeVC1_D,                       AV_CODEC_ID_VC1 },
+    { "VC-1 variable-length decoder",                                                 &DXVA2_ModeVC1_D,                       AV_CODEC_ID_WMV3 },
+    { "VC-1 variable-length decoder",                                                 &DXVA2_ModeVC1_D2010,                   AV_CODEC_ID_VC1 },
+    { "VC-1 variable-length decoder",                                                 &DXVA2_ModeVC1_D2010,                   AV_CODEC_ID_WMV3 },
+    { "VC-1 variable-length decoder 2 (Intel)",                                       &DXVA_Intel_VC1_ClearVideo_2,           0 },
+    { "VC-1 variable-length decoder (Intel)",                                         &DXVA_Intel_VC1_ClearVideo,             0 },
+
+    { "VC-1 inverse discrete cosine transform",                                       &DXVA2_ModeVC1_C,                       0 },
+    { "VC-1 motion compensation",                                                     &DXVA2_ModeVC1_B,                       0 },
+    { "VC-1 post processing",                                                         &DXVA2_ModeVC1_A,                       0 },
+
+    /* Xvid/Divx: TODO */
+    { "MPEG-4 Part 2 nVidia bitstream decoder",                                       &DXVA_nVidia_MPEG4_ASP,                 0 },
+    { "MPEG-4 Part 2 variable-length decoder, Simple Profile",                        &DXVA_ModeMPEG4pt2_VLD_Simple,          0 },
+    { "MPEG-4 Part 2 variable-length decoder, Simple&Advanced Profile, no GMC",       &DXVA_ModeMPEG4pt2_VLD_AdvSimple_NoGMC, 0 },
+    { "MPEG-4 Part 2 variable-length decoder, Simple&Advanced Profile, GMC",          &DXVA_ModeMPEG4pt2_VLD_AdvSimple_GMC,   0 },
+    { "MPEG-4 Part 2 variable-length decoder, Simple&Advanced Profile, Avivo",        &DXVA_ModeMPEG4pt2_VLD_AdvSimple_Avivo, 0 },
+
+    { NULL, NULL, 0 }
+};
+
+/* */
+typedef struct {
+    const char  *name;
+    D3DFORMAT   format;
+} d3d_format_t;
+/* XXX Prefered format must come first */
+static const d3d_format_t d3d_formats[] = {
+    { "YV12",   MAKEFOURCC('Y','V','1','2')},
+    { "NV12",   MAKEFOURCC('N','V','1','2')},
+    { "IMC3",   MAKEFOURCC('I','M','C','3')},
+    { NULL, 0 }
+};
+
+/* */
+typedef struct {
+    LPDIRECT3DSURFACE9  d3d;
+    bool                is_occupied;
+} DXVAPluginSurface;
+
+#define VA_DXVA2_MAX_SURFACE_COUNT (64)
+typedef struct DXVAPluginContext
+{
+    /* Direct3D */
+    D3DPRESENT_PARAMETERS   d3dpp;
+    LPDIRECT3D9             d3dobj;
+    LPDIRECT3DDEVICE9       d3ddev;
+
+    /* Device manager */
+    IDirect3DDeviceManager9 *devmng;
+    HANDLE                  hd3ddev;
+
+    /* Video service */
+    IDirectXVideoDecoderService *vs;
+    GUID                        guid_decdev;
+    D3DFORMAT                   render_fmt;
+
+    /* Video decoder */
+    DXVA2_ConfigPictureDecode   cfg;
+    IDirectXVideoDecoder        *decoder;
+
+    /* Option conversion */
+    D3DFORMAT   output;
+
+    /* */
+    unsigned     surface_count;
+    int          surface_width;
+    int          surface_height;
+
+    int          thread_count;
+
+    DXVAPluginSurface surface[VA_DXVA2_MAX_SURFACE_COUNT];
+    LPDIRECT3DSURFACE9 hw_surface[VA_DXVA2_MAX_SURFACE_COUNT];
+
+    struct dxva_context *hw_context;
+} DXVAPluginContext;
+
+// FIXME : modify dxva_ctx to multi instance for multi-thread decoding.
+static DXVAPluginContext *dxva_ctx = &(DXVAPluginContext) {};
+
+/* DLL */
+static HINSTANCE hd3d9_dll;
+static HINSTANCE hdxva2_dll;
+
+
+static const dxva2_mode_t *Dxva2FindMode(const GUID *guid)
+{
+    unsigned i = 0;
+
+    for (i = 0; dxva2_modes[i].name; i++) {
+        if (IsEqualGUID(dxva2_modes[i].guid, guid))
+            return &dxva2_modes[i];
+    }
+    return NULL;
+}
+
+static const d3d_format_t *D3dFindFormat(D3DFORMAT format)
+{
+    unsigned i = 0;
+
+    for (i = 0; d3d_formats[i].name; i++) {
+        if (d3d_formats[i].format == format)
+            return &d3d_formats[i];
+    }
+    return NULL;
+}
+
+/**
+ * It creates a Direct3D device usable for DXVA 2
+ */
+static int D3dCreateDevice(void)
+{
+    /* */
+    LPDIRECT3D9 (WINAPI *Create9)(UINT SDKVersion);
+    Create9 = (void *)GetProcAddress(hd3d9_dll, "Direct3DCreate9");
+    if (!Create9) {
+        ERR("Cannot locate reference to Direct3DCreate9 ABI in DLL\n");
+        return -1;
+    }
+
+    /* */
+    LPDIRECT3D9 d3dobj;
+    d3dobj = Create9(D3D_SDK_VERSION);
+    if (!d3dobj) {
+        ERR("Direct3DCreate9 failed\n");
+        return -1;
+    }
+    dxva_ctx->d3dobj = d3dobj;
+
+    /* */
+    D3DPRESENT_PARAMETERS *d3dpp = &dxva_ctx->d3dpp;
+    ZeroMemory(d3dpp, sizeof(*d3dpp));
+    d3dpp->Flags                  = D3DPRESENTFLAG_VIDEO;
+    d3dpp->Windowed               = TRUE;
+    d3dpp->hDeviceWindow          = NULL;
+    d3dpp->SwapEffect             = D3DSWAPEFFECT_DISCARD;
+    d3dpp->MultiSampleType        = D3DMULTISAMPLE_NONE;
+    d3dpp->PresentationInterval   = D3DPRESENT_INTERVAL_DEFAULT;
+    d3dpp->BackBufferCount        = 0;                  /* FIXME what to put here */
+    d3dpp->BackBufferFormat       = D3DFMT_X8R8G8B8;    /* FIXME what to put here */
+    d3dpp->BackBufferWidth        = 0;
+    d3dpp->BackBufferHeight       = 0;
+    d3dpp->EnableAutoDepthStencil = FALSE;
+
+    /* Direct3D needs a HWND to create a device, even without using ::Present
+    this HWND is used to alert Direct3D when there's a change of focus window.
+    For now, use GetDesktopWindow, as it looks harmless */
+    LPDIRECT3DDEVICE9 d3ddev;
+    if (FAILED(IDirect3D9_CreateDevice(d3dobj, D3DADAPTER_DEFAULT,
+                                       D3DDEVTYPE_HAL, GetDesktopWindow(),
+                                       D3DCREATE_SOFTWARE_VERTEXPROCESSING |
+                                       D3DCREATE_MULTITHREADED,
+                                       d3dpp, &d3ddev))) {
+        ERR("IDirect3D9_CreateDevice failed\n");
+        return -1;
+    }
+    dxva_ctx->d3ddev = d3ddev;
+
+    return 0;
+}
+
+/**
+ * It creates a Direct3D device manager
+ */
+static int D3dCreateDeviceManager(void)
+{
+    HRESULT (WINAPI *CreateDeviceManager9)(UINT *pResetToken,
+                                           IDirect3DDeviceManager9 **);
+    CreateDeviceManager9 =
+      (void *)GetProcAddress(hdxva2_dll,
+                             "DXVA2CreateDirect3DDeviceManager9");
+
+    if (!CreateDeviceManager9) {
+        ERR("cannot load function\n");
+        return -1;
+    }
+
+    UINT token;
+    IDirect3DDeviceManager9 *devmng;
+    if (FAILED(CreateDeviceManager9(&token, &devmng))) {
+        ERR("OurDirect3DCreateDeviceManager9 failed\n");
+        return -1;
+    }
+    TRACE("OurDirect3DCreateDeviceManager9 Success!\n");
+
+    dxva_ctx->devmng = devmng;
+    TRACE("obtained IDirect3DDeviceManager9\n");
+
+    HRESULT hr = IDirect3DDeviceManager9_ResetDevice(devmng, dxva_ctx->d3ddev, token);
+    if (FAILED(hr)) {
+        ERR("IDirect3DDeviceManager9_ResetDevice failed: %08x\n", (unsigned)hr);
+        return -1;
+    }
+
+    return 0;
+}
+
+/**
+ * It creates a DirectX video service
+ */
+static int DxCreateVideoService(void)
+{
+    HRESULT (WINAPI *CreateVideoService)(IDirect3DDevice9 *,
+                                         REFIID riid,
+                                         void **ppService);
+    CreateVideoService =
+      (void *)GetProcAddress(hdxva2_dll, "DXVA2CreateVideoService");
+
+    if (!CreateVideoService) {
+        ERR("cannot load function\n");
+        return -1;
+    }
+    TRACE("DXVA2CreateVideoService Success!\n");
+
+    HRESULT hr;
+
+    HANDLE hd3ddev;
+    hr = IDirect3DDeviceManager9_OpenDeviceHandle(dxva_ctx->devmng, &hd3ddev);
+    if (FAILED(hr)) {
+        ERR("OpenDeviceHandle failed\n");
+        return -1;
+    }
+    dxva_ctx->hd3ddev = hd3ddev;
+
+    void *pv;
+    hr = IDirect3DDeviceManager9_GetVideoService(dxva_ctx->devmng, hd3ddev,
+                                        &IID_IDirectXVideoDecoderService, &pv);
+    if (FAILED(hr)) {
+        ERR("GetVideoService failed\n");
+        return -1;
+    }
+    dxva_ctx->vs = pv;
+
+    return 0;
+}
+
+/**
+ * Find the best suited decoder mode GUID and render format.
+ */
+static int DxFindVideoServiceConversion(int codec_id)
+{
+    unsigned i = 0;
+    GUID *guid_decdev = &dxva_ctx->guid_decdev;
+    D3DFORMAT *render_fmt = &dxva_ctx->render_fmt;
+
+    /* Retreive supported modes from the decoder service */
+    UINT decdev_count = 0;
+    GUID *guid_decdev_list = NULL;
+    if (FAILED(IDirectXVideoDecoderService_GetDecoderDeviceGuids(dxva_ctx->vs,
+                                                                 &decdev_count,
+                                                                 &guid_decdev_list))) {
+        ERR("IDirectXVideoDecoderService_GetDecoderDeviceGuids failed\n");
+        return -1;
+    }
+
+    INFO("IDirectXVideoDecoderService_GetDecoderDeviceGuids success. count=%d\n", decdev_count);
+
+    for (i = 0; i < decdev_count; i++) {
+        const GUID *g = &guid_decdev_list[i];
+        const dxva2_mode_t *mode = Dxva2FindMode(g);
+        if (mode) {
+            INFO("- '%s' is supported by hardware\n", mode->name);
+        } else {
+            WARN("- Unknown GUID = %08X-%04x-%04x-XXXX\n",
+                     (unsigned)g->Data1, g->Data2, g->Data3);
+        }
+    }
+
+    /* Try all supported mode by our priority */
+    for (i = 0; dxva2_modes[i].name; i++) {
+        const dxva2_mode_t *mode = &dxva2_modes[i];
+        unsigned j = 0;
+        if (!mode->codec || mode->codec != codec_id)
+            continue;
+
+        /* */
+        bool is_supported = false;
+        const GUID *g = &guid_decdev_list[0];
+        for (; !is_supported && g < &guid_decdev_list[decdev_count]; g++) {
+            is_supported = IsEqualGUID(mode->guid, g);
+        }
+        if (!is_supported)
+            continue;
+
+        /* */
+        INFO("Trying to use '%s' as input\n", mode->name);
+        UINT      render_fmt_count = 0;
+        D3DFORMAT *render_fmt_list = NULL;
+        if (FAILED(IDirectXVideoDecoderService_GetDecoderRenderTargets(dxva_ctx->vs, mode->guid,
+                                                                       &render_fmt_count,
+                                                                       &render_fmt_list))) {
+            ERR("IDirectXVideoDecoderService_GetDecoderRenderTargets failed\n");
+            continue;
+        }
+        for (j = 0; j < render_fmt_count; j++) {
+            const D3DFORMAT f = render_fmt_list[j];
+            const d3d_format_t *format = D3dFindFormat(f);
+
+            INFO("HOST supported format %d (%4.4s)\n", f, (const char*)&f);
+            if (format) {
+                INFO("%s is supported for output\n", format->name);
+            } else {
+                INFO("%d is supported for output (%4.4s)\n", f, (const char*)&f);
+            }
+        }
+
+        /* */
+        for (j = 0; d3d_formats[j].name; j++) {
+            const d3d_format_t *format = &d3d_formats[j];
+            unsigned k = 0;
+
+            /* */
+            bool is_supported = false;
+            for (k = 0; !is_supported && k < render_fmt_count; k++) {
+                is_supported = format->format == render_fmt_list[k];
+            }
+            if (!is_supported)
+                continue;
+
+            /* We have our solution */
+            INFO("Using '%s' to decode to '%s'\n", mode->name, format->name);
+            *guid_decdev = *mode->guid;
+            *render_fmt = format->format;
+            CoTaskMemFree(render_fmt_list);
+            CoTaskMemFree(guid_decdev_list);
+            return 0;
+        }
+        CoTaskMemFree(render_fmt_list);
+    }
+    CoTaskMemFree(guid_decdev_list);
+    return -1;
+}
+
+static void DxDestroyVideoDecoder(void)
+{
+    unsigned i = 0;
+
+    if (dxva_ctx->decoder) {
+        IDirectXVideoDecoder_Release(dxva_ctx->decoder);
+    }
+    dxva_ctx->decoder = NULL;
+
+    for (i = 0; i < dxva_ctx->surface_count; i++) {
+        IDirect3DSurface9_Release(dxva_ctx->surface[i].d3d);
+    }
+    dxva_ctx->surface_count = 0;
+}
+
+/**
+ * It creates a DXVA2 decoder using the given video format
+ */
+static int DxCreateVideoDecoder(AVCodecContext *dec_ctx)
+{
+    int surface_count = 0;
+    unsigned i = 0;
+    int width = 0;
+    int height = 0;
+    int codec_id = 0;
+
+    width = dec_ctx->width;
+    height = dec_ctx->height;
+    codec_id = dec_ctx->codec_id;
+
+
+    TRACE("DxCreateVideoDecoder id %d %dx%d\n",
+            codec_id, width, height);
+
+    /* Allocates all surfaces needed for the decoder */
+    dxva_ctx->surface_width  = (width  + 15) & ~15;
+    dxva_ctx->surface_height = (height + 15) & ~15;
+    switch (codec_id) {
+    case AV_CODEC_ID_H264:
+        surface_count = 16 + dxva_ctx->thread_count + 2;
+        break;
+    case AV_CODEC_ID_MPEG1VIDEO:
+    case AV_CODEC_ID_MPEG2VIDEO:
+        surface_count = 2 + 2;
+        break;
+    default:
+        surface_count = 2 + 1;
+        break;
+    }
+    if (surface_count > VA_DXVA2_MAX_SURFACE_COUNT)
+        return -1;
+    dxva_ctx->surface_count = surface_count;
+    if (FAILED(IDirectXVideoDecoderService_CreateSurface(dxva_ctx->vs,
+                                                         dxva_ctx->surface_width,
+                                                         dxva_ctx->surface_height,
+                                                         dxva_ctx->surface_count - 1,
+                                                         dxva_ctx->render_fmt,
+                                                         D3DPOOL_DEFAULT,
+                                                         0,
+                                                         DXVA2_VideoDecoderRenderTarget,
+                                                         dxva_ctx->hw_surface,
+                                                         NULL))) {
+        ERR("IDirectXVideoAccelerationService_CreateSurface failed\n");
+        dxva_ctx->surface_count = 0;
+        return -1;
+    }
+    for (i = 0; i < dxva_ctx->surface_count; i++) {
+        DXVAPluginSurface *surface = &dxva_ctx->surface[i];
+        surface->d3d = dxva_ctx->hw_surface[i];
+        surface->is_occupied = false;
+    }
+    TRACE("IDirectXVideoAccelerationService_CreateSurface succeed with %d surfaces (%dx%d)\n",
+            dxva_ctx->surface_count, width, height);
+
+    /* */
+    DXVA2_VideoDesc dsc;
+    ZeroMemory(&dsc, sizeof(dsc));
+    dsc.SampleWidth     = width;
+    dsc.SampleHeight    = height;
+    dsc.Format          = dxva_ctx->render_fmt;
+    dsc.InputSampleFreq.Numerator   = 0;
+    dsc.InputSampleFreq.Denominator = 0;
+    dsc.OutputFrameFreq = dsc.InputSampleFreq;
+    dsc.UABProtectionLevel = FALSE;
+    dsc.Reserved = 0;
+
+    /* FIXME I am unsure we can let unknown everywhere */
+    DXVA2_ExtendedFormat *ext = &dsc.SampleFormat;
+    ext->SampleFormat = 0;//DXVA2_SampleUnknown;
+    ext->VideoChromaSubsampling = 0;//DXVA2_VideoChromaSubsampling_Unknown;
+    ext->NominalRange = 0;//DXVA2_NominalRange_Unknown;
+    ext->VideoTransferMatrix = 0;//DXVA2_VideoTransferMatrix_Unknown;
+    ext->VideoLighting = 0;//DXVA2_VideoLighting_Unknown;
+    ext->VideoPrimaries = 0;//DXVA2_VideoPrimaries_Unknown;
+    ext->VideoTransferFunction = 0;//DXVA2_VideoTransFunc_Unknown;
+
+    /* List all configurations available for the decoder */
+    UINT                      cfg_count = 0;
+    DXVA2_ConfigPictureDecode *cfg_list = NULL;
+    if (FAILED(IDirectXVideoDecoderService_GetDecoderConfigurations(dxva_ctx->vs,
+                                                                    &dxva_ctx->guid_decdev,
+                                                                    &dsc,
+                                                                    NULL,
+                                                                    &cfg_count,
+                                                                    &cfg_list))) {
+        ERR("IDirectXVideoDecoderService_GetDecoderConfigurations failed\n");
+        return -1;
+    }
+    TRACE("we got %d decoder configurations\n", cfg_count);
+
+    /* Select the best decoder configuration */
+    int cfg_score = 0;
+    for (i = 0; i < cfg_count; i++) {
+        const DXVA2_ConfigPictureDecode *cfg = &cfg_list[i];
+
+        /* */
+        TRACE("configuration[%d] ConfigBitstreamRaw %d\n",
+                i, cfg->ConfigBitstreamRaw);
+
+        /* */
+        int score;
+        if (cfg->ConfigBitstreamRaw == 1)
+            score = 1;
+        else if (codec_id == AV_CODEC_ID_H264 && cfg->ConfigBitstreamRaw == 2)
+            score = 2;
+        else
+            continue;
+        if (IsEqualGUID(&cfg->guidConfigBitstreamEncryption, &DXVA_NoEncrypt))
+            score += 16;
+
+        if (cfg_score < score) {
+            dxva_ctx->cfg = *cfg;
+            cfg_score = score;
+        }
+    }
+    CoTaskMemFree(cfg_list);
+    if (cfg_score <= 0) {
+        ERR("Failed to find a supported decoder configuration\n");
+        return -1;
+    }
+
+    /* Create the decoder */
+    IDirectXVideoDecoder *decoder;
+    if (FAILED(IDirectXVideoDecoderService_CreateVideoDecoder(dxva_ctx->vs,
+                                                              &dxva_ctx->guid_decdev,
+                                                              &dsc,
+                                                              &dxva_ctx->cfg,
+                                                              dxva_ctx->hw_surface,
+                                                              dxva_ctx->surface_count,
+                                                              &decoder))) {
+        ERR("IDirectXVideoDecoderService_CreateVideoDecoder failed\n");
+        return -1;
+    }
+    dxva_ctx->decoder = decoder;
+    TRACE("IDirectXVideoDecoderService_CreateVideoDecoder succeed\n");
+    return 0;
+}
+
+static void DxCreateVideoConversion(void)
+{
+    unsigned int output = dxva_ctx->render_fmt;
+
+    switch (output) {
+    case MAKEFOURCC('N','V','1','2'):
+    case MAKEFOURCC('I','M','C','3'):
+        dxva_ctx->output = MAKEFOURCC('Y','V','1','2');
+        break;
+    default:
+        dxva_ctx->output = dxva_ctx->render_fmt;
+        break;
+    }
+}
+
+/**
+ * It destroys a DirectX video service
+ */
+static void DxDestroyVideoService(void)
+{
+    if (dxva_ctx->hd3ddev)
+        IDirect3DDeviceManager9_CloseDeviceHandle(dxva_ctx->devmng, dxva_ctx->hd3ddev);
+    if (dxva_ctx->vs)
+        IDirectXVideoDecoderService_Release(dxva_ctx->vs);
+}
+
+/**
+ * It destroys a Direct3D device manager
+ */
+static void D3dDestroyDeviceManager(void)
+{
+    if (dxva_ctx->devmng)
+        IDirect3DDeviceManager9_Release(dxva_ctx->devmng);
+}
+
+/**
+ * It releases a Direct3D device and its resources.
+ */
+static void D3dDestroyDevice(void)
+{
+    if (dxva_ctx->d3ddev)
+        IDirect3DDevice9_Release(dxva_ctx->d3ddev);
+    if (dxva_ctx->d3dobj)
+        IDirect3D9_Release(dxva_ctx->d3dobj);
+}
+
+#if 0
+static int DxResetVideoDecoder(void)
+{
+    ERR("DxResetVideoDecoder unimplemented\n");
+    return -1;
+}
+#endif
+
+static void split_planes(uint8_t *dstu, size_t dstu_pitch,
+                        uint8_t *dstv, size_t dstv_pitch,
+                        const uint8_t *src, size_t src_pitch,
+                        unsigned width, unsigned height)
+{
+    unsigned int y = 0;
+    unsigned int x = 0;
+
+    for (y = 0; y < height; y++) {
+        for (x = 0; x < width; x++) {
+            dstu[x] = src[2*x+0];
+            dstv[x] = src[2*x+1];
+        }
+        src  += src_pitch;
+        dstu += dstu_pitch;
+        dstv += dstv_pitch;
+    }
+}
+
+static void copy_plane(uint8_t *dst, size_t dst_pitch,
+                      const uint8_t *src, size_t src_pitch,
+                      unsigned width, unsigned height)
+{
+    unsigned int y;
+#ifndef TESTING1
+    for (y = 0; y < height; y++) {
+        memcpy(dst, src, width);
+        src += src_pitch;
+        dst += dst_pitch;
+    }
+#else
+    memcpy(dst, src, width * height);
+#endif
+}
+
+static void copy_yv12(uint8_t *dst[3], size_t dst_pitch[3],
+                             uint8_t *src[3], size_t src_pitch[3],
+                             unsigned width, unsigned height)
+{
+     copy_plane(dst[0], dst_pitch[0],
+               src[0], src_pitch[0], width, height);
+     copy_plane(dst[1], dst_pitch[1],
+               src[2], src_pitch[2], width / 2, height / 2);
+     copy_plane(dst[2], dst_pitch[2],
+               src[1], src_pitch[1], width / 2, height / 2);
+}
+
+static void copy_nv12(uint8_t *dst[3], int linesizes[4],
+                             uint8_t *src[2], size_t src_pitch[2],
+                             unsigned width, unsigned height)
+{
+     copy_plane(dst[0], linesizes[0],
+               src[0], src_pitch[0], width, height);
+     split_planes(dst[1], linesizes[1], dst[2], linesizes[2],
+               src[1], src_pitch[1], width / 2, height / 2);
+}
+
+
+static void maru_dxva2_close(void)
+{
+    DxDestroyVideoDecoder();
+    DxDestroyVideoService();
+    D3dDestroyDeviceManager();
+    D3dDestroyDevice();
+
+    if (hdxva2_dll) {
+        FreeLibrary(hdxva2_dll);
+        hdxva2_dll = NULL;
+    }
+    if (hd3d9_dll) {
+        FreeLibrary(hd3d9_dll);
+        hd3d9_dll = NULL;
+    }
+
+    if (dxva_ctx->hw_context) {
+        free(dxva_ctx->hw_context);
+        dxva_ctx->hw_context = NULL;
+    }
+
+    //free(dxva_ctx);
+}
+
+static int maru_dxva2_open(AVCodecContext *dec_ctx)
+{
+    if (hd3d9_dll == NULL || hdxva2_dll == NULL) {
+        hd3d9_dll = LoadLibrary(TEXT("D3D9.DLL"));
+        if (!hd3d9_dll) {
+            ERR("cannot load d3d9.dll\n");
+            goto error;
+        }
+        hdxva2_dll = LoadLibrary(TEXT("DXVA2.DLL"));
+        if (!hdxva2_dll) {
+            ERR("cannot load dxva2.dll\n");
+            goto error;
+        }
+        TRACE("DLLs loaded\n");
+
+        if (D3dCreateDevice() < 0) {
+            ERR("Failed to create Direct3D device\n");
+            goto error;
+        }
+        TRACE("D3dCreateDevice succeed\n");
+
+        if (D3dCreateDeviceManager() < 0) {
+            ERR("D3dCreateDeviceManager failed\n");
+            goto error;
+        }
+
+        if (DxCreateVideoService() < 0) {
+            ERR("DxCreateVideoService failed\n");
+            goto error;
+        }
+    }
+
+    if (DxFindVideoServiceConversion(dec_ctx->codec_id)) {
+        ERR("DxFindVideoServiceConversion failed\n");
+        goto error;
+    }
+
+    dxva_ctx->thread_count = dec_ctx->thread_count;
+
+    return 0;
+
+error:
+    maru_dxva2_close();
+    return -1;
+}
+
+static bool dxva_setup(AVCodecContext *dec_ctx, int width, int height)
+{
+    if(maru_dxva2_open(dec_ctx) < 0) {
+        ERR("DXVA initialization failed\n");
+        return false;
+    }
+
+    //TODO: destroy surface and decoder at avcontext deinit()
+    DxDestroyVideoDecoder();
+
+    if (DxCreateVideoDecoder(dec_ctx) < -1) {
+        ERR("DxCreateVideoDecoder failed\n");
+        return false;
+    }
+
+    // prepare the libav hardware context
+    if (dxva_ctx->hw_context == NULL) {
+        dxva_ctx->hw_context = g_malloc0(sizeof(struct dxva_context));
+    }
+    dec_ctx->hwaccel_context = dxva_ctx->hw_context;
+
+    dxva_ctx->hw_context->decoder = dxva_ctx->decoder;
+    dxva_ctx->hw_context->cfg = &dxva_ctx->cfg;
+    dxva_ctx->hw_context->surface_count = dxva_ctx->surface_count;
+    dxva_ctx->hw_context->surface = dxva_ctx->hw_surface;
+
+    DxCreateVideoConversion();
+
+    return true;
+}
+
+static int dxva_get_surface(AVCodecContext *dec_ctx, AVFrame *frame)
+{
+    unsigned i = 0;
+    DXVAPluginSurface *surface = NULL;
+
+    /* Check the device */
+#if 0
+    HRESULT hr = IDirect3DDeviceManager9_TestDevice(dxva_ctx->devmng, dxva_ctx->hd3ddev);
+    if (hr == DXVA2_E_NEW_VIDEO_DEVICE) {
+        if (DxResetVideoDecoder())
+            return -1;
+    } else if (FAILED(hr)) {
+        ERR("IDirect3DDeviceManager9_TestDevice %u\n", (unsigned)hr);
+        return -1;
+    }
+#endif
+
+    for (i = 0; i < dxva_ctx->surface_count; ++i) {
+        surface = &dxva_ctx->surface[i];
+        if (surface->is_occupied) continue;
+
+        break;
+    }
+
+    surface->is_occupied = true;
+
+    frame->data[0] = (void *)surface->d3d;
+    frame->data[3] = frame->data[0];
+    frame->type = FF_BUFFER_TYPE_USER;
+    frame->opaque = surface;
+
+    return 0;
+}
+
+static void dxva_release_surface(AVCodecContext *dec_ctx, AVFrame *frame)
+{
+    DXVAPluginSurface *surface = NULL;
+    unsigned int i;
+
+    for (i = 0; i < 4; ++i) {
+        frame->data[i] = NULL;
+    }
+
+    surface = frame->opaque;
+    surface->is_occupied = false;
+}
+
+static int extract(AVFrame *src, void *dst, size_t size)
+{
+    LPDIRECT3DSURFACE9 d3d = (LPDIRECT3DSURFACE9)(uintptr_t)src->data[3];
+
+    /* */
+    assert(dxva_ctx->output == MAKEFOURCC('Y','V','1','2'));
+
+    /* */
+    D3DLOCKED_RECT lock;
+    if (FAILED(IDirect3DSurface9_LockRect(d3d, &lock, NULL, D3DLOCK_READONLY))) {
+        ERR("Failed to lock surface\n");
+        return -1;
+    }
+
+    if (dxva_ctx->render_fmt == MAKEFOURCC('Y','V','1','2') ||
+        dxva_ctx->render_fmt == MAKEFOURCC('I','M','C','3')) {
+        bool imc3 = dxva_ctx->render_fmt == MAKEFOURCC('I','M','C','3');
+        size_t chroma_pitch = imc3 ? lock.Pitch : (lock.Pitch / 2);
+
+        size_t pitch[3] = {
+            lock.Pitch,
+            chroma_pitch,
+            chroma_pitch,
+        };
+
+        uint8_t *plane[3] = {
+            (uint8_t*)lock.pBits,
+            (uint8_t*)lock.pBits + pitch[0] * dxva_ctx->surface_height,
+            (uint8_t*)lock.pBits + pitch[0] * dxva_ctx->surface_height
+                                 + pitch[1] * dxva_ctx->surface_height / 2,
+        };
+
+        if (imc3) {
+            uint8_t *V = plane[1];
+            plane[1] = plane[2];
+            plane[2] = V;
+        }
+
+        uint8_t *data[4];
+        av_image_fill_pointers(data, PIX_FMT_YUV420P, src->height, dst, (const int *)pitch);
+        copy_yv12(data, pitch, plane, pitch, src->width, src->height);
+    } else if (dxva_ctx->render_fmt == MAKEFOURCC('N','V','1','2')) {
+        uint8_t *plane[2] = {
+            lock.pBits,
+            (uint8_t*)lock.pBits + lock.Pitch * dxva_ctx->surface_height
+        };
+        size_t  pitch[2] = {
+            lock.Pitch,
+            lock.Pitch,
+        };
+
+        uint8_t *data[4];
+        int linesizes[4];
+        av_image_fill_linesizes(linesizes, AV_PIX_FMT_YUV420P, src->width);
+        av_image_fill_pointers(data, PIX_FMT_YUV420P, src->height, dst, linesizes);
+        copy_nv12(data, linesizes, plane, pitch, src->width, src->height);
+    } else {
+        ERR("Not supported format.(%x)\n", dxva_ctx->render_fmt);
+        IDirect3DSurface9_UnlockRect(d3d);
+        return -1;
+    }
+
+    /* */
+    IDirect3DSurface9_UnlockRect(d3d);
+    return 0;
+}
+
+static void dxva_extract(void *dst, void *src, size_t size) {
+    extract((AVFrame *)src, dst, size);
+}
+
+static void dxva_release(void *buf) {}
+
+static DataHandler dxva_video_decode_data_handler = {
+    .get_data = dxva_extract,
+    .release = dxva_release,
+};
+
+CodecPlugin dxva_plugin = {
+    .pix_fmt = PIX_FMT_DXVA2_VLD,
+    .setup = dxva_setup,
+    .get_buffer = dxva_get_surface,
+    .release_buffer = dxva_release_surface,
+    .video_decode_data_handler = &dxva_video_decode_data_handler,
+};