// Copyright 2021 The Chromium Authors
// 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/webcodecs/audio_encoder.h"

#include <algorithm>
#include <cinttypes>
#include <limits>

#include "base/metrics/histogram_functions.h"
#include "base/strings/to_string.h"
#include "base/trace_event/common/trace_event_common.h"
#include "base/trace_event/trace_event.h"
#include "build/build_config.h"
#include "media/audio/audio_opus_encoder.h"
#include "media/base/audio_parameters.h"
#include "media/base/limits.h"
#include "media/base/mime_util.h"
#include "media/base/offloading_audio_encoder.h"
#include "media/mojo/clients/mojo_audio_encoder.h"
#include "media/mojo/mojom/interface_factory.mojom.h"
#include "third_party/blink/public/common/thread_safe_browser_interface_broker_proxy.h"
#include "third_party/blink/public/mojom/use_counter/metrics/web_feature.mojom-blink.h"
#include "third_party/blink/public/platform/platform.h"
#include "third_party/blink/renderer/bindings/core/v8/to_v8_traits.h"
#include "third_party/blink/renderer/bindings/modules/v8/v8_aac_encoder_config.h"
#include "third_party/blink/renderer/bindings/modules/v8/v8_audio_data_init.h"
#include "third_party/blink/renderer/bindings/modules/v8/v8_audio_decoder_config.h"
#include "third_party/blink/renderer/bindings/modules/v8/v8_audio_encoder_config.h"
#include "third_party/blink/renderer/bindings/modules/v8/v8_audio_encoder_support.h"
#include "third_party/blink/renderer/bindings/modules/v8/v8_encoded_audio_chunk_metadata.h"
#include "third_party/blink/renderer/bindings/modules/v8/v8_opus_application.h"
#include "third_party/blink/renderer/bindings/modules/v8/v8_opus_encoder_config.h"
#include "third_party/blink/renderer/bindings/modules/v8/v8_opus_signal.h"
#include "third_party/blink/renderer/modules/webcodecs/array_buffer_util.h"
#include "third_party/blink/renderer/modules/webcodecs/encoded_audio_chunk.h"
#include "third_party/blink/renderer/platform/bindings/exception_state.h"
#include "third_party/blink/renderer/platform/heap/cross_thread_handle.h"
#include "third_party/blink/renderer/platform/wtf/cross_thread_functional.h"
#include "third_party/blink/renderer/platform/wtf/text/format.h"
#include "third_party/blink/renderer/platform/wtf/text/string_builder.h"

