diff options
Diffstat (limited to 'media/cast/logging/stats_event_subscriber.cc')
-rw-r--r-- | media/cast/logging/stats_event_subscriber.cc | 255 |
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() { } |