diff -r 537ddd8aa407 -r 2036ebfaccda src/jlibrtp/RtpPkt.java --- /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