blob: fe2b5474f6bdf8f8fd2833e14ee3a74a86b2593c [file] [log] [blame]
/*
* Copyright (c) 2012 The WebRTC project authors. All Rights Reserved.
*
* Use of this source code is governed by a BSD-style license
* that can be found in the LICENSE file in the root of the source
* tree. An additional intellectual property rights grant can be found
* in the file PATENTS. All contributing project authors may
* be found in the AUTHORS file in the root of the source tree.
*/
#include "webrtc/modules/audio_coding/main/acm2/audio_coding_module_impl.h"
#include <assert.h>
#include <stdlib.h>
#include <vector>
#include "webrtc/base/checks.h"
#include "webrtc/engine_configurations.h"
#include "webrtc/modules/audio_coding/main/interface/audio_coding_module_typedefs.h"
#include "webrtc/modules/audio_coding/main/acm2/acm_codec_database.h"
#include "webrtc/modules/audio_coding/main/acm2/acm_common_defs.h"
#include "webrtc/modules/audio_coding/main/acm2/acm_generic_codec.h"
#include "webrtc/modules/audio_coding/main/acm2/acm_resampler.h"
#include "webrtc/modules/audio_coding/main/acm2/call_statistics.h"
#include "webrtc/system_wrappers/interface/critical_section_wrapper.h"
#include "webrtc/system_wrappers/interface/rw_lock_wrapper.h"
#include "webrtc/system_wrappers/interface/trace.h"
#include "webrtc/typedefs.h"
namespace webrtc {
namespace acm2 {
enum {
kACMToneEnd = 999
};
// Maximum number of bytes in one packet (PCM16B, 20 ms packets, stereo).
enum {
kMaxPacketSize = 2560
};
// Maximum number of payloads that can be packed in one RED packet. For
// regular RED, we only pack two payloads. In case of dual-streaming, in worst
// case we might pack 3 payloads in one RED packet.
enum {
kNumRedFragmentationVectors = 2,
kMaxNumFragmentationVectors = 3
};
// If packet N is arrived all packets prior to N - |kNackThresholdPackets| which
// are not received are considered as lost, and appear in NACK list.
enum {
kNackThresholdPackets = 2
};
namespace {
// TODO(turajs): the same functionality is used in NetEq. If both classes
// need them, make it a static function in ACMCodecDB.
bool IsCodecRED(const CodecInst* codec) {
return (STR_CASE_CMP(codec->plname, "RED") == 0);
}
bool IsCodecRED(int index) {
return (IsCodecRED(&ACMCodecDB::database_[index]));
}
bool IsCodecCN(const CodecInst* codec) {
return (STR_CASE_CMP(codec->plname, "CN") == 0);
}
bool IsCodecCN(int index) {
return (IsCodecCN(&ACMCodecDB::database_[index]));
}
// Stereo-to-mono can be used as in-place.
int DownMix(const AudioFrame& frame, int length_out_buff, int16_t* out_buff) {
if (length_out_buff < frame.samples_per_channel_) {
return -1;
}
for (int n = 0; n < frame.samples_per_channel_; ++n)
out_buff[n] = (frame.data_[2 * n] + frame.data_[2 * n + 1]) >> 1;
return 0;
}
// Mono-to-stereo can be used as in-place.
int UpMix(const AudioFrame& frame, int length_out_buff, int16_t* out_buff) {
if (length_out_buff < frame.samples_per_channel_) {
return -1;
}
for (int n = frame.samples_per_channel_ - 1; n >= 0; --n) {
out_buff[2 * n + 1] = frame.data_[n];
out_buff[2 * n] = frame.data_[n];
}
return 0;
}
} // namespace
AudioCodingModuleImpl::AudioCodingModuleImpl(
const AudioCodingModule::Config& config)
: acm_crit_sect_(CriticalSectionWrapper::CreateCriticalSection()),
id_(config.id),
expected_codec_ts_(0xD87F3F9F),
expected_in_ts_(0xD87F3F9F),
send_codec_inst_(),
cng_nb_pltype_(255),
cng_wb_pltype_(255),
cng_swb_pltype_(255),
cng_fb_pltype_(255),
red_pltype_(255),
vad_enabled_(false),
dtx_enabled_(false),
vad_mode_(VADNormal),
stereo_send_(false),
current_send_codec_idx_(-1),
send_codec_registered_(false),
receiver_(config),
is_first_red_(true),
red_enabled_(false),
last_red_timestamp_(0),
codec_fec_enabled_(false),
previous_pltype_(255),
aux_rtp_header_(NULL),
receiver_initialized_(false),
codec_timestamp_(expected_codec_ts_),
first_10ms_data_(false),
callback_crit_sect_(CriticalSectionWrapper::CreateCriticalSection()),
packetization_callback_(NULL),
vad_callback_(NULL) {
// Nullify send codec memory, set payload type and set codec name to
// invalid values.
const char no_name[] = "noCodecRegistered";
strncpy(send_codec_inst_.plname, no_name, RTP_PAYLOAD_NAME_SIZE - 1);
send_codec_inst_.pltype = -1;
for (int i = 0; i < ACMCodecDB::kMaxNumCodecs; i++) {
codecs_[i] = NULL;
mirror_codec_idx_[i] = -1;
}
// Allocate memory for RED.
red_buffer_ = new uint8_t[MAX_PAYLOAD_SIZE_BYTE];
// TODO(turajs): This might not be exactly how this class is supposed to work.
// The external usage might be that |fragmentationVectorSize| has to match
// the allocated space for the member-arrays, while here, we allocate
// according to the maximum number of fragmentations and change
// |fragmentationVectorSize| on-the-fly based on actual number of
// fragmentations. However, due to copying to local variable before calling
// SendData, the RTP module receives a "valid" fragmentation, where allocated
// space matches |fragmentationVectorSize|, therefore, this should not cause
// any problem. A better approach is not using RTPFragmentationHeader as
// member variable, instead, use an ACM-specific structure to hold RED-related
// data. See module_common_type.h for the definition of
// RTPFragmentationHeader.
fragmentation_.VerifyAndAllocateFragmentationHeader(
kMaxNumFragmentationVectors);
// Register the default payload type for RED and for CNG at sampling rates of
// 8, 16, 32 and 48 kHz.
for (int i = (ACMCodecDB::kNumCodecs - 1); i >= 0; i--) {
if (IsCodecRED(i)) {
red_pltype_ = static_cast<uint8_t>(ACMCodecDB::database_[i].pltype);
} else if (IsCodecCN(i)) {
if (ACMCodecDB::database_[i].plfreq == 8000) {
cng_nb_pltype_ = static_cast<uint8_t>(ACMCodecDB::database_[i].pltype);
} else if (ACMCodecDB::database_[i].plfreq == 16000) {
cng_wb_pltype_ = static_cast<uint8_t>(ACMCodecDB::database_[i].pltype);
} else if (ACMCodecDB::database_[i].plfreq == 32000) {
cng_swb_pltype_ = static_cast<uint8_t>(ACMCodecDB::database_[i].pltype);
} else if (ACMCodecDB::database_[i].plfreq == 48000) {
cng_fb_pltype_ = static_cast<uint8_t>(ACMCodecDB::database_[i].pltype);
}
}
}
if (InitializeReceiverSafe() < 0) {
WEBRTC_TRACE(webrtc::kTraceError, webrtc::kTraceAudioCoding, id_,
"Cannot initialize receiver");
}
WEBRTC_TRACE(webrtc::kTraceMemory, webrtc::kTraceAudioCoding, id_, "Created");
}
AudioCodingModuleImpl::~AudioCodingModuleImpl() {
{
CriticalSectionScoped lock(acm_crit_sect_);
current_send_codec_idx_ = -1;
for (int i = 0; i < ACMCodecDB::kMaxNumCodecs; i++) {
if (codecs_[i] != NULL) {
// Mirror index holds the address of the codec memory.
assert(mirror_codec_idx_[i] > -1);
if (codecs_[mirror_codec_idx_[i]] != NULL) {
delete codecs_[mirror_codec_idx_[i]];
codecs_[mirror_codec_idx_[i]] = NULL;
}
codecs_[i] = NULL;
}
}
if (red_buffer_ != NULL) {
delete[] red_buffer_;
red_buffer_ = NULL;
}
}
if (aux_rtp_header_ != NULL) {
delete aux_rtp_header_;
aux_rtp_header_ = NULL;
}
delete callback_crit_sect_;
callback_crit_sect_ = NULL;
delete acm_crit_sect_;
acm_crit_sect_ = NULL;
WEBRTC_TRACE(webrtc::kTraceMemory, webrtc::kTraceAudioCoding, id_,
"Destroyed");
}
int32_t AudioCodingModuleImpl::ChangeUniqueId(const int32_t id) {
{
CriticalSectionScoped lock(acm_crit_sect_);
id_ = id;
for (int i = 0; i < ACMCodecDB::kMaxNumCodecs; i++) {
if (codecs_[i] != NULL) {
codecs_[i]->SetUniqueID(id);
}
}
}
receiver_.set_id(id_);
return 0;
}
// Returns the number of milliseconds until the module want a
// worker thread to call Process.
int64_t AudioCodingModuleImpl::TimeUntilNextProcess() {
CriticalSectionScoped lock(acm_crit_sect_);
if (!HaveValidEncoder("TimeUntilNextProcess")) {
return -1;
}
return codecs_[current_send_codec_idx_]->SamplesLeftToEncode() /
(send_codec_inst_.plfreq / 1000);
}
// Process any pending tasks such as timeouts.
int32_t AudioCodingModuleImpl::Process() {
// Make room for 1 RED payload.
uint8_t stream[2 * MAX_PAYLOAD_SIZE_BYTE];
// TODO(turajs): |length_bytes| & |red_length_bytes| can be of type int if
// ACMGenericCodec::Encode() & ACMGenericCodec::GetRedPayload() allows.
int16_t length_bytes = 2 * MAX_PAYLOAD_SIZE_BYTE;
int16_t red_length_bytes = length_bytes;
uint32_t rtp_timestamp;
int status;
WebRtcACMEncodingType encoding_type;
FrameType frame_type = kAudioFrameSpeech;
uint8_t current_payload_type = 0;
bool has_data_to_send = false;
bool red_active = false;
RTPFragmentationHeader my_fragmentation;
// Keep the scope of the ACM critical section limited.
{
CriticalSectionScoped lock(acm_crit_sect_);
// Check if there is an encoder before.
if (!HaveValidEncoder("Process")) {
return -1;
}
status = codecs_[current_send_codec_idx_]->Encode(stream, &length_bytes,
&rtp_timestamp,
&encoding_type);
if (status < 0) {
// Encode failed.
WEBRTC_TRACE(webrtc::kTraceError, webrtc::kTraceAudioCoding, id_,
"Process(): Encoding Failed");
length_bytes = 0;
return -1;
} else if (status == 0) {
// Not enough data.
return 0;
} else {
switch (encoding_type) {
case kNoEncoding: {
current_payload_type = previous_pltype_;
frame_type = kFrameEmpty;
length_bytes = 0;
break;
}
case kActiveNormalEncoded:
case kPassiveNormalEncoded: {
current_payload_type = static_cast<uint8_t>(send_codec_inst_.pltype);
frame_type = kAudioFrameSpeech;
break;
}
case kPassiveDTXNB: {
current_payload_type = cng_nb_pltype_;
frame_type = kAudioFrameCN;
is_first_red_ = true;
break;
}
case kPassiveDTXWB: {
current_payload_type = cng_wb_pltype_;
frame_type = kAudioFrameCN;
is_first_red_ = true;
break;
}
case kPassiveDTXSWB: {
current_payload_type = cng_swb_pltype_;
frame_type = kAudioFrameCN;
is_first_red_ = true;
break;
}
case kPassiveDTXFB: {
current_payload_type = cng_fb_pltype_;
frame_type = kAudioFrameCN;
is_first_red_ = true;
break;
}
}
has_data_to_send = true;
previous_pltype_ = current_payload_type;
// Redundancy encode is done here. The two bitstreams packetized into
// one RTP packet and the fragmentation points are set.
// Only apply RED on speech data.
if ((red_enabled_) &&
((encoding_type == kActiveNormalEncoded) ||
(encoding_type == kPassiveNormalEncoded))) {
// RED is enabled within this scope.
//
// Note that, a special solution exists for iSAC since it is the only
// codec for which GetRedPayload has a non-empty implementation.
//
// Summary of the RED scheme below (use iSAC as example):
//
// 1st (is_first_red_ is true) encoded iSAC frame (primary #1) =>
// - call GetRedPayload() and store redundancy for packet #1 in
// second fragment of RED buffer (old data)
// - drop the primary iSAC frame
// - don't call SendData
// 2nd (is_first_red_ is false) encoded iSAC frame (primary #2) =>
// - store primary #2 in 1st fragment of RED buffer and send the
// combined packet
// - the transmitted packet contains primary #2 (new) and
// redundancy for packet #1 (old)
// - call GetRed_Payload() and store redundancy for packet #2 in
// second fragment of RED buffer
//
// ...
//
// Nth encoded iSAC frame (primary #N) =>
// - store primary #N in 1st fragment of RED buffer and send the
// combined packet
// - the transmitted packet contains primary #N (new) and
// reduncancy for packet #(N-1) (old)
// - call GetRedPayload() and store redundancy for packet #N in
// second fragment of RED buffer
//
// For all other codecs, GetRedPayload does nothing and returns -1 =>
// redundant data is only a copy.
//
// First combined packet contains : #2 (new) and #1 (old)
// Second combined packet contains: #3 (new) and #2 (old)
// Third combined packet contains : #4 (new) and #3 (old)
//
// Hence, even if every second packet is dropped, perfect
// reconstruction is possible.
red_active = true;
has_data_to_send = false;
// Skip the following part for the first packet in a RED session.
if (!is_first_red_) {
// Rearrange stream such that RED packets are included.
// Replace stream now that we have stored current stream.
memcpy(stream + fragmentation_.fragmentationOffset[1], red_buffer_,
fragmentation_.fragmentationLength[1]);
// Update the fragmentation time difference vector, in number of
// timestamps.
uint16_t time_since_last = static_cast<uint16_t>(
rtp_timestamp - last_red_timestamp_);
// Update fragmentation vectors.
fragmentation_.fragmentationPlType[1] =
fragmentation_.fragmentationPlType[0];
fragmentation_.fragmentationTimeDiff[1] = time_since_last;
has_data_to_send = true;
}
// Insert new packet length.
fragmentation_.fragmentationLength[0] = length_bytes;
// Insert new packet payload type.
fragmentation_.fragmentationPlType[0] = current_payload_type;
last_red_timestamp_ = rtp_timestamp;
// Can be modified by the GetRedPayload() call if iSAC is utilized.
red_length_bytes = length_bytes;
// A fragmentation header is provided => packetization according to
// RFC 2198 (RTP Payload for Redundant Audio Data) will be used.
// First fragment is the current data (new).
// Second fragment is the previous data (old).
length_bytes = static_cast<int16_t>(
fragmentation_.fragmentationLength[0] +
fragmentation_.fragmentationLength[1]);
// Get, and store, redundant data from the encoder based on the recently
// encoded frame.
// NOTE - only iSAC contains an implementation; all other codecs does
// nothing and returns -1.
if (codecs_[current_send_codec_idx_]->GetRedPayload(
red_buffer_, &red_length_bytes) == -1) {
// The codec was not iSAC => use current encoder output as redundant
// data instead (trivial RED scheme).
memcpy(red_buffer_, stream, red_length_bytes);
}
is_first_red_ = false;
// Update payload type with RED payload type.
current_payload_type = red_pltype_;
// We have packed 2 payloads.
fragmentation_.fragmentationVectorSize = kNumRedFragmentationVectors;
// Copy to local variable, as it will be used outside ACM lock.
my_fragmentation.CopyFrom(fragmentation_);
// Store RED length.
fragmentation_.fragmentationLength[1] = red_length_bytes;
}
}
}
if (has_data_to_send) {
CriticalSectionScoped lock(callback_crit_sect_);
if (packetization_callback_ != NULL) {
if (red_active) {
// Callback with payload data, including redundant data (RED).
packetization_callback_->SendData(frame_type, current_payload_type,
rtp_timestamp, stream, length_bytes,
&my_fragmentation);
} else {
// Callback with payload data.
packetization_callback_->SendData(frame_type, current_payload_type,
rtp_timestamp, stream, length_bytes,
NULL);
}
}
if (vad_callback_ != NULL) {
// Callback with VAD decision.
vad_callback_->InFrameType(static_cast<int16_t>(encoding_type));
}
}
return length_bytes;
}
/////////////////////////////////////////
// Sender
//
// Initialize send codec.
int AudioCodingModuleImpl::InitializeSender() {
CriticalSectionScoped lock(acm_crit_sect_);
// Start with invalid values.
send_codec_registered_ = false;
current_send_codec_idx_ = -1;
send_codec_inst_.plname[0] = '\0';
// Delete all encoders to start fresh.
for (int id = 0; id < ACMCodecDB::kMaxNumCodecs; id++) {
if (codecs_[id] != NULL) {
codecs_[id]->DestructEncoder();
}
}
// Initialize RED.
is_first_red_ = true;
if (red_enabled_) {
if (red_buffer_ != NULL) {
memset(red_buffer_, 0, MAX_PAYLOAD_SIZE_BYTE);
}
ResetFragmentation(kNumRedFragmentationVectors);
}
return 0;
}
int AudioCodingModuleImpl::ResetEncoder() {
CriticalSectionScoped lock(acm_crit_sect_);
if (!HaveValidEncoder("ResetEncoder")) {
return -1;
}
return codecs_[current_send_codec_idx_]->ResetEncoder();
}
ACMGenericCodec* AudioCodingModuleImpl::CreateCodec(const CodecInst& codec) {
ACMGenericCodec* my_codec = NULL;
my_codec = ACMCodecDB::CreateCodecInstance(codec);
if (my_codec == NULL) {
// Error, could not create the codec.
WEBRTC_TRACE(webrtc::kTraceError, webrtc::kTraceAudioCoding, id_,
"ACMCodecDB::CreateCodecInstance() failed in CreateCodec()");
return my_codec;
}
my_codec->SetUniqueID(id_);
return my_codec;
}
// Check if the given codec is a valid to be registered as send codec.
static int IsValidSendCodec(const CodecInst& send_codec,
bool is_primary_encoder,
int acm_id,
int* mirror_id) {
if ((send_codec.channels != 1) && (send_codec.channels != 2)) {
WEBRTC_TRACE(webrtc::kTraceError, webrtc::kTraceAudioCoding, acm_id,
"Wrong number of channels (%d, only mono and stereo are "
"supported) for %s encoder", send_codec.channels,
is_primary_encoder ? "primary" : "secondary");
return -1;
}
int codec_id = ACMCodecDB::CodecNumber(send_codec, mirror_id);
if (codec_id < 0) {
WEBRTC_TRACE(webrtc::kTraceError, webrtc::kTraceAudioCoding, acm_id,
"Invalid codec setting for the send codec.");
return -1;
}
// TODO(tlegrand): Remove this check. Already taken care of in
// ACMCodecDB::CodecNumber().
// Check if the payload-type is valid
if (!ACMCodecDB::ValidPayloadType(send_codec.pltype)) {
WEBRTC_TRACE(webrtc::kTraceError, webrtc::kTraceAudioCoding, acm_id,
"Invalid payload-type %d for %s.", send_codec.pltype,
send_codec.plname);
return -1;
}
// Telephone-event cannot be a send codec.
if (!STR_CASE_CMP(send_codec.plname, "telephone-event")) {
WEBRTC_TRACE(webrtc::kTraceError, webrtc::kTraceAudioCoding, acm_id,
"telephone-event cannot be a send codec");
*mirror_id = -1;
return -1;
}
if (ACMCodecDB::codec_settings_[codec_id].channel_support
< send_codec.channels) {
WEBRTC_TRACE(webrtc::kTraceError, webrtc::kTraceAudioCoding, acm_id,
"%d number of channels not supportedn for %s.",
send_codec.channels, send_codec.plname);
*mirror_id = -1;
return -1;
}
if (!is_primary_encoder) {
// If registering the secondary encoder, then RED and CN are not valid
// choices as encoder.
if (IsCodecRED(&send_codec)) {
WEBRTC_TRACE(webrtc::kTraceError, webrtc::kTraceAudioCoding, acm_id,
"RED cannot be secondary codec");
*mirror_id = -1;
return -1;
}
if (IsCodecCN(&send_codec)) {
WEBRTC_TRACE(webrtc::kTraceError, webrtc::kTraceAudioCoding, acm_id,
"DTX cannot be secondary codec");
*mirror_id = -1;
return -1;
}
}
return codec_id;
}
// Can be called multiple times for Codec, CNG, RED.
int AudioCodingModuleImpl::RegisterSendCodec(const CodecInst& send_codec) {
int mirror_id;
int codec_id = IsValidSendCodec(send_codec, true, id_, &mirror_id);
CriticalSectionScoped lock(acm_crit_sect_);
// Check for reported errors from function IsValidSendCodec().
if (codec_id < 0) {
if (!send_codec_registered_) {
// This values has to be NULL if there is no codec registered.
current_send_codec_idx_ = -1;
}
return -1;
}
// RED can be registered with other payload type. If not registered a default
// payload type is used.
if (IsCodecRED(&send_codec)) {
// TODO(tlegrand): Remove this check. Already taken care of in
// ACMCodecDB::CodecNumber().
// Check if the payload-type is valid
if (!ACMCodecDB::ValidPayloadType(send_codec.pltype)) {
WEBRTC_TRACE(webrtc::kTraceError, webrtc::kTraceAudioCoding, id_,
"Invalid payload-type %d for %s.", send_codec.pltype,
send_codec.plname);
return -1;
}
// Set RED payload type.
red_pltype_ = static_cast<uint8_t>(send_codec.pltype);
return 0;
}
// CNG can be registered with other payload type. If not registered the
// default payload types from codec database will be used.
if (IsCodecCN(&send_codec)) {
// CNG is registered.
switch (send_codec.plfreq) {
case 8000: {
cng_nb_pltype_ = static_cast<uint8_t>(send_codec.pltype);
break;
}
case 16000: {
cng_wb_pltype_ = static_cast<uint8_t>(send_codec.pltype);
break;
}
case 32000: {
cng_swb_pltype_ = static_cast<uint8_t>(send_codec.pltype);
break;
}
case 48000: {
cng_fb_pltype_ = static_cast<uint8_t>(send_codec.pltype);
break;
}
default: {
WEBRTC_TRACE(webrtc::kTraceError, webrtc::kTraceAudioCoding, id_,
"RegisterSendCodec() failed, invalid frequency for CNG "
"registration");
return -1;
}
}
return 0;
}
// Set Stereo, and make sure VAD and DTX is turned off.
if (send_codec.channels == 2) {
stereo_send_ = true;
if (vad_enabled_ || dtx_enabled_) {
WEBRTC_TRACE(webrtc::kTraceWarning, webrtc::kTraceAudioCoding, id_,
"VAD/DTX is turned off, not supported when sending stereo.");
}
vad_enabled_ = false;
dtx_enabled_ = false;
} else {
stereo_send_ = false;
}
// Check if the codec is already registered as send codec.
bool is_send_codec;
if (send_codec_registered_) {
int send_codec_mirror_id;
int send_codec_id = ACMCodecDB::CodecNumber(send_codec_inst_,
&send_codec_mirror_id);
assert(send_codec_id >= 0);
is_send_codec = (send_codec_id == codec_id) ||
(mirror_id == send_codec_mirror_id);
} else {
is_send_codec = false;
}
// If new codec, or new settings, register.
if (!is_send_codec) {
if (codecs_[mirror_id] == NULL) {
codecs_[mirror_id] = CreateCodec(send_codec);
if (codecs_[mirror_id] == NULL) {
WEBRTC_TRACE(webrtc::kTraceError, webrtc::kTraceAudioCoding, id_,
"Cannot Create the codec");
return -1;
}
mirror_codec_idx_[mirror_id] = mirror_id;
}
if (mirror_id != codec_id) {
codecs_[codec_id] = codecs_[mirror_id];
mirror_codec_idx_[codec_id] = mirror_id;
}
ACMGenericCodec* codec_ptr = codecs_[codec_id];
WebRtcACMCodecParams codec_params;
memcpy(&(codec_params.codec_inst), &send_codec, sizeof(CodecInst));
codec_params.enable_vad = vad_enabled_;
codec_params.enable_dtx = dtx_enabled_;
codec_params.vad_mode = vad_mode_;
// Force initialization.
if (codec_ptr->InitEncoder(&codec_params, true) < 0) {
// Could not initialize the encoder.
// Check if already have a registered codec.
// Depending on that different messages are logged.
if (!send_codec_registered_) {
current_send_codec_idx_ = -1;
WEBRTC_TRACE(webrtc::kTraceError, webrtc::kTraceAudioCoding, id_,
"Cannot Initialize the encoder No Encoder is registered");
} else {
WEBRTC_TRACE(webrtc::kTraceError, webrtc::kTraceAudioCoding, id_,
"Cannot Initialize the encoder, continue encoding with "
"the previously registered codec");
}
return -1;
}
// Update states.
dtx_enabled_ = codec_params.enable_dtx;
vad_enabled_ = codec_params.enable_vad;
vad_mode_ = codec_params.vad_mode;
// Everything is fine so we can replace the previous codec with this one.
if (send_codec_registered_) {
// If we change codec we start fresh with RED.
// This is not strictly required by the standard.
is_first_red_ = true;
codec_ptr->SetVAD(&dtx_enabled_, &vad_enabled_, &vad_mode_);
if (!codec_ptr->HasInternalFEC()) {
codec_fec_enabled_ = false;
} else {
if (codec_ptr->SetFEC(codec_fec_enabled_) < 0) {
WEBRTC_TRACE(webrtc::kTraceError, webrtc::kTraceAudioCoding, id_,
"Cannot set codec FEC");
return -1;
}
}
}
current_send_codec_idx_ = codec_id;
send_codec_registered_ = true;
memcpy(&send_codec_inst_, &send_codec, sizeof(CodecInst));
previous_pltype_ = send_codec_inst_.pltype;
return 0;
} else {
// If codec is the same as already registered check if any parameters
// has changed compared to the current values.
// If any parameter is valid then apply it and record.
bool force_init = false;
if (mirror_id != codec_id) {
codecs_[codec_id] = codecs_[mirror_id];
mirror_codec_idx_[codec_id] = mirror_id;
}
// Check the payload type.
if (send_codec.pltype != send_codec_inst_.pltype) {
// At this point check if the given payload type is valid.
// Record it later when the sampling frequency is changed
// successfully.
if (!ACMCodecDB::ValidPayloadType(send_codec.pltype)) {
WEBRTC_TRACE(webrtc::kTraceError, webrtc::kTraceAudioCoding, id_,
"Out of range payload type");
return -1;
}
}
// If there is a codec that ONE instance of codec supports multiple
// sampling frequencies, then we need to take care of it here.
// one such a codec is iSAC. Both WB and SWB are encoded and decoded
// with one iSAC instance. Therefore, we need to update the encoder
// frequency if required.
if (send_codec_inst_.plfreq != send_codec.plfreq) {
force_init = true;
// If sampling frequency is changed we have to start fresh with RED.
is_first_red_ = true;
}
// If packet size or number of channels has changed, we need to
// re-initialize the encoder.
if (send_codec_inst_.pacsize != send_codec.pacsize) {
force_init = true;
}
if (send_codec_inst_.channels != send_codec.channels) {
force_init = true;
}
if (force_init) {
WebRtcACMCodecParams codec_params;
memcpy(&(codec_params.codec_inst), &send_codec, sizeof(CodecInst));
codec_params.enable_vad = vad_enabled_;
codec_params.enable_dtx = dtx_enabled_;
codec_params.vad_mode = vad_mode_;
// Force initialization.
if (codecs_[current_send_codec_idx_]->InitEncoder(&codec_params,
true) < 0) {
WEBRTC_TRACE(webrtc::kTraceError, webrtc::kTraceAudioCoding, id_,
"Could not change the codec packet-size.");
return -1;
}
send_codec_inst_.plfreq = send_codec.plfreq;
send_codec_inst_.pacsize = send_codec.pacsize;
send_codec_inst_.channels = send_codec.channels;
}
// If the change of sampling frequency has been successful then
// we store the payload-type.
send_codec_inst_.pltype = send_codec.pltype;
// Check if a change in Rate is required.
if (send_codec.rate != send_codec_inst_.rate) {
if (codecs_[codec_id]->SetBitRate(send_codec.rate) < 0) {
WEBRTC_TRACE(webrtc::kTraceError, webrtc::kTraceAudioCoding, id_,
"Could not change the codec rate.");
return -1;
}
send_codec_inst_.rate = send_codec.rate;
}
if (!codecs_[codec_id]->HasInternalFEC()) {
codec_fec_enabled_ = false;
} else {
if (codecs_[codec_id]->SetFEC(codec_fec_enabled_) < 0) {
WEBRTC_TRACE(webrtc::kTraceError, webrtc::kTraceAudioCoding, id_,
"Cannot set codec FEC");
return -1;
}
}
previous_pltype_ = send_codec_inst_.pltype;
return 0;
}
}
// Get current send codec.
int AudioCodingModuleImpl::SendCodec(
CodecInst* current_codec) const {
WEBRTC_TRACE(webrtc::kTraceStream, webrtc::kTraceAudioCoding, id_,
"SendCodec()");
CriticalSectionScoped lock(acm_crit_sect_);
if (!send_codec_registered_) {
WEBRTC_TRACE(webrtc::kTraceStream, webrtc::kTraceAudioCoding, id_,
"SendCodec Failed, no codec is registered");
return -1;
}
WebRtcACMCodecParams encoder_param;
codecs_[current_send_codec_idx_]->EncoderParams(&encoder_param);
encoder_param.codec_inst.pltype = send_codec_inst_.pltype;
memcpy(current_codec, &(encoder_param.codec_inst), sizeof(CodecInst));
return 0;
}
// Get current send frequency.
int AudioCodingModuleImpl::SendFrequency() const {
WEBRTC_TRACE(webrtc::kTraceStream, webrtc::kTraceAudioCoding, id_,
"SendFrequency()");
CriticalSectionScoped lock(acm_crit_sect_);
if (!send_codec_registered_) {
WEBRTC_TRACE(webrtc::kTraceStream, webrtc::kTraceAudioCoding, id_,
"SendFrequency Failed, no codec is registered");
return -1;
}
return send_codec_inst_.plfreq;
}
// Get encode bitrate.
// Adaptive rate codecs return their current encode target rate, while other
// codecs return there longterm avarage or their fixed rate.
int AudioCodingModuleImpl::SendBitrate() const {
CriticalSectionScoped lock(acm_crit_sect_);
if (!send_codec_registered_) {
WEBRTC_TRACE(webrtc::kTraceStream, webrtc::kTraceAudioCoding, id_,
"SendBitrate Failed, no codec is registered");
return -1;
}
WebRtcACMCodecParams encoder_param;
codecs_[current_send_codec_idx_]->EncoderParams(&encoder_param);
return encoder_param.codec_inst.rate;
}
// Set available bandwidth, inform the encoder about the estimated bandwidth
// received from the remote party.
int AudioCodingModuleImpl::SetReceivedEstimatedBandwidth(int bw) {
CriticalSectionScoped lock(acm_crit_sect_);
return codecs_[current_send_codec_idx_]->SetEstimatedBandwidth(bw);
}
// Register a transport callback which will be called to deliver
// the encoded buffers.
int AudioCodingModuleImpl::RegisterTransportCallback(
AudioPacketizationCallback* transport) {
CriticalSectionScoped lock(callback_crit_sect_);
packetization_callback_ = transport;
return 0;
}
// Add 10MS of raw (PCM) audio data to the encoder.
int AudioCodingModuleImpl::Add10MsData(
const AudioFrame& audio_frame) {
if (audio_frame.samples_per_channel_ <= 0) {
assert(false);
WEBRTC_TRACE(webrtc::kTraceError, webrtc::kTraceAudioCoding, id_,
"Cannot Add 10 ms audio, payload length is negative or "
"zero");
return -1;
}
if (audio_frame.sample_rate_hz_ > 48000) {
assert(false);
WEBRTC_TRACE(webrtc::kTraceError, webrtc::kTraceAudioCoding, id_,
"Cannot Add 10 ms audio, input frequency not valid");
return -1;
}
// If the length and frequency matches. We currently just support raw PCM.
if ((audio_frame.sample_rate_hz_ / 100)
!= audio_frame.samples_per_channel_) {
WEBRTC_TRACE(webrtc::kTraceError, webrtc::kTraceAudioCoding, id_,
"Cannot Add 10 ms audio, input frequency and length doesn't"
" match");
return -1;
}
if (audio_frame.num_channels_ != 1 && audio_frame.num_channels_ != 2) {
WEBRTC_TRACE(webrtc::kTraceError, webrtc::kTraceAudioCoding, id_,
"Cannot Add 10 ms audio, invalid number of channels.");
return -1;
}
CriticalSectionScoped lock(acm_crit_sect_);
// Do we have a codec registered?
if (!HaveValidEncoder("Add10MsData")) {
return -1;
}
const AudioFrame* ptr_frame;
// Perform a resampling, also down-mix if it is required and can be
// performed before resampling (a down mix prior to resampling will take
// place if both primary and secondary encoders are mono and input is in
// stereo).
if (PreprocessToAddData(audio_frame, &ptr_frame) < 0) {
return -1;
}
// Check whether we need an up-mix or down-mix?
bool remix = ptr_frame->num_channels_ != send_codec_inst_.channels;
// If a re-mix is required (up or down), this buffer will store re-mixed
// version of the input.
int16_t buffer[WEBRTC_10MS_PCM_AUDIO];
if (remix) {
if (ptr_frame->num_channels_ == 1) {
if (UpMix(*ptr_frame, WEBRTC_10MS_PCM_AUDIO, buffer) < 0)
return -1;
} else {
if (DownMix(*ptr_frame, WEBRTC_10MS_PCM_AUDIO, buffer) < 0)
return -1;
}
}
// When adding data to encoders this pointer is pointing to an audio buffer
// with correct number of channels.
const int16_t* ptr_audio = ptr_frame->data_;
// For pushing data to primary, point the |ptr_audio| to correct buffer.
if (send_codec_inst_.channels != ptr_frame->num_channels_)
ptr_audio = buffer;
if (codecs_[current_send_codec_idx_]->Add10MsData(
ptr_frame->timestamp_, ptr_audio, ptr_frame->samples_per_channel_,
send_codec_inst_.channels) < 0)
return -1;
return 0;
}
// Perform a resampling and down-mix if required. We down-mix only if
// encoder is mono and input is stereo. In case of dual-streaming, both
// encoders has to be mono for down-mix to take place.
// |*ptr_out| will point to the pre-processed audio-frame. If no pre-processing
// is required, |*ptr_out| points to |in_frame|.
int AudioCodingModuleImpl::PreprocessToAddData(const AudioFrame& in_frame,
const AudioFrame** ptr_out) {
bool resample = (in_frame.sample_rate_hz_ != send_codec_inst_.plfreq);
// This variable is true if primary codec and secondary codec (if exists)
// are both mono and input is stereo.
bool down_mix =
(in_frame.num_channels_ == 2) && (send_codec_inst_.channels == 1);
if (!first_10ms_data_) {
expected_in_ts_ = in_frame.timestamp_;
expected_codec_ts_ = in_frame.timestamp_;
first_10ms_data_ = true;
} else if (in_frame.timestamp_ != expected_in_ts_) {
// TODO(turajs): Do we need a warning here.
expected_codec_ts_ += (in_frame.timestamp_ - expected_in_ts_) *
static_cast<uint32_t>((static_cast<double>(send_codec_inst_.plfreq) /
static_cast<double>(in_frame.sample_rate_hz_)));
expected_in_ts_ = in_frame.timestamp_;
}
if (!down_mix && !resample) {
// No pre-processing is required.
expected_in_ts_ += in_frame.samples_per_channel_;
expected_codec_ts_ += in_frame.samples_per_channel_;
*ptr_out = &in_frame;
return 0;
}
*ptr_out = &preprocess_frame_;
preprocess_frame_.num_channels_ = in_frame.num_channels_;
int16_t audio[WEBRTC_10MS_PCM_AUDIO];
const int16_t* src_ptr_audio = in_frame.data_;
int16_t* dest_ptr_audio = preprocess_frame_.data_;
if (down_mix) {
// If a resampling is required the output of a down-mix is written into a
// local buffer, otherwise, it will be written to the output frame.
if (resample)
dest_ptr_audio = audio;
if (DownMix(in_frame, WEBRTC_10MS_PCM_AUDIO, dest_ptr_audio) < 0)
return -1;
preprocess_frame_.num_channels_ = 1;
// Set the input of the resampler is the down-mixed signal.
src_ptr_audio = audio;
}
preprocess_frame_.timestamp_ = expected_codec_ts_;
preprocess_frame_.samples_per_channel_ = in_frame.samples_per_channel_;
preprocess_frame_.sample_rate_hz_ = in_frame.sample_rate_hz_;
// If it is required, we have to do a resampling.
if (resample) {
// The result of the resampler is written to output frame.
dest_ptr_audio = preprocess_frame_.data_;
preprocess_frame_.samples_per_channel_ =
resampler_.Resample10Msec(src_ptr_audio,
in_frame.sample_rate_hz_,
send_codec_inst_.plfreq,
preprocess_frame_.num_channels_,
AudioFrame::kMaxDataSizeSamples,
dest_ptr_audio);
if (preprocess_frame_.samples_per_channel_ < 0) {
WEBRTC_TRACE(webrtc::kTraceError, webrtc::kTraceAudioCoding, id_,
"Cannot add 10 ms audio, resampling failed");
return -1;
}
preprocess_frame_.sample_rate_hz_ = send_codec_inst_.plfreq;
}
expected_codec_ts_ += preprocess_frame_.samples_per_channel_;
expected_in_ts_ += in_frame.samples_per_channel_;
return 0;
}
/////////////////////////////////////////
// (RED) Redundant Coding
//
bool AudioCodingModuleImpl::REDStatus() const {
CriticalSectionScoped lock(acm_crit_sect_);
return red_enabled_;
}
// Configure RED status i.e on/off.
int AudioCodingModuleImpl::SetREDStatus(
#ifdef WEBRTC_CODEC_RED
bool enable_red) {
CriticalSectionScoped lock(acm_crit_sect_);
if (enable_red == true && codec_fec_enabled_ == true) {
WEBRTC_TRACE(webrtc::kTraceWarning, webrtc::kTraceAudioCoding, id_,
"Codec internal FEC and RED cannot be co-enabled.");
return -1;
}
if (red_enabled_ != enable_red) {
// Reset the RED buffer.
memset(red_buffer_, 0, MAX_PAYLOAD_SIZE_BYTE);
// Reset fragmentation buffers.
ResetFragmentation(kNumRedFragmentationVectors);
// Set red_enabled_.
red_enabled_ = enable_red;
}
is_first_red_ = true; // Make sure we restart RED.
return 0;
#else
bool /* enable_red */) {
red_enabled_ = false;
WEBRTC_TRACE(webrtc::kTraceWarning, webrtc::kTraceAudioCoding, id_,
" WEBRTC_CODEC_RED is undefined => red_enabled_ = %d",
red_enabled_);
return -1;
#endif
}
/////////////////////////////////////////
// (FEC) Forward Error Correction (codec internal)
//
bool AudioCodingModuleImpl::CodecFEC() const {
CriticalSectionScoped lock(acm_crit_sect_);
return codec_fec_enabled_;
}
int AudioCodingModuleImpl::SetCodecFEC(bool enable_codec_fec) {
CriticalSectionScoped lock(acm_crit_sect_);
if (enable_codec_fec == true && red_enabled_ == true) {
WEBRTC_TRACE(webrtc::kTraceWarning, webrtc::kTraceAudioCoding, id_,
"Codec internal FEC and RED cannot be co-enabled.");
return -1;
}
// Set codec FEC.
if (HaveValidEncoder("SetCodecFEC") &&
codecs_[current_send_codec_idx_]->SetFEC(enable_codec_fec) < 0) {
WEBRTC_TRACE(webrtc::kTraceError, webrtc::kTraceAudioCoding, id_,
"Set codec internal FEC failed.");
return -1;
}
codec_fec_enabled_ = enable_codec_fec;
return 0;
}
int AudioCodingModuleImpl::SetPacketLossRate(int loss_rate) {
CriticalSectionScoped lock(acm_crit_sect_);
if (HaveValidEncoder("SetPacketLossRate") &&
codecs_[current_send_codec_idx_]->SetPacketLossRate(loss_rate) < 0) {
WEBRTC_TRACE(webrtc::kTraceError, webrtc::kTraceAudioCoding, id_,
"Set packet loss rate failed.");
return -1;
}
return 0;
}
/////////////////////////////////////////
// (VAD) Voice Activity Detection
//
int AudioCodingModuleImpl::SetVAD(bool enable_dtx,
bool enable_vad,
ACMVADMode mode) {
CriticalSectionScoped lock(acm_crit_sect_);
return SetVADSafe(enable_dtx, enable_vad, mode);
}
int AudioCodingModuleImpl::SetVADSafe(bool enable_dtx,
bool enable_vad,
ACMVADMode mode) {
// Sanity check of the mode.
if ((mode != VADNormal) && (mode != VADLowBitrate)
&& (mode != VADAggr) && (mode != VADVeryAggr)) {
WEBRTC_TRACE(webrtc::kTraceError, webrtc::kTraceAudioCoding, id_,
"Invalid VAD Mode %d, no change is made to VAD/DTX status",
mode);
return -1;
}
// Check that the send codec is mono. We don't support VAD/DTX for stereo
// sending.
if ((enable_dtx || enable_vad) && stereo_send_) {
WEBRTC_TRACE(webrtc::kTraceError, webrtc::kTraceAudioCoding, id_,
"VAD/DTX not supported for stereo sending");
dtx_enabled_ = false;
vad_enabled_ = false;
vad_mode_ = mode;
return -1;
}
// Store VAD/DTX settings. Values can be changed in the call to "SetVAD"
// below.
dtx_enabled_ = enable_dtx;
vad_enabled_ = enable_vad;
vad_mode_ = mode;
// If a send codec is registered, set VAD/DTX for the codec.
if (HaveValidEncoder("SetVAD") && codecs_[current_send_codec_idx_]->SetVAD(
&dtx_enabled_, &vad_enabled_, &vad_mode_) < 0) {
// SetVAD failed.
WEBRTC_TRACE(webrtc::kTraceError, webrtc::kTraceAudioCoding, id_,
"SetVAD failed");
vad_enabled_ = false;
dtx_enabled_ = false;
return -1;
}
return 0;
}
// Get VAD/DTX settings.
int AudioCodingModuleImpl::VAD(bool* dtx_enabled, bool* vad_enabled,
ACMVADMode* mode) const {
CriticalSectionScoped lock(acm_crit_sect_);
*dtx_enabled = dtx_enabled_;
*vad_enabled = vad_enabled_;
*mode = vad_mode_;
return 0;
}
/////////////////////////////////////////
// Receiver
//
int AudioCodingModuleImpl::InitializeReceiver() {
CriticalSectionScoped lock(acm_crit_sect_);
return InitializeReceiverSafe();
}
// Initialize receiver, resets codec database etc.
int AudioCodingModuleImpl::InitializeReceiverSafe() {
// If the receiver is already initialized then we want to destroy any
// existing decoders. After a call to this function, we should have a clean
// start-up.
if (receiver_initialized_) {
if (receiver_.RemoveAllCodecs() < 0)
return -1;
}
receiver_.set_id(id_);
receiver_.ResetInitialDelay();
receiver_.SetMinimumDelay(0);
receiver_.SetMaximumDelay(0);
receiver_.FlushBuffers();
// Register RED and CN.
for (int i = 0; i < ACMCodecDB::kNumCodecs; i++) {
if (IsCodecRED(i) || IsCodecCN(i)) {
uint8_t pl_type = static_cast<uint8_t>(ACMCodecDB::database_[i].pltype);
if (receiver_.AddCodec(i, pl_type, 1, NULL) < 0) {
WEBRTC_TRACE(webrtc::kTraceError, webrtc::kTraceAudioCoding, id_,
"Cannot register master codec.");
return -1;
}
}
}
receiver_initialized_ = true;
return 0;
}
// TODO(turajs): If NetEq opens an API for reseting the state of decoders then
// implement this method. Otherwise it should be removed. I might be that by
// removing and registering a decoder we can achieve the effect of resetting.
// Reset the decoder state.
int AudioCodingModuleImpl::ResetDecoder() {
return 0;
}
// Get current receive frequency.
int AudioCodingModuleImpl::ReceiveFrequency() const {
WEBRTC_TRACE(webrtc::kTraceStream, webrtc::kTraceAudioCoding, id_,
"ReceiveFrequency()");
CriticalSectionScoped lock(acm_crit_sect_);
int codec_id = receiver_.last_audio_codec_id();
return codec_id < 0 ? receiver_.current_sample_rate_hz() :
ACMCodecDB::database_[codec_id].plfreq;
}
// Get current playout frequency.
int AudioCodingModuleImpl::PlayoutFrequency() const {
WEBRTC_TRACE(webrtc::kTraceStream, webrtc::kTraceAudioCoding, id_,
"PlayoutFrequency()");
CriticalSectionScoped lock(acm_crit_sect_);
return receiver_.current_sample_rate_hz();
}
// Register possible receive codecs, can be called multiple times,
// for codecs, CNG (NB, WB and SWB), DTMF, RED.
int AudioCodingModuleImpl::RegisterReceiveCodec(const CodecInst& codec) {
CriticalSectionScoped lock(acm_crit_sect_);
if (codec.channels > 2 || codec.channels < 0) {
WEBRTC_TRACE(webrtc::kTraceError, webrtc::kTraceAudioCoding, id_,
"Unsupported number of channels, %d.", codec.channels);
return -1;
}
// TODO(turajs) do we need this for NetEq 4?
if (!receiver_initialized_) {
if (InitializeReceiverSafe() < 0) {
WEBRTC_TRACE(webrtc::kTraceError, webrtc::kTraceAudioCoding, id_,
"Cannot initialize receiver, failed registering codec.");
return -1;
}
}
int mirror_id;
int codec_id = ACMCodecDB::ReceiverCodecNumber(codec, &mirror_id);
if (codec_id < 0 || codec_id >= ACMCodecDB::kNumCodecs) {
WEBRTC_TRACE(webrtc::kTraceError, webrtc::kTraceAudioCoding, id_,
"Wrong codec params to be registered as receive codec");
return -1;
}
// Check if the payload-type is valid.
if (!ACMCodecDB::ValidPayloadType(codec.pltype)) {
WEBRTC_TRACE(webrtc::kTraceError, webrtc::kTraceAudioCoding, id_,
"Invalid payload-type %d for %s.", codec.pltype,
codec.plname);
return -1;
}
AudioDecoder* decoder = NULL;
// Get |decoder| associated with |codec|. |decoder| can be NULL if |codec|
// does not own its decoder.
if (GetAudioDecoder(codec, codec_id, mirror_id, &decoder) < 0) {
WEBRTC_TRACE(webrtc::kTraceError, webrtc::kTraceAudioCoding, id_,
"Wrong codec params to be registered as receive codec");
return -1;
}
uint8_t payload_type = static_cast<uint8_t>(codec.pltype);
return receiver_.AddCodec(codec_id, payload_type, codec.channels, decoder);
}
// Get current received codec.
int AudioCodingModuleImpl::ReceiveCodec(CodecInst* current_codec) const {
return receiver_.LastAudioCodec(current_codec);
}
// Incoming packet from network parsed and ready for decode.
int AudioCodingModuleImpl::IncomingPacket(const uint8_t* incoming_payload,
const size_t payload_length,
const WebRtcRTPHeader& rtp_header) {
int last_audio_pltype = receiver_.last_audio_payload_type();
if (receiver_.InsertPacket(rtp_header, incoming_payload, payload_length) <
0) {
return -1;
}
if (receiver_.last_audio_payload_type() != last_audio_pltype) {
int index = receiver_.last_audio_codec_id();
assert(index >= 0);
CriticalSectionScoped lock(acm_crit_sect_);
// |codec_[index]| might not be even created, simply because it is not
// yet registered as send codec. Even if it is registered, unless the
// codec shares same instance for encoder and decoder, this call is
// useless.
if (codecs_[index] != NULL)
codecs_[index]->UpdateDecoderSampFreq(index);
}
return 0;
}
// Minimum playout delay (Used for lip-sync).
int AudioCodingModuleImpl::SetMinimumPlayoutDelay(int time_ms) {
if ((time_ms < 0) || (time_ms > 10000)) {
WEBRTC_TRACE(webrtc::kTraceError, webrtc::kTraceAudioCoding, id_,
"Delay must be in the range of 0-1000 milliseconds.");
return -1;
}
return receiver_.SetMinimumDelay(time_ms);
}
int AudioCodingModuleImpl::SetMaximumPlayoutDelay(int time_ms) {
if ((time_ms < 0) || (time_ms > 10000)) {
WEBRTC_TRACE(webrtc::kTraceError, webrtc::kTraceAudioCoding, id_,
"Delay must be in the range of 0-1000 milliseconds.");
return -1;
}
return receiver_.SetMaximumDelay(time_ms);
}
// Estimate the Bandwidth based on the incoming stream, needed for one way
// audio where the RTCP send the BW estimate.
// This is also done in the RTP module.
int AudioCodingModuleImpl::DecoderEstimatedBandwidth() const {
// We can estimate far-end to near-end bandwidth if the iSAC are sent. Check
// if the last received packets were iSAC packet then retrieve the bandwidth.
int last_audio_codec_id = receiver_.last_audio_codec_id();
if (last_audio_codec_id >= 0 &&
STR_CASE_CMP("ISAC", ACMCodecDB::database_[last_audio_codec_id].plname)) {
CriticalSectionScoped lock(acm_crit_sect_);
return codecs_[last_audio_codec_id]->GetEstimatedBandwidth();
}
return -1;
}
// Set playout mode for: voice, fax, streaming or off.
int AudioCodingModuleImpl::SetPlayoutMode(AudioPlayoutMode mode) {
receiver_.SetPlayoutMode(mode);
return 0; // TODO(turajs): return value is for backward compatibility.
}
// Get playout mode voice, fax, streaming or off.
AudioPlayoutMode AudioCodingModuleImpl::PlayoutMode() const {
return receiver_.PlayoutMode();
}
// Get 10 milliseconds of raw audio data to play out.
// Automatic resample to the requested frequency.
int AudioCodingModuleImpl::PlayoutData10Ms(int desired_freq_hz,
AudioFrame* audio_frame) {
// GetAudio always returns 10 ms, at the requested sample rate.
if (receiver_.GetAudio(desired_freq_hz, audio_frame) != 0) {
WEBRTC_TRACE(webrtc::kTraceError, webrtc::kTraceAudioCoding, id_,
"PlayoutData failed, RecOut Failed");
return -1;
}
audio_frame->id_ = id_;
return 0;
}
/////////////////////////////////////////
// Statistics
//
// TODO(turajs) change the return value to void. Also change the corresponding
// NetEq function.
int AudioCodingModuleImpl::NetworkStatistics(ACMNetworkStatistics* statistics) {
receiver_.NetworkStatistics(statistics);
return 0;
}
int AudioCodingModuleImpl::RegisterVADCallback(ACMVADCallback* vad_callback) {
WEBRTC_TRACE(webrtc::kTraceDebug, webrtc::kTraceAudioCoding, id_,
"RegisterVADCallback()");
CriticalSectionScoped lock(callback_crit_sect_);
vad_callback_ = vad_callback;
return 0;
}
// TODO(tlegrand): Modify this function to work for stereo, and add tests.
int AudioCodingModuleImpl::IncomingPayload(const uint8_t* incoming_payload,
size_t payload_length,
uint8_t payload_type,
uint32_t timestamp) {
// We are not acquiring any lock when interacting with |aux_rtp_header_| no
// other method uses this member variable.
if (aux_rtp_header_ == NULL) {
// This is the first time that we are using |dummy_rtp_header_|
// so we have to create it.
aux_rtp_header_ = new WebRtcRTPHeader;
aux_rtp_header_->header.payloadType = payload_type;
// Don't matter in this case.
aux_rtp_header_->header.ssrc = 0;
aux_rtp_header_->header.markerBit = false;
// Start with random numbers.
aux_rtp_header_->header.sequenceNumber = 0x1234; // Arbitrary.
aux_rtp_header_->type.Audio.channel = 1;
}
aux_rtp_header_->header.timestamp = timestamp;
IncomingPacket(incoming_payload, payload_length, *aux_rtp_header_);
// Get ready for the next payload.
aux_rtp_header_->header.sequenceNumber++;
return 0;
}
int AudioCodingModuleImpl::ReplaceInternalDTXWithWebRtc(bool use_webrtc_dtx) {
CriticalSectionScoped lock(acm_crit_sect_);
if (!HaveValidEncoder("ReplaceInternalDTXWithWebRtc")) {
WEBRTC_TRACE(
webrtc::kTraceError, webrtc::kTraceAudioCoding, id_,
"Cannot replace codec internal DTX when no send codec is registered.");
return -1;
}
int res = codecs_[current_send_codec_idx_]->ReplaceInternalDTX(
use_webrtc_dtx);
// Check if VAD is turned on, or if there is any error.
if (res == 1) {
vad_enabled_ = true;
} else if (res < 0) {
WEBRTC_TRACE(webrtc::kTraceError, webrtc::kTraceAudioCoding, id_,
"Failed to set ReplaceInternalDTXWithWebRtc(%d)",
use_webrtc_dtx);
return res;
}
return 0;
}
int AudioCodingModuleImpl::IsInternalDTXReplacedWithWebRtc(
bool* uses_webrtc_dtx) {
CriticalSectionScoped lock(acm_crit_sect_);
if (!HaveValidEncoder("IsInternalDTXReplacedWithWebRtc")) {
return -1;
}
if (codecs_[current_send_codec_idx_]->IsInternalDTXReplaced(uses_webrtc_dtx)
< 0) {
return -1;
}
return 0;
}
int AudioCodingModuleImpl::SetISACMaxRate(int max_bit_per_sec) {
CriticalSectionScoped lock(acm_crit_sect_);
if (!HaveValidEncoder("SetISACMaxRate")) {
return -1;
}
return codecs_[current_send_codec_idx_]->SetISACMaxRate(max_bit_per_sec);
}
int AudioCodingModuleImpl::SetISACMaxPayloadSize(int max_size_bytes) {
CriticalSectionScoped lock(acm_crit_sect_);
if (!HaveValidEncoder("SetISACMaxPayloadSize")) {
return -1;
}
return codecs_[current_send_codec_idx_]->SetISACMaxPayloadSize(
max_size_bytes);
}
int AudioCodingModuleImpl::ConfigISACBandwidthEstimator(
int frame_size_ms,
int rate_bit_per_sec,
bool enforce_frame_size) {
CriticalSectionScoped lock(acm_crit_sect_);
if (!HaveValidEncoder("ConfigISACBandwidthEstimator")) {
return -1;
}
return codecs_[current_send_codec_idx_]->ConfigISACBandwidthEstimator(
frame_size_ms, rate_bit_per_sec, enforce_frame_size);
}
// Informs Opus encoder of the maximum playback rate the receiver will render.
int AudioCodingModuleImpl::SetOpusMaxPlaybackRate(int frequency_hz) {
CriticalSectionScoped lock(acm_crit_sect_);
if (!HaveValidEncoder("SetOpusMaxPlaybackRate")) {
return -1;
}
return codecs_[current_send_codec_idx_]->SetOpusMaxPlaybackRate(frequency_hz);
}
int AudioCodingModuleImpl::PlayoutTimestamp(uint32_t* timestamp) {
return receiver_.GetPlayoutTimestamp(timestamp) ? 0 : -1;
}
bool AudioCodingModuleImpl::HaveValidEncoder(const char* caller_name) const {
if ((!send_codec_registered_) || (current_send_codec_idx_ < 0) ||
(current_send_codec_idx_ >= ACMCodecDB::kNumCodecs)) {
WEBRTC_TRACE(webrtc::kTraceError, webrtc::kTraceAudioCoding, id_,
"%s failed: No send codec is registered.", caller_name);
return false;
}
if ((current_send_codec_idx_ < 0) ||
(current_send_codec_idx_ >= ACMCodecDB::kNumCodecs)) {
WEBRTC_TRACE(webrtc::kTraceError, webrtc::kTraceAudioCoding, id_,
"%s failed: Send codec index out of range.", caller_name);
return false;
}
if (codecs_[current_send_codec_idx_] == NULL) {
WEBRTC_TRACE(webrtc::kTraceError, webrtc::kTraceAudioCoding, id_,
"%s failed: Send codec is NULL pointer.", caller_name);
return false;
}
return true;
}
int AudioCodingModuleImpl::UnregisterReceiveCodec(uint8_t payload_type) {
return receiver_.RemoveCodec(payload_type);
}
// TODO(turajs): correct the type of |length_bytes| when it is corrected in
// GenericCodec.
int AudioCodingModuleImpl::REDPayloadISAC(int isac_rate,
int isac_bw_estimate,
uint8_t* payload,
int16_t* length_bytes) {
CriticalSectionScoped lock(acm_crit_sect_);
if (!HaveValidEncoder("EncodeData")) {
return -1;
}
int status;
status = codecs_[current_send_codec_idx_]->REDPayloadISAC(isac_rate,
isac_bw_estimate,
payload,
length_bytes);
return status;
}
void AudioCodingModuleImpl::ResetFragmentation(int vector_size) {
for (size_t n = 0; n < kMaxNumFragmentationVectors; n++) {
fragmentation_.fragmentationOffset[n] = n * MAX_PAYLOAD_SIZE_BYTE;
}
memset(fragmentation_.fragmentationLength, 0, kMaxNumFragmentationVectors *
sizeof(fragmentation_.fragmentationLength[0]));
memset(fragmentation_.fragmentationTimeDiff, 0, kMaxNumFragmentationVectors *
sizeof(fragmentation_.fragmentationTimeDiff[0]));
memset(fragmentation_.fragmentationPlType,
0,
kMaxNumFragmentationVectors *
sizeof(fragmentation_.fragmentationPlType[0]));
fragmentation_.fragmentationVectorSize = static_cast<uint16_t>(vector_size);
}
int AudioCodingModuleImpl::GetAudioDecoder(const CodecInst& codec, int codec_id,
int mirror_id,
AudioDecoder** decoder) {
if (ACMCodecDB::OwnsDecoder(codec_id)) {
// This codec has to own its own decoder. Therefore, it should create the
// corresponding AudioDecoder class and insert it into NetEq. If the codec
// does not exist create it.
//
// TODO(turajs): this part of the code is common with RegisterSendCodec(),
// make a method for it.
if (codecs_[mirror_id] == NULL) {
codecs_[mirror_id] = CreateCodec(codec);
if (codecs_[mirror_id] == NULL) {
WEBRTC_TRACE(webrtc::kTraceError, webrtc::kTraceAudioCoding, id_,
"Cannot Create the codec");
return -1;
}
mirror_codec_idx_[mirror_id] = mirror_id;
}
if (mirror_id != codec_id) {
codecs_[codec_id] = codecs_[mirror_id];
mirror_codec_idx_[codec_id] = mirror_id;
}
*decoder = codecs_[codec_id]->Decoder(codec_id);
if (!*decoder) {
assert(false);
return -1;
}
} else {
*decoder = NULL;
}
return 0;
}
int AudioCodingModuleImpl::SetInitialPlayoutDelay(int delay_ms) {
{
CriticalSectionScoped lock(acm_crit_sect_);
// Initialize receiver, if it is not initialized. Otherwise, initial delay
// is reset upon initialization of the receiver.
if (!receiver_initialized_)
InitializeReceiverSafe();
}
return receiver_.SetInitialDelay(delay_ms);
}
int AudioCodingModuleImpl::EnableNack(size_t max_nack_list_size) {
return receiver_.EnableNack(max_nack_list_size);
}
void AudioCodingModuleImpl::DisableNack() {
receiver_.DisableNack();
}
std::vector<uint16_t> AudioCodingModuleImpl::GetNackList(
int64_t round_trip_time_ms) const {
return receiver_.GetNackList(round_trip_time_ms);
}
int AudioCodingModuleImpl::LeastRequiredDelayMs() const {
return receiver_.LeastRequiredDelayMs();
}
void AudioCodingModuleImpl::GetDecodingCallStatistics(
AudioDecodingCallStats* call_stats) const {
receiver_.GetDecodingCallStatistics(call_stats);
}
} // namespace acm2
bool AudioCodingImpl::RegisterSendCodec(AudioEncoder* send_codec) {
FATAL() << "Not implemented yet.";
return false;
}
bool AudioCodingImpl::RegisterSendCodec(int encoder_type,
uint8_t payload_type,
int frame_size_samples) {
std::string codec_name;
int sample_rate_hz;
int channels;
if (!MapCodecTypeToParameters(
encoder_type, &codec_name, &sample_rate_hz, &channels)) {
return false;
}
webrtc::CodecInst codec;
AudioCodingModule::Codec(
codec_name.c_str(), &codec, sample_rate_hz, channels);
codec.pltype = payload_type;
if (frame_size_samples > 0) {
codec.pacsize = frame_size_samples;
}
return acm_old_->RegisterSendCodec(codec) == 0;
}
const AudioEncoder* AudioCodingImpl::GetSenderInfo() const {
FATAL() << "Not implemented yet.";
return reinterpret_cast<const AudioEncoder*>(NULL);
}
const CodecInst* AudioCodingImpl::GetSenderCodecInst() {
if (acm_old_->SendCodec(&current_send_codec_) != 0) {
return NULL;
}
return &current_send_codec_;
}
int AudioCodingImpl::Add10MsAudio(const AudioFrame& audio_frame) {
if (acm_old_->Add10MsData(audio_frame) != 0) {
return -1;
}
return acm_old_->Process();
}
const ReceiverInfo* AudioCodingImpl::GetReceiverInfo() const {
FATAL() << "Not implemented yet.";
return reinterpret_cast<const ReceiverInfo*>(NULL);
}
bool AudioCodingImpl::RegisterReceiveCodec(AudioDecoder* receive_codec) {
FATAL() << "Not implemented yet.";
return false;
}
bool AudioCodingImpl::RegisterReceiveCodec(int decoder_type,
uint8_t payload_type) {
std::string codec_name;
int sample_rate_hz;
int channels;
if (!MapCodecTypeToParameters(
decoder_type, &codec_name, &sample_rate_hz, &channels)) {
return false;
}
webrtc::CodecInst codec;
AudioCodingModule::Codec(
codec_name.c_str(), &codec, sample_rate_hz, channels);
codec.pltype = payload_type;
return acm_old_->RegisterReceiveCodec(codec) == 0;
}
bool AudioCodingImpl::InsertPacket(const uint8_t* incoming_payload,
size_t payload_len_bytes,
const WebRtcRTPHeader& rtp_info) {
return acm_old_->IncomingPacket(
incoming_payload, payload_len_bytes, rtp_info) == 0;
}
bool AudioCodingImpl::InsertPayload(const uint8_t* incoming_payload,
size_t payload_len_byte,
uint8_t payload_type,
uint32_t timestamp) {
FATAL() << "Not implemented yet.";
return false;
}
bool AudioCodingImpl::SetMinimumPlayoutDelay(int time_ms) {
FATAL() << "Not implemented yet.";
return false;
}
bool AudioCodingImpl::SetMaximumPlayoutDelay(int time_ms) {
FATAL() << "Not implemented yet.";
return false;
}
int AudioCodingImpl::LeastRequiredDelayMs() const {
FATAL() << "Not implemented yet.";
return -1;
}
bool AudioCodingImpl::PlayoutTimestamp(uint32_t* timestamp) {
FATAL() << "Not implemented yet.";
return false;
}
bool AudioCodingImpl::Get10MsAudio(AudioFrame* audio_frame) {
return acm_old_->PlayoutData10Ms(playout_frequency_hz_, audio_frame) == 0;
}
bool AudioCodingImpl::NetworkStatistics(
ACMNetworkStatistics* network_statistics) {
FATAL() << "Not implemented yet.";
return false;
}
bool AudioCodingImpl::EnableNack(size_t max_nack_list_size) {
FATAL() << "Not implemented yet.";
return false;
}
void AudioCodingImpl::DisableNack() {
// A bug in the linker of Visual Studio 2013 Update 3 prevent us from using
// FATAL() here, if we do so then the linker hang when the WPO is turned on.
// TODO(sebmarchand): Re-evaluate this when we upgrade the toolchain.
}
bool AudioCodingImpl::SetVad(bool enable_dtx,
bool enable_vad,
ACMVADMode vad_mode) {
return acm_old_->SetVAD(enable_dtx, enable_vad, vad_mode) == 0;
}
std::vector<uint16_t> AudioCodingImpl::GetNackList(
int round_trip_time_ms) const {
return acm_old_->GetNackList(round_trip_time_ms);
}
void AudioCodingImpl::GetDecodingCallStatistics(
AudioDecodingCallStats* call_stats) const {
acm_old_->GetDecodingCallStatistics(call_stats);
}
bool AudioCodingImpl::MapCodecTypeToParameters(int codec_type,
std::string* codec_name,
int* sample_rate_hz,
int* channels) {
switch (codec_type) {
#ifdef WEBRTC_CODEC_PCM16
case acm2::ACMCodecDB::kPCM16B:
*codec_name = "L16";
*sample_rate_hz = 8000;
*channels = 1;
break;
case acm2::ACMCodecDB::kPCM16Bwb:
*codec_name = "L16";
*sample_rate_hz = 16000;
*channels = 1;
break;
case acm2::ACMCodecDB::kPCM16Bswb32kHz:
*codec_name = "L16";
*sample_rate_hz = 32000;
*channels = 1;
break;
case acm2::ACMCodecDB::kPCM16B_2ch:
*codec_name = "L16";
*sample_rate_hz = 8000;
*channels = 2;
break;
case acm2::ACMCodecDB::kPCM16Bwb_2ch:
*codec_name = "L16";
*sample_rate_hz = 16000;
*channels = 2;
break;
case acm2::ACMCodecDB::kPCM16Bswb32kHz_2ch:
*codec_name = "L16";
*sample_rate_hz = 32000;
*channels = 2;
break;
#endif
#if (defined(WEBRTC_CODEC_ISAC) || defined(WEBRTC_CODEC_ISACFX))
case acm2::ACMCodecDB::kISAC:
*codec_name = "ISAC";
*sample_rate_hz = 16000;
*channels = 1;
break;
#endif
#ifdef WEBRTC_CODEC_ISAC
case acm2::ACMCodecDB::kISACSWB:
*codec_name = "ISAC";
*sample_rate_hz = 32000;
*channels = 1;
break;
case acm2::ACMCodecDB::kISACFB:
*codec_name = "ISAC";
*sample_rate_hz = 48000;
*channels = 1;
break;
#endif
#ifdef WEBRTC_CODEC_ILBC
case acm2::ACMCodecDB::kILBC:
*codec_name = "ILBC";
*sample_rate_hz = 8000;
*channels = 1;
break;
#endif
case acm2::ACMCodecDB::kPCMA:
*codec_name = "PCMA";
*sample_rate_hz = 8000;
*channels = 1;
break;
case acm2::ACMCodecDB::kPCMA_2ch:
*codec_name = "PCMA";
*sample_rate_hz = 8000;
*channels = 2;
break;
case acm2::ACMCodecDB::kPCMU:
*codec_name = "PCMU";
*sample_rate_hz = 8000;
*channels = 1;
break;
case acm2::ACMCodecDB::kPCMU_2ch:
*codec_name = "PCMU";
*sample_rate_hz = 8000;
*channels = 2;
break;
#ifdef WEBRTC_CODEC_G722
case acm2::ACMCodecDB::kG722:
*codec_name = "G722";
*sample_rate_hz = 16000;
*channels = 1;
break;
case acm2::ACMCodecDB::kG722_2ch:
*codec_name = "G722";
*sample_rate_hz = 16000;
*channels = 2;
break;
#endif
#ifdef WEBRTC_CODEC_OPUS
case acm2::ACMCodecDB::kOpus:
*codec_name = "opus";
*sample_rate_hz = 48000;
*channels = 2;
break;
#endif
case acm2::ACMCodecDB::kCNNB:
*codec_name = "CN";
*sample_rate_hz = 8000;
*channels = 1;
break;
case acm2::ACMCodecDB::kCNWB:
*codec_name = "CN";
*sample_rate_hz = 16000;
*channels = 1;
break;
case acm2::ACMCodecDB::kCNSWB:
*codec_name = "CN";
*sample_rate_hz = 32000;
*channels = 1;
break;
case acm2::ACMCodecDB::kRED:
*codec_name = "red";
*sample_rate_hz = 8000;
*channels = 1;
break;
#ifdef WEBRTC_CODEC_AVT
case acm2::ACMCodecDB::kAVT:
*codec_name = "telephone-event";
*sample_rate_hz = 8000;
*channels = 1;
break;
#endif
default:
FATAL() << "Codec type " << codec_type << " not supported.";
}
return true;
}
} // namespace webrtc