RTP的封装类RawPacket源码解析
RawPacket类是jitsi封装的一个jar包,里面包含了关于RTP和RTCP的一些基本操作。
目录
3、isPacketMarked 标志位M是否为true(是否是一帧的结束)
8、getPayloadType 获取payloadType
10、getSequenceNumber 获取RTP的sequence number
一、依赖包
<dependency>
<groupId>org.jitsi</groupId>
<artifactId>libjitsi</artifactId>
<version>1.0-20190405.175243-382</version>
</dependency>
二、RTP/RTCP基本结构
RTP结构:
1、 V:RTP协议的版本号,占2位,当前协议版本号为2
2、 P:填充标志,占1位,如果P=1,则在该报文的尾部填充一个或多个额外的八位组,它们不是有效载荷的一部分。
3、 X:扩展标志,占1位,如果X=1,则在RTP报头后跟有一个扩展报头
4、 CC:CSRC计数器,占4位,指示CSRC 标识符的个数
5、 M: 标记,占1位,不同的有效载荷有不同的含义,对于视频,标记一帧的结束;对于音频,标记会话的开始。视频帧比较大,需要通过多个NALU来传输,当看到M位为1时就认为是这个I帧的结束,由于音频帧比较小,一个RTP包就是一个音频帧,所以该位直接置1。
6、 PT: 有效荷载类型,占7位,用于说明RTP报文中有效载荷的类型,如GSM音频、JPEM图像等,在流媒体中大部分是用来区分音频流和视频流的,这样便于客户端进行解析。
7、 序列号:占16位,用于标识发送者所发送的RTP报文的序列号,每发送一个报文,序列号增1。这个字段当下层的承载协议用UDP的时候,网络状况不好的时候可以用来检查丢包。同时出现网络抖动的情况可以用来对数据进行重新排序,序列号的初始值是随机的,同时音频包和视频包的sequence是分别记数的。
8、 时戳(Timestamp):占32位,必须使用90 kHz 时钟频率。时戳反映了该RTP报文的第一个八位组的采样时刻。接收者使用时戳来计算延迟和延迟抖动,并进行同步控制。不是真实的时间。需要说明的是,一个视频帧的时间戳是相同的,但是一个视频帧数据量很大可能需要多个RTP包传输,这样就存在多个RTP包时间戳相同的情况,音频帧数据小,不存在音频帧跨RTP的情况,所以不存在这个问题。
9、 同步信源(SSRC)标识符:占32位,用于标识同步信源。该标识符是随机选择的,参加同一视频会议的两个同步信源不能有相同的SSRC。
10、 特约信源(CSRC)标识符:每个CSRC标识符占32位,可以有0~15个。每个CSRC标识了包含在该RTP报文有效载荷中的所有特约信源。
RTCP结构:
三、相关成员变量
//扩展头长度
public static final int EXT_HEADER_SIZE = 4;
//RTP固定头长度
public static final int FIXED_HEADER_SIZE = 12;
//一个有效RTCP包的最小长度
private static final int RTCP_MIN_SIZE = 8;
//存储包内容
private byte[] buffer;
//包的长度
private int length;
//偏移量,通常为0
private int offset;
四、相关成员方法
1、makeRTP创建RTP包
public static RawPacket makeRTP(
long ssrc, int pt, int seqNum, long ts, int len)
{
byte[] buf = new byte[len];
RawPacket pkt = new RawPacket(buf, 0, buf.length);
pkt.setVersion();
pkt.setPayloadType((byte) pt);
pkt.setSSRC((int) ssrc);
pkt.setTimestamp(ts);
pkt.setSequenceNumber(seqNum);
pkt.setPaddingSize(len - FIXED_HEADER_SIZE);
return pkt;
}
根据传入的参数来构造一个RTP包。接下来看每一个具体的方法。
boolean setVersion()
public boolean setVersion()
{
if (isInvalid())
{
return false;
}
//因为offset为0,0x80的二进制是0b1000_0000, |或运算,将第一位设置成1,所以版本号固定为2
buffer[offset] |= 0x80;
return true;
}
void setPayloadType(byte payload)
public void setPayloadType(byte payload)
{
//0x7F, 二进制是0b0111_1111, 按位与也就是取payload的低7位
payload &= (byte)0x7F;
//offset始终是0.
//从前面的图中可以看出,payloadType处于第二个字节的后7位。buffer[offset + 1]&0x80是先
//取出payloadType原来位置上的第一位的内容,再和参数payload进行或运算,也就拿到了payload
//后7位的内容
buffer[offset + 1] = (byte)((buffer[offset + 1] & 0x80) | payload);
}
void setSSRC(int ssrc)
//设置ssrc的值
public void setSSRC(int ssrc)
{
writeInt(8, ssrc);
}
//从第8位开始,将4个字节ssrc的int写到该位置中
public void writeInt(int off, int data)
{
RTPUtils.writeInt(buffer, offset + off, data);
}
//将data中的每一个字节写入到相应的位置
public static int writeInt(byte[] buf, int off, int data)
{
if (buf == null || buf.length < off + 4)
{
return -1;
}
buf[off++] = (byte)(data>>24);
buf[off++] = (byte)(data>>16);
buf[off++] = (byte)(data>>8);
buf[off] = (byte)data;
return 4;
}
setTimestamp(ts)和setSequenceNumber(seqNum)上面类似。
2、getVersion获取版本号
int getVersion(ByteArrayBuffer baf)
public static int getVersion(ByteArrayBuffer baf)
{
if (baf == null)
{
return -1;
}
return getVersion(baf.getBuffer(), baf.getOffset(), baf.getLength());
}
public static int getVersion(byte[] buffer, int offset, int length)
{
//0xC0,转成二进制是0b1100_0000,然后无符号右移6位,得到的是高2位上面的值
return (buffer[offset] & 0xC0) >>> 6;
}
3、isPacketMarked 标志位M是否为true(是否是一帧的结束)
boolean isPacketMarked(ByteArrayBuffer baf)
public static boolean isPacketMarked(ByteArrayBuffer baf)
{
if (baf == null)
{
return false;
}
return isPacketMarked(baf.getBuffer(), baf.getOffset(), baf.getLength());
}
public static boolean isPacketMarked(byte[] buffer, int offset, int length)
{
//参数检查。
if (buffer == null || buffer.length < offset + length || length < 2)
{
return false;
}
//0x80,二进制是0b1000_0000, &按位与,offset + 1表示第二个字节。所以取的是第二个字节
//的第一位的内容是否为0
return (buffer[offset + 1] & 0x80) != 0;
}
4、isInvalid 检查一个报文是否是RTP/RTCP
boolean isInvalid(byte[] buffer, int offset, int length)
boolean isRtpRtcp(byte[] buf, int off, int len)
public static boolean isInvalid(byte[] buffer, int offset, int length)
{
//RTP至少需要12个字节,RTCP至少8个字节,所以length长度至少是8,否则就是无效的报文(true表
//示无效报文)
if (buffer == null || buffer.length < offset + length
|| length < RTCP_MIN_SIZE)
{
return true;
}
//获取payloadType
int pt = buffer[offset + 1] & 0xff;
if (pt < 200 || pt > 211)
{
//判断payloadType是否是在合理的范围,并且长度是否也在合理的范围
return length < FIXED_HEADER_SIZE;
}
return false;
}
public static boolean isRtpRtcp(byte[] buf, int off, int len)
{
//先进行前面的判断
if (isInvalid(buf, off, len))
{
return false;
}
int version = getVersion(buf, off, len);
if (version != RTPHeader.VERSION)
{
//额外需要判断版本号是否是2
return false;
}
return true;
}
5、getRTCPSSRC 获取RTCP的ssrc
long getRTCPSSRC(ByteArrayBuffer baf)
public static long getRTCPSSRC(ByteArrayBuffer baf)
{
if (baf == null || baf.isInvalid())
{
return -1;
}
return getRTCPSSRC(baf.getBuffer(), baf.getOffset(), baf.getLength());
}
public static long getRTCPSSRC(byte[] buf, int off, int len)
{
//一些长度的判断
if (buf == null || buf.length < off + len || len < 8)
{
return -1;
}
//从第四个字节往后读取一个int的内容
return RTPUtils.readUint32AsLong(buf, off + 4);
}
public static long readUint32AsLong(byte[] buf, int off)
{
return readInt(buf, off) & 0xFFFF_FFFFL;
}
6、getHeaderLength 获取RTP头长度
int getHeaderLength(byte[] buffer, int offset, int length)
public static int getHeaderLength(byte[] buffer, int offset, int length)
{
//RTP除了12个字节的固定内容外,还有csrc以及扩展头的内容
int headerLength
= FIXED_HEADER_SIZE + 4 * getCsrcCount(buffer, offset, length);
// Make sure that the header length doesn't exceed the packet length.
if (headerLength > length)
{
headerLength = length;
}
if (getExtensionBit(buffer, offset, length))
{
// Make sure that the header length doesn't exceed the packet
// length.
if (headerLength + EXT_HEADER_SIZE <= length)
{
headerLength += EXT_HEADER_SIZE
+ getExtensionLength(buffer, offset, length);
}
}
return headerLength;
}
7、getPayload 获取RTP payload的内容
payload是指除了rtp头部以外的内容。
byte[] getPayload()
public byte[] getPayload()
{
//其中,getHeaderLength()和getPayloadLength()分别是rtp头长度和rtp payload的长度
return readRegion(getHeaderLength(), getPayloadLength());
}
public byte[] readRegion(int off, int len)
{
//offset是0,off的长度是rtp头的长度,所以最终偏移量是从rtp的头结束的地方开始
int startOffset = this.offset + off;
if (off < 0 || len <= 0 || startOffset + len > this.buffer.length)
return null;
byte[] region = new byte[len];
//从原始的buffer中,偏移量为rtp头结束的地方,获取长度为payload的长度的字节数
System.arraycopy(this.buffer, startOffset, region, 0, len);
return region;
}
8、getPayloadType 获取payloadType
byte getPayloadType()
public byte getPayloadType()
{
return (byte) getPayloadType(buffer, offset, length);
}
public static int getPayloadType(byte[] buf, int off, int len)
{
if (buf == null || buf.length < off + len || len < 2)
{
return -1;
}
//0x7F,二进制是0b0111_1111,buf[off + 1]是第二个字节。所以是获取第二个字节的后7位。
return (buf[off + 1] & 0x7F);
}
9、getRTCPPacketType 获取RTCP的类型
RTCP的类型正好就是第二个字节,int getRTCPPacketType()
public int getRTCPPacketType()
{
return 0xff & buffer[offset + 1];
}
10、getSequenceNumber 获取RTP的sequence number
RTP的sequence number正好是第3、4个字节。int getSequenceNumber()
public int getSequenceNumber()
{
return getSequenceNumber(buffer, offset, length);
}
public static int getSequenceNumber(byte[] buffer, int offset, int length)
{
//偏移量2,往后读2个字节
return RTPUtils.readUint16AsInt(buffer, offset + 2);
}
public static int readUint16AsInt(byte[] buf, int off)
{
int b1 = (0xFF & (buf[off + 0]));
int b2 = (0xFF & (buf[off + 1]));
int val = b1 << 8 | b2;
return val;
}
本文地址:https://blog.csdn.net/qq_32523587/article/details/107300381
推荐阅读
-
C#中关于zip压缩解压帮助类的封装 附源码下载
-
实例解析iOS app开发中音频文件播放工具类的封装
-
spring5 源码深度解析----- 被面试官给虐懵了,竟然是因为我不懂@Configuration配置类及@Bean的原理
-
C#_Excel数据读取与写入_自定义解析封装类_支持设置标题行位置&使用excel表达式收集数据&单元格映射&标题映射&模板文件的参数数据替换(第二版-增加深度读取和更新功能)
-
Android消息通信机制Handler详解,Handler,Looper,MessageQueue,源码解析,讲解这几个类怎么配合工作的
-
实例解析iOS app开发中音频文件播放工具类的封装
-
ArrayList类源码解析——ArrayList动态数组的实现细节(基于JDK8)
-
Tomcat的类加载机制流程及源码解析
-
RTP的封装类RawPacket源码解析
-
爬虫验证码的几种处理方式,已封装成类,文章末尾有源码!