Update StatsReport and by extension StatsCollector to reduce data copying.
Summary of changes: * We're now using an enum for types instead of strings which both eliminates unecessary string creations+copies and further restricts the type to a known set at compile time. * IDs are now a separate type instead of a string, copying of Values is not possible and values are const to allow grabbing references outside of the statscollector. * StatsReport member variables are no longer public. * Consolidated code in StatsCollector (e.g. merged PrepareLocalReport and PrepareRemoteReport). * Refactored methods that forced copies of string (e.g. ExtractValueFromReport). * More asserts for thread correctness. * Using std::list for the StatsSet instead of a set since order is not important and updates are more efficient in list<>. BUG=2822 R=hta@webrtc.org, perkj@webrtc.org Review URL: https://webrtc-codereview.appspot.com/40439004 git-svn-id: http://webrtc.googlecode.com/svn/trunk@8110 4adac7df-926f-26a2-2b94-8c16560cd09d
This commit is contained in:
parent
f66a6b2a00
commit
4fb7e25843
@ -35,6 +35,8 @@
|
||||
#include "webrtc/base/scoped_ptr.h"
|
||||
#include "webrtc/base/timing.h"
|
||||
|
||||
using rtc::scoped_ptr;
|
||||
|
||||
namespace webrtc {
|
||||
namespace {
|
||||
|
||||
@ -73,55 +75,26 @@ bool GetTransportIdFromProxy(const cricket::ProxyTransportMap& map,
|
||||
return false;
|
||||
}
|
||||
|
||||
std::ostringstream ost;
|
||||
// Component 1 is always used for RTP.
|
||||
ost << "Channel-" << found->second << "-1";
|
||||
*transport = ost.str();
|
||||
scoped_ptr<StatsReport::Id> id(
|
||||
StatsReport::NewComponentId(found->second, 1));
|
||||
// TODO(tommi): Should |transport| simply be of type StatsReport::Id?
|
||||
// When we support more value types than string (e.g. int, double, vector etc)
|
||||
// we should also support a value type for Id.
|
||||
*transport = id->ToString();
|
||||
return true;
|
||||
}
|
||||
|
||||
std::string StatsId(const std::string& type, const std::string& id) {
|
||||
return type + "_" + id;
|
||||
}
|
||||
|
||||
std::string StatsId(const std::string& type, const std::string& id,
|
||||
StatsCollector::TrackDirection direction) {
|
||||
ASSERT(direction == StatsCollector::kSending ||
|
||||
direction == StatsCollector::kReceiving);
|
||||
|
||||
// Strings for the direction of the track.
|
||||
const char kSendDirection[] = "send";
|
||||
const char kRecvDirection[] = "recv";
|
||||
|
||||
const std::string direction_id = (direction == StatsCollector::kSending) ?
|
||||
kSendDirection : kRecvDirection;
|
||||
return type + "_" + id + "_" + direction_id;
|
||||
}
|
||||
|
||||
bool ExtractValueFromReport(
|
||||
const StatsReport& report,
|
||||
StatsReport::StatsValueName name,
|
||||
std::string* value) {
|
||||
StatsReport::Values::const_iterator it = report.values().begin();
|
||||
for (; it != report.values().end(); ++it) {
|
||||
if ((*it)->name == name) {
|
||||
*value = (*it)->value;
|
||||
return true;
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
void AddTrackReport(StatsSet* reports, const std::string& track_id) {
|
||||
void AddTrackReport(StatsCollection* reports, const std::string& track_id) {
|
||||
// Adds an empty track report.
|
||||
StatsReport* report = reports->ReplaceOrAddNew(
|
||||
StatsId(StatsReport::kStatsReportTypeTrack, track_id));
|
||||
report->type = StatsReport::kStatsReportTypeTrack;
|
||||
rtc::scoped_ptr<StatsReport::Id> id(
|
||||
StatsReport::NewTypedId(StatsReport::kStatsReportTypeTrack, track_id));
|
||||
StatsReport* report = reports->ReplaceOrAddNew(id.Pass());
|
||||
report->AddValue(StatsReport::kStatsValueNameTrackId, track_id);
|
||||
}
|
||||
|
||||
template <class TrackVector>
|
||||
void CreateTrackReports(const TrackVector& tracks, StatsSet* reports) {
|
||||
void CreateTrackReports(const TrackVector& tracks, StatsCollection* reports) {
|
||||
for (size_t j = 0; j < tracks.size(); ++j) {
|
||||
webrtc::MediaStreamTrackInterface* track = tracks[j];
|
||||
AddTrackReport(reports, track->id());
|
||||
@ -284,7 +257,7 @@ void ExtractStats(const cricket::BandwidthEstimationInfo& info,
|
||||
double stats_gathering_started,
|
||||
PeerConnectionInterface::StatsOutputLevel level,
|
||||
StatsReport* report) {
|
||||
report->type = StatsReport::kStatsReportTypeBwe;
|
||||
ASSERT(report->type() == StatsReport::kStatsReportTypeBwe);
|
||||
|
||||
// Clear out stats from previous GatherStats calls if any.
|
||||
if (report->timestamp() != stats_gathering_started) {
|
||||
@ -341,25 +314,20 @@ template<typename T>
|
||||
void ExtractStatsFromList(const std::vector<T>& data,
|
||||
const std::string& transport_id,
|
||||
StatsCollector* collector,
|
||||
StatsCollector::TrackDirection direction) {
|
||||
typename std::vector<T>::const_iterator it = data.begin();
|
||||
for (; it != data.end(); ++it) {
|
||||
std::string id;
|
||||
uint32 ssrc = it->ssrc();
|
||||
StatsReport::Direction direction) {
|
||||
for (const auto& d : data) {
|
||||
uint32 ssrc = d.ssrc();
|
||||
// Each track can have stats for both local and remote objects.
|
||||
// TODO(hta): Handle the case of multiple SSRCs per object.
|
||||
StatsReport* report = collector->PrepareLocalReport(ssrc, transport_id,
|
||||
direction);
|
||||
StatsReport* report = collector->PrepareReport(true, ssrc, transport_id,
|
||||
direction);
|
||||
if (report)
|
||||
ExtractStats(*it, report);
|
||||
ExtractStats(d, report);
|
||||
|
||||
if (it->remote_stats.size() > 0) {
|
||||
report = collector->PrepareRemoteReport(ssrc, transport_id,
|
||||
direction);
|
||||
if (!report) {
|
||||
continue;
|
||||
}
|
||||
ExtractRemoteStats(*it, report);
|
||||
if (!d.remote_stats.empty()) {
|
||||
report = collector->PrepareReport(false, ssrc, transport_id, direction);
|
||||
if (report)
|
||||
ExtractRemoteStats(d, report);
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -436,10 +404,14 @@ void StatsCollector::AddLocalAudioTrack(AudioTrackInterface* audio_track,
|
||||
|
||||
// Create the kStatsReportTypeTrack report for the new track if there is no
|
||||
// report yet.
|
||||
StatsReport* found = reports_.Find(
|
||||
StatsId(StatsReport::kStatsReportTypeTrack, audio_track->id()));
|
||||
if (!found)
|
||||
AddTrackReport(&reports_, audio_track->id());
|
||||
rtc::scoped_ptr<StatsReport::Id> id(
|
||||
StatsReport::NewTypedId(StatsReport::kStatsReportTypeTrack,
|
||||
audio_track->id()));
|
||||
StatsReport* report = reports_.Find(*id.get());
|
||||
if (!report) {
|
||||
report = reports_.InsertNew(id.Pass());
|
||||
report->AddValue(StatsReport::kStatsValueNameTrackId, audio_track->id());
|
||||
}
|
||||
}
|
||||
|
||||
void StatsCollector::RemoveLocalAudioTrack(AudioTrackInterface* audio_track,
|
||||
@ -463,20 +435,19 @@ void StatsCollector::GetStats(MediaStreamTrackInterface* track,
|
||||
ASSERT(reports->empty());
|
||||
|
||||
if (!track) {
|
||||
StatsSet::const_iterator it;
|
||||
for (it = reports_.begin(); it != reports_.end(); ++it)
|
||||
reports->push_back(&(*it));
|
||||
reports->reserve(reports_.size());
|
||||
for (auto* r : reports_)
|
||||
reports->push_back(r);
|
||||
return;
|
||||
}
|
||||
|
||||
StatsReport* report =
|
||||
reports_.Find(StatsId(StatsReport::kStatsReportTypeSession,
|
||||
session_->id()));
|
||||
StatsReport* report = reports_.Find(StatsReport::NewTypedId(
|
||||
StatsReport::kStatsReportTypeSession, session_->id()));
|
||||
if (report)
|
||||
reports->push_back(report);
|
||||
|
||||
report = reports_.Find(
|
||||
StatsId(StatsReport::kStatsReportTypeTrack, track->id()));
|
||||
report = reports_.Find(StatsReport::NewTypedId(
|
||||
StatsReport::kStatsReportTypeTrack, track->id()));
|
||||
|
||||
if (!report)
|
||||
return;
|
||||
@ -484,18 +455,14 @@ void StatsCollector::GetStats(MediaStreamTrackInterface* track,
|
||||
reports->push_back(report);
|
||||
|
||||
std::string track_id;
|
||||
for (StatsSet::const_iterator it = reports_.begin(); it != reports_.end();
|
||||
++it) {
|
||||
if (it->type != StatsReport::kStatsReportTypeSsrc)
|
||||
for (const auto* r : reports_) {
|
||||
if (r->type() != StatsReport::kStatsReportTypeSsrc)
|
||||
continue;
|
||||
|
||||
if (ExtractValueFromReport(*it,
|
||||
StatsReport::kStatsValueNameTrackId,
|
||||
&track_id)) {
|
||||
if (track_id == track->id()) {
|
||||
reports->push_back(&(*it));
|
||||
}
|
||||
}
|
||||
const StatsReport::Value* v =
|
||||
r->FindValue(StatsReport::kStatsValueNameTrackId);
|
||||
if (v && v->value == track->id())
|
||||
reports->push_back(r);
|
||||
}
|
||||
}
|
||||
|
||||
@ -520,14 +487,18 @@ StatsCollector::UpdateStats(PeerConnectionInterface::StatsOutputLevel level) {
|
||||
}
|
||||
}
|
||||
|
||||
StatsReport* StatsCollector::PrepareLocalReport(
|
||||
StatsReport* StatsCollector::PrepareReport(
|
||||
bool local,
|
||||
uint32 ssrc,
|
||||
const std::string& transport_id,
|
||||
TrackDirection direction) {
|
||||
StatsReport::Direction direction) {
|
||||
ASSERT(session_->signaling_thread()->IsCurrent());
|
||||
const std::string ssrc_id = rtc::ToString<uint32>(ssrc);
|
||||
StatsReport* report = reports_.Find(
|
||||
StatsId(StatsReport::kStatsReportTypeSsrc, ssrc_id, direction));
|
||||
rtc::scoped_ptr<StatsReport::Id> id(StatsReport::NewIdWithDirection(
|
||||
local ? StatsReport::kStatsReportTypeSsrc :
|
||||
StatsReport::kStatsReportTypeRemoteSsrc,
|
||||
ssrc_id, direction));
|
||||
StatsReport* report = reports_.Find(*id.get());
|
||||
|
||||
// Use the ID of the track that is currently mapped to the SSRC, if any.
|
||||
std::string track_id;
|
||||
@ -540,20 +511,28 @@ StatsReport* StatsCollector::PrepareLocalReport(
|
||||
|
||||
// The ssrc is not used by any existing track. Keeps the old track id
|
||||
// since we want to report the stats for inactive ssrc.
|
||||
ExtractValueFromReport(*report,
|
||||
StatsReport::kStatsValueNameTrackId,
|
||||
&track_id);
|
||||
const StatsReport::Value* v =
|
||||
report->FindValue(StatsReport::kStatsValueNameTrackId);
|
||||
if (v)
|
||||
track_id = v->value;
|
||||
}
|
||||
|
||||
report = GetOrCreateReport(
|
||||
StatsReport::kStatsReportTypeSsrc, ssrc_id, direction);
|
||||
if (!report) {
|
||||
report = reports_.InsertNew(id.Pass());
|
||||
} else {
|
||||
// Clear out stats from previous GatherStats calls if any.
|
||||
// This is required since the report will be returned for the new values.
|
||||
// Having the old values in the report will lead to multiple values with
|
||||
// the same name.
|
||||
// TODO(tommi): This seems to be pretty wasteful if some of these values
|
||||
// have not changed (we basically throw them away just to recreate them).
|
||||
// Figure out a way to not have to do this while not breaking the existing
|
||||
// functionality.
|
||||
report->ResetValues();
|
||||
}
|
||||
|
||||
// Clear out stats from previous GatherStats calls if any.
|
||||
// This is required since the report will be returned for the new values.
|
||||
// Having the old values in the report will lead to multiple values with
|
||||
// the same name.
|
||||
// TODO(xians): Consider changing StatsReport to use map instead of vector.
|
||||
report->ResetValues();
|
||||
ASSERT(report->values().empty());
|
||||
// FYI - for remote reports, the timestamp will be overwritten later.
|
||||
report->set_timestamp(stats_gathering_started_);
|
||||
|
||||
report->AddValue(StatsReport::kStatsValueNameSsrc, ssrc_id);
|
||||
@ -564,49 +543,10 @@ StatsReport* StatsCollector::PrepareLocalReport(
|
||||
return report;
|
||||
}
|
||||
|
||||
StatsReport* StatsCollector::PrepareRemoteReport(
|
||||
uint32 ssrc,
|
||||
const std::string& transport_id,
|
||||
TrackDirection direction) {
|
||||
ASSERT(session_->signaling_thread()->IsCurrent());
|
||||
const std::string ssrc_id = rtc::ToString<uint32>(ssrc);
|
||||
StatsReport* report = reports_.Find(
|
||||
StatsId(StatsReport::kStatsReportTypeRemoteSsrc, ssrc_id, direction));
|
||||
|
||||
// Use the ID of the track that is currently mapped to the SSRC, if any.
|
||||
std::string track_id;
|
||||
if (!GetTrackIdBySsrc(ssrc, &track_id, direction)) {
|
||||
if (!report) {
|
||||
// The ssrc is not used by any track or existing report, return NULL
|
||||
// in such case to indicate no report is prepared for the ssrc.
|
||||
return NULL;
|
||||
}
|
||||
|
||||
// The ssrc is not used by any existing track. Keeps the old track id
|
||||
// since we want to report the stats for inactive ssrc.
|
||||
ExtractValueFromReport(*report,
|
||||
StatsReport::kStatsValueNameTrackId,
|
||||
&track_id);
|
||||
}
|
||||
|
||||
report = GetOrCreateReport(
|
||||
StatsReport::kStatsReportTypeRemoteSsrc, ssrc_id, direction);
|
||||
|
||||
// Clear out stats from previous GatherStats calls if any.
|
||||
// The timestamp will be added later. Zero it for debugging.
|
||||
report->ResetValues();
|
||||
report->set_timestamp(0);
|
||||
|
||||
report->AddValue(StatsReport::kStatsValueNameSsrc, ssrc_id);
|
||||
report->AddValue(StatsReport::kStatsValueNameTrackId, track_id);
|
||||
// Add the mapping of SSRC to transport.
|
||||
report->AddValue(StatsReport::kStatsValueNameTransportId,
|
||||
transport_id);
|
||||
return report;
|
||||
}
|
||||
|
||||
std::string StatsCollector::AddOneCertificateReport(
|
||||
const rtc::SSLCertificate* cert, const std::string& issuer_id) {
|
||||
ASSERT(session_->signaling_thread()->IsCurrent());
|
||||
|
||||
// TODO(bemasc): Move this computation to a helper class that caches these
|
||||
// values to reduce CPU use in GetStats. This will require adding a fast
|
||||
// SSLCertificate::Equals() method to detect certificate changes.
|
||||
@ -633,9 +573,10 @@ std::string StatsCollector::AddOneCertificateReport(
|
||||
rtc::Base64::EncodeFromArray(
|
||||
der_buffer.data(), der_buffer.length(), &der_base64);
|
||||
|
||||
StatsReport* report = reports_.ReplaceOrAddNew(
|
||||
StatsId(StatsReport::kStatsReportTypeCertificate, fingerprint));
|
||||
report->type = StatsReport::kStatsReportTypeCertificate;
|
||||
rtc::scoped_ptr<StatsReport::Id> id(
|
||||
StatsReport::NewTypedId(
|
||||
StatsReport::kStatsReportTypeCertificate, fingerprint));
|
||||
StatsReport* report = reports_.ReplaceOrAddNew(id.Pass());
|
||||
report->set_timestamp(stats_gathering_started_);
|
||||
report->AddValue(StatsReport::kStatsValueNameFingerprint, fingerprint);
|
||||
report->AddValue(StatsReport::kStatsValueNameFingerprintAlgorithm,
|
||||
@ -643,11 +584,13 @@ std::string StatsCollector::AddOneCertificateReport(
|
||||
report->AddValue(StatsReport::kStatsValueNameDer, der_base64);
|
||||
if (!issuer_id.empty())
|
||||
report->AddValue(StatsReport::kStatsValueNameIssuerId, issuer_id);
|
||||
// TODO(tommi): Can we avoid this?
|
||||
return report->id().ToString();
|
||||
}
|
||||
|
||||
std::string StatsCollector::AddCertificateReports(
|
||||
const rtc::SSLCertificate* cert) {
|
||||
ASSERT(session_->signaling_thread()->IsCurrent());
|
||||
// Produces a chain of StatsReports representing this certificate and the rest
|
||||
// of its chain, and adds those reports to |reports_|. The return value is
|
||||
// the id of the leaf report. The provided cert must be non-null, so at least
|
||||
@ -673,20 +616,17 @@ std::string StatsCollector::AddCertificateReports(
|
||||
|
||||
std::string StatsCollector::AddCandidateReport(
|
||||
const cricket::Candidate& candidate,
|
||||
const std::string& report_type) {
|
||||
std::ostringstream ost;
|
||||
ost << "Cand-" << candidate.id();
|
||||
StatsReport* report = reports_.Find(ost.str());
|
||||
bool local) {
|
||||
scoped_ptr<StatsReport::Id> id(
|
||||
StatsReport::NewCandidateId(local, candidate.id()));
|
||||
StatsReport* report = reports_.Find(*id.get());
|
||||
if (!report) {
|
||||
report = reports_.InsertNew(ost.str());
|
||||
DCHECK(StatsReport::kStatsReportTypeIceLocalCandidate == report_type ||
|
||||
StatsReport::kStatsReportTypeIceRemoteCandidate == report_type);
|
||||
report->type = report_type;
|
||||
if (report_type == StatsReport::kStatsReportTypeIceLocalCandidate) {
|
||||
report = reports_.InsertNew(id.Pass());
|
||||
report->set_timestamp(stats_gathering_started_);
|
||||
if (local) {
|
||||
report->AddValue(StatsReport::kStatsValueNameCandidateNetworkType,
|
||||
AdapterTypeToStatsType(candidate.network_type()));
|
||||
}
|
||||
report->set_timestamp(stats_gathering_started_);
|
||||
report->AddValue(StatsReport::kStatsValueNameCandidateIPAddress,
|
||||
candidate.address().ipaddr().ToString());
|
||||
report->AddValue(StatsReport::kStatsValueNameCandidatePortNumber,
|
||||
@ -699,15 +639,17 @@ std::string StatsCollector::AddCandidateReport(
|
||||
candidate.protocol());
|
||||
}
|
||||
|
||||
return ost.str();
|
||||
// TODO(tommi): Necessary?
|
||||
return report->id().ToString();
|
||||
}
|
||||
|
||||
void StatsCollector::ExtractSessionInfo() {
|
||||
ASSERT(session_->signaling_thread()->IsCurrent());
|
||||
// Extract information from the base session.
|
||||
StatsReport* report = reports_.ReplaceOrAddNew(
|
||||
StatsId(StatsReport::kStatsReportTypeSession, session_->id()));
|
||||
report->type = StatsReport::kStatsReportTypeSession;
|
||||
rtc::scoped_ptr<StatsReport::Id> id(
|
||||
StatsReport::NewTypedId(
|
||||
StatsReport::kStatsReportTypeSession, session_->id()));
|
||||
StatsReport* report = reports_.ReplaceOrAddNew(id.Pass());
|
||||
report->set_timestamp(stats_gathering_started_);
|
||||
report->ResetValues();
|
||||
report->AddBoolean(StatsReport::kStatsValueNameInitiator,
|
||||
@ -749,11 +691,10 @@ void StatsCollector::ExtractSessionInfo() {
|
||||
= transport_iter->second.channel_stats.begin();
|
||||
channel_iter != transport_iter->second.channel_stats.end();
|
||||
++channel_iter) {
|
||||
std::ostringstream ostc;
|
||||
ostc << "Channel-" << transport_iter->second.content_name
|
||||
<< "-" << channel_iter->component;
|
||||
StatsReport* channel_report = reports_.ReplaceOrAddNew(ostc.str());
|
||||
channel_report->type = StatsReport::kStatsReportTypeComponent;
|
||||
rtc::scoped_ptr<StatsReport::Id> id(
|
||||
StatsReport::NewComponentId(transport_iter->second.content_name,
|
||||
channel_iter->component));
|
||||
StatsReport* channel_report = reports_.ReplaceOrAddNew(id.Pass());
|
||||
channel_report->set_timestamp(stats_gathering_started_);
|
||||
channel_report->AddValue(StatsReport::kStatsValueNameComponent,
|
||||
channel_iter->component);
|
||||
@ -770,13 +711,13 @@ void StatsCollector::ExtractSessionInfo() {
|
||||
for (size_t i = 0;
|
||||
i < channel_iter->connection_infos.size();
|
||||
++i) {
|
||||
std::ostringstream ost;
|
||||
ost << "Conn-" << transport_iter->first << "-"
|
||||
<< channel_iter->component << "-" << i;
|
||||
StatsReport* report = reports_.ReplaceOrAddNew(ost.str());
|
||||
report->type = StatsReport::kStatsReportTypeCandidatePair;
|
||||
rtc::scoped_ptr<StatsReport::Id> id(
|
||||
StatsReport::NewCandidatePairId(transport_iter->first,
|
||||
channel_iter->component, static_cast<int>(i)));
|
||||
StatsReport* report = reports_.ReplaceOrAddNew(id.Pass());
|
||||
report->set_timestamp(stats_gathering_started_);
|
||||
// Link from connection to its containing channel.
|
||||
// TODO(tommi): Any way to avoid ToString here?
|
||||
report->AddValue(StatsReport::kStatsValueNameChannelId,
|
||||
channel_report->id().ToString());
|
||||
|
||||
@ -797,14 +738,10 @@ void StatsCollector::ExtractSessionInfo() {
|
||||
report->AddBoolean(StatsReport::kStatsValueNameActiveConnection,
|
||||
info.best_connection);
|
||||
report->AddValue(StatsReport::kStatsValueNameLocalCandidateId,
|
||||
AddCandidateReport(
|
||||
info.local_candidate,
|
||||
StatsReport::kStatsReportTypeIceLocalCandidate));
|
||||
AddCandidateReport(info.local_candidate, true));
|
||||
report->AddValue(
|
||||
StatsReport::kStatsValueNameRemoteCandidateId,
|
||||
AddCandidateReport(
|
||||
info.remote_candidate,
|
||||
StatsReport::kStatsReportTypeIceRemoteCandidate));
|
||||
AddCandidateReport(info.remote_candidate, false));
|
||||
report->AddValue(StatsReport::kStatsValueNameLocalAddress,
|
||||
info.local_candidate.address().ToString());
|
||||
report->AddValue(StatsReport::kStatsValueNameRemoteAddress,
|
||||
@ -841,8 +778,10 @@ void StatsCollector::ExtractVoiceInfo() {
|
||||
<< session_->voice_channel()->content_name();
|
||||
return;
|
||||
}
|
||||
ExtractStatsFromList(voice_info.receivers, transport_id, this, kReceiving);
|
||||
ExtractStatsFromList(voice_info.senders, transport_id, this, kSending);
|
||||
ExtractStatsFromList(voice_info.receivers, transport_id, this,
|
||||
StatsReport::kReceive);
|
||||
ExtractStatsFromList(voice_info.senders, transport_id, this,
|
||||
StatsReport::kSend);
|
||||
|
||||
UpdateStatsFromExistingLocalAudioTracks();
|
||||
}
|
||||
@ -871,13 +810,16 @@ void StatsCollector::ExtractVideoInfo(
|
||||
<< session_->video_channel()->content_name();
|
||||
return;
|
||||
}
|
||||
ExtractStatsFromList(video_info.receivers, transport_id, this, kReceiving);
|
||||
ExtractStatsFromList(video_info.senders, transport_id, this, kSending);
|
||||
ExtractStatsFromList(video_info.receivers, transport_id, this,
|
||||
StatsReport::kReceive);
|
||||
ExtractStatsFromList(video_info.senders, transport_id, this,
|
||||
StatsReport::kSend);
|
||||
if (video_info.bw_estimations.size() != 1) {
|
||||
LOG(LS_ERROR) << "BWEs count: " << video_info.bw_estimations.size();
|
||||
} else {
|
||||
StatsReport* report =
|
||||
reports_.FindOrAddNew(StatsReport::kStatsReportVideoBweId);
|
||||
rtc::scoped_ptr<StatsReport::Id> report_id(
|
||||
StatsReport::NewBandwidthEstimationId());
|
||||
StatsReport* report = reports_.FindOrAddNew(report_id.Pass());
|
||||
ExtractStats(
|
||||
video_info.bw_estimations[0], stats_gathering_started_, level, report);
|
||||
}
|
||||
@ -888,9 +830,10 @@ void StatsCollector::ExtractDataInfo() {
|
||||
|
||||
for (const auto& dc :
|
||||
session_->mediastream_signaling()->sctp_data_channels()) {
|
||||
StatsReport* report = reports_.ReplaceOrAddNew(
|
||||
StatsId(StatsReport::kStatsReportTypeDataChannel, dc->label()));
|
||||
report->type = StatsReport::kStatsReportTypeDataChannel;
|
||||
rtc::scoped_ptr<StatsReport::Id> id(
|
||||
StatsReport::NewTypedId(StatsReport::kStatsReportTypeDataChannel,
|
||||
dc->label()));
|
||||
StatsReport* report = reports_.ReplaceOrAddNew(id.Pass());
|
||||
report->AddValue(StatsReport::kStatsValueNameLabel, dc->label());
|
||||
report->AddValue(StatsReport::kStatsValueNameDataChannelId, dc->id());
|
||||
report->AddValue(StatsReport::kStatsValueNameProtocol, dc->protocol());
|
||||
@ -899,26 +842,27 @@ void StatsCollector::ExtractDataInfo() {
|
||||
}
|
||||
}
|
||||
|
||||
StatsReport* StatsCollector::GetReport(const std::string& type,
|
||||
StatsReport* StatsCollector::GetReport(const StatsReport::StatsType& type,
|
||||
const std::string& id,
|
||||
TrackDirection direction) {
|
||||
StatsReport::Direction direction) {
|
||||
ASSERT(session_->signaling_thread()->IsCurrent());
|
||||
ASSERT(type == StatsReport::kStatsReportTypeSsrc ||
|
||||
type == StatsReport::kStatsReportTypeRemoteSsrc);
|
||||
return reports_.Find(StatsId(type, id, direction));
|
||||
return reports_.Find(StatsReport::NewIdWithDirection(type, id, direction));
|
||||
}
|
||||
|
||||
StatsReport* StatsCollector::GetOrCreateReport(const std::string& type,
|
||||
const std::string& id,
|
||||
TrackDirection direction) {
|
||||
StatsReport* StatsCollector::GetOrCreateReport(
|
||||
const StatsReport::StatsType& type,
|
||||
const std::string& id,
|
||||
StatsReport::Direction direction) {
|
||||
ASSERT(session_->signaling_thread()->IsCurrent());
|
||||
ASSERT(type == StatsReport::kStatsReportTypeSsrc ||
|
||||
type == StatsReport::kStatsReportTypeRemoteSsrc);
|
||||
StatsReport* report = GetReport(type, id, direction);
|
||||
if (report == NULL) {
|
||||
std::string statsid = StatsId(type, id, direction);
|
||||
report = reports_.FindOrAddNew(statsid);
|
||||
report->type = type;
|
||||
rtc::scoped_ptr<StatsReport::Id> report_id(
|
||||
StatsReport::NewIdWithDirection(type, id, direction));
|
||||
report = reports_.InsertNew(report_id.Pass());
|
||||
}
|
||||
|
||||
return report;
|
||||
@ -934,7 +878,7 @@ void StatsCollector::UpdateStatsFromExistingLocalAudioTracks() {
|
||||
std::string ssrc_id = rtc::ToString<uint32>(ssrc);
|
||||
StatsReport* report = GetReport(StatsReport::kStatsReportTypeSsrc,
|
||||
ssrc_id,
|
||||
kSending);
|
||||
StatsReport::kSend);
|
||||
if (report == NULL) {
|
||||
// This can happen if a local audio track is added to a stream on the
|
||||
// fly and the report has not been set up yet. Do nothing in this case.
|
||||
@ -943,13 +887,10 @@ void StatsCollector::UpdateStatsFromExistingLocalAudioTracks() {
|
||||
}
|
||||
|
||||
// The same ssrc can be used by both local and remote audio tracks.
|
||||
std::string track_id;
|
||||
if (!ExtractValueFromReport(*report,
|
||||
StatsReport::kStatsValueNameTrackId,
|
||||
&track_id) ||
|
||||
track_id != track->id()) {
|
||||
const StatsReport::Value* v =
|
||||
report->FindValue(StatsReport::kStatsValueNameTrackId);
|
||||
if (!v || v->value != track->id())
|
||||
continue;
|
||||
}
|
||||
|
||||
UpdateReportFromAudioTrack(track, report);
|
||||
}
|
||||
@ -991,16 +932,16 @@ void StatsCollector::UpdateReportFromAudioTrack(AudioTrackInterface* track,
|
||||
}
|
||||
|
||||
bool StatsCollector::GetTrackIdBySsrc(uint32 ssrc, std::string* track_id,
|
||||
TrackDirection direction) {
|
||||
StatsReport::Direction direction) {
|
||||
ASSERT(session_->signaling_thread()->IsCurrent());
|
||||
if (direction == kSending) {
|
||||
if (direction == StatsReport::kSend) {
|
||||
if (!session_->GetLocalTrackIdBySsrc(ssrc, track_id)) {
|
||||
LOG(LS_WARNING) << "The SSRC " << ssrc
|
||||
<< " is not associated with a sending track";
|
||||
return false;
|
||||
}
|
||||
} else {
|
||||
ASSERT(direction == kReceiving);
|
||||
ASSERT(direction == StatsReport::kReceive);
|
||||
if (!session_->GetRemoteTrackIdBySsrc(ssrc, track_id)) {
|
||||
LOG(LS_WARNING) << "The SSRC " << ssrc
|
||||
<< " is not associated with a receiving track";
|
||||
|
@ -54,14 +54,9 @@ const char* AdapterTypeToStatsType(rtc::AdapterType type);
|
||||
|
||||
class StatsCollector {
|
||||
public:
|
||||
enum TrackDirection {
|
||||
kSending = 0,
|
||||
kReceiving,
|
||||
};
|
||||
|
||||
// The caller is responsible for ensuring that the session outlives the
|
||||
// StatsCollector instance.
|
||||
StatsCollector(WebRtcSession* session);
|
||||
explicit StatsCollector(WebRtcSession* session);
|
||||
virtual ~StatsCollector();
|
||||
|
||||
// Adds a MediaStream with tracks that can be used as a |selector| in a call
|
||||
@ -89,13 +84,10 @@ class StatsCollector {
|
||||
void GetStats(MediaStreamTrackInterface* track,
|
||||
StatsReports* reports);
|
||||
|
||||
// Prepare an SSRC report for the given ssrc. Used internally
|
||||
// Prepare a local or remote SSRC report for the given ssrc. Used internally
|
||||
// in the ExtractStatsFromList template.
|
||||
StatsReport* PrepareLocalReport(uint32 ssrc, const std::string& transport,
|
||||
TrackDirection direction);
|
||||
// Prepare an SSRC report for the given remote ssrc. Used internally.
|
||||
StatsReport* PrepareRemoteReport(uint32 ssrc, const std::string& transport,
|
||||
TrackDirection direction);
|
||||
StatsReport* PrepareReport(bool local, uint32 ssrc,
|
||||
const std::string& transport_id, StatsReport::Direction direction);
|
||||
|
||||
// Method used by the unittest to force a update of stats since UpdateStats()
|
||||
// that occur less than kMinGatherStatsPeriod number of ms apart will be
|
||||
@ -114,7 +106,7 @@ class StatsCollector {
|
||||
// Helper method for creating IceCandidate report. |is_local| indicates
|
||||
// whether this candidate is local or remote.
|
||||
std::string AddCandidateReport(const cricket::Candidate& candidate,
|
||||
const std::string& report_type);
|
||||
bool local);
|
||||
|
||||
// Adds a report for this certificate and every certificate in its chain, and
|
||||
// returns the leaf certificate's report's ID.
|
||||
@ -125,12 +117,12 @@ class StatsCollector {
|
||||
void ExtractVoiceInfo();
|
||||
void ExtractVideoInfo(PeerConnectionInterface::StatsOutputLevel level);
|
||||
void BuildSsrcToTransportId();
|
||||
webrtc::StatsReport* GetOrCreateReport(const std::string& type,
|
||||
webrtc::StatsReport* GetOrCreateReport(const StatsReport::StatsType& type,
|
||||
const std::string& id,
|
||||
TrackDirection direction);
|
||||
webrtc::StatsReport* GetReport(const std::string& type,
|
||||
StatsReport::Direction direction);
|
||||
webrtc::StatsReport* GetReport(const StatsReport::StatsType& type,
|
||||
const std::string& id,
|
||||
TrackDirection direction);
|
||||
StatsReport::Direction direction);
|
||||
|
||||
// Helper method to get stats from the local audio tracks.
|
||||
void UpdateStatsFromExistingLocalAudioTracks();
|
||||
@ -140,10 +132,10 @@ class StatsCollector {
|
||||
// Helper method to get the id for the track identified by ssrc.
|
||||
// |direction| tells if the track is for sending or receiving.
|
||||
bool GetTrackIdBySsrc(uint32 ssrc, std::string* track_id,
|
||||
TrackDirection direction);
|
||||
StatsReport::Direction direction);
|
||||
|
||||
// A map from the report id to the report.
|
||||
StatsSet reports_;
|
||||
// A collection for all of our stats reports.
|
||||
StatsCollection reports_;
|
||||
// Raw pointer to the session the statistics are gathered from.
|
||||
WebRtcSession* const session_;
|
||||
double stats_gathering_started_;
|
||||
|
@ -48,6 +48,7 @@
|
||||
#include "webrtc/p2p/base/fakesession.h"
|
||||
|
||||
using cricket::StatsOptions;
|
||||
using rtc::scoped_ptr;
|
||||
using testing::_;
|
||||
using testing::DoAll;
|
||||
using testing::Field;
|
||||
@ -69,7 +70,6 @@ namespace webrtc {
|
||||
|
||||
// Error return values
|
||||
const char kNotFound[] = "NOT FOUND";
|
||||
const char kNoReports[] = "NO REPORTS";
|
||||
|
||||
// Constant names for track identification.
|
||||
const char kLocalTrackId[] = "local_track_id";
|
||||
@ -152,6 +152,7 @@ class FakeAudioTrack
|
||||
rtc::scoped_refptr<FakeAudioProcessor> processor_;
|
||||
};
|
||||
|
||||
// TODO(tommi): Use FindValue().
|
||||
bool GetValue(const StatsReport* report,
|
||||
StatsReport::StatsValueName name,
|
||||
std::string* value) {
|
||||
@ -164,52 +165,69 @@ bool GetValue(const StatsReport* report,
|
||||
return false;
|
||||
}
|
||||
|
||||
std::string ExtractStatsValue(const std::string& type,
|
||||
std::string ExtractStatsValue(const StatsReport::StatsType& type,
|
||||
const StatsReports& reports,
|
||||
StatsReport::StatsValueName name) {
|
||||
if (reports.empty()) {
|
||||
return kNoReports;
|
||||
}
|
||||
for (size_t i = 0; i < reports.size(); ++i) {
|
||||
if (reports[i]->type != type)
|
||||
continue;
|
||||
for (const auto* r : reports) {
|
||||
std::string ret;
|
||||
if (GetValue(reports[i], name, &ret)) {
|
||||
if (r->type() == type && GetValue(r, name, &ret))
|
||||
return ret;
|
||||
}
|
||||
}
|
||||
|
||||
return kNotFound;
|
||||
}
|
||||
|
||||
scoped_ptr<StatsReport::Id> TypedIdFromIdString(StatsReport::StatsType type,
|
||||
const std::string& value) {
|
||||
EXPECT_FALSE(value.empty());
|
||||
scoped_ptr<StatsReport::Id> id;
|
||||
if (value.empty())
|
||||
return id.Pass();
|
||||
|
||||
// This has assumptions about how the ID is constructed. As is, this is
|
||||
// OK since this is for testing purposes only, but if we ever need this
|
||||
// in production, we should add a generic method that does this.
|
||||
size_t index = value.find('_');
|
||||
EXPECT_NE(index, std::string::npos);
|
||||
if (index == std::string::npos || index == (value.length() - 1))
|
||||
return id.Pass();
|
||||
|
||||
id = StatsReport::NewTypedId(type, value.substr(index + 1));
|
||||
EXPECT_EQ(id->ToString(), value);
|
||||
return id.Pass();
|
||||
}
|
||||
|
||||
scoped_ptr<StatsReport::Id> IdFromCertIdString(const std::string& cert_id) {
|
||||
return TypedIdFromIdString(StatsReport::kStatsReportTypeCertificate, cert_id)
|
||||
.Pass();
|
||||
}
|
||||
|
||||
// Finds the |n|-th report of type |type| in |reports|.
|
||||
// |n| starts from 1 for finding the first report.
|
||||
const StatsReport* FindNthReportByType(
|
||||
const StatsReports& reports, const std::string& type, int n) {
|
||||
const StatsReports& reports, const StatsReport::StatsType& type, int n) {
|
||||
for (size_t i = 0; i < reports.size(); ++i) {
|
||||
if (reports[i]->type == type) {
|
||||
if (reports[i]->type() == type) {
|
||||
n--;
|
||||
if (n == 0)
|
||||
return reports[i];
|
||||
}
|
||||
}
|
||||
return NULL;
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
const StatsReport* FindReportById(const StatsReports& reports,
|
||||
const std::string& id) {
|
||||
const StatsReport::Id& id) {
|
||||
for (const auto* r : reports) {
|
||||
if (r->id().ToString() == id) {
|
||||
if (r->id().Equals(id))
|
||||
return r;
|
||||
}
|
||||
}
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
std::string ExtractSsrcStatsValue(StatsReports reports,
|
||||
StatsReport::StatsValueName name) {
|
||||
return ExtractStatsValue(
|
||||
StatsReport::kStatsReportTypeSsrc, reports, name);
|
||||
return ExtractStatsValue(StatsReport::kStatsReportTypeSsrc, reports, name);
|
||||
}
|
||||
|
||||
std::string ExtractBweStatsValue(StatsReports reports,
|
||||
@ -234,18 +252,18 @@ std::vector<std::string> DersToPems(
|
||||
|
||||
void CheckCertChainReports(const StatsReports& reports,
|
||||
const std::vector<std::string>& ders,
|
||||
const std::string& start_id) {
|
||||
std::string certificate_id = start_id;
|
||||
const StatsReport::Id& start_id) {
|
||||
scoped_ptr<StatsReport::Id> cert_id;
|
||||
const StatsReport::Id* certificate_id = &start_id;
|
||||
size_t i = 0;
|
||||
while (true) {
|
||||
const StatsReport* report = FindReportById(reports, certificate_id);
|
||||
const StatsReport* report = FindReportById(reports, *certificate_id);
|
||||
ASSERT_TRUE(report != NULL);
|
||||
|
||||
std::string der_base64;
|
||||
EXPECT_TRUE(GetValue(
|
||||
report, StatsReport::kStatsValueNameDer, &der_base64));
|
||||
std::string der = rtc::Base64::Decode(der_base64,
|
||||
rtc::Base64::DO_STRICT);
|
||||
std::string der = rtc::Base64::Decode(der_base64, rtc::Base64::DO_STRICT);
|
||||
EXPECT_EQ(ders[i], der);
|
||||
|
||||
std::string fingerprint_algorithm;
|
||||
@ -257,16 +275,20 @@ void CheckCertChainReports(const StatsReports& reports,
|
||||
std::string sha_1_str = rtc::DIGEST_SHA_1;
|
||||
EXPECT_EQ(sha_1_str, fingerprint_algorithm);
|
||||
|
||||
std::string dummy_fingerprint; // Value is not checked.
|
||||
EXPECT_TRUE(GetValue(
|
||||
report,
|
||||
StatsReport::kStatsValueNameFingerprint,
|
||||
&dummy_fingerprint));
|
||||
std::string fingerprint;
|
||||
EXPECT_TRUE(GetValue(report, StatsReport::kStatsValueNameFingerprint,
|
||||
&fingerprint));
|
||||
EXPECT_FALSE(fingerprint.empty());
|
||||
|
||||
++i;
|
||||
if (!GetValue(
|
||||
report, StatsReport::kStatsValueNameIssuerId, &certificate_id))
|
||||
std::string issuer_id;
|
||||
if (!GetValue(report, StatsReport::kStatsValueNameIssuerId,
|
||||
&issuer_id)) {
|
||||
break;
|
||||
}
|
||||
|
||||
cert_id = IdFromCertIdString(issuer_id).Pass();
|
||||
certificate_id = cert_id.get();
|
||||
}
|
||||
EXPECT_EQ(ders.size(), i);
|
||||
}
|
||||
@ -510,8 +532,8 @@ class StatsCollectorTest : public testing::Test {
|
||||
|
||||
std::string AddCandidateReport(StatsCollector* collector,
|
||||
const cricket::Candidate& candidate,
|
||||
const std::string& report_type) {
|
||||
return collector->AddCandidateReport(candidate, report_type);
|
||||
bool local) {
|
||||
return collector->AddCandidateReport(candidate, local);
|
||||
}
|
||||
|
||||
void SetupAndVerifyAudioTrackStats(
|
||||
@ -651,7 +673,8 @@ class StatsCollectorTest : public testing::Test {
|
||||
StatsReport::kStatsValueNameLocalCertificateId);
|
||||
if (local_ders.size() > 0) {
|
||||
EXPECT_NE(kNotFound, local_certificate_id);
|
||||
CheckCertChainReports(reports, local_ders, local_certificate_id);
|
||||
scoped_ptr<StatsReport::Id> id(IdFromCertIdString(local_certificate_id));
|
||||
CheckCertChainReports(reports, local_ders, *id.get());
|
||||
} else {
|
||||
EXPECT_EQ(kNotFound, local_certificate_id);
|
||||
}
|
||||
@ -663,7 +686,8 @@ class StatsCollectorTest : public testing::Test {
|
||||
StatsReport::kStatsValueNameRemoteCertificateId);
|
||||
if (remote_ders.size() > 0) {
|
||||
EXPECT_NE(kNotFound, remote_certificate_id);
|
||||
CheckCertChainReports(reports, remote_ders, remote_certificate_id);
|
||||
scoped_ptr<StatsReport::Id> id(IdFromCertIdString(remote_certificate_id));
|
||||
CheckCertChainReports(reports, remote_ders, *id.get());
|
||||
} else {
|
||||
EXPECT_EQ(kNotFound, remote_certificate_id);
|
||||
}
|
||||
@ -842,8 +866,7 @@ TEST_F(StatsCollectorTest, TrackObjectExistsWithoutUpdateStats) {
|
||||
StatsReports reports;
|
||||
stats.GetStats(NULL, &reports);
|
||||
EXPECT_EQ((size_t)1, reports.size());
|
||||
EXPECT_EQ(std::string(StatsReport::kStatsReportTypeTrack),
|
||||
reports[0]->type);
|
||||
EXPECT_EQ(StatsReport::kStatsReportTypeTrack, reports[0]->type());
|
||||
|
||||
std::string trackValue =
|
||||
ExtractStatsValue(StatsReport::kStatsReportTypeTrack,
|
||||
@ -953,8 +976,19 @@ TEST_F(StatsCollectorTest, TransportObjectLinkedFromSsrcObject) {
|
||||
reports,
|
||||
StatsReport::kStatsValueNameTransportId);
|
||||
ASSERT_NE(kNotFound, transport_id);
|
||||
const StatsReport* transport_report = FindReportById(reports,
|
||||
transport_id);
|
||||
// Transport id component ID will always be 1.
|
||||
// This has assumptions about how the ID is constructed. As is, this is
|
||||
// OK since this is for testing purposes only, but if we ever need this
|
||||
// in production, we should add a generic method that does this.
|
||||
size_t index = transport_id.find('-');
|
||||
ASSERT_NE(std::string::npos, index);
|
||||
std::string content = transport_id.substr(index + 1);
|
||||
index = content.rfind('-');
|
||||
ASSERT_NE(std::string::npos, index);
|
||||
content = content.substr(0, index);
|
||||
scoped_ptr<StatsReport::Id> id(StatsReport::NewComponentId(content, 1));
|
||||
ASSERT_EQ(transport_id, id->ToString());
|
||||
const StatsReport* transport_report = FindReportById(reports, *id.get());
|
||||
ASSERT_FALSE(transport_report == NULL);
|
||||
}
|
||||
|
||||
@ -1101,8 +1135,7 @@ TEST_F(StatsCollectorTest, IceCandidateReport) {
|
||||
c.set_address(local_address);
|
||||
c.set_priority(priority);
|
||||
c.set_network_type(network_type);
|
||||
std::string report_id = AddCandidateReport(
|
||||
&stats, c, StatsReport::kStatsReportTypeIceLocalCandidate);
|
||||
std::string report_id = AddCandidateReport(&stats, c, true);
|
||||
EXPECT_EQ("Cand-" + c.id(), report_id);
|
||||
|
||||
c = cricket::Candidate();
|
||||
@ -1112,8 +1145,7 @@ TEST_F(StatsCollectorTest, IceCandidateReport) {
|
||||
c.set_address(remote_address);
|
||||
c.set_priority(priority);
|
||||
c.set_network_type(network_type);
|
||||
report_id = AddCandidateReport(
|
||||
&stats, c, StatsReport::kStatsReportTypeIceRemoteCandidate);
|
||||
report_id = AddCandidateReport(&stats, c, false);
|
||||
EXPECT_EQ("Cand-" + c.id(), report_id);
|
||||
|
||||
stats.GetStats(NULL, &reports);
|
||||
|
@ -30,89 +30,179 @@
|
||||
using rtc::scoped_ptr;
|
||||
|
||||
namespace webrtc {
|
||||
namespace {
|
||||
|
||||
const char StatsReport::kStatsReportTypeSession[] = "googLibjingleSession";
|
||||
const char StatsReport::kStatsReportTypeBwe[] = "VideoBwe";
|
||||
const char StatsReport::kStatsReportTypeRemoteSsrc[] = "remoteSsrc";
|
||||
const char StatsReport::kStatsReportTypeSsrc[] = "ssrc";
|
||||
const char StatsReport::kStatsReportTypeTrack[] = "googTrack";
|
||||
const char StatsReport::kStatsReportTypeIceLocalCandidate[] = "localcandidate";
|
||||
const char StatsReport::kStatsReportTypeIceRemoteCandidate[] =
|
||||
"remotecandidate";
|
||||
const char StatsReport::kStatsReportTypeTransport[] = "googTransport";
|
||||
const char StatsReport::kStatsReportTypeComponent[] = "googComponent";
|
||||
const char StatsReport::kStatsReportTypeCandidatePair[] = "googCandidatePair";
|
||||
const char StatsReport::kStatsReportTypeCertificate[] = "googCertificate";
|
||||
const char StatsReport::kStatsReportTypeDataChannel[] = "datachannel";
|
||||
// The id of StatsReport of type kStatsReportTypeBwe.
|
||||
const char kStatsReportVideoBweId[] = "bweforvideo";
|
||||
|
||||
const char StatsReport::kStatsReportVideoBweId[] = "bweforvideo";
|
||||
|
||||
StatsReport::StatsReport(const StatsReport& src)
|
||||
: id_(src.id_),
|
||||
type(src.type),
|
||||
timestamp_(src.timestamp_),
|
||||
values_(src.values_) {
|
||||
// NOTE: These names need to be consistent with an external
|
||||
// specification (W3C Stats Identifiers).
|
||||
const char* InternalTypeToString(StatsReport::StatsType type) {
|
||||
switch (type) {
|
||||
case StatsReport::kStatsReportTypeSession:
|
||||
return "googLibjingleSession";
|
||||
case StatsReport::kStatsReportTypeBwe:
|
||||
return "VideoBwe";
|
||||
case StatsReport::kStatsReportTypeRemoteSsrc:
|
||||
return "remoteSsrc";
|
||||
case StatsReport::kStatsReportTypeSsrc:
|
||||
return "ssrc";
|
||||
case StatsReport::kStatsReportTypeTrack:
|
||||
return "googTrack";
|
||||
case StatsReport::kStatsReportTypeIceLocalCandidate:
|
||||
return "localcandidate";
|
||||
case StatsReport::kStatsReportTypeIceRemoteCandidate:
|
||||
return "remotecandidate";
|
||||
case StatsReport::kStatsReportTypeTransport:
|
||||
return "googTransport";
|
||||
case StatsReport::kStatsReportTypeComponent:
|
||||
return "googComponent";
|
||||
case StatsReport::kStatsReportTypeCandidatePair:
|
||||
return "googCandidatePair";
|
||||
case StatsReport::kStatsReportTypeCertificate:
|
||||
return "googCertificate";
|
||||
case StatsReport::kStatsReportTypeDataChannel:
|
||||
return "datachannel";
|
||||
}
|
||||
ASSERT(false);
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
StatsReport::StatsReport(const std::string& id)
|
||||
: id_(id), timestamp_(0) {
|
||||
}
|
||||
class BandwidthEstimationId : public StatsReport::Id {
|
||||
public:
|
||||
BandwidthEstimationId() : StatsReport::Id(StatsReport::kStatsReportTypeBwe) {}
|
||||
std::string ToString() const override { return kStatsReportVideoBweId; }
|
||||
};
|
||||
|
||||
StatsReport::StatsReport(scoped_ptr<StatsReport::Id> id)
|
||||
: id_(id->ToString()), timestamp_(0) {
|
||||
}
|
||||
class TypedId : public StatsReport::Id {
|
||||
public:
|
||||
static const char kSeparator = '_';
|
||||
TypedId(StatsReport::StatsType type, const std::string& id)
|
||||
: StatsReport::Id(type), id_(id) {}
|
||||
|
||||
// static
|
||||
scoped_ptr<StatsReport::Id> StatsReport::NewTypedId(
|
||||
StatsReport::StatsType type, const std::string& id) {
|
||||
std::string internal_id(type);
|
||||
internal_id += '_';
|
||||
internal_id += id;
|
||||
return scoped_ptr<Id>(new Id(internal_id)).Pass();
|
||||
}
|
||||
bool Equals(const Id& other) const override {
|
||||
return Id::Equals(other) &&
|
||||
static_cast<const TypedId&>(other).id_ == id_;
|
||||
}
|
||||
|
||||
StatsReport& StatsReport::operator=(const StatsReport& src) {
|
||||
ASSERT(id_ == src.id_);
|
||||
type = src.type;
|
||||
timestamp_ = src.timestamp_;
|
||||
values_ = src.values_;
|
||||
return *this;
|
||||
}
|
||||
std::string ToString() const override {
|
||||
return std::string(InternalTypeToString(type_)) + kSeparator + id_;
|
||||
}
|
||||
|
||||
// Operators provided for STL container/algorithm support.
|
||||
bool StatsReport::operator<(const StatsReport& other) const {
|
||||
return id_ < other.id_;
|
||||
}
|
||||
protected:
|
||||
const std::string id_;
|
||||
};
|
||||
|
||||
bool StatsReport::operator==(const StatsReport& other) const {
|
||||
return id_ == other.id_;
|
||||
}
|
||||
class IdWithDirection : public TypedId {
|
||||
public:
|
||||
IdWithDirection(StatsReport::StatsType type, const std::string& id,
|
||||
StatsReport::Direction direction)
|
||||
: TypedId(type, id), direction_(direction) {}
|
||||
|
||||
// Special support for being able to use std::find on a container
|
||||
// without requiring a new StatsReport instance.
|
||||
bool StatsReport::operator==(const std::string& other_id) const {
|
||||
return id_ == other_id;
|
||||
}
|
||||
bool Equals(const Id& other) const override {
|
||||
return TypedId::Equals(other) &&
|
||||
static_cast<const IdWithDirection&>(other).direction_ == direction_;
|
||||
}
|
||||
|
||||
// The copy ctor can't be declared as explicit due to problems with STL.
|
||||
StatsReport::Value::Value(const Value& other)
|
||||
: name(other.name), value(other.value) {
|
||||
}
|
||||
std::string ToString() const override {
|
||||
std::string ret(TypedId::ToString());
|
||||
ret += '_';
|
||||
ret += direction_ == StatsReport::kSend ? "send" : "recv";
|
||||
return ret;
|
||||
}
|
||||
|
||||
StatsReport::Value::Value(StatsValueName name)
|
||||
: name(name) {
|
||||
private:
|
||||
const StatsReport::Direction direction_;
|
||||
};
|
||||
|
||||
class CandidateId : public TypedId {
|
||||
public:
|
||||
CandidateId(bool local, const std::string& id)
|
||||
: TypedId(local ?
|
||||
StatsReport::kStatsReportTypeIceLocalCandidate :
|
||||
StatsReport::kStatsReportTypeIceRemoteCandidate,
|
||||
id) {
|
||||
}
|
||||
|
||||
std::string ToString() const override {
|
||||
return "Cand-" + id_;
|
||||
}
|
||||
};
|
||||
|
||||
class ComponentId : public StatsReport::Id {
|
||||
public:
|
||||
ComponentId(const std::string& content_name, int component)
|
||||
: ComponentId(StatsReport::kStatsReportTypeComponent, content_name,
|
||||
component) {}
|
||||
|
||||
bool Equals(const Id& other) const override {
|
||||
return Id::Equals(other) &&
|
||||
static_cast<const ComponentId&>(other).component_ == component_ &&
|
||||
static_cast<const ComponentId&>(other).content_name_ == content_name_;
|
||||
}
|
||||
|
||||
std::string ToString() const override {
|
||||
return ToString("Channel-");
|
||||
}
|
||||
|
||||
protected:
|
||||
ComponentId(StatsReport::StatsType type, const std::string& content_name,
|
||||
int component)
|
||||
: Id(type),
|
||||
content_name_(content_name),
|
||||
component_(component) {}
|
||||
|
||||
std::string ToString(const char* prefix) const {
|
||||
std::string ret(prefix);
|
||||
ret += content_name_;
|
||||
ret += '-';
|
||||
ret += rtc::ToString<>(component_);
|
||||
return ret;
|
||||
}
|
||||
|
||||
private:
|
||||
const std::string content_name_;
|
||||
const int component_;
|
||||
};
|
||||
|
||||
class CandidatePairId : public ComponentId {
|
||||
public:
|
||||
CandidatePairId(const std::string& content_name, int component, int index)
|
||||
: ComponentId(StatsReport::kStatsReportTypeCandidatePair, content_name,
|
||||
component),
|
||||
index_(index) {}
|
||||
|
||||
bool Equals(const Id& other) const override {
|
||||
return ComponentId::Equals(other) &&
|
||||
static_cast<const CandidatePairId&>(other).index_ == index_;
|
||||
}
|
||||
|
||||
std::string ToString() const override {
|
||||
std::string ret(ComponentId::ToString("Conn-"));
|
||||
ret += '-';
|
||||
ret += rtc::ToString<>(index_);
|
||||
return ret;
|
||||
}
|
||||
|
||||
private:
|
||||
const int index_;
|
||||
};
|
||||
|
||||
} // namespace
|
||||
|
||||
StatsReport::Id::Id(StatsType type) : type_(type) {}
|
||||
StatsReport::Id::~Id() {}
|
||||
|
||||
StatsReport::StatsType StatsReport::Id::type() const { return type_; }
|
||||
|
||||
bool StatsReport::Id::Equals(const Id& other) const {
|
||||
return other.type_ == type_;
|
||||
}
|
||||
|
||||
StatsReport::Value::Value(StatsValueName name, const std::string& value)
|
||||
: name(name), value(value) {
|
||||
}
|
||||
|
||||
StatsReport::Value& StatsReport::Value::operator=(const Value& other) {
|
||||
const_cast<StatsValueName&>(name) = other.name;
|
||||
value = other.value;
|
||||
return *this;
|
||||
}
|
||||
|
||||
const char* StatsReport::Value::display_name() const {
|
||||
switch (name) {
|
||||
case kStatsValueNameAudioOutputLevel:
|
||||
@ -331,6 +421,50 @@ const char* StatsReport::Value::display_name() const {
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
StatsReport::StatsReport(scoped_ptr<Id> id) : id_(id.Pass()), timestamp_(0.0) {
|
||||
ASSERT(id_.get());
|
||||
}
|
||||
|
||||
// static
|
||||
scoped_ptr<StatsReport::Id> StatsReport::NewBandwidthEstimationId() {
|
||||
return scoped_ptr<Id>(new BandwidthEstimationId()).Pass();
|
||||
}
|
||||
|
||||
// static
|
||||
scoped_ptr<StatsReport::Id> StatsReport::NewTypedId(
|
||||
StatsType type, const std::string& id) {
|
||||
return scoped_ptr<Id>(new TypedId(type, id)).Pass();
|
||||
}
|
||||
|
||||
// static
|
||||
scoped_ptr<StatsReport::Id> StatsReport::NewIdWithDirection(
|
||||
StatsType type, const std::string& id, StatsReport::Direction direction) {
|
||||
return scoped_ptr<Id>(new IdWithDirection(type, id, direction)).Pass();
|
||||
}
|
||||
|
||||
// static
|
||||
scoped_ptr<StatsReport::Id> StatsReport::NewCandidateId(
|
||||
bool local, const std::string& id) {
|
||||
return scoped_ptr<Id>(new CandidateId(local, id)).Pass();
|
||||
}
|
||||
|
||||
// static
|
||||
scoped_ptr<StatsReport::Id> StatsReport::NewComponentId(
|
||||
const std::string& content_name, int component) {
|
||||
return scoped_ptr<Id>(new ComponentId(content_name, component)).Pass();
|
||||
}
|
||||
|
||||
// static
|
||||
scoped_ptr<StatsReport::Id> StatsReport::NewCandidatePairId(
|
||||
const std::string& content_name, int component, int index) {
|
||||
return scoped_ptr<Id>(new CandidatePairId(content_name, component, index))
|
||||
.Pass();
|
||||
}
|
||||
|
||||
const char* StatsReport::TypeToString() const {
|
||||
return InternalTypeToString(id_->type());
|
||||
}
|
||||
|
||||
void StatsReport::AddValue(StatsReport::StatsValueName name,
|
||||
const std::string& value) {
|
||||
values_.push_back(ValuePtr(new Value(name, value)));
|
||||
@ -340,6 +474,7 @@ void StatsReport::AddValue(StatsReport::StatsValueName name, int64 value) {
|
||||
AddValue(name, rtc::ToString<int64>(value));
|
||||
}
|
||||
|
||||
// TODO(tommi): Change the way we store vector values.
|
||||
template <typename T>
|
||||
void StatsReport::AddValue(StatsReport::StatsValueName name,
|
||||
const std::vector<T>& value) {
|
||||
@ -370,6 +505,7 @@ void StatsReport::AddValue<int64_t>(
|
||||
StatsReport::StatsValueName, const std::vector<int64_t>&);
|
||||
|
||||
void StatsReport::AddBoolean(StatsReport::StatsValueName name, bool value) {
|
||||
// TODO(tommi): Store bools as bool.
|
||||
AddValue(name, value ? "true" : "false");
|
||||
}
|
||||
|
||||
@ -392,42 +528,67 @@ void StatsReport::ResetValues() {
|
||||
values_.clear();
|
||||
}
|
||||
|
||||
StatsSet::StatsSet() {
|
||||
const StatsReport::Value* StatsReport::FindValue(StatsValueName name) const {
|
||||
Values::const_iterator it = std::find_if(values_.begin(), values_.end(),
|
||||
[&name](const ValuePtr& v)->bool { return v->name == name; });
|
||||
return it == values_.end() ? nullptr : (*it).get();
|
||||
}
|
||||
|
||||
StatsSet::~StatsSet() {
|
||||
StatsCollection::StatsCollection() {
|
||||
}
|
||||
|
||||
StatsSet::const_iterator StatsSet::begin() const {
|
||||
StatsCollection::~StatsCollection() {
|
||||
for (auto* r : list_)
|
||||
delete r;
|
||||
}
|
||||
|
||||
StatsCollection::const_iterator StatsCollection::begin() const {
|
||||
return list_.begin();
|
||||
}
|
||||
|
||||
StatsSet::const_iterator StatsSet::end() const {
|
||||
StatsCollection::const_iterator StatsCollection::end() const {
|
||||
return list_.end();
|
||||
}
|
||||
|
||||
StatsReport* StatsSet::InsertNew(const std::string& id) {
|
||||
ASSERT(Find(id) == NULL);
|
||||
const StatsReport* ret = &(*list_.insert(StatsReportCopyable(id)).first);
|
||||
return const_cast<StatsReport*>(ret);
|
||||
size_t StatsCollection::size() const {
|
||||
return list_.size();
|
||||
}
|
||||
|
||||
StatsReport* StatsSet::FindOrAddNew(const std::string& id) {
|
||||
StatsReport* ret = Find(id);
|
||||
return ret ? ret : InsertNew(id);
|
||||
StatsReport* StatsCollection::InsertNew(scoped_ptr<StatsReport::Id> id) {
|
||||
ASSERT(Find(*id.get()) == NULL);
|
||||
StatsReport* report = new StatsReport(id.Pass());
|
||||
list_.push_back(report);
|
||||
return report;
|
||||
}
|
||||
|
||||
StatsReport* StatsSet::ReplaceOrAddNew(const std::string& id) {
|
||||
list_.erase(id);
|
||||
return InsertNew(id);
|
||||
StatsReport* StatsCollection::FindOrAddNew(scoped_ptr<StatsReport::Id> id) {
|
||||
StatsReport* ret = Find(*id.get());
|
||||
return ret ? ret : InsertNew(id.Pass());
|
||||
}
|
||||
|
||||
StatsReport* StatsCollection::ReplaceOrAddNew(scoped_ptr<StatsReport::Id> id) {
|
||||
ASSERT(id.get());
|
||||
Container::iterator it = std::find_if(list_.begin(), list_.end(),
|
||||
[&id](const StatsReport* r)->bool { return r->id().Equals(*id.get()); });
|
||||
if (it != end()) {
|
||||
delete *it;
|
||||
StatsReport* report = new StatsReport(id.Pass());
|
||||
*it = report;
|
||||
return report;
|
||||
}
|
||||
return InsertNew(id.Pass());
|
||||
}
|
||||
|
||||
// Looks for a report with the given |id|. If one is not found, NULL
|
||||
// will be returned.
|
||||
StatsReport* StatsSet::Find(const std::string& id) {
|
||||
const_iterator it = std::find(begin(), end(), id);
|
||||
return it == end() ? NULL :
|
||||
const_cast<StatsReport*>(static_cast<const StatsReport*>(&(*it)));
|
||||
StatsReport* StatsCollection::Find(const StatsReport::Id& id) {
|
||||
Container::iterator it = std::find_if(list_.begin(), list_.end(),
|
||||
[&id](const StatsReport* r)->bool { return r->id().Equals(id); });
|
||||
return it == list_.end() ? nullptr : *it;
|
||||
}
|
||||
|
||||
StatsReport* StatsCollection::Find(const scoped_ptr<StatsReport::Id>& id) {
|
||||
return Find(*id.get());
|
||||
}
|
||||
|
||||
} // namespace webrtc
|
||||
|
@ -32,11 +32,13 @@
|
||||
#define TALK_APP_WEBRTC_STATSTYPES_H_
|
||||
|
||||
#include <algorithm>
|
||||
#include <set>
|
||||
#include <list>
|
||||
#include <string>
|
||||
#include <vector>
|
||||
|
||||
#include "webrtc/base/basictypes.h"
|
||||
#include "webrtc/base/linked_ptr.h"
|
||||
#include "webrtc/base/scoped_ptr.h"
|
||||
#include "webrtc/base/common.h"
|
||||
#include "webrtc/base/linked_ptr.h"
|
||||
#include "webrtc/base/scoped_ptr.h"
|
||||
@ -46,35 +48,71 @@ namespace webrtc {
|
||||
|
||||
class StatsReport {
|
||||
public:
|
||||
// TODO(tommi): Remove this ctor after removing reliance upon it in Chromium
|
||||
// (mock_peer_connection_impl.cc).
|
||||
StatsReport() : timestamp_(0) {}
|
||||
// Indicates whether a track is for sending or receiving.
|
||||
// Used in reports for audio/video tracks.
|
||||
enum Direction {
|
||||
kSend = 0,
|
||||
kReceive,
|
||||
};
|
||||
|
||||
// TODO(tommi): Make protected and disallow copy completely once not needed.
|
||||
StatsReport(const StatsReport& src);
|
||||
enum StatsType {
|
||||
// StatsReport types.
|
||||
// A StatsReport of |type| = "googSession" contains overall information
|
||||
// about the thing libjingle calls a session (which may contain one
|
||||
// or more RTP sessions.
|
||||
kStatsReportTypeSession,
|
||||
|
||||
// Constructor is protected to force use of StatsSet.
|
||||
// TODO(tommi): Make this ctor protected.
|
||||
explicit StatsReport(const std::string& id);
|
||||
// A StatsReport of |type| = "googTransport" contains information
|
||||
// about a libjingle "transport".
|
||||
kStatsReportTypeTransport,
|
||||
|
||||
// TODO(tommi): Make this protected.
|
||||
StatsReport& operator=(const StatsReport& src);
|
||||
// A StatsReport of |type| = "googComponent" contains information
|
||||
// about a libjingle "channel" (typically, RTP or RTCP for a transport).
|
||||
// This is intended to be the same thing as an ICE "Component".
|
||||
kStatsReportTypeComponent,
|
||||
|
||||
// Operators provided for STL container/algorithm support.
|
||||
bool operator<(const StatsReport& other) const;
|
||||
bool operator==(const StatsReport& other) const;
|
||||
// Special support for being able to use std::find on a container
|
||||
// without requiring a new StatsReport instance.
|
||||
bool operator==(const std::string& other_id) const;
|
||||
// A StatsReport of |type| = "googCandidatePair" contains information
|
||||
// about a libjingle "connection" - a single source/destination port pair.
|
||||
// This is intended to be the same thing as an ICE "candidate pair".
|
||||
kStatsReportTypeCandidatePair,
|
||||
|
||||
// The unique identifier for this object.
|
||||
// This is used as a key for this report in ordered containers,
|
||||
// so it must never be changed.
|
||||
// TODO(tommi): Make this member variable const.
|
||||
std::string id_; // See below for contents.
|
||||
std::string type; // See below for contents.
|
||||
// A StatsReport of |type| = "VideoBWE" is statistics for video Bandwidth
|
||||
// Estimation, which is global per-session. The |id| field is "bweforvideo"
|
||||
// (will probably change in the future).
|
||||
kStatsReportTypeBwe,
|
||||
|
||||
// A StatsReport of |type| = "ssrc" is statistics for a specific rtp stream.
|
||||
// The |id| field is the SSRC in decimal form of the rtp stream.
|
||||
kStatsReportTypeSsrc,
|
||||
|
||||
// A StatsReport of |type| = "remoteSsrc" is statistics for a specific
|
||||
// rtp stream, generated by the remote end of the connection.
|
||||
kStatsReportTypeRemoteSsrc,
|
||||
|
||||
// A StatsReport of |type| = "googTrack" is statistics for a specific media
|
||||
// track. The |id| field is the track id.
|
||||
kStatsReportTypeTrack,
|
||||
|
||||
// A StatsReport of |type| = "localcandidate" or "remotecandidate" is
|
||||
// attributes on a specific ICE Candidate. It links to its connection pair
|
||||
// by candidate id. The string value is taken from
|
||||
// http://w3c.github.io/webrtc-stats/#rtcstatstype-enum*.
|
||||
kStatsReportTypeIceLocalCandidate,
|
||||
kStatsReportTypeIceRemoteCandidate,
|
||||
|
||||
// A StatsReport of |type| = "googCertificate" contains an SSL certificate
|
||||
// transmitted by one of the endpoints of this connection. The |id| is
|
||||
// controlled by the fingerprint, and is used to identify the certificate in
|
||||
// the Channel stats (as "googLocalCertificateId" or
|
||||
// "googRemoteCertificateId") and in any child certificates (as
|
||||
// "googIssuerId").
|
||||
kStatsReportTypeCertificate,
|
||||
|
||||
// A StatsReport of |type| = "datachannel" with statistics for a
|
||||
// particular DataChannel.
|
||||
kStatsReportTypeDataChannel,
|
||||
};
|
||||
|
||||
// StatsValue names.
|
||||
enum StatsValueName {
|
||||
kStatsValueNameActiveConnection,
|
||||
kStatsValueNameAudioInputLevel,
|
||||
@ -183,40 +221,54 @@ class StatsReport {
|
||||
|
||||
class Id {
|
||||
public:
|
||||
Id(const std::string& id) : id_(id) {}
|
||||
Id(const Id& id) : id_(id.id_) {}
|
||||
const std::string& ToString() const { return id_; }
|
||||
private:
|
||||
const std::string id_;
|
||||
virtual ~Id();
|
||||
StatsType type() const;
|
||||
virtual bool Equals(const Id& other) const;
|
||||
virtual std::string ToString() const = 0;
|
||||
|
||||
protected:
|
||||
Id(StatsType type); // Only meant for derived classes.
|
||||
const StatsType type_;
|
||||
};
|
||||
|
||||
struct Value {
|
||||
// The copy ctor can't be declared as explicit due to problems with STL.
|
||||
Value(const Value& other);
|
||||
explicit Value(StatsValueName name);
|
||||
Value(StatsValueName name, const std::string& value);
|
||||
|
||||
// TODO(tommi): Remove this operator once we don't need it.
|
||||
// The operator is provided for compatibility with STL containers.
|
||||
// The public |name| member variable is otherwise meant to be read-only.
|
||||
Value& operator=(const Value& other);
|
||||
|
||||
// Returns the string representation of |name|.
|
||||
const char* display_name() const;
|
||||
|
||||
const StatsValueName name;
|
||||
// TODO(tommi): Support more value types than string.
|
||||
const std::string value;
|
||||
|
||||
std::string value;
|
||||
private:
|
||||
DISALLOW_COPY_AND_ASSIGN(Value);
|
||||
};
|
||||
|
||||
typedef rtc::linked_ptr<Value> ValuePtr;
|
||||
typedef std::vector<ValuePtr> Values;
|
||||
typedef const char* StatsType;
|
||||
|
||||
// Ownership of |id| is passed to |this|.
|
||||
explicit StatsReport(rtc::scoped_ptr<Id> id);
|
||||
|
||||
// Factory functions for various types of stats IDs.
|
||||
static rtc::scoped_ptr<Id> NewBandwidthEstimationId();
|
||||
static rtc::scoped_ptr<Id> NewTypedId(StatsType type, const std::string& id);
|
||||
static rtc::scoped_ptr<Id> NewIdWithDirection(
|
||||
StatsType type, const std::string& id, Direction direction);
|
||||
static rtc::scoped_ptr<Id> NewCandidateId(bool local, const std::string& id);
|
||||
static rtc::scoped_ptr<Id> NewComponentId(
|
||||
const std::string& content_name, int component);
|
||||
static rtc::scoped_ptr<Id> NewCandidatePairId(
|
||||
const std::string& content_name, int component, int index);
|
||||
|
||||
const Id& id() const { return *id_.get(); }
|
||||
StatsType type() const { return id_->type(); }
|
||||
double timestamp() const { return timestamp_; }
|
||||
void set_timestamp(double t) { timestamp_ = t; }
|
||||
const Values& values() const { return values_; }
|
||||
|
||||
const char* TypeToString() const;
|
||||
|
||||
void AddValue(StatsValueName name, const std::string& value);
|
||||
void AddValue(StatsValueName name, int64 value);
|
||||
@ -228,122 +280,55 @@ class StatsReport {
|
||||
|
||||
void ResetValues();
|
||||
|
||||
const Id id() const { return Id(id_); }
|
||||
double timestamp() const { return timestamp_; }
|
||||
void set_timestamp(double t) { timestamp_ = t; }
|
||||
const Values& values() const { return values_; }
|
||||
|
||||
const char* TypeToString() const { return type.c_str(); }
|
||||
const Value* FindValue(StatsValueName name) const;
|
||||
|
||||
private:
|
||||
// The unique identifier for this object.
|
||||
// This is used as a key for this report in ordered containers,
|
||||
// so it must never be changed.
|
||||
const rtc::scoped_ptr<Id> id_;
|
||||
double timestamp_; // Time since 1970-01-01T00:00:00Z in milliseconds.
|
||||
Values values_;
|
||||
|
||||
// TODO(tommi): These should all be enum values.
|
||||
|
||||
// StatsReport types.
|
||||
// A StatsReport of |type| = "googSession" contains overall information
|
||||
// about the thing libjingle calls a session (which may contain one
|
||||
// or more RTP sessions.
|
||||
static const char kStatsReportTypeSession[];
|
||||
|
||||
// A StatsReport of |type| = "googTransport" contains information
|
||||
// about a libjingle "transport".
|
||||
static const char kStatsReportTypeTransport[];
|
||||
|
||||
// A StatsReport of |type| = "googComponent" contains information
|
||||
// about a libjingle "channel" (typically, RTP or RTCP for a transport).
|
||||
// This is intended to be the same thing as an ICE "Component".
|
||||
static const char kStatsReportTypeComponent[];
|
||||
|
||||
// A StatsReport of |type| = "googCandidatePair" contains information
|
||||
// about a libjingle "connection" - a single source/destination port pair.
|
||||
// This is intended to be the same thing as an ICE "candidate pair".
|
||||
static const char kStatsReportTypeCandidatePair[];
|
||||
|
||||
// StatsReport of |type| = "VideoBWE" is statistics for video Bandwidth
|
||||
// Estimation, which is global per-session. The |id| field is "bweforvideo"
|
||||
// (will probably change in the future).
|
||||
static const char kStatsReportTypeBwe[];
|
||||
|
||||
// StatsReport of |type| = "ssrc" is statistics for a specific rtp stream.
|
||||
// The |id| field is the SSRC in decimal form of the rtp stream.
|
||||
static const char kStatsReportTypeSsrc[];
|
||||
|
||||
// StatsReport of |type| = "remoteSsrc" is statistics for a specific
|
||||
// rtp stream, generated by the remote end of the connection.
|
||||
static const char kStatsReportTypeRemoteSsrc[];
|
||||
|
||||
// StatsReport of |type| = "googTrack" is statistics for a specific media
|
||||
// track. The |id| field is the track id.
|
||||
static const char kStatsReportTypeTrack[];
|
||||
|
||||
// StatsReport of |type| = "localcandidate" or "remotecandidate" is attributes
|
||||
// on a specific ICE Candidate. It links to its connection pair by candidate
|
||||
// id. The string value is taken from
|
||||
// http://w3c.github.io/webrtc-stats/#rtcstatstype-enum*.
|
||||
static const char kStatsReportTypeIceLocalCandidate[];
|
||||
static const char kStatsReportTypeIceRemoteCandidate[];
|
||||
|
||||
// A StatsReport of |type| = "googCertificate" contains an SSL certificate
|
||||
// transmitted by one of the endpoints of this connection. The |id| is
|
||||
// controlled by the fingerprint, and is used to identify the certificate in
|
||||
// the Channel stats (as "googLocalCertificateId" or
|
||||
// "googRemoteCertificateId") and in any child certificates (as
|
||||
// "googIssuerId").
|
||||
static const char kStatsReportTypeCertificate[];
|
||||
|
||||
// The id of StatsReport of type VideoBWE.
|
||||
static const char kStatsReportVideoBweId[];
|
||||
|
||||
// A StatsReport of |type| = "datachannel" with statistics for a
|
||||
// particular DataChannel.
|
||||
static const char kStatsReportTypeDataChannel[];
|
||||
};
|
||||
|
||||
// This class is provided for the cases where we need to keep
|
||||
// snapshots of reports around. This is an edge case.
|
||||
// TODO(tommi): Move into the private section of StatsSet.
|
||||
class StatsReportCopyable : public StatsReport {
|
||||
public:
|
||||
StatsReportCopyable(const std::string& id) : StatsReport(id) {}
|
||||
explicit StatsReportCopyable(const StatsReport& src)
|
||||
: StatsReport(src) {}
|
||||
|
||||
using StatsReport::operator=;
|
||||
DISALLOW_COPY_AND_ASSIGN(StatsReport);
|
||||
};
|
||||
|
||||
// Typedef for an array of const StatsReport pointers.
|
||||
// Ownership of the pointers held by this implementation is assumed to lie
|
||||
// elsewhere and lifetime guarantees are made by the implementation that uses
|
||||
// this type. In the StatsCollector, object ownership lies with the StatsSet
|
||||
// class.
|
||||
// this type. In the StatsCollector, object ownership lies with the
|
||||
// StatsCollection class.
|
||||
typedef std::vector<const StatsReport*> StatsReports;
|
||||
|
||||
// A map from the report id to the report.
|
||||
// This class wraps an STL container and provides a limited set of
|
||||
// functionality in order to keep things simple.
|
||||
// TODO(tommi): Use a thread checker here (currently not in libjingle).
|
||||
class StatsSet {
|
||||
class StatsCollection {
|
||||
public:
|
||||
StatsSet();
|
||||
~StatsSet();
|
||||
StatsCollection();
|
||||
~StatsCollection();
|
||||
|
||||
typedef std::set<StatsReportCopyable> Container;
|
||||
// TODO(tommi): shared_ptr (or linked_ptr)?
|
||||
typedef std::list<StatsReport*> Container;
|
||||
typedef Container::iterator iterator;
|
||||
typedef Container::const_iterator const_iterator;
|
||||
|
||||
const_iterator begin() const;
|
||||
const_iterator end() const;
|
||||
size_t size() const;
|
||||
|
||||
// Creates a new report object with |id| that does not already
|
||||
// exist in the list of reports.
|
||||
StatsReport* InsertNew(const std::string& id);
|
||||
StatsReport* FindOrAddNew(const std::string& id);
|
||||
StatsReport* ReplaceOrAddNew(const std::string& id);
|
||||
StatsReport* InsertNew(rtc::scoped_ptr<StatsReport::Id> id);
|
||||
StatsReport* FindOrAddNew(rtc::scoped_ptr<StatsReport::Id> id);
|
||||
StatsReport* ReplaceOrAddNew(rtc::scoped_ptr<StatsReport::Id> id);
|
||||
|
||||
// Looks for a report with the given |id|. If one is not found, NULL
|
||||
// will be returned.
|
||||
StatsReport* Find(const std::string& id);
|
||||
StatsReport* Find(const StatsReport::Id& id);
|
||||
// TODO(tommi): we should only need one of these.
|
||||
StatsReport* Find(const rtc::scoped_ptr<StatsReport::Id>& id);
|
||||
|
||||
private:
|
||||
Container list_;
|
||||
|
@ -126,7 +126,7 @@ class MockStatsObserver : public webrtc::StatsObserver {
|
||||
memset(&stats_, sizeof(stats_), 0);
|
||||
stats_.number_of_reports = reports.size();
|
||||
for (const auto* r : reports) {
|
||||
if (r->type == StatsReport::kStatsReportTypeSsrc) {
|
||||
if (r->type() == StatsReport::kStatsReportTypeSsrc) {
|
||||
GetIntValue(r, StatsReport::kStatsValueNameAudioOutputLevel,
|
||||
&stats_.audio_output_level);
|
||||
GetIntValue(r, StatsReport::kStatsValueNameAudioInputLevel,
|
||||
@ -135,7 +135,7 @@ class MockStatsObserver : public webrtc::StatsObserver {
|
||||
&stats_.bytes_received);
|
||||
GetIntValue(r, StatsReport::kStatsValueNameBytesSent,
|
||||
&stats_.bytes_sent);
|
||||
} else if (r->type == StatsReport::kStatsReportTypeBwe) {
|
||||
} else if (r->type() == StatsReport::kStatsReportTypeBwe) {
|
||||
GetIntValue(r, StatsReport::kStatsValueNameAvailableReceiveBandwidth,
|
||||
&stats_.available_receive_bandwidth);
|
||||
}
|
||||
|
Loading…
Reference in New Issue
Block a user