namespace blink {

namespace {

constexpr const char kCategory[] = "media";

#if BUILDFLAG(IS_ANDROID) || BUILDFLAG(IS_IOS)
constexpr uint32_t kDefaultOpusComplexity = 5;
#else
constexpr uint32_t kDefaultOpusComplexity = 9;
#endif

template <typename T>
bool VerifyParameterValues(const T& value,
                           String error_message_base_base,
                           Vector<T> supported_values,
                           String* js_error_message) {
  if (std::ranges::contains(supported_values, value)) {
    return true;
  }

  Vector<String> supported_values_str;
  for (const T& val : supported_values) {
    supported_values_str.push_back(base::ToString(val));
  }

  StringBuilder error_builder;
  error_builder.Append(error_message_base_base);
  error_builder.Append(" Supported values: ");
  error_builder.AppendRange(std::move(supported_values_str), ", ");
  *js_error_message = error_builder.ReleaseString();
  return false;
}

bool VerifyDurationValues(int64_t microseconds,
                          String error_message_base_base,
                          String* js_error_message) {
  if (microseconds >= 2500 && microseconds <= 120000 &&
      microseconds % 2500 == 0) {
    return true;
  }

  StringBuilder error_builder;
  error_builder.Append(error_message_base_base);
  error_builder.Append(
      " Needs to be a multiple of 2500 and in the range of [2500, 120000] "
      "microseconds.");
  *js_error_message = error_builder.ToString();
  return false;
}

AudioEncoderTraits::ParsedConfig* ParseAacConfigStatic(
    const AacEncoderConfig* aac_config,
    AudioEncoderTraits::ParsedConfig* result,
    ExceptionState& exception_state) {
  result->options.aac = media::AudioEncoder::AacOptions();
  switch (aac_config->format().AsEnum()) {
    case V8AacBitstreamFormat::Enum::kAac:
      result->options.aac->format = media::AudioEncoder::AacOutputFormat::AAC;
      return result;
    case V8AacBitstreamFormat::Enum::kAdts:
      result->options.aac->format = media::AudioEncoder::AacOutputFormat::ADTS;
      return result;
  }
  return result;
}

AudioEncoderTraits::ParsedConfig* ParseOpusConfigStatic(
    const OpusEncoderConfig* opus_config,
    AudioEncoderTraits::ParsedConfig* result,
    ExceptionState& exception_state) {
  constexpr uint32_t kComplexityUpperBound = 10;
  uint32_t complexity = opus_config->getComplexityOr(kDefaultOpusComplexity);
  if (complexity > kComplexityUpperBound) {
    exception_state.ThrowTypeError(
        ExceptionMessages::IndexExceedsMaximumBound<uint32_t>(
            "Opus complexity", complexity, kComplexityUpperBound));
    return nullptr;
  }

  constexpr uint32_t kPacketLossPercUpperBound = 100;
  uint32_t packet_loss_perc = opus_config->packetlossperc();
  if (packet_loss_perc > kPacketLossPercUpperBound) {
    exception_state.ThrowTypeError(
        ExceptionMessages::IndexExceedsMaximumBound<uint32_t>(
            "Opus packetlossperc", packet_loss_perc,
            kPacketLossPercUpperBound));
    return nullptr;
  }

  // `frame_duration` must be a valid frame duration, defined in section 2.1.4.
  // of RFC6716.
  constexpr base::TimeDelta kFrameDurationLowerBound = base::Microseconds(2500);
  constexpr base::TimeDelta kFrameDurationUpperBound = base::Milliseconds(120);
  uint64_t frame_duration = opus_config->frameDuration();
  if (frame_duration < kFrameDurationLowerBound.InMicroseconds() ||
      frame_duration > kFrameDurationUpperBound.InMicroseconds()) {
    exception_state.ThrowTypeError(
        ExceptionMessages::IndexOutsideRange<uint64_t>(
            "Opus frameDuration", frame_duration,
            kFrameDurationLowerBound.InMicroseconds(),
            ExceptionMessages::BoundType::kInclusiveBound,
            kFrameDurationUpperBound.InMicroseconds(),
            ExceptionMessages::BoundType::kInclusiveBound));
    return nullptr;
  }

  // Any multiple of a frame duration is allowed by RFC6716. Concretely, this
  // means any multiple of 2500 microseconds.
  if (frame_duration % kFrameDurationLowerBound.InMicroseconds() != 0) {
    exception_state.ThrowTypeError(
        Format("Invalid Opus frameDuration; expected a multiple of {}, "
               "received {}.",
               kFrameDurationLowerBound.InMicroseconds(), frame_duration));
    return nullptr;
  }

  if (opus_config->format().AsEnum() == V8OpusBitstreamFormat::Enum::kOgg) {
    exception_state.ThrowDOMException(DOMExceptionCode::kNotSupportedError,
                                      "Opus Ogg format is unsupported");
    return nullptr;
  }

  media::AudioEncoder::OpusSignal opus_signal;
  switch (opus_config->signal().AsEnum()) {
    case blink::V8OpusSignal::Enum::kAuto:
      opus_signal = media::AudioEncoder::OpusSignal::kAuto;
      break;
    case blink::V8OpusSignal::Enum::kMusic:
      opus_signal = media::AudioEncoder::OpusSignal::kMusic;
      break;
    case blink::V8OpusSignal::Enum::kVoice:
      opus_signal = media::AudioEncoder::OpusSignal::kVoice;
      break;
  }

  media::AudioEncoder::OpusApplication opus_application;
  switch (opus_config->application().AsEnum()) {
    case blink::V8OpusApplication::Enum::kVoip:
      opus_application = media::AudioEncoder::OpusApplication::kVoip;
      break;
    case blink::V8OpusApplication::Enum::kAudio:
      opus_application = media::AudioEncoder::OpusApplication::kAudio;
      break;
    case blink::V8OpusApplication::Enum::kLowdelay:
      opus_application = media::AudioEncoder::OpusApplication::kLowDelay;
      break;
  }

  result->options.opus = {
      .frame_duration = base::Microseconds(frame_duration),
      .signal = opus_signal,
      .application = opus_application,
      .complexity = complexity,
      .packet_loss_perc = packet_loss_perc,
      .use_in_band_fec = opus_config->useinbandfec(),
      .use_dtx = opus_config->usedtx(),
  };

  return result;
}

AudioEncoderTraits::ParsedConfig* ParseConfigStatic(
    const AudioEncoderConfig* config,
    ExceptionState& exception_state) {
  if (!config) {
    exception_state.ThrowTypeError("No config provided");
    return nullptr;
  }

  if (config->codec().LengthWithStrippedWhiteSpace() == 0) {
    exception_state.ThrowTypeError("Invalid codec; codec is required.");
    return nullptr;
  }

  auto* result = MakeGarbageCollected<AudioEncoderTraits::ParsedConfig>();

  std::optional<media::AudioType> audio_type =
      media::ParseAudioCodecString("", config->codec().Utf8());
  if (!audio_type) {
    result->options.codec = media::AudioCodec::kUnknown;
    return result;
  }

  // AudioCodecProfile isn't supported by any of Chromium's encoders. It's not
  // likely in the future either since the existing profiles are for multi-pass
  // type encodings that this API is not designed for.
  result->options.codec = audio_type->codec;
  result->options.channels = config->numberOfChannels();
  if (result->options.channels == 0) {
    exception_state.ThrowTypeError(Format(
        "Invalid channel count; channel count must be non-zero, received {}.",
        result->options.channels));
    return nullptr;
  }

  result->options.sample_rate = config->sampleRate();
  if (result->options.sample_rate == 0) {
    exception_state.ThrowTypeError(Format(
        "Invalid sample rate; sample rate must be non-zero, received {}.",
        result->options.sample_rate));
    return nullptr;
  }

  result->codec_string = config->codec();
  if (config->hasBitrate()) {
    if (config->bitrate() > std::numeric_limits<int>::max()) {
      exception_state.ThrowTypeError(
          Format("Bitrate is too large; expected at most {}, received {}",
                 std::numeric_limits<int>::max(), config->bitrate()));
      return nullptr;
    }
    result->options.bitrate = static_cast<int>(config->bitrate());
  }

  if (config->hasBitrateMode()) {
    result->options.bitrate_mode =
        config->bitrateMode().AsEnum() == V8BitrateMode::Enum::kConstant
            ? media::AudioEncoder::BitrateMode::kConstant
            : media::AudioEncoder::BitrateMode::kVariable;
  }

  switch (result->options.codec) {
    case media::AudioCodec::kOpus:
      return ParseOpusConfigStatic(
          config->hasOpus() ? config->opus() : OpusEncoderConfig::Create(),
          result, exception_state);
    case media::AudioCodec::kAAC: {
      auto* aac_config =
          config->hasAac() ? config->aac() : AacEncoderConfig::Create();
      return ParseAacConfigStatic(aac_config, result, exception_state);
    }
    default:
      return result;
  }
}

bool VerifyCodecSupportStatic(AudioEncoderTraits::ParsedConfig* config,
                              String* js_error_message) {
  if (config->options.channels < 1 ||
      config->options.channels > media::limits::kMaxChannels) {
    *js_error_message = Format(
        "Unsupported channel count; expected range from {} to {}, received {}.",
        1, media::limits::kMaxChannels, config->options.channels);
    return false;
  }

  if (config->options.sample_rate < media::limits::kMinSampleRate ||
      config->options.sample_rate > media::limits::kMaxSampleRate) {
    *js_error_message = Format(
        "Unsupported sample rate; expected range from {} to {}, received {}.",
        media::limits::kMinSampleRate, media::limits::kMaxSampleRate,
        config->options.sample_rate);
    return false;
  }

  switch (config->options.codec) {
    case media::AudioCodec::kOpus: {
      if (!VerifyDurationValues(
              config->options.opus->frame_duration.InMicroseconds(),
              "Unsupported Opus frameDuration.", js_error_message)) {
        return false;
      }
      if (config->options.channels > 2) {
        // Our Opus implementation only supports up to 2 channels
        *js_error_message = Format(
            "Too many channels for Opus encoder; expected at most 2, received "
            "{}.",
            config->options.channels);
        return false;
      }
      if (config->options.bitrate.has_value() &&
          config->options.bitrate.value() <
              media::AudioOpusEncoder::kMinBitrate) {
        *js_error_message = Format(
            "Opus bitrate is too low; expected at least {}, received {}.",
            media::AudioOpusEncoder::kMinBitrate,
            config->options.bitrate.value());
        return false;
      }
      return true;
    }
    case media::AudioCodec::kAAC: {
      if (media::MojoAudioEncoder::IsSupported(media::AudioCodec::kAAC)) {
        if (!VerifyParameterValues(config->options.channels,
                                   "Unsupported number of channels.", {1, 2, 6},
                                   js_error_message)) {
          return false;
        }
#if BUILDFLAG(IS_WIN)
        if (config->options.bitrate.has_value()) {
          if (!VerifyParameterValues(
                  config->options.bitrate.value(), "Unsupported bitrate.",
                  {96000, 128000, 160000, 192000}, js_error_message)) {
            return false;
          }
        }
#else
        // Other platforms vary in their bitrate support, so we can't provide
        // an accurate answer here.
#endif
        if (!VerifyParameterValues(config->options.sample_rate,
                                   "Unsupported sample rate.", {44100, 48000},
                                   js_error_message)) {
          return false;
        }
        return true;
      }
      [[fallthrough]];
    }
    default:
      *js_error_message = "Unsupported codec type.";
      return false;
  }
}

AacEncoderConfig* CopyAacConfig(const AacEncoderConfig& config) {
  auto* result = AacEncoderConfig::Create();
  result->setFormat(config.format());
  return result;
}

OpusEncoderConfig* CopyOpusConfig(const OpusEncoderConfig& config) {
  auto* opus_result = OpusEncoderConfig::Create();
  opus_result->setFormat(config.format());
  opus_result->setSignal(config.signal());
  opus_result->setApplication(config.application());
  opus_result->setFrameDuration(config.frameDuration());
  opus_result->setComplexity(config.getComplexityOr(kDefaultOpusComplexity));
  opus_result->setPacketlossperc(config.packetlossperc());
  opus_result->setUseinbandfec(config.useinbandfec());
  opus_result->setUsedtx(config.usedtx());
  return opus_result;
}

AudioEncoderConfig* CopyConfig(const AudioEncoderConfig& config) {
  auto* result = AudioEncoderConfig::Create();
  result->setCodec(config.codec());
  result->setSampleRate(config.sampleRate());
  result->setNumberOfChannels(config.numberOfChannels());
  if (config.hasBitrate())
    result->setBitrate(config.bitrate());

  if (config.hasBitrateMode()) {
    result->setBitrateMode(config.bitrateMode());
  }

  if (config.hasOpus()) {
    result->setOpus(CopyOpusConfig(*config.opus()));
  }
  if (config.hasAac()) {
    result->setAac(CopyAacConfig(*config.aac()));
  }

  return result;
}

std::unique_ptr<media::AudioEncoder> CreateSoftwareAudioEncoder(
    media::AudioCodec codec) {
  if (codec != media::AudioCodec::kOpus)
    return nullptr;
  auto software_encoder = std::make_unique<media::AudioOpusEncoder>();
  return std::make_unique<media::OffloadingAudioEncoder>(
      std::move(software_encoder));
}

std::unique_ptr<media::AudioEncoder> CreatePlatformAudioEncoder(
    media::AudioCodec codec) {
  if (codec != media::AudioCodec::kAAC)
    return nullptr;

  mojo::PendingRemote<media::mojom::InterfaceFactory> pending_interface_factory;
  mojo::Remote<media::mojom::InterfaceFactory> interface_factory;
  Platform::Current()->GetBrowserInterfaceBroker()->GetInterface(
      pending_interface_factory.InitWithNewPipeAndPassReceiver());
  interface_factory.Bind(std::move(pending_interface_factory));

  mojo::PendingRemote<media::mojom::AudioEncoder> encoder_remote;
  interface_factory->CreateAudioEncoder(
      encoder_remote.InitWithNewPipeAndPassReceiver());
  return std::make_unique<media::MojoAudioEncoder>(std::move(encoder_remote));
}

}  // namespace

// static
const char* AudioEncoderTraits::GetName() {
  return "AudioEncoder";
}

AudioEncoder* AudioEncoder::Create(ScriptState* script_state,
                                   const AudioEncoderInit* init,
                                   ExceptionState& exception_state) {
  auto* result =
      MakeGarbageCollected<AudioEncoder>(script_state, init, exception_state);
  return exception_state.HadException() ? nullptr : result;
}

AudioEncoder::AudioEncoder(ScriptState* script_state,
                           const AudioEncoderInit* init,
                           ExceptionState& exception_state)
    : Base(script_state, init, exception_state) {
  UseCounter::Count(ExecutionContext::From(script_state),
                    WebFeature::kWebCodecs);
}

AudioEncoder::~AudioEncoder() = default;

std::unique_ptr<media::AudioEncoder> AudioEncoder::CreateMediaAudioEncoder(
    const ParsedConfig& config) {
  if (auto result = CreatePlatformAudioEncoder(config.options.codec)) {
    is_platform_encoder_ = true;
    return result;
  }
  is_platform_encoder_ = false;
  return CreateSoftwareAudioEncoder(config.options.codec);
}

void AudioEncoder::ProcessConfigure(Request* request) {
  DCHECK_NE(state_.AsEnum(), V8CodecState::Enum::kClosed);
  DCHECK_EQ(request->type, Request::Type::kConfigure);
  DCHECK(request->config);
  DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);

