1 /* This Source Code Form is subject to the terms of the Mozilla Public
2 * License, v. 2.0. If a copy of the MPL was not distributed with this file,
3 * You can obtain one at http://mozilla.org/MPL/2.0/. */
4
5 #include "WebrtcGlobalInformation.h"
6 #include "mozilla/media/webrtc/WebrtcGlobal.h"
7 #include "WebrtcGlobalChild.h"
8 #include "WebrtcGlobalParent.h"
9
10 #include <algorithm>
11 #include <vector>
12 #include <type_traits>
13
14 #include "mozilla/dom/WebrtcGlobalInformationBinding.h"
15 #include "mozilla/dom/RTCStatsReportBinding.h" // for RTCStatsReportInternal
16 #include "mozilla/dom/ContentChild.h"
17
18 #include "nsNetCID.h" // NS_SOCKETTRANSPORTSERVICE_CONTRACTID
19 #include "nsServiceManagerUtils.h" // do_GetService
20 #include "mozilla/ErrorResult.h"
21 #include "nsProxyRelease.h" // nsMainThreadPtrHolder
22 #include "mozilla/Telemetry.h"
23 #include "mozilla/Unused.h"
24 #include "mozilla/RefPtr.h"
25 #include "mozilla/ClearOnShutdown.h"
26
27 #include "common/browser_logging/WebRtcLog.h"
28 #include "transport/runnable_utils.h"
29 #include "MediaTransportHandler.h"
30 #include "PeerConnectionCtx.h"
31 #include "PeerConnectionImpl.h"
32
33 namespace mozilla::dom {
34
35 typedef nsMainThreadPtrHandle<WebrtcGlobalStatisticsCallback>
36 StatsRequestCallback;
37
38 typedef nsMainThreadPtrHandle<WebrtcGlobalLoggingCallback> LogRequestCallback;
39
40 class WebrtcContentParents {
41 public:
42 static WebrtcGlobalParent* Alloc();
43 static void Dealloc(WebrtcGlobalParent* aParent);
Empty()44 static bool Empty() { return sContentParents.empty(); }
GetAll()45 static const std::vector<RefPtr<WebrtcGlobalParent>>& GetAll() {
46 return sContentParents;
47 }
48
49 private:
50 static std::vector<RefPtr<WebrtcGlobalParent>> sContentParents;
51 WebrtcContentParents() = delete;
52 WebrtcContentParents(const WebrtcContentParents&) = delete;
53 WebrtcContentParents& operator=(const WebrtcContentParents&) = delete;
54 };
55
56 std::vector<RefPtr<WebrtcGlobalParent>> WebrtcContentParents::sContentParents;
57
Alloc()58 WebrtcGlobalParent* WebrtcContentParents::Alloc() {
59 RefPtr<WebrtcGlobalParent> cp = new WebrtcGlobalParent;
60 sContentParents.push_back(cp);
61 return cp.get();
62 }
63
Dealloc(WebrtcGlobalParent * aParent)64 void WebrtcContentParents::Dealloc(WebrtcGlobalParent* aParent) {
65 if (aParent) {
66 aParent->mShutdown = true;
67 auto cp =
68 std::find(sContentParents.begin(), sContentParents.end(), aParent);
69 if (cp != sContentParents.end()) {
70 sContentParents.erase(cp);
71 }
72 }
73 }
74
GetPeerConnectionCtx()75 static PeerConnectionCtx* GetPeerConnectionCtx() {
76 if (PeerConnectionCtx::isActive()) {
77 MOZ_ASSERT(PeerConnectionCtx::GetInstance());
78 return PeerConnectionCtx::GetInstance();
79 }
80 return nullptr;
81 }
82
83 static RefPtr<PWebrtcGlobalParent::GetStatsPromise>
GetStatsPromiseForThisProcess(const nsAString & aPcIdFilter)84 GetStatsPromiseForThisProcess(const nsAString& aPcIdFilter) {
85 nsTArray<RefPtr<dom::RTCStatsReportPromise>> promises;
86
87 PeerConnectionCtx* ctx = GetPeerConnectionCtx();
88 if (ctx) {
89 // Grab stats for non-closed PCs
90 for (const auto& [id, pc] : ctx->GetPeerConnections()) {
91 if (aPcIdFilter.IsEmpty() || aPcIdFilter.EqualsASCII(id.c_str())) {
92 if (pc->HasMedia()) {
93 promises.AppendElement(pc->GetStats(nullptr, true));
94 }
95 }
96 }
97
98 // Grab stats for closed PCs
99 for (const auto& report : ctx->mStatsForClosedPeerConnections) {
100 if (aPcIdFilter.IsEmpty() || aPcIdFilter == report.mPcid) {
101 promises.AppendElement(dom::RTCStatsReportPromise::CreateAndResolve(
102 MakeUnique<dom::RTCStatsReportInternal>(report), __func__));
103 }
104 }
105 }
106
107 auto UnwrapUniquePtrs = [](dom::RTCStatsReportPromise::AllSettledPromiseType::
108 ResolveOrRejectValue&& aResult) {
109 nsTArray<dom::RTCStatsReportInternal> reports;
110 MOZ_RELEASE_ASSERT(aResult.IsResolve(), "AllSettled should never reject!");
111 for (auto& reportResult : aResult.ResolveValue()) {
112 if (reportResult.IsResolve()) {
113 reports.AppendElement(*reportResult.ResolveValue());
114 }
115 }
116 return PWebrtcGlobalParent::GetStatsPromise::CreateAndResolve(
117 std::move(reports), __func__);
118 };
119
120 return dom::RTCStatsReportPromise::AllSettled(
121 GetMainThreadSerialEventTarget(), promises)
122 ->Then(GetMainThreadSerialEventTarget(), __func__,
123 std::move(UnwrapUniquePtrs));
124 }
125
GetWebrtcGlobalStatsStash()126 static nsTArray<dom::RTCStatsReportInternal>& GetWebrtcGlobalStatsStash() {
127 static StaticAutoPtr<nsTArray<dom::RTCStatsReportInternal>> sStash;
128 if (!sStash) {
129 sStash = new nsTArray<dom::RTCStatsReportInternal>();
130 ClearOnShutdown(&sStash);
131 }
132 return *sStash;
133 }
134
GetWebrtcGlobalLogStash()135 static std::map<int32_t, dom::Sequence<nsString>>& GetWebrtcGlobalLogStash() {
136 static StaticAutoPtr<std::map<int32_t, dom::Sequence<nsString>>> sStash;
137 if (!sStash) {
138 sStash = new std::map<int32_t, dom::Sequence<nsString>>();
139 ClearOnShutdown(&sStash);
140 }
141 return *sStash;
142 }
143
ClearClosedStats()144 static void ClearClosedStats() {
145 GetWebrtcGlobalStatsStash().Clear();
146 PeerConnectionCtx* ctx = GetPeerConnectionCtx();
147
148 if (ctx) {
149 ctx->mStatsForClosedPeerConnections.Clear();
150 }
151 }
152
ClearAllStats(const GlobalObject & aGlobal)153 void WebrtcGlobalInformation::ClearAllStats(const GlobalObject& aGlobal) {
154 if (!NS_IsMainThread()) {
155 return;
156 }
157
158 // Chrome-only API
159 MOZ_ASSERT(XRE_IsParentProcess());
160
161 if (!WebrtcContentParents::Empty()) {
162 // Pass on the request to any content process based PeerConnections.
163 for (const auto& cp : WebrtcContentParents::GetAll()) {
164 Unused << cp->SendClearStats();
165 }
166 }
167
168 // Flush the history for the chrome process
169 ClearClosedStats();
170 }
171
GetAllStats(const GlobalObject & aGlobal,WebrtcGlobalStatisticsCallback & aStatsCallback,const Optional<nsAString> & pcIdFilter,ErrorResult & aRv)172 void WebrtcGlobalInformation::GetAllStats(
173 const GlobalObject& aGlobal, WebrtcGlobalStatisticsCallback& aStatsCallback,
174 const Optional<nsAString>& pcIdFilter, ErrorResult& aRv) {
175 if (!NS_IsMainThread()) {
176 aRv.Throw(NS_ERROR_NOT_SAME_THREAD);
177 return;
178 }
179
180 MOZ_ASSERT(XRE_IsParentProcess());
181
182 nsTArray<RefPtr<PWebrtcGlobalParent::GetStatsPromise>> statsPromises;
183
184 nsString filter;
185 if (pcIdFilter.WasPassed()) {
186 filter = pcIdFilter.Value();
187 }
188
189 for (const auto& cp : WebrtcContentParents::GetAll()) {
190 statsPromises.AppendElement(cp->SendGetStats(filter));
191 }
192
193 // Stats from this (the parent) process. How long do we keep supporting this?
194 statsPromises.AppendElement(GetStatsPromiseForThisProcess(filter));
195
196 // CallbackObject does not support threadsafe refcounting, and must be
197 // used and destroyed on main.
198 StatsRequestCallback callbackHandle(
199 new nsMainThreadPtrHolder<WebrtcGlobalStatisticsCallback>(
200 "WebrtcGlobalStatisticsCallback", &aStatsCallback));
201
202 auto FlattenThenStashThenCallback =
203 [callbackHandle,
204 filter](PWebrtcGlobalParent::GetStatsPromise::AllSettledPromiseType::
205 ResolveOrRejectValue&& aResult) MOZ_CAN_RUN_SCRIPT_BOUNDARY {
206 std::set<nsString> pcids;
207 WebrtcGlobalStatisticsReport flattened;
208 MOZ_RELEASE_ASSERT(aResult.IsResolve(),
209 "AllSettled should never reject!");
210 for (auto& contentProcessResult : aResult.ResolveValue()) {
211 // TODO: Report rejection on individual content processes someday?
212 if (contentProcessResult.IsResolve()) {
213 for (auto& pcStats : contentProcessResult.ResolveValue()) {
214 pcids.insert(pcStats.mPcid);
215 if (!flattened.mReports.AppendElement(std::move(pcStats),
216 fallible)) {
217 mozalloc_handle_oom(0);
218 }
219 }
220 }
221 }
222
223 if (filter.IsEmpty()) {
224 // Unfiltered is pretty simple; add stuff from stash that is
225 // missing, then stomp the stash with the new reports.
226 for (auto& pcStats : GetWebrtcGlobalStatsStash()) {
227 if (!pcids.count(pcStats.mPcid)) {
228 // Stats from a closed PC or stopped content process.
229 // Content process may have gone away before we got to update
230 // this.
231 pcStats.mClosed = true;
232 if (!flattened.mReports.AppendElement(std::move(pcStats),
233 fallible)) {
234 mozalloc_handle_oom(0);
235 }
236 }
237 }
238 GetWebrtcGlobalStatsStash() = flattened.mReports;
239 } else {
240 // Filtered is slightly more complex
241 if (flattened.mReports.IsEmpty()) {
242 // Find entry from stash and add it to report
243 for (auto& pcStats : GetWebrtcGlobalStatsStash()) {
244 if (pcStats.mPcid == filter) {
245 pcStats.mClosed = true;
246 if (!flattened.mReports.AppendElement(std::move(pcStats),
247 fallible)) {
248 mozalloc_handle_oom(0);
249 }
250 }
251 }
252 } else {
253 // Find entries in stash, remove them, and then add new entries
254 for (size_t i = 0; i < GetWebrtcGlobalStatsStash().Length();) {
255 auto& pcStats = GetWebrtcGlobalStatsStash()[i];
256 if (pcStats.mPcid == filter) {
257 GetWebrtcGlobalStatsStash().RemoveElementAt(i);
258 } else {
259 ++i;
260 }
261 }
262 GetWebrtcGlobalStatsStash().AppendElements(flattened.mReports);
263 }
264 }
265
266 IgnoredErrorResult rv;
267 callbackHandle->Call(flattened, rv);
268 };
269
270 PWebrtcGlobalParent::GetStatsPromise::AllSettled(
271 GetMainThreadSerialEventTarget(), statsPromises)
272 ->Then(GetMainThreadSerialEventTarget(), __func__,
273 std::move(FlattenThenStashThenCallback));
274
275 aRv = NS_OK;
276 }
277
GetLogPromise()278 static RefPtr<PWebrtcGlobalParent::GetLogPromise> GetLogPromise() {
279 PeerConnectionCtx* ctx = GetPeerConnectionCtx();
280 if (!ctx) {
281 // This process has never created a PeerConnection, so no ICE logging.
282 return PWebrtcGlobalParent::GetLogPromise::CreateAndResolve(
283 Sequence<nsString>(), __func__);
284 }
285
286 nsresult rv;
287 nsCOMPtr<nsISerialEventTarget> stsThread =
288 do_GetService(NS_SOCKETTRANSPORTSERVICE_CONTRACTID, &rv);
289
290 if (NS_WARN_IF(NS_FAILED(rv) || !stsThread)) {
291 return PWebrtcGlobalParent::GetLogPromise::CreateAndResolve(
292 Sequence<nsString>(), __func__);
293 }
294
295 RefPtr<MediaTransportHandler> transportHandler = ctx->GetTransportHandler();
296
297 auto AddMarkers =
298 [](MediaTransportHandler::IceLogPromise::ResolveOrRejectValue&& aValue) {
299 nsString pid;
300 pid.AppendInt(getpid());
301 Sequence<nsString> logs;
302 if (aValue.IsResolve() && !aValue.ResolveValue().IsEmpty()) {
303 bool ok = logs.AppendElement(
304 u"+++++++ BEGIN (process id "_ns + pid + u") ++++++++"_ns,
305 fallible);
306 ok &=
307 !!logs.AppendElements(std::move(aValue.ResolveValue()), fallible);
308 ok &= !!logs.AppendElement(
309 u"+++++++ END (process id "_ns + pid + u") ++++++++"_ns,
310 fallible);
311 if (!ok) {
312 mozalloc_handle_oom(0);
313 }
314 }
315 return PWebrtcGlobalParent::GetLogPromise::CreateAndResolve(
316 std::move(logs), __func__);
317 };
318
319 return transportHandler->GetIceLog(nsCString())
320 ->Then(GetMainThreadSerialEventTarget(), __func__, std::move(AddMarkers));
321 }
322
RunLogClear()323 static nsresult RunLogClear() {
324 PeerConnectionCtx* ctx = GetPeerConnectionCtx();
325 if (!ctx) {
326 // This process has never created a PeerConnection, so no ICE logging.
327 return NS_OK;
328 }
329
330 nsresult rv;
331 nsCOMPtr<nsISerialEventTarget> stsThread =
332 do_GetService(NS_SOCKETTRANSPORTSERVICE_CONTRACTID, &rv);
333
334 if (NS_FAILED(rv)) {
335 return rv;
336 }
337 if (!stsThread) {
338 return NS_ERROR_FAILURE;
339 }
340
341 RefPtr<MediaTransportHandler> transportHandler = ctx->GetTransportHandler();
342
343 return RUN_ON_THREAD(
344 stsThread,
345 WrapRunnable(transportHandler, &MediaTransportHandler::ClearIceLog),
346 NS_DISPATCH_NORMAL);
347 }
348
ClearLogging(const GlobalObject & aGlobal)349 void WebrtcGlobalInformation::ClearLogging(const GlobalObject& aGlobal) {
350 if (!NS_IsMainThread()) {
351 return;
352 }
353
354 // Chrome-only API
355 MOZ_ASSERT(XRE_IsParentProcess());
356 GetWebrtcGlobalLogStash().clear();
357
358 if (!WebrtcContentParents::Empty()) {
359 // Clear content process signaling logs
360 for (const auto& cp : WebrtcContentParents::GetAll()) {
361 Unused << cp->SendClearLog();
362 }
363 }
364
365 // Clear chrome process signaling logs
366 Unused << RunLogClear();
367 }
368
UpdateLogStash()369 static RefPtr<GenericPromise> UpdateLogStash() {
370 nsTArray<RefPtr<GenericPromise>> logPromises;
371 MOZ_ASSERT(XRE_IsParentProcess());
372 for (const auto& cp : WebrtcContentParents::GetAll()) {
373 auto StashLog =
374 [id = cp->Id() * 2 /* Make sure 1 isn't used */](
375 PWebrtcGlobalParent::GetLogPromise::ResolveOrRejectValue&& aValue) {
376 if (aValue.IsResolve() && !aValue.ResolveValue().IsEmpty()) {
377 GetWebrtcGlobalLogStash()[id] = aValue.ResolveValue();
378 }
379 return GenericPromise::CreateAndResolve(true, __func__);
380 };
381 logPromises.AppendElement(cp->SendGetLog()->Then(
382 GetMainThreadSerialEventTarget(), __func__, std::move(StashLog)));
383 }
384
385 // Get ICE logging for this (the parent) process. How long do we support this?
386 logPromises.AppendElement(GetLogPromise()->Then(
387 GetMainThreadSerialEventTarget(), __func__,
388 [](PWebrtcGlobalParent::GetLogPromise::ResolveOrRejectValue&& aValue) {
389 if (aValue.IsResolve()) {
390 GetWebrtcGlobalLogStash()[1] = aValue.ResolveValue();
391 }
392 return GenericPromise::CreateAndResolve(true, __func__);
393 }));
394
395 return GenericPromise::AllSettled(GetMainThreadSerialEventTarget(),
396 logPromises)
397 ->Then(GetMainThreadSerialEventTarget(), __func__,
398 [](GenericPromise::AllSettledPromiseType::ResolveOrRejectValue&&
399 aValue) {
400 // We don't care about the value, since we're just going to copy
401 // what is in the stash. This ignores failures too, which is what
402 // we want.
403 return GenericPromise::CreateAndResolve(true, __func__);
404 });
405 }
406
GetLogging(const GlobalObject & aGlobal,const nsAString & aPattern,WebrtcGlobalLoggingCallback & aLoggingCallback,ErrorResult & aRv)407 void WebrtcGlobalInformation::GetLogging(
408 const GlobalObject& aGlobal, const nsAString& aPattern,
409 WebrtcGlobalLoggingCallback& aLoggingCallback, ErrorResult& aRv) {
410 if (!NS_IsMainThread()) {
411 aRv.Throw(NS_ERROR_NOT_SAME_THREAD);
412 return;
413 }
414
415 MOZ_ASSERT(XRE_IsParentProcess());
416
417 nsAutoCString pattern;
418 CopyUTF16toUTF8(aPattern, pattern);
419
420 // CallbackObject does not support threadsafe refcounting, and must be
421 // destroyed on main.
422 LogRequestCallback callbackHandle(
423 new nsMainThreadPtrHolder<WebrtcGlobalLoggingCallback>(
424 "WebrtcGlobalLoggingCallback", &aLoggingCallback));
425
426 auto FilterThenCallback =
427 [pattern, callbackHandle](GenericPromise::ResolveOrRejectValue&& aValue)
428 MOZ_CAN_RUN_SCRIPT_BOUNDARY {
429 dom::Sequence<nsString> flattened;
430 for (const auto& [id, log] : GetWebrtcGlobalLogStash()) {
431 (void)id;
432 for (const auto& line : log) {
433 if (pattern.IsEmpty() || (line.Find(pattern) != kNotFound)) {
434 if (!flattened.AppendElement(line, fallible)) {
435 mozalloc_handle_oom(0);
436 }
437 }
438 }
439 }
440 IgnoredErrorResult rv;
441 callbackHandle->Call(flattened, rv);
442 };
443
444 UpdateLogStash()->Then(GetMainThreadSerialEventTarget(), __func__,
445 std::move(FilterThenCallback));
446 aRv = NS_OK;
447 }
448
449 static int32_t sLastSetLevel = 0;
450 static bool sLastAECDebug = false;
451 static Maybe<nsCString> sAecDebugLogDir;
452
SetDebugLevel(const GlobalObject & aGlobal,int32_t aLevel)453 void WebrtcGlobalInformation::SetDebugLevel(const GlobalObject& aGlobal,
454 int32_t aLevel) {
455 if (aLevel) {
456 StartWebRtcLog(mozilla::LogLevel(aLevel));
457 } else {
458 StopWebRtcLog();
459 }
460 sLastSetLevel = aLevel;
461
462 for (const auto& cp : WebrtcContentParents::GetAll()) {
463 Unused << cp->SendSetDebugMode(aLevel);
464 }
465 }
466
DebugLevel(const GlobalObject & aGlobal)467 int32_t WebrtcGlobalInformation::DebugLevel(const GlobalObject& aGlobal) {
468 return sLastSetLevel;
469 }
470
SetAecDebug(const GlobalObject & aGlobal,bool aEnable)471 void WebrtcGlobalInformation::SetAecDebug(const GlobalObject& aGlobal,
472 bool aEnable) {
473 if (aEnable) {
474 sAecDebugLogDir = Some(StartAecLog());
475 } else {
476 StopAecLog();
477 }
478
479 sLastAECDebug = aEnable;
480
481 for (const auto& cp : WebrtcContentParents::GetAll()) {
482 Unused << cp->SendSetAecLogging(aEnable);
483 }
484 }
485
AecDebug(const GlobalObject & aGlobal)486 bool WebrtcGlobalInformation::AecDebug(const GlobalObject& aGlobal) {
487 return sLastAECDebug;
488 }
489
GetAecDebugLogDir(const GlobalObject & aGlobal,nsAString & aDir)490 void WebrtcGlobalInformation::GetAecDebugLogDir(const GlobalObject& aGlobal,
491 nsAString& aDir) {
492 aDir = NS_ConvertASCIItoUTF16(sAecDebugLogDir.valueOr(""_ns));
493 }
494
Alloc()495 WebrtcGlobalParent* WebrtcGlobalParent::Alloc() {
496 return WebrtcContentParents::Alloc();
497 }
498
Dealloc(WebrtcGlobalParent * aActor)499 bool WebrtcGlobalParent::Dealloc(WebrtcGlobalParent* aActor) {
500 WebrtcContentParents::Dealloc(aActor);
501 return true;
502 }
503
ActorDestroy(ActorDestroyReason aWhy)504 void WebrtcGlobalParent::ActorDestroy(ActorDestroyReason aWhy) {
505 mShutdown = true;
506 }
507
Recv__delete__()508 mozilla::ipc::IPCResult WebrtcGlobalParent::Recv__delete__() {
509 return IPC_OK();
510 }
511
WebrtcGlobalParent()512 MOZ_IMPLICIT WebrtcGlobalParent::WebrtcGlobalParent() : mShutdown(false) {
513 MOZ_COUNT_CTOR(WebrtcGlobalParent);
514 }
515
~WebrtcGlobalParent()516 MOZ_IMPLICIT WebrtcGlobalParent::~WebrtcGlobalParent() {
517 MOZ_COUNT_DTOR(WebrtcGlobalParent);
518 }
519
RecvGetStats(const nsString & aPcIdFilter,GetStatsResolver && aResolve)520 mozilla::ipc::IPCResult WebrtcGlobalChild::RecvGetStats(
521 const nsString& aPcIdFilter, GetStatsResolver&& aResolve) {
522 if (!mShutdown) {
523 GetStatsPromiseForThisProcess(aPcIdFilter)
524 ->Then(
525 GetMainThreadSerialEventTarget(), __func__,
526 [resolve = std::move(aResolve)](
527 nsTArray<dom::RTCStatsReportInternal>&& aReports) {
528 resolve(std::move(aReports));
529 },
530 []() { MOZ_CRASH(); });
531 return IPC_OK();
532 }
533
534 aResolve(nsTArray<RTCStatsReportInternal>());
535 return IPC_OK();
536 }
537
RecvClearStats()538 mozilla::ipc::IPCResult WebrtcGlobalChild::RecvClearStats() {
539 if (mShutdown) {
540 return IPC_OK();
541 }
542
543 ClearClosedStats();
544 return IPC_OK();
545 }
546
RecvGetLog(GetLogResolver && aResolve)547 mozilla::ipc::IPCResult WebrtcGlobalChild::RecvGetLog(
548 GetLogResolver&& aResolve) {
549 if (mShutdown) {
550 aResolve(Sequence<nsString>());
551 return IPC_OK();
552 }
553
554 GetLogPromise()->Then(
555 GetMainThreadSerialEventTarget(), __func__,
556 [aResolve = std::move(aResolve)](
557 PWebrtcGlobalParent::GetLogPromise::ResolveOrRejectValue&& aValue) {
558 if (aValue.IsResolve()) {
559 aResolve(aValue.ResolveValue());
560 } else {
561 aResolve(Sequence<nsString>());
562 }
563 });
564
565 return IPC_OK();
566 }
567
RecvClearLog()568 mozilla::ipc::IPCResult WebrtcGlobalChild::RecvClearLog() {
569 if (mShutdown) {
570 return IPC_OK();
571 }
572
573 RunLogClear();
574 return IPC_OK();
575 }
576
RecvSetAecLogging(const bool & aEnable)577 mozilla::ipc::IPCResult WebrtcGlobalChild::RecvSetAecLogging(
578 const bool& aEnable) {
579 if (!mShutdown) {
580 if (aEnable) {
581 StartAecLog();
582 } else {
583 StopAecLog();
584 }
585 }
586 return IPC_OK();
587 }
588
RecvSetDebugMode(const int & aLevel)589 mozilla::ipc::IPCResult WebrtcGlobalChild::RecvSetDebugMode(const int& aLevel) {
590 if (!mShutdown) {
591 if (aLevel) {
592 StartWebRtcLog(mozilla::LogLevel(aLevel));
593 } else {
594 StopWebRtcLog();
595 }
596 }
597 return IPC_OK();
598 }
599
Create()600 WebrtcGlobalChild* WebrtcGlobalChild::Create() {
601 WebrtcGlobalChild* child = static_cast<WebrtcGlobalChild*>(
602 ContentChild::GetSingleton()->SendPWebrtcGlobalConstructor());
603 return child;
604 }
605
ActorDestroy(ActorDestroyReason aWhy)606 void WebrtcGlobalChild::ActorDestroy(ActorDestroyReason aWhy) {
607 mShutdown = true;
608 }
609
WebrtcGlobalChild()610 MOZ_IMPLICIT WebrtcGlobalChild::WebrtcGlobalChild() : mShutdown(false) {
611 MOZ_COUNT_CTOR(WebrtcGlobalChild);
612 }
613
~WebrtcGlobalChild()614 MOZ_IMPLICIT WebrtcGlobalChild::~WebrtcGlobalChild() {
615 MOZ_COUNT_DTOR(WebrtcGlobalChild);
616 }
617
StoreLongTermICEStatisticsImpl_m(RTCStatsReportInternal * report)618 static void StoreLongTermICEStatisticsImpl_m(RTCStatsReportInternal* report) {
619 using namespace Telemetry;
620
621 report->mClosed = true;
622
623 for (const auto& outboundRtpStats : report->mOutboundRtpStreamStats) {
624 bool isVideo = (outboundRtpStats.mId.Value().Find("video") != -1);
625 if (!isVideo) {
626 continue;
627 }
628 if (outboundRtpStats.mBitrateMean.WasPassed()) {
629 Accumulate(WEBRTC_VIDEO_ENCODER_BITRATE_AVG_PER_CALL_KBPS,
630 uint32_t(outboundRtpStats.mBitrateMean.Value() / 1000));
631 }
632 if (outboundRtpStats.mBitrateStdDev.WasPassed()) {
633 Accumulate(WEBRTC_VIDEO_ENCODER_BITRATE_STD_DEV_PER_CALL_KBPS,
634 uint32_t(outboundRtpStats.mBitrateStdDev.Value() / 1000));
635 }
636 if (outboundRtpStats.mFramerateMean.WasPassed()) {
637 Accumulate(WEBRTC_VIDEO_ENCODER_FRAMERATE_AVG_PER_CALL,
638 uint32_t(outboundRtpStats.mFramerateMean.Value()));
639 }
640 if (outboundRtpStats.mFramerateStdDev.WasPassed()) {
641 Accumulate(WEBRTC_VIDEO_ENCODER_FRAMERATE_10X_STD_DEV_PER_CALL,
642 uint32_t(outboundRtpStats.mFramerateStdDev.Value() * 10));
643 }
644 if (outboundRtpStats.mDroppedFrames.WasPassed() &&
645 report->mCallDurationMs.WasPassed()) {
646 double mins = report->mCallDurationMs.Value() / (1000 * 60);
647 if (mins > 0) {
648 Accumulate(
649 WEBRTC_VIDEO_ENCODER_DROPPED_FRAMES_PER_CALL_FPM,
650 uint32_t(double(outboundRtpStats.mDroppedFrames.Value()) / mins));
651 }
652 }
653 }
654
655 for (const auto& inboundRtpStats : report->mInboundRtpStreamStats) {
656 bool isVideo = (inboundRtpStats.mId.Value().Find("video") != -1);
657 if (!isVideo) {
658 continue;
659 }
660 if (inboundRtpStats.mBitrateMean.WasPassed()) {
661 Accumulate(WEBRTC_VIDEO_DECODER_BITRATE_AVG_PER_CALL_KBPS,
662 uint32_t(inboundRtpStats.mBitrateMean.Value() / 1000));
663 }
664 if (inboundRtpStats.mBitrateStdDev.WasPassed()) {
665 Accumulate(WEBRTC_VIDEO_DECODER_BITRATE_STD_DEV_PER_CALL_KBPS,
666 uint32_t(inboundRtpStats.mBitrateStdDev.Value() / 1000));
667 }
668 if (inboundRtpStats.mFramerateMean.WasPassed()) {
669 Accumulate(WEBRTC_VIDEO_DECODER_FRAMERATE_AVG_PER_CALL,
670 uint32_t(inboundRtpStats.mFramerateMean.Value()));
671 }
672 if (inboundRtpStats.mFramerateStdDev.WasPassed()) {
673 Accumulate(WEBRTC_VIDEO_DECODER_FRAMERATE_10X_STD_DEV_PER_CALL,
674 uint32_t(inboundRtpStats.mFramerateStdDev.Value() * 10));
675 }
676 if (inboundRtpStats.mDiscardedPackets.WasPassed() &&
677 report->mCallDurationMs.WasPassed()) {
678 double mins = report->mCallDurationMs.Value() / (1000 * 60);
679 if (mins > 0) {
680 Accumulate(
681 WEBRTC_VIDEO_DECODER_DISCARDED_PACKETS_PER_CALL_PPM,
682 uint32_t(double(inboundRtpStats.mDiscardedPackets.Value()) / mins));
683 }
684 }
685 }
686
687 // Finally, store the stats
688
689 PeerConnectionCtx* ctx = GetPeerConnectionCtx();
690 if (ctx) {
691 if (!ctx->mStatsForClosedPeerConnections.AppendElement(*report, fallible)) {
692 mozalloc_handle_oom(0);
693 }
694 }
695 }
696
StoreLongTermICEStatistics(PeerConnectionImpl & aPc)697 void WebrtcGlobalInformation::StoreLongTermICEStatistics(
698 PeerConnectionImpl& aPc) {
699 if (aPc.IceConnectionState() == RTCIceConnectionState::New) {
700 // ICE has not started; we won't have any remote candidates, so recording
701 // statistics on gathered candidates is pointless.
702 return;
703 }
704
705 aPc.GetStats(nullptr, true)
706 ->Then(
707 GetMainThreadSerialEventTarget(), __func__,
708 [=](UniquePtr<dom::RTCStatsReportInternal>&& aReport) {
709 StoreLongTermICEStatisticsImpl_m(aReport.get());
710 },
711 [=](nsresult aError) {});
712 }
713
714 } // namespace mozilla::dom
715