blob: 15c60a83b5dedf5f3a44210a702580479e23a3c7 [file] [log] [blame]
/*
* Copyright (c) 2015 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 <memory>
#include "testing/gmock/include/gmock/gmock.h"
#include "webrtc/modules/audio_coding/neteq/tools/neteq_external_decoder_test.h"
#include "webrtc/modules/audio_coding/neteq/tools/rtp_generator.h"
#include "webrtc/modules/include/module_common_types.h"
namespace webrtc {
namespace test {
using ::testing::_;
using ::testing::SetArgPointee;
using ::testing::Return;
class MockAudioDecoder final : public AudioDecoder {
public:
// TODO(nisse): Valid overrides commented out, because the gmock
// methods don't use any override declarations, and we want to avoid
// warnings from -Winconsistent-missing-override. See
// http://crbug.com/428099.
static const int kPacketDuration = 960; // 48 kHz * 20 ms
MockAudioDecoder(int sample_rate_hz, size_t num_channels)
: sample_rate_hz_(sample_rate_hz),
num_channels_(num_channels),
fec_enabled_(false) {}
~MockAudioDecoder() /* override */ { Die(); }
MOCK_METHOD0(Die, void());
MOCK_METHOD0(Reset, void());
int PacketDuration(const uint8_t* encoded,
size_t encoded_len) const /* override */ {
return kPacketDuration;
}
int PacketDurationRedundant(const uint8_t* encoded,
size_t encoded_len) const /* override */ {
return kPacketDuration;
}
bool PacketHasFec(
const uint8_t* encoded, size_t encoded_len) const /* override */ {
return fec_enabled_;
}
int SampleRateHz() const /* override */ { return sample_rate_hz_; }
size_t Channels() const /* override */ { return num_channels_; }
void set_fec_enabled(bool enable_fec) { fec_enabled_ = enable_fec; }
bool fec_enabled() const { return fec_enabled_; }
protected:
// Override the following methods such that no actual payload is needed.
int DecodeInternal(const uint8_t* encoded,
size_t encoded_len,
int /*sample_rate_hz*/,
int16_t* decoded,
SpeechType* speech_type) /* override */ {
*speech_type = kSpeech;
memset(decoded, 0, sizeof(int16_t) * kPacketDuration * Channels());
return kPacketDuration * Channels();
}
int DecodeRedundantInternal(const uint8_t* encoded,
size_t encoded_len,
int sample_rate_hz,
int16_t* decoded,
SpeechType* speech_type) /* override */ {
return DecodeInternal(encoded, encoded_len, sample_rate_hz, decoded,
speech_type);
}
private:
const int sample_rate_hz_;
const size_t num_channels_;
bool fec_enabled_;
};
class NetEqNetworkStatsTest : public NetEqExternalDecoderTest {
public:
static const int kPayloadSizeByte = 30;
static const int kFrameSizeMs = 20;
enum logic {
kIgnore,
kEqual,
kSmallerThan,
kLargerThan,
};
struct NetEqNetworkStatsCheck {
logic current_buffer_size_ms;
logic preferred_buffer_size_ms;
logic jitter_peaks_found;
logic packet_loss_rate;
logic packet_discard_rate;
logic expand_rate;
logic speech_expand_rate;
logic preemptive_rate;
logic accelerate_rate;
logic secondary_decoded_rate;
logic clockdrift_ppm;
logic added_zero_samples;
NetEqNetworkStatistics stats_ref;
};
NetEqNetworkStatsTest(NetEqDecoder codec,
int sample_rate_hz,
MockAudioDecoder* decoder)
: NetEqExternalDecoderTest(codec, sample_rate_hz, decoder),
external_decoder_(decoder),
samples_per_ms_(sample_rate_hz / 1000),
frame_size_samples_(kFrameSizeMs * samples_per_ms_),
rtp_generator_(new test::RtpGenerator(samples_per_ms_)),
last_lost_time_(0),
packet_loss_interval_(0xffffffff) {
Init();
}
bool Lost(uint32_t send_time) {
if (send_time - last_lost_time_ >= packet_loss_interval_) {
last_lost_time_ = send_time;
return true;
}
return false;
}
void SetPacketLossRate(double loss_rate) {
packet_loss_interval_ = (loss_rate >= 1e-3 ?
static_cast<double>(kFrameSizeMs) / loss_rate : 0xffffffff);
}
// |stats_ref|
// expects.x = -1, do not care
// expects.x = 0, 'x' in current stats should equal 'x' in |stats_ref|
// expects.x = 1, 'x' in current stats should < 'x' in |stats_ref|
// expects.x = 2, 'x' in current stats should > 'x' in |stats_ref|
void CheckNetworkStatistics(NetEqNetworkStatsCheck expects) {
NetEqNetworkStatistics stats;
neteq()->NetworkStatistics(&stats);
#define CHECK_NETEQ_NETWORK_STATS(x)\
switch (expects.x) {\
case kEqual:\
EXPECT_EQ(stats.x, expects.stats_ref.x);\
break;\
case kSmallerThan:\
EXPECT_LT(stats.x, expects.stats_ref.x);\
break;\
case kLargerThan:\
EXPECT_GT(stats.x, expects.stats_ref.x);\
break;\
default:\
break;\
}
CHECK_NETEQ_NETWORK_STATS(current_buffer_size_ms);
CHECK_NETEQ_NETWORK_STATS(preferred_buffer_size_ms);
CHECK_NETEQ_NETWORK_STATS(jitter_peaks_found);
CHECK_NETEQ_NETWORK_STATS(packet_loss_rate);
CHECK_NETEQ_NETWORK_STATS(packet_discard_rate);
CHECK_NETEQ_NETWORK_STATS(expand_rate);
CHECK_NETEQ_NETWORK_STATS(speech_expand_rate);
CHECK_NETEQ_NETWORK_STATS(preemptive_rate);
CHECK_NETEQ_NETWORK_STATS(accelerate_rate);
CHECK_NETEQ_NETWORK_STATS(secondary_decoded_rate);
CHECK_NETEQ_NETWORK_STATS(clockdrift_ppm);
CHECK_NETEQ_NETWORK_STATS(added_zero_samples);
#undef CHECK_NETEQ_NETWORK_STATS
// Compare with CurrentDelay, which should be identical.
EXPECT_EQ(stats.current_buffer_size_ms, neteq()->CurrentDelayMs());
}
void RunTest(int num_loops, NetEqNetworkStatsCheck expects) {
uint32_t time_now;
uint32_t next_send_time;
// Initiate |last_lost_time_|.
time_now = next_send_time = last_lost_time_ =
rtp_generator_->GetRtpHeader(kPayloadType, frame_size_samples_,
&rtp_header_);
for (int k = 0; k < num_loops; ++k) {
// Delay by one frame such that the FEC can come in.
while (time_now + kFrameSizeMs >= next_send_time) {
next_send_time = rtp_generator_->GetRtpHeader(kPayloadType,
frame_size_samples_,
&rtp_header_);
if (!Lost(next_send_time)) {
static const uint8_t payload[kPayloadSizeByte] = {0};
InsertPacket(rtp_header_, payload, next_send_time);
}
}
GetOutputAudio(&output_frame_);
time_now += kOutputLengthMs;
}
CheckNetworkStatistics(expects);
neteq()->FlushBuffers();
}
void DecodeFecTest() {
external_decoder_->set_fec_enabled(false);
NetEqNetworkStatsCheck expects = {
kIgnore, // current_buffer_size_ms
kIgnore, // preferred_buffer_size_ms
kIgnore, // jitter_peaks_found
kEqual, // packet_loss_rate
kEqual, // packet_discard_rate
kEqual, // expand_rate
kEqual, // voice_expand_rate
kIgnore, // preemptive_rate
kEqual, // accelerate_rate
kEqual, // decoded_fec_rate
kIgnore, // clockdrift_ppm
kEqual, // added_zero_samples
{0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}
};
RunTest(50, expects);
// Next we introduce packet losses.
SetPacketLossRate(0.1);
expects.stats_ref.packet_loss_rate = 1337;
expects.stats_ref.expand_rate = expects.stats_ref.speech_expand_rate = 1065;
RunTest(50, expects);
// Next we enable FEC.
external_decoder_->set_fec_enabled(true);
// If FEC fills in the lost packets, no packet loss will be counted.
expects.stats_ref.packet_loss_rate = 0;
expects.stats_ref.expand_rate = expects.stats_ref.speech_expand_rate = 0;
expects.stats_ref.secondary_decoded_rate = 2006;
RunTest(50, expects);
}
void NoiseExpansionTest() {
NetEqNetworkStatsCheck expects = {
kIgnore, // current_buffer_size_ms
kIgnore, // preferred_buffer_size_ms
kIgnore, // jitter_peaks_found
kEqual, // packet_loss_rate
kEqual, // packet_discard_rate
kEqual, // expand_rate
kEqual, // speech_expand_rate
kIgnore, // preemptive_rate
kEqual, // accelerate_rate
kEqual, // decoded_fec_rate
kIgnore, // clockdrift_ppm
kEqual, // added_zero_samples
{0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}
};
RunTest(50, expects);
SetPacketLossRate(1);
expects.stats_ref.expand_rate = 16384;
expects.stats_ref.speech_expand_rate = 5324;
RunTest(10, expects); // Lost 10 * 20ms in a row.
}
private:
MockAudioDecoder* external_decoder_;
const int samples_per_ms_;
const size_t frame_size_samples_;
std::unique_ptr<test::RtpGenerator> rtp_generator_;
WebRtcRTPHeader rtp_header_;
uint32_t last_lost_time_;
uint32_t packet_loss_interval_;
AudioFrame output_frame_;
};
TEST(NetEqNetworkStatsTest, DecodeFec) {
MockAudioDecoder decoder(48000, 1);
NetEqNetworkStatsTest test(NetEqDecoder::kDecoderOpus, 48000, &decoder);
test.DecodeFecTest();
EXPECT_CALL(decoder, Die()).Times(1);
}
TEST(NetEqNetworkStatsTest, StereoDecodeFec) {
MockAudioDecoder decoder(48000, 2);
NetEqNetworkStatsTest test(NetEqDecoder::kDecoderOpus, 48000, &decoder);
test.DecodeFecTest();
EXPECT_CALL(decoder, Die()).Times(1);
}
TEST(NetEqNetworkStatsTest, NoiseExpansionTest) {
MockAudioDecoder decoder(48000, 1);
NetEqNetworkStatsTest test(NetEqDecoder::kDecoderOpus, 48000, &decoder);
test.NoiseExpansionTest();
EXPECT_CALL(decoder, Die()).Times(1);
}
} // namespace test
} // namespace webrtc