Move CNG and RED management into the Rent-A-Codec
This leaves CodecOwner without a job, so we eliminate it.
BUG=webrtc:5028
Review URL: https://codereview.webrtc.org/1443653004
Cr-Commit-Position: refs/heads/master@{#10650}
diff --git a/webrtc/modules/audio_coding/BUILD.gn b/webrtc/modules/audio_coding/BUILD.gn
index 13f9a5d..94717f1 100644
--- a/webrtc/modules/audio_coding/BUILD.gn
+++ b/webrtc/modules/audio_coding/BUILD.gn
@@ -63,8 +63,6 @@
"main/acm2/call_statistics.h",
"main/acm2/codec_manager.cc",
"main/acm2/codec_manager.h",
- "main/acm2/codec_owner.cc",
- "main/acm2/codec_owner.h",
"main/acm2/initial_delay_manager.cc",
"main/acm2/initial_delay_manager.h",
"main/include/audio_coding_module.h",
diff --git a/webrtc/modules/audio_coding/main/acm2/codec_manager.cc b/webrtc/modules/audio_coding/main/acm2/codec_manager.cc
index ac21020..47bbbde 100644
--- a/webrtc/modules/audio_coding/main/acm2/codec_manager.cc
+++ b/webrtc/modules/audio_coding/main/acm2/codec_manager.cc
@@ -240,7 +240,7 @@
// Check if the codec is already registered as send codec.
bool new_codec = true;
- if (codec_owner_.Encoder()) {
+ if (CurrentEncoder()) {
auto new_codec_id = RentACodec::CodecIdByInst(send_codec_inst_);
RTC_DCHECK(new_codec_id);
auto old_codec_id = RentACodec::CodecIdFromIndex(codec_id);
@@ -263,10 +263,8 @@
AudioEncoder* enc = rent_a_codec_.RentEncoder(send_codec);
if (!enc)
return -1;
- codec_owner_.SetEncoders(
- enc, dtx_enabled_ ? CngPayloadType(send_codec.plfreq) : -1,
- vad_mode_, red_enabled_ ? RedPayloadType(send_codec.plfreq) : -1);
- RTC_DCHECK(codec_owner_.Encoder());
+ RentEncoderStack(enc, send_codec.plfreq);
+ RTC_DCHECK(CurrentEncoder());
codec_fec_enabled_ = codec_fec_enabled_ &&
enc->SetFec(codec_fec_enabled_);
@@ -282,10 +280,8 @@
AudioEncoder* enc = rent_a_codec_.RentEncoder(send_codec);
if (!enc)
return -1;
- codec_owner_.SetEncoders(
- enc, dtx_enabled_ ? CngPayloadType(send_codec.plfreq) : -1,
- vad_mode_, red_enabled_ ? RedPayloadType(send_codec.plfreq) : -1);
- RTC_DCHECK(codec_owner_.Encoder());
+ RentEncoderStack(enc, send_codec.plfreq);
+ RTC_DCHECK(CurrentEncoder());
}
send_codec_inst_.plfreq = send_codec.plfreq;
send_codec_inst_.pacsize = send_codec.pacsize;
@@ -294,12 +290,12 @@
// Check if a change in Rate is required.
if (send_codec.rate != send_codec_inst_.rate) {
- codec_owner_.Encoder()->SetTargetBitrate(send_codec.rate);
+ CurrentEncoder()->SetTargetBitrate(send_codec.rate);
send_codec_inst_.rate = send_codec.rate;
}
codec_fec_enabled_ =
- codec_fec_enabled_ && codec_owner_.Encoder()->SetFec(codec_fec_enabled_);
+ codec_fec_enabled_ && CurrentEncoder()->SetFec(codec_fec_enabled_);
return 0;
}
@@ -328,11 +324,9 @@
const bool success = external_speech_encoder->SetFec(false);
RTC_DCHECK(success);
}
- int cng_pt = dtx_enabled_
- ? CngPayloadType(external_speech_encoder->SampleRateHz())
- : -1;
- int red_pt = red_enabled_ ? RedPayloadType(send_codec_inst_.plfreq) : -1;
- codec_owner_.SetEncoders(external_speech_encoder, cng_pt, vad_mode_, red_pt);
+
+ RentEncoderStack(external_speech_encoder,
+ external_speech_encoder->SampleRateHz());
}
rtc::Optional<CodecInst> CodecManager::GetCodecInst() const {
@@ -340,7 +334,7 @@
WEBRTC_TRACE(webrtc::kTraceStream, webrtc::kTraceAudioCoding, dummy_id,
"SendCodec()");
- if (!codec_owner_.Encoder()) {
+ if (!CurrentEncoder()) {
WEBRTC_TRACE(webrtc::kTraceStream, webrtc::kTraceAudioCoding, dummy_id,
"SendCodec Failed, no codec is registered");
return rtc::Optional<CodecInst>();
@@ -361,11 +355,8 @@
}
if (red_enabled_ != enable) {
red_enabled_ = enable;
- if (codec_owner_.Encoder()) {
- int cng_pt = dtx_enabled_ ? CngPayloadType(send_codec_inst_.plfreq) : -1;
- int red_pt = red_enabled_ ? RedPayloadType(send_codec_inst_.plfreq) : -1;
- codec_owner_.ChangeCngAndRed(cng_pt, vad_mode_, red_pt);
- }
+ if (CurrentEncoder())
+ RentEncoderStack(rent_a_codec_.GetEncoder(), send_codec_inst_.plfreq);
}
return true;
}
@@ -377,7 +368,7 @@
// Check that the send codec is mono. We don't support VAD/DTX for stereo
// sending.
- const auto* enc = codec_owner_.Encoder();
+ auto* enc = rent_a_codec_.GetEncoder();
const bool stereo_send = enc ? (enc->NumChannels() != 1) : false;
if (enable && stereo_send) {
WEBRTC_TRACE(webrtc::kTraceError, webrtc::kTraceAudioCoding, 0,
@@ -396,11 +387,8 @@
if (dtx_enabled_ != enable || vad_mode_ != mode) {
dtx_enabled_ = enable;
vad_mode_ = mode;
- if (enc) {
- int cng_pt = dtx_enabled_ ? CngPayloadType(send_codec_inst_.plfreq) : -1;
- int red_pt = red_enabled_ ? RedPayloadType(send_codec_inst_.plfreq) : -1;
- codec_owner_.ChangeCngAndRed(cng_pt, vad_mode_, red_pt);
- }
+ if (enc)
+ RentEncoderStack(enc, send_codec_inst_.plfreq);
}
return 0;
}
@@ -420,9 +408,9 @@
return -1;
}
- RTC_CHECK(codec_owner_.Encoder());
+ RTC_CHECK(CurrentEncoder());
codec_fec_enabled_ =
- codec_owner_.Encoder()->SetFec(enable_codec_fec) && enable_codec_fec;
+ CurrentEncoder()->SetFec(enable_codec_fec) && enable_codec_fec;
return codec_fec_enabled_ == enable_codec_fec ? 0 : -1;
}
@@ -460,5 +448,17 @@
}
}
+void CodecManager::RentEncoderStack(AudioEncoder* speech_encoder,
+ int sample_rate_hz) {
+ auto cng_config =
+ dtx_enabled_ ? rtc::Optional<RentACodec::CngConfig>(RentACodec::CngConfig{
+ CngPayloadType(sample_rate_hz), vad_mode_})
+ : rtc::Optional<RentACodec::CngConfig>();
+ auto red_pt = red_enabled_
+ ? rtc::Optional<int>(RedPayloadType(sample_rate_hz))
+ : rtc::Optional<int>();
+ rent_a_codec_.RentEncoderStack(speech_encoder, cng_config, red_pt);
+}
+
} // namespace acm2
} // namespace webrtc
diff --git a/webrtc/modules/audio_coding/main/acm2/codec_manager.h b/webrtc/modules/audio_coding/main/acm2/codec_manager.h
index 37018d3..584b7c4 100644
--- a/webrtc/modules/audio_coding/main/acm2/codec_manager.h
+++ b/webrtc/modules/audio_coding/main/acm2/codec_manager.h
@@ -15,7 +15,6 @@
#include "webrtc/base/optional.h"
#include "webrtc/base/scoped_ptr.h"
#include "webrtc/base/thread_checker.h"
-#include "webrtc/modules/audio_coding/main/acm2/codec_owner.h"
#include "webrtc/modules/audio_coding/main/acm2/rent_a_codec.h"
#include "webrtc/modules/audio_coding/main/include/audio_coding_module_typedefs.h"
#include "webrtc/common_types.h"
@@ -57,15 +56,17 @@
bool codec_fec_enabled() const { return codec_fec_enabled_; }
- AudioEncoder* CurrentEncoder() { return codec_owner_.Encoder(); }
- const AudioEncoder* CurrentEncoder() const { return codec_owner_.Encoder(); }
+ AudioEncoder* CurrentEncoder() { return rent_a_codec_.GetEncoderStack(); }
+ const AudioEncoder* CurrentEncoder() const {
+ return rent_a_codec_.GetEncoderStack();
+ }
bool CurrentEncoderIsOpus() const { return encoder_is_opus_; }
private:
int CngPayloadType(int sample_rate_hz) const;
-
int RedPayloadType(int sample_rate_hz) const;
+ void RentEncoderStack(AudioEncoder* speech_encoder, int sample_rate_hz);
rtc::ThreadChecker thread_checker_;
uint8_t cng_nb_pltype_;
@@ -78,7 +79,6 @@
CodecInst send_codec_inst_;
bool red_enabled_;
bool codec_fec_enabled_;
- CodecOwner codec_owner_;
RentACodec rent_a_codec_;
bool encoder_is_opus_;
diff --git a/webrtc/modules/audio_coding/main/acm2/codec_owner.cc b/webrtc/modules/audio_coding/main/acm2/codec_owner.cc
deleted file mode 100644
index 6b9809f..0000000
--- a/webrtc/modules/audio_coding/main/acm2/codec_owner.cc
+++ /dev/null
@@ -1,116 +0,0 @@
-/*
- * 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 "webrtc/modules/audio_coding/main/acm2/codec_owner.h"
-
-#include "webrtc/base/checks.h"
-#include "webrtc/base/logging.h"
-#include "webrtc/engine_configurations.h"
-#include "webrtc/modules/audio_coding/codecs/cng/include/audio_encoder_cng.h"
-#ifdef WEBRTC_CODEC_RED
-#include "webrtc/modules/audio_coding/codecs/red/audio_encoder_copy_red.h"
-#endif
-
-namespace webrtc {
-namespace acm2 {
-
-CodecOwner::CodecOwner() : speech_encoder_(nullptr) {
-}
-
-CodecOwner::~CodecOwner() = default;
-
-namespace {
-
-AudioEncoder* CreateRedEncoder(int red_payload_type,
- AudioEncoder* encoder,
- rtc::scoped_ptr<AudioEncoder>* red_encoder) {
-#ifdef WEBRTC_CODEC_RED
- if (red_payload_type != -1) {
- AudioEncoderCopyRed::Config config;
- config.payload_type = red_payload_type;
- config.speech_encoder = encoder;
- red_encoder->reset(new AudioEncoderCopyRed(config));
- return red_encoder->get();
- }
-#endif
-
- red_encoder->reset();
- return encoder;
-}
-
-void CreateCngEncoder(int cng_payload_type,
- ACMVADMode vad_mode,
- AudioEncoder* encoder,
- rtc::scoped_ptr<AudioEncoder>* cng_encoder) {
- if (cng_payload_type == -1) {
- cng_encoder->reset();
- return;
- }
- AudioEncoderCng::Config config;
- config.num_channels = encoder->NumChannels();
- config.payload_type = cng_payload_type;
- config.speech_encoder = encoder;
- switch (vad_mode) {
- case VADNormal:
- config.vad_mode = Vad::kVadNormal;
- break;
- case VADLowBitrate:
- config.vad_mode = Vad::kVadLowBitrate;
- break;
- case VADAggr:
- config.vad_mode = Vad::kVadAggressive;
- break;
- case VADVeryAggr:
- config.vad_mode = Vad::kVadVeryAggressive;
- break;
- default:
- FATAL();
- }
- cng_encoder->reset(new AudioEncoderCng(config));
-}
-} // namespace
-
-void CodecOwner::SetEncoders(AudioEncoder* external_speech_encoder,
- int cng_payload_type,
- ACMVADMode vad_mode,
- int red_payload_type) {
- speech_encoder_ = external_speech_encoder;
- ChangeCngAndRed(cng_payload_type, vad_mode, red_payload_type);
-}
-
-void CodecOwner::ChangeCngAndRed(int cng_payload_type,
- ACMVADMode vad_mode,
- int red_payload_type) {
- RTC_DCHECK(speech_encoder_);
- if (cng_payload_type != -1 || red_payload_type != -1) {
- // The RED and CNG encoders need to be in sync with the speech encoder, so
- // reset the latter to ensure its buffer is empty.
- speech_encoder_->Reset();
- }
- AudioEncoder* encoder = CreateRedEncoder(
- red_payload_type, speech_encoder_, &red_encoder_);
- CreateCngEncoder(cng_payload_type, vad_mode, encoder, &cng_encoder_);
-}
-
-AudioEncoder* CodecOwner::Encoder() {
- const auto& const_this = *this;
- return const_cast<AudioEncoder*>(const_this.Encoder());
-}
-
-const AudioEncoder* CodecOwner::Encoder() const {
- if (cng_encoder_)
- return cng_encoder_.get();
- if (red_encoder_)
- return red_encoder_.get();
- return speech_encoder_;
-}
-
-} // namespace acm2
-} // namespace webrtc
diff --git a/webrtc/modules/audio_coding/main/acm2/codec_owner.h b/webrtc/modules/audio_coding/main/acm2/codec_owner.h
deleted file mode 100644
index bfeaee6..0000000
--- a/webrtc/modules/audio_coding/main/acm2/codec_owner.h
+++ /dev/null
@@ -1,54 +0,0 @@
-/*
- * 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.
- */
-
-#ifndef WEBRTC_MODULES_AUDIO_CODING_MAIN_ACM2_CODEC_OWNER_H_
-#define WEBRTC_MODULES_AUDIO_CODING_MAIN_ACM2_CODEC_OWNER_H_
-
-#include "webrtc/base/constructormagic.h"
-#include "webrtc/base/scoped_ptr.h"
-#include "webrtc/common_types.h"
-#include "webrtc/modules/audio_coding/codecs/audio_encoder.h"
-#include "webrtc/modules/audio_coding/codecs/audio_decoder.h"
-#include "webrtc/modules/audio_coding/main/include/audio_coding_module_typedefs.h"
-
-namespace webrtc {
-namespace acm2 {
-
-class CodecOwner {
- public:
- CodecOwner();
- ~CodecOwner();
-
- void SetEncoders(AudioEncoder* external_speech_encoder,
- int cng_payload_type,
- ACMVADMode vad_mode,
- int red_payload_type);
-
- void ChangeCngAndRed(int cng_payload_type,
- ACMVADMode vad_mode,
- int red_payload_type);
-
- AudioEncoder* Encoder();
- const AudioEncoder* Encoder() const;
-
- private:
- AudioEncoder* speech_encoder_;
-
- // |cng_encoder_| and |red_encoder_| are valid iff CNG or RED, respectively,
- // are active.
- rtc::scoped_ptr<AudioEncoder> cng_encoder_;
- rtc::scoped_ptr<AudioEncoder> red_encoder_;
-
- RTC_DISALLOW_COPY_AND_ASSIGN(CodecOwner);
-};
-
-} // namespace acm2
-} // namespace webrtc
-#endif // WEBRTC_MODULES_AUDIO_CODING_MAIN_ACM2_CODEC_OWNER_H_
diff --git a/webrtc/modules/audio_coding/main/acm2/codec_owner_unittest.cc b/webrtc/modules/audio_coding/main/acm2/codec_owner_unittest.cc
deleted file mode 100644
index 317c6bd..0000000
--- a/webrtc/modules/audio_coding/main/acm2/codec_owner_unittest.cc
+++ /dev/null
@@ -1,208 +0,0 @@
-/*
- * 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 <cstring>
-
-#include "testing/gtest/include/gtest/gtest.h"
-#include "webrtc/base/arraysize.h"
-#include "webrtc/base/safe_conversions.h"
-#include "webrtc/modules/audio_coding/codecs/mock/mock_audio_encoder.h"
-#include "webrtc/modules/audio_coding/main/acm2/codec_owner.h"
-#include "webrtc/modules/audio_coding/main/acm2/rent_a_codec.h"
-
-namespace webrtc {
-namespace acm2 {
-
-using ::testing::Return;
-using ::testing::InSequence;
-
-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;
-} // namespace
-
-class CodecOwnerTest : public ::testing::Test {
- protected:
- CodecOwnerTest() : timestamp_(0) {}
-
- void CreateCodec() {
- AudioEncoder *enc = rent_a_codec_.RentEncoder(kDefaultCodecInst);
- ASSERT_TRUE(enc);
- codec_owner_.SetEncoders(enc, kCngPt, VADNormal, -1);
- }
-
- void EncodeAndVerify(size_t expected_out_length,
- uint32_t expected_timestamp,
- int expected_payload_type,
- int expected_send_even_if_empty) {
- uint8_t out[kPacketSizeSamples];
- AudioEncoder::EncodedInfo encoded_info;
- encoded_info = codec_owner_.Encoder()->Encode(timestamp_, kZeroData,
- kPacketSizeSamples, 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<bool>(expected_send_even_if_empty),
- encoded_info.send_even_if_empty);
- }
-
- // 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) {
- MockAudioEncoder speech_encoder;
- EXPECT_CALL(speech_encoder, NumChannels())
- .WillRepeatedly(Return(1));
- EXPECT_CALL(speech_encoder, Max10MsFramesInAPacket())
- .WillRepeatedly(Return(2));
- EXPECT_CALL(speech_encoder, SampleRateHz())
- .WillRepeatedly(Return(8000));
- {
- InSequence s;
- EXPECT_CALL(speech_encoder, Mark("start off"));
- EXPECT_CALL(speech_encoder, Mark("switch on"));
- if (use_cng || use_red)
- EXPECT_CALL(speech_encoder, Reset());
- EXPECT_CALL(speech_encoder, Mark("start on"));
- if (use_cng || use_red)
- EXPECT_CALL(speech_encoder, Reset());
- EXPECT_CALL(speech_encoder, Mark("switch off"));
- EXPECT_CALL(speech_encoder, Die());
- }
-
- int cng_pt = use_cng ? 17 : -1;
- int red_pt = use_red ? 19 : -1;
- speech_encoder.Mark("start off");
- codec_owner_.SetEncoders(&speech_encoder, -1, VADNormal, -1);
- speech_encoder.Mark("switch on");
- codec_owner_.ChangeCngAndRed(cng_pt, VADNormal, red_pt);
- speech_encoder.Mark("start on");
- codec_owner_.SetEncoders(&speech_encoder, cng_pt, VADNormal, red_pt);
- speech_encoder.Mark("switch off");
- codec_owner_.ChangeCngAndRed(-1, VADNormal, -1);
- }
-
- CodecOwner codec_owner_;
- RentACodec rent_a_codec_;
- uint32_t timestamp_;
-};
-
-// 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(CodecOwnerTest, 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_F(CodecOwnerTest, ExternalEncoder) {
- MockAudioEncoder external_encoder;
- codec_owner_.SetEncoders(&external_encoder, -1, VADNormal, -1);
- const int kSampleRateHz = 8000;
- const int kPacketSizeSamples = kSampleRateHz / 100;
- int16_t audio[kPacketSizeSamples] = {0};
- uint8_t encoded[kPacketSizeSamples];
- AudioEncoder::EncodedInfo info;
- EXPECT_CALL(external_encoder, SampleRateHz())
- .WillRepeatedly(Return(kSampleRateHz));
- EXPECT_CALL(external_encoder, NumChannels()).WillRepeatedly(Return(1));
-
- {
- InSequence s;
- info.encoded_timestamp = 0;
- EXPECT_CALL(external_encoder,
- EncodeInternal(0, rtc::ArrayView<const int16_t>(audio),
- arraysize(encoded), encoded))
- .WillOnce(Return(info));
- EXPECT_CALL(external_encoder, Mark("A"));
- EXPECT_CALL(external_encoder, Mark("B"));
- info.encoded_timestamp = 2;
- EXPECT_CALL(external_encoder,
- EncodeInternal(2, rtc::ArrayView<const int16_t>(audio),
- arraysize(encoded), encoded))
- .WillOnce(Return(info));
- EXPECT_CALL(external_encoder, Die());
- }
-
- info = codec_owner_.Encoder()->Encode(0, audio, arraysize(encoded), encoded);
- EXPECT_EQ(0u, info.encoded_timestamp);
- external_encoder.Mark("A");
-
- // Change to internal encoder.
- CodecInst codec_inst = kDefaultCodecInst;
- codec_inst.pacsize = kPacketSizeSamples;
- AudioEncoder* enc = rent_a_codec_.RentEncoder(codec_inst);
- ASSERT_TRUE(enc);
- codec_owner_.SetEncoders(enc, -1, VADNormal, -1);
- // Don't expect any more calls to the external encoder.
- info = codec_owner_.Encoder()->Encode(1, audio, arraysize(encoded), encoded);
- external_encoder.Mark("B");
-
- // Change back to external encoder again.
- codec_owner_.SetEncoders(&external_encoder, -1, VADNormal, -1);
- info = codec_owner_.Encoder()->Encode(2, audio, arraysize(encoded), encoded);
- EXPECT_EQ(2u, info.encoded_timestamp);
-}
-
-TEST_F(CodecOwnerTest, CngResetsSpeechEncoder) {
- TestCngAndRedResetSpeechEncoder(true, false);
-}
-
-TEST_F(CodecOwnerTest, RedResetsSpeechEncoder) {
- TestCngAndRedResetSpeechEncoder(false, true);
-}
-
-TEST_F(CodecOwnerTest, CngAndRedResetsSpeechEncoder) {
- TestCngAndRedResetSpeechEncoder(true, true);
-}
-
-TEST_F(CodecOwnerTest, NoCngAndRedNoSpeechEncoderReset) {
- TestCngAndRedResetSpeechEncoder(false, false);
-}
-
-} // namespace acm2
-} // namespace webrtc
diff --git a/webrtc/modules/audio_coding/main/acm2/rent_a_codec.cc b/webrtc/modules/audio_coding/main/acm2/rent_a_codec.cc
index 2ee953c..e3310ad 100644
--- a/webrtc/modules/audio_coding/main/acm2/rent_a_codec.cc
+++ b/webrtc/modules/audio_coding/main/acm2/rent_a_codec.cc
@@ -11,6 +11,7 @@
#include "webrtc/modules/audio_coding/main/acm2/rent_a_codec.h"
#include "webrtc/base/logging.h"
+#include "webrtc/modules/audio_coding/codecs/cng/include/audio_encoder_cng.h"
#include "webrtc/modules/audio_coding/codecs/g711/include/audio_encoder_pcm.h"
#ifdef WEBRTC_CODEC_G722
#include "webrtc/modules/audio_coding/codecs/g722/include/audio_encoder_g722.h"
@@ -30,6 +31,9 @@
#include "webrtc/modules/audio_coding/codecs/opus/include/audio_encoder_opus.h"
#endif
#include "webrtc/modules/audio_coding/codecs/pcm16b/include/audio_encoder_pcm16b.h"
+#ifdef WEBRTC_CODEC_RED
+#include "webrtc/modules/audio_coding/codecs/red/audio_encoder_copy_red.h"
+#endif
#include "webrtc/modules/audio_coding/main/acm2/acm_codec_database.h"
#include "webrtc/modules/audio_coding/main/acm2/acm_common_defs.h"
@@ -140,6 +144,44 @@
return rtc::scoped_ptr<AudioEncoder>();
}
+rtc::scoped_ptr<AudioEncoder> CreateRedEncoder(AudioEncoder* encoder,
+ int red_payload_type) {
+#ifdef WEBRTC_CODEC_RED
+ AudioEncoderCopyRed::Config config;
+ config.payload_type = red_payload_type;
+ config.speech_encoder = encoder;
+ return rtc::scoped_ptr<AudioEncoder>(new AudioEncoderCopyRed(config));
+#else
+ return rtc::scoped_ptr<AudioEncoder>();
+#endif
+}
+
+rtc::scoped_ptr<AudioEncoder> CreateCngEncoder(
+ AudioEncoder* encoder,
+ RentACodec::CngConfig cng_config) {
+ AudioEncoderCng::Config config;
+ config.num_channels = encoder->NumChannels();
+ config.payload_type = cng_config.cng_payload_type;
+ config.speech_encoder = encoder;
+ switch (cng_config.vad_mode) {
+ case VADNormal:
+ config.vad_mode = Vad::kVadNormal;
+ break;
+ case VADLowBitrate:
+ config.vad_mode = Vad::kVadLowBitrate;
+ break;
+ case VADAggr:
+ config.vad_mode = Vad::kVadAggressive;
+ break;
+ case VADVeryAggr:
+ config.vad_mode = Vad::kVadVeryAggressive;
+ break;
+ default:
+ FATAL();
+ }
+ return rtc::scoped_ptr<AudioEncoder>(new AudioEncoderCng(config));
+}
+
rtc::scoped_ptr<AudioDecoder> CreateIsacDecoder(
LockedIsacBandwidthInfo* bwinfo) {
#if defined(WEBRTC_CODEC_ISACFX)
@@ -162,8 +204,35 @@
CreateEncoder(codec_inst, &isac_bandwidth_info_);
if (!enc)
return nullptr;
- encoder_ = enc.Pass();
- return encoder_.get();
+ speech_encoder_ = enc.Pass();
+ return speech_encoder_.get();
+}
+
+AudioEncoder* RentACodec::RentEncoderStack(
+ AudioEncoder* speech_encoder,
+ rtc::Optional<CngConfig> cng_config,
+ rtc::Optional<int> red_payload_type) {
+ RTC_DCHECK(speech_encoder);
+ if (cng_config || red_payload_type) {
+ // The RED and CNG encoders need to be in sync with the speech encoder, so
+ // reset the latter to ensure its buffer is empty.
+ speech_encoder->Reset();
+ }
+ encoder_stack_ = speech_encoder;
+ if (red_payload_type) {
+ red_encoder_ = CreateRedEncoder(encoder_stack_, *red_payload_type);
+ if (red_encoder_)
+ encoder_stack_ = red_encoder_.get();
+ } else {
+ red_encoder_.reset();
+ }
+ if (cng_config) {
+ cng_encoder_ = CreateCngEncoder(encoder_stack_, *cng_config);
+ encoder_stack_ = cng_encoder_.get();
+ } else {
+ cng_encoder_.reset();
+ }
+ return encoder_stack_;
}
AudioDecoder* RentACodec::RentIsacDecoder() {
diff --git a/webrtc/modules/audio_coding/main/acm2/rent_a_codec.h b/webrtc/modules/audio_coding/main/acm2/rent_a_codec.h
index 4daac6a..4571560 100644
--- a/webrtc/modules/audio_coding/main/acm2/rent_a_codec.h
+++ b/webrtc/modules/audio_coding/main/acm2/rent_a_codec.h
@@ -17,9 +17,10 @@
#include "webrtc/base/constructormagic.h"
#include "webrtc/base/optional.h"
#include "webrtc/base/scoped_ptr.h"
-#include "webrtc/typedefs.h"
-#include "webrtc/modules/audio_coding/codecs/audio_encoder.h"
#include "webrtc/modules/audio_coding/codecs/audio_decoder.h"
+#include "webrtc/modules/audio_coding/codecs/audio_encoder.h"
+#include "webrtc/modules/audio_coding/main/include/audio_coding_module_typedefs.h"
+#include "webrtc/typedefs.h"
#if defined(WEBRTC_CODEC_ISAC) || defined(WEBRTC_CODEC_ISACFX)
#include "webrtc/modules/audio_coding/codecs/isac/locked_bandwidth_info.h"
@@ -188,14 +189,35 @@
// successful call to this function, or until the Rent-A-Codec is destroyed.
AudioEncoder* RentEncoder(const CodecInst& codec_inst);
+ // Creates and returns an audio encoder stack where the given speech encoder
+ // is augmented with the specified CNG/VAD and RED encoders. Leave either
+ // optional field blank if you don't want the corresponding gizmo in the
+ // stack. The returned encoder is live until the next successful call to this
+ // function, or until the Rent-A-Codec is destroyed.
+ struct CngConfig {
+ int cng_payload_type;
+ ACMVADMode vad_mode;
+ };
+ AudioEncoder* RentEncoderStack(AudioEncoder* speech_encoder,
+ rtc::Optional<CngConfig> cng_config,
+ rtc::Optional<int> red_payload_type);
+
+ // Get the last return values of RentEncoder and RentEncoderStack, or null if
+ // they haven't been called.
+ AudioEncoder* GetEncoder() const { return speech_encoder_.get(); }
+ AudioEncoder* GetEncoderStack() const { return encoder_stack_; }
+
// Creates and returns an iSAC decoder, which will remain live until the
// Rent-A-Codec is destroyed. Subsequent calls will simply return the same
// object.
AudioDecoder* RentIsacDecoder();
private:
- rtc::scoped_ptr<AudioEncoder> encoder_;
+ rtc::scoped_ptr<AudioEncoder> speech_encoder_;
+ rtc::scoped_ptr<AudioEncoder> cng_encoder_;
+ rtc::scoped_ptr<AudioEncoder> red_encoder_;
rtc::scoped_ptr<AudioDecoder> isac_decoder_;
+ AudioEncoder* encoder_stack_ = nullptr;
LockedIsacBandwidthInfo isac_bandwidth_info_;
RTC_DISALLOW_COPY_AND_ASSIGN(RentACodec);
diff --git a/webrtc/modules/audio_coding/main/acm2/rent_a_codec_unittest.cc b/webrtc/modules/audio_coding/main/acm2/rent_a_codec_unittest.cc
index 01ba024..6938051 100644
--- a/webrtc/modules/audio_coding/main/acm2/rent_a_codec_unittest.cc
+++ b/webrtc/modules/audio_coding/main/acm2/rent_a_codec_unittest.cc
@@ -9,11 +9,202 @@
*/
#include "testing/gtest/include/gtest/gtest.h"
+#include "webrtc/base/arraysize.h"
+#include "webrtc/modules/audio_coding/codecs/mock/mock_audio_encoder.h"
#include "webrtc/modules/audio_coding/main/acm2/rent_a_codec.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;
+} // namespace
+
+class RentACodecTestF : public ::testing::Test {
+ protected:
+ void CreateCodec() {
+ speech_encoder_ = rent_a_codec_.RentEncoder(kDefaultCodecInst);
+ ASSERT_TRUE(speech_encoder_);
+ encoder_ = rent_a_codec_.RentEncoderStack(
+ speech_encoder_, rtc::Optional<RentACodec::CngConfig>(
+ RentACodec::CngConfig{kCngPt, VADNormal}),
+ rtc::Optional<int>());
+ }
+
+ void EncodeAndVerify(size_t expected_out_length,
+ uint32_t expected_timestamp,
+ int expected_payload_type,
+ int expected_send_even_if_empty) {
+ uint8_t out[kPacketSizeSamples];
+ AudioEncoder::EncodedInfo encoded_info;
+ encoded_info =
+ encoder_->Encode(timestamp_, kZeroData, kPacketSizeSamples, 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<bool>(expected_send_even_if_empty),
+ encoded_info.send_even_if_empty);
+ }
+
+ RentACodec rent_a_codec_;
+ AudioEncoder* speech_encoder_ = nullptr;
+ AudioEncoder* encoder_ = nullptr;
+ 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) {
+ MockAudioEncoder external_encoder;
+ RentACodec rac;
+ EXPECT_EQ(&external_encoder,
+ rac.RentEncoderStack(&external_encoder,
+ rtc::Optional<RentACodec::CngConfig>(),
+ rtc::Optional<int>()));
+ const int kSampleRateHz = 8000;
+ const int kPacketSizeSamples = kSampleRateHz / 100;
+ int16_t audio[kPacketSizeSamples] = {0};
+ uint8_t encoded[kPacketSizeSamples];
+ AudioEncoder::EncodedInfo info;
+ EXPECT_CALL(external_encoder, SampleRateHz())
+ .WillRepeatedly(Return(kSampleRateHz));
+ EXPECT_CALL(external_encoder, NumChannels()).WillRepeatedly(Return(1));
+
+ {
+ ::testing::InSequence s;
+ info.encoded_timestamp = 0;
+ EXPECT_CALL(external_encoder,
+ EncodeInternal(0, rtc::ArrayView<const int16_t>(audio),
+ arraysize(encoded), encoded))
+ .WillOnce(Return(info));
+ EXPECT_CALL(external_encoder, Mark("A"));
+ EXPECT_CALL(external_encoder, Mark("B"));
+ info.encoded_timestamp = 2;
+ EXPECT_CALL(external_encoder,
+ EncodeInternal(2, rtc::ArrayView<const int16_t>(audio),
+ arraysize(encoded), encoded))
+ .WillOnce(Return(info));
+ EXPECT_CALL(external_encoder, Die());
+ }
+
+ info = rac.GetEncoderStack()->Encode(0, audio, arraysize(encoded), encoded);
+ EXPECT_EQ(0u, info.encoded_timestamp);
+ external_encoder.Mark("A");
+
+ // Change to internal encoder.
+ CodecInst codec_inst = kDefaultCodecInst;
+ codec_inst.pacsize = kPacketSizeSamples;
+ AudioEncoder* enc = rac.RentEncoder(codec_inst);
+ ASSERT_TRUE(enc);
+ EXPECT_EQ(enc,
+ rac.RentEncoderStack(enc, rtc::Optional<RentACodec::CngConfig>(),
+ rtc::Optional<int>()));
+
+ // Don't expect any more calls to the external encoder.
+ info = rac.GetEncoderStack()->Encode(1, audio, arraysize(encoded), encoded);
+ external_encoder.Mark("B");
+
+ // Change back to external encoder again.
+ EXPECT_EQ(&external_encoder,
+ rac.RentEncoderStack(&external_encoder,
+ rtc::Optional<RentACodec::CngConfig>(),
+ rtc::Optional<int>()));
+ info = rac.GetEncoderStack()->Encode(2, audio, arraysize(encoded), encoded);
+ EXPECT_EQ(2u, info.encoded_timestamp);
+}
+
+// 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) {
+ MockAudioEncoder speech_encoder;
+ EXPECT_CALL(speech_encoder, NumChannels()).WillRepeatedly(Return(1));
+ EXPECT_CALL(speech_encoder, Max10MsFramesInAPacket())
+ .WillRepeatedly(Return(2));
+ EXPECT_CALL(speech_encoder, SampleRateHz()).WillRepeatedly(Return(8000));
+ {
+ ::testing::InSequence s;
+ EXPECT_CALL(speech_encoder, Mark("disabled"));
+ EXPECT_CALL(speech_encoder, Mark("enabled"));
+ if (use_cng || use_red)
+ EXPECT_CALL(speech_encoder, Reset());
+ EXPECT_CALL(speech_encoder, Die());
+ }
+
+ auto cng_cfg = use_cng ? rtc::Optional<RentACodec::CngConfig>(
+ RentACodec::CngConfig{17, VADNormal})
+ : rtc::Optional<RentACodec::CngConfig>();
+ auto red_pt = use_red ? rtc::Optional<int>(19) : rtc::Optional<int>();
+ speech_encoder.Mark("disabled");
+ RentACodec rac;
+ rac.RentEncoderStack(&speech_encoder, rtc::Optional<RentACodec::CngConfig>(),
+ rtc::Optional<int>());
+ speech_encoder.Mark("enabled");
+ rac.RentEncoderStack(&speech_encoder, cng_cfg, red_pt);
+}
+
+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};
diff --git a/webrtc/modules/audio_coding/main/audio_coding_module.gypi b/webrtc/modules/audio_coding/main/audio_coding_module.gypi
index 09ac0a8..061ffaa 100644
--- a/webrtc/modules/audio_coding/main/audio_coding_module.gypi
+++ b/webrtc/modules/audio_coding/main/audio_coding_module.gypi
@@ -109,8 +109,6 @@
'acm2/call_statistics.h',
'acm2/codec_manager.cc',
'acm2/codec_manager.h',
- 'acm2/codec_owner.cc',
- 'acm2/codec_owner.h',
'acm2/initial_delay_manager.cc',
'acm2/initial_delay_manager.h',
'include/audio_coding_module.h',
diff --git a/webrtc/modules/modules.gyp b/webrtc/modules/modules.gyp
index 22dbff2..7cac619 100644
--- a/webrtc/modules/modules.gyp
+++ b/webrtc/modules/modules.gyp
@@ -102,7 +102,6 @@
'audio_coding/main/acm2/audio_coding_module_unittest_oldapi.cc',
'audio_coding/main/acm2/call_statistics_unittest.cc',
'audio_coding/main/acm2/codec_manager_unittest.cc',
- 'audio_coding/main/acm2/codec_owner_unittest.cc',
'audio_coding/main/acm2/initial_delay_manager_unittest.cc',
'audio_coding/main/acm2/rent_a_codec_unittest.cc',
'audio_coding/codecs/cng/cng_unittest.cc',