Index: remoting/codec/audio_encoder_opus_unittest.cc |
diff --git a/remoting/codec/audio_encoder_opus_unittest.cc b/remoting/codec/audio_encoder_opus_unittest.cc |
new file mode 100644 |
index 0000000000000000000000000000000000000000..810a8b0c0c58b45378b25890499305aa30acdaa5 |
--- /dev/null |
+++ b/remoting/codec/audio_encoder_opus_unittest.cc |
@@ -0,0 +1,166 @@ |
+// Copyright (c) 2012 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. |
+ |
+// MSVC++ requires this to get M_PI. |
+#define _USE_MATH_DEFINES |
+#include <math.h> |
+ |
+#include "remoting/codec/audio_encoder_opus.h" |
+ |
+#include "base/logging.h" |
+#include "remoting/codec/audio_decoder_opus.h" |
+#include "testing/gtest/include/gtest/gtest.h" |
+ |
+namespace remoting { |
+ |
+namespace { |
+ |
+const int kChannels = 2; |
+const double kChannelPhaseShift = 2 * M_PI / 3; |
Wez
2012/10/22 22:50:21
nit: Add comments explaining what this is used for
Sergey Ulanov
2012/10/23 00:43:50
Done.
|
+const AudioPacket_SamplingRate kDefaultSamplingRate = |
+ AudioPacket::SAMPLING_RATE_48000; |
+const int kMaxLatencyMs = 40; |
+ |
+// When verifying results ignore the first 1k samples. This is necessary because |
+// it takes some time for the codec to adjust for the input signal. |
+const int kSkippedFirstSamples = 1000; |
+ |
+} // namespace |
+ |
+class OpusAudioEncoderTest : public testing::Test { |
+ public: |
+ |
+ static int16 GetSampleValue( |
+ AudioPacket::SamplingRate rate, |
+ double frequency, |
Wez
2012/10/22 22:50:21
nit: Specify units.
Sergey Ulanov
2012/10/23 00:43:50
Done.
|
+ double pos, |
Wez
2012/10/22 22:50:21
nit: How can we have sub-sample position?
Sergey Ulanov
2012/10/23 00:43:50
That's used when comparing results - GetSignalShif
|
+ int channel) { |
+ double angle = pos * 2 * M_PI * frequency / rate + |
+ kChannelPhaseShift * channel; |
+ return static_cast<int>(sin(angle) * 32767 + 0.5); |
+ } |
+ |
+ scoped_ptr<AudioPacket> CreatePacket( |
+ int samples, |
+ AudioPacket::SamplingRate rate, |
+ double frequency, |
+ int pos) { |
+ |
+ std::vector<int16> data(samples * kChannels); |
Wez
2012/10/22 22:50:21
nit: Add a comment to explain that we're filling t
Sergey Ulanov
2012/10/23 00:43:50
Done.
|
+ for (int i = 0; i < samples; ++i) { |
+ data[i * kChannels] = GetSampleValue(rate, frequency, i + pos, 0); |
+ data[i * kChannels + 1] = GetSampleValue(rate, frequency, i + pos, 1); |
+ } |
+ |
+ scoped_ptr<AudioPacket> packet(new AudioPacket()); |
+ packet->add_data(reinterpret_cast<char*>(&(data[0])), |
+ samples * kChannels * sizeof(int16)); |
+ packet->set_encoding(AudioPacket::ENCODING_RAW); |
+ packet->set_sampling_rate(rate); |
+ packet->set_bytes_per_sample(AudioPacket::BYTES_PER_SAMPLE_2); |
+ packet->set_channels(AudioPacket::CHANNELS_STEREO); |
+ return packet.Pass(); |
+ } |
+ |
+ // Decoded data is normally shifted in phase relative to the raw date. This |
Wez
2012/10/22 22:50:21
typo: date -> data
Sergey Ulanov
2012/10/23 00:43:50
Done.
|
+ // function returns approximate the phase difference by finding the first |
Wez
2012/10/22 22:50:21
typo: approximate the phase ... -> ... the approxi
Sergey Ulanov
2012/10/23 00:43:50
Done.
|
+ // point when signal goes from negative to positive. |
+ double GetSignalShift(const std::vector<int16>& received_data) { |
Wez
2012/10/22 22:50:21
nit: So this function is supposed to be getting th
Sergey Ulanov
2012/10/23 00:43:50
Renamed to EstimateSignalShift(). I don't think it
|
+ for (size_t i = kSkippedFirstSamples; |
+ i < received_data.size() / kChannels - 1; i++) { |
+ int16 this_sample = received_data[i * kChannels]; |
+ int16 next_sample = received_data[(i + 1) * kChannels]; |
+ if (this_sample < 0 && next_sample > 0) { |
+ return i + |
+ static_cast<double>(-this_sample) / (next_sample - this_sample); |
+ } |
+ } |
+ return 0; |
+ } |
+ |
+ void ValidateReceivedData(int samples, |
+ AudioPacket::SamplingRate rate, |
+ double frequency, |
+ const std::vector<int16>& received_data) { |
Wez
2012/10/22 22:50:21
nit: Add a comment explaining that we validate the
Sergey Ulanov
2012/10/23 00:43:50
Done.
|
+ double shift = GetSignalShift(received_data); |
+ double diff_sqare_sum = 0; |
+ for (size_t i = kSkippedFirstSamples; |
+ i < received_data.size() / kChannels; i++) { |
+ double d = received_data[i * kChannels] - |
+ GetSampleValue(rate, frequency, i - shift, 0); |
+ diff_sqare_sum += d * d; |
+ d = received_data[i * kChannels + 1] - |
+ GetSampleValue(rate, frequency, i - shift, 1); |
+ diff_sqare_sum += d * d; |
+ } |
+ double averate_difference = sqrt(diff_sqare_sum / received_data.size()); |
Wez
2012/10/22 22:50:21
typo: average_difference
Wez
2012/10/22 22:50:21
nit: Clarify the units you're using for the differ
Sergey Ulanov
2012/10/23 00:43:50
Done.
Sergey Ulanov
2012/10/23 00:43:50
Done.
|
+ EXPECT_LE(averate_difference, 5000); |
Wez
2012/10/22 22:50:21
nit: Consider moving 5000 to a constant, above
Sergey Ulanov
2012/10/23 00:43:50
Done.
|
+ } |
+ |
+ void ResetEncoder() { |
Wez
2012/10/22 22:50:21
nit: ResetEncoder -> Reset or ResetEncoderAndDecod
Sergey Ulanov
2012/10/23 00:43:50
Removed this function.
|
+ encoder_.reset(new AudioEncoderOpus()); |
+ decoder_.reset(new AudioDecoderOpus()); |
+ } |
+ |
+ void TestEncoderDecoder(int packet_size, |
Wez
2012/10/22 22:50:21
typo: TestEncodeDecode
Sergey Ulanov
2012/10/23 00:43:50
Done.
|
+ double frequency, |
+ AudioPacket::SamplingRate rate) { |
+ const int kTotalTestSamples = 24000; |
+ ResetEncoder(); |
+ |
+ std::vector<int16> received_data; |
+ int pos = 0; |
+ for (; pos < kTotalTestSamples; pos += packet_size) { |
+ scoped_ptr<AudioPacket> source_packet = |
+ CreatePacket(packet_size, rate, frequency, pos); |
+ scoped_ptr<AudioPacket> encoded = encoder_->Encode(source_packet.Pass()); |
Wez
2012/10/22 22:50:21
Line too long?
Sergey Ulanov
2012/10/23 00:43:50
Done.
|
+ if (encoded.get()) { |
Wez
2012/10/22 22:50:21
EXPECT(encoded.get())?
Sergey Ulanov
2012/10/23 00:43:50
Encoder is allowed to return NULL packet sometimes
|
+ scoped_ptr<AudioPacket> decoded = decoder_->Decode(encoded.Pass()); |
+ EXPECT_EQ(kDefaultSamplingRate, decoded->sampling_rate()); |
+ for (int i = 0; i < decoded->data_size(); ++i) { |
+ const int16* data = |
+ reinterpret_cast<const int16*>(decoded->data(i).data()); |
+ received_data.insert( |
+ received_data.end(), data, |
+ data + decoded->data(i).size() / sizeof(int16)); |
+ } |
+ } |
+ } |
+ |
+ // Verify that at most kMaxLatencyMs worth of samples is buffered inside |
+ // |encoder_| and |decoder_|. |
+ EXPECT_GE(static_cast<int>(received_data.size()) / kChannels, |
+ pos - rate * kMaxLatencyMs / 1000); |
+ |
+ ValidateReceivedData(packet_size, kDefaultSamplingRate, |
+ frequency, received_data); |
+ } |
+ |
+ protected: |
+ scoped_ptr<AudioEncoderOpus> encoder_; |
+ scoped_ptr<AudioDecoderOpus> decoder_; |
+}; |
+ |
+TEST_F(OpusAudioEncoderTest, CreateAndDestroy) { |
+} |
+ |
+TEST_F(OpusAudioEncoderTest, NoResampling) { |
+ TestEncoderDecoder(2000, 50, AudioPacket::SAMPLING_RATE_48000); |
+ TestEncoderDecoder(2000, 3000, AudioPacket::SAMPLING_RATE_48000); |
+ TestEncoderDecoder(2000, 10000, AudioPacket::SAMPLING_RATE_48000); |
+} |
+ |
+TEST_F(OpusAudioEncoderTest, Resampling) { |
+ TestEncoderDecoder(2000, 50, AudioPacket::SAMPLING_RATE_44100); |
+ TestEncoderDecoder(2000, 3000, AudioPacket::SAMPLING_RATE_44100); |
+ TestEncoderDecoder(2000, 10000, AudioPacket::SAMPLING_RATE_44100); |
+} |
+ |
+TEST_F(OpusAudioEncoderTest, BufferSize) { |
Wez
2012/10/22 22:50:21
nit: BufferingAndResampling / BufferSizeAndResampl
Sergey Ulanov
2012/10/23 00:43:50
Done.
|
+ TestEncoderDecoder(500, 3000, AudioPacket::SAMPLING_RATE_44100); |
+ TestEncoderDecoder(1000, 3000, AudioPacket::SAMPLING_RATE_44100); |
+ TestEncoderDecoder(5000, 3000, AudioPacket::SAMPLING_RATE_44100); |
Wez
2012/10/22 22:50:21
How long to these tests take?
Sergey Ulanov
2012/10/23 00:43:50
About 0.6 seconds total for all tests in this file
|
+} |
+ |
+} // namespace remoting |