/* * Copyright (c) 2015 The WebRTC project authors. All Rights Reserved. * * Use of this source code is governed by a BSD-style license * that can be found in the LICENSE file in the root of the source * tree. An additional intellectual property rights grant can be found * in the file PATENTS. All contributing project authors may * be found in the AUTHORS file in the root of the source tree. */ #include #include "common_types.h" #include "modules/audio_coding/acm2/rent_a_codec.h" #include "rtc_base/arraysize.h" #include "test/gtest.h" #include "test/mock_audio_encoder.h" namespace webrtc { namespace acm2 { using ::testing::Return; namespace { const int kDataLengthSamples = 80; const int kPacketSizeSamples = 2 * kDataLengthSamples; const int16_t kZeroData[kDataLengthSamples] = {0}; const CodecInst kDefaultCodecInst = {0, "pcmu", 8000, kPacketSizeSamples, 1, 64000}; const int kCngPt = 13; class Marker final { public: MOCK_METHOD1(Mark, void(std::string desc)); }; } // namespace class RentACodecTestF : public ::testing::Test { protected: void CreateCodec() { auto speech_encoder = rent_a_codec_.RentEncoder(kDefaultCodecInst); ASSERT_TRUE(speech_encoder); RentACodec::StackParameters param; param.use_cng = true; param.speech_encoder = std::move(speech_encoder); encoder_ = rent_a_codec_.RentEncoderStack(¶m); } void EncodeAndVerify(size_t expected_out_length, uint32_t expected_timestamp, int expected_payload_type, int expected_send_even_if_empty) { rtc::Buffer out; AudioEncoder::EncodedInfo encoded_info; encoded_info = encoder_->Encode(timestamp_, kZeroData, &out); timestamp_ += kDataLengthSamples; EXPECT_TRUE(encoded_info.redundant.empty()); EXPECT_EQ(expected_out_length, encoded_info.encoded_bytes); EXPECT_EQ(expected_timestamp, encoded_info.encoded_timestamp); if (expected_payload_type >= 0) EXPECT_EQ(expected_payload_type, encoded_info.payload_type); if (expected_send_even_if_empty >= 0) EXPECT_EQ(static_cast(expected_send_even_if_empty), encoded_info.send_even_if_empty); } RentACodec rent_a_codec_; std::unique_ptr encoder_; uint32_t timestamp_ = 0; }; // This test verifies that CNG frames are delivered as expected. Since the frame // size is set to 20 ms, we expect the first encode call to produce no output // (which is signaled as 0 bytes output of type kNoEncoding). The next encode // call should produce one SID frame of 9 bytes. The third call should not // result in any output (just like the first one). The fourth and final encode // call should produce an "empty frame", which is like no output, but with // AudioEncoder::EncodedInfo::send_even_if_empty set to true. (The reason to // produce an empty frame is to drive sending of DTMF packets in the RTP/RTCP // module.) TEST_F(RentACodecTestF, VerifyCngFrames) { CreateCodec(); uint32_t expected_timestamp = timestamp_; // Verify no frame. { SCOPED_TRACE("First encoding"); EncodeAndVerify(0, expected_timestamp, -1, -1); } // Verify SID frame delivered. { SCOPED_TRACE("Second encoding"); EncodeAndVerify(9, expected_timestamp, kCngPt, 1); } // Verify no frame. { SCOPED_TRACE("Third encoding"); EncodeAndVerify(0, expected_timestamp, -1, -1); } // Verify NoEncoding. expected_timestamp += 2 * kDataLengthSamples; { SCOPED_TRACE("Fourth encoding"); EncodeAndVerify(0, expected_timestamp, kCngPt, 1); } } TEST(RentACodecTest, ExternalEncoder) { const int kSampleRateHz = 8000; auto* external_encoder = new MockAudioEncoder; EXPECT_CALL(*external_encoder, SampleRateHz()) .WillRepeatedly(Return(kSampleRateHz)); EXPECT_CALL(*external_encoder, NumChannels()).WillRepeatedly(Return(1)); EXPECT_CALL(*external_encoder, SetFec(false)).WillRepeatedly(Return(true)); RentACodec rac; RentACodec::StackParameters param; param.speech_encoder = std::unique_ptr(external_encoder); std::unique_ptr encoder_stack = rac.RentEncoderStack(¶m); EXPECT_EQ(external_encoder, encoder_stack.get()); const int kPacketSizeSamples = kSampleRateHz / 100; int16_t audio[kPacketSizeSamples] = {0}; rtc::Buffer encoded; AudioEncoder::EncodedInfo info; Marker marker; { ::testing::InSequence s; info.encoded_timestamp = 0; EXPECT_CALL( *external_encoder, EncodeImpl(0, rtc::ArrayView(audio), &encoded)) .WillOnce(Return(info)); EXPECT_CALL(marker, Mark("A")); EXPECT_CALL(marker, Mark("B")); EXPECT_CALL(marker, Mark("C")); } info = encoder_stack->Encode(0, audio, &encoded); EXPECT_EQ(0u, info.encoded_timestamp); marker.Mark("A"); // Change to internal encoder. CodecInst codec_inst = kDefaultCodecInst; codec_inst.pacsize = kPacketSizeSamples; param.speech_encoder = rac.RentEncoder(codec_inst); ASSERT_TRUE(param.speech_encoder); AudioEncoder* enc = param.speech_encoder.get(); std::unique_ptr stack = rac.RentEncoderStack(¶m); EXPECT_EQ(enc, stack.get()); // Don't expect any more calls to the external encoder. info = stack->Encode(1, audio, &encoded); marker.Mark("B"); encoder_stack.reset(); marker.Mark("C"); } // Verify that the speech encoder's Reset method is called when CNG or RED // (or both) are switched on, but not when they're switched off. void TestCngAndRedResetSpeechEncoder(bool use_cng, bool use_red) { auto make_enc = [] { auto speech_encoder = std::unique_ptr(new MockAudioEncoder); EXPECT_CALL(*speech_encoder, NumChannels()).WillRepeatedly(Return(1)); EXPECT_CALL(*speech_encoder, Max10MsFramesInAPacket()) .WillRepeatedly(Return(2)); EXPECT_CALL(*speech_encoder, SampleRateHz()).WillRepeatedly(Return(8000)); EXPECT_CALL(*speech_encoder, SetFec(false)).WillRepeatedly(Return(true)); return speech_encoder; }; auto speech_encoder1 = make_enc(); auto speech_encoder2 = make_enc(); Marker marker; { ::testing::InSequence s; EXPECT_CALL(marker, Mark("disabled")); EXPECT_CALL(marker, Mark("enabled")); if (use_cng || use_red) EXPECT_CALL(*speech_encoder2, Reset()); } RentACodec::StackParameters param1, param2; param1.speech_encoder = std::move(speech_encoder1); param2.speech_encoder = std::move(speech_encoder2); param2.use_cng = use_cng; param2.use_red = use_red; marker.Mark("disabled"); RentACodec rac; rac.RentEncoderStack(¶m1); marker.Mark("enabled"); rac.RentEncoderStack(¶m2); } TEST(RentACodecTest, CngResetsSpeechEncoder) { TestCngAndRedResetSpeechEncoder(true, false); } TEST(RentACodecTest, RedResetsSpeechEncoder) { TestCngAndRedResetSpeechEncoder(false, true); } TEST(RentACodecTest, CngAndRedResetsSpeechEncoder) { TestCngAndRedResetSpeechEncoder(true, true); } TEST(RentACodecTest, NoCngAndRedNoSpeechEncoderReset) { TestCngAndRedResetSpeechEncoder(false, false); } TEST(RentACodecTest, RentEncoderError) { const CodecInst codec_inst = { 0, "Robert'); DROP TABLE Students;", 8000, 160, 1, 64000}; RentACodec rent_a_codec; EXPECT_FALSE(rent_a_codec.RentEncoder(codec_inst)); } TEST(RentACodecTest, RentEncoderStackWithoutSpeechEncoder) { RentACodec::StackParameters sp; EXPECT_EQ(nullptr, sp.speech_encoder); EXPECT_EQ(nullptr, RentACodec().RentEncoderStack(&sp)); } } // namespace acm2 } // namespace webrtc