diff options
author | enal@chromium.org <enal@chromium.org@0039d316-1c4b-4281-b951-d872f2087c98> | 2011-06-08 18:56:55 +0000 |
---|---|---|
committer | enal@chromium.org <enal@chromium.org@0039d316-1c4b-4281-b951-d872f2087c98> | 2011-06-08 18:56:55 +0000 |
commit | 9a30e9ca9ce478772657375ffaae50f0d4894716 (patch) | |
tree | 3c6dcc5f619dc2b8480f2f0b3b000ca94879f573 /media | |
parent | 90d78ed45ea51db81431ad1db5db93933a762095 (diff) | |
download | chromium_src-9a30e9ca9ce478772657375ffaae50f0d4894716.zip chromium_src-9a30e9ca9ce478772657375ffaae50f0d4894716.tar.gz chromium_src-9a30e9ca9ce478772657375ffaae50f0d4894716.tar.bz2 |
Simplify AlsaPcmOutputStream and AudioManagerLinux. Code was thread-safe, but now
client and audio threads are the same, so we don't need locks/ref counts/etc.
http://codereview.chromium.org/7117001
BUG=62588
Review URL: http://codereview.chromium.org/7117001
git-svn-id: svn://svn.chromium.org/chrome/trunk/src@88369 0039d316-1c4b-4281-b951-d872f2087c98
Diffstat (limited to 'media')
-rw-r--r-- | media/audio/linux/alsa_output.cc | 222 | ||||
-rw-r--r-- | media/audio/linux/alsa_output.h | 101 | ||||
-rw-r--r-- | media/audio/linux/alsa_output_unittest.cc | 90 | ||||
-rw-r--r-- | media/audio/linux/audio_manager_linux.cc | 11 | ||||
-rw-r--r-- | media/audio/linux/audio_manager_linux.h | 8 |
5 files changed, 159 insertions, 273 deletions
diff --git a/media/audio/linux/alsa_output.cc b/media/audio/linux/alsa_output.cc index 31470fc..3802462 100644 --- a/media/audio/linux/alsa_output.cc +++ b/media/audio/linux/alsa_output.cc @@ -4,65 +4,29 @@ // // THREAD SAFETY // -// The AlsaPcmOutputStream object's internal state is accessed by two threads: -// +// AlsaPcmOutputStream object is *not* thread-safe -- we assume that // client thread - creates the object and calls the public APIs. // message loop thread - executes all the internal tasks including querying // the data source for more data, writing to the alsa device, and closing -// the alsa device. It does *not* handle opening the device though. -// -// The class is designed so that most operations that read/modify the object's -// internal state are done on the message loop thread. The exception is data -// conatined in the |shared_data_| structure. Data in this structure needs to -// be accessed by both threads, and should only be accessed when the -// |shared_data_.lock_| is held. -// -// All member variables that are not in |shared_data_| are created/destroyed on -// the |message_loop_|. This allows safe access to them from any task posted to -// |message_loop_|. The values in |shared_data_| are considered to be read-only -// signals by tasks posted to |message_loop_| (see the SEMANTICS of -// |shared_data_| section below). Because of these two constraints, tasks can, -// and must, be coded to be safe in the face of a changing |shared_data_|. -// -// -// SEMANTICS OF |shared_data_| -// -// Though |shared_data_| is accessable by both threads, the code has been -// structured so that all mutations to |shared_data_| are only done in the -// client thread. The message loop thread only ever reads the shared data. -// -// This reduces the need for critical sections because the public API code can -// assume that no mutations occur to the |shared_data_| between queries. -// -// On the message loop side, tasks have been coded such that they can -// operate safely regardless of when state changes happen to |shared_data_|. -// Code that is sensitive to the timing of state changes are delegated to the -// |shared_data_| object so they can executed while holding -// |shared_data_.lock_|. +// the alsa device. +// is actually the same thread. // // // SEMANTICS OF CloseTask() // // The CloseTask() is responsible for cleaning up any resources that were -// acquired after a successful Open(). After a CloseTask() has executed, -// scheduling of reads should stop. Currently scheduled tasks may run, but -// they should not attempt to access any of the internal structures beyond -// querying the |stop_stream_| flag and no-oping themselves. This will -// guarantee that eventually no more tasks will be posted into the message -// loop, and the AlsaPcmOutputStream will be able to delete itself. +// acquired after a successful Open(). CloseTask() would revoke any +// scheduled outstanding runnable methods. // // // SEMANTICS OF ERROR STATES // -// The object has two distinct error states: |shared_data_.state_| == kInError -// and |stop_stream_|. The |shared_data_.state_| state is only settable -// by the client thread, and thus cannot be used to signal when the ALSA device -// fails due to a hardware (or other low-level) event. The |stop_stream_| -// variable is only accessed by the message loop thread; it is used to indicate +// The object has two distinct error states: |state_| == kInError +// and |stop_stream_|. The |stop_stream_| variable is used to indicate // that the playback_handle should no longer be used either because of a -// hardware/low-level event, or because the CloseTask() has been run. +// hardware/low-level event. // -// When |shared_data_.state_| == kInError, all public API functions will fail +// When |state_| == kInError, all public API functions will fail // with an error (Start() will call the OnError() function on the callback // immediately), or no-op themselves with the exception of Close(). Even if an // error state has been entered, if Open() has previously returned successfully, @@ -218,8 +182,7 @@ AlsaPcmOutputStream::AlsaPcmOutputStream(const std::string& device_name, AlsaWrapper* wrapper, AudioManagerLinux* manager, MessageLoop* message_loop) - : shared_data_(MessageLoop::current()), - requested_device_name_(device_name), + : requested_device_name_(device_name), pcm_format_(alsa_util::BitsToFormat(params.bits_per_sample)), channels_(params.channels), sample_rate_(params.sample_rate), @@ -238,30 +201,36 @@ AlsaPcmOutputStream::AlsaPcmOutputStream(const std::string& device_name, manager_(manager), playback_handle_(NULL), frames_per_packet_(packet_size_ / bytes_per_frame_), - client_thread_loop_(MessageLoop::current()), - message_loop_(message_loop) { + message_loop_(message_loop), + ALLOW_THIS_IN_INITIALIZER_LIST(method_factory_(this)), + state_(kCreated), + volume_(1.0f), + source_callback_(NULL) { + DCHECK_EQ(MessageLoop::current(), message_loop_); // Sanity check input values. if ((params.sample_rate > kAlsaMaxSampleRate) || (params.sample_rate <= 0)) { LOG(WARNING) << "Unsupported audio frequency."; - shared_data_.TransitionTo(kInError); + TransitionTo(kInError); } if (AudioParameters::AUDIO_PCM_LINEAR != params.format && AudioParameters::AUDIO_PCM_LOW_LATENCY != params.format) { LOG(WARNING) << "Unsupported audio format"; - shared_data_.TransitionTo(kInError); + TransitionTo(kInError); } if (pcm_format_ == SND_PCM_FORMAT_UNKNOWN) { LOG(WARNING) << "Unsupported bits per sample: " << params.bits_per_sample; - shared_data_.TransitionTo(kInError); + TransitionTo(kInError); } } AlsaPcmOutputStream::~AlsaPcmOutputStream() { - InternalState state = shared_data_.state(); - DCHECK(state == kCreated || state == kIsClosed || state == kInError); + InternalState current_state = state(); + DCHECK(current_state == kCreated || + current_state == kIsClosed || + current_state == kInError); // TODO(ajwong): Ensure that CloseTask has been called and the // playback handle released by DCHECKing that playback_handle_ is NULL. @@ -270,14 +239,14 @@ AlsaPcmOutputStream::~AlsaPcmOutputStream() { } bool AlsaPcmOutputStream::Open() { - DCHECK_EQ(MessageLoop::current(), client_thread_loop_); + DCHECK_EQ(MessageLoop::current(), message_loop_); - if (shared_data_.state() == kInError) { + if (state() == kInError) { return false; } - if (!shared_data_.CanTransitionTo(kIsOpened)) { - NOTREACHED() << "Invalid state: " << shared_data_.state(); + if (!CanTransitionTo(kIsOpened)) { + NOTREACHED() << "Invalid state: " << state(); return false; } @@ -285,67 +254,62 @@ bool AlsaPcmOutputStream::Open() { // CanTransitionTo() was checked above, and it is assumed that this // object's public API is only called on one thread so the state cannot // transition out from under us. - shared_data_.TransitionTo(kIsOpened); + TransitionTo(kIsOpened); message_loop_->PostTask( FROM_HERE, - NewRunnableMethod(this, &AlsaPcmOutputStream::OpenTask)); + method_factory_.NewRunnableMethod(&AlsaPcmOutputStream::OpenTask)); return true; } void AlsaPcmOutputStream::Close() { - DCHECK_EQ(MessageLoop::current(), client_thread_loop_); + DCHECK_EQ(MessageLoop::current(), message_loop_); // Sanity check that the transition occurs correctly. It is safe to // continue anyways because all operations for closing are idempotent. - if (shared_data_.TransitionTo(kIsClosed) != kIsClosed) { + if (TransitionTo(kIsClosed) != kIsClosed) { NOTREACHED() << "Unable to transition Closed."; } message_loop_->PostTask( FROM_HERE, - NewRunnableMethod(this, &AlsaPcmOutputStream::CloseTask)); - - // Signal to the manager that we're closed and can be removed. Since - // we just posted a CloseTask to the message loop, we won't be deleted - // immediately, but it will happen soon afterwards. - manager()->ReleaseOutputStream(this); + method_factory_.NewRunnableMethod(&AlsaPcmOutputStream::CloseTask)); } void AlsaPcmOutputStream::Start(AudioSourceCallback* callback) { - DCHECK_EQ(MessageLoop::current(), client_thread_loop_); + DCHECK_EQ(MessageLoop::current(), message_loop_); CHECK(callback); - shared_data_.set_source_callback(callback); + set_source_callback(callback); // Only post the task if we can enter the playing state. - if (shared_data_.TransitionTo(kIsPlaying) == kIsPlaying) { + if (TransitionTo(kIsPlaying) == kIsPlaying) { message_loop_->PostTask( FROM_HERE, - NewRunnableMethod(this, &AlsaPcmOutputStream::StartTask)); + method_factory_.NewRunnableMethod(&AlsaPcmOutputStream::StartTask)); } } void AlsaPcmOutputStream::Stop() { - DCHECK_EQ(MessageLoop::current(), client_thread_loop_); + DCHECK_EQ(MessageLoop::current(), message_loop_); // Reset the callback, so that it is not called anymore. - shared_data_.set_source_callback(NULL); + set_source_callback(NULL); - shared_data_.TransitionTo(kIsStopped); + TransitionTo(kIsStopped); } void AlsaPcmOutputStream::SetVolume(double volume) { - DCHECK_EQ(MessageLoop::current(), client_thread_loop_); + DCHECK_EQ(MessageLoop::current(), message_loop_); - shared_data_.set_volume(static_cast<float>(volume)); + volume_ = static_cast<float>(volume); } void AlsaPcmOutputStream::GetVolume(double* volume) { - DCHECK_EQ(MessageLoop::current(), client_thread_loop_); + DCHECK_EQ(MessageLoop::current(), message_loop_); - *volume = shared_data_.volume(); + *volume = volume_; } void AlsaPcmOutputStream::OpenTask() { @@ -396,7 +360,7 @@ void AlsaPcmOutputStream::StartTask() { return; } - if (shared_data_.state() != kIsPlaying) { + if (state() != kIsPlaying) { return; } @@ -428,8 +392,6 @@ void AlsaPcmOutputStream::StartTask() { } void AlsaPcmOutputStream::CloseTask() { - // NOTE: Keep this function idempotent to handle errors that might cause - // multiple CloseTasks to be posted. DCHECK_EQ(message_loop_, MessageLoop::current()); // Shutdown the audio device. @@ -443,7 +405,13 @@ void AlsaPcmOutputStream::CloseTask() { buffer_.reset(); // Signal anything that might already be scheduled to stop. - stop_stream_ = true; + stop_stream_ = true; // Not necessary in production, but unit tests + // uses the flag to verify that stream was closed. + method_factory_.RevokeAll(); + + // Signal to the manager that we're closed and can be removed. + // Should be last call in the method as it deletes "this". + manager_->ReleaseOutputStream(this); } void AlsaPcmOutputStream::BufferPacket(bool* source_exhausted) { @@ -470,10 +438,10 @@ void AlsaPcmOutputStream::BufferPacket(bool* source_exhausted) { scoped_refptr<media::DataBuffer> packet = new media::DataBuffer(packet_size_); - size_t packet_size = - shared_data_.OnMoreData( - this, packet->GetWritableData(), packet->GetBufferSize(), - AudioBuffersState(buffer_delay, hardware_delay)); + size_t packet_size = RunDataCallback(packet->GetWritableData(), + packet->GetBufferSize(), + AudioBuffersState(buffer_delay, + hardware_delay)); CHECK(packet_size <= packet->GetBufferSize()) << "Data source overran buffer."; @@ -489,7 +457,7 @@ void AlsaPcmOutputStream::BufferPacket(bool* source_exhausted) { packet_size, channels_, bytes_per_sample_, - shared_data_.volume())) { + volume_)) { // Adjust packet size for downmix. packet_size = packet_size / bytes_per_frame_ * bytes_per_output_frame_; @@ -525,7 +493,7 @@ void AlsaPcmOutputStream::BufferPacket(bool* source_exhausted) { packet_size, channels_, bytes_per_sample_, - shared_data_.volume()); + volume_); } if (packet_size > 0) { @@ -547,7 +515,7 @@ void AlsaPcmOutputStream::WritePacket() { return; } - if (shared_data_.state() == kIsStopped) { + if (state() == kIsStopped) { return; } @@ -579,7 +547,7 @@ void AlsaPcmOutputStream::WritePacket() { if (frames_written != -EAGAIN) { LOG(ERROR) << "Failed to write to pcm device: " << wrapper_->StrError(frames_written); - shared_data_.OnError(this, frames_written); + RunErrorCallback(frames_written); stop_stream_ = true; } } else { @@ -610,7 +578,7 @@ void AlsaPcmOutputStream::WriteTask() { return; } - if (shared_data_.state() == kIsStopped) { + if (state() == kIsStopped) { return; } @@ -655,17 +623,17 @@ void AlsaPcmOutputStream::ScheduleNextWrite(bool source_exhausted) { } // Only schedule more reads/writes if we are still in the playing state. - if (shared_data_.state() == kIsPlaying) { + if (state() == kIsPlaying) { if (next_fill_time_ms == 0) { message_loop_->PostTask( FROM_HERE, - NewRunnableMethod(this, &AlsaPcmOutputStream::WriteTask)); + method_factory_.NewRunnableMethod(&AlsaPcmOutputStream::WriteTask)); } else { // TODO(ajwong): Measure the reliability of the delay interval. Use // base/metrics/histogram.h. message_loop_->PostDelayedTask( FROM_HERE, - NewRunnableMethod(this, &AlsaPcmOutputStream::WriteTask), + method_factory_.NewRunnableMethod(&AlsaPcmOutputStream::WriteTask), next_fill_time_ms); } } @@ -842,28 +810,7 @@ snd_pcm_t* AlsaPcmOutputStream::AutoSelectDevice(unsigned int latency) { return NULL; } -AudioManagerLinux* AlsaPcmOutputStream::manager() { - DCHECK_EQ(MessageLoop::current(), client_thread_loop_); - return manager_; -} - -AlsaPcmOutputStream::SharedData::SharedData( - MessageLoop* state_transition_loop) - : state_(kCreated), - volume_(1.0f), - source_callback_(NULL), - state_transition_loop_(state_transition_loop) { -} - -bool AlsaPcmOutputStream::SharedData::CanTransitionTo(InternalState to) { - base::AutoLock l(lock_); - return CanTransitionTo_Locked(to); -} - -bool AlsaPcmOutputStream::SharedData::CanTransitionTo_Locked( - InternalState to) { - lock_.AssertAcquired(); - +bool AlsaPcmOutputStream::CanTransitionTo(InternalState to) { switch (state_) { case kCreated: return to == kIsOpened || to == kIsClosed || to == kInError; @@ -890,11 +837,10 @@ bool AlsaPcmOutputStream::SharedData::CanTransitionTo_Locked( } AlsaPcmOutputStream::InternalState -AlsaPcmOutputStream::SharedData::TransitionTo(InternalState to) { - DCHECK_EQ(MessageLoop::current(), state_transition_loop_); +AlsaPcmOutputStream::TransitionTo(InternalState to) { + DCHECK_EQ(MessageLoop::current(), message_loop_); - base::AutoLock l(lock_); - if (!CanTransitionTo_Locked(to)) { + if (!CanTransitionTo(to)) { NOTREACHED() << "Cannot transition from: " << state_ << " to: " << to; state_ = kInError; } else { @@ -903,45 +849,29 @@ AlsaPcmOutputStream::SharedData::TransitionTo(InternalState to) { return state_; } -AlsaPcmOutputStream::InternalState AlsaPcmOutputStream::SharedData::state() { - base::AutoLock l(lock_); +AlsaPcmOutputStream::InternalState AlsaPcmOutputStream::state() { return state_; } -float AlsaPcmOutputStream::SharedData::volume() { - base::AutoLock l(lock_); - return volume_; -} - -void AlsaPcmOutputStream::SharedData::set_volume(float v) { - base::AutoLock l(lock_); - volume_ = v; -} - -uint32 AlsaPcmOutputStream::SharedData::OnMoreData( - AudioOutputStream* stream, uint8* dest, uint32 max_size, - AudioBuffersState buffers_state) { - base::AutoLock l(lock_); +uint32 AlsaPcmOutputStream::RunDataCallback(uint8* dest, + uint32 max_size, + AudioBuffersState buffers_state) { if (source_callback_) { - return source_callback_->OnMoreData(stream, dest, max_size, buffers_state); + return source_callback_->OnMoreData(this, dest, max_size, buffers_state); } return 0; } -void AlsaPcmOutputStream::SharedData::OnError(AudioOutputStream* stream, - int code) { - base::AutoLock l(lock_); +void AlsaPcmOutputStream::RunErrorCallback(int code) { if (source_callback_) { - source_callback_->OnError(stream, code); + source_callback_->OnError(this, code); } } // Changes the AudioSourceCallback to proxy calls to. Pass in NULL to // release ownership of the currently registered callback. -void AlsaPcmOutputStream::SharedData::set_source_callback( - AudioSourceCallback* callback) { - DCHECK_EQ(MessageLoop::current(), state_transition_loop_); - base::AutoLock l(lock_); +void AlsaPcmOutputStream::set_source_callback(AudioSourceCallback* callback) { + DCHECK_EQ(MessageLoop::current(), message_loop_); source_callback_ = callback; } diff --git a/media/audio/linux/alsa_output.h b/media/audio/linux/alsa_output.h index 6c629c2..48482b1 100644 --- a/media/audio/linux/alsa_output.h +++ b/media/audio/linux/alsa_output.h @@ -24,13 +24,6 @@ // threading assumptions at the top of the implementation file to avoid // introducing race conditions between tasks posted to the internal // message_loop, and the thread calling the public APIs. -// -// TODO(sergeyu): AlsaPcmOutputStream is always created and used from the -// audio thread (i.e. |client_thread_loop_| and |message_loop_| always point -// to the same thread), so it doesn't need to be thread-safe anymore. -// -// TODO(sergeyu): Remove refcounter from AlsaPcmOutputStream and use -// ScopedRunnableMethodFactory to create tasks. #ifndef MEDIA_AUDIO_LINUX_ALSA_OUTPUT_H_ #define MEDIA_AUDIO_LINUX_ALSA_OUTPUT_H_ @@ -40,9 +33,8 @@ #include <string> #include "base/gtest_prod_util.h" -#include "base/memory/ref_counted.h" #include "base/memory/scoped_ptr.h" -#include "base/synchronization/lock.h" +#include "base/task.h" #include "media/audio/audio_io.h" #include "media/audio/audio_parameters.h" @@ -54,9 +46,7 @@ class AlsaWrapper; class AudioManagerLinux; class MessageLoop; -class AlsaPcmOutputStream : - public AudioOutputStream, - public base::RefCountedThreadSafe<AlsaPcmOutputStream> { +class AlsaPcmOutputStream : public AudioOutputStream { public: // String for the generic "default" ALSA device that has the highest // compatibility and chance of working. @@ -85,6 +75,8 @@ class AlsaPcmOutputStream : AudioManagerLinux* manager, MessageLoop* message_loop); + virtual ~AlsaPcmOutputStream(); + // Implementation of AudioOutputStream. virtual bool Open(); virtual void Close(); @@ -94,7 +86,6 @@ class AlsaPcmOutputStream : virtual void GetVolume(double* volume); private: - friend class base::RefCountedThreadSafe<AlsaPcmOutputStream>; friend class AlsaPcmOutputStreamTest; FRIEND_TEST_ALL_PREFIXES(AlsaPcmOutputStreamTest, AutoSelectDevice_DeviceSelect); @@ -120,8 +111,6 @@ class AlsaPcmOutputStream : FRIEND_TEST_ALL_PREFIXES(AlsaPcmOutputStreamTest, WritePacket_StopStream); FRIEND_TEST_ALL_PREFIXES(AlsaPcmOutputStreamTest, WritePacket_WriteFails); - virtual ~AlsaPcmOutputStream(); - // Flags indicating the state of the stream. enum InternalState { kInError = 0, @@ -156,54 +145,28 @@ class AlsaPcmOutputStream : // of channels. This function will set |device_name_| and |should_downmix_|. snd_pcm_t* AutoSelectDevice(uint32 latency); - // Thread-asserting accessors for member variables. - AudioManagerLinux* manager(); - - // Struct holding all mutable the data that must be shared by the - // message_loop() and the thread that created the object. - class SharedData { - public: - explicit SharedData(MessageLoop* state_transition_loop); - - // Functions to safeguard state transitions and ensure that transitions are - // only allowed occuring on the thread that created the object. All - // changes to the object state should go through these functions. - bool CanTransitionTo(InternalState to); - bool CanTransitionTo_Locked(InternalState to); - InternalState TransitionTo(InternalState to); - InternalState state(); - - float volume(); - void set_volume(float v); - - // API for Proxying calls to the AudioSourceCallback provided during - // Start(). These APIs are threadsafe. - // - // TODO(ajwong): This is necessary because the ownership semantics for the - // |source_callback_| object are incorrect in AudioRenderHost. The callback - // is passed into the output stream, but ownership is not transfered which - // requires a synchronization on access of the |source_callback_| to avoid - // using a deleted callback. - uint32 OnMoreData(AudioOutputStream* stream, uint8* dest, - uint32 max_size, AudioBuffersState buffers_state); - void OnError(AudioOutputStream* stream, int code); + // Functions to safeguard state transitions. All changes to the object state + // should go through these functions. + bool CanTransitionTo(InternalState to); + InternalState TransitionTo(InternalState to); + InternalState state(); - // Changes the AudioSourceCallback to proxy calls to. Pass in NULL to - // release ownership of the currently registered callback. - void set_source_callback(AudioSourceCallback* callback); - - private: - base::Lock lock_; - - InternalState state_; - float volume_; // Volume level from 0.0 to 1.0. - - AudioSourceCallback* source_callback_; - - MessageLoop* const state_transition_loop_; - - DISALLOW_COPY_AND_ASSIGN(SharedData); - } shared_data_; + // API for Proxying calls to the AudioSourceCallback provided during + // Start(). + // + // TODO(ajwong): This is necessary because the ownership semantics for the + // |source_callback_| object are incorrect in AudioRenderHost. The callback + // is passed into the output stream, but ownership is not transfered which + // requires a synchronization on access of the |source_callback_| to avoid + // using a deleted callback. + uint32 RunDataCallback(uint8* dest, + uint32 max_size, + AudioBuffersState buffers_state); + void RunErrorCallback(int code); + + // Changes the AudioSourceCallback to proxy calls to. Pass in NULL to + // release ownership of the currently registered callback. + void set_source_callback(AudioSourceCallback* callback); // Configuration constants from the constructor. Referenceable by all threads // since they are constants. @@ -232,8 +195,6 @@ class AlsaPcmOutputStream : AlsaWrapper* wrapper_; // Audio manager that created us. Used to report that we've been closed. - // This should only be used on the |client_thread_loop_|. Access via - // the manager() function. AudioManagerLinux* manager_; // Handle to the actual PCM playback device. @@ -242,13 +203,19 @@ class AlsaPcmOutputStream : scoped_ptr<media::SeekableBuffer> buffer_; uint32 frames_per_packet_; - // Used to check which message loop is allowed to call the public APIs. - MessageLoop* client_thread_loop_; - // The message loop responsible for querying the data source, and writing to // the output device. MessageLoop* message_loop_; + // Allows us to run tasks on the AlsaPcmOutputStream instance which are + // bound by its lifetime. + ScopedRunnableMethodFactory<AlsaPcmOutputStream> method_factory_; + + InternalState state_; + float volume_; // Volume level from 0.0 to 1.0. + + AudioSourceCallback* source_callback_; + DISALLOW_COPY_AND_ASSIGN(AlsaPcmOutputStream); }; diff --git a/media/audio/linux/alsa_output_unittest.cc b/media/audio/linux/alsa_output_unittest.cc index 3608fcc..eea8321 100644 --- a/media/audio/linux/alsa_output_unittest.cc +++ b/media/audio/linux/alsa_output_unittest.cc @@ -91,11 +91,11 @@ class MockAudioManagerLinux : public AudioManagerLinux { class AlsaPcmOutputStreamTest : public testing::Test { protected: AlsaPcmOutputStreamTest() { - test_stream_ = CreateStream(kTestChannelLayout); + test_stream_.reset(CreateStream(kTestChannelLayout)); } virtual ~AlsaPcmOutputStreamTest() { - test_stream_ = NULL; + test_stream_.reset(NULL); } AlsaPcmOutputStream* CreateStream(ChannelLayout layout) { @@ -156,7 +156,7 @@ class AlsaPcmOutputStreamTest : public testing::Test { StrictMock<MockAlsaWrapper> mock_alsa_wrapper_; StrictMock<MockAudioManagerLinux> mock_manager_; MessageLoop message_loop_; - scoped_refptr<AlsaPcmOutputStream> test_stream_; + scoped_ptr<AlsaPcmOutputStream> test_stream_; scoped_refptr<media::DataBuffer> packet_; private: @@ -194,42 +194,37 @@ void* AlsaPcmOutputStreamTest::kFakeHints[] = { kSurround70, kSurround71, NULL }; TEST_F(AlsaPcmOutputStreamTest, ConstructedState) { - EXPECT_EQ(AlsaPcmOutputStream::kCreated, - test_stream_->shared_data_.state()); + EXPECT_EQ(AlsaPcmOutputStream::kCreated, test_stream_->state()); // Should support mono. - test_stream_ = CreateStream(CHANNEL_LAYOUT_MONO); - EXPECT_EQ(AlsaPcmOutputStream::kCreated, - test_stream_->shared_data_.state()); + test_stream_.reset(CreateStream(CHANNEL_LAYOUT_MONO)); + EXPECT_EQ(AlsaPcmOutputStream::kCreated, test_stream_->state()); // Should support multi-channel. - test_stream_ = CreateStream(CHANNEL_LAYOUT_SURROUND); - EXPECT_EQ(AlsaPcmOutputStream::kCreated, - test_stream_->shared_data_.state()); + test_stream_.reset(CreateStream(CHANNEL_LAYOUT_SURROUND)); + EXPECT_EQ(AlsaPcmOutputStream::kCreated, test_stream_->state()); // Bad bits per sample. AudioParameters bad_bps_params(kTestFormat, kTestChannelLayout, kTestSampleRate, kTestBitsPerSample - 1, kTestFramesPerPacket); - test_stream_ = new AlsaPcmOutputStream(kTestDeviceName, - bad_bps_params, - &mock_alsa_wrapper_, - &mock_manager_, - &message_loop_); - EXPECT_EQ(AlsaPcmOutputStream::kInError, - test_stream_->shared_data_.state()); + test_stream_.reset(new AlsaPcmOutputStream(kTestDeviceName, + bad_bps_params, + &mock_alsa_wrapper_, + &mock_manager_, + &message_loop_)); + EXPECT_EQ(AlsaPcmOutputStream::kInError, test_stream_->state()); // Bad format. AudioParameters bad_format_params( AudioParameters::AUDIO_LAST_FORMAT, kTestChannelLayout, kTestSampleRate, kTestBitsPerSample, kTestFramesPerPacket); - test_stream_ = new AlsaPcmOutputStream(kTestDeviceName, - bad_format_params, - &mock_alsa_wrapper_, - &mock_manager_, - &message_loop_); - EXPECT_EQ(AlsaPcmOutputStream::kInError, - test_stream_->shared_data_.state()); + test_stream_.reset(new AlsaPcmOutputStream(kTestDeviceName, + bad_format_params, + &mock_alsa_wrapper_, + &mock_manager_, + &message_loop_)); + EXPECT_EQ(AlsaPcmOutputStream::kInError, test_stream_->state()); } TEST_F(AlsaPcmOutputStreamTest, LatencyFloor) { @@ -253,7 +248,8 @@ TEST_F(AlsaPcmOutputStreamTest, LatencyFloor) { SetArgumentPointee<2>(kTestFramesPerPacket / 2), Return(0))); - test_stream_ = CreateStream(kTestChannelLayout, kPacketFramesInMinLatency); + test_stream_.reset(CreateStream(kTestChannelLayout, + kPacketFramesInMinLatency)); ASSERT_TRUE(test_stream_->Open()); message_loop_.RunAllPending(); @@ -283,7 +279,8 @@ TEST_F(AlsaPcmOutputStreamTest, LatencyFloor) { SetArgumentPointee<2>(kTestFramesPerPacket / 2), Return(0))); - test_stream_ = CreateStream(kTestChannelLayout, kOverMinLatencyPacketSize); + test_stream_.reset(CreateStream(kTestChannelLayout, + kOverMinLatencyPacketSize)); ASSERT_TRUE(test_stream_->Open()); message_loop_.RunAllPending(); @@ -331,8 +328,7 @@ TEST_F(AlsaPcmOutputStreamTest, OpenClose) { ASSERT_TRUE(test_stream_->Open()); message_loop_.RunAllPending(); - EXPECT_EQ(AlsaPcmOutputStream::kIsOpened, - test_stream_->shared_data_.state()); + EXPECT_EQ(AlsaPcmOutputStream::kIsOpened, test_stream_->state()); EXPECT_EQ(kFakeHandle, test_stream_->playback_handle_); EXPECT_EQ(kTestFramesPerPacket, test_stream_->frames_per_packet_); EXPECT_TRUE(test_stream_->buffer_.get()); @@ -360,14 +356,12 @@ TEST_F(AlsaPcmOutputStreamTest, PcmOpenFailed) { // Open still succeeds since PcmOpen is delegated to another thread. ASSERT_TRUE(test_stream_->Open()); - ASSERT_EQ(AlsaPcmOutputStream::kIsOpened, - test_stream_->shared_data_.state()); + ASSERT_EQ(AlsaPcmOutputStream::kIsOpened, test_stream_->state()); ASSERT_FALSE(test_stream_->stop_stream_); message_loop_.RunAllPending(); // Ensure internal state is set for a no-op stream if PcmOpen() failes. - EXPECT_EQ(AlsaPcmOutputStream::kIsOpened, - test_stream_->shared_data_.state()); + EXPECT_EQ(AlsaPcmOutputStream::kIsOpened, test_stream_->state()); EXPECT_TRUE(test_stream_->stop_stream_); EXPECT_TRUE(test_stream_->playback_handle_ == NULL); EXPECT_FALSE(test_stream_->buffer_.get()); @@ -394,14 +388,12 @@ TEST_F(AlsaPcmOutputStreamTest, PcmSetParamsFailed) { // If open fails, the stream stays in kCreated because it has effectively had // no changes. ASSERT_TRUE(test_stream_->Open()); - EXPECT_EQ(AlsaPcmOutputStream::kIsOpened, - test_stream_->shared_data_.state()); + EXPECT_EQ(AlsaPcmOutputStream::kIsOpened, test_stream_->state()); ASSERT_FALSE(test_stream_->stop_stream_); message_loop_.RunAllPending(); // Ensure internal state is set for a no-op stream if PcmSetParams() failes. - EXPECT_EQ(AlsaPcmOutputStream::kIsOpened, - test_stream_->shared_data_.state()); + EXPECT_EQ(AlsaPcmOutputStream::kIsOpened, test_stream_->state()); EXPECT_TRUE(test_stream_->stop_stream_); EXPECT_TRUE(test_stream_->playback_handle_ == NULL); EXPECT_FALSE(test_stream_->buffer_.get()); @@ -561,7 +553,7 @@ TEST_F(AlsaPcmOutputStreamTest, BufferPacket) { .WillOnce(Return(10)); bool source_exhausted; - test_stream_->shared_data_.set_source_callback(&mock_callback); + test_stream_->set_source_callback(&mock_callback); test_stream_->packet_size_ = kTestPacketSize; test_stream_->BufferPacket(&source_exhausted); @@ -586,7 +578,7 @@ TEST_F(AlsaPcmOutputStreamTest, BufferPacket_Negative) { .WillOnce(Return(10)); bool source_exhausted; - test_stream_->shared_data_.set_source_callback(&mock_callback); + test_stream_->set_source_callback(&mock_callback); test_stream_->packet_size_ = kTestPacketSize; test_stream_->BufferPacket(&source_exhausted); @@ -611,7 +603,7 @@ TEST_F(AlsaPcmOutputStreamTest, BufferPacket_Underrun) { .WillOnce(Return(10)); bool source_exhausted; - test_stream_->shared_data_.set_source_callback(&mock_callback); + test_stream_->set_source_callback(&mock_callback); test_stream_->packet_size_ = kTestPacketSize; test_stream_->BufferPacket(&source_exhausted); @@ -688,7 +680,7 @@ TEST_F(AlsaPcmOutputStreamTest, AutoSelectDevice_DeviceSelect) { EXPECT_CALL(mock_alsa_wrapper_, DeviceNameGetHint(_, StrEq("NAME"))) .WillRepeatedly(Invoke(EchoHint)); - test_stream_ = CreateStream(kExpectedLayouts[i]); + test_stream_.reset(CreateStream(kExpectedLayouts[i])); EXPECT_TRUE(test_stream_->AutoSelectDevice(i)); EXPECT_EQ(kExpectedDownmix[i], test_stream_->should_downmix_); @@ -738,7 +730,7 @@ TEST_F(AlsaPcmOutputStreamTest, AutoSelectDevice_FallbackDevices) { EXPECT_CALL(mock_alsa_wrapper_, PcmOpen(_, StrEq(fourth_try.c_str()), _, _)) .WillOnce(Return(kTestFailedErrno)); - test_stream_ = CreateStream(CHANNEL_LAYOUT_5POINT0); + test_stream_.reset(CreateStream(CHANNEL_LAYOUT_5POINT0)); EXPECT_FALSE(test_stream_->AutoSelectDevice(5)); } @@ -756,7 +748,7 @@ TEST_F(AlsaPcmOutputStreamTest, AutoSelectDevice_HintFail) { EXPECT_CALL(mock_alsa_wrapper_, StrError(kTestFailedErrno)) .WillOnce(Return(kDummyMessage)); - test_stream_ = CreateStream(CHANNEL_LAYOUT_5POINT0); + test_stream_.reset(CreateStream(CHANNEL_LAYOUT_5POINT0)); EXPECT_TRUE(test_stream_->AutoSelectDevice(5)); EXPECT_TRUE(test_stream_->should_downmix_); } @@ -771,8 +763,8 @@ TEST_F(AlsaPcmOutputStreamTest, BufferPacket_StopStream) { } TEST_F(AlsaPcmOutputStreamTest, ScheduleNextWrite) { - test_stream_->shared_data_.TransitionTo(AlsaPcmOutputStream::kIsOpened); - test_stream_->shared_data_.TransitionTo(AlsaPcmOutputStream::kIsPlaying); + test_stream_->TransitionTo(AlsaPcmOutputStream::kIsOpened); + test_stream_->TransitionTo(AlsaPcmOutputStream::kIsPlaying); InitBuffer(); @@ -789,12 +781,12 @@ TEST_F(AlsaPcmOutputStreamTest, ScheduleNextWrite) { test_stream_->stop_stream_ = true; message_loop_.RunAllPending(); - test_stream_->shared_data_.TransitionTo(AlsaPcmOutputStream::kIsClosed); + test_stream_->TransitionTo(AlsaPcmOutputStream::kIsClosed); } TEST_F(AlsaPcmOutputStreamTest, ScheduleNextWrite_StopStream) { - test_stream_->shared_data_.TransitionTo(AlsaPcmOutputStream::kIsOpened); - test_stream_->shared_data_.TransitionTo(AlsaPcmOutputStream::kIsPlaying); + test_stream_->TransitionTo(AlsaPcmOutputStream::kIsOpened); + test_stream_->TransitionTo(AlsaPcmOutputStream::kIsPlaying); InitBuffer(); @@ -805,5 +797,5 @@ TEST_F(AlsaPcmOutputStreamTest, ScheduleNextWrite_StopStream) { // posted so we can verify that the Alsa code will indeed break the task // posting loop. - test_stream_->shared_data_.TransitionTo(AlsaPcmOutputStream::kIsClosed); + test_stream_->TransitionTo(AlsaPcmOutputStream::kIsClosed); } diff --git a/media/audio/linux/audio_manager_linux.cc b/media/audio/linux/audio_manager_linux.cc index 58b4c0e..c21d050 100644 --- a/media/audio/linux/audio_manager_linux.cc +++ b/media/audio/linux/audio_manager_linux.cc @@ -9,6 +9,7 @@ #include "base/logging.h" #include "base/nix/xdg_util.h" #include "base/process_util.h" +#include "base/stl_util-inl.h" #include "media/audio/audio_output_dispatcher.h" #include "media/audio/fake_audio_input_stream.h" #include "media/audio/fake_audio_output_stream.h" @@ -107,9 +108,7 @@ AudioOutputStream* AudioManagerLinux::MakeAudioOutputStream( AlsaPcmOutputStream* stream = new AlsaPcmOutputStream(device_name, params, wrapper_.get(), this, GetMessageLoop()); - - base::AutoLock l(lock_); - active_streams_[stream] = scoped_refptr<AlsaPcmOutputStream>(stream); + active_streams_.insert(stream); return stream; } @@ -153,7 +152,9 @@ AudioManagerLinux::~AudioManagerLinux() { // Free output dispatchers, closing all remaining open streams. output_dispatchers_.clear(); - active_streams_.clear(); + // Delete all the streams. Have to do it manually, we don't have ScopedSet<>, + // and we are not using ScopedVector<> because search there is slow. + STLDeleteElements(&active_streams_); } void AudioManagerLinux::Init() { @@ -171,8 +172,8 @@ void AudioManagerLinux::UnMuteAll() { void AudioManagerLinux::ReleaseOutputStream(AlsaPcmOutputStream* stream) { if (stream) { - base::AutoLock l(lock_); active_streams_.erase(stream); + delete stream; } } diff --git a/media/audio/linux/audio_manager_linux.h b/media/audio/linux/audio_manager_linux.h index 6a5d287..52d2940 100644 --- a/media/audio/linux/audio_manager_linux.h +++ b/media/audio/linux/audio_manager_linux.h @@ -5,11 +5,9 @@ #ifndef MEDIA_AUDIO_LINUX_AUDIO_MANAGER_LINUX_H_ #define MEDIA_AUDIO_LINUX_AUDIO_MANAGER_LINUX_H_ -#include <map> +#include <set> #include "base/memory/ref_counted.h" -#include "base/memory/scoped_ptr.h" -#include "base/synchronization/lock.h" #include "base/threading/thread.h" #include "media/audio/audio_manager_base.h" @@ -46,9 +44,7 @@ class AudioManagerLinux : public AudioManagerBase { scoped_ptr<AlsaWrapper> wrapper_; - base::Lock lock_; - std::map<AlsaPcmOutputStream*, scoped_refptr<AlsaPcmOutputStream> > - active_streams_; + std::set<AlsaPcmOutputStream*> active_streams_; DISALLOW_COPY_AND_ASSIGN(AudioManagerLinux); }; |