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 "content/renderer/media/webrtc/webrtc_audio_sink.h"
  
  #include <algorithm>
  #include <limits>
  
  #include "base/bind.h"
  #include "base/bind_helpers.h"
  #include "base/location.h"
  #include "base/logging.h"
  #include "base/threading/thread_task_runner_handle.h"
  
  namespace content {
  
  WebRtcAudioSink::WebRtcAudioSink(
      const std::string& label,
      scoped_refptr<webrtc::AudioSourceInterface> track_source,
      scoped_refptr<base::SingleThreadTaskRunner> signaling_task_runner)
      : adapter_(new rtc::RefCountedObject<Adapter>(
            label, std::move(track_source), std::move(signaling_task_runner))),
        fifo_(base::Bind(&WebRtcAudioSink::DeliverRebufferedAudio,
                         base::Unretained(this))) {
    DVLOG(1) << "WebRtcAudioSink::WebRtcAudioSink()";
  }
  
  WebRtcAudioSink::~WebRtcAudioSink() {
    DCHECK(thread_checker_.CalledOnValidThread());
    DVLOG(1) << "WebRtcAudioSink::~WebRtcAudioSink()";
  }
  
  void WebRtcAudioSink::SetAudioProcessor(
      scoped_refptr<MediaStreamAudioProcessor> processor) {
    DCHECK(thread_checker_.CalledOnValidThread());
    DCHECK(processor.get());
    adapter_->set_processor(std::move(processor));
  }
  
  void WebRtcAudioSink::SetLevel(
      scoped_refptr<MediaStreamAudioLevelCalculator::Level> level) {
    DCHECK(thread_checker_.CalledOnValidThread());
    DCHECK(level.get());
    adapter_->set_level(std::move(level));
  }
  
  void WebRtcAudioSink::OnEnabledChanged(bool enabled) {
    DCHECK(thread_checker_.CalledOnValidThread());
    adapter_->signaling_task_runner()->PostTask(
        FROM_HERE,
        base::Bind(
            base::IgnoreResult(&WebRtcAudioSink::Adapter::set_enabled),
            adapter_, enabled));
  }
  
  void WebRtcAudioSink::OnData(const media::AudioBus& audio_bus,
                               base::TimeTicks estimated_capture_time) {
    DCHECK(audio_thread_checker_.CalledOnValidThread());
    // The following will result in zero, one, or multiple synchronous calls to
    // DeliverRebufferedAudio().
    fifo_.Push(audio_bus);
  }
  
  void WebRtcAudioSink::OnSetFormat(const media::AudioParameters& params) {
    // On a format change, the thread delivering audio might have also changed.
    audio_thread_checker_.DetachFromThread();
    DCHECK(audio_thread_checker_.CalledOnValidThread());
  
    DCHECK(params.IsValid());
    params_ = params;
    // Make sure that our params always reflect a buffer size of 10ms.
    params_.set_frames_per_buffer(params_.sample_rate() / 100);
    fifo_.Reset(params_.frames_per_buffer());
    const int num_pcm16_data_elements =
        params_.frames_per_buffer() * params_.channels();
    interleaved_data_.reset(new int16_t[num_pcm16_data_elements]);
  }
  
  void WebRtcAudioSink::DeliverRebufferedAudio(const media::AudioBus& audio_bus,
                                               int frame_delay) {
    DCHECK(audio_thread_checker_.CalledOnValidThread());
    DCHECK(params_.IsValid());
  
    // TODO(miu): Why doesn't a WebRTC sink care about reference time passed to
    // OnData(), and the |frame_delay| here?  How is AV sync achieved otherwise?
  
    // TODO(henrika): Remove this conversion once the interface in libjingle
    // supports float vectors.
    audio_bus.ToInterleaved(audio_bus.frames(),
                            sizeof(interleaved_data_[0]),
                            interleaved_data_.get());
    adapter_->DeliverPCMToWebRtcSinks(interleaved_data_.get(),
                                      params_.sample_rate(),
                                      audio_bus.channels(),
                                      audio_bus.frames());
  }
  
  namespace {
  // TODO(miu): MediaStreamAudioProcessor destructor requires this nonsense.
  void DereferenceOnMainThread(
      const scoped_refptr<MediaStreamAudioProcessor>& processor) {}
  }  // namespace
  
  WebRtcAudioSink::Adapter::Adapter(
      const std::string& label,
      scoped_refptr<webrtc::AudioSourceInterface> source,
      scoped_refptr<base::SingleThreadTaskRunner> signaling_task_runner)
      : webrtc::MediaStreamTrack<webrtc::AudioTrackInterface>(label),
        source_(std::move(source)),
        signaling_task_runner_(std::move(signaling_task_runner)),
        main_task_runner_(base::ThreadTaskRunnerHandle::Get()) {
    DCHECK(signaling_task_runner_);
  }
  
  WebRtcAudioSink::Adapter::~Adapter() {
    if (audio_processor_) {
      main_task_runner_->PostTask(
          FROM_HERE,
          base::Bind(&DereferenceOnMainThread, std::move(audio_processor_)));
    }
  }
  
  void WebRtcAudioSink::Adapter::DeliverPCMToWebRtcSinks(
      const int16_t* audio_data,
      int sample_rate,
      size_t number_of_channels,
      size_t number_of_frames) {
    base::AutoLock auto_lock(lock_);
    for (webrtc::AudioTrackSinkInterface* sink : sinks_) {
      sink->OnData(audio_data, sizeof(int16_t) * 8, sample_rate,
                   number_of_channels, number_of_frames);
    }
  }
  
  std::string WebRtcAudioSink::Adapter::kind() const {
    return webrtc::MediaStreamTrackInterface::kAudioKind;
  }
  
  bool WebRtcAudioSink::Adapter::set_enabled(bool enable) {
    DCHECK(!signaling_task_runner_ ||
           signaling_task_runner_->RunsTasksOnCurrentThread());
    return webrtc::MediaStreamTrack<webrtc::AudioTrackInterface>::
        set_enabled(enable);
  }
  
  void WebRtcAudioSink::Adapter::AddSink(webrtc::AudioTrackSinkInterface* sink) {
    DCHECK(!signaling_task_runner_ ||
           signaling_task_runner_->RunsTasksOnCurrentThread());
    DCHECK(sink);
    base::AutoLock auto_lock(lock_);
    DCHECK(std::find(sinks_.begin(), sinks_.end(), sink) == sinks_.end());
    sinks_.push_back(sink);
  }
  
  void WebRtcAudioSink::Adapter::RemoveSink(
      webrtc::AudioTrackSinkInterface* sink) {
    DCHECK(!signaling_task_runner_ ||
           signaling_task_runner_->RunsTasksOnCurrentThread());
    base::AutoLock auto_lock(lock_);
    const auto it = std::find(sinks_.begin(), sinks_.end(), sink);
    if (it != sinks_.end())
      sinks_.erase(it);
  }
  
  bool WebRtcAudioSink::Adapter::GetSignalLevel(int* level) {
    DCHECK(!signaling_task_runner_ ||
           signaling_task_runner_->RunsTasksOnCurrentThread());
  
    // |level_| is only set once, so it's safe to read without first acquiring a
    // mutex.
    if (!level_)
      return false;
    const float signal_level = level_->GetCurrent();
    DCHECK_GE(signal_level, 0.0f);
    DCHECK_LE(signal_level, 1.0f);
    // Convert from float in range [0.0,1.0] to an int in range [0,32767].
    *level = static_cast<int>(signal_level * std::numeric_limits<int16_t>::max() +
                              0.5f /* rounding to nearest int */);
    return true;
  }
  
  rtc::scoped_refptr<webrtc::AudioProcessorInterface>
  WebRtcAudioSink::Adapter::GetAudioProcessor() {
    DCHECK(!signaling_task_runner_ ||
           signaling_task_runner_->RunsTasksOnCurrentThread());
    return audio_processor_.get();
  }
  
  webrtc::AudioSourceInterface* WebRtcAudioSink::Adapter::GetSource() const {
    DCHECK(!signaling_task_runner_ ||
           signaling_task_runner_->RunsTasksOnCurrentThread());
    return source_.get();
  }
  
  }  // namespace content
  