| /* 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; |
| } |