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.
7 #include "media/cast/logging/stats_event_subscriber.h"
9 #include "base/format_macros.h"
10 #include "base/logging.h"
11 #include "base/strings/stringprintf.h"
12 #include "base/values.h"
14 #define STAT_ENUM_TO_STRING(enum) \
23 using media::cast::CastLoggingEvent;
24 using media::cast::EventMediaType;
26 const size_t kMaxPacketEventTimeMapSize = 1000;
28 bool IsReceiverEvent(CastLoggingEvent event) {
29 return event == FRAME_DECODED
30 || event == FRAME_PLAYOUT
31 || event == FRAME_ACK_SENT
32 || event == PACKET_RECEIVED;
37 StatsEventSubscriber::SimpleHistogram::SimpleHistogram(int64 min,
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_);
45 StatsEventSubscriber::SimpleHistogram::~SimpleHistogram() {
48 void StatsEventSubscriber::SimpleHistogram::Add(int64 sample) {
51 } else if (sample >= max_) {
54 size_t index = 1 + (sample - min_) / width_;
55 DCHECK_LT(index, buckets_.size());
60 void StatsEventSubscriber::SimpleHistogram::Reset() {
61 buckets_.assign(buckets_.size(), 0);
64 scoped_ptr<base::ListValue>
65 StatsEventSubscriber::SimpleHistogram::GetHistogram() const {
66 scoped_ptr<base::ListValue> histo(new base::ListValue);
68 scoped_ptr<base::DictionaryValue> bucket(new base::DictionaryValue);
70 if (buckets_.front()) {
71 bucket->SetInteger(base::StringPrintf("<%" PRId64, min_),
73 histo->Append(bucket.release());
76 for (size_t i = 1; i < buckets_.size() - 1; i++) {
79 bucket.reset(new base::DictionaryValue);
80 int64 lower = min_ + (i - 1) * width_;
81 int64 upper = lower + width_ - 1;
83 base::StringPrintf("%" PRId64 "-%" PRId64, lower, upper),
85 histo->Append(bucket.release());
88 if (buckets_.back()) {
89 bucket.reset(new base::DictionaryValue);
90 bucket->SetInteger(base::StringPrintf(">=%" PRId64, max_),
92 histo->Append(bucket.release());
97 StatsEventSubscriber::StatsEventSubscriber(
98 EventMediaType event_media_type,
99 base::TickClock* clock,
100 ReceiverTimeOffsetEstimator* offset_estimator)
101 : event_media_type_(event_media_type),
103 offset_estimator_(offset_estimator),
104 network_latency_datapoints_(0),
105 e2e_latency_datapoints_(0),
106 num_frames_dropped_by_encoder_(0),
108 start_time_(clock_->NowTicks()) {
109 DCHECK(event_media_type == AUDIO_EVENT || event_media_type == VIDEO_EVENT);
114 StatsEventSubscriber::~StatsEventSubscriber() {
115 DCHECK(thread_checker_.CalledOnValidThread());
118 void StatsEventSubscriber::OnReceiveFrameEvent(const FrameEvent& frame_event) {
119 DCHECK(thread_checker_.CalledOnValidThread());
121 CastLoggingEvent type = frame_event.type;
122 if (frame_event.media_type != event_media_type_)
125 FrameStatsMap::iterator it = frame_stats_.find(type);
126 if (it == frame_stats_.end()) {
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));
133 ++(it->second.event_counter);
134 it->second.sum_size += frame_event.size;
135 it->second.sum_delay += frame_event.delay_delta;
138 bool is_receiver_event = IsReceiverEvent(type);
139 UpdateFirstLastEventTime(frame_event.timestamp, is_receiver_event);
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())
157 if (is_receiver_event)
158 UpdateLastResponseTime(frame_event.timestamp);
161 void StatsEventSubscriber::OnReceivePacketEvent(
162 const PacketEvent& packet_event) {
163 DCHECK(thread_checker_.CalledOnValidThread());
165 CastLoggingEvent type = packet_event.type;
166 if (packet_event.media_type != event_media_type_)
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));
176 ++(it->second.event_counter);
177 it->second.sum_size += packet_event.size;
180 bool is_receiver_event = IsReceiverEvent(type);
181 UpdateFirstLastEventTime(packet_event.timestamp, is_receiver_event);
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);
192 if (is_receiver_event)
193 UpdateLastResponseTime(packet_event.timestamp);
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))
202 timestamp -= receiver_offset;
205 if (first_event_time_.is_null()) {
206 first_event_time_ = timestamp;
208 first_event_time_ = std::min(first_event_time_, timestamp);
210 if (last_event_time_.is_null()) {
211 last_event_time_ = timestamp;
213 last_event_time_ = std::max(last_event_time_, timestamp);
217 scoped_ptr<base::DictionaryValue> StatsEventSubscriber::GetStats() const {
219 GetStatsInternal(&stats_map);
220 scoped_ptr<base::DictionaryValue> ret(new base::DictionaryValue);
222 scoped_ptr<base::DictionaryValue> stats(new base::DictionaryValue);
223 for (StatsMap::const_iterator it = stats_map.begin(); it != stats_map.end();
225 // Round to 3 digits after the decimal point.
226 stats->SetDouble(CastStatToString(it->first),
227 round(it->second * 1000.0) / 1000.0);
229 for (HistogramMap::const_iterator it = histograms_.begin();
230 it != histograms_.end();
232 stats->Set(CastStatToString(it->first),
233 it->second->GetHistogram().release());
236 ret->Set(event_media_type_ == AUDIO_EVENT ? "audio" : "video",
242 void StatsEventSubscriber::Reset() {
243 DCHECK(thread_checker_.CalledOnValidThread());
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();
262 first_event_time_ = base::TimeTicks();
263 last_event_time_ = base::TimeTicks();
267 const char* StatsEventSubscriber::CastStatToString(CastStat 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);
299 const int kMaxLatencyBucketMs = 800;
300 const int kBucketWidthMs = 20;
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));
315 void StatsEventSubscriber::GetStatsInternal(StatsMap* stats_map) const {
316 DCHECK(thread_checker_.CalledOnValidThread());
320 base::TimeTicks end_time = clock_->NowTicks();
323 end_time, FRAME_CAPTURE_BEGIN, CAPTURE_FPS, stats_map);
325 end_time, FRAME_ENCODED, ENCODE_FPS, stats_map);
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,
334 PopulatePacketBitrateStat(end_time,
335 PACKET_RETRANSMITTED,
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);
346 if (network_latency_datapoints_ > 0) {
347 double avg_network_latency_ms =
348 total_network_latency_.InMillisecondsF() /
349 network_latency_datapoints_;
351 std::make_pair(AVG_NETWORK_LATENCY_MS, avg_network_latency_ms));
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));
360 if (!last_response_received_time_.is_null()) {
362 std::make_pair(MS_SINCE_LAST_RECEIVER_RESPONSE,
363 (end_time - last_response_received_time_).InMillisecondsF()));
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(
372 (first_event_time_ - base::TimeTicks::UnixEpoch()).InMillisecondsF()));
374 if (!last_event_time_.is_null()) {
375 stats_map->insert(std::make_pair(
377 (last_event_time_ - base::TimeTicks::UnixEpoch()).InMillisecondsF()));
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)) {
389 *offset = (receiver_offset_lower_bound + receiver_offset_upper_bound) / 2;
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) {
402 recent_frame_infos_.insert(std::make_pair(rtp_timestamp, frame_info));
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);
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);
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())
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);
431 it->second.capture_end_time = frame_event.timestamp;
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);
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);
450 it->second.encode_time = frame_event.timestamp;
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())
459 if (it->second.encode_time.is_null())
462 base::TimeDelta receiver_offset;
463 if (!GetReceiverOffset(&receiver_offset))
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);
472 void StatsEventSubscriber::RecordE2ELatency(const FrameEvent& frame_event) {
473 base::TimeDelta receiver_offset;
474 if (!GetReceiverOffset(&receiver_offset))
477 FrameInfoMap::iterator it =
478 recent_frame_infos_.find(frame_event.rtp_timestamp);
479 if (it == recent_frame_infos_.end())
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_++;
489 void StatsEventSubscriber::UpdateLastResponseTime(
490 base::TimeTicks receiver_time) {
491 base::TimeDelta receiver_offset;
492 if (!GetReceiverOffset(&receiver_offset))
494 base::TimeTicks sender_time = receiver_time - receiver_offset;
495 last_response_received_time_ = sender_time;
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);
505 void StatsEventSubscriber::RecordNetworkLatency(
506 const PacketEvent& packet_event) {
507 base::TimeDelta receiver_offset;
508 if (!GetReceiverOffset(&receiver_offset))
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());
521 std::pair<base::TimeTicks, CastLoggingEvent> value = it->second;
522 CastLoggingEvent recorded_type = value.second;
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;
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;
538 // Subtract by offset.
539 packet_received_time -= receiver_offset;
540 base::TimeDelta latency_delta = packet_received_time - packet_sent_time;
542 total_network_latency_ += latency_delta;
543 network_latency_datapoints_++;
545 histograms_[PACKET_LATENCY_MS_HISTO]->Add(
546 latency_delta.InMillisecondsF());
548 packet_sent_times_.erase(it);
553 void StatsEventSubscriber::PopulateFpsStat(base::TimeTicks end_time,
554 CastLoggingEvent event,
556 StatsMap* stats_map) const {
557 FrameStatsMap::const_iterator it = frame_stats_.find(event);
558 if (it != frame_stats_.end()) {
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));
568 void StatsEventSubscriber::PopulateFrameCountStat(CastLoggingEvent event,
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));
577 void StatsEventSubscriber::PopulatePacketCountStat(CastLoggingEvent event,
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));
586 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;
593 avg_delay_ms = sum_delay.InMillisecondsF() / count;
594 stats_map->insert(std::make_pair(AVG_PLAYOUT_DELAY_MS, avg_delay_ms));
598 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()) {
603 base::TimeDelta duration = end_time - start_time_;
604 if (duration > base::TimeDelta()) {
605 kbps = it->second.sum_size / duration.InMillisecondsF() * 8;
608 stats_map->insert(std::make_pair(ENCODE_KBPS, kbps));
612 void StatsEventSubscriber::PopulatePacketBitrateStat(
613 base::TimeTicks end_time,
614 CastLoggingEvent event,
616 StatsMap* stats_map) const {
617 PacketStatsMap::const_iterator it = packet_stats_.find(event);
618 if (it != packet_stats_.end()) {
620 base::TimeDelta duration = end_time - start_time_;
621 if (duration > base::TimeDelta()) {
622 kbps = it->second.sum_size / duration.InMillisecondsF() * 8;
625 stats_map->insert(std::make_pair(stat, kbps));
629 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())
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);
648 std::make_pair(PACKET_LOSS_FRACTION, packet_loss_fraction));
651 StatsEventSubscriber::FrameLogStats::FrameLogStats()
652 : event_counter(0), sum_size(0) {}
653 StatsEventSubscriber::FrameLogStats::~FrameLogStats() {}
655 StatsEventSubscriber::PacketLogStats::PacketLogStats()
656 : event_counter(0), sum_size(0) {}
657 StatsEventSubscriber::PacketLogStats::~PacketLogStats() {}
659 StatsEventSubscriber::FrameInfo::FrameInfo() : encoded(false) {
661 StatsEventSubscriber::FrameInfo::~FrameInfo() {