text: // 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/protocol/webrtc_audio_module.h"
  
  #include <memory>
  
  #include "base/bind.h"
  #include "base/task/single_thread_task_runner.h"
  #include "base/threading/thread_task_runner_handle.h"
  #include "base/timer/timer.h"
  
  namespace remoting {
  namespace protocol {
  
  namespace {
  
  const int kSamplingRate = 48000;
  
  // Webrtc uses 10ms frames.
  const int kFrameLengthMs = 10;
  const int kSamplesPerFrame = kSamplingRate * kFrameLengthMs / 1000;
  
  constexpr base::TimeDelta kPollInterval =
      base::Milliseconds(5 * kFrameLengthMs);
  const int kChannels = 2;
  const int kBytesPerSample = 2;
  
  }  // namespace
  
  // webrtc::AudioDeviceModule is a generic interface that aims to provide all
  // functionality normally supported audio input/output devices, but most of
  // the functions are never called in Webrtc. This class implements only
  // functions that are actually used. All unused functions are marked as
  // NOTREACHED().
  
  WebrtcAudioModule::WebrtcAudioModule() = default;
  WebrtcAudioModule::~WebrtcAudioModule() = default;
  
  void WebrtcAudioModule::SetAudioTaskRunner(
      scoped_refptr<base::SingleThreadTaskRunner> audio_task_runner) {
    DCHECK(!audio_task_runner_);
    DCHECK(audio_task_runner);
    audio_task_runner_ = audio_task_runner;
  }
  
  int32_t WebrtcAudioModule::ActiveAudioLayer(AudioLayer* audio_layer) const {
    NOTREACHED();
    return -1;
  }
  
  int32_t WebrtcAudioModule::RegisterAudioCallback(
      webrtc::AudioTransport* audio_transport) {
    base::AutoLock lock(lock_);
    audio_transport_ = audio_transport;
    return 0;
  }
  
  int32_t WebrtcAudioModule::Init() {
    base::AutoLock auto_lock(lock_);
    initialized_ = true;
    return 0;
  }
  
  int32_t WebrtcAudioModule::Terminate() {
    base::AutoLock auto_lock(lock_);
    initialized_ = false;
    return 0;
  }
  
  bool WebrtcAudioModule::Initialized() const {
    base::AutoLock auto_lock(lock_);
    return initialized_;
  }
  
  int16_t WebrtcAudioModule::PlayoutDevices() {
    return 0;
  }
  
  int16_t WebrtcAudioModule::RecordingDevices() {
    return 0;
  }
  
  int32_t WebrtcAudioModule::PlayoutDeviceName(
      uint16_t index,
      char name[webrtc::kAdmMaxDeviceNameSize],
      char guid[webrtc::kAdmMaxGuidSize]) {
    return 0;
  }
  
  int32_t WebrtcAudioModule::RecordingDeviceName(
      uint16_t index,
      char name[webrtc::kAdmMaxDeviceNameSize],
      char guid[webrtc::kAdmMaxGuidSize]) {
    return 0;
  }
  
  int32_t WebrtcAudioModule::SetPlayoutDevice(uint16_t index) {
    return 0;
  }
  
  int32_t WebrtcAudioModule::SetPlayoutDevice(WindowsDeviceType device) {
    return 0;
  }
  
  int32_t WebrtcAudioModule::SetRecordingDevice(uint16_t index) {
    return 0;
  }
  
  int32_t WebrtcAudioModule::SetRecordingDevice(WindowsDeviceType device) {
    return 0;
  }
  
  int32_t WebrtcAudioModule::PlayoutIsAvailable(bool* available) {
    NOTREACHED();
    return -1;
  }
  
  int32_t WebrtcAudioModule::InitPlayout() {
    return 0;
  }
  
  bool WebrtcAudioModule::PlayoutIsInitialized() const {
    base::AutoLock auto_lock(lock_);
    return initialized_;
  }
  
  int32_t WebrtcAudioModule::RecordingIsAvailable(bool* available) {
    NOTREACHED();
    return -1;
  }
  
  int32_t WebrtcAudioModule::InitRecording() {
    return 0;
  }
  
  bool WebrtcAudioModule::RecordingIsInitialized() const {
    return false;
  }
  
  int32_t WebrtcAudioModule::StartPlayout() {
    base::AutoLock auto_lock(lock_);
    if (!playing_ && audio_task_runner_) {
      audio_task_runner_->PostTask(
          FROM_HERE, base::BindOnce(&WebrtcAudioModule::StartPlayoutOnAudioThread,
                                    rtc::scoped_refptr<WebrtcAudioModule>(this)));
      playing_ = true;
    }
    return 0;
  }
  
  int32_t WebrtcAudioModule::StopPlayout() {
    base::AutoLock auto_lock(lock_);
    if (playing_) {
      audio_task_runner_->PostTask(
          FROM_HERE, base::BindOnce(&WebrtcAudioModule::StopPlayoutOnAudioThread,
                                    rtc::scoped_refptr<WebrtcAudioModule>(this)));
      playing_ = false;
    }
    return 0;
  }
  
  bool WebrtcAudioModule::Playing() const {
    base::AutoLock auto_lock(lock_);
    return playing_;
  }
  
  int32_t WebrtcAudioModule::StartRecording() {
    return 0;
  }
  
  int32_t WebrtcAudioModule::StopRecording() {
    return 0;
  }
  
  bool WebrtcAudioModule::Recording() const {
    return false;
  }
  
  int32_t WebrtcAudioModule::InitSpeaker() {
    return 0;
  }
  
  bool WebrtcAudioModule::SpeakerIsInitialized() const {
    return false;
  }
  
  int32_t WebrtcAudioModule::InitMicrophone() {
    return 0;
  }
  
  bool WebrtcAudioModule::MicrophoneIsInitialized() const {
    return false;
  }
  
  int32_t WebrtcAudioModule::SpeakerVolumeIsAvailable(bool* available) {
    NOTREACHED();
    return -1;
  }
  
  int32_t WebrtcAudioModule::SetSpeakerVolume(uint32_t volume) {
    NOTREACHED();
    return -1;
  }
  
  int32_t WebrtcAudioModule::SpeakerVolume(uint32_t* volume) const {
    NOTREACHED();
    return -1;
  }
  
  int32_t WebrtcAudioModule::MaxSpeakerVolume(uint32_t* max_volume) const {
    NOTREACHED();
    return -1;
  }
  
  int32_t WebrtcAudioModule::MinSpeakerVolume(uint32_t* min_volume) const {
    NOTREACHED();
    return -1;
  }
  
  int32_t WebrtcAudioModule::MicrophoneVolumeIsAvailable(bool* available) {
    NOTREACHED();
    return -1;
  }
  
  int32_t WebrtcAudioModule::SetMicrophoneVolume(uint32_t volume) {
    NOTREACHED();
    return -1;
  }
  
  int32_t WebrtcAudioModule::MicrophoneVolume(uint32_t* volume) const {
    NOTREACHED();
    return -1;
  }
  
  int32_t WebrtcAudioModule::MaxMicrophoneVolume(uint32_t* max_volume) const {
    NOTREACHED();
    return -1;
  }
  
  int32_t WebrtcAudioModule::MinMicrophoneVolume(uint32_t* min_volume) const {
    NOTREACHED();
    return -1;
  }
  
  int32_t WebrtcAudioModule::SpeakerMuteIsAvailable(bool* available) {
    NOTREACHED();
    return -1;
  }
  
  int32_t WebrtcAudioModule::SetSpeakerMute(bool enable) {
    NOTREACHED();
    return -1;
  }
  
  int32_t WebrtcAudioModule::SpeakerMute(bool* enabled) const {
    NOTREACHED();
    return -1;
  }
  
  int32_t WebrtcAudioModule::MicrophoneMuteIsAvailable(bool* available) {
    NOTREACHED();
    return -1;
  }
  
  int32_t WebrtcAudioModule::SetMicrophoneMute(bool enable) {
    NOTREACHED();
    return -1;
  }
  
  int32_t WebrtcAudioModule::MicrophoneMute(bool* enabled) const {
    NOTREACHED();
    return -1;
  }
  
  int32_t WebrtcAudioModule::StereoPlayoutIsAvailable(bool* available) const {
    *available = true;
    return 0;
  }
  
  int32_t WebrtcAudioModule::SetStereoPlayout(bool enable) {
    DCHECK(enable);
    return 0;
  }
  
  int32_t WebrtcAudioModule::StereoPlayout(bool* enabled) const {
    NOTREACHED();
    return -1;
  }
  
  int32_t WebrtcAudioModule::StereoRecordingIsAvailable(bool* available) const {
    *available = false;
    return 0;
  }
  
  int32_t WebrtcAudioModule::SetStereoRecording(bool enable) {
    return 0;
  }
  
  int32_t WebrtcAudioModule::StereoRecording(bool* enabled) const {
    NOTREACHED();
    return -1;
  }
  
  int32_t WebrtcAudioModule::PlayoutDelay(uint16_t* delay_ms) const {
    *delay_ms = 0;
    return 0;
  }
  
  bool WebrtcAudioModule::BuiltInAECIsAvailable() const {
    return false;
  }
  
  bool WebrtcAudioModule::BuiltInAGCIsAvailable() const {
    return false;
  }
  
  bool WebrtcAudioModule::BuiltInNSIsAvailable() const {
    return false;
  }
  
  int32_t WebrtcAudioModule::EnableBuiltInAEC(bool enable) {
    NOTREACHED();
    return -1;
  }
  
  int32_t WebrtcAudioModule::EnableBuiltInAGC(bool enable) {
    NOTREACHED();
    return -1;
  }
  
  int32_t WebrtcAudioModule::EnableBuiltInNS(bool enable) {
    NOTREACHED();
    return -1;
  }
  
  #if defined(WEBRTC_IOS)
  int WebrtcAudioModule::GetPlayoutAudioParameters(
      webrtc::AudioParameters* params) const {
    NOTREACHED();
    return -1;
  }
  
  int WebrtcAudioModule::GetRecordAudioParameters(
      webrtc::AudioParameters* params) const {
    NOTREACHED();
    return -1;
  }
  #endif  // WEBRTC_IOS
  
  void WebrtcAudioModule::StartPlayoutOnAudioThread() {
    DCHECK(audio_task_runner_->BelongsToCurrentThread());
    poll_timer_ = std::make_unique<base::RepeatingTimer>();
    poll_timer_->Start(FROM_HERE, kPollInterval,
                       base::BindRepeating(&WebrtcAudioModule::PollFromSource,
                                           base::Unretained(this)));
  }
  
  void WebrtcAudioModule::StopPlayoutOnAudioThread() {
    DCHECK(audio_task_runner_->BelongsToCurrentThread());
    poll_timer_.reset();
  }
  
  void WebrtcAudioModule::PollFromSource() {
    DCHECK(audio_task_runner_->BelongsToCurrentThread());
  
    base::AutoLock lock(lock_);
    if (!audio_transport_)
      return;
  
    for (int i = 0; i < kPollInterval.InMilliseconds() / kFrameLengthMs; i++) {
      int64_t elapsed_time_ms = -1;
      int64_t ntp_time_ms = -1;
      char data[kBytesPerSample * kChannels * kSamplesPerFrame];
      audio_transport_->PullRenderData(kBytesPerSample * 8, kSamplingRate,
                                       kChannels, kSamplesPerFrame, data,
                                       &elapsed_time_ms, &ntp_time_ms);
    }
  }
  
  }  // namespace protocol
  }  // namespace remoting
  