summaryrefslogtreecommitdiffstats
path: root/media/cast/logging
diff options
context:
space:
mode:
authorimcheng@chromium.org <imcheng@chromium.org@0039d316-1c4b-4281-b951-d872f2087c98>2014-08-15 15:51:30 +0000
committerimcheng@chromium.org <imcheng@chromium.org@0039d316-1c4b-4281-b951-d872f2087c98>2014-08-15 15:52:34 +0000
commit2ed35a46c99dd0447e610872a8f16b185fd08c11 (patch)
treed354cc38a8a5bacc1f2fcd786346502964634310 /media/cast/logging
parentd70654fa450225425f667a5a0c427c9f00a5212b (diff)
downloadchromium_src-2ed35a46c99dd0447e610872a8f16b185fd08c11.zip
chromium_src-2ed35a46c99dd0447e610872a8f16b185fd08c11.tar.gz
chromium_src-2ed35a46c99dd0447e610872a8f16b185fd08c11.tar.bz2
Cast streaming logging: Log additional stats for the GetStats() API.
* Total frames captured * Total frames dropped * Total frames late * Total packet count * Total packet retransmission * Total packet retransmission rejected BUG=403921 Review URL: https://codereview.chromium.org/472203002 Cr-Commit-Position: refs/heads/master@{#289879} git-svn-id: svn://svn.chromium.org/chrome/trunk/src@289879 0039d316-1c4b-4281-b951-d872f2087c98
Diffstat (limited to 'media/cast/logging')
-rw-r--r--media/cast/logging/stats_event_subscriber.cc83
-rw-r--r--media/cast/logging/stats_event_subscriber.h47
-rw-r--r--media/cast/logging/stats_event_subscriber_unittest.cc91
3 files changed, 188 insertions, 33 deletions
diff --git a/media/cast/logging/stats_event_subscriber.cc b/media/cast/logging/stats_event_subscriber.cc
index 9e3226a..03c669c 100644
--- a/media/cast/logging/stats_event_subscriber.cc
+++ b/media/cast/logging/stats_event_subscriber.cc
@@ -19,7 +19,6 @@ namespace {
using media::cast::CastLoggingEvent;
using media::cast::EventMediaType;
-const size_t kMaxFrameEventTimeMapSize = 100;
const size_t kMaxPacketEventTimeMapSize = 1000;
bool IsReceiverEvent(CastLoggingEvent event) {
@@ -39,7 +38,9 @@ StatsEventSubscriber::StatsEventSubscriber(
clock_(clock),
offset_estimator_(offset_estimator),
network_latency_datapoints_(0),
- e2e_latency_datapoints_(0) {
+ e2e_latency_datapoints_(0),
+ num_frames_dropped_by_encoder_(0),
+ num_frames_late_(0) {
DCHECK(event_media_type == AUDIO_EVENT || event_media_type == VIDEO_EVENT);
base::TimeTicks now = clock_->NowTicks();
start_time_ = now;
@@ -71,9 +72,13 @@ void StatsEventSubscriber::OnReceiveFrameEvent(const FrameEvent& frame_event) {
}
if (type == FRAME_CAPTURE_BEGIN) {
- RecordFrameCapturedTime(frame_event);
+ RecordFrameCaptureTime(frame_event);
+ } else if (type == FRAME_ENCODED) {
+ MarkAsEncoded(frame_event.rtp_timestamp);
} else if (type == FRAME_PLAYOUT) {
RecordE2ELatency(frame_event);
+ if (frame_event.delay_delta <= base::TimeDelta())
+ num_frames_late_++;
}
if (IsReceiverEvent(type))
@@ -138,7 +143,9 @@ void StatsEventSubscriber::Reset() {
network_latency_datapoints_ = 0;
total_e2e_latency_ = base::TimeDelta();
e2e_latency_datapoints_ = 0;
- frame_captured_times_.clear();
+ num_frames_dropped_by_encoder_ = 0;
+ num_frames_late_ = 0;
+ recent_captured_frames_.clear();
packet_sent_times_.clear();
start_time_ = clock_->NowTicks();
last_response_received_time_ = base::TimeTicks();
@@ -159,6 +166,12 @@ const char* StatsEventSubscriber::CastStatToString(CastStat stat) {
STAT_ENUM_TO_STRING(RETRANSMISSION_KBPS);
STAT_ENUM_TO_STRING(PACKET_LOSS_FRACTION);
STAT_ENUM_TO_STRING(MS_SINCE_LAST_RECEIVER_RESPONSE);
+ STAT_ENUM_TO_STRING(NUM_FRAMES_CAPTURED);
+ STAT_ENUM_TO_STRING(NUM_FRAMES_DROPPED_BY_ENCODER);
+ STAT_ENUM_TO_STRING(NUM_FRAMES_LATE);
+ STAT_ENUM_TO_STRING(NUM_PACKETS_SENT);
+ STAT_ENUM_TO_STRING(NUM_PACKETS_RETRANSMITTED);
+ STAT_ENUM_TO_STRING(NUM_PACKETS_RTX_REJECTED);
}
NOTREACHED();
return "";
@@ -188,6 +201,12 @@ void StatsEventSubscriber::GetStatsInternal(StatsMap* stats_map) const {
RETRANSMISSION_KBPS,
stats_map);
PopulatePacketLossPercentageStat(stats_map);
+ PopulateFrameCountStat(FRAME_CAPTURE_END, NUM_FRAMES_CAPTURED, stats_map);
+ PopulatePacketCountStat(PACKET_SENT_TO_NETWORK, NUM_PACKETS_SENT, stats_map);
+ PopulatePacketCountStat(
+ PACKET_RETRANSMITTED, NUM_PACKETS_RETRANSMITTED, stats_map);
+ PopulatePacketCountStat(
+ PACKET_RTX_REJECTED, NUM_PACKETS_RTX_REJECTED, stats_map);
if (network_latency_datapoints_ > 0) {
double avg_network_latency_ms =
@@ -208,6 +227,10 @@ void StatsEventSubscriber::GetStatsInternal(StatsMap* stats_map) const {
std::make_pair(MS_SINCE_LAST_RECEIVER_RESPONSE,
(end_time - last_response_received_time_).InMillisecondsF()));
}
+
+ stats_map->insert(std::make_pair(NUM_FRAMES_DROPPED_BY_ENCODER,
+ num_frames_dropped_by_encoder_));
+ stats_map->insert(std::make_pair(NUM_FRAMES_LATE, num_frames_late_));
}
bool StatsEventSubscriber::GetReceiverOffset(base::TimeDelta* offset) {
@@ -222,12 +245,22 @@ bool StatsEventSubscriber::GetReceiverOffset(base::TimeDelta* offset) {
return true;
}
-void StatsEventSubscriber::RecordFrameCapturedTime(
+void StatsEventSubscriber::RecordFrameCaptureTime(
const FrameEvent& frame_event) {
- frame_captured_times_.insert(
- std::make_pair(frame_event.rtp_timestamp, frame_event.timestamp));
- if (frame_captured_times_.size() > kMaxFrameEventTimeMapSize)
- frame_captured_times_.erase(frame_captured_times_.begin());
+ recent_captured_frames_.insert(std::make_pair(
+ frame_event.rtp_timestamp, FrameInfo(frame_event.timestamp)));
+ if (recent_captured_frames_.size() > kMaxFrameInfoMapSize) {
+ FrameInfoMap::iterator erase_it = recent_captured_frames_.begin();
+ if (!erase_it->second.encoded)
+ num_frames_dropped_by_encoder_++;
+ recent_captured_frames_.erase(erase_it);
+ }
+}
+
+void StatsEventSubscriber::MarkAsEncoded(RtpTimestamp rtp_timestamp) {
+ FrameInfoMap::iterator it = recent_captured_frames_.find(rtp_timestamp);
+ if (it != recent_captured_frames_.end())
+ it->second.encoded = true;
}
void StatsEventSubscriber::RecordE2ELatency(const FrameEvent& frame_event) {
@@ -235,15 +268,15 @@ void StatsEventSubscriber::RecordE2ELatency(const FrameEvent& frame_event) {
if (!GetReceiverOffset(&receiver_offset))
return;
- FrameEventTimeMap::iterator it =
- frame_captured_times_.find(frame_event.rtp_timestamp);
- if (it == frame_captured_times_.end())
+ FrameInfoMap::iterator it =
+ recent_captured_frames_.find(frame_event.rtp_timestamp);
+ if (it == recent_captured_frames_.end())
return;
// Playout time is event time + playout delay.
base::TimeTicks playout_time =
frame_event.timestamp + frame_event.delay_delta - receiver_offset;
- total_e2e_latency_ += playout_time - it->second;
+ total_e2e_latency_ += playout_time - it->second.capture_time;
e2e_latency_datapoints_++;
}
@@ -323,6 +356,24 @@ void StatsEventSubscriber::PopulateFpsStat(base::TimeTicks end_time,
}
}
+void StatsEventSubscriber::PopulateFrameCountStat(CastLoggingEvent event,
+ CastStat stat,
+ StatsMap* stats_map) const {
+ FrameStatsMap::const_iterator it = frame_stats_.find(event);
+ if (it != frame_stats_.end()) {
+ stats_map->insert(std::make_pair(stat, it->second.event_counter));
+ }
+}
+
+void StatsEventSubscriber::PopulatePacketCountStat(CastLoggingEvent event,
+ CastStat stat,
+ StatsMap* stats_map) const {
+ PacketStatsMap::const_iterator it = packet_stats_.find(event);
+ if (it != packet_stats_.end()) {
+ stats_map->insert(std::make_pair(stat, it->second.event_counter));
+ }
+}
+
void StatsEventSubscriber::PopulatePlayoutDelayStat(StatsMap* stats_map) const {
FrameStatsMap::const_iterator it = frame_stats_.find(FRAME_PLAYOUT);
if (it != frame_stats_.end()) {
@@ -396,5 +447,11 @@ StatsEventSubscriber::PacketLogStats::PacketLogStats()
: event_counter(0), sum_size(0) {}
StatsEventSubscriber::PacketLogStats::~PacketLogStats() {}
+StatsEventSubscriber::FrameInfo::FrameInfo(base::TimeTicks capture_time)
+ : capture_time(capture_time), encoded(false) {
+}
+StatsEventSubscriber::FrameInfo::~FrameInfo() {
+}
+
} // namespace cast
} // namespace media
diff --git a/media/cast/logging/stats_event_subscriber.h b/media/cast/logging/stats_event_subscriber.h
index 173378a..06ceaca9 100644
--- a/media/cast/logging/stats_event_subscriber.h
+++ b/media/cast/logging/stats_event_subscriber.h
@@ -49,13 +49,15 @@ class StatsEventSubscriber : public RawEventSubscriber {
private:
friend class StatsEventSubscriberTest;
FRIEND_TEST_ALL_PREFIXES(StatsEventSubscriberTest, EmptyStats);
- FRIEND_TEST_ALL_PREFIXES(StatsEventSubscriberTest, Capture);
+ FRIEND_TEST_ALL_PREFIXES(StatsEventSubscriberTest, CaptureEncode);
FRIEND_TEST_ALL_PREFIXES(StatsEventSubscriberTest, Encode);
FRIEND_TEST_ALL_PREFIXES(StatsEventSubscriberTest, Decode);
FRIEND_TEST_ALL_PREFIXES(StatsEventSubscriberTest, PlayoutDelay);
FRIEND_TEST_ALL_PREFIXES(StatsEventSubscriberTest, E2ELatency);
FRIEND_TEST_ALL_PREFIXES(StatsEventSubscriberTest, Packets);
+ static const size_t kMaxFrameInfoMapSize = 100;
+
// Generic statistics given the raw data. More specific data (e.g. frame rate
// and bit rate) can be computed given the basic metrics.
// Some of the metrics will only be set when applicable, e.g. delay and size.
@@ -85,8 +87,7 @@ class StatsEventSubscriber : public RawEventSubscriber {
// TODO(imcheng): This stat is not populated yet because we do not have
// the time when encode started. Record it in FRAME_ENCODED event.
AVG_ENCODE_TIME_MS,
- // Average playout delay in milliseconds, with target delay already
- // accounted for. Ideally, every frame should have a playout delay of 0.
+ // Average playout delay in milliseconds.
AVG_PLAYOUT_DELAY_MS,
// Duration from when a packet is transmitted to when it is received.
// This measures latency from sender to receiver.
@@ -102,11 +103,31 @@ class StatsEventSubscriber : public RawEventSubscriber {
// Fraction of packet loss.
PACKET_LOSS_FRACTION,
// Duration in milliseconds since last receiver response.
- MS_SINCE_LAST_RECEIVER_RESPONSE
+ MS_SINCE_LAST_RECEIVER_RESPONSE,
+ // Number of frames captured.
+ NUM_FRAMES_CAPTURED,
+ // Number of frames dropped by encoder.
+ NUM_FRAMES_DROPPED_BY_ENCODER,
+ // Number of late frames.
+ NUM_FRAMES_LATE,
+ // Number of packets that were sent (not retransmitted).
+ NUM_PACKETS_SENT,
+ // Number of packets that were retransmitted.
+ NUM_PACKETS_RETRANSMITTED,
+ // Number of packets that had their retransmission cancelled.
+ NUM_PACKETS_RTX_REJECTED,
+ };
+
+ struct FrameInfo {
+ explicit FrameInfo(base::TimeTicks capture_time);
+ ~FrameInfo();
+
+ base::TimeTicks capture_time;
+ bool encoded;
};
typedef std::map<CastStat, double> StatsMap;
- typedef std::map<RtpTimestamp, base::TimeTicks> FrameEventTimeMap;
+ typedef std::map<RtpTimestamp, FrameInfo> FrameInfoMap;
typedef std::map<
std::pair<RtpTimestamp, uint16>,
std::pair<base::TimeTicks, CastLoggingEvent> >
@@ -120,7 +141,8 @@ class StatsEventSubscriber : public RawEventSubscriber {
void GetStatsInternal(StatsMap* stats_map) const;
bool GetReceiverOffset(base::TimeDelta* offset);
- void RecordFrameCapturedTime(const FrameEvent& frame_event);
+ void RecordFrameCaptureTime(const FrameEvent& frame_event);
+ void MarkAsEncoded(RtpTimestamp rtp_timestamp);
void RecordE2ELatency(const FrameEvent& frame_event);
void RecordPacketSentTime(const PacketEvent& packet_event);
void ErasePacketSentTime(const PacketEvent& packet_event);
@@ -131,6 +153,12 @@ class StatsEventSubscriber : public RawEventSubscriber {
CastLoggingEvent event,
CastStat stat,
StatsMap* stats_map) const;
+ void PopulateFrameCountStat(CastLoggingEvent event,
+ CastStat stat,
+ StatsMap* stats_map) const;
+ void PopulatePacketCountStat(CastLoggingEvent event,
+ CastStat stat,
+ StatsMap* stats_map) const;
void PopulatePlayoutDelayStat(StatsMap* stats_map) const;
void PopulateFrameBitrateStat(base::TimeTicks now, StatsMap* stats_map) const;
void PopulatePacketBitrateStat(base::TimeTicks now,
@@ -157,8 +185,11 @@ class StatsEventSubscriber : public RawEventSubscriber {
base::TimeTicks last_response_received_time_;
- // Fixed size map to record when recent frames were captured.
- FrameEventTimeMap frame_captured_times_;
+ int num_frames_dropped_by_encoder_;
+ int num_frames_late_;
+
+ // Fixed size map to record when recent frames were captured and other info.
+ FrameInfoMap recent_captured_frames_;
// Fixed size map to record when recent packets were sent.
PacketEventTimeMap packet_sent_times_;
diff --git a/media/cast/logging/stats_event_subscriber_unittest.cc b/media/cast/logging/stats_event_subscriber_unittest.cc
index 09c418b..fe03bc6 100644
--- a/media/cast/logging/stats_event_subscriber_unittest.cc
+++ b/media/cast/logging/stats_event_subscriber_unittest.cc
@@ -65,20 +65,44 @@ class StatsEventSubscriberTest : public ::testing::Test {
scoped_ptr<StatsEventSubscriber> subscriber_;
};
-TEST_F(StatsEventSubscriberTest, Capture) {
+TEST_F(StatsEventSubscriberTest, CaptureEncode) {
Init(VIDEO_EVENT);
uint32 rtp_timestamp = 0;
uint32 frame_id = 0;
- int num_frames = 10;
+ int extra_frames = 50;
+ // Only the first |extra_frames| frames logged will be taken into account
+ // when computing dropped frames.
+ int num_frames = StatsEventSubscriber::kMaxFrameInfoMapSize + 50;
+ int dropped_frames = 0;
base::TimeTicks start_time = sender_clock_->NowTicks();
+ // Drop half the frames during the encode step.
for (int i = 0; i < num_frames; i++) {
cast_environment_->Logging()->InsertFrameEvent(sender_clock_->NowTicks(),
FRAME_CAPTURE_BEGIN,
VIDEO_EVENT,
rtp_timestamp,
frame_id);
-
+ AdvanceClocks(base::TimeDelta::FromMicroseconds(10));
+ cast_environment_->Logging()->InsertFrameEvent(sender_clock_->NowTicks(),
+ FRAME_CAPTURE_END,
+ VIDEO_EVENT,
+ rtp_timestamp,
+ frame_id);
+ if (i % 2 == 0) {
+ AdvanceClocks(base::TimeDelta::FromMicroseconds(10));
+ cast_environment_->Logging()->InsertEncodedFrameEvent(
+ sender_clock_->NowTicks(),
+ FRAME_ENCODED,
+ VIDEO_EVENT,
+ rtp_timestamp,
+ frame_id,
+ 1024,
+ true,
+ 5678);
+ } else if (i < extra_frames) {
+ dropped_frames++;
+ }
AdvanceClocks(base::TimeDelta::FromMicroseconds(34567));
rtp_timestamp += 90;
frame_id++;
@@ -97,6 +121,16 @@ TEST_F(StatsEventSubscriberTest, Capture) {
EXPECT_DOUBLE_EQ(
it->second,
static_cast<double>(num_frames) / duration.InMillisecondsF() * 1000);
+
+ it = stats_map.find(StatsEventSubscriber::NUM_FRAMES_CAPTURED);
+ ASSERT_TRUE(it != stats_map.end());
+
+ EXPECT_DOUBLE_EQ(it->second, static_cast<double>(num_frames));
+
+ it = stats_map.find(StatsEventSubscriber::NUM_FRAMES_DROPPED_BY_ENCODER);
+ ASSERT_TRUE(it != stats_map.end());
+
+ EXPECT_DOUBLE_EQ(it->second, static_cast<double>(dropped_frames));
}
TEST_F(StatsEventSubscriberTest, Encode) {
@@ -185,10 +219,12 @@ TEST_F(StatsEventSubscriberTest, PlayoutDelay) {
uint32 frame_id = 0;
int num_frames = 10;
int total_delay_ms = 0;
- for (int i = 0; i < num_frames; i++) {
- int delay_ms = base::RandInt(-50, 50);
+ int late_frames = 0;
+ for (int i = 0, delay_ms = -50; i < num_frames; i++, delay_ms += 10) {
base::TimeDelta delay = base::TimeDelta::FromMilliseconds(delay_ms);
total_delay_ms += delay_ms;
+ if (delay_ms <= 0)
+ late_frames++;
cast_environment_->Logging()->InsertFrameEventWithDelay(
receiver_clock_.NowTicks(),
FRAME_PLAYOUT,
@@ -211,6 +247,11 @@ TEST_F(StatsEventSubscriberTest, PlayoutDelay) {
EXPECT_DOUBLE_EQ(
it->second, static_cast<double>(total_delay_ms) / num_frames);
+
+ it = stats_map.find(StatsEventSubscriber::NUM_FRAMES_LATE);
+ ASSERT_TRUE(it != stats_map.end());
+
+ EXPECT_DOUBLE_EQ(it->second, late_frames);
}
TEST_F(StatsEventSubscriberTest, E2ELatency) {
@@ -268,11 +309,12 @@ TEST_F(StatsEventSubscriberTest, Packets) {
int total_size = 0;
int retransmit_total_size = 0;
base::TimeDelta total_latency;
- int num_packets_sent = 0;
+ int num_packets_transmitted = 0;
int num_packets_retransmitted = 0;
+ int num_packets_rtx_rejected = 0;
// Every 2nd packet will be retransmitted once.
// Every 4th packet will be retransmitted twice.
- // Every 8th packet will be retransmitted 3 times.
+ // Every 8th packet will be retransmitted 3 times + 1 rejected retransmission.
for (int i = 0; i < num_packets; i++) {
int size = 1000 + base::RandInt(-100, 100);
total_size += size;
@@ -285,7 +327,7 @@ TEST_F(StatsEventSubscriberTest, Packets) {
i,
num_packets - 1,
size);
- num_packets_sent++;
+ num_packets_transmitted++;
int latency_micros = 20000 + base::RandInt(-10000, 10000);
base::TimeDelta latency = base::TimeDelta::FromMicroseconds(latency_micros);
@@ -312,7 +354,7 @@ TEST_F(StatsEventSubscriberTest, Packets) {
num_packets - 1,
size);
retransmit_total_size += size;
- num_packets_sent++;
+ num_packets_transmitted++;
num_packets_retransmitted++;
}
@@ -329,7 +371,7 @@ TEST_F(StatsEventSubscriberTest, Packets) {
num_packets - 1,
size);
retransmit_total_size += size;
- num_packets_sent++;
+ num_packets_transmitted++;
num_packets_retransmitted++;
}
@@ -345,9 +387,19 @@ TEST_F(StatsEventSubscriberTest, Packets) {
i,
num_packets - 1,
size);
+ cast_environment_->Logging()->InsertPacketEvent(
+ receiver_clock_.NowTicks(),
+ PACKET_RTX_REJECTED,
+ VIDEO_EVENT,
+ rtp_timestamp,
+ 0,
+ i,
+ num_packets - 1,
+ size);
retransmit_total_size += size;
- num_packets_sent++;
+ num_packets_transmitted++;
num_packets_retransmitted++;
+ num_packets_rtx_rejected++;
}
cast_environment_->Logging()->InsertPacketEvent(received_time,
@@ -394,7 +446,22 @@ TEST_F(StatsEventSubscriberTest, Packets) {
EXPECT_DOUBLE_EQ(
it->second,
- static_cast<double>(num_packets_retransmitted) / num_packets_sent);
+ static_cast<double>(num_packets_retransmitted) / num_packets_transmitted);
+
+ it = stats_map.find(StatsEventSubscriber::NUM_PACKETS_SENT);
+ ASSERT_TRUE(it != stats_map.end());
+
+ EXPECT_DOUBLE_EQ(it->second, static_cast<double>(num_packets));
+
+ it = stats_map.find(StatsEventSubscriber::NUM_PACKETS_RETRANSMITTED);
+ ASSERT_TRUE(it != stats_map.end());
+
+ EXPECT_DOUBLE_EQ(it->second, static_cast<double>(num_packets_retransmitted));
+
+ it = stats_map.find(StatsEventSubscriber::NUM_PACKETS_RTX_REJECTED);
+ ASSERT_TRUE(it != stats_map.end());
+
+ EXPECT_DOUBLE_EQ(it->second, static_cast<double>(num_packets_rtx_rejected));
}
} // namespace cast