Chromium Code Reviews
chromiumcodereview-hr@appspot.gserviceaccount.com (chromiumcodereview-hr) | Please choose your nickname with Settings | Help | Chromium Project | Gerrit Changes | Sign out
(205)

Unified Diff: remoting/codec/audio_encoder_opus_unittest.cc

Issue 11189047: Add opus audio codec support in remoting (Closed) Base URL: svn://svn.chromium.org/chrome/trunk/src
Patch Set: Created 8 years, 2 months ago
Use n/p to move between diff chunks; N/P to move between comments. Draft comments are only viewable by you.
Jump to:
View side-by-side diff with in-line comments
Download patch
« no previous file with comments | « remoting/codec/audio_encoder_opus.cc ('k') | remoting/host/client_session.cc » ('j') | no next file with comments »
Expand Comments ('e') | Collapse Comments ('c') | Show Comments Hide Comments ('s')
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..b32bbecd901a7cd957a184bd3c5220844f5b8f1c
--- /dev/null
+++ b/remoting/codec/audio_encoder_opus_unittest.cc
@@ -0,0 +1,189 @@
+// 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 {
+
+// Maximum value that can be encoded in a 16-bit signed sample.
+const int kMaxSampleValue = 32767;
+
+const int kChannels = 2;
+
+// Phase shift between left and right channels.
+const double kChannelPhaseShift = 2 * M_PI / 3;
+
+// The sampling rate that OPUS uses internally and that we expect to get
+// from the decoder.
+const AudioPacket_SamplingRate kDefaultSamplingRate =
+ AudioPacket::SAMPLING_RATE_48000;
+
+// Maximum latency expected from the encoder.
+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;
+
+// Maximum standard deviation of the difference between original and decoded
+// signals as a proportion of kMaxSampleValue. For two unrelated signals this
+// difference will be close to 1.0, even for signals that differ only slightly.
+// The value is chosen such that all the tests pass normally, but fail with
+// small changes (e.g. one sample shift between signals).
+const double kMaxSignalDeviation = 0.1;
+
+} // namespace
+
+class OpusAudioEncoderTest : public testing::Test {
+ public:
+ // Return test signal value at the specified position |pos|. |frequency_hz|
+ // defines frequency of the signal. |channel| is used to calculate phase shift
+ // of the signal, so that different signals are generated for left and right
+ // channels.
+ static int16 GetSampleValue(
+ AudioPacket::SamplingRate rate,
+ double frequency_hz,
+ double pos,
+ int channel) {
+ double angle = pos * 2 * M_PI * frequency_hz / rate +
+ kChannelPhaseShift * channel;
+ return static_cast<int>(sin(angle) * kMaxSampleValue + 0.5);
+ }
+
+ // Creates audio packet filled with a test signal with the specified
+ // |frequency_hz|.
+ scoped_ptr<AudioPacket> CreatePacket(
+ int samples,
+ AudioPacket::SamplingRate rate,
+ double frequency_hz,
+ int pos) {
+ std::vector<int16> data(samples * kChannels);
+ for (int i = 0; i < samples; ++i) {
+ data[i * kChannels] = GetSampleValue(rate, frequency_hz, i + pos, 0);
+ data[i * kChannels + 1] = GetSampleValue(rate, frequency_hz, 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 original signal.
+ // This function returns the approximate shift in samples by finding the first
+ // point when signal goes from negative to positive.
+ double EstimateSignalShift(const std::vector<int16>& received_data) {
+ 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;
+ }
+
+ // Compares decoded signal with the test signal that was encoded. It estimates
+ // phase shift from the original signal, then calculates standard deviation of
+ // the difference between original and decoded signals.
+ void ValidateReceivedData(int samples,
+ AudioPacket::SamplingRate rate,
+ double frequency_hz,
+ const std::vector<int16>& received_data) {
+ double shift = EstimateSignalShift(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_hz, i - shift, 0);
+ diff_sqare_sum += d * d;
+ d = received_data[i * kChannels + 1] -
+ GetSampleValue(rate, frequency_hz, i - shift, 1);
+ diff_sqare_sum += d * d;
+ }
+ double deviation = sqrt(diff_sqare_sum / received_data.size())
+ / kMaxSampleValue;
+ EXPECT_LE(deviation, kMaxSignalDeviation);
+ }
+
+ void TestEncodeDecode(int packet_size,
+ double frequency_hz,
+ AudioPacket::SamplingRate rate) {
+ const int kTotalTestSamples = 24000;
+
+ encoder_.reset(new AudioEncoderOpus());
+ decoder_.reset(new AudioDecoderOpus());
+
+ std::vector<int16> received_data;
+ int pos = 0;
+ for (; pos < kTotalTestSamples; pos += packet_size) {
+ scoped_ptr<AudioPacket> source_packet =
+ CreatePacket(packet_size, rate, frequency_hz, pos);
+ scoped_ptr<AudioPacket> encoded =
+ encoder_->Encode(source_packet.Pass());
+ if (encoded.get()) {
+ 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_hz, received_data);
+ }
+
+ protected:
+ scoped_ptr<AudioEncoderOpus> encoder_;
+ scoped_ptr<AudioDecoderOpus> decoder_;
+};
+
+TEST_F(OpusAudioEncoderTest, CreateAndDestroy) {
+}
+
+TEST_F(OpusAudioEncoderTest, NoResampling) {
+ TestEncodeDecode(2000, 50, AudioPacket::SAMPLING_RATE_48000);
+ TestEncodeDecode(2000, 3000, AudioPacket::SAMPLING_RATE_48000);
+ TestEncodeDecode(2000, 10000, AudioPacket::SAMPLING_RATE_48000);
+}
+
+TEST_F(OpusAudioEncoderTest, Resampling) {
+ TestEncodeDecode(2000, 50, AudioPacket::SAMPLING_RATE_44100);
+ TestEncodeDecode(2000, 3000, AudioPacket::SAMPLING_RATE_44100);
+ TestEncodeDecode(2000, 10000, AudioPacket::SAMPLING_RATE_44100);
+}
+
+TEST_F(OpusAudioEncoderTest, BufferSizeAndResampling) {
+ TestEncodeDecode(500, 3000, AudioPacket::SAMPLING_RATE_44100);
+ TestEncodeDecode(1000, 3000, AudioPacket::SAMPLING_RATE_44100);
+ TestEncodeDecode(5000, 3000, AudioPacket::SAMPLING_RATE_44100);
+}
+
+} // namespace remoting
« no previous file with comments | « remoting/codec/audio_encoder_opus.cc ('k') | remoting/host/client_session.cc » ('j') | no next file with comments »

Powered by Google App Engine
This is Rietveld 408576698