mirror of
https://gitlab.freedesktop.org/gstreamer/gstreamer.git
synced 2024-12-27 10:40:34 +00:00
01bbdd6bdf
This is useful and in most cases someone had put arbitrary markup into the docs, misspelled xref'ed symbols, forgot to add stuff to the docs etc..
2059 lines
53 KiB
C
2059 lines
53 KiB
C
/* GStreamer
|
|
* Copyright (C) <2005> Philippe Khalaf <burger@speedy.org>
|
|
* Copyright (C) <2006> Wim Taymans <wim@fluendo.com>
|
|
*
|
|
* 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., 59 Temple Place - Suite 330,
|
|
* Boston, MA 02111-1307, USA.
|
|
*/
|
|
|
|
/**
|
|
* SECTION:gstrtpbuffer
|
|
* @short_description: Helper methods for dealing with RTP buffers
|
|
* @see_also: #GstBaseRTPPayload, #GstBaseRTPDepayload, gstrtcpbuffer
|
|
*
|
|
* <refsect2>
|
|
* <para>
|
|
* The GstRTPBuffer helper functions makes it easy to parse and create regular
|
|
* #GstBuffer objects that contain RTP payloads. These buffers are typically of
|
|
* 'application/x-rtp' #GstCaps.
|
|
* </para>
|
|
* </refsect2>
|
|
*
|
|
* Last reviewed on 2006-07-17 (0.10.10)
|
|
*/
|
|
|
|
#include "gstrtpbuffer.h"
|
|
|
|
#include <stdlib.h>
|
|
#include <string.h>
|
|
|
|
#define GST_RTP_HEADER_LEN 12
|
|
|
|
/* Note: we use bitfields here to make sure the compiler doesn't add padding
|
|
* between fields on certain architectures; can't assume aligned access either
|
|
*/
|
|
typedef struct _GstRTPHeader
|
|
{
|
|
#if G_BYTE_ORDER == G_LITTLE_ENDIAN
|
|
unsigned int csrc_count:4; /* CSRC count */
|
|
unsigned int extension:1; /* header extension flag */
|
|
unsigned int padding:1; /* padding flag */
|
|
unsigned int version:2; /* protocol version */
|
|
unsigned int payload_type:7; /* payload type */
|
|
unsigned int marker:1; /* marker bit */
|
|
#elif G_BYTE_ORDER == G_BIG_ENDIAN
|
|
unsigned int version:2; /* protocol version */
|
|
unsigned int padding:1; /* padding flag */
|
|
unsigned int extension:1; /* header extension flag */
|
|
unsigned int csrc_count:4; /* CSRC count */
|
|
unsigned int marker:1; /* marker bit */
|
|
unsigned int payload_type:7; /* payload type */
|
|
#else
|
|
#error "G_BYTE_ORDER should be big or little endian."
|
|
#endif
|
|
unsigned int seq:16; /* sequence number */
|
|
unsigned int timestamp:32; /* timestamp */
|
|
unsigned int ssrc:32; /* synchronization source */
|
|
guint8 csrclist[4]; /* optional CSRC list, 32 bits each */
|
|
} GstRTPHeader;
|
|
|
|
#define GST_RTP_HEADER_VERSION(data) (((GstRTPHeader *)(data))->version)
|
|
#define GST_RTP_HEADER_PADDING(data) (((GstRTPHeader *)(data))->padding)
|
|
#define GST_RTP_HEADER_EXTENSION(data) (((GstRTPHeader *)(data))->extension)
|
|
#define GST_RTP_HEADER_CSRC_COUNT(data) (((GstRTPHeader *)(data))->csrc_count)
|
|
#define GST_RTP_HEADER_MARKER(data) (((GstRTPHeader *)(data))->marker)
|
|
#define GST_RTP_HEADER_PAYLOAD_TYPE(data) (((GstRTPHeader *)(data))->payload_type)
|
|
#define GST_RTP_HEADER_SEQ(data) (((GstRTPHeader *)(data))->seq)
|
|
#define GST_RTP_HEADER_TIMESTAMP(data) (((GstRTPHeader *)(data))->timestamp)
|
|
#define GST_RTP_HEADER_SSRC(data) (((GstRTPHeader *)(data))->ssrc)
|
|
#define GST_RTP_HEADER_CSRC_LIST_OFFSET(data,i) \
|
|
data + G_STRUCT_OFFSET(GstRTPHeader, csrclist) + \
|
|
((i) * sizeof(guint32))
|
|
#define GST_RTP_HEADER_CSRC_SIZE(data) (GST_RTP_HEADER_CSRC_COUNT(data) * sizeof (guint32))
|
|
|
|
/**
|
|
* gst_rtp_buffer_allocate_data:
|
|
* @buffer: a #GstBuffer
|
|
* @payload_len: the length of the payload
|
|
* @pad_len: the amount of padding
|
|
* @csrc_count: the number of CSRC entries
|
|
*
|
|
* Allocate enough data in @buffer to hold an RTP packet with @csrc_count CSRCs,
|
|
* a payload length of @payload_len and padding of @pad_len.
|
|
* MALLOCDATA of @buffer will be overwritten and will not be freed.
|
|
* All other RTP header fields will be set to 0/FALSE.
|
|
*/
|
|
void
|
|
gst_rtp_buffer_allocate_data (GstBuffer * buffer, guint payload_len,
|
|
guint8 pad_len, guint8 csrc_count)
|
|
{
|
|
guint len;
|
|
guint8 *data;
|
|
|
|
g_return_if_fail (csrc_count <= 15);
|
|
g_return_if_fail (GST_IS_BUFFER (buffer));
|
|
|
|
len = GST_RTP_HEADER_LEN + csrc_count * sizeof (guint32)
|
|
+ payload_len + pad_len;
|
|
|
|
data = g_malloc (len);
|
|
GST_BUFFER_MALLOCDATA (buffer) = data;
|
|
GST_BUFFER_DATA (buffer) = data;
|
|
GST_BUFFER_SIZE (buffer) = len;
|
|
|
|
/* fill in defaults */
|
|
GST_RTP_HEADER_VERSION (data) = GST_RTP_VERSION;
|
|
GST_RTP_HEADER_PADDING (data) = FALSE;
|
|
GST_RTP_HEADER_EXTENSION (data) = FALSE;
|
|
GST_RTP_HEADER_CSRC_COUNT (data) = csrc_count;
|
|
memset (GST_RTP_HEADER_CSRC_LIST_OFFSET (data, 0), 0,
|
|
csrc_count * sizeof (guint32));
|
|
GST_RTP_HEADER_MARKER (data) = FALSE;
|
|
GST_RTP_HEADER_PAYLOAD_TYPE (data) = 0;
|
|
GST_RTP_HEADER_SEQ (data) = 0;
|
|
GST_RTP_HEADER_TIMESTAMP (data) = 0;
|
|
GST_RTP_HEADER_SSRC (data) = 0;
|
|
}
|
|
|
|
/**
|
|
* gst_rtp_buffer_new_take_data:
|
|
* @data: 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_rtp_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 ();
|
|
|
|
GST_BUFFER_MALLOCDATA (result) = data;
|
|
GST_BUFFER_DATA (result) = data;
|
|
GST_BUFFER_SIZE (result) = len;
|
|
|
|
return result;
|
|
}
|
|
|
|
/**
|
|
* gst_rtp_buffer_new_copy_data:
|
|
* @data: 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_rtp_buffer_new_copy_data (gpointer data, guint len)
|
|
{
|
|
return gst_rtp_buffer_new_take_data (g_memdup (data, len), len);
|
|
}
|
|
|
|
/**
|
|
* gst_rtp_buffer_new_allocate:
|
|
* @payload_len: the length of the payload
|
|
* @pad_len: the amount of padding
|
|
* @csrc_count: the number of CSRC entries
|
|
*
|
|
* Allocate a new #GstBuffer with enough data to hold an RTP packet with
|
|
* @csrc_count CSRCs, a payload length of @payload_len and padding of @pad_len.
|
|
* All other RTP header fields will be set to 0/FALSE.
|
|
*
|
|
* Returns: A newly allocated buffer that can hold an RTP packet with given
|
|
* parameters.
|
|
*/
|
|
GstBuffer *
|
|
gst_rtp_buffer_new_allocate (guint payload_len, guint8 pad_len,
|
|
guint8 csrc_count)
|
|
{
|
|
GstBuffer *result;
|
|
|
|
g_return_val_if_fail (csrc_count <= 15, NULL);
|
|
|
|
result = gst_buffer_new ();
|
|
gst_rtp_buffer_allocate_data (result, payload_len, pad_len, csrc_count);
|
|
|
|
return result;
|
|
}
|
|
|
|
/**
|
|
* gst_rtp_buffer_new_allocate_len:
|
|
* @packet_len: the total length of the packet
|
|
* @pad_len: the amount of padding
|
|
* @csrc_count: the number of CSRC entries
|
|
*
|
|
* Create a new #GstBuffer that can hold an RTP packet that is exactly
|
|
* @packet_len long. The length of the payload depends on @pad_len and
|
|
* @csrc_count and can be calculated with gst_rtp_buffer_calc_payload_len().
|
|
* All RTP header fields will be set to 0/FALSE.
|
|
*
|
|
* Returns: A newly allocated buffer that can hold an RTP packet of @packet_len.
|
|
*/
|
|
GstBuffer *
|
|
gst_rtp_buffer_new_allocate_len (guint packet_len, guint8 pad_len,
|
|
guint8 csrc_count)
|
|
{
|
|
guint len;
|
|
|
|
g_return_val_if_fail (csrc_count <= 15, NULL);
|
|
|
|
len = gst_rtp_buffer_calc_payload_len (packet_len, pad_len, csrc_count);
|
|
|
|
return gst_rtp_buffer_new_allocate (len, pad_len, csrc_count);
|
|
}
|
|
|
|
/**
|
|
* gst_rtp_buffer_calc_header_len:
|
|
* @csrc_count: the number of CSRC entries
|
|
*
|
|
* Calculate the header length of an RTP packet with @csrc_count CSRC entries.
|
|
* An RTP packet can have at most 15 CSRC entries.
|
|
*
|
|
* Returns: The length of an RTP header with @csrc_count CSRC entries.
|
|
*/
|
|
guint
|
|
gst_rtp_buffer_calc_header_len (guint8 csrc_count)
|
|
{
|
|
g_return_val_if_fail (csrc_count <= 15, 0);
|
|
|
|
return GST_RTP_HEADER_LEN + (csrc_count * sizeof (guint32));
|
|
}
|
|
|
|
/**
|
|
* gst_rtp_buffer_calc_packet_len:
|
|
* @payload_len: the length of the payload
|
|
* @pad_len: the amount of padding
|
|
* @csrc_count: the number of CSRC entries
|
|
*
|
|
* Calculate the total length of an RTP packet with a payload size of @payload_len,
|
|
* a padding of @pad_len and a @csrc_count CSRC entries.
|
|
*
|
|
* Returns: The total length of an RTP header with given parameters.
|
|
*/
|
|
guint
|
|
gst_rtp_buffer_calc_packet_len (guint payload_len, guint8 pad_len,
|
|
guint8 csrc_count)
|
|
{
|
|
g_return_val_if_fail (csrc_count <= 15, 0);
|
|
|
|
return payload_len + GST_RTP_HEADER_LEN + (csrc_count * sizeof (guint32))
|
|
+ pad_len;
|
|
}
|
|
|
|
/**
|
|
* gst_rtp_buffer_calc_payload_len:
|
|
* @packet_len: the length of the total RTP packet
|
|
* @pad_len: the amount of padding
|
|
* @csrc_count: the number of CSRC entries
|
|
*
|
|
* Calculate the length of the payload of an RTP packet with size @packet_len,
|
|
* a padding of @pad_len and a @csrc_count CSRC entries.
|
|
*
|
|
* Returns: The length of the payload of an RTP packet with given parameters.
|
|
*/
|
|
guint
|
|
gst_rtp_buffer_calc_payload_len (guint packet_len, guint8 pad_len,
|
|
guint8 csrc_count)
|
|
{
|
|
g_return_val_if_fail (csrc_count <= 15, 0);
|
|
|
|
return packet_len - GST_RTP_HEADER_LEN - (csrc_count * sizeof (guint32))
|
|
- pad_len;
|
|
}
|
|
|
|
/*
|
|
* validate_data:
|
|
* @data: the data to validate
|
|
* @len: the length of @data to validate
|
|
* @payload: the payload if @data represents the header only
|
|
* @payload_len: the len of the payload
|
|
*
|
|
* Checks if @data is a valid RTP packet.
|
|
*
|
|
* Returns: TRUE if @data is a valid RTP packet
|
|
*/
|
|
static gboolean
|
|
validate_data (guint8 * data, guint len, guint8 * payload, guint payload_len)
|
|
{
|
|
guint8 padding;
|
|
guint8 csrc_count;
|
|
guint header_len;
|
|
guint8 version;
|
|
|
|
g_return_val_if_fail (data != NULL, FALSE);
|
|
|
|
header_len = GST_RTP_HEADER_LEN;
|
|
if (G_UNLIKELY (len < header_len))
|
|
goto wrong_length;
|
|
|
|
/* check version */
|
|
version = (data[0] & 0xc0);
|
|
if (G_UNLIKELY (version != (GST_RTP_VERSION << 6)))
|
|
goto wrong_version;
|
|
|
|
/* calc header length with csrc */
|
|
csrc_count = (data[0] & 0x0f);
|
|
header_len += csrc_count * sizeof (guint32);
|
|
|
|
/* calc extension length when present. */
|
|
if (data[0] & 0x10) {
|
|
guint8 *extpos;
|
|
guint16 extlen;
|
|
|
|
/* this points to the extenstion bits and header length */
|
|
extpos = &data[header_len];
|
|
|
|
/* skip the header and check that we have enough space */
|
|
header_len += 4;
|
|
if (G_UNLIKELY (len < header_len))
|
|
goto wrong_length;
|
|
|
|
/* skip id */
|
|
extpos += 2;
|
|
/* read length as the number of 32 bits words */
|
|
extlen = GST_READ_UINT16_BE (extpos);
|
|
|
|
header_len += extlen * sizeof (guint32);
|
|
}
|
|
|
|
/* check for padding */
|
|
if (data[0] & 0x20) {
|
|
if (payload)
|
|
padding = payload[payload_len - 1];
|
|
else
|
|
padding = data[len - 1];
|
|
} else {
|
|
padding = 0;
|
|
}
|
|
|
|
/* check if padding and header not bigger than packet length */
|
|
if (G_UNLIKELY (len < padding + header_len))
|
|
goto wrong_padding;
|
|
|
|
return TRUE;
|
|
|
|
/* ERRORS */
|
|
wrong_length:
|
|
{
|
|
GST_DEBUG ("len < header_len check failed (%d < %d)", len, header_len);
|
|
goto dump_packet;
|
|
}
|
|
wrong_version:
|
|
{
|
|
GST_DEBUG ("version check failed (%d != %d)", version, GST_RTP_VERSION);
|
|
goto dump_packet;
|
|
}
|
|
wrong_padding:
|
|
{
|
|
GST_DEBUG ("padding check failed (%d - %d < %d)", len, header_len, padding);
|
|
goto dump_packet;
|
|
}
|
|
dump_packet:
|
|
{
|
|
GST_MEMDUMP ("buffer", data, len);
|
|
return FALSE;
|
|
}
|
|
}
|
|
|
|
/**
|
|
* gst_rtp_buffer_validate_data:
|
|
* @data: the data to validate
|
|
* @len: the length of @data to validate
|
|
*
|
|
* Check if the @data and @size point to the data of a valid RTP packet.
|
|
* This function checks the length, version and padding of the packet data.
|
|
* Use this function to validate a packet before using the other functions in
|
|
* this module.
|
|
*
|
|
* Returns: TRUE if the data points to a valid RTP packet.
|
|
*/
|
|
gboolean
|
|
gst_rtp_buffer_validate_data (guint8 * data, guint len)
|
|
{
|
|
return validate_data (data, len, NULL, 0);
|
|
}
|
|
|
|
/**
|
|
* gst_rtp_buffer_validate:
|
|
* @buffer: the buffer to validate
|
|
*
|
|
* Check if the data pointed to by @buffer is a valid RTP packet using
|
|
* gst_rtp_buffer_validate_data().
|
|
* Use this function to validate a packet before using the other functions in
|
|
* this module.
|
|
*
|
|
* Returns: TRUE if @buffer is a valid RTP packet.
|
|
*/
|
|
gboolean
|
|
gst_rtp_buffer_validate (GstBuffer * buffer)
|
|
{
|
|
guint8 *data;
|
|
guint len;
|
|
|
|
g_return_val_if_fail (GST_IS_BUFFER (buffer), FALSE);
|
|
|
|
data = GST_BUFFER_DATA (buffer);
|
|
len = GST_BUFFER_SIZE (buffer);
|
|
|
|
return validate_data (data, len, NULL, 0);
|
|
}
|
|
|
|
/**
|
|
* gst_rtp_buffer_list_validate:
|
|
* @list: the buffer list to validate
|
|
*
|
|
* Check if all RTP packets in the @list are valid using gst_rtp_buffer_validate_data().
|
|
* Use this function to validate an list before using the other functions in
|
|
* this module.
|
|
*
|
|
* Returns: TRUE if @list consists only of valid RTP packets.
|
|
*
|
|
* Since: 0.10.24
|
|
*/
|
|
gboolean
|
|
gst_rtp_buffer_list_validate (GstBufferList * list)
|
|
{
|
|
guint16 prev_seqnum = 0;
|
|
GstBufferListIterator *it;
|
|
guint i = 0;
|
|
|
|
g_return_val_if_fail (GST_IS_BUFFER_LIST (list), FALSE);
|
|
|
|
it = gst_buffer_list_iterate (list);
|
|
g_return_val_if_fail (it != NULL, FALSE);
|
|
|
|
/* iterate through all the RTP packets in the list */
|
|
while (gst_buffer_list_iterator_next_group (it)) {
|
|
GstBuffer *rtpbuf;
|
|
GstBuffer *paybuf;
|
|
guint8 *packet_header;
|
|
guint8 *packet_payload;
|
|
guint payload_size;
|
|
guint packet_size;
|
|
guint j, n_buffers;
|
|
|
|
/* each group should consists of at least 1 buffer: The first buffer always
|
|
* contains the complete RTP header. Next buffers contain the payload */
|
|
n_buffers = gst_buffer_list_iterator_n_buffers (it);
|
|
if (n_buffers < 1)
|
|
goto invalid_list;
|
|
|
|
/* get the RTP header (and if n_buffers == 1 also the payload) */
|
|
rtpbuf = gst_buffer_list_iterator_next (it);
|
|
packet_header = GST_BUFFER_DATA (rtpbuf);
|
|
if (packet_header == NULL)
|
|
goto invalid_list;
|
|
|
|
/* check the sequence number */
|
|
if (G_UNLIKELY (i == 0)) {
|
|
prev_seqnum = g_ntohs (GST_RTP_HEADER_SEQ (packet_header));
|
|
i++;
|
|
} else {
|
|
if (++prev_seqnum != g_ntohs (GST_RTP_HEADER_SEQ (packet_header)))
|
|
goto invalid_list;
|
|
}
|
|
|
|
packet_size = GST_BUFFER_SIZE (rtpbuf);
|
|
packet_payload = NULL;
|
|
payload_size = 0;
|
|
|
|
/* get the payload buffers */
|
|
for (j = 1; j < n_buffers; j++) {
|
|
/* get the payload */
|
|
paybuf = gst_buffer_list_iterator_next (it);
|
|
|
|
if ((packet_payload = GST_BUFFER_DATA (paybuf)) == NULL)
|
|
goto invalid_list;
|
|
|
|
if ((payload_size = GST_BUFFER_SIZE (paybuf)) == 0)
|
|
goto invalid_list;
|
|
|
|
/* the size of the RTP packet within the current group */
|
|
packet_size += payload_size;
|
|
}
|
|
|
|
/* validate packet */
|
|
if (!validate_data (packet_header, packet_size, packet_payload,
|
|
payload_size)) {
|
|
goto invalid_list;
|
|
}
|
|
}
|
|
|
|
gst_buffer_list_iterator_free (it);
|
|
|
|
return TRUE;
|
|
|
|
/* ERRORS */
|
|
invalid_list:
|
|
{
|
|
gst_buffer_list_iterator_free (it);
|
|
return FALSE;
|
|
}
|
|
}
|
|
|
|
/**
|
|
* gst_rtp_buffer_set_packet_len:
|
|
* @buffer: the buffer
|
|
* @len: the new packet length
|
|
*
|
|
* Set the total @buffer size to @len. The data in the buffer will be made
|
|
* larger if needed. Any padding will be removed from the packet.
|
|
*/
|
|
void
|
|
gst_rtp_buffer_set_packet_len (GstBuffer * buffer, guint len)
|
|
{
|
|
guint oldlen;
|
|
guint8 *data;
|
|
|
|
oldlen = GST_BUFFER_SIZE (buffer);
|
|
data = GST_BUFFER_DATA (buffer);
|
|
|
|
if (oldlen < len) {
|
|
data = g_realloc (GST_BUFFER_MALLOCDATA (buffer), len);
|
|
GST_BUFFER_MALLOCDATA (buffer) = data;
|
|
GST_BUFFER_DATA (buffer) = data;
|
|
}
|
|
GST_BUFFER_SIZE (buffer) = len;
|
|
|
|
/* remove any padding */
|
|
GST_RTP_HEADER_PADDING (data) = FALSE;
|
|
}
|
|
|
|
/**
|
|
* gst_rtp_buffer_get_packet_len:
|
|
* @buffer: the buffer
|
|
*
|
|
* Return the total length of the packet in @buffer.
|
|
*
|
|
* Returns: The total length of the packet in @buffer.
|
|
*/
|
|
guint
|
|
gst_rtp_buffer_get_packet_len (GstBuffer * buffer)
|
|
{
|
|
return GST_BUFFER_SIZE (buffer);
|
|
}
|
|
|
|
/**
|
|
* gst_rtp_buffer_get_header_len:
|
|
* @buffer: the buffer
|
|
*
|
|
* Return the total length of the header in @buffer. This include the length of
|
|
* the fixed header, the CSRC list and the extension header.
|
|
*
|
|
* Returns: The total length of the header in @buffer.
|
|
*/
|
|
guint
|
|
gst_rtp_buffer_get_header_len (GstBuffer * buffer)
|
|
{
|
|
guint len;
|
|
guint8 *data;
|
|
|
|
data = GST_BUFFER_DATA (buffer);
|
|
|
|
len = GST_RTP_HEADER_LEN + GST_RTP_HEADER_CSRC_SIZE (data);
|
|
if (GST_RTP_HEADER_EXTENSION (data))
|
|
len += GST_READ_UINT16_BE (data + len + 2) * 4 + 4;
|
|
|
|
return len;
|
|
}
|
|
|
|
/**
|
|
* gst_rtp_buffer_get_version:
|
|
* @buffer: the buffer
|
|
*
|
|
* Get the version number of the RTP packet in @buffer.
|
|
*
|
|
* Returns: The version of @buffer.
|
|
*/
|
|
guint8
|
|
gst_rtp_buffer_get_version (GstBuffer * buffer)
|
|
{
|
|
return GST_RTP_HEADER_VERSION (GST_BUFFER_DATA (buffer));
|
|
}
|
|
|
|
/**
|
|
* gst_rtp_buffer_set_version:
|
|
* @buffer: the buffer
|
|
* @version: the new version
|
|
*
|
|
* Set the version of the RTP packet in @buffer to @version.
|
|
*/
|
|
void
|
|
gst_rtp_buffer_set_version (GstBuffer * buffer, guint8 version)
|
|
{
|
|
g_return_if_fail (version < 0x04);
|
|
|
|
GST_RTP_HEADER_VERSION (GST_BUFFER_DATA (buffer)) = version;
|
|
}
|
|
|
|
/**
|
|
* gst_rtp_buffer_get_padding:
|
|
* @buffer: the buffer
|
|
*
|
|
* Check if the padding bit is set on the RTP packet in @buffer.
|
|
*
|
|
* Returns: TRUE if @buffer has the padding bit set.
|
|
*/
|
|
gboolean
|
|
gst_rtp_buffer_get_padding (GstBuffer * buffer)
|
|
{
|
|
return GST_RTP_HEADER_PADDING (GST_BUFFER_DATA (buffer));
|
|
}
|
|
|
|
/**
|
|
* gst_rtp_buffer_set_padding:
|
|
* @buffer: the buffer
|
|
* @padding: the new padding
|
|
*
|
|
* Set the padding bit on the RTP packet in @buffer to @padding.
|
|
*/
|
|
void
|
|
gst_rtp_buffer_set_padding (GstBuffer * buffer, gboolean padding)
|
|
{
|
|
GST_RTP_HEADER_PADDING (GST_BUFFER_DATA (buffer)) = padding;
|
|
}
|
|
|
|
/**
|
|
* gst_rtp_buffer_pad_to:
|
|
* @buffer: the buffer
|
|
* @len: the new amount of padding
|
|
*
|
|
* Set the amount of padding in the RTP packet in @buffer to
|
|
* @len. If @len is 0, the padding is removed.
|
|
*
|
|
* NOTE: This function does not work correctly.
|
|
*/
|
|
void
|
|
gst_rtp_buffer_pad_to (GstBuffer * buffer, guint len)
|
|
{
|
|
guint8 *data;
|
|
|
|
data = GST_BUFFER_DATA (buffer);
|
|
|
|
if (len > 0)
|
|
GST_RTP_HEADER_PADDING (data) = TRUE;
|
|
else
|
|
GST_RTP_HEADER_PADDING (data) = FALSE;
|
|
|
|
/* FIXME, set the padding byte at the end of the payload data */
|
|
}
|
|
|
|
/**
|
|
* gst_rtp_buffer_get_extension:
|
|
* @buffer: the buffer
|
|
*
|
|
* Check if the extension bit is set on the RTP packet in @buffer.
|
|
*
|
|
* Returns: TRUE if @buffer has the extension bit set.
|
|
*/
|
|
gboolean
|
|
gst_rtp_buffer_get_extension (GstBuffer * buffer)
|
|
{
|
|
return GST_RTP_HEADER_EXTENSION (GST_BUFFER_DATA (buffer));
|
|
}
|
|
|
|
/**
|
|
* gst_rtp_buffer_set_extension:
|
|
* @buffer: the buffer
|
|
* @extension: the new extension
|
|
*
|
|
* Set the extension bit on the RTP packet in @buffer to @extension.
|
|
*/
|
|
void
|
|
gst_rtp_buffer_set_extension (GstBuffer * buffer, gboolean extension)
|
|
{
|
|
GST_RTP_HEADER_EXTENSION (GST_BUFFER_DATA (buffer)) = extension;
|
|
}
|
|
|
|
/**
|
|
* gst_rtp_buffer_get_extension_data:
|
|
* @buffer: the buffer
|
|
* @bits: location for result bits
|
|
* @data: location for data
|
|
* @wordlen: location for length of @data in 32 bits words
|
|
*
|
|
* Get the extension data. @bits will contain the extension 16 bits of custom
|
|
* data. @data will point to the data in the extension and @wordlen will contain
|
|
* the length of @data in 32 bits words.
|
|
*
|
|
* If @buffer did not contain an extension, this function will return %FALSE
|
|
* with @bits, @data and @wordlen unchanged.
|
|
*
|
|
* Returns: TRUE if @buffer had the extension bit set.
|
|
*
|
|
* Since: 0.10.15
|
|
*/
|
|
gboolean
|
|
gst_rtp_buffer_get_extension_data (GstBuffer * buffer, guint16 * bits,
|
|
gpointer * data, guint * wordlen)
|
|
{
|
|
guint len;
|
|
guint8 *pdata;
|
|
|
|
pdata = GST_BUFFER_DATA (buffer);
|
|
|
|
if (!GST_RTP_HEADER_EXTENSION (pdata))
|
|
return FALSE;
|
|
|
|
/* move to the extension */
|
|
len = GST_RTP_HEADER_LEN + GST_RTP_HEADER_CSRC_SIZE (pdata);
|
|
pdata += len;
|
|
|
|
if (bits)
|
|
*bits = GST_READ_UINT16_BE (pdata);
|
|
if (wordlen)
|
|
*wordlen = GST_READ_UINT16_BE (pdata + 2);
|
|
if (data)
|
|
*data = pdata + 4;
|
|
|
|
return TRUE;
|
|
}
|
|
|
|
/**
|
|
* gst_rtp_buffer_set_extension_data:
|
|
* @buffer: the buffer
|
|
* @bits: the bits specific for the extension
|
|
* @length: the length that counts the number of 32-bit words in
|
|
* the extension, excluding the extension header ( therefore zero is a valid length)
|
|
*
|
|
* Set the extension bit of the rtp buffer and fill in the @bits and @length of the
|
|
* extension header. It will refuse to set the extension data if the buffer is not
|
|
* large enough.
|
|
*
|
|
* Returns: True if done.
|
|
*
|
|
* Since: 0.10.18
|
|
*/
|
|
gboolean
|
|
gst_rtp_buffer_set_extension_data (GstBuffer * buffer, guint16 bits,
|
|
guint16 length)
|
|
{
|
|
guint32 min_size = 0;
|
|
guint8 *data;
|
|
|
|
data = GST_BUFFER_DATA (buffer);
|
|
|
|
/* check if the buffer is big enough to hold the extension */
|
|
min_size =
|
|
GST_RTP_HEADER_LEN + GST_RTP_HEADER_CSRC_SIZE (data) + 4 +
|
|
length * sizeof (guint32);
|
|
if (G_UNLIKELY (min_size > GST_BUFFER_SIZE (buffer)))
|
|
goto too_small;
|
|
|
|
/* now we can set the extension bit */
|
|
gst_rtp_buffer_set_extension (buffer, TRUE);
|
|
|
|
data += GST_RTP_HEADER_LEN + GST_RTP_HEADER_CSRC_SIZE (data);
|
|
GST_WRITE_UINT16_BE (data, bits);
|
|
GST_WRITE_UINT16_BE (data + 2, length);
|
|
|
|
return TRUE;
|
|
|
|
/* ERRORS */
|
|
too_small:
|
|
{
|
|
g_warning
|
|
("rtp buffer too small: need more than %d bytes but only have %d bytes",
|
|
min_size, GST_BUFFER_SIZE (buffer));
|
|
return FALSE;
|
|
}
|
|
}
|
|
|
|
/**
|
|
* gst_rtp_buffer_get_ssrc:
|
|
* @buffer: the buffer
|
|
*
|
|
* Get the SSRC of the RTP packet in @buffer.
|
|
*
|
|
* Returns: the SSRC of @buffer in host order.
|
|
*/
|
|
guint32
|
|
gst_rtp_buffer_get_ssrc (GstBuffer * buffer)
|
|
{
|
|
return g_ntohl (GST_RTP_HEADER_SSRC (GST_BUFFER_DATA (buffer)));
|
|
}
|
|
|
|
/**
|
|
* gst_rtp_buffer_list_get_ssrc:
|
|
* @list: the buffer list
|
|
*
|
|
* Get the SSRC of the first RTP packet in @list.
|
|
* All RTP packets within @list have the same SSRC.
|
|
*
|
|
* Returns: the SSRC of @list in host order.
|
|
*
|
|
* Since: 0.10.24
|
|
*/
|
|
guint32
|
|
gst_rtp_buffer_list_get_ssrc (GstBufferList * list)
|
|
{
|
|
GstBuffer *buffer;
|
|
|
|
buffer = gst_buffer_list_get (list, 0, 0);
|
|
g_return_val_if_fail (buffer != NULL, 0);
|
|
|
|
return g_ntohl (GST_RTP_HEADER_SSRC (GST_BUFFER_DATA (buffer)));
|
|
}
|
|
|
|
/**
|
|
* gst_rtp_buffer_set_ssrc:
|
|
* @buffer: the buffer
|
|
* @ssrc: the new SSRC
|
|
*
|
|
* Set the SSRC on the RTP packet in @buffer to @ssrc.
|
|
*/
|
|
void
|
|
gst_rtp_buffer_set_ssrc (GstBuffer * buffer, guint32 ssrc)
|
|
{
|
|
GST_RTP_HEADER_SSRC (GST_BUFFER_DATA (buffer)) = g_htonl (ssrc);
|
|
}
|
|
|
|
static GstBufferListItem
|
|
set_ssrc_header (GstBuffer ** buffer, guint group, guint idx, guint32 * ssrc)
|
|
{
|
|
GST_RTP_HEADER_SSRC (GST_BUFFER_DATA (*buffer)) = g_htonl (*ssrc);
|
|
return GST_BUFFER_LIST_SKIP_GROUP;
|
|
}
|
|
|
|
/**
|
|
* gst_rtp_buffer_list_set_ssrc:
|
|
* @list: the buffer list
|
|
* @ssrc: the new SSRC
|
|
*
|
|
* Set the SSRC on each RTP packet in @list to @ssrc.
|
|
*
|
|
* Since: 0.10.24
|
|
*/
|
|
void
|
|
gst_rtp_buffer_list_set_ssrc (GstBufferList * list, guint32 ssrc)
|
|
{
|
|
gst_buffer_list_foreach (list, (GstBufferListFunc) set_ssrc_header, &ssrc);
|
|
}
|
|
|
|
/**
|
|
* gst_rtp_buffer_get_csrc_count:
|
|
* @buffer: the buffer
|
|
*
|
|
* Get the CSRC count of the RTP packet in @buffer.
|
|
*
|
|
* Returns: the CSRC count of @buffer.
|
|
*/
|
|
guint8
|
|
gst_rtp_buffer_get_csrc_count (GstBuffer * buffer)
|
|
{
|
|
return GST_RTP_HEADER_CSRC_COUNT (GST_BUFFER_DATA (buffer));
|
|
}
|
|
|
|
/**
|
|
* gst_rtp_buffer_get_csrc:
|
|
* @buffer: the buffer
|
|
* @idx: the index of the CSRC to get
|
|
*
|
|
* Get the CSRC at index @idx in @buffer.
|
|
*
|
|
* Returns: the CSRC at index @idx in host order.
|
|
*/
|
|
guint32
|
|
gst_rtp_buffer_get_csrc (GstBuffer * buffer, guint8 idx)
|
|
{
|
|
guint8 *data;
|
|
|
|
data = GST_BUFFER_DATA (buffer);
|
|
|
|
g_return_val_if_fail (idx < GST_RTP_HEADER_CSRC_COUNT (data), 0);
|
|
|
|
return GST_READ_UINT32_BE (GST_RTP_HEADER_CSRC_LIST_OFFSET (data, idx));
|
|
}
|
|
|
|
/**
|
|
* gst_rtp_buffer_set_csrc:
|
|
* @buffer: the buffer
|
|
* @idx: the CSRC index to set
|
|
* @csrc: the CSRC in host order to set at @idx
|
|
*
|
|
* Modify the CSRC at index @idx in @buffer to @csrc.
|
|
*/
|
|
void
|
|
gst_rtp_buffer_set_csrc (GstBuffer * buffer, guint8 idx, guint32 csrc)
|
|
{
|
|
guint8 *data;
|
|
|
|
data = GST_BUFFER_DATA (buffer);
|
|
|
|
g_return_if_fail (idx < GST_RTP_HEADER_CSRC_COUNT (data));
|
|
|
|
GST_WRITE_UINT32_BE (GST_RTP_HEADER_CSRC_LIST_OFFSET (data, idx), csrc);
|
|
}
|
|
|
|
/**
|
|
* gst_rtp_buffer_get_marker:
|
|
* @buffer: the buffer
|
|
*
|
|
* Check if the marker bit is set on the RTP packet in @buffer.
|
|
*
|
|
* Returns: TRUE if @buffer has the marker bit set.
|
|
*/
|
|
gboolean
|
|
gst_rtp_buffer_get_marker (GstBuffer * buffer)
|
|
{
|
|
return GST_RTP_HEADER_MARKER (GST_BUFFER_DATA (buffer));
|
|
}
|
|
|
|
/**
|
|
* gst_rtp_buffer_set_marker:
|
|
* @buffer: the buffer
|
|
* @marker: the new marker
|
|
*
|
|
* Set the marker bit on the RTP packet in @buffer to @marker.
|
|
*/
|
|
void
|
|
gst_rtp_buffer_set_marker (GstBuffer * buffer, gboolean marker)
|
|
{
|
|
GST_RTP_HEADER_MARKER (GST_BUFFER_DATA (buffer)) = marker;
|
|
}
|
|
|
|
/**
|
|
* gst_rtp_buffer_get_payload_type:
|
|
* @buffer: the buffer
|
|
*
|
|
* Get the payload type of the RTP packet in @buffer.
|
|
*
|
|
* Returns: The payload type.
|
|
*/
|
|
guint8
|
|
gst_rtp_buffer_get_payload_type (GstBuffer * buffer)
|
|
{
|
|
return GST_RTP_HEADER_PAYLOAD_TYPE (GST_BUFFER_DATA (buffer));
|
|
}
|
|
|
|
/**
|
|
* gst_rtp_buffer_list_get_payload_type:
|
|
* @list: the buffer list
|
|
*
|
|
* Get the payload type of the first RTP packet in @list.
|
|
* All packets in @list should have the same payload type.
|
|
*
|
|
* Returns: The payload type.
|
|
*
|
|
* Since: 0.10.24
|
|
*/
|
|
guint8
|
|
gst_rtp_buffer_list_get_payload_type (GstBufferList * list)
|
|
{
|
|
GstBuffer *buffer;
|
|
|
|
buffer = gst_buffer_list_get (list, 0, 0);
|
|
g_return_val_if_fail (buffer != NULL, 0);
|
|
|
|
return GST_RTP_HEADER_PAYLOAD_TYPE (GST_BUFFER_DATA (buffer));
|
|
}
|
|
|
|
/**
|
|
* gst_rtp_buffer_set_payload_type:
|
|
* @buffer: the buffer
|
|
* @payload_type: the new type
|
|
*
|
|
* Set the payload type of the RTP packet in @buffer to @payload_type.
|
|
*/
|
|
void
|
|
gst_rtp_buffer_set_payload_type (GstBuffer * buffer, guint8 payload_type)
|
|
{
|
|
g_return_if_fail (payload_type < 0x80);
|
|
|
|
GST_RTP_HEADER_PAYLOAD_TYPE (GST_BUFFER_DATA (buffer)) = payload_type;
|
|
}
|
|
|
|
static GstBufferListItem
|
|
set_pt_header (GstBuffer ** buffer, guint group, guint idx, guint8 * pt)
|
|
{
|
|
GST_RTP_HEADER_PAYLOAD_TYPE (GST_BUFFER_DATA (*buffer)) = *pt;
|
|
return GST_BUFFER_LIST_SKIP_GROUP;
|
|
}
|
|
|
|
/**
|
|
* gst_rtp_buffer_list_set_payload_type:
|
|
* @list: the buffer list
|
|
* @payload_type: the new type
|
|
*
|
|
* Set the payload type of each RTP packet in @list to @payload_type.
|
|
*
|
|
* Since: 0.10.24
|
|
*/
|
|
void
|
|
gst_rtp_buffer_list_set_payload_type (GstBufferList * list, guint8 payload_type)
|
|
{
|
|
g_return_if_fail (payload_type < 0x80);
|
|
|
|
gst_buffer_list_foreach (list, (GstBufferListFunc) set_pt_header,
|
|
&payload_type);
|
|
}
|
|
|
|
/**
|
|
* gst_rtp_buffer_get_seq:
|
|
* @buffer: the buffer
|
|
*
|
|
* Get the sequence number of the RTP packet in @buffer.
|
|
*
|
|
* Returns: The sequence number in host order.
|
|
*/
|
|
guint16
|
|
gst_rtp_buffer_get_seq (GstBuffer * buffer)
|
|
{
|
|
return g_ntohs (GST_RTP_HEADER_SEQ (GST_BUFFER_DATA (buffer)));
|
|
}
|
|
|
|
/**
|
|
* gst_rtp_buffer_set_seq:
|
|
* @buffer: the buffer
|
|
* @seq: the new sequence number
|
|
*
|
|
* Set the sequence number of the RTP packet in @buffer to @seq.
|
|
*/
|
|
void
|
|
gst_rtp_buffer_set_seq (GstBuffer * buffer, guint16 seq)
|
|
{
|
|
GST_RTP_HEADER_SEQ (GST_BUFFER_DATA (buffer)) = g_htons (seq);
|
|
}
|
|
|
|
static GstBufferListItem
|
|
set_seq_header (GstBuffer ** buffer, guint group, guint idx, guint16 * seq)
|
|
{
|
|
GST_RTP_HEADER_SEQ (GST_BUFFER_DATA (*buffer)) = g_htons (*seq);
|
|
(*seq)++;
|
|
return GST_BUFFER_LIST_SKIP_GROUP;
|
|
}
|
|
|
|
/**
|
|
* gst_rtp_buffer_list_set_seq:
|
|
* @list: the buffer list
|
|
* @seq: the new sequence number
|
|
*
|
|
* Set the sequence number of each RTP packet in @list to @seq.
|
|
*
|
|
* Returns: The seq number of the last packet in the list + 1.
|
|
*
|
|
* Since: 0.10.24
|
|
*/
|
|
guint16
|
|
gst_rtp_buffer_list_set_seq (GstBufferList * list, guint16 seq)
|
|
{
|
|
gst_buffer_list_foreach (list, (GstBufferListFunc) set_seq_header, &seq);
|
|
return seq;
|
|
}
|
|
|
|
/**
|
|
* gst_rtp_buffer_list_get_seq:
|
|
* @list: the buffer list
|
|
*
|
|
* Get the sequence number of the first RTP packet in @list.
|
|
* All packets within @list have the same sequence number.
|
|
*
|
|
* Returns: The seq number
|
|
*
|
|
* Since: 0.10.24
|
|
*/
|
|
guint16
|
|
gst_rtp_buffer_list_get_seq (GstBufferList * list)
|
|
{
|
|
GstBuffer *buffer;
|
|
|
|
buffer = gst_buffer_list_get (list, 0, 0);
|
|
g_return_val_if_fail (buffer != NULL, 0);
|
|
|
|
return g_ntohl (GST_RTP_HEADER_SEQ (GST_BUFFER_DATA (buffer)));
|
|
}
|
|
|
|
|
|
/**
|
|
* gst_rtp_buffer_get_timestamp:
|
|
* @buffer: the buffer
|
|
*
|
|
* Get the timestamp of the RTP packet in @buffer.
|
|
*
|
|
* Returns: The timestamp in host order.
|
|
*/
|
|
guint32
|
|
gst_rtp_buffer_get_timestamp (GstBuffer * buffer)
|
|
{
|
|
return g_ntohl (GST_RTP_HEADER_TIMESTAMP (GST_BUFFER_DATA (buffer)));
|
|
}
|
|
|
|
/**
|
|
* gst_rtp_buffer_list_get_timestamp:
|
|
* @list: the buffer list
|
|
*
|
|
* Get the timestamp of the first RTP packet in @list.
|
|
* All packets within @list have the same timestamp.
|
|
*
|
|
* Returns: The timestamp in host order.
|
|
*
|
|
* Since: 0.10.24
|
|
*/
|
|
guint32
|
|
gst_rtp_buffer_list_get_timestamp (GstBufferList * list)
|
|
{
|
|
GstBuffer *buffer;
|
|
|
|
buffer = gst_buffer_list_get (list, 0, 0);
|
|
g_return_val_if_fail (buffer != NULL, 0);
|
|
|
|
return g_ntohl (GST_RTP_HEADER_TIMESTAMP (GST_BUFFER_DATA (buffer)));
|
|
}
|
|
|
|
/**
|
|
* gst_rtp_buffer_set_timestamp:
|
|
* @buffer: the buffer
|
|
* @timestamp: the new timestamp
|
|
*
|
|
* Set the timestamp of the RTP packet in @buffer to @timestamp.
|
|
*/
|
|
void
|
|
gst_rtp_buffer_set_timestamp (GstBuffer * buffer, guint32 timestamp)
|
|
{
|
|
GST_RTP_HEADER_TIMESTAMP (GST_BUFFER_DATA (buffer)) = g_htonl (timestamp);
|
|
}
|
|
|
|
|
|
static GstBufferListItem
|
|
set_timestamp_header (GstBuffer ** buffer, guint group, guint idx,
|
|
guint32 * timestamp)
|
|
{
|
|
GST_RTP_HEADER_TIMESTAMP (GST_BUFFER_DATA (*buffer)) = g_htonl (*timestamp);
|
|
return GST_BUFFER_LIST_SKIP_GROUP;
|
|
}
|
|
|
|
/**
|
|
* gst_rtp_buffer_list_set_timestamp:
|
|
* @list: the buffer list
|
|
* @timestamp: the new timestamp
|
|
*
|
|
* Set the timestamp of each RTP packet in @list to @timestamp.
|
|
*
|
|
* Since: 0.10.24
|
|
*/
|
|
void
|
|
gst_rtp_buffer_list_set_timestamp (GstBufferList * list, guint32 timestamp)
|
|
{
|
|
gst_buffer_list_foreach (list, (GstBufferListFunc) set_timestamp_header,
|
|
×tamp);
|
|
}
|
|
|
|
/**
|
|
* gst_rtp_buffer_get_payload_subbuffer:
|
|
* @buffer: the buffer
|
|
* @offset: the offset in the payload
|
|
* @len: the length in the payload
|
|
*
|
|
* Create a subbuffer of the payload of the RTP packet in @buffer. @offset bytes
|
|
* are skipped in the payload and the subbuffer will be of size @len.
|
|
* If @len is -1 the total payload starting from @offset if subbuffered.
|
|
*
|
|
* Returns: A new buffer with the specified data of the payload.
|
|
*
|
|
* Since: 0.10.10
|
|
*/
|
|
GstBuffer *
|
|
gst_rtp_buffer_get_payload_subbuffer (GstBuffer * buffer, guint offset,
|
|
guint len)
|
|
{
|
|
guint poffset, plen;
|
|
|
|
plen = gst_rtp_buffer_get_payload_len (buffer);
|
|
/* we can't go past the length */
|
|
if (G_UNLIKELY (offset >= plen))
|
|
goto wrong_offset;
|
|
|
|
/* apply offset */
|
|
poffset = gst_rtp_buffer_get_header_len (buffer) + offset;
|
|
plen -= offset;
|
|
|
|
/* see if we need to shrink the buffer based on @len */
|
|
if (len != -1 && len < plen)
|
|
plen = len;
|
|
|
|
return gst_buffer_create_sub (buffer, poffset, plen);
|
|
|
|
/* ERRORS */
|
|
wrong_offset:
|
|
{
|
|
g_warning ("offset=%u should be less then plen=%u", offset, plen);
|
|
return NULL;
|
|
}
|
|
}
|
|
|
|
/**
|
|
* gst_rtp_buffer_get_payload_buffer:
|
|
* @buffer: the buffer
|
|
*
|
|
* Create a buffer of the payload of the RTP packet in @buffer. This function
|
|
* will internally create a subbuffer of @buffer so that a memcpy can be
|
|
* avoided.
|
|
*
|
|
* Returns: A new buffer with the data of the payload.
|
|
*/
|
|
GstBuffer *
|
|
gst_rtp_buffer_get_payload_buffer (GstBuffer * buffer)
|
|
{
|
|
return gst_rtp_buffer_get_payload_subbuffer (buffer, 0, -1);
|
|
}
|
|
|
|
/**
|
|
* gst_rtp_buffer_get_payload_len:
|
|
* @buffer: the buffer
|
|
*
|
|
* Get the length of the payload of the RTP packet in @buffer.
|
|
*
|
|
* Returns: The length of the payload in @buffer.
|
|
*/
|
|
guint
|
|
gst_rtp_buffer_get_payload_len (GstBuffer * buffer)
|
|
{
|
|
guint len, size;
|
|
guint8 *data;
|
|
|
|
size = GST_BUFFER_SIZE (buffer);
|
|
data = GST_BUFFER_DATA (buffer);
|
|
|
|
len = size - gst_rtp_buffer_get_header_len (buffer);
|
|
|
|
if (GST_RTP_HEADER_PADDING (data))
|
|
len -= data[size - 1];
|
|
|
|
return len;
|
|
}
|
|
|
|
/**
|
|
* gst_rtp_buffer_list_get_payload_len:
|
|
* @list: the buffer list
|
|
*
|
|
* Get the length of the payload of the RTP packet in @list.
|
|
*
|
|
* Returns: The length of the payload in @list.
|
|
*
|
|
* Since: 0.10.24
|
|
*/
|
|
guint
|
|
gst_rtp_buffer_list_get_payload_len (GstBufferList * list)
|
|
{
|
|
guint len;
|
|
GstBufferListIterator *it;
|
|
|
|
it = gst_buffer_list_iterate (list);
|
|
len = 0;
|
|
|
|
while (gst_buffer_list_iterator_next_group (it)) {
|
|
guint i;
|
|
GstBuffer *buf;
|
|
|
|
i = 0;
|
|
while ((buf = gst_buffer_list_iterator_next (it))) {
|
|
/* skip the RTP header */
|
|
if (!i++)
|
|
continue;
|
|
/* take the size of the current buffer */
|
|
len += GST_BUFFER_SIZE (buf);
|
|
}
|
|
}
|
|
|
|
gst_buffer_list_iterator_free (it);
|
|
|
|
return len;
|
|
}
|
|
|
|
/**
|
|
* gst_rtp_buffer_get_payload:
|
|
* @buffer: the buffer
|
|
*
|
|
* Get a pointer to the payload data in @buffer. This pointer is valid as long
|
|
* as a reference to @buffer is held.
|
|
*
|
|
* Returns: A pointer to the payload data in @buffer.
|
|
*/
|
|
gpointer
|
|
gst_rtp_buffer_get_payload (GstBuffer * buffer)
|
|
{
|
|
return GST_BUFFER_DATA (buffer) + gst_rtp_buffer_get_header_len (buffer);
|
|
}
|
|
|
|
/**
|
|
* gst_rtp_buffer_default_clock_rate:
|
|
* @payload_type: the static payload type
|
|
*
|
|
* Get the default clock-rate for the static payload type @payload_type.
|
|
*
|
|
* Returns: the default clock rate or -1 if the payload type is not static or
|
|
* the clock-rate is undefined.
|
|
*
|
|
* Since: 0.10.13
|
|
*/
|
|
guint32
|
|
gst_rtp_buffer_default_clock_rate (guint8 payload_type)
|
|
{
|
|
const GstRTPPayloadInfo *info;
|
|
guint32 res;
|
|
|
|
info = gst_rtp_payload_info_for_pt (payload_type);
|
|
if (!info)
|
|
return -1;
|
|
|
|
res = info->clock_rate;
|
|
/* 0 means unknown so we have to return -1 from this function */
|
|
if (res == 0)
|
|
res = -1;
|
|
|
|
return res;
|
|
}
|
|
|
|
/**
|
|
* gst_rtp_buffer_compare_seqnum:
|
|
* @seqnum1: a sequence number
|
|
* @seqnum2: a sequence number
|
|
*
|
|
* Compare two sequence numbers, taking care of wraparounds. This function
|
|
* returns the difference between @seqnum1 and @seqnum2.
|
|
*
|
|
* Returns: a negative value if @seqnum1 is bigger than @seqnum2, 0 if they
|
|
* are equal or a positive value if @seqnum1 is smaller than @segnum2.
|
|
*
|
|
* Since: 0.10.15
|
|
*/
|
|
gint
|
|
gst_rtp_buffer_compare_seqnum (guint16 seqnum1, guint16 seqnum2)
|
|
{
|
|
return (gint16) (seqnum2 - seqnum1);
|
|
}
|
|
|
|
/**
|
|
* gst_rtp_buffer_ext_timestamp:
|
|
* @exttimestamp: a previous extended timestamp
|
|
* @timestamp: a new timestamp
|
|
*
|
|
* Update the @exttimestamp field with @timestamp. For the first call of the
|
|
* method, @exttimestamp should point to a location with a value of -1.
|
|
*
|
|
* This function makes sure that the returned value is a constantly increasing
|
|
* value even in the case where there is a timestamp wraparound.
|
|
*
|
|
* Returns: The extended timestamp of @timestamp.
|
|
*
|
|
* Since: 0.10.15
|
|
*/
|
|
guint64
|
|
gst_rtp_buffer_ext_timestamp (guint64 * exttimestamp, guint32 timestamp)
|
|
{
|
|
guint64 result, diff, ext;
|
|
|
|
g_return_val_if_fail (exttimestamp != NULL, -1);
|
|
|
|
ext = *exttimestamp;
|
|
|
|
if (ext == -1) {
|
|
result = timestamp;
|
|
} else {
|
|
/* pick wraparound counter from previous timestamp and add to new timestamp */
|
|
result = timestamp + (ext & ~(G_GINT64_CONSTANT (0xffffffff)));
|
|
|
|
/* check for timestamp wraparound */
|
|
if (result < ext)
|
|
diff = ext - result;
|
|
else
|
|
diff = result - ext;
|
|
|
|
if (diff > G_MAXINT32) {
|
|
/* timestamp went backwards more than allowed, we wrap around and get
|
|
* updated extended timestamp. */
|
|
result += (G_GINT64_CONSTANT (1) << 32);
|
|
}
|
|
}
|
|
*exttimestamp = result;
|
|
|
|
return result;
|
|
}
|
|
|
|
/**
|
|
* gst_rtp_buffer_get_extension_onebyte_header:
|
|
* @buffer: the buffer
|
|
* @id: The ID of the header extension to be read (between 1 and 14).
|
|
* @nth: Read the nth extension packet with the requested ID
|
|
* @data: location for data
|
|
* @size: the size of the data in bytes
|
|
*
|
|
* Parses RFC 5285 style header extensions with a one byte header. It will
|
|
* return the nth extension with the requested id.
|
|
*
|
|
* Returns: TRUE if @buffer had the requested header extension
|
|
*
|
|
* Since: 0.10.31
|
|
*/
|
|
|
|
gboolean
|
|
gst_rtp_buffer_get_extension_onebyte_header (GstBuffer * buffer, guint8 id,
|
|
guint nth, gpointer * data, guint * size)
|
|
{
|
|
guint16 bits;
|
|
guint8 *pdata;
|
|
guint wordlen;
|
|
gulong offset = 0;
|
|
guint count = 0;
|
|
|
|
g_return_val_if_fail (id > 0 && id < 15, FALSE);
|
|
|
|
if (!gst_rtp_buffer_get_extension_data (buffer, &bits, (gpointer) & pdata,
|
|
&wordlen))
|
|
return FALSE;
|
|
|
|
if (bits != 0xBEDE)
|
|
return FALSE;
|
|
|
|
for (;;) {
|
|
guint8 read_id, read_len;
|
|
|
|
if (offset + 1 >= wordlen * 4)
|
|
break;
|
|
|
|
read_id = GST_READ_UINT8 (pdata + offset) >> 4;
|
|
read_len = (GST_READ_UINT8 (pdata + offset) & 0x0F) + 1;
|
|
offset += 1;
|
|
|
|
/* ID 0 means its padding, skip */
|
|
if (read_id == 0)
|
|
continue;
|
|
|
|
/* ID 15 is special and means we should stop parsing */
|
|
if (read_id == 15)
|
|
break;
|
|
|
|
/* Ignore extension headers where the size does not fit */
|
|
if (offset + read_len > wordlen * 4)
|
|
break;
|
|
|
|
/* If we have the right one */
|
|
if (id == read_id) {
|
|
if (nth == count) {
|
|
if (data)
|
|
*data = pdata + offset;
|
|
if (size)
|
|
*size = read_len;
|
|
|
|
return TRUE;
|
|
}
|
|
|
|
count++;
|
|
}
|
|
offset += read_len;
|
|
|
|
if (offset >= wordlen * 4)
|
|
break;
|
|
}
|
|
|
|
return FALSE;
|
|
}
|
|
|
|
/**
|
|
* gst_rtp_buffer_get_extension_twobytes_header:
|
|
* @buffer: the buffer
|
|
* @appbits: Application specific bits
|
|
* @id: The ID of the header extension to be read (between 1 and 14).
|
|
* @nth: Read the nth extension packet with the requested ID
|
|
* @data: location for data
|
|
* @size: the size of the data in bytes
|
|
*
|
|
* Parses RFC 5285 style header extensions with a two bytes header. It will
|
|
* return the nth extension with the requested id.
|
|
*
|
|
* Returns: TRUE if @buffer had the requested header extension
|
|
*
|
|
* Since: 0.10.31
|
|
*/
|
|
|
|
gboolean
|
|
gst_rtp_buffer_get_extension_twobytes_header (GstBuffer * buffer,
|
|
guint8 * appbits, guint8 id, guint nth, gpointer * data, guint * size)
|
|
{
|
|
guint16 bits;
|
|
guint8 *pdata;
|
|
guint wordlen;
|
|
guint bytelen;
|
|
gulong offset = 0;
|
|
guint count = 0;
|
|
|
|
if (!gst_rtp_buffer_get_extension_data (buffer, &bits, (gpointer) & pdata,
|
|
&wordlen))
|
|
return FALSE;
|
|
|
|
if (bits >> 4 != 0x100)
|
|
return FALSE;
|
|
|
|
bytelen = wordlen * 4;
|
|
|
|
for (;;) {
|
|
guint8 read_id, read_len;
|
|
|
|
if (offset + 2 >= bytelen)
|
|
break;
|
|
|
|
read_id = GST_READ_UINT8 (pdata + offset);
|
|
offset += 1;
|
|
|
|
if (read_id == 0)
|
|
continue;
|
|
|
|
read_len = GST_READ_UINT8 (pdata + offset);
|
|
offset += 1;
|
|
|
|
/* Ignore extension headers where the size does not fit */
|
|
if (offset + read_len > bytelen)
|
|
break;
|
|
|
|
/* If we have the right one, return it */
|
|
if (id == read_id) {
|
|
if (nth == count) {
|
|
if (data)
|
|
*data = pdata + offset;
|
|
if (size)
|
|
*size = read_len;
|
|
if (appbits)
|
|
*appbits = bits;
|
|
|
|
return TRUE;
|
|
}
|
|
|
|
count++;
|
|
}
|
|
offset += read_len;
|
|
}
|
|
|
|
return FALSE;
|
|
}
|
|
|
|
static guint
|
|
get_onebyte_header_end_offset (guint8 * pdata, guint wordlen)
|
|
{
|
|
guint offset = 0;
|
|
guint bytelen = wordlen * 4;
|
|
guint paddingcount = 0;
|
|
|
|
while (offset + 1 < bytelen) {
|
|
guint8 read_id, read_len;
|
|
|
|
read_id = GST_READ_UINT8 (pdata + offset) >> 4;
|
|
read_len = (GST_READ_UINT8 (pdata + offset) & 0x0F) + 1;
|
|
offset += 1;
|
|
|
|
/* ID 0 means its padding, skip */
|
|
if (read_id == 0) {
|
|
paddingcount++;
|
|
continue;
|
|
}
|
|
|
|
paddingcount = 0;
|
|
|
|
/* ID 15 is special and means we should stop parsing */
|
|
/* It also means we can't add an extra packet */
|
|
if (read_id == 15)
|
|
return 0;
|
|
|
|
/* Ignore extension headers where the size does not fit */
|
|
if (offset + read_len > bytelen)
|
|
return 0;
|
|
|
|
offset += read_len;
|
|
}
|
|
|
|
return offset - paddingcount;
|
|
}
|
|
|
|
/**
|
|
* gst_rtp_buffer_add_extension_onebyte_header:
|
|
* @buffer: the buffer
|
|
* @id: The ID of the header extension (between 1 and 14).
|
|
* @data: location for data
|
|
* @size: the size of the data in bytes
|
|
*
|
|
* Adds a RFC 5285 header extension with a one byte header to the end of the
|
|
* RTP header. If there is already a RFC 5285 header extension with a one byte
|
|
* header, the new extension will be appended.
|
|
* It will not work if there is already a header extension that does not follow
|
|
* the mecanism described in RFC 5285 or if there is a header extension with
|
|
* a two bytes header as described in RFC 5285. In that case, use
|
|
* gst_rtp_buffer_add_extension_twobytes_header()
|
|
*
|
|
* Returns: %TRUE if header extension could be added
|
|
*
|
|
* Since: 0.10.31
|
|
*/
|
|
|
|
gboolean
|
|
gst_rtp_buffer_add_extension_onebyte_header (GstBuffer * buffer, guint8 id,
|
|
gpointer data, guint size)
|
|
{
|
|
guint16 bits;
|
|
guint8 *pdata;
|
|
guint wordlen;
|
|
gboolean has_bit;
|
|
|
|
g_return_val_if_fail (id > 0 && id < 15, FALSE);
|
|
g_return_val_if_fail (size >= 1 && size <= 16, FALSE);
|
|
g_return_val_if_fail (gst_buffer_is_writable (buffer), FALSE);
|
|
|
|
has_bit = gst_rtp_buffer_get_extension_data (buffer, &bits,
|
|
(gpointer) & pdata, &wordlen);
|
|
|
|
if (has_bit) {
|
|
gulong offset = 0;
|
|
guint8 *nextext;
|
|
guint extlen;
|
|
|
|
if (bits != 0xBEDE)
|
|
return FALSE;
|
|
|
|
offset = get_onebyte_header_end_offset (pdata, wordlen);
|
|
if (offset == 0)
|
|
return FALSE;
|
|
|
|
nextext = pdata + offset;
|
|
offset = nextext - GST_BUFFER_DATA (buffer);
|
|
|
|
/* Don't add extra header if there isn't enough space */
|
|
if (GST_BUFFER_SIZE (buffer) < offset + size + 1)
|
|
return FALSE;
|
|
|
|
nextext[0] = (id << 4) | (0x0F & (size - 1));
|
|
memcpy (nextext + 1, data, size);
|
|
|
|
extlen = nextext - pdata + size + 1;
|
|
if (extlen % 4) {
|
|
wordlen = extlen / 4 + 1;
|
|
memset (nextext + size + 1, 0, 4 - extlen % 4);
|
|
} else {
|
|
wordlen = extlen / 4;
|
|
}
|
|
|
|
gst_rtp_buffer_set_extension_data (buffer, 0xBEDE, wordlen);
|
|
} else {
|
|
wordlen = (size + 1) / 4 + (((size + 1) % 4) ? 1 : 0);
|
|
|
|
gst_rtp_buffer_set_extension_data (buffer, 0xBEDE, wordlen);
|
|
|
|
gst_rtp_buffer_get_extension_data (buffer, &bits,
|
|
(gpointer) & pdata, &wordlen);
|
|
|
|
pdata[0] = (id << 4) | (0x0F & (size - 1));
|
|
memcpy (pdata + 1, data, size);
|
|
|
|
if ((size + 1) % 4)
|
|
memset (pdata + size + 1, 0, 4 - ((size + 1) % 4));
|
|
}
|
|
|
|
return TRUE;
|
|
}
|
|
|
|
|
|
static guint
|
|
get_twobytes_header_end_offset (guint8 * pdata, guint wordlen)
|
|
{
|
|
guint offset = 0;
|
|
guint bytelen = wordlen * 4;
|
|
guint paddingcount = 0;
|
|
|
|
while (offset + 2 < bytelen) {
|
|
guint8 read_id, read_len;
|
|
|
|
read_id = GST_READ_UINT8 (pdata + offset);
|
|
offset += 1;
|
|
|
|
/* ID 0 means its padding, skip */
|
|
if (read_id == 0) {
|
|
paddingcount++;
|
|
continue;
|
|
}
|
|
|
|
paddingcount = 0;
|
|
|
|
read_len = GST_READ_UINT8 (pdata + offset);
|
|
offset += 1;
|
|
|
|
/* Ignore extension headers where the size does not fit */
|
|
if (offset + read_len > bytelen)
|
|
return 0;
|
|
|
|
offset += read_len;
|
|
}
|
|
|
|
return offset - paddingcount;
|
|
}
|
|
|
|
/**
|
|
* gst_rtp_buffer_add_extension_twobytes_header:
|
|
* @buffer: the buffer
|
|
* @appbits: Application specific bits
|
|
* @id: The ID of the header extension
|
|
* @data: location for data
|
|
* @size: the size of the data in bytes
|
|
*
|
|
* Adds a RFC 5285 header extension with a two bytes header to the end of the
|
|
* RTP header. If there is already a RFC 5285 header extension with a two bytes
|
|
* header, the new extension will be appended.
|
|
* It will not work if there is already a header extension that does not follow
|
|
* the mecanism described in RFC 5285 or if there is a header extension with
|
|
* a one byte header as described in RFC 5285. In that case, use
|
|
* gst_rtp_buffer_add_extension_onebyte_header()
|
|
*
|
|
* Returns: %TRUE if header extension could be added
|
|
*
|
|
* Since: 0.10.31
|
|
*/
|
|
|
|
gboolean
|
|
gst_rtp_buffer_add_extension_twobytes_header (GstBuffer * buffer,
|
|
guint8 appbits, guint8 id, gpointer data, guint size)
|
|
{
|
|
guint16 bits;
|
|
guint8 *pdata;
|
|
guint wordlen;
|
|
gboolean has_bit;
|
|
|
|
g_return_val_if_fail ((appbits & 0xF0) == 0, FALSE);
|
|
g_return_val_if_fail (size < 256, FALSE);
|
|
g_return_val_if_fail (gst_buffer_is_writable (buffer), FALSE);
|
|
|
|
has_bit = gst_rtp_buffer_get_extension_data (buffer, &bits,
|
|
(gpointer) & pdata, &wordlen);
|
|
|
|
if (has_bit) {
|
|
gulong offset = 0;
|
|
guint8 *nextext;
|
|
guint extlen;
|
|
|
|
if (bits != ((0x100 << 4) | (appbits & 0x0f)))
|
|
return FALSE;
|
|
|
|
offset = get_twobytes_header_end_offset (pdata, wordlen);
|
|
|
|
nextext = pdata + offset;
|
|
|
|
offset = nextext - GST_BUFFER_DATA (buffer);
|
|
|
|
/* Don't add extra header if there isn't enough space */
|
|
if (GST_BUFFER_SIZE (buffer) < offset + size + 2)
|
|
return FALSE;
|
|
|
|
nextext[0] = id;
|
|
nextext[1] = size;
|
|
memcpy (nextext + 2, data, size);
|
|
|
|
extlen = nextext - pdata + size + 2;
|
|
if (extlen % 4) {
|
|
wordlen = extlen / 4 + 1;
|
|
memset (nextext + size + 2, 0, 4 - extlen % 4);
|
|
} else {
|
|
wordlen = extlen / 4;
|
|
}
|
|
|
|
gst_rtp_buffer_set_extension_data (buffer, (0x100 << 4) | (appbits & 0x0F),
|
|
wordlen);
|
|
} else {
|
|
wordlen = (size + 2) / 4 + (((size + 2) % 4) ? 1 : 0);
|
|
|
|
gst_rtp_buffer_set_extension_data (buffer, (0x100 << 4) | (appbits & 0x0F),
|
|
wordlen);
|
|
|
|
gst_rtp_buffer_get_extension_data (buffer, &bits,
|
|
(gpointer) & pdata, &wordlen);
|
|
|
|
pdata[0] = id;
|
|
pdata[1] = size;
|
|
memcpy (pdata + 2, data, size);
|
|
if ((size + 2) % 4)
|
|
memset (pdata + size + 2, 0, 4 - ((size + 2) % 4));
|
|
}
|
|
|
|
return TRUE;
|
|
}
|
|
|
|
/**
|
|
* gst_rtp_buffer_list_get_extension_onebyte_header:
|
|
* @bufferlist: the bufferlist
|
|
* @group_idx: The index of the group in the #GstBufferList
|
|
* @id: The ID of the header extension to be read (between 1 and 14).
|
|
* @nth: Read the nth extension packet with the requested ID
|
|
* @data: location for data
|
|
* @size: the size of the data in bytes
|
|
*
|
|
* Parses RFC 5285 style header extensions with a one byte header. It will
|
|
* return the nth extension with the requested id.
|
|
*
|
|
* Returns: TRUE if @buffer had the requested header extension
|
|
*
|
|
* Since: 0.10.31
|
|
*/
|
|
|
|
gboolean
|
|
gst_rtp_buffer_list_get_extension_onebyte_header (GstBufferList * bufferlist,
|
|
guint group_idx, guint8 id, guint nth, gpointer * data, guint * size)
|
|
{
|
|
GstBuffer *buffer;
|
|
|
|
buffer = gst_buffer_list_get (bufferlist, group_idx, 0);
|
|
|
|
if (!buffer)
|
|
return FALSE;
|
|
|
|
return gst_rtp_buffer_get_extension_onebyte_header (buffer, id, nth, data,
|
|
size);
|
|
}
|
|
|
|
|
|
/**
|
|
* gst_rtp_buffer_list_get_extension_twobytes_header:
|
|
* @bufferlist: the bufferlist
|
|
* @group_idx: The index of the group in the #GstBufferList
|
|
* @appbits: Application specific bits
|
|
* @id: The ID of the header extension to be read (between 1 and 14).
|
|
* @nth: Read the nth extension packet with the requested ID
|
|
* @data: location for data
|
|
* @size: the size of the data in bytes
|
|
*
|
|
* Parses RFC 5285 style header extensions with a two bytes header. It will
|
|
* return the nth extension with the requested id.
|
|
*
|
|
* Returns: TRUE if @buffer had the requested header extension
|
|
*
|
|
* Since: 0.10.31
|
|
*/
|
|
|
|
gboolean
|
|
gst_rtp_buffer_list_get_extension_twobytes_header (GstBufferList * bufferlist,
|
|
guint group_idx, guint8 * appbits, guint8 id, guint nth,
|
|
gpointer * data, guint * size)
|
|
{
|
|
GstBuffer *buffer;
|
|
|
|
buffer = gst_buffer_list_get (bufferlist, group_idx, 0);
|
|
|
|
if (!buffer)
|
|
return FALSE;
|
|
|
|
return gst_rtp_buffer_get_extension_twobytes_header (buffer, appbits, id,
|
|
nth, data, size);
|
|
}
|
|
|
|
/**
|
|
* gst_rtp_buffer_list_add_extension_onebyte_header:
|
|
* @it: a #GstBufferListIterator pointing right after the #GstBuffer where
|
|
* the header extension should be added
|
|
* @id: The ID of the header extension (between 1 and 14).
|
|
* @data: location for data
|
|
* @size: the size of the data in bytes
|
|
*
|
|
* Adds a RFC 5285 header extension with a one byte header to the end of the
|
|
* RTP header. If there is already a RFC 5285 header extension with a one byte
|
|
* header, the new extension will be appended.
|
|
* It will not work if there is already a header extension that does not follow
|
|
* the mecanism described in RFC 5285 or if there is a header extension with
|
|
* a two bytes header as described in RFC 5285. In that case, use
|
|
* gst_rtp_buffer_list_add_extension_twobytes_header()
|
|
*
|
|
* This function will not modify the data section of the RTP buffer, only
|
|
* the header.
|
|
*
|
|
* Returns: %TRUE if header extension could be added
|
|
*
|
|
* Since: 0.10.31
|
|
*/
|
|
|
|
gboolean
|
|
gst_rtp_buffer_list_add_extension_onebyte_header (GstBufferListIterator * it,
|
|
guint8 id, gpointer data, guint size)
|
|
{
|
|
GstBuffer *buffer;
|
|
guint16 bits;
|
|
guint8 *pdata;
|
|
guint wordlen;
|
|
gboolean retval;
|
|
guint endoffset = 0;
|
|
|
|
g_return_val_if_fail (gst_buffer_list_iterator_n_buffers (it) == 1, FALSE);
|
|
g_return_val_if_fail (id > 0 && id < 15, FALSE);
|
|
g_return_val_if_fail (size >= 1 && size <= 16, FALSE);
|
|
|
|
buffer = gst_buffer_list_iterator_steal (it);
|
|
|
|
if (GST_RTP_HEADER_EXTENSION (GST_BUFFER_DATA (buffer))) {
|
|
gst_rtp_buffer_get_extension_data (buffer, &bits, (gpointer) & pdata,
|
|
&wordlen);
|
|
|
|
if (bits != 0xBEDE)
|
|
return FALSE;
|
|
|
|
endoffset = get_onebyte_header_end_offset (pdata, wordlen);
|
|
if (endoffset == 0)
|
|
return FALSE;
|
|
endoffset += pdata - GST_BUFFER_DATA (buffer);
|
|
} else {
|
|
endoffset = GST_BUFFER_SIZE (buffer) + 4;
|
|
}
|
|
|
|
if (endoffset + size + 1 > GST_BUFFER_SIZE (buffer)) {
|
|
guint newsize;
|
|
GstBuffer *newbuffer;
|
|
|
|
newsize = endoffset + size + 1;
|
|
if (newsize % 4)
|
|
newsize += 4 - (newsize % 4);
|
|
newbuffer = gst_buffer_new_and_alloc (newsize);
|
|
memcpy (GST_BUFFER_DATA (newbuffer), GST_BUFFER_DATA (buffer),
|
|
GST_BUFFER_SIZE (buffer));
|
|
gst_buffer_copy_metadata (newbuffer, buffer, GST_BUFFER_COPY_ALL);
|
|
gst_buffer_unref (buffer);
|
|
buffer = newbuffer;
|
|
} else {
|
|
buffer = gst_buffer_make_writable (buffer);
|
|
}
|
|
|
|
retval = gst_rtp_buffer_add_extension_onebyte_header (buffer, id, data, size);
|
|
|
|
gst_buffer_list_iterator_take (it, buffer);
|
|
|
|
return retval;
|
|
}
|
|
|
|
/**
|
|
* gst_rtp_buffer_list_add_extension_twobytes_header:
|
|
* @it: a #GstBufferListIterator pointing right after the #GstBuffer where
|
|
* the header extension should be added
|
|
* @appbits: Application specific bits
|
|
* @id: The ID of the header extension
|
|
* @data: location for data
|
|
* @size: the size of the data in bytes
|
|
*
|
|
* Adds a RFC 5285 header extension with a two bytes header to the end of the
|
|
* RTP header. If there is already a RFC 5285 header extension with a two bytes
|
|
* header, the new extension will be appended.
|
|
* It will not work if there is already a header extension that does not follow
|
|
* the mecanism described in RFC 5285 or if there is a header extension with
|
|
* a one byte header as described in RFC 5285. In that case, use
|
|
* gst_rtp_buffer_add_extension_onebyte_header()
|
|
*
|
|
* This function will not modify the data section of the RTP buffer, only
|
|
* the header.
|
|
*
|
|
* Returns: %TRUE if header extension could be added
|
|
*
|
|
* Since: 0.10.31
|
|
*/
|
|
|
|
gboolean
|
|
gst_rtp_buffer_list_add_extension_twobytes_header (GstBufferListIterator * it,
|
|
guint8 appbits, guint8 id, gpointer data, guint size)
|
|
{
|
|
GstBuffer *buffer;
|
|
guint16 bits;
|
|
guint8 *pdata;
|
|
guint wordlen;
|
|
gboolean retval;
|
|
guint endoffset;
|
|
|
|
g_return_val_if_fail ((appbits & 0xF0) == 0, FALSE);
|
|
g_return_val_if_fail (size < 256, FALSE);
|
|
g_return_val_if_fail (gst_buffer_list_iterator_n_buffers (it) == 1, FALSE);
|
|
|
|
buffer = gst_buffer_list_iterator_steal (it);
|
|
|
|
if (GST_RTP_HEADER_EXTENSION (GST_BUFFER_DATA (buffer))) {
|
|
gst_rtp_buffer_get_extension_data (buffer, &bits, (gpointer) & pdata,
|
|
&wordlen);
|
|
|
|
if (bits != ((0x100 << 4) | (appbits & 0x0f)))
|
|
return FALSE;
|
|
|
|
endoffset = get_twobytes_header_end_offset (pdata, wordlen);
|
|
if (endoffset == 0)
|
|
return FALSE;
|
|
endoffset += pdata - GST_BUFFER_DATA (buffer);
|
|
} else {
|
|
endoffset = GST_BUFFER_SIZE (buffer) + 4;
|
|
}
|
|
|
|
if (endoffset + size + 2 > GST_BUFFER_SIZE (buffer)) {
|
|
guint newsize;
|
|
GstBuffer *newbuffer;
|
|
|
|
newsize = endoffset + size + 2;
|
|
if (newsize % 4)
|
|
newsize += 4 - newsize % 4;
|
|
newbuffer = gst_buffer_new_and_alloc (newsize);
|
|
memcpy (GST_BUFFER_DATA (newbuffer), GST_BUFFER_DATA (buffer),
|
|
GST_BUFFER_SIZE (buffer));
|
|
gst_buffer_copy_metadata (newbuffer, buffer, GST_BUFFER_COPY_ALL);
|
|
gst_buffer_unref (buffer);
|
|
buffer = newbuffer;
|
|
} else {
|
|
buffer = gst_buffer_make_writable (buffer);
|
|
}
|
|
|
|
retval = gst_rtp_buffer_add_extension_twobytes_header (buffer, appbits, id,
|
|
data, size);
|
|
|
|
gst_buffer_list_iterator_take (it, buffer);
|
|
|
|
return retval;
|
|
}
|
|
|
|
/**
|
|
* gst_rtp_buffer_list_from_buffer:
|
|
* @buffer: a #GstBuffer containing a RTP packet
|
|
*
|
|
* Splits a #GstBuffer into a #GstBufferList containing separate
|
|
* buffers for the header and data sections.
|
|
*
|
|
* Returns: a #GstBufferList
|
|
*/
|
|
|
|
GstBufferList *
|
|
gst_rtp_buffer_list_from_buffer (GstBuffer * buffer)
|
|
{
|
|
GstBufferList *bufferlist;
|
|
GstBuffer *sub;
|
|
GstBufferListIterator *it;
|
|
guint8 *payload;
|
|
|
|
bufferlist = gst_buffer_list_new ();
|
|
|
|
it = gst_buffer_list_iterate (bufferlist);
|
|
gst_buffer_list_iterator_add_group (it);
|
|
|
|
payload = gst_rtp_buffer_get_payload (buffer);
|
|
sub = gst_buffer_create_sub (buffer, 0, payload - GST_BUFFER_DATA (buffer));
|
|
gst_buffer_list_iterator_add (it, sub);
|
|
|
|
sub = gst_rtp_buffer_get_payload_buffer (buffer);
|
|
gst_buffer_list_iterator_add (it, sub);
|
|
|
|
gst_buffer_list_iterator_free (it);
|
|
|
|
return bufferlist;
|
|
}
|