package org.red5.server.net.rtp; /* * RED5 Open Source Flash Server - http://www.osflash.org/red5 * * Copyright (c) 2006-2008 by respective authors (see below). All rights reserved. * * 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., * 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */ /*************************************************************************** * * * This program 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 2 of the License, or * * (at your option) any later version. * * * * Copyright (C) 2005 - Matteo Merli - matteo.merli@gmail.com * * * ***************************************************************************/ import org.apache.mina.core.buffer.IoBuffer; import org.red5.io.object.UnsignedByte; import org.red5.io.object.UnsignedInt; import org.red5.io.object.UnsignedShort; import org.slf4j.Logger; import org.slf4j.LoggerFactory; /** * This class wraps a RTP packet providing method to convert from and to a * {@link IoBuffer}. * <p> * A RTP packet is composed of an header and the subsequent payload. * <p> * The RTP header has the following format: * * <pre> * 0 1 2 3 * 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ * |V=2|P|X| CC |M| PT | sequence number | * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ * | timestamp | * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ * | synchronization source (SSRC) identifier | * +=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+ * | contributing source (CSRC) identifiers | * | .... | * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ * </pre> * * The first twelve octets are present in every RTP packet, while the list of * CSRC identifiers is present only when inserted by a mixer. * * @author Matteo Merli */ public class RTPPacket implements Packet { private static Logger log = LoggerFactory.getLogger(RTPPacket.class); /** * This field identifies the version of RTP. The version defined by this * specification is two (2). (The value 1 is used by the first draft version * of RTP and the value 0 is used by the protocol initially implemented in * the "vat" audio tool.) */ private byte version; /** * Padding flag. If the padding bit is set, the packet contains one or more * additional padding octets at the end which are not part of the payload. * The last octet of the padding contains a count of how many padding octets * should be ignored, including itself. Padding may be needed by some * encryption algorithms with fixed block sizes or for carrying several RTP * packets in a lower-layer protocol data unit. */ private boolean padding; /** * Extension Flag. If the extension bit is set, the fixed header MUST be * followed by exactly one header extension, with a format defined in * Section 5.3.1 of the RFC. */ private boolean extension; /** * The CSRC count contains the number of CSRC identifiers that follow the * fixed header. */ private byte csrcCount; /** * The interpretation of the marker is defined by a profile. It is intended * to allow significant events such as frame boundaries to be marked in the * packet stream. A profile MAY define additional marker bits or specify * that there is no marker bit by changing the number of bits in the payload * type field (see Section 5.3). */ private boolean marker; /** * This field identifies the format of the RTP payload and determines its * interpretation by the application. A profile MAY specify a default static * mapping of payload type codes to payload formats. Additional payload type * codes MAY be defined dynamically through non-RTP means (see Section 3). * <p> * A set of default mappings for audio and video is specified in the * companion RFC 3551 [1]. An RTP source MAY change the payload type during * a session, but this field SHOULD NOT be used for multiplexing separate * media streams (see Section 5.2). */ private UnsignedByte payloadType; /** * The sequence number increments by one for each RTP data packet sent, and * may be used by the receiver to detect packet loss and to restore packet * sequence. The initial value of the sequence number SHOULD be random * (unpredictable) to make known-plaintext attacks on encryption more * difficult, even if the source itself does not encrypt according to the * method in Section 9.1, because the packets may flow through a translator * that does. */ private UnsignedShort sequence; /** * The timestamp reflects the sampling instant of the first octet in the RTP * data packet. The sampling instant MUST be derived from a clock that * increments monotonically and linearly in time to allow synchronization * and jitter calculations (see Section 6.4.1). */ private UnsignedInt timestamp; /** * The SSRC field identifies the synchronization source. This identifier * SHOULD be chosen randomly, with the intent that no two synchronization * sources within the same RTP session will have the same SSRC identifier. */ private UnsignedInt ssrc; /** * The CSRC list identifies the contributing sources for the payload * contained in this packet. The number of identifiers is given by the CC * field. If there are more than 15 contributing sources, only 15 can be * identified. */ private UnsignedInt[] csrc = {}; private short profileExtension; private byte[] headerExtension = {}; /** * Content of the packet. */ private byte[] payload = {}; /** * Construct a new RTPPacket reading the fields from a IoBuffer * * @param buffer * the buffer containing the packet */ public RTPPacket(IoBuffer buffer) { // Read the packet header byte c = buffer.get(); // |V=2|P=1|X=1| CC=4 | this.version = (byte) ((c & 0xC0) >> 6); this.padding = ((c & 0x20) >> 5) == 1; this.extension = ((c & 0x10) >> 4) == 1; this.csrcCount = (byte) (c & 0x0F); c = buffer.get(); // |M=1| PT=7 | this.marker = ((c & 0x80) >> 7) == 1; this.payloadType = new UnsignedByte(c & 0x7F); this.sequence = new UnsignedShort(buffer.getShort()); this.timestamp = new UnsignedInt(buffer.getInt()); this.ssrc = new UnsignedInt(buffer.getInt()); // CSRC list csrc = new UnsignedInt[csrcCount]; for (int i = 0; i < csrcCount; i++) { csrc[i] = new UnsignedInt(buffer.getInt()); } // Read the extension header if present if (extension) { this.profileExtension = buffer.getShort(); int length = buffer.getShort(); this.headerExtension = new byte[length]; buffer.get(this.headerExtension); } // Read the payload int payloadSize = buffer.remaining(); this.payload = new byte[payloadSize]; buffer.get(payload); if (version != 2) { log.debug("Packet Version is not 2."); } } protected RTPPacket() { // Creates an empty packet } /** * Convert the packet instance into a {@link IoBuffer} ready to be sent. * * @return a new IoBuffer */ public IoBuffer toByteBuffer() { int packetSize = 12 + csrc.length * 4 + payload.length; if (extension) packetSize += headerExtension.length; IoBuffer buffer = IoBuffer.allocate(packetSize); buffer.limit(packetSize); byte c = 0x00; int bPadding = padding ? 1 : 0; int bExtension = extension ? 1 : 0; c = (byte) ((version << 6) | (bPadding << 5) | (bExtension << 4) | csrcCount); buffer.put(c); int bMarker = marker ? 1 : 0; c = (byte) ((bMarker << 7) | payloadType.intValue()); buffer.put(c); buffer.put(sequence.getBytes()); buffer.put(timestamp.getBytes()); buffer.put(ssrc.getBytes()); for (byte i = 0; i < Math.min(csrcCount, csrc.length); i++) { buffer.put(csrc[i].getBytes()); } // Write the extension header if present if (extension) { buffer.putShort(profileExtension); buffer.putShort((short) headerExtension.length); buffer.put(headerExtension); } buffer.put(payload); buffer.rewind(); return buffer; } /** * @return Returns the csrc. */ public UnsignedInt[] getCsrc() { return csrc; } /** * @param csrc * The csrc to set. */ public void setCsrc(UnsignedInt[] csrc) { this.csrc = csrc; } /** * @return Returns the csrcCount. */ public byte getCsrcCount() { return csrcCount; } /** * @param csrcCount * The csrcCount to set. */ public void setCsrcCount(byte csrcCount) { this.csrcCount = csrcCount; } /** * @return Returns the extension. */ public boolean isExtension() { return extension; } /** * @param extension * The extension to set. */ public void setExtension(boolean extension) { this.extension = extension; } /** * @return Returns the marker. */ public boolean isMarker() { return marker; } /** * @param marker * The marker to set. */ public void setMarker(boolean marker) { this.marker = marker; } /** * @return Returns the padding. */ public boolean isPadding() { return padding; } /** * @param padding * The padding to set. */ public void setPadding(boolean padding) { this.padding = padding; } /** * @return Returns the payload. */ public byte[] getPayload() { return payload; } /** * @param payload * The payload to set. */ public void setPayload(byte[] payload) { this.payload = payload; } /** * @return Returns the payloadType. */ public UnsignedByte getPayloadType() { return payloadType; } /** * @param payloadType * The payloadType to set. */ public void setPayloadType(UnsignedByte payloadType) { this.payloadType = payloadType; } /** * @return Returns the sequence. */ public UnsignedShort getSequence() { return sequence; } /** * @param sequence * The sequence to set. */ public void setSequence(UnsignedShort sequence) { this.sequence = sequence; } /** * @return Returns the ssrc. */ public UnsignedInt getSsrc() { return ssrc; } /** * @param ssrc * The ssrc to set. */ public void setSsrc(UnsignedInt ssrc) { this.ssrc = ssrc; } /** * @return Returns the timestamp. */ public UnsignedInt getTimestamp() { return timestamp; } /** * @param timestamp * The timestamp to set. */ public void setTimestamp(UnsignedInt timestamp) { this.timestamp = timestamp; } /** * @return Returns the version. */ public byte getVersion() { return version; } /** * @param version * The version to set. */ public void setVersion(byte version) { this.version = version; } }