Chromium Code Reviews| 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..ac494f2e25092b6a0ba1f413cb53eb5a7b1a6c58 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() { |
| - if (!started_) |
| + base::AutoLock lock(lock_); |
| + if (!player_) |
|
wjia(left Chromium)
2013/09/07 17:09:36
|started_| is a better flag used here than |player
henrika (OOO until Aug 14)
2013/09/10 11:17:18
Done.
|
| 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; |
| } |
| } |