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..5643f833c3dd98f53eeb578e13e6ff4aaf9c6887 100644 |
--- a/media/audio/android/opensles_output.cc |
+++ b/media/audio/android/opensles_output.cc |
@@ -8,13 +8,13 @@ |
#include "base/logging.h" |
#include "media/audio/android/audio_manager_android.h" |
-#define LOG_ON_FAILURE_AND_RETURN(op, ...) \ |
- do { \ |
- SLresult err = (op); \ |
- if (err != SL_RESULT_SUCCESS) { \ |
+#define LOG_ON_FAILURE_AND_RETURN(op, ...) \ |
+ do { \ |
+ SLresult err = (op); \ |
+ if (err != SL_RESULT_SUCCESS) { \ |
DLOG(ERROR) << #op << " failed: " << err; \ |
- return __VA_ARGS__; \ |
- } \ |
+ return __VA_ARGS__; \ |
+ } \ |
} while (0) |
namespace media { |
@@ -25,10 +25,11 @@ OpenSLESOutputStream::OpenSLESOutputStream(AudioManagerAndroid* manager, |
callback_(NULL), |
player_(NULL), |
simple_buffer_queue_(NULL), |
- active_queue_(0), |
+ active_buffer_index_(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_index_ = 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,48 @@ 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,70 +168,61 @@ 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[] = { |
- { SL_ENGINEOPTION_THREADSAFE, static_cast<SLuint32>(SL_BOOLEAN_TRUE) } |
- }; |
+ {SL_ENGINEOPTION_THREADSAFE, static_cast<SLuint32>(SL_BOOLEAN_TRUE)}}; |
LOG_ON_FAILURE_AND_RETURN( |
slCreateEngine(engine_object_.Receive(), 1, option, 0, NULL, NULL), |
false); |
// Realize the SL engine object in synchronous mode. |
LOG_ON_FAILURE_AND_RETURN( |
- engine_object_->Realize(engine_object_.Get(), SL_BOOLEAN_FALSE), |
- false); |
+ engine_object_->Realize(engine_object_.Get(), SL_BOOLEAN_FALSE), false); |
// Get the SL engine interface which is implicit. |
SLEngineItf engine; |
- LOG_ON_FAILURE_AND_RETURN( |
- engine_object_->GetInterface(engine_object_.Get(), |
- SL_IID_ENGINE, |
- &engine), |
- false); |
+ LOG_ON_FAILURE_AND_RETURN(engine_object_->GetInterface( |
+ engine_object_.Get(), SL_IID_ENGINE, &engine), |
+ false); |
// Create ouput mixer object to be used by the player. |
- LOG_ON_FAILURE_AND_RETURN( |
- (*engine)->CreateOutputMix(engine, |
- output_mixer_.Receive(), |
- 0, |
- NULL, |
- NULL), |
- false); |
+ LOG_ON_FAILURE_AND_RETURN((*engine)->CreateOutputMix( |
+ engine, output_mixer_.Receive(), 0, NULL, NULL), |
+ false); |
// Realizing the output mix object in synchronous mode. |
LOG_ON_FAILURE_AND_RETURN( |
- output_mixer_->Realize(output_mixer_.Get(), SL_BOOLEAN_FALSE), |
- false); |
+ output_mixer_->Realize(output_mixer_.Get(), SL_BOOLEAN_FALSE), false); |
// Audio source configuration. |
SLDataLocator_AndroidSimpleBufferQueue simple_buffer_queue = { |
- SL_DATALOCATOR_ANDROIDSIMPLEBUFFERQUEUE, |
- static_cast<SLuint32>(kNumOfQueuesInBuffer) |
- }; |
- SLDataSource audio_source = { &simple_buffer_queue, &format_ }; |
+ SL_DATALOCATOR_ANDROIDSIMPLEBUFFERQUEUE, |
+ static_cast<SLuint32>(kMaxNumOfBuffersInQueue)}; |
+ SLDataSource audio_source = {&simple_buffer_queue, &format_}; |
// Audio sink configuration. |
- SLDataLocator_OutputMix locator_output_mix = { |
- SL_DATALOCATOR_OUTPUTMIX, output_mixer_.Get() |
- }; |
- SLDataSink audio_sink = { &locator_output_mix, NULL }; |
+ SLDataLocator_OutputMix locator_output_mix = {SL_DATALOCATOR_OUTPUTMIX, |
+ output_mixer_.Get()}; |
+ SLDataSink audio_sink = {&locator_output_mix, NULL}; |
// Create an audio player. |
- const SLInterfaceID interface_id[] = { |
- SL_IID_BUFFERQUEUE, |
- SL_IID_VOLUME, |
- SL_IID_ANDROIDCONFIGURATION |
- }; |
- const SLboolean interface_required[] = { |
- SL_BOOLEAN_TRUE, |
- SL_BOOLEAN_TRUE, |
- SL_BOOLEAN_TRUE |
- }; |
+ const SLInterfaceID interface_id[] = {SL_IID_BUFFERQUEUE, SL_IID_VOLUME, |
+ SL_IID_ANDROIDCONFIGURATION}; |
+ const SLboolean interface_required[] = {SL_BOOLEAN_TRUE, SL_BOOLEAN_TRUE, |
+ SL_BOOLEAN_TRUE}; |
LOG_ON_FAILURE_AND_RETURN( |
(*engine)->CreateAudioPlayer(engine, |
player_object_.Receive(), |
@@ -209,22 +236,21 @@ bool OpenSLESOutputStream::CreatePlayer() { |
// Create AudioPlayer and specify SL_IID_ANDROIDCONFIGURATION. |
SLAndroidConfigurationItf player_config; |
LOG_ON_FAILURE_AND_RETURN( |
- player_object_->GetInterface(player_object_.Get(), |
- SL_IID_ANDROIDCONFIGURATION, |
- &player_config), |
+ player_object_->GetInterface( |
+ player_object_.Get(), SL_IID_ANDROIDCONFIGURATION, &player_config), |
false); |
SLint32 stream_type = SL_ANDROID_STREAM_VOICE; |
LOG_ON_FAILURE_AND_RETURN( |
(*player_config)->SetConfiguration(player_config, |
SL_ANDROID_KEY_STREAM_TYPE, |
- &stream_type, sizeof(SLint32)), |
+ &stream_type, |
+ sizeof(SLint32)), |
false); |
// Realize the player object in synchronous mode. |
LOG_ON_FAILURE_AND_RETURN( |
- player_object_->Realize(player_object_.Get(), SL_BOOLEAN_FALSE), |
- false); |
+ player_object_->Realize(player_object_.Get(), SL_BOOLEAN_FALSE), false); |
// Get an implicit player interface. |
LOG_ON_FAILURE_AND_RETURN( |
@@ -233,72 +259,104 @@ bool OpenSLESOutputStream::CreatePlayer() { |
// Get the simple buffer queue interface. |
LOG_ON_FAILURE_AND_RETURN( |
- player_object_->GetInterface(player_object_.Get(), |
- SL_IID_BUFFERQUEUE, |
- &simple_buffer_queue_), |
+ player_object_->GetInterface( |
+ player_object_.Get(), SL_IID_BUFFERQUEUE, &simple_buffer_queue_), |
false); |
// Register the input callback for the simple buffer queue. |
// This callback will be called when the soundcard needs data. |
LOG_ON_FAILURE_AND_RETURN( |
- (*simple_buffer_queue_)->RegisterCallback(simple_buffer_queue_, |
- SimpleBufferQueueCallback, |
- this), |
+ (*simple_buffer_queue_)->RegisterCallback( |
+ simple_buffer_queue_, SimpleBufferQueueCallback, this), |
false); |
return true; |
} |
void OpenSLESOutputStream::SimpleBufferQueueCallback( |
- SLAndroidSimpleBufferQueueItf buffer_queue, void* instance) { |
+ SLAndroidSimpleBufferQueueItf buffer_queue, |
+ void* instance) { |
OpenSLESOutputStream* stream = |
reinterpret_cast<OpenSLESOutputStream*>(instance); |
stream->FillBufferQueue(); |
} |
void OpenSLESOutputStream::FillBufferQueue() { |
+ base::AutoLock lock(lock_); |
if (!started_) |
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 = |
+ 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_buffer_index_]); |
+ |
+ 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_); |
- // 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. |
- audio_bus_->Scale(volume_); |
- audio_bus_->ToInterleaved( |
- frames_filled, format_.bitsPerSample / 8, audio_data_[active_queue_]); |
// Enqueue the buffer for playback. |
- SLresult err = (*simple_buffer_queue_)->Enqueue( |
- simple_buffer_queue_, |
- audio_data_[active_queue_], |
- num_filled_bytes); |
+ SLresult err = |
+ (*simple_buffer_queue_)->Enqueue(simple_buffer_queue_, |
+ audio_data_[active_buffer_index_], |
+ num_filled_bytes); |
if (SL_RESULT_SUCCESS != err) |
HandleError(err); |
- active_queue_ = (active_queue_ + 1) % kNumOfQueuesInBuffer; |
+ active_buffer_index_ = (active_buffer_index_ + 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) { |
- delete [] audio_data_[i]; |
+ for (int i = 0; i < kMaxNumOfBuffersInQueue; ++i) { |
+ delete[] audio_data_[i]; |
audio_data_[i] = NULL; |
} |
} |