/* 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;
import java.io.ByteArrayOutputStream;
import java.io.ByteArrayInputStream;
import java.io.OutputStream;
import java.io.IOException;
import java.net.InetAddress;
import java.util.LinkedList;
import java.util.ListIterator;
import rtpi.packets.RtpiDataPacket;
/**
* This is the base class for RTPI ADUs.
* In order to put data into an ADU please use the getOutputStream
* method. After the data has been written from the returned output
* stream call the outputComplete method.
* In order to read data from an ADU please use the getInputStream
* method. The data can then be read from the input stream.
*
* @author Martin Mauve
*/
public abstract class RtpiData {
// RTPI information:
int type=RtpiDataPacket.ILLEGAL_TYPE;
int payloadType=RtpiDataPacket.ILLEGAL_PAYLOAD_TYPE;
int priority=0;
byte profileInformation=0x0;
int participantID=0;
long subcomponentID=0;
int sequenceNumber=0;
long timestamp=0;
// Constants
static final int MAX_SEQU_NO = 0xFFFF;
static final long MAX_TIMESTAMP = 0x00000000FFFFFFFFl;
static final int MAX_PRIORITY = 3;
static final int MAX_TYPE = 31;
static final int MAX_PAYLOAD_TYPE = 0xFF;
byte[] payload;
int payloadStart;
int payloadLength;
ByteArrayOutputStream outStream=null;
ByteArrayInputStream inStream=null;
float redundancy=0;
int redundancyTransmissionInterval=0;
/**
* Create a new ADU for the transmission over the
* network.
*
* @param pID The ID of the local participant.
* @param subID The ID of the affected subcomponent.
* @param t The type of the ADU (e.g. EVENT, STATE, etc.).
* @param seqNo The sequence number of the ADU.
* @param pType The payload type of the ADU.
* @param pri The priority of the ADU.
* @param ts The timestamp of the ADU. The value will be
* automatically clipped to a 32 bit value;
* @param pls The amount of bytes required for the combined RTP/I
* packet header (rtpi.packets.RtpiDataPacket.HEADER_SIZE)
* and the reliability header (depending on the reliability
* mechanism) used. If this value is not caluclated correctly
* then the packet will have to be copied one additional time
* before it can be sent. This decreases efficiency but should
* still work. There is a utility function Rtpi.getCombinedHeaderSize
* which returns this value.
*/
RtpiData(int pID,
long subID,
int t,
int seqNo,
int pType,
int pri,
long ts,
int pls
) throws IllegalValueException {
participantID=pID;
subcomponentID=subID;
if (t<0 || t> MAX_TYPE) {
throw new IllegalValueException("RtpiData: type not in valid range!");
}
type=t;
if (seqNo < 0 || seqNo> MAX_SEQU_NO) {
throw new IllegalValueException("RtpiData: sequence number not in valid range!");
}
sequenceNumber=seqNo;
if (pType < 0 || pType> MAX_PAYLOAD_TYPE) {
throw new IllegalValueException("RtpiData: payload type not in valid range!");
}
payloadType=pType;
if (pri < 0 || pri > MAX_PRIORITY) {
throw new IllegalValueException("RtpiData: priority not in valid range!");
}
priority=pri;
timestamp=ts&MAX_TIMESTAMP;
payloadStart=pls;
}
/**
* Create a new ADU from a single received packet.
*/
RtpiData(RtpiDataPacket packet) {
extractInfoFromPacket(packet);
payload=packet.getPacketData();
payloadStart=packet.getPayloadStart();
payloadLength=packet.getPayloadLength();
}
/**
* Create a new ADU from a list of received packets.
*/
RtpiData(LinkedList list) {
ListIterator it = list.listIterator();
int totalLength=0;
while(it.hasNext()) {
totalLength += ((RtpiDataPacket) it.next()).getPayloadLength();
}
payload = new byte[totalLength];
it = list.listIterator();
RtpiDataPacket packet=(RtpiDataPacket)it.next();
extractInfoFromPacket(packet);
byte[] tmpData = packet.getPacketData();
int curSize = packet.getPayloadLength();
System.arraycopy(tmpData,packet.getPayloadStart(), payload, 0, curSize);
int position=curSize;
while(it.hasNext()) {
packet=(RtpiDataPacket)it.next();
tmpData = packet.getPacketData();
curSize = packet.getPayloadLength();
System.arraycopy(tmpData, packet.getPayloadStart(), payload, position, curSize);
position += curSize;
}
payloadLength=position;
payloadStart=0;
}
void extractInfoFromPacket(RtpiDataPacket packet) {
type=packet.getType();
payloadType=packet.getPayloadType();
priority=packet.getPriority();
profileInformation=packet.getProfileInformation();
participantID=packet.getParticipantID();
subcomponentID=packet.getSubcomponentID();
sequenceNumber=packet.getSequenceNumber();
timestamp=packet.getTimestamp();
}
/**
* This sets the amount of redundancy for the transmission
* of the ADU. The reliability service used must support
* this amount of reliability.
*
* @param r The amount of redundancy.
*/
public void setRedundancy(float r) {
redundancy=r;
}
float getRedundancy() {
return redundancy;
}
/**
* This sets the interval during which redundancy information
* may be transmitted for this ADU.
*/
public void setRedundancyTransmissionInterval(int t) {
redundancyTransmissionInterval=t;
}
int getRedundancyTransmissionInterval() {
return redundancyTransmissionInterval;
}
/**
* This returns the payload type of the ADU.
*
* @return The payload type.
*/
public int getPayloadType() {
return payloadType;
}
/**
* This returns the priority of the ADU.
*
* @return The priority.
*/
public int getPriority() {
return priority;
}
/**
* This returns the profile specific information
* contained ADU.
*
* @return The profile information.
*/
public byte getProfileInformation() {
return profileInformation;
}
/**
* This sets the profile specific information for
* the ADU.
*
* @param pi The profile specific information.
*/
public void setProfileInformation(byte pi) {
profileInformation=pi;
}
/**
* This returns the participant ID of this ADU's
* sender.
*
* @return The sender's ID.
*/
public int getParticipantID() {
return participantID;
}
/**
* This returns the ID of the affected subcomponent.
*
* @return The subcomponent ID.
*/
public long getSubcomponentID() {
return subcomponentID;
}
/**
* This gets the sequence number of the ADU.
*
* @return This ADU's sequence number.
*/
public int getSequenceNumber() {
return sequenceNumber;
}
/**
* This returns the timestamp of the ADU.
*
* @return The timestamp.
*/
public long getTimestamp() {
return timestamp;
}
/**
* This returns an output stream to this RtpiData object. This stream should be used
* to put the data in the ADU. After finishing writing to the RtpiData object,
* outputComplete MUST be called on this object.
*
* @return The OutputStream.
*/
public OutputStream getOutputStream() throws IOException {
outStream = new ByteArrayOutputStream();
byte[] dummyHeader = new byte[payloadStart];
outStream.write(dummyHeader,0,dummyHeader.length);
return outStream;
}
/**
* This MUST be called when the output to the RtpiData object is finished.
*/
public void outputComplete() {
try {
outStream.flush();
payload = outStream.toByteArray();
outStream.close();
} catch (IOException exception) {
System.err.println("RTPIMessage: "+exception);
}
payloadLength=payload.length-payloadStart;
}
/**
* This returns an input stream to this RtpiData object. This stream should be used
* to read data from a received ADU.
*
* @return The InputStream.
*/
public ByteArrayInputStream getInputStream() {
ByteArrayInputStream bin = new ByteArrayInputStream(payload,payloadStart, payloadLength);
return bin;
}
LinkedList packetize(int transportPayloadSize) {
LinkedList packets=new LinkedList();
int bytesWritten=0;
RtpiDataPacket packet=null;
if (payloadLength+payloadStart<transportPayloadSize) {
packet=new RtpiDataPacket(payload, payloadStart, payloadLength);
setPacketFields(packet, 0, 1);
packets.add(packet);
return packets;
}
int bytesInThisPacket=0;
byte[] packetData=null;
int fragmentCount=0;
while (bytesWritten<payloadLength) {
if (payloadLength-bytesWritten+payloadStart<transportPayloadSize) {
bytesInThisPacket=payloadLength-bytesWritten+payloadStart;
} else {
bytesInThisPacket=transportPayloadSize;
}
packetData=new byte[bytesInThisPacket];
System.arraycopy(payload, bytesWritten+payloadStart, packetData, payloadStart, bytesInThisPacket-payloadStart);
packet = new RtpiDataPacket(packetData,payloadStart, bytesInThisPacket-payloadStart);
setPacketFields(packet, fragmentCount, 0);
fragmentCount++;
packets.add(packet);
bytesWritten+=bytesInThisPacket-payloadStart;
}
packet.setEnd(1);
return packets;
}
void setPacketFields(RtpiDataPacket packet, int fragmentCount, int end) {
packet.setFragmentCount(fragmentCount);
packet.setEnd(end);
packet.setType(type);
packet.setPayloadType(payloadType);
packet.setPriority(priority);
packet.setProfileInformation(profileInformation);
packet.setParticipantID(participantID);
packet.setSubcomponentID(subcomponentID);
packet.setSequenceNumber(sequenceNumber);
packet.setTimestamp(timestamp);
}
}