--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/src/jlibrtp/Participant.java Sat Mar 14 22:15:41 2009 +0100
@@ -0,0 +1,437 @@
+/**
+ * Java RTP Library (jlibrtp)
+ * Copyright (C) 2006 Arne Kepp
+ *
+ * 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., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
+ */
+package jlibrtp;
+
+import java.net.InetSocketAddress;
+
+/**
+ * A participant represents a peer in an RTPSession. Based on the information stored on
+ * these objects, packets are processed and statistics generated for RTCP.
+ */
+public class Participant {
+ /** Whether the participant is unexpected, e.g. arrived through unicast with SDES */
+ protected boolean unexpected = false;
+ /** Where to send RTP packets (unicast)*/
+ protected InetSocketAddress rtpAddress = null;
+ /** Where to send RTCP packets (unicast) */
+ protected InetSocketAddress rtcpAddress = null;
+ /** Where the first RTP packet was received from */
+ protected InetSocketAddress rtpReceivedFromAddress = null;
+ /** Where the first RTCP packet was received from */
+ protected InetSocketAddress rtcpReceivedFromAddress = null;
+
+ /** SSRC of participant */
+ protected long ssrc = -1;
+ /** SDES CNAME */
+ protected String cname = null;
+ /** SDES The participant's real name */
+ protected String name = null;
+ /** SDES The participant's email */
+ protected String email = null;
+ /** SDES The participant's phone number */
+ protected String phone = null;
+ /** SDES The participant's location*/
+ protected String loc = null;
+ /** SDES The tool the participants is using */
+ protected String tool = null;
+ /** SDES A note */
+ protected String note = null;
+ /** SDES A priv string, loosely defined */
+ protected String priv = null;
+
+ // Receiver Report Items
+ /** RR First sequence number */
+ protected int firstSeqNumber = -1;
+ /** RR Last sequence number */
+ protected int lastSeqNumber = 0;
+ /** RR Number of times sequence number has rolled over */
+ protected long seqRollOverCount = 0;
+ /** RR Number of packets received */
+ protected long receivedPkts = 0;
+ /** RR Number of octets received */
+ protected long receivedOctets = 0;
+ /** RR Number of packets received since last SR */
+ protected int receivedSinceLastSR = 0;
+ /** RR Sequence number associated with last SR */
+ protected int lastSRRseqNumber = 0;
+ /** RR Interarrival jitter */
+ protected double interArrivalJitter = -1.0;
+ /** RR Last received RTP Timestamp */
+ protected long lastRtpTimestamp = 0;
+
+ /** RR Middle 32 bits of the NTP timestamp in the last SR */
+ protected long timeStampLSR = 0;
+ /** RR The time when we actually got the last SR */
+ protected long timeReceivedLSR = 0;
+
+ /** Gradient where UNIX timestamp = ntpGradient*RTPTimestamp * ntpOffset */
+ protected double ntpGradient = -1;
+ /** Offset where UNIX timestamp = ntpGradient*RTPTimestamp * ntpOffset */
+ protected long ntpOffset = -1;
+ /** Last NTP received in SR packet, MSB */
+ protected long lastNtpTs1 = 0; //32 bits
+ /** Last NTP received in SR packet, LSB */
+ protected long lastNtpTs2 = 0; //32 bits
+ /** RTP Timestamp in last SR packet */
+ protected long lastSRRtpTs = 0; //32 bits
+
+ /** UNIX time when a BYE was received from this participant, for pruning */
+ protected long timestampBYE = -1; // The user said BYE at this time
+
+ /** Store the packets received from this participant */
+ protected PktBuffer pktBuffer = null;
+
+ /** UNIX time of last RTP packet, to check whether this participant has sent anything recently */
+ protected long lastRtpPkt = -1; //Time of last RTP packet
+ /** UNIX time of last RTCP packet, to check whether this participant has sent anything recently */
+ protected long lastRtcpPkt = -1; //Time of last RTCP packet
+ /** UNIX time this participant was added by application, to check whether we ever heard back */
+ protected long addedByApp = -1; //Time the participant was added by application
+ /** UNIX time of last time we sent an RR to this user */
+ protected long lastRtcpRRPkt = -1; //Timestamp of last time we sent this person an RR packet
+ /** Unix time of second to last time we sent and RR to this user */
+ protected long secondLastRtcpRRPkt = -1; //Timestamp of 2nd to last time we sent this person an RR Packet
+
+ /**
+ * Create a basic participant. If this is a <b>unicast</b> session you must provide network address (ipv4 or ipv6) and ports for RTP and RTCP,
+ * as well as a cname for this contact. These things should be negotiated through SIP or a similar protocol.
+ *
+ * jlibrtp will listen for RTCP packets to obtain a matching SSRC for this participant, based on cname.
+ * @param networkAddress string representation of network address (ipv4 or ipv6). Use "127.0.0.1" for multicast session.
+ * @param rtpPort port on which peer expects RTP packets. Use 0 if this is a sender-only, or this is a multicast session.
+ * @param rtcpPort port on which peer expects RTCP packets. Use 0 if this is a sender-only, or this is a multicast session.
+ */
+ public Participant(String networkAddress, int rtpPort, int rtcpPort) {
+ if(RTPSession.rtpDebugLevel > 6) {
+ System.out.println("Creating new participant: " + networkAddress);
+ }
+
+ // RTP
+ if(rtpPort > 0) {
+ try {
+ rtpAddress = new InetSocketAddress(networkAddress, rtpPort);
+ } catch (Exception e) {
+ System.out.println("Couldn't resolve " + networkAddress);
+ }
+ //isReceiver = true;
+ }
+
+ // RTCP
+ if(rtcpPort > 0) {
+ try {
+ rtcpAddress = new InetSocketAddress(networkAddress, rtcpPort);
+ } catch (Exception e) {
+ System.out.println("Couldn't resolve " + networkAddress);
+ }
+ }
+
+ //By default this is a sender
+ //isSender = true;
+ }
+
+ // We got a packet, but we don't know this person yet.
+ protected Participant(InetSocketAddress rtpAdr, InetSocketAddress rtcpAdr, long SSRC) {
+ rtpReceivedFromAddress = rtpAdr;
+ rtcpReceivedFromAddress = rtcpAdr;
+ ssrc = SSRC;
+ unexpected = true;
+ }
+
+ // Dummy constructor to ease testing
+ protected Participant() {
+ System.out.println("Don't use the Participan(void) Constructor!");
+ }
+
+ /**
+ * RTP Address registered with this participant.
+ *
+ * @return address of participant
+ */
+ InetSocketAddress getRtpSocketAddress() {
+ return rtpAddress;
+ }
+
+
+ /**
+ * RTCP Address registered with this participant.
+ *
+ * @return address of participant
+ */
+ InetSocketAddress getRtcpSocketAddress() {
+ return rtcpAddress;
+ }
+
+ /**
+ * InetSocketAddress this participant has used to
+ * send us RTP packets.
+ *
+ * @return address of participant
+ */
+ InetSocketAddress getRtpReceivedFromAddress() {
+ return rtpAddress;
+ }
+
+
+
+ /**
+ * InetSocketAddress this participant has used to
+ * send us RTCP packets.
+ *
+ * @return address of participant
+ */
+ InetSocketAddress getRtcpReceivedFromAddress() {
+ return rtcpAddress;
+ }
+
+
+ /**
+ * CNAME registered for this participant.
+ *
+ * @return the cname
+ */
+ public String getCNAME() {
+ return cname;
+ }
+
+
+ /**
+ * NAME registered for this participant.
+ *
+ * @return the name
+ */
+ public String getNAME() {
+ return name;
+ }
+
+ /**
+ * EMAIL registered for this participant.
+ *
+ * @return the email address
+ */
+ public String getEmail() {
+ return email;
+ }
+
+ /**
+ * PHONE registered for this participant.
+ *
+ * @return the phone number
+ */
+ public String getPhone() {
+ return phone;
+ }
+
+ /**
+ * LOCATION registered for this participant.
+ *
+ * @return the location
+ */
+ public String getLocation() {
+ return loc;
+ }
+
+ /**
+ * NOTE registered for this participant.
+ *
+ * @return the note
+ */
+ public String getNote() {
+ return note;
+ }
+
+ /**
+ * PRIVATE something registered for this participant.
+ *
+ * @return the private-string
+ */
+ public String getPriv() {
+ return priv;
+ }
+
+ /**
+ * TOOL something registered for this participant.
+ *
+ * @return the tool
+ */
+ public String getTool() {
+ return tool;
+ }
+
+ /**
+ * SSRC for participant, determined through RTCP SDES
+ *
+ * @return SSRC (32 bit unsigned integer as long)
+ */
+ public long getSSRC() {
+ return this.ssrc;
+ }
+
+ /**
+ * Updates the participant with information for receiver reports.
+ *
+ * @param packetLength to keep track of received octets
+ * @param pkt the most recently received packet
+ */
+ protected void updateRRStats(int packetLength, RtpPkt pkt) {
+ int curSeqNum = pkt.getSeqNumber();
+
+ if(firstSeqNumber < 0) {
+ firstSeqNumber = curSeqNum;
+ }
+
+ receivedOctets += packetLength;
+ receivedSinceLastSR++;
+ receivedPkts++;
+
+ long curTime = System.currentTimeMillis();
+
+ if( this.lastSeqNumber < curSeqNum ) {
+ //In-line packet, best thing you could hope for
+ this.lastSeqNumber = curSeqNum;
+
+ } else if(this.lastSeqNumber - this.lastSeqNumber < -100) {
+ //Sequence counter rolled over
+ this.lastSeqNumber = curSeqNum;
+ seqRollOverCount++;
+
+ } else {
+ //This was probably a duplicate or a late arrival.
+ }
+
+ // Calculate jitter
+ if(this.lastRtpPkt > 0) {
+
+ long D = (pkt.getTimeStamp() - curTime) - (this.lastRtpTimestamp - this.lastRtpPkt);
+ if(D < 0)
+ D = (-1)*D;
+
+ this.interArrivalJitter += ((double)D - this.interArrivalJitter) / 16.0;
+ }
+
+ lastRtpPkt = curTime;
+ lastRtpTimestamp = pkt.getTimeStamp();
+ }
+
+ /**
+ * Calculates the extended highest sequence received by adding
+ * the last sequence number to 65536 times the number of times
+ * the sequence counter has rolled over.
+ *
+ * @return extended highest sequence
+ */
+ protected long getExtHighSeqRecv() {
+ return (65536*seqRollOverCount + lastSeqNumber);
+ }
+
+ /**
+ * Get the fraction of lost packets, calculated as described
+ * in RFC 3550 as a fraction of 256.
+ *
+ * @return the fraction of lost packets since last SR received
+ */
+ protected int getFractionLost() {
+ int expected = (lastSeqNumber - lastSRRseqNumber);
+ if(expected < 0)
+ expected = 65536 + expected;
+
+ int fraction = 256 * (expected - receivedSinceLastSR);
+ if(expected > 0) {
+ fraction = (fraction / expected);
+ } else {
+ fraction = 0;
+ }
+
+ //Clear counters
+ receivedSinceLastSR = 0;
+ lastSRRseqNumber = lastSeqNumber;
+
+ return fraction;
+ }
+
+ /**
+ * The total number of packets lost during the session.
+ *
+ * Returns zero if loss is negative, i.e. duplicates have been received.
+ *
+ * @return number of lost packets, or zero.
+ */
+ protected long getLostPktCount() {
+ long lost = (this.getExtHighSeqRecv() - this.firstSeqNumber) - receivedPkts;
+
+ if(lost < 0)
+ lost = 0;
+ return lost;
+ }
+
+ /**
+ *
+ * @return the interArrivalJitter, calculated continuously
+ */
+ protected double getInterArrivalJitter() {
+ return this.interArrivalJitter;
+ }
+
+ /**
+ * Set the timestamp for last sender report
+ *
+ * @param ntp1 high order bits
+ * @param ntp2 low order bits
+ */
+ protected void setTimeStampLSR(long ntp1, long ntp2) {
+ // Use what we've got
+ byte[] high = StaticProcs.uIntLongToByteWord(ntp1);
+ byte[] low = StaticProcs.uIntLongToByteWord(ntp2);
+ low[3] = low[1];
+ low[2] = low[0];
+ low[1] = high[3];
+ low[0] = high[2];
+
+ this.timeStampLSR = StaticProcs.bytesToUIntLong(low, 0);
+ }
+
+ /**
+ * Calculate the delay between the last received sender report
+ * and now.
+ *
+ * @return the delay in units of 1/65.536ms
+ */
+ protected long delaySinceLastSR() {
+ if(this.timeReceivedLSR < 1)
+ return 0;
+
+ long delay = System.currentTimeMillis() - this.timeReceivedLSR;
+
+ //Convert ms into 1/65536s = 1/65.536ms
+ return (long) ((double)delay * 65.536);
+ }
+
+ /**
+ * Only for debugging purposes
+ */
+ public void debugPrint() {
+ System.out.print(" Participant.debugPrint() SSRC:"+this.ssrc+" CNAME:"+this.cname);
+ if(this.rtpAddress != null)
+ System.out.print(" RTP:"+this.rtpAddress.toString());
+ if(this.rtcpAddress != null)
+ System.out.print(" RTCP:"+this.rtcpAddress.toString());
+ System.out.println("");
+
+ System.out.println(" Packets received:"+this.receivedPkts);
+ }
+}