gstreamer/gst-libs/gst/rtp/gstrtcpbuffer.c
Justin Kim 5303e2c32b 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
2018-12-13 14:01:06 -05:00

3559 lines
90 KiB
C

/* GStreamer
* Copyright (C) <2007> Wim Taymans <wim@fluendo.com>
*
* gstrtcpbuffer.h: various helper functions to manipulate buffers
* with RTCP payload.
*
* This library is free software; you can redistribute it and/or
* modify it under the terms of the GNU Library General Public
* License as published by the Free Software Foundation; either
* version 2 of the License, or (at your option) any later version.
*
* This library is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* Library General Public License for more details.
*
* You should have received a copy of the GNU Library General Public
* License along with this library; if not, write to the
* Free Software Foundation, Inc., 51 Franklin St, Fifth Floor,
* Boston, MA 02110-1301, USA.
*/
/**
* SECTION:gstrtcpbuffer
* @title: GstRTCPBuffer
* @short_description: Helper methods for dealing with RTCP buffers
* @see_also: #GstRTPBasePayload, #GstRTPBaseDepayload, #gstrtpbuffer
*
* Note: The API in this module is not yet declared stable.
*
* The GstRTPCBuffer helper functions makes it easy to parse and create regular
* #GstBuffer objects that contain compound RTCP packets. These buffers are typically
* of 'application/x-rtcp' #GstCaps.
*
* An RTCP buffer consists of 1 or more #GstRTCPPacket structures that you can
* retrieve with gst_rtcp_buffer_get_first_packet(). #GstRTCPPacket acts as a pointer
* into the RTCP buffer; you can move to the next packet with
* gst_rtcp_packet_move_to_next().
*
*/
#ifdef HAVE_CONFIG_H
#include "config.h"
#endif
#include <string.h>
#include "gstrtcpbuffer.h"
/**
* gst_rtcp_buffer_new_take_data:
* @data: (array length=len) (element-type guint8): data for the new buffer
* @len: the length of data
*
* Create a new buffer and set the data and size of the buffer to @data and @len
* respectively. @data will be freed when the buffer is unreffed, so this
* function transfers ownership of @data to the new buffer.
*
* Returns: A newly allocated buffer with @data and of size @len.
*/
GstBuffer *
gst_rtcp_buffer_new_take_data (gpointer data, guint len)
{
GstBuffer *result;
g_return_val_if_fail (data != NULL, NULL);
g_return_val_if_fail (len > 0, NULL);
result = gst_buffer_new_wrapped (data, len);
return result;
}
/**
* gst_rtcp_buffer_new_copy_data:
* @data: (array length=len) (element-type guint8): data for the new buffer
* @len: the length of data
*
* Create a new buffer and set the data to a copy of @len
* bytes of @data and the size to @len. The data will be freed when the buffer
* is freed.
*
* Returns: A newly allocated buffer with a copy of @data and of size @len.
*/
GstBuffer *
gst_rtcp_buffer_new_copy_data (gconstpointer data, guint len)
{
return gst_rtcp_buffer_new_take_data (g_memdup (data, len), len);
}
static gboolean
gst_rtcp_buffer_validate_data_internal (guint8 * data, guint len,
guint16 valid_mask)
{
guint16 header_mask;
guint header_len;
guint8 version;
guint data_len;
gboolean padding;
guint8 pad_bytes;
g_return_val_if_fail (data != NULL, FALSE);
/* we need 4 bytes for the type and length */
if (G_UNLIKELY (len < 4))
goto wrong_length;
/* first packet must be RR or SR and version must be 2 */
header_mask = ((data[0] << 8) | data[1]) & valid_mask;
if (G_UNLIKELY (header_mask != GST_RTCP_VALID_VALUE))
goto wrong_mask;
/* no padding when mask succeeds */
padding = FALSE;
/* store len */
data_len = len;
while (TRUE) {
/* get packet length */
header_len = (((data[2] << 8) | data[3]) + 1) << 2;
if (data_len < header_len)
goto wrong_length;
/* move to next compount packet */
data += header_len;
data_len -= header_len;
/* we are at the end now */
if (data_len < 4)
break;
/* padding only allowed on last packet */
if (padding)
break;
/* check version of new packet */
version = data[0] & 0xc0;
if (version != (GST_RTCP_VERSION << 6))
goto wrong_version;
/* check padding of new packet */
if (data[0] & 0x20) {
padding = TRUE;
/* last byte of padding contains the number of padded bytes including
* itself. must be a multiple of 4, but cannot be 0. */
pad_bytes = data[data_len - 1];
if (pad_bytes == 0 || (pad_bytes & 0x3))
goto wrong_padding;
}
}
if (data_len != 0) {
/* some leftover bytes */
goto wrong_length;
}
return TRUE;
/* ERRORS */
wrong_length:
{
GST_DEBUG ("len check failed");
return FALSE;
}
wrong_mask:
{
GST_DEBUG ("mask check failed (%04x != %04x)", header_mask, valid_mask);
return FALSE;
}
wrong_version:
{
GST_DEBUG ("wrong version (%d < 2)", version >> 6);
return FALSE;
}
wrong_padding:
{
GST_DEBUG ("padding check failed");
return FALSE;
}
}
/**
* gst_rtcp_buffer_validate_data_reduced:
* @data: (array length=len): the data to validate
* @len: the length of @data to validate
*
* Check if the @data and @size point to the data of a valid RTCP packet.
* Use this function to validate a packet before using the other functions in
* this module.
*
* This function is updated to support reduced size rtcp packets according to
* RFC 5506 and will validate full compound RTCP packets as well as reduced
* size RTCP packets.
*
* Returns: TRUE if the data points to a valid RTCP packet.
*
* Since: 1.6
*/
gboolean
gst_rtcp_buffer_validate_data_reduced (guint8 * data, guint len)
{
return gst_rtcp_buffer_validate_data_internal (data, len,
GST_RTCP_REDUCED_SIZE_VALID_MASK);
}
/**
* gst_rtcp_buffer_validate_data:
* @data: (array length=len): the data to validate
* @len: the length of @data to validate
*
* Check if the @data and @size point to the data of a valid compound,
* non-reduced size RTCP packet.
* Use this function to validate a packet before using the other functions in
* this module.
*
* Returns: TRUE if the data points to a valid RTCP packet.
*/
gboolean
gst_rtcp_buffer_validate_data (guint8 * data, guint len)
{
return gst_rtcp_buffer_validate_data_internal (data, len,
GST_RTCP_VALID_MASK);
}
/**
* gst_rtcp_buffer_validate_reduced:
* @buffer: the buffer to validate
*
* Check if the data pointed to by @buffer is a valid RTCP packet using
* gst_rtcp_buffer_validate_reduced().
*
* Returns: TRUE if @buffer is a valid RTCP packet.
*
* Since: 1.6
*/
gboolean
gst_rtcp_buffer_validate_reduced (GstBuffer * buffer)
{
gboolean res;
GstMapInfo map;
g_return_val_if_fail (GST_IS_BUFFER (buffer), FALSE);
gst_buffer_map (buffer, &map, GST_MAP_READ);
res = gst_rtcp_buffer_validate_data_reduced (map.data, map.size);
gst_buffer_unmap (buffer, &map);
return res;
}
/**
* gst_rtcp_buffer_validate:
* @buffer: the buffer to validate
*
* Check if the data pointed to by @buffer is a valid RTCP packet using
* gst_rtcp_buffer_validate_data().
*
* Returns: TRUE if @buffer is a valid RTCP packet.
*/
gboolean
gst_rtcp_buffer_validate (GstBuffer * buffer)
{
gboolean res;
GstMapInfo map;
g_return_val_if_fail (GST_IS_BUFFER (buffer), FALSE);
gst_buffer_map (buffer, &map, GST_MAP_READ);
res = gst_rtcp_buffer_validate_data (map.data, map.size);
gst_buffer_unmap (buffer, &map);
return res;
}
/**
* gst_rtcp_buffer_new:
* @mtu: the maximum mtu size.
*
* Create a new buffer for constructing RTCP packets. The packet will have a
* maximum size of @mtu.
*
* Returns: A newly allocated buffer.
*/
GstBuffer *
gst_rtcp_buffer_new (guint mtu)
{
GstBuffer *result;
guint8 *data;
g_return_val_if_fail (mtu > 0, NULL);
data = g_malloc0 (mtu);
result = gst_buffer_new_wrapped_full (0, data, mtu, 0, 0, data, g_free);
return result;
}
/**
* gst_rtcp_buffer_map:
* @buffer: a buffer with an RTCP packet
* @flags: flags for the mapping
* @rtcp: resulting #GstRTCPBuffer
*
* Open @buffer for reading or writing, depending on @flags. The resulting RTCP
* buffer state is stored in @rtcp.
*/
gboolean
gst_rtcp_buffer_map (GstBuffer * buffer, GstMapFlags flags,
GstRTCPBuffer * rtcp)
{
g_return_val_if_fail (rtcp != NULL, FALSE);
g_return_val_if_fail (rtcp->buffer == NULL, FALSE);
g_return_val_if_fail (GST_IS_BUFFER (buffer), FALSE);
g_return_val_if_fail (flags & GST_MAP_READ, FALSE);
rtcp->buffer = buffer;
gst_buffer_map (buffer, &rtcp->map, flags);
return TRUE;
}
/**
* gst_rtcp_buffer_unmap:
* @rtcp: a buffer with an RTCP packet
*
* Finish @rtcp after being constructed. This function is usually called
* after gst_rtcp_buffer_map() and after adding the RTCP items to the new buffer.
*
* The function adjusts the size of @rtcp with the total length of all the
* added packets.
*/
gboolean
gst_rtcp_buffer_unmap (GstRTCPBuffer * rtcp)
{
g_return_val_if_fail (rtcp != NULL, FALSE);
g_return_val_if_fail (GST_IS_BUFFER (rtcp->buffer), FALSE);
if (rtcp->map.flags & GST_MAP_WRITE) {
/* shrink size */
gst_buffer_resize (rtcp->buffer, 0, rtcp->map.size);
}
gst_buffer_unmap (rtcp->buffer, &rtcp->map);
rtcp->buffer = NULL;
return TRUE;
}
/**
* gst_rtcp_buffer_get_packet_count:
* @rtcp: a valid RTCP buffer
*
* Get the number of RTCP packets in @rtcp.
*
* Returns: the number of RTCP packets in @rtcp.
*/
guint
gst_rtcp_buffer_get_packet_count (GstRTCPBuffer * rtcp)
{
GstRTCPPacket packet;
guint count;
g_return_val_if_fail (rtcp != NULL, 0);
g_return_val_if_fail (GST_IS_BUFFER (rtcp->buffer), 0);
g_return_val_if_fail (rtcp != NULL, 0);
g_return_val_if_fail (rtcp->map.flags & GST_MAP_READ, 0);
count = 0;
if (gst_rtcp_buffer_get_first_packet (rtcp, &packet)) {
do {
count++;
} while (gst_rtcp_packet_move_to_next (&packet));
}
return count;
}
/**
* read_packet_header:
* @packet: a packet
*
* Read the packet headers for the packet pointed to by @packet.
*
* Returns: TRUE if @packet pointed to a valid header.
*/
static gboolean
read_packet_header (GstRTCPPacket * packet)
{
guint8 *data;
gsize maxsize;
guint offset;
g_return_val_if_fail (packet != NULL, FALSE);
data = packet->rtcp->map.data;
maxsize = packet->rtcp->map.size;
offset = packet->offset;
/* check if we are at the end of the buffer, we add 4 because we also want to
* ensure we can read the header. */
if (offset + 4 > maxsize)
return FALSE;
if ((data[offset] & 0xc0) != (GST_RTCP_VERSION << 6))
return FALSE;
/* read count, type and length */
packet->padding = (data[offset] & 0x20) == 0x20;
packet->count = data[offset] & 0x1f;
packet->type = data[offset + 1];
packet->length = (data[offset + 2] << 8) | data[offset + 3];
packet->item_offset = 4;
packet->item_count = 0;
packet->entry_offset = 4;
/* Ensure no overread from the claimed data size. The packet length
is expressed in multiple of 32 bits, to make things obvious. */
if (offset + 4 + packet->length * 4 > maxsize)
return FALSE;
return TRUE;
}
/**
* gst_rtcp_buffer_get_first_packet:
* @rtcp: a valid RTCP buffer
* @packet: a #GstRTCPPacket
*
* Initialize a new #GstRTCPPacket pointer that points to the first packet in
* @rtcp.
*
* Returns: TRUE if the packet existed in @rtcp.
*/
gboolean
gst_rtcp_buffer_get_first_packet (GstRTCPBuffer * rtcp, GstRTCPPacket * packet)
{
g_return_val_if_fail (rtcp != NULL, FALSE);
g_return_val_if_fail (GST_IS_BUFFER (rtcp->buffer), FALSE);
g_return_val_if_fail (packet != NULL, FALSE);
g_return_val_if_fail (rtcp != NULL, 0);
g_return_val_if_fail (rtcp->map.flags & GST_MAP_READ, 0);
/* init to 0 */
packet->rtcp = rtcp;
packet->offset = 0;
packet->type = GST_RTCP_TYPE_INVALID;
if (!read_packet_header (packet))
return FALSE;
return TRUE;
}
/**
* gst_rtcp_packet_move_to_next:
* @packet: a #GstRTCPPacket
*
* Move the packet pointer @packet to the next packet in the payload.
* Use gst_rtcp_buffer_get_first_packet() to initialize @packet.
*
* Returns: TRUE if @packet is pointing to a valid packet after calling this
* function.
*/
gboolean
gst_rtcp_packet_move_to_next (GstRTCPPacket * packet)
{
g_return_val_if_fail (packet != NULL, FALSE);
g_return_val_if_fail (packet->type != GST_RTCP_TYPE_INVALID, FALSE);
g_return_val_if_fail (packet->rtcp != NULL, FALSE);
g_return_val_if_fail (packet->rtcp->map.flags & GST_MAP_READ, FALSE);
/* if we have a padding or invalid packet, it must be the last,
* return FALSE */
if (packet->type == GST_RTCP_TYPE_INVALID || packet->padding)
goto end;
/* move to next packet. Add 4 because the header is not included in length */
packet->offset += (packet->length << 2) + 4;
/* try to read new header */
if (!read_packet_header (packet))
goto end;
return TRUE;
/* ERRORS */
end:
{
packet->type = GST_RTCP_TYPE_INVALID;
return FALSE;
}
}
/**
* gst_rtcp_buffer_add_packet:
* @rtcp: a valid RTCP buffer
* @type: the #GstRTCPType of the new packet
* @packet: pointer to new packet
*
* Add a new packet of @type to @rtcp. @packet will point to the newly created
* packet.
*
* Returns: %TRUE if the packet could be created. This function returns %FALSE
* if the max mtu is exceeded for the buffer.
*/
gboolean
gst_rtcp_buffer_add_packet (GstRTCPBuffer * rtcp, GstRTCPType type,
GstRTCPPacket * packet)
{
guint len;
gsize maxsize;
guint8 *data;
gboolean result;
g_return_val_if_fail (rtcp != NULL, FALSE);
g_return_val_if_fail (GST_IS_BUFFER (rtcp->buffer), FALSE);
g_return_val_if_fail (type != GST_RTCP_TYPE_INVALID, FALSE);
g_return_val_if_fail (packet != NULL, FALSE);
g_return_val_if_fail (rtcp->map.flags & GST_MAP_WRITE, FALSE);
/* find free space */
if (gst_rtcp_buffer_get_first_packet (rtcp, packet))
while (gst_rtcp_packet_move_to_next (packet));
maxsize = rtcp->map.maxsize;
/* packet->offset is now pointing to the next free offset in the buffer to
* start a compount packet. Next we figure out if we have enough free space in
* the buffer to continue. */
switch (type) {
case GST_RTCP_TYPE_SR:
len = 28;
break;
case GST_RTCP_TYPE_RR:
len = 8;
break;
case GST_RTCP_TYPE_SDES:
len = 4;
break;
case GST_RTCP_TYPE_BYE:
len = 4;
break;
case GST_RTCP_TYPE_APP:
len = 12;
break;
case GST_RTCP_TYPE_RTPFB:
len = 12;
break;
case GST_RTCP_TYPE_PSFB:
len = 12;
break;
case GST_RTCP_TYPE_XR:
len = 8;
break;
default:
goto unknown_type;
}
if (packet->offset + len >= maxsize)
goto no_space;
rtcp->map.size += len;
data = rtcp->map.data + packet->offset;
data[0] = (GST_RTCP_VERSION << 6);
data[1] = type;
/* length is stored in multiples of 32 bit words minus the length of the
* header */
len = (len - 4) >> 2;
data[2] = len >> 8;
data[3] = len & 0xff;
/* now try to position to the packet */
result = read_packet_header (packet);
return result;
/* ERRORS */
unknown_type:
{
g_warning ("unknown type %d", type);
return FALSE;
}
no_space:
{
return FALSE;
}
}
/**
* gst_rtcp_packet_remove:
* @packet: a #GstRTCPPacket
*
* Removes the packet pointed to by @packet and moves pointer to the next one
*
* Returns: TRUE if @packet is pointing to a valid packet after calling this
* function.
*/
gboolean
gst_rtcp_packet_remove (GstRTCPPacket * packet)
{
gboolean ret = FALSE;
guint offset = 0;
g_return_val_if_fail (packet != NULL, FALSE);
g_return_val_if_fail (packet->type != GST_RTCP_TYPE_INVALID, FALSE);
g_return_val_if_fail (packet->rtcp != NULL, FALSE);
g_return_val_if_fail (packet->rtcp->map.flags & GST_MAP_WRITE, FALSE);
/* The next packet starts at offset + length + 4 (the header) */
offset = packet->offset + (packet->length << 2) + 4;
/* Overwrite this packet with the rest of the data */
memmove (packet->rtcp->map.data + packet->offset,
packet->rtcp->map.data + offset, packet->rtcp->map.size - offset);
packet->rtcp->map.size -= offset - packet->offset;
/* try to read next header */
ret = read_packet_header (packet);
if (!ret)
packet->type = GST_RTCP_TYPE_INVALID;
return ret;
}
/**
* gst_rtcp_packet_get_padding:
* @packet: a valid #GstRTCPPacket
*
* Get the packet padding of the packet pointed to by @packet.
*
* Returns: If the packet has the padding bit set.
*/
gboolean
gst_rtcp_packet_get_padding (GstRTCPPacket * packet)
{
g_return_val_if_fail (packet != NULL, FALSE);
g_return_val_if_fail (packet->type != GST_RTCP_TYPE_INVALID, FALSE);
return packet->padding;
}
/**
* gst_rtcp_packet_get_type:
* @packet: a valid #GstRTCPPacket
*
* Get the packet type of the packet pointed to by @packet.
*
* Returns: The packet type or GST_RTCP_TYPE_INVALID when @packet is not
* pointing to a valid packet.
*/
GstRTCPType
gst_rtcp_packet_get_type (GstRTCPPacket * packet)
{
g_return_val_if_fail (packet != NULL, GST_RTCP_TYPE_INVALID);
return packet->type;
}
/**
* gst_rtcp_packet_get_count:
* @packet: a valid #GstRTCPPacket
*
* Get the count field in @packet.
*
* Returns: The count field in @packet or -1 if @packet does not point to a
* valid packet.
*/
guint8
gst_rtcp_packet_get_count (GstRTCPPacket * packet)
{
g_return_val_if_fail (packet != NULL, -1);
g_return_val_if_fail (packet->type != GST_RTCP_TYPE_INVALID, -1);
return packet->count;
}
/**
* gst_rtcp_packet_get_length:
* @packet: a valid #GstRTCPPacket
*
* Get the length field of @packet. This is the length of the packet in
* 32-bit words minus one.
*
* Returns: The length field of @packet.
*/
guint16
gst_rtcp_packet_get_length (GstRTCPPacket * packet)
{
g_return_val_if_fail (packet != NULL, 0);
g_return_val_if_fail (packet->type != GST_RTCP_TYPE_INVALID, 0);
return packet->length;
}
/**
* gst_rtcp_packet_sr_get_sender_info:
* @packet: a valid SR #GstRTCPPacket
* @ssrc: (out): result SSRC
* @ntptime: (out): result NTP time
* @rtptime: (out): result RTP time
* @packet_count: (out): result packet count
* @octet_count: (out): result octet count
*
* Parse the SR sender info and store the values.
*/
void
gst_rtcp_packet_sr_get_sender_info (GstRTCPPacket * packet, guint32 * ssrc,
guint64 * ntptime, guint32 * rtptime, guint32 * packet_count,
guint32 * octet_count)
{
guint8 *data;
g_return_if_fail (packet != NULL);
g_return_if_fail (packet->type == GST_RTCP_TYPE_SR);
g_return_if_fail (packet->rtcp != NULL);
g_return_if_fail (packet->rtcp->map.flags & GST_MAP_READ);
data = packet->rtcp->map.data;
/* skip header */
data += packet->offset + 4;
if (ssrc)
*ssrc = GST_READ_UINT32_BE (data);
data += 4;
if (ntptime)
*ntptime = GST_READ_UINT64_BE (data);
data += 8;
if (rtptime)
*rtptime = GST_READ_UINT32_BE (data);
data += 4;
if (packet_count)
*packet_count = GST_READ_UINT32_BE (data);
data += 4;
if (octet_count)
*octet_count = GST_READ_UINT32_BE (data);
}
/**
* gst_rtcp_packet_sr_set_sender_info:
* @packet: a valid SR #GstRTCPPacket
* @ssrc: the SSRC
* @ntptime: the NTP time
* @rtptime: the RTP time
* @packet_count: the packet count
* @octet_count: the octet count
*
* Set the given values in the SR packet @packet.
*/
void
gst_rtcp_packet_sr_set_sender_info (GstRTCPPacket * packet, guint32 ssrc,
guint64 ntptime, guint32 rtptime, guint32 packet_count, guint32 octet_count)
{
guint8 *data;
g_return_if_fail (packet != NULL);
g_return_if_fail (packet->type == GST_RTCP_TYPE_SR);
g_return_if_fail (packet->rtcp != NULL);
g_return_if_fail (packet->rtcp->map.flags & GST_MAP_WRITE);
data = packet->rtcp->map.data;
/* skip header */
data += packet->offset + 4;
GST_WRITE_UINT32_BE (data, ssrc);
data += 4;
GST_WRITE_UINT64_BE (data, ntptime);
data += 8;
GST_WRITE_UINT32_BE (data, rtptime);
data += 4;
GST_WRITE_UINT32_BE (data, packet_count);
data += 4;
GST_WRITE_UINT32_BE (data, octet_count);
}
/**
* gst_rtcp_packet_rr_get_ssrc:
* @packet: a valid RR #GstRTCPPacket
*
* Get the ssrc field of the RR @packet.
*
* Returns: the ssrc.
*/
guint32
gst_rtcp_packet_rr_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_RR, 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_rr_set_ssrc:
* @packet: a valid RR #GstRTCPPacket
* @ssrc: the SSRC to set
*
* Set the ssrc field of the RR @packet.
*/
void
gst_rtcp_packet_rr_set_ssrc (GstRTCPPacket * packet, guint32 ssrc)
{
guint8 *data;
g_return_if_fail (packet != NULL);
g_return_if_fail (packet->type == GST_RTCP_TYPE_RR);
g_return_if_fail (packet->rtcp != NULL);
g_return_if_fail (packet->rtcp->map.flags & GST_MAP_WRITE);
data = packet->rtcp->map.data;
/* skip header */
data += packet->offset + 4;
GST_WRITE_UINT32_BE (data, ssrc);
}
/**
* gst_rtcp_packet_get_rb_count:
* @packet: a valid SR or RR #GstRTCPPacket
*
* Get the number of report blocks in @packet.
*
* Returns: The number of report blocks in @packet.
*/
guint
gst_rtcp_packet_get_rb_count (GstRTCPPacket * packet)
{
g_return_val_if_fail (packet != NULL, 0);
g_return_val_if_fail (packet->type == GST_RTCP_TYPE_RR ||
packet->type == GST_RTCP_TYPE_SR, 0);
g_return_val_if_fail (packet->rtcp != NULL, 0);
g_return_val_if_fail (packet->rtcp->map.flags & GST_MAP_READ, 0);
return packet->count;
}
/**
* gst_rtcp_packet_get_rb:
* @packet: a valid SR or RR #GstRTCPPacket
* @nth: the nth report block in @packet
* @ssrc: (out): result for data source being reported
* @fractionlost: (out): result for fraction lost since last SR/RR
* @packetslost: (out): result for the cumululative number of packets lost
* @exthighestseq: (out): result for the extended last sequence number received
* @jitter: (out): result for the interarrival jitter
* @lsr: (out): result for the last SR packet from this source
* @dlsr: (out): result for the delay since last SR packet
*
* Parse the values of the @nth report block in @packet and store the result in
* the values.
*/
void
gst_rtcp_packet_get_rb (GstRTCPPacket * packet, guint nth, guint32 * ssrc,
guint8 * fractionlost, gint32 * packetslost, guint32 * exthighestseq,
guint32 * jitter, guint32 * lsr, guint32 * dlsr)
{
guint offset;
guint8 *data;
guint32 tmp;
g_return_if_fail (packet != NULL);
g_return_if_fail (packet->type == GST_RTCP_TYPE_RR ||
packet->type == GST_RTCP_TYPE_SR);
g_return_if_fail (packet->rtcp != NULL);
g_return_if_fail (packet->rtcp->map.flags & GST_MAP_READ);
g_return_if_fail (nth < packet->count);
/* get offset in 32-bits words into packet, skip the header */
if (packet->type == GST_RTCP_TYPE_RR)
offset = 2;
else
offset = 7;
/* move to requested index */
offset += (nth * 6);
/* check that we don't go past the packet length */
if (offset > packet->length)
return;
/* scale to bytes */
offset <<= 2;
offset += packet->offset;
/* check if the packet is valid */
if (offset + 24 > packet->rtcp->map.size)
return;
data = packet->rtcp->map.data;
data += offset;
if (ssrc)
*ssrc = GST_READ_UINT32_BE (data);
data += 4;
tmp = GST_READ_UINT32_BE (data);
if (fractionlost)
*fractionlost = (tmp >> 24);
if (packetslost) {
/* sign extend */
if (tmp & 0x00800000)
tmp |= 0xff000000;
else
tmp &= 0x00ffffff;
*packetslost = (gint32) tmp;
}
data += 4;
if (exthighestseq)
*exthighestseq = GST_READ_UINT32_BE (data);
data += 4;
if (jitter)
*jitter = GST_READ_UINT32_BE (data);
data += 4;
if (lsr)
*lsr = GST_READ_UINT32_BE (data);
data += 4;
if (dlsr)
*dlsr = GST_READ_UINT32_BE (data);
}
/**
* gst_rtcp_packet_add_rb:
* @packet: a valid SR or RR #GstRTCPPacket
* @ssrc: data source being reported
* @fractionlost: fraction lost since last SR/RR
* @packetslost: the cumululative number of packets lost
* @exthighestseq: the extended last sequence number received
* @jitter: the interarrival jitter
* @lsr: the last SR packet from this source
* @dlsr: the delay since last SR packet
*
* Add a new report block to @packet with the given values.
*
* Returns: %TRUE if the packet was created. This function can return %FALSE if
* the max MTU is exceeded or the number of report blocks is greater than
* #GST_RTCP_MAX_RB_COUNT.
*/
gboolean
gst_rtcp_packet_add_rb (GstRTCPPacket * packet, guint32 ssrc,
guint8 fractionlost, gint32 packetslost, guint32 exthighestseq,
guint32 jitter, guint32 lsr, guint32 dlsr)
{
guint8 *data;
guint maxsize, offset;
g_return_val_if_fail (packet != NULL, FALSE);
g_return_val_if_fail (packet->type == GST_RTCP_TYPE_RR ||
packet->type == GST_RTCP_TYPE_SR, FALSE);
g_return_val_if_fail (packet->rtcp != NULL, FALSE);
g_return_val_if_fail (packet->rtcp->map.flags & GST_MAP_WRITE, FALSE);
/* if profile-specific extension is added, fail for now!? */
g_return_val_if_fail (gst_rtcp_packet_get_profile_specific_ext_length (packet)
== 0, FALSE);
if (packet->count >= GST_RTCP_MAX_RB_COUNT)
goto no_space;
data = packet->rtcp->map.data;
maxsize = packet->rtcp->map.maxsize;
/* skip header */
offset = packet->offset + 4;
if (packet->type == GST_RTCP_TYPE_RR)
offset += 4;
else
offset += 24;
/* move to current index */
offset += (packet->count * 24);
/* we need 24 free bytes now */
if (offset + 24 >= maxsize)
goto no_space;
/* increment packet count and length */
packet->count++;
data[packet->offset]++;
packet->length += 6;
data[packet->offset + 2] = (packet->length) >> 8;
data[packet->offset + 3] = (packet->length) & 0xff;
packet->rtcp->map.size += 6 * 4;
/* move to new report block offset */
data += offset;
GST_WRITE_UINT32_BE (data, ssrc);
data += 4;
GST_WRITE_UINT32_BE (data,
((guint32) fractionlost << 24) | (packetslost & 0xffffff));
data += 4;
GST_WRITE_UINT32_BE (data, exthighestseq);
data += 4;
GST_WRITE_UINT32_BE (data, jitter);
data += 4;
GST_WRITE_UINT32_BE (data, lsr);
data += 4;
GST_WRITE_UINT32_BE (data, dlsr);
return TRUE;
no_space:
{
return FALSE;
}
}
/**
* gst_rtcp_packet_set_rb:
* @packet: a valid SR or RR #GstRTCPPacket
* @nth: the nth report block to set
* @ssrc: data source being reported
* @fractionlost: fraction lost since last SR/RR
* @packetslost: the cumululative number of packets lost
* @exthighestseq: the extended last sequence number received
* @jitter: the interarrival jitter
* @lsr: the last SR packet from this source
* @dlsr: the delay since last SR packet
*
* Set the @nth new report block in @packet with the given values.
*
* Note: Not implemented.
*/
void
gst_rtcp_packet_set_rb (GstRTCPPacket * packet, guint nth, guint32 ssrc,
guint8 fractionlost, gint32 packetslost, guint32 exthighestseq,
guint32 jitter, guint32 lsr, guint32 dlsr)
{
g_return_if_fail (packet != NULL);
g_return_if_fail (packet->type == GST_RTCP_TYPE_RR ||
packet->type == GST_RTCP_TYPE_SR);
g_return_if_fail (packet->rtcp != NULL);
g_return_if_fail (packet->rtcp->map.flags & GST_MAP_WRITE);
g_warning ("not implemented");
}
/**
* gst_rtcp_packet_add_profile_specific_ext:
* @packet: a valid SR or RR #GstRTCPPacket
* @data: (array length=len) (transfer none): profile-specific data
* @len: length of the profile-specific data in bytes
*
* Add profile-specific extension @data to @packet. If @packet already
* contains profile-specific extension @data will be appended to the existing
* extension.
*
* Returns: %TRUE if the profile specific extension data was added.
*/
gboolean
gst_rtcp_packet_add_profile_specific_ext (GstRTCPPacket * packet,
const guint8 * data, guint len)
{
guint8 *bdata;
guint maxsize, offset;
g_return_val_if_fail (packet != NULL, FALSE);
g_return_val_if_fail (packet->type == GST_RTCP_TYPE_RR ||
packet->type == GST_RTCP_TYPE_SR, FALSE);
g_return_val_if_fail (packet->rtcp != NULL, FALSE);
g_return_val_if_fail (packet->rtcp->map.flags & GST_MAP_WRITE, FALSE);
g_return_val_if_fail ((len & 0x03) == 0, FALSE);
bdata = packet->rtcp->map.data;
maxsize = packet->rtcp->map.maxsize;
/* skip to the end of the packet */
offset = packet->offset + (packet->length << 2) + 4;
/* we need 'len' free bytes now */
if (G_UNLIKELY (offset + len > maxsize))
return FALSE;
memcpy (&bdata[offset], data, len);
packet->length += len >> 2;
bdata[packet->offset + 2] = (packet->length) >> 8;
bdata[packet->offset + 3] = (packet->length) & 0xff;
packet->rtcp->map.size += len;
return TRUE;
}
/**
* gst_rtcp_packet_get_profile_specific_ext_length:
* @packet: a valid SR or RR #GstRTCPPacket
*
* Returns: The number of 32-bit words containing profile-specific extension
* data from @packet.
*/
guint16
gst_rtcp_packet_get_profile_specific_ext_length (GstRTCPPacket * packet)
{
guint pse_offset = 2;
g_return_val_if_fail (packet != NULL, 0);
g_return_val_if_fail (packet->type == GST_RTCP_TYPE_RR ||
packet->type == GST_RTCP_TYPE_SR, 0);
g_return_val_if_fail (packet->rtcp != NULL, 0);
g_return_val_if_fail (packet->rtcp->map.flags & GST_MAP_READ, 0);
if (packet->type == GST_RTCP_TYPE_SR)
pse_offset += 5;
pse_offset += (packet->count * 6);
if (pse_offset <= (packet->length + 1))
return packet->length + 1 - pse_offset;
/* This means that the packet is invalid! */
return 0;
}
/**
* gst_rtcp_packet_get_profile_specific_ext:
* @packet: a valid SR or RR #GstRTCPPacket
* @data: (out) (array length=len) (transfer none): result profile-specific data
* @len: (out): result length of the profile-specific data
*
* Returns: %TRUE if there was valid data.
*/
gboolean
gst_rtcp_packet_get_profile_specific_ext (GstRTCPPacket * packet,
guint8 ** data, guint * len)
{
guint16 pse_len;
g_return_val_if_fail (packet != NULL, FALSE);
g_return_val_if_fail (packet->type == GST_RTCP_TYPE_RR ||
packet->type == GST_RTCP_TYPE_SR, FALSE);
g_return_val_if_fail (packet->rtcp != NULL, FALSE);
g_return_val_if_fail (packet->rtcp->map.flags & GST_MAP_READ, FALSE);
pse_len = gst_rtcp_packet_get_profile_specific_ext_length (packet);
if (pse_len > 0) {
if (len != NULL)
*len = pse_len * sizeof (guint32);
if (data != NULL) {
*data = packet->rtcp->map.data;
*data += packet->offset;
*data += ((packet->length + 1 - pse_len) * sizeof (guint32));
}
return TRUE;
}
return FALSE;
}
/**
* gst_rtcp_packet_copy_profile_specific_ext:
* @packet: a valid SR or RR #GstRTCPPacket
* @data: (out) (array length=len): result profile-specific data
* @len: (out): length of the profile-specific extension data
*
* The profile-specific extension data is copied into a new allocated
* memory area @data. This must be freed with g_free() after usage.
*
* Returns: %TRUE if there was valid data.
*/
gboolean
gst_rtcp_packet_copy_profile_specific_ext (GstRTCPPacket * packet,
guint8 ** data, guint * len)
{
guint16 pse_len;
g_return_val_if_fail (packet != NULL, FALSE);
g_return_val_if_fail (packet->type == GST_RTCP_TYPE_RR ||
packet->type == GST_RTCP_TYPE_SR, FALSE);
g_return_val_if_fail (packet->rtcp != NULL, FALSE);
g_return_val_if_fail (packet->rtcp->map.flags & GST_MAP_READ, FALSE);
pse_len = gst_rtcp_packet_get_profile_specific_ext_length (packet);
if (pse_len > 0) {
if (len != NULL)
*len = pse_len * sizeof (guint32);
if (data != NULL) {
guint8 *ptr = packet->rtcp->map.data + packet->offset;
ptr += ((packet->length + 1 - pse_len) * sizeof (guint32));
*data = g_memdup (ptr, pse_len * sizeof (guint32));
}
return TRUE;
}
return FALSE;
}
/**
* gst_rtcp_packet_sdes_get_item_count:
* @packet: a valid SDES #GstRTCPPacket
*
* Get the number of items in the SDES packet @packet.
*
* Returns: The number of items in @packet.
*/
guint
gst_rtcp_packet_sdes_get_item_count (GstRTCPPacket * packet)
{
g_return_val_if_fail (packet != NULL, 0);
g_return_val_if_fail (packet->type == GST_RTCP_TYPE_SDES, 0);
return packet->count;
}
/**
* gst_rtcp_packet_sdes_first_item:
* @packet: a valid SDES #GstRTCPPacket
*
* Move to the first SDES item in @packet.
*
* Returns: TRUE if there was a first item.
*/
gboolean
gst_rtcp_packet_sdes_first_item (GstRTCPPacket * packet)
{
g_return_val_if_fail (packet != NULL, FALSE);
g_return_val_if_fail (packet->type == GST_RTCP_TYPE_SDES, FALSE);
packet->item_offset = 4;
packet->item_count = 0;
packet->entry_offset = 4;
if (packet->count == 0)
return FALSE;
return TRUE;
}
/**
* gst_rtcp_packet_sdes_next_item:
* @packet: a valid SDES #GstRTCPPacket
*
* Move to the next SDES item in @packet.
*
* Returns: TRUE if there was a next item.
*/
gboolean
gst_rtcp_packet_sdes_next_item (GstRTCPPacket * packet)
{
guint8 *data;
guint offset;
guint len;
g_return_val_if_fail (packet != NULL, FALSE);
g_return_val_if_fail (packet->type == GST_RTCP_TYPE_SDES, FALSE);
g_return_val_if_fail (packet->rtcp != NULL, FALSE);
g_return_val_if_fail (packet->rtcp->map.flags & GST_MAP_READ, FALSE);
/* if we are at the last item, we are done */
if (packet->item_count == packet->count)
return FALSE;
/* move to SDES */
data = packet->rtcp->map.data;
data += packet->offset;
/* move to item */
offset = packet->item_offset;
/* skip SSRC */
offset += 4;
/* don't overrun */
len = (packet->length << 2);
while (offset < len) {
if (data[offset] == 0) {
/* end of list, round to next 32-bit word */
offset = (offset + 4) & ~3;
break;
}
offset += data[offset + 1] + 2;
}
if (offset >= len)
return FALSE;
packet->item_offset = offset;
packet->item_count++;
packet->entry_offset = 4;
return TRUE;
}
/**
* gst_rtcp_packet_sdes_get_ssrc:
* @packet: a valid SDES #GstRTCPPacket
*
* Get the SSRC of the current SDES item.
*
* Returns: the SSRC of the current item.
*/
guint32
gst_rtcp_packet_sdes_get_ssrc (GstRTCPPacket * packet)
{
guint32 ssrc;
guint8 *data;
g_return_val_if_fail (packet != NULL, 0);
g_return_val_if_fail (packet->type == GST_RTCP_TYPE_SDES, 0);
g_return_val_if_fail (packet->rtcp != NULL, 0);
g_return_val_if_fail (packet->rtcp->map.flags & GST_MAP_READ, 0);
/* move to SDES */
data = packet->rtcp->map.data;
data += packet->offset;
/* move to item */
data += packet->item_offset;
ssrc = GST_READ_UINT32_BE (data);
return ssrc;
}
/**
* gst_rtcp_packet_sdes_first_entry:
* @packet: a valid SDES #GstRTCPPacket
*
* Move to the first SDES entry in the current item.
*
* Returns: %TRUE if there was a first entry.
*/
gboolean
gst_rtcp_packet_sdes_first_entry (GstRTCPPacket * packet)
{
guint8 *data;
guint len, offset;
g_return_val_if_fail (packet != NULL, FALSE);
g_return_val_if_fail (packet->type == GST_RTCP_TYPE_SDES, FALSE);
g_return_val_if_fail (packet->rtcp != NULL, FALSE);
g_return_val_if_fail (packet->rtcp->map.flags & GST_MAP_READ, FALSE);
/* move to SDES */
data = packet->rtcp->map.data;
data += packet->offset;
/* move to item */
offset = packet->item_offset;
/* skip SSRC */
offset += 4;
packet->entry_offset = 4;
/* don't overrun */
len = (packet->length << 2);
if (offset >= len)
return FALSE;
if (data[offset] == 0)
return FALSE;
return TRUE;
}
/**
* gst_rtcp_packet_sdes_next_entry:
* @packet: a valid SDES #GstRTCPPacket
*
* Move to the next SDES entry in the current item.
*
* Returns: %TRUE if there was a next entry.
*/
gboolean
gst_rtcp_packet_sdes_next_entry (GstRTCPPacket * packet)
{
guint8 *data;
guint len, offset, item_len;
g_return_val_if_fail (packet != NULL, FALSE);
g_return_val_if_fail (packet->type == GST_RTCP_TYPE_SDES, FALSE);
g_return_val_if_fail (packet->rtcp != NULL, FALSE);
g_return_val_if_fail (packet->rtcp->map.flags & GST_MAP_READ, FALSE);
/* move to SDES */
data = packet->rtcp->map.data;
data += packet->offset;
/* move to item */
offset = packet->item_offset;
/* move to entry */
offset += packet->entry_offset;
item_len = data[offset + 1] + 2;
/* skip item */
offset += item_len;
/* don't overrun */
len = (packet->length << 2);
if (offset >= len)
return FALSE;
packet->entry_offset += item_len;
/* check for end of list */
if (data[offset] == 0)
return FALSE;
return TRUE;
}
/**
* gst_rtcp_packet_sdes_get_entry:
* @packet: a valid SDES #GstRTCPPacket
* @type: result of the entry type
* @len: (out): result length of the entry data
* @data: (out) (array length=len) (transfer none): result entry data
*
* Get the data of the current SDES item entry. @type (when not NULL) will
* contain the type of the entry. @data (when not NULL) will point to @len
* bytes.
*
* When @type refers to a text item, @data will point to a UTF8 string. Note
* that this UTF8 string is NOT null-terminated. Use
* gst_rtcp_packet_sdes_copy_entry() to get a null-terminated copy of the entry.
*
* Returns: %TRUE if there was valid data.
*/
gboolean
gst_rtcp_packet_sdes_get_entry (GstRTCPPacket * packet,
GstRTCPSDESType * type, guint8 * len, guint8 ** data)
{
guint8 *bdata;
guint offset;
g_return_val_if_fail (packet != NULL, FALSE);
g_return_val_if_fail (packet->type == GST_RTCP_TYPE_SDES, FALSE);
g_return_val_if_fail (packet->rtcp != NULL, FALSE);
g_return_val_if_fail (packet->rtcp->map.flags & GST_MAP_READ, FALSE);
/* move to SDES */
bdata = packet->rtcp->map.data;
bdata += packet->offset;
/* move to item */
offset = packet->item_offset;
/* move to entry */
offset += packet->entry_offset;
if (bdata[offset] == 0)
return FALSE;
if (type)
*type = bdata[offset];
if (len)
*len = bdata[offset + 1];
if (data)
*data = &bdata[offset + 2];
return TRUE;
}
/**
* gst_rtcp_packet_sdes_copy_entry:
* @packet: a valid SDES #GstRTCPPacket
* @type: result of the entry type
* @len: (out): result length of the entry data
* @data: (out) (array length=len): result entry data
*
* This function is like gst_rtcp_packet_sdes_get_entry() but it returns a
* null-terminated copy of the data instead. use g_free() after usage.
*
* Returns: %TRUE if there was valid data.
*/
gboolean
gst_rtcp_packet_sdes_copy_entry (GstRTCPPacket * packet,
GstRTCPSDESType * type, guint8 * len, guint8 ** data)
{
guint8 *tdata;
guint8 tlen;
g_return_val_if_fail (packet != NULL, FALSE);
g_return_val_if_fail (packet->type == GST_RTCP_TYPE_SDES, FALSE);
g_return_val_if_fail (packet->rtcp != NULL, FALSE);
g_return_val_if_fail (packet->rtcp->map.flags & GST_MAP_READ, FALSE);
if (!gst_rtcp_packet_sdes_get_entry (packet, type, &tlen, &tdata))
return FALSE;
if (len)
*len = tlen;
if (data)
*data = (guint8 *) g_strndup ((gchar *) tdata, tlen);
return TRUE;
}
/**
* gst_rtcp_packet_sdes_add_item:
* @packet: a valid SDES #GstRTCPPacket
* @ssrc: the SSRC of the new item to add
*
* Add a new SDES item for @ssrc to @packet.
*
* Returns: %TRUE if the item could be added, %FALSE if the maximum amount of
* items has been exceeded for the SDES packet or the MTU has been reached.
*/
gboolean
gst_rtcp_packet_sdes_add_item (GstRTCPPacket * packet, guint32 ssrc)
{
guint8 *data;
guint offset;
gsize maxsize;
g_return_val_if_fail (packet != NULL, FALSE);
g_return_val_if_fail (packet->type == GST_RTCP_TYPE_SDES, FALSE);
g_return_val_if_fail (packet->rtcp != NULL, FALSE);
g_return_val_if_fail (packet->rtcp->map.flags & GST_MAP_WRITE, FALSE);
/* increment item count when possible */
if (packet->count >= GST_RTCP_MAX_SDES_ITEM_COUNT)
goto no_space;
/* pretend there is a next packet for the next call */
packet->count++;
/* jump over current item */
gst_rtcp_packet_sdes_next_item (packet);
/* move to SDES */
data = packet->rtcp->map.data;
maxsize = packet->rtcp->map.maxsize;
data += packet->offset;
/* move to current item */
offset = packet->item_offset;
/* we need 2 free words now */
if (offset + 8 >= maxsize)
goto no_next;
/* write SSRC */
GST_WRITE_UINT32_BE (&data[offset], ssrc);
/* write 0 entry with padding */
GST_WRITE_UINT32_BE (&data[offset + 4], 0);
/* update count */
data[0] = (data[0] & 0xe0) | packet->count;
/* update length, we added 2 words */
packet->length += 2;
data[2] = (packet->length) >> 8;
data[3] = (packet->length) & 0xff;
packet->rtcp->map.size += 8;
return TRUE;
/* ERRORS */
no_space:
{
return FALSE;
}
no_next:
{
packet->count--;
return FALSE;
}
}
/**
* gst_rtcp_packet_sdes_add_entry:
* @packet: a valid SDES #GstRTCPPacket
* @type: the #GstRTCPSDESType of the SDES entry
* @len: the data length
* @data: (array length=len): the data
*
* Add a new SDES entry to the current item in @packet.
*
* Returns: %TRUE if the item could be added, %FALSE if the MTU has been
* reached.
*/
gboolean
gst_rtcp_packet_sdes_add_entry (GstRTCPPacket * packet, GstRTCPSDESType type,
guint8 len, const guint8 * data)
{
guint8 *bdata;
guint offset, padded;
gsize maxsize;
g_return_val_if_fail (packet != NULL, FALSE);
g_return_val_if_fail (packet->type == GST_RTCP_TYPE_SDES, FALSE);
g_return_val_if_fail (packet->rtcp != NULL, FALSE);
g_return_val_if_fail (packet->rtcp->map.flags & GST_MAP_WRITE, FALSE);
/* move to SDES */
bdata = packet->rtcp->map.data;
maxsize = packet->rtcp->map.maxsize;
bdata += packet->offset;
/* move to item */
offset = packet->item_offset;
/* move to entry */
offset += packet->entry_offset;
/* add 1 byte end and up to 3 bytes padding to fill a full 32 bit word */
padded = (offset + 2 + len + 1 + 3) & ~3;
/* we need enough space for type, len, data and padding */
if (packet->offset + padded >= maxsize)
goto no_space;
packet->rtcp->map.size = packet->offset + padded;
bdata[offset] = type;
bdata[offset + 1] = len;
memcpy (&bdata[offset + 2], data, len);
bdata[offset + 2 + len] = 0;
/* calculate new packet length */
packet->length = (padded - 4) >> 2;
bdata[2] = (packet->length) >> 8;
bdata[3] = (packet->length) & 0xff;
/* position to new next entry */
packet->entry_offset += 2 + len;
return TRUE;
/* ERRORS */
no_space:
{
return FALSE;
}
}
/**
* gst_rtcp_packet_bye_get_ssrc_count:
* @packet: a valid BYE #GstRTCPPacket
*
* Get the number of SSRC fields in @packet.
*
* Returns: The number of SSRC fields in @packet.
*/
guint
gst_rtcp_packet_bye_get_ssrc_count (GstRTCPPacket * packet)
{
g_return_val_if_fail (packet != NULL, -1);
g_return_val_if_fail (packet->type == GST_RTCP_TYPE_BYE, -1);
return packet->count;
}
/**
* gst_rtcp_packet_bye_get_nth_ssrc:
* @packet: a valid BYE #GstRTCPPacket
* @nth: the nth SSRC to get
*
* Get the @nth SSRC of the BYE @packet.
*
* Returns: The @nth SSRC of @packet.
*/
guint32
gst_rtcp_packet_bye_get_nth_ssrc (GstRTCPPacket * packet, guint nth)
{
guint8 *data;
guint offset;
guint32 ssrc;
g_return_val_if_fail (packet != NULL, 0);
g_return_val_if_fail (packet->type == GST_RTCP_TYPE_BYE, 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 (nth < packet->count, 0);
/* get offset in 32-bits words into packet, skip the header */
offset = 1 + nth;
/* check that we don't go past the packet length */
if (offset > packet->length)
return 0;
/* scale to bytes */
offset <<= 2;
offset += packet->offset;
/* check if the packet is valid */
if (offset + 4 > packet->rtcp->map.size)
return 0;
data = packet->rtcp->map.data;
data += offset;
ssrc = GST_READ_UINT32_BE (data);
return ssrc;
}
/**
* gst_rtcp_packet_bye_add_ssrc:
* @packet: a valid BYE #GstRTCPPacket
* @ssrc: an SSRC to add
*
* Add @ssrc to the BYE @packet.
*
* Returns: %TRUE if the ssrc was added. This function can return %FALSE if
* the max MTU is exceeded or the number of sources blocks is greater than
* #GST_RTCP_MAX_BYE_SSRC_COUNT.
*/
gboolean
gst_rtcp_packet_bye_add_ssrc (GstRTCPPacket * packet, guint32 ssrc)
{
guint8 *data;
gsize maxsize;
guint offset;
g_return_val_if_fail (packet != NULL, FALSE);
g_return_val_if_fail (packet->type == GST_RTCP_TYPE_BYE, FALSE);
g_return_val_if_fail (packet->rtcp != NULL, FALSE);
g_return_val_if_fail (packet->rtcp->map.flags & GST_MAP_WRITE, FALSE);
if (packet->count >= GST_RTCP_MAX_BYE_SSRC_COUNT)
goto no_space;
data = packet->rtcp->map.data;
maxsize = packet->rtcp->map.maxsize;
/* skip header */
offset = packet->offset + 4;
/* move to current index */
offset += (packet->count * 4);
if (offset + 4 >= maxsize)
goto no_space;
/* increment packet count and length */
packet->count++;
data[packet->offset]++;
packet->length += 1;
data[packet->offset + 2] = (packet->length) >> 8;
data[packet->offset + 3] = (packet->length) & 0xff;
packet->rtcp->map.size += 4;
/* move to new SSRC offset and write ssrc */
data += offset;
GST_WRITE_UINT32_BE (data, ssrc);
return TRUE;
/* ERRORS */
no_space:
{
return FALSE;
}
}
/**
* gst_rtcp_packet_bye_add_ssrcs:
* @packet: a valid BYE #GstRTCPPacket
* @ssrc: (array length=len) (transfer none): an array of SSRCs to add
* @len: number of elements in @ssrc
*
* Adds @len SSRCs in @ssrc to BYE @packet.
*
* Returns: %TRUE if the all the SSRCs were added. This function can return %FALSE if
* the max MTU is exceeded or the number of sources blocks is greater than
* #GST_RTCP_MAX_BYE_SSRC_COUNT.
*/
gboolean
gst_rtcp_packet_bye_add_ssrcs (GstRTCPPacket * packet, guint32 * ssrc,
guint len)
{
guint i;
gboolean res;
g_return_val_if_fail (packet != NULL, FALSE);
g_return_val_if_fail (packet->type == GST_RTCP_TYPE_BYE, FALSE);
g_return_val_if_fail (packet->rtcp != NULL, FALSE);
g_return_val_if_fail (packet->rtcp->map.flags & GST_MAP_WRITE, FALSE);
res = TRUE;
for (i = 0; i < len && res; i++) {
res = gst_rtcp_packet_bye_add_ssrc (packet, ssrc[i]);
}
return res;
}
/* get the offset in packet of the reason length */
static guint
get_reason_offset (GstRTCPPacket * packet)
{
guint offset;
/* get amount of sources plus header */
offset = 1 + packet->count;
/* check that we don't go past the packet length */
if (offset > packet->length)
return 0;
/* scale to bytes */
offset <<= 2;
offset += packet->offset;
/* check if the packet is valid */
if (offset + 1 > packet->rtcp->map.size)
return 0;
return offset;
}
/**
* gst_rtcp_packet_bye_get_reason_len:
* @packet: a valid BYE #GstRTCPPacket
*
* Get the length of the reason string.
*
* Returns: The length of the reason string or 0 when there is no reason string
* present.
*/
guint8
gst_rtcp_packet_bye_get_reason_len (GstRTCPPacket * packet)
{
guint8 *data;
guint roffset;
g_return_val_if_fail (packet != NULL, 0);
g_return_val_if_fail (packet->type == GST_RTCP_TYPE_BYE, 0);
g_return_val_if_fail (packet->rtcp != NULL, 0);
g_return_val_if_fail (packet->rtcp->map.flags & GST_MAP_READ, 0);
roffset = get_reason_offset (packet);
if (roffset == 0)
return 0;
data = packet->rtcp->map.data;
return data[roffset];
}
/**
* gst_rtcp_packet_bye_get_reason:
* @packet: a valid BYE #GstRTCPPacket
*
* Get the reason in @packet.
*
* Returns: The reason for the BYE @packet or NULL if the packet did not contain
* a reason string. The string must be freed with g_free() after usage.
*/
gchar *
gst_rtcp_packet_bye_get_reason (GstRTCPPacket * packet)
{
guint8 *data;
guint roffset;
guint8 len;
g_return_val_if_fail (packet != NULL, NULL);
g_return_val_if_fail (packet->type == GST_RTCP_TYPE_BYE, NULL);
g_return_val_if_fail (packet->rtcp != NULL, NULL);
g_return_val_if_fail (packet->rtcp->map.flags & GST_MAP_READ, NULL);
roffset = get_reason_offset (packet);
if (roffset == 0)
return NULL;
data = packet->rtcp->map.data;
/* get length of reason string */
len = data[roffset];
if (len == 0)
return NULL;
/* move to string */
roffset += 1;
/* check if enough data to copy */
if (roffset + len > packet->rtcp->map.size)
return NULL;
return g_strndup ((gconstpointer) (data + roffset), len);
}
/**
* gst_rtcp_packet_bye_set_reason:
* @packet: a valid BYE #GstRTCPPacket
* @reason: a reason string
*
* Set the reason string to @reason in @packet.
*
* Returns: TRUE if the string could be set.
*/
gboolean
gst_rtcp_packet_bye_set_reason (GstRTCPPacket * packet, const gchar * reason)
{
guint8 *data;
guint roffset;
gsize maxsize;
guint8 len, padded;
g_return_val_if_fail (packet != NULL, FALSE);
g_return_val_if_fail (packet->type == GST_RTCP_TYPE_BYE, FALSE);
g_return_val_if_fail (packet->rtcp != NULL, FALSE);
g_return_val_if_fail (packet->rtcp->map.flags & GST_MAP_WRITE, FALSE);
if (reason == NULL)
return TRUE;
len = strlen (reason);
if (len == 0)
return TRUE;
/* make room for the string before we get the offset */
packet->length++;
roffset = get_reason_offset (packet);
if (roffset == 0)
goto no_space;
data = packet->rtcp->map.data;
maxsize = packet->rtcp->map.maxsize;
/* we have 1 byte length and we need to pad to 4 bytes */
padded = ((len + 1) + 3) & ~3;
/* we need enough space for the padded length */
if (roffset + padded >= maxsize)
goto no_space;
data[roffset] = len;
memcpy (&data[roffset + 1], reason, len);
/* update packet length, we made room for 1 double word already */
packet->length += (padded >> 2) - 1;
data[packet->offset + 2] = (packet->length) >> 8;
data[packet->offset + 3] = (packet->length) & 0xff;
packet->rtcp->map.size += padded;
return TRUE;
/* ERRORS */
no_space:
{
packet->length--;
return FALSE;
}
}
/**
* gst_rtcp_packet_fb_get_sender_ssrc:
* @packet: a valid RTPFB or PSFB #GstRTCPPacket
*
* Get the sender SSRC field of the RTPFB or PSFB @packet.
*
* Returns: the sender SSRC.
*/
guint32
gst_rtcp_packet_fb_get_sender_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_RTPFB ||
packet->type == GST_RTCP_TYPE_PSFB), 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_fb_set_sender_ssrc:
* @packet: a valid RTPFB or PSFB #GstRTCPPacket
* @ssrc: a sender SSRC
*
* Set the sender SSRC field of the RTPFB or PSFB @packet.
*/
void
gst_rtcp_packet_fb_set_sender_ssrc (GstRTCPPacket * packet, guint32 ssrc)
{
guint8 *data;
g_return_if_fail (packet != NULL);
g_return_if_fail (packet->type == GST_RTCP_TYPE_RTPFB ||
packet->type == GST_RTCP_TYPE_PSFB);
g_return_if_fail (packet->rtcp != NULL);
g_return_if_fail (packet->rtcp->map.flags & GST_MAP_READ);
data = packet->rtcp->map.data;
/* skip header */
data += packet->offset + 4;
GST_WRITE_UINT32_BE (data, ssrc);
}
/**
* gst_rtcp_packet_fb_get_media_ssrc:
* @packet: a valid RTPFB or PSFB #GstRTCPPacket
*
* Get the media SSRC field of the RTPFB or PSFB @packet.
*
* Returns: the media SSRC.
*/
guint32
gst_rtcp_packet_fb_get_media_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_RTPFB ||
packet->type == GST_RTCP_TYPE_PSFB), 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 and sender ssrc */
data += packet->offset + 8;
ssrc = GST_READ_UINT32_BE (data);
return ssrc;
}
/**
* gst_rtcp_packet_fb_set_media_ssrc:
* @packet: a valid RTPFB or PSFB #GstRTCPPacket
* @ssrc: a media SSRC
*
* Set the media SSRC field of the RTPFB or PSFB @packet.
*/
void
gst_rtcp_packet_fb_set_media_ssrc (GstRTCPPacket * packet, guint32 ssrc)
{
guint8 *data;
g_return_if_fail (packet != NULL);
g_return_if_fail (packet->type == GST_RTCP_TYPE_RTPFB ||
packet->type == GST_RTCP_TYPE_PSFB);
g_return_if_fail (packet->rtcp != NULL);
g_return_if_fail (packet->rtcp->map.flags & GST_MAP_WRITE);
data = packet->rtcp->map.data;
/* skip header and sender ssrc */
data += packet->offset + 8;
GST_WRITE_UINT32_BE (data, ssrc);
}
/**
* gst_rtcp_packet_fb_get_type:
* @packet: a valid RTPFB or PSFB #GstRTCPPacket
*
* Get the feedback message type of the FB @packet.
*
* Returns: The feedback message type.
*/
GstRTCPFBType
gst_rtcp_packet_fb_get_type (GstRTCPPacket * packet)
{
g_return_val_if_fail (packet != NULL, GST_RTCP_FB_TYPE_INVALID);
g_return_val_if_fail (packet->type == GST_RTCP_TYPE_RTPFB ||
packet->type == GST_RTCP_TYPE_PSFB, GST_RTCP_FB_TYPE_INVALID);
return packet->count;
}
/**
* gst_rtcp_packet_fb_set_type:
* @packet: a valid RTPFB or PSFB #GstRTCPPacket
* @type: the #GstRTCPFBType to set
*
* Set the feedback message type of the FB @packet.
*/
void
gst_rtcp_packet_fb_set_type (GstRTCPPacket * packet, GstRTCPFBType type)
{
guint8 *data;
g_return_if_fail (packet != NULL);
g_return_if_fail (packet->type == GST_RTCP_TYPE_RTPFB ||
packet->type == GST_RTCP_TYPE_PSFB);
g_return_if_fail (packet->rtcp != NULL);
g_return_if_fail (packet->rtcp->map.flags & GST_MAP_WRITE);
data = packet->rtcp->map.data;
data[packet->offset] = (data[packet->offset] & 0xe0) | type;
packet->count = type;
}
/**
* gst_rtcp_ntp_to_unix:
* @ntptime: an NTP timestamp
*
* Converts an NTP time to UNIX nanoseconds. @ntptime can typically be
* the NTP time of an SR RTCP message and contains, in the upper 32 bits, the
* number of seconds since 1900 and, in the lower 32 bits, the fractional
* seconds. The resulting value will be the number of nanoseconds since 1970.
*
* Returns: the UNIX time for @ntptime in nanoseconds.
*/
guint64
gst_rtcp_ntp_to_unix (guint64 ntptime)
{
guint64 unixtime;
/* conversion from NTP timestamp (seconds since 1900) to seconds since
* 1970. */
unixtime = ntptime - (G_GUINT64_CONSTANT (2208988800) << 32);
/* conversion to nanoseconds */
unixtime =
gst_util_uint64_scale (unixtime, GST_SECOND,
(G_GINT64_CONSTANT (1) << 32));
return unixtime;
}
/**
* gst_rtcp_unix_to_ntp:
* @unixtime: an UNIX timestamp in nanoseconds
*
* Converts a UNIX timestamp in nanoseconds to an NTP time. The caller should
* pass a value with nanoseconds since 1970. The NTP time will, in the upper
* 32 bits, contain the number of seconds since 1900 and, in the lower 32
* bits, the fractional seconds. The resulting value can be used as an ntptime
* for constructing SR RTCP packets.
*
* Returns: the NTP time for @unixtime.
*/
guint64
gst_rtcp_unix_to_ntp (guint64 unixtime)
{
guint64 ntptime;
/* convert clock time to NTP time. upper 32 bits should contain the seconds
* and the lower 32 bits, the fractions of a second. */
ntptime =
gst_util_uint64_scale (unixtime, (G_GINT64_CONSTANT (1) << 32),
GST_SECOND);
/* conversion from UNIX timestamp (seconds since 1970) to NTP (seconds
* since 1900). */
ntptime += (G_GUINT64_CONSTANT (2208988800) << 32);
return ntptime;
}
/**
* gst_rtcp_sdes_type_to_name:
* @type: a #GstRTCPSDESType
*
* Converts @type to the string equivalent. The string is typically used as a
* key in a #GstStructure containing SDES items.
*
* Returns: the string equivalent of @type
*/
const gchar *
gst_rtcp_sdes_type_to_name (GstRTCPSDESType type)
{
const gchar *result;
switch (type) {
case GST_RTCP_SDES_CNAME:
result = "cname";
break;
case GST_RTCP_SDES_NAME:
result = "name";
break;
case GST_RTCP_SDES_EMAIL:
result = "email";
break;
case GST_RTCP_SDES_PHONE:
result = "phone";
break;
case GST_RTCP_SDES_LOC:
result = "location";
break;
case GST_RTCP_SDES_TOOL:
result = "tool";
break;
case GST_RTCP_SDES_NOTE:
result = "note";
break;
case GST_RTCP_SDES_PRIV:
result = "priv";
break;
default:
result = NULL;
break;
}
return result;
}
/**
* gst_rtcp_sdes_name_to_type:
* @name: a SDES name
*
* Convert @name into a @GstRTCPSDESType. @name is typically a key in a
* #GstStructure containing SDES items.
*
* Returns: the #GstRTCPSDESType for @name or #GST_RTCP_SDES_PRIV when @name
* is a private sdes item.
*/
GstRTCPSDESType
gst_rtcp_sdes_name_to_type (const gchar * name)
{
if (name == NULL || strlen (name) == 0)
return GST_RTCP_SDES_INVALID;
if (strcmp ("cname", name) == 0)
return GST_RTCP_SDES_CNAME;
if (strcmp ("name", name) == 0)
return GST_RTCP_SDES_NAME;
if (strcmp ("email", name) == 0)
return GST_RTCP_SDES_EMAIL;
if (strcmp ("phone", name) == 0)
return GST_RTCP_SDES_PHONE;
if (strcmp ("location", name) == 0)
return GST_RTCP_SDES_LOC;
if (strcmp ("tool", name) == 0)
return GST_RTCP_SDES_TOOL;
if (strcmp ("note", name) == 0)
return GST_RTCP_SDES_NOTE;
return GST_RTCP_SDES_PRIV;
}
/**
* gst_rtcp_packet_fb_get_fci_length:
* @packet: a valid RTPFB or PSFB #GstRTCPPacket
*
* Get the length of the Feedback Control Information attached to a
* RTPFB or PSFB @packet.
*
* Returns: The length of the FCI in 32-bit words.
*/
guint16
gst_rtcp_packet_fb_get_fci_length (GstRTCPPacket * packet)
{
guint8 *data;
g_return_val_if_fail (packet != NULL, 0);
g_return_val_if_fail (packet->type == GST_RTCP_TYPE_RTPFB ||
packet->type == GST_RTCP_TYPE_PSFB, 0);
g_return_val_if_fail (packet->rtcp != NULL, 0);
g_return_val_if_fail (packet->rtcp->map.flags & GST_MAP_READ, 0);
data = packet->rtcp->map.data + packet->offset + 2;
return GST_READ_UINT16_BE (data) - 2;
}
/**
* gst_rtcp_packet_fb_set_fci_length:
* @packet: a valid RTPFB or PSFB #GstRTCPPacket
* @wordlen: Length of the FCI in 32-bit words
*
* Set the length of the Feedback Control Information attached to a
* RTPFB or PSFB @packet.
*
* Returns: %TRUE if there was enough space in the packet to add this much FCI
*/
gboolean
gst_rtcp_packet_fb_set_fci_length (GstRTCPPacket * packet, guint16 wordlen)
{
guint8 *data;
g_return_val_if_fail (packet != NULL, FALSE);
g_return_val_if_fail (packet->type == GST_RTCP_TYPE_RTPFB ||
packet->type == GST_RTCP_TYPE_PSFB, FALSE);
g_return_val_if_fail (packet->rtcp != NULL, FALSE);
g_return_val_if_fail (packet->rtcp->map.flags & GST_MAP_WRITE, FALSE);
if (packet->rtcp->map.maxsize < packet->offset + ((wordlen + 3) * 4))
return FALSE;
data = packet->rtcp->map.data + packet->offset + 2;
wordlen += 2;
GST_WRITE_UINT16_BE (data, wordlen);
packet->rtcp->map.size = packet->offset + ((wordlen + 1) * 4);
return TRUE;
}
/**
* gst_rtcp_packet_fb_get_fci:
* @packet: a valid RTPFB or PSFB #GstRTCPPacket
*
* Get the Feedback Control Information attached to a RTPFB or PSFB @packet.
*
* Returns: a pointer to the FCI
*/
guint8 *
gst_rtcp_packet_fb_get_fci (GstRTCPPacket * packet)
{
guint8 *data;
g_return_val_if_fail (packet != NULL, NULL);
g_return_val_if_fail (packet->type == GST_RTCP_TYPE_RTPFB ||
packet->type == GST_RTCP_TYPE_PSFB, NULL);
g_return_val_if_fail (packet->rtcp != NULL, NULL);
g_return_val_if_fail (packet->rtcp->map.flags & GST_MAP_READ, NULL);
data = packet->rtcp->map.data + packet->offset;
if (GST_READ_UINT16_BE (data + 2) <= 2)
return NULL;
return data + 12;
}
/**
* gst_rtcp_packet_app_set_subtype:
* @packet: a valid APP #GstRTCPPacket
* @subtype: subtype of the packet
*
* Set the subtype field of the APP @packet.
*
* Since: 1.10
**/
void
gst_rtcp_packet_app_set_subtype (GstRTCPPacket * packet, guint8 subtype)
{
guint8 *data;
g_return_if_fail (packet != NULL);
g_return_if_fail (packet->type == GST_RTCP_TYPE_APP);
g_return_if_fail (packet->rtcp != NULL);
g_return_if_fail (packet->rtcp->map.flags & GST_MAP_WRITE);
data = packet->rtcp->map.data + packet->offset;
data[0] = (data[0] & 0xe0) | subtype;
}
/**
* gst_rtcp_packet_app_get_subtype:
* @packet: a valid APP #GstRTCPPacket
*
* Get the subtype field of the APP @packet.
*
* Returns: The subtype.
*
* Since: 1.10
*/
guint8
gst_rtcp_packet_app_get_subtype (GstRTCPPacket * packet)
{
guint8 *data;
g_return_val_if_fail (packet != NULL, 0);
g_return_val_if_fail (packet->type == GST_RTCP_TYPE_APP, 0);
g_return_val_if_fail (packet->rtcp != NULL, 0);
g_return_val_if_fail (packet->rtcp->map.flags & GST_MAP_READ, 0);
data = packet->rtcp->map.data + packet->offset;
return data[0] & 0x1f;
}
/**
* gst_rtcp_packet_app_set_ssrc:
* @packet: a valid APP #GstRTCPPacket
* @ssrc: SSRC/CSRC of the packet
*
* Set the SSRC/CSRC field of the APP @packet.
*
* Since: 1.10
*/
void
gst_rtcp_packet_app_set_ssrc (GstRTCPPacket * packet, guint32 ssrc)
{
guint8 *data;
g_return_if_fail (packet != NULL);
g_return_if_fail (packet->type == GST_RTCP_TYPE_APP);
g_return_if_fail (packet->rtcp != NULL);
g_return_if_fail (packet->rtcp->map.flags & GST_MAP_WRITE);
data = packet->rtcp->map.data + packet->offset + 4;
GST_WRITE_UINT32_BE (data, ssrc);
}
/**
* gst_rtcp_packet_app_get_ssrc:
* @packet: a valid APP #GstRTCPPacket
*
* Get the SSRC/CSRC field of the APP @packet.
*
* Returns: The SSRC/CSRC.
*
* Since: 1.10
*/
guint32
gst_rtcp_packet_app_get_ssrc (GstRTCPPacket * packet)
{
guint8 *data;
g_return_val_if_fail (packet != NULL, 0);
g_return_val_if_fail (packet->type == GST_RTCP_TYPE_APP, 0);
g_return_val_if_fail (packet->rtcp != NULL, 0);
g_return_val_if_fail (packet->rtcp->map.flags & GST_MAP_READ, 0);
data = packet->rtcp->map.data + packet->offset + 4;
return GST_READ_UINT32_BE (data);
}
/**
* gst_rtcp_packet_app_set_name:
* @packet: a valid APP #GstRTCPPacket
* @name: 4-byte ASCII name
*
* Set the name field of the APP @packet.
*
* Since: 1.10
*/
void
gst_rtcp_packet_app_set_name (GstRTCPPacket * packet, const gchar * name)
{
guint8 *data;
g_return_if_fail (packet != NULL);
g_return_if_fail (packet->type == GST_RTCP_TYPE_APP);
g_return_if_fail (packet->rtcp != NULL);
g_return_if_fail (packet->rtcp->map.flags & GST_MAP_WRITE);
data = packet->rtcp->map.data + packet->offset + 8;
memcpy (data, name, 4);
}
/**
* gst_rtcp_packet_app_get_name:
* @packet: a valid APP #GstRTCPPacket
*
* Get the name field of the APP @packet.
*
* Returns: The 4-byte name field, not zero-terminated.
*
* Since: 1.10
*/
const gchar *
gst_rtcp_packet_app_get_name (GstRTCPPacket * packet)
{
g_return_val_if_fail (packet != NULL, NULL);
g_return_val_if_fail (packet->type == GST_RTCP_TYPE_APP, NULL);
g_return_val_if_fail (packet->rtcp != NULL, NULL);
g_return_val_if_fail (packet->rtcp->map.flags & GST_MAP_READ, NULL);
return (const gchar *) &packet->rtcp->map.data[packet->offset + 8];
}
/**
* gst_rtcp_packet_app_get_data_length:
* @packet: a valid APP #GstRTCPPacket
*
* Get the length of the application-dependent data attached to an APP
* @packet.
*
* Returns: The length of data in 32-bit words.
*
* Since: 1.10
*/
guint16
gst_rtcp_packet_app_get_data_length (GstRTCPPacket * packet)
{
guint8 *data;
g_return_val_if_fail (packet != NULL, 0);
g_return_val_if_fail (packet->type == GST_RTCP_TYPE_APP, 0);
g_return_val_if_fail (packet->rtcp != NULL, 0);
g_return_val_if_fail (packet->rtcp->map.flags & GST_MAP_READ, 0);
data = packet->rtcp->map.data + packet->offset + 2;
return GST_READ_UINT16_BE (data) - 2;
}
/**
* gst_rtcp_packet_app_set_data_length:
* @packet: a valid APP #GstRTCPPacket
* @wordlen: Length of the data in 32-bit words
*
* Set the length of the application-dependent data attached to an APP
* @packet.
*
* Returns: %TRUE if there was enough space in the packet to add this much
* data.
*
* Since: 1.10
*/
gboolean
gst_rtcp_packet_app_set_data_length (GstRTCPPacket * packet, guint16 wordlen)
{
guint8 *data;
g_return_val_if_fail (packet != NULL, FALSE);
g_return_val_if_fail (packet->type == GST_RTCP_TYPE_APP, FALSE);
g_return_val_if_fail (packet->rtcp != NULL, FALSE);
g_return_val_if_fail (packet->rtcp->map.flags & GST_MAP_WRITE, FALSE);
if (packet->rtcp->map.maxsize < packet->offset + ((wordlen + 3) * 4))
return FALSE;
data = packet->rtcp->map.data + packet->offset + 2;
wordlen += 2;
GST_WRITE_UINT16_BE (data, wordlen);
packet->rtcp->map.size = packet->offset + ((wordlen + 1) * 4);
return TRUE;
}
/**
* gst_rtcp_packet_app_get_data:
* @packet: a valid APP #GstRTCPPacket
*
* Get the application-dependent data attached to a RTPFB or PSFB @packet.
*
* Returns: A pointer to the data
*
* Since: 1.10
*/
guint8 *
gst_rtcp_packet_app_get_data (GstRTCPPacket * packet)
{
guint8 *data;
g_return_val_if_fail (packet != NULL, NULL);
g_return_val_if_fail (packet->type == GST_RTCP_TYPE_APP, NULL);
g_return_val_if_fail (packet->rtcp != NULL, NULL);
g_return_val_if_fail (packet->rtcp->map.flags & GST_MAP_READ, NULL);
data = packet->rtcp->map.data + packet->offset;
if (GST_READ_UINT16_BE (data + 2) <= 2)
return NULL;
return data + 12;
}
/**
* 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;
}