1 // Copyright 2014 The Chromium Authors. All rights reserved.
2 // Use of this source code is governed by a BSD-style license that can be
3 // found in the LICENSE file.
5 #include "media/cast/logging/stats_event_subscriber.h"
7 #include "base/logging.h"
8 #include "base/values.h"
10 #define STAT_ENUM_TO_STRING(enum) \
19 using media::cast::CastLoggingEvent;
20 using media::cast::EventMediaType;
22 const size_t kMaxPacketEventTimeMapSize = 1000;
24 bool IsReceiverEvent(CastLoggingEvent event) {
25 return event == FRAME_DECODED
26 || event == FRAME_PLAYOUT
27 || event == FRAME_ACK_SENT
28 || event == PACKET_RECEIVED;
33 StatsEventSubscriber::StatsEventSubscriber(
34 EventMediaType event_media_type,
35 base::TickClock* clock,
36 ReceiverTimeOffsetEstimator* offset_estimator)
37 : event_media_type_(event_media_type),
39 offset_estimator_(offset_estimator),
40 network_latency_datapoints_(0),
41 e2e_latency_datapoints_(0),
42 num_frames_dropped_by_encoder_(0),
44 DCHECK(event_media_type == AUDIO_EVENT || event_media_type == VIDEO_EVENT);
45 base::TimeTicks now = clock_->NowTicks();
47 last_response_received_time_ = base::TimeTicks();
50 StatsEventSubscriber::~StatsEventSubscriber() {
51 DCHECK(thread_checker_.CalledOnValidThread());
54 void StatsEventSubscriber::OnReceiveFrameEvent(const FrameEvent& frame_event) {
55 DCHECK(thread_checker_.CalledOnValidThread());
57 CastLoggingEvent type = frame_event.type;
58 if (frame_event.media_type != event_media_type_)
61 FrameStatsMap::iterator it = frame_stats_.find(type);
62 if (it == frame_stats_.end()) {
64 stats.event_counter = 1;
65 stats.sum_size = frame_event.size;
66 stats.sum_delay = frame_event.delay_delta;
67 frame_stats_.insert(std::make_pair(type, stats));
69 ++(it->second.event_counter);
70 it->second.sum_size += frame_event.size;
71 it->second.sum_delay += frame_event.delay_delta;
74 if (type == FRAME_CAPTURE_BEGIN) {
75 RecordFrameCaptureTime(frame_event);
76 } else if (type == FRAME_ENCODED) {
77 MarkAsEncoded(frame_event.rtp_timestamp);
78 } else if (type == FRAME_PLAYOUT) {
79 RecordE2ELatency(frame_event);
80 if (frame_event.delay_delta <= base::TimeDelta())
84 if (IsReceiverEvent(type))
85 UpdateLastResponseTime(frame_event.timestamp);
88 void StatsEventSubscriber::OnReceivePacketEvent(
89 const PacketEvent& packet_event) {
90 DCHECK(thread_checker_.CalledOnValidThread());
92 CastLoggingEvent type = packet_event.type;
93 if (packet_event.media_type != event_media_type_)
96 PacketStatsMap::iterator it = packet_stats_.find(type);
97 if (it == packet_stats_.end()) {
99 stats.event_counter = 1;
100 stats.sum_size = packet_event.size;
101 packet_stats_.insert(std::make_pair(type, stats));
103 ++(it->second.event_counter);
104 it->second.sum_size += packet_event.size;
107 if (type == PACKET_SENT_TO_NETWORK ||
108 type == PACKET_RECEIVED) {
109 RecordNetworkLatency(packet_event);
110 } else if (type == PACKET_RETRANSMITTED) {
111 // We only measure network latency using packets that doesn't have to be
112 // retransmitted as there is precisely one sent-receive timestamp pairs.
113 ErasePacketSentTime(packet_event);
116 if (IsReceiverEvent(type))
117 UpdateLastResponseTime(packet_event.timestamp);
120 scoped_ptr<base::DictionaryValue> StatsEventSubscriber::GetStats() const {
122 GetStatsInternal(&stats_map);
123 scoped_ptr<base::DictionaryValue> ret(new base::DictionaryValue);
125 scoped_ptr<base::DictionaryValue> stats(new base::DictionaryValue);
126 for (StatsMap::const_iterator it = stats_map.begin(); it != stats_map.end();
128 stats->SetDouble(CastStatToString(it->first), it->second);
131 ret->Set(event_media_type_ == AUDIO_EVENT ? "audio" : "video",
137 void StatsEventSubscriber::Reset() {
138 DCHECK(thread_checker_.CalledOnValidThread());
140 frame_stats_.clear();
141 packet_stats_.clear();
142 total_network_latency_ = base::TimeDelta();
143 network_latency_datapoints_ = 0;
144 total_e2e_latency_ = base::TimeDelta();
145 e2e_latency_datapoints_ = 0;
146 num_frames_dropped_by_encoder_ = 0;
147 num_frames_late_ = 0;
148 recent_captured_frames_.clear();
149 packet_sent_times_.clear();
150 start_time_ = clock_->NowTicks();
151 last_response_received_time_ = base::TimeTicks();
155 const char* StatsEventSubscriber::CastStatToString(CastStat stat) {
157 STAT_ENUM_TO_STRING(CAPTURE_FPS);
158 STAT_ENUM_TO_STRING(ENCODE_FPS);
159 STAT_ENUM_TO_STRING(DECODE_FPS);
160 STAT_ENUM_TO_STRING(AVG_ENCODE_TIME_MS);
161 STAT_ENUM_TO_STRING(AVG_PLAYOUT_DELAY_MS);
162 STAT_ENUM_TO_STRING(AVG_NETWORK_LATENCY_MS);
163 STAT_ENUM_TO_STRING(AVG_E2E_LATENCY_MS);
164 STAT_ENUM_TO_STRING(ENCODE_KBPS);
165 STAT_ENUM_TO_STRING(TRANSMISSION_KBPS);
166 STAT_ENUM_TO_STRING(RETRANSMISSION_KBPS);
167 STAT_ENUM_TO_STRING(PACKET_LOSS_FRACTION);
168 STAT_ENUM_TO_STRING(MS_SINCE_LAST_RECEIVER_RESPONSE);
169 STAT_ENUM_TO_STRING(NUM_FRAMES_CAPTURED);
170 STAT_ENUM_TO_STRING(NUM_FRAMES_DROPPED_BY_ENCODER);
171 STAT_ENUM_TO_STRING(NUM_FRAMES_LATE);
172 STAT_ENUM_TO_STRING(NUM_PACKETS_SENT);
173 STAT_ENUM_TO_STRING(NUM_PACKETS_RETRANSMITTED);
174 STAT_ENUM_TO_STRING(NUM_PACKETS_RTX_REJECTED);
180 void StatsEventSubscriber::GetStatsInternal(StatsMap* stats_map) const {
181 DCHECK(thread_checker_.CalledOnValidThread());
185 base::TimeTicks end_time = clock_->NowTicks();
188 end_time, FRAME_CAPTURE_BEGIN, CAPTURE_FPS, stats_map);
190 end_time, FRAME_ENCODED, ENCODE_FPS, stats_map);
192 end_time, FRAME_DECODED, DECODE_FPS, stats_map);
193 PopulatePlayoutDelayStat(stats_map);
194 PopulateFrameBitrateStat(end_time, stats_map);
195 PopulatePacketBitrateStat(end_time,
196 PACKET_SENT_TO_NETWORK,
199 PopulatePacketBitrateStat(end_time,
200 PACKET_RETRANSMITTED,
203 PopulatePacketLossPercentageStat(stats_map);
204 PopulateFrameCountStat(FRAME_CAPTURE_END, NUM_FRAMES_CAPTURED, stats_map);
205 PopulatePacketCountStat(PACKET_SENT_TO_NETWORK, NUM_PACKETS_SENT, stats_map);
206 PopulatePacketCountStat(
207 PACKET_RETRANSMITTED, NUM_PACKETS_RETRANSMITTED, stats_map);
208 PopulatePacketCountStat(
209 PACKET_RTX_REJECTED, NUM_PACKETS_RTX_REJECTED, stats_map);
211 if (network_latency_datapoints_ > 0) {
212 double avg_network_latency_ms =
213 total_network_latency_.InMillisecondsF() /
214 network_latency_datapoints_;
216 std::make_pair(AVG_NETWORK_LATENCY_MS, avg_network_latency_ms));
219 if (e2e_latency_datapoints_ > 0) {
220 double avg_e2e_latency_ms =
221 total_e2e_latency_.InMillisecondsF() / e2e_latency_datapoints_;
222 stats_map->insert(std::make_pair(AVG_E2E_LATENCY_MS, avg_e2e_latency_ms));
225 if (!last_response_received_time_.is_null()) {
227 std::make_pair(MS_SINCE_LAST_RECEIVER_RESPONSE,
228 (end_time - last_response_received_time_).InMillisecondsF()));
231 stats_map->insert(std::make_pair(NUM_FRAMES_DROPPED_BY_ENCODER,
232 num_frames_dropped_by_encoder_));
233 stats_map->insert(std::make_pair(NUM_FRAMES_LATE, num_frames_late_));
236 bool StatsEventSubscriber::GetReceiverOffset(base::TimeDelta* offset) {
237 base::TimeDelta receiver_offset_lower_bound;
238 base::TimeDelta receiver_offset_upper_bound;
239 if (!offset_estimator_->GetReceiverOffsetBounds(
240 &receiver_offset_lower_bound, &receiver_offset_upper_bound)) {
244 *offset = (receiver_offset_lower_bound + receiver_offset_upper_bound) / 2;
248 void StatsEventSubscriber::RecordFrameCaptureTime(
249 const FrameEvent& frame_event) {
250 recent_captured_frames_.insert(std::make_pair(
251 frame_event.rtp_timestamp, FrameInfo(frame_event.timestamp)));
252 if (recent_captured_frames_.size() > kMaxFrameInfoMapSize) {
253 FrameInfoMap::iterator erase_it = recent_captured_frames_.begin();
254 if (!erase_it->second.encoded)
255 num_frames_dropped_by_encoder_++;
256 recent_captured_frames_.erase(erase_it);
260 void StatsEventSubscriber::MarkAsEncoded(RtpTimestamp rtp_timestamp) {
261 FrameInfoMap::iterator it = recent_captured_frames_.find(rtp_timestamp);
262 if (it != recent_captured_frames_.end())
263 it->second.encoded = true;
266 void StatsEventSubscriber::RecordE2ELatency(const FrameEvent& frame_event) {
267 base::TimeDelta receiver_offset;
268 if (!GetReceiverOffset(&receiver_offset))
271 FrameInfoMap::iterator it =
272 recent_captured_frames_.find(frame_event.rtp_timestamp);
273 if (it == recent_captured_frames_.end())
276 // Playout time is event time + playout delay.
277 base::TimeTicks playout_time =
278 frame_event.timestamp + frame_event.delay_delta - receiver_offset;
279 total_e2e_latency_ += playout_time - it->second.capture_time;
280 e2e_latency_datapoints_++;
283 void StatsEventSubscriber::UpdateLastResponseTime(
284 base::TimeTicks receiver_time) {
285 base::TimeDelta receiver_offset;
286 if (!GetReceiverOffset(&receiver_offset))
288 base::TimeTicks sender_time = receiver_time - receiver_offset;
289 last_response_received_time_ = sender_time;
292 void StatsEventSubscriber::ErasePacketSentTime(
293 const PacketEvent& packet_event) {
294 std::pair<RtpTimestamp, uint16> key(
295 std::make_pair(packet_event.rtp_timestamp, packet_event.packet_id));
296 packet_sent_times_.erase(key);
299 void StatsEventSubscriber::RecordNetworkLatency(
300 const PacketEvent& packet_event) {
301 base::TimeDelta receiver_offset;
302 if (!GetReceiverOffset(&receiver_offset))
305 std::pair<RtpTimestamp, uint16> key(
306 std::make_pair(packet_event.rtp_timestamp, packet_event.packet_id));
307 PacketEventTimeMap::iterator it = packet_sent_times_.find(key);
308 if (it == packet_sent_times_.end()) {
309 std::pair<RtpTimestamp, uint16> key(
310 std::make_pair(packet_event.rtp_timestamp, packet_event.packet_id));
311 std::pair<base::TimeTicks, CastLoggingEvent> value =
312 std::make_pair(packet_event.timestamp, packet_event.type);
313 packet_sent_times_.insert(std::make_pair(key, value));
314 if (packet_sent_times_.size() > kMaxPacketEventTimeMapSize)
315 packet_sent_times_.erase(packet_sent_times_.begin());
317 std::pair<base::TimeTicks, CastLoggingEvent> value = it->second;
318 CastLoggingEvent recorded_type = value.second;
320 base::TimeTicks packet_sent_time;
321 base::TimeTicks packet_received_time;
322 if (recorded_type == PACKET_SENT_TO_NETWORK &&
323 packet_event.type == PACKET_RECEIVED) {
324 packet_sent_time = value.first;
325 packet_received_time = packet_event.timestamp;
327 } else if (recorded_type == PACKET_RECEIVED &&
328 packet_event.type == PACKET_SENT_TO_NETWORK) {
329 packet_sent_time = packet_event.timestamp;
330 packet_received_time = value.first;
334 // Subtract by offset.
335 packet_received_time -= receiver_offset;
337 total_network_latency_ += packet_received_time - packet_sent_time;
338 network_latency_datapoints_++;
339 packet_sent_times_.erase(it);
344 void StatsEventSubscriber::PopulateFpsStat(base::TimeTicks end_time,
345 CastLoggingEvent event,
347 StatsMap* stats_map) const {
348 FrameStatsMap::const_iterator it = frame_stats_.find(event);
349 if (it != frame_stats_.end()) {
351 base::TimeDelta duration = (end_time - start_time_);
352 int count = it->second.event_counter;
353 if (duration > base::TimeDelta())
354 fps = count / duration.InSecondsF();
355 stats_map->insert(std::make_pair(stat, fps));
359 void StatsEventSubscriber::PopulateFrameCountStat(CastLoggingEvent event,
361 StatsMap* stats_map) const {
362 FrameStatsMap::const_iterator it = frame_stats_.find(event);
363 if (it != frame_stats_.end()) {
364 stats_map->insert(std::make_pair(stat, it->second.event_counter));
368 void StatsEventSubscriber::PopulatePacketCountStat(CastLoggingEvent event,
370 StatsMap* stats_map) const {
371 PacketStatsMap::const_iterator it = packet_stats_.find(event);
372 if (it != packet_stats_.end()) {
373 stats_map->insert(std::make_pair(stat, it->second.event_counter));
377 void StatsEventSubscriber::PopulatePlayoutDelayStat(StatsMap* stats_map) const {
378 FrameStatsMap::const_iterator it = frame_stats_.find(FRAME_PLAYOUT);
379 if (it != frame_stats_.end()) {
380 double avg_delay_ms = 0.0;
381 base::TimeDelta sum_delay = it->second.sum_delay;
382 int count = it->second.event_counter;
384 avg_delay_ms = sum_delay.InMillisecondsF() / count;
385 stats_map->insert(std::make_pair(AVG_PLAYOUT_DELAY_MS, avg_delay_ms));
389 void StatsEventSubscriber::PopulateFrameBitrateStat(base::TimeTicks end_time,
390 StatsMap* stats_map) const {
391 FrameStatsMap::const_iterator it = frame_stats_.find(FRAME_ENCODED);
392 if (it != frame_stats_.end()) {
394 base::TimeDelta duration = end_time - start_time_;
395 if (duration > base::TimeDelta()) {
396 kbps = it->second.sum_size / duration.InMillisecondsF() * 8;
399 stats_map->insert(std::make_pair(ENCODE_KBPS, kbps));
403 void StatsEventSubscriber::PopulatePacketBitrateStat(
404 base::TimeTicks end_time,
405 CastLoggingEvent event,
407 StatsMap* stats_map) const {
408 PacketStatsMap::const_iterator it = packet_stats_.find(event);
409 if (it != packet_stats_.end()) {
411 base::TimeDelta duration = end_time - start_time_;
412 if (duration > base::TimeDelta()) {
413 kbps = it->second.sum_size / duration.InMillisecondsF() * 8;
416 stats_map->insert(std::make_pair(stat, kbps));
420 void StatsEventSubscriber::PopulatePacketLossPercentageStat(
421 StatsMap* stats_map) const {
422 // We assume that retransmission means that the packet's previous
423 // (re)transmission was lost.
424 // This means the percentage of packet loss is
425 // (# of retransmit events) / (# of transmit + retransmit events).
426 PacketStatsMap::const_iterator sent_it =
427 packet_stats_.find(PACKET_SENT_TO_NETWORK);
428 if (sent_it == packet_stats_.end())
430 PacketStatsMap::const_iterator retransmitted_it =
431 packet_stats_.find(PACKET_RETRANSMITTED);
432 int sent_count = sent_it->second.event_counter;
433 int retransmitted_count = 0;
434 if (retransmitted_it != packet_stats_.end())
435 retransmitted_count = retransmitted_it->second.event_counter;
436 double packet_loss_fraction = static_cast<double>(retransmitted_count) /
437 (sent_count + retransmitted_count);
439 std::make_pair(PACKET_LOSS_FRACTION, packet_loss_fraction));
442 StatsEventSubscriber::FrameLogStats::FrameLogStats()
443 : event_counter(0), sum_size(0) {}
444 StatsEventSubscriber::FrameLogStats::~FrameLogStats() {}
446 StatsEventSubscriber::PacketLogStats::PacketLogStats()
447 : event_counter(0), sum_size(0) {}
448 StatsEventSubscriber::PacketLogStats::~PacketLogStats() {}
450 StatsEventSubscriber::FrameInfo::FrameInfo(base::TimeTicks capture_time)
451 : capture_time(capture_time), encoded(false) {
453 StatsEventSubscriber::FrameInfo::~FrameInfo() {