RTP的封装类RawPacket源码解析

      RawPacket类是jitsi封装的一个jar包,里面包含了关于RTP和RTCP的一些基本操作。

目录

一、依赖包

二、RTP/RTCP基本结构

三、相关成员变量

四、相关成员方法

1、makeRTP创建RTP包

2、getVersion获取版本号

3、isPacketMarked 标志位M是否为true(是否是一帧的结束)

4、isInvalid 检查一个报文是否是RTP/RTCP

5、getRTCPSSRC 获取RTCP的ssrc

6、getHeaderLength  获取RTP头长度

7、getPayload  获取RTP payload的内容

8、getPayloadType 获取payloadType

9、getRTCPPacketType  获取RTCP的类型

 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;
}

  • 1
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 3
    评论
评论 3
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值