Index: media/audio/android/opensles_output.cc |
diff --git a/media/audio/android/opensles_output.cc b/media/audio/android/opensles_output.cc |
index c6d455715d9afccefe848b1d98c18d44934aa4ec..181550bdf90b419e3909d21927172dbf33d91f7b 100644 |
--- a/media/audio/android/opensles_output.cc |
+++ b/media/audio/android/opensles_output.cc |
@@ -25,10 +25,11 @@ OpenSLESOutputStream::OpenSLESOutputStream(AudioManagerAndroid* manager, |
callback_(NULL), |
player_(NULL), |
simple_buffer_queue_(NULL), |
- active_queue_(0), |
+ active_buffer_(0), |
buffer_size_bytes_(0), |
started_(false), |
volume_(1.0) { |
+ DVLOG(2) << "OpenSLESOutputStream::OpenSLESOutputStream()"; |
format_.formatType = SL_DATAFORMAT_PCM; |
format_.numChannels = static_cast<SLuint32>(params.channels()); |
// Provides sampling rate in milliHertz to OpenSLES. |
@@ -50,6 +51,8 @@ OpenSLESOutputStream::OpenSLESOutputStream(AudioManagerAndroid* manager, |
} |
OpenSLESOutputStream::~OpenSLESOutputStream() { |
+ DVLOG(2) << "OpenSLESOutputStream::~OpenSLESOutputStream()"; |
+ DCHECK(thread_checker_.CalledOnValidThread()); |
DCHECK(!engine_object_.Get()); |
DCHECK(!player_object_.Get()); |
DCHECK(!output_mixer_.Get()); |
@@ -59,6 +62,8 @@ OpenSLESOutputStream::~OpenSLESOutputStream() { |
} |
bool OpenSLESOutputStream::Open() { |
+ DVLOG(2) << "OpenSLESOutputStream::Open()"; |
+ DCHECK(thread_checker_.CalledOnValidThread()); |
if (engine_object_.Get()) |
return false; |
@@ -66,37 +71,46 @@ bool OpenSLESOutputStream::Open() { |
return false; |
SetupAudioBuffer(); |
+ active_buffer_ = 0; |
return true; |
} |
void OpenSLESOutputStream::Start(AudioSourceCallback* callback) { |
+ DVLOG(2) << "OpenSLESOutputStream::Start()"; |
+ DCHECK(thread_checker_.CalledOnValidThread()); |
DCHECK(callback); |
DCHECK(player_); |
DCHECK(simple_buffer_queue_); |
if (started_) |
return; |
- // Enable the flags before streaming. |
+ base::AutoLock lock(lock_); |
+ DCHECK(callback_ == NULL || callback_ == callback); |
callback_ = callback; |
- active_queue_ = 0; |
- started_ = true; |
// Avoid start-up glitches by filling up one buffer queue before starting |
// the stream. |
- FillBufferQueue(); |
+ FillBufferQueueNoLock(); |
- // Start streaming data by setting the play state to |SL_PLAYSTATE_PLAYING|. |
+ // Start streaming data by setting the play state to SL_PLAYSTATE_PLAYING. |
+ // For a player object, when the object is in the SL_PLAYSTATE_PLAYING |
+ // state, adding buffers will implicitly start playback. |
LOG_ON_FAILURE_AND_RETURN( |
(*player_)->SetPlayState(player_, SL_PLAYSTATE_PLAYING)); |
+ |
+ started_ = true; |
} |
void OpenSLESOutputStream::Stop() { |
+ DVLOG(2) << "OpenSLESOutputStream::Stop()"; |
+ DCHECK(thread_checker_.CalledOnValidThread()); |
if (!started_) |
return; |
- started_ = false; |
- // Stop playing by setting the play state to |SL_PLAYSTATE_STOPPED|. |
+ base::AutoLock lock(lock_); |
+ |
+ // Stop playing by setting the play state to SL_PLAYSTATE_STOPPED. |
LOG_ON_FAILURE_AND_RETURN( |
(*player_)->SetPlayState(player_, SL_PLAYSTATE_STOPPED)); |
@@ -104,26 +118,49 @@ void OpenSLESOutputStream::Stop() { |
// resuming playing. |
LOG_ON_FAILURE_AND_RETURN( |
(*simple_buffer_queue_)->Clear(simple_buffer_queue_)); |
+ |
+#ifndef NDEBUG |
+ // Verify that the buffer queue is in fact cleared as it should. |
+ SLAndroidSimpleBufferQueueState buffer_queue_state; |
+ LOG_ON_FAILURE_AND_RETURN( |
+ (*simple_buffer_queue_)->GetState(simple_buffer_queue_, |
+ &buffer_queue_state)); |
+ DCHECK_EQ(0u, buffer_queue_state.count); |
+ DCHECK_EQ(0u, buffer_queue_state.index); |
+#endif |
+ |
+ started_ = false; |
} |
void OpenSLESOutputStream::Close() { |
+ DVLOG(2) << "OpenSLESOutputStream::Close()"; |
+ DCHECK(thread_checker_.CalledOnValidThread()); |
+ |
// Stop the stream if it is still playing. |
Stop(); |
- |
- // Explicitly free the player objects and invalidate their associated |
- // interfaces. They have to be done in the correct order. |
- player_object_.Reset(); |
- output_mixer_.Reset(); |
- engine_object_.Reset(); |
- simple_buffer_queue_ = NULL; |
- player_ = NULL; |
- |
- ReleaseAudioBuffer(); |
+ { |
+ // Destroy the buffer queue player object and invalidate all associated |
+ // interfaces. |
+ player_object_.Reset(); |
+ simple_buffer_queue_ = NULL; |
+ player_ = NULL; |
+ |
+ // Destroy the mixer object. We don't store any associated interface for |
+ // this object. |
+ output_mixer_.Reset(); |
+ |
+ // Destroy the engine object. We don't store any associated interface for |
+ // this object. |
+ engine_object_.Reset(); |
+ ReleaseAudioBuffer(); |
+ } |
audio_manager_->ReleaseOutputStream(this); |
} |
void OpenSLESOutputStream::SetVolume(double volume) { |
+ DVLOG(2) << "OpenSLESOutputStream::SetVolume(" << volume << ")"; |
+ DCHECK(thread_checker_.CalledOnValidThread()); |
float volume_float = static_cast<float>(volume); |
if (volume_float < 0.0f || volume_float > 1.0f) { |
return; |
@@ -132,10 +169,18 @@ void OpenSLESOutputStream::SetVolume(double volume) { |
} |
void OpenSLESOutputStream::GetVolume(double* volume) { |
+ DCHECK(thread_checker_.CalledOnValidThread()); |
*volume = static_cast<double>(volume_); |
} |
bool OpenSLESOutputStream::CreatePlayer() { |
+ DCHECK(thread_checker_.CalledOnValidThread()); |
+ DCHECK(!engine_object_.Get()); |
+ DCHECK(!player_object_.Get()); |
+ DCHECK(!output_mixer_.Get()); |
+ DCHECK(!player_); |
+ DCHECK(!simple_buffer_queue_); |
+ |
// Initializes the engine object with specific option. After working with the |
// object, we need to free the object and its resources. |
SLEngineOption option[] = { |
@@ -175,7 +220,7 @@ bool OpenSLESOutputStream::CreatePlayer() { |
// Audio source configuration. |
SLDataLocator_AndroidSimpleBufferQueue simple_buffer_queue = { |
SL_DATALOCATOR_ANDROIDSIMPLEBUFFERQUEUE, |
- static_cast<SLuint32>(kNumOfQueuesInBuffer) |
+ static_cast<SLuint32>(kMaxNumOfBuffersInQueue) |
}; |
SLDataSource audio_source = { &simple_buffer_queue, &format_ }; |
@@ -257,47 +302,79 @@ void OpenSLESOutputStream::SimpleBufferQueueCallback( |
} |
void OpenSLESOutputStream::FillBufferQueue() { |
- if (!started_) |
+ base::AutoLock lock(lock_); |
+ if (!player_) |
return; |
TRACE_EVENT0("audio", "OpenSLESOutputStream::FillBufferQueue"); |
+ |
+ // Verify that we are in a playing state. |
+ SLuint32 state; |
+ SLresult err = (*player_)->GetPlayState(player_, &state); |
+ if (SL_RESULT_SUCCESS != err) { |
+ HandleError(err); |
+ return; |
+ } |
+ if (state != SL_PLAYSTATE_PLAYING) { |
+ DLOG(WARNING) << "Received callback in non-playing state"; |
+ return; |
+ } |
+ |
+ // Fill up one buffer in the queue by asking the registered source for |
+ // data using the OnMoreData() callback. |
+ FillBufferQueueNoLock(); |
+} |
+ |
+void OpenSLESOutputStream::FillBufferQueueNoLock() { |
+ // Ensure that the calling thread has acquired the lock since it is not |
+ // done in this method. |
+ lock_.AssertAcquired(); |
+ |
// Read data from the registered client source. |
- // TODO(xians): Get an accurate delay estimation. |
- uint32 hardware_delay = buffer_size_bytes_; |
+ // TODO(henrika): Investigate if it is possible to get a more accurate |
+ // delay estimation. |
+ const uint32 hardware_delay = buffer_size_bytes_; |
int frames_filled = callback_->OnMoreData( |
audio_bus_.get(), AudioBuffersState(0, hardware_delay)); |
- if (frames_filled <= 0) |
- return; // Audio source is shutting down, or halted on error. |
- int num_filled_bytes = |
- frames_filled * audio_bus_->channels() * format_.bitsPerSample / 8; |
- DCHECK_LE(static_cast<size_t>(num_filled_bytes), buffer_size_bytes_); |
- // Note: If this ever changes to output raw float the data must be clipped and |
- // sanitized since it may come from an untrusted source such as NaCl. |
+ if (frames_filled <= 0) { |
+ // Audio source is shutting down, or halted on error. |
+ return; |
+ } |
+ |
+ // Note: If the internal representation ever changes from 16-bit PCM to |
+ // raw float, the data must be clipped and sanitized since it may come |
+ // from an untrusted source such as NaCl. |
audio_bus_->Scale(volume_); |
audio_bus_->ToInterleaved( |
- frames_filled, format_.bitsPerSample / 8, audio_data_[active_queue_]); |
+ frames_filled, format_.bitsPerSample / 8, audio_data_[active_buffer_]); |
+ |
+ const int num_filled_bytes = |
+ frames_filled * audio_bus_->channels() * format_.bitsPerSample / 8; |
+ DCHECK_LE(static_cast<size_t>(num_filled_bytes), buffer_size_bytes_); |
// Enqueue the buffer for playback. |
SLresult err = (*simple_buffer_queue_)->Enqueue( |
simple_buffer_queue_, |
- audio_data_[active_queue_], |
+ audio_data_[active_buffer_], |
num_filled_bytes); |
if (SL_RESULT_SUCCESS != err) |
HandleError(err); |
- active_queue_ = (active_queue_ + 1) % kNumOfQueuesInBuffer; |
+ active_buffer_ = (active_buffer_ + 1) % kMaxNumOfBuffersInQueue; |
} |
void OpenSLESOutputStream::SetupAudioBuffer() { |
+ DCHECK(thread_checker_.CalledOnValidThread()); |
DCHECK(!audio_data_[0]); |
- for (int i = 0; i < kNumOfQueuesInBuffer; ++i) { |
+ for (int i = 0; i < kMaxNumOfBuffersInQueue; ++i) { |
audio_data_[i] = new uint8[buffer_size_bytes_]; |
} |
} |
void OpenSLESOutputStream::ReleaseAudioBuffer() { |
+ DCHECK(thread_checker_.CalledOnValidThread()); |
if (audio_data_[0]) { |
- for (int i = 0; i < kNumOfQueuesInBuffer; ++i) { |
+ for (int i = 0; i < kMaxNumOfBuffersInQueue; ++i) { |
delete [] audio_data_[i]; |
audio_data_[i] = NULL; |
} |