gstreamer/gst/realmedia/gstrdtbuffer.c
Wim Taymans 6fb8002cab gst/realmedia/: Add first support for parsing RDT messages.
Original commit message from CVS:
* gst/realmedia/Makefile.am:
* gst/realmedia/gstrdtbuffer.c: (gst_rdt_buffer_validate_data),
(gst_rdt_buffer_validate), (gst_rdt_buffer_get_packet_count),
(read_packet_header), (gst_rdt_buffer_get_first_packet),
(gst_rdt_packet_move_to_next), (gst_rdt_packet_get_type),
(gst_rdt_packet_get_length), (gst_rdt_packet_to_buffer),
(gst_rdt_buffer_compare_seqnum), (gst_rdt_packet_data_get_seq),
(gst_rdt_packet_data_peek_data),
(gst_rdt_packet_data_get_stream_id),
(gst_rdt_packet_data_get_timestamp):
* gst/realmedia/gstrdtbuffer.h:
Add first support for parsing RDT messages.
* gst/realmedia/rdtjitterbuffer.c: (rdt_jitter_buffer_class_init),
(rdt_jitter_buffer_init), (rdt_jitter_buffer_finalize),
(rdt_jitter_buffer_new), (rdt_jitter_buffer_reset_skew),
(calculate_skew), (rdt_jitter_buffer_insert),
(rdt_jitter_buffer_pop), (rdt_jitter_buffer_peek),
(rdt_jitter_buffer_flush), (rdt_jitter_buffer_num_packets),
(rdt_jitter_buffer_get_ts_diff):
* gst/realmedia/rdtjitterbuffer.h:
Add first version of an RDT jitterbuffer.
2008-08-27 09:52:49 +00:00

425 lines
9.9 KiB
C

/* 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., 59 Temple Place - Suite 330,
* Boston, MA 02111-1307, USA.
*/
#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)
{
guint8 *data;
guint 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);
data = GST_BUFFER_DATA (packet->buffer);
size = GST_BUFFER_SIZE (packet->buffer);
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)
return FALSE;
/* 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;
}
/* the length should be smaller than the remaining size */
if (packet->length + offset > size)
goto invalid_length;
return TRUE;
/* ERRORS */
unknown_packet:
{
packet->type = GST_RDT_TYPE_INVALID;
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;
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_create_sub (packet->buffer, 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)
{
guint header;
guint8 *bufdata;
guint bufsize;
guint16 result;
g_return_val_if_fail (packet != NULL, FALSE);
g_return_val_if_fail (GST_RDT_IS_DATA_TYPE (packet->type), FALSE);
bufdata = GST_BUFFER_DATA (packet->buffer);
bufsize = GST_BUFFER_SIZE (packet->buffer);
/* skip header bits */
header = packet->offset + 1;
/* read seq_no */
result = GST_READ_UINT16_BE (&bufdata[header]);
return result;
}
gboolean
gst_rdt_packet_data_peek_data (GstRDTPacket * packet, guint8 ** data,
guint * size)
{
guint header;
guint8 *bufdata;
guint bufsize;
gboolean length_included_flag;
gboolean need_reliable_flag;
guint8 stream_id;
guint8 asm_rule_number;
g_return_val_if_fail (packet != NULL, FALSE);
g_return_val_if_fail (GST_RDT_IS_DATA_TYPE (packet->type), FALSE);
bufdata = GST_BUFFER_DATA (packet->buffer);
bufsize = GST_BUFFER_SIZE (packet->buffer);
header = packet->offset;
length_included_flag = (bufdata[header] & 0x80) == 0x80;
need_reliable_flag = (bufdata[header] & 0x40) == 0x40;
stream_id = (bufdata[header] & 0x3e) >> 1;
/* skip seq_no and header bits */
header += 3;
if (length_included_flag) {
/* skip length */
header += 2;
}
asm_rule_number = (bufdata[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 (data)
*data = &bufdata[header];
if (size)
*size = packet->length - (header - packet->offset);
return TRUE;
}
guint16
gst_rdt_packet_data_get_stream_id (GstRDTPacket * packet)
{
guint16 result;
guint header;
gboolean length_included_flag;
guint8 *bufdata;
guint bufsize;
g_return_val_if_fail (packet != NULL, 0);
g_return_val_if_fail (GST_RDT_IS_DATA_TYPE (packet->type), 0);
bufdata = GST_BUFFER_DATA (packet->buffer);
bufsize = GST_BUFFER_SIZE (packet->buffer);
header = packet->offset;
length_included_flag = (bufdata[header] & 0x80) == 0x80;
result = (bufdata[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 (&bufdata[header]);
}
return result;
}
guint32
gst_rdt_packet_data_get_timestamp (GstRDTPacket * packet)
{
guint32 result;
guint header;
gboolean length_included_flag;
guint8 *bufdata;
guint bufsize;
g_return_val_if_fail (packet != NULL, 0);
g_return_val_if_fail (GST_RDT_IS_DATA_TYPE (packet->type), 0);
bufdata = GST_BUFFER_DATA (packet->buffer);
bufsize = GST_BUFFER_SIZE (packet->buffer);
header = packet->offset;
length_included_flag = (bufdata[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 (&bufdata[header]);
return result;
}