rtp: add bufferlist support
authorWim Taymans <wim.taymans@collabora.co.uk>
Thu, 18 Jun 2009 15:46:01 +0000 (17:46 +0200)
committerWim Taymans <wim.taymans@collabora.co.uk>
Thu, 18 Jun 2009 16:51:04 +0000 (18:51 +0200)
gst-libs/gst/rtp/gstrtpbuffer.c
gst-libs/gst/rtp/gstrtpbuffer.h
tests/check/libs/rtp.c

index 1c4fdd9..87b36d4 100644 (file)
@@ -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
@@ -299,6 +316,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;
   guint header_len;
@@ -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))
@@ -371,31 +514,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
  * @len: the new packet length
@@ -678,6 +796,24 @@ gst_rtp_buffer_get_ssrc (GstBuffer * 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
  * @ssrc: the new SSRC
@@ -691,6 +827,19 @@ gst_rtp_buffer_set_ssrc (GstBuffer * buffer, guint32 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
  *
@@ -787,6 +936,24 @@ gst_rtp_buffer_get_payload_type (GstBuffer * 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
  * @payload_type: the new type
@@ -802,6 +969,21 @@ gst_rtp_buffer_set_payload_type (GstBuffer * buffer, guint8 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
  *
@@ -829,6 +1011,22 @@ gst_rtp_buffer_set_seq (GstBuffer * buffer, guint16 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
  *
@@ -843,6 +1041,24 @@ gst_rtp_buffer_get_timestamp (GstBuffer * 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
  * @timestamp: the new timestamp
@@ -856,6 +1072,19 @@ gst_rtp_buffer_set_timestamp (GstBuffer * buffer, guint32 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, &timestamp, TIMESTAMP);
+}
+
+/**
  * gst_rtp_buffer_get_payload_subbuffer:
  * @buffer: the buffer
  * @offset: the offset in the payload
@@ -940,6 +1169,40 @@ gst_rtp_buffer_get_payload_len (GstBuffer * buffer)
 }
 
 /**
+ * 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;
+  }
+}
index a9106e3..c9680b0 100644 (file)
@@ -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 */
index b523409..e19c96c 100644 (file)
@@ -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;
 }