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}
This commit is contained in:
@ -63,8 +63,6 @@ source_set("audio_coding") {
|
||||
"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",
|
||||
|
||||
@ -240,7 +240,7 @@ int CodecManager::RegisterEncoder(const CodecInst& send_codec) {
|
||||
|
||||
// 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 @@ int CodecManager::RegisterEncoder(const CodecInst& send_codec) {
|
||||
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 @@ int CodecManager::RegisterEncoder(const CodecInst& send_codec) {
|
||||
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 @@ int CodecManager::RegisterEncoder(const CodecInst& send_codec) {
|
||||
|
||||
// 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 @@ void CodecManager::RegisterEncoder(AudioEncoder* external_speech_encoder) {
|
||||
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 @@ rtc::Optional<CodecInst> CodecManager::GetCodecInst() const {
|
||||
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 @@ bool CodecManager::SetCopyRed(bool enable) {
|
||||
}
|
||||
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 @@ int CodecManager::SetVAD(bool enable, ACMVADMode mode) {
|
||||
|
||||
// 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 @@ int CodecManager::SetVAD(bool enable, ACMVADMode mode) {
|
||||
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 @@ int CodecManager::SetCodecFEC(bool enable_codec_fec) {
|
||||
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 @@ int CodecManager::RedPayloadType(int sample_rate_hz) const {
|
||||
}
|
||||
}
|
||||
|
||||
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
|
||||
|
||||
@ -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 @@ class CodecManager final {
|
||||
|
||||
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 @@ class CodecManager final {
|
||||
CodecInst send_codec_inst_;
|
||||
bool red_enabled_;
|
||||
bool codec_fec_enabled_;
|
||||
CodecOwner codec_owner_;
|
||||
RentACodec rent_a_codec_;
|
||||
bool encoder_is_opus_;
|
||||
|
||||
|
||||
@ -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
|
||||
@ -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_
|
||||
@ -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
|
||||
@ -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 @@ rtc::scoped_ptr<AudioEncoder> CreateEncoder(
|
||||
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 @@ AudioEncoder* RentACodec::RentEncoder(const CodecInst& codec_inst) {
|
||||
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() {
|
||||
|
||||
@ -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 @@ class RentACodec {
|
||||
// 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);
|
||||
|
||||
@ -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};
|
||||
|
||||
@ -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',
|
||||
|
||||
@ -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',
|
||||
|
||||
Reference in New Issue
Block a user