[vlc-devel] [PATCH 4/4] d3d11va: handle decoding to DXGI_FORMAT_420_OPAQUE using a ID3D11VideoProcessor
Steve Lhomme
robux4 at gmail.com
Mon Jul 20 13:33:07 CEST 2015
From: Steve Lhomme <robUx4 at gmail.com>
---
modules/codec/avcodec/d3d11va.c | 194 +++++++++++++++++++++++++++++++---
modules/codec/avcodec/directx_va.c | 2 +-
modules/codec/avcodec/directx_va.h | 2 +-
modules/codec/avcodec/dxva2.c | 5 +-
modules/video_output/msw/direct3d11.c | 8 +-
5 files changed, 189 insertions(+), 22 deletions(-)
diff --git a/modules/codec/avcodec/d3d11va.c b/modules/codec/avcodec/d3d11va.c
index d00e634..0d1f856 100644
--- a/modules/codec/avcodec/d3d11va.c
+++ b/modules/codec/avcodec/d3d11va.c
@@ -112,6 +112,11 @@ struct vlc_va_sys_t
/* Option conversion */
filter_t *filter;
+ /* video decoder for 420_OPAQUE */
+ ID3D11VideoProcessor *d3dprocessor;
+ DXGI_FORMAT processor_output;
+ ID3D11VideoProcessorEnumerator *d3dprocenum;
+
/* avcodec internals */
struct AVD3D11VAContext hw;
};
@@ -122,6 +127,7 @@ struct picture_sys_t
ID3D11VideoDecoderOutputView *decoder; /* may be NULL for pictures from the pool */
ID3D11Texture2D *texture;
ID3D11DeviceContext *context;
+ ID3D11VideoProcessorInputView *inputView;
};
/* */
@@ -135,7 +141,7 @@ static void D3dDestroyDeviceManager(vlc_va_t *);
static int DxCreateVideoService(vlc_va_t *);
static void DxDestroyVideoService(vlc_va_t *);
static int DxGetInputList(vlc_va_t *, input_list_t *);
-static int DxSetupOutput(vlc_va_t *, const GUID *);
+static int DxSetupOutput(vlc_va_t *, const GUID *, const video_format_t *);
static int DxCreateDecoderSurfaces(vlc_va_t *, int codec_id, const video_format_t *fmt, bool b_threading);
static void DxDestroySurfaces(vlc_va_t *);
@@ -222,20 +228,59 @@ static int Extract(vlc_va_t *va, picture_t *output, uint8_t *data)
if (output->format.i_chroma == VLC_CODEC_D3D11_OPAQUE)
{
- /* copy decoder slice to surface */
- D3D11_VIDEO_DECODER_OUTPUT_VIEW_DESC viewDesc;
picture_sys_t *p_sys_out = output->p_sys;
picture_sys_t *p_sys_in = surface->p_pic->p_sys;
assert(p_sys_out->texture != NULL);
assert(p_sys_in->decoder == src);
- ID3D11VideoDecoderOutputView_GetDesc( src, &viewDesc );
- ID3D11DeviceContext_CopySubresourceRegion(sys->d3dctx, (ID3D11Resource*) p_sys_out->texture,
- 0, 0, 0, 0,
- (ID3D11Resource*) p_sys_in->texture,
- viewDesc.Texture2D.ArraySlice,
- NULL);
+ if (sys->d3dprocessor)
+ {
+ // extract the decoded video to a the output Texture
+ if (p_sys_out->decoder == NULL)
+ {
+ D3D11_VIDEO_PROCESSOR_OUTPUT_VIEW_DESC outDesc = {
+ .ViewDimension = D3D11_VPOV_DIMENSION_TEXTURE2D,
+ };
+
+ HRESULT hr = ID3D11VideoDevice_CreateVideoProcessorOutputView((ID3D11VideoDevice*) sys->dx_sys.d3ddec,
+ (ID3D11Resource*) p_sys_out->texture,
+ sys->d3dprocenum,
+ &outDesc,
+ (ID3D11VideoProcessorOutputView**) &p_sys_out->decoder);
+ if (FAILED(hr))
+ {
+ msg_Err(va, "Failed to create the processor output. (hr=0x%lX)", hr);
+ return VLC_EGENERIC;
+ }
+ }
+
+ D3D11_VIDEO_PROCESSOR_STREAM stream = {
+ .Enable = TRUE,
+ .pInputSurface = p_sys_in->inputView,
+ };
+
+ HRESULT hr = ID3D11VideoContext_VideoProcessorBlt(sys->d3dvidctx, sys->d3dprocessor,
+ (ID3D11VideoProcessorOutputView*) p_sys_out->decoder,
+ 0, 1, &stream);
+ if (FAILED(hr))
+ {
+ msg_Err(va, "Failed to process the video. (hr=0x%lX)", hr);
+ return VLC_EGENERIC;
+ }
+ }
+ else
+ {
+ D3D11_VIDEO_DECODER_OUTPUT_VIEW_DESC viewDesc;
+ ID3D11VideoDecoderOutputView_GetDesc( src, &viewDesc );
+
+ /* copy decoder slice to surface */
+ ID3D11DeviceContext_CopySubresourceRegion(sys->d3dctx, (ID3D11Resource*) p_sys_out->texture,
+ 0, 0, 0, 0,
+ (ID3D11Resource*) p_sys_in->texture,
+ viewDesc.Texture2D.ArraySlice,
+ NULL);
+ }
}
else if (output->format.i_chroma == VLC_CODEC_YV12) {
va->sys->filter->owner.sys = output;
@@ -450,6 +495,10 @@ static void D3dDestroyDevice(vlc_va_t *va)
ID3D11VideoContext_Release(va->sys->d3dvidctx);
if (va->sys->d3dctx)
ID3D11DeviceContext_Release(va->sys->d3dctx);
+ if (va->sys->d3dprocessor)
+ ID3D11VideoProcessor_Release(va->sys->d3dprocessor);
+ if (va->sys->d3dprocenum)
+ ID3D11VideoProcessorEnumerator_Release(va->sys->d3dprocenum);
}
/**
* It describes our Direct3D object
@@ -615,7 +664,7 @@ static int DxGetInputList(vlc_va_t *va, input_list_t *p_list)
return VLC_SUCCESS;
}
-static int DxSetupOutput(vlc_va_t *va, const GUID *input)
+static int DxSetupOutput(vlc_va_t *va, const GUID *input, const video_format_t *fmt)
{
directx_sys_t *dx_sys = &va->sys->dx_sys;
HRESULT hr;
@@ -634,6 +683,7 @@ static int DxSetupOutput(vlc_va_t *va, const GUID *input)
if ( va->sys->render != DXGI_FORMAT_UNKNOWN )
processorInput[idx++] = va->sys->render;
processorInput[idx++] = DXGI_FORMAT_NV12;
+ processorInput[idx++] = DXGI_FORMAT_420_OPAQUE;
processorInput[idx++] = DXGI_FORMAT_UNKNOWN;
/* */
@@ -660,10 +710,104 @@ static int DxSetupOutput(vlc_va_t *va, const GUID *input)
( i_formatSupport & i_quadSupportFlags ) == i_quadSupportFlags )
b_needsProcessor = false;
- if ( !b_needsProcessor )
+ if ( b_needsProcessor )
{
- va->sys->render = processorInput[idx];
- return VLC_SUCCESS;
+ ID3D11VideoProcessorEnumerator *processorEnumerator;
+ D3D11_VIDEO_PROCESSOR_CONTENT_DESC processorDesc = {
+ .InputFrameFormat = D3D11_VIDEO_FRAME_FORMAT_PROGRESSIVE, /* TODO */
+ .InputFrameRate = {
+ .Numerator = fmt->i_frame_rate,
+ .Denominator = fmt->i_frame_rate_base,
+ },
+ .InputWidth = fmt->i_width,
+ .InputHeight = fmt->i_height,
+ .OutputWidth = fmt->i_width,
+ .OutputHeight = fmt->i_height,
+ .Usage = D3D11_VIDEO_USAGE_PLAYBACK_NORMAL,
+ };
+ hr = ID3D11VideoDevice_CreateVideoProcessorEnumerator((ID3D11VideoDevice*) dx_sys->d3ddec, &processorDesc, &processorEnumerator);
+ if ( processorEnumerator == NULL )
+ {
+ msg_Dbg(va, "Can't get a video processor for the video.");
+ continue;
+ }
+
+ UINT flags;
+#ifndef NDEBUG
+ for (int format = 0; format < 188; format++) {
+ hr = ID3D11VideoProcessorEnumerator_CheckVideoProcessorFormat(processorEnumerator, format, &flags);
+ if (SUCCEEDED(hr) && (flags & D3D11_VIDEO_PROCESSOR_FORMAT_SUPPORT_INPUT))
+ msg_Dbg(va, "processor format %s is supported for input", DxgiFormatToStr(format));
+ if (SUCCEEDED(hr) && (flags & D3D11_VIDEO_PROCESSOR_FORMAT_SUPPORT_OUTPUT))
+ msg_Dbg(va, "processor format %s is supported for output", DxgiFormatToStr(format));
+ }
+#endif
+ DXGI_FORMAT processorOutput = DXGI_FORMAT_UNKNOWN;
+ if ( va->sys->render != DXGI_FORMAT_UNKNOWN )
+ {
+ hr = ID3D11VideoProcessorEnumerator_CheckVideoProcessorFormat(processorEnumerator, va->sys->render, &flags);
+ if (FAILED(hr) && !(flags & D3D11_VIDEO_PROCESSOR_FORMAT_SUPPORT_OUTPUT))
+ msg_Dbg(va, "processor format %s not supported for output", DxgiFormatToStr(va->sys->render));
+ else
+ processorOutput = va->sys->render;
+ }
+
+ if (processorOutput == DXGI_FORMAT_UNKNOWN)
+ {
+ // check if we can create render texture of that format
+ // check the decoder can output to that format
+ const UINT i_quadSupportFlags = D3D11_FORMAT_SUPPORT_TEXTURE2D | D3D11_FORMAT_SUPPORT_SHADER_LOAD;
+ for (const d3d_format_t *output = GetRenderFormatList();
+ output->name != NULL; ++output)
+ {
+ UINT i_formatSupport;
+ if( SUCCEEDED( ID3D11Device_CheckFormatSupport((ID3D11Device*) dx_sys->d3ddev,
+ output->formatTexture,
+ &i_formatSupport)) &&
+ ( i_formatSupport & i_quadSupportFlags ) == i_quadSupportFlags )
+ {
+ msg_Dbg(va, "Render pixel format %s supported", DxgiFormatToStr(output->formatTexture) );
+
+ hr = ID3D11VideoProcessorEnumerator_CheckVideoProcessorFormat(processorEnumerator, output->formatTexture, &flags);
+ if (FAILED(hr) && !(flags & D3D11_VIDEO_PROCESSOR_FORMAT_SUPPORT_OUTPUT))
+ msg_Dbg(va, "Processor format %s not supported for output", DxgiFormatToStr(output->formatTexture));
+ else
+ {
+ processorOutput = output->formatTexture;
+ break;
+ }
+ }
+ }
+ }
+
+ if (processorOutput != DXGI_FORMAT_UNKNOWN)
+ {
+ D3D11_VIDEO_PROCESSOR_CAPS processorCaps;
+
+ hr = ID3D11VideoProcessorEnumerator_GetVideoProcessorCaps(processorEnumerator, &processorCaps);
+
+ ID3D11VideoProcessor *opaque_processor = NULL;
+ for (UINT type = 0; type < processorCaps.RateConversionCapsCount; ++type)
+ {
+ hr = ID3D11VideoDevice_CreateVideoProcessor((ID3D11VideoDevice*) dx_sys->d3ddec, processorEnumerator, type, &opaque_processor);
+ if (SUCCEEDED(hr))
+ break;
+ opaque_processor = NULL;
+ }
+
+ if (opaque_processor != NULL)
+ {
+ msg_Dbg(va, "Using processor %s to %s", DxgiFormatToStr(processorInput[idx]), DxgiFormatToStr(processorOutput));
+
+ va->sys->d3dprocessor = opaque_processor;
+ va->sys->processor_output = processorOutput;
+ va->sys->render = processorInput[idx];
+ va->sys->d3dprocenum = processorEnumerator;
+ return VLC_SUCCESS;
+ }
+
+ }
+ ID3D11VideoProcessorEnumerator_Release(processorEnumerator);
}
}
@@ -814,6 +958,7 @@ static void DestroyPicture(picture_t *picture)
{
picture_sys_t *p_sys = picture->p_sys;
ID3D11Texture2D_Release( p_sys->texture );
+ ID3D11View_Release( (ID3D11View*) p_sys->inputView );
free(p_sys);
free(picture);
@@ -821,16 +966,37 @@ static void DestroyPicture(picture_t *picture)
static picture_t *DxAllocPicture(vlc_va_t *va, const video_format_t *fmt, unsigned index)
{
+ vlc_va_sys_t *sys = va->sys;
video_format_t src_fmt = *fmt;
src_fmt.i_chroma = VLC_CODEC_D3D11_OPAQUE;
picture_sys_t *pic_sys = calloc(1, sizeof(*pic_sys));
if (unlikely(pic_sys == NULL))
return NULL;
- pic_sys->decoder = (ID3D11VideoDecoderOutputView*) va->sys->dx_sys.hw_surface[index];
+ pic_sys->decoder = (ID3D11VideoDecoderOutputView*) sys->dx_sys.hw_surface[index];
ID3D11VideoDecoderOutputView_GetResource(pic_sys->decoder, (ID3D11Resource**) &pic_sys->texture);
pic_sys->context = va->sys->d3dctx;
+ if (sys->d3dprocenum)
+ {
+ D3D11_VIDEO_PROCESSOR_INPUT_VIEW_DESC inDesc = {
+ .ViewDimension = D3D11_VPIV_DIMENSION_TEXTURE2D,
+ .Texture2D.MipSlice = 0,
+ .Texture2D.ArraySlice = index,
+ };
+
+ HRESULT hr = ID3D11VideoDevice_CreateVideoProcessorInputView((ID3D11VideoDevice*) sys->dx_sys.d3ddec,
+ (ID3D11Resource*) pic_sys->texture,
+ sys->d3dprocenum,
+ &inDesc,
+ &pic_sys->inputView);
+ if (FAILED(hr))
+ {
+ msg_Err(va, "Failed to create the processor input ArraySlice=%d. (hr=0x%lX)", inDesc.Texture2D.ArraySlice, hr);
+ return NULL;
+ }
+ }
+
picture_resource_t res = {
.p_sys = pic_sys,
.pf_destroy = DestroyPicture,
diff --git a/modules/codec/avcodec/directx_va.c b/modules/codec/avcodec/directx_va.c
index 034a572..917422d 100644
--- a/modules/codec/avcodec/directx_va.c
+++ b/modules/codec/avcodec/directx_va.c
@@ -548,7 +548,7 @@ static int FindVideoServiceConversion(vlc_va_t *va, directx_sys_t *dx_sys, const
/* */
msg_Dbg(va, "Trying to use '%s' as input", mode->name);
- if (dx_sys->pf_setup_output(va, mode->guid)==VLC_SUCCESS)
+ if (dx_sys->pf_setup_output( va, mode->guid, &fmt->video )==VLC_SUCCESS)
{
dx_sys->input = *mode->guid;
err = VLC_SUCCESS;
diff --git a/modules/codec/avcodec/directx_va.h b/modules/codec/avcodec/directx_va.h
index 7559be2..e1947d8 100644
--- a/modules/codec/avcodec/directx_va.h
+++ b/modules/codec/avcodec/directx_va.h
@@ -110,7 +110,7 @@ typedef struct
* Find a suitable decoder configuration for the input and set the
* internal state to use that output
*/
- int (*pf_setup_output)(vlc_va_t *, const GUID *input);
+ int (*pf_setup_output)(vlc_va_t *, const GUID *input, const video_format_t *fmt);
/**
* Create the DirectX surfaces in hw_surface and the decoder in decoder
diff --git a/modules/codec/avcodec/dxva2.c b/modules/codec/avcodec/dxva2.c
index d92384b..4e45f0d 100644
--- a/modules/codec/avcodec/dxva2.c
+++ b/modules/codec/avcodec/dxva2.c
@@ -152,7 +152,7 @@ static void D3dDestroyDeviceManager(vlc_va_t *);
static int DxCreateVideoService(vlc_va_t *);
static void DxDestroyVideoService(vlc_va_t *);
static int DxGetInputList(vlc_va_t *, input_list_t *);
-static int DxSetupOutput(vlc_va_t *, const GUID *);
+static int DxSetupOutput(vlc_va_t *, const GUID *, const video_format_t *);
static int DxCreateVideoDecoder(vlc_va_t *,
int codec_id, const video_format_t *, bool);
@@ -614,8 +614,9 @@ static int DxGetInputList(vlc_va_t *va, input_list_t *p_list)
return VLC_SUCCESS;
}
-static int DxSetupOutput(vlc_va_t *va, const GUID *input)
+static int DxSetupOutput(vlc_va_t *va, const GUID *input, const video_format_t *fmt)
{
+ VLC_UNUSED(fmt);
int err = VLC_EGENERIC;
UINT output_count = 0;
D3DFORMAT *output_list = NULL;
diff --git a/modules/video_output/msw/direct3d11.c b/modules/video_output/msw/direct3d11.c
index 9894a78..486e94b 100644
--- a/modules/video_output/msw/direct3d11.c
+++ b/modules/video_output/msw/direct3d11.c
@@ -529,9 +529,9 @@ static picture_pool_t *Pool(vout_display_t *vd, unsigned pool_size)
texDesc.SampleDesc.Count = 1;
texDesc.MiscFlags = 0; //D3D11_RESOURCE_MISC_SHARED;
texDesc.ArraySize = 1;
- texDesc.Usage = D3D11_USAGE_DYNAMIC;
- texDesc.BindFlags = D3D11_BIND_SHADER_RESOURCE;
- texDesc.CPUAccessFlags = D3D11_CPU_ACCESS_WRITE;
+ texDesc.Usage = D3D11_USAGE_DEFAULT;
+ texDesc.BindFlags = D3D11_BIND_RENDER_TARGET | D3D11_BIND_SHADER_RESOURCE;
+ texDesc.CPUAccessFlags = 0;
unsigned surface_count;
for (surface_count = 0; surface_count < pool_size; surface_count++) {
@@ -858,7 +858,7 @@ static int Direct3D11Open(vout_display_t *vd, video_format_t *fmt)
#if !VLC_WINSTORE_APP
- UINT creationFlags = 0;
+ UINT creationFlags = D3D11_CREATE_DEVICE_VIDEO_SUPPORT;
HRESULT hr = S_OK;
# if !defined(NDEBUG) && defined(_MSC_VER)
--
2.4.2
More information about the vlc-devel
mailing list