Chromium Code Reviews
chromiumcodereview-hr@appspot.gserviceaccount.com (chromiumcodereview-hr) | Please choose your nickname with Settings | Help | Chromium Project | Gerrit Changes | Sign out
(313)

Side by Side Diff: media/cast/logging/stats_event_subscriber.cc

Issue 236123003: Cast: Provide more meaningful stats. (Closed) Base URL: https://chromium.googlesource.com/chromium/src.git@master
Patch Set: fix compile Created 6 years, 7 months ago
Use n/p to move between diff chunks; N/P to move between comments. Draft comments are only viewable by you.
Jump to:
View unified diff | Download patch
OLDNEW
1 // Copyright 2014 The Chromium Authors. All rights reserved. 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 2 // Use of this source code is governed by a BSD-style license that can be
3 // found in the LICENSE file. 3 // found in the LICENSE file.
4 4
5 #include "media/cast/logging/stats_event_subscriber.h" 5 #include "media/cast/logging/stats_event_subscriber.h"
6 6
7 #include "base/logging.h" 7 #include "base/logging.h"
8 #include "base/values.h"
9
10 #define STAT_ENUM_TO_STRING(enum) \
11 case enum: \
12 return #enum
8 13
9 namespace media { 14 namespace media {
10 namespace cast { 15 namespace cast {
11 16
12 StatsEventSubscriber::StatsEventSubscriber(EventMediaType event_media_type) 17 namespace {
13 : event_media_type_(event_media_type) {} 18
19 using media::cast::CastLoggingEvent;
20 using media::cast::EventMediaType;
21
22 const size_t kMaxFrameEventTimeMapSize = 100;
23 const size_t kMaxPacketEventTimeMapSize = 1000;
24
25 CastLoggingEvent GetCapturedEvent(EventMediaType media_type) {
26 return media_type == AUDIO_EVENT ? kAudioFrameCaptured : kVideoFrameCaptured;
27 }
28
29 CastLoggingEvent GetEncodedEvent(EventMediaType media_type) {
30 return media_type == AUDIO_EVENT ? kAudioFrameEncoded : kVideoFrameEncoded;
31 }
32
33 CastLoggingEvent GetDecodedEvent(EventMediaType media_type) {
34 return media_type == AUDIO_EVENT ? kAudioFrameDecoded : kVideoFrameDecoded;
35 }
36
37 CastLoggingEvent GetPlayoutEvent(EventMediaType media_type) {
38 return media_type == AUDIO_EVENT ? kAudioPlayoutDelay : kVideoRenderDelay;
39 }
40
41 CastLoggingEvent GetPacketSentEvent(EventMediaType media_type) {
42 return media_type == AUDIO_EVENT ? kAudioPacketSentToNetwork :
43 kVideoPacketSentToNetwork;
44 }
45
46 CastLoggingEvent GetPacketReceivedEvent(EventMediaType media_type) {
47 return media_type == AUDIO_EVENT ? kAudioPacketReceived :
48 kVideoPacketReceived;
49 }
50
51 CastLoggingEvent GetPacketRetransmittedEvent(EventMediaType media_type) {
52 return media_type == AUDIO_EVENT ? kAudioPacketRetransmitted :
53 kVideoPacketRetransmitted;
54 }
55
56 bool IsReceiverEvent(CastLoggingEvent event) {
57 return event == kAudioFrameDecoded
58 || event == kVideoFrameDecoded
59 || event == kAudioPlayoutDelay
60 || event == kVideoRenderDelay
61 || event == kAudioAckSent
62 || event == kVideoAckSent
63 || event == kAudioPacketReceived
64 || event == kVideoPacketReceived
65 || event == kDuplicateAudioPacketReceived
66 || event == kDuplicateVideoPacketReceived;
67 }
68
69 } // namespace
70
71 StatsEventSubscriber::StatsEventSubscriber(
72 EventMediaType event_media_type,
73 base::TickClock* clock,
74 ReceiverTimeOffsetEstimator* offset_estimator)
75 : event_media_type_(event_media_type),
76 clock_(clock),
77 offset_estimator_(offset_estimator),
78 network_latency_datapoints_(0),
79 e2e_latency_datapoints_(0) {
80 DCHECK(event_media_type == AUDIO_EVENT || event_media_type == VIDEO_EVENT);
81 base::TimeTicks now = clock_->NowTicks();
82 start_time_ = now;
83 last_response_received_time_ = base::TimeTicks();
84 }
14 85
15 StatsEventSubscriber::~StatsEventSubscriber() { 86 StatsEventSubscriber::~StatsEventSubscriber() {
16 DCHECK(thread_checker_.CalledOnValidThread()); 87 DCHECK(thread_checker_.CalledOnValidThread());
17 } 88 }
18 89
19 void StatsEventSubscriber::OnReceiveFrameEvent(const FrameEvent& frame_event) { 90 void StatsEventSubscriber::OnReceiveFrameEvent(const FrameEvent& frame_event) {
20 DCHECK(thread_checker_.CalledOnValidThread()); 91 DCHECK(thread_checker_.CalledOnValidThread());
21 92
22 CastLoggingEvent type = frame_event.type; 93 CastLoggingEvent type = frame_event.type;
23 if (GetEventMediaType(type) != event_media_type_) 94 if (GetEventMediaType(type) != event_media_type_)
24 return; 95 return;
25 96
26 FrameStatsMap::iterator it = frame_stats_.find(type); 97 FrameStatsMap::iterator it = frame_stats_.find(type);
27 if (it == frame_stats_.end()) { 98 if (it == frame_stats_.end()) {
28 FrameLogStats stats; 99 FrameLogStats stats;
29 stats.first_event_time = frame_event.timestamp;
30 stats.last_event_time = frame_event.timestamp;
31 stats.event_counter = 1; 100 stats.event_counter = 1;
32 stats.sum_size = frame_event.size; 101 stats.sum_size = frame_event.size;
33 stats.min_delay = frame_event.delay_delta;
34 stats.max_delay = frame_event.delay_delta;
35 stats.sum_delay = frame_event.delay_delta; 102 stats.sum_delay = frame_event.delay_delta;
36 frame_stats_.insert(std::make_pair(type, stats)); 103 frame_stats_.insert(std::make_pair(type, stats));
37 } else { 104 } else {
38 ++(it->second.event_counter); 105 ++(it->second.event_counter);
39 it->second.last_event_time = frame_event.timestamp;
40 it->second.sum_size += frame_event.size; 106 it->second.sum_size += frame_event.size;
41 it->second.sum_delay += frame_event.delay_delta; 107 it->second.sum_delay += frame_event.delay_delta;
42 if (frame_event.delay_delta > it->second.max_delay)
43 it->second.max_delay = frame_event.delay_delta;
44 if (frame_event.delay_delta < it->second.min_delay)
45 it->second.min_delay = frame_event.delay_delta;
46 } 108 }
109
110 if (type == GetCapturedEvent(event_media_type_)) {
111 RecordFrameCapturedTime(frame_event);
112 } else if (type == GetPlayoutEvent(event_media_type_)) {
113 RecordE2ELatency(frame_event);
114 }
115
116 if (IsReceiverEvent(type))
117 UpdateLastResponseTime(frame_event.timestamp);
47 } 118 }
48 119
49 void StatsEventSubscriber::OnReceivePacketEvent( 120 void StatsEventSubscriber::OnReceivePacketEvent(
50 const PacketEvent& packet_event) { 121 const PacketEvent& packet_event) {
51 DCHECK(thread_checker_.CalledOnValidThread()); 122 DCHECK(thread_checker_.CalledOnValidThread());
52 123
53 CastLoggingEvent type = packet_event.type; 124 CastLoggingEvent type = packet_event.type;
54 if (GetEventMediaType(type) != event_media_type_) 125 if (GetEventMediaType(type) != event_media_type_)
55 return; 126 return;
56 127
57 PacketStatsMap::iterator it = packet_stats_.find(type); 128 PacketStatsMap::iterator it = packet_stats_.find(type);
58 if (it == packet_stats_.end()) { 129 if (it == packet_stats_.end()) {
59 PacketLogStats stats; 130 PacketLogStats stats;
60 stats.first_event_time = packet_event.timestamp;
61 stats.last_event_time = packet_event.timestamp;
62 stats.event_counter = 1; 131 stats.event_counter = 1;
63 stats.sum_size = packet_event.size; 132 stats.sum_size = packet_event.size;
64 packet_stats_.insert(std::make_pair(type, stats)); 133 packet_stats_.insert(std::make_pair(type, stats));
65 } else { 134 } else {
66 it->second.last_event_time = packet_event.timestamp;
67 ++(it->second.event_counter); 135 ++(it->second.event_counter);
68 it->second.sum_size += packet_event.size; 136 it->second.sum_size += packet_event.size;
69 } 137 }
138
139 if (type == GetPacketSentEvent(event_media_type_) ||
140 type == GetPacketReceivedEvent(event_media_type_)) {
141 RecordNetworkLatency(packet_event);
142 } else if (type == GetPacketRetransmittedEvent(event_media_type_)) {
143 // We only measure network latency using packets that doesn't have to be
144 // retransmitted as there is precisely one sent-receive timestamp pairs.
145 ErasePacketSentTime(packet_event);
146 }
147
148 if (IsReceiverEvent(type))
149 UpdateLastResponseTime(packet_event.timestamp);
70 } 150 }
71 151
72 void StatsEventSubscriber::GetFrameStats(FrameStatsMap* frame_stats_map) const { 152 scoped_ptr<base::DictionaryValue> StatsEventSubscriber::GetStats() const {
73 DCHECK(thread_checker_.CalledOnValidThread()); 153 StatsMap stats_map;
74 DCHECK(frame_stats_map); 154 GetStatsInternal(&stats_map);
155 scoped_ptr<base::DictionaryValue> ret(new base::DictionaryValue);
75 156
76 frame_stats_map->clear(); 157 scoped_ptr<base::DictionaryValue> stats(new base::DictionaryValue);
77 frame_stats_map->insert(frame_stats_.begin(), frame_stats_.end()); 158 for (StatsMap::const_iterator it = stats_map.begin(); it != stats_map.end();
78 } 159 ++it) {
160 stats->SetDouble(CastStatToString(it->first), it->second);
161 }
79 162
80 void StatsEventSubscriber::GetPacketStats( 163 ret->Set(event_media_type_ == AUDIO_EVENT ? "audio" : "video",
81 PacketStatsMap* packet_stats_map) const { 164 stats.release());
82 DCHECK(thread_checker_.CalledOnValidThread());
83 DCHECK(packet_stats_map);
84 165
85 packet_stats_map->clear(); 166 return ret.Pass();
86 packet_stats_map->insert(packet_stats_.begin(), packet_stats_.end());
87 } 167 }
88 168
89 void StatsEventSubscriber::Reset() { 169 void StatsEventSubscriber::Reset() {
90 DCHECK(thread_checker_.CalledOnValidThread()); 170 DCHECK(thread_checker_.CalledOnValidThread());
91 171
92 frame_stats_.clear(); 172 frame_stats_.clear();
93 packet_stats_.clear(); 173 packet_stats_.clear();
94 } 174 total_network_latency_ = base::TimeDelta();
175 network_latency_datapoints_ = 0;
176 total_e2e_latency_ = base::TimeDelta();
177 e2e_latency_datapoints_ = 0;
178 frame_captured_times_.clear();
179 packet_sent_times_.clear();
180 start_time_ = clock_->NowTicks();
181 last_response_received_time_ = base::TimeTicks();
182 }
183
184 // static
185 const char* StatsEventSubscriber::CastStatToString(CastStat stat) {
186 switch (stat) {
187 STAT_ENUM_TO_STRING(CAPTURE_FPS);
188 STAT_ENUM_TO_STRING(ENCODE_FPS);
189 STAT_ENUM_TO_STRING(DECODE_FPS);
190 STAT_ENUM_TO_STRING(AVG_ENCODE_TIME_MS);
191 STAT_ENUM_TO_STRING(AVG_PLAYOUT_DELAY_MS);
192 STAT_ENUM_TO_STRING(AVG_NETWORK_LATENCY_MS);
193 STAT_ENUM_TO_STRING(AVG_E2E_LATENCY_MS);
194 STAT_ENUM_TO_STRING(ENCODE_KBPS);
195 STAT_ENUM_TO_STRING(TRANSMISSION_KBPS);
196 STAT_ENUM_TO_STRING(RETRANSMISSION_KBPS);
197 STAT_ENUM_TO_STRING(PACKET_LOSS_FRACTION);
198 STAT_ENUM_TO_STRING(MS_SINCE_LAST_RECEIVER_RESPONSE);
199 }
200 NOTREACHED();
201 return "";
202 }
203
204 void StatsEventSubscriber::GetStatsInternal(StatsMap* stats_map) const {
205 DCHECK(thread_checker_.CalledOnValidThread());
206
207 stats_map->clear();
208
209 base::TimeTicks end_time = clock_->NowTicks();
210
211 PopulateFpsStat(
212 end_time, GetCapturedEvent(event_media_type_), CAPTURE_FPS, stats_map);
213 PopulateFpsStat(
214 end_time, GetEncodedEvent(event_media_type_), ENCODE_FPS, stats_map);
215 PopulateFpsStat(
216 end_time, GetDecodedEvent(event_media_type_), DECODE_FPS, stats_map);
217 PopulatePlayoutDelayStat(stats_map);
218 PopulateFrameBitrateStat(end_time, stats_map);
219 PopulatePacketBitrateStat(end_time,
220 GetPacketSentEvent(event_media_type_),
221 TRANSMISSION_KBPS,
222 stats_map);
223 PopulatePacketBitrateStat(end_time,
224 GetPacketRetransmittedEvent(event_media_type_),
225 RETRANSMISSION_KBPS,
226 stats_map);
227 PopulatePacketLossPercentageStat(stats_map);
228
229 if (network_latency_datapoints_ > 0) {
230 double avg_network_latency_ms =
231 total_network_latency_.InMillisecondsF() /
232 network_latency_datapoints_;
233 stats_map->insert(
234 std::make_pair(AVG_NETWORK_LATENCY_MS, avg_network_latency_ms));
235 }
236
237 if (e2e_latency_datapoints_ > 0) {
238 double avg_e2e_latency_ms =
239 total_e2e_latency_.InMillisecondsF() / e2e_latency_datapoints_;
240 stats_map->insert(std::make_pair(AVG_E2E_LATENCY_MS, avg_e2e_latency_ms));
241 }
242
243 if (!last_response_received_time_.is_null()) {
244 stats_map->insert(
245 std::make_pair(MS_SINCE_LAST_RECEIVER_RESPONSE,
246 (end_time - last_response_received_time_).InMillisecondsF()));
247 }
248 }
249
250 bool StatsEventSubscriber::GetReceiverOffset(base::TimeDelta* offset) {
251 base::TimeDelta receiver_offset_lower_bound;
252 base::TimeDelta receiver_offset_upper_bound;
253 if (!offset_estimator_->GetReceiverOffsetBounds(
254 &receiver_offset_lower_bound, &receiver_offset_upper_bound)) {
255 return false;
256 }
257
258 *offset = (receiver_offset_lower_bound + receiver_offset_upper_bound) / 2;
259 return true;
260 }
261
262 void StatsEventSubscriber::RecordFrameCapturedTime(
263 const FrameEvent& frame_event) {
264 frame_captured_times_.insert(
265 std::make_pair(frame_event.rtp_timestamp, frame_event.timestamp));
266 if (frame_captured_times_.size() > kMaxFrameEventTimeMapSize)
267 frame_captured_times_.erase(frame_captured_times_.begin());
268 }
269
270 void StatsEventSubscriber::RecordE2ELatency(const FrameEvent& frame_event) {
271 base::TimeDelta receiver_offset;
272 if (!GetReceiverOffset(&receiver_offset))
273 return;
274
275 FrameEventTimeMap::iterator it =
276 frame_captured_times_.find(frame_event.rtp_timestamp);
277 if (it == frame_captured_times_.end())
278 return;
279
280 // Playout time is event time + playout delay.
281 base::TimeTicks playout_time =
282 frame_event.timestamp + frame_event.delay_delta - receiver_offset;
283 total_e2e_latency_ += playout_time - it->second;
284 e2e_latency_datapoints_++;
285 }
286
287 void StatsEventSubscriber::UpdateLastResponseTime(
288 base::TimeTicks receiver_time) {
289 base::TimeDelta receiver_offset;
290 if (!GetReceiverOffset(&receiver_offset))
291 return;
292 base::TimeTicks sender_time = receiver_time - receiver_offset;
293 last_response_received_time_ = sender_time;
294 }
295
296 void StatsEventSubscriber::ErasePacketSentTime(
297 const PacketEvent& packet_event) {
298 std::pair<RtpTimestamp, uint16> key(
299 std::make_pair(packet_event.rtp_timestamp, packet_event.packet_id));
300 packet_sent_times_.erase(key);
301 }
302
303 void StatsEventSubscriber::RecordNetworkLatency(
304 const PacketEvent& packet_event) {
305 base::TimeDelta receiver_offset;
306 if (!GetReceiverOffset(&receiver_offset))
307 return;
308
309 std::pair<RtpTimestamp, uint16> key(
310 std::make_pair(packet_event.rtp_timestamp, packet_event.packet_id));
311 PacketEventTimeMap::iterator it = packet_sent_times_.find(key);
312 if (it == packet_sent_times_.end()) {
313 std::pair<RtpTimestamp, uint16> key(
314 std::make_pair(packet_event.rtp_timestamp, packet_event.packet_id));
315 std::pair<base::TimeTicks, CastLoggingEvent> value =
316 std::make_pair(packet_event.timestamp, packet_event.type);
317 packet_sent_times_.insert(std::make_pair(key, value));
318 if (packet_sent_times_.size() > kMaxPacketEventTimeMapSize)
319 packet_sent_times_.erase(packet_sent_times_.begin());
320 } else {
321 std::pair<base::TimeTicks, CastLoggingEvent> value = it->second;
322 CastLoggingEvent recorded_type = value.second;
323 bool match = false;
324 base::TimeTicks packet_sent_time;
325 base::TimeTicks packet_received_time;
326 if (recorded_type == GetPacketSentEvent(event_media_type_) &&
327 packet_event.type == GetPacketReceivedEvent(event_media_type_)) {
328 packet_sent_time = value.first;
329 packet_received_time = packet_event.timestamp;
330 match = true;
331 } else if (recorded_type == GetPacketReceivedEvent(event_media_type_) &&
332 packet_event.type == GetPacketSentEvent(event_media_type_)) {
333 packet_sent_time = packet_event.timestamp;
334 packet_received_time = value.first;
335 match = true;
336 }
337 if (match) {
338 // Subtract by offset.
339 packet_received_time -= receiver_offset;
340
341 total_network_latency_ += packet_received_time - packet_sent_time;
342 network_latency_datapoints_++;
343 packet_sent_times_.erase(it);
344 }
345 }
346 }
347
348 void StatsEventSubscriber::PopulateFpsStat(base::TimeTicks end_time,
349 CastLoggingEvent event,
350 CastStat stat,
351 StatsMap* stats_map) const {
352 FrameStatsMap::const_iterator it = frame_stats_.find(event);
353 if (it != frame_stats_.end()) {
354 double fps = 0.0;
355 base::TimeDelta duration = (end_time - start_time_);
356 int count = it->second.event_counter;
357 if (duration > base::TimeDelta())
358 fps = count / duration.InSecondsF();
359 stats_map->insert(std::make_pair(stat, fps));
360 }
361 }
362
363 void StatsEventSubscriber::PopulatePlayoutDelayStat(StatsMap* stats_map) const {
364 CastLoggingEvent event = GetPlayoutEvent(event_media_type_);
365 FrameStatsMap::const_iterator it = frame_stats_.find(event);
366 if (it != frame_stats_.end()) {
367 double avg_delay_ms = 0.0;
368 base::TimeDelta sum_delay = it->second.sum_delay;
369 int count = it->second.event_counter;
370 if (count != 0)
371 avg_delay_ms = sum_delay.InMillisecondsF() / count;
372 stats_map->insert(std::make_pair(AVG_PLAYOUT_DELAY_MS, avg_delay_ms));
373 }
374 }
375
376 void StatsEventSubscriber::PopulateFrameBitrateStat(base::TimeTicks end_time,
377 StatsMap* stats_map) const {
378 CastLoggingEvent event = GetEncodedEvent(event_media_type_);
379 FrameStatsMap::const_iterator it = frame_stats_.find(event);
380 if (it != frame_stats_.end()) {
381 double kbps = 0.0;
382 base::TimeDelta duration = end_time - start_time_;
383 if (duration > base::TimeDelta()) {
384 kbps = it->second.sum_size / duration.InMillisecondsF() * 8;
385 }
386
387 stats_map->insert(std::make_pair(ENCODE_KBPS, kbps));
388 }
389 }
390
391 void StatsEventSubscriber::PopulatePacketBitrateStat(
392 base::TimeTicks end_time,
393 CastLoggingEvent event,
394 CastStat stat,
395 StatsMap* stats_map) const {
396 PacketStatsMap::const_iterator it = packet_stats_.find(event);
397 if (it != packet_stats_.end()) {
398 double kbps = 0;
399 base::TimeDelta duration = end_time - start_time_;
400 if (duration > base::TimeDelta()) {
401 kbps = it->second.sum_size / duration.InMillisecondsF() * 8;
402 }
403
404 stats_map->insert(std::make_pair(stat, kbps));
405 }
406 }
407
408 void StatsEventSubscriber::PopulatePacketLossPercentageStat(
409 StatsMap* stats_map) const {
410 // We assume that retransmission means that the packet's previous
411 // (re)transmission was lost.
412 // This means the percentage of packet loss is
413 // (# of retransmit events) / (# of transmit + retransmit events).
414 CastLoggingEvent packet_sent_event = GetPacketSentEvent(event_media_type_);
415 CastLoggingEvent packet_retransmitted_event =
416 GetPacketRetransmittedEvent(event_media_type_);
417 PacketStatsMap::const_iterator sent_it =
418 packet_stats_.find(packet_sent_event);
419 if (sent_it == packet_stats_.end())
420 return;
421 PacketStatsMap::const_iterator retransmitted_it =
422 packet_stats_.find(packet_retransmitted_event);
423 int sent_count = sent_it->second.event_counter;
424 int retransmitted_count = 0;
425 if (retransmitted_it != packet_stats_.end())
426 retransmitted_count = retransmitted_it->second.event_counter;
427 double packet_loss_fraction = static_cast<double>(retransmitted_count) /
428 (sent_count + retransmitted_count);
429 stats_map->insert(
430 std::make_pair(PACKET_LOSS_FRACTION, packet_loss_fraction));
431 }
432
433 StatsEventSubscriber::FrameLogStats::FrameLogStats()
434 : event_counter(0), sum_size(0) {}
435 StatsEventSubscriber::FrameLogStats::~FrameLogStats() {}
436
437 StatsEventSubscriber::PacketLogStats::PacketLogStats()
438 : event_counter(0), sum_size(0) {}
439 StatsEventSubscriber::PacketLogStats::~PacketLogStats() {}
95 440
96 } // namespace cast 441 } // namespace cast
97 } // namespace media 442 } // namespace media
OLDNEW
« no previous file with comments | « media/cast/logging/stats_event_subscriber.h ('k') | media/cast/logging/stats_event_subscriber_unittest.cc » ('j') | no next file with comments »

Powered by Google App Engine
This is Rietveld 408576698