summaryrefslogtreecommitdiffstats
path: root/media
diff options
context:
space:
mode:
authormiu@chromium.org <miu@chromium.org@0039d316-1c4b-4281-b951-d872f2087c98>2013-01-11 09:31:47 +0000
committermiu@chromium.org <miu@chromium.org@0039d316-1c4b-4281-b951-d872f2087c98>2013-01-11 09:31:47 +0000
commitf8d1737f16939011ce55ab2a7ce438bc7172b87c (patch)
treefdfd957972bd74cfbe5c1122bd54a0449bf75ebd /media
parent98efbd9c6e1dd6db2a4f73eca2ce79ac77d3d0db (diff)
downloadchromium_src-f8d1737f16939011ce55ab2a7ce438bc7172b87c.zip
chromium_src-f8d1737f16939011ce55ab2a7ce438bc7172b87c.tar.gz
chromium_src-f8d1737f16939011ce55ab2a7ce438bc7172b87c.tar.bz2
Tab Audio Mirroring/Capture: Browser-side connect/disconnect functionality:
1. Added new AudioMirroringManager to dynamically match/route "divertable" audio streams with mirroring destinations. 2. Modified AudioOutputController to provide "divert audio data" functionality. 3. Modified AudioRendererHost to notify AudioMirroringManager of all audio streams. The intention is, in a later change, to introduce a "WebContentsAudioInputStream" which will implement the AudioMirroringManager::MirroringDestination interface introduced in this change. WCAIS will represent the lifetime of a tab audio mirroring session, calling AudioMirroringManager::Start/StopMirroring() as appropriate. Testing: 1. Rewrote most of unit testing for AudioOutputController, addressing bug 112500. Also added testing for the new Divert functionality. 2. Added extensive unit testing for the new Start/StopMirroring functionality in AudioMirroringManager. 3. Minor testing clean-ups/additions elsewhere. BUG=153392,112500 TEST=Run media_unittests and content_unittests. Review URL: https://chromiumcodereview.appspot.com/11413078 git-svn-id: svn://svn.chromium.org/chrome/trunk/src@176295 0039d316-1c4b-4281-b951-d872f2087c98
Diffstat (limited to 'media')
-rw-r--r--media/audio/audio_output_controller.cc68
-rw-r--r--media/audio/audio_output_controller.h17
-rw-r--r--media/audio/audio_output_controller_unittest.cc490
-rw-r--r--media/audio/audio_source_diverter.h40
-rw-r--r--media/media.gyp1
5 files changed, 404 insertions, 212 deletions
diff --git a/media/audio/audio_output_controller.cc b/media/audio/audio_output_controller.cc
index a9dd2c8..ffeb639 100644
--- a/media/audio/audio_output_controller.cc
+++ b/media/audio/audio_output_controller.cc
@@ -29,14 +29,15 @@ AudioOutputController::AudioOutputController(AudioManager* audio_manager,
const AudioParameters& params,
SyncReader* sync_reader)
: audio_manager_(audio_manager),
+ params_(params),
handler_(handler),
stream_(NULL),
+ diverting_to_stream_(NULL),
volume_(1.0),
state_(kEmpty),
sync_reader_(sync_reader),
message_loop_(audio_manager->GetMessageLoop()),
number_polling_attempts_left_(0),
- params_(params),
ALLOW_THIS_IN_INITIALIZER_LIST(weak_this_(this)) {
}
@@ -70,13 +71,10 @@ scoped_refptr<AudioOutputController> AudioOutputController::Create(
if (!params.IsValid() || !audio_manager)
return NULL;
- // Starts the audio controller thread.
scoped_refptr<AudioOutputController> controller(new AudioOutputController(
audio_manager, event_handler, params, sync_reader));
-
controller->message_loop_->PostTask(FROM_HERE, base::Bind(
&AudioOutputController::DoCreate, controller));
-
return controller;
}
@@ -121,7 +119,8 @@ void AudioOutputController::DoCreate() {
DoStopCloseAndClearStream(NULL); // Calls RemoveOutputDeviceChangeListener().
- stream_ = audio_manager_->MakeAudioOutputStreamProxy(params_);
+ stream_ = diverting_to_stream_ ? diverting_to_stream_ :
+ audio_manager_->MakeAudioOutputStreamProxy(params_);
if (!stream_) {
state_ = kError;
@@ -139,9 +138,10 @@ void AudioOutputController::DoCreate() {
return;
}
- // Everything started okay, so register for state change callbacks if we have
- // not already done so.
- audio_manager_->AddOutputDeviceChangeListener(this);
+ // Everything started okay, so re-register for state change callbacks if
+ // stream_ was created via AudioManager.
+ if (stream_ != diverting_to_stream_)
+ audio_manager_->AddOutputDeviceChangeListener(this);
// We have successfully opened the stream. Set the initial volume.
stream_->SetVolume(volume_);
@@ -349,10 +349,15 @@ void AudioOutputController::DoStopCloseAndClearStream(WaitableEvent* done) {
// Allow calling unconditionally and bail if we don't have a stream_ to close.
if (stream_) {
- audio_manager_->RemoveOutputDeviceChangeListener(this);
+ // De-register from state change callbacks if stream_ was created via
+ // AudioManager.
+ if (stream_ != diverting_to_stream_)
+ audio_manager_->RemoveOutputDeviceChangeListener(this);
stream_->Stop();
stream_->Close();
+ if (stream_ == diverting_to_stream_)
+ diverting_to_stream_ = NULL;
stream_ = NULL;
weak_this_.InvalidateWeakPtrs();
@@ -366,9 +371,6 @@ void AudioOutputController::DoStopCloseAndClearStream(WaitableEvent* done) {
void AudioOutputController::OnDeviceChange() {
DCHECK(message_loop_->BelongsToCurrentThread());
- // We should always have a stream by this point.
- CHECK(stream_);
-
// Recreate the stream (DoCreate() will first shut down an existing stream).
// Exit if we ran into an error.
const State original_state = state_;
@@ -393,4 +395,46 @@ void AudioOutputController::OnDeviceChange() {
}
}
+const AudioParameters& AudioOutputController::GetAudioParameters() {
+ return params_;
+}
+
+void AudioOutputController::StartDiverting(AudioOutputStream* to_stream) {
+ message_loop_->PostTask(
+ FROM_HERE,
+ base::Bind(&AudioOutputController::DoStartDiverting, this, to_stream));
+}
+
+void AudioOutputController::StopDiverting() {
+ message_loop_->PostTask(
+ FROM_HERE, base::Bind(&AudioOutputController::DoStopDiverting, this));
+}
+
+void AudioOutputController::DoStartDiverting(AudioOutputStream* to_stream) {
+ DCHECK(message_loop_->BelongsToCurrentThread());
+
+ if (state_ == kClosed)
+ return;
+
+ DCHECK(!diverting_to_stream_);
+ diverting_to_stream_ = to_stream;
+ // Note: OnDeviceChange() will engage the "re-create" process, which will
+ // detect and use the alternate AudioOutputStream rather than create a new one
+ // via AudioManager.
+ OnDeviceChange();
+}
+
+void AudioOutputController::DoStopDiverting() {
+ DCHECK(message_loop_->BelongsToCurrentThread());
+
+ if (state_ == kClosed)
+ return;
+
+ // Note: OnDeviceChange() will cause the existing stream (the consumer of the
+ // diverted audio data) to be closed, and diverting_to_stream_ will be set
+ // back to NULL.
+ OnDeviceChange();
+ DCHECK(!diverting_to_stream_);
+}
+
} // namespace media
diff --git a/media/audio/audio_output_controller.h b/media/audio/audio_output_controller.h
index 61718a3..21f8efe 100644
--- a/media/audio/audio_output_controller.h
+++ b/media/audio/audio_output_controller.h
@@ -12,6 +12,7 @@
#include "media/audio/audio_buffers_state.h"
#include "media/audio/audio_io.h"
#include "media/audio/audio_manager.h"
+#include "media/audio/audio_source_diverter.h"
#include "media/audio/simple_sources.h"
#include "media/base/media_export.h"
@@ -66,6 +67,7 @@ namespace media {
class MEDIA_EXPORT AudioOutputController
: public base::RefCountedThreadSafe<AudioOutputController>,
public AudioOutputStream::AudioSourceCallback,
+ public AudioSourceDiverter,
NON_EXPORTED_BASE(public AudioManager::AudioDeviceListener) {
public:
// An event handler that receives events from the AudioOutputController. The
@@ -154,8 +156,13 @@ class MEDIA_EXPORT AudioOutputController
// to being called.
virtual void OnDeviceChange() OVERRIDE;
+ // AudioSourceDiverter implementation.
+ virtual const AudioParameters& GetAudioParameters() OVERRIDE;
+ virtual void StartDiverting(AudioOutputStream* to_stream) OVERRIDE;
+ virtual void StopDiverting() OVERRIDE;
+
protected:
- // Internal state of the source.
+ // Internal state of the source.
enum State {
kEmpty,
kCreated,
@@ -188,6 +195,8 @@ class MEDIA_EXPORT AudioOutputController
void DoClose();
void DoSetVolume(double volume);
void DoReportError(int code);
+ void DoStartDiverting(AudioOutputStream* to_stream);
+ void DoStopDiverting();
// Helper method that starts physical stream.
void StartStream();
@@ -197,6 +206,7 @@ class MEDIA_EXPORT AudioOutputController
void DoStopCloseAndClearStream(base::WaitableEvent *done);
AudioManager* const audio_manager_;
+ const AudioParameters params_;
// |handler_| may be called only if |state_| is not kClosed.
EventHandler* handler_;
@@ -205,6 +215,9 @@ class MEDIA_EXPORT AudioOutputController
// changed. See comment for weak_this_.
AudioOutputStream* stream_;
+ // When non-NULL, audio is being diverted to this stream.
+ AudioOutputStream* diverting_to_stream_;
+
// The current volume of the audio stream.
double volume_;
@@ -227,8 +240,6 @@ class MEDIA_EXPORT AudioOutputController
// Number of times left.
int number_polling_attempts_left_;
- AudioParameters params_;
-
// Used to auto-cancel the delayed tasks that are created to poll for data
// (when starting-up a stream).
base::WeakPtrFactory<AudioOutputController> weak_this_;
diff --git a/media/audio/audio_output_controller_unittest.cc b/media/audio/audio_output_controller_unittest.cc
index fe29ce5..ff37b5a 100644
--- a/media/audio/audio_output_controller_unittest.cc
+++ b/media/audio/audio_output_controller_unittest.cc
@@ -2,23 +2,24 @@
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
+#include "base/basictypes.h"
#include "base/bind.h"
#include "base/environment.h"
-#include "base/basictypes.h"
#include "base/logging.h"
+#include "base/memory/ref_counted.h"
+#include "base/memory/scoped_ptr.h"
#include "base/message_loop.h"
#include "base/synchronization/waitable_event.h"
#include "media/audio/audio_output_controller.h"
+#include "media/audio/audio_parameters.h"
+#include "media/base/audio_bus.h"
#include "testing/gmock/include/gmock/gmock.h"
#include "testing/gtest/include/gtest/gtest.h"
-// TODO(vrk): These tests need to be rewritten! (crbug.com/112500)
-
using ::testing::_;
using ::testing::AtLeast;
using ::testing::DoAll;
-using ::testing::Exactly;
-using ::testing::InvokeWithoutArgs;
+using ::testing::Invoke;
using ::testing::NotNull;
using ::testing::Return;
@@ -27,9 +28,10 @@ namespace media {
static const int kSampleRate = AudioParameters::kAudioCDSampleRate;
static const int kBitsPerSample = 16;
static const ChannelLayout kChannelLayout = CHANNEL_LAYOUT_STEREO;
-static const int kSamplesPerPacket = kSampleRate / 10;
+static const int kSamplesPerPacket = kSampleRate / 100;
static const int kHardwareBufferSize = kSamplesPerPacket *
ChannelLayoutToChannelCount(kChannelLayout) * kBitsPerSample / 8;
+static const double kTestVolume = 0.25;
class MockAudioOutputControllerEventHandler
: public AudioOutputController::EventHandler {
@@ -60,227 +62,321 @@ class MockAudioOutputControllerSyncReader
DISALLOW_COPY_AND_ASSIGN(MockAudioOutputControllerSyncReader);
};
+class MockAudioOutputStream : public AudioOutputStream {
+ public:
+ MOCK_METHOD0(Open, bool());
+ MOCK_METHOD1(Start, void(AudioSourceCallback* callback));
+ MOCK_METHOD0(Stop, void());
+ MOCK_METHOD1(SetVolume, void(double volume));
+ MOCK_METHOD1(GetVolume, void(double* volume));
+ MOCK_METHOD0(Close, void());
+
+ // Set/get the callback passed to Start().
+ AudioSourceCallback* callback() const { return callback_; }
+ void SetCallback(AudioSourceCallback* asc) { callback_ = asc; }
+
+ private:
+ AudioSourceCallback* callback_;
+};
+
ACTION_P(SignalEvent, event) {
event->Signal();
}
-// Custom action to clear a memory buffer.
-ACTION(ClearBuffer) {
+static const float kBufferNonZeroData = 1.0f;
+ACTION(PopulateBuffer) {
arg1->Zero();
-}
-
-// Closes AudioOutputController synchronously.
-static void CloseAudioController(AudioOutputController* controller) {
- controller->Close(MessageLoop::QuitClosure());
- MessageLoop::current()->Run();
+ // Note: To confirm the buffer will be populated in these tests, it's
+ // sufficient that only the first float in channel 0 is set to the value.
+ arg1->channel(0)[0] = kBufferNonZeroData;
}
class AudioOutputControllerTest : public testing::Test {
public:
- AudioOutputControllerTest() {}
- virtual ~AudioOutputControllerTest() {}
+ AudioOutputControllerTest()
+ : audio_manager_(AudioManager::Create()),
+ create_event_(false, false),
+ play_event_(false, false),
+ read_event_(false, false),
+ pause_event_(false, false) {
+ }
+
+ virtual ~AudioOutputControllerTest() {
+ }
protected:
- MessageLoopForIO message_loop_;
+ void Create(int samples_per_packet) {
+ EXPECT_FALSE(create_event_.IsSignaled());
+ EXPECT_FALSE(play_event_.IsSignaled());
+ EXPECT_FALSE(read_event_.IsSignaled());
+ EXPECT_FALSE(pause_event_.IsSignaled());
+
+ params_ = AudioParameters(
+ AudioParameters::AUDIO_FAKE, kChannelLayout,
+ kSampleRate, kBitsPerSample, samples_per_packet);
+
+ if (params_.IsValid()) {
+ EXPECT_CALL(mock_event_handler_, OnCreated(NotNull()))
+ .WillOnce(SignalEvent(&create_event_));
+ }
+
+ controller_ = AudioOutputController::Create(
+ audio_manager_.get(), &mock_event_handler_, params_,
+ &mock_sync_reader_);
+ if (controller_)
+ controller_->SetVolume(kTestVolume);
+
+ EXPECT_EQ(params_.IsValid(), controller_ != NULL);
+ }
+
+ void Play() {
+ // Expect the event handler to receive one OnPlaying() call.
+ EXPECT_CALL(mock_event_handler_, OnPlaying(NotNull()))
+ .WillOnce(SignalEvent(&play_event_));
+
+ // During playback, the mock pretends to provide audio data rendered and
+ // sent from the render process.
+ EXPECT_CALL(mock_sync_reader_, UpdatePendingBytes(_))
+ .Times(AtLeast(2));
+ EXPECT_CALL(mock_sync_reader_, Read(_, _))
+ .WillRepeatedly(DoAll(PopulateBuffer(),
+ SignalEvent(&read_event_),
+ Return(params_.frames_per_buffer())));
+ EXPECT_CALL(mock_sync_reader_, DataReady())
+ .WillRepeatedly(Return(true));
+
+ controller_->Play();
+ }
+
+ void Pause() {
+ // Expect the event handler to receive one OnPaused() call.
+ EXPECT_CALL(mock_event_handler_, OnPaused(NotNull()))
+ .WillOnce(SignalEvent(&pause_event_));
+
+ controller_->Pause();
+ }
+
+ void ChangeDevice() {
+ // Expect the event handler to receive one OnPaying() call and no OnPaused()
+ // call.
+ EXPECT_CALL(mock_event_handler_, OnPlaying(NotNull()))
+ .WillOnce(SignalEvent(&play_event_));
+ EXPECT_CALL(mock_event_handler_, OnPaused(NotNull()))
+ .Times(0);
+
+ // Simulate a device change event to AudioOutputController from the
+ // AudioManager.
+ audio_manager_->GetMessageLoop()->PostTask(
+ FROM_HERE,
+ base::Bind(&AudioOutputController::OnDeviceChange, controller_));
+ }
+
+ void Divert(bool was_playing, bool will_be_playing) {
+ if (was_playing) {
+ // Expect the handler to receive one OnPlaying() call as a result of the
+ // stream switching.
+ EXPECT_CALL(mock_event_handler_, OnPlaying(NotNull()))
+ .WillOnce(SignalEvent(&play_event_));
+ }
+
+ EXPECT_CALL(mock_stream_, Open())
+ .WillOnce(Return(true));
+ EXPECT_CALL(mock_stream_, SetVolume(kTestVolume));
+ if (will_be_playing) {
+ EXPECT_CALL(mock_stream_, Start(NotNull()))
+ .Times(AtLeast(1))
+ .WillRepeatedly(
+ Invoke(&mock_stream_, &MockAudioOutputStream::SetCallback));
+ }
+ // Always expect a Stop() call--even without a Start() call--since
+ // AudioOutputController likes to play it safe and Stop() before any
+ // Close().
+ EXPECT_CALL(mock_stream_, Stop())
+ .Times(AtLeast(1));
+
+ controller_->StartDiverting(&mock_stream_);
+ }
+
+ void ReadDivertedAudioData() {
+ scoped_ptr<AudioBus> dest = AudioBus::Create(params_);
+ ASSERT_TRUE(!!mock_stream_.callback());
+ const int frames_read =
+ mock_stream_.callback()->OnMoreData(dest.get(), AudioBuffersState());
+ EXPECT_LT(0, frames_read);
+ EXPECT_EQ(kBufferNonZeroData, dest->channel(0)[0]);
+ }
+
+ void Revert(bool was_playing) {
+ if (was_playing) {
+ // Expect the handler to receive one OnPlaying() call as a result of the
+ // stream switching back.
+ EXPECT_CALL(mock_event_handler_, OnPlaying(NotNull()))
+ .WillOnce(SignalEvent(&play_event_));
+ }
+
+ EXPECT_CALL(mock_stream_, Close());
+
+ controller_->StopDiverting();
+ }
+
+ void Close() {
+ EXPECT_CALL(mock_sync_reader_, Close());
+
+ controller_->Close(MessageLoop::QuitClosure());
+ MessageLoop::current()->Run();
+ }
+
+ // These help make test sequences more readable.
+ void DivertNeverPlaying() { Divert(false, false); }
+ void DivertWillEventuallyBePlaying() { Divert(false, true); }
+ void DivertWhilePlaying() { Divert(true, true); }
+ void RevertWasNotPlaying() { Revert(false); }
+ void RevertWhilePlaying() { Revert(true); }
+
+ // These synchronize the main thread with key events taking place on other
+ // threads.
+ void WaitForCreate() { create_event_.Wait(); }
+ void WaitForPlay() { play_event_.Wait(); }
+ void WaitForReads() {
+ // Note: Arbitrarily chosen, but more iterations causes tests to take
+ // significantly more time.
+ static const int kNumIterations = 3;
+ for (int i = 0; i < kNumIterations; ++i) {
+ read_event_.Wait();
+ }
+ }
+ void WaitForPause() { pause_event_.Wait(); }
private:
+ MessageLoopForIO message_loop_;
+ scoped_ptr<AudioManager> audio_manager_;
+ MockAudioOutputControllerEventHandler mock_event_handler_;
+ MockAudioOutputControllerSyncReader mock_sync_reader_;
+ MockAudioOutputStream mock_stream_;
+ base::WaitableEvent create_event_;
+ base::WaitableEvent play_event_;
+ base::WaitableEvent read_event_;
+ base::WaitableEvent pause_event_;
+ AudioParameters params_;
+ scoped_refptr<AudioOutputController> controller_;
+
DISALLOW_COPY_AND_ASSIGN(AudioOutputControllerTest);
};
TEST_F(AudioOutputControllerTest, CreateAndClose) {
- scoped_ptr<AudioManager> audio_manager(AudioManager::Create());
- if (!audio_manager->HasAudioOutputDevices())
- return;
+ Create(kSamplesPerPacket);
+ Close();
+}
- MockAudioOutputControllerEventHandler event_handler;
+TEST_F(AudioOutputControllerTest, HardwareBufferTooLarge) {
+ Create(kSamplesPerPacket * 1000);
+}
- EXPECT_CALL(event_handler, OnCreated(NotNull()))
- .Times(1);
+TEST_F(AudioOutputControllerTest, PlayAndClose) {
+ Create(kSamplesPerPacket);
+ WaitForCreate();
+ Play();
+ WaitForPlay();
+ WaitForReads();
+ Close();
+}
- MockAudioOutputControllerSyncReader sync_reader;
- EXPECT_CALL(sync_reader, Close());
+TEST_F(AudioOutputControllerTest, PlayPauseClose) {
+ Create(kSamplesPerPacket);
+ WaitForCreate();
+ Play();
+ WaitForPlay();
+ WaitForReads();
+ Pause();
+ WaitForPause();
+ Close();
+}
- AudioParameters params(AudioParameters::AUDIO_PCM_LINEAR, kChannelLayout,
- kSampleRate, kBitsPerSample, kSamplesPerPacket);
- scoped_refptr<AudioOutputController> controller =
- AudioOutputController::Create(
- audio_manager.get(), &event_handler, params, &sync_reader);
- ASSERT_TRUE(controller.get());
+TEST_F(AudioOutputControllerTest, PlayPausePlayClose) {
+ Create(kSamplesPerPacket);
+ WaitForCreate();
+ Play();
+ WaitForPlay();
+ WaitForReads();
+ Pause();
+ WaitForPause();
+ Play();
+ WaitForPlay();
+ Close();
+}
- // Close the controller immediately.
- CloseAudioController(controller);
+TEST_F(AudioOutputControllerTest, PlayDeviceChangeClose) {
+ Create(kSamplesPerPacket);
+ WaitForCreate();
+ Play();
+ WaitForPlay();
+ WaitForReads();
+ ChangeDevice();
+ WaitForPlay();
+ WaitForReads();
+ Close();
}
-TEST_F(AudioOutputControllerTest, PlayPauseClose) {
- scoped_ptr<AudioManager> audio_manager(AudioManager::Create());
- if (!audio_manager->HasAudioOutputDevices())
- return;
-
- MockAudioOutputControllerEventHandler event_handler;
- base::WaitableEvent event(false, false);
- base::WaitableEvent pause_event(false, false);
-
- // If OnCreated is called then signal the event.
- EXPECT_CALL(event_handler, OnCreated(NotNull()))
- .WillOnce(InvokeWithoutArgs(&event, &base::WaitableEvent::Signal));
-
- // OnPlaying() will be called only once.
- EXPECT_CALL(event_handler, OnPlaying(NotNull()));
-
- MockAudioOutputControllerSyncReader sync_reader;
- EXPECT_CALL(sync_reader, UpdatePendingBytes(_))
- .Times(AtLeast(2));
- EXPECT_CALL(sync_reader, Read(_, _))
- .WillRepeatedly(DoAll(ClearBuffer(), SignalEvent(&event),
- Return(4)));
- EXPECT_CALL(sync_reader, DataReady())
- .WillRepeatedly(Return(true));
- EXPECT_CALL(event_handler, OnPaused(NotNull()))
- .WillOnce(InvokeWithoutArgs(&pause_event, &base::WaitableEvent::Signal));
- EXPECT_CALL(sync_reader, Close());
-
- AudioParameters params(AudioParameters::AUDIO_PCM_LINEAR, kChannelLayout,
- kSampleRate, kBitsPerSample, kSamplesPerPacket);
- scoped_refptr<AudioOutputController> controller =
- AudioOutputController::Create(
- audio_manager.get(), &event_handler, params, &sync_reader);
- ASSERT_TRUE(controller.get());
-
- // Wait for OnCreated() to be called.
- event.Wait();
-
- ASSERT_FALSE(pause_event.IsSignaled());
- controller->Play();
- controller->Pause();
- pause_event.Wait();
-
- // Now stop the controller.
- CloseAudioController(controller);
+TEST_F(AudioOutputControllerTest, PlayDivertRevertClose) {
+ Create(kSamplesPerPacket);
+ WaitForCreate();
+ Play();
+ WaitForPlay();
+ WaitForReads();
+ DivertWhilePlaying();
+ WaitForPlay();
+ ReadDivertedAudioData();
+ RevertWhilePlaying();
+ WaitForPlay();
+ WaitForReads();
+ Close();
}
-TEST_F(AudioOutputControllerTest, HardwareBufferTooLarge) {
- scoped_ptr<AudioManager> audio_manager(AudioManager::Create());
- if (!audio_manager->HasAudioOutputDevices())
- return;
-
- // Create an audio device with a very large hardware buffer size.
- MockAudioOutputControllerEventHandler event_handler;
-
- MockAudioOutputControllerSyncReader sync_reader;
- AudioParameters params(AudioParameters::AUDIO_PCM_LINEAR, kChannelLayout,
- kSampleRate, kBitsPerSample,
- kSamplesPerPacket * 1000);
- scoped_refptr<AudioOutputController> controller =
- AudioOutputController::Create(
- audio_manager.get(), &event_handler, params, &sync_reader);
-
- // Use assert because we don't stop the device and assume we can't
- // create one.
- ASSERT_FALSE(controller);
+TEST_F(AudioOutputControllerTest, PlayDivertRevertDivertRevertClose) {
+ Create(kSamplesPerPacket);
+ WaitForCreate();
+ Play();
+ WaitForPlay();
+ WaitForReads();
+ DivertWhilePlaying();
+ WaitForPlay();
+ ReadDivertedAudioData();
+ RevertWhilePlaying();
+ WaitForPlay();
+ WaitForReads();
+ DivertWhilePlaying();
+ WaitForPlay();
+ ReadDivertedAudioData();
+ RevertWhilePlaying();
+ WaitForPlay();
+ WaitForReads();
+ Close();
}
-TEST_F(AudioOutputControllerTest, PlayPausePlayClose) {
- scoped_ptr<AudioManager> audio_manager(AudioManager::Create());
- if (!audio_manager->HasAudioOutputDevices())
- return;
-
- MockAudioOutputControllerEventHandler event_handler;
- base::WaitableEvent event(false, false);
- EXPECT_CALL(event_handler, OnCreated(NotNull()))
- .WillOnce(InvokeWithoutArgs(&event, &base::WaitableEvent::Signal));
-
- // OnPlaying() will be called only once.
- base::WaitableEvent play_event(false, false);
- EXPECT_CALL(event_handler, OnPlaying(NotNull()))
- .WillOnce(InvokeWithoutArgs(&play_event, &base::WaitableEvent::Signal));
-
- // OnPaused() should never be called since the pause during kStarting is
- // dropped when the second play comes in.
- EXPECT_CALL(event_handler, OnPaused(NotNull()))
- .Times(0);
-
- MockAudioOutputControllerSyncReader sync_reader;
- EXPECT_CALL(sync_reader, UpdatePendingBytes(_))
- .Times(AtLeast(1));
- EXPECT_CALL(sync_reader, Read(_, _))
- .WillRepeatedly(DoAll(ClearBuffer(), SignalEvent(&event), Return(4)));
- EXPECT_CALL(sync_reader, DataReady())
- .WillRepeatedly(Return(true));
- EXPECT_CALL(sync_reader, Close());
-
- AudioParameters params(AudioParameters::AUDIO_PCM_LINEAR, kChannelLayout,
- kSampleRate, kBitsPerSample, kSamplesPerPacket);
- scoped_refptr<AudioOutputController> controller =
- AudioOutputController::Create(
- audio_manager.get(), &event_handler, params, &sync_reader);
- ASSERT_TRUE(controller.get());
-
- // Wait for OnCreated() to be called.
- event.Wait();
-
- ASSERT_FALSE(play_event.IsSignaled());
- controller->Play();
- controller->Pause();
- controller->Play();
- play_event.Wait();
-
- // Now stop the controller.
- CloseAudioController(controller);
+TEST_F(AudioOutputControllerTest, DivertPlayPausePlayRevertClose) {
+ Create(kSamplesPerPacket);
+ WaitForCreate();
+ DivertWillEventuallyBePlaying();
+ Play();
+ WaitForPlay();
+ ReadDivertedAudioData();
+ Pause();
+ WaitForPause();
+ Play();
+ WaitForPlay();
+ ReadDivertedAudioData();
+ RevertWhilePlaying();
+ WaitForPlay();
+ WaitForReads();
+ Close();
}
-// Ensure state change events are handled.
-TEST_F(AudioOutputControllerTest, PlayStateChangeClose) {
- scoped_ptr<AudioManager> audio_manager(AudioManager::Create());
- if (!audio_manager->HasAudioOutputDevices())
- return;
-
- MockAudioOutputControllerEventHandler event_handler;
- base::WaitableEvent event(false, false);
- EXPECT_CALL(event_handler, OnCreated(NotNull()))
- .WillOnce(InvokeWithoutArgs(&event, &base::WaitableEvent::Signal));
-
- // OnPlaying() will be called once normally and once after being recreated.
- base::WaitableEvent play_event(false, false);
- EXPECT_CALL(event_handler, OnPlaying(NotNull()))
- .Times(2)
- .WillRepeatedly(InvokeWithoutArgs(
- &play_event, &base::WaitableEvent::Signal));
-
- // OnPaused() should not be called during the state change event.
- EXPECT_CALL(event_handler, OnPaused(NotNull()))
- .Times(0);
-
- MockAudioOutputControllerSyncReader sync_reader;
- EXPECT_CALL(sync_reader, UpdatePendingBytes(_))
- .Times(AtLeast(1));
- EXPECT_CALL(sync_reader, Read(_, _))
- .WillRepeatedly(DoAll(ClearBuffer(), SignalEvent(&event), Return(4)));
- EXPECT_CALL(sync_reader, DataReady())
- .WillRepeatedly(Return(true));
- EXPECT_CALL(sync_reader, Close());
-
- AudioParameters params(AudioParameters::AUDIO_PCM_LINEAR, kChannelLayout,
- kSampleRate, kBitsPerSample, kSamplesPerPacket);
- scoped_refptr<AudioOutputController> controller =
- AudioOutputController::Create(
- audio_manager.get(), &event_handler, params, &sync_reader);
- ASSERT_TRUE(controller.get());
-
- // Wait for OnCreated() to be called.
- event.Wait();
-
- ASSERT_FALSE(play_event.IsSignaled());
- controller->Play();
- play_event.Wait();
-
- // Force a state change and wait for the stream to come back to playing state.
- play_event.Reset();
- audio_manager->GetMessageLoop()->PostTask(FROM_HERE,
- base::Bind(&AudioOutputController::OnDeviceChange, controller));
- play_event.Wait();
-
- // Now stop the controller.
- CloseAudioController(controller);
+TEST_F(AudioOutputControllerTest, DivertRevertClose) {
+ Create(kSamplesPerPacket);
+ WaitForCreate();
+ DivertNeverPlaying();
+ RevertWasNotPlaying();
+ Close();
}
} // namespace media
diff --git a/media/audio/audio_source_diverter.h b/media/audio/audio_source_diverter.h
new file mode 100644
index 0000000..787ddec
--- /dev/null
+++ b/media/audio/audio_source_diverter.h
@@ -0,0 +1,40 @@
+// Copyright (c) 2013 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.
+
+#ifndef MEDIA_AUDIO_AUDIO_SOURCE_DIVERTER_H_
+#define MEDIA_AUDIO_AUDIO_SOURCE_DIVERTER_H_
+
+#include "media/base/media_export.h"
+
+// Audio sources may optionally implement AudioSourceDiverter to temporarily
+// divert audio data to an alternate AudioOutputStream. This allows the audio
+// data to be plumbed to an alternate consumer; for example, a loopback
+// mechanism for audio mirroring.
+
+namespace media {
+
+class AudioOutputStream;
+class AudioParameters;
+
+class MEDIA_EXPORT AudioSourceDiverter {
+public:
+ // Returns the audio parameters of the divertable audio data.
+ virtual const AudioParameters& GetAudioParameters() = 0;
+
+ // Start providing audio data to the given |to_stream|, which is in an
+ // unopened state. |to_stream| remains under the control of the
+ // AudioSourceDiverter.
+ virtual void StartDiverting(AudioOutputStream* to_stream) = 0;
+
+ // Stops diverting audio data to the stream. The AudioSourceDiverter is
+ // responsible for making sure the stream is closed, perhaps asynchronously.
+ virtual void StopDiverting() = 0;
+
+protected:
+ virtual ~AudioSourceDiverter() {}
+};
+
+} // namespace media
+
+#endif // MEDIA_AUDIO_AUDIO_SOURCE_DIVERTER_H_
diff --git a/media/media.gyp b/media/media.gyp
index 05b35cc..feb1cc5 100644
--- a/media/media.gyp
+++ b/media/media.gyp
@@ -83,6 +83,7 @@
'audio/audio_output_proxy.h',
'audio/audio_output_resampler.cc',
'audio/audio_output_resampler.h',
+ 'audio/audio_source_diverter.h',
'audio/audio_util.cc',
'audio/audio_util.h',
'audio/cross_process_notification.cc',