[vlc-devel] [PATCH] DXVA2: output an opaque pixel format with D3D9 surfaces internally

Steve Lhomme robux4 at videolabs.io
Mon Apr 20 15:56:03 CEST 2015


Add a filter to convert these surfaces to I420,YV12 & NV12 planes
D3D9 uses the same surface for its display picure_t
--
D3D9 creates a DXVA/D3D9 picture pool of the right size when used in DXVA
The D3D9 device/handle is shared between the decoder and the vout so surfaces are compatible and all is done in the GPU
The win32 DXVA hack is not needed anymore. DXVA is now thread safe and compatible with multithreaded frame decoding.
---
 include/vlc_fourcc.h                 |   5 +
 modules/codec/Makefile.am            |   2 +-
 modules/codec/avcodec/dxva2.c        | 533 ++++++++++++++++++++++++++---------
 modules/codec/avcodec/video.c        |  15 -
 modules/video_output/msw/direct3d9.c | 296 +++++++++++--------
 src/misc/fourcc.c                    |   3 +
 6 files changed, 589 insertions(+), 265 deletions(-)

diff --git a/include/vlc_fourcc.h b/include/vlc_fourcc.h
index 5d30ece..8eecd9e 100644
--- a/include/vlc_fourcc.h
+++ b/include/vlc_fourcc.h
@@ -332,6 +332,11 @@
 /* Broadcom MMAL opaque buffer type */
 #define VLC_CODEC_MMAL_OPAQUE     VLC_FOURCC('M','M','A','L')
 
+/* DXVA2 opaque video surface for use with D3D9 */
+#define VLC_CODEC_DXVA_N_OPAQUE  VLC_FOURCC('D','X','A','9')
+#define VLC_CODEC_DXVA_Y_OPAQUE  VLC_FOURCC('D','X','Y','9')
+#define VLC_CODEC_DXVA_I_OPAQUE  VLC_FOURCC('D','X','I','9')
+
 /* Image codec (video) */
 #define VLC_CODEC_PNG             VLC_FOURCC('p','n','g',' ')
 #define VLC_CODEC_PPM             VLC_FOURCC('p','p','m',' ')
diff --git a/modules/codec/Makefile.am b/modules/codec/Makefile.am
index 9c31230..0426b66 100644
--- a/modules/codec/Makefile.am
+++ b/modules/codec/Makefile.am
@@ -342,7 +342,7 @@ endif
 
 libdxva2_plugin_la_SOURCES = \
 	video_chroma/copy.c video_chroma/copy.h \
-	codec/avcodec/dxva2.c
+	codec/avcodec/dxva2.c codec/avcodec/dxva2.h
 libdxva2_plugin_la_LIBADD = -lole32 -lshlwapi -luuid
 if HAVE_AVCODEC_DXVA2
 codec_LTLIBRARIES += libdxva2_plugin.la
diff --git a/modules/codec/avcodec/dxva2.c b/modules/codec/avcodec/dxva2.c
index 23f575a..6df29c8 100644
--- a/modules/codec/avcodec/dxva2.c
+++ b/modules/codec/avcodec/dxva2.c
@@ -41,7 +41,8 @@
 #include <vlc_fourcc.h>
 #include <vlc_cpu.h>
 #include <vlc_plugin.h>
-#include <vlc_codecs.h>
+#include <vlc_codec.h>
+#include <vlc_filter.h>
 
 #include <libavcodec/avcodec.h>
 #    define DXVA2API_USE_BITFIELDS
@@ -51,16 +52,25 @@
 #include "avcodec.h"
 #include "va.h"
 #include "../../video_chroma/copy.h"
+#include "../../demux/asf/libasf_guid.h"
+#include "dxva2.h"
 
 static int Open(vlc_va_t *, AVCodecContext *, const es_format_t *);
 static void Close(vlc_va_t *, AVCodecContext *);
 
+static int  OpenConverter( vlc_object_t * );
+static void CloseConverter( vlc_object_t * );
+
 vlc_module_begin()
     set_description(N_("DirectX Video Acceleration (DXVA) 2.0"))
     set_capability("hw decoder", 0)
     set_category(CAT_INPUT)
     set_subcategory(SUBCAT_INPUT_VCODEC)
     set_callbacks(Open, Close)
+    add_submodule()
+        set_description( N_("DXA9 to I420,YV12,NV12") )
+        set_capability( "video filter2", 10 )
+        set_callbacks( OpenConverter, CloseConverter )
 vlc_module_end()
 
 #include <windows.h>
@@ -271,8 +281,8 @@ static const dxva2_mode_t *Dxva2FindMode(const GUID *guid)
 /* */
 typedef struct {
     const char   *name;
-    D3DFORMAT    format;
-    vlc_fourcc_t codec;
+    D3DFORMAT    format;    /* D3D format */
+    vlc_fourcc_t fourcc;    /* VLC fourcc */
 } d3d_format_t;
 /* XXX Prefered format must come first */
 static const d3d_format_t d3d_formats[] = {
@@ -292,14 +302,6 @@ static const d3d_format_t *D3dFindFormat(D3DFORMAT format)
     return NULL;
 }
 
-/* */
-typedef struct {
-    LPDIRECT3DSURFACE9 d3d;
-    int                refcount;
-    unsigned int       order;
-    vlc_mutex_t        *p_lock;
-} vlc_va_surface_t;
-
 #define VA_DXVA2_MAX_SURFACE_COUNT (64)
 struct vlc_va_sys_t
 {
@@ -329,7 +331,7 @@ struct vlc_va_sys_t
     /* Video service */
     IDirectXVideoDecoderService  *vs;
     GUID                         input;
-    D3DFORMAT                    render;
+    const d3d_format_t           *p_render;
 
     /* Video decoder */
     DXVA2_ConfigPictureDecode    cfg;
@@ -337,7 +339,6 @@ struct vlc_va_sys_t
 
     /* Option conversion */
     D3DFORMAT                    output;
-    copy_cache_t                 surface_cache;
 
     /* */
     struct dxva_context hw;
@@ -351,8 +352,13 @@ struct vlc_va_sys_t
 
     int          thread_count;
 
-    vlc_va_surface_t surface[VA_DXVA2_MAX_SURFACE_COUNT];
+    vlc_va_t          *va;
+
+    /* surfaces in the GPU user space */
+    picture_sys_t surface[VA_DXVA2_MAX_SURFACE_COUNT];
     LPDIRECT3DSURFACE9 hw_surface[VA_DXVA2_MAX_SURFACE_COUNT];
+
+    picture_pool_setup_sys_t pool_cookie;
 };
 
 /* */
@@ -365,15 +371,135 @@ static void D3dDestroyDeviceManager(vlc_va_sys_t *);
 
 static int DxCreateVideoService(vlc_va_t *);
 static void DxDestroyVideoService(vlc_va_sys_t *);
-static int DxFindVideoServiceConversion(vlc_va_t *, GUID *input, D3DFORMAT *output);
+static int DxFindVideoServiceConversion(vlc_va_t *, GUID *input, const d3d_format_t **output);
 
 static int DxCreateVideoDecoder(vlc_va_t *,
                                 int codec_id, const video_format_t *, bool);
 static void DxDestroyVideoDecoder(vlc_va_sys_t *);
 static int DxResetVideoDecoder(vlc_va_t *);
 
