• WebRTC研究:audio 丢包判断


    当收到一个包时,丢包判断原理:

    将 当前收包序列号:sequence_number 从丢包集合 nack_list_ 中剔除;

    将 sequence_number 与 最近收到的新包:sequence_num_last_received_rtp_ 对比,判断当前包是否为重传或乱序包。如若是的话,直接终止判断,否则继续;

    对比 当前包 与 最新收到的包 的序列号与时间戳,更新每个包的样本数:samples_per_packet_;

    更新 nack_list_ 中部分包的丢包状态:is_missing = true,这些包我们之前认为后续再考虑,现在需要把它们当做丢包。这些包满足:序列号 在 sequence_number - nack_threshold_packets_ 之前(nack_threshold_packets_ = 2);

    如果 最新收到的包 与 当前包 不连续,则区间内的包都要被放到 nack_list_,其中那些太旧的包(判断标准同上一步)会被直接认为丢包,剩下的包当后续包达到时,会在上一步中再明确是否为丢包;

    将 最近收到的新包 的序列号: sequence_num_last_received_rtp_ 与时间戳更新为 当前包 的序列号与时间戳;

    丢弃掉集合 nack_list_ 中不能重传的包信息,同时保证集合容量不超过限制(最大容量 max_nack_list_size_ = 500,即对于单位长度为 20ms 的帧,保留 10s 长度)。不能重传的包满足序列号在 最新收到的包序列号 - static_cast(max_nack_list_size_) - 1 之前。

    本文福利, 免费领取C++音视频学习资料包、技术视频,内容包括(音视频开发,面试题,FFmpeg webRTC rtmp hls rtsp ffplay srs↓↓↓↓↓↓见下面↓↓文章底部点击免费领取↓↓

    一、判断当前收包是否为重传或乱序包:

    1. /*
    2. sequence_number:当前收到的包的序列号
    3. timestamp:当前收到的包的时间戳
    4. */
    5. void NackTracker::UpdateLastReceivedPacket(uint16_t sequence_number, uint32_t timestamp)
    6. {
    7. /*
    8. 如果这是第一个包
    9. any_rtp_received_ = true,表示收到了任意包
    10. */
    11. if (!any_rtp_received_)
    12. {
    13. /* 第一个包仅仅记录序列号与时间戳 */
    14. sequence_num_last_received_rtp_ = sequence_number;
    15. timestamp_last_received_rtp_ = timestamp;
    16. any_rtp_received_ = true;
    17. // If no packet is decoded, to have a reasonable estimate of time-to-play
    18. // use the given values.
    19. if (!any_rtp_decoded_)
    20. {
    21. sequence_num_last_decoded_rtp_ = sequence_number;
    22. timestamp_last_decoded_rtp_ = timestamp;
    23. }
    24. return;
    25. }
    26. /* 如果是上一个包, */
    27. if (sequence_number == sequence_num_last_received_rtp_)
    28. return;
    29. /* nack_list_:丢包集合 */
    30. nack_list_.erase(sequence_number);
    31. /*
    32. 将当前包 与 最近收到的新包 对比,判断当前包是否为重传或乱序包
    33. 是的话,直接返回
    34. */
    35. if (IsNewerSequenceNumber(sequence_num_last_received_rtp_, sequence_number))
    36. return;
    37. /* 对比 当前包 与 最新收到的包 的序列号与时间戳,更新每个包的样本数:samples_per_packet_ */
    38. UpdateSamplesPerPacket(sequence_number, timestamp);
    39. /* 更新丢包集合:nack_list_ */
    40. UpdateList(sequence_number);
    41. /* 更新最新收包的序列号与时间戳 */
    42. sequence_num_last_received_rtp_ = sequence_number;
    43. timestamp_last_received_rtp_ = timestamp;
    44. /* 丢弃掉集合 nack_list_ 中不能重传的包信息,同时保证集合容量不超过限制 */
    45. LimitNackListSize();
    46. }

    二、更新丢包集合:nack_list_: 

    1. void NackTracker::UpdateList(uint16_t sequence_number_current_received_rtp)
    2. {
    3. /*
    4. 更新 nack_list_ 中部分包的丢包状态:is_missing = true
    5. 这些包我们之前认为后续再考虑,现在需要把它们当做丢包
    6. */
    7. ChangeFromLateToMissing(sequence_number_current_received_rtp);
    8. /*
    9. 如果当前收到的包 比 sequence_num_last_received_rtp_ + 1 新,
    10. 即:最近收到的新包 与 当前收到的包 不连续
    11. */
    12. if (IsNewerSequenceNumber(sequence_number_current_received_rtp,
    13. sequence_num_last_received_rtp_ + 1))
    14. AddToList(sequence_number_current_received_rtp);
    15. }

    三、更新 nack_list_ 中部分包的丢包状态:is_missing = true,这些包我们之前认为后续再考虑,现在需要把它们当做丢包: 

    1. void NackTracker::ChangeFromLateToMissing(uint16_t sequence_number_current_received_rtp)
    2. {
    3. /*
    4. nack_threshold_packets_ = 2,表示:
    5. 当序列号为 N 的包到达时,序列号在 ( , N - |nack_threshold_packets_| ) 区间、所有未到达的包,都会被认为丢失
    6. 而对于序列号在 [ N - |nack_threshold_packets_| , N-1 ] 区间、所有未达到的包,后面再考虑
    7. */
    8. /* 查找 大于等于 N - |nack_threshold_packets_| 的第一个元素 */
    9. NackList::const_iterator lower_bound =
    10. nack_list_.lower_bound(static_cast<uint16_t>(
    11. sequence_number_current_received_rtp - nack_threshold_packets_));
    12. for (NackList::iterator it = nack_list_.begin(); it != lower_bound; ++it)
    13. it->second.is_missing = true;
    14. }

    四、最新收到的包 与 当前包 之间的所有包都要被添加到 nack_list_,其中那些太旧的包会被直接认为丢包,剩下的包当后续包达到时,会在上一步中再明确是否为丢包: 

    1. void NackTracker::AddToList(uint16_t sequence_number_current_received_rtp)
    2. {
    3. assert(!any_rtp_decoded_ ||
    4. IsNewerSequenceNumber(sequence_number_current_received_rtp,
    5. sequence_num_last_decoded_rtp_));
    6. /*
    7. 序列号在 upper_bound_missing 之前的包(不包括 upper_bound_missing),都认为是丢包,添加到 nack_list_
    8. upper_bound_missing 以及之后的包,一并添加到 nack_list_,但后面再考虑
    9. */
    10. uint16_t upper_bound_missing =
    11. sequence_number_current_received_rtp - nack_threshold_packets_;
    12. /*
    13. 最近收到的新包 到 当前收到的包 区间内的包,都可能是丢包,都会被添加到 nack_list_
    14. 但只有在 upper_bound_missing 之前的包,才直接认为丢包
    15. */
    16. for (uint16_t n = sequence_num_last_received_rtp_ + 1;
    17. IsNewerSequenceNumber(sequence_number_current_received_rtp, n); ++n)
    18. {
    19. bool is_missing = IsNewerSequenceNumber(upper_bound_missing, n);
    20. uint32_t timestamp = EstimateTimestamp(n);
    21. NackElement nack_element(TimeToPlay(timestamp), timestamp, is_missing);
    22. nack_list_.insert(nack_list_.end(), std::make_pair(n, nack_element));
    23. }
    24. }

    五、丢弃掉集合 nack_list_ 中不能重传的包信息,同时保证集合容量不超过限制:

    1. void NackTracker::LimitNackListSize()
    2. {
    3. /* max_nack_list_size_ = 500 */
    4. uint16_t limit = sequence_num_last_received_rtp_ - static_cast<uint16_t>(max_nack_list_size_) - 1;
    5. /* 删除 [ start, end ) 区间内的节点 */
    6. nack_list_.erase(nack_list_.begin(), nack_list_.upper_bound(limit));
    7. }

    本文福利, 免费领取C++音视频学习资料包、技术视频,内容包括(音视频开发,面试题,FFmpeg webRTC rtmp hls rtsp ffplay srs↓↓↓↓↓↓见下面↓↓文章底部点击免费领取↓↓

  • 相关阅读:
    2023/11/8JAVA学习
    npm使用国内淘宝镜像的方法
    数据屏蔽:静态与动态
    GeoServer入坑(一)--DEM地形图发布
    CNN样本分类验证
    Java----List接口(Collection的子接口)
    【Git】代码权限&分支管理
    3分钟让你学会axios在vue项目中的基本用法(建议收藏)
    spring上传文件
    【Kotlin】扩展属性、扩展函数
  • 原文地址:https://blog.csdn.net/m0_60259116/article/details/126647806