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 "third_party/blink/renderer/modules/mediastream/local_media_stream_audio_source.h"
  
  #include <utility>
  
  #include "base/strings/stringprintf.h"
  #include "media/audio/audio_source_parameters.h"
  #include "third_party/blink/public/mojom/mediastream/media_stream.mojom-blink.h"
  #include "third_party/blink/public/platform/modules/webrtc/webrtc_logging.h"
  #include "third_party/blink/public/platform/platform.h"
  #include "third_party/blink/public/web/web_local_frame.h"
  #include "third_party/blink/renderer/core/frame/local_frame.h"
  
  namespace blink {
  
  LocalMediaStreamAudioSource::LocalMediaStreamAudioSource(
      LocalFrame* consumer_frame,
      const MediaStreamDevice& device,
      const int* requested_buffer_size,
      bool disable_local_echo,
      ConstraintsRepeatingCallback started_callback,
      scoped_refptr<base::SingleThreadTaskRunner> task_runner)
      : MediaStreamAudioSource(std::move(task_runner),
                               true /* is_local_source */,
                               disable_local_echo),
        consumer_frame_(consumer_frame),
        started_callback_(std::move(started_callback)) {
    DVLOG(1) << "LocalMediaStreamAudioSource::LocalMediaStreamAudioSource()";
    SetDevice(device);
  
    int frames_per_buffer = device.input.frames_per_buffer();
    if (requested_buffer_size)
      frames_per_buffer = *requested_buffer_size;
  
    // If the device buffer size was not provided, use a default.
    if (frames_per_buffer <= 0) {
      frames_per_buffer =
          (device.input.sample_rate() * kFallbackAudioLatencyMs) / 1000;
    }
  
    // Set audio format and take into account the special case where a discrete
    // channel layout is reported since it will result in an invalid channel
    // count (=0) if only default constructions is used.
    media::AudioParameters params(media::AudioParameters::AUDIO_PCM_LOW_LATENCY,
                                  device.input.channel_layout(),
                                  device.input.sample_rate(), frames_per_buffer);
    if (device.input.channel_layout() == media::CHANNEL_LAYOUT_DISCRETE) {
      DCHECK_LE(device.input.channels(), 2);
      params.set_channels_for_discrete(device.input.channels());
    }
    SetFormat(params);
  }
  
  LocalMediaStreamAudioSource::~LocalMediaStreamAudioSource() {
    DVLOG(1) << "LocalMediaStreamAudioSource::~LocalMediaStreamAudioSource()";
    EnsureSourceIsStopped();
  }
  
  bool LocalMediaStreamAudioSource::EnsureSourceIsStarted() {
    DCHECK_CALLED_ON_VALID_THREAD(thread_checker_);
  
    if (source_)
      return true;
  
    std::string str = base::StringPrintf(
        "LocalMediaStreamAudioSource::EnsureSourceIsStarted."
        " channel_layout=%d, sample_rate=%d, buffer_size=%d"
        ", session_id=%s, effects=%d. ",
        device().input.channel_layout(), device().input.sample_rate(),
        device().input.frames_per_buffer(),
        device().session_id().ToString().c_str(), device().input.effects());
    WebRtcLogMessage(str);
    DVLOG(1) << str;
  
    // Sanity-check that the consuming WebLocalFrame still exists.
    // This is required by AudioDeviceFactory.
    if (!consumer_frame_)
      return false;
  
    VLOG(1) << "Starting local audio input device (session_id="
            << device().session_id() << ") with audio parameters={"
            << GetAudioParameters().AsHumanReadableString() << "}.";
  
    auto* web_frame =
        static_cast<WebLocalFrame*>(WebFrame::FromCoreFrame(consumer_frame_));
    source_ = Platform::Current()->NewAudioCapturerSource(
        web_frame, media::AudioSourceParameters(device().session_id()));
    source_->Initialize(GetAudioParameters(), this);
    source_->Start();
    return true;
  }
  
  void LocalMediaStreamAudioSource::EnsureSourceIsStopped() {
    DCHECK_CALLED_ON_VALID_THREAD(thread_checker_);
  
    if (!source_)
      return;
  
    source_->Stop();
    source_ = nullptr;
  
    VLOG(1) << "Stopped local audio input device (session_id="
            << device().session_id() << ") with audio parameters={"
            << GetAudioParameters().AsHumanReadableString() << "}.";
  }
  
  void LocalMediaStreamAudioSource::OnCaptureStarted() {
    started_callback_.Run(this, mojom::MediaStreamRequestResult::OK, "");
  }
  
  void LocalMediaStreamAudioSource::Capture(const media::AudioBus* audio_bus,
                                            base::TimeTicks audio_capture_time,
                                            double volume,
                                            bool key_pressed) {
    DCHECK(audio_bus);
    DeliverDataToTracks(*audio_bus, audio_capture_time);
  }
  
  void LocalMediaStreamAudioSource::OnCaptureError(
      media::AudioCapturerSource::ErrorCode code,
      const std::string& why) {
    WebRtcLogMessage(
        base::StringPrintf("LocalMediaStreamAudioSource::OnCaptureError: %d, %s",
                           code, why.c_str()));
  
    StopSourceOnError(code, why);
  }
  
  void LocalMediaStreamAudioSource::OnCaptureMuted(bool is_muted) {
    SetMutedState(is_muted);
  }
  
  void LocalMediaStreamAudioSource::ChangeSourceImpl(
      const MediaStreamDevice& new_device) {
    WebRtcLogMessage(
        "LocalMediaStreamAudioSource::ChangeSourceImpl(new_device = " +
        new_device.id + ")");
    EnsureSourceIsStopped();
    SetDevice(new_device);
    EnsureSourceIsStarted();
  }
  
  using EchoCancellationType =
      blink::AudioProcessingProperties::EchoCancellationType;
  
  absl::optional<blink::AudioProcessingProperties>
  LocalMediaStreamAudioSource::GetAudioProcessingProperties() const {
    blink::AudioProcessingProperties properties;
    properties.DisableDefaultProperties();
  
    if (device().input.effects() & media::AudioParameters::ECHO_CANCELLER) {
      properties.echo_cancellation_type =
          EchoCancellationType::kEchoCancellationSystem;
    }
  
    return properties;
  }
  
  }  // namespace blink
  