/*
 * GStreamer
 * Copyright (C) 2023 Igalia, S.L.
 *
 * This library is free software; you can redistribute it and/or
 * modify it under the terms of the GNU Library General Public
 * License as published by the Free Software Foundation; either
 * version 2 of the License, or (at your option) any later version.
 *
 * This library is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
 * Library General Public License for more details.
 *
 * You should have received a copy of the GNU Library General Public
 * License along with this library; if not, write to the
 * Free Software Foundation, Inc., 51 Franklin St, Fifth Floor,
 * Boston, MA 02110-1301, USA.
 */

#ifdef HAVE_CONFIG_H
#include "config.h"
#endif

#include "gstvkvideoutils-private.h"

/* *INDENT-OFF* */
static const struct {
  GstVulkanVideoOperation video_operation;
  VkVideoCodecOperationFlagBitsKHR codec;
  const char *mime;
  VkStructureType stype;
} video_codecs_map[] = {
  { GST_VULKAN_VIDEO_OPERATION_DECODE, VK_VIDEO_CODEC_OPERATION_DECODE_H264_BIT_KHR, "video/x-h264",
      VK_STRUCTURE_TYPE_VIDEO_DECODE_H264_PROFILE_INFO_KHR },
  { GST_VULKAN_VIDEO_OPERATION_DECODE, VK_VIDEO_CODEC_OPERATION_DECODE_H265_BIT_KHR, "video/x-h265",
      VK_STRUCTURE_TYPE_VIDEO_DECODE_H265_PROFILE_INFO_KHR },
  { GST_VULKAN_VIDEO_OPERATION_DECODE, VK_VIDEO_CODEC_OPERATION_DECODE_VP9_BIT_KHR, "video/x-vp9",
        VK_STRUCTURE_TYPE_VIDEO_DECODE_VP9_PROFILE_INFO_KHR },
  { GST_VULKAN_VIDEO_OPERATION_ENCODE, VK_VIDEO_CODEC_OPERATION_ENCODE_H264_BIT_KHR, "video/x-h264",
      VK_STRUCTURE_TYPE_VIDEO_ENCODE_H264_PROFILE_INFO_KHR },
  { GST_VULKAN_VIDEO_OPERATION_ENCODE, VK_VIDEO_CODEC_OPERATION_ENCODE_H265_BIT_KHR, "video/x-h265",
      VK_STRUCTURE_TYPE_VIDEO_ENCODE_H265_PROFILE_INFO_KHR },
  { GST_VULKAN_VIDEO_OPERATION_ENCODE, VK_VIDEO_CODEC_OPERATION_ENCODE_AV1_BIT_KHR, "video/x-av1",
      VK_STRUCTURE_TYPE_VIDEO_ENCODE_AV1_PROFILE_INFO_KHR },
  { GST_VULKAN_VIDEO_OPERATION_DECODE, VK_VIDEO_CODEC_OPERATION_DECODE_AV1_BIT_KHR, "video/x-av1",
      VK_STRUCTURE_TYPE_VIDEO_DECODE_AV1_PROFILE_INFO_KHR },
};

#define VK_VIDEO_CHROMA_SUBSAMPLING_ANY                                 \
  VK_VIDEO_CHROMA_SUBSAMPLING_420_BIT_KHR                               \
  | VK_VIDEO_CHROMA_SUBSAMPLING_422_BIT_KHR                             \
  | VK_VIDEO_CHROMA_SUBSAMPLING_444_BIT_KHR

static const struct {
  VkVideoChromaSubsamplingFlagBitsKHR chroma;
  const char *chroma_str;
} video_chroma_map[] = {
  { VK_VIDEO_CHROMA_SUBSAMPLING_420_BIT_KHR, "4:2:0" },
  { VK_VIDEO_CHROMA_SUBSAMPLING_422_BIT_KHR, "4:2:2" },
  { VK_VIDEO_CHROMA_SUBSAMPLING_444_BIT_KHR, "4:4:4" },
};

#define VK_VIDEO_COMPONENT_BIT_DEPTH_ANY    \
  VK_VIDEO_COMPONENT_BIT_DEPTH_8_BIT_KHR    \
  | VK_VIDEO_COMPONENT_BIT_DEPTH_10_BIT_KHR \
  | VK_VIDEO_COMPONENT_BIT_DEPTH_12_BIT_KHR

