diff options
Diffstat (limited to 'media/audio')
-rw-r--r-- | media/audio/audio_manager_base.cc | 18 | ||||
-rw-r--r-- | media/audio/audio_manager_base.h | 1 | ||||
-rw-r--r-- | media/audio/audio_output_dispatcher.cc | 154 | ||||
-rw-r--r-- | media/audio/audio_output_dispatcher.h | 105 | ||||
-rw-r--r-- | media/audio/audio_output_dispatcher_impl.cc | 200 | ||||
-rw-r--r-- | media/audio/audio_output_dispatcher_impl.h | 102 | ||||
-rw-r--r-- | media/audio/audio_output_mixer.cc | 251 | ||||
-rw-r--r-- | media/audio/audio_output_mixer.h | 92 | ||||
-rw-r--r-- | media/audio/audio_output_proxy.cc | 23 | ||||
-rw-r--r-- | media/audio/audio_output_proxy.h | 4 | ||||
-rw-r--r-- | media/audio/audio_output_proxy_unittest.cc | 372 |
11 files changed, 346 insertions, 976 deletions
diff --git a/media/audio/audio_manager_base.cc b/media/audio/audio_manager_base.cc index a0c1c4c..45bf0fc 100644 --- a/media/audio/audio_manager_base.cc +++ b/media/audio/audio_manager_base.cc @@ -5,15 +5,12 @@ #include "media/audio/audio_manager_base.h" #include "base/bind.h" -#include "base/command_line.h" #include "base/message_loop_proxy.h" #include "base/threading/thread.h" -#include "media/audio/audio_output_dispatcher_impl.h" -#include "media/audio/audio_output_mixer.h" +#include "media/audio/audio_output_dispatcher.h" #include "media/audio/audio_output_proxy.h" #include "media/audio/fake_audio_input_stream.h" #include "media/audio/fake_audio_output_stream.h" -#include "media/base/media_switches.h" namespace media { @@ -139,16 +136,9 @@ AudioOutputStream* AudioManagerBase::MakeAudioOutputStreamProxy( scoped_refptr<AudioOutputDispatcher>& dispatcher = output_dispatchers_[params]; - if (!dispatcher) { - base::TimeDelta close_delay = - base::TimeDelta::FromSeconds(kStreamCloseDelaySeconds); - const CommandLine* cmd_line = CommandLine::ForCurrentProcess(); - if (cmd_line->HasSwitch(switches::kEnableAudioMixer)) { - dispatcher = new AudioOutputMixer(this, params, close_delay); - } else { - dispatcher = new AudioOutputDispatcherImpl(this, params, close_delay); - } - } + if (!dispatcher) + dispatcher = new AudioOutputDispatcher( + this, params, base::TimeDelta::FromSeconds(kStreamCloseDelaySeconds)); return new AudioOutputProxy(dispatcher); } diff --git a/media/audio/audio_manager_base.h b/media/audio/audio_manager_base.h index 498b44c..acb85b6 100644 --- a/media/audio/audio_manager_base.h +++ b/media/audio/audio_manager_base.h @@ -6,7 +6,6 @@ #define MEDIA_AUDIO_AUDIO_MANAGER_BASE_H_ #include <map> -#include <string> #include "base/atomic_ref_count.h" #include "base/compiler_specific.h" diff --git a/media/audio/audio_output_dispatcher.cc b/media/audio/audio_output_dispatcher.cc index bfd3fb8..998fc1b 100644 --- a/media/audio/audio_output_dispatcher.cc +++ b/media/audio/audio_output_dispatcher.cc @@ -4,16 +4,29 @@ #include "media/audio/audio_output_dispatcher.h" +#include "base/bind.h" +#include "base/compiler_specific.h" #include "base/message_loop.h" +#include "base/time.h" +#include "media/audio/audio_io.h" namespace media { AudioOutputDispatcher::AudioOutputDispatcher( - AudioManager* audio_manager, - const AudioParameters& params) + AudioManager* audio_manager, const AudioParameters& params, + base::TimeDelta close_delay) : audio_manager_(audio_manager), message_loop_(MessageLoop::current()), - params_(params) { + params_(params), + pause_delay_(base::TimeDelta::FromMilliseconds( + 2 * params.frames_per_buffer() * + base::Time::kMillisecondsPerSecond / params.sample_rate())), + paused_proxies_(0), + ALLOW_THIS_IN_INITIALIZER_LIST(weak_this_(this)), + close_timer_(FROM_HERE, + close_delay, + weak_this_.GetWeakPtr(), + &AudioOutputDispatcher::ClosePendingStreams) { // We expect to be instantiated on the audio thread. Otherwise the // message_loop_ member will point to the wrong message loop! DCHECK(audio_manager->GetMessageLoop()->BelongsToCurrentThread()); @@ -23,4 +36,139 @@ AudioOutputDispatcher::~AudioOutputDispatcher() { DCHECK_EQ(MessageLoop::current(), message_loop_); } +bool AudioOutputDispatcher::StreamOpened() { + DCHECK_EQ(MessageLoop::current(), message_loop_); + paused_proxies_++; + + // Ensure that there is at least one open stream. + if (idle_streams_.empty() && !CreateAndOpenStream()) { + return false; + } + + close_timer_.Reset(); + + return true; +} + +AudioOutputStream* AudioOutputDispatcher::StreamStarted() { + DCHECK_EQ(MessageLoop::current(), message_loop_); + + if (idle_streams_.empty() && !CreateAndOpenStream()) { + return NULL; + } + + AudioOutputStream* stream = idle_streams_.back(); + idle_streams_.pop_back(); + + DCHECK_GT(paused_proxies_, 0u); + paused_proxies_--; + + close_timer_.Reset(); + + // Schedule task to allocate streams for other proxies if we need to. + message_loop_->PostTask(FROM_HERE, base::Bind( + &AudioOutputDispatcher::OpenTask, weak_this_.GetWeakPtr())); + + return stream; +} + +void AudioOutputDispatcher::StreamStopped(AudioOutputStream* stream) { + DCHECK_EQ(MessageLoop::current(), message_loop_); + + paused_proxies_++; + + pausing_streams_.push_front(stream); + + // Don't recycle stream until two buffers worth of time has elapsed. + message_loop_->PostDelayedTask( + FROM_HERE, + base::Bind(&AudioOutputDispatcher::StopStreamTask, + weak_this_.GetWeakPtr()), + pause_delay_); +} + +void AudioOutputDispatcher::StopStreamTask() { + DCHECK_EQ(MessageLoop::current(), message_loop_); + + if (pausing_streams_.empty()) + return; + + AudioOutputStream* stream = pausing_streams_.back(); + pausing_streams_.pop_back(); + idle_streams_.push_back(stream); + close_timer_.Reset(); +} + +void AudioOutputDispatcher::StreamClosed() { + DCHECK_EQ(MessageLoop::current(), message_loop_); + + while (!pausing_streams_.empty()) { + idle_streams_.push_back(pausing_streams_.back()); + pausing_streams_.pop_back(); + } + + DCHECK_GT(paused_proxies_, 0u); + paused_proxies_--; + + while (idle_streams_.size() > paused_proxies_) { + idle_streams_.back()->Close(); + idle_streams_.pop_back(); + } +} + +void AudioOutputDispatcher::Shutdown() { + DCHECK_EQ(MessageLoop::current(), message_loop_); + + // Cancel any pending tasks to close paused streams or create new ones. + weak_this_.InvalidateWeakPtrs(); + + // No AudioOutputProxy objects should hold a reference to us when we get + // to this stage. + DCHECK(HasOneRef()) << "Only the AudioManager should hold a reference"; + + AudioOutputStreamList::iterator it = idle_streams_.begin(); + for (; it != idle_streams_.end(); ++it) + (*it)->Close(); + idle_streams_.clear(); + + it = pausing_streams_.begin(); + for (; it != pausing_streams_.end(); ++it) + (*it)->Close(); + pausing_streams_.clear(); +} + +bool AudioOutputDispatcher::CreateAndOpenStream() { + AudioOutputStream* stream = audio_manager_->MakeAudioOutputStream(params_); + if (!stream) + return false; + + if (!stream->Open()) { + stream->Close(); + return false; + } + idle_streams_.push_back(stream); + return true; +} + +void AudioOutputDispatcher::OpenTask() { + // Make sure that we have at least one stream allocated if there + // are paused streams. + if (paused_proxies_ > 0 && idle_streams_.empty() && + pausing_streams_.empty()) { + CreateAndOpenStream(); + } + + close_timer_.Reset(); +} + +// This method is called by |close_timer_|. +void AudioOutputDispatcher::ClosePendingStreams() { + DCHECK_EQ(MessageLoop::current(), message_loop_); + + while (!idle_streams_.empty()) { + idle_streams_.back()->Close(); + idle_streams_.pop_back(); + } +} + } // namespace media diff --git a/media/audio/audio_output_dispatcher.h b/media/audio/audio_output_dispatcher.h index 5c96873..79474a4 100644 --- a/media/audio/audio_output_dispatcher.h +++ b/media/audio/audio_output_dispatcher.h @@ -2,26 +2,33 @@ // Use of this source code is governed by a BSD-style license that can be // found in the LICENSE file. -// AudioOutputDispatcher is a single-threaded base class that dispatches -// creation and deletion of audio output streams. AudioOutputProxy objects use -// this class to allocate and recycle actual audio output streams. When playback -// is started, the proxy calls StartStream() to get an output stream that it -// uses to play audio. When playback is stopped, the proxy returns the stream -// back to the dispatcher by calling StopStream(). +// AudioOutputDispatcher is a single-threaded class that dispatches creation and +// deletion of audio output streams. AudioOutputProxy objects use this class to +// allocate and recycle actual audio output streams. When playback is started, +// the proxy calls StreamStarted() to get an output stream that it uses to play +// audio. When playback is stopped, the proxy returns the stream back to the +// dispatcher by calling StreamStopped(). // -// AudioManagerBase creates one specialization of AudioOutputDispatcher on the -// audio thread for each possible set of audio parameters. I.e streams with -// different parameters are managed independently. The AudioOutputDispatcher -// instance is then deleted on the audio thread when the AudioManager shuts -// down. +// To avoid opening and closing audio devices more frequently than necessary, +// each dispatcher has a pool of inactive physical streams. A stream is closed +// only if it hasn't been used for a certain period of time (specified via the +// constructor). +// +// AudioManagerBase creates one AudioOutputDispatcher on the audio thread for +// each possible set of audio parameters. I.e streams with different parameters +// are managed independently. The AudioOutputDispatcher instance is then +// deleted on the audio thread when the AudioManager shuts down. #ifndef MEDIA_AUDIO_AUDIO_OUTPUT_DISPATCHER_H_ #define MEDIA_AUDIO_AUDIO_OUTPUT_DISPATCHER_H_ +#include <vector> +#include <list> + #include "base/basictypes.h" #include "base/memory/ref_counted.h" +#include "base/memory/weak_ptr.h" #include "base/timer.h" -#include "media/audio/audio_io.h" #include "media/audio/audio_manager.h" #include "media/audio/audio_parameters.h" @@ -29,43 +36,60 @@ class MessageLoop; namespace media { -class AudioOutputProxy; +class AudioOutputStream; class MEDIA_EXPORT AudioOutputDispatcher : public base::RefCountedThreadSafe<AudioOutputDispatcher> { public: + // |close_delay_ms| specifies delay after the stream is paused until + // the audio device is closed. AudioOutputDispatcher(AudioManager* audio_manager, - const AudioParameters& params); + const AudioParameters& params, + base::TimeDelta close_delay); + ~AudioOutputDispatcher(); - // Called by AudioOutputProxy to open the stream. + // Called by AudioOutputProxy when the stream is closed. Opens a new + // physical stream if there are no pending streams in |idle_streams_|. // Returns false, if it fails to open it. - virtual bool OpenStream() = 0; - - // Called by AudioOutputProxy when the stream is started. - // Uses |callback| to get source data and report errors, if any. - // Does *not* take ownership of this callback. - // Returns true if started successfully, false otherwise. - virtual bool StartStream(AudioOutputStream::AudioSourceCallback* callback, - AudioOutputProxy* stream_proxy) = 0; + bool StreamOpened(); - // Called by AudioOutputProxy when the stream is stopped. - // Ownership of the |stream_proxy| is passed to the dispatcher. - virtual void StopStream(AudioOutputProxy* stream_proxy) = 0; + // Called by AudioOutputProxy when the stream is started. If there + // are pending streams in |idle_streams_| then it returns one of them, + // otherwise creates a new one. Returns a physical stream that must + // be used, or NULL if it fails to open audio device. Ownership of + // the result is passed to the caller. + AudioOutputStream* StreamStarted(); - - // Called by AudioOutputProxy when the volume is set. - virtual void StreamVolumeSet(AudioOutputProxy* stream_proxy, - double volume) = 0; + // Called by AudioOutputProxy when the stream is stopped. Holds the + // stream temporarily in |pausing_streams_| and then |stream| is + // added to the pool of pending streams (i.e. |idle_streams_|). + // Ownership of the |stream| is passed to the dispatcher. + void StreamStopped(AudioOutputStream* stream); // Called by AudioOutputProxy when the stream is closed. - virtual void CloseStream(AudioOutputProxy* stream_proxy) = 0; + void StreamClosed(); // Called on the audio thread when the AudioManager is shutting down. - virtual void Shutdown() = 0; + void Shutdown(); + + private: + friend class AudioOutputProxyTest; - protected: - friend class base::RefCountedThreadSafe<AudioOutputDispatcher>; - virtual ~AudioOutputDispatcher(); + // Creates a new physical output stream, opens it and pushes to + // |idle_streams_|. Returns false if the stream couldn't be created or + // opened. + bool CreateAndOpenStream(); + + // A task scheduled by StreamStarted(). Opens a new stream and puts + // it in |idle_streams_|. + void OpenTask(); + + // Before a stream is reused, it should sit idle for a bit. This task is + // called once that time has elapsed. + void StopStreamTask(); + + // Called by |close_timer_|. Closes all pending stream. + void ClosePendingStreams(); // A no-reference-held pointer (we don't want circular references) back to the // AudioManager that owns this object. @@ -73,7 +97,16 @@ class MEDIA_EXPORT AudioOutputDispatcher MessageLoop* message_loop_; AudioParameters params_; - private: + base::TimeDelta pause_delay_; + size_t paused_proxies_; + typedef std::list<AudioOutputStream*> AudioOutputStreamList; + AudioOutputStreamList idle_streams_; + AudioOutputStreamList pausing_streams_; + + // Used to post delayed tasks to ourselves that we cancel inside Shutdown(). + base::WeakPtrFactory<AudioOutputDispatcher> weak_this_; + base::DelayTimer<AudioOutputDispatcher> close_timer_; + DISALLOW_COPY_AND_ASSIGN(AudioOutputDispatcher); }; diff --git a/media/audio/audio_output_dispatcher_impl.cc b/media/audio/audio_output_dispatcher_impl.cc deleted file mode 100644 index 8a060b7..0000000 --- a/media/audio/audio_output_dispatcher_impl.cc +++ /dev/null @@ -1,200 +0,0 @@ -// 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. - -#include "media/audio/audio_output_dispatcher_impl.h" - -#include <algorithm> - -#include "base/bind.h" -#include "base/compiler_specific.h" -#include "base/message_loop.h" -#include "base/time.h" -#include "media/audio/audio_io.h" -#include "media/audio/audio_output_proxy.h" -#include "media/audio/audio_util.h" - -namespace media { - -AudioOutputDispatcherImpl::AudioOutputDispatcherImpl( - AudioManager* audio_manager, - const AudioParameters& params, - const base::TimeDelta& close_delay) - : AudioOutputDispatcher(audio_manager, params), - pause_delay_(base::TimeDelta::FromMilliseconds( - 2 * params.frames_per_buffer() * - base::Time::kMillisecondsPerSecond / params.sample_rate())), - paused_proxies_(0), - ALLOW_THIS_IN_INITIALIZER_LIST(weak_this_(this)), - close_timer_(FROM_HERE, - close_delay, - weak_this_.GetWeakPtr(), - &AudioOutputDispatcherImpl::ClosePendingStreams) { -} - -AudioOutputDispatcherImpl::~AudioOutputDispatcherImpl() { - DCHECK(proxy_to_physical_map_.empty()); - DCHECK(idle_streams_.empty()); - DCHECK(pausing_streams_.empty()); -} - -bool AudioOutputDispatcherImpl::OpenStream() { - DCHECK_EQ(MessageLoop::current(), message_loop_); - - paused_proxies_++; - - // Ensure that there is at least one open stream. - if (idle_streams_.empty() && !CreateAndOpenStream()) - return false; - - close_timer_.Reset(); - return true; -} - -bool AudioOutputDispatcherImpl::StartStream( - AudioOutputStream::AudioSourceCallback* callback, - AudioOutputProxy* stream_proxy) { - DCHECK_EQ(MessageLoop::current(), message_loop_); - - if (idle_streams_.empty() && !CreateAndOpenStream()) - return false; - - AudioOutputStream* physical_stream = idle_streams_.back(); - DCHECK(physical_stream); - idle_streams_.pop_back(); - - DCHECK_GT(paused_proxies_, 0u); - --paused_proxies_; - - close_timer_.Reset(); - - // Schedule task to allocate streams for other proxies if we need to. - message_loop_->PostTask(FROM_HERE, base::Bind( - &AudioOutputDispatcherImpl::OpenTask, weak_this_.GetWeakPtr())); - - double volume = 0; - stream_proxy->GetVolume(&volume); - physical_stream->SetVolume(volume); - physical_stream->Start(callback); - proxy_to_physical_map_[stream_proxy] = physical_stream; - return true; -} - -void AudioOutputDispatcherImpl::StopStream(AudioOutputProxy* stream_proxy) { - DCHECK_EQ(MessageLoop::current(), message_loop_); - - AudioStreamMap::iterator it = proxy_to_physical_map_.find(stream_proxy); - DCHECK(it != proxy_to_physical_map_.end()); - AudioOutputStream* physical_stream = it->second; - proxy_to_physical_map_.erase(it); - - physical_stream->Stop(); - - ++paused_proxies_; - - pausing_streams_.push_front(physical_stream); - - // Don't recycle stream until two buffers worth of time has elapsed. - message_loop_->PostDelayedTask( - FROM_HERE, - base::Bind(&AudioOutputDispatcherImpl::StopStreamTask, - weak_this_.GetWeakPtr()), - pause_delay_); -} - -void AudioOutputDispatcherImpl::StreamVolumeSet(AudioOutputProxy* stream_proxy, - double volume) { - DCHECK_EQ(MessageLoop::current(), message_loop_); - AudioStreamMap::iterator it = proxy_to_physical_map_.find(stream_proxy); - if (it != proxy_to_physical_map_.end()) { - AudioOutputStream* physical_stream = it->second; - physical_stream->SetVolume(volume); - } -} - -void AudioOutputDispatcherImpl::StopStreamTask() { - DCHECK_EQ(MessageLoop::current(), message_loop_); - - if (pausing_streams_.empty()) - return; - - AudioOutputStream* stream = pausing_streams_.back(); - pausing_streams_.pop_back(); - idle_streams_.push_back(stream); - close_timer_.Reset(); -} - -void AudioOutputDispatcherImpl::CloseStream(AudioOutputProxy* stream_proxy) { - DCHECK_EQ(MessageLoop::current(), message_loop_); - - while (!pausing_streams_.empty()) { - idle_streams_.push_back(pausing_streams_.back()); - pausing_streams_.pop_back(); - } - - DCHECK_GT(paused_proxies_, 0u); - paused_proxies_--; - - while (idle_streams_.size() > paused_proxies_) { - idle_streams_.back()->Close(); - idle_streams_.pop_back(); - } -} - -void AudioOutputDispatcherImpl::Shutdown() { - DCHECK_EQ(MessageLoop::current(), message_loop_); - - // Cancel any pending tasks to close paused streams or create new ones. - weak_this_.InvalidateWeakPtrs(); - - // No AudioOutputProxy objects should hold a reference to us when we get - // to this stage. - DCHECK(HasOneRef()) << "Only the AudioManager should hold a reference"; - - AudioOutputStreamList::iterator it = idle_streams_.begin(); - for (; it != idle_streams_.end(); ++it) - (*it)->Close(); - idle_streams_.clear(); - - it = pausing_streams_.begin(); - for (; it != pausing_streams_.end(); ++it) - (*it)->Close(); - pausing_streams_.clear(); -} - -bool AudioOutputDispatcherImpl::CreateAndOpenStream() { - DCHECK_EQ(MessageLoop::current(), message_loop_); - AudioOutputStream* stream = audio_manager_->MakeAudioOutputStream(params_); - if (!stream) - return false; - - if (!stream->Open()) { - stream->Close(); - return false; - } - idle_streams_.push_back(stream); - return true; -} - -void AudioOutputDispatcherImpl::OpenTask() { - DCHECK_EQ(MessageLoop::current(), message_loop_); - // Make sure that we have at least one stream allocated if there - // are paused streams. - if (paused_proxies_ > 0 && idle_streams_.empty() && - pausing_streams_.empty()) { - CreateAndOpenStream(); - } - - close_timer_.Reset(); -} - -// This method is called by |close_timer_|. -void AudioOutputDispatcherImpl::ClosePendingStreams() { - DCHECK_EQ(MessageLoop::current(), message_loop_); - while (!idle_streams_.empty()) { - idle_streams_.back()->Close(); - idle_streams_.pop_back(); - } -} - -} // namespace media diff --git a/media/audio/audio_output_dispatcher_impl.h b/media/audio/audio_output_dispatcher_impl.h deleted file mode 100644 index 8ad7dda..0000000 --- a/media/audio/audio_output_dispatcher_impl.h +++ /dev/null @@ -1,102 +0,0 @@ -// 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. - -// AudioOutputDispatcherImpl is an implementation of AudioOutputDispatcher. -// -// To avoid opening and closing audio devices more frequently than necessary, -// each dispatcher has a pool of inactive physical streams. A stream is closed -// only if it hasn't been used for a certain period of time (specified via the -// constructor). -// - -#ifndef MEDIA_AUDIO_AUDIO_OUTPUT_DISPATCHER_IMPL_H_ -#define MEDIA_AUDIO_AUDIO_OUTPUT_DISPATCHER_IMPL_H_ - -#include <list> -#include <map> - -#include "base/basictypes.h" -#include "base/memory/ref_counted.h" -#include "base/memory/weak_ptr.h" -#include "base/timer.h" -#include "media/audio/audio_io.h" -#include "media/audio/audio_manager.h" -#include "media/audio/audio_output_dispatcher.h" -#include "media/audio/audio_parameters.h" - -class MessageLoop; - -namespace media { - -class AudioOutputProxy; - -class MEDIA_EXPORT AudioOutputDispatcherImpl : public AudioOutputDispatcher { - public: - // |close_delay_ms| specifies delay after the stream is paused until - // the audio device is closed. - AudioOutputDispatcherImpl(AudioManager* audio_manager, - const AudioParameters& params, - const base::TimeDelta& close_delay); - - // Opens a new physical stream if there are no pending streams in - // |idle_streams_|. - virtual bool OpenStream() OVERRIDE; - - // If there are pending streams in |idle_streams_| then it reuses one of - // them, otherwise creates a new one. - virtual bool StartStream(AudioOutputStream::AudioSourceCallback* callback, - AudioOutputProxy* stream_proxy) OVERRIDE; - - // Holds the physical stream temporarily in |pausing_streams_| and then - // |stream| is added to the pool of pending streams (i.e. |idle_streams_|). - virtual void StopStream(AudioOutputProxy* stream_proxy) OVERRIDE; - - virtual void StreamVolumeSet(AudioOutputProxy* stream_proxy, - double volume) OVERRIDE; - - virtual void CloseStream(AudioOutputProxy* stream_proxy) OVERRIDE; - - virtual void Shutdown() OVERRIDE; - - private: - typedef std::map<AudioOutputProxy*, AudioOutputStream*> AudioStreamMap; - friend class base::RefCountedThreadSafe<AudioOutputDispatcherImpl>; - virtual ~AudioOutputDispatcherImpl(); - - friend class AudioOutputProxyTest; - - // Creates a new physical output stream, opens it and pushes to - // |idle_streams_|. Returns false if the stream couldn't be created or - // opened. - bool CreateAndOpenStream(); - - // A task scheduled by StartStream(). Opens a new stream and puts - // it in |idle_streams_|. - void OpenTask(); - - // Before a stream is reused, it should sit idle for a bit. This task is - // called once that time has elapsed. - void StopStreamTask(); - - // Called by |close_timer_|. Closes all pending streams. - void ClosePendingStreams(); - - base::TimeDelta pause_delay_; - size_t paused_proxies_; - typedef std::list<AudioOutputStream*> AudioOutputStreamList; - AudioOutputStreamList idle_streams_; - AudioOutputStreamList pausing_streams_; - - // Used to post delayed tasks to ourselves that we cancel inside Shutdown(). - base::WeakPtrFactory<AudioOutputDispatcherImpl> weak_this_; - base::DelayTimer<AudioOutputDispatcherImpl> close_timer_; - - AudioStreamMap proxy_to_physical_map_; - - DISALLOW_COPY_AND_ASSIGN(AudioOutputDispatcherImpl); -}; - -} // namespace media - -#endif // MEDIA_AUDIO_AUDIO_OUTPUT_DISPATCHER_IMPL_H_ diff --git a/media/audio/audio_output_mixer.cc b/media/audio/audio_output_mixer.cc deleted file mode 100644 index d091672..0000000 --- a/media/audio/audio_output_mixer.cc +++ /dev/null @@ -1,251 +0,0 @@ -// 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. - -#include "media/audio/audio_output_mixer.h" - -#include <algorithm> - -#include "base/bind.h" -#include "base/compiler_specific.h" -#include "base/message_loop.h" -#include "base/time.h" -#include "media/audio/audio_io.h" -#include "media/audio/audio_output_proxy.h" -#include "media/audio/audio_util.h" - -namespace media { - -AudioOutputMixer::AudioOutputMixer(AudioManager* audio_manager, - const AudioParameters& params, - const base::TimeDelta& close_delay) - : AudioOutputDispatcher(audio_manager, params), - ALLOW_THIS_IN_INITIALIZER_LIST(weak_this_(this)), - close_timer_(FROM_HERE, - close_delay, - weak_this_.GetWeakPtr(), - &AudioOutputMixer::ClosePhysicalStream) { - // TODO(enal): align data. - mixer_data_.reset(new uint8[params_.GetBytesPerBuffer()]); -} - -AudioOutputMixer::~AudioOutputMixer() { -} - -bool AudioOutputMixer::OpenStream() { - DCHECK_EQ(MessageLoop::current(), message_loop_); - - if (physical_stream_.get()) - return true; - AudioOutputStream* stream = audio_manager_->MakeAudioOutputStream(params_); - if (!stream) - return false; - if (!stream->Open()) { - stream->Close(); - return false; - } - physical_stream_.reset(stream); - close_timer_.Reset(); - return true; -} - -bool AudioOutputMixer::StartStream( - AudioOutputStream::AudioSourceCallback* callback, - AudioOutputProxy* stream_proxy) { - DCHECK_EQ(MessageLoop::current(), message_loop_); - - // May need to re-open the physical stream if no active proxies and - // enough time had pass. - OpenStream(); - if (!physical_stream_.get()) - return false; - - double volume = 0.0; - stream_proxy->GetVolume(&volume); - bool should_start = proxies_.empty(); - { - base::AutoLock lock(lock_); - ProxyData* proxy_data = &proxies_[stream_proxy]; - proxy_data->audio_source_callback = callback; - proxy_data->volume = volume; - proxy_data->pending_bytes = 0; - } - // We cannot start physical stream under the lock, - // OnMoreData() would try acquiring it... - if (should_start) { - physical_stream_->SetVolume(1.0); - physical_stream_->Start(this); - } - return true; -} - -void AudioOutputMixer::StopStream(AudioOutputProxy* stream_proxy) { - DCHECK_EQ(MessageLoop::current(), message_loop_); - - // Because of possible deadlock we cannot stop physical stream under the lock - // (physical_stream_->Stop() can call OnError(), and it acquires the lock to - // iterate through proxies), so acquire the lock, update proxy list, release - // the lock, and only then stop physical stream if necessary. - bool stop_physical_stream = false; - { - base::AutoLock lock(lock_); - ProxyMap::iterator it = proxies_.find(stream_proxy); - if (it != proxies_.end()) { - proxies_.erase(it); - stop_physical_stream = proxies_.empty(); - } - } - if (physical_stream_.get()) { - if (stop_physical_stream) - physical_stream_->Stop(); - close_timer_.Reset(); - } -} - -void AudioOutputMixer::StreamVolumeSet(AudioOutputProxy* stream_proxy, - double volume) { - DCHECK_EQ(MessageLoop::current(), message_loop_); - - ProxyMap::iterator it = proxies_.find(stream_proxy); - - // Do nothing if stream is not currently playing. - if (it != proxies_.end()) { - base::AutoLock lock(lock_); - it->second.volume = volume; - } -} - -void AudioOutputMixer::CloseStream(AudioOutputProxy* stream_proxy) { - DCHECK_EQ(MessageLoop::current(), message_loop_); - - StopStream(stream_proxy); -} - -void AudioOutputMixer::Shutdown() { - DCHECK_EQ(MessageLoop::current(), message_loop_); - - // Cancel any pending tasks to close physical stream. - weak_this_.InvalidateWeakPtrs(); - - while (!proxies_.empty()) { - CloseStream(proxies_.begin()->first); - } - ClosePhysicalStream(); - - // No AudioOutputProxy objects should hold a reference to us when we get - // to this stage. - DCHECK(HasOneRef()) << "Only the AudioManager should hold a reference"; -} - -void AudioOutputMixer::ClosePhysicalStream() { - DCHECK_EQ(MessageLoop::current(), message_loop_); - - if (proxies_.empty() && physical_stream_.get() != NULL) - physical_stream_.release()->Close(); -} - -// AudioSourceCallback implementation. -uint32 AudioOutputMixer::OnMoreData(AudioOutputStream* stream, - uint8* dest, - uint32 max_size, - AudioBuffersState buffers_state) { - max_size = std::min(max_size, - static_cast<uint32>(params_.GetBytesPerBuffer())); - // TODO(enal): consider getting rid of lock as it is in time-critical code. - // E.g. swap |proxies_| with local variable, and merge 2 lists - // at the end. That would speed things up but complicate stopping - // the stream. - base::AutoLock lock(lock_); - if (proxies_.empty()) - return 0; - uint32 actual_total_size = 0; - uint32 bytes_per_sample = params_.bits_per_sample() >> 3; - - // Go through all the streams, getting data for every one of them - // and mixing it into destination. - // Minor optimization: for the first stream we are writing data directly into - // destination. This way we don't have to mix the data when there is only one - // active stream, and net win in other cases, too. - bool first_stream = true; - uint8* actual_dest = dest; - for (ProxyMap::iterator it = proxies_.begin(); it != proxies_.end(); ++it) { - AudioOutputProxy* stream_proxy = it->first; - ProxyData* proxy_data = &it->second; - // TODO(enal): We don't know |pending _bytes| for individual stream, and we - // should give that value to individual stream's OnMoreData(). I believe it - // can be used there to evaluate exact position of data it should return. - // Current code "sorta works" if everything works perfectly, but would have - // problems if some of the buffers are only partially filled -- we don't - // know how how much data was in the buffer OS returned to us, so we cannot - // correctly calculate new value. If we know number of buffers we can solve - // the problem by storing not one value but vector of them. - int pending_bytes = std::min(proxy_data->pending_bytes, - buffers_state.pending_bytes); - // Note: there is no way we can deduce hardware_delay_bytes for the - // particular proxy stream. Use zero instead. - uint32 actual_size = proxy_data->audio_source_callback->OnMoreData( - stream_proxy, - actual_dest, - max_size, - AudioBuffersState(pending_bytes, 0)); - - // Should update pending_bytes for each proxy. - // If stream ended, pending_bytes goes down by max_size. - if (actual_size == 0) { - pending_bytes -= max_size; - proxy_data->pending_bytes = std::max(pending_bytes, 0); - continue; - } - - // Otherwise, it goes up by amount of data. It cannot exceed max amount of - // data we can buffer, but we don't know that value. So we increment - // pending_bytes unconditionally but adjust it before actual use (which - // would be on a next OnMoreData() call). - proxy_data->pending_bytes = pending_bytes + actual_size; - - // No need to mix muted stream. - double volume = proxy_data->volume; - if (volume == 0.0) - continue; - - // Different handling for first and all subsequent streams. - if (first_stream) { - if (volume != 1.0) { - media::AdjustVolume(actual_dest, - actual_size, - params_.channels(), - bytes_per_sample, - volume); - } - if (actual_size < max_size) - memset(dest + actual_size, 0, max_size - actual_size); - first_stream = false; - actual_dest = mixer_data_.get(); - actual_total_size = actual_size; - } else { - media::MixStreams(dest, - actual_dest, - actual_size, - bytes_per_sample, - volume); - actual_total_size = std::max(actual_size, actual_total_size); - } - } - return actual_total_size; -} - -void AudioOutputMixer::OnError(AudioOutputStream* stream, int code) { - base::AutoLock lock(lock_); - for (ProxyMap::iterator it = proxies_.begin(); it != proxies_.end(); ++it) { - it->second.audio_source_callback->OnError(it->first, code); - } -} - -void AudioOutputMixer::WaitTillDataReady() { - base::AutoLock lock(lock_); - for (ProxyMap::iterator it = proxies_.begin(); it != proxies_.end(); ++it) { - it->second.audio_source_callback->WaitTillDataReady(); - } -} - -} // namespace media diff --git a/media/audio/audio_output_mixer.h b/media/audio/audio_output_mixer.h deleted file mode 100644 index ae94b5f..0000000 --- a/media/audio/audio_output_mixer.h +++ /dev/null @@ -1,92 +0,0 @@ -// 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. - -// AudioOutputMixer is a class that implements browser-side audio mixer. -// AudioOutputMixer implements both AudioOutputDispatcher and -// AudioSourceCallback interfaces. - -#ifndef MEDIA_AUDIO_AUDIO_OUTPUT_MIXER_H_ -#define MEDIA_AUDIO_AUDIO_OUTPUT_MIXER_H_ - -#include <map> - -#include "base/basictypes.h" -#include "base/memory/ref_counted.h" -#include "base/memory/scoped_ptr.h" -#include "base/memory/weak_ptr.h" -#include "base/synchronization/lock.h" -#include "base/timer.h" -#include "media/audio/audio_io.h" -#include "media/audio/audio_manager.h" -#include "media/audio/audio_output_dispatcher.h" -#include "media/audio/audio_parameters.h" - -namespace media { - -class MEDIA_EXPORT AudioOutputMixer - : public AudioOutputDispatcher, - public AudioOutputStream::AudioSourceCallback { - public: - AudioOutputMixer(AudioManager* audio_manager, - const AudioParameters& params, - const base::TimeDelta& close_delay); - - // AudioOutputDispatcher interface. - virtual bool OpenStream() OVERRIDE; - virtual bool StartStream(AudioOutputStream::AudioSourceCallback* callback, - AudioOutputProxy* stream_proxy) OVERRIDE; - virtual void StopStream(AudioOutputProxy* stream_proxy) OVERRIDE; - virtual void StreamVolumeSet(AudioOutputProxy* stream_proxy, - double volume) OVERRIDE; - virtual void CloseStream(AudioOutputProxy* stream_proxy) OVERRIDE; - virtual void Shutdown() OVERRIDE; - - // AudioSourceCallback interface. - virtual uint32 OnMoreData(AudioOutputStream* stream, - uint8* dest, - uint32 max_size, - AudioBuffersState buffers_state) OVERRIDE; - virtual void OnError(AudioOutputStream* stream, int code) OVERRIDE; - virtual void WaitTillDataReady() OVERRIDE; - - private: - friend class base::RefCountedThreadSafe<AudioOutputMixer>; - virtual ~AudioOutputMixer(); - - // Called by |close_timer_|. Closes physical stream. - void ClosePhysicalStream(); - - // The |lock_| must be acquired whenever we modify |proxies_| in the audio - // manager thread or accessing it in the hardware audio thread. Read in the - // audio manager thread is safe. - base::Lock lock_; - - // List of audio output proxies currently being played. - // For every proxy we store aux structure containing data necessary for - // mixing. - struct ProxyData { - AudioOutputStream::AudioSourceCallback* audio_source_callback; - double volume; - int pending_bytes; - }; - typedef std::map<AudioOutputProxy*, ProxyData> ProxyMap; - ProxyMap proxies_; - - // Physical stream for this mixer. - scoped_ptr<AudioOutputStream> physical_stream_; - - // Temporary buffer used when mixing. Allocated in the constructor - // to avoid constant allocation/deallocation in the callback. - scoped_array<uint8> mixer_data_; - - // Used to post delayed tasks to ourselves that we cancel inside Shutdown(). - base::WeakPtrFactory<AudioOutputMixer> weak_this_; - base::DelayTimer<AudioOutputMixer> close_timer_; - - DISALLOW_COPY_AND_ASSIGN(AudioOutputMixer); -}; - -} // namespace media - -#endif // MEDIA_AUDIO_AUDIO_OUTPUT_MIXER_H_ diff --git a/media/audio/audio_output_proxy.cc b/media/audio/audio_output_proxy.cc index a9cfe68..b1d65ba 100644 --- a/media/audio/audio_output_proxy.cc +++ b/media/audio/audio_output_proxy.cc @@ -14,19 +14,21 @@ namespace media { AudioOutputProxy::AudioOutputProxy(AudioOutputDispatcher* dispatcher) : dispatcher_(dispatcher), state_(kCreated), + physical_stream_(NULL), volume_(1.0) { } AudioOutputProxy::~AudioOutputProxy() { DCHECK(CalledOnValidThread()); DCHECK(state_ == kCreated || state_ == kClosed); + DCHECK(!physical_stream_); } bool AudioOutputProxy::Open() { DCHECK(CalledOnValidThread()); DCHECK_EQ(state_, kCreated); - if (!dispatcher_->OpenStream()) { + if (!dispatcher_->StreamOpened()) { state_ = kError; return false; } @@ -37,13 +39,18 @@ bool AudioOutputProxy::Open() { void AudioOutputProxy::Start(AudioSourceCallback* callback) { DCHECK(CalledOnValidThread()); + DCHECK(physical_stream_ == NULL); DCHECK_EQ(state_, kOpened); - if (!dispatcher_->StartStream(callback, this)) { + physical_stream_= dispatcher_->StreamStarted(); + if (!physical_stream_) { state_ = kError; callback->OnError(this, 0); return; } + + physical_stream_->SetVolume(volume_); + physical_stream_->Start(callback); state_ = kPlaying; } @@ -52,14 +59,19 @@ void AudioOutputProxy::Stop() { if (state_ != kPlaying) return; - dispatcher_->StopStream(this); + DCHECK(physical_stream_); + physical_stream_->Stop(); + dispatcher_->StreamStopped(physical_stream_); + physical_stream_ = NULL; state_ = kOpened; } void AudioOutputProxy::SetVolume(double volume) { DCHECK(CalledOnValidThread()); volume_ = volume; - dispatcher_->StreamVolumeSet(this, volume); + if (physical_stream_) { + physical_stream_->SetVolume(volume); + } } void AudioOutputProxy::GetVolume(double* volume) { @@ -70,9 +82,10 @@ void AudioOutputProxy::GetVolume(double* volume) { void AudioOutputProxy::Close() { DCHECK(CalledOnValidThread()); DCHECK(state_ == kCreated || state_ == kError || state_ == kOpened); + DCHECK(!physical_stream_); if (state_ != kCreated) - dispatcher_->CloseStream(this); + dispatcher_->StreamClosed(); state_ = kClosed; diff --git a/media/audio/audio_output_proxy.h b/media/audio/audio_output_proxy.h index 1fb35a3..d34f81f 100644 --- a/media/audio/audio_output_proxy.h +++ b/media/audio/audio_output_proxy.h @@ -53,6 +53,10 @@ class MEDIA_EXPORT AudioOutputProxy scoped_refptr<AudioOutputDispatcher> dispatcher_; State state_; + // The actual audio stream. Must be set to NULL in any state other + // than kPlaying. + AudioOutputStream* physical_stream_; + // Need to save volume here, so that we can restore it in case the stream // is stopped, and then started again. double volume_; diff --git a/media/audio/audio_output_proxy_unittest.cc b/media/audio/audio_output_proxy_unittest.cc index a3adbac..3dbfd2f2 100644 --- a/media/audio/audio_output_proxy_unittest.cc +++ b/media/audio/audio_output_proxy_unittest.cc @@ -2,13 +2,10 @@ // Use of this source code is governed by a BSD-style license that can be // found in the LICENSE file. -#include <string> - #include "base/message_loop.h" #include "base/message_loop_proxy.h" #include "base/threading/platform_thread.h" -#include "media/audio/audio_output_dispatcher_impl.h" -#include "media/audio/audio_output_mixer.h" +#include "media/audio/audio_output_dispatcher.h" #include "media/audio/audio_output_proxy.h" #include "media/audio/audio_manager.h" #include "testing/gmock/include/gmock/gmock.h" @@ -91,7 +88,7 @@ class AudioOutputProxyTest : public testing::Test { virtual void TearDown() { // All paused proxies should have been closed at this point. - EXPECT_EQ(0u, dispatcher_impl_->paused_proxies_); + EXPECT_EQ(0u, dispatcher_->paused_proxies_); // This is necessary to free all proxy objects that have been // closed by the test. @@ -101,166 +98,42 @@ class AudioOutputProxyTest : public testing::Test { void InitDispatcher(base::TimeDelta close_delay) { AudioParameters params(AudioParameters::AUDIO_PCM_LINEAR, CHANNEL_LAYOUT_STEREO, 44100, 16, 1024); - dispatcher_impl_ = new AudioOutputDispatcherImpl(&manager(), - params, - close_delay); - mixer_ = new AudioOutputMixer(&manager(), params, close_delay); + dispatcher_ = new AudioOutputDispatcher(&manager(), params, close_delay); // Necessary to know how long the dispatcher will wait before posting // StopStreamTask. - pause_delay_ = dispatcher_impl_->pause_delay_; + pause_delay_ = dispatcher_->pause_delay_; } MockAudioManager& manager() { return manager_; } - // Wait for the close timer to fire. - void WaitForCloseTimer(const int timer_delay_ms) { - message_loop_.RunAllPending(); // OpenTask() may reset the timer. - base::PlatformThread::Sleep( - base::TimeDelta::FromMilliseconds(timer_delay_ms) * 2); - message_loop_.RunAllPending(); - } - - // Methods that do actual tests. - void OpenAndClose(AudioOutputDispatcher* dispatcher) { - MockAudioOutputStream stream; - - EXPECT_CALL(manager(), MakeAudioOutputStream(_)) - .WillOnce(Return(&stream)); - EXPECT_CALL(stream, Open()) - .WillOnce(Return(true)); - EXPECT_CALL(stream, Close()) - .Times(1); - - AudioOutputProxy* proxy = new AudioOutputProxy(dispatcher); - EXPECT_TRUE(proxy->Open()); - proxy->Close(); - WaitForCloseTimer(kTestCloseDelayMs); - } - - // Create a stream, and then calls Start() and Stop(). - void StartAndStop(AudioOutputDispatcher* dispatcher) { - MockAudioOutputStream stream; - - EXPECT_CALL(manager(), MakeAudioOutputStream(_)) - .WillOnce(Return(&stream)); - EXPECT_CALL(stream, Open()) - .WillOnce(Return(true)); - EXPECT_CALL(stream, Start(_)) - .Times(1); - EXPECT_CALL(stream, SetVolume(_)) - .Times(1); - EXPECT_CALL(stream, Stop()) - .Times(1); - EXPECT_CALL(stream, Close()) - .Times(1); - - AudioOutputProxy* proxy = new AudioOutputProxy(dispatcher); - EXPECT_TRUE(proxy->Open()); - - proxy->Start(&callback_); - proxy->Stop(); - - proxy->Close(); - WaitForCloseTimer(kTestCloseDelayMs); - } - - // Verify that the stream is closed after Stop is called. - void CloseAfterStop(AudioOutputDispatcher* dispatcher) { - MockAudioOutputStream stream; - - EXPECT_CALL(manager(), MakeAudioOutputStream(_)) - .WillOnce(Return(&stream)); - EXPECT_CALL(stream, Open()) - .WillOnce(Return(true)); - EXPECT_CALL(stream, Start(_)) - .Times(1); - EXPECT_CALL(stream, SetVolume(_)) - .Times(1); - EXPECT_CALL(stream, Stop()) - .Times(1); - EXPECT_CALL(stream, Close()) - .Times(1); - - AudioOutputProxy* proxy = new AudioOutputProxy(dispatcher); - EXPECT_TRUE(proxy->Open()); - - proxy->Start(&callback_); - proxy->Stop(); - - // Wait for StopStream() to post StopStreamTask(). - base::PlatformThread::Sleep(pause_delay_ * 2); - WaitForCloseTimer(kTestCloseDelayMs); - - // Verify expectation before calling Close(). - Mock::VerifyAndClear(&stream); - - proxy->Close(); - } - - // Create two streams, but don't start them. Only one device must be open. - void TwoStreams(AudioOutputDispatcher* dispatcher) { - MockAudioOutputStream stream; - - EXPECT_CALL(manager(), MakeAudioOutputStream(_)) - .WillOnce(Return(&stream)); - EXPECT_CALL(stream, Open()) - .WillOnce(Return(true)); - EXPECT_CALL(stream, Close()) - .Times(1); - - AudioOutputProxy* proxy1 = new AudioOutputProxy(dispatcher); - AudioOutputProxy* proxy2 = new AudioOutputProxy(dispatcher); - EXPECT_TRUE(proxy1->Open()); - EXPECT_TRUE(proxy2->Open()); - proxy1->Close(); - proxy2->Close(); - WaitForCloseTimer(kTestCloseDelayMs); - } - - // Open() method failed. - void OpenFailed(AudioOutputDispatcher* dispatcher) { - MockAudioOutputStream stream; - - EXPECT_CALL(manager(), MakeAudioOutputStream(_)) - .WillOnce(Return(&stream)); - EXPECT_CALL(stream, Open()) - .WillOnce(Return(false)); - EXPECT_CALL(stream, Close()) - .Times(1); - - AudioOutputProxy* proxy = new AudioOutputProxy(dispatcher); - EXPECT_FALSE(proxy->Open()); - proxy->Close(); - WaitForCloseTimer(kTestCloseDelayMs); - } - MessageLoop message_loop_; - scoped_refptr<AudioOutputDispatcherImpl> dispatcher_impl_; - scoped_refptr<AudioOutputMixer> mixer_; + scoped_refptr<AudioOutputDispatcher> dispatcher_; base::TimeDelta pause_delay_; MockAudioManager manager_; MockAudioSourceCallback callback_; }; TEST_F(AudioOutputProxyTest, CreateAndClose) { - AudioOutputProxy* proxy = new AudioOutputProxy(dispatcher_impl_); - proxy->Close(); -} - -TEST_F(AudioOutputProxyTest, CreateAndClose_Mixer) { - AudioOutputProxy* proxy = new AudioOutputProxy(mixer_); + AudioOutputProxy* proxy = new AudioOutputProxy(dispatcher_); proxy->Close(); } TEST_F(AudioOutputProxyTest, OpenAndClose) { - OpenAndClose(dispatcher_impl_); -} + MockAudioOutputStream stream; + + EXPECT_CALL(manager(), MakeAudioOutputStream(_)) + .WillOnce(Return(&stream)); + EXPECT_CALL(stream, Open()) + .WillOnce(Return(true)); + EXPECT_CALL(stream, Close()) + .Times(1); -TEST_F(AudioOutputProxyTest, OpenAndClose_Mixer) { - OpenAndClose(mixer_); + AudioOutputProxy* proxy = new AudioOutputProxy(dispatcher_); + EXPECT_TRUE(proxy->Open()); + proxy->Close(); } // Create a stream, and verify that it is closed after kTestCloseDelayMs. @@ -275,7 +148,7 @@ TEST_F(AudioOutputProxyTest, CreateAndWait) { EXPECT_CALL(stream, Close()) .Times(1); - AudioOutputProxy* proxy = new AudioOutputProxy(dispatcher_impl_); + AudioOutputProxy* proxy = new AudioOutputProxy(dispatcher_); EXPECT_TRUE(proxy->Open()); // Simulate a delay. @@ -289,28 +162,87 @@ TEST_F(AudioOutputProxyTest, CreateAndWait) { proxy->Close(); } +// Create a stream, and then calls Start() and Stop(). TEST_F(AudioOutputProxyTest, StartAndStop) { - StartAndStop(dispatcher_impl_); -} + MockAudioOutputStream stream; + + EXPECT_CALL(manager(), MakeAudioOutputStream(_)) + .WillOnce(Return(&stream)); + EXPECT_CALL(stream, Open()) + .WillOnce(Return(true)); + EXPECT_CALL(stream, Start(_)) + .Times(1); + EXPECT_CALL(stream, SetVolume(_)) + .Times(1); + EXPECT_CALL(stream, Stop()) + .Times(1); + EXPECT_CALL(stream, Close()) + .Times(1); + + AudioOutputProxy* proxy = new AudioOutputProxy(dispatcher_); + EXPECT_TRUE(proxy->Open()); -TEST_F(AudioOutputProxyTest, StartAndStop_Mixer) { - StartAndStop(mixer_); + proxy->Start(&callback_); + proxy->Stop(); + + proxy->Close(); } +// Verify that the stream is closed after Stop is called. TEST_F(AudioOutputProxyTest, CloseAfterStop) { - CloseAfterStop(dispatcher_impl_); -} + MockAudioOutputStream stream; + + EXPECT_CALL(manager(), MakeAudioOutputStream(_)) + .WillOnce(Return(&stream)); + EXPECT_CALL(stream, Open()) + .WillOnce(Return(true)); + EXPECT_CALL(stream, Start(_)) + .Times(1); + EXPECT_CALL(stream, SetVolume(_)) + .Times(1); + EXPECT_CALL(stream, Stop()) + .Times(1); + EXPECT_CALL(stream, Close()) + .Times(1); + + AudioOutputProxy* proxy = new AudioOutputProxy(dispatcher_); + EXPECT_TRUE(proxy->Open()); + + proxy->Start(&callback_); + proxy->Stop(); + + // Wait for StreamStopped() to post StopStreamTask(). + base::PlatformThread::Sleep(pause_delay_ * 2); + message_loop_.RunAllPending(); + + // Wait for the close timer to fire. + base::PlatformThread::Sleep( + base::TimeDelta::FromMilliseconds(kTestCloseDelayMs) * 2); + message_loop_.RunAllPending(); -TEST_F(AudioOutputProxyTest, CloseAfterStop_Mixer) { - CloseAfterStop(mixer_); + // Verify expectation before calling Close(). + Mock::VerifyAndClear(&stream); + + proxy->Close(); } +// Create two streams, but don't start them. Only one device must be open. TEST_F(AudioOutputProxyTest, TwoStreams) { - TwoStreams(dispatcher_impl_); -} + MockAudioOutputStream stream; -TEST_F(AudioOutputProxyTest, TwoStreams_Mixer) { - TwoStreams(mixer_); + EXPECT_CALL(manager(), MakeAudioOutputStream(_)) + .WillOnce(Return(&stream)); + EXPECT_CALL(stream, Open()) + .WillOnce(Return(true)); + EXPECT_CALL(stream, Close()) + .Times(1); + + AudioOutputProxy* proxy1 = new AudioOutputProxy(dispatcher_); + AudioOutputProxy* proxy2 = new AudioOutputProxy(dispatcher_); + EXPECT_TRUE(proxy1->Open()); + EXPECT_TRUE(proxy2->Open()); + proxy1->Close(); + proxy2->Close(); } // Two streams: verify that second stream is allocated when the first @@ -341,8 +273,8 @@ TEST_F(AudioOutputProxyTest, TwoStreams_OnePlaying) { EXPECT_CALL(stream2, Close()) .Times(1); - AudioOutputProxy* proxy1 = new AudioOutputProxy(dispatcher_impl_); - AudioOutputProxy* proxy2 = new AudioOutputProxy(dispatcher_impl_); + AudioOutputProxy* proxy1 = new AudioOutputProxy(dispatcher_); + AudioOutputProxy* proxy2 = new AudioOutputProxy(dispatcher_); EXPECT_TRUE(proxy1->Open()); EXPECT_TRUE(proxy2->Open()); @@ -354,39 +286,6 @@ TEST_F(AudioOutputProxyTest, TwoStreams_OnePlaying) { proxy2->Close(); } -// Two streams: verify that only one device will be created. -TEST_F(AudioOutputProxyTest, TwoStreams_OnePlaying_Mixer) { - MockAudioOutputStream stream; - - InitDispatcher(base::TimeDelta::FromMilliseconds(kTestCloseDelayMs)); - - EXPECT_CALL(manager(), MakeAudioOutputStream(_)) - .WillOnce(Return(&stream)); - - EXPECT_CALL(stream, Open()) - .WillOnce(Return(true)); - EXPECT_CALL(stream, Start(_)) - .Times(1); - EXPECT_CALL(stream, SetVolume(_)) - .Times(1); - EXPECT_CALL(stream, Stop()) - .Times(1); - EXPECT_CALL(stream, Close()) - .Times(1); - - AudioOutputProxy* proxy1 = new AudioOutputProxy(mixer_); - AudioOutputProxy* proxy2 = new AudioOutputProxy(mixer_); - EXPECT_TRUE(proxy1->Open()); - EXPECT_TRUE(proxy2->Open()); - - proxy1->Start(&callback_); - proxy1->Stop(); - - proxy1->Close(); - proxy2->Close(); - WaitForCloseTimer(kTestCloseDelayMs); -} - // Two streams, both are playing. Dispatcher should not open a third stream. TEST_F(AudioOutputProxyTest, TwoStreams_BothPlaying) { MockAudioOutputStream stream1; @@ -420,8 +319,8 @@ TEST_F(AudioOutputProxyTest, TwoStreams_BothPlaying) { EXPECT_CALL(stream2, Close()) .Times(1); - AudioOutputProxy* proxy1 = new AudioOutputProxy(dispatcher_impl_); - AudioOutputProxy* proxy2 = new AudioOutputProxy(dispatcher_impl_); + AudioOutputProxy* proxy1 = new AudioOutputProxy(dispatcher_); + AudioOutputProxy* proxy2 = new AudioOutputProxy(dispatcher_); EXPECT_TRUE(proxy1->Open()); EXPECT_TRUE(proxy2->Open()); @@ -434,47 +333,20 @@ TEST_F(AudioOutputProxyTest, TwoStreams_BothPlaying) { proxy2->Close(); } -// Two streams, both are playing. Still have to use single device. -TEST_F(AudioOutputProxyTest, TwoStreams_BothPlaying_Mixer) { +// Open() method failed. +TEST_F(AudioOutputProxyTest, OpenFailed) { MockAudioOutputStream stream; - InitDispatcher(base::TimeDelta::FromMilliseconds(kTestCloseDelayMs)); - EXPECT_CALL(manager(), MakeAudioOutputStream(_)) .WillOnce(Return(&stream)); - EXPECT_CALL(stream, Open()) - .WillOnce(Return(true)); - EXPECT_CALL(stream, Start(_)) - .Times(1); - EXPECT_CALL(stream, SetVolume(_)) - .Times(1); - EXPECT_CALL(stream, Stop()) - .Times(1); + .WillOnce(Return(false)); EXPECT_CALL(stream, Close()) .Times(1); - AudioOutputProxy* proxy1 = new AudioOutputProxy(mixer_); - AudioOutputProxy* proxy2 = new AudioOutputProxy(mixer_); - EXPECT_TRUE(proxy1->Open()); - EXPECT_TRUE(proxy2->Open()); - - proxy1->Start(&callback_); - proxy2->Start(&callback_); - proxy1->Stop(); - proxy2->Stop(); - - proxy1->Close(); - proxy2->Close(); - WaitForCloseTimer(kTestCloseDelayMs); -} - -TEST_F(AudioOutputProxyTest, OpenFailed) { - OpenFailed(dispatcher_impl_); -} - -TEST_F(AudioOutputProxyTest, OpenFailed_Mixer) { - OpenFailed(mixer_); + AudioOutputProxy* proxy = new AudioOutputProxy(dispatcher_); + EXPECT_FALSE(proxy->Open()); + proxy->Close(); } // Start() method failed. @@ -488,7 +360,7 @@ TEST_F(AudioOutputProxyTest, StartFailed) { EXPECT_CALL(stream, Close()) .Times(1); - AudioOutputProxy* proxy = new AudioOutputProxy(dispatcher_impl_); + AudioOutputProxy* proxy = new AudioOutputProxy(dispatcher_); EXPECT_TRUE(proxy->Open()); // Simulate a delay. @@ -513,48 +385,4 @@ TEST_F(AudioOutputProxyTest, StartFailed) { proxy->Close(); } -// Start() method failed. -TEST_F(AudioOutputProxyTest, StartFailed_Mixer) { - MockAudioOutputStream stream; - - EXPECT_CALL(manager(), MakeAudioOutputStream(_)) - .WillOnce(Return(&stream)); - EXPECT_CALL(stream, Open()) - .WillOnce(Return(true)); - EXPECT_CALL(stream, Close()) - .Times(1); - EXPECT_CALL(stream, Start(_)) - .Times(1); - EXPECT_CALL(stream, SetVolume(_)) - .Times(1); - EXPECT_CALL(stream, Stop()) - .Times(1); - - AudioOutputProxy* proxy1 = new AudioOutputProxy(mixer_); - AudioOutputProxy* proxy2 = new AudioOutputProxy(mixer_); - EXPECT_TRUE(proxy1->Open()); - EXPECT_TRUE(proxy2->Open()); - proxy1->Start(&callback_); - proxy1->Stop(); - proxy1->Close(); - WaitForCloseTimer(kTestCloseDelayMs); - - // Verify expectation before continueing. - Mock::VerifyAndClear(&stream); - - // |stream| is closed at this point. Start() should reopen it again. - EXPECT_CALL(manager(), MakeAudioOutputStream(_)) - .WillOnce(Return(reinterpret_cast<AudioOutputStream*>(NULL))); - - EXPECT_CALL(callback_, OnError(_, _)) - .Times(1); - - proxy2->Start(&callback_); - - Mock::VerifyAndClear(&callback_); - - proxy2->Close(); - WaitForCloseTimer(kTestCloseDelayMs); -} - } // namespace media |