blob: fa1fef325ce35c37bb45a9025d43ed1e2c6acc28 [file] [log] [blame]
aleloi24899e52017-02-21 05:06:29 -08001/*
2 * Copyright (c) 2017 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
Mirko Bonadei92ea95e2017-09-15 06:47:31 +020011#include "modules/audio_mixer/frame_combiner.h"
aleloi24899e52017-02-21 05:06:29 -080012
Yves Gerey3e707812018-11-28 16:47:49 +010013#include <cstdint>
14#include <initializer_list>
aleloi24899e52017-02-21 05:06:29 -080015#include <numeric>
aleloi24899e52017-02-21 05:06:29 -080016#include <string>
Yves Gerey3e707812018-11-28 16:47:49 +010017#include <type_traits>
Doudou Kisabakafe6595f2021-05-18 11:50:01 +020018#include <vector>
aleloi24899e52017-02-21 05:06:29 -080019
Doudou Kisabakafe6595f2021-05-18 11:50:01 +020020#include "absl/types/optional.h"
Yves Gerey3e707812018-11-28 16:47:49 +010021#include "api/array_view.h"
Doudou Kisabakafe6595f2021-05-18 11:50:01 +020022#include "api/rtp_packet_info.h"
23#include "api/rtp_packet_infos.h"
Mirko Bonadei92ea95e2017-09-15 06:47:31 +020024#include "audio/utility/audio_frame_operations.h"
25#include "modules/audio_mixer/gain_change_calculator.h"
26#include "modules/audio_mixer/sine_wave_generator.h"
27#include "rtc_base/checks.h"
Jonas Olsson366a50c2018-09-06 13:41:30 +020028#include "rtc_base/strings/string_builder.h"
Alex Loikob4977de2019-01-28 16:38:38 +010029#include "test/gmock.h"
Mirko Bonadei92ea95e2017-09-15 06:47:31 +020030#include "test/gtest.h"
aleloi24899e52017-02-21 05:06:29 -080031
32namespace webrtc {
33
34namespace {
Doudou Kisabakafe6595f2021-05-18 11:50:01 +020035
36using ::testing::ElementsAreArray;
37using ::testing::IsEmpty;
38using ::testing::UnorderedElementsAreArray;
39
Alex Loiko507e8d12018-02-27 13:51:47 +010040using LimiterType = FrameCombiner::LimiterType;
Doudou Kisabakafe6595f2021-05-18 11:50:01 +020041
Alex Loiko507e8d12018-02-27 13:51:47 +010042struct FrameCombinerConfig {
Alex Loiko8396e342018-06-21 12:04:05 +020043 bool use_limiter;
44 int sample_rate_hz;
Alex Loiko507e8d12018-02-27 13:51:47 +010045 int number_of_channels;
46 float wave_frequency;
47};
48
aleloi24899e52017-02-21 05:06:29 -080049std::string ProduceDebugText(int sample_rate_hz,
50 int number_of_channels,
51 int number_of_sources) {
Jonas Olsson366a50c2018-09-06 13:41:30 +020052 rtc::StringBuilder ss;
aleloi2c9306e2017-03-29 04:25:16 -070053 ss << "Sample rate: " << sample_rate_hz << " ,";
54 ss << "number of channels: " << number_of_channels << " ,";
55 ss << "number of sources: " << number_of_sources;
Jonas Olsson84df1c72018-09-14 16:59:32 +020056 return ss.Release();
aleloi2c9306e2017-03-29 04:25:16 -070057}
58
Alex Loiko507e8d12018-02-27 13:51:47 +010059std::string ProduceDebugText(const FrameCombinerConfig& config) {
Jonas Olsson366a50c2018-09-06 13:41:30 +020060 rtc::StringBuilder ss;
Alex Loiko507e8d12018-02-27 13:51:47 +010061 ss << "Sample rate: " << config.sample_rate_hz << " ,";
62 ss << "number of channels: " << config.number_of_channels << " ,";
Alex Loiko8396e342018-06-21 12:04:05 +020063 ss << "limiter active: " << (config.use_limiter ? "on" : "off") << " ,";
Alex Loiko507e8d12018-02-27 13:51:47 +010064 ss << "wave frequency: " << config.wave_frequency << " ,";
Jonas Olsson84df1c72018-09-14 16:59:32 +020065 return ss.Release();
aleloi24899e52017-02-21 05:06:29 -080066}
67
68AudioFrame frame1;
69AudioFrame frame2;
aleloi24899e52017-02-21 05:06:29 -080070
71void SetUpFrames(int sample_rate_hz, int number_of_channels) {
Doudou Kisabakafe6595f2021-05-18 11:50:01 +020072 RtpPacketInfo packet_info1(
73 /*ssrc=*/1001, /*csrcs=*/{}, /*rtp_timestamp=*/1000,
74 /*audio_level=*/absl::nullopt, /*absolute_capture_time=*/absl::nullopt,
75 /*receive_time_ms=*/1);
76 RtpPacketInfo packet_info2(
77 /*ssrc=*/4004, /*csrcs=*/{}, /*rtp_timestamp=*/1234,
78 /*audio_level=*/absl::nullopt, /*absolute_capture_time=*/absl::nullopt,
79 /*receive_time_ms=*/2);
80 RtpPacketInfo packet_info3(
81 /*ssrc=*/7007, /*csrcs=*/{}, /*rtp_timestamp=*/1333,
82 /*audio_level=*/absl::nullopt, /*absolute_capture_time=*/absl::nullopt,
83 /*receive_time_ms=*/2);
84
85 frame1.packet_infos_ = RtpPacketInfos({packet_info1});
86 frame2.packet_infos_ = RtpPacketInfos({packet_info2, packet_info3});
87
aleloi24899e52017-02-21 05:06:29 -080088 for (auto* frame : {&frame1, &frame2}) {
solenbergc7b4a452017-09-28 07:37:11 -070089 frame->UpdateFrame(0, nullptr, rtc::CheckedDivExact(sample_rate_hz, 100),
aleloi24899e52017-02-21 05:06:29 -080090 sample_rate_hz, AudioFrame::kNormalSpeech,
91 AudioFrame::kVadActive, number_of_channels);
92 }
93}
94} // namespace
95
Alex Loiko8396e342018-06-21 12:04:05 +020096// The limiter requires sample rate divisible by 2000.
aleloi24899e52017-02-21 05:06:29 -080097TEST(FrameCombiner, BasicApiCallsLimiter) {
Alex Loiko8396e342018-06-21 12:04:05 +020098 FrameCombiner combiner(true);
99 for (const int rate : {8000, 18000, 34000, 48000}) {
Alex Loikob4977de2019-01-28 16:38:38 +0100100 for (const int number_of_channels : {1, 2, 4, 8}) {
aleloi24899e52017-02-21 05:06:29 -0800101 const std::vector<AudioFrame*> all_frames = {&frame1, &frame2};
102 SetUpFrames(rate, number_of_channels);
103
104 for (const int number_of_frames : {0, 1, 2}) {
105 SCOPED_TRACE(
106 ProduceDebugText(rate, number_of_channels, number_of_frames));
107 const std::vector<AudioFrame*> frames_to_combine(
108 all_frames.begin(), all_frames.begin() + number_of_frames);
Doudou Kisabakafe6595f2021-05-18 11:50:01 +0200109 AudioFrame audio_frame_for_mixing;
aleloi24899e52017-02-21 05:06:29 -0800110 combiner.Combine(frames_to_combine, number_of_channels, rate,
aleloi2c9306e2017-03-29 04:25:16 -0700111 frames_to_combine.size(), &audio_frame_for_mixing);
aleloi24899e52017-02-21 05:06:29 -0800112 }
113 }
114 }
115}
116
Doudou Kisabakafe6595f2021-05-18 11:50:01 +0200117// The RtpPacketInfos field of the mixed packet should contain the union of the
118// RtpPacketInfos from the frames that were actually mixed.
119TEST(FrameCombiner, ContainsAllRtpPacketInfos) {
120 static constexpr int kSampleRateHz = 48000;
121 static constexpr int kNumChannels = 1;
122 FrameCombiner combiner(true);
123 const std::vector<AudioFrame*> all_frames = {&frame1, &frame2};
124 SetUpFrames(kSampleRateHz, kNumChannels);
125
126 for (const int number_of_frames : {0, 1, 2}) {
127 SCOPED_TRACE(
128 ProduceDebugText(kSampleRateHz, kNumChannels, number_of_frames));
129 const std::vector<AudioFrame*> frames_to_combine(
130 all_frames.begin(), all_frames.begin() + number_of_frames);
131
132 std::vector<RtpPacketInfo> packet_infos;
133 for (const auto& frame : frames_to_combine) {
134 packet_infos.insert(packet_infos.end(), frame->packet_infos_.begin(),
135 frame->packet_infos_.end());
136 }
137
138 AudioFrame audio_frame_for_mixing;
139 combiner.Combine(frames_to_combine, kNumChannels, kSampleRateHz,
140 frames_to_combine.size(), &audio_frame_for_mixing);
141 EXPECT_THAT(audio_frame_for_mixing.packet_infos_,
142 UnorderedElementsAreArray(packet_infos));
143 }
144}
145
Alex Loikob4977de2019-01-28 16:38:38 +0100146// There are DCHECKs in place to check for invalid parameters.
Tommia5e07cc2020-05-26 21:40:37 +0200147TEST(FrameCombinerDeathTest, DebugBuildCrashesWithManyChannels) {
Alex Loikob4977de2019-01-28 16:38:38 +0100148 FrameCombiner combiner(true);
149 for (const int rate : {8000, 18000, 34000, 48000}) {
150 for (const int number_of_channels : {10, 20, 21}) {
151 if (static_cast<size_t>(rate / 100 * number_of_channels) >
152 AudioFrame::kMaxDataSizeSamples) {
153 continue;
154 }
155 const std::vector<AudioFrame*> all_frames = {&frame1, &frame2};
156 SetUpFrames(rate, number_of_channels);
157
158 const int number_of_frames = 2;
159 SCOPED_TRACE(
160 ProduceDebugText(rate, number_of_channels, number_of_frames));
161 const std::vector<AudioFrame*> frames_to_combine(
162 all_frames.begin(), all_frames.begin() + number_of_frames);
Doudou Kisabakafe6595f2021-05-18 11:50:01 +0200163 AudioFrame audio_frame_for_mixing;
Alex Loikob4977de2019-01-28 16:38:38 +0100164#if RTC_DCHECK_IS_ON && GTEST_HAS_DEATH_TEST && !defined(WEBRTC_ANDROID)
165 EXPECT_DEATH(
166 combiner.Combine(frames_to_combine, number_of_channels, rate,
167 frames_to_combine.size(), &audio_frame_for_mixing),
168 "");
169#elif !RTC_DCHECK_IS_ON
170 combiner.Combine(frames_to_combine, number_of_channels, rate,
171 frames_to_combine.size(), &audio_frame_for_mixing);
172#endif
173 }
174 }
175}
176
Tommia5e07cc2020-05-26 21:40:37 +0200177TEST(FrameCombinerDeathTest, DebugBuildCrashesWithHighRate) {
Alex Loikob4977de2019-01-28 16:38:38 +0100178 FrameCombiner combiner(true);
179 for (const int rate : {50000, 96000, 128000, 196000}) {
180 for (const int number_of_channels : {1, 2, 3}) {
181 if (static_cast<size_t>(rate / 100 * number_of_channels) >
182 AudioFrame::kMaxDataSizeSamples) {
183 continue;
184 }
185 const std::vector<AudioFrame*> all_frames = {&frame1, &frame2};
186 SetUpFrames(rate, number_of_channels);
187
188 const int number_of_frames = 2;
189 SCOPED_TRACE(
190 ProduceDebugText(rate, number_of_channels, number_of_frames));
191 const std::vector<AudioFrame*> frames_to_combine(
192 all_frames.begin(), all_frames.begin() + number_of_frames);
Doudou Kisabakafe6595f2021-05-18 11:50:01 +0200193 AudioFrame audio_frame_for_mixing;
Alex Loikob4977de2019-01-28 16:38:38 +0100194#if RTC_DCHECK_IS_ON && GTEST_HAS_DEATH_TEST && !defined(WEBRTC_ANDROID)
195 EXPECT_DEATH(
196 combiner.Combine(frames_to_combine, number_of_channels, rate,
197 frames_to_combine.size(), &audio_frame_for_mixing),
198 "");
199#elif !RTC_DCHECK_IS_ON
200 combiner.Combine(frames_to_combine, number_of_channels, rate,
201 frames_to_combine.size(), &audio_frame_for_mixing);
202#endif
203 }
204 }
205}
206
Alex Loiko8396e342018-06-21 12:04:05 +0200207// With no limiter, the rate has to be divisible by 100 since we use
208// 10 ms frames.
aleloi24899e52017-02-21 05:06:29 -0800209TEST(FrameCombiner, BasicApiCallsNoLimiter) {
Alex Loiko8396e342018-06-21 12:04:05 +0200210 FrameCombiner combiner(false);
aleloi24899e52017-02-21 05:06:29 -0800211 for (const int rate : {8000, 10000, 11000, 32000, 44100}) {
Alex Loikob4977de2019-01-28 16:38:38 +0100212 for (const int number_of_channels : {1, 2, 4, 8}) {
aleloi24899e52017-02-21 05:06:29 -0800213 const std::vector<AudioFrame*> all_frames = {&frame1, &frame2};
214 SetUpFrames(rate, number_of_channels);
215
216 for (const int number_of_frames : {0, 1, 2}) {
217 SCOPED_TRACE(
218 ProduceDebugText(rate, number_of_channels, number_of_frames));
219 const std::vector<AudioFrame*> frames_to_combine(
220 all_frames.begin(), all_frames.begin() + number_of_frames);
Doudou Kisabakafe6595f2021-05-18 11:50:01 +0200221 AudioFrame audio_frame_for_mixing;
aleloi24899e52017-02-21 05:06:29 -0800222 combiner.Combine(frames_to_combine, number_of_channels, rate,
aleloi2c9306e2017-03-29 04:25:16 -0700223 frames_to_combine.size(), &audio_frame_for_mixing);
aleloi24899e52017-02-21 05:06:29 -0800224 }
225 }
226 }
227}
228
229TEST(FrameCombiner, CombiningZeroFramesShouldProduceSilence) {
Alex Loiko8396e342018-06-21 12:04:05 +0200230 FrameCombiner combiner(false);
aleloi24899e52017-02-21 05:06:29 -0800231 for (const int rate : {8000, 10000, 11000, 32000, 44100}) {
232 for (const int number_of_channels : {1, 2}) {
233 SCOPED_TRACE(ProduceDebugText(rate, number_of_channels, 0));
234
Doudou Kisabakafe6595f2021-05-18 11:50:01 +0200235 AudioFrame audio_frame_for_mixing;
236
aleloi24899e52017-02-21 05:06:29 -0800237 const std::vector<AudioFrame*> frames_to_combine;
238 combiner.Combine(frames_to_combine, number_of_channels, rate,
aleloi2c9306e2017-03-29 04:25:16 -0700239 frames_to_combine.size(), &audio_frame_for_mixing);
yujo36b1a5f2017-06-12 12:45:32 -0700240 const int16_t* audio_frame_for_mixing_data =
241 audio_frame_for_mixing.data();
aleloi24899e52017-02-21 05:06:29 -0800242 const std::vector<int16_t> mixed_data(
yujo36b1a5f2017-06-12 12:45:32 -0700243 audio_frame_for_mixing_data,
244 audio_frame_for_mixing_data + number_of_channels * rate / 100);
aleloi24899e52017-02-21 05:06:29 -0800245
246 const std::vector<int16_t> expected(number_of_channels * rate / 100, 0);
247 EXPECT_EQ(mixed_data, expected);
Doudou Kisabakafe6595f2021-05-18 11:50:01 +0200248 EXPECT_THAT(audio_frame_for_mixing.packet_infos_, IsEmpty());
aleloi24899e52017-02-21 05:06:29 -0800249 }
250 }
251}
252
253TEST(FrameCombiner, CombiningOneFrameShouldNotChangeFrame) {
Alex Loiko8396e342018-06-21 12:04:05 +0200254 FrameCombiner combiner(false);
aleloi24899e52017-02-21 05:06:29 -0800255 for (const int rate : {8000, 10000, 11000, 32000, 44100}) {
Alex Loikob4977de2019-01-28 16:38:38 +0100256 for (const int number_of_channels : {1, 2, 4, 8, 10}) {
aleloi24899e52017-02-21 05:06:29 -0800257 SCOPED_TRACE(ProduceDebugText(rate, number_of_channels, 1));
258
Doudou Kisabakafe6595f2021-05-18 11:50:01 +0200259 AudioFrame audio_frame_for_mixing;
260
aleloi24899e52017-02-21 05:06:29 -0800261 SetUpFrames(rate, number_of_channels);
yujo36b1a5f2017-06-12 12:45:32 -0700262 int16_t* frame1_data = frame1.mutable_data();
263 std::iota(frame1_data, frame1_data + number_of_channels * rate / 100, 0);
aleloi24899e52017-02-21 05:06:29 -0800264 const std::vector<AudioFrame*> frames_to_combine = {&frame1};
265 combiner.Combine(frames_to_combine, number_of_channels, rate,
aleloi2c9306e2017-03-29 04:25:16 -0700266 frames_to_combine.size(), &audio_frame_for_mixing);
aleloi24899e52017-02-21 05:06:29 -0800267
yujo36b1a5f2017-06-12 12:45:32 -0700268 const int16_t* audio_frame_for_mixing_data =
269 audio_frame_for_mixing.data();
aleloi24899e52017-02-21 05:06:29 -0800270 const std::vector<int16_t> mixed_data(
yujo36b1a5f2017-06-12 12:45:32 -0700271 audio_frame_for_mixing_data,
272 audio_frame_for_mixing_data + number_of_channels * rate / 100);
aleloi24899e52017-02-21 05:06:29 -0800273
274 std::vector<int16_t> expected(number_of_channels * rate / 100);
275 std::iota(expected.begin(), expected.end(), 0);
276 EXPECT_EQ(mixed_data, expected);
Doudou Kisabakafe6595f2021-05-18 11:50:01 +0200277 EXPECT_THAT(audio_frame_for_mixing.packet_infos_,
278 ElementsAreArray(frame1.packet_infos_));
aleloi24899e52017-02-21 05:06:29 -0800279 }
280 }
281}
282
aleloi2c9306e2017-03-29 04:25:16 -0700283// Send a sine wave through the FrameCombiner, and check that the
Alex Loiko507e8d12018-02-27 13:51:47 +0100284// difference between input and output varies smoothly. Also check
285// that it is inside reasonable bounds. This is to catch issues like
286// chromium:695993 and chromium:816875.
aleloi2c9306e2017-03-29 04:25:16 -0700287TEST(FrameCombiner, GainCurveIsSmoothForAlternatingNumberOfStreams) {
Alex Loiko8396e342018-06-21 12:04:05 +0200288 // Rates are divisible by 2000 when limiter is active.
Alex Loiko507e8d12018-02-27 13:51:47 +0100289 std::vector<FrameCombinerConfig> configs = {
Alex Loiko8396e342018-06-21 12:04:05 +0200290 {false, 30100, 2, 50.f}, {false, 16500, 1, 3200.f},
291 {true, 8000, 1, 3200.f}, {true, 16000, 1, 50.f},
Alex Loikob4977de2019-01-28 16:38:38 +0100292 {true, 18000, 8, 3200.f}, {true, 10000, 2, 50.f},
Alex Loiko507e8d12018-02-27 13:51:47 +0100293 };
aleloi2c9306e2017-03-29 04:25:16 -0700294
Alex Loiko507e8d12018-02-27 13:51:47 +0100295 for (const auto& config : configs) {
296 SCOPED_TRACE(ProduceDebugText(config));
aleloi2c9306e2017-03-29 04:25:16 -0700297
Alex Loiko8396e342018-06-21 12:04:05 +0200298 FrameCombiner combiner(config.use_limiter);
aleloi2c9306e2017-03-29 04:25:16 -0700299
Alex Loiko507e8d12018-02-27 13:51:47 +0100300 constexpr int16_t wave_amplitude = 30000;
301 SineWaveGenerator wave_generator(config.wave_frequency, wave_amplitude);
aleloi2c9306e2017-03-29 04:25:16 -0700302
Alex Loiko507e8d12018-02-27 13:51:47 +0100303 GainChangeCalculator change_calculator;
304 float cumulative_change = 0.f;
aleloi2c9306e2017-03-29 04:25:16 -0700305
Alex Loiko507e8d12018-02-27 13:51:47 +0100306 constexpr size_t iterations = 100;
aleloi2c9306e2017-03-29 04:25:16 -0700307
Alex Loiko507e8d12018-02-27 13:51:47 +0100308 for (size_t i = 0; i < iterations; ++i) {
309 SetUpFrames(config.sample_rate_hz, config.number_of_channels);
310 wave_generator.GenerateNextFrame(&frame1);
311 AudioFrameOperations::Mute(&frame2);
aleloi2c9306e2017-03-29 04:25:16 -0700312
Alex Loiko507e8d12018-02-27 13:51:47 +0100313 std::vector<AudioFrame*> frames_to_combine = {&frame1};
314 if (i % 2 == 0) {
315 frames_to_combine.push_back(&frame2);
aleloi2c9306e2017-03-29 04:25:16 -0700316 }
Alex Loiko507e8d12018-02-27 13:51:47 +0100317 const size_t number_of_samples =
318 frame1.samples_per_channel_ * config.number_of_channels;
319
320 // Ensures limiter is on if 'use_limiter'.
321 constexpr size_t number_of_streams = 2;
Doudou Kisabakafe6595f2021-05-18 11:50:01 +0200322 AudioFrame audio_frame_for_mixing;
Alex Loiko507e8d12018-02-27 13:51:47 +0100323 combiner.Combine(frames_to_combine, config.number_of_channels,
324 config.sample_rate_hz, number_of_streams,
325 &audio_frame_for_mixing);
326 cumulative_change += change_calculator.CalculateGainChange(
327 rtc::ArrayView<const int16_t>(frame1.data(), number_of_samples),
328 rtc::ArrayView<const int16_t>(audio_frame_for_mixing.data(),
329 number_of_samples));
aleloi2c9306e2017-03-29 04:25:16 -0700330 }
Alex Loiko507e8d12018-02-27 13:51:47 +0100331
332 // Check that the gain doesn't vary too much.
333 EXPECT_LT(cumulative_change, 10);
334
335 // Check that the latest gain is within reasonable bounds. It
336 // should be slightly less that 1.
337 EXPECT_LT(0.9f, change_calculator.LatestGain());
338 EXPECT_LT(change_calculator.LatestGain(), 1.01f);
aleloi2c9306e2017-03-29 04:25:16 -0700339 }
340}
aleloi24899e52017-02-21 05:06:29 -0800341} // namespace webrtc