static const struct {
  VkVideoComponentBitDepthFlagBitsKHR bitdepth;
  int bit_depth;
} bit_depth_map[] = {
  { VK_VIDEO_COMPONENT_BIT_DEPTH_8_BIT_KHR, 8 },
  { VK_VIDEO_COMPONENT_BIT_DEPTH_10_BIT_KHR, 10 },
  { VK_VIDEO_COMPONENT_BIT_DEPTH_12_BIT_KHR, 12 },
};

static const struct {
  StdVideoH264ProfileIdc vk_profile;
  const char *profile_str;
} h264_profile_map[] = {
  { STD_VIDEO_H264_PROFILE_IDC_BASELINE, "constrained-baseline" },
  { STD_VIDEO_H264_PROFILE_IDC_MAIN, "main" },
  { STD_VIDEO_H264_PROFILE_IDC_HIGH, "high" },
  { STD_VIDEO_H264_PROFILE_IDC_HIGH_444_PREDICTIVE, "high-4:4:4" },
};

static const struct {
  VkVideoDecodeH264PictureLayoutFlagBitsKHR layout;
  const char *layout_str;
} h264_layout_map[] = {
  { VK_VIDEO_DECODE_H264_PICTURE_LAYOUT_PROGRESSIVE_KHR, "progressive" },
  { VK_VIDEO_DECODE_H264_PICTURE_LAYOUT_INTERLACED_INTERLEAVED_LINES_BIT_KHR,
      "interleaved" },
  { VK_VIDEO_DECODE_H264_PICTURE_LAYOUT_INTERLACED_SEPARATE_PLANES_BIT_KHR,
      "fields" },
};

