summaryrefslogtreecommitdiffstats
path: root/media
diff options
context:
space:
mode:
authorhenrika@chromium.org <henrika@chromium.org@0039d316-1c4b-4281-b951-d872f2087c98>2012-02-03 15:02:12 +0000
committerhenrika@chromium.org <henrika@chromium.org@0039d316-1c4b-4281-b951-d872f2087c98>2012-02-03 15:02:12 +0000
commit8bc74b44f18a1aad65c56d827b5f8f14c7e123b2 (patch)
treea681d9990b7cad9885af2f53c607aef045940101 /media
parentf0f67736354e2e0162811a2308d7f2853ec81eb9 (diff)
downloadchromium_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.cc129
-rw-r--r--media/audio/audio_input_controller.h149
-rw-r--r--media/audio/audio_input_controller_unittest.cc151
-rw-r--r--media/audio/audio_output_controller_unittest.cc14
-rw-r--r--media/audio/test_audio_input_controller_factory.cc4
-rw-r--r--media/audio/test_audio_input_controller_factory.h5
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