packet->type == GST_RTCP_TYPE_SR, FALSE);
g_return_val_if_fail (packet->rtcp != NULL, FALSE);
g_return_val_if_fail (packet->rtcp->map.flags & GST_MAP_WRITE, FALSE);
+ /* if profile-specific extension is added, fail for now!? */
+ g_return_val_if_fail (
+ gst_rtcp_packet_get_profile_specific_ext_length (packet) == 0, FALSE);
if (packet->count >= GST_RTCP_MAX_RB_COUNT)
goto no_space;
/**
+ * gst_rtcp_packet_set_profile_specific_ext:
+ * @packet: a valid SR or RR #GstRTCPPacket
+ * @data: (array length=len) (transfer none): profile-specific data
+ * @len: length of the profile-specific data in bytes
+ *
+ * Add profile-specific extension @data to @packet. If @packet already
+ * contains profile-specific extension @data will be appended to the existing
+ * extension.
+ *
+ * Returns: %TRUE if the profile specific extension data was added.
+ */
+gboolean
+gst_rtcp_packet_add_profile_specific_ext (GstRTCPPacket * packet,
+ const guint8 * data, guint len)
+{
+ guint8 *bdata;
+ guint maxsize, offset;
+
+ g_return_val_if_fail (packet != NULL, FALSE);
+ g_return_val_if_fail (packet->type == GST_RTCP_TYPE_RR ||
+ packet->type == GST_RTCP_TYPE_SR, FALSE);
+ g_return_val_if_fail (packet->rtcp != NULL, FALSE);
+ g_return_val_if_fail (packet->rtcp->map.flags & GST_MAP_WRITE, FALSE);
+ g_return_val_if_fail ((len & 0x03) == 0, FALSE);
+
+ bdata = packet->rtcp->map.data;
+ maxsize = packet->rtcp->map.maxsize;
+
+ /* skip to the end of the packet */
+ offset = packet->offset + (packet->length << 2) + 4;
+
+ /* we need 'len' free bytes now */
+ if (G_UNLIKELY (offset + len > maxsize))
+ return FALSE;
+
+ memcpy (&bdata[offset], data, len);
+ packet->length += len >> 2;
+ bdata[packet->offset + 2] = (packet->length) >> 8;
+ bdata[packet->offset + 3] = (packet->length) & 0xff;
+ packet->rtcp->map.size += len;
+
+ return TRUE;
+}
+
+/**
+ * gst_rtcp_packet_get_profile_specific_ext_length:
+ * @packet: a valid SR or RR #GstRTCPPacket
+ *
+ * Returns: The number of 32-bit words containing profile-specific extension
+ * data from @packet.
+ */
+guint16
+gst_rtcp_packet_get_profile_specific_ext_length (GstRTCPPacket * packet)
+{
+ guint pse_offset = 2;
+
+ g_return_val_if_fail (packet != NULL, 0);
+ g_return_val_if_fail (packet->type == GST_RTCP_TYPE_RR ||
+ packet->type == GST_RTCP_TYPE_SR, 0);
+ g_return_val_if_fail (packet->rtcp != NULL, 0);
+ g_return_val_if_fail (packet->rtcp->map.flags & GST_MAP_READ, 0);
+
+ if (packet->type == GST_RTCP_TYPE_SR)
+ pse_offset += 5;
+ pse_offset += (packet->count * 6);
+
+ if (pse_offset <= (packet->length + 1))
+ return packet->length + 1 - pse_offset;
+
+ /* This means that the packet is invalid! */
+ return 0;
+}
+
+/**
+ * gst_rtcp_packet_get_profile_specific_ext:
+ * @packet: a valid SR or RR #GstRTCPPacket
+ * @data: (out) (array length=len) (transfer none): result profile-specific data
+ * @len: (out): result length of the profile-specific data
+ *
+ * Returns: %TRUE if there was valid data.
+ */
+gboolean
+gst_rtcp_packet_get_profile_specific_ext (GstRTCPPacket * packet,
+ guint8 ** data, guint * len)
+{
+ guint16 pse_len;
+
+ g_return_val_if_fail (packet != NULL, FALSE);
+ g_return_val_if_fail (packet->type == GST_RTCP_TYPE_RR ||
+ packet->type == GST_RTCP_TYPE_SR, FALSE);
+ g_return_val_if_fail (packet->rtcp != NULL, FALSE);
+ g_return_val_if_fail (packet->rtcp->map.flags & GST_MAP_READ, FALSE);
+
+ pse_len = gst_rtcp_packet_get_profile_specific_ext_length (packet);
+ if (pse_len > 0) {
+ if (len != NULL)
+ *len = pse_len * sizeof (guint32);
+ if (data != NULL) {
+ *data = packet->rtcp->map.data;
+ *data += packet->offset;
+ *data += ((packet->length + 1 - pse_len) * sizeof (guint32));
+ }
+
+ return TRUE;
+ }
+
+ return FALSE;
+}
+
+/**
+ * gst_rtcp_packet_copy_profile_specific_ext:
+ * @packet: a valid SR or RR #GstRTCPPacket
+ * @data: (out) (array length=len): result profile-specific data
+ * @len: (out): length of the profile-specific extension data
+ *
+ * The profile-specific extension data is copied into a new allocated
+ * memory area @data. This must be freed with g_free() after usage.
+ *
+ * Returns: %TRUE if there was valid data.
+ */
+gboolean
+gst_rtcp_packet_copy_profile_specific_ext (GstRTCPPacket * packet,
+ guint8 ** data, guint * len)
+{
+ guint16 pse_len;
+
+ g_return_val_if_fail (packet != NULL, FALSE);
+ g_return_val_if_fail (packet->type == GST_RTCP_TYPE_RR ||
+ packet->type == GST_RTCP_TYPE_SR, FALSE);
+ g_return_val_if_fail (packet->rtcp != NULL, FALSE);
+ g_return_val_if_fail (packet->rtcp->map.flags & GST_MAP_READ, FALSE);
+
+ pse_len = gst_rtcp_packet_get_profile_specific_ext_length (packet);
+ if (pse_len > 0) {
+ if (len != NULL)
+ *len = pse_len * sizeof (guint32);
+ if (data != NULL) {
+ guint8 * ptr = packet->rtcp->map.data + packet->offset;
+ ptr += ((packet->length + 1 - pse_len) * sizeof (guint32));
+ *data = g_memdup (ptr, pse_len * sizeof (guint32));
+ }
+
+ return TRUE;
+ }
+
+ return FALSE;
+}
+
+
+/**
* gst_rtcp_packet_sdes_get_item_count:
* @packet: a valid SDES #GstRTCPPacket
*
guint32 exthighestseq, guint32 jitter,
guint32 lsr, guint32 dlsr);
+/* profile-specific extensions for SR and RR */
+gboolean gst_rtcp_packet_add_profile_specific_ext (GstRTCPPacket * packet,
+ const guint8 * data, guint len);
+guint16 gst_rtcp_packet_get_profile_specific_ext_length (GstRTCPPacket * packet);
+gboolean gst_rtcp_packet_get_profile_specific_ext (GstRTCPPacket * packet,
+ guint8 ** data, guint * len);
+gboolean gst_rtcp_packet_copy_profile_specific_ext (GstRTCPPacket * packet,
+ guint8 ** data, guint * len);
+
/* source description packet */
guint gst_rtcp_packet_sdes_get_item_count (GstRTCPPacket *packet);
gboolean gst_rtcp_packet_sdes_first_item (GstRTCPPacket *packet);
GST_END_TEST;
+GST_START_TEST (test_rtcp_buffer_profile_specific_extension)
+{
+ GstBuffer *buf;
+ GstRTCPBuffer rtcp = GST_RTCP_BUFFER_INIT;
+ GstRTCPPacket packet;
+ const guint8 pse[] = { 0x00, 0x11, 0x22, 0x33, 0x44, 0x55, 0x66, 0x77 };
+ const guint8 pse2[] = { 0x01, 0x23, 0x45, 0x67 };
+
+ fail_unless ((buf = gst_rtcp_buffer_new (1400)) != NULL);
+ gst_rtcp_buffer_map (buf, GST_MAP_READWRITE, &rtcp);
+
+ fail_unless (gst_rtcp_buffer_validate (buf) == FALSE);
+ fail_unless (gst_rtcp_buffer_get_first_packet (&rtcp, &packet) == FALSE);
+ fail_unless (gst_rtcp_buffer_get_packet_count (&rtcp) == 0);
+
+ /* add an SR packet with sender info */
+ fail_unless (gst_rtcp_buffer_add_packet (&rtcp, GST_RTCP_TYPE_SR, &packet));
+ gst_rtcp_packet_sr_set_sender_info (&packet, 0x44556677,
+ G_GUINT64_CONSTANT (1), 0x11111111, 101, 123456);
+ fail_unless_equals_int (0,
+ gst_rtcp_packet_get_profile_specific_ext_length (&packet));
+ fail_unless_equals_int (6, gst_rtcp_packet_get_length (&packet));
+
+ /* add profile-specific extension */
+ fail_unless (gst_rtcp_packet_add_profile_specific_ext (&packet,
+ pse, sizeof (pse)));
+ {
+ guint8 *data = NULL;
+ guint len = 0;
+
+ fail_unless_equals_int (8, gst_rtcp_packet_get_length (&packet));
+ fail_unless_equals_int (sizeof (pse) / 4,
+ gst_rtcp_packet_get_profile_specific_ext_length (&packet));
+
+ /* gst_rtcp_packet_get_profile_specific_ext */
+ fail_unless (gst_rtcp_packet_get_profile_specific_ext (&packet, &data, &len));
+ fail_unless_equals_int (sizeof (pse), len);
+ fail_unless (data != NULL);
+ fail_unless_equals_int (0, memcmp (pse, data, sizeof (pse)));
+
+ /* gst_rtcp_packet_copy_profile_specific_ext */
+ fail_unless (gst_rtcp_packet_copy_profile_specific_ext (&packet, &data, &len));
+ fail_unless_equals_int (sizeof (pse), len);
+ fail_unless (data != NULL);
+ fail_unless_equals_int (0, memcmp (pse, data, sizeof (pse)));
+ g_free (data);
+ }
+
+ /* append more profile-specific extension */
+ fail_unless (gst_rtcp_packet_add_profile_specific_ext (&packet,
+ pse2, sizeof (pse2)));
+ {
+ guint8 *data = NULL;
+ guint len = 0;
+ guint concat_len;
+ guint8 *concat_pse;
+
+ /* Expect the second extension to be appended to the first */
+ concat_len = sizeof (pse) + sizeof (pse2);
+ concat_pse = g_malloc (concat_len);
+ memcpy (concat_pse, pse, sizeof (pse));
+ memcpy (concat_pse + sizeof (pse), pse2, sizeof (pse2));
+
+ fail_unless_equals_int (9, gst_rtcp_packet_get_length (&packet));
+ fail_unless_equals_int (concat_len / 4,
+ gst_rtcp_packet_get_profile_specific_ext_length (&packet));
+
+ /* gst_rtcp_packet_get_profile_specific_ext */
+ fail_unless (gst_rtcp_packet_get_profile_specific_ext (&packet, &data, &len));
+ fail_unless_equals_int (concat_len, len);
+ fail_unless (data != NULL);
+ fail_unless_equals_int (0, memcmp (concat_pse, data, len));
+
+ /* gst_rtcp_packet_copy_profile_specific_ext */
+ fail_unless (gst_rtcp_packet_copy_profile_specific_ext (&packet, &data, &len));
+ fail_unless_equals_int (concat_len, len);
+ fail_unless (data != NULL);
+ fail_unless_equals_int (0, memcmp (concat_pse, data, len));
+ g_free (data);
+ g_free (concat_pse);
+ }
+
+ /* close and validate */
+ gst_rtcp_buffer_unmap (&rtcp);
+ fail_unless (gst_rtcp_buffer_validate (buf) == TRUE);
+ gst_buffer_unref (buf);
+}
+GST_END_TEST;
+
GST_START_TEST (test_rtp_ntp64_extension)
{
GstBuffer *buf;
test_rtcp_validate_with_padding_set_in_first_packet);
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_rtp_ntp64_extension);
tcase_add_test (tc_chain, test_rtp_ntp56_extension);
gst_rtcp_packet_bye_get_reason_len
gst_rtcp_packet_bye_get_ssrc_count
gst_rtcp_packet_bye_set_reason
+ gst_rtcp_packet_copy_profile_specific_ext
gst_rtcp_packet_fb_get_fci
gst_rtcp_packet_fb_get_fci_length
gst_rtcp_packet_fb_get_media_ssrc
gst_rtcp_packet_get_count
gst_rtcp_packet_get_length
gst_rtcp_packet_get_padding
+ gst_rtcp_packet_get_profile_specific_ext
+ gst_rtcp_packet_get_profile_specific_ext_len
gst_rtcp_packet_get_rb
gst_rtcp_packet_get_rb_count
gst_rtcp_packet_get_type
gst_rtcp_packet_sdes_get_ssrc
gst_rtcp_packet_sdes_next_entry
gst_rtcp_packet_sdes_next_item
+ gst_rtcp_packet_set_profile_specific_ext
gst_rtcp_packet_set_rb
gst_rtcp_packet_sr_get_sender_info
gst_rtcp_packet_sr_set_sender_info