blob: f41bf7a6a8b3e8bd9fddad86f9ba9854e7a97034 [file] [log] [blame]
/*
* libjingle
* Copyright 2014 Google Inc.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions are met:
*
* 1. Redistributions of source code must retain the above copyright notice,
* this list of conditions and the following disclaimer.
* 2. Redistributions in binary form must reproduce the above copyright notice,
* this list of conditions and the following disclaimer in the documentation
* and/or other materials provided with the distribution.
* 3. The name of the author may not be used to endorse or promote products
* derived from this software without specific prior written permission.
*
* THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR IMPLIED
* WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
* MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO
* EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
* SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
* PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS;
* OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
* WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR
* OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF
* ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*/
#include "talk/app/webrtc/statstypes.h"
using rtc::scoped_ptr;
namespace webrtc {
namespace {
// The id of StatsReport of type kStatsReportTypeBwe.
const char kStatsReportVideoBweId[] = "bweforvideo";
// 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;
}
class BandwidthEstimationId : public StatsReport::Id {
public:
BandwidthEstimationId() : StatsReport::Id(StatsReport::kStatsReportTypeBwe) {}
std::string ToString() const override { return kStatsReportVideoBweId; }
};
class TypedId : public StatsReport::Id {
public:
TypedId(StatsReport::StatsType type, const std::string& id)
: StatsReport::Id(type), id_(id) {}
bool Equals(const Id& other) const override {
return Id::Equals(other) &&
static_cast<const TypedId&>(other).id_ == id_;
}
std::string ToString() const override {
return std::string(InternalTypeToString(type_)) + kSeparator + id_;
}
protected:
const std::string id_;
};
class TypedIntId : public StatsReport::Id {
public:
TypedIntId(StatsReport::StatsType type, int id)
: StatsReport::Id(type), id_(id) {}
bool Equals(const Id& other) const override {
return Id::Equals(other) &&
static_cast<const TypedIntId&>(other).id_ == id_;
}
std::string ToString() const override {
return std::string(InternalTypeToString(type_)) +
kSeparator +
rtc::ToString<int>(id_);
}
protected:
const int id_;
};
class IdWithDirection : public TypedId {
public:
IdWithDirection(StatsReport::StatsType type, const std::string& id,
StatsReport::Direction direction)
: TypedId(type, id), direction_(direction) {}
bool Equals(const Id& other) const override {
return TypedId::Equals(other) &&
static_cast<const IdWithDirection&>(other).direction_ == direction_;
}
std::string ToString() const override {
std::string ret(TypedId::ToString());
ret += kSeparator;
ret += direction_ == StatsReport::kSend ? "send" : "recv";
return ret;
}
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) {
}
const char* StatsReport::Value::display_name() const {
switch (name) {
case kStatsValueNameAudioOutputLevel:
return "audioOutputLevel";
case kStatsValueNameAudioInputLevel:
return "audioInputLevel";
case kStatsValueNameBytesSent:
return "bytesSent";
case kStatsValueNamePacketsSent:
return "packetsSent";
case kStatsValueNameBytesReceived:
return "bytesReceived";
case kStatsValueNameLabel:
return "label";
case kStatsValueNamePacketsReceived:
return "packetsReceived";
case kStatsValueNamePacketsLost:
return "packetsLost";
case kStatsValueNameProtocol:
return "protocol";
case kStatsValueNameTransportId:
return "transportId";
case kStatsValueNameSsrc:
return "ssrc";
case kStatsValueNameState:
return "state";
case kStatsValueNameDataChannelId:
return "datachannelid";
// 'goog' prefixed constants.
case kStatsValueNameActiveConnection:
return "googActiveConnection";
case kStatsValueNameActualEncBitrate:
return "googActualEncBitrate";
case kStatsValueNameAvailableReceiveBandwidth:
return "googAvailableReceiveBandwidth";
case kStatsValueNameAvailableSendBandwidth:
return "googAvailableSendBandwidth";
case kStatsValueNameAvgEncodeMs:
return "googAvgEncodeMs";
case kStatsValueNameBucketDelay:
return "googBucketDelay";
case kStatsValueNameBandwidthLimitedResolution:
return "googBandwidthLimitedResolution";
case kStatsValueNameCaptureJitterMs:
return "googCaptureJitterMs";
case kStatsValueNameCaptureQueueDelayMsPerS:
return "googCaptureQueueDelayMsPerS";
// Candidate related attributes. Values are taken from
// http://w3c.github.io/webrtc-stats/#rtcstatstype-enum*.
case kStatsValueNameCandidateIPAddress:
return "ipAddress";
case kStatsValueNameCandidateNetworkType:
return "networkType";
case kStatsValueNameCandidatePortNumber:
return "portNumber";
case kStatsValueNameCandidatePriority:
return "priority";
case kStatsValueNameCandidateTransportType:
return "transport";
case kStatsValueNameCandidateType:
return "candidateType";
case kStatsValueNameChannelId:
return "googChannelId";
case kStatsValueNameCodecName:
return "googCodecName";
case kStatsValueNameComponent:
return "googComponent";
case kStatsValueNameContentName:
return "googContentName";
case kStatsValueNameCpuLimitedResolution:
return "googCpuLimitedResolution";
case kStatsValueNameDecodingCTSG:
return "googDecodingCTSG";
case kStatsValueNameDecodingCTN:
return "googDecodingCTN";
case kStatsValueNameDecodingNormal:
return "googDecodingNormal";
case kStatsValueNameDecodingPLC:
return "googDecodingPLC";
case kStatsValueNameDecodingCNG:
return "googDecodingCNG";
case kStatsValueNameDecodingPLCCNG:
return "googDecodingPLCCNG";
case kStatsValueNameDer:
return "googDerBase64";
case kStatsValueNameEchoCancellationQualityMin:
return "googEchoCancellationQualityMin";
case kStatsValueNameEchoDelayMedian:
return "googEchoCancellationEchoDelayMedian";
case kStatsValueNameEchoDelayStdDev:
return "googEchoCancellationEchoDelayStdDev";
case kStatsValueNameEchoReturnLoss:
return "googEchoCancellationReturnLoss";
case kStatsValueNameEchoReturnLossEnhancement:
return "googEchoCancellationReturnLossEnhancement";
case kStatsValueNameEncodeUsagePercent:
return "googEncodeUsagePercent";
case kStatsValueNameExpandRate:
return "googExpandRate";
case kStatsValueNameFingerprint:
return "googFingerprint";
case kStatsValueNameFingerprintAlgorithm:
return "googFingerprintAlgorithm";
case kStatsValueNameFirsReceived:
return "googFirsReceived";
case kStatsValueNameFirsSent:
return "googFirsSent";
case kStatsValueNameFrameHeightInput:
return "googFrameHeightInput";
case kStatsValueNameFrameHeightReceived:
return "googFrameHeightReceived";
case kStatsValueNameFrameHeightSent:
return "googFrameHeightSent";
case kStatsValueNameFrameRateReceived:
return "googFrameRateReceived";
case kStatsValueNameFrameRateDecoded:
return "googFrameRateDecoded";
case kStatsValueNameFrameRateOutput:
return "googFrameRateOutput";
case kStatsValueNameDecodeMs:
return "googDecodeMs";
case kStatsValueNameMaxDecodeMs:
return "googMaxDecodeMs";
case kStatsValueNameCurrentDelayMs:
return "googCurrentDelayMs";
case kStatsValueNameTargetDelayMs:
return "googTargetDelayMs";
case kStatsValueNameJitterBufferMs:
return "googJitterBufferMs";
case kStatsValueNameMinPlayoutDelayMs:
return "googMinPlayoutDelayMs";
case kStatsValueNameRenderDelayMs:
return "googRenderDelayMs";
case kStatsValueNameCaptureStartNtpTimeMs:
return "googCaptureStartNtpTimeMs";
case kStatsValueNameFrameRateInput:
return "googFrameRateInput";
case kStatsValueNameFrameRateSent:
return "googFrameRateSent";
case kStatsValueNameFrameWidthInput:
return "googFrameWidthInput";
case kStatsValueNameFrameWidthReceived:
return "googFrameWidthReceived";
case kStatsValueNameFrameWidthSent:
return "googFrameWidthSent";
case kStatsValueNameInitiator:
return "googInitiator";
case kStatsValueNameIssuerId:
return "googIssuerId";
case kStatsValueNameJitterReceived:
return "googJitterReceived";
case kStatsValueNameLocalAddress:
return "googLocalAddress";
case kStatsValueNameLocalCandidateId:
return "localCandidateId";
case kStatsValueNameLocalCandidateType:
return "googLocalCandidateType";
case kStatsValueNameLocalCertificateId:
return "googLocalCertificateId";
case kStatsValueNameAdaptationChanges:
return "googAdaptationChanges";
case kStatsValueNameNacksReceived:
return "googNacksReceived";
case kStatsValueNameNacksSent:
return "googNacksSent";
case kStatsValueNamePlisReceived:
return "googPlisReceived";
case kStatsValueNamePlisSent:
return "googPlisSent";
case kStatsValueNamePreferredJitterBufferMs:
return "googPreferredJitterBufferMs";
case kStatsValueNameReadable:
return "googReadable";
case kStatsValueNameRecvPacketGroupArrivalTimeDebug:
return "googReceivedPacketGroupArrivalTimeDebug";
case kStatsValueNameRecvPacketGroupPropagationDeltaDebug:
return "googReceivedPacketGroupPropagationDeltaDebug";
case kStatsValueNameRecvPacketGroupPropagationDeltaSumDebug:
return "googReceivedPacketGroupPropagationDeltaSumDebug";
case kStatsValueNameRemoteAddress:
return "googRemoteAddress";
case kStatsValueNameRemoteCandidateId:
return "remoteCandidateId";
case kStatsValueNameRemoteCandidateType:
return "googRemoteCandidateType";
case kStatsValueNameRemoteCertificateId:
return "googRemoteCertificateId";
case kStatsValueNameRetransmitBitrate:
return "googRetransmitBitrate";
case kStatsValueNameRtt:
return "googRtt";
case kStatsValueNameSecondaryDecodedRate:
return "googSecondaryDecodedRate";
case kStatsValueNameSendPacketsDiscarded:
return "packetsDiscardedOnSend";
case kStatsValueNameSpeechExpandRate:
return "googSpeechExpandRate";
case kStatsValueNameTargetEncBitrate:
return "googTargetEncBitrate";
case kStatsValueNameTransmitBitrate:
return "googTransmitBitrate";
case kStatsValueNameTransportType:
return "googTransportType";
case kStatsValueNameTrackId:
return "googTrackId";
case kStatsValueNameTypingNoiseState:
return "googTypingNoiseState";
case kStatsValueNameViewLimitedResolution:
return "googViewLimitedResolution";
case kStatsValueNameWritable:
return "googWritable";
default:
ASSERT(false);
break;
}
return nullptr;
}
const std::string& StatsReport::Value::ToString() const {
return value;
}
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::NewTypedIntId(StatsType type, int id) {
return scoped_ptr<Id>(new TypedIntId(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)));
}
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) {
std::ostringstream oss;
oss << "[";
for (size_t i = 0; i < value.size(); ++i) {
oss << rtc::ToString<T>(value[i]);
if (i != value.size() - 1)
oss << ", ";
}
oss << "]";
AddValue(name, oss.str());
}
// Implementation specializations for the variants of AddValue that we use.
// TODO(tommi): Converting these ints to strings and copying strings, is not
// very efficient. Figure out a way to reduce the string churn.
template
void StatsReport::AddValue<std::string>(
StatsReport::StatsValueName, const std::vector<std::string>&);
template
void StatsReport::AddValue<int>(
StatsReport::StatsValueName, const std::vector<int>&);
template
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");
}
void StatsReport::ReplaceValue(StatsReport::StatsValueName name,
const std::string& value) {
Values::iterator it = std::find_if(values_.begin(), values_.end(),
[&name](const ValuePtr& v)->bool { return v->name == name; });
// Values are const once added since they may be used outside of the stats
// collection. So we remove it from values_ when replacing and add a new one.
if (it != values_.end()) {
if ((*it)->value == value)
return;
values_.erase(it);
}
AddValue(name, value);
}
void StatsReport::ResetValues() {
values_.clear();
}
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();
}
StatsCollection::StatsCollection() {
}
StatsCollection::~StatsCollection() {
for (auto* r : list_)
delete r;
}
StatsCollection::const_iterator StatsCollection::begin() const {
return list_.begin();
}
StatsCollection::const_iterator StatsCollection::end() const {
return list_.end();
}
size_t StatsCollection::size() const {
return list_.size();
}
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* 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* 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