/**
* Java RTP Library (jlibrtp)
* Copyright (C) 2006 Arne Kepp
*
* 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., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
*/
package jlibrtp;
import java.net.DatagramPacket;
/**
* RtpPkt is the basic class for creating and parsing RTP packets.
*
* There are two ways of instantiating an RtpPkt. One is for packets that you
* wish to send, which requires that you provide basic information about the
* packet and a payload. Upon calling encode() the fields of the structure are
* written into a bytebuffer, in the form that it would sent across the network,
* excluding the UDP headers.
*
* The other way is by passing a bytebuffer. The assumption is that this is a
* packet that has been received from the network, excluding UDP headers, and
* the bytebuffer will be parsed into the correct fields.
*
* The class keeps track of changes. Therefore, modifications are possible after
* calling encode(), if necessary, the raw version of the packet will be
* regenerated on subsequent requests.
*
* @author Arne Kepp
*/
public class RtpPkt {
/** Whether the packet has been changed since encode() */
private boolean rawPktCurrent = false;
/** The version, always 2, 2 bits */
private int version = 2; // 2 bits
/** Whether the packet is padded, 1 bit */
private int padding; // 1 bit
/** Whether and extension is used, 1 bit */
private int extension = 0; // 1 bit
/** Whether the packet is marked, 1 bit */
private int marker = 0; // 1 bit
/** What payload type is used, 7 bits */
private int payloadType; //
/** The sequence number, taken from RTP Session, 16 bits */
private int seqNumber; // 16 bits
/** The RTP timestamp, 32bits */
private long timeStamp; // 32 bits
/** The SSRC of the packet sender, 32 bits */
private long ssrc; // 32 bits
/** SSRCs of contributing sources, 32xn bits, n<16 */
private long[] csrcArray = new long[0];//
/** Contains the actual data (eventually) */
private byte[] rawPkt = null;
/** The actual data, without any RTP stuff */
//private byte[] payload = null;
private DatagramPacket datagramPacket;
/**
* Construct a packet-instance. The ByteBuffer required for UDP transmission
* can afterwards be obtained from getRawPkt(). If you need to set
* additional parameters, such as the marker bit or contributing sources,
* you should do so before calling getRawPkt;
*
* @param aTimeStamp
* RTP timestamp for data
* @param syncSource
* the SSRC, usually taken from RTPSession
* @param seqNum
* Sequency number
* @param plt
* Type of payload
* @param pl
* Payload, the actual data
*/
protected void initPacket(long aTimeStamp, long syncSource, int seqNum, int plt,
byte[] pl) {
int test = 0;
test += setTimeStamp(aTimeStamp);
test += setSsrc(syncSource);
test += setSeqNumber(seqNum);
test += setPayloadType(plt);
//test += setPayload(pl); //TODO: faire d'une manière propre
datagramPacket = null;
if (test != 0) {
System.out.println("RtpPkt() failed, check with checkPkt()");
}
rawPktCurrent = true;
if (RTPSession.rtpDebugLevel > 5) {
System.out
.println("<--> RtpPkt(aTimeStamp, syncSource, seqNum, plt, pl)");
}
}
/**
* Construct a packet-instance from an raw packet (believed to be RTP). The
* UDP-headers must be removed before invoking this method. Call checkPkt on
* the instance to verify that it was successfully parsed.
*
* @param aRawPkt
* The data-part of a UDP-packet believed to be RTP
* @param packetSize
* the number of valid octets in the packet, should be
* aRawPkt.length
*/
public RtpPkt(byte[] aRawPkt, int packetSize, DatagramPacket packet) {
initPacket(aRawPkt, packetSize, packet);
}
public RtpPkt() {
}
public RtpPkt(long aTimeStamp, long syncSource, int seqNum, int plt,
byte[] pl) {
initPacket(aTimeStamp, syncSource, seqNum, plt, pl);
}
protected void initPacket(byte[] aRawPkt, int packetSize, DatagramPacket packet) {
if (RTPSession.rtpDebugLevel > 5) {
System.out.println("-> RtpPkt(aRawPkt)");
}
// Check size, need to have at least a complete header
if (aRawPkt == null) {
System.out.println("RtpPkt(byte[]) Packet null");
}
int remOct = packetSize - 12;
if (remOct >= 0) {
rawPkt = aRawPkt; // Store it
// Interrogate the packet
datagramPacket = packet;
sliceFirstLine();
if (version == 2) {
sliceTimeStamp();
sliceSSRC();
if (remOct > 4 && getCsrcCount() > 0) {
sliceCSRCs();
remOct -= csrcArray.length * 4; // 4 octets per CSRC
}
// TODO Extension
/*if (remOct > 0) {
slicePayload(remOct);
}*/
// Sanity checks
checkPkt();
// Mark the buffer as current
rawPktCurrent = true;
} else {
System.out
.println("RtpPkt(byte[]) Packet is not version 2, giving up.");
}
} else {
System.out.println("RtpPkt(byte[]) Packet too small to be sliced");
}
rawPktCurrent = true;
if (RTPSession.rtpDebugLevel > 5) {
System.out.println("<- RtpPkt(aRawPkt)");
}
}
/*********************************************************************************************************
* Reading stuff
*********************************************************************************************************/
protected int checkPkt() {
// TODO, check for version 2 etc
return 0;
}
protected int getHeaderLength() {
// TODO include extension
return 12 + 4 * getCsrcCount();
}
protected int getPayloadLength() {
return rawPkt.length - getHeaderLength();
}
// public int getPaddingLength() {
// return lenPadding;
// }
protected int getVersion() {
return version;
}
// public boolean isPadded() {
// if(lenPadding > 0) {
// return true;
// }else {
// return false;
// }
// }
// public int getHeaderExtension() {
// TODO
// }
protected boolean isMarked() {
return (marker != 0);
}
protected int getPayloadType() {
return payloadType;
}
public int getSeqNumber() {
return seqNumber;
}
protected long getTimeStamp() {
return timeStamp;
}
protected long getSsrc() {
return ssrc;
}
protected int getCsrcCount() {
if (csrcArray != null) {
return csrcArray.length;
} else {
return 0;
}
}
protected long[] getCsrcArray() {
return csrcArray;
}
/**
* Encodes the a
*/
protected byte[] encode() {
if (!rawPktCurrent || rawPkt == null) {
writePkt();
android.util.Log.d("RtpPkt", "writePkt");
}
return rawPkt;
}
/* For debugging purposes */
protected void printPkt() {
System.out
.print("V:" + version + " P:" + padding + " EXT:" + extension);
System.out.println(" CC:" + getCsrcCount() + " M:" + marker + " PT:"
+ payloadType + " SN: " + seqNumber);
System.out.println("Timestamp:" + timeStamp
+ "(long output as int, may be 2s complement)");
System.out.println("SSRC:" + ssrc
+ "(long output as int, may be 2s complement)");
for (int i = 0; i < getCsrcCount(); i++) {
System.out.println("CSRC:" + csrcArray[i]
+ "(long output as int, may be 2s complement)");
}
}
/*********************************************************************************************************
* Setting stuff
*********************************************************************************************************/
protected void setMarked(boolean mark) {
rawPktCurrent = false;
if (mark) {
marker = 1;
} else {
marker = 0;
}
}
// public int setHeaderExtension() {
// TODO
// }
public int setPayloadType(int plType) {
int temp = (plType & 0x0000007F); // 7 bits, checks in RTPSession as
// well.
if (temp == plType) {
rawPktCurrent = false;
payloadType = temp;
return 0;
} else {
return -1;
}
}
protected int setSeqNumber(int number) {
if (number <= 65536 && number >= 0) {
rawPktCurrent = false;
seqNumber = number;
return 0;
} else {
System.out.println("RtpPkt.setSeqNumber: invalid number");
return -1;
}
}
protected int setTimeStamp(long time) {
rawPktCurrent = false;
timeStamp = time;
return 0; // Naive for now
}
protected int setSsrc(long source) {
rawPktCurrent = false;
ssrc = source;
return 0; // Naive for now
}
protected int setCsrcs(long[] contributors) {
if (contributors.length <= 16) {
csrcArray = contributors;
return 0;
} else {
System.out
.println("RtpPkt.setCsrcs: Cannot have more than 16 CSRCs");
return -1;
}
}
/*protected int setPayload(byte[] data) {
// TODO Padding
if (data.length < (1500 - 12)) {
rawPktCurrent = false;
payload = data;
return 0;
} else {
System.out
.println("RtpPkt.setPayload: Cannot carry more than 1480 bytes for now.");
return -1;
}
}*/
public byte[] getPayload() {
return rawPkt;
}
/*********************************************************************************************************
* Private functions
*********************************************************************************************************/
// Generate a bytebyffer representing the packet, store it.
private void writePkt() {
int bytes = getPayloadLength();
int headerLen = getHeaderLength();
int csrcLen = getCsrcCount();
rawPkt = new byte[headerLen + bytes];
// The first line contains, version and various bits
writeFirstLine();
byte[] someBytes = StaticProcs.uIntLongToByteWord(timeStamp);
for (int i = 0; i < 4; i++) {
rawPkt[i + 4] = someBytes[i];
}
// System.out.println("writePkt timeStamp:" + rawPkt[7]);
someBytes = StaticProcs.uIntLongToByteWord(ssrc);
System.arraycopy(someBytes, 0, rawPkt, 8, 4);
// System.out.println("writePkt ssrc:" + rawPkt[11]);
for (int i = 0; i < csrcLen; i++) {
someBytes = StaticProcs.uIntLongToByteWord(csrcArray[i]);
System.arraycopy(someBytes, 0, rawPkt, 12 + 4 * i, 4);
}
// TODO Extension
// Payload
//System.arraycopy(payload, 0, rawPkt, headerLen, bytes);
rawPktCurrent = true;
}
void writeHeader() {
//int bytes = rawPkt.length - 12;
//int headerLen = getHeaderLength();
int csrcLen = getCsrcCount();
// The first line contains, version and various bits
writeFirstLine();
StaticProcs.uIntLongToByteWord(timeStamp, rawPkt, 4);
// System.out.println("writePkt timeStamp:" + rawPkt[7]);
StaticProcs.uIntLongToByteWord(ssrc, rawPkt, 8);
//System.arraycopy(someBytes, 0, rawPkt, 8, 4);
// System.out.println("writePkt ssrc:" + rawPkt[11]);
for (int i = 0; i < csrcLen; i++) {
StaticProcs.uIntLongToByteWord(csrcArray[i], rawPkt, 12 + 4 * i);
//System.arraycopy(someBytes, 0, rawPkt, 12 + 4 * i, 4);
}
// TODO Extension
// Payload
//System.arraycopy(payload, 0, rawPkt, headerLen, bytes);
rawPktCurrent = true;
}
// Writes the first 4 octets of the RTP packet
protected void writeFirstLine() {
byte aByte = 0;
aByte |= (version << 6);
aByte |= (padding << 5);
aByte |= (extension << 4);
aByte |= (getCsrcCount());
rawPkt[0] = aByte;
aByte = 0;
aByte |= (marker << 7);
aByte |= payloadType;
rawPkt[1] = aByte;
StaticProcs.uIntIntToByteWord(seqNumber, rawPkt, 2);
}
// Picks apart the first 4 octets of an RTP packet
private void sliceFirstLine() {
version = ((rawPkt[0] & 0xC0) >>> 6);
padding = ((rawPkt[0] & 0x20) >>> 5);
extension = ((rawPkt[0] & 0x10) >>> 4);
//csrcArray = new long[(rawPkt[0] & 0x0F)];
marker = ((rawPkt[1] & 0x80) >> 7);
payloadType = (rawPkt[1] & 0x7F);
seqNumber = StaticProcs.bytesToUIntInt(rawPkt, 2);
}
// Takes the 4 octets representing the timestamp
private void sliceTimeStamp() {
timeStamp = StaticProcs.bytesToUIntLong(rawPkt, 4);
}
// Takes the 4 octets representing the SSRC
private void sliceSSRC() {
ssrc = StaticProcs.bytesToUIntLong(rawPkt, 8);
}
// Check the length of the csrcArray (set during sliceFirstLine)
private void sliceCSRCs() {
for (int i = 0; i < csrcArray.length; i++) {
ssrc = StaticProcs.bytesToUIntLong(rawPkt, i * 4 + 12);
}
}
// Extensions //TODO
/*private void slicePayload(int bytes) {
payload = new byte[bytes];
int headerLen = getHeaderLength();
System.arraycopy(rawPkt, headerLen, payload, 0, bytes);
}*/
public void setRawPkt(byte[] rawPkt) {
this.rawPkt = rawPkt;
}
public DatagramPacket getDatagramPacket() {
return datagramPacket;
}
}