Update ReceiveStatistics to use Timestamp/TimeDelta to represent time

Bug: webrtc:13757
Change-Id: I1606a14ecf8ccb520428b84eed2f9a8ba746162f
Reviewed-on: https://webrtc-review.googlesource.com/c/src/+/307181
Commit-Queue: Danil Chapovalov <danilchap@webrtc.org>
Reviewed-by: Åsa Persson <asapersson@webrtc.org>
Cr-Commit-Position: refs/heads/main@{#40188}
This commit is contained in:
Danil Chapovalov 2023-05-31 13:21:01 +02:00 committed by WebRTC LUCI CQ
parent a9bba047b7
commit 2197300977
2 changed files with 55 additions and 52 deletions

View File

@ -27,8 +27,16 @@
namespace webrtc {
namespace {
constexpr int64_t kStatisticsTimeoutMs = 8000;
constexpr int64_t kStatisticsProcessIntervalMs = 1000;
constexpr TimeDelta kStatisticsTimeout = TimeDelta::Seconds(8);
constexpr TimeDelta kStatisticsProcessInterval = TimeDelta::Seconds(1);
TimeDelta UnixEpochDelta(Clock& clock) {
Timestamp now = clock.CurrentTime();
NtpTime ntp_now = clock.ConvertTimestampToNtpTime(now);
return TimeDelta::Millis(ntp_now.ToMs() - now.ms() -
rtc::kNtpJan1970Millisecs);
}
} // namespace
StreamStatistician::~StreamStatistician() {}
@ -38,10 +46,8 @@ StreamStatisticianImpl::StreamStatisticianImpl(uint32_t ssrc,
int max_reordering_threshold)
: ssrc_(ssrc),
clock_(clock),
delta_internal_unix_epoch_ms_(clock_->CurrentNtpInMilliseconds() -
clock_->TimeInMilliseconds() -
rtc::kNtpJan1970Millisecs),
incoming_bitrate_(kStatisticsProcessIntervalMs,
delta_internal_unix_epoch_(UnixEpochDelta(*clock_)),
incoming_bitrate_(kStatisticsProcessInterval.ms(),
RateStatistics::kBpsScale),
max_reordering_threshold_(max_reordering_threshold),
enable_retransmit_detection_(false),
@ -49,7 +55,6 @@ StreamStatisticianImpl::StreamStatisticianImpl(uint32_t ssrc,
jitter_q4_(0),
cumulative_loss_(0),
cumulative_loss_rtcp_offset_(0),
last_receive_time_ms_(0),
last_received_timestamp_(0),
received_seq_first_(-1),
received_seq_max_(-1),
@ -61,7 +66,7 @@ StreamStatisticianImpl::~StreamStatisticianImpl() = default;
bool StreamStatisticianImpl::UpdateOutOfOrder(const RtpPacketReceived& packet,
int64_t sequence_number,
int64_t now_ms) {
Timestamp now) {
// Check if `packet` is second packet of a stream restart.
if (received_seq_out_of_order_) {
// Count the previous packet as a received; it was postponed below.
@ -103,7 +108,7 @@ bool StreamStatisticianImpl::UpdateOutOfOrder(const RtpPacketReceived& packet,
return false;
// Old out of order packet, may be retransmit.
if (enable_retransmit_detection_ && IsRetransmitOfOldPacket(packet, now_ms))
if (enable_retransmit_detection_ && IsRetransmitOfOldPacket(packet, now))
receive_counters_.retransmitted.AddPacket(packet);
return true;
}
@ -111,9 +116,8 @@ bool StreamStatisticianImpl::UpdateOutOfOrder(const RtpPacketReceived& packet,
void StreamStatisticianImpl::UpdateCounters(const RtpPacketReceived& packet) {
RTC_DCHECK_EQ(ssrc_, packet.Ssrc());
Timestamp now = clock_->CurrentTime();
int64_t now_ms = now.ms();
incoming_bitrate_.Update(packet.size(), now_ms);
incoming_bitrate_.Update(packet.size(), now.ms());
receive_counters_.transmitted.AddPacket(packet);
--cumulative_loss_;
@ -126,7 +130,7 @@ void StreamStatisticianImpl::UpdateCounters(const RtpPacketReceived& packet) {
last_report_seq_max_ = sequence_number - 1;
received_seq_max_ = sequence_number - 1;
receive_counters_.first_packet_time = now;
} else if (UpdateOutOfOrder(packet, sequence_number, now_ms)) {
} else if (UpdateOutOfOrder(packet, sequence_number, now)) {
return;
}
// In order packet.
@ -140,18 +144,19 @@ void StreamStatisticianImpl::UpdateCounters(const RtpPacketReceived& packet) {
if (packet.Timestamp() != last_received_timestamp_ &&
(receive_counters_.transmitted.packets -
receive_counters_.retransmitted.packets) > 1) {
UpdateJitter(packet, now_ms);
UpdateJitter(packet, now);
}
last_received_timestamp_ = packet.Timestamp();
last_receive_time_ms_ = now_ms;
last_receive_time_ = now;
}
void StreamStatisticianImpl::UpdateJitter(const RtpPacketReceived& packet,
int64_t receive_time_ms) {
int64_t receive_diff_ms = receive_time_ms - last_receive_time_ms_;
RTC_DCHECK_GE(receive_diff_ms, 0);
uint32_t receive_diff_rtp = static_cast<uint32_t>(
(receive_diff_ms * packet.payload_type_frequency()) / 1000);
Timestamp receive_time) {
RTC_DCHECK(last_receive_time_.has_value());
TimeDelta receive_diff = receive_time - *last_receive_time_;
RTC_DCHECK_GE(receive_diff, TimeDelta::Zero());
uint32_t receive_diff_rtp =
(receive_diff * packet.payload_type_frequency()).seconds<uint32_t>();
int32_t time_diff_samples =
receive_diff_rtp - (packet.Timestamp() - last_received_timestamp_);
@ -178,13 +183,13 @@ void StreamStatisticianImpl::ReviseFrequencyAndJitter(
if (payload_type_frequency != 0) {
if (last_payload_type_frequency_ != 0) {
// Value in "jitter_q4_" variable is a number of samples.
// I.e. jitter = timestamp (ms) * frequency (kHz).
// I.e. jitter = timestamp (s) * frequency (Hz).
// Since the frequency has changed we have to update the number of samples
// accordingly. The new value should rely on a new frequency.
// If we don't do such procedure we end up with the number of samples that
// cannot be converted into milliseconds correctly
// (i.e. jitter_ms = jitter_q4_ >> 4 / (payload_type_frequency / 1000)).
// cannot be converted into TimeDelta correctly
// (i.e. jitter = jitter_q4_ >> 4 / payload_type_frequency).
// In such case, the number of samples has a "mix".
// Doing so we pretend that everything prior and including the current
@ -219,11 +224,11 @@ RtpReceiveStats StreamStatisticianImpl::GetStats() const {
// Divide value in fractional seconds by frequency to get jitter in
// fractional seconds.
stats.interarrival_jitter =
webrtc::TimeDelta::Seconds(stats.jitter) / last_payload_type_frequency_;
TimeDelta::Seconds(stats.jitter) / last_payload_type_frequency_;
}
if (last_receive_time_ms_ > 0) {
if (last_receive_time_.has_value()) {
stats.last_packet_received_timestamp_ms =
last_receive_time_ms_ + delta_internal_unix_epoch_ms_;
(*last_receive_time_ + delta_internal_unix_epoch_).ms();
}
stats.packet_counter = receive_counters_.transmitted;
return stats;
@ -231,12 +236,12 @@ RtpReceiveStats StreamStatisticianImpl::GetStats() const {
void StreamStatisticianImpl::MaybeAppendReportBlockAndReset(
std::vector<rtcp::ReportBlock>& report_blocks) {
int64_t now_ms = clock_->TimeInMilliseconds();
if (now_ms - last_receive_time_ms_ >= kStatisticsTimeoutMs) {
// Not active.
if (!ReceivedRtpPacket()) {
return;
}
if (!ReceivedRtpPacket()) {
Timestamp now = clock_->CurrentTime();
if (now - *last_receive_time_ >= kStatisticsTimeout) {
// Not active.
return;
}
@ -278,9 +283,9 @@ void StreamStatisticianImpl::MaybeAppendReportBlockAndReset(
// Only for report blocks in RTCP SR and RR.
last_report_cumulative_loss_ = cumulative_loss_;
last_report_seq_max_ = received_seq_max_;
BWE_TEST_LOGGING_PLOT_WITH_SSRC(1, "cumulative_loss_pkts", now_ms,
BWE_TEST_LOGGING_PLOT_WITH_SSRC(1, "cumulative_loss_pkts", now.ms(),
cumulative_loss_, ssrc_);
BWE_TEST_LOGGING_PLOT_WITH_SSRC(1, "received_seq_max_pkts", now_ms,
BWE_TEST_LOGGING_PLOT_WITH_SSRC(1, "received_seq_max_pkts", now.ms(),
(received_seq_max_ - received_seq_first_),
ssrc_);
}
@ -310,30 +315,26 @@ uint32_t StreamStatisticianImpl::BitrateReceived() const {
bool StreamStatisticianImpl::IsRetransmitOfOldPacket(
const RtpPacketReceived& packet,
int64_t now_ms) const {
uint32_t frequency_khz = packet.payload_type_frequency() / 1000;
RTC_DCHECK_GT(frequency_khz, 0);
int64_t time_diff_ms = now_ms - last_receive_time_ms_;
Timestamp now) const {
int frequency_hz = packet.payload_type_frequency();
RTC_DCHECK(last_receive_time_.has_value());
RTC_DCHECK_GT(frequency_hz, 0);
TimeDelta time_diff = now - *last_receive_time_;
// Diff in time stamp since last received in order.
uint32_t timestamp_diff = packet.Timestamp() - last_received_timestamp_;
uint32_t rtp_time_stamp_diff_ms = timestamp_diff / frequency_khz;
int64_t max_delay_ms = 0;
TimeDelta rtp_time_stamp_diff =
TimeDelta::Seconds(timestamp_diff) / frequency_hz;
// Jitter standard deviation in samples.
float jitter_std = 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 is 1ms.
TimeDelta max_delay = std::max(
TimeDelta::Seconds(2 * jitter_std / frequency_hz), TimeDelta::Millis(1));
// Min max_delay_ms is 1.
if (max_delay_ms == 0) {
max_delay_ms = 1;
}
return time_diff_ms > rtp_time_stamp_diff_ms + max_delay_ms;
return time_diff > rtp_time_stamp_diff + max_delay;
}
std::unique_ptr<ReceiveStatistics> ReceiveStatistics::Create(Clock* clock) {

View File

@ -18,6 +18,8 @@
#include <vector>
#include "absl/types/optional.h"
#include "api/units/time_delta.h"
#include "api/units/timestamp.h"
#include "modules/rtp_rtcp/include/receive_statistics.h"
#include "modules/rtp_rtcp/source/rtcp_packet/report_block.h"
#include "rtc_base/containers/flat_map.h"
@ -63,21 +65,21 @@ class StreamStatisticianImpl : public StreamStatisticianImplInterface {
private:
bool IsRetransmitOfOldPacket(const RtpPacketReceived& packet,
int64_t now_ms) const;
void UpdateJitter(const RtpPacketReceived& packet, int64_t receive_time_ms);
Timestamp now) const;
void UpdateJitter(const RtpPacketReceived& packet, Timestamp receive_time);
void ReviseFrequencyAndJitter(int payload_type_frequency);
// Updates StreamStatistician for out of order packets.
// Returns true if packet considered to be out of order.
bool UpdateOutOfOrder(const RtpPacketReceived& packet,
int64_t sequence_number,
int64_t now_ms);
Timestamp now);
// Checks if this StreamStatistician received any rtp packets.
bool ReceivedRtpPacket() const { return received_seq_first_ >= 0; }
bool ReceivedRtpPacket() const { return last_receive_time_.has_value(); }
const uint32_t ssrc_;
Clock* const clock_;
// Delta used to map internal timestamps to Unix epoch ones.
const int64_t delta_internal_unix_epoch_ms_;
const TimeDelta delta_internal_unix_epoch_;
RateStatistics incoming_bitrate_;
// In number of packets or sequence numbers.
int max_reordering_threshold_;
@ -94,7 +96,7 @@ class StreamStatisticianImpl : public StreamStatisticianImplInterface {
// senders, in particular, our own loss-based bandwidth estimator.
int32_t cumulative_loss_rtcp_offset_;
int64_t last_receive_time_ms_;
absl::optional<Timestamp> last_receive_time_;
uint32_t last_received_timestamp_;
RtpSequenceNumberUnwrapper seq_unwrapper_;
int64_t received_seq_first_;