[vlc-devel] [PATCH] DXVA2: output D3D9 GPU surfaces

Steve Lhomme robUx4 at videolabs.io
Tue Apr 28 16:52:10 CEST 2015


also provide a filter to convert these surfaces to regular planes
---
 modules/codec/Makefile.am     |   3 +-
 modules/codec/avcodec/dxva2.c | 203 ++++++++++++++++++++++++++++++++----------
 modules/codec/avcodec/va.c    |   2 +-
 3 files changed, 159 insertions(+), 49 deletions(-)

diff --git a/modules/codec/Makefile.am b/modules/codec/Makefile.am
index e5f7ed8..3efdcac 100644
--- a/modules/codec/Makefile.am
+++ b/modules/codec/Makefile.am
@@ -342,7 +342,8 @@ endif
 
 libdxva2_plugin_la_SOURCES = \
 	video_chroma/copy.c video_chroma/copy.h \
-	codec/avcodec/dxva2.c codec/h264_nal.c codec/h264_nal.h
+	codec/avcodec/dxva2.c codec/h264_nal.c codec/h264_nal.h \
+	../src/win32/direct3d9_pool.c ../src/win32/direct3d9_pool.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 482810c..a55fddf 100644
--- a/modules/codec/avcodec/dxva2.c
+++ b/modules/codec/avcodec/dxva2.c
@@ -42,6 +42,7 @@
 #include <vlc_cpu.h>
 #include <vlc_plugin.h>
 #include <vlc_codecs.h>
+#include <vlc_filter.h>
 
 #include <libavcodec/avcodec.h>
 #    define DXVA2API_USE_BITFIELDS
@@ -57,12 +58,19 @@ static int Open(vlc_va_t *, AVCodecContext *, enum PixelFormat,
                 const es_format_t *, picture_sys_t *p_sys);
 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>
@@ -366,6 +374,11 @@ struct vlc_va_sys_t
     LPDIRECT3DSURFACE9 hw_surface[VA_DXVA2_MAX_SURFACE_COUNT];
 };
 
+struct picture_sys_t
+{
+    LPDIRECT3DSURFACE9 surface;
+};
+
 /* */
 static int D3dCreateDevice(vlc_va_t *);
 static void D3dDestroyDevice(vlc_va_sys_t *);
@@ -385,9 +398,6 @@ static int DxResetVideoDecoder(vlc_va_t *);
 
 static bool profile_supported(const dxva2_mode_t *mode, const es_format_t *fmt);
 
-static void DxCreateVideoConversion(vlc_va_sys_t *);
-static void DxDestroyVideoConversion(vlc_va_sys_t *);
-
 /* */
 static int Setup(vlc_va_t *va, AVCodecContext *avctx, vlc_fourcc_t *chroma)
 {
@@ -398,7 +408,6 @@ static int Setup(vlc_va_t *va, AVCodecContext *avctx, vlc_fourcc_t *chroma)
         goto ok;
 
     /* */
-    DxDestroyVideoConversion(sys);
     DxDestroyVideoDecoder(sys);
 
     avctx->hwaccel_context = NULL;
@@ -421,38 +430,38 @@ static int Setup(vlc_va_t *va, AVCodecContext *avctx, vlc_fourcc_t *chroma)
     sys->hw.surface = sys->hw_surface;
 
     /* */
-    DxCreateVideoConversion(sys);
-
-    /* */
 ok:
     avctx->hwaccel_context = &sys->hw;
-    const d3d_format_t *output = D3dFindFormat(sys->output);
-    *chroma = output->codec;
+    *chroma = VLC_CODEC_D3D9_OPAQUE;
 
     return VLC_SUCCESS;
 }
 
