Package rtpi

Source Code of rtpi.Rtpi

/* 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);
    }
 

}
   



TOP

Related Classes of rtpi.Rtpi

TOP
Copyright © 2018 www.massapi.com. All rights reserved.
All source code are property of their respective owners. Java is a trademark of Sun Microsystems, Inc and owned by ORACLE Inc. Contact coftware#gmail.com.