* Copyright (c) 2012 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 "modules/audio_coding/neteq/neteq_impl.h"
#include <memory>
#include <utility>
#include <vector>
#include "api/audio_codecs/builtin_audio_decoder_factory.h"
#include "api/neteq/default_neteq_controller_factory.h"
#include "api/neteq/neteq.h"
#include "api/neteq/neteq_controller.h"
#include "modules/audio_coding/codecs/g711/audio_decoder_pcm.h"
#include "modules/audio_coding/neteq/accelerate.h"
#include "modules/audio_coding/neteq/decision_logic.h"
#include "modules/audio_coding/neteq/default_neteq_factory.h"
#include "modules/audio_coding/neteq/expand.h"
#include "modules/audio_coding/neteq/histogram.h"
#include "modules/audio_coding/neteq/mock/mock_decoder_database.h"
#include "modules/audio_coding/neteq/mock/mock_dtmf_buffer.h"
#include "modules/audio_coding/neteq/mock/mock_dtmf_tone_generator.h"
#include "modules/audio_coding/neteq/mock/mock_neteq_controller.h"
#include "modules/audio_coding/neteq/mock/mock_packet_buffer.h"
#include "modules/audio_coding/neteq/mock/mock_red_payload_splitter.h"
#include "modules/audio_coding/neteq/preemptive_expand.h"
#include "modules/audio_coding/neteq/statistics_calculator.h"
#include "modules/audio_coding/neteq/sync_buffer.h"
#include "modules/audio_coding/neteq/timestamp_scaler.h"
#include "rtc_base/numerics/safe_conversions.h"
#include "system_wrappers/include/clock.h"
#include "test/audio_decoder_proxy_factory.h"
#include "test/function_audio_decoder_factory.h"
#include "test/gmock.h"
#include "test/gtest.h"
#include "test/mock_audio_decoder.h"
#include "test/mock_audio_decoder_factory.h"
using ::testing::_;
using ::testing::AtLeast;
using ::testing::DoAll;
using ::testing::ElementsAre;
using ::testing::InSequence;
using ::testing::Invoke;
using ::testing::IsEmpty;
using ::testing::IsNull;
using ::testing::Pointee;
using ::testing::Return;
using ::testing::ReturnNull;
using ::testing::SetArgPointee;
using ::testing::SetArrayArgument;
using ::testing::SizeIs;
using ::testing::WithArg;
namespace webrtc {
int DeletePacketsAndReturnOk(PacketList* packet_list) {
packet_list->clear();
return PacketBuffer::kOK;
}
class NetEqImplTest : public ::testing::Test {
protected:
NetEqImplTest() : clock_(0) { config_.sample_rate_hz = 8000; }
void CreateInstance(
const rtc::scoped_refptr<AudioDecoderFactory>& decoder_factory) {
ASSERT_TRUE(decoder_factory);
config_.enable_muted_state = enable_muted_state_;
NetEqImpl::Dependencies deps(config_, &clock_, decoder_factory,
DefaultNetEqControllerFactory());
tick_timer_ = deps.tick_timer.get();
if (use_mock_decoder_database_) {
std::unique_ptr<MockDecoderDatabase> mock(new MockDecoderDatabase);
mock_decoder_database_ = mock.get();
EXPECT_CALL(*mock_decoder_database_, GetActiveCngDecoder())
.WillOnce(ReturnNull());
deps.decoder_database = std::move(mock);
}
decoder_database_ = deps.decoder_database.get();
if (use_mock_dtmf_buffer_) {
std::unique_ptr<MockDtmfBuffer> mock(
new MockDtmfBuffer(config_.sample_rate_hz));
mock_dtmf_buffer_ = mock.get();
deps.dtmf_buffer = std::move(mock);
}
dtmf_buffer_ = deps.dtmf_buffer.get();
if (use_mock_dtmf_tone_generator_) {
std::unique_ptr<MockDtmfToneGenerator> mock(new MockDtmfToneGenerator);
mock_dtmf_tone_generator_ = mock.get();
deps.dtmf_tone_generator = std::move(mock);
}
dtmf_tone_generator_ = deps.dtmf_tone_generator.get();
if (use_mock_packet_buffer_) {
std::unique_ptr<MockPacketBuffer> mock(
new MockPacketBuffer(config_.max_packets_in_buffer, tick_timer_));
mock_packet_buffer_ = mock.get();
deps.packet_buffer = std::move(mock);
}
packet_buffer_ = deps.packet_buffer.get();
if (use_mock_neteq_controller_) {
std::unique_ptr<MockNetEqController> mock(new MockNetEqController());
mock_neteq_controller_ = mock.get();
deps.neteq_controller = std::move(mock);
} else {
deps.stats = std::make_unique<StatisticsCalculator>();
NetEqController::Config controller_config;
controller_config.tick_timer = tick_timer_;
controller_config.base_min_delay_ms = config_.min_delay_ms;
controller_config.allow_time_stretching = true;
controller_config.max_packets_in_buffer = config_.max_packets_in_buffer;
controller_config.clock = &clock_;
deps.neteq_controller =
std::make_unique<DecisionLogic>(std::move(controller_config));
}
neteq_controller_ = deps.neteq_controller.get();
if (use_mock_payload_splitter_) {
std::unique_ptr<MockRedPayloadSplitter> mock(new MockRedPayloadSplitter);
mock_payload_splitter_ = mock.get();
deps.red_payload_splitter = std::move(mock);
}
red_payload_splitter_ = deps.red_payload_splitter.get();
deps.timestamp_scaler = std::unique_ptr<TimestampScaler>(
new TimestampScaler(*deps.decoder_database.get()));
neteq_.reset(new NetEqImpl(config_, std::move(deps)));
ASSERT_TRUE(neteq_ != NULL);
}
void CreateInstance() { CreateInstance(CreateBuiltinAudioDecoderFactory()); }
void UseNoMocks() {
ASSERT_TRUE(neteq_ == NULL) << "Must call UseNoMocks before CreateInstance";
use_mock_decoder_database_ = false;
use_mock_neteq_controller_ = false;
use_mock_dtmf_buffer_ = false;
use_mock_dtmf_tone_generator_ = false;
use_mock_packet_buffer_ = false;
use_mock_payload_splitter_ = false;
}
virtual ~NetEqImplTest() {
if (use_mock_decoder_database_) {
EXPECT_CALL(*mock_decoder_database_, Die()).Times(1);
}
if (use_mock_neteq_controller_) {
EXPECT_CALL(*mock_neteq_controller_, Die()).Times(1);
}
if (use_mock_dtmf_buffer_) {
EXPECT_CALL(*mock_dtmf_buffer_, Die()).Times(1);
}
if (use_mock_dtmf_tone_generator_) {
EXPECT_CALL(*mock_dtmf_tone_generator_, Die()).Times(1);
}
if (use_mock_packet_buffer_) {
EXPECT_CALL(*mock_packet_buffer_, Die()).Times(1);
}
}
void TestDtmfPacket(int sample_rate_hz) {
const size_t kPayloadLength = 4;
const uint8_t kPayloadType = 110;
const int kSampleRateHz = 16000;
config_.sample_rate_hz = kSampleRateHz;
UseNoMocks();
CreateInstance();
uint8_t payload[kPayloadLength] = {0x02, 0x80 + 0x11, 0x10, 0xF0};
RTPHeader rtp_header;
rtp_header.payloadType = kPayloadType;
rtp_header.sequenceNumber = 0x1234;
rtp_header.timestamp = 0x12345678;
rtp_header.ssrc = 0x87654321;
EXPECT_TRUE(neteq_->RegisterPayloadType(
kPayloadType, SdpAudioFormat("telephone-event", sample_rate_hz, 1)));
EXPECT_EQ(NetEq::kOK, neteq_->InsertPacket(rtp_header, payload));
const size_t kMaxOutputSize =
static_cast<size_t>(10 * kSampleRateHz / 1000);
AudioFrame output;
bool muted;
EXPECT_EQ(NetEq::kOK, neteq_->GetAudio(&output, &muted));
ASSERT_FALSE(muted);
ASSERT_EQ(kMaxOutputSize, output.samples_per_channel_);
EXPECT_EQ(1u, output.num_channels_);
EXPECT_EQ(AudioFrame::kNormalSpeech, output.speech_type_);
EXPECT_THAT(output.packet_infos_, IsEmpty());
const std::vector<int16_t> kOutput(
{0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
-1578, -2816, -3460, -3403, -2709, -1594, -363, 671, 1269, 1328,
908, 202, -513, -964, -955, -431, 504, 1617, 2602, 3164,
3101, 2364, 1073, -511, -2047, -3198, -3721, -3525, -2688, -1440,
-99, 1015, 1663, 1744, 1319, 588, -171, -680, -747, -315,
515, 1512, 2378, 2828, 2674, 1877, 568, -986, -2446, -3482,
-3864, -3516, -2534, -1163});
ASSERT_GE(kMaxOutputSize, kOutput.size());
EXPECT_TRUE(std::equal(kOutput.begin(), kOutput.end(), output.data()));
}
std::unique_ptr<NetEqImpl> neteq_;
NetEq::Config config_;
SimulatedClock clock_;
TickTimer* tick_timer_ = nullptr;
MockDecoderDatabase* mock_decoder_database_ = nullptr;
DecoderDatabase* decoder_database_ = nullptr;
bool use_mock_decoder_database_ = true;
MockNetEqController* mock_neteq_controller_ = nullptr;
NetEqController* neteq_controller_ = nullptr;
bool use_mock_neteq_controller_ = true;
MockDtmfBuffer* mock_dtmf_buffer_ = nullptr;
DtmfBuffer* dtmf_buffer_ = nullptr;
bool use_mock_dtmf_buffer_ = true;
MockDtmfToneGenerator* mock_dtmf_tone_generator_ = nullptr;
DtmfToneGenerator* dtmf_tone_generator_ = nullptr;
bool use_mock_dtmf_tone_generator_ = true;
MockPacketBuffer* mock_packet_buffer_ = nullptr;
PacketBuffer* packet_buffer_ = nullptr;
bool use_mock_packet_buffer_ = true;
MockRedPayloadSplitter* mock_payload_splitter_ = nullptr;
RedPayloadSplitter* red_payload_splitter_ = nullptr;
bool use_mock_payload_splitter_ = true;
bool enable_muted_state_ = false;
};
TEST(NetEq, CreateAndDestroy) {
NetEq::Config config;
SimulatedClock clock(0);
auto decoder_factory = CreateBuiltinAudioDecoderFactory();
std::unique_ptr<NetEq> neteq =
DefaultNetEqFactory().CreateNetEq(config, decoder_factory, &clock);
}
TEST_F(NetEqImplTest, RegisterPayloadType) {
CreateInstance();
constexpr int rtp_payload_type = 0;
const SdpAudioFormat format("pcmu", 8000, 1);
EXPECT_CALL(*mock_decoder_database_,
RegisterPayload(rtp_payload_type, format));
neteq_->RegisterPayloadType(rtp_payload_type, format);
}
TEST_F(NetEqImplTest, RemovePayloadType) {
CreateInstance();
uint8_t rtp_payload_type = 0;
EXPECT_CALL(*mock_decoder_database_, Remove(rtp_payload_type))
.WillOnce(Return(DecoderDatabase::kDecoderNotFound));
EXPECT_EQ(NetEq::kOK, neteq_->RemovePayloadType(rtp_payload_type));
}
TEST_F(NetEqImplTest, RemoveAllPayloadTypes) {
CreateInstance();
EXPECT_CALL(*mock_decoder_database_, RemoveAll()).WillOnce(Return());
neteq_->RemoveAllPayloadTypes();
}
TEST_F(NetEqImplTest, InsertPacket) {
using ::testing::AllOf;
using ::testing::Field;
CreateInstance();
const size_t kPayloadLength = 100;
const uint8_t kPayloadType = 0;
const uint16_t kFirstSequenceNumber = 0x1234;
const uint32_t kFirstTimestamp = 0x12345678;
const uint32_t kSsrc = 0x87654321;
uint8_t payload[kPayloadLength] = {0};
RTPHeader rtp_header;
rtp_header.payloadType = kPayloadType;
rtp_header.sequenceNumber = kFirstSequenceNumber;
rtp_header.timestamp = kFirstTimestamp;
rtp_header.ssrc = kSsrc;
Packet fake_packet;
fake_packet.payload_type = kPayloadType;
fake_packet.sequence_number = kFirstSequenceNumber;
fake_packet.timestamp = kFirstTimestamp;
auto mock_decoder_factory = rtc::make_ref_counted<MockAudioDecoderFactory>();
EXPECT_CALL(*mock_decoder_factory, MakeAudioDecoderMock(_, _, _))
.WillOnce(Invoke([&](const SdpAudioFormat& format,
absl::optional<AudioCodecPairId> codec_pair_id,
std::unique_ptr<AudioDecoder>* dec) {
EXPECT_EQ("pcmu", format.name);
std::unique_ptr<MockAudioDecoder> mock_decoder(new MockAudioDecoder);
EXPECT_CALL(*mock_decoder, Channels()).WillRepeatedly(Return(1));
EXPECT_CALL(*mock_decoder, SampleRateHz()).WillRepeatedly(Return(8000));
EXPECT_CALL(*mock_decoder, Die()).Times(1);
*dec = std::move(mock_decoder);
}));
DecoderDatabase::DecoderInfo info(SdpAudioFormat("pcmu", 8000, 1),
absl::nullopt, mock_decoder_factory.get());
EXPECT_CALL(*mock_decoder_database_, GetDecoderInfo(kPayloadType))
.WillRepeatedly(Return(&info));
EXPECT_CALL(*mock_packet_buffer_, Empty())
.WillOnce(Return(false));
EXPECT_CALL(*mock_packet_buffer_, Flush(_)).Times(1);
EXPECT_CALL(*mock_packet_buffer_, InsertPacketList(_, _, _, _, _, _, _, _))
.Times(2)
.WillRepeatedly(DoAll(SetArgPointee<2>(kPayloadType),
WithArg<0>(Invoke(DeletePacketsAndReturnOk))));
EXPECT_CALL(*mock_packet_buffer_, PeekNextPacket())
.Times(1)
.WillOnce(Return(&fake_packet));
EXPECT_CALL(*mock_dtmf_buffer_, Flush()).Times(1);
{
InSequence sequence;
EXPECT_CALL(
*mock_neteq_controller_,
PacketArrived(
8000,
_,
AllOf(
Field(&NetEqController::PacketArrivedInfo::is_cng_or_dtmf,
false),
Field(&NetEqController::PacketArrivedInfo::main_sequence_number,
kFirstSequenceNumber),
Field(&NetEqController::PacketArrivedInfo::main_timestamp,
kFirstTimestamp))));
EXPECT_CALL(
*mock_neteq_controller_,
PacketArrived(
8000,
_,
AllOf(
Field(&NetEqController::PacketArrivedInfo::is_cng_or_dtmf,
false),
Field(&NetEqController::PacketArrivedInfo::main_sequence_number,
kFirstSequenceNumber + 1),
Field(&NetEqController::PacketArrivedInfo::main_timestamp,
kFirstTimestamp + 160))));
}
neteq_->InsertPacket(rtp_header, payload);
rtp_header.timestamp += 160;
rtp_header.sequenceNumber += 1;
neteq_->InsertPacket(rtp_header, payload);
}
TEST_F(NetEqImplTest, InsertPacketsUntilBufferIsFull) {
UseNoMocks();
CreateInstance();
const int kPayloadLengthSamples = 80;
const size_t kPayloadLengthBytes = 2 * kPayloadLengthSamples;
const uint8_t kPayloadType = 17;
uint8_t payload[kPayloadLengthBytes] = {0};
RTPHeader rtp_header;
rtp_header.payloadType = kPayloadType;
rtp_header.sequenceNumber = 0x1234;
rtp_header.timestamp = 0x12345678;
rtp_header.ssrc = 0x87654321;
EXPECT_TRUE(neteq_->RegisterPayloadType(kPayloadType,
SdpAudioFormat("l16", 8000, 1)));
for (size_t i = 1; i <= config_.max_packets_in_buffer; ++i) {
EXPECT_EQ(NetEq::kOK, neteq_->InsertPacket(rtp_header, payload));
rtp_header.timestamp += kPayloadLengthSamples;
rtp_header.sequenceNumber += 1;
EXPECT_EQ(i, packet_buffer_->NumPacketsInBuffer());
}
EXPECT_EQ(NetEq::kOK, neteq_->InsertPacket(rtp_header, payload));
EXPECT_EQ(1u, packet_buffer_->NumPacketsInBuffer());
const Packet* test_packet = packet_buffer_->PeekNextPacket();
EXPECT_EQ(rtp_header.timestamp, test_packet->timestamp);
EXPECT_EQ(rtp_header.sequenceNumber, test_packet->sequence_number);
}
TEST_F(NetEqImplTest, TestDtmfPacketAVT) {
TestDtmfPacket(8000);
}
TEST_F(NetEqImplTest, TestDtmfPacketAVT16kHz) {
TestDtmfPacket(16000);
}
TEST_F(NetEqImplTest, TestDtmfPacketAVT32kHz) {
TestDtmfPacket(32000);
}
TEST_F(NetEqImplTest, TestDtmfPacketAVT48kHz) {
TestDtmfPacket(48000);
}
TEST_F(NetEqImplTest, VerifyTimestampPropagation) {
const uint8_t kPayloadType = 17;
const int kSampleRateHz = 8000;
const size_t kPayloadLengthSamples =
static_cast<size_t>(10 * kSampleRateHz / 1000);
const size_t kPayloadLengthBytes = kPayloadLengthSamples;
uint8_t payload[kPayloadLengthBytes] = {0};
RTPHeader rtp_header;
rtp_header.payloadType = kPayloadType;
rtp_header.sequenceNumber = 0x1234;
rtp_header.timestamp = 0x12345678;
rtp_header.ssrc = 0x87654321;
rtp_header.numCSRCs = 3;
rtp_header.arrOfCSRCs[0] = 43;
rtp_header.arrOfCSRCs[1] = 65;
rtp_header.arrOfCSRCs[2] = 17;
class CountingSamplesDecoder : public AudioDecoder {
public:
CountingSamplesDecoder() : next_value_(1) {}
int DecodeInternal(const uint8_t* encoded,
size_t encoded_len,
int ,
int16_t* decoded,
SpeechType* speech_type) override {
for (size_t i = 0; i < encoded_len; ++i) {
decoded[i] = next_value_++;
}
*speech_type = kSpeech;
return rtc::checked_cast<int>(encoded_len);
}
void Reset() override { next_value_ = 1; }
int SampleRateHz() const override { return kSampleRateHz; }
size_t Channels() const override { return 1; }
uint16_t next_value() const { return next_value_; }
private:
int16_t next_value_;
} decoder_;
auto decoder_factory =
rtc::make_ref_counted<test::AudioDecoderProxyFactory>(&decoder_);
UseNoMocks();
CreateInstance(decoder_factory);
EXPECT_TRUE(neteq_->RegisterPayloadType(kPayloadType,
SdpAudioFormat("L16", 8000, 1)));
clock_.AdvanceTimeMilliseconds(123456);
Timestamp expected_receive_time = clock_.CurrentTime();
EXPECT_EQ(NetEq::kOK, neteq_->InsertPacket(rtp_header, payload));
const size_t kMaxOutputSize = static_cast<size_t>(10 * kSampleRateHz / 1000);
AudioFrame output;
bool muted;
EXPECT_EQ(NetEq::kOK, neteq_->GetAudio(&output, &muted));
ASSERT_FALSE(muted);
ASSERT_EQ(kMaxOutputSize, output.samples_per_channel_);
EXPECT_EQ(1u, output.num_channels_);
EXPECT_EQ(AudioFrame::kNormalSpeech, output.speech_type_);
ASSERT_THAT(output.packet_infos_, SizeIs(1));
{
const auto& packet_info = output.packet_infos_[0];
EXPECT_EQ(packet_info.ssrc(), rtp_header.ssrc);
EXPECT_THAT(packet_info.csrcs(), ElementsAre(43, 65, 17));
EXPECT_EQ(packet_info.rtp_timestamp(), rtp_header.timestamp);
EXPECT_FALSE(packet_info.audio_level().has_value());
EXPECT_EQ(packet_info.receive_time(), expected_receive_time);
}
EXPECT_EQ(kPayloadLengthSamples,
static_cast<size_t>(decoder_.next_value() - 1));
EXPECT_EQ(
absl::optional<uint32_t>(rtp_header.timestamp +
output.data()[output.samples_per_channel_ - 1]),
neteq_->GetPlayoutTimestamp());
const SyncBuffer* sync_buffer = neteq_->sync_buffer_for_test();
ASSERT_TRUE(sync_buffer != NULL);
EXPECT_EQ(rtp_header.timestamp + kPayloadLengthSamples,
sync_buffer->end_timestamp());
EXPECT_EQ(
kPayloadLengthSamples - output.data()[output.samples_per_channel_ - 1],
sync_buffer->FutureLength());
}
TEST_F(NetEqImplTest, ReorderedPacket) {
UseNoMocks();
MockAudioDecoder mock_decoder;
CreateInstance(
rtc::make_ref_counted<test::AudioDecoderProxyFactory>(&mock_decoder));
const uint8_t kPayloadType = 17;
const int kSampleRateHz = 8000;
const size_t kPayloadLengthSamples =
static_cast<size_t>(10 * kSampleRateHz / 1000);
const size_t kPayloadLengthBytes = kPayloadLengthSamples;
uint8_t payload[kPayloadLengthBytes] = {0};
RTPHeader rtp_header;
rtp_header.payloadType = kPayloadType;
rtp_header.sequenceNumber = 0x1234;
rtp_header.timestamp = 0x12345678;
rtp_header.ssrc = 0x87654321;
rtp_header.extension.hasAudioLevel = true;
rtp_header.extension.audioLevel = 42;
EXPECT_CALL(mock_decoder, Reset()).WillRepeatedly(Return());
EXPECT_CALL(mock_decoder, SampleRateHz())
.WillRepeatedly(Return(kSampleRateHz));
EXPECT_CALL(mock_decoder, Channels()).WillRepeatedly(Return(1));
EXPECT_CALL(mock_decoder, PacketDuration(_, kPayloadLengthBytes))
.WillRepeatedly(Return(rtc::checked_cast<int>(kPayloadLengthSamples)));
int16_t dummy_output[kPayloadLengthSamples] = {0};
EXPECT_CALL(mock_decoder, DecodeInternal(Pointee(0), kPayloadLengthBytes,
kSampleRateHz, _, _))
.WillOnce(DoAll(SetArrayArgument<3>(dummy_output,
dummy_output + kPayloadLengthSamples),
SetArgPointee<4>(AudioDecoder::kSpeech),
Return(rtc::checked_cast<int>(kPayloadLengthSamples))));
EXPECT_TRUE(neteq_->RegisterPayloadType(kPayloadType,
SdpAudioFormat("L16", 8000, 1)));
clock_.AdvanceTimeMilliseconds(123456);
Timestamp expected_receive_time = clock_.CurrentTime();
EXPECT_EQ(NetEq::kOK, neteq_->InsertPacket(rtp_header, payload));
const size_t kMaxOutputSize = static_cast<size_t>(10 * kSampleRateHz / 1000);
AudioFrame output;
bool muted;
EXPECT_EQ(NetEq::kOK, neteq_->GetAudio(&output, &muted));
ASSERT_EQ(kMaxOutputSize, output.samples_per_channel_);
EXPECT_EQ(1u, output.num_channels_);
EXPECT_EQ(AudioFrame::kNormalSpeech, output.speech_type_);
ASSERT_THAT(output.packet_infos_, SizeIs(1));
{
const auto& packet_info = output.packet_infos_[0];
EXPECT_EQ(packet_info.ssrc(), rtp_header.ssrc);
EXPECT_THAT(packet_info.csrcs(), IsEmpty());
EXPECT_EQ(packet_info.rtp_timestamp(), rtp_header.timestamp);
EXPECT_EQ(packet_info.audio_level(), rtp_header.extension.audioLevel);
EXPECT_EQ(packet_info.receive_time(), expected_receive_time);
}
rtp_header.sequenceNumber -= 1;
rtp_header.timestamp -= kPayloadLengthSamples;
rtp_header.extension.audioLevel = 1;
payload[0] = 1;
clock_.AdvanceTimeMilliseconds(1000);
EXPECT_EQ(NetEq::kOK, neteq_->InsertPacket(rtp_header, payload));
rtp_header.sequenceNumber += 2;
rtp_header.timestamp += 2 * kPayloadLengthSamples;
rtp_header.extension.audioLevel = 2;
payload[0] = 2;
clock_.AdvanceTimeMilliseconds(2000);
expected_receive_time = clock_.CurrentTime();
EXPECT_EQ(NetEq::kOK, neteq_->InsertPacket(rtp_header, payload));
EXPECT_CALL(mock_decoder, DecodeInternal(Pointee(2), kPayloadLengthBytes,
kSampleRateHz, _, _))
.WillOnce(DoAll(SetArrayArgument<3>(dummy_output,
dummy_output + kPayloadLengthSamples),
SetArgPointee<4>(AudioDecoder::kSpeech),
Return(rtc::checked_cast<int>(kPayloadLengthSamples))));
EXPECT_EQ(NetEq::kOK, neteq_->GetAudio(&output, &muted));
ASSERT_EQ(kMaxOutputSize, output.samples_per_channel_);
EXPECT_EQ(1u, output.num_channels_);
EXPECT_EQ(AudioFrame::kNormalSpeech, output.speech_type_);
EXPECT_TRUE(packet_buffer_->Empty());
EXPECT_EQ(1u, neteq_->GetLifetimeStatistics().packets_discarded);
ASSERT_THAT(output.packet_infos_, SizeIs(1));
{
const auto& packet_info = output.packet_infos_[0];
EXPECT_EQ(packet_info.ssrc(), rtp_header.ssrc);
EXPECT_THAT(packet_info.csrcs(), IsEmpty());
EXPECT_EQ(packet_info.rtp_timestamp(), rtp_header.timestamp);
EXPECT_EQ(packet_info.audio_level(), rtp_header.extension.audioLevel);
EXPECT_EQ(packet_info.receive_time(), expected_receive_time);
}
EXPECT_CALL(mock_decoder, Die());
}
TEST_F(NetEqImplTest, FirstPacketUnknown) {
UseNoMocks();
CreateInstance();
const uint8_t kPayloadType = 17;
const int kSampleRateHz = 8000;
const size_t kPayloadLengthSamples =
static_cast<size_t>(10 * kSampleRateHz / 1000);
const size_t kPayloadLengthBytes = kPayloadLengthSamples * 2;
uint8_t payload[kPayloadLengthBytes] = {0};
RTPHeader rtp_header;
rtp_header.payloadType = kPayloadType;
rtp_header.sequenceNumber = 0x1234;
rtp_header.timestamp = 0x12345678;
rtp_header.ssrc = 0x87654321;
EXPECT_EQ(NetEq::kFail, neteq_->InsertPacket(rtp_header, payload));
const size_t kMaxOutputSize = static_cast<size_t>(10 * kSampleRateHz / 1000);
AudioFrame output;
bool muted;
EXPECT_EQ(NetEq::kOK, neteq_->GetAudio(&output, &muted));
ASSERT_LE(output.samples_per_channel_, kMaxOutputSize);
EXPECT_EQ(kMaxOutputSize, output.samples_per_channel_);
EXPECT_EQ(1u, output.num_channels_);
EXPECT_EQ(AudioFrame::kPLC, output.speech_type_);
EXPECT_THAT(output.packet_infos_, IsEmpty());
EXPECT_TRUE(neteq_->RegisterPayloadType(kPayloadType,
SdpAudioFormat("l16", 8000, 1)));
for (size_t i = 0; i < 10; ++i) {
rtp_header.sequenceNumber++;
rtp_header.timestamp += kPayloadLengthSamples;
EXPECT_EQ(NetEq::kOK, neteq_->InsertPacket(rtp_header, payload));
EXPECT_EQ(i + 1, packet_buffer_->NumPacketsInBuffer());
}
for (size_t i = 0; i < 3; ++i) {
EXPECT_EQ(NetEq::kOK, neteq_->GetAudio(&output, &muted));
ASSERT_LE(output.samples_per_channel_, kMaxOutputSize);
EXPECT_EQ(kMaxOutputSize, output.samples_per_channel_);
EXPECT_EQ(1u, output.num_channels_);
EXPECT_EQ(AudioFrame::kNormalSpeech, output.speech_type_)
<< "NetEq did not decode the packets as expected.";
EXPECT_THAT(output.packet_infos_, SizeIs(1));
}
}
std::vector<uint8_t> CreateRedPayload(size_t num_payloads,
int payload_type,
int payload_size,
int timestamp_offset) {
constexpr int kRedHeaderLength = 4;
const size_t size =
payload_size + 1 + (num_payloads - 1) * (payload_size + kRedHeaderLength);
std::vector<uint8_t> payload(size, 0);
uint8_t* payload_ptr = payload.data();
for (size_t i = 0; i < num_payloads; ++i) {
if (i == num_payloads - 1) {
*payload_ptr = payload_type & 0x7F;
++payload_ptr;
break;
}
*payload_ptr = payload_type & 0x7F;
*payload_ptr |= 0x80;
++payload_ptr;
const int this_offset =
rtc::checked_cast<int>((num_payloads - i - 1) * timestamp_offset);
*payload_ptr = this_offset >> 6;
++payload_ptr;
RTC_DCHECK_LE(payload_size, 1023);
*payload_ptr = ((this_offset & 0x3F) << 2) | (payload_size >> 8);
++payload_ptr;
*payload_ptr = payload_size & 0xFF;
++payload_ptr;
}
return payload;
}
TEST_F(NetEqImplTest, InsertRedPayload) {
UseNoMocks();
CreateInstance();
constexpr int kRedPayloadType = 7;
neteq_->RegisterPayloadType(kRedPayloadType, SdpAudioFormat("red", 8000, 1));
constexpr int kPayloadType = 8;
neteq_->RegisterPayloadType(kPayloadType, SdpAudioFormat("l16", 8000, 1));
size_t frame_size = 80;
size_t payload_size = frame_size * 2;
std::vector<uint8_t> payload =
CreateRedPayload(3, kPayloadType, payload_size, frame_size);
RTPHeader header;
header.payloadType = kRedPayloadType;
header.sequenceNumber = 0x1234;
header.timestamp = 0x12345678;
header.ssrc = 0x87654321;
AbsoluteCaptureTime capture_time;
capture_time.absolute_capture_timestamp = 1234;
header.extension.absolute_capture_time = capture_time;
header.extension.audioLevel = 12;
header.extension.hasAudioLevel = true;
header.numCSRCs = 1;
header.arrOfCSRCs[0] = 123;
neteq_->InsertPacket(header, payload);
AudioFrame frame;
bool muted;
neteq_->GetAudio(&frame, &muted);
constexpr int kNetEqFixedDelay = 5;
EXPECT_EQ(frame.timestamp_,
header.timestamp - frame_size * 2 - kNetEqFixedDelay);
EXPECT_TRUE(frame.packet_infos_.empty());
neteq_->GetAudio(&frame, &muted);
EXPECT_EQ(frame.timestamp_, header.timestamp - frame_size - kNetEqFixedDelay);
EXPECT_TRUE(frame.packet_infos_.empty());
neteq_->GetAudio(&frame, &muted);
EXPECT_EQ(frame.timestamp_, header.timestamp - kNetEqFixedDelay);
EXPECT_EQ(frame.packet_infos_.size(), 1u);
EXPECT_EQ(frame.packet_infos_.front().absolute_capture_time(), capture_time);
EXPECT_EQ(frame.packet_infos_.front().audio_level(),
header.extension.audioLevel);
EXPECT_EQ(frame.packet_infos_.front().csrcs()[0], header.arrOfCSRCs[0]);
}
class NetEqImplTestSampleRateParameter
: public NetEqImplTest,
public testing::WithParamInterface<int> {
protected:
NetEqImplTestSampleRateParameter()
: NetEqImplTest(), initial_sample_rate_hz_(GetParam()) {
config_.sample_rate_hz = initial_sample_rate_hz_;
}
const int initial_sample_rate_hz_;
};
class NetEqImplTestSdpFormatParameter
: public NetEqImplTest,
public testing::WithParamInterface<SdpAudioFormat> {
protected:
NetEqImplTestSdpFormatParameter()
: NetEqImplTest(), sdp_format_(GetParam()) {}
const SdpAudioFormat sdp_format_;
};
TEST_P(NetEqImplTestSampleRateParameter,
NoAudioInterruptionLoggedBeforeFirstDecode) {
UseNoMocks();
CreateInstance();
const uint8_t kPayloadType = 17;
const int kPayloadSampleRateHz = 16000;
const size_t kPayloadLengthSamples =
static_cast<size_t>(10 * kPayloadSampleRateHz / 1000);
const size_t kPayloadLengthBytes = kPayloadLengthSamples * 2;
uint8_t payload[kPayloadLengthBytes] = {0};
RTPHeader rtp_header;
rtp_header.payloadType = kPayloadType;
rtp_header.sequenceNumber = 0x1234;
rtp_header.timestamp = 0x12345678;
rtp_header.ssrc = 0x87654321;
EXPECT_TRUE(neteq_->RegisterPayloadType(
kPayloadType, SdpAudioFormat("l16", kPayloadSampleRateHz, 1)));
const size_t initial_output_size =
static_cast<size_t>(10 * initial_sample_rate_hz_ / 1000);
AudioFrame output;
bool muted;
for (int i = 0; i < 100; ++i) {
EXPECT_EQ(NetEq::kOK, neteq_->GetAudio(&output, &muted));
EXPECT_EQ(initial_output_size, output.samples_per_channel_);
EXPECT_EQ(1u, output.num_channels_);
EXPECT_NE(AudioFrame::kNormalSpeech, output.speech_type_);
EXPECT_THAT(output.packet_infos_, IsEmpty());
}
auto insert_packet = [&]() {
rtp_header.sequenceNumber++;
rtp_header.timestamp += kPayloadLengthSamples;
EXPECT_EQ(NetEq::kOK, neteq_->InsertPacket(rtp_header, payload));
};
for (size_t i = 0; i < 10; ++i) {
insert_packet();
EXPECT_EQ(i + 1, packet_buffer_->NumPacketsInBuffer());
}
constexpr size_t kOutputSize =
static_cast<size_t>(10 * kPayloadSampleRateHz / 1000);
for (size_t i = 0; i < 3; ++i) {
EXPECT_EQ(NetEq::kOK, neteq_->GetAudio(&output, &muted));
EXPECT_EQ(kOutputSize, output.samples_per_channel_);
EXPECT_EQ(1u, output.num_channels_);
EXPECT_EQ(AudioFrame::kNormalSpeech, output.speech_type_)
<< "NetEq did not decode the packets as expected.";
EXPECT_THAT(output.packet_infos_, SizeIs(1));
}
auto lifetime_stats = neteq_->GetLifetimeStatistics();
EXPECT_EQ(0, lifetime_stats.interruption_count);
size_t count_loops = 0;
while (output.speech_type_ == AudioFrame::kNormalSpeech) {
ASSERT_LT(++count_loops, 100u);
EXPECT_EQ(NetEq::kOK, neteq_->GetAudio(&output, &muted));
}
for (size_t i = 0; i < 5; ++i) {
insert_packet();
}
while (output.speech_type_ != AudioFrame::kNormalSpeech) {
ASSERT_LT(++count_loops, 100u);
EXPECT_EQ(NetEq::kOK, neteq_->GetAudio(&output, &muted));
}
lifetime_stats = neteq_->GetLifetimeStatistics();
EXPECT_EQ(0, lifetime_stats.interruption_count);
}
TEST_P(NetEqImplTestSampleRateParameter, AudioInterruptionLogged) {
UseNoMocks();
CreateInstance();
const uint8_t kPayloadType = 17;
const int kPayloadSampleRateHz = 16000;
const size_t kPayloadLengthSamples =
static_cast<size_t>(10 * kPayloadSampleRateHz / 1000);
const size_t kPayloadLengthBytes = kPayloadLengthSamples * 2;
uint8_t payload[kPayloadLengthBytes] = {0};
RTPHeader rtp_header;
rtp_header.payloadType = kPayloadType;
rtp_header.sequenceNumber = 0x1234;
rtp_header.timestamp = 0x12345678;
rtp_header.ssrc = 0x87654321;
EXPECT_TRUE(neteq_->RegisterPayloadType(
kPayloadType, SdpAudioFormat("l16", kPayloadSampleRateHz, 1)));
auto insert_packet = [&]() {
rtp_header.sequenceNumber++;
rtp_header.timestamp += kPayloadLengthSamples;
EXPECT_EQ(NetEq::kOK, neteq_->InsertPacket(rtp_header, payload));
};
for (size_t i = 0; i < 10; ++i) {
insert_packet();
EXPECT_EQ(i + 1, packet_buffer_->NumPacketsInBuffer());
}
AudioFrame output;
bool muted;
size_t count_loops = 0;
do {
ASSERT_LT(++count_loops, 100u);
EXPECT_EQ(NetEq::kOK, neteq_->GetAudio(&output, &muted));
} while (output.speech_type_ == AudioFrame::kNormalSpeech);
for (size_t i = 0; i < 15; ++i) {
EXPECT_EQ(NetEq::kOK, neteq_->GetAudio(&output, &muted));
EXPECT_NE(AudioFrame::kNormalSpeech, output.speech_type_);
}
for (size_t i = 0; i < 5; ++i) {
insert_packet();
}
while (output.speech_type_ != AudioFrame::kNormalSpeech) {
ASSERT_LT(++count_loops, 100u);
EXPECT_EQ(NetEq::kOK, neteq_->GetAudio(&output, &muted));
}
auto lifetime_stats = neteq_->GetLifetimeStatistics();
EXPECT_EQ(1, lifetime_stats.interruption_count);
EXPECT_GT(lifetime_stats.total_interruption_duration_ms, 150);
EXPECT_LT(lifetime_stats.total_interruption_duration_ms, 170);
}
INSTANTIATE_TEST_SUITE_P(SampleRates,
NetEqImplTestSampleRateParameter,
testing::Values(8000, 16000, 32000, 48000));
TEST_P(NetEqImplTestSdpFormatParameter, GetNackListScaledTimestamp) {
UseNoMocks();
CreateInstance();
neteq_->EnableNack(128);
const uint8_t kPayloadType = 17;
const int kPayloadSampleRateHz = sdp_format_.clockrate_hz;
const size_t kPayloadLengthSamples =
static_cast<size_t>(10 * kPayloadSampleRateHz / 1000);
const size_t kPayloadLengthBytes = kPayloadLengthSamples * 2;
std::vector<uint8_t> payload(kPayloadLengthBytes, 0);
RTPHeader rtp_header;
rtp_header.payloadType = kPayloadType;
rtp_header.sequenceNumber = 0x1234;
rtp_header.timestamp = 0x12345678;
rtp_header.ssrc = 0x87654321;
EXPECT_TRUE(neteq_->RegisterPayloadType(kPayloadType, sdp_format_));
auto insert_packet = [&](bool lost = false) {
rtp_header.sequenceNumber++;
rtp_header.timestamp += kPayloadLengthSamples;
if (!lost)
EXPECT_EQ(NetEq::kOK, neteq_->InsertPacket(rtp_header, payload));
};
for (size_t i = 0; i < 10; ++i) {
insert_packet();
}
AudioFrame output;
size_t count_loops = 0;
do {
bool muted;
ASSERT_LT(++count_loops, 100u);
EXPECT_EQ(NetEq::kOK, neteq_->GetAudio(&output, &muted));
} while (output.speech_type_ == AudioFrame::kNormalSpeech);
insert_packet();
insert_packet(true);
for (int i = 0; i < 5; ++i) {
insert_packet();
}
EXPECT_THAT(neteq_->GetNackList(5), Not(IsEmpty()));
EXPECT_THAT(neteq_->GetNackList(500), IsEmpty());
}
INSTANTIATE_TEST_SUITE_P(GetNackList,
NetEqImplTestSdpFormatParameter,
testing::Values(SdpAudioFormat("g722", 8000, 1),
SdpAudioFormat("opus", 48000, 2)));
TEST_F(NetEqImplTest, CodecInternalCng) {
UseNoMocks();
MockAudioDecoder mock_decoder;
CreateInstance(
rtc::make_ref_counted<test::AudioDecoderProxyFactory>(&mock_decoder));
const uint8_t kPayloadType = 17;
const int kSampleRateKhz = 48;
const size_t kPayloadLengthSamples =
static_cast<size_t>(20 * kSampleRateKhz);
const size_t kPayloadLengthBytes = 10;
uint8_t payload[kPayloadLengthBytes] = {0};
int16_t dummy_output[kPayloadLengthSamples] = {0};
RTPHeader rtp_header;
rtp_header.payloadType = kPayloadType;
rtp_header.sequenceNumber = 0x1234;
rtp_header.timestamp = 0x12345678;
rtp_header.ssrc = 0x87654321;
EXPECT_CALL(mock_decoder, Reset()).WillRepeatedly(Return());
EXPECT_CALL(mock_decoder, SampleRateHz())
.WillRepeatedly(Return(kSampleRateKhz * 1000));
EXPECT_CALL(mock_decoder, Channels()).WillRepeatedly(Return(1));
EXPECT_CALL(mock_decoder, PacketDuration(_, kPayloadLengthBytes))
.WillRepeatedly(Return(rtc::checked_cast<int>(kPayloadLengthSamples)));
EXPECT_CALL(mock_decoder, PacketDuration(nullptr, 0))
.WillRepeatedly(Return(rtc::checked_cast<int>(kPayloadLengthSamples)));
EXPECT_TRUE(neteq_->RegisterPayloadType(kPayloadType,
SdpAudioFormat("opus", 48000, 2)));
struct Packet {
int sequence_number_delta;
int timestamp_delta;
AudioDecoder::SpeechType decoder_output_type;
};
std::vector<Packet> packets = {
{0, 0, AudioDecoder::kSpeech},
{1, kPayloadLengthSamples, AudioDecoder::kComfortNoise},
{2, 2 * kPayloadLengthSamples, AudioDecoder::kSpeech},
{1, kPayloadLengthSamples, AudioDecoder::kSpeech}};
for (size_t i = 0; i < packets.size(); ++i) {
rtp_header.sequenceNumber += packets[i].sequence_number_delta;
rtp_header.timestamp += packets[i].timestamp_delta;
payload[0] = i;
EXPECT_EQ(NetEq::kOK, neteq_->InsertPacket(rtp_header, payload));
EXPECT_CALL(mock_decoder, DecodeInternal(Pointee(i), kPayloadLengthBytes,
kSampleRateKhz * 1000, _, _))
.WillOnce(DoAll(SetArrayArgument<3>(
dummy_output, dummy_output + kPayloadLengthSamples),
SetArgPointee<4>(packets[i].decoder_output_type),
Return(rtc::checked_cast<int>(kPayloadLengthSamples))));
}
EXPECT_CALL(mock_decoder,
DecodeInternal(IsNull(), 0, kSampleRateKhz * 1000, _, _))
.WillOnce(DoAll(SetArrayArgument<3>(dummy_output,
dummy_output + kPayloadLengthSamples),
SetArgPointee<4>(AudioDecoder::kComfortNoise),
Return(rtc::checked_cast<int>(kPayloadLengthSamples))));
std::vector<AudioFrame::SpeechType> expected_output = {
AudioFrame::kNormalSpeech, AudioFrame::kCNG, AudioFrame::kNormalSpeech};
size_t output_index = 0;
int timeout_counter = 0;
while (!packet_buffer_->Empty()) {
ASSERT_LT(timeout_counter++, 20) << "Test timed out";
AudioFrame output;
bool muted;
EXPECT_EQ(NetEq::kOK, neteq_->GetAudio(&output, &muted));
if (output_index + 1 < expected_output.size() &&
output.speech_type_ == expected_output[output_index + 1]) {
++output_index;
} else {
EXPECT_EQ(output.speech_type_, expected_output[output_index]);
}
}
EXPECT_CALL(mock_decoder, Die());
}
TEST_F(NetEqImplTest, UnsupportedDecoder) {
UseNoMocks();
::testing::NiceMock<MockAudioDecoder> decoder;
CreateInstance(
rtc::make_ref_counted<test::AudioDecoderProxyFactory>(&decoder));
static const size_t kNetEqMaxFrameSize = 5760;
static const size_t kChannels = 2;
const uint8_t kPayloadType = 17;
const int kSampleRateHz = 8000;
const size_t kPayloadLengthSamples =
static_cast<size_t>(10 * kSampleRateHz / 1000);
const size_t kPayloadLengthBytes = 1;
uint8_t payload[kPayloadLengthBytes] = {0};
int16_t dummy_output[kPayloadLengthSamples * kChannels] = {0};
RTPHeader rtp_header;
rtp_header.payloadType = kPayloadType;
rtp_header.sequenceNumber = 0x1234;
rtp_header.timestamp = 0x12345678;
rtp_header.ssrc = 0x87654321;
const uint8_t kFirstPayloadValue = 1;
const uint8_t kSecondPayloadValue = 2;
EXPECT_CALL(decoder,
PacketDuration(Pointee(kFirstPayloadValue), kPayloadLengthBytes))
.Times(AtLeast(1))
.WillRepeatedly(Return(rtc::checked_cast<int>(kNetEqMaxFrameSize + 1)));
EXPECT_CALL(decoder, DecodeInternal(Pointee(kFirstPayloadValue), _, _, _, _))
.Times(0);
EXPECT_CALL(decoder, DecodeInternal(Pointee(kSecondPayloadValue),
kPayloadLengthBytes, kSampleRateHz, _, _))
.Times(1)
.WillOnce(DoAll(
SetArrayArgument<3>(dummy_output,
dummy_output + kPayloadLengthSamples * kChannels),
SetArgPointee<4>(AudioDecoder::kSpeech),
Return(static_cast<int>(kPayloadLengthSamples * kChannels))));
EXPECT_CALL(decoder,
PacketDuration(Pointee(kSecondPayloadValue), kPayloadLengthBytes))
.Times(AtLeast(1))
.WillRepeatedly(Return(rtc::checked_cast<int>(kNetEqMaxFrameSize)));
EXPECT_CALL(decoder, SampleRateHz()).WillRepeatedly(Return(kSampleRateHz));
EXPECT_CALL(decoder, Channels()).WillRepeatedly(Return(kChannels));
EXPECT_TRUE(neteq_->RegisterPayloadType(kPayloadType,
SdpAudioFormat("L16", 8000, 1)));
payload[0] = kFirstPayloadValue;
EXPECT_EQ(NetEq::kOK, neteq_->InsertPacket(rtp_header, payload));
payload[0] = kSecondPayloadValue;
rtp_header.sequenceNumber++;
rtp_header.timestamp += 3 * kPayloadLengthSamples;
EXPECT_EQ(NetEq::kOK, neteq_->InsertPacket(rtp_header, payload));
AudioFrame output;
bool muted;
EXPECT_EQ(NetEq::kFail, neteq_->GetAudio(&output, &muted));
const size_t kExpectedOutputSize = 10 * (kSampleRateHz / 1000) * kChannels;
EXPECT_EQ(kExpectedOutputSize, output.samples_per_channel_ * kChannels);
EXPECT_EQ(kChannels, output.num_channels_);
EXPECT_THAT(output.packet_infos_, IsEmpty());
EXPECT_EQ(NetEq::kOK, neteq_->GetAudio(&output, &muted));
EXPECT_EQ(kExpectedOutputSize, output.samples_per_channel_ * kChannels);
EXPECT_EQ(kChannels, output.num_channels_);
EXPECT_THAT(output.packet_infos_, SizeIs(1));
EXPECT_CALL(decoder, Die());
}
TEST_F(NetEqImplTest, FloodBufferAndGetNetworkStats) {
UseNoMocks();
CreateInstance();
const size_t kPayloadLengthSamples = 80;
const size_t kPayloadLengthBytes = 2 * kPayloadLengthSamples;
const uint8_t kPayloadType = 17;
uint8_t payload[kPayloadLengthBytes] = {0};
RTPHeader rtp_header;
rtp_header.payloadType = kPayloadType;
rtp_header.sequenceNumber = 0x1234;
rtp_header.timestamp = 0x12345678;
rtp_header.ssrc = 0x87654321;
EXPECT_TRUE(neteq_->RegisterPayloadType(kPayloadType,
SdpAudioFormat("l16", 8000, 1)));
for (size_t i = 0; i <= config_.max_packets_in_buffer; ++i) {
EXPECT_EQ(i, packet_buffer_->NumPacketsInBuffer());
EXPECT_EQ(NetEq::kOK, neteq_->InsertPacket(rtp_header, payload));
rtp_header.timestamp += rtc::checked_cast<uint32_t>(kPayloadLengthSamples);
++rtp_header.sequenceNumber;
}
EXPECT_EQ(1u, packet_buffer_->NumPacketsInBuffer());
NetEqNetworkStatistics stats;
EXPECT_EQ(NetEq::kOK, neteq_->NetworkStatistics(&stats));
}
TEST_F(NetEqImplTest, DecodedPayloadTooShort) {
UseNoMocks();
MockAudioDecoder mock_decoder;
CreateInstance(
rtc::make_ref_counted<test::AudioDecoderProxyFactory>(&mock_decoder));
const uint8_t kPayloadType = 17;
const int kSampleRateHz = 8000;
const size_t kPayloadLengthSamples =
static_cast<size_t>(10 * kSampleRateHz / 1000);
const size_t kPayloadLengthBytes = 2 * kPayloadLengthSamples;
uint8_t payload[kPayloadLengthBytes] = {0};
RTPHeader rtp_header;
rtp_header.payloadType = kPayloadType;
rtp_header.sequenceNumber = 0x1234;
rtp_header.timestamp = 0x12345678;
rtp_header.ssrc = 0x87654321;
EXPECT_CALL(mock_decoder, Reset()).WillRepeatedly(Return());
EXPECT_CALL(mock_decoder, SampleRateHz())
.WillRepeatedly(Return(kSampleRateHz));
EXPECT_CALL(mock_decoder, Channels()).WillRepeatedly(Return(1));
EXPECT_CALL(mock_decoder, PacketDuration(_, _))
.WillRepeatedly(Return(rtc::checked_cast<int>(kPayloadLengthSamples)));
int16_t dummy_output[kPayloadLengthSamples] = {0};
EXPECT_CALL(mock_decoder,
DecodeInternal(_, kPayloadLengthBytes, kSampleRateHz, _, _))
.WillOnce(
DoAll(SetArrayArgument<3>(dummy_output,
dummy_output + kPayloadLengthSamples - 5),
SetArgPointee<4>(AudioDecoder::kSpeech),
Return(rtc::checked_cast<int>(kPayloadLengthSamples - 5))));
EXPECT_TRUE(neteq_->RegisterPayloadType(kPayloadType,
SdpAudioFormat("L16", 8000, 1)));
EXPECT_EQ(NetEq::kOK, neteq_->InsertPacket(rtp_header, payload));
EXPECT_EQ(5u, neteq_->sync_buffer_for_test()->FutureLength());
const size_t kMaxOutputSize = static_cast<size_t>(10 * kSampleRateHz / 1000);
AudioFrame output;
bool muted;
EXPECT_EQ(NetEq::kOK, neteq_->GetAudio(&output, &muted));
ASSERT_EQ(kMaxOutputSize, output.samples_per_channel_);
EXPECT_EQ(1u, output.num_channels_);
EXPECT_EQ(AudioFrame::kNormalSpeech, output.speech_type_);
EXPECT_THAT(output.packet_infos_, SizeIs(1));
EXPECT_CALL(mock_decoder, Die());
}
TEST_F(NetEqImplTest, DecodingError) {
UseNoMocks();
MockAudioDecoder mock_decoder;
CreateInstance(
rtc::make_ref_counted<test::AudioDecoderProxyFactory>(&mock_decoder));
const uint8_t kPayloadType = 17;
const int kSampleRateHz = 8000;
const int kDecoderErrorCode = -97;
const size_t kFrameLengthSamples =
static_cast<size_t>(5 * kSampleRateHz / 1000);
const size_t kPayloadLengthBytes = 1;
uint8_t payload[kPayloadLengthBytes] = {0};
RTPHeader rtp_header;
rtp_header.payloadType = kPayloadType;
rtp_header.sequenceNumber = 0x1234;
rtp_header.timestamp = 0x12345678;
rtp_header.ssrc = 0x87654321;
EXPECT_CALL(mock_decoder, Reset()).WillRepeatedly(Return());
EXPECT_CALL(mock_decoder, SampleRateHz())
.WillRepeatedly(Return(kSampleRateHz));
EXPECT_CALL(mock_decoder, Channels()).WillRepeatedly(Return(1));
EXPECT_CALL(mock_decoder, PacketDuration(_, _))
.WillRepeatedly(Return(rtc::checked_cast<int>(kFrameLengthSamples)));
EXPECT_CALL(mock_decoder, ErrorCode()).WillOnce(Return(kDecoderErrorCode));
EXPECT_CALL(mock_decoder, HasDecodePlc()).WillOnce(Return(false));
int16_t dummy_output[kFrameLengthSamples] = {0};
{
InSequence sequence;
EXPECT_CALL(mock_decoder,
DecodeInternal(_, kPayloadLengthBytes, kSampleRateHz, _, _))
.Times(3)
.WillRepeatedly(
DoAll(SetArrayArgument<3>(dummy_output,
dummy_output + kFrameLengthSamples),
SetArgPointee<4>(AudioDecoder::kSpeech),
Return(rtc::checked_cast<int>(kFrameLengthSamples))))
.RetiresOnSaturation();
EXPECT_CALL(mock_decoder,
DecodeInternal(_, kPayloadLengthBytes, kSampleRateHz, _, _))
.WillOnce(Return(-1))
.RetiresOnSaturation();
EXPECT_CALL(mock_decoder,
DecodeInternal(_, kPayloadLengthBytes, kSampleRateHz, _, _))
.Times(2)
.WillRepeatedly(
DoAll(SetArrayArgument<3>(dummy_output,
dummy_output + kFrameLengthSamples),
SetArgPointee<4>(AudioDecoder::kSpeech),
Return(rtc::checked_cast<int>(kFrameLengthSamples))));
}
EXPECT_TRUE(neteq_->RegisterPayloadType(kPayloadType,
SdpAudioFormat("L16", 8000, 1)));
for (int i = 0; i < 20; ++i) {
rtp_header.sequenceNumber += 1;
rtp_header.timestamp += kFrameLengthSamples;
EXPECT_EQ(NetEq::kOK, neteq_->InsertPacket(rtp_header, payload));
}
const size_t kMaxOutputSize = static_cast<size_t>(10 * kSampleRateHz / 1000);
AudioFrame output;
bool muted;
EXPECT_EQ(NetEq::kOK, neteq_->GetAudio(&output, &muted));
EXPECT_EQ(kMaxOutputSize, output.samples_per_channel_);
EXPECT_EQ(1u, output.num_channels_);
EXPECT_EQ(AudioFrame::kNormalSpeech, output.speech_type_);
EXPECT_THAT(output.packet_infos_, SizeIs(2));
EXPECT_EQ(NetEq::kFail, neteq_->GetAudio(&output, &muted));
EXPECT_EQ(kMaxOutputSize, output.samples_per_channel_);
EXPECT_EQ(1u, output.num_channels_);
EXPECT_EQ(NetEq::kOK, neteq_->GetAudio(&output, &muted));
EXPECT_EQ(kMaxOutputSize, output.samples_per_channel_);
EXPECT_EQ(1u, output.num_channels_);
EXPECT_EQ(AudioFrame::kNormalSpeech, output.speech_type_);
EXPECT_THAT(output.packet_infos_, SizeIs(2));
EXPECT_CALL(mock_decoder, Die());
}
TEST_F(NetEqImplTest, InitialLastOutputSampleRate) {
UseNoMocks();
config_.sample_rate_hz = 48000;
CreateInstance();
EXPECT_EQ(48000, neteq_->last_output_sample_rate_hz());
}
TEST_F(NetEqImplTest, TickTimerIncrement) {
UseNoMocks();
CreateInstance();
ASSERT_TRUE(tick_timer_);
EXPECT_EQ(0u, tick_timer_->ticks());
AudioFrame output;
bool muted;
EXPECT_EQ(NetEq::kOK, neteq_->GetAudio(&output, &muted));
EXPECT_EQ(1u, tick_timer_->ticks());
}
TEST_F(NetEqImplTest, SetBaseMinimumDelay) {
UseNoMocks();
use_mock_neteq_controller_ = true;
CreateInstance();
EXPECT_CALL(*mock_neteq_controller_, SetBaseMinimumDelay(_))
.WillOnce(Return(true))
.WillOnce(Return(false));
const int delay_ms = 200;
EXPECT_EQ(true, neteq_->SetBaseMinimumDelayMs(delay_ms));
EXPECT_EQ(false, neteq_->SetBaseMinimumDelayMs(delay_ms));
}
TEST_F(NetEqImplTest, GetBaseMinimumDelayMs) {
UseNoMocks();
use_mock_neteq_controller_ = true;
CreateInstance();
const int delay_ms = 200;
EXPECT_CALL(*mock_neteq_controller_, GetBaseMinimumDelay())
.WillOnce(Return(delay_ms));
EXPECT_EQ(delay_ms, neteq_->GetBaseMinimumDelayMs());
}
TEST_F(NetEqImplTest, TargetDelayMs) {
UseNoMocks();
use_mock_neteq_controller_ = true;
CreateInstance();
constexpr int kTargetLevelMs = 510;
EXPECT_CALL(*mock_neteq_controller_, TargetLevelMs())
.WillOnce(Return(kTargetLevelMs));
EXPECT_EQ(510, neteq_->TargetDelayMs());
}
TEST_F(NetEqImplTest, InsertEmptyPacket) {
UseNoMocks();
use_mock_neteq_controller_ = true;
CreateInstance();
RTPHeader rtp_header;
rtp_header.payloadType = 17;
rtp_header.sequenceNumber = 0x1234;
rtp_header.timestamp = 0x12345678;
rtp_header.ssrc = 0x87654321;
EXPECT_CALL(*mock_neteq_controller_, RegisterEmptyPacket());
neteq_->InsertEmptyPacket(rtp_header);
}
TEST_F(NetEqImplTest, NotifyControllerOfReorderedPacket) {
using ::testing::AllOf;
using ::testing::Field;
UseNoMocks();
use_mock_neteq_controller_ = true;
CreateInstance();
EXPECT_CALL(*mock_neteq_controller_, GetDecision(_, _))
.Times(1)
.WillOnce(Return(NetEq::Operation::kNormal));
const int kPayloadLengthSamples = 80;
const size_t kPayloadLengthBytes = 2 * kPayloadLengthSamples;
const uint8_t kPayloadType = 17;
uint8_t payload[kPayloadLengthBytes] = {0};
RTPHeader rtp_header;
rtp_header.payloadType = kPayloadType;
rtp_header.sequenceNumber = 0x1234;
rtp_header.timestamp = 0x12345678;
rtp_header.ssrc = 0x87654321;
EXPECT_TRUE(neteq_->RegisterPayloadType(kPayloadType,
SdpAudioFormat("l16", 8000, 1)));
EXPECT_EQ(NetEq::kOK, neteq_->InsertPacket(rtp_header, payload));
AudioFrame output;
bool muted;
EXPECT_EQ(NetEq::kOK, neteq_->GetAudio(&output, &muted));
rtp_header.sequenceNumber -= 1;
rtp_header.timestamp -= kPayloadLengthSamples;
EXPECT_CALL(
*mock_neteq_controller_,
PacketArrived(
8000,
true,
AllOf(
Field(&NetEqController::PacketArrivedInfo::packet_length_samples,
kPayloadLengthSamples),
Field(&NetEqController::PacketArrivedInfo::main_sequence_number,
rtp_header.sequenceNumber),
Field(&NetEqController::PacketArrivedInfo::main_timestamp,
rtp_header.timestamp))));
EXPECT_EQ(NetEq::kOK, neteq_->InsertPacket(rtp_header, payload));
}
TEST_F(NetEqImplTest, NoCrashWith1000Channels) {
using ::testing::AllOf;
using ::testing::Field;
UseNoMocks();
use_mock_decoder_database_ = true;
enable_muted_state_ = true;
CreateInstance();
const size_t kPayloadLength = 100;
const uint8_t kPayloadType = 0;
const uint16_t kFirstSequenceNumber = 0x1234;
const uint32_t kFirstTimestamp = 0x12345678;
const uint32_t kSsrc = 0x87654321;
uint8_t payload[kPayloadLength] = {0};
RTPHeader rtp_header;
rtp_header.payloadType = kPayloadType;
rtp_header.sequenceNumber = kFirstSequenceNumber;
rtp_header.timestamp = kFirstTimestamp;
rtp_header.ssrc = kSsrc;
Packet fake_packet;
fake_packet.payload_type = kPayloadType;
fake_packet.sequence_number = kFirstSequenceNumber;
fake_packet.timestamp = kFirstTimestamp;
AudioDecoder* decoder = nullptr;
auto mock_decoder_factory = rtc::make_ref_counted<MockAudioDecoderFactory>();
EXPECT_CALL(*mock_decoder_factory, MakeAudioDecoderMock(_, _, _))
.WillOnce(Invoke([&](const SdpAudioFormat& format,
absl::optional<AudioCodecPairId> codec_pair_id,
std::unique_ptr<AudioDecoder>* dec) {
EXPECT_EQ("pcmu", format.name);
*dec = std::make_unique<AudioDecoderPcmU>(1000);
decoder = dec->get();
}));
DecoderDatabase::DecoderInfo info(SdpAudioFormat("pcmu", 8000, 1),
absl::nullopt, mock_decoder_factory.get());
EXPECT_CALL(*mock_decoder_database_, GetDecoderInfo(kPayloadType))
.WillRepeatedly(Return(&info));
EXPECT_CALL(*mock_decoder_database_, GetActiveCngDecoder())
.WillRepeatedly(ReturnNull());
EXPECT_CALL(*mock_decoder_database_, GetActiveDecoder())
.WillRepeatedly(Return(decoder));
EXPECT_CALL(*mock_decoder_database_, SetActiveDecoder(_, _))
.WillOnce(Invoke([](uint8_t rtp_payload_type, bool* new_decoder) {
*new_decoder = true;
return 0;
}));
neteq_->InsertPacket(rtp_header, payload);
AudioFrame audio_frame;
bool muted;
for (int i = 0; i < 40; i++) {
EXPECT_NE(0, neteq_->GetAudio(&audio_frame, &muted));
}
}
class Decoder120ms : public AudioDecoder {
public:
Decoder120ms(int sample_rate_hz, SpeechType speech_type)
: sample_rate_hz_(sample_rate_hz),
next_value_(1),
speech_type_(speech_type) {}
int DecodeInternal(const uint8_t* encoded,
size_t encoded_len,
int sample_rate_hz,
int16_t* decoded,
SpeechType* speech_type) override {
EXPECT_EQ(sample_rate_hz_, sample_rate_hz);
size_t decoded_len =
rtc::CheckedDivExact(sample_rate_hz, 1000) * 120 * Channels();
for (size_t i = 0; i < decoded_len; ++i) {
decoded[i] = next_value_++;
}
*speech_type = speech_type_;
return rtc::checked_cast<int>(decoded_len);
}
void Reset() override { next_value_ = 1; }
int SampleRateHz() const override { return sample_rate_hz_; }
size_t Channels() const override { return 2; }
private:
int sample_rate_hz_;
int16_t next_value_;
SpeechType speech_type_;
};
class NetEqImplTest120ms : public NetEqImplTest {
protected:
NetEqImplTest120ms() : NetEqImplTest() {}
virtual ~NetEqImplTest120ms() {}
void CreateInstanceNoMocks() {
UseNoMocks();
CreateInstance(decoder_factory_);
EXPECT_TRUE(neteq_->RegisterPayloadType(
kPayloadType, SdpAudioFormat("opus", 48000, 2, {{"stereo", "1"}})));
}
void CreateInstanceWithDelayManagerMock() {
UseNoMocks();
use_mock_neteq_controller_ = true;
CreateInstance(decoder_factory_);
EXPECT_TRUE(neteq_->RegisterPayloadType(
kPayloadType, SdpAudioFormat("opus", 48000, 2, {{"stereo", "1"}})));
}
uint32_t timestamp_diff_between_packets() const {
return rtc::CheckedDivExact(kSamplingFreq_, 1000u) * 120;
}
uint32_t first_timestamp() const { return 10u; }
void GetFirstPacket() {
bool muted;
for (int i = 0; i < 12; i++) {
EXPECT_EQ(NetEq::kOK, neteq_->GetAudio(&output_, &muted));
EXPECT_FALSE(muted);
}
}
void InsertPacket(uint32_t timestamp) {
RTPHeader rtp_header;
rtp_header.payloadType = kPayloadType;
rtp_header.sequenceNumber = sequence_number_;
rtp_header.timestamp = timestamp;
rtp_header.ssrc = 15;
const size_t kPayloadLengthBytes = 1;
uint8_t payload[kPayloadLengthBytes] = {0};
EXPECT_EQ(NetEq::kOK, neteq_->InsertPacket(rtp_header, payload));
sequence_number_++;
}
void Register120msCodec(AudioDecoder::SpeechType speech_type) {
const uint32_t sampling_freq = kSamplingFreq_;
decoder_factory_ = rtc::make_ref_counted<test::FunctionAudioDecoderFactory>(
[sampling_freq, speech_type]() {
std::unique_ptr<AudioDecoder> decoder =
std::make_unique<Decoder120ms>(sampling_freq, speech_type);
RTC_CHECK_EQ(2, decoder->Channels());
return decoder;
});
}
rtc::scoped_refptr<AudioDecoderFactory> decoder_factory_;
AudioFrame output_;
const uint32_t kPayloadType = 17;
const uint32_t kSamplingFreq_ = 48000;
uint16_t sequence_number_ = 1;
};
TEST_F(NetEqImplTest120ms, CodecInternalCng) {
Register120msCodec(AudioDecoder::kComfortNoise);
CreateInstanceNoMocks();
InsertPacket(first_timestamp());
GetFirstPacket();
bool muted;
EXPECT_EQ(NetEq::kOK, neteq_->GetAudio(&output_, &muted));
EXPECT_EQ(NetEq::Operation::kCodecInternalCng,
neteq_->last_operation_for_test());
}
TEST_F(NetEqImplTest120ms, Normal) {
Register120msCodec(AudioDecoder::kSpeech);
CreateInstanceNoMocks();
InsertPacket(first_timestamp());
GetFirstPacket();
EXPECT_EQ(NetEq::Operation::kNormal, neteq_->last_operation_for_test());
}
TEST_F(NetEqImplTest120ms, Merge) {
Register120msCodec(AudioDecoder::kSpeech);
CreateInstanceWithDelayManagerMock();
InsertPacket(first_timestamp());
GetFirstPacket();
bool muted;
EXPECT_CALL(*mock_neteq_controller_, GetDecision(_, _))
.WillOnce(Return(NetEq::Operation::kExpand));
EXPECT_EQ(NetEq::kOK, neteq_->GetAudio(&output_, &muted));
InsertPacket(first_timestamp() + 2 * timestamp_diff_between_packets());
EXPECT_CALL(*mock_neteq_controller_, GetDecision(_, _))
.WillOnce(Return(NetEq::Operation::kMerge));
EXPECT_EQ(NetEq::kOK, neteq_->GetAudio(&output_, &muted));
EXPECT_EQ(NetEq::Operation::kMerge, neteq_->last_operation_for_test());
}
TEST_F(NetEqImplTest120ms, Expand) {
Register120msCodec(AudioDecoder::kSpeech);
CreateInstanceNoMocks();
InsertPacket(first_timestamp());
GetFirstPacket();
bool muted;
EXPECT_EQ(NetEq::kOK, neteq_->GetAudio(&output_, &muted));
EXPECT_EQ(NetEq::Operation::kExpand, neteq_->last_operation_for_test());
}
TEST_F(NetEqImplTest120ms, FastAccelerate) {
Register120msCodec(AudioDecoder::kSpeech);
CreateInstanceWithDelayManagerMock();
InsertPacket(first_timestamp());
GetFirstPacket();
InsertPacket(first_timestamp() + timestamp_diff_between_packets());
EXPECT_CALL(*mock_neteq_controller_, GetDecision(_, _))
.Times(1)
.WillOnce(Return(NetEq::Operation::kFastAccelerate));
bool muted;
EXPECT_EQ(NetEq::kOK, neteq_->GetAudio(&output_, &muted));
EXPECT_EQ(NetEq::Operation::kFastAccelerate,
neteq_->last_operation_for_test());
}
TEST_F(NetEqImplTest120ms, PreemptiveExpand) {
Register120msCodec(AudioDecoder::kSpeech);
CreateInstanceWithDelayManagerMock();
InsertPacket(first_timestamp());
GetFirstPacket();
InsertPacket(first_timestamp() + timestamp_diff_between_packets());
EXPECT_CALL(*mock_neteq_controller_, GetDecision(_, _))
.Times(1)
.WillOnce(Return(NetEq::Operation::kPreemptiveExpand));
bool muted;
EXPECT_EQ(NetEq::kOK, neteq_->GetAudio(&output_, &muted));
EXPECT_EQ(NetEq::Operation::kPreemptiveExpand,
neteq_->last_operation_for_test());
}
TEST_F(NetEqImplTest120ms, Accelerate) {
Register120msCodec(AudioDecoder::kSpeech);
CreateInstanceWithDelayManagerMock();
InsertPacket(first_timestamp());
GetFirstPacket();
InsertPacket(first_timestamp() + timestamp_diff_between_packets());
EXPECT_CALL(*mock_neteq_controller_, GetDecision(_, _))
.Times(1)
.WillOnce(Return(NetEq::Operation::kAccelerate));
bool muted;
EXPECT_EQ(NetEq::kOK, neteq_->GetAudio(&output_, &muted));
EXPECT_EQ(NetEq::Operation::kAccelerate, neteq_->last_operation_for_test());
}
}