  request->StartTracing();

  active_config_ = request->config;
  String js_error_message;
  if (!VerifyCodecSupport(active_config_, &js_error_message)) {
    blocking_request_in_progress_ = request;
    QueueHandleError(MakeGarbageCollected<DOMException>(
        DOMExceptionCode::kNotSupportedError, js_error_message));
    request->EndTracing();
    return;
  }

  media_encoder_ = CreateMediaAudioEncoder(*active_config_);
  if (!media_encoder_) {
    blocking_request_in_progress_ = request;
    QueueHandleError(MakeOperationError(
        "Encoder creation error.",
        media::EncoderStatus(
            media::EncoderStatus::Codes::kEncoderInitializationError,
            "Unable to create encoder (most likely unsupported "
            "codec/acceleration requirement combination)")));
    request->EndTracing();
    return;
  }

  auto output_cb = ConvertToBaseRepeatingCallback(CrossThreadBindRepeating(
      &AudioEncoder::CallOutputCallback,
      MakeUnwrappingCrossThreadWeakHandle(this),
      // We can't use |active_config_| from |this| because it can change by
      // the time the callback is executed.
      MakeUnwrappingCrossThreadHandle(active_config_.Get()), reset_count_));

  auto done_callback = [](AudioEncoder* self, media::AudioCodec codec,
                          Request* req, media::EncoderStatus status) {
    if (!self || self->reset_count_ != req->reset_count) {
      req->EndTracing(/*aborted=*/true);
      return;
    }
    DCHECK_CALLED_ON_VALID_SEQUENCE(self->sequence_checker_);
    if (!status.is_ok()) {
      self->HandleError(
          self->MakeOperationError("Encoding error.", std::move(status)));
    } else {
      base::UmaHistogramEnumeration("Blink.WebCodecs.AudioEncoder.Codec",
                                    codec);
    }

    req->EndTracing();
    self->blocking_request_in_progress_ = nullptr;
    self->ProcessRequests();
  };

