Refactor StatsCollector and associated types.
* Due to the type changes, I'm going to update the OnCompleted event in two phases to sync with Chrome. This is the first phase. * Reports are now managed in a set, not a map, since it's enough to store the id in one place. * Report ids are now const. * Copying of data has been greatly reduced. * This change includes preparation work for making GetStats fully async. R=xians@webrtc.org Committed: https://code.google.com/p/webrtc/source/detail?r=6745 Review URL: https://webrtc-codereview.appspot.com/18819004 git-svn-id: http://webrtc.googlecode.com/svn/trunk@6747 4adac7df-926f-26a2-2b94-8c16560cd09d
This commit is contained in:
parent
fd61a1d693
commit
756b8462eb
@ -85,11 +85,12 @@ struct SetSessionDescriptionMsg : public talk_base::MessageData {
|
|||||||
};
|
};
|
||||||
|
|
||||||
struct GetStatsMsg : public talk_base::MessageData {
|
struct GetStatsMsg : public talk_base::MessageData {
|
||||||
explicit GetStatsMsg(webrtc::StatsObserver* observer)
|
GetStatsMsg(webrtc::StatsObserver* observer,
|
||||||
: observer(observer) {
|
webrtc::MediaStreamTrackInterface* track)
|
||||||
|
: observer(observer), track(track) {
|
||||||
}
|
}
|
||||||
webrtc::StatsReports reports;
|
|
||||||
talk_base::scoped_refptr<webrtc::StatsObserver> observer;
|
talk_base::scoped_refptr<webrtc::StatsObserver> observer;
|
||||||
|
talk_base::scoped_refptr<webrtc::MediaStreamTrackInterface> track;
|
||||||
};
|
};
|
||||||
|
|
||||||
// |in_str| should be of format
|
// |in_str| should be of format
|
||||||
@ -446,17 +447,15 @@ talk_base::scoped_refptr<DtmfSenderInterface> PeerConnection::CreateDtmfSender(
|
|||||||
bool PeerConnection::GetStats(StatsObserver* observer,
|
bool PeerConnection::GetStats(StatsObserver* observer,
|
||||||
MediaStreamTrackInterface* track,
|
MediaStreamTrackInterface* track,
|
||||||
StatsOutputLevel level) {
|
StatsOutputLevel level) {
|
||||||
|
ASSERT(signaling_thread()->IsCurrent());
|
||||||
if (!VERIFY(observer != NULL)) {
|
if (!VERIFY(observer != NULL)) {
|
||||||
LOG(LS_ERROR) << "GetStats - observer is NULL.";
|
LOG(LS_ERROR) << "GetStats - observer is NULL.";
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
stats_->UpdateStats(level);
|
stats_->UpdateStats(level);
|
||||||
talk_base::scoped_ptr<GetStatsMsg> msg(new GetStatsMsg(observer));
|
signaling_thread()->Post(this, MSG_GETSTATS,
|
||||||
if (!stats_->GetStats(track, &(msg->reports))) {
|
new GetStatsMsg(observer, track));
|
||||||
return false;
|
|
||||||
}
|
|
||||||
signaling_thread()->Post(this, MSG_GETSTATS, msg.release());
|
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -702,7 +701,9 @@ void PeerConnection::OnMessage(talk_base::Message* msg) {
|
|||||||
}
|
}
|
||||||
case MSG_GETSTATS: {
|
case MSG_GETSTATS: {
|
||||||
GetStatsMsg* param = static_cast<GetStatsMsg*>(msg->pdata);
|
GetStatsMsg* param = static_cast<GetStatsMsg*>(msg->pdata);
|
||||||
param->observer->OnComplete(param->reports);
|
StatsReports reports;
|
||||||
|
stats_->GetStats(param->track, &reports);
|
||||||
|
param->observer->OnComplete(reports);
|
||||||
delete param;
|
delete param;
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
@ -113,7 +113,18 @@ class StreamCollectionInterface : public talk_base::RefCountInterface {
|
|||||||
|
|
||||||
class StatsObserver : public talk_base::RefCountInterface {
|
class StatsObserver : public talk_base::RefCountInterface {
|
||||||
public:
|
public:
|
||||||
virtual void OnComplete(const std::vector<StatsReport>& reports) = 0;
|
// TODO(tommi): Remove.
|
||||||
|
virtual void OnComplete(const std::vector<StatsReport>& reports) {}
|
||||||
|
|
||||||
|
// TODO(tommi): Make pure virtual and remove implementation.
|
||||||
|
virtual void OnComplete(const StatsReports& reports) {
|
||||||
|
std::vector<StatsReportCopyable> report_copies;
|
||||||
|
for (size_t i = 0; i < reports.size(); ++i)
|
||||||
|
report_copies.push_back(StatsReportCopyable(*reports[i]));
|
||||||
|
std::vector<StatsReport>* r =
|
||||||
|
reinterpret_cast<std::vector<StatsReport>*>(&report_copies);
|
||||||
|
OnComplete(*r);
|
||||||
|
}
|
||||||
|
|
||||||
protected:
|
protected:
|
||||||
virtual ~StatsObserver() {}
|
virtual ~StatsObserver() {}
|
||||||
|
@ -234,7 +234,6 @@ void StatsReport::ReplaceValue(StatsReport::StatsValueName name,
|
|||||||
}
|
}
|
||||||
|
|
||||||
namespace {
|
namespace {
|
||||||
typedef std::map<std::string, StatsReport> StatsMap;
|
|
||||||
|
|
||||||
double GetTimeNow() {
|
double GetTimeNow() {
|
||||||
return talk_base::Timing::WallTimeNow() * talk_base::kNumMillisecsPerSec;
|
return talk_base::Timing::WallTimeNow() * talk_base::kNumMillisecsPerSec;
|
||||||
@ -294,17 +293,16 @@ bool ExtractValueFromReport(
|
|||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
void AddTrackReport(StatsMap* reports, const std::string& track_id) {
|
void AddTrackReport(StatsSet* reports, const std::string& track_id) {
|
||||||
// Adds an empty track report.
|
// Adds an empty track report.
|
||||||
StatsReport report;
|
StatsReport* report = reports->FindOrAddNew(
|
||||||
report.type = StatsReport::kStatsReportTypeTrack;
|
StatsId(StatsReport::kStatsReportTypeTrack, track_id));
|
||||||
report.id = StatsId(StatsReport::kStatsReportTypeTrack, track_id);
|
report->type = StatsReport::kStatsReportTypeTrack;
|
||||||
report.AddValue(StatsReport::kStatsValueNameTrackId, track_id);
|
report->AddValue(StatsReport::kStatsValueNameTrackId, track_id);
|
||||||
(*reports)[report.id] = report;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
template <class TrackVector>
|
template <class TrackVector>
|
||||||
void CreateTrackReports(const TrackVector& tracks, StatsMap* reports) {
|
void CreateTrackReports(const TrackVector& tracks, StatsSet* reports) {
|
||||||
for (size_t j = 0; j < tracks.size(); ++j) {
|
for (size_t j = 0; j < tracks.size(); ++j) {
|
||||||
webrtc::MediaStreamTrackInterface* track = tracks[j];
|
webrtc::MediaStreamTrackInterface* track = tracks[j];
|
||||||
AddTrackReport(reports, track->id());
|
AddTrackReport(reports, track->id());
|
||||||
@ -469,7 +467,7 @@ void ExtractStats(const cricket::BandwidthEstimationInfo& info,
|
|||||||
double stats_gathering_started,
|
double stats_gathering_started,
|
||||||
PeerConnectionInterface::StatsOutputLevel level,
|
PeerConnectionInterface::StatsOutputLevel level,
|
||||||
StatsReport* report) {
|
StatsReport* report) {
|
||||||
report->id = StatsReport::kStatsReportVideoBweId;
|
ASSERT(report->id == StatsReport::kStatsReportVideoBweId);
|
||||||
report->type = StatsReport::kStatsReportTypeBwe;
|
report->type = StatsReport::kStatsReportTypeBwe;
|
||||||
|
|
||||||
// Clear out stats from previous GatherStats calls if any.
|
// Clear out stats from previous GatherStats calls if any.
|
||||||
@ -583,9 +581,9 @@ void StatsCollector::AddLocalAudioTrack(AudioTrackInterface* audio_track,
|
|||||||
|
|
||||||
// Create the kStatsReportTypeTrack report for the new track if there is no
|
// Create the kStatsReportTypeTrack report for the new track if there is no
|
||||||
// report yet.
|
// report yet.
|
||||||
StatsMap::iterator it = reports_.find(
|
StatsReport* found = reports_.Find(
|
||||||
StatsId(StatsReport::kStatsReportTypeTrack, audio_track->id()));
|
StatsId(StatsReport::kStatsReportTypeTrack, audio_track->id()));
|
||||||
if (it == reports_.end())
|
if (!found)
|
||||||
AddTrackReport(&reports_, audio_track->id());
|
AddTrackReport(&reports_, audio_track->id());
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -603,49 +601,46 @@ void StatsCollector::RemoveLocalAudioTrack(AudioTrackInterface* audio_track,
|
|||||||
ASSERT(false);
|
ASSERT(false);
|
||||||
}
|
}
|
||||||
|
|
||||||
bool StatsCollector::GetStats(MediaStreamTrackInterface* track,
|
void StatsCollector::GetStats(MediaStreamTrackInterface* track,
|
||||||
StatsReports* reports) {
|
StatsReports* reports) {
|
||||||
ASSERT(reports != NULL);
|
ASSERT(reports != NULL);
|
||||||
reports->clear();
|
ASSERT(reports->empty());
|
||||||
|
|
||||||
StatsMap::iterator it;
|
|
||||||
if (!track) {
|
if (!track) {
|
||||||
for (it = reports_.begin(); it != reports_.end(); ++it) {
|
StatsSet::const_iterator it;
|
||||||
reports->push_back(it->second);
|
for (it = reports_.begin(); it != reports_.end(); ++it)
|
||||||
}
|
reports->push_back(&(*it));
|
||||||
return true;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
it = reports_.find(StatsId(StatsReport::kStatsReportTypeSession,
|
StatsReport* report =
|
||||||
|
reports_.Find(StatsId(StatsReport::kStatsReportTypeSession,
|
||||||
session_->id()));
|
session_->id()));
|
||||||
if (it != reports_.end()) {
|
if (report)
|
||||||
reports->push_back(it->second);
|
reports->push_back(report);
|
||||||
}
|
|
||||||
|
|
||||||
it = reports_.find(StatsId(StatsReport::kStatsReportTypeTrack, track->id()));
|
report = reports_.Find(
|
||||||
|
StatsId(StatsReport::kStatsReportTypeTrack, track->id()));
|
||||||
|
|
||||||
if (it == reports_.end()) {
|
if (!report)
|
||||||
LOG(LS_WARNING) << "No StatsReport is available for "<< track->id();
|
return;
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
reports->push_back(it->second);
|
reports->push_back(report);
|
||||||
|
|
||||||
std::string track_id;
|
std::string track_id;
|
||||||
for (it = reports_.begin(); it != reports_.end(); ++it) {
|
for (StatsSet::const_iterator it = reports_.begin(); it != reports_.end();
|
||||||
if (it->second.type != StatsReport::kStatsReportTypeSsrc) {
|
++it) {
|
||||||
|
if (it->type != StatsReport::kStatsReportTypeSsrc)
|
||||||
continue;
|
continue;
|
||||||
}
|
|
||||||
if (ExtractValueFromReport(it->second,
|
if (ExtractValueFromReport(*it,
|
||||||
StatsReport::kStatsValueNameTrackId,
|
StatsReport::kStatsValueNameTrackId,
|
||||||
&track_id)) {
|
&track_id)) {
|
||||||
if (track_id == track->id()) {
|
if (track_id == track->id()) {
|
||||||
reports->push_back(it->second);
|
reports->push_back(&(*it));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
return true;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
void
|
void
|
||||||
@ -672,13 +667,13 @@ StatsReport* StatsCollector::PrepareLocalReport(
|
|||||||
const std::string& transport_id,
|
const std::string& transport_id,
|
||||||
TrackDirection direction) {
|
TrackDirection direction) {
|
||||||
const std::string ssrc_id = talk_base::ToString<uint32>(ssrc);
|
const std::string ssrc_id = talk_base::ToString<uint32>(ssrc);
|
||||||
StatsMap::iterator it = reports_.find(StatsId(
|
StatsReport* report = reports_.Find(
|
||||||
StatsReport::kStatsReportTypeSsrc, ssrc_id, direction));
|
StatsId(StatsReport::kStatsReportTypeSsrc, ssrc_id, direction));
|
||||||
|
|
||||||
// Use the ID of the track that is currently mapped to the SSRC, if any.
|
// Use the ID of the track that is currently mapped to the SSRC, if any.
|
||||||
std::string track_id;
|
std::string track_id;
|
||||||
if (!GetTrackIdBySsrc(ssrc, &track_id, direction)) {
|
if (!GetTrackIdBySsrc(ssrc, &track_id, direction)) {
|
||||||
if (it == reports_.end()) {
|
if (!report) {
|
||||||
// The ssrc is not used by any track or existing report, return NULL
|
// 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.
|
// in such case to indicate no report is prepared for the ssrc.
|
||||||
return NULL;
|
return NULL;
|
||||||
@ -686,13 +681,13 @@ StatsReport* StatsCollector::PrepareLocalReport(
|
|||||||
|
|
||||||
// The ssrc is not used by any existing track. Keeps the old track id
|
// The ssrc is not used by any existing track. Keeps the old track id
|
||||||
// since we want to report the stats for inactive ssrc.
|
// since we want to report the stats for inactive ssrc.
|
||||||
ExtractValueFromReport(it->second,
|
ExtractValueFromReport(*report,
|
||||||
StatsReport::kStatsValueNameTrackId,
|
StatsReport::kStatsValueNameTrackId,
|
||||||
&track_id);
|
&track_id);
|
||||||
}
|
}
|
||||||
|
|
||||||
StatsReport* report = GetOrCreateReport(StatsReport::kStatsReportTypeSsrc,
|
report = GetOrCreateReport(
|
||||||
ssrc_id, direction);
|
StatsReport::kStatsReportTypeSsrc, ssrc_id, direction);
|
||||||
|
|
||||||
// Clear out stats from previous GatherStats calls if any.
|
// Clear out stats from previous GatherStats calls if any.
|
||||||
// This is required since the report will be returned for the new values.
|
// This is required since the report will be returned for the new values.
|
||||||
@ -715,13 +710,13 @@ StatsReport* StatsCollector::PrepareRemoteReport(
|
|||||||
const std::string& transport_id,
|
const std::string& transport_id,
|
||||||
TrackDirection direction) {
|
TrackDirection direction) {
|
||||||
const std::string ssrc_id = talk_base::ToString<uint32>(ssrc);
|
const std::string ssrc_id = talk_base::ToString<uint32>(ssrc);
|
||||||
StatsMap::iterator it = reports_.find(StatsId(
|
StatsReport* report = reports_.Find(
|
||||||
StatsReport::kStatsReportTypeRemoteSsrc, ssrc_id, direction));
|
StatsId(StatsReport::kStatsReportTypeRemoteSsrc, ssrc_id, direction));
|
||||||
|
|
||||||
// Use the ID of the track that is currently mapped to the SSRC, if any.
|
// Use the ID of the track that is currently mapped to the SSRC, if any.
|
||||||
std::string track_id;
|
std::string track_id;
|
||||||
if (!GetTrackIdBySsrc(ssrc, &track_id, direction)) {
|
if (!GetTrackIdBySsrc(ssrc, &track_id, direction)) {
|
||||||
if (it == reports_.end()) {
|
if (!report) {
|
||||||
// The ssrc is not used by any track or existing report, return NULL
|
// 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.
|
// in such case to indicate no report is prepared for the ssrc.
|
||||||
return NULL;
|
return NULL;
|
||||||
@ -729,12 +724,12 @@ StatsReport* StatsCollector::PrepareRemoteReport(
|
|||||||
|
|
||||||
// The ssrc is not used by any existing track. Keeps the old track id
|
// The ssrc is not used by any existing track. Keeps the old track id
|
||||||
// since we want to report the stats for inactive ssrc.
|
// since we want to report the stats for inactive ssrc.
|
||||||
ExtractValueFromReport(it->second,
|
ExtractValueFromReport(*report,
|
||||||
StatsReport::kStatsValueNameTrackId,
|
StatsReport::kStatsValueNameTrackId,
|
||||||
&track_id);
|
&track_id);
|
||||||
}
|
}
|
||||||
|
|
||||||
StatsReport* report = GetOrCreateReport(
|
report = GetOrCreateReport(
|
||||||
StatsReport::kStatsReportTypeRemoteSsrc, ssrc_id, direction);
|
StatsReport::kStatsReportTypeRemoteSsrc, ssrc_id, direction);
|
||||||
|
|
||||||
// Clear out stats from previous GatherStats calls if any.
|
// Clear out stats from previous GatherStats calls if any.
|
||||||
@ -778,18 +773,17 @@ std::string StatsCollector::AddOneCertificateReport(
|
|||||||
talk_base::Base64::EncodeFromArray(
|
talk_base::Base64::EncodeFromArray(
|
||||||
der_buffer.data(), der_buffer.length(), &der_base64);
|
der_buffer.data(), der_buffer.length(), &der_base64);
|
||||||
|
|
||||||
StatsReport report;
|
StatsReport* report = reports_.FindOrAddNew(
|
||||||
report.type = StatsReport::kStatsReportTypeCertificate;
|
StatsId(StatsReport::kStatsReportTypeCertificate, fingerprint));
|
||||||
report.id = StatsId(report.type, fingerprint);
|
report->type = StatsReport::kStatsReportTypeCertificate;
|
||||||
report.timestamp = stats_gathering_started_;
|
report->timestamp = stats_gathering_started_;
|
||||||
report.AddValue(StatsReport::kStatsValueNameFingerprint, fingerprint);
|
report->AddValue(StatsReport::kStatsValueNameFingerprint, fingerprint);
|
||||||
report.AddValue(StatsReport::kStatsValueNameFingerprintAlgorithm,
|
report->AddValue(StatsReport::kStatsValueNameFingerprintAlgorithm,
|
||||||
digest_algorithm);
|
digest_algorithm);
|
||||||
report.AddValue(StatsReport::kStatsValueNameDer, der_base64);
|
report->AddValue(StatsReport::kStatsValueNameDer, der_base64);
|
||||||
if (!issuer_id.empty())
|
if (!issuer_id.empty())
|
||||||
report.AddValue(StatsReport::kStatsValueNameIssuerId, issuer_id);
|
report->AddValue(StatsReport::kStatsValueNameIssuerId, issuer_id);
|
||||||
reports_[report.id] = report;
|
return report->id;
|
||||||
return report.id;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
std::string StatsCollector::AddCertificateReports(
|
std::string StatsCollector::AddCertificateReports(
|
||||||
@ -819,16 +813,14 @@ std::string StatsCollector::AddCertificateReports(
|
|||||||
|
|
||||||
void StatsCollector::ExtractSessionInfo() {
|
void StatsCollector::ExtractSessionInfo() {
|
||||||
// Extract information from the base session.
|
// Extract information from the base session.
|
||||||
StatsReport report;
|
StatsReport* report = reports_.FindOrAddNew(StatsId(
|
||||||
report.id = StatsId(StatsReport::kStatsReportTypeSession, session_->id());
|
StatsReport::kStatsReportTypeSession, session_->id()));
|
||||||
report.type = StatsReport::kStatsReportTypeSession;
|
report->type = StatsReport::kStatsReportTypeSession;
|
||||||
report.timestamp = stats_gathering_started_;
|
report->timestamp = stats_gathering_started_;
|
||||||
report.values.clear();
|
report->values.clear();
|
||||||
report.AddBoolean(StatsReport::kStatsValueNameInitiator,
|
report->AddBoolean(StatsReport::kStatsValueNameInitiator,
|
||||||
session_->initiator());
|
session_->initiator());
|
||||||
|
|
||||||
reports_[report.id] = report;
|
|
||||||
|
|
||||||
cricket::SessionStats stats;
|
cricket::SessionStats stats;
|
||||||
if (session_->GetStats(&stats)) {
|
if (session_->GetStats(&stats)) {
|
||||||
// Store the proxy map away for use in SSRC reporting.
|
// Store the proxy map away for use in SSRC reporting.
|
||||||
@ -857,61 +849,58 @@ void StatsCollector::ExtractSessionInfo() {
|
|||||||
= transport_iter->second.channel_stats.begin();
|
= transport_iter->second.channel_stats.begin();
|
||||||
channel_iter != transport_iter->second.channel_stats.end();
|
channel_iter != transport_iter->second.channel_stats.end();
|
||||||
++channel_iter) {
|
++channel_iter) {
|
||||||
StatsReport channel_report;
|
|
||||||
std::ostringstream ostc;
|
std::ostringstream ostc;
|
||||||
ostc << "Channel-" << transport_iter->second.content_name
|
ostc << "Channel-" << transport_iter->second.content_name
|
||||||
<< "-" << channel_iter->component;
|
<< "-" << channel_iter->component;
|
||||||
channel_report.id = ostc.str();
|
StatsReport* channel_report = reports_.FindOrAddNew(ostc.str());
|
||||||
channel_report.type = StatsReport::kStatsReportTypeComponent;
|
channel_report->type = StatsReport::kStatsReportTypeComponent;
|
||||||
channel_report.timestamp = stats_gathering_started_;
|
channel_report->timestamp = stats_gathering_started_;
|
||||||
channel_report.AddValue(StatsReport::kStatsValueNameComponent,
|
channel_report->AddValue(StatsReport::kStatsValueNameComponent,
|
||||||
channel_iter->component);
|
channel_iter->component);
|
||||||
if (!local_cert_report_id.empty())
|
if (!local_cert_report_id.empty())
|
||||||
channel_report.AddValue(
|
channel_report->AddValue(
|
||||||
StatsReport::kStatsValueNameLocalCertificateId,
|
StatsReport::kStatsValueNameLocalCertificateId,
|
||||||
local_cert_report_id);
|
local_cert_report_id);
|
||||||
if (!remote_cert_report_id.empty())
|
if (!remote_cert_report_id.empty())
|
||||||
channel_report.AddValue(
|
channel_report->AddValue(
|
||||||
StatsReport::kStatsValueNameRemoteCertificateId,
|
StatsReport::kStatsValueNameRemoteCertificateId,
|
||||||
remote_cert_report_id);
|
remote_cert_report_id);
|
||||||
reports_[channel_report.id] = channel_report;
|
|
||||||
for (size_t i = 0;
|
for (size_t i = 0;
|
||||||
i < channel_iter->connection_infos.size();
|
i < channel_iter->connection_infos.size();
|
||||||
++i) {
|
++i) {
|
||||||
StatsReport report;
|
|
||||||
const cricket::ConnectionInfo& info
|
|
||||||
= channel_iter->connection_infos[i];
|
|
||||||
std::ostringstream ost;
|
std::ostringstream ost;
|
||||||
ost << "Conn-" << transport_iter->first << "-"
|
ost << "Conn-" << transport_iter->first << "-"
|
||||||
<< channel_iter->component << "-" << i;
|
<< channel_iter->component << "-" << i;
|
||||||
report.id = ost.str();
|
StatsReport* report = reports_.FindOrAddNew(ost.str());
|
||||||
report.type = StatsReport::kStatsReportTypeCandidatePair;
|
report->type = StatsReport::kStatsReportTypeCandidatePair;
|
||||||
report.timestamp = stats_gathering_started_;
|
report->timestamp = stats_gathering_started_;
|
||||||
// Link from connection to its containing channel.
|
// Link from connection to its containing channel.
|
||||||
report.AddValue(StatsReport::kStatsValueNameChannelId,
|
report->AddValue(StatsReport::kStatsValueNameChannelId,
|
||||||
channel_report.id);
|
channel_report->id);
|
||||||
report.AddValue(StatsReport::kStatsValueNameBytesSent,
|
|
||||||
|
const cricket::ConnectionInfo& info =
|
||||||
|
channel_iter->connection_infos[i];
|
||||||
|
report->AddValue(StatsReport::kStatsValueNameBytesSent,
|
||||||
info.sent_total_bytes);
|
info.sent_total_bytes);
|
||||||
report.AddValue(StatsReport::kStatsValueNameBytesReceived,
|
report->AddValue(StatsReport::kStatsValueNameBytesReceived,
|
||||||
info.recv_total_bytes);
|
info.recv_total_bytes);
|
||||||
report.AddBoolean(StatsReport::kStatsValueNameWritable,
|
report->AddBoolean(StatsReport::kStatsValueNameWritable,
|
||||||
info.writable);
|
info.writable);
|
||||||
report.AddBoolean(StatsReport::kStatsValueNameReadable,
|
report->AddBoolean(StatsReport::kStatsValueNameReadable,
|
||||||
info.readable);
|
info.readable);
|
||||||
report.AddBoolean(StatsReport::kStatsValueNameActiveConnection,
|
report->AddBoolean(StatsReport::kStatsValueNameActiveConnection,
|
||||||
info.best_connection);
|
info.best_connection);
|
||||||
report.AddValue(StatsReport::kStatsValueNameLocalAddress,
|
report->AddValue(StatsReport::kStatsValueNameLocalAddress,
|
||||||
info.local_candidate.address().ToString());
|
info.local_candidate.address().ToString());
|
||||||
report.AddValue(StatsReport::kStatsValueNameRemoteAddress,
|
report->AddValue(StatsReport::kStatsValueNameRemoteAddress,
|
||||||
info.remote_candidate.address().ToString());
|
info.remote_candidate.address().ToString());
|
||||||
report.AddValue(StatsReport::kStatsValueNameRtt, info.rtt);
|
report->AddValue(StatsReport::kStatsValueNameRtt, info.rtt);
|
||||||
report.AddValue(StatsReport::kStatsValueNameTransportType,
|
report->AddValue(StatsReport::kStatsValueNameTransportType,
|
||||||
info.local_candidate.protocol());
|
info.local_candidate.protocol());
|
||||||
report.AddValue(StatsReport::kStatsValueNameLocalCandidateType,
|
report->AddValue(StatsReport::kStatsValueNameLocalCandidateType,
|
||||||
info.local_candidate.type());
|
info.local_candidate.type());
|
||||||
report.AddValue(StatsReport::kStatsValueNameRemoteCandidateType,
|
report->AddValue(StatsReport::kStatsValueNameRemoteCandidateType,
|
||||||
info.remote_candidate.type());
|
info.remote_candidate.type());
|
||||||
reports_[report.id] = report;
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -968,7 +957,8 @@ void StatsCollector::ExtractVideoInfo(
|
|||||||
if (video_info.bw_estimations.size() != 1) {
|
if (video_info.bw_estimations.size() != 1) {
|
||||||
LOG(LS_ERROR) << "BWEs count: " << video_info.bw_estimations.size();
|
LOG(LS_ERROR) << "BWEs count: " << video_info.bw_estimations.size();
|
||||||
} else {
|
} else {
|
||||||
StatsReport* report = &reports_[StatsReport::kStatsReportVideoBweId];
|
StatsReport* report =
|
||||||
|
reports_.FindOrAddNew(StatsReport::kStatsReportVideoBweId);
|
||||||
ExtractStats(
|
ExtractStats(
|
||||||
video_info.bw_estimations[0], stats_gathering_started_, level, report);
|
video_info.bw_estimations[0], stats_gathering_started_, level, report);
|
||||||
}
|
}
|
||||||
@ -979,13 +969,7 @@ StatsReport* StatsCollector::GetReport(const std::string& type,
|
|||||||
TrackDirection direction) {
|
TrackDirection direction) {
|
||||||
ASSERT(type == StatsReport::kStatsReportTypeSsrc ||
|
ASSERT(type == StatsReport::kStatsReportTypeSsrc ||
|
||||||
type == StatsReport::kStatsReportTypeRemoteSsrc);
|
type == StatsReport::kStatsReportTypeRemoteSsrc);
|
||||||
std::string statsid = StatsId(type, id, direction);
|
return reports_.Find(StatsId(type, id, direction));
|
||||||
StatsReport* report = NULL;
|
|
||||||
std::map<std::string, StatsReport>::iterator it = reports_.find(statsid);
|
|
||||||
if (it != reports_.end())
|
|
||||||
report = &(it->second);
|
|
||||||
|
|
||||||
return report;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
StatsReport* StatsCollector::GetOrCreateReport(const std::string& type,
|
StatsReport* StatsCollector::GetOrCreateReport(const std::string& type,
|
||||||
@ -996,8 +980,8 @@ StatsReport* StatsCollector::GetOrCreateReport(const std::string& type,
|
|||||||
StatsReport* report = GetReport(type, id, direction);
|
StatsReport* report = GetReport(type, id, direction);
|
||||||
if (report == NULL) {
|
if (report == NULL) {
|
||||||
std::string statsid = StatsId(type, id, direction);
|
std::string statsid = StatsId(type, id, direction);
|
||||||
report = &reports_[statsid]; // Create new element.
|
report = reports_.FindOrAddNew(statsid);
|
||||||
report->id = statsid;
|
ASSERT(report->id == statsid);
|
||||||
report->type = type;
|
report->type = type;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -72,7 +72,11 @@ class StatsCollector {
|
|||||||
// be called before this function to get the most recent stats. |selector| is
|
// be called before this function to get the most recent stats. |selector| is
|
||||||
// a track label or empty string. The most recent reports are stored in
|
// a track label or empty string. The most recent reports are stored in
|
||||||
// |reports|.
|
// |reports|.
|
||||||
bool GetStats(MediaStreamTrackInterface* track,
|
// TODO(tommi): Change this contract to accept a callback object instead
|
||||||
|
// of filling in |reports|. As is, there's a requirement that the caller
|
||||||
|
// uses |reports| immediately without allowing any async activity on
|
||||||
|
// the thread (message handling etc) and then discard the results.
|
||||||
|
void GetStats(MediaStreamTrackInterface* track,
|
||||||
StatsReports* reports);
|
StatsReports* reports);
|
||||||
|
|
||||||
// Prepare an SSRC report for the given ssrc. Used internally
|
// Prepare an SSRC report for the given ssrc. Used internally
|
||||||
@ -121,7 +125,7 @@ class StatsCollector {
|
|||||||
TrackDirection direction);
|
TrackDirection direction);
|
||||||
|
|
||||||
// A map from the report id to the report.
|
// A map from the report id to the report.
|
||||||
std::map<std::string, StatsReport> reports_;
|
StatsSet reports_;
|
||||||
// Raw pointer to the session the statistics are gathered from.
|
// Raw pointer to the session the statistics are gathered from.
|
||||||
WebRtcSession* const session_;
|
WebRtcSession* const session_;
|
||||||
double stats_gathering_started_;
|
double stats_gathering_started_;
|
||||||
|
@ -168,10 +168,10 @@ std::string ExtractStatsValue(const std::string& type,
|
|||||||
return kNoReports;
|
return kNoReports;
|
||||||
}
|
}
|
||||||
for (size_t i = 0; i < reports.size(); ++i) {
|
for (size_t i = 0; i < reports.size(); ++i) {
|
||||||
if (reports[i].type != type)
|
if (reports[i]->type != type)
|
||||||
continue;
|
continue;
|
||||||
std::string ret;
|
std::string ret;
|
||||||
if (GetValue(&reports[i], name, &ret)) {
|
if (GetValue(reports[i], name, &ret)) {
|
||||||
return ret;
|
return ret;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -184,10 +184,10 @@ std::string ExtractStatsValue(const std::string& type,
|
|||||||
const StatsReport* FindNthReportByType(
|
const StatsReport* FindNthReportByType(
|
||||||
const StatsReports& reports, const std::string& type, int n) {
|
const StatsReports& reports, const std::string& type, int n) {
|
||||||
for (size_t i = 0; i < reports.size(); ++i) {
|
for (size_t i = 0; i < reports.size(); ++i) {
|
||||||
if (reports[i].type == type) {
|
if (reports[i]->type == type) {
|
||||||
n--;
|
n--;
|
||||||
if (n == 0)
|
if (n == 0)
|
||||||
return &reports[i];
|
return reports[i];
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
return NULL;
|
return NULL;
|
||||||
@ -196,8 +196,8 @@ const StatsReport* FindNthReportByType(
|
|||||||
const StatsReport* FindReportById(const StatsReports& reports,
|
const StatsReport* FindReportById(const StatsReports& reports,
|
||||||
const std::string& id) {
|
const std::string& id) {
|
||||||
for (size_t i = 0; i < reports.size(); ++i) {
|
for (size_t i = 0; i < reports.size(); ++i) {
|
||||||
if (reports[i].id == id) {
|
if (reports[i]->id == id) {
|
||||||
return &reports[i];
|
return reports[i];
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
return NULL;
|
return NULL;
|
||||||
@ -433,7 +433,7 @@ void InitVoiceReceiverInfo(cricket::VoiceReceiverInfo* voice_receiver_info) {
|
|||||||
class StatsCollectorTest : public testing::Test {
|
class StatsCollectorTest : public testing::Test {
|
||||||
protected:
|
protected:
|
||||||
StatsCollectorTest()
|
StatsCollectorTest()
|
||||||
: media_engine_(new cricket::FakeMediaEngine),
|
: media_engine_(new cricket::FakeMediaEngine()),
|
||||||
channel_manager_(
|
channel_manager_(
|
||||||
new cricket::ChannelManager(media_engine_,
|
new cricket::ChannelManager(media_engine_,
|
||||||
new cricket::FakeDeviceManager(),
|
new cricket::FakeDeviceManager(),
|
||||||
@ -443,6 +443,8 @@ class StatsCollectorTest : public testing::Test {
|
|||||||
EXPECT_CALL(session_, GetStats(_)).WillRepeatedly(Return(false));
|
EXPECT_CALL(session_, GetStats(_)).WillRepeatedly(Return(false));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
~StatsCollectorTest() {}
|
||||||
|
|
||||||
// This creates a standard setup with a transport called "trspname"
|
// This creates a standard setup with a transport called "trspname"
|
||||||
// having one transport channel
|
// having one transport channel
|
||||||
// and the specified virtual connection name.
|
// and the specified virtual connection name.
|
||||||
@ -786,7 +788,7 @@ TEST_F(StatsCollectorTest, TrackObjectExistsWithoutUpdateStats) {
|
|||||||
stats.GetStats(NULL, &reports);
|
stats.GetStats(NULL, &reports);
|
||||||
EXPECT_EQ((size_t)1, reports.size());
|
EXPECT_EQ((size_t)1, reports.size());
|
||||||
EXPECT_EQ(std::string(StatsReport::kStatsReportTypeTrack),
|
EXPECT_EQ(std::string(StatsReport::kStatsReportTypeTrack),
|
||||||
reports[0].type);
|
reports[0]->type);
|
||||||
|
|
||||||
std::string trackValue =
|
std::string trackValue =
|
||||||
ExtractStatsValue(StatsReport::kStatsReportTypeTrack,
|
ExtractStatsValue(StatsReport::kStatsReportTypeTrack,
|
||||||
@ -832,6 +834,7 @@ TEST_F(StatsCollectorTest, TrackAndSsrcObjectExistAfterUpdateSsrcStats) {
|
|||||||
EXPECT_TRUE(track_report);
|
EXPECT_TRUE(track_report);
|
||||||
|
|
||||||
// Get report for the specific |track|.
|
// Get report for the specific |track|.
|
||||||
|
reports.clear();
|
||||||
stats.GetStats(track_, &reports);
|
stats.GetStats(track_, &reports);
|
||||||
// |reports| should contain at least one session report, one track report,
|
// |reports| should contain at least one session report, one track report,
|
||||||
// and one ssrc report.
|
// and one ssrc report.
|
||||||
@ -1333,7 +1336,7 @@ TEST_F(StatsCollectorTest, LocalAndRemoteTracksWithSameSsrc) {
|
|||||||
webrtc::StatsCollector stats(&session_); // Implementation under test.
|
webrtc::StatsCollector stats(&session_); // Implementation under test.
|
||||||
// Ignore unused callback (logspam).
|
// Ignore unused callback (logspam).
|
||||||
EXPECT_CALL(session_, GetTransport(_))
|
EXPECT_CALL(session_, GetTransport(_))
|
||||||
.WillOnce(Return(static_cast<cricket::Transport*>(NULL)));
|
.WillRepeatedly(Return(static_cast<cricket::Transport*>(NULL)));
|
||||||
MockVoiceMediaChannel* media_channel = new MockVoiceMediaChannel();
|
MockVoiceMediaChannel* media_channel = new MockVoiceMediaChannel();
|
||||||
// The content_name known by the voice channel.
|
// The content_name known by the voice channel.
|
||||||
const std::string kVcName("vcname");
|
const std::string kVcName("vcname");
|
||||||
@ -1396,6 +1399,7 @@ TEST_F(StatsCollectorTest, LocalAndRemoteTracksWithSameSsrc) {
|
|||||||
VerifyVoiceSenderInfoReport(track_report, voice_sender_info);
|
VerifyVoiceSenderInfoReport(track_report, voice_sender_info);
|
||||||
|
|
||||||
// Get stats for the remote track.
|
// Get stats for the remote track.
|
||||||
|
reports.clear();
|
||||||
stats.GetStats(remote_track.get(), &reports);
|
stats.GetStats(remote_track.get(), &reports);
|
||||||
track_report = FindNthReportByType(reports,
|
track_report = FindNthReportByType(reports,
|
||||||
StatsReport::kStatsReportTypeSsrc, 1);
|
StatsReport::kStatsReportTypeSsrc, 1);
|
||||||
@ -1452,6 +1456,7 @@ TEST_F(StatsCollectorTest, TwoLocalTracksWithSameSsrc) {
|
|||||||
cricket::VoiceSenderInfo new_voice_sender_info;
|
cricket::VoiceSenderInfo new_voice_sender_info;
|
||||||
InitVoiceSenderInfo(&new_voice_sender_info);
|
InitVoiceSenderInfo(&new_voice_sender_info);
|
||||||
cricket::VoiceMediaInfo new_stats_read;
|
cricket::VoiceMediaInfo new_stats_read;
|
||||||
|
reports.clear();
|
||||||
SetupAndVerifyAudioTrackStats(
|
SetupAndVerifyAudioTrackStats(
|
||||||
new_audio_track.get(), stream_.get(), &stats, &voice_channel, kVcName,
|
new_audio_track.get(), stream_.get(), &stats, &voice_channel, kVcName,
|
||||||
media_channel, &new_voice_sender_info, NULL, &new_stats_read, &reports);
|
media_channel, &new_voice_sender_info, NULL, &new_stats_read, &reports);
|
||||||
|
@ -31,6 +31,7 @@
|
|||||||
#ifndef TALK_APP_WEBRTC_STATSTYPES_H_
|
#ifndef TALK_APP_WEBRTC_STATSTYPES_H_
|
||||||
#define TALK_APP_WEBRTC_STATSTYPES_H_
|
#define TALK_APP_WEBRTC_STATSTYPES_H_
|
||||||
|
|
||||||
|
#include <set>
|
||||||
#include <string>
|
#include <string>
|
||||||
#include <vector>
|
#include <vector>
|
||||||
|
|
||||||
@ -39,15 +40,45 @@
|
|||||||
|
|
||||||
namespace webrtc {
|
namespace webrtc {
|
||||||
|
|
||||||
|
// TODO(tommi): Move all the implementation that's in this file and
|
||||||
|
// statscollector.cc related to these types, into a new, statstypes.cc file.
|
||||||
|
|
||||||
class StatsReport {
|
class StatsReport {
|
||||||
|
protected:
|
||||||
|
// TODO(tommi): Disallow copy ctor etc completely once not needed.
|
||||||
|
explicit StatsReport(const StatsReport& src)
|
||||||
|
: id(src.id),
|
||||||
|
type(src.type),
|
||||||
|
timestamp(src.timestamp),
|
||||||
|
values(src.values) {}
|
||||||
|
|
||||||
|
StatsReport& operator=(const StatsReport& src) {
|
||||||
|
ASSERT(id == src.id);
|
||||||
|
type = src.type;
|
||||||
|
timestamp = src.timestamp;
|
||||||
|
values = src.values;
|
||||||
|
return *this;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Constructor is protected to force use of StatsSet.
|
||||||
|
explicit StatsReport(const std::string& id) : id(id), timestamp(0) {}
|
||||||
|
|
||||||
public:
|
public:
|
||||||
StatsReport() : timestamp(0) { }
|
// Operators provided for STL container/algorithm support.
|
||||||
|
bool operator<(const StatsReport& other) const { return id < other.id; }
|
||||||
|
bool operator==(const StatsReport& other) const { return id == other.id; }
|
||||||
|
// 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 { return id == other_id; }
|
||||||
|
|
||||||
// TODO(tommi): Change this to be an enum type that holds all the
|
// TODO(tommi): Change this to be an enum type that holds all the
|
||||||
// kStatsValueName constants.
|
// kStatsValueName constants.
|
||||||
typedef const char* StatsValueName;
|
typedef const char* StatsValueName;
|
||||||
|
|
||||||
std::string id; // See below for contents.
|
// 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 std::string id; // See below for contents.
|
||||||
std::string type; // See below for contents.
|
std::string type; // See below for contents.
|
||||||
|
|
||||||
struct Value {
|
struct Value {
|
||||||
@ -239,7 +270,65 @@ class StatsReport {
|
|||||||
static const char kStatsValueNameDecodingPLCCNG[];
|
static const char kStatsValueNameDecodingPLCCNG[];
|
||||||
};
|
};
|
||||||
|
|
||||||
typedef std::vector<StatsReport> StatsReports;
|
// 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=;
|
||||||
|
};
|
||||||
|
|
||||||
|
// 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.
|
||||||
|
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 {
|
||||||
|
public:
|
||||||
|
StatsSet() {}
|
||||||
|
~StatsSet() {}
|
||||||
|
|
||||||
|
typedef std::set<StatsReportCopyable> Container;
|
||||||
|
typedef Container::iterator iterator;
|
||||||
|
typedef Container::const_iterator const_iterator;
|
||||||
|
|
||||||
|
const_iterator begin() const { return list_.begin(); }
|
||||||
|
const_iterator end() const { return list_.end(); }
|
||||||
|
|
||||||
|
// Creates a new report object with |id| that does not already
|
||||||
|
// exist in the list of reports.
|
||||||
|
StatsReport* InsertNew(const std::string& id) {
|
||||||
|
ASSERT(Find(id) == NULL);
|
||||||
|
const StatsReport* ret = &(*list_.insert(StatsReportCopyable(id)).first);
|
||||||
|
return const_cast<StatsReport*>(ret);
|
||||||
|
}
|
||||||
|
|
||||||
|
StatsReport* FindOrAddNew(const std::string& id) {
|
||||||
|
StatsReport* ret = Find(id);
|
||||||
|
return ret ? ret : InsertNew(id);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Looks for a report with the given |id|. If one is not found, NULL
|
||||||
|
// will be returned.
|
||||||
|
StatsReport* 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)));
|
||||||
|
}
|
||||||
|
|
||||||
|
private:
|
||||||
|
Container list_;
|
||||||
|
};
|
||||||
|
|
||||||
} // namespace webrtc
|
} // namespace webrtc
|
||||||
|
|
||||||
|
@ -120,9 +120,13 @@ class MockStatsObserver : public webrtc::StatsObserver {
|
|||||||
MockStatsObserver()
|
MockStatsObserver()
|
||||||
: called_(false) {}
|
: called_(false) {}
|
||||||
virtual ~MockStatsObserver() {}
|
virtual ~MockStatsObserver() {}
|
||||||
virtual void OnComplete(const std::vector<webrtc::StatsReport>& reports) {
|
virtual void OnComplete(const StatsReports& reports) {
|
||||||
called_ = true;
|
called_ = true;
|
||||||
reports_ = reports;
|
reports_.clear();
|
||||||
|
reports_.reserve(reports.size());
|
||||||
|
StatsReports::const_iterator it;
|
||||||
|
for (it = reports.begin(); it != reports.end(); ++it)
|
||||||
|
reports_.push_back(StatsReportCopyable(*(*it)));
|
||||||
}
|
}
|
||||||
|
|
||||||
bool called() const { return called_; }
|
bool called() const { return called_; }
|
||||||
@ -148,7 +152,7 @@ class MockStatsObserver : public webrtc::StatsObserver {
|
|||||||
}
|
}
|
||||||
|
|
||||||
private:
|
private:
|
||||||
int GetSsrcStatsValue(const std::string name) {
|
int GetSsrcStatsValue(StatsReport::StatsValueName name) {
|
||||||
if (reports_.empty()) {
|
if (reports_.empty()) {
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
@ -167,7 +171,7 @@ class MockStatsObserver : public webrtc::StatsObserver {
|
|||||||
}
|
}
|
||||||
|
|
||||||
bool called_;
|
bool called_;
|
||||||
std::vector<webrtc::StatsReport> reports_;
|
std::vector<StatsReportCopyable> reports_;
|
||||||
};
|
};
|
||||||
|
|
||||||
} // namespace webrtc
|
} // namespace webrtc
|
||||||
|
Loading…
x
Reference in New Issue
Block a user