/*
* SIP Communicator, the OpenSource Java VoIP and Instant Messaging client.
*
* Distributable under LGPL license.
* See terms of license at gnu.org.
*/
package net.java.sip.communicator.impl.neomedia;
import java.math.*;
import java.util.*;
/**
* When using TransformConnector, a RTP/RTCP packet is represented using
* RawPacket. RawPacket stores the buffer holding the RTP/RTCP packet, as well
* as the inner offset and length of RTP/RTCP packet data.
*
* After transformation, data is also store in RawPacket objects, either the
* original RawPacket (in place transformation), or a newly created RawPacket.
*
* Besides packet info storage, RawPacket also provides some other operations
* such as readInt() to ease the development process.
*
* @author Werner Dittmann (Werner.Dittmann@t-online.de)
* @author Bing SU (nova.su@gmail.com)
* @author Emil Ivov
*/
public class RawPacket
{
/**
* Byte array storing the content of this Packet
*/
protected byte[] buffer;
/**
* Start offset of the packet data inside buffer.
* Usually this value would be 0. But in order to be compatible with
* RTPManager we store this info. (Not assuming the offset is always zero)
*/
protected int offset;
/**
* Length of this packet's data
*/
protected int length;
/**
* The size of the fixed part of the RTP header as defined by RFC 3550.
*/
public static final int FIXED_HEADER_SIZE = 12;
/**
* Construct a RawPacket using specified value.
*
* @param buffer Byte array holding the content of this Packet
* @param offset Start offset of packet content inside buffer
* @param length Length of the packet's data
*/
public RawPacket(byte[] buffer, int offset, int length)
{
this.buffer = buffer;
this.offset = offset;
this.length = length;
}
/**
* Get buffer containing the content of this packet
*
* @return buffer containing the content of this packet
*/
public byte[] getBuffer()
{
return this.buffer;
}
/**
* Get the length of this packet's data
*
* @return length of this packet's data
*/
public int getLength()
{
return this.length;
}
/**
* Get the start offset of this packet's data inside storing buffer
*
* @return start offset of this packet's data inside storing buffer
*/
public int getOffset()
{
return this.offset;
}
/**
* Read a integer from this packet at specified offset
*
* @param off start offset of the integer to be read
* @return the integer to be read
*/
public int readInt(int off)
{
return (this.buffer[this.offset + off + 0] << 24) |
((this.buffer[this.offset + off + 1] & 0xff) << 16) |
((this.buffer[this.offset + off + 2] & 0xff) << 8) |
(this.buffer[this.offset + off + 3] & 0xff);
}
/**
* Read a short from this packet at specified offset
*
* @param off start offset of this short
* @return short value at offset
*/
public short readShort(int off)
{
return (short) ((this.buffer[this.offset + off + 0] << 8) |
(this.buffer[this.offset + off + 1] & 0xff));
}
/**
* Read an unsigned short at specified offset as a int
*
* @param off start offset of the unsigned short
* @return the int value of the unsigned short at offset
*/
public int readUnsignedShortAsInt(int off)
{
int b1 = (0x000000FF & (this.buffer[this.offset + off + 0]));
int b2 = (0x000000FF & (this.buffer[this.offset + off + 1]));
int val = b1 << 8 | b2;
return val;
}
/**
* Read a byte from this packet at specified offset
*
* @param off start offset of the byte
* @return byte at offset
*/
public byte readByte(int off)
{
return buffer[offset + off];
}
/**
* Read an unsigned integer as long at specified offset
*
* @param off start offset of this unsigned integer
* @return unsigned integer as long at offset
*/
public long readUnsignedIntAsLong(int off)
{
int b0 = (0x000000FF & (this.buffer[this.offset + off + 0]));
int b1 = (0x000000FF & (this.buffer[this.offset + off + 1]));
int b2 = (0x000000FF & (this.buffer[this.offset + off + 2]));
int b3 = (0x000000FF & (this.buffer[this.offset + off + 3]));
return ((b0 << 24 | b1 << 16 | b2 << 8 | b3)) & 0xFFFFFFFFL;
}
/**
* Read a byte region from specified offset with specified length
*
* @param off start offset of the region to be read
* @param len length of the region to be read
* @return byte array of [offset, offset + length)
*/
public byte[] readRegion(int off, int len)
{
int startOffset = this.offset + off;
if (off < 0 || len <= 0
|| startOffset + len > this.buffer.length)
{
return null;
}
byte[] region = new byte[len];
System.arraycopy(this.buffer, startOffset, region, 0, len);
return region;
}
/**
* Read a byte region from specified offset with specified length in given
* buffer
*
* @param off start offset of the region to be read
* @param len length of the region to be read
* @param outBuff output buffer
*/
public void readRegionToBuff(int off, int len, byte[] outBuff)
{
int startOffset = this.offset + off;
if (off < 0 || len <= 0
|| startOffset + len > this.buffer.length)
{
return;
}
if (outBuff.length < len)
{
return;
}
System.arraycopy(this.buffer, startOffset, outBuff, 0, len);
}
/**
* Append a byte array to then end of the packet. This will change the data
* buffer of this packet.
*
* @param data byte array to append
* @param len the number of bytes to append
*/
public void append(byte[] data, int len)
{
if (data == null || len == 0)
{
return;
}
byte[] newBuffer = new byte[this.length + len];
System.arraycopy(this.buffer, this.offset, newBuffer, 0, this.length);
System.arraycopy(data, 0, newBuffer, this.length, len);
this.offset = 0;
this.length = this.length + len;
this.buffer = newBuffer;
}
/**
* Shrink the buffer of this packet by specified length
*
* @param len length to shrink
*/
public void shrink(int len)
{
if (len <= 0)
{
return;
}
this.length -= len;
if (this.length < 0)
{
this.length = 0;
}
}
/**
* Returns the number of CSRC identifiers currently included in this packet.
*
* @return the CSRC count for this RawPacket.
*/
public int getCsrcCount()
{
return (buffer[offset] & 0x0f);
}
/**
* Replaces the existing CSRC list (even if empty) with newCsrcList
* and updates the CC (CSRC count) field of this RawPacket
* accordingly.
*
* @param newCsrcList the list of CSRC identifiers that we'd like to set for
* this RawPacket.
*/
public void setCsrcList(long[] newCsrcList)
{
int newCsrcCount = newCsrcList.length;
byte[] csrcBuff = new byte[newCsrcCount * 4];
int csrcOffset = 0;
for(long csrc : newCsrcList)
{
csrcBuff[csrcOffset] = (byte)(csrc >> 24);
csrcBuff[csrcOffset+1] = (byte)(csrc >> 16);
csrcBuff[csrcOffset+2] = (byte)(csrc >> 8);
csrcBuff[csrcOffset+3] = (byte)csrc;
csrcOffset += 4;
}
int oldCsrcCount = getCsrcCount();
byte[] oldBuffer = this.getBuffer();
//the new buffer needs to be bigger than the new one in order to
//accommodate the list of CSRC IDs (unless there were more of them
//previously than after setting the new list).
byte[] newBuffer
= new byte[oldBuffer.length + csrcBuff.length - oldCsrcCount*4];
//copy the part up to the CSRC list
System.arraycopy(
oldBuffer, 0, newBuffer, 0, offset + FIXED_HEADER_SIZE);
//copy the new CSRC list
System.arraycopy( csrcBuff, 0, newBuffer,
offset + FIXED_HEADER_SIZE, csrcBuff.length);
//now copy the payload from the old buff and make sure we don't copy
//the CSRC list if there was one in the old packet
int payloadOffsetForOldBuff
= offset + FIXED_HEADER_SIZE + oldCsrcCount*4;
int payloadOffsetForNewBuff
= offset + FIXED_HEADER_SIZE + newCsrcCount*4;
System.arraycopy( oldBuffer, payloadOffsetForOldBuff,
newBuffer, payloadOffsetForNewBuff,
oldBuffer.length - payloadOffsetForOldBuff);
//set the new CSRC count
newBuffer[offset] = (byte)((newBuffer[offset] & 0xF0)
| newCsrcCount);
this.buffer = newBuffer;
this.length = newBuffer.length;
}
/**
* Returns the list of CSRC IDs, currently encapsulated in this packet.
*
* @return an array containing the list of CSRC IDs, currently encapsulated
* in this packet.
*/
public long[] extractCsrcList()
{
int csrcCount = getCsrcCount();
byte[] buffer = getBuffer();
long[] csrcList = new long[csrcCount];
int csrcStartIndex = offset + FIXED_HEADER_SIZE;
for (int i = 0; i < csrcCount; i++)
{
csrcList[i] = buffer[csrcStartIndex] << 24
& buffer[csrcStartIndex + 1] << 16
& buffer[csrcStartIndex + 2] << 8
& buffer[csrcStartIndex + 3];
csrcStartIndex += 4;
}
return csrcList;
}
}