diff options
author | henrika@chromium.org <henrika@chromium.org@0039d316-1c4b-4281-b951-d872f2087c98> | 2012-02-03 15:02:12 +0000 |
---|---|---|
committer | henrika@chromium.org <henrika@chromium.org@0039d316-1c4b-4281-b951-d872f2087c98> | 2012-02-03 15:02:12 +0000 |
commit | 8bc74b44f18a1aad65c56d827b5f8f14c7e123b2 (patch) | |
tree | a681d9990b7cad9885af2f53c607aef045940101 /media | |
parent | f0f67736354e2e0162811a2308d7f2853ec81eb9 (diff) | |
download | chromium_src-8bc74b44f18a1aad65c56d827b5f8f14c7e123b2.zip chromium_src-8bc74b44f18a1aad65c56d827b5f8f14c7e123b2.tar.gz chromium_src-8bc74b44f18a1aad65c56d827b5f8f14c7e123b2.tar.bz2 |
Removes the AudioInputControllerThread from AudioInputController.
Now runs on the audio-manager thread instead.
BUG=none
TEST=media_unitest and content_unittests
Review URL: https://chromiumcodereview.appspot.com/9307049
git-svn-id: svn://svn.chromium.org/chrome/trunk/src@120330 0039d316-1c4b-4281-b951-d872f2087c98
Diffstat (limited to 'media')
-rw-r--r-- | media/audio/audio_input_controller.cc | 129 | ||||
-rw-r--r-- | media/audio/audio_input_controller.h | 149 | ||||
-rw-r--r-- | media/audio/audio_input_controller_unittest.cc | 151 | ||||
-rw-r--r-- | media/audio/audio_output_controller_unittest.cc | 14 | ||||
-rw-r--r-- | media/audio/test_audio_input_controller_factory.cc | 4 | ||||
-rw-r--r-- | media/audio/test_audio_input_controller_factory.h | 5 |
6 files changed, 286 insertions, 166 deletions
diff --git a/media/audio/audio_input_controller.cc b/media/audio/audio_input_controller.cc index 7bb1d26..824bb17 100644 --- a/media/audio/audio_input_controller.cc +++ b/media/audio/audio_input_controller.cc @@ -21,7 +21,9 @@ AudioInputController::Factory* AudioInputController::factory_ = NULL; AudioInputController::AudioInputController(AudioManager* audio_manager, EventHandler* handler, SyncWriter* sync_writer) - : audio_manager_(audio_manager), + : creator_loop_(base::MessageLoopProxy::current()), + audio_manager_(audio_manager), + message_loop_(audio_manager->GetMessageLoop()), handler_(handler), stream_(NULL), ALLOW_THIS_IN_INITIALIZER_LIST(no_data_timer_(FROM_HERE, @@ -29,9 +31,8 @@ AudioInputController::AudioInputController(AudioManager* audio_manager, this, &AudioInputController::DoReportNoDataError)), state_(kEmpty), - thread_("AudioInputControllerThread"), sync_writer_(sync_writer) { - DCHECK(audio_manager_); // Fail early. + DCHECK(creator_loop_); } AudioInputController::~AudioInputController() { @@ -44,6 +45,7 @@ scoped_refptr<AudioInputController> AudioInputController::Create( EventHandler* event_handler, const AudioParameters& params) { DCHECK(audio_manager); + if (!params.IsValid() || (params.channels > kMaxInputChannels)) return NULL; @@ -54,13 +56,13 @@ scoped_refptr<AudioInputController> AudioInputController::Create( scoped_refptr<AudioInputController> controller(new AudioInputController( audio_manager, event_handler, NULL)); - // Start the thread and post a task to create the audio input stream. - // Pass an empty string to indicate using default device. + // Create and open a new audio input stream from the existing + // audio-device thread. Use the default audio-input device. std::string device_id = AudioManagerBase::kDefaultDeviceId; - controller->thread_.Start(); - controller->thread_.message_loop()->PostTask(FROM_HERE, base::Bind( - &AudioInputController::DoCreate, controller.get(), + controller->message_loop_->PostTask(FROM_HERE, base::Bind( + &AudioInputController::DoCreate, base::Unretained(controller.get()), params, device_id)); + return controller; } @@ -77,47 +79,35 @@ scoped_refptr<AudioInputController> AudioInputController::CreateLowLatency( if (!params.IsValid() || (params.channels > kMaxInputChannels)) return NULL; - // Starts the audio controller thread. + // Create the AudioInputController object and ensure that it runs on + // the audio-manager thread. scoped_refptr<AudioInputController> controller(new AudioInputController( audio_manager, event_handler, sync_writer)); - // Start the thread and post a task to create the audio input stream. - controller->thread_.Start(); - controller->thread_.message_loop()->PostTask(FROM_HERE, base::Bind( - &AudioInputController::DoCreate, controller.get(), params, device_id)); + // Create and open a new audio input stream from the existing + // audio-device thread. Use the provided audio-input device. + controller->message_loop_->PostTask(FROM_HERE, base::Bind( + &AudioInputController::DoCreate, base::Unretained(controller.get()), + params, device_id)); + return controller; } void AudioInputController::Record() { - DCHECK(thread_.IsRunning()); - thread_.message_loop()->PostTask(FROM_HERE, base::Bind( - &AudioInputController::DoRecord, this)); + message_loop_->PostTask(FROM_HERE, base::Bind( + &AudioInputController::DoRecord, base::Unretained(this))); } -void AudioInputController::Close() { - if (!thread_.IsRunning()) { - // If the thread is not running make sure we are stopped. - DCHECK_EQ(kClosed, state_); - return; - } - - // Wait for all tasks to complete on the audio thread. - thread_.message_loop()->PostTask(FROM_HERE, base::Bind( - &AudioInputController::DoClose, this)); - - // A ScopedAllowIO object is required to join the thread when calling Stop. - // This is because as joining threads may be a long operation it's now - // not allowed in threads without IO access, which is the case of the IO - // thread (it is missnamed) being used here. This object overrides - // temporarily this restriction and should be used only in specific - // infrequent cases where joining is guaranteed to be fast. - // Bug: http://code.google.com/p/chromium/issues/detail?id=67806 - base::ThreadRestrictions::ScopedAllowIO allow_io_for_thread_join; - thread_.Stop(); +void AudioInputController::Close(const base::Closure& closed_task) { + DCHECK(!closed_task.is_null()); + message_loop_->PostTask(FROM_HERE, base::Bind( + &AudioInputController::DoClose, base::Unretained(this), closed_task)); } void AudioInputController::DoCreate(const AudioParameters& params, const std::string& device_id) { + DCHECK(message_loop_->BelongsToCurrentThread()); + stream_ = audio_manager_->MakeAudioInputStream(params, device_id); if (!stream_) { @@ -134,14 +124,15 @@ void AudioInputController::DoCreate(const AudioParameters& params, return; } - thread_.message_loop()->PostTask(FROM_HERE, base::Bind( + creator_loop_->PostTask(FROM_HERE, base::Bind( &AudioInputController::DoResetNoDataTimer, this)); + state_ = kCreated; handler_->OnCreated(this); } void AudioInputController::DoRecord() { - DCHECK_EQ(thread_.message_loop(), MessageLoop::current()); + DCHECK(message_loop_->BelongsToCurrentThread()); if (state_ != kCreated) return; @@ -155,39 +146,38 @@ void AudioInputController::DoRecord() { handler_->OnRecording(this); } -void AudioInputController::DoClose() { - DCHECK_EQ(thread_.message_loop(), MessageLoop::current()); - DCHECK_NE(kClosed, state_); +void AudioInputController::DoClose(const base::Closure& closed_task) { + DCHECK(message_loop_->BelongsToCurrentThread()); - // |stream_| can be null if creating the device failed in DoCreate(). - if (stream_) { - stream_->Stop(); - stream_->Close(); - // After stream is closed it is destroyed, so don't keep a reference to it. - stream_ = NULL; - } + if (state_ != kClosed) { + DoStopCloseAndClearStream(NULL); - if (LowLatencyMode()) { - sync_writer_->Close(); + if (LowLatencyMode()) { + sync_writer_->Close(); + } + + state_ = kClosed; } - // Since the stream is closed at this point there's no other threads reading - // |state_| so we don't need to lock. - state_ = kClosed; + closed_task.Run(); } void AudioInputController::DoReportError(int code) { - DCHECK_EQ(thread_.message_loop(), MessageLoop::current()); + DCHECK(message_loop_->BelongsToCurrentThread()); handler_->OnError(this, code); } void AudioInputController::DoReportNoDataError() { - DCHECK_EQ(thread_.message_loop(), MessageLoop::current()); - handler_->OnError(this, 0); + DCHECK(creator_loop_->BelongsToCurrentThread()); + + // Error notifications should be sent on the audio-manager thread. + int code = 0; + message_loop_->PostTask(FROM_HERE, base::Bind( + &AudioInputController::DoReportError, base::Unretained(this), code)); } void AudioInputController::DoResetNoDataTimer() { - DCHECK_EQ(thread_.message_loop(), MessageLoop::current()); + DCHECK(creator_loop_->BelongsToCurrentThread()); no_data_timer_.Reset(); } @@ -199,7 +189,7 @@ void AudioInputController::OnData(AudioInputStream* stream, const uint8* data, return; } - thread_.message_loop()->PostTask(FROM_HERE, base::Bind( + creator_loop_->PostTask(FROM_HERE, base::Bind(
&AudioInputController::DoResetNoDataTimer, this)); // Use SyncSocket if we are in a low-latency mode. @@ -213,15 +203,32 @@ void AudioInputController::OnData(AudioInputStream* stream, const uint8* data, } void AudioInputController::OnClose(AudioInputStream* stream) { + DVLOG(1) << "AudioInputController::OnClose()"; // TODO(satish): Sometimes the device driver closes the input stream without // us asking for it (may be if the device was unplugged?). Check how to handle // such cases here. } void AudioInputController::OnError(AudioInputStream* stream, int code) { - // Handle error on the audio controller thread. - thread_.message_loop()->PostTask(FROM_HERE, base::Bind( - &AudioInputController::DoReportError, this, code)); + // Handle error on the audio-manager thread. + message_loop_->PostTask(FROM_HERE, base::Bind( + &AudioInputController::DoReportError, base::Unretained(this), code)); +} + +void AudioInputController::DoStopCloseAndClearStream( + base::WaitableEvent *done) { + DCHECK(message_loop_->BelongsToCurrentThread()); + + // Allow calling unconditionally and bail if we don't have a stream to close. + if (stream_ != NULL) { + stream_->Stop(); + stream_->Close(); + stream_ = NULL; + } + + // Should be last in the method, do not touch "this" from here on. + if (done != NULL) + done->Signal(); } } // namespace media diff --git a/media/audio/audio_input_controller.h b/media/audio/audio_input_controller.h index e929467..b92a8c3 100644 --- a/media/audio/audio_input_controller.h +++ b/media/audio/audio_input_controller.h @@ -1,4 +1,4 @@ -// Copyright (c) 2011 The Chromium Authors. All rights reserved. +// 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. @@ -6,21 +6,22 @@ #define MEDIA_AUDIO_AUDIO_INPUT_CONTROLLER_H_ #include <string> +#include "base/callback.h" #include "base/memory/ref_counted.h" #include "base/memory/scoped_ptr.h" #include "base/synchronization/lock.h" +#include "base/synchronization/waitable_event.h" #include "base/threading/thread.h" #include "base/timer.h" #include "media/audio/audio_io.h" #include "media/audio/audio_manager_base.h" // An AudioInputController controls an AudioInputStream and records data -// from this input stream. It has an important function that it executes -// audio operations like record, pause, stop, etc. on a separate thread. +// from this input stream. The two main methods are Record() and Close() and +// they are both executed on the audio thread which is injected by the two +// alternative factory methods, Create() or CreateLowLatency(). // -// All the public methods of AudioInputController are non-blocking except -// close, the actual operations are performed on the audio input controller -// thread. +// All public methods of AudioInputController are non-blocking. // // Here is a state diagram for the AudioInputController: // @@ -34,6 +35,38 @@ // // * Initial state // +// State sequences (assuming low-latency): +// +// [Creating Thread] [Audio Thread] +// +// User AudioInputController EventHandler +// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - +// CrateLowLatency() ==> DoCreate() +// AudioManager::MakeAudioInputStream() +// AudioInputStream::Open() +// .- - - - - - - - - - - - -> OnError() +// DoResetNoDataTimer (posted on creating tread) +// .-------------------------> OnCreated() +// kCreated +// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - +// Record() ==> DoRecord() +// AudioInputStream::Start() +// .-------------------------> OnRecording() +// kRecording +// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - +// Close() ==> DoClose() +// AudioInputStream::Stop() +// AudioInputStream::Close() +// SyncWriter::Close() +// Closure::Run() +// (closure-task) <----------------. +// kClosed +// +// The audio thread itself is owned by the AudioManager that the +// AudioInputController holds a reference to. When performing tasks on the +// audio thread, the controller must not add or release references to the +// AudioManager or itself (since it in turn holds a reference to the manager). +// namespace media { class MEDIA_EXPORT AudioInputController @@ -41,7 +74,7 @@ class MEDIA_EXPORT AudioInputController public AudioInputStream::AudioInputCallback { public: // An event handler that receives events from the AudioInputController. The - // following methods are called on the audio input controller thread. + // following methods are all called on the audio thread. class MEDIA_EXPORT EventHandler { public: virtual ~EventHandler() {} @@ -70,14 +103,13 @@ class MEDIA_EXPORT AudioInputController virtual void Close() = 0; }; - // AudioInputController::Create uses the currently registered Factory to - // create the AudioInputController. Factory is intended for testing. + // AudioInputController::Create() can use the currently registered Factory + // to create the AudioInputController. Factory is intended for testing only. class Factory { public: - virtual AudioInputController* Create( - AudioManager* audio_manager, - EventHandler* event_handler, - AudioParameters params) = 0; + virtual AudioInputController* Create(AudioManager* audio_manager, + EventHandler* event_handler, + AudioParameters params) = 0; protected: virtual ~Factory() {} }; @@ -85,49 +117,57 @@ class MEDIA_EXPORT AudioInputController virtual ~AudioInputController(); // Factory method for creating an AudioInputController. - // If successful, an audio input controller thread is created. The audio - // device will be created on the new thread and when that is done event - // handler will receive a OnCreated() call. + // The audio device will be created on the audio thread, and when that is + // done, the event handler will receive an OnCreated() call from that same + // thread. static scoped_refptr<AudioInputController> Create( AudioManager* audio_manager, EventHandler* event_handler, const AudioParameters& params); - // Factory method for creating a low latency audio stream. + // Sets the factory used by the static method Create(). AudioInputController + // does not take ownership of |factory|. A value of NULL results in an + // AudioInputController being created directly. + static void set_factory_for_testing(Factory* factory) { factory_ = factory; } + AudioInputStream* stream_for_testing() { return stream_; } + + // Factory method for creating an AudioInputController for low-latency mode. + // The audio device will be created on the audio thread, and when that is + // done, the event handler will receive an OnCreated() call from that same + // thread. static scoped_refptr<AudioInputController> CreateLowLatency( AudioManager* audio_manager, EventHandler* event_handler, const AudioParameters& params, const std::string& device_id, - // External synchronous reader for audio controller. + // External synchronous writer for audio controller. SyncWriter* sync_writer); - // Sets the factory used by the static method Create. AudioInputController - // does not take ownership of |factory|. A value of NULL results in an - // AudioInputController being created directly. - static void set_factory_for_testing(Factory* factory) { factory_ = factory; } - AudioInputStream* stream_for_testing() { return stream_; } - - // Starts recording in this audio input stream. + // Starts recording using the created audio input stream. + // This method is called on the audio thread. virtual void Record(); - // Closes the audio input stream and shutdown the audio input controller - // thread. This method returns only after all operations are completed. This - // input controller cannot be used after this method is called. - // + // Closes the audio input stream. The state is changed and the resources + // are freed on the audio thread. |closed_task| is executed after that. + // Callbacks (EventHandler and SyncWriter) must exist until |closed_task| + // is called. // It is safe to call this method more than once. Calls after the first one // will have no effect. - virtual void Close(); - - bool LowLatencyMode() const { return sync_writer_ != NULL; } + // This method is called on the audio thread. + virtual void Close(const base::Closure& closed_task); - /////////////////////////////////////////////////////////////////////////// - // AudioInputCallback methods. + // AudioInputCallback implementation. Threading details depends on the + // device-specific implementation. virtual void OnData(AudioInputStream* stream, const uint8* src, uint32 size, uint32 hardware_delay_bytes) OVERRIDE; virtual void OnClose(AudioInputStream* stream) OVERRIDE; virtual void OnError(AudioInputStream* stream, int code) OVERRIDE; + bool LowLatencyMode() const { return sync_writer_ != NULL; } + scoped_refptr<base::MessageLoopProxy> message_loop() const { + return message_loop_; + } + protected: // Internal state of the source. enum State { @@ -141,35 +181,52 @@ class MEDIA_EXPORT AudioInputController AudioInputController(AudioManager* audio_manager, EventHandler* handler, SyncWriter* sync_writer); - // The following methods are executed on the audio controller thread. + // Methods called on the audio thread (owned by the AudioManager). void DoCreate(const AudioParameters& params, const std::string& device_id); void DoRecord(); - void DoClose(); + void DoClose(const base::Closure& closed_task); void DoReportError(int code); + + // Methods which ensures that OnError() is triggered when data recording + // times out. Both are called on the creating thread. void DoReportNoDataError(); void DoResetNoDataTimer(); + // Helper method that stops, closes, and NULL:s |*stream_|. + // Signals event when done if the event is not NULL. + void DoStopCloseAndClearStream(base::WaitableEvent* done); + + // Gives access to the message loop of the creating thread. + scoped_refptr<base::MessageLoopProxy> creator_loop_; + + // Reference to the reference counted audio manager. scoped_refptr<AudioManager> audio_manager_; + + // The message loop of audio-manager thread that this object runs on. + scoped_refptr<base::MessageLoopProxy> message_loop_; + + // Contains the AudioInputController::EventHandler which receives state + // notifications from this class. EventHandler* handler_; + + // Pointer to the audio input stream object. AudioInputStream* stream_; - // |no_data_timer_| is used to call DoReportNoDataError when we stop - // receiving OnData calls without an OnClose call. This can occur when an - // audio input device is unplugged whilst recording on Windows. + // |no_data_timer_| is used to call DoReportNoDataError() when we stop + // receiving OnData() calls without an OnClose() call. This can occur + // when an audio input device is unplugged whilst recording on Windows. // See http://crbug.com/79936 for details. + // This member is only touched by the creating thread. base::DelayTimer<AudioInputController> no_data_timer_; - // |state_| is written on the audio input controller thread and is read on - // the hardware audio thread. These operations need to be locked. But lock - // is not required for reading on the audio input controller thread. + // |state_| is written on the audio thread and is read on the hardware audio + // thread. These operations need to be locked. But lock is not required for + // reading on the audio input controller thread. State state_; base::Lock lock_; - // The audio input controller thread that this object runs on. - base::Thread thread_; - - // SyncWriter is used only in low latency mode for synchronous writing. + // SyncWriter is used only in low-latency mode for synchronous writing. SyncWriter* sync_writer_; static Factory* factory_; diff --git a/media/audio/audio_input_controller_unittest.cc b/media/audio/audio_input_controller_unittest.cc index f238426..ee6649b 100644 --- a/media/audio/audio_input_controller_unittest.cc +++ b/media/audio/audio_input_controller_unittest.cc @@ -3,7 +3,10 @@ // found in the LICENSE file. #include "base/basictypes.h" +#include "base/bind.h" +#include "base/message_loop.h" #include "base/synchronization/waitable_event.h" +#include "base/test/test_timeouts.h" #include "media/audio/audio_input_controller.h" #include "testing/gmock/include/gmock/gmock.h" #include "testing/gtest/include/gtest/gtest.h" @@ -22,10 +25,22 @@ static const int kChannels = 2; static const ChannelLayout kChannelLayout = CHANNEL_LAYOUT_STEREO; static const int kSamplesPerPacket = kSampleRate / 10; -ACTION_P3(CheckCountAndSignalEvent, count, limit, event) { - if (++*count >= limit) { - event->Signal(); - } +ACTION_P(QuitMessageLoop, loop_or_proxy) { + loop_or_proxy->PostTask(FROM_HERE, MessageLoop::QuitClosure()); +} + +ACTION_P3(CheckCountAndPostQuitTask, count, limit, loop_or_proxy) {
+ if (++*count >= limit) {
+ loop_or_proxy->PostTask(FROM_HERE, MessageLoop::QuitClosure());
+ }
+} + +// Closes AudioOutputController synchronously. +static void CloseAudioController(AudioInputController* controller) { + base::WaitableEvent closed_event(true, false); + controller->Close(base::Bind(&base::WaitableEvent::Signal, + base::Unretained(&closed_event))); + closed_event.Wait(); } class MockAudioInputControllerEventHandler @@ -43,13 +58,26 @@ class MockAudioInputControllerEventHandler DISALLOW_COPY_AND_ASSIGN(MockAudioInputControllerEventHandler); }; +// Test fixture. +class AudioInputControllerTest : public testing::Test { + public: + AudioInputControllerTest() {} + virtual ~AudioInputControllerTest() {} + + protected: + MessageLoopForIO message_loop_; + + private: + DISALLOW_COPY_AND_ASSIGN(AudioInputControllerTest); +}; + // Test AudioInputController for create and close without recording audio. -TEST(AudioInputControllerTest, CreateAndClose) { +TEST_F(AudioInputControllerTest, CreateAndClose) { MockAudioInputControllerEventHandler event_handler; - base::WaitableEvent event(false, false); - // If OnCreated is called then signal the event. + + // OnCreated() will be posted once. EXPECT_CALL(event_handler, OnCreated(NotNull())) - .WillOnce(InvokeWithoutArgs(&event, &base::WaitableEvent::Signal)); + .WillOnce(QuitMessageLoop(message_loop_.message_loop_proxy())); scoped_refptr<AudioManager> audio_manager(AudioManager::Create()); AudioParameters params(AudioParameters::AUDIO_MOCK, kChannelLayout, @@ -58,114 +86,127 @@ TEST(AudioInputControllerTest, CreateAndClose) { AudioInputController::Create(audio_manager, &event_handler, params); ASSERT_TRUE(controller.get()); - // Wait for OnCreated() to be called. - event.Wait(); - - controller->Close(); + // Close the AudioInputController synchronously. + CloseAudioController(controller); } // Test a normal call sequence of create, record and close. -TEST(AudioInputControllerTest, RecordAndClose) { +TEST_F(AudioInputControllerTest, RecordAndClose) { MockAudioInputControllerEventHandler event_handler; - base::WaitableEvent event(false, false); int count = 0; - // If OnCreated is called then signal the event. + // OnCreated() will be called once. EXPECT_CALL(event_handler, OnCreated(NotNull())) - .WillOnce(InvokeWithoutArgs(&event, &base::WaitableEvent::Signal)); + .Times(Exactly(1)); // OnRecording() will be called only once. EXPECT_CALL(event_handler, OnRecording(NotNull())) .Times(Exactly(1)); - // If OnData is called enough then signal the event. + // OnData() shall be called ten times. EXPECT_CALL(event_handler, OnData(NotNull(), NotNull(), _)) .Times(AtLeast(10)) - .WillRepeatedly(CheckCountAndSignalEvent(&count, 10, &event)); + .WillRepeatedly(CheckCountAndPostQuitTask(&count, 10, + message_loop_.message_loop_proxy())); scoped_refptr<AudioManager> audio_manager(AudioManager::Create()); AudioParameters params(AudioParameters::AUDIO_MOCK, kChannelLayout, kSampleRate, kBitsPerSample, kSamplesPerPacket); + + // Creating the AudioInputController should render an OnCreated() call. scoped_refptr<AudioInputController> controller = AudioInputController::Create(audio_manager, &event_handler, params); ASSERT_TRUE(controller.get()); - // Wait for OnCreated() to be called. - event.Wait(); - event.Reset(); - - // Record and then wait for the event to be signaled. + // Start recording and trigger one OnRecording() call. controller->Record(); - event.Wait(); - controller->Close(); + // Record and wait until ten OnData() callbacks are received. + message_loop_.Run(); + + // Close the AudioInputController synchronously. + CloseAudioController(controller); } // Test that the AudioInputController reports an error when the input stream -// stops without an OnClose callback. -TEST(AudioInputControllerTest, RecordAndError) { +// stops without an OnClose() callback. This can happen when the underlying +// audio layer stops feeding data as a result of a removed microphone device. +TEST_F(AudioInputControllerTest, RecordAndError) { MockAudioInputControllerEventHandler event_handler; - base::WaitableEvent event(false, false); int count = 0; - // If OnCreated is called then signal the event. + // OnCreated() will be called once. EXPECT_CALL(event_handler, OnCreated(NotNull())) - .WillOnce(InvokeWithoutArgs(&event, &base::WaitableEvent::Signal)); + .Times(Exactly(1)); // OnRecording() will be called only once. EXPECT_CALL(event_handler, OnRecording(NotNull())) .Times(Exactly(1)); - // If OnData is called enough then signal the event. + // OnData() shall be called ten times. EXPECT_CALL(event_handler, OnData(NotNull(), NotNull(), _)) .Times(AtLeast(10)) - .WillRepeatedly(CheckCountAndSignalEvent(&count, 10, &event)); + .WillRepeatedly(CheckCountAndPostQuitTask(&count, 10, + message_loop_.message_loop_proxy())); - // OnError will be called after the data stream stops. + // OnError() will be called after the data stream stops while the + // controller is in a recording state. EXPECT_CALL(event_handler, OnError(NotNull(), 0)) - .WillOnce(InvokeWithoutArgs(&event, &base::WaitableEvent::Signal)); + .Times(Exactly(1)) + .WillOnce(QuitMessageLoop(message_loop_.message_loop_proxy())); scoped_refptr<AudioManager> audio_manager(AudioManager::Create()); AudioParameters params(AudioParameters::AUDIO_MOCK, kChannelLayout, kSampleRate, kBitsPerSample, kSamplesPerPacket); + + // Creating the AudioInputController should render an OnCreated() call. scoped_refptr<AudioInputController> controller = AudioInputController::Create(audio_manager, &event_handler, params); ASSERT_TRUE(controller.get()); - // Wait for OnCreated() to be called. - event.Wait(); - event.Reset(); - - // Record and then wait for the event to be signaled. + // Start recording and trigger one OnRecording() call. controller->Record(); - event.Wait(); - event.Reset(); - // Wait for the stream to be stopped. + // Record and wait until ten OnData() callbacks are received. + message_loop_.Run(); + + // Stop the stream and verify that OnError() is posted. AudioInputStream* stream = controller->stream_for_testing(); stream->Stop(); - event.Wait(); + message_loop_.Run(); - controller->Close(); + // Close the AudioInputController synchronously. + CloseAudioController(controller); } // Test that AudioInputController rejects insanely large packet sizes. -TEST(AudioInputControllerTest, SamplesPerPacketTooLarge) { +TEST_F(AudioInputControllerTest, SamplesPerPacketTooLarge) { // Create an audio device with a very large packet size. MockAudioInputControllerEventHandler event_handler; + // OnCreated() shall not be called in this test. + EXPECT_CALL(event_handler, OnCreated(NotNull())) + .Times(Exactly(0)); + scoped_refptr<AudioManager> audio_manager(AudioManager::Create()); AudioParameters params(AudioParameters::AUDIO_MOCK, kChannelLayout, kSampleRate, kBitsPerSample, kSamplesPerPacket * 1000); - scoped_refptr<AudioInputController> controller = AudioInputController::Create( - audio_manager, &event_handler, params); + scoped_refptr<AudioInputController> controller = + AudioInputController::Create(audio_manager, &event_handler, params); ASSERT_FALSE(controller); } // Test calling AudioInputController::Close multiple times. -TEST(AudioInputControllerTest, CloseTwice) { +TEST_F(AudioInputControllerTest, CloseTwice) { MockAudioInputControllerEventHandler event_handler; + + // OnRecording() will be called only once. EXPECT_CALL(event_handler, OnCreated(NotNull())); + + // OnRecording() will be called only once. + EXPECT_CALL(event_handler, OnRecording(NotNull())) + .Times(Exactly(1)); + scoped_refptr<AudioManager> audio_manager(AudioManager::Create()); AudioParameters params(AudioParameters::AUDIO_MOCK, kChannelLayout, kSampleRate, kBitsPerSample, kSamplesPerPacket); @@ -173,8 +214,18 @@ TEST(AudioInputControllerTest, CloseTwice) { AudioInputController::Create(audio_manager, &event_handler, params); ASSERT_TRUE(controller.get()); - controller->Close(); - controller->Close(); + controller->Record(); + + base::WaitableEvent closed_event_1(true, false); + controller->Close(base::Bind(&base::WaitableEvent::Signal, + base::Unretained(&closed_event_1))); + + base::WaitableEvent closed_event_2(true, false); + controller->Close(base::Bind(&base::WaitableEvent::Signal, + base::Unretained(&closed_event_2))); + + closed_event_1.Wait(); + closed_event_2.Wait(); } } // namespace media diff --git a/media/audio/audio_output_controller_unittest.cc b/media/audio/audio_output_controller_unittest.cc index 8327e5b..700ee33 100644 --- a/media/audio/audio_output_controller_unittest.cc +++ b/media/audio/audio_output_controller_unittest.cc @@ -64,15 +64,11 @@ ACTION_P(SignalEvent, event) { event->Signal(); } -// Helper functions used to close audio controller. -static void SignalClosedEvent(base::WaitableEvent* event) { - event->Signal(); -} - // Closes AudioOutputController synchronously. static void CloseAudioController(AudioOutputController* controller) { base::WaitableEvent closed_event(true, false); - controller->Close(base::Bind(&SignalClosedEvent, &closed_event)); + controller->Close(base::Bind(&base::WaitableEvent::Signal, + base::Unretained(&closed_event))); closed_event.Wait(); } @@ -421,10 +417,12 @@ TEST(AudioOutputControllerTest, CloseTwice) { event.Wait(); base::WaitableEvent closed_event_1(true, false); - controller->Close(base::Bind(&SignalClosedEvent, &closed_event_1)); + controller->Close(base::Bind(&base::WaitableEvent::Signal, + base::Unretained(&closed_event_1))); base::WaitableEvent closed_event_2(true, false); - controller->Close(base::Bind(&SignalClosedEvent, &closed_event_2)); + controller->Close(base::Bind(&base::WaitableEvent::Signal, + base::Unretained(&closed_event_2))); closed_event_1.Wait(); closed_event_2.Wait(); diff --git a/media/audio/test_audio_input_controller_factory.cc b/media/audio/test_audio_input_controller_factory.cc index 38543c5..441f55b 100644 --- a/media/audio/test_audio_input_controller_factory.cc +++ b/media/audio/test_audio_input_controller_factory.cc @@ -22,6 +22,10 @@ TestAudioInputController::~TestAudioInputController() { factory_->OnTestAudioInputControllerDestroyed(this); } +void TestAudioInputController::Close(const base::Closure& closed_task) { + audio_manager_->GetMessageLoop()->PostTask(FROM_HERE, closed_task); +} + TestAudioInputControllerFactory::TestAudioInputControllerFactory() : controller_(NULL) { } diff --git a/media/audio/test_audio_input_controller_factory.h b/media/audio/test_audio_input_controller_factory.h index 1720969..2915203 100644 --- a/media/audio/test_audio_input_controller_factory.h +++ b/media/audio/test_audio_input_controller_factory.h @@ -6,6 +6,7 @@ #define MEDIA_AUDIO_TEST_AUDIO_INPUT_CONTROLLER_FACTORY_H_ #pragma once +#include "base/bind.h" #include "media/audio/audio_input_controller.h" namespace media { @@ -56,7 +57,9 @@ class TestAudioInputController : public AudioInputController { // Overriden to do nothing. It is assumed the caller will notify the event // handler with recorded data and other events. virtual void Record() OVERRIDE {} - virtual void Close() OVERRIDE {} + + // Ensure that the closure is run on the audio-manager thread. + virtual void Close(const base::Closure& closed_task) OVERRIDE; private: // These are not owned by us and expected to be valid for this object's |