• WebRTC研究:丢包与抖动


    1、累计收包、重传包等信息,并计算抖动:

    1. void StreamStatisticianImpl::UpdateCounters(const RTPHeader& header,
    2. size_t packet_length,
    3. bool retransmitted)
    4. {
    5. rtc::CritScope cs(&stream_lock_);
    6. /*
    7. 判断包是否有序
    8. 判断标准:当前收包seq位于区间:[received_seq_max_ - max_reordering_threshold, received_seq_max_] 之外
    9. max_reordering_threshold,默认为50
    10. */
    11. bool in_order = InOrderPacketInternal(header.sequenceNumber);
    12. ssrc_ = header.ssrc;
    13. // 累计所收到的包数、字节数
    14. incoming_bitrate_.Update(packet_length);
    15. // 累计所收到的包数、header/padding/payload字节数(packet_length是包总长度)
    16. receive_counters_.transmitted.AddPacket(packet_length, header);
    17. // 累计所收到的无序重传包数、字节数
    18. if (!in_order && retransmitted)
    19. {
    20. receive_counters_.retransmitted.AddPacket(packet_length, header);
    21. }
    22. // 记录所收到的第一个包的seq、当前时间
    23. if (receive_counters_.transmitted.packets == 1)
    24. {
    25. received_seq_first_ = header.sequenceNumber;
    26. receive_counters_.first_packet_time_ms = clock_->TimeInMilliseconds();
    27. }
    28. // 收到有序包
    29. // Count only the new packets received. That is, if packets 1, 2, 3, 5, 4, 6
    30. // are received, 4 will be ignored.
    31. if (in_order)
    32. {
    33. // Current time in samples.
    34. NtpTime receive_time(*clock_);
    35. // Wrong if we use RetransmitOfOldPacket.
    36. // 回绕
    37. if (receive_counters_.transmitted.packets > 1 && received_seq_max_ > header.sequenceNumber)
    38. {
    39. // Wrap around detected.
    40. received_seq_wraps_++;
    41. }
    42. // 记录最新 seq
    43. received_seq_max_ = header.sequenceNumber;
    44. // 计算最新抖动
    45. if (header.timestamp != last_received_timestamp_ &&
    46. (receive_counters_.transmitted.packets - receive_counters_.retransmitted.packets) > 1)
    47. {
    48. UpdateJitter(header, receive_time);
    49. }
    50. // 记录最近 所收到的最新包头携带的时间戳、收到最新包时的本地NTP时间与当前时间
    51. last_received_timestamp_ = header.timestamp;
    52. last_receive_time_ntp_ = receive_time;
    53. last_receive_time_ms_ = clock_->TimeInMilliseconds();
    54. }
    55. size_t packet_oh = header.headerLength + header.paddingLength;
    56. // Our measured overhead. Filter from RFC 5104 4.2.1.2:
    57. // avg_OH (new) = 15/16*avg_OH (old) + 1/16*pckt_OH,
    58. received_packet_overhead_ = (15 * received_packet_overhead_ + packet_oh) >> 4;
    59. }

    判断包是否有序:

    1. bool StreamStatisticianImpl::InOrderPacketInternal(uint16_t sequence_number) const
    2. {
    3. // First packet is always in order.
    4. if (last_receive_time_ms_ == 0)
    5. return true;
    6. if (IsNewerSequenceNumber(sequence_number, received_seq_max_))
    7. {
    8. return true;
    9. }
    10. else
    11. {
    12. // If we have a restart of the remote side this packet is still in order.
    13. return !IsNewerSequenceNumber(sequence_number, received_seq_max_ - max_reordering_threshold_);
    14. }
    15. }

    计算抖动:

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

    1. void StreamStatisticianImpl::UpdateJitter(const RTPHeader& header, NtpTime receive_time)
    2. {
    3. // 将NTP时间戳:receive_time(本地当前NTP时间)转变成RTP时间戳
    4. uint32_t receive_time_rtp = NtpToRtp(receive_time, header.payload_type_frequency);
    5. // 将NTP时间戳:last_receive_time_ntp_(最近收到最新包时的本地NTP时间)转变成RTP时间戳
    6. uint32_t last_receive_time_rtp = NtpToRtp(last_receive_time_ntp_, header.payload_type_frequency);
    7. // 第一种计算抖动的方法:jitter_q4_
    8. /*
    9. 计算(最近两次收到最新包时的本地NTP时间差)与(最近两次收到最新包头携带的时间戳)之差
    10. 即:计算抖动值,理想情况下,抖动值为0
    11. */
    12. int32_t time_diff_samples = (receive_time_rtp - last_receive_time_rtp) -
    13. (header.timestamp - last_received_timestamp_);
    14. time_diff_samples = std::abs(time_diff_samples);
    15. // lib_jingle sometimes deliver crazy jumps in TS for the same stream.
    16. // If this happens, don't update jitter value. Use 5 secs video frequency
    17. // as the threshold.
    18. // 更新 jitter_q4_
    19. if (time_diff_samples < 450000)
    20. {
    21. // Note we calculate in Q4 to avoid using float.
    22. int32_t jitter_diff_q4 = (time_diff_samples << 4) - jitter_q4_;
    23. jitter_q4_ += ((jitter_diff_q4 + 8) >> 4);
    24. }
    25. // 第二种计算抖动的方法:jitter_q4_transmission_time_offset_
    26. // transmission_time_offset:一段时间间隔,代表属于同一帧的RTP的实际发送时间距离帧的capture time的偏移量
    27. // 即计算(最近两次收到最新包时的本地NTP时间差)与(最近两次收到最新包实际发送到网络的时间戳)之差
    28. int32_t time_diff_samples_ext = (receive_time_rtp - last_receive_time_rtp) -
    29. ((header.timestamp + header.extension.transmissionTimeOffset) -
    30. (last_received_timestamp_ + last_received_transmission_time_offset_));
    31. time_diff_samples_ext = std::abs(time_diff_samples_ext);
    32. // 计算 jitter_q4_transmission_time_offset_
    33. if (time_diff_samples_ext < 450000)
    34. {
    35. int32_t jitter_diffQ4TransmissionTimeOffset = (time_diff_samples_ext << 4) - jitter_q4_transmission_time_offset_;
    36. jitter_q4_transmission_time_offset_ += ((jitter_diffQ4TransmissionTimeOffset + 8) >> 4);
    37. }
    38. }

    二、丢包统计

    每发送一次RTP包,就会进行一次丢包统计(即丢包统计的周期:5000ms)。

    1、调用流程

    void ModuleRtpRtcpImpl::Process()

    int32_t RTCPSender::SendRTCP(…)

    int32_t RTCPSender::SendCompoundRTCP(…)

    void RTCPSender::PrepareReport(…)

    1. void RTCPSender::PrepareReport(const std::set<RTCPPacketType>& packetTypes,
    2. const FeedbackState& feedback_state)
    3. {
    4. ...
    5. ...
    6. ...
    7. if (receive_statistics_)
    8. {
    9. StatisticianMap statisticians = receive_statistics_->GetActiveStatisticians();
    10. RTC_DCHECK(report_blocks_.empty());
    11. for (auto& it : statisticians)
    12. {
    13. AddReportBlock(feedback_state, it.first, it.second);
    14. }
    15. }
    16. }
    1. bool RTCPSender::AddReportBlock(const FeedbackState& feedback_state,
    2. uint32_t ssrc,
    3. StreamStatistician* statistician)
    4. {
    5. RtcpStatistics stats;
    6. if (!statistician->GetStatistics(&stats, true))
    7. return false;
    8. ...
    9. ...
    10. ...
    11. }
    1. bool StreamStatisticianImpl::GetStatistics(RtcpStatistics* statistics, bool reset)
    2. {
    3. {
    4. rtc::CritScope cs(&stream_lock_);
    5. /*
    6. received_seq_first_:所收到的第一个包的seq
    7. payload_bytes:所收到的包中payload部分字节总数
    8. */
    9. if (received_seq_first_ == 0 && receive_counters_.transmitted.payload_bytes == 0)
    10. {
    11. // We have not received anything.
    12. return false;
    13. }
    14. /* 不重新进行丢包统计 */
    15. if (!reset)
    16. {
    17. /* 收到的包总数 与 重传报数 之差 */
    18. if (last_report_inorder_packets_ == 0)
    19. {
    20. // No report.
    21. return false;
    22. }
    23. // 直接使用上一次的report
    24. *statistics = last_reported_statistics_;
    25. return true;
    26. }
    27. // 丢包统计
    28. *statistics = CalculateRtcpStatistics();
    29. }
    30. NotifyRtcpCallback();
    31. return true;
    32. }

    2、丢包统计

    丢包率 = 255 * 丢包数 / 预期收到的包总数

    丢包数:期望收到的包总数 - 实际收到的包总数

    期望收到的包总数 = 当前收到的最新包seq - 截止到上一次report时收到的最新包seq

    实际收到的包总数 = 正常包总数 + 重传包总数

    正常包总数(不包括重传包)= 截止到目前收到的包总数与重传包总数之差 - 截止到上一次report时收到的包总数与重传包总数之差

    重传包数 = 截止到目前收到的重传包总数 - 截止到上一次report时收到的重传包总数

    1. RtcpStatistics StreamStatisticianImpl::CalculateRtcpStatistics()
    2. {
    3. RtcpStatistics stats;
    4. // 第一次进行统计
    5. if (last_report_inorder_packets_ == 0)
    6. {
    7. // 设置截止到上一次report时,收到的最新包
    8. // First time we send a report.
    9. last_report_seq_max_ = received_seq_first_ - 1;
    10. }
    11. /*
    12. 计算从上一次report到当前这段时间内,预期收到的包总数
    13. 期望收到的包总数 = 当前收到的最新包seq - 截止到上一次report时收到的最新包seq
    14. */
    15. uint16_t exp_since_last = (received_seq_max_ - last_report_seq_max_);
    16. // 遇到seq回绕
    17. if (last_report_seq_max_ > received_seq_max_)
    18. {
    19. exp_since_last = 0;
    20. }
    21. /*
    22. 计算从上一次report到当前这段时间内,所收到的包数(不包括重传包)
    23. 包数(不包括重传包)= 截止到目前收到的包总数与重传包总数之差 - 截止到上一次report时收到的包总数与重传包总数之差
    24. */
    25. uint32_t rec_since_last =
    26. (receive_counters_.transmitted.packets -
    27. receive_counters_.retransmitted.packets) - last_report_inorder_packets_;
    28. // With NACK we don't know the expected retransmissions during the last
    29. // second. We know how many "old" packets we have received. We just count
    30. // the number of old received to estimate the loss, but it still does not
    31. // guarantee an exact number since we run this based on time triggered by
    32. // sending of an RTP packet. This should have a minimum effect.
    33. // With NACK we don't count old packets as received since they are
    34. // re-transmitted. We use RTT to decide if a packet is re-ordered or
    35. // re-transmitted.
    36. /*
    37. 计算从上一次report到当前这段时间内,收到的重传包总数
    38. 重传包数 = 截止到目前收到的重传包总数 - 截止到上一次report时收到的重传包总数
    39. */
    40. uint32_t retransmitted_packets = receive_counters_.retransmitted.packets - last_report_old_packets_;
    41. /*
    42. 计算从上一次report到当前这段时间内,实际收到的包总数
    43. 实际收到的包总数 = 正常包总数 + 重传包总数
    44. */
    45. rec_since_last += retransmitted_packets;
    46. // 计算丢包数:期望收到的包总数 - 实际收到的包总数
    47. int32_t missing = 0;
    48. if (exp_since_last > rec_since_last)
    49. {
    50. missing = (exp_since_last - rec_since_last);
    51. }
    52. /*
    53. 计算丢包率
    54. 丢包率 = 255 * 丢包数 / 预期收到的包总数
    55. 255表示100%丢包
    56. */
    57. uint8_t local_fraction_lost = 0;
    58. if (exp_since_last)
    59. {
    60. local_fraction_lost = static_cast<uint8_t>(255 * missing / exp_since_last);
    61. }
    62. stats.fraction_lost = local_fraction_lost;
    63. // 累计到目前为止的丢包总数
    64. cumulative_loss_ += missing;
    65. stats.cumulative_lost = cumulative_loss_;
    66. /*
    67. 扩展最近收到的最新包seq
    68. received_seq_wraps_:发生seq回绕的次数
    69. */
    70. stats.extended_max_sequence_number = (received_seq_wraps_ << 16) + received_seq_max_;
    71. // 抖动
    72. // Note: internal jitter value is in Q4 and needs to be scaled by 1/16.
    73. stats.jitter = jitter_q4_ >> 4;
    74. // Store this report.
    75. last_reported_statistics_ = stats;
    76. // 截止到目前为止,收到的包总数与重传包总数之差
    77. last_report_inorder_packets_ = receive_counters_.transmitted.packets - receive_counters_.retransmitted.packets;
    78. // 记录本次report时的重传包总数、最新包seq
    79. last_report_old_packets_ = receive_counters_.retransmitted.packets;
    80. last_report_seq_max_ = received_seq_max_;
    81. return stats;
    82. }

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

  • 相关阅读:
    8/15 缩点+割点(割顶)+桥(割边)+字符串哈希
    使用 MRKL 系统跨越神经符号鸿沟
    堆体扫描点云体积计算实现思路分享
    synchronized已经不在臃肿了,放下对他的成见之初识轻量级锁
    计算机毕业设计Java在线问诊系统的设计与实现(源码+系统+mysql数据库+Lw文档)
    Nebula Studio:部署与连接
    理解HTTPS/TLS/SSL(二)可视化TLS握手过程并解密加密数据
    【excel实战】-- 批量提取批准&多重区域复制粘贴
    java中的集合框架基础-5
    【计算机组成原理】考研真题攻克与重点知识点剖析 - 第 1 篇:计算机系统概述
  • 原文地址:https://blog.csdn.net/m0_60259116/article/details/126647103