| // Copyright (c) 2012 The Chromium Authors. All rights reserved. |
| // Use of this source code is governed by a BSD-style license that can be |
| // found in the LICENSE file. |
| |
| #include "third_party/blink/renderer/modules/media/audio/audio_renderer_mixer_manager.h" |
| |
| #include <memory> |
| |
| #include "base/bind.h" |
| #include "base/callback_helpers.h" |
| #include "base/macros.h" |
| #include "base/memory/scoped_refptr.h" |
| #include "base/notreached.h" |
| #include "base/run_loop.h" |
| #include "build/build_config.h" |
| #include "media/audio/audio_device_description.h" |
| #include "media/base/audio_parameters.h" |
| #include "media/base/audio_renderer_mixer.h" |
| #include "media/base/audio_renderer_mixer_input.h" |
| #include "media/base/fake_audio_render_callback.h" |
| #include "media/base/mock_audio_renderer_sink.h" |
| #include "testing/gmock/include/gmock/gmock.h" |
| #include "testing/gtest/include/gtest/gtest.h" |
| |
| namespace blink { |
| |
| namespace { |
| const int kSampleRate = 48000; |
| const int kBufferSize = 8192; |
| const int kHardwareSampleRate = 44100; |
| const int kHardwareBufferSize = 128; |
| const media::ChannelLayout kChannelLayout = media::CHANNEL_LAYOUT_STEREO; |
| const media::ChannelLayout kAnotherChannelLayout = media::CHANNEL_LAYOUT_2_1; |
| const char* const kDefaultDeviceId = |
| media::AudioDeviceDescription::kDefaultDeviceId; |
| const char kAnotherDeviceId[] = "another-device-id"; |
| const char kMatchedDeviceId[] = "matched-device-id"; |
| const char kNonexistentDeviceId[] = "nonexistent-device-id"; |
| |
| const LocalFrameToken kFrameToken; |
| const LocalFrameToken kAnotherFrameToken; |
| } // namespace |
| |
| using media::AudioLatency; |
| using media::AudioParameters; |
| |
| class AudioRendererMixerManagerTest : public testing::Test { |
| public: |
| AudioRendererMixerManagerTest() |
| : manager_(new AudioRendererMixerManager( |
| base::BindRepeating(&AudioRendererMixerManagerTest::GetPlainSink, |
| base::Unretained(this)))) {} |
| |
| scoped_refptr<media::MockAudioRendererSink> CreateNormalSink( |
| const std::string& device_id = std::string(kDefaultDeviceId)) { |
| auto sink = base::MakeRefCounted<media::MockAudioRendererSink>( |
| device_id, media::OUTPUT_DEVICE_STATUS_OK, |
| AudioParameters(AudioParameters::AUDIO_PCM_LINEAR, kChannelLayout, |
| kHardwareSampleRate, kHardwareBufferSize)); |
| EXPECT_CALL(*sink, Stop()).Times(1); |
| return sink; |
| } |
| |
| scoped_refptr<media::MockAudioRendererSink> CreateNoDeviceSink() { |
| return base::MakeRefCounted<media::MockAudioRendererSink>( |
| kNonexistentDeviceId, media::OUTPUT_DEVICE_STATUS_ERROR_NOT_FOUND); |
| } |
| |
| scoped_refptr<media::MockAudioRendererSink> CreateMatchedDeviceSink() { |
| auto sink = base::MakeRefCounted<media::MockAudioRendererSink>( |
| kMatchedDeviceId, media::OUTPUT_DEVICE_STATUS_OK); |
| EXPECT_CALL(*sink, Stop()).Times(1); |
| return sink; |
| } |
| |
| enum class SinkUseState { kExistingSink, kNewSink }; |
| media::AudioRendererMixer* GetMixer(const LocalFrameToken& source_frame_token, |
| const media::AudioParameters& params, |
| AudioLatency::LatencyType latency, |
| const std::string& device_id, |
| SinkUseState sink_state) { |
| auto sink = GetSink( |
| source_frame_token, |
| media::AudioSinkParameters(base::UnguessableToken(), device_id)); |
| auto device_info = sink->GetOutputDeviceInfo(); |
| if (sink_state == SinkUseState::kNewSink) |
| EXPECT_CALL(*sink, Start()).Times(1); |
| return manager_->GetMixer(source_frame_token, params, latency, device_info, |
| std::move(sink)); |
| } |
| |
| void ReturnMixer(media::AudioRendererMixer* mixer) { |
| return manager_->ReturnMixer(mixer); |
| } |
| |
| scoped_refptr<media::AudioRendererMixerInput> CreateInputHelper( |
| const LocalFrameToken& source_frame_token, |
| const base::UnguessableToken& session_id, |
| const std::string& device_id, |
| media::AudioLatency::LatencyType latency, |
| const media::AudioParameters params, |
| media::AudioRendererSink::RenderCallback* callback) { |
| auto input = manager_->CreateInput(source_frame_token, session_id, |
| device_id, latency); |
| input->GetOutputDeviceInfoAsync( |
| base::DoNothing()); // Primes input, needed for tests. |
| base::RunLoop().RunUntilIdle(); |
| input->Initialize(params, callback); |
| return input; |
| } |
| |
| // Number of instantiated mixers. |
| int mixer_count() { return manager_->mixers_.size(); } |
| |
| protected: |
| scoped_refptr<media::MockAudioRendererSink> GetSink( |
| const LocalFrameToken& source_frame_token, |
| const media::AudioSinkParameters& params) { |
| if ((params.device_id == kDefaultDeviceId) || |
| (params.device_id == kAnotherDeviceId)) { |
| return mock_sink_ ? std::move(mock_sink_) |
| : CreateNormalSink(params.device_id); |
| } |
| if (params.device_id == kNonexistentDeviceId) |
| return CreateNoDeviceSink(); |
| if (params.device_id.empty()) { |
| // The sink used to get device ID from session ID if it's not empty |
| return params.session_id |
| ? CreateMatchedDeviceSink() |
| : (mock_sink_ ? std::move(mock_sink_) |
| : CreateNormalSink(params.device_id)); |
| } |
| if (params.device_id == kMatchedDeviceId) |
| return CreateMatchedDeviceSink(); |
| |
| NOTREACHED(); |
| return nullptr; |
| } |
| |
| std::unique_ptr<AudioRendererMixerManager> manager_; |
| scoped_refptr<media::MockAudioRendererSink> mock_sink_; |
| |
| private: |
| scoped_refptr<media::AudioRendererSink> GetPlainSink( |
| const LocalFrameToken& source_frame_token, |
| const media::AudioSinkParameters& params) { |
| return GetSink(source_frame_token, params); |
| } |
| |
| DISALLOW_COPY_AND_ASSIGN(AudioRendererMixerManagerTest); |
| }; |
| |
| // Verify GetMixer() and ReturnMixer() both work as expected; particularly with |
| // respect to the explicit ref counting done. |
| TEST_F(AudioRendererMixerManagerTest, GetReturnMixer) { |
| // There should be no mixers outstanding to start with. |
| EXPECT_EQ(0, mixer_count()); |
| |
| media::AudioParameters params1(media::AudioParameters::AUDIO_PCM_LINEAR, |
| kChannelLayout, kSampleRate, kBufferSize); |
| |
| media::AudioRendererMixer* mixer1 = |
| GetMixer(kFrameToken, params1, AudioLatency::LATENCY_PLAYBACK, |
| kDefaultDeviceId, SinkUseState::kNewSink); |
| ASSERT_TRUE(mixer1); |
| EXPECT_EQ(1, mixer_count()); |
| |
| // The same parameters should return the same mixer1. |
| EXPECT_EQ(mixer1, |
| GetMixer(kFrameToken, params1, AudioLatency::LATENCY_PLAYBACK, |
| kDefaultDeviceId, SinkUseState::kExistingSink)); |
| EXPECT_EQ(1, mixer_count()); |
| |
| // Return the extra mixer we just acquired. |
| ReturnMixer(mixer1); |
| EXPECT_EQ(1, mixer_count()); |
| |
| media::AudioParameters params2(AudioParameters::AUDIO_PCM_LINEAR, |
| kAnotherChannelLayout, kSampleRate * 2, |
| kBufferSize * 2); |
| media::AudioRendererMixer* mixer2 = |
| GetMixer(kFrameToken, params2, AudioLatency::LATENCY_PLAYBACK, |
| kDefaultDeviceId, SinkUseState::kNewSink); |
| ASSERT_TRUE(mixer2); |
| EXPECT_EQ(2, mixer_count()); |
| |
| // Different parameters should result in a different mixer1. |
| EXPECT_NE(mixer1, mixer2); |
| |
| // Return both outstanding mixers. |
| ReturnMixer(mixer1); |
| EXPECT_EQ(1, mixer_count()); |
| ReturnMixer(mixer2); |
| EXPECT_EQ(0, mixer_count()); |
| } |
| |
| // Verify GetMixer() correctly deduplicates mixer with irrelevant AudioParameter |
| // differences. |
| TEST_F(AudioRendererMixerManagerTest, MixerReuse) { |
| EXPECT_EQ(mixer_count(), 0); |
| |
| media::AudioParameters params1(AudioParameters::AUDIO_PCM_LINEAR, |
| kChannelLayout, kSampleRate, kBufferSize); |
| media::AudioRendererMixer* mixer1 = |
| GetMixer(kFrameToken, params1, AudioLatency::LATENCY_PLAYBACK, |
| kDefaultDeviceId, SinkUseState::kNewSink); |
| ASSERT_TRUE(mixer1); |
| EXPECT_EQ(1, mixer_count()); |
| |
| // Different sample rates, formats, bit depths, and buffer sizes should not |
| // result in a different mixer. |
| media::AudioParameters params2(AudioParameters::AUDIO_PCM_LOW_LATENCY, |
| kChannelLayout, kSampleRate * 2, |
| kBufferSize * 2); |
| media::AudioRendererMixer* mixer2 = |
| GetMixer(kFrameToken, params2, AudioLatency::LATENCY_PLAYBACK, |
| kDefaultDeviceId, SinkUseState::kExistingSink); |
| EXPECT_EQ(mixer1, mixer2); |
| EXPECT_EQ(1, mixer_count()); |
| ReturnMixer(mixer2); |
| EXPECT_EQ(1, mixer_count()); |
| |
| // Modify some parameters that do matter: channel layout |
| media::AudioParameters params3(AudioParameters::AUDIO_PCM_LOW_LATENCY, |
| kAnotherChannelLayout, kSampleRate, |
| kBufferSize); |
| ASSERT_NE(params3.channel_layout(), params1.channel_layout()); |
| media::AudioRendererMixer* mixer3 = |
| GetMixer(kFrameToken, params3, AudioLatency::LATENCY_PLAYBACK, |
| kDefaultDeviceId, SinkUseState::kNewSink); |
| EXPECT_NE(mixer1, mixer3); |
| EXPECT_EQ(2, mixer_count()); |
| ReturnMixer(mixer3); |
| EXPECT_EQ(1, mixer_count()); |
| |
| // Return final mixer. |
| ReturnMixer(mixer1); |
| EXPECT_EQ(0, mixer_count()); |
| } |
| |
| // Verify CreateInput() provides AudioRendererMixerInput with the appropriate |
| // callbacks and they are working as expected. Also, verify that separate |
| // mixers are created for separate RenderFrames, even though the |
| // AudioParameters are the same. |
| TEST_F(AudioRendererMixerManagerTest, CreateInput) { |
| media::AudioParameters params(AudioParameters::AUDIO_PCM_LINEAR, |
| kChannelLayout, kSampleRate, kBufferSize); |
| |
| // Create two mixer inputs and ensure this doesn't instantiate any mixers yet. |
| EXPECT_EQ(0, mixer_count()); |
| media::FakeAudioRenderCallback callback(0, kSampleRate); |
| mock_sink_ = CreateNormalSink(); |
| EXPECT_CALL(*mock_sink_, Start()).Times(1); |
| auto input = |
| CreateInputHelper(kFrameToken, base::UnguessableToken(), kDefaultDeviceId, |
| AudioLatency::LATENCY_PLAYBACK, params, &callback); |
| EXPECT_EQ(0, mixer_count()); |
| media::FakeAudioRenderCallback another_callback(1, kSampleRate); |
| |
| EXPECT_FALSE(!!mock_sink_); |
| mock_sink_ = CreateNormalSink(); |
| EXPECT_CALL(*mock_sink_, Start()).Times(1); |
| auto another_input = CreateInputHelper( |
| kAnotherFrameToken, base::UnguessableToken(), kDefaultDeviceId, |
| AudioLatency::LATENCY_PLAYBACK, params, &another_callback); |
| EXPECT_EQ(0, mixer_count()); |
| |
| // Implicitly test that AudioRendererMixerInput was provided with the expected |
| // callbacks needed to acquire an AudioRendererMixer and return it. |
| input->Start(); |
| EXPECT_EQ(1, mixer_count()); |
| another_input->Start(); |
| EXPECT_EQ(2, mixer_count()); |
| |
| // Destroying the inputs should destroy the mixers. |
| input->Stop(); |
| input = nullptr; |
| EXPECT_EQ(1, mixer_count()); |
| another_input->Stop(); |
| another_input = nullptr; |
| EXPECT_EQ(0, mixer_count()); |
| } |
| |
| // Verify CreateInput() provided with session id creates AudioRendererMixerInput |
| // with the appropriate callbacks and they are working as expected. |
| // |
| // TODO(grunell): |session_id| support currently requires calling the |
| // synchronous GetOutputDeviceInfo call, this is not allowed. So this test is |
| // disabled. This should be deleted in the future, https://crbug.com/870836. |
| TEST_F(AudioRendererMixerManagerTest, DISABLED_CreateInputWithSessionId) { |
| media::AudioParameters params(AudioParameters::AUDIO_PCM_LINEAR, |
| kChannelLayout, kSampleRate, kBufferSize); |
| media::FakeAudioRenderCallback callback(0, kSampleRate); |
| EXPECT_EQ(0, mixer_count()); |
| |
| // Empty device id, zero session id; |
| auto input_to_default_device = CreateInputHelper( |
| kFrameToken, base::UnguessableToken(), // session_id |
| std::string(), AudioLatency::LATENCY_PLAYBACK, params, &callback); |
| EXPECT_EQ(0, mixer_count()); |
| |
| // Specific device id, zero session id; |
| auto input_to_another_device = CreateInputHelper( |
| kFrameToken, base::UnguessableToken(), // session_id |
| kMatchedDeviceId, AudioLatency::LATENCY_PLAYBACK, params, &callback); |
| EXPECT_EQ(0, mixer_count()); |
| |
| // Specific device id, non-zero session id (to be ignored); |
| auto input_to_matched_device = CreateInputHelper( |
| kFrameToken, |
| base::UnguessableToken::Create(), // session id |
| kAnotherDeviceId, AudioLatency::LATENCY_PLAYBACK, params, &callback); |
| EXPECT_EQ(0, mixer_count()); |
| |
| // Empty device id, non-zero session id; |
| auto input_to_matched_device_with_session_id = CreateInputHelper( |
| kFrameToken, |
| base::UnguessableToken::Create(), // session id |
| std::string(), AudioLatency::LATENCY_PLAYBACK, params, &callback); |
| EXPECT_EQ(0, mixer_count()); |
| |
| // Implicitly test that AudioRendererMixerInput was provided with the expected |
| // callbacks needed to acquire an AudioRendererMixer and return it. |
| input_to_default_device->Start(); |
| EXPECT_EQ(1, mixer_count()); |
| |
| input_to_another_device->Start(); |
| EXPECT_EQ(2, mixer_count()); |
| |
| input_to_matched_device->Start(); |
| EXPECT_EQ(3, mixer_count()); |
| |
| // Should go to the same device as the input above. |
| input_to_matched_device_with_session_id->Start(); |
| EXPECT_EQ(3, mixer_count()); |
| |
| // Destroying the inputs should destroy the mixers. |
| input_to_default_device->Stop(); |
| input_to_default_device = nullptr; |
| EXPECT_EQ(2, mixer_count()); |
| input_to_another_device->Stop(); |
| input_to_another_device = nullptr; |
| EXPECT_EQ(1, mixer_count()); |
| input_to_matched_device->Stop(); |
| input_to_matched_device = nullptr; |
| EXPECT_EQ(1, mixer_count()); |
| input_to_matched_device_with_session_id->Stop(); |
| input_to_matched_device_with_session_id = nullptr; |
| EXPECT_EQ(0, mixer_count()); |
| } |
| |
| // Verify GetMixer() correctly creates different mixers with the same |
| // parameters, but different device ID. |
| TEST_F(AudioRendererMixerManagerTest, MixerDevices) { |
| EXPECT_EQ(0, mixer_count()); |
| |
| media::AudioParameters params(AudioParameters::AUDIO_PCM_LINEAR, |
| kChannelLayout, kSampleRate, kBufferSize); |
| media::AudioRendererMixer* mixer1 = |
| GetMixer(kFrameToken, params, AudioLatency::LATENCY_PLAYBACK, |
| kDefaultDeviceId, SinkUseState::kNewSink); |
| ASSERT_TRUE(mixer1); |
| EXPECT_EQ(1, mixer_count()); |
| |
| media::AudioRendererMixer* mixer2 = |
| GetMixer(kFrameToken, params, AudioLatency::LATENCY_PLAYBACK, |
| kAnotherDeviceId, SinkUseState::kNewSink); |
| ASSERT_TRUE(mixer2); |
| EXPECT_EQ(2, mixer_count()); |
| EXPECT_NE(mixer1, mixer2); |
| |
| ReturnMixer(mixer1); |
| EXPECT_EQ(1, mixer_count()); |
| ReturnMixer(mixer2); |
| EXPECT_EQ(0, mixer_count()); |
| } |
| |
| // Verify GetMixer() correctly deduplicate mixers with the same |
| // parameters and default device ID, even if one is "" and one is "default". |
| TEST_F(AudioRendererMixerManagerTest, OneMixerDifferentDefaultDeviceIDs) { |
| EXPECT_EQ(0, mixer_count()); |
| |
| media::AudioParameters params(AudioParameters::AUDIO_PCM_LINEAR, |
| kChannelLayout, kSampleRate, kBufferSize); |
| media::AudioRendererMixer* mixer1 = |
| GetMixer(kFrameToken, params, AudioLatency::LATENCY_PLAYBACK, |
| kDefaultDeviceId, SinkUseState::kNewSink); |
| ASSERT_TRUE(mixer1); |
| EXPECT_EQ(1, mixer_count()); |
| |
| media::AudioRendererMixer* mixer2 = |
| GetMixer(kFrameToken, params, AudioLatency::LATENCY_PLAYBACK, |
| std::string(), SinkUseState::kExistingSink); |
| ASSERT_TRUE(mixer2); |
| EXPECT_EQ(1, mixer_count()); |
| EXPECT_EQ(mixer1, mixer2); |
| |
| ReturnMixer(mixer1); |
| EXPECT_EQ(1, mixer_count()); |
| ReturnMixer(mixer2); |
| EXPECT_EQ(0, mixer_count()); |
| } |
| |
| // Verify that GetMixer() correctly returns a null mixer and an appropriate |
| // status code when a nonexistent device is requested. |
| TEST_F(AudioRendererMixerManagerTest, NonexistentDevice) { |
| EXPECT_EQ(0, mixer_count()); |
| |
| media::AudioParameters params(AudioParameters::AUDIO_PCM_LINEAR, |
| kChannelLayout, kSampleRate, kBufferSize); |
| |
| auto sink = |
| GetSink(kFrameToken, media::AudioSinkParameters(base::UnguessableToken(), |
| kNonexistentDeviceId)); |
| auto device_info = sink->GetOutputDeviceInfo(); |
| |
| EXPECT_EQ(media::OUTPUT_DEVICE_STATUS_ERROR_NOT_FOUND, |
| device_info.device_status()); |
| EXPECT_EQ(0, mixer_count()); |
| } |
| |
| // Verify GetMixer() correctly deduplicate mixers basing on latency |
| // requirements. |
| TEST_F(AudioRendererMixerManagerTest, LatencyMixing) { |
| EXPECT_EQ(0, mixer_count()); |
| |
| media::AudioParameters params(AudioParameters::AUDIO_PCM_LINEAR, |
| kChannelLayout, kSampleRate, kBufferSize); |
| media::AudioRendererMixer* mixer1 = |
| GetMixer(kFrameToken, params, AudioLatency::LATENCY_PLAYBACK, |
| kDefaultDeviceId, SinkUseState::kNewSink); |
| ASSERT_TRUE(mixer1); |
| EXPECT_EQ(1, mixer_count()); |
| |
| media::AudioRendererMixer* mixer2 = |
| GetMixer(kFrameToken, params, AudioLatency::LATENCY_PLAYBACK, |
| kDefaultDeviceId, SinkUseState::kExistingSink); |
| ASSERT_TRUE(mixer2); |
| EXPECT_EQ(mixer1, mixer2); // Same latency => same mixer. |
| EXPECT_EQ(1, mixer_count()); |
| |
| media::AudioRendererMixer* mixer3 = |
| GetMixer(kFrameToken, params, AudioLatency::LATENCY_RTC, kDefaultDeviceId, |
| SinkUseState::kNewSink); |
| ASSERT_TRUE(mixer3); |
| EXPECT_NE(mixer1, mixer3); |
| EXPECT_EQ(2, mixer_count()); // Another latency => another mixer. |
| |
| media::AudioRendererMixer* mixer4 = |
| GetMixer(kFrameToken, params, AudioLatency::LATENCY_RTC, kDefaultDeviceId, |
| SinkUseState::kExistingSink); |
| EXPECT_EQ(mixer3, mixer4); |
| EXPECT_EQ(2, mixer_count()); // Same latency => same mixer. |
| |
| media::AudioRendererMixer* mixer5 = |
| GetMixer(kFrameToken, params, AudioLatency::LATENCY_INTERACTIVE, |
| kDefaultDeviceId, SinkUseState::kNewSink); |
| ASSERT_TRUE(mixer5); |
| EXPECT_EQ(3, mixer_count()); // Another latency => another mixer. |
| |
| media::AudioRendererMixer* mixer6 = |
| GetMixer(kFrameToken, params, AudioLatency::LATENCY_INTERACTIVE, |
| kDefaultDeviceId, SinkUseState::kExistingSink); |
| EXPECT_EQ(mixer5, mixer6); |
| EXPECT_EQ(3, mixer_count()); // Same latency => same mixer. |
| |
| ReturnMixer(mixer1); |
| EXPECT_EQ(3, mixer_count()); |
| ReturnMixer(mixer2); |
| EXPECT_EQ(2, mixer_count()); |
| ReturnMixer(mixer3); |
| EXPECT_EQ(2, mixer_count()); |
| ReturnMixer(mixer4); |
| EXPECT_EQ(1, mixer_count()); |
| ReturnMixer(mixer5); |
| EXPECT_EQ(1, mixer_count()); |
| ReturnMixer(mixer6); |
| EXPECT_EQ(0, mixer_count()); |
| } |
| |
| // Verify GetMixer() correctly deduplicate mixers basing on the effects |
| // requirements. |
| TEST_F(AudioRendererMixerManagerTest, EffectsMixing) { |
| EXPECT_EQ(0, mixer_count()); |
| |
| media::AudioParameters params(AudioParameters::AUDIO_PCM_LINEAR, |
| kChannelLayout, kSampleRate, kBufferSize); |
| params.set_effects(1); |
| media::AudioRendererMixer* mixer1 = |
| GetMixer(kFrameToken, params, AudioLatency::LATENCY_PLAYBACK, |
| kDefaultDeviceId, SinkUseState::kNewSink); |
| ASSERT_TRUE(mixer1); |
| EXPECT_EQ(1, mixer_count()); |
| |
| media::AudioRendererMixer* mixer2 = |
| GetMixer(kFrameToken, params, AudioLatency::LATENCY_PLAYBACK, |
| kDefaultDeviceId, SinkUseState::kExistingSink); |
| ASSERT_TRUE(mixer2); |
| EXPECT_EQ(mixer1, mixer2); // Same effects => same mixer. |
| EXPECT_EQ(1, mixer_count()); |
| |
| params.set_effects(2); |
| media::AudioRendererMixer* mixer3 = |
| GetMixer(kFrameToken, params, AudioLatency::LATENCY_PLAYBACK, |
| kDefaultDeviceId, SinkUseState::kNewSink); |
| ASSERT_TRUE(mixer3); |
| EXPECT_NE(mixer1, mixer3); |
| EXPECT_EQ(2, mixer_count()); // Another effects => another mixer. |
| |
| media::AudioRendererMixer* mixer4 = |
| GetMixer(kFrameToken, params, AudioLatency::LATENCY_PLAYBACK, |
| kDefaultDeviceId, SinkUseState::kExistingSink); |
| EXPECT_EQ(mixer3, mixer4); |
| EXPECT_EQ(2, mixer_count()); // Same effects => same mixer. |
| |
| params.set_effects(3); |
| media::AudioRendererMixer* mixer5 = |
| GetMixer(kFrameToken, params, AudioLatency::LATENCY_PLAYBACK, |
| kDefaultDeviceId, SinkUseState::kNewSink); |
| ASSERT_TRUE(mixer5); |
| EXPECT_EQ(3, mixer_count()); // Another effects => another mixer. |
| |
| media::AudioRendererMixer* mixer6 = |
| GetMixer(kFrameToken, params, AudioLatency::LATENCY_PLAYBACK, |
| kDefaultDeviceId, SinkUseState::kExistingSink); |
| EXPECT_EQ(mixer5, mixer6); |
| EXPECT_EQ(3, mixer_count()); // Same effects => same mixer. |
| |
| ReturnMixer(mixer1); |
| EXPECT_EQ(3, mixer_count()); |
| ReturnMixer(mixer2); |
| EXPECT_EQ(2, mixer_count()); |
| ReturnMixer(mixer3); |
| EXPECT_EQ(2, mixer_count()); |
| ReturnMixer(mixer4); |
| EXPECT_EQ(1, mixer_count()); |
| ReturnMixer(mixer5); |
| EXPECT_EQ(1, mixer_count()); |
| ReturnMixer(mixer6); |
| EXPECT_EQ(0, mixer_count()); |
| } |
| |
| // Verify output bufer size of the mixer is correctly adjusted for Playback |
| // latency. |
| TEST_F(AudioRendererMixerManagerTest, MixerParamsLatencyPlayback) { |
| mock_sink_ = CreateNormalSink(); |
| |
| // Expecting hardware buffer size of 128 frames |
| EXPECT_EQ(44100, |
| mock_sink_->GetOutputDeviceInfo().output_params().sample_rate()); |
| // Expecting hardware buffer size of 128 frames |
| EXPECT_EQ( |
| 128, |
| mock_sink_->GetOutputDeviceInfo().output_params().frames_per_buffer()); |
| |
| media::AudioParameters params(AudioParameters::AUDIO_PCM_LINEAR, |
| kChannelLayout, 32000, 512); |
| params.set_latency_tag(AudioLatency::LATENCY_PLAYBACK); |
| |
| media::AudioRendererMixer* mixer = |
| GetMixer(kFrameToken, params, params.latency_tag(), kDefaultDeviceId, |
| SinkUseState::kNewSink); |
| |
| if (AudioLatency::IsResamplingPassthroughSupported(params.latency_tag())) { |
| // Expecting input sample rate |
| EXPECT_EQ(32000, mixer->get_output_params_for_testing().sample_rate()); |
| // Round up 20 ms (640) to the power of 2. |
| EXPECT_EQ(1024, mixer->get_output_params_for_testing().frames_per_buffer()); |
| } else { |
| // Expecting hardware sample rate |
| EXPECT_EQ(44100, mixer->get_output_params_for_testing().sample_rate()); |
| |
| // 20 ms at 44100 is 882 frames per buffer. |
| #if defined(OS_WIN) |
| // Round up 882 to the nearest multiple of the output buffer size (128). |
| // which is 7 * 128 = 896 |
| EXPECT_EQ(896, mixer->get_output_params_for_testing().frames_per_buffer()); |
| #else |
| // Round up 882 to the power of 2. |
| EXPECT_EQ(1024, mixer->get_output_params_for_testing().frames_per_buffer()); |
| #endif // defined(OS_WIN) |
| } |
| |
| ReturnMixer(mixer); |
| } |
| |
| // Verify output bufer size of the mixer is correctly adjusted for Playback |
| // latency when the device buffer size exceeds 20 ms. |
| TEST_F(AudioRendererMixerManagerTest, |
| MixerParamsLatencyPlaybackLargeDeviceBufferSize) { |
| mock_sink_ = new media::MockAudioRendererSink( |
| std::string(), media::OUTPUT_DEVICE_STATUS_OK, |
| AudioParameters(AudioParameters::AUDIO_PCM_LINEAR, kChannelLayout, 44100, |
| 2048)); |
| EXPECT_CALL(*mock_sink_, Stop()).Times(1); |
| |
| media::AudioParameters params(AudioParameters::AUDIO_PCM_LINEAR, |
| kChannelLayout, 32000, 512); |
| params.set_latency_tag(AudioLatency::LATENCY_PLAYBACK); |
| |
| media::AudioRendererMixer* mixer = |
| GetMixer(kFrameToken, params, params.latency_tag(), kDefaultDeviceId, |
| SinkUseState::kNewSink); |
| |
| // 20 ms at 44100 is 882 frames per buffer. |
| if (AudioLatency::IsResamplingPassthroughSupported(params.latency_tag())) { |
| // Expecting input sample rate |
| EXPECT_EQ(32000, mixer->get_output_params_for_testing().sample_rate()); |
| } else { |
| // Expecting hardware sample rate |
| EXPECT_EQ(44100, mixer->get_output_params_for_testing().sample_rate()); |
| } |
| |
| // Prefer device buffer size (2048) if is larger than 20 ms buffer size. |
| EXPECT_EQ(2048, mixer->get_output_params_for_testing().frames_per_buffer()); |
| |
| ReturnMixer(mixer); |
| } |
| |
| // Verify output bufer size of the mixer is correctly adjusted for Playback |
| // latency when output audio is fake. |
| TEST_F(AudioRendererMixerManagerTest, MixerParamsLatencyPlaybackFakeAudio) { |
| mock_sink_ = new media::MockAudioRendererSink( |
| std::string(), media::OUTPUT_DEVICE_STATUS_OK, |
| AudioParameters(AudioParameters::AUDIO_FAKE, kChannelLayout, 44100, |
| 2048)); |
| EXPECT_CALL(*mock_sink_, Stop()).Times(1); |
| |
| media::AudioParameters params(AudioParameters::AUDIO_PCM_LINEAR, |
| kChannelLayout, 32000, 512); |
| |
| media::AudioRendererMixer* mixer = |
| GetMixer(kFrameToken, params, AudioLatency::LATENCY_PLAYBACK, |
| kDefaultDeviceId, SinkUseState::kNewSink); |
| |
| // Expecting input sample rate |
| EXPECT_EQ(32000, mixer->get_output_params_for_testing().sample_rate()); |
| |
| // 20 ms at 32000 is 640 frames per buffer. |
| #if defined(OS_WIN) |
| // Use 20 ms buffer. |
| EXPECT_EQ(640, mixer->get_output_params_for_testing().frames_per_buffer()); |
| #else |
| // Ignore device buffer size, round up 640 to the power of 2. |
| EXPECT_EQ(1024, mixer->get_output_params_for_testing().frames_per_buffer()); |
| #endif // defined(OS_WIN) |
| |
| ReturnMixer(mixer); |
| } |
| |
| // Verify output bufer size of the mixer is correctly adjusted for RTC latency. |
| TEST_F(AudioRendererMixerManagerTest, MixerParamsLatencyRtc) { |
| mock_sink_ = CreateNormalSink(); |
| |
| // Expecting hardware buffer size of 128 frames |
| EXPECT_EQ(44100, |
| mock_sink_->GetOutputDeviceInfo().output_params().sample_rate()); |
| // Expecting hardware buffer size of 128 frames |
| EXPECT_EQ( |
| 128, |
| mock_sink_->GetOutputDeviceInfo().output_params().frames_per_buffer()); |
| |
| media::AudioParameters params(AudioParameters::AUDIO_PCM_LINEAR, |
| kChannelLayout, 32000, 512); |
| params.set_latency_tag(AudioLatency::LATENCY_RTC); |
| |
| media::AudioRendererMixer* mixer = |
| GetMixer(kFrameToken, params, params.latency_tag(), kDefaultDeviceId, |
| SinkUseState::kNewSink); |
| |
| int output_sample_rate = |
| AudioLatency::IsResamplingPassthroughSupported(params.latency_tag()) |
| ? 32000 |
| : 44100; |
| |
| EXPECT_EQ(output_sample_rate, |
| mixer->get_output_params_for_testing().sample_rate()); |
| |
| #if defined(OS_LINUX) || defined(OS_CHROMEOS) || defined(OS_MAC) || \ |
| defined(OS_FUCHSIA) |
| // Use 10 ms buffer (441 frames per buffer). |
| EXPECT_EQ(output_sample_rate / 100, |
| mixer->get_output_params_for_testing().frames_per_buffer()); |
| #elif defined(OS_ANDROID) |
| // If hardware buffer size (128) is less than 20 ms (882), use 20 ms buffer |
| // (otherwise, use hardware buffer). |
| EXPECT_EQ(882, mixer->get_output_params_for_testing().frames_per_buffer()); |
| #else |
| // Use hardware buffer size (128). |
| EXPECT_EQ(128, mixer->get_output_params_for_testing().frames_per_buffer()); |
| #endif // defined(OS_LINUX) || defined(OS_CHROMEOS) || defined(OS_MAC) |
| |
| ReturnMixer(mixer); |
| } |
| |
| // Verify output bufer size of the mixer is correctly adjusted for RTC latency |
| // when output audio is fake. |
| TEST_F(AudioRendererMixerManagerTest, MixerParamsLatencyRtcFakeAudio) { |
| mock_sink_ = new media::MockAudioRendererSink( |
| std::string(), media::OUTPUT_DEVICE_STATUS_OK, |
| AudioParameters(AudioParameters::AUDIO_FAKE, kChannelLayout, 44100, 128)); |
| EXPECT_CALL(*mock_sink_, Stop()).Times(1); |
| |
| media::AudioParameters params(AudioParameters::AUDIO_PCM_LINEAR, |
| kChannelLayout, 32000, 512); |
| |
| media::AudioRendererMixer* mixer = |
| GetMixer(kFrameToken, params, AudioLatency::LATENCY_RTC, kDefaultDeviceId, |
| SinkUseState::kNewSink); |
| |
| // Expecting input sample rate. |
| EXPECT_EQ(32000, mixer->get_output_params_for_testing().sample_rate()); |
| |
| // 10 ms at 32000 is 320 frames per buffer. Expect it on all the platforms for |
| // fake audio output. |
| EXPECT_EQ(320, mixer->get_output_params_for_testing().frames_per_buffer()); |
| |
| ReturnMixer(mixer); |
| } |
| |
| // Verify output bufer size of the mixer is correctly adjusted for Interactive |
| // latency. |
| TEST_F(AudioRendererMixerManagerTest, MixerParamsLatencyInteractive) { |
| mock_sink_ = CreateNormalSink(); |
| |
| // Expecting hardware buffer size of 128 frames |
| EXPECT_EQ(44100, |
| mock_sink_->GetOutputDeviceInfo().output_params().sample_rate()); |
| // Expecting hardware buffer size of 128 frames |
| EXPECT_EQ( |
| 128, |
| mock_sink_->GetOutputDeviceInfo().output_params().frames_per_buffer()); |
| |
| media::AudioParameters params(AudioParameters::AUDIO_PCM_LINEAR, |
| kChannelLayout, 32000, 512); |
| params.set_latency_tag(AudioLatency::LATENCY_INTERACTIVE); |
| |
| media::AudioRendererMixer* mixer = |
| GetMixer(kFrameToken, params, params.latency_tag(), kDefaultDeviceId, |
| SinkUseState::kNewSink); |
| |
| if (AudioLatency::IsResamplingPassthroughSupported(params.latency_tag())) { |
| // Expecting input sample rate. |
| EXPECT_EQ(32000, mixer->get_output_params_for_testing().sample_rate()); |
| } else { |
| // Expecting hardware sample rate. |
| EXPECT_EQ(44100, mixer->get_output_params_for_testing().sample_rate()); |
| } |
| |
| // Expect hardware buffer size. |
| EXPECT_EQ(128, mixer->get_output_params_for_testing().frames_per_buffer()); |
| |
| ReturnMixer(mixer); |
| } |
| |
| // Verify output parameters are the same as input properties for bitstream |
| // formats. |
| TEST_F(AudioRendererMixerManagerTest, MixerParamsBitstreamFormat) { |
| mock_sink_ = new media::MockAudioRendererSink( |
| std::string(), media::OUTPUT_DEVICE_STATUS_OK, |
| AudioParameters(AudioParameters::AUDIO_PCM_LINEAR, kChannelLayout, 44100, |
| 2048)); |
| EXPECT_CALL(*mock_sink_, Stop()).Times(1); |
| |
| media::AudioParameters params(AudioParameters::AUDIO_BITSTREAM_EAC3, |
| kAnotherChannelLayout, 32000, 512); |
| params.set_latency_tag(AudioLatency::LATENCY_PLAYBACK); |
| |
| media::AudioRendererMixer* mixer = |
| GetMixer(kFrameToken, params, params.latency_tag(), kDefaultDeviceId, |
| SinkUseState::kNewSink); |
| |
| // Output parameters should be the same as input properties for bitstream |
| // formats. |
| EXPECT_EQ(params.format(), mixer->get_output_params_for_testing().format()); |
| EXPECT_EQ(params.channel_layout(), |
| mixer->get_output_params_for_testing().channel_layout()); |
| EXPECT_EQ(params.sample_rate(), |
| mixer->get_output_params_for_testing().sample_rate()); |
| EXPECT_EQ(params.frames_per_buffer(), |
| mixer->get_output_params_for_testing().frames_per_buffer()); |
| |
| ReturnMixer(mixer); |
| } |
| |
| } // namespace blink |