/*
 *  Copyright (c) 2016 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 "modules/video_coding/utility/simulcast_rate_allocator.h"

#include <limits>
#include <memory>
#include <utility>
#include <vector>

#include "api/video_codecs/vp8_frame_buffer_controller.h"
#include "api/video_codecs/vp8_frame_config.h"
#include "api/video_codecs/vp8_temporal_layers.h"
#include "rtc_base/checks.h"
#include "test/field_trial.h"
#include "test/gmock.h"
#include "test/gtest.h"

namespace webrtc {
namespace {
using ::testing::_;

constexpr uint32_t kFramerateFps = 5;
constexpr uint32_t kMinBitrateKbps = 50;
// These correspond to kLegacyScreenshareTl(0|1)BitrateKbps in cc.
constexpr uint32_t kLegacyScreenshareTargetBitrateKbps = 200;
constexpr uint32_t kLegacyScreenshareMaxBitrateKbps = 1000;
// Bitrates for upper simulcast screenshare layer.
constexpr uint32_t kSimulcastScreenshareMinBitrateKbps = 600;
constexpr uint32_t kSimulcastScreenshareMaxBitrateKbps = 1250;
// Default video hysteresis factor: allocatable bitrate for next layer must
// exceed 20% of min setting in order to be initially turned on.
const double kDefaultHysteresis = 1.2;

class MockTemporalLayers : public Vp8FrameBufferController {
 public:
  MOCK_METHOD(Vp8FrameConfig, NextFrameConfig, (size_t, uint32_t), (override));
  MOCK_METHOD(void,
              OnRatesUpdated,
              (size_t, const std::vector<uint32_t>&, int),
              (override));
  MOCK_METHOD(Vp8EncoderConfig, UpdateConfiguration, (size_t), (override));
  MOCK_METHOD(void,
              OnEncodeDone,
              (size_t, uint32_t, size_t, bool, int, CodecSpecificInfo*),
              (override));
};
}  // namespace

class SimulcastRateAllocatorTest : public ::testing::TestWithParam<bool> {
 public:
  SimulcastRateAllocatorTest() {
    codec_.codecType = kVideoCodecVP8;
    codec_.minBitrate = kMinBitrateKbps;
    codec_.maxBitrate = kLegacyScreenshareMaxBitrateKbps;
    codec_.active = true;
    CreateAllocator();
  }
  virtual ~SimulcastRateAllocatorTest() {}

  template <size_t S>
  void ExpectEqual(uint32_t (&expected)[S],
                   const std::vector<uint32_t>& actual) {
    EXPECT_EQ(S, actual.size());
    for (size_t i = 0; i < S; ++i)
      EXPECT_EQ(expected[i], actual[i]) << "Mismatch at index " << i;
  }

  template <size_t S>
  void ExpectEqual(uint32_t (&expected)[S],
                   const VideoBitrateAllocation& actual) {
    // EXPECT_EQ(S, actual.size());
    uint32_t sum = 0;
    for (size_t i = 0; i < S; ++i) {
      uint32_t layer_bitrate = actual.GetSpatialLayerSum(i);
      if (layer_bitrate == 0) {
        EXPECT_FALSE(actual.IsSpatialLayerUsed(i));
      }
      EXPECT_EQ(expected[i] * 1000U, layer_bitrate)
          << "Mismatch at index " << i;
      sum += layer_bitrate;
    }
    EXPECT_EQ(sum, actual.get_sum_bps());
  }

  void CreateAllocator(bool legacy_conference_mode = false) {
    allocator_.reset(new SimulcastRateAllocator(codec_));
    allocator_->SetLegacyConferenceMode(legacy_conference_mode);
  }

  void SetupCodec3SL3TL(const std::vector<bool>& active_streams) {
    const size_t num_simulcast_layers = 3;
    RTC_DCHECK_GE(active_streams.size(), num_simulcast_layers);
    SetupCodec2SL3TL(active_streams);
    codec_.numberOfSimulcastStreams = num_simulcast_layers;
    codec_.simulcastStream[2].numberOfTemporalLayers = 3;
    codec_.simulcastStream[2].maxBitrate = 4000;
    codec_.simulcastStream[2].targetBitrate = 3000;
    codec_.simulcastStream[2].minBitrate = 2000;
    codec_.simulcastStream[2].active = active_streams[2];
  }

  void SetupCodec2SL3TL(const std::vector<bool>& active_streams) {
    const size_t num_simulcast_layers = 2;
    RTC_DCHECK_GE(active_streams.size(), num_simulcast_layers);
    SetupCodec1SL3TL(active_streams);
    codec_.numberOfSimulcastStreams = num_simulcast_layers;
    codec_.simulcastStream[1].numberOfTemporalLayers = 3;
    codec_.simulcastStream[1].maxBitrate = 1000;
    codec_.simulcastStream[1].targetBitrate = 500;
    codec_.simulcastStream[1].minBitrate = 50;
    codec_.simulcastStream[1].active = active_streams[1];
  }

  void SetupCodec1SL3TL(const std::vector<bool>& active_streams) {
    const size_t num_simulcast_layers = 2;
    RTC_DCHECK_GE(active_streams.size(), num_simulcast_layers);
    SetupCodec3TL();
    codec_.numberOfSimulcastStreams = num_simulcast_layers;
    codec_.simulcastStream[0].numberOfTemporalLayers = 3;
    codec_.simulcastStream[0].maxBitrate = 500;
    codec_.simulcastStream[0].targetBitrate = 100;
    codec_.simulcastStream[0].minBitrate = 10;
    codec_.simulcastStream[0].active = active_streams[0];
  }

  void SetupCodec3TL() {
    codec_.maxBitrate = 0;
    codec_.VP8()->numberOfTemporalLayers = 3;
  }

  VideoBitrateAllocation GetAllocation(uint32_t target_bitrate) {
    return allocator_->Allocate(VideoBitrateAllocationParameters(
        DataRate::KilobitsPerSec(target_bitrate), kDefaultFrameRate));
  }

  VideoBitrateAllocation GetAllocation(DataRate target_rate,
                                       DataRate stable_rate) {
    return allocator_->Allocate(VideoBitrateAllocationParameters(
        target_rate, stable_rate, kDefaultFrameRate));
  }

  DataRate MinRate(size_t layer_index) const {
    return DataRate::KilobitsPerSec(
        codec_.simulcastStream[layer_index].minBitrate);
  }

  DataRate TargetRate(size_t layer_index) const {
    return DataRate::KilobitsPerSec(
        codec_.simulcastStream[layer_index].targetBitrate);
  }

  DataRate MaxRate(size_t layer_index) const {
    return DataRate::KilobitsPerSec(
        codec_.simulcastStream[layer_index].maxBitrate);
  }

 protected:
  static const int kDefaultFrameRate = 30;
  VideoCodec codec_;
  std::unique_ptr<SimulcastRateAllocator> allocator_;
};

TEST_F(SimulcastRateAllocatorTest, NoSimulcastBelowMin) {
  uint32_t expected[] = {codec_.minBitrate};
  codec_.active = true;
  ExpectEqual(expected, GetAllocation(codec_.minBitrate - 1));
  ExpectEqual(expected, GetAllocation(1));
  ExpectEqual(expected, GetAllocation(0));
}

TEST_F(SimulcastRateAllocatorTest, NoSimulcastAboveMax) {
  uint32_t expected[] = {codec_.maxBitrate};
  codec_.active = true;
  ExpectEqual(expected, GetAllocation(codec_.maxBitrate + 1));
  ExpectEqual(expected, GetAllocation(std::numeric_limits<uint32_t>::max()));
}

TEST_F(SimulcastRateAllocatorTest, NoSimulcastNoMax) {
  const uint32_t kMax = VideoBitrateAllocation::kMaxBitrateBps / 1000;
  codec_.active = true;
  codec_.maxBitrate = 0;
  CreateAllocator();

  uint32_t expected[] = {kMax};
  ExpectEqual(expected, GetAllocation(kMax));
}

TEST_F(SimulcastRateAllocatorTest, NoSimulcastWithinLimits) {
  codec_.active = true;
  for (uint32_t bitrate = codec_.minBitrate; bitrate <= codec_.maxBitrate;
       ++bitrate) {
    uint32_t expected[] = {bitrate};
    ExpectEqual(expected, GetAllocation(bitrate));
  }
}

// Tests that when we aren't using simulcast and the codec is marked inactive no
// bitrate will be allocated.
TEST_F(SimulcastRateAllocatorTest, NoSimulcastInactive) {
  codec_.active = false;
  uint32_t expected[] = {0};
  CreateAllocator();

  ExpectEqual(expected, GetAllocation(kMinBitrateKbps - 10));
  ExpectEqual(expected, GetAllocation(kLegacyScreenshareTargetBitrateKbps));
  ExpectEqual(expected, GetAllocation(kLegacyScreenshareMaxBitrateKbps + 10));
}

TEST_F(SimulcastRateAllocatorTest, SingleSimulcastBelowMin) {
  // With simulcast, use the min bitrate from the ss spec instead of the global.
  codec_.numberOfSimulcastStreams = 1;
  const uint32_t kMin = codec_.minBitrate - 10;
  codec_.simulcastStream[0].minBitrate = kMin;
  codec_.simulcastStream[0].targetBitrate = kLegacyScreenshareTargetBitrateKbps;
  codec_.simulcastStream[0].active = true;
  CreateAllocator();

  uint32_t expected[] = {kMin};
  ExpectEqual(expected, GetAllocation(kMin - 1));
  ExpectEqual(expected, GetAllocation(1));
  ExpectEqual(expected, GetAllocation(0));
}

TEST_F(SimulcastRateAllocatorTest, SignalsBwLimited) {
  // Enough to enable all layers.
  const int kVeryBigBitrate = 100000;

  // With simulcast, use the min bitrate from the ss spec instead of the global.
  SetupCodec3SL3TL({true, true, true});
  CreateAllocator();

  EXPECT_TRUE(
      GetAllocation(codec_.simulcastStream[0].minBitrate - 10).is_bw_limited());
  EXPECT_TRUE(
      GetAllocation(codec_.simulcastStream[0].targetBitrate).is_bw_limited());
  EXPECT_TRUE(GetAllocation(codec_.simulcastStream[0].targetBitrate +
                            codec_.simulcastStream[1].minBitrate)
                  .is_bw_limited());
  EXPECT_FALSE(
      GetAllocation(
          codec_.simulcastStream[0].targetBitrate +
          codec_.simulcastStream[1].targetBitrate +
          static_cast<uint32_t>(
              codec_.simulcastStream[2].minBitrate * kDefaultHysteresis + 0.5))
          .is_bw_limited());
  EXPECT_FALSE(GetAllocation(kVeryBigBitrate).is_bw_limited());
}

TEST_F(SimulcastRateAllocatorTest, SingleSimulcastAboveMax) {
  codec_.numberOfSimulcastStreams = 1;
  codec_.simulcastStream[0].minBitrate = kMinBitrateKbps;
  const uint32_t kMax = codec_.simulcastStream[0].maxBitrate + 1000;
  codec_.simulcastStream[0].maxBitrate = kMax;
  codec_.simulcastStream[0].active = true;
  CreateAllocator();

  uint32_t expected[] = {kMax};
  ExpectEqual(expected, GetAllocation(kMax));
  ExpectEqual(expected, GetAllocation(kMax + 1));
  ExpectEqual(expected, GetAllocation(std::numeric_limits<uint32_t>::max()));
}

TEST_F(SimulcastRateAllocatorTest, SingleSimulcastWithinLimits) {
  codec_.numberOfSimulcastStreams = 1;
  codec_.simulcastStream[0].minBitrate = kMinBitrateKbps;
  codec_.simulcastStream[0].targetBitrate = kLegacyScreenshareTargetBitrateKbps;
  codec_.simulcastStream[0].maxBitrate = kLegacyScreenshareMaxBitrateKbps;
  codec_.simulcastStream[0].active = true;
  CreateAllocator();

  for (uint32_t bitrate = kMinBitrateKbps;
       bitrate <= kLegacyScreenshareMaxBitrateKbps; ++bitrate) {
    uint32_t expected[] = {bitrate};
    ExpectEqual(expected, GetAllocation(bitrate));
  }
}

TEST_F(SimulcastRateAllocatorTest, Regular3TLTemporalRateAllocation) {
  SetupCodec3SL3TL({true, true, true});
  CreateAllocator();

  const VideoBitrateAllocation alloc = GetAllocation(kMinBitrateKbps);
  // 40/20/40.
  EXPECT_EQ(static_cast<uint32_t>(0.4 * kMinBitrateKbps),
            alloc.GetBitrate(0, 0) / 1000);
  EXPECT_EQ(static_cast<uint32_t>(0.2 * kMinBitrateKbps),
            alloc.GetBitrate(0, 1) / 1000);
  EXPECT_EQ(static_cast<uint32_t>(0.4 * kMinBitrateKbps),
            alloc.GetBitrate(0, 2) / 1000);
}

TEST_F(SimulcastRateAllocatorTest, BaseHeavy3TLTemporalRateAllocation) {
  test::ScopedFieldTrials field_trials(
      "WebRTC-UseBaseHeavyVP8TL3RateAllocation/Enabled/");

  SetupCodec3SL3TL({true, true, true});
  CreateAllocator();

  const VideoBitrateAllocation alloc = GetAllocation(kMinBitrateKbps);
  // 60/20/20.
  EXPECT_EQ(static_cast<uint32_t>(0.6 * kMinBitrateKbps),
            alloc.GetBitrate(0, 0) / 1000);
  EXPECT_EQ(static_cast<uint32_t>(0.2 * kMinBitrateKbps),
            alloc.GetBitrate(0, 1) / 1000);
  EXPECT_EQ(static_cast<uint32_t>(0.2 * kMinBitrateKbps),
            alloc.GetBitrate(0, 2) / 1000);
}

TEST_F(SimulcastRateAllocatorTest, SingleSimulcastInactive) {
  codec_.numberOfSimulcastStreams = 1;
  codec_.simulcastStream[0].minBitrate = kMinBitrateKbps;
  codec_.simulcastStream[0].targetBitrate = kLegacyScreenshareTargetBitrateKbps;
  codec_.simulcastStream[0].maxBitrate = kLegacyScreenshareMaxBitrateKbps;
  codec_.simulcastStream[0].active = false;
  CreateAllocator();

  uint32_t expected[] = {0};
  ExpectEqual(expected, GetAllocation(kMinBitrateKbps - 10));
  ExpectEqual(expected, GetAllocation(kLegacyScreenshareTargetBitrateKbps));
  ExpectEqual(expected, GetAllocation(kLegacyScreenshareMaxBitrateKbps + 10));
}

TEST_F(SimulcastRateAllocatorTest, OneToThreeStreams) {
  SetupCodec3SL3TL({true, true, true});
  CreateAllocator();

  {
    // Single stream, min bitrate.
    const uint32_t bitrate = codec_.simulcastStream[0].minBitrate;
    uint32_t expected[] = {bitrate, 0, 0};
    ExpectEqual(expected, GetAllocation(bitrate));
  }

  {
    // Single stream at target bitrate.
    const uint32_t bitrate = codec_.simulcastStream[0].targetBitrate;
    uint32_t expected[] = {bitrate, 0, 0};
    ExpectEqual(expected, GetAllocation(bitrate));
  }

  uint32_t kMinInitialRateTwoLayers =
      codec_.simulcastStream[0].targetBitrate +
      static_cast<uint32_t>(codec_.simulcastStream[1].minBitrate *
                            kDefaultHysteresis);
  {
    // Bitrate above target for first stream, but below min for the next one.
    const uint32_t bitrate = kMinInitialRateTwoLayers - 1;
    uint32_t expected[] = {bitrate, 0, 0};
    ExpectEqual(expected, GetAllocation(bitrate));
  }

  {
    // Just enough for two streams.
    const uint32_t bitrate = kMinInitialRateTwoLayers;
    uint32_t expected[] = {
        codec_.simulcastStream[0].targetBitrate,
        kMinInitialRateTwoLayers - codec_.simulcastStream[0].targetBitrate, 0};
    ExpectEqual(expected, GetAllocation(bitrate));
  }

  {
    // Second stream maxed out, but not enough for third.
    const uint32_t bitrate = codec_.simulcastStream[0].targetBitrate +
                             codec_.simulcastStream[1].maxBitrate;
    uint32_t expected[] = {codec_.simulcastStream[0].targetBitrate,
                           codec_.simulcastStream[1].maxBitrate, 0};
    ExpectEqual(expected, GetAllocation(bitrate));
  }

  uint32_t kMinInitialRateThreeLayers =
      codec_.simulcastStream[0].targetBitrate +
      codec_.simulcastStream[1].targetBitrate +
      static_cast<uint32_t>(codec_.simulcastStream[2].minBitrate *
                            kDefaultHysteresis);
  {
    // First two streams maxed out, but not enough for third. Nowhere to put
    // remaining bits.
    const uint32_t bitrate = kMinInitialRateThreeLayers - 1;
    uint32_t expected[] = {codec_.simulcastStream[0].targetBitrate,
                           codec_.simulcastStream[1].maxBitrate, 0};
    ExpectEqual(expected, GetAllocation(bitrate));
  }

  {
    // Just enough for all three streams.
    const uint32_t bitrate = kMinInitialRateThreeLayers;
    uint32_t expected[] = {
        codec_.simulcastStream[0].targetBitrate,
        codec_.simulcastStream[1].targetBitrate,
        static_cast<uint32_t>(codec_.simulcastStream[2].minBitrate *
                              kDefaultHysteresis)};
    ExpectEqual(expected, GetAllocation(bitrate));
  }

  {
    // Third maxed out.
    const uint32_t bitrate = codec_.simulcastStream[0].targetBitrate +
                             codec_.simulcastStream[1].targetBitrate +
                             codec_.simulcastStream[2].maxBitrate;
    uint32_t expected[] = {codec_.simulcastStream[0].targetBitrate,
                           codec_.simulcastStream[1].targetBitrate,
                           codec_.simulcastStream[2].maxBitrate};
    ExpectEqual(expected, GetAllocation(bitrate));
  }

  {
    // Enough to max out all streams which will allocate the target amount to
    // the lower streams.
    const uint32_t bitrate = codec_.simulcastStream[0].maxBitrate +
                             codec_.simulcastStream[1].maxBitrate +
                             codec_.simulcastStream[2].maxBitrate;
    uint32_t expected[] = {codec_.simulcastStream[0].targetBitrate,
                           codec_.simulcastStream[1].targetBitrate,
                           codec_.simulcastStream[2].maxBitrate};
    ExpectEqual(expected, GetAllocation(bitrate));
  }
}

// If three simulcast streams that are all inactive, none of them should be
// allocated bitrate.
TEST_F(SimulcastRateAllocatorTest, ThreeStreamsInactive) {
  SetupCodec3SL3TL({false, false, false});
  CreateAllocator();

  // Just enough to allocate the min.
  const uint32_t min_bitrate = codec_.simulcastStream[0].minBitrate +
                               codec_.simulcastStream[1].minBitrate +
                               codec_.simulcastStream[2].minBitrate;
  // Enough bitrate to allocate target to all streams.
  const uint32_t target_bitrate = codec_.simulcastStream[0].targetBitrate +
                                  codec_.simulcastStream[1].targetBitrate +
                                  codec_.simulcastStream[2].targetBitrate;
  // Enough bitrate to allocate max to all streams.
  const uint32_t max_bitrate = codec_.simulcastStream[0].maxBitrate +
                               codec_.simulcastStream[1].maxBitrate +
                               codec_.simulcastStream[2].maxBitrate;
  uint32_t expected[] = {0, 0, 0};
  ExpectEqual(expected, GetAllocation(0));
  ExpectEqual(expected, GetAllocation(min_bitrate));
  ExpectEqual(expected, GetAllocation(target_bitrate));
  ExpectEqual(expected, GetAllocation(max_bitrate));
}

// If there are two simulcast streams, we expect the high active stream to be
// allocated as if it is a single active stream.
TEST_F(SimulcastRateAllocatorTest, TwoStreamsLowInactive) {
  SetupCodec2SL3TL({false, true});
  CreateAllocator();

  const uint32_t kActiveStreamMinBitrate = codec_.simulcastStream[1].minBitrate;
  const uint32_t kActiveStreamTargetBitrate =
      codec_.simulcastStream[1].targetBitrate;
  const uint32_t kActiveStreamMaxBitrate = codec_.simulcastStream[1].maxBitrate;
  {
    // Expect that the stream is always allocated its min bitrate.
    uint32_t expected[] = {0, kActiveStreamMinBitrate};
    ExpectEqual(expected, GetAllocation(0));
    ExpectEqual(expected, GetAllocation(kActiveStreamMinBitrate - 10));
    ExpectEqual(expected, GetAllocation(kActiveStreamMinBitrate));
  }

  {
    // The stream should be allocated its target bitrate.
    uint32_t expected[] = {0, kActiveStreamTargetBitrate};
    ExpectEqual(expected, GetAllocation(kActiveStreamTargetBitrate));
  }

  {
    // The stream should be allocated its max if the target input is sufficient.
    uint32_t expected[] = {0, kActiveStreamMaxBitrate};
    ExpectEqual(expected, GetAllocation(kActiveStreamMaxBitrate));
    ExpectEqual(expected, GetAllocation(std::numeric_limits<uint32_t>::max()));
  }
}

// If there are two simulcast streams, we expect the low active stream to be
// allocated as if it is a single active stream.
TEST_F(SimulcastRateAllocatorTest, TwoStreamsHighInactive) {
  SetupCodec2SL3TL({true, false});
  CreateAllocator();

  const uint32_t kActiveStreamMinBitrate = codec_.simulcastStream[0].minBitrate;
  const uint32_t kActiveStreamTargetBitrate =
      codec_.simulcastStream[0].targetBitrate;
  const uint32_t kActiveStreamMaxBitrate = codec_.simulcastStream[0].maxBitrate;
  {
    // Expect that the stream is always allocated its min bitrate.
    uint32_t expected[] = {kActiveStreamMinBitrate, 0};
    ExpectEqual(expected, GetAllocation(0));
    ExpectEqual(expected, GetAllocation(kActiveStreamMinBitrate - 10));
    ExpectEqual(expected, GetAllocation(kActiveStreamMinBitrate));
  }

  {
    // The stream should be allocated its target bitrate.
    uint32_t expected[] = {kActiveStreamTargetBitrate, 0};
    ExpectEqual(expected, GetAllocation(kActiveStreamTargetBitrate));
  }

  {
    // The stream should be allocated its max if the target input is sufficent.
    uint32_t expected[] = {kActiveStreamMaxBitrate, 0};
    ExpectEqual(expected, GetAllocation(kActiveStreamMaxBitrate));
    ExpectEqual(expected, GetAllocation(std::numeric_limits<uint32_t>::max()));
  }
}

// If there are three simulcast streams and the middle stream is inactive, the
// other two streams should be allocated bitrate the same as if they are two
// active simulcast streams.
TEST_F(SimulcastRateAllocatorTest, ThreeStreamsMiddleInactive) {
  SetupCodec3SL3TL({true, false, true});
  CreateAllocator();

  {
    const uint32_t kLowStreamMinBitrate = codec_.simulcastStream[0].minBitrate;
    // The lowest stream should always be allocated its minimum bitrate.
    uint32_t expected[] = {kLowStreamMinBitrate, 0, 0};
    ExpectEqual(expected, GetAllocation(0));
    ExpectEqual(expected, GetAllocation(kLowStreamMinBitrate - 10));
    ExpectEqual(expected, GetAllocation(kLowStreamMinBitrate));
  }

  {
    // The lowest stream gets its target bitrate.
    uint32_t expected[] = {codec_.simulcastStream[0].targetBitrate, 0, 0};
    ExpectEqual(expected,
                GetAllocation(codec_.simulcastStream[0].targetBitrate));
  }

  {
    // The lowest stream gets its max bitrate, but not enough for the high
    // stream.
    const uint32_t bitrate = codec_.simulcastStream[0].targetBitrate +
                             codec_.simulcastStream[2].minBitrate - 1;
    uint32_t expected[] = {codec_.simulcastStream[0].maxBitrate, 0, 0};
    ExpectEqual(expected, GetAllocation(bitrate));
  }

  {
    // Both active streams get allocated target bitrate.
    const uint32_t bitrate = codec_.simulcastStream[0].targetBitrate +
                             codec_.simulcastStream[2].targetBitrate;
    uint32_t expected[] = {codec_.simulcastStream[0].targetBitrate, 0,
                           codec_.simulcastStream[2].targetBitrate};
    ExpectEqual(expected, GetAllocation(bitrate));
  }

  {
    // Lowest stream gets its target bitrate, high stream gets its max bitrate.
    uint32_t bitrate = codec_.simulcastStream[0].targetBitrate +
                       codec_.simulcastStream[2].maxBitrate;
    uint32_t expected[] = {codec_.simulcastStream[0].targetBitrate, 0,
                           codec_.simulcastStream[2].maxBitrate};
    ExpectEqual(expected, GetAllocation(bitrate));
    ExpectEqual(expected, GetAllocation(bitrate + 10));
    ExpectEqual(expected, GetAllocation(std::numeric_limits<uint32_t>::max()));
  }
}

TEST_F(SimulcastRateAllocatorTest, NonConferenceModeScreenshare) {
  codec_.mode = VideoCodecMode::kScreensharing;
  SetupCodec3SL3TL({true, true, true});
  CreateAllocator();

  // Make sure we have enough bitrate for all 3 simulcast layers
  const uint32_t bitrate = codec_.simulcastStream[0].maxBitrate +
                           codec_.simulcastStream[1].maxBitrate +
                           codec_.simulcastStream[2].maxBitrate;
  const VideoBitrateAllocation alloc = GetAllocation(bitrate);

  EXPECT_EQ(alloc.GetTemporalLayerAllocation(0).size(), 3u);
  EXPECT_EQ(alloc.GetTemporalLayerAllocation(1).size(), 3u);
  EXPECT_EQ(alloc.GetTemporalLayerAllocation(2).size(), 3u);
}

TEST_F(SimulcastRateAllocatorTest, StableRate) {
  webrtc::test::ScopedFieldTrials field_trials(
      "WebRTC-StableTargetRate/"
      "enabled:true,"
      "video_hysteresis_factor:1.1/");

  SetupCodec3SL3TL({true, true, true});
  CreateAllocator();

  // Let the volatile rate always be be enough for all streams, in this test we
  // are only interested in how the stable rate affects enablement.
  const DataRate volatile_rate =
      (TargetRate(0) + TargetRate(1) + MinRate(2)) * 1.1;

  {
    // On the first call to a new SimulcastRateAllocator instance, hysteresis
    // is disabled, but stable rate still caps layers.
    uint32_t expected[] = {TargetRate(0).kbps<uint32_t>(),
                           MaxRate(1).kbps<uint32_t>()};
    ExpectEqual(expected,
                GetAllocation(volatile_rate, TargetRate(0) + MinRate(1)));
  }

  {
    // Let stable rate go to a bitrate below what is needed for two streams.
    uint32_t expected[] = {MaxRate(0).kbps<uint32_t>(), 0};
    ExpectEqual(expected,
                GetAllocation(volatile_rate, TargetRate(0) + MinRate(1) -
                                                 DataRate::BitsPerSec(1)));
  }

  {
    // Don't enable stream as we need to get up above hysteresis threshold.
    uint32_t expected[] = {MaxRate(0).kbps<uint32_t>(), 0};
    ExpectEqual(expected,
                GetAllocation(volatile_rate, TargetRate(0) + MinRate(1)));
  }

  {
    // Above threshold with hysteresis, enable second stream.
    uint32_t expected[] = {TargetRate(0).kbps<uint32_t>(),
                           MaxRate(1).kbps<uint32_t>()};
    ExpectEqual(expected, GetAllocation(volatile_rate,
                                        (TargetRate(0) + MinRate(1)) * 1.1));
  }

  {
    // Enough to enable all thee layers.
    uint32_t expected[] = {
        TargetRate(0).kbps<uint32_t>(), TargetRate(1).kbps<uint32_t>(),
        (volatile_rate - TargetRate(0) - TargetRate(1)).kbps<uint32_t>()};
    ExpectEqual(expected, GetAllocation(volatile_rate, volatile_rate));
  }

  {
    // Drop hysteresis, all three still on.
    uint32_t expected[] = {
        TargetRate(0).kbps<uint32_t>(), TargetRate(1).kbps<uint32_t>(),
        (volatile_rate - TargetRate(0) - TargetRate(1)).kbps<uint32_t>()};
    ExpectEqual(expected,
                GetAllocation(volatile_rate,
                              TargetRate(0) + TargetRate(1) + MinRate(2)));
  }
}

class ScreenshareRateAllocationTest : public SimulcastRateAllocatorTest {
 public:
  void SetupConferenceScreenshare(bool use_simulcast, bool active = true) {
    codec_.mode = VideoCodecMode::kScreensharing;
    codec_.minBitrate = kMinBitrateKbps;
    codec_.maxBitrate =
        kLegacyScreenshareMaxBitrateKbps + kSimulcastScreenshareMaxBitrateKbps;
    if (use_simulcast) {
      codec_.numberOfSimulcastStreams = 2;
      codec_.simulcastStream[0].minBitrate = kMinBitrateKbps;
      codec_.simulcastStream[0].targetBitrate =
          kLegacyScreenshareTargetBitrateKbps;
      codec_.simulcastStream[0].maxBitrate = kLegacyScreenshareMaxBitrateKbps;
      codec_.simulcastStream[0].numberOfTemporalLayers = 2;
      codec_.simulcastStream[0].active = active;

      codec_.simulcastStream[1].minBitrate =
          kSimulcastScreenshareMinBitrateKbps;
      codec_.simulcastStream[1].targetBitrate =
          kSimulcastScreenshareMaxBitrateKbps;
      codec_.simulcastStream[1].maxBitrate =
          kSimulcastScreenshareMaxBitrateKbps;
      codec_.simulcastStream[1].numberOfTemporalLayers = 2;
      codec_.simulcastStream[1].active = active;
    } else {
      codec_.numberOfSimulcastStreams = 0;
      codec_.VP8()->numberOfTemporalLayers = 2;
      codec_.active = active;
    }
  }
};

INSTANTIATE_TEST_SUITE_P(ScreenshareTest,
                         ScreenshareRateAllocationTest,
                         ::testing::Bool());

TEST_P(ScreenshareRateAllocationTest, ConferenceBitrateBelowTl0) {
  SetupConferenceScreenshare(GetParam());
  CreateAllocator(true);

  VideoBitrateAllocation allocation =
      allocator_->Allocate(VideoBitrateAllocationParameters(
          kLegacyScreenshareTargetBitrateKbps * 1000, kFramerateFps));

  // All allocation should go in TL0.
  EXPECT_EQ(kLegacyScreenshareTargetBitrateKbps, allocation.get_sum_kbps());
  EXPECT_EQ(kLegacyScreenshareTargetBitrateKbps,
            allocation.GetBitrate(0, 0) / 1000);
  EXPECT_EQ(allocation.is_bw_limited(), GetParam());
}

TEST_P(ScreenshareRateAllocationTest, ConferenceBitrateAboveTl0) {
  SetupConferenceScreenshare(GetParam());
  CreateAllocator(true);

  uint32_t target_bitrate_kbps =
      (kLegacyScreenshareTargetBitrateKbps + kLegacyScreenshareMaxBitrateKbps) /
      2;
  VideoBitrateAllocation allocation =
      allocator_->Allocate(VideoBitrateAllocationParameters(
          target_bitrate_kbps * 1000, kFramerateFps));

  // Fill TL0, then put the rest in TL1.
  EXPECT_EQ(target_bitrate_kbps, allocation.get_sum_kbps());
  EXPECT_EQ(kLegacyScreenshareTargetBitrateKbps,
            allocation.GetBitrate(0, 0) / 1000);
  EXPECT_EQ(target_bitrate_kbps - kLegacyScreenshareTargetBitrateKbps,
            allocation.GetBitrate(0, 1) / 1000);
  EXPECT_EQ(allocation.is_bw_limited(), GetParam());
}

TEST_F(ScreenshareRateAllocationTest, ConferenceBitrateAboveTl1) {
  // This test is only for the non-simulcast case.
  SetupConferenceScreenshare(false);
  CreateAllocator(true);

  VideoBitrateAllocation allocation =
      allocator_->Allocate(VideoBitrateAllocationParameters(
          kLegacyScreenshareMaxBitrateKbps * 2000, kFramerateFps));

  // Fill both TL0 and TL1, but no more.
  EXPECT_EQ(kLegacyScreenshareMaxBitrateKbps, allocation.get_sum_kbps());
  EXPECT_EQ(kLegacyScreenshareTargetBitrateKbps,
            allocation.GetBitrate(0, 0) / 1000);
  EXPECT_EQ(
      kLegacyScreenshareMaxBitrateKbps - kLegacyScreenshareTargetBitrateKbps,
      allocation.GetBitrate(0, 1) / 1000);
  EXPECT_FALSE(allocation.is_bw_limited());
}

// This tests when the screenshare is inactive it should be allocated 0 bitrate
// for all layers.
TEST_P(ScreenshareRateAllocationTest, InactiveScreenshare) {
  SetupConferenceScreenshare(GetParam(), false);
  CreateAllocator();

  // Enough bitrate for TL0 and TL1.
  uint32_t target_bitrate_kbps =
      (kLegacyScreenshareTargetBitrateKbps + kLegacyScreenshareMaxBitrateKbps) /
      2;
  VideoBitrateAllocation allocation =
      allocator_->Allocate(VideoBitrateAllocationParameters(
          target_bitrate_kbps * 1000, kFramerateFps));

  EXPECT_EQ(0U, allocation.get_sum_kbps());
}

TEST_F(ScreenshareRateAllocationTest, Hysteresis) {
  // This test is only for the simulcast case.
  SetupConferenceScreenshare(true);
  CreateAllocator();

  // The bitrate at which we would normally enable the upper simulcast stream.
  const uint32_t default_enable_rate_bps =
      codec_.simulcastStream[0].targetBitrate +
      codec_.simulcastStream[1].minBitrate;
  const uint32_t enable_rate_with_hysteresis_bps =
      (default_enable_rate_bps * 135) / 100;

  {
    // On the first call to a new SimulcastRateAllocator instance, hysteresis
    // is disabled.
    const uint32_t bitrate = default_enable_rate_bps;
    uint32_t expected[] = {codec_.simulcastStream[0].targetBitrate,
                           codec_.simulcastStream[1].minBitrate};
    ExpectEqual(expected, GetAllocation(bitrate));
  }

  {
    // Go down to a bitrate below what is needed for two streams.
    const uint32_t bitrate = default_enable_rate_bps - 1;
    uint32_t expected[] = {bitrate, 0};
    ExpectEqual(expected, GetAllocation(bitrate));
  }

  {
    // Don't enable stream as we need to get up above hysteresis threshold.
    const uint32_t bitrate = default_enable_rate_bps;
    uint32_t expected[] = {bitrate, 0};
    ExpectEqual(expected, GetAllocation(bitrate));
  }

  {
    // Above threshold, enable second stream.
    const uint32_t bitrate = enable_rate_with_hysteresis_bps;
    uint32_t expected[] = {codec_.simulcastStream[0].targetBitrate,
                           enable_rate_with_hysteresis_bps -
                               codec_.simulcastStream[0].targetBitrate};
    ExpectEqual(expected, GetAllocation(bitrate));
  }

  {
    // Go down again, still keep the second stream alive.
    const uint32_t bitrate = default_enable_rate_bps;
    uint32_t expected[] = {codec_.simulcastStream[0].targetBitrate,
                           codec_.simulcastStream[1].minBitrate};
    ExpectEqual(expected, GetAllocation(bitrate));
  }

  {
    // Go down below default enable, second stream is shut down again.
    const uint32_t bitrate = default_enable_rate_bps - 1;
    uint32_t expected[] = {bitrate, 0};
    ExpectEqual(expected, GetAllocation(bitrate));
  }

  {
    // Go up, hysteresis is blocking us again.
    const uint32_t bitrate = default_enable_rate_bps;
    uint32_t expected[] = {bitrate, 0};
    ExpectEqual(expected, GetAllocation(bitrate));
  }
}

}  // namespace webrtc
