在Java中填充字节数组

5

我正在处理的项目中,需要实现RTP数据包,在这个数据包中,我需要用RTP头字段填充字节数组的头部。

  //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 ];

这是我的方法:
/*      
 * 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();

有其他更好的方法吗?也许可以这样做。

2
不要创建一个对象只是为了从中提取一个原始类型。这样做既丑陋又低效。 - Peter Lawrey
5个回答

6

我认为我会使用 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);

2
在Java中,您可以直接将一个int转换为byte,无需创建一个Integer对象。因为byte的可能值范围比int更小,所以需要进行显式转换。例如:
header[1] = (byte) (Marker << 7 | PayloadType);

0

使用Preon,RtpHeader可以表示为:

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; 

}

将其编码为字节可能就像这样简单:

    Codec<RtpHeader> codec = Codecs.create(RtpHeader.class);
    RtpHeader header = new RtpHeader();
    ... // Setting header values
    OutputStream out = ...;
    Codecs.encode(header, codec, out);

然而,请记住 Preon 中的编码仍处于早期阶段。对于这种特定情况,它似乎是有效的,但我不会做出任何保证。

使用 Preon 的好处显然在于你不必费心去编写所有编解码逻辑。


0

这种数据存在一个问题。通常协议使用无符号字节,而Java使用有符号字节。因此,为了正确填充字节数组,我通常使用以下结构:

bytearray[index] = (byte) ((some integer-result calculation) & 0xff);

简单的强制类型转换为字节类型将无法正确工作。

更新。这里不需要"& 0xff"。简单的强制转换即可。


2
真的吗?看一下 Integer#byteValue() 的源代码:/** * 返回此 Integer 对象表示值的byte值。 */ public byte byteValue() { return (byte)value; } - rhu
1
抱歉,我的错误。这是一种相反的转换,需要 (UByte & 0xff) -> 正确的整数。当我们从整数转换为字节时,直接进行字节强制转换即可。 - bealex

0

除了提供的答案外,也可以尝试一下Preon


网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接