blob: 50bc7f438a352a0d0973315a3a2994ff9bbff23c [file] [log] [blame]
/* GStreamer
* Copyright (C) <2008> Wim Taymans <wim.taymans@gmail.com>
*
* 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., 51 Franklin St, Fifth Floor,
* Boston, MA 02110-1301, USA.
*/
#include <string.h>
#include "gstrdtbuffer.h"
gboolean
gst_rdt_buffer_validate_data (guint8 * data, guint len)
{
return TRUE;
}
gboolean
gst_rdt_buffer_validate (GstBuffer * buffer)
{
return TRUE;
}
guint
gst_rdt_buffer_get_packet_count (GstBuffer * buffer)
{
GstRDTPacket packet;
guint count;
g_return_val_if_fail (GST_IS_BUFFER (buffer), 0);
count = 0;
if (gst_rdt_buffer_get_first_packet (buffer, &packet)) {
do {
count++;
} while (gst_rdt_packet_move_to_next (&packet));
}
return count;
}
static gboolean
read_packet_header (GstRDTPacket * packet)
{
GstMapInfo map;
guint8 *data;
gsize size;
guint offset;
guint length;
guint length_offset;
g_return_val_if_fail (packet != NULL, FALSE);
g_return_val_if_fail (GST_IS_BUFFER (packet->buffer), FALSE);
gst_buffer_map (packet->buffer, &map, GST_MAP_READ);
data = map.data;
size = map.size;
offset = packet->offset;
/* check if we are at the end of the buffer, we add 3 because we also want to
* ensure we can read the type, which is always at offset 1 and 2 bytes long. */
if (offset + 3 > size)
goto packet_end;
/* read type */
packet->type = GST_READ_UINT16_BE (&data[offset + 1]);
length = -1;
length_offset = -1;
/* figure out the length of the packet, this depends on the type */
if (GST_RDT_IS_DATA_TYPE (packet->type)) {
if (data[offset] & 0x80)
/* length is present */
length_offset = 3;
} else {
switch (packet->type) {
case GST_RDT_TYPE_ASMACTION:
if (data[offset] & 0x80)
length_offset = 5;
break;
case GST_RDT_TYPE_BWREPORT:
if (data[offset] & 0x80)
length_offset = 3;
break;
case GST_RDT_TYPE_ACK:
if (data[offset] & 0x80)
length_offset = 3;
break;
case GST_RDT_TYPE_RTTREQ:
length = 3;
break;
case GST_RDT_TYPE_RTTRESP:
length = 11;
break;
case GST_RDT_TYPE_CONGESTION:
length = 11;
break;
case GST_RDT_TYPE_STREAMEND:
length = 9;
/* total_reliable */
if (data[offset] & 0x80)
length += 2;
/* stream_id_expansion */
if ((data[offset] & 0x7c) == 0x7c)
length += 2;
/* ext_flag, FIXME, get string length */
if ((data[offset] & 0x1) == 0x1)
length += 7;
break;
case GST_RDT_TYPE_REPORT:
if (data[offset] & 0x80)
length_offset = 3;
break;
case GST_RDT_TYPE_LATENCY:
if (data[offset] & 0x80)
length_offset = 3;
break;
case GST_RDT_TYPE_INFOREQ:
length = 3;
/* request_time_ms */
if (data[offset] & 0x2)
length += 2;
break;
case GST_RDT_TYPE_INFORESP:
length = 3;
/* has_rtt_info */
if (data[offset] & 0x4) {
length += 4;
/* is_delayed */
if (data[offset] & 0x2) {
length += 4;
}
}
if (data[offset] & 0x1) {
/* buffer_info_count, FIXME read and skip */
length += 2;
}
break;
case GST_RDT_TYPE_AUTOBW:
if (data[offset] & 0x80)
length_offset = 3;
break;
case GST_RDT_TYPE_INVALID:
default:
goto unknown_packet;
}
}
if (length != -1) {
/* we have a fixed length */
packet->length = length;
} else if (length_offset != -1) {
/* we can read the length from an offset */
packet->length = GST_READ_UINT16_BE (&data[length_offset]);
} else {
/* length is remainder of packet */
packet->length = size - offset;
}
gst_buffer_unmap (packet->buffer, &map);
/* the length should be smaller than the remaining size */
if (packet->length + offset > size)
goto invalid_length;
return TRUE;
/* ERRORS */
packet_end:
{
gst_buffer_unmap (packet->buffer, &map);
return FALSE;
}
unknown_packet:
{
packet->type = GST_RDT_TYPE_INVALID;
gst_buffer_unmap (packet->buffer, &map);
return FALSE;
}
invalid_length:
{
packet->type = GST_RDT_TYPE_INVALID;
packet->length = 0;
return FALSE;
}
}
gboolean
gst_rdt_buffer_get_first_packet (GstBuffer * buffer, GstRDTPacket * packet)
{
g_return_val_if_fail (GST_IS_BUFFER (buffer), FALSE);
g_return_val_if_fail (packet != NULL, FALSE);
/* init to 0 */
packet->buffer = buffer;
packet->offset = 0;
packet->type = GST_RDT_TYPE_INVALID;
memset (&packet->map, 0, sizeof (GstMapInfo));
if (!read_packet_header (packet))
return FALSE;
return TRUE;
}
gboolean
gst_rdt_packet_move_to_next (GstRDTPacket * packet)
{
g_return_val_if_fail (packet != NULL, FALSE);
g_return_val_if_fail (packet->type != GST_RDT_TYPE_INVALID, FALSE);
g_return_val_if_fail (GST_IS_BUFFER (packet->buffer), FALSE);
/* if we have an invalid packet, it must be the last,
* return FALSE */
if (packet->type == GST_RDT_TYPE_INVALID)
goto end;
/* move to next packet */
packet->offset += packet->length;
/* try to read new header */
if (!read_packet_header (packet))
goto end;
return TRUE;
/* ERRORS */
end:
{
packet->type = GST_RDT_TYPE_INVALID;
return FALSE;
}
}
GstRDTType
gst_rdt_packet_get_type (GstRDTPacket * packet)
{
g_return_val_if_fail (packet != NULL, GST_RDT_TYPE_INVALID);
g_return_val_if_fail (packet->type != GST_RDT_TYPE_INVALID,
GST_RDT_TYPE_INVALID);
return packet->type;
}
guint16
gst_rdt_packet_get_length (GstRDTPacket * packet)
{
g_return_val_if_fail (packet != NULL, 0);
g_return_val_if_fail (packet->type != GST_RDT_TYPE_INVALID, 0);
return packet->length;
}
GstBuffer *
gst_rdt_packet_to_buffer (GstRDTPacket * packet)
{
GstBuffer *result;
g_return_val_if_fail (packet != NULL, NULL);
g_return_val_if_fail (packet->type != GST_RDT_TYPE_INVALID, NULL);
result =
gst_buffer_copy_region (packet->buffer, GST_BUFFER_COPY_ALL,
packet->offset, packet->length);
/* timestamp applies to all packets in this buffer */
GST_BUFFER_TIMESTAMP (result) = GST_BUFFER_TIMESTAMP (packet->buffer);
return result;
}
gint
gst_rdt_buffer_compare_seqnum (guint16 seqnum1, guint16 seqnum2)
{
return (gint16) (seqnum2 - seqnum1);
}
guint16
gst_rdt_packet_data_get_seq (GstRDTPacket * packet)
{
GstMapInfo map;
guint header;
guint16 result;
g_return_val_if_fail (packet != NULL, FALSE);
g_return_val_if_fail (GST_RDT_IS_DATA_TYPE (packet->type), FALSE);
gst_buffer_map (packet->buffer, &map, GST_MAP_READ);
/* skip header bits */
header = packet->offset + 1;
/* read seq_no */
result = GST_READ_UINT16_BE (&map.data[header]);
gst_buffer_unmap (packet->buffer, &map);
return result;
}
guint8 *
gst_rdt_packet_data_map (GstRDTPacket * packet, guint * size)
{
GstMapInfo map;
guint header;
gboolean length_included_flag;
gboolean need_reliable_flag;
guint8 stream_id;
guint8 asm_rule_number;
g_return_val_if_fail (packet != NULL, NULL);
g_return_val_if_fail (packet->map.data == NULL, NULL);
g_return_val_if_fail (GST_RDT_IS_DATA_TYPE (packet->type), NULL);
gst_buffer_map (packet->buffer, &map, GST_MAP_READ);
header = packet->offset;
length_included_flag = (map.data[header] & 0x80) == 0x80;
need_reliable_flag = (map.data[header] & 0x40) == 0x40;
stream_id = (map.data[header] & 0x3e) >> 1;
/* skip seq_no and header bits */
header += 3;
if (length_included_flag) {
/* skip length */
header += 2;
}
asm_rule_number = (map.data[header] & 0x3f);
/* skip timestamp and asm_rule_number */
header += 5;
if (stream_id == 0x1f) {
/* skip stream_id_expansion */
header += 2;
}
if (need_reliable_flag) {
/* skip total_reliable */
header += 2;
}
if (asm_rule_number == 63) {
/* skip asm_rule_number_expansion */
header += 2;
}
if (size)
*size = packet->length - (header - packet->offset);
packet->map = map;
return &map.data[header];
}
gboolean
gst_rdt_packet_data_unmap (GstRDTPacket * packet)
{
g_return_val_if_fail (packet != NULL, FALSE);
g_return_val_if_fail (packet->map.data != NULL, FALSE);
gst_buffer_unmap (packet->buffer, &packet->map);
packet->map.data = NULL;
return TRUE;
}
guint16
gst_rdt_packet_data_get_stream_id (GstRDTPacket * packet)
{
GstMapInfo map;
guint16 result;
guint header;
gboolean length_included_flag;
g_return_val_if_fail (packet != NULL, 0);
g_return_val_if_fail (GST_RDT_IS_DATA_TYPE (packet->type), 0);
gst_buffer_map (packet->buffer, &map, GST_MAP_READ);
header = packet->offset;
length_included_flag = (map.data[header] & 0x80) == 0x80;
result = (map.data[header] & 0x3e) >> 1;
if (result == 31) {
/* skip seq_no and header bits */
header += 3;
if (length_included_flag) {
/* skip length */
header += 2;
}
/* skip asm_rule_number and timestamp */
header += 5;
/* stream_id_expansion */
result = GST_READ_UINT16_BE (&map.data[header]);
}
gst_buffer_unmap (packet->buffer, &map);
return result;
}
guint32
gst_rdt_packet_data_get_timestamp (GstRDTPacket * packet)
{
GstMapInfo map;
guint header;
gboolean length_included_flag;
guint32 result;
g_return_val_if_fail (packet != NULL, 0);
g_return_val_if_fail (GST_RDT_IS_DATA_TYPE (packet->type), 0);
gst_buffer_map (packet->buffer, &map, GST_MAP_READ);
header = packet->offset;
length_included_flag = (map.data[header] & 0x80) == 0x80;
/* skip seq_no and header bits */
header += 3;
if (length_included_flag) {
/* skip length */
header += 2;
}
/* skip asm_rule_number */
header += 1;
/* get timestamp */
result = GST_READ_UINT32_BE (&map.data[header]);
gst_buffer_unmap (packet->buffer, &map);
return result;
}
guint8
gst_rdt_packet_data_get_flags (GstRDTPacket * packet)
{
GstMapInfo map;
guint8 result;
guint header;
gboolean length_included_flag;
g_return_val_if_fail (packet != NULL, 0);
g_return_val_if_fail (GST_RDT_IS_DATA_TYPE (packet->type), 0);
gst_buffer_map (packet->buffer, &map, GST_MAP_READ);
header = packet->offset;
length_included_flag = (map.data[header] & 0x80) == 0x80;
/* skip seq_no and header bits */
header += 3;
if (length_included_flag) {
/* skip length */
header += 2;
}
/* get flags */
result = map.data[header];
gst_buffer_unmap (packet->buffer, &map);
return result;
}