/* 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 java.io.ByteArrayInputStream;
import java.io.DataInputStream;
import java.io.ByteArrayOutputStream;
import java.io.DataOutputStream;
import java.util.LinkedList;
import java.util.ListIterator;
import rtpi.IllegalValueException;
/**
* This implements the RTCP/I source description packet.
*/
public class RtcpiSourceDescriptionPacket extends RtcpiPacket {
/**
* The payload type for source description packets.
*/
public static final int SDES = 72;
private LinkedList items=null;
/**
* This creates a new source description packet that is to be transmitted over
* the network.
*
* @param pid The id of the participant transmitting the packet.
* @param sdi The list of source description items. All objects in this list
* must be of the type SourceDescriptionItems (package rtpi.packets).
* The first item MUST be a CNAME item.
*/
public RtcpiSourceDescriptionPacket(int pid, LinkedList sdi) throws IllegalValueException {
super(pid);
payloadType=SDES;
setSourceDescriptionItems(sdi);
}
/**
* This creates an RTCP/I source description packet from a transport packet
* that has been received.
*
* @param d The transport packet.
* @param s The start of the RTCP/I packet in the transport packet
* (position of the first byte of the RTCP/I header.
*/
public RtcpiSourceDescriptionPacket(byte[] d, int s) {
super(d, s);
}
/**
* This flushes an outgoing packet.
*/
public void flush() throws RtpiFlushException {
if (items==null || items.size()==0) {
throw new RtpiFlushException("RtcpiSourceDescriptionPacket: Can't flush report with no entires");
}
ByteArrayOutputStream bos = new ByteArrayOutputStream();
DataOutputStream dos = new DataOutputStream(bos);
flushHeader(dos);
SourceDescriptionItem item;
int writtenBytes=0;
int itemLength=0;
for(ListIterator iter=items.listIterator();iter.hasNext();) {
item = (SourceDescriptionItem) iter.next();
int type = item.getType();
byte[] data = item.getData();
try {
dos.writeByte((byte) type);
} catch (Exception ex) {
throw new RtpiFlushException("RtcpiSourceDescriptionPacket: Couldn't write SDES type");
}
if (item.getType()==SourceDescriptionItem.PRIV) {
try {
byte[] prefix=item.getPrefix();
itemLength=prefix.length+data.length+1;
dos.writeByte((byte) itemLength);
dos.writeByte((byte) prefix.length);
dos.write(prefix);
} catch (Exception ex) {
throw new RtpiFlushException("RtcpiSourceDescriptionPacket: Couldn't write prefix to SDES item");
}
} else {
try {
itemLength=data.length;
dos.writeByte((byte) itemLength);
} catch (Exception ex) {
throw new RtpiFlushException("RtcpiSourceDescriptionPacket: Couldn't write length of SDES item");
}
}
try {
dos.write(data);
} catch (Exception ex) {
throw new RtpiFlushException("RtcpiSourceDescriptionPacket: Couldn't write SDES data");
}
writtenBytes+=itemLength+2;
}
try {
for(int c = 0; c < (4-writtenBytes%4)%4; c++) {
dos.writeByte((byte)0); // padding bytes
}
} catch (Exception ex) {
throw new RtpiFlushException("RtcpiSourceDescriptionPacket: Couldn't write padding bytes");
}
try {
dos.flush();
bos.flush();
} catch (Exception ex) {
throw new RtpiFlushException("RtcpiSourceDescriptionPacket: Couldn't flush output streams");
}
packetData = bos.toByteArray();
if (packetData.length!=(length+2)*4) {
throw new RtpiFlushException("RtcpiSourceDescriptionPacket: Illegal packet length after flushing packet");
}
try {
dos.close();
bos.close();
} catch (Exception ex) {
throw new RtpiFlushException("RtcpiSourceDescriptionPacket: Couldn't close output streams");
}
}
/**
* This parses an incoming packet.
*/
public void parse() throws RtpiParseException {
ByteArrayInputStream bis = new ByteArrayInputStream(packetData, packetStart, packetData.length-packetStart);
DataInputStream dis = new DataInputStream(bis);
parseHeader(dis);
if (payloadType!=SDES) {
throw new RtpiParseException("RtcpiSourceDescriptionPacket: Wrong payload type "+payloadType+" should be "+SDES);
}
int itemLength;
byte[] data;
int prefixLength;
byte[] prefix;
int type;
int readBytes;
items=new LinkedList();
SourceDescriptionItem item;
for(int i=0;i<count;i++) {
try {
type=dis.readUnsignedByte();
} catch (Exception ex) {
throw new RtpiParseException("RtcpiSourceDescriptionPacket: Couldn't read SDES type");
}
if (type<1 || type>8) {
throw new RtpiParseException("RtcpiSourceDescriptionPacket: received illegal SDES item type");
}
try {
itemLength=dis.readUnsignedByte();
} catch (Exception ex) {
throw new RtpiParseException("RtcpiSourceDescriptionPacket: Couldn't read SDES length");
}
if (type==SourceDescriptionItem.PRIV) {
try {
prefixLength=dis.readUnsignedByte();
itemLength=itemLength-prefixLength-1;
prefix = new byte[prefixLength];
data = new byte[itemLength];
dis.readFully(prefix);
dis.readFully(data);
item=new SourceDescriptionItem(data,prefix);
items.add(item);
} catch (Exception ex) {
throw new RtpiParseException("RtcpiSourceDescriptionPacket: Couldn't read SDES entry "+ex);
}
} else {
try {
data = new byte[itemLength];
dis.readFully(data);
item=new SourceDescriptionItem(type,data);
items.add(item);
} catch (Exception ex) {
throw new RtpiParseException("RtcpiSourceDescriptionPacket: Couldn't read SDES entry "+ex);
}
}
}
try {
dis.close();
bis.close();
} catch (Exception exe) {
throw new RtpiParseException("RtcpiSourceDescriptionPacket: Could not close input stream for packet");
}
}
private void setSourceDescriptionItems(LinkedList its) throws IllegalValueException {
items=its;
if (items==null || items.size()==0 || items.size()>31) {
throw new IllegalValueException("RtcpiSourceDescriptionPacket: Can't set empty entries list");
}
count = items.size();
SourceDescriptionItem item = (SourceDescriptionItem) items.getFirst();
if (item.getType()!=SourceDescriptionItem.CNAME) {
throw new IllegalValueException("RtcpiSourceDescriptionPacket: First element in entries list is not a CNAME entry");
}
length = 0; // first calculate them on a byte basis, then devide it by 4!
for(ListIterator iter=items.listIterator();iter.hasNext();) {
item = (SourceDescriptionItem) iter.next();
if (item.getType()==SourceDescriptionItem.PRIV) {
length+=item.getPrefix().length+item.getData().length+3;
} else {
length+=item.getData().length+2;
}
}
length += (4-length%4)%4; // with padding
length /= 4;
}
/**
* This returns the list of SourceDescriptionItems that are contained in
* this packet.
*
* @return The list of SDES items.
*/
public LinkedList getSourceDescriptionItems() {
return items;
}
/**
* Convert this object to a string.
*
* @return This object as a string.
*/
public String toString() {
String returnValue;
returnValue ="RtcpiSourceDescriptionPacket: {\n";
returnValue+=toStringDetails();
returnValue+="}\n";
return returnValue;
}
String toStringDetails() {
String returnValue;
returnValue=super.toStringDetails();
SourceDescriptionItem item = null;
for(ListIterator iter=items.listIterator();iter.hasNext();) {
item = (SourceDescriptionItem) iter.next();
returnValue+=item;
}
return returnValue;
}
}