| 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;
|
| }
|
| }
|
|
|