|  | /* | 
|  | *  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/neteq4/interface/neteq.h" | 
|  | #include "webrtc/modules/audio_coding/neteq4/neteq_impl.h" | 
|  |  | 
|  | #include "gmock/gmock.h" | 
|  | #include "gtest/gtest.h" | 
|  | #include "webrtc/modules/audio_coding/neteq4/mock/mock_audio_decoder.h" | 
|  | #include "webrtc/modules/audio_coding/neteq4/mock/mock_buffer_level_filter.h" | 
|  | #include "webrtc/modules/audio_coding/neteq4/mock/mock_decoder_database.h" | 
|  | #include "webrtc/modules/audio_coding/neteq4/mock/mock_delay_manager.h" | 
|  | #include "webrtc/modules/audio_coding/neteq4/mock/mock_delay_peak_detector.h" | 
|  | #include "webrtc/modules/audio_coding/neteq4/mock/mock_dtmf_buffer.h" | 
|  | #include "webrtc/modules/audio_coding/neteq4/mock/mock_dtmf_tone_generator.h" | 
|  | #include "webrtc/modules/audio_coding/neteq4/mock/mock_packet_buffer.h" | 
|  | #include "webrtc/modules/audio_coding/neteq4/mock/mock_payload_splitter.h" | 
|  | #include "webrtc/modules/audio_coding/neteq4/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: | 
|  | static const int kInitSampleRateHz = 8000; | 
|  | NetEqImplTest() { | 
|  | buffer_level_filter_ = new MockBufferLevelFilter; | 
|  | decoder_database_ = new MockDecoderDatabase; | 
|  | delay_peak_detector_ = new MockDelayPeakDetector; | 
|  | EXPECT_CALL(*delay_peak_detector_, Reset()).Times(1); | 
|  | delay_manager_ = new MockDelayManager(NetEq::kMaxNumPacketsInBuffer, | 
|  | delay_peak_detector_); | 
|  | dtmf_buffer_ = new MockDtmfBuffer(kInitSampleRateHz); | 
|  | dtmf_tone_generator_ = new MockDtmfToneGenerator; | 
|  | packet_buffer_ = new MockPacketBuffer(NetEq::kMaxNumPacketsInBuffer, | 
|  | NetEq::kMaxBytesInBuffer); | 
|  | payload_splitter_ = new MockPayloadSplitter; | 
|  | timestamp_scaler_ = new TimestampScaler(*decoder_database_); | 
|  | EXPECT_CALL(*decoder_database_, GetActiveCngDecoder()) | 
|  | .WillOnce(ReturnNull()); | 
|  | neteq_ = new NetEqImpl(kInitSampleRateHz, | 
|  | buffer_level_filter_, | 
|  | decoder_database_, | 
|  | delay_manager_, | 
|  | delay_peak_detector_, | 
|  | dtmf_buffer_, | 
|  | dtmf_tone_generator_, | 
|  | packet_buffer_, | 
|  | payload_splitter_, | 
|  | timestamp_scaler_); | 
|  | } | 
|  |  | 
|  | virtual ~NetEqImplTest() { | 
|  | EXPECT_CALL(*buffer_level_filter_, Die()).Times(1); | 
|  | EXPECT_CALL(*decoder_database_, Die()).Times(1); | 
|  | EXPECT_CALL(*delay_manager_, Die()).Times(1); | 
|  | EXPECT_CALL(*delay_peak_detector_, Die()).Times(1); | 
|  | EXPECT_CALL(*dtmf_buffer_, Die()).Times(1); | 
|  | EXPECT_CALL(*dtmf_tone_generator_, Die()).Times(1); | 
|  | EXPECT_CALL(*packet_buffer_, Die()).Times(1); | 
|  | delete neteq_; | 
|  | } | 
|  |  | 
|  | NetEqImpl* neteq_; | 
|  | MockBufferLevelFilter* buffer_level_filter_; | 
|  | MockDecoderDatabase* decoder_database_; | 
|  | MockDelayPeakDetector* delay_peak_detector_; | 
|  | MockDelayManager* delay_manager_; | 
|  | MockDtmfBuffer* dtmf_buffer_; | 
|  | MockDtmfToneGenerator* dtmf_tone_generator_; | 
|  | MockPacketBuffer* packet_buffer_; | 
|  | MockPayloadSplitter* payload_splitter_; | 
|  | TimestampScaler* timestamp_scaler_; | 
|  | }; | 
|  |  | 
|  |  | 
|  | // This tests the interface class NetEq. | 
|  | // TODO(hlundin): Move to separate file? | 
|  | TEST(NetEq, CreateAndDestroy) { | 
|  | NetEq* neteq = NetEq::Create(8000); | 
|  | delete neteq; | 
|  | } | 
|  |  | 
|  | TEST_F(NetEqImplTest, RegisterPayloadType) { | 
|  | uint8_t rtp_payload_type = 0; | 
|  | NetEqDecoder codec_type = kDecoderPCMu; | 
|  | EXPECT_CALL(*decoder_database_, | 
|  | RegisterPayload(rtp_payload_type, codec_type)); | 
|  | neteq_->RegisterPayloadType(codec_type, rtp_payload_type); | 
|  | } | 
|  |  | 
|  | TEST_F(NetEqImplTest, RemovePayloadType) { | 
|  | uint8_t rtp_payload_type = 0; | 
|  | EXPECT_CALL(*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) { | 
|  | 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(*decoder_database_, IsRed(kPayloadType)) | 
|  | .WillRepeatedly(Return(false));  // This is not RED. | 
|  | EXPECT_CALL(*decoder_database_, CheckPayloadTypes(_)) | 
|  | .Times(2) | 
|  | .WillRepeatedly(Return(DecoderDatabase::kOK));  // Payload type is valid. | 
|  | EXPECT_CALL(*decoder_database_, IsDtmf(kPayloadType)) | 
|  | .WillRepeatedly(Return(false));  // This is not DTMF. | 
|  | EXPECT_CALL(*decoder_database_, GetDecoder(kPayloadType)) | 
|  | .Times(2) | 
|  | .WillRepeatedly(Return(&mock_decoder)); | 
|  | EXPECT_CALL(*decoder_database_, IsComfortNoise(kPayloadType)) | 
|  | .WillRepeatedly(Return(false));  // This is not CNG. | 
|  | DecoderDatabase::DecoderInfo info; | 
|  | info.codec_type = kDecoderPCMu; | 
|  | EXPECT_CALL(*decoder_database_, GetDecoderInfo(kPayloadType)) | 
|  | .WillRepeatedly(Return(&info)); | 
|  |  | 
|  | // Expectations for packet buffer. | 
|  | EXPECT_CALL(*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(*packet_buffer_, Flush()) | 
|  | .Times(1); | 
|  | EXPECT_CALL(*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). | 
|  |  | 
|  | // Expectations for DTMF buffer. | 
|  | EXPECT_CALL(*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(*delay_manager_, LastDecoderType(kDecoderPCMu)) | 
|  | .Times(1); | 
|  | EXPECT_CALL(*delay_manager_, last_pack_cng_or_dtmf()) | 
|  | .Times(2) | 
|  | .WillRepeatedly(Return(-1)); | 
|  | EXPECT_CALL(*delay_manager_, set_last_pack_cng_or_dtmf(0)) | 
|  | .Times(1); | 
|  | EXPECT_CALL(*delay_manager_, ResetPacketIatCount()).Times(1); | 
|  | // Expectations when the second packet is inserted. Slightly different. | 
|  | EXPECT_CALL(*delay_manager_, LastDecoderType(kDecoderPCMu)) | 
|  | .Times(1); | 
|  | EXPECT_CALL(*delay_manager_, last_pack_cng_or_dtmf()) | 
|  | .WillOnce(Return(0)); | 
|  | } | 
|  |  | 
|  | // Expectations for payload splitter. | 
|  | EXPECT_CALL(*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); | 
|  | } | 
|  |  | 
|  | }  // namespace webrtc |