/* 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 * * * * 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" #define GST_RTP_HEADER_LEN 12 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 guint16 seq; /* sequence number */ guint32 timestamp; /* timestamp */ guint32 ssrc; /* synchronization source */ guint32 csrc[1]; /* optional CSRC list */ } GstRTPHeader; #define GST_RTP_HEADER_VERSION(buf) (((GstRTPHeader *)(GST_BUFFER_DATA (buf)))->version) #define GST_RTP_HEADER_PADDING(buf) (((GstRTPHeader *)(GST_BUFFER_DATA (buf)))->padding) #define GST_RTP_HEADER_EXTENSION(buf) (((GstRTPHeader *)(GST_BUFFER_DATA (buf)))->extension) #define GST_RTP_HEADER_CSRC_COUNT(buf) (((GstRTPHeader *)(GST_BUFFER_DATA (buf)))->csrc_count) #define GST_RTP_HEADER_MARKER(buf) (((GstRTPHeader *)(GST_BUFFER_DATA (buf)))->marker) #define GST_RTP_HEADER_PAYLOAD_TYPE(buf)(((GstRTPHeader *)(GST_BUFFER_DATA (buf)))->payload_type) #define GST_RTP_HEADER_SEQ(buf) (((GstRTPHeader *)(GST_BUFFER_DATA (buf)))->seq) #define GST_RTP_HEADER_TIMESTAMP(buf) (((GstRTPHeader *)(GST_BUFFER_DATA (buf)))->timestamp) #define GST_RTP_HEADER_SSRC(buf) (((GstRTPHeader *)(GST_BUFFER_DATA (buf)))->ssrc) #define GST_RTP_HEADER_CSRC(buf,i) (((GstRTPHeader *)(GST_BUFFER_DATA (buf)))->csrc[i]) #define GST_RTP_HEADER_CSRC_SIZE(buf) (GST_RTP_HEADER_CSRC_COUNT(buf) * sizeof (guint32)) void gst_rtp_buffer_allocate_data (GstBuffer * buffer, guint payload_len, guint8 pad_len, guint8 csrc_count) { guint len; 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; GST_BUFFER_MALLOCDATA (buffer) = g_malloc (len); GST_BUFFER_DATA (buffer) = GST_BUFFER_MALLOCDATA (buffer); GST_BUFFER_SIZE (buffer) = len; /* fill in defaults */ GST_RTP_HEADER_VERSION (buffer) = GST_RTP_VERSION; GST_RTP_HEADER_PADDING (buffer) = FALSE; GST_RTP_HEADER_EXTENSION (buffer) = FALSE; GST_RTP_HEADER_CSRC_COUNT (buffer) = 0; GST_RTP_HEADER_MARKER (buffer) = FALSE; GST_RTP_HEADER_PAYLOAD_TYPE (buffer) = 0; GST_RTP_HEADER_SEQ (buffer) = 0; GST_RTP_HEADER_TIMESTAMP (buffer) = 0; GST_RTP_HEADER_SSRC (buffer) = 0; } 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; } GstBuffer * gst_rtp_buffer_new_copy_data (gpointer data, guint len) { return gst_rtp_buffer_new_take_data (g_memdup (data, len), len); } 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; } 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); } 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)); } 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; } 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; } gboolean gst_rtp_buffer_validate_data (guint8 * data, guint 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) >> 6; if (G_UNLIKELY (version != GST_RTP_VERSION)) goto wrong_version; /* calc header length with csrc */ csrc_count = (data[0] & 0x0f); header_len += csrc_count * sizeof (guint32); /* check for padding */ if (data[0] & 0x40) padding = data[len - 1]; else padding = 0; /* check if padding not bigger than packet and header */ if (G_UNLIKELY (len - header_len <= padding)) 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; } } 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 gst_rtp_buffer_validate_data (data, len); } void gst_rtp_buffer_set_packet_len (GstBuffer * buffer, guint len) { guint oldlen; g_return_if_fail (GST_IS_BUFFER (buffer)); oldlen = GST_BUFFER_SIZE (buffer); if (oldlen < len) { guint8 *newdata; newdata = g_realloc (GST_BUFFER_MALLOCDATA (buffer), len); GST_BUFFER_MALLOCDATA (buffer) = newdata; GST_BUFFER_DATA (buffer) = newdata; } GST_BUFFER_SIZE (buffer) = len; /* remove any padding */ GST_RTP_HEADER_PADDING (buffer) = FALSE; } guint gst_rtp_buffer_get_packet_len (GstBuffer * buffer) { g_return_val_if_fail (GST_IS_BUFFER (buffer), 0); return GST_BUFFER_SIZE (buffer); } guint8 gst_rtp_buffer_get_version (GstBuffer * buffer) { g_return_val_if_fail (GST_IS_BUFFER (buffer), 0); g_return_val_if_fail (GST_BUFFER_DATA (buffer) != NULL, 0); return GST_RTP_HEADER_VERSION (buffer); } void gst_rtp_buffer_set_version (GstBuffer * buffer, guint8 version) { g_return_if_fail (GST_IS_BUFFER (buffer)); g_return_if_fail (version < 0x04); g_return_if_fail (GST_BUFFER_DATA (buffer) != NULL); GST_RTP_HEADER_VERSION (buffer) = version; } gboolean gst_rtp_buffer_get_padding (GstBuffer * buffer) { g_return_val_if_fail (GST_IS_BUFFER (buffer), FALSE); g_return_val_if_fail (GST_BUFFER_DATA (buffer) != NULL, FALSE); return GST_RTP_HEADER_PADDING (buffer); } void gst_rtp_buffer_set_padding (GstBuffer * buffer, gboolean padding) { g_return_if_fail (GST_IS_BUFFER (buffer)); g_return_if_fail (GST_BUFFER_DATA (buffer) != NULL); GST_RTP_HEADER_PADDING (buffer) = padding; } void gst_rtp_buffer_pad_to (GstBuffer * buffer, guint len) { g_return_if_fail (GST_IS_BUFFER (buffer)); g_return_if_fail (GST_BUFFER_DATA (buffer) != NULL); if (len > 0) GST_RTP_HEADER_PADDING (buffer) = TRUE; else GST_RTP_HEADER_PADDING (buffer) = FALSE; } gboolean gst_rtp_buffer_get_extension (GstBuffer * buffer) { g_return_val_if_fail (GST_IS_BUFFER (buffer), FALSE); g_return_val_if_fail (GST_BUFFER_DATA (buffer) != NULL, FALSE); return GST_RTP_HEADER_EXTENSION (buffer); } void gst_rtp_buffer_set_extension (GstBuffer * buffer, gboolean extension) { g_return_if_fail (GST_IS_BUFFER (buffer)); g_return_if_fail (GST_BUFFER_DATA (buffer) != NULL); GST_RTP_HEADER_EXTENSION (buffer) = extension; } guint32 gst_rtp_buffer_get_ssrc (GstBuffer * buffer) { g_return_val_if_fail (GST_IS_BUFFER (buffer), 0); g_return_val_if_fail (GST_BUFFER_DATA (buffer) != NULL, 0); return g_ntohl (GST_RTP_HEADER_SSRC (buffer)); } void gst_rtp_buffer_set_ssrc (GstBuffer * buffer, guint32 ssrc) { g_return_if_fail (GST_IS_BUFFER (buffer)); g_return_if_fail (GST_BUFFER_DATA (buffer) != NULL); GST_RTP_HEADER_SSRC (buffer) = g_htonl (ssrc); } guint8 gst_rtp_buffer_get_csrc_count (GstBuffer * buffer) { g_return_val_if_fail (GST_IS_BUFFER (buffer), 0); g_return_val_if_fail (GST_BUFFER_DATA (buffer) != NULL, 0); return GST_RTP_HEADER_CSRC_COUNT (buffer); } guint32 gst_rtp_buffer_get_csrc (GstBuffer * buffer, guint8 idx) { g_return_val_if_fail (GST_IS_BUFFER (buffer), 0); g_return_val_if_fail (GST_BUFFER_DATA (buffer) != NULL, 0); g_return_val_if_fail (GST_RTP_HEADER_CSRC_COUNT (buffer) < idx, 0); return g_ntohl (GST_RTP_HEADER_CSRC (buffer, idx)); } void gst_rtp_buffer_set_csrc (GstBuffer * buffer, guint8 idx, guint32 csrc) { g_return_if_fail (GST_IS_BUFFER (buffer)); g_return_if_fail (GST_BUFFER_DATA (buffer) != NULL); g_return_if_fail (GST_RTP_HEADER_CSRC_COUNT (buffer) < idx); GST_RTP_HEADER_CSRC (buffer, idx) = g_htonl (csrc); } gboolean gst_rtp_buffer_get_marker (GstBuffer * buffer) { g_return_val_if_fail (GST_IS_BUFFER (buffer), FALSE); g_return_val_if_fail (GST_BUFFER_DATA (buffer) != NULL, FALSE); return GST_RTP_HEADER_MARKER (buffer); } void gst_rtp_buffer_set_marker (GstBuffer * buffer, gboolean marker) { g_return_if_fail (GST_IS_BUFFER (buffer)); g_return_if_fail (GST_BUFFER_DATA (buffer) != NULL); GST_RTP_HEADER_MARKER (buffer) = marker; } guint8 gst_rtp_buffer_get_payload_type (GstBuffer * buffer) { g_return_val_if_fail (GST_IS_BUFFER (buffer), 0); g_return_val_if_fail (GST_BUFFER_DATA (buffer) != NULL, 0); return GST_RTP_HEADER_PAYLOAD_TYPE (buffer); } void gst_rtp_buffer_set_payload_type (GstBuffer * buffer, guint8 payload_type) { g_return_if_fail (GST_IS_BUFFER (buffer)); g_return_if_fail (GST_BUFFER_DATA (buffer) != NULL); g_return_if_fail (payload_type < 0x80); GST_RTP_HEADER_PAYLOAD_TYPE (buffer) = payload_type; } guint16 gst_rtp_buffer_get_seq (GstBuffer * buffer) { g_return_val_if_fail (GST_IS_BUFFER (buffer), 0); g_return_val_if_fail (GST_BUFFER_DATA (buffer) != NULL, 0); return g_ntohs (GST_RTP_HEADER_SEQ (buffer)); } void gst_rtp_buffer_set_seq (GstBuffer * buffer, guint16 seq) { g_return_if_fail (GST_IS_BUFFER (buffer)); g_return_if_fail (GST_BUFFER_DATA (buffer) != NULL); GST_RTP_HEADER_SEQ (buffer) = g_htons (seq); } guint32 gst_rtp_buffer_get_timestamp (GstBuffer * buffer) { g_return_val_if_fail (GST_IS_BUFFER (buffer), 0); g_return_val_if_fail (GST_BUFFER_DATA (buffer) != NULL, 0); return g_ntohl (GST_RTP_HEADER_TIMESTAMP (buffer)); } void gst_rtp_buffer_set_timestamp (GstBuffer * buffer, guint32 timestamp) { g_return_if_fail (GST_IS_BUFFER (buffer)); g_return_if_fail (GST_BUFFER_DATA (buffer) != NULL); GST_RTP_HEADER_TIMESTAMP (buffer) = g_htonl (timestamp); } GstBuffer * gst_rtp_buffer_get_payload_buffer (GstBuffer * buffer) { guint len; g_return_val_if_fail (GST_IS_BUFFER (buffer), 0); g_return_val_if_fail (GST_BUFFER_DATA (buffer) != NULL, 0); len = gst_rtp_buffer_get_payload_len (buffer); return gst_buffer_create_sub (buffer, GST_RTP_HEADER_LEN + GST_RTP_HEADER_CSRC_SIZE (buffer), len); } guint gst_rtp_buffer_get_payload_len (GstBuffer * buffer) { guint len; g_return_val_if_fail (GST_IS_BUFFER (buffer), 0); g_return_val_if_fail (GST_BUFFER_DATA (buffer) != NULL, 0); len = GST_BUFFER_SIZE (buffer) - GST_RTP_HEADER_LEN - GST_RTP_HEADER_CSRC_SIZE (buffer); if (GST_RTP_HEADER_PADDING (buffer)) len -= ((guint8 *) GST_BUFFER_DATA (buffer))[GST_BUFFER_SIZE (buffer) - 1]; return len; } gpointer gst_rtp_buffer_get_payload (GstBuffer * buffer) { g_return_val_if_fail (GST_IS_BUFFER (buffer), NULL); g_return_val_if_fail (GST_BUFFER_DATA (buffer) != NULL, NULL); return GST_BUFFER_DATA (buffer) + GST_RTP_HEADER_LEN + GST_RTP_HEADER_CSRC_SIZE (buffer); }