-static void DxCreateVideoConversion(vlc_va_sys_t *);
-static void DxDestroyVideoConversion(vlc_va_sys_t *);
+static void CopySurface( picture_t *p_dst, picture_t *p_src )
+{
+    picture_sys_t *p_src_sys = p_src->p_sys;
+    picture_sys_t *p_dst_sys = p_dst->p_sys;
+
+    if (!p_dst_sys)
+        return;
+
+    LPDIRECT3DSURFACE9 source = p_src_sys->surface;
+    LPDIRECT3DSURFACE9 output = p_dst_sys->surface;
+
+    HRESULT hr = IDirect3DDevice9_StretchRect( p_src_sys->surface_device, source, NULL, output, NULL, D3DTEXF_NONE);
+    if (FAILED(hr)) {
+        //msg_Err( p_src_sys->p_va, "Failed to copy the hw surface to the decoder surface (hr=0x%0lx)", hr );
+    }
+}
+
+void DestroySurface(picture_t *p_pic)
+{
+    picture_sys_t *p_pic_sys = p_pic->p_sys;
+    IDirect3DSurface9_Release( p_pic_sys->surface );
+    free( p_pic_sys );
+    p_pic->p_sys = NULL;
+}
+
+static int LockOutputSurface(picture_t *p_pic)
+{
+    IDirect3DSurface9_AddRef( p_pic->p_sys->surface );
+    return VLC_SUCCESS;
+}
+
+static void UnlockOutputSurface(picture_t *p_pic)
+{
+    IDirect3DSurface9_Release( p_pic->p_sys->surface );
+}
+
+static void DestroySurfacePool(void *p_pool_gc_sys)
+{
+    picture_pool_configuration_t *p_pool = p_pool_gc_sys;
+    free( p_pool );
+}
+
+static picture_pool_configuration_t *CreateSurfacePoolConfig(picture_pool_setup_sys_t *p_sys, const video_format_t *fmt, unsigned count)
+{
+    vlc_va_sys_t *sys = p_sys->p_va_sys;
+    LPDIRECT3DSURFACE9 hw_surfaces[count];
+    picture_sys_t *p_sys_pictures[count];
+    picture_pool_configuration_t *p_pool = NULL;
+    picture_t **pictures = NULL;
+
+    memset(p_sys_pictures, 0, count * sizeof(picture_sys_t *));
+
+    p_pool = calloc(1, sizeof(picture_pool_configuration_t));
+    if ( p_pool == NULL )
+        goto error;
+
+    pictures = calloc(count, sizeof(picture_t*));
+    if ( pictures == NULL )
+        goto error;
+
+    if (FAILED(IDirectXVideoDecoderService_CreateSurface(sys->vs,
+                                                         sys->surface_width,
+                                                         sys->surface_height,
+                                                         count - 1,
+                                                         sys->p_render->format,
+                                                         D3DPOOL_DEFAULT,
+                                                         0,
+                                                         DXVA2_VideoDecoderRenderTarget,
+                                                         hw_surfaces,
+                                                         NULL))) {
+        msg_Err(sys->va, "IDirectXVideoAccelerationService_CreateSurface display failed");
+        goto error;
+    }
+    p_pool->picture_count = count;
+
+    for (unsigned i = 0; i < count; ++i)
+    {
+        p_sys_pictures[i] = calloc(1, sizeof(picture_sys_t));
+        if ( p_sys_pictures[i] == NULL )
+            goto error;
+        p_sys_pictures[i]->surface = hw_surfaces[i];
+        p_sys_pictures[i]->surface_device = sys->d3ddev;
+        p_sys_pictures[i]->index = i;
+        if ( sys->b_thread_safe )
+            p_sys_pictures[i]->p_lock = &sys->surface_lock;
+
+        picture_resource_t pic_resource = {
+            .p_sys = p_sys_pictures[i],
+            .pf_destroy = DestroySurface,
+        };
+        pictures[i] = picture_NewFromResource( fmt, &pic_resource );
+        if ( pictures[i] == NULL)
+            goto error;
+    }
+
+    p_pool->picture = pictures;
+    p_pool->lock = LockOutputSurface;
+    p_pool->unlock = UnlockOutputSurface;
+    p_pool->gc.pf_destroy = DestroySurfacePool;
+    p_pool->gc.p_sys = p_pool;
+
+    return p_pool;
+
+error:
+    for (unsigned i = 0; i < p_pool->picture_count; ++i)
+    {
+        IDirect3DSurface9_Release( hw_surfaces[i] );
+    }
+    for (unsigned i = 0; i < count; ++i)
+    {
+        if (p_sys_pictures != NULL && p_sys_pictures[i] != NULL)
+            free( p_sys_pictures[i] );
+        if (pictures != NULL && pictures[i] != NULL)
+            picture_Release( pictures[i] );
+    }
+    if ( pictures != NULL )
+        free( pictures );
+    if ( p_pool != NULL )
+        free( p_pool );
+
+    return NULL;
+}
 
 /* */
 static int Setup(vlc_va_t *va, AVCodecContext *avctx, vlc_fourcc_t *chroma, picture_pool_setup_t *output_init)
