接收端twcc主要功能就是通过fb通知发送端每一个rtp的接收时间。
基本逻辑:当接收端接收到每一个rtp包的时候,记录当前的接受时间和包序号,然后按一定策略定时的发送到发送端,然后发送端可以根据的发送,接收时间统计延迟,为后续估算提供数据点。
代码分析 remote_estimator_proxy.cc文件
webrtc::RemoteEstimatorProxy类主要作用,保存窗口期的rtp包接收时间,组合成feedback rtcp反馈包,然后定时发送。
一. 缓存数据
//接收到rtp时候,会通过此接口,输入接收时间等信息
//对外接口,然后判断数据有效性后调用OnPacketArrival
void RemoteEstimatorProxy::IncomingPacket(int64_t arrival_time_ms,size_t payload_size,const RTPHeader& header)
//函数功能:把seq存储到缓存中,
// 1.去掉时间窗口期(500ms)外的。 2. 去掉超过最大缓存数量(kMaxNumberOfPackets)的
// 保存缓存中最小的seq值到periodic_window_start_seq_,
void RemoteEstimatorProxy::OnPacketArrival(uint16_t sequence_number,int64_t arrival_time, absl::optional<FeedbackRequest> feedback_request) {
...
//消除seq回绕
int64_t seq = unwrapper_.Unwrap(sequence_number);
//send_periodic_feedback_:反馈模式,是否定时反馈
//删除periodic_window_start_seq_前500ms(send_config_.back_window-ms)的包,periodic_window_start_seq_保存的缓存中的最小seq
if (send_periodic_feedback_) {
if (periodic_window_start_seq_ &&
packet_arrival_times_.lower_bound(*periodic_window_start_seq_) ==
packet_arrival_times_.end()) {
// Start new feedback packet, cull old packets.
//删除超出当前时间窗口的包 500ms前的
//如果删除的包中存在对应seq,我感觉应该不需要这个包了,删除完成后,直接退出函数
//因为500ms前这个seq包已经到达过,乱序包也不会存在这么长时间,如果真是乱序,重传的这么长的时间会影响后面的估算
for (auto it = packet_arrival_times_.begin();
it != packet_arrival_times_.end() && it->first < seq &&
arrival_time - it->second >= send_config_.back_window->ms();) {
it = packet_arrival_times_.erase(it);
}
}
//保存待发送反馈的最小值seq
if (!periodic_window_start_seq_ || seq < *periodic_window_start_seq_) {
periodic_window_start_seq_ = seq;
}
}
// We are only interested in the first time a packet is received.
if (packet_arrival_times_.find(seq) != packet_arrival_times_.end())
return;
packet_arrival_times_[seq] = arrival_time;
//最多存储kMaxNumberOfPackets个数的包,清除以前的
auto first_arrival_time_to_keep = packet_arrival_times_.lower_bound(
packet_arrival_times_.rbegin()->first - kMaxNumberOfPackets);
if (first_arrival_time_to_keep != packet_arrival_times_.begin()) {
packet_arrival_times_.erase(packet_arrival_times_.begin(),
first_arrival_time_to_keep);
if (send_periodic_feedback_) {
// |packet_arrival_times_| cannot be empty since we just added one element
// and the last element is not deleted.
RTC_DCHECK(!packet_arrival_times_.empty());
periodic_window_start_seq_ = packet_arrival_times_.begin()->first;
}
}
//
//发送请求
if (feedback_request) {
// Send feedback packet immediately.
SendFeedbackOnRequest(seq, *feedback_request);
}
}
二. 发送feedback
把缓存的数据构造成FeedBack(可能分解成多个数据包),然后发送出去(feedback_sender_发送接口)
void RemoteEstimatorProxy::SendPeriodicFeedbacks() {
for (auto begin_iterator =
packet_arrival_times_.lower_bound(*periodic_window_start_seq_);
begin_iterator != packet_arrival_times_.cend();
begin_iterator =
packet_arrival_times_.lower_bound(*periodic_window_start_seq_)) {
rtcp::TransportFeedback feedback_packet;
//当达到反馈包最大大小时候,返回下一次使用的开始seq
periodic_window_start_seq_ = BuildFeedbackPacket(
feedback_packet_count_++, media_ssrc_, *periodic_window_start_seq_,
begin_iterator, packet_arrival_times_.cend(), &feedback_packet);
RTC_DCHECK(feedback_sender_ != nullptr);
feedback_sender_->SendTransportFeedback(&feedback_packet);
}
}
三. 计算发送间隔
//当前码率计算发送间隔, 必选介于 50ms -250ms之间(send_config_.min_interval,send_config_.max_interval),这些值可以通过sdp设置的
void RemoteEstimatorProxy::OnBitrateChanged(int bitrate_bps) {
// TwccReportSize = Ipv4(20B) + UDP(8B) + SRTP(10B) +
// AverageTwccReport(30B)
// TwccReport size at 50ms interval is 24 byte.
// TwccReport size at 250ms interval is 36 byte.
// AverageTwccReport = (TwccReport(50ms) + TwccReport(250ms)) / 2
//每个fb twcc包的大小的均值
constexpr int kTwccReportSize = 20 + 8 + 10 + 30;
const double kMinTwccRate =
kTwccReportSize * 8.0 * 1000.0 / send_config_.max_interval->ms();
const double kMaxTwccRate =
kTwccReportSize * 8.0 * 1000.0 / send_config_.min_interval->ms();
//计算发送间隔,限制到50ms-250ms之间
// Let TWCC reports occupy 5% of total bandwidth.
rtc::CritScope cs(&lock_);
send_interval_ms_ = static_cast<int>(
0.5 + kTwccReportSize * 8.0 * 1000.0 /
rtc::SafeClamp(0.05 * bitrate_bps, kMinTwccRate, kMaxTwccRate));
}