sdp: add helper fuctions from/to sdp from/to caps
authorHyunjun Ko <zzoon.ko@samsung.com>
Wed, 7 Oct 2015 09:50:18 +0000 (18:50 +0900)
committerSebastian Dröge <sebastian@centricular.com>
Thu, 31 Dec 2015 15:11:57 +0000 (17:11 +0200)
<gstsdpmessage.h>
GstCaps*       gst_sdp_media_get_caps_from_media   (const GstSDPMedia *media, gint pt);
GstSDPResult   gst_sdp_media_set_media_from_caps   (const GstCaps* caps, GstSDPMedia *media);
gchar *        gst_sdp_make_keymgmt                (const gchar *uri, const gchar *base64);
GstSDPResult   gst_sdp_message_attributes_to_caps  (GstSDPMessage *msg, GstCaps *caps);
GstSDPResult   gst_sdp_media_attributes_to_caps    (GstSDPMedia *media, GstCaps *caps);

<gstmikey.h>
GstMIKEYMessage * gst_mikey_message_new_from_caps  (GstCaps *caps);
gchar *           gst_mikey_message_base64_encode  (GstMIKEYMessage* msg);

https://bugzilla.gnome.org/show_bug.cgi?id=745880

docs/libs/gst-plugins-base-libs-sections.txt
gst-libs/gst/sdp/Makefile.am
gst-libs/gst/sdp/gstmikey.c
gst-libs/gst/sdp/gstmikey.h
gst-libs/gst/sdp/gstsdpmessage.c
gst-libs/gst/sdp/gstsdpmessage.h
tests/check/libs/sdp.c
win32/common/libgstsdp.def

index fd3afdd..410c2cc 100644 (file)
@@ -1793,6 +1793,11 @@ gst_sdp_media_insert_attribute
 gst_sdp_media_replace_attribute
 gst_sdp_media_remove_attribute
 gst_sdp_media_add_attribute
+gst_sdp_media_get_caps_from_media
+gst_sdp_media_set_media_from_caps
+gst_sdp_make_keymgmt
+gst_sdp_message_attributes_to_caps
+gst_sdp_media_attributes_to_caps
 <SUBSECTION Standard>
 gst_sdp_message_get_type
 </SECTION>
@@ -1807,8 +1812,10 @@ GstMIKEYMessage
 
 gst_mikey_message_new
 gst_mikey_message_new_from_bytes
+gst_mikey_message_new_from_caps
 gst_mikey_message_new_from_data
 gst_mikey_message_to_bytes
+gst_mikey_message_base64_encode
 gst_mikey_message_copy
 gst_mikey_message_ref
 gst_mikey_message_unref
index f4f4137..2d35703 100644 (file)
@@ -10,7 +10,7 @@ lib_LTLIBRARIES = libgstsdp-@GST_API_VERSION@.la
 libgstsdp_@GST_API_VERSION@_la_SOURCES = gstsdpmessage.c gstmikey.c
 
 libgstsdp_@GST_API_VERSION@_la_CFLAGS = $(GST_PLUGINS_BASE_CFLAGS) $(GST_BASE_CFLAGS) $(GST_CFLAGS) $(GIO_CFLAGS)
-libgstsdp_@GST_API_VERSION@_la_LIBADD = $(GST_LIBS) $(GIO_LIBS)
+libgstsdp_@GST_API_VERSION@_la_LIBADD = $(top_builddir)/gst-libs/gst/rtp/libgstrtp-@GST_API_VERSION@.la $(GST_LIBS) $(GIO_LIBS)
 libgstsdp_@GST_API_VERSION@_la_LDFLAGS = $(GST_LIB_LDFLAGS) $(GST_ALL_LDFLAGS) $(GST_LT_LDFLAGS)
 
 if HAVE_INTROSPECTION
index 406d498..6aa3941 100644 (file)
@@ -2148,3 +2148,170 @@ parse_error:
     return NULL;
   }
 }