-static int Extract(vlc_va_t *va, picture_t *picture, uint8_t *data)
+static void DXA9_YV12(filter_t *p_filter, picture_t *src, picture_t *dst)
 {
-    vlc_va_sys_t *sys = va->sys;
-    LPDIRECT3DSURFACE9 d3d = (LPDIRECT3DSURFACE9)(uintptr_t)data;
+    copy_cache_t *p_copy_cache = (copy_cache_t*) p_filter->p_sys;
 
-    if (!sys->surface_cache.buffer)
-        return VLC_EGENERIC;
-
-    /* */
-    assert(sys->output == MAKEFOURCC('Y','V','1','2'));
+    LPDIRECT3DSURFACE9 d3d = src->p_sys->surface;
+    D3DSURFACE_DESC desc;
+    if (FAILED( IDirect3DSurface9_GetDesc(d3d, &desc) ))
+        return;
 
     /* */
     D3DLOCKED_RECT lock;
     if (FAILED(IDirect3DSurface9_LockRect(d3d, &lock, NULL, D3DLOCK_READONLY))) {
-        msg_Err(va, "Failed to lock surface");
-        return VLC_EGENERIC;
+        msg_Err(p_filter, "Failed to lock surface");
+        return;
     }
 
-    if (sys->render == MAKEFOURCC('Y','V','1','2') ||
-        sys->render == MAKEFOURCC('I','M','C','3')) {
-        bool imc3 = sys->render == MAKEFOURCC('I','M','C','3');
+    if (dst->format.i_chroma == VLC_CODEC_I420) {
+        uint8_t *tmp = dst->p[1].p_pixels;
+        dst->p[1].p_pixels = dst->p[2].p_pixels;
+        dst->p[2].p_pixels = tmp;
+    }
+
+    if (desc.Format == MAKEFOURCC('Y','V','1','2') ||
+        desc.Format == MAKEFOURCC('I','M','C','3')) {
+        bool imc3 = desc.Format == MAKEFOURCC('I','M','C','3');
         size_t chroma_pitch = imc3 ? lock.Pitch : (lock.Pitch / 2);
 
         size_t pitch[3] = {
@@ -463,9 +472,9 @@ static int Extract(vlc_va_t *va, picture_t *picture, uint8_t *data)
 
         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,
+            (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,
         };
 
         if (imc3) {
@@ -473,24 +482,95 @@ static int Extract(vlc_va_t *va, picture_t *picture, uint8_t *data)
             plane[1] = plane[2];
             plane[2] = V;
         }
-        CopyFromYv12(picture, plane, pitch, sys->width, sys->height,
-                     &sys->surface_cache);
+        CopyFromYv12(dst, plane, pitch, src->format.i_width, src->format.i_visible_height,
+                     p_copy_cache);
+    } else if (desc.Format == MAKEFOURCC('N','V','1','2')) {
+        uint8_t *plane[2] = {
+            lock.pBits,
+            (uint8_t*)lock.pBits + lock.Pitch * src->format.i_visible_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);
     } else {
-        assert(sys->render == MAKEFOURCC('N','V','1','2'));
+        msg_Err(p_filter, "Unsupported DXA9 conversion from 0x%08X to YV12", desc.Format);
+    }
+
+    if (dst->format.i_chroma == VLC_CODEC_I420) {
+        uint8_t *tmp = dst->p[1].p_pixels;
+        dst->p[1].p_pixels = dst->p[2].p_pixels;
+        dst->p[2].p_pixels = tmp;
+    }
+
+    /* */
+    IDirect3DSurface9_UnlockRect(d3d);
+}
+
+static void DXA9_NV12(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;
+    D3DSURFACE_DESC desc;
+    if (FAILED( IDirect3DSurface9_GetDesc(d3d, &desc) ))
+        return;
+
+    /* */
+    D3DLOCKED_RECT lock;
+    if (FAILED(IDirect3DSurface9_LockRect(d3d, &lock, NULL, D3DLOCK_READONLY))) {
+        msg_Err(p_filter, "Failed to lock surface");
+        return;
+    }
+
+    if (desc.Format == MAKEFOURCC('N','V','1','2')) {
         uint8_t *plane[2] = {
             lock.pBits,
-            (uint8_t*)lock.pBits + lock.Pitch * sys->surface_height
+            (uint8_t*)lock.pBits + lock.Pitch * src->format.i_visible_height
         };
         size_t  pitch[2] = {
             lock.Pitch,
             lock.Pitch,
         };
-        CopyFromNv12(picture, plane, pitch, sys->width, sys->height,
-                     &sys->surface_cache);
+        CopyFromNv12ToNv12(dst, plane, pitch, src->format.i_width, src->format.i_visible_height,
+                     p_copy_cache);
+    } else {
+        msg_Err(p_filter, "Unsupported DXA9 conversion from 0x%08X to NV12", desc.Format);
     }
 
     /* */
     IDirect3DSurface9_UnlockRect(d3d);
+}
+
+static int Extract(vlc_va_t *va, picture_t *picture, uint8_t *data)
+{
+    vlc_va_sys_t *sys = va->sys;
+    LPDIRECT3DSURFACE9 d3d = (LPDIRECT3DSURFACE9)(uintptr_t)data;
+    picture_sys_t *p_sys = picture->p_sys;
+    LPDIRECT3DSURFACE9 output = p_sys->surface;
+
+    assert(d3d != output);
+#ifndef NDEBUG
+    LPDIRECT3DDEVICE9 srcDevice, dstDevice;
+    IDirect3DSurface9_GetDevice(d3d, &srcDevice);
+    IDirect3DSurface9_GetDevice(output, &dstDevice);
+    assert(srcDevice == dstDevice);
+#endif
+
+    HRESULT hr;
+    RECT visibleSource;
+    visibleSource.left = 0;
+    visibleSource.top = 0;
+    visibleSource.right = picture->format.i_visible_width;
+    visibleSource.bottom = picture->format.i_visible_height;
+    hr = IDirect3DDevice9_StretchRect( sys->d3ddev, d3d, &visibleSource, output, &visibleSource, D3DTEXF_NONE);
+    if (FAILED(hr)) {
+        msg_Err(va, "Failed to copy the hw surface to the decoder surface (hr=0x%0lx)", hr );
+        return VLC_EGENERIC;
+    }
+
     return VLC_SUCCESS;
 }
 
@@ -557,7 +637,6 @@ static void Close(vlc_va_t *va, AVCodecContext *ctx)
     vlc_va_sys_t *sys = va->sys;
 
     (void) ctx;
-    DxDestroyVideoConversion(sys);
     DxDestroyVideoDecoder(sys);
     DxDestroyVideoService(sys);
     D3dDestroyDeviceManager(sys);
@@ -603,12 +682,20 @@ static int Open(vlc_va_t *va, AVCodecContext *ctx, enum PixelFormat pix_fmt,
     }
     msg_Dbg(va, "DLLs loaded");
 
-    /* */
-    if (D3dCreateDevice(va)) {
-        msg_Err(va, "Failed to create Direct3D device");
-        goto error;
+    sys->d3ddev = NULL;
+    if (p_sys!=NULL)
+        IDirect3DSurface9_GetDevice(p_sys->surface, &sys->d3ddev);
+
+    if (sys->d3ddev) {
+        msg_Dbg(va, "Reusing D3D9 device");
+    } else {
+        /* */
+        if (D3dCreateDevice(va)) {
+            msg_Err(va, "Failed to create Direct3D device");
+            goto error;
+        }
+        msg_Dbg(va, "D3dCreateDevice succeed");
     }
-    msg_Dbg(va, "D3dCreateDevice succeed");
 
     if (D3dCreateDeviceManager(va)) {
         msg_Err(va, "D3dCreateDeviceManager failed");
@@ -1148,21 +1235,43 @@ static int DxResetVideoDecoder(vlc_va_t *va)
     return VLC_EGENERIC;
 }
 
-static void DxCreateVideoConversion(vlc_va_sys_t *va)
+VIDEO_FILTER_WRAPPER (DXA9_YV12)
+VIDEO_FILTER_WRAPPER (DXA9_NV12)
+
+static int OpenConverter( vlc_object_t *obj )
 {
-    switch (va->render) {
-    case MAKEFOURCC('N','V','1','2'):
-    case MAKEFOURCC('I','M','C','3'):
-        va->output = MAKEFOURCC('Y','V','1','2');
+    filter_t *p_filter = (filter_t *)obj;
+    if ( p_filter->fmt_in.video.i_chroma != VLC_CODEC_D3D9_OPAQUE )
+        return VLC_EGENERIC;
+
+    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;
+
+    switch( p_filter->fmt_out.video.i_chroma ) {
+    case VLC_CODEC_I420:
+    case VLC_CODEC_YV12:
+        p_filter->pf_video_filter = DXA9_YV12_Filter;
         break;
-    default:
-        va->output = va->render;
+    case VLC_CODEC_NV12:
+        p_filter->pf_video_filter = DXA9_NV12_Filter;
         break;
+    default:
+        return VLC_EGENERIC;
     }
-    CopyInitCache(&va->surface_cache, va->surface_width);
+
+    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/va.c b/modules/codec/avcodec/va.c
index 413574d..f9e94e6 100644
--- a/modules/codec/avcodec/va.c
+++ b/modules/codec/avcodec/va.c
@@ -41,7 +41,7 @@ vlc_fourcc_t vlc_va_GetChroma(enum PixelFormat hwfmt, enum PixelFormat swfmt)
             return VLC_CODEC_YV12;
 
         case AV_PIX_FMT_DXVA2_VLD:
-            return VLC_CODEC_YV12;
+            return VLC_CODEC_D3D9_OPAQUE;
 #if (LIBAVUTIL_VERSION_INT >= AV_VERSION_INT(53, 14, 0))
         case AV_PIX_FMT_VDA_VLD:
             return VLC_CODEC_UYVY;
-- 
2.3.2




More information about the vlc-devel mailing list