/* GStreamer * Copyright (C) <2008> Wim Taymans * * 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 #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; }