From 6bf0a5cb5034a7e684dcc3500e841785237ce2dd Mon Sep 17 00:00:00 2001 From: Daniel Baumann Date: Sun, 7 Apr 2024 19:32:43 +0200 Subject: Adding upstream version 1:115.7.0. Signed-off-by: Daniel Baumann --- .../webrtc/jsapi/WebrtcGlobalStatsHistory.cpp | 282 +++++++++++++++++++++ 1 file changed, 282 insertions(+) create mode 100644 dom/media/webrtc/jsapi/WebrtcGlobalStatsHistory.cpp (limited to 'dom/media/webrtc/jsapi/WebrtcGlobalStatsHistory.cpp') diff --git a/dom/media/webrtc/jsapi/WebrtcGlobalStatsHistory.cpp b/dom/media/webrtc/jsapi/WebrtcGlobalStatsHistory.cpp new file mode 100644 index 0000000000..1d576b5dca --- /dev/null +++ b/dom/media/webrtc/jsapi/WebrtcGlobalStatsHistory.cpp @@ -0,0 +1,282 @@ +/* This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this file, + * You can obtain one at http://mozilla.org/MPL/2.0/. */ + +#include "WebrtcGlobalStatsHistory.h" +#include + +#include "domstubs.h" +#include "mozilla/LinkedList.h" +#include "mozilla/dom/BindingUtils.h" +#include "mozilla/dom/RTCStatsReportBinding.h" // for RTCStatsReportInternal +#include "mozilla/ClearOnShutdown.h" +#include "mozilla/StaticPrefs_media.h" +#include "mozilla/fallible.h" +#include "mozilla/mozalloc_oom.h" +#include "nsDOMNavigationTiming.h" + +namespace mozilla::dom { + +constexpr auto SEC_TO_MS(const DOMHighResTimeStamp sec) -> DOMHighResTimeStamp { + return sec * 1000.0; +} + +constexpr auto MIN_TO_MS(const DOMHighResTimeStamp min) -> DOMHighResTimeStamp { + return SEC_TO_MS(min * 60.0); +} + +// Prefs +auto WebrtcGlobalStatsHistory::Pref::Enabled() -> bool { + return mozilla::StaticPrefs::media_aboutwebrtc_hist_enabled(); +} + +auto WebrtcGlobalStatsHistory::Pref::PollIntervalMs() -> uint32_t { + return mozilla::StaticPrefs::media_aboutwebrtc_hist_poll_interval_ms(); +} + +auto WebrtcGlobalStatsHistory::Pref::StorageWindowS() -> uint32_t { + return mozilla::StaticPrefs::media_aboutwebrtc_hist_storage_window_s(); +} + +auto WebrtcGlobalStatsHistory::Pref::PruneAfterM() -> uint32_t { + return mozilla::StaticPrefs::media_aboutwebrtc_hist_prune_after_m(); +} + +auto WebrtcGlobalStatsHistory::Pref::ClosedStatsToRetain() -> uint32_t { + return mozilla::StaticPrefs::media_aboutwebrtc_hist_closed_stats_to_retain(); +} + +auto WebrtcGlobalStatsHistory::Get() -> WebrtcGlobalStatsHistory::StatsMap& { + static StaticAutoPtr sHist; + if (!sHist) { + sHist = new StatsMap(); + ClearOnShutdown(&sHist); + } + return *sHist; +} + +auto WebrtcGlobalStatsHistory::Entry::ReportElement::Timestamp() const + -> DOMHighResTimeStamp { + return report->mTimestamp; +} + +auto WebrtcGlobalStatsHistory::Entry::SdpElement::Timestamp() const + -> DOMHighResTimeStamp { + return sdp.mTimestamp; +} + +auto WebrtcGlobalStatsHistory::Entry::MakeReportElement( + UniquePtr aReport) + -> WebrtcGlobalStatsHistory::Entry::ReportElement* { + auto* elem = new ReportElement(); + elem->report = std::move(aReport); + // We don't want to store a copy of the SDP history with each stats entry. + // SDP History is stored seperately, see MakeSdpElements. + elem->report->mSdpHistory.Clear(); + return elem; +} + +auto WebrtcGlobalStatsHistory::Entry::MakeSdpElementsSince( + Sequence&& aSdpHistory, + const Maybe& aSdpAfter) + -> AutoCleanLinkedList { + AutoCleanLinkedList result; + for (auto& sdpHist : aSdpHistory) { + if (!aSdpAfter || aSdpAfter.value() < sdpHist.mTimestamp) { + auto* element = new SdpElement(); + element->sdp = sdpHist; + result.insertBack(element); + } + } + return result; +} + +template +auto FindFirstEntryAfter(const T* first, + const Maybe& aAfter) -> const T* { + const auto* current = first; + while (aAfter && current && current->Timestamp() <= aAfter.value()) { + current = current->getNext(); + } + return current; +} + +template +auto CountElementsToEndInclusive(const LinkedListElement* elem) -> size_t { + size_t count = 0; + const auto* cursor = elem; + while (cursor && cursor->isInList()) { + count++; + cursor = cursor->getNext(); + } + return count; +} + +auto WebrtcGlobalStatsHistory::Entry::Since( + const Maybe& aAfter) const + -> nsTArray { + nsTArray results; + const auto* cursor = FindFirstEntryAfter(mReports.getFirst(), aAfter); + const auto count = CountElementsToEndInclusive(cursor); + if (!results.SetCapacity(count, fallible)) { + mozalloc_handle_oom(0); + } + while (cursor) { + results.AppendElement(RTCStatsReportInternal(*cursor->report)); + cursor = cursor->getNext(); + } + return results; +} + +auto WebrtcGlobalStatsHistory::Entry::SdpSince( + const Maybe& aAfter) const -> RTCSdpHistoryInternal { + RTCSdpHistoryInternal results; + results.mPcid = mPcid; + // If no timestamp was passed copy the entire history + const auto* cursor = FindFirstEntryAfter(mSdp.getFirst(), aAfter); + const auto count = CountElementsToEndInclusive(cursor); + if (!results.mSdpHistory.SetCapacity(count, fallible)) { + mozalloc_handle_oom(0); + } + while (cursor) { + if (!results.mSdpHistory.AppendElement( + RTCSdpHistoryEntryInternal(cursor->sdp), fallible)) { + mozalloc_handle_oom(0); + } + cursor = cursor->getNext(); + } + return results; +} + +auto WebrtcGlobalStatsHistory::Entry::Prune(const DOMHighResTimeStamp aBefore) + -> void { + // Clear everything in the case that we don't keep stats + if (mIsLongTermStatsDisabled) { + mReports.clear(); + } + // Clear everything before the cutoff + for (auto* element = mReports.getFirst(); + element && element->report->mTimestamp < aBefore; + element = mReports.getFirst()) { + delete mReports.popFirst(); + } + // I don't think we should prune SDPs but if we did it would look like this: + // Note: we always keep the most recent SDP +} + +auto WebrtcGlobalStatsHistory::InitHistory(const nsAString& aPcId, + const bool aIsLongTermStatsDisabled) + -> void { + MOZ_ASSERT(XRE_IsParentProcess()); + if (WebrtcGlobalStatsHistory::Get().MaybeGet(aPcId)) { + return; + } + WebrtcGlobalStatsHistory::Get().GetOrInsertNew(aPcId, nsString(aPcId), + aIsLongTermStatsDisabled); +}; + +auto WebrtcGlobalStatsHistory::Record(UniquePtr aReport) + -> void { + MOZ_ASSERT(XRE_IsParentProcess()); + // Use the report timestamp as "now" for determining time depth + // based pruning. + const auto now = aReport->mTimestamp; + const auto earliest = now - SEC_TO_MS(Pref::StorageWindowS()); + + // Store closed state before moving the report + const auto closed = aReport->mClosed; + const auto pcId = aReport->mPcid; + + auto history = WebrtcGlobalStatsHistory::GetHistory(aReport->mPcid); + if (history && Pref::Enabled()) { + auto entry = history.value(); + // Remove expired entries + entry->Prune(earliest); + // Find new SDP entries + auto sdpAfter = Maybe(Nothing()); + if (auto* lastSdp = entry->mSdp.getLast(); lastSdp) { + sdpAfter = Some(lastSdp->Timestamp()); + } + entry->mSdp.extendBack( + Entry::MakeSdpElementsSince(std::move(aReport->mSdpHistory), sdpAfter)); + // Reports must be in ascending order by mTimestamp + const auto* latest = entry->mReports.getLast(); + // Maintain sorted order + if (!latest || latest->report->mTimestamp < aReport->mTimestamp) { + entry->mReports.insertBack(Entry::MakeReportElement(std::move(aReport))); + } + } + // Close the history if needed + if (closed) { + CloseHistory(pcId); + } +} + +auto WebrtcGlobalStatsHistory::CloseHistory(const nsAString& aPcId) -> void { + MOZ_ASSERT(XRE_IsParentProcess()); + auto maybeHist = WebrtcGlobalStatsHistory::Get().MaybeGet(aPcId); + if (!maybeHist) { + return; + } + { + auto&& hist = maybeHist.value(); + hist->mIsClosed = true; + + if (hist->mIsLongTermStatsDisabled) { + WebrtcGlobalStatsHistory::Get().Remove(aPcId); + return; + } + } + size_t remainingClosedStatsToRetain = + WebrtcGlobalStatsHistory::Pref::ClosedStatsToRetain(); + WebrtcGlobalStatsHistory::Get().RemoveIf([&](auto& iter) { + auto& entry = iter.Data(); + if (!entry->mIsClosed) { + return false; + } + if (entry->mIsLongTermStatsDisabled) { + return true; + } + if (remainingClosedStatsToRetain > 0) { + remainingClosedStatsToRetain -= 1; + return false; + } + return true; + }); +} + +auto WebrtcGlobalStatsHistory::Clear() -> void { + MOZ_ASSERT(XRE_IsParentProcess()); + WebrtcGlobalStatsHistory::Get().RemoveIf([](auto& aIter) { + // First clear all the closed histories. + if (aIter.Data()->mIsClosed) { + return true; + } + // For all remaining histories clear their stored reports + aIter.Data()->mReports.clear(); + // As an optimization we don't clear the SDP, because that would + // be reconstitued in the very next stats gathering polling period. + // Those are potentially large allocations which we can skip. + return false; + }); +} + +auto WebrtcGlobalStatsHistory::PcIds() -> dom::Sequence { + MOZ_ASSERT(XRE_IsParentProcess()); + dom::Sequence pcIds; + for (const auto& pcId : WebrtcGlobalStatsHistory::Get().Keys()) { + if (!pcIds.AppendElement(pcId, fallible)) { + mozalloc_handle_oom(0); + } + } + return pcIds; +} + +auto WebrtcGlobalStatsHistory::GetHistory(const nsAString& aPcId) + -> Maybe > { + MOZ_ASSERT(XRE_IsParentProcess()); + const auto pcid = NS_ConvertUTF16toUTF8(aPcId); + + return WebrtcGlobalStatsHistory::Get().MaybeGet(aPcId); +} +} // namespace mozilla::dom -- cgit v1.2.3