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);