欢迎您访问程序员文章站本站旨在为大家提供分享程序员计算机编程知识!
您现在的位置是: 首页  >  移动技术

RTP的封装类RawPacket源码解析

程序员文章站 2022-06-23 10:09:19
RawPacket类是jitsi封装的一个jar包,里面包含了关于RTP和RTCP的一些基本操作。一、依赖包 org.jitsi libjitsi 1.0-20190405.175243-382......

      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结构:

RTP的封装类RawPacket源码解析

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结构:

RTP的封装类RawPacket源码解析

三、相关成员变量

//扩展头长度
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的封装类RawPacket源码解析

根据传入的参数来构造一个RTP包。接下来看每一个具体的方法。

 

boolean setVersion()

public boolean setVersion()
{
	if (isInvalid())
	{
		return false;
	}

        //因为offset为0,0x80的二进制是0b1000_0000, |或运算,将第一位设置成1,所以版本号固定为2
	buffer[offset] |= 0x80;
	return true;
}

RTP的封装类RawPacket源码解析

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

RTP的封装类RawPacket源码解析 

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

RTP的封装类RawPacket源码解析 

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

RTP的封装类RawPacket源码解析 

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

RTP的封装类RawPacket源码解析

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

 

RTP的封装类RawPacket源码解析

9、getRTCPPacketType  获取RTCP的类型

RTCP的类型正好就是第二个字节,int getRTCPPacketType()

public int getRTCPPacketType()
{
	return 0xff & buffer[offset + 1];
}

RTP的封装类RawPacket源码解析

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

RTP的封装类RawPacket源码解析

本文地址:https://blog.csdn.net/qq_32523587/article/details/107300381