GCC Implementation in WebRTC
Overview
- RTP header extension
- RTCP feedback extension
- ProbeBitrateEstimator : 根据feedback计算探测码率,PacingController中会将包按照cluster进行划分,transport-CC报文能得到包所属的cluster以及发送和接收信息,通过发送和接收的数据大小比判断是否到达链路上限从而进行带宽探测
- AcknowledgedBitrateEstimator : 估算当前的吞吐量
- BitrateEstimator :使用滑动窗口 + 卡尔曼滤波计算当前发送吞吐量
- DelayBasedBwe : 基于延迟预估码率
- TrendlineEstimator : 使用线性回归计算当前网络拥堵情况
- AimdRateControl : 通过TrendLine预测出来的网络状态对码率进行aimd方式调整
- SendSideBandwidthEstimation : 基于丢包计算预估码率,结合延迟预估码率,得到最终的目标码率
- ProbeController : 探测控制器,通过目标码率判断下次是否探测,探测码率大小
- CongestionWindowPushbackController : 基于当前的rtt设置一个时间窗口,同时基于当前的码率设置当前时间窗口下的数据量,通过判断当前窗口的使用量,如果使用量过大的时候,降低编码时使用的目标码率,加速窗口消退,减少延迟
- AlrDetector : 应用(码率)受限检测,检测当前的发送码率是否和目标码率由于编码器等原因相差过大受限了,受限情况下会触发带宽预测过程的特殊处理
- NetworkStateEstimator 、 NetworkStatePredictor : 此两者属于待开发类,还没用上.
Protocol
参考协议
- https://datatracker.ietf.org/doc/html/draft-holmer-rmcat-transport-wide-cc-extensions-01
- https://datatracker.ietf.org/doc/html/draft-ietf-rmcat-gcc-02
参考实现
Transport-wide Sequence Number
在每个要发送的 RTP 包中添加一个扩展头,包含 16 bits 的序号 sequence number. 在同一个传输通道中,每发一个 RTP 包,这个序号就加一
0 1 2 3 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ | 0xBE | 0xDE | length=1 | +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ | ID | L=1 |transport-wide sequence number | zero padding | +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
Transport-wide RTCP Feedback Message
0 1 2 3 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ |V=2|P| FMT=15 | PT=205 | length | +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ | SSRC of packet sender | +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ | SSRC of media source | +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ | base sequence number | packet status count | +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ | reference time | fb pkt. count | +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ | packet chunk | packet chunk | +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ . . . . +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ | packet chunk | recv delta | recv delta | +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ . . . . +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ | recv delta | recv delta | zero padding | +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
- version (V): 2 bits This field identifies the RTP version. The current version is 2.
- padding (P): 1 bit If set, the padding bit indicates that the packet contains additional padding octets at the end that are not part of the control information but are included in the length field.
- feedback message type (FMT): 5 bits This field identifies the type of the FB message. It must have the value 15.
- payload type (PT): 8 bits This is the RTCP packet type that identifies the packet as being an RTCP FB message. The value must be RTPFB = 205.
- SSRC of packet sender: 32 bits The synchronization source identifier for the originator of this packet.
- SSRC of media source: 32 bits The synchronization source identifier of the media source that this piece of feedback
- information is related to.
(TODO: This is transport wide, do we just pick any of the media source SSRCs?) - base sequence number: 16 bits The transport-wide sequence number of the first packet in this feedback. This number is not necessarily increased for every feedback; in the case of reordering it may be decreased.
- 该 fb 包首个 rtp 包的 transport seq,非 rtp 包序列号, 而是传输通道的包号。 - packet status count: 16 bits The number of packets this feedback contains status for, starting with the packet identified by the base sequence number.
- 该 feedback packet 包含 rtp 包个数。 - reference time: 24 bits Signed integer indicating an absolute reference time in some (unknown) time base chosen by the sender of the feedback packets.
The value is to be interpreted in multiples of 64ms.
The first recv delta in this packet is relative to the reference time.
The reference time makes it possible to calculate the delta between feedbacks even if some feedback packets are lost, since it always uses the same time base.
- 参考时间,feedback 包首个 rtp 的到达时间, 它是 64ms 的倍数 - feedback packet count: 8 bits A counter incremented by one for each feedback packet sent. Used to detect feedback packet losses.
- 已发送 feedback 包计数器,可用于 feedback packet 丢失检测 - packet chunk: 16 bits A list of packet status chunks. These indicate the status of a number of packets starting with the one identified by base sequence number. See below for details.
- 描述 rtp 包 4 种状态(见:4.2),有 Run Length Chunk 和 Status Vector Chunk 两种格式 - recv delta: 8 bits For each “packet received” status, in the packet status chunks, a receive delta block will follow. See details below.
- 当 rtp 包的状态为 Packet received,通过 recv delta 记录其与前一个 rtp 包到达的时间间隔。
一个 Transport Feedback RTCP 反馈包包含:
- 一个起始序号 base sequence number,
- 一个包的状态的数量 packet status count,
- 一个参考时间 reference time, 它是发送方选定的一个以64ms为倍数的时间戳
- 一个反馈包计数 feedback packet count, 每发送一个 RTCP 反馈包, 这个读数器就加1
- 若干个包块 packet chunk , 它表示了每个应收到的 RTP 包的状态
- 紧随 packet chunks 的 recv delta , 表示接收到的 RTP 包的时间间隔变化
Packet Stats Symbols
00 | 包未收到 |
01 | 包收到了,间隔很小 |
10 | 包收到了,间隔很大,或者是负值 |
11 | 保留 |
Packet Chunk
Run Length Chunk
TBD.
Status Vector Chunk
TBD.
Receive Delta
Delta 表示为 250 us 的倍数
Implementation
参见 https://xie.infoq.cn/article/2f944089023274ef0ac6eabd8

