/***************************************************************************
* *
* 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 *
* *
***************************************************************************/
/*
* $Id: RtpPacket.java 309 2005-12-01 22:36:28Z merlimat $
*
* $URL: http://svn.berlios.de/svnroot/repos/rtspproxy/tags/3.0-ALPHA2/src/main/java/rtspproxy/rtp/RtpPacket.java $
*
*/
package video.lib;
import org.apache.mina.core.buffer.IoBuffer;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
/**
* This class wraps a RTP packet providing method to convert from and to a
* {@link ByteBuffer}.
* <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 final Logger LOGGER = 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 = {};
private int packetSize;
/**
* Construct a new RtpPacket reading the fields from a ByteBuffer
*
* @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 |
version = (byte) ((c & 0xC0) >> 6);
padding = ((c & 0x20) >> 5) == 1;
extension = ((c & 0x10) >> 4) == 1;
csrcCount = (byte) (c & 0x0F);
c = buffer.get();
// |M=1| PT=7 |
marker = ((c & 0x80) >> 7) == 1;
payloadType = new UnsignedByte(c & 0x7F);
sequence = new UnsignedShort(buffer.getShort());
timestamp = new UnsignedInt(buffer.getInt());
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) {
profileExtension = buffer.getShort();
int length = buffer.getShort();
headerExtension = new byte[length];
buffer.get(headerExtension);
}
// Read the payload
int payloadSize = buffer.remaining();
payload = new byte[payloadSize];
buffer.get(payload);
if (version != 2) {
LOGGER.debug("Packet Version is not 2.");
}
packetSize = buffer.capacity();
}
protected RtpPacket() {
// Creates an empty packet
}
/**
* Convert the packet instance into a {@link ByteBuffer} ready to be sent.
*
* @return a new ByteBuffer
*/
public IoBuffer toIoBuffer() {
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;
}
@Override
public int size() {
return packetSize;
}
}