| // 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 "content/renderer/media/audio_renderer_mixer_manager.h" |
| |
| #include <memory> |
| |
| #include "base/bind.h" |
| #include "build/build_config.h" |
| #include "base/logging.h" |
| #include "base/macros.h" |
| #include "base/memory/ref_counted.h" |
| #include "content/renderer/media/audio_renderer_sink_cache.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" |
| #include "url/gurl.h" |
| |
| namespace content { |
| |
| namespace { |
| const int kBitsPerChannel = 16; |
| 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 int kRenderFrameId = 124; |
| const int kAnotherRenderFrameId = 678; |
| } // namespace; |
| |
| using media::AudioParameters; |
| using media::AudioLatency; |
| |
| class FakeAudioRendererSinkCache : public AudioRendererSinkCache { |
| public: |
| using GetSinkCallback = |
| base::Callback<scoped_refptr<media::AudioRendererSink>( |
| int render_frame_id, |
| int session_id, |
| const std::string& device_id, |
| const url::Origin& security_origin)>; |
| |
| using ReleaseSinkCallback = |
| base::Callback<void(const media::AudioRendererSink*)>; |
| |
| FakeAudioRendererSinkCache(const GetSinkCallback& get_sink_cb, |
| const ReleaseSinkCallback& release_sink_cb) |
| : get_sink_cb_(get_sink_cb), release_sink_cb_(release_sink_cb) {} |
| |
| media::OutputDeviceInfo GetSinkInfo( |
| int source_render_frame_id, |
| int session_id, |
| const std::string& device_id, |
| const url::Origin& security_origin) final { |
| return get_sink_cb_ |
| .Run(source_render_frame_id, session_id, device_id, security_origin) |
| ->GetOutputDeviceInfo(); |
| } |
| |
| scoped_refptr<media::AudioRendererSink> GetSink( |
| int source_render_frame_id, |
| const std::string& device_id, |
| const url::Origin& security_origin) final { |
| return get_sink_cb_.Run(source_render_frame_id, 0, device_id, |
| security_origin); |
| } |
| |
| void ReleaseSink(const media::AudioRendererSink* sink) final { |
| release_sink_cb_.Run(sink); |
| } |
| |
| private: |
| GetSinkCallback get_sink_cb_; |
| ReleaseSinkCallback release_sink_cb_; |
| }; |
| |
| class AudioRendererMixerManagerTest : public testing::Test { |
| public: |
| AudioRendererMixerManagerTest() |
| : manager_(new AudioRendererMixerManager( |
| std::unique_ptr<AudioRendererSinkCache>( |
| new FakeAudioRendererSinkCache( |
| base::Bind(&AudioRendererMixerManagerTest::GetSinkPtr, |
| base::Unretained(this)), |
| base::Bind(&AudioRendererMixerManagerTest::ReleaseSinkPtr, |
| base::Unretained(this)))))), |
| mock_sink_(new media::MockAudioRendererSink( |
| kDefaultDeviceId, |
| media::OUTPUT_DEVICE_STATUS_OK, |
| AudioParameters(AudioParameters::AUDIO_PCM_LINEAR, |
| kChannelLayout, |
| kHardwareSampleRate, |
| kBitsPerChannel, |
| kHardwareBufferSize))), |
| mock_sink_no_device_(new media::MockAudioRendererSink( |
| kNonexistentDeviceId, |
| media::OUTPUT_DEVICE_STATUS_ERROR_NOT_FOUND)), |
| mock_sink_matched_device_( |
| new media::MockAudioRendererSink(kMatchedDeviceId, |
| media::OUTPUT_DEVICE_STATUS_OK)), |
| kSecurityOrigin2(GURL("http://localhost")) {} |
| |
| media::AudioRendererMixer* GetMixer( |
| int source_render_frame_id, |
| const media::AudioParameters& params, |
| AudioLatency::LatencyType latency, |
| const std::string& device_id, |
| const url::Origin& security_origin, |
| media::OutputDeviceStatus* device_status) { |
| return manager_->GetMixer(source_render_frame_id, params, latency, |
| device_id, security_origin, device_status); |
| } |
| |
| void ReturnMixer(media::AudioRendererMixer* mixer) { |
| return manager_->ReturnMixer(mixer); |
| } |
| |
| // Number of instantiated mixers. |
| int mixer_count() { |
| return manager_->mixers_.size(); |
| } |
| |
| protected: |
| scoped_refptr<media::AudioRendererSink> GetSinkPtr( |
| int source_render_frame_id, |
| int session_id, |
| const std::string& device_id, |
| const url::Origin& security_origin) { |
| if ((device_id == kDefaultDeviceId) || (device_id == kAnotherDeviceId)) { |
| // We don't care about separate sinks for these devices. |
| return mock_sink_; |
| } |
| if (device_id == kNonexistentDeviceId) |
| return mock_sink_no_device_; |
| if (device_id.empty()) { |
| // The sink used to get device ID from session ID if it's not empty |
| return session_id ? mock_sink_matched_device_ : mock_sink_; |
| } |
| if (device_id == kMatchedDeviceId) |
| return mock_sink_matched_device_; |
| |
| NOTREACHED(); |
| return nullptr; |
| } |
| |
| MOCK_METHOD1(ReleaseSinkPtr, void(const media::AudioRendererSink*)); |
| |
| std::unique_ptr<AudioRendererMixerManager> manager_; |
| |
| scoped_refptr<media::MockAudioRendererSink> mock_sink_; |
| scoped_refptr<media::MockAudioRendererSink> mock_sink_no_device_; |
| scoped_refptr<media::MockAudioRendererSink> mock_sink_matched_device_; |
| |
| // To avoid global/static non-POD constants. |
| const url::Origin kSecurityOrigin; |
| const url::Origin kSecurityOrigin2; |
| |
| private: |
| 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) { |
| // Since we're testing two different sets of parameters, we expect |
| // AudioRendererMixerManager to call Start and Stop on our mock twice. |
| EXPECT_CALL(*mock_sink_.get(), Start()).Times(2); |
| EXPECT_CALL(*mock_sink_.get(), Stop()).Times(2); |
| |
| // We expect 2 mixers to be created; each of them should release the sink. |
| EXPECT_CALL(*this, ReleaseSinkPtr(mock_sink_.get())).Times(2); |
| |
| // There should be no mixers outstanding to start with. |
| EXPECT_EQ(0, mixer_count()); |
| |
| media::AudioParameters params1(media::AudioParameters::AUDIO_PCM_LINEAR, |
| kChannelLayout, kSampleRate, kBitsPerChannel, |
| kBufferSize); |
| |
| media::AudioRendererMixer* mixer1 = |
| GetMixer(kRenderFrameId, params1, AudioLatency::LATENCY_PLAYBACK, |
| kDefaultDeviceId, kSecurityOrigin, nullptr); |
| ASSERT_TRUE(mixer1); |
| EXPECT_EQ(1, mixer_count()); |
| |
| // The same parameters should return the same mixer1. |
| EXPECT_EQ(mixer1, |
| GetMixer(kRenderFrameId, params1, AudioLatency::LATENCY_PLAYBACK, |
| kDefaultDeviceId, kSecurityOrigin, nullptr)); |
| 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, |
| kBitsPerChannel, kBufferSize * 2); |
| media::AudioRendererMixer* mixer2 = |
| GetMixer(kRenderFrameId, params2, AudioLatency::LATENCY_PLAYBACK, |
| kDefaultDeviceId, kSecurityOrigin, nullptr); |
| 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_CALL(*mock_sink_.get(), Start()).Times(2); |
| EXPECT_CALL(*mock_sink_.get(), Stop()).Times(2); |
| EXPECT_EQ(mixer_count(), 0); |
| |
| // We expect 2 mixers to be created; each of them should release the sink. |
| EXPECT_CALL(*this, ReleaseSinkPtr(mock_sink_.get())).Times(2); |
| |
| media::AudioParameters params1(AudioParameters::AUDIO_PCM_LINEAR, |
| kChannelLayout, |
| kSampleRate, |
| kBitsPerChannel, |
| kBufferSize); |
| media::AudioRendererMixer* mixer1 = |
| GetMixer(kRenderFrameId, params1, AudioLatency::LATENCY_PLAYBACK, |
| kDefaultDeviceId, kSecurityOrigin, nullptr); |
| 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, |
| kBitsPerChannel * 2, |
| kBufferSize * 2); |
| media::AudioRendererMixer* mixer2 = |
| GetMixer(kRenderFrameId, params2, AudioLatency::LATENCY_PLAYBACK, |
| kDefaultDeviceId, kSecurityOrigin, nullptr); |
| 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, |
| kBitsPerChannel, |
| kBufferSize); |
| ASSERT_NE(params3.channel_layout(), params1.channel_layout()); |
| media::AudioRendererMixer* mixer3 = |
| GetMixer(kRenderFrameId, params3, AudioLatency::LATENCY_PLAYBACK, |
| kDefaultDeviceId, kSecurityOrigin, nullptr); |
| 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) { |
| // Expect AudioRendererMixerManager to call Start and Stop on our mock twice |
| // each. Note: Under normal conditions, each mixer would get its own sink! |
| EXPECT_CALL(*mock_sink_.get(), Start()).Times(2); |
| EXPECT_CALL(*mock_sink_.get(), Stop()).Times(2); |
| |
| // We expect 2 mixers to be created; each of them should release the sink. |
| EXPECT_CALL(*this, ReleaseSinkPtr(mock_sink_.get())).Times(2); |
| |
| media::AudioParameters params( |
| AudioParameters::AUDIO_PCM_LINEAR, kChannelLayout, kSampleRate, |
| kBitsPerChannel, kBufferSize); |
| |
| // Create two mixer inputs and ensure this doesn't instantiate any mixers yet. |
| EXPECT_EQ(0, mixer_count()); |
| media::FakeAudioRenderCallback callback(0, kSampleRate); |
| scoped_refptr<media::AudioRendererMixerInput> input( |
| manager_->CreateInput(kRenderFrameId, 0, kDefaultDeviceId, |
| kSecurityOrigin, AudioLatency::LATENCY_PLAYBACK)); |
| input->Initialize(params, &callback); |
| EXPECT_EQ(0, mixer_count()); |
| media::FakeAudioRenderCallback another_callback(1, kSampleRate); |
| scoped_refptr<media::AudioRendererMixerInput> another_input( |
| manager_->CreateInput(kAnotherRenderFrameId, 0, kDefaultDeviceId, |
| kSecurityOrigin, AudioLatency::LATENCY_PLAYBACK)); |
| another_input->Initialize(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. |
| TEST_F(AudioRendererMixerManagerTest, CreateInputWithSessionId) { |
| // Expect AudioRendererMixerManager to call Start and Stop on our mock twice |
| // each: for kDefaultDeviceId and for kAnotherDeviceId. Note: Under normal |
| // conditions, each mixer would get its own sink! |
| EXPECT_CALL(*mock_sink_.get(), Start()).Times(2); |
| EXPECT_CALL(*mock_sink_.get(), Stop()).Times(2); |
| |
| // Expect AudioRendererMixerManager to call Start and Stop on the matched sink |
| // once. |
| EXPECT_CALL(*mock_sink_matched_device_.get(), Start()).Times(1); |
| EXPECT_CALL(*mock_sink_matched_device_.get(), Stop()).Times(1); |
| |
| // We expect 3 mixers to be created; each of them should release a sink. |
| EXPECT_CALL(*this, ReleaseSinkPtr(mock_sink_.get())).Times(2); |
| EXPECT_CALL(*this, ReleaseSinkPtr(mock_sink_matched_device_.get())).Times(1); |
| |
| media::AudioParameters params(AudioParameters::AUDIO_PCM_LINEAR, |
| kChannelLayout, kSampleRate, kBitsPerChannel, |
| kBufferSize); |
| media::FakeAudioRenderCallback callback(0, kSampleRate); |
| EXPECT_EQ(0, mixer_count()); |
| |
| // Empty device id, zero session id; |
| scoped_refptr<media::AudioRendererMixerInput> input_to_default_device( |
| manager_->CreateInput(kRenderFrameId, 0, // session_id |
| std::string(), kSecurityOrigin, |
| AudioLatency::LATENCY_PLAYBACK)); |
| input_to_default_device->Initialize(params, &callback); |
| EXPECT_EQ(0, mixer_count()); |
| |
| // Specific device id, zero session id; |
| scoped_refptr<media::AudioRendererMixerInput> input_to_matched_device( |
| manager_->CreateInput(kRenderFrameId, 0, // session_id |
| kMatchedDeviceId, kSecurityOrigin, |
| AudioLatency::LATENCY_PLAYBACK)); |
| input_to_matched_device->Initialize(params, &callback); |
| EXPECT_EQ(0, mixer_count()); |
| |
| // Specific device id, non-zero session id (to be ignored); |
| scoped_refptr<media::AudioRendererMixerInput> input_to_another_device( |
| manager_->CreateInput(kRenderFrameId, 1, // session id |
| kAnotherDeviceId, kSecurityOrigin, |
| AudioLatency::LATENCY_PLAYBACK)); |
| input_to_another_device->Initialize(params, &callback); |
| EXPECT_EQ(0, mixer_count()); |
| |
| // Empty device id, non-zero session id; |
| scoped_refptr<media::AudioRendererMixerInput> |
| input_to_matched_device_with_session_id(manager_->CreateInput( |
| kRenderFrameId, 2, // session id |
| std::string(), kSecurityOrigin, AudioLatency::LATENCY_PLAYBACK)); |
| input_to_matched_device_with_session_id->Initialize(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 and/or security origin |
| TEST_F(AudioRendererMixerManagerTest, MixerDevices) { |
| EXPECT_CALL(*mock_sink_.get(), Start()).Times(3); |
| EXPECT_CALL(*mock_sink_.get(), Stop()).Times(3); |
| EXPECT_EQ(0, mixer_count()); |
| |
| // We expect 3 mixers to be created; each of them should release a sink. |
| EXPECT_CALL(*this, ReleaseSinkPtr(mock_sink_.get())).Times(3); |
| |
| media::AudioParameters params(AudioParameters::AUDIO_PCM_LINEAR, |
| kChannelLayout, kSampleRate, kBitsPerChannel, |
| kBufferSize); |
| media::AudioRendererMixer* mixer1 = |
| GetMixer(kRenderFrameId, params, AudioLatency::LATENCY_PLAYBACK, |
| kDefaultDeviceId, kSecurityOrigin, nullptr); |
| ASSERT_TRUE(mixer1); |
| EXPECT_EQ(1, mixer_count()); |
| |
| media::AudioRendererMixer* mixer2 = |
| GetMixer(kRenderFrameId, params, AudioLatency::LATENCY_PLAYBACK, |
| kAnotherDeviceId, kSecurityOrigin, nullptr); |
| ASSERT_TRUE(mixer2); |
| EXPECT_EQ(2, mixer_count()); |
| EXPECT_NE(mixer1, mixer2); |
| |
| media::AudioRendererMixer* mixer3 = |
| GetMixer(kRenderFrameId, params, AudioLatency::LATENCY_PLAYBACK, |
| kAnotherDeviceId, kSecurityOrigin2, nullptr); |
| ASSERT_TRUE(mixer3); |
| EXPECT_EQ(3, mixer_count()); |
| EXPECT_NE(mixer1, mixer3); |
| EXPECT_NE(mixer2, mixer3); |
| |
| ReturnMixer(mixer1); |
| EXPECT_EQ(2, mixer_count()); |
| ReturnMixer(mixer2); |
| EXPECT_EQ(1, mixer_count()); |
| ReturnMixer(mixer3); |
| EXPECT_EQ(0, mixer_count()); |
| } |
| |
| // Verify GetMixer() correctly deduplicate mixers with the same |
| // parameters, different security origins but default device ID |
| TEST_F(AudioRendererMixerManagerTest, OneMixerDifferentOriginsDefaultDevice) { |
| EXPECT_CALL(*mock_sink_.get(), Start()).Times(1); |
| EXPECT_CALL(*mock_sink_.get(), Stop()).Times(1); |
| EXPECT_EQ(0, mixer_count()); |
| |
| // We expect 1 mixer to be created; it should release its sink. |
| EXPECT_CALL(*this, ReleaseSinkPtr(mock_sink_.get())).Times(1); |
| |
| media::AudioParameters params(AudioParameters::AUDIO_PCM_LINEAR, |
| kChannelLayout, kSampleRate, kBitsPerChannel, |
| kBufferSize); |
| media::AudioRendererMixer* mixer1 = |
| GetMixer(kRenderFrameId, params, AudioLatency::LATENCY_PLAYBACK, |
| kDefaultDeviceId, kSecurityOrigin, nullptr); |
| ASSERT_TRUE(mixer1); |
| EXPECT_EQ(1, mixer_count()); |
| |
| media::AudioRendererMixer* mixer2 = |
| GetMixer(kRenderFrameId, params, AudioLatency::LATENCY_PLAYBACK, |
| std::string(), kSecurityOrigin, nullptr); |
| ASSERT_TRUE(mixer2); |
| EXPECT_EQ(1, mixer_count()); |
| EXPECT_EQ(mixer1, mixer2); |
| |
| media::AudioRendererMixer* mixer3 = |
| GetMixer(kRenderFrameId, params, AudioLatency::LATENCY_PLAYBACK, |
| kDefaultDeviceId, kSecurityOrigin2, nullptr); |
| ASSERT_TRUE(mixer3); |
| EXPECT_EQ(1, mixer_count()); |
| EXPECT_EQ(mixer1, mixer3); |
| |
| media::AudioRendererMixer* mixer4 = |
| GetMixer(kRenderFrameId, params, AudioLatency::LATENCY_PLAYBACK, |
| std::string(), kSecurityOrigin2, nullptr); |
| ASSERT_TRUE(mixer4); |
| EXPECT_EQ(1, mixer_count()); |
| EXPECT_EQ(mixer1, mixer4); |
| |
| ReturnMixer(mixer1); |
| EXPECT_EQ(1, mixer_count()); |
| ReturnMixer(mixer2); |
| EXPECT_EQ(1, mixer_count()); |
| ReturnMixer(mixer3); |
| EXPECT_EQ(1, mixer_count()); |
| ReturnMixer(mixer4); |
| 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()); |
| |
| // Mixer manager should release a not-ok sink when failing to create a mixer. |
| EXPECT_CALL(*this, ReleaseSinkPtr(mock_sink_no_device_.get())).Times(1); |
| |
| media::AudioParameters params(AudioParameters::AUDIO_PCM_LINEAR, |
| kChannelLayout, kSampleRate, kBitsPerChannel, |
| kBufferSize); |
| media::OutputDeviceStatus device_status = media::OUTPUT_DEVICE_STATUS_OK; |
| |
| media::AudioRendererMixer* mixer = |
| GetMixer(kRenderFrameId, params, AudioLatency::LATENCY_PLAYBACK, |
| kNonexistentDeviceId, kSecurityOrigin, &device_status); |
| |
| EXPECT_FALSE(mixer); |
| EXPECT_EQ(media::OUTPUT_DEVICE_STATUS_ERROR_NOT_FOUND, device_status); |
| EXPECT_EQ(0, mixer_count()); |
| } |
| |
| // Verify GetMixer() correctly deduplicate mixers basing on latency |
| // requirements. |
| TEST_F(AudioRendererMixerManagerTest, LatencyMixing) { |
| EXPECT_CALL(*mock_sink_.get(), Start()).Times(3); |
| EXPECT_CALL(*mock_sink_.get(), Stop()).Times(3); |
| EXPECT_CALL(*this, ReleaseSinkPtr(mock_sink_.get())).Times(3); |
| |
| EXPECT_EQ(0, mixer_count()); |
| |
| media::AudioParameters params(AudioParameters::AUDIO_PCM_LINEAR, |
| kChannelLayout, kSampleRate, kBitsPerChannel, |
| kBufferSize); |
| media::AudioRendererMixer* mixer1 = |
| GetMixer(kRenderFrameId, params, AudioLatency::LATENCY_PLAYBACK, |
| kDefaultDeviceId, kSecurityOrigin, nullptr); |
| ASSERT_TRUE(mixer1); |
| EXPECT_EQ(1, mixer_count()); |
| |
| media::AudioRendererMixer* mixer2 = |
| GetMixer(kRenderFrameId, params, AudioLatency::LATENCY_PLAYBACK, |
| kDefaultDeviceId, kSecurityOrigin, nullptr); |
| ASSERT_TRUE(mixer2); |
| EXPECT_EQ(mixer1, mixer2); // Same latency => same mixer. |
| EXPECT_EQ(1, mixer_count()); |
| |
| media::AudioRendererMixer* mixer3 = |
| GetMixer(kRenderFrameId, params, AudioLatency::LATENCY_RTC, |
| kDefaultDeviceId, kSecurityOrigin, nullptr); |
| ASSERT_TRUE(mixer3); |
| EXPECT_NE(mixer1, mixer3); |
| EXPECT_EQ(2, mixer_count()); // Another latency => another mixer. |
| |
| media::AudioRendererMixer* mixer4 = |
| GetMixer(kRenderFrameId, params, AudioLatency::LATENCY_RTC, |
| kDefaultDeviceId, kSecurityOrigin, nullptr); |
| EXPECT_EQ(mixer3, mixer4); |
| EXPECT_EQ(2, mixer_count()); // Same latency => same mixer. |
| |
| media::AudioRendererMixer* mixer5 = |
| GetMixer(kRenderFrameId, params, AudioLatency::LATENCY_INTERACTIVE, |
| kDefaultDeviceId, kSecurityOrigin, nullptr); |
| ASSERT_TRUE(mixer5); |
| EXPECT_EQ(3, mixer_count()); // Another latency => another mixer. |
| |
| media::AudioRendererMixer* mixer6 = |
| GetMixer(kRenderFrameId, params, AudioLatency::LATENCY_INTERACTIVE, |
| kDefaultDeviceId, kSecurityOrigin, nullptr); |
| 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 output bufer size of the mixer is correctly adjusted for Playback |
| // latency. |
| TEST_F(AudioRendererMixerManagerTest, MixerParamsLatencyPlayback) { |
| // 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()); |
| |
| EXPECT_CALL(*mock_sink_.get(), Start()).Times(1); |
| EXPECT_CALL(*mock_sink_.get(), Stop()).Times(1); |
| EXPECT_CALL(*this, ReleaseSinkPtr(mock_sink_.get())).Times(1); |
| |
| media::AudioParameters params(AudioParameters::AUDIO_PCM_LINEAR, |
| kChannelLayout, 32000, kBitsPerChannel, 512); |
| |
| media::AudioRendererMixer* mixer = |
| GetMixer(kRenderFrameId, params, AudioLatency::LATENCY_PLAYBACK, |
| kDefaultDeviceId, kSecurityOrigin, nullptr); |
| |
| #if defined(OS_CHROMEOS) |
| // Expecting input sample rate |
| EXPECT_EQ(32000, mixer->GetOutputParamsForTesting().sample_rate()); |
| // Round up 20 ms (640) to the power of 2. |
| EXPECT_EQ(1024, mixer->GetOutputParamsForTesting().frames_per_buffer()); |
| |
| #else |
| // Expecting hardware sample rate |
| EXPECT_EQ(44100, mixer->GetOutputParamsForTesting().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->GetOutputParamsForTesting().frames_per_buffer()); |
| #else |
| // Round up 882 to the power of 2. |
| EXPECT_EQ(1024, mixer->GetOutputParamsForTesting().frames_per_buffer()); |
| #endif // defined(OS_WIN) |
| |
| #endif // defined(OS_CHROMEOS) |
| |
| 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, |
| kBitsPerChannel, 2048)); |
| |
| EXPECT_CALL(*mock_sink_.get(), Start()).Times(1); |
| EXPECT_CALL(*mock_sink_.get(), Stop()).Times(1); |
| EXPECT_CALL(*this, ReleaseSinkPtr(mock_sink_.get())).Times(1); |
| |
| media::AudioParameters params(AudioParameters::AUDIO_PCM_LINEAR, |
| kChannelLayout, 32000, kBitsPerChannel, 512); |
| |
| media::AudioRendererMixer* mixer = |
| GetMixer(kRenderFrameId, params, AudioLatency::LATENCY_PLAYBACK, |
| kDefaultDeviceId, kSecurityOrigin, nullptr); |
| |
| // 20 ms at 44100 is 882 frames per buffer. |
| #if defined(OS_CHROMEOS) |
| // Expecting input sample rate |
| EXPECT_EQ(32000, mixer->GetOutputParamsForTesting().sample_rate()); |
| // Ignore device buffer size, round up 20 ms (640) to the power of 2. |
| EXPECT_EQ(1024, mixer->GetOutputParamsForTesting().frames_per_buffer()); |
| #else |
| // Expecting hardware sample rate |
| EXPECT_EQ(44100, mixer->GetOutputParamsForTesting().sample_rate()); |
| // Prefer device buffer size (2048) if is larger than 20 ms buffer size (882). |
| EXPECT_EQ(2048, mixer->GetOutputParamsForTesting().frames_per_buffer()); |
| #endif |
| |
| 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, |
| kBitsPerChannel, 2048)); |
| |
| EXPECT_CALL(*mock_sink_.get(), Start()).Times(1); |
| EXPECT_CALL(*mock_sink_.get(), Stop()).Times(1); |
| EXPECT_CALL(*this, ReleaseSinkPtr(mock_sink_.get())).Times(1); |
| |
| media::AudioParameters params(AudioParameters::AUDIO_PCM_LINEAR, |
| kChannelLayout, 32000, kBitsPerChannel, 512); |
| |
| media::AudioRendererMixer* mixer = |
| GetMixer(kRenderFrameId, params, AudioLatency::LATENCY_PLAYBACK, |
| kDefaultDeviceId, kSecurityOrigin, nullptr); |
| |
| // Expecting input sample rate |
| EXPECT_EQ(32000, mixer->GetOutputParamsForTesting().sample_rate()); |
| |
| // 20 ms at 32000 is 640 frames per buffer. |
| #if defined(OS_WIN) |
| // Use 20 ms buffer. |
| EXPECT_EQ(640, mixer->GetOutputParamsForTesting().frames_per_buffer()); |
| #else |
| // Ignore device buffer size, round up 640 to the power of 2. |
| EXPECT_EQ(1024, mixer->GetOutputParamsForTesting().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) { |
| // 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()); |
| |
| EXPECT_CALL(*mock_sink_.get(), Start()).Times(1); |
| EXPECT_CALL(*mock_sink_.get(), Stop()).Times(1); |
| EXPECT_CALL(*this, ReleaseSinkPtr(mock_sink_.get())).Times(1); |
| |
| media::AudioParameters params(AudioParameters::AUDIO_PCM_LINEAR, |
| kChannelLayout, 32000, kBitsPerChannel, 512); |
| |
| media::AudioRendererMixer* mixer = |
| GetMixer(kRenderFrameId, params, AudioLatency::LATENCY_RTC, |
| kDefaultDeviceId, kSecurityOrigin, nullptr); |
| |
| #if defined(OS_CHROMEOS) |
| int output_sample_rate = 32000; |
| #else |
| // Expecting hardware sample rate. |
| int output_sample_rate = 44100; |
| #endif // defined(OS_CHROMEOS) |
| |
| EXPECT_EQ(output_sample_rate, |
| mixer->GetOutputParamsForTesting().sample_rate()); |
| |
| #if defined(OS_LINUX) || defined(OS_MACOSX) |
| // Use 10 ms buffer (441 frames per buffer). |
| EXPECT_EQ(output_sample_rate / 100, |
| mixer->GetOutputParamsForTesting().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->GetOutputParamsForTesting().frames_per_buffer()); |
| #else |
| // Use hardware buffer size (128). |
| EXPECT_EQ(128, mixer->GetOutputParamsForTesting().frames_per_buffer()); |
| #endif // defined(OS_LINUX) || defined(OS_MACOSX) |
| |
| 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, |
| kBitsPerChannel, 128)); |
| |
| EXPECT_CALL(*mock_sink_.get(), Start()).Times(1); |
| EXPECT_CALL(*mock_sink_.get(), Stop()).Times(1); |
| EXPECT_CALL(*this, ReleaseSinkPtr(mock_sink_.get())).Times(1); |
| |
| media::AudioParameters params(AudioParameters::AUDIO_PCM_LINEAR, |
| kChannelLayout, 32000, kBitsPerChannel, 512); |
| |
| media::AudioRendererMixer* mixer = |
| GetMixer(kRenderFrameId, params, AudioLatency::LATENCY_RTC, |
| kDefaultDeviceId, kSecurityOrigin, nullptr); |
| |
| // Expecting input sample rate. |
| EXPECT_EQ(32000, mixer->GetOutputParamsForTesting().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->GetOutputParamsForTesting().frames_per_buffer()); |
| |
| ReturnMixer(mixer); |
| } |
| |
| // Verify output bufer size of the mixer is correctly adjusted for Interactive |
| // latency. |
| TEST_F(AudioRendererMixerManagerTest, MixerParamsLatencyInteractive) { |
| // 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()); |
| |
| EXPECT_CALL(*mock_sink_.get(), Start()).Times(1); |
| EXPECT_CALL(*mock_sink_.get(), Stop()).Times(1); |
| EXPECT_CALL(*this, ReleaseSinkPtr(mock_sink_.get())).Times(1); |
| |
| media::AudioParameters params(AudioParameters::AUDIO_PCM_LINEAR, |
| kChannelLayout, 32000, kBitsPerChannel, 512); |
| |
| media::AudioRendererMixer* mixer = |
| GetMixer(kRenderFrameId, params, AudioLatency::LATENCY_INTERACTIVE, |
| kDefaultDeviceId, kSecurityOrigin, nullptr); |
| |
| #if defined(OS_CHROMEOS) |
| // Expecting input sample rate. |
| EXPECT_EQ(32000, mixer->GetOutputParamsForTesting().sample_rate()); |
| #else |
| // Expecting hardware sample rate. |
| EXPECT_EQ(44100, mixer->GetOutputParamsForTesting().sample_rate()); |
| #endif // defined(OS_CHROMEOS) |
| |
| #if defined(OS_ANDROID) |
| // If hardware buffer size (128) is less than 1024, use 2048. |
| EXPECT_EQ(2048, mixer->GetOutputParamsForTesting().frames_per_buffer()); |
| #else |
| // Expect hardware buffer size. |
| EXPECT_EQ(128, mixer->GetOutputParamsForTesting().frames_per_buffer()); |
| #endif |
| |
| ReturnMixer(mixer); |
| } |
| |
| } // namespace content |