static const struct {
  StdVideoH265ProfileIdc vk_profile;
  VkVideoChromaSubsamplingFlagsKHR subsampling;
  VkVideoComponentBitDepthFlagsKHR depth;
  const char *profile_str;
} h265_profile_map[] = {
  { STD_VIDEO_H265_PROFILE_IDC_MAIN, VK_VIDEO_CHROMA_SUBSAMPLING_ANY, VK_VIDEO_COMPONENT_BIT_DEPTH_ANY, "main" },
  { STD_VIDEO_H265_PROFILE_IDC_MAIN_10, VK_VIDEO_CHROMA_SUBSAMPLING_ANY, VK_VIDEO_COMPONENT_BIT_DEPTH_ANY, "main-10" },
  { STD_VIDEO_H265_PROFILE_IDC_MAIN_STILL_PICTURE, VK_VIDEO_CHROMA_SUBSAMPLING_420_BIT_KHR, VK_VIDEO_COMPONENT_BIT_DEPTH_ANY, "main-still-picture" },
  { STD_VIDEO_H265_PROFILE_IDC_MAIN_STILL_PICTURE, VK_VIDEO_CHROMA_SUBSAMPLING_422_BIT_KHR, VK_VIDEO_COMPONENT_BIT_DEPTH_ANY, "main-still-picture" },
  { STD_VIDEO_H265_PROFILE_IDC_MAIN_STILL_PICTURE, VK_VIDEO_CHROMA_SUBSAMPLING_444_BIT_KHR, VK_VIDEO_COMPONENT_BIT_DEPTH_ANY, "main-444-still-picture" },
  /* { STD_VIDEO_H265_PROFILE_IDC_FORMAT_RANGE_EXTENSIONS, VK_VIDEO_CHROMA_SUBSAMPLING_420_BIT_KHR, VK_VIDEO_COMPONENT_BIT_DEPTH_8_BIT_KHR, "XXX" }, */
  /* { STD_VIDEO_H265_PROFILE_IDC_FORMAT_RANGE_EXTENSIONS, VK_VIDEO_CHROMA_SUBSAMPLING_420_BIT_KHR, VK_VIDEO_COMPONENT_BIT_DEPTH_10_BIT_KHR, "XXX" }, */
  { STD_VIDEO_H265_PROFILE_IDC_FORMAT_RANGE_EXTENSIONS, VK_VIDEO_CHROMA_SUBSAMPLING_420_BIT_KHR, VK_VIDEO_COMPONENT_BIT_DEPTH_12_BIT_KHR, "main-12" },
  { STD_VIDEO_H265_PROFILE_IDC_FORMAT_RANGE_EXTENSIONS, VK_VIDEO_CHROMA_SUBSAMPLING_422_BIT_KHR, VK_VIDEO_COMPONENT_BIT_DEPTH_8_BIT_KHR, "main-422" },
  { STD_VIDEO_H265_PROFILE_IDC_FORMAT_RANGE_EXTENSIONS, VK_VIDEO_CHROMA_SUBSAMPLING_422_BIT_KHR, VK_VIDEO_COMPONENT_BIT_DEPTH_10_BIT_KHR, "main-422-10" },
  { STD_VIDEO_H265_PROFILE_IDC_FORMAT_RANGE_EXTENSIONS, VK_VIDEO_CHROMA_SUBSAMPLING_422_BIT_KHR, VK_VIDEO_COMPONENT_BIT_DEPTH_12_BIT_KHR, "main-422-12" },
  { STD_VIDEO_H265_PROFILE_IDC_FORMAT_RANGE_EXTENSIONS, VK_VIDEO_CHROMA_SUBSAMPLING_444_BIT_KHR, VK_VIDEO_COMPONENT_BIT_DEPTH_8_BIT_KHR, "main-444" },
  { STD_VIDEO_H265_PROFILE_IDC_FORMAT_RANGE_EXTENSIONS, VK_VIDEO_CHROMA_SUBSAMPLING_444_BIT_KHR, VK_VIDEO_COMPONENT_BIT_DEPTH_10_BIT_KHR, "main-444-10" },
  { STD_VIDEO_H265_PROFILE_IDC_FORMAT_RANGE_EXTENSIONS, VK_VIDEO_CHROMA_SUBSAMPLING_444_BIT_KHR, VK_VIDEO_COMPONENT_BIT_DEPTH_12_BIT_KHR, "main-444-12" },
  { STD_VIDEO_H265_PROFILE_IDC_SCC_EXTENSIONS, VK_VIDEO_CHROMA_SUBSAMPLING_420_BIT_KHR, VK_VIDEO_COMPONENT_BIT_DEPTH_8_BIT_KHR, "screen-extended-main" },
  { STD_VIDEO_H265_PROFILE_IDC_SCC_EXTENSIONS, VK_VIDEO_CHROMA_SUBSAMPLING_420_BIT_KHR, VK_VIDEO_COMPONENT_BIT_DEPTH_10_BIT_KHR, "screen-extended-main-10" },
  /* { STD_VIDEO_H265_PROFILE_IDC_SCC_EXTENSIONS, VK_VIDEO_CHROMA_SUBSAMPLING_420_BIT_KHR, VK_VIDEO_COMPONENT_BIT_DEPTH_12_BIT_KHR, "XXX" }, */
  /* { STD_VIDEO_H265_PROFILE_IDC_SCC_EXTENSIONS, VK_VIDEO_CHROMA_SUBSAMPLING_422_BIT_KHR, VK_VIDEO_COMPONENT_BIT_DEPTH_8_BIT_KHR, "XXX" }, */
  /* { STD_VIDEO_H265_PROFILE_IDC_SCC_EXTENSIONS, VK_VIDEO_CHROMA_SUBSAMPLING_422_BIT_KHR, VK_VIDEO_COMPONENT_BIT_DEPTH_10_BIT_KHR, "XXX" }, */
  /* { STD_VIDEO_H265_PROFILE_IDC_SCC_EXTENSIONS, VK_VIDEO_CHROMA_SUBSAMPLING_422_BIT_KHR, VK_VIDEO_COMPONENT_BIT_DEPTH_12_BIT_KHR, "XXX" }, */
  { STD_VIDEO_H265_PROFILE_IDC_SCC_EXTENSIONS, VK_VIDEO_CHROMA_SUBSAMPLING_444_BIT_KHR, VK_VIDEO_COMPONENT_BIT_DEPTH_8_BIT_KHR, "screen-extended-main-444" },
  { STD_VIDEO_H265_PROFILE_IDC_SCC_EXTENSIONS, VK_VIDEO_CHROMA_SUBSAMPLING_444_BIT_KHR, VK_VIDEO_COMPONENT_BIT_DEPTH_10_BIT_KHR, "screen-extended-main-444-10" },
  /* { STD_VIDEO_H265_PROFILE_IDC_SCC_EXTENSIONS, VK_VIDEO_CHROMA_SUBSAMPLING_444_BIT_KHR, VK_VIDEO_COMPONENT_BIT_DEPTH_12_BIT_KHR, "XXX" }, */
};

