| Index: media/cast/logging/stats_event_subscriber_unittest.cc
|
| diff --git a/media/cast/logging/stats_event_subscriber_unittest.cc b/media/cast/logging/stats_event_subscriber_unittest.cc
|
| index 8f4d0c5271154d83ef98df8acb0beb03a40862f8..3d5f8f141784d82f8cb7a535c2e12d79482beb64 100644
|
| --- a/media/cast/logging/stats_event_subscriber_unittest.cc
|
| +++ b/media/cast/logging/stats_event_subscriber_unittest.cc
|
| @@ -4,169 +4,401 @@
|
|
|
| #include "base/memory/ref_counted.h"
|
| #include "base/memory/scoped_ptr.h"
|
| +#include "base/rand_util.h"
|
| #include "base/test/simple_test_tick_clock.h"
|
| #include "base/time/tick_clock.h"
|
| #include "media/cast/cast_environment.h"
|
| #include "media/cast/logging/logging_defines.h"
|
| #include "media/cast/logging/stats_event_subscriber.h"
|
| +#include "media/cast/test/fake_receiver_time_offset_estimator.h"
|
| #include "media/cast/test/fake_single_thread_task_runner.h"
|
| #include "testing/gtest/include/gtest/gtest.h"
|
|
|
| +namespace {
|
| +const int kReceiverOffsetSecs = 100;
|
| +}
|
| +
|
| namespace media {
|
| namespace cast {
|
|
|
| class StatsEventSubscriberTest : public ::testing::Test {
|
| protected:
|
| StatsEventSubscriberTest()
|
| - : testing_clock_(new base::SimpleTestTickClock()),
|
| - task_runner_(new test::FakeSingleThreadTaskRunner(testing_clock_)),
|
| + : sender_clock_(new base::SimpleTestTickClock()),
|
| + task_runner_(new test::FakeSingleThreadTaskRunner(sender_clock_)),
|
| cast_environment_(new CastEnvironment(
|
| - scoped_ptr<base::TickClock>(testing_clock_).Pass(),
|
| + scoped_ptr<base::TickClock>(sender_clock_).Pass(),
|
| task_runner_,
|
| task_runner_,
|
| - task_runner_)) {}
|
| + task_runner_)),
|
| + fake_offset_estimator_(
|
| + base::TimeDelta::FromSeconds(kReceiverOffsetSecs)) {
|
| + receiver_clock_.Advance(base::TimeDelta::FromSeconds(kReceiverOffsetSecs));
|
| + cast_environment_->Logging()->AddRawEventSubscriber(
|
| + &fake_offset_estimator_);
|
| + }
|
|
|
| virtual ~StatsEventSubscriberTest() {
|
| if (subscriber_.get())
|
| cast_environment_->Logging()->RemoveRawEventSubscriber(subscriber_.get());
|
| + cast_environment_->Logging()->RemoveRawEventSubscriber(
|
| + &fake_offset_estimator_);
|
| + }
|
| +
|
| + void AdvanceClocks(base::TimeDelta delta) {
|
| + sender_clock_->Advance(delta);
|
| + receiver_clock_.Advance(delta);
|
| }
|
|
|
| void Init(EventMediaType event_media_type) {
|
| DCHECK(!subscriber_.get());
|
| - subscriber_.reset(new StatsEventSubscriber(event_media_type));
|
| + subscriber_.reset(new StatsEventSubscriber(
|
| + event_media_type, cast_environment_->Clock(), &fake_offset_estimator_));
|
| cast_environment_->Logging()->AddRawEventSubscriber(subscriber_.get());
|
| }
|
|
|
| - base::SimpleTestTickClock* testing_clock_; // Owned by CastEnvironment.
|
| + base::SimpleTestTickClock* sender_clock_; // Owned by CastEnvironment.
|
| + base::SimpleTestTickClock receiver_clock_;
|
| scoped_refptr<test::FakeSingleThreadTaskRunner> task_runner_;
|
| scoped_refptr<CastEnvironment> cast_environment_;
|
| + test::FakeReceiverTimeOffsetEstimator fake_offset_estimator_;
|
| scoped_ptr<StatsEventSubscriber> subscriber_;
|
| };
|
|
|
| -TEST_F(StatsEventSubscriberTest, FrameStats) {
|
| +TEST_F(StatsEventSubscriberTest, EmptyStats) {
|
| + Init(VIDEO_EVENT);
|
| +
|
| + StatsEventSubscriber::StatsMap stats_map;
|
| + subscriber_->GetStatsInternal(&stats_map);
|
| +
|
| + EXPECT_TRUE(stats_map.empty());
|
| +}
|
| +
|
| +TEST_F(StatsEventSubscriberTest, Capture) {
|
| + Init(VIDEO_EVENT);
|
| +
|
| + uint32 rtp_timestamp = 0;
|
| + uint32 frame_id = 0;
|
| + int num_frames = 10;
|
| + base::TimeTicks start_time = sender_clock_->NowTicks();
|
| + for (int i = 0; i < num_frames; i++) {
|
| + cast_environment_->Logging()->InsertFrameEvent(sender_clock_->NowTicks(),
|
| + kVideoFrameCaptured,
|
| + rtp_timestamp,
|
| + frame_id);
|
| +
|
| + AdvanceClocks(base::TimeDelta::FromMilliseconds(30));
|
| + rtp_timestamp += 90;
|
| + frame_id++;
|
| + }
|
| +
|
| + base::TimeTicks end_time = sender_clock_->NowTicks();
|
| +
|
| + StatsEventSubscriber::StatsMap stats_map;
|
| + subscriber_->GetStatsInternal(&stats_map);
|
| +
|
| + StatsEventSubscriber::StatsMap::iterator it =
|
| + stats_map.find(StatsEventSubscriber::CAPTURE_FPS);
|
| + ASSERT_NE(it, stats_map.end());
|
| +
|
| + base::TimeDelta duration = end_time - start_time;
|
| + EXPECT_NEAR(
|
| + it->second,
|
| + static_cast<double>(num_frames) / duration.InMilliseconds() * 1000,
|
| + 0.05);
|
| +}
|
| +
|
| +TEST_F(StatsEventSubscriberTest, Encode) {
|
| Init(VIDEO_EVENT);
|
| +
|
| uint32 rtp_timestamp = 0;
|
| uint32 frame_id = 0;
|
| int num_frames = 10;
|
| - int frame_size = 123;
|
| - int delay_base_ms = 10;
|
| - base::TimeTicks now;
|
| + base::TimeTicks start_time = sender_clock_->NowTicks();
|
| + int total_size = 0;
|
| for (int i = 0; i < num_frames; i++) {
|
| - now = testing_clock_->NowTicks();
|
| - cast_environment_->Logging()->InsertFrameEvent(
|
| - now, kVideoFrameReceived, rtp_timestamp, frame_id);
|
| - testing_clock_->Advance(base::TimeDelta::FromMilliseconds(30));
|
| + int size = 1000 + base::RandInt(-100, 100);
|
| + total_size += size;
|
| + cast_environment_->Logging()->InsertFrameEventWithSize(
|
| + sender_clock_->NowTicks(),
|
| + kVideoFrameEncoded,
|
| + rtp_timestamp,
|
| + frame_id,
|
| + size);
|
| +
|
| + AdvanceClocks(base::TimeDelta::FromMilliseconds(30));
|
| + rtp_timestamp += 90;
|
| + frame_id++;
|
| + }
|
| +
|
| + base::TimeTicks end_time = sender_clock_->NowTicks();
|
| +
|
| + StatsEventSubscriber::StatsMap stats_map;
|
| + subscriber_->GetStatsInternal(&stats_map);
|
| +
|
| + StatsEventSubscriber::StatsMap::iterator it =
|
| + stats_map.find(StatsEventSubscriber::ENCODE_FPS);
|
| + ASSERT_NE(it, stats_map.end());
|
| +
|
| + base::TimeDelta duration = end_time - start_time;
|
| + EXPECT_NEAR(
|
| + it->second,
|
| + static_cast<double>(num_frames) / duration.InMilliseconds() * 1000,
|
| + 0.05);
|
| +
|
| + it = stats_map.find(StatsEventSubscriber::ENCODE_KBPS);
|
| + ASSERT_NE(it, stats_map.end());
|
|
|
| - cast_environment_->Logging()->InsertEncodedFrameEvent(
|
| - now, kVideoFrameEncoded, rtp_timestamp, i, frame_size, true);
|
| - testing_clock_->Advance(base::TimeDelta::FromMilliseconds(30));
|
| + EXPECT_NEAR(it->second,
|
| + static_cast<double>(total_size) / duration.InMilliseconds() * 8,
|
| + 0.05);
|
| +}
|
| +
|
| +TEST_F(StatsEventSubscriberTest, Decode) {
|
| + Init(VIDEO_EVENT);
|
| +
|
| + uint32 rtp_timestamp = 0;
|
| + uint32 frame_id = 0;
|
| + int num_frames = 10;
|
| + base::TimeTicks start_time = sender_clock_->NowTicks();
|
| + for (int i = 0; i < num_frames; i++) {
|
| + cast_environment_->Logging()->InsertFrameEvent(receiver_clock_.NowTicks(),
|
| + kVideoFrameDecoded,
|
| + rtp_timestamp,
|
| + frame_id);
|
| +
|
| + AdvanceClocks(base::TimeDelta::FromMilliseconds(30));
|
| + rtp_timestamp += 90;
|
| + frame_id++;
|
| + }
|
|
|
| + base::TimeTicks end_time = sender_clock_->NowTicks();
|
| +
|
| + StatsEventSubscriber::StatsMap stats_map;
|
| + subscriber_->GetStatsInternal(&stats_map);
|
| +
|
| + StatsEventSubscriber::StatsMap::iterator it =
|
| + stats_map.find(StatsEventSubscriber::DECODE_FPS);
|
| + ASSERT_NE(it, stats_map.end());
|
| +
|
| + base::TimeDelta duration = end_time - start_time;
|
| + EXPECT_NEAR(
|
| + it->second,
|
| + static_cast<double>(num_frames) / duration.InMilliseconds() * 1000,
|
| + 0.05);
|
| +}
|
| +
|
| +TEST_F(StatsEventSubscriberTest, PlayoutDelay) {
|
| + Init(VIDEO_EVENT);
|
| +
|
| + uint32 rtp_timestamp = 0;
|
| + 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);
|
| + base::TimeDelta delay = base::TimeDelta::FromMilliseconds(delay_ms);
|
| + total_delay_ms += delay_ms;
|
| cast_environment_->Logging()->InsertFrameEventWithDelay(
|
| - now,
|
| + receiver_clock_.NowTicks(),
|
| kVideoRenderDelay,
|
| rtp_timestamp,
|
| - i,
|
| - base::TimeDelta::FromMilliseconds(i * delay_base_ms));
|
| - testing_clock_->Advance(base::TimeDelta::FromMilliseconds(30));
|
| + frame_id,
|
| + delay);
|
|
|
| + AdvanceClocks(base::TimeDelta::FromMilliseconds(30));
|
| rtp_timestamp += 90;
|
| + frame_id++;
|
| }
|
|
|
| - // Verify stats.
|
| - FrameStatsMap frame_stats;
|
| - subscriber_->GetFrameStats(&frame_stats);
|
| + StatsEventSubscriber::StatsMap stats_map;
|
| + subscriber_->GetStatsInternal(&stats_map);
|
|
|
| - // Size of stats equals the number of events.
|
| - EXPECT_EQ(3u, frame_stats.size());
|
| - FrameStatsMap::const_iterator it = frame_stats.find(kVideoFrameReceived);
|
| - ASSERT_TRUE(it != frame_stats.end());
|
| - EXPECT_EQ(num_frames, it->second.event_counter);
|
| + StatsEventSubscriber::StatsMap::iterator it =
|
| + stats_map.find(StatsEventSubscriber::AVG_PLAYOUT_DELAY_MS);
|
| + ASSERT_NE(it, stats_map.end());
|
|
|
| - it = frame_stats.find(kVideoFrameEncoded);
|
| - ASSERT_TRUE(it != frame_stats.end());
|
| + EXPECT_NEAR(
|
| + it->second, static_cast<double>(total_delay_ms) / num_frames, 0.05);
|
| +}
|
|
|
| - EXPECT_EQ(num_frames * frame_size, static_cast<int>(it->second.sum_size));
|
| +TEST_F(StatsEventSubscriberTest, E2ELatency) {
|
| + Init(VIDEO_EVENT);
|
| +
|
| + uint32 rtp_timestamp = 0;
|
| + uint32 frame_id = 0;
|
| + int num_frames = 10;
|
| + int total_latency_ms = 0;
|
| + for (int i = 0; i < num_frames; i++) {
|
| + cast_environment_->Logging()->InsertFrameEvent(sender_clock_->NowTicks(),
|
| + kVideoFrameCaptured,
|
| + rtp_timestamp,
|
| + frame_id);
|
|
|
| - it = frame_stats.find(kVideoRenderDelay);
|
| - ASSERT_TRUE(it != frame_stats.end());
|
| + int latency_ms = 100 + base::RandInt(-50, 50);
|
| + AdvanceClocks(base::TimeDelta::FromMilliseconds(latency_ms));
|
| +
|
| + int delay_ms = base::RandInt(-50, 50);
|
| + base::TimeDelta delay = base::TimeDelta::FromMilliseconds(delay_ms);
|
| + total_latency_ms += latency_ms + delay_ms;
|
| +
|
| + cast_environment_->Logging()->InsertFrameEventWithDelay(
|
| + receiver_clock_.NowTicks(),
|
| + kVideoRenderDelay,
|
| + rtp_timestamp,
|
| + frame_id,
|
| + delay);
|
|
|
| - EXPECT_EQ(0, it->second.min_delay.InMilliseconds());
|
| - EXPECT_EQ((num_frames - 1) * delay_base_ms,
|
| - it->second.max_delay.InMilliseconds());
|
| - EXPECT_EQ((num_frames - 1) * num_frames / 2 * delay_base_ms,
|
| - it->second.sum_delay.InMilliseconds());
|
| + rtp_timestamp += 90;
|
| + frame_id++;
|
| + }
|
| +
|
| + StatsEventSubscriber::StatsMap stats_map;
|
| + subscriber_->GetStatsInternal(&stats_map);
|
| +
|
| + StatsEventSubscriber::StatsMap::iterator it =
|
| + stats_map.find(StatsEventSubscriber::AVG_E2E_LATENCY_MS);
|
| + ASSERT_NE(it, stats_map.end());
|
| +
|
| + EXPECT_NEAR(
|
| + it->second, static_cast<double>(total_latency_ms) / num_frames, 0.05);
|
| }
|
|
|
| -TEST_F(StatsEventSubscriberTest, PacketStats) {
|
| - Init(AUDIO_EVENT);
|
| +TEST_F(StatsEventSubscriberTest, Packets) {
|
| + Init(VIDEO_EVENT);
|
| +
|
| uint32 rtp_timestamp = 0;
|
| - uint32 frame_id = 0;
|
| int num_packets = 10;
|
| - int packet_size = 123;
|
| - base::TimeTicks first_event_time = testing_clock_->NowTicks();
|
| - base::TimeTicks now;
|
| + int num_latency_recorded_packets = 0;
|
| + base::TimeTicks start_time = sender_clock_->NowTicks();
|
| + int total_size = 0;
|
| + int retransmit_total_size = 0;
|
| + int total_latency_ms = 0;
|
| + int num_packets_sent = 0;
|
| + int num_packets_retransmitted = 0;
|
| + // Every 2nd packet will be retransmitted once.
|
| + // Every 4th packet will be retransmitted twice.
|
| + // Every 8th packet will be retransmitted 3 times.
|
| for (int i = 0; i < num_packets; i++) {
|
| - now = testing_clock_->NowTicks();
|
| - cast_environment_->Logging()->InsertPacketEvent(now,
|
| - kAudioPacketSentToPacer,
|
| + int size = 1000 + base::RandInt(-100, 100);
|
| + total_size += size;
|
| +
|
| + cast_environment_->Logging()->InsertPacketEvent(sender_clock_->NowTicks(),
|
| + kVideoPacketSentToNetwork,
|
| rtp_timestamp,
|
| - frame_id,
|
| 0,
|
| - 10,
|
| - packet_size);
|
| - testing_clock_->Advance(base::TimeDelta::FromMilliseconds(30));
|
| - }
|
| + i,
|
| + num_packets - 1,
|
| + size);
|
| + num_packets_sent++;
|
|
|
| - PacketStatsMap stats_map;
|
| - subscriber_->GetPacketStats(&stats_map);
|
| + int latency_ms = 20 + base::RandInt(-10, 10);
|
| + // Latency is only recorded for packets that aren't retransmitted.
|
| + if (i % 2 != 0) {
|
| + total_latency_ms += latency_ms;
|
| + num_latency_recorded_packets++;
|
| + }
|
|
|
| - // Size of stats equals the number of event types.
|
| - EXPECT_EQ(1u, stats_map.size());
|
| - PacketStatsMap::const_iterator it = stats_map.find(kAudioPacketSentToPacer);
|
| - ASSERT_NE(stats_map.end(), it);
|
| + AdvanceClocks(base::TimeDelta::FromMilliseconds(latency_ms));
|
|
|
| - EXPECT_EQ(first_event_time, it->second.first_event_time);
|
| - EXPECT_EQ(now, it->second.last_event_time);
|
| - EXPECT_EQ(num_packets, it->second.event_counter);
|
| - EXPECT_EQ(num_packets * packet_size, static_cast<int>(it->second.sum_size));
|
| -}
|
| + base::TimeTicks received_time = receiver_clock_.NowTicks();
|
|
|
| -TEST_F(StatsEventSubscriberTest, GenericStats) {
|
| - Init(OTHER_EVENT);
|
| - int num_generic_events = 10;
|
| - int value = 123;
|
| - for (int i = 0; i < num_generic_events; i++) {
|
| - cast_environment_->Logging()->InsertGenericEvent(
|
| - testing_clock_->NowTicks(), kRttMs, value);
|
| + // Retransmission 1.
|
| + AdvanceClocks(base::TimeDelta::FromMilliseconds(10));
|
| + if (i % 2 == 0) {
|
| + cast_environment_->Logging()->InsertPacketEvent(
|
| + receiver_clock_.NowTicks(),
|
| + kVideoPacketRetransmitted,
|
| + rtp_timestamp,
|
| + 0,
|
| + i,
|
| + num_packets - 1,
|
| + size);
|
| + retransmit_total_size += size;
|
| + num_packets_sent++;
|
| + num_packets_retransmitted++;
|
| + }
|
| +
|
| + // Retransmission 2.
|
| + AdvanceClocks(base::TimeDelta::FromMilliseconds(10));
|
| + if (i % 4 == 0) {
|
| + cast_environment_->Logging()->InsertPacketEvent(
|
| + receiver_clock_.NowTicks(),
|
| + kVideoPacketRetransmitted,
|
| + rtp_timestamp,
|
| + 0,
|
| + i,
|
| + num_packets - 1,
|
| + size);
|
| + retransmit_total_size += size;
|
| + num_packets_sent++;
|
| + num_packets_retransmitted++;
|
| + }
|
| +
|
| + // Retransmission 3.
|
| + AdvanceClocks(base::TimeDelta::FromMilliseconds(10));
|
| + if (i % 8 == 0) {
|
| + cast_environment_->Logging()->InsertPacketEvent(
|
| + receiver_clock_.NowTicks(),
|
| + kVideoPacketRetransmitted,
|
| + rtp_timestamp,
|
| + 0,
|
| + i,
|
| + num_packets - 1,
|
| + size);
|
| + retransmit_total_size += size;
|
| + num_packets_sent++;
|
| + num_packets_retransmitted++;
|
| + }
|
| +
|
| + cast_environment_->Logging()->InsertPacketEvent(received_time,
|
| + kVideoPacketReceived,
|
| + rtp_timestamp,
|
| + 0,
|
| + i,
|
| + num_packets - 1,
|
| + size);
|
| }
|
|
|
| - GenericStatsMap stats_map;
|
| - subscriber_->GetGenericStats(&stats_map);
|
| + base::TimeTicks end_time = sender_clock_->NowTicks();
|
| + base::TimeDelta duration = end_time - start_time;
|
|
|
| - EXPECT_EQ(1u, stats_map.size());
|
| - GenericStatsMap::const_iterator it = stats_map.find(kRttMs);
|
| - ASSERT_NE(stats_map.end(), it);
|
| + StatsEventSubscriber::StatsMap stats_map;
|
| + subscriber_->GetStatsInternal(&stats_map);
|
|
|
| - EXPECT_EQ(num_generic_events, it->second.event_counter);
|
| - EXPECT_EQ(num_generic_events * value, it->second.sum);
|
| - EXPECT_EQ(static_cast<uint64>(num_generic_events * value * value),
|
| - it->second.sum_squared);
|
| - EXPECT_LE(value, it->second.min);
|
| - EXPECT_GE(value, it->second.max);
|
| -}
|
| + // Measure AVG_NETWORK_LATENCY_MS, TRANSMISSION_KBPS, RETRANSMISSION_KBPS,
|
| + // and PACKET_LOSS_PERCENTAGE.
|
| + StatsEventSubscriber::StatsMap::iterator it =
|
| + stats_map.find(StatsEventSubscriber::AVG_NETWORK_LATENCY_MS);
|
| + ASSERT_NE(it, stats_map.end());
|
|
|
| -TEST_F(StatsEventSubscriberTest, Reset) {
|
| - Init(VIDEO_EVENT);
|
| - cast_environment_->Logging()->InsertFrameEvent(
|
| - testing_clock_->NowTicks(), kVideoFrameReceived, 0, 0);
|
| + EXPECT_NEAR(
|
| + it->second,
|
| + static_cast<double>(total_latency_ms) / num_latency_recorded_packets,
|
| + 0.05);
|
| +
|
| + it = stats_map.find(StatsEventSubscriber::TRANSMISSION_KBPS);
|
| + ASSERT_NE(it, stats_map.end());
|
| +
|
| + EXPECT_NEAR(it->second,
|
| + static_cast<double>(total_size) / duration.InMilliseconds() * 8,
|
| + 0.05);
|
| +
|
| + it = stats_map.find(StatsEventSubscriber::RETRANSMISSION_KBPS);
|
| + ASSERT_NE(it, stats_map.end());
|
| +
|
| + EXPECT_NEAR(it->second,
|
| + static_cast<double>(retransmit_total_size) /
|
| + duration.InMilliseconds() * 8,
|
| + 0.05);
|
|
|
| - FrameStatsMap frame_stats;
|
| - subscriber_->GetFrameStats(&frame_stats);
|
| - EXPECT_EQ(1u, frame_stats.size());
|
| + it = stats_map.find(StatsEventSubscriber::PACKET_LOSS_PERCENTAGE);
|
| + ASSERT_NE(it, stats_map.end());
|
|
|
| - subscriber_->Reset();
|
| - subscriber_->GetFrameStats(&frame_stats);
|
| - EXPECT_TRUE(frame_stats.empty());
|
| + EXPECT_NEAR(
|
| + it->second,
|
| + static_cast<double>(num_packets_retransmitted) / num_packets_sent * 100,
|
| + 0.05);
|
| }
|
|
|
| } // namespace cast
|
|
|