Bug 1729455 - Modularize RTCStatsTimestampMaker::GetNow(). r=ng,bwc

This is needed in order to provide precision reduction on timestamps from
libwebrtc. Similarly useful for providing a clock adapter so libwebrtc can use
our clock.

Differential Revision: https://phabricator.services.mozilla.com/D125715
This commit is contained in:
Andreas Pehrson 2021-11-03 15:23:23 +00:00
parent 3d7a221d58
commit 4a1326da33
16 changed files with 137 additions and 58 deletions

View File

@ -2649,7 +2649,8 @@ nsTArray<RefPtr<dom::RTCStatsPromise>> PeerConnectionImpl::GetSenderStats(
[&](RTCOutboundRtpStreamStats& aLocal) {
ssrc.apply(
[&](uint32_t aSsrc) { aLocal.mSsrc.Construct(aSsrc); });
aLocal.mTimestamp.Construct(aPipeline->GetNow());
aLocal.mTimestamp.Construct(
aPipeline->GetTimestampMaker().GetNow());
aLocal.mId.Construct(localId);
aLocal.mType.Construct(RTCStatsType::Outbound_rtp);
aLocal.mMediaType.Construct(

View File

@ -267,7 +267,8 @@ nsTArray<RefPtr<RTCStatsPromise>> RTCRtpReceiver::GetStatsInternal() {
auto constructCommonInboundRtpStats =
[&](RTCInboundRtpStreamStats& aLocal) {
aLocal.mTimestamp.Construct(pipeline->GetNow());
aLocal.mTimestamp.Construct(
pipeline->GetTimestampMaker().GetNow());
aLocal.mId.Construct(localId);
aLocal.mType.Construct(RTCStatsType::Inbound_rtp);
ssrc.apply(
@ -483,7 +484,8 @@ nsTArray<RefPtr<RTCStatsPromise>> RTCRtpReceiver::GetStatsInternal() {
if (mJsepTransceiver->mTransport.mComponents) {
promises.AppendElement(mTransportHandler->GetIceStats(
mJsepTransceiver->mTransport.mTransportId, mPipeline->GetNow()));
mJsepTransceiver->mTransport.mTransportId,
mPipeline->GetTimestampMaker().GetNow()));
}
return promises;

View File

@ -5,30 +5,34 @@
* file, You can obtain one at https://mozilla.org/MPL/2.0/. */
#include "RTCStatsReport.h"
#include "libwebrtcglue/SystemTime.h"
#include "mozilla/dom/Performance.h"
#include "mozilla/dom/PerformanceService.h"
#include "nsRFPService.h"
namespace mozilla::dom {
RTCStatsTimestampMaker::RTCStatsTimestampMaker(nsPIDOMWindowInner* aWindow)
: mRandomTimelineSeed(aWindow && aWindow->GetPerformance() ?
aWindow->GetPerformance()->GetRandomTimelineSeed() : 0),
mStartMonotonic(aWindow && aWindow->GetPerformance() ?
aWindow->GetPerformance()->CreationTimeStamp() : mozilla::TimeStamp::Now()),
// Ugh. Performance::TimeOrigin is not constant, which means we need to
// emulate this weird behavior so our time stamps are consistent with JS
// timeOrigin. This is based on the code here:
// https://searchfox.org/mozilla-central/rev/
// 053826b10f838f77c27507e5efecc96e34718541/dom/performance/Performance.cpp#111-117
RTCStatsTimestampMaker::RTCStatsTimestampMaker()
: mRandomTimelineSeed(0),
mStartRealtime(WebrtcSystemTimeBase()),
mCrossOriginIsolated(false),
mStartWallClockRaw(
aWindow
? PerformanceService::GetOrCreate()->TimeOrigin(mStartMonotonic)
: (double)PR_Now() / PR_USEC_PER_MSEC),
mCrossOriginIsolated(aWindow ? aWindow->AsGlobal()->CrossOriginIsolated()
: false) {}
PerformanceService::GetOrCreate()->TimeOrigin(mStartRealtime)) {}
DOMHighResTimeStamp RTCStatsTimestampMaker::GetNow() const {
RTCStatsTimestampMaker::RTCStatsTimestampMaker(nsPIDOMWindowInner* aWindow)
: mRandomTimelineSeed(
aWindow && aWindow->GetPerformance()
? aWindow->GetPerformance()->GetRandomTimelineSeed()
: 0),
mStartRealtime(aWindow && aWindow->GetPerformance()
? aWindow->GetPerformance()->CreationTimeStamp()
: WebrtcSystemTimeBase()),
mCrossOriginIsolated(aWindow ? aWindow->AsGlobal()->CrossOriginIsolated()
: false),
mStartWallClockRaw(
PerformanceService::GetOrCreate()->TimeOrigin(mStartRealtime)) {}
DOMHighResTimeStamp RTCStatsTimestampMaker::ReduceRealtimePrecision(
webrtc::Timestamp aRealtime) const {
// webrtc-pc says to use performance.timeOrigin + performance.now(), but
// keeping a Performance object around is difficult because it is
// main-thread-only. So, we perform the same calculation here. Note that this
@ -36,18 +40,50 @@ DOMHighResTimeStamp RTCStatsTimestampMaker::GetNow() const {
// to the wall clock, or monotonic clock drift over long periods of time.
// We are very careful to do exactly what Performance does, to avoid timestamp
// discrepancies.
DOMHighResTimeStamp msSinceStart =
(TimeStamp::Now() - mStartMonotonic).ToMilliseconds();
DOMHighResTimeStamp realtime = aRealtime.ms<double>();
// mRandomTimelineSeed is not set in the unit-tests.
if (mRandomTimelineSeed) {
msSinceStart = nsRFPService::ReduceTimePrecisionAsMSecs(
msSinceStart, mRandomTimelineSeed, /* aIsSystemPrincipal */ false,
mCrossOriginIsolated);
realtime = nsRFPService::ReduceTimePrecisionAsMSecs(
realtime, mRandomTimelineSeed,
/* aIsSystemPrincipal */ false, mCrossOriginIsolated);
}
return msSinceStart + nsRFPService::ReduceTimePrecisionAsMSecs(
mStartWallClockRaw, 0,
/* aIsSystemPrincipal */ false,
mCrossOriginIsolated);
// Ugh. Performance::TimeOrigin is not constant, which means we need to
// emulate this weird behavior so our time stamps are consistent with JS
// timeOrigin. This is based on the code here:
// https://searchfox.org/mozilla-central/rev/
// 053826b10f838f77c27507e5efecc96e34718541/dom/performance/Performance.cpp#111-117
DOMHighResTimeStamp start = nsRFPService::ReduceTimePrecisionAsMSecs(
mStartWallClockRaw, 0, /* aIsSystemPrincipal = */ false,
mCrossOriginIsolated);
return start + realtime;
}
webrtc::Timestamp RTCStatsTimestampMaker::ConvertRealtimeTo1Jan1970(
webrtc::Timestamp aRealtime) const {
return aRealtime + webrtc::TimeDelta::Millis(mStartWallClockRaw);
}
DOMHighResTimeStamp RTCStatsTimestampMaker::ConvertNtpToDomTime(
webrtc::Timestamp aNtpTime) const {
const auto realtime = aNtpTime -
webrtc::TimeDelta::Seconds(webrtc::kNtpJan1970) -
webrtc::TimeDelta::Millis(mStartWallClockRaw);
// Ntp times exposed by libwebrtc to stats are always **rounded** to
// milliseconds. That means they can jump up to half a millisecond into the
// future. We compensate for that here so that things seem consistent to js.
return ReduceRealtimePrecision(realtime - webrtc::TimeDelta::Micros(500));
}
DOMHighResTimeStamp RTCStatsTimestampMaker::GetNow() const {
return ReduceRealtimePrecision(GetNowRealtime());
}
webrtc::Timestamp RTCStatsTimestampMaker::GetNowRealtime() const {
return webrtc::Timestamp::Micros(
(TimeStamp::Now() - mStartRealtime).ToMicroseconds());
}
NS_IMPL_CYCLE_COLLECTION_WRAPPERCACHE(RTCStatsReport, mParent)

View File

@ -7,6 +7,7 @@
#ifndef RTCStatsReport_h_
#define RTCStatsReport_h_
#include "api/units/timestamp.h" // webrtc::Timestamp
#include "nsWrapperCache.h"
#include "nsCOMPtr.h"
@ -15,29 +16,54 @@
#include "nsIGlobalObject.h"
#include "js/RootingAPI.h" // JS::Rooted
#include "js/Value.h"
#include "libwebrtcglue/SystemTime.h"
#include "mozilla/ErrorResult.h"
#include "mozilla/UniquePtr.h"
#include "prtime.h" // PR_Now
#include "mozilla/MozPromise.h"
#include "mozilla/TimeStamp.h"
#include "mozilla/dom/PerformanceService.h"
#include "mozilla/dom/RTCStatsReportBinding.h" // RTCStatsCollection
#include "mozilla/dom/ToJSValue.h"
namespace mozilla {
extern TimeStamp WebrtcSystemTimeBase();
namespace dom {
/**
* Class that facilitates creating timestamps for webrtc stats by mimicking
* dom::Performance, as well as getting and converting timestamps for libwebrtc
* and our integration with it.
*
* It converts and uses time from these clocks:
* - Realtime : Monotonic, unspecified (but constant) epoch.
* - 1Jan1970 : Monotonic, unix epoch (00:00:00 UTC on 1 January 1970).
* - Ntp : Monotonic, ntp epoch (00:00:00 UTC on 1 January 1900).
* - Dom : Monotonic, milliseconds since unix epoch, as the timestamps
* defined by webrtc-pc. Corresponds to Performance.timeOrigin +
* Performance.now().
* - WallClock: Non-monotonic, unix epoch.
*/
class RTCStatsTimestampMaker {
public:
RTCStatsTimestampMaker() = default;
RTCStatsTimestampMaker();
explicit RTCStatsTimestampMaker(nsPIDOMWindowInner* aWindow);
DOMHighResTimeStamp GetNow() const;
private:
const uint64_t mRandomTimelineSeed = 0;
const TimeStamp mStartMonotonic = TimeStamp::Now();
const DOMHighResTimeStamp mStartWallClockRaw =
(double)PR_Now() / PR_USEC_PER_MSEC;
const bool mCrossOriginIsolated = false;
webrtc::Timestamp GetNowRealtime() const;
webrtc::Timestamp ConvertRealtimeTo1Jan1970(
webrtc::Timestamp aRealtime) const;
DOMHighResTimeStamp ConvertNtpToDomTime(webrtc::Timestamp aNtpTime) const;
DOMHighResTimeStamp ReduceRealtimePrecision(
webrtc::Timestamp aRealtime) const;
const uint64_t mRandomTimelineSeed;
const TimeStamp mStartRealtime;
const bool mCrossOriginIsolated;
const DOMHighResTimeStamp mStartWallClockRaw;
};
// TODO(bug 1588303): If we ever get move semantics for webidl dictionaries, we

View File

@ -511,8 +511,9 @@ Maybe<uint16_t> WebrtcAudioConduit::RtpSendBaseSeqFor(uint32_t aSsrc) const {
return Some(it->second);
}
DOMHighResTimeStamp WebrtcAudioConduit::GetNow() const {
return mCall->GetNow();
const dom::RTCStatsTimestampMaker& WebrtcAudioConduit::GetTimestampMaker()
const {
return mCall->GetTimestampMaker();
}
void WebrtcAudioConduit::StopTransmitting() {

View File

@ -85,7 +85,7 @@ class WebrtcAudioConduit : public AudioSessionConduit,
Maybe<uint16_t> RtpSendBaseSeqFor(uint32_t aSsrc) const override;
DOMHighResTimeStamp GetNow() const override;
const dom::RTCStatsTimestampMaker& GetTimestampMaker() const override;
void StopTransmitting();
void StartTransmitting();

View File

@ -63,7 +63,7 @@ void MediaSessionConduit::UpdateRtpSources(
// Fix up timestamps to be consistent with JS time. We assume that
// source.timestamp_ms() was not terribly long ago, and so clock drift
// between the libwebrtc clock and our JS clock is not that significant.
auto jsNow = GetNow();
auto jsNow = GetTimestampMaker().GetNow();
double libwebrtcNow = webrtc::Clock::GetRealTimeClock()->TimeInMilliseconds();
for (const auto& source : aSources) {
@ -141,7 +141,7 @@ void MediaSessionConduit::InsertAudioLevelForContributingSource(
int64_t libwebrtcNow =
webrtc::Clock::GetRealTimeClock()->TimeInMilliseconds();
double jsNow = GetNow();
double jsNow = GetTimestampMaker().GetNow();
double ago = jsNow - aTimestamp;
uint64_t convertedTimestamp = libwebrtcNow - ago;

View File

@ -136,7 +136,8 @@ class MediaSessionConduit {
// Sts thread only.
virtual Maybe<uint16_t> RtpSendBaseSeqFor(uint32_t aSsrc) const = 0;
virtual DOMHighResTimeStamp GetNow() const = 0;
// Any thread.
virtual const dom::RTCStatsTimestampMaker& GetTimestampMaker() const = 0;
virtual Ssrcs GetLocalSSRCs() const = 0;

View File

@ -6,6 +6,8 @@
#include "SystemTime.h"
#include "TimeUnits.h"
namespace mozilla {
TimeStamp WebrtcSystemTimeBase() {
static TimeStamp base = TimeStamp::Now();
@ -17,6 +19,17 @@ webrtc::Timestamp WebrtcSystemTime() {
const TimeStamp now = TimeStamp::Now();
return webrtc::Timestamp::Micros((now - base).ToMicroseconds());
}
webrtc::NtpTime CreateNtp(webrtc::Timestamp aTime) {
const int64_t timeNtpUs = aTime.us();
const uint32_t seconds = static_cast<uint32_t>(timeNtpUs / USECS_PER_S);
constexpr int64_t fractionsPerSec = 1LL << 32;
const int64_t fractionsUs = timeNtpUs % USECS_PER_S;
const uint32_t fractions = (fractionsUs * fractionsPerSec) / USECS_PER_S;
return webrtc::NtpTime(seconds, fractions);
}
} // namespace mozilla
namespace rtc {

View File

@ -1498,8 +1498,9 @@ Maybe<uint16_t> WebrtcVideoConduit::RtpSendBaseSeqFor(uint32_t aSsrc) const {
return Some(it->second);
}
DOMHighResTimeStamp WebrtcVideoConduit::GetNow() const {
return mCall->GetNow();
const dom::RTCStatsTimestampMaker& WebrtcVideoConduit::GetTimestampMaker()
const {
return mCall->GetTimestampMaker();
}
void WebrtcVideoConduit::StopTransmitting() {
@ -1694,7 +1695,7 @@ void WebrtcVideoConduit::OnFrame(const webrtc::VideoFrame& video_frame) {
}
// Record frame history
const auto historyNow = mCall->GetNow();
const auto historyNow = mCall->GetTimestampMaker().GetNow();
if (needsNewHistoryElement) {
dom::RTCVideoFrameHistoryEntryInternal frameHistoryElement;
frameHistoryElement.mConsecutiveFrames = 0;

View File

@ -83,7 +83,7 @@ class WebrtcVideoConduit
Maybe<uint16_t> RtpSendBaseSeqFor(uint32_t aSsrc) const override;
DOMHighResTimeStamp GetNow() const override;
const dom::RTCStatsTimestampMaker& GetTimestampMaker() const override;
void StopTransmitting();
void StartTransmitting();

View File

@ -67,10 +67,6 @@ void WebrtcCallWrapper::UnregisterConduit(MediaSessionConduit* conduit) {
mConduits.erase(conduit);
}
DOMHighResTimeStamp WebrtcCallWrapper::GetNow() const {
return mTimestampMaker.GetNow();
}
void WebrtcCallWrapper::Destroy() {
MOZ_ASSERT(mCallThread->IsOnCurrentThread());
mCall = nullptr;

View File

@ -54,8 +54,6 @@ class WebrtcCallWrapper {
// Idempotent.
void UnregisterConduit(MediaSessionConduit* conduit);
DOMHighResTimeStamp GetNow() const;
// Allow destroying the Call instance on the Call worker thread.
//
// Note that shutdown is blocked until the Call instance is destroyed.

View File

@ -363,7 +363,8 @@ void MediaPipeline::GetContributingSourceStats(
FallibleTArray<dom::RTCRTPContributingSourceStats>& aArr) const {
ASSERT_ON_THREAD(mStsThread);
// Get the expiry from now
DOMHighResTimeStamp expiry = RtpCSRCStats::GetExpiryFromTime(GetNow());
DOMHighResTimeStamp expiry =
RtpCSRCStats::GetExpiryFromTime(GetTimestampMaker().GetNow());
for (auto info : mCsrcStats) {
if (!info.second.Expired(expiry)) {
RTCRTPContributingSourceStats stats;
@ -556,7 +557,7 @@ void MediaPipeline::RtpPacketReceived(const MediaPacket& packet) {
// Remove expired RtpCSRCStats
if (!mCsrcStats.empty()) {
if (!hasTime) {
now = GetNow();
now = GetTimestampMaker().GetNow();
hasTime = true;
}
auto expiry = RtpCSRCStats::GetExpiryFromTime(now);
@ -573,7 +574,7 @@ void MediaPipeline::RtpPacketReceived(const MediaPacket& packet) {
if (header.numCSRCs) {
for (auto i = 0; i < header.numCSRCs; i++) {
if (!hasTime) {
now = GetNow();
now = GetTimestampMaker().GetNow();
hasTime = true;
}
auto csrcInfo = mCsrcStats.find(header.arrOfCSRCs[i]);
@ -1676,7 +1677,9 @@ void MediaPipelineReceiveVideo::UpdateListener() {
}
}
DOMHighResTimeStamp MediaPipeline::GetNow() const { return mConduit->GetNow(); }
const dom::RTCStatsTimestampMaker& MediaPipeline::GetTimestampMaker() const {
return mConduit->GetTimestampMaker();
}
DOMHighResTimeStamp MediaPipeline::RtpCSRCStats::GetExpiryFromTime(
const DOMHighResTimeStamp aTime) {

View File

@ -152,8 +152,7 @@ class MediaPipeline : public sigslot::has_slots<> {
int64_t RtpBytesReceived() const { return mRtpBytesReceived; }
int32_t RtcpPacketsReceived() const { return mRtcpPacketsReceived; }
// Gets the current time as a DOMHighResTimeStamp
DOMHighResTimeStamp GetNow() const;
const dom::RTCStatsTimestampMaker& GetTimestampMaker() const;
// Thread counting
NS_INLINE_DECL_THREADSAFE_REFCOUNTING(MediaPipeline)

View File

@ -24,6 +24,8 @@ LOCAL_INCLUDES += [
"/dom/media/webrtc/transport",
"/media/webrtc",
"/netwerk/sctp/src",
"/third_party/libwebrtc",
"/third_party/libwebrtc/third_party/abseil-cpp",
]
DEFINES["SCTP_DEBUG"] = 1