From d8e9a711a04cee8bbfdf9f2245449a9efe78599d Mon Sep 17 00:00:00 2001 From: Haakon Sporsheim Date: Tue, 29 Jul 2014 15:37:12 +0200 Subject: [PATCH] rtcpbuffer: Add profile-specific extension API. https://bugzilla.gnome.org/show_bug.cgi?id=761950 --- gst-libs/gst/rtp/gstrtcpbuffer.c | 153 +++++++++++++++++++++++++++++++++++++++ gst-libs/gst/rtp/gstrtcpbuffer.h | 9 +++ tests/check/libs/rtp.c | 91 +++++++++++++++++++++++ win32/common/libgstrtp.def | 4 + 4 files changed, 257 insertions(+) diff --git a/gst-libs/gst/rtp/gstrtcpbuffer.c b/gst-libs/gst/rtp/gstrtcpbuffer.c index e5e4ca7..8fb31c3 100644 --- a/gst-libs/gst/rtp/gstrtcpbuffer.c +++ b/gst-libs/gst/rtp/gstrtcpbuffer.c @@ -956,6 +956,9 @@ gst_rtcp_packet_add_rb (GstRTCPPacket * packet, guint32 ssrc, 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; @@ -1040,6 +1043,156 @@ gst_rtcp_packet_set_rb (GstRTCPPacket * packet, guint nth, guint32 ssrc, /** + * 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 * diff --git a/gst-libs/gst/rtp/gstrtcpbuffer.h b/gst-libs/gst/rtp/gstrtcpbuffer.h index 328ecdd..c7cfe6a 100644 --- a/gst-libs/gst/rtp/gstrtcpbuffer.h +++ b/gst-libs/gst/rtp/gstrtcpbuffer.h @@ -279,6 +279,15 @@ void gst_rtcp_packet_set_rb (GstRTCPPacket *packet, gu 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); diff --git a/tests/check/libs/rtp.c b/tests/check/libs/rtp.c index d69357a..d802855 100644 --- a/tests/check/libs/rtp.c +++ b/tests/check/libs/rtp.c @@ -991,6 +991,95 @@ GST_START_TEST (test_rtcp_validate_reduced_with_padding) 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; @@ -1231,6 +1320,8 @@ rtp_suite (void) 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); diff --git a/win32/common/libgstrtp.def b/win32/common/libgstrtp.def index 52f654f..7a25680 100644 --- a/win32/common/libgstrtp.def +++ b/win32/common/libgstrtp.def @@ -20,6 +20,7 @@ EXPORTS 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 @@ -32,6 +33,8 @@ EXPORTS 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 @@ -49,6 +52,7 @@ EXPORTS 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 -- 2.7.4