summaryrefslogtreecommitdiffstats
path: root/media/webrtc/signaling/src/peerconnection/PeerConnectionCtx.cpp
diff options
context:
space:
mode:
authorMatt A. Tobin <mattatobin@localhost.localdomain>2018-02-02 04:16:08 -0500
committerMatt A. Tobin <mattatobin@localhost.localdomain>2018-02-02 04:16:08 -0500
commit5f8de423f190bbb79a62f804151bc24824fa32d8 (patch)
tree10027f336435511475e392454359edea8e25895d /media/webrtc/signaling/src/peerconnection/PeerConnectionCtx.cpp
parent49ee0794b5d912db1f95dce6eb52d781dc210db5 (diff)
downloadUXP-5f8de423f190bbb79a62f804151bc24824fa32d8.tar
UXP-5f8de423f190bbb79a62f804151bc24824fa32d8.tar.gz
UXP-5f8de423f190bbb79a62f804151bc24824fa32d8.tar.lz
UXP-5f8de423f190bbb79a62f804151bc24824fa32d8.tar.xz
UXP-5f8de423f190bbb79a62f804151bc24824fa32d8.zip
Add m-esr52 at 52.6.0
Diffstat (limited to 'media/webrtc/signaling/src/peerconnection/PeerConnectionCtx.cpp')
-rw-r--r--media/webrtc/signaling/src/peerconnection/PeerConnectionCtx.cpp452
1 files changed, 452 insertions, 0 deletions
diff --git a/media/webrtc/signaling/src/peerconnection/PeerConnectionCtx.cpp b/media/webrtc/signaling/src/peerconnection/PeerConnectionCtx.cpp
new file mode 100644
index 000000000..515258efb
--- /dev/null
+++ b/media/webrtc/signaling/src/peerconnection/PeerConnectionCtx.cpp
@@ -0,0 +1,452 @@
+/* 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 "CSFLog.h"
+
+#include "PeerConnectionImpl.h"
+#include "PeerConnectionCtx.h"
+#include "runnable_utils.h"
+#include "prcvar.h"
+
+#include "mozilla/Telemetry.h"
+#include "browser_logging/WebRtcLog.h"
+
+#if !defined(MOZILLA_EXTERNAL_LINKAGE)
+#include "mozilla/dom/RTCPeerConnectionBinding.h"
+#include "mozilla/Preferences.h"
+#include <mozilla/Types.h>
+#endif
+
+#include "nsNetCID.h" // NS_SOCKETTRANSPORTSERVICE_CONTRACTID
+#include "nsServiceManagerUtils.h" // do_GetService
+#include "nsIObserverService.h"
+#include "nsIObserver.h"
+#include "mozilla/Services.h"
+#include "mozilla/StaticPtr.h"
+
+#include "gmp-video-decode.h" // GMP_API_VIDEO_DECODER
+#include "gmp-video-encode.h" // GMP_API_VIDEO_ENCODER
+
+static const char* logTag = "PeerConnectionCtx";
+
+namespace mozilla {
+
+using namespace dom;
+
+class PeerConnectionCtxShutdown : public nsIObserver
+{
+public:
+ NS_DECL_ISUPPORTS
+
+ PeerConnectionCtxShutdown() {}
+
+ void Init()
+ {
+ nsCOMPtr<nsIObserverService> observerService =
+ services::GetObserverService();
+ if (!observerService)
+ return;
+
+ nsresult rv = NS_OK;
+
+#ifdef MOZILLA_INTERNAL_API
+ rv = observerService->AddObserver(this,
+ NS_XPCOM_SHUTDOWN_OBSERVER_ID,
+ false);
+ MOZ_ALWAYS_SUCCEEDS(rv);
+#endif
+ (void) rv;
+ }
+
+ NS_IMETHOD Observe(nsISupports* aSubject, const char* aTopic,
+ const char16_t* aData) override {
+ if (strcmp(aTopic, NS_XPCOM_SHUTDOWN_OBSERVER_ID) == 0) {
+ CSFLogDebug(logTag, "Shutting down PeerConnectionCtx");
+ PeerConnectionCtx::Destroy();
+
+ nsCOMPtr<nsIObserverService> observerService =
+ services::GetObserverService();
+ if (!observerService)
+ return NS_ERROR_FAILURE;
+
+ nsresult rv = observerService->RemoveObserver(this,
+ NS_XPCOM_SHUTDOWN_OBSERVER_ID);
+ MOZ_ALWAYS_SUCCEEDS(rv);
+
+ // Make sure we're not deleted while still inside ::Observe()
+ RefPtr<PeerConnectionCtxShutdown> kungFuDeathGrip(this);
+ PeerConnectionCtx::gPeerConnectionCtxShutdown = nullptr;
+ }
+ return NS_OK;
+ }
+
+private:
+ virtual ~PeerConnectionCtxShutdown()
+ {
+ nsCOMPtr<nsIObserverService> observerService =
+ services::GetObserverService();
+ if (observerService)
+ observerService->RemoveObserver(this, NS_XPCOM_SHUTDOWN_OBSERVER_ID);
+ }
+};
+
+NS_IMPL_ISUPPORTS(PeerConnectionCtxShutdown, nsIObserver);
+}
+
+namespace mozilla {
+
+PeerConnectionCtx* PeerConnectionCtx::gInstance;
+nsIThread* PeerConnectionCtx::gMainThread;
+StaticRefPtr<PeerConnectionCtxShutdown> PeerConnectionCtx::gPeerConnectionCtxShutdown;
+
+const std::map<const std::string, PeerConnectionImpl *>&
+PeerConnectionCtx::mGetPeerConnections()
+{
+ return mPeerConnections;
+}
+
+nsresult PeerConnectionCtx::InitializeGlobal(nsIThread *mainThread,
+ nsIEventTarget* stsThread) {
+ if (!gMainThread) {
+ gMainThread = mainThread;
+ } else {
+ MOZ_ASSERT(gMainThread == mainThread);
+ }
+
+ nsresult res;
+
+ MOZ_ASSERT(NS_IsMainThread());
+
+ if (!gInstance) {
+ CSFLogDebug(logTag, "Creating PeerConnectionCtx");
+ PeerConnectionCtx *ctx = new PeerConnectionCtx();
+
+ res = ctx->Initialize();
+ PR_ASSERT(NS_SUCCEEDED(res));
+ if (!NS_SUCCEEDED(res))
+ return res;
+
+ gInstance = ctx;
+
+ if (!PeerConnectionCtx::gPeerConnectionCtxShutdown) {
+ PeerConnectionCtx::gPeerConnectionCtxShutdown = new PeerConnectionCtxShutdown();
+ PeerConnectionCtx::gPeerConnectionCtxShutdown->Init();
+ }
+ }
+
+ EnableWebRtcLog();
+ return NS_OK;
+}
+
+PeerConnectionCtx* PeerConnectionCtx::GetInstance() {
+ MOZ_ASSERT(gInstance);
+ return gInstance;
+}
+
+bool PeerConnectionCtx::isActive() {
+ return gInstance;
+}
+
+void PeerConnectionCtx::Destroy() {
+ CSFLogDebug(logTag, "%s", __FUNCTION__);
+
+ if (gInstance) {
+ gInstance->Cleanup();
+ delete gInstance;
+ gInstance = nullptr;
+ }
+
+ StopWebRtcLog();
+}
+
+#if !defined(MOZILLA_EXTERNAL_LINKAGE)
+typedef Vector<nsAutoPtr<RTCStatsQuery>> RTCStatsQueries;
+
+// Telemetry reporting every second after start of first call.
+// The threading model around the media pipelines is weird:
+// - The pipelines are containers,
+// - containers that are only safe on main thread, with members only safe on STS,
+// - hence the there and back again approach.
+
+static auto
+FindId(const Sequence<RTCInboundRTPStreamStats>& aArray,
+ const nsString &aId) -> decltype(aArray.Length()) {
+ for (decltype(aArray.Length()) i = 0; i < aArray.Length(); i++) {
+ if (aArray[i].mId.Value() == aId) {
+ return i;
+ }
+ }
+ return aArray.NoIndex;
+}
+
+static auto
+FindId(const nsTArray<nsAutoPtr<RTCStatsReportInternal>>& aArray,
+ const nsString &aId) -> decltype(aArray.Length()) {
+ for (decltype(aArray.Length()) i = 0; i < aArray.Length(); i++) {
+ if (aArray[i]->mPcid == aId) {
+ return i;
+ }
+ }
+ return aArray.NoIndex;
+}
+
+static void
+FreeOnMain_m(nsAutoPtr<RTCStatsQueries> aQueryList) {
+ MOZ_ASSERT(NS_IsMainThread());
+}
+
+static void
+EverySecondTelemetryCallback_s(nsAutoPtr<RTCStatsQueries> aQueryList) {
+ using namespace Telemetry;
+
+ if(!PeerConnectionCtx::isActive()) {
+ return;
+ }
+ PeerConnectionCtx *ctx = PeerConnectionCtx::GetInstance();
+
+ for (auto q = aQueryList->begin(); q != aQueryList->end(); ++q) {
+ PeerConnectionImpl::ExecuteStatsQuery_s(*q);
+ auto& r = *(*q)->report;
+ if (r.mInboundRTPStreamStats.WasPassed()) {
+ // First, get reports from a second ago, if any, for calculations below
+ const Sequence<RTCInboundRTPStreamStats> *lastInboundStats = nullptr;
+ {
+ auto i = FindId(ctx->mLastReports, r.mPcid);
+ if (i != ctx->mLastReports.NoIndex) {
+ lastInboundStats = &ctx->mLastReports[i]->mInboundRTPStreamStats.Value();
+ }
+ }
+ // Then, look for the things we want telemetry on
+ auto& array = r.mInboundRTPStreamStats.Value();
+ for (decltype(array.Length()) i = 0; i < array.Length(); i++) {
+ auto& s = array[i];
+ bool isAudio = (s.mId.Value().Find("audio") != -1);
+ if (s.mPacketsLost.WasPassed() && s.mPacketsReceived.WasPassed() &&
+ (s.mPacketsLost.Value() + s.mPacketsReceived.Value()) != 0) {
+ ID id;
+ if (s.mIsRemote) {
+ id = isAudio ? WEBRTC_AUDIO_QUALITY_OUTBOUND_PACKETLOSS_RATE :
+ WEBRTC_VIDEO_QUALITY_OUTBOUND_PACKETLOSS_RATE;
+ } else {
+ id = isAudio ? WEBRTC_AUDIO_QUALITY_INBOUND_PACKETLOSS_RATE :
+ WEBRTC_VIDEO_QUALITY_INBOUND_PACKETLOSS_RATE;
+ }
+ // *1000 so we can read in 10's of a percent (permille)
+ Accumulate(id,
+ (s.mPacketsLost.Value() * 1000) /
+ (s.mPacketsLost.Value() + s.mPacketsReceived.Value()));
+ }
+ if (s.mJitter.WasPassed()) {
+ ID id;
+ if (s.mIsRemote) {
+ id = isAudio ? WEBRTC_AUDIO_QUALITY_OUTBOUND_JITTER :
+ WEBRTC_VIDEO_QUALITY_OUTBOUND_JITTER;
+ } else {
+ id = isAudio ? WEBRTC_AUDIO_QUALITY_INBOUND_JITTER :
+ WEBRTC_VIDEO_QUALITY_INBOUND_JITTER;
+ }
+ Accumulate(id, s.mJitter.Value());
+ }
+ if (s.mMozRtt.WasPassed()) {
+ MOZ_ASSERT(s.mIsRemote);
+ ID id = isAudio ? WEBRTC_AUDIO_QUALITY_OUTBOUND_RTT :
+ WEBRTC_VIDEO_QUALITY_OUTBOUND_RTT;
+ Accumulate(id, s.mMozRtt.Value());
+ }
+ if (lastInboundStats && s.mBytesReceived.WasPassed()) {
+ auto& laststats = *lastInboundStats;
+ auto i = FindId(laststats, s.mId.Value());
+ if (i != laststats.NoIndex) {
+ auto& lasts = laststats[i];
+ if (lasts.mBytesReceived.WasPassed()) {
+ auto delta_ms = int32_t(s.mTimestamp.Value() -
+ lasts.mTimestamp.Value());
+ // In theory we're called every second, so delta *should* be in that range.
+ // Small deltas could cause errors due to division
+ if (delta_ms > 500 && delta_ms < 60000) {
+ ID id;
+ if (s.mIsRemote) {
+ id = isAudio ? WEBRTC_AUDIO_QUALITY_OUTBOUND_BANDWIDTH_KBITS :
+ WEBRTC_VIDEO_QUALITY_OUTBOUND_BANDWIDTH_KBITS;
+ } else {
+ id = isAudio ? WEBRTC_AUDIO_QUALITY_INBOUND_BANDWIDTH_KBITS :
+ WEBRTC_VIDEO_QUALITY_INBOUND_BANDWIDTH_KBITS;
+ }
+ Accumulate(id, ((s.mBytesReceived.Value() -
+ lasts.mBytesReceived.Value()) * 8) / delta_ms);
+ }
+ // We could accumulate values until enough time has passed
+ // and then Accumulate() but this isn't that important.
+ }
+ }
+ }
+ }
+ }
+ }
+ // Steal and hang on to reports for the next second
+ ctx->mLastReports.Clear();
+ for (auto q = aQueryList->begin(); q != aQueryList->end(); ++q) {
+ ctx->mLastReports.AppendElement((*q)->report.forget()); // steal avoids copy
+ }
+ // Container must be freed back on main thread
+ NS_DispatchToMainThread(WrapRunnableNM(&FreeOnMain_m, aQueryList),
+ NS_DISPATCH_NORMAL);
+}
+
+void
+PeerConnectionCtx::EverySecondTelemetryCallback_m(nsITimer* timer, void *closure) {
+ MOZ_ASSERT(NS_IsMainThread());
+ MOZ_ASSERT(PeerConnectionCtx::isActive());
+ auto ctx = static_cast<PeerConnectionCtx*>(closure);
+ if (ctx->mPeerConnections.empty()) {
+ return;
+ }
+ nsresult rv;
+ nsCOMPtr<nsIEventTarget> stsThread =
+ do_GetService(NS_SOCKETTRANSPORTSERVICE_CONTRACTID, &rv);
+ if (NS_FAILED(rv)) {
+ return;
+ }
+ MOZ_ASSERT(stsThread);
+
+ nsAutoPtr<RTCStatsQueries> queries(new RTCStatsQueries);
+ for (auto p = ctx->mPeerConnections.begin();
+ p != ctx->mPeerConnections.end(); ++p) {
+ if (p->second->HasMedia()) {
+ if (!queries->append(nsAutoPtr<RTCStatsQuery>(new RTCStatsQuery(true)))) {
+ return;
+ }
+ if (NS_WARN_IF(NS_FAILED(p->second->BuildStatsQuery_m(nullptr, // all tracks
+ queries->back())))) {
+ queries->popBack();
+ } else {
+ MOZ_ASSERT(queries->back()->report);
+ }
+ }
+ }
+ if (!queries->empty()) {
+ rv = RUN_ON_THREAD(stsThread,
+ WrapRunnableNM(&EverySecondTelemetryCallback_s, queries),
+ NS_DISPATCH_NORMAL);
+ NS_ENSURE_SUCCESS_VOID(rv);
+ }
+}
+#endif
+
+nsresult PeerConnectionCtx::Initialize() {
+ initGMP();
+
+#if !defined(MOZILLA_EXTERNAL_LINKAGE)
+ mTelemetryTimer = do_CreateInstance(NS_TIMER_CONTRACTID);
+ MOZ_ASSERT(mTelemetryTimer);
+ nsresult rv = mTelemetryTimer->SetTarget(gMainThread);
+ NS_ENSURE_SUCCESS(rv, rv);
+ mTelemetryTimer->InitWithFuncCallback(EverySecondTelemetryCallback_m, this, 1000,
+ nsITimer::TYPE_REPEATING_PRECISE_CAN_SKIP);
+
+ if (XRE_IsContentProcess()) {
+ WebrtcGlobalChild::Create();
+ }
+#endif // MOZILLA_INTERNAL_API
+
+ return NS_OK;
+}
+
+static void GMPReady_m() {
+ if (PeerConnectionCtx::isActive()) {
+ PeerConnectionCtx::GetInstance()->onGMPReady();
+ }
+};
+
+static void GMPReady() {
+ PeerConnectionCtx::gMainThread->Dispatch(WrapRunnableNM(&GMPReady_m),
+ NS_DISPATCH_NORMAL);
+};
+
+void PeerConnectionCtx::initGMP()
+{
+ mGMPService = do_GetService("@mozilla.org/gecko-media-plugin-service;1");
+
+ if (!mGMPService) {
+ CSFLogError(logTag, "%s failed to get the gecko-media-plugin-service",
+ __FUNCTION__);
+ return;
+ }
+
+ nsCOMPtr<nsIThread> thread;
+ nsresult rv = mGMPService->GetThread(getter_AddRefs(thread));
+
+ if (NS_FAILED(rv)) {
+ mGMPService = nullptr;
+ CSFLogError(logTag,
+ "%s failed to get the gecko-media-plugin thread, err=%u",
+ __FUNCTION__,
+ static_cast<unsigned>(rv));
+ return;
+ }
+
+ // presumes that all GMP dir scans have been queued for the GMPThread
+ thread->Dispatch(WrapRunnableNM(&GMPReady), NS_DISPATCH_NORMAL);
+}
+
+nsresult PeerConnectionCtx::Cleanup() {
+ CSFLogDebug(logTag, "%s", __FUNCTION__);
+
+ mQueuedJSEPOperations.Clear();
+ mGMPService = nullptr;
+ return NS_OK;
+}
+
+PeerConnectionCtx::~PeerConnectionCtx() {
+ // ensure mTelemetryTimer ends on main thread
+ MOZ_ASSERT(NS_IsMainThread());
+#if !defined(MOZILLA_EXTERNAL_LINKAGE)
+ if (mTelemetryTimer) {
+ mTelemetryTimer->Cancel();
+ }
+#endif
+};
+
+void PeerConnectionCtx::queueJSEPOperation(nsIRunnable* aOperation) {
+ mQueuedJSEPOperations.AppendElement(aOperation);
+}
+
+void PeerConnectionCtx::onGMPReady() {
+ mGMPReady = true;
+ for (size_t i = 0; i < mQueuedJSEPOperations.Length(); ++i) {
+ mQueuedJSEPOperations[i]->Run();
+ }
+ mQueuedJSEPOperations.Clear();
+}
+
+bool PeerConnectionCtx::gmpHasH264() {
+ if (!mGMPService) {
+ return false;
+ }
+
+ // XXX I'd prefer if this was all known ahead of time...
+
+ nsTArray<nsCString> tags;
+ tags.AppendElement(NS_LITERAL_CSTRING("h264"));
+
+ bool has_gmp;
+ nsresult rv;
+ rv = mGMPService->HasPluginForAPI(NS_LITERAL_CSTRING(GMP_API_VIDEO_ENCODER),
+ &tags,
+ &has_gmp);
+ if (NS_FAILED(rv) || !has_gmp) {
+ return false;
+ }
+
+ rv = mGMPService->HasPluginForAPI(NS_LITERAL_CSTRING(GMP_API_VIDEO_DECODER),
+ &tags,
+ &has_gmp);
+ if (NS_FAILED(rv) || !has_gmp) {
+ return false;
+ }
+
+ return true;
+}
+
+} // namespace mozilla