/* The Java RTP/I library, Version 0.1 alpha.
*
* This library provides the functionality of RTP/I as it is specified in
* Internet Draft draft-mauve-rtpi-00.txt.
*
* Copyright (C) 2000 Martin Mauve
* University of Mannheim / Germany
*
* This library is free software; you can redistribute it and/or
* modify it under the terms of the GNU Lesser General Public
* License as published by the Free Software Foundation; either
* version 2.1 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
* Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser 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
*
* Martin Mauve
*
* e-mail:
* mauve@informatik.uni-mannheim.de
*
* paper mail:
* Martin Mauve
* University of Mannheim
* Lehrstuhl Praktische Informatik IV
* L15, 16
* 68131 Mannheim
* Germany
*/
package rtpi.packets;
import rtpi.IllegalValueException;
import java.io.ByteArrayInputStream;
import java.io.DataInputStream;
import java.io.ByteArrayOutputStream;
import java.io.DataOutputStream;
import java.io.IOException;
/**
* This class represents an RTP/I data packet.
*
* @author Martin Mauve
*/
final public class RtpiDataPacket {
/**
* The RTP/I header size.
*/
public final static int HEADER_SIZE = 28;
/**
* The protocol version number.
*/
public final static int VERSION = 1;
/**
* EVENT ADU type.
*/
public final static int EVENT = 0;
/**
* STATE ADU type.
*/
public final static int STATE = 1;
/**
* DELTA_STATE ADU type.
*/
public final static int DELTA_STATE = 2;
/**
* STATE_QUERY ADU type.
*/
public final static int STATE_QUERY = 3;
/**
* ILLEGAL ADU type.
*/
public final static int ILLEGAL_TYPE = 16;
/**
* Reliability mapping ID for unreliable transmission.
*/
public final static int NO_RELIABILITY = 0;
/**
* Reliability mapping ID for transprent transport level reliability (such as TCP).
*/
public final static int RELIABLE_TRANSPORT = 1;
/**
* Illegal value for the reliability mapping.
*/
public final static int ILLEGAL_RELIABILITY_TYPE = 64;
/**
* Illegal value for the payload type field.
*/
public final static int ILLEGAL_PAYLOAD_TYPE = 0xFF;
// fixed header fields
private int version=VERSION;
private int end=0;
private int extension=0;
private int type=ILLEGAL_TYPE;
private int payloadType=ILLEGAL_PAYLOAD_TYPE;
private int length=0;
private int reliabilityType=ILLEGAL_RELIABILITY_TYPE;
private int priority=0;
private byte profileInformation=0x0;
private byte[] reliabilityInformation=new byte[2];
private int participantID=0;
private long subcomponentID=0;
private int sequenceNumber=0;
private int fragmentCount=0;
private long timestamp=0;
// optional header extension
private int reliabilityHeaderLength=0;
private byte[] reliabilityHeader=null;
// binary representation of the packet
private byte[] packetData=null;
private int payloadStart=0;
private int headerStart=0;
private int payloadLength=0;
// Bitmasks for parsing:
private final static int VERSION_BITMASK = 0xC0;
private final static int END_BITMASK = 0x20;
private final static int EXTENSION_BITMASK = 0x10;
private final static int TYPE_BITMASK = 0x0F;
private final static int RELIABILITY_TYPE_BITMASK = 0xFC;
private final static int PRIORITY_BITMASK = 0x03;
// Bitmasks for removing the sign
private final static int INT_REMOVE_SIGN_BITMASK = 0x7fffffff;
private final static int INT_SIGN_BITMASK = 0x80000000;
/**
* This creates ab RtpiDataPacket from an incoming packet. The
* incoming transport layer packet may contain more than one
* RtpiDataPacket.
*
* @param transportPacket The transport packet that has been received.
* @param start The start of the RtpiDataPacket within the transport packet.
* This identifies the position of the first byte of the header
* of this RtpiDataPacket in the transportPacket. It must be
* in the range between 0 and transportPacket.length.
*/
public RtpiDataPacket(byte[] transportPacket, int start) {
packetData=transportPacket;
headerStart=start;
}
/**
* This creates a new RtpiDataPacket that is to be transmitted
* over the network.
*
* @param rtpiDataPacket The payload data.
* @param start The start of the payload data in the byte array. Ideally this should
* be identical to the sum of RTP/I header and reliability header. Then
* the header can be prepended to tha payload without copy operation.
* @param size The size of the payload data in the data array.
*/
public RtpiDataPacket(byte[] data, int begin, int size) {
packetData=data;
payloadStart=begin;
payloadLength=size;
}
/**
* This returns the position of the first byte of this packet's payload.
*
* @return The position of the first byte of the payload.
*/
public int getPayloadStart() {
return payloadStart;
}
/**
* This returns the length of the payload in bytes.
*
* @return The length of the payload.
*/
public int getPayloadLength() {
return payloadLength;
}
/**
* This returns the protocol version of the packet.
*
* @return The protocol version of this packet
*/
public int getVersion() {
return version;
}
/**
* This returns the value of this packets end bit.
*
* @return Value of the end bit.
*/
public int getEnd() {
return end;
}
/**
* This sets the value of the end bit for this packet.
*
* @param e The end bit.
*/
public void setEnd(int e) {
end=e;
}
/**
* This returns the value of the extension bit.
*
* @return The value of the extension bit.
*/
public int getExtension() {
return extension;
}
/**
* This sets the value of the extension bit for this packet.
*
* @param e The value of the extension bit.
*/
public void setExtension(int e) {
extension=e;
}
/**
* This returns the ADU type of the packet.
*
* @return The ADU type (e.g. STATE or EVENT).
*/
public int getType() {
return type;
}
/**
* This sets the ADU type for this packet.
*
* @param t The ADU type (e.g. STATE or EVENT).
*/
public void setType(int t) {
type=t;
}
/**
* This returns the payload type of the packet.
*
* @return The payload type.
*/
public int getPayloadType() {
return payloadType;
}
/**
* This sets the payload type for the packet.
*
* @param pt The payload type.
*/
public void setPayloadType(int pt) {
payloadType=pt;
}
/**
* This gets the length field of the packet.
*
* @return The length field of the packet.
*/
public int getLength() {
return length;
}
/**
* This returns the reliability type of the packet.
*
* @return The reliability Type.
*/
public int getReliabilityType() {
return reliabilityType;
}
/**
* This sets the reliability type of the packet.
*
* @param rt The reliability Type.
*/
public void setReliabilityType(int rt) {
reliabilityType=rt;
}
/**
* This gets the priority of the packet.
*
* @return The priority.
*/
public int getPriority() {
return priority;
}
/**
* This sets the priority of the packet.
*
* @param The Priority.
*/
public void setPriority(int p) {
priority=p;
}
/**
* This gets the profile specific information contained in
* this packet.
*
* @return The profile specific information.
*/
public byte getProfileInformation() {
return profileInformation;
}
/**
* This sets the profile specific information for this
* packet.
*
* @param pi The profile specific information.
*/
public void setProfileInformation(byte pi) {
profileInformation=pi;
}
/**
* This gets the reliability specific information contained
* in the packet.
*
* @return The profile specific information.
*/
public byte[] getReliabilityInformation() {
return reliabilityInformation;
}
/**
* This sets the reliability specific information for
* this packet.
*
* @param The reliability specific information.
*/
public void setReliabilityInformation(byte b0, byte b1) {
reliabilityInformation[0]=b0;
reliabilityInformation[1]=b1;
}
/**
* This gets the participant ID of this packets sender.
*
* @return The participant ID.
*/
public int getParticipantID() {
return participantID;
}
/**
* This sets the participant ID of this packet.
*
* @param The participant ID.
*/
public void setParticipantID(int pid) {
participantID=pid;
}
/**
* This gets the subcomponent ID of the subcomponent
* this packet refers to.
*
* @return The subcomponent ID.
*/
public long getSubcomponentID() {
return subcomponentID;
}
/**
* This sets the subcomponent ID for this packet.
*
* @param The subcomponent ID.
*/
public void setSubcomponentID(long subID) {
subcomponentID=subID;
}
/**
* This returns the sequence number of the ADU
* this packet belongs to.
*
* @return The sequence number.
*/
public int getSequenceNumber() {
return sequenceNumber;
}
/**
* This sets the sequence number of the packet.
*
* @param nr The sequence number of the ADU this packet belongs to.
*/
public void setSequenceNumber(int nr) {
sequenceNumber=nr;
}
/**
* This gets the fragment count of this packet.
*
* @return The fragment count of this packet.
*/
public int getFragmentCount() {
return fragmentCount;
}
/**
* Thist sets the fragment count of this packet.
*
* @prarm fc The fragment count.
*/
public void setFragmentCount(int fc) {
fragmentCount=fc;
}
/**
* This returns the timestamp of this packet.
*
* @return The timestamp.
*/
public long getTimestamp() {
return timestamp;
}
/**
* This sets the timestamp of this packet.
*
* @param The timestamp.
*/
public void setTimestamp(long ts) {
timestamp=ts;
}
/**
* This gets the raw bytes of the encoded packet. This
* method should be called AFTER flush has been performed
* successfully.
*
* @return The byte array containing the encoded packet.
*/
public byte[] getPacketData() {
return packetData;
}
/**
* This sets the reliablity extension header.
*
* @param header A reliability specific extension header that, when added
* one byte for the header length field, must be a multiple
* of four bytes in length.
*/
public void setReliabilityHeader(byte[] header) throws IllegalValueException {
if (header==null) {
throw new IllegalValueException("RtpiDataPacket: Reliability header needs to be a non null value");
}
if ((header.length+1)%4!=0) {
throw new IllegalValueException("RtpiDataPacket: The size of a reliability header need to be a multiple of 4 bytes");
}
extension=1;
reliabilityHeader=header;
reliabilityHeaderLength=(header.length+1)/4-1;
}
/**
* This deletes the reliability header of a packet.
*/
public void deleteReliabilityHeader() {
extension=0;
reliabilityHeader=null;
reliabilityHeaderLength=0;
}
/**
* This returns the length of the reliability header in multiples
* of four bytes.
*
* @return The length of the reliability header.
*/
public int getReliabilityHeaderLength() {
return reliabilityHeaderLength;
}
/**
* This returns the reliability header.
*
* @return The reliability header.
*/
public byte[] getReliabilityHeader() {
return reliabilityHeader;
}
/**
* This parses a packet.
*/
public void parse() throws RtpiParseException {
if (packetData==null || headerStart>=packetData.length) {
throw new RtpiParseException("RtpiDataPacket: the size of packetData does not match the start of the packet header");
}
ByteArrayInputStream bis = new ByteArrayInputStream(packetData, headerStart, packetData.length-headerStart);
DataInputStream dis = new DataInputStream(bis);
byte b=0;
int word=0;
try {
b = dis.readByte();
} catch (Exception exe) {
throw new RtpiParseException("RtpiDataPacket: Could not read first two bytes from the RTP/I header");
}
version=(b & VERSION_BITMASK) >>> 6;
if (version != VERSION) {
throw new RtpiParseException("RtpiDataPacket: wrong RTP/I Version");
}
end=(b & END_BITMASK) >>> 5;
extension=(b & EXTENSION_BITMASK) >>> 4;
type=(b & TYPE_BITMASK);
try {
payloadType = dis.readUnsignedByte();
} catch (Exception exe) {
throw new RtpiParseException("RtpiDataPacket: Could not read payload type from the RTP/I header");
}
try {
length = dis.readUnsignedShort();
} catch (Exception exe) {
throw new RtpiParseException("RtpiDataPacket: Could not read length from the RTP/I header");
}
try {
b = dis.readByte();
} catch (Exception exe) {
throw new RtpiParseException("RtpiDataPacket: Could not read fifth byte from the RTP/I header");
}
reliabilityType = (b & RELIABILITY_TYPE_BITMASK) >>> 2;
priority = (b & PRIORITY_BITMASK);
try {
profileInformation = dis.readByte();
} catch (Exception exe) {
throw new RtpiParseException("RtpiDataPacket: Could not read profile information from the RTP/I header");
}
try {
reliabilityInformation[0] = dis.readByte();
} catch (Exception exe) {
throw new RtpiParseException("RtpiDataPacket: Could not read reliability information byte 0 from the RTP/I header");
}
try {
reliabilityInformation[1] = dis.readByte();
} catch (Exception exe) {
throw new RtpiParseException("RtpiDataPacket: Could not read reliability information byte 1 from the RTP/I header");
}
try {
participantID = dis.readInt();
} catch (Exception exe) {
throw new RtpiParseException("RtpiDataPacket: Could not read participant ID from the RTP/I header");
}
try {
subcomponentID = dis.readLong();
} catch (Exception exe) {
throw new RtpiParseException("RtpiDataPacket: Could not read SUBID from the RTP/I header");
}
try {
sequenceNumber = dis.readUnsignedShort();
} catch (Exception exe) {
throw new RtpiParseException("RtpiDataPacket: Could not read sequence number from the RTP/I header");
}
try {
fragmentCount = dis.readUnsignedShort();
} catch (Exception exe) {
throw new RtpiParseException("RtpiDataPacket: Could not read fragement count from the RTP/I header");
}
try {
word = dis.readInt();
} catch (Exception exe) {
throw new RtpiParseException("RtpiDataPacket: Could not read timestamp from the RTP/I header");
}
long intLeadingBit = (word & INT_SIGN_BITMASK) >>> 31;
timestamp=word&INT_REMOVE_SIGN_BITMASK;
timestamp=timestamp|(intLeadingBit<<31);
payloadStart=headerStart+HEADER_SIZE;
payloadLength=length;
if (extension==1) {
try {
reliabilityHeaderLength=dis.readUnsignedByte();
} catch (Exception exe) {
throw new RtpiParseException("RtpiDataPacket: Could not read reliability header length from the RTP/I header");
}
reliabilityHeader=new byte[reliabilityHeaderLength*4+3];
try {
dis.readFully(reliabilityHeader);
} catch (Exception exe) {
throw new RtpiParseException("RtpiDataPacket: Could not read reliability header from the RTP/I header");
}
payloadStart+=reliabilityHeader.length+1;
payloadLength-=reliabilityHeader.length+1;
}
try {
dis.close();
bis.close();
} catch (Exception exe) {
throw new RtpiParseException("RtpiDataPacket: Could not close input stream for RTP/I packet");
}
}
/**
* This flushes a packet.
*/
public void flush() throws RtpiFlushException {
length=payloadLength;
if (extension==1) {
length+=(reliabilityHeaderLength+1)*4;
}
ByteArrayOutputStream bos = new ByteArrayOutputStream();
DataOutputStream dos = new DataOutputStream(bos);
int word;
byte b;
word=version<<6;
word=word|(end<<5);
word=word|(extension<<4);
word=word|type;
try {
dos.writeByte((byte) word);
} catch (Exception ex) {
throw new RtpiFlushException("RtpiDataPacket: Couldn't write first byte to RTP/I header");
}
try {
dos.writeByte((short) payloadType);
} catch (Exception ex) {
throw new RtpiFlushException("RtpiDataPacket: Couldn't write second byte to RTP/I header");
}
try {
dos.writeShort((short) length);
} catch (Exception ex) {
throw new RtpiFlushException("RtpiDataPacket: Couldn't write length to RTP/I header");
}
word=reliabilityType<<2;
word=word|priority;
try {
dos.writeByte((byte) word);
} catch (Exception ex) {
throw new RtpiFlushException("RtpiDataPacket: Couldn't write fifth byte to RTP/I header");
}
try {
dos.writeByte((byte) profileInformation);
} catch (Exception ex) {
throw new RtpiFlushException("RtpiDataPacket: Couldn't write profile information to RTP/I header");
}
try {
dos.writeByte(reliabilityInformation[0]);
} catch (Exception ex) {
throw new RtpiFlushException("RtpiDataPacket: Couldn't write reliability informatioin byte 0 to RTP/I header");
}
try {
dos.writeByte(reliabilityInformation[1]);
} catch (Exception ex) {
throw new RtpiFlushException("RtpiDataPacket: Couldn't write reliability informatioin byte 1 to RTP/I header");
}
try {
dos.writeInt(participantID);
} catch (Exception ex) {
throw new RtpiFlushException("RtpiDataPacket: Couldn't write participant ID to RTP/I header");
}
try {
dos.writeLong(subcomponentID);
} catch (Exception ex) {
throw new RtpiFlushException("RtpiDataPacket: Couldn't write subcomponent ID to RTP/I header");
}
try {
dos.writeShort(sequenceNumber);
} catch (Exception ex) {
throw new RtpiFlushException("RtpiDataPacket: Couldn't write sequence number to RTP/I header");
}
try {
dos.writeShort(fragmentCount);
} catch (Exception ex) {
throw new RtpiFlushException("RtpiDataPacket: Couldn't write fragment count to RTP/I header");
}
try {
dos.writeInt((int)timestamp);
} catch (Exception ex) {
throw new RtpiFlushException("RtpiDataPacket: Couldn't write timestamp to RTP/I header");
}
if (extension==1) {
try {
dos.writeByte((byte)reliabilityHeaderLength);
} catch (Exception ex) {
throw new RtpiFlushException("RtpiDataPacket: Couldn't write reliability header length to RTP/I header");
}
}
try {
dos.flush();
bos.flush();
} catch (Exception ex) {
throw new RtpiFlushException("RtpiDataPacket: Couldn't flush output streams to RTP/I header!");
}
byte[] header = bos.toByteArray();
int payloadShouldStart=HEADER_SIZE;
if (extension==1) {
payloadShouldStart+=(reliabilityHeaderLength+1)*4;
}
if (payloadStart!=payloadShouldStart) {
byte[] buf = packetData;
packetData = new byte[HEADER_SIZE+length];
System.arraycopy(buf, payloadStart, packetData, payloadShouldStart, payloadLength);
payloadStart=payloadShouldStart;
}
System.arraycopy(header,0,packetData,0,header.length);
if (extension==1) {
System.arraycopy(reliabilityHeader,0,packetData,HEADER_SIZE+1,reliabilityHeaderLength*4+3);
}
try {
dos.close();
bos.close();
} catch (Exception ex) {
throw new RtpiFlushException("RtpiDataPacket: Couldn't close output streams to RTP/I header");
}
}
public int flushToArray(byte[] destination, int start) throws RtpiFlushException {
flush();
if (start+length+HEADER_SIZE>destination.length) {
throw new RtpiFlushException("RtpiDataPacket: Couldn't flush to array, array too small!");
}
System.arraycopy(packetData, headerStart, destination, start, length+HEADER_SIZE);
return start+length+HEADER_SIZE;
}
/**
* Print this object to a String.
*
* @return This object as a String.
*/
public String toString() {
String returnValue;
returnValue ="RtpiDataPacket: {\n";
returnValue+=" version: "+version+"\n";
returnValue+=" end: "+end+"\n";
returnValue+=" extension: "+extension+"\n";
returnValue+=" type: "+type+"\n";
returnValue+=" payload type: "+payloadType+"\n";
returnValue+=" length: "+length+"\n";
returnValue+=" reliability type: "+reliabilityType+"\n";
returnValue+=" profile information: "+profileInformation+"\n";
returnValue+=" reliability information byte 0: "+reliabilityInformation[0]+"\n";
returnValue+=" reliability information byte 1: "+reliabilityInformation[1]+"\n";
returnValue+=" participant identifier: "+participantID+"\n";
returnValue+=" sub-component identifier: "+subcomponentID+"\n";
returnValue+=" sequence number: "+sequenceNumber+"\n";
returnValue+=" fragment count: "+fragmentCount+"\n";
returnValue+=" timestamp: "+timestamp+"\n";
returnValue+=" payload start: "+payloadStart+"\n";
returnValue+=" header start: "+headerStart+"\n";
returnValue+=" payload length: "+payloadLength+"\n";
if (extension==1) {
returnValue+=" reliabilityHeaderLength: "+reliabilityHeaderLength+"\n";
returnValue+=" reliabilityHeader: ";
for (int i=0;i<reliabilityHeaderLength*4+3;i++) {
returnValue+=reliabilityHeader[i]+" ";
}
returnValue+="\n";
}
returnValue+=" payloadData: ";
for (int i=payloadStart;i<payloadStart+payloadLength;i++) {
returnValue+=packetData[i]+" ";
}
returnValue+="\n";
returnValue+="}\n";
return returnValue;
}
}