diff options
author | Zeno Albisser <zeno.albisser@digia.com> | 2013-08-15 21:46:11 +0200 |
---|---|---|
committer | Zeno Albisser <zeno.albisser@digia.com> | 2013-08-15 21:46:11 +0200 |
commit | 679147eead574d186ebf3069647b4c23e8ccace6 (patch) | |
tree | fc247a0ac8ff119f7c8550879ebb6d3dd8d1ff69 /chromium/third_party/webrtc/modules/audio_coding/main/source/audio_coding_module_impl.cc |
Initial import.
Diffstat (limited to 'chromium/third_party/webrtc/modules/audio_coding/main/source/audio_coding_module_impl.cc')
-rw-r--r-- | chromium/third_party/webrtc/modules/audio_coding/main/source/audio_coding_module_impl.cc | 3129 |
1 files changed, 3129 insertions, 0 deletions
diff --git a/chromium/third_party/webrtc/modules/audio_coding/main/source/audio_coding_module_impl.cc b/chromium/third_party/webrtc/modules/audio_coding/main/source/audio_coding_module_impl.cc new file mode 100644 index 00000000000..f35ec04c2fc --- /dev/null +++ b/chromium/third_party/webrtc/modules/audio_coding/main/source/audio_coding_module_impl.cc @@ -0,0 +1,3129 @@ +/* + * Copyright (c) 2012 The WebRTC project authors. All Rights Reserved. + * + * Use of this source code is governed by a BSD-style license + * that can be found in the LICENSE file in the root of the source + * tree. An additional intellectual property rights grant can be found + * in the file PATENTS. All contributing project authors may + * be found in the AUTHORS file in the root of the source tree. + */ + +#include "webrtc/modules/audio_coding/main/source/audio_coding_module_impl.h" + +#include <assert.h> +#include <stdlib.h> + +#include <algorithm> // For std::max. + +#include "webrtc/engine_configurations.h" +#include "webrtc/modules/audio_coding/main/source/acm_codec_database.h" +#include "webrtc/modules/audio_coding/main/source/acm_common_defs.h" +#include "webrtc/modules/audio_coding/main/source/acm_dtmf_detection.h" +#include "webrtc/modules/audio_coding/main/source/acm_generic_codec.h" +#include "webrtc/modules/audio_coding/main/source/acm_resampler.h" +#include "webrtc/modules/audio_coding/main/source/nack.h" +#include "webrtc/system_wrappers/interface/clock.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" +#include "webrtc/system_wrappers/interface/trace_event.h" + +namespace webrtc { + +enum { + kACMToneEnd = 999 +}; + +// Maximum number of bytes in one packet (PCM16B, 20 ms packets, stereo). +enum { + kMaxPacketSize = 2560 +}; + +// Maximum number of payloads that can be packed in one RED payload. For +// regular FEC, we only pack two payloads. In case of dual-streaming, in worst +// case we might pack 3 payloads in one RED payload. +enum { + kNumFecFragmentationVectors = 2, + kMaxNumFragmentationVectors = 3 +}; + +static const uint32_t kMaskTimestamp = 0x03ffffff; +static const int kDefaultTimestampDiff = 960; // 20 ms @ 48 kHz. + +// If packet N is arrived all packets prior to N - |kNackThresholdPackets| which +// are not received are considered as lost, and appear in NACK list. +static const int kNackThresholdPackets = 2; + +namespace { + +bool IsCodecRED(const CodecInst* codec) { + return (STR_CASE_CMP(codec->plname, "RED") == 0); +} + +bool IsCodecRED(int index) { + return (IsCodecRED(&ACMCodecDB::database_[index])); +} + +bool IsCodecCN(const CodecInst* codec) { + return (STR_CASE_CMP(codec->plname, "CN") == 0); +} + +bool IsCodecCN(int index) { + return (IsCodecCN(&ACMCodecDB::database_[index])); +} + +// Stereo-to-mono can be used as in-place. +int DownMix(const AudioFrame& frame, int length_out_buff, int16_t* out_buff) { + if (length_out_buff < frame.samples_per_channel_) { + return -1; + } + for (int n = 0; n < frame.samples_per_channel_; ++n) + out_buff[n] = (frame.data_[2 * n] + frame.data_[2 * n + 1]) >> 1; + return 0; +} + +// Mono-to-stereo can be used as in-place. +int UpMix(const AudioFrame& frame, int length_out_buff, int16_t* out_buff) { + if (length_out_buff < frame.samples_per_channel_) { + return -1; + } + for (int n = frame.samples_per_channel_ - 1; n >= 0; --n) { + out_buff[2 * n + 1] = frame.data_[n]; + out_buff[2 * n] = frame.data_[n]; + } + return 0; +} + +// Return 1 if timestamp t1 is less than timestamp t2, while compensating for +// wrap-around. +int TimestampLessThan(uint32_t t1, uint32_t t2) { + uint32_t kHalfFullRange = static_cast<uint32_t>(0xFFFFFFFF) / 2; + if (t1 == t2) { + return 0; + } else if (t1 < t2) { + if (t2 - t1 < kHalfFullRange) + return 1; + return 0; + } else { + if (t1 - t2 < kHalfFullRange) + return 0; + return 1; + } +} + +} // namespace + +AudioCodingModuleImpl::AudioCodingModuleImpl(const int32_t id, Clock* clock) + : packetization_callback_(NULL), + id_(id), + last_timestamp_(0xD87F3F9F), + last_in_timestamp_(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_receive_registered_(false), + stereo_send_(false), + prev_received_channel_(0), + expected_channels_(1), + current_send_codec_idx_(-1), + current_receive_codec_idx_(-1), + send_codec_registered_(false), + acm_crit_sect_(CriticalSectionWrapper::CreateCriticalSection()), + vad_callback_(NULL), + last_recv_audio_codec_pltype_(255), + is_first_red_(true), + fec_enabled_(false), + last_fec_timestamp_(0), + receive_red_pltype_(255), + previous_pltype_(255), + dummy_rtp_header_(NULL), + recv_pl_frame_size_smpls_(0), + receiver_initialized_(false), + dtmf_detector_(NULL), + dtmf_callback_(NULL), + last_detected_tone_(kACMToneEnd), + callback_crit_sect_(CriticalSectionWrapper::CreateCriticalSection()), + secondary_send_codec_inst_(), + secondary_encoder_(NULL), + initial_delay_ms_(0), + num_packets_accumulated_(0), + num_bytes_accumulated_(0), + accumulated_audio_ms_(0), + first_payload_received_(false), + last_incoming_send_timestamp_(0), + track_neteq_buffer_(false), + playout_ts_(0), + av_sync_(false), + last_timestamp_diff_(kDefaultTimestampDiff), + last_sequence_number_(0), + last_ssrc_(0), + last_packet_was_sync_(false), + clock_(clock), + nack_(), + nack_enabled_(false) { + + // Nullify send codec memory, set payload type and set codec name to + // invalid values. + const char no_name[] = "noCodecRegistered"; + strncpy(send_codec_inst_.plname, no_name, RTP_PAYLOAD_NAME_SIZE - 1); + send_codec_inst_.pltype = -1; + + strncpy(secondary_send_codec_inst_.plname, no_name, + RTP_PAYLOAD_NAME_SIZE - 1); + secondary_send_codec_inst_.pltype = -1; + + for (int i = 0; i < ACMCodecDB::kMaxNumCodecs; i++) { + codecs_[i] = NULL; + registered_pltypes_[i] = -1; + stereo_receive_[i] = false; + slave_codecs_[i] = NULL; + mirror_codec_idx_[i] = -1; + } + + neteq_.set_id(id_); + + // Allocate memory for RED. + red_buffer_ = new uint8_t[MAX_PAYLOAD_SIZE_BYTE]; + + // TODO(turajs): This might not be exactly how this class is supposed to work. + // The external usage might be that |fragmentationVectorSize| has to match + // the allocated space for the member-arrays, while here, we allocate + // according to the maximum number of fragmentations and change + // |fragmentationVectorSize| on-the-fly based on actual number of + // fragmentations. However, due to copying to local variable before calling + // SendData, the RTP module receives a "valid" fragmentation, where allocated + // space matches |fragmentationVectorSize|, therefore, this should not cause + // any problem. A better approach is not using RTPFragmentationHeader as + // member variable, instead, use an ACM-specific structure to hold RED-related + // data. See module_common_type.h for the definition of + // RTPFragmentationHeader. + fragmentation_.VerifyAndAllocateFragmentationHeader( + kMaxNumFragmentationVectors); + + // Register the default payload type for RED and for CNG at sampling rates of + // 8, 16, 32 and 48 kHz. + for (int i = (ACMCodecDB::kNumCodecs - 1); i >= 0; i--) { + if (IsCodecRED(i)) { + red_pltype_ = static_cast<uint8_t>(ACMCodecDB::database_[i].pltype); + } else if (IsCodecCN(i)) { + if (ACMCodecDB::database_[i].plfreq == 8000) { + cng_nb_pltype_ = static_cast<uint8_t>(ACMCodecDB::database_[i].pltype); + } else if (ACMCodecDB::database_[i].plfreq == 16000) { + cng_wb_pltype_ = static_cast<uint8_t>(ACMCodecDB::database_[i].pltype); + } else if (ACMCodecDB::database_[i].plfreq == 32000) { + cng_swb_pltype_ = static_cast<uint8_t>(ACMCodecDB::database_[i].pltype); + } else if (ACMCodecDB::database_[i].plfreq == 48000) { + cng_fb_pltype_ = static_cast<uint8_t>(ACMCodecDB::database_[i].pltype); + } + } + } + + if (InitializeReceiverSafe() < 0) { + WEBRTC_TRACE(webrtc::kTraceError, webrtc::kTraceAudioCoding, id_, + "Cannot initialize receiver"); + } + WEBRTC_TRACE(webrtc::kTraceMemory, webrtc::kTraceAudioCoding, id, "Created"); +} + +AudioCodingModuleImpl::~AudioCodingModuleImpl() { + { + CriticalSectionScoped lock(acm_crit_sect_); + current_send_codec_idx_ = -1; + + for (int i = 0; i < ACMCodecDB::kMaxNumCodecs; i++) { + if (codecs_[i] != NULL) { + // True stereo codecs share the same memory for master and + // slave, so slave codec need to be nullified here, since the + // memory will be deleted. + if (slave_codecs_[i] == codecs_[i]) { + slave_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 (slave_codecs_[i] != NULL) { + // Delete memory for stereo usage of mono codecs. + assert(mirror_codec_idx_[i] > -1); + if (slave_codecs_[mirror_codec_idx_[i]] != NULL) { + delete slave_codecs_[mirror_codec_idx_[i]]; + slave_codecs_[mirror_codec_idx_[i]] = NULL; + } + slave_codecs_[i] = NULL; + } + } + + if (dtmf_detector_ != NULL) { + delete dtmf_detector_; + dtmf_detector_ = NULL; + } + if (dummy_rtp_header_ != NULL) { + delete dummy_rtp_header_; + dummy_rtp_header_ = NULL; + } + if (red_buffer_ != NULL) { + delete[] red_buffer_; + red_buffer_ = 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); + } + } + } + + neteq_.set_id(id_); + return 0; +} + +// Returns the number of milliseconds until the module want a +// worker thread to call Process. +int32_t AudioCodingModuleImpl::TimeUntilNextProcess() { + CriticalSectionScoped lock(acm_crit_sect_); + + if (!HaveValidEncoder("TimeUntilNextProcess")) { + return -1; + } + return codecs_[current_send_codec_idx_]->SamplesLeftToEncode() / + (send_codec_inst_.plfreq / 1000); +} + +int32_t AudioCodingModuleImpl::Process() { + bool dual_stream; + { + CriticalSectionScoped lock(acm_crit_sect_); + dual_stream = (secondary_encoder_.get() != NULL); + } + if (dual_stream) { + return ProcessDualStream(); + } + return ProcessSingleStream(); +} + +int AudioCodingModuleImpl::EncodeFragmentation(int fragmentation_index, + int payload_type, + uint32_t current_timestamp, + ACMGenericCodec* encoder, + uint8_t* stream) { + int16_t len_bytes = MAX_PAYLOAD_SIZE_BYTE; + uint32_t rtp_timestamp; + WebRtcACMEncodingType encoding_type; + if (encoder->Encode(stream, &len_bytes, &rtp_timestamp, &encoding_type) < 0) { + return -1; + } + assert(encoding_type == kActiveNormalEncoded); + assert(len_bytes > 0); + + fragmentation_.fragmentationLength[fragmentation_index] = len_bytes; + fragmentation_.fragmentationPlType[fragmentation_index] = payload_type; + fragmentation_.fragmentationTimeDiff[fragmentation_index] = + static_cast<uint16_t>(current_timestamp - rtp_timestamp); + fragmentation_.fragmentationVectorSize++; + return len_bytes; +} + +// Primary payloads are sent immediately, whereas a single secondary payload is +// buffered to be combined with "the next payload." +// Normally "the next payload" would be a primary payload. In case two +// consecutive secondary payloads are generated with no primary payload in +// between, then two secondary payloads are packed in one RED. +int AudioCodingModuleImpl::ProcessDualStream() { + uint8_t stream[kMaxNumFragmentationVectors * MAX_PAYLOAD_SIZE_BYTE]; + uint32_t current_timestamp; + int16_t length_bytes = 0; + RTPFragmentationHeader my_fragmentation; + + uint8_t my_red_payload_type; + + { + CriticalSectionScoped lock(acm_crit_sect_); + // Check if there is an encoder before. + if (!HaveValidEncoder("ProcessDualStream") || + secondary_encoder_.get() == NULL) { + return -1; + } + ACMGenericCodec* primary_encoder = codecs_[current_send_codec_idx_]; + // If primary encoder has a full frame of audio to generate payload. + bool primary_ready_to_encode = primary_encoder->HasFrameToEncode(); + // If the secondary encoder has a frame of audio to generate a payload. + bool secondary_ready_to_encode = secondary_encoder_->HasFrameToEncode(); + + if (!primary_ready_to_encode && !secondary_ready_to_encode) { + // Nothing to send. + return 0; + } + int len_bytes_previous_secondary = static_cast<int>( + fragmentation_.fragmentationLength[2]); + assert(len_bytes_previous_secondary <= MAX_PAYLOAD_SIZE_BYTE); + bool has_previous_payload = len_bytes_previous_secondary > 0; + + uint32_t primary_timestamp = primary_encoder->EarliestTimestamp(); + uint32_t secondary_timestamp = secondary_encoder_->EarliestTimestamp(); + + if (!has_previous_payload && !primary_ready_to_encode && + secondary_ready_to_encode) { + // Secondary payload will be the ONLY bit-stream. Encode by secondary + // encoder, store the payload, and return. No packet is sent. + int16_t len_bytes = MAX_PAYLOAD_SIZE_BYTE; + WebRtcACMEncodingType encoding_type; + if (secondary_encoder_->Encode(red_buffer_, &len_bytes, + &last_fec_timestamp_, + &encoding_type) < 0) { + WEBRTC_TRACE(webrtc::kTraceError, webrtc::kTraceAudioCoding, id_, + "ProcessDual(): Encoding of secondary encoder Failed"); + return -1; + } + assert(len_bytes > 0); + assert(encoding_type == kActiveNormalEncoded); + assert(len_bytes <= MAX_PAYLOAD_SIZE_BYTE); + fragmentation_.fragmentationLength[2] = len_bytes; + return 0; + } + + // Initialize with invalid but different values, so later can have sanity + // check if they are different. + int index_primary = -1; + int index_secondary = -2; + int index_previous_secondary = -3; + + if (primary_ready_to_encode) { + index_primary = secondary_ready_to_encode ? + TimestampLessThan(primary_timestamp, secondary_timestamp) : 0; + index_primary += has_previous_payload ? + TimestampLessThan(primary_timestamp, last_fec_timestamp_) : 0; + } + + if (secondary_ready_to_encode) { + // Timestamp of secondary payload can only be less than primary payload, + // but is always larger than the timestamp of previous secondary payload. + index_secondary = primary_ready_to_encode ? + (1 - TimestampLessThan(primary_timestamp, secondary_timestamp)) : 0; + } + + if (has_previous_payload) { + index_previous_secondary = primary_ready_to_encode ? + (1 - TimestampLessThan(primary_timestamp, last_fec_timestamp_)) : 0; + // If secondary is ready it always have a timestamp larger than previous + // secondary. So the index is either 0 or 1. + index_previous_secondary += secondary_ready_to_encode ? 1 : 0; + } + + // Indices must not be equal. + assert(index_primary != index_secondary); + assert(index_primary != index_previous_secondary); + assert(index_secondary != index_previous_secondary); + + // One of the payloads has to be at position zero. + assert(index_primary == 0 || index_secondary == 0 || + index_previous_secondary == 0); + + // Timestamp of the RED payload. + if (index_primary == 0) { + current_timestamp = primary_timestamp; + } else if (index_secondary == 0) { + current_timestamp = secondary_timestamp; + } else { + current_timestamp = last_fec_timestamp_; + } + + fragmentation_.fragmentationVectorSize = 0; + if (has_previous_payload) { + assert(index_previous_secondary >= 0 && + index_previous_secondary < kMaxNumFragmentationVectors); + assert(len_bytes_previous_secondary <= MAX_PAYLOAD_SIZE_BYTE); + memcpy(&stream[index_previous_secondary * MAX_PAYLOAD_SIZE_BYTE], + red_buffer_, sizeof(stream[0]) * len_bytes_previous_secondary); + fragmentation_.fragmentationLength[index_previous_secondary] = + len_bytes_previous_secondary; + fragmentation_.fragmentationPlType[index_previous_secondary] = + secondary_send_codec_inst_.pltype; + fragmentation_.fragmentationTimeDiff[index_previous_secondary] = + static_cast<uint16_t>(current_timestamp - last_fec_timestamp_); + fragmentation_.fragmentationVectorSize++; + } + + if (primary_ready_to_encode) { + assert(index_primary >= 0 && index_primary < kMaxNumFragmentationVectors); + int i = index_primary * MAX_PAYLOAD_SIZE_BYTE; + if (EncodeFragmentation(index_primary, send_codec_inst_.pltype, + current_timestamp, primary_encoder, + &stream[i]) < 0) { + WEBRTC_TRACE(webrtc::kTraceError, webrtc::kTraceAudioCoding, id_, + "ProcessDualStream(): Encoding of primary encoder Failed"); + return -1; + } + } + + if (secondary_ready_to_encode) { + assert(index_secondary >= 0 && + index_secondary < kMaxNumFragmentationVectors - 1); + int i = index_secondary * MAX_PAYLOAD_SIZE_BYTE; + if (EncodeFragmentation(index_secondary, + secondary_send_codec_inst_.pltype, + current_timestamp, secondary_encoder_.get(), + &stream[i]) < 0) { + WEBRTC_TRACE(webrtc::kTraceError, webrtc::kTraceAudioCoding, id_, + "ProcessDualStream(): Encoding of secondary encoder " + "Failed"); + return -1; + } + } + // Copy to local variable, as it will be used outside the ACM lock. + my_fragmentation.CopyFrom(fragmentation_); + my_red_payload_type = red_pltype_; + length_bytes = 0; + for (int n = 0; n < fragmentation_.fragmentationVectorSize; n++) { + length_bytes += fragmentation_.fragmentationLength[n]; + } + } + + { + CriticalSectionScoped lock(callback_crit_sect_); + if (packetization_callback_ != NULL) { + // Callback with payload data, including redundant data (FEC/RED). + if (packetization_callback_->SendData(kAudioFrameSpeech, + my_red_payload_type, + current_timestamp, stream, + length_bytes, + &my_fragmentation) < 0) { + return -1; + } + } + } + + { + CriticalSectionScoped lock(acm_crit_sect_); + // Now that data is sent, clean up fragmentation. + ResetFragmentation(0); + } + return 0; +} + +// Process any pending tasks such as timeouts. +int AudioCodingModuleImpl::ProcessSingleStream() { + // Make room for 1 RED payload. + uint8_t stream[2 * MAX_PAYLOAD_SIZE_BYTE]; + int16_t length_bytes = 2 * MAX_PAYLOAD_SIZE_BYTE; + int16_t red_length_bytes = length_bytes; + uint32_t rtp_timestamp; + int16_t status; + WebRtcACMEncodingType encoding_type; + FrameType frame_type = kAudioFrameSpeech; + uint8_t current_payload_type = 0; + bool has_data_to_send = false; + bool fec_active = false; + RTPFragmentationHeader my_fragmentation; + + // Keep the scope of the ACM critical section limited. + { + CriticalSectionScoped lock(acm_crit_sect_); + // Check if there is an encoder before. + if (!HaveValidEncoder("ProcessSingleStream")) { + return -1; + } + status = codecs_[current_send_codec_idx_]->Encode(stream, &length_bytes, + &rtp_timestamp, + &encoding_type); + if (status < 0) { + // Encode failed. + WEBRTC_TRACE(webrtc::kTraceError, webrtc::kTraceAudioCoding, id_, + "ProcessSingleStream(): Encoding Failed"); + length_bytes = 0; + return -1; + } else if (status == 0) { + // Not enough data. + return 0; + } else { + switch (encoding_type) { + case kNoEncoding: { + current_payload_type = previous_pltype_; + frame_type = kFrameEmpty; + length_bytes = 0; + break; + } + case kActiveNormalEncoded: + case kPassiveNormalEncoded: { + current_payload_type = (uint8_t) send_codec_inst_.pltype; + frame_type = kAudioFrameSpeech; + break; + } + case kPassiveDTXNB: { + current_payload_type = cng_nb_pltype_; + frame_type = kAudioFrameCN; + is_first_red_ = true; + break; + } + case kPassiveDTXWB: { + current_payload_type = cng_wb_pltype_; + frame_type = kAudioFrameCN; + is_first_red_ = true; + break; + } + case kPassiveDTXSWB: { + current_payload_type = cng_swb_pltype_; + frame_type = kAudioFrameCN; + is_first_red_ = true; + break; + } + case kPassiveDTXFB: { + current_payload_type = cng_fb_pltype_; + frame_type = kAudioFrameCN; + is_first_red_ = true; + break; + } + } + has_data_to_send = true; + previous_pltype_ = current_payload_type; + + // Redundancy encode is done here. The two bitstreams packetized into + // one RTP packet and the fragmentation points are set. + // Only apply RED on speech data. + if ((fec_enabled_) && + ((encoding_type == kActiveNormalEncoded) || + (encoding_type == kPassiveNormalEncoded))) { + // FEC is enabled within this scope. + // + // Note that, a special solution exists for iSAC since it is the only + // codec for which GetRedPayload has a non-empty implementation. + // + // Summary of the FEC scheme below (use iSAC as example): + // + // 1st (is_first_red_ is true) encoded iSAC frame (primary #1) => + // - call GetRedPayload() and store redundancy for packet #1 in + // second fragment of RED buffer (old data) + // - drop the primary iSAC frame + // - don't call SendData + // 2nd (is_first_red_ is false) encoded iSAC frame (primary #2) => + // - store primary #2 in 1st fragment of RED buffer and send the + // combined packet + // - the transmitted packet contains primary #2 (new) and + // reduncancy for packet #1 (old) + // - call GetRed_Payload() and store redundancy for packet #2 in + // second fragment of RED buffer + // + // ... + // + // Nth encoded iSAC frame (primary #N) => + // - store primary #N in 1st fragment of RED buffer and send the + // combined packet + // - the transmitted packet contains primary #N (new) and + // reduncancy for packet #(N-1) (old) + // - call GetRedPayload() and store redundancy for packet #N in + // second fragment of RED buffer + // + // For all other codecs, GetRedPayload does nothing and returns -1 => + // redundant data is only a copy. + // + // First combined packet contains : #2 (new) and #1 (old) + // Second combined packet contains: #3 (new) and #2 (old) + // Third combined packet contains : #4 (new) and #3 (old) + // + // Hence, even if every second packet is dropped, perfect + // reconstruction is possible. + fec_active = true; + + has_data_to_send = false; + // Skip the following part for the first packet in a RED session. + if (!is_first_red_) { + // Rearrange stream such that FEC packets are included. + // Replace stream now that we have stored current stream. + memcpy(stream + fragmentation_.fragmentationOffset[1], red_buffer_, + fragmentation_.fragmentationLength[1]); + // Update the fragmentation time difference vector, in number of + // timestamps. + uint16_t time_since_last = uint16_t( + rtp_timestamp - last_fec_timestamp_); + + // Update fragmentation vectors. + fragmentation_.fragmentationPlType[1] = + fragmentation_.fragmentationPlType[0]; + fragmentation_.fragmentationTimeDiff[1] = time_since_last; + has_data_to_send = true; + } + + // Insert new packet length. + fragmentation_.fragmentationLength[0] = length_bytes; + + // Insert new packet payload type. + fragmentation_.fragmentationPlType[0] = current_payload_type; + last_fec_timestamp_ = rtp_timestamp; + + // Can be modified by the GetRedPayload() call if iSAC is utilized. + red_length_bytes = length_bytes; + + // A fragmentation header is provided => packetization according to + // RFC 2198 (RTP Payload for Redundant Audio Data) will be used. + // First fragment is the current data (new). + // Second fragment is the previous data (old). + length_bytes = static_cast<int16_t>( + fragmentation_.fragmentationLength[0] + + fragmentation_.fragmentationLength[1]); + + // Get, and store, redundant data from the encoder based on the recently + // encoded frame. + // NOTE - only iSAC contains an implementation; all other codecs does + // nothing and returns -1. + if (codecs_[current_send_codec_idx_]->GetRedPayload( + red_buffer_, + &red_length_bytes) == -1) { + // The codec was not iSAC => use current encoder output as redundant + // data instead (trivial FEC scheme). + memcpy(red_buffer_, stream, red_length_bytes); + } + + is_first_red_ = false; + // Update payload type with RED payload type. + current_payload_type = red_pltype_; + // We have packed 2 payloads. + fragmentation_.fragmentationVectorSize = kNumFecFragmentationVectors; + + // Copy to local variable, as it will be used outside ACM lock. + my_fragmentation.CopyFrom(fragmentation_); + // Store RED length. + fragmentation_.fragmentationLength[1] = red_length_bytes; + } + } + } + + if (has_data_to_send) { + CriticalSectionScoped lock(callback_crit_sect_); + + if (packetization_callback_ != NULL) { + if (fec_active) { + // Callback with payload data, including redundant data (FEC/RED). + packetization_callback_->SendData(frame_type, current_payload_type, + rtp_timestamp, stream, + length_bytes, + &my_fragmentation); + } else { + // Callback with payload data. + packetization_callback_->SendData(frame_type, current_payload_type, + rtp_timestamp, stream, + length_bytes, NULL); + } + } + + if (vad_callback_ != NULL) { + // Callback with VAD decision. + vad_callback_->InFrameType(((int16_t) encoding_type)); + } + } + return length_bytes; +} + +///////////////////////////////////////// +// Sender +// + +// Initialize send codec. +int32_t AudioCodingModuleImpl::InitializeSender() { + CriticalSectionScoped lock(acm_crit_sect_); + + // Start with invalid values. + send_codec_registered_ = false; + current_send_codec_idx_ = -1; + send_codec_inst_.plname[0] = '\0'; + + // Delete all encoders to start fresh. + for (int id = 0; id < ACMCodecDB::kMaxNumCodecs; id++) { + if (codecs_[id] != NULL) { + codecs_[id]->DestructEncoder(); + } + } + + // Initialize FEC/RED. + is_first_red_ = true; + if (fec_enabled_ || secondary_encoder_.get() != NULL) { + if (red_buffer_ != NULL) { + memset(red_buffer_, 0, MAX_PAYLOAD_SIZE_BYTE); + } + if (fec_enabled_) { + ResetFragmentation(kNumFecFragmentationVectors); + } else { + ResetFragmentation(0); + } + } + + return 0; +} + +int32_t AudioCodingModuleImpl::ResetEncoder() { + CriticalSectionScoped lock(acm_crit_sect_); + if (!HaveValidEncoder("ResetEncoder")) { + return -1; + } + return codecs_[current_send_codec_idx_]->ResetEncoder(); +} + +void AudioCodingModuleImpl::UnregisterSendCodec() { + CriticalSectionScoped lock(acm_crit_sect_); + send_codec_registered_ = false; + current_send_codec_idx_ = -1; + // If send Codec is unregistered then remove the secondary codec as well. + if (secondary_encoder_.get() != NULL) + secondary_encoder_.reset(); + return; +} + +ACMGenericCodec* AudioCodingModuleImpl::CreateCodec(const CodecInst& codec) { + ACMGenericCodec* my_codec = NULL; + + my_codec = ACMCodecDB::CreateCodecInstance(&codec); + if (my_codec == NULL) { + // Error, could not create the codec. + WEBRTC_TRACE(webrtc::kTraceError, webrtc::kTraceAudioCoding, id_, + "ACMCodecDB::CreateCodecInstance() failed in CreateCodec()"); + return my_codec; + } + my_codec->SetUniqueID(id_); + my_codec->SetNetEqDecodeLock(neteq_.DecodeLock()); + + return my_codec; +} + +// Check if the given codec is a valid to be registered as send codec. +static int IsValidSendCodec(const CodecInst& send_codec, + bool is_primary_encoder, + int acm_id, + int* mirror_id) { + if ((send_codec.channels != 1) && (send_codec.channels != 2)) { + WEBRTC_TRACE(webrtc::kTraceError, webrtc::kTraceAudioCoding, acm_id, + "Wrong number of channels (%d, only mono and stereo are " + "supported) for %s encoder", send_codec.channels, + is_primary_encoder ? "primary" : "secondary"); + return -1; + } + + int codec_id = ACMCodecDB::CodecNumber(&send_codec, mirror_id); + if (codec_id < 0) { + WEBRTC_TRACE(webrtc::kTraceError, webrtc::kTraceAudioCoding, acm_id, + "Invalid settings for the send codec."); + return -1; + } + + // TODO(tlegrand): Remove this check. Already taken care of in + // ACMCodecDB::CodecNumber(). + // Check if the payload-type is valid + if (!ACMCodecDB::ValidPayloadType(send_codec.pltype)) { + WEBRTC_TRACE(webrtc::kTraceError, webrtc::kTraceAudioCoding, acm_id, + "Invalid payload-type %d for %s.", send_codec.pltype, + send_codec.plname); + return -1; + } + + // Telephone-event cannot be a send codec. + if (!STR_CASE_CMP(send_codec.plname, "telephone-event")) { + WEBRTC_TRACE(webrtc::kTraceError, webrtc::kTraceAudioCoding, acm_id, + "telephone-event cannot be a send codec"); + *mirror_id = -1; + return -1; + } + + if (ACMCodecDB::codec_settings_[codec_id].channel_support + < send_codec.channels) { + WEBRTC_TRACE(webrtc::kTraceError, webrtc::kTraceAudioCoding, acm_id, + "%d number of channels not supportedn for %s.", + send_codec.channels, send_codec.plname); + *mirror_id = -1; + return -1; + } + + if (!is_primary_encoder) { + // If registering the secondary encoder, then RED and CN are not valid + // choices as encoder. + if (IsCodecRED(&send_codec)) { + WEBRTC_TRACE(webrtc::kTraceError, webrtc::kTraceAudioCoding, acm_id, + "RED cannot be secondary codec"); + *mirror_id = -1; + return -1; + } + + if (IsCodecCN(&send_codec)) { + WEBRTC_TRACE(webrtc::kTraceError, webrtc::kTraceAudioCoding, acm_id, + "DTX cannot be secondary codec"); + *mirror_id = -1; + return -1; + } + } + return codec_id; +} + +int AudioCodingModuleImpl::RegisterSecondarySendCodec( + const CodecInst& send_codec) { + CriticalSectionScoped lock(acm_crit_sect_); + if (!send_codec_registered_) { + return -1; + } + // Primary and Secondary codecs should have the same sampling rates. + if (send_codec.plfreq != send_codec_inst_.plfreq) { + return -1; + } + int mirror_id; + int codec_id = IsValidSendCodec(send_codec, false, id_, &mirror_id); + if (codec_id < 0) { + return -1; + } + ACMGenericCodec* encoder = CreateCodec(send_codec); + WebRtcACMCodecParams codec_params; + // Initialize the codec before registering. For secondary codec VAD & DTX are + // disabled. + memcpy(&(codec_params.codec_inst), &send_codec, sizeof(CodecInst)); + codec_params.enable_vad = false; + codec_params.enable_dtx = false; + codec_params.vad_mode = VADNormal; + // Force initialization. + if (encoder->InitEncoder(&codec_params, true) < 0) { + // Could not initialize, therefore cannot be registered. + delete encoder; + return -1; + } + secondary_encoder_.reset(encoder); + memcpy(&secondary_send_codec_inst_, &send_codec, sizeof(send_codec)); + + // Disable VAD & DTX. + SetVADSafe(false, false, VADNormal); + + // Cleaning. + if (red_buffer_) { + memset(red_buffer_, 0, MAX_PAYLOAD_SIZE_BYTE); + } + ResetFragmentation(0); + return 0; +} + +void AudioCodingModuleImpl::UnregisterSecondarySendCodec() { + CriticalSectionScoped lock(acm_crit_sect_); + if (secondary_encoder_.get() == NULL) { + return; + } + secondary_encoder_.reset(); + ResetFragmentation(0); +} + +int AudioCodingModuleImpl::SecondarySendCodec( + CodecInst* secondary_codec) const { + CriticalSectionScoped lock(acm_crit_sect_); + if (secondary_encoder_.get() == NULL) { + return -1; + } + memcpy(secondary_codec, &secondary_send_codec_inst_, + sizeof(secondary_send_codec_inst_)); + return 0; +} + +// Can be called multiple times for Codec, CNG, RED. +int32_t AudioCodingModuleImpl::RegisterSendCodec( + const CodecInst& send_codec) { + int mirror_id; + int codec_id = IsValidSendCodec(send_codec, true, id_, &mirror_id); + + CriticalSectionScoped lock(acm_crit_sect_); + + // Check for reported errors from function IsValidSendCodec(). + if (codec_id < 0) { + if (!send_codec_registered_) { + // This values has to be NULL if there is no codec registered. + current_send_codec_idx_ = -1; + } + return -1; + } + + // RED can be registered with other payload type. If not registered a default + // payload type is used. + if (IsCodecRED(&send_codec)) { + // TODO(tlegrand): Remove this check. Already taken care of in + // ACMCodecDB::CodecNumber(). + // Check if the payload-type is valid + if (!ACMCodecDB::ValidPayloadType(send_codec.pltype)) { + WEBRTC_TRACE(webrtc::kTraceError, webrtc::kTraceAudioCoding, id_, + "Invalid payload-type %d for %s.", send_codec.pltype, + send_codec.plname); + return -1; + } + // Set RED payload type. + red_pltype_ = static_cast<uint8_t>(send_codec.pltype); + return 0; + } + + // CNG can be registered with other payload type. If not registered the + // default payload types from codec database will be used. + if (IsCodecCN(&send_codec)) { + // CNG is registered. + switch (send_codec.plfreq) { + case 8000: { + cng_nb_pltype_ = static_cast<uint8_t>(send_codec.pltype); + break; + } + case 16000: { + cng_wb_pltype_ = static_cast<uint8_t>(send_codec.pltype); + break; + } + case 32000: { + cng_swb_pltype_ = static_cast<uint8_t>(send_codec.pltype); + break; + } + case 48000: { + cng_fb_pltype_ = static_cast<uint8_t>(send_codec.pltype); + break; + } + default: { + WEBRTC_TRACE(webrtc::kTraceError, webrtc::kTraceAudioCoding, id_, + "RegisterSendCodec() failed, invalid frequency for CNG " + "registration"); + return -1; + } + } + return 0; + } + + // Set Stereo, and make sure VAD and DTX is turned off. + if (send_codec.channels == 2) { + stereo_send_ = true; + if (vad_enabled_ || dtx_enabled_) { + WEBRTC_TRACE(webrtc::kTraceWarning, webrtc::kTraceAudioCoding, id_, + "VAD/DTX is turned off, not supported when sending stereo."); + } + vad_enabled_ = false; + dtx_enabled_ = false; + } else { + stereo_send_ = false; + } + + // Check if the codec is already registered as send codec. + bool is_send_codec; + if (send_codec_registered_) { + int send_codec_mirror_id; + int send_codec_id = ACMCodecDB::CodecNumber(&send_codec_inst_, + &send_codec_mirror_id); + assert(send_codec_id >= 0); + is_send_codec = (send_codec_id == codec_id) || + (mirror_id == send_codec_mirror_id); + } else { + is_send_codec = false; + } + + // If there is secondary codec registered and the new send codec has a + // sampling rate different than that of secondary codec, then unregister the + // secondary codec. + if (secondary_encoder_.get() != NULL && + secondary_send_codec_inst_.plfreq != send_codec.plfreq) { + secondary_encoder_.reset(); + ResetFragmentation(0); + } + + // If new codec, or new settings, register. + if (!is_send_codec) { + if (codecs_[mirror_id] == NULL) { + codecs_[mirror_id] = CreateCodec(send_codec); + if (codecs_[mirror_id] == NULL) { + WEBRTC_TRACE(webrtc::kTraceError, webrtc::kTraceAudioCoding, id_, + "Cannot Create the codec"); + return -1; + } + mirror_codec_idx_[mirror_id] = mirror_id; + } + + if (mirror_id != codec_id) { + codecs_[codec_id] = codecs_[mirror_id]; + mirror_codec_idx_[codec_id] = mirror_id; + } + + ACMGenericCodec* codec_ptr = codecs_[codec_id]; + int16_t status; + WebRtcACMCodecParams codec_params; + + memcpy(&(codec_params.codec_inst), &send_codec, sizeof(CodecInst)); + codec_params.enable_vad = vad_enabled_; + codec_params.enable_dtx = dtx_enabled_; + codec_params.vad_mode = vad_mode_; + // Force initialization. + status = codec_ptr->InitEncoder(&codec_params, true); + + // Check if VAD was turned on, or if error is reported. + if (status == 1) { + vad_enabled_ = true; + } else if (status < 0) { + // Could not initialize the encoder. + + // Check if already have a registered codec. + // Depending on that different messages are logged. + if (!send_codec_registered_) { + current_send_codec_idx_ = -1; + WEBRTC_TRACE(webrtc::kTraceError, webrtc::kTraceAudioCoding, id_, + "Cannot Initialize the encoder No Encoder is registered"); + } else { + WEBRTC_TRACE(webrtc::kTraceError, webrtc::kTraceAudioCoding, id_, + "Cannot Initialize the encoder, continue encoding with " + "the previously registered codec"); + } + return -1; + } + + // Everything is fine so we can replace the previous codec with this one. + if (send_codec_registered_) { + // If we change codec we start fresh with FEC. + // This is not strictly required by the standard. + is_first_red_ = true; + + if (codec_ptr->SetVAD(dtx_enabled_, vad_enabled_, vad_mode_) < 0) { + // SetVAD failed. + vad_enabled_ = false; + dtx_enabled_ = false; + } + } + + current_send_codec_idx_ = codec_id; + send_codec_registered_ = true; + memcpy(&send_codec_inst_, &send_codec, sizeof(CodecInst)); + previous_pltype_ = send_codec_inst_.pltype; + return 0; + } else { + // If codec is the same as already registered check if any parameters + // has changed compared to the current values. + // If any parameter is valid then apply it and record. + bool force_init = false; + + if (mirror_id != codec_id) { + codecs_[codec_id] = codecs_[mirror_id]; + mirror_codec_idx_[codec_id] = mirror_id; + } + + // Check the payload type. + if (send_codec.pltype != send_codec_inst_.pltype) { + // At this point check if the given payload type is valid. + // Record it later when the sampling frequency is changed + // successfully. + if (!ACMCodecDB::ValidPayloadType(send_codec.pltype)) { + WEBRTC_TRACE(webrtc::kTraceError, webrtc::kTraceAudioCoding, id_, + "Out of range payload type"); + return -1; + } + } + + // If there is a codec that ONE instance of codec supports multiple + // sampling frequencies, then we need to take care of it here. + // one such a codec is iSAC. Both WB and SWB are encoded and decoded + // with one iSAC instance. Therefore, we need to update the encoder + // frequency if required. + if (send_codec_inst_.plfreq != send_codec.plfreq) { + force_init = true; + + // If sampling frequency is changed we have to start fresh with RED. + is_first_red_ = true; + } + + // If packet size or number of channels has changed, we need to + // re-initialize the encoder. + if (send_codec_inst_.pacsize != send_codec.pacsize) { + force_init = true; + } + if (send_codec_inst_.channels != send_codec.channels) { + force_init = true; + } + + if (force_init) { + WebRtcACMCodecParams codec_params; + + memcpy(&(codec_params.codec_inst), &send_codec, sizeof(CodecInst)); + codec_params.enable_vad = vad_enabled_; + codec_params.enable_dtx = dtx_enabled_; + codec_params.vad_mode = vad_mode_; + + // Force initialization. + if (codecs_[current_send_codec_idx_]->InitEncoder(&codec_params, + true) < 0) { + WEBRTC_TRACE(webrtc::kTraceError, webrtc::kTraceAudioCoding, id_, + "Could not change the codec packet-size."); + return -1; + } + + send_codec_inst_.plfreq = send_codec.plfreq; + send_codec_inst_.pacsize = send_codec.pacsize; + send_codec_inst_.channels = send_codec.channels; + } + + // If the change of sampling frequency has been successful then + // we store the payload-type. + send_codec_inst_.pltype = send_codec.pltype; + + // Check if a change in Rate is required. + if (send_codec.rate != send_codec_inst_.rate) { + if (codecs_[codec_id]->SetBitRate(send_codec.rate) < 0) { + WEBRTC_TRACE(webrtc::kTraceError, webrtc::kTraceAudioCoding, id_, + "Could not change the codec rate."); + return -1; + } + send_codec_inst_.rate = send_codec.rate; + } + previous_pltype_ = send_codec_inst_.pltype; + + return 0; + } +} + +// Get current send codec. +int32_t AudioCodingModuleImpl::SendCodec( + CodecInst* current_codec) const { + WEBRTC_TRACE(webrtc::kTraceStream, webrtc::kTraceAudioCoding, id_, + "SendCodec()"); + CriticalSectionScoped lock(acm_crit_sect_); + + assert(current_codec); + 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. +int32_t 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. +int32_t 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. +int32_t AudioCodingModuleImpl::SetReceivedEstimatedBandwidth( + const int32_t bw) { + return codecs_[current_send_codec_idx_]->SetEstimatedBandwidth(bw); +} + +// Register a transport callback which will be called to deliver +// the encoded buffers. +int32_t AudioCodingModuleImpl::RegisterTransportCallback( + AudioPacketizationCallback* transport) { + CriticalSectionScoped lock(callback_crit_sect_); + packetization_callback_ = transport; + return 0; +} + +// Used by the module to deliver messages to the codec module/application +// AVT(DTMF). +int32_t AudioCodingModuleImpl::RegisterIncomingMessagesCallback( +#ifndef WEBRTC_DTMF_DETECTION + AudioCodingFeedback* /* incoming_message */, + const ACMCountries /* cpt */) { + return -1; +#else + AudioCodingFeedback* incoming_message, + const ACMCountries cpt) { + int16_t status = 0; + + // Enter the critical section for callback. + { + CriticalSectionScoped lock(callback_crit_sect_); + dtmf_callback_ = incoming_message; + } + // Enter the ACM critical section to set up the DTMF class. + { + CriticalSectionScoped lock(acm_crit_sect_); + // Check if the call is to disable or enable the callback. + if (incoming_message == NULL) { + // Callback is disabled, delete DTMF-detector class. + if (dtmf_detector_ != NULL) { + delete dtmf_detector_; + dtmf_detector_ = NULL; + } + status = 0; + } else { + status = 0; + if (dtmf_detector_ == NULL) { + dtmf_detector_ = new ACMDTMFDetection; + if (dtmf_detector_ == NULL) { + status = -1; + } + } + if (status >= 0) { + status = dtmf_detector_->Enable(cpt); + if (status < 0) { + // Failed to initialize if DTMF-detection was not enabled before, + // delete the class, and set the callback to NULL and return -1. + delete dtmf_detector_; + dtmf_detector_ = NULL; + } + } + } + } + // Check if we failed in setting up the DTMF-detector class. + if ((status < 0)) { + // We failed, we cannot have the callback. + CriticalSectionScoped lock(callback_crit_sect_); + dtmf_callback_ = NULL; + } + + return status; +#endif +} + +// Add 10MS of raw (PCM) audio data to the encoder. +int32_t AudioCodingModuleImpl::Add10MsData( + const AudioFrame& audio_frame) { + if (audio_frame.samples_per_channel_ <= 0) { + assert(false); + WEBRTC_TRACE(webrtc::kTraceError, webrtc::kTraceAudioCoding, id_, + "Cannot Add 10 ms audio, payload length is negative or " + "zero"); + return -1; + } + + // Allow for 8, 16, 32 and 48kHz input audio. + if ((audio_frame.sample_rate_hz_ != 8000) + && (audio_frame.sample_rate_hz_ != 16000) + && (audio_frame.sample_rate_hz_ != 32000) + && (audio_frame.sample_rate_hz_ != 48000)) { + assert(false); + WEBRTC_TRACE(webrtc::kTraceError, webrtc::kTraceAudioCoding, id_, + "Cannot Add 10 ms audio, input frequency not valid"); + return -1; + } + + // If the length and frequency matches. We currently just support raw PCM. + if ((audio_frame.sample_rate_hz_ / 100) + != audio_frame.samples_per_channel_) { + WEBRTC_TRACE(webrtc::kTraceError, webrtc::kTraceAudioCoding, id_, + "Cannot Add 10 ms audio, input frequency and length doesn't" + " match"); + return -1; + } + + if (audio_frame.num_channels_ != 1 && audio_frame.num_channels_ != 2) { + WEBRTC_TRACE(webrtc::kTraceError, webrtc::kTraceAudioCoding, id_, + "Cannot Add 10 ms audio, invalid number of channels."); + return -1; + } + + CriticalSectionScoped lock(acm_crit_sect_); + // Do we have a codec registered? + if (!HaveValidEncoder("Add10MsData")) { + return -1; + } + + const AudioFrame* ptr_frame; + // Perform a resampling, also down-mix if it is required and can be + // performed before resampling (a down mix prior to resampling will take + // place if both primary and secondary encoders are mono and input is in + // stereo). + if (PreprocessToAddData(audio_frame, &ptr_frame) < 0) { + return -1; + } + TRACE_EVENT_ASYNC_BEGIN1("webrtc", "Audio", ptr_frame->timestamp_, + "now", clock_->TimeInMilliseconds()); + + // Check whether we need an up-mix or down-mix? + bool remix = ptr_frame->num_channels_ != send_codec_inst_.channels; + if (secondary_encoder_.get() != NULL) { + remix = remix || + (ptr_frame->num_channels_ != secondary_send_codec_inst_.channels); + } + + // If a re-mix is required (up or down), this buffer will store re-mixed + // version of the input. + int16_t buffer[WEBRTC_10MS_PCM_AUDIO]; + if (remix) { + if (ptr_frame->num_channels_ == 1) { + if (UpMix(*ptr_frame, WEBRTC_10MS_PCM_AUDIO, buffer) < 0) + return -1; + } else { + if (DownMix(*ptr_frame, WEBRTC_10MS_PCM_AUDIO, buffer) < 0) + return -1; + } + } + + // When adding data to encoders this pointer is pointing to an audio buffer + // with correct number of channels. + const int16_t* ptr_audio = ptr_frame->data_; + + // For pushing data to primary, point the |ptr_audio| to correct buffer. + if (send_codec_inst_.channels != ptr_frame->num_channels_) + ptr_audio = buffer; + + if (codecs_[current_send_codec_idx_]->Add10MsData( + ptr_frame->timestamp_, ptr_audio, ptr_frame->samples_per_channel_, + send_codec_inst_.channels) < 0) + return -1; + + if (secondary_encoder_.get() != NULL) { + // For pushing data to secondary, point the |ptr_audio| to correct buffer. + ptr_audio = ptr_frame->data_; + if (secondary_send_codec_inst_.channels != ptr_frame->num_channels_) + ptr_audio = buffer; + + if (secondary_encoder_->Add10MsData( + ptr_frame->timestamp_, ptr_audio, ptr_frame->samples_per_channel_, + secondary_send_codec_inst_.channels) < 0) + return -1; + } + + return 0; +} + +// Perform a resampling and down-mix if required. We down-mix only if +// encoder is mono and input is stereo. In case of dual-streaming, both +// encoders has to be mono for down-mix to take place. +// |*ptr_out| will point to the pre-processed audio-frame. If no pre-processing +// is required, |*ptr_out| points to |in_frame|. +int AudioCodingModuleImpl::PreprocessToAddData(const AudioFrame& in_frame, + const AudioFrame** ptr_out) { + // Primary and secondary (if exists) should have the same sampling rate. + assert((secondary_encoder_.get() != NULL) ? + secondary_send_codec_inst_.plfreq == send_codec_inst_.plfreq : true); + + bool resample = ((int32_t) in_frame.sample_rate_hz_ + != send_codec_inst_.plfreq); + + // This variable is true if primary codec and secondary codec (if exists) + // are both mono and input is stereo. + bool down_mix; + if (secondary_encoder_.get() != NULL) { + down_mix = (in_frame.num_channels_ == 2) && + (send_codec_inst_.channels == 1) && + (secondary_send_codec_inst_.channels == 1); + } else { + down_mix = (in_frame.num_channels_ == 2) && + (send_codec_inst_.channels == 1); + } + + if (!down_mix && !resample) { + // No pre-processing is required. + last_in_timestamp_ = in_frame.timestamp_; + last_timestamp_ = in_frame.timestamp_; + *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_ = in_frame.timestamp_; + 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_; + + uint32_t timestamp_diff; + + // Calculate the timestamp of this frame. + if (last_in_timestamp_ > in_frame.timestamp_) { + // A wrap around has happened. + timestamp_diff = ((uint32_t) 0xFFFFFFFF - last_in_timestamp_) + + in_frame.timestamp_; + } else { + timestamp_diff = in_frame.timestamp_ - last_in_timestamp_; + } + preprocess_frame_.timestamp_ = last_timestamp_ + + static_cast<uint32_t>(timestamp_diff * + (static_cast<double>(send_codec_inst_.plfreq) / + static_cast<double>(in_frame.sample_rate_hz_))); + + preprocess_frame_.samples_per_channel_ = input_resampler_.Resample10Msec( + src_ptr_audio, in_frame.sample_rate_hz_, dest_ptr_audio, + send_codec_inst_.plfreq, preprocess_frame_.num_channels_); + + if (preprocess_frame_.samples_per_channel_ < 0) { + WEBRTC_TRACE(webrtc::kTraceError, webrtc::kTraceAudioCoding, id_, + "Cannot add 10 ms audio, resmapling failed"); + return -1; + } + preprocess_frame_.sample_rate_hz_ = send_codec_inst_.plfreq; + } + last_in_timestamp_ = in_frame.timestamp_; + last_timestamp_ = preprocess_frame_.timestamp_; + + return 0; +} + +///////////////////////////////////////// +// (FEC) Forward Error Correction +// + +bool AudioCodingModuleImpl::FECStatus() const { + CriticalSectionScoped lock(acm_crit_sect_); + return fec_enabled_; +} + +// Configure FEC status i.e on/off. +int32_t +AudioCodingModuleImpl::SetFECStatus( +#ifdef WEBRTC_CODEC_RED + const bool enable_fec) { + CriticalSectionScoped lock(acm_crit_sect_); + + if (fec_enabled_ != enable_fec) { + // Reset the RED buffer. + memset(red_buffer_, 0, MAX_PAYLOAD_SIZE_BYTE); + + // Reset fragmentation buffers. + ResetFragmentation(kNumFecFragmentationVectors); + // Set fec_enabled_. + fec_enabled_ = enable_fec; + } + is_first_red_ = true; // Make sure we restart FEC. + return 0; +#else + const bool /* enable_fec */) { + fec_enabled_ = false; + WEBRTC_TRACE(webrtc::kTraceWarning, webrtc::kTraceAudioCoding, id_, + " WEBRTC_CODEC_RED is undefined => fec_enabled_ = %d", + fec_enabled_); + return -1; +#endif +} + +///////////////////////////////////////// +// (VAD) Voice Activity Detection +// +int32_t AudioCodingModuleImpl::SetVAD(const bool enable_dtx, + const bool enable_vad, + const 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", + static_cast<int>(mode)); + return -1; + } + + // Check that the send codec is mono. We don't support VAD/DTX for stereo + // sending. + if ((enable_dtx || enable_vad) && stereo_send_) { + WEBRTC_TRACE(webrtc::kTraceError, webrtc::kTraceAudioCoding, id_, + "VAD/DTX not supported for stereo sending"); + return -1; + } + + // We don't support VAD/DTX when dual-streaming is enabled, i.e. + // secondary-encoder is registered. + if ((enable_dtx || enable_vad) && secondary_encoder_.get() != NULL) { + WEBRTC_TRACE(webrtc::kTraceError, webrtc::kTraceAudioCoding, id_, + "VAD/DTX not supported when dual-streaming is enabled."); + return -1; + } + + // If a send codec is registered, set VAD/DTX for the codec. + if (HaveValidEncoder("SetVAD")) { + int16_t status = codecs_[current_send_codec_idx_]->SetVAD(enable_dtx, + enable_vad, + mode); + if (status == 1) { + // Vad was enabled. + vad_enabled_ = true; + dtx_enabled_ = enable_dtx; + vad_mode_ = mode; + + return 0; + } else if (status < 0) { + // SetVAD failed. + WEBRTC_TRACE(webrtc::kTraceError, webrtc::kTraceAudioCoding, id_, + "SetVAD failed"); + + vad_enabled_ = false; + dtx_enabled_ = false; + + return -1; + } + } + + vad_enabled_ = enable_vad; + dtx_enabled_ = enable_dtx; + vad_mode_ = mode; + + return 0; +} + +// Get VAD/DTX settings. +// TODO(tlegrand): Change this method to void. +int32_t 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 +// + +int32_t AudioCodingModuleImpl::InitializeReceiver() { + CriticalSectionScoped lock(acm_crit_sect_); + return InitializeReceiverSafe(); +} + +// Initialize receiver, resets codec database etc. +int32_t AudioCodingModuleImpl::InitializeReceiverSafe() { + initial_delay_ms_ = 0; + num_packets_accumulated_ = 0; + num_bytes_accumulated_ = 0; + accumulated_audio_ms_ = 0; + first_payload_received_ = 0; + last_incoming_send_timestamp_ = 0; + track_neteq_buffer_ = false; + playout_ts_ = 0; + // 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_) { + for (int i = 0; i < ACMCodecDB::kNumCodecs; i++) { + if (UnregisterReceiveCodecSafe(i) < 0) { + WEBRTC_TRACE(webrtc::kTraceError, webrtc::kTraceAudioCoding, id_, + "InitializeReceiver() failed, Could not unregister codec"); + return -1; + } + } + } + if (neteq_.Init() != 0) { + WEBRTC_TRACE(webrtc::kTraceError, webrtc::kTraceAudioCoding, id_, + "InitializeReceiver() failed, Could not initialize NetEQ"); + return -1; + } + neteq_.set_id(id_); + if (neteq_.AllocatePacketBuffer(ACMCodecDB::NetEQDecoders(), + ACMCodecDB::kNumCodecs) != 0) { + WEBRTC_TRACE(webrtc::kTraceError, webrtc::kTraceAudioCoding, id_, + "NetEQ cannot allocate_packet Buffer"); + return -1; + } + + // Register RED and CN. + for (int i = 0; i < ACMCodecDB::kNumCodecs; i++) { + if (IsCodecRED(i) || IsCodecCN(i)) { + if (RegisterRecCodecMSSafe(ACMCodecDB::database_[i], i, i, + ACMNetEQ::kMasterJb) < 0) { + WEBRTC_TRACE(webrtc::kTraceError, webrtc::kTraceAudioCoding, id_, + "Cannot register master codec."); + return -1; + } + registered_pltypes_[i] = ACMCodecDB::database_[i].pltype; + } + } + + receiver_initialized_ = true; + return 0; +} + +// Reset the decoder state. +int32_t AudioCodingModuleImpl::ResetDecoder() { + CriticalSectionScoped lock(acm_crit_sect_); + + for (int id = 0; id < ACMCodecDB::kMaxNumCodecs; id++) { + if ((codecs_[id] != NULL) && (registered_pltypes_[id] != -1)) { + if (codecs_[id]->ResetDecoder(registered_pltypes_[id]) < 0) { + WEBRTC_TRACE(webrtc::kTraceError, webrtc::kTraceAudioCoding, id_, + "ResetDecoder failed:"); + return -1; + } + } + } + return neteq_.FlushBuffers(); +} + +// Get current receive frequency. +int32_t AudioCodingModuleImpl::ReceiveFrequency() const { + WEBRTC_TRACE(webrtc::kTraceStream, webrtc::kTraceAudioCoding, id_, + "ReceiveFrequency()"); + WebRtcACMCodecParams codec_params; + + CriticalSectionScoped lock(acm_crit_sect_); + if (DecoderParamByPlType(last_recv_audio_codec_pltype_, codec_params) < 0) { + return neteq_.CurrentSampFreqHz(); + } else if (codec_params.codec_inst.plfreq == 48000) { + // TODO(tlegrand): Remove this option when we have full 48 kHz support. + return 32000; + } else { + return codec_params.codec_inst.plfreq; + } +} + +// Get current playout frequency. +int32_t AudioCodingModuleImpl::PlayoutFrequency() const { + WEBRTC_TRACE(webrtc::kTraceStream, webrtc::kTraceAudioCoding, id_, + "PlayoutFrequency()"); + + CriticalSectionScoped lock(acm_crit_sect_); + + return neteq_.CurrentSampFreqHz(); +} + +// Register possible receive codecs, can be called multiple times, +// for codecs, CNG (NB, WB and SWB), DTMF, RED. +int32_t AudioCodingModuleImpl::RegisterReceiveCodec( + const CodecInst& receive_codec) { + CriticalSectionScoped lock(acm_crit_sect_); + + if (receive_codec.channels > 2) { + WEBRTC_TRACE(webrtc::kTraceError, webrtc::kTraceAudioCoding, id_, + "More than 2 audio channel is not supported."); + return -1; + } + + int mirror_id; + int codec_id = ACMCodecDB::ReceiverCodecNumber(&receive_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(receive_codec.pltype)) { + WEBRTC_TRACE(webrtc::kTraceError, webrtc::kTraceAudioCoding, id_, + "Invalid payload-type %d for %s.", receive_codec.pltype, + receive_codec.plname); + return -1; + } + + if (!receiver_initialized_) { + if (InitializeReceiverSafe() < 0) { + WEBRTC_TRACE(webrtc::kTraceError, webrtc::kTraceAudioCoding, id_, + "Cannot initialize reciver, so failed registering a codec."); + return -1; + } + } + + // If codec already registered, unregister. Except for CN where we only + // unregister if payload type is changing. + if ((registered_pltypes_[codec_id] == receive_codec.pltype) + && IsCodecCN(&receive_codec)) { + // Codec already registered as receiver with this payload type. Nothing + // to be done. + return 0; + } else if (registered_pltypes_[codec_id] != -1) { + if (UnregisterReceiveCodecSafe(codec_id) < 0) { + WEBRTC_TRACE(webrtc::kTraceError, webrtc::kTraceAudioCoding, id_, + "Cannot register master codec."); + return -1; + } + } + + if (RegisterRecCodecMSSafe(receive_codec, codec_id, mirror_id, + ACMNetEQ::kMasterJb) < 0) { + WEBRTC_TRACE(webrtc::kTraceError, webrtc::kTraceAudioCoding, id_, + "Cannot register master codec."); + return -1; + } + + // TODO(andrew): Refactor how the slave is initialized. Can we instead + // always start up a slave and pre-register CN and RED? We should be able + // to get rid of stereo_receive_registered_. + // http://code.google.com/p/webrtc/issues/detail?id=453 + + // Register stereo codecs with the slave, or, if we've had already seen a + // stereo codec, register CN or RED as a special case. + if (receive_codec.channels == 2 || + (stereo_receive_registered_ && (IsCodecCN(&receive_codec) || + IsCodecRED(&receive_codec)))) { + // TODO(andrew): refactor this block to combine with InitStereoSlave(). + + if (!stereo_receive_registered_) { + // This is the first time a stereo codec has been registered. Make + // some stereo preparations. + + // Add a stereo slave. + assert(neteq_.num_slaves() == 0); + if (neteq_.AddSlave(ACMCodecDB::NetEQDecoders(), + ACMCodecDB::kNumCodecs) < 0) { + WEBRTC_TRACE(webrtc::kTraceError, webrtc::kTraceAudioCoding, id_, + "Cannot add slave jitter buffer to NetEQ."); + return -1; + } + + // Register any existing CN or RED codecs with the slave and as stereo. + for (int i = 0; i < ACMCodecDB::kNumCodecs; i++) { + if (registered_pltypes_[i] != -1 && (IsCodecRED(i) || IsCodecCN(i))) { + stereo_receive_[i] = true; + + CodecInst codec; + memcpy(&codec, &ACMCodecDB::database_[i], sizeof(CodecInst)); + codec.pltype = registered_pltypes_[i]; + if (RegisterRecCodecMSSafe(codec, i, i, ACMNetEQ::kSlaveJb) < 0) { + WEBRTC_TRACE(kTraceError, kTraceAudioCoding, id_, + "Cannot register slave codec."); + return -1; + } + } + } + } + + if (RegisterRecCodecMSSafe(receive_codec, codec_id, mirror_id, + ACMNetEQ::kSlaveJb) < 0) { + WEBRTC_TRACE(webrtc::kTraceError, webrtc::kTraceAudioCoding, id_, + "Cannot register slave codec."); + return -1; + } + + if (!stereo_receive_[codec_id] && + (last_recv_audio_codec_pltype_ == receive_codec.pltype)) { + // The last received payload type is the same as the one we are + // registering. Expected number of channels to receive is one (mono), + // but we are now registering the receiving codec as stereo (number of + // channels is 2). + // Set |last_recv_audio_coded_pltype_| to invalid value to trigger a + // flush in NetEq, and a reset of expected number of channels next time a + // packet is received in AudioCodingModuleImpl::IncomingPacket(). + last_recv_audio_codec_pltype_ = -1; + } + + stereo_receive_[codec_id] = true; + stereo_receive_registered_ = true; + } else { + if (last_recv_audio_codec_pltype_ == receive_codec.pltype && + expected_channels_ == 2) { + // The last received payload type is the same as the one we are + // registering. Expected number of channels to receive is two (stereo), + // but we are now registering the receiving codec as mono (number of + // channels is 1). + // Set |last_recv_audio_coded_pl_type_| to invalid value to trigger a + // flush in NetEq, and a reset of expected number of channels next time a + // packet is received in AudioCodingModuleImpl::IncomingPacket(). + last_recv_audio_codec_pltype_ = -1; + } + stereo_receive_[codec_id] = false; + } + + registered_pltypes_[codec_id] = receive_codec.pltype; + + if (IsCodecRED(&receive_codec)) { + receive_red_pltype_ = receive_codec.pltype; + } + return 0; +} + +int32_t AudioCodingModuleImpl::RegisterRecCodecMSSafe( + const CodecInst& receive_codec, int16_t codec_id, + int16_t mirror_id, ACMNetEQ::JitterBuffer jitter_buffer) { + ACMGenericCodec** codecs; + if (jitter_buffer == ACMNetEQ::kMasterJb) { + codecs = &codecs_[0]; + } else if (jitter_buffer == ACMNetEQ::kSlaveJb) { + codecs = &slave_codecs_[0]; + if (codecs_[codec_id]->IsTrueStereoCodec()) { + // True stereo codecs need to use the same codec memory + // for both master and slave. + slave_codecs_[mirror_id] = codecs_[mirror_id]; + mirror_codec_idx_[mirror_id] = mirror_id; + } + } else { + WEBRTC_TRACE(webrtc::kTraceError, webrtc::kTraceAudioCoding, id_, + "RegisterReceiveCodecMSSafe failed, jitter_buffer is neither " + "master or slave "); + return -1; + } + + if (codecs[mirror_id] == NULL) { + codecs[mirror_id] = CreateCodec(receive_codec); + if (codecs[mirror_id] == NULL) { + WEBRTC_TRACE(webrtc::kTraceError, webrtc::kTraceAudioCoding, id_, + "Cannot create codec to register as receive 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; + } + + codecs[codec_id]->SetIsMaster(jitter_buffer == ACMNetEQ::kMasterJb); + + int16_t status = 0; + WebRtcACMCodecParams codec_params; + memcpy(&(codec_params.codec_inst), &receive_codec, sizeof(CodecInst)); + codec_params.enable_vad = false; + codec_params.enable_dtx = false; + codec_params.vad_mode = VADNormal; + if (!codecs[codec_id]->DecoderInitialized()) { + // Force initialization. + status = codecs[codec_id]->InitDecoder(&codec_params, true); + if (status < 0) { + // Could not initialize the decoder, we don't want to + // continue if we could not initialize properly. + WEBRTC_TRACE( + webrtc::kTraceError, webrtc::kTraceAudioCoding, id_, + "could not initialize the receive codec, codec not registered"); + + return -1; + } + } else if (mirror_id != codec_id) { + // Currently this only happens for iSAC. + // We have to store the decoder parameters. + codecs[codec_id]->SaveDecoderParam(&codec_params); + } + + if (codecs[codec_id]->RegisterInNetEq(&neteq_, receive_codec) != 0) { + WEBRTC_TRACE(webrtc::kTraceError, webrtc::kTraceAudioCoding, id_, + "Receive codec could not be registered in NetEQ"); + return -1; + } + // Guarantee that the same payload-type that is + // registered in NetEQ is stored in the codec. + codecs[codec_id]->SaveDecoderParam(&codec_params); + + return status; +} + +// Get current received codec. +int32_t AudioCodingModuleImpl::ReceiveCodec( + CodecInst* current_codec) const { + WebRtcACMCodecParams decoder_param; + CriticalSectionScoped lock(acm_crit_sect_); + + for (int id = 0; id < ACMCodecDB::kMaxNumCodecs; id++) { + if (codecs_[id] != NULL) { + if (codecs_[id]->DecoderInitialized()) { + if (codecs_[id]->DecoderParams(&decoder_param, + last_recv_audio_codec_pltype_)) { + memcpy(current_codec, &decoder_param.codec_inst, + sizeof(CodecInst)); + return 0; + } + } + } + } + + // If we are here then we haven't found any codec. Set codec pltype to -1 to + // indicate that the structure is invalid and return -1. + current_codec->pltype = -1; + return -1; +} + +// Incoming packet from network parsed and ready for decode. +int32_t AudioCodingModuleImpl::IncomingPacket( + const uint8_t* incoming_payload, + const int32_t payload_length, + const WebRtcRTPHeader& rtp_info) { + WebRtcRTPHeader rtp_header; + + memcpy(&rtp_header, &rtp_info, sizeof(WebRtcRTPHeader)); + + if (payload_length < 0) { + WEBRTC_TRACE(webrtc::kTraceError, webrtc::kTraceAudioCoding, id_, + "IncomingPacket() Error, payload-length cannot be negative"); + return -1; + } + + { + // Store the payload Type. This will be used to retrieve "received codec" + // and "received frequency." + CriticalSectionScoped lock(acm_crit_sect_); + + // Check there are packets missed between the last injected packet, and the + // latest received packet. If so and we are in AV-sync mode then we would + // like to fill the gap. Shouldn't be the first payload. + if (av_sync_ && first_payload_received_ && + rtp_info.header.sequenceNumber > last_sequence_number_ + 1) { + // If the last packet pushed was sync-packet account for all missing + // packets. Otherwise leave some room for PLC. + if (last_packet_was_sync_) { + while (rtp_info.header.sequenceNumber > last_sequence_number_ + 2) { + PushSyncPacketSafe(); + } + } else { + // Leave two packet room for NetEq perform PLC. + if (rtp_info.header.sequenceNumber > last_sequence_number_ + 3) { + last_sequence_number_ += 2; + last_incoming_send_timestamp_ += last_timestamp_diff_ * 2; + last_receive_timestamp_ += 2 * last_timestamp_diff_; + while (rtp_info.header.sequenceNumber > last_sequence_number_ + 1) + PushSyncPacketSafe(); + } + } + } + + uint8_t my_payload_type; + + // Check if this is an RED payload. + if (rtp_info.header.payloadType == receive_red_pltype_) { + // Get the primary payload-type. + my_payload_type = incoming_payload[0] & 0x7F; + } else { + my_payload_type = rtp_info.header.payloadType; + } + + // If payload is audio, check if received payload is different from + // previous. + if (!rtp_info.type.Audio.isCNG) { + // This is Audio not CNG. + + if (my_payload_type != last_recv_audio_codec_pltype_) { + // We detect a change in payload type. It is necessary for iSAC + // we are going to use ONE iSAC instance for decoding both WB and + // SWB payloads. If payload is changed there might be a need to reset + // sampling rate of decoder. depending what we have received "now". + for (int i = 0; i < ACMCodecDB::kMaxNumCodecs; i++) { + if (registered_pltypes_[i] == my_payload_type) { + if (UpdateUponReceivingCodec(i) != 0) + return -1; + break; + } + } + // Codec is changed, there might be a jump in timestamp, therefore, + // we have to reset some variables that track NetEq buffer. + if (track_neteq_buffer_ || av_sync_) { + last_incoming_send_timestamp_ = rtp_info.header.timestamp; + } + + if (nack_enabled_) { + assert(nack_.get()); + // Codec is changed, reset NACK and update sampling rate. + nack_->Reset(); + nack_->UpdateSampleRate( + ACMCodecDB::database_[current_receive_codec_idx_].plfreq); + } + } + last_recv_audio_codec_pltype_ = my_payload_type; + } + + // Current timestamp based on the receiver sampling frequency. + last_receive_timestamp_ = NowTimestamp(current_receive_codec_idx_); + + if (nack_enabled_) { + assert(nack_.get()); + nack_->UpdateLastReceivedPacket(rtp_header.header.sequenceNumber, + rtp_header.header.timestamp); + } + } + + int per_neteq_payload_length = payload_length; + // Split the payload for stereo packets, so that first half of payload + // vector holds left channel, and second half holds right channel. + if (expected_channels_ == 2) { + if (!rtp_info.type.Audio.isCNG) { + // Create a new vector for the payload, maximum payload size. + int32_t length = payload_length; + uint8_t payload[kMaxPacketSize]; + assert(payload_length <= kMaxPacketSize); + memcpy(payload, incoming_payload, payload_length); + codecs_[current_receive_codec_idx_]->SplitStereoPacket(payload, &length); + rtp_header.type.Audio.channel = 2; + per_neteq_payload_length = length / 2; + // Insert packet into NetEQ. + if (neteq_.RecIn(payload, length, rtp_header, + last_receive_timestamp_) < 0) + return -1; + } else { + // If we receive a CNG packet while expecting stereo, we ignore the + // packet and continue. CNG is not supported for stereo. + return 0; + } + } else { + if (neteq_.RecIn(incoming_payload, payload_length, rtp_header, + last_receive_timestamp_) < 0) + return -1; + } + + { + CriticalSectionScoped lock(acm_crit_sect_); + + // Update buffering uses |last_incoming_send_timestamp_| so it should be + // before the next block. + if (track_neteq_buffer_) + UpdateBufferingSafe(rtp_header, per_neteq_payload_length); + + if (av_sync_) { + if (rtp_info.header.sequenceNumber == last_sequence_number_ + 1) { + last_timestamp_diff_ = rtp_info.header.timestamp - + last_incoming_send_timestamp_; + } + last_sequence_number_ = rtp_info.header.sequenceNumber; + last_ssrc_ = rtp_info.header.ssrc; + last_packet_was_sync_ = false; + } + + if (av_sync_ || track_neteq_buffer_) { + last_incoming_send_timestamp_ = rtp_info.header.timestamp; + } + + // Set the following regardless of tracking NetEq buffer or being in + // AV-sync mode. Only if the received packet is not CNG. + if (!rtp_info.type.Audio.isCNG) + first_payload_received_ = true; + } + return 0; +} + +int AudioCodingModuleImpl::UpdateUponReceivingCodec(int index) { + if (codecs_[index] == NULL) { + WEBRTC_TRACE(kTraceError, kTraceAudioCoding, id_, + "IncomingPacket() error: payload type found but " + "corresponding codec is NULL"); + return -1; + } + codecs_[index]->UpdateDecoderSampFreq(index); + neteq_.set_received_stereo(stereo_receive_[index]); + current_receive_codec_idx_ = index; + + // If we have a change in the expected number of channels, flush packet + // buffers in NetEQ. + if ((stereo_receive_[index] && (expected_channels_ == 1)) || + (!stereo_receive_[index] && (expected_channels_ == 2))) { + neteq_.FlushBuffers(); + codecs_[index]->ResetDecoder(registered_pltypes_[index]); + } + + if (stereo_receive_[index] && (expected_channels_ == 1)) { + // When switching from a mono to stereo codec reset the slave. + if (InitStereoSlave() != 0) + return -1; + } + + // Store number of channels we expect to receive for the current payload type. + if (stereo_receive_[index]) { + expected_channels_ = 2; + } else { + expected_channels_ = 1; + } + + // Reset previous received channel. + prev_received_channel_ = 0; + return 0; +} + +bool AudioCodingModuleImpl::IsCodecForSlave(int index) const { + return (registered_pltypes_[index] != -1 && stereo_receive_[index]); +} + +int AudioCodingModuleImpl::InitStereoSlave() { + neteq_.RemoveSlaves(); + + if (neteq_.AddSlave(ACMCodecDB::NetEQDecoders(), + ACMCodecDB::kNumCodecs) < 0) { + WEBRTC_TRACE(webrtc::kTraceError, webrtc::kTraceAudioCoding, id_, + "Cannot add slave jitter buffer to NetEQ."); + return -1; + } + + // Register all needed codecs with slave. + for (int i = 0; i < ACMCodecDB::kNumCodecs; i++) { + if (codecs_[i] != NULL && IsCodecForSlave(i)) { + WebRtcACMCodecParams decoder_params; + if (codecs_[i]->DecoderParams(&decoder_params, registered_pltypes_[i])) { + if (RegisterRecCodecMSSafe(decoder_params.codec_inst, + i, ACMCodecDB::MirrorID(i), + ACMNetEQ::kSlaveJb) < 0) { + WEBRTC_TRACE(kTraceError, kTraceAudioCoding, id_, + "Cannot register slave codec."); + return -1; + } + } + } + } + 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-10000 milliseconds."); + return -1; + } + { + CriticalSectionScoped lock(acm_crit_sect_); + // Don't let the extra delay modified while accumulating buffers in NetEq. + if (track_neteq_buffer_ && first_payload_received_) + return 0; + } + return neteq_.SetMinimumDelay(time_ms); +} + +// Get Dtmf playout status. +bool AudioCodingModuleImpl::DtmfPlayoutStatus() const { +#ifndef WEBRTC_CODEC_AVT + return false; +#else + return neteq_.avt_playout(); +#endif +} + +// Configure Dtmf playout status i.e on/off playout the incoming outband +// Dtmf tone. +int32_t AudioCodingModuleImpl::SetDtmfPlayoutStatus( +#ifndef WEBRTC_CODEC_AVT + const bool /* enable */) { + WEBRTC_TRACE(webrtc::kTraceWarning, webrtc::kTraceAudioCoding, id_, + "SetDtmfPlayoutStatus() failed: AVT is not supported."); + return -1; +#else + const bool enable) { + return neteq_.SetAVTPlayout(enable); +#endif +} + +// 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. +int32_t AudioCodingModuleImpl::DecoderEstimatedBandwidth() const { + CodecInst codec; + int16_t codec_id = -1; + int pltype_wb; + int pltype_swb; + + // Get iSAC settings. + for (int id = 0; id < ACMCodecDB::kNumCodecs; id++) { + // Store codec settings for codec number "codeCntr" in the output struct. + ACMCodecDB::Codec(id, &codec); + + if (!STR_CASE_CMP(codec.plname, "isac")) { + codec_id = 1; + pltype_wb = codec.pltype; + + ACMCodecDB::Codec(id + 1, &codec); + pltype_swb = codec.pltype; + + break; + } + } + + if (codec_id < 0) { + WEBRTC_TRACE(webrtc::kTraceError, webrtc::kTraceAudioCoding, id_, + "DecoderEstimatedBandwidth failed"); + return -1; + } + + if ((last_recv_audio_codec_pltype_ == pltype_wb) || + (last_recv_audio_codec_pltype_ == pltype_swb)) { + return codecs_[codec_id]->GetEstimatedBandwidth(); + } else { + return -1; + } +} + +// Set playout mode for: voice, fax, or streaming. +int32_t AudioCodingModuleImpl::SetPlayoutMode( + const AudioPlayoutMode mode) { + if ((mode != voice) && (mode != fax) && (mode != streaming) && + (mode != off)) { + WEBRTC_TRACE(webrtc::kTraceError, webrtc::kTraceAudioCoding, id_, + "Invalid playout mode."); + return -1; + } + return neteq_.SetPlayoutMode(mode); +} + +// Get playout mode voice, fax. +AudioPlayoutMode AudioCodingModuleImpl::PlayoutMode() const { + return neteq_.playout_mode(); +} + +// Get 10 milliseconds of raw audio data to play out. +// Automatic resample to the requested frequency. +int32_t AudioCodingModuleImpl::PlayoutData10Ms( + int32_t desired_freq_hz, AudioFrame* audio_frame) { + TRACE_EVENT_ASYNC_BEGIN0("webrtc", "ACM::PlayoutData10Ms", this); + bool stereo_mode; + + if (GetSilence(desired_freq_hz, audio_frame)) { + TRACE_EVENT_ASYNC_END1("webrtc", "ACM::PlayoutData10Ms", this, + "silence", true); + return 0; // Silence is generated, return. + } + + // RecOut always returns 10 ms. + if (neteq_.RecOut(audio_frame_) != 0) { + WEBRTC_TRACE(webrtc::kTraceError, webrtc::kTraceAudioCoding, id_, + "PlayoutData failed, RecOut Failed"); + return -1; + } + int decoded_seq_num; + uint32_t decoded_timestamp; + bool update_nack = + neteq_.DecodedRtpInfo(&decoded_seq_num, &decoded_timestamp) && + nack_enabled_; // Update NACK only if it is enabled. + audio_frame->num_channels_ = audio_frame_.num_channels_; + audio_frame->vad_activity_ = audio_frame_.vad_activity_; + audio_frame->speech_type_ = audio_frame_.speech_type_; + + stereo_mode = (audio_frame_.num_channels_ > 1); + + // For stereo playout: + // Master and Slave samples are interleaved starting with Master. + const uint16_t receive_freq = + static_cast<uint16_t>(audio_frame_.sample_rate_hz_); + bool tone_detected = false; + int16_t last_detected_tone; + int16_t tone; + + // Limit the scope of ACM Critical section. + { + CriticalSectionScoped lock(acm_crit_sect_); + + if (update_nack) { + assert(nack_.get()); + nack_->UpdateLastDecodedPacket(decoded_seq_num, decoded_timestamp); + } + + // If we are in AV-sync and have already received an audio packet, but the + // latest packet is too late, then insert sync packet. + if (av_sync_ && first_payload_received_ && + NowTimestamp(current_receive_codec_idx_) > 5 * last_timestamp_diff_ + + last_receive_timestamp_) { + if (!last_packet_was_sync_) { + // If the last packet inserted has been a regular packet Skip two + // packets to give room for PLC. + last_incoming_send_timestamp_ += 2 * last_timestamp_diff_; + last_sequence_number_ += 2; + last_receive_timestamp_ += 2 * last_timestamp_diff_; + } + + // One sync packet. + if (PushSyncPacketSafe() < 0) + return -1; + } + + if ((receive_freq != desired_freq_hz) && (desired_freq_hz != -1)) { + TRACE_EVENT_ASYNC_END2("webrtc", "ACM::PlayoutData10Ms", this, + "seqnum", decoded_seq_num, + "now", clock_->TimeInMilliseconds()); + // Resample payload_data. + int16_t temp_len = output_resampler_.Resample10Msec( + audio_frame_.data_, receive_freq, audio_frame->data_, + desired_freq_hz, audio_frame_.num_channels_); + + if (temp_len < 0) { + WEBRTC_TRACE(webrtc::kTraceError, webrtc::kTraceAudioCoding, id_, + "PlayoutData failed, resampler failed"); + return -1; + } + + // Set the payload data length from the resampler. + audio_frame->samples_per_channel_ = (uint16_t) temp_len; + // Set the sampling frequency. + audio_frame->sample_rate_hz_ = desired_freq_hz; + } else { + TRACE_EVENT_ASYNC_END2("webrtc", "ACM::PlayoutData10Ms", this, + "seqnum", decoded_seq_num, + "now", clock_->TimeInMilliseconds()); + memcpy(audio_frame->data_, audio_frame_.data_, + audio_frame_.samples_per_channel_ * audio_frame->num_channels_ + * sizeof(int16_t)); + // Set the payload length. + audio_frame->samples_per_channel_ = + audio_frame_.samples_per_channel_; + // Set the sampling frequency. + audio_frame->sample_rate_hz_ = receive_freq; + } + + // Tone detection done for master channel. + if (dtmf_detector_ != NULL) { + // Dtmf Detection. + if (audio_frame->sample_rate_hz_ == 8000) { + // Use audio_frame->data_ then Dtmf detector doesn't + // need resampling. + if (!stereo_mode) { + dtmf_detector_->Detect(audio_frame->data_, + audio_frame->samples_per_channel_, + audio_frame->sample_rate_hz_, tone_detected, + tone); + } else { + // We are in 8 kHz so the master channel needs only 80 samples. + int16_t master_channel[80]; + for (int n = 0; n < 80; n++) { + master_channel[n] = audio_frame->data_[n << 1]; + } + dtmf_detector_->Detect(master_channel, + audio_frame->samples_per_channel_, + audio_frame->sample_rate_hz_, tone_detected, + tone); + } + } else { + // Do the detection on the audio that we got from NetEQ (audio_frame_). + if (!stereo_mode) { + dtmf_detector_->Detect(audio_frame_.data_, + audio_frame_.samples_per_channel_, + receive_freq, tone_detected, tone); + } else { + int16_t master_channel[WEBRTC_10MS_PCM_AUDIO]; + for (int n = 0; n < audio_frame_.samples_per_channel_; n++) { + master_channel[n] = audio_frame_.data_[n << 1]; + } + dtmf_detector_->Detect(master_channel, + audio_frame_.samples_per_channel_, + receive_freq, tone_detected, tone); + } + } + } + + // We want to do this while we are in acm_crit_sect_. + // (Doesn't really need to initialize the following + // variable but Linux complains if we don't.) + last_detected_tone = kACMToneEnd; + if (tone_detected) { + last_detected_tone = last_detected_tone_; + last_detected_tone_ = tone; + } + } + + if (tone_detected) { + // We will deal with callback here, so enter callback critical section. + CriticalSectionScoped lock(callback_crit_sect_); + + if (dtmf_callback_ != NULL) { + if (tone != kACMToneEnd) { + // just a tone + dtmf_callback_->IncomingDtmf((uint8_t) tone, false); + } else if ((tone == kACMToneEnd) && (last_detected_tone != kACMToneEnd)) { + // The tone is "END" and the previously detected tone is + // not "END," so call fir an end. + dtmf_callback_->IncomingDtmf((uint8_t) last_detected_tone, true); + } + } + } + + audio_frame->id_ = id_; + audio_frame->energy_ = -1; + audio_frame->timestamp_ = 0; + + return 0; +} + +///////////////////////////////////////// +// (CNG) Comfort Noise Generation +// Generate comfort noise when receiving DTX packets +// + +// Get VAD aggressiveness on the incoming stream +ACMVADMode AudioCodingModuleImpl::ReceiveVADMode() const { + return neteq_.vad_mode(); +} + +// Configure VAD aggressiveness on the incoming stream. +int16_t AudioCodingModuleImpl::SetReceiveVADMode(const ACMVADMode mode) { + return neteq_.SetVADMode(mode); +} + +///////////////////////////////////////// +// Statistics +// + +int32_t AudioCodingModuleImpl::NetworkStatistics( + ACMNetworkStatistics* statistics) const { + int32_t status; + status = neteq_.NetworkStatistics(statistics); + return status; +} + +void AudioCodingModuleImpl::DestructEncoderInst(void* inst) { + WEBRTC_TRACE(webrtc::kTraceDebug, webrtc::kTraceAudioCoding, id_, + "DestructEncoderInst()"); + if (!HaveValidEncoder("DestructEncoderInst")) { + return; + } + + codecs_[current_send_codec_idx_]->DestructEncoderInst(inst); +} + +int16_t AudioCodingModuleImpl::AudioBuffer( + WebRtcACMAudioBuff& buffer) { + WEBRTC_TRACE(webrtc::kTraceDebug, webrtc::kTraceAudioCoding, id_, + "AudioBuffer()"); + if (!HaveValidEncoder("AudioBuffer")) { + return -1; + } + buffer.last_in_timestamp = last_in_timestamp_; + return codecs_[current_send_codec_idx_]->AudioBuffer(buffer); +} + +int16_t AudioCodingModuleImpl::SetAudioBuffer( + WebRtcACMAudioBuff& buffer) { + WEBRTC_TRACE(webrtc::kTraceDebug, webrtc::kTraceAudioCoding, id_, + "SetAudioBuffer()"); + if (!HaveValidEncoder("SetAudioBuffer")) { + return -1; + } + return codecs_[current_send_codec_idx_]->SetAudioBuffer(buffer); +} + +uint32_t AudioCodingModuleImpl::EarliestTimestamp() const { + WEBRTC_TRACE(webrtc::kTraceDebug, webrtc::kTraceAudioCoding, id_, + "EarliestTimestamp()"); + if (!HaveValidEncoder("EarliestTimestamp")) { + return -1; + } + return codecs_[current_send_codec_idx_]->EarliestTimestamp(); +} + +int32_t 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(turajs): Remove this API if it is not used. +// TODO(tlegrand): Modify this function to work for stereo, and add tests. +// TODO(turajs): Receive timestamp in this method is incremented by frame-size +// and does not reflect the true receive frame-size. Therefore, subsequent +// jitter computations are not accurate. +int32_t AudioCodingModuleImpl::IncomingPayload( + const uint8_t* incoming_payload, const int32_t payload_length, + const uint8_t payload_type, const uint32_t timestamp) { + if (payload_length < 0) { + // Log error in trace file. + WEBRTC_TRACE(webrtc::kTraceError, webrtc::kTraceAudioCoding, id_, + "IncomingPacket() Error, payload-length cannot be negative"); + return -1; + } + + if (dummy_rtp_header_ == NULL) { + // This is the first time that we are using |dummy_rtp_header_| + // so we have to create it. + WebRtcACMCodecParams codec_params; + dummy_rtp_header_ = new WebRtcRTPHeader; + if (dummy_rtp_header_ == NULL) { + WEBRTC_TRACE(webrtc::kTraceError, webrtc::kTraceAudioCoding, id_, + "IncomingPayload() Error, out of memory"); + return -1; + } + dummy_rtp_header_->header.payloadType = payload_type; + // Don't matter in this case. + dummy_rtp_header_->header.ssrc = 0; + dummy_rtp_header_->header.markerBit = false; + // Start with random numbers. + dummy_rtp_header_->header.sequenceNumber = rand(); + dummy_rtp_header_->header.timestamp = + (static_cast<uint32_t>(rand()) << 16) + + static_cast<uint32_t>(rand()); + dummy_rtp_header_->type.Audio.channel = 1; + + if (DecoderParamByPlType(payload_type, codec_params) < 0) { + // We didn't find a codec with the given payload. + // Something is wrong we exit, but we delete |dummy_rtp_header_| + // and set it to NULL to start clean next time. + delete dummy_rtp_header_; + dummy_rtp_header_ = NULL; + return -1; + } + recv_pl_frame_size_smpls_ = codec_params.codec_inst.pacsize; + } + + if (payload_type != dummy_rtp_header_->header.payloadType) { + // Payload type has changed since the last time we might need to + // update the frame-size. + WebRtcACMCodecParams codec_params; + if (DecoderParamByPlType(payload_type, codec_params) < 0) { + // We didn't find a codec with the given payload. + return -1; + } + recv_pl_frame_size_smpls_ = codec_params.codec_inst.pacsize; + dummy_rtp_header_->header.payloadType = payload_type; + } + + if (timestamp > 0) { + dummy_rtp_header_->header.timestamp = timestamp; + } + + // Store the payload Type. this will be used to retrieve "received codec" + // and "received frequency." + last_recv_audio_codec_pltype_ = payload_type; + + last_receive_timestamp_ += recv_pl_frame_size_smpls_; + // Insert in NetEQ. + if (neteq_.RecIn(incoming_payload, payload_length, *dummy_rtp_header_, + last_receive_timestamp_) < 0) { + return -1; + } + + // Get ready for the next payload. + dummy_rtp_header_->header.sequenceNumber++; + dummy_rtp_header_->header.timestamp += recv_pl_frame_size_smpls_; + return 0; +} + +int16_t AudioCodingModuleImpl::DecoderParamByPlType( + const uint8_t payload_type, + WebRtcACMCodecParams& codec_params) const { + CriticalSectionScoped lock(acm_crit_sect_); + for (int16_t id = 0; id < ACMCodecDB::kMaxNumCodecs; + id++) { + if (codecs_[id] != NULL) { + if (codecs_[id]->DecoderInitialized()) { + if (codecs_[id]->DecoderParams(&codec_params, payload_type)) { + return 0; + } + } + } + } + // If we are here it means that we could not find a + // codec with that payload type. reset the values to + // not acceptable values and return -1. + codec_params.codec_inst.plname[0] = '\0'; + codec_params.codec_inst.pacsize = 0; + codec_params.codec_inst.rate = 0; + codec_params.codec_inst.pltype = -1; + return -1; +} + +int16_t AudioCodingModuleImpl::DecoderListIDByPlName( + const char* name, const uint16_t frequency) const { + WebRtcACMCodecParams codec_params; + CriticalSectionScoped lock(acm_crit_sect_); + for (int16_t id = 0; id < ACMCodecDB::kMaxNumCodecs; id++) { + if ((codecs_[id] != NULL)) { + if (codecs_[id]->DecoderInitialized()) { + assert(registered_pltypes_[id] >= 0); + assert(registered_pltypes_[id] <= 255); + codecs_[id]->DecoderParams( + &codec_params, (uint8_t) registered_pltypes_[id]); + if (!STR_CASE_CMP(codec_params.codec_inst.plname, name)) { + // Check if the given sampling frequency matches. + // A zero sampling frequency means we matching the names + // is sufficient and we don't need to check for the + // frequencies. + // Currently it is only iSAC which has one name but two + // sampling frequencies. + if ((frequency == 0)|| + (codec_params.codec_inst.plfreq == frequency)) { + return id; + } + } + } + } + } + // If we are here it means that we could not find a + // codec with that payload type. return -1. + return -1; +} + +int32_t AudioCodingModuleImpl::LastEncodedTimestamp( + uint32_t& timestamp) const { + CriticalSectionScoped lock(acm_crit_sect_); + if (!HaveValidEncoder("LastEncodedTimestamp")) { + return -1; + } + timestamp = codecs_[current_send_codec_idx_]->LastEncodedTimestamp(); + return 0; +} + +int32_t 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; + } + + int32_t 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; +} + +int32_t 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; +} + +int32_t AudioCodingModuleImpl::SetISACMaxRate( + const uint32_t 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); +} + +int32_t AudioCodingModuleImpl::SetISACMaxPayloadSize( + const uint16_t max_size_bytes) { + CriticalSectionScoped lock(acm_crit_sect_); + + if (!HaveValidEncoder("SetISACMaxPayloadSize")) { + return -1; + } + + return codecs_[current_send_codec_idx_]->SetISACMaxPayloadSize( + max_size_bytes); +} + +int32_t AudioCodingModuleImpl::ConfigISACBandwidthEstimator( + const uint8_t frame_size_ms, + const uint16_t rate_bit_per_sec, + const 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); +} + +int32_t AudioCodingModuleImpl::SetBackgroundNoiseMode( + const ACMBackgroundNoiseMode mode) { + if ((mode < On) || (mode > Off)) { + WEBRTC_TRACE(webrtc::kTraceError, webrtc::kTraceAudioCoding, id_, + "The specified background noise is out of range.\n"); + return -1; + } + return neteq_.SetBackgroundNoiseMode(mode); +} + +int32_t AudioCodingModuleImpl::BackgroundNoiseMode( + ACMBackgroundNoiseMode* mode) { + return neteq_.BackgroundNoiseMode(*mode); +} + +int32_t AudioCodingModuleImpl::PlayoutTimestamp( + uint32_t* timestamp) { + WEBRTC_TRACE(webrtc::kTraceStream, webrtc::kTraceAudioCoding, id_, + "PlayoutTimestamp()"); + { + CriticalSectionScoped lock(acm_crit_sect_); + if (track_neteq_buffer_) { + *timestamp = playout_ts_; + return 0; + } + } + return neteq_.PlayoutTimestamp(*timestamp); +} + +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; +} + +int32_t AudioCodingModuleImpl::UnregisterReceiveCodec( + const int16_t payload_type) { + CriticalSectionScoped lock(acm_crit_sect_); + int id; + + // Search through the list of registered payload types. + for (id = 0; id < ACMCodecDB::kMaxNumCodecs; id++) { + if (registered_pltypes_[id] == payload_type) { + // We have found the id registered with the payload type. + break; + } + } + + if (id >= ACMCodecDB::kNumCodecs) { + // Payload type was not registered. No need to unregister. + return 0; + } + + // Unregister the codec with the given payload type. + return UnregisterReceiveCodecSafe(id); +} + +int32_t AudioCodingModuleImpl::UnregisterReceiveCodecSafe( + const int16_t codec_id) { + const WebRtcNetEQDecoder *neteq_decoder = ACMCodecDB::NetEQDecoders(); + int16_t mirror_id = ACMCodecDB::MirrorID(codec_id); + bool stereo_receiver = false; + + if (codecs_[codec_id] != NULL) { + if (registered_pltypes_[codec_id] != -1) { + // Store stereo information for future use. + stereo_receiver = stereo_receive_[codec_id]; + + // Before deleting the decoder instance unregister from NetEQ. + if (neteq_.RemoveCodec(neteq_decoder[codec_id], + stereo_receive_[codec_id]) < 0) { + CodecInst codec; + ACMCodecDB::Codec(codec_id, &codec); + WEBRTC_TRACE(webrtc::kTraceError, webrtc::kTraceAudioCoding, id_, + "Unregistering %s-%d from NetEQ failed.", codec.plname, + codec.plfreq); + return -1; + } + + // CN is a special case for NetEQ, all three sampling frequencies + // are unregistered if one is deleted. + if (IsCodecCN(codec_id)) { + for (int i = 0; i < ACMCodecDB::kNumCodecs; i++) { + if (IsCodecCN(i)) { + stereo_receive_[i] = false; + registered_pltypes_[i] = -1; + } + } + } else { + if (codec_id == mirror_id) { + codecs_[codec_id]->DestructDecoder(); + if (stereo_receive_[codec_id]) { + slave_codecs_[codec_id]->DestructDecoder(); + stereo_receive_[codec_id] = false; + } + } + } + + // Check if this is the last registered stereo receive codec. + if (stereo_receiver) { + bool no_stereo = true; + + for (int i = 0; i < ACMCodecDB::kNumCodecs; i++) { + if (stereo_receive_[i]) { + // We still have stereo codecs registered. + no_stereo = false; + break; + } + } + + // If we don't have any stereo codecs left, change status. + if (no_stereo) { + neteq_.RemoveSlaves(); // No longer need the slave. + stereo_receive_registered_ = false; + } + } + } + } + + if (registered_pltypes_[codec_id] == receive_red_pltype_) { + // RED is going to be unregistered, set to an invalid value. + receive_red_pltype_ = 255; + } + registered_pltypes_[codec_id] = -1; + + return 0; +} + +int32_t AudioCodingModuleImpl::REDPayloadISAC( + const int32_t isac_rate, const int16_t isac_bw_estimate, + uint8_t* payload, int16_t* length_bytes) { + if (!HaveValidEncoder("EncodeData")) { + return -1; + } + int16_t status; + status = codecs_[current_send_codec_idx_]->REDPayloadISAC(isac_rate, + isac_bw_estimate, + payload, + length_bytes); + return status; +} + +void AudioCodingModuleImpl::ResetFragmentation(int vector_size) { + for (int n = 0; n < kMaxNumFragmentationVectors; n++) { + fragmentation_.fragmentationOffset[n] = n * MAX_PAYLOAD_SIZE_BYTE; + } + memset(fragmentation_.fragmentationLength, 0, kMaxNumFragmentationVectors * + sizeof(fragmentation_.fragmentationLength[0])); + memset(fragmentation_.fragmentationTimeDiff, 0, kMaxNumFragmentationVectors * + sizeof(fragmentation_.fragmentationTimeDiff[0])); + memset(fragmentation_.fragmentationPlType, 0, kMaxNumFragmentationVectors * + sizeof(fragmentation_.fragmentationPlType[0])); + fragmentation_.fragmentationVectorSize = + static_cast<uint16_t>(vector_size); +} + +// TODO(turajs): Add second parameter to enable/disable AV-sync. +int AudioCodingModuleImpl::SetInitialPlayoutDelay(int delay_ms) { + if (delay_ms < 0 || delay_ms > 10000) { + return -1; + } + + CriticalSectionScoped lock(acm_crit_sect_); + + // Receiver should be initialized before this call processed. + if (!receiver_initialized_) { + InitializeReceiverSafe(); + } + + if (first_payload_received_) { + // Too late for this API. Only works before a call is started. + return -1; + } + initial_delay_ms_ = delay_ms; + + // If initial delay is zero, NetEq buffer should not be tracked, also we + // don't want to be in AV-sync mode. + track_neteq_buffer_ = delay_ms > 0; + av_sync_ = delay_ms > 0; + + neteq_.EnableAVSync(av_sync_); + return neteq_.SetMinimumDelay(delay_ms); +} + +bool AudioCodingModuleImpl::GetSilence(int desired_sample_rate_hz, + AudioFrame* frame) { + CriticalSectionScoped lock(acm_crit_sect_); + if (initial_delay_ms_ == 0 || !track_neteq_buffer_) { + return false; + } + + if (accumulated_audio_ms_ >= initial_delay_ms_) { + // We have enough data stored that match our initial delay target. + track_neteq_buffer_ = false; + return false; + } + + // We stop accumulating packets, if the number of packets or the total size + // exceeds a threshold. + int max_num_packets; + int buffer_size_bytes; + int per_payload_overhead_bytes; + neteq_.BufferSpec(max_num_packets, buffer_size_bytes, + per_payload_overhead_bytes); + int total_bytes_accumulated = num_bytes_accumulated_ + + num_packets_accumulated_ * per_payload_overhead_bytes; + if (num_packets_accumulated_ > max_num_packets * 0.9 || + total_bytes_accumulated > buffer_size_bytes * 0.9) { + WEBRTC_TRACE(webrtc::kTraceWarning, webrtc::kTraceAudioCoding, id_, + "GetSilence: Initial delay couldn't be achieved." + " num_packets_accumulated=%d, total_bytes_accumulated=%d", + num_packets_accumulated_, num_bytes_accumulated_); + track_neteq_buffer_ = false; + return false; + } + + if (desired_sample_rate_hz > 0) { + frame->sample_rate_hz_ = desired_sample_rate_hz; + } else { + frame->sample_rate_hz_ = 0; + if (current_receive_codec_idx_ >= 0) { + frame->sample_rate_hz_ = + ACMCodecDB::database_[current_receive_codec_idx_].plfreq; + } else { + // No payload received yet, use the default sampling rate of NetEq. + frame->sample_rate_hz_ = neteq_.CurrentSampFreqHz(); + } + } + frame->num_channels_ = expected_channels_; + 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; +} + +// Must be called within the scope of ACM critical section. +int AudioCodingModuleImpl::PushSyncPacketSafe() { + assert(av_sync_); + last_sequence_number_++; + last_incoming_send_timestamp_ += last_timestamp_diff_; + last_receive_timestamp_ += last_timestamp_diff_; + + WebRtcRTPHeader rtp_info; + rtp_info.header.payloadType = last_recv_audio_codec_pltype_; + rtp_info.header.ssrc = last_ssrc_; + rtp_info.header.markerBit = false; + rtp_info.header.sequenceNumber = last_sequence_number_; + rtp_info.header.timestamp = last_incoming_send_timestamp_; + rtp_info.type.Audio.channel = stereo_receive_[current_receive_codec_idx_] ? + 2 : 1; + last_packet_was_sync_ = true; + int payload_len_bytes = neteq_.RecIn(rtp_info, last_receive_timestamp_); + + if (payload_len_bytes < 0) + return -1; + + // This is to account for sync packets inserted during the buffering phase. + if (track_neteq_buffer_) + UpdateBufferingSafe(rtp_info, payload_len_bytes); + + return 0; +} + +// Must be called within the scope of ACM critical section. +void AudioCodingModuleImpl::UpdateBufferingSafe(const WebRtcRTPHeader& rtp_info, + int payload_len_bytes) { + const int in_sample_rate_khz = + (ACMCodecDB::database_[current_receive_codec_idx_].plfreq / 1000); + if (first_payload_received_ && + rtp_info.header.timestamp > last_incoming_send_timestamp_ && + in_sample_rate_khz > 0) { + accumulated_audio_ms_ += (rtp_info.header.timestamp - + last_incoming_send_timestamp_) / in_sample_rate_khz; + } + + num_packets_accumulated_++; + num_bytes_accumulated_ += payload_len_bytes; + + playout_ts_ = static_cast<uint32_t>( + rtp_info.header.timestamp - static_cast<uint32_t>( + initial_delay_ms_ * in_sample_rate_khz)); +} + +uint32_t AudioCodingModuleImpl::NowTimestamp(int codec_id) { + // 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 we don't lose resolution + // when do the following multiplication. + int sample_rate_khz = ACMCodecDB::database_[codec_id].plfreq / 1000; + const uint32_t now_in_ms = static_cast<uint32_t>( + clock_->TimeInMilliseconds() & kMaskTimestamp); + return static_cast<uint32_t>(sample_rate_khz * now_in_ms); +} + +std::vector<uint16_t> AudioCodingModuleImpl::GetNackList( + int round_trip_time_ms) const { + CriticalSectionScoped lock(acm_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; +} + +int AudioCodingModuleImpl::LeastRequiredDelayMs() const { + return std::max(neteq_.LeastRequiredDelayMs(), initial_delay_ms_); +} + +int AudioCodingModuleImpl::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(acm_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 (current_receive_codec_idx_ >= 0) { + nack_->UpdateSampleRate( + ACMCodecDB::database_[current_receive_codec_idx_].plfreq); + } + } + return nack_->SetMaxNackListSize(max_nack_list_size); +} + +void AudioCodingModuleImpl::DisableNack() { + CriticalSectionScoped lock(acm_crit_sect_); + nack_.reset(); // Memory is released. + nack_enabled_ = false; +} + +} // namespace webrtc |