/* * Copyright (c) 2016 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 "modules/audio_mixer/audio_mixer_impl.h" #include #include #include #include #include #include #include #include "absl/types/optional.h" #include "api/audio/audio_mixer.h" #include "api/rtp_packet_info.h" #include "api/rtp_packet_infos.h" #include "api/units/timestamp.h" #include "modules/audio_mixer/default_output_rate_calculator.h" #include "rtc_base/checks.h" #include "rtc_base/strings/string_builder.h" #include "rtc_base/task_queue_for_test.h" #include "test/gmock.h" #include "test/gtest.h" using ::testing::_; using ::testing::Exactly; using ::testing::Invoke; using ::testing::Return; using ::testing::UnorderedElementsAre; namespace webrtc { namespace { constexpr int kDefaultSampleRateHz = 48000; // Utility function that resets the frame member variables with // sensible defaults. void ResetFrame(AudioFrame* frame) { frame->sample_rate_hz_ = kDefaultSampleRateHz; frame->num_channels_ = 1; // Frame duration 10ms. frame->samples_per_channel_ = kDefaultSampleRateHz / 100; frame->vad_activity_ = AudioFrame::kVadActive; frame->speech_type_ = AudioFrame::kNormalSpeech; } std::string ProduceDebugText(int sample_rate_hz, int number_of_channels, int number_of_sources) { rtc::StringBuilder ss; ss << "Sample rate: " << sample_rate_hz << " "; ss << "Number of channels: " << number_of_channels << " "; ss << "Number of sources: " << number_of_sources; return ss.Release(); } AudioFrame frame_for_mixing; } // namespace class MockMixerAudioSource : public ::testing::NiceMock { public: MockMixerAudioSource() : fake_audio_frame_info_(AudioMixer::Source::AudioFrameInfo::kNormal) { ON_CALL(*this, GetAudioFrameWithInfo(_, _)) .WillByDefault( Invoke(this, &MockMixerAudioSource::FakeAudioFrameWithInfo)); ON_CALL(*this, PreferredSampleRate()) .WillByDefault(Return(kDefaultSampleRateHz)); } MOCK_METHOD(AudioFrameInfo, GetAudioFrameWithInfo, (int sample_rate_hz, AudioFrame* audio_frame), (override)); MOCK_METHOD(int, PreferredSampleRate, (), (const, override)); MOCK_METHOD(int, Ssrc, (), (const, override)); AudioFrame* fake_frame() { return &fake_frame_; } AudioFrameInfo fake_info() { return fake_audio_frame_info_; } void set_fake_info(const AudioFrameInfo audio_frame_info) { fake_audio_frame_info_ = audio_frame_info; } void set_packet_infos(const RtpPacketInfos& packet_infos) { packet_infos_ = packet_infos; } private: AudioFrameInfo FakeAudioFrameWithInfo(int sample_rate_hz, AudioFrame* audio_frame) { audio_frame->CopyFrom(fake_frame_); audio_frame->sample_rate_hz_ = sample_rate_hz; audio_frame->samples_per_channel_ = rtc::CheckedDivExact(sample_rate_hz, 100); audio_frame->packet_infos_ = packet_infos_; return fake_info(); } AudioFrame fake_frame_; AudioFrameInfo fake_audio_frame_info_; RtpPacketInfos packet_infos_; }; class CustomRateCalculator : public OutputRateCalculator { public: explicit CustomRateCalculator(int rate) : rate_(rate) {} int CalculateOutputRateFromRange( rtc::ArrayView preferred_rates) override { return rate_; } private: const int rate_; }; // Creates participants from `frames` and `frame_info` and adds them // to the mixer. Compares mixed status with `expected_status` void MixAndCompare( const std::vector& frames, const std::vector& frame_info, const std::vector& expected_status) { const size_t num_audio_sources = frames.size(); RTC_DCHECK(frames.size() == frame_info.size()); RTC_DCHECK(frame_info.size() == expected_status.size()); const auto mixer = AudioMixerImpl::Create(); std::vector participants(num_audio_sources); for (size_t i = 0; i < num_audio_sources; ++i) { participants[i].fake_frame()->CopyFrom(frames[i]); participants[i].set_fake_info(frame_info[i]); } for (size_t i = 0; i < num_audio_sources; ++i) { EXPECT_TRUE(mixer->AddSource(&participants[i])); EXPECT_CALL(participants[i], GetAudioFrameWithInfo(kDefaultSampleRateHz, _)) .Times(Exactly(1)); } mixer->Mix(1, &frame_for_mixing); for (size_t i = 0; i < num_audio_sources; ++i) { EXPECT_EQ(expected_status[i], mixer->GetAudioSourceMixabilityStatusForTest(&participants[i])) << "Mixed status of AudioSource #" << i << " wrong."; } } void MixMonoAtGivenNativeRate(int native_sample_rate, AudioFrame* mix_frame, rtc::scoped_refptr mixer, MockMixerAudioSource* audio_source) { ON_CALL(*audio_source, PreferredSampleRate()) .WillByDefault(Return(native_sample_rate)); audio_source->fake_frame()->sample_rate_hz_ = native_sample_rate; audio_source->fake_frame()->samples_per_channel_ = native_sample_rate / 100; mixer->Mix(1, mix_frame); } TEST(AudioMixer, LargestEnergyVadActiveMixed) { constexpr int kAudioSources = AudioMixerImpl::kDefaultNumberOfMixedAudioSources + 3; const auto mixer = AudioMixerImpl::Create(); MockMixerAudioSource participants[kAudioSources]; for (int i = 0; i < kAudioSources; ++i) { ResetFrame(participants[i].fake_frame()); // We set the 80-th sample value since the first 80 samples may be // modified by a ramped-in window. participants[i].fake_frame()->mutable_data()[80] = i; EXPECT_TRUE(mixer->AddSource(&participants[i])); EXPECT_CALL(participants[i], GetAudioFrameWithInfo(_, _)).Times(Exactly(1)); } // Last participant gives audio frame with passive VAD, although it has the // largest energy. participants[kAudioSources - 1].fake_frame()->vad_activity_ = AudioFrame::kVadPassive; AudioFrame audio_frame; mixer->Mix(1, // number of channels &audio_frame); for (int i = 0; i < kAudioSources; ++i) { bool is_mixed = mixer->GetAudioSourceMixabilityStatusForTest(&participants[i]); if (i == kAudioSources - 1 || i < kAudioSources - 1 - AudioMixerImpl::kDefaultNumberOfMixedAudioSources) { EXPECT_FALSE(is_mixed) << "Mixing status of AudioSource #" << i << " wrong."; } else { EXPECT_TRUE(is_mixed) << "Mixing status of AudioSource #" << i << " wrong."; } } } TEST(AudioMixer, FrameNotModifiedForSingleParticipant) { const auto mixer = AudioMixerImpl::Create(); MockMixerAudioSource participant; ResetFrame(participant.fake_frame()); const size_t n_samples = participant.fake_frame()->samples_per_channel_; // Modify the frame so that it's not zero. int16_t* fake_frame_data = participant.fake_frame()->mutable_data(); for (size_t j = 0; j < n_samples; ++j) { fake_frame_data[j] = static_cast(j); } EXPECT_TRUE(mixer->AddSource(&participant)); EXPECT_CALL(participant, GetAudioFrameWithInfo(_, _)).Times(Exactly(2)); AudioFrame audio_frame; // Two mix iteration to compare after the ramp-up step. for (int i = 0; i < 2; ++i) { mixer->Mix(1, // number of channels &audio_frame); } EXPECT_EQ(0, memcmp(participant.fake_frame()->data(), audio_frame.data(), n_samples)); } TEST(AudioMixer, SourceAtNativeRateShouldNeverResample) { const auto mixer = AudioMixerImpl::Create(); MockMixerAudioSource audio_source; ResetFrame(audio_source.fake_frame()); mixer->AddSource(&audio_source); for (auto frequency : {8000, 16000, 32000, 48000}) { EXPECT_CALL(audio_source, GetAudioFrameWithInfo(frequency, _)) .Times(Exactly(1)); MixMonoAtGivenNativeRate(frequency, &frame_for_mixing, mixer, &audio_source); } } TEST(AudioMixer, MixerShouldMixAtNativeSourceRate) { const auto mixer = AudioMixerImpl::Create(); MockMixerAudioSource audio_source; ResetFrame(audio_source.fake_frame()); mixer->AddSource(&audio_source); for (auto frequency : {8000, 16000, 32000, 48000}) { MixMonoAtGivenNativeRate(frequency, &frame_for_mixing, mixer, &audio_source); EXPECT_EQ(frequency, frame_for_mixing.sample_rate_hz_); } } TEST(AudioMixer, MixerShouldAlwaysMixAtNativeRate) { const auto mixer = AudioMixerImpl::Create(); MockMixerAudioSource participant; ResetFrame(participant.fake_frame()); mixer->AddSource(&participant); const int needed_frequency = 44100; ON_CALL(participant, PreferredSampleRate()) .WillByDefault(Return(needed_frequency)); // We expect mixing frequency to be native and >= needed_frequency. const int expected_mix_frequency = 48000; EXPECT_CALL(participant, GetAudioFrameWithInfo(expected_mix_frequency, _)) .Times(Exactly(1)); participant.fake_frame()->sample_rate_hz_ = expected_mix_frequency; participant.fake_frame()->samples_per_channel_ = expected_mix_frequency / 100; mixer->Mix(1, &frame_for_mixing); EXPECT_EQ(48000, frame_for_mixing.sample_rate_hz_); } // Check that the mixing rate is always >= participants preferred rate. TEST(AudioMixer, ShouldNotCauseQualityLossForMultipleSources) { const auto mixer = AudioMixerImpl::Create(); std::vector audio_sources(2); const std::vector source_sample_rates = {8000, 16000}; for (int i = 0; i < 2; ++i) { auto& source = audio_sources[i]; ResetFrame(source.fake_frame()); mixer->AddSource(&source); const auto sample_rate = source_sample_rates[i]; EXPECT_CALL(source, PreferredSampleRate()).WillOnce(Return(sample_rate)); EXPECT_CALL(source, GetAudioFrameWithInfo(::testing::Ge(sample_rate), _)); } mixer->Mix(1, &frame_for_mixing); } TEST(AudioMixer, ParticipantNumberOfChannels) { const auto mixer = AudioMixerImpl::Create(); MockMixerAudioSource participant; ResetFrame(participant.fake_frame()); EXPECT_TRUE(mixer->AddSource(&participant)); for (size_t number_of_channels : {1, 2}) { EXPECT_CALL(participant, GetAudioFrameWithInfo(kDefaultSampleRateHz, _)) .Times(Exactly(1)); mixer->Mix(number_of_channels, &frame_for_mixing); EXPECT_EQ(number_of_channels, frame_for_mixing.num_channels_); } } // Maximal amount of participants are mixed one iteration, then // another participant with higher energy is added. TEST(AudioMixer, RampedOutSourcesShouldNotBeMarkedMixed) { constexpr int kAudioSources = AudioMixerImpl::kDefaultNumberOfMixedAudioSources + 1; const auto mixer = AudioMixerImpl::Create(); MockMixerAudioSource participants[kAudioSources]; for (int i = 0; i < kAudioSources; ++i) { ResetFrame(participants[i].fake_frame()); // Set the participant audio energy to increase with the index // `i`. participants[i].fake_frame()->mutable_data()[0] = 100 * i; } // Add all participants but the loudest for mixing. for (int i = 0; i < kAudioSources - 1; ++i) { EXPECT_TRUE(mixer->AddSource(&participants[i])); EXPECT_CALL(participants[i], GetAudioFrameWithInfo(kDefaultSampleRateHz, _)) .Times(Exactly(1)); } // First mixer iteration mixer->Mix(1, &frame_for_mixing); // All participants but the loudest should have been mixed. for (int i = 0; i < kAudioSources - 1; ++i) { EXPECT_TRUE(mixer->GetAudioSourceMixabilityStatusForTest(&participants[i])) << "Mixed status of AudioSource #" << i << " wrong."; } // Add new participant with higher energy. EXPECT_TRUE(mixer->AddSource(&participants[kAudioSources - 1])); for (int i = 0; i < kAudioSources; ++i) { EXPECT_CALL(participants[i], GetAudioFrameWithInfo(kDefaultSampleRateHz, _)) .Times(Exactly(1)); } mixer->Mix(1, &frame_for_mixing); // The most quiet participant should not have been mixed. EXPECT_FALSE(mixer->GetAudioSourceMixabilityStatusForTest(&participants[0])) << "Mixed status of AudioSource #0 wrong."; // The loudest participants should have been mixed. for (int i = 1; i < kAudioSources; ++i) { EXPECT_EQ(true, mixer->GetAudioSourceMixabilityStatusForTest(&participants[i])) << "Mixed status of AudioSource #" << i << " wrong."; } } // This test checks that the initialization and participant addition // can be done on a different thread. TEST(AudioMixer, ConstructFromOtherThread) { TaskQueueForTest init_queue("init"); rtc::scoped_refptr mixer; init_queue.SendTask([&mixer]() { mixer = AudioMixerImpl::Create(); }); MockMixerAudioSource participant; EXPECT_CALL(participant, PreferredSampleRate()) .WillRepeatedly(Return(kDefaultSampleRateHz)); ResetFrame(participant.fake_frame()); TaskQueueForTest participant_queue("participant"); participant_queue.SendTask( [&mixer, &participant]() { mixer->AddSource(&participant); }); EXPECT_CALL(participant, GetAudioFrameWithInfo(kDefaultSampleRateHz, _)) .Times(Exactly(1)); // Do one mixer iteration mixer->Mix(1, &frame_for_mixing); } TEST(AudioMixer, MutedShouldMixAfterUnmuted) { constexpr int kAudioSources = AudioMixerImpl::kDefaultNumberOfMixedAudioSources + 1; std::vector frames(kAudioSources); for (auto& frame : frames) { ResetFrame(&frame); } std::vector frame_info( kAudioSources, AudioMixer::Source::AudioFrameInfo::kNormal); frame_info[0] = AudioMixer::Source::AudioFrameInfo::kMuted; std::vector expected_status(kAudioSources, true); expected_status[0] = false; MixAndCompare(frames, frame_info, expected_status); } TEST(AudioMixer, PassiveShouldMixAfterNormal) { constexpr int kAudioSources = AudioMixerImpl::kDefaultNumberOfMixedAudioSources + 1; std::vector frames(kAudioSources); for (auto& frame : frames) { ResetFrame(&frame); } std::vector frame_info( kAudioSources, AudioMixer::Source::AudioFrameInfo::kNormal); frames[0].vad_activity_ = AudioFrame::kVadPassive; std::vector expected_status(kAudioSources, true); expected_status[0] = false; MixAndCompare(frames, frame_info, expected_status); } TEST(AudioMixer, ActiveShouldMixBeforeLoud) { constexpr int kAudioSources = AudioMixerImpl::kDefaultNumberOfMixedAudioSources + 1; std::vector frames(kAudioSources); for (auto& frame : frames) { ResetFrame(&frame); } std::vector frame_info( kAudioSources, AudioMixer::Source::AudioFrameInfo::kNormal); frames[0].vad_activity_ = AudioFrame::kVadPassive; int16_t* frame_data = frames[0].mutable_data(); std::fill(frame_data, frame_data + kDefaultSampleRateHz / 100, std::numeric_limits::max()); std::vector expected_status(kAudioSources, true); expected_status[0] = false; MixAndCompare(frames, frame_info, expected_status); } TEST(AudioMixer, ShouldMixUpToSpecifiedNumberOfSourcesToMix) { constexpr int kAudioSources = 5; constexpr int kSourcesToMix = 2; std::vector frames(kAudioSources); for (auto& frame : frames) { ResetFrame(&frame); } std::vector frame_info( kAudioSources, AudioMixer::Source::AudioFrameInfo::kNormal); // Set up to kSourceToMix sources with kVadActive so that they're mixed. const std::vector kVadActivities = { AudioFrame::kVadUnknown, AudioFrame::kVadPassive, AudioFrame::kVadPassive, AudioFrame::kVadActive, AudioFrame::kVadActive}; // Populate VAD and frame for all sources. for (int i = 0; i < kAudioSources; i++) { frames[i].vad_activity_ = kVadActivities[i]; } std::vector participants(kAudioSources); for (int i = 0; i < kAudioSources; ++i) { participants[i].fake_frame()->CopyFrom(frames[i]); participants[i].set_fake_info(frame_info[i]); } const auto mixer = AudioMixerImpl::Create(kSourcesToMix); for (int i = 0; i < kAudioSources; ++i) { EXPECT_TRUE(mixer->AddSource(&participants[i])); EXPECT_CALL(participants[i], GetAudioFrameWithInfo(kDefaultSampleRateHz, _)) .Times(Exactly(1)); } mixer->Mix(1, &frame_for_mixing); std::vector expected_status = {false, false, false, true, true}; for (int i = 0; i < kAudioSources; ++i) { EXPECT_EQ(expected_status[i], mixer->GetAudioSourceMixabilityStatusForTest(&participants[i])) << "Wrong mix status for source #" << i << " is wrong"; } } TEST(AudioMixer, UnmutedShouldMixBeforeLoud) { constexpr int kAudioSources = AudioMixerImpl::kDefaultNumberOfMixedAudioSources + 1; std::vector frames(kAudioSources); for (auto& frame : frames) { ResetFrame(&frame); } std::vector frame_info( kAudioSources, AudioMixer::Source::AudioFrameInfo::kNormal); frame_info[0] = AudioMixer::Source::AudioFrameInfo::kMuted; int16_t* frame_data = frames[0].mutable_data(); std::fill(frame_data, frame_data + kDefaultSampleRateHz / 100, std::numeric_limits::max()); std::vector expected_status(kAudioSources, true); expected_status[0] = false; MixAndCompare(frames, frame_info, expected_status); } TEST(AudioMixer, MixingRateShouldBeDecidedByRateCalculator) { constexpr int kOutputRate = 22000; const auto mixer = AudioMixerImpl::Create(std::unique_ptr( new CustomRateCalculator(kOutputRate)), true); MockMixerAudioSource audio_source; mixer->AddSource(&audio_source); ResetFrame(audio_source.fake_frame()); EXPECT_CALL(audio_source, GetAudioFrameWithInfo(kOutputRate, _)) .Times(Exactly(1)); mixer->Mix(1, &frame_for_mixing); } TEST(AudioMixer, ZeroSourceRateShouldBeDecidedByRateCalculator) { constexpr int kOutputRate = 8000; const auto mixer = AudioMixerImpl::Create(std::unique_ptr( new CustomRateCalculator(kOutputRate)), true); mixer->Mix(1, &frame_for_mixing); EXPECT_EQ(kOutputRate, frame_for_mixing.sample_rate_hz_); } TEST(AudioMixer, NoLimiterBasicApiCalls) { const auto mixer = AudioMixerImpl::Create( std::unique_ptr(new DefaultOutputRateCalculator()), false); mixer->Mix(1, &frame_for_mixing); } TEST(AudioMixer, AnyRateIsPossibleWithNoLimiter) { // No APM limiter means no AudioProcessing::NativeRate restriction // on mixing rate. The rate has to be divisible by 100 since we use // 10 ms frames, though. for (const auto rate : {8000, 20000, 24000, 32000, 44100}) { for (const size_t number_of_channels : {1, 2}) { for (const auto number_of_sources : {0, 1, 2, 3, 4}) { SCOPED_TRACE( ProduceDebugText(rate, number_of_sources, number_of_sources)); const auto mixer = AudioMixerImpl::Create(std::unique_ptr( new CustomRateCalculator(rate)), false); std::vector sources(number_of_sources); for (auto& source : sources) { ResetFrame(source.fake_frame()); mixer->AddSource(&source); } mixer->Mix(number_of_channels, &frame_for_mixing); EXPECT_EQ(rate, frame_for_mixing.sample_rate_hz_); EXPECT_EQ(number_of_channels, frame_for_mixing.num_channels_); } } } } TEST(AudioMixer, MultipleChannelsOneParticipant) { // Set up a participant with a 6-channel frame, and make sure a 6-channel // frame with the right sample values comes out from the mixer. There are 2 // Mix calls because of ramp-up. constexpr size_t kNumberOfChannels = 6; MockMixerAudioSource source; ResetFrame(source.fake_frame()); const auto mixer = AudioMixerImpl::Create(); mixer->AddSource(&source); mixer->Mix(1, &frame_for_mixing); auto* frame = source.fake_frame(); frame->num_channels_ = kNumberOfChannels; std::fill(frame->mutable_data(), frame->mutable_data() + AudioFrame::kMaxDataSizeSamples, 0); for (size_t i = 0; i < kNumberOfChannels; ++i) { frame->mutable_data()[100 * frame->num_channels_ + i] = 1000 * i; } mixer->Mix(kNumberOfChannels, &frame_for_mixing); EXPECT_EQ(frame_for_mixing.num_channels_, kNumberOfChannels); for (size_t i = 0; i < kNumberOfChannels; ++i) { EXPECT_EQ(frame_for_mixing.data()[100 * frame_for_mixing.num_channels_ + i], static_cast(1000 * i)); } } TEST(AudioMixer, MultipleChannelsManyParticipants) { // Sets up 2 participants. One has a 6-channel frame. Make sure a 6-channel // frame with the right sample values comes out from the mixer. There are 2 // Mix calls because of ramp-up. constexpr size_t kNumberOfChannels = 6; MockMixerAudioSource source; const auto mixer = AudioMixerImpl::Create(); mixer->AddSource(&source); ResetFrame(source.fake_frame()); mixer->Mix(1, &frame_for_mixing); auto* frame = source.fake_frame(); frame->num_channels_ = kNumberOfChannels; std::fill(frame->mutable_data(), frame->mutable_data() + AudioFrame::kMaxDataSizeSamples, 0); for (size_t i = 0; i < kNumberOfChannels; ++i) { frame->mutable_data()[100 * frame->num_channels_ + i] = 1000 * i; } MockMixerAudioSource other_source; ResetFrame(other_source.fake_frame()); mixer->AddSource(&other_source); mixer->Mix(kNumberOfChannels, &frame_for_mixing); EXPECT_EQ(frame_for_mixing.num_channels_, kNumberOfChannels); for (size_t i = 0; i < kNumberOfChannels; ++i) { EXPECT_EQ(frame_for_mixing.data()[100 * frame_for_mixing.num_channels_ + i], static_cast(1000 * i)); } } TEST(AudioMixer, ShouldIncludeRtpPacketInfoFromAllMixedSources) { const uint32_t kSsrc0 = 10; const uint32_t kSsrc1 = 11; const uint32_t kSsrc2 = 12; const uint32_t kCsrc0 = 20; const uint32_t kCsrc1 = 21; const uint32_t kCsrc2 = 22; const uint32_t kCsrc3 = 23; const int kAudioLevel0 = 10; const int kAudioLevel1 = 40; const absl::optional kAudioLevel2 = absl::nullopt; const uint32_t kRtpTimestamp0 = 300; const uint32_t kRtpTimestamp1 = 400; const Timestamp kReceiveTime0 = Timestamp::Millis(10); const Timestamp kReceiveTime1 = Timestamp::Millis(20); RtpPacketInfo p0(kSsrc0, {kCsrc0, kCsrc1}, kRtpTimestamp0, kReceiveTime0); p0.set_audio_level(kAudioLevel0); RtpPacketInfo p1(kSsrc1, {kCsrc2}, kRtpTimestamp1, kReceiveTime1); p1.set_audio_level(kAudioLevel1); RtpPacketInfo p2(kSsrc2, {kCsrc3}, kRtpTimestamp1, kReceiveTime1); p2.set_audio_level(kAudioLevel2); const auto mixer = AudioMixerImpl::Create(); MockMixerAudioSource source; source.set_packet_infos(RtpPacketInfos({p0})); mixer->AddSource(&source); ResetFrame(source.fake_frame()); mixer->Mix(1, &frame_for_mixing); MockMixerAudioSource other_source; other_source.set_packet_infos(RtpPacketInfos({p1, p2})); ResetFrame(other_source.fake_frame()); mixer->AddSource(&other_source); mixer->Mix(/*number_of_channels=*/1, &frame_for_mixing); EXPECT_THAT(frame_for_mixing.packet_infos_, UnorderedElementsAre(p0, p1, p2)); } TEST(AudioMixer, MixerShouldIncludeRtpPacketInfoFromMixedSourcesOnly) { const uint32_t kSsrc0 = 10; const uint32_t kSsrc1 = 11; const uint32_t kSsrc2 = 21; const uint32_t kCsrc0 = 30; const uint32_t kCsrc1 = 31; const uint32_t kCsrc2 = 32; const uint32_t kCsrc3 = 33; const int kAudioLevel0 = 10; const absl::optional kAudioLevelMissing = absl::nullopt; const uint32_t kRtpTimestamp0 = 300; const uint32_t kRtpTimestamp1 = 400; const Timestamp kReceiveTime0 = Timestamp::Millis(10); const Timestamp kReceiveTime1 = Timestamp::Millis(20); RtpPacketInfo p0(kSsrc0, {kCsrc0, kCsrc1}, kRtpTimestamp0, kReceiveTime0); p0.set_audio_level(kAudioLevel0); RtpPacketInfo p1(kSsrc1, {kCsrc2}, kRtpTimestamp1, kReceiveTime1); p1.set_audio_level(kAudioLevelMissing); RtpPacketInfo p2(kSsrc2, {kCsrc3}, kRtpTimestamp1, kReceiveTime1); p2.set_audio_level(kAudioLevelMissing); const auto mixer = AudioMixerImpl::Create(/*max_sources_to_mix=*/2); MockMixerAudioSource source1; source1.set_packet_infos(RtpPacketInfos({p0})); mixer->AddSource(&source1); ResetFrame(source1.fake_frame()); mixer->Mix(1, &frame_for_mixing); MockMixerAudioSource source2; source2.set_packet_infos(RtpPacketInfos({p1})); ResetFrame(source2.fake_frame()); mixer->AddSource(&source2); // The mixer prioritizes kVadActive over kVadPassive. // We limit the number of sources to mix to 2 and set the third source's VAD // activity to kVadPassive so that it will not be added to the mix. MockMixerAudioSource source3; source3.set_packet_infos(RtpPacketInfos({p2})); ResetFrame(source3.fake_frame()); source3.fake_frame()->vad_activity_ = AudioFrame::kVadPassive; mixer->AddSource(&source3); mixer->Mix(/*number_of_channels=*/1, &frame_for_mixing); EXPECT_THAT(frame_for_mixing.packet_infos_, UnorderedElementsAre(p0, p1)); } class HighOutputRateCalculator : public OutputRateCalculator { public: static const int kDefaultFrequency = 76000; int CalculateOutputRateFromRange( rtc::ArrayView preferred_sample_rates) override { return kDefaultFrequency; } ~HighOutputRateCalculator() override {} }; const int HighOutputRateCalculator::kDefaultFrequency; TEST(AudioMixerDeathTest, MultipleChannelsAndHighRate) { constexpr size_t kSamplesPerChannel = HighOutputRateCalculator::kDefaultFrequency / 100; // As many channels as an AudioFrame can fit: constexpr size_t kNumberOfChannels = AudioFrame::kMaxDataSizeSamples / kSamplesPerChannel; MockMixerAudioSource source; const auto mixer = AudioMixerImpl::Create( std::make_unique(), true); mixer->AddSource(&source); ResetFrame(source.fake_frame()); mixer->Mix(1, &frame_for_mixing); auto* frame = source.fake_frame(); frame->num_channels_ = kNumberOfChannels; frame->sample_rate_hz_ = HighOutputRateCalculator::kDefaultFrequency; frame->samples_per_channel_ = kSamplesPerChannel; std::fill(frame->mutable_data(), frame->mutable_data() + AudioFrame::kMaxDataSizeSamples, 0); MockMixerAudioSource other_source; ResetFrame(other_source.fake_frame()); auto* other_frame = other_source.fake_frame(); other_frame->num_channels_ = kNumberOfChannels; other_frame->sample_rate_hz_ = HighOutputRateCalculator::kDefaultFrequency; other_frame->samples_per_channel_ = kSamplesPerChannel; mixer->AddSource(&other_source); #if RTC_DCHECK_IS_ON && GTEST_HAS_DEATH_TEST && !defined(WEBRTC_ANDROID) EXPECT_DEATH(mixer->Mix(kNumberOfChannels, &frame_for_mixing), ""); #elif !RTC_DCHECK_IS_ON mixer->Mix(kNumberOfChannels, &frame_for_mixing); EXPECT_EQ(frame_for_mixing.num_channels_, kNumberOfChannels); EXPECT_EQ(frame_for_mixing.sample_rate_hz_, HighOutputRateCalculator::kDefaultFrequency); #endif } } // namespace webrtc