WebRTC系列之JitterBuffer(1)

在音视频网络传输过程中,由于存在网路抖动情况,接收端视频接受不及时导致播放卡顿,为了消除帧间抖动情况,一个解决手段是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)之间的空
  • 0
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值