LCOV - code coverage report
Current view: top level - media/webrtc/signaling/src/peerconnection - PeerConnectionCtx.cpp (source / functions) Hit Total Coverage
Test: output.info Lines: 2 239 0.8 %
Date: 2017-07-14 16:53:18 Functions: 2 31 6.5 %
Legend: Lines: hit not hit

          Line data    Source code
       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
       3             :  * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
       4             : 
       5             : #include "CSFLog.h"
       6             : 
       7             : #include "PeerConnectionImpl.h"
       8             : #include "PeerConnectionCtx.h"
       9             : #include "runnable_utils.h"
      10             : #include "prcvar.h"
      11             : 
      12             : #include "mozilla/Telemetry.h"
      13             : #include "browser_logging/WebRtcLog.h"
      14             : 
      15             : #include "mozilla/dom/RTCPeerConnectionBinding.h"
      16             : #include "mozilla/Preferences.h"
      17             : #include <mozilla/Types.h>
      18             : 
      19             : #include "nsNetCID.h" // NS_SOCKETTRANSPORTSERVICE_CONTRACTID
      20             : #include "nsServiceManagerUtils.h" // do_GetService
      21             : #include "nsIObserverService.h"
      22             : #include "nsIObserver.h"
      23             : #include "nsIIOService.h" // NS_IOSERVICE_*
      24             : #include "mozilla/Services.h"
      25             : #include "mozilla/StaticPtr.h"
      26             : 
      27             : #include "nsCRTGlue.h"
      28             : 
      29             : #include "gmp-video-decode.h" // GMP_API_VIDEO_DECODER
      30             : #include "gmp-video-encode.h" // GMP_API_VIDEO_ENCODER
      31             : 
      32             : static const char* logTag = "PeerConnectionCtx";
      33             : 
      34             : namespace mozilla {
      35             : 
      36             : using namespace dom;
      37             : 
      38             : class PeerConnectionCtxObserver : public nsIObserver
      39             : {
      40             : public:
      41             :   NS_DECL_ISUPPORTS
      42             : 
      43           0 :   PeerConnectionCtxObserver() {}
      44             : 
      45           0 :   void Init()
      46             :     {
      47             :       nsCOMPtr<nsIObserverService> observerService =
      48           0 :         services::GetObserverService();
      49           0 :       if (!observerService)
      50           0 :         return;
      51             : 
      52           0 :       nsresult rv = NS_OK;
      53             : 
      54           0 :       rv = observerService->AddObserver(this,
      55             :                                         NS_XPCOM_SHUTDOWN_OBSERVER_ID,
      56           0 :                                         false);
      57           0 :       MOZ_ALWAYS_SUCCEEDS(rv);
      58           0 :       rv = observerService->AddObserver(this,
      59             :                                         NS_IOSERVICE_OFFLINE_STATUS_TOPIC,
      60           0 :                                         false);
      61           0 :       MOZ_ALWAYS_SUCCEEDS(rv);
      62             :       (void) rv;
      63             :     }
      64             : 
      65           0 :   NS_IMETHOD Observe(nsISupports* aSubject, const char* aTopic,
      66             :                      const char16_t* aData) override {
      67           0 :     if (strcmp(aTopic, NS_XPCOM_SHUTDOWN_OBSERVER_ID) == 0) {
      68           0 :       CSFLogDebug(logTag, "Shutting down PeerConnectionCtx");
      69           0 :       PeerConnectionCtx::Destroy();
      70             : 
      71             :       nsCOMPtr<nsIObserverService> observerService =
      72           0 :         services::GetObserverService();
      73           0 :       if (!observerService)
      74           0 :         return NS_ERROR_FAILURE;
      75             : 
      76           0 :       nsresult rv = observerService->RemoveObserver(this,
      77           0 :                                            NS_IOSERVICE_OFFLINE_STATUS_TOPIC);
      78           0 :       MOZ_ALWAYS_SUCCEEDS(rv);
      79           0 :       rv = observerService->RemoveObserver(this,
      80           0 :                                            NS_XPCOM_SHUTDOWN_OBSERVER_ID);
      81           0 :       MOZ_ALWAYS_SUCCEEDS(rv);
      82             : 
      83             :       // Make sure we're not deleted while still inside ::Observe()
      84           0 :       RefPtr<PeerConnectionCtxObserver> kungFuDeathGrip(this);
      85           0 :       PeerConnectionCtx::gPeerConnectionCtxObserver = nullptr;
      86             :     }
      87           0 :     if (strcmp(aTopic, NS_IOSERVICE_OFFLINE_STATUS_TOPIC) == 0) {
      88           0 :       if (NS_strcmp(aData, u"" NS_IOSERVICE_OFFLINE) == 0) {
      89           0 :         CSFLogDebug(logTag, "Updating network state to offline");
      90           0 :         PeerConnectionCtx::UpdateNetworkState(false);
      91           0 :       } else if(NS_strcmp(aData, u"" NS_IOSERVICE_ONLINE) == 0) {
      92           0 :         CSFLogDebug(logTag, "Updating network state to online");
      93           0 :         PeerConnectionCtx::UpdateNetworkState(true);
      94             :       } else {
      95           0 :         CSFLogDebug(logTag, "Received unsupported network state event");
      96           0 :         MOZ_CRASH();
      97             :       }
      98             :     }
      99           0 :     return NS_OK;
     100             :   }
     101             : 
     102             : private:
     103           0 :   virtual ~PeerConnectionCtxObserver()
     104           0 :     {
     105             :       nsCOMPtr<nsIObserverService> observerService =
     106           0 :         services::GetObserverService();
     107           0 :       if (observerService) {
     108           0 :         observerService->RemoveObserver(this, NS_IOSERVICE_OFFLINE_STATUS_TOPIC);
     109           0 :         observerService->RemoveObserver(this, NS_XPCOM_SHUTDOWN_OBSERVER_ID);
     110             :       }
     111           0 :     }
     112             : };
     113             : 
     114           0 : NS_IMPL_ISUPPORTS(PeerConnectionCtxObserver, nsIObserver);
     115             : }
     116             : 
     117             : namespace mozilla {
     118             : 
     119             : PeerConnectionCtx* PeerConnectionCtx::gInstance;
     120             : nsIThread* PeerConnectionCtx::gMainThread;
     121           3 : StaticRefPtr<PeerConnectionCtxObserver> PeerConnectionCtx::gPeerConnectionCtxObserver;
     122             : 
     123             : const std::map<const std::string, PeerConnectionImpl *>&
     124           0 : PeerConnectionCtx::mGetPeerConnections()
     125             : {
     126           0 :   return mPeerConnections;
     127             : }
     128             : 
     129           0 : nsresult PeerConnectionCtx::InitializeGlobal(nsIThread *mainThread,
     130             :   nsIEventTarget* stsThread) {
     131           0 :   if (!gMainThread) {
     132           0 :     gMainThread = mainThread;
     133             :   } else {
     134           0 :     MOZ_ASSERT(gMainThread == mainThread);
     135             :   }
     136             : 
     137             :   nsresult res;
     138             : 
     139           0 :   MOZ_ASSERT(NS_IsMainThread());
     140             : 
     141           0 :   if (!gInstance) {
     142           0 :     CSFLogDebug(logTag, "Creating PeerConnectionCtx");
     143           0 :     PeerConnectionCtx *ctx = new PeerConnectionCtx();
     144             : 
     145           0 :     res = ctx->Initialize();
     146           0 :     PR_ASSERT(NS_SUCCEEDED(res));
     147           0 :     if (!NS_SUCCEEDED(res))
     148           0 :       return res;
     149             : 
     150           0 :     gInstance = ctx;
     151             : 
     152           0 :     if (!PeerConnectionCtx::gPeerConnectionCtxObserver) {
     153           0 :       PeerConnectionCtx::gPeerConnectionCtxObserver = new PeerConnectionCtxObserver();
     154           0 :       PeerConnectionCtx::gPeerConnectionCtxObserver->Init();
     155             :     }
     156             :   }
     157             : 
     158           0 :   EnableWebRtcLog();
     159           0 :   return NS_OK;
     160             : }
     161             : 
     162           0 : PeerConnectionCtx* PeerConnectionCtx::GetInstance() {
     163           0 :   MOZ_ASSERT(gInstance);
     164           0 :   return gInstance;
     165             : }
     166             : 
     167           0 : bool PeerConnectionCtx::isActive() {
     168           0 :   return gInstance;
     169             : }
     170             : 
     171           0 : void PeerConnectionCtx::Destroy() {
     172           0 :   CSFLogDebug(logTag, "%s", __FUNCTION__);
     173             : 
     174           0 :   if (gInstance) {
     175           0 :     gInstance->Cleanup();
     176           0 :     delete gInstance;
     177           0 :     gInstance = nullptr;
     178             :   }
     179             : 
     180           0 :   StopWebRtcLog();
     181           0 : }
     182             : 
     183             : typedef Vector<nsAutoPtr<RTCStatsQuery>> RTCStatsQueries;
     184             : 
     185             : // Telemetry reporting every second after start of first call.
     186             : // The threading model around the media pipelines is weird:
     187             : // - The pipelines are containers,
     188             : // - containers that are only safe on main thread, with members only safe on STS,
     189             : // - hence the there and back again approach.
     190             : 
     191             : static auto
     192           0 : FindId(const Sequence<RTCInboundRTPStreamStats>& aArray,
     193             :        const nsString &aId) -> decltype(aArray.Length()) {
     194           0 :   for (decltype(aArray.Length()) i = 0; i < aArray.Length(); i++) {
     195           0 :     if (aArray[i].mId.Value() == aId) {
     196           0 :       return i;
     197             :     }
     198             :   }
     199           0 :   return aArray.NoIndex;
     200             : }
     201             : 
     202             : static auto
     203           0 : FindId(const nsTArray<nsAutoPtr<RTCStatsReportInternal>>& aArray,
     204             :        const nsString &aId) -> decltype(aArray.Length()) {
     205           0 :   for (decltype(aArray.Length()) i = 0; i < aArray.Length(); i++) {
     206           0 :     if (aArray[i]->mPcid == aId) {
     207           0 :       return i;
     208             :     }
     209             :   }
     210           0 :   return aArray.NoIndex;
     211             : }
     212             : 
     213             : static void
     214           0 : FreeOnMain_m(nsAutoPtr<RTCStatsQueries> aQueryList) {
     215           0 :   MOZ_ASSERT(NS_IsMainThread());
     216           0 : }
     217             : 
     218             : static void
     219           0 : EverySecondTelemetryCallback_s(nsAutoPtr<RTCStatsQueries> aQueryList) {
     220             :   using namespace Telemetry;
     221             : 
     222           0 :   if(!PeerConnectionCtx::isActive()) {
     223           0 :     return;
     224             :   }
     225           0 :   PeerConnectionCtx *ctx = PeerConnectionCtx::GetInstance();
     226             : 
     227           0 :   for (auto & q : *aQueryList) {
     228           0 :     PeerConnectionImpl::ExecuteStatsQuery_s(q);
     229           0 :     auto& r = *q->report;
     230           0 :     if (r.mInboundRTPStreamStats.WasPassed()) {
     231             :       // First, get reports from a second ago, if any, for calculations below
     232           0 :       const Sequence<RTCInboundRTPStreamStats> *lastInboundStats = nullptr;
     233             :       {
     234           0 :         auto i = FindId(ctx->mLastReports, r.mPcid);
     235           0 :         if (i != ctx->mLastReports.NoIndex) {
     236           0 :           lastInboundStats = &ctx->mLastReports[i]->mInboundRTPStreamStats.Value();
     237             :         }
     238             :       }
     239             :       // Then, look for the things we want telemetry on
     240           0 :       auto& array = r.mInboundRTPStreamStats.Value();
     241           0 :       for (decltype(array.Length()) i = 0; i < array.Length(); i++) {
     242           0 :         auto& s = array[i];
     243           0 :         bool isAudio = (s.mId.Value().Find("audio") != -1);
     244           0 :         if (s.mPacketsLost.WasPassed() && s.mPacketsReceived.WasPassed() &&
     245           0 :             (s.mPacketsLost.Value() + s.mPacketsReceived.Value()) != 0) {
     246             :           HistogramID id;
     247           0 :           if (s.mIsRemote) {
     248           0 :             id = isAudio ? WEBRTC_AUDIO_QUALITY_OUTBOUND_PACKETLOSS_RATE :
     249             :                            WEBRTC_VIDEO_QUALITY_OUTBOUND_PACKETLOSS_RATE;
     250             :           } else {
     251           0 :             id = isAudio ? WEBRTC_AUDIO_QUALITY_INBOUND_PACKETLOSS_RATE :
     252             :                            WEBRTC_VIDEO_QUALITY_INBOUND_PACKETLOSS_RATE;
     253             :           }
     254             :           // *1000 so we can read in 10's of a percent (permille)
     255           0 :           Accumulate(id,
     256           0 :                      (s.mPacketsLost.Value() * 1000) /
     257           0 :                      (s.mPacketsLost.Value() + s.mPacketsReceived.Value()));
     258             :         }
     259           0 :         if (s.mJitter.WasPassed()) {
     260             :           HistogramID id;
     261           0 :           if (s.mIsRemote) {
     262           0 :             id = isAudio ? WEBRTC_AUDIO_QUALITY_OUTBOUND_JITTER :
     263             :                            WEBRTC_VIDEO_QUALITY_OUTBOUND_JITTER;
     264             :           } else {
     265           0 :             id = isAudio ? WEBRTC_AUDIO_QUALITY_INBOUND_JITTER :
     266             :                            WEBRTC_VIDEO_QUALITY_INBOUND_JITTER;
     267             :           }
     268           0 :           Accumulate(id, s.mJitter.Value());
     269             :         }
     270           0 :         if (s.mRoundTripTime.WasPassed()) {
     271           0 :           MOZ_ASSERT(s.mIsRemote);
     272           0 :           HistogramID id = isAudio ? WEBRTC_AUDIO_QUALITY_OUTBOUND_RTT :
     273           0 :                                      WEBRTC_VIDEO_QUALITY_OUTBOUND_RTT;
     274           0 :           Accumulate(id, s.mRoundTripTime.Value());
     275             :         }
     276           0 :         if (lastInboundStats && s.mBytesReceived.WasPassed()) {
     277           0 :           auto& laststats = *lastInboundStats;
     278           0 :           auto i = FindId(laststats, s.mId.Value());
     279           0 :           if (i != laststats.NoIndex) {
     280           0 :             auto& lasts = laststats[i];
     281           0 :             if (lasts.mBytesReceived.WasPassed()) {
     282           0 :               auto delta_ms = int32_t(s.mTimestamp.Value() -
     283           0 :                                       lasts.mTimestamp.Value());
     284             :               // In theory we're called every second, so delta *should* be in that range.
     285             :               // Small deltas could cause errors due to division
     286           0 :               if (delta_ms > 500 && delta_ms < 60000) {
     287             :                 HistogramID id;
     288           0 :                 if (s.mIsRemote) {
     289           0 :                   id = isAudio ? WEBRTC_AUDIO_QUALITY_OUTBOUND_BANDWIDTH_KBITS :
     290             :                                  WEBRTC_VIDEO_QUALITY_OUTBOUND_BANDWIDTH_KBITS;
     291             :                 } else {
     292           0 :                   id = isAudio ? WEBRTC_AUDIO_QUALITY_INBOUND_BANDWIDTH_KBITS :
     293             :                                  WEBRTC_VIDEO_QUALITY_INBOUND_BANDWIDTH_KBITS;
     294             :                 }
     295           0 :                 Accumulate(id, ((s.mBytesReceived.Value() -
     296           0 :                                  lasts.mBytesReceived.Value()) * 8) / delta_ms);
     297             :               }
     298             :               // We could accumulate values until enough time has passed
     299             :               // and then Accumulate() but this isn't that important.
     300             :             }
     301             :           }
     302             :         }
     303             :       }
     304             :     }
     305             :   }
     306             :   // Steal and hang on to reports for the next second
     307           0 :   ctx->mLastReports.Clear();
     308           0 :   for (auto & q : *aQueryList) {
     309           0 :     ctx->mLastReports.AppendElement(q->report.forget()); // steal avoids copy
     310             :   }
     311             :   // Container must be freed back on main thread
     312           0 :   NS_DispatchToMainThread(WrapRunnableNM(&FreeOnMain_m, aQueryList),
     313           0 :                           NS_DISPATCH_NORMAL);
     314             : }
     315             : 
     316             : void
     317           0 : PeerConnectionCtx::EverySecondTelemetryCallback_m(nsITimer* timer, void *closure) {
     318           0 :   MOZ_ASSERT(NS_IsMainThread());
     319           0 :   MOZ_ASSERT(PeerConnectionCtx::isActive());
     320           0 :   auto ctx = static_cast<PeerConnectionCtx*>(closure);
     321           0 :   if (ctx->mPeerConnections.empty()) {
     322           0 :     return;
     323             :   }
     324             :   nsresult rv;
     325             :   nsCOMPtr<nsIEventTarget> stsThread =
     326           0 :       do_GetService(NS_SOCKETTRANSPORTSERVICE_CONTRACTID, &rv);
     327           0 :   if (NS_FAILED(rv)) {
     328           0 :     return;
     329             :   }
     330           0 :   MOZ_ASSERT(stsThread);
     331             : 
     332           0 :   nsAutoPtr<RTCStatsQueries> queries(new RTCStatsQueries);
     333           0 :   for (auto p = ctx->mPeerConnections.begin();
     334           0 :         p != ctx->mPeerConnections.end(); ++p) {
     335           0 :     if (p->second->HasMedia()) {
     336           0 :       if (!queries->append(nsAutoPtr<RTCStatsQuery>(new RTCStatsQuery(true)))) {
     337           0 :         return;
     338             :       }
     339           0 :       if (NS_WARN_IF(NS_FAILED(p->second->BuildStatsQuery_m(nullptr, // all tracks
     340             :                                                             queries->back())))) {
     341           0 :         queries->popBack();
     342             :       } else {
     343           0 :         MOZ_ASSERT(queries->back()->report);
     344             :       }
     345             :     }
     346             :   }
     347           0 :   if (!queries->empty()) {
     348           0 :     rv = RUN_ON_THREAD(stsThread,
     349           0 :                        WrapRunnableNM(&EverySecondTelemetryCallback_s, queries),
     350             :                        NS_DISPATCH_NORMAL);
     351           0 :     NS_ENSURE_SUCCESS_VOID(rv);
     352             :   }
     353             : }
     354             : 
     355             : void
     356           0 : PeerConnectionCtx::UpdateNetworkState(bool online) {
     357           0 :   auto ctx = GetInstance();
     358           0 :   if (ctx->mPeerConnections.empty()) {
     359           0 :     return;
     360             :   }
     361           0 :   for (auto pc : ctx->mPeerConnections) {
     362           0 :     pc.second->UpdateNetworkState(online);
     363             :   }
     364             : }
     365             : 
     366           0 : nsresult PeerConnectionCtx::Initialize() {
     367           0 :   initGMP();
     368             : 
     369           0 :   mTelemetryTimer = do_CreateInstance(NS_TIMER_CONTRACTID);
     370           0 :   MOZ_ASSERT(mTelemetryTimer);
     371           0 :   nsresult rv = mTelemetryTimer->SetTarget(gMainThread);
     372           0 :   NS_ENSURE_SUCCESS(rv, rv);
     373           0 :   mTelemetryTimer->InitWithNamedFuncCallback(EverySecondTelemetryCallback_m, this, 1000,
     374             :                                              nsITimer::TYPE_REPEATING_PRECISE_CAN_SKIP,
     375           0 :                                              "EverySecondTelemetryCallback_m");
     376             : 
     377           0 :   if (XRE_IsContentProcess()) {
     378           0 :     WebrtcGlobalChild::Create();
     379             :   }
     380             : 
     381           0 :   return NS_OK;
     382             : }
     383             : 
     384           0 : static void GMPReady_m() {
     385           0 :   if (PeerConnectionCtx::isActive()) {
     386           0 :     PeerConnectionCtx::GetInstance()->onGMPReady();
     387             :   }
     388           0 : };
     389             : 
     390           0 : static void GMPReady() {
     391           0 :   PeerConnectionCtx::gMainThread->Dispatch(WrapRunnableNM(&GMPReady_m),
     392           0 :                                            NS_DISPATCH_NORMAL);
     393           0 : };
     394             : 
     395           0 : void PeerConnectionCtx::initGMP()
     396             : {
     397           0 :   mGMPService = do_GetService("@mozilla.org/gecko-media-plugin-service;1");
     398             : 
     399           0 :   if (!mGMPService) {
     400             :     CSFLogError(logTag, "%s failed to get the gecko-media-plugin-service",
     401           0 :                 __FUNCTION__);
     402           0 :     return;
     403             :   }
     404             : 
     405           0 :   nsCOMPtr<nsIThread> thread;
     406           0 :   nsresult rv = mGMPService->GetThread(getter_AddRefs(thread));
     407             : 
     408           0 :   if (NS_FAILED(rv)) {
     409           0 :     mGMPService = nullptr;
     410             :     CSFLogError(logTag,
     411             :                 "%s failed to get the gecko-media-plugin thread, err=%u",
     412             :                 __FUNCTION__,
     413           0 :                 static_cast<unsigned>(rv));
     414           0 :     return;
     415             :   }
     416             : 
     417             :   // presumes that all GMP dir scans have been queued for the GMPThread
     418           0 :   thread->Dispatch(WrapRunnableNM(&GMPReady), NS_DISPATCH_NORMAL);
     419             : }
     420             : 
     421           0 : nsresult PeerConnectionCtx::Cleanup() {
     422           0 :   CSFLogDebug(logTag, "%s", __FUNCTION__);
     423             : 
     424           0 :   mQueuedJSEPOperations.Clear();
     425           0 :   mGMPService = nullptr;
     426           0 :   return NS_OK;
     427             : }
     428             : 
     429           0 : PeerConnectionCtx::~PeerConnectionCtx() {
     430             :     // ensure mTelemetryTimer ends on main thread
     431           0 :   MOZ_ASSERT(NS_IsMainThread());
     432           0 :   if (mTelemetryTimer) {
     433           0 :     mTelemetryTimer->Cancel();
     434             :   }
     435           0 : };
     436             : 
     437           0 : void PeerConnectionCtx::queueJSEPOperation(nsIRunnable* aOperation) {
     438           0 :   mQueuedJSEPOperations.AppendElement(aOperation);
     439           0 : }
     440             : 
     441           0 : void PeerConnectionCtx::onGMPReady() {
     442           0 :   mGMPReady = true;
     443           0 :   for (size_t i = 0; i < mQueuedJSEPOperations.Length(); ++i) {
     444           0 :     mQueuedJSEPOperations[i]->Run();
     445             :   }
     446           0 :   mQueuedJSEPOperations.Clear();
     447           0 : }
     448             : 
     449           0 : bool PeerConnectionCtx::gmpHasH264() {
     450           0 :   if (!mGMPService) {
     451           0 :     return false;
     452             :   }
     453             : 
     454             :   // XXX I'd prefer if this was all known ahead of time...
     455             : 
     456           0 :   nsTArray<nsCString> tags;
     457           0 :   tags.AppendElement(NS_LITERAL_CSTRING("h264"));
     458             : 
     459             :   bool has_gmp;
     460             :   nsresult rv;
     461           0 :   rv = mGMPService->HasPluginForAPI(NS_LITERAL_CSTRING(GMP_API_VIDEO_ENCODER),
     462             :                                     &tags,
     463           0 :                                     &has_gmp);
     464           0 :   if (NS_FAILED(rv) || !has_gmp) {
     465           0 :     return false;
     466             :   }
     467             : 
     468           0 :   rv = mGMPService->HasPluginForAPI(NS_LITERAL_CSTRING(GMP_API_VIDEO_DECODER),
     469             :                                     &tags,
     470           0 :                                     &has_gmp);
     471           0 :   if (NS_FAILED(rv) || !has_gmp) {
     472           0 :     return false;
     473             :   }
     474             : 
     475           0 :   return true;
     476             : }
     477             : 
     478           9 : } // namespace mozilla

Generated by: LCOV version 1.13