diff --git a/webrtc/call/call_perf_tests.cc b/webrtc/call/call_perf_tests.cc index 3ee49d9427..f4422f4433 100644 --- a/webrtc/call/call_perf_tests.cc +++ b/webrtc/call/call_perf_tests.cc @@ -26,6 +26,7 @@ #include "webrtc/system_wrappers/include/rtp_to_ntp.h" #include "webrtc/test/call_test.h" #include "webrtc/test/direct_transport.h" +#include "webrtc/test/drifting_clock.h" #include "webrtc/test/encoder_settings.h" #include "webrtc/test/fake_audio_device.h" #include "webrtc/test/fake_decoder.h" @@ -41,11 +42,18 @@ #include "webrtc/voice_engine/include/voe_rtp_rtcp.h" #include "webrtc/voice_engine/include/voe_video_sync.h" +using webrtc::test::DriftingClock; +using webrtc::test::FakeAudioDevice; + namespace webrtc { class CallPerfTest : public test::CallTest { protected: - void TestAudioVideoSync(bool fec, bool create_audio_first); + void TestAudioVideoSync(bool fec, + bool create_audio_first, + float video_ntp_speed, + float video_rtp_speed, + float audio_rtp_speed); void TestCpuOveruse(LoadObserver::Load tested_load, int encode_delay_ms); @@ -188,7 +196,11 @@ class VideoRtcpAndSyncObserver : public SyncRtcpObserver, public VideoRenderer { int64_t first_time_in_sync_; }; -void CallPerfTest::TestAudioVideoSync(bool fec, bool create_audio_first) { +void CallPerfTest::TestAudioVideoSync(bool fec, + bool create_audio_first, + float video_ntp_speed, + float video_rtp_speed, + float audio_rtp_speed) { const char* kSyncGroup = "av_sync"; const uint32_t kAudioSendSsrc = 1234; const uint32_t kAudioRecvSsrc = 5678; @@ -228,8 +240,8 @@ void CallPerfTest::TestAudioVideoSync(bool fec, bool create_audio_first) { const std::string audio_filename = test::ResourcePath("voice_engine/audio_long16", "pcm"); ASSERT_STRNE("", audio_filename.c_str()); - test::FakeAudioDevice fake_audio_device(Clock::GetRealTimeClock(), - audio_filename); + FakeAudioDevice fake_audio_device(Clock::GetRealTimeClock(), audio_filename, + audio_rtp_speed); EXPECT_EQ(0, voe_base->Init(&fake_audio_device, nullptr)); Config voe_config; voe_config.Set(new VoicePacing(true)); @@ -324,7 +336,8 @@ void CallPerfTest::TestAudioVideoSync(bool fec, bool create_audio_first) { receiver_call_->CreateAudioReceiveStream(audio_recv_config); } - CreateFrameGeneratorCapturer(); + DriftingClock drifting_clock(clock_, video_ntp_speed); + CreateFrameGeneratorCapturerWithDrift(&drifting_clock, video_rtp_speed); Start(); @@ -365,15 +378,49 @@ void CallPerfTest::TestAudioVideoSync(bool fec, bool create_audio_first) { } TEST_F(CallPerfTest, PlaysOutAudioAndVideoInSyncWithAudioCreatedFirst) { - TestAudioVideoSync(false, true); + TestAudioVideoSync(false, true, DriftingClock::kNoDrift, + DriftingClock::kNoDrift, DriftingClock::kNoDrift); } TEST_F(CallPerfTest, PlaysOutAudioAndVideoInSyncWithVideoCreatedFirst) { - TestAudioVideoSync(false, false); + TestAudioVideoSync(false, false, DriftingClock::kNoDrift, + DriftingClock::kNoDrift, DriftingClock::kNoDrift); } TEST_F(CallPerfTest, PlaysOutAudioAndVideoInSyncWithFec) { - TestAudioVideoSync(true, false); + TestAudioVideoSync(true, false, DriftingClock::kNoDrift, + DriftingClock::kNoDrift, DriftingClock::kNoDrift); +} + +// TODO(danilchap): Reenable after adding support for frame capture clock +// that is not in sync with local TickTime clock. +TEST_F(CallPerfTest, DISABLED_PlaysOutAudioAndVideoInSyncWithVideoNtpDrift) { + TestAudioVideoSync(false, true, DriftingClock::PercentsFaster(10.0f), + DriftingClock::kNoDrift, DriftingClock::kNoDrift); +} + +TEST_F(CallPerfTest, PlaysOutAudioAndVideoInSyncWithAudioRtpDrift) { + TestAudioVideoSync(false, true, DriftingClock::kNoDrift, + DriftingClock::kNoDrift, + DriftingClock::PercentsFaster(30.0f)); +} + +TEST_F(CallPerfTest, PlaysOutAudioAndVideoInSyncWithVideoRtpDrift) { + TestAudioVideoSync(false, true, DriftingClock::kNoDrift, + DriftingClock::PercentsFaster(30.0f), + DriftingClock::kNoDrift); +} + +TEST_F(CallPerfTest, PlaysOutAudioAndVideoInSyncWithAudioFasterThanVideoDrift) { + TestAudioVideoSync(false, true, DriftingClock::kNoDrift, + DriftingClock::PercentsSlower(30.0f), + DriftingClock::PercentsFaster(30.0f)); +} + +TEST_F(CallPerfTest, PlaysOutAudioAndVideoInSyncWithVideoFasterThanAudioDrift) { + TestAudioVideoSync(false, true, DriftingClock::kNoDrift, + DriftingClock::PercentsFaster(30.0f), + DriftingClock::PercentsSlower(30.0f)); } void CallPerfTest::TestCaptureNtpTime(const FakeNetworkPipe::Config& net_config, diff --git a/webrtc/test/call_test.cc b/webrtc/test/call_test.cc index e9651e33f5..a0b5234c4c 100644 --- a/webrtc/test/call_test.cc +++ b/webrtc/test/call_test.cc @@ -236,6 +236,14 @@ void CallTest::CreateMatchingReceiveConfigs(Transport* rtcp_send_transport) { } } +void CallTest::CreateFrameGeneratorCapturerWithDrift(Clock* clock, + float speed) { + VideoStream stream = video_encoder_config_.streams.back(); + frame_generator_capturer_.reset(test::FrameGeneratorCapturer::Create( + video_send_stream_->Input(), stream.width, stream.height, + stream.max_framerate * speed, clock)); +} + void CallTest::CreateFrameGeneratorCapturer() { VideoStream stream = video_encoder_config_.streams.back(); frame_generator_capturer_.reset(test::FrameGeneratorCapturer::Create( @@ -245,9 +253,11 @@ void CallTest::CreateFrameGeneratorCapturer() { void CallTest::CreateFakeAudioDevices() { fake_send_audio_device_.reset(new FakeAudioDevice( - clock_, test::ResourcePath("voice_engine/audio_long16", "pcm"))); + clock_, test::ResourcePath("voice_engine/audio_long16", "pcm"), + DriftingClock::kNoDrift)); fake_recv_audio_device_.reset(new FakeAudioDevice( - clock_, test::ResourcePath("voice_engine/audio_long16", "pcm"))); + clock_, test::ResourcePath("voice_engine/audio_long16", "pcm"), + DriftingClock::kNoDrift)); } void CallTest::CreateVideoStreams() { diff --git a/webrtc/test/call_test.h b/webrtc/test/call_test.h index 251d7f6044..327253d241 100644 --- a/webrtc/test/call_test.h +++ b/webrtc/test/call_test.h @@ -71,6 +71,7 @@ class CallTest : public ::testing::Test { Transport* send_transport); void CreateMatchingReceiveConfigs(Transport* rtcp_send_transport); + void CreateFrameGeneratorCapturerWithDrift(Clock* drift_clock, float speed); void CreateFrameGeneratorCapturer(); void CreateFakeAudioDevices(); diff --git a/webrtc/test/drifting_clock.cc b/webrtc/test/drifting_clock.cc new file mode 100644 index 0000000000..76f17235ce --- /dev/null +++ b/webrtc/test/drifting_clock.cc @@ -0,0 +1,57 @@ +/* + * Copyright (c) 2016 The WebRTC project authors. All Rights Reserved. + * + * Use of this source code is governed by a BSD-style license + * that can be found in the LICENSE file in the root of the source + * tree. An additional intellectual property rights grant can be found + * in the file PATENTS. All contributing project authors may + * be found in the AUTHORS file in the root of the source tree. + */ + +#include "webrtc/test/drifting_clock.h" +#include "webrtc/base/checks.h" + +namespace webrtc { +namespace test { +const float DriftingClock::kDoubleSpeed = 2.0f; +const float DriftingClock::kNoDrift = 1.0f; +const float DriftingClock::kHalfSpeed = 0.5f; + +DriftingClock::DriftingClock(Clock* clock, float speed) + : clock_(clock), + drift_(speed - 1.0f), + start_time_(clock_->TimeInMicroseconds()) { + RTC_CHECK(clock); + RTC_CHECK_GT(speed, 0.0f); +} + +float DriftingClock::Drift() const { + int64_t now = clock_->TimeInMicroseconds(); + RTC_DCHECK_GE(now, start_time_); + return (now - start_time_) * drift_; +} + +int64_t DriftingClock::TimeInMilliseconds() const { + return clock_->TimeInMilliseconds() + Drift() / 1000.; +} + +int64_t DriftingClock::TimeInMicroseconds() const { + return clock_->TimeInMicroseconds() + Drift(); +} + +void DriftingClock::CurrentNtp(uint32_t& seconds, uint32_t& fractions) const { + // NTP precision is 1/2^32 seconds, i.e. 2^32 ntp fractions = 1 second. + const double kNtpFracPerMicroSecond = 4294.967296; // = 2^32 / 10^6 + + clock_->CurrentNtp(seconds, fractions); + uint64_t total_fractions = (static_cast(seconds) << 32) | fractions; + total_fractions += Drift() * kNtpFracPerMicroSecond; + seconds = total_fractions >> 32; + fractions = static_cast(total_fractions); +} + +int64_t DriftingClock::CurrentNtpInMilliseconds() const { + return clock_->CurrentNtpInMilliseconds() + Drift() / 1000.; +} +} // namespace test +} // namespace webrtc diff --git a/webrtc/test/drifting_clock.h b/webrtc/test/drifting_clock.h new file mode 100644 index 0000000000..d434186d7d --- /dev/null +++ b/webrtc/test/drifting_clock.h @@ -0,0 +1,45 @@ +/* + * Copyright (c) 2016 The WebRTC project authors. All Rights Reserved. + * + * Use of this source code is governed by a BSD-style license + * that can be found in the LICENSE file in the root of the source + * tree. An additional intellectual property rights grant can be found + * in the file PATENTS. All contributing project authors may + * be found in the AUTHORS file in the root of the source tree. + */ +#ifndef WEBRTC_TEST_DRIFTING_CLOCK_H_ +#define WEBRTC_TEST_DRIFTING_CLOCK_H_ + +#include "webrtc/system_wrappers/include/clock.h" + +namespace webrtc { +namespace test { +class DriftingClock : public Clock { + public: + // TODO(danilchap): Make this constants constexpr when it would be supported. + static const float kDoubleSpeed; // 2.0f; + static const float kNoDrift; // 1.0f; + static const float kHalfSpeed; // 0.5f; + + DriftingClock(Clock* clock, float speed); + + // TODO(danilchap): Make this functions constexpr when it would be supported. + static float PercentsFaster(float percent) { return 1.0f + percent / 100.0f; } + static float PercentsSlower(float percent) { return 1.0f - percent / 100.0f; } + + int64_t TimeInMilliseconds() const override; + int64_t TimeInMicroseconds() const override; + void CurrentNtp(uint32_t& seconds, uint32_t& fractions) const override; + int64_t CurrentNtpInMilliseconds() const override; + + private: + float Drift() const; + + Clock* const clock_; + const float drift_; + const int64_t start_time_; +}; +} // namespace test +} // namespace webrtc + +#endif // WEBRTC_TEST_DRIFTING_CLOCK_H_ diff --git a/webrtc/test/fake_audio_device.cc b/webrtc/test/fake_audio_device.cc index 31cebda652..435c53f878 100644 --- a/webrtc/test/fake_audio_device.cc +++ b/webrtc/test/fake_audio_device.cc @@ -22,13 +22,16 @@ namespace webrtc { namespace test { -FakeAudioDevice::FakeAudioDevice(Clock* clock, const std::string& filename) +FakeAudioDevice::FakeAudioDevice(Clock* clock, + const std::string& filename, + float speed) : audio_callback_(NULL), capturing_(false), captured_audio_(), playout_buffer_(), + speed_(speed), last_playout_ms_(-1), - clock_(clock), + clock_(clock, speed), tick_(EventTimerWrapper::Create()), thread_(FakeAudioDevice::Run, this, "FakeAudioDevice"), file_utility_(new ModuleFileUtility(0)), @@ -51,7 +54,7 @@ int32_t FakeAudioDevice::Init() { if (file_utility_->InitPCMReading(*input_stream_.get()) != 0) return -1; - if (!tick_->StartTimer(true, 10)) + if (!tick_->StartTimer(true, 10 / speed_)) return -1; thread_.Start(); thread_.SetPriority(rtc::kHighPriority); @@ -107,7 +110,7 @@ void FakeAudioDevice::CaptureAudio() { false, new_mic_level)); size_t samples_needed = kFrequencyHz / 100; - int64_t now_ms = clock_->TimeInMilliseconds(); + int64_t now_ms = clock_.TimeInMilliseconds(); uint32_t time_since_last_playout_ms = now_ms - last_playout_ms_; if (last_playout_ms_ > 0 && time_since_last_playout_ms > 0) { samples_needed = std::min( diff --git a/webrtc/test/fake_audio_device.h b/webrtc/test/fake_audio_device.h index ce480c1554..180abf6c92 100644 --- a/webrtc/test/fake_audio_device.h +++ b/webrtc/test/fake_audio_device.h @@ -16,6 +16,7 @@ #include "webrtc/base/platform_thread.h" #include "webrtc/base/scoped_ptr.h" #include "webrtc/modules/audio_device/include/fake_audio_device.h" +#include "webrtc/test/drifting_clock.h" #include "webrtc/typedefs.h" namespace webrtc { @@ -29,7 +30,7 @@ namespace test { class FakeAudioDevice : public FakeAudioDeviceModule { public: - FakeAudioDevice(Clock* clock, const std::string& filename); + FakeAudioDevice(Clock* clock, const std::string& filename, float speed); virtual ~FakeAudioDevice(); @@ -54,9 +55,10 @@ class FakeAudioDevice : public FakeAudioDeviceModule { bool capturing_; int8_t captured_audio_[kBufferSizeBytes]; int8_t playout_buffer_[kBufferSizeBytes]; + const float speed_; int64_t last_playout_ms_; - Clock* clock_; + DriftingClock clock_; rtc::scoped_ptr tick_; rtc::CriticalSection lock_; rtc::PlatformThread thread_; diff --git a/webrtc/test/webrtc_test_common.gyp b/webrtc/test/webrtc_test_common.gyp index 07ea2c9b74..318f5bbea4 100644 --- a/webrtc/test/webrtc_test_common.gyp +++ b/webrtc/test/webrtc_test_common.gyp @@ -22,6 +22,8 @@ 'constants.h', 'direct_transport.cc', 'direct_transport.h', + 'drifting_clock.cc', + 'drifting_clock.h', 'encoder_settings.cc', 'encoder_settings.h', 'fake_audio_device.cc',