Filling a byte array in Java
Question
For part of a project I'm working on I am implementing a RTPpacket where I have to fill the header array of byte with RTP header fields.
//size of the RTP header:
static int HEADER_SIZE = 12; // bytes
//Fields that compose the RTP header
public int Version; // 2 bits
public int Padding; // 1 bit
public int Extension; // 1 bit
public int CC; // 4 bits
public int Marker; // 1 bit
public int PayloadType; // 7 bits
public int SequenceNumber; // 16 bits
public int TimeStamp; // 32 bits
public int Ssrc; // 32 bits
//Bitstream of the RTP header
public byte[] header = new byte[ HEADER_SIZE ];
This was my approach:
/*
* bits 0-1: Version
* bit 2: Padding
* bit 3: Extension
* bits 4-7: CC
*/
header[0] = new Integer( (Version << 6)|(Padding << 5)|(Extension << 6)|CC ).byteValue();
/*
* bit 0: Marker
* bits 1-7: PayloadType
*/
header[1] = new Integer( (Marker << 7)|PayloadType ).byteValue();
/* SequenceNumber takes 2 bytes = 16 bits */
header[2] = new Integer( SequenceNumber >> 8 ).byteValue();
header[3] = new Integer( SequenceNumber ).byteValue();
/* TimeStamp takes 4 bytes = 32 bits */
for ( int i = 0; i < 4; i++ )
header[7-i] = new Integer( TimeStamp >> (8*i) ).byteValue();
/* Ssrc takes 4 bytes = 32 bits */
for ( int i = 0; i < 4; i++ )
header[11-i] = new Integer( Ssrc >> (8*i) ).byteValue();
Any other, maybe 'better' ways to do this?
Solution
I think I would use a ByteBuffer
ByteBuffer buf = ByteBuffer.wrap(header);
buf.setOrder(ByteOrder.BIG_ENDIAN);
buf.put((byte)((Version << 6)|(Padding << 5)|(Extension << 6)|CC));
buf.put((byte)((Marker << 7)|PayloadType));
buf.put((short)SequenceNumber);
buf.put(TimeStamp);
buf.put(Ssrc);
OTHER TIPS
You can convert an int
directly to a byte
in Java, without having to create an Integer
object. An explicit cast is required, because a byte
has a narrower range of possible values than an int
. For instance:
header[1] = (byte) (Marker << 7 | PayloadType);
There is one problem with such data. Usually protocols use unsigned bytes there and Java has signed bytes. So, for correct byte array fill I usually use such construct:
bytearray[index] = (byte) ((some integer-result calculation) & 0xff);
Simple casting to byte type won't work correctly.
Update. "& 0xff" is not needed here. Simple cast will work.
in addition to the presented answers give a try to Preon
With Preon, the RtpHeader could be represented like this:
public class RtpHeader {
@BoundNumber(size = "2")
public int version;
@Bound
public boolean padding;
@Bound
public boolean extension;
@BoundNumber(size="4")
public int csrcCount;
@Bound
public boolean marker;
@BoundNumber(size="7")
public int payloadType;
@BoundNumber(size="16", byteOrder = ByteOrder.BigEndian)
public int sequenceNumber;
@BoundNumber(size="32", byteOrder = ByteOrder.BigEndian)
public int timestamp;
@BoundNumber(size="32", byteOrder = ByteOrder.BigEndian)
public int synchronizationSource;
@BoundList(size="csrcCount")
public int[] csrcs;
}
Encoding this to bytes could be as simple as this:
Codec<RtpHeader> codec = Codecs.create(RtpHeader.class);
RtpHeader header = new RtpHeader();
... // Setting header values
OutputStream out = ...;
Codecs.encode(header, codec, out);
However, bear in mind that encoding in Preon is still in early stages. It seems to work, for this particular case, but I am not going to make any guarantees.
The benefit of using Preon is obviously the fact that you don't have to bother writing all of the encoding and decoding logic yourself.