rtcpbuffer: Add API for APP packets
authorStian Selnes <stian@pexip.com>
Tue, 6 Oct 2015 15:02:03 +0000 (17:02 +0200)
committerSebastian Dröge <sebastian@centricular.com>
Thu, 24 Mar 2016 12:24:11 +0000 (14:24 +0200)
https://bugzilla.gnome.org/show_bug.cgi?id=761944

gst-libs/gst/rtp/gstrtcpbuffer.c
gst-libs/gst/rtp/gstrtcpbuffer.h
tests/check/libs/rtp.c

index 8fb31c3..a75613d 100644 (file)
@@ -2311,3 +2311,232 @@ gst_rtcp_packet_fb_get_fci (GstRTCPPacket * packet)
 
   return data + 12;
 }
+
+/**
+ * gst_rtcp_packet_app_set_subtype:
+ * @packet: a valid APP #GstRTCPPacket
+ * @subtype: subtype of the packet
+ *
+ * Set the subtype field of the APP @packet.
+ *
+ * Since: 1.8
+ **/
+void
+gst_rtcp_packet_app_set_subtype (GstRTCPPacket * packet, guint8 subtype)
+{
+  guint8 *data;
+
+  g_return_if_fail (packet != NULL);
+  g_return_if_fail (packet->type == GST_RTCP_TYPE_APP);
+  g_return_if_fail (packet->rtcp != NULL);
+  g_return_if_fail (packet->rtcp->map.flags & GST_MAP_WRITE);
+
+  data = packet->rtcp->map.data + packet->offset;
+  data[0] = (data[0] & 0xe0) | subtype;
+}
+
+/**
+ * gst_rtcp_packet_app_get_subtype:
+ * @packet: a valid APP #GstRTCPPacket
+ *
+ * Get the subtype field of the APP @packet.
+ *
+ * Returns: The subtype.
+ *
+ * Since: 1.8
+ */
+guint8
+gst_rtcp_packet_app_get_subtype (GstRTCPPacket * packet)
+{
+  guint8 *data;
+
+  g_return_val_if_fail (packet != NULL, 0);
+  g_return_val_if_fail (packet->type == GST_RTCP_TYPE_APP, 0);
+  g_return_val_if_fail (packet->rtcp != NULL, 0);
+  g_return_val_if_fail (packet->rtcp->map.flags & GST_MAP_READ, 0);
+
+  data = packet->rtcp->map.data + packet->offset;
+
+  return data[0] & 0x1f;
+}
+
+/**
+ * gst_rtcp_packet_app_set_ssrc:
+ * @packet: a valid APP #GstRTCPPacket
+ * @ssrc: SSRC/CSRC of the packet
+ *
+ * Set the SSRC/CSRC field of the APP @packet.
+ *
+ * Since: 1.8
+ **/
+void
+gst_rtcp_packet_app_set_ssrc (GstRTCPPacket * packet, guint32 ssrc)
+{
+  guint8 *data;
+
+  g_return_if_fail (packet != NULL);
+  g_return_if_fail (packet->type == GST_RTCP_TYPE_APP);
+  g_return_if_fail (packet->rtcp != NULL);
+  g_return_if_fail (packet->rtcp->map.flags & GST_MAP_WRITE);
+
+  data = packet->rtcp->map.data + packet->offset + 4;
+  GST_WRITE_UINT32_BE (data, ssrc);
+}
+
+/**
+ * gst_rtcp_packet_app_get_ssrc:
+ * @packet: a valid APP #GstRTCPPacket
+ *
+ * Get the SSRC/CSRC field of the APP @packet.
+ *
+ * Returns: The SSRC/CSRC.
+ *
+ * Since: 1.8
+ **/
+guint32
+gst_rtcp_packet_app_get_ssrc (GstRTCPPacket * packet)
+{
+  guint8 *data;
+
+  g_return_val_if_fail (packet != NULL, 0);
+  g_return_val_if_fail (packet->type == GST_RTCP_TYPE_APP, 0);
+  g_return_val_if_fail (packet->rtcp != NULL, 0);
+  g_return_val_if_fail (packet->rtcp->map.flags & GST_MAP_READ, 0);
+
+  data = packet->rtcp->map.data + packet->offset + 4;
+
+  return GST_READ_UINT32_BE (data);
+}
+
+/**
+ * gst_rtcp_packet_app_set_name:
+ * @packet: a valid APP #GstRTCPPacket
+ * @name: 4-byte ASCII name
+ *
+ * Set the name field of the APP @packet.
+ *
+ * Since: 1.8
+ **/
+void
+gst_rtcp_packet_app_set_name (GstRTCPPacket * packet, const gchar * name)
+{
+  guint8 *data;
+
+  g_return_if_fail (packet != NULL);
+  g_return_if_fail (packet->type == GST_RTCP_TYPE_APP);
+  g_return_if_fail (packet->rtcp != NULL);
+  g_return_if_fail (packet->rtcp->map.flags & GST_MAP_WRITE);
+
+  data = packet->rtcp->map.data + packet->offset + 8;
+  memcpy (data, name, 4);
+}
+
+/**
+ * gst_rtcp_packet_app_get_name:
+ * @packet: a valid APP #GstRTCPPacket
+ *
+ * Get the name field of the APP @packet.
+ *
+ * Returns: The 4-byte name field, not zero-terminated.
+ *
+ * Since: 1.8
+ **/
+const gchar *
+gst_rtcp_packet_app_get_name (GstRTCPPacket * packet)
+{
+  g_return_val_if_fail (packet != NULL, NULL);
+  g_return_val_if_fail (packet->type == GST_RTCP_TYPE_APP, NULL);
+  g_return_val_if_fail (packet->rtcp != NULL, NULL);
+  g_return_val_if_fail (packet->rtcp->map.flags & GST_MAP_READ, NULL);
+
+  return (const gchar *)&packet->rtcp->map.data[packet->offset + 8];
+}
+
+/**
+ * gst_rtcp_packet_app_get_data_length:
+ * @packet: a valid APP #GstRTCPPacket
+ *
+ * Get the length of the application-dependent data attached to an APP
+ * @packet.
+ *
+ * Returns: The length of data in 32-bit words.
+ *
+ * Since: 1.8
+ **/
+guint16
+gst_rtcp_packet_app_get_data_length (GstRTCPPacket * packet)
+{
+  guint8 *data;
+
+  g_return_val_if_fail (packet != NULL, 0);
+  g_return_val_if_fail (packet->type == GST_RTCP_TYPE_APP, 0);
+  g_return_val_if_fail (packet->rtcp != NULL, 0);
+  g_return_val_if_fail (packet->rtcp->map.flags & GST_MAP_READ, 0);
+
+  data = packet->rtcp->map.data + packet->offset + 2;
+
+  return GST_READ_UINT16_BE (data) - 2;
+}
+
+/**
+ * gst_rtcp_packet_app_set_data_length:
+ * @packet: a valid APP #GstRTCPPacket
+ * @wordlen: Length of the data in 32-bit words
+ *
+ * Set the length of the application-dependent data attached to an APP
+ * @packet.
+ *
+ * Returns: %TRUE if there was enough space in the packet to add this much
+ * data.
+ *
+ * Since: 1.8
+ */
+gboolean
+gst_rtcp_packet_app_set_data_length (GstRTCPPacket * packet, guint16 wordlen)
+{
+  guint8 *data;
+
+  g_return_val_if_fail (packet != NULL, FALSE);
+  g_return_val_if_fail (packet->type == GST_RTCP_TYPE_APP, FALSE);
+  g_return_val_if_fail (packet->rtcp != NULL, FALSE);
+  g_return_val_if_fail (packet->rtcp->map.flags & GST_MAP_WRITE, FALSE);
+
+  if (packet->rtcp->map.maxsize < packet->offset + ((wordlen + 3) * 4))
+    return FALSE;
+
+  data = packet->rtcp->map.data + packet->offset + 2;
+  wordlen += 2;
+  GST_WRITE_UINT16_BE (data, wordlen);
+
+  packet->rtcp->map.size = packet->offset + ((wordlen + 1) * 4);
+
+  return TRUE;
+}
+
+/**
+ * gst_rtcp_packet_app_get_data:
+ * @packet: a valid APP #GstRTCPPacket
+ *
+ * Get the application-dependent data attached to a RTPFB or PSFB @packet.
+ *
+ * Returns: A pointer to the data
+ *
+ * Since: 1.8
+ */
+guint8 *
+gst_rtcp_packet_app_get_data (GstRTCPPacket * packet)
+{
+  guint8 *data;
+
+  g_return_val_if_fail (packet != NULL, NULL);
+  g_return_val_if_fail (packet->type == GST_RTCP_TYPE_APP, NULL);
+  g_return_val_if_fail (packet->rtcp != NULL, NULL);
+  g_return_val_if_fail (packet->rtcp->map.flags & GST_MAP_READ, NULL);
+
+  data = packet->rtcp->map.data + packet->offset;
+
+  if (GST_READ_UINT16_BE (data + 2) <= 2)
+    return NULL;
+
+  return data + 12;
+}
index c7cfe6a..c28fb8b 100644 (file)
@@ -315,6 +315,17 @@ guint8          gst_rtcp_packet_bye_get_reason_len    (GstRTCPPacket *packet);
 gchar*          gst_rtcp_packet_bye_get_reason        (GstRTCPPacket *packet);
 gboolean        gst_rtcp_packet_bye_set_reason        (GstRTCPPacket *packet, const gchar *reason);
 
