diff -r 000000000000 -r 6474c204b198 media/webrtc/signaling/src/peerconnection/WebrtcGlobalInformation.cpp --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/media/webrtc/signaling/src/peerconnection/WebrtcGlobalInformation.cpp Wed Dec 31 06:09:35 2014 +0100 @@ -0,0 +1,371 @@ +/* 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 +#include + +#include "CSFLog.h" + +#include "mozilla/dom/WebrtcGlobalInformationBinding.h" + +#include "nsAutoPtr.h" +#include "nsNetCID.h" // NS_SOCKETTRANSPORTSERVICE_CONTRACTID +#include "nsServiceManagerUtils.h" // do_GetService +#include "mozilla/ErrorResult.h" +#include "mozilla/Vector.h" +#include "nsProxyRelease.h" +#include "mozilla/Telemetry.h" + +#include "rlogringbuffer.h" +#include "runnable_utils.h" +#include "PeerConnectionCtx.h" +#include "PeerConnectionImpl.h" + +using sipcc::PeerConnectionImpl; +using sipcc::PeerConnectionCtx; +using sipcc::RTCStatsQuery; + +static const char* logTag = "WebrtcGlobalInformation"; + +namespace mozilla { +namespace dom { + +typedef Vector> RTCStatsQueries; + +static void OnStatsReport_m( + nsMainThreadPtrHandle aStatsCallback, + nsAutoPtr aQueryList) +{ + MOZ_ASSERT(NS_IsMainThread()); + MOZ_ASSERT(aQueryList); + + WebrtcGlobalStatisticsReport report; + report.mReports.Construct(); + for (auto q = aQueryList->begin(); q != aQueryList->end(); ++q) { + MOZ_ASSERT(*q); + report.mReports.Value().AppendElement((*q)->report); + } + + ErrorResult rv; + aStatsCallback.get()->Call(report, rv); + + if (rv.Failed()) { + CSFLogError(logTag, "Error firing stats observer callback"); + } +} + +static void GetAllStats_s( + nsMainThreadPtrHandle aStatsCallback, + nsAutoPtr aQueryList) +{ + MOZ_ASSERT(aQueryList); + + for (auto q = aQueryList->begin(); q != aQueryList->end(); ++q) { + MOZ_ASSERT(*q); + PeerConnectionImpl::ExecuteStatsQuery_s(*q); + } + + NS_DispatchToMainThread(WrapRunnableNM(&OnStatsReport_m, + aStatsCallback, + aQueryList), + NS_DISPATCH_NORMAL); +} + +static void OnGetLogging_m( + nsMainThreadPtrHandle aLoggingCallback, + const std::string& aPattern, + nsAutoPtr> aLogList) +{ + ErrorResult rv; + if (!aLogList->empty()) { + Sequence nsLogs; + for (auto l = aLogList->begin(); l != aLogList->end(); ++l) { + nsLogs.AppendElement(NS_ConvertUTF8toUTF16(l->c_str())); + } + aLoggingCallback.get()->Call(nsLogs, rv); + } + + if (rv.Failed()) { + CSFLogError(logTag, "Error firing logging observer callback"); + } +} + +static void GetLogging_s( + nsMainThreadPtrHandle aLoggingCallback, + const std::string& aPattern) +{ + RLogRingBuffer* logs = RLogRingBuffer::GetInstance(); + nsAutoPtr> result(new std::deque); + // Might not exist yet. + if (logs) { + logs->Filter(aPattern, 0, result); + } + NS_DispatchToMainThread(WrapRunnableNM(&OnGetLogging_m, + aLoggingCallback, + aPattern, + result), + NS_DISPATCH_NORMAL); +} + + +void +WebrtcGlobalInformation::GetAllStats( + const GlobalObject& aGlobal, + WebrtcGlobalStatisticsCallback& aStatsCallback, + ErrorResult& aRv) +{ + if (!NS_IsMainThread()) { + aRv.Throw(NS_ERROR_NOT_SAME_THREAD); + return; + } + + nsresult rv; + nsCOMPtr stsThread = + do_GetService(NS_SOCKETTRANSPORTSERVICE_CONTRACTID, &rv); + + if (NS_FAILED(rv)) { + aRv.Throw(rv); + return; + } + + if (!stsThread) { + aRv.Throw(NS_ERROR_UNEXPECTED); + return; + } + + nsAutoPtr queries(new RTCStatsQueries); + + // If there is no PeerConnectionCtx, go through the same motions, since + // the API consumer doesn't care why there are no PeerConnectionImpl. + if (PeerConnectionCtx::isActive()) { + PeerConnectionCtx *ctx = PeerConnectionCtx::GetInstance(); + MOZ_ASSERT(ctx); + for (auto p = ctx->mPeerConnections.begin(); + p != ctx->mPeerConnections.end(); + ++p) { + MOZ_ASSERT(p->second); + + if (p->second->HasMedia()) { + queries->append(nsAutoPtr(new RTCStatsQuery(true))); + p->second->BuildStatsQuery_m(nullptr, // all tracks + queries->back()); + } + } + } + + // CallbackObject does not support threadsafe refcounting, and must be + // destroyed on main. + nsMainThreadPtrHandle callbackHandle( + new nsMainThreadPtrHolder(&aStatsCallback)); + + rv = RUN_ON_THREAD(stsThread, + WrapRunnableNM(&GetAllStats_s, callbackHandle, queries), + NS_DISPATCH_NORMAL); + + aRv = rv; +} + +void +WebrtcGlobalInformation::GetLogging( + const GlobalObject& aGlobal, + const nsAString& aPattern, + WebrtcGlobalLoggingCallback& aLoggingCallback, + ErrorResult& aRv) +{ + if (!NS_IsMainThread()) { + aRv.Throw(NS_ERROR_NOT_SAME_THREAD); + return; + } + + nsresult rv; + nsCOMPtr stsThread = + do_GetService(NS_SOCKETTRANSPORTSERVICE_CONTRACTID, &rv); + + if (NS_FAILED(rv)) { + aRv.Throw(rv); + return; + } + + if (!stsThread) { + aRv.Throw(NS_ERROR_UNEXPECTED); + return; + } + + std::string pattern(NS_ConvertUTF16toUTF8(aPattern).get()); + + // CallbackObject does not support threadsafe refcounting, and must be + // destroyed on main. + nsMainThreadPtrHandle callbackHandle( + new nsMainThreadPtrHolder(&aLoggingCallback)); + + rv = RUN_ON_THREAD(stsThread, + WrapRunnableNM(&GetLogging_s, callbackHandle, pattern), + NS_DISPATCH_NORMAL); + + if (NS_FAILED(rv)) { + aLoggingCallback.Release(); + } + + aRv = rv; +} + +struct StreamResult { + StreamResult() : candidateTypeBitpattern(0), streamSucceeded(false) {} + uint8_t candidateTypeBitpattern; + bool streamSucceeded; +}; + +static void StoreLongTermICEStatisticsImpl_m( + nsresult result, + nsAutoPtr query) { + + if (NS_FAILED(result) || + !query->error.empty() || + !query->report.mIceCandidateStats.WasPassed()) { + return; + } + + // First, store stuff in telemetry + enum { + REMOTE_GATHERED_SERVER_REFLEXIVE = 1, + REMOTE_GATHERED_TURN = 1 << 1, + LOCAL_GATHERED_SERVER_REFLEXIVE = 1 << 2, + LOCAL_GATHERED_TURN_UDP = 1 << 3, + LOCAL_GATHERED_TURN_TCP = 1 << 4, + LOCAL_GATHERED_TURN_TLS = 1 << 5, + LOCAL_GATHERED_TURN_HTTPS = 1 << 6, + }; + + // TODO(bcampen@mozilla.com): Do we need to watch out for cases where the + // components within a stream didn't have the same types of relayed + // candidates? I have a feeling that late trickle could cause this, but right + // now we don't have enough information to detect it (we would need to know + // the ICE component id for each candidate pair and candidate) + + std::map streamResults; + + // Build list of streams, and whether or not they failed. + for (size_t i = 0; + i < query->report.mIceCandidatePairStats.Value().Length(); + ++i) { + const RTCIceCandidatePairStats &pair = + query->report.mIceCandidatePairStats.Value()[i]; + + if (!pair.mState.WasPassed() || !pair.mComponentId.WasPassed()) { + MOZ_CRASH(); + continue; + } + + // Note: this is not a "component" in the ICE definition, this is really a + // stream ID. This is just the way the stats API is standardized right now. + // Very confusing. + std::string streamId( + NS_ConvertUTF16toUTF8(pair.mComponentId.Value()).get()); + + streamResults[streamId].streamSucceeded |= + pair.mState.Value() == RTCStatsIceCandidatePairState::Succeeded; + } + + for (size_t i = 0; + i < query->report.mIceCandidateStats.Value().Length(); + ++i) { + const RTCIceCandidateStats &cand = + query->report.mIceCandidateStats.Value()[i]; + + if (!cand.mType.WasPassed() || + !cand.mCandidateType.WasPassed() || + !cand.mComponentId.WasPassed()) { + // Crash on debug, ignore this candidate otherwise. + MOZ_CRASH(); + continue; + } + + // Note: this is not a "component" in the ICE definition, this is really a + // stream ID. This is just the way the stats API is standardized right now + // Very confusing. + std::string streamId( + NS_ConvertUTF16toUTF8(cand.mComponentId.Value()).get()); + + if (cand.mCandidateType.Value() == RTCStatsIceCandidateType::Relayed) { + if (cand.mType.Value() == RTCStatsType::Localcandidate) { + NS_ConvertUTF16toUTF8 transport(cand.mMozLocalTransport.Value()); + if (transport == kNrIceTransportUdp) { + streamResults[streamId].candidateTypeBitpattern |= + LOCAL_GATHERED_TURN_UDP; + } else if (transport == kNrIceTransportTcp) { + streamResults[streamId].candidateTypeBitpattern |= + LOCAL_GATHERED_TURN_TCP; + } + } else { + streamResults[streamId].candidateTypeBitpattern |= REMOTE_GATHERED_TURN; + } + } else if (cand.mCandidateType.Value() == + RTCStatsIceCandidateType::Serverreflexive) { + if (cand.mType.Value() == RTCStatsType::Localcandidate) { + streamResults[streamId].candidateTypeBitpattern |= + LOCAL_GATHERED_SERVER_REFLEXIVE; + } else { + streamResults[streamId].candidateTypeBitpattern |= + REMOTE_GATHERED_SERVER_REFLEXIVE; + } + } + } + + for (auto i = streamResults.begin(); i != streamResults.end(); ++i) { + if (i->second.streamSucceeded) { + Telemetry::Accumulate(Telemetry::WEBRTC_CANDIDATE_TYPES_GIVEN_SUCCESS, + i->second.candidateTypeBitpattern); + } else { + Telemetry::Accumulate(Telemetry::WEBRTC_CANDIDATE_TYPES_GIVEN_FAILURE, + i->second.candidateTypeBitpattern); + } + } +} + +static void GetStatsForLongTermStorage_s( + nsAutoPtr query) { + + MOZ_ASSERT(query); + + nsresult rv = PeerConnectionImpl::ExecuteStatsQuery_s(query.get()); + + // Even if Telemetry::Accumulate is threadsafe, we still need to send the + // query back to main, since that is where it must be destroyed. + NS_DispatchToMainThread( + WrapRunnableNM( + &StoreLongTermICEStatisticsImpl_m, + rv, + query), + NS_DISPATCH_NORMAL); +} + +void WebrtcGlobalInformation::StoreLongTermICEStatistics( + sipcc::PeerConnectionImpl& aPc) { + Telemetry::Accumulate(Telemetry::WEBRTC_ICE_FINAL_CONNECTION_STATE, + static_cast(aPc.IceConnectionState())); + + if (aPc.IceConnectionState() == PCImplIceConnectionState::New) { + // ICE has not started; we won't have any remote candidates, so recording + // statistics on gathered candidates is pointless. + return; + } + + nsAutoPtr query(new RTCStatsQuery(true)); + + nsresult rv = aPc.BuildStatsQuery_m(nullptr, query.get()); + + NS_ENSURE_SUCCESS_VOID(rv); + + RUN_ON_THREAD(aPc.GetSTSThread(), + WrapRunnableNM(&GetStatsForLongTermStorage_s, + query), + NS_DISPATCH_NORMAL); +} + + +} // namespace dom +} // namespace mozilla +