blob: 42ae662c1c10bf76e1c3fa238780d3e7da36bb51 [file] [log] [blame]
henrike@webrtc.org28e20752013-07-10 00:45:36 +00001/*
2 * libjingle
3 * Copyright 2012, Google Inc.
4 *
5 * Redistribution and use in source and binary forms, with or without
6 * modification, are permitted provided that the following conditions are met:
7 *
8 * 1. Redistributions of source code must retain the above copyright notice,
9 * this list of conditions and the following disclaimer.
10 * 2. Redistributions in binary form must reproduce the above copyright notice,
11 * this list of conditions and the following disclaimer in the documentation
12 * and/or other materials provided with the distribution.
13 * 3. The name of the author may not be used to endorse or promote products
14 * derived from this software without specific prior written permission.
15 *
16 * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR IMPLIED
17 * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
18 * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO
19 * EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
20 * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
21 * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS;
22 * OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
23 * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR
24 * OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF
25 * ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
26 */
27
28#include "talk/app/webrtc/statscollector.h"
29
30#include <utility>
31#include <vector>
32
wu@webrtc.org4551b792013-10-09 15:37:36 +000033#include "talk/base/base64.h"
34#include "talk/base/scoped_ptr.h"
henrike@webrtc.org28e20752013-07-10 00:45:36 +000035#include "talk/session/media/channel.h"
36
37namespace webrtc {
38
39// The items below are in alphabetical order.
40const char StatsReport::kStatsValueNameActiveConnection[] =
41 "googActiveConnection";
42const char StatsReport::kStatsValueNameActualEncBitrate[] =
43 "googActualEncBitrate";
44const char StatsReport::kStatsValueNameAudioOutputLevel[] = "audioOutputLevel";
45const char StatsReport::kStatsValueNameAudioInputLevel[] = "audioInputLevel";
46const char StatsReport::kStatsValueNameAvailableReceiveBandwidth[] =
47 "googAvailableReceiveBandwidth";
48const char StatsReport::kStatsValueNameAvailableSendBandwidth[] =
49 "googAvailableSendBandwidth";
50const char StatsReport::kStatsValueNameBucketDelay[] = "googBucketDelay";
51const char StatsReport::kStatsValueNameBytesReceived[] = "bytesReceived";
52const char StatsReport::kStatsValueNameBytesSent[] = "bytesSent";
53const char StatsReport::kStatsValueNameChannelId[] = "googChannelId";
54const char StatsReport::kStatsValueNameCodecName[] = "googCodecName";
55const char StatsReport::kStatsValueNameComponent[] = "googComponent";
56const char StatsReport::kStatsValueNameContentName[] = "googContentName";
wu@webrtc.org4551b792013-10-09 15:37:36 +000057const char StatsReport::kStatsValueNameDer[] = "googDerBase64";
henrike@webrtc.org28e20752013-07-10 00:45:36 +000058// Echo metrics from the audio processing module.
59const char StatsReport::kStatsValueNameEchoCancellationQualityMin[] =
60 "googEchoCancellationQualityMin";
61const char StatsReport::kStatsValueNameEchoDelayMedian[] =
62 "googEchoCancellationEchoDelayMedian";
63const char StatsReport::kStatsValueNameEchoDelayStdDev[] =
64 "googEchoCancellationEchoDelayStdDev";
65const char StatsReport::kStatsValueNameEchoReturnLoss[] =
66 "googEchoCancellationReturnLoss";
67const char StatsReport::kStatsValueNameEchoReturnLossEnhancement[] =
68 "googEchoCancellationReturnLossEnhancement";
69
wu@webrtc.org4551b792013-10-09 15:37:36 +000070const char StatsReport::kStatsValueNameFingerprint[] = "googFingerprint";
mallinath@webrtc.org19f27e62013-10-13 17:18:27 +000071const char StatsReport::kStatsValueNameFingerprintAlgorithm[] =
72 "googFingerprintAlgorithm";
henrike@webrtc.org28e20752013-07-10 00:45:36 +000073const char StatsReport::kStatsValueNameFirsReceived[] = "googFirsReceived";
74const char StatsReport::kStatsValueNameFirsSent[] = "googFirsSent";
75const char StatsReport::kStatsValueNameFrameHeightReceived[] =
76 "googFrameHeightReceived";
77const char StatsReport::kStatsValueNameFrameHeightSent[] =
78 "googFrameHeightSent";
79const char StatsReport::kStatsValueNameFrameRateReceived[] =
80 "googFrameRateReceived";
81const char StatsReport::kStatsValueNameFrameRateDecoded[] =
82 "googFrameRateDecoded";
83const char StatsReport::kStatsValueNameFrameRateOutput[] =
84 "googFrameRateOutput";
85const char StatsReport::kStatsValueNameFrameRateInput[] = "googFrameRateInput";
86const char StatsReport::kStatsValueNameFrameRateSent[] = "googFrameRateSent";
87const char StatsReport::kStatsValueNameFrameWidthReceived[] =
88 "googFrameWidthReceived";
89const char StatsReport::kStatsValueNameFrameWidthSent[] = "googFrameWidthSent";
90const char StatsReport::kStatsValueNameInitiator[] = "googInitiator";
wu@webrtc.org4551b792013-10-09 15:37:36 +000091const char StatsReport::kStatsValueNameIssuerId[] = "googIssuerId";
henrike@webrtc.org28e20752013-07-10 00:45:36 +000092const char StatsReport::kStatsValueNameJitterReceived[] = "googJitterReceived";
93const char StatsReport::kStatsValueNameLocalAddress[] = "googLocalAddress";
wu@webrtc.org4551b792013-10-09 15:37:36 +000094const char StatsReport::kStatsValueNameLocalCertificateId[] =
95 "googLocalCertificateId";
henrike@webrtc.org28e20752013-07-10 00:45:36 +000096const char StatsReport::kStatsValueNameNacksReceived[] = "googNacksReceived";
97const char StatsReport::kStatsValueNameNacksSent[] = "googNacksSent";
98const char StatsReport::kStatsValueNamePacketsReceived[] = "packetsReceived";
99const char StatsReport::kStatsValueNamePacketsSent[] = "packetsSent";
100const char StatsReport::kStatsValueNamePacketsLost[] = "packetsLost";
101const char StatsReport::kStatsValueNameReadable[] = "googReadable";
102const char StatsReport::kStatsValueNameRemoteAddress[] = "googRemoteAddress";
wu@webrtc.org4551b792013-10-09 15:37:36 +0000103const char StatsReport::kStatsValueNameRemoteCertificateId[] =
104 "googRemoteCertificateId";
henrike@webrtc.org28e20752013-07-10 00:45:36 +0000105const char StatsReport::kStatsValueNameRetransmitBitrate[] =
106 "googRetransmitBitrate";
107const char StatsReport::kStatsValueNameRtt[] = "googRtt";
wu@webrtc.org967bfff2013-09-19 05:49:50 +0000108const char StatsReport::kStatsValueNameSsrc[] = "ssrc";
henrike@webrtc.org28e20752013-07-10 00:45:36 +0000109const char StatsReport::kStatsValueNameTargetEncBitrate[] =
110 "googTargetEncBitrate";
111const char StatsReport::kStatsValueNameTransmitBitrate[] =
112 "googTransmitBitrate";
113const char StatsReport::kStatsValueNameTransportId[] = "transportId";
114const char StatsReport::kStatsValueNameTransportType[] = "googTransportType";
115const char StatsReport::kStatsValueNameTrackId[] = "googTrackId";
wu@webrtc.org967bfff2013-09-19 05:49:50 +0000116const char StatsReport::kStatsValueNameTypingNoiseState[] =
117 "googTypingNoiseState";
henrike@webrtc.org28e20752013-07-10 00:45:36 +0000118const char StatsReport::kStatsValueNameWritable[] = "googWritable";
119
120const char StatsReport::kStatsReportTypeSession[] = "googLibjingleSession";
121const char StatsReport::kStatsReportTypeBwe[] = "VideoBwe";
122const char StatsReport::kStatsReportTypeSsrc[] = "ssrc";
123const char StatsReport::kStatsReportTypeTrack[] = "googTrack";
124const char StatsReport::kStatsReportTypeIceCandidate[] = "iceCandidate";
125const char StatsReport::kStatsReportTypeTransport[] = "googTransport";
126const char StatsReport::kStatsReportTypeComponent[] = "googComponent";
127const char StatsReport::kStatsReportTypeCandidatePair[] = "googCandidatePair";
wu@webrtc.org4551b792013-10-09 15:37:36 +0000128const char StatsReport::kStatsReportTypeCertificate[] = "googCertificate";
henrike@webrtc.org28e20752013-07-10 00:45:36 +0000129
130const char StatsReport::kStatsReportVideoBweId[] = "bweforvideo";
131
wu@webrtc.org967bfff2013-09-19 05:49:50 +0000132
henrike@webrtc.org28e20752013-07-10 00:45:36 +0000133// Implementations of functions in statstypes.h
134void StatsReport::AddValue(const std::string& name, const std::string& value) {
135 Value temp;
136 temp.name = name;
137 temp.value = value;
138 values.push_back(temp);
139}
140
141void StatsReport::AddValue(const std::string& name, int64 value) {
142 AddValue(name, talk_base::ToString<int64>(value));
143}
144
145void StatsReport::AddBoolean(const std::string& name, bool value) {
146 AddValue(name, value ? "true" : "false");
147}
148
149namespace {
150typedef std::map<std::string, StatsReport> StatsMap;
151
152std::string StatsId(const std::string& type, const std::string& id) {
153 return type + "_" + id;
154}
155
156bool ExtractValueFromReport(
157 const StatsReport& report,
158 const std::string& name,
159 std::string* value) {
160 StatsReport::Values::const_iterator it = report.values.begin();
161 for (; it != report.values.end(); ++it) {
162 if (it->name == name) {
163 *value = it->value;
164 return true;
165 }
166 }
167 return false;
168}
169
170template <class TrackVector>
171void CreateTrackReports(const TrackVector& tracks, StatsMap* reports) {
172 for (size_t j = 0; j < tracks.size(); ++j) {
173 webrtc::MediaStreamTrackInterface* track = tracks[j];
174 // Adds an empty track report.
175 StatsReport report;
176 report.type = StatsReport::kStatsReportTypeTrack;
177 report.id = StatsId(StatsReport::kStatsReportTypeTrack, track->id());
178 report.AddValue(StatsReport::kStatsValueNameTrackId,
179 track->id());
180 (*reports)[report.id] = report;
181 }
182}
183
184void ExtractStats(const cricket::VoiceReceiverInfo& info, StatsReport* report) {
185 report->AddValue(StatsReport::kStatsValueNameAudioOutputLevel,
186 info.audio_level);
187 report->AddValue(StatsReport::kStatsValueNameBytesReceived,
188 info.bytes_rcvd);
189 report->AddValue(StatsReport::kStatsValueNameJitterReceived,
190 info.jitter_ms);
191 report->AddValue(StatsReport::kStatsValueNamePacketsReceived,
192 info.packets_rcvd);
193 report->AddValue(StatsReport::kStatsValueNamePacketsLost,
194 info.packets_lost);
195}
196
197void ExtractStats(const cricket::VoiceSenderInfo& info, StatsReport* report) {
198 report->AddValue(StatsReport::kStatsValueNameAudioInputLevel,
199 info.audio_level);
200 report->AddValue(StatsReport::kStatsValueNameBytesSent,
201 info.bytes_sent);
202 report->AddValue(StatsReport::kStatsValueNamePacketsSent,
203 info.packets_sent);
204 report->AddValue(StatsReport::kStatsValueNameJitterReceived,
205 info.jitter_ms);
206 report->AddValue(StatsReport::kStatsValueNameRtt, info.rtt_ms);
207 report->AddValue(StatsReport::kStatsValueNameEchoCancellationQualityMin,
208 talk_base::ToString<float>(info.aec_quality_min));
209 report->AddValue(StatsReport::kStatsValueNameEchoDelayMedian,
210 info.echo_delay_median_ms);
211 report->AddValue(StatsReport::kStatsValueNameEchoDelayStdDev,
212 info.echo_delay_std_ms);
213 report->AddValue(StatsReport::kStatsValueNameEchoReturnLoss,
214 info.echo_return_loss);
215 report->AddValue(StatsReport::kStatsValueNameEchoReturnLossEnhancement,
216 info.echo_return_loss_enhancement);
217 report->AddValue(StatsReport::kStatsValueNameCodecName, info.codec_name);
wu@webrtc.org967bfff2013-09-19 05:49:50 +0000218 report->AddBoolean(StatsReport::kStatsValueNameTypingNoiseState,
219 info.typing_noise_detected);
henrike@webrtc.org28e20752013-07-10 00:45:36 +0000220}
221
222void ExtractStats(const cricket::VideoReceiverInfo& info, StatsReport* report) {
223 report->AddValue(StatsReport::kStatsValueNameBytesReceived,
224 info.bytes_rcvd);
225 report->AddValue(StatsReport::kStatsValueNamePacketsReceived,
226 info.packets_rcvd);
227 report->AddValue(StatsReport::kStatsValueNamePacketsLost,
228 info.packets_lost);
229
230 report->AddValue(StatsReport::kStatsValueNameFirsSent,
231 info.firs_sent);
232 report->AddValue(StatsReport::kStatsValueNameNacksSent,
233 info.nacks_sent);
234 report->AddValue(StatsReport::kStatsValueNameFrameWidthReceived,
235 info.frame_width);
236 report->AddValue(StatsReport::kStatsValueNameFrameHeightReceived,
237 info.frame_height);
238 report->AddValue(StatsReport::kStatsValueNameFrameRateReceived,
239 info.framerate_rcvd);
240 report->AddValue(StatsReport::kStatsValueNameFrameRateDecoded,
241 info.framerate_decoded);
242 report->AddValue(StatsReport::kStatsValueNameFrameRateOutput,
243 info.framerate_output);
244}
245
246void ExtractStats(const cricket::VideoSenderInfo& info, StatsReport* report) {
247 report->AddValue(StatsReport::kStatsValueNameBytesSent,
248 info.bytes_sent);
249 report->AddValue(StatsReport::kStatsValueNamePacketsSent,
250 info.packets_sent);
251
252 report->AddValue(StatsReport::kStatsValueNameFirsReceived,
253 info.firs_rcvd);
254 report->AddValue(StatsReport::kStatsValueNameNacksReceived,
255 info.nacks_rcvd);
256 report->AddValue(StatsReport::kStatsValueNameFrameWidthSent,
257 info.frame_width);
258 report->AddValue(StatsReport::kStatsValueNameFrameHeightSent,
259 info.frame_height);
260 report->AddValue(StatsReport::kStatsValueNameFrameRateInput,
261 info.framerate_input);
262 report->AddValue(StatsReport::kStatsValueNameFrameRateSent,
263 info.framerate_sent);
264 report->AddValue(StatsReport::kStatsValueNameRtt, info.rtt_ms);
265 report->AddValue(StatsReport::kStatsValueNameCodecName, info.codec_name);
266}
267
268void ExtractStats(const cricket::BandwidthEstimationInfo& info,
269 double stats_gathering_started,
270 StatsReport* report) {
271 report->id = StatsReport::kStatsReportVideoBweId;
272 report->type = StatsReport::kStatsReportTypeBwe;
273
274 // Clear out stats from previous GatherStats calls if any.
275 if (report->timestamp != stats_gathering_started) {
276 report->values.clear();
277 report->timestamp = stats_gathering_started;
278 }
279
280 report->AddValue(StatsReport::kStatsValueNameAvailableSendBandwidth,
281 info.available_send_bandwidth);
282 report->AddValue(StatsReport::kStatsValueNameAvailableReceiveBandwidth,
283 info.available_recv_bandwidth);
284 report->AddValue(StatsReport::kStatsValueNameTargetEncBitrate,
285 info.target_enc_bitrate);
286 report->AddValue(StatsReport::kStatsValueNameActualEncBitrate,
287 info.actual_enc_bitrate);
288 report->AddValue(StatsReport::kStatsValueNameRetransmitBitrate,
289 info.retransmit_bitrate);
290 report->AddValue(StatsReport::kStatsValueNameTransmitBitrate,
291 info.transmit_bitrate);
292 report->AddValue(StatsReport::kStatsValueNameBucketDelay,
293 info.bucket_delay);
294}
295
296uint32 ExtractSsrc(const cricket::VoiceReceiverInfo& info) {
297 return info.ssrc;
298}
299
300uint32 ExtractSsrc(const cricket::VoiceSenderInfo& info) {
301 return info.ssrc;
302}
303
304uint32 ExtractSsrc(const cricket::VideoReceiverInfo& info) {
305 return info.ssrcs[0];
306}
307
308uint32 ExtractSsrc(const cricket::VideoSenderInfo& info) {
309 return info.ssrcs[0];
310}
311
312// Template to extract stats from a data vector.
313// ExtractSsrc and ExtractStats must be defined and overloaded for each type.
314template<typename T>
315void ExtractStatsFromList(const std::vector<T>& data,
316 const std::string& transport_id,
317 StatsCollector* collector) {
318 typename std::vector<T>::const_iterator it = data.begin();
319 for (; it != data.end(); ++it) {
320 std::string id;
321 uint32 ssrc = ExtractSsrc(*it);
322 StatsReport* report = collector->PrepareReport(ssrc, transport_id);
323 if (!report) {
324 continue;
325 }
326 ExtractStats(*it, report);
327 }
328};
329
330} // namespace
331
332StatsCollector::StatsCollector()
333 : session_(NULL), stats_gathering_started_(0) {
334}
335
336// Adds a MediaStream with tracks that can be used as a |selector| in a call
337// to GetStats.
338void StatsCollector::AddStream(MediaStreamInterface* stream) {
339 ASSERT(stream != NULL);
340
341 CreateTrackReports<AudioTrackVector>(stream->GetAudioTracks(),
342 &reports_);
343 CreateTrackReports<VideoTrackVector>(stream->GetVideoTracks(),
344 &reports_);
345}
346
347bool StatsCollector::GetStats(MediaStreamTrackInterface* track,
348 StatsReports* reports) {
349 ASSERT(reports != NULL);
350 reports->clear();
351
352 StatsMap::iterator it;
353 if (!track) {
354 for (it = reports_.begin(); it != reports_.end(); ++it) {
355 reports->push_back(it->second);
356 }
357 return true;
358 }
359
360 it = reports_.find(StatsId(StatsReport::kStatsReportTypeSession,
361 session_->id()));
362 if (it != reports_.end()) {
363 reports->push_back(it->second);
364 }
365
366 it = reports_.find(StatsId(StatsReport::kStatsReportTypeTrack, track->id()));
367
368 if (it == reports_.end()) {
369 LOG(LS_WARNING) << "No StatsReport is available for "<< track->id();
370 return false;
371 }
372
373 reports->push_back(it->second);
374
375 std::string track_id;
376 for (it = reports_.begin(); it != reports_.end(); ++it) {
377 if (it->second.type != StatsReport::kStatsReportTypeSsrc) {
378 continue;
379 }
380 if (ExtractValueFromReport(it->second,
381 StatsReport::kStatsValueNameTrackId,
382 &track_id)) {
383 if (track_id == track->id()) {
384 reports->push_back(it->second);
385 }
386 }
387 }
388
389 return true;
390}
391
392void StatsCollector::UpdateStats() {
393 double time_now = GetTimeNow();
394 // Calls to UpdateStats() that occur less than kMinGatherStatsPeriod number of
395 // ms apart will be ignored.
396 const double kMinGatherStatsPeriod = 50;
397 if (stats_gathering_started_ + kMinGatherStatsPeriod > time_now) {
398 return;
399 }
400 stats_gathering_started_ = time_now;
401
402 if (session_) {
403 ExtractSessionInfo();
404 ExtractVoiceInfo();
405 ExtractVideoInfo();
406 }
407}
408
409StatsReport* StatsCollector::PrepareReport(uint32 ssrc,
410 const std::string& transport_id) {
411 std::string ssrc_id = talk_base::ToString<uint32>(ssrc);
412 StatsMap::iterator it = reports_.find(StatsId(
413 StatsReport::kStatsReportTypeSsrc, ssrc_id));
414
415 std::string track_id;
416 if (it == reports_.end()) {
417 if (!session()->GetTrackIdBySsrc(ssrc, &track_id)) {
sergeyu@chromium.orga59696b2013-09-13 23:48:58 +0000418 LOG(LS_WARNING) << "The SSRC " << ssrc
419 << " is not associated with a track";
henrike@webrtc.org28e20752013-07-10 00:45:36 +0000420 return NULL;
421 }
422 } else {
423 // Keeps the old track id since we want to report the stats for inactive
424 // tracks.
425 ExtractValueFromReport(it->second,
426 StatsReport::kStatsValueNameTrackId,
427 &track_id);
428 }
429
430 StatsReport* report = &reports_[
431 StatsId(StatsReport::kStatsReportTypeSsrc, ssrc_id)];
432 report->id = StatsId(StatsReport::kStatsReportTypeSsrc, ssrc_id);
433 report->type = StatsReport::kStatsReportTypeSsrc;
434
435 // Clear out stats from previous GatherStats calls if any.
436 if (report->timestamp != stats_gathering_started_) {
437 report->values.clear();
438 report->timestamp = stats_gathering_started_;
439 }
440
441 report->AddValue(StatsReport::kStatsValueNameSsrc, ssrc_id);
442 report->AddValue(StatsReport::kStatsValueNameTrackId, track_id);
443 // Add the mapping of SSRC to transport.
444 report->AddValue(StatsReport::kStatsValueNameTransportId,
445 transport_id);
446 return report;
447}
448
wu@webrtc.org4551b792013-10-09 15:37:36 +0000449std::string StatsCollector::AddOneCertificateReport(
450 const talk_base::SSLCertificate* cert, const std::string& issuer_id) {
451 // TODO(bemasc): Move this computation to a helper class that caches these
452 // values to reduce CPU use in GetStats. This will require adding a fast
453 // SSLCertificate::Equals() method to detect certificate changes.
mallinath@webrtc.org19f27e62013-10-13 17:18:27 +0000454
455 std::string digest_algorithm;
456 if (!cert->GetSignatureDigestAlgorithm(&digest_algorithm))
457 return std::string();
458
wu@webrtc.org4551b792013-10-09 15:37:36 +0000459 talk_base::scoped_ptr<talk_base::SSLFingerprint> ssl_fingerprint(
mallinath@webrtc.org19f27e62013-10-13 17:18:27 +0000460 talk_base::SSLFingerprint::Create(digest_algorithm, cert));
wu@webrtc.org4551b792013-10-09 15:37:36 +0000461 std::string fingerprint = ssl_fingerprint->GetRfc4572Fingerprint();
462
463 talk_base::Buffer der_buffer;
464 cert->ToDER(&der_buffer);
465 std::string der_base64;
466 talk_base::Base64::EncodeFromArray(
467 der_buffer.data(), der_buffer.length(), &der_base64);
468
469 StatsReport report;
470 report.type = StatsReport::kStatsReportTypeCertificate;
471 report.id = StatsId(report.type, fingerprint);
472 report.timestamp = stats_gathering_started_;
473 report.AddValue(StatsReport::kStatsValueNameFingerprint, fingerprint);
mallinath@webrtc.org19f27e62013-10-13 17:18:27 +0000474 report.AddValue(StatsReport::kStatsValueNameFingerprintAlgorithm,
475 digest_algorithm);
wu@webrtc.org4551b792013-10-09 15:37:36 +0000476 report.AddValue(StatsReport::kStatsValueNameDer, der_base64);
477 if (!issuer_id.empty())
478 report.AddValue(StatsReport::kStatsValueNameIssuerId, issuer_id);
479 reports_[report.id] = report;
480 return report.id;
481}
482
483std::string StatsCollector::AddCertificateReports(
484 const talk_base::SSLCertificate* cert) {
485 // Produces a chain of StatsReports representing this certificate and the rest
486 // of its chain, and adds those reports to |reports_|. The return value is
487 // the id of the leaf report. The provided cert must be non-null, so at least
488 // one report will always be provided and the returned string will never be
489 // empty.
490 ASSERT(cert != NULL);
491
492 std::string issuer_id;
493 talk_base::scoped_ptr<talk_base::SSLCertChain> chain;
494 if (cert->GetChain(chain.accept())) {
495 // This loop runs in reverse, i.e. from root to leaf, so that each
496 // certificate's issuer's report ID is known before the child certificate's
497 // report is generated. The root certificate does not have an issuer ID
498 // value.
499 for (ptrdiff_t i = chain->GetSize() - 1; i >= 0; --i) {
500 const talk_base::SSLCertificate& cert_i = chain->Get(i);
501 issuer_id = AddOneCertificateReport(&cert_i, issuer_id);
502 }
503 }
504 // Add the leaf certificate.
505 return AddOneCertificateReport(cert, issuer_id);
506}
507
henrike@webrtc.org28e20752013-07-10 00:45:36 +0000508void StatsCollector::ExtractSessionInfo() {
509 // Extract information from the base session.
510 StatsReport report;
511 report.id = StatsId(StatsReport::kStatsReportTypeSession, session_->id());
512 report.type = StatsReport::kStatsReportTypeSession;
513 report.timestamp = stats_gathering_started_;
514 report.values.clear();
515 report.AddBoolean(StatsReport::kStatsValueNameInitiator,
516 session_->initiator());
517
518 reports_[report.id] = report;
519
520 cricket::SessionStats stats;
521 if (session_->GetStats(&stats)) {
522 // Store the proxy map away for use in SSRC reporting.
523 proxy_to_transport_ = stats.proxy_to_transport;
524
525 for (cricket::TransportStatsMap::iterator transport_iter
526 = stats.transport_stats.begin();
527 transport_iter != stats.transport_stats.end(); ++transport_iter) {
wu@webrtc.org4551b792013-10-09 15:37:36 +0000528 // Attempt to get a copy of the certificates from the transport and
529 // expose them in stats reports. All channels in a transport share the
530 // same local and remote certificates.
531 std::string local_cert_report_id, remote_cert_report_id;
532 cricket::Transport* transport =
533 session_->GetTransport(transport_iter->second.content_name);
534 if (transport) {
535 talk_base::scoped_ptr<talk_base::SSLIdentity> identity;
536 if (transport->GetIdentity(identity.accept()))
537 local_cert_report_id = AddCertificateReports(
538 &(identity->certificate()));
539
540 talk_base::scoped_ptr<talk_base::SSLCertificate> cert;
541 if (transport->GetRemoteCertificate(cert.accept()))
542 remote_cert_report_id = AddCertificateReports(cert.get());
543 }
henrike@webrtc.org28e20752013-07-10 00:45:36 +0000544 for (cricket::TransportChannelStatsList::iterator channel_iter
545 = transport_iter->second.channel_stats.begin();
546 channel_iter != transport_iter->second.channel_stats.end();
547 ++channel_iter) {
548 StatsReport channel_report;
549 std::ostringstream ostc;
550 ostc << "Channel-" << transport_iter->second.content_name
551 << "-" << channel_iter->component;
552 channel_report.id = ostc.str();
553 channel_report.type = StatsReport::kStatsReportTypeComponent;
554 channel_report.timestamp = stats_gathering_started_;
555 channel_report.AddValue(StatsReport::kStatsValueNameComponent,
556 channel_iter->component);
wu@webrtc.org4551b792013-10-09 15:37:36 +0000557 if (!local_cert_report_id.empty())
558 channel_report.AddValue(
559 StatsReport::kStatsValueNameLocalCertificateId,
560 local_cert_report_id);
561 if (!remote_cert_report_id.empty())
562 channel_report.AddValue(
563 StatsReport::kStatsValueNameRemoteCertificateId,
564 remote_cert_report_id);
henrike@webrtc.org28e20752013-07-10 00:45:36 +0000565 reports_[channel_report.id] = channel_report;
566 for (size_t i = 0;
567 i < channel_iter->connection_infos.size();
568 ++i) {
569 StatsReport report;
570 const cricket::ConnectionInfo& info
571 = channel_iter->connection_infos[i];
572 std::ostringstream ost;
573 ost << "Conn-" << transport_iter->first << "-"
574 << channel_iter->component << "-" << i;
575 report.id = ost.str();
576 report.type = StatsReport::kStatsReportTypeCandidatePair;
577 report.timestamp = stats_gathering_started_;
578 // Link from connection to its containing channel.
579 report.AddValue(StatsReport::kStatsValueNameChannelId,
580 channel_report.id);
581 report.AddValue(StatsReport::kStatsValueNameBytesSent,
582 info.sent_total_bytes);
583 report.AddValue(StatsReport::kStatsValueNameBytesReceived,
584 info.recv_total_bytes);
585 report.AddBoolean(StatsReport::kStatsValueNameWritable,
586 info.writable);
587 report.AddBoolean(StatsReport::kStatsValueNameReadable,
588 info.readable);
589 report.AddBoolean(StatsReport::kStatsValueNameActiveConnection,
590 info.best_connection);
591 report.AddValue(StatsReport::kStatsValueNameLocalAddress,
592 info.local_candidate.address().ToString());
593 report.AddValue(StatsReport::kStatsValueNameRemoteAddress,
594 info.remote_candidate.address().ToString());
595 reports_[report.id] = report;
596 }
597 }
598 }
599 }
600}
601
602void StatsCollector::ExtractVoiceInfo() {
603 if (!session_->voice_channel()) {
604 return;
605 }
606 cricket::VoiceMediaInfo voice_info;
607 if (!session_->voice_channel()->GetStats(&voice_info)) {
608 LOG(LS_ERROR) << "Failed to get voice channel stats.";
609 return;
610 }
611 std::string transport_id;
612 if (!GetTransportIdFromProxy(session_->voice_channel()->content_name(),
613 &transport_id)) {
614 LOG(LS_ERROR) << "Failed to get transport name for proxy "
615 << session_->voice_channel()->content_name();
616 return;
617 }
618 ExtractStatsFromList(voice_info.receivers, transport_id, this);
619 ExtractStatsFromList(voice_info.senders, transport_id, this);
620}
621
622void StatsCollector::ExtractVideoInfo() {
623 if (!session_->video_channel()) {
624 return;
625 }
626 cricket::VideoMediaInfo video_info;
627 if (!session_->video_channel()->GetStats(&video_info)) {
628 LOG(LS_ERROR) << "Failed to get video channel stats.";
629 return;
630 }
631 std::string transport_id;
632 if (!GetTransportIdFromProxy(session_->video_channel()->content_name(),
633 &transport_id)) {
634 LOG(LS_ERROR) << "Failed to get transport name for proxy "
635 << session_->video_channel()->content_name();
636 return;
637 }
638 ExtractStatsFromList(video_info.receivers, transport_id, this);
639 ExtractStatsFromList(video_info.senders, transport_id, this);
640 if (video_info.bw_estimations.size() != 1) {
641 LOG(LS_ERROR) << "BWEs count: " << video_info.bw_estimations.size();
642 } else {
643 StatsReport* report = &reports_[StatsReport::kStatsReportVideoBweId];
644 ExtractStats(
645 video_info.bw_estimations[0], stats_gathering_started_, report);
646 }
647}
648
649double StatsCollector::GetTimeNow() {
650 return timing_.WallTimeNow() * talk_base::kNumMillisecsPerSec;
651}
652
653bool StatsCollector::GetTransportIdFromProxy(const std::string& proxy,
654 std::string* transport) {
655 // TODO(hta): Remove handling of empty proxy name once tests do not use it.
656 if (proxy.empty()) {
657 transport->clear();
658 return true;
659 }
660 if (proxy_to_transport_.find(proxy) == proxy_to_transport_.end()) {
661 LOG(LS_ERROR) << "No transport ID mapping for " << proxy;
662 return false;
663 }
664 std::ostringstream ost;
665 // Component 1 is always used for RTP.
666 ost << "Channel-" << proxy_to_transport_[proxy] << "-1";
667 *transport = ost.str();
668 return true;
669}
670
671} // namespace webrtc