summaryrefslogtreecommitdiffstats
path: root/chromium/third_party/webrtc/modules/audio_coding/neteq/neteq_impl_unittest.cc
diff options
context:
space:
mode:
Diffstat (limited to 'chromium/third_party/webrtc/modules/audio_coding/neteq/neteq_impl_unittest.cc')
-rw-r--r--chromium/third_party/webrtc/modules/audio_coding/neteq/neteq_impl_unittest.cc498
1 files changed, 498 insertions, 0 deletions
diff --git a/chromium/third_party/webrtc/modules/audio_coding/neteq/neteq_impl_unittest.cc b/chromium/third_party/webrtc/modules/audio_coding/neteq/neteq_impl_unittest.cc
new file mode 100644
index 00000000000..2e66487fae5
--- /dev/null
+++ b/chromium/third_party/webrtc/modules/audio_coding/neteq/neteq_impl_unittest.cc
@@ -0,0 +1,498 @@
+/*
+ * 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/interface/neteq.h"
+#include "webrtc/modules/audio_coding/neteq/neteq_impl.h"
+
+#include "gmock/gmock.h"
+#include "gtest/gtest.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::Return;
+using ::testing::ReturnNull;
+using ::testing::_;
+using ::testing::SetArgPointee;
+using ::testing::InSequence;
+using ::testing::Invoke;
+using ::testing::WithArg;
+
+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_.sample_rate_hz,
+ 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 int 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;
+ // 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 (int 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(1, 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 int kPayloadLengthSamples = 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:
+ explicit CountingSamplesDecoder(enum NetEqDecoder type)
+ : AudioDecoder(type), next_value_(1) {}
+
+ // Produce as many samples as input bytes (|encoded_len|).
+ virtual int Decode(const uint8_t* encoded,
+ size_t encoded_len,
+ int16_t* decoded,
+ SpeechType* speech_type) {
+ for (size_t i = 0; i < encoded_len; ++i) {
+ decoded[i] = next_value_++;
+ }
+ *speech_type = kSpeech;
+ return encoded_len;
+ }
+
+ virtual int Init() {
+ next_value_ = 1;
+ return 0;
+ }
+
+ uint16_t next_value() const { return next_value_; }
+
+ private:
+ int16_t next_value_;
+ } decoder_(kDecoderPCM16B);
+
+ EXPECT_EQ(NetEq::kOK,
+ neteq_->RegisterExternalDecoder(
+ &decoder_, kDecoderPCM16B, kPayloadType));
+
+ // Insert one packet.
+ EXPECT_EQ(NetEq::kOK,
+ neteq_->InsertPacket(
+ rtp_header, payload, kPayloadLengthBytes, kReceiveTime));
+
+ // Pull audio once.
+ const int kMaxOutputSize = 10 * kSampleRateHz / 1000;
+ int16_t output[kMaxOutputSize];
+ int 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, 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],
+ static_cast<int>(sync_buffer->FutureLength()));
+}
+
+} // namespace webrtc