Introduce CodecManager and move code from AudioCodingModuleImpl

This change essentially divides AudioCodingModuleImpl into two parts:
one is the code related to managing codecs, now moved into CodecManager,
and the other is what remains in AudioCodingModuleImpl.

This change also removes AudioCodingModuleImpl::InitializeSender. The
function was essentially no-op, since it was always called immediately
after construction.

COAUTHOR=kwiberg@webrtc.org
BUG=4228
R=minyue@webrtc.org, tina.legrand@webrtc.org

Review URL: https://webrtc-codereview.appspot.com/51469004

Cr-Commit-Position: refs/heads/master@{#8893}
This commit is contained in:
Henrik Lundin
2015-03-30 19:00:44 +02:00
parent f7b9cf54a6
commit 45c6449114
15 changed files with 800 additions and 722 deletions

View File

@ -209,7 +209,7 @@ size_t AudioDecoderProxy::Channels() const {
return decoder_->Channels();
}
int16_t ACMGenericCodec::EncoderParams(WebRtcACMCodecParams* enc_params) {
int16_t ACMGenericCodec::EncoderParams(WebRtcACMCodecParams* enc_params) const {
*enc_params = acm_codec_params_;
return 0;
}
@ -540,6 +540,10 @@ AudioEncoder* ACMGenericCodec::GetAudioEncoder() {
return encoder_;
}
const AudioEncoder* ACMGenericCodec::GetAudioEncoder() const {
return encoder_;
}
} // namespace acm2
} // namespace webrtc

View File

