| 1 | package cz.vutbr.feec.session.rtprtcp.internal; |
| 2 | |
| 3 | import java.net.InetAddress; |
| 4 | import java.net.UnknownHostException; |
| 5 | import java.util.Random; |
| 6 | |
| 7 | import org.apache.log4j.Logger; |
| 8 | |
| 9 | import cz.vutbr.feec.packets.PacketGenerateException; |
| 10 | import cz.vutbr.feec.packets.PacketParseException; |
| 11 | import cz.vutbr.feec.packets.rtp.RTPPacket; |
| 12 | import cz.vutbr.feec.session.SessionThread; |
| 13 | |
| 14 | import net.java.dev.jssm.Socket; |
| 15 | |
| 16 | /** |
| 17 | * This class encapsulates the functionality to construct and send out RTP |
| 18 | * Packets and also to receive RTP Packets. It provides a seperate thread to |
| 19 | * receive and send out RTP Packets. |
| 20 | */ |
| 21 | |
| 22 | public class RTPThreadHandler extends SessionThread { |
| 23 | /** The logger. */ |
| 24 | private static Logger logger = Logger.getLogger(RTPThreadHandler.class |
| 25 | .getName()); |
| 26 | |
| 27 | /** Multicast Socket for sending RTP. */ |
| 28 | protected Socket socket; |
| 29 | |
| 30 | /** The destination port. */ |
| 31 | protected int destPort; |
| 32 | |
| 33 | /** |
| 34 | * Constructs a datagram, assembles it into an RTP packet and sends it out. |
| 35 | * @param marker the marker |
| 36 | * @param length the length |
| 37 | * @param data Payload to be sent out as part of the RTP Packet. |
| 38 | * @return the int |
| 39 | */ |
| 40 | private byte[] sendBuffer = new byte[1500]; |
| 41 | |
| 42 | /** The sequence number of rtp packet. */ |
| 43 | private long sequence_number; // 16 bits |
| 44 | |
| 45 | /** The RTP session. */ |
| 46 | private Session3550 session; |
| 47 | |
| 48 | /** |
| 49 | * Constructor for the class. Takes in a TCP/IP Address and a port number. |
| 50 | * It initializes a a new multicast socket according to the multicast |
| 51 | * address and the port number given |
| 52 | * |
| 53 | * @param rtpPort |
| 54 | * the rtp port |
| 55 | * @param multicastSocket |
| 56 | * the multicast socket |
| 57 | * @param rtpSession |
| 58 | * the rtp session |
| 59 | */ |
| 60 | public RTPThreadHandler(Session3550 rtpSession, Socket multicastSocket, |
| 61 | int rtpPort) { |
| 62 | this.setName("RTP-in/out (R) or (S)"); |
| 63 | this.session = rtpSession; |
| 64 | this.socket = multicastSocket; |
| 65 | |
| 66 | this.destPort = rtpPort; |
| 67 | // socket.setDestinationPort(rtpPort); |
| 68 | |
| 69 | Random rnd = new Random(); // Use time as default seed |
| 70 | |
| 71 | // Start with a random sequence number |
| 72 | sequence_number = (long) (Math.abs(rnd.nextInt()) & 0x000000FF); |
| 73 | |
| 74 | // Session3550.outprintln("RTP Session SSRC: " |
| 75 | // + Long.toHexString(session.getOwnSource().getSSRC())); |
| 76 | // Session3550.outprintln(" Starting Seq: " + sequence_number); |
| 77 | } |
| 78 | |
| 79 | /** |
| 80 | * Send packet. |
| 81 | * |
| 82 | * @param length |
| 83 | * the length |
| 84 | * @param data |
| 85 | * the data |
| 86 | * |
| 87 | * @return the int |
| 88 | * @throws PacketGenerateException |
| 89 | */ |
| 90 | public int sendPacket(byte[] data, int length) throws PacketGenerateException { |
| 91 | boolean marker = false; |
| 92 | return sendPacket(data, length, marker); |
| 93 | } |
| 94 | |
| 95 | public int sendPacket(byte[] data, int length, boolean marker) throws PacketGenerateException { |
| 96 | RTPPacket packet = new RTPPacket(); |
| 97 | packet.setVersion(RTCPConstants.VERSION); |
| 98 | packet.setPadding(RTCPConstants.PADDING); |
| 99 | packet.setExtension(RTCPConstants.EXTENSION); |
| 100 | int m = marker ? 1 : 0; |
| 101 | packet.setMarker(m); |
| 102 | packet.setPayloadtype(session.getPayloadType()); |
| 103 | packet.setSeqNum(sequence_number); |
| 104 | packet.setTimeStamp(System.currentTimeMillis()); |
| 105 | packet.setSSRC(session.getOwnSource().getSSRC()); |
| 106 | packet.setData(data, 0, data.length); |
| 107 | int pktLen = packet.generate(sendBuffer, 0); |
| 108 | |
| 109 | // invoke event for listeners |
| 110 | session.onRTPSent(packet); |
| 111 | |
| 112 | sequence_number++; |
| 113 | session.packetCount++; |
| 114 | session.octetCount += data.length; |
| 115 | session.getOwnSource().timeOfLastRTPSent = System.currentTimeMillis(); |
| 116 | |
| 117 | logger.debug(" RTP_PKT_LEN:" + pktLen + " RTP_HEADER_LEN:" |
| 118 | + " PAYLOAD_LEN:"+length); |
| 119 | socket.sendTo(sendBuffer, pktLen , destPort); |
| 120 | |
| 121 | return pktLen; |
| 122 | } |
| 123 | |
| 124 | /** |
| 125 | * Starts the RTP Receiver. This method instantiates a multicast socket on |
| 126 | * the multicast address and port specified and listens for packets on that |
| 127 | * multicast group. When it receives a packet, it parses the packet out and |
| 128 | * updates several session and source level statistics. It also posts an |
| 129 | * event to the Application about the reception of an RTP Packet |
| 130 | */ |
| 131 | |
| 132 | public void run() { |
| 133 | byte buf[] = new byte[1024]; |
| 134 | // DatagramPacket packet = new DatagramPacket(buf, buf.length); |
| 135 | int packetLen; |
| 136 | long lastTime = System.currentTimeMillis(); |
| 137 | |
| 138 | while (!isStopped()) { |
| 139 | packetLen = socket.receiveFrom(buf); |
| 140 | if (packetLen == -1) { |
| 141 | continue; // repeat - only timeout |
| 142 | } |
| 143 | |
| 144 | lastTime = System.currentTimeMillis(); |
| 145 | |
| 146 | if (validateRTPPacketHeader(buf)) { |
| 147 | |
| 148 | RTPPacket rtppkt = new RTPPacket(); |
| 149 | try { |
| 150 | rtppkt.parse(buf, 0, packetLen); |
| 151 | } catch (PacketParseException e) { |
| 152 | logger.warn("Packet parsing failed"); |
| 153 | continue; |
| 154 | } |
| 155 | |
| 156 | final int RTP_PACKET_HEADER_LENGTH = 12; |
| 157 | final int RTP_PACKET_CSRC_LENGTH = 4 * rtppkt.getCSRCcount(); |
| 158 | |
| 159 | // the payload is after the fixed 12 byte header |
| 160 | |
| 161 | // TODO: Neefektivni kod - zlepsit naroky na pamet |
| 162 | byte payload[] = new byte[packetLen - RTP_PACKET_HEADER_LENGTH |
| 163 | - RTP_PACKET_CSRC_LENGTH]; |
| 164 | |
| 165 | for (int i = 0; i < payload.length; i++) { |
| 166 | payload[i] = buf[i + RTP_PACKET_HEADER_LENGTH |
| 167 | + RTP_PACKET_CSRC_LENGTH]; |
| 168 | // System.out.print(" "+payload[i]); |
| 169 | } |
| 170 | |
| 171 | rtppkt.setData(payload, 0, payload.length); |
| 172 | |
| 173 | // Get the source corresponding to this SSRC |
| 174 | Source src = session.getMemberOrCreateMember(rtppkt.getSSRC()); |
| 175 | |
| 176 | if (rtppkt.getSSRC() != session.getOwnSource().ssrc) { // not me |
| 177 | |
| 178 | long curTime = System.currentTimeMillis(); |
| 179 | |
| 180 | // store last received packet |
| 181 | session.lastRTPPktReceived = rtppkt; |
| 182 | src.timeOfLastRTPArrival = curTime; |
| 183 | |
| 184 | // actualize max timestamp seen |
| 185 | if (src.update_seq(rtppkt.getSeqNum(), rtppkt.getTimeStamp())) { |
| 186 | |
| 187 | // if this is the first RTP Packet Received from |
| 188 | // this source |
| 189 | // then store the seq no. as its base |
| 190 | |
| 191 | session.onRTPReceive(rtppkt); |
| 192 | // Source of RTP packet is not yet sender |
| 193 | if (!src.isSender()) { |
| 194 | src.setSender(true); |
| 195 | // add saneder to Senders Table |
| 196 | session.addSender(rtppkt.getSSRC(), rtppkt.getSeqNum()); |
| 197 | // call nottifier |
| 198 | session.onNewSender(src); |
| 199 | } |
| 200 | |
| 201 | // AudioPlayer2 player = |
| 202 | // GuiFactory.getAudioPlayer(); |
| 203 | // player.play(payload, payload.length); |
| 204 | } |
| 205 | // else { |
| 206 | // System.out.println("!!!!!!!!!!!!!!!!! NOT YET |
| 207 | // VALIDATED"+src.ssrc); |
| 208 | // } |
| 209 | } |
| 210 | } else { |
| 211 | InetAddress from; |
| 212 | try { |
| 213 | from = InetAddress.getByAddress(socket.getRemoteAddress()); |
| 214 | logger.warn("Bad RTP Packet received From:" + from + " LEN:" |
| 215 | + packetLen); |
| 216 | // from = InetAddressFactory.newInetAddress( |
| 217 | // socket.getRemoteAddress()).toString(); |
| 218 | } catch (UnknownHostException e) { |
| 219 | // ok |
| 220 | } |
| 221 | } |
| 222 | logger.debug("TIME FOR PROCESSING RTP PACKET: " |
| 223 | + (System.currentTimeMillis() - lastTime)); |
| 224 | } |
| 225 | } |
| 226 | |
| 227 | /** |
| 228 | * Validates RTP Packet. Returns true or false corresponding to the test |
| 229 | * results. |
| 230 | * |
| 231 | * @param packet |
| 232 | * the packet |
| 233 | * |
| 234 | * @return True if validation was successful, False otherwise. |
| 235 | */ |
| 236 | |
| 237 | private boolean validateRTPPacketHeader(byte packet[]) { |
| 238 | // +-+-+-+-+-+-+-+-+ |
| 239 | // |V=2|P|X| CC | |
| 240 | // +-+-+-+-+-+-+-+-+ |
| 241 | |
| 242 | if (packet.length <= 0) { |
| 243 | logger.warn("packet.length <= 0: " + packet.length); |
| 244 | return false; |
| 245 | } |
| 246 | |
| 247 | // Version MUST be 2 |
| 248 | if (((packet[0] & 0xC0) >> 6) != 2) { |
| 249 | // VersionValid = false; |
| 250 | logger.warn("RTP version != 2: " + ((packet[0] & 0xC0) >> 6)); |
| 251 | return false; |
| 252 | } |
| 253 | |
| 254 | // +-+-+-+-+-+-+-+-+ |
| 255 | // |M| PT | |
| 256 | // +-+-+-+-+-+-+-+-+ |
| 257 | // 0 1 0 1 1 0 0 0 |
| 258 | |
| 259 | // Payload Type must be the same as the session's |
| 260 | if ((packet[1] & 0x7F) != session.getPayloadType()) { |
| 261 | logger.warn("PAYLOAD TYPE != " + session.getPayloadType() + "(" |
| 262 | + (packet[1] & 0x7F) + ")"); |
| 263 | |
| 264 | return false; |
| 265 | } |
| 266 | return true; |
| 267 | } |
| 268 | } |