  blocking_request_in_progress_ = request;
  first_output_after_configure_ = true;
  media_encoder_->Initialize(
      active_config_->options, std::move(output_cb),
      ConvertToBaseOnceCallback(CrossThreadBindOnce(
          done_callback, MakeUnwrappingCrossThreadWeakHandle(this),
          active_config_->options.codec,
          MakeUnwrappingCrossThreadHandle(request))));
}

bool AudioEncoder::ReadyToProcessNextRequest() {
  if (active_encodes_ >= kMaxActiveEncodes) {
    return false;
  }

  return Base::ReadyToProcessNextRequest();
}

void AudioEncoder::ProcessEncode(Request* request) {
  DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
  DCHECK_EQ(state_, V8CodecState::Enum::kConfigured);
  DCHECK(media_encoder_);
  DCHECK_EQ(request->type, Request::Type::kEncode);
  DCHECK_GT(requested_encodes_, 0u);

  request->StartTracing();

  auto* audio_data = request->input.Release();

  auto data = audio_data->data();

  // The data shouldn't be closed at this point.
  DCHECK(data);

  auto done_callback = [](AudioEncoder* self, Request* req,
                          media::EncoderStatus status) {
    if (!self || self->reset_count_ != req->reset_count) {
      req->EndTracing(/*aborted=*/true);
      return;
    }
    DCHECK_CALLED_ON_VALID_SEQUENCE(self->sequence_checker_);
    if (!status.is_ok()) {
      self->HandleError(
          self->MakeEncodingError("Encoding error.", std::move(status)));
    }

    --self->active_encodes_;
    req->EndTracing();
    self->ProcessRequests();
  };

  if (data->channel_count() != active_config_->options.channels ||
      data->sample_rate() != active_config_->options.sample_rate) {
    // Per spec we must queue a task for error handling.
    QueueHandleError(MakeEncodingError(
        "Input audio buffer is incompatible with codec parameters",
        media::EncoderStatus(media::EncoderStatus::Codes::kEncoderFailedEncode)
            .WithData("channels", data->channel_count())
            .WithData("sampleRate", data->sample_rate())));

    request->EndTracing();

    audio_data->close();
    return;
  }

  // If |data|'s memory layout allows it, |audio_bus| will be a simple wrapper
  // around it. Otherwise, |audio_bus| will contain a converted copy of |data|.
  auto audio_bus = media::AudioBuffer::WrapOrCopyToAudioBus(data);

  base::TimeTicks timestamp = base::TimeTicks() + data->timestamp();

  --requested_encodes_;
  ScheduleDequeueEvent();
  ++active_encodes_;

  media_encoder_->Encode(
      std::move(audio_bus), timestamp,
      ConvertToBaseOnceCallback(CrossThreadBindOnce(
          done_callback, MakeUnwrappingCrossThreadWeakHandle(this),
          MakeUnwrappingCrossThreadHandle(request))));

  audio_data->close();
}

