[vlc-devel] [PATCH] d3d11va: handle decoding to DXGI_FORMAT_420_OPAQUE using a ID3D11VideoProcessor

Steve Lhomme robux4 at videolabs.io
Sun May 15 21:11:51 CEST 2016


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.
---
 modules/codec/avcodec/d3d11va.c    | 219 +++++++++++++++++++++++++++++++++----
 modules/codec/avcodec/directx_va.c |   2 +-
 modules/codec/avcodec/directx_va.h |   2 +-
 modules/codec/avcodec/dxva2.c      |   5 +-
 4 files changed, 203 insertions(+), 25 deletions(-)

diff --git a/modules/codec/avcodec/d3d11va.c b/modules/codec/avcodec/d3d11va.c
index 308a791..79d731a 100644
--- a/modules/codec/avcodec/d3d11va.c
+++ b/modules/codec/avcodec/d3d11va.c
@@ -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,
diff --git a/modules/codec/avcodec/directx_va.c b/modules/codec/avcodec/directx_va.c
index 90e9611..fd915e3 100644
--- a/modules/codec/avcodec/directx_va.c
+++ b/modules/codec/avcodec/directx_va.c
@@ -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;
diff --git a/modules/codec/avcodec/directx_va.h b/modules/codec/avcodec/directx_va.h
index 905ae0d..dcc4f6e 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 4ab9fc9..89bbb13 100644
--- a/modules/codec/avcodec/dxva2.c
+++ b/modules/codec/avcodec/dxva2.c
@@ -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;
-- 
2.8.1



More information about the vlc-devel mailing list