+
+#define AES_128_KEY_LEN 16
+#define AES_256_KEY_LEN 32
+#define HMAC_32_KEY_LEN 4
+#define HMAC_80_KEY_LEN 10
+
+static guint8
+enc_key_length_from_cipher_name (const gchar * cipher)
+{
+  if (g_strcmp0 (cipher, "aes-128-icm") == 0)
+    return AES_128_KEY_LEN;
+  else if (g_strcmp0 (cipher, "aes-256-icm") == 0)
+    return AES_256_KEY_LEN;
+  else {
+    GST_ERROR ("encryption algorithm '%s' not supported", cipher);
+    return 0;
+  }
+}
+
+static guint8
+auth_key_length_from_auth_name (const gchar * auth)
+{
+  if (g_strcmp0 (auth, "hmac-sha1-32") == 0)
+    return HMAC_32_KEY_LEN;
+  else if (g_strcmp0 (auth, "hmac-sha1-80") == 0)
+    return HMAC_80_KEY_LEN;
+  else {
+    GST_ERROR ("authentication algorithm '%s' not supported", auth);
+    return 0;
+  }
+}
+
+/**
+ * gst_mikey_message_new_from_caps:
+ * @caps: a #GstCaps, including SRTP parameters (srtp/srtcp cipher, authorization, key data)
+ *
+ * Makes mikey message including:
+ *  - Security Policy Payload
+ *  - Key Data Transport Payload
+ *  - Key Data Sub-Payload
+ *
+ * Returns: (transfer full): a #GstMIKEYMessage,
+ * or %NULL if there is no srtp information in the caps.
+ *
+ * Since: 1.8
+ */
+GstMIKEYMessage *
+gst_mikey_message_new_from_caps (GstCaps * caps)
+{
+  GstMIKEYMessage *msg;
+  GstMIKEYPayload *payload, *pkd;
+  guint8 byte;
+  GstStructure *s;
+  GstMapInfo info;
+  GstBuffer *srtpkey;
+  const GValue *val;
+  const gchar *srtpcipher, *srtpauth, *srtcpcipher, *srtcpauth;
+
+  g_return_val_if_fail (caps != NULL && GST_IS_CAPS (caps), NULL);
+
+  s = gst_caps_get_structure (caps, 0);
+  g_return_val_if_fail (s != NULL, NULL);
+
+  val = gst_structure_get_value (s, "srtp-key");
+  if (!val)
+    goto no_key;
+
+  srtpkey = gst_value_get_buffer (val);
+  if (!srtpkey || !GST_IS_BUFFER (srtpkey))
+    goto no_key;
+
+  srtpcipher = gst_structure_get_string (s, "srtp-cipher");
+  srtpauth = gst_structure_get_string (s, "srtp-auth");
+  srtcpcipher = gst_structure_get_string (s, "srtcp-cipher");
+  srtcpauth = gst_structure_get_string (s, "srtcp-auth");
+
+  if (srtpcipher == NULL || srtpauth == NULL || srtcpcipher == NULL ||
+      srtcpauth == NULL) {
+    GST_WARNING ("could not find the right SRTP parameters in caps");
+    return NULL;
+  }
+
+  msg = gst_mikey_message_new ();
+  /* unencrypted MIKEY message, we send this over TLS so this is allowed */
+  gst_mikey_message_set_info (msg, GST_MIKEY_VERSION, GST_MIKEY_TYPE_PSK_INIT,
+      FALSE, GST_MIKEY_PRF_MIKEY_1, g_random_int (), GST_MIKEY_MAP_TYPE_SRTP);
+
+  /* timestamp is now */
+  gst_mikey_message_add_t_now_ntp_utc (msg);
+  /* add some random data */
+  gst_mikey_message_add_rand_len (msg, 16);
+
+  /* the policy '0' is SRTP */
+  payload = gst_mikey_payload_new (GST_MIKEY_PT_SP);
+  gst_mikey_payload_sp_set (payload, 0, GST_MIKEY_SEC_PROTO_SRTP);
+
+  /* only AES-CM is supported */
+  byte = 1;
+  gst_mikey_payload_sp_add_param (payload, GST_MIKEY_SP_SRTP_ENC_ALG, 1, &byte);
+  /* encryption key length */
+  byte = enc_key_length_from_cipher_name (srtpcipher);
+  gst_mikey_payload_sp_add_param (payload, GST_MIKEY_SP_SRTP_ENC_KEY_LEN, 1,
+      &byte);
+  /* only HMAC-SHA1 */
+  gst_mikey_payload_sp_add_param (payload, GST_MIKEY_SP_SRTP_AUTH_ALG, 1,
+      &byte);
+  /* authentication key length */
+  byte = auth_key_length_from_auth_name (srtpauth);
+  gst_mikey_payload_sp_add_param (payload, GST_MIKEY_SP_SRTP_AUTH_KEY_LEN, 1,
+      &byte);
+  /* we enable encryption on RTP and RTCP */
+  gst_mikey_payload_sp_add_param (payload, GST_MIKEY_SP_SRTP_SRTP_ENC, 1,
+      &byte);
+  gst_mikey_payload_sp_add_param (payload, GST_MIKEY_SP_SRTP_SRTCP_ENC, 1,
+      &byte);
+  /* we enable authentication on RTP and RTCP */
+  gst_mikey_payload_sp_add_param (payload, GST_MIKEY_SP_SRTP_SRTP_AUTH, 1,
+      &byte);
+  gst_mikey_message_add_payload (msg, payload);
+
+  /* make unencrypted KEMAC */
+  payload = gst_mikey_payload_new (GST_MIKEY_PT_KEMAC);
+  gst_mikey_payload_kemac_set (payload, GST_MIKEY_ENC_NULL, GST_MIKEY_MAC_NULL);
+  /* add the key in KEMAC */
+  pkd = gst_mikey_payload_new (GST_MIKEY_PT_KEY_DATA);
+  gst_buffer_map (srtpkey, &info, GST_MAP_READ);
+  gst_mikey_payload_key_data_set_key (pkd, GST_MIKEY_KD_TEK, info.size,
+      info.data);
+  gst_buffer_unmap (srtpkey, &info);
+  gst_mikey_payload_kemac_add_sub (payload, pkd);
+  gst_mikey_message_add_payload (msg, payload);
+
+  return msg;
+
+no_key:
+  GST_INFO ("No srtp key");
+  return NULL;
+}
+
+/**
+ * gst_mikey_message_base64_encode:
+ * @msg: a #GstMIKEYMessage
+ *
+ * Returns: (transfer full): a #gchar, base64-encoded data
+ *
+ * Since: 1.8
+ */
+gchar *
+gst_mikey_message_base64_encode (GstMIKEYMessage * msg)
+{
+  GBytes *bytes;
+  gchar *base64;
+  const guint8 *data;
+  gsize size;
+
+  g_return_val_if_fail (msg != NULL, NULL);
+
+  /* serialize mikey message to bytes */
+  bytes = gst_mikey_message_to_bytes (msg, NULL, NULL);
+
+  /* and make it into base64 */
+  data = g_bytes_get_data (bytes, &size);
+  base64 = g_base64_encode (data, size);
+  g_bytes_unref (bytes);
+
+  return base64;
+}
index bbd06b3..a72a4ef 100644 (file)
@@ -559,6 +559,9 @@ GstMIKEYMessage *           gst_mikey_message_new_from_bytes    (GBytes *bytes,
                                                                  GError **error);
 GBytes *                    gst_mikey_message_to_bytes          (GstMIKEYMessage *msg, GstMIKEYEncryptInfo *info,
                                                                  GError **error);
