summaryrefslogtreecommitdiffstats
path: root/media/cast/logging/stats_event_subscriber.cc
diff options
context:
space:
mode:
Diffstat (limited to 'media/cast/logging/stats_event_subscriber.cc')
-rw-r--r--media/cast/logging/stats_event_subscriber.cc255
1 files changed, 228 insertions, 27 deletions
diff --git a/media/cast/logging/stats_event_subscriber.cc b/media/cast/logging/stats_event_subscriber.cc
index 03c669c..b9cf265 100644
--- a/media/cast/logging/stats_event_subscriber.cc
+++ b/media/cast/logging/stats_event_subscriber.cc
@@ -4,7 +4,9 @@
#include "media/cast/logging/stats_event_subscriber.h"
+#include "base/format_macros.h"
#include "base/logging.h"
+#include "base/strings/stringprintf.h"
#include "base/values.h"
#define STAT_ENUM_TO_STRING(enum) \
@@ -30,6 +32,63 @@ bool IsReceiverEvent(CastLoggingEvent event) {
} // namespace
+StatsEventSubscriber::SimpleHistogram::SimpleHistogram(int64 min,
+ int64 max,
+ int64 width)
+ : min_(min), max_(max), width_(width), buckets_((max - min) / width + 2) {
+ CHECK_GT(buckets_.size(), 2u);
+ CHECK_EQ(0, (max_ - min_) % width_);
+}
+
+StatsEventSubscriber::SimpleHistogram::~SimpleHistogram() {
+}
+
+void StatsEventSubscriber::SimpleHistogram::Add(int64 sample) {
+ if (sample < min_) {
+ ++buckets_.front();
+ } else if (sample >= max_) {
+ ++buckets_.back();
+ } else {
+ size_t index = 1 + (sample - min_) / width_;
+ DCHECK_LT(index, buckets_.size());
+ ++buckets_[index];
+ }
+}
+
+void StatsEventSubscriber::SimpleHistogram::Reset() {
+ buckets_.assign(buckets_.size(), 0);
+}
+
+scoped_ptr<base::ListValue>
+StatsEventSubscriber::SimpleHistogram::GetHistogram() const {
+ scoped_ptr<base::ListValue> histo(new base::ListValue);
+
+ scoped_ptr<base::DictionaryValue> bucket(new base::DictionaryValue);
+
+ bucket->SetString("bucket", base::StringPrintf("< %" PRId64, min_));
+ bucket->SetInteger("count", buckets_.front());
+ histo->Append(bucket.release());
+
+ for (size_t i = 1; i < buckets_.size() - 1; i++) {
+ bucket.reset(new base::DictionaryValue);
+
+ int64 lower = min_ + (i - 1) * width_;
+ int64 upper = lower + width_ - 1;
+ bucket->SetString(
+ "bucket", base::StringPrintf("%" PRId64 " - %" PRId64, lower, upper));
+ bucket->SetInteger("count", buckets_[i]);
+ histo->Append(bucket.release());
+ }
+
+ bucket.reset(new base::DictionaryValue);
+
+ bucket->SetString("bucket", base::StringPrintf(">= %" PRId64, max_));
+ bucket->SetInteger("count", buckets_.back());
+ histo->Append(bucket.release());
+
+ return histo.Pass();
+}
+
StatsEventSubscriber::StatsEventSubscriber(
EventMediaType event_media_type,
base::TickClock* clock,
@@ -40,11 +99,11 @@ StatsEventSubscriber::StatsEventSubscriber(
network_latency_datapoints_(0),
e2e_latency_datapoints_(0),
num_frames_dropped_by_encoder_(0),
- num_frames_late_(0) {
+ num_frames_late_(0),
+ start_time_(clock_->NowTicks()) {
DCHECK(event_media_type == AUDIO_EVENT || event_media_type == VIDEO_EVENT);
- base::TimeTicks now = clock_->NowTicks();
- start_time_ = now;
- last_response_received_time_ = base::TimeTicks();
+
+ InitHistograms();
}
StatsEventSubscriber::~StatsEventSubscriber() {
@@ -71,17 +130,26 @@ void StatsEventSubscriber::OnReceiveFrameEvent(const FrameEvent& frame_event) {
it->second.sum_delay += frame_event.delay_delta;
}
+ bool is_receiver_event = IsReceiverEvent(type);
+ UpdateFirstLastEventTime(frame_event.timestamp, is_receiver_event);
+
if (type == FRAME_CAPTURE_BEGIN) {
RecordFrameCaptureTime(frame_event);
+ } else if (type == FRAME_CAPTURE_END) {
+ RecordCaptureLatency(frame_event);
} else if (type == FRAME_ENCODED) {
- MarkAsEncoded(frame_event.rtp_timestamp);
+ RecordEncodeLatency(frame_event);
+ } else if (type == FRAME_ACK_SENT) {
+ RecordFrameTxLatency(frame_event);
} else if (type == FRAME_PLAYOUT) {
RecordE2ELatency(frame_event);
- if (frame_event.delay_delta <= base::TimeDelta())
+ base::TimeDelta delay_delta = frame_event.delay_delta;
+ histograms_[PLAYOUT_DELAY_MS_HISTO]->Add(delay_delta.InMillisecondsF());
+ if (delay_delta <= base::TimeDelta())
num_frames_late_++;
}
- if (IsReceiverEvent(type))
+ if (is_receiver_event)
UpdateLastResponseTime(frame_event.timestamp);
}
@@ -104,6 +172,9 @@ void StatsEventSubscriber::OnReceivePacketEvent(
it->second.sum_size += packet_event.size;
}
+ bool is_receiver_event = IsReceiverEvent(type);
+ UpdateFirstLastEventTime(packet_event.timestamp, is_receiver_event);
+
if (type == PACKET_SENT_TO_NETWORK ||
type == PACKET_RECEIVED) {
RecordNetworkLatency(packet_event);
@@ -113,10 +184,31 @@ void StatsEventSubscriber::OnReceivePacketEvent(
ErasePacketSentTime(packet_event);
}
- if (IsReceiverEvent(type))
+ if (is_receiver_event)
UpdateLastResponseTime(packet_event.timestamp);
}
+void StatsEventSubscriber::UpdateFirstLastEventTime(base::TimeTicks timestamp,
+ bool is_receiver_event) {
+ if (is_receiver_event) {
+ base::TimeDelta receiver_offset;
+ if (!GetReceiverOffset(&receiver_offset))
+ return;
+ timestamp -= receiver_offset;
+ }
+
+ if (first_event_time_.is_null()) {
+ first_event_time_ = timestamp;
+ } else {
+ first_event_time_ = std::min(first_event_time_, timestamp);
+ }
+ if (last_event_time_.is_null()) {
+ last_event_time_ = timestamp;
+ } else {
+ last_event_time_ = std::max(last_event_time_, timestamp);
+ }
+}
+
scoped_ptr<base::DictionaryValue> StatsEventSubscriber::GetStats() const {
StatsMap stats_map;
GetStatsInternal(&stats_map);
@@ -127,6 +219,12 @@ scoped_ptr<base::DictionaryValue> StatsEventSubscriber::GetStats() const {
++it) {
stats->SetDouble(CastStatToString(it->first), it->second);
}
+ for (HistogramMap::const_iterator it = histograms_.begin();
+ it != histograms_.end();
+ ++it) {
+ stats->Set(CastStatToString(it->first),
+ it->second->GetHistogram().release());
+ }
ret->Set(event_media_type_ == AUDIO_EVENT ? "audio" : "video",
stats.release());
@@ -145,10 +243,17 @@ void StatsEventSubscriber::Reset() {
e2e_latency_datapoints_ = 0;
num_frames_dropped_by_encoder_ = 0;
num_frames_late_ = 0;
- recent_captured_frames_.clear();
+ recent_frame_infos_.clear();
packet_sent_times_.clear();
start_time_ = clock_->NowTicks();
last_response_received_time_ = base::TimeTicks();
+ for (HistogramMap::iterator it = histograms_.begin(); it != histograms_.end();
+ ++it) {
+ it->second->Reset();
+ }
+
+ first_event_time_ = base::TimeTicks();
+ last_event_time_ = base::TimeTicks();
}
// static
@@ -172,11 +277,34 @@ const char* StatsEventSubscriber::CastStatToString(CastStat stat) {
STAT_ENUM_TO_STRING(NUM_PACKETS_SENT);
STAT_ENUM_TO_STRING(NUM_PACKETS_RETRANSMITTED);
STAT_ENUM_TO_STRING(NUM_PACKETS_RTX_REJECTED);
+ STAT_ENUM_TO_STRING(FIRST_EVENT_TIME_MS);
+ STAT_ENUM_TO_STRING(LAST_EVENT_TIME_MS);
+ STAT_ENUM_TO_STRING(CAPTURE_LATENCY_MS_HISTO);
+ STAT_ENUM_TO_STRING(ENCODE_LATENCY_MS_HISTO);
+ STAT_ENUM_TO_STRING(PACKET_LATENCY_MS_HISTO);
+ STAT_ENUM_TO_STRING(FRAME_LATENCY_MS_HISTO);
+ STAT_ENUM_TO_STRING(PLAYOUT_DELAY_MS_HISTO);
}
NOTREACHED();
return "";
}
+const int kMaxLatencyBucketMs = 800;
+const int kBucketWidthMs = 20;
+
+void StatsEventSubscriber::InitHistograms() {
+ histograms_[CAPTURE_LATENCY_MS_HISTO].reset(
+ new SimpleHistogram(0, kMaxLatencyBucketMs, kBucketWidthMs));
+ histograms_[ENCODE_LATENCY_MS_HISTO].reset(
+ new SimpleHistogram(0, kMaxLatencyBucketMs, kBucketWidthMs));
+ histograms_[PACKET_LATENCY_MS_HISTO].reset(
+ new SimpleHistogram(0, kMaxLatencyBucketMs, kBucketWidthMs));
+ histograms_[FRAME_LATENCY_MS_HISTO].reset(
+ new SimpleHistogram(0, kMaxLatencyBucketMs, kBucketWidthMs));
+ histograms_[PLAYOUT_DELAY_MS_HISTO].reset(
+ new SimpleHistogram(0, kMaxLatencyBucketMs, kBucketWidthMs));
+}
+
void StatsEventSubscriber::GetStatsInternal(StatsMap* stats_map) const {
DCHECK(thread_checker_.CalledOnValidThread());
@@ -231,6 +359,16 @@ void StatsEventSubscriber::GetStatsInternal(StatsMap* stats_map) const {
stats_map->insert(std::make_pair(NUM_FRAMES_DROPPED_BY_ENCODER,
num_frames_dropped_by_encoder_));
stats_map->insert(std::make_pair(NUM_FRAMES_LATE, num_frames_late_));
+ if (!first_event_time_.is_null()) {
+ stats_map->insert(std::make_pair(
+ FIRST_EVENT_TIME_MS,
+ (first_event_time_ - base::TimeTicks::UnixEpoch()).InMillisecondsF()));
+ }
+ if (!last_event_time_.is_null()) {
+ stats_map->insert(std::make_pair(
+ LAST_EVENT_TIME_MS,
+ (last_event_time_ - base::TimeTicks::UnixEpoch()).InMillisecondsF()));
+ }
}
bool StatsEventSubscriber::GetReceiverOffset(base::TimeDelta* offset) {
@@ -245,22 +383,83 @@ bool StatsEventSubscriber::GetReceiverOffset(base::TimeDelta* offset) {
return true;
}
+void StatsEventSubscriber::MaybeInsertFrameInfo(RtpTimestamp rtp_timestamp,
+ const FrameInfo& frame_info) {
+ // No need to insert if |rtp_timestamp| is the smaller than every key in the
+ // map as it is just going to get erased anyway.
+ if (recent_frame_infos_.size() == kMaxFrameInfoMapSize &&
+ rtp_timestamp < recent_frame_infos_.begin()->first) {
+ return;
+ }
+
+ recent_frame_infos_.insert(std::make_pair(rtp_timestamp, frame_info));
+
+ if (recent_frame_infos_.size() >= kMaxFrameInfoMapSize) {
+ FrameInfoMap::iterator erase_it = recent_frame_infos_.begin();
+ if (erase_it->second.encode_time.is_null())
+ num_frames_dropped_by_encoder_++;
+ recent_frame_infos_.erase(erase_it);
+ }
+}
+
void StatsEventSubscriber::RecordFrameCaptureTime(
const FrameEvent& frame_event) {
- recent_captured_frames_.insert(std::make_pair(
- frame_event.rtp_timestamp, FrameInfo(frame_event.timestamp)));
- if (recent_captured_frames_.size() > kMaxFrameInfoMapSize) {
- FrameInfoMap::iterator erase_it = recent_captured_frames_.begin();
- if (!erase_it->second.encoded)
- num_frames_dropped_by_encoder_++;
- recent_captured_frames_.erase(erase_it);
+ FrameInfo frame_info;
+ frame_info.capture_time = frame_event.timestamp;
+ MaybeInsertFrameInfo(frame_event.rtp_timestamp, frame_info);
+}
+
+void StatsEventSubscriber::RecordCaptureLatency(const FrameEvent& frame_event) {
+ FrameInfoMap::iterator it =
+ recent_frame_infos_.find(frame_event.rtp_timestamp);
+ if (it == recent_frame_infos_.end())
+ return;
+
+ if (!it->second.capture_time.is_null()) {
+ double capture_latency_ms =
+ (it->second.capture_time - frame_event.timestamp).InMillisecondsF();
+ histograms_[CAPTURE_LATENCY_MS_HISTO]->Add(capture_latency_ms);
}
+
+ it->second.capture_end_time = frame_event.timestamp;
}
-void StatsEventSubscriber::MarkAsEncoded(RtpTimestamp rtp_timestamp) {
- FrameInfoMap::iterator it = recent_captured_frames_.find(rtp_timestamp);
- if (it != recent_captured_frames_.end())
- it->second.encoded = true;
+void StatsEventSubscriber::RecordEncodeLatency(const FrameEvent& frame_event) {
+ FrameInfoMap::iterator it =
+ recent_frame_infos_.find(frame_event.rtp_timestamp);
+ if (it == recent_frame_infos_.end()) {
+ FrameInfo frame_info;
+ frame_info.encode_time = frame_event.timestamp;
+ MaybeInsertFrameInfo(frame_event.rtp_timestamp, frame_info);
+ return;
+ }
+
+ if (!it->second.capture_end_time.is_null()) {
+ double encode_latency_ms =
+ (frame_event.timestamp - it->second.capture_end_time).InMillisecondsF();
+ histograms_[ENCODE_LATENCY_MS_HISTO]->Add(encode_latency_ms);
+ }
+
+ it->second.encode_time = frame_event.timestamp;
+}
+
+void StatsEventSubscriber::RecordFrameTxLatency(const FrameEvent& frame_event) {
+ FrameInfoMap::iterator it =
+ recent_frame_infos_.find(frame_event.rtp_timestamp);
+ if (it == recent_frame_infos_.end())
+ return;
+
+ if (it->second.encode_time.is_null())
+ return;
+
+ base::TimeDelta receiver_offset;
+ if (!GetReceiverOffset(&receiver_offset))
+ return;
+
+ base::TimeTicks sender_time = frame_event.timestamp - receiver_offset;
+ double frame_tx_latency_ms =
+ (sender_time - it->second.encode_time).InMillisecondsF();
+ histograms_[FRAME_LATENCY_MS_HISTO]->Add(frame_tx_latency_ms);
}
void StatsEventSubscriber::RecordE2ELatency(const FrameEvent& frame_event) {
@@ -269,8 +468,8 @@ void StatsEventSubscriber::RecordE2ELatency(const FrameEvent& frame_event) {
return;
FrameInfoMap::iterator it =
- recent_captured_frames_.find(frame_event.rtp_timestamp);
- if (it == recent_captured_frames_.end())
+ recent_frame_infos_.find(frame_event.rtp_timestamp);
+ if (it == recent_frame_infos_.end())
return;
// Playout time is event time + playout delay.
@@ -306,8 +505,6 @@ void StatsEventSubscriber::RecordNetworkLatency(
std::make_pair(packet_event.rtp_timestamp, packet_event.packet_id));
PacketEventTimeMap::iterator it = packet_sent_times_.find(key);
if (it == packet_sent_times_.end()) {
- std::pair<RtpTimestamp, uint16> key(
- std::make_pair(packet_event.rtp_timestamp, packet_event.packet_id));
std::pair<base::TimeTicks, CastLoggingEvent> value =
std::make_pair(packet_event.timestamp, packet_event.type);
packet_sent_times_.insert(std::make_pair(key, value));
@@ -333,9 +530,14 @@ void StatsEventSubscriber::RecordNetworkLatency(
if (match) {
// Subtract by offset.
packet_received_time -= receiver_offset;
+ base::TimeDelta latency_delta = packet_received_time - packet_sent_time;
- total_network_latency_ += packet_received_time - packet_sent_time;
+ total_network_latency_ += latency_delta;
network_latency_datapoints_++;
+
+ histograms_[PACKET_LATENCY_MS_HISTO]->Add(
+ latency_delta.InMillisecondsF());
+
packet_sent_times_.erase(it);
}
}
@@ -447,8 +649,7 @@ StatsEventSubscriber::PacketLogStats::PacketLogStats()
: event_counter(0), sum_size(0) {}
StatsEventSubscriber::PacketLogStats::~PacketLogStats() {}
-StatsEventSubscriber::FrameInfo::FrameInfo(base::TimeTicks capture_time)
- : capture_time(capture_time), encoded(false) {
+StatsEventSubscriber::FrameInfo::FrameInfo() : encoded(false) {
}
StatsEventSubscriber::FrameInfo::~FrameInfo() {
}