Commit 5914e379 authored by Steve Lhomme's avatar Steve Lhomme Committed by Jean-Baptiste Kempf

d3d11va: handle decoding to DXGI_FORMAT_420_OPAQUE using a ID3D11VideoProcessor

The DXGI_FORMAT_420_OPAQUE cannot be displayed or handled by the CPU. But it
can be converted using a VideoProcessor. It can convert directly to the textures
created by the vout.
Signed-off-by: Jean-Baptiste Kempf's avatarJean-Baptiste Kempf <jb@videolan.org>
parent bab96227
......@@ -127,6 +127,11 @@ struct vlc_va_sys_t
/* avcodec internals */
struct AVD3D11VAContext hw;
/* I420_OPAQUE processor */
ID3D11VideoProcessorEnumerator *procEnumerator;
ID3D11VideoProcessor *videoProcessor;
DXGI_FORMAT processorFormat;
};
/* VLC_CODEC_D3D11_OPAQUE */
......@@ -135,6 +140,7 @@ struct picture_sys_t
ID3D11VideoDecoderOutputView *decoder; /* may be NULL for pictures from the pool */
ID3D11Texture2D *texture;
ID3D11DeviceContext *context;
ID3D11VideoProcessorInputView *inputView; /* when used as processor input */
};
/* */
......@@ -148,7 +154,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 *);
......@@ -249,15 +255,54 @@ static int Extract(vlc_va_t *va, picture_t *output, uint8_t *data)
WaitForSingleObjectEx( sys->context_mutex, INFINITE, FALSE );
}
#endif
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);
if (sys->videoProcessor)
{
// 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->procEnumerator,
&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->videoProcessor,
(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;
......@@ -500,6 +545,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->videoProcessor)
ID3D11VideoProcessor_Release(va->sys->videoProcessor);
if (va->sys->procEnumerator)
ID3D11VideoProcessorEnumerator_Release(va->sys->procEnumerator);
}
/**
* It describes our Direct3D object
......@@ -666,7 +715,109 @@ 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 bool SetupProcessor(vlc_va_t *va, const video_format_t *fmt)
{
vlc_va_sys_t *sys = va->sys;
directx_sys_t *dx_sys = &va->sys->dx_sys;
HRESULT hr;
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.");
return false;
}
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 (%d) is supported for input", DxgiFormatToStr(format),format);
if (SUCCEEDED(hr) && (flags & D3D11_VIDEO_PROCESSOR_FORMAT_SUPPORT_OUTPUT))
msg_Dbg(va, "processor format %s (%d) is supported for output", DxgiFormatToStr(format),format);
}
#endif
DXGI_FORMAT processorOutput = DXGI_FORMAT_UNKNOWN;
if ( sys->render != DXGI_FORMAT_UNKNOWN )
{
/* shortcut for the rendering output */
hr = ID3D11VideoProcessorEnumerator_CheckVideoProcessorFormat(processorEnumerator, sys->render, &flags);
if (FAILED(hr) && !(flags & D3D11_VIDEO_PROCESSOR_FORMAT_SUPPORT_OUTPUT))
msg_Dbg(va, "processor format %s not supported for output", DxgiFormatToStr(sys->render));
else
processorOutput = 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);
for (UINT type = 0; type < processorCaps.RateConversionCapsCount; ++type)
{
hr = ID3D11VideoDevice_CreateVideoProcessor((ID3D11VideoDevice*) dx_sys->d3ddec,
processorEnumerator, type, &sys->videoProcessor);
if (SUCCEEDED(hr))
break;
sys->videoProcessor = NULL;
}
if (sys->videoProcessor != NULL)
{
sys->processorFormat = processorOutput;
sys->procEnumerator = processorEnumerator;
return true;
}
}
ID3D11VideoProcessorEnumerator_Release(processorEnumerator);
return false;
}
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;
......@@ -685,6 +836,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;
char *psz_decoder_name = directx_va_GetDecoderName(input);
......@@ -706,20 +858,21 @@ static int DxSetupOutput(vlc_va_t *va, const GUID *input)
// check the decoder can output to that format
const UINT i_quadSupportFlags = D3D11_FORMAT_SUPPORT_TEXTURE2D | D3D11_FORMAT_SUPPORT_SHADER_LOAD;
UINT i_formatSupport;
bool b_needsProcessor = true;
if( SUCCEEDED( ID3D11Device_CheckFormatSupport((ID3D11Device*) dx_sys->d3ddev,
processorInput[idx],
&i_formatSupport)) &&
( i_formatSupport & i_quadSupportFlags ) == i_quadSupportFlags )
b_needsProcessor = false;
if ( !b_needsProcessor )
( i_formatSupport & i_quadSupportFlags ) != i_quadSupportFlags )
{
msg_Dbg(va, "Using output format %s for decoder %s", DxgiFormatToStr(processorInput[idx]), psz_decoder_name);
va->sys->render = processorInput[idx];
free(psz_decoder_name);
return VLC_SUCCESS;
msg_Dbg(va, "Format %s needs a processor", DxgiFormatToStr(processorInput[idx]));
if (!SetupProcessor( va, fmt ))
continue;
msg_Dbg(va, "Using processor %s to %s", DxgiFormatToStr(processorInput[idx]), DxgiFormatToStr(va->sys->processorFormat));
}
msg_Dbg(va, "Using output format %s for decoder %s", DxgiFormatToStr(processorInput[idx]), psz_decoder_name);
va->sys->render = processorInput[idx];
free(psz_decoder_name);
return VLC_SUCCESS;
}
free(psz_decoder_name);
......@@ -871,6 +1024,8 @@ static void DestroyPicture(picture_t *picture)
{
picture_sys_t *p_sys = picture->p_sys;
ID3D11Texture2D_Release( p_sys->texture );
if (p_sys->inputView)
ID3D11View_Release( (ID3D11View*) p_sys->inputView );
free(p_sys);
free(picture);
......@@ -878,15 +1033,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;
pic_sys->context = sys->d3dctx;
if (sys->procEnumerator)
{
/* the decoder output will be used as a processor input */
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->procEnumerator,
&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,
......
......@@ -561,7 +561,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;
......
......@@ -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
......
......@@ -151,7 +151,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);
......@@ -613,8 +613,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;
......
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment