/*
 *  Copyright 2021 The WebRTC project authors. All Rights Reserved.
 *
 *  Use of this source code is governed by a BSD-style license
 *  that can be found in the LICENSE file in the root of the source
 *  tree. An additional intellectual property rights grant can be found
 *  in the file PATENTS.  All contributing project authors may
 *  be found in the AUTHORS file in the root of the source tree.
 */

#include "rtc_base/experiments/encoder_info_settings.h"

#include <algorithm>
#include <cstdint>
#include <cstdio>
#include <optional>
#include <string>
#include <vector>

#include "absl/strings/string_view.h"
#include "api/field_trials_view.h"
#include "api/video/video_codec_type.h"
#include "api/video_codecs/video_encoder.h"
#include "rtc_base/experiments/field_trial_list.h"
#include "rtc_base/experiments/field_trial_parser.h"
#include "rtc_base/logging.h"

namespace webrtc {
namespace {

std::vector<VideoEncoder::ResolutionBitrateLimits> ToResolutionBitrateLimits(
    const std::vector<EncoderInfoSettings::BitrateLimit>& limits) {
  std::vector<VideoEncoder::ResolutionBitrateLimits> result;
  for (const auto& limit : limits) {
    result.push_back(VideoEncoder::ResolutionBitrateLimits(
        limit.frame_size_pixels, limit.min_start_bitrate_bps,
        limit.min_bitrate_bps, limit.max_bitrate_bps));
  }
  return result;
}
constexpr float kDefaultMinBitratebps = 30000;
}  // namespace

// Default bitrate limits for simulcast with one active stream:
// {frame_size_pixels, min_start_bitrate_bps, min_bitrate_bps, max_bitrate_bps}.
std::vector<VideoEncoder::ResolutionBitrateLimits>
EncoderInfoSettings::GetDefaultSinglecastBitrateLimits(
    VideoCodecType codec_type) {
  if (codec_type == kVideoCodecAV1) {
    // AV1 singlecast max bitrate limits are higher than AV1 SVC max limits.
    // This is because in singlecast we normally have just one receiver, BWE is
    // known end-to-end and the encode target bitrate guarantees delivery of
    // video.
    // The min bitrate limits are not used in singlecast (used in SVC/simulcast
    // to de-/activate spatial layers) and are set to zero. Send resolution in
    // singlecast is assumed to be regulated by QP-based quality scaler.
    return {
        {320 * 180, 0, 0, 256000},        {480 * 270, 176000, 0, 384000},
        {640 * 360, 256000, 0, 512000},   {960 * 540, 384000, 0, 1024000},
        {1280 * 720, 576000, 0, 1536000}, {1920 * 1080, 1000000, 0, 3700000}};
  }

  if (codec_type == kVideoCodecVP9 || codec_type == kVideoCodecH265) {
    // VP9 singlecast bitrate limits are derived ~directly from VP9 SVC bitrate
    // limits. The current max limits are unnecessarily too strict for
    // singlecast, where BWE is known end-to-end, especially for low
    // resolutions.
    // TODO(crbugs.com/39206082): Consider fine-tuning H.265 to have its own
    // bitrate settings separate from VP9.
    return {{320 * 180, 0, 30000, 150000},
            {480 * 270, 120000, 30000, 300000},
            {640 * 360, 190000, 30000, 420000},
            {960 * 540, 350000, 30000, 1000000},
            {1280 * 720, 480000, 30000, 1500000},
            {1920 * 1080, 1000000, 30000, 3700000}};
  }

  // VP8 and other codecs.
  return {{320 * 180, 0, 30000, 300000},
          {480 * 270, 200000, 30000, 500000},
          {640 * 360, 300000, 30000, 800000},
          {960 * 540, 500000, 30000, 1500000},
          {1280 * 720, 900000, 30000, 2500000},
          {1920 * 1080, 2000000, 30000, 5000000}};
}

std::optional<VideoEncoder::ResolutionBitrateLimits>
EncoderInfoSettings::GetDefaultSinglecastBitrateLimitsForResolution(
    VideoCodecType codec_type,
    int frame_size_pixels) {
  VideoEncoder::EncoderInfo info;
  info.resolution_bitrate_limits =
      GetDefaultSinglecastBitrateLimits(codec_type);
  return info.GetEncoderBitrateLimitsForResolution(frame_size_pixels);
}

// Return the suitable bitrate limits for specified resolution when qp is
// untrusted, they are experimental values.
std::vector<VideoEncoder::ResolutionBitrateLimits>
EncoderInfoSettings::GetDefaultSinglecastBitrateLimitsWhenQpIsUntrusted(
    VideoCodecType codec_type) {
  if (codec_type == kVideoCodecH265) {
    // Similar settings from the simulcast bitate limits for H.265.
    return {{0 * 0, 0, 0, 0},
            {320 * 180, 0, 30000, 150000},
            {480 * 270, 150000, 30000, 300000},
            {640 * 360, 300000, 30000, 420000},
            {960 * 540, 420000, 30000, 1000000},
            {1280 * 720, 1000000, 30000, 1500000},
            {1920 * 1080, 1500000, 30000, 3300000}};
  } else {
    // Settings for H.264. Other codecs will not work in QP-untrusted mode.
    return {{0 * 0, 0, 0, 0},
            {320 * 180, 0, 30000, 300000},
            {480 * 270, 300000, 30000, 500000},
            {640 * 360, 500000, 30000, 800000},
            {960 * 540, 800000, 30000, 1500000},
            {1280 * 720, 1500000, 30000, 2500000},
            {1920 * 1080, 2500000, 30000, 4000000}};
  }
}

// Through linear interpolation, return the bitrate limit corresponding to the
// specified |frame_size_pixels|.
std::optional<VideoEncoder::ResolutionBitrateLimits>
EncoderInfoSettings::GetSinglecastBitrateLimitForResolutionWhenQpIsUntrusted(
    std::optional<int> frame_size_pixels,
    const std::vector<VideoEncoder::ResolutionBitrateLimits>&
        resolution_bitrate_limits) {
  if (!frame_size_pixels.has_value() || frame_size_pixels.value() <= 0) {
    return std::nullopt;
  }

  std::vector<VideoEncoder::ResolutionBitrateLimits> bitrate_limits =
      resolution_bitrate_limits;

  // Sort the list of bitrate limits by resolution.
  sort(bitrate_limits.begin(), bitrate_limits.end(),
       [](const VideoEncoder::ResolutionBitrateLimits& lhs,
          const VideoEncoder::ResolutionBitrateLimits& rhs) {
         return lhs.frame_size_pixels < rhs.frame_size_pixels;
       });

  if (bitrate_limits.empty()) {
    return std::nullopt;
  }

  int interpolation_index = -1;
  for (size_t i = 0; i < bitrate_limits.size(); ++i) {
    if (bitrate_limits[i].frame_size_pixels >= frame_size_pixels.value()) {
      interpolation_index = i;
      break;
    }
  }

  // -1 means that the maximum resolution is exceeded, we will select the
  // largest data as the return result.
  if (interpolation_index == -1) {
    return *bitrate_limits.rbegin();
  }

  // If we have a matching resolution, return directly without interpolation.
  if (bitrate_limits[interpolation_index].frame_size_pixels ==
      frame_size_pixels.value()) {
    return bitrate_limits[interpolation_index];
  }

  // 0 means our resolution is smaller than the smallest resolution in the list,
  // we will select smallest data as the return result.
  if (interpolation_index == 0) {
    return *bitrate_limits.begin();
  }

  // No matching resolution, do a linear interpolate.
  int lower_pixel_count =
      bitrate_limits[interpolation_index - 1].frame_size_pixels;
  int upper_pixel_count = bitrate_limits[interpolation_index].frame_size_pixels;
  float alpha = (frame_size_pixels.value() - lower_pixel_count) * 1.0 /
                (upper_pixel_count - lower_pixel_count);
  int min_start_bitrate_bps = static_cast<int>(
      bitrate_limits[interpolation_index].min_start_bitrate_bps * alpha +
      bitrate_limits[interpolation_index - 1].min_start_bitrate_bps *
          (1.0 - alpha));
  int max_bitrate_bps = static_cast<int>(
      bitrate_limits[interpolation_index].max_bitrate_bps * alpha +
      bitrate_limits[interpolation_index - 1].max_bitrate_bps * (1.0 - alpha));

  if (max_bitrate_bps >= min_start_bitrate_bps) {
    return VideoEncoder::ResolutionBitrateLimits(
        frame_size_pixels.value(), min_start_bitrate_bps, kDefaultMinBitratebps,
        max_bitrate_bps);
  } else {
    RTC_LOG(LS_WARNING)
        << "BitRate interpolation calculating result is abnormal. "
        << " lower_pixel_count = " << lower_pixel_count
        << " upper_pixel_count = " << upper_pixel_count
        << " frame_size_pixels = " << frame_size_pixels.value()
        << " min_start_bitrate_bps = " << min_start_bitrate_bps
        << " min_bitrate_bps = " << kDefaultMinBitratebps
        << " max_bitrate_bps = " << max_bitrate_bps;
    return std::nullopt;
  }
}

EncoderInfoSettings::EncoderInfoSettings(const FieldTrialsView& field_trials,
                                         absl::string_view name)
    : requested_resolution_alignment_("requested_resolution_alignment"),
      apply_alignment_to_all_simulcast_layers_(
          "apply_alignment_to_all_simulcast_layers") {
  FieldTrialStructList<BitrateLimit> bitrate_limits(
      {FieldTrialStructMember(
           "frame_size_pixels",
           [](BitrateLimit* b) { return &b->frame_size_pixels; }),
       FieldTrialStructMember(
           "min_start_bitrate_bps",
           [](BitrateLimit* b) { return &b->min_start_bitrate_bps; }),
       FieldTrialStructMember(
           "min_bitrate_bps",
           [](BitrateLimit* b) { return &b->min_bitrate_bps; }),
       FieldTrialStructMember(
           "max_bitrate_bps",
           [](BitrateLimit* b) { return &b->max_bitrate_bps; })},
      {});

  std::string experiment_string = field_trials.Lookup(name);
  if (experiment_string.empty()) {
    // Encoder name not found, use common string applying to all encoders.
    experiment_string = field_trials.Lookup("WebRTC-GetEncoderInfoOverride");
  }

  ParseFieldTrial({&bitrate_limits, &requested_resolution_alignment_,
                   &apply_alignment_to_all_simulcast_layers_},
                  experiment_string);

  resolution_bitrate_limits_ = ToResolutionBitrateLimits(bitrate_limits.Get());
}

std::optional<uint32_t> EncoderInfoSettings::requested_resolution_alignment()
    const {
  if (requested_resolution_alignment_ &&
      requested_resolution_alignment_.Value() < 1) {
    RTC_LOG(LS_WARNING) << "Unsupported alignment value, ignored.";
    return std::nullopt;
  }
  return requested_resolution_alignment_.GetOptional();
}

EncoderInfoSettings::~EncoderInfoSettings() {}

SimulcastEncoderAdapterEncoderInfoSettings::
    SimulcastEncoderAdapterEncoderInfoSettings(
        const FieldTrialsView& field_trials)
    : EncoderInfoSettings(
          field_trials,
          "WebRTC-SimulcastEncoderAdapter-GetEncoderInfoOverride") {}

LibvpxVp8EncoderInfoSettings::LibvpxVp8EncoderInfoSettings(
    const FieldTrialsView& field_trials)
    : EncoderInfoSettings(field_trials, "WebRTC-VP8-GetEncoderInfoOverride") {}

LibvpxVp9EncoderInfoSettings::LibvpxVp9EncoderInfoSettings(
    const FieldTrialsView& field_trials)
    : EncoderInfoSettings(field_trials, "WebRTC-VP9-GetEncoderInfoOverride") {}

LibaomAv1EncoderInfoSettings::LibaomAv1EncoderInfoSettings(
    const FieldTrialsView& field_trials)
    : EncoderInfoSettings(field_trials, "WebRTC-Av1-GetEncoderInfoOverride") {}

}  // namespace webrtc