+/* app packets */
+void            gst_rtcp_packet_app_set_subtype       (GstRTCPPacket * packet, guint8 subtype);
+guint8          gst_rtcp_packet_app_get_subtype       (GstRTCPPacket * packet);
+void            gst_rtcp_packet_app_set_ssrc          (GstRTCPPacket * packet, guint32 ssrc);
+guint32         gst_rtcp_packet_app_get_ssrc          (GstRTCPPacket * packet);
+void            gst_rtcp_packet_app_set_name          (GstRTCPPacket * packet, const gchar *name);
+const gchar*    gst_rtcp_packet_app_get_name          (GstRTCPPacket * packet);
+guint16         gst_rtcp_packet_app_get_data_length   (GstRTCPPacket * packet);
+gboolean        gst_rtcp_packet_app_set_data_length   (GstRTCPPacket * packet, guint16 wordlen);
+guint8*         gst_rtcp_packet_app_get_data          (GstRTCPPacket * packet);
+
 /* feedback packets */
 guint32         gst_rtcp_packet_fb_get_sender_ssrc    (GstRTCPPacket *packet);
 void            gst_rtcp_packet_fb_set_sender_ssrc    (GstRTCPPacket *packet, guint32 ssrc);
index d802855..013bccf 100644 (file)
@@ -1080,6 +1080,56 @@ GST_START_TEST (test_rtcp_buffer_profile_specific_extension)
 }
 GST_END_TEST;
 
