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 kMaxFrameEventTimeMapSize
= 100;
23 const size_t kMaxPacketEventTimeMapSize
= 1000;
25 CastLoggingEvent
GetCapturedEvent(EventMediaType media_type
) {
26 return media_type
== AUDIO_EVENT
?
27 kAudioFrameCaptureBegin
: kVideoFrameCaptureBegin
;
30 CastLoggingEvent
GetEncodedEvent(EventMediaType media_type
) {
31 return media_type
== AUDIO_EVENT
? kAudioFrameEncoded
: kVideoFrameEncoded
;
34 CastLoggingEvent
GetDecodedEvent(EventMediaType media_type
) {
35 return media_type
== AUDIO_EVENT
? kAudioFrameDecoded
: kVideoFrameDecoded
;
38 CastLoggingEvent
GetPlayoutEvent(EventMediaType media_type
) {
39 return media_type
== AUDIO_EVENT
? kAudioPlayoutDelay
: kVideoRenderDelay
;
42 CastLoggingEvent
GetPacketSentEvent(EventMediaType media_type
) {
43 return media_type
== AUDIO_EVENT
? kAudioPacketSentToNetwork
:
44 kVideoPacketSentToNetwork
;
47 CastLoggingEvent
GetPacketReceivedEvent(EventMediaType media_type
) {
48 return media_type
== AUDIO_EVENT
? kAudioPacketReceived
:
52 CastLoggingEvent
GetPacketRetransmittedEvent(EventMediaType media_type
) {
53 return media_type
== AUDIO_EVENT
? kAudioPacketRetransmitted
:
54 kVideoPacketRetransmitted
;
57 bool IsReceiverEvent(CastLoggingEvent event
) {
58 return event
== kAudioFrameDecoded
59 || event
== kVideoFrameDecoded
60 || event
== kAudioPlayoutDelay
61 || event
== kVideoRenderDelay
62 || event
== kAudioAckSent
63 || event
== kVideoAckSent
64 || event
== kAudioPacketReceived
65 || event
== kVideoPacketReceived
66 || event
== kDuplicateAudioPacketReceived
67 || event
== kDuplicateVideoPacketReceived
;
72 StatsEventSubscriber::StatsEventSubscriber(
73 EventMediaType event_media_type
,
74 base::TickClock
* clock
,
75 ReceiverTimeOffsetEstimator
* offset_estimator
)
76 : event_media_type_(event_media_type
),
78 offset_estimator_(offset_estimator
),
79 network_latency_datapoints_(0),
80 e2e_latency_datapoints_(0) {
81 DCHECK(event_media_type
== AUDIO_EVENT
|| event_media_type
== VIDEO_EVENT
);
82 base::TimeTicks now
= clock_
->NowTicks();
84 last_response_received_time_
= base::TimeTicks();
87 StatsEventSubscriber::~StatsEventSubscriber() {
88 DCHECK(thread_checker_
.CalledOnValidThread());
91 void StatsEventSubscriber::OnReceiveFrameEvent(const FrameEvent
& frame_event
) {
92 DCHECK(thread_checker_
.CalledOnValidThread());
94 CastLoggingEvent type
= frame_event
.type
;
95 if (GetEventMediaType(type
) != event_media_type_
)
98 FrameStatsMap::iterator it
= frame_stats_
.find(type
);
99 if (it
== frame_stats_
.end()) {
101 stats
.event_counter
= 1;
102 stats
.sum_size
= frame_event
.size
;
103 stats
.sum_delay
= frame_event
.delay_delta
;
104 frame_stats_
.insert(std::make_pair(type
, stats
));
106 ++(it
->second
.event_counter
);
107 it
->second
.sum_size
+= frame_event
.size
;
108 it
->second
.sum_delay
+= frame_event
.delay_delta
;
111 if (type
== GetCapturedEvent(event_media_type_
)) {
112 RecordFrameCapturedTime(frame_event
);
113 } else if (type
== GetPlayoutEvent(event_media_type_
)) {
114 RecordE2ELatency(frame_event
);
117 if (IsReceiverEvent(type
))
118 UpdateLastResponseTime(frame_event
.timestamp
);
121 void StatsEventSubscriber::OnReceivePacketEvent(
122 const PacketEvent
& packet_event
) {
123 DCHECK(thread_checker_
.CalledOnValidThread());
125 CastLoggingEvent type
= packet_event
.type
;
126 if (GetEventMediaType(type
) != event_media_type_
)
129 PacketStatsMap::iterator it
= packet_stats_
.find(type
);
130 if (it
== packet_stats_
.end()) {
131 PacketLogStats stats
;
132 stats
.event_counter
= 1;
133 stats
.sum_size
= packet_event
.size
;
134 packet_stats_
.insert(std::make_pair(type
, stats
));
136 ++(it
->second
.event_counter
);
137 it
->second
.sum_size
+= packet_event
.size
;
140 if (type
== GetPacketSentEvent(event_media_type_
) ||
141 type
== GetPacketReceivedEvent(event_media_type_
)) {
142 RecordNetworkLatency(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
);
149 if (IsReceiverEvent(type
))
150 UpdateLastResponseTime(packet_event
.timestamp
);
153 scoped_ptr
<base::DictionaryValue
> StatsEventSubscriber::GetStats() const {
155 GetStatsInternal(&stats_map
);
156 scoped_ptr
<base::DictionaryValue
> ret(new base::DictionaryValue
);
158 scoped_ptr
<base::DictionaryValue
> stats(new base::DictionaryValue
);
159 for (StatsMap::const_iterator it
= stats_map
.begin(); it
!= stats_map
.end();
161 stats
->SetDouble(CastStatToString(it
->first
), it
->second
);
164 ret
->Set(event_media_type_
== AUDIO_EVENT
? "audio" : "video",
170 void StatsEventSubscriber::Reset() {
171 DCHECK(thread_checker_
.CalledOnValidThread());
173 frame_stats_
.clear();
174 packet_stats_
.clear();
175 total_network_latency_
= base::TimeDelta();
176 network_latency_datapoints_
= 0;
177 total_e2e_latency_
= base::TimeDelta();
178 e2e_latency_datapoints_
= 0;
179 frame_captured_times_
.clear();
180 packet_sent_times_
.clear();
181 start_time_
= clock_
->NowTicks();
182 last_response_received_time_
= base::TimeTicks();
186 const char* StatsEventSubscriber::CastStatToString(CastStat stat
) {
188 STAT_ENUM_TO_STRING(CAPTURE_FPS
);
189 STAT_ENUM_TO_STRING(ENCODE_FPS
);
190 STAT_ENUM_TO_STRING(DECODE_FPS
);
191 STAT_ENUM_TO_STRING(AVG_ENCODE_TIME_MS
);
192 STAT_ENUM_TO_STRING(AVG_PLAYOUT_DELAY_MS
);
193 STAT_ENUM_TO_STRING(AVG_NETWORK_LATENCY_MS
);
194 STAT_ENUM_TO_STRING(AVG_E2E_LATENCY_MS
);
195 STAT_ENUM_TO_STRING(ENCODE_KBPS
);
196 STAT_ENUM_TO_STRING(TRANSMISSION_KBPS
);
197 STAT_ENUM_TO_STRING(RETRANSMISSION_KBPS
);
198 STAT_ENUM_TO_STRING(PACKET_LOSS_FRACTION
);
199 STAT_ENUM_TO_STRING(MS_SINCE_LAST_RECEIVER_RESPONSE
);
205 void StatsEventSubscriber::GetStatsInternal(StatsMap
* stats_map
) const {
206 DCHECK(thread_checker_
.CalledOnValidThread());
210 base::TimeTicks end_time
= clock_
->NowTicks();
213 end_time
, GetCapturedEvent(event_media_type_
), CAPTURE_FPS
, stats_map
);
215 end_time
, GetEncodedEvent(event_media_type_
), ENCODE_FPS
, stats_map
);
217 end_time
, GetDecodedEvent(event_media_type_
), DECODE_FPS
, stats_map
);
218 PopulatePlayoutDelayStat(stats_map
);
219 PopulateFrameBitrateStat(end_time
, stats_map
);
220 PopulatePacketBitrateStat(end_time
,
221 GetPacketSentEvent(event_media_type_
),
224 PopulatePacketBitrateStat(end_time
,
225 GetPacketRetransmittedEvent(event_media_type_
),
228 PopulatePacketLossPercentageStat(stats_map
);
230 if (network_latency_datapoints_
> 0) {
231 double avg_network_latency_ms
=
232 total_network_latency_
.InMillisecondsF() /
233 network_latency_datapoints_
;
235 std::make_pair(AVG_NETWORK_LATENCY_MS
, avg_network_latency_ms
));
238 if (e2e_latency_datapoints_
> 0) {
239 double avg_e2e_latency_ms
=
240 total_e2e_latency_
.InMillisecondsF() / e2e_latency_datapoints_
;
241 stats_map
->insert(std::make_pair(AVG_E2E_LATENCY_MS
, avg_e2e_latency_ms
));
244 if (!last_response_received_time_
.is_null()) {
246 std::make_pair(MS_SINCE_LAST_RECEIVER_RESPONSE
,
247 (end_time
- last_response_received_time_
).InMillisecondsF()));
251 bool StatsEventSubscriber::GetReceiverOffset(base::TimeDelta
* offset
) {
252 base::TimeDelta receiver_offset_lower_bound
;
253 base::TimeDelta receiver_offset_upper_bound
;
254 if (!offset_estimator_
->GetReceiverOffsetBounds(
255 &receiver_offset_lower_bound
, &receiver_offset_upper_bound
)) {
259 *offset
= (receiver_offset_lower_bound
+ receiver_offset_upper_bound
) / 2;
263 void StatsEventSubscriber::RecordFrameCapturedTime(
264 const FrameEvent
& frame_event
) {
265 frame_captured_times_
.insert(
266 std::make_pair(frame_event
.rtp_timestamp
, frame_event
.timestamp
));
267 if (frame_captured_times_
.size() > kMaxFrameEventTimeMapSize
)
268 frame_captured_times_
.erase(frame_captured_times_
.begin());
271 void StatsEventSubscriber::RecordE2ELatency(const FrameEvent
& frame_event
) {
272 base::TimeDelta receiver_offset
;
273 if (!GetReceiverOffset(&receiver_offset
))
276 FrameEventTimeMap::iterator it
=
277 frame_captured_times_
.find(frame_event
.rtp_timestamp
);
278 if (it
== frame_captured_times_
.end())
281 // Playout time is event time + playout delay.
282 base::TimeTicks playout_time
=
283 frame_event
.timestamp
+ frame_event
.delay_delta
- receiver_offset
;
284 total_e2e_latency_
+= playout_time
- it
->second
;
285 e2e_latency_datapoints_
++;
288 void StatsEventSubscriber::UpdateLastResponseTime(
289 base::TimeTicks receiver_time
) {
290 base::TimeDelta receiver_offset
;
291 if (!GetReceiverOffset(&receiver_offset
))
293 base::TimeTicks sender_time
= receiver_time
- receiver_offset
;
294 last_response_received_time_
= sender_time
;
297 void StatsEventSubscriber::ErasePacketSentTime(
298 const PacketEvent
& packet_event
) {
299 std::pair
<RtpTimestamp
, uint16
> key(
300 std::make_pair(packet_event
.rtp_timestamp
, packet_event
.packet_id
));
301 packet_sent_times_
.erase(key
);
304 void StatsEventSubscriber::RecordNetworkLatency(
305 const PacketEvent
& packet_event
) {
306 base::TimeDelta receiver_offset
;
307 if (!GetReceiverOffset(&receiver_offset
))
310 std::pair
<RtpTimestamp
, uint16
> key(
311 std::make_pair(packet_event
.rtp_timestamp
, packet_event
.packet_id
));
312 PacketEventTimeMap::iterator it
= packet_sent_times_
.find(key
);
313 if (it
== packet_sent_times_
.end()) {
314 std::pair
<RtpTimestamp
, uint16
> key(
315 std::make_pair(packet_event
.rtp_timestamp
, packet_event
.packet_id
));
316 std::pair
<base::TimeTicks
, CastLoggingEvent
> value
=
317 std::make_pair(packet_event
.timestamp
, packet_event
.type
);
318 packet_sent_times_
.insert(std::make_pair(key
, value
));
319 if (packet_sent_times_
.size() > kMaxPacketEventTimeMapSize
)
320 packet_sent_times_
.erase(packet_sent_times_
.begin());
322 std::pair
<base::TimeTicks
, CastLoggingEvent
> value
= it
->second
;
323 CastLoggingEvent recorded_type
= value
.second
;
325 base::TimeTicks packet_sent_time
;
326 base::TimeTicks packet_received_time
;
327 if (recorded_type
== GetPacketSentEvent(event_media_type_
) &&
328 packet_event
.type
== GetPacketReceivedEvent(event_media_type_
)) {
329 packet_sent_time
= value
.first
;
330 packet_received_time
= packet_event
.timestamp
;
332 } else if (recorded_type
== GetPacketReceivedEvent(event_media_type_
) &&
333 packet_event
.type
== GetPacketSentEvent(event_media_type_
)) {
334 packet_sent_time
= packet_event
.timestamp
;
335 packet_received_time
= value
.first
;
339 // Subtract by offset.
340 packet_received_time
-= receiver_offset
;
342 total_network_latency_
+= packet_received_time
- packet_sent_time
;
343 network_latency_datapoints_
++;
344 packet_sent_times_
.erase(it
);
349 void StatsEventSubscriber::PopulateFpsStat(base::TimeTicks end_time
,
350 CastLoggingEvent event
,
352 StatsMap
* stats_map
) const {
353 FrameStatsMap::const_iterator it
= frame_stats_
.find(event
);
354 if (it
!= frame_stats_
.end()) {
356 base::TimeDelta duration
= (end_time
- start_time_
);
357 int count
= it
->second
.event_counter
;
358 if (duration
> base::TimeDelta())
359 fps
= count
/ duration
.InSecondsF();
360 stats_map
->insert(std::make_pair(stat
, fps
));
364 void StatsEventSubscriber::PopulatePlayoutDelayStat(StatsMap
* stats_map
) const {
365 CastLoggingEvent event
= GetPlayoutEvent(event_media_type_
);
366 FrameStatsMap::const_iterator it
= frame_stats_
.find(event
);
367 if (it
!= frame_stats_
.end()) {
368 double avg_delay_ms
= 0.0;
369 base::TimeDelta sum_delay
= it
->second
.sum_delay
;
370 int count
= it
->second
.event_counter
;
372 avg_delay_ms
= sum_delay
.InMillisecondsF() / count
;
373 stats_map
->insert(std::make_pair(AVG_PLAYOUT_DELAY_MS
, avg_delay_ms
));
377 void StatsEventSubscriber::PopulateFrameBitrateStat(base::TimeTicks end_time
,
378 StatsMap
* stats_map
) const {
379 CastLoggingEvent event
= GetEncodedEvent(event_media_type_
);
380 FrameStatsMap::const_iterator it
= frame_stats_
.find(event
);
381 if (it
!= frame_stats_
.end()) {
383 base::TimeDelta duration
= end_time
- start_time_
;
384 if (duration
> base::TimeDelta()) {
385 kbps
= it
->second
.sum_size
/ duration
.InMillisecondsF() * 8;
388 stats_map
->insert(std::make_pair(ENCODE_KBPS
, kbps
));
392 void StatsEventSubscriber::PopulatePacketBitrateStat(
393 base::TimeTicks end_time
,
394 CastLoggingEvent event
,
396 StatsMap
* stats_map
) const {
397 PacketStatsMap::const_iterator it
= packet_stats_
.find(event
);
398 if (it
!= packet_stats_
.end()) {
400 base::TimeDelta duration
= end_time
- start_time_
;
401 if (duration
> base::TimeDelta()) {
402 kbps
= it
->second
.sum_size
/ duration
.InMillisecondsF() * 8;
405 stats_map
->insert(std::make_pair(stat
, kbps
));
409 void StatsEventSubscriber::PopulatePacketLossPercentageStat(
410 StatsMap
* stats_map
) const {
411 // We assume that retransmission means that the packet's previous
412 // (re)transmission was lost.
413 // This means the percentage of packet loss is
414 // (# of retransmit events) / (# of transmit + retransmit events).
415 CastLoggingEvent packet_sent_event
= GetPacketSentEvent(event_media_type_
);
416 CastLoggingEvent packet_retransmitted_event
=
417 GetPacketRetransmittedEvent(event_media_type_
);
418 PacketStatsMap::const_iterator sent_it
=
419 packet_stats_
.find(packet_sent_event
);
420 if (sent_it
== packet_stats_
.end())
422 PacketStatsMap::const_iterator retransmitted_it
=
423 packet_stats_
.find(packet_retransmitted_event
);
424 int sent_count
= sent_it
->second
.event_counter
;
425 int retransmitted_count
= 0;
426 if (retransmitted_it
!= packet_stats_
.end())
427 retransmitted_count
= retransmitted_it
->second
.event_counter
;
428 double packet_loss_fraction
= static_cast<double>(retransmitted_count
) /
429 (sent_count
+ retransmitted_count
);
431 std::make_pair(PACKET_LOSS_FRACTION
, packet_loss_fraction
));
434 StatsEventSubscriber::FrameLogStats::FrameLogStats()
435 : event_counter(0), sum_size(0) {}
436 StatsEventSubscriber::FrameLogStats::~FrameLogStats() {}
438 StatsEventSubscriber::PacketLogStats::PacketLogStats()
439 : event_counter(0), sum_size(0) {}
440 StatsEventSubscriber::PacketLogStats::~PacketLogStats() {}