ALR Detector
ALR(Application limited region detector)该模块也属于 gcc 的一个子模块, 其大概原理就是 SentRate/EstimatedRate 的百分比与 kAlrStartUsagePercent(60)做比较,当小于该值认为网络受限,需要启动 probe 重新探测带宽,当大于 kAlrEndUsagePercent(70),认为网络恢复则不会进行启动下次 probe 探测。
参见 https://xie.infoq.cn/article/2091ae4f237a7f89ca7ecfedd
Overuse Detector 过度使用检测器
参见 https://developer.aliyun.com/article/781511
网络带宽使用状态
WebRTC 定义了三种网络带宽的使用状态:Normal、Underuse、Overuse,即正常、低载、过载。
enumclass BandwidthUsage {
kBwNormal = 0,
kBwUnderusing = 1,
kBwOverusing = 2,
};
下图展示了过载检测中三种信号的产生机制,其中,上下两条红色曲线表示动态阈值:
蓝色曲线表示调整后的延迟梯度斜率值:


延迟梯度代表延迟变化的一个趋势的一个斜率, 取值范围是 [-1, 1]


class TrendlineEstimator : public DelayIncreaseDetectorInterface {
void Detect(double trend, double ts_delta, int64_t now_ms);
void UpdateThreshold(double modified_offset, int64_t now_ms);
}
/*
trend 是包间延迟梯度趋势的斜率
num_of_deltas_ 代表 包间延迟梯度计算的次数,取值范围是 [2, 60]
threshold_gain_ 是对于斜率的增益参数,默认为 4
*/
void TrendlineEstimator::Detect(double trend, double ts_delta, int64_t now_ms) {
if (num_of_deltas_ < 2) {
hypothesis_ = BandwidthUsage::kBwNormal;
return;
}
// 将 trend 放大
const double modified_trend =
std::min(num_of_deltas_, kMinNumDeltas) * trend * threshold_gain_;
prev_modified_trend_ = modified_trend;
BWE_TEST_LOGGING_PLOT(1, "T", now_ms, modified_trend);
BWE_TEST_LOGGING_PLOT(1, "threshold", now_ms, threshold_);
/*
延迟梯度斜率 > 当前阈值
过载总时长 > 设定时长
过载次数 >= 1
当前延迟梯度斜率值 > 上一次的斜率值, 情况在恶化
*/
if (modified_trend > threshold_) {
if (time_over_using_ == -1) {
// Initialize the timer. Assume that we've been
// over-using half of the time since the previous
// sample.
time_over_using_ = ts_delta / 2;
} else {
// Increment timer
time_over_using_ += ts_delta;
}
overuse_counter_++;
if (time_over_using_ > overusing_time_threshold_ && overuse_counter_ > 1) {
if (trend >= prev_trend_) {
time_over_using_ = 0;
overuse_counter_ = 0;
hypothesis_ = BandwidthUsage::kBwOverusing; // 假定为过度使用
}
}
} else if (modified_trend < -threshold_) {
time_over_using_ = -1;
overuse_counter_ = 0;
hypothesis_ = BandwidthUsage::kBwUnderusing;
} else {
time_over_using_ = -1;
overuse_counter_ = 0;
hypothesis_ = BandwidthUsage::kBwNormal;
}
prev_trend_ = trend;
UpdateThreshold(modified_trend, now_ms);
}
/*
参照协议的公式,根据上一次的阈值,距上一次更新的时间差,阈值的增长率,当前斜率与上一次阈值的差值,来自适应地更新阈值
*/
void TrendlineEstimator::UpdateThreshold(double modified_trend,
int64_t now_ms) {
if (last_update_ms_ == -1)
last_update_ms_ = now_ms;
if (fabs(modified_trend) > threshold_ + kMaxAdaptOffsetMs) {
// Avoid adapting the threshold to big latency spikes, caused e.g.,
// by a sudden capacity drop.
last_update_ms_ = now_ms;
return;
}
const double k = fabs(modified_trend) < threshold_ ? k_down_ : k_up_;
const int64_t kMaxTimeDeltaMs = 100;
int64_t time_delta_ms = std::min(now_ms - last_update_ms_, kMaxTimeDeltaMs);
threshold_ += k * (fabs(modified_trend) - threshold_) * time_delta_ms;
threshold_ = rtc::SafeClamp(threshold_, 6.f, 600.f);
last_update_ms_ = now_ms;
}
数据结构
上述的 RTCP 反馈消息对应于 class TransportFeedback
// Header size:
// * 4 bytes Common RTCP Packet Header
// * 8 bytes Common Packet Format for RTCP Feedback Messages
// * 8 bytes FeedbackPacket header
constexpr size_t kTransportFeedbackHeaderSizeBytes = 4 + 8 + 8;
constexpr size_t kChunkSizeBytes = 2;
// TODO(sprang): Add support for dynamic max size for easier fragmentation,
// eg. set it to what's left in the buffer or IP_PACKET_SIZE.
// Size constraint imposed by RTCP common header: 16bit size field interpreted
// as number of four byte words minus the first header word.
constexpr size_t kMaxSizeBytes = (1 << 16) * 4;
// Payload size:
// * 8 bytes Common Packet Format for RTCP Feedback Messages
// * 8 bytes FeedbackPacket header.
// * 2 bytes for one chunk.
constexpr size_t kMinPayloadSizeBytes = 8 + 8 + 2;
constexpr int kBaseScaleFactor =
TransportFeedback::kDeltaScaleFactor * (1 << 8);
constexpr int64_t kTimeWrapPeriodUs = (1ll << 24) * kBaseScaleFactor;
// Message format
//
// 0 1 2 3
// 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1
// +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
// |V=2|P| FMT=15 | PT=205 | length |
// +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
// 0 | SSRC of packet sender |
// +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
// 4 | SSRC of media source |
// +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
// 8 | base sequence number | packet status count |
// +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
// 12 | reference time | fb pkt. count |
// +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
// 16 | packet chunk | packet chunk |
// +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
// . .
// . .
// +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
// | packet chunk | recv delta | recv delta |
// +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
// . .
// . .
// +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
// | recv delta | recv delta | zero padding |
// +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
method | responsibility | Collaborator | Comment |
bool AddReceivedPacket(uint16_t sequence_number, int64_t timestamp_us); | |||
ReceivedPacket
class ReceivedPacket {
public:
ReceivedPacket(uint16_t sequence_number, int16_t delta_ticks)
: sequence_number_(sequence_number),
delta_ticks_(delta_ticks),
received_(true) {}
explicit ReceivedPacket(uint16_t sequence_number)
: sequence_number_(sequence_number), received_(false) {}
ReceivedPacket(const ReceivedPacket&) = default;
ReceivedPacket& operator=(const ReceivedPacket&) = default;
uint16_t sequence_number() const { return sequence_number_; }
int16_t delta_ticks() const { return delta_ticks_; }
int32_t delta_us() const { return delta_ticks_ * kDeltaScaleFactor; }
TimeDelta delta() const { return TimeDelta::Micros(delta_us()); }
bool received() const { return received_; }
private:
uint16_t sequence_number_;
int16_t delta_ticks_;
bool received_;
};
SentPacket
struct PacedPacketInfo {
PacedPacketInfo();
PacedPacketInfo(int probe_cluster_id,
int probe_cluster_min_probes,
int probe_cluster_min_bytes);
bool operator==(const PacedPacketInfo& rhs) const;
// TODO(srte): Move probing info to a separate, optional struct.
static constexpr int kNotAProbe = -1;
int send_bitrate_bps = -1;
int probe_cluster_id = kNotAProbe;
int probe_cluster_min_probes = -1;
int probe_cluster_min_bytes = -1;
int probe_cluster_bytes_sent = 0;
};
struct SentPacket {
Timestamp send_time = Timestamp::PlusInfinity();
// Size of packet with overhead up to IP layer.
DataSize size = DataSize::Zero();
// Size of preceeding packets that are not part of feedback.
DataSize prior_unacked_data = DataSize::Zero();
// Probe cluster id and parameters including bitrate, number of packets and
// number of bytes.
PacedPacketInfo pacing_info;
// True if the packet is an audio packet, false for video, padding, RTX etc.
bool audio = false;
// Transport independent sequence number, any tracked packet should have a
// sequence number that is unique over the whole call and increasing by 1 for
// each packet.
int64_t sequence_number;
// Tracked data in flight when the packet was sent, excluding unacked data.
DataSize data_in_flight = DataSize::Zero();
};
struct PacketResult {
class ReceiveTimeOrder {
public:
bool operator()(const PacketResult& lhs, const PacketResult& rhs);
};
PacketResult();
PacketResult(const PacketResult&);
~PacketResult();
inline bool IsReceived() const { return !receive_time.IsPlusInfinity(); }
SentPacket sent_packet;
Timestamp receive_time = Timestamp::PlusInfinity();
};
struct TransportPacketsFeedback {
TransportPacketsFeedback();
TransportPacketsFeedback(const TransportPacketsFeedback& other);
~TransportPacketsFeedback();
Timestamp feedback_time = Timestamp::PlusInfinity();
Timestamp first_unacked_send_time = Timestamp::PlusInfinity();
DataSize data_in_flight = DataSize::Zero();
DataSize prior_in_flight = DataSize::Zero();
std::vector<PacketResult> packet_feedbacks;
// Arrival times for messages without send time information.
std::vector<Timestamp> sendless_arrival_times;
std::vector<PacketResult> ReceivedWithSendInfo() const;
std::vector<PacketResult> LostWithSendInfo() const;
std::vector<PacketResult> PacketsWithFeedback() const;
std::vector<PacketResult> SortedByReceiveTime() const;
};
class PacketArrivalTimeMap {
public:
// Impossible to request feedback older than what can be represented by 15
// bits.
static constexpr size_t kMaxNumberOfPackets = (1 << 15);
// Indicates if the packet with `sequence_number` has already been received.
bool has_received(int64_t sequence_number) const;
// Returns the sequence number of the first entry in the map, i.e. the
// sequence number that a `begin()` iterator would represent.
int64_t begin_sequence_number() const { return begin_sequence_number_; }
// Returns the sequence number of the element just after the map, i.e. the
// sequence number that an `end()` iterator would represent.
int64_t end_sequence_number() const {
return begin_sequence_number_ + arrival_times.size();
}
// Returns an element by `sequence_number`, which must be valid, i.e.
// between [begin_sequence_number, end_sequence_number).
int64_t get(int64_t sequence_number) {
int64_t pos = sequence_number - begin_sequence_number_;
RTC_DCHECK(pos >= 0 && pos < static_cast<int64_t>(arrival_times.size()));
return arrival_times[pos];
}
// Clamps `sequence_number` between [begin_sequence_number,
// end_sequence_number].
int64_t clamp(int64_t sequence_number) const;
// Erases all elements from the beginning of the map until `sequence_number`.
void EraseTo(int64_t sequence_number);
// Records the fact that a packet with `sequence_number` arrived at
// `arrival_time_ms`.
void AddPacket(int64_t sequence_number, int64_t arrival_time_ms);
// Removes packets from the beginning of the map as long as they are received
// before `sequence_number` and with an age older than `arrival_time_limit`
void RemoveOldPackets(int64_t sequence_number, int64_t arrival_time_limit);
private:
// Deque representing unwrapped sequence number -> time, where the index +
// `begin_sequence_number_` represents the packet's sequence number.
存储了每个包的时间,包号就是 begin_sequence_number_ 加上索引
std::deque<int64_t> arrival_times;
// The unwrapped sequence number for the first element in
// `arrival_times`.
int64_t begin_sequence_number_ = 0;
// Indicates if this map has had any packet added to it. The first packet
// decides the initial sequence number.
bool has_seen_packet_ = false;
};
构造方法
std::unique_ptr<rtcp::TransportFeedback>
RemoteEstimatorProxy::MaybeBuildFeedbackPacket(
bool include_timestamps,
int64_t begin_sequence_number_inclusive, // 起始的包序号 (包含)
int64_t end_sequence_number_exclusive, // 结束的包序号(不包含)
bool is_periodic_update) {
RTC_DCHECK_LT(begin_sequence_number_inclusive, end_sequence_number_exclusive);
int64_t start_seq =
packet_arrival_times_.clamp(begin_sequence_number_inclusive);
int64_t end_seq = packet_arrival_times_.clamp(end_sequence_number_exclusive);
// Create the packet on demand, as it's not certain that there are packets
// in the range that have been received.
// feedback_packet 就是我们要构造的 RTCP 消息
std::unique_ptr<rtcp::TransportFeedback> feedback_packet = nullptr;
int64_t next_sequence_number = begin_sequence_number_inclusive;
// 逐个从 packet_arrival_times_ 中取出各个 RTP 包的到达时间
for (int64_t seq = start_seq; seq < end_seq; ++seq) {
int64_t arrival_time_ms = packet_arrival_times_.get(seq);
// 0 代表包没收到
if (arrival_time_ms == 0) {
// Packet not received.
continue;
}
if (feedback_packet == nullptr) {
feedback_packet =
std::make_unique<rtcp::TransportFeedback>(include_timestamps);
// TODO(sprang): Measure receive times in microseconds and remove the
// conversions below.
feedback_packet->SetMediaSsrc(media_ssrc_);
// Base sequence number is the expected first sequence number. This is
// known, but we might not have actually received it, so the base time
// shall be the time of the first received packet in the feedback.
feedback_packet->SetBase(
static_cast<uint16_t>(begin_sequence_number_inclusive & 0xFFFF),
arrival_time_ms * 1000);
feedback_packet->SetFeedbackSequenceNumber(feedback_packet_count_++);
}
if (!feedback_packet->AddReceivedPacket(static_cast<uint16_t>(seq & 0xFFFF),
arrival_time_ms * 1000)) {
// Could not add timestamp, feedback packet might be full. Return and
// try again with a fresh packet.
break;
}
next_sequence_number = seq + 1;
}
if (is_periodic_update) {
periodic_window_start_seq_ = next_sequence_number;
}
return feedback_packet;
}
Receiver side

