RTCStatsReport.cpp (8749B)
1 /* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ 2 /* vim: set ts=8 sts=2 et sw=2 tw=80: */ 3 /* This Source Code Form is subject to the terms of the Mozilla Public 4 * License, v. 2.0. If a copy of the MPL was not distributed with this 5 * file, You can obtain one at https://mozilla.org/MPL/2.0/. */ 6 7 #include "RTCStatsReport.h" 8 9 #include "WebrtcGlobal.h" 10 #include "libwebrtcglue/SystemTime.h" 11 #include "mozilla/dom/Performance.h" 12 #include "nsRFPService.h" 13 14 namespace mozilla::dom { 15 16 RTCStatsTimestampState::RTCStatsTimestampState(TimeStamp aStartDomRealtime, 17 webrtc::Timestamp aStartRealtime) 18 : mRandomTimelineSeed(0), 19 mStartDomRealtime(aStartDomRealtime), 20 mStartRealtime(webrtc::Timestamp::Micros(0)), 21 mRTPCallerType(RTPCallerType::Normal), 22 mStartWallClockRaw(0) {} 23 24 RTCStatsTimestampState::RTCStatsTimestampState() 25 : mRandomTimelineSeed(0), 26 mStartDomRealtime(WebrtcSystemTimeBase()), 27 mStartRealtime( 28 WebrtcSystemTime() - 29 webrtc::TimeDelta::Micros( 30 (TimeStamp::Now() - mStartDomRealtime).ToMicroseconds())), 31 mRTPCallerType(RTPCallerType::Normal), 32 mStartWallClockRaw( 33 PerformanceService::GetOrCreate()->TimeOrigin(mStartDomRealtime)) {} 34 35 RTCStatsTimestampState::RTCStatsTimestampState(Performance& aPerformance) 36 : mRandomTimelineSeed(aPerformance.GetRandomTimelineSeed()), 37 mStartDomRealtime(aPerformance.CreationTimeStamp()), 38 mStartRealtime( 39 WebrtcSystemTime() - 40 webrtc::TimeDelta::Micros( 41 (TimeStamp::Now() - mStartDomRealtime).ToMicroseconds())), 42 mRTPCallerType(aPerformance.GetRTPCallerType()), 43 mStartWallClockRaw( 44 PerformanceService::GetOrCreate()->TimeOrigin(mStartDomRealtime)) {} 45 46 TimeStamp RTCStatsTimestamp::ToMozTime() const { return mMozTime; } 47 48 webrtc::Timestamp RTCStatsTimestamp::ToRealtime() const { 49 return ToDomRealtime() + 50 webrtc::TimeDelta::Micros(mState.mStartRealtime.us()); 51 } 52 53 webrtc::Timestamp RTCStatsTimestamp::To1Jan1970() const { 54 return ToDomRealtime() + webrtc::TimeDelta::Millis(mState.mStartWallClockRaw); 55 } 56 57 webrtc::Timestamp RTCStatsTimestamp::ToNtp() const { 58 return To1Jan1970() + webrtc::TimeDelta::Seconds(webrtc::kNtpJan1970); 59 } 60 61 webrtc::Timestamp RTCStatsTimestamp::ToDomRealtime() const { 62 return webrtc::Timestamp::Micros( 63 (mMozTime - mState.mStartDomRealtime).ToMicroseconds()); 64 } 65 66 DOMHighResTimeStamp RTCStatsTimestamp::ToDom() const { 67 // webrtc-pc says to use performance.timeOrigin + performance.now(), but 68 // keeping a Performance object around is difficult because it is 69 // main-thread-only. So, we perform the same calculation here. Note that this 70 // can be very different from the current wall-clock time because of changes 71 // to the wall clock, or monotonic clock drift over long periods of time. 72 // We are very careful to do exactly what Performance does, to avoid timestamp 73 // discrepancies. 74 75 DOMHighResTimeStamp realtime = ToDomRealtime().ms<double>(); 76 // mRandomTimelineSeed is not set in the unit-tests. 77 if (mState.mRandomTimelineSeed) { 78 realtime = nsRFPService::ReduceTimePrecisionAsMSecs( 79 realtime, mState.mRandomTimelineSeed, mState.mRTPCallerType); 80 } 81 82 // Ugh. Performance::TimeOrigin is not constant, which means we need to 83 // emulate this weird behavior so our time stamps are consistent with JS 84 // timeOrigin. This is based on the code here: 85 // https://searchfox.org/mozilla-central/rev/ 86 // 053826b10f838f77c27507e5efecc96e34718541/dom/performance/Performance.cpp#111-117 87 DOMHighResTimeStamp start = nsRFPService::ReduceTimePrecisionAsMSecs( 88 mState.mStartWallClockRaw, 0, mState.mRTPCallerType); 89 90 return start + realtime; 91 } 92 93 /* static */ RTCStatsTimestamp RTCStatsTimestamp::FromMozTime( 94 const RTCStatsTimestampMaker& aMaker, TimeStamp aMozTime) { 95 return RTCStatsTimestamp(aMaker.mState, aMozTime); 96 } 97 98 /* static */ RTCStatsTimestamp RTCStatsTimestamp::FromRealtime( 99 const RTCStatsTimestampMaker& aMaker, webrtc::Timestamp aRealtime) { 100 return FromDomRealtime( 101 aMaker, 102 aRealtime - webrtc::TimeDelta::Micros(aMaker.mState.mStartRealtime.us())); 103 } 104 105 /* static */ RTCStatsTimestamp RTCStatsTimestamp::From1Jan1970( 106 const RTCStatsTimestampMaker& aMaker, webrtc::Timestamp a1Jan1970) { 107 const auto& state = aMaker.mState; 108 return FromDomRealtime( 109 aMaker, a1Jan1970 - webrtc::TimeDelta::Millis(state.mStartWallClockRaw)); 110 } 111 112 /* static */ RTCStatsTimestamp RTCStatsTimestamp::FromNtp( 113 const RTCStatsTimestampMaker& aMaker, webrtc::Timestamp aNtpTime) { 114 const auto& state = aMaker.mState; 115 const auto domRealtime = aNtpTime - 116 webrtc::TimeDelta::Seconds(webrtc::kNtpJan1970) - 117 webrtc::TimeDelta::Millis(state.mStartWallClockRaw); 118 // Ntp times exposed by libwebrtc to stats are always **rounded** to 119 // milliseconds. That means they can jump up to half a millisecond into the 120 // future. We compensate for that here so that things seem consistent to js. 121 return FromDomRealtime(aMaker, domRealtime - webrtc::TimeDelta::Micros(500)); 122 } 123 124 /* static */ RTCStatsTimestamp RTCStatsTimestamp::FromDomRealtime( 125 const RTCStatsTimestampMaker& aMaker, webrtc::Timestamp aDomRealtime) { 126 return RTCStatsTimestamp(aMaker.mState, aMaker.mState.mStartDomRealtime + 127 TimeDuration::FromMicroseconds( 128 aDomRealtime.us<double>())); 129 } 130 131 RTCStatsTimestamp::RTCStatsTimestamp(RTCStatsTimestampState aState, 132 TimeStamp aMozTime) 133 : mState(aState), mMozTime(aMozTime) {} 134 135 RTCStatsTimestampMaker::RTCStatsTimestampMaker(RTCStatsTimestampState aState) 136 : mState(aState) {} 137 138 /* static */ 139 RTCStatsTimestampMaker RTCStatsTimestampMaker::Create( 140 nsPIDOMWindowInner* aWindow /* = nullptr */) { 141 if (!aWindow) { 142 return RTCStatsTimestampMaker(RTCStatsTimestampState()); 143 } 144 if (Performance* p = aWindow->GetPerformance()) { 145 return RTCStatsTimestampMaker(RTCStatsTimestampState(*p)); 146 } 147 return RTCStatsTimestampMaker(RTCStatsTimestampState()); 148 } 149 150 RTCStatsTimestamp RTCStatsTimestampMaker::GetNow() const { 151 return RTCStatsTimestamp::FromMozTime(*this, TimeStamp::Now()); 152 } 153 154 NS_IMPL_CYCLE_COLLECTION_WRAPPERCACHE(RTCStatsReport, mParent) 155 156 RTCStatsReport::RTCStatsReport(nsPIDOMWindowInner* aParent) 157 : mParent(aParent) {} 158 159 /*static*/ 160 already_AddRefed<RTCStatsReport> RTCStatsReport::Constructor( 161 const GlobalObject& aGlobal) { 162 nsCOMPtr<nsPIDOMWindowInner> window( 163 do_QueryInterface(aGlobal.GetAsSupports())); 164 RefPtr<RTCStatsReport> report(new RTCStatsReport(window)); 165 return report.forget(); 166 } 167 168 JSObject* RTCStatsReport::WrapObject(JSContext* aCx, 169 JS::Handle<JSObject*> aGivenProto) { 170 return RTCStatsReport_Binding::Wrap(aCx, this, aGivenProto); 171 } 172 173 void RTCStatsReport::Incorporate(RTCStatsCollection& aStats) { 174 ForAllPublicRTCStatsCollectionMembers( 175 aStats, [&](auto... aMember) { (SetRTCStats(aMember), ...); }); 176 } 177 178 void RTCStatsReport::Set(const nsAString& aKey, JS::Handle<JSObject*> aValue, 179 ErrorResult& aRv) { 180 RTCStatsReport_Binding::MaplikeHelpers::Set(this, aKey, aValue, aRv); 181 } 182 183 namespace { 184 template <size_t I, typename... Ts> 185 bool MoveInto(std::tuple<Ts...>& aFrom, std::tuple<Ts*...>& aInto) { 186 return std::get<I>(aInto)->AppendElements(std::move(std::get<I>(aFrom)), 187 fallible); 188 } 189 190 template <size_t... Is, typename... Ts> 191 bool MoveInto(std::tuple<Ts...>&& aFrom, std::tuple<Ts*...>& aInto, 192 std::index_sequence<Is...>) { 193 return (... && MoveInto<Is>(aFrom, aInto)); 194 } 195 196 template <typename... Ts> 197 bool MoveInto(std::tuple<Ts...>&& aFrom, std::tuple<Ts*...>& aInto) { 198 return MoveInto(std::move(aFrom), aInto, std::index_sequence_for<Ts...>()); 199 } 200 } // namespace 201 202 void MergeStats(UniquePtr<RTCStatsCollection> aFromStats, 203 RTCStatsCollection* aIntoStats) { 204 auto fromTuple = ForAllRTCStatsCollectionMembers( 205 *aFromStats, 206 [&](auto&... aMember) { return std::make_tuple(std::move(aMember)...); }); 207 auto intoTuple = ForAllRTCStatsCollectionMembers( 208 *aIntoStats, 209 [&](auto&... aMember) { return std::make_tuple(&aMember...); }); 210 if (!MoveInto(std::move(fromTuple), intoTuple)) { 211 mozalloc_handle_oom(0); 212 } 213 } 214 215 void FlattenStats(nsTArray<UniquePtr<RTCStatsCollection>> aFromStats, 216 RTCStatsCollection* aIntoStats) { 217 for (auto& stats : aFromStats) { 218 MergeStats(std::move(stats), aIntoStats); 219 } 220 } 221 222 } // namespace mozilla::dom