--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/src/jlibrtp/RtpPkt.java Fri Nov 20 19:29:42 2009 +0100
@@ -0,0 +1,465 @@
+/**
+ * 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;
+ }
+}
\ No newline at end of file