diff --git a/webrtc/modules/audio_coding/main/acm2/acm_amr.cc b/webrtc/modules/audio_coding/main/acm2/acm_amr.cc new file mode 100644 index 0000000000..75430f1a86 --- /dev/null +++ b/webrtc/modules/audio_coding/main/acm2/acm_amr.cc @@ -0,0 +1,310 @@ +/* + * 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/source/acm_amr.h" + +#ifdef WEBRTC_CODEC_AMR +// NOTE! GSM AMR is not included in the open-source package. The following +// interface file is needed: +#include "webrtc/modules/audio_coding/main/codecs/amr/interface/amr_interface.h" +#include "webrtc/modules/audio_coding/main/interface/audio_coding_module_typedefs.h" +#include "webrtc/modules/audio_coding/main/source/acm_common_defs.h" +#include "webrtc/system_wrappers/interface/rw_lock_wrapper.h" +#include "webrtc/system_wrappers/interface/trace.h" + +// The API in the header file should match the one below. +// +// int16_t WebRtcAmr_CreateEnc(AMR_encinst_t_** enc_inst); +// int16_t WebRtcAmr_CreateDec(AMR_decinst_t_** dec_inst); +// int16_t WebRtcAmr_FreeEnc(AMR_encinst_t_* enc_inst); +// int16_t WebRtcAmr_FreeDec(AMR_decinst_t_* dec_inst); +// int16_t WebRtcAmr_Encode(AMR_encinst_t_* enc_inst, +// int16_t* input, +// int16_t len, +// int16_t*output, +// int16_t mode); +// int16_t WebRtcAmr_EncoderInit(AMR_encinst_t_* enc_inst, +// int16_t dtx_mode); +// int16_t WebRtcAmr_EncodeBitmode(AMR_encinst_t_* enc_inst, +// int format); +// int16_t WebRtcAmr_Decode(AMR_decinst_t_* dec_inst); +// int16_t WebRtcAmr_DecodePlc(AMR_decinst_t_* dec_inst); +// int16_t WebRtcAmr_DecoderInit(AMR_decinst_t_* dec_inst); +// int16_t WebRtcAmr_DecodeBitmode(AMR_decinst_t_* dec_inst, +// int format); +#endif + +namespace webrtc { + +#ifndef WEBRTC_CODEC_AMR +ACMAMR::ACMAMR(int16_t /* codec_id */) + : encoder_inst_ptr_(NULL), + encoding_mode_(-1), // Invalid value. + encoding_rate_(0), // Invalid value. + encoder_packing_format_(AMRBandwidthEfficient) { + return; +} + +ACMAMR::~ACMAMR() { return; } + +int16_t ACMAMR::InternalEncode(uint8_t* /* bitstream */, + int16_t* /* bitstream_len_byte */) { + return -1; +} + +int16_t ACMAMR::EnableDTX() { return -1; } + +int16_t ACMAMR::DisableDTX() { return -1; } + +int16_t ACMAMR::InternalInitEncoder(WebRtcACMCodecParams* /* codec_params */) { + return -1; +} + +ACMGenericCodec* ACMAMR::CreateInstance(void) { return NULL; } + +int16_t ACMAMR::InternalCreateEncoder() { return -1; } + +void ACMAMR::DestructEncoderSafe() { return; } + +int16_t ACMAMR::SetBitRateSafe(const int32_t /* rate */) { return -1; } + +void ACMAMR::InternalDestructEncoderInst(void* /* ptr_inst */) { return; } + +int16_t ACMAMR::SetAMREncoderPackingFormat( + ACMAMRPackingFormat /* packing_format */) { + return -1; +} + +ACMAMRPackingFormat ACMAMR::AMREncoderPackingFormat() const { + return AMRUndefined; +} + +int16_t ACMAMR::SetAMRDecoderPackingFormat( + ACMAMRPackingFormat /* packing_format */) { + return -1; +} + +ACMAMRPackingFormat ACMAMR::AMRDecoderPackingFormat() const { + return AMRUndefined; +} + +#else //===================== Actual Implementation ======================= + +#define WEBRTC_AMR_MR475 0 +#define WEBRTC_AMR_MR515 1 +#define WEBRTC_AMR_MR59 2 +#define WEBRTC_AMR_MR67 3 +#define WEBRTC_AMR_MR74 4 +#define WEBRTC_AMR_MR795 5 +#define WEBRTC_AMR_MR102 6 +#define WEBRTC_AMR_MR122 7 + +ACMAMR::ACMAMR(int16_t codec_id) + : encoder_inst_ptr_(NULL), + encoding_mode_(-1), // invalid value + encoding_rate_(0) { // invalid value + codec_id_ = codec_id; + has_internal_dtx_ = true; + encoder_packing_format_ = AMRBandwidthEfficient; + return; +} + +ACMAMR::~ACMAMR() { + if (encoder_inst_ptr_ != NULL) { + WebRtcAmr_FreeEnc(encoder_inst_ptr_); + encoder_inst_ptr_ = NULL; + } + return; +} + +int16_t ACMAMR::InternalEncode(uint8_t* bitstream, + int16_t* bitstream_len_byte) { + int16_t vad_decision = 1; + // sanity check, if the rate is set correctly. we might skip this + // sanity check. if rate is not set correctly, initialization flag + // should be false and should not be here. + if ((encoding_mode_ < WEBRTC_AMR_MR475) || + (encoding_mode_ > WEBRTC_AMR_MR122)) { + *bitstream_len_byte = 0; + return -1; + } + *bitstream_len_byte = WebRtcAmr_Encode(encoder_inst_ptr_, + &in_audio_[in_audio_ix_read_], + frame_len_smpl_, + reinterpret_cast(bitstream), + encoding_mode_); + + // Update VAD, if internal DTX is used + if (has_internal_dtx_ && dtx_enabled_) { + if (*bitstream_len_byte <= (7 * frame_len_smpl_ / 160)) { + vad_decision = 0; + } + for (int16_t n = 0; n < MAX_FRAME_SIZE_10MSEC; n++) { + vad_label_[n] = vad_decision; + } + } + // increment the read index + in_audio_ix_read_ += frame_len_smpl_; + return *bitstream_len_byte; +} + +int16_t ACMAMR::EnableDTX() { + if (dtx_enabled_) { + return 0; + } else if (encoder_exist_) { // check if encoder exist + // enable DTX + if (WebRtcAmr_EncoderInit(encoder_inst_ptr_, 1) < 0) { + return -1; + } + dtx_enabled_ = true; + return 0; + } else { + return -1; + } +} + +int16_t ACMAMR::DisableDTX() { + if (!dtx_enabled_) { + return 0; + } else if (encoder_exist_) { // check if encoder exist + // disable DTX + if (WebRtcAmr_EncoderInit(encoder_inst_ptr_, 0) < 0) { + return -1; + } + dtx_enabled_ = false; + return 0; + } else { + // encoder doesn't exists, therefore disabling is harmless + return 0; + } +} + +int16_t ACMAMR::InternalInitEncoder(WebRtcACMCodecParams* codec_params) { + int16_t status = SetBitRateSafe((codec_params->codec_inst).rate); + status += (WebRtcAmr_EncoderInit(encoder_inst_ptr_, + ((codec_params->enable_dtx) ? 1 : 0)) < 0) + ? -1 + : 0; + status += + (WebRtcAmr_EncodeBitmode(encoder_inst_ptr_, encoder_packing_format_) < 0) + ? -1 + : 0; + return (status < 0) ? -1 : 0; +} + +ACMGenericCodec* ACMAMR::CreateInstance(void) { return NULL; } + +int16_t ACMAMR::InternalCreateEncoder() { + return WebRtcAmr_CreateEnc(&encoder_inst_ptr_); +} + +void ACMAMR::DestructEncoderSafe() { + if (encoder_inst_ptr_ != NULL) { + WebRtcAmr_FreeEnc(encoder_inst_ptr_); + encoder_inst_ptr_ = NULL; + } + // there is no encoder set the following + encoder_exist_ = false; + encoder_initialized_ = false; + encoding_mode_ = -1; // invalid value + encoding_rate_ = 0; // invalid value +} + +int16_t ACMAMR::SetBitRateSafe(const int32_t rate) { + switch (rate) { + case 4750: { + encoding_mode_ = WEBRTC_AMR_MR475; + encoding_rate_ = 4750; + break; + } + case 5150: { + encoding_mode_ = WEBRTC_AMR_MR515; + encoding_rate_ = 5150; + break; + } + case 5900: { + encoding_mode_ = WEBRTC_AMR_MR59; + encoding_rate_ = 5900; + break; + } + case 6700: { + encoding_mode_ = WEBRTC_AMR_MR67; + encoding_rate_ = 6700; + break; + } + case 7400: { + encoding_mode_ = WEBRTC_AMR_MR74; + encoding_rate_ = 7400; + break; + } + case 7950: { + encoding_mode_ = WEBRTC_AMR_MR795; + encoding_rate_ = 7950; + break; + } + case 10200: { + encoding_mode_ = WEBRTC_AMR_MR102; + encoding_rate_ = 10200; + break; + } + case 12200: { + encoding_mode_ = WEBRTC_AMR_MR122; + encoding_rate_ = 12200; + break; + } + default: { + return -1; + } + } + return 0; +} + +void ACMAMR::InternalDestructEncoderInst(void* ptr_inst) { + // Free the memory where ptr_inst is pointing to + if (ptr_inst != NULL) { + WebRtcAmr_FreeEnc(static_cast(ptr_inst)); + } + return; +} + +int16_t ACMAMR::SetAMREncoderPackingFormat(ACMAMRPackingFormat packing_format) { + if ((packing_format != AMRBandwidthEfficient) && + (packing_format != AMROctetAlligned) && + (packing_format != AMRFileStorage)) { + WEBRTC_TRACE(webrtc::kTraceError, webrtc::kTraceAudioCoding, unique_id_, + "Invalid AMR Encoder packing-format."); + return -1; + } else { + if (WebRtcAmr_EncodeBitmode(encoder_inst_ptr_, packing_format) < 0) { + return -1; + } else { + encoder_packing_format_ = packing_format; + return 0; + } + } +} + +ACMAMRPackingFormat ACMAMR::AMREncoderPackingFormat() const { + return encoder_packing_format_; +} + +int16_t ACMAMR::SetAMRDecoderPackingFormat( + ACMAMRPackingFormat /* packing_format */) { + // Not implemented. + return -1; +} + +ACMAMRPackingFormat ACMAMR::AMRDecoderPackingFormat() const { + // Not implemented. + return AMRUndefined; +} + +#endif +} // namespace webrtc diff --git a/webrtc/modules/audio_coding/main/acm2/acm_amr.h b/webrtc/modules/audio_coding/main/acm2/acm_amr.h new file mode 100644 index 0000000000..c58b5111f6 --- /dev/null +++ b/webrtc/modules/audio_coding/main/acm2/acm_amr.h @@ -0,0 +1,65 @@ +/* + * 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. + */ + +#ifndef WEBRTC_MODULES_AUDIO_CODING_MAIN_SOURCE_ACM_AMR_H_ +#define WEBRTC_MODULES_AUDIO_CODING_MAIN_SOURCE_ACM_AMR_H_ + +#include "webrtc/modules/audio_coding/main/source/acm_generic_codec.h" + +// forward declaration +struct AMR_encinst_t_; +struct AMR_decinst_t_; + +namespace webrtc { + +enum ACMAMRPackingFormat; + +class ACMAMR : public ACMGenericCodec { + public: + explicit ACMAMR(int16_t codec_id); + ~ACMAMR(); + + // for FEC + ACMGenericCodec* CreateInstance(void); + + int16_t InternalEncode(uint8_t* bitstream, int16_t* bitstream_len_byte); + + int16_t InternalInitEncoder(WebRtcACMCodecParams* codec_params); + + int16_t SetAMREncoderPackingFormat(const ACMAMRPackingFormat packing_format); + + ACMAMRPackingFormat AMREncoderPackingFormat() const; + + int16_t SetAMRDecoderPackingFormat(const ACMAMRPackingFormat packing_format); + + ACMAMRPackingFormat AMRDecoderPackingFormat() const; + + protected: + void DestructEncoderSafe(); + + int16_t InternalCreateEncoder(); + + void InternalDestructEncoderInst(void* ptr_inst); + + int16_t SetBitRateSafe(const int32_t rate); + + int16_t EnableDTX(); + + int16_t DisableDTX(); + + AMR_encinst_t_* encoder_inst_ptr_; + int16_t encoding_mode_; + int16_t encoding_rate_; + ACMAMRPackingFormat encoder_packing_format_; +}; + +} // namespace webrtc + +#endif // WEBRTC_MODULES_AUDIO_CODING_MAIN_SOURCE_ACM_AMR_H_ diff --git a/webrtc/modules/audio_coding/main/acm2/acm_amrwb.cc b/webrtc/modules/audio_coding/main/acm2/acm_amrwb.cc new file mode 100644 index 0000000000..1b82674b1d --- /dev/null +++ b/webrtc/modules/audio_coding/main/acm2/acm_amrwb.cc @@ -0,0 +1,316 @@ +/* + * 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/source/acm_amrwb.h" + +#ifdef WEBRTC_CODEC_AMRWB +// NOTE! GSM AMR-wb is not included in the open-source package. The +// following interface file is needed: +#include "webrtc/modules/audio_coding/main/codecs/amrwb/interface/amrwb_interface.h" +#include "webrtc/modules/audio_coding/main/interface/audio_coding_module_typedefs.h" +#include "webrtc/modules/audio_coding/main/source/acm_common_defs.h" +#include "webrtc/system_wrappers/interface/rw_lock_wrapper.h" +#include "webrtc/system_wrappers/interface/trace.h" + +// The API in the header file should match the one below. +// +// int16_t WebRtcAmrWb_CreateEnc(AMRWB_encinst_t_** enc_inst); +// int16_t WebRtcAmrWb_CreateDec(AMRWB_decinst_t_** dec_inst); +// int16_t WebRtcAmrWb_FreeEnc(AMRWB_encinst_t_* enc_inst); +// int16_t WebRtcAmrWb_FreeDec(AMRWB_decinst_t_* dec_inst); +// int16_t WebRtcAmrWb_Encode(AMRWB_encinst_t_* enc_inst, int16_t* input, +// int16_t len, int16_t* output, int16_t mode); +// int16_t WebRtcAmrWb_EncoderInit(AMRWB_encinst_t_* enc_inst, +// int16_t dtx_mode); +// int16_t WebRtcAmrWb_EncodeBitmode(AMRWB_encinst_t_* enc_inst, +// int format); +// int16_t WebRtcAmrWb_Decode(AMRWB_decinst_t_* dec_inst); +// int16_t WebRtcAmrWb_DecodePlc(AMRWB_decinst_t_* dec_inst); +// int16_t WebRtcAmrWb_DecoderInit(AMRWB_decinst_t_* dec_inst); +// int16_t WebRtcAmrWb_DecodeBitmode(AMRWB_decinst_t_* dec_inst, +// int format); +#endif + +namespace webrtc { + +#ifndef WEBRTC_CODEC_AMRWB +ACMAMRwb::ACMAMRwb(int16_t /* codec_id */) + : encoder_inst_ptr_(NULL), + encoding_mode_(-1), // invalid value + encoding_rate_(0), // invalid value + encoder_packing_format_(AMRBandwidthEfficient) {} + +ACMAMRwb::~ACMAMRwb() {} + +int16_t ACMAMRwb::InternalEncode(uint8_t* /* bitstream */, + int16_t* /* bitstream_len_byte */) { + return -1; +} + +int16_t ACMAMRwb::EnableDTX() { return -1; } + +int16_t ACMAMRwb::DisableDTX() { return -1; } + +int16_t ACMAMRwb::InternalInitEncoder( + WebRtcACMCodecParams* /* codec_params */) { + return -1; +} + +ACMGenericCodec* ACMAMRwb::CreateInstance(void) { return NULL; } + +int16_t ACMAMRwb::InternalCreateEncoder() { return -1; } + +void ACMAMRwb::DestructEncoderSafe() { return; } + +int16_t ACMAMRwb::SetBitRateSafe(const int32_t /* rate */) { return -1; } + +void ACMAMRwb::InternalDestructEncoderInst(void* /* ptr_inst */) { return; } + +int16_t ACMAMRwb::SetAMRwbEncoderPackingFormat( + ACMAMRPackingFormat /* packing_format */) { + return -1; +} + +ACMAMRPackingFormat ACMAMRwb::AMRwbEncoderPackingFormat() const { + return AMRUndefined; +} + +int16_t ACMAMRwb::SetAMRwbDecoderPackingFormat( + ACMAMRPackingFormat /* packing_format */) { + return -1; +} + +ACMAMRPackingFormat ACMAMRwb::AMRwbDecoderPackingFormat() const { + return AMRUndefined; +} + +#else //===================== Actual Implementation ======================= + +#define AMRWB_MODE_7k 0 +#define AMRWB_MODE_9k 1 +#define AMRWB_MODE_12k 2 +#define AMRWB_MODE_14k 3 +#define AMRWB_MODE_16k 4 +#define AMRWB_MODE_18k 5 +#define AMRWB_MODE_20k 6 +#define AMRWB_MODE_23k 7 +#define AMRWB_MODE_24k 8 + +ACMAMRwb::ACMAMRwb(int16_t codec_id) + : encoder_inst_ptr_(NULL), + encoding_mode_(-1), // invalid value + encoding_rate_(0) { // invalid value + codec_id_ = codec_id; + has_internal_dtx_ = true; + encoder_packing_format_ = AMRBandwidthEfficient; + return; +} + +ACMAMRwb::~ACMAMRwb() { + if (encoder_inst_ptr_ != NULL) { + WebRtcAmrWb_FreeEnc(encoder_inst_ptr_); + encoder_inst_ptr_ = NULL; + } + return; +} + +int16_t ACMAMRwb::InternalEncode(uint8_t* bitstream, + int16_t* bitstream_len_byte) { + int16_t vad_decision = 1; + // sanity check, if the rate is set correctly. we might skip this + // sanity check. if rate is not set correctly, initialization flag + // should be false and should not be here. + if ((encoding_mode_ < AMRWB_MODE_7k) || (encoding_mode_ > AMRWB_MODE_24k)) { + *bitstream_len_byte = 0; + return -1; + } + *bitstream_len_byte = WebRtcAmrWb_Encode( + encoder_inst_ptr_, &in_audio_[in_audio_ix_read_], frame_len_smpl_, + reinterpret_cast(bitstream), encoding_mode_); + + // Update VAD, if internal DTX is used + if (has_internal_dtx_ && dtx_enabled_) { + if (*bitstream_len_byte <= (7 * frame_len_smpl_ / 160)) { + vad_decision = 0; + } + for (int16_t n = 0; n < MAX_FRAME_SIZE_10MSEC; n++) { + vad_label_[n] = vad_decision; + } + } + // increment the read index this tell the caller that how far + // we have gone forward in reading the audio buffer + in_audio_ix_read_ += frame_len_smpl_; + return *bitstream_len_byte; +} + +int16_t ACMAMRwb::EnableDTX() { + if (dtx_enabled_) { + return 0; + } else if (encoder_exist_) { // check if encoder exist + // enable DTX + if (WebRtcAmrWb_EncoderInit(encoder_inst_ptr_, 1) < 0) { + return -1; + } + dtx_enabled_ = true; + return 0; + } else { + return -1; + } +} + +int16_t ACMAMRwb::DisableDTX() { + if (!dtx_enabled_) { + return 0; + } else if (encoder_exist_) { // check if encoder exist + // disable DTX + if (WebRtcAmrWb_EncoderInit(encoder_inst_ptr_, 0) < 0) { + return -1; + } + dtx_enabled_ = false; + return 0; + } else { + // encoder doesn't exists, therefore disabling is harmless + return 0; + } +} + +int16_t ACMAMRwb::InternalInitEncoder(WebRtcACMCodecParams* codec_params) { + // sanity check + if (encoder_inst_ptr_ == NULL) { + return -1; + } + + int16_t status = SetBitRateSafe((codec_params->codec_inst).rate); + status += (WebRtcAmrWb_EncoderInit(encoder_inst_ptr_, + ((codec_params->enable_dtx) ? 1 : 0)) < 0) + ? -1 + : 0; + status += (WebRtcAmrWb_EncodeBitmode(encoder_inst_ptr_, + encoder_packing_format_) < 0) + ? -1 + : 0; + return (status < 0) ? -1 : 0; +} + +ACMGenericCodec* ACMAMRwb::CreateInstance(void) { return NULL; } + +int16_t ACMAMRwb::InternalCreateEncoder() { + return WebRtcAmrWb_CreateEnc(&encoder_inst_ptr_); +} + +void ACMAMRwb::DestructEncoderSafe() { + if (encoder_inst_ptr_ != NULL) { + WebRtcAmrWb_FreeEnc(encoder_inst_ptr_); + encoder_inst_ptr_ = NULL; + } + // there is no encoder set the following + encoder_exist_ = false; + encoder_initialized_ = false; + encoding_mode_ = -1; // invalid value + encoding_rate_ = 0; +} + +int16_t ACMAMRwb::SetBitRateSafe(const int32_t rate) { + switch (rate) { + case 7000: { + encoding_mode_ = AMRWB_MODE_7k; + encoding_rate_ = 7000; + break; + } + case 9000: { + encoding_mode_ = AMRWB_MODE_9k; + encoding_rate_ = 9000; + break; + } + case 12000: { + encoding_mode_ = AMRWB_MODE_12k; + encoding_rate_ = 12000; + break; + } + case 14000: { + encoding_mode_ = AMRWB_MODE_14k; + encoding_rate_ = 14000; + break; + } + case 16000: { + encoding_mode_ = AMRWB_MODE_16k; + encoding_rate_ = 16000; + break; + } + case 18000: { + encoding_mode_ = AMRWB_MODE_18k; + encoding_rate_ = 18000; + break; + } + case 20000: { + encoding_mode_ = AMRWB_MODE_20k; + encoding_rate_ = 20000; + break; + } + case 23000: { + encoding_mode_ = AMRWB_MODE_23k; + encoding_rate_ = 23000; + break; + } + case 24000: { + encoding_mode_ = AMRWB_MODE_24k; + encoding_rate_ = 24000; + break; + } + default: { + return -1; + } + } + return 0; +} + +void ACMAMRwb::InternalDestructEncoderInst(void* ptr_inst) { + if (ptr_inst != NULL) { + WebRtcAmrWb_FreeEnc(static_cast(ptr_inst)); + } + return; +} + +int16_t ACMAMRwb::SetAMRwbEncoderPackingFormat( + ACMAMRPackingFormat packing_format) { + if ((packing_format != AMRBandwidthEfficient) && + (packing_format != AMROctetAlligned) && + (packing_format != AMRFileStorage)) { + WEBRTC_TRACE(webrtc::kTraceError, webrtc::kTraceAudioCoding, unique_id_, + "Invalid AMRwb encoder packing-format."); + return -1; + } else { + if (WebRtcAmrWb_EncodeBitmode(encoder_inst_ptr_, packing_format) < 0) { + return -1; + } else { + encoder_packing_format_ = packing_format; + return 0; + } + } +} + +ACMAMRPackingFormat ACMAMRwb::AMRwbEncoderPackingFormat() const { + return encoder_packing_format_; +} + +int16_t ACMAMRwb::SetAMRwbDecoderPackingFormat( + ACMAMRPackingFormat packing_format) { + // Not implemented. + return -1; +} + +ACMAMRPackingFormat ACMAMRwb::AMRwbDecoderPackingFormat() const { + // Not implemented. + return AMRUndefined; +} + +#endif + +} // namespace webrtc diff --git a/webrtc/modules/audio_coding/main/acm2/acm_amrwb.h b/webrtc/modules/audio_coding/main/acm2/acm_amrwb.h new file mode 100644 index 0000000000..550bab2d32 --- /dev/null +++ b/webrtc/modules/audio_coding/main/acm2/acm_amrwb.h @@ -0,0 +1,66 @@ +/* + * 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. + */ + +#ifndef WEBRTC_MODULES_AUDIO_CODING_MAIN_SOURCE_ACM_AMRWB_H_ +#define WEBRTC_MODULES_AUDIO_CODING_MAIN_SOURCE_ACM_AMRWB_H_ + +#include "webrtc/modules/audio_coding/main/source/acm_generic_codec.h" + +// forward declaration +struct AMRWB_encinst_t_; +struct AMRWB_decinst_t_; + +namespace webrtc { + +class ACMAMRwb : public ACMGenericCodec { + public: + explicit ACMAMRwb(int16_t codec_id); + ~ACMAMRwb(); + + // for FEC + ACMGenericCodec* CreateInstance(void); + + int16_t InternalEncode(uint8_t* bitstream, int16_t* bitstream_len_byte); + + int16_t InternalInitEncoder(WebRtcACMCodecParams* codec_params); + + int16_t SetAMRwbEncoderPackingFormat( + const ACMAMRPackingFormat packing_format); + + ACMAMRPackingFormat AMRwbEncoderPackingFormat() const; + + int16_t SetAMRwbDecoderPackingFormat( + const ACMAMRPackingFormat packing_format); + + ACMAMRPackingFormat AMRwbDecoderPackingFormat() const; + + protected: + void DestructEncoderSafe(); + + int16_t InternalCreateEncoder(); + + void InternalDestructEncoderInst(void* ptr_inst); + + int16_t SetBitRateSafe(const int32_t rate); + + int16_t EnableDTX(); + + int16_t DisableDTX(); + + AMRWB_encinst_t_* encoder_inst_ptr_; + + int16_t encoding_mode_; + int16_t encoding_rate_; + ACMAMRPackingFormat encoder_packing_format_; +}; + +} // namespace webrtc + +#endif // WEBRTC_MODULES_AUDIO_CODING_MAIN_SOURCE_ACM_AMRWB_H_ diff --git a/webrtc/modules/audio_coding/main/acm2/acm_celt.cc b/webrtc/modules/audio_coding/main/acm2/acm_celt.cc new file mode 100644 index 0000000000..6f2c807e1b --- /dev/null +++ b/webrtc/modules/audio_coding/main/acm2/acm_celt.cc @@ -0,0 +1,191 @@ +/* + * 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/source/acm_celt.h" + +#ifdef WEBRTC_CODEC_CELT +// NOTE! Celt is not included in the open-source package. Modify this file or +// your codec API to match the function call and name of used CELT API file. +#include "webrtc/modules/audio_coding/codecs/celt/include/celt_interface.h" +#include "webrtc/modules/audio_coding/main/source/acm_common_defs.h" +#include "webrtc/system_wrappers/interface/trace.h" +#endif + +namespace webrtc { + +#ifndef WEBRTC_CODEC_CELT + +ACMCELT::ACMCELT(int16_t /* codec_id */) + : enc_inst_ptr_(NULL), + sampling_freq_(0), + bitrate_(0), + channels_(1) { + return; +} + +ACMCELT::~ACMCELT() { + return; +} + +int16_t ACMCELT::InternalEncode(uint8_t* /* bitstream */, + int16_t* /* bitstream_len_byte */) { + return -1; +} + +int16_t ACMCELT::InternalInitEncoder(WebRtcACMCodecParams* /* codec_params */) { + return -1; +} + +ACMGenericCodec* ACMCELT::CreateInstance(void) { + return NULL; +} + +int16_t ACMCELT::InternalCreateEncoder() { + return -1; +} + +void ACMCELT::DestructEncoderSafe() { + return; +} + +void ACMCELT::InternalDestructEncoderInst(void* /* ptr_inst */) { + return; +} + +int16_t ACMCELT::SetBitRateSafe(const int32_t /*rate*/) { + return -1; +} + +#else //===================== Actual Implementation ======================= + +ACMCELT::ACMCELT(int16_t codec_id) + : enc_inst_ptr_(NULL), + sampling_freq_(32000), // Default sampling frequency. + bitrate_(64000), // Default rate. + channels_(1) { // Default send mono. + // TODO(tlegrand): remove later when ACMGenericCodec has a new constructor. + codec_id_ = codec_id; + + return; +} + +ACMCELT::~ACMCELT() { + if (enc_inst_ptr_ != NULL) { + WebRtcCelt_FreeEnc(enc_inst_ptr_); + enc_inst_ptr_ = NULL; + } + return; +} + +int16_t ACMCELT::InternalEncode(uint8_t* bitstream, + int16_t* bitstream_len_byte) { + *bitstream_len_byte = 0; + + // Call Encoder. + *bitstream_len_byte = WebRtcCelt_Encode(enc_inst_ptr_, + &in_audio_[in_audio_ix_read_], + 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_ += frame_len_smpl_ * channels_; + + if (*bitstream_len_byte < 0) { + // Error reported from the encoder. + WEBRTC_TRACE(webrtc::kTraceError, webrtc::kTraceAudioCoding, unique_id_, + "InternalEncode: Encode error for Celt"); + *bitstream_len_byte = 0; + return -1; + } + + return *bitstream_len_byte; +} + +int16_t ACMCELT::InternalInitEncoder(WebRtcACMCodecParams* codec_params) { + // Set bitrate and check that it is within the valid range. + int16_t status = SetBitRateSafe((codec_params->codec_inst).rate); + if (status < 0) { + return -1; + } + + // If number of channels changed we need to re-create memory. + if (codec_params->codec_inst.channels != channels_) { + WebRtcCelt_FreeEnc(enc_inst_ptr_); + enc_inst_ptr_ = NULL; + // Store new number of channels. + channels_ = codec_params->codec_inst.channels; + if (WebRtcCelt_CreateEnc(&enc_inst_ptr_, channels_) < 0) { + return -1; + } + } + + // Initiate encoder. + if (WebRtcCelt_EncoderInit(enc_inst_ptr_, channels_, bitrate_) >= 0) { + return 0; + } else { + return -1; + } +} + +ACMGenericCodec* ACMCELT::CreateInstance(void) { + return NULL; +} + +int16_t ACMCELT::InternalCreateEncoder() { + if (WebRtcCelt_CreateEnc(&enc_inst_ptr_, num_channels_) < 0) { + WEBRTC_TRACE(webrtc::kTraceError, webrtc::kTraceAudioCoding, unique_id_, + "InternalCreateEncoder: create encoder failed for Celt"); + return -1; + } + channels_ = num_channels_; + return 0; +} + +void ACMCELT::DestructEncoderSafe() { + encoder_exist_ = false; + encoder_initialized_ = false; + if (enc_inst_ptr_ != NULL) { + WebRtcCelt_FreeEnc(enc_inst_ptr_); + enc_inst_ptr_ = NULL; + } +} + +void ACMCELT::InternalDestructEncoderInst(void* ptr_inst) { + if (ptr_inst != NULL) { + WebRtcCelt_FreeEnc(static_cast(ptr_inst)); + } + return; +} + +int16_t ACMCELT::SetBitRateSafe(const int32_t rate) { + // Check that rate is in the valid range. + if ((rate >= 48000) && (rate <= 128000)) { + // Store new rate. + bitrate_ = rate; + + // Initiate encoder with new rate. + if (WebRtcCelt_EncoderInit(enc_inst_ptr_, channels_, bitrate_) >= 0) { + return 0; + } else { + WEBRTC_TRACE(webrtc::kTraceError, webrtc::kTraceAudioCoding, unique_id_, + "SetBitRateSafe: Failed to initiate Celt with rate %d", + rate); + return -1; + } + } else { + WEBRTC_TRACE(webrtc::kTraceError, webrtc::kTraceAudioCoding, unique_id_, + "SetBitRateSafe: Invalid rate Celt, %d", rate); + return -1; + } +} + +#endif + +} // namespace webrtc diff --git a/webrtc/modules/audio_coding/main/acm2/acm_celt.h b/webrtc/modules/audio_coding/main/acm2/acm_celt.h new file mode 100644 index 0000000000..b90a4e8500 --- /dev/null +++ b/webrtc/modules/audio_coding/main/acm2/acm_celt.h @@ -0,0 +1,50 @@ +/* + * 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. + */ + +#ifndef WEBRTC_MODULES_AUDIO_CODING_MAIN_SOURCE_ACM_CELT_H_ +#define WEBRTC_MODULES_AUDIO_CODING_MAIN_SOURCE_ACM_CELT_H_ + +#include "webrtc/modules/audio_coding/main/source/acm_generic_codec.h" + +// forward declaration +struct CELT_encinst_t_; +struct CELT_decinst_t_; + +namespace webrtc { + +class ACMCELT : public ACMGenericCodec { + public: + explicit ACMCELT(int16_t codec_id); + ~ACMCELT(); + + ACMGenericCodec* CreateInstance(void); + + int16_t InternalEncode(uint8_t* bitstream, int16_t* bitstream_len_byte); + + int16_t InternalInitEncoder(WebRtcACMCodecParams *codec_params); + + protected: + void DestructEncoderSafe(); + + int16_t InternalCreateEncoder(); + + void InternalDestructEncoderInst(void* ptr_inst); + + int16_t SetBitRateSafe(const int32_t rate); + + CELT_encinst_t_* enc_inst_ptr_; + uint16_t sampling_freq_; + int32_t bitrate_; + uint16_t channels_; +}; + +} // namespace webrtc + +#endif // WEBRTC_MODULES_AUDIO_CODING_MAIN_SOURCE_ACM_CELT_H_ diff --git a/webrtc/modules/audio_coding/main/acm2/acm_cng.cc b/webrtc/modules/audio_coding/main/acm2/acm_cng.cc new file mode 100644 index 0000000000..b04fd6ad11 --- /dev/null +++ b/webrtc/modules/audio_coding/main/acm2/acm_cng.cc @@ -0,0 +1,79 @@ +/* + * 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/source/acm_cng.h" + +#include "webrtc/modules/audio_coding/codecs/cng/include/webrtc_cng.h" +#include "webrtc/modules/audio_coding/main/source/acm_codec_database.h" +#include "webrtc/modules/audio_coding/main/source/acm_common_defs.h" +#include "webrtc/system_wrappers/interface/trace.h" + +namespace webrtc { + +ACMCNG::ACMCNG(int16_t codec_id) { + encoder_inst_ptr_ = NULL; + codec_id_ = codec_id; + samp_freq_hz_ = ACMCodecDB::CodecFreq(codec_id_); + return; +} + +ACMCNG::~ACMCNG() { + if (encoder_inst_ptr_ != NULL) { + WebRtcCng_FreeEnc(encoder_inst_ptr_); + encoder_inst_ptr_ = NULL; + } + return; +} + +// CNG is not like a regular encoder, this function +// should not be called normally +// instead the following function is called from inside +// ACMGenericCodec::ProcessFrameVADDTX +int16_t ACMCNG::InternalEncode(uint8_t* /* bitstream */, + int16_t* /* bitstream_len_byte */) { + return -1; +} + +// CNG is not like a regular encoder, +// this function should not be called normally +// instead the following function is called from inside +// ACMGenericCodec::ProcessFrameVADDTX +int16_t ACMCNG::InternalInitEncoder(WebRtcACMCodecParams* /* codec_params */) { + return -1; +} + +ACMGenericCodec* ACMCNG::CreateInstance(void) { return NULL; } + +int16_t ACMCNG::InternalCreateEncoder() { + if (WebRtcCng_CreateEnc(&encoder_inst_ptr_) < 0) { + encoder_inst_ptr_ = NULL; + return -1; + } else { + return 0; + } +} + +void ACMCNG::DestructEncoderSafe() { + if (encoder_inst_ptr_ != NULL) { + WebRtcCng_FreeEnc(encoder_inst_ptr_); + encoder_inst_ptr_ = NULL; + } + encoder_exist_ = false; + encoder_initialized_ = false; +} + +void ACMCNG::InternalDestructEncoderInst(void* ptr_inst) { + if (ptr_inst != NULL) { + WebRtcCng_FreeEnc(static_cast(ptr_inst)); + } + return; +} + +} // namespace webrtc diff --git a/webrtc/modules/audio_coding/main/acm2/acm_cng.h b/webrtc/modules/audio_coding/main/acm2/acm_cng.h new file mode 100644 index 0000000000..2ea4f02db7 --- /dev/null +++ b/webrtc/modules/audio_coding/main/acm2/acm_cng.h @@ -0,0 +1,56 @@ +/* + * 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. + */ + +#ifndef WEBRTC_MODULES_AUDIO_CODING_MAIN_SOURCE_ACM_CNG_H_ +#define WEBRTC_MODULES_AUDIO_CODING_MAIN_SOURCE_ACM_CNG_H_ + +#include "webrtc/modules/audio_coding/main/source/acm_generic_codec.h" + +// forward declaration +struct WebRtcCngEncInst; +struct WebRtcCngDecInst; + +namespace webrtc { + +class ACMCNG: public ACMGenericCodec { + public: + explicit ACMCNG(int16_t codec_id); + ~ACMCNG(); + + // for FEC + ACMGenericCodec* CreateInstance(void); + + int16_t InternalEncode(uint8_t* bitstream, + int16_t* bitstream_len_byte); + + int16_t InternalInitEncoder(WebRtcACMCodecParams *codec_params); + + protected: + void DestructEncoderSafe(); + + int16_t InternalCreateEncoder(); + + void InternalDestructEncoderInst(void* ptr_inst); + + int16_t EnableDTX() { + return -1; + } + + int16_t DisableDTX() { + return -1; + } + + WebRtcCngEncInst* encoder_inst_ptr_; + uint16_t samp_freq_hz_; +}; + +} // namespace webrtc + +#endif // WEBRTC_MODULES_AUDIO_CODING_MAIN_SOURCE_ACM_CNG_H_ diff --git a/webrtc/modules/audio_coding/main/acm2/acm_codec_database.cc b/webrtc/modules/audio_coding/main/acm2/acm_codec_database.cc new file mode 100644 index 0000000000..08080d1dcb --- /dev/null +++ b/webrtc/modules/audio_coding/main/acm2/acm_codec_database.cc @@ -0,0 +1,964 @@ +/* + * 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. + */ + +/* + * This file generates databases with information about all supported audio + * codecs. + */ + +// TODO(tlegrand): Change constant input pointers in all functions to constant +// references, where appropriate. +#include "webrtc/modules/audio_coding/main/source/acm_codec_database.h" + +#include + +#include "webrtc/modules/audio_coding/main/source/acm_common_defs.h" +#include "webrtc/modules/audio_coding/neteq4/interface/audio_decoder.h" +#include "webrtc/system_wrappers/interface/trace.h" + +// Includes needed to create the codecs. +// G711, PCM mu-law and A-law +#include "webrtc/modules/audio_coding/main/source/acm_pcma.h" +#include "webrtc/modules/audio_coding/main/source/acm_pcmu.h" +#include "webrtc/modules/audio_coding/codecs/g711/include/g711_interface.h" +// CNG +#include "webrtc/modules/audio_coding/codecs/cng/include/webrtc_cng.h" +#include "webrtc/modules/audio_coding/main/source/acm_cng.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_ISACFX) || (defined WEBRTC_CODEC_ISAC) +#include "webrtc/modules/audio_coding/main/source/acm_isac.h" +#include "webrtc/modules/audio_coding/main/source/acm_isac_macros.h" +#endif +#ifdef WEBRTC_CODEC_PCM16 +#include "webrtc/modules/audio_coding/codecs/pcm16b/include/pcm16b.h" +#include "webrtc/modules/audio_coding/main/source/acm_pcm16b.h" +#endif +#ifdef WEBRTC_CODEC_ILBC +#include "webrtc/modules/audio_coding/codecs/ilbc/interface/ilbc.h" +#include "webrtc/modules/audio_coding/main/source/acm_ilbc.h" +#endif +#ifdef WEBRTC_CODEC_AMR +#include "webrtc/modules/audio_coding/codecs/amr/include/amr_interface.h" +#include "webrtc/modules/audio_coding/main/source/acm_amr.h" +#endif +#ifdef WEBRTC_CODEC_AMRWB +#include "webrtc/modules/audio_coding/codecs/amrwb/include/amrwb_interface.h" +#include "webrtc/modules/audio_coding/main/source/acm_amrwb.h" +#endif +#ifdef WEBRTC_CODEC_CELT +#include "webrtc/modules/audio_coding/codecs/celt/include/celt_interface.h" +#include "webrtc/modules/audio_coding/main/source/acm_celt.h" +#endif +#ifdef WEBRTC_CODEC_G722 +#include "webrtc/modules/audio_coding/codecs/g722/include/g722_interface.h" +#include "webrtc/modules/audio_coding/main/source/acm_g722.h" +#endif +#ifdef WEBRTC_CODEC_G722_1 +#include "webrtc/modules/audio_coding/codecs/g7221/include/g7221_interface.h" +#include "webrtc/modules/audio_coding/main/source/acm_g7221.h" +#endif +#ifdef WEBRTC_CODEC_G722_1C +#include "webrtc/modules/audio_coding/codecs/g7221c/include/g7221c_interface.h" +#include "webrtc/modules/audio_coding/main/source/acm_g7221c.h" +#endif +#ifdef WEBRTC_CODEC_G729 +#include "webrtc/modules/audio_coding/codecs/g729/include/g729_interface.h" +#include "webrtc/modules/audio_coding/main/source/acm_g729.h" +#endif +#ifdef WEBRTC_CODEC_G729_1 +#include "webrtc/modules/audio_coding/codecs/g7291/include/g7291_interface.h" +#include "webrtc/modules/audio_coding/main/source/acm_g7291.h" +#endif +#ifdef WEBRTC_CODEC_GSMFR +#include "webrtc/modules/audio_coding/codecs/gsmfr/include/gsmfr_interface.h" +#include "webrtc/modules/audio_coding/main/source/acm_gsmfr.h" +#endif +#ifdef WEBRTC_CODEC_OPUS +#include "webrtc/modules/audio_coding/codecs/opus/interface/opus_interface.h" +#include "webrtc/modules/audio_coding/main/source/acm_opus.h" +#endif +#ifdef WEBRTC_CODEC_SPEEX +#include "webrtc/modules/audio_coding/codecs/speex/include/speex_interface.h" +#include "webrtc/modules/audio_coding/main/source/acm_speex.h" +#endif +#ifdef WEBRTC_CODEC_AVT +#include "webrtc/modules/audio_coding/main/source/acm_dtmf_playout.h" +#endif +#ifdef WEBRTC_CODEC_RED +#include "webrtc/modules/audio_coding/main/source/acm_red.h" +#endif + +namespace webrtc { + +// We dynamically allocate some of the dynamic payload types to the defined +// codecs. Note! There are a limited number of payload types. If more codecs +// are defined they will receive reserved fixed payload types (values 69-95). +const int kDynamicPayloadtypes[ACMCodecDB::kMaxNumCodecs] = { + 107, 108, 109, 111, 112, 113, 114, 115, 116, 117, 92, + 91, 90, 89, 88, 87, 86, 85, 84, 83, 82, 81, 80, + 79, 78, 77, 76, 75, 74, 73, 72, 71, 70, 69, 68, + 67, 66, 65 +}; + +// Creates database with all supported codecs at compile time. +// Each entry needs the following parameters in the given order: +// payload type, name, sampling frequency, packet size in samples, +// number of channels, and default rate. +#if (defined(WEBRTC_CODEC_AMR) || defined(WEBRTC_CODEC_AMRWB) || \ + defined(WEBRTC_CODEC_CELT) || defined(WEBRTC_CODEC_G722_1) || \ + defined(WEBRTC_CODEC_G722_1C) || defined(WEBRTC_CODEC_G729_1) || \ + defined(WEBRTC_CODEC_PCM16) || defined(WEBRTC_CODEC_SPEEX)) +static int count_database = 0; +#endif + +const CodecInst ACMCodecDB::database_[] = { +#if (defined(WEBRTC_CODEC_ISAC) || defined(WEBRTC_CODEC_ISACFX)) + {103, "ISAC", 16000, kIsacPacSize480, 1, kIsacWbDefaultRate}, +# if (defined(WEBRTC_CODEC_ISAC)) + {104, "ISAC", 32000, kIsacPacSize960, 1, kIsacSwbDefaultRate}, + {105, "ISAC", 48000, kIsacPacSize1440, 1, kIsacSwbDefaultRate}, +# endif +#endif +#ifdef WEBRTC_CODEC_PCM16 + // Mono + {kDynamicPayloadtypes[count_database++], "L16", 8000, 80, 1, 128000}, + {kDynamicPayloadtypes[count_database++], "L16", 16000, 160, 1, 256000}, + {kDynamicPayloadtypes[count_database++], "L16", 32000, 320, 1, 512000}, + // Stereo + {kDynamicPayloadtypes[count_database++], "L16", 8000, 80, 2, 128000}, + {kDynamicPayloadtypes[count_database++], "L16", 16000, 160, 2, 256000}, + {kDynamicPayloadtypes[count_database++], "L16", 32000, 320, 2, 512000}, +#endif + // G.711, PCM mu-law and A-law. + // Mono + {0, "PCMU", 8000, 160, 1, 64000}, + {8, "PCMA", 8000, 160, 1, 64000}, + // Stereo + {110, "PCMU", 8000, 160, 2, 64000}, + {118, "PCMA", 8000, 160, 2, 64000}, +#ifdef WEBRTC_CODEC_ILBC + {102, "ILBC", 8000, 240, 1, 13300}, +#endif +#ifdef WEBRTC_CODEC_AMR + {kDynamicPayloadtypes[count_database++], "AMR", 8000, 160, 1, 12200}, +#endif +#ifdef WEBRTC_CODEC_AMRWB + {kDynamicPayloadtypes[count_database++], "AMR-WB", 16000, 320, 1, 20000}, +#endif +#ifdef WEBRTC_CODEC_CELT + // Mono + {kDynamicPayloadtypes[count_database++], "CELT", 32000, 640, 1, 64000}, + // Stereo + {kDynamicPayloadtypes[count_database++], "CELT", 32000, 640, 2, 64000}, +#endif +#ifdef WEBRTC_CODEC_G722 + // Mono + {9, "G722", 16000, 320, 1, 64000}, + // Stereo + {119, "G722", 16000, 320, 2, 64000}, +#endif +#ifdef WEBRTC_CODEC_G722_1 + {kDynamicPayloadtypes[count_database++], "G7221", 16000, 320, 1, 32000}, + {kDynamicPayloadtypes[count_database++], "G7221", 16000, 320, 1, 24000}, + {kDynamicPayloadtypes[count_database++], "G7221", 16000, 320, 1, 16000}, +#endif +#ifdef WEBRTC_CODEC_G722_1C + {kDynamicPayloadtypes[count_database++], "G7221", 32000, 640, 1, 48000}, + {kDynamicPayloadtypes[count_database++], "G7221", 32000, 640, 1, 32000}, + {kDynamicPayloadtypes[count_database++], "G7221", 32000, 640, 1, 24000}, +#endif +#ifdef WEBRTC_CODEC_G729 + {18, "G729", 8000, 240, 1, 8000}, +#endif +#ifdef WEBRTC_CODEC_G729_1 + {kDynamicPayloadtypes[count_database++], "G7291", 16000, 320, 1, 32000}, +#endif +#ifdef WEBRTC_CODEC_GSMFR + {3, "GSM", 8000, 160, 1, 13200}, +#endif +#ifdef WEBRTC_CODEC_OPUS + // Opus internally supports 48, 24, 16, 12, 8 kHz. + // Mono and stereo. + {120, "opus", 48000, 960, 2, 64000}, +#endif +#ifdef WEBRTC_CODEC_SPEEX + {kDynamicPayloadtypes[count_database++], "speex", 8000, 160, 1, 11000}, + {kDynamicPayloadtypes[count_database++], "speex", 16000, 320, 1, 22000}, +#endif + // Comfort noise for four different sampling frequencies. + {13, "CN", 8000, 240, 1, 0}, + {98, "CN", 16000, 480, 1, 0}, + {99, "CN", 32000, 960, 1, 0}, + {100, "CN", 48000, 1440, 1, 0}, +#ifdef WEBRTC_CODEC_AVT + {106, "telephone-event", 8000, 240, 1, 0}, +#endif +#ifdef WEBRTC_CODEC_RED + {127, "red", 8000, 0, 1, 0}, +#endif + // To prevent compile errors due to trailing commas. + {-1, "Null", -1, -1, -1, -1} +}; + +// Create database with all codec settings at compile time. +// Each entry needs the following parameters in the given order: +// Number of allowed packet sizes, a vector with the allowed packet sizes, +// Basic block samples, max number of channels that are supported. +const ACMCodecDB::CodecSettings ACMCodecDB::codec_settings_[] = { +#if (defined(WEBRTC_CODEC_ISAC) || defined(WEBRTC_CODEC_ISACFX)) + {2, {kIsacPacSize480, kIsacPacSize960}, 0, 1, true}, +# if (defined(WEBRTC_CODEC_ISAC)) + {1, {kIsacPacSize960}, 0, 1, false}, + {1, {kIsacPacSize1440}, 0, 1, true}, +# endif +#endif +#ifdef WEBRTC_CODEC_PCM16 + // Mono + {4, {80, 160, 240, 320}, 0, 2, false}, + {4, {160, 320, 480, 640}, 0, 2, false}, + {2, {320, 640}, 0, 2, false}, + // Stereo + {4, {80, 160, 240, 320}, 0, 2, false}, + {4, {160, 320, 480, 640}, 0, 2, false}, + {2, {320, 640}, 0, 2}, +#endif + // G.711, PCM mu-law and A-law. + // Mono + {6, {80, 160, 240, 320, 400, 480}, 0, 2, false}, + {6, {80, 160, 240, 320, 400, 480}, 0, 2, false}, + // Stereo + {6, {80, 160, 240, 320, 400, 480}, 0, 2, false}, + {6, {80, 160, 240, 320, 400, 480}, 0, 2, false}, +#ifdef WEBRTC_CODEC_ILBC + {4, {160, 240, 320, 480}, 0, 1, false}, +#endif +#ifdef WEBRTC_CODEC_AMR + {3, {160, 320, 480}, 0, 1, true}, +#endif +#ifdef WEBRTC_CODEC_AMRWB + {3, {320, 640, 960}, 0, 1, true}, +#endif +#ifdef WEBRTC_CODEC_CELT + // Mono + {1, {640}, 0, 2, false}, + // Stereo + {1, {640}, 0, 2, false}, +#endif +#ifdef WEBRTC_CODEC_G722 + // Mono + {6, {160, 320, 480, 640, 800, 960}, 0, 2, false}, + // Stereo + {6, {160, 320, 480, 640, 800, 960}, 0, 2, false}, +#endif +#ifdef WEBRTC_CODEC_G722_1 + {1, {320}, 320, 1, false}, + {1, {320}, 320, 1, false}, + {1, {320}, 320, 1, false}, +#endif +#ifdef WEBRTC_CODEC_G722_1C + {1, {640}, 640, 1, false}, + {1, {640}, 640, 1, false}, + {1, {640}, 640, 1, false}, +#endif +#ifdef WEBRTC_CODEC_G729 + {6, {80, 160, 240, 320, 400, 480}, 0, 1, false}, +#endif +#ifdef WEBRTC_CODEC_G729_1 + {3, {320, 640, 960}, 0, 1, false}, +#endif +#ifdef WEBRTC_CODEC_GSMFR + {3, {160, 320, 480}, 160, 1, false}, +#endif +#ifdef WEBRTC_CODEC_OPUS + // Opus supports frames shorter than 10ms, + // but it doesn't help us to use them. + // Mono and stereo. + {1, {960}, 0, 2, false}, +#endif +#ifdef WEBRTC_CODEC_SPEEX + {3, {160, 320, 480}, 0, 1, false}, + {3, {320, 640, 960}, 0, 1, false}, +#endif + // Comfort noise for three different sampling frequencies. + {1, {240}, 240, 1, false}, + {1, {480}, 480, 1, false}, + {1, {960}, 960, 1, false}, + {1, {1440}, 1440, 1, false}, +#ifdef WEBRTC_CODEC_AVT + {1, {240}, 240, 1, false}, +#endif +#ifdef WEBRTC_CODEC_RED + {1, {0}, 0, 1, false}, +#endif + // To prevent compile errors due to trailing commas. + {-1, {-1}, -1, -1, false} +}; + +// Create a database of all NetEQ decoders at compile time. +const NetEqDecoder ACMCodecDB::neteq_decoders_[] = { +#if (defined(WEBRTC_CODEC_ISAC) || defined(WEBRTC_CODEC_ISACFX)) + kDecoderISAC, +# if (defined(WEBRTC_CODEC_ISAC)) + kDecoderISACswb, + kDecoderISACfb, +# endif +#endif +#ifdef WEBRTC_CODEC_PCM16 + // Mono + kDecoderPCM16B, + kDecoderPCM16Bwb, + kDecoderPCM16Bswb32kHz, + // Stereo + kDecoderPCM16B_2ch, + kDecoderPCM16Bwb_2ch, + kDecoderPCM16Bswb32kHz_2ch, +#endif + // G.711, PCM mu-las and A-law. + // Mono + kDecoderPCMu, + kDecoderPCMa, + // Stereo + kDecoderPCMu_2ch, + kDecoderPCMa_2ch, +#ifdef WEBRTC_CODEC_ILBC + kDecoderILBC, +#endif +#ifdef WEBRTC_CODEC_AMR + kDecoderAMR, +#endif +#ifdef WEBRTC_CODEC_AMRWB + kDecoderAMRWB, +#endif +#ifdef WEBRTC_CODEC_CELT + // Mono + kDecoderCELT_32, + // Stereo + kDecoderCELT_32_2ch, +#endif +#ifdef WEBRTC_CODEC_G722 + // Mono + kDecoderG722, + // Stereo + kDecoderG722_2ch, +#endif +#ifdef WEBRTC_CODEC_G722_1 + kDecoderG722_1_32, + kDecoderG722_1_24, + kDecoderG722_1_16, +#endif +#ifdef WEBRTC_CODEC_G722_1C + kDecoderG722_1C_48, + kDecoderG722_1C_32, + kDecoderG722_1C_24, +#endif +#ifdef WEBRTC_CODEC_G729 + kDecoderG729, +#endif +#ifdef WEBRTC_CODEC_G729_1 + kDecoderG729_1, +#endif +#ifdef WEBRTC_CODEC_GSMFR + kDecoderGSMFR, +#endif +#ifdef WEBRTC_CODEC_OPUS + // Mono and stereo. + kDecoderOpus, +#endif +#ifdef WEBRTC_CODEC_SPEEX + kDecoderSPEEX_8, + kDecoderSPEEX_16, +#endif + // Comfort noise for three different sampling frequencies. + kDecoderCNGnb, + kDecoderCNGwb, + kDecoderCNGswb32kHz, + kDecoderCNGswb48kHz +#ifdef WEBRTC_CODEC_AVT + , kDecoderAVT +#endif +#ifdef WEBRTC_CODEC_RED + , kDecoderRED +#endif +}; + +// Get codec information from database. +// TODO(tlegrand): replace memcpy with a pointer to the data base memory. +int ACMCodecDB::Codec(int codec_id, CodecInst* codec_inst) { + // Error check to see that codec_id is not out of bounds. + if ((codec_id < 0) || (codec_id >= kNumCodecs)) { + return -1; + } + + // Copy database information for the codec to the output. + memcpy(codec_inst, &database_[codec_id], sizeof(CodecInst)); + + return 0; +} + +// Enumerator for error codes when asking for codec database id. +enum { + kInvalidCodec = -10, + kInvalidPayloadtype = -30, + kInvalidPacketSize = -40, + kInvalidRate = -50 +}; + +// Gets the codec id number from the database. If there is some mismatch in +// the codec settings, the function will return an error code. +// NOTE! The first mismatch found will generate the return value. +int ACMCodecDB::CodecNumber(const CodecInst& codec_inst, int* mirror_id) { + // Look for a matching codec in the database. + int codec_id = CodecId(codec_inst); + + // Checks if we found a matching codec. + if (codec_id == -1) { + return kInvalidCodec; + } + + // Checks the validity of payload type + if (!ValidPayloadType(codec_inst.pltype)) { + return kInvalidPayloadtype; + } + + // Comfort Noise is special case, packet-size & rate is not checked. + if (STR_CASE_CMP(database_[codec_id].plname, "CN") == 0) { + *mirror_id = codec_id; + return codec_id; + } + + // RED is special case, packet-size & rate is not checked. + if (STR_CASE_CMP(database_[codec_id].plname, "red") == 0) { + *mirror_id = codec_id; + return codec_id; + } + + // Checks the validity of packet size. + if (codec_settings_[codec_id].num_packet_sizes > 0) { + bool packet_size_ok = false; + int i; + int packet_size_samples; + for (i = 0; i < codec_settings_[codec_id].num_packet_sizes; i++) { + packet_size_samples = + codec_settings_[codec_id].packet_sizes_samples[i]; + if (codec_inst.pacsize == packet_size_samples) { + packet_size_ok = true; + break; + } + } + + if (!packet_size_ok) { + return kInvalidPacketSize; + } + } + + if (codec_inst.pacsize < 1) { + return kInvalidPacketSize; + } + + // Check the validity of rate. Codecs with multiple rates have their own + // function for this. + *mirror_id = codec_id; + if (STR_CASE_CMP("isac", codec_inst.plname) == 0) { + if (IsISACRateValid(codec_inst.rate)) { + // Set mirrorID to iSAC WB which is only created once to be used both for + // iSAC WB and SWB, because they need to share struct. + *mirror_id = kISAC; + return codec_id; + } else { + return kInvalidRate; + } + } else if (STR_CASE_CMP("ilbc", codec_inst.plname) == 0) { + return IsILBCRateValid(codec_inst.rate, codec_inst.pacsize) + ? codec_id : kInvalidRate; + } else if (STR_CASE_CMP("amr", codec_inst.plname) == 0) { + return IsAMRRateValid(codec_inst.rate) + ? codec_id : kInvalidRate; + } else if (STR_CASE_CMP("amr-wb", codec_inst.plname) == 0) { + return IsAMRwbRateValid(codec_inst.rate) + ? codec_id : kInvalidRate; + } else if (STR_CASE_CMP("g7291", codec_inst.plname) == 0) { + return IsG7291RateValid(codec_inst.rate) + ? codec_id : kInvalidRate; + } else if (STR_CASE_CMP("opus", codec_inst.plname) == 0) { + return IsOpusRateValid(codec_inst.rate) + ? codec_id : kInvalidRate; + } else if (STR_CASE_CMP("speex", codec_inst.plname) == 0) { + return IsSpeexRateValid(codec_inst.rate) + ? codec_id : kInvalidRate; + } else if (STR_CASE_CMP("celt", codec_inst.plname) == 0) { + return IsCeltRateValid(codec_inst.rate) + ? codec_id : kInvalidRate; + } + + return IsRateValid(codec_id, codec_inst.rate) ? + codec_id : kInvalidRate; +} + +// Looks for a matching payload name, frequency, and channels in the +// codec list. Need to check all three since some codecs have several codec +// entries with different frequencies and/or channels. +// Does not check other codec settings, such as payload type and packet size. +// Returns the id of the codec, or -1 if no match is found. +int ACMCodecDB::CodecId(const CodecInst& codec_inst) { + return (CodecId(codec_inst.plname, codec_inst.plfreq, + codec_inst.channels)); +} + +int ACMCodecDB::CodecId(const char* payload_name, int frequency, int channels) { + for (int id = 0; id < kNumCodecs; id++) { + bool name_match = false; + bool frequency_match = false; + bool channels_match = false; + + // Payload name, sampling frequency and number of channels need to match. + // NOTE! If |frequency| is -1, the frequency is not applicable, and is + // always treated as true, like for RED. + name_match = (STR_CASE_CMP(database_[id].plname, payload_name) == 0); + frequency_match = (frequency == database_[id].plfreq) || (frequency == -1); + // The number of channels must match for all codecs but Opus. + if (STR_CASE_CMP(payload_name, "opus") != 0) { + channels_match = (channels == database_[id].channels); + } else { + // For opus we just check that number of channels is valid. + channels_match = (channels == 1 || channels == 2); + } + + if (name_match && frequency_match && channels_match) { + // We have found a matching codec in the list. + return id; + } + } + + // We didn't find a matching codec. + return -1; +} +// Gets codec id number, and mirror id, from database for the receiver. +int ACMCodecDB::ReceiverCodecNumber(const CodecInst& codec_inst, + int* mirror_id) { + // Look for a matching codec in the database. + int codec_id = CodecId(codec_inst); + + // Set |mirror_id| to |codec_id|, except for iSAC. In case of iSAC we always + // set |mirror_id| to iSAC WB (kISAC) which is only created once to be used + // both for iSAC WB and SWB, because they need to share struct. + if (STR_CASE_CMP(codec_inst.plname, "ISAC") != 0) { + *mirror_id = codec_id; + } else { + *mirror_id = kISAC; + } + + return codec_id; +} + +// Returns the codec sampling frequency for codec with id = "codec_id" in +// database. +int ACMCodecDB::CodecFreq(int codec_id) { + // Error check to see that codec_id is not out of bounds. + if (codec_id < 0 || codec_id >= kNumCodecs) { + return -1; + } + + return database_[codec_id].plfreq; +} + +// Returns the codec's basic coding block size in samples. +int ACMCodecDB::BasicCodingBlock(int codec_id) { + // Error check to see that codec_id is not out of bounds. + if (codec_id < 0 || codec_id >= kNumCodecs) { + return -1; + } + + return codec_settings_[codec_id].basic_block_samples; +} + +// Returns the NetEQ decoder database. +const NetEqDecoder* ACMCodecDB::NetEQDecoders() { + return neteq_decoders_; +} + +// Gets mirror id. The Id is used for codecs sharing struct for settings that +// need different payload types. +int ACMCodecDB::MirrorID(int codec_id) { + if (STR_CASE_CMP(database_[codec_id].plname, "isac") == 0) { + return kISAC; + } else { + return codec_id; + } +} + +// Creates memory/instance for storing codec state. +ACMGenericCodec* ACMCodecDB::CreateCodecInstance(const CodecInst& codec_inst) { + // All we have support for right now. + if (!STR_CASE_CMP(codec_inst.plname, "ISAC")) { +#if (defined(WEBRTC_CODEC_ISAC) || defined(WEBRTC_CODEC_ISACFX)) + return new ACMISAC(kISAC); +#endif + } else if (!STR_CASE_CMP(codec_inst.plname, "PCMU")) { + if (codec_inst.channels == 1) { + return new ACMPCMU(kPCMU); + } else { + return new ACMPCMU(kPCMU_2ch); + } + } else if (!STR_CASE_CMP(codec_inst.plname, "PCMA")) { + if (codec_inst.channels == 1) { + return new ACMPCMA(kPCMA); + } else { + return new ACMPCMA(kPCMA_2ch); + } + } else if (!STR_CASE_CMP(codec_inst.plname, "ILBC")) { +#ifdef WEBRTC_CODEC_ILBC + return new ACMILBC(kILBC); +#endif + } else if (!STR_CASE_CMP(codec_inst.plname, "AMR")) { +#ifdef WEBRTC_CODEC_AMR + return new ACMAMR(kGSMAMR); +#endif + } else if (!STR_CASE_CMP(codec_inst.plname, "AMR-WB")) { +#ifdef WEBRTC_CODEC_AMRWB + return new ACMAMRwb(kGSMAMRWB); +#endif + } else if (!STR_CASE_CMP(codec_inst.plname, "CELT")) { +#ifdef WEBRTC_CODEC_CELT + if (codec_inst.channels == 1) { + return new ACMCELT(kCELT32); + } else { + return new ACMCELT(kCELT32_2ch); + } +#endif + } else if (!STR_CASE_CMP(codec_inst.plname, "G722")) { +#ifdef WEBRTC_CODEC_G722 + if (codec_inst.channels == 1) { + return new ACMG722(kG722); + } else { + return new ACMG722(kG722_2ch); + } +#endif + } else if (!STR_CASE_CMP(codec_inst.plname, "G7221")) { + switch (codec_inst.plfreq) { + case 16000: { +#ifdef WEBRTC_CODEC_G722_1 + int codec_id; + switch (codec_inst->rate) { + case 16000 : { + codec_id = kG722_1_16; + break; + } + case 24000 : { + codec_id = kG722_1_24; + break; + } + case 32000 : { + codec_id = kG722_1_32; + break; + } + default: { + return NULL; + } + return new ACMG722_1(codec_id); + } +#endif + } + case 32000: { +#ifdef WEBRTC_CODEC_G722_1C + int codec_id; + switch (codec_inst->rate) { + case 24000 : { + codec_id = kG722_1C_24; + break; + } + case 32000 : { + codec_id = kG722_1C_32; + break; + } + case 48000 : { + codec_id = kG722_1C_48; + break; + } + default: { + return NULL; + } + return new ACMG722_1C(codec_id); + } +#endif + } + } + } else if (!STR_CASE_CMP(codec_inst.plname, "CN")) { + // For CN we need to check sampling frequency to know what codec to create. + int codec_id; + switch (codec_inst.plfreq) { + case 8000: { + codec_id = kCNNB; + break; + } + case 16000: { + codec_id = kCNWB; + break; + } + case 32000: { + codec_id = kCNSWB; + break; + } + case 48000: { + codec_id = kCNFB; + break; + } + default: { + return NULL; + } + } + return new ACMCNG(codec_id); + } else if (!STR_CASE_CMP(codec_inst.plname, "G729")) { +#ifdef WEBRTC_CODEC_G729 + return new ACMG729(kG729); +#endif + } else if (!STR_CASE_CMP(codec_inst.plname, "G7291")) { +#ifdef WEBRTC_CODEC_G729_1 + return new ACMG729_1(kG729_1); +#endif + } else if (!STR_CASE_CMP(codec_inst.plname, "opus")) { +#ifdef WEBRTC_CODEC_OPUS + return new ACMOpus(kOpus); +#endif + } else if (!STR_CASE_CMP(codec_inst.plname, "speex")) { +#ifdef WEBRTC_CODEC_SPEEX + int codec_id; + switch (codec_inst->plfreq) { + case 8000: { + codec_id = kSPEEX8; + break; + } + case 16000: { + codec_id = kSPEEX16; + break; + } + default: { + return NULL; + } + } + return new ACMSPEEX(codec_id); +#endif + } else if (!STR_CASE_CMP(codec_inst.plname, "CN")) { + // For CN we need to check sampling frequency to know what codec to create. + int codec_id; + switch (codec_inst.plfreq) { + case 8000: { + codec_id = kCNNB; + break; + } + case 16000: { + codec_id = kCNWB; + break; + } + case 32000: { + codec_id = kCNSWB; + break; + } + case 48000: { + codec_id = kCNFB; + break; + } + default: { + return NULL; + } + } + return new ACMCNG(codec_id); + } else if (!STR_CASE_CMP(codec_inst.plname, "L16")) { +#ifdef WEBRTC_CODEC_PCM16 + // For L16 we need to check sampling frequency to know what codec to create. + int codec_id; + if (codec_inst.channels == 1) { + switch (codec_inst.plfreq) { + case 8000: { + codec_id = kPCM16B; + break; + } + case 16000: { + codec_id = kPCM16Bwb; + break; + } + case 32000: { + codec_id = kPCM16Bswb32kHz; + break; + } + default: { + return NULL; + } + } + } else { + switch (codec_inst.plfreq) { + case 8000: { + codec_id = kPCM16B_2ch; + break; + } + case 16000: { + codec_id = kPCM16Bwb_2ch; + break; + } + case 32000: { + codec_id = kPCM16Bswb32kHz_2ch; + break; + } + default: { + return NULL; + } + } + } + return new ACMPCM16B(codec_id); +#endif + } else if (!STR_CASE_CMP(codec_inst.plname, "telephone-event")) { +#ifdef WEBRTC_CODEC_AVT + return new ACMDTMFPlayout(kAVT); +#endif + } else if (!STR_CASE_CMP(codec_inst.plname, "red")) { +#ifdef WEBRTC_CODEC_RED + return new ACMRED(kRED); +#endif + } + return NULL; +} + +// Checks if the bitrate is valid for the codec. +bool ACMCodecDB::IsRateValid(int codec_id, int rate) { + if (database_[codec_id].rate == rate) { + return true; + } else { + return false; + } +} + +// Checks if the bitrate is valid for iSAC. +bool ACMCodecDB::IsISACRateValid(int rate) { + if ((rate == -1) || ((rate <= 56000) && (rate >= 10000))) { + return true; + } else { + return false; + } +} + +// Checks if the bitrate is valid for iLBC. +bool ACMCodecDB::IsILBCRateValid(int rate, int frame_size_samples) { + if (((frame_size_samples == 240) || (frame_size_samples == 480)) && + (rate == 13300)) { + return true; + } else if (((frame_size_samples == 160) || (frame_size_samples == 320)) && + (rate == 15200)) { + return true; + } else { + return false; + } +} + +// Check if the bitrate is valid for the GSM-AMR. +bool ACMCodecDB::IsAMRRateValid(int rate) { + switch (rate) { + case 4750: + case 5150: + case 5900: + case 6700: + case 7400: + case 7950: + case 10200: + case 12200: { + return true; + } + default: { + return false; + } + } +} + +// Check if the bitrate is valid for GSM-AMR-WB. +bool ACMCodecDB::IsAMRwbRateValid(int rate) { + switch (rate) { + case 7000: + case 9000: + case 12000: + case 14000: + case 16000: + case 18000: + case 20000: + case 23000: + case 24000: { + return true; + } + default: { + return false; + } + } +} + +// Check if the bitrate is valid for G.729.1. +bool ACMCodecDB::IsG7291RateValid(int rate) { + switch (rate) { + case 8000: + case 12000: + case 14000: + case 16000: + case 18000: + case 20000: + case 22000: + case 24000: + case 26000: + case 28000: + case 30000: + case 32000: { + return true; + } + default: { + return false; + } + } +} + +// Checks if the bitrate is valid for Speex. +bool ACMCodecDB::IsSpeexRateValid(int rate) { + if (rate > 2000) { + return true; + } else { + return false; + } +} + +// Checks if the bitrate is valid for Opus. +bool ACMCodecDB::IsOpusRateValid(int rate) { + if ((rate < 6000) || (rate > 510000)) { + return false; + } + return true; +} + +// Checks if the bitrate is valid for Celt. +bool ACMCodecDB::IsCeltRateValid(int rate) { + if ((rate >= 48000) && (rate <= 128000)) { + return true; + } else { + return false; + } +} + +// Checks if the payload type is in the valid range. +bool ACMCodecDB::ValidPayloadType(int payload_type) { + if ((payload_type < 0) || (payload_type > 127)) { + return false; + } + return true; +} + +bool ACMCodecDB::OwnsDecoder(int codec_id) { + assert(codec_id >= 0 && codec_id < ACMCodecDB::kNumCodecs); + return ACMCodecDB::codec_settings_[codec_id].owns_decoder; +} + +} // namespace webrtc diff --git a/webrtc/modules/audio_coding/main/acm2/acm_codec_database.h b/webrtc/modules/audio_coding/main/acm2/acm_codec_database.h new file mode 100644 index 0000000000..fb5cb9a039 --- /dev/null +++ b/webrtc/modules/audio_coding/main/acm2/acm_codec_database.h @@ -0,0 +1,350 @@ +/* + * 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. + */ + +/* + * This file generates databases with information about all supported audio + * codecs. + */ + +#ifndef WEBRTC_MODULES_AUDIO_CODING_MAIN_SOURCE_ACM_CODEC_DATABASE_H_ +#define WEBRTC_MODULES_AUDIO_CODING_MAIN_SOURCE_ACM_CODEC_DATABASE_H_ + +#include "webrtc/common_types.h" +#include "webrtc/modules/audio_coding/main/source/acm_generic_codec.h" +#include "webrtc/modules/audio_coding/neteq4/interface/neteq.h" + +namespace webrtc { + +// TODO(tlegrand): replace class ACMCodecDB with a namespace. +class ACMCodecDB { + public: + // Enum with array indexes for the supported codecs. NOTE! The order MUST + // be the same as when creating the database in acm_codec_database.cc. + enum { + kNone = -1 +#if (defined(WEBRTC_CODEC_ISAC) || defined(WEBRTC_CODEC_ISACFX)) + , kISAC +# if (defined(WEBRTC_CODEC_ISAC)) + , kISACSWB + , kISACFB +# endif +#endif +#ifdef WEBRTC_CODEC_PCM16 + // Mono + , kPCM16B + , kPCM16Bwb + , kPCM16Bswb32kHz + // Stereo + , kPCM16B_2ch + , kPCM16Bwb_2ch + , kPCM16Bswb32kHz_2ch +#endif + // Mono + , kPCMU + , kPCMA + // Stereo + , kPCMU_2ch + , kPCMA_2ch +#ifdef WEBRTC_CODEC_ILBC + , kILBC +#endif +#ifdef WEBRTC_CODEC_AMR + , kGSMAMR +#endif +#ifdef WEBRTC_CODEC_AMRWB + , kGSMAMRWB +#endif +#ifdef WEBRTC_CODEC_CELT + // Mono + , kCELT32 + // Stereo + , kCELT32_2ch +#endif +#ifdef WEBRTC_CODEC_G722 + // Mono + , kG722 + // Stereo + , kG722_2ch +#endif +#ifdef WEBRTC_CODEC_G722_1 + , kG722_1_32 + , kG722_1_24 + , kG722_1_16 +#endif +#ifdef WEBRTC_CODEC_G722_1C + , kG722_1C_48 + , kG722_1C_32 + , kG722_1C_24 +#endif +#ifdef WEBRTC_CODEC_G729 + , kG729 +#endif +#ifdef WEBRTC_CODEC_G729_1 + , kG729_1 +#endif +#ifdef WEBRTC_CODEC_GSMFR + , kGSMFR +#endif +#ifdef WEBRTC_CODEC_OPUS + // Mono and stereo + , kOpus +#endif +#ifdef WEBRTC_CODEC_SPEEX + , kSPEEX8 + , kSPEEX16 +#endif + , kCNNB + , kCNWB + , kCNSWB + , kCNFB +#ifdef WEBRTC_CODEC_AVT + , kAVT +#endif +#ifdef WEBRTC_CODEC_RED + , kRED +#endif + , kNumCodecs + }; + + // Set unsupported codecs to -1 +#ifndef WEBRTC_CODEC_ISAC + enum {kISACSWB = -1}; + enum {kISACFB = -1}; +# ifndef WEBRTC_CODEC_ISACFX + enum {kISAC = -1}; +# endif +#endif +#ifndef WEBRTC_CODEC_PCM16 + // Mono + enum {kPCM16B = -1}; + enum {kPCM16Bwb = -1}; + enum {kPCM16Bswb32kHz = -1}; + // Stereo + enum {kPCM16B_2ch = -1}; + enum {kPCM16Bwb_2ch = -1}; + enum {kPCM16Bswb32kHz_2ch = -1}; +#endif + // 48 kHz not supported, always set to -1. + enum {kPCM16Bswb48kHz = -1}; +#ifndef WEBRTC_CODEC_ILBC + enum {kILBC = -1}; +#endif +#ifndef WEBRTC_CODEC_AMR + enum {kGSMAMR = -1}; +#endif +#ifndef WEBRTC_CODEC_AMRWB + enum {kGSMAMRWB = -1}; +#endif +#ifndef WEBRTC_CODEC_CELT + // Mono + enum {kCELT32 = -1}; + // Stereo + enum {kCELT32_2ch = -1}; +#endif +#ifndef WEBRTC_CODEC_G722 + // Mono + enum {kG722 = -1}; + // Stereo + enum {kG722_2ch = -1}; +#endif +#ifndef WEBRTC_CODEC_G722_1 + enum {kG722_1_32 = -1}; + enum {kG722_1_24 = -1}; + enum {kG722_1_16 = -1}; +#endif +#ifndef WEBRTC_CODEC_G722_1C + enum {kG722_1C_48 = -1}; + enum {kG722_1C_32 = -1}; + enum {kG722_1C_24 = -1}; +#endif +#ifndef WEBRTC_CODEC_G729 + enum {kG729 = -1}; +#endif +#ifndef WEBRTC_CODEC_G729_1 + enum {kG729_1 = -1}; +#endif +#ifndef WEBRTC_CODEC_GSMFR + enum {kGSMFR = -1}; +#endif +#ifndef WEBRTC_CODEC_SPEEX + enum {kSPEEX8 = -1}; + enum {kSPEEX16 = -1}; +#endif +#ifndef WEBRTC_CODEC_OPUS + // Mono and stereo + enum {kOpus = -1}; +#endif +#ifndef WEBRTC_CODEC_AVT + enum {kAVT = -1}; +#endif +#ifndef WEBRTC_CODEC_RED + enum {kRED = -1}; +#endif + + // kMaxNumCodecs - Maximum number of codecs that can be activated in one + // build. + // kMaxNumPacketSize - Maximum number of allowed packet sizes for one codec. + // These might need to be increased if adding a new codec to the database + static const int kMaxNumCodecs = 50; + static const int kMaxNumPacketSize = 6; + + // Codec specific settings + // + // num_packet_sizes - number of allowed packet sizes. + // packet_sizes_samples - list of the allowed packet sizes. + // basic_block_samples - assigned a value different from 0 if the codec + // requires to be fed with a specific number of samples + // that can be different from packet size. + // channel_support - number of channels supported to encode; + // 1 = mono, 2 = stereo, etc. + // owns_decoder - if true, it means that the codec should own the + // decoder instance. In this case, the codec should + // implement ACMGenericCodec::Decoder(), which returns + // a pointer to AudioDecoder. This pointer is injected + // into NetEq when this codec is registered as receive + // codec. + struct CodecSettings { + int num_packet_sizes; + int packet_sizes_samples[kMaxNumPacketSize]; + int basic_block_samples; + int channel_support; + bool owns_decoder; + }; + + // Gets codec information from database at the position in database given by + // [codec_id]. + // Input: + // [codec_id] - number that specifies at what position in the database to + // get the information. + // Output: + // [codec_inst] - filled with information about the codec. + // Return: + // 0 if successful, otherwise -1. + static int Codec(int codec_id, CodecInst* codec_inst); + + // Returns codec id and mirror id from database, given the information + // received in the input [codec_inst]. Mirror id is a number that tells + // where to find the codec's memory (instance). The number is either the + // same as codec id (most common), or a number pointing at a different + // entry in the database, if the codec has several entries with different + // payload types. This is used for codecs that must share one struct even if + // the payload type differs. + // One example is the codec iSAC which has the same struct for both 16 and + // 32 khz, but they have different entries in the database. Let's say the + // function is called with iSAC 32kHz. The function will return 1 as that is + // the entry in the data base, and [mirror_id] = 0, as that is the entry for + // iSAC 16 kHz, which holds the shared memory. + // Input: + // [codec_inst] - Information about the codec for which we require the + // database id. + // Output: + // [mirror_id] - mirror id, which most often is the same as the return + // value, see above. + // [err_message] - if present, in the event of a mismatch found between the + // input and the database, a descriptive error message is + // written here. + // [err_message] - if present, the length of error message is returned here. + // Return: + // codec id if successful, otherwise < 0. + static int CodecNumber(const CodecInst& codec_inst, int* mirror_id, + char* err_message, int max_message_len_byte); + static int CodecNumber(const CodecInst& codec_inst, int* mirror_id); + static int CodecId(const CodecInst& codec_inst); + static int CodecId(const char* payload_name, int frequency, int channels); + static int ReceiverCodecNumber(const CodecInst& codec_inst, int* mirror_id); + + // Returns the codec sampling frequency for codec with id = "codec_id" in + // database. + // TODO(tlegrand): Check if function is needed, or if we can change + // to access database directly. + // Input: + // [codec_id] - number that specifies at what position in the database to + // get the information. + // Return: + // codec sampling frequency if successful, otherwise -1. + static int CodecFreq(int codec_id); + + // Return the codec's basic coding block size in samples. + // TODO(tlegrand): Check if function is needed, or if we can change + // to access database directly. + // Input: + // [codec_id] - number that specifies at what position in the database to + // get the information. + // Return: + // codec basic block size if successful, otherwise -1. + static int BasicCodingBlock(int codec_id); + + // Returns the NetEQ decoder database. + static const NetEqDecoder* NetEQDecoders(); + + // Returns mirror id, which is a number that tells where to find the codec's + // memory (instance). It is either the same as codec id (most common), or a + // number pointing at a different entry in the database, if the codec have + // several entries with different payload types. This is used for codecs that + // must share struct even if the payload type differs. + // TODO(tlegrand): Check if function is needed, or if we can change + // to access database directly. + // Input: + // [codec_id] - number that specifies codec's position in the database. + // Return: + // Mirror id on success, otherwise -1. + static int MirrorID(int codec_id); + + // Create memory/instance for storing codec state. + // Input: + // [codec_inst] - information about codec. Only name of codec, "plname", is + // used in this function. + static ACMGenericCodec* CreateCodecInstance(const CodecInst& codec_inst); + + // Specifies if the codec specified by |codec_id| MUST own its own decoder. + // This is the case for codecs which *should* share a single codec instance + // between encoder and decoder. Or for codecs which ACM should have control + // over the decoder. For instance iSAC is such a codec that encoder and + // decoder share the same codec instance. + static bool OwnsDecoder(int codec_id); + + // Checks if the bitrate is valid for the codec. + // Input: + // [codec_id] - number that specifies codec's position in the database. + // [rate] - bitrate to check. + // [frame_size_samples] - (used for iLBC) specifies which frame size to go + // with the rate. + static bool IsRateValid(int codec_id, int rate); + static bool IsISACRateValid(int rate); + static bool IsILBCRateValid(int rate, int frame_size_samples); + static bool IsAMRRateValid(int rate); + static bool IsAMRwbRateValid(int rate); + static bool IsG7291RateValid(int rate); + static bool IsSpeexRateValid(int rate); + static bool IsOpusRateValid(int rate); + static bool IsCeltRateValid(int rate); + + // Check if the payload type is valid, meaning that it is in the valid range + // of 0 to 127. + // Input: + // [payload_type] - payload type. + static bool ValidPayloadType(int payload_type); + + // Databases with information about the supported codecs + // database_ - stored information about all codecs: payload type, name, + // sampling frequency, packet size in samples, default channel + // support, and default rate. + // codec_settings_ - stored codec settings: number of allowed packet sizes, + // a vector with the allowed packet sizes, basic block + // samples, and max number of channels that are supported. + // neteq_decoders_ - list of supported decoders in NetEQ. + static const CodecInst database_[kMaxNumCodecs]; + static const CodecSettings codec_settings_[kMaxNumCodecs]; + static const NetEqDecoder neteq_decoders_[kMaxNumCodecs]; +}; + +} // namespace webrtc + +#endif // WEBRTC_MODULES_AUDIO_CODING_MAIN_SOURCE_ACM_CODEC_DATABASE_H_ diff --git a/webrtc/modules/audio_coding/main/acm2/acm_common_defs.h b/webrtc/modules/audio_coding/main/acm2/acm_common_defs.h new file mode 100644 index 0000000000..b27256a00f --- /dev/null +++ b/webrtc/modules/audio_coding/main/acm2/acm_common_defs.h @@ -0,0 +1,98 @@ +/* + * Copyright (c) 2011 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_SOURCE_ACM_COMMON_DEFS_H_ +#define WEBRTC_MODULES_AUDIO_CODING_MAIN_SOURCE_ACM_COMMON_DEFS_H_ + +#include + +#include "webrtc/common_types.h" +#include "webrtc/engine_configurations.h" +#include "webrtc/modules/audio_coding/main/interface/audio_coding_module_typedefs.h" +#include "webrtc/typedefs.h" + +// Checks for enabled codecs, we prevent enabling codecs which are not +// compatible. +#if ((defined WEBRTC_CODEC_ISAC) && (defined WEBRTC_CODEC_ISACFX)) +#error iSAC and iSACFX codecs cannot be enabled at the same time +#endif + +#ifndef STR_CASE_CMP +#ifdef WIN32 +// OS-dependent case-insensitive string comparison +#define STR_CASE_CMP(x, y) ::_stricmp(x, y) +#else +// OS-dependent case-insensitive string comparison +#define STR_CASE_CMP(x, y) ::strcasecmp(x, y) +#endif +#endif + +namespace webrtc { + +// 60 ms is the maximum block size we support. An extra 20 ms is considered +// for safety if process() method is not called when it should be, i.e. we +// accept 20 ms of jitter. 80 ms @ 32 kHz (super wide-band) is 2560 samples. +#define AUDIO_BUFFER_SIZE_W16 2560 + +// There is one timestamp per each 10 ms of audio +// the audio buffer, at max, may contain 32 blocks of 10ms +// audio if the sampling frequency is 8000 Hz (80 samples per block). +// Therefore, The size of the buffer where we keep timestamps +// is defined as follows +#define TIMESTAMP_BUFFER_SIZE_W32 (AUDIO_BUFFER_SIZE_W16/80) + +// The maximum size of a payload, that is 60 ms of PCM-16 @ 32 kHz stereo +#define MAX_PAYLOAD_SIZE_BYTE 7680 + +// General codec specific defines +const int kIsacWbDefaultRate = 32000; +const int kIsacSwbDefaultRate = 56000; +const int kIsacPacSize480 = 480; +const int kIsacPacSize960 = 960; +const int kIsacPacSize1440 = 1440; + +// An encoded bit-stream is labeled by one of the following enumerators. +// +// kNoEncoding : There has been no encoding. +// kActiveNormalEncoded : Active audio frame coded by the codec. +// kPassiveNormalEncoded : Passive audio frame coded by the codec. +// kPassiveDTXNB : Passive audio frame coded by narrow-band CN. +// kPassiveDTXWB : Passive audio frame coded by wide-band CN. +// kPassiveDTXSWB : Passive audio frame coded by super-wide-band CN. +// kPassiveDTXFB : Passive audio frame coded by full-band CN. +enum WebRtcACMEncodingType { + kNoEncoding, + kActiveNormalEncoded, + kPassiveNormalEncoded, + kPassiveDTXNB, + kPassiveDTXWB, + kPassiveDTXSWB, + kPassiveDTXFB +}; + +// A structure which contains codec parameters. For instance, used when +// initializing encoder and decoder. +// +// codec_inst: c.f. common_types.h +// enable_dtx: set true to enable DTX. If codec does not have +// internal DTX, this will enable VAD. +// enable_vad: set true to enable VAD. +// vad_mode: VAD mode, c.f. audio_coding_module_typedefs.h +// for possible values. +struct WebRtcACMCodecParams { + CodecInst codec_inst; + bool enable_dtx; + bool enable_vad; + ACMVADMode vad_mode; +}; + +} // namespace webrtc + +#endif // WEBRTC_MODULES_AUDIO_CODING_MAIN_SOURCE_ACM_COMMON_DEFS_H_ diff --git a/webrtc/modules/audio_coding/main/acm2/acm_dtmf_playout.cc b/webrtc/modules/audio_coding/main/acm2/acm_dtmf_playout.cc new file mode 100644 index 0000000000..b6b91029de --- /dev/null +++ b/webrtc/modules/audio_coding/main/acm2/acm_dtmf_playout.cc @@ -0,0 +1,86 @@ +/* + * 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/source/acm_dtmf_playout.h" + +#ifdef WEBRTC_CODEC_AVT +#include "webrtc/modules/audio_coding/main/source/acm_common_defs.h" +#include "webrtc/modules/audio_coding/main/source/acm_receiver.h" +#include "webrtc/system_wrappers/interface/trace.h" +#endif + +namespace webrtc { + +#ifndef WEBRTC_CODEC_AVT + +ACMDTMFPlayout::ACMDTMFPlayout(int16_t /* codec_id */) { return; } + +ACMDTMFPlayout::~ACMDTMFPlayout() { return; } + +int16_t ACMDTMFPlayout::InternalEncode(uint8_t* /* bitstream */, + int16_t* /* bitstream_len_byte */) { + return -1; +} + +int16_t ACMDTMFPlayout::InternalInitEncoder( + WebRtcACMCodecParams* /* codec_params */) { + return -1; +} + +ACMGenericCodec* ACMDTMFPlayout::CreateInstance(void) { return NULL; } + +int16_t ACMDTMFPlayout::InternalCreateEncoder() { return -1; } + +void ACMDTMFPlayout::InternalDestructEncoderInst(void* /* ptr_inst */) { + return; +} + +void ACMDTMFPlayout::DestructEncoderSafe() { + return; +} + +#else //===================== Actual Implementation ======================= + +ACMDTMFPlayout::ACMDTMFPlayout(int16_t codec_id) { codec_id_ = codec_id; } + +ACMDTMFPlayout::~ACMDTMFPlayout() { return; } + +int16_t ACMDTMFPlayout::InternalEncode(uint8_t* /* bitstream */, + int16_t* /* bitstream_len_byte */) { + return 0; +} + +int16_t ACMDTMFPlayout::InternalInitEncoder( + WebRtcACMCodecParams* /* codec_params */) { + // This codec does not need initialization, + // DTMFPlayout has no instance + return 0; +} + +ACMGenericCodec* ACMDTMFPlayout::CreateInstance(void) { return NULL; } + +int16_t ACMDTMFPlayout::InternalCreateEncoder() { + // DTMFPlayout has no instance + return 0; +} + +void ACMDTMFPlayout::InternalDestructEncoderInst(void* /* ptr_inst */) { + // DTMFPlayout has no instance + return; +} + +void ACMDTMFPlayout::DestructEncoderSafe() { + // DTMFPlayout has no instance + return; +} + +#endif + +} // namespace webrtc diff --git a/webrtc/modules/audio_coding/main/acm2/acm_dtmf_playout.h b/webrtc/modules/audio_coding/main/acm2/acm_dtmf_playout.h new file mode 100644 index 0000000000..e16653cdf6 --- /dev/null +++ b/webrtc/modules/audio_coding/main/acm2/acm_dtmf_playout.h @@ -0,0 +1,40 @@ +/* + * 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. + */ + +#ifndef WEBRTC_MODULES_AUDIO_CODING_MAIN_SOURCE_ACM_DTMF_PLAYOUT_H_ +#define WEBRTC_MODULES_AUDIO_CODING_MAIN_SOURCE_ACM_DTMF_PLAYOUT_H_ + +#include "webrtc/modules/audio_coding/main/source/acm_generic_codec.h" + +namespace webrtc { + +class ACMDTMFPlayout : public ACMGenericCodec { + public: + explicit ACMDTMFPlayout(int16_t codec_id); + ~ACMDTMFPlayout(); + + // for FEC + ACMGenericCodec* CreateInstance(void); + + int16_t InternalEncode(uint8_t* bitstream, int16_t* bitstream_len_byte); + + int16_t InternalInitEncoder(WebRtcACMCodecParams* codec_params); + + protected: + void DestructEncoderSafe(); + + int16_t InternalCreateEncoder(); + + void InternalDestructEncoderInst(void* ptr_inst); +}; + +} // namespace webrtc + +#endif // WEBRTC_MODULES_AUDIO_CODING_MAIN_SOURCE_ACM_DTMF_PLAYOUT_H_ diff --git a/webrtc/modules/audio_coding/main/acm2/acm_g722.cc b/webrtc/modules/audio_coding/main/acm2/acm_g722.cc new file mode 100644 index 0000000000..6ba0d7b4a7 --- /dev/null +++ b/webrtc/modules/audio_coding/main/acm2/acm_g722.cc @@ -0,0 +1,197 @@ +/* + * 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/source/acm_g722.h" + +#ifdef WEBRTC_CODEC_G722 +#include "webrtc/modules/audio_coding/codecs/g722/include/g722_interface.h" +#include "webrtc/modules/audio_coding/main/source/acm_codec_database.h" +#include "webrtc/modules/audio_coding/main/source/acm_common_defs.h" +#include "webrtc/system_wrappers/interface/trace.h" +#endif + +namespace webrtc { + +#ifndef WEBRTC_CODEC_G722 + +ACMG722::ACMG722(int16_t /* codec_id */) + : ptr_enc_str_(NULL), + encoder_inst_ptr_(NULL), + encoder_inst_ptr_right_(NULL) {} + +ACMG722::~ACMG722() {} + +int32_t ACMG722::Add10MsDataSafe(const uint32_t /* timestamp */, + const int16_t* /* data */, + const uint16_t /* length_smpl */, + const uint8_t /* audio_channel */) { + return -1; +} + +int16_t ACMG722::InternalEncode(uint8_t* /* bitstream */, + int16_t* /* bitstream_len_byte */) { + return -1; +} + +int16_t ACMG722::InternalInitEncoder(WebRtcACMCodecParams* /* codec_params */) { + return -1; +} + +ACMGenericCodec* ACMG722::CreateInstance(void) { return NULL; } + +int16_t ACMG722::InternalCreateEncoder() { return -1; } + +void ACMG722::DestructEncoderSafe() { return; } + +void ACMG722::InternalDestructEncoderInst(void* /* ptr_inst */) { return; } + +#else //===================== Actual Implementation ======================= + +// Encoder and decoder memory +struct ACMG722EncStr { + G722EncInst* inst; // instance for left channel in case of stereo + G722EncInst* inst_right; // instance for right channel in case of stereo +}; +struct ACMG722DecStr { + G722DecInst* inst; // instance for left channel in case of stereo + G722DecInst* inst_right; // instance for right channel in case of stereo +}; + +ACMG722::ACMG722(int16_t codec_id) + : encoder_inst_ptr_(NULL), encoder_inst_ptr_right_(NULL) { + ptr_enc_str_ = new ACMG722EncStr; + if (ptr_enc_str_ != NULL) { + ptr_enc_str_->inst = NULL; + ptr_enc_str_->inst_right = NULL; + } + codec_id_ = codec_id; + return; +} + +ACMG722::~ACMG722() { + // Encoder + if (ptr_enc_str_ != NULL) { + if (ptr_enc_str_->inst != NULL) { + WebRtcG722_FreeEncoder(ptr_enc_str_->inst); + ptr_enc_str_->inst = NULL; + } + if (ptr_enc_str_->inst_right != NULL) { + WebRtcG722_FreeEncoder(ptr_enc_str_->inst_right); + ptr_enc_str_->inst_right = NULL; + } + delete ptr_enc_str_; + ptr_enc_str_ = NULL; + } + return; +} + +int32_t ACMG722::Add10MsDataSafe(const uint32_t timestamp, + const int16_t* data, + const uint16_t length_smpl, + const uint8_t audio_channel) { + return ACMGenericCodec::Add10MsDataSafe( + (timestamp >> 1), data, length_smpl, audio_channel); +} + +int16_t ACMG722::InternalEncode(uint8_t* bitstream, + int16_t* bitstream_len_byte) { + // If stereo, split input signal in left and right channel before encoding + if (num_channels_ == 2) { + int16_t left_channel[960]; + int16_t right_channel[960]; + uint8_t out_left[480]; + uint8_t out_right[480]; + int16_t len_in_bytes; + for (int i = 0, j = 0; i < frame_len_smpl_ * 2; i += 2, j++) { + left_channel[j] = in_audio_[in_audio_ix_read_ + i]; + right_channel[j] = in_audio_[in_audio_ix_read_ + i + 1]; + } + len_in_bytes = WebRtcG722_Encode( + encoder_inst_ptr_, left_channel, frame_len_smpl_, + reinterpret_cast(out_left)); + len_in_bytes += WebRtcG722_Encode(encoder_inst_ptr_right_, + right_channel, + frame_len_smpl_, + reinterpret_cast(out_right)); + *bitstream_len_byte = len_in_bytes; + + // Interleave the 4 bits per sample from left and right channel + for (int i = 0, j = 0; i < len_in_bytes; i += 2, j++) { + bitstream[i] = (out_left[j] & 0xF0) + (out_right[j] >> 4); + bitstream[i + 1] = ((out_left[j] & 0x0F) << 4) + (out_right[j] & 0x0F); + } + } else { + *bitstream_len_byte = WebRtcG722_Encode( + encoder_inst_ptr_, &in_audio_[in_audio_ix_read_], frame_len_smpl_, + reinterpret_cast(bitstream)); + } + + // increment the read index this tell the caller how far + // we have gone forward in reading the audio buffer + in_audio_ix_read_ += frame_len_smpl_ * num_channels_; + return *bitstream_len_byte; +} + +int16_t ACMG722::InternalInitEncoder(WebRtcACMCodecParams* codec_params) { + if (codec_params->codec_inst.channels == 2) { + // Create codec struct for right channel + if (ptr_enc_str_->inst_right == NULL) { + WebRtcG722_CreateEncoder(&ptr_enc_str_->inst_right); + if (ptr_enc_str_->inst_right == NULL) { + return -1; + } + } + encoder_inst_ptr_right_ = ptr_enc_str_->inst_right; + if (WebRtcG722_EncoderInit(encoder_inst_ptr_right_) < 0) { + return -1; + } + } + + return WebRtcG722_EncoderInit(encoder_inst_ptr_); +} + +ACMGenericCodec* ACMG722::CreateInstance(void) { return NULL; } + +int16_t ACMG722::InternalCreateEncoder() { + if (ptr_enc_str_ == NULL) { + // this structure must be created at the costructor + // if it is still NULL then there is a probelm and + // we dont continue + return -1; + } + WebRtcG722_CreateEncoder(&ptr_enc_str_->inst); + if (ptr_enc_str_->inst == NULL) { + return -1; + } + encoder_inst_ptr_ = ptr_enc_str_->inst; + return 0; +} + +void ACMG722::DestructEncoderSafe() { + if (ptr_enc_str_ != NULL) { + if (ptr_enc_str_->inst != NULL) { + WebRtcG722_FreeEncoder(ptr_enc_str_->inst); + ptr_enc_str_->inst = NULL; + } + } + encoder_exist_ = false; + encoder_initialized_ = false; +} + +void ACMG722::InternalDestructEncoderInst(void* ptr_inst) { + if (ptr_inst != NULL) { + WebRtcG722_FreeEncoder(static_cast(ptr_inst)); + } + return; +} + +#endif + +} // namespace webrtc diff --git a/webrtc/modules/audio_coding/main/acm2/acm_g722.h b/webrtc/modules/audio_coding/main/acm2/acm_g722.h new file mode 100644 index 0000000000..21a0fdb2e9 --- /dev/null +++ b/webrtc/modules/audio_coding/main/acm2/acm_g722.h @@ -0,0 +1,57 @@ +/* + * 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. + */ + +#ifndef WEBRTC_MODULES_AUDIO_CODING_MAIN_SOURCE_ACM_G722_H_ +#define WEBRTC_MODULES_AUDIO_CODING_MAIN_SOURCE_ACM_G722_H_ + +#include "webrtc/modules/audio_coding/main/source/acm_generic_codec.h" + +typedef struct WebRtcG722EncInst G722EncInst; +typedef struct WebRtcG722DecInst G722DecInst; + +namespace webrtc { + +// Forward declaration. +struct ACMG722EncStr; +struct ACMG722DecStr; + +class ACMG722 : public ACMGenericCodec { + public: + explicit ACMG722(int16_t codec_id); + ~ACMG722(); + + // For FEC. + ACMGenericCodec* CreateInstance(void); + + int16_t InternalEncode(uint8_t* bitstream, int16_t* bitstream_len_byte); + + int16_t InternalInitEncoder(WebRtcACMCodecParams* codec_params); + + protected: + int32_t Add10MsDataSafe(const uint32_t timestamp, + const int16_t* data, + const uint16_t length_smpl, + const uint8_t audio_channel); + + void DestructEncoderSafe(); + + int16_t InternalCreateEncoder(); + + void InternalDestructEncoderInst(void* ptr_inst); + + ACMG722EncStr* ptr_enc_str_; + + G722EncInst* encoder_inst_ptr_; + G722EncInst* encoder_inst_ptr_right_; // Prepared for stereo +}; + +} // namespace webrtc + +#endif // WEBRTC_MODULES_AUDIO_CODING_MAIN_SOURCE_ACM_G722_H_ diff --git a/webrtc/modules/audio_coding/main/acm2/acm_g7221.cc b/webrtc/modules/audio_coding/main/acm2/acm_g7221.cc new file mode 100644 index 0000000000..65b34b08fb --- /dev/null +++ b/webrtc/modules/audio_coding/main/acm2/acm_g7221.cc @@ -0,0 +1,326 @@ +/* + * 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/source/acm_g7221.h" + +#ifdef WEBRTC_CODEC_G722_1 +// NOTE! G.722.1 is not included in the open-source package. The following +// interface file is needed: +#include "webrtc/modules/audio_coding/main/codecs/g7221/interface/g7221_interface.h" +#include "webrtc/modules/audio_coding/main/source/acm_codec_database.h" +#include "webrtc/modules/audio_coding/main/source/acm_common_defs.h" +#include "webrtc/system_wrappers/interface/trace.h" + +// The API in the header file should match the one below. +// +// int16_t WebRtcG7221_CreateEnc16(G722_1_16_encinst_t_** enc_inst); +// int16_t WebRtcG7221_CreateEnc24(G722_1_24_encinst_t_** enc_inst); +// int16_t WebRtcG7221_CreateEnc32(G722_1_32_encinst_t_** enc_inst); +// int16_t WebRtcG7221_CreateDec16(G722_1_16_decinst_t_** dec_inst); +// int16_t WebRtcG7221_CreateDec24(G722_1_24_decinst_t_** dec_inst); +// int16_t WebRtcG7221_CreateDec32(G722_1_32_decinst_t_** dec_inst); +// +// int16_t WebRtcG7221_FreeEnc16(G722_1_16_encinst_t_** enc_inst); +// int16_t WebRtcG7221_FreeEnc24(G722_1_24_encinst_t_** enc_inst); +// int16_t WebRtcG7221_FreeEnc32(G722_1_32_encinst_t_** enc_inst); +// int16_t WebRtcG7221_FreeDec16(G722_1_16_decinst_t_** dec_inst); +// int16_t WebRtcG7221_FreeDec24(G722_1_24_decinst_t_** dec_inst); +// int16_t WebRtcG7221_FreeDec32(G722_1_32_decinst_t_** dec_inst); +// +// int16_t WebRtcG7221_EncoderInit16(G722_1_16_encinst_t_* enc_inst); +// int16_t WebRtcG7221_EncoderInit24(G722_1_24_encinst_t_* enc_inst); +// int16_t WebRtcG7221_EncoderInit32(G722_1_32_encinst_t_* enc_inst); +// int16_t WebRtcG7221_DecoderInit16(G722_1_16_decinst_t_* dec_inst); +// int16_t WebRtcG7221_DecoderInit24(G722_1_24_decinst_t_* dec_inst); +// int16_t WebRtcG7221_DecoderInit32(G722_1_32_decinst_t_* dec_inst); +// +// int16_t WebRtcG7221_Encode16(G722_1_16_encinst_t_* enc_inst, +// int16_t* input, +// int16_t len, +// int16_t* output); +// int16_t WebRtcG7221_Encode24(G722_1_24_encinst_t_* enc_inst, +// int16_t* input, +// int16_t len, +// int16_t* output); +// int16_t WebRtcG7221_Encode32(G722_1_32_encinst_t_* enc_inst, +// int16_t* input, +// int16_t len, +// int16_t* output); +// +// int16_t WebRtcG7221_Decode16(G722_1_16_decinst_t_* dec_inst, +// int16_t* bitstream, +// int16_t len, +// int16_t* output); +// int16_t WebRtcG7221_Decode24(G722_1_24_decinst_t_* dec_inst, +// int16_t* bitstream, +// int16_t len, +// int16_t* output); +// int16_t WebRtcG7221_Decode32(G722_1_32_decinst_t_* dec_inst, +// int16_t* bitstream, +// int16_t len, +// int16_t* output); +// +// int16_t WebRtcG7221_DecodePlc16(G722_1_16_decinst_t_* dec_inst, +// int16_t* output, +// int16_t nr_lost_frames); +// int16_t WebRtcG7221_DecodePlc24(G722_1_24_decinst_t_* dec_inst, +// int16_t* output, +// int16_t nr_lost_frames); +// int16_t WebRtcG7221_DecodePlc32(G722_1_32_decinst_t_* dec_inst, +// int16_t* output, +// int16_t nr_lost_frames); +#endif + +namespace webrtc { + +#ifndef WEBRTC_CODEC_G722_1 + +ACMG722_1::ACMG722_1(int16_t /* codec_id */) + : operational_rate_(-1), + encoder_inst_ptr_(NULL), + encoder_inst_ptr_right_(NULL), + encoder_inst16_ptr_(NULL), + encoder_inst16_ptr_right_(NULL), + encoder_inst24_ptr_(NULL), + encoder_inst24_ptr_right_(NULL), + encoder_inst32_ptr_(NULL), + encoder_inst32_ptr_right_(NULL) { + return; +} + +ACMG722_1::~ACMG722_1() { return; } + +int16_t ACMG722_1::InternalEncode(uint8_t* /* bitstream */, + int16_t* /* bitstream_len_byte */) { + return -1; +} + +int16_t ACMG722_1::InternalInitEncoder( + WebRtcACMCodecParams* /* codec_params */) { + return -1; +} + +ACMGenericCodec* ACMG722_1::CreateInstance(void) { return NULL; } + +int16_t ACMG722_1::InternalCreateEncoder() { return -1; } + +void ACMG722_1::DestructEncoderSafe() { return; } + +void ACMG722_1::InternalDestructEncoderInst(void* /* ptr_inst */) { return; } + +#else //===================== Actual Implementation ======================= +ACMG722_1::ACMG722_1(int16_t codec_id) + : encoder_inst_ptr_(NULL), + encoder_inst_ptr_right_(NULL), + encoder_inst16_ptr_(NULL), + encoder_inst16_ptr_right_(NULL), + encoder_inst24_ptr_(NULL), + encoder_inst24_ptr_right_(NULL), + encoder_inst32_ptr_(NULL), + encoder_inst32_ptr_right_(NULL) { + codec_id_ = codec_id; + if (codec_id_ == ACMCodecDB::kG722_1_16) { + operational_rate_ = 16000; + } else if (codec_id_ == ACMCodecDB::kG722_1_24) { + operational_rate_ = 24000; + } else if (codec_id_ == ACMCodecDB::kG722_1_32) { + operational_rate_ = 32000; + } else { + operational_rate_ = -1; + } + return; +} + +ACMG722_1::~ACMG722_1() { + if (encoder_inst_ptr_ != NULL) { + delete encoder_inst_ptr_; + encoder_inst_ptr_ = NULL; + } + if (encoder_inst_ptr_right_ != NULL) { + delete encoder_inst_ptr_right_; + encoder_inst_ptr_right_ = NULL; + } + + switch (operational_rate_) { + case 16000: { + encoder_inst16_ptr_ = NULL; + encoder_inst16_ptr_right_ = NULL; + break; + } + case 24000: { + encoder_inst24_ptr_ = NULL; + encoder_inst24_ptr_right_ = NULL; + break; + } + case 32000: { + encoder_inst32_ptr_ = NULL; + encoder_inst32_ptr_right_ = NULL; + break; + } + default: { + break; + } + } + return; +} + +int16_t ACMG722_1::InternalEncode(uint8_t* bitstream, + int16_t* bitstream_len_byte) { + int16_t left_channel[320]; + int16_t right_channel[320]; + int16_t len_in_bytes; + int16_t out_bits[160]; + + // If stereo, split input signal in left and right channel before encoding + if (num_channels_ == 2) { + for (int i = 0, j = 0; i < frame_len_smpl_ * 2; i += 2, j++) { + left_channel[j] = in_audio_[in_audio_ix_read_ + i]; + right_channel[j] = in_audio_[in_audio_ix_read_ + i + 1]; + } + } else { + memcpy(left_channel, &in_audio_[in_audio_ix_read_], 320); + } + + switch (operational_rate_) { + case 16000: { + len_in_bytes = WebRtcG7221_Encode16(encoder_inst16_ptr_, left_channel, + 320, &out_bits[0]); + if (num_channels_ == 2) { + len_in_bytes += WebRtcG7221_Encode16(encoder_inst16_ptr_right_, + right_channel, 320, + &out_bits[len_in_bytes / 2]); + } + break; + } + case 24000: { + len_in_bytes = WebRtcG7221_Encode24(encoder_inst24_ptr_, left_channel, + 320, &out_bits[0]); + if (num_channels_ == 2) { + len_in_bytes += WebRtcG7221_Encode24(encoder_inst24_ptr_right_, + right_channel, 320, + &out_bits[len_in_bytes / 2]); + } + break; + } + case 32000: { + len_in_bytes = WebRtcG7221_Encode32(encoder_inst32_ptr_, left_channel, + 320, &out_bits[0]); + if (num_channels_ == 2) { + len_in_bytes += WebRtcG7221_Encode32(encoder_inst32_ptr_right_, + right_channel, 320, + &out_bits[len_in_bytes / 2]); + } + break; + } + default: { + WEBRTC_TRACE(webrtc::kTraceError, webrtc::kTraceAudioCoding, unique_id_, + "InternalInitEncode: Wrong rate for G722_1."); + return -1; + } + } + memcpy(bitstream, out_bits, len_in_bytes); + *bitstream_len_byte = len_in_bytes; + + // increment the read index this tell the caller that how far + // we have gone forward in reading the audio buffer + in_audio_ix_read_ += 320 * num_channels_; + return *bitstream_len_byte; +} + +int16_t ACMG722_1::InternalInitEncoder(WebRtcACMCodecParams* codec_params) { + int16_t ret; + + switch (operational_rate_) { + case 16000: { + ret = WebRtcG7221_EncoderInit16(encoder_inst16_ptr_right_); + if (ret < 0) { + return ret; + } + return WebRtcG7221_EncoderInit16(encoder_inst16_ptr_); + } + case 24000: { + ret = WebRtcG7221_EncoderInit24(encoder_inst24_ptr_right_); + if (ret < 0) { + return ret; + } + return WebRtcG7221_EncoderInit24(encoder_inst24_ptr_); + } + case 32000: { + ret = WebRtcG7221_EncoderInit32(encoder_inst32_ptr_right_); + if (ret < 0) { + return ret; + } + return WebRtcG7221_EncoderInit32(encoder_inst32_ptr_); + } + default: { + WEBRTC_TRACE(webrtc::kTraceError, webrtc::kTraceAudioCoding, + unique_id_, "InternalInitEncoder: Wrong rate for G722_1."); + return -1; + } + } +} + +ACMGenericCodec* ACMG722_1::CreateInstance(void) { return NULL; } + +int16_t ACMG722_1::InternalCreateEncoder() { + if ((encoder_inst_ptr_ == NULL) || (encoder_inst_ptr_right_ == NULL)) { + return -1; + } + switch (operational_rate_) { + case 16000: { + WebRtcG7221_CreateEnc16(&encoder_inst16_ptr_); + WebRtcG7221_CreateEnc16(&encoder_inst16_ptr_right_); + break; + } + case 24000: { + WebRtcG7221_CreateEnc24(&encoder_inst24_ptr_); + WebRtcG7221_CreateEnc24(&encoder_inst24_ptr_right_); + break; + } + case 32000: { + WebRtcG7221_CreateEnc32(&encoder_inst32_ptr_); + WebRtcG7221_CreateEnc32(&encoder_inst32_ptr_right_); + break; + } + default: { + WEBRTC_TRACE(webrtc::kTraceError, webrtc::kTraceAudioCoding, unique_id_, + "InternalCreateEncoder: Wrong rate for G722_1."); + return -1; + } + } + return 0; +} + +void ACMG722_1::DestructEncoderSafe() { + encoder_exist_ = false; + encoder_initialized_ = false; + if (encoder_inst_ptr_ != NULL) { + delete encoder_inst_ptr_; + encoder_inst_ptr_ = NULL; + } + if (encoder_inst_ptr_right_ != NULL) { + delete encoder_inst_ptr_right_; + encoder_inst_ptr_right_ = NULL; + } + encoder_inst16_ptr_ = NULL; + encoder_inst24_ptr_ = NULL; + encoder_inst32_ptr_ = NULL; +} + +void ACMG722_1::InternalDestructEncoderInst(void* ptr_inst) { + if (ptr_inst != NULL) { + delete ptr_inst; + } + return; +} + +#endif + +} // namespace webrtc diff --git a/webrtc/modules/audio_coding/main/acm2/acm_g7221.h b/webrtc/modules/audio_coding/main/acm2/acm_g7221.h new file mode 100644 index 0000000000..2b532db93a --- /dev/null +++ b/webrtc/modules/audio_coding/main/acm2/acm_g7221.h @@ -0,0 +1,62 @@ +/* + * 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. + */ + +#ifndef WEBRTC_MODULES_AUDIO_CODING_MAIN_SOURCE_ACM_G7221_H_ +#define WEBRTC_MODULES_AUDIO_CODING_MAIN_SOURCE_ACM_G7221_H_ + +#include "webrtc/modules/audio_coding/main/source/acm_generic_codec.h" + +// forward declaration +struct G722_1_16_encinst_t_; +struct G722_1_16_decinst_t_; +struct G722_1_24_encinst_t_; +struct G722_1_24_decinst_t_; +struct G722_1_32_encinst_t_; +struct G722_1_32_decinst_t_; +struct G722_1_Inst_t_; + +namespace webrtc { + +class ACMG722_1 : public ACMGenericCodec { + public: + explicit ACMG722_1(int16_t codec_id); + ~ACMG722_1(); + + // for FEC + ACMGenericCodec* CreateInstance(void); + + int16_t InternalEncode(uint8_t* bitstream, int16_t* bitstream_len_byte); + + int16_t InternalInitEncoder(WebRtcACMCodecParams* codec_params); + + protected: + void DestructEncoderSafe(); + + int16_t InternalCreateEncoder(); + + void InternalDestructEncoderInst(void* ptr_inst); + + int32_t operational_rate_; + + G722_1_Inst_t_* encoder_inst_ptr_; + G722_1_Inst_t_* encoder_inst_ptr_right_; // Used in stereo mode + + // Only one set of these pointer is valid at any instance + G722_1_16_encinst_t_* encoder_inst16_ptr_; + G722_1_16_encinst_t_* encoder_inst16_ptr_right_; + G722_1_24_encinst_t_* encoder_inst24_ptr_; + G722_1_24_encinst_t_* encoder_inst24_ptr_right_; + G722_1_32_encinst_t_* encoder_inst32_ptr_; + G722_1_32_encinst_t_* encoder_inst32_ptr_right_; +}; + +} // namespace webrtc + +#endif // WEBRTC_MODULES_AUDIO_CODING_MAIN_SOURCE_ACM_G7221_H_ diff --git a/webrtc/modules/audio_coding/main/acm2/acm_g7221c.cc b/webrtc/modules/audio_coding/main/acm2/acm_g7221c.cc new file mode 100644 index 0000000000..b426d1f8b1 --- /dev/null +++ b/webrtc/modules/audio_coding/main/acm2/acm_g7221c.cc @@ -0,0 +1,332 @@ +/* + * 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/source/acm_g7221c.h" + +#ifdef WEBRTC_CODEC_G722_1C +// NOTE! G.722.1C is not included in the open-source package. The following +// interface file is needed: +#include "webrtc/modules/audio_coding/main/codecs/g7221c/interface/g7221c_interface.h" +#include "webrtc/modules/audio_coding/main/source/acm_codec_database.h" +#include "webrtc/modules/audio_coding/main/source/acm_common_defs.h" +#include "webrtc/system_wrappers/interface/trace.h" + +// The API in the header file should match the one below. +// +// int16_t WebRtcG7221C_CreateEnc24(G722_1C_24_encinst_t_** enc_inst); +// int16_t WebRtcG7221C_CreateEnc32(G722_1C_32_encinst_t_** enc_inst); +// int16_t WebRtcG7221C_CreateEnc48(G722_1C_48_encinst_t_** enc_inst); +// int16_t WebRtcG7221C_CreateDec24(G722_1C_24_decinst_t_** dec_inst); +// int16_t WebRtcG7221C_CreateDec32(G722_1C_32_decinst_t_** dec_inst); +// int16_t WebRtcG7221C_CreateDec48(G722_1C_48_decinst_t_** dec_inst); +// +// int16_t WebRtcG7221C_FreeEnc24(G722_1C_24_encinst_t_** enc_inst); +// int16_t WebRtcG7221C_FreeEnc32(G722_1C_32_encinst_t_** enc_inst); +// int16_t WebRtcG7221C_FreeEnc48(G722_1C_48_encinst_t_** enc_inst); +// int16_t WebRtcG7221C_FreeDec24(G722_1C_24_decinst_t_** dec_inst); +// int16_t WebRtcG7221C_FreeDec32(G722_1C_32_decinst_t_** dec_inst); +// int16_t WebRtcG7221C_FreeDec48(G722_1C_48_decinst_t_** dec_inst); +// +// int16_t WebRtcG7221C_EncoderInit24(G722_1C_24_encinst_t_* enc_inst); +// int16_t WebRtcG7221C_EncoderInit32(G722_1C_32_encinst_t_* enc_inst); +// int16_t WebRtcG7221C_EncoderInit48(G722_1C_48_encinst_t_* enc_inst); +// int16_t WebRtcG7221C_DecoderInit24(G722_1C_24_decinst_t_* dec_inst); +// int16_t WebRtcG7221C_DecoderInit32(G722_1C_32_decinst_t_* dec_inst); +// int16_t WebRtcG7221C_DecoderInit48(G722_1C_48_decinst_t_* dec_inst); +// +// int16_t WebRtcG7221C_Encode24(G722_1C_24_encinst_t_* enc_inst, +// int16_t* input, +// int16_t len, +// int16_t* output); +// int16_t WebRtcG7221C_Encode32(G722_1C_32_encinst_t_* enc_inst, +// int16_t* input, +// int16_t len, +// int16_t* output); +// int16_t WebRtcG7221C_Encode48(G722_1C_48_encinst_t_* enc_inst, +// int16_t* input, +// int16_t len, +// int16_t* output); +// +// int16_t WebRtcG7221C_Decode24(G722_1C_24_decinst_t_* dec_inst, +// int16_t* bitstream, +// int16_t len, +// int16_t* output); +// int16_t WebRtcG7221C_Decode32(G722_1C_32_decinst_t_* dec_inst, +// int16_t* bitstream, +// int16_t len, +// int16_t* output); +// int16_t WebRtcG7221C_Decode48(G722_1C_48_decinst_t_* dec_inst, +// int16_t* bitstream, +// int16_t len, +// int16_t* output); +// +// int16_t WebRtcG7221C_DecodePlc24(G722_1C_24_decinst_t_* dec_inst, +// int16_t* output, +// int16_t nr_lost_frames); +// int16_t WebRtcG7221C_DecodePlc32(G722_1C_32_decinst_t_* dec_inst, +// int16_t* output, +// int16_t nr_lost_frames); +// int16_t WebRtcG7221C_DecodePlc48(G722_1C_48_decinst_t_* dec_inst, +// int16_t* output, +// int16_t nr_lost_frames); +#endif + +namespace webrtc { + +#ifndef WEBRTC_CODEC_G722_1C + +ACMG722_1C::ACMG722_1C(int16_t /* codec_id */) + : operational_rate_(-1), + encoder_inst_ptr_(NULL), + encoder_inst_ptr_right_(NULL), + encoder_inst24_ptr_(NULL), + encoder_inst24_ptr_right_(NULL), + encoder_inst32_ptr_(NULL), + encoder_inst32_ptr_right_(NULL), + encoder_inst48_ptr_(NULL), + encoder_inst48_ptr_right_(NULL) { + return; +} + +ACMG722_1C::~ACMG722_1C() { return; } + +int16_t ACMG722_1C::InternalEncode(uint8_t* /* bitstream */, + int16_t* /* bitstream_len_byte */) { + return -1; +} + +int16_t ACMG722_1C::InternalInitEncoder( + WebRtcACMCodecParams* /* codec_params */) { + return -1; +} + +ACMGenericCodec* ACMG722_1C::CreateInstance(void) { return NULL; } + +int16_t ACMG722_1C::InternalCreateEncoder() { return -1; } + +void ACMG722_1C::DestructEncoderSafe() { return; } + +void ACMG722_1C::InternalDestructEncoderInst(void* /* ptr_inst */) { return; } + +#else //===================== Actual Implementation ======================= +ACMG722_1C::ACMG722_1C(int16_t codec_id) + : encoder_inst_ptr_(NULL), + encoder_inst_ptr_right_(NULL), + encoder_inst24_ptr_(NULL), + encoder_inst24_ptr_right_(NULL), + encoder_inst32_ptr_(NULL), + encoder_inst32_ptr_right_(NULL), + encoder_inst48_ptr_(NULL), + encoder_inst48_ptr_right_(NULL) { + codec_id_ = codec_id; + if (codec_id_ == ACMCodecDB::kG722_1C_24) { + operational_rate_ = 24000; + } else if (codec_id_ == ACMCodecDB::kG722_1C_32) { + operational_rate_ = 32000; + } else if (codec_id_ == ACMCodecDB::kG722_1C_48) { + operational_rate_ = 48000; + } else { + WEBRTC_TRACE(webrtc::kTraceError, webrtc::kTraceAudioCoding, unique_id_, + "Wrong codec id for G722_1c."); + operational_rate_ = -1; + } + return; +} + +ACMG722_1C::~ACMG722_1C() { + if (encoder_inst_ptr_ != NULL) { + delete encoder_inst_ptr_; + encoder_inst_ptr_ = NULL; + } + if (encoder_inst_ptr_right_ != NULL) { + delete encoder_inst_ptr_right_; + encoder_inst_ptr_right_ = NULL; + } + + switch (operational_rate_) { + case 24000: { + encoder_inst24_ptr_ = NULL; + encoder_inst24_ptr_right_ = NULL; + break; + } + case 32000: { + encoder_inst32_ptr_ = NULL; + encoder_inst32_ptr_right_ = NULL; + break; + } + case 48000: { + encoder_inst48_ptr_ = NULL; + encoder_inst48_ptr_right_ = NULL; + break; + } + default: { + WEBRTC_TRACE(webrtc::kTraceError, webrtc::kTraceAudioCoding, unique_id_, + "Wrong rate for G722_1c."); + break; + } + } + return; +} + +int16_t ACMG722_1C::InternalEncode(uint8_t* bitstream, + int16_t* bitstream_len_byte) { + int16_t left_channel[640]; + int16_t right_channel[640]; + int16_t len_in_bytes; + int16_t out_bits[240]; + + // If stereo, split input signal in left and right channel before encoding + if (num_channels_ == 2) { + for (int i = 0, j = 0; i < frame_len_smpl_ * 2; i += 2, j++) { + left_channel[j] = in_audio_[in_audio_ix_read_ + i]; + right_channel[j] = in_audio_[in_audio_ix_read_ + i + 1]; + } + } else { + memcpy(left_channel, &in_audio_[in_audio_ix_read_], 640); + } + + switch (operational_rate_) { + case 24000: { + len_in_bytes = WebRtcG7221C_Encode24(encoder_inst24_ptr_, left_channel, + 640, &out_bits[0]); + if (num_channels_ == 2) { + len_in_bytes += WebRtcG7221C_Encode24(encoder_inst24_ptr_right_, + right_channel, 640, + &out_bits[len_in_bytes / 2]); + } + break; + } + case 32000: { + len_in_bytes = WebRtcG7221C_Encode32(encoder_inst32_ptr_, left_channel, + 640, &out_bits[0]); + if (num_channels_ == 2) { + len_in_bytes += WebRtcG7221C_Encode32(encoder_inst32_ptr_right_, + right_channel, 640, + &out_bits[len_in_bytes / 2]); + } + break; + } + case 48000: { + len_in_bytes = WebRtcG7221C_Encode48(encoder_inst48_ptr_, left_channel, + 640, &out_bits[0]); + if (num_channels_ == 2) { + len_in_bytes += WebRtcG7221C_Encode48(encoder_inst48_ptr_right_, + right_channel, 640, + &out_bits[len_in_bytes / 2]); + } + break; + } + default: { + WEBRTC_TRACE(webrtc::kTraceError, webrtc::kTraceAudioCoding, unique_id_, + "InternalEncode: Wrong rate for G722_1c."); + return -1; + } + } + + memcpy(bitstream, out_bits, len_in_bytes); + *bitstream_len_byte = len_in_bytes; + + // increment the read index this tell the caller that how far + // we have gone forward in reading the audio buffer + in_audio_ix_read_ += 640 * num_channels_; + + return *bitstream_len_byte; +} + +int16_t ACMG722_1C::InternalInitEncoder(WebRtcACMCodecParams* codec_params) { + int16_t ret; + + switch (operational_rate_) { + case 24000: { + ret = WebRtcG7221C_EncoderInit24(encoder_inst24_ptr_right_); + if (ret < 0) { + return ret; + } + return WebRtcG7221C_EncoderInit24(encoder_inst24_ptr_); + } + case 32000: { + ret = WebRtcG7221C_EncoderInit32(encoder_inst32_ptr_right_); + if (ret < 0) { + return ret; + } + return WebRtcG7221C_EncoderInit32(encoder_inst32_ptr_); + } + case 48000: { + ret = WebRtcG7221C_EncoderInit48(encoder_inst48_ptr_right_); + if (ret < 0) { + return ret; + } + return WebRtcG7221C_EncoderInit48(encoder_inst48_ptr_); + } + default: { + WEBRTC_TRACE(webrtc::kTraceError, webrtc::kTraceAudioCoding, unique_id_, + "InternalInitEncode: Wrong rate for G722_1c."); + return -1; + } + } +} + +ACMGenericCodec* ACMG722_1C::CreateInstance(void) { return NULL; } + +int16_t ACMG722_1C::InternalCreateEncoder() { + if ((encoder_inst_ptr_ == NULL) || (encoder_inst_ptr_right_ == NULL)) { + return -1; + } + switch (operational_rate_) { + case 24000: { + WebRtcG7221C_CreateEnc24(&encoder_inst24_ptr_); + WebRtcG7221C_CreateEnc24(&encoder_inst24_ptr_right_); + break; + } + case 32000: { + WebRtcG7221C_CreateEnc32(&encoder_inst32_ptr_); + WebRtcG7221C_CreateEnc32(&encoder_inst32_ptr_right_); + break; + } + case 48000: { + WebRtcG7221C_CreateEnc48(&encoder_inst48_ptr_); + WebRtcG7221C_CreateEnc48(&encoder_inst48_ptr_right_); + break; + } + default: { + WEBRTC_TRACE(webrtc::kTraceError, webrtc::kTraceAudioCoding, unique_id_, + "InternalCreateEncoder: Wrong rate for G722_1c."); + return -1; + } + } + return 0; +} + +void ACMG722_1C::DestructEncoderSafe() { + encoder_exist_ = false; + encoder_initialized_ = false; + if (encoder_inst_ptr_ != NULL) { + delete encoder_inst_ptr_; + encoder_inst_ptr_ = NULL; + } + if (encoder_inst_ptr_right_ != NULL) { + delete encoder_inst_ptr_right_; + encoder_inst_ptr_right_ = NULL; + } + encoder_inst24_ptr_ = NULL; + encoder_inst32_ptr_ = NULL; + encoder_inst48_ptr_ = NULL; +} + +void ACMG722_1C::InternalDestructEncoderInst(void* ptr_inst) { + if (ptr_inst != NULL) { + delete ptr_inst; + } + return; +} + +#endif + +} // namespace webrtc diff --git a/webrtc/modules/audio_coding/main/acm2/acm_g7221c.h b/webrtc/modules/audio_coding/main/acm2/acm_g7221c.h new file mode 100644 index 0000000000..d051b28b6c --- /dev/null +++ b/webrtc/modules/audio_coding/main/acm2/acm_g7221c.h @@ -0,0 +1,62 @@ +/* + * 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. + */ + +#ifndef WEBRTC_MODULES_AUDIO_CODING_MAIN_SOURCE_ACM_G7221C_H_ +#define WEBRTC_MODULES_AUDIO_CODING_MAIN_SOURCE_ACM_G7221C_H_ + +#include "webrtc/modules/audio_coding/main/source/acm_generic_codec.h" + +// forward declaration +struct G722_1C_24_encinst_t_; +struct G722_1C_24_decinst_t_; +struct G722_1C_32_encinst_t_; +struct G722_1C_32_decinst_t_; +struct G722_1C_48_encinst_t_; +struct G722_1C_48_decinst_t_; +struct G722_1_Inst_t_; + +namespace webrtc { + +class ACMG722_1C : public ACMGenericCodec { + public: + explicit ACMG722_1C(int16_t codec_id); + ~ACMG722_1C(); + + // for FEC + ACMGenericCodec* CreateInstance(void); + + int16_t InternalEncode(uint8_t* bitstream, int16_t* bitstream_len_byte); + + int16_t InternalInitEncoder(WebRtcACMCodecParams* codec_params); + + protected: + void DestructEncoderSafe(); + + int16_t InternalCreateEncoder(); + + void InternalDestructEncoderInst(void* ptr_inst); + + int32_t operational_rate_; + + G722_1_Inst_t_* encoder_inst_ptr_; + G722_1_Inst_t_* encoder_inst_ptr_right_; // Used in stereo mode + + // Only one set of these pointer is valid at any instance + G722_1C_24_encinst_t_* encoder_inst24_ptr_; + G722_1C_24_encinst_t_* encoder_inst24_ptr_right_; + G722_1C_32_encinst_t_* encoder_inst32_ptr_; + G722_1C_32_encinst_t_* encoder_inst32_ptr_right_; + G722_1C_48_encinst_t_* encoder_inst48_ptr_; + G722_1C_48_encinst_t_* encoder_inst48_ptr_right_; +}; + +} // namespace webrtc + +#endif // WEBRTC_MODULES_AUDIO_CODING_MAIN_SOURCE_ACM_G7221C_H_ diff --git a/webrtc/modules/audio_coding/main/acm2/acm_g729.cc b/webrtc/modules/audio_coding/main/acm2/acm_g729.cc new file mode 100644 index 0000000000..a2349ce4a8 --- /dev/null +++ b/webrtc/modules/audio_coding/main/acm2/acm_g729.cc @@ -0,0 +1,255 @@ +/* + * 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/source/acm_g729.h" + +#ifdef WEBRTC_CODEC_G729 +// NOTE! G.729 is not included in the open-source package. Modify this file +// or your codec API to match the function calls and names of used G.729 API +// file. +#include "webrtc/modules/audio_coding/main/codecs/g729/interface/g729_interface.h" +#include "webrtc/modules/audio_coding/main/source/acm_common_defs.h" +#include "webrtc/modules/audio_coding/main/source/acm_receiver.h" +#include "webrtc/system_wrappers/interface/trace.h" +#endif + +namespace webrtc { + +#ifndef WEBRTC_CODEC_G729 + +ACMG729::ACMG729(int16_t /* codec_id */) : encoder_inst_ptr_(NULL) {} + +ACMG729::~ACMG729() { return; } + +int16_t ACMG729::InternalEncode(uint8_t* /* bitstream */, + int16_t* /* bitstream_len_byte */) { + return -1; +} + +int16_t ACMG729::EnableDTX() { return -1; } + +int16_t ACMG729::DisableDTX() { return -1; } + +int32_t ACMG729::ReplaceInternalDTXSafe(const bool /*replace_internal_dtx */) { + return -1; +} + +int32_t ACMG729::IsInternalDTXReplacedSafe(bool* /* internal_dtx_replaced */) { + return -1; +} + +int16_t ACMG729::InternalInitEncoder(WebRtcACMCodecParams* /* codec_params */) { + return -1; +} + +ACMGenericCodec* ACMG729::CreateInstance(void) { return NULL; } + +int16_t ACMG729::InternalCreateEncoder() { return -1; } + +void ACMG729::DestructEncoderSafe() { return; } + +void ACMG729::InternalDestructEncoderInst(void* /* ptr_inst */) { return; } + +#else //===================== Actual Implementation ======================= +ACMG729::ACMG729(int16_t codec_id) + : codec_id_(codec_id), + has_internal_dtx_(), + encoder_inst_ptr_(NULL) {} + +ACMG729::~ACMG729() { + if (encoder_inst_ptr_ != NULL) { + // Delete encoder memory + WebRtcG729_FreeEnc(encoder_inst_ptr_); + encoder_inst_ptr_ = NULL; + } + return; +} + +int16_t ACMG729::InternalEncode(uint8_t* bitstream, + int16_t* bitstream_len_byte) { + // Initialize before entering the loop + int16_t num_encoded_samples = 0; + int16_t tmp_len_byte = 0; + int16_t vad_decision = 0; + *bitstream_len_byte = 0; + while (num_encoded_samples < frame_len_smpl_) { + // Call G.729 encoder with pointer to encoder memory, input + // audio, number of samples and bitsream + tmp_len_byte = WebRtcG729_Encode( + encoder_inst_ptr_, &in_audio_[in_audio_ix_read_], 80, + reinterpret_cast(&(bitstream[*bitstream_len_byte]))); + + // increment the read index this tell the caller that how far + // we have gone forward in reading the audio buffer + in_audio_ix_read_ += 80; + + // sanity check + if (tmp_len_byte < 0) { + // error has happened + *bitstream_len_byte = 0; + return -1; + } + + // increment number of written bytes + *bitstream_len_byte += tmp_len_byte; + switch (tmp_len_byte) { + case 0: { + if (0 == num_encoded_samples) { + // this is the first 10 ms in this packet and there is + // no data generated, perhaps DTX is enabled and the + // codec is not generating any bit-stream for this 10 ms. + // we do not continue encoding this frame. + return 0; + } + break; + } + case 2: { + // check if G.729 internal DTX is enabled + if (has_internal_dtx_ && dtx_enabled_) { + vad_decision = 0; + for (int16_t n = 0; n < MAX_FRAME_SIZE_10MSEC; n++) { + vad_label_[n] = vad_decision; + } + } + // we got a SID and have to send out this packet no matter + // how much audio we have encoded + return *bitstream_len_byte; + } + case 10: { + vad_decision = 1; + // this is a valid length just continue encoding + break; + } + default: { + return -1; + } + } + + // update number of encoded samples + num_encoded_samples += 80; + } + + // update VAD decision vector + if (has_internal_dtx_ && !vad_decision && dtx_enabled_) { + for (int16_t n = 0; n < MAX_FRAME_SIZE_10MSEC; n++) { + vad_label_[n] = vad_decision; + } + } + + // done encoding, return number of encoded bytes + return *bitstream_len_byte; +} + +int16_t ACMG729::EnableDTX() { + if (dtx_enabled_) { + // DTX already enabled, do nothing + return 0; + } else if (encoder_exist_) { + // Re-init the G.729 encoder to turn on DTX + if (WebRtcG729_EncoderInit(encoder_inst_ptr_, 1) < 0) { + return -1; + } + dtx_enabled_ = true; + return 0; + } else { + return -1; + } +} + +int16_t ACMG729::DisableDTX() { + if (!dtx_enabled_) { + // DTX already dissabled, do nothing + return 0; + } else if (encoder_exist_) { + // Re-init the G.729 decoder to turn off DTX + if (WebRtcG729_EncoderInit(encoder_inst_ptr_, 0) < 0) { + return -1; + } + dtx_enabled_ = false; + return 0; + } else { + // encoder doesn't exists, therefore disabling is harmless + return 0; + } +} + +int32_t ACMG729::ReplaceInternalDTXSafe(const bool replace_internal_dtx) { + // This function is used to disable the G.729 built in DTX and use an + // external instead. + + if (replace_internal_dtx == has_internal_dtx_) { + // Make sure we keep the DTX/VAD setting if possible + bool old_enable_dtx = dtx_enabled_; + bool old_enable_vad = vad_enabled_; + ACMVADMode old_mode = vad_mode_; + if (replace_internal_dtx) { + // Disable internal DTX before enabling external DTX + DisableDTX(); + } else { + // Disable external DTX before enabling internal + ACMGenericCodec::DisableDTX(); + } + has_internal_dtx_ = !replace_internal_dtx; + int16_t status = SetVADSafe(old_enable_dtx, old_enable_vad, old_mode); + // Check if VAD status has changed from inactive to active, or if error was + // reported + if (status == 1) { + vad_enabled_ = true; + return status; + } else if (status < 0) { + has_internal_dtx_ = replace_internal_dtx; + return -1; + } + } + return 0; +} + +int32_t ACMG729::IsInternalDTXReplacedSafe(bool* internal_dtx_replaced) { + // Get status of wether DTX is replaced or not + *internal_dtx_replaced = !has_internal_dtx_; + return 0; +} + +int16_t ACMG729::InternalInitEncoder(WebRtcACMCodecParams* codec_params) { + // Init G.729 encoder + return WebRtcG729_EncoderInit(encoder_inst_ptr_, + ((codec_params->enable_dtx) ? 1 : 0)); +} + +ACMGenericCodec* ACMG729::CreateInstance(void) { + // Function not used + return NULL; +} + +int16_t ACMG729::InternalCreateEncoder() { + // Create encoder memory + return WebRtcG729_CreateEnc(&encoder_inst_ptr_); +} + +void ACMG729::DestructEncoderSafe() { + // Free encoder memory + encoder_exist_ = false; + encoder_initialized_ = false; + if (encoder_inst_ptr_ != NULL) { + WebRtcG729_FreeEnc(encoder_inst_ptr_); + encoder_inst_ptr_ = NULL; + } +} + +void ACMG729::InternalDestructEncoderInst(void* ptr_inst) { + if (ptr_inst != NULL) { + WebRtcG729_FreeEnc(static_cast(ptr_inst)); + } + return; +} + +#endif + +} // namespace webrtc diff --git a/webrtc/modules/audio_coding/main/acm2/acm_g729.h b/webrtc/modules/audio_coding/main/acm2/acm_g729.h new file mode 100644 index 0000000000..3b35f3b90e --- /dev/null +++ b/webrtc/modules/audio_coding/main/acm2/acm_g729.h @@ -0,0 +1,54 @@ +/* + * 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. + */ + +#ifndef WEBRTC_MODULES_AUDIO_CODING_MAIN_SOURCE_ACM_G729_H_ +#define WEBRTC_MODULES_AUDIO_CODING_MAIN_SOURCE_ACM_G729_H_ + +#include "webrtc/modules/audio_coding/main/source/acm_generic_codec.h" + +// forward declaration +struct G729_encinst_t_; +struct G729_decinst_t_; + +namespace webrtc { + +class ACMG729 : public ACMGenericCodec { + public: + explicit ACMG729(int16_t codec_id); + ~ACMG729(); + + // for FEC + ACMGenericCodec* CreateInstance(void); + + int16_t InternalEncode(uint8_t* bitstream, int16_t* bitstream_len_byte); + + int16_t InternalInitEncoder(WebRtcACMCodecParams* codec_params); + + protected: + void DestructEncoderSafe(); + + int16_t InternalCreateEncoder(); + + void InternalDestructEncoderInst(void* ptr_inst); + + int16_t EnableDTX(); + + int16_t DisableDTX(); + + int32_t ReplaceInternalDTXSafe(const bool replace_internal_dtx); + + int32_t IsInternalDTXReplacedSafe(bool* internal_dtx_replaced); + + G729_encinst_t_* encoder_inst_ptr_; +}; + +} // namespace webrtc + +#endif // WEBRTC_MODULES_AUDIO_CODING_MAIN_SOURCE_ACM_G729_H_ diff --git a/webrtc/modules/audio_coding/main/acm2/acm_g7291.cc b/webrtc/modules/audio_coding/main/acm2/acm_g7291.cc new file mode 100644 index 0000000000..1c661c11b1 --- /dev/null +++ b/webrtc/modules/audio_coding/main/acm2/acm_g7291.cc @@ -0,0 +1,240 @@ +/* + * 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/source/acm_g7291.h" + +#ifdef WEBRTC_CODEC_G729_1 +// NOTE! G.729.1 is not included in the open-source package. Modify this file +// or your codec API to match the function calls and names of used G.729.1 API +// file. +#include "webrtc/modules/audio_coding/main/codecs/g7291/interface/g7291_interface.h" +#include "webrtc/modules/audio_coding/main/source/acm_common_defs.h" +#include "webrtc/system_wrappers/interface/trace.h" +#endif + +namespace webrtc { + +#ifndef WEBRTC_CODEC_G729_1 + +ACMG729_1::ACMG729_1(int16_t /* codec_id */) + : encoder_inst_ptr_(NULL), + my_rate_(32000), + flag_8khz_(0), + flag_g729_mode_(0) { + return; +} + +ACMG729_1::~ACMG729_1() { return; } + +int16_t ACMG729_1::InternalEncode(uint8_t* /* bitstream */, + int16_t* /* bitstream_len_byte */) { + return -1; +} + +int16_t ACMG729_1::InternalInitEncoder( + WebRtcACMCodecParams* /* codec_params */) { + return -1; +} + +ACMGenericCodec* ACMG729_1::CreateInstance(void) { return NULL; } + +int16_t ACMG729_1::InternalCreateEncoder() { return -1; } + +void ACMG729_1::DestructEncoderSafe() { return; } + +void ACMG729_1::InternalDestructEncoderInst(void* /* ptr_inst */) { return; } + +int16_t ACMG729_1::SetBitRateSafe(const int32_t /*rate*/) { return -1; } + +#else //===================== Actual Implementation ======================= + +struct G729_1_inst_t_; + +ACMG729_1::ACMG729_1(int16_t codec_id) + : encoder_inst_ptr_(NULL), + my_rate_(32000), // Default rate. + flag_8khz_(0), + flag_g729_mode_(0) { + // TODO(tlegrand): We should add codec_id as a input variable to the + // constructor of ACMGenericCodec. + codec_id_ = codec_id; + return; +} + +ACMG729_1::~ACMG729_1() { + if (encoder_inst_ptr_ != NULL) { + WebRtcG7291_Free(encoder_inst_ptr_); + encoder_inst_ptr_ = NULL; + } + return; +} + +int16_t ACMG729_1::InternalEncode(uint8_t* bitstream, + int16_t* bitstream_len_byte) { + // Initialize before entering the loop + int16_t num_encoded_samples = 0; + *bitstream_len_byte = 0; + + int16_t byte_length_frame = 0; + + // Derive number of 20ms frames per encoded packet. + // [1,2,3] <=> [20,40,60]ms <=> [320,640,960] samples + int16_t num_20ms_frames = (frame_len_smpl_ / 320); + // Byte length for the frame. +1 is for rate information. + byte_length_frame = + my_rate_ / (8 * 50) * num_20ms_frames + (1 - flag_g729_mode_); + + // The following might be revised if we have G729.1 Annex C (support for DTX); + do { + *bitstream_len_byte = WebRtcG7291_Encode( + encoder_inst_ptr_, &in_audio_[in_audio_ix_read_], + reinterpret_cast(bitstream), my_rate_, num_20ms_frames); + + // increment the read index this tell the caller that how far + // we have gone forward in reading the audio buffer + in_audio_ix_read_ += 160; + + // sanity check + if (*bitstream_len_byte < 0) { + // error has happened + WEBRTC_TRACE(webrtc::kTraceError, webrtc::kTraceAudioCoding, unique_id_, + "InternalEncode: Encode error for G729_1"); + *bitstream_len_byte = 0; + return -1; + } + + num_encoded_samples += 160; + } while (*bitstream_len_byte == 0); + + // This criteria will change if we have Annex C. + if (*bitstream_len_byte != byte_length_frame) { + WEBRTC_TRACE(webrtc::kTraceError, webrtc::kTraceAudioCoding, unique_id_, + "InternalEncode: Encode error for G729_1"); + *bitstream_len_byte = 0; + return -1; + } + + if (num_encoded_samples != frame_len_smpl_) { + *bitstream_len_byte = 0; + return -1; + } + + return *bitstream_len_byte; +} + +int16_t ACMG729_1::InternalInitEncoder(WebRtcACMCodecParams* codec_params) { + // set the bit rate and initialize + my_rate_ = codec_params->codec_inst.rate; + return SetBitRateSafe((uint32_t)my_rate_); +} + +ACMGenericCodec* ACMG729_1::CreateInstance(void) { return NULL; } + +int16_t ACMG729_1::InternalCreateEncoder() { + if (WebRtcG7291_Create(&encoder_inst_ptr_) < 0) { + WEBRTC_TRACE(webrtc::kTraceError, + webrtc::kTraceAudioCoding, + unique_id_, + "InternalCreateEncoder: create encoder failed for G729_1"); + return -1; + } + return 0; +} + +void ACMG729_1::DestructEncoderSafe() { + encoder_exist_ = false; + encoder_initialized_ = false; + if (encoder_inst_ptr_ != NULL) { + WebRtcG7291_Free(encoder_inst_ptr_); + encoder_inst_ptr_ = NULL; + } +} + +void ACMG729_1::InternalDestructEncoderInst(void* ptr_inst) { + if (ptr_inst != NULL) { + // WebRtcG7291_Free((G729_1_inst_t*)ptrInst); + } + return; +} + +int16_t ACMG729_1::SetBitRateSafe(const int32_t rate) { + // allowed rates: { 8000, 12000, 14000, 16000, 18000, 20000, + // 22000, 24000, 26000, 28000, 30000, 32000}; + // TODO(tlegrand): This check exists in one other place two. Should be + // possible to reuse code. + switch (rate) { + case 8000: { + my_rate_ = 8000; + break; + } + case 12000: { + my_rate_ = 12000; + break; + } + case 14000: { + my_rate_ = 14000; + break; + } + case 16000: { + my_rate_ = 16000; + break; + } + case 18000: { + my_rate_ = 18000; + break; + } + case 20000: { + my_rate_ = 20000; + break; + } + case 22000: { + my_rate_ = 22000; + break; + } + case 24000: { + my_rate_ = 24000; + break; + } + case 26000: { + my_rate_ = 26000; + break; + } + case 28000: { + my_rate_ = 28000; + break; + } + case 30000: { + my_rate_ = 30000; + break; + } + case 32000: { + my_rate_ = 32000; + break; + } + default: { + WEBRTC_TRACE(webrtc::kTraceError, webrtc::kTraceAudioCoding, unique_id_, + "SetBitRateSafe: Invalid rate G729_1"); + return -1; + } + } + + // Re-init with new rate + if (WebRtcG7291_EncoderInit(encoder_inst_ptr_, my_rate_, flag_8khz_, + flag_g729_mode_) >= 0) { + encoder_params_.codec_inst.rate = my_rate_; + return 0; + } else { + return -1; + } +} + +#endif + +} // namespace webrtc diff --git a/webrtc/modules/audio_coding/main/acm2/acm_g7291.h b/webrtc/modules/audio_coding/main/acm2/acm_g7291.h new file mode 100644 index 0000000000..97601eac1a --- /dev/null +++ b/webrtc/modules/audio_coding/main/acm2/acm_g7291.h @@ -0,0 +1,52 @@ +/* + * 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. + */ + +#ifndef WEBRTC_MODULES_AUDIO_CODING_MAIN_SOURCE_ACM_G7291_H_ +#define WEBRTC_MODULES_AUDIO_CODING_MAIN_SOURCE_ACM_G7291_H_ + +#include "webrtc/modules/audio_coding/main/source/acm_generic_codec.h" + +// forward declaration +struct G729_1_inst_t_; +struct G729_1_inst_t_; + +namespace webrtc { + +class ACMG729_1 : public ACMGenericCodec { + public: + explicit ACMG729_1(int16_t codec_id); + ~ACMG729_1(); + + // for FEC + ACMGenericCodec* CreateInstance(void); + + int16_t InternalEncode(uint8_t* bitstream, int16_t* bitstream_len_byte); + + int16_t InternalInitEncoder(WebRtcACMCodecParams* codec_params); + + protected: + void DestructEncoderSafe(); + + int16_t InternalCreateEncoder(); + + void InternalDestructEncoderInst(void* ptr_inst); + + int16_t SetBitRateSafe(const int32_t rate); + + G729_1_inst_t_* encoder_inst_ptr_; + + uint16_t my_rate_; + int16_t flag_8khz_; + int16_t flag_g729_mode_; +}; + +} // namespace webrtc + +#endif // WEBRTC_MODULES_AUDIO_CODING_MAIN_SOURCE_ACM_G7291_H_ diff --git a/webrtc/modules/audio_coding/main/acm2/acm_generic_codec.cc b/webrtc/modules/audio_coding/main/acm2/acm_generic_codec.cc new file mode 100644 index 0000000000..5e210efd6d --- /dev/null +++ b/webrtc/modules/audio_coding/main/acm2/acm_generic_codec.cc @@ -0,0 +1,995 @@ +/* + * 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/source/acm_generic_codec.h" + +#include +#include + +#include "webrtc/common_audio/vad/include/webrtc_vad.h" +#include "webrtc/modules/audio_coding/codecs/cng/include/webrtc_cng.h" +#include "webrtc/modules/audio_coding/main/source/acm_codec_database.h" +#include "webrtc/modules/audio_coding/main/source/acm_common_defs.h" +#include "webrtc/system_wrappers/interface/trace.h" + +namespace webrtc { + +// Enum for CNG +enum { + kMaxPLCParamsCNG = WEBRTC_CNG_MAX_LPC_ORDER, + kNewCNGNumPLCParams = 8 +}; + +// Interval for sending new CNG parameters (SID frames) is 100 msec. +enum { + kCngSidIntervalMsec = 100 +}; + +// We set some of the variables to invalid values as a check point +// if a proper initialization has happened. Another approach is +// to initialize to a default codec that we are sure is always included. +ACMGenericCodec::ACMGenericCodec() + : in_audio_ix_write_(0), + in_audio_ix_read_(0), + in_timestamp_ix_write_(0), + in_audio_(NULL), + in_timestamp_(NULL), + frame_len_smpl_(-1), // invalid value + num_channels_(1), + codec_id_(-1), // invalid value + num_missed_samples_(0), + encoder_exist_(false), + encoder_initialized_(false), + registered_in_neteq_(false), + has_internal_dtx_(false), + ptr_vad_inst_(NULL), + vad_enabled_(false), + vad_mode_(VADNormal), + dtx_enabled_(false), + ptr_dtx_inst_(NULL), + num_lpc_params_(kNewCNGNumPLCParams), + sent_cn_previous_(false), + prev_frame_cng_(0), + neteq_decode_lock_(NULL), + codec_wrapper_lock_(*RWLockWrapper::CreateRWLock()), + last_timestamp_(0xD87F3F9F), + unique_id_(0) { + // Initialize VAD vector. + for (int i = 0; i < MAX_FRAME_SIZE_10MSEC; i++) { + vad_label_[i] = 0; + } + // Nullify memory for encoder and decoder, and set payload type to an + // invalid value. + memset(&encoder_params_, 0, sizeof(WebRtcACMCodecParams)); + encoder_params_.codec_inst.pltype = -1; +} + +ACMGenericCodec::~ACMGenericCodec() { + // Check all the members which are pointers, and if they are not NULL + // delete/free them. + if (ptr_vad_inst_ != NULL) { + WebRtcVad_Free(ptr_vad_inst_); + ptr_vad_inst_ = NULL; + } + if (in_audio_ != NULL) { + delete[] in_audio_; + in_audio_ = NULL; + } + if (in_timestamp_ != NULL) { + delete[] in_timestamp_; + in_timestamp_ = NULL; + } + if (ptr_dtx_inst_ != NULL) { + WebRtcCng_FreeEnc(ptr_dtx_inst_); + ptr_dtx_inst_ = NULL; + } + delete &codec_wrapper_lock_; +} + +int32_t ACMGenericCodec::Add10MsData(const uint32_t timestamp, + const int16_t* data, + const uint16_t length_smpl, + const uint8_t audio_channel) { + WriteLockScoped wl(codec_wrapper_lock_); + return Add10MsDataSafe(timestamp, data, length_smpl, audio_channel); +} + +int32_t ACMGenericCodec::Add10MsDataSafe(const uint32_t timestamp, + const int16_t* data, + const uint16_t length_smpl, + const uint8_t audio_channel) { + // The codec expects to get data in correct sampling rate. Get the sampling + // frequency of the codec. + uint16_t plfreq_hz; + if (EncoderSampFreq(&plfreq_hz) < 0) { + return -1; + } + + // Sanity check to make sure the length of the input corresponds to 10 ms. + if ((plfreq_hz / 100) != length_smpl) { + // This is not 10 ms of audio, given the sampling frequency of the codec. + return -1; + } + + if (last_timestamp_ == timestamp) { + // Same timestamp as the last time, overwrite. + if ((in_audio_ix_write_ >= length_smpl * audio_channel) && + (in_timestamp_ix_write_ > 0)) { + in_audio_ix_write_ -= length_smpl * audio_channel; + in_timestamp_ix_write_--; + WEBRTC_TRACE(webrtc::kTraceDebug, webrtc::kTraceAudioCoding, unique_id_, + "Adding 10ms with previous timestamp, overwriting the " + "previous 10ms"); + } else { + WEBRTC_TRACE(webrtc::kTraceDebug, webrtc::kTraceAudioCoding, unique_id_, + "Adding 10ms with previous timestamp, this will sound bad"); + } + } + + last_timestamp_ = timestamp; + + // If the data exceeds the buffer size, we throw away the oldest data and + // add the newly received 10 msec at the end. + if ((in_audio_ix_write_ + length_smpl * audio_channel) > + AUDIO_BUFFER_SIZE_W16) { + // Get the number of samples to be overwritten. + int16_t missed_samples = in_audio_ix_write_ + length_smpl * audio_channel - + AUDIO_BUFFER_SIZE_W16; + + // Move the data (overwrite the old data). + memmove(in_audio_, in_audio_ + missed_samples, + (AUDIO_BUFFER_SIZE_W16 - length_smpl * audio_channel) * + sizeof(int16_t)); + + // Copy the new data. + memcpy(in_audio_ + (AUDIO_BUFFER_SIZE_W16 - length_smpl * audio_channel), + data, length_smpl * audio_channel * sizeof(int16_t)); + + // Get the number of 10 ms blocks which are overwritten. + int16_t missed_10ms_blocks =static_cast( + (missed_samples / audio_channel * 100) / plfreq_hz); + + // Move the timestamps. + memmove(in_timestamp_, in_timestamp_ + missed_10ms_blocks, + (in_timestamp_ix_write_ - missed_10ms_blocks) * sizeof(uint32_t)); + in_timestamp_ix_write_ -= missed_10ms_blocks; + in_timestamp_[in_timestamp_ix_write_] = timestamp; + in_timestamp_ix_write_++; + + // Buffer is full. + in_audio_ix_write_ = AUDIO_BUFFER_SIZE_W16; + IncreaseNoMissedSamples(missed_samples); + return -missed_samples; + } + + // Store the input data in our data buffer. + memcpy(in_audio_ + in_audio_ix_write_, data, + length_smpl * audio_channel * sizeof(int16_t)); + in_audio_ix_write_ += length_smpl * audio_channel; + + assert(in_timestamp_ix_write_ < TIMESTAMP_BUFFER_SIZE_W32); + assert(in_timestamp_ix_write_ >= 0); + + in_timestamp_[in_timestamp_ix_write_] = timestamp; + in_timestamp_ix_write_++; + return 0; +} + +bool ACMGenericCodec::HasFrameToEncode() const { + ReadLockScoped lockCodec(codec_wrapper_lock_); + if (in_audio_ix_write_ < frame_len_smpl_ * num_channels_) + return false; + return true; +} + +int16_t ACMGenericCodec::Encode(uint8_t* bitstream, + int16_t* bitstream_len_byte, + uint32_t* timestamp, + WebRtcACMEncodingType* encoding_type) { + if (!HasFrameToEncode()) { + // There is not enough audio + *timestamp = 0; + *bitstream_len_byte = 0; + // Doesn't really matter what this parameter set to + *encoding_type = kNoEncoding; + return 0; + } + WriteLockScoped lockCodec(codec_wrapper_lock_); + ReadLockScoped lockNetEq(*neteq_decode_lock_); + + // Not all codecs accept the whole frame to be pushed into encoder at once. + // Some codecs needs to be feed with a specific number of samples different + // from the frame size. If this is the case, |myBasicCodingBlockSmpl| will + // report a number different from 0, and we will loop over calls to encoder + // further down, until we have encode a complete frame. + const int16_t my_basic_coding_block_smpl = + ACMCodecDB::BasicCodingBlock(codec_id_); + if (my_basic_coding_block_smpl < 0 || !encoder_initialized_ || + !encoder_exist_) { + // This should not happen, but in case it does, report no encoding done. + *timestamp = 0; + *bitstream_len_byte = 0; + *encoding_type = kNoEncoding; + WEBRTC_TRACE(webrtc::kTraceError, webrtc::kTraceAudioCoding, unique_id_, + "EncodeSafe: error, basic coding sample block is negative"); + return -1; + } + // This makes the internal encoder read from the beginning of the buffer. + in_audio_ix_read_ = 0; + *timestamp = in_timestamp_[0]; + + // Process the audio through VAD. The function will set |_vad_labels|. + // If VAD is disabled all entries in |_vad_labels| are set to ONE (active). + int16_t status = 0; + int16_t dtx_processed_samples = 0; + status = ProcessFrameVADDTX(bitstream, bitstream_len_byte, + &dtx_processed_samples); + if (status < 0) { + *timestamp = 0; + *bitstream_len_byte = 0; + *encoding_type = kNoEncoding; + } else { + if (dtx_processed_samples > 0) { + // Dtx have processed some samples, and even if a bit-stream is generated + // we should not do any encoding (normally there won't be enough data). + + // Setting the following makes sure that the move of audio data and + // timestamps done correctly. + in_audio_ix_read_ = dtx_processed_samples; + // This will let the owner of ACMGenericCodec to know that the + // generated bit-stream is DTX to use correct payload type. + uint16_t samp_freq_hz; + EncoderSampFreq(&samp_freq_hz); + if (samp_freq_hz == 8000) { + *encoding_type = kPassiveDTXNB; + } else if (samp_freq_hz == 16000) { + *encoding_type = kPassiveDTXWB; + } else if (samp_freq_hz == 32000) { + *encoding_type = kPassiveDTXSWB; + } else if (samp_freq_hz == 48000) { + *encoding_type = kPassiveDTXFB; + } else { + status = -1; + WEBRTC_TRACE(webrtc::kTraceError, webrtc::kTraceAudioCoding, unique_id_, + "EncodeSafe: Wrong sampling frequency for DTX."); + } + + // Transport empty frame if we have an empty bitstream. + if ((*bitstream_len_byte == 0) && + (sent_cn_previous_ || + ((in_audio_ix_write_ - in_audio_ix_read_) <= 0))) { + // Makes sure we transmit an empty frame. + *bitstream_len_byte = 1; + *encoding_type = kNoEncoding; + } + sent_cn_previous_ = true; + } else { + // We should encode the audio frame. Either VAD and/or DTX is off, or the + // audio was considered "active". + + sent_cn_previous_ = false; + if (my_basic_coding_block_smpl == 0) { + // This codec can handle all allowed frame sizes as basic coding block. + status = InternalEncode(bitstream, bitstream_len_byte); + if (status < 0) { + // TODO(tlegrand): Maybe reseting the encoder to be fresh for the next + // frame. + WEBRTC_TRACE(webrtc::kTraceError, webrtc::kTraceAudioCoding, + unique_id_, "EncodeSafe: error in internal_encode"); + *bitstream_len_byte = 0; + *encoding_type = kNoEncoding; + } + } else { + // A basic-coding-block for this codec is defined so we loop over the + // audio with the steps of the basic-coding-block. + int16_t tmp_bitstream_len_byte; + + // Reset the variables which will be incremented in the loop. + *bitstream_len_byte = 0; + bool done = false; + while (!done) { + status = InternalEncode(&bitstream[*bitstream_len_byte], + &tmp_bitstream_len_byte); + *bitstream_len_byte += tmp_bitstream_len_byte; + + // Guard Against errors and too large payloads. + if ((status < 0) || (*bitstream_len_byte > MAX_PAYLOAD_SIZE_BYTE)) { + // Error has happened, and even if we are in the middle of a full + // frame we have to exit. Before exiting, whatever bits are in the + // buffer are probably corrupted, so we ignore them. + *bitstream_len_byte = 0; + *encoding_type = kNoEncoding; + // We might have come here because of the second condition. + status = -1; + WEBRTC_TRACE(webrtc::kTraceError, webrtc::kTraceAudioCoding, + unique_id_, "EncodeSafe: error in InternalEncode"); + // break from the loop + break; + } + + // TODO(andrew): This should be multiplied by the number of + // channels, right? + // http://code.google.com/p/webrtc/issues/detail?id=714 + done = in_audio_ix_read_ >= frame_len_smpl_; + } + } + if (status >= 0) { + *encoding_type = (vad_label_[0] == 1) ? kActiveNormalEncoded : + kPassiveNormalEncoded; + // Transport empty frame if we have an empty bitstream. + if ((*bitstream_len_byte == 0) && + ((in_audio_ix_write_ - in_audio_ix_read_) <= 0)) { + // Makes sure we transmit an empty frame. + *bitstream_len_byte = 1; + *encoding_type = kNoEncoding; + } + } + } + } + + // Move the timestamp buffer according to the number of 10 ms blocks + // which are read. + uint16_t samp_freq_hz; + EncoderSampFreq(&samp_freq_hz); + int16_t num_10ms_blocks = static_cast( + (in_audio_ix_read_ / num_channels_ * 100) / samp_freq_hz); + if (in_timestamp_ix_write_ > num_10ms_blocks) { + memmove(in_timestamp_, in_timestamp_ + num_10ms_blocks, + (in_timestamp_ix_write_ - num_10ms_blocks) * sizeof(int32_t)); + } + in_timestamp_ix_write_ -= num_10ms_blocks; + + // Remove encoded audio and move next audio to be encoded to the beginning + // of the buffer. Accordingly, adjust the read and write indices. + if (in_audio_ix_read_ < in_audio_ix_write_) { + memmove(in_audio_, &in_audio_[in_audio_ix_read_], + (in_audio_ix_write_ - in_audio_ix_read_) * sizeof(int16_t)); + } + in_audio_ix_write_ -= in_audio_ix_read_; + in_audio_ix_read_ = 0; + return (status < 0) ? (-1) : (*bitstream_len_byte); +} + +bool ACMGenericCodec::EncoderInitialized() { + ReadLockScoped rl(codec_wrapper_lock_); + return encoder_initialized_; +} + +int16_t ACMGenericCodec::EncoderParams(WebRtcACMCodecParams* enc_params) { + ReadLockScoped rl(codec_wrapper_lock_); + return EncoderParamsSafe(enc_params); +} + +int16_t ACMGenericCodec::EncoderParamsSafe(WebRtcACMCodecParams* enc_params) { + // Codec parameters are valid only if the encoder is initialized. + if (encoder_initialized_) { + int32_t current_rate; + memcpy(enc_params, &encoder_params_, sizeof(WebRtcACMCodecParams)); + current_rate = enc_params->codec_inst.rate; + CurrentRate(¤t_rate); + enc_params->codec_inst.rate = current_rate; + return 0; + } else { + enc_params->codec_inst.plname[0] = '\0'; + enc_params->codec_inst.pltype = -1; + enc_params->codec_inst.pacsize = 0; + enc_params->codec_inst.rate = 0; + WEBRTC_TRACE(webrtc::kTraceError, webrtc::kTraceAudioCoding, unique_id_, + "EncoderParamsSafe: error, encoder not initialized"); + return -1; + } +} + +int16_t ACMGenericCodec::ResetEncoder() { + WriteLockScoped lockCodec(codec_wrapper_lock_); + ReadLockScoped lockNetEq(*neteq_decode_lock_); + return ResetEncoderSafe(); +} + +int16_t ACMGenericCodec::ResetEncoderSafe() { + if (!encoder_exist_ || !encoder_initialized_) { + // We don't reset if encoder doesn't exists or isn't initialized yet. + return 0; + } + + in_audio_ix_write_ = 0; + in_audio_ix_read_ = 0; + in_timestamp_ix_write_ = 0; + num_missed_samples_ = 0; + memset(in_audio_, 0, AUDIO_BUFFER_SIZE_W16 * sizeof(int16_t)); + memset(in_timestamp_, 0, TIMESTAMP_BUFFER_SIZE_W32 * sizeof(int32_t)); + + // Store DTX/VAD parameters. + bool enable_vad = vad_enabled_; + bool enable_dtx = dtx_enabled_; + ACMVADMode mode = vad_mode_; + + // Reset the encoder. + if (InternalResetEncoder() < 0) { + WEBRTC_TRACE(webrtc::kTraceError, webrtc::kTraceAudioCoding, unique_id_, + "ResetEncoderSafe: error in reset encoder"); + return -1; + } + + // Disable DTX & VAD to delete the states and have a fresh start. + DisableDTX(); + DisableVAD(); + + // Set DTX/VAD. + return SetVADSafe(enable_dtx, enable_vad, mode); +} + +int16_t ACMGenericCodec::InternalResetEncoder() { + // Call the codecs internal encoder initialization/reset function. + return InternalInitEncoder(&encoder_params_); +} + +int16_t ACMGenericCodec::InitEncoder(WebRtcACMCodecParams* codec_params, + bool force_initialization) { + WriteLockScoped lockCodec(codec_wrapper_lock_); + ReadLockScoped lockNetEq(*neteq_decode_lock_); + return InitEncoderSafe(codec_params, force_initialization); +} + +int16_t ACMGenericCodec::InitEncoderSafe(WebRtcACMCodecParams* codec_params, + bool force_initialization) { + // Check if we got a valid set of parameters. + int mirrorID; + int codec_number = ACMCodecDB::CodecNumber(codec_params->codec_inst, + &mirrorID); + if (codec_number < 0) { + WEBRTC_TRACE(webrtc::kTraceError, webrtc::kTraceAudioCoding, unique_id_, + "InitEncoderSafe: error, codec number negative"); + return -1; + } + // Check if the parameters are for this codec. + if ((codec_id_ >= 0) && (codec_id_ != codec_number) && + (codec_id_ != mirrorID)) { + // The current codec is not the same as the one given by codec_params. + WEBRTC_TRACE(webrtc::kTraceError, webrtc::kTraceAudioCoding, unique_id_, + "InitEncoderSafe: current codec is not the same as the one " + "given by codec_params"); + return -1; + } + + if (encoder_initialized_ && !force_initialization) { + // The encoder is already initialized, and we don't want to force + // initialization. + return 0; + } + int16_t status; + if (!encoder_exist_) { + // New encoder, start with creating. + encoder_initialized_ = false; + status = CreateEncoder(); + if (status < 0) { + WEBRTC_TRACE(webrtc::kTraceError, webrtc::kTraceAudioCoding, unique_id_, + "InitEncoderSafe: cannot create encoder"); + return -1; + } else { + encoder_exist_ = true; + } + } + frame_len_smpl_ = (codec_params->codec_inst).pacsize; + num_channels_ = codec_params->codec_inst.channels; + status = InternalInitEncoder(codec_params); + if (status < 0) { + WEBRTC_TRACE(webrtc::kTraceError, webrtc::kTraceAudioCoding, unique_id_, + "InitEncoderSafe: error in init encoder"); + encoder_initialized_ = false; + return -1; + } else { + // Store encoder parameters. + memcpy(&encoder_params_, codec_params, sizeof(WebRtcACMCodecParams)); + encoder_initialized_ = true; + if (in_audio_ == NULL) { + in_audio_ = new int16_t[AUDIO_BUFFER_SIZE_W16]; + if (in_audio_ == NULL) { + return -1; + } + memset(in_audio_, 0, AUDIO_BUFFER_SIZE_W16 * sizeof(int16_t)); + } + if (in_timestamp_ == NULL) { + in_timestamp_ = new uint32_t[TIMESTAMP_BUFFER_SIZE_W32]; + if (in_timestamp_ == NULL) { + return -1; + } + memset(in_timestamp_, 0, sizeof(uint32_t) * TIMESTAMP_BUFFER_SIZE_W32); + } + } + status = SetVADSafe(codec_params->enable_dtx, codec_params->enable_vad, + codec_params->vad_mode); + + return status; +} + +void ACMGenericCodec::ResetNoMissedSamples() { + WriteLockScoped cs(codec_wrapper_lock_); + num_missed_samples_ = 0; +} + +void ACMGenericCodec::IncreaseNoMissedSamples(const int16_t num_samples) { + num_missed_samples_ += num_samples; +} + +// Get the number of missed samples, this can be public. +uint32_t ACMGenericCodec::NoMissedSamples() const { + ReadLockScoped cs(codec_wrapper_lock_); + return num_missed_samples_; +} + +void ACMGenericCodec::DestructEncoder() { + WriteLockScoped wl(codec_wrapper_lock_); + + // Disable VAD and delete the instance. + if (ptr_vad_inst_ != NULL) { + WebRtcVad_Free(ptr_vad_inst_); + ptr_vad_inst_ = NULL; + } + vad_enabled_ = false; + vad_mode_ = VADNormal; + + // Disable DTX and delete the instance. + dtx_enabled_ = false; + if (ptr_dtx_inst_ != NULL) { + WebRtcCng_FreeEnc(ptr_dtx_inst_); + ptr_dtx_inst_ = NULL; + } + num_lpc_params_ = kNewCNGNumPLCParams; + + DestructEncoderSafe(); +} + +int16_t ACMGenericCodec::SetBitRate(const int32_t bitrate_bps) { + WriteLockScoped wl(codec_wrapper_lock_); + return SetBitRateSafe(bitrate_bps); +} + +int16_t ACMGenericCodec::SetBitRateSafe(const int32_t bitrate_bps) { + // If the codec can change the bit-rate this function is overloaded. + // Otherwise the only acceptable value is the one that is in the database. + CodecInst codec_params; + if (ACMCodecDB::Codec(codec_id_, &codec_params) < 0) { + WEBRTC_TRACE(webrtc::kTraceError, webrtc::kTraceAudioCoding, unique_id_, + "SetBitRateSafe: error in ACMCodecDB::Codec"); + return -1; + } + if (codec_params.rate != bitrate_bps) { + WEBRTC_TRACE(webrtc::kTraceError, webrtc::kTraceAudioCoding, unique_id_, + "SetBitRateSafe: rate value is not acceptable"); + return -1; + } else { + return 0; + } +} + +// iSAC specific functions: +int32_t ACMGenericCodec::GetEstimatedBandwidth() { + WriteLockScoped wl(codec_wrapper_lock_); + return GetEstimatedBandwidthSafe(); +} + +int32_t ACMGenericCodec::GetEstimatedBandwidthSafe() { + // All codecs but iSAC will return -1. + return -1; +} + +int32_t ACMGenericCodec::SetEstimatedBandwidth(int32_t estimated_bandwidth) { + WriteLockScoped wl(codec_wrapper_lock_); + return SetEstimatedBandwidthSafe(estimated_bandwidth); +} + +int32_t ACMGenericCodec::SetEstimatedBandwidthSafe( + int32_t /*estimated_bandwidth*/) { + // All codecs but iSAC will return -1. + return -1; +} +// End of iSAC specific functions. + +int32_t ACMGenericCodec::GetRedPayload(uint8_t* red_payload, + int16_t* payload_bytes) { + WriteLockScoped wl(codec_wrapper_lock_); + return GetRedPayloadSafe(red_payload, payload_bytes); +} + +int32_t ACMGenericCodec::GetRedPayloadSafe(uint8_t* /* red_payload */, + int16_t* /* payload_bytes */) { + return -1; // Do nothing by default. +} + +int16_t ACMGenericCodec::CreateEncoder() { + int16_t status = 0; + if (!encoder_exist_) { + status = InternalCreateEncoder(); + // We just created the codec and obviously it is not initialized. + encoder_initialized_ = false; + } + if (status < 0) { + WEBRTC_TRACE(webrtc::kTraceError, webrtc::kTraceAudioCoding, unique_id_, + "CreateEncoder: error in internal create encoder"); + encoder_exist_ = false; + } else { + encoder_exist_ = true; + } + return status; +} + +void ACMGenericCodec::DestructEncoderInst(void* ptr_inst) { + if (ptr_inst != NULL) { + WriteLockScoped lockCodec(codec_wrapper_lock_); + ReadLockScoped lockNetEq(*neteq_decode_lock_); + InternalDestructEncoderInst(ptr_inst); + } +} + +uint32_t ACMGenericCodec::EarliestTimestamp() const { + ReadLockScoped cs(codec_wrapper_lock_); + return in_timestamp_[0]; +} + +int16_t ACMGenericCodec::SetVAD(const bool enable_dtx, + const bool enable_vad, + const ACMVADMode mode) { + WriteLockScoped cs(codec_wrapper_lock_); + return SetVADSafe(enable_dtx, enable_vad, mode); +} + +int16_t ACMGenericCodec::SetVADSafe(const bool enable_dtx, + const bool enable_vad, + const ACMVADMode mode) { + if (enable_dtx) { + // Make G729 AnnexB a special case. + if (!STR_CASE_CMP(encoder_params_.codec_inst.plname, "G729") + && !has_internal_dtx_) { + if (ACMGenericCodec::EnableDTX() < 0) { + WEBRTC_TRACE(webrtc::kTraceError, webrtc::kTraceAudioCoding, unique_id_, + "SetVADSafe: error in enable DTX"); + return -1; + } + } else { + if (EnableDTX() < 0) { + WEBRTC_TRACE(webrtc::kTraceError, webrtc::kTraceAudioCoding, unique_id_, + "SetVADSafe: error in enable DTX"); + return -1; + } + } + + if (has_internal_dtx_) { + // Codec has internal DTX, practically we don't need WebRtc VAD, however, + // we let the user to turn it on if they need call-backs on silence. + // Store VAD mode for future even if VAD is off. + vad_mode_ = mode; + return (enable_vad) ? EnableVAD(mode) : DisableVAD(); + } else { + // Codec does not have internal DTX so enabling DTX requires an active + // VAD. 'enable_dtx == true' overwrites VAD status. + if (EnableVAD(mode) < 0) { + // If we cannot create VAD we have to disable DTX. + if (!vad_enabled_) { + DisableDTX(); + } + WEBRTC_TRACE(webrtc::kTraceError, webrtc::kTraceAudioCoding, unique_id_, + "SetVADSafe: error in enable VAD"); + return -1; + } + + // Return '1', to let the caller know VAD was turned on, even if the + // function was called with VAD='false'. + if (enable_vad == false) { + return 1; + } else { + return 0; + } + } + } else { + // Make G729 AnnexB a special case. + if (!STR_CASE_CMP(encoder_params_.codec_inst.plname, "G729") + && !has_internal_dtx_) { + ACMGenericCodec::DisableDTX(); + } else { + DisableDTX(); + } + return (enable_vad) ? EnableVAD(mode) : DisableVAD(); + } +} + +int16_t ACMGenericCodec::EnableDTX() { + if (has_internal_dtx_) { + // We should not be here if we have internal DTX this function should be + // overloaded by the derived class in this case. + return -1; + } + if (!dtx_enabled_) { + if (WebRtcCng_CreateEnc(&ptr_dtx_inst_) < 0) { + ptr_dtx_inst_ = NULL; + return -1; + } + uint16_t freq_hz; + EncoderSampFreq(&freq_hz); + if (WebRtcCng_InitEnc(ptr_dtx_inst_, freq_hz, kCngSidIntervalMsec, + num_lpc_params_) < 0) { + // Couldn't initialize, has to return -1, and free the memory. + WebRtcCng_FreeEnc(ptr_dtx_inst_); + ptr_dtx_inst_ = NULL; + return -1; + } + dtx_enabled_ = true; + } + return 0; +} + +int16_t ACMGenericCodec::DisableDTX() { + if (has_internal_dtx_) { + // We should not be here if we have internal DTX this function should be + // overloaded by the derived class in this case. + return -1; + } + if (ptr_dtx_inst_ != NULL) { + WebRtcCng_FreeEnc(ptr_dtx_inst_); + ptr_dtx_inst_ = NULL; + } + dtx_enabled_ = false; + return 0; +} + +int16_t ACMGenericCodec::EnableVAD(ACMVADMode mode) { + if ((mode < VADNormal) || (mode > VADVeryAggr)) { + WEBRTC_TRACE(webrtc::kTraceError, webrtc::kTraceAudioCoding, unique_id_, + "EnableVAD: error in VAD mode range"); + return -1; + } + + if (!vad_enabled_) { + if (WebRtcVad_Create(&ptr_vad_inst_) < 0) { + ptr_vad_inst_ = NULL; + WEBRTC_TRACE(webrtc::kTraceError, webrtc::kTraceAudioCoding, unique_id_, + "EnableVAD: error in create VAD"); + return -1; + } + if (WebRtcVad_Init(ptr_vad_inst_) < 0) { + WebRtcVad_Free(ptr_vad_inst_); + ptr_vad_inst_ = NULL; + WEBRTC_TRACE(webrtc::kTraceError, webrtc::kTraceAudioCoding, unique_id_, + "EnableVAD: error in init VAD"); + return -1; + } + } + + // Set the VAD mode to the given value. + if (WebRtcVad_set_mode(ptr_vad_inst_, mode) < 0) { + // We failed to set the mode and we have to return -1. If we already have a + // working VAD (vad_enabled_ == true) then we leave it to work. Otherwise, + // the following will be executed. + if (!vad_enabled_) { + // We just created the instance but cannot set the mode we have to free + // the memory. + WebRtcVad_Free(ptr_vad_inst_); + ptr_vad_inst_ = NULL; + } + WEBRTC_TRACE(webrtc::kTraceDebug, webrtc::kTraceAudioCoding, unique_id_, + "EnableVAD: failed to set the VAD mode"); + return -1; + } + vad_mode_ = mode; + vad_enabled_ = true; + return 0; +} + +int16_t ACMGenericCodec::DisableVAD() { + if (ptr_vad_inst_ != NULL) { + WebRtcVad_Free(ptr_vad_inst_); + ptr_vad_inst_ = NULL; + } + vad_enabled_ = false; + return 0; +} + +int32_t ACMGenericCodec::ReplaceInternalDTX(const bool replace_internal_dtx) { + WriteLockScoped cs(codec_wrapper_lock_); + return ReplaceInternalDTXSafe(replace_internal_dtx); +} + +int32_t ACMGenericCodec::ReplaceInternalDTXSafe( + const bool /* replace_internal_dtx */) { + return -1; +} + +int32_t ACMGenericCodec::IsInternalDTXReplaced(bool* internal_dtx_replaced) { + WriteLockScoped cs(codec_wrapper_lock_); + return IsInternalDTXReplacedSafe(internal_dtx_replaced); +} + +int32_t ACMGenericCodec::IsInternalDTXReplacedSafe( + bool* internal_dtx_replaced) { + *internal_dtx_replaced = false; + return 0; +} + +int16_t ACMGenericCodec::ProcessFrameVADDTX(uint8_t* bitstream, + int16_t* bitstream_len_byte, + int16_t* samples_processed) { + if (!vad_enabled_) { + // VAD not enabled, set all |vad_lable_[]| to 1 (speech detected). + for (int n = 0; n < MAX_FRAME_SIZE_10MSEC; n++) { + vad_label_[n] = 1; + } + *samples_processed = 0; + return 0; + } + + uint16_t freq_hz; + EncoderSampFreq(&freq_hz); + + // Calculate number of samples in 10 ms blocks, and number ms in one frame. + int16_t samples_in_10ms = static_cast(freq_hz / 100); + int32_t frame_len_ms = static_cast(frame_len_smpl_) * 1000 / freq_hz; + int16_t status; + + // Vector for storing maximum 30 ms of mono audio at 48 kHz. + int16_t audio[1440]; + + // Calculate number of VAD-blocks to process, and number of samples in each + // block. + int num_samples_to_process[2]; + if (frame_len_ms == 40) { + // 20 ms in each VAD block. + num_samples_to_process[0] = num_samples_to_process[1] = 2 * samples_in_10ms; + } else { + // For 10-30 ms framesizes, second VAD block will be size zero ms, + // for 50 and 60 ms first VAD block will be 30 ms. + num_samples_to_process[0] = + (frame_len_ms > 30) ? 3 * samples_in_10ms : frame_len_smpl_; + num_samples_to_process[1] = frame_len_smpl_ - num_samples_to_process[0]; + } + + int offset = 0; + int loops = (num_samples_to_process[1] > 0) ? 2 : 1; + for (int i = 0; i < loops; i++) { + // TODO(turajs): Do we need to care about VAD together with stereo? + // If stereo, calculate mean of the two channels. + if (num_channels_ == 2) { + for (int j = 0; j < num_samples_to_process[i]; j++) { + audio[j] = (in_audio_[(offset + j) * 2] + + in_audio_[(offset + j) * 2 + 1]) / 2; + } + offset = num_samples_to_process[0]; + } else { + // Mono, copy data from in_audio_ to continue work on. + memcpy(audio, in_audio_, sizeof(int16_t) * num_samples_to_process[i]); + } + + // Call VAD. + status = static_cast(WebRtcVad_Process(ptr_vad_inst_, + static_cast(freq_hz), + audio, + num_samples_to_process[i])); + vad_label_[i] = status; + + if (status < 0) { + // This will force that the data be removed from the buffer. + *samples_processed += num_samples_to_process[i]; + return -1; + } + + // If VAD decision non-active, update DTX. NOTE! We only do this if the + // first part of a frame gets the VAD decision "inactive". Otherwise DTX + // might say it is time to transmit SID frame, but we will encode the whole + // frame, because the first part is active. + *samples_processed = 0; + if ((status == 0) && (i == 0) && dtx_enabled_ && !has_internal_dtx_) { + int16_t bitstream_len; + int num_10ms_frames = num_samples_to_process[i] / samples_in_10ms; + *bitstream_len_byte = 0; + for (int n = 0; n < num_10ms_frames; n++) { + // This block is (passive) && (vad enabled). If first CNG after + // speech, force SID by setting last parameter to "1". + status = WebRtcCng_Encode(ptr_dtx_inst_, &audio[n * samples_in_10ms], + samples_in_10ms, bitstream, &bitstream_len, + !prev_frame_cng_); + if (status < 0) { + return -1; + } + + // Update previous frame was CNG. + prev_frame_cng_ = 1; + + *samples_processed += samples_in_10ms * num_channels_; + + // |bitstream_len_byte| will only be > 0 once per 100 ms. + *bitstream_len_byte += bitstream_len; + } + + // Check if all samples got processed by the DTX. + if (*samples_processed != num_samples_to_process[i] * num_channels_) { + // Set to zero since something went wrong. Shouldn't happen. + *samples_processed = 0; + } + } else { + // Update previous frame was not CNG. + prev_frame_cng_ = 0; + } + + if (*samples_processed > 0) { + // The block contains inactive speech, and is processed by DTX. + // Discontinue running VAD. + break; + } + } + + return status; +} + +int16_t ACMGenericCodec::SamplesLeftToEncode() { + ReadLockScoped rl(codec_wrapper_lock_); + return (frame_len_smpl_ <= in_audio_ix_write_) ? 0 : + (frame_len_smpl_ - in_audio_ix_write_); +} + +void ACMGenericCodec::SetUniqueID(const uint32_t id) { + unique_id_ = id; +} + +// This function is replaced by codec specific functions for some codecs. +int16_t ACMGenericCodec::EncoderSampFreq(uint16_t* samp_freq_hz) { + int32_t f; + f = ACMCodecDB::CodecFreq(codec_id_); + if (f < 0) { + WEBRTC_TRACE(webrtc::kTraceError, webrtc::kTraceAudioCoding, unique_id_, + "EncoderSampFreq: codec frequency is negative"); + return -1; + } else { + *samp_freq_hz = static_cast(f); + return 0; + } +} + +int32_t ACMGenericCodec::ConfigISACBandwidthEstimator( + const uint8_t /* init_frame_size_msec */, + const uint16_t /* init_rate_bit_per_sec */, + const bool /* enforce_frame_size */) { + WEBRTC_TRACE(webrtc::kTraceWarning, webrtc::kTraceAudioCoding, unique_id_, + "The send-codec is not iSAC, failed to config iSAC bandwidth " + "estimator."); + return -1; +} + +int32_t ACMGenericCodec::SetISACMaxRate( + const uint32_t /* max_rate_bit_per_sec */) { + WEBRTC_TRACE(webrtc::kTraceWarning, webrtc::kTraceAudioCoding, unique_id_, + "The send-codec is not iSAC, failed to set iSAC max rate."); + return -1; +} + +int32_t ACMGenericCodec::SetISACMaxPayloadSize( + const uint16_t /* max_payload_len_bytes */) { + WEBRTC_TRACE(webrtc::kTraceWarning, webrtc::kTraceAudioCoding, unique_id_, + "The send-codec is not iSAC, failed to set iSAC max " + "payload-size."); + return -1; +} + +int16_t ACMGenericCodec::UpdateEncoderSampFreq( + uint16_t /* samp_freq_hz */) { + WEBRTC_TRACE(webrtc::kTraceError, webrtc::kTraceAudioCoding, unique_id_, + "It is asked for a change in smapling frequency while the " + "current send-codec supports only one sampling rate."); + return -1; +} + +int16_t ACMGenericCodec::REDPayloadISAC(const int32_t /* isac_rate */, + const int16_t /* isac_bw_estimate */, + uint8_t* /* payload */, + int16_t* /* payload_len_bytes */) { + WEBRTC_TRACE(webrtc::kTraceError, webrtc::kTraceAudioCoding, unique_id_, + "Error: REDPayloadISAC is an iSAC specific function"); + return -1; +} + +} // namespace webrtc diff --git a/webrtc/modules/audio_coding/main/acm2/acm_generic_codec.h b/webrtc/modules/audio_coding/main/acm2/acm_generic_codec.h new file mode 100644 index 0000000000..cde2e42843 --- /dev/null +++ b/webrtc/modules/audio_coding/main/acm2/acm_generic_codec.h @@ -0,0 +1,918 @@ +/* + * 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. + */ + +#ifndef WEBRTC_MODULES_AUDIO_CODING_MAIN_SOURCE_ACM_GENERIC_CODEC_H_ +#define WEBRTC_MODULES_AUDIO_CODING_MAIN_SOURCE_ACM_GENERIC_CODEC_H_ + +#include "webrtc/modules/audio_coding/main/interface/audio_coding_module_typedefs.h" +#include "webrtc/modules/audio_coding/main/source/acm_common_defs.h" +#include "webrtc/modules/audio_coding/neteq4/interface/neteq.h" +#include "webrtc/modules/audio_coding/neteq4/interface/audio_decoder.h" +#include "webrtc/system_wrappers/interface/rw_lock_wrapper.h" +#include "webrtc/system_wrappers/interface/trace.h" + +#define MAX_FRAME_SIZE_10MSEC 6 + +// forward declaration +struct WebRtcVadInst; +struct WebRtcCngEncInst; + +namespace webrtc { + +// forward declaration +struct CodecInst; +class AcmReceiver; + +class ACMGenericCodec { + public: + /////////////////////////////////////////////////////////////////////////// + // Constructor of the class + // + ACMGenericCodec(); + + /////////////////////////////////////////////////////////////////////////// + // Destructor of the class. + // + virtual ~ACMGenericCodec(); + + /////////////////////////////////////////////////////////////////////////// + // ACMGenericCodec* CreateInstance(); + // The function will be used for FEC. It is not implemented yet. + // + virtual ACMGenericCodec* CreateInstance() = 0; + + /////////////////////////////////////////////////////////////////////////// + // int16_t Encode() + // The function is called to perform an encoding of the audio stored in + // audio buffer. An encoding is performed only if enough audio, i.e. equal + // to the frame-size of the codec, exist. The audio frame will be processed + // by VAD and CN/DTX if required. There are few different cases. + // + // A) Neither VAD nor DTX is active; the frame is encoded by the encoder. + // + // B) VAD is enabled but not DTX; in this case the audio is processed by VAD + // and encoded by the encoder. The "*encoding_type" will be either + // "kActiveNormalEncode" or "kPassiveNormalEncode" if frame is active or + // passive, respectively. + // + // C) DTX is enabled; if the codec has internal VAD/DTX we just encode the + // frame by the encoder. Otherwise, the frame is passed through VAD and + // if identified as passive, then it will be processed by CN/DTX. If the + // frame is active it will be encoded by the encoder. + // + // This function acquires the appropriate locks and calls EncodeSafe() for + // the actual processing. + // + // Outputs: + // -bitstream : a buffer where bit-stream will be written to. + // -bitstream_len_byte : contains the length of the bit-stream in + // bytes. + // -timestamp : contains the RTP timestamp, this is the + // sampling time of the first sample encoded + // (measured in number of samples). + // -encoding_type : contains the type of encoding applied on the + // audio samples. The alternatives are + // (c.f. acm_common_types.h) + // -kNoEncoding: + // there was not enough data to encode. or + // some error has happened that we could + // not do encoding. + // -kActiveNormalEncoded: + // the audio frame is active and encoded by + // the given codec. + // -kPassiveNormalEncoded: + // the audio frame is passive but coded with + // the given codec (NO DTX). + // -kPassiveDTXWB: + // The audio frame is passive and used + // wide-band CN to encode. + // -kPassiveDTXNB: + // The audio frame is passive and used + // narrow-band CN to encode. + // + // Return value: + // -1 if error is occurred, otherwise the length of the bit-stream in + // bytes. + // + int16_t Encode(uint8_t* bitstream, + int16_t* bitstream_len_byte, + uint32_t* timestamp, + WebRtcACMEncodingType* encoding_type); + + /////////////////////////////////////////////////////////////////////////// + // bool EncoderInitialized(); + // + // Return value: + // True if the encoder is successfully initialized, + // false otherwise. + // + bool EncoderInitialized(); + + /////////////////////////////////////////////////////////////////////////// + // int16_t EncoderParams() + // It is called to get encoder parameters. It will call + // EncoderParamsSafe() in turn. + // + // Output: + // -enc_params : a buffer where the encoder parameters is + // written to. If the encoder is not + // initialized this buffer is filled with + // invalid values + // Return value: + // -1 if the encoder is not initialized, + // 0 otherwise. + // + int16_t EncoderParams(WebRtcACMCodecParams* enc_params); + + /////////////////////////////////////////////////////////////////////////// + // int16_t InitEncoder(...) + // This function is called to initialize the encoder with the given + // parameters. + // + // Input: + // -codec_params : parameters of encoder. + // -force_initialization: if false the initialization is invoked only if + // the encoder is not initialized. If true the + // encoder is forced to (re)initialize. + // + // Return value: + // 0 if could initialize successfully, + // -1 if failed to initialize. + // + // + int16_t InitEncoder(WebRtcACMCodecParams* codec_params, + bool force_initialization); + + /////////////////////////////////////////////////////////////////////////// + // int32_t Add10MsData(...) + // This function is called to add 10 ms of audio to the audio buffer of + // the codec. + // + // Inputs: + // -timestamp : the timestamp of the 10 ms audio. the timestamp + // is the sampling time of the + // first sample measured in number of samples. + // -data : a buffer that contains the audio. The codec + // expects to get the audio in correct sampling + // frequency + // -length : the length of the audio buffer + // -audio_channel : 0 for mono, 1 for stereo (not supported yet) + // + // Return values: + // -1 if failed + // 0 otherwise. + // + int32_t Add10MsData(const uint32_t timestamp, + const int16_t* data, + const uint16_t length, + const uint8_t audio_channel); + + /////////////////////////////////////////////////////////////////////////// + // uint32_t NoMissedSamples() + // This function returns the number of samples which are overwritten in + // the audio buffer. The audio samples are overwritten if the input audio + // buffer is full, but Add10MsData() is called. (We might remove this + // function if it is not used) + // + // Return Value: + // Number of samples which are overwritten. + // + uint32_t NoMissedSamples() const; + + /////////////////////////////////////////////////////////////////////////// + // void ResetNoMissedSamples() + // This function resets the number of overwritten samples to zero. + // (We might remove this function if we remove NoMissedSamples()) + // + void ResetNoMissedSamples(); + + /////////////////////////////////////////////////////////////////////////// + // int16_t SetBitRate() + // The function is called to set the encoding rate. + // + // Input: + // -bitrate_bps : encoding rate in bits per second + // + // Return value: + // -1 if failed to set the rate, due to invalid input or given + // codec is not rate-adjustable. + // 0 if the rate is adjusted successfully + // + int16_t SetBitRate(const int32_t bitrate_bps); + + /////////////////////////////////////////////////////////////////////////// + // DestructEncoderInst() + // This API is used in conferencing. It will free the memory that is pointed + // by |ptr_inst|. |ptr_inst| is a pointer to encoder instance, created and + // filled up by calling EncoderInst(...). + // + // Inputs: + // -ptr_inst : pointer to an encoder instance to be deleted. + // + // + void DestructEncoderInst(void* ptr_inst); + + /////////////////////////////////////////////////////////////////////////// + // uint32_t EarliestTimestamp() + // Returns the timestamp of the first 10 ms in audio buffer. This is used + // to identify if a synchronization of two encoders is required. + // + // Return value: + // timestamp of the first 10 ms audio in the audio buffer. + // + uint32_t EarliestTimestamp() const; + + /////////////////////////////////////////////////////////////////////////// + // int16_t SetVAD() + // This is called to set VAD & DTX. If the codec has internal DTX that will + // be used. If DTX is enabled and the codec does not have internal DTX, + // WebRtc-VAD will be used to decide if the frame is active. If DTX is + // disabled but VAD is enabled. The audio is passed through VAD to label it + // as active or passive, but the frame is encoded normally. However the + // bit-stream is labeled properly so that ACM::Process() can use this + // information. In case of failure, the previous states of the VAD & DTX + // are kept. + // + // Inputs: + // -enable_dtx : if true DTX will be enabled otherwise the DTX is + // disabled. If codec has internal DTX that will be + // used, otherwise WebRtc-CNG is used. In the latter + // case VAD is automatically activated. + // -enable_vad : if true WebRtc-VAD is enabled, otherwise VAD is + // disabled, except for the case that DTX is enabled + // but codec doesn't have internal DTX. In this case + // VAD is enabled regardless of the value of + // |enable_vad|. + // -mode : this specifies the aggressiveness of VAD. + // + // Return value + // -1 if failed to set DTX & VAD as specified, + // 0 if succeeded. + // + int16_t SetVAD(const bool enable_dtx = true, + const bool enable_vad = false, + const ACMVADMode mode = VADNormal); + + /////////////////////////////////////////////////////////////////////////// + // int32_t ReplaceInternalDTX() + // This is called to replace the codec internal DTX with WebRtc DTX. + // This is only valid for G729 where the user has possibility to replace + // AnnexB with WebRtc DTX. For other codecs this function has no effect. + // + // Input: + // -replace_internal_dtx : if true the internal DTX is replaced with WebRtc. + // + // Return value + // -1 if failed to replace internal DTX, + // 0 if succeeded. + // + int32_t ReplaceInternalDTX(const bool replace_internal_dtx); + + /////////////////////////////////////////////////////////////////////////// + // int32_t IsInternalDTXReplaced() + // This is called to check if the codec internal DTX is replaced by WebRtc + // DTX. This is only valid for G729 where the user has possibility to replace + // AnnexB with WebRtc DTX. For other codecs this function has no effect. + // + // Output: + // -internal_dtx_replaced: if true the internal DTX is replaced with WebRtc. + // + // Return value + // -1 if failed to check + // 0 if succeeded. + // + int32_t IsInternalDTXReplaced(bool* internal_dtx_replaced); + + /////////////////////////////////////////////////////////////////////////// + // void SetNetEqDecodeLock() + // Passes the NetEq lock to the codec. + // + // Input: + // -neteq_decode_lock : pointer to the lock associated with NetEQ of ACM. + // + void SetNetEqDecodeLock(RWLockWrapper* neteq_decode_lock) { + neteq_decode_lock_ = neteq_decode_lock; + } + + /////////////////////////////////////////////////////////////////////////// + // bool HasInternalDTX() + // Used to check if the codec has internal DTX. + // + // Return value: + // true if the codec has an internal DTX, e.g. G729, + // false otherwise. + // + bool HasInternalDTX() const { return has_internal_dtx_; } + + /////////////////////////////////////////////////////////////////////////// + // int32_t GetEstimatedBandwidth() + // Used to get decoder estimated bandwidth. Only iSAC will provide a value. + // + // + // Return value: + // -1 if fails to get decoder estimated bandwidth, + // >0 estimated bandwidth in bits/sec. + // + int32_t GetEstimatedBandwidth(); + + /////////////////////////////////////////////////////////////////////////// + // int32_t SetEstimatedBandwidth() + // Used to set estiamted bandwidth sent out of band from other side. Only + // iSAC will have use for the value. + // + // Input: + // -estimated_bandwidth: estimated bandwidth in bits/sec + // + // Return value: + // -1 if fails to set estimated bandwidth, + // 0 on success. + // + int32_t SetEstimatedBandwidth(int32_t estimated_bandwidth); + + /////////////////////////////////////////////////////////////////////////// + // int32_t GetRedPayload() + // Used to get codec specific RED payload (if such is implemented). + // Currently only done in iSAC. + // + // Outputs: + // -red_payload : a pointer to the data for RED payload. + // -payload_bytes : number of bytes in RED payload. + // + // Return value: + // -1 if fails to get codec specific RED, + // 0 if succeeded. + // + int32_t GetRedPayload(uint8_t* red_payload, int16_t* payload_bytes); + + /////////////////////////////////////////////////////////////////////////// + // int16_t ResetEncoder() + // By calling this function you would re-initialize the encoder with the + // current parameters. All the settings, e.g. VAD/DTX, frame-size... should + // remain unchanged. (In case of iSAC we don't want to lose BWE history.) + // + // Return value + // -1 if failed, + // 0 if succeeded. + // + int16_t ResetEncoder(); + + /////////////////////////////////////////////////////////////////////////// + // void DestructEncoder() + // This function is called to delete the encoder instance, if possible, to + // have a fresh start. For codecs where encoder and decoder share the same + // instance we cannot delete the encoder and instead we will initialize the + // encoder. We also delete VAD and DTX if they have been created. + // + void DestructEncoder(); + + /////////////////////////////////////////////////////////////////////////// + // int16_t SamplesLeftToEncode() + // Returns the number of samples required to be able to do encoding. + // + // Return value: + // Number of samples. + // + int16_t SamplesLeftToEncode(); + + /////////////////////////////////////////////////////////////////////////// + // SetUniqueID() + // Set a unique ID for the codec to be used for tracing and debugging + // + // Input + // -id : A number to identify the codec. + // + void SetUniqueID(const uint32_t id); + + /////////////////////////////////////////////////////////////////////////// + // UpdateDecoderSampFreq() + // For most of the codecs this function does nothing. It must be + // implemented for those codecs that one codec instance serves as the + // decoder for different flavors of the codec. One example is iSAC. there, + // iSAC 16 kHz and iSAC 32 kHz are treated as two different codecs with + // different payload types, however, there is only one iSAC instance to + // decode. The reason for that is we would like to decode and encode with + // the same codec instance for bandwidth estimator to work. + // + // Each time that we receive a new payload type, we call this function to + // prepare the decoder associated with the new payload. Normally, decoders + // doesn't have to do anything. For iSAC the decoder has to change it's + // sampling rate. The input parameter specifies the current flavor of the + // codec in codec database. For instance, if we just got a SWB payload then + // the input parameter is ACMCodecDB::isacswb. + // + // Input: + // -codec_id : the ID of the codec associated with the + // payload type that we just received. + // + // Return value: + // 0 if succeeded in updating the decoder. + // -1 if failed to update. + // + virtual int16_t UpdateDecoderSampFreq(int16_t /* codec_id */) { return 0; } + + /////////////////////////////////////////////////////////////////////////// + // UpdateEncoderSampFreq() + // Call this function to update the encoder sampling frequency. This + // is for codecs where one payload-name supports several encoder sampling + // frequencies. Otherwise, to change the sampling frequency we need to + // register new codec. ACM will consider that as registration of a new + // codec, not a change in parameter. For iSAC, switching from WB to SWB + // is treated as a change in parameter. Therefore, we need this function. + // + // Input: + // -samp_freq_hz : encoder sampling frequency. + // + // Return value: + // -1 if failed, or if this is meaningless for the given codec. + // 0 if succeeded. + // + virtual int16_t UpdateEncoderSampFreq(uint16_t samp_freq_hz); + + /////////////////////////////////////////////////////////////////////////// + // EncoderSampFreq() + // Get the sampling frequency that the encoder (WebRtc wrapper) expects. + // + // Output: + // -samp_freq_hz : sampling frequency, in Hertz, which the encoder + // should be fed with. + // + // Return value: + // -1 if failed to output sampling rate. + // 0 if the sample rate is returned successfully. + // + virtual int16_t EncoderSampFreq(uint16_t* samp_freq_hz); + + /////////////////////////////////////////////////////////////////////////// + // int32_t ConfigISACBandwidthEstimator() + // Call this function to configure the bandwidth estimator of ISAC. + // During the adaptation of bit-rate, iSAC automatically adjusts the + // frame-size (either 30 or 60 ms) to save on RTP header. The initial + // frame-size can be specified by the first argument. The configuration also + // regards the initial estimate of bandwidths. The estimator starts from + // this point and converges to the actual bottleneck. This is given by the + // second parameter. Furthermore, it is also possible to control the + // adaptation of frame-size. This is specified by the last parameter. + // + // Input: + // -init_frame_fize_ms : initial frame-size in milliseconds. For iSAC-wb + // 30 ms and 60 ms (default) are acceptable values, + // and for iSAC-swb 30 ms is the only acceptable + // value. Zero indicates default value. + // -init_rate_bps : initial estimate of the bandwidth. Values + // between 10000 and 58000 are acceptable. + // -enforce_frame_size : if true, the frame-size will not be adapted. + // + // Return value: + // -1 if failed to configure the bandwidth estimator, + // 0 if the configuration was successfully applied. + // + virtual int32_t ConfigISACBandwidthEstimator( + const uint8_t init_frame_size_msec, + const uint16_t init_rate_bps, + const bool enforce_frame_size); + + /////////////////////////////////////////////////////////////////////////// + // SetISACMaxPayloadSize() + // Set the maximum payload size of iSAC packets. No iSAC payload, + // regardless of its frame-size, may exceed the given limit. For + // an iSAC payload of size B bits and frame-size T sec we have; + // (B < max_payload_len_bytes * 8) and (B/T < max_rate_bit_per_sec), c.f. + // SetISACMaxRate(). + // + // Input: + // -max_payload_len_bytes : maximum payload size in bytes. + // + // Return value: + // -1 if failed to set the maximum payload-size. + // 0 if the given length is set successfully. + // + virtual int32_t SetISACMaxPayloadSize(const uint16_t max_payload_len_bytes); + + /////////////////////////////////////////////////////////////////////////// + // SetISACMaxRate() + // Set the maximum instantaneous rate of iSAC. For a payload of B bits + // with a frame-size of T sec the instantaneous rate is B/T bits per + // second. Therefore, (B/T < max_rate_bit_per_sec) and + // (B < max_payload_len_bytes * 8) are always satisfied for iSAC payloads, + // c.f SetISACMaxPayloadSize(). + // + // Input: + // -max_rate_bps : maximum instantaneous bit-rate given in bits/sec. + // + // Return value: + // -1 if failed to set the maximum rate. + // 0 if the maximum rate is set successfully. + // + virtual int32_t SetISACMaxRate(const uint32_t max_rate_bps); + + int32_t FrameSize() { return frame_len_smpl_; } + + /////////////////////////////////////////////////////////////////////////// + // REDPayloadISAC() + // This is an iSAC-specific function. The function is called to get RED + // payload from a default-encoder. + // + // Inputs: + // -isac_rate : the target rate of the main payload. A RED + // payload is generated according to the rate of + // main payload. Note that we are not specifying the + // rate of RED payload, but the main payload. + // -isac_bw_estimate : bandwidth information should be inserted in + // RED payload. + // + // Output: + // -payload : pointer to a buffer where the RED payload will + // written to. + // -payload_len_bytes : a place-holder to write the length of the RED + // payload in Bytes. + // + // Return value: + // -1 if an error occurs, otherwise the length of the payload (in Bytes) + // is returned. + // + virtual int16_t REDPayloadISAC(const int32_t isac_rate, + const int16_t isac_bw_estimate, + uint8_t* payload, + int16_t* payload_len_bytes); + + /////////////////////////////////////////////////////////////////////////// + // HasFrameToEncode() + // Returns true if there is enough audio buffered for encoding, such that + // calling Encode() will return a payload. + // + bool HasFrameToEncode() const; + + // + // Returns pointer to the AudioDecoder class of this codec. A codec which + // should own its own decoder (e.g. iSAC which need same instance for encoding + // and decoding, or a codec which should access decoder instance for specific + // decoder setting) should implement this method. This method is called if + // and only if the ACMCodecDB::codec_settings[codec_id].owns_decoder is true. + // + virtual AudioDecoder* Decoder(int /* codec_id */) { return NULL; } + + protected: + /////////////////////////////////////////////////////////////////////////// + // All the functions with FunctionNameSafe(...) contain the actual + // implementation of FunctionName(...). FunctionName() acquires an + // appropriate lock and calls FunctionNameSafe() to do the actual work. + // Therefore, for the description of functionality, input/output arguments + // and return value we refer to FunctionName() + // + + /////////////////////////////////////////////////////////////////////////// + // See Add10MsSafe() for the description of function, input(s)/output(s) + // and return value. + // + virtual int32_t Add10MsDataSafe(const uint32_t timestamp, + const int16_t* data, + const uint16_t length, + const uint8_t audio_channel); + + /////////////////////////////////////////////////////////////////////////// + // See EncoderParam() for the description of function, input(s)/output(s) + // and return value. + // + int16_t EncoderParamsSafe(WebRtcACMCodecParams* enc_params); + + /////////////////////////////////////////////////////////////////////////// + // See ResetEncoder() for the description of function, input(s)/output(s) + // and return value. + // + int16_t ResetEncoderSafe(); + + /////////////////////////////////////////////////////////////////////////// + // See InitEncoder() for the description of function, input(s)/output(s) + // and return value. + // + int16_t InitEncoderSafe(WebRtcACMCodecParams* codec_params, + bool force_initialization); + + /////////////////////////////////////////////////////////////////////////// + // See InitDecoder() for the description of function, input(s)/output(s) + // and return value. + // + int16_t InitDecoderSafe(WebRtcACMCodecParams* codec_params, + bool force_initialization); + + /////////////////////////////////////////////////////////////////////////// + // See DestructEncoder() for the description of function, + // input(s)/output(s) and return value. + // + virtual void DestructEncoderSafe() = 0; + + /////////////////////////////////////////////////////////////////////////// + // See SetBitRate() for the description of function, input(s)/output(s) + // and return value. + // + // Any codec that can change the bit-rate has to implement this. + // + virtual int16_t SetBitRateSafe(const int32_t bitrate_bps); + + /////////////////////////////////////////////////////////////////////////// + // See GetEstimatedBandwidth() for the description of function, + // input(s)/output(s) and return value. + // + virtual int32_t GetEstimatedBandwidthSafe(); + + /////////////////////////////////////////////////////////////////////////// + // See SetEstimatedBandwidth() for the description of function, + // input(s)/output(s) and return value. + // + virtual int32_t SetEstimatedBandwidthSafe(int32_t estimated_bandwidth); + + /////////////////////////////////////////////////////////////////////////// + // See GetRedPayload() for the description of function, input(s)/output(s) + // and return value. + // + virtual int32_t GetRedPayloadSafe(uint8_t* red_payload, + int16_t* payload_bytes); + + /////////////////////////////////////////////////////////////////////////// + // See SetVAD() for the description of function, input(s)/output(s) and + // return value. + // + int16_t SetVADSafe(const bool enable_dtx = true, + const bool enable_vad = false, + const ACMVADMode mode = VADNormal); + + /////////////////////////////////////////////////////////////////////////// + // See ReplaceInternalDTX() for the description of function, input and + // return value. + // + virtual int32_t ReplaceInternalDTXSafe(const bool replace_internal_dtx); + + /////////////////////////////////////////////////////////////////////////// + // See IsInternalDTXReplaced() for the description of function, input and + // return value. + // + virtual int32_t IsInternalDTXReplacedSafe(bool* internal_dtx_replaced); + + /////////////////////////////////////////////////////////////////////////// + // int16_t CreateEncoder() + // Creates the encoder instance. + // + // Return value: + // -1 if failed, + // 0 if succeeded. + // + int16_t CreateEncoder(); + + /////////////////////////////////////////////////////////////////////////// + // int16_t EnableVAD(); + // Enables VAD with the given mode. The VAD instance will be created if + // it does not exists. + // + // Input: + // -mode : VAD mode c.f. audio_coding_module_typedefs.h for + // the options. + // + // Return value: + // -1 if failed, + // 0 if succeeded. + // + int16_t EnableVAD(ACMVADMode mode); + + /////////////////////////////////////////////////////////////////////////// + // int16_t DisableVAD() + // Disables VAD. + // + // Return value: + // -1 if failed, + // 0 if succeeded. + // + int16_t DisableVAD(); + + /////////////////////////////////////////////////////////////////////////// + // int16_t EnableDTX() + // Enables DTX. This method should be overwritten for codecs which have + // internal DTX. + // + // Return value: + // -1 if failed, + // 0 if succeeded. + // + virtual int16_t EnableDTX(); + + /////////////////////////////////////////////////////////////////////////// + // int16_t DisableDTX() + // Disables usage of DTX. This method should be overwritten for codecs which + // have internal DTX. + // + // Return value: + // -1 if failed, + // 0 if succeeded. + // + virtual int16_t DisableDTX(); + + /////////////////////////////////////////////////////////////////////////// + // int16_t InternalEncode() + // This is a codec-specific function called in EncodeSafe() to actually + // encode a frame of audio. + // + // Outputs: + // -bitstream : pointer to a buffer where the bit-stream is + // written to. + // -bitstream_len_byte : the length of the bit-stream in bytes, + // a negative value indicates error. + // + // Return value: + // -1 if failed, + // otherwise the length of the bit-stream is returned. + // + virtual int16_t InternalEncode(uint8_t* bitstream, + int16_t* bitstream_len_byte) = 0; + + /////////////////////////////////////////////////////////////////////////// + // int16_t InternalInitEncoder() + // This is a codec-specific function called in InitEncoderSafe(), it has to + // do all codec-specific operation to initialize the encoder given the + // encoder parameters. + // + // Input: + // -codec_params : pointer to a structure that contains parameters to + // initialize encoder. + // Set codec_params->codec_inst.rate to -1 for + // iSAC to operate in adaptive mode. + // (to do: if frame-length is -1 frame-length will be + // automatically adjusted, otherwise, given + // frame-length is forced) + // + // Return value: + // -1 if failed, + // 0 if succeeded. + // + virtual int16_t InternalInitEncoder(WebRtcACMCodecParams* codec_params) = 0; + + /////////////////////////////////////////////////////////////////////////// + // void IncreaseNoMissedSamples() + // This method is called to increase the number of samples that are + // overwritten in the audio buffer. + // + // Input: + // -num_samples : the number of overwritten samples is incremented + // by this value. + // + void IncreaseNoMissedSamples(const int16_t num_samples); + + /////////////////////////////////////////////////////////////////////////// + // int16_t InternalCreateEncoder() + // This is a codec-specific method called in CreateEncoderSafe() it is + // supposed to perform all codec-specific operations to create encoder + // instance. + // + // Return value: + // -1 if failed, + // 0 if succeeded. + // + virtual int16_t InternalCreateEncoder() = 0; + + /////////////////////////////////////////////////////////////////////////// + // void InternalDestructEncoderInst() + // This is a codec-specific method, used in conferencing, called from + // DestructEncoderInst(). The input argument is pointer to encoder instance + // (codec instance for codecs that encoder and decoder share the same + // instance). This method is called to free the memory that |ptr_inst| is + // pointing to. + // + // Input: + // -ptr_inst : pointer to encoder instance. + // + // Return value: + // -1 if failed, + // 0 if succeeded. + // + virtual void InternalDestructEncoderInst(void* ptr_inst) = 0; + + /////////////////////////////////////////////////////////////////////////// + // int16_t InternalResetEncoder() + // This method is called to reset the states of encoder. However, the + // current parameters, e.g. frame-length, should remain as they are. For + // most of the codecs a re-initialization of the encoder is what needs to + // be down. But for iSAC we like to keep the BWE history so we cannot + // re-initialize. As soon as such an API is implemented in iSAC this method + // has to be overwritten in ACMISAC class. + // + // Return value: + // -1 if failed, + // 0 if succeeded. + // + virtual int16_t InternalResetEncoder(); + + /////////////////////////////////////////////////////////////////////////// + // int16_t ProcessFrameVADDTX() + // This function is called when a full frame of audio is available. It will + // break the audio frame into blocks such that each block could be processed + // by VAD & CN/DTX. If a frame is divided into two blocks then there are two + // cases. First, the first block is active, the second block will not be + // processed by CN/DTX but only by VAD and return to caller with + // '*samples_processed' set to zero. There, the audio frame will be encoded + // by the encoder. Second, the first block is inactive and is processed by + // CN/DTX, then we stop processing the next block and return to the caller + // which is EncodeSafe(), with "*samples_processed" equal to the number of + // samples in first block. + // + // Output: + // -bitstream : pointer to a buffer where DTX frame, if + // generated, will be written to. + // -bitstream_len_byte : contains the length of bit-stream in bytes, if + // generated. Zero if no bit-stream is generated. + // -samples_processed : contains no of samples that actually CN has + // processed. Those samples processed by CN will not + // be encoded by the encoder, obviously. If + // contains zero, it means that the frame has been + // identified as active by VAD. Note that + // "*samples_processed" might be non-zero but + // "*bitstream_len_byte" be zero. + // + // Return value: + // -1 if failed, + // 0 if succeeded. + // + int16_t ProcessFrameVADDTX(uint8_t* bitstream, + int16_t* bitstream_len_byte, + int16_t* samples_processed); + + /////////////////////////////////////////////////////////////////////////// + // CurrentRate() + // Call to get the current encoding rate of the encoder. This function + // should be overwritten for codecs which automatically change their + // target rate. One example is iSAC. The output of the function is the + // current target rate. + // + // Output: + // -rate_bps : the current target rate of the codec. + // + virtual void CurrentRate(int32_t* /* rate_bps */) {} + + // &in_audio_[in_audio_ix_write_] always point to where new audio can be + // written to + int16_t in_audio_ix_write_; + + // &in_audio_[in_audio_ix_read_] points to where audio has to be read from + int16_t in_audio_ix_read_; + + int16_t in_timestamp_ix_write_; + + // Where the audio is stored before encoding, + // To save memory the following buffer can be allocated + // dynamically for 80 ms depending on the sampling frequency + // of the codec. + int16_t* in_audio_; + uint32_t* in_timestamp_; + + int16_t frame_len_smpl_; + uint16_t num_channels_; + + // This will point to a static database of the supported codecs + int16_t codec_id_; + + // This will account for the number of samples were not encoded + // the case is rare, either samples are missed due to overwrite + // at input buffer or due to encoding error + uint32_t num_missed_samples_; + + // True if the encoder instance created + bool encoder_exist_; + + // True if the encoder instance initialized + bool encoder_initialized_; + + bool registered_in_neteq_; + + // VAD/DTX + bool has_internal_dtx_; + WebRtcVadInst* ptr_vad_inst_; + bool vad_enabled_; + ACMVADMode vad_mode_; + int16_t vad_label_[MAX_FRAME_SIZE_10MSEC]; + bool dtx_enabled_; + WebRtcCngEncInst* ptr_dtx_inst_; + uint8_t num_lpc_params_; + bool sent_cn_previous_; + int16_t prev_frame_cng_; + + WebRtcACMCodecParams encoder_params_; + + // Used as a global lock for all available decoders + // so that no decoder is used when NetEQ decodes. + RWLockWrapper* neteq_decode_lock_; + + // Used to lock wrapper internal data + // such as buffers and state variables. + RWLockWrapper& codec_wrapper_lock_; + + uint32_t last_timestamp_; + uint32_t unique_id_; +}; + +} // namespace webrtc + +#endif // WEBRTC_MODULES_AUDIO_CODING_MAIN_SOURCE_ACM_GENERIC_CODEC_H_ diff --git a/webrtc/modules/audio_coding/main/acm2/acm_gsmfr.cc b/webrtc/modules/audio_coding/main/acm2/acm_gsmfr.cc new file mode 100644 index 0000000000..9fd097c2ef --- /dev/null +++ b/webrtc/modules/audio_coding/main/acm2/acm_gsmfr.cc @@ -0,0 +1,157 @@ +/* + * 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/source/acm_gsmfr.h" + +#ifdef WEBRTC_CODEC_GSMFR +// NOTE! GSM-FR is not included in the open-source package. Modify this file +// or your codec API to match the function calls and names of used GSM-FR API +// file. +#include "webrtc/modules/audio_coding/main/codecs/gsmfr/interface/gsmfr_interface.h" +#include "webrtc/modules/audio_coding/main/source/acm_common_defs.h" +#include "webrtc/system_wrappers/interface/trace.h" +#endif + +namespace webrtc { + +#ifndef WEBRTC_CODEC_GSMFR + +ACMGSMFR::ACMGSMFR(int16_t /* codec_id */) : encoder_inst_ptr_(NULL) {} + +ACMGSMFR::~ACMGSMFR() { return; } + +int16_t ACMGSMFR::InternalEncode(uint8_t* /* bitstream */, + int16_t* /* bitstream_len_byte */) { + return -1; +} + +int16_t ACMGSMFR::EnableDTX() { return -1; } + +int16_t ACMGSMFR::DisableDTX() { return -1; } + +int16_t ACMGSMFR::InternalInitEncoder( + WebRtcACMCodecParams* /* codec_params */) { + return -1; +} + +ACMGenericCodec* ACMGSMFR::CreateInstance(void) { return NULL; } + +int16_t ACMGSMFR::InternalCreateEncoder() { return -1; } + +void ACMGSMFR::DestructEncoderSafe() { return; } + +void ACMGSMFR::InternalDestructEncoderInst(void* /* ptr_inst */) { + return; +} + +#else //===================== Actual Implementation ======================= + +ACMGSMFR::ACMGSMFR(int16_t codec_id) + : codec_id_(codec_id), + has_internal_dtx_(true), + encoder_inst_ptr_(NULL) {} + +ACMGSMFR::~ACMGSMFR() { + if (encoder_inst_ptr_ != NULL) { + WebRtcGSMFR_FreeEnc(encoder_inst_ptr_); + encoder_inst_ptr_ = NULL; + } + return; +} + +int16_t ACMGSMFR::InternalEncode(uint8_t* bitstream, + int16_t* bitstream_len_byte) { + *bitstream_len_byte = WebRtcGSMFR_Encode( + encoder_inst_ptr_, &in_audio_[in_audio_ix_read_], frame_len_smpl_, + reinterpret_cast(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_ += frame_len_smpl_; + return *bitstream_len_byte; +} + +int16_t ACMGSMFR::EnableDTX() { + if (dtx_enabled_) { + return 0; + } else if (encoder_exist_) { + if (WebRtcGSMFR_EncoderInit(encoder_inst_ptr_, 1) < 0) { + WEBRTC_TRACE(webrtc::kTraceError, webrtc::kTraceAudioCoding, unique_id_, + "EnableDTX: cannot init encoder for GSMFR"); + return -1; + } + dtx_enabled_ = true; + return 0; + } else { + return -1; + } +} + +int16_t ACMGSMFR::DisableDTX() { + if (!dtx_enabled_) { + return 0; + } else if (encoder_exist_) { + if (WebRtcGSMFR_EncoderInit(encoder_inst_ptr_, 0) < 0) { + WEBRTC_TRACE(webrtc::kTraceError, webrtc::kTraceAudioCoding, unique_id_, + "DisableDTX: cannot init encoder for GSMFR"); + return -1; + } + dtx_enabled_ = false; + return 0; + } else { + // encoder doesn't exists, therefore disabling is harmless + return 0; + } +} + +int16_t ACMGSMFR::InternalInitEncoder(WebRtcACMCodecParams* codec_params) { + if (WebRtcGSMFR_EncoderInit(encoder_inst_ptr_, + ((codec_params->enable_dtx) ? 1 : 0)) < 0) { + WEBRTC_TRACE(webrtc::kTraceError, + webrtc::kTraceAudioCoding, + unique_id_, + "InternalInitEncoder: cannot init encoder for GSMFR"); + } + return 0; +} + +ACMGenericCodec* ACMGSMFR::CreateInstance(void) { return NULL; } + +int16_t ACMGSMFR::InternalCreateEncoder() { + if (WebRtcGSMFR_CreateEnc(&encoder_inst_ptr_) < 0) { + WEBRTC_TRACE(webrtc::kTraceError, + webrtc::kTraceAudioCoding, + unique_id_, + "InternalCreateEncoder: cannot create instance for GSMFR " + "encoder"); + return -1; + } + return 0; +} + +void ACMGSMFR::DestructEncoderSafe() { + if (encoder_inst_ptr_ != NULL) { + WebRtcGSMFR_FreeEnc(encoder_inst_ptr_); + encoder_inst_ptr_ = NULL; + } + encoder_exist_ = false; + encoder_initialized_ = false; +} + +void ACMGSMFR::InternalDestructEncoderInst(void* ptr_inst) { + if (ptr_inst != NULL) { + WebRtcGSMFR_FreeEnc(static_cast(ptr_inst)); + } + return; +} + +#endif + +} // namespace webrtc diff --git a/webrtc/modules/audio_coding/main/acm2/acm_gsmfr.h b/webrtc/modules/audio_coding/main/acm2/acm_gsmfr.h new file mode 100644 index 0000000000..935ac444fa --- /dev/null +++ b/webrtc/modules/audio_coding/main/acm2/acm_gsmfr.h @@ -0,0 +1,50 @@ +/* + * 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. + */ + +#ifndef WEBRTC_MODULES_AUDIO_CODING_MAIN_SOURCE_ACM_GSMFR_H_ +#define WEBRTC_MODULES_AUDIO_CODING_MAIN_SOURCE_ACM_GSMFR_H_ + +#include "webrtc/modules/audio_coding/main/source/acm_generic_codec.h" + +// forward declaration +struct GSMFR_encinst_t_; +struct GSMFR_decinst_t_; + +namespace webrtc { + +class ACMGSMFR : public ACMGenericCodec { + public: + explicit ACMGSMFR(int16_t codec_id); + ~ACMGSMFR(); + + // for FEC + ACMGenericCodec* CreateInstance(void); + + int16_t InternalEncode(uint8_t* bitstream, int16_t* bitstream_len_byte); + + int16_t InternalInitEncoder(WebRtcACMCodecParams* codec_params); + + protected: + void DestructEncoderSafe(); + + int16_t InternalCreateEncoder(); + + void InternalDestructEncoderInst(void* ptr_inst); + + int16_t EnableDTX(); + + int16_t DisableDTX(); + + GSMFR_encinst_t_* encoder_inst_ptr_; +}; + +} // namespace webrtc + +#endif // WEBRTC_MODULES_AUDIO_CODING_MAIN_SOURCE_ACM_GSMFR_H_ diff --git a/webrtc/modules/audio_coding/main/acm2/acm_ilbc.cc b/webrtc/modules/audio_coding/main/acm2/acm_ilbc.cc new file mode 100644 index 0000000000..204e1e950f --- /dev/null +++ b/webrtc/modules/audio_coding/main/acm2/acm_ilbc.cc @@ -0,0 +1,141 @@ +/* + * 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/source/acm_ilbc.h" + +#ifdef WEBRTC_CODEC_ILBC +#include "webrtc/modules/audio_coding/codecs/ilbc/interface/ilbc.h" +#include "webrtc/modules/audio_coding/main/source/acm_common_defs.h" +#include "webrtc/system_wrappers/interface/trace.h" +#endif + +namespace webrtc { + +#ifndef WEBRTC_CODEC_ILBC + +ACMILBC::ACMILBC(int16_t /* codec_id */) : encoder_inst_ptr_(NULL) {} + +ACMILBC::~ACMILBC() { return; } + +int16_t ACMILBC::InternalEncode(uint8_t* /* bitstream */, + int16_t* /* bitstream_len_byte */) { + return -1; +} + +int16_t ACMILBC::InternalInitEncoder(WebRtcACMCodecParams* /* codec_params */) { + return -1; +} + +ACMGenericCodec* ACMILBC::CreateInstance(void) { return NULL; } + +int16_t ACMILBC::InternalCreateEncoder() { return -1; } + +void ACMILBC::DestructEncoderSafe() { return; } + +void ACMILBC::InternalDestructEncoderInst(void* /* ptr_inst */) { return; } + +int16_t ACMILBC::SetBitRateSafe(const int32_t /* rate */) { return -1; } + +#else //===================== Actual Implementation ======================= + +ACMILBC::ACMILBC(int16_t codec_id) : encoder_inst_ptr_(NULL) { + codec_id_ = codec_id; + return; +} + +ACMILBC::~ACMILBC() { + if (encoder_inst_ptr_ != NULL) { + WebRtcIlbcfix_EncoderFree(encoder_inst_ptr_); + encoder_inst_ptr_ = NULL; + } + return; +} + +int16_t ACMILBC::InternalEncode(uint8_t* bitstream, + int16_t* bitstream_len_byte) { + *bitstream_len_byte = WebRtcIlbcfix_Encode( + encoder_inst_ptr_, &in_audio_[in_audio_ix_read_], frame_len_smpl_, + reinterpret_cast(bitstream)); + if (*bitstream_len_byte < 0) { + WEBRTC_TRACE(webrtc::kTraceError, + webrtc::kTraceAudioCoding, + unique_id_, + "InternalEncode: error in encode for ILBC"); + return -1; + } + // increment the read index this tell the caller that how far + // we have gone forward in reading the audio buffer + in_audio_ix_read_ += frame_len_smpl_; + return *bitstream_len_byte; +} + +int16_t ACMILBC::InternalInitEncoder(WebRtcACMCodecParams* codec_params) { + // initialize with a correct processing block length + if ((160 == (codec_params->codec_inst).pacsize) || + (320 == (codec_params->codec_inst).pacsize)) { + // processing block of 20ms + return WebRtcIlbcfix_EncoderInit(encoder_inst_ptr_, 20); + } else if ((240 == (codec_params->codec_inst).pacsize) || + (480 == (codec_params->codec_inst).pacsize)) { + // processing block of 30ms + return WebRtcIlbcfix_EncoderInit(encoder_inst_ptr_, 30); + } else { + WEBRTC_TRACE(webrtc::kTraceError, webrtc::kTraceAudioCoding, unique_id_, + "InternalInitEncoder: invalid processing block"); + return -1; + } +} + +ACMGenericCodec* ACMILBC::CreateInstance(void) { return NULL; } + +int16_t ACMILBC::InternalCreateEncoder() { + if (WebRtcIlbcfix_EncoderCreate(&encoder_inst_ptr_) < 0) { + WEBRTC_TRACE(webrtc::kTraceError, + webrtc::kTraceAudioCoding, + unique_id_, + "InternalCreateEncoder: cannot create instance for ILBC " + "encoder"); + return -1; + } + return 0; +} + +void ACMILBC::DestructEncoderSafe() { + encoder_initialized_ = false; + encoder_exist_ = false; + if (encoder_inst_ptr_ != NULL) { + WebRtcIlbcfix_EncoderFree(encoder_inst_ptr_); + encoder_inst_ptr_ = NULL; + } +} + +void ACMILBC::InternalDestructEncoderInst(void* ptr_inst) { + if (ptr_inst != NULL) { + WebRtcIlbcfix_EncoderFree(static_cast(ptr_inst)); + } + return; +} + +int16_t ACMILBC::SetBitRateSafe(const int32_t rate) { + // Check that rate is valid. No need to store the value + if (rate == 13300) { + WebRtcIlbcfix_EncoderInit(encoder_inst_ptr_, 30); + } else if (rate == 15200) { + WebRtcIlbcfix_EncoderInit(encoder_inst_ptr_, 20); + } else { + return -1; + } + encoder_params_.codec_inst.rate = rate; + + return 0; +} + +#endif + +} // namespace webrtc diff --git a/webrtc/modules/audio_coding/main/acm2/acm_ilbc.h b/webrtc/modules/audio_coding/main/acm2/acm_ilbc.h new file mode 100644 index 0000000000..11e759c6a1 --- /dev/null +++ b/webrtc/modules/audio_coding/main/acm2/acm_ilbc.h @@ -0,0 +1,48 @@ +/* + * 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. + */ + +#ifndef WEBRTC_MODULES_AUDIO_CODING_MAIN_SOURCE_ACM_ILBC_H_ +#define WEBRTC_MODULES_AUDIO_CODING_MAIN_SOURCE_ACM_ILBC_H_ + +#include "webrtc/modules/audio_coding/main/source/acm_generic_codec.h" + +// forward declaration +struct iLBC_encinst_t_; +struct iLBC_decinst_t_; + +namespace webrtc { + +class ACMILBC : public ACMGenericCodec { + public: + explicit ACMILBC(int16_t codec_id); + ~ACMILBC(); + + // for FEC + ACMGenericCodec* CreateInstance(void); + + int16_t InternalEncode(uint8_t* bitstream, int16_t* bitstream_len_byte); + + int16_t InternalInitEncoder(WebRtcACMCodecParams* codec_params); + + protected: + int16_t SetBitRateSafe(const int32_t rate); + + void DestructEncoderSafe(); + + int16_t InternalCreateEncoder(); + + void InternalDestructEncoderInst(void* ptr_inst); + + iLBC_encinst_t_* encoder_inst_ptr_; +}; + +} // namespace webrtc + +#endif // WEBRTC_MODULES_AUDIO_CODING_MAIN_SOURCE_ACM_ILBC_H_ diff --git a/webrtc/modules/audio_coding/main/acm2/acm_isac.cc b/webrtc/modules/audio_coding/main/acm2/acm_isac.cc new file mode 100644 index 0000000000..1c7e8b3937 --- /dev/null +++ b/webrtc/modules/audio_coding/main/acm2/acm_isac.cc @@ -0,0 +1,829 @@ +/* + * 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/source/acm_isac.h" + +#include + +#include "webrtc/modules/audio_coding/main/interface/audio_coding_module_typedefs.h" +#include "webrtc/modules/audio_coding/main/source/acm_codec_database.h" +#include "webrtc/modules/audio_coding/main/source/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/source/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(state_), + reinterpret_cast(encoded), + static_cast(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(state_), + decoded, static_cast(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(state_), + reinterpret_cast(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(state_), + reinterpret_cast(encoded), + static_cast(encoded_len), decoded, + &temp_type); + *speech_type = ConvertSpeechType(temp_type); + return ret; + } + + virtual int ErrorCode() { + return ACM_ISAC_GETERRORCODE(static_cast(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(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(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(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(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 diff --git a/webrtc/modules/audio_coding/main/acm2/acm_isac.h b/webrtc/modules/audio_coding/main/acm2/acm_isac.h new file mode 100644 index 0000000000..f4cf1a6ac9 --- /dev/null +++ b/webrtc/modules/audio_coding/main/acm2/acm_isac.h @@ -0,0 +1,98 @@ +/* + * 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. + */ + +#ifndef WEBRTC_MODULES_AUDIO_CODING_MAIN_SOURCE_ACM_ISAC_H_ +#define WEBRTC_MODULES_AUDIO_CODING_MAIN_SOURCE_ACM_ISAC_H_ + +#include "webrtc/modules/audio_coding/main/source/acm_generic_codec.h" + +namespace webrtc { + +struct ACMISACInst; +class AcmAudioDecoderIsac; + +enum IsacCodingMode { + ADAPTIVE, + CHANNEL_INDEPENDENT +}; + +class ACMISAC : public ACMGenericCodec { + public: + explicit ACMISAC(int16_t codec_id); + ~ACMISAC(); + + // for FEC + ACMGenericCodec* CreateInstance(void); + + int16_t InternalEncode(uint8_t* bitstream, int16_t* bitstream_len_byte); + + int16_t InternalInitEncoder(WebRtcACMCodecParams* codec_params); + + int16_t InternalInitDecoder(WebRtcACMCodecParams* codec_params); + + int16_t UpdateDecoderSampFreq(int16_t codec_id); + + int16_t UpdateEncoderSampFreq(uint16_t samp_freq_hz); + + int16_t EncoderSampFreq(uint16_t* samp_freq_hz); + + int32_t ConfigISACBandwidthEstimator(const uint8_t init_frame_size_msec, + const uint16_t init_rate_bit_per_sec, + const bool enforce_frame_size); + + int32_t SetISACMaxPayloadSize(const uint16_t max_payload_len_bytes); + + int32_t SetISACMaxRate(const uint32_t max_rate_bit_per_sec); + + int16_t REDPayloadISAC(const int32_t isac_rate, + const int16_t isac_bw_estimate, + uint8_t* payload, + int16_t* payload_len_bytes); + + protected: + void DestructEncoderSafe(); + + int16_t SetBitRateSafe(const int32_t bit_rate); + + int32_t GetEstimatedBandwidthSafe(); + + int32_t SetEstimatedBandwidthSafe(int32_t estimated_bandwidth); + + int32_t GetRedPayloadSafe(uint8_t* red_payload, int16_t* payload_bytes); + + int16_t InternalCreateEncoder(); + + void InternalDestructEncoderInst(void* ptr_inst); + + int16_t Transcode(uint8_t* bitstream, + int16_t* bitstream_len_byte, + int16_t q_bwe, + int32_t rate, + bool is_red); + + void CurrentRate(int32_t* rate_bit_per_sec); + + void UpdateFrameLen(); + + virtual AudioDecoder* Decoder(int codec_id); + + ACMISACInst* codec_inst_ptr_; + bool is_enc_initialized_; + IsacCodingMode isac_coding_mode_; + bool enforce_frame_size_; + int32_t isac_current_bn_; + uint16_t samples_in_10ms_audio_; + AcmAudioDecoderIsac* audio_decoder_; + bool decoder_initialized_; +}; + +} // namespace webrtc + +#endif // WEBRTC_MODULES_AUDIO_CODING_MAIN_SOURCE_ACM_ISAC_H_ diff --git a/webrtc/modules/audio_coding/main/acm2/acm_isac_macros.h b/webrtc/modules/audio_coding/main/acm2/acm_isac_macros.h new file mode 100644 index 0000000000..646b3cc380 --- /dev/null +++ b/webrtc/modules/audio_coding/main/acm2/acm_isac_macros.h @@ -0,0 +1,76 @@ +/* + * 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. + */ + +#ifndef WEBRTC_MODULES_AUDIO_CODING_MAIN_SOURCE_ACM_ISAC_MACROS_H_ +#define WEBRTC_MODULES_AUDIO_CODING_MAIN_SOURCE_ACM_ISAC_MACROS_H_ + +#include "webrtc/engine_configurations.h" + +namespace webrtc { + +#ifdef WEBRTC_CODEC_ISAC +#define ACM_ISAC_CREATE WebRtcIsac_Create +#define ACM_ISAC_FREE WebRtcIsac_Free +#define ACM_ISAC_ENCODERINIT WebRtcIsac_EncoderInit +#define ACM_ISAC_ENCODE WebRtcIsac_Encode +#define ACM_ISAC_DECODERINIT WebRtcIsac_DecoderInit +#define ACM_ISAC_DECODE_BWE WebRtcIsac_UpdateBwEstimate +#define ACM_ISAC_DECODE_B WebRtcIsac_Decode +#define ACM_ISAC_DECODEPLC WebRtcIsac_DecodePlc +#define ACM_ISAC_CONTROL WebRtcIsac_Control +#define ACM_ISAC_CONTROL_BWE WebRtcIsac_ControlBwe +#define ACM_ISAC_GETFRAMELEN WebRtcIsac_ReadFrameLen +#define ACM_ISAC_GETERRORCODE WebRtcIsac_GetErrorCode +#define ACM_ISAC_GETSENDBITRATE WebRtcIsac_GetUplinkBw +#define ACM_ISAC_SETMAXPAYLOADSIZE WebRtcIsac_SetMaxPayloadSize +#define ACM_ISAC_SETMAXRATE WebRtcIsac_SetMaxRate +#define ACM_ISAC_GETNEWBITSTREAM WebRtcIsac_GetNewBitStream +#define ACM_ISAC_GETSENDBWE WebRtcIsac_GetDownLinkBwIndex +#define ACM_ISAC_SETBWE WebRtcIsac_UpdateUplinkBw +#define ACM_ISAC_GETBWE WebRtcIsac_ReadBwIndex +#define ACM_ISAC_GETNEWFRAMELEN WebRtcIsac_GetNewFrameLen +#define ACM_ISAC_STRUCT ISACStruct +#define ACM_ISAC_GETENCSAMPRATE WebRtcIsac_EncSampRate +#define ACM_ISAC_GETDECSAMPRATE WebRtcIsac_DecSampRate +#define ACM_ISAC_DECODERCU WebRtcIsac_DecodeRcu +#endif + +#ifdef WEBRTC_CODEC_ISACFX +#define ACM_ISAC_CREATE WebRtcIsacfix_Create +#define ACM_ISAC_FREE WebRtcIsacfix_Free +#define ACM_ISAC_ENCODERINIT WebRtcIsacfix_EncoderInit +#define ACM_ISAC_ENCODE WebRtcIsacfix_Encode +#define ACM_ISAC_DECODERINIT WebRtcIsacfix_DecoderInit +#define ACM_ISAC_DECODE_BWE WebRtcIsacfix_UpdateBwEstimate +#define ACM_ISAC_DECODE_B WebRtcIsacfix_Decode +#define ACM_ISAC_DECODEPLC WebRtcIsacfix_DecodePlc +#define ACM_ISAC_CONTROL ACMISACFixControl // Local Impl +#define ACM_ISAC_CONTROL_BWE ACMISACFixControlBWE // Local Impl +#define ACM_ISAC_GETFRAMELEN WebRtcIsacfix_ReadFrameLen +#define ACM_ISAC_GETERRORCODE WebRtcIsacfix_GetErrorCode +#define ACM_ISAC_GETSENDBITRATE ACMISACFixGetSendBitrate // Local Impl +#define ACM_ISAC_SETMAXPAYLOADSIZE WebRtcIsacfix_SetMaxPayloadSize +#define ACM_ISAC_SETMAXRATE WebRtcIsacfix_SetMaxRate +#define ACM_ISAC_GETNEWBITSTREAM ACMISACFixGetNewBitstream // Local Impl +#define ACM_ISAC_GETSENDBWE ACMISACFixGetSendBWE // Local Impl +#define ACM_ISAC_SETBWE WebRtcIsacfix_UpdateUplinkBw +#define ACM_ISAC_GETBWE WebRtcIsacfix_ReadBwIndex +#define ACM_ISAC_GETNEWFRAMELEN WebRtcIsacfix_GetNewFrameLen +#define ACM_ISAC_STRUCT ISACFIX_MainStruct +#define ACM_ISAC_GETENCSAMPRATE ACMISACFixGetEncSampRate // Local Impl +#define ACM_ISAC_GETDECSAMPRATE ACMISACFixGetDecSampRate // Local Impl +#define ACM_ISAC_DECODERCU WebRtcIsacfix_Decode // No special RCU + // decoder +#endif + +} // namespace webrtc + +#endif // WEBRTC_MODULES_AUDIO_CODING_MAIN_SOURCE_ACM_ISAC_MACROS_H_ + diff --git a/webrtc/modules/audio_coding/main/acm2/acm_neteq_unittest.cc b/webrtc/modules/audio_coding/main/acm2/acm_neteq_unittest.cc new file mode 100644 index 0000000000..3f5a200bfc --- /dev/null +++ b/webrtc/modules/audio_coding/main/acm2/acm_neteq_unittest.cc @@ -0,0 +1,13 @@ +/* + * 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. + */ + +// This file contains unit tests for ACM's NetEQ wrapper (class ACMNetEQ). + +namespace webrtc {} // namespace diff --git a/webrtc/modules/audio_coding/main/acm2/acm_opus.cc b/webrtc/modules/audio_coding/main/acm2/acm_opus.cc new file mode 100644 index 0000000000..a7380859c6 --- /dev/null +++ b/webrtc/modules/audio_coding/main/acm2/acm_opus.cc @@ -0,0 +1,187 @@ +/* + * 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/source/acm_opus.h" + +#ifdef WEBRTC_CODEC_OPUS +#include "webrtc/modules/audio_coding/codecs/opus/interface/opus_interface.h" +#include "webrtc/modules/audio_coding/main/source/acm_codec_database.h" +#include "webrtc/modules/audio_coding/main/source/acm_common_defs.h" +#include "webrtc/system_wrappers/interface/trace.h" +#endif + +namespace webrtc { + +#ifndef WEBRTC_CODEC_OPUS + +ACMOpus::ACMOpus(int16_t /* codec_id */) + : encoder_inst_ptr_(NULL), + sample_freq_(0), + bitrate_(0), + channels_(1) { + return; +} + +ACMOpus::~ACMOpus() { + return; +} + +int16_t ACMOpus::InternalEncode(uint8_t* /* bitstream */, + int16_t* /* bitstream_len_byte */) { + return -1; +} + +int16_t ACMOpus::InternalInitEncoder(WebRtcACMCodecParams* /* codec_params */) { + return -1; +} + +ACMGenericCodec* ACMOpus::CreateInstance(void) { + return NULL; +} + +int16_t ACMOpus::InternalCreateEncoder() { + return -1; +} + +void ACMOpus::DestructEncoderSafe() { + return; +} + +void ACMOpus::InternalDestructEncoderInst(void* /* ptr_inst */) { + return; +} + +int16_t ACMOpus::SetBitRateSafe(const int32_t /*rate*/) { + return -1; +} + +#else //===================== Actual Implementation ======================= + +ACMOpus::ACMOpus(int16_t codec_id) + : encoder_inst_ptr_(NULL), + sample_freq_(32000), // Default sampling frequency. + bitrate_(20000), // Default bit-rate. + channels_(1) { // Default mono + codec_id_ = codec_id; + // Opus has internal DTX, but we dont use it for now. + has_internal_dtx_ = false; + + if (codec_id_ != ACMCodecDB::kOpus) { + WEBRTC_TRACE(webrtc::kTraceError, webrtc::kTraceAudioCoding, unique_id_, + "Wrong codec id for Opus."); + sample_freq_ = -1; + bitrate_ = -1; + } + return; +} + +ACMOpus::~ACMOpus() { + if (encoder_inst_ptr_ != NULL) { + WebRtcOpus_EncoderFree(encoder_inst_ptr_); + encoder_inst_ptr_ = NULL; + } +} + +int16_t ACMOpus::InternalEncode(uint8_t* bitstream, + int16_t* bitstream_len_byte) { + // Call Encoder. + *bitstream_len_byte = WebRtcOpus_Encode(encoder_inst_ptr_, + &in_audio_[in_audio_ix_read_], + frame_len_smpl_, + MAX_PAYLOAD_SIZE_BYTE, bitstream); + // Check for error reported from encoder. + if (*bitstream_len_byte < 0) { + WEBRTC_TRACE(webrtc::kTraceError, webrtc::kTraceAudioCoding, unique_id_, + "InternalEncode: Encode error for Opus"); + *bitstream_len_byte = 0; + return -1; + } + + // Increment the read index. This tells the caller how far + // we have gone forward in reading the audio buffer. + in_audio_ix_read_ += frame_len_smpl_ * channels_; + + return *bitstream_len_byte; +} + +int16_t ACMOpus::InternalInitEncoder(WebRtcACMCodecParams* codec_params) { + int16_t ret; + if (encoder_inst_ptr_ != NULL) { + WebRtcOpus_EncoderFree(encoder_inst_ptr_); + encoder_inst_ptr_ = NULL; + } + ret = WebRtcOpus_EncoderCreate(&encoder_inst_ptr_, + codec_params->codec_inst.channels); + // Store number of channels. + channels_ = codec_params->codec_inst.channels; + + if (ret < 0) { + WEBRTC_TRACE(webrtc::kTraceError, webrtc::kTraceAudioCoding, unique_id_, + "Encoder creation failed for Opus"); + return ret; + } + ret = WebRtcOpus_SetBitRate(encoder_inst_ptr_, + codec_params->codec_inst.rate); + if (ret < 0) { + WEBRTC_TRACE(webrtc::kTraceError, webrtc::kTraceAudioCoding, unique_id_, + "Setting initial bitrate failed for Opus"); + return ret; + } + + // Store bitrate. + bitrate_ = codec_params->codec_inst.rate; + + return 0; +} + +ACMGenericCodec* ACMOpus::CreateInstance(void) { + return NULL; +} + +int16_t ACMOpus::InternalCreateEncoder() { + // Real encoder will be created in InternalInitEncoder. + return 0; +} + +void ACMOpus::DestructEncoderSafe() { + if (encoder_inst_ptr_) { + WebRtcOpus_EncoderFree(encoder_inst_ptr_); + encoder_inst_ptr_ = NULL; + } +} + +void ACMOpus::InternalDestructEncoderInst(void* ptr_inst) { + if (ptr_inst != NULL) { + WebRtcOpus_EncoderFree(static_cast(ptr_inst)); + } + return; +} + +int16_t ACMOpus::SetBitRateSafe(const int32_t rate) { + if (rate < 6000 || rate > 510000) { + WEBRTC_TRACE(webrtc::kTraceError, webrtc::kTraceAudioCoding, unique_id_, + "SetBitRateSafe: Invalid rate Opus"); + return -1; + } + + bitrate_ = rate; + + // Ask the encoder for the new rate. + if (WebRtcOpus_SetBitRate(encoder_inst_ptr_, bitrate_) >= 0) { + encoder_params_.codec_inst.rate = bitrate_; + return 0; + } + + return -1; +} + +#endif // WEBRTC_CODEC_OPUS + +} // namespace webrtc diff --git a/webrtc/modules/audio_coding/main/acm2/acm_opus.h b/webrtc/modules/audio_coding/main/acm2/acm_opus.h new file mode 100644 index 0000000000..28b08b6fd3 --- /dev/null +++ b/webrtc/modules/audio_coding/main/acm2/acm_opus.h @@ -0,0 +1,50 @@ +/* + * 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. + */ + +#ifndef WEBRTC_MODULES_AUDIO_CODING_MAIN_SOURCE_ACM_OPUS_H_ +#define WEBRTC_MODULES_AUDIO_CODING_MAIN_SOURCE_ACM_OPUS_H_ + +#include "webrtc/common_audio/resampler/include/resampler.h" +#include "webrtc/modules/audio_coding/main/source/acm_generic_codec.h" + +struct WebRtcOpusEncInst; +struct WebRtcOpusDecInst; + +namespace webrtc { + +class ACMOpus : public ACMGenericCodec { + public: + explicit ACMOpus(int16_t codec_id); + ~ACMOpus(); + + ACMGenericCodec* CreateInstance(void); + + int16_t InternalEncode(uint8_t* bitstream, int16_t* bitstream_len_byte); + + int16_t InternalInitEncoder(WebRtcACMCodecParams *codec_params); + + protected: + void DestructEncoderSafe(); + + int16_t InternalCreateEncoder(); + + void InternalDestructEncoderInst(void* ptr_inst); + + int16_t SetBitRateSafe(const int32_t rate); + + WebRtcOpusEncInst* encoder_inst_ptr_; + uint16_t sample_freq_; + uint16_t bitrate_; + int channels_; +}; + +} // namespace webrtc + +#endif // WEBRTC_MODULES_AUDIO_CODING_MAIN_SOURCE_ACM_OPUS_H_ diff --git a/webrtc/modules/audio_coding/main/acm2/acm_pcm16b.cc b/webrtc/modules/audio_coding/main/acm2/acm_pcm16b.cc new file mode 100644 index 0000000000..3bc964241f --- /dev/null +++ b/webrtc/modules/audio_coding/main/acm2/acm_pcm16b.cc @@ -0,0 +1,92 @@ +/* + * 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/source/acm_pcm16b.h" + +#ifdef WEBRTC_CODEC_PCM16 +#include "webrtc/modules/audio_coding/codecs/pcm16b/include/pcm16b.h" +#include "webrtc/modules/audio_coding/main/source/acm_codec_database.h" +#include "webrtc/modules/audio_coding/main/source/acm_common_defs.h" +#include "webrtc/system_wrappers/interface/trace.h" +#endif + +namespace webrtc { + +#ifndef WEBRTC_CODEC_PCM16 + +ACMPCM16B::ACMPCM16B(int16_t /* codec_id */) { return; } + +ACMPCM16B::~ACMPCM16B() { return; } + +int16_t ACMPCM16B::InternalEncode(uint8_t* /* bitstream */, + int16_t* /* bitstream_len_byte */) { + return -1; +} + +int16_t ACMPCM16B::InternalInitEncoder( + WebRtcACMCodecParams* /* codec_params */) { + return -1; +} + +ACMGenericCodec* ACMPCM16B::CreateInstance(void) { return NULL; } + +int16_t ACMPCM16B::InternalCreateEncoder() { return -1; } + +void ACMPCM16B::InternalDestructEncoderInst(void* /* ptr_inst */) { return; } + +void ACMPCM16B::DestructEncoderSafe() { return; } + +#else //===================== Actual Implementation ======================= +ACMPCM16B::ACMPCM16B(int16_t codec_id) { + codec_id_ = codec_id; + sampling_freq_hz_ = ACMCodecDB::CodecFreq(codec_id_); +} + +ACMPCM16B::~ACMPCM16B() { return; } + +int16_t ACMPCM16B::InternalEncode(uint8_t* bitstream, + int16_t* bitstream_len_byte) { + *bitstream_len_byte = WebRtcPcm16b_Encode(&in_audio_[in_audio_ix_read_], + frame_len_smpl_ * num_channels_, + bitstream); + // Increment the read index to tell the caller that how far + // we have gone forward in reading the audio buffer. + in_audio_ix_read_ += frame_len_smpl_ * num_channels_; + return *bitstream_len_byte; +} + +int16_t ACMPCM16B::InternalInitEncoder( + WebRtcACMCodecParams* /* codec_params */) { + // This codec does not need initialization, PCM has no instance. + return 0; +} + +ACMGenericCodec* ACMPCM16B::CreateInstance(void) { return NULL; } + +int16_t ACMPCM16B::InternalCreateEncoder() { + // PCM has no instance. + return 0; +} + +void ACMPCM16B::InternalDestructEncoderInst(void* /* ptr_inst */) { + // PCM has no instance. + return; +} + +void ACMPCM16B::DestructEncoderSafe() { + // PCM has no instance. + encoder_exist_ = false; + encoder_initialized_ = false; + return; +} + +#endif + +} // namespace webrtc diff --git a/webrtc/modules/audio_coding/main/acm2/acm_pcm16b.h b/webrtc/modules/audio_coding/main/acm2/acm_pcm16b.h new file mode 100644 index 0000000000..a7fff0f52b --- /dev/null +++ b/webrtc/modules/audio_coding/main/acm2/acm_pcm16b.h @@ -0,0 +1,42 @@ +/* + * 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. + */ + +#ifndef WEBRTC_MODULES_AUDIO_CODING_MAIN_SOURCE_ACM_PCM16B_H_ +#define WEBRTC_MODULES_AUDIO_CODING_MAIN_SOURCE_ACM_PCM16B_H_ + +#include "webrtc/modules/audio_coding/main/source/acm_generic_codec.h" + +namespace webrtc { + +class ACMPCM16B : public ACMGenericCodec { + public: + explicit ACMPCM16B(int16_t codec_id); + ~ACMPCM16B(); + + // For FEC. + ACMGenericCodec* CreateInstance(void); + + int16_t InternalEncode(uint8_t* bitstream, int16_t* bitstream_len_byte); + + int16_t InternalInitEncoder(WebRtcACMCodecParams* codec_params); + + protected: + void DestructEncoderSafe(); + + int16_t InternalCreateEncoder(); + + void InternalDestructEncoderInst(void* ptr_inst); + + int32_t sampling_freq_hz_; +}; + +} // namespace webrtc + +#endif // WEBRTC_MODULES_AUDIO_CODING_MAIN_SOURCE_ACM_PCM16B_H_ diff --git a/webrtc/modules/audio_coding/main/acm2/acm_pcma.cc b/webrtc/modules/audio_coding/main/acm2/acm_pcma.cc new file mode 100644 index 0000000000..0d574fe24b --- /dev/null +++ b/webrtc/modules/audio_coding/main/acm2/acm_pcma.cc @@ -0,0 +1,58 @@ +/* + * 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/source/acm_pcma.h" + +#include "webrtc/modules/audio_coding/codecs/g711/include/g711_interface.h" +#include "webrtc/modules/audio_coding/main/source/acm_common_defs.h" +#include "webrtc/system_wrappers/interface/trace.h" + +// Codec interface + +namespace webrtc { + +ACMPCMA::ACMPCMA(int16_t codec_id) { codec_id_ = codec_id; } + +ACMPCMA::~ACMPCMA() { return; } + +int16_t ACMPCMA::InternalEncode(uint8_t* bitstream, + int16_t* bitstream_len_byte) { + *bitstream_len_byte = WebRtcG711_EncodeA( + NULL, &in_audio_[in_audio_ix_read_], frame_len_smpl_ * num_channels_, + reinterpret_cast(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_ += frame_len_smpl_ * num_channels_; + return *bitstream_len_byte; +} + +int16_t ACMPCMA::InternalInitEncoder(WebRtcACMCodecParams* /* codec_params */) { + // This codec does not need initialization, PCM has no instance. + return 0; +} + +ACMGenericCodec* ACMPCMA::CreateInstance(void) { return NULL; } + +int16_t ACMPCMA::InternalCreateEncoder() { + // PCM has no instance. + return 0; +} + +void ACMPCMA::InternalDestructEncoderInst(void* /* ptr_inst */) { + // PCM has no instance. + return; +} + +void ACMPCMA::DestructEncoderSafe() { + // PCM has no instance. + return; +} + +} // namespace webrtc diff --git a/webrtc/modules/audio_coding/main/acm2/acm_pcma.h b/webrtc/modules/audio_coding/main/acm2/acm_pcma.h new file mode 100644 index 0000000000..61386d3127 --- /dev/null +++ b/webrtc/modules/audio_coding/main/acm2/acm_pcma.h @@ -0,0 +1,40 @@ +/* + * 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. + */ + +#ifndef WEBRTC_MODULES_AUDIO_CODING_MAIN_SOURCE_ACM_PCMA_H_ +#define WEBRTC_MODULES_AUDIO_CODING_MAIN_SOURCE_ACM_PCMA_H_ + +#include "webrtc/modules/audio_coding/main/source/acm_generic_codec.h" + +namespace webrtc { + +class ACMPCMA : public ACMGenericCodec { + public: + explicit ACMPCMA(int16_t codec_id); + ~ACMPCMA(); + + // For FEC. + ACMGenericCodec* CreateInstance(void); + + int16_t InternalEncode(uint8_t* bitstream, int16_t* bitstream_len_byte); + + int16_t InternalInitEncoder(WebRtcACMCodecParams* codec_params); + + protected: + void DestructEncoderSafe(); + + int16_t InternalCreateEncoder(); + + void InternalDestructEncoderInst(void* ptr_inst); +}; + +} // namespace webrtc + +#endif // WEBRTC_MODULES_AUDIO_CODING_MAIN_SOURCE_ACM_PCMA_H_ diff --git a/webrtc/modules/audio_coding/main/acm2/acm_pcmu.cc b/webrtc/modules/audio_coding/main/acm2/acm_pcmu.cc new file mode 100644 index 0000000000..441e3ddcde --- /dev/null +++ b/webrtc/modules/audio_coding/main/acm2/acm_pcmu.cc @@ -0,0 +1,59 @@ +/* + * 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/source/acm_pcmu.h" + +#include "webrtc/modules/audio_coding/codecs/g711/include/g711_interface.h" +#include "webrtc/modules/audio_coding/main/source/acm_common_defs.h" +#include "webrtc/system_wrappers/interface/trace.h" + +// Codec interface. + +namespace webrtc { + +ACMPCMU::ACMPCMU(int16_t codec_id) { codec_id_ = codec_id; } + +ACMPCMU::~ACMPCMU() {} + +int16_t ACMPCMU::InternalEncode(uint8_t* bitstream, + int16_t* bitstream_len_byte) { + *bitstream_len_byte = WebRtcG711_EncodeU( + NULL, &in_audio_[in_audio_ix_read_], frame_len_smpl_ * num_channels_, + reinterpret_cast(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_ += frame_len_smpl_ * num_channels_; + return *bitstream_len_byte; +} + +int16_t ACMPCMU::InternalInitEncoder(WebRtcACMCodecParams* /* codec_params */) { + // This codec does not need initialization, PCM has no instance. + return 0; +} + +ACMGenericCodec* ACMPCMU::CreateInstance(void) { return NULL; } + +int16_t ACMPCMU::InternalCreateEncoder() { + // PCM has no instance. + return 0; +} + +void ACMPCMU::InternalDestructEncoderInst(void* /* ptr_inst */) { + // PCM has no instance. +} + +void ACMPCMU::DestructEncoderSafe() { + // PCM has no instance. + encoder_exist_ = false; + encoder_initialized_ = false; +} + +} // namespace webrtc diff --git a/webrtc/modules/audio_coding/main/acm2/acm_pcmu.h b/webrtc/modules/audio_coding/main/acm2/acm_pcmu.h new file mode 100644 index 0000000000..832a00d1d7 --- /dev/null +++ b/webrtc/modules/audio_coding/main/acm2/acm_pcmu.h @@ -0,0 +1,40 @@ +/* + * 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. + */ + +#ifndef WEBRTC_MODULES_AUDIO_CODING_MAIN_SOURCE_ACM_PCMU_H_ +#define WEBRTC_MODULES_AUDIO_CODING_MAIN_SOURCE_ACM_PCMU_H_ + +#include "webrtc/modules/audio_coding/main/source/acm_generic_codec.h" + +namespace webrtc { + +class ACMPCMU : public ACMGenericCodec { + public: + explicit ACMPCMU(int16_t codec_id); + ~ACMPCMU(); + + // For FEC. + ACMGenericCodec* CreateInstance(void); + + int16_t InternalEncode(uint8_t* bitstream, int16_t* bitstream_len_byte); + + int16_t InternalInitEncoder(WebRtcACMCodecParams* codec_params); + + protected: + void DestructEncoderSafe(); + + int16_t InternalCreateEncoder(); + + void InternalDestructEncoderInst(void* ptr_inst); +}; + +} // namespace webrtc + +#endif // WEBRTC_MODULES_AUDIO_CODING_MAIN_SOURCE_ACM_PCMU_H_ diff --git a/webrtc/modules/audio_coding/main/acm2/acm_receiver.cc b/webrtc/modules/audio_coding/main/acm2/acm_receiver.cc new file mode 100644 index 0000000000..fb3fe3e60a --- /dev/null +++ b/webrtc/modules/audio_coding/main/acm2/acm_receiver.cc @@ -0,0 +1,827 @@ +/* + * Copyright (c) 2013 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/source/acm_receiver.h" + +#include // malloc + +#include // sort +#include + +#include "webrtc/common_audio/signal_processing/include/signal_processing_library.h" +#include "webrtc/common_types.h" +#include "webrtc/modules/audio_coding/main/source/acm_common_defs.h" +#include "webrtc/modules/audio_coding/main/source/acm_resampler.h" +#include "webrtc/modules/audio_coding/main/source/nack.h" +#include "webrtc/modules/audio_coding/neteq4/interface/audio_decoder.h" +#include "webrtc/modules/audio_coding/neteq4/interface/neteq.h" +#include "webrtc/system_wrappers/interface/critical_section_wrapper.h" +#include "webrtc/system_wrappers/interface/logging.h" +#include "webrtc/system_wrappers/interface/rw_lock_wrapper.h" +#include "webrtc/system_wrappers/interface/tick_util.h" +#include "webrtc/system_wrappers/interface/trace.h" + +namespace webrtc { + +namespace { + +const int kRtpHeaderSize = 12; +const int kNeteqInitSampleRateHz = 16000; +const int kNackThresholdPackets = 2; + +// |vad_activity_| field of |audio_frame| is set to |previous_audio_activity_| +// before the call to this function. +void SetAudioFrameActivityAndType(bool vad_enabled, + NetEqOutputType type, + AudioFrame* audio_frame) { + if (vad_enabled) { + switch (type) { + case kOutputNormal: { + audio_frame->vad_activity_ = AudioFrame::kVadActive; + audio_frame->speech_type_ = AudioFrame::kNormalSpeech; + break; + } + case kOutputVADPassive: { + audio_frame->vad_activity_ = AudioFrame::kVadPassive; + audio_frame->speech_type_ = AudioFrame::kNormalSpeech; + break; + } + case kOutputCNG: { + audio_frame->vad_activity_ = AudioFrame::kVadPassive; + audio_frame->speech_type_ = AudioFrame::kCNG; + break; + } + case kOutputPLC: { + // Don't change |audio_frame->vad_activity_|, it should be the same as + // |previous_audio_activity_|. + audio_frame->speech_type_ = AudioFrame::kPLC; + break; + } + case kOutputPLCtoCNG: { + audio_frame->vad_activity_ = AudioFrame::kVadPassive; + audio_frame->speech_type_ = AudioFrame::kPLCCNG; + break; + } + default: + assert(false); + } + } else { + // Always return kVadUnknown when receive VAD is inactive + audio_frame->vad_activity_ = AudioFrame::kVadUnknown; + switch (type) { + case kOutputNormal: { + audio_frame->speech_type_ = AudioFrame::kNormalSpeech; + break; + } + case kOutputCNG: { + audio_frame->speech_type_ = AudioFrame::kCNG; + break; + } + case kOutputPLC: { + audio_frame->speech_type_ = AudioFrame::kPLC; + break; + } + case kOutputPLCtoCNG: { + audio_frame->speech_type_ = AudioFrame::kPLCCNG; + break; + } + case kOutputVADPassive: { + // Normally, we should no get any VAD decision if post-decoding VAD is + // not active. However, if post-decoding VAD has been active then + // disabled, we might be here for couple of frames. + audio_frame->speech_type_ = AudioFrame::kNormalSpeech; + LOG_F(LS_WARNING) << "Post-decoding VAD is disabled but output is " + << "labeled VAD-passive"; + break; + } + default: + assert(false); + } + } +} + +// Is the given codec a CNG codec? +bool IsCng(int codec_id) { + return (codec_id == ACMCodecDB::kCNNB || codec_id == ACMCodecDB::kCNWB || + codec_id == ACMCodecDB::kCNSWB || codec_id == ACMCodecDB::kCNFB); +} + +} // namespace + +AcmReceiver::AcmReceiver() + : id_(0), + neteq_(NetEq::Create(kNeteqInitSampleRateHz)), + last_audio_decoder_(-1), // Invalid value. + decode_lock_(RWLockWrapper::CreateRWLock()), + neteq_crit_sect_(CriticalSectionWrapper::CreateCriticalSection()), + vad_enabled_(false), + previous_audio_activity_(AudioFrame::kVadUnknown), + current_sample_rate_hz_(kNeteqInitSampleRateHz), + nack_(), + nack_enabled_(false), + av_sync_(false), + initial_delay_manager_(), + missing_packets_sync_stream_(), + late_packets_sync_stream_() { + for (int n = 0; n < ACMCodecDB::kMaxNumCodecs; ++n) { + decoders_[n].registered = false; + } + + // Make sure we are on the same page as NetEq, although the default behavior + // for NetEq has been VAD disabled. + if (vad_enabled_) + neteq_->EnableVad(); + else + neteq_->DisableVad(); +} + +AcmReceiver::~AcmReceiver() { + delete neteq_; + delete decode_lock_; + delete neteq_crit_sect_; +} + +int AcmReceiver::SetMinimumDelay(int delay_ms) { + if (neteq_->SetMinimumDelay(delay_ms)) + return 0; + LOG_FERR1(LS_ERROR, "AcmReceiver::SetExtraDelay", delay_ms); + return -1; +} + +int AcmReceiver::SetInitialDelay(int delay_ms) { + if (delay_ms < 0 || delay_ms > 10000) { + return -1; + } + CriticalSectionScoped lock(neteq_crit_sect_); + + if (delay_ms == 0) { + av_sync_ = false; + initial_delay_manager_.reset(); + missing_packets_sync_stream_.reset(); + late_packets_sync_stream_.reset(); + neteq_->SetMinimumDelay(0); + return 0; + } + + if (av_sync_ && initial_delay_manager_->PacketBuffered()) { + // Too late for this API. Only works before a call is started. + return -1; + } + + // Most of places NetEq calls are not within AcmReceiver's critical section to + // improve performance. Here, this call has to be placed before the following + // block, therefore, we keep it inside critical section. Otherwise, we have to + // release |neteq_crit_sect_| and acquire it again, which seems an overkill. + if (neteq_->SetMinimumDelay(delay_ms) < 0) + return -1; + + const int kLatePacketThreshold = 5; + av_sync_ = true; + initial_delay_manager_.reset(new InitialDelayManager(delay_ms, + kLatePacketThreshold)); + missing_packets_sync_stream_.reset(new InitialDelayManager::SyncStream); + late_packets_sync_stream_.reset(new InitialDelayManager::SyncStream); + return 0; +} + +int AcmReceiver::SetMaximumDelay(int delay_ms) { + if (neteq_->SetMaximumDelay(delay_ms)) + return 0; + LOG_FERR1(LS_ERROR, "AcmReceiver::SetExtraDelay", delay_ms); + return -1; +} + +int AcmReceiver::LeastRequiredDelayMs() const { + return neteq_->LeastRequiredDelayMs(); +} + +int AcmReceiver::current_sample_rate_hz() const { + CriticalSectionScoped lock(neteq_crit_sect_); + return current_sample_rate_hz_; +} + +// TODO(turajs): use one set of enumerators, e.g. the one defined in +// common_types.h +void AcmReceiver::SetPlayoutMode(AudioPlayoutMode mode) { + enum NetEqPlayoutMode playout_mode = kPlayoutOn; + enum NetEqBackgroundNoiseMode bgn_mode = kBgnOn; + switch (mode) { + case voice: + playout_mode = kPlayoutOn; + bgn_mode = kBgnOn; + break; + case fax: // No change to background noise mode. + playout_mode = kPlayoutFax; + bgn_mode = neteq_->BackgroundNoiseMode(); + break; + case streaming: + playout_mode = kPlayoutStreaming; + bgn_mode = kBgnOff; + break; + case off: + playout_mode = kPlayoutOff; + bgn_mode = kBgnOff; + break; + } + neteq_->SetPlayoutMode(playout_mode); + neteq_->SetBackgroundNoiseMode(bgn_mode); +} + +AudioPlayoutMode AcmReceiver::PlayoutMode() const { + AudioPlayoutMode acm_mode = voice; + NetEqPlayoutMode mode = neteq_->PlayoutMode(); + switch (mode) { + case kPlayoutOn: + acm_mode = voice; + break; + case kPlayoutOff: + acm_mode = off; + break; + case kPlayoutFax: + acm_mode = fax; + break; + case kPlayoutStreaming: + acm_mode = streaming; + break; + default: + assert(false); + } + return acm_mode; +} + +int AcmReceiver::InsertPacket(const WebRtcRTPHeader& rtp_header, + const uint8_t* incoming_payload, + int length_payload) { + uint32_t receive_timestamp = 0; + InitialDelayManager::PacketType packet_type = + InitialDelayManager::kUndefinedPacket; + bool new_codec = false; + const RTPHeader* header = &rtp_header.header; // Just a shorthand. + + { + CriticalSectionScoped lock(neteq_crit_sect_); + + int codec_id = RtpHeaderToCodecIndex(*header, incoming_payload); + if (codec_id < 0) { + LOG_F(LS_ERROR) << "Payload-type " << header->payloadType + << " is not registered."; + return -1; + } + assert(codec_id < ACMCodecDB::kMaxNumCodecs); + const int sample_rate_hz = ACMCodecDB::CodecFreq(codec_id); + receive_timestamp = NowInTimestamp(sample_rate_hz); + + if (IsCng(codec_id)) { + // If this is a CNG while the audio codec is not mono skip pushing in + // packets into NetEq. + if (last_audio_decoder_ >= 0 && + decoders_[last_audio_decoder_].channels > 1) + return 0; + packet_type = InitialDelayManager::kCngPacket; + } else if (codec_id == ACMCodecDB::kAVT) { + packet_type = InitialDelayManager::kAvtPacket; + } else { + if (codec_id != last_audio_decoder_) { + // This is either the first audio packet or send codec is changed. + // Therefore, either NetEq buffer is empty or will be flushed when this + // packet inserted. Note that |last_audio_decoder_| is initialized to + // an invalid value (-1), hence, the above condition is true for the + // very first audio packet. + new_codec = true; + + // Updating NACK'sampling rate is required, either first packet is + // received or codec is changed. Furthermore, reset is required if codec + // is changed (NetEq flushes its buffer so NACK should reset its list). + if (nack_enabled_) { + assert(nack_.get()); + nack_->Reset(); + nack_->UpdateSampleRate(sample_rate_hz); + } + last_audio_decoder_ = codec_id; + } + packet_type = InitialDelayManager::kAudioPacket; + } + + if (nack_enabled_) { + assert(nack_.get()); + nack_->UpdateLastReceivedPacket(header->sequenceNumber, + header->timestamp); + } + + if (av_sync_) { + assert(initial_delay_manager_.get()); + assert(missing_packets_sync_stream_.get()); + // This updates |initial_delay_manager_| and specifies an stream of + // sync-packets, if required to be inserted. We insert the sync-packets + // when AcmReceiver lock is released and |decoder_lock_| is acquired. + initial_delay_manager_->UpdateLastReceivedPacket( + rtp_header, receive_timestamp, packet_type, new_codec, sample_rate_hz, + missing_packets_sync_stream_.get()); + } + } + + { + WriteLockScoped lock_codecs(*decode_lock_); // Lock to prevent an encoding. + + // If |missing_packets_sync_stream_| is allocated then we are in AV-sync and + // we may need to insert sync-packets. We don't check |av_sync_| as we are + // outside AcmReceiver's critical section. + if (missing_packets_sync_stream_.get()) { + InsertStreamOfSyncPackets(missing_packets_sync_stream_.get()); + } + + if (neteq_->InsertPacket(rtp_header, incoming_payload, length_payload, + receive_timestamp) < 0) { + LOG_FERR1(LS_ERROR, "AcmReceiver::InsertPacket", header->payloadType) << + " Failed to insert packet"; + return -1; + } + } + return 0; +} + +int AcmReceiver::GetAudio(int desired_freq_hz, AudioFrame* audio_frame) { + enum NetEqOutputType type; + int16_t* ptr_audio_buffer = audio_frame->data_; + int samples_per_channel; + int num_channels; + bool return_silence = false; + + { + // Accessing members, take the lock. + CriticalSectionScoped lock(neteq_crit_sect_); + + if (av_sync_) { + assert(initial_delay_manager_.get()); + assert(late_packets_sync_stream_.get()); + return_silence = GetSilence(desired_freq_hz, audio_frame); + uint32_t timestamp_now = NowInTimestamp(current_sample_rate_hz_); + initial_delay_manager_->LatePackets(timestamp_now, + late_packets_sync_stream_.get()); + } + + if (!return_silence) { + // This is our initial guess regarding whether a resampling will be + // required. It is based on previous sample rate of netEq. Most often, + // this is a correct guess, however, in case that incoming payload changes + // the resampling might might be needed. By doing so, we avoid an + // unnecessary memcpy(). + if (desired_freq_hz != -1 && + current_sample_rate_hz_ != desired_freq_hz) { + ptr_audio_buffer = audio_buffer_; + } + } + } + + { + WriteLockScoped lock_codecs(*decode_lock_); // Lock to prevent an encoding. + + // If |late_packets_sync_stream_| is allocated then we have been in AV-sync + // mode and we might have to insert sync-packets. + if (late_packets_sync_stream_.get()) { + InsertStreamOfSyncPackets(late_packets_sync_stream_.get()); + if (return_silence) // Silence generated, don't pull from NetEq. + return 0; + } + + if (neteq_->GetAudio(AudioFrame::kMaxDataSizeSamples, + ptr_audio_buffer, + &samples_per_channel, + &num_channels, &type) != NetEq::kOK) { + LOG_FERR0(LS_ERROR, "AcmReceiver::GetAudio") << "NetEq Failed."; + return -1; + } + } + + // Accessing members, take the lock. + CriticalSectionScoped lock(neteq_crit_sect_); + + // Update NACK. + int decoded_sequence_num = 0; + uint32_t decoded_timestamp = 0; + bool update_nack = nack_enabled_ && // Update NACK only if it is enabled. + neteq_->DecodedRtpInfo(&decoded_sequence_num, &decoded_timestamp); + if (update_nack) { + assert(nack_.get()); + nack_->UpdateLastDecodedPacket(decoded_sequence_num, decoded_timestamp); + } + + // NetEq always returns 10 ms of audio. + current_sample_rate_hz_ = samples_per_channel * 100; + + // Update if resampling is required. + bool need_resampling = (desired_freq_hz != -1) && + (current_sample_rate_hz_ != desired_freq_hz); + + if (ptr_audio_buffer == audio_buffer_) { + // Data is written to local buffer. + if (need_resampling) { + samples_per_channel = resampler_.Resample10Msec( + audio_buffer_, current_sample_rate_hz_, desired_freq_hz, + num_channels, audio_frame->data_); + if (samples_per_channel < 0) { + LOG_FERR0(LS_ERROR, "AcmReceiver::GetAudio") << "Resampler Failed."; + return -1; + } + } else { + // We might end up here ONLY if codec is changed. + memcpy(audio_frame->data_, audio_buffer_, samples_per_channel * + num_channels * sizeof(int16_t)); + } + } else { + // Data is written into |audio_frame|. + if (need_resampling) { + // We might end up here ONLY if codec is changed. + samples_per_channel = resampler_.Resample10Msec( + audio_frame->data_, current_sample_rate_hz_, desired_freq_hz, + num_channels, audio_buffer_); + if (samples_per_channel < 0) { + LOG_FERR0(LS_ERROR, "AcmReceiver::GetAudio") << "Resampler Failed."; + return -1; + } + memcpy(audio_frame->data_, audio_buffer_, samples_per_channel * + num_channels * sizeof(int16_t)); + } + } + + audio_frame->num_channels_ = num_channels; + audio_frame->samples_per_channel_ = samples_per_channel; + audio_frame->sample_rate_hz_ = samples_per_channel * 100; + + // Should set |vad_activity| before calling SetAudioFrameActivityAndType(). + audio_frame->vad_activity_ = previous_audio_activity_; + SetAudioFrameActivityAndType(vad_enabled_, type, audio_frame); + previous_audio_activity_ = audio_frame->vad_activity_; + return 0; +} + +int32_t AcmReceiver::AddCodec(int acm_codec_id, + uint8_t payload_type, + int channels, + AudioDecoder* audio_decoder) { + assert(acm_codec_id >= 0 && acm_codec_id < ACMCodecDB::kMaxNumCodecs); + NetEqDecoder neteq_decoder = ACMCodecDB::neteq_decoders_[acm_codec_id]; + + CriticalSectionScoped lock(neteq_crit_sect_); + + // The corresponding NetEq decoder ID. + // If this coder has been registered before. + if (decoders_[acm_codec_id].registered) { + if (decoders_[acm_codec_id].payload_type == payload_type) { + // Re-registering the same codec with the same payload-type. Do nothing + // and return. + return 0; + } + + // Changing the payload-type of this codec. First unregister. Then register + // with new payload-type. + if (neteq_->RemovePayloadType(decoders_[acm_codec_id].payload_type) != + NetEq::kOK) { + LOG_F(LS_ERROR) << "Cannot remover payload " + << decoders_[acm_codec_id].payload_type; + return -1; + } + } + + int ret_val; + if (!audio_decoder) { + ret_val = neteq_->RegisterPayloadType(neteq_decoder, payload_type); + } else { + ret_val = neteq_->RegisterExternalDecoder( + audio_decoder, neteq_decoder, + ACMCodecDB::database_[acm_codec_id].plfreq, payload_type); + } + if (ret_val != NetEq::kOK) { + LOG_FERR3(LS_ERROR, "AcmReceiver::AddCodec", acm_codec_id, payload_type, + channels); + // Registration failed, delete the allocated space and set the pointer to + // NULL, for the record. + decoders_[acm_codec_id].registered = false; + return -1; + } + + decoders_[acm_codec_id].registered = true; + decoders_[acm_codec_id].payload_type = payload_type; + decoders_[acm_codec_id].channels = channels; + return 0; +} + +void AcmReceiver::EnableVad() { + neteq_->EnableVad(); + CriticalSectionScoped lock(neteq_crit_sect_); + vad_enabled_ = true; +} + +void AcmReceiver::DisableVad() { + neteq_->DisableVad(); + CriticalSectionScoped lock(neteq_crit_sect_); + vad_enabled_ = false; +} + +void AcmReceiver::FlushBuffers() { + neteq_->FlushBuffers(); +} + +// If failed in removing one of the codecs, this method continues to remove as +// many as it can. +int AcmReceiver::RemoveAllCodecs() { + int ret_val = 0; + CriticalSectionScoped lock(neteq_crit_sect_); + for (int n = 0; n < ACMCodecDB::kMaxNumCodecs; ++n) { + if (decoders_[n].registered) { + if (neteq_->RemovePayloadType(decoders_[n].payload_type) == 0) { + decoders_[n].registered = false; + } else { + LOG_F(LS_ERROR) << "Cannot remove payload " + << decoders_[n].payload_type; + ret_val = -1; + } + } + } + return ret_val; +} + +int AcmReceiver::RemoveCodec(uint8_t payload_type) { + int codec_index = PayloadType2CodecIndex(payload_type); + if (codec_index < 0) { // Such a payload-type is not registered. + LOG(LS_ERROR) << "payload_type " << payload_type << " is not registered" + " to be removed."; + return -1; + } + if (neteq_->RemovePayloadType(payload_type) != NetEq::kOK) { + LOG_FERR1(LS_ERROR, "AcmReceiver::RemoveCodec", payload_type); + return -1; + } + CriticalSectionScoped lock(neteq_crit_sect_); + decoders_[codec_index].registered = false; + return 0; +} + +void AcmReceiver::set_id(int id) { + CriticalSectionScoped lock(neteq_crit_sect_); + id_ = id; +} + +uint32_t AcmReceiver::PlayoutTimestamp() { + if (av_sync_) { + assert(initial_delay_manager_.get()); + if (initial_delay_manager_->buffering()) + return initial_delay_manager_->playout_timestamp(); + } + return neteq_->PlayoutTimestamp(); +} + +int AcmReceiver::last_audio_codec_id() const { + CriticalSectionScoped lock(neteq_crit_sect_); + return last_audio_decoder_; +} + +int AcmReceiver::last_audio_payload_type() const { + CriticalSectionScoped lock(neteq_crit_sect_); + if (last_audio_decoder_ < 0) + return -1; + assert(decoders_[last_audio_decoder_].registered); + return decoders_[last_audio_decoder_].payload_type; +} + +int AcmReceiver::RedPayloadType() const { + CriticalSectionScoped lock(neteq_crit_sect_); + if (!decoders_[ACMCodecDB::kRED].registered) { + LOG_F(LS_WARNING) << "RED is not registered."; + return -1; + } + return decoders_[ACMCodecDB::kRED].payload_type; +} + +int AcmReceiver::LastAudioCodec(CodecInst* codec) const { + CriticalSectionScoped lock(neteq_crit_sect_); + if (last_audio_decoder_ < 0) { + LOG_F(LS_WARNING) << "No audio payload is received, yet."; + return -1; + } + assert(decoders_[last_audio_decoder_].registered); + memcpy(codec, &ACMCodecDB::database_[last_audio_decoder_], sizeof(CodecInst)); + codec->pltype = decoders_[last_audio_decoder_].payload_type; + codec->channels = decoders_[last_audio_decoder_].channels; + return 0; +} + +void AcmReceiver::NetworkStatistics(ACMNetworkStatistics* acm_stat) { + NetEqNetworkStatistics neteq_stat; + // NetEq function always returns zero, so we don't check the return value. + neteq_->NetworkStatistics(&neteq_stat); + + acm_stat->currentBufferSize = neteq_stat.current_buffer_size_ms; + acm_stat->preferredBufferSize = neteq_stat.preferred_buffer_size_ms; + acm_stat->jitterPeaksFound = neteq_stat.jitter_peaks_found; + acm_stat->currentPacketLossRate = neteq_stat.packet_loss_rate; + acm_stat->currentDiscardRate = neteq_stat.packet_discard_rate; + acm_stat->currentExpandRate = neteq_stat.expand_rate; + acm_stat->currentPreemptiveRate = neteq_stat.preemptive_rate; + acm_stat->currentAccelerateRate = neteq_stat.accelerate_rate; + acm_stat->clockDriftPPM = neteq_stat.clockdrift_ppm; + + std::vector waiting_times; + neteq_->WaitingTimes(&waiting_times); + size_t size = waiting_times.size(); + if (size == 0) { + acm_stat->meanWaitingTimeMs = -1; + acm_stat->medianWaitingTimeMs = -1; + acm_stat->minWaitingTimeMs = -1; + acm_stat->maxWaitingTimeMs = -1; + } else { + std::sort(waiting_times.begin(), waiting_times.end()); + if ((size & 0x1) == 0) { + acm_stat->medianWaitingTimeMs = (waiting_times[size / 2 - 1] + + waiting_times[size / 2]) / 2; + } else { + acm_stat->medianWaitingTimeMs = waiting_times[size / 2]; + } + acm_stat->minWaitingTimeMs = waiting_times.front(); + acm_stat->maxWaitingTimeMs = waiting_times.back(); + double sum = 0; + for (size_t i = 0; i < size; ++i) { + sum += waiting_times[i]; + } + acm_stat->meanWaitingTimeMs = static_cast(sum / size); + } +} + +int AcmReceiver::DecoderByPayloadType(uint8_t payload_type, + CodecInst* codec) const { + CriticalSectionScoped lock(neteq_crit_sect_); + int codec_index = PayloadType2CodecIndex(payload_type); + if (codec_index < 0) { + LOG_FERR1(LS_ERROR, "AcmReceiver::DecoderByPayloadType", payload_type); + return -1; + } + memcpy(codec, &ACMCodecDB::database_[codec_index], sizeof(CodecInst)); + codec->pltype = decoders_[codec_index].payload_type; + codec->channels = decoders_[codec_index].channels; + return 0; +} + +int AcmReceiver::PayloadType2CodecIndex(uint8_t payload_type) const { + for (int n = 0; n < ACMCodecDB::kMaxNumCodecs; ++n) { + if (decoders_[n].registered && decoders_[n].payload_type == payload_type) { + return n; + } + } + return -1; +} + +int AcmReceiver::EnableNack(size_t max_nack_list_size) { + // Don't do anything if |max_nack_list_size| is out of range. + if (max_nack_list_size == 0 || max_nack_list_size > Nack::kNackListSizeLimit) + return -1; + + CriticalSectionScoped lock(neteq_crit_sect_); + if (!nack_enabled_) { + nack_.reset(Nack::Create(kNackThresholdPackets)); + nack_enabled_ = true; + + // Sampling rate might need to be updated if we change from disable to + // enable. Do it if the receive codec is valid. + if (last_audio_decoder_ >= 0) { + nack_->UpdateSampleRate( + ACMCodecDB::database_[last_audio_decoder_].plfreq); + } + } + return nack_->SetMaxNackListSize(max_nack_list_size); +} + +void AcmReceiver::DisableNack() { + CriticalSectionScoped lock(neteq_crit_sect_); + nack_.reset(); // Memory is released. + nack_enabled_ = false; +} + +std::vector AcmReceiver::GetNackList( + int round_trip_time_ms) const { + CriticalSectionScoped lock(neteq_crit_sect_); + if (round_trip_time_ms < 0) { + WEBRTC_TRACE(webrtc::kTraceWarning, webrtc::kTraceAudioCoding, id_, + "GetNackList: round trip time cannot be negative." + " round_trip_time_ms=%d", round_trip_time_ms); + } + if (nack_enabled_ && round_trip_time_ms >= 0) { + assert(nack_.get()); + return nack_->GetNackList(round_trip_time_ms); + } + std::vector empty_list; + return empty_list; +} + +void AcmReceiver::ResetInitialDelay() { + { + CriticalSectionScoped lock(neteq_crit_sect_); + av_sync_ = false; + initial_delay_manager_.reset(NULL); + missing_packets_sync_stream_.reset(NULL); + late_packets_sync_stream_.reset(NULL); + } + neteq_->SetMinimumDelay(0); + // TODO(turajs): Should NetEq Buffer be flushed? +} + +// This function is called within critical section, no need to acquire a lock. +bool AcmReceiver::GetSilence(int desired_sample_rate_hz, AudioFrame* frame) { + assert(av_sync_); + assert(initial_delay_manager_.get()); + if (!initial_delay_manager_->buffering()) { + return false; + } + + // We stop accumulating packets, if the number of packets or the total size + // exceeds a threshold. + int num_packets; + int max_num_packets; + int buffer_size_byte; + int max_buffer_size_byte; + const float kBufferingThresholdScale = 0.9; + neteq_->PacketBufferStatistics(&num_packets, &max_num_packets, + &buffer_size_byte, &max_buffer_size_byte); + if (num_packets > max_num_packets * kBufferingThresholdScale || + buffer_size_byte > max_buffer_size_byte * kBufferingThresholdScale) { + initial_delay_manager_->DisableBuffering(); + return false; + } + + // Set the values if already got a packet, otherwise set to default values. + if (last_audio_decoder_ >= 0) { + current_sample_rate_hz_ = ACMCodecDB::database_[last_audio_decoder_].plfreq; + frame->num_channels_ = decoders_[last_audio_decoder_].channels; + } else { + current_sample_rate_hz_ = kNeteqInitSampleRateHz; + frame->num_channels_ = 1; + } + + // Set the audio frame's sampling frequency. + if (desired_sample_rate_hz > 0) { + frame->sample_rate_hz_ = desired_sample_rate_hz; + } else { + frame->sample_rate_hz_ = current_sample_rate_hz_; + } + + frame->samples_per_channel_ = frame->sample_rate_hz_ / 100; // Always 10 ms. + frame->speech_type_ = AudioFrame::kCNG; + frame->vad_activity_ = AudioFrame::kVadPassive; + frame->energy_ = 0; + int samples = frame->samples_per_channel_ * frame->num_channels_; + memset(frame->data_, 0, samples * sizeof(int16_t)); + return true; +} + +NetEqBackgroundNoiseMode AcmReceiver::BackgroundNoiseModeForTest() const { + return neteq_->BackgroundNoiseMode(); +} + +int AcmReceiver::RtpHeaderToCodecIndex( + const RTPHeader &rtp_header, const uint8_t* payload) const { + uint8_t payload_type = rtp_header.payloadType; + if (decoders_[ACMCodecDB::kRED].registered && + payload_type == decoders_[ACMCodecDB::kRED].payload_type) { + // This is a RED packet, get the payload of the audio codec. + payload_type = payload[0] & 0x7F; + } + + // Check if the payload is registered. + return PayloadType2CodecIndex(payload_type); +} + +uint32_t AcmReceiver::NowInTimestamp(int decoder_sampling_rate) const { + // Down-cast the time to (32-6)-bit since we only care about + // the least significant bits. (32-6) bits cover 2^(32-6) = 67108864 ms. + // We masked 6 most significant bits of 32-bit so there is no overflow in + // the conversion from milliseconds to timestamp. + const uint32_t now_in_ms = static_cast( + TickTime::MillisecondTimestamp() & 0x03ffffff); + return static_cast( + (decoder_sampling_rate / 1000) * now_in_ms); +} + +// This function only interacts with |neteq_|, therefore, it does not have to +// be within critical section of AcmReceiver. It is inserting packets +// into NetEq, so we call it when |decode_lock_| is acquired. However, this is +// not essential as sync-packets do not interact with codecs (especially BWE). +void AcmReceiver::InsertStreamOfSyncPackets( + InitialDelayManager::SyncStream* sync_stream) { + assert(sync_stream); + assert(av_sync_); + for (int n = 0; n < sync_stream->num_sync_packets; ++n) { + neteq_->InsertSyncPacket(sync_stream->rtp_info, + sync_stream->receive_timestamp); + ++sync_stream->rtp_info.header.sequenceNumber; + sync_stream->rtp_info.header.timestamp += sync_stream->timestamp_step; + sync_stream->receive_timestamp += sync_stream->timestamp_step; + } +} + +} // namespace webrtc diff --git a/webrtc/modules/audio_coding/main/acm2/acm_receiver.h b/webrtc/modules/audio_coding/main/acm2/acm_receiver.h new file mode 100644 index 0000000000..344e1c9206 --- /dev/null +++ b/webrtc/modules/audio_coding/main/acm2/acm_receiver.h @@ -0,0 +1,365 @@ +/* + * Copyright (c) 2013 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_SOURCE_ACM_RECEIVER_H_ +#define WEBRTC_MODULES_AUDIO_CODING_MAIN_SOURCE_ACM_RECEIVER_H_ + +#include + +#include "webrtc/common_audio/vad/include/webrtc_vad.h" +#include "webrtc/engine_configurations.h" +#include "webrtc/modules/audio_coding/main/interface/audio_coding_module.h" +#include "webrtc/modules/audio_coding/main/source/acm_codec_database.h" +#include "webrtc/modules/audio_coding/main/source/acm_resampler.h" +#include "webrtc/modules/audio_coding/main/source/initial_delay_manager.h" +#include "webrtc/modules/audio_coding/neteq4/interface/neteq.h" +#include "webrtc/modules/interface/module_common_types.h" +#include "webrtc/system_wrappers/interface/scoped_ptr.h" +#include "webrtc/typedefs.h" + +namespace webrtc { + +class CriticalSectionWrapper; +class RWLockWrapper; +class NetEq; +class Nack; +struct CodecInst; + +class AcmReceiver { + public: + struct Decoder { + bool registered; + uint8_t payload_type; + // This field is meaningful for codecs where both mono and + // stereo versions are registered under the same ID. + int channels; + }; + + // Constructor of the class + AcmReceiver(); + + explicit AcmReceiver(NetEq* neteq); + + // Destructor of the class. + ~AcmReceiver(); + + // + // Inserts a payload with its associated RTP-header into NetEq. + // + // Input: + // - rtp_header : RTP header for the incoming payload containing + // information about payload type, sequence number, + // timestamp, SSRC and marker bit. + // - incoming_payload : Incoming audio payload. + // - length_payload : Length of incoming audio payload in bytes. + // + // Return value : 0 if OK. + // <0 if NetEq returned an error. + // + int InsertPacket(const WebRtcRTPHeader& rtp_header, + const uint8_t* incoming_payload, + int length_payload); + + // + // Asks NetEq for 10 milliseconds of decoded audio. + // + // Input: + // -desired_freq_hz : specifies the sampling rate [Hz] of the output + // audio. If set -1 indicates to resampling is + // is required and the audio returned at the + // sampling rate of the decoder. + // + // Output: + // -audio_frame : an audio frame were output data and + // associated parameters are written to. + // + // Return value : 0 if OK. + // -1 if NetEq returned an error. + // + int GetAudio(int desired_freq_hz, AudioFrame* audio_frame); + + // + // Adds a new codec to the NetEq codec database. + // + // Input: + // - acm_codec_id : ACM codec ID. + // - payload_type : payload type. + // - audio_decoder : pointer to a decoder object. If it is NULL + // then NetEq will internally create the decoder + // object. Otherwise, NetEq will store this pointer + // as the decoder corresponding with the given + // payload type. NetEq won't acquire the ownership + // of this pointer. It is up to the client of this + // class (ACM) to delete it. By providing + // |audio_decoder| ACM will have control over the + // decoder instance of the codec. This is essential + // for a codec like iSAC which encoder/decoder + // encoder has to know about decoder (bandwidth + // estimator that is updated at decoding time). + // + // Return value : 0 if OK. + // <0 if NetEq returned an error. + // + int AddCodec(int acm_codec_id, + uint8_t payload_type, + int channels, + AudioDecoder* audio_decoder); + + // + // Sets a minimum delay for packet buffer. The given delay is maintained, + // unless channel condition dictates a higher delay. + // + // Input: + // - delay_ms : minimum delay in milliseconds. + // + // Return value : 0 if OK. + // <0 if NetEq returned an error. + // + int SetMinimumDelay(int delay_ms); + + // + // Sets a maximum delay [ms] for the packet buffer. The target delay does not + // exceed the given value, even if channel condition requires so. + // + // Input: + // - delay_ms : maximum delay in milliseconds. + // + // Return value : 0 if OK. + // <0 if NetEq returned an error. + // + int SetMaximumDelay(int delay_ms); + + // + // Get least required delay computed based on channel conditions. Note that + // this is before applying any user-defined limits (specified by calling + // (SetMinimumDelay() and/or SetMaximumDelay()). + // + int LeastRequiredDelayMs() const; + + // + // Sets an initial delay of |delay_ms| milliseconds. This introduces a playout + // delay. Silence (zero signal) is played out until equivalent of |delay_ms| + // millisecond of audio is buffered. Then, NetEq maintains the delay. + // + // Input: + // - delay_ms : initial delay in milliseconds. + // + // Return value : 0 if OK. + // <0 if NetEq returned an error. + // + int SetInitialDelay(int delay_ms); + + // + // Resets the initial delay to zero. + // + void ResetInitialDelay(); + + // + // Get the current sampling frequency in Hz. + // + // Return value : Sampling frequency in Hz. + // + int current_sample_rate_hz() const; + + // + // Sets the playout mode. + // + // Input: + // - mode : an enumerator specifying the playout mode. + // + void SetPlayoutMode(AudioPlayoutMode mode); + + // + // Get the current playout mode. + // + // Return value : The current playout mode. + // + AudioPlayoutMode PlayoutMode() const; + + // + // Get the current network statistics from NetEq. + // + // Output: + // - statistics : The current network statistics. + // + void NetworkStatistics(ACMNetworkStatistics* statistics); + + // + // Enable post-decoding VAD. + // + void EnableVad(); + + // + // Disable post-decoding VAD. + // + void DisableVad(); + + // + // Returns whether post-decoding VAD is enabled (true) or disabled (false). + // + bool vad_enabled() const { return vad_enabled_; } + + // + // Get the decode lock used to protect decoder instances while decoding. + // + // Return value : Pointer to the decode lock. + // + RWLockWrapper* DecodeLock() const { return decode_lock_; } + + // + // Flushes the NetEq packet and speech buffers. + // + void FlushBuffers(); + + // + // Removes a payload-type from the NetEq codec database. + // + // Input: + // - payload_type : the payload-type to be removed. + // + // Return value : 0 if OK. + // -1 if an error occurred. + // + int RemoveCodec(uint8_t payload_type); + + // + // Remove all registered codecs. + // + int RemoveAllCodecs(); + + // + // Set ID. + // + void set_id(int id); // TODO(turajs): can be inline. + + // + // Returns the RTP timestamp of the last sample delivered by GetAudio(). + // + uint32_t PlayoutTimestamp(); + + // + // Return the index of the codec associated with the last non-CNG/non-DTMF + // received payload. If no non-CNG/non-DTMF payload is received -1 is + // returned. + // + int last_audio_codec_id() const; // TODO(turajs): can be inline. + + // + // Return the payload-type of the last non-CNG/non-DTMF RTP packet. If no + // non-CNG/non-DTMF packet is received -1 is returned. + // + int last_audio_payload_type() const; // TODO(turajs): can be inline. + + // + // Get the audio codec associated with the last non-CNG/non-DTMF received + // payload. If no non-CNG/non-DTMF packet is received -1 is returned, + // otherwise return 0. + // + int LastAudioCodec(CodecInst* codec) const; + + // + // Return payload type of RED if it is registered, otherwise return -1; + // + int RedPayloadType() const; + + // + // Get a decoder given its registered payload-type. + // + // Input: + // -payload_type : the payload-type of the codec to be retrieved. + // + // Output: + // -codec : codec associated with the given payload-type. + // + // Return value : 0 if succeeded. + // -1 if failed, e.g. given payload-type is not + // registered. + // + int DecoderByPayloadType(uint8_t payload_type, + CodecInst* codec) const; + + // + // Enable NACK and set the maximum size of the NACK list. If NACK is already + // enabled then the maximum NACK list size is modified accordingly. + // + // Input: + // -max_nack_list_size : maximum NACK list size + // should be positive (none zero) and less than or + // equal to |Nack::kNackListSizeLimit| + // Return value + // : 0 if succeeded. + // -1 if failed + // + int EnableNack(size_t max_nack_list_size); + + // Disable NACK. + void DisableNack(); + + // + // Get a list of packets to be retransmitted. + // + // Input: + // -round_trip_time_ms : estimate of the round-trip-time (in milliseconds). + // Return value : list of packets to be retransmitted. + // + std::vector GetNackList(int round_trip_time_ms) const; + + // + // Returns the background noise mode. This is only for testing and ACM is not + // calling this function. Used in acm_receiver_unittest.cc. + // + NetEqBackgroundNoiseMode BackgroundNoiseModeForTest() const; + + private: + int PayloadType2CodecIndex(uint8_t payload_type) const; + + bool GetSilence(int desired_sample_rate_hz, AudioFrame* frame); + + int GetNumSyncPacketToInsert(uint16_t received_squence_number); + + int RtpHeaderToCodecIndex( + const RTPHeader& rtp_header, const uint8_t* payload) const; + + uint32_t NowInTimestamp(int decoder_sampling_rate) const; + + void InsertStreamOfSyncPackets(InitialDelayManager::SyncStream* sync_stream); + + int id_; + NetEq* neteq_; + Decoder decoders_[ACMCodecDB::kMaxNumCodecs]; + int last_audio_decoder_; + RWLockWrapper* decode_lock_; + CriticalSectionWrapper* neteq_crit_sect_; + bool vad_enabled_; + AudioFrame::VADActivity previous_audio_activity_; + int current_sample_rate_hz_; + ACMResampler resampler_; + // Used in GetAudio, declared as member to avoid allocating every 10ms. + int16_t audio_buffer_[AudioFrame::kMaxDataSizeSamples]; + scoped_ptr nack_; + bool nack_enabled_; + + // Indicates if a non-zero initial delay is set, and the receiver is in + // AV-sync mode. + bool av_sync_; + scoped_ptr initial_delay_manager_; + + // The following are defined as members to avoid creating them in every + // iteration. |missing_packets_sync_stream_| is *ONLY* used in InsertPacket(). + // |late_packets_sync_stream_| is only used in GetAudio(). Both of these + // member variables are allocated only when we AV-sync is enabled, i.e. + // initial delay is set. + scoped_ptr missing_packets_sync_stream_; + scoped_ptr late_packets_sync_stream_; +}; + +} // namespace webrtc + +#endif // WEBRTC_MODULES_AUDIO_CODING_MAIN_SOURCE_ACM_RECEIVER_H_ diff --git a/webrtc/modules/audio_coding/main/acm2/acm_receiver_unittest.cc b/webrtc/modules/audio_coding/main/acm2/acm_receiver_unittest.cc new file mode 100644 index 0000000000..ab652cfaa4 --- /dev/null +++ b/webrtc/modules/audio_coding/main/acm2/acm_receiver_unittest.cc @@ -0,0 +1,419 @@ +/* + * Copyright (c) 2013 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/source/acm_receiver.h" + +#include // std::min + +#include "gtest/gtest.h" +#include "webrtc/modules/audio_coding/main/interface/audio_coding_module.h" +#include "webrtc/modules/audio_coding/main/source/acm_codec_database.h" +#include "webrtc/modules/audio_coding/neteq4/tools/rtp_generator.h" +#include "webrtc/system_wrappers/interface/scoped_ptr.h" +#include "webrtc/test/test_suite.h" +#include "webrtc/test/testsupport/fileutils.h" + +namespace webrtc { +namespace { + +bool CodecsEqual(const CodecInst& codec_a, const CodecInst& codec_b) { + if (strcmp(codec_a.plname, codec_b.plname) != 0 || + codec_a.plfreq != codec_b.plfreq || + codec_a.pltype != codec_b.pltype || + codec_b.channels != codec_a.channels) + return false; + return true; +} + +} // namespace + +class AcmReceiverTest : public AudioPacketizationCallback, + public ::testing::Test { + protected: + AcmReceiverTest() + : receiver_(new AcmReceiver), + acm_(AudioCodingModule::Create(0)), + timestamp_(0), + packet_sent_(false), + last_packet_send_timestamp_(timestamp_), + last_frame_type_(kFrameEmpty) {} + + ~AcmReceiverTest() {} + + void SetUp() { + ASSERT_TRUE(receiver_.get() != NULL); + ASSERT_TRUE(acm_ != NULL); + for (int n = 0; n < ACMCodecDB::kNumCodecs; n++) { + ASSERT_EQ(0, ACMCodecDB::Codec(n, &codecs_[n])); + } + + acm_->InitializeReceiver(); + acm_->InitializeSender(); + acm_->RegisterTransportCallback(this); + + rtp_header_.header.sequenceNumber = 0; + rtp_header_.header.timestamp = 0; + rtp_header_.header.markerBit = false; + rtp_header_.header.ssrc = 0x12345678; // Arbitrary. + rtp_header_.header.numCSRCs = 0; + rtp_header_.header.payloadType = 0; + rtp_header_.frameType = kAudioFrameSpeech; + rtp_header_.type.Audio.isCNG = false; + } + + void TearDown() { + AudioCodingModule::Destroy(acm_); + } + + void InsertOnePacketOfSilence(int codec_id) { + CodecInst codec; + ACMCodecDB::Codec(codec_id, &codec); + if (timestamp_ == 0) { // This is the first time inserting audio. + ASSERT_EQ(0, acm_->RegisterSendCodec(codec)); + } else { + CodecInst current_codec; + ASSERT_EQ(0, acm_->SendCodec(¤t_codec)); + if (!CodecsEqual(codec, current_codec)) + ASSERT_EQ(0, acm_->RegisterSendCodec(codec)); + } + AudioFrame frame; + // Frame setup according to the codec. + frame.sample_rate_hz_ = codec.plfreq; + frame.samples_per_channel_ = codec.plfreq / 100; // 10 ms. + frame.num_channels_ = codec.channels; + memset(frame.data_, 0, frame.samples_per_channel_ * frame.num_channels_ * + sizeof(int16_t)); + int num_bytes = 0; + packet_sent_ = false; + last_packet_send_timestamp_ = timestamp_; + while (num_bytes == 0) { + frame.timestamp_ = timestamp_; + timestamp_ += frame.samples_per_channel_; + ASSERT_EQ(0, acm_->Add10MsData(frame)); + num_bytes = acm_->Process(); + ASSERT_GE(num_bytes, 0); + } + ASSERT_TRUE(packet_sent_); // Sanity check. + } + + // Last element of id should be negative. + void AddSetOfCodecs(const int* id) { + int n = 0; + while (id[n] >= 0) { + ASSERT_EQ(0, receiver_->AddCodec(id[n], codecs_[id[n]].pltype, + codecs_[id[n]].channels, NULL)); + ++n; + } + } + + virtual int SendData( + FrameType frame_type, + uint8_t payload_type, + uint32_t timestamp, + const uint8_t* payload_data, + uint16_t payload_len_bytes, + const RTPFragmentationHeader* fragmentation) { + if (frame_type == kFrameEmpty) + return 0; + + rtp_header_.header.payloadType = payload_type; + rtp_header_.frameType = frame_type; + if (frame_type == kAudioFrameSpeech) + rtp_header_.type.Audio.isCNG = false; + else + rtp_header_.type.Audio.isCNG = true; + rtp_header_.header.timestamp = timestamp; + + int ret_val = receiver_->InsertPacket(rtp_header_, payload_data, + payload_len_bytes); + if (ret_val < 0) { + assert(false); + return -1; + } + rtp_header_.header.sequenceNumber++; + packet_sent_ = true; + last_frame_type_ = frame_type; + return 0; + } + + scoped_ptr receiver_; + CodecInst codecs_[ACMCodecDB::kMaxNumCodecs]; + AudioCodingModule* acm_; + WebRtcRTPHeader rtp_header_; + uint32_t timestamp_; + bool packet_sent_; // Set when SendData is called reset when inserting audio. + uint32_t last_packet_send_timestamp_; + FrameType last_frame_type_; +}; + +TEST_F(AcmReceiverTest, AddCodecGetCodec) { + // Add codec. + for (int n = 0; n < ACMCodecDB::kNumCodecs; ++n) { + if (n & 0x1) // Just add codecs with odd index. + EXPECT_EQ(0, receiver_->AddCodec(n, codecs_[n].pltype, + codecs_[n].channels, NULL)); + } + // Get codec and compare. + for (int n = 0; n < ACMCodecDB::kNumCodecs; ++n) { + CodecInst my_codec; + if (n & 0x1) { + // Codecs with odd index should match the reference. + EXPECT_EQ(0, receiver_->DecoderByPayloadType(codecs_[n].pltype, + &my_codec)); + EXPECT_TRUE(CodecsEqual(codecs_[n], my_codec)); + } else { + // Codecs with even index are not registered. + EXPECT_EQ(-1, receiver_->DecoderByPayloadType(codecs_[n].pltype, + &my_codec)); + } + } +} + +TEST_F(AcmReceiverTest, AddCodecChangePayloadType) { + CodecInst ref_codec; + const int codec_id = ACMCodecDB::kPCMA; + EXPECT_EQ(0, ACMCodecDB::Codec(codec_id, &ref_codec)); + const int payload_type = ref_codec.pltype; + EXPECT_EQ(0, receiver_->AddCodec(codec_id, ref_codec.pltype, + ref_codec.channels, NULL)); + CodecInst test_codec; + EXPECT_EQ(0, receiver_->DecoderByPayloadType(payload_type, &test_codec)); + EXPECT_EQ(true, CodecsEqual(ref_codec, test_codec)); + + // Re-register the same codec with different payload. + ref_codec.pltype = payload_type + 1; + EXPECT_EQ(0, receiver_->AddCodec(codec_id, ref_codec.pltype, + ref_codec.channels, NULL)); + + // Payload type |payload_type| should not exist. + EXPECT_EQ(-1, receiver_->DecoderByPayloadType(payload_type, &test_codec)); + + // Payload type |payload_type + 1| should exist. + EXPECT_EQ(0, receiver_->DecoderByPayloadType(payload_type + 1, &test_codec)); + EXPECT_TRUE(CodecsEqual(test_codec, ref_codec)); +} + +TEST_F(AcmReceiverTest, AddCodecRemoveCodec) { + CodecInst codec; + const int codec_id = ACMCodecDB::kPCMA; + EXPECT_EQ(0, ACMCodecDB::Codec(codec_id, &codec)); + const int payload_type = codec.pltype; + EXPECT_EQ(0, receiver_->AddCodec(codec_id, codec.pltype, + codec.channels, NULL)); + + // Remove non-existing codec, must fail. + EXPECT_EQ(-1, receiver_->RemoveCodec(payload_type + 1)); + + // Remove an existing codec. + EXPECT_EQ(0, receiver_->RemoveCodec(payload_type)); + + // Ask for the removed codec, must fail. + EXPECT_EQ(-1, receiver_->DecoderByPayloadType(payload_type, &codec)); +} + +TEST_F(AcmReceiverTest, SampleRate) { + const int kCodecId[] = { + ACMCodecDB::kISAC, ACMCodecDB::kISACSWB, ACMCodecDB::kISACFB, + -1 // Terminator. + }; + AddSetOfCodecs(kCodecId); + + AudioFrame frame; + const int kOutSampleRateHz = 8000; // Different than codec sample rate. + int n = 0; + while (kCodecId[n] >= 0) { + const int num_10ms_frames = codecs_[kCodecId[n]].pacsize / + (codecs_[kCodecId[n]].plfreq / 100); + InsertOnePacketOfSilence(kCodecId[n]); + for (int k = 0; k < num_10ms_frames; ++k) { + EXPECT_EQ(0, receiver_->GetAudio(kOutSampleRateHz, &frame)); + } + EXPECT_EQ(std::min(32000, codecs_[kCodecId[n]].plfreq), + receiver_->current_sample_rate_hz()); + ++n; + } +} + +// Changing playout mode to FAX should not change the background noise mode. +TEST_F(AcmReceiverTest, PlayoutModeAndBackgroundNoiseMode) { + EXPECT_EQ(kBgnOn, receiver_->BackgroundNoiseModeForTest()); // Default + + receiver_->SetPlayoutMode(voice); + EXPECT_EQ(voice, receiver_->PlayoutMode()); + EXPECT_EQ(kBgnOn, receiver_->BackgroundNoiseModeForTest()); + + receiver_->SetPlayoutMode(streaming); + EXPECT_EQ(streaming, receiver_->PlayoutMode()); + EXPECT_EQ(kBgnOff, receiver_->BackgroundNoiseModeForTest()); + + receiver_->SetPlayoutMode(fax); + EXPECT_EQ(fax, receiver_->PlayoutMode()); + EXPECT_EQ(kBgnOff, receiver_->BackgroundNoiseModeForTest()); + + receiver_->SetPlayoutMode(off); + EXPECT_EQ(off, receiver_->PlayoutMode()); + EXPECT_EQ(kBgnOff, receiver_->BackgroundNoiseModeForTest()); + + // Change to voice then to FAX. + receiver_->SetPlayoutMode(voice); + EXPECT_EQ(voice, receiver_->PlayoutMode()); + EXPECT_EQ(kBgnOn, receiver_->BackgroundNoiseModeForTest()); + receiver_->SetPlayoutMode(fax); + EXPECT_EQ(fax, receiver_->PlayoutMode()); + EXPECT_EQ(kBgnOn, receiver_->BackgroundNoiseModeForTest()); +} + +TEST_F(AcmReceiverTest, PostdecodingVad) { + receiver_->EnableVad(); + EXPECT_TRUE(receiver_->vad_enabled()); + + const int id = ACMCodecDB::kPCM16Bwb; + ASSERT_EQ(0, receiver_->AddCodec(id, codecs_[id].pltype, codecs_[id].channels, + NULL)); + const int kNumPackets = 5; + const int num_10ms_frames = codecs_[id].pacsize / (codecs_[id].plfreq / 100); + AudioFrame frame; + for (int n = 0; n < kNumPackets; ++n) { + InsertOnePacketOfSilence(id); + for (int k = 0; k < num_10ms_frames; ++k) + ASSERT_EQ(0, receiver_->GetAudio(codecs_[id].plfreq, &frame)); + } + EXPECT_EQ(AudioFrame::kVadPassive, frame.vad_activity_); + + receiver_->DisableVad(); + EXPECT_FALSE(receiver_->vad_enabled()); + + for (int n = 0; n < kNumPackets; ++n) { + InsertOnePacketOfSilence(id); + for (int k = 0; k < num_10ms_frames; ++k) + ASSERT_EQ(0, receiver_->GetAudio(codecs_[id].plfreq, &frame)); + } + EXPECT_EQ(AudioFrame::kVadUnknown, frame.vad_activity_); +} + +TEST_F(AcmReceiverTest, FlushBuffer) { + const int id = ACMCodecDB::kISAC; + EXPECT_EQ(0, receiver_->AddCodec(id, codecs_[id].pltype, codecs_[id].channels, + NULL)); + const int kNumPackets = 5; + const int num_10ms_frames = codecs_[id].pacsize / (codecs_[id].plfreq / 100); + for (int n = 0; n < kNumPackets; ++n) + InsertOnePacketOfSilence(id); + ACMNetworkStatistics statistics; + receiver_->NetworkStatistics(&statistics); + ASSERT_EQ(num_10ms_frames * kNumPackets * 10, statistics.currentBufferSize); + + receiver_->FlushBuffers(); + receiver_->NetworkStatistics(&statistics); + ASSERT_EQ(0, statistics.currentBufferSize); +} + +TEST_F(AcmReceiverTest, PlayoutTimestamp) { + const int id = ACMCodecDB::kPCM16Bwb; + EXPECT_EQ(0, receiver_->AddCodec(id, codecs_[id].pltype, codecs_[id].channels, + NULL)); + receiver_->SetPlayoutMode(fax); + const int kNumPackets = 5; + const int num_10ms_frames = codecs_[id].pacsize / (codecs_[id].plfreq / 100); + uint32_t expected_timestamp; + AudioFrame frame; + int ts_offset = 0; + bool first_audio_frame = true; + for (int n = 0; n < kNumPackets; ++n) { + packet_sent_ = false; + InsertOnePacketOfSilence(id); + ASSERT_TRUE(packet_sent_); + expected_timestamp = last_packet_send_timestamp_; + for (int k = 0; k < num_10ms_frames; ++k) { + ASSERT_EQ(0, receiver_->GetAudio(codecs_[id].plfreq, &frame)); + if (first_audio_frame) { + // There is an offset in playout timestamps. Perhaps, it is related to + // initial delay that NetEq applies + ts_offset = receiver_->PlayoutTimestamp() - expected_timestamp; + first_audio_frame = false; + } else { + EXPECT_EQ(expected_timestamp + ts_offset, + receiver_->PlayoutTimestamp()); + } + expected_timestamp += codecs_[id].plfreq / 100; // Increment by 10 ms. + } + } +} + +TEST_F(AcmReceiverTest, LastAudioCodec) { + const int kCodecId[] = { + ACMCodecDB::kISAC, ACMCodecDB::kPCMA, ACMCodecDB::kISACSWB, + ACMCodecDB::kPCM16Bswb32kHz, ACMCodecDB::kG722_1C_48, + -1 // Terminator. + }; + AddSetOfCodecs(kCodecId); + + const int kCngId[] = { // Not including full-band. + ACMCodecDB::kCNNB, ACMCodecDB::kCNWB, ACMCodecDB::kCNSWB, + -1 // Terminator. + }; + AddSetOfCodecs(kCngId); + + // Register CNG at sender side. + int n = 0; + while (kCngId[n] > 0) { + ASSERT_EQ(0, acm_->RegisterSendCodec(codecs_[kCngId[n]])); + ++n; + } + + CodecInst codec; + // No audio payload is received. + EXPECT_EQ(-1, receiver_->LastAudioCodec(&codec)); + + // Start with sending DTX. + ASSERT_EQ(0, acm_->SetVAD(true, true, VADVeryAggr)); + packet_sent_ = false; + InsertOnePacketOfSilence(kCodecId[0]); // Enough to test with one codec. + ASSERT_TRUE(packet_sent_); + EXPECT_EQ(kAudioFrameCN, last_frame_type_); + + // Has received, only, DTX. Last Audio codec is undefined. + EXPECT_EQ(-1, receiver_->LastAudioCodec(&codec)); + EXPECT_EQ(-1, receiver_->last_audio_codec_id()); + EXPECT_EQ(-1, receiver_->last_audio_payload_type()); + + n = 0; + while (kCodecId[n] >= 0) { // Loop over codecs. + // Set DTX off to send audio payload. + acm_->SetVAD(false, false, VADAggr); + packet_sent_ = false; + InsertOnePacketOfSilence(kCodecId[n]); + + // Sanity check if Actually an audio payload received, and it should be + // of type "speech." + ASSERT_TRUE(packet_sent_); + ASSERT_EQ(kAudioFrameSpeech, last_frame_type_); + EXPECT_EQ(kCodecId[n], receiver_->last_audio_codec_id()); + + // Set VAD on to send DTX. Then check if the "Last Audio codec" returns + // the expected codec. + acm_->SetVAD(true, true, VADAggr); + + // Do as many encoding until a DTX is sent. + while (last_frame_type_ != kAudioFrameCN) { + packet_sent_ = false; + InsertOnePacketOfSilence(kCodecId[n]); + ASSERT_TRUE(packet_sent_); + } + EXPECT_EQ(kCodecId[n], receiver_->last_audio_codec_id()); + EXPECT_EQ(codecs_[kCodecId[n]].pltype, + receiver_->last_audio_payload_type()); + EXPECT_EQ(0, receiver_->LastAudioCodec(&codec)); + EXPECT_TRUE(CodecsEqual(codecs_[kCodecId[n]], codec)); + ++n; + } +} + +} // namespace webrtc diff --git a/webrtc/modules/audio_coding/main/acm2/acm_red.cc b/webrtc/modules/audio_coding/main/acm2/acm_red.cc new file mode 100644 index 0000000000..5b5b16f0de --- /dev/null +++ b/webrtc/modules/audio_coding/main/acm2/acm_red.cc @@ -0,0 +1,50 @@ +/* + * 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/source/acm_red.h" + +#include "webrtc/modules/audio_coding/main/source/acm_common_defs.h" +#include "webrtc/system_wrappers/interface/trace.h" + +namespace webrtc { + +ACMRED::ACMRED(int16_t codec_id) { codec_id_ = codec_id; } + +ACMRED::~ACMRED() {} + +int16_t ACMRED::InternalEncode(uint8_t* /* bitstream */, + int16_t* /* bitstream_len_byte */) { + // RED is never used as an encoder + // RED has no instance + return 0; +} + +int16_t ACMRED::InternalInitEncoder(WebRtcACMCodecParams* /* codec_params */) { + // This codec does not need initialization, + // RED has no instance + return 0; +} + +ACMGenericCodec* ACMRED::CreateInstance(void) { return NULL; } + +int16_t ACMRED::InternalCreateEncoder() { + // RED has no instance + return 0; +} + +void ACMRED::InternalDestructEncoderInst(void* /* ptr_inst */) { + // RED has no instance +} + +void ACMRED::DestructEncoderSafe() { + // RED has no instance +} + +} // namespace webrtc diff --git a/webrtc/modules/audio_coding/main/acm2/acm_red.h b/webrtc/modules/audio_coding/main/acm2/acm_red.h new file mode 100644 index 0000000000..c8023db635 --- /dev/null +++ b/webrtc/modules/audio_coding/main/acm2/acm_red.h @@ -0,0 +1,40 @@ +/* + * 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. + */ + +#ifndef WEBRTC_MODULES_AUDIO_CODING_MAIN_SOURCE_ACM_RED_H_ +#define WEBRTC_MODULES_AUDIO_CODING_MAIN_SOURCE_ACM_RED_H_ + +#include "webrtc/modules/audio_coding/main/source/acm_generic_codec.h" + +namespace webrtc { + +class ACMRED : public ACMGenericCodec { + public: + explicit ACMRED(int16_t codec_id); + ~ACMRED(); + + // For FEC. + ACMGenericCodec* CreateInstance(void); + + int16_t InternalEncode(uint8_t* bitstream, int16_t* bitstream_len_byte); + + int16_t InternalInitEncoder(WebRtcACMCodecParams* codec_params); + + protected: + void DestructEncoderSafe(); + + int16_t InternalCreateEncoder(); + + void InternalDestructEncoderInst(void* ptr_inst); +}; + +} // namespace webrtc + +#endif // WEBRTC_MODULES_AUDIO_CODING_MAIN_SOURCE_ACM_RED_H_ diff --git a/webrtc/modules/audio_coding/main/acm2/acm_resampler.cc b/webrtc/modules/audio_coding/main/acm2/acm_resampler.cc new file mode 100644 index 0000000000..d399cee8e7 --- /dev/null +++ b/webrtc/modules/audio_coding/main/acm2/acm_resampler.cc @@ -0,0 +1,66 @@ +/* + * 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/source/acm_resampler.h" + +#include + +#include "webrtc/common_audio/resampler/include/resampler.h" +#include "webrtc/common_audio/signal_processing/include/signal_processing_library.h" +#include "webrtc/system_wrappers/interface/critical_section_wrapper.h" +#include "webrtc/system_wrappers/interface/trace.h" + +namespace webrtc { + +ACMResampler::ACMResampler() + : resampler_crit_sect_(CriticalSectionWrapper::CreateCriticalSection()) { +} + +ACMResampler::~ACMResampler() { + delete resampler_crit_sect_; +} + +int ACMResampler::Resample10Msec(const int16_t* in_audio, + int in_freq_hz, + int out_freq_hz, + int num_audio_channels, + int16_t* out_audio) { + CriticalSectionScoped cs(resampler_crit_sect_); + + if (in_freq_hz == out_freq_hz) { + size_t length = static_cast(in_freq_hz * num_audio_channels / 100); + memcpy(out_audio, in_audio, length * sizeof(int16_t)); + return static_cast(in_freq_hz / 100); + } + + // |maxLen| is maximum number of samples for 10ms at 48kHz. + int max_len = 480 * num_audio_channels; + int length_in = (in_freq_hz / 100) * num_audio_channels; + int out_len; + + ResamplerType type = (num_audio_channels == 1) ? kResamplerSynchronous : + kResamplerSynchronousStereo; + + if (resampler_.ResetIfNeeded(in_freq_hz, out_freq_hz, type) < 0) { + WEBRTC_TRACE(webrtc::kTraceError, webrtc::kTraceAudioCoding, 0, + "Error in reset of resampler"); + return -1; + } + + if (resampler_.Push(in_audio, length_in, out_audio, max_len, out_len) < 0) { + WEBRTC_TRACE(webrtc::kTraceError, webrtc::kTraceAudioCoding, 0, + "Error in resampler: resampler.Push"); + return -1; + } + + return out_len / num_audio_channels; +} + +} // namespace webrtc diff --git a/webrtc/modules/audio_coding/main/acm2/acm_resampler.h b/webrtc/modules/audio_coding/main/acm2/acm_resampler.h new file mode 100644 index 0000000000..c44fbc47f7 --- /dev/null +++ b/webrtc/modules/audio_coding/main/acm2/acm_resampler.h @@ -0,0 +1,40 @@ +/* + * 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. + */ + +#ifndef WEBRTC_MODULES_AUDIO_CODING_MAIN_SOURCE_ACM_RESAMPLER_H_ +#define WEBRTC_MODULES_AUDIO_CODING_MAIN_SOURCE_ACM_RESAMPLER_H_ + +#include "webrtc/common_audio/resampler/include/resampler.h" +#include "webrtc/typedefs.h" + +namespace webrtc { + +class CriticalSectionWrapper; + +class ACMResampler { + public: + ACMResampler(); + ~ACMResampler(); + + int Resample10Msec(const int16_t* in_audio, + int in_freq_hz, + int out_freq_hz, + int num_audio_channels, + int16_t* out_audio); + + private: + // Use the Resampler class. + Resampler resampler_; + CriticalSectionWrapper* resampler_crit_sect_; +}; + +} // namespace webrtc + +#endif // WEBRTC_MODULES_AUDIO_CODING_MAIN_SOURCE_ACM_RESAMPLER_H_ diff --git a/webrtc/modules/audio_coding/main/acm2/acm_speex.cc b/webrtc/modules/audio_coding/main/acm2/acm_speex.cc new file mode 100644 index 0000000000..80dcf5c20a --- /dev/null +++ b/webrtc/modules/audio_coding/main/acm2/acm_speex.cc @@ -0,0 +1,329 @@ +/* + * 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/source/acm_speex.h" + +#ifdef WEBRTC_CODEC_SPEEX +// NOTE! Speex is not included in the open-source package. Modify this file or +// your codec API to match the function calls and names of used Speex API file. +#include "webrtc/modules/audio_coding/main/codecs/speex/interface/speex_interface.h" +#include "webrtc/modules/audio_coding/main/source/acm_codec_database.h" +#include "webrtc/modules/audio_coding/main/source/acm_common_defs.h" +#include "webrtc/system_wrappers/interface/trace.h" +#endif + +namespace webrtc { + +#ifndef WEBRTC_CODEC_SPEEX +ACMSPEEX::ACMSPEEX(int16_t /* codec_id */) + : encoder_inst_ptr_(NULL), + compl_mode_(0), + vbr_enabled_(false), + encoding_rate_(-1), + sampling_frequency_(-1), + samples_in_20ms_audio_(-1) { + return; +} + +ACMSPEEX::~ACMSPEEX() { return; } + +int16_t ACMSPEEX::InternalEncode(uint8_t* /* bitstream */, + int16_t* /* bitstream_len_byte */) { + return -1; +} + +int16_t ACMSPEEX::EnableDTX() { return -1; } + +int16_t ACMSPEEX::DisableDTX() { return -1; } + +int16_t ACMSPEEX::InternalInitEncoder( + WebRtcACMCodecParams* /* codec_params */) { + return -1; +} + +ACMGenericCodec* ACMSPEEX::CreateInstance(void) { return NULL; } + +int16_t ACMSPEEX::InternalCreateEncoder() { return -1; } + +void ACMSPEEX::DestructEncoderSafe() { return; } + +int16_t ACMSPEEX::SetBitRateSafe(const int32_t /* rate */) { return -1; } + +void ACMSPEEX::InternalDestructEncoderInst(void* /* ptr_inst */) { return; } + +#ifdef UNUSEDSPEEX +int16_t ACMSPEEX::EnableVBR() { return -1; } + +int16_t ACMSPEEX::DisableVBR() { return -1; } + +int16_t ACMSPEEX::SetComplMode(int16_t mode) { return -1; } +#endif + +#else //===================== Actual Implementation ======================= + +ACMSPEEX::ACMSPEEX(int16_t codec_id) : encoder_inst_ptr_(NULL) { + codec_id_ = codec_id; + + // Set sampling frequency, frame size and rate Speex + if (codec_id_ == ACMCodecDB::kSPEEX8) { + sampling_frequency_ = 8000; + samples_in_20ms_audio_ = 160; + encoding_rate_ = 11000; + } else if (codec_id_ == ACMCodecDB::kSPEEX16) { + sampling_frequency_ = 16000; + samples_in_20ms_audio_ = 320; + encoding_rate_ = 22000; + } else { + WEBRTC_TRACE(webrtc::kTraceError, webrtc::kTraceAudioCoding, unique_id_, + "Wrong codec id for Speex."); + + sampling_frequency_ = -1; + samples_in_20ms_audio_ = -1; + encoding_rate_ = -1; + } + + has_internal_dtx_ = true; + dtx_enabled_ = false; + vbr_enabled_ = false; + compl_mode_ = 3; // default complexity value + + return; +} + +ACMSPEEX::~ACMSPEEX() { + if (encoder_inst_ptr_ != NULL) { + WebRtcSpeex_FreeEnc(encoder_inst_ptr_); + encoder_inst_ptr_ = NULL; + } + return; +} + +int16_t ACMSPEEX::InternalEncode(uint8_t* bitstream, + int16_t* bitstream_len_byte) { + int16_t status; + int16_t num_encoded_samples = 0; + int16_t n = 0; + + while (num_encoded_samples < frame_len_smpl_) { + status = WebRtcSpeex_Encode( + encoder_inst_ptr_, &in_audio_[in_audio_ix_read_], encoding_rate_); + + // 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_20ms_audio_; + num_encoded_samples += samples_in_20ms_audio_; + + if (status < 0) { + WEBRTC_TRACE(webrtc::kTraceError, webrtc::kTraceAudioCoding, unique_id_, + "Error in Speex encoder"); + return status; + } + + // Update VAD, if internal DTX is used + if (has_internal_dtx_ && dtx_enabled_) { + vad_label_[n++] = status; + vad_label_[n++] = status; + } + + if (status == 0) { + // This frame is detected as inactive. We need send whatever + // encoded so far. + *bitstream_len_byte = WebRtcSpeex_GetBitstream( + encoder_inst_ptr_, reinterpret_cast(bitstream)); + return *bitstream_len_byte; + } + } + + *bitstream_len_byte = WebRtcSpeex_GetBitstream( + encoder_inst_ptr_, reinterpret_cast(bitstream)); + return *bitstream_len_byte; +} + +int16_t ACMSPEEX::EnableDTX() { + if (dtx_enabled_) { + return 0; + } else if (encoder_exist_) { // check if encoder exist + // enable DTX + if (WebRtcSpeex_EncoderInit(encoder_inst_ptr_, vbr_enabled_ ? 1 : 0, + compl_mode_, 1) < 0) { + WEBRTC_TRACE(webrtc::kTraceError, webrtc::kTraceAudioCoding, unique_id_, + "Cannot enable DTX for Speex"); + return -1; + } + dtx_enabled_ = true; + return 0; + } else { + return -1; + } + + return 0; +} + +int16_t ACMSPEEX::DisableDTX() { + if (!dtx_enabled_) { + return 0; + } else if (encoder_exist_) { // check if encoder exist + // disable DTX + if (WebRtcSpeex_EncoderInit(encoder_inst_ptr_, (vbr_enabled_ ? 1 : 0), + compl_mode_, 0) < 0) { + WEBRTC_TRACE(webrtc::kTraceError, webrtc::kTraceAudioCoding, unique_id_, + "Cannot disable DTX for Speex"); + return -1; + } + dtx_enabled_ = false; + return 0; + } else { + // encoder doesn't exists, therefore disabling is harmless + return 0; + } + + return 0; +} + +int16_t ACMSPEEX::InternalInitEncoder(WebRtcACMCodecParams* codec_params) { + // sanity check + if (encoder_inst_ptr_ == NULL) { + WEBRTC_TRACE(webrtc::kTraceError, + webrtc::kTraceAudioCoding, + unique_id_, + "Cannot initialize Speex encoder, instance does not exist"); + return -1; + } + + int16_t status = SetBitRateSafe((codec_params->codecInstant).rate); + status += (WebRtcSpeex_EncoderInit(encoder_inst_ptr_, + vbr_enabled_, + compl_mode_, + ((codec_params->enable_dtx) ? 1 : 0)) < 0) + ? -1 + : 0; + + if (status >= 0) { + return 0; + } else { + WEBRTC_TRACE(webrtc::kTraceError, webrtc::kTraceAudioCoding, unique_id_, + "Error in initialization of Speex encoder"); + return -1; + } +} + +ACMGenericCodec* ACMSPEEX::CreateInstance(void) { return NULL; } + +int16_t ACMSPEEX::InternalCreateEncoder() { + return WebRtcSpeex_CreateEnc(&encoder_inst_ptr_, sampling_frequency_); +} + +void ACMSPEEX::DestructEncoderSafe() { + if (encoder_inst_ptr_ != NULL) { + WebRtcSpeex_FreeEnc(encoder_inst_ptr_); + encoder_inst_ptr_ = NULL; + } + // there is no encoder set the following + encoder_exist_ = false; + encoder_initialized_ = false; + encoding_rate_ = 0; +} + +int16_t ACMSPEEX::SetBitRateSafe(const int32_t rate) { + // Check if changed rate + if (rate == encoding_rate_) { + return 0; + } else if (rate > 2000) { + encoding_rate_ = rate; + encoder_params_.codecInstant.rate = rate; + } else { + WEBRTC_TRACE(webrtc::kTraceError, webrtc::kTraceAudioCoding, unique_id_, + "Unsupported encoding rate for Speex"); + + return -1; + } + + return 0; +} + +void ACMSPEEX::InternalDestructEncoderInst(void* ptr_inst) { + if (ptr_inst != NULL) { + WebRtcSpeex_FreeEnc(static_cast(ptr_inst)); + } + return; +} + +#ifdef UNUSEDSPEEX + +// This API is currently not in use. If requested to be able to enable/disable +// VBR an ACM API need to be added. +int16_t ACMSPEEX::EnableVBR() { + if (vbr_enabled_) { + return 0; + } else if (encoder_exist_) { // check if encoder exist + // enable Variable Bit Rate (VBR) + if (WebRtcSpeex_EncoderInit(encoder_inst_ptr_, 1, compl_mode_, + (dtx_enabled_ ? 1 : 0)) < 0) { + WEBRTC_TRACE(webrtc::kTraceError, webrtc::kTraceAudioCoding, unique_id_, + "Cannot enable VBR mode for Speex"); + + return -1; + } + vbr_enabled_ = true; + return 0; + } else { + return -1; + } +} + +// This API is currently not in use. If requested to be able to enable/disable +// VBR an ACM API need to be added. +int16_t ACMSPEEX::DisableVBR() { + if (!vbr_enabled_) { + return 0; + } else if (encoder_exist_) { // check if encoder exist + // disable DTX + if (WebRtcSpeex_EncoderInit(encoder_inst_ptr_, 0, compl_mode_, + (dtx_enabled_ ? 1 : 0)) < 0) { + WEBRTC_TRACE(webrtc::kTraceError, webrtc::kTraceAudioCoding, unique_id_, + "Cannot disable DTX for Speex"); + + return -1; + } + vbr_enabled_ = false; + return 0; + } else { + // encoder doesn't exists, therefore disabling is harmless + return 0; + } +} + +// This API is currently not in use. If requested to be able to set complexity +// an ACM API need to be added. +int16_t ACMSPEEX::SetComplMode(int16_t mode) { + // Check if new mode + if (mode == compl_mode_) { + return 0; + } else if (encoder_exist_) { // check if encoder exist + // Set new mode + if (WebRtcSpeex_EncoderInit(encoder_inst_ptr_, 0, mode, + (dtx_enabled_ ? 1 : 0)) < 0) { + WEBRTC_TRACE(webrtc::kTraceError, webrtc::kTraceAudioCoding, unique_id_, + "Error in complexity mode for Speex"); + return -1; + } + compl_mode_ = mode; + return 0; + } else { + // encoder doesn't exists, therefore disabling is harmless + return 0; + } +} + +#endif + +#endif + +} // namespace webrtc diff --git a/webrtc/modules/audio_coding/main/acm2/acm_speex.h b/webrtc/modules/audio_coding/main/acm2/acm_speex.h new file mode 100644 index 0000000000..68953a8d8d --- /dev/null +++ b/webrtc/modules/audio_coding/main/acm2/acm_speex.h @@ -0,0 +1,65 @@ +/* + * 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. + */ + +#ifndef WEBRTC_MODULES_AUDIO_CODING_MAIN_SOURCE_ACM_SPEEX_H_ +#define WEBRTC_MODULES_AUDIO_CODING_MAIN_SOURCE_ACM_SPEEX_H_ + +#include "webrtc/modules/audio_coding/main/source/acm_generic_codec.h" + +// forward declaration +struct SPEEX_encinst_t_; +struct SPEEX_decinst_t_; + +namespace webrtc { + +class ACMSPEEX : public ACMGenericCodec { + public: + explicit ACMSPEEX(int16_t codec_id); + ~ACMSPEEX(); + + // For FEC. + ACMGenericCodec* CreateInstance(void); + + int16_t InternalEncode(uint8_t* bitstream, int16_t* bitstream_len_byte); + + int16_t InternalInitEncoder(WebRtcACMCodecParams* codec_params); + + protected: + void DestructEncoderSafe(); + + int16_t InternalCreateEncoder(); + + void InternalDestructEncoderInst(void* ptr_inst); + + int16_t SetBitRateSafe(const int32_t rate); + + int16_t EnableDTX(); + + int16_t DisableDTX(); + +#ifdef UNUSEDSPEEX + int16_t EnableVBR(); + + int16_t DisableVBR(); + + int16_t SetComplMode(int16_t mode); +#endif + + SPEEX_encinst_t_* encoder_inst_ptr_; + int16_t compl_mode_; + bool vbr_enabled_; + int32_t encoding_rate_; + int16_t sampling_frequency_; + uint16_t samples_in_20ms_audio_; +}; + +} // namespace webrtc + +#endif // WEBRTC_MODULES_AUDIO_CODING_MAIN_SOURCE_ACM_SPEEX_H_ diff --git a/webrtc/modules/audio_coding/main/acm2/audio_coding_module.cc b/webrtc/modules/audio_coding/main/acm2/audio_coding_module.cc new file mode 100644 index 0000000000..97d5d46287 --- /dev/null +++ b/webrtc/modules/audio_coding/main/acm2/audio_coding_module.cc @@ -0,0 +1,101 @@ +/* + * 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/interface/audio_coding_module.h" + +#include "webrtc/common_types.h" +#include "webrtc/modules/audio_coding/main/source/acm_codec_database.h" +#include "webrtc/modules/audio_coding/main/source/audio_coding_module_impl.h" +#include "webrtc/system_wrappers/interface/trace.h" + +namespace webrtc { + +// Create module +AudioCodingModule* AudioCodingModule::Create(int id) { + return new AudioCodingModuleImpl(id); +} + +// Destroy module +void AudioCodingModule::Destroy(AudioCodingModule* module) { + delete static_cast(module); +} + +// Get number of supported codecs +int AudioCodingModule::NumberOfCodecs() { + return ACMCodecDB::kNumCodecs; +} + +// Get supported codec parameters with id +int AudioCodingModule::Codec(int list_id, CodecInst* codec) { + // Get the codec settings for the codec with the given list ID + return ACMCodecDB::Codec(list_id, codec); +} + +// Get supported codec parameters with name, frequency and number of channels. +int AudioCodingModule::Codec(const char* payload_name, + CodecInst* codec, + int sampling_freq_hz, + int channels) { + int codec_id; + + // Get the id of the codec from the database. + codec_id = ACMCodecDB::CodecId(payload_name, sampling_freq_hz, channels); + if (codec_id < 0) { + // We couldn't find a matching codec, set the parameters to unacceptable + // values and return. + codec->plname[0] = '\0'; + codec->pltype = -1; + codec->pacsize = 0; + codec->rate = 0; + codec->plfreq = 0; + return -1; + } + + // Get default codec settings. + ACMCodecDB::Codec(codec_id, codec); + + // Keep the number of channels from the function call. For most codecs it + // will be the same value as in default codec settings, but not for all. + codec->channels = channels; + + return 0; +} + +// Get supported codec Index with name, frequency and number of channels. +int AudioCodingModule::Codec(const char* payload_name, + int sampling_freq_hz, + int channels) { + return ACMCodecDB::CodecId(payload_name, sampling_freq_hz, channels); +} + +// Checks the validity of the parameters of the given codec +bool AudioCodingModule::IsCodecValid(const CodecInst& codec) { + int mirror_id; + + int codec_number = ACMCodecDB::CodecNumber(codec, &mirror_id); + + if (codec_number < 0) { + WEBRTC_TRACE(webrtc::kTraceError, webrtc::kTraceAudioCoding, -1, + "Invalid codec setting"); + return false; + } else { + return true; + } +} + +AudioCodingModule* AudioCodingModuleFactory::Create(const int32_t id) const { + return NULL; +} + +AudioCodingModule* NewAudioCodingModuleFactory::Create(const int32_t id) const { + return new AudioCodingModuleImpl(id); +} + +} // namespace webrtc diff --git a/webrtc/modules/audio_coding/main/acm2/audio_coding_module.gypi b/webrtc/modules/audio_coding/main/acm2/audio_coding_module.gypi new file mode 100644 index 0000000000..8b0fbe111a --- /dev/null +++ b/webrtc/modules/audio_coding/main/acm2/audio_coding_module.gypi @@ -0,0 +1,190 @@ +# 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. + +{ + 'variables': { + 'audio_coding_dependencies': [ + 'CNG', + 'G711', + 'G722', + 'iLBC', + 'iSAC', + 'iSACFix', + 'PCM16B', + 'NetEq4', + '<(webrtc_root)/common_audio/common_audio.gyp:common_audio', + '<(webrtc_root)/system_wrappers/source/system_wrappers.gyp:system_wrappers', + ], + 'audio_coding_defines': [], + 'conditions': [ + ['include_opus==1', { + 'audio_coding_dependencies': ['webrtc_opus',], + 'audio_coding_defines': ['WEBRTC_CODEC_OPUS',], + }], + ], + }, + 'targets': [ + { + 'target_name': 'audio_coding_module', + 'type': 'static_library', + 'defines': [ + '<@(audio_coding_defines)', + ], + 'dependencies': [ + '<@(audio_coding_dependencies)', + ], + 'include_dirs': [ + '../interface', + '../../../interface', + ], + 'direct_dependent_settings': { + 'include_dirs': [ + '../interface', + '../../../interface', + ], + }, + 'sources': [ + '../interface/audio_coding_module.h', + '../interface/audio_coding_module_typedefs.h', + 'acm_amr.cc', + 'acm_amr.h', + 'acm_amrwb.cc', + 'acm_amrwb.h', + 'acm_celt.cc', + 'acm_celt.h', + 'acm_cng.cc', + 'acm_cng.h', + 'acm_codec_database.cc', + 'acm_codec_database.h', + 'acm_dtmf_playout.cc', + 'acm_dtmf_playout.h', + 'acm_g722.cc', + 'acm_g722.h', + 'acm_g7221.cc', + 'acm_g7221.h', + 'acm_g7221c.cc', + 'acm_g7221c.h', + 'acm_g729.cc', + 'acm_g729.h', + 'acm_g7291.cc', + 'acm_g7291.h', + 'acm_generic_codec.cc', + 'acm_generic_codec.h', + 'acm_gsmfr.cc', + 'acm_gsmfr.h', + 'acm_ilbc.cc', + 'acm_ilbc.h', + 'acm_isac.cc', + 'acm_isac.h', + 'acm_isac_macros.h', + 'acm_opus.cc', + 'acm_opus.h', + 'acm_speex.cc', + 'acm_speex.h', + 'acm_pcm16b.cc', + 'acm_pcm16b.h', + 'acm_pcma.cc', + 'acm_pcma.h', + 'acm_pcmu.cc', + 'acm_pcmu.h', + 'acm_red.cc', + 'acm_red.h', + 'acm_receiver.cc', + 'acm_receiver.h', + 'acm_resampler.cc', + 'acm_resampler.h', + 'audio_coding_module.cc', + 'audio_coding_module_impl.cc', + 'audio_coding_module_impl.h', + 'initial_delay_manager.cc', + 'initial_delay_manager.h', + 'nack.cc', + 'nack.h', + ], + }, + ], + 'conditions': [ + ['include_tests==1', { + 'targets': [ + { + 'target_name': 'delay_test', + 'type': 'executable', + 'dependencies': [ + 'audio_coding_module', + '<(DEPTH)/testing/gtest.gyp:gtest', + '<(webrtc_root)/test/test.gyp:test_support_main', + '<(webrtc_root)/system_wrappers/source/system_wrappers.gyp:system_wrappers', + '<(DEPTH)/third_party/gflags/gflags.gyp:gflags', + ], + 'sources': [ + '../test/delay_test.cc', + '../test/Channel.cc', + '../test/PCMFile.cc', + ], + }, # delay_test + { + # This is handy for testing codecs with different settings. I like to + # keep it while we are developing ACM 2. Not sure if we keep it + # forever, though I don't have strong reason to remove it. + 'target_name': 'codec_test', + 'type': 'executable', + 'dependencies': [ + 'audio_coding_module', + '<(DEPTH)/testing/gtest.gyp:gtest', + '<(webrtc_root)/test/test.gyp:test_support_main', + '<(webrtc_root)/system_wrappers/source/system_wrappers.gyp:system_wrappers', + '<(DEPTH)/third_party/gflags/gflags.gyp:gflags', + ], + 'sources': [ + '../test/codec_test.cc', + '../test/Channel.cc', + '../test/PCMFile.cc', + ], + }, # codec_test +# TODO(turajs): Add this target. +# { +# 'target_name': 'insert_packet_with_timing', +# 'type': 'executable', +# 'dependencies': [ +# 'audio_coding_module', +# '<(DEPTH)/testing/gtest.gyp:gtest', +# '<(webrtc_root)/test/test.gyp:test_support_main', +# '<(webrtc_root)/system_wrappers/source/system_wrappers.gyp:system_wrappers', +# '<(DEPTH)/third_party/gflags/gflags.gyp:gflags', +# ], +# 'sources': [ +# 'acm_receiver_unittest.cc', +# '../test/Channel.cc', +# '../test/PCMFile.cc', +# ], +# }, # insert_packet_with_timing + { + # TODO(turajs): This test will be included in module.gyp when ACM 2 is in + # public repository. + 'target_name': 'acm2_unittests', + 'type': 'executable', + 'defines': [ + '<@(audio_coding_defines)', + ], + 'dependencies': [ + 'audio_coding_module', + '<(DEPTH)/testing/gtest.gyp:gtest', + '<(webrtc_root)/test/test.gyp:test_support_main', + #'<(webrtc_root)/system_wrappers/source/system_wrappers.gyp:system_wrappers', + '<(DEPTH)/third_party/gflags/gflags.gyp:gflags', + ], + 'sources': [ + 'nack_unittest.cc', + 'acm_receiver_unittest.cc', + 'initial_delay_manager_unittest.cc', + ], + }, # acm2_unittests + ], + }], + ], +} diff --git a/webrtc/modules/audio_coding/main/acm2/audio_coding_module_impl.cc b/webrtc/modules/audio_coding/main/acm2/audio_coding_module_impl.cc new file mode 100644 index 0000000000..f5fac732eb --- /dev/null +++ b/webrtc/modules/audio_coding/main/acm2/audio_coding_module_impl.cc @@ -0,0 +1,1990 @@ +/* + * 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/source/audio_coding_module_impl.h" + +#include +#include +#include + +#include "webrtc/engine_configurations.h" +#include "webrtc/modules/audio_coding/main/interface/audio_coding_module_typedefs.h" +#include "webrtc/modules/audio_coding/main/source/acm_codec_database.h" +#include "webrtc/modules/audio_coding/main/source/acm_common_defs.h" +#include "webrtc/modules/audio_coding/main/source/acm_generic_codec.h" +#include "webrtc/modules/audio_coding/main/source/acm_resampler.h" +#include "webrtc/system_wrappers/interface/critical_section_wrapper.h" +#include "webrtc/system_wrappers/interface/rw_lock_wrapper.h" +#include "webrtc/system_wrappers/interface/trace.h" +#include "webrtc/typedefs.h" + +namespace webrtc { + +enum { + kACMToneEnd = 999 +}; + +// Maximum number of bytes in one packet (PCM16B, 20 ms packets, stereo). +enum { + kMaxPacketSize = 2560 +}; + +// Maximum number of payloads that can be packed in one RED payload. For +// regular FEC, we only pack two payloads. In case of dual-streaming, in worst +// case we might pack 3 payloads in one RED payload. +enum { + kNumFecFragmentationVectors = 2, + kMaxNumFragmentationVectors = 3 +}; + +// If packet N is arrived all packets prior to N - |kNackThresholdPackets| which +// are not received are considered as lost, and appear in NACK list. +enum { + kNackThresholdPackets = 2 +}; + +namespace { + +// TODO(turajs): the same functionality is used in NetEq. If both classes +// need them, make it a static function in ACMCodecDB. +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])); +} + +// Stereo-to-mono can be used as in-place. +int DownMix(const AudioFrame& frame, int length_out_buff, int16_t* out_buff) { + if (length_out_buff < frame.samples_per_channel_) { + return -1; + } + for (int n = 0; n < frame.samples_per_channel_; ++n) + out_buff[n] = (frame.data_[2 * n] + frame.data_[2 * n + 1]) >> 1; + return 0; +} + +// Mono-to-stereo can be used as in-place. +int UpMix(const AudioFrame& frame, int length_out_buff, int16_t* out_buff) { + if (length_out_buff < frame.samples_per_channel_) { + return -1; + } + for (int n = frame.samples_per_channel_ - 1; n >= 0; --n) { + out_buff[2 * n + 1] = frame.data_[n]; + out_buff[2 * n] = frame.data_[n]; + } + return 0; +} + +// Return 1 if timestamp t1 is less than timestamp t2, while compensating for +// wrap-around. +static int TimestampLessThan(uint32_t t1, uint32_t t2) { + uint32_t kHalfFullRange = static_cast(0xFFFFFFFF) / 2; + if (t1 == t2) { + return 0; + } else if (t1 < t2) { + if (t2 - t1 < kHalfFullRange) + return 1; + return 0; + } else { + if (t1 - t2 < kHalfFullRange) + return 0; + return 1; + } +} + +} // namespace + +AudioCodingModuleImpl::AudioCodingModuleImpl(int id) + : packetization_callback_(NULL), + id_(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_pltype_(255), + vad_enabled_(false), + dtx_enabled_(false), + vad_mode_(VADNormal), + stereo_send_(false), + current_send_codec_idx_(-1), + send_codec_registered_(false), + acm_crit_sect_(CriticalSectionWrapper::CreateCriticalSection()), + vad_callback_(NULL), + is_first_red_(true), + fec_enabled_(false), + last_fec_timestamp_(0), + previous_pltype_(255), + aux_rtp_header_(NULL), + receiver_initialized_(false), + callback_crit_sect_(CriticalSectionWrapper::CreateCriticalSection()), + secondary_send_codec_inst_(), + secondary_encoder_(NULL), + codec_timestamp_(expected_codec_ts_), + first_10ms_data_(false) { + + // 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; + + strncpy(secondary_send_codec_inst_.plname, no_name, + RTP_PAYLOAD_NAME_SIZE - 1); + secondary_send_codec_inst_.pltype = -1; + + for (int i = 0; i < ACMCodecDB::kMaxNumCodecs; i++) { + codecs_[i] = NULL; + mirror_codec_idx_[i] = -1; + } + + receiver_.set_id(id_); + + // Allocate memory for RED. + red_buffer_ = new uint8_t[MAX_PAYLOAD_SIZE_BYTE]; + + // TODO(turajs): This might not be exactly how this class is supposed to work. + // The external usage might be that |fragmentationVectorSize| has to match + // the allocated space for the member-arrays, while here, we allocate + // according to the maximum number of fragmentations and change + // |fragmentationVectorSize| on-the-fly based on actual number of + // fragmentations. However, due to copying to local variable before calling + // SendData, the RTP module receives a "valid" fragmentation, where allocated + // space matches |fragmentationVectorSize|, therefore, this should not cause + // any problem. A better approach is not using RTPFragmentationHeader as + // member variable, instead, use an ACM-specific structure to hold RED-related + // data. See module_common_type.h for the definition of + // RTPFragmentationHeader. + fragmentation_.VerifyAndAllocateFragmentationHeader( + kMaxNumFragmentationVectors); + + // 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)) { + red_pltype_ = static_cast(ACMCodecDB::database_[i].pltype); + } else if (IsCodecCN(i)) { + if (ACMCodecDB::database_[i].plfreq == 8000) { + cng_nb_pltype_ = static_cast(ACMCodecDB::database_[i].pltype); + } else if (ACMCodecDB::database_[i].plfreq == 16000) { + cng_wb_pltype_ = static_cast(ACMCodecDB::database_[i].pltype); + } else if (ACMCodecDB::database_[i].plfreq == 32000) { + cng_swb_pltype_ = static_cast(ACMCodecDB::database_[i].pltype); + } else if (ACMCodecDB::database_[i].plfreq == 48000) { + cng_fb_pltype_ = static_cast(ACMCodecDB::database_[i].pltype); + } + } + } + + if (InitializeReceiverSafe() < 0) { + WEBRTC_TRACE(webrtc::kTraceError, webrtc::kTraceAudioCoding, id_, + "Cannot initialize receiver"); + } + WEBRTC_TRACE(webrtc::kTraceMemory, webrtc::kTraceAudioCoding, id, "Created"); +} + +AudioCodingModuleImpl::~AudioCodingModuleImpl() { + { + CriticalSectionScoped lock(acm_crit_sect_); + current_send_codec_idx_ = -1; + + 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 (red_buffer_ != NULL) { + delete[] red_buffer_; + red_buffer_ = NULL; + } + } + + if (aux_rtp_header_ != NULL) { + delete aux_rtp_header_; + aux_rtp_header_ = NULL; + } + + delete callback_crit_sect_; + callback_crit_sect_ = NULL; + + delete acm_crit_sect_; + acm_crit_sect_ = NULL; + WEBRTC_TRACE(webrtc::kTraceMemory, webrtc::kTraceAudioCoding, id_, + "Destroyed"); +} + +int32_t AudioCodingModuleImpl::ChangeUniqueId(const int32_t id) { + { + CriticalSectionScoped lock(acm_crit_sect_); + id_ = id; + + for (int i = 0; i < ACMCodecDB::kMaxNumCodecs; i++) { + if (codecs_[i] != NULL) { + codecs_[i]->SetUniqueID(id); + } + } + } + + receiver_.set_id(id_); + return 0; +} + +// Returns the number of milliseconds until the module want a +// worker thread to call Process. +int32_t AudioCodingModuleImpl::TimeUntilNextProcess() { + CriticalSectionScoped lock(acm_crit_sect_); + + if (!HaveValidEncoder("TimeUntilNextProcess")) { + return -1; + } + return codecs_[current_send_codec_idx_]->SamplesLeftToEncode() / + (send_codec_inst_.plfreq / 1000); +} + +int32_t AudioCodingModuleImpl::Process() { + bool dual_stream; + { + CriticalSectionScoped lock(acm_crit_sect_); + dual_stream = (secondary_encoder_.get() != NULL); + } + if (dual_stream) { + return ProcessDualStream(); + } + return ProcessSingleStream(); +} + +int AudioCodingModuleImpl::EncodeFragmentation(int fragmentation_index, + int payload_type, + uint32_t current_timestamp, + ACMGenericCodec* encoder, + uint8_t* stream) { + int16_t len_bytes = MAX_PAYLOAD_SIZE_BYTE; + uint32_t rtp_timestamp; + WebRtcACMEncodingType encoding_type; + if (encoder->Encode(stream, &len_bytes, &rtp_timestamp, &encoding_type) < 0) { + return -1; + } + assert(encoding_type == kActiveNormalEncoded); + assert(len_bytes > 0); + + fragmentation_.fragmentationLength[fragmentation_index] = len_bytes; + fragmentation_.fragmentationPlType[fragmentation_index] = payload_type; + fragmentation_.fragmentationTimeDiff[fragmentation_index] = + static_cast(current_timestamp - rtp_timestamp); + fragmentation_.fragmentationVectorSize++; + return len_bytes; +} + +// Primary payloads are sent immediately, whereas a single secondary payload is +// buffered to be combined with "the next payload." +// Normally "the next payload" would be a primary payload. In case two +// consecutive secondary payloads are generated with no primary payload in +// between, then two secondary payloads are packed in one RED. +int AudioCodingModuleImpl::ProcessDualStream() { + uint8_t stream[kMaxNumFragmentationVectors * MAX_PAYLOAD_SIZE_BYTE]; + uint32_t current_timestamp; + int16_t length_bytes = 0; + RTPFragmentationHeader my_fragmentation; + + uint8_t my_red_payload_type; + + { + CriticalSectionScoped lock(acm_crit_sect_); + // Check if there is an encoder before. + if (!HaveValidEncoder("ProcessDualStream") || + secondary_encoder_.get() == NULL) { + return -1; + } + ACMGenericCodec* primary_encoder = codecs_[current_send_codec_idx_]; + // If primary encoder has a full frame of audio to generate payload. + bool primary_ready_to_encode = primary_encoder->HasFrameToEncode(); + // If the secondary encoder has a frame of audio to generate a payload. + bool secondary_ready_to_encode = secondary_encoder_->HasFrameToEncode(); + + if (!primary_ready_to_encode && !secondary_ready_to_encode) { + // Nothing to send. + return 0; + } + int len_bytes_previous_secondary = static_cast( + fragmentation_.fragmentationLength[2]); + assert(len_bytes_previous_secondary <= MAX_PAYLOAD_SIZE_BYTE); + bool has_previous_payload = len_bytes_previous_secondary > 0; + + uint32_t primary_timestamp = primary_encoder->EarliestTimestamp(); + uint32_t secondary_timestamp = secondary_encoder_->EarliestTimestamp(); + + if (!has_previous_payload && !primary_ready_to_encode && + secondary_ready_to_encode) { + // Secondary payload will be the ONLY bit-stream. Encode by secondary + // encoder, store the payload, and return. No packet is sent. + int16_t len_bytes = MAX_PAYLOAD_SIZE_BYTE; + WebRtcACMEncodingType encoding_type; + if (secondary_encoder_->Encode(red_buffer_, &len_bytes, + &last_fec_timestamp_, + &encoding_type) < 0) { + WEBRTC_TRACE(webrtc::kTraceError, webrtc::kTraceAudioCoding, id_, + "ProcessDual(): Encoding of secondary encoder Failed"); + return -1; + } + assert(len_bytes > 0); + assert(encoding_type == kActiveNormalEncoded); + assert(len_bytes <= MAX_PAYLOAD_SIZE_BYTE); + fragmentation_.fragmentationLength[2] = len_bytes; + return 0; + } + + // Initialize with invalid but different values, so later can have sanity + // check if they are different. + int index_primary = -1; + int index_secondary = -2; + int index_previous_secondary = -3; + + if (primary_ready_to_encode) { + index_primary = secondary_ready_to_encode ? + TimestampLessThan(primary_timestamp, secondary_timestamp) : 0; + index_primary += has_previous_payload ? + TimestampLessThan(primary_timestamp, last_fec_timestamp_) : 0; + } + + if (secondary_ready_to_encode) { + // Timestamp of secondary payload can only be less than primary payload, + // but is always larger than the timestamp of previous secondary payload. + index_secondary = primary_ready_to_encode ? + (1 - TimestampLessThan(primary_timestamp, secondary_timestamp)) : 0; + } + + if (has_previous_payload) { + index_previous_secondary = primary_ready_to_encode ? + (1 - TimestampLessThan(primary_timestamp, last_fec_timestamp_)) : 0; + // If secondary is ready it always have a timestamp larger than previous + // secondary. So the index is either 0 or 1. + index_previous_secondary += secondary_ready_to_encode ? 1 : 0; + } + + // Indices must not be equal. + assert(index_primary != index_secondary); + assert(index_primary != index_previous_secondary); + assert(index_secondary != index_previous_secondary); + + // One of the payloads has to be at position zero. + assert(index_primary == 0 || index_secondary == 0 || + index_previous_secondary == 0); + + // Timestamp of the RED payload. + if (index_primary == 0) { + current_timestamp = primary_timestamp; + } else if (index_secondary == 0) { + current_timestamp = secondary_timestamp; + } else { + current_timestamp = last_fec_timestamp_; + } + + fragmentation_.fragmentationVectorSize = 0; + if (has_previous_payload) { + assert(index_previous_secondary >= 0 && + index_previous_secondary < kMaxNumFragmentationVectors); + assert(len_bytes_previous_secondary <= MAX_PAYLOAD_SIZE_BYTE); + memcpy(&stream[index_previous_secondary * MAX_PAYLOAD_SIZE_BYTE], + red_buffer_, sizeof(stream[0]) * len_bytes_previous_secondary); + fragmentation_.fragmentationLength[index_previous_secondary] = + len_bytes_previous_secondary; + fragmentation_.fragmentationPlType[index_previous_secondary] = + secondary_send_codec_inst_.pltype; + fragmentation_.fragmentationTimeDiff[index_previous_secondary] = + static_cast(current_timestamp - last_fec_timestamp_); + fragmentation_.fragmentationVectorSize++; + } + + if (primary_ready_to_encode) { + assert(index_primary >= 0 && index_primary < kMaxNumFragmentationVectors); + int i = index_primary * MAX_PAYLOAD_SIZE_BYTE; + if (EncodeFragmentation(index_primary, send_codec_inst_.pltype, + current_timestamp, primary_encoder, + &stream[i]) < 0) { + WEBRTC_TRACE(webrtc::kTraceError, webrtc::kTraceAudioCoding, id_, + "ProcessDualStream(): Encoding of primary encoder Failed"); + return -1; + } + } + + if (secondary_ready_to_encode) { + assert(index_secondary >= 0 && + index_secondary < kMaxNumFragmentationVectors - 1); + int i = index_secondary * MAX_PAYLOAD_SIZE_BYTE; + if (EncodeFragmentation(index_secondary, + secondary_send_codec_inst_.pltype, + current_timestamp, secondary_encoder_.get(), + &stream[i]) < 0) { + WEBRTC_TRACE(webrtc::kTraceError, webrtc::kTraceAudioCoding, id_, + "ProcessDualStream(): Encoding of secondary encoder " + "Failed"); + return -1; + } + } + // Copy to local variable, as it will be used outside the ACM lock. + my_fragmentation.CopyFrom(fragmentation_); + my_red_payload_type = red_pltype_; + length_bytes = 0; + for (int n = 0; n < fragmentation_.fragmentationVectorSize; n++) { + length_bytes += fragmentation_.fragmentationLength[n]; + } + } + + { + CriticalSectionScoped lock(callback_crit_sect_); + if (packetization_callback_ != NULL) { + // Callback with payload data, including redundant data (FEC/RED). + if (packetization_callback_->SendData(kAudioFrameSpeech, + my_red_payload_type, + current_timestamp, stream, + length_bytes, + &my_fragmentation) < 0) { + return -1; + } + } + } + + { + CriticalSectionScoped lock(acm_crit_sect_); + // Now that data is sent, clean up fragmentation. + ResetFragmentation(0); + } + return 0; +} + +// Process any pending tasks such as timeouts. +int AudioCodingModuleImpl::ProcessSingleStream() { + // Make room for 1 RED payload. + uint8_t stream[2 * MAX_PAYLOAD_SIZE_BYTE]; + // TODO(turajs): |length_bytes| & |red_length_bytes| can be of type int if + // ACMGenericCodec::Encode() & ACMGenericCodec::GetRedPayload() allows. + int16_t length_bytes = 2 * MAX_PAYLOAD_SIZE_BYTE; + int16_t red_length_bytes = length_bytes; + uint32_t rtp_timestamp; + int status; + WebRtcACMEncodingType encoding_type; + FrameType frame_type = kAudioFrameSpeech; + uint8_t current_payload_type = 0; + bool has_data_to_send = false; + bool fec_active = false; + RTPFragmentationHeader my_fragmentation; + + // Keep the scope of the ACM critical section limited. + { + CriticalSectionScoped lock(acm_crit_sect_); + // Check if there is an encoder before. + if (!HaveValidEncoder("ProcessSingleStream")) { + return -1; + } + status = codecs_[current_send_codec_idx_]->Encode(stream, &length_bytes, + &rtp_timestamp, + &encoding_type); + if (status < 0) { + // Encode failed. + WEBRTC_TRACE(webrtc::kTraceError, webrtc::kTraceAudioCoding, id_, + "ProcessSingleStream(): Encoding Failed"); + length_bytes = 0; + return -1; + } else if (status == 0) { + // Not enough data. + return 0; + } else { + switch (encoding_type) { + case kNoEncoding: { + current_payload_type = previous_pltype_; + frame_type = kFrameEmpty; + length_bytes = 0; + break; + } + case kActiveNormalEncoded: + case kPassiveNormalEncoded: { + current_payload_type = (uint8_t)send_codec_inst_.pltype; + frame_type = kAudioFrameSpeech; + break; + } + case kPassiveDTXNB: { + current_payload_type = cng_nb_pltype_; + frame_type = kAudioFrameCN; + is_first_red_ = true; + break; + } + case kPassiveDTXWB: { + current_payload_type = cng_wb_pltype_; + frame_type = kAudioFrameCN; + is_first_red_ = true; + break; + } + case kPassiveDTXSWB: { + current_payload_type = cng_swb_pltype_; + frame_type = kAudioFrameCN; + is_first_red_ = true; + break; + } + case kPassiveDTXFB: { + current_payload_type = cng_fb_pltype_; + frame_type = kAudioFrameCN; + is_first_red_ = true; + break; + } + } + has_data_to_send = true; + previous_pltype_ = current_payload_type; + + // Redundancy encode is done here. The two bitstreams packetized into + // one RTP packet and the fragmentation points are set. + // Only apply RED on speech data. + if ((fec_enabled_) && + ((encoding_type == kActiveNormalEncoded) || + (encoding_type == kPassiveNormalEncoded))) { + // FEC is enabled within this scope. + // + // Note that, a special solution exists for iSAC since it is the only + // codec for which GetRedPayload has a non-empty implementation. + // + // Summary of the FEC scheme below (use iSAC as example): + // + // 1st (is_first_red_ is true) encoded iSAC frame (primary #1) => + // - call GetRedPayload() and store redundancy for packet #1 in + // second fragment of RED buffer (old data) + // - drop the primary iSAC frame + // - don't call SendData + // 2nd (is_first_red_ is false) encoded iSAC frame (primary #2) => + // - store primary #2 in 1st fragment of RED buffer and send the + // combined packet + // - the transmitted packet contains primary #2 (new) and + // reduncancy for packet #1 (old) + // - call GetRed_Payload() and store redundancy for packet #2 in + // second fragment of RED buffer + // + // ... + // + // Nth encoded iSAC frame (primary #N) => + // - store primary #N in 1st fragment of RED buffer and send the + // combined packet + // - the transmitted packet contains primary #N (new) and + // reduncancy for packet #(N-1) (old) + // - call GetRedPayload() and store redundancy for packet #N in + // second fragment of RED buffer + // + // For all other codecs, GetRedPayload does nothing and returns -1 => + // redundant data is only a copy. + // + // First combined packet contains : #2 (new) and #1 (old) + // Second combined packet contains: #3 (new) and #2 (old) + // Third combined packet contains : #4 (new) and #3 (old) + // + // Hence, even if every second packet is dropped, perfect + // reconstruction is possible. + fec_active = true; + + has_data_to_send = false; + // Skip the following part for the first packet in a RED session. + if (!is_first_red_) { + // Rearrange stream such that FEC packets are included. + // Replace stream now that we have stored current stream. + memcpy(stream + fragmentation_.fragmentationOffset[1], red_buffer_, + fragmentation_.fragmentationLength[1]); + // Update the fragmentation time difference vector, in number of + // timestamps. + uint16_t time_since_last = static_cast( + rtp_timestamp - last_fec_timestamp_); + + // Update fragmentation vectors. + fragmentation_.fragmentationPlType[1] = + fragmentation_.fragmentationPlType[0]; + fragmentation_.fragmentationTimeDiff[1] = time_since_last; + has_data_to_send = true; + } + + // Insert new packet length. + fragmentation_.fragmentationLength[0] = length_bytes; + + // Insert new packet payload type. + fragmentation_.fragmentationPlType[0] = current_payload_type; + last_fec_timestamp_ = rtp_timestamp; + + // Can be modified by the GetRedPayload() call if iSAC is utilized. + red_length_bytes = length_bytes; + + // A fragmentation header is provided => packetization according to + // RFC 2198 (RTP Payload for Redundant Audio Data) will be used. + // First fragment is the current data (new). + // Second fragment is the previous data (old). + length_bytes = static_cast( + fragmentation_.fragmentationLength[0] + + fragmentation_.fragmentationLength[1]); + + // Get, and store, redundant data from the encoder based on the recently + // encoded frame. + // NOTE - only iSAC contains an implementation; all other codecs does + // nothing and returns -1. + if (codecs_[current_send_codec_idx_]->GetRedPayload( + red_buffer_, &red_length_bytes) == -1) { + // The codec was not iSAC => use current encoder output as redundant + // data instead (trivial FEC scheme). + memcpy(red_buffer_, stream, red_length_bytes); + } + + is_first_red_ = false; + // Update payload type with RED payload type. + current_payload_type = red_pltype_; + // We have packed 2 payloads. + fragmentation_.fragmentationVectorSize = kNumFecFragmentationVectors; + + // Copy to local variable, as it will be used outside ACM lock. + my_fragmentation.CopyFrom(fragmentation_); + // Store RED length. + fragmentation_.fragmentationLength[1] = red_length_bytes; + } + } + } + + if (has_data_to_send) { + CriticalSectionScoped lock(callback_crit_sect_); + + if (packetization_callback_ != NULL) { + if (fec_active) { + // Callback with payload data, including redundant data (FEC/RED). + packetization_callback_->SendData(frame_type, current_payload_type, + rtp_timestamp, stream, length_bytes, + &my_fragmentation); + } else { + // Callback with payload data. + packetization_callback_->SendData(frame_type, current_payload_type, + rtp_timestamp, stream, length_bytes, + NULL); + } + } + + if (vad_callback_ != NULL) { + // Callback with VAD decision. + vad_callback_->InFrameType(((int16_t)encoding_type)); + } + } + return length_bytes; +} + +///////////////////////////////////////// +// Sender +// + +// Initialize send codec. +int AudioCodingModuleImpl::InitializeSender() { + CriticalSectionScoped lock(acm_crit_sect_); + + // Start with invalid values. + send_codec_registered_ = false; + current_send_codec_idx_ = -1; + send_codec_inst_.plname[0] = '\0'; + + // Delete all encoders to start fresh. + for (int id = 0; id < ACMCodecDB::kMaxNumCodecs; id++) { + if (codecs_[id] != NULL) { + codecs_[id]->DestructEncoder(); + } + } + + // Initialize FEC/RED. + is_first_red_ = true; + if (fec_enabled_ || secondary_encoder_.get() != NULL) { + if (red_buffer_ != NULL) { + memset(red_buffer_, 0, MAX_PAYLOAD_SIZE_BYTE); + } + if (fec_enabled_) { + ResetFragmentation(kNumFecFragmentationVectors); + } else { + ResetFragmentation(0); + } + } + + return 0; +} + +int AudioCodingModuleImpl::ResetEncoder() { + CriticalSectionScoped lock(acm_crit_sect_); + if (!HaveValidEncoder("ResetEncoder")) { + return -1; + } + return codecs_[current_send_codec_idx_]->ResetEncoder(); +} + +ACMGenericCodec* AudioCodingModuleImpl::CreateCodec(const CodecInst& codec) { + ACMGenericCodec* my_codec = NULL; + + my_codec = ACMCodecDB::CreateCodecInstance(codec); + 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; + } + my_codec->SetUniqueID(id_); + my_codec->SetNetEqDecodeLock(receiver_.DecodeLock()); + + 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; +} + +int AudioCodingModuleImpl::RegisterSecondarySendCodec( + const CodecInst& send_codec) { + CriticalSectionScoped lock(acm_crit_sect_); + if (!send_codec_registered_) { + return -1; + } + // Primary and Secondary codecs should have the same sampling rates. + if (send_codec.plfreq != send_codec_inst_.plfreq) { + return -1; + } + int mirror_id; + int codec_id = IsValidSendCodec(send_codec, false, id_, &mirror_id); + if (codec_id < 0) { + return -1; + } + ACMGenericCodec* encoder = CreateCodec(send_codec); + WebRtcACMCodecParams codec_params; + // Initialize the codec before registering. For secondary codec VAD & DTX are + // disabled. + memcpy(&(codec_params.codec_inst), &send_codec, sizeof(CodecInst)); + codec_params.enable_vad = false; + codec_params.enable_dtx = false; + codec_params.vad_mode = VADNormal; + // Force initialization. + if (encoder->InitEncoder(&codec_params, true) < 0) { + // Could not initialize, therefore cannot be registered. + delete encoder; + return -1; + } + secondary_encoder_.reset(encoder); + memcpy(&secondary_send_codec_inst_, &send_codec, sizeof(send_codec)); + + // Disable VAD & DTX. + SetVADSafe(false, false, VADNormal); + + // Cleaning. + if (red_buffer_) { + memset(red_buffer_, 0, MAX_PAYLOAD_SIZE_BYTE); + } + ResetFragmentation(0); + return 0; +} + +void AudioCodingModuleImpl::UnregisterSecondarySendCodec() { + CriticalSectionScoped lock(acm_crit_sect_); + if (secondary_encoder_.get() == NULL) { + return; + } + secondary_encoder_.reset(); + ResetFragmentation(0); +} + +int AudioCodingModuleImpl::SecondarySendCodec( + CodecInst* secondary_codec) const { + CriticalSectionScoped lock(acm_crit_sect_); + if (secondary_encoder_.get() == NULL) { + return -1; + } + memcpy(secondary_codec, &secondary_send_codec_inst_, + sizeof(secondary_send_codec_inst_)); + return 0; +} + +// 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) { + if (!send_codec_registered_) { + // This values has to be NULL if there is no codec registered. + current_send_codec_idx_ = -1; + } + 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. + red_pltype_ = static_cast(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(send_codec.pltype); + break; + } + case 16000: { + cng_wb_pltype_ = static_cast(send_codec.pltype); + break; + } + case 32000: { + cng_swb_pltype_ = static_cast(send_codec.pltype); + break; + } + case 48000: { + cng_fb_pltype_ = static_cast(send_codec.pltype); + break; + } + default: { + WEBRTC_TRACE(webrtc::kTraceError, webrtc::kTraceAudioCoding, id_, + "RegisterSendCodec() failed, invalid frequency for CNG " + "registration"); + return -1; + } + } + 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 (send_codec_registered_) { + 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 there is secondary codec registered and the new send codec has a + // sampling rate different than that of secondary codec, then unregister the + // secondary codec. + if (secondary_encoder_.get() != NULL && + secondary_send_codec_inst_.plfreq != send_codec.plfreq) { + secondary_encoder_.reset(); + ResetFragmentation(0); + } + + // 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]; + int status; + 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. + status = codec_ptr->InitEncoder(&codec_params, true); + + // Check if VAD was turned on, or if error is reported. + if (status == 1) { + vad_enabled_ = true; + } else if (status < 0) { + // Could not initialize the encoder. + + // Check if already have a registered codec. + // Depending on that different messages are logged. + if (!send_codec_registered_) { + current_send_codec_idx_ = -1; + 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; + } + + // Everything is fine so we can replace the previous codec with this one. + if (send_codec_registered_) { + // If we change codec we start fresh with FEC. + // This is not strictly required by the standard. + is_first_red_ = true; + + if (codec_ptr->SetVAD(dtx_enabled_, vad_enabled_, vad_mode_) < 0) { + // SetVAD failed. + vad_enabled_ = false; + dtx_enabled_ = false; + } + } + + current_send_codec_idx_ = codec_id; + send_codec_registered_ = true; + memcpy(&send_codec_inst_, &send_codec, sizeof(CodecInst)); + previous_pltype_ = send_codec_inst_.pltype; + 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 sampling frequency is changed we have to start fresh with RED. + is_first_red_ = 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 (codecs_[current_send_codec_idx_]->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; + } + previous_pltype_ = send_codec_inst_.pltype; + + return 0; + } +} + +// Get current send codec. +int AudioCodingModuleImpl::SendCodec( + CodecInst* current_codec) const { + WEBRTC_TRACE(webrtc::kTraceStream, webrtc::kTraceAudioCoding, id_, + "SendCodec()"); + CriticalSectionScoped lock(acm_crit_sect_); + + if (!send_codec_registered_) { + WEBRTC_TRACE(webrtc::kTraceStream, webrtc::kTraceAudioCoding, id_, + "SendCodec Failed, no codec is registered"); + return -1; + } + WebRtcACMCodecParams encoder_param; + codecs_[current_send_codec_idx_]->EncoderParams(&encoder_param); + encoder_param.codec_inst.pltype = send_codec_inst_.pltype; + memcpy(current_codec, &(encoder_param.codec_inst), sizeof(CodecInst)); + + return 0; +} + +// Get current send frequency. +int AudioCodingModuleImpl::SendFrequency() const { + WEBRTC_TRACE(webrtc::kTraceStream, webrtc::kTraceAudioCoding, id_, + "SendFrequency()"); + CriticalSectionScoped lock(acm_crit_sect_); + + if (!send_codec_registered_) { + WEBRTC_TRACE(webrtc::kTraceStream, webrtc::kTraceAudioCoding, id_, + "SendFrequency Failed, no codec is registered"); + return -1; + } + + return send_codec_inst_.plfreq; +} + +// Get encode bitrate. +// Adaptive rate codecs return their current encode target rate, while other +// codecs return there longterm avarage or their fixed rate. +int AudioCodingModuleImpl::SendBitrate() const { + CriticalSectionScoped lock(acm_crit_sect_); + + if (!send_codec_registered_) { + WEBRTC_TRACE(webrtc::kTraceStream, webrtc::kTraceAudioCoding, id_, + "SendBitrate Failed, no codec is registered"); + return -1; + } + + WebRtcACMCodecParams encoder_param; + codecs_[current_send_codec_idx_]->EncoderParams(&encoder_param); + + return encoder_param.codec_inst.rate; +} + +// Set available bandwidth, inform the encoder about the estimated bandwidth +// received from the remote party. +int AudioCodingModuleImpl::SetReceivedEstimatedBandwidth(int bw) { + return codecs_[current_send_codec_idx_]->SetEstimatedBandwidth(bw); +} + +// Register a transport callback which will be called to deliver +// the encoded buffers. +int AudioCodingModuleImpl::RegisterTransportCallback( + AudioPacketizationCallback* transport) { + CriticalSectionScoped lock(callback_crit_sect_); + packetization_callback_ = transport; + return 0; +} + +// Add 10MS of raw (PCM) audio data to the encoder. +int AudioCodingModuleImpl::Add10MsData( + const AudioFrame& audio_frame) { + if (audio_frame.samples_per_channel_ <= 0) { + assert(false); + WEBRTC_TRACE(webrtc::kTraceError, webrtc::kTraceAudioCoding, id_, + "Cannot Add 10 ms audio, payload length is negative or " + "zero"); + return -1; + } + + // Allow for 8, 16, 32 and 48kHz input audio. + if ((audio_frame.sample_rate_hz_ != 8000) + && (audio_frame.sample_rate_hz_ != 16000) + && (audio_frame.sample_rate_hz_ != 32000) + && (audio_frame.sample_rate_hz_ != 48000)) { + assert(false); + WEBRTC_TRACE(webrtc::kTraceError, webrtc::kTraceAudioCoding, id_, + "Cannot Add 10 ms audio, input frequency not valid"); + return -1; + } + + // If the length and frequency matches. We currently just support raw PCM. + if ((audio_frame.sample_rate_hz_ / 100) + != audio_frame.samples_per_channel_) { + WEBRTC_TRACE(webrtc::kTraceError, webrtc::kTraceAudioCoding, id_, + "Cannot Add 10 ms audio, input frequency and length doesn't" + " match"); + return -1; + } + + if (audio_frame.num_channels_ != 1 && audio_frame.num_channels_ != 2) { + WEBRTC_TRACE(webrtc::kTraceError, webrtc::kTraceAudioCoding, id_, + "Cannot Add 10 ms audio, invalid number of channels."); + return -1; + } + + CriticalSectionScoped lock(acm_crit_sect_); + // Do we have a codec registered? + if (!HaveValidEncoder("Add10MsData")) { + return -1; + } + + const AudioFrame* ptr_frame; + // Perform a resampling, also down-mix if it is required and can be + // performed before resampling (a down mix prior to resampling will take + // place if both primary and secondary encoders are mono and input is in + // stereo). + if (PreprocessToAddData(audio_frame, &ptr_frame) < 0) { + return -1; + } + + // Check whether we need an up-mix or down-mix? + bool remix = ptr_frame->num_channels_ != send_codec_inst_.channels; + if (secondary_encoder_.get() != NULL) { + remix = remix || + (ptr_frame->num_channels_ != secondary_send_codec_inst_.channels); + } + + // If a re-mix is required (up or down), this buffer will store re-mixed + // version of the input. + int16_t buffer[WEBRTC_10MS_PCM_AUDIO]; + if (remix) { + if (ptr_frame->num_channels_ == 1) { + if (UpMix(*ptr_frame, WEBRTC_10MS_PCM_AUDIO, buffer) < 0) + return -1; + } else { + if (DownMix(*ptr_frame, WEBRTC_10MS_PCM_AUDIO, buffer) < 0) + return -1; + } + } + + // When adding data to encoders this pointer is pointing to an audio buffer + // with correct number of channels. + 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_) + ptr_audio = buffer; + + if (codecs_[current_send_codec_idx_]->Add10MsData( + ptr_frame->timestamp_, ptr_audio, ptr_frame->samples_per_channel_, + send_codec_inst_.channels) < 0) + return -1; + + if (secondary_encoder_.get() != NULL) { + // For pushing data to secondary, point the |ptr_audio| to correct buffer. + ptr_audio = ptr_frame->data_; + if (secondary_send_codec_inst_.channels != ptr_frame->num_channels_) + ptr_audio = buffer; + + if (secondary_encoder_->Add10MsData( + ptr_frame->timestamp_, ptr_audio, ptr_frame->samples_per_channel_, + secondary_send_codec_inst_.channels) < 0) + return -1; + } + + return 0; +} + +// Perform a resampling and down-mix if required. We down-mix only if +// encoder is mono and input is stereo. In case of dual-streaming, both +// encoders has to be mono for down-mix to take place. +// |*ptr_out| will point to the pre-processed audio-frame. If no pre-processing +// is required, |*ptr_out| points to |in_frame|. +int AudioCodingModuleImpl::PreprocessToAddData(const AudioFrame& in_frame, + const AudioFrame** ptr_out) { + // Primary and secondary (if exists) should have the same sampling rate. + assert((secondary_encoder_.get() != NULL) ? + secondary_send_codec_inst_.plfreq == send_codec_inst_.plfreq : true); + + bool resample = (in_frame.sample_rate_hz_ != send_codec_inst_.plfreq); + + // This variable is true if primary codec and secondary codec (if exists) + // are both mono and input is stereo. + bool down_mix; + if (secondary_encoder_.get() != NULL) { + down_mix = (in_frame.num_channels_ == 2) && + (send_codec_inst_.channels == 1) && + (secondary_send_codec_inst_.channels == 1); + } else { + down_mix = (in_frame.num_channels_ == 2) && + (send_codec_inst_.channels == 1); + } + + if (!first_10ms_data_) { + expected_in_ts_ = in_frame.timestamp_; + expected_codec_ts_ = in_frame.timestamp_; + 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((static_cast(send_codec_inst_.plfreq) / + static_cast(in_frame.sample_rate_hz_))); + expected_in_ts_ = in_frame.timestamp_; + } + + + if (!down_mix && !resample) { + // No pre-processing is required. + expected_in_ts_ += in_frame.samples_per_channel_; + expected_codec_ts_ += in_frame.samples_per_channel_; + *ptr_out = &in_frame; + return 0; + } + + *ptr_out = &preprocess_frame_; + preprocess_frame_.num_channels_ = in_frame.num_channels_; + int16_t audio[WEBRTC_10MS_PCM_AUDIO]; + const int16_t* src_ptr_audio = in_frame.data_; + int16_t* dest_ptr_audio = preprocess_frame_.data_; + if (down_mix) { + // If a resampling is required the output of a down-mix is written into a + // local buffer, otherwise, it will be written to the output frame. + if (resample) + dest_ptr_audio = audio; + if (DownMix(in_frame, WEBRTC_10MS_PCM_AUDIO, dest_ptr_audio) < 0) + return -1; + preprocess_frame_.num_channels_ = 1; + // Set the input of the resampler is the down-mixed signal. + src_ptr_audio = audio; + } + + preprocess_frame_.timestamp_ = expected_codec_ts_; + preprocess_frame_.samples_per_channel_ = in_frame.samples_per_channel_; + preprocess_frame_.sample_rate_hz_ = in_frame.sample_rate_hz_; + // If it is required, we have to do a resampling. + if (resample) { + // 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_, dest_ptr_audio); + + if (preprocess_frame_.samples_per_channel_ < 0) { + WEBRTC_TRACE(webrtc::kTraceError, webrtc::kTraceAudioCoding, id_, + "Cannot add 10 ms audio, resmapling failed"); + return -1; + } + preprocess_frame_.sample_rate_hz_ = send_codec_inst_.plfreq; + } + + expected_codec_ts_ += preprocess_frame_.samples_per_channel_; + expected_in_ts_ += in_frame.samples_per_channel_; + + return 0; +} + +///////////////////////////////////////// +// (FEC) Forward Error Correction +// + +bool AudioCodingModuleImpl::FECStatus() const { + CriticalSectionScoped lock(acm_crit_sect_); + return fec_enabled_; +} + +// Configure FEC status i.e on/off. +int AudioCodingModuleImpl::SetFECStatus( +#ifdef WEBRTC_CODEC_RED + bool enable_fec) { + CriticalSectionScoped lock(acm_crit_sect_); + + if (fec_enabled_ != enable_fec) { + // Reset the RED buffer. + memset(red_buffer_, 0, MAX_PAYLOAD_SIZE_BYTE); + + // Reset fragmentation buffers. + ResetFragmentation(kNumFecFragmentationVectors); + // Set fec_enabled_. + fec_enabled_ = enable_fec; + } + is_first_red_ = true; // Make sure we restart FEC. + return 0; +#else + bool /* enable_fec */) { + fec_enabled_ = false; + WEBRTC_TRACE(webrtc::kTraceWarning, webrtc::kTraceAudioCoding, id_, + " WEBRTC_CODEC_RED is undefined => fec_enabled_ = %d", + fec_enabled_); + return -1; +#endif +} + +///////////////////////////////////////// +// (VAD) Voice Activity Detection +// +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"); + return -1; + } + + // We don't support VAD/DTX when dual-streaming is enabled, i.e. + // secondary-encoder is registered. + if ((enable_dtx || enable_vad) && secondary_encoder_.get() != NULL) { + WEBRTC_TRACE(webrtc::kTraceError, webrtc::kTraceAudioCoding, id_, + "VAD/DTX not supported when dual-streaming is enabled."); + return -1; + } + + // If a send codec is registered, set VAD/DTX for the codec. + if (HaveValidEncoder("SetVAD")) { + int status = codecs_[current_send_codec_idx_]->SetVAD(enable_dtx, + enable_vad, + mode); + if (status == 1) { + // Vad was enabled. + vad_enabled_ = true; + dtx_enabled_ = enable_dtx; + vad_mode_ = mode; + + return 0; + } else if (status < 0) { + // SetVAD failed. + WEBRTC_TRACE(webrtc::kTraceError, webrtc::kTraceAudioCoding, id_, + "SetVAD failed"); + + vad_enabled_ = false; + dtx_enabled_ = false; + + return -1; + } + } + + vad_enabled_ = enable_vad; + dtx_enabled_ = enable_dtx; + vad_mode_ = mode; + + return 0; +} + +// 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_; + + return 0; +} + +///////////////////////////////////////// +// Receiver +// + +int AudioCodingModuleImpl::InitializeReceiver() { + CriticalSectionScoped lock(acm_crit_sect_); + return InitializeReceiverSafe(); +} + +// Initialize receiver, resets codec database etc. +int AudioCodingModuleImpl::InitializeReceiverSafe() { + // If the receiver is already initialized then we want to destroy any + // existing decoders. After a call to this function, we should have a clean + // start-up. + if (receiver_initialized_) { + if (receiver_.RemoveAllCodecs() < 0) + return -1; + } + receiver_.set_id(id_); + receiver_.ResetInitialDelay(); + receiver_.SetMinimumDelay(0); + receiver_.SetMaximumDelay(0); + receiver_.FlushBuffers(); + + // Register RED and CN. + for (int i = 0; i < ACMCodecDB::kNumCodecs; i++) { + if (IsCodecRED(i) || IsCodecCN(i)) { + uint8_t pl_type = static_cast(ACMCodecDB::database_[i].pltype); + if (receiver_.AddCodec(i, pl_type, 1, NULL) < 0) { + WEBRTC_TRACE(webrtc::kTraceError, webrtc::kTraceAudioCoding, id_, + "Cannot register master codec."); + return -1; + } + } + } + receiver_initialized_ = true; + return 0; +} + +// TODO(turajs): If NetEq opens an API for reseting the state of decoders then +// implement this method. Otherwise it should be removed. I might be that by +// removing and registering a decoder we can achieve the effect of resetting. +// Reset the decoder state. +int AudioCodingModuleImpl::ResetDecoder() { + CriticalSectionScoped lock(acm_crit_sect_); + return -1; +} + +// Get current receive frequency. +int AudioCodingModuleImpl::ReceiveFrequency() const { + WEBRTC_TRACE(webrtc::kTraceStream, webrtc::kTraceAudioCoding, id_, + "ReceiveFrequency()"); + + CriticalSectionScoped lock(acm_crit_sect_); + + int codec_id = receiver_.last_audio_codec_id(); + + int sample_rate_hz; + if (codec_id < 0) + sample_rate_hz = receiver_.current_sample_rate_hz(); + else + sample_rate_hz = ACMCodecDB::database_[codec_id].plfreq; + + // TODO(tlegrand): Remove this option when we have full 48 kHz support. + return (sample_rate_hz > 32000) ? 32000 : sample_rate_hz; +} + +// Get current playout frequency. +int AudioCodingModuleImpl::PlayoutFrequency() const { + WEBRTC_TRACE(webrtc::kTraceStream, webrtc::kTraceAudioCoding, id_, + "PlayoutFrequency()"); + + CriticalSectionScoped lock(acm_crit_sect_); + + return receiver_.current_sample_rate_hz(); +} + +// Register possible receive codecs, can be called multiple times, +// 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(codec.pltype); + return receiver_.AddCodec(codec_id, payload_type, codec.channels, decoder); +} + +// Get current received codec. +int AudioCodingModuleImpl::ReceiveCodec(CodecInst* current_codec) const { + return receiver_.LastAudioCodec(current_codec); +} + +// Incoming packet from network parsed and ready for decode. +int AudioCodingModuleImpl::IncomingPacket(const uint8_t* incoming_payload, + const int payload_length, + const WebRtcRTPHeader& rtp_header) { + if (payload_length < 0) { + WEBRTC_TRACE(webrtc::kTraceError, webrtc::kTraceAudioCoding, id_, + "IncomingPacket() Error, payload-length cannot be negative"); + return -1; + } + int last_audio_pltype = receiver_.last_audio_payload_type(); + if (receiver_.InsertPacket(rtp_header, incoming_payload, payload_length) < + 0) { + return -1; + } + if (receiver_.last_audio_payload_type() != last_audio_pltype) { + int index = receiver_.last_audio_codec_id(); + assert(index >= 0); + CriticalSectionScoped lock(acm_crit_sect_); + + // |codec_[index]| might not be even created, simply because it is not + // yet registered as send codec. Even if it is registered, unless the + // codec shares same instance for encoder and decoder, this call is + // useless. + if (codecs_[index] != NULL) + codecs_[index]->UpdateDecoderSampFreq(index); + } + return 0; +} + +// Minimum playout delay (Used for lip-sync). +int AudioCodingModuleImpl::SetMinimumPlayoutDelay(int time_ms) { + if ((time_ms < 0) || (time_ms > 10000)) { + WEBRTC_TRACE(webrtc::kTraceError, webrtc::kTraceAudioCoding, id_, + "Delay must be in the range of 0-1000 milliseconds."); + return -1; + } + return receiver_.SetMinimumDelay(time_ms); +} + +int AudioCodingModuleImpl::SetMaximumPlayoutDelay(int time_ms) { + if ((time_ms < 0) || (time_ms > 10000)) { + WEBRTC_TRACE(webrtc::kTraceError, webrtc::kTraceAudioCoding, id_, + "Delay must be in the range of 0-1000 milliseconds."); + return -1; + } + return receiver_.SetMaximumDelay(time_ms); +} + +// Estimate the Bandwidth based on the incoming stream, needed for one way +// audio where the RTCP send the BW estimate. +// This is also done in the RTP module. +int AudioCodingModuleImpl::DecoderEstimatedBandwidth() const { + // We can estimate far-end to near-end bandwidth if the iSAC are sent. Check + // if the last received packets were iSAC packet then retrieve the bandwidth. + int last_audio_codec_id = receiver_.last_audio_codec_id(); + if (last_audio_codec_id >= 0 && + STR_CASE_CMP("ISAC", ACMCodecDB::database_[last_audio_codec_id].plname)) { + CriticalSectionScoped lock(acm_crit_sect_); + return codecs_[last_audio_codec_id]->GetEstimatedBandwidth(); + } + return -1; +} + +// Set playout mode for: voice, fax, streaming or off. +int AudioCodingModuleImpl::SetPlayoutMode(AudioPlayoutMode mode) { + receiver_.SetPlayoutMode(mode); + return 0; // TODO(turajs): return value is for backward compatibility. +} + +// Get playout mode voice, fax, streaming or off. +AudioPlayoutMode AudioCodingModuleImpl::PlayoutMode() const { + return receiver_.PlayoutMode(); +} + +// Get 10 milliseconds of raw audio data to play out. +// Automatic resample to the requested frequency. +int AudioCodingModuleImpl::PlayoutData10Ms(int desired_freq_hz, + AudioFrame* audio_frame) { + // GetAudio always returns 10 ms, at the requested sample rate. + if (receiver_.GetAudio(desired_freq_hz, audio_frame) != 0) { + WEBRTC_TRACE(webrtc::kTraceError, webrtc::kTraceAudioCoding, id_, + "PlayoutData failed, RecOut Failed"); + return -1; + } + + audio_frame->id_ = id_; + audio_frame->energy_ = 0; + audio_frame->timestamp_ = 0; + return 0; +} + +///////////////////////////////////////// +// Statistics +// + +// TODO(turajs) change the return value to void. Also change the corresponding +// NetEq function. +int AudioCodingModuleImpl::NetworkStatistics(ACMNetworkStatistics* statistics) { + receiver_.NetworkStatistics(statistics); + return 0; +} + +void AudioCodingModuleImpl::DestructEncoderInst(void* inst) { + CriticalSectionScoped lock(acm_crit_sect_); + WEBRTC_TRACE(webrtc::kTraceDebug, webrtc::kTraceAudioCoding, id_, + "DestructEncoderInst()"); + if (!HaveValidEncoder("DestructEncoderInst")) + return; + codecs_[current_send_codec_idx_]->DestructEncoderInst(inst); +} + +int AudioCodingModuleImpl::RegisterVADCallback(ACMVADCallback* vad_callback) { + WEBRTC_TRACE(webrtc::kTraceDebug, webrtc::kTraceAudioCoding, id_, + "RegisterVADCallback()"); + CriticalSectionScoped lock(callback_crit_sect_); + vad_callback_ = vad_callback; + return 0; +} + +// TODO(tlegrand): Modify this function to work for stereo, and add tests. +int AudioCodingModuleImpl::IncomingPayload(const uint8_t* incoming_payload, + int payload_length, + uint8_t payload_type, + uint32_t timestamp) { + if (payload_length < 0) { + // Log error in trace file. + WEBRTC_TRACE(webrtc::kTraceError, webrtc::kTraceAudioCoding, id_, + "IncomingPacket() Error, payload-length cannot be negative"); + return -1; + } + + // We are not acquiring any lock when interacting with |aux_rtp_header_| no + // other method uses this member variable. + if (aux_rtp_header_ == NULL) { + // This is the first time that we are using |dummy_rtp_header_| + // so we have to create it. + aux_rtp_header_ = new WebRtcRTPHeader; + aux_rtp_header_->header.payloadType = payload_type; + // Don't matter in this case. + aux_rtp_header_->header.ssrc = 0; + aux_rtp_header_->header.markerBit = false; + // Start with random numbers. + aux_rtp_header_->header.sequenceNumber = 0x1234; // Arbitrary. + aux_rtp_header_->type.Audio.channel = 1; + } + + IncomingPacket(incoming_payload, payload_length, *aux_rtp_header_); + // Get ready for the next payload. + aux_rtp_header_->header.sequenceNumber++; + return 0; +} + +int AudioCodingModuleImpl::ReplaceInternalDTXWithWebRtc(bool use_webrtc_dtx) { + CriticalSectionScoped lock(acm_crit_sect_); + + if (!HaveValidEncoder("ReplaceInternalDTXWithWebRtc")) { + WEBRTC_TRACE( + webrtc::kTraceError, webrtc::kTraceAudioCoding, id_, + "Cannot replace codec internal DTX when no send codec is registered."); + return -1; + } + + int res = codecs_[current_send_codec_idx_]->ReplaceInternalDTX( + use_webrtc_dtx); + // Check if VAD is turned on, or if there is any error. + if (res == 1) { + vad_enabled_ = true; + } else if (res < 0) { + WEBRTC_TRACE(webrtc::kTraceError, webrtc::kTraceAudioCoding, id_, + "Failed to set ReplaceInternalDTXWithWebRtc(%d)", + use_webrtc_dtx); + return res; + } + + return 0; +} + +int AudioCodingModuleImpl::IsInternalDTXReplacedWithWebRtc( + bool* uses_webrtc_dtx) { + CriticalSectionScoped lock(acm_crit_sect_); + + if (!HaveValidEncoder("IsInternalDTXReplacedWithWebRtc")) { + return -1; + } + if (codecs_[current_send_codec_idx_]->IsInternalDTXReplaced(uses_webrtc_dtx) + < 0) { + return -1; + } + return 0; +} + +int AudioCodingModuleImpl::SetISACMaxRate(int max_bit_per_sec) { + CriticalSectionScoped lock(acm_crit_sect_); + + if (!HaveValidEncoder("SetISACMaxRate")) { + return -1; + } + + return codecs_[current_send_codec_idx_]->SetISACMaxRate(max_bit_per_sec); +} + +int AudioCodingModuleImpl::SetISACMaxPayloadSize(int max_size_bytes) { + CriticalSectionScoped lock(acm_crit_sect_); + + if (!HaveValidEncoder("SetISACMaxPayloadSize")) { + return -1; + } + + return codecs_[current_send_codec_idx_]->SetISACMaxPayloadSize( + max_size_bytes); +} + +int AudioCodingModuleImpl::ConfigISACBandwidthEstimator( + int frame_size_ms, + int rate_bit_per_sec, + bool enforce_frame_size) { + CriticalSectionScoped lock(acm_crit_sect_); + + if (!HaveValidEncoder("ConfigISACBandwidthEstimator")) { + return -1; + } + + return codecs_[current_send_codec_idx_]->ConfigISACBandwidthEstimator( + frame_size_ms, rate_bit_per_sec, enforce_frame_size); +} + +int AudioCodingModuleImpl::PlayoutTimestamp(uint32_t* timestamp) { + *timestamp = receiver_.PlayoutTimestamp(); + return 0; +} + +bool AudioCodingModuleImpl::HaveValidEncoder(const char* caller_name) const { + if ((!send_codec_registered_) || (current_send_codec_idx_ < 0) || + (current_send_codec_idx_ >= ACMCodecDB::kNumCodecs)) { + WEBRTC_TRACE(webrtc::kTraceError, webrtc::kTraceAudioCoding, id_, + "%s failed: No send codec is registered.", caller_name); + return false; + } + if ((current_send_codec_idx_ < 0) || + (current_send_codec_idx_ >= ACMCodecDB::kNumCodecs)) { + WEBRTC_TRACE(webrtc::kTraceError, webrtc::kTraceAudioCoding, id_, + "%s failed: Send codec index out of range.", caller_name); + return false; + } + if (codecs_[current_send_codec_idx_] == NULL) { + WEBRTC_TRACE(webrtc::kTraceError, webrtc::kTraceAudioCoding, id_, + "%s failed: Send codec is NULL pointer.", caller_name); + return false; + } + return true; +} + +int AudioCodingModuleImpl::UnregisterReceiveCodec(uint8_t payload_type) { + return receiver_.RemoveCodec(payload_type); +} + +// TODO(turajs): correct the type of |length_bytes| when it is corrected in +// GenericCodec. +int AudioCodingModuleImpl::REDPayloadISAC(int isac_rate, + int isac_bw_estimate, + uint8_t* payload, + int16_t* length_bytes) { + if (!HaveValidEncoder("EncodeData")) { + return -1; + } + int status; + status = codecs_[current_send_codec_idx_]->REDPayloadISAC(isac_rate, + isac_bw_estimate, + payload, + length_bytes); + return status; +} + +void AudioCodingModuleImpl::ResetFragmentation(int vector_size) { + for (int n = 0; n < kMaxNumFragmentationVectors; n++) { + fragmentation_.fragmentationOffset[n] = n * MAX_PAYLOAD_SIZE_BYTE; + } + memset(fragmentation_.fragmentationLength, 0, kMaxNumFragmentationVectors * + sizeof(fragmentation_.fragmentationLength[0])); + memset(fragmentation_.fragmentationTimeDiff, 0, kMaxNumFragmentationVectors * + sizeof(fragmentation_.fragmentationTimeDiff[0])); + memset(fragmentation_.fragmentationPlType, + 0, + kMaxNumFragmentationVectors * + sizeof(fragmentation_.fragmentationPlType[0])); + fragmentation_.fragmentationVectorSize = static_cast(vector_size); +} + +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(codec_id); + if (!*decoder) { + assert(false); + return -1; + } + } else { + *decoder = NULL; + } + + return 0; +} + +int AudioCodingModuleImpl::SetInitialPlayoutDelay(int delay_ms) { + { + CriticalSectionScoped lock(acm_crit_sect_); + // Initialize receiver, if it is not initialized. Otherwise, initial delay + // is reset upon initialization of the receiver. + if (!receiver_initialized_) + InitializeReceiverSafe(); + } + return receiver_.SetInitialDelay(delay_ms); +} + +int AudioCodingModuleImpl::EnableNack(size_t max_nack_list_size) { + return receiver_.EnableNack(max_nack_list_size); +} + +void AudioCodingModuleImpl::DisableNack() { + receiver_.DisableNack(); +} + +std::vector AudioCodingModuleImpl::GetNackList( + int round_trip_time_ms) const { + return receiver_.GetNackList(round_trip_time_ms); +} + +int AudioCodingModuleImpl::LeastRequiredDelayMs() const { + return receiver_.LeastRequiredDelayMs(); +} + +} // namespace webrtc diff --git a/webrtc/modules/audio_coding/main/acm2/audio_coding_module_impl.h b/webrtc/modules/audio_coding/main/acm2/audio_coding_module_impl.h new file mode 100644 index 0000000000..b9c70e9bb9 --- /dev/null +++ b/webrtc/modules/audio_coding/main/acm2/audio_coding_module_impl.h @@ -0,0 +1,354 @@ +/* + * 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. + */ + +#ifndef WEBRTC_MODULES_AUDIO_CODING_MAIN_SOURCE_AUDIO_CODING_MODULE_IMPL_H_ +#define WEBRTC_MODULES_AUDIO_CODING_MAIN_SOURCE_AUDIO_CODING_MODULE_IMPL_H_ + +#include + +#include "webrtc/common_types.h" +#include "webrtc/engine_configurations.h" +#include "webrtc/modules/audio_coding/main/source/acm_codec_database.h" +#include "webrtc/modules/audio_coding/main/source/acm_receiver.h" +#include "webrtc/modules/audio_coding/main/source/acm_resampler.h" +#include "webrtc/system_wrappers/interface/scoped_ptr.h" + +namespace webrtc { + +class ACMDTMFDetection; +class ACMGenericCodec; +class CriticalSectionWrapper; +class RWLockWrapper; + +class AudioCodingModuleImpl : public AudioCodingModule { + public: + // Constructor + explicit AudioCodingModuleImpl(int id); + + // Destructor + ~AudioCodingModuleImpl(); + + // Change the unique identifier of this object. + virtual int32_t ChangeUniqueId(const int32_t id); + + // Returns the number of milliseconds until the module want a worker thread + // to call Process. + int32_t TimeUntilNextProcess(); + + // Process any pending tasks such as timeouts. + int32_t Process(); + + ///////////////////////////////////////// + // Sender + // + + // Initialize send codec. + int InitializeSender(); + + // Reset send codec. + int ResetEncoder(); + + // Can be called multiple times for Codec, CNG, RED. + int RegisterSendCodec(const CodecInst& send_codec); + + // Register Secondary codec for dual-streaming. Dual-streaming is activated + // right after the secondary codec is registered. + int RegisterSecondarySendCodec(const CodecInst& send_codec); + + // Unregister the secondary codec. Dual-streaming is deactivated right after + // deregistering secondary codec. + void UnregisterSecondarySendCodec(); + + // Get the secondary codec. + int SecondarySendCodec(CodecInst* secondary_codec) const; + + // Get current send codec. + int SendCodec(CodecInst* current_codec) const; + + // Get current send frequency. + int SendFrequency() const; + + // Get encode bit-rate. + // Adaptive rate codecs return their current encode target rate, while other + // codecs return there long-term average or their fixed rate. + int SendBitrate() const; + + // Set available bandwidth, inform the encoder about the + // estimated bandwidth received from the remote party. + virtual int SetReceivedEstimatedBandwidth(int bw); + + // Register a transport callback which will be + // called to deliver the encoded buffers. + int RegisterTransportCallback(AudioPacketizationCallback* transport); + + // Add 10 ms of raw (PCM) audio data to the encoder. + int Add10MsData(const AudioFrame& audio_frame); + + ///////////////////////////////////////// + // (FEC) Forward Error Correction + // + + // Configure FEC status i.e on/off. + int SetFECStatus(bool enable_fec); + + // Get FEC status. + bool FECStatus() const; + + ///////////////////////////////////////// + // (VAD) Voice Activity Detection + // and + // (CNG) Comfort Noise Generation + // + + int SetVAD(bool enable_dtx = true, + bool enable_vad = false, + ACMVADMode mode = VADNormal); + + int VAD(bool* dtx_enabled, bool* vad_enabled, ACMVADMode* mode) const; + + int RegisterVADCallback(ACMVADCallback* vad_callback); + + ///////////////////////////////////////// + // Receiver + // + + // Initialize receiver, resets codec database etc. + int InitializeReceiver(); + + // Reset the decoder state. + int ResetDecoder(); + + // Get current receive frequency. + int ReceiveFrequency() const; + + // Get current playout frequency. + int PlayoutFrequency() const; + + // Register possible receive codecs, can be called multiple times, + // for codecs, CNG, DTMF, RED. + int RegisterReceiveCodec(const CodecInst& receive_codec); + + // Get current received codec. + int ReceiveCodec(CodecInst* current_codec) const; + + // Incoming packet from network parsed and ready for decode. + int IncomingPacket(const uint8_t* incoming_payload, + int payload_length, + const WebRtcRTPHeader& rtp_info); + + // Incoming payloads, without rtp-info, the rtp-info will be created in ACM. + // One usage for this API is when pre-encoded files are pushed in ACM. + int IncomingPayload(const uint8_t* incoming_payload, + int payload_length, + uint8_t payload_type, + uint32_t timestamp); + + // Minimum playout delay. + int SetMinimumPlayoutDelay(int time_ms); + + // Maximum playout delay. + int SetMaximumPlayoutDelay(int time_ms); + + // Smallest latency NetEq will maintain. + int LeastRequiredDelayMs() const; + + // Impose an initial delay on playout. ACM plays silence until |delay_ms| + // audio is accumulated in NetEq buffer, then starts decoding payloads. + int SetInitialPlayoutDelay(int delay_ms); + + // TODO(turajs): DTMF playout is always activated in NetEq these APIs should + // be removed, as well as all VoE related APIs and methods. + // + // Configure Dtmf playout status i.e on/off playout the incoming outband Dtmf + // tone. + int SetDtmfPlayoutStatus(bool enable) { return 0; } + + // Get Dtmf playout status. + bool DtmfPlayoutStatus() const { return true; } + + // Estimate the Bandwidth based on the incoming stream, needed + // for one way audio where the RTCP send the BW estimate. + // This is also done in the RTP module . + int DecoderEstimatedBandwidth() const; + + // Set playout mode voice, fax. + int SetPlayoutMode(AudioPlayoutMode mode); + + // Get playout mode voice, fax. + AudioPlayoutMode PlayoutMode() const; + + // Get playout timestamp. + int PlayoutTimestamp(uint32_t* timestamp); + + // Get 10 milliseconds of raw audio data to play out, and + // automatic resample to the requested frequency if > 0. + int PlayoutData10Ms(int desired_freq_hz, AudioFrame* audio_frame); + + ///////////////////////////////////////// + // Statistics + // + + int NetworkStatistics(ACMNetworkStatistics* statistics); + + void DestructEncoderInst(void* inst); + + // GET RED payload for iSAC. The method id called when 'this' ACM is + // the default ACM. + int REDPayloadISAC(int isac_rate, + int isac_bw_estimate, + uint8_t* payload, + int16_t* length_bytes); + + int ReplaceInternalDTXWithWebRtc(bool use_webrtc_dtx); + + int IsInternalDTXReplacedWithWebRtc(bool* uses_webrtc_dtx); + + int SetISACMaxRate(int max_bit_per_sec); + + int SetISACMaxPayloadSize(int max_size_bytes); + + int ConfigISACBandwidthEstimator(int frame_size_ms, + int rate_bit_per_sec, + bool enforce_frame_size = false); + + int UnregisterReceiveCodec(uint8_t payload_type); + + int EnableNack(size_t max_nack_list_size); + + void DisableNack(); + + std::vector GetNackList(int round_trip_time_ms) const; + + private: + int UnregisterReceiveCodecSafe(int payload_type); + + ACMGenericCodec* CreateCodec(const CodecInst& codec); + + int InitializeReceiverSafe(); + + bool HaveValidEncoder(const char* caller_name) const; + + // 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); + + // Process buffered audio when dual-streaming is not enabled (When RED is + // enabled still this function is used.) + int ProcessSingleStream(); + + // Process buffered audio when dual-streaming is enabled, i.e. secondary send + // codec is registered. + int ProcessDualStream(); + + // Preprocessing of input audio, including resampling and down-mixing if + // required, before pushing audio into encoder's buffer. + // + // in_frame: input audio-frame + // ptr_out: pointer to output audio_frame. If no preprocessing is required + // |ptr_out| will be pointing to |in_frame|, otherwise pointing to + // |preprocess_frame_|. + // + // Return value: + // -1: if encountering an error. + // 0: otherwise. + int PreprocessToAddData(const AudioFrame& in_frame, + const AudioFrame** ptr_out); + + // Change required states after starting to receive the codec corresponding + // to |index|. + int UpdateUponReceivingCodec(int index); + + int EncodeFragmentation(int fragmentation_index, int payload_type, + uint32_t current_timestamp, + ACMGenericCodec* encoder, + uint8_t* stream); + + void ResetFragmentation(int vector_size); + + // 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); + + AudioPacketizationCallback* packetization_callback_; + + int id_; + uint32_t expected_codec_ts_; + uint32_t expected_in_ts_; + CodecInst send_codec_inst_; + + uint8_t cng_nb_pltype_; + uint8_t cng_wb_pltype_; + uint8_t cng_swb_pltype_; + uint8_t cng_fb_pltype_; + + uint8_t red_pltype_; + bool vad_enabled_; + bool dtx_enabled_; + ACMVADMode vad_mode_; + ACMGenericCodec* codecs_[ACMCodecDB::kMaxNumCodecs]; + int mirror_codec_idx_[ACMCodecDB::kMaxNumCodecs]; + bool stereo_send_; + int current_send_codec_idx_; + bool send_codec_registered_; + ACMResampler resampler_; + AcmReceiver receiver_; + CriticalSectionWrapper* acm_crit_sect_; + ACMVADCallback* vad_callback_; + + // RED/FEC. + bool is_first_red_; + bool fec_enabled_; + + // TODO(turajs): |red_buffer_| is allocated in constructor, why having them + // as pointers and not an array. If concerned about the memory, then make a + // set-up function to allocate them only when they are going to be used, i.e. + // FEC or Dual-streaming is enabled. + uint8_t* red_buffer_; + + // TODO(turajs): we actually don't need |fragmentation_| as a member variable. + // It is sufficient to keep the length & payload type of previous payload in + // member variables. + RTPFragmentationHeader fragmentation_; + uint32_t last_fec_timestamp_; + + // This is to keep track of CN instances where we can send DTMFs. + uint8_t previous_pltype_; + + // Used when payloads are pushed into ACM without any RTP info + // One example is when pre-encoded bit-stream is pushed from + // a file. + // IMPORTANT: this variable is only used in IncomingPayload(), therefore, + // no lock acquired when interacting with this variable. If it is going to + // be used in other methods, locks need to be taken. + WebRtcRTPHeader* aux_rtp_header_; + + bool receiver_initialized_; + + CriticalSectionWrapper* callback_crit_sect_; + + AudioFrame preprocess_frame_; + CodecInst secondary_send_codec_inst_; + scoped_ptr secondary_encoder_; + uint32_t codec_timestamp_; + bool first_10ms_data_; +}; + +} // namespace webrtc + +#endif // WEBRTC_MODULES_AUDIO_CODING_MAIN_SOURCE_AUDIO_CODING_MODULE_IMPL_H_ diff --git a/webrtc/modules/audio_coding/main/acm2/initial_delay_manager.cc b/webrtc/modules/audio_coding/main/acm2/initial_delay_manager.cc new file mode 100644 index 0000000000..dffed646c7 --- /dev/null +++ b/webrtc/modules/audio_coding/main/acm2/initial_delay_manager.cc @@ -0,0 +1,224 @@ +/* + * Copyright (c) 2013 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/source/initial_delay_manager.h" + +namespace webrtc { + +InitialDelayManager::InitialDelayManager(int initial_delay_ms, + int late_packet_threshold) + : last_packet_type_(kUndefinedPacket), + last_receive_timestamp_(0), + timestamp_step_(0), + audio_payload_type_(kInvalidPayloadType), + initial_delay_ms_(initial_delay_ms), + buffered_audio_ms_(0), + buffering_(true), + playout_timestamp_(0), + late_packet_threshold_(late_packet_threshold) {} + +void InitialDelayManager::UpdateLastReceivedPacket( + const WebRtcRTPHeader& rtp_info, + uint32_t receive_timestamp, + PacketType type, + bool new_codec, + int sample_rate_hz, + SyncStream* sync_stream) { + assert(sync_stream); + + // If payload of audio packets is changing |new_codec| has to be true. + assert(!(!new_codec && type == kAudioPacket && + rtp_info.header.payloadType != audio_payload_type_)); + + // Just shorthands. + const RTPHeader* current_header = &rtp_info.header; + RTPHeader* last_header = &last_packet_rtp_info_.header; + + // Don't do anything if getting DTMF. The chance of DTMF in applications where + // initial delay is required is very low (we don't know of any). This avoids a + // lot of corner cases. The effect of ignoring DTMF packet is minimal. Note + // that DTMFs are inserted into NetEq just not accounted here. + if (type == kAvtPacket || + (last_packet_type_ != kUndefinedPacket && + !IsNewerSequenceNumber(current_header->sequenceNumber, + last_header->sequenceNumber))) { + sync_stream->num_sync_packets = 0; + return; + } + + if (new_codec) { + timestamp_step_ = 0; + if (type == kAudioPacket) + audio_payload_type_ = rtp_info.header.payloadType; + else + audio_payload_type_ = kInvalidPayloadType; // Invalid. + + RecordLastPacket(rtp_info, receive_timestamp, type); + sync_stream->num_sync_packets = 0; + buffered_audio_ms_ = 0; + buffering_ = true; + + // If |buffering_| is set then |playout_timestamp_| should have correct + // value. + UpdatePlayoutTimestamp(*current_header, sample_rate_hz); + return; + } + + uint32_t timestamp_increase = current_header->timestamp - + last_header->timestamp; + + // |timestamp_increase| is invalid if this is the first packet. The effect is + // that |buffered_audio_ms_| is not increased. + if (last_packet_type_ == kUndefinedPacket) { + timestamp_increase = 0; + } + + if (buffering_) { + buffered_audio_ms_ += timestamp_increase * 1000 / sample_rate_hz; + + // A timestamp that reflects the initial delay, while buffering. + UpdatePlayoutTimestamp(*current_header, sample_rate_hz); + + if (buffered_audio_ms_ >= initial_delay_ms_) + buffering_ = false; + } + + if (current_header->sequenceNumber == last_header->sequenceNumber + 1) { + // Two consecutive audio packets, the previous packet-type is audio, so we + // can update |timestamp_step_|. + if (last_packet_type_ == kAudioPacket) + timestamp_step_ = timestamp_increase; + RecordLastPacket(rtp_info, receive_timestamp, type); + sync_stream->num_sync_packets = 0; + return; + } + + uint16_t packet_gap = current_header->sequenceNumber - + last_header->sequenceNumber - 1; + + // For smooth transitions leave a gap between audio and sync packets. + sync_stream->num_sync_packets = last_packet_type_ == kSyncPacket ? + packet_gap - 1 : packet_gap - 2; + + // Do nothing if we haven't received any audio packet. + if (sync_stream->num_sync_packets > 0 && + audio_payload_type_ != kInvalidPayloadType) { + if (timestamp_step_ == 0) { + // Make an estimate for |timestamp_step_| if it is not updated, yet. + assert(packet_gap > 0); + timestamp_step_ = timestamp_increase / (packet_gap + 1); + } + sync_stream->timestamp_step = timestamp_step_; + + // Build the first sync-packet based on the current received packet. + memcpy(&sync_stream->rtp_info, &rtp_info, sizeof(rtp_info)); + sync_stream->rtp_info.header.payloadType = audio_payload_type_; + + uint16_t sequence_number_update = sync_stream->num_sync_packets + 1; + uint32_t timestamp_update = timestamp_step_ * sequence_number_update; + + // Rewind sequence number and timestamps. This will give a more accurate + // description of the missing packets. + // + // Note that we leave a gap between the last packet in sync-stream and the + // current received packet, so it should be compensated for in the following + // computation of timestamps and sequence number. + sync_stream->rtp_info.header.sequenceNumber -= sequence_number_update; + sync_stream->receive_timestamp = receive_timestamp - timestamp_update; + sync_stream->rtp_info.header.timestamp -= timestamp_update; + sync_stream->rtp_info.header.payloadType = audio_payload_type_; + } else { + sync_stream->num_sync_packets = 0; + } + + RecordLastPacket(rtp_info, receive_timestamp, type); + return; +} + +void InitialDelayManager::RecordLastPacket(const WebRtcRTPHeader& rtp_info, + uint32_t receive_timestamp, + PacketType type) { + last_packet_type_ = type; + last_receive_timestamp_ = receive_timestamp; + memcpy(&last_packet_rtp_info_, &rtp_info, sizeof(rtp_info)); +} + +void InitialDelayManager::LatePackets( + uint32_t timestamp_now, SyncStream* sync_stream) { + assert(sync_stream); + const int kLateThreshold = 5; + sync_stream->num_sync_packets = 0; + + // If there is no estimate of timestamp increment, |timestamp_step_|, then + // we cannot estimate the number of late packets. + // If the last packet has been CNG, estimating late packets is not meaningful, + // as a CNG packet is on unknown length. + // We can set a higher threshold if the last packet is CNG and continue + // execution, but this is how ACM1 code was written. + if (timestamp_step_ <= 0 || + last_packet_type_ == kCngPacket || + last_packet_type_ == kUndefinedPacket || + audio_payload_type_ == kInvalidPayloadType) // No audio packet received. + return; + + int num_late_packets = (timestamp_now - last_receive_timestamp_) / + timestamp_step_; + + if (num_late_packets < kLateThreshold) + return; + + int sync_offset = 1; // One gap at the end of the sync-stream. + if (last_packet_type_ != kSyncPacket) { + ++sync_offset; // One more gap at the beginning of the sync-stream. + --num_late_packets; + } + uint32_t timestamp_update = sync_offset * timestamp_step_; + + sync_stream->num_sync_packets = num_late_packets; + if (num_late_packets == 0) + return; + + // Build the first sync-packet in the sync-stream. + memcpy(&sync_stream->rtp_info, &last_packet_rtp_info_, + sizeof(last_packet_rtp_info_)); + + // Increase sequence number and timestamps. + sync_stream->rtp_info.header.sequenceNumber += sync_offset; + sync_stream->rtp_info.header.timestamp += timestamp_update; + sync_stream->receive_timestamp = last_receive_timestamp_ + timestamp_update; + sync_stream->timestamp_step = timestamp_step_; + + // Sync-packets have audio payload-type. + sync_stream->rtp_info.header.payloadType = audio_payload_type_; + + uint16_t sequence_number_update = num_late_packets + sync_offset - 1; + timestamp_update = sequence_number_update * timestamp_step_; + + // Fake the last RTP, assuming the caller will inject the whole sync-stream. + last_packet_rtp_info_.header.timestamp += timestamp_update; + last_packet_rtp_info_.header.sequenceNumber += sequence_number_update; + last_packet_rtp_info_.header.payloadType = audio_payload_type_; + last_receive_timestamp_ += timestamp_update; + + last_packet_type_ = kSyncPacket; + return; +} + +void InitialDelayManager::DisableBuffering() { + buffering_ = false; +} + +void InitialDelayManager::UpdatePlayoutTimestamp( + const RTPHeader& current_header, int sample_rate_hz) { + playout_timestamp_ = current_header.timestamp - static_cast( + initial_delay_ms_ * sample_rate_hz / 1000); +} + +} // namespace webrtc diff --git a/webrtc/modules/audio_coding/main/acm2/initial_delay_manager.h b/webrtc/modules/audio_coding/main/acm2/initial_delay_manager.h new file mode 100644 index 0000000000..5c8ae18be5 --- /dev/null +++ b/webrtc/modules/audio_coding/main/acm2/initial_delay_manager.h @@ -0,0 +1,115 @@ +/* + * Copyright (c) 2013 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_SOURCE_INITIAL_DELAY_MANAGER_H_ +#define WEBRTC_MODULES_AUDIO_CODING_MAIN_SOURCE_INITIAL_DELAY_MANAGER_H_ + +#include "webrtc/modules/interface/module_common_types.h" +#include "webrtc/system_wrappers/interface/scoped_ptr.h" + +namespace webrtc { + +class InitialDelayManager { + public: + enum PacketType { + kUndefinedPacket, kCngPacket, kAvtPacket, kAudioPacket, kSyncPacket }; + + // Specifies a stream of sync-packets. + struct SyncStream { + SyncStream() + : num_sync_packets(0), + receive_timestamp(0), + timestamp_step(0) { + memset(&rtp_info, 0, sizeof(rtp_info)); + } + + int num_sync_packets; + + // RTP header of the first sync-packet in the sequence. + WebRtcRTPHeader rtp_info; + + // Received timestamp of the first sync-packet in the sequence. + uint32_t receive_timestamp; + + // Samples per packet. + uint32_t timestamp_step; + }; + + InitialDelayManager(int initial_delay_ms, int late_packet_threshold); + + // Update with the last received RTP header, |header|, and received timestamp, + // |received_timestamp|. |type| indicates the packet type. If codec is changed + // since the last time |new_codec| should be true. |sample_rate_hz| is the + // decoder's sampling rate in Hz. |header| has a field to store sampling rate + // but we are not sure if that is properly set at the send side, and |header| + // is declared constant in the caller of this function + // (AcmReceiver::InsertPacket()). |sync_stream| contains information required + // to generate a stream of sync packets. + void UpdateLastReceivedPacket(const WebRtcRTPHeader& header, + uint32_t receive_timestamp, + PacketType type, + bool new_codec, + int sample_rate_hz, + SyncStream* sync_stream); + + // Based on the last received timestamp and given the current timestamp, + // sequence of late (or perhaps missing) packets is computed. + void LatePackets(uint32_t timestamp_now, SyncStream* sync_stream); + + // Playout timestamp, valid when buffering. + uint32_t playout_timestamp() { return playout_timestamp_; } + + // True if buffered audio is less than the given initial delay (specified at + // the constructor). Buffering might be disabled by the client of this class. + bool buffering() { return buffering_; } + + // Disable buffering in the class. + void DisableBuffering(); + + // True if any packet received for buffering. + bool PacketBuffered() { return last_packet_type_ != kUndefinedPacket; } + + private: + static const uint8_t kInvalidPayloadType = 0xFF; + + // Update playout timestamps. While buffering, this is about + // |initial_delay_ms| millisecond behind the latest received timestamp. + void UpdatePlayoutTimestamp(const RTPHeader& current_header, + int sample_rate_hz); + + // Record an RTP headr and related parameter + void RecordLastPacket(const WebRtcRTPHeader& rtp_info, + uint32_t receive_timestamp, + PacketType type); + + PacketType last_packet_type_; + WebRtcRTPHeader last_packet_rtp_info_; + uint32_t last_receive_timestamp_; + uint32_t timestamp_step_; + uint8_t audio_payload_type_; + const int initial_delay_ms_; + int buffered_audio_ms_; + bool buffering_; + + // During the initial phase where packets are being accumulated and silence + // is played out, |playout_ts| is a timestamp which is equal to + // |initial_delay_ms_| milliseconds earlier than the most recently received + // RTP timestamp. + uint32_t playout_timestamp_; + + // If the number of late packets exceed this value (computed based on current + // timestamp and last received timestamp), sequence of sync-packets is + // specified. + const int late_packet_threshold_; +}; + +} // namespace webrtc + +#endif // WEBRTC_MODULES_AUDIO_CODING_MAIN_SOURCE_INITIAL_DELAY_MANAGER_H_ diff --git a/webrtc/modules/audio_coding/main/acm2/initial_delay_manager_unittest.cc b/webrtc/modules/audio_coding/main/acm2/initial_delay_manager_unittest.cc new file mode 100644 index 0000000000..9d96d1770d --- /dev/null +++ b/webrtc/modules/audio_coding/main/acm2/initial_delay_manager_unittest.cc @@ -0,0 +1,371 @@ +/* + * Copyright (c) 2013 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 + +#include "gtest/gtest.h" +#include "webrtc/modules/audio_coding/main/source/initial_delay_manager.h" + +namespace webrtc { + +namespace { + +const uint8_t kAudioPayloadType = 0; +const uint8_t kCngPayloadType = 1; +const uint8_t kAvtPayloadType = 2; + +const int kSamplingRateHz = 16000; +const int kInitDelayMs = 200; +const int kFrameSizeMs = 20; +const uint32_t kTimestampStep = kFrameSizeMs * kSamplingRateHz / 1000; +const int kLatePacketThreshold = 5; + +void InitRtpInfo(WebRtcRTPHeader* rtp_info) { + memset(rtp_info, 0, sizeof(*rtp_info)); + rtp_info->header.markerBit = false; + rtp_info->header.payloadType = kAudioPayloadType; + rtp_info->header.sequenceNumber = 1234; + rtp_info->header.timestamp = 0xFFFFFFFD; // Close to wrap around. + rtp_info->header.ssrc = 0x87654321; // Arbitrary. + rtp_info->header.numCSRCs = 0; // Arbitrary. + rtp_info->header.paddingLength = 0; + rtp_info->header.headerLength = sizeof(RTPHeader); + rtp_info->header.payload_type_frequency = kSamplingRateHz; + rtp_info->header.extension.absoluteSendTime = 0; + rtp_info->header.extension.transmissionTimeOffset = 0; + rtp_info->frameType = kAudioFrameSpeech; +} + +void ForwardRtpHeader(int n, + WebRtcRTPHeader* rtp_info, + uint32_t* rtp_receive_timestamp) { + rtp_info->header.sequenceNumber += n; + rtp_info->header.timestamp += n * kTimestampStep; + *rtp_receive_timestamp += n * kTimestampStep; +} + +void NextRtpHeader(WebRtcRTPHeader* rtp_info, + uint32_t* rtp_receive_timestamp) { + ForwardRtpHeader(1, rtp_info, rtp_receive_timestamp); +} + +} // namespace + +class InitialDelayManagerTest : public ::testing::Test { + protected: + InitialDelayManagerTest() + : manager_(new InitialDelayManager(kInitDelayMs, kLatePacketThreshold)), + rtp_receive_timestamp_(1111) { } // Arbitrary starting point. + + virtual void SetUp() { + ASSERT_TRUE(manager_.get() != NULL); + InitRtpInfo(&rtp_info_); + } + + void GetNextRtpHeader(WebRtcRTPHeader* rtp_info, + uint32_t* rtp_receive_timestamp) const { + memcpy(rtp_info, &rtp_info_, sizeof(*rtp_info)); + *rtp_receive_timestamp = rtp_receive_timestamp_; + NextRtpHeader(rtp_info, rtp_receive_timestamp); + } + + scoped_ptr manager_; + WebRtcRTPHeader rtp_info_; + uint32_t rtp_receive_timestamp_; +}; + +TEST_F(InitialDelayManagerTest, Init) { + EXPECT_TRUE(manager_->buffering()); + EXPECT_FALSE(manager_->PacketBuffered()); + manager_->DisableBuffering(); + EXPECT_FALSE(manager_->buffering()); + InitialDelayManager::SyncStream sync_stream; + + // Call before any packet inserted. + manager_->LatePackets(0x6789ABCD, &sync_stream); // Arbitrary but large + // receive timestamp. + EXPECT_EQ(0, sync_stream.num_sync_packets); + + // Insert non-audio packets, a CNG and DTMF. + rtp_info_.header.payloadType = kCngPayloadType; + manager_->UpdateLastReceivedPacket(rtp_info_, rtp_receive_timestamp_, + InitialDelayManager::kCngPacket, false, + kSamplingRateHz, &sync_stream); + EXPECT_EQ(0, sync_stream.num_sync_packets); + ForwardRtpHeader(5, &rtp_info_, &rtp_receive_timestamp_); + rtp_info_.header.payloadType = kAvtPayloadType; + manager_->UpdateLastReceivedPacket(rtp_info_, rtp_receive_timestamp_, + InitialDelayManager::kAvtPacket, false, + kSamplingRateHz, &sync_stream); + // Gap in sequence numbers but no audio received, sync-stream should be empty. + EXPECT_EQ(0, sync_stream.num_sync_packets); + manager_->LatePackets(0x45678987, &sync_stream); // Large arbitrary receive + // timestamp. + // |manager_| has no estimate of timestamp-step and has not received any + // audio packet. + EXPECT_EQ(0, sync_stream.num_sync_packets); + + + NextRtpHeader(&rtp_info_, &rtp_receive_timestamp_); + rtp_info_.header.payloadType = kAudioPayloadType; + // First packet. + manager_->UpdateLastReceivedPacket(rtp_info_, rtp_receive_timestamp_, + InitialDelayManager::kAudioPacket, true, + kSamplingRateHz, &sync_stream); + EXPECT_EQ(0, sync_stream.num_sync_packets); + + // Call LatePAcket() after only one packet inserted. + manager_->LatePackets(0x6789ABCD, &sync_stream); // Arbitrary but large + // receive timestamp. + EXPECT_EQ(0, sync_stream.num_sync_packets); + + // Gap in timestamp, but this packet is also flagged as "new," therefore, + // expecting empty sync-stream. + ForwardRtpHeader(5, &rtp_info_, &rtp_receive_timestamp_); + manager_->UpdateLastReceivedPacket(rtp_info_, rtp_receive_timestamp_, + InitialDelayManager::kAudioPacket, true, + kSamplingRateHz, &sync_stream); +} + +TEST_F(InitialDelayManagerTest, MissingPacket) { + InitialDelayManager::SyncStream sync_stream; + // First packet. + manager_->UpdateLastReceivedPacket(rtp_info_, rtp_receive_timestamp_, + InitialDelayManager::kAudioPacket, true, + kSamplingRateHz, &sync_stream); + ASSERT_EQ(0, sync_stream.num_sync_packets); + + // Second packet. + NextRtpHeader(&rtp_info_, &rtp_receive_timestamp_); + manager_->UpdateLastReceivedPacket(rtp_info_, rtp_receive_timestamp_, + InitialDelayManager::kAudioPacket, false, + kSamplingRateHz, &sync_stream); + ASSERT_EQ(0, sync_stream.num_sync_packets); + + // Third packet, missing packets start from here. + NextRtpHeader(&rtp_info_, &rtp_receive_timestamp_); + + // First sync-packet in sync-stream is one after the above packet. + WebRtcRTPHeader expected_rtp_info; + uint32_t expected_receive_timestamp; + GetNextRtpHeader(&expected_rtp_info, &expected_receive_timestamp); + + const int kNumMissingPackets = 10; + ForwardRtpHeader(kNumMissingPackets, &rtp_info_, &rtp_receive_timestamp_); + manager_->UpdateLastReceivedPacket(rtp_info_, rtp_receive_timestamp_, + InitialDelayManager::kAudioPacket, false, + kSamplingRateHz, &sync_stream); + EXPECT_EQ(kNumMissingPackets - 2, sync_stream.num_sync_packets); + EXPECT_EQ(0, memcmp(&expected_rtp_info, &sync_stream.rtp_info, + sizeof(expected_rtp_info))); + EXPECT_EQ(kTimestampStep, sync_stream.timestamp_step); + EXPECT_EQ(expected_receive_timestamp, sync_stream.receive_timestamp); +} + +// There hasn't been any consecutive packets to estimate timestamp-step. +TEST_F(InitialDelayManagerTest, MissingPacketEstimateTimestamp) { + InitialDelayManager::SyncStream sync_stream; + // First packet. + manager_->UpdateLastReceivedPacket(rtp_info_, rtp_receive_timestamp_, + InitialDelayManager::kAudioPacket, true, + kSamplingRateHz, &sync_stream); + ASSERT_EQ(0, sync_stream.num_sync_packets); + + // Second packet, missing packets start here. + NextRtpHeader(&rtp_info_, &rtp_receive_timestamp_); + + // First sync-packet in sync-stream is one after the above. + WebRtcRTPHeader expected_rtp_info; + uint32_t expected_receive_timestamp; + GetNextRtpHeader(&expected_rtp_info, &expected_receive_timestamp); + + const int kNumMissingPackets = 10; + ForwardRtpHeader(kNumMissingPackets, &rtp_info_, &rtp_receive_timestamp_); + manager_->UpdateLastReceivedPacket(rtp_info_, rtp_receive_timestamp_, + InitialDelayManager::kAudioPacket, false, + kSamplingRateHz, &sync_stream); + EXPECT_EQ(kNumMissingPackets - 2, sync_stream.num_sync_packets); + EXPECT_EQ(0, memcmp(&expected_rtp_info, &sync_stream.rtp_info, + sizeof(expected_rtp_info))); +} + +TEST_F(InitialDelayManagerTest, MissingPacketWithCng) { + InitialDelayManager::SyncStream sync_stream; + + // First packet. + manager_->UpdateLastReceivedPacket(rtp_info_, rtp_receive_timestamp_, + InitialDelayManager::kAudioPacket, true, + kSamplingRateHz, &sync_stream); + ASSERT_EQ(0, sync_stream.num_sync_packets); + + // Second packet as CNG. + NextRtpHeader(&rtp_info_, &rtp_receive_timestamp_); + rtp_info_.header.payloadType = kCngPayloadType; + manager_->UpdateLastReceivedPacket(rtp_info_, rtp_receive_timestamp_, + InitialDelayManager::kCngPacket, false, + kSamplingRateHz, &sync_stream); + ASSERT_EQ(0, sync_stream.num_sync_packets); + + // Audio packet after CNG. Missing packets start from this packet. + rtp_info_.header.payloadType = kAudioPayloadType; + NextRtpHeader(&rtp_info_, &rtp_receive_timestamp_); + + // Timestamps are increased higher than regular packet. + const uint32_t kCngTimestampStep = 5 * kTimestampStep; + rtp_info_.header.timestamp += kCngTimestampStep; + rtp_receive_timestamp_ += kCngTimestampStep; + + // First sync-packet in sync-stream is the one after the above packet. + WebRtcRTPHeader expected_rtp_info; + uint32_t expected_receive_timestamp; + GetNextRtpHeader(&expected_rtp_info, &expected_receive_timestamp); + + const int kNumMissingPackets = 10; + ForwardRtpHeader(kNumMissingPackets, &rtp_info_, &rtp_receive_timestamp_); + manager_->UpdateLastReceivedPacket(rtp_info_, rtp_receive_timestamp_, + InitialDelayManager::kAudioPacket, false, + kSamplingRateHz, &sync_stream); + EXPECT_EQ(kNumMissingPackets - 2, sync_stream.num_sync_packets); + EXPECT_EQ(0, memcmp(&expected_rtp_info, &sync_stream.rtp_info, + sizeof(expected_rtp_info))); + EXPECT_EQ(kTimestampStep, sync_stream.timestamp_step); + EXPECT_EQ(expected_receive_timestamp, sync_stream.receive_timestamp); +} + +TEST_F(InitialDelayManagerTest, LatePacket) { + InitialDelayManager::SyncStream sync_stream; + // First packet. + manager_->UpdateLastReceivedPacket(rtp_info_, rtp_receive_timestamp_, + InitialDelayManager::kAudioPacket, true, + kSamplingRateHz, &sync_stream); + ASSERT_EQ(0, sync_stream.num_sync_packets); + + // Second packet. + NextRtpHeader(&rtp_info_, &rtp_receive_timestamp_); + manager_->UpdateLastReceivedPacket(rtp_info_, rtp_receive_timestamp_, + InitialDelayManager::kAudioPacket, false, + kSamplingRateHz, &sync_stream); + ASSERT_EQ(0, sync_stream.num_sync_packets); + + // Timestamp increment for 10ms; + const uint32_t kTimestampStep10Ms = kSamplingRateHz / 100; + + // 10 ms after the second packet is inserted. + uint32_t timestamp_now = rtp_receive_timestamp_ + kTimestampStep10Ms; + + // Third packet, late packets start from this packet. + NextRtpHeader(&rtp_info_, &rtp_receive_timestamp_); + + // First sync-packet in sync-stream, which is one after the above packet. + WebRtcRTPHeader expected_rtp_info; + uint32_t expected_receive_timestamp; + GetNextRtpHeader(&expected_rtp_info, &expected_receive_timestamp); + + const int kLatePacketThreshold = 5; + + int expected_num_late_packets = kLatePacketThreshold - 1; + for (int k = 0; k < 2; ++k) { + for (int n = 1; n < kLatePacketThreshold * kFrameSizeMs / 10; ++n) { + manager_->LatePackets(timestamp_now, &sync_stream); + EXPECT_EQ(0, sync_stream.num_sync_packets) << + "try " << k << " loop number " << n; + timestamp_now += kTimestampStep10Ms; + } + manager_->LatePackets(timestamp_now, &sync_stream); + + EXPECT_EQ(expected_num_late_packets, sync_stream.num_sync_packets) << + "try " << k; + EXPECT_EQ(kTimestampStep, sync_stream.timestamp_step) << + "try " << k; + EXPECT_EQ(expected_receive_timestamp, sync_stream.receive_timestamp) << + "try " << k; + EXPECT_EQ(0, memcmp(&expected_rtp_info, &sync_stream.rtp_info, + sizeof(expected_rtp_info))); + + timestamp_now += kTimestampStep10Ms; + + // |manger_| assumes the |sync_stream| obtained by LatePacket() is fully + // injected. The last injected packet is sync-packet, therefore, there will + // not be any gap between sync stream of this and the next iteration. + ForwardRtpHeader(sync_stream.num_sync_packets, &expected_rtp_info, + &expected_receive_timestamp); + expected_num_late_packets = kLatePacketThreshold; + } + + // Test "no-gap" for missing packet after late packet. + // |expected_rtp_info| is the expected sync-packet if any packet is missing. + memcpy(&rtp_info_, &expected_rtp_info, sizeof(rtp_info_)); + rtp_receive_timestamp_ = expected_receive_timestamp; + + int kNumMissingPackets = 3; // Arbitrary. + ForwardRtpHeader(kNumMissingPackets, &rtp_info_, &rtp_receive_timestamp_); + manager_->UpdateLastReceivedPacket(rtp_info_, rtp_receive_timestamp_, + InitialDelayManager::kAudioPacket, false, + kSamplingRateHz, &sync_stream); + + // Note that there is one packet gap between the last sync-packet and the + // latest inserted packet. + EXPECT_EQ(kNumMissingPackets - 1, sync_stream.num_sync_packets); + EXPECT_EQ(kTimestampStep, sync_stream.timestamp_step); + EXPECT_EQ(expected_receive_timestamp, sync_stream.receive_timestamp); + EXPECT_EQ(0, memcmp(&expected_rtp_info, &sync_stream.rtp_info, + sizeof(expected_rtp_info))); +} + +TEST_F(InitialDelayManagerTest, NoLatePacketAfterCng) { + InitialDelayManager::SyncStream sync_stream; + + // First packet. + manager_->UpdateLastReceivedPacket(rtp_info_, rtp_receive_timestamp_, + InitialDelayManager::kAudioPacket, true, + kSamplingRateHz, &sync_stream); + ASSERT_EQ(0, sync_stream.num_sync_packets); + + // Second packet as CNG. + NextRtpHeader(&rtp_info_, &rtp_receive_timestamp_); + const uint8_t kCngPayloadType = 1; // Arbitrary. + rtp_info_.header.payloadType = kCngPayloadType; + manager_->UpdateLastReceivedPacket(rtp_info_, rtp_receive_timestamp_, + InitialDelayManager::kCngPacket, false, + kSamplingRateHz, &sync_stream); + ASSERT_EQ(0, sync_stream.num_sync_packets); + + // Forward the time more then |kLatePacketThreshold| packets. + uint32_t timestamp_now = rtp_receive_timestamp_ + kTimestampStep * (3 + + kLatePacketThreshold); + + manager_->LatePackets(timestamp_now, &sync_stream); + EXPECT_EQ(0, sync_stream.num_sync_packets); +} + +TEST_F(InitialDelayManagerTest, BufferingAudio) { + InitialDelayManager::SyncStream sync_stream; + + // Very first packet is not counted in calculation of buffered audio. + for (int n = 0; n < kInitDelayMs / kFrameSizeMs; ++n) { + manager_->UpdateLastReceivedPacket(rtp_info_, rtp_receive_timestamp_, + InitialDelayManager::kAudioPacket, + n == 0, kSamplingRateHz, &sync_stream); + EXPECT_EQ(0, sync_stream.num_sync_packets); + EXPECT_TRUE(manager_->buffering()); + const uint32_t expected_playout_timestamp = rtp_info_.header.timestamp - + kInitDelayMs * kSamplingRateHz / 1000; + EXPECT_EQ(expected_playout_timestamp, manager_->playout_timestamp()); + NextRtpHeader(&rtp_info_, &rtp_receive_timestamp_); + } + + manager_->UpdateLastReceivedPacket(rtp_info_, rtp_receive_timestamp_, + InitialDelayManager::kAudioPacket, + false, kSamplingRateHz, &sync_stream); + EXPECT_EQ(0, sync_stream.num_sync_packets); + EXPECT_FALSE(manager_->buffering()); +} + +} // namespace webrtc diff --git a/webrtc/modules/audio_coding/main/acm2/nack.cc b/webrtc/modules/audio_coding/main/acm2/nack.cc new file mode 100644 index 0000000000..d4d0c3b8fb --- /dev/null +++ b/webrtc/modules/audio_coding/main/acm2/nack.cc @@ -0,0 +1,225 @@ +/* + * Copyright (c) 2013 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/source/nack.h" + +#include // For assert. + +#include // For std::max. + +#include "webrtc/modules/interface/module_common_types.h" +#include "webrtc/system_wrappers/interface/logging.h" + +namespace webrtc { + +namespace { + +const int kDefaultSampleRateKhz = 48; +const int kDefaultPacketSizeMs = 20; + +} // namespace + +Nack::Nack(int nack_threshold_packets) + : nack_threshold_packets_(nack_threshold_packets), + sequence_num_last_received_rtp_(0), + timestamp_last_received_rtp_(0), + any_rtp_received_(false), + sequence_num_last_decoded_rtp_(0), + timestamp_last_decoded_rtp_(0), + any_rtp_decoded_(false), + sample_rate_khz_(kDefaultSampleRateKhz), + samples_per_packet_(sample_rate_khz_ * kDefaultPacketSizeMs), + max_nack_list_size_(kNackListSizeLimit) {} + +Nack* Nack::Create(int nack_threshold_packets) { + return new Nack(nack_threshold_packets); +} + +void Nack::UpdateSampleRate(int sample_rate_hz) { + assert(sample_rate_hz > 0); + sample_rate_khz_ = sample_rate_hz / 1000; +} + +void Nack::UpdateLastReceivedPacket(uint16_t sequence_number, + uint32_t timestamp) { + // Just record the value of sequence number and timestamp if this is the + // first packet. + if (!any_rtp_received_) { + sequence_num_last_received_rtp_ = sequence_number; + timestamp_last_received_rtp_ = timestamp; + any_rtp_received_ = true; + // If no packet is decoded, to have a reasonable estimate of time-to-play + // use the given values. + if (!any_rtp_decoded_) { + sequence_num_last_decoded_rtp_ = sequence_number; + timestamp_last_decoded_rtp_ = timestamp; + } + return; + } + + if (sequence_number == sequence_num_last_received_rtp_) + return; + + // Received RTP should not be in the list. + nack_list_.erase(sequence_number); + + // If this is an old sequence number, no more action is required, return. + if (IsNewerSequenceNumber(sequence_num_last_received_rtp_, sequence_number)) + return; + + UpdateSamplesPerPacket(sequence_number, timestamp); + + UpdateList(sequence_number); + + sequence_num_last_received_rtp_ = sequence_number; + timestamp_last_received_rtp_ = timestamp; + LimitNackListSize(); +} + +void Nack::UpdateSamplesPerPacket(uint16_t sequence_number_current_received_rtp, + uint32_t timestamp_current_received_rtp) { + uint32_t timestamp_increase = timestamp_current_received_rtp - + timestamp_last_received_rtp_; + uint16_t sequence_num_increase = sequence_number_current_received_rtp - + sequence_num_last_received_rtp_; + + samples_per_packet_ = timestamp_increase / sequence_num_increase; +} + +void Nack::UpdateList(uint16_t sequence_number_current_received_rtp) { + // Some of the packets which were considered late, now are considered missing. + ChangeFromLateToMissing(sequence_number_current_received_rtp); + + if (IsNewerSequenceNumber(sequence_number_current_received_rtp, + sequence_num_last_received_rtp_ + 1)) + AddToList(sequence_number_current_received_rtp); +} + +void Nack::ChangeFromLateToMissing( + uint16_t sequence_number_current_received_rtp) { + NackList::const_iterator lower_bound = nack_list_.lower_bound( + static_cast(sequence_number_current_received_rtp - + nack_threshold_packets_)); + + for (NackList::iterator it = nack_list_.begin(); it != lower_bound; ++it) + it->second.is_missing = true; +} + +uint32_t Nack::EstimateTimestamp(uint16_t sequence_num) { + uint16_t sequence_num_diff = sequence_num - sequence_num_last_received_rtp_; + return sequence_num_diff * samples_per_packet_ + timestamp_last_received_rtp_; +} + +void Nack::AddToList(uint16_t sequence_number_current_received_rtp) { + assert(!any_rtp_decoded_ || IsNewerSequenceNumber( + sequence_number_current_received_rtp, sequence_num_last_decoded_rtp_)); + + // Packets with sequence numbers older than |upper_bound_missing| are + // considered missing, and the rest are considered late. + uint16_t upper_bound_missing = sequence_number_current_received_rtp - + nack_threshold_packets_; + + for (uint16_t n = sequence_num_last_received_rtp_ + 1; + IsNewerSequenceNumber(sequence_number_current_received_rtp, n); ++n) { + bool is_missing = IsNewerSequenceNumber(upper_bound_missing, n); + uint32_t timestamp = EstimateTimestamp(n); + NackElement nack_element(TimeToPlay(timestamp), timestamp, is_missing); + nack_list_.insert(nack_list_.end(), std::make_pair(n, nack_element)); + } +} + +void Nack::UpdateEstimatedPlayoutTimeBy10ms() { + while (!nack_list_.empty() && + nack_list_.begin()->second.time_to_play_ms <= 10) + nack_list_.erase(nack_list_.begin()); + + for (NackList::iterator it = nack_list_.begin(); it != nack_list_.end(); ++it) + it->second.time_to_play_ms -= 10; +} + +void Nack::UpdateLastDecodedPacket(uint16_t sequence_number, + uint32_t timestamp) { + if (IsNewerSequenceNumber(sequence_number, sequence_num_last_decoded_rtp_) || + !any_rtp_decoded_) { + sequence_num_last_decoded_rtp_ = sequence_number; + timestamp_last_decoded_rtp_ = timestamp; + // Packets in the list with sequence numbers less than the + // sequence number of the decoded RTP should be removed from the lists. + // They will be discarded by the jitter buffer if they arrive. + nack_list_.erase(nack_list_.begin(), nack_list_.upper_bound( + sequence_num_last_decoded_rtp_)); + + // Update estimated time-to-play. + for (NackList::iterator it = nack_list_.begin(); it != nack_list_.end(); + ++it) + it->second.time_to_play_ms = TimeToPlay(it->second.estimated_timestamp); + } else { + assert(sequence_number == sequence_num_last_decoded_rtp_); + + // Same sequence number as before. 10 ms is elapsed, update estimations for + // time-to-play. + UpdateEstimatedPlayoutTimeBy10ms(); + + // Update timestamp for better estimate of time-to-play, for packets which + // are added to NACK list later on. + timestamp_last_decoded_rtp_ += sample_rate_khz_ * 10; + } + any_rtp_decoded_ = true; +} + +Nack::NackList Nack::GetNackList() const { + return nack_list_; +} + +void Nack::Reset() { + nack_list_.clear(); + + sequence_num_last_received_rtp_ = 0; + timestamp_last_received_rtp_ = 0; + any_rtp_received_ = false; + sequence_num_last_decoded_rtp_ = 0; + timestamp_last_decoded_rtp_ = 0; + any_rtp_decoded_ = false; + sample_rate_khz_ = kDefaultSampleRateKhz; + samples_per_packet_ = sample_rate_khz_ * kDefaultPacketSizeMs; +} + +int Nack::SetMaxNackListSize(size_t max_nack_list_size) { + if (max_nack_list_size == 0 || max_nack_list_size > kNackListSizeLimit) + return -1; + max_nack_list_size_ = max_nack_list_size; + LimitNackListSize(); + return 0; +} + +void Nack::LimitNackListSize() { + uint16_t limit = sequence_num_last_received_rtp_ - + static_cast(max_nack_list_size_) - 1; + nack_list_.erase(nack_list_.begin(), nack_list_.upper_bound(limit)); +} + +int Nack::TimeToPlay(uint32_t timestamp) const { + uint32_t timestamp_increase = timestamp - timestamp_last_decoded_rtp_; + return timestamp_increase / sample_rate_khz_; +} + +// We don't erase elements with time-to-play shorter than round-trip-time. +std::vector Nack::GetNackList(int round_trip_time_ms) const { + std::vector sequence_numbers; + for (NackList::const_iterator it = nack_list_.begin(); it != nack_list_.end(); + ++it) { + if (it->second.is_missing && + it->second.time_to_play_ms > round_trip_time_ms) + sequence_numbers.push_back(it->first); + } + return sequence_numbers; +} + +} // namespace webrtc diff --git a/webrtc/modules/audio_coding/main/acm2/nack.h b/webrtc/modules/audio_coding/main/acm2/nack.h new file mode 100644 index 0000000000..ddafbcdf63 --- /dev/null +++ b/webrtc/modules/audio_coding/main/acm2/nack.h @@ -0,0 +1,209 @@ +/* + * Copyright (c) 2013 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_SOURCE_NACK_H_ +#define WEBRTC_MODULES_AUDIO_CODING_MAIN_SOURCE_NACK_H_ + +#include +#include + +#include "webrtc/modules/audio_coding/main/interface/audio_coding_module_typedefs.h" +#include "webrtc/system_wrappers/interface/scoped_ptr.h" +#include "webrtc/test/testsupport/gtest_prod_util.h" + +// +// The Nack class keeps track of the lost packets, an estimate of time-to-play +// for each packet is also given. +// +// Every time a packet is pushed into NetEq, LastReceivedPacket() has to be +// called to update the NACK list. +// +// Every time 10ms audio is pulled from NetEq LastDecodedPacket() should be +// called, and time-to-play is updated at that moment. +// +// If packet N is received, any packet prior to |N - NackThreshold| which is not +// arrived is considered lost, and should be labeled as "missing" (the size of +// the list might be limited and older packet eliminated from the list). Packets +// |N - NackThreshold|, |N - NackThreshold + 1|, ..., |N - 1| are considered +// "late." A "late" packet with sequence number K is changed to "missing" any +// time a packet with sequence number newer than |K + NackList| is arrived. +// +// The Nack class has to know about the sample rate of the packets to compute +// time-to-play. So sample rate should be set as soon as the first packet is +// received. If there is a change in the receive codec (sender changes codec) +// then Nack should be reset. This is because NetEQ would flush its buffer and +// re-transmission is meaning less for old packet. Therefore, in that case, +// after reset the sampling rate has to be updated. +// +// Thread Safety +// ============= +// Please note that this class in not thread safe. The class must be protected +// if different APIs are called from different threads. +// +namespace webrtc { + +class Nack { + public: + // A limit for the size of the NACK list. + static const size_t kNackListSizeLimit = 500; // 10 seconds for 20 ms frame + // packets. + // Factory method. + static Nack* Create(int nack_threshold_packets); + + ~Nack() {} + + // Set a maximum for the size of the NACK list. If the last received packet + // has sequence number of N, then NACK list will not contain any element + // with sequence number earlier than N - |max_nack_list_size|. + // + // The largest maximum size is defined by |kNackListSizeLimit| + int SetMaxNackListSize(size_t max_nack_list_size); + + // Set the sampling rate. + // + // If associated sampling rate of the received packets is changed, call this + // function to update sampling rate. Note that if there is any change in + // received codec then NetEq will flush its buffer and NACK has to be reset. + // After Reset() is called sampling rate has to be set. + void UpdateSampleRate(int sample_rate_hz); + + // Update the sequence number and the timestamp of the last decoded RTP. This + // API should be called every time 10 ms audio is pulled from NetEq. + void UpdateLastDecodedPacket(uint16_t sequence_number, uint32_t timestamp); + + // Update the sequence number and the timestamp of the last received RTP. This + // API should be called every time a packet pushed into ACM. + void UpdateLastReceivedPacket(uint16_t sequence_number, uint32_t timestamp); + + // Get a list of "missing" packets which have expected time-to-play larger + // than the given round-trip-time (in milliseconds). + // Note: Late packets are not included. + std::vector GetNackList(int round_trip_time_ms) const; + + // Reset to default values. The NACK list is cleared. + // |nack_threshold_packets_| & |max_nack_list_size_| preserve their values. + void Reset(); + + private: + // This test need to access the private method GetNackList(). + FRIEND_TEST_ALL_PREFIXES(NackTest, EstimateTimestampAndTimeToPlay); + + struct NackElement { + NackElement(int initial_time_to_play_ms, + uint32_t initial_timestamp, + bool missing) + : time_to_play_ms(initial_time_to_play_ms), + estimated_timestamp(initial_timestamp), + is_missing(missing) {} + + // Estimated time (ms) left for this packet to be decoded. This estimate is + // updated every time jitter buffer decodes a packet. + int time_to_play_ms; + + // A guess about the timestamp of the missing packet, it is used for + // estimation of |time_to_play_ms|. The estimate might be slightly wrong if + // there has been frame-size change since the last received packet and the + // missing packet. However, the risk of this is low, and in case of such + // errors, there will be a minor misestimation in time-to-play of missing + // packets. This will have a very minor effect on NACK performance. + uint32_t estimated_timestamp; + + // True if the packet is considered missing. Otherwise indicates packet is + // late. + bool is_missing; + }; + + class NackListCompare { + public: + bool operator() (uint16_t sequence_number_old, + uint16_t sequence_number_new) const { + return IsNewerSequenceNumber(sequence_number_new, sequence_number_old); + } + }; + + typedef std::map NackList; + + // Constructor. + explicit Nack(int nack_threshold_packets); + + // This API is used only for testing to assess whether time-to-play is + // computed correctly. + NackList GetNackList() const; + + // Given the |sequence_number_current_received_rtp| of currently received RTP, + // recognize packets which are not arrive and add to the list. + void AddToList(uint16_t sequence_number_current_received_rtp); + + // This function subtracts 10 ms of time-to-play for all packets in NACK list. + // This is called when 10 ms elapsed with no new RTP packet decoded. + void UpdateEstimatedPlayoutTimeBy10ms(); + + // Given the |sequence_number_current_received_rtp| and + // |timestamp_current_received_rtp| of currently received RTP update number + // of samples per packet. + void UpdateSamplesPerPacket(uint16_t sequence_number_current_received_rtp, + uint32_t timestamp_current_received_rtp); + + // Given the |sequence_number_current_received_rtp| of currently received RTP + // update the list. That is; some packets will change from late to missing, + // some packets are inserted as missing and some inserted as late. + void UpdateList(uint16_t sequence_number_current_received_rtp); + + // Packets which are considered late for too long (according to + // |nack_threshold_packets_|) are flagged as missing. + void ChangeFromLateToMissing(uint16_t sequence_number_current_received_rtp); + + // Packets which have sequence number older that + // |sequence_num_last_received_rtp_| - |max_nack_list_size_| are removed + // from the NACK list. + void LimitNackListSize(); + + // Estimate timestamp of a missing packet given its sequence number. + uint32_t EstimateTimestamp(uint16_t sequence_number); + + // Compute time-to-play given a timestamp. + int TimeToPlay(uint32_t timestamp) const; + + // If packet N is arrived, any packet prior to N - |nack_threshold_packets_| + // which is not arrived is considered missing, and should be in NACK list. + // Also any packet in the range of N-1 and N - |nack_threshold_packets_|, + // exclusive, which is not arrived is considered late, and should should be + // in the list of late packets. + const int nack_threshold_packets_; + + // Valid if a packet is received. + uint16_t sequence_num_last_received_rtp_; + uint32_t timestamp_last_received_rtp_; + bool any_rtp_received_; // If any packet received. + + // Valid if a packet is decoded. + uint16_t sequence_num_last_decoded_rtp_; + uint32_t timestamp_last_decoded_rtp_; + bool any_rtp_decoded_; // If any packet decoded. + + int sample_rate_khz_; // Sample rate in kHz. + + // Number of samples per packet. We update this every time we receive a + // packet, not only for consecutive packets. + int samples_per_packet_; + + // A list of missing packets to be retransmitted. Components of the list + // contain the sequence number of missing packets and the estimated time that + // each pack is going to be played out. + NackList nack_list_; + + // NACK list will not keep track of missing packets prior to + // |sequence_num_last_received_rtp_| - |max_nack_list_size_|. + size_t max_nack_list_size_; +}; + +} // namespace webrtc + +#endif // WEBRTC_MODULES_AUDIO_CODING_MAIN_SOURCE_NACK_H_ diff --git a/webrtc/modules/audio_coding/main/acm2/nack_unittest.cc b/webrtc/modules/audio_coding/main/acm2/nack_unittest.cc new file mode 100644 index 0000000000..b84211dc32 --- /dev/null +++ b/webrtc/modules/audio_coding/main/acm2/nack_unittest.cc @@ -0,0 +1,482 @@ +/* + * Copyright (c) 2013 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/source/nack.h" + +#include + +#include + +#include "gtest/gtest.h" +#include "webrtc/typedefs.h" +#include "webrtc/modules/audio_coding/main/interface/audio_coding_module_typedefs.h" +#include "webrtc/system_wrappers/interface/scoped_ptr.h" + +namespace webrtc { + +namespace { + +const int kNackThreshold = 3; +const int kSampleRateHz = 16000; +const int kPacketSizeMs = 30; +const uint32_t kTimestampIncrement = 480; // 30 ms. +const int kShortRoundTripTimeMs = 1; + +bool IsNackListCorrect(const std::vector& nack_list, + const uint16_t* lost_sequence_numbers, + size_t num_lost_packets) { + if (nack_list.size() != num_lost_packets) + return false; + + if (num_lost_packets == 0) + return true; + + for (size_t k = 0; k < nack_list.size(); ++k) { + int seq_num = nack_list[k]; + bool seq_num_matched = false; + for (size_t n = 0; n < num_lost_packets; ++n) { + if (seq_num == lost_sequence_numbers[n]) { + seq_num_matched = true; + break; + } + } + if (!seq_num_matched) + return false; + } + return true; +} + +} // namespace + +TEST(NackTest, EmptyListWhenNoPacketLoss) { + scoped_ptr nack(Nack::Create(kNackThreshold)); + nack->UpdateSampleRate(kSampleRateHz); + + int seq_num = 1; + uint32_t timestamp = 0; + + std::vector nack_list; + for (int n = 0; n < 100; n++) { + nack->UpdateLastReceivedPacket(seq_num, timestamp); + nack_list = nack->GetNackList(kShortRoundTripTimeMs); + seq_num++; + timestamp += kTimestampIncrement; + nack_list = nack->GetNackList(kShortRoundTripTimeMs); + EXPECT_TRUE(nack_list.empty()); + } +} + +TEST(NackTest, NoNackIfReorderWithinNackThreshold) { + scoped_ptr nack(Nack::Create(kNackThreshold)); + nack->UpdateSampleRate(kSampleRateHz); + + int seq_num = 1; + uint32_t timestamp = 0; + std::vector nack_list; + + nack->UpdateLastReceivedPacket(seq_num, timestamp); + nack_list = nack->GetNackList(kShortRoundTripTimeMs); + EXPECT_TRUE(nack_list.empty()); + int num_late_packets = kNackThreshold + 1; + + // Push in reverse order + while (num_late_packets > 0) { + nack->UpdateLastReceivedPacket(seq_num + num_late_packets, timestamp + + num_late_packets * kTimestampIncrement); + nack_list = nack->GetNackList(kShortRoundTripTimeMs); + EXPECT_TRUE(nack_list.empty()); + num_late_packets--; + } +} + +TEST(NackTest, LatePacketsMovedToNackThenNackListDoesNotChange) { + const uint16_t kSequenceNumberLostPackets[] = { 2, 3, 4, 5, 6, 7, 8, 9 }; + static const int kNumAllLostPackets = sizeof(kSequenceNumberLostPackets) / + sizeof(kSequenceNumberLostPackets[0]); + + for (int k = 0; k < 2; k++) { // Two iteration with/without wrap around. + scoped_ptr nack(Nack::Create(kNackThreshold)); + nack->UpdateSampleRate(kSampleRateHz); + + uint16_t sequence_num_lost_packets[kNumAllLostPackets]; + for (int n = 0; n < kNumAllLostPackets; n++) { + sequence_num_lost_packets[n] = kSequenceNumberLostPackets[n] + k * + 65531; // Have wrap around in sequence numbers for |k == 1|. + } + uint16_t seq_num = sequence_num_lost_packets[0] - 1; + + uint32_t timestamp = 0; + std::vector nack_list; + + nack->UpdateLastReceivedPacket(seq_num, timestamp); + nack_list = nack->GetNackList(kShortRoundTripTimeMs); + EXPECT_TRUE(nack_list.empty()); + + seq_num = sequence_num_lost_packets[kNumAllLostPackets - 1] + 1; + timestamp += kTimestampIncrement * (kNumAllLostPackets + 1); + int num_lost_packets = std::max(0, kNumAllLostPackets - kNackThreshold); + + for (int n = 0; n < kNackThreshold + 1; ++n) { + nack->UpdateLastReceivedPacket(seq_num, timestamp); + nack_list = nack->GetNackList(kShortRoundTripTimeMs); + EXPECT_TRUE(IsNackListCorrect(nack_list, sequence_num_lost_packets, + num_lost_packets)); + seq_num++; + timestamp += kTimestampIncrement; + num_lost_packets++; + } + + for (int n = 0; n < 100; ++n) { + nack->UpdateLastReceivedPacket(seq_num, timestamp); + nack_list = nack->GetNackList(kShortRoundTripTimeMs); + EXPECT_TRUE(IsNackListCorrect(nack_list, sequence_num_lost_packets, + kNumAllLostPackets)); + seq_num++; + timestamp += kTimestampIncrement; + } + } +} + +TEST(NackTest, ArrivedPacketsAreRemovedFromNackList) { + const uint16_t kSequenceNumberLostPackets[] = { 2, 3, 4, 5, 6, 7, 8, 9 }; + static const int kNumAllLostPackets = sizeof(kSequenceNumberLostPackets) / + sizeof(kSequenceNumberLostPackets[0]); + + for (int k = 0; k < 2; ++k) { // Two iteration with/without wrap around. + scoped_ptr nack(Nack::Create(kNackThreshold)); + nack->UpdateSampleRate(kSampleRateHz); + + uint16_t sequence_num_lost_packets[kNumAllLostPackets]; + for (int n = 0; n < kNumAllLostPackets; ++n) { + sequence_num_lost_packets[n] = kSequenceNumberLostPackets[n] + k * + 65531; // Wrap around for |k == 1|. + } + + uint16_t seq_num = sequence_num_lost_packets[0] - 1; + uint32_t timestamp = 0; + + nack->UpdateLastReceivedPacket(seq_num, timestamp); + std::vector nack_list = nack->GetNackList(kShortRoundTripTimeMs); + EXPECT_TRUE(nack_list.empty()); + + size_t index_retransmitted_rtp = 0; + uint32_t timestamp_retransmitted_rtp = timestamp + kTimestampIncrement; + + seq_num = sequence_num_lost_packets[kNumAllLostPackets - 1] + 1; + timestamp += kTimestampIncrement * (kNumAllLostPackets + 1); + size_t num_lost_packets = std::max(0, kNumAllLostPackets - kNackThreshold); + for (int n = 0; n < kNumAllLostPackets; ++n) { + // Number of lost packets does not change for the first + // |kNackThreshold + 1| packets, one is added to the list and one is + // removed. Thereafter, the list shrinks every iteration. + if (n >= kNackThreshold + 1) + num_lost_packets--; + + nack->UpdateLastReceivedPacket(seq_num, timestamp); + nack_list = nack->GetNackList(kShortRoundTripTimeMs); + EXPECT_TRUE(IsNackListCorrect( + nack_list, &sequence_num_lost_packets[index_retransmitted_rtp], + num_lost_packets)); + seq_num++; + timestamp += kTimestampIncrement; + + // Retransmission of a lost RTP. + nack->UpdateLastReceivedPacket( + sequence_num_lost_packets[index_retransmitted_rtp], + timestamp_retransmitted_rtp); + index_retransmitted_rtp++; + timestamp_retransmitted_rtp += kTimestampIncrement; + + nack_list = nack->GetNackList(kShortRoundTripTimeMs); + EXPECT_TRUE(IsNackListCorrect( + nack_list, &sequence_num_lost_packets[index_retransmitted_rtp], + num_lost_packets - 1)); // One less lost packet in the list. + } + ASSERT_TRUE(nack_list.empty()); + } +} + +// Assess if estimation of timestamps and time-to-play is correct. Introduce all +// combinations that timestamps and sequence numbers might have wrap around. +TEST(NackTest, EstimateTimestampAndTimeToPlay) { + const uint16_t kLostPackets[] = { 2, 3, 4, 5, 6, 7, 8, 9, 10, + 11, 12, 13, 14, 15 }; + static const int kNumAllLostPackets = sizeof(kLostPackets) / + sizeof(kLostPackets[0]); + + + for (int k = 0; k < 4; ++k) { + scoped_ptr nack(Nack::Create(kNackThreshold)); + nack->UpdateSampleRate(kSampleRateHz); + + // Sequence number wrap around if |k| is 2 or 3; + int seq_num_offset = (k < 2) ? 0 : 65531; + + // Timestamp wrap around if |k| is 1 or 3. + uint32_t timestamp_offset = (k & 0x1) ? + static_cast(0xffffffff) - 6 : 0; + + uint32_t timestamp_lost_packets[kNumAllLostPackets]; + uint16_t seq_num_lost_packets[kNumAllLostPackets]; + for (int n = 0; n < kNumAllLostPackets; ++n) { + timestamp_lost_packets[n] = timestamp_offset + kLostPackets[n] * + kTimestampIncrement; + seq_num_lost_packets[n] = seq_num_offset + kLostPackets[n]; + } + + // We and to push two packets before lost burst starts. + uint16_t seq_num = seq_num_lost_packets[0] - 2; + uint32_t timestamp = timestamp_lost_packets[0] - 2 * kTimestampIncrement; + + const uint16_t first_seq_num = seq_num; + const uint32_t first_timestamp = timestamp; + + // Two consecutive packets to have a correct estimate of timestamp increase. + nack->UpdateLastReceivedPacket(seq_num, timestamp); + seq_num++; + timestamp += kTimestampIncrement; + nack->UpdateLastReceivedPacket(seq_num, timestamp); + + // A packet after the last one which is supposed to be lost. + seq_num = seq_num_lost_packets[kNumAllLostPackets - 1] + 1; + timestamp = timestamp_lost_packets[kNumAllLostPackets - 1] + + kTimestampIncrement; + nack->UpdateLastReceivedPacket(seq_num, timestamp); + + Nack::NackList nack_list = nack->GetNackList(); + EXPECT_EQ(static_cast(kNumAllLostPackets), nack_list.size()); + + // Pretend the first packet is decoded. + nack->UpdateLastDecodedPacket(first_seq_num, first_timestamp); + nack_list = nack->GetNackList(); + + Nack::NackList::iterator it = nack_list.begin(); + while (it != nack_list.end()) { + seq_num = it->first - seq_num_offset; + int index = seq_num - kLostPackets[0]; + EXPECT_EQ(timestamp_lost_packets[index], it->second.estimated_timestamp); + EXPECT_EQ((index + 2) * kPacketSizeMs, it->second.time_to_play_ms); + ++it; + } + + // Pretend 10 ms is passed, and we had pulled audio from NetEq, it still + // reports the same sequence number as decoded, time-to-play should be + // updated by 10 ms. + nack->UpdateLastDecodedPacket(first_seq_num, first_timestamp); + nack_list = nack->GetNackList(); + it = nack_list.begin(); + while (it != nack_list.end()) { + seq_num = it->first - seq_num_offset; + int index = seq_num - kLostPackets[0]; + EXPECT_EQ((index + 2) * kPacketSizeMs - 10, it->second.time_to_play_ms); + ++it; + } + } +} + +TEST(NackTest, MissingPacketsPriorToLastDecodedRtpShouldNotBeInNackList) { + for (int m = 0; m < 2; ++m) { + uint16_t seq_num_offset = (m == 0) ? 0 : 65531; // Wrap around if |m| is 1. + scoped_ptr nack(Nack::Create(kNackThreshold)); + nack->UpdateSampleRate(kSampleRateHz); + + // Two consecutive packets to have a correct estimate of timestamp increase. + uint16_t seq_num = 0; + nack->UpdateLastReceivedPacket(seq_num_offset + seq_num, + seq_num * kTimestampIncrement); + seq_num++; + nack->UpdateLastReceivedPacket(seq_num_offset + seq_num, + seq_num * kTimestampIncrement); + + // Skip 10 packets (larger than NACK threshold). + const int kNumLostPackets = 10; + seq_num += kNumLostPackets + 1; + nack->UpdateLastReceivedPacket(seq_num_offset + seq_num, + seq_num * kTimestampIncrement); + + const size_t kExpectedListSize = kNumLostPackets - kNackThreshold; + std::vector nack_list = nack->GetNackList(kShortRoundTripTimeMs); + EXPECT_EQ(kExpectedListSize, nack_list.size()); + + for (int k = 0; k < 2; ++k) { + // Decoding of the first and the second arrived packets. + for (int n = 0; n < kPacketSizeMs / 10; ++n) { + nack->UpdateLastDecodedPacket(seq_num_offset + k, + k * kTimestampIncrement); + nack_list = nack->GetNackList(kShortRoundTripTimeMs); + EXPECT_EQ(kExpectedListSize, nack_list.size()); + } + } + + // Decoding of the last received packet. + nack->UpdateLastDecodedPacket(seq_num + seq_num_offset, + seq_num * kTimestampIncrement); + nack_list = nack->GetNackList(kShortRoundTripTimeMs); + EXPECT_TRUE(nack_list.empty()); + + // Make sure list of late packets is also empty. To check that, push few + // packets, if the late list is not empty its content will pop up in NACK + // list. + for (int n = 0; n < kNackThreshold + 10; ++n) { + seq_num++; + nack->UpdateLastReceivedPacket(seq_num_offset + seq_num, + seq_num * kTimestampIncrement); + nack_list = nack->GetNackList(kShortRoundTripTimeMs); + EXPECT_TRUE(nack_list.empty()); + } + } +} + +TEST(NackTest, Reset) { + scoped_ptr nack(Nack::Create(kNackThreshold)); + nack->UpdateSampleRate(kSampleRateHz); + + // Two consecutive packets to have a correct estimate of timestamp increase. + uint16_t seq_num = 0; + nack->UpdateLastReceivedPacket(seq_num, seq_num * kTimestampIncrement); + seq_num++; + nack->UpdateLastReceivedPacket(seq_num, seq_num * kTimestampIncrement); + + // Skip 10 packets (larger than NACK threshold). + const int kNumLostPackets = 10; + seq_num += kNumLostPackets + 1; + nack->UpdateLastReceivedPacket(seq_num, seq_num * kTimestampIncrement); + + const size_t kExpectedListSize = kNumLostPackets - kNackThreshold; + std::vector nack_list = nack->GetNackList(kShortRoundTripTimeMs); + EXPECT_EQ(kExpectedListSize, nack_list.size()); + + nack->Reset(); + nack_list = nack->GetNackList(kShortRoundTripTimeMs); + EXPECT_TRUE(nack_list.empty()); +} + +TEST(NackTest, ListSizeAppliedFromBeginning) { + const size_t kNackListSize = 10; + for (int m = 0; m < 2; ++m) { + uint16_t seq_num_offset = (m == 0) ? 0 : 65525; // Wrap around if |m| is 1. + scoped_ptr nack(Nack::Create(kNackThreshold)); + nack->UpdateSampleRate(kSampleRateHz); + nack->SetMaxNackListSize(kNackListSize); + + uint16_t seq_num = seq_num_offset; + uint32_t timestamp = 0x12345678; + nack->UpdateLastReceivedPacket(seq_num, timestamp); + + // Packet lost more than NACK-list size limit. + uint16_t num_lost_packets = kNackThreshold + kNackListSize + 5; + + seq_num += num_lost_packets + 1; + timestamp += (num_lost_packets + 1) * kTimestampIncrement; + nack->UpdateLastReceivedPacket(seq_num, timestamp); + + std::vector nack_list = nack->GetNackList(kShortRoundTripTimeMs); + EXPECT_EQ(kNackListSize - kNackThreshold, nack_list.size()); + } +} + +TEST(NackTest, ChangeOfListSizeAppliedAndOldElementsRemoved) { + const size_t kNackListSize = 10; + for (int m = 0; m < 2; ++m) { + uint16_t seq_num_offset = (m == 0) ? 0 : 65525; // Wrap around if |m| is 1. + scoped_ptr nack(Nack::Create(kNackThreshold)); + nack->UpdateSampleRate(kSampleRateHz); + + uint16_t seq_num = seq_num_offset; + uint32_t timestamp = 0x87654321; + nack->UpdateLastReceivedPacket(seq_num, timestamp); + + // Packet lost more than NACK-list size limit. + uint16_t num_lost_packets = kNackThreshold + kNackListSize + 5; + + scoped_array seq_num_lost(new uint16_t[num_lost_packets]); + for (int n = 0; n < num_lost_packets; ++n) { + seq_num_lost[n] = ++seq_num; + } + + ++seq_num; + timestamp += (num_lost_packets + 1) * kTimestampIncrement; + nack->UpdateLastReceivedPacket(seq_num, timestamp); + size_t expected_size = num_lost_packets - kNackThreshold; + + std::vector nack_list = nack->GetNackList(kShortRoundTripTimeMs); + EXPECT_EQ(expected_size, nack_list.size()); + + nack->SetMaxNackListSize(kNackListSize); + expected_size = kNackListSize - kNackThreshold; + nack_list = nack->GetNackList(kShortRoundTripTimeMs); + EXPECT_TRUE(IsNackListCorrect( + nack_list, &seq_num_lost[num_lost_packets - kNackListSize], + expected_size)); + + // NACK list does not change size but the content is changing. The oldest + // element is removed and one from late list is inserted. + size_t n; + for (n = 1; n <= static_cast(kNackThreshold); ++n) { + ++seq_num; + timestamp += kTimestampIncrement; + nack->UpdateLastReceivedPacket(seq_num, timestamp); + nack_list = nack->GetNackList(kShortRoundTripTimeMs); + EXPECT_TRUE(IsNackListCorrect( + nack_list, &seq_num_lost[num_lost_packets - kNackListSize + n], + expected_size)); + } + + // NACK list should shrink. + for (; n < kNackListSize; ++n) { + ++seq_num; + timestamp += kTimestampIncrement; + nack->UpdateLastReceivedPacket(seq_num, timestamp); + --expected_size; + nack_list = nack->GetNackList(kShortRoundTripTimeMs); + EXPECT_TRUE(IsNackListCorrect( + nack_list, &seq_num_lost[num_lost_packets - kNackListSize + n], + expected_size)); + } + + // After this packet, NACK list should be empty. + ++seq_num; + timestamp += kTimestampIncrement; + nack->UpdateLastReceivedPacket(seq_num, timestamp); + nack_list = nack->GetNackList(kShortRoundTripTimeMs); + EXPECT_TRUE(nack_list.empty()); + } +} + +TEST(NackTest, RoudTripTimeIsApplied) { + const int kNackListSize = 200; + scoped_ptr nack(Nack::Create(kNackThreshold)); + nack->UpdateSampleRate(kSampleRateHz); + nack->SetMaxNackListSize(kNackListSize); + + uint16_t seq_num = 0; + uint32_t timestamp = 0x87654321; + nack->UpdateLastReceivedPacket(seq_num, timestamp); + + // Packet lost more than NACK-list size limit. + uint16_t kNumLostPackets = kNackThreshold + 5; + + seq_num += (1 + kNumLostPackets); + timestamp += (1 + kNumLostPackets) * kTimestampIncrement; + nack->UpdateLastReceivedPacket(seq_num, timestamp); + + // Expected time-to-play are: + // kPacketSizeMs - 10, 2*kPacketSizeMs - 10, 3*kPacketSizeMs - 10, ... + // + // sequence number: 1, 2, 3, 4, 5 + // time-to-play: 20, 50, 80, 110, 140 + // + std::vector nack_list = nack->GetNackList(100); + ASSERT_EQ(2u, nack_list.size()); + EXPECT_EQ(4, nack_list[0]); + EXPECT_EQ(5, nack_list[1]); +} + +} // namespace webrtc