/// Version 1.0.0
/// Last modified 23.5.2014
///
/// Copyright (C) 2014 Veli-Mikko Puupponen
///
/// Halyri-system is a prototype emergency call system. Its purpose is to
/// demonstrate the use of the advanced capabilities available in the current
/// generation smartphones in facilitating the emergency service dispatcher's
/// capability to determine the nature of the emergency and to dispatch help.
///
/// For more information, see the README file of this package.
///
/// The MIT License (MIT)
///
/// Permission is hereby granted, free of charge, to any person obtaining a copy
/// of this software and associated documentation files (the "Software"), to
/// deal in the Software without restriction, including without limitation the
/// rights to use, copy, modify, merge, publish, distribute, sublicense,
/// and/or sell copies of the Software, and to permit persons to whom the
/// Software is furnished to do so, subject to the following conditions:
///
/// The above copyright notice and this permission notice shall be included in
/// all copies or substantial portions of the Software.
///
/// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
/// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
/// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
/// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
/// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
/// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS
/// IN THE SOFTWARE.
///
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
namespace SimpleUdpMediaClient.Packets
{
/// Veli-Mikko Puupponen
///
/// The class represents media information and payload data
/// up to one network MTU in length from a MediaPacket.
/// MediaHeaderPackets are used to transfer a portion of
/// payload data and the media information from a MediaPacket.
/// The combination of a MediaHeaderPacket and the subsequent
/// MediaContinuationPackets is a transfer sequence.
///
/// The packet consist of a packet type identifier, sender GUID,
/// a Int64 sequence number, media type identifier, media compression
/// identified, payload data length and originating data length.
///
/// The packet must always have payload data. If the payload data
/// length is smaller than network MTU, the transfer sequence
/// can consist of only a single MediaHeaderPacket.
///
public class MediaHeaderPacket : INetworkPacket
{
///
/// The specified length of the packet header in octets.
///
public const int HeaderLengthInOctets = 41;
///
/// The specified id of the packet type. It is used to identify a packet.
///
public const byte PacketId = 0;
private const int packetIdHeaderIndex = 0;
private const int sourceGuidHeaderIndex = 1;
private const int sequenceHeaderIndex = 17;
private const int mediaTypeHeaderIndex = 25;
private const int mediaTransferCompressionHeaderIndex = 27;
private const int totalPacketCountHeaderIndex = 29;
private const int dataLengthHeaderIndex = 33;
private const int originatingLengthHeaderIndex = 37;
private byte[] sourceGuid;
///
/// The function to get the Guid identifying the sender.
///
public string SourceGuid
{
get { return (sourceGuid == null || sourceGuid.Length != 16) ? "" : new Guid(sourceGuid).ToString(); }
}
private Int64 sequence;
///
/// The function to get and set the sequence number of this packet.
///
public Int64 Sequence
{
get { return sequence; }
set { sequence = value; }
}
private Int32 totalPackets;
///
/// The function to get the total count of packets in the related transfer sequence.
///
public Int32 TotalPacketCount
{
get { return totalPackets; }
}
private Int32 totalPayloadLength;
///
/// The function to get the total length of payload data in the related transfer sequence.
///
public Int32 TotalPayloadDataLength
{
get { return totalPayloadLength; }
}
private Int32 originatingLength;
///
/// The function to get the original length of the data prior to encoding it into the transfer format.
///
public Int32 OriginatingDataLength
{
get { return originatingLength; }
}
private Int16 mediaFormat;
///
/// The function to get the format of media in the packet.
///
public Int16 MediaFormat
{
get { return mediaFormat; }
}
private Int16 mediaTransferCompression;
///
/// The function to get the compression format of media in the packet.
///
public Int16 MediaTransferCompression
{
get { return mediaTransferCompression; }
}
private byte[] payloadData;
///
/// The function to get the payload data in this packet.
///
public byte[] PayloadData
{
get { return payloadData; }
}
///
/// The function initializes a new MediaHeaderPacket instance with no
/// parameters.
///
public MediaHeaderPacket()
{
}
///
/// The function initializes a new MediaHeaderPacket instance that has the
/// specified source Guid, media type identified, media transfer
/// compression identified, packet count, total payload data length,
/// originating data length, sequence number and payload data
///
/// Sthe string representation of the sender's Guid.
/// The payload media type.
/// The payload media transfer compression type.
/// Total count of packets in the related trasfer sequence.
/// The total number of payload octets in the complete
/// related trasfer sequence.
/// The original length of the data before
/// encoding into the transfer format.
/// The payload data in this packet.
/// The squence number for this packet, must not
/// vary within a transfer sequence.
public MediaHeaderPacket(byte[] guid, Int16 mediaTypeIdentifier, Int16 mediaTransferCompressioIdentifier,
Int32 packetCount, Int32 totalPayloadDataLength, Int32 originatingDataLength, byte[] payload, Int64 sequenceNumber)
: this(guid, mediaTypeIdentifier, mediaTransferCompressioIdentifier,
packetCount, totalPayloadDataLength, originatingDataLength, payload)
{
sequence = sequenceNumber;
}
///
/// The function initializes a new MediaHeaderPacket instance that has the
/// specified source Guid, media type identified, media transfer
/// compression identified, packet count, total payload data length,
/// originating data length and payload data
///
/// The sequence number must be provided prior any calls to the parameterless GetBytes.
///
/// Sthe string representation of the sender's Guid.
/// The payload media type.
/// The payload media transfer compression type.
/// Total count of packets in the related trasfer sequence.
/// The total number of payload octets in the complete
/// related trasfer sequence.
/// The original length of the data before
/// encoding into the transfer format.
/// The payload data in this packet.
public MediaHeaderPacket(byte[] guid, Int16 mediaTypeIdentifier, Int16 mediaTransferCompressioIdentifier,
Int32 packetCount, Int32 totalPayloadDataLength, Int32 originatingDataLength, byte[] payload)
{
sourceGuid = guid;
mediaFormat = mediaTypeIdentifier;
mediaTransferCompression = mediaTransferCompressioIdentifier;
totalPackets = packetCount;
totalPayloadLength = totalPayloadDataLength;
originatingLength = originatingDataLength;
payloadData = payload;
}
///
/// The function saves the given sequence number and returns the data header of the given sequence.
///
/// The sequence to retrieve.
/// The data header of given the sequence.
public byte[] GetBytes(long sequenceNumber)
{
sequence = sequenceNumber;
return GetBytes();
}
///
/// The function gets the media data, the packet control data and the packet data from the given packet and saves them.
///
/// The given data.
public void FromBytes(byte[] packetBytes)
{
if (packetBytes.Length < HeaderLengthInOctets)
throw new ArgumentException("Data length insufficient for headers.");
if(packetBytes[packetIdHeaderIndex] != PacketId)
throw new ArgumentException("Incompatible packet type.");
byte[] guidBytes = new byte[sequenceHeaderIndex - sourceGuidHeaderIndex];
Array.Copy(packetBytes, sourceGuidHeaderIndex, guidBytes, 0, guidBytes.Length);
sourceGuid = guidBytes;
sequence = BitConverter.ToInt64(packetBytes, sequenceHeaderIndex);
mediaFormat = BitConverter.ToInt16(packetBytes, mediaTypeHeaderIndex);
mediaTransferCompression = BitConverter.ToInt16(packetBytes, mediaTransferCompressionHeaderIndex);
totalPackets = BitConverter.ToInt32(packetBytes, totalPacketCountHeaderIndex);
totalPayloadLength = BitConverter.ToInt32(packetBytes, dataLengthHeaderIndex);
originatingLength = BitConverter.ToInt32(packetBytes, originatingLengthHeaderIndex);
if (packetBytes.Length > HeaderLengthInOctets)
{
payloadData = new byte[packetBytes.Length - HeaderLengthInOctets];
Array.Copy(packetBytes, HeaderLengthInOctets, payloadData, 0, payloadData.Length);
}
}
///
/// The function returns the data header of the currently saved sequence.
///
/// The data header of current the sequence.
public byte[] GetBytes()
{
int payloadLength = (payloadData == null) ? 0 : payloadData.Length;
byte[] networkPacketData = new byte[HeaderLengthInOctets + payloadLength];
networkPacketData[packetIdHeaderIndex] = PacketId;
Array.Copy(sourceGuid, 0, networkPacketData, sourceGuidHeaderIndex, sourceGuid.Length);
byte[] sequenceBytes = BitConverter.GetBytes(sequence);
Array.Copy(sequenceBytes, 0, networkPacketData, sequenceHeaderIndex, sequenceBytes.Length);
byte[] mediaTypeBytes = BitConverter.GetBytes(mediaFormat);
Array.Copy(mediaTypeBytes, 0, networkPacketData, mediaTypeHeaderIndex, mediaTypeBytes.Length);
byte[] mediaTransferCompressionBytes = BitConverter.GetBytes(mediaTransferCompression);
Array.Copy(mediaTransferCompressionBytes, 0, networkPacketData, mediaTransferCompressionHeaderIndex, mediaTransferCompressionBytes.Length);
byte[] totalPacketsBytes = BitConverter.GetBytes(totalPackets);
Array.Copy(totalPacketsBytes, 0, networkPacketData, totalPacketCountHeaderIndex, totalPacketsBytes.Length);
byte[] dataLengthBytes = BitConverter.GetBytes(totalPayloadLength);
Array.Copy(dataLengthBytes, 0, networkPacketData, dataLengthHeaderIndex, dataLengthBytes.Length);
byte[] originatingLengthBytes = BitConverter.GetBytes(originatingLength);
Array.Copy(originatingLengthBytes, 0, networkPacketData, originatingLengthHeaderIndex, originatingLengthBytes.Length);
if (payloadLength > 0)
Array.Copy(payloadData, 0, networkPacketData, HeaderLengthInOctets, payloadData.Length);
return networkPacketData;
}
}
}