ACM2 integration with NetEq 4.
nack{.cc, .h, _unittest.cc} are basically copies from main/source/ folder, with cpplint warning cleaned up.
BUG=
R=andrew@webrtc.org
Review URL: https://webrtc-codereview.appspot.com/2190009
git-svn-id: http://webrtc.googlecode.com/svn/trunk@4736 4adac7df-926f-26a2-2b94-8c16560cd09d
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 0000000..75430f1
--- /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<int16_t*>(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<AMR_encinst_t_*>(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 0000000..c58b511
--- /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 0000000..1b82674
--- /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<int16_t*>(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<AMRWB_encinst_t_*>(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 0000000..550bab2
--- /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 0000000..6f2c807
--- /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<CELT_encinst_t*>(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 0000000..b90a4e8
--- /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 0000000..b04fd6a
--- /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<CNG_enc_inst*>(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 0000000..2ea4f02
--- /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 0000000..08080d1
--- /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 <assert.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"
+
+// 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 0000000..fb5cb9a
--- /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 0000000..b27256a
--- /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 <string.h>
+
+#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 0000000..b6b9102
--- /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 0000000..e16653c
--- /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 0000000..6ba0d7b
--- /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<int16_t*>(out_left));
+ len_in_bytes += WebRtcG722_Encode(encoder_inst_ptr_right_,
+ right_channel,
+ frame_len_smpl_,
+ reinterpret_cast<int16_t*>(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<int16_t*>(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<G722EncInst*>(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 0000000..21a0fdb
--- /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 0000000..65b34b0
--- /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 0000000..2b532db9
--- /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 0000000..b426d1f
--- /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 0000000..d051b28
--- /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 0000000..a2349ce
--- /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<int16_t*>(&(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<G729_encinst_t_*>(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 0000000..3b35f3b
--- /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 0000000..1c661c1
--- /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<int16_t*>(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 0000000..97601ea
--- /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 0000000..5e210ef
--- /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 <assert.h>
+#include <string.h>
+
+#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<int16_t>(
+ (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<int16_t>(
+ (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<int16_t>(freq_hz / 100);
+ int32_t frame_len_ms = static_cast<int32_t>(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<int16_t>(WebRtcVad_Process(ptr_vad_inst_,
+ static_cast<int>(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<uint16_t>(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 0000000..cde2e42
--- /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 0000000..9fd097c
--- /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<int16_t*>(bitstream));
+
+ // increment the read index this tell the caller that how far
+ // we have gone forward in reading the audio buffer
+ in_audio_ix_read_ += 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<GSMFR_encinst_t_*>(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 0000000..935ac44
--- /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 0000000..204e1e9
--- /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<int16_t*>(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<iLBC_encinst_t_*>(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 0000000..11e759c
--- /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 0000000..1c7e8b3
--- /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 <assert.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/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<ACM_ISAC_STRUCT*>(state_),
+ reinterpret_cast<const uint16_t*>(encoded),
+ static_cast<int16_t>(encoded_len), decoded,
+ &temp_type);
+ *speech_type = ConvertSpeechType(temp_type);
+ return ret;
+ }
+
+ virtual bool HasDecodePlc() const { return true; }
+
+ virtual int DecodePlc(int num_frames, int16_t* decoded) {
+ return ACM_ISAC_DECODEPLC(static_cast<ACM_ISAC_STRUCT*>(state_),
+ decoded, static_cast<int16_t>(num_frames));
+ }
+
+ virtual int Init() {
+ return 0; // We expect that the initialized instance is injected in the
+ // constructor.
+ }
+
+ virtual int IncomingPacket(const uint8_t* payload,
+ size_t payload_len,
+ uint16_t rtp_sequence_number,
+ uint32_t rtp_timestamp,
+ uint32_t arrival_timestamp) {
+ return ACM_ISAC_DECODE_BWE(static_cast<ACM_ISAC_STRUCT*>(state_),
+ reinterpret_cast<const uint16_t*>(payload),
+ payload_len,
+ rtp_sequence_number,
+ rtp_timestamp,
+ arrival_timestamp);
+ }
+
+ virtual int DecodeRedundant(const uint8_t* encoded,
+ size_t encoded_len, int16_t* decoded,
+ SpeechType* speech_type) {
+ int16_t temp_type = 1; // Default is speech.
+ int16_t ret = ACM_ISAC_DECODERCU(static_cast<ISACStruct*>(state_),
+ reinterpret_cast<const uint16_t*>(encoded),
+ static_cast<int16_t>(encoded_len), decoded,
+ &temp_type);
+ *speech_type = ConvertSpeechType(temp_type);
+ return ret;
+ }
+
+ virtual int ErrorCode() {
+ return ACM_ISAC_GETERRORCODE(static_cast<ACM_ISAC_STRUCT*>(state_));
+ }
+
+ private:
+ DISALLOW_COPY_AND_ASSIGN(AcmAudioDecoderIsac);
+};
+
+ACMISAC::ACMISAC(int16_t codec_id)
+ : is_enc_initialized_(false),
+ isac_coding_mode_(CHANNEL_INDEPENDENT),
+ enforce_frame_size_(false),
+ isac_current_bn_(32000),
+ samples_in_10ms_audio_(160), // Initiates to 16 kHz mode.
+ audio_decoder_(NULL),
+ decoder_initialized_(false) {
+ codec_id_ = codec_id;
+
+ // Create codec instance.
+ codec_inst_ptr_ = new ACMISACInst;
+ if (codec_inst_ptr_ == NULL) {
+ return;
+ }
+ codec_inst_ptr_->inst = NULL;
+}
+
+ACMISAC::~ACMISAC() {
+ if (audio_decoder_ != NULL) {
+ delete audio_decoder_;
+ audio_decoder_ = NULL;
+ }
+
+ if (codec_inst_ptr_ != NULL) {
+ if (codec_inst_ptr_->inst != NULL) {
+ ACM_ISAC_FREE(codec_inst_ptr_->inst);
+ codec_inst_ptr_->inst = NULL;
+ }
+ delete codec_inst_ptr_;
+ codec_inst_ptr_ = NULL;
+ }
+ return;
+}
+
+ACMGenericCodec* ACMISAC::CreateInstance(void) { return NULL; }
+
+int16_t ACMISAC::InternalEncode(uint8_t* bitstream,
+ int16_t* bitstream_len_byte) {
+ // ISAC takes 10ms audio every time we call encoder, therefore,
+ // it should be treated like codecs with 'basic coding block'
+ // non-zero, and the following 'while-loop' should not be necessary.
+ // However, due to a mistake in the codec the frame-size might change
+ // at the first 10ms pushed in to iSAC if the bit-rate is low, this is
+ // sort of a bug in iSAC. to address this we treat iSAC as the
+ // following.
+ if (codec_inst_ptr_ == NULL) {
+ return -1;
+ }
+ *bitstream_len_byte = 0;
+ while ((*bitstream_len_byte == 0) && (in_audio_ix_read_ < frame_len_smpl_)) {
+ if (in_audio_ix_read_ > in_audio_ix_write_) {
+ // something is wrong.
+ WEBRTC_TRACE(webrtc::kTraceError, webrtc::kTraceAudioCoding, unique_id_,
+ "The actual frame-size of iSAC appears to be larger that "
+ "expected. All audio pushed in but no bit-stream is "
+ "generated.");
+ return -1;
+ }
+ *bitstream_len_byte = ACM_ISAC_ENCODE(
+ codec_inst_ptr_->inst, &in_audio_[in_audio_ix_read_],
+ reinterpret_cast<int16_t*>(bitstream));
+ // increment the read index this tell the caller that how far
+ // we have gone forward in reading the audio buffer
+ in_audio_ix_read_ += samples_in_10ms_audio_;
+ }
+ if (*bitstream_len_byte == 0) {
+ WEBRTC_TRACE(webrtc::kTraceWarning, webrtc::kTraceAudioCoding, unique_id_,
+ "ISAC Has encoded the whole frame but no bit-stream is "
+ "generated.");
+ }
+
+ // a packet is generated iSAC, is set in adaptive mode may change
+ // the frame length and we like to update the bottleneck value as
+ // well, although updating bottleneck is not crucial
+ if ((*bitstream_len_byte > 0) && (isac_coding_mode_ == ADAPTIVE)) {
+ ACM_ISAC_GETSENDBITRATE(codec_inst_ptr_->inst, &isac_current_bn_);
+ }
+ UpdateFrameLen();
+ return *bitstream_len_byte;
+}
+
+int16_t ACMISAC::InternalInitEncoder(WebRtcACMCodecParams* codec_params) {
+ // if rate is set to -1 then iSAC has to be in adaptive mode
+ if (codec_params->codec_inst.rate == -1) {
+ isac_coding_mode_ = ADAPTIVE;
+ } else if ((codec_params->codec_inst.rate >= ISAC_MIN_RATE) &&
+ (codec_params->codec_inst.rate <= ISAC_MAX_RATE)) {
+ // sanity check that rate is in acceptable range
+ isac_coding_mode_ = CHANNEL_INDEPENDENT;
+ isac_current_bn_ = codec_params->codec_inst.rate;
+ } else {
+ return -1;
+ }
+
+ // we need to set the encoder sampling frequency.
+ if (UpdateEncoderSampFreq((uint16_t)codec_params->codec_inst.plfreq) < 0) {
+ return -1;
+ }
+ if (ACM_ISAC_ENCODERINIT(codec_inst_ptr_->inst, isac_coding_mode_) < 0) {
+ return -1;
+ }
+
+ // apply the frame-size and rate if operating in
+ // channel-independent mode
+ if (isac_coding_mode_ == CHANNEL_INDEPENDENT) {
+ if (ACM_ISAC_CONTROL(codec_inst_ptr_->inst,
+ codec_params->codec_inst.rate,
+ codec_params->codec_inst.pacsize /
+ (codec_params->codec_inst.plfreq / 1000)) < 0) {
+ return -1;
+ }
+ } else {
+ // We need this for adaptive case and has to be called
+ // after initialization
+ ACM_ISAC_GETSENDBITRATE(codec_inst_ptr_->inst, &isac_current_bn_);
+ }
+ frame_len_smpl_ = ACM_ISAC_GETNEWFRAMELEN(codec_inst_ptr_->inst);
+ return 0;
+}
+
+int16_t ACMISAC::InternalInitDecoder(WebRtcACMCodecParams* codec_params) {
+ if (codec_inst_ptr_ == NULL) {
+ return -1;
+ }
+
+ // set decoder sampling frequency.
+ if (codec_params->codec_inst.plfreq == 32000 ||
+ codec_params->codec_inst.plfreq == 48000) {
+ UpdateDecoderSampFreq(ACMCodecDB::kISACSWB);
+ } else {
+ UpdateDecoderSampFreq(ACMCodecDB::kISAC);
+ }
+
+ // in a one-way communication we may never register send-codec.
+ // However we like that the BWE to work properly so it has to
+ // be initialized. The BWE is initialized when iSAC encoder is initialized.
+ // Therefore, we need this.
+ if (!encoder_initialized_) {
+ // Since we don't require a valid rate or a valid packet size when
+ // initializing the decoder, we set valid values before initializing encoder
+ codec_params->codec_inst.rate = kIsacWbDefaultRate;
+ codec_params->codec_inst.pacsize = kIsacPacSize960;
+ if (InternalInitEncoder(codec_params) < 0) {
+ return -1;
+ }
+ encoder_initialized_ = true;
+ }
+
+ return ACM_ISAC_DECODERINIT(codec_inst_ptr_->inst);
+}
+
+int16_t ACMISAC::InternalCreateEncoder() {
+ if (codec_inst_ptr_ == NULL) {
+ return -1;
+ }
+ decoder_initialized_ = false;
+ int16_t status = ACM_ISAC_CREATE(&(codec_inst_ptr_->inst));
+
+ if (status < 0)
+ codec_inst_ptr_->inst = NULL;
+ return status;
+}
+
+void ACMISAC::DestructEncoderSafe() {
+ // codec with shared instance cannot delete.
+ encoder_initialized_ = false;
+ return;
+}
+
+void ACMISAC::InternalDestructEncoderInst(void* ptr_inst) {
+ if (ptr_inst != NULL) {
+ ACM_ISAC_FREE(static_cast<ACM_ISAC_STRUCT *>(ptr_inst));
+ }
+ return;
+}
+
+int16_t ACMISAC::Transcode(uint8_t* bitstream,
+ int16_t* bitstream_len_byte,
+ int16_t q_bwe,
+ int32_t rate,
+ bool is_red) {
+ int16_t jitter_info = 0;
+ // transcode from a higher rate to lower rate sanity check
+ if (codec_inst_ptr_ == NULL) {
+ return -1;
+ }
+
+ *bitstream_len_byte = ACM_ISAC_GETNEWBITSTREAM(
+ codec_inst_ptr_->inst, q_bwe, jitter_info, rate,
+ reinterpret_cast<int16_t*>(bitstream), (is_red) ? 1 : 0);
+
+ if (*bitstream_len_byte < 0) {
+ // error happened
+ *bitstream_len_byte = 0;
+ return -1;
+ } else {
+ return *bitstream_len_byte;
+ }
+}
+
+int16_t ACMISAC::SetBitRateSafe(int32_t bit_rate) {
+ if (codec_inst_ptr_ == NULL) {
+ return -1;
+ }
+ uint16_t encoder_samp_freq;
+ EncoderSampFreq(&encoder_samp_freq);
+ bool reinit = false;
+ // change the BN of iSAC
+ if (bit_rate == -1) {
+ // ADAPTIVE MODE
+ // Check if it was already in adaptive mode
+ if (isac_coding_mode_ != ADAPTIVE) {
+ // was not in adaptive, then set the mode to adaptive
+ // and flag for re-initialization
+ isac_coding_mode_ = ADAPTIVE;
+ reinit = true;
+ }
+ } else if ((bit_rate >= ISAC_MIN_RATE) && (bit_rate <= ISAC_MAX_RATE)) {
+ // Sanity check if the rate valid
+ // check if it was in channel-independent mode before
+ if (isac_coding_mode_ != CHANNEL_INDEPENDENT) {
+ // was not in channel independent, set the mode to
+ // channel-independent and flag for re-initialization
+ isac_coding_mode_ = CHANNEL_INDEPENDENT;
+ reinit = true;
+ }
+ // store the bottleneck
+ isac_current_bn_ = (uint16_t)bit_rate;
+ } else {
+ // invlaid rate
+ return -1;
+ }
+
+ int16_t status = 0;
+ if (reinit) {
+ // initialize and check if it is successful
+ if (ACM_ISAC_ENCODERINIT(codec_inst_ptr_->inst, isac_coding_mode_) < 0) {
+ // failed initialization
+ return -1;
+ }
+ }
+ if (isac_coding_mode_ == CHANNEL_INDEPENDENT) {
+ status = ACM_ISAC_CONTROL(
+ codec_inst_ptr_->inst, isac_current_bn_,
+ (encoder_samp_freq == 32000 || encoder_samp_freq == 48000) ? 30 :
+ (frame_len_smpl_ / 16));
+ if (status < 0) {
+ status = -1;
+ }
+ }
+
+ // Update encoder parameters
+ encoder_params_.codec_inst.rate = bit_rate;
+
+ UpdateFrameLen();
+ return status;
+}
+
+int32_t ACMISAC::GetEstimatedBandwidthSafe() {
+ int16_t bandwidth_index = 0;
+ int16_t delay_index = 0;
+ int samp_rate;
+
+ // Get bandwidth information
+ ACM_ISAC_GETSENDBWE(codec_inst_ptr_->inst, &bandwidth_index, &delay_index);
+
+ // Validy check of index
+ if ((bandwidth_index < 0) || (bandwidth_index >= NR_ISAC_BANDWIDTHS)) {
+ return -1;
+ }
+
+ // Check sample frequency
+ samp_rate = ACM_ISAC_GETDECSAMPRATE(codec_inst_ptr_->inst);
+ if (samp_rate == 16000) {
+ return kIsacRatesWb[bandwidth_index];
+ } else {
+ return kIsacRatesSwb[bandwidth_index];
+ }
+}
+
+int32_t ACMISAC::SetEstimatedBandwidthSafe(int32_t estimated_bandwidth) {
+ int samp_rate;
+ int16_t bandwidth_index;
+
+ // Check sample frequency and choose appropriate table
+ samp_rate = ACM_ISAC_GETENCSAMPRATE(codec_inst_ptr_->inst);
+
+ if (samp_rate == 16000) {
+ // Search through the WB rate table to find the index
+ bandwidth_index = NR_ISAC_BANDWIDTHS / 2 - 1;
+ for (int i = 0; i < (NR_ISAC_BANDWIDTHS / 2); i++) {
+ if (estimated_bandwidth == kIsacRatesWb[i]) {
+ bandwidth_index = i;
+ break;
+ } else if (estimated_bandwidth
+ == kIsacRatesWb[i + NR_ISAC_BANDWIDTHS / 2]) {
+ bandwidth_index = i + NR_ISAC_BANDWIDTHS / 2;
+ break;
+ } else if (estimated_bandwidth < kIsacRatesWb[i]) {
+ bandwidth_index = i;
+ break;
+ }
+ }
+ } else {
+ // Search through the SWB rate table to find the index
+ bandwidth_index = NR_ISAC_BANDWIDTHS - 1;
+ for (int i = 0; i < NR_ISAC_BANDWIDTHS; i++) {
+ if (estimated_bandwidth <= kIsacRatesSwb[i]) {
+ bandwidth_index = i;
+ break;
+ }
+ }
+ }
+
+ // Set iSAC Bandwidth Estimate
+ ACM_ISAC_SETBWE(codec_inst_ptr_->inst, bandwidth_index);
+
+ return 0;
+}
+
+int32_t ACMISAC::GetRedPayloadSafe(
+#if (!defined(WEBRTC_CODEC_ISAC))
+ uint8_t* /* red_payload */,
+ int16_t* /* payload_bytes */) {
+ return -1;
+#else
+ uint8_t* red_payload, int16_t* payload_bytes) {
+ int16_t bytes =
+ WebRtcIsac_GetRedPayload(
+ codec_inst_ptr_->inst, reinterpret_cast<int16_t*>(red_payload));
+ if (bytes < 0) {
+ return -1;
+ }
+ *payload_bytes = bytes;
+ return 0;
+#endif
+}
+
+int16_t ACMISAC::UpdateDecoderSampFreq(
+#ifdef WEBRTC_CODEC_ISAC
+ int16_t codec_id) {
+ // The decoder supports only wideband and super-wideband.
+ if (ACMCodecDB::kISAC == codec_id) {
+ return WebRtcIsac_SetDecSampRate(codec_inst_ptr_->inst, 16000);
+ } else if (ACMCodecDB::kISACSWB == codec_id ||
+ ACMCodecDB::kISACFB == codec_id) {
+ return WebRtcIsac_SetDecSampRate(codec_inst_ptr_->inst, 32000);
+ } else {
+ return -1;
+ }
+#else
+ int16_t /* codec_id */) {
+ return 0;
+#endif
+}
+
+int16_t ACMISAC::UpdateEncoderSampFreq(
+#ifdef WEBRTC_CODEC_ISAC
+ uint16_t encoder_samp_freq_hz) {
+ uint16_t current_samp_rate_hz;
+ EncoderSampFreq(¤t_samp_rate_hz);
+
+ if (current_samp_rate_hz != encoder_samp_freq_hz) {
+ if ((encoder_samp_freq_hz != 16000) && (encoder_samp_freq_hz != 32000) &&
+ (encoder_samp_freq_hz != 48000)) {
+ return -1;
+ } else {
+ in_audio_ix_read_ = 0;
+ in_audio_ix_write_ = 0;
+ in_timestamp_ix_write_ = 0;
+ if (WebRtcIsac_SetEncSampRate(codec_inst_ptr_->inst,
+ encoder_samp_freq_hz) < 0) {
+ return -1;
+ }
+ samples_in_10ms_audio_ = encoder_samp_freq_hz / 100;
+ frame_len_smpl_ = ACM_ISAC_GETNEWFRAMELEN(codec_inst_ptr_->inst);
+ encoder_params_.codec_inst.pacsize = frame_len_smpl_;
+ encoder_params_.codec_inst.plfreq = encoder_samp_freq_hz;
+ return 0;
+ }
+ }
+#else
+ uint16_t /* codec_id */) {
+#endif
+ return 0;
+}
+
+int16_t ACMISAC::EncoderSampFreq(uint16_t* samp_freq_hz) {
+ *samp_freq_hz = ACM_ISAC_GETENCSAMPRATE(codec_inst_ptr_->inst);
+ return 0;
+}
+
+int32_t ACMISAC::ConfigISACBandwidthEstimator(
+ const uint8_t init_frame_size_msec,
+ const uint16_t init_rate_bit_per_sec,
+ const bool enforce_frame_size) {
+ int16_t status;
+ {
+ uint16_t samp_freq_hz;
+ EncoderSampFreq(&samp_freq_hz);
+ // TODO(turajs): at 32kHz we hardcode calling with 30ms and enforce
+ // the frame-size otherwise we might get error. Revise if
+ // control-bwe is changed.
+ if (samp_freq_hz == 32000 || samp_freq_hz == 48000) {
+ status = ACM_ISAC_CONTROL_BWE(codec_inst_ptr_->inst,
+ init_rate_bit_per_sec, 30, 1);
+ } else {
+ status = ACM_ISAC_CONTROL_BWE(codec_inst_ptr_->inst,
+ init_rate_bit_per_sec,
+ init_frame_size_msec,
+ enforce_frame_size ? 1 : 0);
+ }
+ }
+ if (status < 0) {
+ WEBRTC_TRACE(webrtc::kTraceError, webrtc::kTraceAudioCoding, unique_id_,
+ "Couldn't config iSAC BWE.");
+ return -1;
+ }
+ UpdateFrameLen();
+ ACM_ISAC_GETSENDBITRATE(codec_inst_ptr_->inst, &isac_current_bn_);
+ return 0;
+}
+
+int32_t ACMISAC::SetISACMaxPayloadSize(const uint16_t max_payload_len_bytes) {
+ return ACM_ISAC_SETMAXPAYLOADSIZE(codec_inst_ptr_->inst,
+ max_payload_len_bytes);
+}
+
+int32_t ACMISAC::SetISACMaxRate(const uint32_t max_rate_bit_per_sec) {
+ return ACM_ISAC_SETMAXRATE(codec_inst_ptr_->inst, max_rate_bit_per_sec);
+}
+
+void ACMISAC::UpdateFrameLen() {
+ frame_len_smpl_ = ACM_ISAC_GETNEWFRAMELEN(codec_inst_ptr_->inst);
+ encoder_params_.codec_inst.pacsize = frame_len_smpl_;
+}
+
+void ACMISAC::CurrentRate(int32_t* rate_bit_per_sec) {
+ if (isac_coding_mode_ == ADAPTIVE) {
+ ACM_ISAC_GETSENDBITRATE(codec_inst_ptr_->inst, rate_bit_per_sec);
+ }
+}
+
+int16_t ACMISAC::REDPayloadISAC(const int32_t isac_rate,
+ const int16_t isac_bw_estimate,
+ uint8_t* payload,
+ int16_t* payload_len_bytes) {
+ int16_t status;
+ ReadLockScoped rl(codec_wrapper_lock_);
+ status =
+ Transcode(payload, payload_len_bytes, isac_bw_estimate, isac_rate, true);
+ return status;
+}
+
+AudioDecoder* ACMISAC::Decoder(int codec_id) {
+ if (audio_decoder_)
+ return audio_decoder_;
+
+ // Create iSAC instance if it does not exist.
+ if (!encoder_exist_) {
+ assert(codec_inst_ptr_->inst == NULL);
+ encoder_initialized_ = false;
+ decoder_initialized_ = false;
+ if (ACM_ISAC_CREATE(&(codec_inst_ptr_->inst)) < 0) {
+ codec_inst_ptr_->inst = NULL;
+ return NULL;
+ }
+ encoder_exist_ = true;
+ }
+
+ WebRtcACMCodecParams codec_params;
+ if (!encoder_initialized_ || !decoder_initialized_) {
+ ACMCodecDB::Codec(codec_id, &codec_params.codec_inst);
+ // The following three values are not used but we set them to valid values.
+ codec_params.enable_dtx = false;
+ codec_params.enable_vad = false;
+ codec_params.vad_mode = VADNormal;
+ }
+
+ if (!encoder_initialized_) {
+ // Initialize encoder to make sure bandwidth estimator works.
+ if (InternalInitEncoder(&codec_params) < 0)
+ return NULL;
+ encoder_initialized_ = true;
+ }
+
+ if (!decoder_initialized_) {
+ if (InternalInitDecoder(&codec_params) < 0)
+ return NULL;
+ decoder_initialized_ = true;
+ }
+
+ audio_decoder_ = new AcmAudioDecoderIsac(codec_id, codec_inst_ptr_->inst);
+ return audio_decoder_;
+}
+
+#endif
+
+} // namespace webrtc
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 0000000..f4cf1a6
--- /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 0000000..646b3cc
--- /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 0000000..3f5a200
--- /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 0000000..a738085
--- /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<OpusEncInst*>(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 0000000..28b08b6
--- /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 0000000..3bc9642
--- /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 0000000..a7fff0f
--- /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 0000000..0d574fe
--- /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<int16_t*>(bitstream));
+ // Increment the read index this tell the caller that how far
+ // we have gone forward in reading the audio buffer.
+ in_audio_ix_read_ += 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 0000000..61386d3
--- /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 0000000..441e3dd
--- /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<int16_t*>(bitstream));
+
+ // Increment the read index this tell the caller that how far
+ // we have gone forward in reading the audio buffer.
+ in_audio_ix_read_ += 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 0000000..832a00d
--- /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 0000000..fb3fe3e
--- /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 <stdlib.h> // malloc
+
+#include <algorithm> // sort
+#include <vector>
+
+#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<int> 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<int>(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<uint16_t> 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<uint16_t> 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<uint32_t>(
+ TickTime::MillisecondTimestamp() & 0x03ffffff);
+ return static_cast<uint32_t>(
+ (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 0000000..344e1c9
--- /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 <vector>
+
+#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<uint16_t> 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> 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<InitialDelayManager> 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<InitialDelayManager::SyncStream> missing_packets_sync_stream_;
+ scoped_ptr<InitialDelayManager::SyncStream> 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 0000000..ab652cf
--- /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 <algorithm> // 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<AcmReceiver> 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 0000000..5b5b16f
--- /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 0000000..c8023db
--- /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 0000000..d399cee
--- /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 <string.h>
+
+#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<size_t>(in_freq_hz * num_audio_channels / 100);
+ memcpy(out_audio, in_audio, length * sizeof(int16_t));
+ return static_cast<int16_t>(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 0000000..c44fbc4
--- /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 0000000..80dcf5c2
--- /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<int16_t*>(bitstream));
+ return *bitstream_len_byte;
+ }
+ }
+
+ *bitstream_len_byte = WebRtcSpeex_GetBitstream(
+ encoder_inst_ptr_, reinterpret_cast<int16_t*>(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<SPEEX_encinst_t_*>(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 0000000..68953a8
--- /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 0000000..97d5d46
--- /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<AudioCodingModuleImpl*>(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 0000000..8b0fbe1
--- /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 0000000..f5fac73
--- /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 <assert.h>
+#include <stdlib.h>
+#include <vector>
+
+#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<uint32_t>(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<uint8_t>(ACMCodecDB::database_[i].pltype);
+ } else if (IsCodecCN(i)) {
+ if (ACMCodecDB::database_[i].plfreq == 8000) {
+ cng_nb_pltype_ = static_cast<uint8_t>(ACMCodecDB::database_[i].pltype);
+ } else if (ACMCodecDB::database_[i].plfreq == 16000) {
+ cng_wb_pltype_ = static_cast<uint8_t>(ACMCodecDB::database_[i].pltype);
+ } else if (ACMCodecDB::database_[i].plfreq == 32000) {
+ cng_swb_pltype_ = static_cast<uint8_t>(ACMCodecDB::database_[i].pltype);
+ } else if (ACMCodecDB::database_[i].plfreq == 48000) {
+ cng_fb_pltype_ = static_cast<uint8_t>(ACMCodecDB::database_[i].pltype);
+ }
+ }
+ }
+
+ if (InitializeReceiverSafe() < 0) {
+ WEBRTC_TRACE(webrtc::kTraceError, webrtc::kTraceAudioCoding, id_,
+ "Cannot initialize receiver");
+ }
+ 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<uint16_t>(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<int>(
+ 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<uint16_t>(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<uint16_t>(
+ 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<int16_t>(
+ 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<uint8_t>(send_codec.pltype);
+ return 0;
+ }
+
+ // CNG can be registered with other payload type. If not registered the
+ // default payload types from codec database will be used.
+ if (IsCodecCN(&send_codec)) {
+ // CNG is registered.
+ switch (send_codec.plfreq) {
+ case 8000: {
+ cng_nb_pltype_ = static_cast<uint8_t>(send_codec.pltype);
+ break;
+ }
+ case 16000: {
+ cng_wb_pltype_ = static_cast<uint8_t>(send_codec.pltype);
+ break;
+ }
+ case 32000: {
+ cng_swb_pltype_ = static_cast<uint8_t>(send_codec.pltype);
+ break;
+ }
+ case 48000: {
+ cng_fb_pltype_ = static_cast<uint8_t>(send_codec.pltype);
+ break;
+ }
+ default: {
+ WEBRTC_TRACE(webrtc::kTraceError, webrtc::kTraceAudioCoding, id_,
+ "RegisterSendCodec() failed, invalid frequency for CNG "
+ "registration");
+ return -1;
+ }
+ }
+ 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<uint32_t>((static_cast<double>(send_codec_inst_.plfreq) /
+ static_cast<double>(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<uint8_t>(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<uint8_t>(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<uint16_t>(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<uint16_t> 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 0000000..b9c70e9
--- /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 <vector>
+
+#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<uint16_t> 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<ACMGenericCodec> 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 0000000..dffed64
--- /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<uint32_t>(
+ 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 0000000..5c8ae18
--- /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 0000000..9d96d17
--- /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 <cstring>
+
+#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<InitialDelayManager> 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 0000000..d4d0c3b
--- /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 <assert.h> // For assert.
+
+#include <algorithm> // 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<uint16_t>(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<uint16_t>(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<uint16_t> Nack::GetNackList(int round_trip_time_ms) const {
+ std::vector<uint16_t> 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 0000000..ddafbcd
--- /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 <vector>
+#include <map>
+
+#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<uint16_t> 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<uint16_t, NackElement, NackListCompare> 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 0000000..b84211d
--- /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 <stdint.h>
+
+#include <algorithm>
+
+#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<uint16_t>& 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(Nack::Create(kNackThreshold));
+ nack->UpdateSampleRate(kSampleRateHz);
+
+ int seq_num = 1;
+ uint32_t timestamp = 0;
+
+ std::vector<uint16_t> 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(Nack::Create(kNackThreshold));
+ nack->UpdateSampleRate(kSampleRateHz);
+
+ int seq_num = 1;
+ uint32_t timestamp = 0;
+ std::vector<uint16_t> 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(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<uint16_t> 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(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<uint16_t> 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(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<uint32_t>(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<size_t>(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(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<uint16_t> 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(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<uint16_t> 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(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<uint16_t> 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(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<uint16_t> 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<uint16_t> 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<size_t>(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(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<uint16_t> 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