/* 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 subcomponent report packet.
*/
public class RtcpiSubcomponentReportPacket extends RtcpiPacket {
/**
* The payload type for subcomponent report packets.
*/
public static final int SUBREP = 70;
private LinkedList subcomponents=null;
private static int ACTIVE_BITMASK=0x00000001;
/**
* This creates a new subcomponent report packet that is to be transmitted over
* the network.
*
* @param pid The id of the participant transmitting the packet.
* @param applicationLevelNames This must be set to 1 if application level names are used, otherwhise
* it must be set to 0 if no application level names are used
* @param subs The list of subcomponent report items. All objects in this list
* must be of the type SubcomponentReportInfo (package rtpi.packets). If
* applicationLevelNames is 1 then application level names must be present
* for each subcomponent. If applicationLevelNames is 0 then no application
* level names may be given.
*/
public RtcpiSubcomponentReportPacket(int pid, int applicationLevelNames, LinkedList subs) throws IllegalValueException {
super(pid);
payloadType=SUBREP;
setSubcomponentReportInfo(applicationLevelNames, subs);
}
/**
* This creates an RTCP/I subcomponent report 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 RtcpiSubcomponentReportPacket(byte[] d, int l) {
super(d, l);
}
/**
* This flushes an outgoing packet.
*/
public void flush() throws RtpiFlushException {
if (subcomponents==null || subcomponents.size()==0) {
throw new RtpiFlushException("RtcpioSubcomponentReportPacket: Can't flush report with no entires");
}
ByteArrayOutputStream bos = new ByteArrayOutputStream();
DataOutputStream dos = new DataOutputStream(bos);
flushHeader(dos);
int activeBits=0;
SubcomponentReportInfo info = null;
int i=0;
for(ListIterator iter=subcomponents.listIterator();iter.hasNext();) {
info = (SubcomponentReportInfo) iter.next();
i++;
if (info.getActive()==1) {
activeBits |= 1 ;
}
if (i!=32) {
activeBits <<= 1;
}
}
if (i<31) {
activeBits <<= (31-i);
}
try {
dos.writeInt(activeBits);
} catch (Exception ex) {
throw new RtpiFlushException("RtcpiSubcomponentReportPacket: Couldn't write active list");
}
for(ListIterator iter=subcomponents.listIterator();iter.hasNext();) {
info = (SubcomponentReportInfo) iter.next();
try {
dos.writeLong(info.getSubcomponentID());
} catch (Exception ex) {
throw new RtpiFlushException("RtcpiSubcomponentReportPacket: Couldn't write subcomponent ID");
}
if (reserved==1) {
try {
byte[] name = info.getApplicationLevelName();
dos.writeByte((byte) name.length); // the length in bytes
dos.write(name); // the name
for(int c = 0; c < (4-(name.length+1)%4)%4; c++) {
dos.writeByte((byte)0); // padding bytes
}
} catch (Exception ex) {
throw new RtpiFlushException("RtcpiSubcomponentReportPacket: Couldn't write application level name");
}
}
}
try {
dos.flush();
bos.flush();
} catch (Exception ex) {
throw new RtpiFlushException("RtcpiSubcomponentReportPacket: Couldn't flush output streams");
}
packetData = bos.toByteArray();
if (packetData.length!=(length+2)*4) {
throw new RtpiFlushException("RtcpiSubcomponentReportPacket: Illegal packet length after flushing packet");
}
try {
dos.close();
bos.close();
} catch (Exception ex) {
throw new RtpiFlushException("RtCpiSubcomponentReportPacket: 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!=SUBREP) {
throw new RtpiParseException("RtCpiSubcomponentReportPacket: Wrong payload type "+payloadType+" should be "+SUBREP);
}
int activeBits=0;
try {
activeBits=dis.readInt();
} catch (Exception ex) {
throw new RtpiParseException("RtcpiSubcomponentReportPacket: Couldn't read active list");
}
int active=0;
long sID=0;
int nameLength=0;
byte[] name=null;
SubcomponentReportInfo info=null;
subcomponents=new LinkedList();
for(int i=0;i<count;i++) {
try {
sID=dis.readLong();
} catch (Exception ex) {
throw new RtpiParseException("RtcpiSubcomponentReportPacket: Couldn't read subcomponent ID");
}
if (reserved==1) {
try {
nameLength=dis.readUnsignedByte();
} catch (Exception ex) {
throw new RtpiParseException("RtcpiSubcomponentReportPacket: Couldn't read subcomponent ID");
}
name=new byte[nameLength];
try {
dis.readFully(name);
} catch (Exception ex) {
throw new RtpiParseException("RtcpiSubcomponentReportPacket: Couldn't read subcomponent name");
}
for(int c = 0; c < (4-(nameLength+1)%4)%4; c++) {
try {
dis.readByte(); // padding bytes
} catch (Exception ex) {
throw new RtpiParseException("RtcpiSubcomponentReportPacket: Couldn't read padding bytes");
}
}
}
active=(activeBits>>>(31-i))&ACTIVE_BITMASK;
info=new SubcomponentReportInfo(sID,active,name);
subcomponents.add(info);
}
try {
dis.close();
bis.close();
} catch (Exception exe) {
throw new RtpiParseException("RtCpiSubcomponentReportPacket: Could not close input stream for packet");
}
}
void setSubcomponentReportInfo(int applicationLevelNames, LinkedList subs) throws IllegalValueException {
subcomponents=subs;
if (subcomponents==null || subcomponents.size()==0 || subcomponents.size()>31) {
throw new IllegalValueException("RtcpiSubcomponentReportPacket: Can't set empty entries list");
}
reserved=applicationLevelNames;
count = subcomponents.size();
length = 1+subcomponents.size()*2; // 1 is for active list, header does not count towards length!
if (reserved!=1) { // no application level names
return;
}
SubcomponentReportInfo info = null;
for(ListIterator iter=subcomponents.listIterator();iter.hasNext();) {
info = (SubcomponentReportInfo) iter.next();
byte[] name=info.getApplicationLevelName();
if (name==null || name.length>255)
throw new IllegalValueException("RtcpiSubcomponentReportPacket: no application level name present");
int entryLength=name.length + 1; // without padding
length += (entryLength+(4-entryLength%4)%4)/4; // with padding
}
}
/**
* This returns the list of SubcomponentReportInfo items that are contained in
* this packet.
*
* @return The list of items.
*/
public LinkedList getSubcomponentReportInfo() {
return subcomponents;
}
/**
* This returns the application level names flag.
*
* @return The value of the application level names flag.
*/
public int getApplicationLevelNames() {
return reserved;
}
/**
* Convert this object to a string.
*
* @return This object as a string.
*/
public String toString() {
String returnValue;
returnValue ="RtcpiSubcomponentReportPacket: {\n";
returnValue+=toStringDetails();
returnValue+="}\n";
return returnValue;
}
String toStringDetails() {
String returnValue;
returnValue=super.toStringDetails();
SubcomponentReportInfo info = null;
for(ListIterator iter=subcomponents.listIterator();iter.hasNext();) {
info = (SubcomponentReportInfo) iter.next();
returnValue+=info;
}
return returnValue;
}
}