rtpmanager: improve SDES handling
authorPascal Buhler <pascal.buhler@tandberg.com>
Mon, 31 Aug 2009 16:42:25 +0000 (18:42 +0200)
committerWim Taymans <wim@metal.(none)>
Tue, 22 Dec 2009 20:43:25 +0000 (21:43 +0100)
Store SDES internally as a struct to support multiple PRIV values.
Include all values set in SDES struct when sending RTCP SDES.

gst/rtpmanager/rtpsession.c
gst/rtpmanager/rtpsource.c
gst/rtpmanager/rtpsource.h

index e3eae0f..898d59d 100644 (file)
@@ -1669,6 +1669,7 @@ rtp_session_process_sdes (RTPSession * sess, GstRTCPPacket * packet,
     guint32 ssrc;
     gboolean changed, created;
     RTPSource *source;
+    GstStructure *sdes;
 
     ssrc = gst_rtcp_packet_sdes_get_ssrc (packet);
 
@@ -1681,24 +1682,45 @@ rtp_session_process_sdes (RTPSession * sess, GstRTCPPacket * packet,
     if (!source)
       return;
 
+    sdes = gst_structure_new ("application/x-rtp-source-sdes", NULL);
+
     more_entries = gst_rtcp_packet_sdes_first_entry (packet);
     j = 0;
     while (more_entries) {
       GstRTCPSDESType type;
       guint8 len;
       guint8 *data;
+      gchar *name;
+      gchar *value;
 
       gst_rtcp_packet_sdes_get_entry (packet, &type, &len, &data);
 
       GST_DEBUG ("entry %d, type %d, len %d, data %.*s", j, type, len, len,
           data);
 
-      changed |= rtp_source_set_sdes (source, type, data, len);
+      if (type == GST_RTCP_SDES_PRIV) {
+        name = g_strndup ((const gchar *) &data[1], data[0]);
+        len -= data[0] + 1;
+        data += data[0] + 1;
+      } else {
+        name = g_strdup (gst_rtcp_sdes_type_to_name (type));
+      }
+
+      value = g_strndup ((const gchar *) data, len);
+
+      gst_structure_set (sdes, name, G_TYPE_STRING, value, NULL);
+
+      g_free (name);
+      g_free (value);
 
       more_entries = gst_rtcp_packet_sdes_next_entry (packet);
       j++;
     }
 
+    changed = rtp_source_set_sdes_struct (source, sdes);
+
+    gst_structure_free (sdes);
+
     source->validated = TRUE;
 
     if (created)
@@ -2296,27 +2318,60 @@ static void
 session_sdes (RTPSession * sess, ReportData * data)
 {
   GstRTCPPacket *packet = &data->packet;
-  guint8 *sdes_data;
-  guint sdes_len;
+  GstStructure *sdes;
+  gint i, n_fields;
 
   /* add SDES packet */
   gst_rtcp_buffer_add_packet (data->rtcp, GST_RTCP_TYPE_SDES, packet);
 
   gst_rtcp_packet_sdes_add_item (packet, sess->source->ssrc);
 
-  rtp_source_get_sdes (sess->source, GST_RTCP_SDES_CNAME, &sdes_data,
-      &sdes_len);
-  gst_rtcp_packet_sdes_add_entry (packet, GST_RTCP_SDES_CNAME, sdes_len,
-      sdes_data);
-
-  /* other SDES items must only be added at regular intervals and only when the
-   * user requests to since it might be a privacy problem */
-#if 0
-  gst_rtcp_packet_sdes_add_entry (&packet, GST_RTCP_SDES_NAME,
-      strlen (sess->name), (guint8 *) sess->name);
-  gst_rtcp_packet_sdes_add_entry (&packet, GST_RTCP_SDES_TOOL,
-      strlen (sess->tool), (guint8 *) sess->tool);
-#endif
+  sdes = rtp_source_get_sdes_struct (sess->source);
+
+  /* add all fields in the structure, the order is not important. */
+  n_fields = gst_structure_n_fields (sdes);
+  for (i = 0; i < n_fields; ++i) {
+    const gchar *field;
+    const gchar *value;
+    GstRTCPSDESType type;
+
+    field = gst_structure_nth_field_name (sdes, i);
+    if (field == NULL)
+      continue;
+    value = gst_structure_get_string (sdes, field);
+    if (value == NULL)
+      continue;
+    type = gst_rtcp_sdes_name_to_type (field);
+
+    if (type > GST_RTCP_SDES_END && type < GST_RTCP_SDES_PRIV) {
+      gst_rtcp_packet_sdes_add_entry (packet, type, strlen (value),
+          (const guint8 *) value);
+    } else if (type == GST_RTCP_SDES_PRIV) {
+      gsize prefix_len;
+      gsize value_len;
+      gsize data_len;
+      guint8 data[256];
+
+      /* don't accept entries that are too big */
+      prefix_len = strlen (field);
+      if (prefix_len > 255)
+        continue;
+      value_len = strlen (value);
+      if (value_len > 255)
+        continue;
+      data_len = 1 + prefix_len + value_len;
+      if (data_len > 255)
+        continue;
+
+      data[0] = prefix_len;
+      memcpy (&data[1], field, prefix_len);
+      memcpy (&data[1 + prefix_len], value, value_len);
+
+      gst_rtcp_packet_sdes_add_entry (packet, type, data_len, data);
+    }
+  }
+
+  gst_structure_free (sdes);
 
   data->has_sdes = TRUE;
 }
index 03e5ae5..e6f8bef 100644 (file)
@@ -98,16 +98,19 @@ rtp_source_class_init (RTPSourceClass * klass)
   /**
    * RTPSource::sdes
    *
-   * The current SDES items of the source. Returns a structure with the
-   * following fields:
+   * The current SDES items of the source. Returns a structure with name
+   * application/x-rtp-source-sdes and may contain the following fields:
    *
-   *  'cname'    G_TYPE_STRING  : The canonical name
-   *  'name'     G_TYPE_STRING  : The user name
-   *  'email'    G_TYPE_STRING  : The user's electronic mail address
-   *  'phone'    G_TYPE_STRING  : The user's phone number
-   *  'location' G_TYPE_STRING  : The geographic user location
-   *  'tool'     G_TYPE_STRING  : The name of application or tool
-   *  'note'     G_TYPE_STRING  : A notice about the source
+   *  'cname'       G_TYPE_STRING  : The canonical name
+   *  'name'        G_TYPE_STRING  : The user name
+   *  'email'       G_TYPE_STRING  : The user's electronic mail address
+   *  'phone'       G_TYPE_STRING  : The user's phone number
+   *  'location'    G_TYPE_STRING  : The geographic user location
+   *  'tool'        G_TYPE_STRING  : The name of application or tool
+   *  'note'        G_TYPE_STRING  : A notice about the source
+   *
+   *  other fields may be present and these represent private items in
+   *  the SDES where the field name is the prefix.
    */
   g_object_class_install_property (gobject_class, PROP_SDES,
       g_param_spec_boxed ("sdes", "SDES",
@@ -156,6 +159,8 @@ rtp_source_init (RTPSource * src)
   src->internal = FALSE;
   src->probation = RTP_DEFAULT_PROBATION;
 
+  src->sdes = gst_structure_new ("application/x-rtp-source-sdes", NULL);
+
   src->payload = -1;
   src->clock_rate = -1;
   src->packets = g_queue_new ();
@@ -170,7 +175,6 @@ rtp_source_finalize (GObject * object)
 {
   RTPSource *src;
   GstBuffer *buffer;
-  gint i;
 
   src = RTP_SOURCE_CAST (object);
 
@@ -178,8 +182,7 @@ rtp_source_finalize (GObject * object)
     gst_buffer_unref (buffer);
   g_queue_free (src->packets);
 
-  for (i = 0; i < 9; i++)
-    g_free (src->sdes[i]);
+  gst_structure_free (src->sdes);
 
   g_free (src->bye_reason);
 
@@ -281,88 +284,64 @@ rtp_source_create_stats (RTPSource * src)
 
 /**
  * rtp_source_get_sdes_struct:
- * @src: an #RTSPSource
+ * @src: an #RTPSource
  *
- * Get the SDES data as a GstStructure
+ * Get the SDES from @src.
  *
- * Returns: a GstStructure with SDES items for @src.
+ * Returns: %GstStructure of type "application/x-rtp-source-sdes", see
+ * the SDES property for more details.
  */
 GstStructure *
 rtp_source_get_sdes_struct (RTPSource * src)
 {
-  GstStructure *s;
-  gchar *str;
+  g_return_val_if_fail (RTP_IS_SOURCE (src), NULL);
 
-  s = gst_structure_new ("application/x-rtp-source-sdes",
-      "ssrc", G_TYPE_UINT, (guint) src->ssrc, NULL);
+  return gst_structure_copy (src->sdes);
+}
 
-  if ((str = rtp_source_get_sdes_string (src, GST_RTCP_SDES_CNAME))) {
-    gst_structure_set (s, "cname", G_TYPE_STRING, str, NULL);
-    g_free (str);
-  }
-  if ((str = rtp_source_get_sdes_string (src, GST_RTCP_SDES_NAME))) {
-    gst_structure_set (s, "name", G_TYPE_STRING, str, NULL);
-    g_free (str);
-  }
-  if ((str = rtp_source_get_sdes_string (src, GST_RTCP_SDES_EMAIL))) {
-    gst_structure_set (s, "email", G_TYPE_STRING, str, NULL);
-    g_free (str);
-  }
-  if ((str = rtp_source_get_sdes_string (src, GST_RTCP_SDES_PHONE))) {
-    gst_structure_set (s, "phone", G_TYPE_STRING, str, NULL);
-    g_free (str);
-  }
-  if ((str = rtp_source_get_sdes_string (src, GST_RTCP_SDES_LOC))) {
-    gst_structure_set (s, "location", G_TYPE_STRING, str, NULL);
-    g_free (str);
-  }
-  if ((str = rtp_source_get_sdes_string (src, GST_RTCP_SDES_TOOL))) {
-    gst_structure_set (s, "tool", G_TYPE_STRING, str, NULL);
-    g_free (str);
-  }
-  if ((str = rtp_source_get_sdes_string (src, GST_RTCP_SDES_NOTE))) {
-    gst_structure_set (s, "note", G_TYPE_STRING, str, NULL);
-    g_free (str);
-  }
-  return s;
+static gboolean
+sdes_struct_compare_func (GQuark field_id, const GValue * value,
+    gpointer user_data)
+{
+  GstStructure *old = GST_STRUCTURE (user_data);
+  const gchar *field = g_quark_to_string (field_id);
+
+  if (!gst_structure_has_field (old, field))
+    return FALSE;
+
+  g_assert (G_VALUE_HOLDS_STRING (value));
+  return strcmp (g_value_get_string (value), gst_structure_get_string (old,
+          field)) == 0;
 }
 
 /**
- * rtp_source_set_sdes_struct:
- * @src: an #RTSPSource
- * @sdes: a #GstStructure with SDES info
+ * rtp_source_set_sdes:
+ * @src: an #RTPSource
+ * @sdes: the SDES structure
  *
- * Set the SDES items from @sdes.
+ * Store the @sdes in @src. @sdes must be a structure of type
+ * "application/x-rtp-source-sdes", see the SDES property for more details.
+ *
+ * Returns: %FALSE if the SDES was unchanged.
  */
-void
+gboolean
 rtp_source_set_sdes_struct (RTPSource * src, const GstStructure * sdes)
 {
-  const gchar *str;
+  gboolean changed;
 
-  if (!gst_structure_has_name (sdes, "application/x-rtp-source-sdes"))
-    return;
+  g_return_val_if_fail (RTP_IS_SOURCE (src), FALSE);
 
-  if ((str = gst_structure_get_string (sdes, "cname"))) {
-    rtp_source_set_sdes_string (src, GST_RTCP_SDES_CNAME, str);
-  }
-  if ((str = gst_structure_get_string (sdes, "name"))) {
-    rtp_source_set_sdes_string (src, GST_RTCP_SDES_NAME, str);
-  }
-  if ((str = gst_structure_get_string (sdes, "email"))) {
-    rtp_source_set_sdes_string (src, GST_RTCP_SDES_EMAIL, str);
-  }
-  if ((str = gst_structure_get_string (sdes, "phone"))) {
-    rtp_source_set_sdes_string (src, GST_RTCP_SDES_PHONE, str);
-  }
-  if ((str = gst_structure_get_string (sdes, "location"))) {
-    rtp_source_set_sdes_string (src, GST_RTCP_SDES_LOC, str);
-  }
-  if ((str = gst_structure_get_string (sdes, "tool"))) {
-    rtp_source_set_sdes_string (src, GST_RTCP_SDES_TOOL, str);
-  }
-  if ((str = gst_structure_get_string (sdes, "note"))) {
-    rtp_source_set_sdes_string (src, GST_RTCP_SDES_NOTE, str);
+  g_return_val_if_fail (strcmp (gst_structure_get_name (sdes),
+          "application/x-rtp-source-sdes") == 0, FALSE);
+
+  changed = !gst_structure_foreach (sdes, sdes_struct_compare_func, src->sdes);
+
+  if (changed) {
+    gst_structure_free (src->sdes);
+    src->sdes = gst_structure_copy (sdes);
   }
+
+  return changed;
 }
 
 static void
@@ -657,54 +636,12 @@ rtp_source_update_caps (RTPSource * src, GstCaps * caps)
 }
 
 /**
- * rtp_source_set_sdes:
- * @src: an #RTPSource
- * @type: the type of the SDES item
- * @data: the SDES data
- * @len: the SDES length
- *
- * Store an SDES item of @type in @src.
- *
- * Returns: %FALSE if the SDES item was unchanged or @type is unknown.
- */
-gboolean
-rtp_source_set_sdes (RTPSource * src, GstRTCPSDESType type,
-    const guint8 * data, guint len)
-{
-  guint8 *old;
-
-  g_return_val_if_fail (RTP_IS_SOURCE (src), FALSE);
-
-  if (type < 0 || type > GST_RTCP_SDES_PRIV)
-    return FALSE;
-
-  old = src->sdes[type];
-
-  /* lengths are the same, check if the data is the same */
-  if ((src->sdes_len[type] == len))
-    if (data != NULL && old != NULL && (memcmp (old, data, len) == 0))
-      return FALSE;
-
-  /* NULL data, make sure we store 0 length or if no length is given,
-   * take strlen */
-  if (data == NULL)
-    len = 0;
-
-  g_free (src->sdes[type]);
-  src->sdes[type] = g_memdup (data, len);
-  src->sdes_len[type] = len;
-
-  return TRUE;
-}
-
-/**
  * rtp_source_set_sdes_string:
  * @src: an #RTPSource
  * @type: the type of the SDES item
  * @data: the SDES data
  *
- * Store an SDES item of @type in @src. This function is similar to
- * rtp_source_set_sdes() but takes a null-terminated string for convenience.
+ * Store an SDES item of @type in @src.
  *
  * Returns: %FALSE if the SDES item was unchanged or @type is unknown.
  */
@@ -712,48 +649,26 @@ gboolean
 rtp_source_set_sdes_string (RTPSource * src, GstRTCPSDESType type,
     const gchar * data)
 {
-  guint len;
-  gboolean result;
+  const gchar *old;
+  const gchar *field;
 
-  if (data)
-    len = strlen (data);
-  else
-    len = 0;
-
-  result = rtp_source_set_sdes (src, type, (guint8 *) data, len);
+  field = gst_rtcp_sdes_type_to_name (type);
 
-  return result;
-}
+  if (gst_structure_has_field (src->sdes, field))
+    old = gst_structure_get_string (src->sdes, field);
+  else
+    old = NULL;
 
-/**
- * rtp_source_get_sdes:
- * @src: an #RTPSource
- * @type: the type of the SDES item
- * @data: location to store the SDES data or NULL
- * @len: location to store the SDES length or NULL
- *
- * Get the SDES item of @type from @src. Note that @data does not always point
- * to a null-terminated string, use rtp_source_get_sdes_string() to retrieve a
- * null-terminated string instead.
- *
- * @data remains valid until the next call to rtp_source_set_sdes().
- *
- * Returns: %TRUE if @type was valid and @data and @len contain valid
- * data. @data can be NULL when the item was unset.
- */
-gboolean
-rtp_source_get_sdes (RTPSource * src, GstRTCPSDESType type, guint8 ** data,
-    guint * len)
-{
-  g_return_val_if_fail (RTP_IS_SOURCE (src), FALSE);
+  if (old == NULL && data == NULL)
+    return FALSE;
 
-  if (type < 0 || type > GST_RTCP_SDES_PRIV)
+  if (old != NULL && data != NULL && strcmp (old, data) == 0)
     return FALSE;
 
-  if (data)
-    *data = src->sdes[type];
-  if (len)
-    *len = src->sdes_len[type];
+  if (data == NULL)
+    gst_structure_remove_field (src->sdes, field);
+  else
+    gst_structure_set (src->sdes, field, G_TYPE_STRING, data, NULL);
 
   return TRUE;
 }
@@ -772,13 +687,19 @@ gchar *
 rtp_source_get_sdes_string (RTPSource * src, GstRTCPSDESType type)
 {
   gchar *result;
+  const gchar *type_name;
 
   g_return_val_if_fail (RTP_IS_SOURCE (src), NULL);
 
-  if (type < 0 || type > GST_RTCP_SDES_PRIV)
+  if (type < 0 || type > GST_RTCP_SDES_PRIV - 1)
+    return NULL;
+
+  type_name = gst_rtcp_sdes_type_to_name (type);
+
+  if (!gst_structure_has_field (src->sdes, type_name))
     return NULL;
 
-  result = g_strndup ((const gchar *) src->sdes[type], src->sdes_len[type]);
+  result = g_strdup (gst_structure_get_string (src->sdes, type_name));
 
   return result;
 }
index 8355bc0..5271d54 100644 (file)
@@ -117,8 +117,7 @@ struct _RTPSource {
   gboolean      is_csrc;
   gboolean      is_sender;
 
-  guint8       *sdes[9];
-  guint         sdes_len[9];
+  GstStructure  *sdes;
 
   gboolean      received_bye;
   gchar        *bye_reason;
@@ -179,16 +178,11 @@ gchar *         rtp_source_get_bye_reason      (RTPSource *src);
 void            rtp_source_update_caps         (RTPSource *src, GstCaps *caps);
 
 /* SDES info */
-gboolean        rtp_source_set_sdes            (RTPSource *src, GstRTCPSDESType type, 
-                                                const guint8 *data, guint len);
 gboolean        rtp_source_set_sdes_string     (RTPSource *src, GstRTCPSDESType type,
                                                 const gchar *data);
-gboolean        rtp_source_get_sdes            (RTPSource *src, GstRTCPSDESType type,
-                                                guint8 **data, guint *len);
 gchar*          rtp_source_get_sdes_string     (RTPSource *src, GstRTCPSDESType type);
-
 GstStructure *  rtp_source_get_sdes_struct     (RTPSource * src);
-void            rtp_source_set_sdes_struct     (RTPSource * src, const GstStructure *sdes);
+gboolean        rtp_source_set_sdes_struct     (RTPSource * src, const GstStructure *sdes);
 
 /* handling network address */
 void            rtp_source_set_rtp_from        (RTPSource *src, GstNetAddress *address);