/* * Copyright (c) 2019 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 "test/pc/e2e/echo/echo_emulation.h" #include #include namespace webrtc { namespace webrtc_pc_e2e { namespace { constexpr int kSingleBufferDurationMs = 10; } // namespace EchoEmulatingCapturer::EchoEmulatingCapturer( std::unique_ptr capturer, PeerConnectionE2EQualityTestFixture::EchoEmulationConfig config) : delegate_(std::move(capturer)), config_(config), renderer_queue_(2 * config_.echo_delay.ms() / kSingleBufferDurationMs), queue_input_(TestAudioDeviceModule::SamplesPerFrame( delegate_->SamplingFrequency()) * delegate_->NumChannels()), queue_output_(TestAudioDeviceModule::SamplesPerFrame( delegate_->SamplingFrequency()) * delegate_->NumChannels()) { renderer_thread_.Detach(); capturer_thread_.Detach(); } void EchoEmulatingCapturer::OnAudioRendered( rtc::ArrayView data) { RTC_DCHECK_RUN_ON(&renderer_thread_); if (!recording_started_) { // Because rendering can start before capturing in the beginning we can have // a set of empty audio data frames. So we will skip them and will start // fill the queue only after 1st non-empty audio data frame will arrive. bool is_empty = true; for (auto d : data) { if (d != 0) { is_empty = false; break; } } if (is_empty) { return; } recording_started_ = true; } queue_input_.assign(data.begin(), data.end()); if (!renderer_queue_.Insert(&queue_input_)) { RTC_LOG(WARNING) << "Echo queue is full"; } } bool EchoEmulatingCapturer::Capture(rtc::BufferT* buffer) { RTC_DCHECK_RUN_ON(&capturer_thread_); bool result = delegate_->Capture(buffer); // Now we have to reduce input signal to avoid saturation when mixing in the // fake echo. for (size_t i = 0; i < buffer->size(); ++i) { (*buffer)[i] /= 2; } // When we accumulated enough delay in the echo buffer we will pop from // that buffer on each ::Capture(...) call. If the buffer become empty it // will mean some bug, so we will crash during removing item from the queue. if (!delay_accumulated_) { delay_accumulated_ = renderer_queue_.SizeAtLeast() >= static_cast(config_.echo_delay.ms() / kSingleBufferDurationMs); } if (delay_accumulated_) { RTC_CHECK(renderer_queue_.Remove(&queue_output_)); for (size_t i = 0; i < buffer->size() && i < queue_output_.size(); ++i) { int32_t res = (*buffer)[i] + queue_output_[i]; if (res < std::numeric_limits::min()) { res = std::numeric_limits::min(); } if (res > std::numeric_limits::max()) { res = std::numeric_limits::max(); } (*buffer)[i] = static_cast(res); } } return result; } EchoEmulatingRenderer::EchoEmulatingRenderer( std::unique_ptr renderer, EchoEmulatingCapturer* echo_emulating_capturer) : delegate_(std::move(renderer)), echo_emulating_capturer_(echo_emulating_capturer) { RTC_DCHECK(echo_emulating_capturer_); } bool EchoEmulatingRenderer::Render(rtc::ArrayView data) { if (data.size() > 0) { echo_emulating_capturer_->OnAudioRendered(data); } return delegate_->Render(data); } } // namespace webrtc_pc_e2e } // namespace webrtc