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.
4
5 #include <cmath>
6
7 #include "media/cast/logging/stats_event_subscriber.h"
8
9 #include "base/format_macros.h"
10 #include "base/logging.h"
11 #include "base/strings/stringprintf.h"
12 #include "base/values.h"
13
14 #define STAT_ENUM_TO_STRING(enum) \
15 case enum: \
16 return #enum
17
18 namespace media {
19 namespace cast {
20
21 namespace {
22
23 using media::cast::CastLoggingEvent;
24 using media::cast::EventMediaType;
25
26 const size_t kMaxPacketEventTimeMapSize = 1000;
27
IsReceiverEvent(CastLoggingEvent event)28 bool IsReceiverEvent(CastLoggingEvent event) {
29 return event == FRAME_DECODED
30 || event == FRAME_PLAYOUT
31 || event == FRAME_ACK_SENT
32 || event == PACKET_RECEIVED;
33 }
34
35 } // namespace
36
SimpleHistogram(int64 min,int64 max,int64 width)37 StatsEventSubscriber::SimpleHistogram::SimpleHistogram(int64 min,
38 int64 max,
39 int64 width)
40 : min_(min), max_(max), width_(width), buckets_((max - min) / width + 2) {
41 CHECK_GT(buckets_.size(), 2u);
42 CHECK_EQ(0, (max_ - min_) % width_);
43 }
44
~SimpleHistogram()45 StatsEventSubscriber::SimpleHistogram::~SimpleHistogram() {
46 }
47
Add(int64 sample)48 void StatsEventSubscriber::SimpleHistogram::Add(int64 sample) {
49 if (sample < min_) {
50 ++buckets_.front();
51 } else if (sample >= max_) {
52 ++buckets_.back();
53 } else {
54 size_t index = 1 + (sample - min_) / width_;
55 DCHECK_LT(index, buckets_.size());
56 ++buckets_[index];
57 }
58 }
59
Reset()60 void StatsEventSubscriber::SimpleHistogram::Reset() {
61 buckets_.assign(buckets_.size(), 0);
62 }
63
64 scoped_ptr<base::ListValue>
GetHistogram() const65 StatsEventSubscriber::SimpleHistogram::GetHistogram() const {
66 scoped_ptr<base::ListValue> histo(new base::ListValue);
67
68 scoped_ptr<base::DictionaryValue> bucket(new base::DictionaryValue);
69
70 if (buckets_.front()) {
71 bucket->SetInteger(base::StringPrintf("<%" PRId64, min_),
72 buckets_.front());
73 histo->Append(bucket.release());
74 }
75
76 for (size_t i = 1; i < buckets_.size() - 1; i++) {
77 if (!buckets_[i])
78 continue;
79 bucket.reset(new base::DictionaryValue);
80 int64 lower = min_ + (i - 1) * width_;
81 int64 upper = lower + width_ - 1;
82 bucket->SetInteger(
83 base::StringPrintf("%" PRId64 "-%" PRId64, lower, upper),
84 buckets_[i]);
85 histo->Append(bucket.release());
86 }
87
88 if (buckets_.back()) {
89 bucket.reset(new base::DictionaryValue);
90 bucket->SetInteger(base::StringPrintf(">=%" PRId64, max_),
91 buckets_.back());
92 histo->Append(bucket.release());
93 }
94 return histo.Pass();
95 }
96
StatsEventSubscriber(EventMediaType event_media_type,base::TickClock * clock,ReceiverTimeOffsetEstimator * offset_estimator)97 StatsEventSubscriber::StatsEventSubscriber(
98 EventMediaType event_media_type,
99 base::TickClock* clock,
100 ReceiverTimeOffsetEstimator* offset_estimator)
101 : event_media_type_(event_media_type),
102 clock_(clock),
103 offset_estimator_(offset_estimator),
104 network_latency_datapoints_(0),
105 e2e_latency_datapoints_(0),
106 num_frames_dropped_by_encoder_(0),
107 num_frames_late_(0),
108 start_time_(clock_->NowTicks()) {
109 DCHECK(event_media_type == AUDIO_EVENT || event_media_type == VIDEO_EVENT);
110
111 InitHistograms();
112 }
113
~StatsEventSubscriber()114 StatsEventSubscriber::~StatsEventSubscriber() {
115 DCHECK(thread_checker_.CalledOnValidThread());
116 }
117
OnReceiveFrameEvent(const FrameEvent & frame_event)118 void StatsEventSubscriber::OnReceiveFrameEvent(const FrameEvent& frame_event) {
119 DCHECK(thread_checker_.CalledOnValidThread());
120
121 CastLoggingEvent type = frame_event.type;
122 if (frame_event.media_type != event_media_type_)
123 return;
124
125 FrameStatsMap::iterator it = frame_stats_.find(type);
126 if (it == frame_stats_.end()) {
127 FrameLogStats stats;
128 stats.event_counter = 1;
129 stats.sum_size = frame_event.size;
130 stats.sum_delay = frame_event.delay_delta;
131 frame_stats_.insert(std::make_pair(type, stats));
132 } else {
133 ++(it->second.event_counter);
134 it->second.sum_size += frame_event.size;
135 it->second.sum_delay += frame_event.delay_delta;
136 }
137
138 bool is_receiver_event = IsReceiverEvent(type);
139 UpdateFirstLastEventTime(frame_event.timestamp, is_receiver_event);
140
141 if (type == FRAME_CAPTURE_BEGIN) {
142 RecordFrameCaptureTime(frame_event);
143 } else if (type == FRAME_CAPTURE_END) {
144 RecordCaptureLatency(frame_event);
145 } else if (type == FRAME_ENCODED) {
146 RecordEncodeLatency(frame_event);
147 } else if (type == FRAME_ACK_SENT) {
148 RecordFrameTxLatency(frame_event);
149 } else if (type == FRAME_PLAYOUT) {
150 RecordE2ELatency(frame_event);
151 base::TimeDelta delay_delta = frame_event.delay_delta;
152 histograms_[PLAYOUT_DELAY_MS_HISTO]->Add(delay_delta.InMillisecondsF());
153 if (delay_delta <= base::TimeDelta())
154 num_frames_late_++;
155 }
156
157 if (is_receiver_event)
158 UpdateLastResponseTime(frame_event.timestamp);
159 }
160
OnReceivePacketEvent(const PacketEvent & packet_event)161 void StatsEventSubscriber::OnReceivePacketEvent(
162 const PacketEvent& packet_event) {
163 DCHECK(thread_checker_.CalledOnValidThread());
164
165 CastLoggingEvent type = packet_event.type;
166 if (packet_event.media_type != event_media_type_)
167 return;
168
169 PacketStatsMap::iterator it = packet_stats_.find(type);
170 if (it == packet_stats_.end()) {
171 PacketLogStats stats;
172 stats.event_counter = 1;
173 stats.sum_size = packet_event.size;
174 packet_stats_.insert(std::make_pair(type, stats));
175 } else {
176 ++(it->second.event_counter);
177 it->second.sum_size += packet_event.size;
178 }
179
180 bool is_receiver_event = IsReceiverEvent(type);
181 UpdateFirstLastEventTime(packet_event.timestamp, is_receiver_event);
182
183 if (type == PACKET_SENT_TO_NETWORK ||
184 type == PACKET_RECEIVED) {
185 RecordNetworkLatency(packet_event);
186 } else if (type == PACKET_RETRANSMITTED) {
187 // We only measure network latency using packets that doesn't have to be
188 // retransmitted as there is precisely one sent-receive timestamp pairs.
189 ErasePacketSentTime(packet_event);
190 }
191
192 if (is_receiver_event)
193 UpdateLastResponseTime(packet_event.timestamp);
194 }
195
UpdateFirstLastEventTime(base::TimeTicks timestamp,bool is_receiver_event)196 void StatsEventSubscriber::UpdateFirstLastEventTime(base::TimeTicks timestamp,
197 bool is_receiver_event) {
198 if (is_receiver_event) {
199 base::TimeDelta receiver_offset;
200 if (!GetReceiverOffset(&receiver_offset))
201 return;
202 timestamp -= receiver_offset;
203 }
204
205 if (first_event_time_.is_null()) {
206 first_event_time_ = timestamp;
207 } else {
208 first_event_time_ = std::min(first_event_time_, timestamp);
209 }
210 if (last_event_time_.is_null()) {
211 last_event_time_ = timestamp;
212 } else {
213 last_event_time_ = std::max(last_event_time_, timestamp);
214 }
215 }
216
GetStats() const217 scoped_ptr<base::DictionaryValue> StatsEventSubscriber::GetStats() const {
218 StatsMap stats_map;
219 GetStatsInternal(&stats_map);
220 scoped_ptr<base::DictionaryValue> ret(new base::DictionaryValue);
221
222 scoped_ptr<base::DictionaryValue> stats(new base::DictionaryValue);
223 for (StatsMap::const_iterator it = stats_map.begin(); it != stats_map.end();
224 ++it) {
225 // Round to 3 digits after the decimal point.
226 stats->SetDouble(CastStatToString(it->first),
227 round(it->second * 1000.0) / 1000.0);
228 }
229 for (HistogramMap::const_iterator it = histograms_.begin();
230 it != histograms_.end();
231 ++it) {
232 stats->Set(CastStatToString(it->first),
233 it->second->GetHistogram().release());
234 }
235
236 ret->Set(event_media_type_ == AUDIO_EVENT ? "audio" : "video",
237 stats.release());
238
239 return ret.Pass();
240 }
241
Reset()242 void StatsEventSubscriber::Reset() {
243 DCHECK(thread_checker_.CalledOnValidThread());
244
245 frame_stats_.clear();
246 packet_stats_.clear();
247 total_network_latency_ = base::TimeDelta();
248 network_latency_datapoints_ = 0;
249 total_e2e_latency_ = base::TimeDelta();
250 e2e_latency_datapoints_ = 0;
251 num_frames_dropped_by_encoder_ = 0;
252 num_frames_late_ = 0;
253 recent_frame_infos_.clear();
254 packet_sent_times_.clear();
255 start_time_ = clock_->NowTicks();
256 last_response_received_time_ = base::TimeTicks();
257 for (HistogramMap::iterator it = histograms_.begin(); it != histograms_.end();
258 ++it) {
259 it->second->Reset();
260 }
261
262 first_event_time_ = base::TimeTicks();
263 last_event_time_ = base::TimeTicks();
264 }
265
266 // static
CastStatToString(CastStat stat)267 const char* StatsEventSubscriber::CastStatToString(CastStat stat) {
268 switch (stat) {
269 STAT_ENUM_TO_STRING(CAPTURE_FPS);
270 STAT_ENUM_TO_STRING(ENCODE_FPS);
271 STAT_ENUM_TO_STRING(DECODE_FPS);
272 STAT_ENUM_TO_STRING(AVG_ENCODE_TIME_MS);
273 STAT_ENUM_TO_STRING(AVG_PLAYOUT_DELAY_MS);
274 STAT_ENUM_TO_STRING(AVG_NETWORK_LATENCY_MS);
275 STAT_ENUM_TO_STRING(AVG_E2E_LATENCY_MS);
276 STAT_ENUM_TO_STRING(ENCODE_KBPS);
277 STAT_ENUM_TO_STRING(TRANSMISSION_KBPS);
278 STAT_ENUM_TO_STRING(RETRANSMISSION_KBPS);
279 STAT_ENUM_TO_STRING(PACKET_LOSS_FRACTION);
280 STAT_ENUM_TO_STRING(MS_SINCE_LAST_RECEIVER_RESPONSE);
281 STAT_ENUM_TO_STRING(NUM_FRAMES_CAPTURED);
282 STAT_ENUM_TO_STRING(NUM_FRAMES_DROPPED_BY_ENCODER);
283 STAT_ENUM_TO_STRING(NUM_FRAMES_LATE);
284 STAT_ENUM_TO_STRING(NUM_PACKETS_SENT);
285 STAT_ENUM_TO_STRING(NUM_PACKETS_RETRANSMITTED);
286 STAT_ENUM_TO_STRING(NUM_PACKETS_RTX_REJECTED);
287 STAT_ENUM_TO_STRING(FIRST_EVENT_TIME_MS);
288 STAT_ENUM_TO_STRING(LAST_EVENT_TIME_MS);
289 STAT_ENUM_TO_STRING(CAPTURE_LATENCY_MS_HISTO);
290 STAT_ENUM_TO_STRING(ENCODE_LATENCY_MS_HISTO);
291 STAT_ENUM_TO_STRING(PACKET_LATENCY_MS_HISTO);
292 STAT_ENUM_TO_STRING(FRAME_LATENCY_MS_HISTO);
293 STAT_ENUM_TO_STRING(PLAYOUT_DELAY_MS_HISTO);
294 }
295 NOTREACHED();
296 return "";
297 }
298
299 const int kMaxLatencyBucketMs = 800;
300 const int kBucketWidthMs = 20;
301
InitHistograms()302 void StatsEventSubscriber::InitHistograms() {
303 histograms_[CAPTURE_LATENCY_MS_HISTO].reset(
304 new SimpleHistogram(0, kMaxLatencyBucketMs, kBucketWidthMs));
305 histograms_[ENCODE_LATENCY_MS_HISTO].reset(
306 new SimpleHistogram(0, kMaxLatencyBucketMs, kBucketWidthMs));
307 histograms_[PACKET_LATENCY_MS_HISTO].reset(
308 new SimpleHistogram(0, kMaxLatencyBucketMs, kBucketWidthMs));
309 histograms_[FRAME_LATENCY_MS_HISTO].reset(
310 new SimpleHistogram(0, kMaxLatencyBucketMs, kBucketWidthMs));
311 histograms_[PLAYOUT_DELAY_MS_HISTO].reset(
312 new SimpleHistogram(0, kMaxLatencyBucketMs, kBucketWidthMs));
313 }
314
GetStatsInternal(StatsMap * stats_map) const315 void StatsEventSubscriber::GetStatsInternal(StatsMap* stats_map) const {
316 DCHECK(thread_checker_.CalledOnValidThread());
317
318 stats_map->clear();
319
320 base::TimeTicks end_time = clock_->NowTicks();
321
322 PopulateFpsStat(
323 end_time, FRAME_CAPTURE_BEGIN, CAPTURE_FPS, stats_map);
324 PopulateFpsStat(
325 end_time, FRAME_ENCODED, ENCODE_FPS, stats_map);
326 PopulateFpsStat(
327 end_time, FRAME_DECODED, DECODE_FPS, stats_map);
328 PopulatePlayoutDelayStat(stats_map);
329 PopulateFrameBitrateStat(end_time, stats_map);
330 PopulatePacketBitrateStat(end_time,
331 PACKET_SENT_TO_NETWORK,
332 TRANSMISSION_KBPS,
333 stats_map);
334 PopulatePacketBitrateStat(end_time,
335 PACKET_RETRANSMITTED,
336 RETRANSMISSION_KBPS,
337 stats_map);
338 PopulatePacketLossPercentageStat(stats_map);
339 PopulateFrameCountStat(FRAME_CAPTURE_END, NUM_FRAMES_CAPTURED, stats_map);
340 PopulatePacketCountStat(PACKET_SENT_TO_NETWORK, NUM_PACKETS_SENT, stats_map);
341 PopulatePacketCountStat(
342 PACKET_RETRANSMITTED, NUM_PACKETS_RETRANSMITTED, stats_map);
343 PopulatePacketCountStat(
344 PACKET_RTX_REJECTED, NUM_PACKETS_RTX_REJECTED, stats_map);
345
346 if (network_latency_datapoints_ > 0) {
347 double avg_network_latency_ms =
348 total_network_latency_.InMillisecondsF() /
349 network_latency_datapoints_;
350 stats_map->insert(
351 std::make_pair(AVG_NETWORK_LATENCY_MS, avg_network_latency_ms));
352 }
353
354 if (e2e_latency_datapoints_ > 0) {
355 double avg_e2e_latency_ms =
356 total_e2e_latency_.InMillisecondsF() / e2e_latency_datapoints_;
357 stats_map->insert(std::make_pair(AVG_E2E_LATENCY_MS, avg_e2e_latency_ms));
358 }
359
360 if (!last_response_received_time_.is_null()) {
361 stats_map->insert(
362 std::make_pair(MS_SINCE_LAST_RECEIVER_RESPONSE,
363 (end_time - last_response_received_time_).InMillisecondsF()));
364 }
365
366 stats_map->insert(std::make_pair(NUM_FRAMES_DROPPED_BY_ENCODER,
367 num_frames_dropped_by_encoder_));
368 stats_map->insert(std::make_pair(NUM_FRAMES_LATE, num_frames_late_));
369 if (!first_event_time_.is_null()) {
370 stats_map->insert(std::make_pair(
371 FIRST_EVENT_TIME_MS,
372 (first_event_time_ - base::TimeTicks::UnixEpoch()).InMillisecondsF()));
373 }
374 if (!last_event_time_.is_null()) {
375 stats_map->insert(std::make_pair(
376 LAST_EVENT_TIME_MS,
377 (last_event_time_ - base::TimeTicks::UnixEpoch()).InMillisecondsF()));
378 }
379 }
380
GetReceiverOffset(base::TimeDelta * offset)381 bool StatsEventSubscriber::GetReceiverOffset(base::TimeDelta* offset) {
382 base::TimeDelta receiver_offset_lower_bound;
383 base::TimeDelta receiver_offset_upper_bound;
384 if (!offset_estimator_->GetReceiverOffsetBounds(
385 &receiver_offset_lower_bound, &receiver_offset_upper_bound)) {
386 return false;
387 }
388
389 *offset = (receiver_offset_lower_bound + receiver_offset_upper_bound) / 2;
390 return true;
391 }
392
MaybeInsertFrameInfo(RtpTimestamp rtp_timestamp,const FrameInfo & frame_info)393 void StatsEventSubscriber::MaybeInsertFrameInfo(RtpTimestamp rtp_timestamp,
394 const FrameInfo& frame_info) {
395 // No need to insert if |rtp_timestamp| is the smaller than every key in the
396 // map as it is just going to get erased anyway.
397 if (recent_frame_infos_.size() == kMaxFrameInfoMapSize &&
398 rtp_timestamp < recent_frame_infos_.begin()->first) {
399 return;
400 }
401
402 recent_frame_infos_.insert(std::make_pair(rtp_timestamp, frame_info));
403
404 if (recent_frame_infos_.size() >= kMaxFrameInfoMapSize) {
405 FrameInfoMap::iterator erase_it = recent_frame_infos_.begin();
406 if (erase_it->second.encode_time.is_null())
407 num_frames_dropped_by_encoder_++;
408 recent_frame_infos_.erase(erase_it);
409 }
410 }
411
RecordFrameCaptureTime(const FrameEvent & frame_event)412 void StatsEventSubscriber::RecordFrameCaptureTime(
413 const FrameEvent& frame_event) {
414 FrameInfo frame_info;
415 frame_info.capture_time = frame_event.timestamp;
416 MaybeInsertFrameInfo(frame_event.rtp_timestamp, frame_info);
417 }
418
RecordCaptureLatency(const FrameEvent & frame_event)419 void StatsEventSubscriber::RecordCaptureLatency(const FrameEvent& frame_event) {
420 FrameInfoMap::iterator it =
421 recent_frame_infos_.find(frame_event.rtp_timestamp);
422 if (it == recent_frame_infos_.end())
423 return;
424
425 if (!it->second.capture_time.is_null()) {
426 double capture_latency_ms =
427 (it->second.capture_time - frame_event.timestamp).InMillisecondsF();
428 histograms_[CAPTURE_LATENCY_MS_HISTO]->Add(capture_latency_ms);
429 }
430
431 it->second.capture_end_time = frame_event.timestamp;
432 }
433
RecordEncodeLatency(const FrameEvent & frame_event)434 void StatsEventSubscriber::RecordEncodeLatency(const FrameEvent& frame_event) {
435 FrameInfoMap::iterator it =
436 recent_frame_infos_.find(frame_event.rtp_timestamp);
437 if (it == recent_frame_infos_.end()) {
438 FrameInfo frame_info;
439 frame_info.encode_time = frame_event.timestamp;
440 MaybeInsertFrameInfo(frame_event.rtp_timestamp, frame_info);
441 return;
442 }
443
444 if (!it->second.capture_end_time.is_null()) {
445 double encode_latency_ms =
446 (frame_event.timestamp - it->second.capture_end_time).InMillisecondsF();
447 histograms_[ENCODE_LATENCY_MS_HISTO]->Add(encode_latency_ms);
448 }
449
450 it->second.encode_time = frame_event.timestamp;
451 }
452
RecordFrameTxLatency(const FrameEvent & frame_event)453 void StatsEventSubscriber::RecordFrameTxLatency(const FrameEvent& frame_event) {
454 FrameInfoMap::iterator it =
455 recent_frame_infos_.find(frame_event.rtp_timestamp);
456 if (it == recent_frame_infos_.end())
457 return;
458
459 if (it->second.encode_time.is_null())
460 return;
461
462 base::TimeDelta receiver_offset;
463 if (!GetReceiverOffset(&receiver_offset))
464 return;
465
466 base::TimeTicks sender_time = frame_event.timestamp - receiver_offset;
467 double frame_tx_latency_ms =
468 (sender_time - it->second.encode_time).InMillisecondsF();
469 histograms_[FRAME_LATENCY_MS_HISTO]->Add(frame_tx_latency_ms);
470 }
471
RecordE2ELatency(const FrameEvent & frame_event)472 void StatsEventSubscriber::RecordE2ELatency(const FrameEvent& frame_event) {
473 base::TimeDelta receiver_offset;
474 if (!GetReceiverOffset(&receiver_offset))
475 return;
476
477 FrameInfoMap::iterator it =
478 recent_frame_infos_.find(frame_event.rtp_timestamp);
479 if (it == recent_frame_infos_.end())
480 return;
481
482 // Playout time is event time + playout delay.
483 base::TimeTicks playout_time =
484 frame_event.timestamp + frame_event.delay_delta - receiver_offset;
485 total_e2e_latency_ += playout_time - it->second.capture_time;
486 e2e_latency_datapoints_++;
487 }
488
UpdateLastResponseTime(base::TimeTicks receiver_time)489 void StatsEventSubscriber::UpdateLastResponseTime(
490 base::TimeTicks receiver_time) {
491 base::TimeDelta receiver_offset;
492 if (!GetReceiverOffset(&receiver_offset))
493 return;
494 base::TimeTicks sender_time = receiver_time - receiver_offset;
495 last_response_received_time_ = sender_time;
496 }
497
ErasePacketSentTime(const PacketEvent & packet_event)498 void StatsEventSubscriber::ErasePacketSentTime(
499 const PacketEvent& packet_event) {
500 std::pair<RtpTimestamp, uint16> key(
501 std::make_pair(packet_event.rtp_timestamp, packet_event.packet_id));
502 packet_sent_times_.erase(key);
503 }
504
RecordNetworkLatency(const PacketEvent & packet_event)505 void StatsEventSubscriber::RecordNetworkLatency(
506 const PacketEvent& packet_event) {
507 base::TimeDelta receiver_offset;
508 if (!GetReceiverOffset(&receiver_offset))
509 return;
510
511 std::pair<RtpTimestamp, uint16> key(
512 std::make_pair(packet_event.rtp_timestamp, packet_event.packet_id));
513 PacketEventTimeMap::iterator it = packet_sent_times_.find(key);
514 if (it == packet_sent_times_.end()) {
515 std::pair<base::TimeTicks, CastLoggingEvent> value =
516 std::make_pair(packet_event.timestamp, packet_event.type);
517 packet_sent_times_.insert(std::make_pair(key, value));
518 if (packet_sent_times_.size() > kMaxPacketEventTimeMapSize)
519 packet_sent_times_.erase(packet_sent_times_.begin());
520 } else {
521 std::pair<base::TimeTicks, CastLoggingEvent> value = it->second;
522 CastLoggingEvent recorded_type = value.second;
523 bool match = false;
524 base::TimeTicks packet_sent_time;
525 base::TimeTicks packet_received_time;
526 if (recorded_type == PACKET_SENT_TO_NETWORK &&
527 packet_event.type == PACKET_RECEIVED) {
528 packet_sent_time = value.first;
529 packet_received_time = packet_event.timestamp;
530 match = true;
531 } else if (recorded_type == PACKET_RECEIVED &&
532 packet_event.type == PACKET_SENT_TO_NETWORK) {
533 packet_sent_time = packet_event.timestamp;
534 packet_received_time = value.first;
535 match = true;
536 }
537 if (match) {
538 // Subtract by offset.
539 packet_received_time -= receiver_offset;
540 base::TimeDelta latency_delta = packet_received_time - packet_sent_time;
541
542 total_network_latency_ += latency_delta;
543 network_latency_datapoints_++;
544
545 histograms_[PACKET_LATENCY_MS_HISTO]->Add(
546 latency_delta.InMillisecondsF());
547
548 packet_sent_times_.erase(it);
549 }
550 }
551 }
552
PopulateFpsStat(base::TimeTicks end_time,CastLoggingEvent event,CastStat stat,StatsMap * stats_map) const553 void StatsEventSubscriber::PopulateFpsStat(base::TimeTicks end_time,
554 CastLoggingEvent event,
555 CastStat stat,
556 StatsMap* stats_map) const {
557 FrameStatsMap::const_iterator it = frame_stats_.find(event);
558 if (it != frame_stats_.end()) {
559 double fps = 0.0;
560 base::TimeDelta duration = (end_time - start_time_);
561 int count = it->second.event_counter;
562 if (duration > base::TimeDelta())
563 fps = count / duration.InSecondsF();
564 stats_map->insert(std::make_pair(stat, fps));
565 }
566 }
567
PopulateFrameCountStat(CastLoggingEvent event,CastStat stat,StatsMap * stats_map) const568 void StatsEventSubscriber::PopulateFrameCountStat(CastLoggingEvent event,
569 CastStat stat,
570 StatsMap* stats_map) const {
571 FrameStatsMap::const_iterator it = frame_stats_.find(event);
572 if (it != frame_stats_.end()) {
573 stats_map->insert(std::make_pair(stat, it->second.event_counter));
574 }
575 }
576
PopulatePacketCountStat(CastLoggingEvent event,CastStat stat,StatsMap * stats_map) const577 void StatsEventSubscriber::PopulatePacketCountStat(CastLoggingEvent event,
578 CastStat stat,
579 StatsMap* stats_map) const {
580 PacketStatsMap::const_iterator it = packet_stats_.find(event);
581 if (it != packet_stats_.end()) {
582 stats_map->insert(std::make_pair(stat, it->second.event_counter));
583 }
584 }
585
PopulatePlayoutDelayStat(StatsMap * stats_map) const586 void StatsEventSubscriber::PopulatePlayoutDelayStat(StatsMap* stats_map) const {
587 FrameStatsMap::const_iterator it = frame_stats_.find(FRAME_PLAYOUT);
588 if (it != frame_stats_.end()) {
589 double avg_delay_ms = 0.0;
590 base::TimeDelta sum_delay = it->second.sum_delay;
591 int count = it->second.event_counter;
592 if (count != 0)
593 avg_delay_ms = sum_delay.InMillisecondsF() / count;
594 stats_map->insert(std::make_pair(AVG_PLAYOUT_DELAY_MS, avg_delay_ms));
595 }
596 }
597
PopulateFrameBitrateStat(base::TimeTicks end_time,StatsMap * stats_map) const598 void StatsEventSubscriber::PopulateFrameBitrateStat(base::TimeTicks end_time,
599 StatsMap* stats_map) const {
600 FrameStatsMap::const_iterator it = frame_stats_.find(FRAME_ENCODED);
601 if (it != frame_stats_.end()) {
602 double kbps = 0.0;
603 base::TimeDelta duration = end_time - start_time_;
604 if (duration > base::TimeDelta()) {
605 kbps = it->second.sum_size / duration.InMillisecondsF() * 8;
606 }
607
608 stats_map->insert(std::make_pair(ENCODE_KBPS, kbps));
609 }
610 }
611
PopulatePacketBitrateStat(base::TimeTicks end_time,CastLoggingEvent event,CastStat stat,StatsMap * stats_map) const612 void StatsEventSubscriber::PopulatePacketBitrateStat(
613 base::TimeTicks end_time,
614 CastLoggingEvent event,
615 CastStat stat,
616 StatsMap* stats_map) const {
617 PacketStatsMap::const_iterator it = packet_stats_.find(event);
618 if (it != packet_stats_.end()) {
619 double kbps = 0;
620 base::TimeDelta duration = end_time - start_time_;
621 if (duration > base::TimeDelta()) {
622 kbps = it->second.sum_size / duration.InMillisecondsF() * 8;
623 }
624
625 stats_map->insert(std::make_pair(stat, kbps));
626 }
627 }
628
PopulatePacketLossPercentageStat(StatsMap * stats_map) const629 void StatsEventSubscriber::PopulatePacketLossPercentageStat(
630 StatsMap* stats_map) const {
631 // We assume that retransmission means that the packet's previous
632 // (re)transmission was lost.
633 // This means the percentage of packet loss is
634 // (# of retransmit events) / (# of transmit + retransmit events).
635 PacketStatsMap::const_iterator sent_it =
636 packet_stats_.find(PACKET_SENT_TO_NETWORK);
637 if (sent_it == packet_stats_.end())
638 return;
639 PacketStatsMap::const_iterator retransmitted_it =
640 packet_stats_.find(PACKET_RETRANSMITTED);
641 int sent_count = sent_it->second.event_counter;
642 int retransmitted_count = 0;
643 if (retransmitted_it != packet_stats_.end())
644 retransmitted_count = retransmitted_it->second.event_counter;
645 double packet_loss_fraction = static_cast<double>(retransmitted_count) /
646 (sent_count + retransmitted_count);
647 stats_map->insert(
648 std::make_pair(PACKET_LOSS_FRACTION, packet_loss_fraction));
649 }
650
FrameLogStats()651 StatsEventSubscriber::FrameLogStats::FrameLogStats()
652 : event_counter(0), sum_size(0) {}
~FrameLogStats()653 StatsEventSubscriber::FrameLogStats::~FrameLogStats() {}
654
PacketLogStats()655 StatsEventSubscriber::PacketLogStats::PacketLogStats()
656 : event_counter(0), sum_size(0) {}
~PacketLogStats()657 StatsEventSubscriber::PacketLogStats::~PacketLogStats() {}
658
FrameInfo()659 StatsEventSubscriber::FrameInfo::FrameInfo() : encoded(false) {
660 }
~FrameInfo()661 StatsEventSubscriber::FrameInfo::~FrameInfo() {
662 }
663
664 } // namespace cast
665 } // namespace media
666