/* * Copyright 2007 Sun Microsystems, Inc. * * This file is part of jVoiceBridge. * * jVoiceBridge is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License version 2 as * published by the Free Software Foundation and distributed hereunder * to you. * * jVoiceBridge 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 General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program. If not, see <http://www.gnu.org/licenses/>. * * Sun designates this particular file as subject to the "Classpath" * exception as provided by Sun in the License file that accompanied this * code. */ package com.sun.voip; import java.io.IOException; import java.net.DatagramPacket; import java.net.DatagramSocket; import java.net.InetSocketAddress; import java.util.HashMap; import com.sun.stun.StunServerImpl; public class RtcpReceiver extends Thread { private boolean done; private DatagramSocket rtcpSocket; private byte[] rtcpData; private StunServerImpl stunServerImpl; /* * The rtcpSocket needs to be passed in here because * it is obtained the same the the rtpSocket is obtained * and ports for the two socket must be an even/odd pair. */ public RtcpReceiver(DatagramSocket rtcpSocket, boolean loneChannel) { this.rtcpSocket = rtcpSocket; if (loneChannel) { timeLastReceivedMap = new HashMap(); } stunServerImpl = new StunServerImpl(); setName("RtcpReceiver-" + rtcpSocket.getLocalPort()); setPriority(Thread.NORM_PRIORITY); start(); } public void end() { done = true; rtcpSocket.close(); } private HashMap<String, Long> timeLastReceivedMap; private long timeLastReceived; /* * Receive both sender and receiver reports. */ public void run() { rtcpData = new byte[1500]; DatagramPacket packet = new DatagramPacket(rtcpData, rtcpData.length); while (!done) { try { rtcpSocket.receive(packet); if (Logger.logLevel >= Logger.LOG_INFO) { Logger.println("Got RTCP Packet from " + packet.getSocketAddress()); } byte[] data = packet.getData(); RtcpPacket rtcpPacket = null; if (isStunBindingRequest(data) == true) { stunServerImpl.processStunRequest(rtcpSocket, packet); continue; } if ((data[1] & 0xff) == 200) { rtcpPacket = new RtcpSenderPacket(packet); ((RtcpSenderPacket)rtcpPacket).printReport(); } else if ((data[1] & 0xff) == 201) { rtcpPacket = new RtcpReceiverPacket(packet); ((RtcpReceiverPacket)rtcpPacket).printReport(); } else { Util.dump("unknown RTCP packet", data, 0, 16); } if (rtcpPacket != null) { timeLastReceived = System.currentTimeMillis(); if (timeLastReceivedMap != null) { if (Logger.logLevel >= Logger.LOG_INFO) { Logger.println("Updated map for " + packet.getSocketAddress() + " " + timeLastReceived); } synchronized (timeLastReceivedMap) { timeLastReceivedMap.put(packet.getSocketAddress().toString(), new Long(timeLastReceived)); } } } } catch (Exception e) { if (!done) { Logger.error("RtcpReceiver: receive failed! " + e.getMessage()); end(); } } } } private boolean isStunBindingRequest(byte[] data) { /* * If this is an RTP packet, the first byte * must have bit 7 set indicating RTP v2. * If byte 0 is 0 and byte 1 is 1, then we * assume this packet is a STUN Binding request. */ return data[0] == 0 && data[1] == 1; } public long secondsSinceLastReport(InetSocketAddress isa) { if (timeLastReceivedMap != null && isa == null) { return 0; } long now = System.currentTimeMillis(); if (timeLastReceivedMap == null) { if (timeLastReceived == 0) { timeLastReceived = now; } long elapsed = now - timeLastReceived; timeLastReceived = now; return elapsed; } Long t; synchronized (timeLastReceivedMap) { t = timeLastReceivedMap.get(isa.toString()); } if (t == null) { synchronized (timeLastReceivedMap) { if (Logger.logLevel >= Logger.LOG_INFO) { Logger.println("Putting " + isa); } timeLastReceivedMap.put(isa.toString(), new Long(now)); } return 0; } return (now - t.longValue()) / 1000; } }