static const struct {
  StdVideoAV1Profile vk_profile;
  const char *profile_str;
} av1_profile_map[] = {
  { STD_VIDEO_AV1_PROFILE_MAIN, "main" },
  { STD_VIDEO_AV1_PROFILE_HIGH, "high" },
  { STD_VIDEO_AV1_PROFILE_PROFESSIONAL, "professional" },
};
static const struct {
  StdVideoVP9Profile vk_profile;
  const char *profile_str;
} vp9_profile_map[] = {
  { STD_VIDEO_VP9_PROFILE_0, "0" },
  { STD_VIDEO_VP9_PROFILE_1, "1" },
  { STD_VIDEO_VP9_PROFILE_2, "2" },
  { STD_VIDEO_VP9_PROFILE_3, "3" },
};
/* *INDENT-ON* */

typedef enum _GstVulkanVideoProfileFeature
{
  NO = 0,
  YES = 1,
  UNDEFINED = -1,
} GstVulkanVideoProfileFeature;

static inline gboolean
h265_subsampling_match (const VkVideoProfileInfoKHR * profile, int i)
{
  return (((profile->chromaSubsampling & h265_profile_map[i].subsampling) ==
          profile->chromaSubsampling)
      && ((profile->chromaBitDepth & h265_profile_map[i].depth) ==
          profile->chromaBitDepth)
      && ((profile->lumaBitDepth & h265_profile_map[i].depth) ==
          profile->lumaBitDepth));
}

/**
 * gst_vulkan_video_profile_to_caps: (skip)
 * @profile: #GstVulkanVideoProfile to convert into a #GstCaps
 *
 * Returns: (transfer full): a #GstCaps from @profile
 *
 * Since: 1.24
 */
