mirror of
https://gitlab.freedesktop.org/gstreamer/gstreamer.git
synced 2024-12-21 07:46:38 +00:00
93cb325fa1
Part-of: <https://gitlab.freedesktop.org/gstreamer/gst-plugins-base/-/merge_requests/476>
3584 lines
91 KiB
C
3584 lines
91 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 compound 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;
|
|
}
|
|
|
|
static gint
|
|
rtcp_packet_min_length (GstRTCPType type)
|
|
{
|
|
switch (type) {
|
|
case GST_RTCP_TYPE_SR:
|
|
return 28;
|
|
case GST_RTCP_TYPE_RR:
|
|
return 8;
|
|
case GST_RTCP_TYPE_SDES:
|
|
return 4;
|
|
case GST_RTCP_TYPE_BYE:
|
|
return 4;
|
|
case GST_RTCP_TYPE_APP:
|
|
return 12;
|
|
case GST_RTCP_TYPE_RTPFB:
|
|
return 12;
|
|
case GST_RTCP_TYPE_PSFB:
|
|
return 12;
|
|
case GST_RTCP_TYPE_XR:
|
|
return 8;
|
|
default:
|
|
return -1;
|
|
}
|
|
}
|
|
|
|
/**
|
|
* 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;
|
|
gint minsize;
|
|
guint minlength;
|
|
|
|
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;
|
|
|
|
minsize = rtcp_packet_min_length (packet->type);
|
|
if (minsize == -1)
|
|
minsize = 0;
|
|
minlength = (minsize - 4) >> 2;
|
|
|
|
/* Validate the size */
|
|
if (packet->length < minlength)
|
|
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));
|
|
|
|
if (packet->padding) {
|
|
/* Last packet is a padding packet. Let's not replace it silently */
|
|
/* and let the application know that it could not be added because */
|
|
/* it would involve replacing a packet */
|
|
return FALSE;
|
|
}
|
|
}
|
|
|
|
maxsize = rtcp->map.maxsize;
|
|
|
|
/* packet->offset is now pointing to the next free offset in the buffer to
|
|
* start a compound packet. Next we figure out if we have enough free space in
|
|
* the buffer to continue. */
|
|
len = rtcp_packet_min_length (type);
|
|
if (len == -1)
|
|
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.
|
|
*
|
|
* Since: 1.10
|
|
*/
|
|
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.
|
|
*
|
|
* Since: 1.10
|
|
*/
|
|
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.
|
|
*
|
|
* Since: 1.10
|
|
*/
|
|
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.
|
|
*
|
|
* Since: 1.10
|
|
*/
|
|
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.
|
|
* @thinning: 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 * thinning, 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 (thinning)
|
|
*thinning = 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;
|
|
|
|
if (!gst_rtcp_packet_xr_get_rle_info (packet, NULL, NULL, NULL, NULL,
|
|
&chunk_count))
|
|
g_return_val_if_reached (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.
|
|
* @thinning: 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 * thinning, 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 (thinning)
|
|
*thinning = 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_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;
|
|
|
|
if (!gst_rtcp_packet_xr_get_prt_info (packet, NULL, NULL, &begin_seq,
|
|
&end_seq))
|
|
g_return_val_if_reached (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;
|
|
}
|