RTT intermediate calculation use ntp time instead of milliseconds.

Compact NTP representation was designed exactly for that purpose: calculate RTT. No need to map to ms before doing arithmetic on this values.
  Because of this change there is no need to keep mapping between compact ntp presentation and milliseconds in the RTCPSender.

BUG=webrtc:5565
R=stefan@webrtc.org

Review URL: https://codereview.webrtc.org/1491843004 .

Cr-Commit-Position: refs/heads/master@{#11710}
This commit is contained in:
Danil Chapovalov
2016-02-22 18:59:36 +01:00
parent 723ead844b
commit a094fd1550
8 changed files with 120 additions and 182 deletions

View File

@ -21,6 +21,8 @@
#include "webrtc/modules/rtp_rtcp/source/rtcp_packet/transport_feedback.h"
#include "webrtc/modules/rtp_rtcp/source/rtcp_utility.h"
#include "webrtc/modules/rtp_rtcp/source/rtp_rtcp_impl.h"
#include "webrtc/modules/rtp_rtcp/source/time_util.h"
#include "webrtc/system_wrappers/include/ntp_time.h"
namespace webrtc {
using RTCPHelp::RTCPPacketInformation;
@ -487,13 +489,6 @@ void RTCPReceiver::HandleReportBlock(
return;
}
// To avoid problem with acquiring _criticalSectionRTCPSender while holding
// _criticalSectionRTCPReceiver.
_criticalSectionRTCPReceiver->Leave();
int64_t sendTimeMS =
_rtpRtcp.SendTimeOfSendReport(rtcpPacket.ReportBlockItem.LastSR);
_criticalSectionRTCPReceiver->Enter();
RTCPReportBlockInformation* reportBlock =
CreateOrGetReportBlockInformation(remoteSSRC,
rtcpPacket.ReportBlockItem.SSRC);
@ -526,60 +521,48 @@ void RTCPReceiver::HandleReportBlock(
reportBlock->remoteMaxJitter = rtcpPacket.ReportBlockItem.Jitter;
}
uint32_t delaySinceLastSendReport =
rtcpPacket.ReportBlockItem.DelayLastSR;
uint32_t send_time = rtcpPacket.ReportBlockItem.LastSR;
uint32_t rtt = 0;
// local NTP time when we received this
uint32_t lastReceivedRRNTPsecs = 0;
uint32_t lastReceivedRRNTPfrac = 0;
if (send_time > 0) {
uint32_t delay = rtcpPacket.ReportBlockItem.DelayLastSR;
// Local NTP time.
uint32_t receive_time = CompactNtp(NtpTime(*_clock));
_clock->CurrentNtp(lastReceivedRRNTPsecs, lastReceivedRRNTPfrac);
// time when we received this in MS
int64_t receiveTimeMS = Clock::NtpToMs(lastReceivedRRNTPsecs,
lastReceivedRRNTPfrac);
// Estimate RTT
uint32_t d = (delaySinceLastSendReport & 0x0000ffff) * 1000;
d /= 65536;
d += ((delaySinceLastSendReport & 0xffff0000) >> 16) * 1000;
int64_t RTT = 0;
if (sendTimeMS > 0) {
RTT = receiveTimeMS - d - sendTimeMS;
if (RTT <= 0) {
RTT = 1;
}
if (RTT > reportBlock->maxRTT) {
// store max RTT
reportBlock->maxRTT = RTT;
// RTT in 1/(2^16) seconds.
uint32_t rtt_ntp = receive_time - delay - send_time;
// Convert to 1/1000 seconds (milliseconds).
uint32_t rtt_ms = CompactNtpIntervalToMs(rtt_ntp);
rtt = std::max<uint32_t>(rtt_ms, 1);
if (rtt > reportBlock->maxRTT) {
// Store max RTT.
reportBlock->maxRTT = rtt;
}
if (reportBlock->minRTT == 0) {
// first RTT
reportBlock->minRTT = RTT;
} else if (RTT < reportBlock->minRTT) {
// Store min RTT
reportBlock->minRTT = RTT;
// First RTT.
reportBlock->minRTT = rtt;
} else if (rtt < reportBlock->minRTT) {
// Store min RTT.
reportBlock->minRTT = rtt;
}
// store last RTT
reportBlock->RTT = RTT;
// Store last RTT.
reportBlock->RTT = rtt;
// store average RTT
if (reportBlock->numAverageCalcs != 0) {
float ac = static_cast<float>(reportBlock->numAverageCalcs);
float newAverage =
((ac / (ac + 1)) * reportBlock->avgRTT) + ((1 / (ac + 1)) * RTT);
((ac / (ac + 1)) * reportBlock->avgRTT) + ((1 / (ac + 1)) * rtt);
reportBlock->avgRTT = static_cast<int64_t>(newAverage + 0.5f);
} else {
// first RTT
reportBlock->avgRTT = RTT;
// First RTT.
reportBlock->avgRTT = rtt;
}
reportBlock->numAverageCalcs++;
}
TRACE_COUNTER_ID1(TRACE_DISABLED_BY_DEFAULT("webrtc_rtp"), "RR_RTT", rb.SSRC,
RTT);
rtt);
rtcpPacketInformation.AddReportInfo(*reportBlock);
}
@ -934,28 +917,19 @@ void RTCPReceiver::HandleXrDlrrReportBlockItem(
rtcpPacketInformation.xr_dlrr_item = true;
// To avoid problem with acquiring _criticalSectionRTCPSender while holding
// _criticalSectionRTCPReceiver.
_criticalSectionRTCPReceiver->Leave();
int64_t send_time_ms;
bool found = _rtpRtcp.SendTimeOfXrRrReport(
packet.XRDLRRReportBlockItem.LastRR, &send_time_ms);
_criticalSectionRTCPReceiver->Enter();
if (!found) {
// The send_time and delay_rr fields are in units of 1/2^16 sec.
uint32_t send_time = packet.XRDLRRReportBlockItem.LastRR;
// RFC3411, section 4.5, LRR field discription states:
// If no such block has been received, the field is set to zero.
if (send_time == 0)
return;
}
// The DelayLastRR field is in units of 1/65536 sec.
uint32_t delay_rr_ms =
(((packet.XRDLRRReportBlockItem.DelayLastRR & 0x0000ffff) * 1000) >> 16) +
(((packet.XRDLRRReportBlockItem.DelayLastRR & 0xffff0000) >> 16) * 1000);
uint32_t delay_rr = packet.XRDLRRReportBlockItem.DelayLastRR;
uint32_t now = CompactNtp(NtpTime(*_clock));
int64_t rtt = _clock->CurrentNtpInMilliseconds() - delay_rr_ms - send_time_ms;
xr_rr_rtt_ms_ = std::max<int64_t>(rtt, 1);
uint32_t rtt_ntp = now - delay_rr - send_time;
uint32_t rtt_ms = CompactNtpIntervalToMs(rtt_ntp);
xr_rr_rtt_ms_ = std::max<uint32_t>(rtt_ms, 1);
rtcpPacketInformation.rtcpPacketTypeFlags |= kRtcpXrDlrrReportBlock;
}

View File

@ -36,7 +36,8 @@
#include "webrtc/modules/rtp_rtcp/source/rtcp_receiver.h"
#include "webrtc/modules/rtp_rtcp/source/rtcp_sender.h"
#include "webrtc/modules/rtp_rtcp/source/rtp_rtcp_impl.h"
#include "webrtc/modules/rtp_rtcp/source/rtp_utility.h"
#include "webrtc/modules/rtp_rtcp/source/time_util.h"
#include "webrtc/system_wrappers/include/ntp_time.h"
namespace webrtc {
@ -179,6 +180,42 @@ TEST_F(RtcpReceiverTest, InjectSrPacketFromExpectedPeer) {
EXPECT_EQ(kRtcpSr, rtcp_packet_info_.rtcpPacketTypeFlags);
}
TEST_F(RtcpReceiverTest, InjectSrPacketCalculatesRTT) {
Random r(0x0123456789abcdef);
const uint32_t kSenderSsrc = r.Rand(0x00000001u, 0xfffffffeu);
const uint32_t kRemoteSsrc = r.Rand(0x00000001u, 0xfffffffeu);
const int64_t kRttMs = r.Rand(1, 18 * 3600 * 1000);
const uint32_t kDelayNtp = r.Rand<uint32_t>();
const uint32_t kDelayMs = CompactNtpIntervalToMs(kDelayNtp);
rtcp_receiver_->SetRemoteSSRC(kSenderSsrc);
std::set<uint32_t> ssrcs;
ssrcs.insert(kRemoteSsrc);
rtcp_receiver_->SetSsrcs(kRemoteSsrc, ssrcs);
int64_t rtt_ms = 0;
EXPECT_EQ(
-1, rtcp_receiver_->RTT(kSenderSsrc, &rtt_ms, nullptr, nullptr, nullptr));
uint32_t sent_ntp = CompactNtp(NtpTime(system_clock_));
system_clock_.AdvanceTimeMilliseconds(kRttMs + kDelayMs);
rtcp::SenderReport sr;
sr.From(kSenderSsrc);
rtcp::ReportBlock block;
block.To(kRemoteSsrc);
block.WithLastSr(sent_ntp);
block.WithDelayLastSr(kDelayNtp);
sr.WithReportBlock(block);
rtc::Buffer packet = sr.Build();
EXPECT_EQ(0, InjectRtcpPacket(packet.data(), packet.size()));
EXPECT_EQ(
0, rtcp_receiver_->RTT(kSenderSsrc, &rtt_ms, nullptr, nullptr, nullptr));
EXPECT_NEAR(kRttMs, rtt_ms, 1);
}
TEST_F(RtcpReceiverTest, InjectRrPacket) {
const uint32_t kSenderSsrc = 0x10203;
rtcp::ReceiverReport rr;
@ -668,8 +705,7 @@ TEST_F(RtcpReceiverTest, InjectExtendedReportsDlrrPacketWithSubBlock) {
xr.WithDlrr(dlrr);
rtc::Buffer packet = xr.Build();
EXPECT_EQ(0, InjectRtcpPacket(packet.data(), packet.size()));
// The parser should note the DLRR report block item, but not flag the packet
// since the RTT is not estimated.
// The parser should note the DLRR report block item.
EXPECT_TRUE(rtcp_packet_info_.xr_dlrr_item);
}
@ -688,8 +724,7 @@ TEST_F(RtcpReceiverTest, InjectExtendedReportsDlrrPacketWithMultipleSubBlocks) {
xr.WithDlrr(dlrr);
rtc::Buffer packet = xr.Build();
EXPECT_EQ(0, InjectRtcpPacket(packet.data(), packet.size()));
// The parser should note the DLRR report block item, but not flag the packet
// since the RTT is not estimated.
// The parser should note the DLRR report block item.
EXPECT_TRUE(rtcp_packet_info_.xr_dlrr_item);
}
@ -701,7 +736,7 @@ TEST_F(RtcpReceiverTest, InjectExtendedReportsPacketWithMultipleReportBlocks) {
rtcp::Rrtr rrtr;
rtcp::Dlrr dlrr;
dlrr.WithDlrrItem(kSourceSsrc, 0x12345, 0x67890);
dlrr.WithDlrrItem(kSourceSsrc, 0, 0x67890);
rtcp::VoipMetric metric;
metric.To(kSourceSsrc);
rtcp::ExtendedReports xr;
@ -711,11 +746,11 @@ TEST_F(RtcpReceiverTest, InjectExtendedReportsPacketWithMultipleReportBlocks) {
xr.WithVoipMetric(metric);
rtc::Buffer packet = xr.Build();
EXPECT_EQ(0, InjectRtcpPacket(packet.data(), packet.size()));
// The parser should not flag the packet since the RTT is not estimated.
EXPECT_EQ(static_cast<unsigned int>(kRtcpXrReceiverReferenceTime +
kRtcpXrVoipMetric),
rtcp_packet_info_.rtcpPacketTypeFlags);
// The parser should note the DLRR report block item, but not flag the packet
// since the RTT is not estimated.
// The parser should note the DLRR report block item.
EXPECT_TRUE(rtcp_packet_info_.xr_dlrr_item);
}
@ -755,6 +790,32 @@ TEST_F(RtcpReceiverTest, TestXrRrRttInitiallyFalse) {
EXPECT_FALSE(rtcp_receiver_->GetAndResetXrRrRtt(&rtt_ms));
}
TEST_F(RtcpReceiverTest, RttCalculatedAfterXrDlrr) {
Random rand(0x0123456789abcdef);
const uint32_t kSourceSsrc = rand.Rand(0x00000001u, 0xfffffffeu);
const uint32_t kRttMs = rand.Rand(1, 18 * 3600 * 1000);
const uint32_t kDelayNtp = rand.Rand<uint32_t>();
const uint32_t kDelayMs = CompactNtpIntervalToMs(kDelayNtp);
std::set<uint32_t> ssrcs;
ssrcs.insert(kSourceSsrc);
rtcp_receiver_->SetSsrcs(kSourceSsrc, ssrcs);
NtpTime now(system_clock_);
uint32_t sent_ntp = CompactNtp(now);
system_clock_.AdvanceTimeMilliseconds(kRttMs + kDelayMs);
rtcp::Dlrr dlrr;
dlrr.WithDlrrItem(kSourceSsrc, sent_ntp, kDelayNtp);
rtcp::ExtendedReports xr;
xr.From(0x2345);
xr.WithDlrr(dlrr);
rtc::Buffer packet = xr.Build();
EXPECT_EQ(0, InjectRtcpPacket(packet.data(), packet.size()));
int64_t rtt_ms = 0;
EXPECT_TRUE(rtcp_receiver_->GetAndResetXrRrRtt(&rtt_ms));
EXPECT_NEAR(kRttMs, rtt_ms, 1);
}
TEST_F(RtcpReceiverTest, LastReceivedXrReferenceTimeInfoInitiallyFalse) {
RtcpReceiveTimeInfo info;
EXPECT_FALSE(rtcp_receiver_->LastReceivedXrReferenceTimeInfo(&info));
@ -763,8 +824,7 @@ TEST_F(RtcpReceiverTest, LastReceivedXrReferenceTimeInfoInitiallyFalse) {
TEST_F(RtcpReceiverTest, GetLastReceivedExtendedReportsReferenceTimeInfo) {
const uint32_t kSenderSsrc = 0x123456;
const NtpTime kNtp(0x10203, 0x40506);
const uint32_t kNtpMid =
RTCPUtility::MidNtp(kNtp.seconds(), kNtp.fractions());
const uint32_t kNtpMid = CompactNtp(kNtp);
rtcp::Rrtr rrtr;
rrtr.WithNtp(kNtp);

View File

@ -195,8 +195,6 @@ RTCPSender::RTCPSender(
xr_send_receiver_reference_time_enabled_(false),
packet_type_counter_observer_(packet_type_counter_observer) {
memset(last_send_report_, 0, sizeof(last_send_report_));
memset(last_rtcp_time_, 0, sizeof(last_rtcp_time_));
RTC_DCHECK(transport_ != nullptr);
builders_[kRtcpSr] = &RTCPSender::BuildSR;
@ -445,46 +443,7 @@ bool RTCPSender::TimeToSendRTCPReport(bool sendKeyframeBeforeRTP) const {
return false;
}
int64_t RTCPSender::SendTimeOfSendReport(uint32_t sendReport) {
CriticalSectionScoped lock(critical_section_rtcp_sender_.get());
// This is only saved when we are the sender
if ((last_send_report_[0] == 0) || (sendReport == 0)) {
return 0; // will be ignored
} else {
for (int i = 0; i < RTCP_NUMBER_OF_SR; ++i) {
if (last_send_report_[i] == sendReport)
return last_rtcp_time_[i];
}
}
return 0;
}
bool RTCPSender::SendTimeOfXrRrReport(uint32_t mid_ntp,
int64_t* time_ms) const {
CriticalSectionScoped lock(critical_section_rtcp_sender_.get());
if (last_xr_rr_.empty()) {
return false;
}
std::map<uint32_t, int64_t>::const_iterator it = last_xr_rr_.find(mid_ntp);
if (it == last_xr_rr_.end()) {
return false;
}
*time_ms = it->second;
return true;
}
rtc::scoped_ptr<rtcp::RtcpPacket> RTCPSender::BuildSR(const RtcpContext& ctx) {
for (int i = (RTCP_NUMBER_OF_SR - 2); i >= 0; i--) {
// shift old
last_send_report_[i + 1] = last_send_report_[i];
last_rtcp_time_[i + 1] = last_rtcp_time_[i];
}
last_rtcp_time_[0] = Clock::NtpToMs(ctx.ntp_sec_, ctx.ntp_frac_);
last_send_report_[0] = (ctx.ntp_sec_ << 16) + (ctx.ntp_frac_ >> 16);
// The timestamp of this RTCP packet should be estimated as the timestamp of
// the frame being captured at this moment. We are calculating that
// timestamp as the last frame's timestamp + the time since the last frame
@ -752,11 +711,6 @@ rtc::scoped_ptr<rtcp::RtcpPacket> RTCPSender::BuildBYE(const RtcpContext& ctx) {
rtc::scoped_ptr<rtcp::RtcpPacket> RTCPSender::BuildReceiverReferenceTime(
const RtcpContext& ctx) {
if (last_xr_rr_.size() >= RTCP_NUMBER_OF_SR)
last_xr_rr_.erase(last_xr_rr_.begin());
last_xr_rr_.insert(std::pair<uint32_t, int64_t>(
RTCPUtility::MidNtp(ctx.ntp_sec_, ctx.ntp_frac_),
Clock::NtpToMs(ctx.ntp_sec_, ctx.ntp_frac_)));
rtcp::ExtendedReports* xr = new rtcp::ExtendedReports();
xr->From(ssrc_);

View File

@ -106,10 +106,6 @@ class RTCPSender {
int32_t RemoveMixedCNAME(uint32_t SSRC);
int64_t SendTimeOfSendReport(uint32_t sendReport);
bool SendTimeOfXrRrReport(uint32_t mid_ntp, int64_t* time_ms) const;
bool TimeToSendRTCPReport(bool sendKeyframeBeforeRTP = false) const;
int32_t SendRTCP(const FeedbackState& feedback_state,
@ -231,17 +227,6 @@ class RTCPSender {
std::map<uint32_t, std::string> csrc_cnames_
GUARDED_BY(critical_section_rtcp_sender_);
// Sent
uint32_t last_send_report_[RTCP_NUMBER_OF_SR] GUARDED_BY(
critical_section_rtcp_sender_); // allow packet loss and RTT above 1 sec
int64_t last_rtcp_time_[RTCP_NUMBER_OF_SR] GUARDED_BY(
critical_section_rtcp_sender_);
// Sent XR receiver reference time report.
// <mid ntp (mid 32 bits of the 64 bits NTP timestamp), send time in ms>.
std::map<uint32_t, int64_t> last_xr_rr_
GUARDED_BY(critical_section_rtcp_sender_);
// send CSRCs
std::vector<uint32_t> csrcs_ GUARDED_BY(critical_section_rtcp_sender_);

View File

@ -643,34 +643,6 @@ TEST_F(RtcpSenderTest, TestNoXrRrtrSentIfNotEnabled) {
EXPECT_EQ(0, parser()->rrtr()->num_packets());
}
TEST_F(RtcpSenderTest, TestSendTimeOfXrRrtr) {
rtcp_sender_->SetRTCPStatus(RtcpMode::kCompound);
RTCPSender::FeedbackState feedback_state = rtp_rtcp_impl_->GetFeedbackState();
EXPECT_EQ(0, rtcp_sender_->SetSendingStatus(feedback_state, false));
rtcp_sender_->SendRtcpXrReceiverReferenceTime(true);
uint32_t ntp_sec;
uint32_t ntp_frac;
clock_.CurrentNtp(ntp_sec, ntp_frac);
uint32_t initial_mid_ntp = RTCPUtility::MidNtp(ntp_sec, ntp_frac);
// No packet sent.
int64_t time_ms;
EXPECT_FALSE(rtcp_sender_->SendTimeOfXrRrReport(initial_mid_ntp, &time_ms));
// Send XR RR packets.
for (int i = 0; i <= RTCP_NUMBER_OF_SR; ++i) {
EXPECT_EQ(0, rtcp_sender_->SendRTCP(feedback_state, kRtcpReport));
EXPECT_EQ(i + 1, test_transport_.parser_.rrtr()->num_packets());
clock_.CurrentNtp(ntp_sec, ntp_frac);
uint32_t mid_ntp = RTCPUtility::MidNtp(ntp_sec, ntp_frac);
EXPECT_TRUE(rtcp_sender_->SendTimeOfXrRrReport(mid_ntp, &time_ms));
EXPECT_EQ(clock_.CurrentNtpInMilliseconds(), time_ms);
clock_.AdvanceTimeMilliseconds(1000);
}
// The first report should no longer be stored.
EXPECT_FALSE(rtcp_sender_->SendTimeOfXrRrReport(initial_mid_ntp, &time_ms));
}
TEST_F(RtcpSenderTest, TestRegisterRtcpPacketTypeObserver) {
RtcpPacketTypeCounterObserverImpl observer;
rtcp_sender_.reset(new RTCPSender(false, &clock_, receive_statistics_.get(),

View File

@ -896,16 +896,6 @@ int32_t ModuleRtpRtcpImpl::SendRTCPReferencePictureSelection(
GetFeedbackState(), kRtcpRpsi, 0, 0, false, picture_id);
}
int64_t ModuleRtpRtcpImpl::SendTimeOfSendReport(
const uint32_t send_report) {
return rtcp_sender_.SendTimeOfSendReport(send_report);
}
bool ModuleRtpRtcpImpl::SendTimeOfXrRrReport(
uint32_t mid_ntp, int64_t* time_ms) const {
return rtcp_sender_.SendTimeOfXrRrReport(mid_ntp, time_ms);
}
void ModuleRtpRtcpImpl::OnReceivedNACK(
const std::list<uint16_t>& nack_sequence_numbers) {
for (uint16_t nack_sequence_number : nack_sequence_numbers) {

View File

@ -309,10 +309,10 @@ TEST_F(RtpRtcpImplTest, Rtt) {
int64_t max_rtt;
EXPECT_EQ(0,
sender_.impl_->RTT(kReceiverSsrc, &rtt, &avg_rtt, &min_rtt, &max_rtt));
EXPECT_EQ(2 * kOneWayNetworkDelayMs, rtt);
EXPECT_EQ(2 * kOneWayNetworkDelayMs, avg_rtt);
EXPECT_EQ(2 * kOneWayNetworkDelayMs, min_rtt);
EXPECT_EQ(2 * kOneWayNetworkDelayMs, max_rtt);
EXPECT_NEAR(2 * kOneWayNetworkDelayMs, rtt, 1);
EXPECT_NEAR(2 * kOneWayNetworkDelayMs, avg_rtt, 1);
EXPECT_NEAR(2 * kOneWayNetworkDelayMs, min_rtt, 1);
EXPECT_NEAR(2 * kOneWayNetworkDelayMs, max_rtt, 1);
// No RTT from other ssrc.
EXPECT_EQ(-1,
@ -322,8 +322,9 @@ TEST_F(RtpRtcpImplTest, Rtt) {
EXPECT_EQ(0, sender_.rtt_stats_.LastProcessedRtt());
EXPECT_EQ(0, sender_.impl_->rtt_ms());
sender_.impl_->Process();
EXPECT_EQ(2 * kOneWayNetworkDelayMs, sender_.rtt_stats_.LastProcessedRtt());
EXPECT_EQ(2 * kOneWayNetworkDelayMs, sender_.impl_->rtt_ms());
EXPECT_NEAR(2 * kOneWayNetworkDelayMs, sender_.rtt_stats_.LastProcessedRtt(),
1);
EXPECT_NEAR(2 * kOneWayNetworkDelayMs, sender_.impl_->rtt_ms(), 1);
}
TEST_F(RtpRtcpImplTest, SetRtcpXrRrtrStatus) {
@ -346,8 +347,9 @@ TEST_F(RtpRtcpImplTest, RttForReceiverOnly) {
EXPECT_EQ(0, receiver_.rtt_stats_.LastProcessedRtt());
EXPECT_EQ(0, receiver_.impl_->rtt_ms());
receiver_.impl_->Process();
EXPECT_EQ(2 * kOneWayNetworkDelayMs, receiver_.rtt_stats_.LastProcessedRtt());
EXPECT_EQ(2 * kOneWayNetworkDelayMs, receiver_.impl_->rtt_ms());
EXPECT_NEAR(2 * kOneWayNetworkDelayMs,
receiver_.rtt_stats_.LastProcessedRtt(), 1);
EXPECT_NEAR(2 * kOneWayNetworkDelayMs, receiver_.impl_->rtt_ms(), 1);
}
TEST_F(RtpRtcpImplTest, NoSrBeforeMedia) {

View File

@ -41,7 +41,8 @@ inline uint32_t CompactNtp(NtpTime ntp) {
// Converts interval between compact ntp timestamps to milliseconds.
// This interval can be upto ~18.2 hours (2^16 seconds).
inline uint32_t CompactNtpIntervalToMs(uint32_t compact_ntp_interval) {
return static_cast<uint64_t>(compact_ntp_interval) * 1000 / (1 << 16);
uint64_t value = static_cast<uint64_t>(compact_ntp_interval);
return (value * 1000 + (1 << 15)) >> 16;
}
} // namespace webrtc