va: Add extended formats support such as 10/12 bits, 4:2:2 and 4:4:4.

Part-of: <https://gitlab.freedesktop.org/gstreamer/gstreamer/-/merge_requests/2036>
This commit is contained in:
He Junyan 2022-05-02 23:14:32 +08:00
parent 17c416ca92
commit 0952c556dc

View file

@ -566,8 +566,8 @@ _h265_fill_ptl (GstVaH265Enc * self,
/* additional indications specified for general_profile_idc from 4~10 */
if (sequence->general_profile_idc == 4) {
/* In A.3.5, Format range extensions profiles.
Just support main444, main444-10 and main422-10 profile now, may add
more profiles when needed. */
Just support main444, main444-10 main422-10 main422-12 and main-12
profile now, may add more profiles when needed. */
switch (base->profile) {
case VAProfileHEVCMain444:
ptl->max_12bit_constraint_flag = 1;
@ -602,6 +602,17 @@ _h265_fill_ptl (GstVaH265Enc * self,
ptl->one_picture_only_constraint_flag = 0;
ptl->lower_bit_rate_constraint_flag = 1;
break;
case VAProfileHEVCMain422_12:
ptl->max_12bit_constraint_flag = 1;
ptl->max_10bit_constraint_flag = 0;
ptl->max_8bit_constraint_flag = 0;
ptl->max_422chroma_constraint_flag = 1;
ptl->max_420chroma_constraint_flag = 0;
ptl->max_monochrome_constraint_flag = 0;
ptl->intra_constraint_flag = 0;
ptl->one_picture_only_constraint_flag = 0;
ptl->lower_bit_rate_constraint_flag = 1;
break;
case VAProfileHEVCMain12:
ptl->max_12bit_constraint_flag = 1;
ptl->max_10bit_constraint_flag = 0;
@ -2391,17 +2402,59 @@ gst_va_h265_enc_reset_state (GstVaBaseEnc * base)
}
static guint
_h265_get_rtformat (GstVaH265Enc * self, GstVideoFormat format)
_h265_get_rtformat (GstVaH265Enc * self, GstVideoFormat format,
guint * depth, guint * chrome)
{
guint chroma;
chroma = gst_va_chroma_from_video_format (format);
/* Check whether the rtformat is supported. */
if (chroma != VA_RT_FORMAT_YUV420) {
GST_ERROR_OBJECT (self, "Unsupported chroma for video format: %s",
gst_video_format_to_string (format));
return 0;
switch (chroma) {
case VA_RT_FORMAT_YUV400:
*depth = 8;
*chrome = 0;
break;
case VA_RT_FORMAT_YUV420:
*depth = 8;
*chrome = 1;
break;
case VA_RT_FORMAT_YUV422:
*depth = 8;
*chrome = 2;
break;
case VA_RT_FORMAT_YUV444:
*depth = 8;
*chrome = 3;
break;
case VA_RT_FORMAT_YUV420_10:
*depth = 10;
*chrome = 1;
break;
case VA_RT_FORMAT_YUV422_10:
*depth = 10;
*chrome = 2;
break;
case VA_RT_FORMAT_YUV444_10:
*depth = 10;
*chrome = 3;
break;
case VA_RT_FORMAT_YUV420_12:
*depth = 12;
*chrome = 1;
break;
case VA_RT_FORMAT_YUV422_12:
*depth = 12;
*chrome = 2;
break;
case VA_RT_FORMAT_YUV444_12:
*depth = 12;
*chrome = 3;
break;
default:
chroma = 0;
GST_ERROR_OBJECT (self, "Unsupported chroma for video format: %s",
gst_video_format_to_string (format));
break;
}
return chroma;
@ -2417,13 +2470,15 @@ _h265_decide_profile (GstVaH265Enc * self, VAProfile * _profile,
VAProfile profile;
guint rt_format;
GstCaps *allowed_caps = NULL;
guint num_structures, i;
guint num_structures, i, j;
GstStructure *structure;
const GValue *v_profile;
GPtrArray *candidates = NULL;
gchar *profile_name;
GArray *caps_candidates = NULL;
GArray *chroma_candidates = NULL;
guint depth = 0, chrome = 0;
candidates = g_ptr_array_new_with_free_func (g_free);
caps_candidates = g_array_new (TRUE, TRUE, sizeof (VAProfile));
chroma_candidates = g_array_new (TRUE, TRUE, sizeof (VAProfile));
/* First, check whether the downstream requires a specified profile. */
allowed_caps = gst_pad_get_allowed_caps (GST_VIDEO_ENCODER_SRC_PAD (base));
@ -2439,8 +2494,12 @@ _h265_decide_profile (GstVaH265Enc * self, VAProfile * _profile,
continue;
if (G_VALUE_HOLDS_STRING (v_profile)) {
profile_name = g_strdup (g_value_get_string (v_profile));
g_ptr_array_add (candidates, profile_name);
profile =
gst_va_profile_from_name (HEVC, g_value_get_string (v_profile));
if (profile == VAProfileNone)
continue;
g_array_append_val (caps_candidates, profile);
} else if (GST_VALUE_HOLDS_LIST (v_profile)) {
guint j;
@ -2449,21 +2508,23 @@ _h265_decide_profile (GstVaH265Enc * self, VAProfile * _profile,
if (!p)
continue;
profile_name = g_strdup (g_value_get_string (p));
g_ptr_array_add (candidates, profile_name);
profile = gst_va_profile_from_name (HEVC, g_value_get_string (p));
if (profile == VAProfileNone)
continue;
g_array_append_val (caps_candidates, profile);
}
}
}
}
if (candidates->len == 0) {
if (caps_candidates->len == 0) {
GST_ERROR_OBJECT (self, "No available profile in caps");
ret = FALSE;
goto out;
}
in_format = GST_VIDEO_INFO_FORMAT (&base->input_state->info);
rt_format = _h265_get_rtformat (self, in_format);
rt_format = _h265_get_rtformat (self, in_format, &depth, &chrome);
if (!rt_format) {
GST_ERROR_OBJECT (self, "unsupported video format %s",
gst_video_format_to_string (in_format));
@ -2471,18 +2532,58 @@ _h265_decide_profile (GstVaH265Enc * self, VAProfile * _profile,
goto out;
}
/* Find the suitable profile by features and check the HW
* support. */
/* To make the thing a little simple here, We only consider the bit
depth compatibility for each level. For example, we will consider
that Main-4:4:4-10 is able to contain 8 bits 4:4:4 streams, but
the we wiil not consider that it will contain 10 bits 4:2:0 stream. */
if (chrome == 3) {
/* 4:4:4 */
if (depth == 8) {
profile = VAProfileHEVCMain444;
g_array_append_val (chroma_candidates, profile);
}
/* Just use the first HW available profile and disable features if
* needed. */
profile_name = NULL;
for (i = 0; i < candidates->len; i++) {
profile_name = g_ptr_array_index (candidates, i);
profile = gst_va_profile_from_name (HEVC, profile_name);
if (profile == VAProfileNone)
continue;
if (depth <= 10) {
profile = VAProfileHEVCMain444_10;
g_array_append_val (chroma_candidates, profile);
}
if (depth <= 12) {
profile = VAProfileHEVCMain444_12;
g_array_append_val (chroma_candidates, profile);
}
} else if (chrome == 2) {
/* 4:2:2 */
if (depth <= 10) {
profile = VAProfileHEVCMain422_10;
g_array_append_val (chroma_candidates, profile);
}
if (depth <= 12) {
profile = VAProfileHEVCMain422_12;
g_array_append_val (chroma_candidates, profile);
}
} else if (chrome == 1 || chrome == 0) {
/* 4:2:0 or 4:0:0 */
if (depth == 8) {
profile = VAProfileHEVCMain;
g_array_append_val (chroma_candidates, profile);
}
if (depth <= 10) {
profile = VAProfileHEVCMain10;
g_array_append_val (chroma_candidates, profile);
}
if (depth <= 12) {
profile = VAProfileHEVCMain12;
g_array_append_val (chroma_candidates, profile);
}
}
/* Just use the first HW available profile in candidate. */
for (i = 0; i < chroma_candidates->len; i++) {
profile = g_array_index (chroma_candidates, VAProfile, i);
if (!gst_va_encoder_has_profile (base->encoder, profile))
continue;
@ -2490,17 +2591,23 @@ _h265_decide_profile (GstVaH265Enc * self, VAProfile * _profile,
profile, GST_VA_BASE_ENC_ENTRYPOINT (base))) == 0)
continue;
for (j = 0; j < caps_candidates->len; j++) {
VAProfile p = g_array_index (caps_candidates, VAProfile, j);
if (profile == p)
break;
}
if (j == caps_candidates->len)
continue;
*_profile = profile;
*_rt_format = rt_format;
ret = TRUE;
goto out;
}
if (ret == FALSE)
goto out;
out:
g_clear_pointer (&candidates, g_ptr_array_unref);
g_clear_pointer (&caps_candidates, g_array_unref);
g_clear_pointer (&chroma_candidates, g_array_unref);
g_clear_pointer (&allowed_caps, gst_caps_unref);
if (ret) {
@ -3068,16 +3175,35 @@ _h265_ensure_rate_control (GstVaH265Enc * self)
if ((self->rc.rc_ctrl_mode == VA_RC_CBR || self->rc.rc_ctrl_mode == VA_RC_VBR
|| self->rc.rc_ctrl_mode == VA_RC_VCM) && bitrate == 0) {
/* FIXME: Provide better estimation. */
/* Just Using a 1/6 compression ratio, 12 bits per pixel for YUV420.
TODO: Other video format. */
/* Choose the max value of all levels' MinCr which is 8, and x2 for
conservative calculation. So just using a 1/16 compression ratio,
and the bits per pixel for YUV420, YUV422, YUV444, accordingly. */
guint64 factor;
guint depth = 8, chrome = 1;
guint bits_per_pix;
factor = (guint64) self->luma_width * self->luma_height * 12 / 6;
if (!_h265_get_rtformat (self,
GST_VIDEO_INFO_FORMAT (&base->input_state->info), &depth, &chrome))
g_assert_not_reached ();
if (chrome == 3) {
bits_per_pix = 24;
} else if (chrome == 2) {
bits_per_pix = 16;
} else {
bits_per_pix = 12;
}
bits_per_pix = bits_per_pix + bits_per_pix * (depth - 8) / 8;
factor = (guint64) self->luma_width * self->luma_height * bits_per_pix / 16;
bitrate = gst_util_uint64_scale (factor,
GST_VIDEO_INFO_FPS_N (&base->input_state->info),
GST_VIDEO_INFO_FPS_D (&base->input_state->info)) / 1000;
GST_INFO_OBJECT (self, "target bitrate computed to %u kbps", bitrate);
self->prop.bitrate = bitrate;
g_object_notify_by_pspec (G_OBJECT (self), properties[PROP_BITRATE]);
}
/* Adjust the setting based on RC mode. */
@ -3372,11 +3498,40 @@ static void
_h265_calculate_coded_size (GstVaH265Enc * self)
{
GstVaBaseEnc *base = GST_VA_BASE_ENC (self);
guint codedbuf_size = 0;
guint chrome, depth;
if (!_h265_get_rtformat (self,
GST_VIDEO_INFO_FORMAT (&base->input_state->info), &depth, &chrome))
g_assert_not_reached ();
switch (chrome) {
case 0:
/* 4:0:0 */
case 1:
/* 4:2:0 */
codedbuf_size = (self->luma_width * self->luma_height * 3 / 2);
break;
case 2:
/* 4:2:2 */
codedbuf_size = (self->luma_width * self->luma_height * 2);
break;
case 3:
/* 4:4:4 */
codedbuf_size = (self->luma_width * self->luma_height * 3);
break;
default:
g_assert_not_reached ();
break;
}
codedbuf_size = codedbuf_size + (codedbuf_size * (depth - 8) / 8);
codedbuf_size = codedbuf_size / (self->min_cr / 2 /* For safety */ );
/* FIXME: Using only a rough approximation for bitstream headers.
* Not taken into account: ScalingList, RefPicListModification,
* PredWeightTable, which is not used now. */
/* Calculate the maximum sizes for common headers (in bits) */
guint codedbuf_size = 0;
/* Account for VPS header */
codedbuf_size += 4 /* start code */ + GST_ROUND_UP_8 (MAX_VPS_HDR_SIZE +
@ -3394,11 +3549,6 @@ _h265_calculate_coded_size (GstVaH265Enc * self)
codedbuf_size += self->partition.num_slices * (4 +
GST_ROUND_UP_8 (MAX_SLICE_HDR_SIZE + MAX_SHORT_TERM_REFPICSET_SIZE) / 8);
/* TODO: Only YUV 4:2:0 formats are supported for now.
more video format to check. */
codedbuf_size +=
(self->luma_width * self->luma_height * 3 / 2) / self->min_cr;
base->codedbuf_size = codedbuf_size;
GST_INFO_OBJECT (self, "Calculate codedbuf size: %u", base->codedbuf_size);
}