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/call_statistics.h",
|
||||||
"main/acm2/codec_manager.cc",
|
"main/acm2/codec_manager.cc",
|
||||||
"main/acm2/codec_manager.h",
|
"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.cc",
|
||||||
"main/acm2/initial_delay_manager.h",
|
"main/acm2/initial_delay_manager.h",
|
||||||
"main/include/audio_coding_module.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.
|
// Check if the codec is already registered as send codec.
|
||||||
bool new_codec = true;
|
bool new_codec = true;
|
||||||
if (codec_owner_.Encoder()) {
|
if (CurrentEncoder()) {
|
||||||
auto new_codec_id = RentACodec::CodecIdByInst(send_codec_inst_);
|
auto new_codec_id = RentACodec::CodecIdByInst(send_codec_inst_);
|
||||||
RTC_DCHECK(new_codec_id);
|
RTC_DCHECK(new_codec_id);
|
||||||
auto old_codec_id = RentACodec::CodecIdFromIndex(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);
|
AudioEncoder* enc = rent_a_codec_.RentEncoder(send_codec);
|
||||||
if (!enc)
|
if (!enc)
|
||||||
return -1;
|
return -1;
|
||||||
codec_owner_.SetEncoders(
|
RentEncoderStack(enc, send_codec.plfreq);
|
||||||
enc, dtx_enabled_ ? CngPayloadType(send_codec.plfreq) : -1,
|
RTC_DCHECK(CurrentEncoder());
|
||||||
vad_mode_, red_enabled_ ? RedPayloadType(send_codec.plfreq) : -1);
|
|
||||||
RTC_DCHECK(codec_owner_.Encoder());
|
|
||||||
|
|
||||||
codec_fec_enabled_ = codec_fec_enabled_ &&
|
codec_fec_enabled_ = codec_fec_enabled_ &&
|
||||||
enc->SetFec(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);
|
AudioEncoder* enc = rent_a_codec_.RentEncoder(send_codec);
|
||||||
if (!enc)
|
if (!enc)
|
||||||
return -1;
|
return -1;
|
||||||
codec_owner_.SetEncoders(
|
RentEncoderStack(enc, send_codec.plfreq);
|
||||||
enc, dtx_enabled_ ? CngPayloadType(send_codec.plfreq) : -1,
|
RTC_DCHECK(CurrentEncoder());
|
||||||
vad_mode_, red_enabled_ ? RedPayloadType(send_codec.plfreq) : -1);
|
|
||||||
RTC_DCHECK(codec_owner_.Encoder());
|
|
||||||
}
|
}
|
||||||
send_codec_inst_.plfreq = send_codec.plfreq;
|
send_codec_inst_.plfreq = send_codec.plfreq;
|
||||||
send_codec_inst_.pacsize = send_codec.pacsize;
|
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.
|
// Check if a change in Rate is required.
|
||||||
if (send_codec.rate != send_codec_inst_.rate) {
|
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;
|
send_codec_inst_.rate = send_codec.rate;
|
||||||
}
|
}
|
||||||
|
|
||||||
codec_fec_enabled_ =
|
codec_fec_enabled_ =
|
||||||
codec_fec_enabled_ && codec_owner_.Encoder()->SetFec(codec_fec_enabled_);
|
codec_fec_enabled_ && CurrentEncoder()->SetFec(codec_fec_enabled_);
|
||||||
|
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
@ -328,11 +324,9 @@ void CodecManager::RegisterEncoder(AudioEncoder* external_speech_encoder) {
|
|||||||
const bool success = external_speech_encoder->SetFec(false);
|
const bool success = external_speech_encoder->SetFec(false);
|
||||||
RTC_DCHECK(success);
|
RTC_DCHECK(success);
|
||||||
}
|
}
|
||||||
int cng_pt = dtx_enabled_
|
|
||||||
? CngPayloadType(external_speech_encoder->SampleRateHz())
|
RentEncoderStack(external_speech_encoder,
|
||||||
: -1;
|
external_speech_encoder->SampleRateHz());
|
||||||
int red_pt = red_enabled_ ? RedPayloadType(send_codec_inst_.plfreq) : -1;
|
|
||||||
codec_owner_.SetEncoders(external_speech_encoder, cng_pt, vad_mode_, red_pt);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
rtc::Optional<CodecInst> CodecManager::GetCodecInst() const {
|
rtc::Optional<CodecInst> CodecManager::GetCodecInst() const {
|
||||||
@ -340,7 +334,7 @@ rtc::Optional<CodecInst> CodecManager::GetCodecInst() const {
|
|||||||
WEBRTC_TRACE(webrtc::kTraceStream, webrtc::kTraceAudioCoding, dummy_id,
|
WEBRTC_TRACE(webrtc::kTraceStream, webrtc::kTraceAudioCoding, dummy_id,
|
||||||
"SendCodec()");
|
"SendCodec()");
|
||||||
|
|
||||||
if (!codec_owner_.Encoder()) {
|
if (!CurrentEncoder()) {
|
||||||
WEBRTC_TRACE(webrtc::kTraceStream, webrtc::kTraceAudioCoding, dummy_id,
|
WEBRTC_TRACE(webrtc::kTraceStream, webrtc::kTraceAudioCoding, dummy_id,
|
||||||
"SendCodec Failed, no codec is registered");
|
"SendCodec Failed, no codec is registered");
|
||||||
return rtc::Optional<CodecInst>();
|
return rtc::Optional<CodecInst>();
|
||||||
@ -361,11 +355,8 @@ bool CodecManager::SetCopyRed(bool enable) {
|
|||||||
}
|
}
|
||||||
if (red_enabled_ != enable) {
|
if (red_enabled_ != enable) {
|
||||||
red_enabled_ = enable;
|
red_enabled_ = enable;
|
||||||
if (codec_owner_.Encoder()) {
|
if (CurrentEncoder())
|
||||||
int cng_pt = dtx_enabled_ ? CngPayloadType(send_codec_inst_.plfreq) : -1;
|
RentEncoderStack(rent_a_codec_.GetEncoder(), send_codec_inst_.plfreq);
|
||||||
int red_pt = red_enabled_ ? RedPayloadType(send_codec_inst_.plfreq) : -1;
|
|
||||||
codec_owner_.ChangeCngAndRed(cng_pt, vad_mode_, red_pt);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
return true;
|
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
|
// Check that the send codec is mono. We don't support VAD/DTX for stereo
|
||||||
// sending.
|
// sending.
|
||||||
const auto* enc = codec_owner_.Encoder();
|
auto* enc = rent_a_codec_.GetEncoder();
|
||||||
const bool stereo_send = enc ? (enc->NumChannels() != 1) : false;
|
const bool stereo_send = enc ? (enc->NumChannels() != 1) : false;
|
||||||
if (enable && stereo_send) {
|
if (enable && stereo_send) {
|
||||||
WEBRTC_TRACE(webrtc::kTraceError, webrtc::kTraceAudioCoding, 0,
|
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) {
|
if (dtx_enabled_ != enable || vad_mode_ != mode) {
|
||||||
dtx_enabled_ = enable;
|
dtx_enabled_ = enable;
|
||||||
vad_mode_ = mode;
|
vad_mode_ = mode;
|
||||||
if (enc) {
|
if (enc)
|
||||||
int cng_pt = dtx_enabled_ ? CngPayloadType(send_codec_inst_.plfreq) : -1;
|
RentEncoderStack(enc, send_codec_inst_.plfreq);
|
||||||
int red_pt = red_enabled_ ? RedPayloadType(send_codec_inst_.plfreq) : -1;
|
|
||||||
codec_owner_.ChangeCngAndRed(cng_pt, vad_mode_, red_pt);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
@ -420,9 +408,9 @@ int CodecManager::SetCodecFEC(bool enable_codec_fec) {
|
|||||||
return -1;
|
return -1;
|
||||||
}
|
}
|
||||||
|
|
||||||
RTC_CHECK(codec_owner_.Encoder());
|
RTC_CHECK(CurrentEncoder());
|
||||||
codec_fec_enabled_ =
|
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;
|
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 acm2
|
||||||
} // namespace webrtc
|
} // namespace webrtc
|
||||||
|
|||||||
@ -15,7 +15,6 @@
|
|||||||
#include "webrtc/base/optional.h"
|
#include "webrtc/base/optional.h"
|
||||||
#include "webrtc/base/scoped_ptr.h"
|
#include "webrtc/base/scoped_ptr.h"
|
||||||
#include "webrtc/base/thread_checker.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/acm2/rent_a_codec.h"
|
||||||
#include "webrtc/modules/audio_coding/main/include/audio_coding_module_typedefs.h"
|
#include "webrtc/modules/audio_coding/main/include/audio_coding_module_typedefs.h"
|
||||||
#include "webrtc/common_types.h"
|
#include "webrtc/common_types.h"
|
||||||
@ -57,15 +56,17 @@ class CodecManager final {
|
|||||||
|
|
||||||
bool codec_fec_enabled() const { return codec_fec_enabled_; }
|
bool codec_fec_enabled() const { return codec_fec_enabled_; }
|
||||||
|
|
||||||
AudioEncoder* CurrentEncoder() { return codec_owner_.Encoder(); }
|
AudioEncoder* CurrentEncoder() { return rent_a_codec_.GetEncoderStack(); }
|
||||||
const AudioEncoder* CurrentEncoder() const { return codec_owner_.Encoder(); }
|
const AudioEncoder* CurrentEncoder() const {
|
||||||
|
return rent_a_codec_.GetEncoderStack();
|
||||||
|
}
|
||||||
|
|
||||||
bool CurrentEncoderIsOpus() const { return encoder_is_opus_; }
|
bool CurrentEncoderIsOpus() const { return encoder_is_opus_; }
|
||||||
|
|
||||||
private:
|
private:
|
||||||
int CngPayloadType(int sample_rate_hz) const;
|
int CngPayloadType(int sample_rate_hz) const;
|
||||||
|
|
||||||
int RedPayloadType(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_;
|
rtc::ThreadChecker thread_checker_;
|
||||||
uint8_t cng_nb_pltype_;
|
uint8_t cng_nb_pltype_;
|
||||||
@ -78,7 +79,6 @@ class CodecManager final {
|
|||||||
CodecInst send_codec_inst_;
|
CodecInst send_codec_inst_;
|
||||||
bool red_enabled_;
|
bool red_enabled_;
|
||||||
bool codec_fec_enabled_;
|
bool codec_fec_enabled_;
|
||||||
CodecOwner codec_owner_;
|
|
||||||
RentACodec rent_a_codec_;
|
RentACodec rent_a_codec_;
|
||||||
bool encoder_is_opus_;
|
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/modules/audio_coding/main/acm2/rent_a_codec.h"
|
||||||
|
|
||||||
#include "webrtc/base/logging.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"
|
#include "webrtc/modules/audio_coding/codecs/g711/include/audio_encoder_pcm.h"
|
||||||
#ifdef WEBRTC_CODEC_G722
|
#ifdef WEBRTC_CODEC_G722
|
||||||
#include "webrtc/modules/audio_coding/codecs/g722/include/audio_encoder_g722.h"
|
#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"
|
#include "webrtc/modules/audio_coding/codecs/opus/include/audio_encoder_opus.h"
|
||||||
#endif
|
#endif
|
||||||
#include "webrtc/modules/audio_coding/codecs/pcm16b/include/audio_encoder_pcm16b.h"
|
#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_codec_database.h"
|
||||||
#include "webrtc/modules/audio_coding/main/acm2/acm_common_defs.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>();
|
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(
|
rtc::scoped_ptr<AudioDecoder> CreateIsacDecoder(
|
||||||
LockedIsacBandwidthInfo* bwinfo) {
|
LockedIsacBandwidthInfo* bwinfo) {
|
||||||
#if defined(WEBRTC_CODEC_ISACFX)
|
#if defined(WEBRTC_CODEC_ISACFX)
|
||||||
@ -162,8 +204,35 @@ AudioEncoder* RentACodec::RentEncoder(const CodecInst& codec_inst) {
|
|||||||
CreateEncoder(codec_inst, &isac_bandwidth_info_);
|
CreateEncoder(codec_inst, &isac_bandwidth_info_);
|
||||||
if (!enc)
|
if (!enc)
|
||||||
return nullptr;
|
return nullptr;
|
||||||
encoder_ = enc.Pass();
|
speech_encoder_ = enc.Pass();
|
||||||
return encoder_.get();
|
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() {
|
AudioDecoder* RentACodec::RentIsacDecoder() {
|
||||||
|
|||||||
@ -17,9 +17,10 @@
|
|||||||
#include "webrtc/base/constructormagic.h"
|
#include "webrtc/base/constructormagic.h"
|
||||||
#include "webrtc/base/optional.h"
|
#include "webrtc/base/optional.h"
|
||||||
#include "webrtc/base/scoped_ptr.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_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)
|
#if defined(WEBRTC_CODEC_ISAC) || defined(WEBRTC_CODEC_ISACFX)
|
||||||
#include "webrtc/modules/audio_coding/codecs/isac/locked_bandwidth_info.h"
|
#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.
|
// successful call to this function, or until the Rent-A-Codec is destroyed.
|
||||||
AudioEncoder* RentEncoder(const CodecInst& codec_inst);
|
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
|
// Creates and returns an iSAC decoder, which will remain live until the
|
||||||
// Rent-A-Codec is destroyed. Subsequent calls will simply return the same
|
// Rent-A-Codec is destroyed. Subsequent calls will simply return the same
|
||||||
// object.
|
// object.
|
||||||
AudioDecoder* RentIsacDecoder();
|
AudioDecoder* RentIsacDecoder();
|
||||||
|
|
||||||
private:
|
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_;
|
rtc::scoped_ptr<AudioDecoder> isac_decoder_;
|
||||||
|
AudioEncoder* encoder_stack_ = nullptr;
|
||||||
LockedIsacBandwidthInfo isac_bandwidth_info_;
|
LockedIsacBandwidthInfo isac_bandwidth_info_;
|
||||||
|
|
||||||
RTC_DISALLOW_COPY_AND_ASSIGN(RentACodec);
|
RTC_DISALLOW_COPY_AND_ASSIGN(RentACodec);
|
||||||
|
|||||||
@ -9,11 +9,202 @@
|
|||||||
*/
|
*/
|
||||||
|
|
||||||
#include "testing/gtest/include/gtest/gtest.h"
|
#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"
|
#include "webrtc/modules/audio_coding/main/acm2/rent_a_codec.h"
|
||||||
|
|
||||||
namespace webrtc {
|
namespace webrtc {
|
||||||
namespace acm2 {
|
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) {
|
TEST(RentACodecTest, RentEncoderError) {
|
||||||
const CodecInst codec_inst = {
|
const CodecInst codec_inst = {
|
||||||
0, "Robert'); DROP TABLE Students;", 8000, 160, 1, 64000};
|
0, "Robert'); DROP TABLE Students;", 8000, 160, 1, 64000};
|
||||||
|
|||||||
@ -109,8 +109,6 @@
|
|||||||
'acm2/call_statistics.h',
|
'acm2/call_statistics.h',
|
||||||
'acm2/codec_manager.cc',
|
'acm2/codec_manager.cc',
|
||||||
'acm2/codec_manager.h',
|
'acm2/codec_manager.h',
|
||||||
'acm2/codec_owner.cc',
|
|
||||||
'acm2/codec_owner.h',
|
|
||||||
'acm2/initial_delay_manager.cc',
|
'acm2/initial_delay_manager.cc',
|
||||||
'acm2/initial_delay_manager.h',
|
'acm2/initial_delay_manager.h',
|
||||||
'include/audio_coding_module.h',
|
'include/audio_coding_module.h',
|
||||||
|
|||||||
@ -102,7 +102,6 @@
|
|||||||
'audio_coding/main/acm2/audio_coding_module_unittest_oldapi.cc',
|
'audio_coding/main/acm2/audio_coding_module_unittest_oldapi.cc',
|
||||||
'audio_coding/main/acm2/call_statistics_unittest.cc',
|
'audio_coding/main/acm2/call_statistics_unittest.cc',
|
||||||
'audio_coding/main/acm2/codec_manager_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/initial_delay_manager_unittest.cc',
|
||||||
'audio_coding/main/acm2/rent_a_codec_unittest.cc',
|
'audio_coding/main/acm2/rent_a_codec_unittest.cc',
|
||||||
'audio_coding/codecs/cng/cng_unittest.cc',
|
'audio_coding/codecs/cng/cng_unittest.cc',
|
||||||
|
|||||||
Reference in New Issue
Block a user