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

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: Created 6 years, 8 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) {
hubbe 2014/04/15 00:23:22 Ouch, this level of repetition hurts my brain.
imcheng 2014/04/15 01:18:36 I can't think of much that can be done here. This
miu 2014/04/15 21:29:43 Suggestion (you probably want to put this in the h
imcheng 2014/04/17 19:19:06 Honestly I am not a huge fan of macros in a situat
miu 2014/04/18 00:02:03 Thinking about this some more, the real problem is
imcheng 2014/04/18 18:20:27 Yes, it is unfortunate that we have split event ty
26 if (media_type == AUDIO_EVENT)
27 return kAudioFrameCaptured;
28 else
29 return kVideoFrameCaptured;
30 }
31
32 CastLoggingEvent GetEncodedEvent(EventMediaType media_type) {
33 if (media_type == AUDIO_EVENT)
34 return kAudioFrameEncoded;
35 else
36 return kVideoFrameEncoded;
37 }
38
39 CastLoggingEvent GetDecodedEvent(EventMediaType media_type) {
40 if (media_type == AUDIO_EVENT)
41 return kAudioFrameDecoded;
42 else
43 return kVideoFrameDecoded;
44 }
45
46 CastLoggingEvent GetPlayoutEvent(EventMediaType media_type) {
47 if (media_type == AUDIO_EVENT)
48 return kAudioPlayoutDelay;
49 else
50 return kVideoRenderDelay;
51 }
52
53 CastLoggingEvent GetPacketSentEvent(EventMediaType media_type) {
54 if (media_type == AUDIO_EVENT)
55 return kAudioPacketSentToNetwork;
56 else
57 return kVideoPacketSentToNetwork;
58 }
59
60 CastLoggingEvent GetPacketReceivedEvent(EventMediaType media_type) {
61 if (media_type == AUDIO_EVENT)
62 return kAudioPacketReceived;
63 else
64 return kVideoPacketReceived;
65 }
66
67 CastLoggingEvent GetPacketRetransmittedEvent(EventMediaType media_type) {
68 if (media_type == AUDIO_EVENT)
69 return kAudioPacketRetransmitted;
70 else
71 return kVideoPacketRetransmitted;
72 }
73
74 } // namespace
75
76 StatsEventSubscriber::StatsEventSubscriber(
77 EventMediaType event_media_type,
78 base::TickClock* clock,
79 ReceiverTimeOffsetEstimator* offset_estimator)
80 : event_media_type_(event_media_type),
81 total_network_latency_ms_(0),
82 network_latency_datapoints_(0),
83 total_e2e_latency_ms_(0),
84 e2e_latency_datapoints_(0),
85 start_time_(clock->NowTicks()),
86 clock_(clock),
87 offset_estimator_(offset_estimator) {
88 DCHECK(event_media_type == AUDIO_EVENT || event_media_type == VIDEO_EVENT);
89 }
14 90
15 StatsEventSubscriber::~StatsEventSubscriber() { 91 StatsEventSubscriber::~StatsEventSubscriber() {
16 DCHECK(thread_checker_.CalledOnValidThread()); 92 DCHECK(thread_checker_.CalledOnValidThread());
17 } 93 }
18 94
19 void StatsEventSubscriber::OnReceiveFrameEvent(const FrameEvent& frame_event) { 95 void StatsEventSubscriber::OnReceiveFrameEvent(const FrameEvent& frame_event) {
20 DCHECK(thread_checker_.CalledOnValidThread()); 96 DCHECK(thread_checker_.CalledOnValidThread());
21 97
22 CastLoggingEvent type = frame_event.type; 98 CastLoggingEvent type = frame_event.type;
23 if (GetEventMediaType(type) != event_media_type_) 99 if (GetEventMediaType(type) != event_media_type_)
24 return; 100 return;
25 101
26 FrameStatsMap::iterator it = frame_stats_.find(type); 102 FrameStatsMap::iterator it = frame_stats_.find(type);
27 if (it == frame_stats_.end()) { 103 if (it == frame_stats_.end()) {
28 FrameLogStats stats; 104 FrameLogStats stats;
29 stats.first_event_time = frame_event.timestamp;
30 stats.last_event_time = frame_event.timestamp;
31 stats.event_counter = 1; 105 stats.event_counter = 1;
32 stats.sum_size = frame_event.size; 106 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; 107 stats.sum_delay = frame_event.delay_delta;
36 frame_stats_.insert(std::make_pair(type, stats)); 108 frame_stats_.insert(std::make_pair(type, stats));
37 } else { 109 } else {
38 ++(it->second.event_counter); 110 ++(it->second.event_counter);
39 it->second.last_event_time = frame_event.timestamp;
40 it->second.sum_size += frame_event.size; 111 it->second.sum_size += frame_event.size;
41 it->second.sum_delay += frame_event.delay_delta; 112 it->second.sum_delay += frame_event.delay_delta;
42 if (frame_event.delay_delta > it->second.max_delay) 113 }
43 it->second.max_delay = frame_event.delay_delta; 114
44 if (frame_event.delay_delta < it->second.min_delay) 115 if (type == GetCapturedEvent(event_media_type_)) {
45 it->second.min_delay = frame_event.delay_delta; 116 RecordFrameCapturedTime(frame_event);
117 } else if (type == GetPlayoutEvent(event_media_type_)) {
118 RecordE2ELatency(frame_event);
46 } 119 }
47 } 120 }
48 121
49 void StatsEventSubscriber::OnReceivePacketEvent( 122 void StatsEventSubscriber::OnReceivePacketEvent(
50 const PacketEvent& packet_event) { 123 const PacketEvent& packet_event) {
51 DCHECK(thread_checker_.CalledOnValidThread()); 124 DCHECK(thread_checker_.CalledOnValidThread());
52 125
53 CastLoggingEvent type = packet_event.type; 126 CastLoggingEvent type = packet_event.type;
54 if (GetEventMediaType(type) != event_media_type_) 127 if (GetEventMediaType(type) != event_media_type_)
55 return; 128 return;
56 129
57 PacketStatsMap::iterator it = packet_stats_.find(type); 130 PacketStatsMap::iterator it = packet_stats_.find(type);
58 if (it == packet_stats_.end()) { 131 if (it == packet_stats_.end()) {
59 PacketLogStats stats; 132 PacketLogStats stats;
60 stats.first_event_time = packet_event.timestamp;
61 stats.last_event_time = packet_event.timestamp;
62 stats.event_counter = 1; 133 stats.event_counter = 1;
63 stats.sum_size = packet_event.size; 134 stats.sum_size = packet_event.size;
64 packet_stats_.insert(std::make_pair(type, stats)); 135 packet_stats_.insert(std::make_pair(type, stats));
65 } else { 136 } else {
66 it->second.last_event_time = packet_event.timestamp;
67 ++(it->second.event_counter); 137 ++(it->second.event_counter);
68 it->second.sum_size += packet_event.size; 138 it->second.sum_size += packet_event.size;
69 } 139 }
140
141 if (type == GetPacketSentEvent(event_media_type_)) {
142 RecordPacketSentTime(packet_event);
143 } else if (type == GetPacketRetransmittedEvent(event_media_type_)) {
144 // We only measure network latency using packets that doesn't have to be
145 // retransmitted as there is precisely one sent-receive timestamp pairs.
146 ErasePacketSentTime(packet_event);
147 } else if (type == GetPacketReceivedEvent(event_media_type_)) {
148 RecordNetworkLatency(packet_event);
149 }
70 } 150 }
71 151
72 void StatsEventSubscriber::OnReceiveGenericEvent( 152 void StatsEventSubscriber::OnReceiveGenericEvent(
73 const GenericEvent& generic_event) { 153 const GenericEvent& generic_event) {
74 DCHECK(thread_checker_.CalledOnValidThread()); 154 DCHECK(thread_checker_.CalledOnValidThread());
75 155 // Not interested in generic events.
76 CastLoggingEvent type = generic_event.type; 156 }
77 if (GetEventMediaType(type) != event_media_type_) 157
78 return; 158 scoped_ptr<base::DictionaryValue> StatsEventSubscriber::GetStats() const {
79 159 StatsMap stats_map;
80 GenericStatsMap::iterator it = generic_stats_.find(type); 160 GetStatsInternal(&stats_map);
81 if (it == generic_stats_.end()) { 161 scoped_ptr<base::DictionaryValue> ret(new base::DictionaryValue);
82 GenericLogStats stats; 162
83 stats.first_event_time = generic_event.timestamp; 163 scoped_ptr<base::DictionaryValue> stats(new base::DictionaryValue);
84 stats.last_event_time = generic_event.timestamp; 164 for (StatsMap::const_iterator it = stats_map.begin(); it != stats_map.end();
85 stats.event_counter = 1; 165 ++it) {
86 stats.sum = generic_event.value; 166 stats->SetDouble(CastStatToString(it->first), it->second);
87 stats.sum_squared = generic_event.value * generic_event.value; 167 }
88 stats.min = generic_event.value; 168
89 stats.max = generic_event.value; 169 ret->Set(event_media_type_ == AUDIO_EVENT ? "audio" : "video",
90 generic_stats_.insert(std::make_pair(type, stats)); 170 stats.release());
91 } else { 171
92 it->second.last_event_time = generic_event.timestamp; 172 return ret.Pass();
93 ++(it->second.event_counter);
94 it->second.sum += generic_event.value;
95 it->second.sum_squared += generic_event.value * generic_event.value;
96 if (it->second.min > generic_event.value) {
97 it->second.min = generic_event.value;
98 } else if (it->second.max < generic_event.value) {
99 it->second.max = generic_event.value;
100 }
101 }
102 }
103
104 void StatsEventSubscriber::GetFrameStats(FrameStatsMap* frame_stats_map) const {
105 DCHECK(thread_checker_.CalledOnValidThread());
106 DCHECK(frame_stats_map);
107
108 frame_stats_map->clear();
109 frame_stats_map->insert(frame_stats_.begin(), frame_stats_.end());
110 }
111
112 void StatsEventSubscriber::GetPacketStats(
113 PacketStatsMap* packet_stats_map) const {
114 DCHECK(thread_checker_.CalledOnValidThread());
115 DCHECK(packet_stats_map);
116
117 packet_stats_map->clear();
118 packet_stats_map->insert(packet_stats_.begin(), packet_stats_.end());
119 }
120
121 void StatsEventSubscriber::GetGenericStats(
122 GenericStatsMap* generic_stats_map) const {
123 DCHECK(thread_checker_.CalledOnValidThread());
124 DCHECK(generic_stats_map);
125
126 generic_stats_map->clear();
127 generic_stats_map->insert(generic_stats_.begin(), generic_stats_.end());
128 } 173 }
129 174
130 void StatsEventSubscriber::Reset() { 175 void StatsEventSubscriber::Reset() {
131 DCHECK(thread_checker_.CalledOnValidThread()); 176 DCHECK(thread_checker_.CalledOnValidThread());
132 177
133 frame_stats_.clear(); 178 frame_stats_.clear();
134 packet_stats_.clear(); 179 packet_stats_.clear();
135 generic_stats_.clear(); 180 total_network_latency_ms_ = 0;
136 } 181 network_latency_datapoints_ = 0;
182 total_e2e_latency_ms_ = 0;
183 e2e_latency_datapoints_ = 0;
184 frame_captured_times_.clear();
185 packet_sent_times_.clear();
186 start_time_ = clock_->NowTicks();
187 }
188
189 // static
190 const char* StatsEventSubscriber::CastStatToString(CastStat stat) {
191 switch (stat) {
192 STAT_ENUM_TO_STRING(CAPTURE_FPS);
193 STAT_ENUM_TO_STRING(ENCODE_FPS);
194 STAT_ENUM_TO_STRING(DECODE_FPS);
195 STAT_ENUM_TO_STRING(AVG_ENCODE_TIME_MS);
196 STAT_ENUM_TO_STRING(AVG_PLAYOUT_DELAY_MS);
197 STAT_ENUM_TO_STRING(AVG_NETWORK_LATENCY_MS);
198 STAT_ENUM_TO_STRING(AVG_E2E_LATENCY_MS);
199 STAT_ENUM_TO_STRING(ENCODE_KBPS);
200 STAT_ENUM_TO_STRING(TRANSMISSION_KBPS);
201 STAT_ENUM_TO_STRING(RETRANSMISSION_KBPS);
202 STAT_ENUM_TO_STRING(PACKET_LOSS_PERCENTAGE);
203 }
204 NOTREACHED();
205 return "";
206 }
207
208 void StatsEventSubscriber::GetStatsInternal(StatsMap* stats_map) const {
209 DCHECK(thread_checker_.CalledOnValidThread());
210
211 stats_map->clear();
212
213 base::TimeTicks end_time = clock_->NowTicks();
214
215 PopulateFpsStat(
216 end_time, GetCapturedEvent(event_media_type_), CAPTURE_FPS, stats_map);
217 PopulateFpsStat(
218 end_time, GetEncodedEvent(event_media_type_), ENCODE_FPS, stats_map);
219 PopulateFpsStat(
220 end_time, GetDecodedEvent(event_media_type_), DECODE_FPS, stats_map);
221 PopulatePlayoutDelayStat(stats_map);
222 PopulateFrameBitrateStat(end_time, stats_map);
223 PopulatePacketBitrateStat(end_time,
224 GetPacketSentEvent(event_media_type_),
225 TRANSMISSION_KBPS,
226 stats_map);
227 PopulatePacketBitrateStat(end_time,
228 GetPacketRetransmittedEvent(event_media_type_),
229 RETRANSMISSION_KBPS,
230 stats_map);
231 PopulatePacketLossPercentageStat(stats_map);
232
233 if (network_latency_datapoints_ > 0) {
234 double avg_network_latency_ms =
235 static_cast<double>(total_network_latency_ms_) /
236 network_latency_datapoints_;
237 stats_map->insert(
238 std::make_pair(AVG_NETWORK_LATENCY_MS, avg_network_latency_ms));
239 }
240
241 if (e2e_latency_datapoints_ > 0) {
242 double avg_e2e_latency_ms =
243 static_cast<double>(total_e2e_latency_ms_) / e2e_latency_datapoints_;
244 stats_map->insert(std::make_pair(AVG_E2E_LATENCY_MS, avg_e2e_latency_ms));
245 }
246 }
247
248 bool StatsEventSubscriber::GetReceiverOffset(base::TimeDelta* offset) {
249 base::TimeDelta receiver_offset_lower_bound;
250 base::TimeDelta receiver_offset_upper_bound;
251 if (!offset_estimator_->GetReceiverOffsetBounds(
252 &receiver_offset_lower_bound, &receiver_offset_upper_bound)) {
253 return false;
254 }
255
256 *offset = (receiver_offset_lower_bound + receiver_offset_upper_bound) / 2;
257 return true;
258 }
259
260 void StatsEventSubscriber::RecordFrameCapturedTime(
261 const FrameEvent& frame_event) {
262 frame_captured_times_.insert(
263 std::make_pair(frame_event.rtp_timestamp, frame_event.timestamp));
264 if (frame_captured_times_.size() > kMaxFrameEventTimeMapSize)
265 frame_captured_times_.erase(frame_captured_times_.begin());
266 }
267
268 void StatsEventSubscriber::RecordE2ELatency(const FrameEvent& frame_event) {
269 base::TimeDelta receiver_offset;
270 if (!GetReceiverOffset(&receiver_offset))
271 return;
272
273 FrameEventTimeMap::iterator it =
274 frame_captured_times_.find(frame_event.rtp_timestamp);
275 if (it == frame_captured_times_.end())
276 return;
277
278 // Playout time is event time + playout delay.
279 base::TimeTicks playout_time =
280 frame_event.timestamp + frame_event.delay_delta - receiver_offset;
281 int64 e2e_latency_ms = (playout_time - it->second).InMilliseconds();
282
283 // Bogus value - probably because the offset value has not stabilized enough
284 // yet.
285 if (e2e_latency_ms <= 0)
286 return;
287
288 total_e2e_latency_ms_ += e2e_latency_ms;
289 e2e_latency_datapoints_++;
290 }
291
292 void StatsEventSubscriber::RecordPacketSentTime(
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_.insert(std::make_pair(key, packet_event.timestamp));
297 if (packet_sent_times_.size() > kMaxPacketEventTimeMapSize)
298 packet_sent_times_.erase(packet_sent_times_.begin());
299 }
300
301 void StatsEventSubscriber::ErasePacketSentTime(
302 const PacketEvent& packet_event) {
303 std::pair<RtpTimestamp, uint16> key(
304 std::make_pair(packet_event.rtp_timestamp, packet_event.packet_id));
305 packet_sent_times_.erase(key);
306 }
307
308 void StatsEventSubscriber::RecordNetworkLatency(
309 const PacketEvent& packet_event) {
310 base::TimeDelta receiver_offset;
311 if (!GetReceiverOffset(&receiver_offset))
312 return;
313
314 base::TimeTicks packet_sent_time;
315 std::pair<RtpTimestamp, uint16> key(
316 std::make_pair(packet_event.rtp_timestamp, packet_event.packet_id));
317 PacketEventTimeMap::iterator it = packet_sent_times_.find(key);
318 if (it == packet_sent_times_.end())
319 return;
320 packet_sent_time = it->second;
321
322 // Subtract by offset.
323 base::TimeTicks packet_received_time =
324 packet_event.timestamp - receiver_offset;
325
326 int64 latency_ms = (packet_received_time - packet_sent_time).InMilliseconds();
327
328 // Bogus value - probably because the offset value has not stabilized enough
329 // yet.
330 if (latency_ms <= 0)
331 return;
332
333 total_network_latency_ms_ += latency_ms;
334 network_latency_datapoints_++;
335 }
336
337 void StatsEventSubscriber::PopulateFpsStat(base::TimeTicks end_time,
338 CastLoggingEvent event,
339 CastStat stat,
340 StatsMap* stats_map) const {
341 FrameStatsMap::const_iterator it = frame_stats_.find(event);
342 if (it != frame_stats_.end()) {
343 double fps = 0.0;
344 int64 duration_ms = (end_time - start_time_).InMilliseconds();
345 int count = it->second.event_counter;
346 if (duration_ms != 0 && count != 0)
347 fps = static_cast<double>(count) / duration_ms * 1000;
348 stats_map->insert(std::make_pair(stat, fps));
349 }
350 }
351
352 void StatsEventSubscriber::PopulatePlayoutDelayStat(StatsMap* stats_map) const {
353 CastLoggingEvent event = GetPlayoutEvent(event_media_type_);
354 FrameStatsMap::const_iterator it = frame_stats_.find(event);
355 if (it != frame_stats_.end()) {
356 double avg_delay_ms = 0.0;
357 int64 sum_delay_ms = it->second.sum_delay.InMilliseconds();
358 int count = it->second.event_counter;
359 if (sum_delay_ms != 0 && count != 0)
360 avg_delay_ms = static_cast<double>(sum_delay_ms) / count;
361 stats_map->insert(std::make_pair(AVG_PLAYOUT_DELAY_MS, avg_delay_ms));
362 }
363 }
364
365 void StatsEventSubscriber::PopulateFrameBitrateStat(base::TimeTicks end_time,
366 StatsMap* stats_map) const {
367 CastLoggingEvent event = GetEncodedEvent(event_media_type_);
368 FrameStatsMap::const_iterator it = frame_stats_.find(event);
369 if (it != frame_stats_.end()) {
370 double kbps = 0;
371 int64 duration_ms = (end_time - start_time_).InMilliseconds();
372 if (it->second.sum_size != 0 && duration_ms != 0) {
373 kbps = static_cast<double>(it->second.sum_size) / duration_ms * 8;
374 }
375
376 stats_map->insert(std::make_pair(ENCODE_KBPS, kbps));
377 }
378 }
379
380 void StatsEventSubscriber::PopulatePacketBitrateStat(
381 base::TimeTicks end_time,
382 CastLoggingEvent event,
383 CastStat stat,
384 StatsMap* stats_map) const {
385 PacketStatsMap::const_iterator it = packet_stats_.find(event);
386 if (it != packet_stats_.end()) {
387 double kbps = 0;
388 int64 duration_ms = (end_time - start_time_).InMilliseconds();
389 if (it->second.sum_size != 0 && duration_ms != 0) {
390 kbps = static_cast<double>(it->second.sum_size) / duration_ms * 8;
391 }
392
393 stats_map->insert(std::make_pair(stat, kbps));
394 }
395 }
396
397 void StatsEventSubscriber::PopulatePacketLossPercentageStat(
398 StatsMap* stats_map) const {
399 // We assume that retransmission means that the packet's previous
400 // (re)transmission was lost.
401 // This means the percentage of packet loss is
402 // (# of retransmit events) / (# of transmit + retransmit events).
403 CastLoggingEvent packet_sent_event = GetPacketSentEvent(event_media_type_);
404 CastLoggingEvent packet_retransmitted_event =
405 GetPacketRetransmittedEvent(event_media_type_);
406 PacketStatsMap::const_iterator sent_it =
407 packet_stats_.find(packet_sent_event);
408 if (sent_it == packet_stats_.end())
409 return;
410 PacketStatsMap::const_iterator retransmitted_it =
411 packet_stats_.find(packet_retransmitted_event);
412 int sent_count = sent_it->second.event_counter;
413 int retransmitted_count = 0;
414 if (retransmitted_it != packet_stats_.end())
415 retransmitted_count = retransmitted_it->second.event_counter;
416 double packet_loss_percentage = static_cast<double>(retransmitted_count) /
417 (sent_count + retransmitted_count) * 100;
418 stats_map->insert(
419 std::make_pair(PACKET_LOSS_PERCENTAGE, packet_loss_percentage));
420 }
421
422 StatsEventSubscriber::FrameLogStats::FrameLogStats()
423 : event_counter(0), sum_size(0) {}
424 StatsEventSubscriber::FrameLogStats::~FrameLogStats() {}
425
426 StatsEventSubscriber::PacketLogStats::PacketLogStats()
427 : event_counter(0), sum_size(0) {}
428 StatsEventSubscriber::PacketLogStats::~PacketLogStats() {}
137 429
138 } // namespace cast 430 } // namespace cast
139 } // namespace media 431 } // namespace media
OLDNEW

Powered by Google App Engine
This is Rietveld 408576698