class | method | responsibility | Collaborators |
InterArrival | compute the inter-arrival time delta and the size delta between two timestamp groups | ||
RemoteEstimatorProxy | process incoming packetsbuild Transport CC feedback RTCP message | NetworkStateEstimate | |
IncomingPacket(int64_t arrival_time_ms,size_t payload_size,const RTPHeader& header) | |||
MaybeBuildFeedbackPacket | rtcp::TransportFeedbackrtcp::TransportFeedback::ReceivedPacket |
Send side

class | method | responsibility | collaborators |
DelayBasedBwe | Delay based Bandwidth Estimator | ||
IncomingPacketFeedbackVector(const TransportPacketsFeedback& msg,absl::optional<DataRate> acked_bitrate,absl::optional<DataRate> probe_bitrate,absl::optional<NetworkStateEstimate> network_estimate,bool in_alr) | Process the TransportFeedback RTCP message | PacketResultBandwidthUsageTrendlineEstimator | |
IncomingPacketFeedback(const PacketResult& packet_feedback,Timestamp at_time) | |||
TrendlineEstimator | parent interface :DelayIncreaseDetectorInterfaceNetworkStatePredictor | ||
Update(double recv_delta_ms,double send_delta_ms,int64_t send_time_ms,int64_t arrival_time_ms,size_t packet_size,bool calculated_deltas) | |||
void Detect(double trend, double ts_delta, int64_t now_ms); | |||
void UpdateThreshold(double modified_offset, int64_t now_ms); | |||
class RtpTransportControllerSend final
: public RtpTransportControllerSendInterface,
public RtcpBandwidthObserver,
public TransportFeedbackObserver,
public NetworkStateEstimateObserver
{
}
class GoogCcNetworkController : public NetworkControllerInterface {
//…
const std::unique_ptr<ProbeController> probe_controller_;
const std::unique_ptr<CongestionWindowPushbackController>
congestion_window_pushback_controller_;
std::unique_ptr<SendSideBandwidthEstimation> bandwidth_estimation_;
std::unique_ptr<AlrDetector> alr_detector_;
std::unique_ptr<ProbeBitrateEstimator> probe_bitrate_estimator_;
std::unique_ptr<NetworkStateEstimator> network_estimator_;
std::unique_ptr<NetworkStatePredictor> network_state_predictor_;
std::unique_ptr<DelayBasedBwe> delay_based_bwe_;
std::unique_ptr<AcknowledgedBitrateEstimatorInterface>
acknowledged_bitrate_estimator_;
}
class SendSideBandwidthEstimation {
public:
SendSideBandwidthEstimation() = delete;
SendSideBandwidthEstimation(const WebRtcKeyValueConfig* key_value_config,
RtcEventLog* event_log);
~SendSideBandwidthEstimation();
void OnRouteChange();
DataRate target_rate() const;
uint8_t fraction_loss() const { return last_fraction_loss_; }
TimeDelta round_trip_time() const { return last_round_trip_time_; }
DataRate GetEstimatedLinkCapacity() const;
// Call periodically to update estimate.
void UpdateEstimate(Timestamp at_time);
void OnSentPacket(const SentPacket& sent_packet);
void UpdatePropagationRtt(Timestamp at_time, TimeDelta propagation_rtt);
// Call when we receive a RTCP message with TMMBR or REMB.
void UpdateReceiverEstimate(Timestamp at_time, DataRate bandwidth);
// Call when a new delay-based estimate is available.
void UpdateDelayBasedEstimate(Timestamp at_time, DataRate bitrate);
// Call when we receive a RTCP message with a ReceiveBlock.
void UpdatePacketsLost(int64_t packets_lost,
int64_t number_of_packets,
Timestamp at_time);
// Call when we receive a RTCP message with a ReceiveBlock.
void UpdateRtt(TimeDelta rtt, Timestamp at_time);
void SetBitrates(absl::optional<DataRate> send_bitrate,
DataRate min_bitrate,
DataRate max_bitrate,
Timestamp at_time);
void SetSendBitrate(DataRate bitrate, Timestamp at_time);
void SetMinMaxBitrate(DataRate min_bitrate, DataRate max_bitrate);
int GetMinBitrate() const;
void SetAcknowledgedRate(absl::optional<DataRate> acknowledged_rate,
Timestamp at_time);
void IncomingPacketFeedbackVector(const TransportPacketsFeedback& report);
}
class DelayBasedBwe {
public:
struct Result {
Result();
~Result() = default;
bool updated;
bool probe;
DataRate target_bitrate = DataRate::Zero();
bool recovered_from_overuse;
bool backoff_in_alr;
};
explicit DelayBasedBwe(const WebRtcKeyValueConfig* key_value_config,
RtcEventLog* event_log,
NetworkStatePredictor* network_state_predictor);
DelayBasedBwe() = delete;
DelayBasedBwe(const DelayBasedBwe&) = delete;
DelayBasedBwe& operator=(const DelayBasedBwe&) = delete;
virtual ~DelayBasedBwe();
Result IncomingPacketFeedbackVector(
const TransportPacketsFeedback& msg,
absl::optional<DataRate> acked_bitrate,
absl::optional<DataRate> probe_bitrate,
absl::optional<NetworkStateEstimate> network_estimate,
bool in_alr);
void OnRttUpdate(TimeDelta avg_rtt);
bool LatestEstimate(std::vector<uint32_t>* ssrcs, DataRate* bitrate) const;
void SetStartBitrate(DataRate start_bitrate);
void SetMinBitrate(DataRate min_bitrate);
TimeDelta GetExpectedBwePeriod() const;
void SetAlrLimitedBackoffExperiment(bool enabled);
DataRate TriggerOveruse(Timestamp at_time,
absl::optional<DataRate> link_capacity);
DataRate last_estimate() const { return prev_bitrate_; }
//…
// Alternatively, run two separate overuse detectors for audio and video,
// and fall back to the audio one if we haven't seen a video packet in a
// while.
BweSeparateAudioPacketsSettings separate_audio_;
int64_t audio_packets_since_last_video_;
Timestamp last_video_packet_recv_time_;
NetworkStatePredictor* network_state_predictor_;
std::unique_ptr<InterArrival> video_inter_arrival_;
std::unique_ptr<InterArrivalDelta> video_inter_arrival_delta_;
std::unique_ptr<DelayIncreaseDetectorInterface> video_delay_detector_;
std::unique_ptr<InterArrival> audio_inter_arrival_;
std::unique_ptr<InterArrivalDelta> audio_inter_arrival_delta_;
std::unique_ptr<DelayIncreaseDetectorInterface> audio_delay_detector_;
DelayIncreaseDetectorInterface* active_delay_detector_;
Timestamp last_seen_packet_;
bool uma_recorded_;
AimdRateControl rate_control_;
DataRate prev_bitrate_;
bool has_once_detected_overuse_;
BandwidthUsage prev_state_;
const bool use_new_inter_arrival_delta_;
bool alr_limited_backoff_enabled_;
}
class DelayIncreaseDetectorInterface {
public:
DelayIncreaseDetectorInterface() {}
virtual ~DelayIncreaseDetectorInterface() {}
// Update the detector with a new sample. The deltas should represent deltas
// between timestamp groups as defined by the InterArrival class.
virtual void Update(double recv_delta_ms,
double send_delta_ms,
int64_t send_time_ms,
int64_t arrival_time_ms,
size_t packet_size,
bool calculated_deltas) = 0;
virtual BandwidthUsage State() const = 0;
RTC_DISALLOW_COPY_AND_ASSIGN(DelayIncreaseDetectorInterface);
};
class TrendlineEstimator : public DelayIncreaseDetectorInterface {
public:
TrendlineEstimator(const WebRtcKeyValueConfig* key_value_config,
NetworkStatePredictor* network_state_predictor);
~TrendlineEstimator() override;
// Update the estimator with a new sample. The deltas should represent deltas
// between timestamp groups as defined by the InterArrival class.
void Update(double recv_delta_ms,
double send_delta_ms,
int64_t send_time_ms,
int64_t arrival_time_ms,
size_t packet_size,
bool calculated_deltas) override;
void UpdateTrendline(double recv_delta_ms,
double send_delta_ms,
int64_t send_time_ms,
int64_t arrival_time_ms,
size_t packet_size);
BandwidthUsage State() const override;
//…
}
class AcknowledgedBitrateEstimator
: public AcknowledgedBitrateEstimatorInterface {
public:
AcknowledgedBitrateEstimator(
const WebRtcKeyValueConfig* key_value_config,
std::unique_ptr<BitrateEstimator> bitrate_estimator);
explicit AcknowledgedBitrateEstimator(
const WebRtcKeyValueConfig* key_value_config);
~AcknowledgedBitrateEstimator() override;
void IncomingPacketFeedbackVector(
const std::vector<PacketResult>& packet_feedback_vector) override;
absl::optional<DataRate> bitrate() const override;
absl::optional<DataRate> PeekRate() const override;
void SetAlr(bool in_alr) override;
void SetAlrEndedTime(Timestamp alr_ended_time) override;
private:
absl::optional<Timestamp> alr_ended_time_;
bool in_alr_;
std::unique_ptr<BitrateEstimator> bitrate_estimator_;
};
// Computes a bayesian estimate of the throughput given acks containing
// the arrival time and payload size. Samples which are far from the current
// estimate or are based on few packets are given a smaller weight, as they
// are considered to be more likely to have been caused by, e.g., delay spikes
// unrelated to congestion.
class BitrateEstimator {
public:
explicit BitrateEstimator(const WebRtcKeyValueConfig* key_value_config);
virtual ~BitrateEstimator();
virtual void Update(Timestamp at_time, DataSize amount, bool in_alr);
virtual absl::optional<DataRate> bitrate() const;
absl::optional<DataRate> PeekRate() const;
virtual void ExpectFastRateChange();
private:
float UpdateWindow(int64_t now_ms,
int bytes,
int rate_window_ms,
bool* is_small_sample);
int sum_;
FieldTrialConstrained<int> initial_window_ms_;
FieldTrialConstrained<int> noninitial_window_ms_;
FieldTrialParameter<double> uncertainty_scale_;
FieldTrialParameter<double> uncertainty_scale_in_alr_;
FieldTrialParameter<double> small_sample_uncertainty_scale_;
FieldTrialParameter<DataSize> small_sample_threshold_;
FieldTrialParameter<DataRate> uncertainty_symmetry_cap_;
FieldTrialParameter<DataRate> estimate_floor_;
int64_t current_window_ms_;
int64_t prev_time_ms_;
float bitrate_estimate_kbps_;
float bitrate_estimate_var_;
};
Reference
Source codes of webrtc: https://source.chromium.org/chromium/chromium/src/+/main:third_party/webrtc/modules/
- webrtc/modules/remote_bitrate_estimator
- webrtc/modules/congestion_controller
- webrtc/modules/rtp_rtcp/source/rtcp_packet/transport_feedback.cc