+GST_START_TEST (test_rtcp_buffer_app)
+{
+  GstBuffer *buf;
+  GstRTCPBuffer rtcp = GST_RTCP_BUFFER_INIT;
+  GstRTCPPacket packet;
+  guint mtu = 1000;
+  const guint8 data[] = { 0x00, 0x11, 0x22, 0x33, 0x44, 0x55, 0x66, 0x77 };
+  guint max_data_length = (mtu - 12) / 4;
+  guint8 *data_ptr;
+
+  fail_unless ((buf = gst_rtcp_buffer_new (mtu)) != NULL);
+  gst_rtcp_buffer_map (buf, GST_MAP_READWRITE, &rtcp);
+
+  /* Not a valid packet yet */
+  fail_if (gst_rtcp_buffer_validate (buf));
+  fail_if (gst_rtcp_buffer_get_first_packet (&rtcp, &packet));
+  fail_unless_equals_int (gst_rtcp_buffer_get_packet_count (&rtcp), 0);
+
+  /* Add APP packet  */
+  fail_unless (gst_rtcp_buffer_add_packet (&rtcp, GST_RTCP_TYPE_APP, &packet));
+  gst_rtcp_packet_app_set_subtype (&packet, 0x15);
+  gst_rtcp_packet_app_set_ssrc (&packet, 0x01234567);
+  gst_rtcp_packet_app_set_name (&packet, "Test");
+
+  /* Check maximum allowed data */
+  fail_if (gst_rtcp_packet_app_set_data_length (&packet, max_data_length + 1));
+  fail_unless (gst_rtcp_packet_app_set_data_length (&packet, max_data_length));
+
+  /* Add data */
+  fail_unless (gst_rtcp_packet_app_set_data_length (&packet, (sizeof (data) + 3) / 4));
+  fail_unless_equals_int (gst_rtcp_packet_app_get_data_length (&packet), 2);
+  fail_unless ((data_ptr = gst_rtcp_packet_app_get_data (&packet)));
+  memcpy (data_ptr, data, sizeof (data));
+
+  gst_rtcp_buffer_unmap (&rtcp);
+
+  /* Map again with only the READ flag and check fields */
+  gst_rtcp_buffer_map (buf, GST_MAP_READ, &rtcp);
+  fail_unless_equals_int (gst_rtcp_packet_app_get_subtype (&packet), 0x15);
+  fail_unless_equals_int (gst_rtcp_packet_app_get_ssrc (&packet), 0x01234567);
+  fail_unless (memcmp (gst_rtcp_packet_app_get_name (&packet), "Test", 4) == 0);
+  fail_unless_equals_int (gst_rtcp_packet_app_get_data_length (&packet), 2);
+  fail_unless ((data_ptr = gst_rtcp_packet_app_get_data (&packet)));
+  fail_unless (memcmp (data_ptr, data, sizeof (data)) == 0);
+  gst_rtcp_buffer_unmap (&rtcp);
+
+  gst_buffer_unref (buf);
+}
+GST_END_TEST;
+
 GST_START_TEST (test_rtp_ntp64_extension)
 {
   GstBuffer *buf;
@@ -1321,6 +1371,7 @@ rtp_suite (void)
   tcase_add_test (tc_chain, test_rtcp_validate_reduced_without_padding);
   tcase_add_test (tc_chain, test_rtcp_validate_reduced_with_padding);
   tcase_add_test (tc_chain, test_rtcp_buffer_profile_specific_extension);
+  tcase_add_test (tc_chain, test_rtcp_buffer_app);
 
   tcase_add_test (tc_chain, test_rtp_ntp64_extension);
   tcase_add_test (tc_chain, test_rtp_ntp56_extension);