[vlc-devel] [PATCH 2/3] dxva2: convert D3D9 textures to planes using an external filter
Steve Lhomme
robux4 at gmail.com
Fri Jun 5 09:45:47 CEST 2015
New patch coming with a cleaner way to call the conversion filter.
On Tue, Jun 2, 2015 at 4:08 PM, Steve Lhomme <robux4 at gmail.com> wrote:
> ---
> When the picture_sys_t coming from the display/decoder pool as
> VLC_CODEC_D3D9_OPAQUE is NULL, we know it was not created properly so
> we need to output another format, like YV12.
>
> A call to updateFormat on the next frame will reconfigure the vout and its
> pool with this new output format.
>
> No need for D3D9_OPAQUE pool in the core anymore ! Only the vout may create
> one for direct rendering.
> ---
> modules/MODULES_LIST | 1 +
> modules/codec/avcodec/dxva2.c | 117 ++++++++++++++++++----
> modules/codec/avcodec/va.c | 3 +-
> modules/video_chroma/Makefile.am | 9 ++
> modules/video_chroma/dxa9.c | 207 +++++++++++++++++++++++++++++++++++++++
> po/POTFILES.in | 1 +
> 6 files changed, 318 insertions(+), 20 deletions(-)
> create mode 100644 modules/video_chroma/dxa9.c
>
> diff --git a/modules/MODULES_LIST b/modules/MODULES_LIST
> index 0d794e0..4181362 100644
> --- a/modules/MODULES_LIST
> +++ b/modules/MODULES_LIST
> @@ -119,6 +119,7 @@ $Id$
> * dvbsub: decoder module for subs in dvb streams
> * dvdnav: access module for DVDs with libdvdnav
> * dvdread: input module for accessing DVDs, uses libdvdread
> + * dxa9: Convert D3D9 GPU textures to YUV planes
> * dxva2: DxVA2 hardware-accelerated decoding
> * dynamicoverlay: subpicture filter using shared memory that can be written to by external applications
> * edummy: dummy encoder
> diff --git a/modules/codec/avcodec/dxva2.c b/modules/codec/avcodec/dxva2.c
> index 382b2b1..d293342 100644
> --- a/modules/codec/avcodec/dxva2.c
> +++ b/modules/codec/avcodec/dxva2.c
> @@ -31,6 +31,8 @@
>
> #include <vlc_common.h>
> #include <vlc_picture.h>
> +#include <vlc_filter.h> /* need for the DXA9 to YV12 conversion */
> +#include <vlc_modules.h>
> #include <vlc_plugin.h>
>
> #include "directx_va.h"
> @@ -102,6 +104,12 @@ static const d3d_format_t *D3dFindFormat(D3DFORMAT format)
> return NULL;
> }
>
> +typedef struct
> +{
> + filter_t filter;
> + picture_t *pic_out;
> +} plane_filter_t;
> +
> struct vlc_va_sys_t
> {
> directx_sys_t dx_sys;
> @@ -124,6 +132,9 @@ struct vlc_va_sys_t
> /* Video decoder */
> DXVA2_ConfigPictureDecode cfg;
>
> + /* Option conversion */
> + filter_t *filter;
> +
> /* avcodec internals */
> struct dxva_context hw;
> };
> @@ -155,6 +166,49 @@ static void SetupAVCodecContext(vlc_va_t *);
> static picture_t *DxAllocPicture(vlc_va_t *, const video_format_t *, unsigned index);
>
>
> +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 *video_new_buffer(plane_filter_t *p_filter)
> +{
> + return p_filter->pic_out;
> +}
> +
> +static filter_t *CreateFilter( vlc_object_t *p_this, const es_format_t *p_fmt_in,
> + vlc_fourcc_t fmt_out )
> +{
> + filter_t *p_filter;
> +
> + p_filter = vlc_object_create( p_this, sizeof(filter_t) );
> + if (unlikely(p_filter == NULL))
> + return NULL;
> +
> + p_filter->owner.video.buffer_new = (picture_t *(*)(filter_t *))video_new_buffer;
> +
> + 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_in.i_codec = p_filter->fmt_in.video.i_chroma = VLC_CODEC_D3D9_OPAQUE;
> + p_filter->fmt_out.i_codec = p_filter->fmt_out.video.i_chroma = fmt_out;
> + p_filter->p_module = module_need( p_filter, "video filter2", NULL, false );
> +
> + if( !p_filter->p_module )
> + {
> + msg_Dbg( p_filter, "no video filter found" );
> + DeleteFilter( p_filter );
> + return NULL;
> + }
> +
> + return p_filter;
> +}
> +
> /* */
> static int Setup(vlc_va_t *va, AVCodecContext *avctx, vlc_fourcc_t *chroma)
> {
> @@ -163,7 +217,7 @@ static int Setup(vlc_va_t *va, AVCodecContext *avctx, vlc_fourcc_t *chroma)
> return VLC_EGENERIC;
>
> avctx->hwaccel_context = &sys->hw;
> - *chroma = VLC_CODEC_D3D9_OPAQUE;
> + *chroma = sys->filter == NULL ? VLC_CODEC_D3D9_OPAQUE : VLC_CODEC_YV12;
>
> return VLC_SUCCESS;
> }
> @@ -186,26 +240,41 @@ static int Extract(vlc_va_t *va, picture_t *picture, uint8_t *data)
> {
> directx_sys_t *dx_sys = &va->sys->dx_sys;
> LPDIRECT3DSURFACE9 d3d = (LPDIRECT3DSURFACE9)(uintptr_t)data;
> - picture_sys_t *p_sys = picture->p_sys;
> - LPDIRECT3DSURFACE9 output = p_sys->surface;
> + if (picture->format.i_chroma == VLC_CODEC_D3D9_OPAQUE)
> + {
> + picture_sys_t *p_sys = picture->p_sys;
> + LPDIRECT3DSURFACE9 output = p_sys->surface;
>
> - assert(d3d != output);
> + assert(d3d != output);
> #ifndef NDEBUG
> - LPDIRECT3DDEVICE9 srcDevice, dstDevice;
> - IDirect3DSurface9_GetDevice(d3d, &srcDevice);
> - IDirect3DSurface9_GetDevice(output, &dstDevice);
> - assert(srcDevice == dstDevice);
> + 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( (IDirect3DDevice9*) dx_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 );
> + 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( (IDirect3DDevice9*) dx_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;
> + }
> + }
> + else if (va->sys->filter != NULL) {
> + plane_filter_t filter = {
> + .filter = *va->sys->filter,
> + .pic_out = picture,
> + };
> + vlc_va_surface_t *surface = picture->context;
> + picture_Hold( surface->p_pic );
> + va->sys->filter->pf_video_filter( &filter.filter, surface->p_pic );
> + } else {
> + msg_Err(va, "Unsupported output picture format %08X", picture->format.i_chroma );
> return VLC_EGENERIC;
> }
>
> @@ -239,6 +308,12 @@ static void Close(vlc_va_t *va, AVCodecContext *ctx)
>
> (void) ctx;
>
> + if (sys->filter)
> + {
> + DeleteFilter( sys->filter );
> + sys->filter = NULL;
> + }
> +
> directx_va_Close(va, &sys->dx_sys);
>
> if (sys->hd3d9_dll)
> @@ -257,8 +332,6 @@ static int Open(vlc_va_t *va, AVCodecContext *ctx, enum PixelFormat pix_fmt,
> if (pix_fmt != AV_PIX_FMT_DXVA2_VLD)
> return VLC_EGENERIC;
>
> - (void) p_sys;
> -
> vlc_va_sys_t *sys = calloc(1, sizeof (*sys));
> if (unlikely(sys == NULL))
> return VLC_ENOMEM;
> @@ -297,6 +370,12 @@ static int Open(vlc_va_t *va, AVCodecContext *ctx, enum PixelFormat pix_fmt,
> if (err!=VLC_SUCCESS)
> goto error;
>
> + if (p_sys == NULL)
> + {
> + sys->filter = CreateFilter( VLC_OBJECT(va), fmt, VLC_CODEC_YV12);
> + if (sys->filter == NULL)
> + goto error;
> + }
>
> /* TODO print the hardware name/vendor for debugging purposes */
> va->description = DxDescribe(sys);
> diff --git a/modules/codec/avcodec/va.c b/modules/codec/avcodec/va.c
> index 413574d..63feb49 100644
> --- a/modules/codec/avcodec/va.c
> +++ b/modules/codec/avcodec/va.c
> @@ -41,7 +41,8 @@ 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;
> diff --git a/modules/video_chroma/Makefile.am b/modules/video_chroma/Makefile.am
> index 3aa874c..cf6941e 100644
> --- a/modules/video_chroma/Makefile.am
> +++ b/modules/video_chroma/Makefile.am
> @@ -97,3 +97,12 @@ chroma_LTLIBRARIES += \
> libi420_yuy2_sse2_plugin.la \
> libi422_yuy2_sse2_plugin.la
> endif
> +
> +# DXVA2
> +libdxa9_plugin_la_SOURCES = video_chroma/dxa9.c \
> + video_chroma/copy.c video_chroma/copy.h
> +
> +if HAVE_AVCODEC_DXVA2
> +chroma_LTLIBRARIES += \
> + libdxa9_plugin.la
> +endif
> diff --git a/modules/video_chroma/dxa9.c b/modules/video_chroma/dxa9.c
> new file mode 100644
> index 0000000..9741e6a
> --- /dev/null
> +++ b/modules/video_chroma/dxa9.c
> @@ -0,0 +1,207 @@
> +/*****************************************************************************
> + * dxa9.c : DXVA2 GPU surface conversion module for vlc
> + *****************************************************************************
> + * Copyright (C) 2015 VLC authors and VideoLAN
> + * $Id$
> + *
> + * Authors: Steve Lhomme <robux4 at gmail.com>
> + *
> + * 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 <vlc_common.h>
> +#include <vlc_plugin.h>
> +#include <vlc_filter.h>
> +
> +#include "copy.h"
> +
> +static int OpenConverter( vlc_object_t * );
> +static void CloseConverter( vlc_object_t * );
> +
> +/*****************************************************************************
> + * Module descriptor.
> + *****************************************************************************/
> +vlc_module_begin ()
> + set_description( N_("Conversions from DXA9 to I420,YV12,NV12") )
> + set_capability( "video filter2", 10 )
> + set_callbacks( OpenConverter, CloseConverter )
> +vlc_module_end ()
> +
> +#include <windows.h>
> +#include <d3d9.h>
> +
> +struct picture_sys_t
> +{
> + LPDIRECT3DSURFACE9 surface;
> +};
> +
> +static bool GetLock(filter_t *p_filter, LPDIRECT3DSURFACE9 d3d,
> + D3DLOCKED_RECT *p_lock, D3DSURFACE_DESC *p_desc)
> +{
> + if (FAILED( IDirect3DSurface9_GetDesc(d3d, p_desc)))
> + return false;
> +
> + /* */
> + if (FAILED(IDirect3DSurface9_LockRect(d3d, p_lock, NULL, D3DLOCK_READONLY))) {
> + msg_Err(p_filter, "Failed to lock surface");
> + return false;
> + }
> +
> + return true;
> +}
> +
> +static void DXA9_YV12(filter_t *p_filter, picture_t *src, picture_t *dst)
> +{
> + copy_cache_t *p_copy_cache = (copy_cache_t*) p_filter->p_sys;
> +
> + D3DSURFACE_DESC desc;
> + D3DLOCKED_RECT lock;
> + if (!GetLock(p_filter, src->p_sys->surface, &lock, &desc))
> + return;
> +
> + 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] = {
> + lock.Pitch,
> + chroma_pitch,
> + chroma_pitch,
> + };
> +
> + 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,
> + };
> +
> + if (imc3) {
> + uint8_t *V = plane[1];
> + plane[1] = plane[2];
> + plane[2] = V;
> + }
> + CopyFromYv12(dst, plane, pitch, src->format.i_width,
> + src->format.i_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_height
> + };
> + size_t pitch[2] = {
> + lock.Pitch,
> + lock.Pitch,
> + };
> + CopyFromNv12(dst, plane, pitch, src->format.i_width,
> + src->format.i_height, p_copy_cache);
> + } else {
> + 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(src->p_sys->surface);
> +}
> +
> +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;
> +
> + D3DSURFACE_DESC desc;
> + D3DLOCKED_RECT lock;
> + if (!GetLock(p_filter, src->p_sys->surface, &lock, &desc))
> + return;
> +
> + if (desc.Format == MAKEFOURCC('N','V','1','2')) {
> + uint8_t *plane[2] = {
> + lock.pBits,
> + (uint8_t*)lock.pBits + lock.Pitch * src->format.i_height
> + };
> + size_t pitch[2] = {
> + lock.Pitch,
> + lock.Pitch,
> + };
> + CopyFromNv12ToNv12(dst, plane, pitch, src->format.i_width,
> + src->format.i_height, p_copy_cache);
> + } else {
> + msg_Err(p_filter, "Unsupported DXA9 conversion from 0x%08X to NV12", desc.Format);
> + }
> +
> + /* */
> + IDirect3DSurface9_UnlockRect(src->p_sys->surface);
> +}
> +
> +VIDEO_FILTER_WRAPPER (DXA9_YV12)
> +VIDEO_FILTER_WRAPPER (DXA9_NV12)
> +
> +static int OpenConverter( vlc_object_t *obj )
> +{
> + 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;
> + case VLC_CODEC_NV12:
> + p_filter->pf_video_filter = DXA9_NV12_Filter;
> + break;
> + default:
> + return VLC_EGENERIC;
> + }
> +
> + copy_cache_t *p_copy_cache = calloc(1, sizeof(*p_copy_cache));
> + if (!p_copy_cache)
> + return VLC_ENOMEM;
> + 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 CloseConverter( vlc_object_t *obj )
> +{
> + 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/po/POTFILES.in b/po/POTFILES.in
> index e66ae78..69a316c 100644
> --- a/po/POTFILES.in
> +++ b/po/POTFILES.in
> @@ -1062,6 +1062,7 @@ modules/text_renderer/svg.c
> modules/text_renderer/tdummy.c
> modules/text_renderer/win32text.c
> modules/video_chroma/chain.c
> +modules/video_chroma/dxa9.c
> modules/video_chroma/grey_yuv.c
> modules/video_chroma/i420_rgb16.c
> modules/video_chroma/i420_rgb8.c
> --
> 2.4.0
>
More information about the vlc-devel
mailing list