From b49724fdb6f3a539910207b74da4e73df406ac03 Mon Sep 17 00:00:00 2001 From: Seungha Yang Date: Fri, 9 Aug 2019 12:52:31 +0900 Subject: [PATCH] x265enc: Enhance profile setting with fixing infinite loop condition Don't fixate profile caps which will choose the first profile from list. Instead, store all profiles allowed by peer and try them until x265 can accept one of them. --- ext/x265/gstx265enc.c | 166 ++++++++++++++++++++++++++++-------------- ext/x265/gstx265enc.h | 3 +- 2 files changed, 111 insertions(+), 58 deletions(-) diff --git a/ext/x265/gstx265enc.c b/ext/x265/gstx265enc.c index 8a787de0ab..83088b4ea8 100644 --- a/ext/x265/gstx265enc.c +++ b/ext/x265/gstx265enc.c @@ -564,6 +564,8 @@ gst_x265_enc_init (GstX265Enc * encoder) encoder->api = &default_vtable; encoder->api->param_default (&encoder->x265param); + + encoder->peer_profiles = g_ptr_array_new (); } typedef struct @@ -630,6 +632,10 @@ gst_x265_enc_dequeue_all_frames (GstX265Enc * enc) static gboolean gst_x265_enc_start (GstVideoEncoder * encoder) { + GstX265Enc *x265enc = GST_X265_ENC (encoder); + + g_ptr_array_set_size (x265enc->peer_profiles, 0); + return TRUE; } @@ -648,6 +654,8 @@ gst_x265_enc_stop (GstVideoEncoder * encoder) gst_video_codec_state_unref (x265enc->input_state); x265enc->input_state = NULL; + g_ptr_array_set_size (x265enc->peer_profiles, 0); + return TRUE; } @@ -681,6 +689,9 @@ gst_x265_enc_finalize (GObject * object) g_string_free (encoder->option_string_prop, TRUE); + if (encoder->peer_profiles) + g_ptr_array_free (encoder->peer_profiles, FALSE); + G_OBJECT_CLASS (parent_class)->finalize (object); } @@ -781,6 +792,7 @@ gst_x265_enc_init_encoder (GstX265Enc * encoder) { GstVideoInfo *info; guint bitdepth; + gboolean peer_intra = FALSE; if (!encoder->input_state) { GST_DEBUG_OBJECT (encoder, "Have no input state yet"); @@ -888,25 +900,37 @@ gst_x265_enc_init_encoder (GstX265Enc * encoder) encoder->x265param.rc.rateControlMode = X265_RC_ABR; } - if (encoder->peer_profile) { - GST_DEBUG_OBJECT (encoder, "Apply peer profile %s", encoder->peer_profile); - if (encoder->api->param_apply_profile (&encoder->x265param, - encoder->peer_profile) < 0) { - GST_ERROR_OBJECT (encoder, "Failed to apply profile %s", - encoder->peer_profile); + if (encoder->peer_profiles->len > 0) { + gint i; + + for (i = 0; i < encoder->peer_profiles->len; i++) { + const gchar *profile = g_ptr_array_index (encoder->peer_profiles, i); + + GST_DEBUG_OBJECT (encoder, "Apply peer profile %s", profile); + if (encoder->api->param_apply_profile (&encoder->x265param, profile) < 0) { + GST_WARNING_OBJECT (encoder, "Failed to apply profile %s", profile); + } else { + /* libx265 chooses still-picture profile only if x265_param::totalFrames + * equals to one (otherwise, -intra profile will be chosen) */ + if (g_strrstr (profile, "stillpicture")) + encoder->x265param.totalFrames = 1; + + if (g_str_has_suffix (profile, "-intra")) + peer_intra = TRUE; + + break; + } + } + + if (i == encoder->peer_profiles->len) { + GST_ERROR_OBJECT (encoder, "Could't apply peer profile"); GST_OBJECT_UNLOCK (encoder); return FALSE; } - - /* libx265 chooses still-picture profile only if x265_param::totalFrames - * equals to one (otherwise, -intra profile will be chosen) */ - if (g_strrstr (encoder->peer_profile, "stillpicture")) { - encoder->x265param.totalFrames = 1; - } } - if (encoder->peer_intra_profile) { + if (peer_intra) { encoder->x265param.keyframeMax = 1; } else if (encoder->keyintmax > 0) { encoder->x265param.keyframeMax = encoder->keyintmax; @@ -1287,6 +1311,45 @@ typedef struct const gchar *x265_profile; } GstX265EncProfileTable; +static const gchar * +gst_x265_enc_profile_from_gst (const gchar * profile) +{ + gint i; + static const GstX265EncProfileTable profile_table[] = { + /* 8 bits */ + {"main", "main"}, + {"main-still-picture", "mainstillpicture"}, + {"main-intra", "main-intra"}, + {"main-444", "main444-8"}, + {"main-444-intra", "main444-intra"}, + {"main-444-still-picture", "main444-stillpicture"}, + /* 10 bits */ + {"main-10", "main10"}, + {"main-10-intra", "main10-intra"}, + {"main-422-10", "main422-10"}, + {"main-422-10-intra", "main422-10-intra"}, + {"main-444-10", "main444-10"}, + {"main-444-10-intra", "main444-10-intra"}, + /* 12 bits */ + {"main-12", "main12"}, + {"main-12-intra", "main12-intra"}, + {"main-422-12", "main422-12"}, + {"main-422-12-intra", "main422-12-intra"}, + {"main-444-12", "main444-12"}, + {"main-444-12-intra", "main444-12-intra"}, + }; + + if (!profile) + return NULL; + + for (i = 0; i < G_N_ELEMENTS (profile_table); i++) { + if (!strcmp (profile, profile_table[i].gst_profile)) + return profile_table[i].x265_profile; + } + + return NULL; +} + static gboolean gst_x265_enc_set_format (GstVideoEncoder * video_enc, GstVideoCodecState * state) @@ -1318,41 +1381,17 @@ gst_x265_enc_set_format (GstVideoEncoder * video_enc, gst_video_codec_state_unref (encoder->input_state); encoder->input_state = gst_video_codec_state_ref (state); - encoder->peer_profile = NULL; - encoder->peer_intra_profile = FALSE; + g_ptr_array_set_size (encoder->peer_profiles, 0); template_caps = gst_static_pad_template_get_caps (&src_factory); allowed_caps = gst_pad_get_allowed_caps (GST_VIDEO_ENCODER_SRC_PAD (encoder)); + GST_DEBUG_OBJECT (encoder, "allowed caps %" GST_PTR_FORMAT, allowed_caps); + /* allowed != template is meaning that downstream has some restriction * so we need check whether there is requested profile or not */ if (allowed_caps && !gst_caps_is_equal (allowed_caps, template_caps)) { - GstStructure *s; - const gchar *profile; - gint i; - static const GstX265EncProfileTable profile_table[] = { - /* 8 bits */ - {"main", "main"}, - {"main-still-picture", "mainstillpicture"}, - {"main-intra", "main-intra"}, - {"main-444", "main444-8"}, - {"main-444-intra", "main444-intra"}, - {"main-444-still-picture", "main444-stillpicture"}, - /* 10 bits */ - {"main-10", "main10"}, - {"main-10-intra", "main10-intra"}, - {"main-422-10", "main422-10"}, - {"main-422-10-intra", "main422-10-intra"}, - {"main-444-10", "main444-10"}, - {"main-444-10-intra", "main444-10-intra"}, - /* 12 bits */ - {"main-12", "main12"}, - {"main-12-intra", "main12-intra"}, - {"main-422-12", "main422-12"}, - {"main-422-12-intra", "main422-12-intra"}, - {"main-444-12", "main444-12"}, - {"main-444-12-intra", "main444-12-intra"}, - }; + gint i, j; if (gst_caps_is_empty (allowed_caps)) { gst_caps_unref (allowed_caps); @@ -1360,25 +1399,40 @@ gst_x265_enc_set_format (GstVideoEncoder * video_enc, return FALSE; } - allowed_caps = gst_caps_make_writable (allowed_caps); - allowed_caps = gst_caps_fixate (allowed_caps); - s = gst_caps_get_structure (allowed_caps, 0); + for (i = 0; i < gst_caps_get_size (allowed_caps); i++) { + GstStructure *s; + const GValue *val; + const gchar *profile; + const gchar *x265_profile; - profile = gst_structure_get_string (s, "profile"); - if (profile) { - if (g_str_has_suffix (profile, "-intra")) { - encoder->peer_intra_profile = TRUE; - } + s = gst_caps_get_structure (allowed_caps, i); - for (i = 0; G_N_ELEMENTS (profile_table); i++) { - if (!strcmp (profile, profile_table[i].gst_profile)) { - encoder->peer_profile = profile_table[i].x265_profile; - break; + if ((val = gst_structure_get_value (s, "profile"))) { + if (G_VALUE_HOLDS_STRING (val)) { + profile = g_value_get_string (val); + x265_profile = gst_x265_enc_profile_from_gst (profile); + + if (x265_profile) { + GST_DEBUG_OBJECT (encoder, + "Add profile %s to peer profile list", x265_profile); + + g_ptr_array_add (encoder->peer_profiles, (gpointer) x265_profile); + } + } else if (GST_VALUE_HOLDS_LIST (val)) { + for (j = 0; j < gst_value_list_get_size (val); j++) { + const GValue *vlist = gst_value_list_get_value (val, j); + profile = g_value_get_string (vlist); + x265_profile = gst_x265_enc_profile_from_gst (profile); + + if (x265_profile) { + GST_DEBUG_OBJECT (encoder, + "Add profile %s to peer profile list", x265_profile); + + g_ptr_array_add (encoder->peer_profiles, (gpointer) x265_profile); + } + } } } - - if (!encoder->peer_profile) - GST_WARNING_OBJECT (encoder, "Unknown peer profile %s", profile); } } diff --git a/ext/x265/gstx265enc.h b/ext/x265/gstx265enc.h index 4d1dfab27f..a4b5d1142c 100644 --- a/ext/x265/gstx265enc.h +++ b/ext/x265/gstx265enc.h @@ -73,8 +73,7 @@ struct _GstX265Enc gboolean reconfig; /* from the downstream caps */ - const gchar *peer_profile; - gboolean peer_intra_profile; + GPtrArray *peer_profiles; /*const x265_level_t *peer_level; */ };