/* 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 rtpi.transport.Transport;
import rtpi.transport.TransportRecipient;
import rtpi.transport.TransportPacket;
import rtpi.reliability.Reliable;
import rtpi.reliability.ReliableRecipient;
import rtpi.packets.RtpiDataPacket;
import rtpi.packets.RtcpiPacket;
import rtpi.packets.RtcpiSourceDescriptionPacket;
import rtpi.packets.RtcpiByePacket;
import rtpi.packets.RtcpiSubcomponentReportPacket;
import rtpi.packets.SubcomponentReportInfo;
import rtpi.packets.SourceDescriptionItem;
import rtpi.util.SyncQueue;
import rtpi.util.Sleeper;
import rtpi.util.WakeUpThread;
import java.util.LinkedList;
import java.util.ListIterator;
import java.util.Hashtable;
import java.util.Random;
import java.util.Enumeration;
/**
* This class represents the main API to the RTP/I library. It also provides
* the functionality to fragment oversized ADUs and to handle RTCP/I.
* @author Martin Mauve
*/
public final class Rtpi extends Thread implements ReliableRecipient, Sleeper, TransportRecipient {
// general information
private SyncQueue messages = new SyncQueue();
private RtpiRecipient recipient=null;
// RTP/I information
private Reliable reliabilityService=null;
private int transportPayloadSize=0;
// General RTCP/I information
private boolean applicationLevelNames=false;
private Hashtable localActiveSubcomponents = new Hashtable();
private Hashtable localPassiveSubcomponents = new Hashtable();
private Hashtable remoteActiveSubcomponents = new Hashtable();
private Hashtable remotePassiveSubcomponents = new Hashtable();
private Transport rtcpiTransport;
private RtpiSourceInfo localParticipant;
private Hashtable remoteParticipants=new Hashtable();
private Hashtable deletedParticipants=new Hashtable();
// RTCP/I information for the calculation of the transmission interval
private int timeoutIntervalsSource = 5;
private int timeoutIntervalsSubcomponent = 3;
private int timeoutByeIntervals = 3;
private int minimalTransmissionInterval=5000;
private int currentDeterministicTransmissionInterval=2500;
private long tp;
private long tn;
private float pmembers;
private float members;
private int bw;
private float avg_rtcpi_size;
private boolean initial;
private boolean groupJoined;
private boolean byeSent;
private boolean sentSomething;
private Random randomGenerator = new Random();
private WakeUpThread wakeUpThread;
// RTCP/I information for the SDES reporting;
private static final int MAX_PARTICIPANTS_FOR_IMMEDIATE_BYE = 50;
private static final int NOTE_FINISHED_COUNT = 3;
private static final float OLD_FACTOR = 0.9375f;
private static final float NEW_FACTOR = 0.0625f;
private boolean sendName=true;
private boolean sendNote=true;
private int noteFinishedCount=0;
private int othersCount=0;
private Rtpi right = null;
private Rtpi left = null;
/**
* Construct the Rtpi object.
*
* @param reliabilityService The reliability service that is used to transmit the data part of RTP/I.
* The Rtpi instance will automatically subscribe as a recipient to the
* service. The application is responsible for setting the QoS parameters
* of the reliability service. Rtpi will forward any messages of lost ADUs
* to the application.
* @param localParticipant The description of the local participant. This information is used by RTCP/I
* to construct SDES packets.
* @param rtcpiTransport The transport instance over which RTCP/I packets are to be transmitted.
* Rtpi will automatically subscribe as a recipient to this Transport
* instance.
* @param maxControlBandwidth The maximum amount of bandwidth available for RTCP/I packets for the
* whole session (all participants). This value MUST be the same for
* all participants in a session. The value is given in bytes per second.
* @param useApplicationLevelNames If set to true, application level names are used. If set to false application level
* names will not be used. This value MUST be the same for all participants of a session.
*/
public Rtpi(Reliable reliabilityService,
RtpiSourceInfo localParticipant,
Transport rtcpiTransport,
int maxControlBandwidth,
boolean useApplicationLevelNames) {
groupJoined=false;
this.reliabilityService=reliabilityService;
this.reliabilityService.registerRecipient(this);
this.transportPayloadSize=this.reliabilityService.getTransportPayloadSize();
this.localParticipant=localParticipant;
this.rtcpiTransport=rtcpiTransport;
this.rtcpiTransport.registerTransportRecipient(this);
this.bw=maxControlBandwidth;
this.applicationLevelNames=useApplicationLevelNames;
start();
}
public Rtpi(Reliable reliabilityService,
RtpiSourceInfo localParticipant,
Transport rtcpiTransport,
int maxControlBandwidth,
boolean useApplicationLevelNames, Rtpi right, Rtpi left) {
groupJoined=false;
this.reliabilityService=reliabilityService;
this.reliabilityService.registerRecipient(this);
this.transportPayloadSize=this.reliabilityService.getTransportPayloadSize();
this.localParticipant=localParticipant;
this.rtcpiTransport=rtcpiTransport;
this.rtcpiTransport.registerTransportRecipient(this);
this.bw=maxControlBandwidth;
this.applicationLevelNames=useApplicationLevelNames;
this.right = right;
this.left = left;
start();
}
/**
* This returns the size of the combined RTP/I and
* reliability headers.
*
* @return The header size.
*/
public int getCombinedHeaderSize() {
return reliabilityService.getCombinedHeaderSize();
}
public void run() {
Message message=null;
while (true) {
try {
message=(Message) messages.get();
} catch (Exception exe) {
System.err.println("RTPI : could not get next message!");
continue;
}
//System.out.println("Got message " + message.opCode);
switch(message.opCode) {
case Message.QUIT:
execQuit(); // clean up!
return; // return to leave the run method!
case Message.JOIN_GROUP:
execJoinGroup();
break;
case Message.LEAVE_GROUP:
execLeaveGroup();
break;
case Message.SET_RTPI_RECIPIENT:
execSetRtpiRecipient((RtpiRecipient) message.args);
break;
case Message.ADD_SUBCOMPONENT:
AddSubcomponentArgs args0 = (AddSubcomponentArgs) message.args;
execAddSubcomponent(args0.subcomponentID, args0.appName, args0.active, args0.remote);
break;
case Message.REMOVE_SUBCOMPONENT:
execRemoveSubcomponent(((Long) message.args).longValue());
break;
case Message.ACTIVATE_SUBCOMPONENT:
execActivateSubcomponent(((Long) message.args).longValue());
break;
case Message.DEACTIVATE_SUBCOMPONENT:
execDeactivateSubcomponent(((Long) message.args).longValue());
break;
case Message.TRANSMIT_STATE:
execTransmitState((RtpiState) message.args);
break;
case Message.TRANSMIT_DELTA_STATE:
execTransmitDeltaState((RtpiDeltaState) message.args);
break;
case Message.TRANSMIT_EVENT:
execTransmitEvent((RtpiEvent) message.args);
break;
case Message.TRANSMIT_STATE_QUERY:
execTransmitStateQuery((RtpiStateQuery) message.args);
break;
case Message.RECEIVE_RTPI_ADU:
execReceiveRtpiAdu((LinkedList) message.args);
break;
case Message.RTPI_ADU_LOST:
RtpiAduLostArgs args2 = (RtpiAduLostArgs) message.args;
execRtpiAduLost(args2.participantID, args2.subID, args2.type, args2.sequenceNumber, args2.timestamp);
break;
case Message.RTPI_COULD_NOT_RECOVER:
RtpiCouldNotRecoverArgs args3 = (RtpiCouldNotRecoverArgs) message.args;
execRtpiCouldNotRecover(args3.participantID, args3.subID, args3.type, args3.sequenceNumber, args3.timestamp);
break;
case Message.CONNECTION_CLOSED:
execConnectionClosed();
break;
case Message.WAKE_UP:
execWakeUp();
break;
case Message.RECEIVE_TRANSPORT_PACKET:
execReceiveTransportPacket((TransportPacket) message.args);
break;
default:
System.err.println("Rtpi: illegal message opcode: "+message.opCode+message.args);
break;
}
}
}
/**
* This terminates Rtpi. No RTCP/I BYE will be sent.
*/
public void quit(){
messages.put(new Message(Message.QUIT, null));
}
private void execQuit() {
reliabilityService.quit();
rtcpiTransport.quit();
if (wakeUpThread!=null) {
wakeUpThread.interrupt();
}
return;
}
/**
* This sets the recipient of incoming ADUs and notifications.
*
* @param r The recipient.
*/
public void setRtpiRecipient(RtpiRecipient r) {
messages.put(new Message(Message.SET_RTPI_RECIPIENT, r));
}
private void execSetRtpiRecipient(RtpiRecipient r) {
recipient = r;
}
/**
* This joins both, the RTP/I and the RTCP/I groups.
* If these groups were joined and left previously, then
* all remote participants as well as local and remote
* sub-components are cleared.
*/
public void joinGroup() {
//System.out.println("RTPI join group");
messages.put(new Message(Message.JOIN_GROUP,null));
}
private void execJoinGroup() {
//System.out.println("RTPI execJoinGroup");
remoteActiveSubcomponents.clear();
remotePassiveSubcomponents.clear();
remoteParticipants.clear();
deletedParticipants.clear();
localActiveSubcomponents.clear();
localPassiveSubcomponents.clear();
remoteActiveSubcomponents.clear();
remotePassiveSubcomponents.clear();
initRtcpiTransmissionInterval();
reliabilityService.joinGroup();
try {
rtcpiTransport.joinGroup();
} catch (Exception ex) {
System.err.println("Rtpi: could not join RTCP/I session "+ex);
}
sentSomething=false;
groupJoined=true;
}
private void initRtcpiTransmissionInterval() {
members=1;
pmembers=1;
initial=true;
tp=System.currentTimeMillis();
// Now calculate the size of the first RTCP/I packet that we are going to send:
// this packet will contain a single SDES packet with the CNAME and NAME items!
avg_rtcpi_size=RtcpiPacket.HEADER_SIZE;
SourceDescriptionItem item;
try {
item = localParticipant.getStandardItem(SourceDescriptionItem.CNAME);
if (item!= null) {
avg_rtcpi_size+=item.getData().length+1; // +1 for length field!
}
} catch (Exception ex) {
System.err.println("Rtpi: could not calculate length of CNAME SDES item "+ex);
}
try {
item = localParticipant.getStandardItem(SourceDescriptionItem.NAME);
if (item!= null) {
avg_rtcpi_size+=item.getData().length+1; // +1 for length field!
}
} catch (Exception ex) {
System.err.println("Rtpi: could not calculate length of NAME SDES item "+ex);
}
avg_rtcpi_size+=(4-avg_rtcpi_size%4)%4; // add padding!
avg_rtcpi_size+=rtcpiTransport.getHeaderSize(); // add transport and network header size!
tn=calculateTransmissionInterval()+tp;
if (wakeUpThread!=null && wakeUpThread.isAlive()) {
wakeUpThread.interrupt();
}
if (right == null)
wakeUpThread = new WakeUpThread(tn-System.currentTimeMillis(),this);
}
private int calculateTransmissionInterval() {
int min=minimalTransmissionInterval;
int transmissionInterval;
if (initial) {
min/=2;
}
transmissionInterval=(int) (1000*members*avg_rtcpi_size/bw); // *1000 because the timer is set in ms, not in seconds
if (transmissionInterval<min)
transmissionInterval=min;
currentDeterministicTransmissionInterval=transmissionInterval; // remember the deterministic transmission interval
transmissionInterval*=(randomGenerator.nextFloat()+0.5);
transmissionInterval*=1.21828;
return transmissionInterval;
}
/**
* Leave both the RTP/I and RTCP/I groups.
* After leaving it may be joined later on. However,
* this will delete all information about sub-components
* (remote and local) as well as about all remote
* participants.
*/
public void leaveGroup() {
messages.put(new Message(Message.LEAVE_GROUP,null));
}
private void execLeaveGroup() {
if (!groupJoined) {
System.err.println("Rtpi: cannot leave group before it has been joined!");
return;
}
groupJoined=false;
reliabilityService.leaveGroup();
if (sentSomething==false) {
if (wakeUpThread!=null && wakeUpThread.isAlive()) {
wakeUpThread.interrupt();
}
byeSent=true;
rtcpiTransport.leaveGroup();
return;
}
if (members<MAX_PARTICIPANTS_FOR_IMMEDIATE_BYE) {
try {
RtcpiSourceDescriptionPacket spacket=constructSdesPacket();
spacket.flush();
byte[] spacketData = spacket.getPacket();
RtcpiByePacket bpacket=new RtcpiByePacket(localParticipant.getParticipantID());
bpacket.flush();
byte[] bpacketData = bpacket.getPacket();
byte [] packetData = new byte[spacketData.length+bpacketData.length];
System.arraycopy(spacketData, 0, packetData, 0, spacketData.length);
System.arraycopy(bpacketData, 0, packetData, spacketData.length, bpacketData.length);
TransportPacket tpacket = new TransportPacket(packetData.length,packetData);
rtcpiTransport.sendTransportPacket(tpacket);
byeSent=true;
} catch (Exception ex) {
System.err.println("Rtpi: could not send BYE packet when leaving the group, error was: "+ex);
}
rtcpiTransport.leaveGroup();
} else {
byeSent=false;
initRtcpiByeTransmissionInterval();
}
}
private void initRtcpiByeTransmissionInterval() {
members=1;
pmembers=1;
initial=true;
tp=System.currentTimeMillis();
// a bye packet without reason field:
avg_rtcpi_size=RtcpiPacket.HEADER_SIZE+rtcpiTransport.getHeaderSize(); // add transport and network header size!
tn=calculateTransmissionInterval()+tp;
if (wakeUpThread!=null && wakeUpThread.isAlive()) {
wakeUpThread.interrupt();
}
if (right == null)
wakeUpThread = new WakeUpThread(tn-System.currentTimeMillis(),this);
}
/**
* This should be called by the application to indicate that it is now interested in
* a certain subcomponent (e.g. keeps track of its state).
* The name must be non-null if the Rtpi instance was created with useApplicationLevelNames
* set to true. It must be null if the Rtpi instance was created with
* useApplicationLevelNames set to false.
*
* @param subID The (unique) ID of this sub-component.
* @param appName The application-level name of this sub-component.
* @param active Indicates whether this sub-component is active or not (i.e. used to display the medium to the user)
*/
public void addSubcomponent(long subID, byte[] appName, boolean active, boolean remote) throws IllegalValueException {
if (applicationLevelNames==true && appName==null) {
throw new IllegalValueException("Rtpi: application level name missing (applicationLevelNames was set to true");
}
if (applicationLevelNames==false && appName!=null) {
throw new IllegalValueException("Rtpi: application level name used (useApplicationLevelNames was set to false");
}
//System.out.println("Putting ADD_SUB message with subID: " + subID + " active: " + active);
messages.put(new Message(Message.ADD_SUBCOMPONENT,
new AddSubcomponentArgs(subID,appName, active, remote)));
}
private void execAddSubcomponent(long subID, byte[] appName, boolean active, boolean remote) {
if (remote)
{
//System.out.println("in AddSub subID: " + subID);
if (remoteActiveSubcomponents.containsKey(new Long(subID))) {
System.err.println("Rtpi1: Added an already existing sub-component - ignored!");
return;
}
if (remotePassiveSubcomponents.containsKey(new Long(subID))) {
System.err.println("Rtpi2: aded an already existing sub-component - ignored!");
return;
}
SubcomponentInfo info = new SubcomponentInfo(appName);
if (active)
{
remoteActiveSubcomponents.put(new Long(subID), info);
//System.out.println("added subID " + subID + " to ras list");
} else {
remotePassiveSubcomponents.put(new Long(subID), info);
//System.out.println("added subID " + subID + " to rps list");
}
}
else
{
if (localActiveSubcomponents.containsKey(new Long(subID))) {
System.err.println("Rtpi3: Added an already existing sub-component - ignored!");
return;
}
if (localPassiveSubcomponents.containsKey(new Long(subID))) {
System.err.println("Rtpi4: Added an already existing sub-component - ignored!");
return;
}
SubcomponentInfo info = new SubcomponentInfo(appName);
if (active) {
localActiveSubcomponents.put(new Long(subID),info);
} else {
localPassiveSubcomponents.put(new Long(subID),info);
}
}
}
/**
* This should be called by the local application to indicate that it is no longer interested in
* a certain subcomponent.
*
* @param subID The (unique) ID of this sub-component.
*/
public void removeSubcomponent(long subID) {
messages.put(new Message(Message.REMOVE_SUBCOMPONENT, new Long(subID)));
}
private void execRemoveSubcomponent(long subID) {
if (!localActiveSubcomponents.containsKey(new Long(subID)) &&
!localPassiveSubcomponents.containsKey(new Long(subID))) {
System.err.println("Rtpi: Remove called for a non existing sub-component - ignored!");
return;
}
localActiveSubcomponents.remove(new Long(subID));
localPassiveSubcomponents.remove(new Long(subID));
remoteActiveSubcomponents.remove(new Long(subID));
remotePassiveSubcomponents.remove(new Long(subID));
}
/**
* This should be called by the local application on an existing (that is: previously
* registered) sub-component as soon as this sub-component is locally needed to display
* the interactive medium to the user.
*
* @param subID The (unique) ID of this sub-component.
*/
public void activateSubcomponent(long subID) {
messages.put(new Message(Message.ACTIVATE_SUBCOMPONENT,new Long(subID)));
}
private void execActivateSubcomponent(long subID) {
//System.out.println("subID for activateSub is: " + subID);
if (!localPassiveSubcomponents.containsKey(new Long(subID))) {
if (localActiveSubcomponents.containsKey(new Long(subID))) {
System.err.println("Rtpi: Activated an active sub-component - ignored!");
} else {
System.err.println("Rtpi: Activated an non existing sub-component - ignored!");
}
return;
}
localActiveSubcomponents.put(new Long(subID),localPassiveSubcomponents.remove(new Long(subID)));
}
/**
* This should be called by the local application on an existing (that is: previously
* registered) and activated sub-component as soon as this sub-component is no
* longer needed to display the interactive medium locally to the user.
*
* @param subID The (unique) ID of this sub-component.
*/
public void deactivateSubcomponent(long subID) {
messages.put(new Message(Message.DEACTIVATE_SUBCOMPONENT, new Long(subID)));
}
private void execDeactivateSubcomponent(long subID) {
if (!localActiveSubcomponents.containsKey(new Long(subID))) {
if (localPassiveSubcomponents.containsKey(new Long(subID))) {
System.err.println("Rtpi: deactivate a passive sub-component - ignored!");
} else {
System.err.println("Rtpi: deactive an non existing sub-component - ignored!");
}
return;
}
localPassiveSubcomponents.put(new Long(subID),localActiveSubcomponents.remove(new Long(subID)));
}
/**
* Transmit an RtpiState.
*
* @param state The RtpiState which should be transmitted.
*/
public void transmitState(RtpiState state) {
//System.out.println("transmit state");
messages.put(new Message(Message.TRANSMIT_STATE, state));
}
private void execTransmitState(RtpiState state) {
if (!groupJoined) {
System.err.println("Rtpi: cannot transmit state before the group has been joined - state transmission aborted!");
return;
}
if (!localActiveSubcomponents.containsKey(new Long(state.getSubcomponentID()))) {
System.err.println("Rtpi: cannot transmit state before activating the subcomponent - state transmission aborted!");
}
if (!(state.getParticipantID()==localParticipant.getParticipantID())) {
System.err.println("Rtpi: cannot transmit state, wrong participant ID - state transmission aborted!");
}
try {
reliabilityService.transmitRtpiAdu(state.packetize(transportPayloadSize),
state.getRedundancy(),
state.getRedundancyTransmissionInterval());
sentSomething=true;
} catch (Exception ex) {
System.err.println("Rtpi: could not transmit state "+ex);
}
}
public void sendState(RtpiState state) {
try {
reliabilityService.transmitRtpiAdu(state.packetize(transportPayloadSize), state.getRedundancy(), state.getRedundancyTransmissionInterval());;
} catch (Exception ex) {
System.err.println("Rtpi: could not relay state " + ex);
}
}
/*
public void sendRtpiAdu(LinkedList packet, float redundancy, int transmissionInterval) {
try {
reliabilityService.transmitRtpiAdu(packet, redundancy, transmissionInterval);
sentSomething = true;
} catch (Exception ex) {
System.err.println("Rtpi: could not relay state " + ex);
}
}
*/
/**
* Transmit an RtpiDeltaState.
*
* @param deltaState The RtpiDeltaState which should be transmitted.
*/
public void transmitDeltaState(RtpiDeltaState deltaState) {
messages.put(new Message(Message.TRANSMIT_DELTA_STATE, deltaState));
}
private void execTransmitDeltaState(RtpiDeltaState deltaState) {
if (!groupJoined) {
System.err.println("Rtpi: cannot transmit delta state before the group has been joined - delta state transmission aborted!");
return;
}
if (!localActiveSubcomponents.containsKey(new Long(deltaState.getSubcomponentID()))) {
System.err.println("Rtpi: cannot transmit delta state before activating the subcomponent - delta state transmission aborted!");
}
if (!(deltaState.getParticipantID()==localParticipant.getParticipantID())) {
System.err.println("Rtpi: cannot transmit delta state, wrong participant ID - delta state transmission aborted!");
}
try {
reliabilityService.transmitRtpiAdu(deltaState.packetize(transportPayloadSize),
deltaState.getRedundancy(),
deltaState.getRedundancyTransmissionInterval());
sentSomething=true;
} catch (Exception ex) {
System.err.println("Rtpi: could not transmit delta state "+ex);
}
}
/**
* Transmit an RtpiEvent.
*
* @param event The RtpiEvent which should be transmitted.
*/
public void transmitEvent(RtpiEvent event) {
messages.put(new Message(Message.TRANSMIT_EVENT, event));
}
private void execTransmitEvent(RtpiEvent event) {
if (!groupJoined) {
System.err.println("Rtpi: cannot transmit event before the group has been joined - event transmission aborted!");
return;
}
if (!localActiveSubcomponents.containsKey(new Long(event.getSubcomponentID()))) {
System.err.println("Rtpi: cannot transmit event before activating the subcomponent - event transmission aborted!");
}
if (!(event.getParticipantID()==localParticipant.getParticipantID())) {
System.err.println("Rtpi: cannot transmit event, wrong participant ID - event transmission aborted!");
}
try {
reliabilityService.transmitRtpiAdu(event.packetize(transportPayloadSize),
event.getRedundancy(),
event.getRedundancyTransmissionInterval());
sentSomething=true;
} catch (Exception ex) {
System.err.println("Rtpi: could not transmit state "+ex);
}
}
/**
* Transmit an RtpiStateQuery.
*
* @param query The RtpiStateQuery which should be transmitted.
*/
public void transmitStateQuery(RtpiStateQuery query) {
messages.put(new Message(Message.TRANSMIT_STATE_QUERY, query));
}
private void execTransmitStateQuery(RtpiStateQuery query) {
if (!groupJoined) {
System.err.println("Rtpi: cannot transmit state query before the group has been joined - state query transmission aborted!");
return;
}
if (!(query.getParticipantID()==localParticipant.getParticipantID())) {
System.err.println("Rtpi: cannot transmit state query, wrong participant ID - state query transmission aborted!");
}
try {
reliabilityService.transmitRtpiAdu(query.packetize(transportPayloadSize),
query.getRedundancy(),
query.getRedundancyTransmissionInterval());
sentSomething=true;
} catch (Exception ex) {
System.err.println("Rtpi: could not transmit state "+ex);
}
}
/**
* This is the implementation of receiveRtpiAdu from the ReliableRecipient interface.
* (package rtpi.reliability). It is called when an ADU is received by the reliability
* service.
*
* @param adu The RTP/I ADU which has been received.
*/
public void receiveRtpiAdu(LinkedList adu) {
//System.out.println("received adu");
messages.put(new Message(Message.RECEIVE_RTPI_ADU, adu));
}
private void execReceiveRtpiAdu(LinkedList adu) {
if (!groupJoined) {
System.err.println("Rtpi: discarding ADU that has been received after the group was left!");
return;
}
if (recipient == null)
{
//System.out.println("Recipient has not yet been specified, returning ...");
return;
}
RtpiDataPacket packet = (RtpiDataPacket) adu.getFirst();
//System.out.println("packet is: " + packet);
int thisSendersID = packet.getParticipantID();
//System.out.println("senders ID is: " + thisSendersID);
if (thisSendersID==localParticipant.getParticipantID()) {
// System.err.println("Rtpi: WARNING: received RtpiAdu with same PID as local user, this may be cause by a PID collision or a routing loop!");
return;
}
//System.out.println("remoteParticipants: " + remoteParticipants);
//System.out.println("deletedParticipants: " + deletedParticipants);
if (!remoteParticipants.containsKey(new Integer(thisSendersID)) && !deletedParticipants.containsKey(new Integer(thisSendersID))) {
//System.out.println("In if");
RtpiSourceInfo participant = new RtpiSourceInfo(thisSendersID);
remoteParticipants.put(new Integer(thisSendersID), participant);
members++;
//System.out.println("recipient is : " + recipient);
recipient.changeSourceInfo(this, participant);
}
if (!remoteParticipants.containsKey(new Integer(thisSendersID))) {
return;
}
//System.out.println("calling heard from for pid: " + thisSendersID);
((RtpiSourceInfo) remoteParticipants.get(new Integer(thisSendersID))).heardFrom();
switch (packet.getType()) {
case RtpiDataPacket.EVENT:
try {
//System.out.println("Got event packet");
recipient.receiveEvent(this, new RtpiEvent(adu));
} catch (Exception ex) {
System.err.println("Rtpi: Error while defragmenting Rtpi event "+ex);
}
break;
case RtpiDataPacket.STATE:
try {
//System.out.println("Got state packet");
if (right != null) {
right.sendState(new RtpiState(adu));
left.sendState(new RtpiState(adu));
}
else
recipient.receiveState(this, new RtpiState(adu));
} catch (Exception ex) {
System.err.println("Rtpi: Error while defragmenting Rtpi state "+ex);
}
break;
case RtpiDataPacket.DELTA_STATE:
try {
//System.out.println("Got delta state packet");
recipient.receiveDeltaState(this, new RtpiDeltaState(adu));
} catch (Exception ex) {
System.err.println("Rtpi: Error while defragmenting Rtpi delta state "+ex);
}
break;
case RtpiDataPacket.STATE_QUERY:
try {
//System.out.println("Got state query packet");
recipient.receiveStateQuery(this, new RtpiStateQuery(adu));
} catch (Exception ex) {
System.err.println("Rtpi: Error while defragmenting Rtpi State Query "+ex);
}
break;
default:
System.err.println("Rtpi: received unknown RTPI ADU type!");
}
}
/**
* This is the implementation of rtpiAduLost from the ReliableRecipient interface.
* (package rtpi.reliability). The call is directly forwarded to the recipient registered
* with this Rtpi object.
*
* @param participantID The ID of the sender of the lost ADU.
* @param subID The ID of the subcomponent the lost ADU refers to.
* @param type The type of the lost ADU.
* @param sequenceNumber the sequenceNumber of the lost ADU.
* @param timestamp the timestamp of the lost ADU.
*/
public void rtpiAduLost(int participantID, long subID, int type, int sequenceNumber, int timestamp) {
messages.put(new Message(Message.RTPI_ADU_LOST,
new RtpiAduLostArgs(participantID, subID,type,sequenceNumber,timestamp)));
}
private void execRtpiAduLost(int participantID, long subID, int type, int sequenceNumber, int timestamp) {
if (!groupJoined) {
System.err.println("Rtpi: discarding adu lost notification because the group is not joined!");
return;
}
recipient.rtpiAduLost(this, participantID, subID, type, sequenceNumber, timestamp);
}
/**
* This is the implementation of rtpiCouldNotRecover from the ReliableRecipient interface.
* (package rtpi.reliability). The call is directly forwarded to the recipient registered
* with this Rtpi object.
*
* @param participantID The ID of the sender of the lost ADU.
* @param subID The ID of the subcomponent the lost ADU refers to.
* @param type The type of the lost ADU.
* @param sequenceNumber the sequenceNumber of the lost ADU.
* @param timestamp the timestamp of the lost ADU.
*/
public void rtpiCouldNotRecover(int participantID, long subID, int type, int sequenceNumber, int timestamp) {
messages.put(new Message(Message.RTPI_COULD_NOT_RECOVER,
new RtpiCouldNotRecoverArgs(participantID, subID,type,sequenceNumber,timestamp)));
}
private void execRtpiCouldNotRecover(int participantID, long subID, int type, int sequenceNumber, int timestamp) {
if (!groupJoined) {
System.err.println("Rtpi: discarding could not recover notification because the group is not joined!");
return;
}
recipient.rtpiCouldNotRecover(this, participantID, subID, type, sequenceNumber, timestamp);
}
/**
* This is the implementation of the connectionClosed method from the ReliableRecipient
* (package rtpi.reliability) and the TransportRecipient (package rtpi.transport) interfaces.
* These messages are directly forwarded to the recipient registered with this Rtpi object.
*/
public void connectionClosed() {
messages.put(new Message(Message.CONNECTION_CLOSED, null));
}
private void execConnectionClosed() {
if (!groupJoined) {
System.err.println("Rtpi: discarding connection lost notification because the group is not joined!");
return;
}
recipient.connectionClosed(this);
}
/**
* This method is called by the transport instance that is responsible for
* RTCP/I as soon as a packet has been received.
*
* @param packet The received transport packet.
*/
public void receiveTransportPacket(TransportPacket packet) {
messages.put(new Message(Message.RECEIVE_TRANSPORT_PACKET,packet));
}
private void execReceiveTransportPacket(TransportPacket packet) {
// Check if this is a stereo-server machine
//System.out.println("received RTCPI transport packet");
if (right != null)
{
byte[] temp = packet.getData();
byte[] temp2 = (byte[]) temp.clone();
byte[] temp3 = (byte[]) temp.clone();
TransportPacket copy = new TransportPacket(packet.getLength(), packet.getData(), packet.getSenderAddress(), packet.getSenderPort());
right.sendTransportPacket(copy);
copy = new TransportPacket(packet.getLength(), packet.getData(), packet.getSenderAddress(), packet.getSenderPort());
left.sendTransportPacket(copy);
packet = new TransportPacket(packet.getLength(), temp3);
}
int currentStart=0;
byte[] data = packet.getData();
int length = packet.getLength();
LinkedList rtcpiPackets = new LinkedList();
RtcpiPacket rtcpiPacket=null;
try {
while(currentStart<length) {
switch (RtcpiPacket.inspectPayloadType(data,currentStart)) {
case RtcpiSourceDescriptionPacket.SDES:
rtcpiPacket = new RtcpiSourceDescriptionPacket(data, currentStart);
rtcpiPacket.parse();
rtcpiPackets.add(rtcpiPacket);
break;
case RtcpiByePacket.BYE:
rtcpiPacket = new RtcpiByePacket(data, currentStart);
rtcpiPacket.parse();
rtcpiPackets.add(rtcpiPacket);
break;
case RtcpiSubcomponentReportPacket.SUBREP:
rtcpiPacket = new RtcpiSubcomponentReportPacket(data, currentStart);
rtcpiPacket.parse();
rtcpiPackets.add(rtcpiPacket);
break;
default:
System.err.println("Rtpi: Unknown Rtcpi packet received -ignored! Type was "+RtcpiPacket.inspectPayloadType(data,currentStart));
}
currentStart+=RtcpiPacket.inspectLength(data,currentStart);
}
} catch (Exception ex) {
System.err.println("Rtpi: invalid RTCP/I compound packet received - ignored! Error was: "+ex);
return;
}
if(!(rtcpiPackets.getFirst() instanceof RtcpiSourceDescriptionPacket)) {
System.err.println("Rtpi: Received invalid RTCP/I compound packet. It did not start with an SDES packet!");
return;
}
RtcpiSourceDescriptionPacket sdesPacket=(RtcpiSourceDescriptionPacket) rtcpiPackets.getFirst();
LinkedList items = sdesPacket.getSourceDescriptionItems();
if (((SourceDescriptionItem) items.getFirst()).getType()!=SourceDescriptionItem.CNAME) {
System.err.println("Rtpi: Received invalid RTCP/I compound packet. SDES packet did not start with a CNAME item!");
return;
}
// now we are sure that we have received a valid RTCPI packet!
avg_rtcpi_size=(int) (avg_rtcpi_size*OLD_FACTOR+(packet.getLength()+rtcpiTransport.getHeaderSize())*NEW_FACTOR);
RtpiSourceInfo info = (RtpiSourceInfo) remoteParticipants.get(new Integer(sdesPacket.getParticipantID()));
if (info!=null) {
info.heardFrom();
//System.out.println("calling heardFrom for source info: " + sdesPacket.getParticipantID());
}
for (ListIterator iter=rtcpiPackets.listIterator();iter.hasNext();) {
rtcpiPacket = (RtcpiPacket) iter.next();
switch (rtcpiPacket.getPayloadType()) {
case RtcpiSourceDescriptionPacket.SDES:
handleSdesPacket((RtcpiSourceDescriptionPacket) rtcpiPacket);
break;
case RtcpiByePacket.BYE:
handleByePacket((RtcpiByePacket) rtcpiPacket);
break;
case RtcpiSubcomponentReportPacket.SUBREP:
handleSubcomponentReportPacket((RtcpiSubcomponentReportPacket) rtcpiPacket);
break;
default:
System.err.println("Rtpi: Could not handle RTCP/I packet, unknown type: "+rtcpiPacket.getPayloadType());
break;
}
}
}
private void handleSdesPacket(RtcpiSourceDescriptionPacket spacket) {
if (!groupJoined) {
System.err.println("Rtpi: discarding sdes packet because the group is not joined!");
return;
}
//System.out.println("Got RtcpiSourceDescriptionPacket: " + spacket);
RtpiSourceInfo info = (RtpiSourceInfo) remoteParticipants.get(new Integer(spacket.getParticipantID()));
boolean newParticipant=false;
boolean infoModified=false;
if (info == null) {
if (deletedParticipants.containsKey(new Integer(spacket.getParticipantID()))) {
return; // we have already received a BYE packet from this participant!
}
info = new RtpiSourceInfo(spacket.getParticipantID());
newParticipant=true;
}
LinkedList items = spacket.getSourceDescriptionItems();
SourceDescriptionItem item;
int itemType;
for (ListIterator iter=items.listIterator(); iter.hasNext();) {
item=(SourceDescriptionItem) iter.next();
try {
if (info.checkForItemValueChange(item)) {
info.setStandardItem(item);
infoModified=true;
}
} catch (Exception ex) {
System.err.println("Rtpi: Could not check or set SDES item for participant, reason: "+ex);
}
}
if (newParticipant) {
recipient.changeSourceInfo(this, info);
remoteParticipants.put(new Integer(info.getParticipantID()),info);
members++;
return;
}
if (infoModified) {
recipient.changeSourceInfo(this, info);
}
}
private void handleByePacket(RtcpiByePacket bpacket) {
if (!groupJoined) {
if (byeSent) {
return;
}
members++;
return;
}
RtpiSourceInfo info = (RtpiSourceInfo) remoteParticipants.remove(new Integer(bpacket.getParticipantID()));
if (info==null) {
return;
}
deletedParticipants.put(new Integer(bpacket.getParticipantID()), info);
recipient.removeSource(this, info);
members--;
doReverseReconsideration();
}
private void handleSubcomponentReportPacket(RtcpiSubcomponentReportPacket packet) {
if (!groupJoined) {
System.err.println("Rtpi: discarding SUBREP packet because the group is not joined!");
return;
}
if (packet.getParticipantID() == localParticipant.getParticipantID())
return;
//System.out.println("Got RtcpiSubcomponentReportPacket: " + packet);
LinkedList subcomponents = packet.getSubcomponentReportInfo();
SubcomponentReportInfo info = null;
long subID=0;
int active=0;
byte[] name=null;
for(ListIterator iter=subcomponents.listIterator();iter.hasNext();) {
info = (SubcomponentReportInfo) iter.next();
subID = info.getSubcomponentID();
active = info.getActive();
name = info.getApplicationLevelName();
//System.out.println("Active is: " + active);
if (name==null && applicationLevelNames) {
System.err.println("Rtpi: received SUBREP packet without application level names,");
System.err.println(" even though there should be application level names for this application - packet ignored!");
return;
}
SubcomponentInfo activeInfo = (SubcomponentInfo) remoteActiveSubcomponents.get(new Long(subID));
SubcomponentInfo passiveInfo = (SubcomponentInfo) remotePassiveSubcomponents.get(new Long(subID));
SubcomponentInfo localActiveInfo = (SubcomponentInfo) localActiveSubcomponents.get(new Long(subID));
SubcomponentInfo localPassiveInfo = (SubcomponentInfo) localPassiveSubcomponents.get(new Long(subID));
if (active==1) {
if (activeInfo==null) {
activeInfo=new SubcomponentInfo(name);
if (remotePassiveSubcomponents.containsKey(new Long(subID)))
{
remotePassiveSubcomponents.remove(new Long(subID));
passiveInfo = null;
}
remoteActiveSubcomponents.put(new Long(subID), activeInfo);
//System.out.println("2 added subID " + subID + " to ras list");
if (localActiveInfo==null) {
if (passiveInfo==null && localPassiveInfo==null) {
recipient.addSubcomponent(this, subID, true, name);
} else {
recipient.activateSubcomponent(this, subID);
}
}
} else {
//System.out.println("calling heard from for id: " + subID);
activeInfo.heardFrom();
}
} else {
if (passiveInfo == null) {
passiveInfo=new SubcomponentInfo(name);
remotePassiveSubcomponents.put(new Long(subID), passiveInfo);
if (localActiveInfo==null && activeInfo==null && localPassiveInfo==null) {
//System.out.println("adding inactive sub");
recipient.addSubcomponent(this, subID, false, name);
}
} else {
//System.out.println("2 calling heard from for id: " + subID);
passiveInfo.heardFrom();
}
}
}
}
private void doReverseReconsideration() {
if (wakeUpThread!=null && wakeUpThread.isAlive()) {
wakeUpThread.interrupt();
}
long tc= System.currentTimeMillis();
long off1 = (long) ((members/pmembers)*(tn - tc));
long off2 = (long) ((members/pmembers)*(tc - tp));
tn = tc + off1;
tp = tc - off2;
if (right == null)
wakeUpThread = new WakeUpThread(tn-tc,this);
pmembers=members;
}
public void wakeUp() {
if (right == null)
messages.put(new Message(Message.WAKE_UP, null));
}
void execWakeUp() {
long currentTime=System.currentTimeMillis();
tn = calculateTransmissionInterval()+tp;
if (currentTime>=tn) {
if (!groupJoined) {
if (byeSent) {
return;
} else {
//System.out.println("Sending ADU");
try {
RtcpiSourceDescriptionPacket spacket=constructSdesPacket();
spacket.flush();
byte[] spacketData = spacket.getPacket();
RtcpiByePacket bpacket=new RtcpiByePacket(localParticipant.getParticipantID());
bpacket.flush();
byte[] bpacketData = bpacket.getPacket();
byte [] packetData = new byte[spacketData.length+bpacketData.length];
System.arraycopy(spacketData, 0, packetData, 0, spacketData.length);
System.arraycopy(bpacketData, 0, packetData, spacketData.length, bpacketData.length);
TransportPacket tpacket = new TransportPacket(packetData.length,packetData);
rtcpiTransport.sendTransportPacket(tpacket);
byeSent=true;
} catch (Exception ex) {
System.err.println("Rtpi: could not send BYE packet when leaving the group, error was: "+ex);
}
rtcpiTransport.leaveGroup();
return;
}
} else {
initial=false;
RtcpiSourceDescriptionPacket spacket;
RtcpiSubcomponentReportPacket rpacket;
try {
//System.out.println("sending ADU");
checkTimeouts(); // we do not need to perform the reverse reconsideration algorithm since
// we are about to calculate a new transmission interval anyway.
spacket=constructSdesPacket();
spacket.flush();
byte[] spacketData = spacket.getPacket();
rpacket=constructSubRepPacket();
//System.out.println("created subreppacket: " + rpacket);
TransportPacket tpacket=null;
int tpacketLength=0;
if (rpacket==null) {
tpacket= new TransportPacket(spacketData.length, spacketData);
tpacketLength=spacketData.length;
} else {
rpacket.flush();
byte[] rpacketData = rpacket.getPacket();
byte [] packetData = new byte[spacketData.length+rpacketData.length];
System.arraycopy(spacketData, 0, packetData, 0, spacketData.length);
System.arraycopy(rpacketData, 0, packetData, spacketData.length, rpacketData.length);
tpacket = new TransportPacket(packetData.length,packetData);
tpacketLength=packetData.length;
}
rtcpiTransport.sendTransportPacket(tpacket);
//System.out.println("Sending tpacket: " + tpacket);
avg_rtcpi_size=(int) (avg_rtcpi_size*OLD_FACTOR+(tpacketLength+rtcpiTransport.getHeaderSize())*NEW_FACTOR);
sentSomething=true;
} catch (Exception ex) {
System.err.println("Rtpi: Unable to construct RTCP/I compound packet - aborting report, reason: "+ex);
}
// calculate and transmit rtcpi packet, update avg_rtcpi_size!
tp=currentTime;
tn =calculateTransmissionInterval()+tp;
pmembers=members;
}
}
if (right == null)
wakeUpThread=new WakeUpThread(tn-currentTime,this);
}
private void checkTimeouts() { // this is ugly, can anyone please clean this up?
RtpiSourceInfo info;
LinkedList removeList = new LinkedList();
Integer pid;
// Timing out participants
for (Enumeration iter=remoteParticipants.keys();iter.hasMoreElements();) {
pid = (Integer) iter.nextElement();
info = (RtpiSourceInfo) remoteParticipants.get(pid);
if (info.timedOut(currentDeterministicTransmissionInterval*timeoutIntervalsSource)) {
removeList.add(pid);
}
}
for (ListIterator iter=removeList.listIterator();iter.hasNext();) {
info= (RtpiSourceInfo) remoteParticipants.remove((Integer) iter.next());
recipient.removeSource(this, info);
members--;
}
removeList.clear();
for (Enumeration iter=deletedParticipants.keys();iter.hasMoreElements();) {
pid = (Integer) iter.nextElement();
info = (RtpiSourceInfo) deletedParticipants.get(pid);
if (info.timedOut(currentDeterministicTransmissionInterval*timeoutByeIntervals)) {
removeList.add(pid);
}
}
for (ListIterator iter=removeList.listIterator();iter.hasNext();) {
deletedParticipants.remove((Integer) iter.next());
}
removeList.clear();
// Timing out subcomponents
Long subID;
SubcomponentInfo sinfo;
for (Enumeration iter=remotePassiveSubcomponents.keys();iter.hasMoreElements();) {
subID = (Long) iter.nextElement();
sinfo = (SubcomponentInfo) remotePassiveSubcomponents.get(subID);
if (sinfo.timedOut(currentDeterministicTransmissionInterval*timeoutIntervalsSubcomponent)) {
removeList.add(subID);
}
}
for (ListIterator iter=removeList.listIterator();iter.hasNext();) {
subID = (Long) iter.next();
sinfo = (SubcomponentInfo) remotePassiveSubcomponents.remove(subID);
if (!remoteActiveSubcomponents.containsKey(subID) &&
!localPassiveSubcomponents.containsKey(subID) &&
!localActiveSubcomponents.containsKey(subID)) {
//System.out.println("remove sub 1");
recipient.removeSubcomponent(this, subID.longValue());
}
}
removeList.clear();
for (Enumeration iter=remoteActiveSubcomponents.keys();iter.hasMoreElements();) {
subID = (Long) iter.nextElement();
sinfo = (SubcomponentInfo) remoteActiveSubcomponents.get(subID);
if (sinfo.timedOut(currentDeterministicTransmissionInterval*timeoutIntervalsSubcomponent)) {
//System.out.println("currentDeterministicTransmissionInterval: " + currentDeterministicTransmissionInterval);
//System.out.println("timeoutIntervalsSubcomponent: " + timeoutIntervalsSubcomponent);
removeList.add(subID);
}
}
for (ListIterator iter=removeList.listIterator();iter.hasNext();) {
subID = (Long) iter.next();
sinfo = (SubcomponentInfo) remoteActiveSubcomponents.remove(subID);
if (!remotePassiveSubcomponents.containsKey(subID) &&
!localPassiveSubcomponents.containsKey(subID) &&
!localActiveSubcomponents.containsKey(subID)) {
//System.out.println("remove sub 2");
recipient.removeSubcomponent(this, subID.longValue());
} else {
if(!localActiveSubcomponents.containsKey(subID)) {
recipient.deactivateSubcomponent(this, subID.longValue());
}
}
}
removeList.clear();
}
private RtcpiSourceDescriptionPacket constructSdesPacket() throws IllegalValueException {
LinkedList itemList=new LinkedList();
itemList.add(localParticipant.getStandardItem(SourceDescriptionItem.CNAME));
SourceDescriptionItem item;
if ((item=localParticipant.getStandardItem(SourceDescriptionItem.NOTE))!=null) {
noteFinishedCount=NOTE_FINISHED_COUNT;
if(sendNote) {
sendNote=!sendNote;
itemList.add(item);
RtcpiSourceDescriptionPacket packet = new RtcpiSourceDescriptionPacket(localParticipant.getParticipantID(), itemList);
return packet;
}
sendNote=!sendNote;
} else if (noteFinishedCount>0) {
if(sendNote) {
noteFinishedCount--;
sendNote=!sendNote;
item=new SourceDescriptionItem(SourceDescriptionItem.NOTE, new byte[0]);
itemList.add(item);
RtcpiSourceDescriptionPacket packet = new RtcpiSourceDescriptionPacket(localParticipant.getParticipantID(), itemList);
return packet;
}
sendNote=!sendNote;
}
if (sendName && (item=localParticipant.getStandardItem(SourceDescriptionItem.NAME))!=null) {
sendName=!sendName;
itemList.add(item);
RtcpiSourceDescriptionPacket packet = new RtcpiSourceDescriptionPacket(localParticipant.getParticipantID(), itemList);
return packet;
}
sendName=!sendName;
switch(othersCount) {
case 0:
if ((item=localParticipant.getStandardItem(SourceDescriptionItem.EMAIL))!=null) {
itemList.add(item);
}
break;
case 1:
if ((item=localParticipant.getStandardItem(SourceDescriptionItem.PHONE))!=null) {
itemList.add(item);
}
break;
case 2:
if ((item=localParticipant.getStandardItem(SourceDescriptionItem.LOC))!=null) {
itemList.add(item);
}
break;
case 3:
if ((item=localParticipant.getStandardItem(SourceDescriptionItem.TOOL))!=null) {
itemList.add(item);
}
break;
default:
System.err.println("Rtpi: illegal value for othersCount!");
}
othersCount=(othersCount+1)%4;
RtcpiSourceDescriptionPacket packet = new RtcpiSourceDescriptionPacket(localParticipant.getParticipantID(),itemList);
return packet;
}
private RtcpiSubcomponentReportPacket constructSubRepPacket() throws IllegalValueException {
LinkedList list = new LinkedList();
SubcomponentInfo info, info2;
for (Enumeration iter=localPassiveSubcomponents.keys();iter.hasMoreElements();) {
Long subID = (Long) iter.nextElement();
info = (SubcomponentInfo) localPassiveSubcomponents.get(subID);
info2 = (SubcomponentInfo) remotePassiveSubcomponents.get(subID);
if (info2!=null) {
if (!info2.timedOut((int)currentDeterministicTransmissionInterval*2)) {
continue;
}
}
info2 = (SubcomponentInfo) remoteActiveSubcomponents.get(subID);
if (info2!=null) {
if (!info2.timedOut((int)currentDeterministicTransmissionInterval*2)) {
continue;
}
}
SubcomponentReportInfo srInfo = new SubcomponentReportInfo(subID.longValue(), 0, info.getApplicationLevelName());
list.add(srInfo);
}
for (Enumeration iter=localActiveSubcomponents.keys();iter.hasMoreElements();) {
Long subID = (Long) iter.nextElement();
info = (SubcomponentInfo) localActiveSubcomponents.get(subID);
info2 = (SubcomponentInfo) remoteActiveSubcomponents.get(subID);
if (info2!=null) {
if (!info2.timedOut((int)currentDeterministicTransmissionInterval*2)) {
continue;
}
}
SubcomponentReportInfo srInfo = new SubcomponentReportInfo(subID.longValue(), 1, info.getApplicationLevelName());
list.add(srInfo);
}
if (list.size() == 0)
return null;
int aln=0;
if (applicationLevelNames) {
aln=1;
}
RtcpiSubcomponentReportPacket packet = new RtcpiSubcomponentReportPacket(localParticipant.getParticipantID(), aln, list);
return packet;
}
public void sendTransportPacket(TransportPacket packet)
{
rtcpiTransport.sendTransportPacket(packet);
}
}