chromium/remoting/client/audio/audio_player_android.cc

// Copyright 2016 The Chromium Authors
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.

#include "remoting/client/audio/audio_player_android.h"

#include "base/logging.h"
#include "base/time/time.h"
#include "build/build_config.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;

// TODO(nicholss): Update legacy audio player to use new audio buffer code.

AudioPlayerAndroid::AudioPlayerAndroid() {
  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,
                                    std::size(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