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

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

Issue 9655023: Adding input and output audio backend to Android. (Closed) Base URL: svn://svn.chromium.org/chrome/trunk/src
Patch Set: rebased && addressed qinmin's comments Created 8 years, 9 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
new file mode 100644
index 0000000000000000000000000000000000000000..4f17dfab1f7c25823437240b112ce9e4227ec209
--- /dev/null
+++ b/media/audio/android/opensles_output.cc
@@ -0,0 +1,277 @@
+// Copyright (c) 2012 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "media/audio/android/opensles_output.h"
+
+#include "base/logging.h"
+#include "media/audio/audio_util.h"
+#include "media/audio/android/audio_manager_android.h"
+
+OpenSLESOutputStream::OpenSLESOutputStream(AudioManagerAndroid* manager,
+ const AudioParameters& params)
+ : audio_manager_(manager),
+ callback_(NULL),
+ player_(NULL),
+ simple_buffer_queue_(NULL),
+ active_queue_(0),
+ buffer_size_bytes_(0),
+ started_(false),
+ volume_(1.0) {
+ format_.formatType = SL_DATAFORMAT_PCM;
+ format_.numChannels = static_cast<SLuint32>(params.channels());
+ // Provides sampling rate in milliHertz to OpenSLES.
+ format_.samplesPerSec = static_cast<SLuint32>(params.sample_rate() * 1000);
+ format_.bitsPerSample = params.bits_per_sample();
+ format_.containerSize = params.bits_per_sample();
+ format_.channelMask = SL_SPEAKER_FRONT_CENTER;
+ format_.endianness = SL_BYTEORDER_LITTLEENDIAN;
+
+ buffer_size_bytes_ = params.GetBytesPerBuffer();
+
+ for (int i = 0; i < kNumOfQueuesInBuffer; ++i) {
+ audio_data_[i] = new uint8[buffer_size_bytes_];
+ }
+}
+
+OpenSLESOutputStream::~OpenSLESOutputStream() {
+ DCHECK(!engine_object_.Get());
+ DCHECK(!player_object_.Get());
+ DCHECK(!output_mixer_.Get());
+ DCHECK(!player_);
+ DCHECK(!simple_buffer_queue_);
+ for (int i = 0; i < kNumOfQueuesInBuffer; ++i)
+ delete [] audio_data_[i];
+}
+
+bool OpenSLESOutputStream::Open() {
+ if (engine_object_.Get())
+ return false;
+
+ return CreatePlayer();
+}
+
+void OpenSLESOutputStream::Start(AudioSourceCallback* callback) {
+ DCHECK(callback);
+ DCHECK(player_);
+ DCHECK(simple_buffer_queue_);
+ if (started_)
+ return;
+
+ // Enable the flags before streaming.
+ callback_ = callback;
+ active_queue_ = 0;
+ started_ = true;
+
+ // Avoid start-up glitches by filling up one buffer queue before starting
+ // the stream.
+ FillBufferQueue();
+
+ // Start streaming data by setting the play state to |SL_PLAYSTATE_PLAYING|.
+ SLresult err = (*player_)->SetPlayState(player_, SL_PLAYSTATE_PLAYING);
+ DCHECK_EQ(SL_RESULT_SUCCESS, err);
+ if (SL_RESULT_SUCCESS != err) {
+ DLOG(WARNING) << "SetPlayState() failed to start playing";
+ }
+}
+
+void OpenSLESOutputStream::Stop() {
+ if (!started_)
+ return;
+
+ started_ = false;
+ // Stop playing by setting the play state to |SL_PLAYSTATE_STOPPED|.
+ SLresult err = (*player_)->SetPlayState(player_, SL_PLAYSTATE_STOPPED);
+ DLOG_IF(WARNING, SL_RESULT_SUCCESS != err) << "SetPlayState() failed to "
+ << "set the state to stop";
+
+
+ // Clear the buffer queue so that the old data won't be played when
+ // resuming playing.
+ err = (*simple_buffer_queue_)->Clear(simple_buffer_queue_);
+ DLOG_IF(WARNING, SL_RESULT_SUCCESS != err) << "Clear() failed to "
+ << "clear the buffer queue";
+}
+
+void OpenSLESOutputStream::Close() {
+ // 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.
+ output_mixer_.Reset();
+ player_object_.Reset();
+ engine_object_.Reset();
+ simple_buffer_queue_ = NULL;
+ player_ = NULL;
+
+ audio_manager_->ReleaseOutputStream(this);
+}
+
+void OpenSLESOutputStream::SetVolume(double volume) {
+ float volume_float = static_cast<float>(volume);
+ if (volume_float < 0.0f || volume_float > 1.0f) {
+ return;
+ }
+ volume_ = volume_float;
+}
+
+void OpenSLESOutputStream::GetVolume(double* volume) {
+ *volume = static_cast<double>(volume_);
+}
+
+bool OpenSLESOutputStream::CreatePlayer() {
+ // 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) }
+ };
+ SLresult err = slCreateEngine(engine_object_.Receive(), 1, option, 0,
+ NULL, NULL);
+ DCHECK_EQ(SL_RESULT_SUCCESS, err);
+ if (SL_RESULT_SUCCESS != err)
+ return false;
+
+ // Realize the SL engine object in synchronous mode.
+ err = engine_object_->Realize(engine_object_.Get(), SL_BOOLEAN_FALSE);
+ DCHECK_EQ(SL_RESULT_SUCCESS, err);
+ if (SL_RESULT_SUCCESS != err)
+ return false;
+
+ // Get the SL engine interface which is implicit.
+ SLEngineItf engine;
+ err = engine_object_->GetInterface(engine_object_.Get(),
+ SL_IID_ENGINE,
+ &engine);
+ DCHECK_EQ(SL_RESULT_SUCCESS, err);
+ if (SL_RESULT_SUCCESS != err)
+ return false;
+
+ // Create ouput mixer object to be used by the player.
+ // TODO(xians): Do we need the environmental reverb auxiliary effect?
+ err = (*engine)->CreateOutputMix(engine,
+ output_mixer_.Receive(),
+ 0,
+ NULL,
+ NULL);
+ DCHECK_EQ(SL_RESULT_SUCCESS, err);
+ if (SL_RESULT_SUCCESS != err)
+ return false;
+
+ // Realizing the output mix object in synchronous mode.
+ err = output_mixer_->Realize(output_mixer_.Get(), SL_BOOLEAN_FALSE);
+ DCHECK_EQ(SL_RESULT_SUCCESS, err);
+ if (SL_RESULT_SUCCESS != err)
+ return false;
+
+ // Audio source configuration.
+ SLDataLocator_AndroidSimpleBufferQueue simple_buffer_queue = {
+ SL_DATALOCATOR_ANDROIDSIMPLEBUFFERQUEUE,
+ static_cast<SLuint32>(kNumOfQueuesInBuffer)
+ };
+ 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 };
+
+ // Create an audio player.
+ const SLuint32 number_of_interfaces = 1;
+ const SLInterfaceID interface_id[number_of_interfaces] = {
+ SL_IID_BUFFERQUEUE
+ };
+ const SLboolean interface_required[number_of_interfaces] = {
+ SL_BOOLEAN_TRUE
+ };
+ err = (*engine)->CreateAudioPlayer(engine,
+ player_object_.Receive(),
+ &audio_source,
+ &audio_sink,
+ number_of_interfaces,
+ interface_id,
+ interface_required);
+ DCHECK_EQ(SL_RESULT_SUCCESS, err);
+ if (SL_RESULT_SUCCESS != err) {
+ DLOG(ERROR) << "CreateAudioPlayer() failed with error code " << err;
+ return false;
+ }
+
+ // Realize the player object in synchronous mode.
+ err = player_object_->Realize(player_object_.Get(), SL_BOOLEAN_FALSE);
+ DCHECK_EQ(SL_RESULT_SUCCESS, err);
+ if (SL_RESULT_SUCCESS != err) {
+ DLOG(ERROR) << "Player Realize() failed with error code " << err;
+ return false;
+ }
+
+ // Get an implicit player interface.
+ err = player_object_->GetInterface(
+ player_object_.Get(), SL_IID_PLAY, &player_);
+ DCHECK_EQ(SL_RESULT_SUCCESS, err);
+ if (SL_RESULT_SUCCESS != err)
+ return false;
+
+ // Get the simple buffer queue interface.
+ err = player_object_->GetInterface(player_object_.Get(),
+ SL_IID_BUFFERQUEUE,
+ &simple_buffer_queue_);
+ DCHECK_EQ(SL_RESULT_SUCCESS, err);
+ if (SL_RESULT_SUCCESS != err)
+ return false;
+
+ // Register the input callback for the simple buffer queue.
+ // This callback will be called when the soundcard needs data.
+ err = (*simple_buffer_queue_)->RegisterCallback(simple_buffer_queue_,
+ SimpleBufferQueueCallback,
+ this);
+ DCHECK_EQ(SL_RESULT_SUCCESS, err);
+
+ return (SL_RESULT_SUCCESS == err);
+}
+
+void OpenSLESOutputStream::SimpleBufferQueueCallback(
+ SLAndroidSimpleBufferQueueItf buffer_queue, void* instance) {
+ OpenSLESOutputStream* stream =
+ reinterpret_cast<OpenSLESOutputStream*>(instance);
+ stream->FillBufferQueue();
+}
+
+void OpenSLESOutputStream::FillBufferQueue() {
+ if (!started_)
+ return;
+
+ // Read data from the registered client source.
+ // TODO(xians): Get an accurate delay estimation.
+ uint32 hardware_delay = buffer_size_bytes_;
+ size_t num_filled_bytes = callback_->OnMoreData(
+ this,
+ audio_data_[active_queue_],
+ buffer_size_bytes_,
+ AudioBuffersState(0, hardware_delay));
+ DCHECK(num_filled_bytes <= buffer_size_bytes_);
+
+ // Perform in-place, software-volume adjustments.
+ media::AdjustVolume(audio_data_[active_queue_],
+ num_filled_bytes,
+ format_.numChannels,
+ format_.containerSize >> 3,
+ volume_);
+
+ // Enqueue the buffer for playback.
+ SLresult err = (*simple_buffer_queue_)->Enqueue(
+ simple_buffer_queue_,
+ audio_data_[active_queue_],
+ num_filled_bytes);
+ if (SL_RESULT_SUCCESS != err)
+ HandleError(err);
+
+ active_queue_ = ++active_queue_ % kNumOfQueuesInBuffer;
+}
+
+void OpenSLESOutputStream::HandleError(SLresult error) {
+ DLOG(FATAL) << "OpenSLES error " << error;
+ if (callback_)
+ callback_->OnError(this, error);
+}

Powered by Google App Engine
This is Rietveld 408576698