void AudioEncoder::ProcessReconfigure(Request* request) {
  // Audio decoders don't currently support any meaningful reconfiguring
}

AudioEncoder::ParsedConfig* AudioEncoder::OnNewConfigure(
    const AudioEncoderConfig* opts,
    ExceptionState& exception_state) {
  return ParseConfigStatic(opts, exception_state);
}

bool AudioEncoder::CanReconfigure(ParsedConfig& original_config,
                                  ParsedConfig& new_config) {
  return original_config.options.codec == new_config.options.codec &&
         original_config.options.channels == new_config.options.channels &&
         original_config.options.bitrate == new_config.options.bitrate &&
         original_config.options.sample_rate == new_config.options.sample_rate;
}

bool AudioEncoder::VerifyCodecSupport(ParsedConfig* config,
                                      String* js_error_message) {
  return VerifyCodecSupportStatic(config, js_error_message);
}

void AudioEncoder::ResetInternal(DOMException* ex) {
  Base::ResetInternal(ex);
  active_encodes_ = 0;
}

void AudioEncoder::OnNewEncode(InputType* input,
                               ExceptionState& exception_state) {}

void AudioEncoder::CallOutputCallback(
    ParsedConfig* active_config,
    uint32_t reset_count,
    media::EncodedAudioBuffer encoded_buffer,
    std::optional<media::AudioEncoder::CodecDescription> codec_desc) {
  DCHECK(active_config);
  if (!script_state_->ContextIsValid() || !output_callback_ ||
      state_.AsEnum() != V8CodecState::Enum::kConfigured ||
      reset_count != reset_count_) {
    return;
  }

  DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);

  MarkCodecActive();

  auto buffer =
      media::DecoderBuffer::FromArray(std::move(encoded_buffer.encoded_data));
  buffer->set_timestamp(encoded_buffer.timestamp - base::TimeTicks());
  buffer->set_is_key_frame(true);
  buffer->set_duration(encoded_buffer.duration);
  auto* chunk = MakeGarbageCollected<EncodedAudioChunk>(std::move(buffer));

  auto* metadata = MakeGarbageCollected<EncodedAudioChunkMetadata>();
  if (first_output_after_configure_ || codec_desc.has_value()) {
    first_output_after_configure_ = false;
    auto* decoder_config = MakeGarbageCollected<AudioDecoderConfig>();
    decoder_config->setCodec(active_config->codec_string);
    decoder_config->setSampleRate(encoded_buffer.params.sample_rate());
    decoder_config->setNumberOfChannels(active_config->options.channels);
    if (codec_desc.has_value()) {
      auto* desc_array_buf = DOMArrayBuffer::Create(codec_desc.value());
      decoder_config->setDescription(
          MakeGarbageCollected<AllowSharedBufferSource>(desc_array_buf));
    }
    metadata->setDecoderConfig(decoder_config);
  }

  TRACE_EVENT(kCategory,
              perfetto::StaticString(GetTraceNames()->output.c_str()),
              "timestamp", chunk->timestamp());

  ScriptState::Scope scope(script_state_);
  output_callback_->InvokeAndReportException(nullptr, chunk, metadata);
}

