OLD | NEW |
(Empty) | |
| 1 /* |
| 2 * Copyright (c) 2016 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 |
| 11 #include <memory> |
| 12 |
| 13 #include "testing/gmock/include/gmock/gmock.h" |
| 14 |
| 15 #include "webrtc/modules/audio_conference_mixer/source/audio_frame_manipulator.h
" |
| 16 #include "webrtc/modules/audio_mixer/audio_mixer.h" |
| 17 #include "webrtc/modules/audio_mixer/include/audio_mixer_defines.h" |
| 18 #include "webrtc/modules/audio_mixer/include/new_audio_conference_mixer.h" |
| 19 #include "webrtc/modules/audio_mixer/source/new_audio_conference_mixer_impl.h" |
| 20 |
| 21 using testing::_; |
| 22 using testing::AtLeast; |
| 23 using testing::Invoke; |
| 24 using testing::Return; |
| 25 |
| 26 using webrtc::voe::AudioMixer; |
| 27 |
| 28 namespace webrtc { |
| 29 class MockMixerAudioSource : public MixerAudioSource { |
| 30 public: |
| 31 MockMixerAudioSource() { |
| 32 ON_CALL(*this, GetAudioFrame(_, _)) |
| 33 .WillByDefault(Invoke(this, &MockMixerAudioSource::FakeAudioFrame)); |
| 34 } |
| 35 MOCK_METHOD2(GetAudioFrame, |
| 36 int32_t(const int32_t id, AudioFrame* audio_frame)); |
| 37 MOCK_CONST_METHOD1(NeededFrequency, int32_t(const int32_t id)); |
| 38 AudioFrame* fake_frame() { return &fake_frame_; } |
| 39 |
| 40 private: |
| 41 AudioFrame fake_frame_; |
| 42 int32_t FakeAudioFrame(const int32_t id, AudioFrame* audio_frame) { |
| 43 audio_frame->CopyFrom(fake_frame_); |
| 44 return 0; |
| 45 } |
| 46 }; |
| 47 |
| 48 class BothMixersTest : public testing::Test { |
| 49 protected: |
| 50 BothMixersTest() { |
| 51 // Create an OutputMixer. |
| 52 AudioMixer::Create(audio_mixer_, kId); |
| 53 |
| 54 // Create one mixer participant and add it to the mixer. |
| 55 EXPECT_EQ(0, audio_mixer_->SetMixabilityStatus(participant_, true)); |
| 56 |
| 57 // Each iteration, the participant will return a frame with this content: |
| 58 participant_.fake_frame()->id_ = 1; |
| 59 participant_.fake_frame()->sample_rate_hz_ = kSampleRateHz; |
| 60 participant_.fake_frame()->speech_type_ = AudioFrame::kNormalSpeech; |
| 61 participant_.fake_frame()->vad_activity_ = AudioFrame::kVadActive; |
| 62 participant_.fake_frame()->num_channels_ = 1; |
| 63 |
| 64 // We modify one sample within the RampIn window and one sample |
| 65 // outside of it. |
| 66 participant_.fake_frame()->data_[10] = 100; |
| 67 participant_.fake_frame()->data_[20] = -200; |
| 68 participant_.fake_frame()->data_[30] = 300; |
| 69 participant_.fake_frame()->data_[90] = -400; |
| 70 |
| 71 // Frame duration 10ms. |
| 72 participant_.fake_frame()->samples_per_channel_ = kSampleRateHz / 100; |
| 73 EXPECT_CALL(participant_, NeededFrequency(_)) |
| 74 .WillRepeatedly(Return(kSampleRateHz)); |
| 75 } |
| 76 |
| 77 ~BothMixersTest() { AudioMixer::Destroy(audio_mixer_); } |
| 78 |
| 79 // Mark the participant as 'unmixed' last round. |
| 80 void ResetAudioSource() { participant_._mixHistory->SetIsMixed(false); } |
| 81 |
| 82 AudioMixer* audio_mixer_; |
| 83 MockMixerAudioSource participant_; |
| 84 AudioFrame mixing_round_frame, mixed_results_frame_; |
| 85 |
| 86 constexpr static int kSampleRateHz = 48000; |
| 87 constexpr static int kId = 1; |
| 88 }; |
| 89 |
| 90 TEST(AudioMixer, AnonymousAndNamed) { |
| 91 constexpr int kId = 1; |
| 92 // Should not matter even if partipants are more than |
| 93 // kMaximumAmountOfMixedAudioSources. |
| 94 constexpr int kNamed = |
| 95 NewAudioConferenceMixer::kMaximumAmountOfMixedAudioSources + 1; |
| 96 constexpr int kAnonymous = |
| 97 NewAudioConferenceMixer::kMaximumAmountOfMixedAudioSources + 1; |
| 98 |
| 99 std::unique_ptr<NewAudioConferenceMixer> mixer( |
| 100 NewAudioConferenceMixer::Create(kId)); |
| 101 |
| 102 MockMixerAudioSource named[kNamed]; |
| 103 MockMixerAudioSource anonymous[kAnonymous]; |
| 104 |
| 105 for (int i = 0; i < kNamed; ++i) { |
| 106 EXPECT_EQ(0, mixer->SetMixabilityStatus(&named[i], true)); |
| 107 EXPECT_TRUE(mixer->MixabilityStatus(named[i])); |
| 108 } |
| 109 |
| 110 for (int i = 0; i < kAnonymous; ++i) { |
| 111 // AudioSource must be registered before turning it into anonymous. |
| 112 EXPECT_EQ(-1, mixer->SetAnonymousMixabilityStatus(&anonymous[i], true)); |
| 113 EXPECT_EQ(0, mixer->SetMixabilityStatus(&anonymous[i], true)); |
| 114 EXPECT_TRUE(mixer->MixabilityStatus(anonymous[i])); |
| 115 EXPECT_FALSE(mixer->AnonymousMixabilityStatus(anonymous[i])); |
| 116 |
| 117 EXPECT_EQ(0, mixer->SetAnonymousMixabilityStatus(&anonymous[i], true)); |
| 118 EXPECT_TRUE(mixer->AnonymousMixabilityStatus(anonymous[i])); |
| 119 |
| 120 // Anonymous participants do not show status by MixabilityStatus. |
| 121 EXPECT_FALSE(mixer->MixabilityStatus(anonymous[i])); |
| 122 } |
| 123 |
| 124 for (int i = 0; i < kNamed; ++i) { |
| 125 EXPECT_EQ(0, mixer->SetMixabilityStatus(&named[i], false)); |
| 126 EXPECT_FALSE(mixer->MixabilityStatus(named[i])); |
| 127 } |
| 128 |
| 129 for (int i = 0; i < kAnonymous - 1; i++) { |
| 130 EXPECT_EQ(0, mixer->SetAnonymousMixabilityStatus(&anonymous[i], false)); |
| 131 EXPECT_FALSE(mixer->AnonymousMixabilityStatus(anonymous[i])); |
| 132 |
| 133 // SetAnonymousMixabilityStatus(anonymous, false) moves anonymous to the |
| 134 // named group. |
| 135 EXPECT_TRUE(mixer->MixabilityStatus(anonymous[i])); |
| 136 } |
| 137 |
| 138 // SetMixabilityStatus(anonymous, false) will remove anonymous from both |
| 139 // anonymous and named groups. |
| 140 EXPECT_EQ(0, mixer->SetMixabilityStatus(&anonymous[kAnonymous - 1], false)); |
| 141 EXPECT_FALSE(mixer->AnonymousMixabilityStatus(anonymous[kAnonymous - 1])); |
| 142 EXPECT_FALSE(mixer->MixabilityStatus(anonymous[kAnonymous - 1])); |
| 143 } |
| 144 |
| 145 TEST(AudioMixer, LargestEnergyVadActiveMixed) { |
| 146 const int kId = 1; |
| 147 const int kAudioSources = |
| 148 NewAudioConferenceMixer::kMaximumAmountOfMixedAudioSources + 3; |
| 149 const int kSampleRateHz = 32000; |
| 150 |
| 151 std::unique_ptr<NewAudioConferenceMixer> mixer( |
| 152 NewAudioConferenceMixer::Create(kId)); |
| 153 |
| 154 MockMixerAudioSource participants[kAudioSources]; |
| 155 |
| 156 for (int i = 0; i < kAudioSources; ++i) { |
| 157 participants[i].fake_frame()->id_ = i; |
| 158 participants[i].fake_frame()->sample_rate_hz_ = kSampleRateHz; |
| 159 participants[i].fake_frame()->speech_type_ = AudioFrame::kNormalSpeech; |
| 160 participants[i].fake_frame()->vad_activity_ = AudioFrame::kVadActive; |
| 161 participants[i].fake_frame()->num_channels_ = 1; |
| 162 |
| 163 // Frame duration 10ms. |
| 164 participants[i].fake_frame()->samples_per_channel_ = kSampleRateHz / 100; |
| 165 |
| 166 // We set the 80-th sample value since the first 80 samples may be |
| 167 // modified by a ramped-in window. |
| 168 participants[i].fake_frame()->data_[80] = i; |
| 169 |
| 170 EXPECT_EQ(0, mixer->SetMixabilityStatus(&participants[i], true)); |
| 171 EXPECT_CALL(participants[i], GetAudioFrame(_, _)).Times(AtLeast(1)); |
| 172 EXPECT_CALL(participants[i], NeededFrequency(_)) |
| 173 .WillRepeatedly(Return(kSampleRateHz)); |
| 174 } |
| 175 |
| 176 // Last participant gives audio frame with passive VAD, although it has the |
| 177 // largest energy. |
| 178 participants[kAudioSources - 1].fake_frame()->vad_activity_ = |
| 179 AudioFrame::kVadPassive; |
| 180 |
| 181 AudioFrame audio_frame; |
| 182 mixer->Mix(&audio_frame); |
| 183 |
| 184 for (int i = 0; i < kAudioSources; ++i) { |
| 185 bool is_mixed = participants[i].IsMixed(); |
| 186 if (i == kAudioSources - 1 || |
| 187 i < kAudioSources - 1 - |
| 188 NewAudioConferenceMixer::kMaximumAmountOfMixedAudioSources) { |
| 189 EXPECT_FALSE(is_mixed) << "Mixing status of AudioSource #" << i |
| 190 << " wrong."; |
| 191 } else { |
| 192 EXPECT_TRUE(is_mixed) << "Mixing status of AudioSource #" << i |
| 193 << " wrong."; |
| 194 } |
| 195 } |
| 196 } |
| 197 |
| 198 TEST_F(BothMixersTest, CompareInitialFrameAudio) { |
| 199 EXPECT_CALL(participant_, GetAudioFrame(_, _)).Times(AtLeast(1)); |
| 200 |
| 201 // Make sure the participant is marked as 'non-mixed' so that it is |
| 202 // ramped in next round. |
| 203 ResetAudioSource(); |
| 204 |
| 205 // Construct the expected sound for the first mixing round. |
| 206 mixing_round_frame.CopyFrom(*participant_.fake_frame()); |
| 207 RampIn(mixing_round_frame); |
| 208 |
| 209 // Mix frames and put the result into a frame. |
| 210 audio_mixer_->MixActiveChannels(); |
| 211 audio_mixer_->GetMixedAudio(kSampleRateHz, 1, &mixed_results_frame_); |
| 212 |
| 213 // Compare the received frame with the expected. |
| 214 EXPECT_EQ(mixing_round_frame.sample_rate_hz_, |
| 215 mixed_results_frame_.sample_rate_hz_); |
| 216 EXPECT_EQ(mixing_round_frame.num_channels_, |
| 217 mixed_results_frame_.num_channels_); |
| 218 EXPECT_EQ(mixing_round_frame.samples_per_channel_, |
| 219 mixed_results_frame_.samples_per_channel_); |
| 220 EXPECT_EQ(0, memcmp(mixing_round_frame.data_, mixed_results_frame_.data_, |
| 221 sizeof(mixing_round_frame.data_))); |
| 222 } |
| 223 |
| 224 TEST_F(BothMixersTest, CompareSecondFrameAudio) { |
| 225 EXPECT_CALL(participant_, GetAudioFrame(_, _)).Times(AtLeast(1)); |
| 226 |
| 227 // Make sure the participant is marked as 'non-mixed' so that it is |
| 228 // ramped in next round. |
| 229 ResetAudioSource(); |
| 230 |
| 231 // Do one mixing iteration. |
| 232 audio_mixer_->MixActiveChannels(); |
| 233 |
| 234 // Mix frames a second time and compare with the expected frame |
| 235 // (which is the participant's frame). |
| 236 audio_mixer_->MixActiveChannels(); |
| 237 audio_mixer_->GetMixedAudio(kSampleRateHz, 1, &mixed_results_frame_); |
| 238 EXPECT_EQ(0, |
| 239 memcmp(participant_.fake_frame()->data_, mixed_results_frame_.data_, |
| 240 sizeof(mixing_round_frame.data_))); |
| 241 } |
| 242 |
| 243 } // namespace webrtc |
OLD | NEW |