Chromium Code Reviews| Index: webrtc/modules/audio_mixer/test/audio_mixer_unittest.cc |
| diff --git a/webrtc/modules/audio_mixer/test/audio_mixer_unittest.cc b/webrtc/modules/audio_mixer/test/audio_mixer_unittest.cc |
| new file mode 100644 |
| index 0000000000000000000000000000000000000000..96a04276f2faf071f904a9f50c36b5f7703b3215 |
| --- /dev/null |
| +++ b/webrtc/modules/audio_mixer/test/audio_mixer_unittest.cc |
| @@ -0,0 +1,244 @@ |
| +/* |
| + * Copyright (c) 2015 The WebRTC project authors. All Rights Reserved. |
|
tommi
2016/07/06 19:42:17
update this header (it's 2016)
aleloi
2016/07/07 09:20:16
Done. I also updated the other files (they are cop
|
| + * |
| + * 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 <memory> |
| + |
| +#include "testing/gmock/include/gmock/gmock.h" |
| + |
| +#include "webrtc/modules/audio_conference_mixer/source/audio_frame_manipulator.h" |
| +#include "webrtc/modules/audio_mixer/audio_mixer.h" |
| +#include "webrtc/modules/audio_mixer/include/new_audio_conference_mixer.h" |
| +#include "webrtc/modules/audio_mixer/include/audio_mixer_defines.h" |
|
tommi
2016/07/06 19:42:17
fix include order. git cl format could help.
aleloi
2016/07/07 09:20:16
Done.
|
| +#include "webrtc/modules/audio_mixer/source/new_audio_conference_mixer_impl.h" |
| + |
| +namespace webrtc { |
|
ivoc
2016/07/07 13:46:16
Move this below the "using" lines below.
aleloi
2016/07/07 14:29:12
Done.
|
| + |
| +using testing::_; |
| +using testing::AtLeast; |
| +using testing::Invoke; |
| +using testing::Return; |
| + |
| +using voe::AudioMixer; |
| + |
| +class MockMixerAudioSource : public MixerAudioSource { |
| + public: |
| + MockMixerAudioSource() { |
| + ON_CALL(*this, GetAudioFrame(_, _)) |
| + .WillByDefault(Invoke(this, &MockMixerAudioSource::FakeAudioFrame)); |
| + } |
| + MOCK_METHOD2(GetAudioFrame, |
| + int32_t(const int32_t id, AudioFrame* audio_frame)); |
| + MOCK_CONST_METHOD1(NeededFrequency, int32_t(const int32_t id)); |
| + AudioFrame* fake_frame() { return &fake_frame_; } |
| + |
| + private: |
| + AudioFrame fake_frame_; |
| + int32_t FakeAudioFrame(const int32_t id, AudioFrame* audio_frame) { |
| + audio_frame->CopyFrom(fake_frame_); |
| + return 0; |
| + } |
| +}; |
| + |
| +class BothMixersTest : public testing::Test { |
| + protected: |
| + BothMixersTest() { |
| + // Create an OutputMixer. |
| + AudioMixer::Create(audio_mixer_, kId); |
| + |
| + // Create one mixer participant and add it to the mixer. |
| + EXPECT_EQ(0, audio_mixer_->SetMixabilityStatus(participant_, true)); |
| + |
| + // Each iteration, the participant will return a frame with this content: |
| + participant_.fake_frame()->id_ = 1; |
| + participant_.fake_frame()->sample_rate_hz_ = kSampleRateHz; |
| + participant_.fake_frame()->speech_type_ = AudioFrame::kNormalSpeech; |
| + participant_.fake_frame()->vad_activity_ = AudioFrame::kVadActive; |
| + participant_.fake_frame()->num_channels_ = 1; |
| + |
| + // We modify one sample within the RampIn window and one sample |
| + // outside of it. |
| + participant_.fake_frame()->data_[10] = 100; |
| + participant_.fake_frame()->data_[20] = -200; |
| + participant_.fake_frame()->data_[30] = 300; |
| + participant_.fake_frame()->data_[90] = -400; |
| + |
| + // Frame duration 10ms. |
| + participant_.fake_frame()->samples_per_channel_ = kSampleRateHz / 100; |
| + EXPECT_CALL(participant_, NeededFrequency(_)) |
| + .WillRepeatedly(Return(kSampleRateHz)); |
| + } |
| + |
| + ~BothMixersTest() { AudioMixer::Destroy(audio_mixer_); } |
| + |
| + // Mark the participant as 'unmixed' last round. |
| + void ResetParticipant() { participant_._mixHistory->SetIsMixed(false); } |
| + |
| + AudioMixer* audio_mixer_; |
| + MockMixerAudioSource participant_; |
| + AudioFrame mixing_round_frame, mixed_results_frame_; |
| + |
| + constexpr static int kSampleRateHz = 48000; |
| + constexpr static int kId = 1; |
| +}; |
| + |
| +TEST(AudioMixer, AnonymousAndNamed) { |
| + const int kId = 1; |
|
tommi
2016/07/06 19:42:17
nit: make this 'static const int kId'. For non st
aleloi
2016/07/07 09:20:16
Done. The test was mostly copied from modules/audi
|
| + // Should not matter even if partipants are more than |
| + // kMaximumAmountOfMixedParticipants. |
| + const int kNamed = |
| + NewAudioConferenceMixer::kMaximumAmountOfMixedParticipants + 1; |
| + const int kAnonymous = |
| + NewAudioConferenceMixer::kMaximumAmountOfMixedParticipants + 1; |
| + |
| + std::unique_ptr<NewAudioConferenceMixer> mixer( |
| + NewAudioConferenceMixer::Create(kId)); |
| + |
| + MockMixerAudioSource named[kNamed]; |
| + MockMixerAudioSource anonymous[kAnonymous]; |
| + |
| + for (int i = 0; i < kNamed; ++i) { |
| + EXPECT_EQ(0, mixer->SetMixabilityStatus(&named[i], true)); |
| + EXPECT_TRUE(mixer->MixabilityStatus(named[i])); |
| + } |
| + |
| + for (int i = 0; i < kAnonymous; ++i) { |
| + // Participant must be registered before turning it into anonymous. |
| + EXPECT_EQ(-1, mixer->SetAnonymousMixabilityStatus(&anonymous[i], true)); |
| + EXPECT_EQ(0, mixer->SetMixabilityStatus(&anonymous[i], true)); |
| + EXPECT_TRUE(mixer->MixabilityStatus(anonymous[i])); |
| + EXPECT_FALSE(mixer->AnonymousMixabilityStatus(anonymous[i])); |
| + |
| + EXPECT_EQ(0, mixer->SetAnonymousMixabilityStatus(&anonymous[i], true)); |
| + EXPECT_TRUE(mixer->AnonymousMixabilityStatus(anonymous[i])); |
| + |
| + // Anonymous participants do not show status by MixabilityStatus. |
| + EXPECT_FALSE(mixer->MixabilityStatus(anonymous[i])); |
| + } |
| + |
| + for (int i = 0; i < kNamed; ++i) { |
| + EXPECT_EQ(0, mixer->SetMixabilityStatus(&named[i], false)); |
| + EXPECT_FALSE(mixer->MixabilityStatus(named[i])); |
| + } |
| + |
| + for (int i = 0; i < kAnonymous - 1; i++) { |
| + EXPECT_EQ(0, mixer->SetAnonymousMixabilityStatus(&anonymous[i], false)); |
| + EXPECT_FALSE(mixer->AnonymousMixabilityStatus(anonymous[i])); |
| + |
| + // SetAnonymousMixabilityStatus(anonymous, false) moves anonymous to the |
| + // named group. |
| + EXPECT_TRUE(mixer->MixabilityStatus(anonymous[i])); |
| + } |
| + |
| + // SetMixabilityStatus(anonymous, false) will remove anonymous from both |
| + // anonymous and named groups. |
| + EXPECT_EQ(0, mixer->SetMixabilityStatus(&anonymous[kAnonymous - 1], false)); |
| + EXPECT_FALSE(mixer->AnonymousMixabilityStatus(anonymous[kAnonymous - 1])); |
| + EXPECT_FALSE(mixer->MixabilityStatus(anonymous[kAnonymous - 1])); |
| +} |
| + |
| +TEST(AudioMixer, LargestEnergyVadActiveMixed) { |
| + const int kId = 1; |
| + const int kParticipants = |
| + NewAudioConferenceMixer::kMaximumAmountOfMixedParticipants + 3; |
| + const int kSampleRateHz = 32000; |
| + |
| + std::unique_ptr<NewAudioConferenceMixer> mixer( |
| + NewAudioConferenceMixer::Create(kId)); |
| + |
| + MockMixerAudioSource participants[kParticipants]; |
| + |
| + for (int i = 0; i < kParticipants; ++i) { |
| + participants[i].fake_frame()->id_ = i; |
| + participants[i].fake_frame()->sample_rate_hz_ = kSampleRateHz; |
| + participants[i].fake_frame()->speech_type_ = AudioFrame::kNormalSpeech; |
| + participants[i].fake_frame()->vad_activity_ = AudioFrame::kVadActive; |
| + participants[i].fake_frame()->num_channels_ = 1; |
| + |
| + // Frame duration 10ms. |
| + participants[i].fake_frame()->samples_per_channel_ = kSampleRateHz / 100; |
| + |
| + // We set the 80-th sample value since the first 80 samples may be |
| + // modified by a ramped-in window. |
| + participants[i].fake_frame()->data_[80] = i; |
| + |
| + EXPECT_EQ(0, mixer->SetMixabilityStatus(&participants[i], true)); |
| + EXPECT_CALL(participants[i], GetAudioFrame(_, _)).Times(AtLeast(1)); |
| + EXPECT_CALL(participants[i], NeededFrequency(_)) |
| + .WillRepeatedly(Return(kSampleRateHz)); |
| + } |
| + |
| + // Last participant gives audio frame with passive VAD, although it has the |
| + // largest energy. |
| + participants[kParticipants - 1].fake_frame()->vad_activity_ = |
| + AudioFrame::kVadPassive; |
| + |
| + AudioFrame audio_frame; |
| + mixer->Mix(&audio_frame); |
| + |
| + for (int i = 0; i < kParticipants; ++i) { |
| + bool is_mixed = participants[i].IsMixed(); |
| + if (i == kParticipants - 1 || |
| + i < kParticipants - 1 - |
| + NewAudioConferenceMixer::kMaximumAmountOfMixedParticipants) { |
| + EXPECT_FALSE(is_mixed) << "Mixing status of Participant #" << i |
| + << " wrong."; |
| + } else { |
| + EXPECT_TRUE(is_mixed) << "Mixing status of Participant #" << i |
| + << " wrong."; |
| + } |
| + } |
| +} |
| + |
| +TEST_F(BothMixersTest, CompareInitialFrameAudio) { |
| + EXPECT_CALL(participant_, GetAudioFrame(_, _)).Times(AtLeast(1)); |
| + |
| + // Make sure the participant is marked as 'non-mixed' so that it is |
| + // ramped in next round. |
| + ResetParticipant(); |
| + |
| + // Construct the expected sound for the first mixing round. |
| + mixing_round_frame.CopyFrom(*participant_.fake_frame()); |
| + RampIn(mixing_round_frame); |
| + |
| + // Mix frames and put the result into a frame. |
| + audio_mixer_->MixActiveChannels(); |
| + audio_mixer_->GetMixedAudio(kSampleRateHz, 1, &mixed_results_frame_); |
| + |
| + // Compare the received frame with the expected. |
| + EXPECT_EQ(mixing_round_frame.sample_rate_hz_, |
| + mixed_results_frame_.sample_rate_hz_); |
| + EXPECT_EQ(mixing_round_frame.num_channels_, |
| + mixed_results_frame_.num_channels_); |
| + EXPECT_EQ(mixing_round_frame.samples_per_channel_, |
| + mixed_results_frame_.samples_per_channel_); |
| + EXPECT_EQ(0, memcmp(mixing_round_frame.data_, mixed_results_frame_.data_, |
| + sizeof(mixing_round_frame.data_))); |
| +} |
| + |
| +TEST_F(BothMixersTest, CompareSecondFrameAudio) { |
| + EXPECT_CALL(participant_, GetAudioFrame(_, _)).Times(AtLeast(1)); |
| + |
| + // Make sure the participant is marked as 'non-mixed' so that it is |
| + // ramped in next round. |
| + ResetParticipant(); |
| + |
| + // Do one mixing iteration. |
| + audio_mixer_->MixActiveChannels(); |
| + |
| + // Mix frames a second time and compare with the expected frame |
| + // (which is the participant's frame). |
| + audio_mixer_->MixActiveChannels(); |
| + audio_mixer_->GetMixedAudio(kSampleRateHz, 1, &mixed_results_frame_); |
| + EXPECT_EQ(0, |
| + memcmp(participant_.fake_frame()->data_, mixed_results_frame_.data_, |
| + sizeof(mixing_round_frame.data_))); |
| +} |
| + |
| +} // namespace webrtc |