// Copyright 2014 The Chromium Authors. All rights reserved. // Use of this source code is governed by a BSD-style license that can be // found in the LICENSE file. #include "media/cast/logging/stats_event_subscriber.h" #include #include #include #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 "base/values.h" #include "media/cast/cast_environment.h" #include "media/cast/logging/logging_defines.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() : sender_clock_(new base::SimpleTestTickClock()), task_runner_(new test::FakeSingleThreadTaskRunner(sender_clock_)), cast_environment_( new CastEnvironment(scoped_ptr(sender_clock_), task_runner_, task_runner_, task_runner_)), fake_offset_estimator_( base::TimeDelta::FromSeconds(kReceiverOffsetSecs)) { receiver_clock_.Advance(base::TimeDelta::FromSeconds(kReceiverOffsetSecs)); cast_environment_->logger()->Subscribe(&fake_offset_estimator_); } ~StatsEventSubscriberTest() override { if (subscriber_.get()) cast_environment_->logger()->Unsubscribe(subscriber_.get()); cast_environment_->logger()->Unsubscribe(&fake_offset_estimator_); } void AdvanceClocks(base::TimeDelta delta) { task_runner_->Sleep(delta); receiver_clock_.Advance(delta); } void Init(EventMediaType event_media_type) { DCHECK(!subscriber_.get()); subscriber_.reset(new StatsEventSubscriber( event_media_type, cast_environment_->Clock(), &fake_offset_estimator_)); cast_environment_->logger()->Subscribe(subscriber_.get()); } base::SimpleTestTickClock* sender_clock_; // Owned by CastEnvironment. base::SimpleTestTickClock receiver_clock_; scoped_refptr task_runner_; scoped_refptr cast_environment_; test::FakeReceiverTimeOffsetEstimator fake_offset_estimator_; scoped_ptr subscriber_; }; TEST_F(StatsEventSubscriberTest, CaptureEncode) { Init(VIDEO_EVENT); RtpTimeTicks rtp_timestamp; uint32_t frame_id = 0; 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++) { scoped_ptr capture_begin_event(new FrameEvent()); capture_begin_event->timestamp = sender_clock_->NowTicks(); capture_begin_event->type = FRAME_CAPTURE_BEGIN; capture_begin_event->media_type = VIDEO_EVENT; capture_begin_event->rtp_timestamp = rtp_timestamp; cast_environment_->logger()->DispatchFrameEvent( std::move(capture_begin_event)); AdvanceClocks(base::TimeDelta::FromMicroseconds(10)); scoped_ptr capture_end_event(new FrameEvent()); capture_end_event->timestamp = sender_clock_->NowTicks(); capture_end_event->type = FRAME_CAPTURE_END; capture_end_event->media_type = VIDEO_EVENT; capture_end_event->rtp_timestamp = rtp_timestamp; cast_environment_->logger()->DispatchFrameEvent( std::move(capture_end_event)); if (i % 2 == 0) { AdvanceClocks(base::TimeDelta::FromMicroseconds(10)); scoped_ptr encode_event(new FrameEvent()); encode_event->timestamp = sender_clock_->NowTicks(); encode_event->type = FRAME_ENCODED; encode_event->media_type = VIDEO_EVENT; encode_event->rtp_timestamp = rtp_timestamp; encode_event->frame_id = frame_id; encode_event->size = 1024; encode_event->key_frame = true; encode_event->target_bitrate = 5678; encode_event->encoder_cpu_utilization = 9.10; encode_event->idealized_bitrate_utilization = 11.12; cast_environment_->logger()->DispatchFrameEvent(std::move(encode_event)); } else if (i < extra_frames) { dropped_frames++; } AdvanceClocks(base::TimeDelta::FromMicroseconds(34567)); rtp_timestamp += RtpTimeDelta::FromTicks(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_TRUE(it != stats_map.end()); base::TimeDelta duration = end_time - start_time; EXPECT_DOUBLE_EQ( it->second, static_cast(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(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(dropped_frames)); it = stats_map.find(StatsEventSubscriber::AVG_CAPTURE_LATENCY_MS); ASSERT_TRUE(it != stats_map.end()); EXPECT_DOUBLE_EQ(it->second, static_cast(0.01)); } TEST_F(StatsEventSubscriberTest, Encode) { Init(VIDEO_EVENT); RtpTimeTicks rtp_timestamp; uint32_t frame_id = 0; int num_frames = 10; base::TimeTicks start_time = sender_clock_->NowTicks(); AdvanceClocks(base::TimeDelta::FromMicroseconds(35678)); base::TimeTicks first_event_time = sender_clock_->NowTicks(); base::TimeTicks last_event_time; int total_size = 0; for (int i = 0; i < num_frames; i++) { int size = 1000 + base::RandInt(-100, 100); total_size += size; scoped_ptr encode_event(new FrameEvent()); encode_event->timestamp = sender_clock_->NowTicks(); encode_event->type = FRAME_ENCODED; encode_event->media_type = VIDEO_EVENT; encode_event->rtp_timestamp = rtp_timestamp; encode_event->frame_id = frame_id; encode_event->size = size; encode_event->key_frame = true; encode_event->target_bitrate = 5678; encode_event->encoder_cpu_utilization = 9.10; encode_event->idealized_bitrate_utilization = 11.12; cast_environment_->logger()->DispatchFrameEvent(std::move(encode_event)); last_event_time = sender_clock_->NowTicks(); AdvanceClocks(base::TimeDelta::FromMicroseconds(35678)); rtp_timestamp += RtpTimeDelta::FromTicks(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_TRUE(it != stats_map.end()); base::TimeDelta duration = end_time - start_time; EXPECT_DOUBLE_EQ( it->second, static_cast(num_frames) / duration.InMillisecondsF() * 1000); it = stats_map.find(StatsEventSubscriber::ENCODE_KBPS); ASSERT_TRUE(it != stats_map.end()); EXPECT_DOUBLE_EQ(it->second, static_cast(total_size) / duration.InMillisecondsF() * 8); it = stats_map.find(StatsEventSubscriber::FIRST_EVENT_TIME_MS); ASSERT_TRUE(it != stats_map.end()); EXPECT_DOUBLE_EQ( it->second, (first_event_time - base::TimeTicks::UnixEpoch()).InMillisecondsF()); it = stats_map.find(StatsEventSubscriber::LAST_EVENT_TIME_MS); ASSERT_TRUE(it != stats_map.end()); EXPECT_DOUBLE_EQ( it->second, (last_event_time - base::TimeTicks::UnixEpoch()).InMillisecondsF()); } TEST_F(StatsEventSubscriberTest, Decode) { Init(VIDEO_EVENT); RtpTimeTicks rtp_timestamp; uint32_t frame_id = 0; int num_frames = 10; base::TimeTicks start_time = sender_clock_->NowTicks(); for (int i = 0; i < num_frames; i++) { scoped_ptr decode_event(new FrameEvent()); decode_event->timestamp = receiver_clock_.NowTicks(); decode_event->type = FRAME_DECODED; decode_event->media_type = VIDEO_EVENT; decode_event->rtp_timestamp = rtp_timestamp; decode_event->frame_id = frame_id; cast_environment_->logger()->DispatchFrameEvent(std::move(decode_event)); AdvanceClocks(base::TimeDelta::FromMicroseconds(36789)); rtp_timestamp += RtpTimeDelta::FromTicks(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_TRUE(it != stats_map.end()); base::TimeDelta duration = end_time - start_time; EXPECT_DOUBLE_EQ( it->second, static_cast(num_frames) / duration.InMillisecondsF() * 1000); } TEST_F(StatsEventSubscriberTest, PlayoutDelay) { Init(VIDEO_EVENT); RtpTimeTicks rtp_timestamp; uint32_t frame_id = 0; int num_frames = 10; 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); if (delay_ms > 0) late_frames++; scoped_ptr playout_event(new FrameEvent()); playout_event->timestamp = receiver_clock_.NowTicks(); playout_event->type = FRAME_PLAYOUT; playout_event->media_type = VIDEO_EVENT; playout_event->rtp_timestamp = rtp_timestamp; playout_event->frame_id = frame_id; playout_event->delay_delta = delay; cast_environment_->logger()->DispatchFrameEvent(std::move(playout_event)); AdvanceClocks(base::TimeDelta::FromMicroseconds(37890)); rtp_timestamp += RtpTimeDelta::FromTicks(90); frame_id++; } StatsEventSubscriber::StatsMap stats_map; subscriber_->GetStatsInternal(&stats_map); StatsEventSubscriber::StatsMap::iterator 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) { Init(VIDEO_EVENT); RtpTimeTicks rtp_timestamp; uint32_t frame_id = 0; int num_frames = 10; base::TimeDelta total_latency; for (int i = 0; i < num_frames; i++) { scoped_ptr capture_begin_event(new FrameEvent()); capture_begin_event->timestamp = sender_clock_->NowTicks(); capture_begin_event->type = FRAME_CAPTURE_BEGIN; capture_begin_event->media_type = VIDEO_EVENT; capture_begin_event->rtp_timestamp = rtp_timestamp; cast_environment_->logger()->DispatchFrameEvent( std::move(capture_begin_event)); int latency_micros = 100000 + base::RandInt(-5000, 50000); base::TimeDelta latency = base::TimeDelta::FromMicroseconds(latency_micros); AdvanceClocks(latency); int delay_micros = base::RandInt(-50000, 50000); base::TimeDelta delay = base::TimeDelta::FromMilliseconds(delay_micros); total_latency += latency; scoped_ptr playout_event(new FrameEvent()); playout_event->timestamp = receiver_clock_.NowTicks(); playout_event->type = FRAME_PLAYOUT; playout_event->media_type = VIDEO_EVENT; playout_event->rtp_timestamp = rtp_timestamp; playout_event->frame_id = frame_id; playout_event->delay_delta = delay; cast_environment_->logger()->DispatchFrameEvent(std::move(playout_event)); rtp_timestamp += RtpTimeDelta::FromTicks(90); frame_id++; } StatsEventSubscriber::StatsMap stats_map; subscriber_->GetStatsInternal(&stats_map); StatsEventSubscriber::StatsMap::iterator it = stats_map.find(StatsEventSubscriber::AVG_E2E_LATENCY_MS); ASSERT_TRUE(it != stats_map.end()); EXPECT_DOUBLE_EQ( it->second, total_latency.InMillisecondsF() / num_frames); } TEST_F(StatsEventSubscriberTest, Packets) { Init(VIDEO_EVENT); RtpTimeTicks rtp_timestamp; int num_packets = 10; int num_latency_recorded_packets = 0; base::TimeTicks start_time = sender_clock_->NowTicks(); int total_size = 0; int retransmit_total_size = 0; base::TimeDelta total_network_latency; base::TimeDelta total_queueing_latency; base::TimeDelta total_packet_latency; int num_packets_transmitted = 0; int num_packets_received = 0; int num_packets_retransmitted = 0; int num_packets_rtx_rejected = 0; base::TimeTicks sender_encoded_time = sender_clock_->NowTicks(); base::TimeTicks receiver_encoded_time = receiver_clock_.NowTicks(); scoped_ptr encode_event(new FrameEvent()); encode_event->timestamp = sender_encoded_time; encode_event->type = FRAME_ENCODED; encode_event->media_type = VIDEO_EVENT; encode_event->rtp_timestamp = rtp_timestamp; encode_event->frame_id = 0; cast_environment_->logger()->DispatchFrameEvent(std::move(encode_event)); // Every 2nd packet will be retransmitted once. // Every 4th packet will be retransmitted twice. // 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; scoped_ptr send_event(new PacketEvent()); send_event->timestamp = sender_clock_->NowTicks(); send_event->type = PACKET_SENT_TO_NETWORK; send_event->media_type = VIDEO_EVENT; send_event->rtp_timestamp = rtp_timestamp; send_event->frame_id = 0; send_event->packet_id = i; send_event->max_packet_id = num_packets - 1; send_event->size = size; cast_environment_->logger()->DispatchPacketEvent(std::move(send_event)); num_packets_transmitted++; total_queueing_latency += sender_clock_->NowTicks() - sender_encoded_time; int latency_micros = 20000 + base::RandInt(-10000, 10000); base::TimeDelta latency = base::TimeDelta::FromMicroseconds(latency_micros); // Latency is only recorded for packets that aren't retransmitted. if (i % 2 != 0) { total_network_latency += latency; total_packet_latency += receiver_clock_.NowTicks() - receiver_encoded_time + latency; num_latency_recorded_packets++; } AdvanceClocks(latency); base::TimeTicks received_time = receiver_clock_.NowTicks(); // Retransmission 1. AdvanceClocks(base::TimeDelta::FromMicroseconds(12345)); if (i % 2 == 0) { scoped_ptr retransmit_event(new PacketEvent()); retransmit_event->timestamp = receiver_clock_.NowTicks(); retransmit_event->type = PACKET_RETRANSMITTED; retransmit_event->media_type = VIDEO_EVENT; retransmit_event->rtp_timestamp = rtp_timestamp; retransmit_event->frame_id = 0; retransmit_event->packet_id = i; retransmit_event->max_packet_id = num_packets - 1; retransmit_event->size = size; cast_environment_->logger()->DispatchPacketEvent( std::move(retransmit_event)); retransmit_total_size += size; num_packets_transmitted++; num_packets_retransmitted++; } // Retransmission 2. AdvanceClocks(base::TimeDelta::FromMicroseconds(13456)); if (i % 4 == 0) { scoped_ptr retransmit_event(new PacketEvent()); retransmit_event->timestamp = receiver_clock_.NowTicks(); retransmit_event->type = PACKET_RETRANSMITTED; retransmit_event->media_type = VIDEO_EVENT; retransmit_event->rtp_timestamp = rtp_timestamp; retransmit_event->frame_id = 0; retransmit_event->packet_id = i; retransmit_event->max_packet_id = num_packets - 1; retransmit_event->size = size; cast_environment_->logger()->DispatchPacketEvent( std::move(retransmit_event)); retransmit_total_size += size; num_packets_transmitted++; num_packets_retransmitted++; } // Retransmission 3. AdvanceClocks(base::TimeDelta::FromMicroseconds(14567)); if (i % 8 == 0) { scoped_ptr retransmit_event(new PacketEvent()); retransmit_event->timestamp = receiver_clock_.NowTicks(); retransmit_event->type = PACKET_RETRANSMITTED; retransmit_event->media_type = VIDEO_EVENT; retransmit_event->rtp_timestamp = rtp_timestamp; retransmit_event->frame_id = 0; retransmit_event->packet_id = i; retransmit_event->max_packet_id = num_packets - 1; retransmit_event->size = size; cast_environment_->logger()->DispatchPacketEvent( std::move(retransmit_event)); scoped_ptr reject_event(new PacketEvent()); reject_event->timestamp = receiver_clock_.NowTicks(); reject_event->type = PACKET_RTX_REJECTED; reject_event->media_type = VIDEO_EVENT; reject_event->rtp_timestamp = rtp_timestamp; reject_event->frame_id = 0; reject_event->packet_id = i; reject_event->max_packet_id = num_packets - 1; reject_event->size = size; cast_environment_->logger()->DispatchPacketEvent(std::move(reject_event)); retransmit_total_size += size; num_packets_transmitted++; num_packets_retransmitted++; num_packets_rtx_rejected++; } scoped_ptr receive_event(new PacketEvent()); receive_event->timestamp = received_time; receive_event->type = PACKET_RECEIVED; receive_event->media_type = VIDEO_EVENT; receive_event->rtp_timestamp = rtp_timestamp; receive_event->frame_id = 0; receive_event->packet_id = i; receive_event->max_packet_id = num_packets - 1; receive_event->size = size; cast_environment_->logger()->DispatchPacketEvent(std::move(receive_event)); num_packets_received++; } base::TimeTicks end_time = sender_clock_->NowTicks(); base::TimeDelta duration = end_time - start_time; StatsEventSubscriber::StatsMap stats_map; subscriber_->GetStatsInternal(&stats_map); // Measure AVG_NETWORK_LATENCY_MS, TRANSMISSION_KBPS, RETRANSMISSION_KBPS. StatsEventSubscriber::StatsMap::iterator it = stats_map.find(StatsEventSubscriber::AVG_NETWORK_LATENCY_MS); ASSERT_TRUE(it != stats_map.end()); EXPECT_DOUBLE_EQ( it->second, total_network_latency.InMillisecondsF() / num_latency_recorded_packets); it = stats_map.find(StatsEventSubscriber::AVG_QUEUEING_LATENCY_MS); ASSERT_TRUE(it != stats_map.end()); EXPECT_DOUBLE_EQ( it->second, total_queueing_latency.InMillisecondsF() / num_packets); it = stats_map.find(StatsEventSubscriber::AVG_PACKET_LATENCY_MS); ASSERT_TRUE(it != stats_map.end()); EXPECT_DOUBLE_EQ( it->second, total_packet_latency.InMillisecondsF() / num_latency_recorded_packets); it = stats_map.find(StatsEventSubscriber::TRANSMISSION_KBPS); ASSERT_TRUE(it != stats_map.end()); EXPECT_DOUBLE_EQ(it->second, static_cast(total_size) / duration.InMillisecondsF() * 8); it = stats_map.find(StatsEventSubscriber::RETRANSMISSION_KBPS); ASSERT_TRUE(it != stats_map.end()); EXPECT_DOUBLE_EQ(it->second, static_cast(retransmit_total_size) / duration.InMillisecondsF() * 8); it = stats_map.find(StatsEventSubscriber::NUM_PACKETS_SENT); ASSERT_TRUE(it != stats_map.end()); EXPECT_DOUBLE_EQ(it->second, static_cast(num_packets)); it = stats_map.find(StatsEventSubscriber::NUM_PACKETS_RECEIVED); ASSERT_TRUE(it != stats_map.end()); EXPECT_DOUBLE_EQ(it->second, static_cast(num_packets_received)); it = stats_map.find(StatsEventSubscriber::NUM_PACKETS_RETRANSMITTED); ASSERT_TRUE(it != stats_map.end()); EXPECT_DOUBLE_EQ(it->second, static_cast(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(num_packets_rtx_rejected)); } bool CheckHistogramHasValue(base::ListValue* values, const std::string& bucket, int expected_count) { for (size_t i = 0; i < values->GetSize(); ++i) { const base::DictionaryValue* dict = NULL; values->GetDictionary(i, &dict); if (!dict->HasKey(bucket)) continue; int bucket_count = 0; if (!dict->GetInteger(bucket, &bucket_count)) return false; return bucket_count == expected_count; } return false; } TEST_F(StatsEventSubscriberTest, Histograms) { Init(VIDEO_EVENT); AdvanceClocks(base::TimeDelta::FromMilliseconds(123)); RtpTimeTicks rtp_timestamp = RtpTimeTicks().Expand(UINT32_C(123)); uint32_t frame_id = 0; // 10 Frames with capture latency in the bucket of "10-14"ms. // 10 Frames with encode time in the bucket of "15-19"ms. for (int i = 0; i < 10; ++i) { ++frame_id; rtp_timestamp += RtpTimeDelta::FromTicks(1); scoped_ptr capture_begin_event(new FrameEvent()); capture_begin_event->timestamp = sender_clock_->NowTicks(); capture_begin_event->type = FRAME_CAPTURE_BEGIN; capture_begin_event->media_type = VIDEO_EVENT; capture_begin_event->rtp_timestamp = rtp_timestamp; cast_environment_->logger()->DispatchFrameEvent( std::move(capture_begin_event)); AdvanceClocks(base::TimeDelta::FromMilliseconds(10)); scoped_ptr capture_end_event(new FrameEvent()); capture_end_event->timestamp = sender_clock_->NowTicks(); capture_end_event->type = FRAME_CAPTURE_END; capture_end_event->media_type = VIDEO_EVENT; capture_end_event->rtp_timestamp = rtp_timestamp; cast_environment_->logger()->DispatchFrameEvent( std::move(capture_end_event)); AdvanceClocks(base::TimeDelta::FromMilliseconds(15)); scoped_ptr encode_event(new FrameEvent()); encode_event->timestamp = sender_clock_->NowTicks(); encode_event->type = FRAME_ENCODED; encode_event->media_type = VIDEO_EVENT; encode_event->rtp_timestamp = rtp_timestamp; encode_event->frame_id = frame_id; encode_event->size = 1024; encode_event->key_frame = true; encode_event->target_bitrate = 5678; encode_event->encoder_cpu_utilization = 9.10; encode_event->idealized_bitrate_utilization = 11.12; cast_environment_->logger()->DispatchFrameEvent(std::move(encode_event)); } // Send 3 packets for the last frame. // Queueing latencies are 100ms, 200ms and 300ms. for (int i = 0; i < 3; ++i) { AdvanceClocks(base::TimeDelta::FromMilliseconds(100)); scoped_ptr send_event(new PacketEvent()); send_event->timestamp = sender_clock_->NowTicks(); send_event->type = PACKET_SENT_TO_NETWORK; send_event->media_type = VIDEO_EVENT; send_event->rtp_timestamp = rtp_timestamp; send_event->frame_id = 0; send_event->packet_id = i; send_event->max_packet_id = 2; send_event->size = 123; cast_environment_->logger()->DispatchPacketEvent(std::move(send_event)); } // Receive 3 packets for the last frame. // Network latencies are 100ms, 200ms and 300ms. // Packet latencies are 400ms. AdvanceClocks(base::TimeDelta::FromMilliseconds(100)); for (int i = 0; i < 3; ++i) { scoped_ptr receive_event(new PacketEvent()); receive_event->timestamp = receiver_clock_.NowTicks(); receive_event->type = PACKET_RECEIVED; receive_event->media_type = VIDEO_EVENT; receive_event->rtp_timestamp = rtp_timestamp; receive_event->frame_id = 0; receive_event->packet_id = i; receive_event->max_packet_id = 2; receive_event->size = 123; cast_environment_->logger()->DispatchPacketEvent(std::move(receive_event)); } scoped_ptr playout_event(new FrameEvent()); playout_event->timestamp = receiver_clock_.NowTicks(); playout_event->type = FRAME_PLAYOUT; playout_event->media_type = VIDEO_EVENT; playout_event->rtp_timestamp = rtp_timestamp; playout_event->frame_id = frame_id; playout_event->delay_delta = base::TimeDelta::FromMilliseconds(100); cast_environment_->logger()->DispatchFrameEvent(std::move(playout_event)); StatsEventSubscriber::SimpleHistogram* histogram; scoped_ptr values; histogram = subscriber_->GetHistogramForTesting( StatsEventSubscriber::CAPTURE_LATENCY_MS_HISTO); ASSERT_TRUE(histogram); values = histogram->GetHistogram(); EXPECT_TRUE(CheckHistogramHasValue(values.get(), "10-14", 10)); histogram = subscriber_->GetHistogramForTesting( StatsEventSubscriber::ENCODE_TIME_MS_HISTO); ASSERT_TRUE(histogram); values = histogram->GetHistogram(); EXPECT_TRUE(CheckHistogramHasValue(values.get(), "15-19", 10)); histogram = subscriber_->GetHistogramForTesting( StatsEventSubscriber::QUEUEING_LATENCY_MS_HISTO); ASSERT_TRUE(histogram); values = histogram->GetHistogram(); EXPECT_TRUE(CheckHistogramHasValue(values.get(), "100-119", 1)); EXPECT_TRUE(CheckHistogramHasValue(values.get(), "200-219", 1)); EXPECT_TRUE(CheckHistogramHasValue(values.get(), "300-319", 1)); histogram = subscriber_->GetHistogramForTesting( StatsEventSubscriber::NETWORK_LATENCY_MS_HISTO); ASSERT_TRUE(histogram); values = histogram->GetHistogram(); EXPECT_TRUE(CheckHistogramHasValue(values.get(), "100-119", 1)); EXPECT_TRUE(CheckHistogramHasValue(values.get(), "200-219", 1)); EXPECT_TRUE(CheckHistogramHasValue(values.get(), "300-319", 1)); histogram = subscriber_->GetHistogramForTesting( StatsEventSubscriber::PACKET_LATENCY_MS_HISTO); ASSERT_TRUE(histogram); values = histogram->GetHistogram(); EXPECT_TRUE(CheckHistogramHasValue(values.get(), "400-419", 3)); histogram = subscriber_->GetHistogramForTesting( StatsEventSubscriber::LATE_FRAME_MS_HISTO); ASSERT_TRUE(histogram); values = histogram->GetHistogram(); EXPECT_TRUE(CheckHistogramHasValue(values.get(), "100-119", 1)); } } // namespace cast } // namespace media