/* * Copyright (C) 2011-2014 GUIGUI Simon, fyhertz@gmail.com * * This file is part of libstreaming (https://github.com/fyhertz/libstreaming) * * Spydroid is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 3 of the License, or * (at your option) any later version. * * This source code 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 source code; if not, write to the Free Software * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */ package net.majorkernelpanic.streaming.rtcp; import static net.majorkernelpanic.streaming.rtp.RtpSocket.TRANSPORT_TCP; import static net.majorkernelpanic.streaming.rtp.RtpSocket.TRANSPORT_UDP; import java.io.IOException; import java.io.OutputStream; import java.net.DatagramPacket; import java.net.InetAddress; import java.net.MulticastSocket; import java.nio.channels.IllegalSelectorException; import android.os.SystemClock; import android.util.Log; /** * Implementation of Sender Report RTCP packets. */ public class SenderReport { public static final int MTU = 1500; private static final int PACKET_LENGTH = 28; private MulticastSocket usock; private DatagramPacket upack; private int mTransport; private OutputStream mOutputStream = null; private byte[] mBuffer = new byte[MTU]; private int mSSRC, mPort = -1; private int mOctetCount = 0, mPacketCount = 0; private long interval, delta, now, oldnow; private byte mTcpHeader[]; public SenderReport(int ssrc) throws IOException { super(); this.mSSRC = ssrc; } public SenderReport() { mTransport = TRANSPORT_UDP; mTcpHeader = new byte[] {'$',0,0,PACKET_LENGTH}; /* Version(2) Padding(0) */ /* ^ ^ PT = 0 */ /* | | ^ */ /* | -------- | */ /* | |--------------------- */ /* | || */ /* | || */ mBuffer[0] = (byte) Integer.parseInt("10000000",2); /* Packet Type PT */ mBuffer[1] = (byte) 200; /* Byte 2,3 -> Length */ setLong(PACKET_LENGTH/4-1, 2, 4); /* Byte 4,5,6,7 -> SSRC */ /* Byte 8,9,10,11 -> NTP timestamp hb */ /* Byte 12,13,14,15 -> NTP timestamp lb */ /* Byte 16,17,18,19 -> RTP timestamp */ /* Byte 20,21,22,23 -> packet count */ /* Byte 24,25,26,27 -> octet count */ try { usock = new MulticastSocket(); } catch (IOException e) { // Very unlikely to happen. Means that all UDP ports are already being used throw new RuntimeException(e.getMessage()); } upack = new DatagramPacket(mBuffer, 1); // By default we sent one report every 3 secconde interval = 3000; } public void close() { usock.close(); } /** * Sets the temporal interval between two RTCP Sender Reports. * Default interval is set to 3 seconds. * Set 0 to disable RTCP. * @param interval The interval in milliseconds */ public void setInterval(long interval) { this.interval = interval; } /** * Updates the number of packets sent, and the total amount of data sent. * @param length The length of the packet * @param rtpts * The RTP timestamp. * @throws IOException **/ public void update(int length, long rtpts) throws IOException { mPacketCount += 1; mOctetCount += length; setLong(mPacketCount, 20, 24); setLong(mOctetCount, 24, 28); now = SystemClock.elapsedRealtime(); delta += oldnow != 0 ? now-oldnow : 0; oldnow = now; if (interval>0) { if (delta>=interval) { // We send a Sender Report send(System.nanoTime(), rtpts); delta = 0; } } } public void setSSRC(int ssrc) { this.mSSRC = ssrc; setLong(ssrc,4,8); mPacketCount = 0; mOctetCount = 0; setLong(mPacketCount, 20, 24); setLong(mOctetCount, 24, 28); } public void setDestination(InetAddress dest, int dport) { mTransport = TRANSPORT_UDP; mPort = dport; upack.setPort(dport); upack.setAddress(dest); } /** * If a TCP is used as the transport protocol for the RTP session, * the output stream to which RTP packets will be written to must * be specified with this method. */ public void setOutputStream(OutputStream os, byte channelIdentifier) { mTransport = TRANSPORT_TCP; mOutputStream = os; mTcpHeader[1] = channelIdentifier; } public int getPort() { return mPort; } public int getLocalPort() { return usock.getLocalPort(); } public int getSSRC() { return mSSRC; } /** * Resets the reports (total number of bytes sent, number of packets sent, etc.) */ public void reset() { mPacketCount = 0; mOctetCount = 0; setLong(mPacketCount, 20, 24); setLong(mOctetCount, 24, 28); delta = now = oldnow = 0; } private void setLong(long n, int begin, int end) { for (end--; end >= begin; end--) { mBuffer[end] = (byte) (n % 256); n >>= 8; } } /** * Sends the RTCP packet over the network. * * @param ntpts * the NTP timestamp. * @param rtpts * the RTP timestamp. */ private void send(long ntpts, long rtpts) throws IOException { long hb = ntpts/1000000000; long lb = ( ( ntpts - hb*1000000000 ) * 4294967296L )/1000000000; setLong(hb, 8, 12); setLong(lb, 12, 16); setLong(rtpts, 16, 20); if (mTransport == TRANSPORT_UDP) { upack.setLength(PACKET_LENGTH); usock.send(upack); } else { synchronized (mOutputStream) { try { mOutputStream.write(mTcpHeader); mOutputStream.write(mBuffer, 0, PACKET_LENGTH); } catch (Exception e) {} } } } }