
ACM1 code is wrapped in namespace acm1. Inculde paths and define guards of ACM2 source codes are corrected. gypi file of ACM2 is changed so that ACM1 will later on depends on ACM2. BUG= R=andrew@webrtc.org Review URL: https://webrtc-codereview.appspot.com/2206004 git-svn-id: http://webrtc.googlecode.com/svn/trunk@4743 4adac7df-926f-26a2-2b94-8c16560cd09d
830 lines
26 KiB
C++
830 lines
26 KiB
C++
/*
|
|
* Copyright (c) 2012 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/acm_isac.h"
|
|
|
|
#include <assert.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/neteq4/interface/audio_decoder.h"
|
|
#include "webrtc/system_wrappers/interface/trace.h"
|
|
|
|
#ifdef WEBRTC_CODEC_ISAC
|
|
#include "webrtc/modules/audio_coding/codecs/isac/main/interface/isac.h"
|
|
#endif
|
|
|
|
#ifdef WEBRTC_CODEC_ISACFX
|
|
#include "webrtc/modules/audio_coding/codecs/isac/fix/interface/isacfix.h"
|
|
#endif
|
|
|
|
#if defined (WEBRTC_CODEC_ISAC) || defined (WEBRTC_CODEC_ISACFX)
|
|
#include "webrtc/modules/audio_coding/main/acm2/acm_isac_macros.h"
|
|
#endif
|
|
|
|
namespace webrtc {
|
|
|
|
// we need this otherwise we cannot use forward declaration
|
|
// in the header file
|
|
#if (defined(WEBRTC_CODEC_ISAC) || defined(WEBRTC_CODEC_ISACFX))
|
|
struct ACMISACInst {
|
|
ACM_ISAC_STRUCT* inst;
|
|
};
|
|
#endif
|
|
|
|
#define ISAC_MIN_RATE 10000
|
|
#define ISAC_MAX_RATE 56000
|
|
|
|
// Tables for bandwidth estimates
|
|
#define NR_ISAC_BANDWIDTHS 24
|
|
static const int32_t kIsacRatesWb[NR_ISAC_BANDWIDTHS] = {
|
|
10000, 11100, 12300, 13700, 15200, 16900, 18800, 20900, 23300, 25900, 28700,
|
|
31900, 10100, 11200, 12400, 13800, 15300, 17000, 18900, 21000, 23400, 26000,
|
|
28800, 32000};
|
|
|
|
static const int32_t kIsacRatesSwb[NR_ISAC_BANDWIDTHS] = {
|
|
10000, 11000, 12400, 13800, 15300, 17000, 18900, 21000, 23200, 25400, 27600,
|
|
29800, 32000, 34100, 36300, 38500, 40700, 42900, 45100, 47300, 49500, 51700,
|
|
53900, 56000 };
|
|
|
|
#if (!defined(WEBRTC_CODEC_ISAC) && !defined(WEBRTC_CODEC_ISACFX))
|
|
|
|
ACMISAC::ACMISAC(int16_t /* codec_id */)
|
|
: codec_inst_ptr_(NULL),
|
|
is_enc_initialized_(false),
|
|
isac_coding_mode_(CHANNEL_INDEPENDENT),
|
|
enforce_frame_size_(false),
|
|
isac_currentBN_(32000),
|
|
samples_in10MsAudio_(160), // Initiates to 16 kHz mode.
|
|
audio_decoder_(NULL),
|
|
decoder_initialized_(false) {}
|
|
|
|
ACMISAC::~ACMISAC() {
|
|
return;
|
|
}
|
|
|
|
ACMGenericCodec* ACMISAC::CreateInstance(void) { return NULL; }
|
|
|
|
int16_t ACMISAC::InternalEncode(uint8_t* /* bitstream */,
|
|
int16_t* /* bitstream_len_byte */) {
|
|
return -1;
|
|
}
|
|
|
|
int16_t ACMISAC::InternalInitEncoder(WebRtcACMCodecParams* /* codec_params */) {
|
|
return -1;
|
|
}
|
|
|
|
int16_t ACMISAC::InternalInitDecoder(WebRtcACMCodecParams* /* codec_params */) {
|
|
return -1;
|
|
}
|
|
|
|
int16_t ACMISAC::InternalCreateEncoder() { return -1; }
|
|
|
|
void ACMISAC::DestructEncoderSafe() { return; }
|
|
|
|
void ACMISAC::InternalDestructEncoderInst(void* /* ptr_inst */) { return; }
|
|
|
|
int16_t ACMISAC::Transcode(uint8_t* /* bitstream */,
|
|
int16_t* /* bitstream_len_byte */,
|
|
int16_t /* q_bwe */,
|
|
int32_t /* scale */,
|
|
bool /* is_red */) {
|
|
return -1;
|
|
}
|
|
|
|
int16_t ACMISAC::SetBitRateSafe(int32_t /* bit_rate */) { return -1; }
|
|
|
|
int32_t ACMISAC::GetEstimatedBandwidthSafe() { return -1; }
|
|
|
|
int32_t ACMISAC::SetEstimatedBandwidthSafe(int32_t /* estimated_bandwidth */) {
|
|
return -1;
|
|
}
|
|
|
|
int32_t ACMISAC::GetRedPayloadSafe(uint8_t* /* red_payload */,
|
|
int16_t* /* payload_bytes */) {
|
|
return -1;
|
|
}
|
|
|
|
int16_t ACMISAC::UpdateDecoderSampFreq(int16_t /* codec_id */) { return -1; }
|
|
|
|
int16_t ACMISAC::UpdateEncoderSampFreq(uint16_t /* encoder_samp_freq_hz */) {
|
|
return -1;
|
|
}
|
|
|
|
int16_t ACMISAC::EncoderSampFreq(uint16_t* /* samp_freq_hz */) { return -1; }
|
|
|
|
int32_t ACMISAC::ConfigISACBandwidthEstimator(
|
|
const uint8_t /* init_frame_size_msec */,
|
|
const uint16_t /* init_rate_bit_per_sec */,
|
|
const bool /* enforce_frame_size */) {
|
|
return -1;
|
|
}
|
|
|
|
int32_t ACMISAC::SetISACMaxPayloadSize(
|
|
const uint16_t /* max_payload_len_bytes */) {
|
|
return -1;
|
|
}
|
|
|
|
int32_t ACMISAC::SetISACMaxRate(const uint32_t /* max_rate_bit_per_sec */) {
|
|
return -1;
|
|
}
|
|
|
|
void ACMISAC::UpdateFrameLen() { return; }
|
|
|
|
void ACMISAC::CurrentRate(int32_t* /*rate_bit_per_sec */) { return; }
|
|
|
|
bool ACMISAC::DecoderParamsSafe(WebRtcACMCodecParams* /* dec_params */,
|
|
const uint8_t /* payload_type */) {
|
|
return false;
|
|
}
|
|
|
|
int16_t ACMISAC::REDPayloadISAC(const int32_t /* isac_rate */,
|
|
const int16_t /* isac_bw_estimate */,
|
|
uint8_t* /* payload */,
|
|
int16_t* /* payload_len_bytes */) {
|
|
return -1;
|
|
}
|
|
|
|
AudioDecoder* ACMISAC::Decoder(int /* codec_id */) { return NULL; }
|
|
|
|
#else //===================== Actual Implementation =======================
|
|
|
|
#ifdef WEBRTC_CODEC_ISACFX
|
|
|
|
// How the scaling is computed. iSAC computes a gain based on the
|
|
// bottleneck. It follows the following expression for that
|
|
//
|
|
// G(BN_kbps) = pow(10, (a + b * BN_kbps + c * BN_kbps * BN_kbps) / 20.0)
|
|
// / 3.4641;
|
|
//
|
|
// Where for 30 ms framelength we have,
|
|
//
|
|
// a = -23; b = 0.48; c = 0;
|
|
//
|
|
// As the default encoder is operating at 32kbps we have the scale as
|
|
//
|
|
// S(BN_kbps) = G(BN_kbps) / G(32);
|
|
|
|
#define ISAC_NUM_SUPPORTED_RATES 9
|
|
|
|
static const uint16_t kIsacSuportedRates[ISAC_NUM_SUPPORTED_RATES] = {
|
|
32000, 30000, 26000, 23000, 21000, 19000, 17000, 15000, 12000};
|
|
|
|
static const float kIsacScale[ISAC_NUM_SUPPORTED_RATES] = {
|
|
1.0f, 0.8954f, 0.7178f, 0.6081f, 0.5445f,
|
|
0.4875f, 0.4365f, 0.3908f, 0.3311f
|
|
};
|
|
|
|
enum IsacSamplingRate {
|
|
kIsacWideband = 16,
|
|
kIsacSuperWideband = 32
|
|
};
|
|
|
|
static float ACMISACFixTranscodingScale(uint16_t rate) {
|
|
// find the scale for transcoding, the scale is rounded
|
|
// downward
|
|
float scale = -1;
|
|
for (int16_t n = 0; n < ISAC_NUM_SUPPORTED_RATES; n++) {
|
|
if (rate >= kIsacSuportedRates[n]) {
|
|
scale = kIsacScale[n];
|
|
break;
|
|
}
|
|
}
|
|
return scale;
|
|
}
|
|
|
|
static void ACMISACFixGetSendBitrate(ACM_ISAC_STRUCT* inst,
|
|
int32_t* bottleneck) {
|
|
*bottleneck = WebRtcIsacfix_GetUplinkBw(inst);
|
|
}
|
|
|
|
static int16_t ACMISACFixGetNewBitstream(ACM_ISAC_STRUCT* inst,
|
|
int16_t bwe_index,
|
|
int16_t /* jitter_index */,
|
|
int32_t rate,
|
|
int16_t* bitstream,
|
|
bool is_red) {
|
|
if (is_red) {
|
|
// RED not supported with iSACFIX
|
|
return -1;
|
|
}
|
|
float scale = ACMISACFixTranscodingScale((uint16_t)rate);
|
|
return WebRtcIsacfix_GetNewBitStream(inst, bwe_index, scale, bitstream);
|
|
}
|
|
|
|
static int16_t ACMISACFixGetSendBWE(ACM_ISAC_STRUCT* inst,
|
|
int16_t* rate_index,
|
|
int16_t* /* dummy */) {
|
|
int16_t local_rate_index;
|
|
int16_t status = WebRtcIsacfix_GetDownLinkBwIndex(inst, &local_rate_index);
|
|
if (status < 0) {
|
|
return -1;
|
|
} else {
|
|
*rate_index = local_rate_index;
|
|
return 0;
|
|
}
|
|
}
|
|
|
|
static int16_t ACMISACFixControlBWE(ACM_ISAC_STRUCT* inst,
|
|
int32_t rate_bps,
|
|
int16_t frame_size_ms,
|
|
int16_t enforce_frame_size) {
|
|
return WebRtcIsacfix_ControlBwe(
|
|
inst, (int16_t)rate_bps, frame_size_ms, enforce_frame_size);
|
|
}
|
|
|
|
static int16_t ACMISACFixControl(ACM_ISAC_STRUCT* inst,
|
|
int32_t rate_bps,
|
|
int16_t frame_size_ms) {
|
|
return WebRtcIsacfix_Control(inst, (int16_t)rate_bps, frame_size_ms);
|
|
}
|
|
|
|
// The following two function should have the same signature as their counter
|
|
// part in iSAC floating-point, i.e. WebRtcIsac_EncSampRate &
|
|
// WebRtcIsac_DecSampRate.
|
|
static uint16_t ACMISACFixGetEncSampRate(ACM_ISAC_STRUCT* /* inst */) {
|
|
return 16000;
|
|
}
|
|
|
|
static uint16_t ACMISACFixGetDecSampRate(ACM_ISAC_STRUCT* /* inst */) {
|
|
return 16000;
|
|
}
|
|
|
|
#endif
|
|
|
|
// Decoder class to be injected into NetEq.
|
|
class AcmAudioDecoderIsac : public AudioDecoder {
|
|
public:
|
|
AcmAudioDecoderIsac(int codec_id, void* state)
|
|
: AudioDecoder(ACMCodecDB::neteq_decoders_[codec_id]) {
|
|
state_ = state;
|
|
}
|
|
|
|
// ACMISAC is the owner of the object where |state_| is pointing to.
|
|
// Therefore, it should not be deleted in this destructor.
|
|
virtual ~AcmAudioDecoderIsac() {}
|
|
|
|
virtual int Decode(const uint8_t* encoded, size_t encoded_len,
|
|
int16_t* decoded, SpeechType* speech_type) {
|
|
int16_t temp_type;
|
|
int ret = ACM_ISAC_DECODE_B(static_cast<ACM_ISAC_STRUCT*>(state_),
|
|
reinterpret_cast<const uint16_t*>(encoded),
|
|
static_cast<int16_t>(encoded_len), decoded,
|
|
&temp_type);
|
|
*speech_type = ConvertSpeechType(temp_type);
|
|
return ret;
|
|
}
|
|
|
|
virtual bool HasDecodePlc() const { return true; }
|
|
|
|
virtual int DecodePlc(int num_frames, int16_t* decoded) {
|
|
return ACM_ISAC_DECODEPLC(static_cast<ACM_ISAC_STRUCT*>(state_),
|
|
decoded, static_cast<int16_t>(num_frames));
|
|
}
|
|
|
|
virtual int Init() {
|
|
return 0; // We expect that the initialized instance is injected in the
|
|
// constructor.
|
|
}
|
|
|
|
virtual int IncomingPacket(const uint8_t* payload,
|
|
size_t payload_len,
|
|
uint16_t rtp_sequence_number,
|
|
uint32_t rtp_timestamp,
|
|
uint32_t arrival_timestamp) {
|
|
return ACM_ISAC_DECODE_BWE(static_cast<ACM_ISAC_STRUCT*>(state_),
|
|
reinterpret_cast<const uint16_t*>(payload),
|
|
payload_len,
|
|
rtp_sequence_number,
|
|
rtp_timestamp,
|
|
arrival_timestamp);
|
|
}
|
|
|
|
virtual int DecodeRedundant(const uint8_t* encoded,
|
|
size_t encoded_len, int16_t* decoded,
|
|
SpeechType* speech_type) {
|
|
int16_t temp_type = 1; // Default is speech.
|
|
int16_t ret = ACM_ISAC_DECODERCU(static_cast<ISACStruct*>(state_),
|
|
reinterpret_cast<const uint16_t*>(encoded),
|
|
static_cast<int16_t>(encoded_len), decoded,
|
|
&temp_type);
|
|
*speech_type = ConvertSpeechType(temp_type);
|
|
return ret;
|
|
}
|
|
|
|
virtual int ErrorCode() {
|
|
return ACM_ISAC_GETERRORCODE(static_cast<ACM_ISAC_STRUCT*>(state_));
|
|
}
|
|
|
|
private:
|
|
DISALLOW_COPY_AND_ASSIGN(AcmAudioDecoderIsac);
|
|
};
|
|
|
|
ACMISAC::ACMISAC(int16_t codec_id)
|
|
: is_enc_initialized_(false),
|
|
isac_coding_mode_(CHANNEL_INDEPENDENT),
|
|
enforce_frame_size_(false),
|
|
isac_current_bn_(32000),
|
|
samples_in_10ms_audio_(160), // Initiates to 16 kHz mode.
|
|
audio_decoder_(NULL),
|
|
decoder_initialized_(false) {
|
|
codec_id_ = codec_id;
|
|
|
|
// Create codec instance.
|
|
codec_inst_ptr_ = new ACMISACInst;
|
|
if (codec_inst_ptr_ == NULL) {
|
|
return;
|
|
}
|
|
codec_inst_ptr_->inst = NULL;
|
|
}
|
|
|
|
ACMISAC::~ACMISAC() {
|
|
if (audio_decoder_ != NULL) {
|
|
delete audio_decoder_;
|
|
audio_decoder_ = NULL;
|
|
}
|
|
|
|
if (codec_inst_ptr_ != NULL) {
|
|
if (codec_inst_ptr_->inst != NULL) {
|
|
ACM_ISAC_FREE(codec_inst_ptr_->inst);
|
|
codec_inst_ptr_->inst = NULL;
|
|
}
|
|
delete codec_inst_ptr_;
|
|
codec_inst_ptr_ = NULL;
|
|
}
|
|
return;
|
|
}
|
|
|
|
ACMGenericCodec* ACMISAC::CreateInstance(void) { return NULL; }
|
|
|
|
int16_t ACMISAC::InternalEncode(uint8_t* bitstream,
|
|
int16_t* bitstream_len_byte) {
|
|
// ISAC takes 10ms audio every time we call encoder, therefore,
|
|
// it should be treated like codecs with 'basic coding block'
|
|
// non-zero, and the following 'while-loop' should not be necessary.
|
|
// However, due to a mistake in the codec the frame-size might change
|
|
// at the first 10ms pushed in to iSAC if the bit-rate is low, this is
|
|
// sort of a bug in iSAC. to address this we treat iSAC as the
|
|
// following.
|
|
if (codec_inst_ptr_ == NULL) {
|
|
return -1;
|
|
}
|
|
*bitstream_len_byte = 0;
|
|
while ((*bitstream_len_byte == 0) && (in_audio_ix_read_ < frame_len_smpl_)) {
|
|
if (in_audio_ix_read_ > in_audio_ix_write_) {
|
|
// something is wrong.
|
|
WEBRTC_TRACE(webrtc::kTraceError, webrtc::kTraceAudioCoding, unique_id_,
|
|
"The actual frame-size of iSAC appears to be larger that "
|
|
"expected. All audio pushed in but no bit-stream is "
|
|
"generated.");
|
|
return -1;
|
|
}
|
|
*bitstream_len_byte = ACM_ISAC_ENCODE(
|
|
codec_inst_ptr_->inst, &in_audio_[in_audio_ix_read_],
|
|
reinterpret_cast<int16_t*>(bitstream));
|
|
// increment the read index this tell the caller that how far
|
|
// we have gone forward in reading the audio buffer
|
|
in_audio_ix_read_ += samples_in_10ms_audio_;
|
|
}
|
|
if (*bitstream_len_byte == 0) {
|
|
WEBRTC_TRACE(webrtc::kTraceWarning, webrtc::kTraceAudioCoding, unique_id_,
|
|
"ISAC Has encoded the whole frame but no bit-stream is "
|
|
"generated.");
|
|
}
|
|
|
|
// a packet is generated iSAC, is set in adaptive mode may change
|
|
// the frame length and we like to update the bottleneck value as
|
|
// well, although updating bottleneck is not crucial
|
|
if ((*bitstream_len_byte > 0) && (isac_coding_mode_ == ADAPTIVE)) {
|
|
ACM_ISAC_GETSENDBITRATE(codec_inst_ptr_->inst, &isac_current_bn_);
|
|
}
|
|
UpdateFrameLen();
|
|
return *bitstream_len_byte;
|
|
}
|
|
|
|
int16_t ACMISAC::InternalInitEncoder(WebRtcACMCodecParams* codec_params) {
|
|
// if rate is set to -1 then iSAC has to be in adaptive mode
|
|
if (codec_params->codec_inst.rate == -1) {
|
|
isac_coding_mode_ = ADAPTIVE;
|
|
} else if ((codec_params->codec_inst.rate >= ISAC_MIN_RATE) &&
|
|
(codec_params->codec_inst.rate <= ISAC_MAX_RATE)) {
|
|
// sanity check that rate is in acceptable range
|
|
isac_coding_mode_ = CHANNEL_INDEPENDENT;
|
|
isac_current_bn_ = codec_params->codec_inst.rate;
|
|
} else {
|
|
return -1;
|
|
}
|
|
|
|
// we need to set the encoder sampling frequency.
|
|
if (UpdateEncoderSampFreq((uint16_t)codec_params->codec_inst.plfreq) < 0) {
|
|
return -1;
|
|
}
|
|
if (ACM_ISAC_ENCODERINIT(codec_inst_ptr_->inst, isac_coding_mode_) < 0) {
|
|
return -1;
|
|
}
|
|
|
|
// apply the frame-size and rate if operating in
|
|
// channel-independent mode
|
|
if (isac_coding_mode_ == CHANNEL_INDEPENDENT) {
|
|
if (ACM_ISAC_CONTROL(codec_inst_ptr_->inst,
|
|
codec_params->codec_inst.rate,
|
|
codec_params->codec_inst.pacsize /
|
|
(codec_params->codec_inst.plfreq / 1000)) < 0) {
|
|
return -1;
|
|
}
|
|
} else {
|
|
// We need this for adaptive case and has to be called
|
|
// after initialization
|
|
ACM_ISAC_GETSENDBITRATE(codec_inst_ptr_->inst, &isac_current_bn_);
|
|
}
|
|
frame_len_smpl_ = ACM_ISAC_GETNEWFRAMELEN(codec_inst_ptr_->inst);
|
|
return 0;
|
|
}
|
|
|
|
int16_t ACMISAC::InternalInitDecoder(WebRtcACMCodecParams* codec_params) {
|
|
if (codec_inst_ptr_ == NULL) {
|
|
return -1;
|
|
}
|
|
|
|
// set decoder sampling frequency.
|
|
if (codec_params->codec_inst.plfreq == 32000 ||
|
|
codec_params->codec_inst.plfreq == 48000) {
|
|
UpdateDecoderSampFreq(ACMCodecDB::kISACSWB);
|
|
} else {
|
|
UpdateDecoderSampFreq(ACMCodecDB::kISAC);
|
|
}
|
|
|
|
// in a one-way communication we may never register send-codec.
|
|
// However we like that the BWE to work properly so it has to
|
|
// be initialized. The BWE is initialized when iSAC encoder is initialized.
|
|
// Therefore, we need this.
|
|
if (!encoder_initialized_) {
|
|
// Since we don't require a valid rate or a valid packet size when
|
|
// initializing the decoder, we set valid values before initializing encoder
|
|
codec_params->codec_inst.rate = kIsacWbDefaultRate;
|
|
codec_params->codec_inst.pacsize = kIsacPacSize960;
|
|
if (InternalInitEncoder(codec_params) < 0) {
|
|
return -1;
|
|
}
|
|
encoder_initialized_ = true;
|
|
}
|
|
|
|
return ACM_ISAC_DECODERINIT(codec_inst_ptr_->inst);
|
|
}
|
|
|
|
int16_t ACMISAC::InternalCreateEncoder() {
|
|
if (codec_inst_ptr_ == NULL) {
|
|
return -1;
|
|
}
|
|
decoder_initialized_ = false;
|
|
int16_t status = ACM_ISAC_CREATE(&(codec_inst_ptr_->inst));
|
|
|
|
if (status < 0)
|
|
codec_inst_ptr_->inst = NULL;
|
|
return status;
|
|
}
|
|
|
|
void ACMISAC::DestructEncoderSafe() {
|
|
// codec with shared instance cannot delete.
|
|
encoder_initialized_ = false;
|
|
return;
|
|
}
|
|
|
|
void ACMISAC::InternalDestructEncoderInst(void* ptr_inst) {
|
|
if (ptr_inst != NULL) {
|
|
ACM_ISAC_FREE(static_cast<ACM_ISAC_STRUCT *>(ptr_inst));
|
|
}
|
|
return;
|
|
}
|
|
|
|
int16_t ACMISAC::Transcode(uint8_t* bitstream,
|
|
int16_t* bitstream_len_byte,
|
|
int16_t q_bwe,
|
|
int32_t rate,
|
|
bool is_red) {
|
|
int16_t jitter_info = 0;
|
|
// transcode from a higher rate to lower rate sanity check
|
|
if (codec_inst_ptr_ == NULL) {
|
|
return -1;
|
|
}
|
|
|
|
*bitstream_len_byte = ACM_ISAC_GETNEWBITSTREAM(
|
|
codec_inst_ptr_->inst, q_bwe, jitter_info, rate,
|
|
reinterpret_cast<int16_t*>(bitstream), (is_red) ? 1 : 0);
|
|
|
|
if (*bitstream_len_byte < 0) {
|
|
// error happened
|
|
*bitstream_len_byte = 0;
|
|
return -1;
|
|
} else {
|
|
return *bitstream_len_byte;
|
|
}
|
|
}
|
|
|
|
int16_t ACMISAC::SetBitRateSafe(int32_t bit_rate) {
|
|
if (codec_inst_ptr_ == NULL) {
|
|
return -1;
|
|
}
|
|
uint16_t encoder_samp_freq;
|
|
EncoderSampFreq(&encoder_samp_freq);
|
|
bool reinit = false;
|
|
// change the BN of iSAC
|
|
if (bit_rate == -1) {
|
|
// ADAPTIVE MODE
|
|
// Check if it was already in adaptive mode
|
|
if (isac_coding_mode_ != ADAPTIVE) {
|
|
// was not in adaptive, then set the mode to adaptive
|
|
// and flag for re-initialization
|
|
isac_coding_mode_ = ADAPTIVE;
|
|
reinit = true;
|
|
}
|
|
} else if ((bit_rate >= ISAC_MIN_RATE) && (bit_rate <= ISAC_MAX_RATE)) {
|
|
// Sanity check if the rate valid
|
|
// check if it was in channel-independent mode before
|
|
if (isac_coding_mode_ != CHANNEL_INDEPENDENT) {
|
|
// was not in channel independent, set the mode to
|
|
// channel-independent and flag for re-initialization
|
|
isac_coding_mode_ = CHANNEL_INDEPENDENT;
|
|
reinit = true;
|
|
}
|
|
// store the bottleneck
|
|
isac_current_bn_ = (uint16_t)bit_rate;
|
|
} else {
|
|
// invlaid rate
|
|
return -1;
|
|
}
|
|
|
|
int16_t status = 0;
|
|
if (reinit) {
|
|
// initialize and check if it is successful
|
|
if (ACM_ISAC_ENCODERINIT(codec_inst_ptr_->inst, isac_coding_mode_) < 0) {
|
|
// failed initialization
|
|
return -1;
|
|
}
|
|
}
|
|
if (isac_coding_mode_ == CHANNEL_INDEPENDENT) {
|
|
status = ACM_ISAC_CONTROL(
|
|
codec_inst_ptr_->inst, isac_current_bn_,
|
|
(encoder_samp_freq == 32000 || encoder_samp_freq == 48000) ? 30 :
|
|
(frame_len_smpl_ / 16));
|
|
if (status < 0) {
|
|
status = -1;
|
|
}
|
|
}
|
|
|
|
// Update encoder parameters
|
|
encoder_params_.codec_inst.rate = bit_rate;
|
|
|
|
UpdateFrameLen();
|
|
return status;
|
|
}
|
|
|
|
int32_t ACMISAC::GetEstimatedBandwidthSafe() {
|
|
int16_t bandwidth_index = 0;
|
|
int16_t delay_index = 0;
|
|
int samp_rate;
|
|
|
|
// Get bandwidth information
|
|
ACM_ISAC_GETSENDBWE(codec_inst_ptr_->inst, &bandwidth_index, &delay_index);
|
|
|
|
// Validy check of index
|
|
if ((bandwidth_index < 0) || (bandwidth_index >= NR_ISAC_BANDWIDTHS)) {
|
|
return -1;
|
|
}
|
|
|
|
// Check sample frequency
|
|
samp_rate = ACM_ISAC_GETDECSAMPRATE(codec_inst_ptr_->inst);
|
|
if (samp_rate == 16000) {
|
|
return kIsacRatesWb[bandwidth_index];
|
|
} else {
|
|
return kIsacRatesSwb[bandwidth_index];
|
|
}
|
|
}
|
|
|
|
int32_t ACMISAC::SetEstimatedBandwidthSafe(int32_t estimated_bandwidth) {
|
|
int samp_rate;
|
|
int16_t bandwidth_index;
|
|
|
|
// Check sample frequency and choose appropriate table
|
|
samp_rate = ACM_ISAC_GETENCSAMPRATE(codec_inst_ptr_->inst);
|
|
|
|
if (samp_rate == 16000) {
|
|
// Search through the WB rate table to find the index
|
|
bandwidth_index = NR_ISAC_BANDWIDTHS / 2 - 1;
|
|
for (int i = 0; i < (NR_ISAC_BANDWIDTHS / 2); i++) {
|
|
if (estimated_bandwidth == kIsacRatesWb[i]) {
|
|
bandwidth_index = i;
|
|
break;
|
|
} else if (estimated_bandwidth
|
|
== kIsacRatesWb[i + NR_ISAC_BANDWIDTHS / 2]) {
|
|
bandwidth_index = i + NR_ISAC_BANDWIDTHS / 2;
|
|
break;
|
|
} else if (estimated_bandwidth < kIsacRatesWb[i]) {
|
|
bandwidth_index = i;
|
|
break;
|
|
}
|
|
}
|
|
} else {
|
|
// Search through the SWB rate table to find the index
|
|
bandwidth_index = NR_ISAC_BANDWIDTHS - 1;
|
|
for (int i = 0; i < NR_ISAC_BANDWIDTHS; i++) {
|
|
if (estimated_bandwidth <= kIsacRatesSwb[i]) {
|
|
bandwidth_index = i;
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
|
|
// Set iSAC Bandwidth Estimate
|
|
ACM_ISAC_SETBWE(codec_inst_ptr_->inst, bandwidth_index);
|
|
|
|
return 0;
|
|
}
|
|
|
|
int32_t ACMISAC::GetRedPayloadSafe(
|
|
#if (!defined(WEBRTC_CODEC_ISAC))
|
|
uint8_t* /* red_payload */,
|
|
int16_t* /* payload_bytes */) {
|
|
return -1;
|
|
#else
|
|
uint8_t* red_payload, int16_t* payload_bytes) {
|
|
int16_t bytes =
|
|
WebRtcIsac_GetRedPayload(
|
|
codec_inst_ptr_->inst, reinterpret_cast<int16_t*>(red_payload));
|
|
if (bytes < 0) {
|
|
return -1;
|
|
}
|
|
*payload_bytes = bytes;
|
|
return 0;
|
|
#endif
|
|
}
|
|
|
|
int16_t ACMISAC::UpdateDecoderSampFreq(
|
|
#ifdef WEBRTC_CODEC_ISAC
|
|
int16_t codec_id) {
|
|
// The decoder supports only wideband and super-wideband.
|
|
if (ACMCodecDB::kISAC == codec_id) {
|
|
return WebRtcIsac_SetDecSampRate(codec_inst_ptr_->inst, 16000);
|
|
} else if (ACMCodecDB::kISACSWB == codec_id ||
|
|
ACMCodecDB::kISACFB == codec_id) {
|
|
return WebRtcIsac_SetDecSampRate(codec_inst_ptr_->inst, 32000);
|
|
} else {
|
|
return -1;
|
|
}
|
|
#else
|
|
int16_t /* codec_id */) {
|
|
return 0;
|
|
#endif
|
|
}
|
|
|
|
int16_t ACMISAC::UpdateEncoderSampFreq(
|
|
#ifdef WEBRTC_CODEC_ISAC
|
|
uint16_t encoder_samp_freq_hz) {
|
|
uint16_t current_samp_rate_hz;
|
|
EncoderSampFreq(¤t_samp_rate_hz);
|
|
|
|
if (current_samp_rate_hz != encoder_samp_freq_hz) {
|
|
if ((encoder_samp_freq_hz != 16000) && (encoder_samp_freq_hz != 32000) &&
|
|
(encoder_samp_freq_hz != 48000)) {
|
|
return -1;
|
|
} else {
|
|
in_audio_ix_read_ = 0;
|
|
in_audio_ix_write_ = 0;
|
|
in_timestamp_ix_write_ = 0;
|
|
if (WebRtcIsac_SetEncSampRate(codec_inst_ptr_->inst,
|
|
encoder_samp_freq_hz) < 0) {
|
|
return -1;
|
|
}
|
|
samples_in_10ms_audio_ = encoder_samp_freq_hz / 100;
|
|
frame_len_smpl_ = ACM_ISAC_GETNEWFRAMELEN(codec_inst_ptr_->inst);
|
|
encoder_params_.codec_inst.pacsize = frame_len_smpl_;
|
|
encoder_params_.codec_inst.plfreq = encoder_samp_freq_hz;
|
|
return 0;
|
|
}
|
|
}
|
|
#else
|
|
uint16_t /* codec_id */) {
|
|
#endif
|
|
return 0;
|
|
}
|
|
|
|
int16_t ACMISAC::EncoderSampFreq(uint16_t* samp_freq_hz) {
|
|
*samp_freq_hz = ACM_ISAC_GETENCSAMPRATE(codec_inst_ptr_->inst);
|
|
return 0;
|
|
}
|
|
|
|
int32_t ACMISAC::ConfigISACBandwidthEstimator(
|
|
const uint8_t init_frame_size_msec,
|
|
const uint16_t init_rate_bit_per_sec,
|
|
const bool enforce_frame_size) {
|
|
int16_t status;
|
|
{
|
|
uint16_t samp_freq_hz;
|
|
EncoderSampFreq(&samp_freq_hz);
|
|
// TODO(turajs): at 32kHz we hardcode calling with 30ms and enforce
|
|
// the frame-size otherwise we might get error. Revise if
|
|
// control-bwe is changed.
|
|
if (samp_freq_hz == 32000 || samp_freq_hz == 48000) {
|
|
status = ACM_ISAC_CONTROL_BWE(codec_inst_ptr_->inst,
|
|
init_rate_bit_per_sec, 30, 1);
|
|
} else {
|
|
status = ACM_ISAC_CONTROL_BWE(codec_inst_ptr_->inst,
|
|
init_rate_bit_per_sec,
|
|
init_frame_size_msec,
|
|
enforce_frame_size ? 1 : 0);
|
|
}
|
|
}
|
|
if (status < 0) {
|
|
WEBRTC_TRACE(webrtc::kTraceError, webrtc::kTraceAudioCoding, unique_id_,
|
|
"Couldn't config iSAC BWE.");
|
|
return -1;
|
|
}
|
|
UpdateFrameLen();
|
|
ACM_ISAC_GETSENDBITRATE(codec_inst_ptr_->inst, &isac_current_bn_);
|
|
return 0;
|
|
}
|
|
|
|
int32_t ACMISAC::SetISACMaxPayloadSize(const uint16_t max_payload_len_bytes) {
|
|
return ACM_ISAC_SETMAXPAYLOADSIZE(codec_inst_ptr_->inst,
|
|
max_payload_len_bytes);
|
|
}
|
|
|
|
int32_t ACMISAC::SetISACMaxRate(const uint32_t max_rate_bit_per_sec) {
|
|
return ACM_ISAC_SETMAXRATE(codec_inst_ptr_->inst, max_rate_bit_per_sec);
|
|
}
|
|
|
|
void ACMISAC::UpdateFrameLen() {
|
|
frame_len_smpl_ = ACM_ISAC_GETNEWFRAMELEN(codec_inst_ptr_->inst);
|
|
encoder_params_.codec_inst.pacsize = frame_len_smpl_;
|
|
}
|
|
|
|
void ACMISAC::CurrentRate(int32_t* rate_bit_per_sec) {
|
|
if (isac_coding_mode_ == ADAPTIVE) {
|
|
ACM_ISAC_GETSENDBITRATE(codec_inst_ptr_->inst, rate_bit_per_sec);
|
|
}
|
|
}
|
|
|
|
int16_t ACMISAC::REDPayloadISAC(const int32_t isac_rate,
|
|
const int16_t isac_bw_estimate,
|
|
uint8_t* payload,
|
|
int16_t* payload_len_bytes) {
|
|
int16_t status;
|
|
ReadLockScoped rl(codec_wrapper_lock_);
|
|
status =
|
|
Transcode(payload, payload_len_bytes, isac_bw_estimate, isac_rate, true);
|
|
return status;
|
|
}
|
|
|
|
AudioDecoder* ACMISAC::Decoder(int codec_id) {
|
|
if (audio_decoder_)
|
|
return audio_decoder_;
|
|
|
|
// Create iSAC instance if it does not exist.
|
|
if (!encoder_exist_) {
|
|
assert(codec_inst_ptr_->inst == NULL);
|
|
encoder_initialized_ = false;
|
|
decoder_initialized_ = false;
|
|
if (ACM_ISAC_CREATE(&(codec_inst_ptr_->inst)) < 0) {
|
|
codec_inst_ptr_->inst = NULL;
|
|
return NULL;
|
|
}
|
|
encoder_exist_ = true;
|
|
}
|
|
|
|
WebRtcACMCodecParams codec_params;
|
|
if (!encoder_initialized_ || !decoder_initialized_) {
|
|
ACMCodecDB::Codec(codec_id, &codec_params.codec_inst);
|
|
// The following three values are not used but we set them to valid values.
|
|
codec_params.enable_dtx = false;
|
|
codec_params.enable_vad = false;
|
|
codec_params.vad_mode = VADNormal;
|
|
}
|
|
|
|
if (!encoder_initialized_) {
|
|
// Initialize encoder to make sure bandwidth estimator works.
|
|
if (InternalInitEncoder(&codec_params) < 0)
|
|
return NULL;
|
|
encoder_initialized_ = true;
|
|
}
|
|
|
|
if (!decoder_initialized_) {
|
|
if (InternalInitDecoder(&codec_params) < 0)
|
|
return NULL;
|
|
decoder_initialized_ = true;
|
|
}
|
|
|
|
audio_decoder_ = new AcmAudioDecoderIsac(codec_id, codec_inst_ptr_->inst);
|
|
return audio_decoder_;
|
|
}
|
|
|
|
#endif
|
|
|
|
} // namespace webrtc
|