/* GStreamer * Copyright (C) <2005> Philippe Khalaf * Copyright (C) <2006> Wim Taymans * * 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 * * * * 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. * * * * Last reviewed on 2006-07-17 (0.10.10) */ #include "gstrtpbuffer.h" #include #include #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); return FALSE; } wrong_version: { GST_DEBUG ("version check failed (%d != %d)", version, GST_RTP_VERSION); return FALSE; } wrong_padding: { GST_DEBUG ("padding check failed (%d - %d < %d)", len, header_len, padding); 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 * 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 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; /* each group should consists of 2 buffers: one containing the RTP header * and the other one the payload, FIXME, relax the requirement of only one * payload buffer. */ if (gst_buffer_list_iterator_n_buffers (it) != 2) goto invalid_list; /* get the RTP header */ rtpbuf = gst_buffer_list_iterator_next (it); packet_header = GST_BUFFER_DATA (rtpbuf); if (packet_header == NULL) goto invalid_list; /* get the payload */ paybuf = gst_buffer_list_iterator_next (it); packet_payload = GST_BUFFER_DATA (paybuf); if (packet_payload == NULL) { goto invalid_list; } payload_size = GST_BUFFER_SIZE (paybuf); if (payload_size == 0) { goto invalid_list; } /* the size of the RTP packet within the current group */ packet_size = GST_BUFFER_SIZE (rtpbuf) + payload_size; /* 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; } /* 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; }