@ -120,7 +120,7 @@ class ACMGenericCodec {
// -1 if the encoder is not initialized,
// 0 otherwise.
//
int16_t EncoderParams(WebRtcACMCodecParams* enc_params);
int16_t EncoderParams(WebRtcACMCodecParams* enc_params) const;
///////////////////////////////////////////////////////////////////////////
// int16_t InitEncoder(...)
@ -410,6 +410,8 @@ class ACMGenericCodec {
AudioEncoder* GetAudioEncoder();
const AudioEncoder* GetAudioEncoder() const;
private:
bool has_internal_fec_;

View File

@ -62,7 +62,6 @@ class AcmReceiverTestOldApi : public AudioPacketizationCallback,
}
acm_->InitializeReceiver();
acm_->InitializeSender();
acm_->RegisterTransportCallback(this);
rtp_header_.header.sequenceNumber = 0;

View File

@ -70,6 +70,8 @@
'audio_coding_module_impl.h',
'call_statistics.cc',
'call_statistics.h',
'codec_manager.cc',
'codec_manager.h',
'initial_delay_manager.cc',
'initial_delay_manager.h',
'nack.cc',

View File

@ -18,7 +18,6 @@
#include "webrtc/base/safe_conversions.h"
#include "webrtc/engine_configurations.h"
#include "webrtc/modules/audio_coding/main/interface/audio_coding_module_typedefs.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_generic_codec.h"
#include "webrtc/modules/audio_coding/main/acm2/acm_resampler.h"
@ -126,20 +125,8 @@ AudioCodingModuleImpl::AudioCodingModuleImpl(
id_(config.id),
expected_codec_ts_(0xD87F3F9F),
expected_in_ts_(0xD87F3F9F),
send_codec_inst_(),
cng_nb_pltype_(255),
cng_wb_pltype_(255),
cng_swb_pltype_(255),
cng_fb_pltype_(255),
red_nb_pltype_(255),
vad_enabled_(false),
dtx_enabled_(false),
vad_mode_(VADNormal),
current_encoder_(nullptr),
stereo_send_(false),
receiver_(config),
red_enabled_(false),
codec_fec_enabled_(false),
codec_manager_(this),
previous_pltype_(255),
aux_rtp_header_(NULL),
receiver_initialized_(false),
@ -148,35 +135,6 @@ AudioCodingModuleImpl::AudioCodingModuleImpl(
callback_crit_sect_(CriticalSectionWrapper::CreateCriticalSection()),
packetization_callback_(NULL),
vad_callback_(NULL) {
// Nullify send codec memory, set payload type and set codec name to
// invalid values.
const char no_name[] = "noCodecRegistered";
strncpy(send_codec_inst_.plname, no_name, RTP_PAYLOAD_NAME_SIZE - 1);
send_codec_inst_.pltype = -1;
for (int i = 0; i < ACMCodecDB::kMaxNumCodecs; i++) {
codecs_[i] = NULL;
mirror_codec_idx_[i] = -1;
}
// Register the default payload type for RED and for CNG at sampling rates of
// 8, 16, 32 and 48 kHz.
for (int i = (ACMCodecDB::kNumCodecs - 1); i >= 0; i--) {
if (IsCodecRED(i) && ACMCodecDB::database_[i].plfreq == 8000) {
red_nb_pltype_ = static_cast<uint8_t>(ACMCodecDB::database_[i].pltype);
} else if (IsCodecCN(i)) {
if (ACMCodecDB::database_[i].plfreq == 8000) {
cng_nb_pltype_ = static_cast<uint8_t>(ACMCodecDB::database_[i].pltype);
} else if (ACMCodecDB::database_[i].plfreq == 16000) {
cng_wb_pltype_ = static_cast<uint8_t>(ACMCodecDB::database_[i].pltype);
} else if (ACMCodecDB::database_[i].plfreq == 32000) {
cng_swb_pltype_ = static_cast<uint8_t>(ACMCodecDB::database_[i].pltype);
} else if (ACMCodecDB::database_[i].plfreq == 48000) {
cng_fb_pltype_ = static_cast<uint8_t>(ACMCodecDB::database_[i].pltype);
}
}
}
if (InitializeReceiverSafe() < 0) {
WEBRTC_TRACE(webrtc::kTraceError, webrtc::kTraceAudioCoding, id_,
"Cannot initialize receiver");
@ -185,23 +143,6 @@ AudioCodingModuleImpl::AudioCodingModuleImpl(
}
AudioCodingModuleImpl::~AudioCodingModuleImpl() {
{
CriticalSectionScoped lock(acm_crit_sect_);
for (int i = 0; i < ACMCodecDB::kMaxNumCodecs; i++) {
if (codecs_[i] != NULL) {
// Mirror index holds the address of the codec memory.
assert(mirror_codec_idx_[i] > -1);
if (codecs_[mirror_codec_idx_[i]] != NULL) {
delete codecs_[mirror_codec_idx_[i]];
codecs_[mirror_codec_idx_[i]] = NULL;
}
codecs_[i] = NULL;
}
}
}
if (aux_rtp_header_ != NULL) {
delete aux_rtp_header_;
aux_rtp_header_ = NULL;
@ -229,7 +170,8 @@ int32_t AudioCodingModuleImpl::Encode(const InputData& input_data) {
return -1;
}
AudioEncoder* audio_encoder = current_encoder_->GetAudioEncoder();
AudioEncoder* audio_encoder =
codec_manager_.current_encoder()->GetAudioEncoder();
// Scale the timestamp to the codec's RTP timestamp rate.
uint32_t rtp_timestamp =
first_frame_ ? input_data.input_timestamp
@ -271,7 +213,7 @@ int32_t AudioCodingModuleImpl::Encode(const InputData& input_data) {
frame_type, encoded_info.payload_type, encoded_info.encoded_timestamp,
stream, encoded_info.encoded_bytes,
my_fragmentation.fragmentationVectorSize > 0 ? &my_fragmentation
: nullptr);
: nullptr);
}
if (vad_callback_) {
@ -290,17 +232,6 @@ int32_t AudioCodingModuleImpl::Encode(const InputData& input_data) {
// Sender
//
// Initialize send codec.
int AudioCodingModuleImpl::InitializeSender() {
CriticalSectionScoped lock(acm_crit_sect_);
// Start with invalid values.
current_encoder_ = nullptr;
send_codec_inst_.plname[0] = '\0';
return 0;
}
// TODO(henrik.lundin): Remove this method; only used in tests.
int AudioCodingModuleImpl::ResetEncoder() {
CriticalSectionScoped lock(acm_crit_sect_);
@ -310,365 +241,16 @@ int AudioCodingModuleImpl::ResetEncoder() {
return 0;
}
ACMGenericCodec* AudioCodingModuleImpl::CreateCodec(const CodecInst& codec) {
ACMGenericCodec* my_codec = NULL;
CriticalSectionScoped lock(acm_crit_sect_);
my_codec = ACMCodecDB::CreateCodecInstance(
codec, cng_nb_pltype_, cng_wb_pltype_, cng_swb_pltype_, cng_fb_pltype_,
red_enabled_, red_nb_pltype_);
if (my_codec == NULL) {
// Error, could not create the codec.
WEBRTC_TRACE(webrtc::kTraceError, webrtc::kTraceAudioCoding, id_,
"ACMCodecDB::CreateCodecInstance() failed in CreateCodec()");
return my_codec;
}
return my_codec;
}
// Check if the given codec is a valid to be registered as send codec.
static int IsValidSendCodec(const CodecInst& send_codec,
bool is_primary_encoder,
int acm_id,
int* mirror_id) {
if ((send_codec.channels != 1) && (send_codec.channels != 2)) {
WEBRTC_TRACE(webrtc::kTraceError, webrtc::kTraceAudioCoding, acm_id,
"Wrong number of channels (%d, only mono and stereo are "
"supported) for %s encoder", send_codec.channels,
is_primary_encoder ? "primary" : "secondary");
return -1;
}
int codec_id = ACMCodecDB::CodecNumber(send_codec, mirror_id);
if (codec_id < 0) {
WEBRTC_TRACE(webrtc::kTraceError, webrtc::kTraceAudioCoding, acm_id,
"Invalid codec setting for the send codec.");
return -1;
}
// TODO(tlegrand): Remove this check. Already taken care of in
// ACMCodecDB::CodecNumber().
// Check if the payload-type is valid
if (!ACMCodecDB::ValidPayloadType(send_codec.pltype)) {
WEBRTC_TRACE(webrtc::kTraceError, webrtc::kTraceAudioCoding, acm_id,
"Invalid payload-type %d for %s.", send_codec.pltype,
send_codec.plname);
return -1;
}
// Telephone-event cannot be a send codec.
if (!STR_CASE_CMP(send_codec.plname, "telephone-event")) {
WEBRTC_TRACE(webrtc::kTraceError, webrtc::kTraceAudioCoding, acm_id,
"telephone-event cannot be a send codec");
*mirror_id = -1;
return -1;
}
if (ACMCodecDB::codec_settings_[codec_id].channel_support
< send_codec.channels) {
WEBRTC_TRACE(webrtc::kTraceError, webrtc::kTraceAudioCoding, acm_id,
"%d number of channels not supportedn for %s.",
send_codec.channels, send_codec.plname);
*mirror_id = -1;
return -1;
}
if (!is_primary_encoder) {
// If registering the secondary encoder, then RED and CN are not valid
// choices as encoder.
if (IsCodecRED(&send_codec)) {
WEBRTC_TRACE(webrtc::kTraceError, webrtc::kTraceAudioCoding, acm_id,
"RED cannot be secondary codec");
*mirror_id = -1;
return -1;
}
if (IsCodecCN(&send_codec)) {
WEBRTC_TRACE(webrtc::kTraceError, webrtc::kTraceAudioCoding, acm_id,
"DTX cannot be secondary codec");
*mirror_id = -1;
return -1;
}
}
return codec_id;
}
// Can be called multiple times for Codec, CNG, RED.
int AudioCodingModuleImpl::RegisterSendCodec(const CodecInst& send_codec) {
int mirror_id;
int codec_id = IsValidSendCodec(send_codec, true, id_, &mirror_id);
CriticalSectionScoped lock(acm_crit_sect_);
// Check for reported errors from function IsValidSendCodec().
if (codec_id < 0) {
return -1;
}
// RED can be registered with other payload type. If not registered a default
// payload type is used.
if (IsCodecRED(&send_codec)) {
// TODO(tlegrand): Remove this check. Already taken care of in
// ACMCodecDB::CodecNumber().
// Check if the payload-type is valid
if (!ACMCodecDB::ValidPayloadType(send_codec.pltype)) {
WEBRTC_TRACE(webrtc::kTraceError, webrtc::kTraceAudioCoding, id_,
"Invalid payload-type %d for %s.", send_codec.pltype,
send_codec.plname);
return -1;
}
// Set RED payload type.
if (send_codec.plfreq == 8000) {
red_nb_pltype_ = static_cast<uint8_t>(send_codec.pltype);
} else {
WEBRTC_TRACE(webrtc::kTraceError, webrtc::kTraceAudioCoding, id_,
"RegisterSendCodec() failed, invalid frequency for RED "
"registration");
return -1;
}
SetRedPayloadType(send_codec.plfreq, send_codec.pltype);
return 0;
}
// CNG can be registered with other payload type. If not registered the
// default payload types from codec database will be used.
if (IsCodecCN(&send_codec)) {
// CNG is registered.
switch (send_codec.plfreq) {
case 8000: {
cng_nb_pltype_ = static_cast<uint8_t>(send_codec.pltype);
break;
}
case 16000: {
cng_wb_pltype_ = static_cast<uint8_t>(send_codec.pltype);
break;
}
case 32000: {
cng_swb_pltype_ = static_cast<uint8_t>(send_codec.pltype);
break;
}
case 48000: {
cng_fb_pltype_ = static_cast<uint8_t>(send_codec.pltype);
break;
}
default: {
WEBRTC_TRACE(webrtc::kTraceError, webrtc::kTraceAudioCoding, id_,
"RegisterSendCodec() failed, invalid frequency for CNG "
"registration");
return -1;
}
}
SetCngPayloadType(send_codec.plfreq, send_codec.pltype);
return 0;
}
// Set Stereo, and make sure VAD and DTX is turned off.
if (send_codec.channels == 2) {
stereo_send_ = true;
if (vad_enabled_ || dtx_enabled_) {
WEBRTC_TRACE(webrtc::kTraceWarning, webrtc::kTraceAudioCoding, id_,
"VAD/DTX is turned off, not supported when sending stereo.");
}
vad_enabled_ = false;
dtx_enabled_ = false;
} else {
stereo_send_ = false;
}
// Check if the codec is already registered as send codec.
bool is_send_codec;
if (current_encoder_) {
int send_codec_mirror_id;
int send_codec_id = ACMCodecDB::CodecNumber(send_codec_inst_,
&send_codec_mirror_id);
assert(send_codec_id >= 0);
is_send_codec = (send_codec_id == codec_id) ||
(mirror_id == send_codec_mirror_id);
} else {
is_send_codec = false;
}
// If new codec, or new settings, register.
if (!is_send_codec) {
if (codecs_[mirror_id] == NULL) {
codecs_[mirror_id] = CreateCodec(send_codec);
if (codecs_[mirror_id] == NULL) {
WEBRTC_TRACE(webrtc::kTraceError, webrtc::kTraceAudioCoding, id_,
"Cannot Create the codec");
return -1;
}
mirror_codec_idx_[mirror_id] = mirror_id;
}
if (mirror_id != codec_id) {
codecs_[codec_id] = codecs_[mirror_id];
mirror_codec_idx_[codec_id] = mirror_id;
}
ACMGenericCodec* codec_ptr = codecs_[codec_id];
WebRtcACMCodecParams codec_params;
memcpy(&(codec_params.codec_inst), &send_codec, sizeof(CodecInst));
codec_params.enable_vad = vad_enabled_;
codec_params.enable_dtx = dtx_enabled_;
codec_params.vad_mode = vad_mode_;
// Force initialization.
if (codec_ptr->InitEncoder(&codec_params, true) < 0) {
// Could not initialize the encoder.
// Check if already have a registered codec.
// Depending on that different messages are logged.
if (!current_encoder_) {
WEBRTC_TRACE(webrtc::kTraceError, webrtc::kTraceAudioCoding, id_,
"Cannot Initialize the encoder No Encoder is registered");
} else {
WEBRTC_TRACE(webrtc::kTraceError, webrtc::kTraceAudioCoding, id_,
"Cannot Initialize the encoder, continue encoding with "
"the previously registered codec");
}
return -1;
}
// Update states.
dtx_enabled_ = codec_params.enable_dtx;
vad_enabled_ = codec_params.enable_vad;
vad_mode_ = codec_params.vad_mode;
// Everything is fine so we can replace the previous codec with this one.
if (current_encoder_) {
// If we change codec we start fresh with RED.
// This is not strictly required by the standard.
if(codec_ptr->SetCopyRed(red_enabled_) < 0) {
// We tried to preserve the old red status, if failed, it means the
// red status has to be flipped.
red_enabled_ = !red_enabled_;
}
codec_ptr->SetVAD(&dtx_enabled_, &vad_enabled_, &vad_mode_);
if (!codec_ptr->HasInternalFEC()) {
codec_fec_enabled_ = false;
} else {
if (codec_ptr->SetFEC(codec_fec_enabled_) < 0) {
WEBRTC_TRACE(webrtc::kTraceError, webrtc::kTraceAudioCoding, id_,
"Cannot set codec FEC");
return -1;
}
}
}
current_encoder_ = codecs_[codec_id];
DCHECK(current_encoder_);
memcpy(&send_codec_inst_, &send_codec, sizeof(CodecInst));
return 0;
} else {
// If codec is the same as already registered check if any parameters
// has changed compared to the current values.
// If any parameter is valid then apply it and record.
bool force_init = false;
if (mirror_id != codec_id) {
codecs_[codec_id] = codecs_[mirror_id];
mirror_codec_idx_[codec_id] = mirror_id;
}
// Check the payload type.
if (send_codec.pltype != send_codec_inst_.pltype) {
// At this point check if the given payload type is valid.
// Record it later when the sampling frequency is changed
// successfully.
if (!ACMCodecDB::ValidPayloadType(send_codec.pltype)) {
WEBRTC_TRACE(webrtc::kTraceError, webrtc::kTraceAudioCoding, id_,
"Out of range payload type");
return -1;
}
}
// If there is a codec that ONE instance of codec supports multiple
// sampling frequencies, then we need to take care of it here.
// one such a codec is iSAC. Both WB and SWB are encoded and decoded
// with one iSAC instance. Therefore, we need to update the encoder
// frequency if required.
if (send_codec_inst_.plfreq != send_codec.plfreq) {
force_init = true;
}
// If packet size or number of channels has changed, we need to
// re-initialize the encoder.
if (send_codec_inst_.pacsize != send_codec.pacsize) {
force_init = true;
}
if (send_codec_inst_.channels != send_codec.channels) {
force_init = true;
}
if (force_init) {
WebRtcACMCodecParams codec_params;
memcpy(&(codec_params.codec_inst), &send_codec, sizeof(CodecInst));
codec_params.enable_vad = vad_enabled_;
codec_params.enable_dtx = dtx_enabled_;
codec_params.vad_mode = vad_mode_;
// Force initialization.
if (current_encoder_->InitEncoder(&codec_params, true) < 0) {
WEBRTC_TRACE(webrtc::kTraceError, webrtc::kTraceAudioCoding, id_,
"Could not change the codec packet-size.");
return -1;
}
send_codec_inst_.plfreq = send_codec.plfreq;
send_codec_inst_.pacsize = send_codec.pacsize;
send_codec_inst_.channels = send_codec.channels;
}
// If the change of sampling frequency has been successful then
// we store the payload-type.
send_codec_inst_.pltype = send_codec.pltype;
// Check if a change in Rate is required.
if (send_codec.rate != send_codec_inst_.rate) {
if (codecs_[codec_id]->SetBitRate(send_codec.rate) < 0) {
WEBRTC_TRACE(webrtc::kTraceError, webrtc::kTraceAudioCoding, id_,
"Could not change the codec rate.");
return -1;
}
send_codec_inst_.rate = send_codec.rate;
}
if (!codecs_[codec_id]->HasInternalFEC()) {
codec_fec_enabled_ = false;
} else {
if (codecs_[codec_id]->SetFEC(codec_fec_enabled_) < 0) {
WEBRTC_TRACE(webrtc::kTraceError, webrtc::kTraceAudioCoding, id_,
"Cannot set codec FEC");
return -1;
}
}
return 0;
}
return codec_manager_.RegisterSendCodec(send_codec);
}
// Get current send codec.
int AudioCodingModuleImpl::SendCodec(
CodecInst* current_codec) const {
WEBRTC_TRACE(webrtc::kTraceStream, webrtc::kTraceAudioCoding, id_,
"SendCodec()");
int AudioCodingModuleImpl::SendCodec(CodecInst* current_codec) const {
CriticalSectionScoped lock(acm_crit_sect_);
if (!current_encoder_) {
WEBRTC_TRACE(webrtc::kTraceStream, webrtc::kTraceAudioCoding, id_,
"SendCodec Failed, no codec is registered");
return -1;
}
WebRtcACMCodecParams encoder_param;
current_encoder_->EncoderParams(&encoder_param);
encoder_param.codec_inst.pltype = send_codec_inst_.pltype;
memcpy(current_codec, &(encoder_param.codec_inst), sizeof(CodecInst));
return 0;
return codec_manager_.SendCodec(current_codec);
}
// Get current send frequency.
@ -677,13 +259,13 @@ int AudioCodingModuleImpl::SendFrequency() const {
"SendFrequency()");
CriticalSectionScoped lock(acm_crit_sect_);
if (!current_encoder_) {
if (!codec_manager_.current_encoder()) {
WEBRTC_TRACE(webrtc::kTraceStream, webrtc::kTraceAudioCoding, id_,
"SendFrequency Failed, no codec is registered");
return -1;
}
return send_codec_inst_.plfreq;
return codec_manager_.current_encoder()->GetAudioEncoder()->SampleRateHz();
}
// Get encode bitrate.
@ -693,14 +275,14 @@ int AudioCodingModuleImpl::SendFrequency() const {
int AudioCodingModuleImpl::SendBitrate() const {
CriticalSectionScoped lock(acm_crit_sect_);
if (!current_encoder_) {
if (!codec_manager_.current_encoder()) {
WEBRTC_TRACE(webrtc::kTraceStream, webrtc::kTraceAudioCoding, id_,
"SendBitrate Failed, no codec is registered");
return -1;
}
WebRtcACMCodecParams encoder_param;
current_encoder_->EncoderParams(&encoder_param);
codec_manager_.current_encoder()->EncoderParams(&encoder_param);
return encoder_param.codec_inst.rate;
}
@ -779,7 +361,9 @@ int AudioCodingModuleImpl::Add10MsDataInternal(const AudioFrame& audio_frame,
}
// Check whether we need an up-mix or down-mix?
bool remix = ptr_frame->num_channels_ != send_codec_inst_.channels;
bool remix =
ptr_frame->num_channels_ !=
codec_manager_.current_encoder()->GetAudioEncoder()->NumChannels();
if (remix) {
if (ptr_frame->num_channels_ == 1) {
@ -796,13 +380,15 @@ int AudioCodingModuleImpl::Add10MsDataInternal(const AudioFrame& audio_frame,
const int16_t* ptr_audio = ptr_frame->data_;
// For pushing data to primary, point the |ptr_audio| to correct buffer.
if (send_codec_inst_.channels != ptr_frame->num_channels_)
if (codec_manager_.current_encoder()->GetAudioEncoder()->NumChannels() !=
ptr_frame->num_channels_)
ptr_audio = input_data->buffer;
input_data->input_timestamp = ptr_frame->timestamp_;
input_data->audio = ptr_audio;
input_data->length_per_channel = ptr_frame->samples_per_channel_;
input_data->audio_channel = send_codec_inst_.channels;
input_data->audio_channel =
codec_manager_.current_encoder()->GetAudioEncoder()->NumChannels();
return 0;
}
@ -814,12 +400,15 @@ int AudioCodingModuleImpl::Add10MsDataInternal(const AudioFrame& audio_frame,
// is required, |*ptr_out| points to |in_frame|.
int AudioCodingModuleImpl::PreprocessToAddData(const AudioFrame& in_frame,
const AudioFrame** ptr_out) {
bool resample = (in_frame.sample_rate_hz_ != send_codec_inst_.plfreq);
bool resample =
(in_frame.sample_rate_hz_ !=
codec_manager_.current_encoder()->GetAudioEncoder()->SampleRateHz());
// This variable is true if primary codec and secondary codec (if exists)
// are both mono and input is stereo.
bool down_mix =
(in_frame.num_channels_ == 2) && (send_codec_inst_.channels == 1);
(in_frame.num_channels_ == 2) &&
(codec_manager_.current_encoder()->GetAudioEncoder()->NumChannels() == 1);
if (!first_10ms_data_) {
expected_in_ts_ = in_frame.timestamp_;
@ -827,9 +416,13 @@ int AudioCodingModuleImpl::PreprocessToAddData(const AudioFrame& in_frame,
first_10ms_data_ = true;
} else if (in_frame.timestamp_ != expected_in_ts_) {
// TODO(turajs): Do we need a warning here.
expected_codec_ts_ += (in_frame.timestamp_ - expected_in_ts_) *
static_cast<uint32_t>((static_cast<double>(send_codec_inst_.plfreq) /
static_cast<double>(in_frame.sample_rate_hz_)));
expected_codec_ts_ +=
(in_frame.timestamp_ - expected_in_ts_) *
static_cast<uint32_t>(
(static_cast<double>(codec_manager_.current_encoder()
->GetAudioEncoder()
->SampleRateHz()) /
static_cast<double>(in_frame.sample_rate_hz_)));
expected_in_ts_ = in_frame.timestamp_;
}
@ -867,20 +460,19 @@ int AudioCodingModuleImpl::PreprocessToAddData(const AudioFrame& in_frame,
// The result of the resampler is written to output frame.
dest_ptr_audio = preprocess_frame_.data_;
preprocess_frame_.samples_per_channel_ =
resampler_.Resample10Msec(src_ptr_audio,
in_frame.sample_rate_hz_,
send_codec_inst_.plfreq,
preprocess_frame_.num_channels_,
AudioFrame::kMaxDataSizeSamples,
dest_ptr_audio);
preprocess_frame_.samples_per_channel_ = resampler_.Resample10Msec(
src_ptr_audio, in_frame.sample_rate_hz_,
codec_manager_.current_encoder()->GetAudioEncoder()->SampleRateHz(),
preprocess_frame_.num_channels_, AudioFrame::kMaxDataSizeSamples,
dest_ptr_audio);
if (preprocess_frame_.samples_per_channel_ < 0) {
WEBRTC_TRACE(webrtc::kTraceError, webrtc::kTraceAudioCoding, id_,
"Cannot add 10 ms audio, resampling failed");
return -1;
}
preprocess_frame_.sample_rate_hz_ = send_codec_inst_.plfreq;
preprocess_frame_.sample_rate_hz_ =
codec_manager_.current_encoder()->GetAudioEncoder()->SampleRateHz();
}
expected_codec_ts_ += preprocess_frame_.samples_per_channel_;
@ -895,8 +487,7 @@ int AudioCodingModuleImpl::PreprocessToAddData(const AudioFrame& in_frame,
bool AudioCodingModuleImpl::REDStatus() const {
CriticalSectionScoped lock(acm_crit_sect_);
return red_enabled_;
return codec_manager_.red_enabled();
}
// Configure RED status i.e on/off.
@ -904,23 +495,7 @@ int AudioCodingModuleImpl::SetREDStatus(
#ifdef WEBRTC_CODEC_RED
bool enable_red) {
CriticalSectionScoped lock(acm_crit_sect_);
if (enable_red == true && codec_fec_enabled_ == true) {
WEBRTC_TRACE(webrtc::kTraceWarning, webrtc::kTraceAudioCoding, id_,
"Codec internal FEC and RED cannot be co-enabled.");
return -1;
}
// If a send codec is registered, set RED for the codec. We now only support
// copy red.
if (HaveValidEncoder("SetCopyRed") &&
current_encoder_->SetCopyRed(enable_red) < 0) {
WEBRTC_TRACE(webrtc::kTraceError, webrtc::kTraceAudioCoding, id_,
"SetREDStatus failed");
return -1;
}
red_enabled_ = enable_red;
return 0;
return codec_manager_.SetCopyRed(enable_red) ? 0 : -1;
#else
bool /* enable_red */) {
WEBRTC_TRACE(webrtc::kTraceWarning, webrtc::kTraceAudioCoding, id_,
@ -935,33 +510,18 @@ int AudioCodingModuleImpl::SetREDStatus(
bool AudioCodingModuleImpl::CodecFEC() const {
CriticalSectionScoped lock(acm_crit_sect_);
return codec_fec_enabled_;
return codec_manager_.codec_fec_enabled();
}
int AudioCodingModuleImpl::SetCodecFEC(bool enable_codec_fec) {
CriticalSectionScoped lock(acm_crit_sect_);
if (enable_codec_fec == true && red_enabled_ == true) {
WEBRTC_TRACE(webrtc::kTraceWarning, webrtc::kTraceAudioCoding, id_,
"Codec internal FEC and RED cannot be co-enabled.");
return -1;
}
// Set codec FEC.
if (HaveValidEncoder("SetCodecFEC") &&
current_encoder_->SetFEC(enable_codec_fec) < 0) {
WEBRTC_TRACE(webrtc::kTraceError, webrtc::kTraceAudioCoding, id_,
"Set codec internal FEC failed.");
return -1;
}
codec_fec_enabled_ = enable_codec_fec;
return 0;
return codec_manager_.SetCodecFEC(enable_codec_fec);
}
int AudioCodingModuleImpl::SetPacketLossRate(int loss_rate) {
CriticalSectionScoped lock(acm_crit_sect_);
if (HaveValidEncoder("SetPacketLossRate") &&
current_encoder_->SetPacketLossRate(loss_rate) < 0) {
codec_manager_.current_encoder()->SetPacketLossRate(loss_rate) < 0) {
WEBRTC_TRACE(webrtc::kTraceError, webrtc::kTraceAudioCoding, id_,
"Set packet loss rate failed.");
return -1;
@ -976,60 +536,14 @@ int AudioCodingModuleImpl::SetVAD(bool enable_dtx,
bool enable_vad,
ACMVADMode mode) {
CriticalSectionScoped lock(acm_crit_sect_);
return SetVADSafe(enable_dtx, enable_vad, mode);
}
int AudioCodingModuleImpl::SetVADSafe(bool enable_dtx,
bool enable_vad,
ACMVADMode mode) {
// Sanity check of the mode.
if ((mode != VADNormal) && (mode != VADLowBitrate)
&& (mode != VADAggr) && (mode != VADVeryAggr)) {
WEBRTC_TRACE(webrtc::kTraceError, webrtc::kTraceAudioCoding, id_,
"Invalid VAD Mode %d, no change is made to VAD/DTX status",
mode);
return -1;
}
// Check that the send codec is mono. We don't support VAD/DTX for stereo
// sending.
if ((enable_dtx || enable_vad) && stereo_send_) {
WEBRTC_TRACE(webrtc::kTraceError, webrtc::kTraceAudioCoding, id_,
"VAD/DTX not supported for stereo sending");
dtx_enabled_ = false;
vad_enabled_ = false;
vad_mode_ = mode;
return -1;
}
// Store VAD/DTX settings. Values can be changed in the call to "SetVAD"
// below.
dtx_enabled_ = enable_dtx;
vad_enabled_ = enable_vad;
vad_mode_ = mode;
// If a send codec is registered, set VAD/DTX for the codec.
if (HaveValidEncoder("SetVAD") &&
current_encoder_->SetVAD(&dtx_enabled_, &vad_enabled_, &vad_mode_) < 0) {
// SetVAD failed.
WEBRTC_TRACE(webrtc::kTraceError, webrtc::kTraceAudioCoding, id_,
"SetVAD failed");
vad_enabled_ = false;
dtx_enabled_ = false;
return -1;
}
return 0;
return codec_manager_.SetVAD(enable_dtx, enable_vad, mode);
}
// Get VAD/DTX settings.
int AudioCodingModuleImpl::VAD(bool* dtx_enabled, bool* vad_enabled,
ACMVADMode* mode) const {
CriticalSectionScoped lock(acm_crit_sect_);
*dtx_enabled = dtx_enabled_;
*vad_enabled = vad_enabled_;
*mode = vad_mode_;
codec_manager_.VAD(dtx_enabled, vad_enabled, mode);
return 0;
}
@ -1108,56 +622,24 @@ int AudioCodingModuleImpl::PlayoutFrequency() const {
// for codecs, CNG (NB, WB and SWB), DTMF, RED.
int AudioCodingModuleImpl::RegisterReceiveCodec(const CodecInst& codec) {
CriticalSectionScoped lock(acm_crit_sect_);
if (codec.channels > 2 || codec.channels < 0) {
WEBRTC_TRACE(webrtc::kTraceError, webrtc::kTraceAudioCoding, id_,
"Unsupported number of channels, %d.", codec.channels);
return -1;
}
// TODO(turajs) do we need this for NetEq 4?
if (!receiver_initialized_) {
if (InitializeReceiverSafe() < 0) {
WEBRTC_TRACE(webrtc::kTraceError, webrtc::kTraceAudioCoding, id_,
"Cannot initialize receiver, failed registering codec.");
return -1;
}
}
int mirror_id;
int codec_id = ACMCodecDB::ReceiverCodecNumber(codec, &mirror_id);
if (codec_id < 0 || codec_id >= ACMCodecDB::kNumCodecs) {
WEBRTC_TRACE(webrtc::kTraceError, webrtc::kTraceAudioCoding, id_,
"Wrong codec params to be registered as receive codec");
return -1;
}
// Check if the payload-type is valid.
if (!ACMCodecDB::ValidPayloadType(codec.pltype)) {
WEBRTC_TRACE(webrtc::kTraceError, webrtc::kTraceAudioCoding, id_,
"Invalid payload-type %d for %s.", codec.pltype,
codec.plname);
return -1;
}
AudioDecoder* decoder = NULL;
// Get |decoder| associated with |codec|. |decoder| can be NULL if |codec|
// does not own its decoder.
if (GetAudioDecoder(codec, codec_id, mirror_id, &decoder) < 0) {
WEBRTC_TRACE(webrtc::kTraceError, webrtc::kTraceAudioCoding, id_,
"Wrong codec params to be registered as receive codec");
return -1;
}
uint8_t payload_type = static_cast<uint8_t>(codec.pltype);
return receiver_.AddCodec(codec_id, payload_type, codec.channels, decoder);
DCHECK(receiver_initialized_);
return codec_manager_.RegisterReceiveCodec(codec);
}
// Get current received codec.
int AudioCodingModuleImpl::ReceiveCodec(CodecInst* current_codec) const {
CriticalSectionScoped lock(acm_crit_sect_);
return receiver_.LastAudioCodec(current_codec);
}
int AudioCodingModuleImpl::RegisterDecoder(int acm_codec_id,
uint8_t payload_type,
int channels,
AudioDecoder* audio_decoder) {
return receiver_.AddCodec(acm_codec_id, payload_type, channels,
audio_decoder);
}
// Incoming packet from network parsed and ready for decode.
int AudioCodingModuleImpl::IncomingPacket(const uint8_t* incoming_payload,
const size_t payload_length,
@ -1312,7 +794,7 @@ int AudioCodingModuleImpl::SetISACMaxRate(int max_bit_per_sec) {
return -1;
}
return current_encoder_->SetISACMaxRate(max_bit_per_sec);
return codec_manager_.current_encoder()->SetISACMaxRate(max_bit_per_sec);
}
// TODO(henrik.lundin): Remove? Only used in tests. Deprecated in VoiceEngine.
@ -1323,7 +805,8 @@ int AudioCodingModuleImpl::SetISACMaxPayloadSize(int max_size_bytes) {
return -1;
}
return current_encoder_->SetISACMaxPayloadSize(max_size_bytes);
return codec_manager_.current_encoder()->SetISACMaxPayloadSize(
max_size_bytes);
}
// TODO(henrik.lundin): Remove? Only used in tests.
@ -1349,8 +832,8 @@ int AudioCodingModuleImpl::SetOpusApplication(OpusApplicationMode application,
if (!HaveValidEncoder("SetOpusApplication")) {
return -1;
}
return current_encoder_->SetOpusApplication(application,
disable_dtx_if_needed);
return codec_manager_.current_encoder()->SetOpusApplication(
application, disable_dtx_if_needed);
}
// Informs Opus encoder of the maximum playback rate the receiver will render.
@ -1359,7 +842,7 @@ int AudioCodingModuleImpl::SetOpusMaxPlaybackRate(int frequency_hz) {
if (!HaveValidEncoder("SetOpusMaxPlaybackRate")) {
return -1;
}
return current_encoder_->SetOpusMaxPlaybackRate(frequency_hz);
return codec_manager_.current_encoder()->SetOpusMaxPlaybackRate(frequency_hz);
}
int AudioCodingModuleImpl::EnableOpusDtx(bool force_voip) {
@ -1367,7 +850,7 @@ int AudioCodingModuleImpl::EnableOpusDtx(bool force_voip) {
if (!HaveValidEncoder("EnableOpusDtx")) {
return -1;
}
return current_encoder_->EnableOpusDtx(force_voip);
return codec_manager_.current_encoder()->EnableOpusDtx(force_voip);
}
int AudioCodingModuleImpl::DisableOpusDtx() {
@ -1375,7 +858,7 @@ int AudioCodingModuleImpl::DisableOpusDtx() {
if (!HaveValidEncoder("DisableOpusDtx")) {
return -1;
}
return current_encoder_->DisableOpusDtx();
return codec_manager_.current_encoder()->DisableOpusDtx();
}
int AudioCodingModuleImpl::PlayoutTimestamp(uint32_t* timestamp) {
@ -1383,7 +866,7 @@ int AudioCodingModuleImpl::PlayoutTimestamp(uint32_t* timestamp) {
}
bool AudioCodingModuleImpl::HaveValidEncoder(const char* caller_name) const {
if (!current_encoder_) {
if (!codec_manager_.current_encoder()) {
WEBRTC_TRACE(webrtc::kTraceError, webrtc::kTraceAudioCoding, id_,
"%s failed: No send codec is registered.", caller_name);
return false;
@ -1415,60 +898,6 @@ int AudioCodingModuleImpl::REDPayloadISAC(int isac_rate,
// return status;
}
int AudioCodingModuleImpl::GetAudioDecoder(const CodecInst& codec, int codec_id,
int mirror_id,
AudioDecoder** decoder) {
if (ACMCodecDB::OwnsDecoder(codec_id)) {
// This codec has to own its own decoder. Therefore, it should create the
// corresponding AudioDecoder class and insert it into NetEq. If the codec
// does not exist create it.
//
// TODO(turajs): this part of the code is common with RegisterSendCodec(),
// make a method for it.
if (codecs_[mirror_id] == NULL) {
codecs_[mirror_id] = CreateCodec(codec);
if (codecs_[mirror_id] == NULL) {
WEBRTC_TRACE(webrtc::kTraceError, webrtc::kTraceAudioCoding, id_,
"Cannot Create the codec");
return -1;
}
mirror_codec_idx_[mirror_id] = mirror_id;
}
if (mirror_id != codec_id) {
codecs_[codec_id] = codecs_[mirror_id];
mirror_codec_idx_[codec_id] = mirror_id;
}
*decoder = codecs_[codec_id]->Decoder();
if (!*decoder) {
assert(false);
return -1;
}
} else {
*decoder = NULL;
}
return 0;
}
void AudioCodingModuleImpl::SetCngPayloadType(int sample_rate_hz,
int payload_type) {
for (auto* codec : codecs_) {
if (codec) {
codec->SetCngPt(sample_rate_hz, payload_type);
}
}
}
void AudioCodingModuleImpl::SetRedPayloadType(int sample_rate_hz,
int payload_type) {
for (auto* codec : codecs_) {
if (codec) {
codec->SetRedPt(sample_rate_hz, payload_type);
}
}
}
int AudioCodingModuleImpl::SetInitialPlayoutDelay(int delay_ms) {
{
CriticalSectionScoped lock(acm_crit_sect_);

View File

@ -20,6 +20,7 @@
#include "webrtc/modules/audio_coding/main/acm2/acm_codec_database.h"
#include "webrtc/modules/audio_coding/main/acm2/acm_receiver.h"
#include "webrtc/modules/audio_coding/main/acm2/acm_resampler.h"
#include "webrtc/modules/audio_coding/main/acm2/codec_manager.h"
namespace webrtc {
@ -42,9 +43,6 @@ class AudioCodingModuleImpl : public AudioCodingModule {
// Sender
//
// Initialize send codec.
int InitializeSender() override;
// Reset send codec.
int ResetEncoder() override;
@ -135,6 +133,11 @@ class AudioCodingModuleImpl : public AudioCodingModule {
// Get current received codec.
int ReceiveCodec(CodecInst* current_codec) const override;
int RegisterDecoder(int acm_codec_id,
uint8_t payload_type,
int channels,
AudioDecoder* audio_decoder);
// Incoming packet from network parsed and ready for decode.
int IncomingPacket(const uint8_t* incoming_payload,
const size_t payload_length,
@ -249,18 +252,11 @@ class AudioCodingModuleImpl : public AudioCodingModule {
int Add10MsDataInternal(const AudioFrame& audio_frame, InputData* input_data);
int Encode(const InputData& input_data);
ACMGenericCodec* CreateCodec(const CodecInst& codec);
int InitializeReceiverSafe() EXCLUSIVE_LOCKS_REQUIRED(acm_crit_sect_);
bool HaveValidEncoder(const char* caller_name) const
EXCLUSIVE_LOCKS_REQUIRED(acm_crit_sect_);
// Set VAD/DTX status. This function does not acquire a lock, and it is
// created to be called only from inside a critical section.
int SetVADSafe(bool enable_dtx, bool enable_vad, ACMVADMode mode)
EXCLUSIVE_LOCKS_REQUIRED(acm_crit_sect_);
// Preprocessing of input audio, including resampling and down-mixing if
// required, before pushing audio into encoder's buffer.
//
@ -280,54 +276,13 @@ class AudioCodingModuleImpl : public AudioCodingModule {
// to |index|.
int UpdateUponReceivingCodec(int index);
// Get a pointer to AudioDecoder of the given codec. For some codecs, e.g.
// iSAC, encoding and decoding have to be performed on a shared
// codec-instance. By calling this method, we get the codec-instance that ACM
// owns, then pass that to NetEq. This way, we perform both encoding and
// decoding on the same codec-instance. Furthermore, ACM would have control
// over decoder functionality if required. If |codec| does not share an
// instance between encoder and decoder, the |*decoder| is set NULL.
// The field ACMCodecDB::CodecSettings.owns_decoder indicates that if a
// codec owns the decoder-instance. For such codecs |*decoder| should be a
// valid pointer, otherwise it will be NULL.
int GetAudioDecoder(const CodecInst& codec, int codec_id,
int mirror_id, AudioDecoder** decoder)
EXCLUSIVE_LOCKS_REQUIRED(acm_crit_sect_);
void SetCngPayloadType(int sample_rate_hz, int payload_type)
EXCLUSIVE_LOCKS_REQUIRED(acm_crit_sect_);
void SetRedPayloadType(int sample_rate_hz, int payload_type)
EXCLUSIVE_LOCKS_REQUIRED(acm_crit_sect_);
CriticalSectionWrapper* acm_crit_sect_;
int id_; // TODO(henrik.lundin) Make const.
uint32_t expected_codec_ts_ GUARDED_BY(acm_crit_sect_);
uint32_t expected_in_ts_ GUARDED_BY(acm_crit_sect_);
CodecInst send_codec_inst_ GUARDED_BY(acm_crit_sect_);
uint8_t cng_nb_pltype_ GUARDED_BY(acm_crit_sect_);
uint8_t cng_wb_pltype_ GUARDED_BY(acm_crit_sect_);
uint8_t cng_swb_pltype_ GUARDED_BY(acm_crit_sect_);
uint8_t cng_fb_pltype_ GUARDED_BY(acm_crit_sect_);
uint8_t red_nb_pltype_ GUARDED_BY(acm_crit_sect_);
bool vad_enabled_ GUARDED_BY(acm_crit_sect_);
bool dtx_enabled_ GUARDED_BY(acm_crit_sect_);
ACMVADMode vad_mode_ GUARDED_BY(acm_crit_sect_);
ACMGenericCodec* codecs_[ACMCodecDB::kMaxNumCodecs]
GUARDED_BY(acm_crit_sect_);
ACMGenericCodec* current_encoder_ GUARDED_BY(acm_crit_sect_);
int mirror_codec_idx_[ACMCodecDB::kMaxNumCodecs] GUARDED_BY(acm_crit_sect_);
bool stereo_send_ GUARDED_BY(acm_crit_sect_);
ACMResampler resampler_ GUARDED_BY(acm_crit_sect_);
AcmReceiver receiver_; // AcmReceiver has it's own internal lock.
// RED.
bool red_enabled_ GUARDED_BY(acm_crit_sect_);
// Codec internal FEC
bool codec_fec_enabled_ GUARDED_BY(acm_crit_sect_);
CodecManager codec_manager_ GUARDED_BY(acm_crit_sect_);
// This is to keep track of CN instances where we can send DTMFs.
uint8_t previous_pltype_ GUARDED_BY(acm_crit_sect_);

View File

@ -0,0 +1,612 @@
/*
* 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_manager.h"
#include "webrtc/base/checks.h"
#include "webrtc/modules/audio_coding/main/acm2/audio_coding_module_impl.h"
namespace webrtc {
namespace acm2 {
namespace {
bool IsCodecRED(const CodecInst* codec) {
return (STR_CASE_CMP(codec->plname, "RED") == 0);
}
bool IsCodecRED(int index) {
return (IsCodecRED(&ACMCodecDB::database_[index]));
}
bool IsCodecCN(const CodecInst* codec) {
return (STR_CASE_CMP(codec->plname, "CN") == 0);
}
bool IsCodecCN(int index) {
return (IsCodecCN(&ACMCodecDB::database_[index]));
}
// Check if the given codec is a valid to be registered as send codec.
int IsValidSendCodec(const CodecInst& send_codec,
bool is_primary_encoder,
int* mirror_id) {
int dummy_id = 0;
if ((send_codec.channels != 1) && (send_codec.channels != 2)) {
WEBRTC_TRACE(webrtc::kTraceError, webrtc::kTraceAudioCoding, dummy_id,
"Wrong number of channels (%d, only mono and stereo are "
"supported) for %s encoder",
send_codec.channels,
is_primary_encoder ? "primary" : "secondary");
return -1;
}
int codec_id = ACMCodecDB::CodecNumber(send_codec, mirror_id);
if (codec_id < 0) {
WEBRTC_TRACE(webrtc::kTraceError, webrtc::kTraceAudioCoding, dummy_id,
"Invalid codec setting for the send codec.");
return -1;
}
// TODO(tlegrand): Remove this check. Already taken care of in
// ACMCodecDB::CodecNumber().
// Check if the payload-type is valid
if (!ACMCodecDB::ValidPayloadType(send_codec.pltype)) {
WEBRTC_TRACE(webrtc::kTraceError, webrtc::kTraceAudioCoding, dummy_id,
"Invalid payload-type %d for %s.", send_codec.pltype,
send_codec.plname);
return -1;
}
// Telephone-event cannot be a send codec.
if (!STR_CASE_CMP(send_codec.plname, "telephone-event")) {
WEBRTC_TRACE(webrtc::kTraceError, webrtc::kTraceAudioCoding, dummy_id,
"telephone-event cannot be a send codec");
*mirror_id = -1;
return -1;
}
if (ACMCodecDB::codec_settings_[codec_id].channel_support <
send_codec.channels) {
WEBRTC_TRACE(webrtc::kTraceError, webrtc::kTraceAudioCoding, dummy_id,
"%d number of channels not supportedn for %s.",
send_codec.channels, send_codec.plname);
*mirror_id = -1;
return -1;
}
if (!is_primary_encoder) {
// If registering the secondary encoder, then RED and CN are not valid
// choices as encoder.
if (IsCodecRED(&send_codec)) {
WEBRTC_TRACE(webrtc::kTraceError, webrtc::kTraceAudioCoding, dummy_id,
"RED cannot be secondary codec");
*mirror_id = -1;
return -1;
}
if (IsCodecCN(&send_codec)) {
WEBRTC_TRACE(webrtc::kTraceError, webrtc::kTraceAudioCoding, dummy_id,
"DTX cannot be secondary codec");
*mirror_id = -1;
return -1;
}
}
return codec_id;
}
const CodecInst kEmptyCodecInst = {-1, "noCodecRegistered", 0, 0, 0, 0};
} // namespace
CodecManager::CodecManager(AudioCodingModuleImpl* acm)
: acm_(acm),
cng_nb_pltype_(255),
cng_wb_pltype_(255),
cng_swb_pltype_(255),
cng_fb_pltype_(255),
red_nb_pltype_(255),
stereo_send_(false),
vad_enabled_(false),
dtx_enabled_(false),
vad_mode_(VADNormal),
current_encoder_(nullptr),
send_codec_inst_(kEmptyCodecInst),
red_enabled_(false),
codec_fec_enabled_(false) {
for (int i = 0; i < ACMCodecDB::kMaxNumCodecs; i++) {
codecs_[i] = nullptr;
mirror_codec_idx_[i] = -1;
}
// Register the default payload type for RED and for CNG at sampling rates of
// 8, 16, 32 and 48 kHz.
for (int i = (ACMCodecDB::kNumCodecs - 1); i >= 0; i--) {
if (IsCodecRED(i) && ACMCodecDB::database_[i].plfreq == 8000) {
red_nb_pltype_ = static_cast<uint8_t>(ACMCodecDB::database_[i].pltype);
} else if (IsCodecCN(i)) {
if (ACMCodecDB::database_[i].plfreq == 8000) {
cng_nb_pltype_ = static_cast<uint8_t>(ACMCodecDB::database_[i].pltype);
} else if (ACMCodecDB::database_[i].plfreq == 16000) {
cng_wb_pltype_ = static_cast<uint8_t>(ACMCodecDB::database_[i].pltype);
} else if (ACMCodecDB::database_[i].plfreq == 32000) {
cng_swb_pltype_ = static_cast<uint8_t>(ACMCodecDB::database_[i].pltype);
} else if (ACMCodecDB::database_[i].plfreq == 48000) {
cng_fb_pltype_ = static_cast<uint8_t>(ACMCodecDB::database_[i].pltype);
}
}
}
thread_checker_.DetachFromThread();
}
CodecManager::~CodecManager() {
for (int i = 0; i < ACMCodecDB::kMaxNumCodecs; i++) {
if (codecs_[i] != NULL) {
// Mirror index holds the address of the codec memory.
assert(mirror_codec_idx_[i] > -1);
if (codecs_[mirror_codec_idx_[i]] != NULL) {
delete codecs_[mirror_codec_idx_[i]];
codecs_[mirror_codec_idx_[i]] = NULL;
}
codecs_[i] = NULL;
}
}
}
int CodecManager::RegisterSendCodec(const CodecInst& send_codec) {
DCHECK(thread_checker_.CalledOnValidThread());
int mirror_id;
int codec_id = IsValidSendCodec(send_codec, true, &mirror_id);
// Check for reported errors from function IsValidSendCodec().
if (codec_id < 0) {
return -1;
}
int dummy_id = 0;
// RED can be registered with other payload type. If not registered a default
// payload type is used.
if (IsCodecRED(&send_codec)) {
// TODO(tlegrand): Remove this check. Already taken care of in
// ACMCodecDB::CodecNumber().
// Check if the payload-type is valid
if (!ACMCodecDB::ValidPayloadType(send_codec.pltype)) {
WEBRTC_TRACE(webrtc::kTraceError, webrtc::kTraceAudioCoding, dummy_id,
"Invalid payload-type %d for %s.", send_codec.pltype,
send_codec.plname);
return -1;
}
// Set RED payload type.
if (send_codec.plfreq == 8000) {
red_nb_pltype_ = static_cast<uint8_t>(send_codec.pltype);
} else {
WEBRTC_TRACE(webrtc::kTraceError, webrtc::kTraceAudioCoding, dummy_id,
"RegisterSendCodec() failed, invalid frequency for RED "
"registration");
return -1;
}
SetRedPayloadType(send_codec.plfreq, send_codec.pltype);
return 0;
}
// CNG can be registered with other payload type. If not registered the
// default payload types from codec database will be used.
if (IsCodecCN(&send_codec)) {
// CNG is registered.
switch (send_codec.plfreq) {
case 8000: {
cng_nb_pltype_ = static_cast<uint8_t>(send_codec.pltype);
break;
}
case 16000: {
cng_wb_pltype_ = static_cast<uint8_t>(send_codec.pltype);
break;
}
case 32000: {
cng_swb_pltype_ = static_cast<uint8_t>(send_codec.pltype);
break;
}
case 48000: {
cng_fb_pltype_ = static_cast<uint8_t>(send_codec.pltype);
break;
}
default: {
WEBRTC_TRACE(webrtc::kTraceError, webrtc::kTraceAudioCoding, dummy_id,
"RegisterSendCodec() failed, invalid frequency for CNG "
"registration");
return -1;
}
}
SetCngPayloadType(send_codec.plfreq, send_codec.pltype);
return 0;
}
// Set Stereo, and make sure VAD and DTX is turned off.
if (send_codec.channels == 2) {
stereo_send_ = true;
if (vad_enabled_ || dtx_enabled_) {
WEBRTC_TRACE(webrtc::kTraceWarning, webrtc::kTraceAudioCoding, dummy_id,
"VAD/DTX is turned off, not supported when sending stereo.");
}
vad_enabled_ = false;
dtx_enabled_ = false;
} else {
stereo_send_ = false;
}
// Check if the codec is already registered as send codec.
bool is_send_codec;
if (current_encoder_) {
int send_codec_mirror_id;
int send_codec_id =
ACMCodecDB::CodecNumber(send_codec_inst_, &send_codec_mirror_id);
assert(send_codec_id >= 0);
is_send_codec =
(send_codec_id == codec_id) || (mirror_id == send_codec_mirror_id);
} else {
is_send_codec = false;
}
// If new codec, or new settings, register.
if (!is_send_codec) {
if (!codecs_[mirror_id]) {
codecs_[mirror_id] = ACMCodecDB::CreateCodecInstance(
send_codec, cng_nb_pltype_, cng_wb_pltype_, cng_swb_pltype_,
cng_fb_pltype_, red_enabled_, red_nb_pltype_);
if (!codecs_[mirror_id]) {
WEBRTC_TRACE(webrtc::kTraceError, webrtc::kTraceAudioCoding, dummy_id,
"Cannot Create the codec");
return -1;
}
mirror_codec_idx_[mirror_id] = mirror_id;
}
if (mirror_id != codec_id) {
codecs_[codec_id] = codecs_[mirror_id];
mirror_codec_idx_[codec_id] = mirror_id;
}
ACMGenericCodec* codec_ptr = codecs_[codec_id];
WebRtcACMCodecParams codec_params;
memcpy(&(codec_params.codec_inst), &send_codec, sizeof(CodecInst));
codec_params.enable_vad = vad_enabled_;
codec_params.enable_dtx = dtx_enabled_;
codec_params.vad_mode = vad_mode_;
// Force initialization.
if (codec_ptr->InitEncoder(&codec_params, true) < 0) {
// Could not initialize the encoder.
// Check if already have a registered codec.
// Depending on that different messages are logged.
if (!current_encoder_) {
WEBRTC_TRACE(webrtc::kTraceError, webrtc::kTraceAudioCoding, dummy_id,
"Cannot Initialize the encoder No Encoder is registered");
} else {
WEBRTC_TRACE(webrtc::kTraceError, webrtc::kTraceAudioCoding, dummy_id,
"Cannot Initialize the encoder, continue encoding with "
"the previously registered codec");
}
return -1;
}
// Update states.
dtx_enabled_ = codec_params.enable_dtx;
vad_enabled_ = codec_params.enable_vad;
vad_mode_ = codec_params.vad_mode;
// Everything is fine so we can replace the previous codec with this one.
if (current_encoder_) {
// If we change codec we start fresh with RED.
// This is not strictly required by the standard.
if (codec_ptr->SetCopyRed(red_enabled_) < 0) {
// We tried to preserve the old red status, if failed, it means the
// red status has to be flipped.
red_enabled_ = !red_enabled_;
}
codec_ptr->SetVAD(&dtx_enabled_, &vad_enabled_, &vad_mode_);
if (!codec_ptr->HasInternalFEC()) {
codec_fec_enabled_ = false;
} else {
if (codec_ptr->SetFEC(codec_fec_enabled_) < 0) {
WEBRTC_TRACE(webrtc::kTraceError, webrtc::kTraceAudioCoding, dummy_id,
"Cannot set codec FEC");
return -1;
}
}
}
current_encoder_ = codecs_[codec_id];
DCHECK(current_encoder_);
memcpy(&send_codec_inst_, &send_codec, sizeof(CodecInst));
return 0;
} else {
// If codec is the same as already registered check if any parameters
// has changed compared to the current values.
// If any parameter is valid then apply it and record.
bool force_init = false;
if (mirror_id != codec_id) {
codecs_[codec_id] = codecs_[mirror_id];
mirror_codec_idx_[codec_id] = mirror_id;
}
// Check the payload type.
if (send_codec.pltype != send_codec_inst_.pltype) {
// At this point check if the given payload type is valid.
// Record it later when the sampling frequency is changed
// successfully.
if (!ACMCodecDB::ValidPayloadType(send_codec.pltype)) {
WEBRTC_TRACE(webrtc::kTraceError, webrtc::kTraceAudioCoding, dummy_id,
"Out of range payload type");
return -1;
}
}
// If there is a codec that ONE instance of codec supports multiple
// sampling frequencies, then we need to take care of it here.
// one such a codec is iSAC. Both WB and SWB are encoded and decoded
// with one iSAC instance. Therefore, we need to update the encoder
// frequency if required.
if (send_codec_inst_.plfreq != send_codec.plfreq) {
force_init = true;
}
// If packet size or number of channels has changed, we need to
// re-initialize the encoder.
if (send_codec_inst_.pacsize != send_codec.pacsize) {
force_init = true;
}
if (send_codec_inst_.channels != send_codec.channels) {
force_init = true;
}
if (force_init) {
WebRtcACMCodecParams codec_params;
memcpy(&(codec_params.codec_inst), &send_codec, sizeof(CodecInst));
codec_params.enable_vad = vad_enabled_;
codec_params.enable_dtx = dtx_enabled_;
codec_params.vad_mode = vad_mode_;
// Force initialization.
if (current_encoder_->InitEncoder(&codec_params, true) < 0) {
WEBRTC_TRACE(webrtc::kTraceError, webrtc::kTraceAudioCoding, dummy_id,
"Could not change the codec packet-size.");
return -1;
}
send_codec_inst_.plfreq = send_codec.plfreq;
send_codec_inst_.pacsize = send_codec.pacsize;
send_codec_inst_.channels = send_codec.channels;
}
// If the change of sampling frequency has been successful then
// we store the payload-type.
send_codec_inst_.pltype = send_codec.pltype;
// Check if a change in Rate is required.
if (send_codec.rate != send_codec_inst_.rate) {
if (codecs_[codec_id]->SetBitRate(send_codec.rate) < 0) {
WEBRTC_TRACE(webrtc::kTraceError, webrtc::kTraceAudioCoding, dummy_id,
"Could not change the codec rate.");
return -1;
}
send_codec_inst_.rate = send_codec.rate;
}
if (!codecs_[codec_id]->HasInternalFEC()) {
codec_fec_enabled_ = false;
} else {
if (codecs_[codec_id]->SetFEC(codec_fec_enabled_) < 0) {
WEBRTC_TRACE(webrtc::kTraceError, webrtc::kTraceAudioCoding, dummy_id,
"Cannot set codec FEC");
return -1;
}
}
return 0;
}
}
int CodecManager::SendCodec(CodecInst* current_codec) const {
int dummy_id = 0;
WEBRTC_TRACE(webrtc::kTraceStream, webrtc::kTraceAudioCoding, dummy_id,
"SendCodec()");
if (!current_encoder_) {
WEBRTC_TRACE(webrtc::kTraceStream, webrtc::kTraceAudioCoding, dummy_id,
"SendCodec Failed, no codec is registered");
return -1;
}
WebRtcACMCodecParams encoder_param;
current_encoder_->EncoderParams(&encoder_param);
encoder_param.codec_inst.pltype = send_codec_inst_.pltype;
memcpy(current_codec, &(encoder_param.codec_inst), sizeof(CodecInst));
return 0;
}
// Register possible receive codecs, can be called multiple times,
// for codecs, CNG (NB, WB and SWB), DTMF, RED.
int CodecManager::RegisterReceiveCodec(const CodecInst& codec) {
if (codec.channels > 2 || codec.channels < 0) {
WEBRTC_TRACE(webrtc::kTraceError, webrtc::kTraceAudioCoding, 0,
"Unsupported number of channels, %d.", codec.channels);
return -1;
}
int mirror_id;
int codec_id = ACMCodecDB::ReceiverCodecNumber(codec, &mirror_id);
if (codec_id < 0 || codec_id >= ACMCodecDB::kNumCodecs) {
WEBRTC_TRACE(webrtc::kTraceError, webrtc::kTraceAudioCoding, 0,
"Wrong codec params to be registered as receive codec");
return -1;
}
// Check if the payload-type is valid.
if (!ACMCodecDB::ValidPayloadType(codec.pltype)) {
WEBRTC_TRACE(webrtc::kTraceError, webrtc::kTraceAudioCoding, 0,
"Invalid payload-type %d for %s.", codec.pltype, codec.plname);
return -1;
}
AudioDecoder* decoder = NULL;
// Get |decoder| associated with |codec|. |decoder| can be NULL if |codec|
// does not own its decoder.
if (GetAudioDecoder(codec, codec_id, mirror_id, &decoder) < 0) {
WEBRTC_TRACE(webrtc::kTraceError, webrtc::kTraceAudioCoding, 0,
"Wrong codec params to be registered as receive codec");
return -1;
}
uint8_t payload_type = static_cast<uint8_t>(codec.pltype);
return acm_->RegisterDecoder(codec_id, payload_type, codec.channels, decoder);
}
bool CodecManager::SetCopyRed(bool enable) {
if (enable && codec_fec_enabled_) {
WEBRTC_TRACE(webrtc::kTraceWarning, webrtc::kTraceAudioCoding, 0,
"Codec internal FEC and RED cannot be co-enabled.");
return false;
}
if (current_encoder_ && current_encoder_->SetCopyRed(enable) < 0) {
WEBRTC_TRACE(webrtc::kTraceError, webrtc::kTraceAudioCoding, 0,
"SetCopyRed failed");
return false;
}
red_enabled_ = enable;
return true;
}
int CodecManager::SetVAD(bool enable_dtx, bool enable_vad, ACMVADMode mode) {
// Sanity check of the mode.
if ((mode != VADNormal) && (mode != VADLowBitrate) && (mode != VADAggr) &&
(mode != VADVeryAggr)) {
WEBRTC_TRACE(webrtc::kTraceError, webrtc::kTraceAudioCoding, 0,
"Invalid VAD Mode %d, no change is made to VAD/DTX status",
mode);
return -1;
}
// Check that the send codec is mono. We don't support VAD/DTX for stereo
// sending.
if ((enable_dtx || enable_vad) && stereo_send_) {
WEBRTC_TRACE(webrtc::kTraceError, webrtc::kTraceAudioCoding, 0,
"VAD/DTX not supported for stereo sending");
dtx_enabled_ = false;
vad_enabled_ = false;
vad_mode_ = mode;
return -1;
}
// Store VAD/DTX settings. Values can be changed in the call to "SetVAD"
// below.
dtx_enabled_ = enable_dtx;
vad_enabled_ = enable_vad;
vad_mode_ = mode;
// If a send codec is registered, set VAD/DTX for the codec.
if (current_encoder_ &&
current_encoder_->SetVAD(&dtx_enabled_, &vad_enabled_, &vad_mode_) < 0) {
// SetVAD failed.
WEBRTC_TRACE(webrtc::kTraceError, webrtc::kTraceAudioCoding, 0,
"SetVAD failed");
vad_enabled_ = false;
dtx_enabled_ = false;
return -1;
}
return 0;
}
void CodecManager::VAD(bool* dtx_enabled,
bool* vad_enabled,
ACMVADMode* mode) const {
*dtx_enabled = dtx_enabled_;
*vad_enabled = vad_enabled_;
*mode = vad_mode_;
}
int CodecManager::SetCodecFEC(bool enable_codec_fec) {
if (enable_codec_fec == true && red_enabled_ == true) {
WEBRTC_TRACE(webrtc::kTraceWarning, webrtc::kTraceAudioCoding, 0,
"Codec internal FEC and RED cannot be co-enabled.");
return -1;
}
// Set codec FEC.
if (current_encoder_ && current_encoder_->SetFEC(enable_codec_fec) < 0) {
WEBRTC_TRACE(webrtc::kTraceError, webrtc::kTraceAudioCoding, 0,
"Set codec internal FEC failed.");
return -1;
}
codec_fec_enabled_ = enable_codec_fec;
return 0;
}
void CodecManager::SetCngPayloadType(int sample_rate_hz, int payload_type) {
for (auto* codec : codecs_) {
if (codec) {
codec->SetCngPt(sample_rate_hz, payload_type);
}
}
}
void CodecManager::SetRedPayloadType(int sample_rate_hz, int payload_type) {
for (auto* codec : codecs_) {
if (codec) {
codec->SetRedPt(sample_rate_hz, payload_type);
}
}
}
int CodecManager::GetAudioDecoder(const CodecInst& codec,
int codec_id,
int mirror_id,
AudioDecoder** decoder) {
if (ACMCodecDB::OwnsDecoder(codec_id)) {
// This codec has to own its own decoder. Therefore, it should create the
// corresponding AudioDecoder class and insert it into NetEq. If the codec
// does not exist create it.
//
// TODO(turajs): this part of the code is common with RegisterSendCodec(),
// make a method for it.
if (codecs_[mirror_id] == NULL) {
codecs_[mirror_id] = ACMCodecDB::CreateCodecInstance(
codec, cng_nb_pltype_, cng_wb_pltype_, cng_swb_pltype_,
cng_fb_pltype_, red_enabled_, red_nb_pltype_);
if (codecs_[mirror_id] == NULL) {
WEBRTC_TRACE(webrtc::kTraceError, webrtc::kTraceAudioCoding, 0,
"Cannot Create the codec");
return -1;
}
mirror_codec_idx_[mirror_id] = mirror_id;
}
if (mirror_id != codec_id) {
codecs_[codec_id] = codecs_[mirror_id];
mirror_codec_idx_[codec_id] = mirror_id;
}
*decoder = codecs_[codec_id]->Decoder();
if (!*decoder) {
assert(false);
return -1;
}
} else {
*decoder = NULL;
}
return 0;
}
} // namespace acm2
} // namespace webrtc

View File

@ -0,0 +1,101 @@
/*
* 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_MANAGER_H_
#define WEBRTC_MODULES_AUDIO_CODING_MAIN_ACM2_CODEC_MANAGER_H_
#include "webrtc/base/constructormagic.h"
#include "webrtc/base/thread_checker.h"
#include "webrtc/modules/audio_coding/main/acm2/acm_codec_database.h"
#include "webrtc/modules/audio_coding/main/interface/audio_coding_module_typedefs.h"
#include "webrtc/common_types.h"
namespace webrtc {
class AudioDecoder;
namespace acm2 {
class ACMGenericCodec;
class AudioCodingModuleImpl;
class CodecManager final {
public:
explicit CodecManager(AudioCodingModuleImpl* acm);
~CodecManager();
int RegisterSendCodec(const CodecInst& send_codec);
int SendCodec(CodecInst* current_codec) const;
int RegisterReceiveCodec(const CodecInst& receive_codec);
bool SetCopyRed(bool enable);
int SetVAD(bool enable_dtx, bool enable_vad, ACMVADMode mode);
void VAD(bool* dtx_enabled, bool* vad_enabled, ACMVADMode* mode) const;
int SetCodecFEC(bool enable_codec_fec);
bool stereo_send() const { return stereo_send_; }
bool red_enabled() const { return red_enabled_; }
bool codec_fec_enabled() const { return codec_fec_enabled_; }
ACMGenericCodec* current_encoder() { return current_encoder_; }
const ACMGenericCodec* current_encoder() const { return current_encoder_; }
private:
void SetCngPayloadType(int sample_rate_hz, int payload_type);
void SetRedPayloadType(int sample_rate_hz, int payload_type);
// Get a pointer to AudioDecoder of the given codec. For some codecs, e.g.
// iSAC, encoding and decoding have to be performed on a shared
// codec-instance. By calling this method, we get the codec-instance that ACM
// owns, then pass that to NetEq. This way, we perform both encoding and
// decoding on the same codec-instance. Furthermore, ACM would have control
// over decoder functionality if required. If |codec| does not share an
// instance between encoder and decoder, the |*decoder| is set NULL.
// The field ACMCodecDB::CodecSettings.owns_decoder indicates that if a
// codec owns the decoder-instance. For such codecs |*decoder| should be a
// valid pointer, otherwise it will be NULL.
int GetAudioDecoder(const CodecInst& codec,
int codec_id,
int mirror_id,
AudioDecoder** decoder);
AudioCodingModuleImpl* acm_;
rtc::ThreadChecker thread_checker_;
uint8_t cng_nb_pltype_;
uint8_t cng_wb_pltype_;
uint8_t cng_swb_pltype_;
uint8_t cng_fb_pltype_;
uint8_t red_nb_pltype_;
bool stereo_send_;
bool vad_enabled_;
bool dtx_enabled_;
ACMVADMode vad_mode_;
ACMGenericCodec* current_encoder_;
CodecInst send_codec_inst_;
bool red_enabled_;
bool codec_fec_enabled_;
ACMGenericCodec* codecs_[ACMCodecDB::kMaxNumCodecs];
int mirror_codec_idx_[ACMCodecDB::kMaxNumCodecs];
DISALLOW_COPY_AND_ASSIGN(CodecManager);
};
} // namespace acm2
} // namespace webrtc
#endif // WEBRTC_MODULES_AUDIO_CODING_MAIN_ACM2_CODEC_MANAGER_H_

View File

@ -190,20 +190,6 @@ class AudioCodingModule {
// Sender
//
///////////////////////////////////////////////////////////////////////////
// int32_t InitializeSender()
// Any encoder-related state of ACM will be initialized to the
// same state when ACM is created. This will not interrupt or
// effect decoding functionality of ACM. ACM will lose all the
// encoding-related settings by calling this function.
// For instance, a send codec has to be registered again.
//
// Return value:
// -1 if failed to initialize,
// 0 if succeeded.
//
virtual int32_t InitializeSender() = 0;
///////////////////////////////////////////////////////////////////////////
// int32_t ResetEncoder()
// This API resets the states of encoder. All the encoder settings, such as

View File

@ -1144,9 +1144,6 @@ void APITest::ChangeCodec(char side) {
WriteLockScoped wl(_apiTestRWLock);
*thereIsEncoder = false;
}
CHECK_ERROR_MT(myACM->InitializeSender());
Wait(1000);
// After Initialization CN is lost, re-register them
if (AudioCodingModule::Codec("CN", &myCodec, 8000, 1) >= 0) {
CHECK_ERROR_MT(myACM->RegisterSendCodec(myCodec));

View File

@ -53,7 +53,6 @@ Sender::Sender()
void Sender::Setup(AudioCodingModule *acm, RTPStream *rtpStream,
std::string in_file_name, int sample_rate, int channels) {
acm->InitializeSender();
struct CodecInst sendCodec;
int noOfCodecs = acm->NumberOfCodecs();
int codecNo;

View File

@ -281,7 +281,6 @@ void TwoWayCommunication::Perform() {
// In the middle of a session with data flowing between two sides, called A
// and B, APIs will be called, like ResetEncoder(), and the code should
// continue to run, and be able to recover.
bool expect_error_add = false;
while (!_inFileA.EndOfFile() && !_inFileB.EndOfFile()) {
msecPassed += 10;
EXPECT_GT(_inFileA.Read10MsData(audioFrame), 0);
@ -290,12 +289,7 @@ void TwoWayCommunication::Perform() {
EXPECT_GT(_inFileB.Read10MsData(audioFrame), 0);
// Expect call to pass except for the time when no send codec is registered.
if (!expect_error_add) {
EXPECT_GE(_acmB->Add10MsData(audioFrame), 0);
} else {
EXPECT_EQ(-1, _acmB->Add10MsData(audioFrame));
}
EXPECT_GE(_acmB->Add10MsData(audioFrame), 0);
EXPECT_GE(_acmRefB->Add10MsData(audioFrame), 0);
EXPECT_EQ(0, _acmA->PlayoutData10Ms(outFreqHzA, &audioFrame));
_outFileA.Write10MsData(audioFrame);
@ -315,14 +309,11 @@ void TwoWayCommunication::Perform() {
// side B.
if (((secPassed % 5) == 4) && (msecPassed == 0)) {
EXPECT_EQ(0, _acmA->ResetEncoder());
EXPECT_EQ(0, _acmB->InitializeSender());
expect_error_add = true;
}
// Re-register send codec on side B.
if (((secPassed % 5) == 4) && (msecPassed >= 990)) {
EXPECT_EQ(0, _acmB->RegisterSendCodec(codecInst_B));
EXPECT_EQ(0, _acmB->SendCodec(&dummy));
expect_error_add = false;
}
// Reset decoder on side B, and initialize receiver on side A.
if (((secPassed % 7) == 6) && (msecPassed == 0)) {