GstCaps *
gst_vulkan_video_profile_to_caps (const GstVulkanVideoProfile * profile)
{
  const char *mime = NULL, *chroma_sub = NULL;
  const char *profile_str = NULL, *layout = NULL;
  int i, luma = 0, chroma = 0;
  GstVulkanVideoProfileFeature film_grain = UNDEFINED;
  GstCaps *caps;

  g_return_val_if_fail (profile
      && profile->profile.sType == VK_STRUCTURE_TYPE_VIDEO_PROFILE_INFO_KHR,
      NULL);

  for (i = 0; i < G_N_ELEMENTS (video_codecs_map); i++) {
    if (profile->profile.videoCodecOperation == video_codecs_map[i].codec) {
      mime = video_codecs_map[i].mime;

      switch (profile->profile.videoCodecOperation) {
        case VK_VIDEO_CODEC_OPERATION_DECODE_H264_BIT_KHR:
          if (profile->codec.h264dec.sType == video_codecs_map[i].stype) {
            int j;
            for (j = 0; j < G_N_ELEMENTS (h264_profile_map); j++) {
              if (profile->codec.h264dec.stdProfileIdc
                  == h264_profile_map[j].vk_profile) {
                profile_str = h264_profile_map[j].profile_str;
                break;
              }
            }
            for (j = 0; j < G_N_ELEMENTS (h264_layout_map); j++) {
              if (profile->codec.h264dec.pictureLayout
                  == h264_layout_map[j].layout) {
                layout = h264_layout_map[j].layout_str;
                break;
              }
            }
          }
          break;
        case VK_VIDEO_CODEC_OPERATION_DECODE_H265_BIT_KHR:
          if (profile->codec.h265dec.sType == video_codecs_map[i].stype) {
            int j;
            for (j = 0; j < G_N_ELEMENTS (h265_profile_map); j++) {
              if ((profile->codec.h265dec.stdProfileIdc
                      == h265_profile_map[j].vk_profile)
                  && h265_subsampling_match (&profile->profile, j)) {
                profile_str = h265_profile_map[j].profile_str;
                break;
              }
            }
          }
          break;
        case VK_VIDEO_CODEC_OPERATION_DECODE_VP9_BIT_KHR:
          if (profile->codec.vp9dec.sType == video_codecs_map[i].stype) {
            int j;
            for (j = 0; j < G_N_ELEMENTS (vp9_profile_map); j++) {
              if (profile->codec.vp9dec.stdProfile
                  == vp9_profile_map[j].vk_profile) {
                profile_str = vp9_profile_map[j].profile_str;
                break;
              }
            }
          }
          break;
        case VK_VIDEO_CODEC_OPERATION_DECODE_AV1_BIT_KHR:
          if (profile->codec.av1dec.sType == video_codecs_map[i].stype) {
            int j;
            for (j = 0; j < G_N_ELEMENTS (av1_profile_map); j++) {
              if (profile->codec.av1dec.stdProfile
                  == av1_profile_map[j].vk_profile) {
                profile_str = av1_profile_map[j].profile_str;
                film_grain = profile->codec.av1dec.filmGrainSupport == VK_TRUE ?
                    YES : NO;
                break;
              }
            }
          }
          break;
        case VK_VIDEO_CODEC_OPERATION_ENCODE_H264_BIT_KHR:
          if (profile->codec.h264enc.sType == video_codecs_map[i].stype) {
            int j;
            for (j = 0; j < G_N_ELEMENTS (h264_profile_map); j++) {
              if (profile->codec.h264enc.stdProfileIdc
                  == h264_profile_map[j].vk_profile) {
                profile_str = h264_profile_map[j].profile_str;
                break;
              }
            }
          }
          break;
        case VK_VIDEO_CODEC_OPERATION_ENCODE_H265_BIT_KHR:
          if (profile->codec.h265enc.sType == video_codecs_map[i].stype) {
            int j;
            for (j = 0; j < G_N_ELEMENTS (h265_profile_map); j++) {
              if ((profile->codec.h265enc.stdProfileIdc
                      == h265_profile_map[j].vk_profile)
                  && h265_subsampling_match (&profile->profile, j)) {
                profile_str = h265_profile_map[j].profile_str;
                break;
              }
            }
          }
          break;
        case VK_VIDEO_CODEC_OPERATION_ENCODE_AV1_BIT_KHR:
          if (profile->codec.av1enc.sType == video_codecs_map[i].stype) {
            int j;
            for (j = 0; j < G_N_ELEMENTS (av1_profile_map); j++) {
              if (profile->codec.av1enc.stdProfile
                  == av1_profile_map[j].vk_profile) {
                profile_str = av1_profile_map[j].profile_str;
                break;
              }
            }
          }
          break;
        default:
          break;
      }

      break;
    }
  }
  if (i == G_N_ELEMENTS (video_codecs_map))
    return NULL;

  if (!profile_str)
    return NULL;

  for (i = 0; i < G_N_ELEMENTS (video_chroma_map); i++) {
    if (profile->profile.chromaSubsampling == video_chroma_map[i].chroma) {
      chroma_sub = video_chroma_map[i].chroma_str;
      break;
    }
  }
  if (i == G_N_ELEMENTS (video_chroma_map))
    return NULL;

  for (i = 0; i < G_N_ELEMENTS (bit_depth_map); i++) {
    if (profile->profile.chromaBitDepth == bit_depth_map[i].bitdepth) {
      chroma = bit_depth_map[i].bit_depth;
      break;
    }
  }
  if (i == G_N_ELEMENTS (bit_depth_map))
    return NULL;

  for (i = 0; i < G_N_ELEMENTS (bit_depth_map); i++) {
    if (profile->profile.lumaBitDepth == bit_depth_map[i].bitdepth) {
      luma = bit_depth_map[i].bit_depth;
      break;
    }
  }
  if (i == G_N_ELEMENTS (bit_depth_map))
    return NULL;

  caps = gst_caps_new_simple (mime, "profile", G_TYPE_STRING, profile_str,
      "chroma-format", G_TYPE_STRING, chroma_sub, "bit-depth-luma", G_TYPE_UINT,
      luma, "bit-depth-chroma", G_TYPE_UINT, chroma, NULL);

  if (layout)
    gst_caps_set_simple (caps, "interlace-mode", G_TYPE_STRING, layout, NULL);

  if (film_grain != UNDEFINED) {
    gst_caps_set_simple (caps, "film-grain", G_TYPE_BOOLEAN,
        film_grain == YES ? TRUE : FALSE, NULL);
  }

  return caps;
}

