blob: 18ab45993c1068b0144ee881705ccccd1a79d9cb [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";
sergeyu@chromium.org5bc25c42013-12-05 00:24:06 +000050const char StatsReport::kStatsValueNameAvgEncodeMs[] = "googAvgEncodeMs";
henrike@webrtc.org28e20752013-07-10 00:45:36 +000051const char StatsReport::kStatsValueNameBucketDelay[] = "googBucketDelay";
52const char StatsReport::kStatsValueNameBytesReceived[] = "bytesReceived";
53const char StatsReport::kStatsValueNameBytesSent[] = "bytesSent";
sergeyu@chromium.org5bc25c42013-12-05 00:24:06 +000054const char StatsReport::kStatsValueNameCaptureJitterMs[] =
55 "googCaptureJitterMs";
henrike@webrtc.org28e20752013-07-10 00:45:36 +000056const char StatsReport::kStatsValueNameChannelId[] = "googChannelId";
57const char StatsReport::kStatsValueNameCodecName[] = "googCodecName";
58const char StatsReport::kStatsValueNameComponent[] = "googComponent";
59const char StatsReport::kStatsValueNameContentName[] = "googContentName";
wu@webrtc.org4551b792013-10-09 15:37:36 +000060const char StatsReport::kStatsValueNameDer[] = "googDerBase64";
henrike@webrtc.org28e20752013-07-10 00:45:36 +000061// Echo metrics from the audio processing module.
62const char StatsReport::kStatsValueNameEchoCancellationQualityMin[] =
63 "googEchoCancellationQualityMin";
64const char StatsReport::kStatsValueNameEchoDelayMedian[] =
65 "googEchoCancellationEchoDelayMedian";
66const char StatsReport::kStatsValueNameEchoDelayStdDev[] =
67 "googEchoCancellationEchoDelayStdDev";
68const char StatsReport::kStatsValueNameEchoReturnLoss[] =
69 "googEchoCancellationReturnLoss";
70const char StatsReport::kStatsValueNameEchoReturnLossEnhancement[] =
71 "googEchoCancellationReturnLossEnhancement";
72
wu@webrtc.org4551b792013-10-09 15:37:36 +000073const char StatsReport::kStatsValueNameFingerprint[] = "googFingerprint";
mallinath@webrtc.org19f27e62013-10-13 17:18:27 +000074const char StatsReport::kStatsValueNameFingerprintAlgorithm[] =
75 "googFingerprintAlgorithm";
henrike@webrtc.org28e20752013-07-10 00:45:36 +000076const char StatsReport::kStatsValueNameFirsReceived[] = "googFirsReceived";
77const char StatsReport::kStatsValueNameFirsSent[] = "googFirsSent";
78const char StatsReport::kStatsValueNameFrameHeightReceived[] =
79 "googFrameHeightReceived";
80const char StatsReport::kStatsValueNameFrameHeightSent[] =
81 "googFrameHeightSent";
82const char StatsReport::kStatsValueNameFrameRateReceived[] =
83 "googFrameRateReceived";
84const char StatsReport::kStatsValueNameFrameRateDecoded[] =
85 "googFrameRateDecoded";
86const char StatsReport::kStatsValueNameFrameRateOutput[] =
87 "googFrameRateOutput";
wu@webrtc.org97077a32013-10-25 21:18:33 +000088const char StatsReport::kStatsValueNameDecodeMs[] = "googDecodeMs";
89const char StatsReport::kStatsValueNameMaxDecodeMs[] = "googMaxDecodeMs";
90const char StatsReport::kStatsValueNameCurrentDelayMs[] = "googCurrentDelayMs";
91const char StatsReport::kStatsValueNameTargetDelayMs[] = "googTargetDelayMs";
92const char StatsReport::kStatsValueNameJitterBufferMs[] = "googJitterBufferMs";
93const char StatsReport::kStatsValueNameMinPlayoutDelayMs[] =
94 "googMinPlayoutDelayMs";
95const char StatsReport::kStatsValueNameRenderDelayMs[] = "googRenderDelayMs";
96
henrike@webrtc.org28e20752013-07-10 00:45:36 +000097const char StatsReport::kStatsValueNameFrameRateInput[] = "googFrameRateInput";
98const char StatsReport::kStatsValueNameFrameRateSent[] = "googFrameRateSent";
99const char StatsReport::kStatsValueNameFrameWidthReceived[] =
100 "googFrameWidthReceived";
101const char StatsReport::kStatsValueNameFrameWidthSent[] = "googFrameWidthSent";
102const char StatsReport::kStatsValueNameInitiator[] = "googInitiator";
wu@webrtc.org4551b792013-10-09 15:37:36 +0000103const char StatsReport::kStatsValueNameIssuerId[] = "googIssuerId";
henrike@webrtc.org28e20752013-07-10 00:45:36 +0000104const char StatsReport::kStatsValueNameJitterReceived[] = "googJitterReceived";
105const char StatsReport::kStatsValueNameLocalAddress[] = "googLocalAddress";
wu@webrtc.org364f2042013-11-20 21:49:41 +0000106const char StatsReport::kStatsValueNameLocalCandidateType[] =
107 "googLocalCandidateType";
wu@webrtc.org4551b792013-10-09 15:37:36 +0000108const char StatsReport::kStatsValueNameLocalCertificateId[] =
109 "googLocalCertificateId";
henrike@webrtc.org28e20752013-07-10 00:45:36 +0000110const char StatsReport::kStatsValueNameNacksReceived[] = "googNacksReceived";
111const char StatsReport::kStatsValueNameNacksSent[] = "googNacksSent";
112const char StatsReport::kStatsValueNamePacketsReceived[] = "packetsReceived";
113const char StatsReport::kStatsValueNamePacketsSent[] = "packetsSent";
114const char StatsReport::kStatsValueNamePacketsLost[] = "packetsLost";
115const char StatsReport::kStatsValueNameReadable[] = "googReadable";
116const char StatsReport::kStatsValueNameRemoteAddress[] = "googRemoteAddress";
wu@webrtc.org364f2042013-11-20 21:49:41 +0000117const char StatsReport::kStatsValueNameRemoteCandidateType[] =
118 "googRemoteCandidateType";
wu@webrtc.org4551b792013-10-09 15:37:36 +0000119const char StatsReport::kStatsValueNameRemoteCertificateId[] =
120 "googRemoteCertificateId";
henrike@webrtc.org28e20752013-07-10 00:45:36 +0000121const char StatsReport::kStatsValueNameRetransmitBitrate[] =
122 "googRetransmitBitrate";
123const char StatsReport::kStatsValueNameRtt[] = "googRtt";
wu@webrtc.org967bfff2013-09-19 05:49:50 +0000124const char StatsReport::kStatsValueNameSsrc[] = "ssrc";
henrike@webrtc.org28e20752013-07-10 00:45:36 +0000125const char StatsReport::kStatsValueNameTargetEncBitrate[] =
126 "googTargetEncBitrate";
127const char StatsReport::kStatsValueNameTransmitBitrate[] =
128 "googTransmitBitrate";
129const char StatsReport::kStatsValueNameTransportId[] = "transportId";
130const char StatsReport::kStatsValueNameTransportType[] = "googTransportType";
131const char StatsReport::kStatsValueNameTrackId[] = "googTrackId";
wu@webrtc.org967bfff2013-09-19 05:49:50 +0000132const char StatsReport::kStatsValueNameTypingNoiseState[] =
133 "googTypingNoiseState";
henrike@webrtc.org28e20752013-07-10 00:45:36 +0000134const char StatsReport::kStatsValueNameWritable[] = "googWritable";
135
136const char StatsReport::kStatsReportTypeSession[] = "googLibjingleSession";
137const char StatsReport::kStatsReportTypeBwe[] = "VideoBwe";
wu@webrtc.org97077a32013-10-25 21:18:33 +0000138const char StatsReport::kStatsReportTypeRemoteSsrc[] = "remoteSsrc";
henrike@webrtc.org28e20752013-07-10 00:45:36 +0000139const char StatsReport::kStatsReportTypeSsrc[] = "ssrc";
140const char StatsReport::kStatsReportTypeTrack[] = "googTrack";
141const char StatsReport::kStatsReportTypeIceCandidate[] = "iceCandidate";
142const char StatsReport::kStatsReportTypeTransport[] = "googTransport";
143const char StatsReport::kStatsReportTypeComponent[] = "googComponent";
144const char StatsReport::kStatsReportTypeCandidatePair[] = "googCandidatePair";
wu@webrtc.org4551b792013-10-09 15:37:36 +0000145const char StatsReport::kStatsReportTypeCertificate[] = "googCertificate";
henrike@webrtc.org28e20752013-07-10 00:45:36 +0000146
147const char StatsReport::kStatsReportVideoBweId[] = "bweforvideo";
148
wu@webrtc.org967bfff2013-09-19 05:49:50 +0000149
henrike@webrtc.org28e20752013-07-10 00:45:36 +0000150// Implementations of functions in statstypes.h
151void StatsReport::AddValue(const std::string& name, const std::string& value) {
152 Value temp;
153 temp.name = name;
154 temp.value = value;
155 values.push_back(temp);
156}
157
158void StatsReport::AddValue(const std::string& name, int64 value) {
159 AddValue(name, talk_base::ToString<int64>(value));
160}
161
162void StatsReport::AddBoolean(const std::string& name, bool value) {
163 AddValue(name, value ? "true" : "false");
164}
165
166namespace {
167typedef std::map<std::string, StatsReport> StatsMap;
168
169std::string StatsId(const std::string& type, const std::string& id) {
170 return type + "_" + id;
171}
172
173bool ExtractValueFromReport(
174 const StatsReport& report,
175 const std::string& name,
176 std::string* value) {
177 StatsReport::Values::const_iterator it = report.values.begin();
178 for (; it != report.values.end(); ++it) {
179 if (it->name == name) {
180 *value = it->value;
181 return true;
182 }
183 }
184 return false;
185}
186
187template <class TrackVector>
188void CreateTrackReports(const TrackVector& tracks, StatsMap* reports) {
189 for (size_t j = 0; j < tracks.size(); ++j) {
190 webrtc::MediaStreamTrackInterface* track = tracks[j];
191 // Adds an empty track report.
192 StatsReport report;
193 report.type = StatsReport::kStatsReportTypeTrack;
194 report.id = StatsId(StatsReport::kStatsReportTypeTrack, track->id());
195 report.AddValue(StatsReport::kStatsValueNameTrackId,
196 track->id());
197 (*reports)[report.id] = report;
198 }
199}
200
201void ExtractStats(const cricket::VoiceReceiverInfo& info, StatsReport* report) {
202 report->AddValue(StatsReport::kStatsValueNameAudioOutputLevel,
203 info.audio_level);
204 report->AddValue(StatsReport::kStatsValueNameBytesReceived,
205 info.bytes_rcvd);
206 report->AddValue(StatsReport::kStatsValueNameJitterReceived,
207 info.jitter_ms);
208 report->AddValue(StatsReport::kStatsValueNamePacketsReceived,
209 info.packets_rcvd);
210 report->AddValue(StatsReport::kStatsValueNamePacketsLost,
211 info.packets_lost);
212}
213
214void ExtractStats(const cricket::VoiceSenderInfo& info, StatsReport* report) {
215 report->AddValue(StatsReport::kStatsValueNameAudioInputLevel,
216 info.audio_level);
217 report->AddValue(StatsReport::kStatsValueNameBytesSent,
218 info.bytes_sent);
219 report->AddValue(StatsReport::kStatsValueNamePacketsSent,
220 info.packets_sent);
221 report->AddValue(StatsReport::kStatsValueNameJitterReceived,
222 info.jitter_ms);
223 report->AddValue(StatsReport::kStatsValueNameRtt, info.rtt_ms);
224 report->AddValue(StatsReport::kStatsValueNameEchoCancellationQualityMin,
225 talk_base::ToString<float>(info.aec_quality_min));
226 report->AddValue(StatsReport::kStatsValueNameEchoDelayMedian,
227 info.echo_delay_median_ms);
228 report->AddValue(StatsReport::kStatsValueNameEchoDelayStdDev,
229 info.echo_delay_std_ms);
230 report->AddValue(StatsReport::kStatsValueNameEchoReturnLoss,
231 info.echo_return_loss);
232 report->AddValue(StatsReport::kStatsValueNameEchoReturnLossEnhancement,
233 info.echo_return_loss_enhancement);
234 report->AddValue(StatsReport::kStatsValueNameCodecName, info.codec_name);
wu@webrtc.org967bfff2013-09-19 05:49:50 +0000235 report->AddBoolean(StatsReport::kStatsValueNameTypingNoiseState,
236 info.typing_noise_detected);
henrike@webrtc.org28e20752013-07-10 00:45:36 +0000237}
238
239void ExtractStats(const cricket::VideoReceiverInfo& info, StatsReport* report) {
240 report->AddValue(StatsReport::kStatsValueNameBytesReceived,
241 info.bytes_rcvd);
242 report->AddValue(StatsReport::kStatsValueNamePacketsReceived,
243 info.packets_rcvd);
244 report->AddValue(StatsReport::kStatsValueNamePacketsLost,
245 info.packets_lost);
246
247 report->AddValue(StatsReport::kStatsValueNameFirsSent,
248 info.firs_sent);
249 report->AddValue(StatsReport::kStatsValueNameNacksSent,
250 info.nacks_sent);
251 report->AddValue(StatsReport::kStatsValueNameFrameWidthReceived,
252 info.frame_width);
253 report->AddValue(StatsReport::kStatsValueNameFrameHeightReceived,
254 info.frame_height);
255 report->AddValue(StatsReport::kStatsValueNameFrameRateReceived,
256 info.framerate_rcvd);
257 report->AddValue(StatsReport::kStatsValueNameFrameRateDecoded,
258 info.framerate_decoded);
259 report->AddValue(StatsReport::kStatsValueNameFrameRateOutput,
260 info.framerate_output);
wu@webrtc.org97077a32013-10-25 21:18:33 +0000261
262 report->AddValue(StatsReport::kStatsValueNameDecodeMs,
263 info.decode_ms);
264 report->AddValue(StatsReport::kStatsValueNameMaxDecodeMs,
265 info.max_decode_ms);
266 report->AddValue(StatsReport::kStatsValueNameCurrentDelayMs,
267 info.current_delay_ms);
268 report->AddValue(StatsReport::kStatsValueNameTargetDelayMs,
269 info.target_delay_ms);
270 report->AddValue(StatsReport::kStatsValueNameJitterBufferMs,
271 info.jitter_buffer_ms);
272 report->AddValue(StatsReport::kStatsValueNameMinPlayoutDelayMs,
273 info.min_playout_delay_ms);
274 report->AddValue(StatsReport::kStatsValueNameRenderDelayMs,
275 info.render_delay_ms);
henrike@webrtc.org28e20752013-07-10 00:45:36 +0000276}
277
278void ExtractStats(const cricket::VideoSenderInfo& info, StatsReport* report) {
279 report->AddValue(StatsReport::kStatsValueNameBytesSent,
280 info.bytes_sent);
281 report->AddValue(StatsReport::kStatsValueNamePacketsSent,
282 info.packets_sent);
283
284 report->AddValue(StatsReport::kStatsValueNameFirsReceived,
285 info.firs_rcvd);
286 report->AddValue(StatsReport::kStatsValueNameNacksReceived,
287 info.nacks_rcvd);
288 report->AddValue(StatsReport::kStatsValueNameFrameWidthSent,
289 info.frame_width);
290 report->AddValue(StatsReport::kStatsValueNameFrameHeightSent,
291 info.frame_height);
292 report->AddValue(StatsReport::kStatsValueNameFrameRateInput,
293 info.framerate_input);
294 report->AddValue(StatsReport::kStatsValueNameFrameRateSent,
295 info.framerate_sent);
296 report->AddValue(StatsReport::kStatsValueNameRtt, info.rtt_ms);
297 report->AddValue(StatsReport::kStatsValueNameCodecName, info.codec_name);
sergeyu@chromium.org5bc25c42013-12-05 00:24:06 +0000298 report->AddValue(StatsReport::kStatsValueNameAvgEncodeMs, info.avg_encode_ms);
299 report->AddValue(StatsReport::kStatsValueNameCaptureJitterMs,
300 info.capture_jitter_ms);
henrike@webrtc.org28e20752013-07-10 00:45:36 +0000301}
302
303void ExtractStats(const cricket::BandwidthEstimationInfo& info,
304 double stats_gathering_started,
305 StatsReport* report) {
306 report->id = StatsReport::kStatsReportVideoBweId;
307 report->type = StatsReport::kStatsReportTypeBwe;
308
309 // Clear out stats from previous GatherStats calls if any.
310 if (report->timestamp != stats_gathering_started) {
311 report->values.clear();
312 report->timestamp = stats_gathering_started;
313 }
314
315 report->AddValue(StatsReport::kStatsValueNameAvailableSendBandwidth,
316 info.available_send_bandwidth);
317 report->AddValue(StatsReport::kStatsValueNameAvailableReceiveBandwidth,
318 info.available_recv_bandwidth);
319 report->AddValue(StatsReport::kStatsValueNameTargetEncBitrate,
320 info.target_enc_bitrate);
321 report->AddValue(StatsReport::kStatsValueNameActualEncBitrate,
322 info.actual_enc_bitrate);
323 report->AddValue(StatsReport::kStatsValueNameRetransmitBitrate,
324 info.retransmit_bitrate);
325 report->AddValue(StatsReport::kStatsValueNameTransmitBitrate,
326 info.transmit_bitrate);
327 report->AddValue(StatsReport::kStatsValueNameBucketDelay,
328 info.bucket_delay);
329}
330
wu@webrtc.org97077a32013-10-25 21:18:33 +0000331void ExtractRemoteStats(const cricket::MediaSenderInfo& info,
332 StatsReport* report) {
333 report->timestamp = info.remote_stats[0].timestamp;
334 // TODO(hta): Extract some stats here.
335}
336
337void ExtractRemoteStats(const cricket::MediaReceiverInfo& info,
338 StatsReport* report) {
339 report->timestamp = info.remote_stats[0].timestamp;
340 // TODO(hta): Extract some stats here.
341}
342
henrike@webrtc.org28e20752013-07-10 00:45:36 +0000343// Template to extract stats from a data vector.
sergeyu@chromium.org5bc25c42013-12-05 00:24:06 +0000344// In order to use the template, the functions that are called from it,
345// ExtractStats and ExtractRemoteStats, must be defined and overloaded
346// for each type.
henrike@webrtc.org28e20752013-07-10 00:45:36 +0000347template<typename T>
348void ExtractStatsFromList(const std::vector<T>& data,
349 const std::string& transport_id,
350 StatsCollector* collector) {
351 typename std::vector<T>::const_iterator it = data.begin();
352 for (; it != data.end(); ++it) {
353 std::string id;
sergeyu@chromium.org5bc25c42013-12-05 00:24:06 +0000354 uint32 ssrc = it->ssrc();
wu@webrtc.org97077a32013-10-25 21:18:33 +0000355 // Each object can result in 2 objects, a local and a remote object.
356 // TODO(hta): Handle the case of multiple SSRCs per object.
357 StatsReport* report = collector->PrepareLocalReport(ssrc, transport_id);
henrike@webrtc.org28e20752013-07-10 00:45:36 +0000358 if (!report) {
359 continue;
360 }
361 ExtractStats(*it, report);
wu@webrtc.org97077a32013-10-25 21:18:33 +0000362 if (it->remote_stats.size() > 0) {
363 report = collector->PrepareRemoteReport(ssrc, transport_id);
364 if (!report) {
365 continue;
366 }
367 ExtractRemoteStats(*it, report);
368 }
henrike@webrtc.org28e20752013-07-10 00:45:36 +0000369 }
370};
371
372} // namespace
373
374StatsCollector::StatsCollector()
375 : session_(NULL), stats_gathering_started_(0) {
376}
377
378// Adds a MediaStream with tracks that can be used as a |selector| in a call
379// to GetStats.
380void StatsCollector::AddStream(MediaStreamInterface* stream) {
381 ASSERT(stream != NULL);
382
383 CreateTrackReports<AudioTrackVector>(stream->GetAudioTracks(),
384 &reports_);
385 CreateTrackReports<VideoTrackVector>(stream->GetVideoTracks(),
386 &reports_);
387}
388
389bool StatsCollector::GetStats(MediaStreamTrackInterface* track,
390 StatsReports* reports) {
391 ASSERT(reports != NULL);
392 reports->clear();
393
394 StatsMap::iterator it;
395 if (!track) {
396 for (it = reports_.begin(); it != reports_.end(); ++it) {
397 reports->push_back(it->second);
398 }
399 return true;
400 }
401
402 it = reports_.find(StatsId(StatsReport::kStatsReportTypeSession,
403 session_->id()));
404 if (it != reports_.end()) {
405 reports->push_back(it->second);
406 }
407
408 it = reports_.find(StatsId(StatsReport::kStatsReportTypeTrack, track->id()));
409
410 if (it == reports_.end()) {
411 LOG(LS_WARNING) << "No StatsReport is available for "<< track->id();
412 return false;
413 }
414
415 reports->push_back(it->second);
416
417 std::string track_id;
418 for (it = reports_.begin(); it != reports_.end(); ++it) {
419 if (it->second.type != StatsReport::kStatsReportTypeSsrc) {
420 continue;
421 }
422 if (ExtractValueFromReport(it->second,
423 StatsReport::kStatsValueNameTrackId,
424 &track_id)) {
425 if (track_id == track->id()) {
426 reports->push_back(it->second);
427 }
428 }
429 }
430
431 return true;
432}
433
434void StatsCollector::UpdateStats() {
435 double time_now = GetTimeNow();
436 // Calls to UpdateStats() that occur less than kMinGatherStatsPeriod number of
437 // ms apart will be ignored.
438 const double kMinGatherStatsPeriod = 50;
439 if (stats_gathering_started_ + kMinGatherStatsPeriod > time_now) {
440 return;
441 }
442 stats_gathering_started_ = time_now;
443
444 if (session_) {
445 ExtractSessionInfo();
446 ExtractVoiceInfo();
447 ExtractVideoInfo();
448 }
449}
450
wu@webrtc.org97077a32013-10-25 21:18:33 +0000451StatsReport* StatsCollector::PrepareLocalReport(
452 uint32 ssrc,
453 const std::string& transport_id) {
henrike@webrtc.org28e20752013-07-10 00:45:36 +0000454 std::string ssrc_id = talk_base::ToString<uint32>(ssrc);
455 StatsMap::iterator it = reports_.find(StatsId(
456 StatsReport::kStatsReportTypeSsrc, ssrc_id));
457
458 std::string track_id;
459 if (it == reports_.end()) {
460 if (!session()->GetTrackIdBySsrc(ssrc, &track_id)) {
sergeyu@chromium.orga59696b2013-09-13 23:48:58 +0000461 LOG(LS_WARNING) << "The SSRC " << ssrc
462 << " is not associated with a track";
henrike@webrtc.org28e20752013-07-10 00:45:36 +0000463 return NULL;
464 }
465 } else {
466 // Keeps the old track id since we want to report the stats for inactive
467 // tracks.
468 ExtractValueFromReport(it->second,
469 StatsReport::kStatsValueNameTrackId,
470 &track_id);
471 }
472
wu@webrtc.org97077a32013-10-25 21:18:33 +0000473 StatsReport* report = GetOrCreateReport(StatsReport::kStatsReportTypeSsrc,
474 ssrc_id);
henrike@webrtc.org28e20752013-07-10 00:45:36 +0000475
476 // Clear out stats from previous GatherStats calls if any.
477 if (report->timestamp != stats_gathering_started_) {
478 report->values.clear();
479 report->timestamp = stats_gathering_started_;
480 }
481
482 report->AddValue(StatsReport::kStatsValueNameSsrc, ssrc_id);
483 report->AddValue(StatsReport::kStatsValueNameTrackId, track_id);
484 // Add the mapping of SSRC to transport.
485 report->AddValue(StatsReport::kStatsValueNameTransportId,
486 transport_id);
487 return report;
488}
489
wu@webrtc.org97077a32013-10-25 21:18:33 +0000490StatsReport* StatsCollector::PrepareRemoteReport(
491 uint32 ssrc,
492 const std::string& transport_id) {
493 std::string ssrc_id = talk_base::ToString<uint32>(ssrc);
494 StatsMap::iterator it = reports_.find(StatsId(
495 StatsReport::kStatsReportTypeRemoteSsrc, ssrc_id));
496
497 std::string track_id;
498 if (it == reports_.end()) {
499 if (!session()->GetTrackIdBySsrc(ssrc, &track_id)) {
500 LOG(LS_WARNING) << "The SSRC " << ssrc
501 << " is not associated with a track";
502 return NULL;
503 }
504 } else {
505 // Keeps the old track id since we want to report the stats for inactive
506 // tracks.
507 ExtractValueFromReport(it->second,
508 StatsReport::kStatsValueNameTrackId,
509 &track_id);
510 }
511
512 StatsReport* report = GetOrCreateReport(
513 StatsReport::kStatsReportTypeRemoteSsrc, ssrc_id);
514
515 // Clear out stats from previous GatherStats calls if any.
516 // The timestamp will be added later. Zero it for debugging.
517 report->values.clear();
518 report->timestamp = 0;
519
520 report->AddValue(StatsReport::kStatsValueNameSsrc, ssrc_id);
521 report->AddValue(StatsReport::kStatsValueNameTrackId, track_id);
522 // Add the mapping of SSRC to transport.
523 report->AddValue(StatsReport::kStatsValueNameTransportId,
524 transport_id);
525 return report;
526}
527
wu@webrtc.org4551b792013-10-09 15:37:36 +0000528std::string StatsCollector::AddOneCertificateReport(
529 const talk_base::SSLCertificate* cert, const std::string& issuer_id) {
530 // TODO(bemasc): Move this computation to a helper class that caches these
531 // values to reduce CPU use in GetStats. This will require adding a fast
532 // SSLCertificate::Equals() method to detect certificate changes.
mallinath@webrtc.org19f27e62013-10-13 17:18:27 +0000533
534 std::string digest_algorithm;
535 if (!cert->GetSignatureDigestAlgorithm(&digest_algorithm))
536 return std::string();
537
wu@webrtc.org4551b792013-10-09 15:37:36 +0000538 talk_base::scoped_ptr<talk_base::SSLFingerprint> ssl_fingerprint(
mallinath@webrtc.org19f27e62013-10-13 17:18:27 +0000539 talk_base::SSLFingerprint::Create(digest_algorithm, cert));
wu@webrtc.org4551b792013-10-09 15:37:36 +0000540 std::string fingerprint = ssl_fingerprint->GetRfc4572Fingerprint();
541
542 talk_base::Buffer der_buffer;
543 cert->ToDER(&der_buffer);
544 std::string der_base64;
545 talk_base::Base64::EncodeFromArray(
546 der_buffer.data(), der_buffer.length(), &der_base64);
547
548 StatsReport report;
549 report.type = StatsReport::kStatsReportTypeCertificate;
550 report.id = StatsId(report.type, fingerprint);
551 report.timestamp = stats_gathering_started_;
552 report.AddValue(StatsReport::kStatsValueNameFingerprint, fingerprint);
mallinath@webrtc.org19f27e62013-10-13 17:18:27 +0000553 report.AddValue(StatsReport::kStatsValueNameFingerprintAlgorithm,
554 digest_algorithm);
wu@webrtc.org4551b792013-10-09 15:37:36 +0000555 report.AddValue(StatsReport::kStatsValueNameDer, der_base64);
556 if (!issuer_id.empty())
557 report.AddValue(StatsReport::kStatsValueNameIssuerId, issuer_id);
558 reports_[report.id] = report;
559 return report.id;
560}
561
562std::string StatsCollector::AddCertificateReports(
563 const talk_base::SSLCertificate* cert) {
564 // Produces a chain of StatsReports representing this certificate and the rest
565 // of its chain, and adds those reports to |reports_|. The return value is
566 // the id of the leaf report. The provided cert must be non-null, so at least
567 // one report will always be provided and the returned string will never be
568 // empty.
569 ASSERT(cert != NULL);
570
571 std::string issuer_id;
572 talk_base::scoped_ptr<talk_base::SSLCertChain> chain;
573 if (cert->GetChain(chain.accept())) {
574 // This loop runs in reverse, i.e. from root to leaf, so that each
575 // certificate's issuer's report ID is known before the child certificate's
576 // report is generated. The root certificate does not have an issuer ID
577 // value.
578 for (ptrdiff_t i = chain->GetSize() - 1; i >= 0; --i) {
579 const talk_base::SSLCertificate& cert_i = chain->Get(i);
580 issuer_id = AddOneCertificateReport(&cert_i, issuer_id);
581 }
582 }
583 // Add the leaf certificate.
584 return AddOneCertificateReport(cert, issuer_id);
585}
586
henrike@webrtc.org28e20752013-07-10 00:45:36 +0000587void StatsCollector::ExtractSessionInfo() {
588 // Extract information from the base session.
589 StatsReport report;
590 report.id = StatsId(StatsReport::kStatsReportTypeSession, session_->id());
591 report.type = StatsReport::kStatsReportTypeSession;
592 report.timestamp = stats_gathering_started_;
593 report.values.clear();
594 report.AddBoolean(StatsReport::kStatsValueNameInitiator,
595 session_->initiator());
596
597 reports_[report.id] = report;
598
599 cricket::SessionStats stats;
600 if (session_->GetStats(&stats)) {
601 // Store the proxy map away for use in SSRC reporting.
602 proxy_to_transport_ = stats.proxy_to_transport;
603
604 for (cricket::TransportStatsMap::iterator transport_iter
605 = stats.transport_stats.begin();
606 transport_iter != stats.transport_stats.end(); ++transport_iter) {
wu@webrtc.org4551b792013-10-09 15:37:36 +0000607 // Attempt to get a copy of the certificates from the transport and
608 // expose them in stats reports. All channels in a transport share the
609 // same local and remote certificates.
610 std::string local_cert_report_id, remote_cert_report_id;
611 cricket::Transport* transport =
612 session_->GetTransport(transport_iter->second.content_name);
613 if (transport) {
614 talk_base::scoped_ptr<talk_base::SSLIdentity> identity;
615 if (transport->GetIdentity(identity.accept()))
616 local_cert_report_id = AddCertificateReports(
617 &(identity->certificate()));
618
619 talk_base::scoped_ptr<talk_base::SSLCertificate> cert;
620 if (transport->GetRemoteCertificate(cert.accept()))
621 remote_cert_report_id = AddCertificateReports(cert.get());
622 }
henrike@webrtc.org28e20752013-07-10 00:45:36 +0000623 for (cricket::TransportChannelStatsList::iterator channel_iter
624 = transport_iter->second.channel_stats.begin();
625 channel_iter != transport_iter->second.channel_stats.end();
626 ++channel_iter) {
627 StatsReport channel_report;
628 std::ostringstream ostc;
629 ostc << "Channel-" << transport_iter->second.content_name
630 << "-" << channel_iter->component;
631 channel_report.id = ostc.str();
632 channel_report.type = StatsReport::kStatsReportTypeComponent;
633 channel_report.timestamp = stats_gathering_started_;
634 channel_report.AddValue(StatsReport::kStatsValueNameComponent,
635 channel_iter->component);
wu@webrtc.org4551b792013-10-09 15:37:36 +0000636 if (!local_cert_report_id.empty())
637 channel_report.AddValue(
638 StatsReport::kStatsValueNameLocalCertificateId,
639 local_cert_report_id);
640 if (!remote_cert_report_id.empty())
641 channel_report.AddValue(
642 StatsReport::kStatsValueNameRemoteCertificateId,
643 remote_cert_report_id);
henrike@webrtc.org28e20752013-07-10 00:45:36 +0000644 reports_[channel_report.id] = channel_report;
645 for (size_t i = 0;
646 i < channel_iter->connection_infos.size();
647 ++i) {
648 StatsReport report;
649 const cricket::ConnectionInfo& info
650 = channel_iter->connection_infos[i];
651 std::ostringstream ost;
652 ost << "Conn-" << transport_iter->first << "-"
653 << channel_iter->component << "-" << i;
654 report.id = ost.str();
655 report.type = StatsReport::kStatsReportTypeCandidatePair;
656 report.timestamp = stats_gathering_started_;
657 // Link from connection to its containing channel.
658 report.AddValue(StatsReport::kStatsValueNameChannelId,
659 channel_report.id);
660 report.AddValue(StatsReport::kStatsValueNameBytesSent,
661 info.sent_total_bytes);
662 report.AddValue(StatsReport::kStatsValueNameBytesReceived,
663 info.recv_total_bytes);
664 report.AddBoolean(StatsReport::kStatsValueNameWritable,
665 info.writable);
666 report.AddBoolean(StatsReport::kStatsValueNameReadable,
667 info.readable);
668 report.AddBoolean(StatsReport::kStatsValueNameActiveConnection,
669 info.best_connection);
670 report.AddValue(StatsReport::kStatsValueNameLocalAddress,
671 info.local_candidate.address().ToString());
672 report.AddValue(StatsReport::kStatsValueNameRemoteAddress,
673 info.remote_candidate.address().ToString());
wu@webrtc.org97077a32013-10-25 21:18:33 +0000674 report.AddValue(StatsReport::kStatsValueNameRtt, info.rtt);
675 report.AddValue(StatsReport::kStatsValueNameTransportType,
676 info.local_candidate.protocol());
wu@webrtc.org364f2042013-11-20 21:49:41 +0000677 report.AddValue(StatsReport::kStatsValueNameLocalCandidateType,
678 info.local_candidate.type());
679 report.AddValue(StatsReport::kStatsValueNameRemoteCandidateType,
680 info.remote_candidate.type());
henrike@webrtc.org28e20752013-07-10 00:45:36 +0000681 reports_[report.id] = report;
682 }
683 }
684 }
685 }
686}
687
688void StatsCollector::ExtractVoiceInfo() {
689 if (!session_->voice_channel()) {
690 return;
691 }
692 cricket::VoiceMediaInfo voice_info;
693 if (!session_->voice_channel()->GetStats(&voice_info)) {
694 LOG(LS_ERROR) << "Failed to get voice channel stats.";
695 return;
696 }
697 std::string transport_id;
698 if (!GetTransportIdFromProxy(session_->voice_channel()->content_name(),
699 &transport_id)) {
700 LOG(LS_ERROR) << "Failed to get transport name for proxy "
701 << session_->voice_channel()->content_name();
702 return;
703 }
704 ExtractStatsFromList(voice_info.receivers, transport_id, this);
705 ExtractStatsFromList(voice_info.senders, transport_id, this);
706}
707
708void StatsCollector::ExtractVideoInfo() {
709 if (!session_->video_channel()) {
710 return;
711 }
712 cricket::VideoMediaInfo video_info;
713 if (!session_->video_channel()->GetStats(&video_info)) {
714 LOG(LS_ERROR) << "Failed to get video channel stats.";
715 return;
716 }
717 std::string transport_id;
718 if (!GetTransportIdFromProxy(session_->video_channel()->content_name(),
719 &transport_id)) {
720 LOG(LS_ERROR) << "Failed to get transport name for proxy "
721 << session_->video_channel()->content_name();
722 return;
723 }
724 ExtractStatsFromList(video_info.receivers, transport_id, this);
725 ExtractStatsFromList(video_info.senders, transport_id, this);
726 if (video_info.bw_estimations.size() != 1) {
727 LOG(LS_ERROR) << "BWEs count: " << video_info.bw_estimations.size();
728 } else {
729 StatsReport* report = &reports_[StatsReport::kStatsReportVideoBweId];
730 ExtractStats(
731 video_info.bw_estimations[0], stats_gathering_started_, report);
732 }
733}
734
735double StatsCollector::GetTimeNow() {
736 return timing_.WallTimeNow() * talk_base::kNumMillisecsPerSec;
737}
738
739bool StatsCollector::GetTransportIdFromProxy(const std::string& proxy,
740 std::string* transport) {
741 // TODO(hta): Remove handling of empty proxy name once tests do not use it.
742 if (proxy.empty()) {
743 transport->clear();
744 return true;
745 }
746 if (proxy_to_transport_.find(proxy) == proxy_to_transport_.end()) {
747 LOG(LS_ERROR) << "No transport ID mapping for " << proxy;
748 return false;
749 }
750 std::ostringstream ost;
751 // Component 1 is always used for RTP.
752 ost << "Channel-" << proxy_to_transport_[proxy] << "-1";
753 *transport = ost.str();
754 return true;
755}
756
wu@webrtc.org97077a32013-10-25 21:18:33 +0000757StatsReport* StatsCollector::GetOrCreateReport(const std::string& type,
758 const std::string& id) {
759 std::string statsid = StatsId(type, id);
760 StatsReport* report = NULL;
761 std::map<std::string, StatsReport>::iterator it = reports_.find(statsid);
762 if (it == reports_.end()) {
763 report = &reports_[statsid]; // Create new element.
764 report->id = statsid;
765 report->type = type;
766 } else {
sergeyu@chromium.org5bc25c42013-12-05 00:24:06 +0000767 report = &(it->second);
wu@webrtc.org97077a32013-10-25 21:18:33 +0000768 }
769 return report;
770}
771
henrike@webrtc.org28e20752013-07-10 00:45:36 +0000772} // namespace webrtc