diff --git a/gst/videoparsers/gsth264parse.c b/gst/videoparsers/gsth264parse.c index db8e90966c..551786628d 100644 --- a/gst/videoparsers/gsth264parse.c +++ b/gst/videoparsers/gsth264parse.c @@ -1197,6 +1197,176 @@ gst_h264_parse_get_par (GstH264Parse * h264parse, gint * num, gint * den) } } +static GstCaps * +get_compatible_profile_caps (GstH264SPS * sps) +{ + GstCaps *caps = NULL; + const gchar **profiles = NULL; + gint i; + GValue compat_profiles = G_VALUE_INIT; + g_value_init (&compat_profiles, GST_TYPE_LIST); + + switch (sps->profile_idc) { + case GST_H264_PROFILE_EXTENDED: + if (sps->constraint_set0_flag) { /* A.2.1 */ + if (sps->constraint_set1_flag) { + static const gchar *profile_array[] = + { "constrained-baseline", "baseline", "main", "high", + "high-10", "high-4:2:2", "high-4:4:4", NULL + }; + profiles = profile_array; + } else { + static const gchar *profile_array[] = { "baseline", NULL }; + profiles = profile_array; + } + } else if (sps->constraint_set1_flag) { /* A.2.2 */ + static const gchar *profile_array[] = + { "main", "high", "high-10", "high-4:2:2", "high-4:4:4", NULL }; + profiles = profile_array; + } + break; + case GST_H264_PROFILE_BASELINE: + if (sps->constraint_set1_flag) { /* A.2.1 */ + static const gchar *profile_array[] = + { "constrained-baseline", "main", "high", "high-10", "high-4:2:2", + "high-4:4:4", NULL + }; + profiles = profile_array; + } else { + static const gchar *profile_array[] = { "extended", NULL }; + profiles = profile_array; + } + break; + case GST_H264_PROFILE_MAIN: + { + static const gchar *profile_array[] = + { "high", "high-10", "high-4:2:2", "high-4:4:4", NULL }; + profiles = profile_array; + } + break; + case GST_H264_PROFILE_HIGH: + if (sps->constraint_set1_flag) { + static const gchar *profile_array[] = + { "main", "high-10", "high-4:2:2", "high-4:4:4", NULL }; + profiles = profile_array; + } else { + static const gchar *profile_array[] = + { "high-10", "high-4:2:2", "high-4:4:4", NULL }; + profiles = profile_array; + } + break; + case GST_H264_PROFILE_HIGH10: + if (sps->constraint_set1_flag) { + static const gchar *profile_array[] = + { "main", "high", "high-4:2:2", "high-4:4:4", NULL }; + profiles = profile_array; + } else { + if (sps->constraint_set3_flag) { /* A.2.8 */ + static const gchar *profile_array[] = + { "high-10", "high-4:2:2", "high-4:4:4", "high-4:2:2-intra", + "high-4:4:4-intra", NULL + }; + profiles = profile_array; + } else { + static const gchar *profile_array[] = + { "high-4:2:2", "high-4:4:4", NULL }; + profiles = profile_array; + } + } + break; + case GST_H264_PROFILE_HIGH_422: + if (sps->constraint_set1_flag) { + static const gchar *profile_array[] = + { "main", "high", "high-10", "high-4:4:4", NULL }; + profiles = profile_array; + } else { + if (sps->constraint_set3_flag) { /* A.2.9 */ + static const gchar *profile_array[] = + { "high-4:2:2", "high-4:4:4", "high-4:4:4-intra", NULL }; + profiles = profile_array; + } else { + static const gchar *profile_array[] = { "high-4:4:4", NULL }; + profiles = profile_array; + } + } + break; + case GST_H264_PROFILE_HIGH_444: + if (sps->constraint_set1_flag) { + static const gchar *profile_array[] = + { "main", "high", "high-10", "high-4:2:2", NULL }; + profiles = profile_array; + } else if (sps->constraint_set3_flag) { /* A.2.10 */ + static const gchar *profile_array[] = { "high-4:4:4", NULL }; + profiles = profile_array; + } + break; + case GST_H264_PROFILE_MULTIVIEW_HIGH: + /* Fix: should add GST_H264_PROFILE_STEREO_HIGH as compatible + * profile if number of views == 2. */ + break; + default: + break; + } + + if (profiles) { + GValue value = G_VALUE_INIT; + caps = gst_caps_new_empty_simple ("video/x-h264"); + for (i = 0; profiles[i]; i++) { + g_value_init (&value, G_TYPE_STRING); + g_value_set_string (&value, profiles[i]); + gst_value_list_append_value (&compat_profiles, &value); + g_value_unset (&value); + } + gst_caps_set_value (caps, "profile", &compat_profiles); + g_value_unset (&compat_profiles); + } + + return caps; +} + +/* if downstream didn't support the exact profile indicated in sps header, + * check for the compatible profiles also */ +static void +ensure_caps_profile (GstH264Parse * h264parse, GstCaps * caps, GstH264SPS * sps) +{ + GstCaps *filter_caps, *peer_caps, *compat_caps; + + filter_caps = gst_caps_new_empty_simple ("video/x-h264"); + peer_caps = + gst_pad_peer_query_caps (GST_BASE_PARSE_SRC_PAD (h264parse), filter_caps); + + if (peer_caps && !gst_caps_can_intersect (caps, peer_caps)) { + GstStructure *structure; + + compat_caps = get_compatible_profile_caps (sps); + if (compat_caps != NULL) { + GstCaps *res_caps = NULL; + + res_caps = gst_caps_intersect (peer_caps, compat_caps); + + if (res_caps && !gst_caps_is_empty (res_caps)) { + const gchar *profile_str = NULL; + + res_caps = gst_caps_fixate (res_caps); + structure = gst_caps_get_structure (res_caps, 0); + profile_str = gst_structure_get_string (structure, "profile"); + if (profile_str) { + gst_caps_set_simple (caps, "profile", G_TYPE_STRING, profile_str, + NULL); + GST_DEBUG_OBJECT (h264parse, + "Setting compatible profile %s to the caps", profile_str); + } + } + if (res_caps) + gst_caps_unref (res_caps); + gst_caps_unref (compat_caps); + } + } + if (peer_caps) + gst_caps_unref (peer_caps); + gst_caps_unref (filter_caps); +} + static void gst_h264_parse_update_src_caps (GstH264Parse * h264parse, GstCaps * caps) { @@ -1364,6 +1534,8 @@ gst_h264_parse_update_src_caps (GstH264Parse * h264parse, GstCaps * caps) gst_codec_utils_h264_caps_set_level_and_profile (caps, map.data + 1, map.size - 1); gst_buffer_unmap (sps_buf, &map); + /* relax the profile constraint to find a suitable decoder */ + ensure_caps_profile (h264parse, caps, sps); } }