/**
 * gst_vulkan_video_profile_from_caps: (skip)
 * @profile: (out): the output profile
 * @caps: a #GstCaps to parse
 * @video_operation: a supported video operation
 *
 * Returns: %TRUE if @caps was parsed correctly, otherwise %FALSE
 *
 * Since: 1.24
 */
gboolean
gst_vulkan_video_profile_from_caps (GstVulkanVideoProfile * profile,
    GstCaps * caps, GstVulkanVideoOperation video_operation)
{
  const GstStructure *structure;
  const gchar *mime, *profile_str = NULL, *layout = NULL;
  gint i;

  g_return_val_if_fail (GST_IS_CAPS (caps), FALSE);
  g_return_val_if_fail (profile, FALSE);
  g_return_val_if_fail (video_operation < GST_VULKAN_VIDEO_OPERATION_UNKNOWN,
      FALSE);

  structure = gst_caps_get_structure (caps, 0);

  profile->usage.decode.sType = VK_STRUCTURE_TYPE_VIDEO_DECODE_USAGE_INFO_KHR;
  profile->usage.decode.videoUsageHints = VK_VIDEO_DECODE_USAGE_DEFAULT_KHR;

  profile->profile.sType = VK_STRUCTURE_TYPE_VIDEO_PROFILE_INFO_KHR;
  profile->profile.pNext = &profile->usage;

  mime = gst_structure_get_name (structure);

  {
    gint i, luma, chroma;
    const gchar *chroma_sub;

    chroma_sub = gst_structure_get_string (structure, "chroma-format");
    if (!chroma_sub)
      return FALSE;
    if (!gst_structure_get (structure, "bit-depth-luma", G_TYPE_UINT, &luma,
            "bit-depth-chroma", G_TYPE_UINT, &chroma, NULL))
      return FALSE;

    for (i = 0; i < G_N_ELEMENTS (video_chroma_map); i++) {
      if (g_strcmp0 (chroma_sub, video_chroma_map[i].chroma_str) == 0) {
        profile->profile.chromaSubsampling = video_chroma_map[i].chroma;
        break;
      }
    }
    if (i == G_N_ELEMENTS (video_chroma_map))
      return FALSE;

    for (i = 0; i < G_N_ELEMENTS (bit_depth_map); i++) {
      if (luma == bit_depth_map[i].bit_depth) {
        profile->profile.lumaBitDepth = bit_depth_map[i].bitdepth;
        break;
      }
    }
    if (i == G_N_ELEMENTS (bit_depth_map))
      return FALSE;

    for (i = 0; i < G_N_ELEMENTS (bit_depth_map); i++) {
      if (chroma == bit_depth_map[i].bit_depth) {
        profile->profile.chromaBitDepth = bit_depth_map[i].bitdepth;
        break;
      }
    }
    if (i == G_N_ELEMENTS (bit_depth_map))
      return FALSE;
  }

  for (i = 0; i < G_N_ELEMENTS (video_codecs_map); i++) {
    if ((video_codecs_map[i].video_operation == video_operation)
        && (g_strcmp0 (video_codecs_map[i].mime, mime) == 0)) {
      profile->profile.videoCodecOperation = video_codecs_map[i].codec;

      switch (profile->profile.videoCodecOperation) {
        case VK_VIDEO_CODEC_OPERATION_DECODE_H264_BIT_KHR:{
          int j;

          profile->codec.h264dec.sType = video_codecs_map[i].stype;
          profile->codec.h264dec.stdProfileIdc =
              STD_VIDEO_H264_PROFILE_IDC_INVALID;
          profile->codec.h264dec.pictureLayout =
              VK_VIDEO_DECODE_H264_PICTURE_LAYOUT_FLAG_BITS_MAX_ENUM_KHR;
          profile->usage.decode.pNext = &profile->codec;

          profile_str = gst_structure_get_string (structure, "profile");
          for (j = 0; profile_str && j < G_N_ELEMENTS (h264_profile_map); j++) {
            if (g_strcmp0 (profile_str, h264_profile_map[j].profile_str) == 0) {
              profile->codec.h264dec.stdProfileIdc =
                  h264_profile_map[j].vk_profile;
              break;
            }
          }
          layout = gst_structure_get_string (structure, "interlace-mode");
          for (j = 0; layout && j < G_N_ELEMENTS (h264_layout_map); j++) {
            if (g_strcmp0 (layout, h264_layout_map[j].layout_str) == 0) {
              profile->codec.h264dec.pictureLayout = h264_layout_map[j].layout;
              break;
            }
          }
          break;
        }
        case VK_VIDEO_CODEC_OPERATION_DECODE_H265_BIT_KHR:{
          int j;

          profile->codec.h265dec.sType = video_codecs_map[i].stype;
          profile->codec.h265dec.stdProfileIdc =
              STD_VIDEO_H265_PROFILE_IDC_INVALID;
          profile->usage.decode.pNext = &profile->codec;

          profile_str = gst_structure_get_string (structure, "profile");

          for (j = 0; profile_str && j < G_N_ELEMENTS (h265_profile_map); j++) {
            if ((g_strcmp0 (profile_str, h265_profile_map[j].profile_str) == 0)
                && h265_subsampling_match (&profile->profile, j)) {
              profile->codec.h265dec.stdProfileIdc =
                  h265_profile_map[j].vk_profile;
              break;
            }
          }
          break;
        }
        case VK_VIDEO_CODEC_OPERATION_DECODE_VP9_BIT_KHR:{
          int j;

          profile->codec.vp9dec.sType = video_codecs_map[i].stype;
          profile->codec.vp9dec.stdProfile = STD_VIDEO_VP9_PROFILE_INVALID;
          profile->usage.decode.pNext = &profile->codec;

          profile_str = gst_structure_get_string (structure, "profile");
          for (j = 0; profile_str && j < G_N_ELEMENTS (vp9_profile_map); j++) {
            if (g_strcmp0 (profile_str, vp9_profile_map[j].profile_str) == 0) {
              profile->codec.vp9dec.stdProfile = vp9_profile_map[j].vk_profile;
              break;
            }
          }
          break;
        }
        case VK_VIDEO_CODEC_OPERATION_DECODE_AV1_BIT_KHR:{
          int j;
          gboolean film_grain;

          profile->codec.av1dec.sType = video_codecs_map[i].stype;
          profile->codec.av1dec.stdProfile = STD_VIDEO_AV1_PROFILE_INVALID;
          profile->usage.decode.pNext = &profile->codec;

          profile_str = gst_structure_get_string (structure, "profile");
          for (j = 0; profile_str && j < G_N_ELEMENTS (av1_profile_map); j++) {
            if (g_strcmp0 (profile_str, av1_profile_map[j].profile_str) == 0) {
              profile->codec.av1dec.stdProfile = av1_profile_map[j].vk_profile;
              break;
            }
          }

          if (gst_structure_get_boolean (structure, "film-grain", &film_grain)) {
            profile->codec.av1dec.filmGrainSupport =
                film_grain ? VK_TRUE : VK_FALSE;
          }

          break;
        }
        case VK_VIDEO_CODEC_OPERATION_ENCODE_H264_BIT_KHR:{
          int j;

          profile->codec.h264enc.sType = video_codecs_map[i].stype;
          profile->codec.h264enc.stdProfileIdc =
              STD_VIDEO_H264_PROFILE_IDC_INVALID;
          profile->profile.pNext = &profile->codec;

          profile_str = gst_structure_get_string (structure, "profile");
          for (j = 0; profile_str && j < G_N_ELEMENTS (h264_profile_map); j++) {
            if (g_strcmp0 (profile_str, h264_profile_map[j].profile_str) == 0) {
              profile->codec.h264enc.stdProfileIdc =
                  h264_profile_map[j].vk_profile;
              break;
            }
          }
          break;
        }
        case VK_VIDEO_CODEC_OPERATION_ENCODE_H265_BIT_KHR:{
          int j;

          profile->codec.h265enc.sType = video_codecs_map[i].stype;
          profile->codec.h265enc.stdProfileIdc =
              STD_VIDEO_H265_PROFILE_IDC_INVALID;
          profile->profile.pNext = &profile->codec;

          profile_str = gst_structure_get_string (structure, "profile");
          for (j = 0; profile_str && j < G_N_ELEMENTS (h265_profile_map); j++) {
            if ((g_strcmp0 (profile_str, h265_profile_map[j].profile_str) == 0)
                && h265_subsampling_match (&profile->profile, j)) {
              profile->codec.h265enc.stdProfileIdc =
                  h265_profile_map[j].vk_profile;
              break;
            }
          }
          break;
        }
        case VK_VIDEO_CODEC_OPERATION_ENCODE_AV1_BIT_KHR:{
          int j;

          profile->codec.av1enc.sType = video_codecs_map[i].stype;
          profile->codec.av1enc.stdProfile = STD_VIDEO_AV1_PROFILE_INVALID;
          profile->profile.pNext = &profile->codec;

          profile_str = gst_structure_get_string (structure, "profile");
          for (j = 0; profile_str && j < G_N_ELEMENTS (av1_profile_map); j++) {
            if (g_strcmp0 (profile_str, av1_profile_map[j].profile_str) == 0) {
              profile->codec.av1enc.stdProfile = av1_profile_map[j].vk_profile;
              break;
            }
          }
          break;
        }
        default:
          profile->usage.decode.pNext = NULL;
          break;
      }

      break;
    }
  }
  if (i == G_N_ELEMENTS (video_codecs_map))
    return FALSE;

  return TRUE;
}

