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