1 /*
2 * Copyright (c) 2019 The WebRTC project authors. All Rights Reserved.
3 *
4 * Use of this source code is governed by a BSD-style license
5 * that can be found in the LICENSE file in the root of the source
6 * tree. An additional intellectual property rights grant can be found
7 * in the file PATENTS. All contributing project authors may
8 * be found in the AUTHORS file in the root of the source tree.
9 */
10 #include "test/pc/e2e/echo/echo_emulation.h"
11
12 #include <limits>
13 #include <utility>
14
15 namespace webrtc {
16 namespace webrtc_pc_e2e {
17 namespace {
18
19 constexpr int kSingleBufferDurationMs = 10;
20
21 } // namespace
22
EchoEmulatingCapturer(std::unique_ptr<TestAudioDeviceModule::Capturer> capturer,PeerConnectionE2EQualityTestFixture::EchoEmulationConfig config)23 EchoEmulatingCapturer::EchoEmulatingCapturer(
24 std::unique_ptr<TestAudioDeviceModule::Capturer> capturer,
25 PeerConnectionE2EQualityTestFixture::EchoEmulationConfig config)
26 : delegate_(std::move(capturer)),
27 config_(config),
28 renderer_queue_(2 * config_.echo_delay.ms() / kSingleBufferDurationMs),
29 queue_input_(TestAudioDeviceModule::SamplesPerFrame(
30 delegate_->SamplingFrequency()) *
31 delegate_->NumChannels()),
32 queue_output_(TestAudioDeviceModule::SamplesPerFrame(
33 delegate_->SamplingFrequency()) *
34 delegate_->NumChannels()) {
35 renderer_thread_.Detach();
36 capturer_thread_.Detach();
37 }
38
OnAudioRendered(rtc::ArrayView<const int16_t> data)39 void EchoEmulatingCapturer::OnAudioRendered(
40 rtc::ArrayView<const int16_t> data) {
41 RTC_DCHECK_RUN_ON(&renderer_thread_);
42 if (!recording_started_) {
43 // Because rendering can start before capturing in the beginning we can have
44 // a set of empty audio data frames. So we will skip them and will start
45 // fill the queue only after 1st non-empty audio data frame will arrive.
46 bool is_empty = true;
47 for (auto d : data) {
48 if (d != 0) {
49 is_empty = false;
50 break;
51 }
52 }
53 if (is_empty) {
54 return;
55 }
56 recording_started_ = true;
57 }
58 queue_input_.assign(data.begin(), data.end());
59 if (!renderer_queue_.Insert(&queue_input_)) {
60 RTC_LOG(WARNING) << "Echo queue is full";
61 }
62 }
63
Capture(rtc::BufferT<int16_t> * buffer)64 bool EchoEmulatingCapturer::Capture(rtc::BufferT<int16_t>* buffer) {
65 RTC_DCHECK_RUN_ON(&capturer_thread_);
66 bool result = delegate_->Capture(buffer);
67 // Now we have to reduce input signal to avoid saturation when mixing in the
68 // fake echo.
69 for (size_t i = 0; i < buffer->size(); ++i) {
70 (*buffer)[i] /= 2;
71 }
72
73 // When we accumulated enough delay in the echo buffer we will pop from
74 // that buffer on each ::Capture(...) call. If the buffer become empty it
75 // will mean some bug, so we will crash during removing item from the queue.
76 if (!delay_accumulated_) {
77 delay_accumulated_ =
78 renderer_queue_.SizeAtLeast() >=
79 static_cast<size_t>(config_.echo_delay.ms() / kSingleBufferDurationMs);
80 }
81
82 if (delay_accumulated_) {
83 RTC_CHECK(renderer_queue_.Remove(&queue_output_));
84 for (size_t i = 0; i < buffer->size() && i < queue_output_.size(); ++i) {
85 int32_t res = (*buffer)[i] + queue_output_[i];
86 if (res < std::numeric_limits<int16_t>::min()) {
87 res = std::numeric_limits<int16_t>::min();
88 }
89 if (res > std::numeric_limits<int16_t>::max()) {
90 res = std::numeric_limits<int16_t>::max();
91 }
92 (*buffer)[i] = static_cast<int16_t>(res);
93 }
94 }
95
96 return result;
97 }
98
EchoEmulatingRenderer(std::unique_ptr<TestAudioDeviceModule::Renderer> renderer,EchoEmulatingCapturer * echo_emulating_capturer)99 EchoEmulatingRenderer::EchoEmulatingRenderer(
100 std::unique_ptr<TestAudioDeviceModule::Renderer> renderer,
101 EchoEmulatingCapturer* echo_emulating_capturer)
102 : delegate_(std::move(renderer)),
103 echo_emulating_capturer_(echo_emulating_capturer) {
104 RTC_DCHECK(echo_emulating_capturer_);
105 }
106
Render(rtc::ArrayView<const int16_t> data)107 bool EchoEmulatingRenderer::Render(rtc::ArrayView<const int16_t> data) {
108 if (data.size() > 0) {
109 echo_emulating_capturer_->OnAudioRendered(data);
110 }
111 return delegate_->Render(data);
112 }
113
114 } // namespace webrtc_pc_e2e
115 } // namespace webrtc
116