3

我学习了一些在小型网络中使用 voip over udp 的知识。我知道有很多库可以做,并且通过一些方法调用来完成我所需要的一切,但正如我所说我正在学习,所以需要重新发明轮子来看看它是如何工作的。

我目前正在研究 DatagramPacket 类,我注意到没有方法可以在 DatagramPacket 类中设置标头信息(即我需要知道进行交错的数据包顺序序列号)。

反映环境的一点代码:

byte[] block;
DatagramPacket packet; // UDP packet                

/* x Bytes per block , y blocks per second,
   z ms time block playback duration */

block = recorder.getBlock(); // assume I have class that handles audio
                              // recording and returns speech in a
                              // uncompressed form of bytes

packet = new DatagramPacket(block, block.length, clientIP, PORT);

首先,我假设因为它是 UDP,所以发送者并不真正关心任何事情,除了他将数据包扔到某个地方的简单事实。所以这就是为什么里面没有这样的方法。

其次,我假设我需要自己做 - 在要发送的字节块中添加额外的字节,其中包含数据包的序列号?但是我也担心如果我这样做,那么我如何识别字节是否是标题字节而不是音频字节?我可以假设第一个字节代表一个数字,但是我们知道这个字节只能代表 258 个数字。我以前从未真正在字节级别上工作过。或者可能还有其他技术?

简而言之,要进行交错,我需要知道如何设置数据包序列号,因为我无法订购无序数据包:-)

谢谢你,

4

1 回答 1

6

您需要将程序使用的数据类型序列化/反序列化到字节数组中。

假设您正在谈论RTP,并且您想发送带有这些字段的数据包 - 请查看 RTP 规范中的第 5 章:

版本 = 2 填充 = 0 扩展 = 0 CSRC 计数 = 1 标记 = 0 有效载荷类型 = 8 (G711 alaw) 序列号 = 1234 时间戳 = 1 一个 CSRC = 4321

让我们将它们放入一些变量中,为方便起见使用整数,或者当我们需要处理无符号 32 位值时使用 long:

int version = 2;
int padding = 0;
int extension = 0;
int csrcCount = 1;
int marker = 0;
int payloadType = 8;
int sequenceNumber = 1234;
long timestamp = 1;
long ourCsrc = 4321;

byte buf[] = ...; //allocate this big enough to hold the RTP header + audio data

//assemble the first bytes according to the RTP spec (note, the spec marks version as bit 0 and 1, but
//this is really the high bits of the first byte ...
buf[0] = (byte) ((version & 0x3) << 6 | (padding & 0x1) << 5 | (extension & 0x1) << 4 | (csrcCount & 0xf));

//2.byte
buf[1] = (byte)((marker & 0x1) << 7 | payloadType & 0x7f);

//squence number, 2 bytes, in big endian format. So the MSB first, then the LSB.
buf[2] = (byte)((sequenceNumber & 0xff00) >> 8);
buf[3] = (byte)(sequenceNumber  & 0x00ff);

//packet timestamp , 4 bytes in big endian format
buf[4] = (byte)((timestamp & 0xff000000) >> 24);
buf[5] = (byte)((timestamp & 0x00ff0000) >> 16);
buf[6] = (byte)((timestamp & 0x0000ff00) >> 8);
buf[7] = (byte) (timestamp & 0x000000ff);
//our CSRC , 4 bytes in big endian format
buf[ 8] = (byte)((sequenceNumber & 0xff000000) >> 24);
buf[ 9] = (byte)((sequenceNumber & 0x00ff0000) >> 16);
buf[10] = (byte)((sequenceNumber & 0x0000ff00) >> 8);
buf[11] = (byte) (sequenceNumber & 0x000000ff);

那是标头,现在您可以将音频字节复制到buf,从开始buf[12]buf作为一个数据包发送。

现在,上面当然只是为了展示原理,根据 RTP 规范,RTP 数据包的实际序列化器必须处理更多(例如,您可能需要一些扩展标头,您可能需要多个 CSRC,根据您拥有的音频数据的格式,您需要正确的有效负载类型,您需要正确打包和安排这些音频数据 - 例如对于 G.711Alaw,您应该用 160 字节的音频数据填充每个 RTP 数据包并发送一个每 20 毫秒一个数据包。

于 2012-02-01T15:48:55.323 回答