Module: Mesa
Branch: main
Commit: 2785d54b71c19a2f9d191af21794bf5b919d7952
URL:    
http://cgit.freedesktop.org/mesa/mesa/commit/?id=2785d54b71c19a2f9d191af21794bf5b919d7952

Author: David Rosca <[email protected]>
Date:   Sun Jul 16 20:26:18 2023 +0200

frontends/va: Parse H264 SPS for video signal parameters

Since packed headers support is now advertised for H264,
it also fixes encoding into mkv with ffmpeg.

Closes: https://gitlab.freedesktop.org/mesa/mesa/-/issues/3524

Reviewed-by: Ruijing Dong <[email protected]>
Part-of: <https://gitlab.freedesktop.org/mesa/mesa/-/merge_requests/24174>

---

 src/gallium/frontends/va/config.c           |   4 +
 src/gallium/frontends/va/picture.c          |  13 +++
 src/gallium/frontends/va/picture_h264_enc.c | 129 ++++++++++++++++++++++++++++
 src/gallium/frontends/va/va_private.h       |   1 +
 src/gallium/include/pipe/p_video_state.h    |   7 ++
 5 files changed, 154 insertions(+)

diff --git a/src/gallium/frontends/va/config.c 
b/src/gallium/frontends/va/config.c
index 0e484763c13..84742148e3f 100644
--- a/src/gallium/frontends/va/config.c
+++ b/src/gallium/frontends/va/config.c
@@ -235,6 +235,8 @@ vlVaGetConfigAttributes(VADriverContextP ctx, VAProfile 
profile, VAEntrypoint en
             break;
          case VAConfigAttribEncPackedHeaders:
             value = VA_ENC_PACKED_HEADER_NONE;
+            if ((u_reduce_video_profile(ProfileToPipe(profile)) == 
PIPE_VIDEO_FORMAT_MPEG4_AVC))
+               value |= VA_ENC_PACKED_HEADER_SEQUENCE;
             if ((u_reduce_video_profile(ProfileToPipe(profile)) == 
PIPE_VIDEO_FORMAT_HEVC))
                value |= VA_ENC_PACKED_HEADER_SEQUENCE;
             else if (u_reduce_video_profile(ProfileToPipe(profile)) == 
PIPE_VIDEO_FORMAT_AV1)
@@ -620,6 +622,8 @@ vlVaCreateConfig(VADriverContextP ctx, VAProfile profile, 
VAEntrypoint entrypoin
       if (attrib_list[i].type == VAConfigAttribEncPackedHeaders) {
          if (config->entrypoint != PIPE_VIDEO_ENTRYPOINT_ENCODE ||
              (((attrib_list[i].value != 0)) &&
+              ((attrib_list[i].value != 1) || 
u_reduce_video_profile(ProfileToPipe(profile))
+               != PIPE_VIDEO_FORMAT_MPEG4_AVC) &&
               ((attrib_list[i].value != 1) || 
u_reduce_video_profile(ProfileToPipe(profile))
                != PIPE_VIDEO_FORMAT_HEVC) &&
               ((attrib_list[i].value != 3) || 
u_reduce_video_profile(ProfileToPipe(profile))
diff --git a/src/gallium/frontends/va/picture.c 
b/src/gallium/frontends/va/picture.c
index 737a1d1b301..76220dcfb1b 100644
--- a/src/gallium/frontends/va/picture.c
+++ b/src/gallium/frontends/va/picture.c
@@ -702,6 +702,12 @@ handleVAEncPackedHeaderParameterBufferType(vlVaContext 
*context, vlVaBuffer *buf
    VAEncPackedHeaderParameterBuffer *param = buf->data;
 
    switch (u_reduce_video_profile(context->templat.profile)) {
+   case PIPE_VIDEO_FORMAT_MPEG4_AVC:
+      if (param->type == VAEncPackedHeaderSequence)
+         context->packed_header_type = param->type;
+      else
+         status = VA_STATUS_ERROR_UNIMPLEMENTED;
+      break;
    case PIPE_VIDEO_FORMAT_HEVC:
       if (param->type == VAEncPackedHeaderSequence)
          context->packed_header_type = param->type;
@@ -725,6 +731,13 @@ handleVAEncPackedHeaderDataBufferType(vlVaContext 
*context, vlVaBuffer *buf)
    VAStatus status = VA_STATUS_SUCCESS;
 
    switch (u_reduce_video_profile(context->templat.profile)) {
+   case PIPE_VIDEO_FORMAT_MPEG4_AVC:
+      if (context->packed_header_type != VAEncPackedHeaderSequence)
+         return VA_STATUS_ERROR_UNIMPLEMENTED;
+
+      status = vlVaHandleVAEncPackedHeaderDataBufferTypeH264(context, buf);
+      break;
+
    case PIPE_VIDEO_FORMAT_HEVC:
       if (context->packed_header_type != VAEncPackedHeaderSequence)
          return VA_STATUS_ERROR_UNIMPLEMENTED;
diff --git a/src/gallium/frontends/va/picture_h264_enc.c 
b/src/gallium/frontends/va/picture_h264_enc.c
index 28084c82cbb..1db984c90b5 100644
--- a/src/gallium/frontends/va/picture_h264_enc.c
+++ b/src/gallium/frontends/va/picture_h264_enc.c
@@ -29,6 +29,13 @@
 #include "util/u_video.h"
 #include "va_private.h"
 
+#include "util/vl_rbsp.h"
+
+enum H264NALUnitType {
+    H264_NAL_SPS        = 7,
+    H264_NAL_PPS        = 8,
+};
+
 VAStatus
 vlVaHandleVAEncPictureParameterBufferTypeH264(vlVaDriver *drv, vlVaContext 
*context, vlVaBuffer *buf)
 {
@@ -311,6 +318,128 @@ vlVaHandleVAEncMiscParameterTypeFrameRateH264(vlVaContext 
*context, VAEncMiscPar
    return VA_STATUS_SUCCESS;
 }
 
+static void parseEncSpsParamsH264(vlVaContext *context, struct vl_rbsp *rbsp)
+{
+   unsigned i, profile_idc, num_ref_frames_in_pic_order_cnt_cycle;
+
+   profile_idc = vl_rbsp_u(rbsp, 8);
+
+   vl_rbsp_u(rbsp, 8); /* constraint_set_flags */
+   vl_rbsp_u(rbsp, 8); /* level_idc */
+
+   vl_rbsp_ue(rbsp); /* seq_parameter_set_id */
+
+   if (profile_idc == 100 || profile_idc == 110 ||
+       profile_idc == 122 || profile_idc == 244 || profile_idc == 44 ||
+       profile_idc == 83 || profile_idc == 86 || profile_idc == 118 ||
+       profile_idc == 128 || profile_idc == 138 || profile_idc == 139 ||
+       profile_idc == 134 || profile_idc == 135) {
+
+      if (vl_rbsp_ue(rbsp) == 3) /* chroma_format_idc */
+         vl_rbsp_u(rbsp, 1); /* separate_colour_plane_flag */
+
+      vl_rbsp_ue(rbsp); /* bit_depth_luma_minus8 */
+      vl_rbsp_ue(rbsp); /* bit_depth_chroma_minus8 */
+      vl_rbsp_u(rbsp, 1); /* qpprime_y_zero_transform_bypass_flag */
+
+      if (vl_rbsp_u(rbsp, 1)) /* seq_scaling_matrix_present_flag */
+         return; /* TODO */
+   }
+
+   vl_rbsp_ue(rbsp); /* log2_max_frame_num_minus4 */
+   vl_rbsp_ue(rbsp); /* pic_order_cnt_type */
+
+   if (context->desc.h264enc.seq.pic_order_cnt_type == 0)
+      vl_rbsp_ue(rbsp); /* log2_max_pic_order_cnt_lsb_minus4 */
+   else if (context->desc.h264enc.seq.pic_order_cnt_type == 1) {
+      vl_rbsp_u(rbsp, 1); /* delta_pic_order_always_zero_flag */
+      vl_rbsp_se(rbsp); /* offset_for_non_ref_pic */
+      vl_rbsp_se(rbsp); /* offset_for_top_to_bottom_field */
+      num_ref_frames_in_pic_order_cnt_cycle = vl_rbsp_ue(rbsp);
+      for (i = 0; i < num_ref_frames_in_pic_order_cnt_cycle; ++i)
+         vl_rbsp_se(rbsp); /* offset_for_ref_frame[i] */
+   }
+
+   vl_rbsp_ue(rbsp); /* max_num_ref_frames */
+   vl_rbsp_u(rbsp, 1); /* gaps_in_frame_num_value_allowed_flag */
+   vl_rbsp_ue(rbsp); /* pic_width_in_mbs_minus1 */
+   vl_rbsp_ue(rbsp); /* pic_height_in_map_units_minus1 */
+   if (!vl_rbsp_u(rbsp, 1)) /* frame_mbs_only_flag */
+      vl_rbsp_u(rbsp, 1); /* mb_adaptive_frame_field_flag */
+
+   vl_rbsp_u(rbsp, 1); /* direct_8x8_inference_flag */
+   if (vl_rbsp_u(rbsp, 1)) { /* frame_cropping_flag */
+      vl_rbsp_ue(rbsp); /* frame_crop_left_offset */
+      vl_rbsp_ue(rbsp); /* frame_crop_right_offset */
+      vl_rbsp_ue(rbsp); /* frame_crop_top_offset */
+      vl_rbsp_ue(rbsp); /* frame_crop_bottom_offset */
+   }
+
+   context->desc.h264enc.seq.vui_parameters_present_flag = vl_rbsp_u(rbsp, 1);
+   if (context->desc.h264enc.seq.vui_parameters_present_flag) {
+      context->desc.h264enc.seq.vui_flags.aspect_ratio_info_present_flag = 
vl_rbsp_u(rbsp, 1);
+      if (context->desc.h264enc.seq.vui_flags.aspect_ratio_info_present_flag) {
+         if (vl_rbsp_u(rbsp, 8) == 255) { /* aspect_ratio_idc == Extended_SAR 
*/
+            vl_rbsp_u(rbsp, 16); /* sar_width */
+            vl_rbsp_u(rbsp, 16); /* sar_height */
+         }
+      }
+
+      if (vl_rbsp_u(rbsp, 1)) /* overscan_info_present_flag */
+         vl_rbsp_u(rbsp, 1); /* overscan_appropriate_flag */
+
+      context->desc.h264enc.seq.vui_flags.video_signal_type_present_flag = 
vl_rbsp_u(rbsp, 1);
+      if (context->desc.h264enc.seq.vui_flags.video_signal_type_present_flag) {
+         context->desc.h264enc.seq.video_format = vl_rbsp_u(rbsp, 3);
+         context->desc.h264enc.seq.video_full_range_flag = vl_rbsp_u(rbsp, 1);
+         context->desc.h264enc.seq.vui_flags.colour_description_present_flag = 
vl_rbsp_u(rbsp, 1);
+         if 
(context->desc.h264enc.seq.vui_flags.colour_description_present_flag) {
+            context->desc.h264enc.seq.colour_primaries = vl_rbsp_u(rbsp, 8);
+            context->desc.h264enc.seq.transfer_characteristics = 
vl_rbsp_u(rbsp, 8);
+            context->desc.h264enc.seq.matrix_coefficients = vl_rbsp_u(rbsp, 8);
+         }
+      }
+   }
+}
+
+VAStatus
+vlVaHandleVAEncPackedHeaderDataBufferTypeH264(vlVaContext *context, vlVaBuffer 
*buf)
+{
+   struct vl_vlc vlc = {0};
+   vl_vlc_init(&vlc, 1, (const void * const*)&buf->data, &buf->size);
+
+   while (vl_vlc_bits_left(&vlc) > 0) {
+      /* search the first 64 bytes for a startcode */
+      for (int i = 0; i < 64 && vl_vlc_bits_left(&vlc) >= 24; ++i) {
+         if (vl_vlc_peekbits(&vlc, 24) == 0x000001)
+            break;
+         vl_vlc_eatbits(&vlc, 8);
+         vl_vlc_fillbits(&vlc);
+      }
+      vl_vlc_eatbits(&vlc, 24); /* eat the startcode */
+
+      if (vl_vlc_valid_bits(&vlc) < 15)
+         vl_vlc_fillbits(&vlc);
+
+      vl_vlc_eatbits(&vlc, 3);
+      unsigned nal_unit_type = vl_vlc_get_uimsbf(&vlc, 5);
+
+      struct vl_rbsp rbsp;
+      vl_rbsp_init(&rbsp, &vlc, ~0);
+
+      switch(nal_unit_type) {
+      case H264_NAL_SPS:
+         parseEncSpsParamsH264(context, &rbsp);
+         break;
+      case H264_NAL_PPS:
+      default:
+         break;
+      }
+   }
+
+   return VA_STATUS_SUCCESS;
+}
+
 VAStatus
 vlVaHandleVAEncMiscParameterTypeTemporalLayerH264(vlVaContext *context, 
VAEncMiscParameterBuffer *misc)
 {
diff --git a/src/gallium/frontends/va/va_private.h 
b/src/gallium/frontends/va/va_private.h
index 33c48eea910..2341efe47c6 100644
--- a/src/gallium/frontends/va/va_private.h
+++ b/src/gallium/frontends/va/va_private.h
@@ -540,6 +540,7 @@ VAStatus 
vlVaHandleVAEncSliceParameterBufferTypeH264(vlVaDriver *drv, vlVaContex
 VAStatus vlVaHandleVAEncSequenceParameterBufferTypeH264(vlVaDriver *drv, 
vlVaContext *context, vlVaBuffer *buf);
 VAStatus vlVaHandleVAEncMiscParameterTypeRateControlH264(vlVaContext *context, 
VAEncMiscParameterBuffer *buf);
 VAStatus vlVaHandleVAEncMiscParameterTypeFrameRateH264(vlVaContext *context, 
VAEncMiscParameterBuffer *buf);
+VAStatus vlVaHandleVAEncPackedHeaderDataBufferTypeH264(vlVaContext *context, 
vlVaBuffer *buf);
 VAStatus vlVaHandleVAEncMiscParameterTypeTemporalLayerH264(vlVaContext 
*context, VAEncMiscParameterBuffer *buf);
 VAStatus vlVaHandleVAEncMiscParameterTypeQualityLevelH264(vlVaContext 
*context, VAEncMiscParameterBuffer *buf);
 VAStatus vlVaHandleVAEncMiscParameterTypeMaxFrameSizeH264(vlVaContext 
*context, VAEncMiscParameterBuffer *buf);
diff --git a/src/gallium/include/pipe/p_video_state.h 
b/src/gallium/include/pipe/p_video_state.h
index deae42d1cf7..4da7467a904 100644
--- a/src/gallium/include/pipe/p_video_state.h
+++ b/src/gallium/include/pipe/p_video_state.h
@@ -511,12 +511,19 @@ struct pipe_h264_enc_seq_param
    struct {
       uint32_t aspect_ratio_info_present_flag: 1;
       uint32_t timing_info_present_flag: 1;
+      uint32_t video_signal_type_present_flag: 1;
+      uint32_t colour_description_present_flag: 1;
    } vui_flags;
    uint32_t aspect_ratio_idc;
    uint32_t sar_width;
    uint32_t sar_height;
    uint32_t num_units_in_tick;
    uint32_t time_scale;
+   uint32_t video_format;
+   uint32_t video_full_range_flag;
+   uint32_t colour_primaries;
+   uint32_t transfer_characteristics;
+   uint32_t matrix_coefficients;
 };
 
 struct pipe_h264_enc_picture_desc

Reply via email to