| // Copyright 2016 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 "remoting/client/audio_player_android.h" |
| |
| #include "base/logging.h" |
| |
| namespace remoting { |
| |
| const int kFrameSizeMs = 40; |
| const int kNumOfBuffers = 1; |
| |
| static_assert(AudioPlayer::kChannels == 2, |
| "AudioPlayer must be feeding 2 channels data."); |
| const int kChannelMask = SL_SPEAKER_FRONT_LEFT | SL_SPEAKER_FRONT_RIGHT; |
| |
| AudioPlayerAndroid::AudioPlayerAndroid() : weak_factory_(this) { |
| if (slCreateEngine(&engine_object_, 0, nullptr, 0, nullptr, nullptr) != |
| SL_RESULT_SUCCESS || |
| (*engine_object_)->Realize(engine_object_, SL_BOOLEAN_FALSE) != |
| SL_RESULT_SUCCESS || |
| (*engine_object_) |
| ->GetInterface(engine_object_, SL_IID_ENGINE, &engine_) != |
| SL_RESULT_SUCCESS || |
| (*engine_)->CreateOutputMix(engine_, &output_mix_object_, 0, nullptr, |
| nullptr) != SL_RESULT_SUCCESS || |
| (*output_mix_object_)->Realize(output_mix_object_, SL_BOOLEAN_FALSE) != |
| SL_RESULT_SUCCESS) { |
| LOG(ERROR) << "Failed to initialize OpenSL ES."; |
| } |
| } |
| |
| AudioPlayerAndroid::~AudioPlayerAndroid() { |
| DestroyPlayer(); |
| if (output_mix_object_) { |
| (*output_mix_object_)->Destroy(output_mix_object_); |
| } |
| if (engine_object_) { |
| (*engine_object_)->Destroy(engine_object_); |
| } |
| } |
| |
| base::WeakPtr<AudioPlayerAndroid> AudioPlayerAndroid::GetWeakPtr() { |
| return weak_factory_.GetWeakPtr(); |
| } |
| |
| uint32_t AudioPlayerAndroid::GetSamplesPerFrame() { |
| return sample_per_frame_; |
| } |
| |
| bool AudioPlayerAndroid::ResetAudioPlayer( |
| AudioPacket::SamplingRate sampling_rate) { |
| if (!output_mix_object_) { |
| // output mixer not successfully created in ctor. |
| return false; |
| } |
| DestroyPlayer(); |
| sample_per_frame_ = |
| kFrameSizeMs * sampling_rate / base::Time::kMillisecondsPerSecond; |
| buffer_size_ = kChannels * kSampleSizeBytes * sample_per_frame_; |
| frame_buffer_.reset(new uint8_t[buffer_size_]); |
| FillWithSamples(frame_buffer_.get(), buffer_size_); |
| SLDataLocator_AndroidSimpleBufferQueue locator_bufqueue; |
| locator_bufqueue.locatorType = SL_DATALOCATOR_ANDROIDSIMPLEBUFFERQUEUE; |
| locator_bufqueue.numBuffers = kNumOfBuffers; |
| SLDataFormat_PCM format = CreatePcmFormat(sampling_rate); |
| SLDataSource source = {&locator_bufqueue, &format}; |
| SLDataLocator_OutputMix locator_out; |
| locator_out.locatorType = SL_DATALOCATOR_OUTPUTMIX; |
| locator_out.outputMix = output_mix_object_; |
| SLDataSink sink; |
| sink.pLocator = &locator_out; |
| sink.pFormat = nullptr; |
| |
| const SLInterfaceID ids[] = {SL_IID_BUFFERQUEUE}; |
| const SLboolean reqs[] = {SL_BOOLEAN_TRUE}; |
| |
| if ((*engine_)->CreateAudioPlayer(engine_, &player_object_, &source, &sink, |
| arraysize(ids), ids, |
| reqs) != SL_RESULT_SUCCESS || |
| (*player_object_)->Realize(player_object_, SL_BOOLEAN_FALSE) != |
| SL_RESULT_SUCCESS || |
| (*player_object_)->GetInterface(player_object_, SL_IID_PLAY, &player_) != |
| SL_RESULT_SUCCESS || |
| (*player_object_) |
| ->GetInterface(player_object_, SL_IID_BUFFERQUEUE, |
| &buffer_queue_) != SL_RESULT_SUCCESS || |
| (*buffer_queue_) |
| ->RegisterCallback(buffer_queue_, |
| &AudioPlayerAndroid::BufferQueueCallback, |
| this) != SL_RESULT_SUCCESS || |
| (*player_)->SetPlayState(player_, SL_PLAYSTATE_PLAYING) != |
| SL_RESULT_SUCCESS || |
| |
| // The player will only ask for more data after it consumes all its |
| // buffers. Having an empty queue will not trigger it to ask for more |
| // data. |
| (*buffer_queue_) |
| ->Enqueue(buffer_queue_, frame_buffer_.get(), buffer_size_) != |
| SL_RESULT_SUCCESS) { |
| LOG(ERROR) << "Failed to initialize the player."; |
| return false; |
| } |
| return true; |
| } |
| |
| // static |
| void AudioPlayerAndroid::BufferQueueCallback( |
| SLAndroidSimpleBufferQueueItf caller, |
| void* args) { |
| AudioPlayerAndroid* player = static_cast<AudioPlayerAndroid*>(args); |
| player->FillWithSamples(player->frame_buffer_.get(), player->buffer_size_); |
| if ((*caller)->Enqueue(caller, player->frame_buffer_.get(), |
| player->buffer_size_) != SL_RESULT_SUCCESS) { |
| LOG(ERROR) << "Failed to enqueue the frame."; |
| } |
| } |
| |
| // static |
| SLDataFormat_PCM AudioPlayerAndroid::CreatePcmFormat(int sampling_rate) { |
| SLDataFormat_PCM format; |
| format.formatType = SL_DATAFORMAT_PCM; |
| format.numChannels = kChannels; |
| switch (sampling_rate) { |
| case AudioPacket::SAMPLING_RATE_44100: |
| format.samplesPerSec = SL_SAMPLINGRATE_44_1; |
| break; |
| case AudioPacket::SAMPLING_RATE_48000: |
| format.samplesPerSec = SL_SAMPLINGRATE_48; |
| break; |
| default: |
| LOG(FATAL) << "Unsupported audio sampling rate: " << sampling_rate; |
| } // samplesPerSec is in mHz. OpenSL doesn't name this field well. |
| format.bitsPerSample = kSampleSizeBytes * 8; |
| format.containerSize = kSampleSizeBytes * 8; |
| #if defined(ARCH_CPU_LITTLE_ENDIAN) |
| format.endianness = SL_BYTEORDER_LITTLEENDIAN; |
| #else |
| format.endianness = SL_BYTEORDER_BIGENDIAN; |
| #endif |
| format.channelMask = kChannelMask; |
| return format; |
| } |
| |
| void AudioPlayerAndroid::DestroyPlayer() { |
| if (player_object_) { |
| (*player_object_)->Destroy(player_object_); |
| player_object_ = nullptr; |
| } |
| frame_buffer_.reset(); |
| buffer_size_ = 0; |
| player_ = nullptr; |
| buffer_queue_ = nullptr; |
| } |
| |
| } // namespace remoting |