diff -r 537ddd8aa407 -r 2036ebfaccda src/jlibrtp/Participant.java --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/src/jlibrtp/Participant.java Fri Nov 20 19:29:42 2009 +0100 @@ -0,0 +1,464 @@ +/** + * 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 unicast 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); + } +}