在音视频网络传输过程中,由于存在网路抖动情况,接收端视频接受不及时导致播放卡顿,为了消除帧间抖动情况,一个解决手段是JitterBuffer。JitterBuffer包括RTP包的排序,GOP内帧排序以及GOP间排序。(文末注解名词解释)。
1、RTP包排序:PacketBuffer
1.1 插入RTP数据包(PacketBuffer::InsertPacket)
这个函数首先判断是不是首包,是的话就记录一下,接下来的包开始往后排序,不是的话就调用包序列号比较函数AheadOf。在利用索引计算的包在缓存中的位置如果被占用并且序列号一样,就是重复包,丢掉。如果被占用但是序列号不相同,就说明缓存满了,需要扩容,重新计算包的索引值,扩容后还是满的就要情况缓存了
PacketBuffer::InsertResult PacketBuffer::InsertPacket(
std::unique_ptr<PacketBuffer::Packet> packet) {
PacketBuffer::InsertResult result;
//当前包序号
uint16_t seq_num = packet->seq_num;
//当前包在缓存中的索引
size_t index = seq_num % buffer_.size();
if (!first_packet_received_) {
//保存第一个包
first_seq_num_ = seq_num;
//第一个包的序号
first_packet_received_ = true;
//收到了第一个包
} else if (AheadOf(first_seq_num_, seq_num)) {
// If we have explicitly cleared past this packet then it's old,
// don't insert it, just silently ignore it.
// 如果当前包比之前记录的第一个包first_seq_num_还老
// 并且之前已经清理过第一个包序列号,说明已经至少成功解码过一帧,RtpVideoStreamReceiver::FrameDecoded
// 会调用PacketBuffer::ClearTo(seq_num),清理first_seq_num_之前的所有缓存,这个时候还来一个比first_seq_num_还
// 老的包,就没有必要再留着了。
if (is_cleared_to_first_seq_num_) {
return result;
}
// 相反如果没有被清理过,则是有必要保留成第一个包的,比如发生了乱序。
first_seq_num_ = seq_num;
}
//如果缓存的槽被占了,而且序号一样,说明是重复包,丢掉
if (buffer_[index] != nullptr) {
// Duplicate packet, just delete the payload.
if (buffer_[index]->seq_num == packet->seq_num) {
return result;
}
// The packet buffer is full, try to expand the buffer.
// 如果槽被占,但是输入包和对应槽的包序列号不等,说明缓存满了,需要扩容。
// ExpandBufferSize() 会更新缓存在新的队列的位置,并不会引起位置错误
while (ExpandBufferSize() && buffer_[seq_num % buffer_.size()] != nullptr) {
}
// 重新计算输入包索引.
index = seq_num % buffer_.size();
// Packet buffer is still full since we were unable to expand the buffer.
// 如果对应的槽还是被占用了,还是满,已经不行了,致命错误.
if (buffer_[index] != nullptr) {
// Clear the buffer, delete payload, and return false to signal that a
// new keyframe is needed.
RTC_LOG(LS_WARNING) << "Clear PacketBuffer and request key frame.";
ClearInternal();
result.buffer_cleared = true;
return result;
}
}
//之前的包是否连续,这里初始为false,在FindFrames中置位
packet->continuous = false;
//此处的move移动语义提升了效率
buffer_[index] = std::move(packet);
// 更新丢包信息,检查收到当前包后是否有丢包导致的空洞,也就是不连续.
UpdateMissingPackets(seq_num);
result.packets = FindFrames(seq_num);
return result;
}
1.2 插入填充包(PacketBuffer::InsertPadding)
这里的填充包类似于滥竽充数,主要是由于发送端为了满足输出码率的情况下进行的Padding,
PacketBuffer::InsertResult PacketBuffer::InsertPadding(uint16_t seq_num) {
PacketBuffer::InsertResult result;
// 更新丢包信息,检查收到当前包后是否有丢包导致的空洞,也就是不连续.
UpdateMissingPackets(seq_num);
// 分析排序缓存,检查是否能够组装出完整的帧并返回.
result.packets = FindFrames(static_cast<uint16_t>(seq_num + 1));
return result;
}
1.3 丢包检测(PacketBuffer::UpdateMissingPackets)
这个函数主要完成的是包是否是连续的,主要靠丢包缓存missing_packets_维护包序列号。
void PacketBuffer::UpdateMissingPackets(uint16_t seq_num) {
// 如果最新插入的包序列号还未设置过,这里直接设置一次.
if (!newest_inserted_seq_num_)
newest_inserted_seq_num_ = seq_num;
const int kMaxPaddingAge = 1000;
// 如果当前包的序列号新于之前的最新包序列号,没有发生乱序
if (AheadOf(seq_num, *newest_inserted_seq_num_)) {
// 丢包缓存missing_packets_最大保存1000个包,这里得到当前包1000个包以前的序列号,
// 也就差不多是丢包缓存里应该保存的最老的包.
uint16_t old_seq_num = seq_num - kMaxPaddingAge;
// 第一个>= old_seq_num的包的位置
auto erase_to = missing_packets_.lower_bound(old_seq_num);
// 删除丢包缓存里所有1000个包之前的所有包(如果有的话)
missing_packets_.erase(missing_packets_.begin(), erase_to);
// Guard against inserting a large amount of missing packets if there is a
// jump in the sequence number.
// 如果最老的包的序列号都比当前最新包序列号新,那么更新一下当前最新包序列号
if (AheadOf(old_seq_num, *newest_inserted_seq_num_))
*newest_inserted_seq_num_ = old_seq_num;
// 因为seq_num >newest_inserted_seq_num_,这里开始统计(newest_inserted_seq_num_, sum)之间的空