blob: b994f2faad724323bca3b8ba555dd3a6fd7c587b [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
33#include "talk/session/media/channel.h"
34
35namespace webrtc {
36
37// The items below are in alphabetical order.
38const char StatsReport::kStatsValueNameActiveConnection[] =
39 "googActiveConnection";
40const char StatsReport::kStatsValueNameActualEncBitrate[] =
41 "googActualEncBitrate";
42const char StatsReport::kStatsValueNameAudioOutputLevel[] = "audioOutputLevel";
43const char StatsReport::kStatsValueNameAudioInputLevel[] = "audioInputLevel";
44const char StatsReport::kStatsValueNameAvailableReceiveBandwidth[] =
45 "googAvailableReceiveBandwidth";
46const char StatsReport::kStatsValueNameAvailableSendBandwidth[] =
47 "googAvailableSendBandwidth";
48const char StatsReport::kStatsValueNameBucketDelay[] = "googBucketDelay";
49const char StatsReport::kStatsValueNameBytesReceived[] = "bytesReceived";
50const char StatsReport::kStatsValueNameBytesSent[] = "bytesSent";
51const char StatsReport::kStatsValueNameChannelId[] = "googChannelId";
52const char StatsReport::kStatsValueNameCodecName[] = "googCodecName";
53const char StatsReport::kStatsValueNameComponent[] = "googComponent";
54const char StatsReport::kStatsValueNameContentName[] = "googContentName";
55// Echo metrics from the audio processing module.
56const char StatsReport::kStatsValueNameEchoCancellationQualityMin[] =
57 "googEchoCancellationQualityMin";
58const char StatsReport::kStatsValueNameEchoDelayMedian[] =
59 "googEchoCancellationEchoDelayMedian";
60const char StatsReport::kStatsValueNameEchoDelayStdDev[] =
61 "googEchoCancellationEchoDelayStdDev";
62const char StatsReport::kStatsValueNameEchoReturnLoss[] =
63 "googEchoCancellationReturnLoss";
64const char StatsReport::kStatsValueNameEchoReturnLossEnhancement[] =
65 "googEchoCancellationReturnLossEnhancement";
66
67const char StatsReport::kStatsValueNameFirsReceived[] = "googFirsReceived";
68const char StatsReport::kStatsValueNameFirsSent[] = "googFirsSent";
69const char StatsReport::kStatsValueNameFrameHeightReceived[] =
70 "googFrameHeightReceived";
71const char StatsReport::kStatsValueNameFrameHeightSent[] =
72 "googFrameHeightSent";
73const char StatsReport::kStatsValueNameFrameRateReceived[] =
74 "googFrameRateReceived";
75const char StatsReport::kStatsValueNameFrameRateDecoded[] =
76 "googFrameRateDecoded";
77const char StatsReport::kStatsValueNameFrameRateOutput[] =
78 "googFrameRateOutput";
79const char StatsReport::kStatsValueNameFrameRateInput[] = "googFrameRateInput";
80const char StatsReport::kStatsValueNameFrameRateSent[] = "googFrameRateSent";
81const char StatsReport::kStatsValueNameFrameWidthReceived[] =
82 "googFrameWidthReceived";
83const char StatsReport::kStatsValueNameFrameWidthSent[] = "googFrameWidthSent";
84const char StatsReport::kStatsValueNameInitiator[] = "googInitiator";
85const char StatsReport::kStatsValueNameJitterReceived[] = "googJitterReceived";
86const char StatsReport::kStatsValueNameLocalAddress[] = "googLocalAddress";
87const char StatsReport::kStatsValueNameNacksReceived[] = "googNacksReceived";
88const char StatsReport::kStatsValueNameNacksSent[] = "googNacksSent";
89const char StatsReport::kStatsValueNamePacketsReceived[] = "packetsReceived";
90const char StatsReport::kStatsValueNamePacketsSent[] = "packetsSent";
91const char StatsReport::kStatsValueNamePacketsLost[] = "packetsLost";
92const char StatsReport::kStatsValueNameReadable[] = "googReadable";
93const char StatsReport::kStatsValueNameRemoteAddress[] = "googRemoteAddress";
94const char StatsReport::kStatsValueNameRetransmitBitrate[] =
95 "googRetransmitBitrate";
96const char StatsReport::kStatsValueNameRtt[] = "googRtt";
97const char StatsReport::kStatsValueNameTargetEncBitrate[] =
98 "googTargetEncBitrate";
99const char StatsReport::kStatsValueNameTransmitBitrate[] =
100 "googTransmitBitrate";
101const char StatsReport::kStatsValueNameTransportId[] = "transportId";
102const char StatsReport::kStatsValueNameTransportType[] = "googTransportType";
103const char StatsReport::kStatsValueNameTrackId[] = "googTrackId";
104const char StatsReport::kStatsValueNameSsrc[] = "ssrc";
105const char StatsReport::kStatsValueNameWritable[] = "googWritable";
106
107const char StatsReport::kStatsReportTypeSession[] = "googLibjingleSession";
108const char StatsReport::kStatsReportTypeBwe[] = "VideoBwe";
109const char StatsReport::kStatsReportTypeSsrc[] = "ssrc";
110const char StatsReport::kStatsReportTypeTrack[] = "googTrack";
111const char StatsReport::kStatsReportTypeIceCandidate[] = "iceCandidate";
112const char StatsReport::kStatsReportTypeTransport[] = "googTransport";
113const char StatsReport::kStatsReportTypeComponent[] = "googComponent";
114const char StatsReport::kStatsReportTypeCandidatePair[] = "googCandidatePair";
115
116const char StatsReport::kStatsReportVideoBweId[] = "bweforvideo";
117
118// Implementations of functions in statstypes.h
119void StatsReport::AddValue(const std::string& name, const std::string& value) {
120 Value temp;
121 temp.name = name;
122 temp.value = value;
123 values.push_back(temp);
124}
125
126void StatsReport::AddValue(const std::string& name, int64 value) {
127 AddValue(name, talk_base::ToString<int64>(value));
128}
129
130void StatsReport::AddBoolean(const std::string& name, bool value) {
131 AddValue(name, value ? "true" : "false");
132}
133
134namespace {
135typedef std::map<std::string, StatsReport> StatsMap;
136
137std::string StatsId(const std::string& type, const std::string& id) {
138 return type + "_" + id;
139}
140
141bool ExtractValueFromReport(
142 const StatsReport& report,
143 const std::string& name,
144 std::string* value) {
145 StatsReport::Values::const_iterator it = report.values.begin();
146 for (; it != report.values.end(); ++it) {
147 if (it->name == name) {
148 *value = it->value;
149 return true;
150 }
151 }
152 return false;
153}
154
155template <class TrackVector>
156void CreateTrackReports(const TrackVector& tracks, StatsMap* reports) {
157 for (size_t j = 0; j < tracks.size(); ++j) {
158 webrtc::MediaStreamTrackInterface* track = tracks[j];
159 // Adds an empty track report.
160 StatsReport report;
161 report.type = StatsReport::kStatsReportTypeTrack;
162 report.id = StatsId(StatsReport::kStatsReportTypeTrack, track->id());
163 report.AddValue(StatsReport::kStatsValueNameTrackId,
164 track->id());
165 (*reports)[report.id] = report;
166 }
167}
168
169void ExtractStats(const cricket::VoiceReceiverInfo& info, StatsReport* report) {
170 report->AddValue(StatsReport::kStatsValueNameAudioOutputLevel,
171 info.audio_level);
172 report->AddValue(StatsReport::kStatsValueNameBytesReceived,
173 info.bytes_rcvd);
174 report->AddValue(StatsReport::kStatsValueNameJitterReceived,
175 info.jitter_ms);
176 report->AddValue(StatsReport::kStatsValueNamePacketsReceived,
177 info.packets_rcvd);
178 report->AddValue(StatsReport::kStatsValueNamePacketsLost,
179 info.packets_lost);
180}
181
182void ExtractStats(const cricket::VoiceSenderInfo& info, StatsReport* report) {
183 report->AddValue(StatsReport::kStatsValueNameAudioInputLevel,
184 info.audio_level);
185 report->AddValue(StatsReport::kStatsValueNameBytesSent,
186 info.bytes_sent);
187 report->AddValue(StatsReport::kStatsValueNamePacketsSent,
188 info.packets_sent);
189 report->AddValue(StatsReport::kStatsValueNameJitterReceived,
190 info.jitter_ms);
191 report->AddValue(StatsReport::kStatsValueNameRtt, info.rtt_ms);
192 report->AddValue(StatsReport::kStatsValueNameEchoCancellationQualityMin,
193 talk_base::ToString<float>(info.aec_quality_min));
194 report->AddValue(StatsReport::kStatsValueNameEchoDelayMedian,
195 info.echo_delay_median_ms);
196 report->AddValue(StatsReport::kStatsValueNameEchoDelayStdDev,
197 info.echo_delay_std_ms);
198 report->AddValue(StatsReport::kStatsValueNameEchoReturnLoss,
199 info.echo_return_loss);
200 report->AddValue(StatsReport::kStatsValueNameEchoReturnLossEnhancement,
201 info.echo_return_loss_enhancement);
202 report->AddValue(StatsReport::kStatsValueNameCodecName, info.codec_name);
203}
204
205void ExtractStats(const cricket::VideoReceiverInfo& info, StatsReport* report) {
206 report->AddValue(StatsReport::kStatsValueNameBytesReceived,
207 info.bytes_rcvd);
208 report->AddValue(StatsReport::kStatsValueNamePacketsReceived,
209 info.packets_rcvd);
210 report->AddValue(StatsReport::kStatsValueNamePacketsLost,
211 info.packets_lost);
212
213 report->AddValue(StatsReport::kStatsValueNameFirsSent,
214 info.firs_sent);
215 report->AddValue(StatsReport::kStatsValueNameNacksSent,
216 info.nacks_sent);
217 report->AddValue(StatsReport::kStatsValueNameFrameWidthReceived,
218 info.frame_width);
219 report->AddValue(StatsReport::kStatsValueNameFrameHeightReceived,
220 info.frame_height);
221 report->AddValue(StatsReport::kStatsValueNameFrameRateReceived,
222 info.framerate_rcvd);
223 report->AddValue(StatsReport::kStatsValueNameFrameRateDecoded,
224 info.framerate_decoded);
225 report->AddValue(StatsReport::kStatsValueNameFrameRateOutput,
226 info.framerate_output);
227}
228
229void ExtractStats(const cricket::VideoSenderInfo& info, StatsReport* report) {
230 report->AddValue(StatsReport::kStatsValueNameBytesSent,
231 info.bytes_sent);
232 report->AddValue(StatsReport::kStatsValueNamePacketsSent,
233 info.packets_sent);
234
235 report->AddValue(StatsReport::kStatsValueNameFirsReceived,
236 info.firs_rcvd);
237 report->AddValue(StatsReport::kStatsValueNameNacksReceived,
238 info.nacks_rcvd);
239 report->AddValue(StatsReport::kStatsValueNameFrameWidthSent,
240 info.frame_width);
241 report->AddValue(StatsReport::kStatsValueNameFrameHeightSent,
242 info.frame_height);
243 report->AddValue(StatsReport::kStatsValueNameFrameRateInput,
244 info.framerate_input);
245 report->AddValue(StatsReport::kStatsValueNameFrameRateSent,
246 info.framerate_sent);
247 report->AddValue(StatsReport::kStatsValueNameRtt, info.rtt_ms);
248 report->AddValue(StatsReport::kStatsValueNameCodecName, info.codec_name);
249}
250
251void ExtractStats(const cricket::BandwidthEstimationInfo& info,
252 double stats_gathering_started,
253 StatsReport* report) {
254 report->id = StatsReport::kStatsReportVideoBweId;
255 report->type = StatsReport::kStatsReportTypeBwe;
256
257 // Clear out stats from previous GatherStats calls if any.
258 if (report->timestamp != stats_gathering_started) {
259 report->values.clear();
260 report->timestamp = stats_gathering_started;
261 }
262
263 report->AddValue(StatsReport::kStatsValueNameAvailableSendBandwidth,
264 info.available_send_bandwidth);
265 report->AddValue(StatsReport::kStatsValueNameAvailableReceiveBandwidth,
266 info.available_recv_bandwidth);
267 report->AddValue(StatsReport::kStatsValueNameTargetEncBitrate,
268 info.target_enc_bitrate);
269 report->AddValue(StatsReport::kStatsValueNameActualEncBitrate,
270 info.actual_enc_bitrate);
271 report->AddValue(StatsReport::kStatsValueNameRetransmitBitrate,
272 info.retransmit_bitrate);
273 report->AddValue(StatsReport::kStatsValueNameTransmitBitrate,
274 info.transmit_bitrate);
275 report->AddValue(StatsReport::kStatsValueNameBucketDelay,
276 info.bucket_delay);
277}
278
279uint32 ExtractSsrc(const cricket::VoiceReceiverInfo& info) {
280 return info.ssrc;
281}
282
283uint32 ExtractSsrc(const cricket::VoiceSenderInfo& info) {
284 return info.ssrc;
285}
286
287uint32 ExtractSsrc(const cricket::VideoReceiverInfo& info) {
288 return info.ssrcs[0];
289}
290
291uint32 ExtractSsrc(const cricket::VideoSenderInfo& info) {
292 return info.ssrcs[0];
293}
294
295// Template to extract stats from a data vector.
296// ExtractSsrc and ExtractStats must be defined and overloaded for each type.
297template<typename T>
298void ExtractStatsFromList(const std::vector<T>& data,
299 const std::string& transport_id,
300 StatsCollector* collector) {
301 typename std::vector<T>::const_iterator it = data.begin();
302 for (; it != data.end(); ++it) {
303 std::string id;
304 uint32 ssrc = ExtractSsrc(*it);
305 StatsReport* report = collector->PrepareReport(ssrc, transport_id);
306 if (!report) {
307 continue;
308 }
309 ExtractStats(*it, report);
310 }
311};
312
313} // namespace
314
315StatsCollector::StatsCollector()
316 : session_(NULL), stats_gathering_started_(0) {
317}
318
319// Adds a MediaStream with tracks that can be used as a |selector| in a call
320// to GetStats.
321void StatsCollector::AddStream(MediaStreamInterface* stream) {
322 ASSERT(stream != NULL);
323
324 CreateTrackReports<AudioTrackVector>(stream->GetAudioTracks(),
325 &reports_);
326 CreateTrackReports<VideoTrackVector>(stream->GetVideoTracks(),
327 &reports_);
328}
329
330bool StatsCollector::GetStats(MediaStreamTrackInterface* track,
331 StatsReports* reports) {
332 ASSERT(reports != NULL);
333 reports->clear();
334
335 StatsMap::iterator it;
336 if (!track) {
337 for (it = reports_.begin(); it != reports_.end(); ++it) {
338 reports->push_back(it->second);
339 }
340 return true;
341 }
342
343 it = reports_.find(StatsId(StatsReport::kStatsReportTypeSession,
344 session_->id()));
345 if (it != reports_.end()) {
346 reports->push_back(it->second);
347 }
348
349 it = reports_.find(StatsId(StatsReport::kStatsReportTypeTrack, track->id()));
350
351 if (it == reports_.end()) {
352 LOG(LS_WARNING) << "No StatsReport is available for "<< track->id();
353 return false;
354 }
355
356 reports->push_back(it->second);
357
358 std::string track_id;
359 for (it = reports_.begin(); it != reports_.end(); ++it) {
360 if (it->second.type != StatsReport::kStatsReportTypeSsrc) {
361 continue;
362 }
363 if (ExtractValueFromReport(it->second,
364 StatsReport::kStatsValueNameTrackId,
365 &track_id)) {
366 if (track_id == track->id()) {
367 reports->push_back(it->second);
368 }
369 }
370 }
371
372 return true;
373}
374
375void StatsCollector::UpdateStats() {
376 double time_now = GetTimeNow();
377 // Calls to UpdateStats() that occur less than kMinGatherStatsPeriod number of
378 // ms apart will be ignored.
379 const double kMinGatherStatsPeriod = 50;
380 if (stats_gathering_started_ + kMinGatherStatsPeriod > time_now) {
381 return;
382 }
383 stats_gathering_started_ = time_now;
384
385 if (session_) {
386 ExtractSessionInfo();
387 ExtractVoiceInfo();
388 ExtractVideoInfo();
389 }
390}
391
392StatsReport* StatsCollector::PrepareReport(uint32 ssrc,
393 const std::string& transport_id) {
394 std::string ssrc_id = talk_base::ToString<uint32>(ssrc);
395 StatsMap::iterator it = reports_.find(StatsId(
396 StatsReport::kStatsReportTypeSsrc, ssrc_id));
397
398 std::string track_id;
399 if (it == reports_.end()) {
400 if (!session()->GetTrackIdBySsrc(ssrc, &track_id)) {
401 LOG(LS_ERROR) << "The SSRC " << ssrc
402 << " is not associated with a track";
403 return NULL;
404 }
405 } else {
406 // Keeps the old track id since we want to report the stats for inactive
407 // tracks.
408 ExtractValueFromReport(it->second,
409 StatsReport::kStatsValueNameTrackId,
410 &track_id);
411 }
412
413 StatsReport* report = &reports_[
414 StatsId(StatsReport::kStatsReportTypeSsrc, ssrc_id)];
415 report->id = StatsId(StatsReport::kStatsReportTypeSsrc, ssrc_id);
416 report->type = StatsReport::kStatsReportTypeSsrc;
417
418 // Clear out stats from previous GatherStats calls if any.
419 if (report->timestamp != stats_gathering_started_) {
420 report->values.clear();
421 report->timestamp = stats_gathering_started_;
422 }
423
424 report->AddValue(StatsReport::kStatsValueNameSsrc, ssrc_id);
425 report->AddValue(StatsReport::kStatsValueNameTrackId, track_id);
426 // Add the mapping of SSRC to transport.
427 report->AddValue(StatsReport::kStatsValueNameTransportId,
428 transport_id);
429 return report;
430}
431
432void StatsCollector::ExtractSessionInfo() {
433 // Extract information from the base session.
434 StatsReport report;
435 report.id = StatsId(StatsReport::kStatsReportTypeSession, session_->id());
436 report.type = StatsReport::kStatsReportTypeSession;
437 report.timestamp = stats_gathering_started_;
438 report.values.clear();
439 report.AddBoolean(StatsReport::kStatsValueNameInitiator,
440 session_->initiator());
441
442 reports_[report.id] = report;
443
444 cricket::SessionStats stats;
445 if (session_->GetStats(&stats)) {
446 // Store the proxy map away for use in SSRC reporting.
447 proxy_to_transport_ = stats.proxy_to_transport;
448
449 for (cricket::TransportStatsMap::iterator transport_iter
450 = stats.transport_stats.begin();
451 transport_iter != stats.transport_stats.end(); ++transport_iter) {
452 for (cricket::TransportChannelStatsList::iterator channel_iter
453 = transport_iter->second.channel_stats.begin();
454 channel_iter != transport_iter->second.channel_stats.end();
455 ++channel_iter) {
456 StatsReport channel_report;
457 std::ostringstream ostc;
458 ostc << "Channel-" << transport_iter->second.content_name
459 << "-" << channel_iter->component;
460 channel_report.id = ostc.str();
461 channel_report.type = StatsReport::kStatsReportTypeComponent;
462 channel_report.timestamp = stats_gathering_started_;
463 channel_report.AddValue(StatsReport::kStatsValueNameComponent,
464 channel_iter->component);
465 reports_[channel_report.id] = channel_report;
466 for (size_t i = 0;
467 i < channel_iter->connection_infos.size();
468 ++i) {
469 StatsReport report;
470 const cricket::ConnectionInfo& info
471 = channel_iter->connection_infos[i];
472 std::ostringstream ost;
473 ost << "Conn-" << transport_iter->first << "-"
474 << channel_iter->component << "-" << i;
475 report.id = ost.str();
476 report.type = StatsReport::kStatsReportTypeCandidatePair;
477 report.timestamp = stats_gathering_started_;
478 // Link from connection to its containing channel.
479 report.AddValue(StatsReport::kStatsValueNameChannelId,
480 channel_report.id);
481 report.AddValue(StatsReport::kStatsValueNameBytesSent,
482 info.sent_total_bytes);
483 report.AddValue(StatsReport::kStatsValueNameBytesReceived,
484 info.recv_total_bytes);
485 report.AddBoolean(StatsReport::kStatsValueNameWritable,
486 info.writable);
487 report.AddBoolean(StatsReport::kStatsValueNameReadable,
488 info.readable);
489 report.AddBoolean(StatsReport::kStatsValueNameActiveConnection,
490 info.best_connection);
491 report.AddValue(StatsReport::kStatsValueNameLocalAddress,
492 info.local_candidate.address().ToString());
493 report.AddValue(StatsReport::kStatsValueNameRemoteAddress,
494 info.remote_candidate.address().ToString());
495 reports_[report.id] = report;
496 }
497 }
498 }
499 }
500}
501
502void StatsCollector::ExtractVoiceInfo() {
503 if (!session_->voice_channel()) {
504 return;
505 }
506 cricket::VoiceMediaInfo voice_info;
507 if (!session_->voice_channel()->GetStats(&voice_info)) {
508 LOG(LS_ERROR) << "Failed to get voice channel stats.";
509 return;
510 }
511 std::string transport_id;
512 if (!GetTransportIdFromProxy(session_->voice_channel()->content_name(),
513 &transport_id)) {
514 LOG(LS_ERROR) << "Failed to get transport name for proxy "
515 << session_->voice_channel()->content_name();
516 return;
517 }
518 ExtractStatsFromList(voice_info.receivers, transport_id, this);
519 ExtractStatsFromList(voice_info.senders, transport_id, this);
520}
521
522void StatsCollector::ExtractVideoInfo() {
523 if (!session_->video_channel()) {
524 return;
525 }
526 cricket::VideoMediaInfo video_info;
527 if (!session_->video_channel()->GetStats(&video_info)) {
528 LOG(LS_ERROR) << "Failed to get video channel stats.";
529 return;
530 }
531 std::string transport_id;
532 if (!GetTransportIdFromProxy(session_->video_channel()->content_name(),
533 &transport_id)) {
534 LOG(LS_ERROR) << "Failed to get transport name for proxy "
535 << session_->video_channel()->content_name();
536 return;
537 }
538 ExtractStatsFromList(video_info.receivers, transport_id, this);
539 ExtractStatsFromList(video_info.senders, transport_id, this);
540 if (video_info.bw_estimations.size() != 1) {
541 LOG(LS_ERROR) << "BWEs count: " << video_info.bw_estimations.size();
542 } else {
543 StatsReport* report = &reports_[StatsReport::kStatsReportVideoBweId];
544 ExtractStats(
545 video_info.bw_estimations[0], stats_gathering_started_, report);
546 }
547}
548
549double StatsCollector::GetTimeNow() {
550 return timing_.WallTimeNow() * talk_base::kNumMillisecsPerSec;
551}
552
553bool StatsCollector::GetTransportIdFromProxy(const std::string& proxy,
554 std::string* transport) {
555 // TODO(hta): Remove handling of empty proxy name once tests do not use it.
556 if (proxy.empty()) {
557 transport->clear();
558 return true;
559 }
560 if (proxy_to_transport_.find(proxy) == proxy_to_transport_.end()) {
561 LOG(LS_ERROR) << "No transport ID mapping for " << proxy;
562 return false;
563 }
564 std::ostringstream ost;
565 // Component 1 is always used for RTP.
566 ost << "Channel-" << proxy_to_transport_[proxy] << "-1";
567 *transport = ost.str();
568 return true;
569}
570
571} // namespace webrtc