Chromium Code Reviews| Index: media/cast/logging/stats_event_subscriber.cc |
| diff --git a/media/cast/logging/stats_event_subscriber.cc b/media/cast/logging/stats_event_subscriber.cc |
| index 03c669cfcbcf4e2a0173079fdae6fbdc1b7426e1..87e5b425fa0be4c5b3a11a1e6e898c65a7b5f3b5 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,17 +277,43 @@ 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(DURATION_MS); |
|
Alpha Left Google
2014/08/22 01:49:04
This seems redundant since we have first event tim
imcheng
2014/08/22 19:14:17
Removed.
|
| + 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()); |
| stats_map->clear(); |
| base::TimeTicks end_time = clock_->NowTicks(); |
| + stats_map->insert( |
| + std::make_pair(DURATION_MS, (end_time - start_time_).InMillisecondsF())); |
| PopulateFpsStat( |
| end_time, FRAME_CAPTURE_BEGIN, CAPTURE_FPS, stats_map); |
| @@ -231,6 +362,12 @@ 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_)); |
| + stats_map->insert(std::make_pair( |
| + FIRST_EVENT_TIME_MS, |
| + (first_event_time_ - base::TimeTicks::UnixEpoch()).InMillisecondsF())); |
| + 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 +382,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 +467,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 +504,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 +529,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 +648,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() { |
| } |