// static
ScriptPromise<AudioEncoderSupport> AudioEncoder::isConfigSupported(
    ScriptState* script_state,
    const AudioEncoderConfig* config,
    ExceptionState& exception_state) {
  auto* parsed_config = ParseConfigStatic(config, exception_state);
  if (!parsed_config) {
    DCHECK(exception_state.HadException());
    return EmptyPromise();
  }

  String unused_js_error_message;
  auto* support = AudioEncoderSupport::Create();
  support->setSupported(
      VerifyCodecSupportStatic(parsed_config, &unused_js_error_message));
  support->setConfig(CopyConfig(*config));
  return ToResolvedPromise<AudioEncoderSupport>(script_state, support);
}

const AtomicString& AudioEncoder::InterfaceName() const {
  return event_target_names::kAudioEncoder;
}

bool AudioEncoder::HasPendingActivity() const {
  return (active_encodes_ > 0) || Base::HasPendingActivity();
}

DOMException* AudioEncoder::MakeOperationError(std::string error_msg,
                                               media::EncoderStatus status) {
  if (is_platform_encoder_) {
    return logger_->MakeOperationError(std::move(error_msg), std::move(status));
  }
  return logger_->MakeSoftwareCodecOperationError(std::move(error_msg),
                                                  std::move(status));
}

DOMException* AudioEncoder::MakeEncodingError(std::string error_msg,
                                              media::EncoderStatus status) {
  if (is_platform_encoder_) {
    return logger_->MakeEncodingError(std::move(error_msg), std::move(status));
  }
  return logger_->MakeSoftwareCodecEncodingError(std::move(error_msg),
                                                 std::move(status));
}

}  // namespace blink
