diff options
author | mflodman@google.com <mflodman@google.com@0039d316-1c4b-4281-b951-d872f2087c98> | 2011-07-20 18:19:25 +0000 |
---|---|---|
committer | mflodman@google.com <mflodman@google.com@0039d316-1c4b-4281-b951-d872f2087c98> | 2011-07-20 18:19:25 +0000 |
commit | eb5a9afce9288e65ed0233867692b7ef1b95bb67 (patch) | |
tree | 51b2f8953ae09907ddbd5db2c52955d4cd60be02 /content | |
parent | a19358d3fc8effc088e5212d11d7ab4eed4a04c9 (diff) | |
download | chromium_src-eb5a9afce9288e65ed0233867692b7ef1b95bb67.zip chromium_src-eb5a9afce9288e65ed0233867692b7ef1b95bb67.tar.gz chromium_src-eb5a9afce9288e65ed0233867692b7ef1b95bb67.tar.bz2 |
Adding AudioInputDeviceManager.
AudioIndputDeviceManager is responsible for tracking which devices has been opened, normally by WebKit. AudioInputDeviceManager is called from MediaStreamManager when a user has approved a webpage to use the audio input device.
AudioInputRendererHost will call AudioInputDeviceManager asking for which device to start, given the session id created when opening the device.
This patch is one of the patches to add support for WhatWG peer connection API.
This patch replaces 7081002 previously uploaded by xians.
BUG=none
TEST=unit_tests
Review URL: http://codereview.chromium.org/7307021
git-svn-id: svn://svn.chromium.org/chrome/trunk/src@93224 0039d316-1c4b-4281-b951-d872f2087c98
Diffstat (limited to 'content')
5 files changed, 858 insertions, 0 deletions
diff --git a/content/browser/renderer_host/media/audio_input_device_manager.cc b/content/browser/renderer_host/media/audio_input_device_manager.cc new file mode 100644 index 0000000..8fafb01 --- /dev/null +++ b/content/browser/renderer_host/media/audio_input_device_manager.cc @@ -0,0 +1,328 @@ +// Copyright (c) 2011 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/browser/renderer_host/media/audio_input_device_manager.h" + +#include "base/memory/scoped_ptr.h" +#include "content/browser/browser_thread.h" +#include "content/browser/renderer_host/media/audio_input_device_manager_event_handler.h" +#include "media/audio/audio_manager.h" + +namespace media_stream { + +const int AudioInputDeviceManager::kFakeOpenSessionId = 0; +const int AudioInputDeviceManager::kInvalidSessionId = -1; +const int AudioInputDeviceManager::kInvalidDevice = -1; +const int AudioInputDeviceManager::kDefaultDeviceIndex = 0; + +// Starting id for the first capture session. +const int kFirstSessionId = AudioInputDeviceManager::kFakeOpenSessionId + 1; + +// Helper function. +static bool IsValidAudioInputDevice(const media::AudioDeviceName& device) { + AudioManager* audio_manager = AudioManager::GetAudioManager(); + if (!audio_manager) + return false; + + // Get the up-to-date list of devices and verify the device is in the list. + media::AudioDeviceNames device_names; + audio_manager->GetAudioInputDeviceNames(&device_names); + if (!device_names.empty()) { + for (media::AudioDeviceNames::iterator iter = device_names.begin(); + iter != device_names.end(); + ++iter) { + if (iter->device_name == device.device_name && + iter->unique_id == device.unique_id) + return true; + } + } + // The device wasn't found. + return false; +} + +AudioInputDeviceManager::AudioInputDeviceManager() + : audio_input_device_thread_("AudioInputDeviceManagerThread"), + listener_(NULL), + next_capture_session_id_(kFirstSessionId) { + audio_input_device_thread_.Start(); +} + +AudioInputDeviceManager::~AudioInputDeviceManager() { + audio_input_device_thread_.Stop(); +} + +void AudioInputDeviceManager::Register(MediaStreamProviderListener* listener) { + DCHECK(BrowserThread::CurrentlyOn(BrowserThread::IO)); + DCHECK(!listener_); + listener_ = listener; +} + +void AudioInputDeviceManager::Unregister() { + DCHECK(BrowserThread::CurrentlyOn(BrowserThread::IO)); + DCHECK(listener_); + listener_ = NULL; +} + +void AudioInputDeviceManager::EnumerateDevices() { + DCHECK(BrowserThread::CurrentlyOn(BrowserThread::IO)); + DCHECK(listener_); + + audio_input_device_thread_.message_loop()->PostTask( + FROM_HERE, + NewRunnableMethod(this, + &AudioInputDeviceManager::EnumerateOnDeviceThread)); +} + +int AudioInputDeviceManager::Open(const StreamDeviceInfo& device) { + DCHECK(BrowserThread::CurrentlyOn(BrowserThread::IO)); + + // Generate a new id for this device. + int audio_input_session_id = next_capture_session_id_++; + + audio_input_device_thread_.message_loop()->PostTask( + FROM_HERE, + NewRunnableMethod(this, + &AudioInputDeviceManager::OpenOnDeviceThread, + audio_input_session_id, + device)); + + return audio_input_session_id; +} + +void AudioInputDeviceManager::Close(int session_id) { + DCHECK(BrowserThread::CurrentlyOn(BrowserThread::IO)); + DCHECK(listener_); + + audio_input_device_thread_.message_loop()->PostTask( + FROM_HERE, + NewRunnableMethod(this, + &AudioInputDeviceManager::CloseOnDeviceThread, + session_id)); +} + +void AudioInputDeviceManager::Start( + int session_id, AudioInputDeviceManagerEventHandler* event_handler) { + DCHECK(BrowserThread::CurrentlyOn(BrowserThread::IO)); + DCHECK(event_handler); + + // Solution for not using MediaStreamManager. This is needed when Start() is + // called without using Open(), we post 0(default device) for test purpose. + // And we do not store the info for the kFakeOpenSessionId but return + // the callback immediately. + if (session_id == kFakeOpenSessionId) { + event_handler->OnStartDevice(session_id, kDefaultDeviceIndex); + return; + } + + // If session has been started, post a callback with an error. + if (event_handlers_.find(session_id) != event_handlers_.end()) { + // Session has been started, post a callback with error. + event_handler->OnStartDevice(session_id, kInvalidDevice); + return; + } + + // Add the event handler to the session. + event_handlers_.insert(std::make_pair(session_id, event_handler)); + + audio_input_device_thread_.message_loop()->PostTask( + FROM_HERE, + NewRunnableMethod(this, + &AudioInputDeviceManager::StartOnDeviceThread, + session_id)); +} + +void AudioInputDeviceManager::Stop(int session_id) { + DCHECK(BrowserThread::CurrentlyOn(BrowserThread::IO)); + + audio_input_device_thread_.message_loop()->PostTask( + FROM_HERE, + NewRunnableMethod(this, + &AudioInputDeviceManager::StopOnDeviceThread, + session_id)); +} + +void AudioInputDeviceManager::EnumerateOnDeviceThread() { + DCHECK(IsOnCaptureDeviceThread()); + + // Get the device list from system. + media::AudioDeviceNames device_names; + AudioManager::GetAudioManager()->GetAudioInputDeviceNames(&device_names); + + StreamDeviceInfoArray devices; + for (media::AudioDeviceNames::iterator it = device_names.begin(); + it != device_names.end(); + ++it) { + devices.push_back(StreamDeviceInfo(kAudioCapture, it->device_name, + it->unique_id, false)); + } + + BrowserThread::PostTask( + BrowserThread::IO, + FROM_HERE, + NewRunnableMethod(this, + &AudioInputDeviceManager::DevicesEnumeratedOnIOThread, + devices)); +} + +void AudioInputDeviceManager::OpenOnDeviceThread( + int session_id, const StreamDeviceInfo& device) { + DCHECK(IsOnCaptureDeviceThread()); + DCHECK(devices_.find(session_id) == devices_.end()); + + media::AudioDeviceName audio_input_device_name; + audio_input_device_name.device_name = device.name; + audio_input_device_name.unique_id = device.device_id; + + // Check if the device is valid + if (!IsValidAudioInputDevice(audio_input_device_name)) { + SignalError(session_id, kDeviceNotAvailable); + return; + } + + // Add the session_id and device to the list. + devices_[session_id] = audio_input_device_name; + BrowserThread::PostTask(BrowserThread::IO, + FROM_HERE, + NewRunnableMethod( + this, + &AudioInputDeviceManager::OpenedOnIOThread, + session_id)); +} + +void AudioInputDeviceManager::CloseOnDeviceThread(int session_id) { + DCHECK(IsOnCaptureDeviceThread()); + DCHECK(devices_.find(session_id) != devices_.end()); + + devices_.erase(session_id); + BrowserThread::PostTask(BrowserThread::IO, + FROM_HERE, + NewRunnableMethod( + this, + &AudioInputDeviceManager::ClosedOnIOThread, + session_id)); +} + +void AudioInputDeviceManager::StartOnDeviceThread(const int session_id) { + DCHECK(IsOnCaptureDeviceThread()); + + // Get the up-to-date device enumeration list from the system and find out + // the index of the device. + int device_index = kInvalidDevice; + AudioInputDeviceMap::const_iterator it = devices_.find(session_id); + if (it != devices_.end()) { + media::AudioDeviceNames device_names; + AudioManager::GetAudioManager()->GetAudioInputDeviceNames(&device_names); + if (!device_names.empty()) { + int index = 0; + for (media::AudioDeviceNames::iterator iter = device_names.begin(); + iter != device_names.end(); + ++iter, ++index) { + if (iter->device_name == it->second.device_name && + iter->unique_id == it->second.unique_id) { + // Found the device. + device_index = index; + break; + } + } + } + } + // Posts the index to AudioInputRenderHost through the event handler. + BrowserThread::PostTask(BrowserThread::IO, + FROM_HERE, + NewRunnableMethod( + this, + &AudioInputDeviceManager::StartedOnIOThread, + session_id, + device_index)); +} + +void AudioInputDeviceManager::StopOnDeviceThread(int session_id) { + DCHECK(IsOnCaptureDeviceThread()); + BrowserThread::PostTask(BrowserThread::IO, + FROM_HERE, + NewRunnableMethod( + this, + &AudioInputDeviceManager::StoppedOnIOThread, + session_id)); +} + +void AudioInputDeviceManager::DevicesEnumeratedOnIOThread( + const StreamDeviceInfoArray& devices) { + DCHECK(BrowserThread::CurrentlyOn(BrowserThread::IO)); + if (listener_) + listener_->DevicesEnumerated(kAudioCapture, devices); +} + +void AudioInputDeviceManager::OpenedOnIOThread(int session_id) { + DCHECK(BrowserThread::CurrentlyOn(BrowserThread::IO)); + if (listener_) + listener_->Opened(kAudioCapture, session_id); +} + +void AudioInputDeviceManager::ClosedOnIOThread(int session_id) { + DCHECK(BrowserThread::CurrentlyOn(BrowserThread::IO)); + EventHandlerMap::iterator it = event_handlers_.find(session_id); + if (it != event_handlers_.end()) { + // The device hasn't been stopped, send stop signal. + it->second->OnStopDevice(session_id); + event_handlers_.erase(session_id); + } + listener_->Closed(kAudioCapture, session_id); +} + + +void AudioInputDeviceManager::ErrorOnIOThread(int session_id, + MediaStreamProviderError error) { + DCHECK(BrowserThread::CurrentlyOn(BrowserThread::IO)); + if (listener_) + listener_->Error(kAudioCapture, session_id, error); +} + +void AudioInputDeviceManager::StartedOnIOThread(int session_id, int index) { + DCHECK(BrowserThread::CurrentlyOn(BrowserThread::IO)); + + EventHandlerMap::iterator it = event_handlers_.find(session_id); + if (it == event_handlers_.end()) + return; + + // Post a callback through the event handler to start the device. + it->second->OnStartDevice(session_id, index); +} + +void AudioInputDeviceManager::StoppedOnIOThread(int session_id) { + DCHECK(BrowserThread::CurrentlyOn(BrowserThread::IO)); + // Erase the event handler referenced by the session_id. + event_handlers_.erase(session_id); +} + +void AudioInputDeviceManager::SignalError(int session_id, + MediaStreamProviderError error) { + BrowserThread::PostTask(BrowserThread::IO, + FROM_HERE, + NewRunnableMethod( + this, + &AudioInputDeviceManager::ErrorOnIOThread, + session_id, + error)); +} + +bool AudioInputDeviceManager::IsOnCaptureDeviceThread() const { + return MessageLoop::current() == audio_input_device_thread_.message_loop(); +} + +void AudioInputDeviceManager::UnregisterEventHandler(int session_id) { + DCHECK(BrowserThread::CurrentlyOn(BrowserThread::IO)); + event_handlers_.erase(session_id); +} + +bool AudioInputDeviceManager::HasEventHandler(int session_id) { + DCHECK(BrowserThread::CurrentlyOn(BrowserThread::IO)); + return event_handlers_.find(session_id) != event_handlers_.end(); +} + +MessageLoop* AudioInputDeviceManager::message_loop() { + return audio_input_device_thread_.message_loop(); +} + +} // namespace media_stream diff --git a/content/browser/renderer_host/media/audio_input_device_manager.h b/content/browser/renderer_host/media/audio_input_device_manager.h new file mode 100644 index 0000000..7ca04c3 --- /dev/null +++ b/content/browser/renderer_host/media/audio_input_device_manager.h @@ -0,0 +1,104 @@ +// Copyright (c) 2011 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. +// +// AudioInputDeviceManager manages the audio input devices. In particular it +// communicates with MediaStreamManager and AudioInputRendererHost on the +// browser IO thread, handles queries like enumerate/open/close from +// MediaStreamManager and start/stop from AudioInputRendererHost. + +// All the queries come from the IO thread, while the work to enumerate devices +// is done on its own thread. + +#ifndef CONTENT_BROWSER_RENDERER_HOST_MEDIA_AUDIO_INPUT_DEVICE_MANAGER_H_ +#define CONTENT_BROWSER_RENDERER_HOST_MEDIA_AUDIO_INPUT_DEVICE_MANAGER_H_ + +#include <map> + +#include "base/threading/thread.h" +#include "content/browser/renderer_host/media/media_stream_provider.h" +#include "content/common/media/media_stream_options.h" +#include "media/audio/audio_device_name.h" + +namespace media_stream { + +class AudioInputDeviceManagerEventHandler; + +class AudioInputDeviceManager : public MediaStreamProvider { + public: + // Calling Start() with this kFakeOpenSessionId will open the default device, + // even though Open() has not been called. This is used to be able to use the + // AudioInputDeviceManager before MediaStream is implemented. + static const int kFakeOpenSessionId; + static const int kInvalidSessionId; + static const int kInvalidDevice; + static const int kDefaultDeviceIndex; + + AudioInputDeviceManager(); + virtual ~AudioInputDeviceManager(); + + // MediaStreamProvider implementation, called on IO thread. + virtual void Register(MediaStreamProviderListener* listener); + virtual void Unregister(); + virtual void EnumerateDevices(); + virtual int Open(const StreamDeviceInfo& device); + virtual void Close(int session_id); + + // Functions used by AudioInputRenderHost, called on IO thread. + // Start the device referenced by the session id. + void Start(int session_id, + AudioInputDeviceManagerEventHandler* event_handler); + // Stop the device referenced by the session id. + void Stop(int session_id); + + // Function used for testing to mock platform dependent device code. + MessageLoop* message_loop(); + + private: + // Executed on audio_input_device_thread_. + void EnumerateOnDeviceThread(); + void OpenOnDeviceThread(int session_id, const StreamDeviceInfo& device); + void CloseOnDeviceThread(int session_id); + void StartOnDeviceThread(int session_id); + void StopOnDeviceThread(int session_id); + + // Executed on IO thread to call Listener. + void DevicesEnumeratedOnIOThread(const StreamDeviceInfoArray& devices); + void OpenedOnIOThread(int session_id); + void ClosedOnIOThread(int session_id); + void ErrorOnIOThread(int session_id, MediaStreamProviderError error); + + // Executed on IO thread to call the event handler. + void StartedOnIOThread(int session_id, int index); + void StoppedOnIOThread(int session_id); + + // Executed on audio_input_device_thread_ to make sure + // MediaStreamProviderListener is called from IO thread. + void SignalError(int session_id, MediaStreamProviderError error); + + // Helpers. + bool IsOnCaptureDeviceThread() const; + void UnregisterEventHandler(int session_id); + bool HasEventHandler(int session_id); + + // Thread for all calls to AudioInputDeviceManager. + base::Thread audio_input_device_thread_; + + // Only accessed on Browser::IO thread. + MediaStreamProviderListener* listener_; + int next_capture_session_id_; + typedef std::map<int, AudioInputDeviceManagerEventHandler*> EventHandlerMap; + EventHandlerMap event_handlers_; + + // Only accessed from audio_input_device_thread_. + typedef std::map<int, media::AudioDeviceName> AudioInputDeviceMap; + AudioInputDeviceMap devices_; + + DISALLOW_COPY_AND_ASSIGN(AudioInputDeviceManager); +}; + +} // namespace media_stream + +DISABLE_RUNNABLE_METHOD_REFCOUNT(media_stream::AudioInputDeviceManager); + +#endif // CONTENT_BROWSER_RENDERER_HOST_MEDIA_AUDIO_INPUT_DEVICE_MANAGER_H_ diff --git a/content/browser/renderer_host/media/audio_input_device_manager_event_handler.h b/content/browser/renderer_host/media/audio_input_device_manager_event_handler.h new file mode 100644 index 0000000..75cb984 --- /dev/null +++ b/content/browser/renderer_host/media/audio_input_device_manager_event_handler.h @@ -0,0 +1,27 @@ +// Copyright (c) 2011 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. + +// AudioInputDeviceManagerEventHandler is used to signal events from +// AudioInoutDeviceManager when it's time to start and stop devices. + +#ifndef CONTENT_BROWSER_RENDERER_HOST_MEDIA_AUDIO_INPUT_DEVICE_MANAGER_EVENT_HANDLER_H_ +#define CONTENT_BROWSER_RENDERER_HOST_MEDIA_AUDIO_INPUT_DEVICE_MANAGER_EVENT_HANDLER_H_ + +namespace media_stream { + +class AudioInputDeviceManagerEventHandler { + public: + // Used to start the device referenced by session id and index. + virtual void OnStartDevice(int session_id, int index) = 0; + + // Used to stop the device referenced by session id. This method is used + // only when users call Close() without calling Stop() on a started device. + virtual void OnStopDevice(int session_id) = 0; + + virtual ~AudioInputDeviceManagerEventHandler() {} +}; + +} // namespace media_stream + +#endif // CONTENT_BROWSER_RENDERER_HOST_MEDIA_AUDIO_INPUT_DEVICE_MANAGER_EVENT_HANDLER_H_ diff --git a/content/browser/renderer_host/media/audio_input_device_manager_unittest.cc b/content/browser/renderer_host/media/audio_input_device_manager_unittest.cc new file mode 100644 index 0000000..de27d5d --- /dev/null +++ b/content/browser/renderer_host/media/audio_input_device_manager_unittest.cc @@ -0,0 +1,396 @@ +// Copyright (c) 2011 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 <string> + +#include "base/memory/scoped_ptr.h" +#include "base/message_loop.h" +#include "content/browser/browser_thread.h" +#include "content/browser/renderer_host/media/audio_input_device_manager.h" +#include "content/browser/renderer_host/media/audio_input_device_manager_event_handler.h" +#include "media/audio/audio_manager.h" +#include "testing/gmock/include/gmock/gmock.h" +#include "testing/gtest/include/gtest/gtest.h" + +using ::testing::_; +using ::testing::AnyNumber; +using ::testing::InSequence; +using ::testing::Return; + +namespace media_stream { + +class MockAudioInputDeviceManagerListener + : public MediaStreamProviderListener { + public: + MockAudioInputDeviceManagerListener() {} + virtual ~MockAudioInputDeviceManagerListener() {} + + MOCK_METHOD2(Opened, void(MediaStreamType, const int)); + MOCK_METHOD2(Closed, void(MediaStreamType, const int)); + MOCK_METHOD1(DevicesEnumerated, void(const StreamDeviceInfoArray&)); + MOCK_METHOD3(Error, void(MediaStreamType, int, MediaStreamProviderError)); + + virtual void DevicesEnumerated(MediaStreamType service_type, + const StreamDeviceInfoArray& devices) { + if (service_type != kAudioCapture) + return; + + devices_ = devices; + DevicesEnumerated(devices); + } + + StreamDeviceInfoArray devices_; + + private: + DISALLOW_COPY_AND_ASSIGN(MockAudioInputDeviceManagerListener); +}; + +class MockAudioInputDeviceManagerEventHandler + : public AudioInputDeviceManagerEventHandler { + public: + MockAudioInputDeviceManagerEventHandler() {} + virtual ~MockAudioInputDeviceManagerEventHandler() {} + + MOCK_METHOD2(OnStartDevice, void(int, int)); + MOCK_METHOD1(OnStopDevice, void(int)); + + private: + DISALLOW_COPY_AND_ASSIGN(MockAudioInputDeviceManagerEventHandler); +}; + +// Returns true if machine has audio input device, else returns false. +static bool CanRunAudioInputDeviceTests() { + AudioManager* audio_manager = AudioManager::GetAudioManager(); + if (!audio_manager) + return false; + + return audio_manager->HasAudioInputDevices(); +} + +ACTION_P(ExitMessageLoop, message_loop) { + message_loop->PostTask(FROM_HERE, new MessageLoop::QuitTask()); +} + +class AudioInputDeviceManagerTest: public testing::Test { + public: + AudioInputDeviceManagerTest() + : message_loop_(), + io_thread_(), + manager_(), + audio_input_listener_() {} + + protected: + virtual void SetUp() { + // The test must run on Browser::IO. + message_loop_.reset(new MessageLoop(MessageLoop::TYPE_IO)); + io_thread_.reset(new BrowserThread(BrowserThread::IO, message_loop_.get())); + manager_.reset(new media_stream::AudioInputDeviceManager()); + audio_input_listener_.reset(new MockAudioInputDeviceManagerListener()); + manager_->Register(audio_input_listener_.get()); + + // Get the enumerated device list from the AudioInputDeviceManager. + manager_->EnumerateDevices(); + EXPECT_CALL(*audio_input_listener_, DevicesEnumerated(_)) + .Times(1); + // Sync up the threads to make sure we get the list. + SyncWithAudioInputDeviceManagerThread(); + } + + virtual void TearDown() { + manager_->Unregister(); + io_thread_.reset(); + } + + // Called on the AudioInputDeviceManager thread. + static void PostQuitMessageLoop(MessageLoop* message_loop) { + message_loop->PostTask(FROM_HERE, new MessageLoop::QuitTask()); + } + + // Called on the main thread. + static void PostQuitOnAudioInputDeviceManagerThread( + MessageLoop* message_loop, AudioInputDeviceManager* manager) { + manager->message_loop()->PostTask( + FROM_HERE, NewRunnableFunction(&PostQuitMessageLoop, message_loop)); + } + + // SyncWithAudioInputDeviceManagerThread() waits until all pending tasks on + // the audio_input_device_manager thread are executed while also processing + // pending task in message_loop_ on the current thread. + void SyncWithAudioInputDeviceManagerThread() { + message_loop_->PostTask( + FROM_HERE, + NewRunnableFunction(&PostQuitOnAudioInputDeviceManagerThread, + message_loop_.get(), + manager_.get())); + message_loop_->Run(); + } + scoped_ptr<MessageLoop> message_loop_; + scoped_ptr<BrowserThread> io_thread_; + scoped_ptr<AudioInputDeviceManager> manager_; + scoped_ptr<MockAudioInputDeviceManagerListener> audio_input_listener_; + + private: + DISALLOW_COPY_AND_ASSIGN(AudioInputDeviceManagerTest); +}; + +// Test the devices can be opened and closed. +TEST_F(AudioInputDeviceManagerTest, OpenAndCloseDevice) { + if (!CanRunAudioInputDeviceTests()) + return; + InSequence s; + + for (StreamDeviceInfoArray::const_iterator iter = + audio_input_listener_->devices_.begin(); + iter != audio_input_listener_->devices_.end(); ++iter) { + // Open/close the devices. + int session_id = manager_->Open(*iter); + manager_->Close(session_id); + + // Expected mock call with expected return value. + EXPECT_CALL(*audio_input_listener_, Opened(kAudioCapture, session_id)) + .Times(1); + EXPECT_CALL(*audio_input_listener_, Closed(kAudioCapture, session_id)) + .Times(1); + SyncWithAudioInputDeviceManagerThread(); + } +} + +// Open multiple devices at one time and close them later. +TEST_F(AudioInputDeviceManagerTest, OpenMultipleDevices) { + if (!CanRunAudioInputDeviceTests()) + return; + InSequence s; + + int index = 0; + const int kDeviceSize = audio_input_listener_->devices_.size(); + scoped_array<int> session_id(new int[kDeviceSize]); + + // Open the devices in a loop. + for (StreamDeviceInfoArray::const_iterator iter = + audio_input_listener_->devices_.begin(); + iter != audio_input_listener_->devices_.end(); ++iter, ++index) { + // Open the devices. + session_id[index] = manager_->Open(*iter); + + // Expected mock call with expected return value. + EXPECT_CALL(*audio_input_listener_, Opened(kAudioCapture, + session_id[index])) + .Times(1); + SyncWithAudioInputDeviceManagerThread(); + } + + // Check if the session_ids are unique + for (int i = 0; i < kDeviceSize - 1; ++i) { + for (int k = i+1; k < kDeviceSize; ++k) { + EXPECT_TRUE(session_id[i] != session_id[k]); + } + } + + for (int i = 0; i < kDeviceSize; ++i) { + // Close the devices. + manager_->Close(session_id[i]); + EXPECT_CALL(*audio_input_listener_, Closed(kAudioCapture, session_id[i])) + .Times(1); + SyncWithAudioInputDeviceManagerThread(); + } +} + +// Try to open a non-existing device. +TEST_F(AudioInputDeviceManagerTest, OpenNotExistingDevice) { + if (!CanRunAudioInputDeviceTests()) + return; + InSequence s; + + MediaStreamType stream_type = kAudioCapture; + std::string device_name("device_doesnt_exist"); + std::string device_id("id_doesnt_exist"); + StreamDeviceInfo dummy_device(stream_type, device_name, device_id, false); + + // This should fail and trigger error code 'kDeviceNotAvailable'. + int session_id = manager_->Open(dummy_device); + + EXPECT_CALL(*audio_input_listener_, Error(_, session_id, kDeviceNotAvailable)) + .Times(1); + SyncWithAudioInputDeviceManagerThread(); +} + +// Try open an invalid device. +TEST_F(AudioInputDeviceManagerTest, OpenInvalidDevice) { + if (!CanRunAudioInputDeviceTests()) + return; + InSequence s; + + MediaStreamType stream_type = kAudioCapture; + std::string device_name; + std::string device_id; + device_name = audio_input_listener_->devices_.front().name; + device_id = "wrong_id"; + StreamDeviceInfo invalid_device(stream_type, device_name, device_id, false); + + // This should fail and trigger error code 'kDeviceNotAvailable'. + int session_id = manager_->Open(invalid_device); + + EXPECT_CALL(*audio_input_listener_, Error(_, session_id, kDeviceNotAvailable)) + .Times(1); + SyncWithAudioInputDeviceManagerThread(); +} + +// Opening default device twice should work. +TEST_F(AudioInputDeviceManagerTest, OpenDeviceTwice) { + if (!CanRunAudioInputDeviceTests()) + return; + InSequence s; + + // Open/close the default device twice. + int first_session_id = manager_->Open( + audio_input_listener_->devices_.front()); + int second_session_id = manager_->Open( + audio_input_listener_->devices_.front()); + manager_->Close(first_session_id); + manager_->Close(second_session_id); + + // Expected mock calls with expected return values. + EXPECT_NE(first_session_id, second_session_id); + EXPECT_CALL(*audio_input_listener_, Opened(kAudioCapture, first_session_id)) + .Times(1); + EXPECT_CALL(*audio_input_listener_, Opened(kAudioCapture, second_session_id)) + .Times(1); + EXPECT_CALL(*audio_input_listener_, Closed(kAudioCapture, first_session_id)) + .Times(1); + EXPECT_CALL(*audio_input_listener_, Closed(kAudioCapture, second_session_id)) + .Times(1); + SyncWithAudioInputDeviceManagerThread(); +} + +// Test the Start and Close function after opening the devices. +TEST_F(AudioInputDeviceManagerTest, StartAndStopDevice) { + if (!CanRunAudioInputDeviceTests()) + return; + InSequence s; + + int index = 0; + const int kDeviceSize = audio_input_listener_->devices_.size(); + scoped_array<int> session_id(new int[kDeviceSize]); + + // Create the EventHandler for the sessions. + scoped_ptr<MockAudioInputDeviceManagerEventHandler> + audio_input_event_handler(new MockAudioInputDeviceManagerEventHandler()); + + // Loop through the devices, and Open/start/stop/close each device. + for (StreamDeviceInfoArray::const_iterator iter = + audio_input_listener_->devices_.begin(); + iter != audio_input_listener_->devices_.end(); ++iter, ++index) { + // Note that no stop device notification for Event Handler as we have + // stopped the device before calling close. + // Open/start/stop/close the device. + session_id[index] = manager_->Open(*iter); + manager_->Start(session_id[index], audio_input_event_handler.get()); + manager_->Stop(session_id[index]); + manager_->Close(session_id[index]); + + // Expected mock calls with expected return values. + EXPECT_CALL(*audio_input_listener_, Opened(kAudioCapture, + session_id[index])) + .Times(1); + EXPECT_CALL(*audio_input_event_handler, + OnStartDevice(session_id[index], index)) + .Times(1); + EXPECT_CALL(*audio_input_listener_, Closed(kAudioCapture, + session_id[index])) + .Times(1); + SyncWithAudioInputDeviceManagerThread(); + } +} + +// Test the behavior of calling Close without calling Stop. +TEST_F(AudioInputDeviceManagerTest, CloseWithoutStopDevice) { + if (!CanRunAudioInputDeviceTests()) + return; + InSequence s; + + int index = 0; + const int kDeviceSize = audio_input_listener_->devices_.size(); + scoped_array<int> session_id(new int[kDeviceSize]); + + // Create the EventHandlers for the sessions. + scoped_ptr<MockAudioInputDeviceManagerEventHandler> + audio_input_event_handler(new MockAudioInputDeviceManagerEventHandler()); + + // Loop through the devices, and open/start/close the devices. + // Note that we do not call stop. + for (StreamDeviceInfoArray::const_iterator iter = + audio_input_listener_->devices_.begin(); + iter != audio_input_listener_->devices_.end(); ++iter, ++index) { + // Open/start/close the device. + session_id[index] = manager_->Open(*iter); + manager_->Start(session_id[index], audio_input_event_handler.get()); + manager_->Close(session_id[index]); + + // Expected mock calls with expected return values. + EXPECT_CALL(*audio_input_listener_, Opened(kAudioCapture, + session_id[index])) + .Times(1); + EXPECT_CALL(*audio_input_event_handler, + OnStartDevice(session_id[index], index)) + .Times(1); + // Event Handler should get a stop device notification as no stop is called + // before closing the device. + EXPECT_CALL(*audio_input_event_handler, + OnStopDevice(session_id[index])) + .Times(1); + EXPECT_CALL(*audio_input_listener_, Closed(kAudioCapture, + session_id[index])) + .Times(1); + SyncWithAudioInputDeviceManagerThread(); + } +} + +// Should be able to start the default device twice. +TEST_F(AudioInputDeviceManagerTest, StartDeviceTwice) { + if (!CanRunAudioInputDeviceTests()) + return; + InSequence s; + + // Create one EventHandler for each session. + scoped_ptr<MockAudioInputDeviceManagerEventHandler> + first_audio_input_event_handler( + new MockAudioInputDeviceManagerEventHandler()); + scoped_ptr<MockAudioInputDeviceManagerEventHandler> + second_audio_input_event_handler( + new MockAudioInputDeviceManagerEventHandler()); + + // Open the default device twice. + StreamDeviceInfoArray::const_iterator iter = + audio_input_listener_->devices_.begin(); + int first_session_id = manager_->Open(*iter); + int second_session_id = manager_->Open(*iter); + + // Start/stop/close the default device twice. + manager_->Start(first_session_id, first_audio_input_event_handler.get()); + manager_->Start(second_session_id, second_audio_input_event_handler.get()); + manager_->Stop(first_session_id); + manager_->Stop(second_session_id); + manager_->Close(first_session_id); + manager_->Close(second_session_id); + + // Expected mock calls with expected return values. + EXPECT_NE(first_session_id, second_session_id); + EXPECT_CALL(*audio_input_listener_, Opened(kAudioCapture, first_session_id)) + .Times(1); + EXPECT_CALL(*audio_input_listener_, Opened(kAudioCapture, second_session_id)) + .Times(1); + EXPECT_CALL(*first_audio_input_event_handler, + OnStartDevice(first_session_id, 0)) + .Times(1); + EXPECT_CALL(*second_audio_input_event_handler, + OnStartDevice(second_session_id, 0)) + .Times(1); + EXPECT_CALL(*audio_input_listener_, Closed(kAudioCapture, first_session_id)) + .Times(1); + EXPECT_CALL(*audio_input_listener_, Closed(kAudioCapture, second_session_id)) + .Times(1); + SyncWithAudioInputDeviceManagerThread(); +} + +} // namespace media_stream diff --git a/content/content_browser.gypi b/content/content_browser.gypi index 67e5a17..0017034 100644 --- a/content/content_browser.gypi +++ b/content/content_browser.gypi @@ -285,6 +285,9 @@ 'browser/renderer_host/gpu_message_filter.h', 'browser/renderer_host/media/audio_common.cc', 'browser/renderer_host/media/audio_common.h', + 'browser/renderer_host/media/audio_input_device_manager.cc', + 'browser/renderer_host/media/audio_input_device_manager.h', + 'browser/renderer_host/media/audio_input_device_manager_event_handler.h', 'browser/renderer_host/media/audio_input_renderer_host.cc', 'browser/renderer_host/media/audio_input_renderer_host.h', 'browser/renderer_host/media/audio_input_sync_writer.cc', |