media/webrtc/signaling/src/peerconnection/WebrtcGlobalInformation.cpp

branch
TOR_BUG_9701
changeset 10
ac0c01689b40
equal deleted inserted replaced
-1:000000000000 0:8a9ec787aab6
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
7 #include <deque>
8 #include <string>
9
10 #include "CSFLog.h"
11
12 #include "mozilla/dom/WebrtcGlobalInformationBinding.h"
13
14 #include "nsAutoPtr.h"
15 #include "nsNetCID.h" // NS_SOCKETTRANSPORTSERVICE_CONTRACTID
16 #include "nsServiceManagerUtils.h" // do_GetService
17 #include "mozilla/ErrorResult.h"
18 #include "mozilla/Vector.h"
19 #include "nsProxyRelease.h"
20 #include "mozilla/Telemetry.h"
21
22 #include "rlogringbuffer.h"
23 #include "runnable_utils.h"
24 #include "PeerConnectionCtx.h"
25 #include "PeerConnectionImpl.h"
26
27 using sipcc::PeerConnectionImpl;
28 using sipcc::PeerConnectionCtx;
29 using sipcc::RTCStatsQuery;
30
31 static const char* logTag = "WebrtcGlobalInformation";
32
33 namespace mozilla {
34 namespace dom {
35
36 typedef Vector<nsAutoPtr<RTCStatsQuery>> RTCStatsQueries;
37
38 static void OnStatsReport_m(
39 nsMainThreadPtrHandle<WebrtcGlobalStatisticsCallback> aStatsCallback,
40 nsAutoPtr<RTCStatsQueries> aQueryList)
41 {
42 MOZ_ASSERT(NS_IsMainThread());
43 MOZ_ASSERT(aQueryList);
44
45 WebrtcGlobalStatisticsReport report;
46 report.mReports.Construct();
47 for (auto q = aQueryList->begin(); q != aQueryList->end(); ++q) {
48 MOZ_ASSERT(*q);
49 report.mReports.Value().AppendElement((*q)->report);
50 }
51
52 ErrorResult rv;
53 aStatsCallback.get()->Call(report, rv);
54
55 if (rv.Failed()) {
56 CSFLogError(logTag, "Error firing stats observer callback");
57 }
58 }
59
60 static void GetAllStats_s(
61 nsMainThreadPtrHandle<WebrtcGlobalStatisticsCallback> aStatsCallback,
62 nsAutoPtr<RTCStatsQueries> aQueryList)
63 {
64 MOZ_ASSERT(aQueryList);
65
66 for (auto q = aQueryList->begin(); q != aQueryList->end(); ++q) {
67 MOZ_ASSERT(*q);
68 PeerConnectionImpl::ExecuteStatsQuery_s(*q);
69 }
70
71 NS_DispatchToMainThread(WrapRunnableNM(&OnStatsReport_m,
72 aStatsCallback,
73 aQueryList),
74 NS_DISPATCH_NORMAL);
75 }
76
77 static void OnGetLogging_m(
78 nsMainThreadPtrHandle<WebrtcGlobalLoggingCallback> aLoggingCallback,
79 const std::string& aPattern,
80 nsAutoPtr<std::deque<std::string>> aLogList)
81 {
82 ErrorResult rv;
83 if (!aLogList->empty()) {
84 Sequence<nsString> nsLogs;
85 for (auto l = aLogList->begin(); l != aLogList->end(); ++l) {
86 nsLogs.AppendElement(NS_ConvertUTF8toUTF16(l->c_str()));
87 }
88 aLoggingCallback.get()->Call(nsLogs, rv);
89 }
90
91 if (rv.Failed()) {
92 CSFLogError(logTag, "Error firing logging observer callback");
93 }
94 }
95
96 static void GetLogging_s(
97 nsMainThreadPtrHandle<WebrtcGlobalLoggingCallback> aLoggingCallback,
98 const std::string& aPattern)
99 {
100 RLogRingBuffer* logs = RLogRingBuffer::GetInstance();
101 nsAutoPtr<std::deque<std::string>> result(new std::deque<std::string>);
102 // Might not exist yet.
103 if (logs) {
104 logs->Filter(aPattern, 0, result);
105 }
106 NS_DispatchToMainThread(WrapRunnableNM(&OnGetLogging_m,
107 aLoggingCallback,
108 aPattern,
109 result),
110 NS_DISPATCH_NORMAL);
111 }
112
113
114 void
115 WebrtcGlobalInformation::GetAllStats(
116 const GlobalObject& aGlobal,
117 WebrtcGlobalStatisticsCallback& aStatsCallback,
118 ErrorResult& aRv)
119 {
120 if (!NS_IsMainThread()) {
121 aRv.Throw(NS_ERROR_NOT_SAME_THREAD);
122 return;
123 }
124
125 nsresult rv;
126 nsCOMPtr<nsIEventTarget> stsThread =
127 do_GetService(NS_SOCKETTRANSPORTSERVICE_CONTRACTID, &rv);
128
129 if (NS_FAILED(rv)) {
130 aRv.Throw(rv);
131 return;
132 }
133
134 if (!stsThread) {
135 aRv.Throw(NS_ERROR_UNEXPECTED);
136 return;
137 }
138
139 nsAutoPtr<RTCStatsQueries> queries(new RTCStatsQueries);
140
141 // If there is no PeerConnectionCtx, go through the same motions, since
142 // the API consumer doesn't care why there are no PeerConnectionImpl.
143 if (PeerConnectionCtx::isActive()) {
144 PeerConnectionCtx *ctx = PeerConnectionCtx::GetInstance();
145 MOZ_ASSERT(ctx);
146 for (auto p = ctx->mPeerConnections.begin();
147 p != ctx->mPeerConnections.end();
148 ++p) {
149 MOZ_ASSERT(p->second);
150
151 if (p->second->HasMedia()) {
152 queries->append(nsAutoPtr<RTCStatsQuery>(new RTCStatsQuery(true)));
153 p->second->BuildStatsQuery_m(nullptr, // all tracks
154 queries->back());
155 }
156 }
157 }
158
159 // CallbackObject does not support threadsafe refcounting, and must be
160 // destroyed on main.
161 nsMainThreadPtrHandle<WebrtcGlobalStatisticsCallback> callbackHandle(
162 new nsMainThreadPtrHolder<WebrtcGlobalStatisticsCallback>(&aStatsCallback));
163
164 rv = RUN_ON_THREAD(stsThread,
165 WrapRunnableNM(&GetAllStats_s, callbackHandle, queries),
166 NS_DISPATCH_NORMAL);
167
168 aRv = rv;
169 }
170
171 void
172 WebrtcGlobalInformation::GetLogging(
173 const GlobalObject& aGlobal,
174 const nsAString& aPattern,
175 WebrtcGlobalLoggingCallback& aLoggingCallback,
176 ErrorResult& aRv)
177 {
178 if (!NS_IsMainThread()) {
179 aRv.Throw(NS_ERROR_NOT_SAME_THREAD);
180 return;
181 }
182
183 nsresult rv;
184 nsCOMPtr<nsIEventTarget> stsThread =
185 do_GetService(NS_SOCKETTRANSPORTSERVICE_CONTRACTID, &rv);
186
187 if (NS_FAILED(rv)) {
188 aRv.Throw(rv);
189 return;
190 }
191
192 if (!stsThread) {
193 aRv.Throw(NS_ERROR_UNEXPECTED);
194 return;
195 }
196
197 std::string pattern(NS_ConvertUTF16toUTF8(aPattern).get());
198
199 // CallbackObject does not support threadsafe refcounting, and must be
200 // destroyed on main.
201 nsMainThreadPtrHandle<WebrtcGlobalLoggingCallback> callbackHandle(
202 new nsMainThreadPtrHolder<WebrtcGlobalLoggingCallback>(&aLoggingCallback));
203
204 rv = RUN_ON_THREAD(stsThread,
205 WrapRunnableNM(&GetLogging_s, callbackHandle, pattern),
206 NS_DISPATCH_NORMAL);
207
208 if (NS_FAILED(rv)) {
209 aLoggingCallback.Release();
210 }
211
212 aRv = rv;
213 }
214
215 struct StreamResult {
216 StreamResult() : candidateTypeBitpattern(0), streamSucceeded(false) {}
217 uint8_t candidateTypeBitpattern;
218 bool streamSucceeded;
219 };
220
221 static void StoreLongTermICEStatisticsImpl_m(
222 nsresult result,
223 nsAutoPtr<RTCStatsQuery> query) {
224
225 if (NS_FAILED(result) ||
226 !query->error.empty() ||
227 !query->report.mIceCandidateStats.WasPassed()) {
228 return;
229 }
230
231 // First, store stuff in telemetry
232 enum {
233 REMOTE_GATHERED_SERVER_REFLEXIVE = 1,
234 REMOTE_GATHERED_TURN = 1 << 1,
235 LOCAL_GATHERED_SERVER_REFLEXIVE = 1 << 2,
236 LOCAL_GATHERED_TURN_UDP = 1 << 3,
237 LOCAL_GATHERED_TURN_TCP = 1 << 4,
238 LOCAL_GATHERED_TURN_TLS = 1 << 5,
239 LOCAL_GATHERED_TURN_HTTPS = 1 << 6,
240 };
241
242 // TODO(bcampen@mozilla.com): Do we need to watch out for cases where the
243 // components within a stream didn't have the same types of relayed
244 // candidates? I have a feeling that late trickle could cause this, but right
245 // now we don't have enough information to detect it (we would need to know
246 // the ICE component id for each candidate pair and candidate)
247
248 std::map<std::string, StreamResult> streamResults;
249
250 // Build list of streams, and whether or not they failed.
251 for (size_t i = 0;
252 i < query->report.mIceCandidatePairStats.Value().Length();
253 ++i) {
254 const RTCIceCandidatePairStats &pair =
255 query->report.mIceCandidatePairStats.Value()[i];
256
257 if (!pair.mState.WasPassed() || !pair.mComponentId.WasPassed()) {
258 MOZ_CRASH();
259 continue;
260 }
261
262 // Note: this is not a "component" in the ICE definition, this is really a
263 // stream ID. This is just the way the stats API is standardized right now.
264 // Very confusing.
265 std::string streamId(
266 NS_ConvertUTF16toUTF8(pair.mComponentId.Value()).get());
267
268 streamResults[streamId].streamSucceeded |=
269 pair.mState.Value() == RTCStatsIceCandidatePairState::Succeeded;
270 }
271
272 for (size_t i = 0;
273 i < query->report.mIceCandidateStats.Value().Length();
274 ++i) {
275 const RTCIceCandidateStats &cand =
276 query->report.mIceCandidateStats.Value()[i];
277
278 if (!cand.mType.WasPassed() ||
279 !cand.mCandidateType.WasPassed() ||
280 !cand.mComponentId.WasPassed()) {
281 // Crash on debug, ignore this candidate otherwise.
282 MOZ_CRASH();
283 continue;
284 }
285
286 // Note: this is not a "component" in the ICE definition, this is really a
287 // stream ID. This is just the way the stats API is standardized right now
288 // Very confusing.
289 std::string streamId(
290 NS_ConvertUTF16toUTF8(cand.mComponentId.Value()).get());
291
292 if (cand.mCandidateType.Value() == RTCStatsIceCandidateType::Relayed) {
293 if (cand.mType.Value() == RTCStatsType::Localcandidate) {
294 NS_ConvertUTF16toUTF8 transport(cand.mMozLocalTransport.Value());
295 if (transport == kNrIceTransportUdp) {
296 streamResults[streamId].candidateTypeBitpattern |=
297 LOCAL_GATHERED_TURN_UDP;
298 } else if (transport == kNrIceTransportTcp) {
299 streamResults[streamId].candidateTypeBitpattern |=
300 LOCAL_GATHERED_TURN_TCP;
301 }
302 } else {
303 streamResults[streamId].candidateTypeBitpattern |= REMOTE_GATHERED_TURN;
304 }
305 } else if (cand.mCandidateType.Value() ==
306 RTCStatsIceCandidateType::Serverreflexive) {
307 if (cand.mType.Value() == RTCStatsType::Localcandidate) {
308 streamResults[streamId].candidateTypeBitpattern |=
309 LOCAL_GATHERED_SERVER_REFLEXIVE;
310 } else {
311 streamResults[streamId].candidateTypeBitpattern |=
312 REMOTE_GATHERED_SERVER_REFLEXIVE;
313 }
314 }
315 }
316
317 for (auto i = streamResults.begin(); i != streamResults.end(); ++i) {
318 if (i->second.streamSucceeded) {
319 Telemetry::Accumulate(Telemetry::WEBRTC_CANDIDATE_TYPES_GIVEN_SUCCESS,
320 i->second.candidateTypeBitpattern);
321 } else {
322 Telemetry::Accumulate(Telemetry::WEBRTC_CANDIDATE_TYPES_GIVEN_FAILURE,
323 i->second.candidateTypeBitpattern);
324 }
325 }
326 }
327
328 static void GetStatsForLongTermStorage_s(
329 nsAutoPtr<RTCStatsQuery> query) {
330
331 MOZ_ASSERT(query);
332
333 nsresult rv = PeerConnectionImpl::ExecuteStatsQuery_s(query.get());
334
335 // Even if Telemetry::Accumulate is threadsafe, we still need to send the
336 // query back to main, since that is where it must be destroyed.
337 NS_DispatchToMainThread(
338 WrapRunnableNM(
339 &StoreLongTermICEStatisticsImpl_m,
340 rv,
341 query),
342 NS_DISPATCH_NORMAL);
343 }
344
345 void WebrtcGlobalInformation::StoreLongTermICEStatistics(
346 sipcc::PeerConnectionImpl& aPc) {
347 Telemetry::Accumulate(Telemetry::WEBRTC_ICE_FINAL_CONNECTION_STATE,
348 static_cast<uint32_t>(aPc.IceConnectionState()));
349
350 if (aPc.IceConnectionState() == PCImplIceConnectionState::New) {
351 // ICE has not started; we won't have any remote candidates, so recording
352 // statistics on gathered candidates is pointless.
353 return;
354 }
355
356 nsAutoPtr<RTCStatsQuery> query(new RTCStatsQuery(true));
357
358 nsresult rv = aPc.BuildStatsQuery_m(nullptr, query.get());
359
360 NS_ENSURE_SUCCESS_VOID(rv);
361
362 RUN_ON_THREAD(aPc.GetSTSThread(),
363 WrapRunnableNM(&GetStatsForLongTermStorage_s,
364 query),
365 NS_DISPATCH_NORMAL);
366 }
367
368
369 } // namespace dom
370 } // namespace mozilla
371

mercurial