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

Unified Diff: media/audio/android/opensles_output.cc

Issue 23296008: Adding audio unit tests for Android (Closed) Base URL: http://git.chromium.org/chromium/src.git@master
Patch Set: wjia@ Created 7 years, 3 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/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;
}
}

Powered by Google App Engine
This is Rietveld 408576698