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