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
This commit is contained in:
parent
82a846f0cb
commit
7959e16cc2
310
webrtc/modules/audio_coding/main/acm2/acm_amr.cc
Normal file
310
webrtc/modules/audio_coding/main/acm2/acm_amr.cc
Normal file
@ -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
|
65
webrtc/modules/audio_coding/main/acm2/acm_amr.h
Normal file
65
webrtc/modules/audio_coding/main/acm2/acm_amr.h
Normal file
@ -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_
|
316
webrtc/modules/audio_coding/main/acm2/acm_amrwb.cc
Normal file
316
webrtc/modules/audio_coding/main/acm2/acm_amrwb.cc
Normal file
@ -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
|
66
webrtc/modules/audio_coding/main/acm2/acm_amrwb.h
Normal file
66
webrtc/modules/audio_coding/main/acm2/acm_amrwb.h
Normal file
@ -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_
|
191
webrtc/modules/audio_coding/main/acm2/acm_celt.cc
Normal file
191
webrtc/modules/audio_coding/main/acm2/acm_celt.cc
Normal file
@ -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
|
50
webrtc/modules/audio_coding/main/acm2/acm_celt.h
Normal file
50
webrtc/modules/audio_coding/main/acm2/acm_celt.h
Normal file
@ -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_
|
79
webrtc/modules/audio_coding/main/acm2/acm_cng.cc
Normal file
79
webrtc/modules/audio_coding/main/acm2/acm_cng.cc
Normal file
@ -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
|
56
webrtc/modules/audio_coding/main/acm2/acm_cng.h
Normal file
56
webrtc/modules/audio_coding/main/acm2/acm_cng.h
Normal file
@ -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_
|
964
webrtc/modules/audio_coding/main/acm2/acm_codec_database.cc
Normal file
964
webrtc/modules/audio_coding/main/acm2/acm_codec_database.cc
Normal file
@ -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
|
350
webrtc/modules/audio_coding/main/acm2/acm_codec_database.h
Normal file
350
webrtc/modules/audio_coding/main/acm2/acm_codec_database.h
Normal file
@ -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_
|
98
webrtc/modules/audio_coding/main/acm2/acm_common_defs.h
Normal file
98
webrtc/modules/audio_coding/main/acm2/acm_common_defs.h
Normal file
@ -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_
|
86
webrtc/modules/audio_coding/main/acm2/acm_dtmf_playout.cc
Normal file
86
webrtc/modules/audio_coding/main/acm2/acm_dtmf_playout.cc
Normal file
@ -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
|
40
webrtc/modules/audio_coding/main/acm2/acm_dtmf_playout.h
Normal file
40
webrtc/modules/audio_coding/main/acm2/acm_dtmf_playout.h
Normal file
@ -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_
|
197
webrtc/modules/audio_coding/main/acm2/acm_g722.cc
Normal file
197
webrtc/modules/audio_coding/main/acm2/acm_g722.cc
Normal file
@ -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
|
57
webrtc/modules/audio_coding/main/acm2/acm_g722.h
Normal file
57
webrtc/modules/audio_coding/main/acm2/acm_g722.h
Normal file
@ -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_
|
326
webrtc/modules/audio_coding/main/acm2/acm_g7221.cc
Normal file
326
webrtc/modules/audio_coding/main/acm2/acm_g7221.cc
Normal file
@ -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
|
62
webrtc/modules/audio_coding/main/acm2/acm_g7221.h
Normal file
62
webrtc/modules/audio_coding/main/acm2/acm_g7221.h
Normal file
@ -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_
|
332
webrtc/modules/audio_coding/main/acm2/acm_g7221c.cc
Normal file
332
webrtc/modules/audio_coding/main/acm2/acm_g7221c.cc
Normal file
@ -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
|
62
webrtc/modules/audio_coding/main/acm2/acm_g7221c.h
Normal file
62
webrtc/modules/audio_coding/main/acm2/acm_g7221c.h
Normal file
@ -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_
|
255
webrtc/modules/audio_coding/main/acm2/acm_g729.cc
Normal file
255
webrtc/modules/audio_coding/main/acm2/acm_g729.cc
Normal file
@ -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
|
54
webrtc/modules/audio_coding/main/acm2/acm_g729.h
Normal file
54
webrtc/modules/audio_coding/main/acm2/acm_g729.h
Normal file
@ -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_
|
240
webrtc/modules/audio_coding/main/acm2/acm_g7291.cc
Normal file
240
webrtc/modules/audio_coding/main/acm2/acm_g7291.cc
Normal file
@ -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
|
52
webrtc/modules/audio_coding/main/acm2/acm_g7291.h
Normal file
52
webrtc/modules/audio_coding/main/acm2/acm_g7291.h
Normal file
@ -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_
|
995
webrtc/modules/audio_coding/main/acm2/acm_generic_codec.cc
Normal file
995
webrtc/modules/audio_coding/main/acm2/acm_generic_codec.cc
Normal file
@ -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
|
918
webrtc/modules/audio_coding/main/acm2/acm_generic_codec.h
Normal file
918
webrtc/modules/audio_coding/main/acm2/acm_generic_codec.h
Normal file
@ -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_
|
157
webrtc/modules/audio_coding/main/acm2/acm_gsmfr.cc
Normal file
157
webrtc/modules/audio_coding/main/acm2/acm_gsmfr.cc
Normal file
@ -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
|
50
webrtc/modules/audio_coding/main/acm2/acm_gsmfr.h
Normal file
50
webrtc/modules/audio_coding/main/acm2/acm_gsmfr.h
Normal file
@ -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_
|
141
webrtc/modules/audio_coding/main/acm2/acm_ilbc.cc
Normal file
141
webrtc/modules/audio_coding/main/acm2/acm_ilbc.cc
Normal file
@ -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
|
48
webrtc/modules/audio_coding/main/acm2/acm_ilbc.h
Normal file
48
webrtc/modules/audio_coding/main/acm2/acm_ilbc.h
Normal file
@ -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_
|
829
webrtc/modules/audio_coding/main/acm2/acm_isac.cc
Normal file
829
webrtc/modules/audio_coding/main/acm2/acm_isac.cc
Normal file
@ -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
|
98
webrtc/modules/audio_coding/main/acm2/acm_isac.h
Normal file
98
webrtc/modules/audio_coding/main/acm2/acm_isac.h
Normal file
@ -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_
|
76
webrtc/modules/audio_coding/main/acm2/acm_isac_macros.h
Normal file
76
webrtc/modules/audio_coding/main/acm2/acm_isac_macros.h
Normal file
@ -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_
|
||||
|
13
webrtc/modules/audio_coding/main/acm2/acm_neteq_unittest.cc
Normal file
13
webrtc/modules/audio_coding/main/acm2/acm_neteq_unittest.cc
Normal file
@ -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
|
187
webrtc/modules/audio_coding/main/acm2/acm_opus.cc
Normal file
187
webrtc/modules/audio_coding/main/acm2/acm_opus.cc
Normal file
@ -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
|
50
webrtc/modules/audio_coding/main/acm2/acm_opus.h
Normal file
50
webrtc/modules/audio_coding/main/acm2/acm_opus.h
Normal file
@ -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_
|
92
webrtc/modules/audio_coding/main/acm2/acm_pcm16b.cc
Normal file
92
webrtc/modules/audio_coding/main/acm2/acm_pcm16b.cc
Normal file
@ -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
|
42
webrtc/modules/audio_coding/main/acm2/acm_pcm16b.h
Normal file
42
webrtc/modules/audio_coding/main/acm2/acm_pcm16b.h
Normal file
@ -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_
|
58
webrtc/modules/audio_coding/main/acm2/acm_pcma.cc
Normal file
58
webrtc/modules/audio_coding/main/acm2/acm_pcma.cc
Normal file
@ -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
|
40
webrtc/modules/audio_coding/main/acm2/acm_pcma.h
Normal file
40
webrtc/modules/audio_coding/main/acm2/acm_pcma.h
Normal file
@ -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_
|
59
webrtc/modules/audio_coding/main/acm2/acm_pcmu.cc
Normal file
59
webrtc/modules/audio_coding/main/acm2/acm_pcmu.cc
Normal file
@ -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
|
40
webrtc/modules/audio_coding/main/acm2/acm_pcmu.h
Normal file
40
webrtc/modules/audio_coding/main/acm2/acm_pcmu.h
Normal file
@ -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_
|
827
webrtc/modules/audio_coding/main/acm2/acm_receiver.cc
Normal file
827
webrtc/modules/audio_coding/main/acm2/acm_receiver.cc
Normal file
@ -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
|
365
webrtc/modules/audio_coding/main/acm2/acm_receiver.h
Normal file
365
webrtc/modules/audio_coding/main/acm2/acm_receiver.h
Normal file
@ -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_
|
419
webrtc/modules/audio_coding/main/acm2/acm_receiver_unittest.cc
Normal file
419
webrtc/modules/audio_coding/main/acm2/acm_receiver_unittest.cc
Normal file
@ -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
|
50
webrtc/modules/audio_coding/main/acm2/acm_red.cc
Normal file
50
webrtc/modules/audio_coding/main/acm2/acm_red.cc
Normal file
@ -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
|
40
webrtc/modules/audio_coding/main/acm2/acm_red.h
Normal file
40
webrtc/modules/audio_coding/main/acm2/acm_red.h
Normal file
@ -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_
|
66
webrtc/modules/audio_coding/main/acm2/acm_resampler.cc
Normal file
66
webrtc/modules/audio_coding/main/acm2/acm_resampler.cc
Normal file
@ -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
|
40
webrtc/modules/audio_coding/main/acm2/acm_resampler.h
Normal file
40
webrtc/modules/audio_coding/main/acm2/acm_resampler.h
Normal file
@ -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_
|
329
webrtc/modules/audio_coding/main/acm2/acm_speex.cc
Normal file
329
webrtc/modules/audio_coding/main/acm2/acm_speex.cc
Normal file
@ -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
|
65
webrtc/modules/audio_coding/main/acm2/acm_speex.h
Normal file
65
webrtc/modules/audio_coding/main/acm2/acm_speex.h
Normal file
@ -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_
|
101
webrtc/modules/audio_coding/main/acm2/audio_coding_module.cc
Normal file
101
webrtc/modules/audio_coding/main/acm2/audio_coding_module.cc
Normal file
@ -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
|
190
webrtc/modules/audio_coding/main/acm2/audio_coding_module.gypi
Normal file
190
webrtc/modules/audio_coding/main/acm2/audio_coding_module.gypi
Normal file
@ -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
|
||||
],
|
||||
}],
|
||||
],
|
||||
}
|
1990
webrtc/modules/audio_coding/main/acm2/audio_coding_module_impl.cc
Normal file
1990
webrtc/modules/audio_coding/main/acm2/audio_coding_module_impl.cc
Normal file
File diff suppressed because it is too large
Load Diff
354
webrtc/modules/audio_coding/main/acm2/audio_coding_module_impl.h
Normal file
354
webrtc/modules/audio_coding/main/acm2/audio_coding_module_impl.h
Normal file
@ -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_
|
224
webrtc/modules/audio_coding/main/acm2/initial_delay_manager.cc
Normal file
224
webrtc/modules/audio_coding/main/acm2/initial_delay_manager.cc
Normal file
@ -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
|
115
webrtc/modules/audio_coding/main/acm2/initial_delay_manager.h
Normal file
115
webrtc/modules/audio_coding/main/acm2/initial_delay_manager.h
Normal file
@ -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_
|
@ -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
|
225
webrtc/modules/audio_coding/main/acm2/nack.cc
Normal file
225
webrtc/modules/audio_coding/main/acm2/nack.cc
Normal file
@ -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
|
209
webrtc/modules/audio_coding/main/acm2/nack.h
Normal file
209
webrtc/modules/audio_coding/main/acm2/nack.h
Normal file
@ -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_
|
482
webrtc/modules/audio_coding/main/acm2/nack_unittest.cc
Normal file
482
webrtc/modules/audio_coding/main/acm2/nack_unittest.cc
Normal file
@ -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
|
Loading…
x
Reference in New Issue
Block a user