| /* |
| * Copyright (c) 2012 The WebRTC project authors. All Rights Reserved. |
| * |
| * Use of this source code is governed by a BSD-style license |
| * that can be found in the LICENSE file in the root of the source |
| * tree. An additional intellectual property rights grant can be found |
| * in the file PATENTS. All contributing project authors may |
| * be found in the AUTHORS file in the root of the source tree. |
| */ |
| |
| #include "webrtc/modules/audio_coding/main/source/acm_neteq.h" |
| |
| #include <stdlib.h> // malloc |
| |
| #include <algorithm> // sort |
| #include <vector> |
| |
| #include "webrtc/common_audio/signal_processing/include/signal_processing_library.h" |
| #include "webrtc/common_types.h" |
| #include "webrtc/modules/audio_coding/neteq/interface/webrtc_neteq.h" |
| #include "webrtc/modules/audio_coding/neteq/interface/webrtc_neteq_internal.h" |
| #include "webrtc/system_wrappers/interface/critical_section_wrapper.h" |
| #include "webrtc/system_wrappers/interface/rw_lock_wrapper.h" |
| #include "webrtc/system_wrappers/interface/trace.h" |
| #include "webrtc/system_wrappers/interface/trace_event.h" |
| |
| namespace webrtc { |
| |
| namespace acm1 { |
| |
| #define RTP_HEADER_SIZE 12 |
| #define NETEQ_INIT_FREQ 8000 |
| #define NETEQ_INIT_FREQ_KHZ (NETEQ_INIT_FREQ/1000) |
| #define NETEQ_ERR_MSG_LEN_BYTE (WEBRTC_NETEQ_MAX_ERROR_NAME + 1) |
| |
| ACMNetEQ::ACMNetEQ() |
| : id_(0), |
| current_samp_freq_khz_(NETEQ_INIT_FREQ_KHZ), |
| avt_playout_(false), |
| playout_mode_(voice), |
| neteq_crit_sect_(CriticalSectionWrapper::CreateCriticalSection()), |
| vad_status_(false), |
| vad_mode_(VADNormal), |
| decode_lock_(RWLockWrapper::CreateRWLock()), |
| num_slaves_(0), |
| received_stereo_(false), |
| master_slave_info_(NULL), |
| previous_audio_activity_(AudioFrame::kVadUnknown), |
| callback_crit_sect_(CriticalSectionWrapper::CreateCriticalSection()), |
| min_of_max_num_packets_(0), |
| min_of_buffer_size_bytes_(0), |
| per_packet_overhead_bytes_(0), |
| av_sync_(false), |
| minimum_delay_ms_(0), |
| maximum_delay_ms_(0) { |
| for (int n = 0; n < MAX_NUM_SLAVE_NETEQ + 1; n++) { |
| is_initialized_[n] = false; |
| ptr_vadinst_[n] = NULL; |
| inst_[n] = NULL; |
| inst_mem_[n] = NULL; |
| neteq_packet_buffer_[n] = NULL; |
| } |
| } |
| |
| ACMNetEQ::~ACMNetEQ() { |
| { |
| CriticalSectionScoped lock(neteq_crit_sect_); |
| RemoveNetEQSafe(0); // Master. |
| RemoveSlavesSafe(); |
| } |
| if (neteq_crit_sect_ != NULL) { |
| delete neteq_crit_sect_; |
| } |
| |
| if (decode_lock_ != NULL) { |
| delete decode_lock_; |
| } |
| |
| if (callback_crit_sect_ != NULL) { |
| delete callback_crit_sect_; |
| } |
| } |
| |
| int32_t ACMNetEQ::Init() { |
| CriticalSectionScoped lock(neteq_crit_sect_); |
| |
| for (int16_t idx = 0; idx < num_slaves_ + 1; idx++) { |
| if (InitByIdxSafe(idx) < 0) { |
| return -1; |
| } |
| // delete VAD instance and start fresh if required. |
| if (ptr_vadinst_[idx] != NULL) { |
| WebRtcVad_Free(ptr_vadinst_[idx]); |
| ptr_vadinst_[idx] = NULL; |
| } |
| if (vad_status_) { |
| // Has to enable VAD |
| if (EnableVADByIdxSafe(idx) < 0) { |
| // Failed to enable VAD. |
| // Delete VAD instance, if it is created |
| if (ptr_vadinst_[idx] != NULL) { |
| WebRtcVad_Free(ptr_vadinst_[idx]); |
| ptr_vadinst_[idx] = NULL; |
| } |
| // We are at initialization of NetEq, if failed to |
| // enable VAD, we delete the NetEq instance. |
| if (inst_mem_[idx] != NULL) { |
| free(inst_mem_[idx]); |
| inst_mem_[idx] = NULL; |
| inst_[idx] = NULL; |
| } |
| is_initialized_[idx] = false; |
| return -1; |
| } |
| } |
| is_initialized_[idx] = true; |
| } |
| if (EnableVAD() == -1) { |
| return -1; |
| } |
| return 0; |
| } |
| |
| int16_t ACMNetEQ::InitByIdxSafe(const int16_t idx) { |
| int memory_size_bytes; |
| if (WebRtcNetEQ_AssignSize(&memory_size_bytes) != 0) { |
| LogError("AssignSize", idx); |
| return -1; |
| } |
| |
| if (inst_mem_[idx] != NULL) { |
| free(inst_mem_[idx]); |
| inst_mem_[idx] = NULL; |
| } |
| inst_mem_[idx] = malloc(memory_size_bytes); |
| if (inst_mem_[idx] == NULL) { |
| WEBRTC_TRACE(webrtc::kTraceError, webrtc::kTraceAudioCoding, id_, |
| "InitByIdxSafe: NetEq Initialization error: could not " |
| "allocate memory for NetEq"); |
| is_initialized_[idx] = false; |
| return -1; |
| } |
| if (WebRtcNetEQ_Assign(&inst_[idx], inst_mem_[idx]) != 0) { |
| if (inst_mem_[idx] != NULL) { |
| free(inst_mem_[idx]); |
| inst_mem_[idx] = NULL; |
| } |
| LogError("Assign", idx); |
| WEBRTC_TRACE(webrtc::kTraceError, webrtc::kTraceAudioCoding, id_, |
| "InitByIdxSafe: NetEq Initialization error: could not Assign"); |
| is_initialized_[idx] = false; |
| return -1; |
| } |
| if (WebRtcNetEQ_Init(inst_[idx], NETEQ_INIT_FREQ) != 0) { |
| if (inst_mem_[idx] != NULL) { |
| free(inst_mem_[idx]); |
| inst_mem_[idx] = NULL; |
| } |
| LogError("Init", idx); |
| WEBRTC_TRACE(webrtc::kTraceError, webrtc::kTraceAudioCoding, id_, |
| "InitByIdxSafe: NetEq Initialization error: could not " |
| "initialize NetEq"); |
| is_initialized_[idx] = false; |
| return -1; |
| } |
| is_initialized_[idx] = true; |
| return 0; |
| } |
| |
| int16_t ACMNetEQ::EnableVADByIdxSafe(const int16_t idx) { |
| if (ptr_vadinst_[idx] == NULL) { |
| if (WebRtcVad_Create(&ptr_vadinst_[idx]) < 0) { |
| ptr_vadinst_[idx] = NULL; |
| WEBRTC_TRACE(webrtc::kTraceError, webrtc::kTraceAudioCoding, id_, |
| "EnableVADByIdxSafe: NetEq Initialization error: could not " |
| "create VAD"); |
| return -1; |
| } |
| } |
| |
| if (WebRtcNetEQ_SetVADInstance( |
| inst_[idx], ptr_vadinst_[idx], |
| (WebRtcNetEQ_VADInitFunction) WebRtcVad_Init, |
| (WebRtcNetEQ_VADSetmodeFunction) WebRtcVad_set_mode, |
| (WebRtcNetEQ_VADFunction) WebRtcVad_Process) < 0) { |
| LogError("setVADinstance", idx); |
| WEBRTC_TRACE(webrtc::kTraceError, webrtc::kTraceAudioCoding, id_, |
| "EnableVADByIdxSafe: NetEq Initialization error: could not " |
| "set VAD instance"); |
| return -1; |
| } |
| |
| if (WebRtcNetEQ_SetVADMode(inst_[idx], vad_mode_) < 0) { |
| LogError("setVADmode", idx); |
| WEBRTC_TRACE(webrtc::kTraceError, webrtc::kTraceAudioCoding, id_, |
| "EnableVADByIdxSafe: NetEq Initialization error: could not " |
| "set VAD mode"); |
| return -1; |
| } |
| return 0; |
| } |
| |
| int32_t ACMNetEQ::AllocatePacketBuffer( |
| const WebRtcNetEQDecoder* used_codecs, |
| int16_t num_codecs) { |
| // Due to WebRtcNetEQ_GetRecommendedBufferSize |
| // the following has to be int otherwise we will have compiler error |
| // if not casted |
| |
| CriticalSectionScoped lock(neteq_crit_sect_); |
| for (int16_t idx = 0; idx < num_slaves_ + 1; idx++) { |
| if (AllocatePacketBufferByIdxSafe(used_codecs, num_codecs, idx) < 0) { |
| return -1; |
| } |
| } |
| return 0; |
| } |
| |
| int16_t ACMNetEQ::AllocatePacketBufferByIdxSafe( |
| const WebRtcNetEQDecoder* used_codecs, |
| int16_t num_codecs, |
| const int16_t idx) { |
| int max_num_packets; |
| int buffer_size_in_bytes; |
| int per_packet_overhead_bytes; |
| |
| if (!is_initialized_[idx]) { |
| WEBRTC_TRACE(webrtc::kTraceError, webrtc::kTraceAudioCoding, id_, |
| "AllocatePacketBufferByIdxSafe: NetEq is not initialized."); |
| return -1; |
| } |
| if (WebRtcNetEQ_GetRecommendedBufferSize(inst_[idx], used_codecs, |
| num_codecs, |
| kTCPXLargeJitter, |
| &max_num_packets, |
| &buffer_size_in_bytes, |
| &per_packet_overhead_bytes) != 0) { |
| LogError("GetRecommendedBufferSize", idx); |
| return -1; |
| } |
| if (idx == 0) { |
| min_of_buffer_size_bytes_ = buffer_size_in_bytes; |
| min_of_max_num_packets_ = max_num_packets; |
| per_packet_overhead_bytes_ = per_packet_overhead_bytes; |
| } else { |
| min_of_buffer_size_bytes_ = std::min(min_of_buffer_size_bytes_, |
| buffer_size_in_bytes); |
| min_of_max_num_packets_ = std::min(min_of_max_num_packets_, |
| max_num_packets); |
| } |
| if (neteq_packet_buffer_[idx] != NULL) { |
| free(neteq_packet_buffer_[idx]); |
| neteq_packet_buffer_[idx] = NULL; |
| } |
| |
| neteq_packet_buffer_[idx] = (int16_t *) malloc(buffer_size_in_bytes); |
| if (neteq_packet_buffer_[idx] == NULL) { |
| WEBRTC_TRACE(webrtc::kTraceError, webrtc::kTraceAudioCoding, id_, |
| "AllocatePacketBufferByIdxSafe: NetEq Initialization error: " |
| "could not allocate memory for NetEq Packet Buffer"); |
| return -1; |
| } |
| if (WebRtcNetEQ_AssignBuffer(inst_[idx], max_num_packets, |
| neteq_packet_buffer_[idx], |
| buffer_size_in_bytes) != 0) { |
| if (neteq_packet_buffer_[idx] != NULL) { |
| free(neteq_packet_buffer_[idx]); |
| neteq_packet_buffer_[idx] = NULL; |
| } |
| LogError("AssignBuffer", idx); |
| return -1; |
| } |
| return 0; |
| } |
| |
| int32_t ACMNetEQ::SetAVTPlayout(const bool enable) { |
| CriticalSectionScoped lock(neteq_crit_sect_); |
| if (avt_playout_ != enable) { |
| for (int16_t idx = 0; idx < num_slaves_ + 1; idx++) { |
| if (!is_initialized_[idx]) { |
| WEBRTC_TRACE(webrtc::kTraceError, webrtc::kTraceAudioCoding, id_, |
| "SetAVTPlayout: NetEq is not initialized."); |
| return -1; |
| } |
| if (WebRtcNetEQ_SetAVTPlayout(inst_[idx], (enable) ? 1 : 0) < 0) { |
| LogError("SetAVTPlayout", idx); |
| return -1; |
| } |
| } |
| } |
| avt_playout_ = enable; |
| return 0; |
| } |
| |
| bool ACMNetEQ::avt_playout() const { |
| CriticalSectionScoped lock(neteq_crit_sect_); |
| return avt_playout_; |
| } |
| |
| int32_t ACMNetEQ::CurrentSampFreqHz() const { |
| CriticalSectionScoped lock(neteq_crit_sect_); |
| if (!is_initialized_[0]) { |
| WEBRTC_TRACE(webrtc::kTraceError, webrtc::kTraceAudioCoding, id_, |
| "CurrentSampFreqHz: NetEq is not initialized."); |
| return -1; |
| } |
| return (int32_t)(1000 * current_samp_freq_khz_); |
| } |
| |
| int32_t ACMNetEQ::SetPlayoutMode(const AudioPlayoutMode mode) { |
| CriticalSectionScoped lock(neteq_crit_sect_); |
| if (playout_mode_ == mode) |
| return 0; |
| |
| enum WebRtcNetEQPlayoutMode playout_mode = kPlayoutOff; |
| enum WebRtcNetEQBGNMode background_noise_mode = kBGNOn; |
| switch (mode) { |
| case voice: |
| playout_mode = kPlayoutOn; |
| background_noise_mode = kBGNOn; |
| break; |
| case fax: |
| playout_mode = kPlayoutFax; |
| WebRtcNetEQ_GetBGNMode(inst_[0], &background_noise_mode); // No change. |
| break; |
| case streaming: |
| playout_mode = kPlayoutStreaming; |
| background_noise_mode = kBGNOff; |
| break; |
| case off: |
| playout_mode = kPlayoutOff; |
| background_noise_mode = kBGNOff; |
| break; |
| } |
| |
| int err = 0; |
| for (int16_t idx = 0; idx < num_slaves_ + 1; idx++) { |
| if (!is_initialized_[idx]) { |
| WEBRTC_TRACE(webrtc::kTraceError, webrtc::kTraceAudioCoding, id_, |
| "SetPlayoutMode: NetEq is not initialized."); |
| return -1; |
| } |
| |
| if (WebRtcNetEQ_SetPlayoutMode(inst_[idx], playout_mode) < 0) { |
| LogError("SetPlayoutMode", idx); |
| err = -1; |
| } |
| |
| if (WebRtcNetEQ_SetBGNMode(inst_[idx], kBGNOff) < 0) { |
| LogError("SetPlayoutMode::SetBGNMode", idx); |
| err = -1; |
| } |
| } |
| if (err == 0) |
| playout_mode_ = mode; |
| return err; |
| } |
| |
| AudioPlayoutMode ACMNetEQ::playout_mode() const { |
| CriticalSectionScoped lock(neteq_crit_sect_); |
| return playout_mode_; |
| } |
| |
| int32_t ACMNetEQ::NetworkStatistics( |
| ACMNetworkStatistics* statistics) const { |
| WebRtcNetEQ_NetworkStatistics stats; |
| CriticalSectionScoped lock(neteq_crit_sect_); |
| if (!is_initialized_[0]) { |
| WEBRTC_TRACE(webrtc::kTraceError, webrtc::kTraceAudioCoding, id_, |
| "NetworkStatistics: NetEq is not initialized."); |
| return -1; |
| } |
| if (WebRtcNetEQ_GetNetworkStatistics(inst_[0], &stats) == 0) { |
| statistics->currentAccelerateRate = stats.currentAccelerateRate; |
| statistics->currentBufferSize = stats.currentBufferSize; |
| statistics->jitterPeaksFound = (stats.jitterPeaksFound > 0); |
| statistics->currentDiscardRate = stats.currentDiscardRate; |
| statistics->currentExpandRate = stats.currentExpandRate; |
| statistics->currentPacketLossRate = stats.currentPacketLossRate; |
| statistics->currentPreemptiveRate = stats.currentPreemptiveRate; |
| statistics->preferredBufferSize = stats.preferredBufferSize; |
| statistics->clockDriftPPM = stats.clockDriftPPM; |
| statistics->addedSamples = stats.addedSamples; |
| } else { |
| LogError("getNetworkStatistics", 0); |
| return -1; |
| } |
| const int kArrayLen = 100; |
| int waiting_times[kArrayLen]; |
| int waiting_times_len = WebRtcNetEQ_GetRawFrameWaitingTimes(inst_[0], |
| kArrayLen, |
| waiting_times); |
| if (waiting_times_len > 0) { |
| std::vector<int> waiting_times_vec(waiting_times, |
| waiting_times + waiting_times_len); |
| std::sort(waiting_times_vec.begin(), waiting_times_vec.end()); |
| size_t size = waiting_times_vec.size(); |
| assert(size == static_cast<size_t>(waiting_times_len)); |
| if (size % 2 == 0) { |
| statistics->medianWaitingTimeMs = (waiting_times_vec[size / 2 - 1] + |
| waiting_times_vec[size / 2]) / 2; |
| } else { |
| statistics->medianWaitingTimeMs = waiting_times_vec[size / 2]; |
| } |
| statistics->minWaitingTimeMs = waiting_times_vec.front(); |
| statistics->maxWaitingTimeMs = waiting_times_vec.back(); |
| double sum = 0; |
| for (size_t i = 0; i < size; ++i) { |
| sum += waiting_times_vec[i]; |
| } |
| statistics->meanWaitingTimeMs = static_cast<int>(sum / size); |
| } else if (waiting_times_len == 0) { |
| statistics->meanWaitingTimeMs = -1; |
| statistics->medianWaitingTimeMs = -1; |
| statistics->minWaitingTimeMs = -1; |
| statistics->maxWaitingTimeMs = -1; |
| } else { |
| LogError("getRawFrameWaitingTimes", 0); |
| return -1; |
| } |
| return 0; |
| } |
| |
| // Should only be called in AV-sync mode. |
| int ACMNetEQ::RecIn(const WebRtcRTPHeader& rtp_info, |
| uint32_t receive_timestamp) { |
| assert(av_sync_); |
| |
| // Translate to NetEq structure. |
| WebRtcNetEQ_RTPInfo neteq_rtpinfo; |
| neteq_rtpinfo.payloadType = rtp_info.header.payloadType; |
| neteq_rtpinfo.sequenceNumber = rtp_info.header.sequenceNumber; |
| neteq_rtpinfo.timeStamp = rtp_info.header.timestamp; |
| neteq_rtpinfo.SSRC = rtp_info.header.ssrc; |
| neteq_rtpinfo.markerBit = rtp_info.header.markerBit; |
| |
| CriticalSectionScoped lock(neteq_crit_sect_); |
| |
| // Master should be initialized. |
| assert(is_initialized_[0]); |
| |
| // Push into Master. |
| int status = WebRtcNetEQ_RecInSyncRTP(inst_[0], &neteq_rtpinfo, |
| receive_timestamp); |
| if (status < 0) { |
| LogError("RecInSyncRTP", 0); |
| WEBRTC_TRACE(webrtc::kTraceError, webrtc::kTraceAudioCoding, id_, |
| "RecIn (sync): NetEq, error in pushing in Master"); |
| return -1; |
| } |
| |
| // If the received stream is stereo, insert a sync payload into slave. |
| if (rtp_info.type.Audio.channel == 2) { |
| // Slave should be initialized. |
| assert(is_initialized_[1]); |
| |
| // PUSH into Slave |
| status = WebRtcNetEQ_RecInSyncRTP(inst_[1], &neteq_rtpinfo, |
| receive_timestamp); |
| if (status < 0) { |
| LogError("RecInRTPStruct", 1); |
| WEBRTC_TRACE(webrtc::kTraceError, webrtc::kTraceAudioCoding, id_, |
| "RecIn (sync): NetEq, error in pushing in Slave"); |
| return -1; |
| } |
| } |
| return status; |
| } |
| |
| int32_t ACMNetEQ::RecIn(const uint8_t* incoming_payload, |
| const int32_t length_payload, |
| const WebRtcRTPHeader& rtp_info, |
| uint32_t receive_timestamp) { |
| int16_t payload_length = static_cast<int16_t>(length_payload); |
| |
| // Translate to NetEq structure. |
| WebRtcNetEQ_RTPInfo neteq_rtpinfo; |
| neteq_rtpinfo.payloadType = rtp_info.header.payloadType; |
| neteq_rtpinfo.sequenceNumber = rtp_info.header.sequenceNumber; |
| neteq_rtpinfo.timeStamp = rtp_info.header.timestamp; |
| neteq_rtpinfo.SSRC = rtp_info.header.ssrc; |
| neteq_rtpinfo.markerBit = rtp_info.header.markerBit; |
| |
| CriticalSectionScoped lock(neteq_crit_sect_); |
| |
| int status; |
| // In case of stereo payload, first half of the data should be pushed into |
| // master, and the second half into slave. |
| if (rtp_info.type.Audio.channel == 2) { |
| payload_length = payload_length / 2; |
| } |
| |
| // Check that master is initialized. |
| if (!is_initialized_[0]) { |
| WEBRTC_TRACE(webrtc::kTraceError, webrtc::kTraceAudioCoding, id_, |
| "RecIn: NetEq is not initialized."); |
| return -1; |
| } |
| // Push into Master. |
| status = WebRtcNetEQ_RecInRTPStruct(inst_[0], &neteq_rtpinfo, |
| incoming_payload, payload_length, |
| receive_timestamp); |
| if (status < 0) { |
| LogError("RecInRTPStruct", 0); |
| WEBRTC_TRACE(webrtc::kTraceError, webrtc::kTraceAudioCoding, id_, |
| "RecIn: NetEq, error in pushing in Master"); |
| return -1; |
| } |
| |
| // If the received stream is stereo, insert second half of paket into slave. |
| if (rtp_info.type.Audio.channel == 2) { |
| if (!is_initialized_[1]) { |
| WEBRTC_TRACE(webrtc::kTraceError, webrtc::kTraceAudioCoding, id_, |
| "RecIn: NetEq is not initialized."); |
| return -1; |
| } |
| // Push into Slave. |
| status = WebRtcNetEQ_RecInRTPStruct(inst_[1], &neteq_rtpinfo, |
| &incoming_payload[payload_length], |
| payload_length, receive_timestamp); |
| if (status < 0) { |
| LogError("RecInRTPStruct", 1); |
| WEBRTC_TRACE(webrtc::kTraceError, webrtc::kTraceAudioCoding, id_, |
| "RecIn: NetEq, error in pushing in Slave"); |
| return -1; |
| } |
| } |
| |
| return 0; |
| } |
| |
| int32_t ACMNetEQ::RecOut(AudioFrame& audio_frame) { |
| enum WebRtcNetEQOutputType type; |
| int16_t payload_len_sample; |
| enum WebRtcNetEQOutputType type_master; |
| enum WebRtcNetEQOutputType type_slave; |
| |
| int16_t payload_len_sample_slave; |
| |
| CriticalSectionScoped lockNetEq(neteq_crit_sect_); |
| |
| if (!received_stereo_) { |
| if (!is_initialized_[0]) { |
| WEBRTC_TRACE(webrtc::kTraceError, webrtc::kTraceAudioCoding, id_, |
| "RecOut: NetEq is not initialized."); |
| return -1; |
| } |
| { |
| WriteLockScoped lockCodec(*decode_lock_); |
| if (WebRtcNetEQ_RecOut(inst_[0], &(audio_frame.data_[0]), |
| &payload_len_sample) != 0) { |
| LogError("RecOut", 0); |
| WEBRTC_TRACE(webrtc::kTraceError, webrtc::kTraceAudioCoding, id_, |
| "RecOut: NetEq, error in pulling out for mono case"); |
| // Check for errors that can be recovered from: |
| // RECOUT_ERROR_SAMPLEUNDERRUN = 2003 |
| int error_code = WebRtcNetEQ_GetErrorCode(inst_[0]); |
| if (error_code != 2003) { |
| // Cannot recover; return an error |
| return -1; |
| } |
| } |
| } |
| WebRtcNetEQ_GetSpeechOutputType(inst_[0], &type); |
| audio_frame.num_channels_ = 1; |
| } else { |
| if (!is_initialized_[0] || !is_initialized_[1]) { |
| WEBRTC_TRACE(webrtc::kTraceError, webrtc::kTraceAudioCoding, id_, |
| "RecOut: NetEq is not initialized."); |
| return -1; |
| } |
| int16_t payload_master[480]; |
| int16_t payload_slave[480]; |
| { |
| WriteLockScoped lockCodec(*decode_lock_); |
| if (WebRtcNetEQ_RecOutMasterSlave(inst_[0], payload_master, |
| &payload_len_sample, master_slave_info_, |
| 1) != 0) { |
| LogError("RecOutMasterSlave", 0); |
| WEBRTC_TRACE(webrtc::kTraceError, webrtc::kTraceAudioCoding, id_, |
| "RecOut: NetEq, error in pulling out for master"); |
| |
| // Check for errors that can be recovered from: |
| // RECOUT_ERROR_SAMPLEUNDERRUN = 2003 |
| int error_code = WebRtcNetEQ_GetErrorCode(inst_[0]); |
| if (error_code != 2003) { |
| // Cannot recover; return an error |
| return -1; |
| } |
| } |
| if (WebRtcNetEQ_RecOutMasterSlave(inst_[1], payload_slave, |
| &payload_len_sample_slave, |
| master_slave_info_, 0) != 0) { |
| LogError("RecOutMasterSlave", 1); |
| WEBRTC_TRACE(webrtc::kTraceError, webrtc::kTraceAudioCoding, id_, |
| "RecOut: NetEq, error in pulling out for slave"); |
| |
| // Check for errors that can be recovered from: |
| // RECOUT_ERROR_SAMPLEUNDERRUN = 2003 |
| int error_code = WebRtcNetEQ_GetErrorCode(inst_[1]); |
| if (error_code != 2003) { |
| // Cannot recover; return an error |
| return -1; |
| } |
| } |
| } |
| |
| if (payload_len_sample != payload_len_sample_slave) { |
| WEBRTC_TRACE(webrtc::kTraceWarning, webrtc::kTraceAudioCoding, id_, |
| "RecOut: mismatch between the lenght of the decoded audio " |
| "by Master (%d samples) and Slave (%d samples).", |
| payload_len_sample, payload_len_sample_slave); |
| if (payload_len_sample > payload_len_sample_slave) { |
| memset(&payload_slave[payload_len_sample_slave], 0, |
| (payload_len_sample - payload_len_sample_slave) * |
| sizeof(int16_t)); |
| } |
| } |
| |
| for (int16_t n = 0; n < payload_len_sample; n++) { |
| audio_frame.data_[n << 1] = payload_master[n]; |
| audio_frame.data_[(n << 1) + 1] = payload_slave[n]; |
| } |
| audio_frame.num_channels_ = 2; |
| |
| WebRtcNetEQ_GetSpeechOutputType(inst_[0], &type_master); |
| WebRtcNetEQ_GetSpeechOutputType(inst_[1], &type_slave); |
| if ((type_master == kOutputNormal) || (type_slave == kOutputNormal)) { |
| type = kOutputNormal; |
| } else { |
| type = type_master; |
| } |
| } |
| |
| audio_frame.samples_per_channel_ = |
| static_cast<uint16_t>(payload_len_sample); |
| // NetEq always returns 10 ms of audio. |
| current_samp_freq_khz_ = |
| static_cast<float>(audio_frame.samples_per_channel_) / 10.0f; |
| audio_frame.sample_rate_hz_ = audio_frame.samples_per_channel_ * 100; |
| if (vad_status_) { |
| if (type == kOutputVADPassive) { |
| audio_frame.vad_activity_ = AudioFrame::kVadPassive; |
| audio_frame.speech_type_ = AudioFrame::kNormalSpeech; |
| } else if (type == kOutputNormal) { |
| audio_frame.vad_activity_ = AudioFrame::kVadActive; |
| audio_frame.speech_type_ = AudioFrame::kNormalSpeech; |
| } else if (type == kOutputPLC) { |
| audio_frame.vad_activity_ = previous_audio_activity_; |
| audio_frame.speech_type_ = AudioFrame::kPLC; |
| } else if (type == kOutputCNG) { |
| audio_frame.vad_activity_ = AudioFrame::kVadPassive; |
| audio_frame.speech_type_ = AudioFrame::kCNG; |
| } else { |
| audio_frame.vad_activity_ = AudioFrame::kVadPassive; |
| audio_frame.speech_type_ = AudioFrame::kPLCCNG; |
| } |
| } else { |
| // Always return kVadUnknown when receive VAD is inactive |
| audio_frame.vad_activity_ = AudioFrame::kVadUnknown; |
| if (type == kOutputNormal) { |
| audio_frame.speech_type_ = AudioFrame::kNormalSpeech; |
| } else if (type == kOutputPLC) { |
| audio_frame.speech_type_ = AudioFrame::kPLC; |
| } else if (type == kOutputPLCtoCNG) { |
| audio_frame.speech_type_ = AudioFrame::kPLCCNG; |
| } else if (type == kOutputCNG) { |
| audio_frame.speech_type_ = AudioFrame::kCNG; |
| } else { |
| // type is kOutputVADPassive which |
| // we don't expect to get if vad_status_ is false |
| WEBRTC_TRACE(webrtc::kTraceWarning, webrtc::kTraceAudioCoding, id_, |
| "RecOut: NetEq returned kVadPassive while vad_status_ is " |
| "false."); |
| audio_frame.vad_activity_ = AudioFrame::kVadUnknown; |
| audio_frame.speech_type_ = AudioFrame::kNormalSpeech; |
| } |
| } |
| previous_audio_activity_ = audio_frame.vad_activity_; |
| |
| WebRtcNetEQ_ProcessingActivity processing_stats; |
| WebRtcNetEQ_GetProcessingActivity(inst_[0], &processing_stats); |
| WEBRTC_TRACE(webrtc::kTraceDebug, webrtc::kTraceAudioCoding, id_, |
| "ACM::RecOut accelerate_bgn=%d accelerate_normal=%d" |
| " expand_bgn=%d expand_normal=%d" |
| " preemptive_bgn=%d preemptive_normal=%d" |
| " merge_bgn=%d merge_normal=%d", |
| processing_stats.accelerate_bgn_samples, |
| processing_stats.accelerate_normal_samples, |
| processing_stats.expand_bgn_sampels, |
| processing_stats.expand_normal_samples, |
| processing_stats.preemptive_expand_bgn_samples, |
| processing_stats.preemptive_expand_normal_samples, |
| processing_stats.merge_expand_bgn_samples, |
| processing_stats.merge_expand_normal_samples); |
| return 0; |
| } |
| |
| // When ACMGenericCodec has set the codec specific parameters in codec_def |
| // it calls AddCodec() to add the new codec to the NetEQ database. |
| int32_t ACMNetEQ::AddCodec(WebRtcNetEQ_CodecDef* codec_def, |
| bool to_master) { |
| if (codec_def == NULL) { |
| WEBRTC_TRACE(webrtc::kTraceError, webrtc::kTraceAudioCoding, id_, |
| "ACMNetEQ::AddCodec: error, codec_def is NULL"); |
| return -1; |
| } |
| CriticalSectionScoped lock(neteq_crit_sect_); |
| |
| int16_t idx; |
| if (to_master) { |
| idx = 0; |
| } else { |
| idx = 1; |
| } |
| |
| if (!is_initialized_[idx]) { |
| WEBRTC_TRACE(webrtc::kTraceError, webrtc::kTraceAudioCoding, id_, |
| "ACMNetEQ::AddCodec: NetEq is not initialized."); |
| return -1; |
| } |
| if (WebRtcNetEQ_CodecDbAdd(inst_[idx], codec_def) < 0) { |
| LogError("CodecDB_Add", idx); |
| WEBRTC_TRACE(webrtc::kTraceError, webrtc::kTraceAudioCoding, id_, |
| "ACMNetEQ::AddCodec: NetEq, error in adding codec"); |
| return -1; |
| } else { |
| return 0; |
| } |
| } |
| |
| // Creates a Word16 RTP packet out of a Word8 payload and an rtp info struct. |
| // Must be byte order safe. |
| void ACMNetEQ::RTPPack(int16_t* rtp_packet, const int8_t* payload, |
| const int32_t payload_length_bytes, |
| const WebRtcRTPHeader& rtp_info) { |
| int32_t idx = 0; |
| WEBRTC_SPL_SET_BYTE(rtp_packet, (int8_t) 0x80, idx); |
| idx++; |
| WEBRTC_SPL_SET_BYTE(rtp_packet, rtp_info.header.payloadType, idx); |
| idx++; |
| WEBRTC_SPL_SET_BYTE(rtp_packet, |
| WEBRTC_SPL_GET_BYTE(&(rtp_info.header.sequenceNumber), 1), |
| idx); |
| idx++; |
| WEBRTC_SPL_SET_BYTE(rtp_packet, |
| WEBRTC_SPL_GET_BYTE(&(rtp_info.header.sequenceNumber), 0), |
| idx); |
| idx++; |
| WEBRTC_SPL_SET_BYTE(rtp_packet, |
| WEBRTC_SPL_GET_BYTE(&(rtp_info.header.timestamp), 3), |
| idx); |
| idx++; |
| WEBRTC_SPL_SET_BYTE(rtp_packet, |
| WEBRTC_SPL_GET_BYTE(&(rtp_info.header.timestamp), 2), |
| idx); |
| idx++; |
| WEBRTC_SPL_SET_BYTE(rtp_packet, |
| WEBRTC_SPL_GET_BYTE(&(rtp_info.header.timestamp), 1), |
| idx); |
| idx++; |
| WEBRTC_SPL_SET_BYTE(rtp_packet, |
| WEBRTC_SPL_GET_BYTE(&(rtp_info.header.timestamp), 0), |
| idx); |
| idx++; |
| WEBRTC_SPL_SET_BYTE(rtp_packet, |
| WEBRTC_SPL_GET_BYTE(&(rtp_info.header.ssrc), 3), idx); |
| idx++; |
| WEBRTC_SPL_SET_BYTE(rtp_packet, WEBRTC_SPL_GET_BYTE(&(rtp_info.header.ssrc), |
| 2), idx); |
| idx++; |
| WEBRTC_SPL_SET_BYTE(rtp_packet, WEBRTC_SPL_GET_BYTE(&(rtp_info.header.ssrc), |
| 1), idx); |
| idx++; |
| WEBRTC_SPL_SET_BYTE(rtp_packet, WEBRTC_SPL_GET_BYTE(&(rtp_info.header.ssrc), |
| 0), idx); |
| idx++; |
| for (int16_t i = 0; i < payload_length_bytes; i++) { |
| WEBRTC_SPL_SET_BYTE(rtp_packet, payload[i], idx); |
| idx++; |
| } |
| if (payload_length_bytes & 1) { |
| // Our 16 bits buffer is one byte too large, set that |
| // last byte to zero. |
| WEBRTC_SPL_SET_BYTE(rtp_packet, 0x0, idx); |
| } |
| } |
| |
| int16_t ACMNetEQ::EnableVAD() { |
| CriticalSectionScoped lock(neteq_crit_sect_); |
| if (vad_status_) { |
| return 0; |
| } |
| for (int16_t idx = 0; idx < num_slaves_ + 1; idx++) { |
| if (!is_initialized_[idx]) { |
| WEBRTC_TRACE(webrtc::kTraceError, webrtc::kTraceAudioCoding, id_, |
| "SetVADStatus: NetEq is not initialized."); |
| return -1; |
| } |
| // VAD was off and we have to turn it on |
| if (EnableVADByIdxSafe(idx) < 0) { |
| return -1; |
| } |
| |
| // Set previous VAD status to PASSIVE |
| previous_audio_activity_ = AudioFrame::kVadPassive; |
| } |
| vad_status_ = true; |
| return 0; |
| } |
| |
| ACMVADMode ACMNetEQ::vad_mode() const { |
| CriticalSectionScoped lock(neteq_crit_sect_); |
| return vad_mode_; |
| } |
| |
| int16_t ACMNetEQ::SetVADMode(const ACMVADMode mode) { |
| CriticalSectionScoped lock(neteq_crit_sect_); |
| if ((mode < VADNormal) || (mode > VADVeryAggr)) { |
| WEBRTC_TRACE(webrtc::kTraceError, webrtc::kTraceAudioCoding, id_, |
| "SetVADMode: NetEq error: could not set VAD mode, mode is not " |
| "supported"); |
| return -1; |
| } else { |
| for (int16_t idx = 0; idx < num_slaves_ + 1; idx++) { |
| if (!is_initialized_[idx]) { |
| WEBRTC_TRACE(webrtc::kTraceError, webrtc::kTraceAudioCoding, id_, |
| "SetVADMode: NetEq is not initialized."); |
| return -1; |
| } |
| if (WebRtcNetEQ_SetVADMode(inst_[idx], mode) < 0) { |
| LogError("SetVADmode", idx); |
| return -1; |
| } |
| } |
| vad_mode_ = mode; |
| return 0; |
| } |
| } |
| |
| int32_t ACMNetEQ::FlushBuffers() { |
| CriticalSectionScoped lock(neteq_crit_sect_); |
| for (int16_t idx = 0; idx < num_slaves_ + 1; idx++) { |
| if (!is_initialized_[idx]) { |
| WEBRTC_TRACE(webrtc::kTraceError, webrtc::kTraceAudioCoding, id_, |
| "FlushBuffers: NetEq is not initialized."); |
| return -1; |
| } |
| if (WebRtcNetEQ_FlushBuffers(inst_[idx]) < 0) { |
| LogError("FlushBuffers", idx); |
| return -1; |
| } |
| } |
| return 0; |
| } |
| |
| int16_t ACMNetEQ::RemoveCodec(WebRtcNetEQDecoder codec_idx, |
| bool is_stereo) { |
| // sanity check |
| if ((codec_idx <= kDecoderReservedStart) || |
| (codec_idx >= kDecoderReservedEnd)) { |
| WEBRTC_TRACE(webrtc::kTraceError, webrtc::kTraceAudioCoding, id_, |
| "RemoveCodec: NetEq error: could not Remove Codec, codec " |
| "index out of range"); |
| return -1; |
| } |
| CriticalSectionScoped lock(neteq_crit_sect_); |
| if (!is_initialized_[0]) { |
| WEBRTC_TRACE(webrtc::kTraceError, webrtc::kTraceAudioCoding, id_, |
| "RemoveCodec: NetEq is not initialized."); |
| return -1; |
| } |
| |
| if (WebRtcNetEQ_CodecDbRemove(inst_[0], codec_idx) < 0) { |
| LogError("CodecDB_Remove", 0); |
| return -1; |
| } |
| |
| if (is_stereo) { |
| if (WebRtcNetEQ_CodecDbRemove(inst_[1], codec_idx) < 0) { |
| LogError("CodecDB_Remove", 1); |
| return -1; |
| } |
| } |
| |
| return 0; |
| } |
| |
| int16_t ACMNetEQ::SetBackgroundNoiseMode( |
| const ACMBackgroundNoiseMode mode) { |
| CriticalSectionScoped lock(neteq_crit_sect_); |
| for (int16_t idx = 0; idx < num_slaves_ + 1; idx++) { |
| if (!is_initialized_[idx]) { |
| WEBRTC_TRACE(webrtc::kTraceError, webrtc::kTraceAudioCoding, id_, |
| "SetBackgroundNoiseMode: NetEq is not initialized."); |
| return -1; |
| } |
| if (WebRtcNetEQ_SetBGNMode(inst_[idx], (WebRtcNetEQBGNMode) mode) < 0) { |
| LogError("SetBGNMode", idx); |
| return -1; |
| } |
| } |
| return 0; |
| } |
| |
| int16_t ACMNetEQ::BackgroundNoiseMode(ACMBackgroundNoiseMode& mode) { |
| WebRtcNetEQBGNMode my_mode; |
| CriticalSectionScoped lock(neteq_crit_sect_); |
| if (!is_initialized_[0]) { |
| WEBRTC_TRACE(webrtc::kTraceError, webrtc::kTraceAudioCoding, id_, |
| "BackgroundNoiseMode: NetEq is not initialized."); |
| return -1; |
| } |
| if (WebRtcNetEQ_GetBGNMode(inst_[0], &my_mode) < 0) { |
| LogError("WebRtcNetEQ_GetBGNMode", 0); |
| return -1; |
| } else { |
| mode = (ACMBackgroundNoiseMode) my_mode; |
| } |
| return 0; |
| } |
| |
| void ACMNetEQ::set_id(int32_t id) { |
| CriticalSectionScoped lock(neteq_crit_sect_); |
| id_ = id; |
| } |
| |
| void ACMNetEQ::LogError(const char* neteq_func_name, |
| const int16_t idx) const { |
| char error_name[NETEQ_ERR_MSG_LEN_BYTE]; |
| char my_func_name[50]; |
| int neteq_error_code = WebRtcNetEQ_GetErrorCode(inst_[idx]); |
| WebRtcNetEQ_GetErrorName(neteq_error_code, error_name, |
| NETEQ_ERR_MSG_LEN_BYTE - 1); |
| strncpy(my_func_name, neteq_func_name, 49); |
| error_name[NETEQ_ERR_MSG_LEN_BYTE - 1] = '\0'; |
| my_func_name[49] = '\0'; |
| WEBRTC_TRACE(webrtc::kTraceError, webrtc::kTraceAudioCoding, id_, |
| "NetEq-%d Error in function %s, error-code: %d, error-string: " |
| " %s", idx, my_func_name, neteq_error_code, error_name); |
| } |
| |
| int32_t ACMNetEQ::PlayoutTimestamp(uint32_t& timestamp) { |
| CriticalSectionScoped lock(neteq_crit_sect_); |
| if (WebRtcNetEQ_GetSpeechTimeStamp(inst_[0], ×tamp) < 0) { |
| LogError("GetSpeechTimeStamp", 0); |
| return -1; |
| } else { |
| return 0; |
| } |
| } |
| |
| void ACMNetEQ::RemoveSlaves() { |
| CriticalSectionScoped lock(neteq_crit_sect_); |
| RemoveSlavesSafe(); |
| } |
| |
| void ACMNetEQ::RemoveSlavesSafe() { |
| for (int i = 1; i < num_slaves_ + 1; i++) { |
| RemoveNetEQSafe(i); |
| } |
| |
| if (master_slave_info_ != NULL) { |
| free(master_slave_info_); |
| master_slave_info_ = NULL; |
| } |
| num_slaves_ = 0; |
| } |
| |
| void ACMNetEQ::RemoveNetEQSafe(int index) { |
| if (inst_mem_[index] != NULL) { |
| free(inst_mem_[index]); |
| inst_mem_[index] = NULL; |
| } |
| if (neteq_packet_buffer_[index] != NULL) { |
| free(neteq_packet_buffer_[index]); |
| neteq_packet_buffer_[index] = NULL; |
| } |
| if (ptr_vadinst_[index] != NULL) { |
| WebRtcVad_Free(ptr_vadinst_[index]); |
| ptr_vadinst_[index] = NULL; |
| } |
| } |
| |
| int16_t ACMNetEQ::AddSlave(const WebRtcNetEQDecoder* used_codecs, |
| int16_t num_codecs) { |
| CriticalSectionScoped lock(neteq_crit_sect_); |
| const int16_t slave_idx = 1; |
| if (num_slaves_ < 1) { |
| // initialize the receiver, this also sets up VAD. |
| if (InitByIdxSafe(slave_idx) < 0) { |
| WEBRTC_TRACE(webrtc::kTraceError, webrtc::kTraceAudioCoding, id_, |
| "AddSlave: AddSlave Failed, Could not Initialize"); |
| return -1; |
| } |
| |
| // Allocate buffer. |
| if (AllocatePacketBufferByIdxSafe(used_codecs, num_codecs, |
| slave_idx) < 0) { |
| WEBRTC_TRACE(webrtc::kTraceError, webrtc::kTraceAudioCoding, id_, |
| "AddSlave: AddSlave Failed, Could not Allocate Packet " |
| "Buffer"); |
| return -1; |
| } |
| |
| if (master_slave_info_ != NULL) { |
| free(master_slave_info_); |
| master_slave_info_ = NULL; |
| } |
| int ms_info_size = WebRtcNetEQ_GetMasterSlaveInfoSize(); |
| master_slave_info_ = malloc(ms_info_size); |
| |
| if (master_slave_info_ == NULL) { |
| WEBRTC_TRACE(webrtc::kTraceError, webrtc::kTraceAudioCoding, id_, |
| "AddSlave: AddSlave Failed, Could not Allocate memory for " |
| "Master-Slave Info"); |
| return -1; |
| } |
| |
| // We accept this as initialized NetEQ, the rest is to synchronize |
| // Slave with Master. |
| num_slaves_ = 1; |
| is_initialized_[slave_idx] = true; |
| |
| // Set AVT |
| if (WebRtcNetEQ_SetAVTPlayout(inst_[slave_idx], |
| (avt_playout_) ? 1 : 0) < 0) { |
| LogError("SetAVTPlayout", slave_idx); |
| WEBRTC_TRACE(webrtc::kTraceError, webrtc::kTraceAudioCoding, id_, |
| "AddSlave: AddSlave Failed, Could not set AVT playout."); |
| return -1; |
| } |
| |
| // Set Background Noise |
| WebRtcNetEQBGNMode current_mode; |
| if (WebRtcNetEQ_GetBGNMode(inst_[0], ¤t_mode) < 0) { |
| LogError("GetBGNMode", 0); |
| WEBRTC_TRACE(webrtc::kTraceError, webrtc::kTraceAudioCoding, id_, |
| "AAddSlave: AddSlave Failed, Could not Get BGN form " |
| "Master."); |
| return -1; |
| } |
| |
| if (WebRtcNetEQ_SetBGNMode(inst_[slave_idx], |
| (WebRtcNetEQBGNMode) current_mode) < 0) { |
| LogError("SetBGNMode", slave_idx); |
| WEBRTC_TRACE(webrtc::kTraceError, webrtc::kTraceAudioCoding, id_, |
| "AddSlave: AddSlave Failed, Could not set BGN mode."); |
| return -1; |
| } |
| |
| enum WebRtcNetEQPlayoutMode playout_mode = kPlayoutOff; |
| switch (playout_mode_) { |
| case voice: |
| playout_mode = kPlayoutOn; |
| break; |
| case fax: |
| playout_mode = kPlayoutFax; |
| break; |
| case streaming: |
| playout_mode = kPlayoutStreaming; |
| break; |
| case off: |
| playout_mode = kPlayoutOff; |
| break; |
| } |
| if (WebRtcNetEQ_SetPlayoutMode(inst_[slave_idx], playout_mode) < 0) { |
| LogError("SetPlayoutMode", 1); |
| WEBRTC_TRACE(webrtc::kTraceError, webrtc::kTraceAudioCoding, id_, |
| "AddSlave: AddSlave Failed, Could not Set Playout Mode."); |
| return -1; |
| } |
| |
| // Set AV-sync for the slave. |
| WebRtcNetEQ_EnableAVSync(inst_[slave_idx], av_sync_ ? 1 : 0); |
| |
| // Set minimum delay. |
| if (minimum_delay_ms_ > 0) |
| WebRtcNetEQ_SetMinimumDelay(inst_[slave_idx], minimum_delay_ms_); |
| |
| // Set maximum delay. |
| if (maximum_delay_ms_ > 0) |
| WebRtcNetEQ_SetMaximumDelay(inst_[slave_idx], maximum_delay_ms_); |
| } |
| |
| return 0; |
| } |
| |
| void ACMNetEQ::set_received_stereo(bool received_stereo) { |
| CriticalSectionScoped lock(neteq_crit_sect_); |
| received_stereo_ = received_stereo; |
| } |
| |
| uint8_t ACMNetEQ::num_slaves() { |
| CriticalSectionScoped lock(neteq_crit_sect_); |
| return num_slaves_; |
| } |
| |
| void ACMNetEQ::EnableAVSync(bool enable) { |
| CriticalSectionScoped lock(neteq_crit_sect_); |
| av_sync_ = enable; |
| for (int i = 0; i < num_slaves_ + 1; ++i) { |
| assert(is_initialized_[i]); |
| WebRtcNetEQ_EnableAVSync(inst_[i], enable ? 1 : 0); |
| } |
| } |
| |
| int ACMNetEQ::SetMinimumDelay(int minimum_delay_ms) { |
| CriticalSectionScoped lock(neteq_crit_sect_); |
| for (int i = 0; i < num_slaves_ + 1; ++i) { |
| assert(is_initialized_[i]); |
| if (WebRtcNetEQ_SetMinimumDelay(inst_[i], minimum_delay_ms) < 0) |
| return -1; |
| } |
| minimum_delay_ms_ = minimum_delay_ms; |
| return 0; |
| } |
| |
| int ACMNetEQ::SetMaximumDelay(int maximum_delay_ms) { |
| CriticalSectionScoped lock(neteq_crit_sect_); |
| for (int i = 0; i < num_slaves_ + 1; ++i) { |
| assert(is_initialized_[i]); |
| if (WebRtcNetEQ_SetMaximumDelay(inst_[i], maximum_delay_ms) < 0) |
| return -1; |
| } |
| maximum_delay_ms_ = maximum_delay_ms; |
| return 0; |
| } |
| |
| int ACMNetEQ::LeastRequiredDelayMs() const { |
| CriticalSectionScoped lock(neteq_crit_sect_); |
| assert(is_initialized_[0]); |
| |
| // Sufficient to query the master. |
| return WebRtcNetEQ_GetRequiredDelayMs(inst_[0]); |
| } |
| |
| bool ACMNetEQ::DecodedRtpInfo(int* sequence_number, uint32_t* timestamp) const { |
| CriticalSectionScoped lock(neteq_crit_sect_); |
| if (WebRtcNetEQ_DecodedRtpInfo(inst_[0], sequence_number, timestamp) < 0) |
| return false; |
| return true; |
| } |
| |
| } // namespace acm1 |
| |
| } // namespace webrtc |