/**
 * gst_vulkan_video_profile_is_valid: (skip)
 * @profile: the output profile
 * @codec: VkVideoCodecOperationFlagBitsKHR described by @profile
 *
 * Returns: %TRUE if @profile is correct and matches with @codec
 *
 * Since: 1.24
 */
gboolean
gst_vulkan_video_profile_is_valid (GstVulkanVideoProfile * profile, guint codec)
{
  int i;
  VkVideoCodecOperationFlagBitsKHR op = codec;
  VkStructureType stype = VK_STRUCTURE_TYPE_MAX_ENUM;

  if (op == VK_VIDEO_CODEC_OPERATION_NONE_KHR)
    return FALSE;

  if (profile->profile.videoCodecOperation != op)
    return FALSE;

  for (i = 0; i < G_N_ELEMENTS (video_codecs_map); i++) {
    if (op == video_codecs_map[i].codec) {
      stype = video_codecs_map[i].stype;
      break;
    }
  }

  if (stype == VK_STRUCTURE_TYPE_MAX_ENUM)
    return FALSE;

  if (profile->codec.base.sType != stype)
    return FALSE;

  return TRUE;
}

/**
 * gst_vulkan_video_profile_is_equal:
 * @a: a #GstVulkanVideoProfile
 * @b: another #GstVulkanVideoProfile
 *
 * Returns: whether @a and @b contains the same information.
 */