+GstMIKEYMessage *           gst_mikey_message_new_from_caps         (GstCaps *caps);
+gchar *                     gst_mikey_message_base64_encode     (GstMIKEYMessage* msg);
+
 /**
  * gst_mikey_message_ref:
  * @message: The message to refcount
index 0bd235c..9878d04 100644 (file)
 #include "config.h"
 #endif
 
+#include <stdio.h>
 #include <stdlib.h>
 #include <string.h>
 
 #include <gio/gio.h>
 
+#include <gst/rtp/gstrtppayloads.h>
 #include "gstsdpmessage.h"
+#include "gstmikey.h"
 
 #define FREE_STRING(field)              g_free (field); (field) = NULL
 #define REPLACE_STRING(field, val)      FREE_STRING(field); (field) = g_strdup (val)
@@ -2802,7 +2805,7 @@ gst_sdp_parse_line (SDPContext * c, gchar type, gchar * buffer)
   switch (type) {
     case 'v':
       if (buffer[0] != '0')
-        g_warning ("wrong SDP version");
+        GST_WARNING ("wrong SDP version");
       gst_sdp_message_set_version (c->msg, buffer);
       break;
     case 'o':
@@ -3165,3 +3168,699 @@ gst_sdp_message_dump (const GstSDPMessage * msg)
   }
   return GST_SDP_OK;
 }
+
+static const gchar *
+gst_sdp_get_attribute_for_pt (const GstSDPMedia * media, const gchar * name,
+    gint pt)
+{
+  guint i;
+
+  for (i = 0;; i++) {
+    const gchar *attr;
+    gint val;
+
+    if ((attr = gst_sdp_media_get_attribute_val_n (media, name, i)) == NULL)
+      break;
+
+    if (sscanf (attr, "%d ", &val) != 1)
+      continue;
+
+    if (val == pt)
+      return attr;
+  }
+  return NULL;
+}
+
+#define PARSE_INT(p, del, res)          \
+G_STMT_START {                          \
+  gchar *t = p;                         \
+  p = strstr (p, del);                  \
+  if (p == NULL)                        \
+    res = -1;                           \
+  else {                                \
+    *p = '\0';                          \
+    p++;                                \
+    res = atoi (t);                     \
+  }                                     \
+} G_STMT_END
+
+#define PARSE_STRING(p, del, res)       \
+G_STMT_START {                          \
+  gchar *t = p;                         \
+  p = strstr (p, del);                  \
+  if (p == NULL) {                      \
+    res = NULL;                         \
+    p = t;                              \
+  }                                     \
+  else {                                \
+    *p = '\0';                          \
+    p++;                                \
+    res = t;                            \
+  }                                     \
+} G_STMT_END
+
+#define SKIP_SPACES(p)                  \
+  while (*p && g_ascii_isspace (*p))    \
+    p++;
+
+/* rtpmap contains:
+ *
+ *  <payload> <encoding_name>/<clock_rate>[/<encoding_params>]
+ */
+static gboolean
+gst_sdp_parse_rtpmap (const gchar * rtpmap, gint * payload, gchar ** name,
+    gint * rate, gchar ** params)
+{
+  gchar *p, *t;
+
+  p = (gchar *) rtpmap;
+
+  PARSE_INT (p, " ", *payload);
+  if (*payload == -1)
+    return FALSE;
+
+  SKIP_SPACES (p);
+  if (*p == '\0')
+    return FALSE;
+
+  PARSE_STRING (p, "/", *name);
+  if (*name == NULL) {
+    GST_DEBUG ("no rate, name %s", p);
+    /* no rate, assume -1 then, this is not supposed to happen but RealMedia
+     * streams seem to omit the rate. */
+    *name = p;
+    *rate = -1;
+    return TRUE;
+  }
+
+  t = p;
+  p = strstr (p, "/");
+  if (p == NULL) {
+    *rate = atoi (t);
+    return TRUE;
+  }
+  *p = '\0';
+  p++;
+  *rate = atoi (t);
+
+  t = p;
+  if (*p == '\0')
+    return TRUE;
+  *params = t;
+
+  return TRUE;
+}
+
+/**
+ * gst_sdp_media_get_caps_from_media:
+ * @media: a #GstSDPMedia
+ * @pt: a payload type
+ *
+ * Mapping of caps from SDP fields:
+ *
+ * a=rtpmap:(payload) (encoding_name)/(clock_rate)[/(encoding_params)]
+ *
+ * a=framesize:(payload) (width)-(height)
+ *
+ * a=fmtp:(payload) (param)[=(value)];...
+ *
+ * Returns: a #GstCaps, or %NULL if an error happened
+ *
+ * Since: 1.8
+ */
+GstCaps *
+gst_sdp_media_get_caps_from_media (const GstSDPMedia * media, gint pt)
+{
+  GstCaps *caps;
+  const gchar *rtpmap;
+  const gchar *fmtp;
+  const gchar *framesize;
+  gchar *name = NULL;
+  gint rate = -1;
+  gchar *params = NULL;
+  gchar *tmp;
+  GstStructure *s;
+  gint payload = 0;
+  gboolean ret;
+
+  /* get and parse rtpmap */
+  rtpmap = gst_sdp_get_attribute_for_pt (media, "rtpmap", pt);
+
+  if (rtpmap) {
+    ret = gst_sdp_parse_rtpmap (rtpmap, &payload, &name, &rate, &params);
+    if (!ret) {
+      GST_ERROR ("error parsing rtpmap, ignoring");
+      rtpmap = NULL;
+    }
+  }
+  /* dynamic payloads need rtpmap or we fail */
+  if (rtpmap == NULL && pt >= 96)
+    goto no_rtpmap;
+
+  /* check if we have a rate, if not, we need to look up the rate from the
+   * default rates based on the payload types. */
+  if (rate == -1) {
+    const GstRTPPayloadInfo *info;
+
+    if (GST_RTP_PAYLOAD_IS_DYNAMIC (pt)) {
+      /* dynamic types, use media and encoding_name */
+      tmp = g_ascii_strdown (media->media, -1);
+      info = gst_rtp_payload_info_for_name (tmp, name);
+      g_free (tmp);
+    } else {
+      /* static types, use payload type */
+      info = gst_rtp_payload_info_for_pt (pt);
+    }
+
+    if (info) {
+      if ((rate = info->clock_rate) == 0)
+        rate = -1;
+    }
+    /* we fail if we cannot find one */
+    if (rate == -1)
+      goto no_rate;
+  }
+
+  tmp = g_ascii_strdown (media->media, -1);
+  caps = gst_caps_new_simple ("application/x-unknown",
+      "media", G_TYPE_STRING, tmp, "payload", G_TYPE_INT, pt, NULL);
+  g_free (tmp);
+  s = gst_caps_get_structure (caps, 0);
+
+  gst_structure_set (s, "clock-rate", G_TYPE_INT, rate, NULL);
+
+  /* encoding name must be upper case */
+  if (name != NULL) {
+    tmp = g_ascii_strup (name, -1);
+    gst_structure_set (s, "encoding-name", G_TYPE_STRING, tmp, NULL);
+    g_free (tmp);
+  }
+
+  /* params must be lower case */
+  if (params != NULL) {
+    tmp = g_ascii_strdown (params, -1);
+    gst_structure_set (s, "encoding-params", G_TYPE_STRING, tmp, NULL);
+    g_free (tmp);
+  }
+
+  /* parse optional fmtp: field */
+  if ((fmtp = gst_sdp_get_attribute_for_pt (media, "fmtp", pt))) {
+    gchar *p;
+    gint payload = 0;
+
+    p = (gchar *) fmtp;
+
+    /* p is now of the format <payload> <param>[=<value>];... */
+    PARSE_INT (p, " ", payload);
+    if (payload != -1 && payload == pt) {
+      gchar **pairs;
+      gint i;
+
+      /* <param>[=<value>] are separated with ';' */
+      pairs = g_strsplit (p, ";", 0);
+      for (i = 0; pairs[i]; i++) {
+        gchar *valpos;
+        const gchar *val, *key;
+        gint j;
+        const gchar *reserved_keys[] =
+            { "media", "payload", "clock-rate", "encoding-name",
+          "encoding-params"
+        };
+
+        /* the key may not have a '=', the value can have other '='s */
+        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]);
+
+        /* skip keys from the fmtp, which we already use ourselves for the
+         * caps. Some software is adding random things like clock-rate into
+         * the fmtp, and we would otherwise here set a string-typed clock-rate
+         * in the caps... and thus fail to create valid RTP caps
+         */
+        for (j = 0; j < G_N_ELEMENTS (reserved_keys); j++) {
+          if (g_ascii_strcasecmp (reserved_keys[j], key) == 0) {
+            key = "";
+            break;
+          }
+        }
+
+        if (strlen (key) > 1) {
+          tmp = g_ascii_strdown (key, -1);
+          gst_structure_set (s, tmp, G_TYPE_STRING, val, NULL);
+          g_free (tmp);
+        }
+      }
+      g_strfreev (pairs);
+    }
+  }
+
+  /* parse framesize: field */
+  if ((framesize = gst_sdp_media_get_attribute_val (media, "framesize"))) {
+    gchar *p;
+
+    /* p is now of the format <payload> <width>-<height> */
+    p = (gchar *) framesize;
+
+    PARSE_INT (p, " ", payload);
+    if (payload != -1 && payload == pt) {
+      gst_structure_set (s, "a-framesize", G_TYPE_STRING, p, NULL);
+    }
+  }
+
+  return caps;
+
+  /* ERRORS */
+no_rtpmap:
+  {
+    GST_ERROR ("rtpmap type not given for dynamic payload %d", pt);
+    return NULL;
+  }
+no_rate:
+  {
+    GST_ERROR ("rate unknown for payload type %d", pt);
+    return NULL;
+  }
+}
+
+/**
+ * gst_sdp_media_set_media_from_caps:
+ * @caps: a #GstCaps
+ * @media: a #GstSDPMedia
+ *
+ * Mapping of caps to SDP fields:
+ *
+ * a=rtpmap:(payload) (encoding_name) or (clock_rate)[or (encoding_params)]
+ *
+ * a=framesize:(payload) (width)-(height)
+ *
+ * a=fmtp:(payload) (param)[=(value)];...
+ *
+ * Returns: a #GstSDPResult.
+ *
+ * Since: 1.8
+ */
+GstSDPResult
+gst_sdp_media_set_media_from_caps (const GstCaps * caps, GstSDPMedia * media)
+{
+  const gchar *caps_str, *caps_enc, *caps_params;
+  gchar *tmp;
+  gint caps_pt, caps_rate;
+  guint n_fields, j;
+  gboolean first;
+  GString *fmtp;
+  GstStructure *s;
+
+  g_return_val_if_fail (media != NULL, GST_SDP_EINVAL);
+  g_return_val_if_fail (caps != NULL && GST_IS_CAPS (caps), GST_SDP_EINVAL);
+
+  s = gst_caps_get_structure (caps, 0);
+  if (s == NULL) {
+    GST_ERROR ("ignoring stream without media type");
+    goto error;
+  }
+
+  /* get media type and payload for the m= line */
+  caps_str = gst_structure_get_string (s, "media");
+  gst_sdp_media_set_media (media, caps_str);
+
+  gst_structure_get_int (s, "payload", &caps_pt);
+  tmp = g_strdup_printf ("%d", caps_pt);
+  gst_sdp_media_add_format (media, tmp);
+  g_free (tmp);
+
+  /* get clock-rate, media type and params for the rtpmap attribute */
+  gst_structure_get_int (s, "clock-rate", &caps_rate);
+  caps_enc = gst_structure_get_string (s, "encoding-name");
+  caps_params = gst_structure_get_string (s, "encoding-params");
+
+  if (caps_enc) {
+    if (caps_params)
+      tmp = g_strdup_printf ("%d %s/%d/%s", caps_pt, caps_enc, caps_rate,
+          caps_params);
+    else
+      tmp = g_strdup_printf ("%d %s/%d", caps_pt, caps_enc, caps_rate);
+
+    gst_sdp_media_add_attribute (media, "rtpmap", tmp);
+    g_free (tmp);
+  }
+
+  /* collect all other properties and add them to fmtp or attributes */
+  fmtp = g_string_new ("");
+  g_string_append_printf (fmtp, "%d ", caps_pt);
+  first = TRUE;
+  n_fields = gst_structure_n_fields (s);
+  for (j = 0; j < n_fields; j++) {
+    const gchar *fname, *fval;
+
+    fname = gst_structure_nth_field_name (s, j);
+
+    /* filter out standard properties */
+    if (!strcmp (fname, "media"))
+      continue;
+    if (!strcmp (fname, "payload"))
+      continue;
+    if (!strcmp (fname, "clock-rate"))
+      continue;
+    if (!strcmp (fname, "encoding-name"))
+      continue;
+    if (!strcmp (fname, "encoding-params"))
+      continue;
+    if (!strcmp (fname, "ssrc"))
+      continue;
+    if (!strcmp (fname, "timestamp-offset"))
+      continue;
+    if (!strcmp (fname, "seqnum-offset"))
+      continue;
+    if (g_str_has_prefix (fname, "srtp-"))
+      continue;
+    if (g_str_has_prefix (fname, "srtcp-"))
+      continue;
+    /* handled later */
+    if (g_str_has_prefix (fname, "x-gst-rtsp-server-rtx-time"))
+      continue;
+
+    if (!strcmp (fname, "a-framesize")) {
+      /* a-framesize attribute */
+      if ((fval = gst_structure_get_string (s, fname))) {
+        tmp = g_strdup_printf ("%d %s", caps_pt, fval);
+        gst_sdp_media_add_attribute (media, fname + 2, tmp);
+        g_free (tmp);
+      }
+      continue;
+    }
+
+    if (g_str_has_prefix (fname, "a-")) {
+      /* attribute */
+      if ((fval = gst_structure_get_string (s, fname)))
+        gst_sdp_media_add_attribute (media, fname + 2, fval);
+      continue;
+    }
+    if (g_str_has_prefix (fname, "x-")) {
+      /* attribute */
+      if ((fval = gst_structure_get_string (s, fname)))
+        gst_sdp_media_add_attribute (media, fname, fval);
+      continue;
+    }
+
+    if ((fval = gst_structure_get_string (s, fname))) {
+      g_string_append_printf (fmtp, "%s%s=%s", first ? "" : ";", fname, fval);
+      first = FALSE;
+    }
+  }
+
+  if (!first) {
+    tmp = g_string_free (fmtp, FALSE);
+    gst_sdp_media_add_attribute (media, "fmtp", tmp);
+    g_free (tmp);
+  } else {
+    g_string_free (fmtp, TRUE);
+  }
+
+  return GST_SDP_OK;
+
+  /* ERRORS */
+error:
+  {
+    GST_DEBUG ("ignoring stream");
+    return GST_SDP_EINVAL;
+  }
+}
+
+/**
+ * gst_sdp_make_keymgmt:
+ * @uri: a #gchar URI
+ * @base64: a #gchar base64-encoded key data
+ *
+ * Makes key management data
+ *
+ * Returns: (transfer full): a #gchar key-mgmt data,
+ *
+ * Since: 1.8
+ */
+gchar *
+gst_sdp_make_keymgmt (const gchar * uri, const gchar * base64)
+{
+  g_return_val_if_fail (uri != NULL, NULL);
+  g_return_val_if_fail (base64 != NULL, NULL);
+
+  return g_strdup_printf ("prot=mikey;uri=\"%s\";data=\"%s\"", uri, base64);
+}
+
+#define AES_128_KEY_LEN 16
+#define AES_256_KEY_LEN 32
+#define HMAC_32_KEY_LEN 4
+#define HMAC_80_KEY_LEN 10
+
+static gboolean
+gst_sdp_parse_keymgmt (const gchar * keymgmt, GstCaps * caps)
+{
+  gboolean res = FALSE;
+  gsize size;
+  guchar *data;
+  GstMIKEYMessage *msg;
+  const GstMIKEYPayload *payload;
+  const gchar *srtp_cipher;
+  const gchar *srtp_auth;
+  gchar *orig_value;
+  gchar *p, *kmpid;
+
+  p = orig_value = g_strdup (keymgmt);
+
+  SKIP_SPACES (p);
+  if (*p == '\0') {
+    g_free (orig_value);
+    return FALSE;
+  }
+
+  PARSE_STRING (p, " ", kmpid);
+  if (kmpid == NULL || !g_str_equal (kmpid, "mikey")) {
+    g_free (orig_value);
+    return FALSE;
+  }
+  data = g_base64_decode (p, &size);
+  g_free (orig_value);          /* Don't need this any more */
+
+  if (data == NULL)
+    return FALSE;
+
+  msg = gst_mikey_message_new_from_data (data, size, NULL, NULL);
+  g_free (data);
+  if (msg == NULL)
+    return FALSE;
+
+  srtp_cipher = "aes-128-icm";
+  srtp_auth = "hmac-sha1-80";
+
+  /* check the Security policy if any */
+  if ((payload = gst_mikey_message_find_payload (msg, GST_MIKEY_PT_SP, 0))) {
+    GstMIKEYPayloadSP *p = (GstMIKEYPayloadSP *) payload;
+    guint len, i;
+
+    if (p->proto != GST_MIKEY_SEC_PROTO_SRTP)
+      goto done;
+
+    len = gst_mikey_payload_sp_get_n_params (payload);
+    for (i = 0; i < len; i++) {
+      const GstMIKEYPayloadSPParam *param =
+          gst_mikey_payload_sp_get_param (payload, i);
+
+      switch (param->type) {
+        case GST_MIKEY_SP_SRTP_ENC_ALG:
+          switch (param->val[0]) {
+            case 0:
+              srtp_cipher = "null";
+              break;
+            case 2:
+            case 1:
+              srtp_cipher = "aes-128-icm";
+              break;
+            default:
+              break;
+          }
+          break;
+        case GST_MIKEY_SP_SRTP_ENC_KEY_LEN:
+          switch (param->val[0]) {
+            case AES_128_KEY_LEN:
+              srtp_cipher = "aes-128-icm";
+              break;
+            case AES_256_KEY_LEN:
+              srtp_cipher = "aes-256-icm";
+              break;
+            default:
+              break;
+          }
+          break;
+        case GST_MIKEY_SP_SRTP_AUTH_ALG:
+          switch (param->val[0]) {
+            case 0:
+              srtp_auth = "null";
+              break;
+            case 2:
+            case 1:
+              srtp_auth = "hmac-sha1-80";
+              break;
+            default:
+              break;
+          }
+          break;
+        case GST_MIKEY_SP_SRTP_AUTH_KEY_LEN:
+          switch (param->val[0]) {
+            case HMAC_32_KEY_LEN:
+              srtp_auth = "hmac-sha1-32";
+              break;
+            case HMAC_80_KEY_LEN:
+              srtp_auth = "hmac-sha1-80";
+              break;
+            default:
+              break;
+          }
+          break;
+        case GST_MIKEY_SP_SRTP_SRTP_ENC:
+          break;
+        case GST_MIKEY_SP_SRTP_SRTCP_ENC:
+          break;
+        default:
+          break;
+      }
+    }
+  }
+
+  if (!(payload = gst_mikey_message_find_payload (msg, GST_MIKEY_PT_KEMAC, 0)))
+    goto done;
+  else {
+    GstMIKEYPayloadKEMAC *p = (GstMIKEYPayloadKEMAC *) payload;
+    const GstMIKEYPayload *sub;
+    GstMIKEYPayloadKeyData *pkd;
+    GstBuffer *buf;
+
+    if (p->enc_alg != GST_MIKEY_ENC_NULL || p->mac_alg != GST_MIKEY_MAC_NULL)
+      goto done;
+
+    if (!(sub = gst_mikey_payload_kemac_get_sub (payload, 0)))
+      goto done;
+
+    if (sub->type != GST_MIKEY_PT_KEY_DATA)
+      goto done;
+
+    pkd = (GstMIKEYPayloadKeyData *) sub;
+    buf =
+        gst_buffer_new_wrapped (g_memdup (pkd->key_data, pkd->key_len),
+        pkd->key_len);
+    gst_caps_set_simple (caps, "srtp-key", GST_TYPE_BUFFER, buf, NULL);
+    gst_buffer_unref (buf);
+  }
+
+  gst_caps_set_simple (caps,
+      "srtp-cipher", G_TYPE_STRING, srtp_cipher,
+      "srtp-auth", G_TYPE_STRING, srtp_auth,
+      "srtcp-cipher", G_TYPE_STRING, srtp_cipher,
+      "srtcp-auth", G_TYPE_STRING, srtp_auth, NULL);
+
+  res = TRUE;
+done:
+  gst_mikey_message_unref (msg);
+
+  return res;
+}
+
+static GstSDPResult
+sdp_addtributes_to_caps (GArray * attributes, GstCaps * caps)
+{
+  if (attributes->len > 0) {
+    GstStructure *s;
+    guint i;
+
+    s = gst_caps_get_structure (caps, 0);
+
+    for (i = 0; i < attributes->len; i++) {
+      GstSDPAttribute *attr = &g_array_index (attributes, GstSDPAttribute, i);
+      gchar *tofree, *key;
+
+      key = attr->key;
+
+      /* skip some of the attribute we already handle */
+      if (!strcmp (key, "fmtp"))
+        continue;
+      if (!strcmp (key, "rtpmap"))
+        continue;
+      if (!strcmp (key, "control"))
+        continue;
+      if (!strcmp (key, "range"))
+        continue;
+      if (!strcmp (key, "framesize"))
+        continue;
+      if (g_str_equal (key, "key-mgmt")) {
+        gst_sdp_parse_keymgmt (attr->value, caps);
+        continue;
+      }
+
+      /* string must be valid UTF8 */
+      if (!g_utf8_validate (attr->value, -1, NULL))
+        continue;
+
+      if (!g_str_has_prefix (key, "x-"))
+        tofree = key = g_strdup_printf ("a-%s", key);
+      else
+        tofree = NULL;
+
+      GST_DEBUG ("adding caps: %s=%s", key, attr->value);
+      gst_structure_set (s, key, G_TYPE_STRING, attr->value, NULL);
+      g_free (tofree);
+    }
+  }
+
+  return GST_SDP_OK;
+}
+
+/**
+ * gst_sdp_message_attributes_to_caps:
+ * @msg: a #GstSDPMessage
+ * @caps: a #GstCaps
+ *
+ * Mapping of attributes of #GstSDPMessage to #GstCaps
+ *
+ * Returns: a #GstSDPResult.
+ *
+ * Since: 1.8
+ */
+GstSDPResult
+gst_sdp_message_attributes_to_caps (const GstSDPMessage * msg, GstCaps * caps)
+{
+  g_return_val_if_fail (msg != NULL, GST_SDP_EINVAL);
+  g_return_val_if_fail (caps != NULL && GST_IS_CAPS (caps), GST_SDP_EINVAL);
+
+  return sdp_addtributes_to_caps (msg->attributes, caps);
+}
+
+/**
+ * gst_sdp_media_attributes_to_caps:
+ * @media: a #GstSDPMedia
+ * @caps: a #GstCaps
+ *
+ * Mapping of attributes of #GstSDPMedia to #GstCaps
+ *
+ * Returns: a #GstSDPResult.
+ *
+ * Since: 1.8
+ */
+GstSDPResult
+gst_sdp_media_attributes_to_caps (const GstSDPMedia * media, GstCaps * caps)
+{
+  g_return_val_if_fail (media != NULL, GST_SDP_EINVAL);
+  g_return_val_if_fail (caps != NULL && GST_IS_CAPS (caps), GST_SDP_EINVAL);
+
+  return sdp_addtributes_to_caps (media->attributes, caps);
+}
index ea9aed8..deffb4c 100644 (file)
@@ -501,6 +501,11 @@ GstSDPResult            gst_sdp_media_replace_attribute     (GstSDPMedia *media,
 GstSDPResult            gst_sdp_media_remove_attribute      (GstSDPMedia *media, guint idx);
 GstSDPResult            gst_sdp_media_add_attribute         (GstSDPMedia *media, const gchar *key,
                                                              const gchar *value);
+GstCaps*                gst_sdp_media_get_caps_from_media   (const GstSDPMedia *media, gint pt);
+GstSDPResult            gst_sdp_media_set_media_from_caps   (const GstCaps* caps, GstSDPMedia *media);
+gchar *                 gst_sdp_make_keymgmt                (const gchar *uri, const gchar *base64);
+GstSDPResult            gst_sdp_message_attributes_to_caps  (const GstSDPMessage *msg, GstCaps *caps);
+GstSDPResult            gst_sdp_media_attributes_to_caps    (const GstSDPMedia *media, GstCaps *caps);
 
 #ifdef G_DEFINE_AUTOPTR_CLEANUP_FUNC
 G_DEFINE_AUTOPTR_CLEANUP_FUNC(GstSDPMessage, gst_sdp_message_free)
index 0cf5fc4..c9192d7 100644 (file)
@@ -62,6 +62,18 @@ static const gchar *sdp = "v=0\r\n"
     "a=sendrecv\r\n" "m=audio 4545 RTP/AVP 14\r\n" "a=sendrecv\r\n"
     "m=audio 1010 TCP 14\r\n";
 
+static const gchar caps_video_string1[] =
+    "application/x-unknown, media=(string)video, payload=(int)96, "
+    "clock-rate=(int)90000, encoding-name=(string)MP4V-ES";
+
+static const gchar caps_video_string2[] =
+    "application/x-unknown, media=(string)video, payload=(int)97, "
+    "clock-rate=(int)90000, encoding-name=(string)H263-1998";
+
+static const gchar caps_audio_string[] =
+    "application/x-unknown, media=(string)audio, payload=(int)14, "
+    "clock-rate=(int)90000";
+
 GST_START_TEST (boxed)
 {
   GValue value = G_VALUE_INIT;
@@ -194,6 +206,97 @@ GST_START_TEST (modify)
 }
 
 GST_END_TEST
+GST_START_TEST (caps_from_media)
+{
+  GstSDPMessage *message;
+  glong length = -1;
+  const GstSDPMedia *media1, *media2, *media3;
+  GstCaps *caps_video1, *caps_video2, *caps_audio;
+  GstCaps *result_video1, *result_video2, *result_audio;
+
+  gst_sdp_message_new (&message);
+  gst_sdp_message_parse_buffer ((guint8 *) sdp, length, message);
+
+  media1 = gst_sdp_message_get_media (message, 0);
+  fail_unless (media1 != NULL);
+
+  media2 = gst_sdp_message_get_media (message, 1);
+  fail_unless (media2 != NULL);
+
+  media3 = gst_sdp_message_get_media (message, 2);
+  fail_unless (media2 != NULL);
+
+  caps_video1 = gst_sdp_media_get_caps_from_media (media1, 96);
+  caps_video2 = gst_sdp_media_get_caps_from_media (media1, 97);
+  caps_audio = gst_sdp_media_get_caps_from_media (media3, 14);
+
+  result_video1 = gst_caps_from_string (caps_video_string1);
+  fail_unless (gst_caps_is_strictly_equal (caps_video1, result_video1));
+  gst_caps_unref (result_video1);
+  gst_caps_unref (caps_video1);
+
+  result_video2 = gst_caps_from_string (caps_video_string2);
+  fail_unless (gst_caps_is_strictly_equal (caps_video2, result_video2));
+  gst_caps_unref (result_video2);
+  gst_caps_unref (caps_video2);
+
+  result_audio = gst_caps_from_string (caps_audio_string);
+  fail_unless (gst_caps_is_strictly_equal (caps_audio, result_audio));
+  gst_caps_unref (result_audio);
+  gst_caps_unref (caps_audio);
+
+  gst_sdp_message_free (message);
+}
+
+GST_END_TEST
+GST_START_TEST (media_from_caps)
+{
+  GstSDPResult ret = GST_SDP_OK;
+  GstSDPMessage *message;
+  glong length = -1;
+  GstSDPMedia *media_video, *media_audio;
+  const GstSDPMedia *result_video, *result_audio;
+  GstCaps *caps_video, *caps_audio;
+  const gchar *media1_text, *media2_text, *media3_text, *media4_text;
+
+  caps_video = gst_caps_from_string (caps_video_string1);
+  caps_audio = gst_caps_from_string (caps_audio_string);
+
+  gst_sdp_media_new (&media_video);
+  fail_unless (media_video != NULL);
+  gst_sdp_media_new (&media_audio);
+  fail_unless (media_audio != NULL);
+
+  ret = gst_sdp_media_set_media_from_caps (caps_video, media_video);
+  fail_unless (ret == GST_SDP_OK);
+  gst_caps_unref (caps_video);
+  ret = gst_sdp_media_set_media_from_caps (caps_audio, media_audio);
+  fail_unless (ret == GST_SDP_OK);
+  gst_caps_unref (caps_audio);
+
+  gst_sdp_message_new (&message);
+  gst_sdp_message_parse_buffer ((guint8 *) sdp, length, message);
+
+  result_video = gst_sdp_message_get_media (message, 0);
+  fail_unless (result_video != NULL);
+
+  result_audio = gst_sdp_message_get_media (message, 2);
+  fail_unless (result_audio != NULL);
+
+  media1_text = gst_sdp_media_get_attribute_val (media_video, "rtpmap");
+  media2_text = gst_sdp_media_get_attribute_val (result_video, "rtpmap");
+  media3_text = gst_sdp_media_get_format (media_audio, 0);
+  media4_text = gst_sdp_media_get_format (result_audio, 0);
+
+  fail_if (g_strcmp0 (media1_text, media2_text) != 0);
+  fail_if (g_strcmp0 (media3_text, media4_text) != 0);
+
+  gst_sdp_media_free (media_video);
+  gst_sdp_media_free (media_audio);
+  gst_sdp_message_free (message);
+}
+
+GST_END_TEST
 /*
  * End of test cases
  */
@@ -207,6 +310,8 @@ sdp_suite (void)
   tcase_add_test (tc_chain, copy);
   tcase_add_test (tc_chain, boxed);
   tcase_add_test (tc_chain, modify);
+  tcase_add_test (tc_chain, caps_from_media);
+  tcase_add_test (tc_chain, media_from_caps);
 
   return s;
 }