@@ -385,7 +511,6 @@ static int Setup(vlc_va_t *va, AVCodecContext *avctx, vlc_fourcc_t *chroma, pict
         goto ok;
 
     /* */
-    DxDestroyVideoConversion(sys);
     DxDestroyVideoDecoder(sys);
 
     avctx->hwaccel_context = NULL;
@@ -408,78 +533,76 @@ static int Setup(vlc_va_t *va, AVCodecContext *avctx, vlc_fourcc_t *chroma, pict
     sys->hw.surface = sys->hw_surface;
 
     /* */
-    DxCreateVideoConversion(sys);
+    sys->output = sys->p_render->format;
 
     /* */
 ok:
+    switch(sys->p_render->format)
+    {
+    case MAKEFOURCC('N','V','1','2'):
+        *chroma = VLC_CODEC_DXVA_N_OPAQUE;
+        break;
+    case MAKEFOURCC('Y','V','1','2'):
+        *chroma = VLC_CODEC_DXVA_Y_OPAQUE;
+        break;
+    case MAKEFOURCC('I','M','C','3'):
+        *chroma = VLC_CODEC_DXVA_I_OPAQUE;
+        break;
+    default:
+        return VLC_EGENERIC;
+    }
     avctx->hwaccel_context = &sys->hw;
-    const d3d_format_t *output = D3dFindFormat(sys->output);
-    *chroma = output->codec;
+    output_init->pf_create_config = CreateSurfacePoolConfig;
+    sys->pool_cookie.p_va_sys = sys;
+    sys->pool_cookie.d3ddev = sys->d3ddev;
+    sys->pool_cookie.d3dobj = sys->d3dobj;
+    sys->pool_cookie.d3dpp = sys->d3dpp;
+    output_init->p_sys = &sys->pool_cookie;
 
     return VLC_SUCCESS;
 }
 
+struct dxva_opaque
+{
+    picture_sys_t *p_dxva_surface;
+    vlc_va_sys_t  *p_sys;
+};
+
 static int Extract(vlc_va_t *va, picture_t *picture, void *opaque,
                    uint8_t *data)
 {
-    vlc_va_sys_t *sys = va->sys;
-    LPDIRECT3DSURFACE9 d3d = (LPDIRECT3DSURFACE9)(uintptr_t)data;
+    struct dxva_opaque *p_output = opaque;
 
-    if (!sys->surface_cache.buffer)
-        return VLC_EGENERIC;
+    vlc_va_sys_t *sys = va->sys;
+    LPDIRECT3DSURFACE9 source = (LPDIRECT3DSURFACE9)(uintptr_t)data;
 
-    /* */
-    assert(sys->output == MAKEFOURCC('Y','V','1','2'));
+    LPDIRECT3DSURFACE9 output = picture->p_sys->surface;
 
-    /* */
-    D3DLOCKED_RECT lock;
-    if (FAILED(IDirect3DSurface9_LockRect(d3d, &lock, NULL, D3DLOCK_READONLY))) {
-        msg_Err(va, "Failed to lock surface");
-        return VLC_EGENERIC;
-    }
+    assert(source == p_output->p_dxva_surface->surface);
 
-    if (sys->render == MAKEFOURCC('Y','V','1','2') ||
-        sys->render == MAKEFOURCC('I','M','C','3')) {
-        bool imc3 = sys->render == MAKEFOURCC('I','M','C','3');
-        size_t chroma_pitch = imc3 ? lock.Pitch : (lock.Pitch / 2);
+    picture->pf_copy_private = CopySurface;
 
-        size_t pitch[3] = {
-            lock.Pitch,
-            chroma_pitch,
-            chroma_pitch,
-        };
-
-        uint8_t *plane[3] = {
-            (uint8_t*)lock.pBits,
-            (uint8_t*)lock.pBits + pitch[0] * sys->surface_height,
-            (uint8_t*)lock.pBits + pitch[0] * sys->surface_height
-                                 + pitch[1] * sys->surface_height / 2,
-        };
+    /* */
+    if ( picture->p_sys->p_lock )
+        vlc_mutex_lock( picture->p_sys->p_lock );
 
-        if (imc3) {
-            uint8_t *V = plane[1];
-            plane[1] = plane[2];
-            plane[2] = V;
-        }
-        CopyFromYv12(picture, plane, pitch, sys->width, sys->height,
-                     &sys->surface_cache);
-    } else {
-        assert(sys->render == MAKEFOURCC('N','V','1','2'));
-        uint8_t *plane[2] = {
-            lock.pBits,
-            (uint8_t*)lock.pBits + lock.Pitch * sys->surface_height
-        };
-        size_t  pitch[2] = {
-            lock.Pitch,
-            lock.Pitch,
-        };
-        CopyFromNv12(picture, plane, pitch, sys->width, sys->height,
-                     &sys->surface_cache);
+    HRESULT hr;
+#if 0
+    RECT outputRect;
+    outputRect.top = 0;
+    outputRect.left = 0;
+    outputRect.right = picture->format.i_visible_width;
+    outputRect.bottom = picture->format.i_visible_height;
+#endif
+    hr = IDirect3DDevice9_StretchRect( sys->d3ddev, source, NULL, output, NULL, D3DTEXF_NONE);
+    if (FAILED(hr)) {
+        msg_Err(va, "Failed to copy the hw surface to the decoder surface (hr=0x%0lx)", hr );
     }
 
     /* */
-    IDirect3DSurface9_UnlockRect(d3d);
-    (void) opaque;
+    if ( picture->p_sys->p_lock )
+        vlc_mutex_unlock( picture->p_sys->p_lock );
+
     return VLC_SUCCESS;
 }
 
@@ -498,30 +621,31 @@ static int Get(vlc_va_t *va, void **opaque, uint8_t **data)
         return VLC_EGENERIC;
     }
 
+    struct dxva_opaque *p_output = malloc(sizeof(*p_output));
+    *opaque = p_output;
+    p_output->p_sys = sys;
+
     if ( sys->b_thread_safe )
         vlc_mutex_lock( &sys->surface_lock );
 
     /* Grab an unused surface, in case none are, try the oldest
      * XXX using the oldest is a workaround in case a problem happens with libavcodec */
-    unsigned i, old;
-    for (i = 0, old = 0; i < sys->surface_count; i++) {
-        vlc_va_surface_t *surface = &sys->surface[i];
-
-        if (!surface->refcount)
-            break;
-
-        if (surface->order < sys->surface[old].order)
-            old = i;
+    unsigned i;
+    picture_sys_t *oldest = NULL;
+    for (i = 0; i < sys->surface_count; i++) {
+        picture_sys_t *surface = &sys->surface[i];
+        if (!surface->refcount && (!oldest || surface->order < oldest->order))
+           oldest = surface;
     }
-    if (i >= sys->surface_count)
-        i = old;
-
-    vlc_va_surface_t *surface = &sys->surface[i];
-
-    surface->refcount = 1;
-    surface->order = sys->surface_order++;
-    *data = (void *)surface->d3d;
-    *opaque = surface;
+    if ( oldest == NULL )
+    {
+        msg_Warn(va, "%lx could not find an unused surface, use oldest index", GetCurrentThreadId());
+        oldest = &sys->surface[0];
+    }
+    *data = (void *)oldest->surface;
+    p_output->p_dxva_surface = oldest;
+    p_output->p_dxva_surface->refcount++;
+    assert( p_output->p_dxva_surface->refcount == 1 );
 
     if ( sys->b_thread_safe )
         vlc_mutex_unlock( &sys->surface_lock );
@@ -531,23 +655,27 @@ static int Get(vlc_va_t *va, void **opaque, uint8_t **data)
 
 static void Release(void *opaque, uint8_t *data)
 {
-    vlc_va_surface_t *surface = opaque;
-    if ( surface->p_lock )
-        vlc_mutex_lock( surface->p_lock );
+    VLC_UNUSED( data );
+
+    struct dxva_opaque *p_output = opaque;
+    if ( p_output->p_dxva_surface->p_lock )
+        vlc_mutex_lock( p_output->p_dxva_surface->p_lock );
 
-    surface->refcount--;
-    (void) data;
+    p_output->p_dxva_surface->refcount--;
+    p_output->p_dxva_surface->order = p_output->p_sys->surface_order++;
 
-    if ( surface->p_lock )
-        vlc_mutex_unlock( surface->p_lock );
+    if ( p_output->p_dxva_surface->p_lock )
+        vlc_mutex_unlock( p_output->p_dxva_surface->p_lock );
+
+    free( p_output );
 }
 
 static void Close(vlc_va_t *va, AVCodecContext *ctx)
 {
+    VLC_UNUSED( ctx );
+
     vlc_va_sys_t *sys = va->sys;
 
-    (void) ctx;
-    DxDestroyVideoConversion(sys);
     DxDestroyVideoDecoder(sys);
     DxDestroyVideoService(sys);
     D3dDestroyDeviceManager(sys);
@@ -566,6 +694,8 @@ static void Close(vlc_va_t *va, AVCodecContext *ctx)
 
 static int Open(vlc_va_t *va, AVCodecContext *ctx, const es_format_t *fmt)
 {
+    VLC_UNUSED( fmt );
+
     vlc_va_sys_t *sys = calloc(1, sizeof (*sys));
     if (unlikely(sys == NULL))
         return VLC_ENOMEM;
@@ -609,7 +739,7 @@ static int Open(vlc_va_t *va, AVCodecContext *ctx, const es_format_t *fmt)
     }
 
     /* */
-    if (DxFindVideoServiceConversion(va, &sys->input, &sys->render)) {
+    if (DxFindVideoServiceConversion(va, &sys->input, &sys->p_render)) {
         msg_Err(va, "DxFindVideoServiceConversion failed");
         goto error;
     }
@@ -663,6 +793,35 @@ static int D3dCreateDevice(vlc_va_t *va)
         ZeroMemory(d3dai, sizeof(*d3dai));
     }
 
+    /*
+    ** Get the current desktop display mode, so we can set up a back
+    ** buffer of the same format
+    */
+    D3DDISPLAYMODE d3ddm;
+    HRESULT hr = IDirect3D9_GetAdapterDisplayMode(sys->d3dobj,
+                                                  D3DADAPTER_DEFAULT, &d3ddm);
+    if (FAILED(hr)) {
+       msg_Err(va, "Could not read adapter display mode. (hr=0x%0lx)", hr);
+       return VLC_EGENERIC;
+    }
+
+    UINT AdapterToUse = D3DADAPTER_DEFAULT;
+    D3DDEVTYPE DeviceType = D3DDEVTYPE_HAL;
+
+#ifndef NDEBUG
+    // Look for 'NVIDIA PerfHUD' adapter
+    // If it is present, override default settings
+    for (UINT Adapter=0; Adapter< IDirect3D9_GetAdapterCount(d3dobj); ++Adapter) {
+        D3DADAPTER_IDENTIFIER9 Identifier;
+        HRESULT Res = IDirect3D9_GetAdapterIdentifier(d3dobj,Adapter,0,&Identifier);
+        if (SUCCEEDED(Res) && strstr(Identifier.Description,"PerfHUD") != 0) {
+            AdapterToUse = Adapter;
+            DeviceType = D3DDEVTYPE_REF;
+            break;
+        }
+    }
+#endif
+
     /* */
     D3DPRESENT_PARAMETERS *d3dpp = &sys->d3dpp;
     ZeroMemory(d3dpp, sizeof(*d3dpp));
@@ -672,18 +831,20 @@ static int D3dCreateDevice(vlc_va_t *va)
     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->BackBufferCount        = 1;
+    d3dpp->BackBufferFormat       = d3ddm.Format;
+    d3dpp->BackBufferWidth        = __MAX((unsigned int)GetSystemMetrics(SM_CXVIRTUALSCREEN),
+                                          d3ddm.Width);
+    d3dpp->BackBufferHeight       = __MAX((unsigned int)GetSystemMetrics(SM_CYVIRTUALSCREEN),
+                                          d3ddm.Height);
     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(),
+    if (FAILED(IDirect3D9_CreateDevice(d3dobj, AdapterToUse,
+                                       DeviceType, d3dpp->hDeviceWindow,
                                        D3DCREATE_SOFTWARE_VERTEXPROCESSING |
                                        D3DCREATE_MULTITHREADED,
                                        d3dpp, &d3ddev))) {
@@ -838,7 +999,7 @@ static void DxDestroyVideoService(vlc_va_sys_t *va)
 /**
  * Find the best suited decoder mode GUID and render format.
  */
-static int DxFindVideoServiceConversion(vlc_va_t *va, GUID *input, D3DFORMAT *output)
+static int DxFindVideoServiceConversion(vlc_va_t *va, GUID *input, const d3d_format_t **output)
 {
     vlc_va_sys_t *sys = va->sys;
 
@@ -951,7 +1112,7 @@ static int DxFindVideoServiceConversion(vlc_va_t *va, GUID *input, D3DFORMAT *ou
             /* We have our solution */
             msg_Dbg(va, "Using '%s' to decode to '%s'", mode->name, format->name);
             *input  = *mode->guid;
-            *output = format->format;
+            *output = format;
             CoTaskMemFree(output_list);
             CoTaskMemFree(input_list);
             return VLC_SUCCESS;
@@ -1018,7 +1179,7 @@ static int DxCreateVideoDecoder(vlc_va_t *va, int codec_id,
                                                          sys->surface_width,
                                                          sys->surface_height,
                                                          sys->surface_count - 1,
-                                                         sys->render,
+                                                         sys->p_render->format,
                                                          D3DPOOL_DEFAULT,
                                                          0,
                                                          DXVA2_VideoDecoderRenderTarget,
@@ -1029,13 +1190,16 @@ static int DxCreateVideoDecoder(vlc_va_t *va, int codec_id,
         return VLC_EGENERIC;
     }
     for (unsigned i = 0; i < sys->surface_count; i++) {
-        vlc_va_surface_t *surface = &sys->surface[i];
-        surface->d3d = sys->hw_surface[i];
-        surface->refcount = 0;
-        surface->order = 0;
+        picture_sys_t *p_picture = &sys->surface[i];
+        p_picture->surface = sys->hw_surface[i];
+        p_picture->surface_device = sys->d3ddev;
+        p_picture->refcount = 0;
+        p_picture->index = i;
+        p_picture->order = i;
         if ( sys->b_thread_safe )
-            surface->p_lock = &sys->surface_lock;
+            p_picture->p_lock = &sys->surface_lock;
     }
+    sys->surface_order = sys->surface_count;
     msg_Dbg(va, "IDirectXVideoAccelerationService_CreateSurface succeed with %d surfaces (%dx%d)",
             sys->surface_count, fmt->i_width, fmt->i_height);
 
@@ -1044,7 +1208,7 @@ static int DxCreateVideoDecoder(vlc_va_t *va, int codec_id,
     ZeroMemory(&dsc, sizeof(dsc));
     dsc.SampleWidth     = fmt->i_width;
     dsc.SampleHeight    = fmt->i_height;
-    dsc.Format          = sys->render;
+    dsc.Format          = sys->p_render->format;
     if (fmt->i_frame_rate > 0 && fmt->i_frame_rate_base > 0) {
         dsc.InputSampleFreq.Numerator   = fmt->i_frame_rate;
         dsc.InputSampleFreq.Denominator = fmt->i_frame_rate_base;
@@ -1139,7 +1303,7 @@ static void DxDestroyVideoDecoder(vlc_va_sys_t *va)
     va->decoder = NULL;
 
     for (unsigned i = 0; i < va->surface_count; i++)
-        IDirect3DSurface9_Release(va->surface[i].d3d);
+        IDirect3DSurface9_Release( va->hw_surface[i] );
     va->surface_count = 0;
 }
 
@@ -1149,21 +1313,138 @@ static int DxResetVideoDecoder(vlc_va_t *va)
     return VLC_EGENERIC;
 }
 
-static void DxCreateVideoConversion(vlc_va_sys_t *va)
+static void NV12_I420 (filter_t *p_filter, picture_t *src, picture_t *dst)
 {
-    switch (va->render) {
-    case MAKEFOURCC('N','V','1','2'):
-    case MAKEFOURCC('I','M','C','3'):
-        va->output = MAKEFOURCC('Y','V','1','2');
-        break;
-    default:
-        va->output = va->render;
-        break;
+    copy_cache_t *p_copy_cache = (copy_cache_t*) p_filter->p_sys;
+
+    LPDIRECT3DSURFACE9 d3d = src->p_sys->surface;
+
+    /* */
+    D3DLOCKED_RECT lock;
+    if (FAILED(IDirect3DSurface9_LockRect(d3d, &lock, NULL, D3DLOCK_READONLY))) {
+        msg_Err(p_filter, "Failed to lock surface");
+        return;
     }
-    CopyInitCache(&va->surface_cache, va->surface_width);
+
+    uint8_t *plane[2] = {
+        lock.pBits,
+        (uint8_t*)lock.pBits + lock.Pitch * src->format.i_height
+    };
+    size_t  pitch[2] = {
+        lock.Pitch,
+        lock.Pitch,
+    };
+    CopyFromNv12(dst, plane, pitch, src->format.i_width, src->format.i_visible_height,
+                 p_copy_cache);
+
+    /* */
+    IDirect3DSurface9_UnlockRect(d3d);
+}
+
+static void YV12_I420 (filter_t *p_filter, picture_t *src, picture_t *dst)
+{
+    copy_cache_t *p_copy_cache = (copy_cache_t*) p_filter->p_sys;
+
+    LPDIRECT3DSURFACE9 d3d = src->p_sys->surface;
+
+    /* */
+    D3DLOCKED_RECT lock;
+    if (FAILED(IDirect3DSurface9_LockRect(d3d, &lock, NULL, D3DLOCK_READONLY))) {
+        msg_Err(p_filter, "Failed to lock surface");
+        return;
+    }
+
+    size_t pitch[3] = {
+        lock.Pitch,
+        lock.Pitch / 2,
+        lock.Pitch / 2,
+    };
+
+    uint8_t *plane[3] = {
+        (uint8_t*)lock.pBits,
+        (uint8_t*)lock.pBits + pitch[0] * src->format.i_height,
+        (uint8_t*)lock.pBits + pitch[0] * src->format.i_height
+                             + pitch[1] * src->format.i_height / 2,
+    };
+
+    CopyFromYv12(dst, plane, pitch, src->format.i_width, src->format.i_visible_height,
+                 p_copy_cache);
+
+    /* */
+    IDirect3DSurface9_UnlockRect(d3d);
+}
+
+static void IMC3_I420 (filter_t *p_filter, picture_t *src, picture_t *dst)
+{
+    copy_cache_t *p_copy_cache = (copy_cache_t*) p_filter->p_sys;
+
+    LPDIRECT3DSURFACE9 d3d = src->p_sys->surface;
+
+    /* */
+    D3DLOCKED_RECT lock;
+    if (FAILED(IDirect3DSurface9_LockRect(d3d, &lock, NULL, D3DLOCK_READONLY))) {
+        msg_Err(p_filter, "Failed to lock surface");
+        return;
+    }
+
+    size_t pitch[3] = {
+        lock.Pitch,
+        lock.Pitch,
+        lock.Pitch,
+    };
+
+    uint8_t *plane[3] = {
+        (uint8_t*)lock.pBits,
+        (uint8_t*)lock.pBits + pitch[0] * src->format.i_height
+                             + pitch[1] * src->format.i_height / 2,
+        (uint8_t*)lock.pBits + pitch[0] * src->format.i_height,
+    };
+
+    CopyFromYv12(dst, plane, pitch, src->format.i_width, src->format.i_visible_height,
+                 p_copy_cache);
+
+    /* */
+    IDirect3DSurface9_UnlockRect(d3d);
+}
+
+
+VIDEO_FILTER_WRAPPER (NV12_I420)
+VIDEO_FILTER_WRAPPER (YV12_I420)
+VIDEO_FILTER_WRAPPER (IMC3_I420)
+
+static int OpenConverter( vlc_object_t *obj )
+{
+    filter_t *p_filter = (filter_t *)obj;
+    if ( p_filter->fmt_in.video.i_height != p_filter->fmt_out.video.i_height
+         || p_filter->fmt_in.video.i_width != p_filter->fmt_out.video.i_width )
+        return VLC_EGENERIC;
+
+    if ( p_filter->fmt_out.video.i_chroma != VLC_CODEC_I420
+         && p_filter->fmt_out.video.i_chroma != VLC_CODEC_YV12
+         && p_filter->fmt_out.video.i_chroma != VLC_CODEC_NV12 )
+        return VLC_EGENERIC;
+
+    if ( p_filter->fmt_in.video.i_chroma == VLC_CODEC_DXVA_N_OPAQUE )
+        p_filter->pf_video_filter = NV12_I420_Filter;
+    else if ( p_filter->fmt_in.video.i_chroma == VLC_CODEC_DXVA_Y_OPAQUE )
+        p_filter->pf_video_filter = YV12_I420_Filter;
+    else if ( p_filter->fmt_in.video.i_chroma == VLC_CODEC_DXVA_I_OPAQUE )
+        p_filter->pf_video_filter = IMC3_I420_Filter;
+    else
+        return VLC_EGENERIC;
+
+    copy_cache_t *p_copy_cache = calloc(1, sizeof(*p_copy_cache));
+    CopyInitCache(p_copy_cache, p_filter->fmt_in.video.i_width );
+    p_filter->p_sys = (filter_sys_t*) p_copy_cache;
+
+    return VLC_SUCCESS;
 }
 
-static void DxDestroyVideoConversion(vlc_va_sys_t *va)
+static void CloseConverter( vlc_object_t *obj )
 {
-    CopyCleanCache(&va->surface_cache);
+    filter_t *p_filter = (filter_t *)obj;
+    copy_cache_t *p_copy_cache = (copy_cache_t*) p_filter->p_sys;
+    CopyCleanCache(p_copy_cache);
+    free( p_copy_cache );
+    p_filter->p_sys = NULL;
 }
diff --git a/modules/codec/avcodec/video.c b/modules/codec/avcodec/video.c
index fbbd719..1790587 100644
--- a/modules/codec/avcodec/video.c
+++ b/modules/codec/avcodec/video.c
@@ -396,21 +396,6 @@ int InitVideoDec( decoder_t *p_dec, AVCodecContext *p_context,
             break;
     }
 
-    /* Workaround: frame multithreading is not compatible with
-     * DXVA2. When a frame is being copied to host memory, the frame
-     * is locked and cannot be used as a reference frame
-     * simultaneously and thus decoding fails for some frames. This
-     * causes major image corruption. */
-# if defined(_WIN32)
-    char *avcodec_hw = var_InheritString( p_dec, "avcodec-hw" );
-    if( avcodec_hw == NULL || strcasecmp( avcodec_hw, "none" ) )
-    {
-        msg_Warn( p_dec, "threaded frame decoding is not compatible with DXVA2, disabled" );
-        p_context->thread_type &= ~FF_THREAD_FRAME;
-    }
-    free( avcodec_hw );
-# endif
-
     if( p_context->thread_type & FF_THREAD_FRAME )
         p_dec->i_extra_picture_buffers = 2 * p_context->thread_count;
 #endif
diff --git a/modules/video_output/msw/direct3d9.c b/modules/video_output/msw/direct3d9.c
index db96275..db6a1ed 100644
--- a/modules/video_output/msw/direct3d9.c
+++ b/modules/video_output/msw/direct3d9.c
@@ -50,6 +50,7 @@
 
 #include "common.h"
 #include "builtin_shaders.h"
+#include "../../codec/avcodec/dxva2.h"
 
 /*****************************************************************************
  * Module descriptor
@@ -104,12 +105,6 @@ static const vlc_fourcc_t d3d_subpicture_chromas[] = {
     0
 };
 
-struct picture_sys_t
-{
-    LPDIRECT3DSURFACE9 surface;
-    picture_t          *fallback;
-};
-
 static int  Open(vlc_object_t *);
 
 static picture_pool_t *Pool  (vout_display_t *, unsigned);
@@ -153,6 +148,9 @@ static void Direct3D9RenderScene(vout_display_t *vd, d3d_region_t *, int, d3d_re
 /* */
 static int DesktopCallback(vlc_object_t *, char const *, vlc_value_t, vlc_value_t, void *);
 
+#define format_is_DXVA(fmt) (fmt == VLC_CODEC_DXVA_N_OPAQUE \
+    || fmt == VLC_CODEC_DXVA_Y_OPAQUE || fmt == VLC_CODEC_DXVA_I_OPAQUE)
+
 /**
  * It creates a Direct3D vout display.
  */
@@ -202,10 +200,10 @@ static int Open(vlc_object_t *object)
 
     /* */
     vout_display_info_t info = vd->info;
-    info.is_slow = true;
+    info.is_slow = !format_is_DXVA( fmt.i_chroma );
     info.has_double_click = true;
     info.has_hide_mouse = false;
-    info.has_pictures_invalid = true;
+    info.has_pictures_invalid = !format_is_DXVA( fmt.i_chroma );
     info.has_event_thread = true;
     if (var_InheritBool(vd, "direct3d9-hw-blending") &&
         sys->d3dregion_format != D3DFMT_UNKNOWN &&
@@ -274,7 +272,18 @@ static void Close(vlc_object_t *object)
 /* */
 static picture_pool_t *Pool(vout_display_t *vd, unsigned count)
 {
-    VLC_UNUSED(count);
+    if ( vd->sys->pool == NULL )
+    {
+        const picture_pool_configuration_t *conf = NULL;
+        if ( vd->sys->pool == NULL && vd->cfg->p_pool_setup->pf_create_config)
+            conf = vd->cfg->p_pool_setup->pf_create_config( vd->cfg->p_pool_setup->p_sys, &vd->fmt, count);
+
+        if ( conf != NULL )
+            vd->sys->pool = picture_pool_NewExtended( conf );
+        else
+            vd->sys->pool = picture_pool_NewFromFormat( &vd->fmt, count);
+    }
+
     return vd->sys->pool;
 }
 
@@ -283,21 +292,18 @@ static void Direct3D9UnlockSurface(picture_t *);
 
 static void Prepare(vout_display_t *vd, picture_t *picture, subpicture_t *subpicture)
 {
+    VLC_UNUSED(subpicture);
+
     vout_display_sys_t *sys = vd->sys;
     LPDIRECT3DSURFACE9 surface = picture->p_sys->surface;
-#if 0
-    picture_Release(picture);
-    VLC_UNUSED(subpicture);
-#else
+
     /* FIXME it is a bit ugly, we need the surface to be unlocked for
      * rendering.
      *  The clean way would be to release the picture (and ensure that
      * the vout doesn't keep a reference). But because of the vout
      * wrapper, we can't */
-
-    Direct3D9UnlockSurface(picture);
-    VLC_UNUSED(subpicture);
-#endif
+    if ( !format_is_DXVA( picture->format.i_chroma ) )
+        Direct3D9UnlockSurface(picture);
 
     /* check if device is still available */
     HRESULT hr = IDirect3DDevice9_TestCooperativeLevel(sys->d3ddev);
@@ -335,6 +341,8 @@ static void Prepare(vout_display_t *vd, picture_t *picture, subpicture_t *subpic
 
 static void Display(vout_display_t *vd, picture_t *picture, subpicture_t *subpicture)
 {
+    VLC_UNUSED(subpicture);
+
     vout_display_sys_t *sys = vd->sys;
     LPDIRECT3DDEVICE9 d3ddev = sys->d3ddev;
 
@@ -349,19 +357,15 @@ static void Display(vout_display_t *vd, picture_t *picture, subpicture_t *subpic
     // No stretching should happen here !
     const RECT src = sys->rect_dest_clipped;
     const RECT dst = sys->rect_dest_clipped;
-    HRESULT hr = IDirect3DDevice9_Present(d3ddev, &src, &dst, NULL, NULL);
+    HRESULT hr = IDirect3DDevice9_Present(d3ddev, &src, &dst, sys->hvideownd, NULL);
     if (FAILED(hr)) {
         msg_Dbg(vd, "Failed IDirect3DDevice9_Present: 0x%0lx", hr);
     }
 
-#if 0
-    VLC_UNUSED(picture);
-    VLC_UNUSED(subpicture);
-#else
     /* XXX See Prepare() */
-    Direct3D9LockSurface(picture);
+    if ( !format_is_DXVA( picture->format.i_chroma ) )
+        Direct3D9LockSurface(picture);
     picture_Release(picture);
-#endif
     if (subpicture)
         subpicture_Delete(subpicture);
 
@@ -534,21 +538,33 @@ static int Direct3D9Create(vout_display_t *vd)
         return VLC_EGENERIC;
     }
 
-    LPDIRECT3D9 (WINAPI *OurDirect3DCreate9)(UINT SDKVersion);
-    OurDirect3DCreate9 =
-        (void *)GetProcAddress(sys->hd3d9_dll, "Direct3DCreate9");
-    if (!OurDirect3DCreate9) {
-        msg_Err(vd, "Cannot locate reference to Direct3DCreate9 ABI in DLL");
-        return VLC_EGENERIC;
+    if ( format_is_DXVA( vd->fmt.i_chroma ) &&
+         vd->cfg->p_pool_setup != NULL &&
+         vd->cfg->p_pool_setup->p_sys != NULL )
+    {
+        sys->d3dobj = vd->cfg->p_pool_setup->p_sys->d3dobj;
     }
 
-    /* Create the D3D object. */
-    LPDIRECT3D9 d3dobj = OurDirect3DCreate9(D3D_SDK_VERSION);
-    if (!d3dobj) {
-       msg_Err(vd, "Could not create Direct3D9 instance.");
-       return VLC_EGENERIC;
+    if ( sys->d3dobj == NULL )
+    {
+        LPDIRECT3D9 (WINAPI *OurDirect3DCreate9)(UINT SDKVersion);
+        OurDirect3DCreate9 =
+            (void *)GetProcAddress(sys->hd3d9_dll, "Direct3DCreate9");
+        if (!OurDirect3DCreate9) {
+            msg_Err(vd, "Cannot locate reference to Direct3DCreate9 ABI in DLL");
+            return VLC_EGENERIC;
+        }
+
+        /* Create the D3D object. */
+        LPDIRECT3D9 d3dobj = OurDirect3DCreate9(D3D_SDK_VERSION);
+        if (!d3dobj) {
+           msg_Err(vd, "Could not create Direct3D9 instance.");
+           return VLC_EGENERIC;
+        }
+        sys->d3dobj = d3dobj;
+    } else {
+        IDirect3D9_AddRef( sys->d3dobj );
     }
-    sys->d3dobj = d3dobj;
 
     sys->hd3d9x_dll = Direct3D9LoadShaderLibrary();
     if (!sys->hd3d9x_dll)
@@ -558,7 +574,7 @@ static int Direct3D9Create(vout_display_t *vd)
     ** Get device capabilities
     */
     ZeroMemory(&sys->d3dcaps, sizeof(sys->d3dcaps));
-    HRESULT hr = IDirect3D9_GetDeviceCaps(d3dobj, D3DADAPTER_DEFAULT, D3DDEVTYPE_HAL, &sys->d3dcaps);
+    HRESULT hr = IDirect3D9_GetDeviceCaps(sys->d3dobj, D3DADAPTER_DEFAULT, D3DDEVTYPE_HAL, &sys->d3dcaps);
     if (FAILED(hr)) {
        msg_Err(vd, "Could not read adapter capabilities. (hr=0x%0lx)", hr);
        return VLC_EGENERIC;
@@ -657,45 +673,58 @@ static int Direct3D9Open(vout_display_t *vd, video_format_t *fmt)
     if (Direct3D9FillPresentationParameters(vd))
         return VLC_EGENERIC;
 
-    // Create the D3DDevice
-    LPDIRECT3DDEVICE9 d3ddev;
+    if ( format_is_DXVA( vd->fmt.i_chroma ) &&
+         vd->cfg->p_pool_setup != NULL &&
+         vd->cfg->p_pool_setup->p_sys != NULL )
+    {
+        sys->d3ddev = vd->cfg->p_pool_setup->p_sys->d3ddev;
+        sys->d3dpp  = vd->cfg->p_pool_setup->p_sys->d3dpp;
+    }
+
+    if ( sys->d3ddev == NULL )
+    {
+        // Create the D3DDevice
+        LPDIRECT3DDEVICE9 d3ddev;
 
-    UINT AdapterToUse = D3DADAPTER_DEFAULT;
-    D3DDEVTYPE DeviceType = D3DDEVTYPE_HAL;
+        UINT AdapterToUse = D3DADAPTER_DEFAULT;
+        D3DDEVTYPE DeviceType = D3DDEVTYPE_HAL;
 
 #ifndef NDEBUG
-    // Look for 'NVIDIA PerfHUD' adapter
-    // If it is present, override default settings
-    for (UINT Adapter=0; Adapter< IDirect3D9_GetAdapterCount(d3dobj); ++Adapter) {
-        D3DADAPTER_IDENTIFIER9 Identifier;
-        HRESULT Res = IDirect3D9_GetAdapterIdentifier(d3dobj,Adapter,0,&Identifier);
-        if (SUCCEEDED(Res) && strstr(Identifier.Description,"PerfHUD") != 0) {
-            AdapterToUse = Adapter;
-            DeviceType = D3DDEVTYPE_REF;
-            break;
+        // Look for 'NVIDIA PerfHUD' adapter
+        // If it is present, override default settings
+        for (UINT Adapter=0; Adapter< IDirect3D9_GetAdapterCount(d3dobj); ++Adapter) {
+            D3DADAPTER_IDENTIFIER9 Identifier;
+            HRESULT Res = IDirect3D9_GetAdapterIdentifier(d3dobj,Adapter,0,&Identifier);
+            if (SUCCEEDED(Res) && strstr(Identifier.Description,"PerfHUD") != 0) {
+                AdapterToUse = Adapter;
+                DeviceType = D3DDEVTYPE_REF;
+                break;
+            }
         }
-    }
 #endif
 
-    /* */
-    D3DADAPTER_IDENTIFIER9 d3dai;
-    if (FAILED(IDirect3D9_GetAdapterIdentifier(d3dobj,AdapterToUse,0, &d3dai))) {
-        msg_Warn(vd, "IDirect3D9_GetAdapterIdentifier failed");
-    } else {
-        msg_Dbg(vd, "Direct3d9 Device: %s %lu %lu %lu", d3dai.Description,
-                d3dai.VendorId, d3dai.DeviceId, d3dai.Revision );
-    }
+        /* */
+        D3DADAPTER_IDENTIFIER9 d3dai;
+        if (FAILED(IDirect3D9_GetAdapterIdentifier(d3dobj,AdapterToUse,0, &d3dai))) {
+            msg_Warn(vd, "IDirect3D9_GetAdapterIdentifier failed");
+        } else {
+            msg_Dbg(vd, "Direct3d9 Device: %s %lu %lu %lu", d3dai.Description,
+                    d3dai.VendorId, d3dai.DeviceId, d3dai.Revision );
+        }
 
-    HRESULT hr = IDirect3D9_CreateDevice(d3dobj, AdapterToUse,
-                                         DeviceType, sys->hvideownd,
-                                         D3DCREATE_SOFTWARE_VERTEXPROCESSING|
-                                         D3DCREATE_MULTITHREADED,
-                                         &sys->d3dpp, &d3ddev);
-    if (FAILED(hr)) {
-       msg_Err(vd, "Could not create the D3D9 device! (hr=0x%0lx)", hr);
-       return VLC_EGENERIC;
+        HRESULT hr = IDirect3D9_CreateDevice(d3dobj, AdapterToUse,
+                                             DeviceType, sys->hvideownd,
+                                             D3DCREATE_SOFTWARE_VERTEXPROCESSING|
+                                             D3DCREATE_MULTITHREADED,
+                                             &sys->d3dpp, &d3ddev);
+        if (FAILED(hr)) {
+           msg_Err(vd, "Could not create the D3D9 device! (hr=0x%0lx)", hr);
+           return VLC_EGENERIC;
+        }
+        sys->d3ddev = d3ddev;
+    } else {
+        IDirect3DDevice9_AddRef( sys->d3ddev );
     }
-    sys->d3ddev = d3ddev;
 
     UpdateRects(vd, NULL, NULL, true);
 
@@ -860,6 +889,9 @@ static const d3d_format_t d3d_formats[] = {
     { "YV12",       MAKEFOURCC('Y','V','1','2'),    VLC_CODEC_I420,  0,0,0 },
     { "YV12",       MAKEFOURCC('Y','V','1','2'),    VLC_CODEC_J420,  0,0,0 },
     { "NV12",       MAKEFOURCC('N','V','1','2'),    VLC_CODEC_NV12,  0,0,0 },
+    { "DXVANV12",   MAKEFOURCC('N','V','1','2'),    VLC_CODEC_DXVA_N_OPAQUE,  0,0,0 },
+    { "DXVAYV12",   MAKEFOURCC('Y','V','1','2'),    VLC_CODEC_DXVA_Y_OPAQUE,  0,0,0 },
+    { "DXVAIMC3",   MAKEFOURCC('I','M','C','3'),    VLC_CODEC_DXVA_I_OPAQUE,  0,0,0 },
     { "UYVY",       D3DFMT_UYVY,    VLC_CODEC_UYVY,  0,0,0 },
     { "YUY2",       D3DFMT_YUY2,    VLC_CODEC_YUYV,  0,0,0 },
     { "X8R8G8B8",   D3DFMT_X8R8G8B8,VLC_CODEC_RGB32, 0xff0000, 0x00ff00, 0x0000ff },
@@ -879,8 +911,11 @@ static const d3d_format_t *Direct3DFindFormat(vout_display_t *vd, vlc_fourcc_t c
 
     for (unsigned pass = 0; pass < 2; pass++) {
         const vlc_fourcc_t *list;
+        const vlc_fourcc_t dxva_chroma[] = {chroma, 0};
 
-        if (pass == 0 && sys->allow_hw_yuv && vlc_fourcc_IsYUV(chroma))
+        if (pass == 0 && format_is_DXVA( chroma ))
+            list = dxva_chroma;
+        else if (pass == 0 && sys->allow_hw_yuv && vlc_fourcc_IsYUV(chroma))
             list = vlc_fourcc_GetYUVFallback(chroma);
         else if (pass == 1)
             list = vlc_fourcc_GetRGBFallback(chroma);
@@ -968,64 +1003,76 @@ static int Direct3D9CreatePool(vout_display_t *vd, video_format_t *fmt)
 
     /* We create one picture.
      * It is useless to create more as we can't be used for direct rendering */
-
-    /* Create a surface */
-    LPDIRECT3DSURFACE9 surface;
-    HRESULT hr = IDirect3DDevice9_CreateOffscreenPlainSurface(d3ddev,
-                                                              fmt->i_visible_width,
-                                                              fmt->i_visible_height,
-                                                              d3dfmt->format,
-                                                              D3DPOOL_DEFAULT,
-                                                              &surface,
-                                                              NULL);
-    if (FAILED(hr)) {
-        msg_Err(vd, "Failed to create picture surface. (hr=0x%lx)", hr);
-        return VLC_EGENERIC;
+    if ( format_is_DXVA( fmt->i_chroma ) &&
+         vd->cfg->p_pool_setup != NULL &&
+         vd->cfg->p_pool_setup->p_sys != NULL &&
+         vd->cfg->p_pool_setup->p_sys->p_va_sys != NULL )
+    {
+        sys->pool = NULL;
     }
+    else
+    {
+        /* Create a surface */
+        LPDIRECT3DSURFACE9 surface;
+        HRESULT hr = IDirect3DDevice9_CreateOffscreenPlainSurface(d3ddev,
+                                                                  fmt->i_visible_width,
+                                                                  fmt->i_visible_height,
+                                                                  d3dfmt->format,
+                                                                  D3DPOOL_DEFAULT,
+                                                                  &surface,
+                                                                  NULL);
+        if (FAILED(hr)) {
+            msg_Err(vd, "Failed to create picture surface. (hr=0x%lx)", hr);
+            return VLC_EGENERIC;
+        }
 
 #ifndef NDEBUG
-    msg_Dbg(vd, "Direct3D created offscreen surface: %ix%i",
-                fmt->i_visible_width, fmt->i_visible_height);
+        msg_Dbg(vd, "Direct3D created offscreen surface: %ix%i",
+                    fmt->i_visible_width, fmt->i_visible_height);
 #endif
 
-    /* fill surface with black color */
-    IDirect3DDevice9_ColorFill(d3ddev, surface, NULL, D3DCOLOR_ARGB(0xFF, 0, 0, 0));
+        /* fill surface with black color */
+        IDirect3DDevice9_ColorFill(d3ddev, surface, NULL, D3DCOLOR_ARGB(0xFF, 0, 0, 0));
 
-    /* Create the associated picture */
-    picture_sys_t *picsys = malloc(sizeof(*picsys));
-    if (unlikely(picsys == NULL)) {
-        IDirect3DSurface9_Release(surface);
-        return VLC_ENOMEM;
-    }
-    picsys->surface = surface;
-    picsys->fallback = NULL;
-
-    picture_resource_t resource = { .p_sys = picsys };
-    for (int i = 0; i < PICTURE_PLANE_MAX; i++)
-        resource.p[i].i_lines = fmt->i_visible_height / (i > 0 ? 2 : 1);
-
-    picture_t *picture = picture_NewFromResource(fmt, &resource);
-    if (!picture) {
-        msg_Err(vd, "Failed to create a picture from resources.");
-        IDirect3DSurface9_Release(surface);
-        free(picsys);
-        return VLC_ENOMEM;
-    }
-    sys->picsys = picsys;
-
-    /* Wrap it into a picture pool */
-    picture_pool_configuration_t pool_cfg;
-    memset(&pool_cfg, 0, sizeof(pool_cfg));
-    pool_cfg.picture_count = 1;
-    pool_cfg.picture       = &picture;
-    pool_cfg.lock          = Direct3D9LockSurface;
-    pool_cfg.unlock        = Direct3D9UnlockSurface;
+        /* Create the associated picture */
+        picture_sys_t *picsys = calloc(1, sizeof(*picsys));
+        if (unlikely(picsys == NULL)) {
+            IDirect3DSurface9_Release(surface);
+            return VLC_ENOMEM;
+        }
+        picsys->surface = surface;
+        picsys->surface_device = d3ddev;
+
+        picture_resource_t resource = { .p_sys = picsys };
+        for (int i = 0; i < PICTURE_PLANE_MAX; i++)
+            resource.p[i].i_lines = fmt->i_visible_height / (i > 0 ? 2 : 1);
+
+        picture_t *picture = picture_NewFromResource(fmt, &resource);
+        if (!picture) {
+            msg_Err(vd, "Failed to create a picture from resources.");
+            IDirect3DSurface9_Release(surface);
+            free(picsys);
+            return VLC_ENOMEM;
+        }
+        sys->picsys = picsys;
+
+        /* Wrap it into a picture pool */
+        picture_pool_configuration_t pool_cfg;
+        memset(&pool_cfg, 0, sizeof(pool_cfg));
+        pool_cfg.picture_count = 1;
+        pool_cfg.picture       = &picture;
+        if (!format_is_DXVA( fmt->i_chroma ))
+        {
+            pool_cfg.lock      = Direct3D9LockSurface;
+            pool_cfg.unlock    = Direct3D9UnlockSurface;
+        }
 
-    sys->pool = picture_pool_NewExtended(&pool_cfg);
-    if (!sys->pool) {
-        picture_Release(picture);
-        IDirect3DSurface9_Release(surface);
-        return VLC_ENOMEM;
+        sys->pool = picture_pool_NewExtended(&pool_cfg);
+        if (!sys->pool) {
+            picture_Release(picture);
+            IDirect3DSurface9_Release(surface);
+            return VLC_ENOMEM;
+        }
     }
     return VLC_SUCCESS;
 }
@@ -1038,9 +1085,12 @@ static void Direct3D9DestroyPool(vout_display_t *vd)
 
     if (sys->pool) {
         picture_sys_t *picsys = sys->picsys;
-        IDirect3DSurface9_Release(picsys->surface);
-        if (picsys->fallback)
-            picture_Release(picsys->fallback);
+        if ( picsys != NULL )
+        {
+            IDirect3DSurface9_Release(picsys->surface);
+            if (picsys->fallback)
+                picture_Release(picsys->fallback);
+        }
         picture_pool_Release(sys->pool);
     }
     sys->pool = NULL;
diff --git a/src/misc/fourcc.c b/src/misc/fourcc.c
index 3b5c9b3..330d40c 100644
--- a/src/misc/fourcc.c
+++ b/src/misc/fourcc.c
@@ -2193,6 +2193,9 @@ static const struct
     { { VLC_CODEC_ANDROID_OPAQUE, VLC_CODEC_MMAL_OPAQUE, },
                                                FAKE_FMT() },
 
+    { { VLC_CODEC_DXVA_N_OPAQUE, VLC_CODEC_DXVA_Y_OPAQUE, VLC_CODEC_DXVA_I_OPAQUE, },
+                                                FAKE_FMT() },
+
     { { 0 },                                   FAKE_FMT() }
 };
 
-- 
2.3.0





More information about the vlc-devel mailing list