From 5303e2c32b18822f119fefb5eb9a00c5a33c0c76 Mon Sep 17 00:00:00 2001 From: Justin Kim Date: Wed, 1 Nov 2017 10:54:06 +0900 Subject: [PATCH] rtcpbuffer: add support XR packet parsing According to RFC3611, the extended report blocks in XR packet can have variable length. To visit each block, the iterator should look into block header. Once XR type is extracted, users can parse the detailed information by given functions. Loss/Duplicate RLE The Loss RLE and the Duplicate RLE have same format so they can share parsers. For unit test, randomly generated pseudo packet is used. Packet Receipt Times The packet receipt times report block has a list of receipt times which are in [begin_seq, end_seq). Receiver Reference Time paser for XR packet The receiver reference time has ntptime which is 64 bit type. DLRR The DLRR report block consists of sub-blocks which has ssrc, last RR, and delay since last RR. The number of sub-blocks should be calculated from block length. Statistics Summary The Statistics Summary report block provides fixed length information. VoIP Metrics VoIP Metrics consists of several metrics even though they are in a report block. Data retrieving functions are added per metrics. https://bugzilla.gnome.org/show_bug.cgi?id=789822 --- docs/libs/gst-plugins-base-libs-sections.txt | 32 + gst-libs/gst/rtp/gstrtcpbuffer.c | 1014 +++++++++++++++++- gst-libs/gst/rtp/gstrtcpbuffer.h | 129 ++- tests/check/libs/rtp.c | 468 ++++++++ 4 files changed, 1641 insertions(+), 2 deletions(-) diff --git a/docs/libs/gst-plugins-base-libs-sections.txt b/docs/libs/gst-plugins-base-libs-sections.txt index 1e5a9538d5..64cb596a94 100644 --- a/docs/libs/gst-plugins-base-libs-sections.txt +++ b/docs/libs/gst-plugins-base-libs-sections.txt @@ -1457,6 +1457,7 @@ GstRTCPType GstRTCPPacket GstRTCPSDESType GstRTCPFBType +GstRTCPXRType gst_rtcp_buffer_new_take_data gst_rtcp_buffer_new_copy_data @@ -1550,6 +1551,36 @@ gst_rtcp_unix_to_ntp gst_rtcp_sdes_name_to_type gst_rtcp_sdes_type_to_name +gst_rtcp_packet_xr_first_rb +gst_rtcp_packet_xr_next_rb +gst_rtcp_packet_xr_get_ssrc +gst_rtcp_packet_xr_get_block_length +gst_rtcp_packet_xr_get_block_type + +gst_rtcp_packet_xr_get_dlrr_block + +gst_rtcp_packet_xr_get_prt_by_seq +gst_rtcp_packet_xr_get_prt_info + +gst_rtcp_packet_xr_get_rle_info +gst_rtcp_packet_xr_get_rle_nth_chunk + +gst_rtcp_packet_xr_get_rrt + +gst_rtcp_packet_xr_get_summary_info +gst_rtcp_packet_xr_get_summary_jitter +gst_rtcp_packet_xr_get_summary_pkt +gst_rtcp_packet_xr_get_summary_ttl + +gst_rtcp_packet_xr_get_voip_burst_metrics +gst_rtcp_packet_xr_get_voip_configuration_params +gst_rtcp_packet_xr_get_voip_delay_metrics +gst_rtcp_packet_xr_get_voip_jitter_buffer_params +gst_rtcp_packet_xr_get_voip_metrics_ssrc +gst_rtcp_packet_xr_get_voip_packet_metrics +gst_rtcp_packet_xr_get_voip_quality_metrics +gst_rtcp_packet_xr_get_voip_signal_metrics + GST_RTCP_RTPFB_TYPE_RCTP_SR_REQ GST_TYPE_RTCPFB_TYPE @@ -1558,6 +1589,7 @@ GST_TYPE_RTCP_TYPE gst_rtcp_type_get_type gst_rtcpfb_type_get_type gst_rtcpsdes_type_get_type +gst_rtcpxr_type_get_type
diff --git a/gst-libs/gst/rtp/gstrtcpbuffer.c b/gst-libs/gst/rtp/gstrtcpbuffer.c index ce0b91f829..e1944e2763 100644 --- a/gst-libs/gst/rtp/gstrtcpbuffer.c +++ b/gst-libs/gst/rtp/gstrtcpbuffer.c @@ -550,7 +550,7 @@ gst_rtcp_buffer_add_packet (GstRTCPBuffer * rtcp, GstRTCPType type, len = 12; break; case GST_RTCP_TYPE_XR: - len = 4; + len = 8; break; default: goto unknown_type; @@ -2544,3 +2544,1015 @@ gst_rtcp_packet_app_get_data (GstRTCPPacket * packet) return data + 12; } + +/** + * gst_rtcp_packet_xr_get_ssrc: + * @packet: a valid XR #GstRTCPPacket + * + * Get the ssrc field of the XR @packet. + * + * Returns: the ssrc. + * + * Since: 1.16 + */ +guint32 +gst_rtcp_packet_xr_get_ssrc (GstRTCPPacket * packet) +{ + guint8 *data; + guint32 ssrc; + + g_return_val_if_fail (packet != NULL, 0); + g_return_val_if_fail (packet->type == GST_RTCP_TYPE_XR, 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; + + /* skip header */ + data += packet->offset + 4; + ssrc = GST_READ_UINT32_BE (data); + + return ssrc; +} + +/** + * gst_rtcp_packet_xr_first_rb: + * @packet: a valid XR #GstRTCPPacket + * + * Move to the first extended report block in XR @packet. + * + * Returns: TRUE if there was a first extended report block. + * + * Since: 1.16 + */ +gboolean +gst_rtcp_packet_xr_first_rb (GstRTCPPacket * packet) +{ + guint16 block_len; + guint offset, len; + + g_return_val_if_fail (packet != NULL, FALSE); + g_return_val_if_fail (packet->type == GST_RTCP_TYPE_XR, FALSE); + + if (packet->length < 2) + return FALSE; + + /* skip header + ssrc */ + packet->item_offset = 8; + + /* Validate the block's length */ + block_len = gst_rtcp_packet_xr_get_block_length (packet); + offset = 8 + (block_len * 1) + 4; + + len = packet->length << 2; + + if (offset >= len) { + packet->item_offset = 0; + return FALSE; + } + + return TRUE; +} + +/** + * gst_rtcp_packet_xr_next_rb: + * @packet: a valid XR #GstRTCPPacket + * + * Move to the next extended report block in XR @packet. + * + * Returns: TRUE if there was a next extended report block. + * + * Since: 1.16 + */ +gboolean +gst_rtcp_packet_xr_next_rb (GstRTCPPacket * packet) +{ + guint16 block_len; + guint offset; + guint len; + + g_return_val_if_fail (packet != NULL, FALSE); + g_return_val_if_fail (packet->type == GST_RTCP_TYPE_XR, FALSE); + g_return_val_if_fail (packet->rtcp != NULL, FALSE); + g_return_val_if_fail (packet->rtcp->map.flags & GST_MAP_READ, FALSE); + + block_len = gst_rtcp_packet_xr_get_block_length (packet); + + offset = packet->item_offset; + offset += (block_len + 1) * 4; + + /* don't overrun */ + len = (packet->length << 2); + + if (offset >= len) + return FALSE; + + packet->item_offset = offset; + + return TRUE; +} + +/** + * gst_rtcp_packet_xr_get_block_type: + * @packet: a valid XR #GstRTCPPacket + * + * Get the extended report block type of the XR @packet. + * + * Returns: The extended report block type. + * + * Since: 1.16 + */ +GstRTCPXRType +gst_rtcp_packet_xr_get_block_type (GstRTCPPacket * packet) +{ + guint8 *data; + guint8 type; + GstRTCPXRType xr_type = GST_RTCP_XR_TYPE_INVALID; + + g_return_val_if_fail (packet != NULL, GST_RTCP_XR_TYPE_INVALID); + g_return_val_if_fail (packet->type == GST_RTCP_TYPE_XR, + GST_RTCP_XR_TYPE_INVALID); + g_return_val_if_fail (packet->rtcp != NULL, GST_RTCP_XR_TYPE_INVALID); + g_return_val_if_fail (packet->rtcp->map.flags & GST_MAP_READ, + GST_RTCP_XR_TYPE_INVALID); + g_return_val_if_fail (packet->length >= (packet->item_offset >> 2), + GST_RTCP_XR_TYPE_INVALID); + + data = packet->rtcp->map.data; + + /* skip header + current item offset */ + data += packet->offset + packet->item_offset; + + /* XR block type can be defined more than described in RFC3611. + * If undefined type is detected, user might want to know. */ + type = GST_READ_UINT8 (data); + switch (type) { + case GST_RTCP_XR_TYPE_LRLE: + case GST_RTCP_XR_TYPE_DRLE: + case GST_RTCP_XR_TYPE_PRT: + case GST_RTCP_XR_TYPE_RRT: + case GST_RTCP_XR_TYPE_DLRR: + case GST_RTCP_XR_TYPE_SSUMM: + case GST_RTCP_XR_TYPE_VOIP_METRICS: + xr_type = type; + break; + default: + GST_DEBUG ("got 0x%x type, but that might be out of scope of RFC3611", + type); + break; + } + + return xr_type; +} + +/** + * gst_rtcp_packet_xr_get_block_length: + * @packet: a valid XR #GstRTCPPacket + * + * Returns: The number of 32-bit words containing type-specific block + * data from @packet. + * + * Since: 1.16 + */ +guint16 +gst_rtcp_packet_xr_get_block_length (GstRTCPPacket * packet) +{ + guint8 *data; + guint16 len; + + g_return_val_if_fail (packet != NULL, 0); + g_return_val_if_fail (packet->type == GST_RTCP_TYPE_XR, 0); + g_return_val_if_fail (packet->rtcp != NULL, 0); + g_return_val_if_fail (packet->rtcp->map.flags & GST_MAP_READ, 0); + g_return_val_if_fail (packet->length >= (packet->item_offset >> 2), 0); + + data = packet->rtcp->map.data; + data += packet->offset + packet->item_offset + 2; + + len = GST_READ_UINT16_BE (data); + + return len; +} + +/** + * gst_rtcp_packet_xr_get_rle_info: + * @packet: a valid XR #GstRTCPPacket which is Loss RLE or Duplicate RLE report. + * @ssrc: the SSRC of the RTP data packet source being reported upon by this report block. + * @thining: the amount of thinning performed on the sequence number space. + * @begin_seq: the first sequence number that this block reports on. + * @end_seq: the last sequence number that this block reports on plus one. + * @chunk_count: the number of chunks calculated by block length. + * + * Parse the extended report block for Loss RLE and Duplicated LRE block type. + * + * Returns: %TRUE if the report block is correctly parsed. + * + * Since: 1.16 + */ +gboolean +gst_rtcp_packet_xr_get_rle_info (GstRTCPPacket * packet, guint32 * ssrc, + guint8 * thining, guint16 * begin_seq, guint16 * end_seq, + guint32 * chunk_count) +{ + guint8 *data; + guint16 block_len; + + g_return_val_if_fail (gst_rtcp_packet_xr_get_block_type (packet) == + GST_RTCP_XR_TYPE_LRLE + || gst_rtcp_packet_xr_get_block_type (packet) == GST_RTCP_XR_TYPE_DRLE, + FALSE); + + block_len = gst_rtcp_packet_xr_get_block_length (packet); + if (block_len < 3) + return FALSE; + + if (chunk_count) + *chunk_count = (block_len - 2) * 2; + + data = packet->rtcp->map.data; + /* skip header + current item offset */ + data += packet->offset + packet->item_offset; + + if (thining) + *thining = data[1] & 0x0f; + + /* go to ssrc */ + data += 4; + if (ssrc) + *ssrc = GST_READ_UINT32_BE (data); + /* go to begin_seq */ + data += 4; + if (begin_seq) + *begin_seq = ((data[0] << 8) | data[1]); + /* go to end_seq */ + data += 2; + if (end_seq) + *end_seq = ((data[0] << 8) | data[1]); + + return TRUE; +} + +/** + * gst_rtcp_packet_xr_get_rle_nth_chunk: + * @packet: a valid XR #GstRTCPPacket which is Loss RLE or Duplicate RLE report. + * @nth: the index of chunk to retrieve. + * @chunk: the @nth chunk. + * + * Retrieve actual chunk data. + * + * Returns: %TRUE if the report block returns chunk correctly. + * + * Since: 1.16 + */ +gboolean +gst_rtcp_packet_xr_get_rle_nth_chunk (GstRTCPPacket * packet, + guint nth, guint16 * chunk) +{ + guint32 chunk_count; + guint8 *data; + + g_return_val_if_fail (gst_rtcp_packet_xr_get_rle_info (packet, NULL, NULL, + NULL, NULL, &chunk_count), FALSE); + + if (nth >= chunk_count) + return FALSE; + + data = packet->rtcp->map.data; + /* skip header + current item offset */ + data += packet->offset + packet->item_offset; + + /* skip ssrc, {begin,end}_seq */ + data += 12; + + /* goto nth chunk */ + data += nth * 2; + if (chunk) + *chunk = ((data[0] << 8) | data[1]); + + return TRUE; +} + +/** + * gst_rtcp_packet_xr_get_prt_info: + * @packet: a valid XR #GstRTCPPacket which has a Packet Receipt Times Report Block + * @ssrc: the SSRC of the RTP data packet source being reported upon by this report block. + * @thining: the amount of thinning performed on the sequence number space. + * @begin_seq: the first sequence number that this block reports on. + * @end_seq: the last sequence number that this block reports on plus one. + * + * Parse the Packet Recept Times Report Block from a XR @packet + * + * Returns: %TRUE if the report block is correctly parsed. + * + * Since: 1.16 + */ +gboolean +gst_rtcp_packet_xr_get_prt_info (GstRTCPPacket * packet, + guint32 * ssrc, guint8 * thining, guint16 * begin_seq, guint16 * end_seq) +{ + guint8 *data; + guint16 block_len; + + g_return_val_if_fail (gst_rtcp_packet_xr_get_block_type (packet) == + GST_RTCP_XR_TYPE_PRT, FALSE); + + block_len = gst_rtcp_packet_xr_get_block_length (packet); + if (block_len < 3) + return FALSE; + + data = packet->rtcp->map.data; + /* skip header + current item offset */ + data += packet->offset + packet->item_offset; + + if (thining) + *thining = data[1] & 0x0f; + + /* go to ssrc */ + data += 4; + if (ssrc) + *ssrc = GST_READ_UINT32_BE (data); + + /* go to begin_seq */ + data += 4; + if (begin_seq) + *begin_seq = ((data[0] << 8) | data[1]); + /* go to end_seq */ + data += 2; + if (end_seq) + *end_seq = ((data[0] << 8) | data[1]); + + if (block_len < (end_seq - begin_seq) + 2) + return FALSE; + + return TRUE; +} + +/** + * gst_rtcp_packet_xr_get_prt_by_seq: + * @packet: a valid XR #GstRTCPPacket which has the Packet Recept Times Report Block. + * @seq: the sequence to retrieve the time. + * @receipt_time: the packet receipt time of @seq. + * + * Retrieve the packet receipt time of @seq which ranges in [begin_seq, end_seq). + * + * Returns: %TRUE if the report block returns the receipt time correctly. + * + * Since: 1.16 + */ +gboolean +gst_rtcp_packet_xr_get_prt_by_seq (GstRTCPPacket * packet, + guint16 seq, guint32 * receipt_time) +{ + guint16 begin_seq, end_seq; + guint8 *data; + + g_return_val_if_fail (gst_rtcp_packet_xr_get_prt_info (packet, NULL, NULL, + &begin_seq, &end_seq), FALSE); + + if (seq >= end_seq || seq < begin_seq) + return FALSE; + + data = packet->rtcp->map.data; + /* skip header + current item offset */ + data += packet->offset + packet->item_offset; + + /* skip ssrc, {begin,end}_seq */ + data += 12; + + data += (seq - begin_seq) * 4; + + if (receipt_time) + *receipt_time = GST_READ_UINT32_BE (data); + + return TRUE; +} + +/** + * gst_rtcp_packet_xr_get_rrt: + * @packet: a valid XR #GstRTCPPacket which has the Receiver Reference Time. + * @timestamp: NTP timestamp + * + * Returns: %TRUE if the report block returns the reference time correctly. + * + * Since: 1.16 + */ +gboolean +gst_rtcp_packet_xr_get_rrt (GstRTCPPacket * packet, guint64 * timestamp) +{ + guint8 *data; + + g_return_val_if_fail (gst_rtcp_packet_xr_get_block_type (packet) == + GST_RTCP_XR_TYPE_RRT, FALSE); + + if (gst_rtcp_packet_xr_get_block_length (packet) != 2) + return FALSE; + + data = packet->rtcp->map.data; + /* skip header + current item offset */ + data += packet->offset + packet->item_offset; + + /* skip block header */ + data += 4; + if (timestamp) + *timestamp = GST_READ_UINT64_BE (data); + + return TRUE; +} + +/** + * gst_rtcp_packet_xr_get_dlrr_block: + * @packet: a valid XR #GstRTCPPacket which has DLRR Report Block. + * @nth: the index of sub-block to retrieve. + * @ssrc: the SSRC of the receiver. + * @last_rr: the last receiver reference timestamp of @ssrc. + * @delay: the delay since @last_rr. + * + * Parse the extended report block for DLRR report block type. + * + * Returns: %TRUE if the report block is correctly parsed. + * + * Since: 1.16 + */ +gboolean +gst_rtcp_packet_xr_get_dlrr_block (GstRTCPPacket * packet, + guint nth, guint32 * ssrc, guint32 * last_rr, guint32 * delay) +{ + guint8 *data; + guint16 block_len; + + g_return_val_if_fail (gst_rtcp_packet_xr_get_block_type (packet) == + GST_RTCP_XR_TYPE_DLRR, FALSE); + + block_len = gst_rtcp_packet_xr_get_block_length (packet); + + if (nth * 3 >= block_len) + return FALSE; + + data = packet->rtcp->map.data; + /* skip header + current item offset */ + data += packet->offset + packet->item_offset; + /* skip block header */ + data += 4; + data += nth * 3 * 4; + + if (ssrc) + *ssrc = GST_READ_UINT32_BE (data); + + data += 4; + if (last_rr) + *last_rr = GST_READ_UINT32_BE (data); + + data += 4; + if (delay) + *delay = GST_READ_UINT32_BE (data); + + return TRUE; +} + +/** + * gst_rtcp_packet_xr_get_summary_info: + * @packet: a valid XR #GstRTCPPacket which has Statics Summary Report Block. + * @ssrc: the SSRC of the source. + * @begin_seq: the first sequence number that this block reports on. + * @end_seq: the last sequence number that this block reports on plus one. + * + * Extract a basic information from static summary report block of XR @packet. + * + * Returns: %TRUE if the report block is correctly parsed. + * + * Since: 1.16 + */ +gboolean +gst_rtcp_packet_xr_get_summary_info (GstRTCPPacket * packet, guint32 * ssrc, + guint16 * begin_seq, guint16 * end_seq) +{ + guint8 *data; + + g_return_val_if_fail (gst_rtcp_packet_xr_get_block_type (packet) == + GST_RTCP_XR_TYPE_SSUMM, FALSE); + + if (gst_rtcp_packet_xr_get_block_length (packet) != 9) + return FALSE; + + data = packet->rtcp->map.data; + /* skip header + current item offset */ + data += packet->offset + packet->item_offset; + /* skip block header */ + data += 4; + + if (ssrc) + *ssrc = GST_READ_UINT32_BE (data); + + /* go to begin_seq */ + data += 4; + if (begin_seq) + *begin_seq = ((data[0] << 8) | data[1]); + /* go to end_seq */ + data += 2; + if (end_seq) + *end_seq = ((data[0] << 8) | data[1]); + + return TRUE; +} + +/** + * gst_rtcp_packet_xr_get_summary_pkt: + * @packet: a valid XR #GstRTCPPacket which has Statics Summary Report Block. + * @lost_packets: the number of lost packets between begin_seq and end_seq. + * @dup_packets: the number of duplicate packets between begin_seq and end_seq. + * + * Get the number of lost or duplicate packets. If the flag in a block header + * is set as zero, @lost_packets or @dup_packets will be zero. + * + * Returns: %TRUE if the report block is correctly parsed. + * + * Since: 1.16 + */ +gboolean +gst_rtcp_packet_xr_get_summary_pkt (GstRTCPPacket * packet, + guint32 * lost_packets, guint32 * dup_packets) +{ + guint8 *data; + guint8 flags; + + g_return_val_if_fail (gst_rtcp_packet_xr_get_block_type (packet) == + GST_RTCP_XR_TYPE_SSUMM, FALSE); + if (gst_rtcp_packet_xr_get_block_length (packet) != 9) + return FALSE; + + data = packet->rtcp->map.data; + /* skip header + current item offset */ + data += packet->offset + packet->item_offset; + flags = data[1]; + /* skip block header,ssrc, {begin,end}_seq */ + data += 12; + + if (lost_packets) { + if (!(flags & 0x80)) + *lost_packets = 0; + else + *lost_packets = GST_READ_UINT32_BE (data); + } + + data += 4; + if (dup_packets) { + if (!(flags & 0x40)) + *dup_packets = 0; + else + *dup_packets = GST_READ_UINT32_BE (data); + } + + return TRUE; +} + +/** + * gst_rtcp_packet_xr_get_summary_jitter: + * @packet: a valid XR #GstRTCPPacket which has Statics Summary Report Block. + * @min_jitter: the minimum relative transit time between two sequences. + * @max_jitter: the maximum relative transit time between two sequences. + * @mean_jitter: the mean relative transit time between two sequences. + * @dev_jitter: the standard deviation of the relative transit time between two sequences. + * + * Extract jitter information from the statistics summary. If the jitter flag in + * a block header is set as zero, all of jitters will be zero. + * + * Returns: %TRUE if the report block is correctly parsed. + * + * Since: 1.16 + */ +gboolean +gst_rtcp_packet_xr_get_summary_jitter (GstRTCPPacket * packet, + guint32 * min_jitter, guint32 * max_jitter, + guint32 * mean_jitter, guint32 * dev_jitter) +{ + guint8 *data; + guint8 flags; + + g_return_val_if_fail (gst_rtcp_packet_xr_get_block_type (packet) == + GST_RTCP_XR_TYPE_SSUMM, FALSE); + + if (gst_rtcp_packet_xr_get_block_length (packet) != 9) + return FALSE; + + data = packet->rtcp->map.data; + /* skip header + current item offset */ + data += packet->offset + packet->item_offset; + flags = data[1]; + + if (!(flags & 0x20)) { + if (min_jitter) + *min_jitter = 0; + if (max_jitter) + *max_jitter = 0; + if (mean_jitter) + *mean_jitter = 0; + if (dev_jitter) + *dev_jitter = 0; + + return TRUE; + } + + /* skip block header,ssrc, {begin,end}_seq, packets */ + data += 20; + if (min_jitter) + *min_jitter = GST_READ_UINT32_BE (data); + + data += 4; + if (max_jitter) + *max_jitter = GST_READ_UINT32_BE (data); + + data += 4; + if (mean_jitter) + *mean_jitter = GST_READ_UINT32_BE (data); + + data += 4; + if (dev_jitter) + *dev_jitter = GST_READ_UINT32_BE (data); + + return TRUE; +} + +/** + * gst_rtcp_packet_xr_get_summary_ttl: + * @packet: a valid XR #GstRTCPPacket which has Statics Summary Report Block. + * @is_ipv4: the flag to indicate that the return values are ipv4 ttl or ipv6 hop limits. + * @min_ttl: the minimum TTL or Hop Limit value of data packets between two sequences. + * @max_ttl: the maximum TTL or Hop Limit value of data packets between two sequences. + * @mean_ttl: the mean TTL or Hop Limit value of data packets between two sequences. + * @dev_ttl: the standard deviation of the TTL or Hop Limit value of data packets between two sequences. + * + * Extract the value of ttl for ipv4, or hop limit for ipv6. + * + * Returns: %TRUE if the report block is correctly parsed. + * + * Since: 1.16 + */ +gboolean +gst_rtcp_packet_xr_get_summary_ttl (GstRTCPPacket * packet, + gboolean * is_ipv4, guint8 * min_ttl, guint8 * max_ttl, guint8 * mean_ttl, + guint8 * dev_ttl) +{ + guint8 *data; + guint8 flags; + + g_return_val_if_fail (gst_rtcp_packet_xr_get_block_type (packet) == + GST_RTCP_XR_TYPE_SSUMM, FALSE); + + if (gst_rtcp_packet_xr_get_block_length (packet) != 9) + return FALSE; + + data = packet->rtcp->map.data; + /* skip header + current item offset */ + data += packet->offset + packet->item_offset; + flags = (data[1] & 0x18) >> 3; + + if (flags > 2) + return FALSE; + + if (is_ipv4) + *is_ipv4 = (flags == 1); + + /* skip block header,ssrc, {begin,end}_seq, packets, jitters */ + data += 36; + if (min_ttl) + *min_ttl = data[0]; + + if (max_ttl) + *max_ttl = data[1]; + + if (mean_ttl) + *mean_ttl = data[2]; + + if (dev_ttl) + *dev_ttl = data[3]; + + return TRUE; +} + +/** + * gst_rtcp_packet_xr_get_voip_metrics_ssrc: + * @packet: a valid XR #GstRTCPPacket which has VoIP Metrics Report Block. + * @ssrc: the SSRC of source + * + * Returns: %TRUE if the report block is correctly parsed. + * + * Since: 1.16 + */ +gboolean +gst_rtcp_packet_xr_get_voip_metrics_ssrc (GstRTCPPacket * packet, + guint32 * ssrc) +{ + guint8 *data; + + g_return_val_if_fail (gst_rtcp_packet_xr_get_block_type (packet) == + GST_RTCP_XR_TYPE_VOIP_METRICS, FALSE); + + if (gst_rtcp_packet_xr_get_block_length (packet) != 8) + return FALSE; + + data = packet->rtcp->map.data; + /* skip header + current item offset */ + data += packet->offset + packet->item_offset; + + /* skip block header */ + data += 4; + if (ssrc) + *ssrc = GST_READ_UINT32_BE (data); + + return TRUE; +} + +/** + * gst_rtcp_packet_xr_get_voip_packet_metrics: + * @packet: a valid XR #GstRTCPPacket which has VoIP Metrics Report Block. + * @loss_rate: the fraction of RTP data packets from the source lost. + * @discard_rate: the fraction of RTP data packets from the source that have been discarded. + * + * Returns: %TRUE if the report block is correctly parsed. + * + * Since: 1.16 + */ +gboolean +gst_rtcp_packet_xr_get_voip_packet_metrics (GstRTCPPacket * packet, + guint8 * loss_rate, guint8 * discard_rate) +{ + guint8 *data; + + g_return_val_if_fail (gst_rtcp_packet_xr_get_block_type (packet) == + GST_RTCP_XR_TYPE_VOIP_METRICS, FALSE); + + if (gst_rtcp_packet_xr_get_block_length (packet) != 8) + return FALSE; + + data = packet->rtcp->map.data; + /* skip header + current item offset */ + data += packet->offset + packet->item_offset; + + /* skip block header, ssrc */ + data += 8; + if (loss_rate) + *loss_rate = data[0]; + + if (discard_rate) + *discard_rate = data[1]; + + return TRUE; +} + +/** + * gst_rtcp_packet_xr_get_voip_burst_metrics: + * @packet: a valid XR #GstRTCPPacket which has VoIP Metrics Report Block. + * @burst_density: the fraction of RTP data packets within burst periods. + * @gap_density: the fraction of RTP data packets within inter-burst gaps. + * @burst_duration: the mean duration(ms) of the burst periods. + * @gap_duration: the mean duration(ms) of the gap periods. + * + * Returns: %TRUE if the report block is correctly parsed. + * + * Since: 1.16 + */ +gboolean +gst_rtcp_packet_xr_get_voip_burst_metrics (GstRTCPPacket * packet, + guint8 * burst_density, guint8 * gap_density, guint16 * burst_duration, + guint16 * gap_duration) +{ + guint8 *data; + + g_return_val_if_fail (gst_rtcp_packet_xr_get_block_type (packet) == + GST_RTCP_XR_TYPE_VOIP_METRICS, FALSE); + + if (gst_rtcp_packet_xr_get_block_length (packet) != 8) + return FALSE; + + data = packet->rtcp->map.data; + /* skip header + current item offset */ + data += packet->offset + packet->item_offset; + + /* skip block header, ssrc, packet metrics */ + data += 10; + if (burst_density) + *burst_density = data[0]; + + if (gap_density) + *gap_density = data[1]; + + data += 2; + if (burst_duration) + *burst_duration = GST_READ_UINT16_BE (data); + + data += 2; + if (gap_duration) + *gap_duration = GST_READ_UINT16_BE (data); + + return TRUE; +} + +/** + * gst_rtcp_packet_xr_get_voip_delay_metrics: + * @packet: a valid XR #GstRTCPPacket which has VoIP Metrics Report Block. + * @roundtrip_delay: the most recently calculated round trip time between RTP interfaces(ms) + * @end_system_delay: the most recently estimated end system delay(ms) + * + * Returns: %TRUE if the report block is correctly parsed. + * + * Since: 1.16 + */ +gboolean +gst_rtcp_packet_xr_get_voip_delay_metrics (GstRTCPPacket * packet, + guint16 * roundtrip_delay, guint16 * end_system_delay) +{ + guint8 *data; + + g_return_val_if_fail (gst_rtcp_packet_xr_get_block_type (packet) == + GST_RTCP_XR_TYPE_VOIP_METRICS, FALSE); + + if (gst_rtcp_packet_xr_get_block_length (packet) != 8) + return FALSE; + + data = packet->rtcp->map.data; + /* skip header + current item offset */ + data += packet->offset + packet->item_offset; + + /* skip block header, ssrc, packet metrics, burst metrics */ + data += 16; + if (roundtrip_delay) + *roundtrip_delay = GST_READ_UINT16_BE (data); + + data += 2; + if (end_system_delay) + *end_system_delay = GST_READ_UINT16_BE (data); + + return TRUE; +} + +/** + * gst_rtcp_packet_xr_get_voip_signal_metrics: + * @packet: a valid XR #GstRTCPPacket which has VoIP Metrics Report Block. + * @signal_level: the ratio of the signal level to a 0 dBm reference. + * @noise_level: the ratio of the silent period background noise level to a 0 dBm reference. + * @rerl: the residual echo return loss value. + * @gmin: the gap threshold. + * + * Returns: %TRUE if the report block is correctly parsed. + * + * Since: 1.16 + */ +gboolean +gst_rtcp_packet_xr_get_voip_signal_metrics (GstRTCPPacket * packet, + guint8 * signal_level, guint8 * noise_level, guint8 * rerl, guint8 * gmin) +{ + guint8 *data; + + g_return_val_if_fail (gst_rtcp_packet_xr_get_block_type (packet) == + GST_RTCP_XR_TYPE_VOIP_METRICS, FALSE); + + if (gst_rtcp_packet_xr_get_block_length (packet) != 8) + return FALSE; + + data = packet->rtcp->map.data; + /* skip header + current item offset */ + data += packet->offset + packet->item_offset; + + /* skip block header, ssrc, packet metrics, burst metrics, + * delay metrics */ + data += 20; + if (signal_level) + *signal_level = data[0]; + + if (noise_level) + *noise_level = data[1]; + + if (rerl) + *rerl = data[2]; + + if (gmin) + *gmin = data[3]; + + return TRUE; +} + +/** + * gst_rtcp_packet_xr_get_voip_quality_metrics: + * @packet: a valid XR #GstRTCPPacket which has VoIP Metrics Report Block. + * @r_factor: the R factor is a voice quality metric describing the segment of the call. + * @ext_r_factor: the external R factor is a voice quality metric. + * @mos_lq: the estimated mean opinion score for listening quality. + * @mos_cq: the estimated mean opinion score for conversational quality. + * + * Returns: %TRUE if the report block is correctly parsed. + * + * Since: 1.16 + */ +gboolean +gst_rtcp_packet_xr_get_voip_quality_metrics (GstRTCPPacket * packet, + guint8 * r_factor, guint8 * ext_r_factor, guint8 * mos_lq, guint8 * mos_cq) +{ + guint8 *data; + + g_return_val_if_fail (gst_rtcp_packet_xr_get_block_type (packet) == + GST_RTCP_XR_TYPE_VOIP_METRICS, FALSE); + + if (gst_rtcp_packet_xr_get_block_length (packet) != 8) + return FALSE; + + data = packet->rtcp->map.data; + /* skip header + current item offset */ + data += packet->offset + packet->item_offset; + + /* skip block header, ssrc, packet metrics, burst metrics, + * delay metrics, signal metrics */ + data += 24; + if (r_factor) + *r_factor = data[0]; + + if (ext_r_factor) + *ext_r_factor = data[1]; + + if (mos_lq) + *mos_lq = data[2]; + + if (mos_cq) + *mos_cq = data[3]; + + return TRUE; +} + +/** + * gst_rtcp_packet_xr_get_voip_configuration_params: + * @packet: a valid XR #GstRTCPPacket which has VoIP Metrics Report Block. + * @gmin: the gap threshold. + * @rx_config: the receiver configuration byte. + * + * Returns: %TRUE if the report block is correctly parsed. + * + * Since: 1.16 + */ +gboolean +gst_rtcp_packet_xr_get_voip_configuration_params (GstRTCPPacket * packet, + guint8 * gmin, guint8 * rx_config) +{ + guint8 *data; + + g_return_val_if_fail (gst_rtcp_packet_xr_get_block_type (packet) == + GST_RTCP_XR_TYPE_VOIP_METRICS, FALSE); + + if (gst_rtcp_packet_xr_get_block_length (packet) != 8) + return FALSE; + + data = packet->rtcp->map.data; + /* skip header + current item offset */ + data += packet->offset + packet->item_offset; + + if (gmin) + *gmin = data[23]; + + if (rx_config) + *rx_config = data[28]; + + return TRUE; +} + +/** + * gst_rtcp_packet_xr_get_voip_jitter_buffer_params: + * @packet: a valid XR #GstRTCPPacket which has VoIP Metrics Report Block. + * @jb_nominal: the current nominal jitter buffer delay(ms) + * @jb_maximum: the current maximum jitter buffer delay(ms) + * @jb_abs_max: the absolute maximum delay(ms) + * + * Returns: %TRUE if the report block is correctly parsed. + * + * Since: 1.16 + */ +gboolean +gst_rtcp_packet_xr_get_voip_jitter_buffer_params (GstRTCPPacket * packet, + guint16 * jb_nominal, guint16 * jb_maximum, guint16 * jb_abs_max) +{ + guint8 *data; + + g_return_val_if_fail (gst_rtcp_packet_xr_get_block_type (packet) == + GST_RTCP_XR_TYPE_VOIP_METRICS, FALSE); + + if (gst_rtcp_packet_xr_get_block_length (packet) != 8) + return FALSE; + + data = packet->rtcp->map.data; + /* skip header + current item offset */ + data += packet->offset + packet->item_offset; + + /* skip block header, ssrc, packet metrics, burst metrics, + * delay metrics, signal metrics, config */ + data += 30; + + if (jb_nominal) + *jb_nominal = GST_READ_UINT16_BE (data); + + data += 2; + if (jb_maximum) + *jb_maximum = GST_READ_UINT16_BE (data); + + data += 2; + if (jb_abs_max) + *jb_abs_max = GST_READ_UINT16_BE (data); + + return TRUE; +} diff --git a/gst-libs/gst/rtp/gstrtcpbuffer.h b/gst-libs/gst/rtp/gstrtcpbuffer.h index b13c212dcb..8b49d8a04a 100644 --- a/gst-libs/gst/rtp/gstrtcpbuffer.h +++ b/gst-libs/gst/rtp/gstrtcpbuffer.h @@ -137,6 +137,34 @@ typedef enum GST_RTCP_SDES_PRIV = 8 } GstRTCPSDESType; +/** + * GstRTCPXRType: + * @GST_RTCP_XR_TYPE_INVALID: Invalid XR Report Block + * @GST_RTCP_XR_TYPE_LRLE: Loss RLE Report Block + * @GST_RTCP_XR_TYPE_DRLE: Duplicate RLE Report Block + * @GST_RTCP_XR_TYPE_PRT: Packet Receipt Times Report Block + * @GST_RTCP_XR_TYPE_RRT: Receiver Reference Time Report Block + * @GST_RTCP_XR_TYPE_DLRR: Delay since the last Receiver Report + * @GST_RTCP_XR_TYPE_SSUMM: Statistics Summary Report Block + * @GST_RTCP_XR_TYPE_VOIP_METRICS: VoIP Metrics Report Block + * + * Types of RTCP Extended Reports, those are defined in RFC 3611 and other RFCs + * according to the [IANA registry](https://www.iana.org/assignments/rtcp-xr-block-types/rtcp-xr-block-types.xhtml). + * + * Since: 1.16 + */ +typedef enum +{ + GST_RTCP_XR_TYPE_INVALID = -1, + GST_RTCP_XR_TYPE_LRLE = 1, + GST_RTCP_XR_TYPE_DRLE = 2, + GST_RTCP_XR_TYPE_PRT = 3, + GST_RTCP_XR_TYPE_RRT = 4, + GST_RTCP_XR_TYPE_DLRR = 5, + GST_RTCP_XR_TYPE_SSUMM = 6, + GST_RTCP_XR_TYPE_VOIP_METRICS = 7 +} GstRTCPXRType; + /** * GST_RTCP_MAX_SDES: * @@ -217,7 +245,7 @@ struct _GstRTCPPacket gboolean padding; /* padding field of current packet */ guint8 count; /* count field of current packet */ GstRTCPType type; /* type of current packet */ - guint16 length; /* length of current packet in 32-bits words */ + guint16 length; /* length of current packet in 32-bits words minus one, this is validated when doing _get_first_packet() and _move_to_next() */ guint item_offset; /* current item offset for navigating SDES */ guint item_count; /* current item count */ @@ -479,6 +507,105 @@ const gchar * gst_rtcp_sdes_type_to_name (GstRTCPSDESType type); GST_RTP_API GstRTCPSDESType gst_rtcp_sdes_name_to_type (const gchar *name); +/* extended report */ + +GST_RTP_API +guint32 gst_rtcp_packet_xr_get_ssrc (GstRTCPPacket *packet); + +GST_RTP_API +gboolean gst_rtcp_packet_xr_first_rb (GstRTCPPacket *packet); + +GST_RTP_API +gboolean gst_rtcp_packet_xr_next_rb (GstRTCPPacket * packet); + +GST_RTP_API +GstRTCPXRType gst_rtcp_packet_xr_get_block_type (GstRTCPPacket * packet); + +GST_RTP_API +guint16 gst_rtcp_packet_xr_get_block_length (GstRTCPPacket * packet); + +GST_RTP_API +gboolean gst_rtcp_packet_xr_get_rle_info (GstRTCPPacket * packet, + guint32 * ssrc, guint8 * thining, + guint16 * begin_seq, guint16 * end_seq, + guint32 * chunk_count); + +GST_RTP_API +gboolean gst_rtcp_packet_xr_get_rle_nth_chunk (GstRTCPPacket * packet, guint nth, + guint16 * chunk); + +GST_RTP_API +gboolean gst_rtcp_packet_xr_get_prt_info (GstRTCPPacket * packet, + guint32 * ssrc, guint8 * thining, + guint16 * begin_seq, guint16 * end_seq); + +GST_RTP_API +gboolean gst_rtcp_packet_xr_get_prt_by_seq (GstRTCPPacket * packet, guint16 seq, + guint32 * receipt_time); + +GST_RTP_API +gboolean gst_rtcp_packet_xr_get_rrt (GstRTCPPacket * packet, guint64 * timestamp); + +GST_RTP_API +gboolean gst_rtcp_packet_xr_get_dlrr_block (GstRTCPPacket * packet, + guint nth, guint32 * ssrc, + guint32 * last_rr, guint32 * delay); + +GST_RTP_API +gboolean gst_rtcp_packet_xr_get_summary_info (GstRTCPPacket * packet, guint32 * ssrc, + guint16 * begin_seq, guint16 * end_seq); + +GST_RTP_API +gboolean gst_rtcp_packet_xr_get_summary_pkt (GstRTCPPacket * packet, + guint32 * lost_packets, guint32 * dup_packets); + +GST_RTP_API +gboolean gst_rtcp_packet_xr_get_summary_jitter (GstRTCPPacket * packet, + guint32 * min_jitter, guint32 * max_jitter, + guint32 * mean_jitter, guint32 * dev_jitter); + +GST_RTP_API +gboolean gst_rtcp_packet_xr_get_summary_ttl (GstRTCPPacket * packet, gboolean * is_ipv4, + guint8 * min_ttl, guint8 * max_ttl, + guint8 * mean_ttl, guint8 * dev_ttl); + +GST_RTP_API +gboolean gst_rtcp_packet_xr_get_voip_metrics_ssrc (GstRTCPPacket * packet, guint32 * ssrc); + +GST_RTP_API +gboolean gst_rtcp_packet_xr_get_voip_packet_metrics (GstRTCPPacket * packet, + guint8 * loss_rate, guint8 * discard_rate); + +GST_RTP_API +gboolean gst_rtcp_packet_xr_get_voip_burst_metrics (GstRTCPPacket * packet, + guint8 * burst_density, guint8 * gap_density, + guint16 * burst_duration, guint16 * gap_duration); + +GST_RTP_API +gboolean gst_rtcp_packet_xr_get_voip_delay_metrics (GstRTCPPacket * packet, + guint16 * roundtrip_delay, + guint16 * end_system_delay); + +GST_RTP_API +gboolean gst_rtcp_packet_xr_get_voip_signal_metrics (GstRTCPPacket * packet, + guint8 * signal_level, guint8 * noise_level, + guint8 * rerl, guint8 * gmin); + +GST_RTP_API +gboolean gst_rtcp_packet_xr_get_voip_quality_metrics (GstRTCPPacket * packet, + guint8 * r_factor, guint8 * ext_r_factor, + guint8 * mos_lq, guint8 * mos_cq); + +GST_RTP_API +gboolean gst_rtcp_packet_xr_get_voip_configuration_params (GstRTCPPacket * packet, + guint8 * gmin, guint8 * rx_config); + +GST_RTP_API +gboolean gst_rtcp_packet_xr_get_voip_jitter_buffer_params (GstRTCPPacket * packet, + guint16 * jb_nominal, + guint16 * jb_maximum, + guint16 * jb_abs_max); + G_END_DECLS #endif /* __GST_RTCPBUFFER_H__ */ diff --git a/tests/check/libs/rtp.c b/tests/check/libs/rtp.c index 72d526ab94..cdb9224c04 100644 --- a/tests/check/libs/rtp.c +++ b/tests/check/libs/rtp.c @@ -1137,6 +1137,467 @@ GST_START_TEST (test_rtcp_buffer_app) GST_END_TEST; +GST_START_TEST (test_rtcp_buffer_xr) +{ + GstBuffer *buffer; + GstRTCPPacket packet; + GstRTCPBuffer rtcp = GST_RTCP_BUFFER_INIT; + guint8 rtcp_pkt[] = { + 0x80, 0xCF, 0x00, 0x0e, /* Type XR, length = 14 */ + 0x97, 0x6d, 0x21, 0x6a, + 0x01, 0x00, 0x00, 0x03, /* Loss RLE, No thining, length = 3 */ + 0x97, 0x6d, 0x21, 0x6a, /* SSRC of source */ + 0x00, 0x01, 0x00, 0x02, + 0xcf, 0xb7, 0x8f, 0xb7, + 0x02, 0x00, 0x00, 0x03, /* Dup RLE, No thining, length = 3 */ + 0x97, 0x6d, 0x21, 0x6a, /* SSRC of source */ + 0x00, 0x01, 0x00, 0x02, + 0xcf, 0xb7, 0x8f, 0xb7, + 0x03, 0x00, 0x00, 0x04, /* Packet Receipt Times, No thining, length = 4 */ + 0x97, 0x6d, 0x21, 0x6a, /* SSRC of source */ + 0x00, 0x01, 0x00, 0x02, + 0x59, 0xf9, 0xdd, 0x7e, + 0x59, 0xf9, 0xdd, 0x7e, + }; + + buffer = gst_buffer_new_wrapped_full (GST_MEMORY_FLAG_READONLY, + rtcp_pkt, sizeof (rtcp_pkt), 0, sizeof (rtcp_pkt), NULL, NULL); + + fail_unless (gst_rtcp_buffer_map (buffer, GST_MAP_READ, &rtcp)); + + fail_unless (gst_rtcp_buffer_get_first_packet (&rtcp, &packet)); + fail_unless (gst_rtcp_packet_get_type (&packet) == GST_RTCP_TYPE_XR); + fail_unless (gst_rtcp_packet_xr_get_ssrc (&packet) == + GST_READ_UINT32_BE (rtcp_pkt + 12)); + fail_unless (gst_rtcp_packet_xr_first_rb (&packet)); + fail_unless (gst_rtcp_packet_xr_get_block_type (&packet) == + GST_RTCP_XR_TYPE_LRLE); + fail_unless (gst_rtcp_packet_xr_next_rb (&packet)); + fail_unless (gst_rtcp_packet_xr_get_block_type (&packet) == + GST_RTCP_XR_TYPE_DRLE); + fail_unless (gst_rtcp_packet_xr_next_rb (&packet)); + fail_unless (gst_rtcp_packet_xr_get_block_type (&packet) == + GST_RTCP_XR_TYPE_PRT); + + fail_if (gst_rtcp_packet_xr_next_rb (&packet)); + + gst_rtcp_buffer_unmap (&rtcp); + gst_buffer_unref (buffer); +} + +GST_END_TEST; + +GST_START_TEST (test_rtcp_buffer_xr_rle) +{ + GstBuffer *buffer; + GstRTCPPacket packet; + guint32 ssrc, chunk_count; + guint8 thining; + guint16 begin_seq, end_seq, chunk; + GstRTCPBuffer rtcp = GST_RTCP_BUFFER_INIT; + guint8 rtcp_pkt[] = { + 0x80, 0xCF, 0x00, 0x0a, /* Type XR, length = 10 */ + 0x97, 0x6d, 0x21, 0x6a, + 0x01, 0x00, 0x00, 0x03, /* Loss RLE, No thining, length = 3 */ + 0x97, 0x6d, 0x21, 0x6a, /* SSRC of source */ + 0x00, 0x01, 0x00, 0x02, + 0x80, 0x12, 0x00, 0x00, + 0x02, 0x00, 0x00, 0x04, /* Dup RLE, No thining, length = 4 */ + 0x97, 0x6d, 0x21, 0x7b, /* SSRC of source */ + 0x00, 0x01, 0x00, 0x04, + 0x8f, 0x21, 0x8f, 0x22, + 0x8f, 0x23, 0x8f, 0x24 + }; + guint8 rtcp_pkt_invalid_pkt_length[] = { + 0x80, 0xCF, 0x00, 0x04, /* Type XR, length = 4 */ + 0x97, 0x6d, 0x21, 0x6a, + 0x01, 0x00, 0x00, 0x02, /* Loss RLE, No thining, length = 1 (but really 3) */ + 0x97, 0x6d, 0x21, 0x6a, /* SSRC of source */ + 0x00, 0x01, 0x00, 0x02, + }; + + buffer = gst_buffer_new_wrapped_full (GST_MEMORY_FLAG_READONLY, + rtcp_pkt, sizeof (rtcp_pkt), 0, sizeof (rtcp_pkt), NULL, NULL); + gst_rtcp_buffer_map (buffer, GST_MAP_READ, &rtcp); + + fail_unless (gst_rtcp_buffer_get_first_packet (&rtcp, &packet)); + + /* check LRLE */ + fail_unless (gst_rtcp_packet_xr_first_rb (&packet)); + fail_unless (gst_rtcp_packet_xr_get_block_type (&packet) == + GST_RTCP_XR_TYPE_LRLE); + fail_unless (gst_rtcp_packet_xr_get_rle_info (&packet, &ssrc, &thining, + &begin_seq, &end_seq, &chunk_count)); + fail_unless_equals_int (ssrc, GST_READ_UINT32_BE (rtcp_pkt + 12)); + fail_unless_equals_int (thining, 0); + fail_unless_equals_int (begin_seq, 0x0001); + fail_unless_equals_int (end_seq, 0x0002); + fail_unless_equals_int (chunk_count, 2); + + gst_rtcp_packet_xr_get_rle_nth_chunk (&packet, 0, &chunk); + fail_unless_equals_int (chunk, 0x8012); + + gst_rtcp_packet_xr_get_rle_nth_chunk (&packet, 1, &chunk); + fail_unless_equals_int (chunk, 0x0); + + /* check DRLE */ + fail_unless (gst_rtcp_packet_xr_next_rb (&packet)); + fail_unless (gst_rtcp_packet_xr_get_block_type (&packet) == + GST_RTCP_XR_TYPE_DRLE); + fail_unless (gst_rtcp_packet_xr_get_rle_info (&packet, &ssrc, &thining, + &begin_seq, &end_seq, &chunk_count)); + fail_unless_equals_int (ssrc, GST_READ_UINT32_BE (rtcp_pkt + 28)); + fail_unless_equals_int (thining, 0); + fail_unless_equals_int (begin_seq, 0x0001); + fail_unless_equals_int (end_seq, 0x0004); + fail_unless_equals_int (chunk_count, 4); + + gst_rtcp_packet_xr_get_rle_nth_chunk (&packet, 1, &chunk); + fail_unless_equals_int (chunk, 0x8f22); + + gst_rtcp_packet_xr_get_rle_nth_chunk (&packet, 2, &chunk); + fail_unless_equals_int (chunk, 0x8f23); + + gst_rtcp_buffer_unmap (&rtcp); + gst_buffer_unref (buffer); + + /* Test invalid length */ + buffer = gst_buffer_new_wrapped_full (GST_MEMORY_FLAG_READONLY, + rtcp_pkt_invalid_pkt_length, sizeof (rtcp_pkt_invalid_pkt_length), 0, + sizeof (rtcp_pkt_invalid_pkt_length), NULL, NULL); + gst_rtcp_buffer_map (buffer, GST_MAP_READ, &rtcp); + fail_unless (gst_rtcp_buffer_get_first_packet (&rtcp, &packet)); + + /* check LRLE (should fail because length is too short) */ + fail_unless (gst_rtcp_packet_xr_first_rb (&packet)); + fail_unless (gst_rtcp_packet_xr_get_block_type (&packet) == + GST_RTCP_XR_TYPE_LRLE); + fail_if (gst_rtcp_packet_xr_get_rle_info (&packet, &ssrc, &thining, + &begin_seq, &end_seq, &chunk_count)); + gst_rtcp_buffer_unmap (&rtcp); + gst_buffer_unref (buffer); +} + +GST_END_TEST; + +GST_START_TEST (test_rtcp_buffer_xr_prt) +{ + GstBuffer *buffer; + GstRTCPPacket packet; + guint32 ssrc, receipt_time; + guint8 thining; + guint16 begin_seq, end_seq; + GstRTCPBuffer rtcp = GST_RTCP_BUFFER_INIT; + guint8 rtcp_pkt[] = { + 0x80, 0xCF, 0x00, 0x06, /* Type XR, length = 6 */ + 0x97, 0x6d, 0x21, 0x6a, + 0x03, 0x00, 0x00, 0x04, /* Packet Receipt Times, No thining, length = 4 */ + 0x97, 0x6d, 0x21, 0x6a, /* SSRC of source */ + 0x00, 0x01, 0x00, 0x03, + 0x59, 0xf9, 0xdd, 0x7e, + 0x59, 0xf9, 0xde, 0x00, + }; + guint8 rtcp_pkt_invalid_pkt_length[] = { + 0x80, 0xCF, 0x00, 0x04, /* Type XR, length = 4 */ + 0x97, 0x6d, 0x21, 0x6a, + 0x03, 0x00, 0x00, 0x02, /* Packet Receipt Times, No thining, length = 2 (but should be 4) */ + 0x97, 0x6d, 0x21, 0x6a, /* SSRC of source */ + 0x00, 0x01, 0x00, 0x03, + }; + buffer = gst_buffer_new_wrapped_full (GST_MEMORY_FLAG_READONLY, + rtcp_pkt, sizeof (rtcp_pkt), 0, sizeof (rtcp_pkt), NULL, NULL); + gst_rtcp_buffer_map (buffer, GST_MAP_READ, &rtcp); + + fail_unless (gst_rtcp_buffer_get_first_packet (&rtcp, &packet)); + + fail_unless (gst_rtcp_packet_xr_first_rb (&packet)); + fail_unless (gst_rtcp_packet_xr_get_block_type (&packet) == + GST_RTCP_XR_TYPE_PRT); + + fail_unless (gst_rtcp_packet_xr_get_prt_info (&packet, &ssrc, &thining, + &begin_seq, &end_seq)); + fail_unless (gst_rtcp_packet_xr_get_prt_by_seq (&packet, 2, &receipt_time)); + fail_unless_equals_int_hex (receipt_time, 0x59f9de00L); + + gst_rtcp_buffer_unmap (&rtcp); + gst_buffer_unref (buffer); + + /* Test for invalid length */ + buffer = gst_buffer_new_wrapped_full (GST_MEMORY_FLAG_READONLY, + rtcp_pkt_invalid_pkt_length, sizeof (rtcp_pkt_invalid_pkt_length), 0, + sizeof (rtcp_pkt_invalid_pkt_length), NULL, NULL); + gst_rtcp_buffer_map (buffer, GST_MAP_READ, &rtcp); + + fail_unless (gst_rtcp_buffer_get_first_packet (&rtcp, &packet)); + + fail_unless (gst_rtcp_packet_xr_first_rb (&packet)); + fail_unless (gst_rtcp_packet_xr_get_block_type (&packet) == + GST_RTCP_XR_TYPE_PRT); + + fail_if (gst_rtcp_packet_xr_get_prt_info (&packet, &ssrc, &thining, + &begin_seq, &end_seq)); + gst_rtcp_buffer_unmap (&rtcp); + gst_buffer_unref (buffer); + +} + +GST_END_TEST; + +GST_START_TEST (test_rtcp_buffer_xr_rrt) +{ + GstBuffer *buffer; + GstRTCPPacket packet; + guint64 ntptime; + GstRTCPBuffer rtcp = GST_RTCP_BUFFER_INIT; + guint8 rtcp_pkt[] = { + 0x80, 0xCF, 0x00, 0x04, /* Type XR, length = 4 */ + 0x97, 0x6d, 0x21, 0x6a, + 0x04, 0x00, 0x00, 0x02, /* Receiver Reference Time, length = 2 */ + 0x01, 0x23, 0x45, 0x67, + 0x89, 0x01, 0x23, 0x45 + }; + guint8 rtcp_pkt_invalid_pkt_length[] = { + 0x80, 0xCF, 0x00, 0x04, /* Type XR, length = 4 */ + 0x97, 0x6d, 0x21, 0x6a, + 0x04, 0x00, 0x00, 0x01, /* Receiver Reference Time, length = 1 */ + 0x01, 0x23, 0x45, 0x67, + 0x89, 0x01, 0x23, 0x45 + }; + + buffer = gst_buffer_new_wrapped_full (GST_MEMORY_FLAG_READONLY, + rtcp_pkt, sizeof (rtcp_pkt), 0, sizeof (rtcp_pkt), NULL, NULL); + gst_rtcp_buffer_map (buffer, GST_MAP_READ, &rtcp); + + fail_unless (gst_rtcp_buffer_get_first_packet (&rtcp, &packet)); + + fail_unless (gst_rtcp_packet_xr_first_rb (&packet)); + fail_unless (gst_rtcp_packet_xr_get_block_type (&packet) == + GST_RTCP_XR_TYPE_RRT); + + fail_unless (gst_rtcp_packet_xr_get_rrt (&packet, &ntptime)); + fail_unless_equals_uint64_hex (ntptime, 0x0123456789012345LL); + + gst_rtcp_buffer_unmap (&rtcp); + gst_buffer_unref (buffer); + + /* Test invalid length */ + buffer = gst_buffer_new_wrapped_full (GST_MEMORY_FLAG_READONLY, + rtcp_pkt_invalid_pkt_length, sizeof (rtcp_pkt_invalid_pkt_length), 0, + sizeof (rtcp_pkt_invalid_pkt_length), NULL, NULL); + gst_rtcp_buffer_map (buffer, GST_MAP_READ, &rtcp); + + fail_unless (gst_rtcp_buffer_get_first_packet (&rtcp, &packet)); + + fail_unless (gst_rtcp_packet_xr_first_rb (&packet)); + fail_unless (gst_rtcp_packet_xr_get_block_type (&packet) == + GST_RTCP_XR_TYPE_RRT); + + fail_if (gst_rtcp_packet_xr_get_rrt (&packet, &ntptime)); + + gst_rtcp_buffer_unmap (&rtcp); + gst_buffer_unref (buffer); +} + +GST_END_TEST; + +GST_START_TEST (test_rtcp_buffer_xr_dlrr) +{ + GstBuffer *buffer; + GstRTCPPacket packet; + guint32 ssrc, last_rr, delay; + GstRTCPBuffer rtcp = GST_RTCP_BUFFER_INIT; + guint8 rtcp_pkt[] = { + 0x80, 0xCF, 0x00, 0x08, /* Type XR, length = 8 */ + 0x97, 0x6d, 0x21, 0x6a, + 0x05, 0x00, 0x00, 0x06, /* DLRR, length = 6 */ + 0x97, 0x6d, 0x21, 0x6a, /* SSRC of source */ + 0x01, 0x23, 0x45, 0x67, + 0x89, 0x01, 0x23, 0x45, + 0x97, 0x6d, 0x21, 0x6b, /* SSRC of source */ + 0x01, 0x23, 0x45, 0x67, + 0x89, 0x01, 0x23, 0x45 + }; + + buffer = gst_buffer_new_wrapped_full (GST_MEMORY_FLAG_READONLY, + rtcp_pkt, sizeof (rtcp_pkt), 0, sizeof (rtcp_pkt), NULL, NULL); + gst_rtcp_buffer_map (buffer, GST_MAP_READ, &rtcp); + + fail_unless (gst_rtcp_buffer_get_first_packet (&rtcp, &packet)); + + fail_unless (gst_rtcp_packet_xr_first_rb (&packet)); + fail_unless (gst_rtcp_packet_xr_get_block_type (&packet) == + GST_RTCP_XR_TYPE_DLRR); + + fail_unless (gst_rtcp_packet_xr_get_dlrr_block (&packet, 0, &ssrc, &last_rr, + &delay)); + fail_unless_equals_int_hex (ssrc, GST_READ_UINT32_BE (rtcp_pkt + 12)); + fail_unless (gst_rtcp_packet_xr_get_dlrr_block (&packet, 1, &ssrc, &last_rr, + &delay)); + fail_unless_equals_int_hex (ssrc, GST_READ_UINT32_BE (rtcp_pkt + 24)); + + /* it has only two sub-blocks. */ + fail_if (gst_rtcp_packet_xr_get_dlrr_block (&packet, 2, &ssrc, &last_rr, + &delay)); + + gst_rtcp_buffer_unmap (&rtcp); + gst_buffer_unref (buffer); +} + +GST_END_TEST; + +GST_START_TEST (test_rtcp_buffer_xr_ssumm) +{ + GstBuffer *buffer; + GstRTCPPacket packet; + guint32 ssrc, lost_packets, dup_packets; + guint16 begin_seq, end_seq; + guint32 min_jitter, max_jitter, mean_jitter, dev_jitter; + guint8 min_ttl, max_ttl, mean_ttl, dev_ttl; + gboolean ipv4; + GstRTCPBuffer rtcp = GST_RTCP_BUFFER_INIT; + guint8 rtcp_pkt[] = { + 0x80, 0xCF, 0x00, 0x0b, /* Type XR, length = 11 */ + 0x97, 0x6d, 0x21, 0x6a, + 0x06, 0xe8, 0x00, 0x09, /* Statistics summary, length = 9 */ + 0x97, 0x6d, 0x21, 0x6a, /* SSRC of source */ + 0x00, 0x01, 0x00, 0x02, + 0x00, 0x00, 0x00, 0x01, + 0x00, 0x00, 0x00, 0x02, + 0x00, 0x00, 0x00, 0x03, + 0x00, 0x00, 0x00, 0x04, + 0x00, 0x00, 0x00, 0x05, + 0x00, 0x00, 0x00, 0x06, + 0x01, 0x80, 0x0f, 0x8f + }; + + buffer = gst_buffer_new_wrapped_full (GST_MEMORY_FLAG_READONLY, + rtcp_pkt, sizeof (rtcp_pkt), 0, sizeof (rtcp_pkt), NULL, NULL); + gst_rtcp_buffer_map (buffer, GST_MAP_READ, &rtcp); + + fail_unless (gst_rtcp_buffer_get_first_packet (&rtcp, &packet)); + + fail_unless (gst_rtcp_packet_xr_first_rb (&packet)); + fail_unless (gst_rtcp_packet_xr_get_block_type (&packet) == + GST_RTCP_XR_TYPE_SSUMM); + + fail_unless (gst_rtcp_packet_xr_get_summary_info (&packet, &ssrc, &begin_seq, + &end_seq)); + fail_unless_equals_int_hex (ssrc, GST_READ_UINT32_BE (rtcp_pkt + 12)); + fail_unless_equals_int (begin_seq, GST_READ_UINT16_BE (rtcp_pkt + 16)); + fail_unless_equals_int (end_seq, GST_READ_UINT16_BE (rtcp_pkt + 18)); + + fail_unless (gst_rtcp_packet_xr_get_summary_pkt (&packet, &lost_packets, + &dup_packets)); + fail_unless_equals_int (lost_packets, GST_READ_UINT32_BE (rtcp_pkt + 20)); + fail_unless_equals_int (dup_packets, GST_READ_UINT32_BE (rtcp_pkt + 24)); + + fail_unless (gst_rtcp_packet_xr_get_summary_jitter (&packet, &min_jitter, + &max_jitter, &mean_jitter, &dev_jitter)); + fail_unless_equals_int (min_jitter, GST_READ_UINT32_BE (rtcp_pkt + 28)); + fail_unless_equals_int (max_jitter, GST_READ_UINT32_BE (rtcp_pkt + 32)); + fail_unless_equals_int (mean_jitter, GST_READ_UINT32_BE (rtcp_pkt + 36)); + fail_unless_equals_int (dev_jitter, GST_READ_UINT32_BE (rtcp_pkt + 40)); + + fail_unless (gst_rtcp_packet_xr_get_summary_ttl (&packet, &ipv4, &min_ttl, + &max_ttl, &mean_ttl, &dev_ttl)); + fail_unless (ipv4); + fail_unless_equals_int (min_ttl, rtcp_pkt[44]); + fail_unless_equals_int (max_ttl, rtcp_pkt[45]); + fail_unless_equals_int (mean_ttl, rtcp_pkt[46]); + fail_unless_equals_int (dev_ttl, rtcp_pkt[47]); + + gst_rtcp_buffer_unmap (&rtcp); + gst_buffer_unref (buffer); +} + +GST_END_TEST; + +GST_START_TEST (test_rtcp_buffer_xr_voipmtrx) +{ + GstBuffer *buffer; + GstRTCPPacket packet; + guint32 ssrc; + guint8 loss_rate, discard_rate, burst_density, gap_density; + guint8 signal_level, noise_level, rerl, gmin; + guint8 r_factor, ext_r_factor, mos_lq, mos_cq, rx_config; + guint16 burst_duration, gap_duration; + guint16 roundtrip_delay, end_system_delay; + guint16 jb_nominal, jb_maximum, jb_abs_max; + GstRTCPBuffer rtcp = GST_RTCP_BUFFER_INIT; + guint8 rtcp_pkt[] = { + 0x80, 0xCF, 0x00, 0x0a, /* Type XR, length = 10 */ + 0x97, 0x6d, 0x21, 0x6a, + 0x07, 0x00, 0x00, 0x08, /* VoIP Metrics, length = 8 */ + 0x97, 0x6d, 0x21, 0x6a, /* SSRC of source */ + 0x01, 0x02, 0x03, 0x04, + 0x05, 0x06, 0x07, 0x08, + 0x09, 0x0a, 0x0b, 0x0c, + 0x0d, 0x0e, 0x0f, 0x10, + 0x11, 0x12, 0x13, 0x14, + 0x15, 0x00, 0x16, 0x17, + 0x18, 0x19, 0x1a, 0x1b + }; + + buffer = gst_buffer_new_wrapped_full (GST_MEMORY_FLAG_READONLY, + rtcp_pkt, sizeof (rtcp_pkt), 0, sizeof (rtcp_pkt), NULL, NULL); + gst_rtcp_buffer_map (buffer, GST_MAP_READ, &rtcp); + + fail_unless (gst_rtcp_buffer_get_first_packet (&rtcp, &packet)); + + fail_unless (gst_rtcp_packet_xr_first_rb (&packet)); + fail_unless (gst_rtcp_packet_xr_get_block_type (&packet) == + GST_RTCP_XR_TYPE_VOIP_METRICS); + fail_unless (gst_rtcp_packet_xr_get_voip_metrics_ssrc (&packet, &ssrc)); + fail_unless_equals_int_hex (ssrc, GST_READ_UINT32_BE (rtcp_pkt + 12)); + + fail_unless (gst_rtcp_packet_xr_get_voip_packet_metrics (&packet, &loss_rate, + &discard_rate)); + fail_unless_equals_int (loss_rate, rtcp_pkt[16]); + fail_unless_equals_int (discard_rate, rtcp_pkt[17]); + + fail_unless (gst_rtcp_packet_xr_get_voip_burst_metrics (&packet, + &burst_density, &gap_density, &burst_duration, &gap_duration)); + fail_unless_equals_int (burst_density, rtcp_pkt[18]); + fail_unless_equals_int (gap_density, rtcp_pkt[19]); + fail_unless_equals_int (burst_duration, GST_READ_UINT16_BE (rtcp_pkt + 20)); + fail_unless_equals_int (gap_duration, GST_READ_UINT16_BE (rtcp_pkt + 22)); + + fail_unless (gst_rtcp_packet_xr_get_voip_delay_metrics (&packet, + &roundtrip_delay, &end_system_delay)); + fail_unless_equals_int (roundtrip_delay, GST_READ_UINT16_BE (rtcp_pkt + 24)); + fail_unless_equals_int (end_system_delay, GST_READ_UINT16_BE (rtcp_pkt + 26)); + + fail_unless (gst_rtcp_packet_xr_get_voip_signal_metrics (&packet, + &signal_level, &noise_level, &rerl, &gmin)); + fail_unless_equals_int (signal_level, rtcp_pkt[28]); + fail_unless_equals_int (noise_level, rtcp_pkt[29]); + fail_unless_equals_int (rerl, rtcp_pkt[30]); + fail_unless_equals_int (gmin, rtcp_pkt[31]); + + fail_unless (gst_rtcp_packet_xr_get_voip_quality_metrics (&packet, &r_factor, + &ext_r_factor, &mos_lq, &mos_cq)); + fail_unless_equals_int (r_factor, rtcp_pkt[32]); + fail_unless_equals_int (ext_r_factor, rtcp_pkt[33]); + fail_unless_equals_int (mos_lq, rtcp_pkt[34]); + fail_unless_equals_int (mos_cq, rtcp_pkt[35]); + + fail_unless (gst_rtcp_packet_xr_get_voip_configuration_params (&packet, &gmin, + &rx_config)); + fail_unless_equals_int (gmin, rtcp_pkt[31]); + fail_unless_equals_int (rx_config, rtcp_pkt[36]); + + fail_unless (gst_rtcp_packet_xr_get_voip_jitter_buffer_params (&packet, + &jb_nominal, &jb_maximum, &jb_abs_max)); + fail_unless_equals_int (jb_nominal, GST_READ_UINT16_BE (rtcp_pkt + 38)); + fail_unless_equals_int (jb_maximum, GST_READ_UINT16_BE (rtcp_pkt + 40)); + fail_unless_equals_int (jb_abs_max, GST_READ_UINT16_BE (rtcp_pkt + 42)); + + gst_rtcp_buffer_unmap (&rtcp); + gst_buffer_unref (buffer); +} + +GST_END_TEST; + GST_START_TEST (test_rtp_ntp64_extension) { GstBuffer *buf; @@ -1463,6 +1924,13 @@ rtp_suite (void) 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_rtcp_buffer_xr); + tcase_add_test (tc_chain, test_rtcp_buffer_xr_rle); + tcase_add_test (tc_chain, test_rtcp_buffer_xr_prt); + tcase_add_test (tc_chain, test_rtcp_buffer_xr_rrt); + tcase_add_test (tc_chain, test_rtcp_buffer_xr_dlrr); + tcase_add_test (tc_chain, test_rtcp_buffer_xr_ssumm); + tcase_add_test (tc_chain, test_rtcp_buffer_xr_voipmtrx); tcase_add_test (tc_chain, test_rtp_ntp64_extension); tcase_add_test (tc_chain, test_rtp_ntp56_extension);