From 66c388a0e00ad639d5c3f76a6ac033a5c432848c Mon Sep 17 00:00:00 2001 From: Wim Taymans Date: Thu, 18 Jun 2009 17:46:01 +0200 Subject: [PATCH] rtp: add bufferlist support --- gst-libs/gst/rtp/gstrtpbuffer.c | 409 +++++++++++++++++++++++++++++--- gst-libs/gst/rtp/gstrtpbuffer.h | 10 + tests/check/libs/rtp.c | 54 +++++ 3 files changed, 445 insertions(+), 28 deletions(-) diff --git a/gst-libs/gst/rtp/gstrtpbuffer.c b/gst-libs/gst/rtp/gstrtpbuffer.c index 1c4fdd9c07..87b36d4dc4 100644 --- a/gst-libs/gst/rtp/gstrtpbuffer.c +++ b/gst-libs/gst/rtp/gstrtpbuffer.c @@ -83,6 +83,23 @@ typedef struct _GstRTPHeader ((i) * sizeof(guint32)) #define GST_RTP_HEADER_CSRC_SIZE(data) (GST_RTP_HEADER_CSRC_COUNT(data) * sizeof (guint32)) +typedef enum +{ + PAYLOAD_TYPE, + SEQ, + TIMESTAMP, + SSRC, + NO_MORE +} rtp_header_data_type; + +static gboolean validate_data (guint8 * data, guint len, guint8 * payload, + guint payload_len); +static guint8 *gst_rtp_buffer_list_get_data (GstBufferList * list); +static void gst_rtp_buffer_list_set_rtp_headers (GstBufferList * list, + gpointer data, rtp_header_data_type type); +static void gst_rtp_buffer_list_set_data (guint8 * rtp_header, gpointer data, + rtp_header_data_type type); + /** * gst_rtp_buffer_allocate_data: * @buffer: a #GstBuffer @@ -298,6 +315,128 @@ gst_rtp_buffer_calc_payload_len (guint packet_len, guint8 pad_len, */ 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. + */ +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 */ + 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; + +invalid_list: + gst_buffer_list_iterator_free (it); + g_return_val_if_reached (FALSE); +} + +/** + * 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; @@ -341,10 +480,14 @@ gst_rtp_buffer_validate_data (guint8 * data, guint len) } /* check for padding */ - if (data[0] & 0x20) - padding = data[len - 1]; - else + 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)) @@ -370,31 +513,6 @@ wrong_padding: } } -/** - * 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 gst_rtp_buffer_validate_data (data, len); -} - /** * gst_rtp_buffer_set_packet_len: * @buffer: the buffer @@ -677,6 +795,24 @@ 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 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. + */ +guint32 +gst_rtp_buffer_list_get_ssrc (GstBufferList * list) +{ + guint8 *data; + data = gst_rtp_buffer_list_get_data (list); + g_return_val_if_fail (data != NULL, 0); + return g_ntohl (GST_RTP_HEADER_SSRC (data)); +} + /** * gst_rtp_buffer_set_ssrc: * @buffer: the buffer @@ -690,6 +826,19 @@ gst_rtp_buffer_set_ssrc (GstBuffer * buffer, guint32 ssrc) GST_RTP_HEADER_SSRC (GST_BUFFER_DATA (buffer)) = g_htonl (ssrc); } +/** + * 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. + */ +void +gst_rtp_buffer_list_set_ssrc (GstBufferList * list, guint32 ssrc) +{ + gst_rtp_buffer_list_set_rtp_headers (list, &ssrc, SSRC); +} + /** * gst_rtp_buffer_get_csrc_count: * @buffer: the buffer @@ -786,6 +935,24 @@ 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 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. + */ +guint8 +gst_rtp_buffer_list_get_payload_type (GstBufferList * list) +{ + guint8 *data; + data = gst_rtp_buffer_list_get_data (list); + g_return_val_if_fail (data != NULL, 0); + return GST_RTP_HEADER_PAYLOAD_TYPE (data); +} + /** * gst_rtp_buffer_set_payload_type: * @buffer: the buffer @@ -801,6 +968,21 @@ gst_rtp_buffer_set_payload_type (GstBuffer * buffer, guint8 payload_type) GST_RTP_HEADER_PAYLOAD_TYPE (GST_BUFFER_DATA (buffer)) = payload_type; } +/** + * 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. + */ +void +gst_rtp_buffer_list_set_payload_type (GstBufferList * list, guint8 payload_type) +{ + g_return_if_fail (payload_type < 0x80); + + gst_rtp_buffer_list_set_rtp_headers (list, &payload_type, PAYLOAD_TYPE); +} + /** * gst_rtp_buffer_get_seq: * @buffer: the buffer @@ -828,6 +1010,22 @@ gst_rtp_buffer_set_seq (GstBuffer * buffer, guint16 seq) GST_RTP_HEADER_SEQ (GST_BUFFER_DATA (buffer)) = g_htons (seq); } +/** + * 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. + */ +guint16 +gst_rtp_buffer_list_set_seq (GstBufferList * list, guint16 seq) +{ + gst_rtp_buffer_list_set_rtp_headers (list, &seq, SEQ); + return seq; +} + /** * gst_rtp_buffer_get_timestamp: * @buffer: the buffer @@ -842,6 +1040,24 @@ 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 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. + */ +guint32 +gst_rtp_buffer_list_get_timestamp (GstBufferList * list) +{ + guint8 *data; + data = gst_rtp_buffer_list_get_data (list); + g_return_val_if_fail (data != NULL, 0); + return g_ntohl (GST_RTP_HEADER_TIMESTAMP (data)); +} + /** * gst_rtp_buffer_set_timestamp: * @buffer: the buffer @@ -855,6 +1071,19 @@ gst_rtp_buffer_set_timestamp (GstBuffer * buffer, guint32 timestamp) GST_RTP_HEADER_TIMESTAMP (GST_BUFFER_DATA (buffer)) = g_htonl (timestamp); } +/** + * 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. + */ +void +gst_rtp_buffer_list_set_timestamp (GstBufferList * list, guint32 timestamp) +{ + gst_rtp_buffer_list_set_rtp_headers (list, ×tamp, TIMESTAMP); +} + /** * gst_rtp_buffer_get_payload_subbuffer: * @buffer: the buffer @@ -939,6 +1168,40 @@ gst_rtp_buffer_get_payload_len (GstBuffer * buffer) return len; } +/** + * gst_rtp_buffer_list_get_payload_len: + * @buffer: the buffer + * + * Get the length of the payload of the RTP packet in @list. + * + * Returns: The length of the payload in @list. + */ +guint +gst_rtp_buffer_list_get_payload_len (GstBufferList * list) +{ + guint len = 0; + GstBufferListIterator *it; + it = gst_buffer_list_iterate (list); + + 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 @@ -1048,3 +1311,93 @@ gst_rtp_buffer_ext_timestamp (guint64 * exttimestamp, guint32 timestamp) return result; } + +/** + * gst_rtp_buffer_list_get_data: + * @list: a buffer list + * + * Returns ponter to the RTP header of the first packet within the list + * + * Returns: pointer to the first RTP header + */ +static guint8 * +gst_rtp_buffer_list_get_data (GstBufferList * list) +{ + GstBufferListIterator *it; + GstBuffer *rtpbuf; + + it = gst_buffer_list_iterate (list); + if (!gst_buffer_list_iterator_next_group (it)) + goto invalid_list; + rtpbuf = gst_buffer_list_iterator_next (it); + if (!rtpbuf) + goto invalid_list; + + gst_buffer_list_iterator_free (it); + return GST_BUFFER_DATA (rtpbuf); + +invalid_list: + gst_buffer_list_iterator_free (it); + g_return_val_if_reached (FALSE); +} + +/** + * gst_rtp_buffer_list_set_rtp_headers: + * @list: a buffer list + * @data: data to be set + * @type: which field in the header to be set + * + * Sets the field specified by @type to @data. + * This function updates all RTP headers within @list. + */ +static void +gst_rtp_buffer_list_set_rtp_headers (GstBufferList * list, + gpointer data, rtp_header_data_type type) +{ + GstBufferListIterator *it; + it = gst_buffer_list_iterate (list); + + while (gst_buffer_list_iterator_next_group (it)) { + GstBuffer *rtpbuf; + guint8 *rtp_header; + rtpbuf = gst_buffer_list_iterator_next (it); + rtp_header = GST_BUFFER_DATA (rtpbuf); + gst_rtp_buffer_list_set_data (rtp_header, data, type); + } + + gst_buffer_list_iterator_free (it); +} + +/** + * gst_rtp_buffer_list_set_data: + * @rtp_header: rtp header to be updated + * @data: data to be set + * @type: which field in the header to be set + * + * Sets the field specified by @type to @data. + * When setting SEQ number, this function will also increase + * @data by one. + */ +static void +gst_rtp_buffer_list_set_data (guint8 * rtp_header, + gpointer data, rtp_header_data_type type) +{ + switch (type) { + case PAYLOAD_TYPE: + GST_RTP_HEADER_PAYLOAD_TYPE (rtp_header) = *(guint8 *) data; + break; + case SEQ: + GST_RTP_HEADER_SEQ (rtp_header) = g_htons (*(guint16 *) data); + (*(guint16 *) data)++; + break; + case SSRC: + GST_RTP_HEADER_SSRC (rtp_header) = g_htonl (*(guint32 *) data); + break; + case TIMESTAMP: + GST_RTP_HEADER_TIMESTAMP (rtp_header) = g_htonl (*(guint32 *) data); + break; + default: + g_warning ("Unknown data type"); + break; + } +} diff --git a/gst-libs/gst/rtp/gstrtpbuffer.h b/gst-libs/gst/rtp/gstrtpbuffer.h index a9106e3655..c9680b039c 100644 --- a/gst-libs/gst/rtp/gstrtpbuffer.h +++ b/gst-libs/gst/rtp/gstrtpbuffer.h @@ -51,6 +51,7 @@ guint gst_rtp_buffer_calc_payload_len (guint packet_len, guint8 p gboolean gst_rtp_buffer_validate_data (guint8 *data, guint len); gboolean gst_rtp_buffer_validate (GstBuffer *buffer); +gboolean gst_rtp_buffer_list_validate (GstBufferList *list); void gst_rtp_buffer_set_packet_len (GstBuffer *buffer, guint len); guint gst_rtp_buffer_get_packet_len (GstBuffer *buffer); @@ -71,7 +72,9 @@ gboolean gst_rtp_buffer_get_extension_data (GstBuffer *buffer, guint16 gboolean gst_rtp_buffer_set_extension_data (GstBuffer *buffer, guint16 bits, guint16 length); guint32 gst_rtp_buffer_get_ssrc (GstBuffer *buffer); +guint32 gst_rtp_buffer_list_get_ssrc (GstBufferList *list); void gst_rtp_buffer_set_ssrc (GstBuffer *buffer, guint32 ssrc); +void gst_rtp_buffer_list_set_ssrc (GstBufferList *list, guint32 ssrc); guint8 gst_rtp_buffer_get_csrc_count (GstBuffer *buffer); guint32 gst_rtp_buffer_get_csrc (GstBuffer *buffer, guint8 idx); @@ -81,18 +84,25 @@ gboolean gst_rtp_buffer_get_marker (GstBuffer *buffer); void gst_rtp_buffer_set_marker (GstBuffer *buffer, gboolean marker); guint8 gst_rtp_buffer_get_payload_type (GstBuffer *buffer); +guint8 gst_rtp_buffer_list_get_payload_type (GstBufferList *list); void gst_rtp_buffer_set_payload_type (GstBuffer *buffer, guint8 payload_type); +void gst_rtp_buffer_list_set_payload_type (GstBufferList *list, guint8 payload_type); guint16 gst_rtp_buffer_get_seq (GstBuffer *buffer); +guint16 gst_rtp_buffer_list_get_seq (GstBufferList *list); void gst_rtp_buffer_set_seq (GstBuffer *buffer, guint16 seq); +guint16 gst_rtp_buffer_list_set_seq (GstBufferList *list, guint16 seq); guint32 gst_rtp_buffer_get_timestamp (GstBuffer *buffer); +guint32 gst_rtp_buffer_list_get_timestamp (GstBufferList *list); void gst_rtp_buffer_set_timestamp (GstBuffer *buffer, guint32 timestamp); +void gst_rtp_buffer_list_set_timestamp (GstBufferList *list, guint32 timestamp); GstBuffer* gst_rtp_buffer_get_payload_buffer (GstBuffer *buffer); GstBuffer* gst_rtp_buffer_get_payload_subbuffer (GstBuffer *buffer, guint offset, guint len); guint gst_rtp_buffer_get_payload_len (GstBuffer *buffer); +guint gst_rtp_buffer_list_get_payload_len (GstBufferList *list); gpointer gst_rtp_buffer_get_payload (GstBuffer *buffer); /* some helpers */ diff --git a/tests/check/libs/rtp.c b/tests/check/libs/rtp.c index b52340996a..e19c96cde4 100644 --- a/tests/check/libs/rtp.c +++ b/tests/check/libs/rtp.c @@ -152,6 +152,58 @@ GST_START_TEST (test_rtp_buffer_validate_corrupt) GST_END_TEST; +GST_START_TEST (test_rtp_buffer_list) +{ + GstBuffer *rtp_header; + GstBuffer *rtp_payload; + GstBufferList *list = NULL; + GstBufferListIterator *it; + guint i; + + list = gst_buffer_list_new (); + it = gst_buffer_list_iterate (list); + + /* Creating a list of two RTP packages */ + + /* Create first group to hold the rtp header and the payload */ + gst_buffer_list_iterator_add_group (it); + rtp_header = gst_rtp_buffer_new_allocate (0, 0, 0); + gst_buffer_list_iterator_add (it, rtp_header); + rtp_payload = gst_buffer_new_and_alloc (42); + gst_buffer_list_iterator_add (it, rtp_payload); + + /* Create second group to hold an rtp header and a payload */ + gst_buffer_list_iterator_add_group (it); + rtp_header = gst_rtp_buffer_new_allocate (0, 0, 0); + gst_buffer_list_iterator_add (it, rtp_header); + rtp_payload = gst_buffer_new_and_alloc (42); + gst_buffer_list_iterator_add (it, rtp_payload); + + gst_buffer_list_iterator_free (it); + + /* Test SEQ number */ + i = gst_rtp_buffer_list_set_seq (list, 1024); + fail_if (1026 != i); + fail_if (!gst_rtp_buffer_list_validate (list)); + + /* Timestamp */ + gst_rtp_buffer_list_set_timestamp (list, 432191); + fail_unless_equals_int (gst_rtp_buffer_list_get_timestamp (list), 432191); + + /* SSRC */ + gst_rtp_buffer_list_set_ssrc (list, 0xf04043C2); + fail_unless_equals_int (gst_rtp_buffer_list_get_ssrc (list), + (gint) 0xf04043c2); + + /* Payload type */ + gst_rtp_buffer_list_set_payload_type (list, 127); + fail_unless_equals_int (gst_rtp_buffer_list_get_payload_type (list), 127); + + gst_buffer_list_unref (list); +} + +GST_END_TEST; + GST_START_TEST (test_rtp_buffer_set_extension_data) { GstBuffer *buf; @@ -394,6 +446,8 @@ rtp_suite (void) tcase_add_test (tc_chain, test_rtcp_buffer); + tcase_add_test (tc_chain, test_rtp_buffer_list); + return s; }