| Index: remoting/protocol/connection_unittest.cc
|
| diff --git a/remoting/protocol/connection_unittest.cc b/remoting/protocol/connection_unittest.cc
|
| index 92a07198aa83e3931e59f34a9e6f0ec60742bf33..03ee5e35694e46be68aadc65ef3ddb955b9fd134 100644
|
| --- a/remoting/protocol/connection_unittest.cc
|
| +++ b/remoting/protocol/connection_unittest.cc
|
| @@ -2,6 +2,8 @@
|
| // Use of this source code is governed by a BSD-style license that can be
|
| // found in the LICENSE file.
|
|
|
| +#define _USE_MATH_DEFINES // For VC++ to get M_PI. This has to be first.
|
| +
|
| #include <utility>
|
|
|
| #include "base/bind.h"
|
| @@ -10,6 +12,10 @@
|
| #include "base/message_loop/message_loop.h"
|
| #include "base/run_loop.h"
|
| #include "remoting/base/constants.h"
|
| +#include "remoting/proto/audio.pb.h"
|
| +#include "remoting/protocol/audio_source.h"
|
| +#include "remoting/protocol/audio_stream.h"
|
| +#include "remoting/protocol/audio_stub.h"
|
| #include "remoting/protocol/fake_session.h"
|
| #include "remoting/protocol/fake_video_renderer.h"
|
| #include "remoting/protocol/ice_connection_to_client.h"
|
| @@ -93,6 +99,137 @@ class TestScreenCapturer : public webrtc::DesktopCapturer {
|
| bool first_frame_sent_ = false;
|
| };
|
|
|
| +static const int kAudioSampleRate = AudioPacket::SAMPLING_RATE_48000;
|
| +static const int kAudioPacketDurationMs = 50;
|
| +static constexpr int kSamplesPerAudioPacket =
|
| + kAudioSampleRate * kAudioPacketDurationMs /
|
| + base::Time::kMillisecondsPerSecond;
|
| +static constexpr base::TimeDelta kAudioPacketDuration =
|
| + base::TimeDelta::FromMilliseconds(kAudioPacketDurationMs);
|
| +
|
| +static const int kAudioChannels = 2;
|
| +
|
| +static const int kTestAudioSignalFrequencyLeftHz = 3000;
|
| +static const int kTestAudioSignalFrequencyRightHz = 2000;
|
| +
|
| +class TestAudioSource : public AudioSource {
|
| + public:
|
| + TestAudioSource() {}
|
| + ~TestAudioSource() override {}
|
| +
|
| + // AudioSource interface.
|
| + bool Start(const PacketCapturedCallback& callback) override {
|
| + callback_ = callback;
|
| + timer_.Start(FROM_HERE, kAudioPacketDuration,
|
| + base::Bind(&TestAudioSource::GenerateAudioSamples,
|
| + base::Unretained(this)));
|
| + return true;
|
| + }
|
| +
|
| + private:
|
| + static int16_t GetSampleValue(double pos, int frequency) {
|
| + const int kMaxSampleValue = 32767;
|
| + return static_cast<int>(
|
| + sin(pos * 2 * M_PI * frequency / kAudioSampleRate) * kMaxSampleValue +
|
| + 0.5);
|
| + }
|
| +
|
| + void GenerateAudioSamples() {
|
| + std::vector<int16_t> data(kSamplesPerAudioPacket * kAudioChannels);
|
| + for (int i = 0; i < kSamplesPerAudioPacket; ++i) {
|
| + data[i * kAudioChannels] = GetSampleValue(
|
| + position_samples_ + i, kTestAudioSignalFrequencyLeftHz);
|
| + data[i * kAudioChannels + 1] = GetSampleValue(
|
| + position_samples_ + i, kTestAudioSignalFrequencyRightHz);
|
| + }
|
| + position_samples_ += kSamplesPerAudioPacket;
|
| +
|
| + std::unique_ptr<AudioPacket> packet(new AudioPacket());
|
| + packet->add_data(reinterpret_cast<char*>(&(data[0])),
|
| + kSamplesPerAudioPacket * kAudioChannels * sizeof(int16_t));
|
| + packet->set_encoding(AudioPacket::ENCODING_RAW);
|
| + packet->set_sampling_rate(AudioPacket::SAMPLING_RATE_48000);
|
| + packet->set_bytes_per_sample(AudioPacket::BYTES_PER_SAMPLE_2);
|
| + packet->set_channels(AudioPacket::CHANNELS_STEREO);
|
| + callback_.Run(std::move(packet));
|
| + }
|
| +
|
| + PacketCapturedCallback callback_;
|
| + base::RepeatingTimer timer_;
|
| + int position_samples_ = 0;
|
| +};
|
| +
|
| +class FakeAudioPlayer : public AudioStub {
|
| + public:
|
| + FakeAudioPlayer() : weak_factory_(this) {}
|
| + ~FakeAudioPlayer() override {}
|
| +
|
| + // AudioStub interface.
|
| + void ProcessAudioPacket(std::unique_ptr<AudioPacket> packet,
|
| + const base::Closure& done) override {
|
| + EXPECT_EQ(AudioPacket::ENCODING_RAW, packet->encoding());
|
| + EXPECT_EQ(AudioPacket::SAMPLING_RATE_48000, packet->sampling_rate());
|
| + EXPECT_EQ(AudioPacket::BYTES_PER_SAMPLE_2, packet->bytes_per_sample());
|
| + EXPECT_EQ(AudioPacket::CHANNELS_STEREO, packet->channels());
|
| +
|
| + data_.insert(data_.end(), packet->data(0).begin(), packet->data(0).end());
|
| +
|
| + if (run_loop_ && data_.size() >= samples_expected_ * 4)
|
| + run_loop_->Quit();
|
| +
|
| + if (!done.is_null())
|
| + done.Run();
|
| + }
|
| +
|
| + void WaitForSamples(size_t samples_expected) {
|
| + samples_expected_ = samples_expected;
|
| + base::RunLoop run_loop;
|
| + run_loop_ = &run_loop;
|
| + run_loop.Run();
|
| + run_loop_ = nullptr;
|
| + }
|
| +
|
| + void Verify() {
|
| + const int16_t* data = reinterpret_cast<const int16_t*>(data_.data());
|
| + int num_samples = data_.size() / kAudioChannels / sizeof(int16_t);
|
| +
|
| + int skipped_samples = 0;
|
| + while (skipped_samples < num_samples &&
|
| + data[skipped_samples * kAudioChannels] == 0 &&
|
| + data[skipped_samples * kAudioChannels + 1] == 0) {
|
| + skipped_samples += kAudioChannels;
|
| + }
|
| +
|
| + // Estimate signal frequency by counting how often it crosses 0.
|
| + int left = 0;
|
| + int right = 0;
|
| + for (int i = skipped_samples + 1; i < num_samples; ++i) {
|
| + if (data[(i - 1) * kAudioChannels] < 0 && data[i * kAudioChannels] >= 0) {
|
| + ++left;
|
| + }
|
| + if (data[(i - 1) * kAudioChannels + 1] < 0 &&
|
| + data[i * kAudioChannels + 1] >= 0) {
|
| + ++right;
|
| + }
|
| + }
|
| + int left_hz = (left * kAudioSampleRate / (num_samples - skipped_samples));
|
| + EXPECT_LE(kTestAudioSignalFrequencyLeftHz - 50, left_hz);
|
| + EXPECT_GE(kTestAudioSignalFrequencyLeftHz + 50, left_hz);
|
| + int right_hz = (right * kAudioSampleRate / (num_samples - skipped_samples));
|
| + EXPECT_LE(kTestAudioSignalFrequencyRightHz - 50, right_hz);
|
| + EXPECT_GE(kTestAudioSignalFrequencyRightHz + 50, right_hz);
|
| + }
|
| +
|
| + base::WeakPtr<AudioStub> GetWeakPtr() { return weak_factory_.GetWeakPtr(); }
|
| +
|
| + private:
|
| + std::vector<char> data_;
|
| + base::RunLoop* run_loop_ = nullptr;
|
| + size_t samples_expected_ = 0;
|
| +
|
| + base::WeakPtrFactory<FakeAudioPlayer> weak_factory_;
|
| +};
|
| +
|
| } // namespace
|
|
|
| class ConnectionTest : public testing::Test,
|
| @@ -140,6 +277,9 @@ class ConnectionTest : public testing::Test,
|
| client_connection_->set_client_stub(&client_stub_);
|
| client_connection_->set_clipboard_stub(&client_clipboard_stub_);
|
| client_connection_->set_video_renderer(&client_video_renderer_);
|
| +
|
| + client_connection_->InitializeAudio(message_loop_.task_runner(),
|
| + client_audio_player_.GetWeakPtr());
|
| }
|
|
|
| void Connect() {
|
| @@ -266,6 +406,7 @@ class ConnectionTest : public testing::Test,
|
| MockClientStub client_stub_;
|
| MockClipboardStub client_clipboard_stub_;
|
| FakeVideoRenderer client_video_renderer_;
|
| + FakeAudioPlayer client_audio_player_;
|
| std::unique_ptr<ConnectionToHost> client_connection_;
|
| FakeSession* client_session_; // Owned by |client_connection_|.
|
| std::unique_ptr<FakeSession> owned_client_session_;
|
| @@ -438,5 +579,16 @@ TEST_P(ConnectionTest, VideoStats) {
|
| EXPECT_TRUE(stats.client_stats.time_rendered <= finish_time);
|
| }
|
|
|
| +TEST_P(ConnectionTest, Audio) {
|
| + Connect();
|
| +
|
| + std::unique_ptr<AudioStream> audio_stream =
|
| + host_connection_->StartAudioStream(base::MakeUnique<TestAudioSource>());
|
| +
|
| + // Wait for 1 second worth of audio samples.
|
| + client_audio_player_.WaitForSamples(kAudioSampleRate * 2);
|
| + client_audio_player_.Verify();
|
| +}
|
| +
|
| } // namespace protocol
|
| } // namespace remoting
|
|
|