Remove use of AcmReceiver in ChannelReceive

ChannelReceive is now owning and interfacing with NetEq directly.
A new ResamplerHelper is added to acm_resampler.cc/.h, to do the
audio resampling that was previously done inside AcmReceiver.

AcmReceiver still remains, since it is used in other places for now.

Bug: webrtc:14867
Change-Id: If3eb6415e06b9b5e729d393713f3fccb31b0570f
Reviewed-on: https://webrtc-review.googlesource.com/c/src/+/361820
Auto-Submit: Henrik Lundin <henrik.lundin@webrtc.org>
Reviewed-by: Jakob Ivarsson‎ <jakobi@webrtc.org>
Commit-Queue: Henrik Lundin <henrik.lundin@webrtc.org>
Cr-Commit-Position: refs/heads/main@{#42974}
This commit is contained in:
Henrik Lundin 2024-09-06 11:53:14 +00:00 committed by WebRTC LUCI CQ
parent 3ad2c8d717
commit c9aaf11985
6 changed files with 230 additions and 117 deletions

View File

@ -81,6 +81,7 @@ rtc_library("audio") {
"../modules/audio_coding:audio_coding_module_typedefs", "../modules/audio_coding:audio_coding_module_typedefs",
"../modules/audio_coding:audio_encoder_cng", "../modules/audio_coding:audio_encoder_cng",
"../modules/audio_coding:audio_network_adaptor_config", "../modules/audio_coding:audio_network_adaptor_config",
"../modules/audio_coding:default_neteq_factory",
"../modules/audio_coding:red", "../modules/audio_coding:red",
"../modules/audio_device", "../modules/audio_device",
"../modules/audio_processing", "../modules/audio_processing",

View File

@ -20,6 +20,7 @@
#include "api/audio/audio_device.h" #include "api/audio/audio_device.h"
#include "api/crypto/frame_decryptor_interface.h" #include "api/crypto/frame_decryptor_interface.h"
#include "api/frame_transformer_interface.h" #include "api/frame_transformer_interface.h"
#include "api/neteq/neteq.h"
#include "api/rtc_event_log/rtc_event_log.h" #include "api/rtc_event_log/rtc_event_log.h"
#include "api/sequence_checker.h" #include "api/sequence_checker.h"
#include "api/task_queue/pending_task_safety_flag.h" #include "api/task_queue/pending_task_safety_flag.h"
@ -32,8 +33,10 @@
#include "audio/utility/audio_frame_operations.h" #include "audio/utility/audio_frame_operations.h"
#include "logging/rtc_event_log/events/rtc_event_audio_playout.h" #include "logging/rtc_event_log/events/rtc_event_audio_playout.h"
#include "logging/rtc_event_log/events/rtc_event_neteq_set_minimum_delay.h" #include "logging/rtc_event_log/events/rtc_event_neteq_set_minimum_delay.h"
#include "modules/audio_coding/acm2/acm_receiver.h" #include "modules/audio_coding/acm2/acm_resampler.h"
#include "modules/audio_coding/acm2/call_statistics.h"
#include "modules/audio_coding/audio_network_adaptor/include/audio_network_adaptor_config.h" #include "modules/audio_coding/audio_network_adaptor/include/audio_network_adaptor_config.h"
#include "modules/audio_coding/neteq/default_neteq_factory.h"
#include "modules/pacing/packet_router.h" #include "modules/pacing/packet_router.h"
#include "modules/rtp_rtcp/include/receive_statistics.h" #include "modules/rtp_rtcp/include/receive_statistics.h"
#include "modules/rtp_rtcp/include/remote_ntp_time_estimator.h" #include "modules/rtp_rtcp/include/remote_ntp_time_estimator.h"
@ -67,23 +70,24 @@ constexpr double kAudioSampleDurationSeconds = 0.01;
constexpr int kVoiceEngineMinMinPlayoutDelayMs = 0; constexpr int kVoiceEngineMinMinPlayoutDelayMs = 0;
constexpr int kVoiceEngineMaxMinPlayoutDelayMs = 10000; constexpr int kVoiceEngineMaxMinPlayoutDelayMs = 10000;
acm2::AcmReceiver::Config AcmConfig( std::unique_ptr<NetEq> CreateNetEq(
NetEqFactory* neteq_factory, NetEqFactory* neteq_factory,
rtc::scoped_refptr<AudioDecoderFactory> decoder_factory,
std::optional<AudioCodecPairId> codec_pair_id, std::optional<AudioCodecPairId> codec_pair_id,
size_t jitter_buffer_max_packets, size_t jitter_buffer_max_packets,
bool jitter_buffer_fast_playout, bool jitter_buffer_fast_playout,
int jitter_buffer_min_delay_ms) { int jitter_buffer_min_delay_ms,
acm2::AcmReceiver::Config acm_config; const Environment& env,
acm_config.neteq_factory = neteq_factory; scoped_refptr<AudioDecoderFactory> decoder_factory) {
acm_config.decoder_factory = decoder_factory; NetEq::Config config;
acm_config.neteq_config.codec_pair_id = codec_pair_id; config.codec_pair_id = codec_pair_id;
acm_config.neteq_config.max_packets_in_buffer = jitter_buffer_max_packets; config.max_packets_in_buffer = jitter_buffer_max_packets;
acm_config.neteq_config.enable_fast_accelerate = jitter_buffer_fast_playout; config.enable_fast_accelerate = jitter_buffer_fast_playout;
acm_config.neteq_config.enable_muted_state = true; config.enable_muted_state = true;
acm_config.neteq_config.min_delay_ms = jitter_buffer_min_delay_ms; config.min_delay_ms = jitter_buffer_min_delay_ms;
if (neteq_factory) {
return acm_config; return neteq_factory->Create(env, config, std::move(decoder_factory));
}
return DefaultNetEqFactory().Create(env, config, std::move(decoder_factory));
} }
class ChannelReceive : public ChannelReceiveInterface, class ChannelReceive : public ChannelReceiveInterface,
@ -231,6 +235,7 @@ class ChannelReceive : public ChannelReceiveInterface,
rtc::RaceChecker audio_thread_race_checker_; rtc::RaceChecker audio_thread_race_checker_;
Mutex callback_mutex_; Mutex callback_mutex_;
Mutex volume_settings_mutex_; Mutex volume_settings_mutex_;
mutable Mutex call_stats_mutex_;
bool playing_ RTC_GUARDED_BY(worker_thread_checker_) = false; bool playing_ RTC_GUARDED_BY(worker_thread_checker_) = false;
@ -249,8 +254,10 @@ class ChannelReceive : public ChannelReceiveInterface,
std::optional<int64_t> last_received_rtp_system_time_ms_ std::optional<int64_t> last_received_rtp_system_time_ms_
RTC_GUARDED_BY(&worker_thread_checker_); RTC_GUARDED_BY(&worker_thread_checker_);
// The AcmReceiver is thread safe, using its own lock. const std::unique_ptr<NetEq> neteq_; // NetEq is thread-safe; no lock needed.
acm2::AcmReceiver acm_receiver_; acm2::ResamplerHelper resampler_helper_
RTC_GUARDED_BY(audio_thread_race_checker_);
acm2::CallStatistics call_stats_ RTC_GUARDED_BY(call_stats_mutex_);
AudioSinkInterface* audio_sink_ = nullptr; AudioSinkInterface* audio_sink_ = nullptr;
AudioLevel _outputAudioLevel; AudioLevel _outputAudioLevel;
@ -340,17 +347,19 @@ void ChannelReceive::OnReceivedPayloadData(
return; return;
} }
// Push the incoming payload (parsed and ready for decoding) into the ACM // Push the incoming payload (parsed and ready for decoding) into NetEq.
if (acm_receiver_.InsertPacket(rtpHeader, payload, receive_time) != 0) { if (payload.empty()) {
neteq_->InsertEmptyPacket(rtpHeader);
} else if (neteq_->InsertPacket(rtpHeader, payload, receive_time) < 0) {
RTC_DLOG(LS_ERROR) << "ChannelReceive::OnReceivedPayloadData() unable to " RTC_DLOG(LS_ERROR) << "ChannelReceive::OnReceivedPayloadData() unable to "
"push data to the ACM"; "insert packet into NetEq; PT = "
<< static_cast<int>(rtpHeader.payloadType);
return; return;
} }
TimeDelta round_trip_time = rtp_rtcp_->LastRtt().value_or(TimeDelta::Zero()); TimeDelta round_trip_time = rtp_rtcp_->LastRtt().value_or(TimeDelta::Zero());
std::vector<uint16_t> nack_list = std::vector<uint16_t> nack_list = neteq_->GetNackList(round_trip_time.ms());
acm_receiver_.GetNackList(round_trip_time.ms());
if (!nack_list.empty()) { if (!nack_list.empty()) {
// Can't use nack_list.data() since it's not supported by all // Can't use nack_list.data() since it's not supported by all
// compilers. // compilers.
@ -390,8 +399,8 @@ AudioMixer::Source::AudioFrameInfo ChannelReceive::GetAudioFrameWithInfo(
env_.event_log().Log(std::make_unique<RtcEventAudioPlayout>(remote_ssrc_)); env_.event_log().Log(std::make_unique<RtcEventAudioPlayout>(remote_ssrc_));
// Get 10ms raw PCM data from the ACM (mixer limits output frequency) if ((neteq_->GetAudio(audio_frame) != NetEq::kOK) ||
if (acm_receiver_.GetAudio(audio_frame->sample_rate_hz_, audio_frame) == -1) { !resampler_helper_.MaybeResample(sample_rate_hz, audio_frame)) {
RTC_DLOG(LS_ERROR) RTC_DLOG(LS_ERROR)
<< "ChannelReceive::GetAudioFrame() PlayoutData10Ms() failed!"; << "ChannelReceive::GetAudioFrame() PlayoutData10Ms() failed!";
// In all likelihood, the audio in this frame is garbage. We return an // In all likelihood, the audio in this frame is garbage. We return an
@ -404,6 +413,11 @@ AudioMixer::Source::AudioFrameInfo ChannelReceive::GetAudioFrameWithInfo(
return AudioMixer::Source::AudioFrameInfo::kError; return AudioMixer::Source::AudioFrameInfo::kError;
} }
{
MutexLock lock(&call_stats_mutex_);
call_stats_.DecodedByNetEq(audio_frame->speech_type_, audio_frame->muted());
}
{ {
// Pass the audio buffers to an optional sink callback, before applying // Pass the audio buffers to an optional sink callback, before applying
// scaling/panning, as that applies to the mix operation. // scaling/panning, as that applies to the mix operation.
@ -497,8 +511,8 @@ AudioMixer::Source::AudioFrameInfo ChannelReceive::GetAudioFrameWithInfo(
worker_thread_->PostTask(SafeTask(worker_safety_.flag(), [this]() { worker_thread_->PostTask(SafeTask(worker_safety_.flag(), [this]() {
RTC_DCHECK_RUN_ON(&worker_thread_checker_); RTC_DCHECK_RUN_ON(&worker_thread_checker_);
RTC_HISTOGRAM_COUNTS_1000("WebRTC.Audio.TargetJitterBufferDelayMs", RTC_HISTOGRAM_COUNTS_1000("WebRTC.Audio.TargetJitterBufferDelayMs",
acm_receiver_.TargetDelayMs()); neteq_->TargetDelayMs());
const int jitter_buffer_delay = acm_receiver_.FilteredCurrentDelayMs(); const int jitter_buffer_delay = neteq_->FilteredCurrentDelayMs();
RTC_HISTOGRAM_COUNTS_1000("WebRTC.Audio.ReceiverDelayEstimateMs", RTC_HISTOGRAM_COUNTS_1000("WebRTC.Audio.ReceiverDelayEstimateMs",
jitter_buffer_delay + playout_delay_ms_); jitter_buffer_delay + playout_delay_ms_);
RTC_HISTOGRAM_COUNTS_1000("WebRTC.Audio.ReceiverJitterBufferDelayMs", RTC_HISTOGRAM_COUNTS_1000("WebRTC.Audio.ReceiverJitterBufferDelayMs",
@ -516,9 +530,12 @@ AudioMixer::Source::AudioFrameInfo ChannelReceive::GetAudioFrameWithInfo(
int ChannelReceive::PreferredSampleRate() const { int ChannelReceive::PreferredSampleRate() const {
RTC_DCHECK_RUNS_SERIALIZED(&audio_thread_race_checker_); RTC_DCHECK_RUNS_SERIALIZED(&audio_thread_race_checker_);
const std::optional<NetEq::DecoderFormat> decoder =
neteq_->GetCurrentDecoderFormat();
const int last_packet_sample_rate_hz = decoder ? decoder->sample_rate_hz : 0;
// Return the bigger of playout and receive frequency in the ACM. // Return the bigger of playout and receive frequency in the ACM.
return std::max(acm_receiver_.last_packet_sample_rate_hz().value_or(0), return std::max(last_packet_sample_rate_hz,
acm_receiver_.last_output_sample_rate_hz()); neteq_->last_output_sample_rate_hz());
} }
ChannelReceive::ChannelReceive( ChannelReceive::ChannelReceive(
@ -542,13 +559,13 @@ ChannelReceive::ChannelReceive(
rtp_receive_statistics_(ReceiveStatistics::Create(&env_.clock())), rtp_receive_statistics_(ReceiveStatistics::Create(&env_.clock())),
remote_ssrc_(remote_ssrc), remote_ssrc_(remote_ssrc),
source_tracker_(&env_.clock()), source_tracker_(&env_.clock()),
acm_receiver_(env_, neteq_(CreateNetEq(neteq_factory,
AcmConfig(neteq_factory, codec_pair_id,
decoder_factory, jitter_buffer_max_packets,
codec_pair_id, jitter_buffer_fast_playout,
jitter_buffer_max_packets, jitter_buffer_min_delay_ms,
jitter_buffer_fast_playout, env_,
jitter_buffer_min_delay_ms)), decoder_factory)),
_outputAudioLevel(), _outputAudioLevel(),
ntp_estimator_(&env_.clock()), ntp_estimator_(&env_.clock()),
playout_timestamp_rtp_(0), playout_timestamp_rtp_(0),
@ -610,13 +627,18 @@ void ChannelReceive::StopPlayout() {
RTC_DCHECK_RUN_ON(&worker_thread_checker_); RTC_DCHECK_RUN_ON(&worker_thread_checker_);
playing_ = false; playing_ = false;
_outputAudioLevel.ResetLevelFullRange(); _outputAudioLevel.ResetLevelFullRange();
acm_receiver_.FlushBuffers(); neteq_->FlushBuffers();
} }
std::optional<std::pair<int, SdpAudioFormat>> ChannelReceive::GetReceiveCodec() std::optional<std::pair<int, SdpAudioFormat>> ChannelReceive::GetReceiveCodec()
const { const {
RTC_DCHECK_RUN_ON(&worker_thread_checker_); RTC_DCHECK_RUN_ON(&worker_thread_checker_);
return acm_receiver_.LastDecoder(); std::optional<NetEq::DecoderFormat> decoder =
neteq_->GetCurrentDecoderFormat();
if (!decoder) {
return std::nullopt;
}
return std::make_pair(decoder->payload_type, decoder->sdp_format);
} }
void ChannelReceive::SetReceiveCodecs( void ChannelReceive::SetReceiveCodecs(
@ -627,7 +649,7 @@ void ChannelReceive::SetReceiveCodecs(
payload_type_frequencies_[kv.first] = kv.second.clockrate_hz; payload_type_frequencies_[kv.first] = kv.second.clockrate_hz;
} }
payload_type_map_ = codecs; payload_type_map_ = codecs;
acm_receiver_.SetCodecs(codecs); neteq_->SetCodecs(codecs);
} }
void ChannelReceive::OnRtpPacket(const RtpPacketReceived& packet) { void ChannelReceive::OnRtpPacket(const RtpPacketReceived& packet) {
@ -877,11 +899,11 @@ void ChannelReceive::SetNACKStatus(bool enable, int max_packets) {
// None of these functions can fail. // None of these functions can fail.
if (enable) { if (enable) {
rtp_receive_statistics_->SetMaxReorderingThreshold(max_packets); rtp_receive_statistics_->SetMaxReorderingThreshold(max_packets);
acm_receiver_.EnableNack(max_packets); neteq_->EnableNack(max_packets);
} else { } else {
rtp_receive_statistics_->SetMaxReorderingThreshold( rtp_receive_statistics_->SetMaxReorderingThreshold(
kDefaultMaxReorderingThreshold); kDefaultMaxReorderingThreshold);
acm_receiver_.DisableNack(); neteq_->DisableNack();
} }
} }
@ -959,22 +981,83 @@ uint32_t ChannelReceive::GetLocalSsrc() const {
NetworkStatistics ChannelReceive::GetNetworkStatistics( NetworkStatistics ChannelReceive::GetNetworkStatistics(
bool get_and_clear_legacy_stats) const { bool get_and_clear_legacy_stats) const {
RTC_DCHECK_RUN_ON(&worker_thread_checker_); RTC_DCHECK_RUN_ON(&worker_thread_checker_);
NetworkStatistics stats; NetworkStatistics acm_stat;
acm_receiver_.GetNetworkStatistics(&stats, get_and_clear_legacy_stats); NetEqNetworkStatistics neteq_stat;
return stats; if (get_and_clear_legacy_stats) {
// NetEq function always returns zero, so we don't check the return value.
neteq_->NetworkStatistics(&neteq_stat);
acm_stat.currentExpandRate = neteq_stat.expand_rate;
acm_stat.currentSpeechExpandRate = neteq_stat.speech_expand_rate;
acm_stat.currentPreemptiveRate = neteq_stat.preemptive_rate;
acm_stat.currentAccelerateRate = neteq_stat.accelerate_rate;
acm_stat.currentSecondaryDecodedRate = neteq_stat.secondary_decoded_rate;
acm_stat.currentSecondaryDiscardedRate =
neteq_stat.secondary_discarded_rate;
acm_stat.meanWaitingTimeMs = neteq_stat.mean_waiting_time_ms;
acm_stat.maxWaitingTimeMs = neteq_stat.max_waiting_time_ms;
} else {
neteq_stat = neteq_->CurrentNetworkStatistics();
acm_stat.currentExpandRate = 0;
acm_stat.currentSpeechExpandRate = 0;
acm_stat.currentPreemptiveRate = 0;
acm_stat.currentAccelerateRate = 0;
acm_stat.currentSecondaryDecodedRate = 0;
acm_stat.currentSecondaryDiscardedRate = 0;
acm_stat.meanWaitingTimeMs = -1;
acm_stat.maxWaitingTimeMs = 1;
}
acm_stat.currentBufferSize = neteq_stat.current_buffer_size_ms;
acm_stat.preferredBufferSize = neteq_stat.preferred_buffer_size_ms;
acm_stat.jitterPeaksFound = neteq_stat.jitter_peaks_found ? true : false;
NetEqLifetimeStatistics neteq_lifetime_stat = neteq_->GetLifetimeStatistics();
acm_stat.totalSamplesReceived = neteq_lifetime_stat.total_samples_received;
acm_stat.concealedSamples = neteq_lifetime_stat.concealed_samples;
acm_stat.silentConcealedSamples =
neteq_lifetime_stat.silent_concealed_samples;
acm_stat.concealmentEvents = neteq_lifetime_stat.concealment_events;
acm_stat.jitterBufferDelayMs = neteq_lifetime_stat.jitter_buffer_delay_ms;
acm_stat.jitterBufferTargetDelayMs =
neteq_lifetime_stat.jitter_buffer_target_delay_ms;
acm_stat.jitterBufferMinimumDelayMs =
neteq_lifetime_stat.jitter_buffer_minimum_delay_ms;
acm_stat.jitterBufferEmittedCount =
neteq_lifetime_stat.jitter_buffer_emitted_count;
acm_stat.delayedPacketOutageSamples =
neteq_lifetime_stat.delayed_packet_outage_samples;
acm_stat.relativePacketArrivalDelayMs =
neteq_lifetime_stat.relative_packet_arrival_delay_ms;
acm_stat.interruptionCount = neteq_lifetime_stat.interruption_count;
acm_stat.totalInterruptionDurationMs =
neteq_lifetime_stat.total_interruption_duration_ms;
acm_stat.insertedSamplesForDeceleration =
neteq_lifetime_stat.inserted_samples_for_deceleration;
acm_stat.removedSamplesForAcceleration =
neteq_lifetime_stat.removed_samples_for_acceleration;
acm_stat.fecPacketsReceived = neteq_lifetime_stat.fec_packets_received;
acm_stat.fecPacketsDiscarded = neteq_lifetime_stat.fec_packets_discarded;
acm_stat.totalProcessingDelayUs =
neteq_lifetime_stat.total_processing_delay_us;
acm_stat.packetsDiscarded = neteq_lifetime_stat.packets_discarded;
NetEqOperationsAndState neteq_operations_and_state =
neteq_->GetOperationsAndState();
acm_stat.packetBufferFlushes =
neteq_operations_and_state.packet_buffer_flushes;
return acm_stat;
} }
AudioDecodingCallStats ChannelReceive::GetDecodingCallStatistics() const { AudioDecodingCallStats ChannelReceive::GetDecodingCallStatistics() const {
RTC_DCHECK_RUN_ON(&worker_thread_checker_); RTC_DCHECK_RUN_ON(&worker_thread_checker_);
AudioDecodingCallStats stats; MutexLock lock(&call_stats_mutex_);
acm_receiver_.GetDecodingCallStatistics(&stats); return call_stats_.GetDecodingStatistics();
return stats;
} }
uint32_t ChannelReceive::GetDelayEstimate() const { uint32_t ChannelReceive::GetDelayEstimate() const {
RTC_DCHECK_RUN_ON(&worker_thread_checker_); RTC_DCHECK_RUN_ON(&worker_thread_checker_);
// Return the current jitter buffer delay + playout delay. // Return the current jitter buffer delay + playout delay.
return acm_receiver_.FilteredCurrentDelayMs() + playout_delay_ms_; return neteq_->FilteredCurrentDelayMs() + playout_delay_ms_;
} }
bool ChannelReceive::SetMinimumPlayoutDelay(int delay_ms) { bool ChannelReceive::SetMinimumPlayoutDelay(int delay_ms) {
@ -986,9 +1069,10 @@ bool ChannelReceive::SetMinimumPlayoutDelay(int delay_ms) {
// close as possible, instead of failing. // close as possible, instead of failing.
delay_ms = rtc::SafeClamp(delay_ms, kVoiceEngineMinMinPlayoutDelayMs, delay_ms = rtc::SafeClamp(delay_ms, kVoiceEngineMinMinPlayoutDelayMs,
kVoiceEngineMaxMinPlayoutDelayMs); kVoiceEngineMaxMinPlayoutDelayMs);
if (acm_receiver_.SetMinimumDelay(delay_ms) != 0) { if (!neteq_->SetMinimumDelay(delay_ms)) {
RTC_DLOG(LS_ERROR) RTC_DLOG(LS_ERROR)
<< "SetMinimumPlayoutDelay() failed to set min playout delay"; << "SetMinimumPlayoutDelay() failed to set min playout delay "
<< delay_ms;
return false; return false;
} }
return true; return true;
@ -1024,11 +1108,11 @@ std::optional<int64_t> ChannelReceive::GetCurrentEstimatedPlayoutNtpTimestampMs(
bool ChannelReceive::SetBaseMinimumPlayoutDelayMs(int delay_ms) { bool ChannelReceive::SetBaseMinimumPlayoutDelayMs(int delay_ms) {
env_.event_log().Log( env_.event_log().Log(
std::make_unique<RtcEventNetEqSetMinimumDelay>(remote_ssrc_, delay_ms)); std::make_unique<RtcEventNetEqSetMinimumDelay>(remote_ssrc_, delay_ms));
return acm_receiver_.SetBaseMinimumDelayMs(delay_ms); return neteq_->SetBaseMinimumDelayMs(delay_ms);
} }
int ChannelReceive::GetBaseMinimumPlayoutDelayMs() const { int ChannelReceive::GetBaseMinimumPlayoutDelayMs() const {
return acm_receiver_.GetBaseMinimumDelayMs(); return neteq_->GetBaseMinimumDelayMs();
} }
std::optional<Syncable::Info> ChannelReceive::GetSyncInfo() const { std::optional<Syncable::Info> ChannelReceive::GetSyncInfo() const {
@ -1052,7 +1136,7 @@ std::optional<Syncable::Info> ChannelReceive::GetSyncInfo() const {
info.latest_received_capture_timestamp = *last_received_rtp_timestamp_; info.latest_received_capture_timestamp = *last_received_rtp_timestamp_;
info.latest_receive_time_ms = *last_received_rtp_system_time_ms_; info.latest_receive_time_ms = *last_received_rtp_system_time_ms_;
int jitter_buffer_delay = acm_receiver_.FilteredCurrentDelayMs(); int jitter_buffer_delay = neteq_->FilteredCurrentDelayMs();
info.current_delay_ms = jitter_buffer_delay + playout_delay_ms_; info.current_delay_ms = jitter_buffer_delay + playout_delay_ms_;
return info; return info;
@ -1063,7 +1147,7 @@ void ChannelReceive::UpdatePlayoutTimestamp(bool rtcp, int64_t now_ms) {
// TODO(bugs.webrtc.org/11993): Expect to be called exclusively on the // TODO(bugs.webrtc.org/11993): Expect to be called exclusively on the
// network thread. Once that's done, we won't need video_sync_lock_. // network thread. Once that's done, we won't need video_sync_lock_.
jitter_buffer_playout_timestamp_ = acm_receiver_.GetPlayoutTimestamp(); jitter_buffer_playout_timestamp_ = neteq_->GetPlayoutTimestamp();
if (!jitter_buffer_playout_timestamp_) { if (!jitter_buffer_playout_timestamp_) {
// This can happen if this channel has not received any RTP packets. In // This can happen if this channel has not received any RTP packets. In
@ -1093,17 +1177,18 @@ void ChannelReceive::UpdatePlayoutTimestamp(bool rtcp, int64_t now_ms) {
} }
int ChannelReceive::GetRtpTimestampRateHz() const { int ChannelReceive::GetRtpTimestampRateHz() const {
const auto decoder = acm_receiver_.LastDecoder(); const auto decoder_format = neteq_->GetCurrentDecoderFormat();
// Default to the playout frequency if we've not gotten any packets yet. // Default to the playout frequency if we've not gotten any packets yet.
// TODO(ossu): Zero clockrate can only happen if we've added an external // TODO(ossu): Zero clockrate can only happen if we've added an external
// decoder for a format we don't support internally. Remove once that way of // decoder for a format we don't support internally. Remove once that way of
// adding decoders is gone! // adding decoders is gone!
// TODO(kwiberg): `decoder->second.clockrate_hz` is an RTP clockrate as it // TODO(kwiberg): `decoder_format->sdp_format.clockrate_hz` is an RTP
// should, but `acm_receiver_.last_output_sample_rate_hz()` is a codec sample // clockrate as it should, but `neteq_->last_output_sample_rate_hz()` is a
// rate, which is not always the same thing. // codec sample rate, which is not always the same thing.
return (decoder && decoder->second.clockrate_hz != 0) return (decoder_format && decoder_format->sdp_format.clockrate_hz != 0)
? decoder->second.clockrate_hz ? decoder_format->sdp_format.clockrate_hz
: acm_receiver_.last_output_sample_rate_hz(); : neteq_->last_output_sample_rate_hz();
} }
std::vector<RtpSource> ChannelReceive::GetSources() const { std::vector<RtpSource> ChannelReceive::GetSources() const {

View File

@ -61,10 +61,7 @@ AcmReceiver::AcmReceiver(const Environment& env, Config config)
neteq_(CreateNetEq(config.neteq_factory, neteq_(CreateNetEq(config.neteq_factory,
config.neteq_config, config.neteq_config,
env_, env_,
std::move(config.decoder_factory))), std::move(config.decoder_factory))) {}
resampled_last_output_frame_(true) {
ClearSamples(last_audio_buffer_);
}
AcmReceiver::~AcmReceiver() = default; AcmReceiver::~AcmReceiver() = default;
@ -128,60 +125,13 @@ int AcmReceiver::GetAudio(int desired_freq_hz,
RTC_LOG(LS_ERROR) << "AcmReceiver::GetAudio - NetEq Failed."; RTC_LOG(LS_ERROR) << "AcmReceiver::GetAudio - NetEq Failed.";
return -1; return -1;
} }
RTC_DCHECK_EQ(audio_frame->sample_rate_hz_, current_sample_rate_hz);
RTC_DCHECK_NE(current_sample_rate_hz, 0);
// Update if resampling is required.
const bool need_resampling =
(desired_freq_hz != -1) && (current_sample_rate_hz != desired_freq_hz);
// Accessing members, take the lock. // Accessing members, take the lock.
MutexLock lock(&mutex_); MutexLock lock(&mutex_);
if (need_resampling && !resampled_last_output_frame_) { if (!resampler_helper_.MaybeResample(desired_freq_hz, audio_frame)) {
// Prime the resampler with the last frame. return -1;
int16_t temp_output[AudioFrame::kMaxDataSizeSamples];
int samples_per_channel_int = resampler_.Resample10Msec(
last_audio_buffer_.data(), current_sample_rate_hz, desired_freq_hz,
audio_frame->num_channels_, AudioFrame::kMaxDataSizeSamples,
temp_output);
if (samples_per_channel_int < 0) {
RTC_LOG(LS_ERROR) << "AcmReceiver::GetAudio - "
"Resampling last_audio_buffer_ failed.";
return -1;
}
} }
// TODO(bugs.webrtc.org/3923) Glitches in the output may appear if the output
// rate from NetEq changes.
if (need_resampling) {
// TODO(yujo): handle this more efficiently for muted frames.
int samples_per_channel_int = resampler_.Resample10Msec(
audio_frame->data(), current_sample_rate_hz, desired_freq_hz,
audio_frame->num_channels_, AudioFrame::kMaxDataSizeSamples,
audio_frame->mutable_data());
if (samples_per_channel_int < 0) {
RTC_LOG(LS_ERROR)
<< "AcmReceiver::GetAudio - Resampling audio_buffer_ failed.";
return -1;
}
audio_frame->samples_per_channel_ =
static_cast<size_t>(samples_per_channel_int);
audio_frame->sample_rate_hz_ = desired_freq_hz;
RTC_DCHECK_EQ(
audio_frame->sample_rate_hz_,
rtc::dchecked_cast<int>(audio_frame->samples_per_channel_ * 100));
resampled_last_output_frame_ = true;
} else {
resampled_last_output_frame_ = false;
// We might end up here ONLY if codec is changed.
}
// Store current audio in `last_audio_buffer_` for next time.
// TODO: b/335805780 - Use CopySamples().
memcpy(last_audio_buffer_.data(), audio_frame->data(),
sizeof(int16_t) * audio_frame->samples_per_channel_ *
audio_frame->num_channels_);
call_stats_.DecodedByNetEq(audio_frame->speech_type_, audio_frame->muted()); call_stats_.DecodedByNetEq(audio_frame->speech_type_, audio_frame->muted());
return 0; return 0;
} }

View File

@ -43,6 +43,7 @@ struct RTPHeader;
namespace acm2 { namespace acm2 {
// This class is deprecated. See https://issues.webrtc.org/issues/42225167.
class AcmReceiver { class AcmReceiver {
public: public:
struct Config { struct Config {
@ -223,12 +224,9 @@ class AcmReceiver {
const Environment env_; const Environment env_;
mutable Mutex mutex_; mutable Mutex mutex_;
ACMResampler resampler_ RTC_GUARDED_BY(mutex_);
CallStatistics call_stats_ RTC_GUARDED_BY(mutex_); CallStatistics call_stats_ RTC_GUARDED_BY(mutex_);
const std::unique_ptr<NetEq> neteq_; // NetEq is thread-safe; no lock needed. const std::unique_ptr<NetEq> neteq_; // NetEq is thread-safe; no lock needed.
bool resampled_last_output_frame_ RTC_GUARDED_BY(mutex_); ResamplerHelper resampler_helper_ RTC_GUARDED_BY(mutex_);
std::array<int16_t, AudioFrame::kMaxDataSizeSamples> last_audio_buffer_
RTC_GUARDED_BY(mutex_);
}; };
} // namespace acm2 } // namespace acm2

View File

@ -56,5 +56,67 @@ int ACMResampler::Resample10Msec(const int16_t* in_audio,
return static_cast<int>(dst.samples_per_channel()); return static_cast<int>(dst.samples_per_channel());
} }
ResamplerHelper::ResamplerHelper() {
ClearSamples(last_audio_buffer_);
}
bool ResamplerHelper::MaybeResample(int desired_sample_rate_hz,
AudioFrame* audio_frame) {
const int current_sample_rate_hz = audio_frame->sample_rate_hz_;
RTC_DCHECK_NE(current_sample_rate_hz, 0);
// Update if resampling is required.
const bool need_resampling =
(desired_sample_rate_hz != -1) &&
(current_sample_rate_hz != desired_sample_rate_hz);
if (need_resampling && !resampled_last_output_frame_) {
// Prime the resampler with the last frame.
int16_t temp_output[AudioFrame::kMaxDataSizeSamples];
int samples_per_channel_int = resampler_.Resample10Msec(
last_audio_buffer_.data(), current_sample_rate_hz,
desired_sample_rate_hz, audio_frame->num_channels_,
AudioFrame::kMaxDataSizeSamples, temp_output);
if (samples_per_channel_int < 0) {
RTC_LOG(LS_ERROR) << "AcmReceiver::GetAudio - "
"Resampling last_audio_buffer_ failed.";
return false;
}
}
// TODO(bugs.webrtc.org/3923) Glitches in the output may appear if the output
// rate from NetEq changes.
if (need_resampling) {
// TODO(yujo): handle this more efficiently for muted frames.
int samples_per_channel_int = resampler_.Resample10Msec(
audio_frame->data(), current_sample_rate_hz, desired_sample_rate_hz,
audio_frame->num_channels_, AudioFrame::kMaxDataSizeSamples,
audio_frame->mutable_data());
if (samples_per_channel_int < 0) {
RTC_LOG(LS_ERROR)
<< "AcmReceiver::GetAudio - Resampling audio_buffer_ failed.";
return false;
}
audio_frame->samples_per_channel_ =
static_cast<size_t>(samples_per_channel_int);
audio_frame->sample_rate_hz_ = desired_sample_rate_hz;
RTC_DCHECK_EQ(
audio_frame->sample_rate_hz_,
rtc::dchecked_cast<int>(audio_frame->samples_per_channel_ * 100));
resampled_last_output_frame_ = true;
} else {
resampled_last_output_frame_ = false;
// We might end up here ONLY if codec is changed.
}
// Store current audio in `last_audio_buffer_` for next time.
// TODO: b/335805780 - Use CopySamples().
memcpy(last_audio_buffer_.data(), audio_frame->data(),
sizeof(int16_t) * audio_frame->samples_per_channel_ *
audio_frame->num_channels_);
return true;
}
} // namespace acm2 } // namespace acm2
} // namespace webrtc } // namespace webrtc

View File

@ -14,6 +14,7 @@
#include <stddef.h> #include <stddef.h>
#include <stdint.h> #include <stdint.h>
#include "api/audio/audio_frame.h"
#include "common_audio/resampler/include/push_resampler.h" #include "common_audio/resampler/include/push_resampler.h"
namespace webrtc { namespace webrtc {
@ -36,6 +37,22 @@ class ACMResampler {
PushResampler<int16_t> resampler_; PushResampler<int16_t> resampler_;
}; };
// Helper class to perform resampling if needed, meant to be used after
// receiving the audio_frame from NetEq. Provides reasonably glitch free
// transitions between different output sample rates from NetEq.
class ResamplerHelper {
public:
ResamplerHelper();
// Resamples audio_frame if it is not already in desired_sample_rate_hz.
bool MaybeResample(int desired_sample_rate_hz, AudioFrame* audio_frame);
private:
ACMResampler resampler_;
bool resampled_last_output_frame_ = true;
std::array<int16_t, AudioFrame::kMaxDataSizeSamples> last_audio_buffer_;
};
} // namespace acm2 } // namespace acm2
} // namespace webrtc } // namespace webrtc