blob: 5cacce65045d60e10ea1a63d86da9dff9685b0bb [file] [log] [blame]
/*
* 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 "webrtc/modules/audio_coding/neteq/include/neteq.h"
#include "webrtc/modules/audio_coding/neteq/neteq_impl.h"
#include "testing/gmock/include/gmock/gmock.h"
#include "testing/gtest/include/gtest/gtest.h"
#include "webrtc/base/safe_conversions.h"
#include "webrtc/modules/audio_coding/neteq/accelerate.h"
#include "webrtc/modules/audio_coding/neteq/expand.h"
#include "webrtc/modules/audio_coding/neteq/mock/mock_audio_decoder.h"
#include "webrtc/modules/audio_coding/neteq/mock/mock_buffer_level_filter.h"
#include "webrtc/modules/audio_coding/neteq/mock/mock_decoder_database.h"
#include "webrtc/modules/audio_coding/neteq/mock/mock_delay_manager.h"
#include "webrtc/modules/audio_coding/neteq/mock/mock_delay_peak_detector.h"
#include "webrtc/modules/audio_coding/neteq/mock/mock_dtmf_buffer.h"
#include "webrtc/modules/audio_coding/neteq/mock/mock_dtmf_tone_generator.h"
#include "webrtc/modules/audio_coding/neteq/mock/mock_packet_buffer.h"
#include "webrtc/modules/audio_coding/neteq/mock/mock_payload_splitter.h"
#include "webrtc/modules/audio_coding/neteq/preemptive_expand.h"
#include "webrtc/modules/audio_coding/neteq/sync_buffer.h"
#include "webrtc/modules/audio_coding/neteq/timestamp_scaler.h"
using ::testing::AtLeast;
using ::testing::Return;
using ::testing::ReturnNull;
using ::testing::_;
using ::testing::SetArgPointee;
using ::testing::SetArrayArgument;
using ::testing::InSequence;
using ::testing::Invoke;
using ::testing::WithArg;
using ::testing::Pointee;
using ::testing::IsNull;
namespace webrtc {
// This function is called when inserting a packet list into the mock packet
// buffer. The purpose is to delete all inserted packets properly, to avoid
// memory leaks in the test.
int DeletePacketsAndReturnOk(PacketList* packet_list) {
PacketBuffer::DeleteAllPackets(packet_list);
return PacketBuffer::kOK;
}
class NetEqImplTest : public ::testing::Test {
protected:
NetEqImplTest()
: neteq_(NULL),
config_(),
mock_buffer_level_filter_(NULL),
buffer_level_filter_(NULL),
use_mock_buffer_level_filter_(true),
mock_decoder_database_(NULL),
decoder_database_(NULL),
use_mock_decoder_database_(true),
mock_delay_peak_detector_(NULL),
delay_peak_detector_(NULL),
use_mock_delay_peak_detector_(true),
mock_delay_manager_(NULL),
delay_manager_(NULL),
use_mock_delay_manager_(true),
mock_dtmf_buffer_(NULL),
dtmf_buffer_(NULL),
use_mock_dtmf_buffer_(true),
mock_dtmf_tone_generator_(NULL),
dtmf_tone_generator_(NULL),
use_mock_dtmf_tone_generator_(true),
mock_packet_buffer_(NULL),
packet_buffer_(NULL),
use_mock_packet_buffer_(true),
mock_payload_splitter_(NULL),
payload_splitter_(NULL),
use_mock_payload_splitter_(true),
timestamp_scaler_(NULL) {
config_.sample_rate_hz = 8000;
}
void CreateInstance() {
if (use_mock_buffer_level_filter_) {
mock_buffer_level_filter_ = new MockBufferLevelFilter;
buffer_level_filter_ = mock_buffer_level_filter_;
} else {
buffer_level_filter_ = new BufferLevelFilter;
}
if (use_mock_decoder_database_) {
mock_decoder_database_ = new MockDecoderDatabase;
EXPECT_CALL(*mock_decoder_database_, GetActiveCngDecoder())
.WillOnce(ReturnNull());
decoder_database_ = mock_decoder_database_;
} else {
decoder_database_ = new DecoderDatabase;
}
if (use_mock_delay_peak_detector_) {
mock_delay_peak_detector_ = new MockDelayPeakDetector;
EXPECT_CALL(*mock_delay_peak_detector_, Reset()).Times(1);
delay_peak_detector_ = mock_delay_peak_detector_;
} else {
delay_peak_detector_ = new DelayPeakDetector;
}
if (use_mock_delay_manager_) {
mock_delay_manager_ = new MockDelayManager(config_.max_packets_in_buffer,
delay_peak_detector_);
EXPECT_CALL(*mock_delay_manager_, set_streaming_mode(false)).Times(1);
delay_manager_ = mock_delay_manager_;
} else {
delay_manager_ =
new DelayManager(config_.max_packets_in_buffer, delay_peak_detector_);
}
if (use_mock_dtmf_buffer_) {
mock_dtmf_buffer_ = new MockDtmfBuffer(config_.sample_rate_hz);
dtmf_buffer_ = mock_dtmf_buffer_;
} else {
dtmf_buffer_ = new DtmfBuffer(config_.sample_rate_hz);
}
if (use_mock_dtmf_tone_generator_) {
mock_dtmf_tone_generator_ = new MockDtmfToneGenerator;
dtmf_tone_generator_ = mock_dtmf_tone_generator_;
} else {
dtmf_tone_generator_ = new DtmfToneGenerator;
}
if (use_mock_packet_buffer_) {
mock_packet_buffer_ = new MockPacketBuffer(config_.max_packets_in_buffer);
packet_buffer_ = mock_packet_buffer_;
} else {
packet_buffer_ = new PacketBuffer(config_.max_packets_in_buffer);
}
if (use_mock_payload_splitter_) {
mock_payload_splitter_ = new MockPayloadSplitter;
payload_splitter_ = mock_payload_splitter_;
} else {
payload_splitter_ = new PayloadSplitter;
}
timestamp_scaler_ = new TimestampScaler(*decoder_database_);
AccelerateFactory* accelerate_factory = new AccelerateFactory;
ExpandFactory* expand_factory = new ExpandFactory;
PreemptiveExpandFactory* preemptive_expand_factory =
new PreemptiveExpandFactory;
neteq_ = new NetEqImpl(config_,
buffer_level_filter_,
decoder_database_,
delay_manager_,
delay_peak_detector_,
dtmf_buffer_,
dtmf_tone_generator_,
packet_buffer_,
payload_splitter_,
timestamp_scaler_,
accelerate_factory,
expand_factory,
preemptive_expand_factory);
ASSERT_TRUE(neteq_ != NULL);
}
void UseNoMocks() {
ASSERT_TRUE(neteq_ == NULL) << "Must call UseNoMocks before CreateInstance";
use_mock_buffer_level_filter_ = false;
use_mock_decoder_database_ = false;
use_mock_delay_peak_detector_ = false;
use_mock_delay_manager_ = 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_buffer_level_filter_) {
EXPECT_CALL(*mock_buffer_level_filter_, Die()).Times(1);
}
if (use_mock_decoder_database_) {
EXPECT_CALL(*mock_decoder_database_, Die()).Times(1);
}
if (use_mock_delay_manager_) {
EXPECT_CALL(*mock_delay_manager_, Die()).Times(1);
}
if (use_mock_delay_peak_detector_) {
EXPECT_CALL(*mock_delay_peak_detector_, 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);
}
delete neteq_;
}
NetEqImpl* neteq_;
NetEq::Config config_;
MockBufferLevelFilter* mock_buffer_level_filter_;
BufferLevelFilter* buffer_level_filter_;
bool use_mock_buffer_level_filter_;
MockDecoderDatabase* mock_decoder_database_;
DecoderDatabase* decoder_database_;
bool use_mock_decoder_database_;
MockDelayPeakDetector* mock_delay_peak_detector_;
DelayPeakDetector* delay_peak_detector_;
bool use_mock_delay_peak_detector_;
MockDelayManager* mock_delay_manager_;
DelayManager* delay_manager_;
bool use_mock_delay_manager_;
MockDtmfBuffer* mock_dtmf_buffer_;
DtmfBuffer* dtmf_buffer_;
bool use_mock_dtmf_buffer_;
MockDtmfToneGenerator* mock_dtmf_tone_generator_;
DtmfToneGenerator* dtmf_tone_generator_;
bool use_mock_dtmf_tone_generator_;
MockPacketBuffer* mock_packet_buffer_;
PacketBuffer* packet_buffer_;
bool use_mock_packet_buffer_;
MockPayloadSplitter* mock_payload_splitter_;
PayloadSplitter* payload_splitter_;
bool use_mock_payload_splitter_;
TimestampScaler* timestamp_scaler_;
};
// This tests the interface class NetEq.
// TODO(hlundin): Move to separate file?
TEST(NetEq, CreateAndDestroy) {
NetEq::Config config;
NetEq* neteq = NetEq::Create(config);
delete neteq;
}
TEST_F(NetEqImplTest, RegisterPayloadType) {
CreateInstance();
uint8_t rtp_payload_type = 0;
NetEqDecoder codec_type = kDecoderPCMu;
EXPECT_CALL(*mock_decoder_database_,
RegisterPayload(rtp_payload_type, codec_type));
neteq_->RegisterPayloadType(codec_type, rtp_payload_type);
}
TEST_F(NetEqImplTest, RemovePayloadType) {
CreateInstance();
uint8_t rtp_payload_type = 0;
EXPECT_CALL(*mock_decoder_database_, Remove(rtp_payload_type))
.WillOnce(Return(DecoderDatabase::kDecoderNotFound));
// Check that kFail is returned when database returns kDecoderNotFound.
EXPECT_EQ(NetEq::kFail, neteq_->RemovePayloadType(rtp_payload_type));
}
TEST_F(NetEqImplTest, InsertPacket) {
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;
const uint32_t kFirstReceiveTime = 17;
uint8_t payload[kPayloadLength] = {0};
WebRtcRTPHeader rtp_header;
rtp_header.header.payloadType = kPayloadType;
rtp_header.header.sequenceNumber = kFirstSequenceNumber;
rtp_header.header.timestamp = kFirstTimestamp;
rtp_header.header.ssrc = kSsrc;
// Create a mock decoder object.
MockAudioDecoder mock_decoder;
EXPECT_CALL(mock_decoder, Channels()).WillRepeatedly(Return(1));
// BWE update function called with first packet.
EXPECT_CALL(mock_decoder, IncomingPacket(_,
kPayloadLength,
kFirstSequenceNumber,
kFirstTimestamp,
kFirstReceiveTime));
// BWE update function called with second packet.
EXPECT_CALL(mock_decoder, IncomingPacket(_,
kPayloadLength,
kFirstSequenceNumber + 1,
kFirstTimestamp + 160,
kFirstReceiveTime + 155));
EXPECT_CALL(mock_decoder, Die()).Times(1); // Called when deleted.
// Expectations for decoder database.
EXPECT_CALL(*mock_decoder_database_, IsRed(kPayloadType))
.WillRepeatedly(Return(false)); // This is not RED.
EXPECT_CALL(*mock_decoder_database_, CheckPayloadTypes(_))
.Times(2)
.WillRepeatedly(Return(DecoderDatabase::kOK)); // Payload type is valid.
EXPECT_CALL(*mock_decoder_database_, IsDtmf(kPayloadType))
.WillRepeatedly(Return(false)); // This is not DTMF.
EXPECT_CALL(*mock_decoder_database_, GetDecoder(kPayloadType))
.Times(3)
.WillRepeatedly(Return(&mock_decoder));
EXPECT_CALL(*mock_decoder_database_, IsComfortNoise(kPayloadType))
.WillRepeatedly(Return(false)); // This is not CNG.
DecoderDatabase::DecoderInfo info;
info.codec_type = kDecoderPCMu;
EXPECT_CALL(*mock_decoder_database_, GetDecoderInfo(kPayloadType))
.WillRepeatedly(Return(&info));
// Expectations for packet buffer.
EXPECT_CALL(*mock_packet_buffer_, NumPacketsInBuffer())
.WillOnce(Return(0)) // First packet.
.WillOnce(Return(1)) // Second packet.
.WillOnce(Return(2)); // Second packet, checking after it was inserted.
EXPECT_CALL(*mock_packet_buffer_, Empty())
.WillOnce(Return(false)); // Called once after first packet is inserted.
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))));
// SetArgPointee<2>(kPayloadType) means that the third argument (zero-based
// index) is a pointer, and the variable pointed to is set to kPayloadType.
// Also invoke the function DeletePacketsAndReturnOk to properly delete all
// packets in the list (to avoid memory leaks in the test).
EXPECT_CALL(*mock_packet_buffer_, NextRtpHeader())
.Times(1)
.WillOnce(Return(&rtp_header.header));
// Expectations for DTMF buffer.
EXPECT_CALL(*mock_dtmf_buffer_, Flush())
.Times(1);
// Expectations for delay manager.
{
// All expectations within this block must be called in this specific order.
InSequence sequence; // Dummy variable.
// Expectations when the first packet is inserted.
EXPECT_CALL(*mock_delay_manager_, LastDecoderType(kDecoderPCMu))
.Times(1);
EXPECT_CALL(*mock_delay_manager_, last_pack_cng_or_dtmf())
.Times(2)
.WillRepeatedly(Return(-1));
EXPECT_CALL(*mock_delay_manager_, set_last_pack_cng_or_dtmf(0))
.Times(1);
EXPECT_CALL(*mock_delay_manager_, ResetPacketIatCount()).Times(1);
// Expectations when the second packet is inserted. Slightly different.
EXPECT_CALL(*mock_delay_manager_, LastDecoderType(kDecoderPCMu))
.Times(1);
EXPECT_CALL(*mock_delay_manager_, last_pack_cng_or_dtmf())
.WillOnce(Return(0));
EXPECT_CALL(*mock_delay_manager_, SetPacketAudioLength(30))
.WillOnce(Return(0));
}
// Expectations for payload splitter.
EXPECT_CALL(*mock_payload_splitter_, SplitAudio(_, _))
.Times(2)
.WillRepeatedly(Return(PayloadSplitter::kOK));
// Insert first packet.
neteq_->InsertPacket(rtp_header, payload, kPayloadLength, kFirstReceiveTime);
// Insert second packet.
rtp_header.header.timestamp += 160;
rtp_header.header.sequenceNumber += 1;
neteq_->InsertPacket(rtp_header, payload, kPayloadLength,
kFirstReceiveTime + 155);
}
TEST_F(NetEqImplTest, InsertPacketsUntilBufferIsFull) {
UseNoMocks();
CreateInstance();
const int kPayloadLengthSamples = 80;
const size_t kPayloadLengthBytes = 2 * kPayloadLengthSamples; // PCM 16-bit.
const uint8_t kPayloadType = 17; // Just an arbitrary number.
const uint32_t kReceiveTime = 17; // Value doesn't matter for this test.
uint8_t payload[kPayloadLengthBytes] = {0};
WebRtcRTPHeader rtp_header;
rtp_header.header.payloadType = kPayloadType;
rtp_header.header.sequenceNumber = 0x1234;
rtp_header.header.timestamp = 0x12345678;
rtp_header.header.ssrc = 0x87654321;
EXPECT_EQ(NetEq::kOK,
neteq_->RegisterPayloadType(kDecoderPCM16B, kPayloadType));
// Insert packets. The buffer should not flush.
for (size_t i = 1; i <= config_.max_packets_in_buffer; ++i) {
EXPECT_EQ(NetEq::kOK,
neteq_->InsertPacket(
rtp_header, payload, kPayloadLengthBytes, kReceiveTime));
rtp_header.header.timestamp += kPayloadLengthSamples;
rtp_header.header.sequenceNumber += 1;
EXPECT_EQ(i, packet_buffer_->NumPacketsInBuffer());
}
// Insert one more packet and make sure the buffer got flushed. That is, it
// should only hold one single packet.
EXPECT_EQ(NetEq::kOK,
neteq_->InsertPacket(
rtp_header, payload, kPayloadLengthBytes, kReceiveTime));
EXPECT_EQ(1u, packet_buffer_->NumPacketsInBuffer());
const RTPHeader* test_header = packet_buffer_->NextRtpHeader();
EXPECT_EQ(rtp_header.header.timestamp, test_header->timestamp);
EXPECT_EQ(rtp_header.header.sequenceNumber, test_header->sequenceNumber);
}
// This test verifies that timestamps propagate from the incoming packets
// through to the sync buffer and to the playout timestamp.
TEST_F(NetEqImplTest, VerifyTimestampPropagation) {
UseNoMocks();
CreateInstance();
const uint8_t kPayloadType = 17; // Just an arbitrary number.
const uint32_t kReceiveTime = 17; // Value doesn't matter for this test.
const int kSampleRateHz = 8000;
const size_t kPayloadLengthSamples =
static_cast<size_t>(10 * kSampleRateHz / 1000); // 10 ms.
const size_t kPayloadLengthBytes = kPayloadLengthSamples;
uint8_t payload[kPayloadLengthBytes] = {0};
WebRtcRTPHeader rtp_header;
rtp_header.header.payloadType = kPayloadType;
rtp_header.header.sequenceNumber = 0x1234;
rtp_header.header.timestamp = 0x12345678;
rtp_header.header.ssrc = 0x87654321;
// This is a dummy decoder that produces as many output samples as the input
// has bytes. The output is an increasing series, starting at 1 for the first
// sample, and then increasing by 1 for each sample.
class CountingSamplesDecoder : public AudioDecoder {
public:
CountingSamplesDecoder() : next_value_(1) {}
// Produce as many samples as input bytes (|encoded_len|).
int Decode(const uint8_t* encoded,
size_t encoded_len,
int /* sample_rate_hz */,
size_t /* max_decoded_bytes */,
int16_t* decoded,
SpeechType* speech_type) override {
for (size_t i = 0; i < encoded_len; ++i) {
decoded[i] = next_value_++;
}
*speech_type = kSpeech;
return encoded_len;
}
void Reset() override { next_value_ = 1; }
size_t Channels() const override { return 1; }
uint16_t next_value() const { return next_value_; }
private:
int16_t next_value_;
} decoder_;
EXPECT_EQ(NetEq::kOK,
neteq_->RegisterExternalDecoder(&decoder_, kDecoderPCM16B,
kPayloadType, kSampleRateHz));
// Insert one packet.
EXPECT_EQ(NetEq::kOK,
neteq_->InsertPacket(
rtp_header, payload, kPayloadLengthBytes, kReceiveTime));
// Pull audio once.
const size_t kMaxOutputSize = static_cast<size_t>(10 * kSampleRateHz / 1000);
int16_t output[kMaxOutputSize];
size_t samples_per_channel;
int num_channels;
NetEqOutputType type;
EXPECT_EQ(
NetEq::kOK,
neteq_->GetAudio(
kMaxOutputSize, output, &samples_per_channel, &num_channels, &type));
ASSERT_EQ(kMaxOutputSize, samples_per_channel);
EXPECT_EQ(1, num_channels);
EXPECT_EQ(kOutputNormal, type);
// Start with a simple check that the fake decoder is behaving as expected.
EXPECT_EQ(kPayloadLengthSamples,
static_cast<size_t>(decoder_.next_value() - 1));
// The value of the last of the output samples is the same as the number of
// samples played from the decoded packet. Thus, this number + the RTP
// timestamp should match the playout timestamp.
uint32_t timestamp = 0;
EXPECT_TRUE(neteq_->GetPlayoutTimestamp(&timestamp));
EXPECT_EQ(rtp_header.header.timestamp + output[samples_per_channel - 1],
timestamp);
// Check the timestamp for the last value in the sync buffer. This should
// be one full frame length ahead of the RTP timestamp.
const SyncBuffer* sync_buffer = neteq_->sync_buffer_for_test();
ASSERT_TRUE(sync_buffer != NULL);
EXPECT_EQ(rtp_header.header.timestamp + kPayloadLengthSamples,
sync_buffer->end_timestamp());
// Check that the number of samples still to play from the sync buffer add
// up with what was already played out.
EXPECT_EQ(kPayloadLengthSamples - output[samples_per_channel - 1],
sync_buffer->FutureLength());
}
TEST_F(NetEqImplTest, ReorderedPacket) {
UseNoMocks();
CreateInstance();
const uint8_t kPayloadType = 17; // Just an arbitrary number.
const uint32_t kReceiveTime = 17; // Value doesn't matter for this test.
const int kSampleRateHz = 8000;
const size_t kPayloadLengthSamples =
static_cast<size_t>(10 * kSampleRateHz / 1000); // 10 ms.
const size_t kPayloadLengthBytes = kPayloadLengthSamples;
uint8_t payload[kPayloadLengthBytes] = {0};
WebRtcRTPHeader rtp_header;
rtp_header.header.payloadType = kPayloadType;
rtp_header.header.sequenceNumber = 0x1234;
rtp_header.header.timestamp = 0x12345678;
rtp_header.header.ssrc = 0x87654321;
// Create a mock decoder object.
MockAudioDecoder mock_decoder;
EXPECT_CALL(mock_decoder, Reset()).WillRepeatedly(Return());
EXPECT_CALL(mock_decoder, Channels()).WillRepeatedly(Return(1));
EXPECT_CALL(mock_decoder, IncomingPacket(_, kPayloadLengthBytes, _, _, _))
.WillRepeatedly(Return(0));
int16_t dummy_output[kPayloadLengthSamples] = {0};
// The below expectation will make the mock decoder write
// |kPayloadLengthSamples| zeros to the output array, and mark it as speech.
EXPECT_CALL(mock_decoder,
Decode(Pointee(0), kPayloadLengthBytes, kSampleRateHz, _, _, _))
.WillOnce(DoAll(SetArrayArgument<4>(dummy_output,
dummy_output + kPayloadLengthSamples),
SetArgPointee<5>(AudioDecoder::kSpeech),
Return(kPayloadLengthSamples)));
EXPECT_EQ(NetEq::kOK,
neteq_->RegisterExternalDecoder(&mock_decoder, kDecoderPCM16B,
kPayloadType, kSampleRateHz));
// Insert one packet.
EXPECT_EQ(NetEq::kOK,
neteq_->InsertPacket(
rtp_header, payload, kPayloadLengthBytes, kReceiveTime));
// Pull audio once.
const size_t kMaxOutputSize = static_cast<size_t>(10 * kSampleRateHz / 1000);
int16_t output[kMaxOutputSize];
size_t samples_per_channel;
int num_channels;
NetEqOutputType type;
EXPECT_EQ(
NetEq::kOK,
neteq_->GetAudio(
kMaxOutputSize, output, &samples_per_channel, &num_channels, &type));
ASSERT_EQ(kMaxOutputSize, samples_per_channel);
EXPECT_EQ(1, num_channels);
EXPECT_EQ(kOutputNormal, type);
// Insert two more packets. The first one is out of order, and is already too
// old, the second one is the expected next packet.
rtp_header.header.sequenceNumber -= 1;
rtp_header.header.timestamp -= kPayloadLengthSamples;
payload[0] = 1;
EXPECT_EQ(NetEq::kOK,
neteq_->InsertPacket(
rtp_header, payload, kPayloadLengthBytes, kReceiveTime));
rtp_header.header.sequenceNumber += 2;
rtp_header.header.timestamp += 2 * kPayloadLengthSamples;
payload[0] = 2;
EXPECT_EQ(NetEq::kOK,
neteq_->InsertPacket(
rtp_header, payload, kPayloadLengthBytes, kReceiveTime));
// Expect only the second packet to be decoded (the one with "2" as the first
// payload byte).
EXPECT_CALL(mock_decoder,
Decode(Pointee(2), kPayloadLengthBytes, kSampleRateHz, _, _, _))
.WillOnce(DoAll(SetArrayArgument<4>(dummy_output,
dummy_output + kPayloadLengthSamples),
SetArgPointee<5>(AudioDecoder::kSpeech),
Return(kPayloadLengthSamples)));
// Pull audio once.
EXPECT_EQ(
NetEq::kOK,
neteq_->GetAudio(
kMaxOutputSize, output, &samples_per_channel, &num_channels, &type));
ASSERT_EQ(kMaxOutputSize, samples_per_channel);
EXPECT_EQ(1, num_channels);
EXPECT_EQ(kOutputNormal, type);
// Now check the packet buffer, and make sure it is empty, since the
// out-of-order packet should have been discarded.
EXPECT_TRUE(packet_buffer_->Empty());
EXPECT_CALL(mock_decoder, Die());
}
// This test verifies that NetEq can handle the situation where the first
// incoming packet is rejected.
TEST_F(NetEqImplTest, FirstPacketUnknown) {
UseNoMocks();
CreateInstance();
const uint8_t kPayloadType = 17; // Just an arbitrary number.
const uint32_t kReceiveTime = 17; // Value doesn't matter for this test.
const int kSampleRateHz = 8000;
const size_t kPayloadLengthSamples =
static_cast<size_t>(10 * kSampleRateHz / 1000); // 10 ms.
const size_t kPayloadLengthBytes = kPayloadLengthSamples;
uint8_t payload[kPayloadLengthBytes] = {0};
WebRtcRTPHeader rtp_header;
rtp_header.header.payloadType = kPayloadType;
rtp_header.header.sequenceNumber = 0x1234;
rtp_header.header.timestamp = 0x12345678;
rtp_header.header.ssrc = 0x87654321;
// Insert one packet. Note that we have not registered any payload type, so
// this packet will be rejected.
EXPECT_EQ(NetEq::kFail,
neteq_->InsertPacket(rtp_header, payload, kPayloadLengthBytes,
kReceiveTime));
EXPECT_EQ(NetEq::kUnknownRtpPayloadType, neteq_->LastError());
// Pull audio once.
const size_t kMaxOutputSize = static_cast<size_t>(10 * kSampleRateHz / 1000);
int16_t output[kMaxOutputSize];
size_t samples_per_channel;
int num_channels;
NetEqOutputType type;
EXPECT_EQ(NetEq::kOK,
neteq_->GetAudio(kMaxOutputSize, output, &samples_per_channel,
&num_channels, &type));
ASSERT_LE(samples_per_channel, kMaxOutputSize);
EXPECT_EQ(kMaxOutputSize, samples_per_channel);
EXPECT_EQ(1, num_channels);
EXPECT_EQ(kOutputPLC, type);
// Register the payload type.
EXPECT_EQ(NetEq::kOK,
neteq_->RegisterPayloadType(kDecoderPCM16B, kPayloadType));
// Insert 10 packets.
for (size_t i = 0; i < 10; ++i) {
rtp_header.header.sequenceNumber++;
rtp_header.header.timestamp += kPayloadLengthSamples;
EXPECT_EQ(NetEq::kOK,
neteq_->InsertPacket(rtp_header, payload, kPayloadLengthBytes,
kReceiveTime));
EXPECT_EQ(i + 1, packet_buffer_->NumPacketsInBuffer());
}
// Pull audio repeatedly and make sure we get normal output, that is not PLC.
for (size_t i = 0; i < 3; ++i) {
EXPECT_EQ(NetEq::kOK,
neteq_->GetAudio(kMaxOutputSize, output, &samples_per_channel,
&num_channels, &type));
ASSERT_LE(samples_per_channel, kMaxOutputSize);
EXPECT_EQ(kMaxOutputSize, samples_per_channel);
EXPECT_EQ(1, num_channels);
EXPECT_EQ(kOutputNormal, type)
<< "NetEq did not decode the packets as expected.";
}
}
// This test verifies that NetEq can handle comfort noise and enters/quits codec
// internal CNG mode properly.
TEST_F(NetEqImplTest, CodecInternalCng) {
UseNoMocks();
CreateInstance();
const uint8_t kPayloadType = 17; // Just an arbitrary number.
const uint32_t kReceiveTime = 17; // Value doesn't matter for this test.
const int kSampleRateKhz = 48;
const size_t kPayloadLengthSamples =
static_cast<size_t>(20 * kSampleRateKhz); // 20 ms.
const size_t kPayloadLengthBytes = 10;
uint8_t payload[kPayloadLengthBytes] = {0};
int16_t dummy_output[kPayloadLengthSamples] = {0};
WebRtcRTPHeader rtp_header;
rtp_header.header.payloadType = kPayloadType;
rtp_header.header.sequenceNumber = 0x1234;
rtp_header.header.timestamp = 0x12345678;
rtp_header.header.ssrc = 0x87654321;
// Create a mock decoder object.
MockAudioDecoder mock_decoder;
EXPECT_CALL(mock_decoder, Reset()).WillRepeatedly(Return());
EXPECT_CALL(mock_decoder, Channels()).WillRepeatedly(Return(1));
EXPECT_CALL(mock_decoder, IncomingPacket(_, kPayloadLengthBytes, _, _, _))
.WillRepeatedly(Return(0));
// Pointee(x) verifies that first byte of the payload equals x, this makes it
// possible to verify that the correct payload is fed to Decode().
EXPECT_CALL(mock_decoder, Decode(Pointee(0), kPayloadLengthBytes,
kSampleRateKhz * 1000, _, _, _))
.WillOnce(DoAll(SetArrayArgument<4>(dummy_output,
dummy_output + kPayloadLengthSamples),
SetArgPointee<5>(AudioDecoder::kSpeech),
Return(kPayloadLengthSamples)));
EXPECT_CALL(mock_decoder, Decode(Pointee(1), kPayloadLengthBytes,
kSampleRateKhz * 1000, _, _, _))
.WillOnce(DoAll(SetArrayArgument<4>(dummy_output,
dummy_output + kPayloadLengthSamples),
SetArgPointee<5>(AudioDecoder::kComfortNoise),
Return(kPayloadLengthSamples)));
EXPECT_CALL(mock_decoder, Decode(IsNull(), 0, kSampleRateKhz * 1000, _, _, _))
.WillOnce(DoAll(SetArrayArgument<4>(dummy_output,
dummy_output + kPayloadLengthSamples),
SetArgPointee<5>(AudioDecoder::kComfortNoise),
Return(kPayloadLengthSamples)));
EXPECT_CALL(mock_decoder, Decode(Pointee(2), kPayloadLengthBytes,
kSampleRateKhz * 1000, _, _, _))
.WillOnce(DoAll(SetArrayArgument<4>(dummy_output,
dummy_output + kPayloadLengthSamples),
SetArgPointee<5>(AudioDecoder::kSpeech),
Return(kPayloadLengthSamples)));
EXPECT_EQ(NetEq::kOK, neteq_->RegisterExternalDecoder(
&mock_decoder, kDecoderOpus, kPayloadType,
kSampleRateKhz * 1000));
// Insert one packet (decoder will return speech).
EXPECT_EQ(NetEq::kOK,
neteq_->InsertPacket(
rtp_header, payload, kPayloadLengthBytes, kReceiveTime));
// Insert second packet (decoder will return CNG).
payload[0] = 1;
rtp_header.header.sequenceNumber++;
rtp_header.header.timestamp += kPayloadLengthSamples;
EXPECT_EQ(NetEq::kOK,
neteq_->InsertPacket(
rtp_header, payload, kPayloadLengthBytes, kReceiveTime));
const size_t kMaxOutputSize = static_cast<size_t>(10 * kSampleRateKhz);
int16_t output[kMaxOutputSize];
size_t samples_per_channel;
int num_channels;
uint32_t timestamp;
uint32_t last_timestamp;
NetEqOutputType type;
NetEqOutputType expected_type[8] = {
kOutputNormal, kOutputNormal,
kOutputCNG, kOutputCNG,
kOutputCNG, kOutputCNG,
kOutputNormal, kOutputNormal
};
int expected_timestamp_increment[8] = {
-1, // will not be used.
10 * kSampleRateKhz,
0, 0, // timestamp does not increase during CNG mode.
0, 0,
50 * kSampleRateKhz, 10 * kSampleRateKhz
};
EXPECT_EQ(NetEq::kOK,
neteq_->GetAudio(kMaxOutputSize, output, &samples_per_channel,
&num_channels, &type));
EXPECT_TRUE(neteq_->GetPlayoutTimestamp(&last_timestamp));
for (size_t i = 1; i < 6; ++i) {
ASSERT_EQ(kMaxOutputSize, samples_per_channel);
EXPECT_EQ(1, num_channels);
EXPECT_EQ(expected_type[i - 1], type);
EXPECT_TRUE(neteq_->GetPlayoutTimestamp(&timestamp));
EXPECT_EQ(NetEq::kOK,
neteq_->GetAudio(kMaxOutputSize, output, &samples_per_channel,
&num_channels, &type));
EXPECT_TRUE(neteq_->GetPlayoutTimestamp(&timestamp));
EXPECT_EQ(timestamp, last_timestamp + expected_timestamp_increment[i]);
last_timestamp = timestamp;
}
// Insert third packet, which leaves a gap from last packet.
payload[0] = 2;
rtp_header.header.sequenceNumber += 2;
rtp_header.header.timestamp += 2 * kPayloadLengthSamples;
EXPECT_EQ(NetEq::kOK,
neteq_->InsertPacket(
rtp_header, payload, kPayloadLengthBytes, kReceiveTime));
for (size_t i = 6; i < 8; ++i) {
ASSERT_EQ(kMaxOutputSize, samples_per_channel);
EXPECT_EQ(1, num_channels);
EXPECT_EQ(expected_type[i - 1], type);
EXPECT_EQ(NetEq::kOK,
neteq_->GetAudio(kMaxOutputSize, output, &samples_per_channel,
&num_channels, &type));
EXPECT_TRUE(neteq_->GetPlayoutTimestamp(&timestamp));
EXPECT_EQ(timestamp, last_timestamp + expected_timestamp_increment[i]);
last_timestamp = timestamp;
}
// Now check the packet buffer, and make sure it is empty.
EXPECT_TRUE(packet_buffer_->Empty());
EXPECT_CALL(mock_decoder, Die());
}
TEST_F(NetEqImplTest, UnsupportedDecoder) {
UseNoMocks();
CreateInstance();
static const size_t kNetEqMaxFrameSize = 2880; // 60 ms @ 48 kHz.
static const int kChannels = 2;
const uint8_t kPayloadType = 17; // Just an arbitrary number.
const uint32_t kReceiveTime = 17; // Value doesn't matter for this test.
const int kSampleRateHz = 8000;
const size_t kPayloadLengthSamples =
static_cast<size_t>(10 * kSampleRateHz / 1000); // 10 ms.
const size_t kPayloadLengthBytes = 1;
uint8_t payload[kPayloadLengthBytes]= {0};
int16_t dummy_output[kPayloadLengthSamples * kChannels] = {0};
WebRtcRTPHeader rtp_header;
rtp_header.header.payloadType = kPayloadType;
rtp_header.header.sequenceNumber = 0x1234;
rtp_header.header.timestamp = 0x12345678;
rtp_header.header.ssrc = 0x87654321;
class MockAudioDecoder : public AudioDecoder {
public:
void Reset() override {}
MOCK_CONST_METHOD2(PacketDuration, int(const uint8_t*, size_t));
MOCK_METHOD5(DecodeInternal, int(const uint8_t*, size_t, int, int16_t*,
SpeechType*));
size_t Channels() const override { return kChannels; }
} decoder_;
const uint8_t kFirstPayloadValue = 1;
const uint8_t kSecondPayloadValue = 2;
EXPECT_CALL(decoder_, PacketDuration(Pointee(kFirstPayloadValue),
kPayloadLengthBytes))
.Times(AtLeast(1))
.WillRepeatedly(Return(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(kNetEqMaxFrameSize));
EXPECT_EQ(NetEq::kOK,
neteq_->RegisterExternalDecoder(&decoder_, kDecoderPCM16B,
kPayloadType, kSampleRateHz));
// Insert one packet.
payload[0] = kFirstPayloadValue; // This will make Decode() fail.
EXPECT_EQ(NetEq::kOK,
neteq_->InsertPacket(
rtp_header, payload, kPayloadLengthBytes, kReceiveTime));
// Insert another packet.
payload[0] = kSecondPayloadValue; // This will make Decode() successful.
rtp_header.header.sequenceNumber++;
// The second timestamp needs to be at least 30 ms after the first to make
// the second packet get decoded.
rtp_header.header.timestamp += 3 * kPayloadLengthSamples;
EXPECT_EQ(NetEq::kOK,
neteq_->InsertPacket(
rtp_header, payload, kPayloadLengthBytes, kReceiveTime));
const size_t kMaxOutputSize =
static_cast<size_t>(10 * kSampleRateHz / 1000 * kChannels);
int16_t output[kMaxOutputSize];
size_t samples_per_channel;
int num_channels;
NetEqOutputType type;
EXPECT_EQ(NetEq::kFail, neteq_->GetAudio(kMaxOutputSize, output,
&samples_per_channel, &num_channels,
&type));
EXPECT_EQ(NetEq::kOtherDecoderError, neteq_->LastError());
EXPECT_EQ(kMaxOutputSize, samples_per_channel * kChannels);
EXPECT_EQ(kChannels, num_channels);
EXPECT_EQ(NetEq::kOK, neteq_->GetAudio(kMaxOutputSize, output,
&samples_per_channel, &num_channels,
&type));
EXPECT_EQ(kMaxOutputSize, samples_per_channel * kChannels);
EXPECT_EQ(kChannels, num_channels);
}
// This test inserts packets until the buffer is flushed. After that, it asks
// NetEq for the network statistics. The purpose of the test is to make sure
// that even though the buffer size increment is negative (which it becomes when
// the packet causing a flush is inserted), the packet length stored in the
// decision logic remains valid.
TEST_F(NetEqImplTest, FloodBufferAndGetNetworkStats) {
UseNoMocks();
CreateInstance();
const size_t kPayloadLengthSamples = 80;
const size_t kPayloadLengthBytes = 2 * kPayloadLengthSamples; // PCM 16-bit.
const uint8_t kPayloadType = 17; // Just an arbitrary number.
const uint32_t kReceiveTime = 17; // Value doesn't matter for this test.
uint8_t payload[kPayloadLengthBytes] = {0};
WebRtcRTPHeader rtp_header;
rtp_header.header.payloadType = kPayloadType;
rtp_header.header.sequenceNumber = 0x1234;
rtp_header.header.timestamp = 0x12345678;
rtp_header.header.ssrc = 0x87654321;
EXPECT_EQ(NetEq::kOK,
neteq_->RegisterPayloadType(kDecoderPCM16B, kPayloadType));
// Insert packets until the buffer flushes.
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, kPayloadLengthBytes,
kReceiveTime));
rtp_header.header.timestamp +=
rtc::checked_cast<uint32_t>(kPayloadLengthSamples);
++rtp_header.header.sequenceNumber;
}
EXPECT_EQ(1u, packet_buffer_->NumPacketsInBuffer());
// Ask for network statistics. This should not crash.
NetEqNetworkStatistics stats;
EXPECT_EQ(NetEq::kOK, neteq_->NetworkStatistics(&stats));
}
TEST_F(NetEqImplTest, DecodedPayloadTooShort) {
UseNoMocks();
CreateInstance();
const uint8_t kPayloadType = 17; // Just an arbitrary number.
const uint32_t kReceiveTime = 17; // Value doesn't matter for this test.
const int kSampleRateHz = 8000;
const size_t kPayloadLengthSamples =
static_cast<size_t>(10 * kSampleRateHz / 1000); // 10 ms.
const size_t kPayloadLengthBytes = 2 * kPayloadLengthSamples;
uint8_t payload[kPayloadLengthBytes] = {0};
WebRtcRTPHeader rtp_header;
rtp_header.header.payloadType = kPayloadType;
rtp_header.header.sequenceNumber = 0x1234;
rtp_header.header.timestamp = 0x12345678;
rtp_header.header.ssrc = 0x87654321;
// Create a mock decoder object.
MockAudioDecoder mock_decoder;
EXPECT_CALL(mock_decoder, Reset()).WillRepeatedly(Return());
EXPECT_CALL(mock_decoder, Channels()).WillRepeatedly(Return(1));
EXPECT_CALL(mock_decoder, IncomingPacket(_, kPayloadLengthBytes, _, _, _))
.WillRepeatedly(Return(0));
EXPECT_CALL(mock_decoder, PacketDuration(_, _))
.WillRepeatedly(Return(kPayloadLengthSamples));
int16_t dummy_output[kPayloadLengthSamples] = {0};
// The below expectation will make the mock decoder write
// |kPayloadLengthSamples| - 5 zeros to the output array, and mark it as
// speech. That is, the decoded length is 5 samples shorter than the expected.
EXPECT_CALL(mock_decoder,
Decode(_, kPayloadLengthBytes, kSampleRateHz, _, _, _))
.WillOnce(
DoAll(SetArrayArgument<4>(dummy_output,
dummy_output + kPayloadLengthSamples - 5),
SetArgPointee<5>(AudioDecoder::kSpeech),
Return(kPayloadLengthSamples - 5)));
EXPECT_EQ(NetEq::kOK,
neteq_->RegisterExternalDecoder(&mock_decoder, kDecoderPCM16B,
kPayloadType, kSampleRateHz));
// Insert one packet.
EXPECT_EQ(NetEq::kOK,
neteq_->InsertPacket(rtp_header, payload, kPayloadLengthBytes,
kReceiveTime));
EXPECT_EQ(5u, neteq_->sync_buffer_for_test()->FutureLength());
// Pull audio once.
const size_t kMaxOutputSize = static_cast<size_t>(10 * kSampleRateHz / 1000);
int16_t output[kMaxOutputSize];
size_t samples_per_channel;
int num_channels;
NetEqOutputType type;
EXPECT_EQ(NetEq::kOK,
neteq_->GetAudio(kMaxOutputSize, output, &samples_per_channel,
&num_channels, &type));
ASSERT_EQ(kMaxOutputSize, samples_per_channel);
EXPECT_EQ(1, num_channels);
EXPECT_EQ(kOutputNormal, type);
EXPECT_CALL(mock_decoder, Die());
}
// This test checks the behavior of NetEq when audio decoder fails.
TEST_F(NetEqImplTest, DecodingError) {
UseNoMocks();
CreateInstance();
const uint8_t kPayloadType = 17; // Just an arbitrary number.
const uint32_t kReceiveTime = 17; // Value doesn't matter for this test.
const int kSampleRateHz = 8000;
const int kDecoderErrorCode = -97; // Any negative number.
// We let decoder return 5 ms each time, and therefore, 2 packets make 10 ms.
const size_t kFrameLengthSamples =
static_cast<size_t>(5 * kSampleRateHz / 1000);
const size_t kPayloadLengthBytes = 1; // This can be arbitrary.
uint8_t payload[kPayloadLengthBytes] = {0};
WebRtcRTPHeader rtp_header;
rtp_header.header.payloadType = kPayloadType;
rtp_header.header.sequenceNumber = 0x1234;
rtp_header.header.timestamp = 0x12345678;
rtp_header.header.ssrc = 0x87654321;
// Create a mock decoder object.
MockAudioDecoder mock_decoder;
EXPECT_CALL(mock_decoder, Reset()).WillRepeatedly(Return());
EXPECT_CALL(mock_decoder, Channels()).WillRepeatedly(Return(1));
EXPECT_CALL(mock_decoder, IncomingPacket(_, kPayloadLengthBytes, _, _, _))
.WillRepeatedly(Return(0));
EXPECT_CALL(mock_decoder, PacketDuration(_, _))
.WillRepeatedly(Return(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; // Dummy variable.
// Mock decoder works normally the first time.
EXPECT_CALL(mock_decoder,
Decode(_, kPayloadLengthBytes, kSampleRateHz, _, _, _))
.Times(3)
.WillRepeatedly(
DoAll(SetArrayArgument<4>(dummy_output,
dummy_output + kFrameLengthSamples),
SetArgPointee<5>(AudioDecoder::kSpeech),
Return(kFrameLengthSamples)))
.RetiresOnSaturation();
// Then mock decoder fails. A common reason for failure can be buffer being
// too short
EXPECT_CALL(mock_decoder,
Decode(_, kPayloadLengthBytes, kSampleRateHz, _, _, _))
.WillOnce(Return(-1))
.RetiresOnSaturation();
// Mock decoder finally returns to normal.
EXPECT_CALL(mock_decoder,
Decode(_, kPayloadLengthBytes, kSampleRateHz, _, _, _))
.Times(2)
.WillRepeatedly(
DoAll(SetArrayArgument<4>(dummy_output,
dummy_output + kFrameLengthSamples),
SetArgPointee<5>(AudioDecoder::kSpeech),
Return(kFrameLengthSamples)));
}
EXPECT_EQ(NetEq::kOK,
neteq_->RegisterExternalDecoder(&mock_decoder, kDecoderPCM16B,
kPayloadType, kSampleRateHz));
// Insert packets.
for (int i = 0; i < 6; ++i) {
rtp_header.header.sequenceNumber += 1;
rtp_header.header.timestamp += kFrameLengthSamples;
EXPECT_EQ(NetEq::kOK,
neteq_->InsertPacket(rtp_header, payload, kPayloadLengthBytes,
kReceiveTime));
}
// Pull audio.
const size_t kMaxOutputSize = static_cast<size_t>(10 * kSampleRateHz / 1000);
int16_t output[kMaxOutputSize];
size_t samples_per_channel;
int num_channels;
NetEqOutputType type;
EXPECT_EQ(NetEq::kOK,
neteq_->GetAudio(kMaxOutputSize, output, &samples_per_channel,
&num_channels, &type));
EXPECT_EQ(kMaxOutputSize, samples_per_channel);
EXPECT_EQ(1, num_channels);
EXPECT_EQ(kOutputNormal, type);
// Pull audio again. Decoder fails.
EXPECT_EQ(NetEq::kFail,
neteq_->GetAudio(kMaxOutputSize, output, &samples_per_channel,
&num_channels, &type));
EXPECT_EQ(NetEq::kDecoderErrorCode, neteq_->LastError());
EXPECT_EQ(kDecoderErrorCode, neteq_->LastDecoderError());
EXPECT_EQ(kMaxOutputSize, samples_per_channel);
EXPECT_EQ(1, num_channels);
// TODO(minyue): should NetEq better give kOutputPLC, since it is actually an
// expansion.
EXPECT_EQ(kOutputNormal, type);
// Pull audio again, should continue an expansion.
EXPECT_EQ(NetEq::kOK,
neteq_->GetAudio(kMaxOutputSize, output, &samples_per_channel,
&num_channels, &type));
EXPECT_EQ(kMaxOutputSize, samples_per_channel);
EXPECT_EQ(1, num_channels);
EXPECT_EQ(kOutputPLC, type);
// Pull audio again, should behave normal.
EXPECT_EQ(NetEq::kOK,
neteq_->GetAudio(kMaxOutputSize, output, &samples_per_channel,
&num_channels, &type));
EXPECT_EQ(kMaxOutputSize, samples_per_channel);
EXPECT_EQ(1, num_channels);
EXPECT_EQ(kOutputNormal, type);
EXPECT_CALL(mock_decoder, Die());
}
// This test checks the behavior of NetEq when audio decoder fails during CNG.
TEST_F(NetEqImplTest, DecodingErrorDuringInternalCng) {
UseNoMocks();
CreateInstance();
const uint8_t kPayloadType = 17; // Just an arbitrary number.
const uint32_t kReceiveTime = 17; // Value doesn't matter for this test.
const int kSampleRateHz = 8000;
const int kDecoderErrorCode = -97; // Any negative number.
// We let decoder return 5 ms each time, and therefore, 2 packets make 10 ms.
const size_t kFrameLengthSamples =
static_cast<size_t>(5 * kSampleRateHz / 1000);
const size_t kPayloadLengthBytes = 1; // This can be arbitrary.
uint8_t payload[kPayloadLengthBytes] = {0};
WebRtcRTPHeader rtp_header;
rtp_header.header.payloadType = kPayloadType;
rtp_header.header.sequenceNumber = 0x1234;
rtp_header.header.timestamp = 0x12345678;
rtp_header.header.ssrc = 0x87654321;
// Create a mock decoder object.
MockAudioDecoder mock_decoder;
EXPECT_CALL(mock_decoder, Reset()).WillRepeatedly(Return());
EXPECT_CALL(mock_decoder, Channels()).WillRepeatedly(Return(1));
EXPECT_CALL(mock_decoder, IncomingPacket(_, kPayloadLengthBytes, _, _, _))
.WillRepeatedly(Return(0));
EXPECT_CALL(mock_decoder, PacketDuration(_, _))
.WillRepeatedly(Return(kFrameLengthSamples));
EXPECT_CALL(mock_decoder, ErrorCode())
.WillOnce(Return(kDecoderErrorCode));
int16_t dummy_output[kFrameLengthSamples] = {0};
{
InSequence sequence; // Dummy variable.
// Mock decoder works normally the first 2 times.
EXPECT_CALL(mock_decoder,
Decode(_, kPayloadLengthBytes, kSampleRateHz, _, _, _))
.Times(2)
.WillRepeatedly(
DoAll(SetArrayArgument<4>(dummy_output,
dummy_output + kFrameLengthSamples),
SetArgPointee<5>(AudioDecoder::kComfortNoise),
Return(kFrameLengthSamples)))
.RetiresOnSaturation();
// Then mock decoder fails. A common reason for failure can be buffer being
// too short
EXPECT_CALL(mock_decoder, Decode(nullptr, 0, kSampleRateHz, _, _, _))
.WillOnce(Return(-1))
.RetiresOnSaturation();
// Mock decoder finally returns to normal.
EXPECT_CALL(mock_decoder, Decode(nullptr, 0, kSampleRateHz, _, _, _))
.Times(2)
.WillRepeatedly(
DoAll(SetArrayArgument<4>(dummy_output,
dummy_output + kFrameLengthSamples),
SetArgPointee<5>(AudioDecoder::kComfortNoise),
Return(kFrameLengthSamples)));
}
EXPECT_EQ(NetEq::kOK,
neteq_->RegisterExternalDecoder(&mock_decoder, kDecoderPCM16B,
kPayloadType, kSampleRateHz));
// Insert 2 packets. This will make netEq into codec internal CNG mode.
for (int i = 0; i < 2; ++i) {
rtp_header.header.sequenceNumber += 1;
rtp_header.header.timestamp += kFrameLengthSamples;
EXPECT_EQ(NetEq::kOK,
neteq_->InsertPacket(rtp_header, payload, kPayloadLengthBytes,
kReceiveTime));
}
// Pull audio.
const size_t kMaxOutputSize = static_cast<size_t>(10 * kSampleRateHz / 1000);
int16_t output[kMaxOutputSize];
size_t samples_per_channel;
int num_channels;
NetEqOutputType type;
EXPECT_EQ(NetEq::kOK,
neteq_->GetAudio(kMaxOutputSize, output, &samples_per_channel,
&num_channels, &type));
EXPECT_EQ(kMaxOutputSize, samples_per_channel);
EXPECT_EQ(1, num_channels);
EXPECT_EQ(kOutputCNG, type);
// Pull audio again. Decoder fails.
EXPECT_EQ(NetEq::kFail,
neteq_->GetAudio(kMaxOutputSize, output, &samples_per_channel,
&num_channels, &type));
EXPECT_EQ(NetEq::kDecoderErrorCode, neteq_->LastError());
EXPECT_EQ(kDecoderErrorCode, neteq_->LastDecoderError());
EXPECT_EQ(kMaxOutputSize, samples_per_channel);
EXPECT_EQ(1, num_channels);
// TODO(minyue): should NetEq better give kOutputPLC, since it is actually an
// expansion.
EXPECT_EQ(kOutputCNG, type);
// Pull audio again, should resume codec CNG.
EXPECT_EQ(NetEq::kOK,
neteq_->GetAudio(kMaxOutputSize, output, &samples_per_channel,
&num_channels, &type));
EXPECT_EQ(kMaxOutputSize, samples_per_channel);
EXPECT_EQ(1, num_channels);
EXPECT_EQ(kOutputCNG, type);
EXPECT_CALL(mock_decoder, Die());
}
}// namespace webrtc