Chromium Code Reviews
chromiumcodereview-hr@appspot.gserviceaccount.com (chromiumcodereview-hr) | Please choose your nickname with Settings | Help | Chromium Project | Gerrit Changes | Sign out
(1340)

Unified Diff: media/audio/linux/alsa_output.cc

Issue 7117001: Simplify AlsaPcmOutputStream and AudioManagerLinux. Code was thread-safe, but now (Closed) Base URL: http://src.chromium.org/svn/trunk/src/
Patch Set: Created 9 years, 6 months ago
Use n/p to move between diff chunks; N/P to move between comments. Draft comments are only viewable by you.
Jump to:
View side-by-side diff with in-line comments
Download patch
Index: media/audio/linux/alsa_output.cc
===================================================================
--- media/audio/linux/alsa_output.cc (revision 87982)
+++ media/audio/linux/alsa_output.cc (working copy)
@@ -4,43 +4,14 @@
//
// 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 alsa device.
+// is actually the same thread.
//
-// 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_|.
-//
-//
// SEMANTICS OF CloseTask()
//
// The CloseTask() is responsible for cleaning up any resources that were
@@ -49,20 +20,17 @@
// 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.
+// loop.
//
//
// 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.
//
-// 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 +186,7 @@
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 +205,36 @@
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 +243,14 @@
}
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,26 +258,26 @@
// 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));
+ method_factory_.NewRunnableMethod(&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
@@ -313,39 +286,39 @@
}
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));
+ set_volume(static_cast<float>(volume));
}
-void AlsaPcmOutputStream::GetVolume(double* volume) {
- DCHECK_EQ(MessageLoop::current(), client_thread_loop_);
+void AlsaPcmOutputStream::GetVolume(double* pointer_to_volume) {
+ DCHECK_EQ(MessageLoop::current(), message_loop_);
- *volume = shared_data_.volume();
+ *pointer_to_volume = volume();
}
void AlsaPcmOutputStream::OpenTask() {
@@ -396,7 +369,7 @@
return;
}
- if (shared_data_.state() != kIsPlaying) {
+ if (state() != kIsPlaying) {
return;
}
@@ -471,9 +444,8 @@
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));
+ OnMoreData(this, packet->GetWritableData(), packet->GetBufferSize(),
+ AudioBuffersState(buffer_delay, hardware_delay));
CHECK(packet_size <= packet->GetBufferSize()) <<
"Data source overran buffer.";
@@ -489,7 +461,7 @@
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 +497,7 @@
packet_size,
channels_,
bytes_per_sample_,
- shared_data_.volume());
+ volume());
}
if (packet_size > 0) {
@@ -547,7 +519,7 @@
return;
}
- if (shared_data_.state() == kIsStopped) {
+ if (state() == kIsStopped) {
return;
}
@@ -579,7 +551,7 @@
if (frames_written != -EAGAIN) {
LOG(ERROR) << "Failed to write to pcm device: "
<< wrapper_->StrError(frames_written);
- shared_data_.OnError(this, frames_written);
+ OnError(this, frames_written);
stop_stream_ = true;
}
} else {
@@ -610,7 +582,7 @@
return;
}
- if (shared_data_.state() == kIsStopped) {
+ if (state() == kIsStopped) {
return;
}
@@ -655,17 +627,17 @@
}
// 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);
}
}
@@ -843,27 +815,11 @@
}
AudioManagerLinux* AlsaPcmOutputStream::manager() {
scherkus (not reviewing) 2011/06/07 00:19:01 inline this method as it no longer needs to be spl
enal 2011/06/07 17:29:00 Done.
- DCHECK_EQ(MessageLoop::current(), client_thread_loop_);
+ CHECK_EQ(MessageLoop::current(), message_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 +846,10 @@
}
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,25 +858,22 @@
return state_;
}
-AlsaPcmOutputStream::InternalState AlsaPcmOutputStream::SharedData::state() {
- base::AutoLock l(lock_);
+AlsaPcmOutputStream::InternalState AlsaPcmOutputStream::state() {
scherkus (not reviewing) 2011/06/07 00:19:01 I'd drop this method as it's private
enal 2011/06/07 17:29:00 It is used in the unit tests, I'd prefer tests to
return state_;
}
-float AlsaPcmOutputStream::SharedData::volume() {
- base::AutoLock l(lock_);
+float AlsaPcmOutputStream::volume() {
scherkus (not reviewing) 2011/06/07 00:19:01 I'd drop this method as it's private
enal 2011/06/07 17:29:00 Done.
return volume_;
}
-void AlsaPcmOutputStream::SharedData::set_volume(float v) {
- base::AutoLock l(lock_);
+void AlsaPcmOutputStream::set_volume(float v) {
scherkus (not reviewing) 2011/06/07 00:19:01 I'd drop this method as it's private
enal 2011/06/07 17:29:00 Done.
volume_ = v;
}
-uint32 AlsaPcmOutputStream::SharedData::OnMoreData(
- AudioOutputStream* stream, uint8* dest, uint32 max_size,
- AudioBuffersState buffers_state) {
- base::AutoLock l(lock_);
+uint32 AlsaPcmOutputStream::OnMoreData(AudioOutputStream* stream,
scherkus (not reviewing) 2011/06/07 00:19:01 it looks like we pass "this" for the stream parame
enal 2011/06/07 17:29:00 Name and arguments are defined in the parent class
scherkus (not reviewing) 2011/06/07 23:01:59 the abstract virtual function is actually from Aud
+ uint8* dest,
+ uint32 max_size,
+ AudioBuffersState buffers_state) {
if (source_callback_) {
return source_callback_->OnMoreData(stream, dest, max_size, buffers_state);
}
@@ -929,9 +881,7 @@
return 0;
}
-void AlsaPcmOutputStream::SharedData::OnError(AudioOutputStream* stream,
- int code) {
- base::AutoLock l(lock_);
+void AlsaPcmOutputStream::OnError(AudioOutputStream* stream, int code) {
scherkus (not reviewing) 2011/06/07 00:19:01 ditto, maybe RunErrorCallback()
enal 2011/06/07 17:29:00 Name and arguments are defined in the parent class
scherkus (not reviewing) 2011/06/07 23:01:59 Ditto
if (source_callback_) {
source_callback_->OnError(stream, code);
}
@@ -939,9 +889,7 @@
// 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) {
scherkus (not reviewing) 2011/06/07 00:19:01 I'd drop this method as it's private
enal 2011/06/07 17:29:00 Called in the unit tests.
+ DCHECK_EQ(MessageLoop::current(), message_loop_);
source_callback_ = callback;
}

Powered by Google App Engine
This is Rietveld 408576698