diff mbox series

[FFmpeg-devel,1/3] avcodec/dxva2_hevc: add support for parsing HEVC Range Extension data

Message ID 20200313102354.2500-1-robux4@ycbcr.xyz
State New
Headers show
Series [FFmpeg-devel,1/3] avcodec/dxva2_hevc: add support for parsing HEVC Range Extension data
Related show

Checks

Context Check Description
andriy/ffmpeg-patchwork pending
andriy/ffmpeg-patchwork success Applied patch
andriy/ffmpeg-patchwork success Configure finished
andriy/ffmpeg-patchwork success Make finished
andriy/ffmpeg-patchwork success Make fate finished

Commit Message

Steve Lhomme March 13, 2020, 10:23 a.m. UTC
Mimick the existing structure and add the extra fields from the Range Extension
in a wrapping structure.

The FF_DXVA2_WORKAROUND_HEVC_REXT is set by the decoder user to signal the
selected decoder is expecting this extended structure rather than the default
one.
---
 libavcodec/d3d11va.h    |  1 +
 libavcodec/dxva2.h      |  1 +
 libavcodec/dxva2_hevc.c | 79 ++++++++++++++++++++++++++++++++++++++---
 3 files changed, 76 insertions(+), 5 deletions(-)

Comments

Hendrik Leppkes March 13, 2020, 2:46 p.m. UTC | #1
On Fri, Mar 13, 2020 at 11:25 AM Steve Lhomme <robux4@ycbcr.xyz> wrote:
>
> Mimick the existing structure and add the extra fields from the Range Extension
> in a wrapping structure.
>
> The FF_DXVA2_WORKAROUND_HEVC_REXT is set by the decoder user to signal the
> selected decoder is expecting this extended structure rather than the default
> one.
> ---
>  libavcodec/d3d11va.h    |  1 +
>  libavcodec/dxva2.h      |  1 +
>  libavcodec/dxva2_hevc.c | 79 ++++++++++++++++++++++++++++++++++++++---
>  3 files changed, 76 insertions(+), 5 deletions(-)
>
> diff --git a/libavcodec/d3d11va.h b/libavcodec/d3d11va.h
> index 6816b6c1e6..68a69c372d 100644
> --- a/libavcodec/d3d11va.h
> +++ b/libavcodec/d3d11va.h
> @@ -47,6 +47,7 @@
>
>  #define FF_DXVA2_WORKAROUND_SCALING_LIST_ZIGZAG 1 ///< Work around for Direct3D11 and old UVD/UVD+ ATI video cards
>  #define FF_DXVA2_WORKAROUND_INTEL_CLEARVIDEO    2 ///< Work around for Direct3D11 and old Intel GPUs with ClearVideo interface
> +#define FF_DXVA2_WORKAROUND_HEVC_REXT           4 ///< Signal the D3D11VA decoder is using the HEVC Rext picture structure
>
>  /**
>   * This structure is used to provides the necessary configurations and data
> diff --git a/libavcodec/dxva2.h b/libavcodec/dxva2.h
> index 22c93992f2..024999239d 100644
> --- a/libavcodec/dxva2.h
> +++ b/libavcodec/dxva2.h
> @@ -47,6 +47,7 @@
>
>  #define FF_DXVA2_WORKAROUND_SCALING_LIST_ZIGZAG 1 ///< Work around for DXVA2 and old UVD/UVD+ ATI video cards
>  #define FF_DXVA2_WORKAROUND_INTEL_CLEARVIDEO    2 ///< Work around for DXVA2 and old Intel GPUs with ClearVideo interface
> +#define FF_DXVA2_WORKAROUND_HEVC_REXT           4 ///< Signal the DXVA2 decoder is using the HEVC Rext picture structure
>
>  /**
>   * This structure is used to provides the necessary configurations and data
> diff --git a/libavcodec/dxva2_hevc.c b/libavcodec/dxva2_hevc.c
> index dbb701fb1c..98b3e74bd7 100644
> --- a/libavcodec/dxva2_hevc.c
> +++ b/libavcodec/dxva2_hevc.c
> @@ -26,10 +26,47 @@
>  #include "hevc_data.h"
>  #include "hevcdec.h"
>
> +#pragma pack(push, 1)
> +typedef struct
> +{
> +    DXVA_PicParams_HEVC main;
> +
> +    // HEVC Range Extension
> +    __C89_NAMELESS union {
> +        __C89_NAMELESS struct {
> +            UINT32 transform_skip_rotation_enabled_flag : 1;
> +            UINT32 transform_skip_context_enabled_flag : 1;
> +            UINT32 implicit_rdpcm_enabled_flag : 1;
> +            UINT32 explicit_rdpcm_enabled_flag : 1;
> +            UINT32 extended_precision_processing_flag : 1;
> +            UINT32 intra_smoothing_disabled_flag : 1;
> +            UINT32 high_precision_offsets_enabled_flag : 1;
> +            UINT32 persistent_rice_adaptation_enabled_flag : 1;
> +            UINT32 cabac_bypass_alignment_enabled_flag : 1;
> +            UINT32 cross_component_prediction_enabled_flag : 1;
> +            UINT32 chroma_qp_offset_list_enabled_flag : 1;
> +            UINT32 BitDepthLuma16 : 1; // TODO merge in ReservedBits5 if not needed
> +            UINT32 BitDepthChroma16 : 1; // TODO merge in ReservedBits5 if not needed
> +            UINT32 ReservedBits8 : 19;
> +        };
> +        UINT32 dwRangeExtensionFlags;
> +    };
> +
> +    UCHAR diff_cu_chroma_qp_offset_depth;
> +    UCHAR chroma_qp_offset_list_len_minus1;
> +    UCHAR log2_sao_offset_scale_luma;
> +    UCHAR log2_sao_offset_scale_chroma;
> +    UCHAR log2_max_transform_skip_block_size_minus2;
> +    CHAR cb_qp_offset_list[6];
> +    CHAR cr_qp_offset_list[6];
> +
> +} DXVA_PicParams_HEVC_Rext;
> +#pragma pack(pop)
> +

Where is this struct specified? I don't see it in the latest released
Windows SDK.

- Hendrik
Steve Lhomme March 15, 2020, 7:12 a.m. UTC | #2
> On March 13, 2020 3:46 PM Hendrik Leppkes <h.leppkes@gmail.com> wrote:
> 
>  
> On Fri, Mar 13, 2020 at 11:25 AM Steve Lhomme <robux4@ycbcr.xyz> wrote:
> >
> > Mimick the existing structure and add the extra fields from the Range Extension
> > in a wrapping structure.
> >
> > The FF_DXVA2_WORKAROUND_HEVC_REXT is set by the decoder user to signal the
> > selected decoder is expecting this extended structure rather than the default
> > one.
> > ---
> >  libavcodec/d3d11va.h    |  1 +
> >  libavcodec/dxva2.h      |  1 +
> >  libavcodec/dxva2_hevc.c | 79 ++++++++++++++++++++++++++++++++++++++---
> >  3 files changed, 76 insertions(+), 5 deletions(-)
> >
> > diff --git a/libavcodec/d3d11va.h b/libavcodec/d3d11va.h
> > index 6816b6c1e6..68a69c372d 100644
> > --- a/libavcodec/d3d11va.h
> > +++ b/libavcodec/d3d11va.h
> > @@ -47,6 +47,7 @@
> >
> >  #define FF_DXVA2_WORKAROUND_SCALING_LIST_ZIGZAG 1 ///< Work around for Direct3D11 and old UVD/UVD+ ATI video cards
> >  #define FF_DXVA2_WORKAROUND_INTEL_CLEARVIDEO    2 ///< Work around for Direct3D11 and old Intel GPUs with ClearVideo interface
> > +#define FF_DXVA2_WORKAROUND_HEVC_REXT           4 ///< Signal the D3D11VA decoder is using the HEVC Rext picture structure
> >
> >  /**
> >   * This structure is used to provides the necessary configurations and data
> > diff --git a/libavcodec/dxva2.h b/libavcodec/dxva2.h
> > index 22c93992f2..024999239d 100644
> > --- a/libavcodec/dxva2.h
> > +++ b/libavcodec/dxva2.h
> > @@ -47,6 +47,7 @@
> >
> >  #define FF_DXVA2_WORKAROUND_SCALING_LIST_ZIGZAG 1 ///< Work around for DXVA2 and old UVD/UVD+ ATI video cards
> >  #define FF_DXVA2_WORKAROUND_INTEL_CLEARVIDEO    2 ///< Work around for DXVA2 and old Intel GPUs with ClearVideo interface
> > +#define FF_DXVA2_WORKAROUND_HEVC_REXT           4 ///< Signal the DXVA2 decoder is using the HEVC Rext picture structure
> >
> >  /**
> >   * This structure is used to provides the necessary configurations and data
> > diff --git a/libavcodec/dxva2_hevc.c b/libavcodec/dxva2_hevc.c
> > index dbb701fb1c..98b3e74bd7 100644
> > --- a/libavcodec/dxva2_hevc.c
> > +++ b/libavcodec/dxva2_hevc.c
> > @@ -26,10 +26,47 @@
> >  #include "hevc_data.h"
> >  #include "hevcdec.h"
> >
> > +#pragma pack(push, 1)
> > +typedef struct
> > +{
> > +    DXVA_PicParams_HEVC main;
> > +
> > +    // HEVC Range Extension
> > +    __C89_NAMELESS union {
> > +        __C89_NAMELESS struct {
> > +            UINT32 transform_skip_rotation_enabled_flag : 1;
> > +            UINT32 transform_skip_context_enabled_flag : 1;
> > +            UINT32 implicit_rdpcm_enabled_flag : 1;
> > +            UINT32 explicit_rdpcm_enabled_flag : 1;
> > +            UINT32 extended_precision_processing_flag : 1;
> > +            UINT32 intra_smoothing_disabled_flag : 1;
> > +            UINT32 high_precision_offsets_enabled_flag : 1;
> > +            UINT32 persistent_rice_adaptation_enabled_flag : 1;
> > +            UINT32 cabac_bypass_alignment_enabled_flag : 1;
> > +            UINT32 cross_component_prediction_enabled_flag : 1;
> > +            UINT32 chroma_qp_offset_list_enabled_flag : 1;
> > +            UINT32 BitDepthLuma16 : 1; // TODO merge in ReservedBits5 if not needed
> > +            UINT32 BitDepthChroma16 : 1; // TODO merge in ReservedBits5 if not needed
> > +            UINT32 ReservedBits8 : 19;
> > +        };
> > +        UINT32 dwRangeExtensionFlags;
> > +    };
> > +
> > +    UCHAR diff_cu_chroma_qp_offset_depth;
> > +    UCHAR chroma_qp_offset_list_len_minus1;
> > +    UCHAR log2_sao_offset_scale_luma;
> > +    UCHAR log2_sao_offset_scale_chroma;
> > +    UCHAR log2_max_transform_skip_block_size_minus2;
> > +    CHAR cb_qp_offset_list[6];
> > +    CHAR cr_qp_offset_list[6];
> > +
> > +} DXVA_PicParams_HEVC_Rext;
> > +#pragma pack(pop)
> > +
> 
> Where is this struct specified? I don't see it in the latest released
> Windows SDK.

It is not. It is reversed engineered from the existing structure and wild guessing based on the HEVC Range Extension specs. The bits/fields are in the same order as the specs. Then I tested some GUIDs that output non 4:2:0 pixel formats on an Intel GPU that is known to decoder HEVC Range Extension.

Apparently NVIDIA doesn't provide DXVA GUIDs that output non 4:2:0 pixel formats on GPUs that supposedly decode HEVC 444 (likley nvdec only). AMD doesn't have such GPUs AFAIK.

So for now it's Intel only.
Hendrik Leppkes March 15, 2020, 12:05 p.m. UTC | #3
On Sun, Mar 15, 2020 at 8:12 AM Steve Lhomme <robux4@ycbcr.xyz> wrote:
> > Where is this struct specified? I don't see it in the latest released
> > Windows SDK.
>
> It is not. It is reversed engineered from the existing structure and wild guessing based on the HEVC Range Extension specs. The bits/fields are in the same order as the specs. Then I tested some GUIDs that output non 4:2:0 pixel formats on an Intel GPU that is known to decoder HEVC Range Extension.
>
> Apparently NVIDIA doesn't provide DXVA GUIDs that output non 4:2:0 pixel formats on GPUs that supposedly decode HEVC 444 (likley nvdec only). AMD doesn't have such GPUs AFAIK.
>
> So for now it's Intel only.

That seems very hackish for something thats otherwise cleanly based on
official specifications only.  Maybe try to get them to release specs
for this instead?

- Hendrik
Steve Lhomme March 17, 2020, 9:17 a.m. UTC | #4
On 2020-03-15 13:05, Hendrik Leppkes wrote:
> On Sun, Mar 15, 2020 at 8:12 AM Steve Lhomme <robux4@ycbcr.xyz> wrote:
>>> Where is this struct specified? I don't see it in the latest released
>>> Windows SDK.
>>
>> It is not. It is reversed engineered from the existing structure and wild guessing based on the HEVC Range Extension specs. The bits/fields are in the same order as the specs. Then I tested some GUIDs that output non 4:2:0 pixel formats on an Intel GPU that is known to decoder HEVC Range Extension.
>>
>> Apparently NVIDIA doesn't provide DXVA GUIDs that output non 4:2:0 pixel formats on GPUs that supposedly decode HEVC 444 (likley nvdec only). AMD doesn't have such GPUs AFAIK.
>>
>> So for now it's Intel only.
> 
> That seems very hackish for something thats otherwise cleanly based on
> official specifications only.  Maybe try to get them to release specs
> for this instead?

 From what I understand only MS publishes DXVA specs and it seems they 
have no intention on doing one for HEVC Range Extension. That may be why 
NVIDIA has not implemented it. I don't know if Intel is planning to 
release a documentation.
diff mbox series

Patch

diff --git a/libavcodec/d3d11va.h b/libavcodec/d3d11va.h
index 6816b6c1e6..68a69c372d 100644
--- a/libavcodec/d3d11va.h
+++ b/libavcodec/d3d11va.h
@@ -47,6 +47,7 @@ 
 
 #define FF_DXVA2_WORKAROUND_SCALING_LIST_ZIGZAG 1 ///< Work around for Direct3D11 and old UVD/UVD+ ATI video cards
 #define FF_DXVA2_WORKAROUND_INTEL_CLEARVIDEO    2 ///< Work around for Direct3D11 and old Intel GPUs with ClearVideo interface
+#define FF_DXVA2_WORKAROUND_HEVC_REXT           4 ///< Signal the D3D11VA decoder is using the HEVC Rext picture structure
 
 /**
  * This structure is used to provides the necessary configurations and data
diff --git a/libavcodec/dxva2.h b/libavcodec/dxva2.h
index 22c93992f2..024999239d 100644
--- a/libavcodec/dxva2.h
+++ b/libavcodec/dxva2.h
@@ -47,6 +47,7 @@ 
 
 #define FF_DXVA2_WORKAROUND_SCALING_LIST_ZIGZAG 1 ///< Work around for DXVA2 and old UVD/UVD+ ATI video cards
 #define FF_DXVA2_WORKAROUND_INTEL_CLEARVIDEO    2 ///< Work around for DXVA2 and old Intel GPUs with ClearVideo interface
+#define FF_DXVA2_WORKAROUND_HEVC_REXT           4 ///< Signal the DXVA2 decoder is using the HEVC Rext picture structure
 
 /**
  * This structure is used to provides the necessary configurations and data
diff --git a/libavcodec/dxva2_hevc.c b/libavcodec/dxva2_hevc.c
index dbb701fb1c..98b3e74bd7 100644
--- a/libavcodec/dxva2_hevc.c
+++ b/libavcodec/dxva2_hevc.c
@@ -26,10 +26,47 @@ 
 #include "hevc_data.h"
 #include "hevcdec.h"
 
+#pragma pack(push, 1)
+typedef struct
+{
+    DXVA_PicParams_HEVC main;
+
+    // HEVC Range Extension
+    __C89_NAMELESS union {
+        __C89_NAMELESS struct {
+            UINT32 transform_skip_rotation_enabled_flag : 1;
+            UINT32 transform_skip_context_enabled_flag : 1;
+            UINT32 implicit_rdpcm_enabled_flag : 1;
+            UINT32 explicit_rdpcm_enabled_flag : 1;
+            UINT32 extended_precision_processing_flag : 1;
+            UINT32 intra_smoothing_disabled_flag : 1;
+            UINT32 high_precision_offsets_enabled_flag : 1;
+            UINT32 persistent_rice_adaptation_enabled_flag : 1;
+            UINT32 cabac_bypass_alignment_enabled_flag : 1;
+            UINT32 cross_component_prediction_enabled_flag : 1;
+            UINT32 chroma_qp_offset_list_enabled_flag : 1;
+            UINT32 BitDepthLuma16 : 1; // TODO merge in ReservedBits5 if not needed
+            UINT32 BitDepthChroma16 : 1; // TODO merge in ReservedBits5 if not needed
+            UINT32 ReservedBits8 : 19;
+        };
+        UINT32 dwRangeExtensionFlags;
+    };
+
+    UCHAR diff_cu_chroma_qp_offset_depth;
+    UCHAR chroma_qp_offset_list_len_minus1;
+    UCHAR log2_sao_offset_scale_luma;
+    UCHAR log2_sao_offset_scale_chroma;
+    UCHAR log2_max_transform_skip_block_size_minus2;
+    CHAR cb_qp_offset_list[6];
+    CHAR cr_qp_offset_list[6];
+
+} DXVA_PicParams_HEVC_Rext;
+#pragma pack(pop)
+
 #define MAX_SLICES 256
 
 struct hevc_dxva2_picture_context {
-    DXVA_PicParams_HEVC   pp;
+    DXVA_PicParams_HEVC_Rext pp;
     DXVA_Qmatrix_HEVC     qm;
     unsigned              slice_count;
     DXVA_Slice_HEVC_Short slice_short[MAX_SLICES];
@@ -55,18 +92,48 @@  static int get_refpic_index(const DXVA_PicParams_HEVC *pp, int surface_index)
 }
 
 static void fill_picture_parameters(const AVCodecContext *avctx, AVDXVAContext *ctx, const HEVCContext *h,
-                                    DXVA_PicParams_HEVC *pp)
+                                    DXVA_PicParams_HEVC_Rext *ppext)
 {
     const HEVCFrame *current_picture = h->ref;
     const HEVCSPS *sps = h->ps.sps;
     const HEVCPPS *pps = h->ps.pps;
     int i, j;
+    DXVA_PicParams_HEVC *pp = &ppext->main;
 
-    memset(pp, 0, sizeof(*pp));
+    memset(ppext, 0, sizeof(*ppext));
 
     pp->PicWidthInMinCbsY  = sps->min_cb_width;
     pp->PicHeightInMinCbsY = sps->min_cb_height;
 
+    if (sps->sps_range_extension_flag) {
+        ppext->dwRangeExtensionFlags |= (sps->transform_skip_rotation_enabled_flag     <<  0) |
+                                        (sps->transform_skip_context_enabled_flag      <<  1) |
+                                        (sps->implicit_rdpcm_enabled_flag              <<  2) |
+                                        (sps->explicit_rdpcm_enabled_flag              <<  3) |
+                                        (sps->extended_precision_processing_flag       <<  4) |
+                                        (sps->intra_smoothing_disabled_flag            <<  5) |
+                                        (sps->high_precision_offsets_enabled_flag      <<  5) |
+                                        (sps->persistent_rice_adaptation_enabled_flag  <<  7) |
+                                        (sps->cabac_bypass_alignment_enabled_flag      <<  8);
+    }
+    if (pps->pps_range_extensions_flag) {
+        ppext->dwRangeExtensionFlags |= (pps->cross_component_prediction_enabled_flag  <<  9) |
+                                        (pps->chroma_qp_offset_list_enabled_flag       << 10);
+        if (pps->chroma_qp_offset_list_enabled_flag) {
+            ppext->diff_cu_chroma_qp_offset_depth   = pps->diff_cu_chroma_qp_offset_depth;
+            ppext->chroma_qp_offset_list_len_minus1 = pps->chroma_qp_offset_list_len_minus1;
+            for (i = 0; i <= pps->chroma_qp_offset_list_len_minus1; i++) {
+                ppext->cb_qp_offset_list[i] = pps->cb_qp_offset_list[i];
+                ppext->cr_qp_offset_list[i] = pps->cr_qp_offset_list[i];
+            }
+        }
+        ppext->log2_sao_offset_scale_luma   = pps->log2_sao_offset_scale_luma;
+        ppext->log2_sao_offset_scale_chroma = pps->log2_sao_offset_scale_chroma;
+        if (pps->transform_skip_enabled_flag) {
+            ppext->log2_max_transform_skip_block_size_minus2 = pps->log2_max_transform_skip_block_size - 2;
+        }
+    }
+
     pp->wFormatAndSequenceInfoFlags = (sps->chroma_format_idc             <<  0) |
                                       (sps->separate_colour_plane_flag    <<  2) |
                                       ((sps->bit_depth - 8)               <<  3) |
@@ -402,16 +469,18 @@  static int dxva2_hevc_decode_slice(AVCodecContext *avctx,
 
 static int dxva2_hevc_end_frame(AVCodecContext *avctx)
 {
+    AVDXVAContext *ctx = DXVA_CONTEXT(avctx);
     HEVCContext *h = avctx->priv_data;
     struct hevc_dxva2_picture_context *ctx_pic = h->ref->hwaccel_picture_private;
-    int scale = ctx_pic->pp.dwCodingParamToolFlags & 1;
+    int scale = ctx_pic->pp.main.dwCodingParamToolFlags & 1;
+    int rext = (DXVA_CONTEXT_WORKAROUND(avctx, ctx) & FF_DXVA2_WORKAROUND_HEVC_REXT);
     int ret;
 
     if (ctx_pic->slice_count <= 0 || ctx_pic->bitstream_size <= 0)
         return -1;
 
     ret = ff_dxva2_common_end_frame(avctx, h->ref->frame,
-                                    &ctx_pic->pp, sizeof(ctx_pic->pp),
+                                    &ctx_pic->pp, rext ? sizeof(ctx_pic->pp) : sizeof(ctx_pic->pp.main),
                                     scale ? &ctx_pic->qm : NULL, scale ? sizeof(ctx_pic->qm) : 0,
                                     commit_bitstream_and_slice_buffer);
     return ret;