gboolean
gst_vulkan_video_profile_is_equal (const GstVulkanVideoProfile * a,
    const GstVulkanVideoProfile * b)
{
  gboolean profile;

  g_return_val_if_fail (a && b, FALSE);

  profile = ((a->profile.videoCodecOperation == b->profile.videoCodecOperation)
      && (a->profile.chromaSubsampling == b->profile.chromaSubsampling)
      && (a->profile.chromaBitDepth == b->profile.chromaBitDepth)
      && (a->profile.lumaBitDepth == b->profile.lumaBitDepth)
      && (a->codec.base.sType == b->codec.base.sType));

  if (!profile)
    return FALSE;

  switch (a->profile.videoCodecOperation) {
    case VK_VIDEO_CODEC_OPERATION_DECODE_H264_BIT_KHR:
      return ((a->codec.h264dec.stdProfileIdc == b->codec.h264dec.stdProfileIdc)
          && a->codec.h264dec.pictureLayout == b->codec.h264dec.pictureLayout);
    case VK_VIDEO_CODEC_OPERATION_DECODE_H265_BIT_KHR:
      return (a->codec.h265dec.stdProfileIdc == b->codec.h265dec.stdProfileIdc);
    case VK_VIDEO_CODEC_OPERATION_DECODE_VP9_BIT_KHR:
      return (a->codec.vp9dec.stdProfile == b->codec.vp9dec.stdProfile);
    case VK_VIDEO_CODEC_OPERATION_DECODE_AV1_BIT_KHR:
      return (a->codec.av1dec.stdProfile == b->codec.av1dec.stdProfile);
    default:
      return FALSE;
  }

  g_assert_not_reached ();
}