index 6ff1e6b..2c31ff7 100644 (file)
@@ -6,6 +6,7 @@ EXPORTS
        gst_mikey_message_add_rand_len
        gst_mikey_message_add_t
        gst_mikey_message_add_t_now_ntp_utc
+       gst_mikey_message_base64_encode
        gst_mikey_message_find_payload
        gst_mikey_message_get_cs_srtp
        gst_mikey_message_get_n_cs
@@ -16,6 +17,7 @@ EXPORTS
        gst_mikey_message_insert_payload
        gst_mikey_message_new
        gst_mikey_message_new_from_bytes
+       gst_mikey_message_new_from_caps
        gst_mikey_message_new_from_data
        gst_mikey_message_remove_cs_srtp
        gst_mikey_message_remove_payload
@@ -49,12 +51,14 @@ EXPORTS
        gst_sdp_bandwidth_set
        gst_sdp_connection_clear
        gst_sdp_connection_set
+       gst_sdp_make_keymgmt
        gst_sdp_media_add_attribute
        gst_sdp_media_add_bandwidth
        gst_sdp_media_add_connection
        gst_sdp_media_add_format
        gst_sdp_media_as_text
        gst_sdp_media_attributes_len
+       gst_sdp_media_attributes_to_caps
        gst_sdp_media_bandwidths_len
        gst_sdp_media_connections_len
        gst_sdp_media_copy
@@ -64,6 +68,7 @@ EXPORTS
        gst_sdp_media_get_attribute_val
        gst_sdp_media_get_attribute_val_n
        gst_sdp_media_get_bandwidth
+       gst_sdp_media_get_caps_from_media
        gst_sdp_media_get_connection
        gst_sdp_media_get_format
        gst_sdp_media_get_information
@@ -89,6 +94,7 @@ EXPORTS
        gst_sdp_media_set_information
        gst_sdp_media_set_key
        gst_sdp_media_set_media
+       gst_sdp_media_set_media_from_caps
        gst_sdp_media_set_port_info
        gst_sdp_media_set_proto
        gst_sdp_media_uninit
@@ -102,6 +108,7 @@ EXPORTS
        gst_sdp_message_as_text
        gst_sdp_message_as_uri
        gst_sdp_message_attributes_len
+       gst_sdp_message_attributes_to_caps
        gst_sdp_message_bandwidths_len
        gst_sdp_message_copy
        gst_sdp_message_dump