[vlc-devel] [PATCH 6/7] video_chroma: add a filter to convert from CPU to a D3D11 surface
Steve Lhomme
robux4 at videolabs.io
Thu May 11 09:36:08 CEST 2017
---
modules/MODULES_LIST | 1 +
modules/video_chroma/Makefile.am | 5 +-
modules/video_chroma/d3d11_from_cpu.c | 268 ++++++++++++++++++++++++++++++++++
3 files changed, 273 insertions(+), 1 deletion(-)
create mode 100644 modules/video_chroma/d3d11_from_cpu.c
diff --git a/modules/MODULES_LIST b/modules/MODULES_LIST
index 3205a0850b..9c3217bc9d 100644
--- a/modules/MODULES_LIST
+++ b/modules/MODULES_LIST
@@ -86,6 +86,7 @@ $Id$
* crystalhd: crystalhd decoder
* cvdsub: CVD subtitles decoder
* cvpx_i420: filter to copy from OS X accelerated surfaces to CPU in YUV
+ * d3d11_out: Convert YUV planes to D3D11 GPU textures
* d3d11_surface: Convert D3D11 GPU textures to YUV planes
* d3d11va: Direct3D11 hardware-accelerated decoding
* daala: a daala video decoder/packetizer using libdaala
diff --git a/modules/video_chroma/Makefile.am b/modules/video_chroma/Makefile.am
index cf11b3b81e..956fb6dce6 100644
--- a/modules/video_chroma/Makefile.am
+++ b/modules/video_chroma/Makefile.am
@@ -126,10 +126,13 @@ endif
libd3d11_surface_plugin_la_SOURCES = video_chroma/d3d11_surface.c \
video_chroma/d3d11_fmt.h video_chroma/dxgi_fmt.c video_chroma/dxgi_fmt.h \
video_chroma/copy.c video_chroma/copy.h
+libd3d11_out_plugin_la_SOURCES = video_chroma/d3d11_from_cpu.c \
+ video_chroma/d3d11_fmt.h video_chroma/dxgi_fmt.c video_chroma/dxgi_fmt.h
if HAVE_AVCODEC_D3D11VA
chroma_LTLIBRARIES += \
- libd3d11_surface_plugin.la
+ libd3d11_surface_plugin.la \
+ libd3d11_out_plugin.la
endif
libcvpx_i420_plugin_la_SOURCES = video_chroma/cvpx_i420.c video_chroma/copy.c video_chroma/copy.h
diff --git a/modules/video_chroma/d3d11_from_cpu.c b/modules/video_chroma/d3d11_from_cpu.c
new file mode 100644
index 0000000000..9b839d8578
--- /dev/null
+++ b/modules/video_chroma/d3d11_from_cpu.c
@@ -0,0 +1,268 @@
+/*****************************************************************************
+ * d3d11_from_cpu.c : D3D11 CPU to GPU surface conversion module for vlc
+ *****************************************************************************
+ * Copyright (C) 2017 VLC authors, VideoLAN and VideoLabs
+ *
+ * Authors: Steve Lhomme <robux4 at videolabs.io>
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU Lesser General Public License as published by
+ * the Free Software Foundation; either version 2.1 of the License, or
+ * (at your option) any later version.
+ *
+ * This 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 Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser 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.
+ *****************************************************************************/
+
+/*****************************************************************************
+ * Preamble
+ *****************************************************************************/
+
+#ifdef HAVE_CONFIG_H
+# include "config.h"
+#endif
+
+#include <assert.h>
+
+#include <vlc_common.h>
+#include <vlc_plugin.h>
+#include <vlc_filter.h>
+#include <vlc_picture.h>
+#include <vlc_modules.h>
+
+/*****************************************************************************
+ * Module descriptor.
+ *****************************************************************************/
+#include <windows.h>
+#define COBJMACROS
+#include <d3d11.h>
+#include "d3d11_fmt.h"
+
+struct filter_sys_t {
+ filter_t *filter;
+ picture_t *staging;
+};
+
+static void DestroyPicture(picture_t *picture)
+{
+ picture_sys_t *p_sys = picture->p_sys;
+ ReleasePictureSys( p_sys );
+ free(p_sys);
+ free(picture);
+}
+
+static void DeleteFilter( filter_t * p_filter )
+{
+ if( p_filter->p_module )
+ module_unneed( p_filter, p_filter->p_module );
+
+ es_format_Clean( &p_filter->fmt_in );
+ es_format_Clean( &p_filter->fmt_out );
+
+ vlc_object_release( p_filter );
+}
+
+static picture_t *NewBuffer(filter_t *p_filter)
+{
+ filter_t *p_parent = p_filter->owner.sys;
+ return p_parent->p_sys->staging;
+}
+
+static filter_t *CreateFilter( vlc_object_t *p_this, const es_format_t *p_fmt_in,
+ vlc_fourcc_t dst_chroma )
+{
+ filter_t *p_filter;
+
+ p_filter = vlc_object_create( p_this, sizeof(filter_t) );
+ if (unlikely(p_filter == NULL))
+ return NULL;
+
+ p_filter->b_allow_fmt_out_change = false;
+ p_filter->owner.video.buffer_new = NewBuffer;
+ p_filter->owner.sys = p_this;
+
+ es_format_InitFromVideo( &p_filter->fmt_in, &p_fmt_in->video );
+ es_format_InitFromVideo( &p_filter->fmt_out, &p_fmt_in->video );
+ p_filter->fmt_out.i_codec = p_filter->fmt_out.video.i_chroma = dst_chroma;
+ p_filter->p_module = module_need( p_filter, "video converter", NULL, false );
+
+ if( !p_filter->p_module )
+ {
+ msg_Dbg( p_filter, "no video converter found" );
+ DeleteFilter( p_filter );
+ return NULL;
+ }
+
+ return p_filter;
+}
+
+static void D3D11_NV12(filter_t *p_filter, picture_t *src, picture_t *dst)
+{
+ filter_sys_t *sys = (filter_sys_t*) p_filter->p_sys;
+ picture_sys_t *p_sys = dst->p_sys;
+
+ D3D11_TEXTURE2D_DESC texDesc;
+ ID3D11Texture2D_GetDesc( sys->staging->p_sys->texture[KNOWN_DXGI_INDEX], &texDesc);
+
+ D3D11_MAPPED_SUBRESOURCE lock;
+ HRESULT hr = ID3D11DeviceContext_Map(p_sys->context, sys->staging->p_sys->resource[KNOWN_DXGI_INDEX],
+ 0, D3D11_MAP_WRITE, 0, &lock);
+ if (FAILED(hr)) {
+ msg_Err(p_filter, "Failed to map source surface. (hr=0x%0lx)", hr);
+ return;
+ }
+
+ picture_UpdatePlanes(sys->staging, lock.pData, lock.RowPitch, true);
+
+ picture_Hold( src );
+ sys->filter->pf_video_filter(sys->filter, src);
+
+ ID3D11DeviceContext_Unmap(p_sys->context, sys->staging->p_sys->resource[KNOWN_DXGI_INDEX], 0);
+
+ D3D11_BOX copyBox = {
+ .right = dst->format.i_width, .bottom = dst->format.i_height, .back = 1,
+ };
+ ID3D11DeviceContext_CopySubresourceRegion(p_sys->context,
+ p_sys->resource[KNOWN_DXGI_INDEX],
+ p_sys->slice_index,
+ 0, 0, 0,
+ sys->staging->p_sys->resource[KNOWN_DXGI_INDEX], 0,
+ ©Box);
+}
+
+VIDEO_FILTER_WRAPPER (D3D11_NV12)
+
+static int OpenConverter( vlc_object_t *obj )
+{
+ filter_t *p_filter = (filter_t *)obj;
+ int err = VLC_EGENERIC;
+ ID3D11Texture2D *texture = NULL;
+ filter_t *p_cpu_filter = NULL;
+ video_format_t fmt_staging;
+
+ if ( p_filter->fmt_out.video.i_chroma != VLC_CODEC_D3D11_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_in.video.i_chroma ) {
+ case VLC_CODEC_I420:
+ case VLC_CODEC_YV12:
+ case VLC_CODEC_NV12:
+ case VLC_CODEC_P010:
+ p_filter->pf_video_filter = D3D11_NV12_Filter;
+ break;
+ default:
+ return VLC_EGENERIC;
+ }
+
+ picture_t *peek = filter_NewPicture(p_filter);
+ if (peek == NULL)
+ return VLC_EGENERIC;
+ assert(peek->p_sys != NULL);
+
+ video_format_Init(&fmt_staging, 0);
+
+ D3D11_TEXTURE2D_DESC texDesc;
+ ID3D11Texture2D_GetDesc( peek->p_sys->texture[KNOWN_DXGI_INDEX], &texDesc);
+ vlc_fourcc_t d3d_fourcc = DxgiFormatFourcc(texDesc.Format);
+ if (d3d_fourcc == 0)
+ goto done;
+
+ picture_resource_t res;
+ res.pf_destroy = DestroyPicture;
+ res.p_sys = calloc(1, sizeof(picture_sys_t));
+ if (res.p_sys == NULL) {
+ err = VLC_ENOMEM;
+ goto done;
+ }
+ res.p_sys->context = peek->p_sys->context;
+ res.p_sys->formatTexture = texDesc.Format;
+
+ video_format_Copy(&fmt_staging, &p_filter->fmt_out.video);
+ fmt_staging.i_chroma = d3d_fourcc;
+ fmt_staging.i_height = texDesc.Height;
+ fmt_staging.i_width = texDesc.Width;
+
+ picture_t *p_dst = picture_NewFromResource(&fmt_staging, &res);
+ if (p_dst == NULL) {
+ msg_Err(p_filter, "Failed to map create the temporary picture.");
+ goto done;
+ }
+ plane_Setup(p_dst->format.i_chroma, &p_dst->format, p_dst->p, &p_dst->i_planes, &p_dst->format.i_height);
+
+ texDesc.MipLevels = 1;
+ //texDesc.SampleDesc.Count = 1;
+ texDesc.MiscFlags = 0;
+ texDesc.ArraySize = 1;
+ texDesc.Usage = D3D11_USAGE_STAGING;
+ texDesc.CPUAccessFlags = D3D11_CPU_ACCESS_WRITE;
+ texDesc.BindFlags = 0;
+ texDesc.Height = p_dst->format.i_height; /* make sure we match plane_Setup() */
+
+ ID3D11Device *p_device;
+ ID3D11DeviceContext_GetDevice(peek->p_sys->context, &p_device);
+ HRESULT hr = ID3D11Device_CreateTexture2D( p_device, &texDesc, NULL, &texture);
+ ID3D11Device_Release(p_device);
+ if (FAILED(hr)) {
+ msg_Err(p_filter, "Failed to create a %s staging texture to extract surface pixels (hr=0x%0lx)", DxgiFormatToStr(texDesc.Format), hr );
+ goto done;
+ }
+
+ res.p_sys->texture[KNOWN_DXGI_INDEX] = texture;
+ ID3D11DeviceContext_AddRef(p_dst->p_sys->context);
+
+ if ( p_filter->fmt_in.video.i_chroma != d3d_fourcc )
+ {
+ p_cpu_filter = CreateFilter(VLC_OBJECT(p_filter), &p_filter->fmt_in, p_dst->format.i_chroma);
+ if (!p_cpu_filter)
+ goto done;
+ }
+
+ filter_sys_t *p_sys = calloc(1, sizeof(filter_sys_t));
+ if (!p_sys) {
+ err = VLC_ENOMEM;
+ goto done;
+ }
+ p_sys->filter = p_cpu_filter;
+ p_sys->staging = p_dst;
+
+ p_filter->p_sys = p_sys;
+ err = VLC_SUCCESS;
+
+done:
+ video_format_Clean(&fmt_staging);
+ picture_Release(peek);
+ if (err != VLC_SUCCESS)
+ {
+ if (p_cpu_filter)
+ DeleteFilter( p_cpu_filter );
+ if (texture)
+ ID3D11Texture2D_Release(texture);
+ }
+ return err;
+}
+
+static void CloseConverter( vlc_object_t *obj )
+{
+ filter_t *p_filter = (filter_t *)obj;
+ filter_sys_t *p_sys = (filter_sys_t*) p_filter->p_sys;
+ DeleteFilter(p_sys->filter);
+ picture_Release(p_sys->staging);
+ free( p_sys );
+ p_filter->p_sys = NULL;
+}
+
+vlc_module_begin ()
+ set_description( N_("Conversions from YUV to D3D11") )
+ set_capability( "video converter", 10 )
+ set_callbacks( OpenConverter, CloseConverter )
+vlc_module_end ()
--
2.12.1
More information about the vlc-devel
mailing list