sdpmessage: Try to re-create profile-level-id
Some WebRTC implementations such as Pion are unhappy if the profile-level-id isn't returned with a compatible profile as the RFC requires. Let's try to reform it In practice, the correct way to do this would be to not use caps intersection, but to instead implement the correct RFC compliant SDP O/A negotiation of formats. Include a unit test written by Philippe Normand Part-of: <https://gitlab.freedesktop.org/gstreamer/gstreamer/-/merge_requests/9031>
This commit is contained in:
parent
7fb6965ded
commit
5d9abea64c
@ -3836,6 +3836,87 @@ _sdp_media_has_extmap (const GstSDPMedia * media, guint id)
|
||||
|
||||
}
|
||||
|
||||
/* This function tries to recreate the profile_idc and constraints, but
|
||||
* it's not foolproof, as there could be many variants. To be perfectly
|
||||
* compliant with the RFC, we'd need to store and return the orignal
|
||||
* profile-level-id
|
||||
*/
|
||||
static gboolean
|
||||
h264_get_profile_idc (const gchar * profile, guint8 * profile_idc,
|
||||
guint8 * constraint_flags)
|
||||
{
|
||||
const guint8 CSF0 = 0x80; /* Baseline compatible */
|
||||
const guint8 CSF1 = 0x40; /* Main compatible */
|
||||
// const guint8 CSF2 = 0x20; /* Extended compatible */
|
||||
const guint8 CSF3 = 0x10;
|
||||
const guint8 CSF4 = 0x08;
|
||||
const guint8 CSF5 = 0x04;
|
||||
g_return_val_if_fail (profile, FALSE);
|
||||
|
||||
*constraint_flags = 0;
|
||||
|
||||
if (!strcmp (profile, "constrained-baseline")) {
|
||||
*profile_idc = 66;
|
||||
*constraint_flags = CSF0 | CSF1;
|
||||
} else if (!strcmp (profile, "baseline")) {
|
||||
*profile_idc = 66;
|
||||
*constraint_flags = CSF0;
|
||||
} else if (!strcmp (profile, "main")) {
|
||||
*profile_idc = 77;
|
||||
*constraint_flags = CSF1;
|
||||
} else if (!strcmp (profile, "extended")) {
|
||||
*profile_idc = 88;
|
||||
*constraint_flags = CSF3;
|
||||
} else if (!strcmp (profile, "constrained-high")) {
|
||||
*profile_idc = 100;
|
||||
*constraint_flags = CSF4 | CSF5;
|
||||
} else if (!strcmp (profile, "progressive-high")) {
|
||||
*profile_idc = 100;
|
||||
*constraint_flags = CSF4;
|
||||
} else if (!strcmp (profile, "high-10")) {
|
||||
*profile_idc = 110;
|
||||
} else if (!strcmp (profile, "high-10-intra")) {
|
||||
*profile_idc = 110;
|
||||
*constraint_flags = CSF3;
|
||||
} else if (!strcmp (profile, "progressive-high-10")) {
|
||||
*profile_idc = 110;
|
||||
*constraint_flags = CSF4;
|
||||
} else if (!strcmp (profile, "high-4:2:2")) {
|
||||
*profile_idc = 122;
|
||||
} else if (!strcmp (profile, "high-4:2:2-intra")) {
|
||||
*profile_idc = 122;
|
||||
*constraint_flags = CSF3;
|
||||
} else if (!strcmp (profile, "high-4:4:4")) {
|
||||
*profile_idc = 244;
|
||||
} else if (!strcmp (profile, "high-4:4:4-intra")) {
|
||||
*profile_idc = 244;
|
||||
*constraint_flags = CSF3;
|
||||
} else if (!strcmp (profile, "cavlc-4:4:4-intra")) {
|
||||
*profile_idc = 44;
|
||||
} else if (!strcmp (profile, "multiview-high")) {
|
||||
*profile_idc = 118;
|
||||
} else if (!strcmp (profile, "stereo-high")) {
|
||||
*profile_idc = 128;
|
||||
} else if (!strcmp (profile, "scalable-baseline")) {
|
||||
*profile_idc = 83;
|
||||
} else if (!strcmp (profile, "scalable-constrained-baseline")) {
|
||||
*profile_idc = 83;
|
||||
*constraint_flags = CSF5;
|
||||
} else if (!strcmp (profile, "scalable-high")) {
|
||||
*profile_idc = 86;
|
||||
} else if (!strcmp (profile, "scalable-constrained-high")) {
|
||||
*profile_idc = 86;
|
||||
*constraint_flags = CSF5;
|
||||
} else if (!strcmp (profile, "scalable-high-intra")) {
|
||||
*profile_idc = 86;
|
||||
*constraint_flags = CSF3;
|
||||
} else {
|
||||
return FALSE;
|
||||
}
|
||||
|
||||
return TRUE;
|
||||
}
|
||||
|
||||
static GstSDPResult
|
||||
_add_media_format_from_structure (const GstStructure * s, GstSDPMedia * media)
|
||||
{
|
||||
@ -4091,11 +4172,19 @@ _add_media_format_from_structure (const GstStructure * s, GstSDPMedia * media)
|
||||
* representation */
|
||||
if (!g_strcmp0 (gst_structure_get_string (s, "encoding-name"), "H264")
|
||||
&& !g_strcmp0 (fname, "profile")) {
|
||||
fname = "level-asymmetry-allowed";
|
||||
fval = "1";
|
||||
}
|
||||
guint8 profile_idc, constraint_flags;
|
||||
|
||||
g_string_append_printf (fmtp, "%s%s=%s", first ? "" : ";", fname, fval);
|
||||
g_string_append_printf (fmtp, "%slevel-asymmetry-allowed=1",
|
||||
first ? "" : ";");
|
||||
if (h264_get_profile_idc (fval, &profile_idc, &constraint_flags))
|
||||
g_string_append_printf (fmtp, ";profile-level-id=%02x%02x1f",
|
||||
profile_idc, constraint_flags);
|
||||
else
|
||||
GST_FIXME ("Can't convert profile %s back into profile-level-id",
|
||||
fval);
|
||||
} else {
|
||||
g_string_append_printf (fmtp, "%s%s=%s", first ? "" : ";", fname, fval);
|
||||
}
|
||||
first = FALSE;
|
||||
}
|
||||
}
|
||||
|
@ -27,6 +27,7 @@
|
||||
|
||||
#include <gst/check/gstcheck.h>
|
||||
#include <gst/sdp/gstsdpmessage.h>
|
||||
#include <gst/pbutils/pbutils.h>
|
||||
|
||||
/* *INDENT-OFF* */
|
||||
static const gchar *sdp = "v=0\r\n"
|
||||
@ -774,11 +775,15 @@ GST_START_TEST (media_from_caps_h264_with_profile_asymmetry_allowed)
|
||||
const GstSDPMedia *result_video;
|
||||
GstStructure *s_video;
|
||||
GstCaps *caps_video;
|
||||
GstSDPMedia media;
|
||||
GstSDPResult ret = GST_SDP_OK;
|
||||
const gchar *fmtp;
|
||||
gboolean profile_level_id_found = FALSE;
|
||||
gchar **pairs;
|
||||
|
||||
gst_sdp_message_new (&message);
|
||||
gst_sdp_message_parse_buffer ((guint8 *) h264_sdp, length, message);
|
||||
|
||||
|
||||
result_video = gst_sdp_message_get_media (message, 0);
|
||||
fail_unless (result_video != NULL);
|
||||
caps_video = gst_sdp_media_get_caps_from_media (result_video, 96);
|
||||
@ -789,6 +794,59 @@ GST_START_TEST (media_from_caps_h264_with_profile_asymmetry_allowed)
|
||||
fail_unless_equals_string (gst_structure_get_string (s_video, "profile"),
|
||||
"constrained-baseline");
|
||||
|
||||
/* Check that the conversion from caps to SDP preserved the profile and level
|
||||
* caps fields, through the profile-level-id SDP sub-attribute of the fmtp
|
||||
* attribute. */
|
||||
memset (&media, 0, sizeof (media));
|
||||
fail_unless_equals_int (GST_SDP_OK, gst_sdp_media_init (&media));
|
||||
|
||||
ret = gst_sdp_media_set_media_from_caps (caps_video, &media);
|
||||
fail_unless (ret == GST_SDP_OK);
|
||||
|
||||
fmtp = gst_sdp_media_get_attribute_val (&media, "fmtp");
|
||||
fail_unless (fmtp != NULL);
|
||||
pairs = g_strsplit (fmtp, ";", 0);
|
||||
for (int i = 0; pairs[i]; i++) {
|
||||
gchar *valpos;
|
||||
const gchar *val, *key;
|
||||
valpos = strstr (pairs[i], "=");
|
||||
if (valpos) {
|
||||
/* we have a '=' and thus a value, remove the '=' with \0 */
|
||||
*valpos = '\0';
|
||||
/* value is everything between '=' and ';'. We split the pairs at ;
|
||||
* boundaries so we can take the remainder of the value. Some servers
|
||||
* put spaces around the value which we strip off here. Alternatively
|
||||
* we could strip those spaces in the depayloaders should these spaces
|
||||
* actually carry any meaning in the future. */
|
||||
val = g_strstrip (valpos + 1);
|
||||
} else {
|
||||
/* simple <param>;.. is translated into <param>=1;... */
|
||||
val = "1";
|
||||
}
|
||||
/* strip the key of spaces, convert key to lowercase but not the value. */
|
||||
key = g_strstrip (pairs[i]);
|
||||
if (!strcmp (key, "profile-level-id")) {
|
||||
gint64 spsint;
|
||||
guint8 sps[3];
|
||||
spsint = g_ascii_strtoll (val, NULL, 16);
|
||||
sps[0] = spsint >> 16;
|
||||
sps[1] = (spsint >> 8) & 0xff;
|
||||
sps[2] = spsint & 0xff;
|
||||
|
||||
g_print ("%s\n", val);
|
||||
|
||||
fail_unless_equals_string (gst_codec_utils_h264_get_profile (sps, 3),
|
||||
"constrained-baseline");
|
||||
fail_unless_equals_string (gst_codec_utils_h264_get_level (sps, 3),
|
||||
"3.1");
|
||||
profile_level_id_found = TRUE;
|
||||
break;
|
||||
}
|
||||
}
|
||||
g_strfreev (pairs);
|
||||
fail_unless (profile_level_id_found);
|
||||
|
||||
gst_sdp_media_uninit (&media);
|
||||
gst_caps_unref (caps_video);
|
||||
gst_sdp_message_free (message);
|
||||
}
|
||||
|
Loading…
x
Reference in New Issue
Block a user