Commit 11a494d5 authored by Wim Taymans's avatar Wim Taymans
Browse files

rtp: Add support for multiple memory blocks in RTP

Add support RTP buffers with multiple memory blocks. We allow one block for the
header, one for the extension data, N for data and one memory block for the
padding.
Remove the validate function, we validate now when we map because we need to
parse things in order to map multiple memory blocks.
parent 5d82addc
......@@ -1190,9 +1190,6 @@ gst_rtp_buffer_calc_header_len
gst_rtp_buffer_calc_packet_len
gst_rtp_buffer_calc_payload_len
gst_rtp_buffer_validate
gst_rtp_buffer_validate_data
gst_rtp_buffer_set_packet_len
gst_rtp_buffer_get_packet_len
......
......@@ -21,9 +21,7 @@ The RTP libraries
duplicate GstBuffer with the same data as the previous one). The
function will create a new RTP buffer with the given data as the whole RTP
packet. Alternatively, gst_rtp_buffer_new_copy_data() can be used if the user
wishes to make a copy of the data before using it in the new RTP buffer. An
important function is gst_rtp_buffer_validate() that is used to verify that
the buffer a well formed RTP buffer.
wishes to make a copy of the data before using it in the new RTP buffer.
It is now possible to use all the gst_rtp_buffer_get_*() or
gst_rtp_buffer_set_*() functions to read or write the different parts of the
......
......@@ -257,9 +257,7 @@ gst_rtp_base_depayload_chain (GstPad * pad, GstObject * parent, GstBuffer * in)
if (G_UNLIKELY (!priv->negotiated))
goto not_negotiated;
/* we must validate, it's possible that this element is plugged right after a
* network receiver and we don't want to operate on invalid data */
if (G_UNLIKELY (!gst_rtp_buffer_validate (in)))
if (G_UNLIKELY (!gst_rtp_buffer_map (in, GST_MAP_READ, &rtp)))
goto invalid_buffer;
if (!priv->discont)
......@@ -275,7 +273,6 @@ gst_rtp_base_depayload_chain (GstPad * pad, GstObject * parent, GstBuffer * in)
priv->dts = dts;
priv->duration = GST_BUFFER_DURATION (in);
gst_rtp_buffer_map (in, GST_MAP_READ, &rtp);
seqnum = gst_rtp_buffer_get_seq (&rtp);
rtptime = gst_rtp_buffer_get_timestamp (&rtp);
gst_rtp_buffer_unmap (&rtp);
......
......@@ -747,7 +747,9 @@ set_headers (GstBuffer ** buffer, guint idx, gpointer user_data)
HeaderData *data = user_data;
GstRTPBuffer rtp = { NULL, };
gst_rtp_buffer_map (*buffer, GST_MAP_WRITE, &rtp);
if (!gst_rtp_buffer_map (*buffer, GST_MAP_WRITE, &rtp))
goto map_failed;
gst_rtp_buffer_set_ssrc (&rtp, data->ssrc);
gst_rtp_buffer_set_payload_type (&rtp, data->pt);
gst_rtp_buffer_set_seq (&rtp, data->seqnum);
......@@ -758,6 +760,12 @@ set_headers (GstBuffer ** buffer, guint idx, gpointer user_data)
data->seqnum++;
return TRUE;
/* ERRORS */
map_failed:
{
GST_ERROR ("failed to map buffer %p", *buffer);
return FALSE;
}
}
/* Updates the SSRC, payload type, seqnum and timestamp of the RTP buffer
......
......@@ -283,29 +283,41 @@ gst_rtp_buffer_calc_payload_len (guint packet_len, guint8 pad_len,
- 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
/**
* gst_rtp_buffer_map:
* @buffer: a #GstBuffer
* @flags: #GstMapFlags
* @rtp: (out): a #GstRTPBuffer
*
* Checks if @data is a valid RTP packet.
* Map the contents of @buffer into @rtp.
*
* Returns: TRUE if @data is a valid RTP packet
* Returns: %TRUE if @buffer could be mapped.
*/
static gboolean
validate_data (guint8 * data, guint len, guint8 * payload, guint payload_len)
gboolean
gst_rtp_buffer_map (GstBuffer * buffer, GstMapFlags flags, GstRTPBuffer * rtp)
{
guint8 padding;
guint8 csrc_count;
guint header_len;
guint8 version;
guint8 *data;
guint size;
gsize bufsize, skip;
guint idx, length;
g_return_val_if_fail (data != NULL, FALSE);
g_return_val_if_fail (GST_IS_BUFFER (buffer), FALSE);
g_return_val_if_fail (rtp != NULL, FALSE);
g_return_val_if_fail (rtp->buffer == NULL, FALSE);
/* map first memory, this should be the header */
if (!gst_buffer_map_range (buffer, 0, 1, &rtp->map[0], flags))
goto map_failed;
data = rtp->data[0] = rtp->map[0].data;
size = rtp->map[0].size;
header_len = GST_RTP_HEADER_LEN;
if (G_UNLIKELY (len < header_len))
if (G_UNLIKELY (size < header_len))
goto wrong_length;
/* check version */
......@@ -317,47 +329,79 @@ validate_data (guint8 * data, guint len, guint8 * payload, guint payload_len)
csrc_count = (data[0] & 0x0f);
header_len += csrc_count * sizeof (guint32);
rtp->size[0] = header_len;
bufsize = gst_buffer_get_size (buffer);
/* calc extension length when present. */
if (data[0] & 0x10) {
guint8 *extpos;
guint8 *extdata;
guint16 extlen;
/* this points to the extension 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))
/* find memory for the extension bits */
if (!gst_buffer_find_memory (buffer, header_len, 4, &idx, &length, &skip))
goto wrong_length;
if (!gst_buffer_map_range (buffer, idx, length, &rtp->map[1], flags))
goto map_failed;
extdata = rtp->data[1] = rtp->map[1].data + skip;
/* skip id */
extpos += 2;
extdata += 2;
/* read length as the number of 32 bits words */
extlen = GST_READ_UINT16_BE (extpos);
extlen = GST_READ_UINT16_BE (extdata);
rtp->size[1] = extlen * sizeof (guint32);
header_len += extlen * sizeof (guint32);
header_len += rtp->size[1];
} else {
rtp->data[1] = NULL;
rtp->size[1] = 0;
}
/* check for padding */
if (data[0] & 0x20) {
if (payload)
padding = payload[payload_len - 1];
else
padding = data[len - 1];
/* find memory for the padding bits */
if (!gst_buffer_find_memory (buffer, bufsize - 1, 1, &idx, &length, &skip))
goto wrong_length;
if (!gst_buffer_map_range (buffer, idx, length, &rtp->map[3], flags))
goto map_failed;
padding = rtp->map[3].data[skip];
if (skip + 1 < padding)
goto wrong_length;
rtp->data[3] = rtp->map[3].data + skip + 1 - padding;
rtp->size[3] = padding;
} else {
rtp->data[3] = NULL;
rtp->size[3] = 0;
padding = 0;
}
/* check if padding and header not bigger than packet length */
if (G_UNLIKELY (len < padding + header_len))
if (G_UNLIKELY (bufsize < padding + header_len))
goto wrong_padding;
rtp->buffer = buffer;
/* we have not yet mapped the payload */
rtp->data[2] = NULL;
rtp->size[2] = 0;
rtp->state = 0;
rtp->n_map = 1;
return TRUE;
/* ERRORS */
map_failed:
{
GST_ERROR ("failed to map memory");
return FALSE;
}
wrong_length:
{
GST_DEBUG ("len < header_len check failed (%d < %d)", len, header_len);
GST_DEBUG ("length check failed");
goto dump_packet;
}
wrong_version:
......@@ -367,89 +411,37 @@ wrong_version:
}
wrong_padding:
{
GST_DEBUG ("padding check failed (%d - %d < %d)", len, header_len, padding);
GST_DEBUG ("padding check failed (%d - %d < %d)", bufsize, header_len,
padding);
goto dump_packet;
}
dump_packet:
{
GST_MEMDUMP ("buffer", data, len);
GST_MEMDUMP ("buffer", data, size);
return FALSE;
}
}
/**
* gst_rtp_buffer_validate_data:
* @data: (array length=len) (element-type guint8): 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, gsize 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.
* gst_rtp_buffer_unmap:
* @rtp: a #GstRTPBuffer
*
* Returns: TRUE if @buffer is a valid RTP packet.
* Unmap @rtp previously mapped with gst_rtp_buffer_map().
*/
gboolean
gst_rtp_buffer_validate (GstBuffer * buffer)
{
gboolean res;
GstMapInfo map;
g_return_val_if_fail (GST_IS_BUFFER (buffer), FALSE);
gst_buffer_map (buffer, &map, GST_MAP_READ);
res = validate_data (map.data, map.size, NULL, 0);
gst_buffer_unmap (buffer, &map);
return res;
}
gboolean
gst_rtp_buffer_map (GstBuffer * buffer, GstMapFlags flags, GstRTPBuffer * rtp)
{
g_return_val_if_fail (GST_IS_BUFFER (buffer), FALSE);
g_return_val_if_fail (rtp != NULL, FALSE);
g_return_val_if_fail (rtp->buffer == NULL, FALSE);
if (!gst_buffer_map (buffer, &rtp->map[0], flags))
return FALSE;
rtp->buffer = buffer;
rtp->state = 0;
rtp->n_map = 1;
return TRUE;
}
gboolean
void
gst_rtp_buffer_unmap (GstRTPBuffer * rtp)
{
g_return_val_if_fail (rtp != NULL, FALSE);
g_return_val_if_fail (rtp->buffer != NULL, FALSE);
gint i;
g_return_if_fail (rtp != NULL);
g_return_if_fail (rtp->buffer != NULL);
gst_buffer_unmap (rtp->buffer, &rtp->map[0]);
for (i = 0; i < 4; i++) {
if (rtp->data[i])
gst_buffer_unmap (rtp->buffer, &rtp->map[i]);
}
rtp->buffer = NULL;
rtp->n_map = 0;
return TRUE;
}
......@@ -466,7 +458,9 @@ gst_rtp_buffer_set_packet_len (GstRTPBuffer * rtp, guint len)
{
guint8 *data;
data = rtp->map[0].data;
data = rtp->data[0];
/* FIXME */
if (rtp->map[0].maxsize <= len) {
/* FIXME, realloc bigger space */
......@@ -506,16 +500,7 @@ gst_rtp_buffer_get_packet_len (GstRTPBuffer * rtp)
guint
gst_rtp_buffer_get_header_len (GstRTPBuffer * rtp)
{
guint len;
guint8 *data;
data = rtp->map[0].data;
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;
return rtp->size[0] + rtp->size[1];
}
/**
......@@ -529,7 +514,7 @@ gst_rtp_buffer_get_header_len (GstRTPBuffer * rtp)
guint8
gst_rtp_buffer_get_version (GstRTPBuffer * rtp)
{
return GST_RTP_HEADER_VERSION (rtp->map[0].data);
return GST_RTP_HEADER_VERSION (rtp->data[0]);
}
/**
......@@ -544,7 +529,7 @@ gst_rtp_buffer_set_version (GstRTPBuffer * rtp, guint8 version)
{
g_return_if_fail (version < 0x04);
GST_RTP_HEADER_VERSION (rtp->map[0].data) = version;
GST_RTP_HEADER_VERSION (rtp->data[0]) = version;
}
/**
......@@ -558,7 +543,7 @@ gst_rtp_buffer_set_version (GstRTPBuffer * rtp, guint8 version)
gboolean
gst_rtp_buffer_get_padding (GstRTPBuffer * rtp)
{
return GST_RTP_HEADER_PADDING (rtp->map[0].data);
return GST_RTP_HEADER_PADDING (rtp->data[0]);
}
/**
......@@ -571,7 +556,7 @@ gst_rtp_buffer_get_padding (GstRTPBuffer * rtp)
void
gst_rtp_buffer_set_padding (GstRTPBuffer * rtp, gboolean padding)
{
GST_RTP_HEADER_PADDING (rtp->map[0].data) = padding;
GST_RTP_HEADER_PADDING (rtp->data[0]) = padding;
}
/**
......@@ -589,7 +574,7 @@ gst_rtp_buffer_pad_to (GstRTPBuffer * rtp, guint len)
{
guint8 *data;
data = rtp->map[0].data;
data = rtp->data[0];
if (len > 0)
GST_RTP_HEADER_PADDING (data) = TRUE;
......@@ -610,7 +595,7 @@ gst_rtp_buffer_pad_to (GstRTPBuffer * rtp, guint len)
gboolean
gst_rtp_buffer_get_extension (GstRTPBuffer * rtp)
{
return GST_RTP_HEADER_EXTENSION (rtp->map[0].data);
return GST_RTP_HEADER_EXTENSION (rtp->data[0]);
}
/**
......@@ -623,7 +608,7 @@ gst_rtp_buffer_get_extension (GstRTPBuffer * rtp)
void
gst_rtp_buffer_set_extension (GstRTPBuffer * rtp, gboolean extension)
{
GST_RTP_HEADER_EXTENSION (rtp->map[0].data) = extension;
GST_RTP_HEADER_EXTENSION (rtp->data[0]) = extension;
}
/**
......@@ -646,17 +631,12 @@ gboolean
gst_rtp_buffer_get_extension_data (GstRTPBuffer * rtp, guint16 * bits,
gpointer * data, guint * wordlen)
{
guint len;
guint8 *pdata;
pdata = rtp->map[0].data;
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;
pdata = rtp->data[1];
if (!pdata)
return FALSE;
if (bits)
*bits = GST_READ_UINT16_BE (pdata);
......@@ -689,19 +669,18 @@ gst_rtp_buffer_set_extension_data (GstRTPBuffer * rtp, guint16 bits,
guint32 min_size = 0;
guint8 *data;
data = rtp->map[0].data;
/* FIXME, we should allocate and map the extension data */
data = rtp->data[0];
/* 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 > rtp->map[0].size))
min_size = 4 + length * sizeof (guint32);
if (G_UNLIKELY (min_size > rtp->size[1]))
goto too_small;
/* now we can set the extension bit */
GST_RTP_HEADER_EXTENSION (rtp->map[0].data) = TRUE;
GST_RTP_HEADER_EXTENSION (data) = TRUE;
data += GST_RTP_HEADER_LEN + GST_RTP_HEADER_CSRC_SIZE (data);
data = rtp->data[1];
GST_WRITE_UINT16_BE (data, bits);
GST_WRITE_UINT16_BE (data + 2, length);
......@@ -712,7 +691,7 @@ too_small:
{
g_warning
("rtp buffer too small: need more than %d bytes but only have %"
G_GSIZE_FORMAT " bytes", min_size, rtp->map[0].size);
G_GSIZE_FORMAT " bytes", min_size, rtp->size[1]);
return FALSE;
}
}
......@@ -728,7 +707,7 @@ too_small:
guint32
gst_rtp_buffer_get_ssrc (GstRTPBuffer * rtp)
{
return g_ntohl (GST_RTP_HEADER_SSRC (rtp->map[0].data));
return g_ntohl (GST_RTP_HEADER_SSRC (rtp->data[0]));
}
/**
......@@ -741,7 +720,7 @@ gst_rtp_buffer_get_ssrc (GstRTPBuffer * rtp)
void
gst_rtp_buffer_set_ssrc (GstRTPBuffer * rtp, guint32 ssrc)
{
GST_RTP_HEADER_SSRC (rtp->map[0].data) = g_htonl (ssrc);
GST_RTP_HEADER_SSRC (rtp->data[0]) = g_htonl (ssrc);
}
/**
......@@ -755,7 +734,7 @@ gst_rtp_buffer_set_ssrc (GstRTPBuffer * rtp, guint32 ssrc)
guint8
gst_rtp_buffer_get_csrc_count (GstRTPBuffer * rtp)
{
return GST_RTP_HEADER_CSRC_COUNT (rtp->map[0].data);
return GST_RTP_HEADER_CSRC_COUNT (rtp->data[0]);
}
/**
......@@ -772,7 +751,7 @@ gst_rtp_buffer_get_csrc (GstRTPBuffer * rtp, guint8 idx)
{
guint8 *data;
data = rtp->map[0].data;
data = rtp->data[0];
g_return_val_if_fail (idx < GST_RTP_HEADER_CSRC_COUNT (data), 0);
......@@ -792,7 +771,7 @@ gst_rtp_buffer_set_csrc (GstRTPBuffer * rtp, guint8 idx, guint32 csrc)
{
guint8 *data;
data = rtp->map[0].data;
data = rtp->data[0];
g_return_if_fail (idx < GST_RTP_HEADER_CSRC_COUNT (data));
......@@ -810,7 +789,7 @@ gst_rtp_buffer_set_csrc (GstRTPBuffer * rtp, guint8 idx, guint32 csrc)
gboolean
gst_rtp_buffer_get_marker (GstRTPBuffer * rtp)
{
return GST_RTP_HEADER_MARKER (rtp->map[0].data);
return GST_RTP_HEADER_MARKER (rtp->data[0]);
}
/**
......@@ -823,7 +802,7 @@ gst_rtp_buffer_get_marker (GstRTPBuffer * rtp)
void
gst_rtp_buffer_set_marker (GstRTPBuffer * rtp, gboolean marker)
{
GST_RTP_HEADER_MARKER (rtp->map[0].data) = marker;
GST_RTP_HEADER_MARKER (rtp->data[0]) = marker;
}
/**
......@@ -837,7 +816,7 @@ gst_rtp_buffer_set_marker (GstRTPBuffer * rtp, gboolean marker)
guint8
gst_rtp_buffer_get_payload_type (GstRTPBuffer * rtp)
{
return GST_RTP_HEADER_PAYLOAD_TYPE (rtp->map[0].data);
return GST_RTP_HEADER_PAYLOAD_TYPE (rtp->data[0]);
}
/**
......@@ -852,7 +831,7 @@ gst_rtp_buffer_set_payload_type (GstRTPBuffer * rtp, guint8 payload_type)
{
g_return_if_fail (payload_type < 0x80);
GST_RTP_HEADER_PAYLOAD_TYPE (rtp->map[0].data) = payload_type;
GST_RTP_HEADER_PAYLOAD_TYPE (rtp->data[0]) = payload_type;
}
/**
......@@ -866,7 +845,7 @@ gst_rtp_buffer_set_payload_type (GstRTPBuffer * rtp, guint8 payload_type)
guint16
gst_rtp_buffer_get_seq (GstRTPBuffer * rtp)
{
return g_ntohs (GST_RTP_HEADER_SEQ (rtp->map[0].data));
return g_ntohs (GST_RTP_HEADER_SEQ (rtp->data[0]));
}
/**
......@@ -879,7 +858,7 @@ gst_rtp_buffer_get_seq (GstRTPBuffer * rtp)
void
gst_rtp_buffer_set_seq (GstRTPBuffer * rtp, guint16 seq)
{
GST_RTP_HEADER_SEQ (rtp->map[0].data) = g_htons (seq);
GST_RTP_HEADER_SEQ (rtp->data[0]) = g_htons (seq);
}
/**
......@@ -893,7 +872,7 @@ gst_rtp_buffer_set_seq (GstRTPBuffer * rtp, guint16 seq)
guint32
gst_rtp_buffer_get_timestamp (GstRTPBuffer * rtp)
{
return g_ntohl (GST_RTP_HEADER_TIMESTAMP (rtp->map[0].data));
return g_ntohl (GST_RTP_HEADER_TIMESTAMP (rtp->data[0]));
}
/**
......@@ -906,7 +885,7 @@ gst_rtp_buffer_get_timestamp (GstRTPBuffer * rtp)
void
gst_rtp_buffer_set_timestamp (GstRTPBuffer * rtp, guint32 timestamp)
{
GST_RTP_HEADER_TIMESTAMP (rtp->map[0].data) = g_htonl (timestamp);
GST_RTP_HEADER_TIMESTAMP (rtp->data[0]) = g_htonl (timestamp);
}
......@@ -979,18 +958,8 @@ gst_rtp_buffer_get_payload_buffer (GstRTPBuffer * rtp)
guint
gst_rtp_buffer_get_payload_len (GstRTPBuffer * rtp)
{
guint len, size;
guint8 *data;
size = rtp->map[0].size;
data = rtp->map[0].data;
len = size - gst_rtp_buffer_get_header_len (rtp);
if (GST_RTP_HEADER_PADDING (data))
len -= data[size - 1];
return len;
return gst_buffer_get_size (rtp->buffer) - gst_rtp_buffer_get_header_len (rtp)
- rtp->size[3];
}
/**
......@@ -1006,7 +975,27 @@ gst_rtp_buffer_get_payload_len (GstRTPBuffer * rtp)
gpointer
gst_rtp_buffer_get_payload (GstRTPBuffer * rtp)
{
return rtp->map[0].data + gst_rtp_buffer_get_header_len (rtp);
guint hlen, plen;
guint idx, length;
gsize skip;
if (rtp->data[2])
return rtp->data[2];
hlen = gst_rtp_buffer_get_header_len (rtp);
plen = gst_buffer_get_size (rtp->buffer) - hlen - rtp->size[3];
if (!gst_buffer_find_memory (rtp->buffer, hlen, plen, &idx, &length, &skip))
return NULL;
if (!gst_buffer_map_range (rtp->buffer, idx, length, &rtp->map[2],
rtp->map[0].flags))
return NULL;
rtp->data[2] = rtp->map[2].data + skip;
rtp->size[2] = plen;
return rtp->data[2];
}