1.1 --- /dev/null Thu Jan 01 00:00:00 1970 +0000 1.2 +++ b/media/webrtc/signaling/src/peerconnection/WebrtcGlobalInformation.cpp Wed Dec 31 06:09:35 2014 +0100 1.3 @@ -0,0 +1,371 @@ 1.4 +/* This Source Code Form is subject to the terms of the Mozilla Public 1.5 + * License, v. 2.0. If a copy of the MPL was not distributed with this file, 1.6 + * You can obtain one at http://mozilla.org/MPL/2.0/. */ 1.7 + 1.8 +#include "WebrtcGlobalInformation.h" 1.9 + 1.10 +#include <deque> 1.11 +#include <string> 1.12 + 1.13 +#include "CSFLog.h" 1.14 + 1.15 +#include "mozilla/dom/WebrtcGlobalInformationBinding.h" 1.16 + 1.17 +#include "nsAutoPtr.h" 1.18 +#include "nsNetCID.h" // NS_SOCKETTRANSPORTSERVICE_CONTRACTID 1.19 +#include "nsServiceManagerUtils.h" // do_GetService 1.20 +#include "mozilla/ErrorResult.h" 1.21 +#include "mozilla/Vector.h" 1.22 +#include "nsProxyRelease.h" 1.23 +#include "mozilla/Telemetry.h" 1.24 + 1.25 +#include "rlogringbuffer.h" 1.26 +#include "runnable_utils.h" 1.27 +#include "PeerConnectionCtx.h" 1.28 +#include "PeerConnectionImpl.h" 1.29 + 1.30 +using sipcc::PeerConnectionImpl; 1.31 +using sipcc::PeerConnectionCtx; 1.32 +using sipcc::RTCStatsQuery; 1.33 + 1.34 +static const char* logTag = "WebrtcGlobalInformation"; 1.35 + 1.36 +namespace mozilla { 1.37 +namespace dom { 1.38 + 1.39 +typedef Vector<nsAutoPtr<RTCStatsQuery>> RTCStatsQueries; 1.40 + 1.41 +static void OnStatsReport_m( 1.42 + nsMainThreadPtrHandle<WebrtcGlobalStatisticsCallback> aStatsCallback, 1.43 + nsAutoPtr<RTCStatsQueries> aQueryList) 1.44 +{ 1.45 + MOZ_ASSERT(NS_IsMainThread()); 1.46 + MOZ_ASSERT(aQueryList); 1.47 + 1.48 + WebrtcGlobalStatisticsReport report; 1.49 + report.mReports.Construct(); 1.50 + for (auto q = aQueryList->begin(); q != aQueryList->end(); ++q) { 1.51 + MOZ_ASSERT(*q); 1.52 + report.mReports.Value().AppendElement((*q)->report); 1.53 + } 1.54 + 1.55 + ErrorResult rv; 1.56 + aStatsCallback.get()->Call(report, rv); 1.57 + 1.58 + if (rv.Failed()) { 1.59 + CSFLogError(logTag, "Error firing stats observer callback"); 1.60 + } 1.61 +} 1.62 + 1.63 +static void GetAllStats_s( 1.64 + nsMainThreadPtrHandle<WebrtcGlobalStatisticsCallback> aStatsCallback, 1.65 + nsAutoPtr<RTCStatsQueries> aQueryList) 1.66 +{ 1.67 + MOZ_ASSERT(aQueryList); 1.68 + 1.69 + for (auto q = aQueryList->begin(); q != aQueryList->end(); ++q) { 1.70 + MOZ_ASSERT(*q); 1.71 + PeerConnectionImpl::ExecuteStatsQuery_s(*q); 1.72 + } 1.73 + 1.74 + NS_DispatchToMainThread(WrapRunnableNM(&OnStatsReport_m, 1.75 + aStatsCallback, 1.76 + aQueryList), 1.77 + NS_DISPATCH_NORMAL); 1.78 +} 1.79 + 1.80 +static void OnGetLogging_m( 1.81 + nsMainThreadPtrHandle<WebrtcGlobalLoggingCallback> aLoggingCallback, 1.82 + const std::string& aPattern, 1.83 + nsAutoPtr<std::deque<std::string>> aLogList) 1.84 +{ 1.85 + ErrorResult rv; 1.86 + if (!aLogList->empty()) { 1.87 + Sequence<nsString> nsLogs; 1.88 + for (auto l = aLogList->begin(); l != aLogList->end(); ++l) { 1.89 + nsLogs.AppendElement(NS_ConvertUTF8toUTF16(l->c_str())); 1.90 + } 1.91 + aLoggingCallback.get()->Call(nsLogs, rv); 1.92 + } 1.93 + 1.94 + if (rv.Failed()) { 1.95 + CSFLogError(logTag, "Error firing logging observer callback"); 1.96 + } 1.97 +} 1.98 + 1.99 +static void GetLogging_s( 1.100 + nsMainThreadPtrHandle<WebrtcGlobalLoggingCallback> aLoggingCallback, 1.101 + const std::string& aPattern) 1.102 +{ 1.103 + RLogRingBuffer* logs = RLogRingBuffer::GetInstance(); 1.104 + nsAutoPtr<std::deque<std::string>> result(new std::deque<std::string>); 1.105 + // Might not exist yet. 1.106 + if (logs) { 1.107 + logs->Filter(aPattern, 0, result); 1.108 + } 1.109 + NS_DispatchToMainThread(WrapRunnableNM(&OnGetLogging_m, 1.110 + aLoggingCallback, 1.111 + aPattern, 1.112 + result), 1.113 + NS_DISPATCH_NORMAL); 1.114 +} 1.115 + 1.116 + 1.117 +void 1.118 +WebrtcGlobalInformation::GetAllStats( 1.119 + const GlobalObject& aGlobal, 1.120 + WebrtcGlobalStatisticsCallback& aStatsCallback, 1.121 + ErrorResult& aRv) 1.122 +{ 1.123 + if (!NS_IsMainThread()) { 1.124 + aRv.Throw(NS_ERROR_NOT_SAME_THREAD); 1.125 + return; 1.126 + } 1.127 + 1.128 + nsresult rv; 1.129 + nsCOMPtr<nsIEventTarget> stsThread = 1.130 + do_GetService(NS_SOCKETTRANSPORTSERVICE_CONTRACTID, &rv); 1.131 + 1.132 + if (NS_FAILED(rv)) { 1.133 + aRv.Throw(rv); 1.134 + return; 1.135 + } 1.136 + 1.137 + if (!stsThread) { 1.138 + aRv.Throw(NS_ERROR_UNEXPECTED); 1.139 + return; 1.140 + } 1.141 + 1.142 + nsAutoPtr<RTCStatsQueries> queries(new RTCStatsQueries); 1.143 + 1.144 + // If there is no PeerConnectionCtx, go through the same motions, since 1.145 + // the API consumer doesn't care why there are no PeerConnectionImpl. 1.146 + if (PeerConnectionCtx::isActive()) { 1.147 + PeerConnectionCtx *ctx = PeerConnectionCtx::GetInstance(); 1.148 + MOZ_ASSERT(ctx); 1.149 + for (auto p = ctx->mPeerConnections.begin(); 1.150 + p != ctx->mPeerConnections.end(); 1.151 + ++p) { 1.152 + MOZ_ASSERT(p->second); 1.153 + 1.154 + if (p->second->HasMedia()) { 1.155 + queries->append(nsAutoPtr<RTCStatsQuery>(new RTCStatsQuery(true))); 1.156 + p->second->BuildStatsQuery_m(nullptr, // all tracks 1.157 + queries->back()); 1.158 + } 1.159 + } 1.160 + } 1.161 + 1.162 + // CallbackObject does not support threadsafe refcounting, and must be 1.163 + // destroyed on main. 1.164 + nsMainThreadPtrHandle<WebrtcGlobalStatisticsCallback> callbackHandle( 1.165 + new nsMainThreadPtrHolder<WebrtcGlobalStatisticsCallback>(&aStatsCallback)); 1.166 + 1.167 + rv = RUN_ON_THREAD(stsThread, 1.168 + WrapRunnableNM(&GetAllStats_s, callbackHandle, queries), 1.169 + NS_DISPATCH_NORMAL); 1.170 + 1.171 + aRv = rv; 1.172 +} 1.173 + 1.174 +void 1.175 +WebrtcGlobalInformation::GetLogging( 1.176 + const GlobalObject& aGlobal, 1.177 + const nsAString& aPattern, 1.178 + WebrtcGlobalLoggingCallback& aLoggingCallback, 1.179 + ErrorResult& aRv) 1.180 +{ 1.181 + if (!NS_IsMainThread()) { 1.182 + aRv.Throw(NS_ERROR_NOT_SAME_THREAD); 1.183 + return; 1.184 + } 1.185 + 1.186 + nsresult rv; 1.187 + nsCOMPtr<nsIEventTarget> stsThread = 1.188 + do_GetService(NS_SOCKETTRANSPORTSERVICE_CONTRACTID, &rv); 1.189 + 1.190 + if (NS_FAILED(rv)) { 1.191 + aRv.Throw(rv); 1.192 + return; 1.193 + } 1.194 + 1.195 + if (!stsThread) { 1.196 + aRv.Throw(NS_ERROR_UNEXPECTED); 1.197 + return; 1.198 + } 1.199 + 1.200 + std::string pattern(NS_ConvertUTF16toUTF8(aPattern).get()); 1.201 + 1.202 + // CallbackObject does not support threadsafe refcounting, and must be 1.203 + // destroyed on main. 1.204 + nsMainThreadPtrHandle<WebrtcGlobalLoggingCallback> callbackHandle( 1.205 + new nsMainThreadPtrHolder<WebrtcGlobalLoggingCallback>(&aLoggingCallback)); 1.206 + 1.207 + rv = RUN_ON_THREAD(stsThread, 1.208 + WrapRunnableNM(&GetLogging_s, callbackHandle, pattern), 1.209 + NS_DISPATCH_NORMAL); 1.210 + 1.211 + if (NS_FAILED(rv)) { 1.212 + aLoggingCallback.Release(); 1.213 + } 1.214 + 1.215 + aRv = rv; 1.216 +} 1.217 + 1.218 +struct StreamResult { 1.219 + StreamResult() : candidateTypeBitpattern(0), streamSucceeded(false) {} 1.220 + uint8_t candidateTypeBitpattern; 1.221 + bool streamSucceeded; 1.222 +}; 1.223 + 1.224 +static void StoreLongTermICEStatisticsImpl_m( 1.225 + nsresult result, 1.226 + nsAutoPtr<RTCStatsQuery> query) { 1.227 + 1.228 + if (NS_FAILED(result) || 1.229 + !query->error.empty() || 1.230 + !query->report.mIceCandidateStats.WasPassed()) { 1.231 + return; 1.232 + } 1.233 + 1.234 + // First, store stuff in telemetry 1.235 + enum { 1.236 + REMOTE_GATHERED_SERVER_REFLEXIVE = 1, 1.237 + REMOTE_GATHERED_TURN = 1 << 1, 1.238 + LOCAL_GATHERED_SERVER_REFLEXIVE = 1 << 2, 1.239 + LOCAL_GATHERED_TURN_UDP = 1 << 3, 1.240 + LOCAL_GATHERED_TURN_TCP = 1 << 4, 1.241 + LOCAL_GATHERED_TURN_TLS = 1 << 5, 1.242 + LOCAL_GATHERED_TURN_HTTPS = 1 << 6, 1.243 + }; 1.244 + 1.245 + // TODO(bcampen@mozilla.com): Do we need to watch out for cases where the 1.246 + // components within a stream didn't have the same types of relayed 1.247 + // candidates? I have a feeling that late trickle could cause this, but right 1.248 + // now we don't have enough information to detect it (we would need to know 1.249 + // the ICE component id for each candidate pair and candidate) 1.250 + 1.251 + std::map<std::string, StreamResult> streamResults; 1.252 + 1.253 + // Build list of streams, and whether or not they failed. 1.254 + for (size_t i = 0; 1.255 + i < query->report.mIceCandidatePairStats.Value().Length(); 1.256 + ++i) { 1.257 + const RTCIceCandidatePairStats &pair = 1.258 + query->report.mIceCandidatePairStats.Value()[i]; 1.259 + 1.260 + if (!pair.mState.WasPassed() || !pair.mComponentId.WasPassed()) { 1.261 + MOZ_CRASH(); 1.262 + continue; 1.263 + } 1.264 + 1.265 + // Note: this is not a "component" in the ICE definition, this is really a 1.266 + // stream ID. This is just the way the stats API is standardized right now. 1.267 + // Very confusing. 1.268 + std::string streamId( 1.269 + NS_ConvertUTF16toUTF8(pair.mComponentId.Value()).get()); 1.270 + 1.271 + streamResults[streamId].streamSucceeded |= 1.272 + pair.mState.Value() == RTCStatsIceCandidatePairState::Succeeded; 1.273 + } 1.274 + 1.275 + for (size_t i = 0; 1.276 + i < query->report.mIceCandidateStats.Value().Length(); 1.277 + ++i) { 1.278 + const RTCIceCandidateStats &cand = 1.279 + query->report.mIceCandidateStats.Value()[i]; 1.280 + 1.281 + if (!cand.mType.WasPassed() || 1.282 + !cand.mCandidateType.WasPassed() || 1.283 + !cand.mComponentId.WasPassed()) { 1.284 + // Crash on debug, ignore this candidate otherwise. 1.285 + MOZ_CRASH(); 1.286 + continue; 1.287 + } 1.288 + 1.289 + // Note: this is not a "component" in the ICE definition, this is really a 1.290 + // stream ID. This is just the way the stats API is standardized right now 1.291 + // Very confusing. 1.292 + std::string streamId( 1.293 + NS_ConvertUTF16toUTF8(cand.mComponentId.Value()).get()); 1.294 + 1.295 + if (cand.mCandidateType.Value() == RTCStatsIceCandidateType::Relayed) { 1.296 + if (cand.mType.Value() == RTCStatsType::Localcandidate) { 1.297 + NS_ConvertUTF16toUTF8 transport(cand.mMozLocalTransport.Value()); 1.298 + if (transport == kNrIceTransportUdp) { 1.299 + streamResults[streamId].candidateTypeBitpattern |= 1.300 + LOCAL_GATHERED_TURN_UDP; 1.301 + } else if (transport == kNrIceTransportTcp) { 1.302 + streamResults[streamId].candidateTypeBitpattern |= 1.303 + LOCAL_GATHERED_TURN_TCP; 1.304 + } 1.305 + } else { 1.306 + streamResults[streamId].candidateTypeBitpattern |= REMOTE_GATHERED_TURN; 1.307 + } 1.308 + } else if (cand.mCandidateType.Value() == 1.309 + RTCStatsIceCandidateType::Serverreflexive) { 1.310 + if (cand.mType.Value() == RTCStatsType::Localcandidate) { 1.311 + streamResults[streamId].candidateTypeBitpattern |= 1.312 + LOCAL_GATHERED_SERVER_REFLEXIVE; 1.313 + } else { 1.314 + streamResults[streamId].candidateTypeBitpattern |= 1.315 + REMOTE_GATHERED_SERVER_REFLEXIVE; 1.316 + } 1.317 + } 1.318 + } 1.319 + 1.320 + for (auto i = streamResults.begin(); i != streamResults.end(); ++i) { 1.321 + if (i->second.streamSucceeded) { 1.322 + Telemetry::Accumulate(Telemetry::WEBRTC_CANDIDATE_TYPES_GIVEN_SUCCESS, 1.323 + i->second.candidateTypeBitpattern); 1.324 + } else { 1.325 + Telemetry::Accumulate(Telemetry::WEBRTC_CANDIDATE_TYPES_GIVEN_FAILURE, 1.326 + i->second.candidateTypeBitpattern); 1.327 + } 1.328 + } 1.329 +} 1.330 + 1.331 +static void GetStatsForLongTermStorage_s( 1.332 + nsAutoPtr<RTCStatsQuery> query) { 1.333 + 1.334 + MOZ_ASSERT(query); 1.335 + 1.336 + nsresult rv = PeerConnectionImpl::ExecuteStatsQuery_s(query.get()); 1.337 + 1.338 + // Even if Telemetry::Accumulate is threadsafe, we still need to send the 1.339 + // query back to main, since that is where it must be destroyed. 1.340 + NS_DispatchToMainThread( 1.341 + WrapRunnableNM( 1.342 + &StoreLongTermICEStatisticsImpl_m, 1.343 + rv, 1.344 + query), 1.345 + NS_DISPATCH_NORMAL); 1.346 +} 1.347 + 1.348 +void WebrtcGlobalInformation::StoreLongTermICEStatistics( 1.349 + sipcc::PeerConnectionImpl& aPc) { 1.350 + Telemetry::Accumulate(Telemetry::WEBRTC_ICE_FINAL_CONNECTION_STATE, 1.351 + static_cast<uint32_t>(aPc.IceConnectionState())); 1.352 + 1.353 + if (aPc.IceConnectionState() == PCImplIceConnectionState::New) { 1.354 + // ICE has not started; we won't have any remote candidates, so recording 1.355 + // statistics on gathered candidates is pointless. 1.356 + return; 1.357 + } 1.358 + 1.359 + nsAutoPtr<RTCStatsQuery> query(new RTCStatsQuery(true)); 1.360 + 1.361 + nsresult rv = aPc.BuildStatsQuery_m(nullptr, query.get()); 1.362 + 1.363 + NS_ENSURE_SUCCESS_VOID(rv); 1.364 + 1.365 + RUN_ON_THREAD(aPc.GetSTSThread(), 1.366 + WrapRunnableNM(&GetStatsForLongTermStorage_s, 1.367 + query), 1.368 + NS_DISPATCH_NORMAL); 1.369 +} 1.370 + 1.371 + 1.372 +} // namespace dom 1.373 +} // namespace mozilla 1.374 +