diff options
author | miu@chromium.org <miu@chromium.org@0039d316-1c4b-4281-b951-d872f2087c98> | 2013-01-11 09:31:47 +0000 |
---|---|---|
committer | miu@chromium.org <miu@chromium.org@0039d316-1c4b-4281-b951-d872f2087c98> | 2013-01-11 09:31:47 +0000 |
commit | f8d1737f16939011ce55ab2a7ce438bc7172b87c (patch) | |
tree | fdfd957972bd74cfbe5c1122bd54a0449bf75ebd /media | |
parent | 98efbd9c6e1dd6db2a4f73eca2ce79ac77d3d0db (diff) | |
download | chromium_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.cc | 68 | ||||
-rw-r--r-- | media/audio/audio_output_controller.h | 17 | ||||
-rw-r--r-- | media/audio/audio_output_controller_unittest.cc | 490 | ||||
-rw-r--r-- | media/audio/audio_source_diverter.h | 40 | ||||
-rw-r--r-- | media/media.gyp | 1 |
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', |