diff options
Diffstat (limited to 'dom/media/webrtc/jsapi/WebrtcGlobalInformation.cpp')
-rw-r--r-- | dom/media/webrtc/jsapi/WebrtcGlobalInformation.cpp | 714 |
1 files changed, 714 insertions, 0 deletions
diff --git a/dom/media/webrtc/jsapi/WebrtcGlobalInformation.cpp b/dom/media/webrtc/jsapi/WebrtcGlobalInformation.cpp new file mode 100644 index 0000000000..25bcc2a6b0 --- /dev/null +++ b/dom/media/webrtc/jsapi/WebrtcGlobalInformation.cpp @@ -0,0 +1,714 @@ +/* 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 "WebrtcGlobalInformation.h" +#include "mozilla/media/webrtc/WebrtcGlobal.h" +#include "WebrtcGlobalChild.h" +#include "WebrtcGlobalParent.h" + +#include <algorithm> +#include <vector> +#include <type_traits> + +#include "mozilla/dom/WebrtcGlobalInformationBinding.h" +#include "mozilla/dom/RTCStatsReportBinding.h" // for RTCStatsReportInternal +#include "mozilla/dom/ContentChild.h" + +#include "nsNetCID.h" // NS_SOCKETTRANSPORTSERVICE_CONTRACTID +#include "nsServiceManagerUtils.h" // do_GetService +#include "mozilla/ErrorResult.h" +#include "nsProxyRelease.h" // nsMainThreadPtrHolder +#include "mozilla/Telemetry.h" +#include "mozilla/Unused.h" +#include "mozilla/RefPtr.h" +#include "mozilla/ClearOnShutdown.h" + +#include "common/browser_logging/WebRtcLog.h" +#include "transport/runnable_utils.h" +#include "MediaTransportHandler.h" +#include "PeerConnectionCtx.h" +#include "PeerConnectionImpl.h" + +namespace mozilla::dom { + +typedef nsMainThreadPtrHandle<WebrtcGlobalStatisticsCallback> + StatsRequestCallback; + +typedef nsMainThreadPtrHandle<WebrtcGlobalLoggingCallback> LogRequestCallback; + +class WebrtcContentParents { + public: + static WebrtcGlobalParent* Alloc(); + static void Dealloc(WebrtcGlobalParent* aParent); + static bool Empty() { return sContentParents.empty(); } + static const std::vector<RefPtr<WebrtcGlobalParent>>& GetAll() { + return sContentParents; + } + + private: + static std::vector<RefPtr<WebrtcGlobalParent>> sContentParents; + WebrtcContentParents() = delete; + WebrtcContentParents(const WebrtcContentParents&) = delete; + WebrtcContentParents& operator=(const WebrtcContentParents&) = delete; +}; + +std::vector<RefPtr<WebrtcGlobalParent>> WebrtcContentParents::sContentParents; + +WebrtcGlobalParent* WebrtcContentParents::Alloc() { + RefPtr<WebrtcGlobalParent> cp = new WebrtcGlobalParent; + sContentParents.push_back(cp); + return cp.get(); +} + +void WebrtcContentParents::Dealloc(WebrtcGlobalParent* aParent) { + if (aParent) { + aParent->mShutdown = true; + auto cp = + std::find(sContentParents.begin(), sContentParents.end(), aParent); + if (cp != sContentParents.end()) { + sContentParents.erase(cp); + } + } +} + +static PeerConnectionCtx* GetPeerConnectionCtx() { + if (PeerConnectionCtx::isActive()) { + MOZ_ASSERT(PeerConnectionCtx::GetInstance()); + return PeerConnectionCtx::GetInstance(); + } + return nullptr; +} + +static RefPtr<PWebrtcGlobalParent::GetStatsPromise> +GetStatsPromiseForThisProcess(const nsAString& aPcIdFilter) { + nsTArray<RefPtr<dom::RTCStatsReportPromise>> promises; + + PeerConnectionCtx* ctx = GetPeerConnectionCtx(); + if (ctx) { + // Grab stats for non-closed PCs + for (const auto& [id, pc] : ctx->GetPeerConnections()) { + if (aPcIdFilter.IsEmpty() || aPcIdFilter.EqualsASCII(id.c_str())) { + if (pc->HasMedia()) { + promises.AppendElement(pc->GetStats(nullptr, true)); + } + } + } + + // Grab stats for closed PCs + for (const auto& report : ctx->mStatsForClosedPeerConnections) { + if (aPcIdFilter.IsEmpty() || aPcIdFilter == report.mPcid) { + promises.AppendElement(dom::RTCStatsReportPromise::CreateAndResolve( + MakeUnique<dom::RTCStatsReportInternal>(report), __func__)); + } + } + } + + auto UnwrapUniquePtrs = [](dom::RTCStatsReportPromise::AllSettledPromiseType:: + ResolveOrRejectValue&& aResult) { + nsTArray<dom::RTCStatsReportInternal> reports; + MOZ_RELEASE_ASSERT(aResult.IsResolve(), "AllSettled should never reject!"); + for (auto& reportResult : aResult.ResolveValue()) { + if (reportResult.IsResolve()) { + reports.AppendElement(*reportResult.ResolveValue()); + } + } + return PWebrtcGlobalParent::GetStatsPromise::CreateAndResolve( + std::move(reports), __func__); + }; + + return dom::RTCStatsReportPromise::AllSettled( + GetMainThreadSerialEventTarget(), promises) + ->Then(GetMainThreadSerialEventTarget(), __func__, + std::move(UnwrapUniquePtrs)); +} + +static nsTArray<dom::RTCStatsReportInternal>& GetWebrtcGlobalStatsStash() { + static StaticAutoPtr<nsTArray<dom::RTCStatsReportInternal>> sStash; + if (!sStash) { + sStash = new nsTArray<dom::RTCStatsReportInternal>(); + ClearOnShutdown(&sStash); + } + return *sStash; +} + +static std::map<int32_t, dom::Sequence<nsString>>& GetWebrtcGlobalLogStash() { + static StaticAutoPtr<std::map<int32_t, dom::Sequence<nsString>>> sStash; + if (!sStash) { + sStash = new std::map<int32_t, dom::Sequence<nsString>>(); + ClearOnShutdown(&sStash); + } + return *sStash; +} + +static void ClearClosedStats() { + GetWebrtcGlobalStatsStash().Clear(); + PeerConnectionCtx* ctx = GetPeerConnectionCtx(); + + if (ctx) { + ctx->mStatsForClosedPeerConnections.Clear(); + } +} + +void WebrtcGlobalInformation::ClearAllStats(const GlobalObject& aGlobal) { + if (!NS_IsMainThread()) { + return; + } + + // Chrome-only API + MOZ_ASSERT(XRE_IsParentProcess()); + + if (!WebrtcContentParents::Empty()) { + // Pass on the request to any content process based PeerConnections. + for (const auto& cp : WebrtcContentParents::GetAll()) { + Unused << cp->SendClearStats(); + } + } + + // Flush the history for the chrome process + ClearClosedStats(); +} + +void WebrtcGlobalInformation::GetAllStats( + const GlobalObject& aGlobal, WebrtcGlobalStatisticsCallback& aStatsCallback, + const Optional<nsAString>& pcIdFilter, ErrorResult& aRv) { + if (!NS_IsMainThread()) { + aRv.Throw(NS_ERROR_NOT_SAME_THREAD); + return; + } + + MOZ_ASSERT(XRE_IsParentProcess()); + + nsTArray<RefPtr<PWebrtcGlobalParent::GetStatsPromise>> statsPromises; + + nsString filter; + if (pcIdFilter.WasPassed()) { + filter = pcIdFilter.Value(); + } + + for (const auto& cp : WebrtcContentParents::GetAll()) { + statsPromises.AppendElement(cp->SendGetStats(filter)); + } + + // Stats from this (the parent) process. How long do we keep supporting this? + statsPromises.AppendElement(GetStatsPromiseForThisProcess(filter)); + + // CallbackObject does not support threadsafe refcounting, and must be + // used and destroyed on main. + StatsRequestCallback callbackHandle( + new nsMainThreadPtrHolder<WebrtcGlobalStatisticsCallback>( + "WebrtcGlobalStatisticsCallback", &aStatsCallback)); + + auto FlattenThenStashThenCallback = + [callbackHandle, + filter](PWebrtcGlobalParent::GetStatsPromise::AllSettledPromiseType:: + ResolveOrRejectValue&& aResult) MOZ_CAN_RUN_SCRIPT_BOUNDARY { + std::set<nsString> pcids; + WebrtcGlobalStatisticsReport flattened; + MOZ_RELEASE_ASSERT(aResult.IsResolve(), + "AllSettled should never reject!"); + for (auto& contentProcessResult : aResult.ResolveValue()) { + // TODO: Report rejection on individual content processes someday? + if (contentProcessResult.IsResolve()) { + for (auto& pcStats : contentProcessResult.ResolveValue()) { + pcids.insert(pcStats.mPcid); + if (!flattened.mReports.AppendElement(std::move(pcStats), + fallible)) { + mozalloc_handle_oom(0); + } + } + } + } + + if (filter.IsEmpty()) { + // Unfiltered is pretty simple; add stuff from stash that is + // missing, then stomp the stash with the new reports. + for (auto& pcStats : GetWebrtcGlobalStatsStash()) { + if (!pcids.count(pcStats.mPcid)) { + // Stats from a closed PC or stopped content process. + // Content process may have gone away before we got to update + // this. + pcStats.mClosed = true; + if (!flattened.mReports.AppendElement(std::move(pcStats), + fallible)) { + mozalloc_handle_oom(0); + } + } + } + GetWebrtcGlobalStatsStash() = flattened.mReports; + } else { + // Filtered is slightly more complex + if (flattened.mReports.IsEmpty()) { + // Find entry from stash and add it to report + for (auto& pcStats : GetWebrtcGlobalStatsStash()) { + if (pcStats.mPcid == filter) { + pcStats.mClosed = true; + if (!flattened.mReports.AppendElement(std::move(pcStats), + fallible)) { + mozalloc_handle_oom(0); + } + } + } + } else { + // Find entries in stash, remove them, and then add new entries + for (size_t i = 0; i < GetWebrtcGlobalStatsStash().Length();) { + auto& pcStats = GetWebrtcGlobalStatsStash()[i]; + if (pcStats.mPcid == filter) { + GetWebrtcGlobalStatsStash().RemoveElementAt(i); + } else { + ++i; + } + } + GetWebrtcGlobalStatsStash().AppendElements(flattened.mReports); + } + } + + IgnoredErrorResult rv; + callbackHandle->Call(flattened, rv); + }; + + PWebrtcGlobalParent::GetStatsPromise::AllSettled( + GetMainThreadSerialEventTarget(), statsPromises) + ->Then(GetMainThreadSerialEventTarget(), __func__, + std::move(FlattenThenStashThenCallback)); + + aRv = NS_OK; +} + +static RefPtr<PWebrtcGlobalParent::GetLogPromise> GetLogPromise() { + PeerConnectionCtx* ctx = GetPeerConnectionCtx(); + if (!ctx) { + // This process has never created a PeerConnection, so no ICE logging. + return PWebrtcGlobalParent::GetLogPromise::CreateAndResolve( + Sequence<nsString>(), __func__); + } + + nsresult rv; + nsCOMPtr<nsISerialEventTarget> stsThread = + do_GetService(NS_SOCKETTRANSPORTSERVICE_CONTRACTID, &rv); + + if (NS_WARN_IF(NS_FAILED(rv) || !stsThread)) { + return PWebrtcGlobalParent::GetLogPromise::CreateAndResolve( + Sequence<nsString>(), __func__); + } + + RefPtr<MediaTransportHandler> transportHandler = ctx->GetTransportHandler(); + + auto AddMarkers = + [](MediaTransportHandler::IceLogPromise::ResolveOrRejectValue&& aValue) { + nsString pid; + pid.AppendInt(getpid()); + Sequence<nsString> logs; + if (aValue.IsResolve() && !aValue.ResolveValue().IsEmpty()) { + bool ok = logs.AppendElement( + u"+++++++ BEGIN (process id "_ns + pid + u") ++++++++"_ns, + fallible); + ok &= + !!logs.AppendElements(std::move(aValue.ResolveValue()), fallible); + ok &= !!logs.AppendElement( + u"+++++++ END (process id "_ns + pid + u") ++++++++"_ns, + fallible); + if (!ok) { + mozalloc_handle_oom(0); + } + } + return PWebrtcGlobalParent::GetLogPromise::CreateAndResolve( + std::move(logs), __func__); + }; + + return transportHandler->GetIceLog(nsCString()) + ->Then(GetMainThreadSerialEventTarget(), __func__, std::move(AddMarkers)); +} + +static nsresult RunLogClear() { + PeerConnectionCtx* ctx = GetPeerConnectionCtx(); + if (!ctx) { + // This process has never created a PeerConnection, so no ICE logging. + return NS_OK; + } + + nsresult rv; + nsCOMPtr<nsISerialEventTarget> stsThread = + do_GetService(NS_SOCKETTRANSPORTSERVICE_CONTRACTID, &rv); + + if (NS_FAILED(rv)) { + return rv; + } + if (!stsThread) { + return NS_ERROR_FAILURE; + } + + RefPtr<MediaTransportHandler> transportHandler = ctx->GetTransportHandler(); + + return RUN_ON_THREAD( + stsThread, + WrapRunnable(transportHandler, &MediaTransportHandler::ClearIceLog), + NS_DISPATCH_NORMAL); +} + +void WebrtcGlobalInformation::ClearLogging(const GlobalObject& aGlobal) { + if (!NS_IsMainThread()) { + return; + } + + // Chrome-only API + MOZ_ASSERT(XRE_IsParentProcess()); + GetWebrtcGlobalLogStash().clear(); + + if (!WebrtcContentParents::Empty()) { + // Clear content process signaling logs + for (const auto& cp : WebrtcContentParents::GetAll()) { + Unused << cp->SendClearLog(); + } + } + + // Clear chrome process signaling logs + Unused << RunLogClear(); +} + +static RefPtr<GenericPromise> UpdateLogStash() { + nsTArray<RefPtr<GenericPromise>> logPromises; + MOZ_ASSERT(XRE_IsParentProcess()); + for (const auto& cp : WebrtcContentParents::GetAll()) { + auto StashLog = + [id = cp->Id() * 2 /* Make sure 1 isn't used */]( + PWebrtcGlobalParent::GetLogPromise::ResolveOrRejectValue&& aValue) { + if (aValue.IsResolve() && !aValue.ResolveValue().IsEmpty()) { + GetWebrtcGlobalLogStash()[id] = aValue.ResolveValue(); + } + return GenericPromise::CreateAndResolve(true, __func__); + }; + logPromises.AppendElement(cp->SendGetLog()->Then( + GetMainThreadSerialEventTarget(), __func__, std::move(StashLog))); + } + + // Get ICE logging for this (the parent) process. How long do we support this? + logPromises.AppendElement(GetLogPromise()->Then( + GetMainThreadSerialEventTarget(), __func__, + [](PWebrtcGlobalParent::GetLogPromise::ResolveOrRejectValue&& aValue) { + if (aValue.IsResolve()) { + GetWebrtcGlobalLogStash()[1] = aValue.ResolveValue(); + } + return GenericPromise::CreateAndResolve(true, __func__); + })); + + return GenericPromise::AllSettled(GetMainThreadSerialEventTarget(), + logPromises) + ->Then(GetMainThreadSerialEventTarget(), __func__, + [](GenericPromise::AllSettledPromiseType::ResolveOrRejectValue&& + aValue) { + // We don't care about the value, since we're just going to copy + // what is in the stash. This ignores failures too, which is what + // we want. + return GenericPromise::CreateAndResolve(true, __func__); + }); +} + +void WebrtcGlobalInformation::GetLogging( + const GlobalObject& aGlobal, const nsAString& aPattern, + WebrtcGlobalLoggingCallback& aLoggingCallback, ErrorResult& aRv) { + if (!NS_IsMainThread()) { + aRv.Throw(NS_ERROR_NOT_SAME_THREAD); + return; + } + + MOZ_ASSERT(XRE_IsParentProcess()); + + nsAutoCString pattern; + CopyUTF16toUTF8(aPattern, pattern); + + // CallbackObject does not support threadsafe refcounting, and must be + // destroyed on main. + LogRequestCallback callbackHandle( + new nsMainThreadPtrHolder<WebrtcGlobalLoggingCallback>( + "WebrtcGlobalLoggingCallback", &aLoggingCallback)); + + auto FilterThenCallback = + [pattern, callbackHandle](GenericPromise::ResolveOrRejectValue&& aValue) + MOZ_CAN_RUN_SCRIPT_BOUNDARY { + dom::Sequence<nsString> flattened; + for (const auto& [id, log] : GetWebrtcGlobalLogStash()) { + (void)id; + for (const auto& line : log) { + if (pattern.IsEmpty() || (line.Find(pattern) != kNotFound)) { + if (!flattened.AppendElement(line, fallible)) { + mozalloc_handle_oom(0); + } + } + } + } + IgnoredErrorResult rv; + callbackHandle->Call(flattened, rv); + }; + + UpdateLogStash()->Then(GetMainThreadSerialEventTarget(), __func__, + std::move(FilterThenCallback)); + aRv = NS_OK; +} + +static int32_t sLastSetLevel = 0; +static bool sLastAECDebug = false; +static Maybe<nsCString> sAecDebugLogDir; + +void WebrtcGlobalInformation::SetDebugLevel(const GlobalObject& aGlobal, + int32_t aLevel) { + if (aLevel) { + StartWebRtcLog(mozilla::LogLevel(aLevel)); + } else { + StopWebRtcLog(); + } + sLastSetLevel = aLevel; + + for (const auto& cp : WebrtcContentParents::GetAll()) { + Unused << cp->SendSetDebugMode(aLevel); + } +} + +int32_t WebrtcGlobalInformation::DebugLevel(const GlobalObject& aGlobal) { + return sLastSetLevel; +} + +void WebrtcGlobalInformation::SetAecDebug(const GlobalObject& aGlobal, + bool aEnable) { + if (aEnable) { + sAecDebugLogDir = Some(StartAecLog()); + } else { + StopAecLog(); + } + + sLastAECDebug = aEnable; + + for (const auto& cp : WebrtcContentParents::GetAll()) { + Unused << cp->SendSetAecLogging(aEnable); + } +} + +bool WebrtcGlobalInformation::AecDebug(const GlobalObject& aGlobal) { + return sLastAECDebug; +} + +void WebrtcGlobalInformation::GetAecDebugLogDir(const GlobalObject& aGlobal, + nsAString& aDir) { + aDir = NS_ConvertASCIItoUTF16(sAecDebugLogDir.valueOr(""_ns)); +} + +WebrtcGlobalParent* WebrtcGlobalParent::Alloc() { + return WebrtcContentParents::Alloc(); +} + +bool WebrtcGlobalParent::Dealloc(WebrtcGlobalParent* aActor) { + WebrtcContentParents::Dealloc(aActor); + return true; +} + +void WebrtcGlobalParent::ActorDestroy(ActorDestroyReason aWhy) { + mShutdown = true; +} + +mozilla::ipc::IPCResult WebrtcGlobalParent::Recv__delete__() { + return IPC_OK(); +} + +MOZ_IMPLICIT WebrtcGlobalParent::WebrtcGlobalParent() : mShutdown(false) { + MOZ_COUNT_CTOR(WebrtcGlobalParent); +} + +MOZ_IMPLICIT WebrtcGlobalParent::~WebrtcGlobalParent() { + MOZ_COUNT_DTOR(WebrtcGlobalParent); +} + +mozilla::ipc::IPCResult WebrtcGlobalChild::RecvGetStats( + const nsString& aPcIdFilter, GetStatsResolver&& aResolve) { + if (!mShutdown) { + GetStatsPromiseForThisProcess(aPcIdFilter) + ->Then( + GetMainThreadSerialEventTarget(), __func__, + [resolve = std::move(aResolve)]( + nsTArray<dom::RTCStatsReportInternal>&& aReports) { + resolve(std::move(aReports)); + }, + []() { MOZ_CRASH(); }); + return IPC_OK(); + } + + aResolve(nsTArray<RTCStatsReportInternal>()); + return IPC_OK(); +} + +mozilla::ipc::IPCResult WebrtcGlobalChild::RecvClearStats() { + if (mShutdown) { + return IPC_OK(); + } + + ClearClosedStats(); + return IPC_OK(); +} + +mozilla::ipc::IPCResult WebrtcGlobalChild::RecvGetLog( + GetLogResolver&& aResolve) { + if (mShutdown) { + aResolve(Sequence<nsString>()); + return IPC_OK(); + } + + GetLogPromise()->Then( + GetMainThreadSerialEventTarget(), __func__, + [aResolve = std::move(aResolve)]( + PWebrtcGlobalParent::GetLogPromise::ResolveOrRejectValue&& aValue) { + if (aValue.IsResolve()) { + aResolve(aValue.ResolveValue()); + } else { + aResolve(Sequence<nsString>()); + } + }); + + return IPC_OK(); +} + +mozilla::ipc::IPCResult WebrtcGlobalChild::RecvClearLog() { + if (mShutdown) { + return IPC_OK(); + } + + RunLogClear(); + return IPC_OK(); +} + +mozilla::ipc::IPCResult WebrtcGlobalChild::RecvSetAecLogging( + const bool& aEnable) { + if (!mShutdown) { + if (aEnable) { + StartAecLog(); + } else { + StopAecLog(); + } + } + return IPC_OK(); +} + +mozilla::ipc::IPCResult WebrtcGlobalChild::RecvSetDebugMode(const int& aLevel) { + if (!mShutdown) { + if (aLevel) { + StartWebRtcLog(mozilla::LogLevel(aLevel)); + } else { + StopWebRtcLog(); + } + } + return IPC_OK(); +} + +WebrtcGlobalChild* WebrtcGlobalChild::Create() { + WebrtcGlobalChild* child = static_cast<WebrtcGlobalChild*>( + ContentChild::GetSingleton()->SendPWebrtcGlobalConstructor()); + return child; +} + +void WebrtcGlobalChild::ActorDestroy(ActorDestroyReason aWhy) { + mShutdown = true; +} + +MOZ_IMPLICIT WebrtcGlobalChild::WebrtcGlobalChild() : mShutdown(false) { + MOZ_COUNT_CTOR(WebrtcGlobalChild); +} + +MOZ_IMPLICIT WebrtcGlobalChild::~WebrtcGlobalChild() { + MOZ_COUNT_DTOR(WebrtcGlobalChild); +} + +static void StoreLongTermICEStatisticsImpl_m(RTCStatsReportInternal* report) { + using namespace Telemetry; + + report->mClosed = true; + + for (const auto& outboundRtpStats : report->mOutboundRtpStreamStats) { + bool isVideo = (outboundRtpStats.mId.Value().Find("video") != -1); + if (!isVideo) { + continue; + } + if (outboundRtpStats.mBitrateMean.WasPassed()) { + Accumulate(WEBRTC_VIDEO_ENCODER_BITRATE_AVG_PER_CALL_KBPS, + uint32_t(outboundRtpStats.mBitrateMean.Value() / 1000)); + } + if (outboundRtpStats.mBitrateStdDev.WasPassed()) { + Accumulate(WEBRTC_VIDEO_ENCODER_BITRATE_STD_DEV_PER_CALL_KBPS, + uint32_t(outboundRtpStats.mBitrateStdDev.Value() / 1000)); + } + if (outboundRtpStats.mFramerateMean.WasPassed()) { + Accumulate(WEBRTC_VIDEO_ENCODER_FRAMERATE_AVG_PER_CALL, + uint32_t(outboundRtpStats.mFramerateMean.Value())); + } + if (outboundRtpStats.mFramerateStdDev.WasPassed()) { + Accumulate(WEBRTC_VIDEO_ENCODER_FRAMERATE_10X_STD_DEV_PER_CALL, + uint32_t(outboundRtpStats.mFramerateStdDev.Value() * 10)); + } + if (outboundRtpStats.mDroppedFrames.WasPassed() && + report->mCallDurationMs.WasPassed()) { + double mins = report->mCallDurationMs.Value() / (1000 * 60); + if (mins > 0) { + Accumulate( + WEBRTC_VIDEO_ENCODER_DROPPED_FRAMES_PER_CALL_FPM, + uint32_t(double(outboundRtpStats.mDroppedFrames.Value()) / mins)); + } + } + } + + for (const auto& inboundRtpStats : report->mInboundRtpStreamStats) { + bool isVideo = (inboundRtpStats.mId.Value().Find("video") != -1); + if (!isVideo) { + continue; + } + if (inboundRtpStats.mBitrateMean.WasPassed()) { + Accumulate(WEBRTC_VIDEO_DECODER_BITRATE_AVG_PER_CALL_KBPS, + uint32_t(inboundRtpStats.mBitrateMean.Value() / 1000)); + } + if (inboundRtpStats.mBitrateStdDev.WasPassed()) { + Accumulate(WEBRTC_VIDEO_DECODER_BITRATE_STD_DEV_PER_CALL_KBPS, + uint32_t(inboundRtpStats.mBitrateStdDev.Value() / 1000)); + } + if (inboundRtpStats.mFramerateMean.WasPassed()) { + Accumulate(WEBRTC_VIDEO_DECODER_FRAMERATE_AVG_PER_CALL, + uint32_t(inboundRtpStats.mFramerateMean.Value())); + } + if (inboundRtpStats.mFramerateStdDev.WasPassed()) { + Accumulate(WEBRTC_VIDEO_DECODER_FRAMERATE_10X_STD_DEV_PER_CALL, + uint32_t(inboundRtpStats.mFramerateStdDev.Value() * 10)); + } + if (inboundRtpStats.mDiscardedPackets.WasPassed() && + report->mCallDurationMs.WasPassed()) { + double mins = report->mCallDurationMs.Value() / (1000 * 60); + if (mins > 0) { + Accumulate( + WEBRTC_VIDEO_DECODER_DISCARDED_PACKETS_PER_CALL_PPM, + uint32_t(double(inboundRtpStats.mDiscardedPackets.Value()) / mins)); + } + } + } + + // Finally, store the stats + + PeerConnectionCtx* ctx = GetPeerConnectionCtx(); + if (ctx) { + if (!ctx->mStatsForClosedPeerConnections.AppendElement(*report, fallible)) { + mozalloc_handle_oom(0); + } + } +} + +void WebrtcGlobalInformation::StoreLongTermICEStatistics( + PeerConnectionImpl& aPc) { + if (aPc.IceConnectionState() == RTCIceConnectionState::New) { + // ICE has not started; we won't have any remote candidates, so recording + // statistics on gathered candidates is pointless. + return; + } + + aPc.GetStats(nullptr, true) + ->Then( + GetMainThreadSerialEventTarget(), __func__, + [=](UniquePtr<dom::RTCStatsReportInternal>&& aReport) { + StoreLongTermICEStatisticsImpl_m(aReport.get()); + }, + [=](nsresult aError) {}); +} + +} // namespace mozilla::dom |