webrtc SequenceNumber 比较 IsNewerSequenceNumber
inline bool IsNewerSequenceNumber(uint16_t sequence_number,
uint16_t prev_sequence_number) {
// Distinguish between elements that are exactly 0x8000 apart.
// If s1>s2 and |s1-s2| = 0x8000: IsNewer(s1,s2)=true, IsNewer(s2,s1)=false
// rather than having IsNewer(s1,s2) = IsNewer(s2,s1) = false.
if (static_cast<uint16_t>(sequence_number - prev_sequence_number) == 0x8000) {
return sequence_number > prev_sequence_number;
}
return sequence_number != prev_sequence_number &&
static_cast<uint16_t>(sequence_number - prev_sequence_number) < 0x8000;
}
RTP序列号sequence number
用两字节表示,时间戳timestamp
用四字节表示。所以序列号以及时间戳就存在一个取值范围。由于是无符号数,所以序列号范围为:[0,2^16-1]
,时间戳范围为:[0,2^32-1]
。当达到最大值时,将发生所谓的回绕。例如,序列号到了2^16-1,下个包序列号就将是0。所以我们不能直接根据数学意义上的大小进行序列号以及时间戳的比较。
在WebRTC中定义了一个大小比较算法,包含数字回绕处理,判断是否是更新的数字。下面说下算法原理:
1)假设有两个U
类型的无符号整数:value
,prev_value
;2)定义一个常量
kBreakpoint
,为U
类型取值范围的一半;3)
value > prev_value
,满足value - prev_value = kBreakpoint
时,value
大于prev_value
;4)
value
与prev_value
不相等,满足(U)(valude - prev_value) < kBreakpoint
时,value
大于prev_value
。5)
value
<prev_value
不相等,满足(U)(valude - prev_value) > kBreakpoint
,由于是usign类型大于kBreakpoint其实就是负数,value
小于prev_value
。
总结起来就是value
与prev_value
距离小于取值范围的一半且不相等或者value
与prev_value
距离等于取值范围的一半,value
大于prev_value
,就可以说明value
大于prev_value
。
Timestamp比较也是同样的方法。
inline bool IsNewerTimestamp(uint32_t timestamp, uint32_t prev_timestamp) {
// Distinguish between elements that are exactly 0x80000000 apart.
// If t1>t2 and |t1-t2| = 0x80000000: IsNewer(t1,t2)=true,
// IsNewer(t2,t1)=false
// rather than having IsNewer(t1,t2) = IsNewer(t2,t1) = false.
if (static_cast<uint32_t>(timestamp - prev_timestamp) == 0x80000000) {
return timestamp > prev_timestamp;
}
return timestamp != prev_timestamp &&
static_cast<uint32_t>(timestamp - prev_timestamp) < 0x80000000;
}
基于IsNewerSequenceNumber判断是否乱序IsPacketInOrder
bool StreamStatisticianImpl::InOrderPacketInternal(
uint16_t sequence_number) const {
// First packet is always in order.
if (last_receive_time_ms_ == 0)
return true;
if (IsNewerSequenceNumber(sequence_number, received_seq_max_)) {
return true;
} else {
// If we have a restart of the remote side this packet is still in order.
return !IsNewerSequenceNumber(sequence_number, received_seq_max_ -
max_reordering_threshold_);
}
}
基于RTT判断是否是Nack重发包IsRetransmitOfOldPacket
bool StreamStatisticianImpl::IsRetransmitOfOldPacket(
const RTPHeader& header, int64_t min_rtt) const {
rtc::CritScope cs(&stream_lock_);
if (InOrderPacketInternal(header.sequenceNumber)) {
return false;
}
uint32_t frequency_khz = header.payload_type_frequency / 1000;
assert(frequency_khz > 0);
int64_t time_diff_ms = clock_->TimeInMilliseconds() -
last_receive_time_ms_;
// Diff in time stamp since last received in order.
uint32_t timestamp_diff = header.timestamp - last_received_timestamp_;
uint32_t rtp_time_stamp_diff_ms = timestamp_diff / frequency_khz;
int64_t max_delay_ms = 0;
if (min_rtt == 0) {
// Jitter standard deviation in samples.
float jitter_std = sqrt(static_cast<float>(jitter_q4_ >> 4));
// 2 times the standard deviation => 95% confidence.
// And transform to milliseconds by dividing by the frequency in kHz.
max_delay_ms = static_cast<int64_t>((2 * jitter_std) / frequency_khz);
// Min max_delay_ms is 1.
if (max_delay_ms == 0) {
max_delay_ms = 1;
}
} else {
max_delay_ms = (min_rtt / 3) + 1;
}
return time_diff_ms > rtp_time_stamp_diff_ms + max_delay_ms;
}
- time_diff_ms = 上一个收包的接收时间(last_receive_time_ms_)- 当前包接收时间
- rtp_time_stamp_diff_ms=上一个包的时间戳(last_received_timestamp_)- 减去当前包的时间戳
- 如果
time_diff_ms > rtp_time_stamp_diff_ms + max_delay_ms
则认为当前包是重传包,max_delay_ms 正常情况为max_delay_ms = (min_rtt / 3) + 1
。 - 直观解释就是,当前包的时间戳已经是过去的时间,其加上f(rtt)后仍然是过去的时间,说明可能是接收端通过NACK通知发送端发送的重传包(可能经过了一个rtt),可以认为是重传包。
判定为重传包
bool RtpStreamReceiver::IsPacketRetransmitted(const RTPHeader& header,
bool in_order) const {
// Retransmissions are handled separately if RTX is enabled.
if (rtp_payload_registry_.RtxEnabled())
return false;
StreamStatistician* statistician =
rtp_receive_statistics_->GetStatistician(header.ssrc);
if (!statistician)
return false;
// Check if this is a retransmission.
int64_t min_rtt = 0;
rtp_rtcp_->RTT(rtp_receiver_->SSRC(), nullptr, nullptr, &min_rtt, nullptr);
return !in_order &&
statistician->IsRetransmitOfOldPacket(header, min_rtt);
}
!in_order && statistician->IsRetransmitOfOldPacket(header, min_rtt)
满足乱序,且加上rtt时间后还是过去的的时间就是nack重传包。
References
https://blog.jianchihu.net/webrtc-research-rtp-number-timestamp-compare.html