products/Sources/formale Sprachen/C/Firefox/intl/icu/source/common/unicode/   (Firefox Browser Version 153.0.1©)  Datei vom 27.6.2026 mit Größe 7 kB image not shown  

Quelle  encode_api_test.cc

  Sprache: C
 

/*
 *  Copyright (c) 2016 The WebM 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 <array>
#include <cmath>
#include <cassert>
#include <climits>
#include <cstdint>
#include <cstdio>
#include <cstdlib>
#include <cstring>
#include <initializer_list>
#include <memory>
#include <vector>

#include "gtest/gtest.h"
#include "test/acm_random.h"
#include "test/video_source.h"
#include "test/y4m_video_source.h"
#include "test/yuv_video_source.h"

#include "./vpx_config.h"
#include "vpx/vp8cx.h"
#include "vpx/vpx_codec.h"
#include "vpx/vpx_encoder.h"
#include "vpx/vpx_image.h"
#include "vpx_mem/vpx_mem.h"

#if CONFIG_VP9_ENCODER
#include "vp9/encoder/vp9_encoder.h"
#include "vp9/encoder/vp9_firstpass_stats.h"
#endif

namespace {

vpx_codec_iface_t *kCodecIfaces[] = {
#if CONFIG_VP8_ENCODER
  &vpx_codec_vp8_cx_algo,
#endif
#if CONFIG_VP9_ENCODER
  &vpx_codec_vp9_cx_algo,
#endif
};

bool IsVP9(vpx_codec_iface_t *iface) {
  static const char kVP9Name[] = "WebM Project VP9";
  return strncmp(kVP9Name, vpx_codec_iface_name(iface), sizeof(kVP9Name) - 1) ==
         0;
}

void *Memset16(void *dest, int val, size_t length) {
  uint16_t *dest16 = reinterpret_cast<uint16_t *>(dest);
  for (size_t i = 0; i < length; i++) {
    *dest16++ = val;
  }
  return dest;
}

vpx_image_t *CreateImage(vpx_bit_depth_t bit_depth, vpx_img_fmt_t fmt,
                         unsigned int width, unsigned int height,
                         libvpx_test::ACMRandom *rng = nullptr) {
  assert(fmt != VPX_IMG_FMT_NV12);
  if (bit_depth > VPX_BITS_8) {
    fmt = static_cast<vpx_img_fmt_t>(fmt | VPX_IMG_FMT_HIGHBITDEPTH);
  }
  vpx_image_t *image = vpx_img_alloc(nullptr, fmt, width, height, 1);
  if (!image) return image;

  auto get_val = [bit_depth, &rng]() {
    if (rng != nullptr) {
      switch (bit_depth) {
        case VPX_BITS_8: return static_cast<int>(rng->Rand8());
        case VPX_BITS_10: return static_cast<int>(rng->Rand10());
        case VPX_BITS_12: return static_cast<int>(rng->Rand12());
      }
    }
    return 1 << (bit_depth - 1);
  };
  const int val_y = get_val();
  const int val_u = get_val();
  const int val_v = get_val();
  const unsigned int uv_h =
      (image->d_h + image->y_chroma_shift) >> image->y_chroma_shift;
  const unsigned int uv_w =
      (image->d_w + image->x_chroma_shift) >> image->x_chroma_shift;
  if (bit_depth > VPX_BITS_8) {
    for (unsigned int i = 0; i < image->d_h; ++i) {
      Memset16(image->planes[0] + i * image->stride[0], val_y, image->d_w);
    }
    for (unsigned int i = 0; i < uv_h; ++i) {
      Memset16(image->planes[1] + i * image->stride[1], val_u, uv_w);
      Memset16(image->planes[2] + i * image->stride[2], val_v, uv_w);
    }
  } else {
    for (unsigned int i = 0; i < image->d_h; ++i) {
      memset(image->planes[0] + i * image->stride[0], val_y, image->d_w);
    }
    for (unsigned int i = 0; i < uv_h; ++i) {
      memset(image->planes[1] + i * image->stride[1], val_u, uv_w);
      memset(image->planes[2] + i * image->stride[2], val_v, uv_w);
    }
  }

  return image;
}

void InitCodec(vpx_codec_iface_t &iface, int width, int height,
               vpx_codec_ctx_t *enc, vpx_codec_enc_cfg_t *cfg) {
  cfg->g_w = width;
  cfg->g_h = height;
  cfg->g_lag_in_frames = 0;
  cfg->g_pass = VPX_RC_ONE_PASS;
  ASSERT_EQ(vpx_codec_enc_init(enc, &iface, cfg, 0), VPX_CODEC_OK);

  ASSERT_EQ(vpx_codec_control_(enc, VP8E_SET_CPUUSED, 2), VPX_CODEC_OK);
}

// Encodes 1 frame of size |cfg.g_w| x |cfg.g_h| setting |enc|'s configuration
// to |cfg|.
void EncodeWithConfig(const vpx_codec_enc_cfg_t &cfg, vpx_codec_ctx_t *enc) {
  libvpx_test::DummyVideoSource video;
  video.SetSize(cfg.g_w, cfg.g_h);
  video.Begin();
  EXPECT_EQ(vpx_codec_enc_config_set(enc, &cfg), VPX_CODEC_OK)
      << vpx_codec_error_detail(enc);

  EXPECT_EQ(vpx_codec_encode(enc, video.img(), video.pts(), video.duration(),
                             /*flags=*/0, VPX_DL_GOOD_QUALITY),
            VPX_CODEC_OK)
      << vpx_codec_error_detail(enc);
}

TEST(EncodeAPI, InvalidParams) {
  uint8_t buf[1] = { 0 };
  vpx_image_t img;
  vpx_codec_ctx_t enc;
  vpx_codec_enc_cfg_t cfg;

  EXPECT_EQ(&img, vpx_img_wrap(&img, VPX_IMG_FMT_I420, 111, buf));

  EXPECT_EQ(VPX_CODEC_INVALID_PARAM,
            vpx_codec_enc_init(nullptr, nullptr, nullptr, 0));
  EXPECT_EQ(VPX_CODEC_INVALID_PARAM,
            vpx_codec_enc_init(&enc, nullptr, nullptr, 0));
  EXPECT_EQ(VPX_CODEC_INVALID_PARAM,
            vpx_codec_encode(nullptr, nullptr, 0000));
  EXPECT_EQ(VPX_CODEC_INVALID_PARAM,
            vpx_codec_encode(nullptr, &img, 0000));
  EXPECT_EQ(VPX_CODEC_INVALID_PARAM, vpx_codec_destroy(nullptr));
  EXPECT_EQ(VPX_CODEC_INVALID_PARAM,
            vpx_codec_enc_config_default(nullptr, nullptr, 0));
  EXPECT_EQ(VPX_CODEC_INVALID_PARAM,
            vpx_codec_enc_config_default(nullptr, &cfg, 0));
  EXPECT_NE(vpx_codec_error(nullptr), nullptr);

  for (const auto *iface : kCodecIfaces) {
    SCOPED_TRACE(vpx_codec_iface_name(iface));
    EXPECT_EQ(VPX_CODEC_INVALID_PARAM,
              vpx_codec_enc_init(nullptr, iface, nullptr, 0));
    EXPECT_EQ(VPX_CODEC_INVALID_PARAM,
              vpx_codec_enc_init(&enc, iface, nullptr, 0));
    EXPECT_EQ(VPX_CODEC_INVALID_PARAM,
              vpx_codec_enc_config_default(iface, &cfg, 1));

    EXPECT_EQ(VPX_CODEC_OK, vpx_codec_enc_config_default(iface, &cfg, 0));
    EXPECT_EQ(VPX_CODEC_OK, vpx_codec_enc_init(&enc, iface, &cfg, 0));
    EXPECT_EQ(VPX_CODEC_OK, vpx_codec_encode(&enc, nullptr, 0000));

    EXPECT_EQ(VPX_CODEC_OK, vpx_codec_destroy(&enc));
  }
}

TEST(EncodeAPI, InvalidUVStrides) {
  static const std::vector<vpx_img_fmt_t> kVp8ImageFormats = {
    VPX_IMG_FMT_YV12, VPX_IMG_FMT_I420, VPX_IMG_FMT_NV12
  };
  static const std::vector<vpx_img_fmt_t> kVp9ImageFormats = {
    VPX_IMG_FMT_YV12,   VPX_IMG_FMT_I420,   VPX_IMG_FMT_I422,
    VPX_IMG_FMT_I444,   VPX_IMG_FMT_I440,   VPX_IMG_FMT_NV12,
    VPX_IMG_FMT_I42016, VPX_IMG_FMT_I42216, VPX_IMG_FMT_I44416,
    VPX_IMG_FMT_I44016
  };
  struct UVStride {
    int u_stride;
    int v_stride;
  };
  constexpr int kWidth = 64;
  constexpr int kHeight = 64;
  static constexpr std::array<UVStride, 4> kUVStrides = {
    UVStride{ kWidth, kWidth - 1 }, UVStride{ kWidth, kWidth + 1 },
    UVStride{ kWidth - 1, kWidth }, UVStride{ kWidth + 1, kWidth }
  };
  vpx_image_t img;
  vpx_codec_ctx_t enc;
  vpx_codec_enc_cfg_t cfg;
  // Allocate a buffer large enough for a non-subsampled, high bitdepth image.
  auto buf = std::make_unique<uint8_t[]>(kWidth * kHeight * 3 * 2);

  for (const auto *iface : kCodecIfaces) {
    SCOPED_TRACE(vpx_codec_iface_name(iface));
    ASSERT_EQ(vpx_codec_enc_config_default(iface, &cfg, /*usage=*/0),
              VPX_CODEC_OK);

    for (const auto img_fmt :
         IsVP9(iface) ? kVp9ImageFormats : kVp8ImageFormats) {
      const bool high_bit_depth =
          (img_fmt & VPX_IMG_FMT_HIGHBITDEPTH) == VPX_IMG_FMT_HIGHBITDEPTH;
      if (high_bit_depth &&
          !(vpx_codec_get_caps(iface) & VPX_CODEC_CAP_HIGHBITDEPTH)) {
        break;
      }
      ASSERT_EQ(vpx_img_wrap(&img, img_fmt, kWidth, kHeight, /*stride_align=*/1,
                             buf.get()),
                &img)
          << "Unable to wrap vpx_image for format: " << img_fmt;
      const bool is_420 =
          (img.x_chroma_shift == 1 && img.y_chroma_shift == 1) ||
          img_fmt == VPX_IMG_FMT_NV12;
      // In profiles 0 and 2, only 4:2:0 format is allowed. In profiles 1 and 3,
      // all other subsampling formats are allowed. In profiles 0 and 1, only
      // bit depth 8 is allowed. In profiles 2 and 3, only bit depths 10 and 12
      // are allowed.
      cfg.g_profile = 2 * high_bit_depth + !is_420;
      cfg.g_w = kWidth;
      cfg.g_h = kHeight;
      cfg.g_bit_depth = high_bit_depth ? VPX_BITS_10 : VPX_BITS_8;
      ASSERT_EQ(
          vpx_codec_enc_init(&enc, iface, &cfg,
                             high_bit_depth ? VPX_CODEC_USE_HIGHBITDEPTH : 0),
          VPX_CODEC_OK)
          << " high_bit_depth: " << high_bit_depth;

      for (const auto uv_stride : kUVStrides) {
        const UVStride orig = { img.stride[VPX_PLANE_U],
                                img.stride[VPX_PLANE_V] };
        img.stride[VPX_PLANE_U] = uv_stride.u_stride;
        img.stride[VPX_PLANE_V] = uv_stride.v_stride;
        EXPECT_EQ(vpx_codec_encode(&enc, &img, /*pts=*/0, /*duration=*/1,
                                   /*flags=*/0, VPX_DL_REALTIME),
                  VPX_CODEC_INVALID_PARAM)
            << "Error: " << vpx_codec_error_detail(&enc)
            << ", format: " << img_fmt << ", U stride: " << uv_stride.u_stride
            << ", V stride: " << uv_stride.v_stride;

        // Ensure the encoder can recover when given valid strides.
        img.stride[VPX_PLANE_U] = orig.u_stride;
        img.stride[VPX_PLANE_V] = orig.v_stride;
        EXPECT_EQ(vpx_codec_encode(&enc, &img, /*pts=*/0, /*duration=*/1,
                                   /*flags=*/0, VPX_DL_REALTIME),
                  VPX_CODEC_OK)
            << "Error: " << vpx_codec_error_detail(&enc)
            << ", format: " << img_fmt << ", U stride: " << orig.u_stride
            << ", V stride: " << orig.v_stride;
      }

      EXPECT_EQ(
          vpx_codec_encode(&enc, /*img=*/nullptr, /*pts=*/0, /*duration=*/0,
                           /*flags=*/0, VPX_DL_REALTIME),
          VPX_CODEC_OK);
      EXPECT_EQ(vpx_codec_destroy(&enc), VPX_CODEC_OK);
    }
  }
}

TEST(EncodeAPI, InvalidImageFormats) {
  EXPECT_EQ(vpx_img_alloc(/*img=*/nullptr, VPX_IMG_FMT_NONE, /*d_w=*/32,
                          /*d_h=*/32, /*align=*/1),
            nullptr);
  EXPECT_EQ(vpx_img_alloc(/*img=*/nullptr,
                          static_cast<vpx_img_fmt_t>(VPX_IMG_FMT_NONE - 1),
                          /*d_w=*/32, /*d_h=*/32, /*align=*/1),
            nullptr);
  EXPECT_EQ(vpx_img_alloc(/*img=*/nullptr,
                          static_cast<vpx_img_fmt_t>(VPX_IMG_FMT_NV12 + 1),
                          /*d_w=*/32, /*d_h=*/32, /*align=*/1),
            nullptr);
  EXPECT_EQ(vpx_img_alloc(/*img=*/nullptr,
                          static_cast<vpx_img_fmt_t>(VPX_IMG_FMT_I44016 + 1),
                          /*d_w=*/32, /*d_h=*/32, /*align=*/1),
            nullptr);
}

TEST(EncodeAPI, HighBitDepthCapability) {
// VP8 should not claim VP9 HBD as a capability.
#if CONFIG_VP8_ENCODER
  const vpx_codec_caps_t vp8_caps = vpx_codec_get_caps(&vpx_codec_vp8_cx_algo);
  EXPECT_EQ(vp8_caps & VPX_CODEC_CAP_HIGHBITDEPTH, 0);
#endif

#if CONFIG_VP9_ENCODER
  const vpx_codec_caps_t vp9_caps = vpx_codec_get_caps(&vpx_codec_vp9_cx_algo);
#if CONFIG_VP9_HIGHBITDEPTH
  EXPECT_EQ(vp9_caps & VPX_CODEC_CAP_HIGHBITDEPTH, VPX_CODEC_CAP_HIGHBITDEPTH);
#else
  EXPECT_EQ(vp9_caps & VPX_CODEC_CAP_HIGHBITDEPTH, 0);
#endif
#endif
}

#if CONFIG_VP8_ENCODER
TEST(EncodeAPI, ImageSizeSetting) {
  const int width = 711;
  const int height = 360;
  const int bps = 12;
  vpx_image_t img;
  vpx_codec_ctx_t enc;
  vpx_codec_enc_cfg_t cfg;
  uint8_t *img_buf = reinterpret_cast<uint8_t *>(
      calloc(width * height * bps / 8sizeof(*img_buf)));
  vpx_codec_enc_config_default(vpx_codec_vp8_cx(), &cfg, 0);

  cfg.g_w = width;
  cfg.g_h = height;

  vpx_img_wrap(&img, VPX_IMG_FMT_I420, width, height, 1, img_buf);

  vpx_codec_enc_init(&enc, vpx_codec_vp8_cx(), &cfg, 0);

  EXPECT_EQ(VPX_CODEC_OK, vpx_codec_encode(&enc, &img, 0100));

  free(img_buf);

  vpx_codec_destroy(&enc);
}

// Verifies the fix for a float-cast-overflow in vp8_change_config().
//
// Causes cpi->framerate to become the largest possible value (10,000,000) in
// VP8 by setting cfg.g_timebase to 1/10000000 and passing a duration of 1 to
// vpx_codec_encode().
TEST(EncodeAPI, HugeFramerateVp8) {
  vpx_codec_iface_t *const iface = vpx_codec_vp8_cx();
  vpx_codec_enc_cfg_t cfg;
  ASSERT_EQ(vpx_codec_enc_config_default(iface, &cfg, 0), VPX_CODEC_OK);
  cfg.g_w = 271;
  cfg.g_h = 1080;
  cfg.g_timebase.num = 1;
  // Largest value (VP8's TICKS_PER_SEC) such that frame duration is nonzero (1
  // tick).
  cfg.g_timebase.den = 10000000;
  cfg.g_pass = VPX_RC_ONE_PASS;
  cfg.g_lag_in_frames = 0;
  cfg.rc_end_usage = VPX_CBR;

  vpx_codec_ctx_t enc;
  // Before we encode the first frame, cpi->framerate is set to a guess (the
  // reciprocal of cfg.g_timebase). If this guess doesn't seem reasonable
  // (> 180), cpi->framerate is set to 30.
  ASSERT_EQ(vpx_codec_enc_init(&enc, iface, &cfg, 0), VPX_CODEC_OK);

  ASSERT_EQ(vpx_codec_control(&enc, VP8E_SET_CPUUSED, -12), VPX_CODEC_OK);

  vpx_image_t *const image =
      vpx_img_alloc(nullptr, VPX_IMG_FMT_I420, cfg.g_w, cfg.g_h, 1);
  ASSERT_NE(image, nullptr);

  for (unsigned int i = 0; i < image->d_h; ++i) {
    memset(image->planes[0] + i * image->stride[0], 128, image->d_w);
  }
  const unsigned int uv_h = (image->d_h + 1) / 2;
  const unsigned int uv_w = (image->d_w + 1) / 2;
  for (unsigned int i = 0; i < uv_h; ++i) {
    memset(image->planes[1] + i * image->stride[1], 128, uv_w);
    memset(image->planes[2] + i * image->stride[2], 128, uv_w);
  }

  // Encode a frame.
  // Up to this point cpi->framerate is 30. Now pass a duration of only 1. This
  // causes cpi->framerate to become 10,000,000.
  ASSERT_EQ(vpx_codec_encode(&enc, image, 010, VPX_DL_REALTIME),
            VPX_CODEC_OK);

  // Change to the same config. Since cpi->framerate is now huge, when it is
  // used to calculate raw_target_rate (bit rate of uncompressed frames), the
  // result is likely to overflow an unsigned int.
  ASSERT_EQ(vpx_codec_enc_config_set(&enc, &cfg), VPX_CODEC_OK);

  vpx_img_free(image);
  ASSERT_EQ(vpx_codec_destroy(&enc), VPX_CODEC_OK);
}

// A test that reproduces https://crbug.com/webm/1831.
TEST(EncodeAPI, RandomPixelsVp8) {
  // Initialize libvpx encoder
  vpx_codec_iface_t *const iface = vpx_codec_vp8_cx();
  vpx_codec_enc_cfg_t cfg;
  ASSERT_EQ(vpx_codec_enc_config_default(iface, &cfg, 0), VPX_CODEC_OK);

  cfg.rc_target_bitrate = 2000;
  cfg.g_w = 1280;
  cfg.g_h = 720;

  vpx_codec_ctx_t enc;
  ASSERT_EQ(vpx_codec_enc_init(&enc, iface, &cfg, 0), VPX_CODEC_OK);

  // Generate random frame data and encode
  libvpx_test::RandomVideoSource video;
  video.SetSize(cfg.g_w, cfg.g_h);
  video.SetImageFormat(VPX_IMG_FMT_I420);
  video.Begin();
  ASSERT_EQ(vpx_codec_encode(&enc, video.img(), video.pts(), video.duration(),
                             /*flags=*/0, VPX_DL_BEST_QUALITY),
            VPX_CODEC_OK);

  // Destroy libvpx encoder
  vpx_codec_destroy(&enc);
}

// Large static_threshold: issue: 505902433.
TEST(EncodeAPI, LargeStaticThresholdVp8) {
  // Initialize libvpx encoder
  vpx_codec_iface_t *const iface = vpx_codec_vp8_cx();
  vpx_codec_enc_cfg_t cfg;
  ASSERT_EQ(vpx_codec_enc_config_default(iface, &cfg, 0), VPX_CODEC_OK);

  cfg.rc_target_bitrate = 2000;
  cfg.g_w = 1280;
  cfg.g_h = 720;

  vpx_codec_ctx_t enc;
  ASSERT_EQ(vpx_codec_enc_init(&enc, iface, &cfg, 0), VPX_CODEC_OK);

  ASSERT_EQ(vpx_codec_control(&enc, VP8E_SET_CPUUSED, -8), VPX_CODEC_OK);

  // 131071 = 1024 << 7 - 1: max value for static_threshold.
  ASSERT_EQ(vpx_codec_control(&enc, VP8E_SET_STATIC_THRESHOLD, 131072),
            VPX_CODEC_INVALID_PARAM);

  ASSERT_EQ(vpx_codec_control(&enc, VP8E_SET_STATIC_THRESHOLD, 131071),
            VPX_CODEC_OK);

  // Generate random frame data and encode
  libvpx_test::RandomVideoSource video;
  video.SetSize(cfg.g_w, cfg.g_h);
  video.SetImageFormat(VPX_IMG_FMT_I420);
  video.Begin();
  ASSERT_EQ(vpx_codec_encode(&enc, video.img(), video.pts(), video.duration(),
                             /*flags=*/0, VPX_DL_REALTIME),
            VPX_CODEC_OK);

  // Destroy libvpx encoder
  vpx_codec_destroy(&enc);
}

TEST(EncodeAPI, ChangeToL1T3AndSetBitrateVp8) {
  // Initialize libvpx encoder
  vpx_codec_iface_t *const iface = vpx_codec_vp8_cx();
  vpx_codec_enc_cfg_t cfg;
  ASSERT_EQ(vpx_codec_enc_config_default(iface, &cfg, 0), VPX_CODEC_OK);

  cfg.g_threads = 1;
  cfg.g_profile = 0;
  cfg.g_w = 1;
  cfg.g_h = 64;
  cfg.g_bit_depth = VPX_BITS_8;
  cfg.g_input_bit_depth = 8;
  cfg.g_timebase.num = 1;
  cfg.g_timebase.den = 1000000;
  cfg.g_pass = VPX_RC_ONE_PASS;
  cfg.g_lag_in_frames = 0;
  cfg.rc_dropframe_thresh = 0;  // Don't drop frames
  cfg.rc_resize_allowed = 0;
  cfg.rc_end_usage = VPX_VBR;
  cfg.rc_target_bitrate = 10;
  cfg.rc_min_quantizer = 2;
  cfg.rc_max_quantizer = 58;
  cfg.kf_mode = VPX_KF_AUTO;
  cfg.kf_min_dist = 0;
  cfg.kf_max_dist = 10000;

  vpx_codec_ctx_t enc;
  ASSERT_EQ(vpx_codec_enc_init(&enc, iface, &cfg, 0), VPX_CODEC_OK);

  ASSERT_EQ(vpx_codec_control(&enc, VP8E_SET_CPUUSED, -6), VPX_CODEC_OK);

  // Generate random frame data and encode
  uint8_t img[1 * 64 * 3 / 2];
  libvpx_test::ACMRandom rng;
  for (size_t i = 0; i < sizeof(img); ++i) {
    img[i] = rng.Rand8();
  }
  vpx_image_t img_wrapper;
  ASSERT_EQ(
      vpx_img_wrap(&img_wrapper, VPX_IMG_FMT_I420, cfg.g_w, cfg.g_h, 1, img),
      &img_wrapper);
  vpx_enc_frame_flags_t flags = VPX_EFLAG_FORCE_KF;
  ASSERT_EQ(
      vpx_codec_encode(&enc, &img_wrapper, 0500000, flags, VPX_DL_REALTIME),
      VPX_CODEC_OK);
  ASSERT_EQ(vpx_codec_encode(&enc, nullptr, -1000), VPX_CODEC_OK);

  cfg.rc_target_bitrate = 4294967;
  // Set the scalability mode to L1T3.
  cfg.ts_number_layers = 3;
  cfg.ts_periodicity = 4;
  cfg.ts_layer_id[0] = 0;
  cfg.ts_layer_id[1] = 2;
  cfg.ts_layer_id[2] = 1;
  cfg.ts_layer_id[3] = 2;
  cfg.ts_rate_decimator[0] = 4;
  cfg.ts_rate_decimator[1] = 2;
  cfg.ts_rate_decimator[2] = 1;
  // Bitrate allocation L0: 50% L1: 20% L2: 30%
  cfg.layer_target_bitrate[0] = cfg.ts_target_bitrate[0] =
      50 * cfg.rc_target_bitrate / 100;
  cfg.layer_target_bitrate[1] = cfg.ts_target_bitrate[1] =
      70 * cfg.rc_target_bitrate / 100;
  cfg.layer_target_bitrate[2] = cfg.ts_target_bitrate[2] =
      cfg.rc_target_bitrate;
  cfg.temporal_layering_mode = VP9E_TEMPORAL_LAYERING_MODE_0212;
  cfg.g_error_resilient = VPX_ERROR_RESILIENT_DEFAULT;
  ASSERT_EQ(vpx_codec_enc_config_set(&enc, &cfg), VPX_CODEC_OK);

  ASSERT_EQ(vpx_codec_control(&enc, VP8E_SET_TEMPORAL_LAYER_ID, 2),
            VPX_CODEC_OK);

  constexpr vpx_enc_frame_flags_t VP8_UPDATE_NOTHING =
      VP8_EFLAG_NO_UPD_ARF | VP8_EFLAG_NO_UPD_GF | VP8_EFLAG_NO_UPD_LAST;
  // Layer 2: only reference last frame, no updates
  // It only depends on layer 0
  flags = VP8_UPDATE_NOTHING | VP8_EFLAG_NO_REF_ARF | VP8_EFLAG_NO_REF_GF;
  ASSERT_EQ(
      vpx_codec_encode(&enc, &img_wrapper, 0500000, flags, VPX_DL_REALTIME),
      VPX_CODEC_OK);

  // Destroy libvpx encoder
  vpx_codec_destroy(&enc);
}

// Emulates the WebCodecs VideoEncoder interface.
class VP8Encoder {
 public:
  explicit VP8Encoder(int speed) : speed_(speed) {}
  ~VP8Encoder();

  void Configure(unsigned int threads, unsigned int width, unsigned int height,
                 vpx_rc_mode end_usage, vpx_enc_deadline_t deadline);
  void Encode(bool key_frame);

 private:
  const int speed_;
  bool initialized_ = false;
  vpx_codec_enc_cfg_t cfg_;
  vpx_codec_ctx_t enc_;
  int frame_index_ = 0;
  vpx_enc_deadline_t deadline_ = 0;
};

VP8Encoder::~VP8Encoder() {
  if (initialized_) {
    EXPECT_EQ(vpx_codec_destroy(&enc_), VPX_CODEC_OK);
  }
}

void VP8Encoder::Configure(unsigned int threads, unsigned int width,
                           unsigned int height, vpx_rc_mode end_usage,
                           vpx_enc_deadline_t deadline) {
  deadline_ = deadline;

  if (!initialized_) {
    vpx_codec_iface_t *const iface = vpx_codec_vp8_cx();
    ASSERT_EQ(vpx_codec_enc_config_default(iface, &cfg_, /*usage=*/0),
              VPX_CODEC_OK);
    cfg_.g_threads = threads;
    cfg_.g_w = width;
    cfg_.g_h = height;
    cfg_.g_timebase.num = 1;
    cfg_.g_timebase.den = 1000 * 1000;  // microseconds
    cfg_.g_pass = VPX_RC_ONE_PASS;
    cfg_.g_lag_in_frames = 0;
    cfg_.rc_end_usage = end_usage;
    cfg_.rc_min_quantizer = 2;
    cfg_.rc_max_quantizer = 58;
    ASSERT_EQ(vpx_codec_enc_init(&enc_, iface, &cfg_, 0), VPX_CODEC_OK);
    ASSERT_EQ(vpx_codec_control(&enc_, VP8E_SET_CPUUSED, speed_), VPX_CODEC_OK);
    initialized_ = true;
    return;
  }

  cfg_.g_threads = threads;
  cfg_.g_w = width;
  cfg_.g_h = height;
  cfg_.rc_end_usage = end_usage;
  ASSERT_EQ(vpx_codec_enc_config_set(&enc_, &cfg_), VPX_CODEC_OK)
      << vpx_codec_error_detail(&enc_);
}

void VP8Encoder::Encode(bool key_frame) {
  assert(initialized_);
  const vpx_codec_cx_pkt_t *pkt;
  vpx_image_t *image =
      CreateImage(VPX_BITS_8, VPX_IMG_FMT_I420, cfg_.g_w, cfg_.g_h);
  ASSERT_NE(image, nullptr);
  const vpx_enc_frame_flags_t flags = key_frame ? VPX_EFLAG_FORCE_KF : 0;
  ASSERT_EQ(vpx_codec_encode(&enc_, image, frame_index_, 1, flags, deadline_),
            VPX_CODEC_OK);
  ++frame_index_;
  vpx_codec_iter_t iter = nullptr;
  while ((pkt = vpx_codec_get_cx_data(&enc_, &iter)) != nullptr) {
    ASSERT_EQ(pkt->kind, VPX_CODEC_CX_FRAME_PKT);
    if (key_frame) {
      ASSERT_EQ(pkt->data.frame.flags & VPX_FRAME_IS_KEY, VPX_FRAME_IS_KEY);
    }
  }
  vpx_img_free(image);
}

// This is the reproducer testcase for crbug.com/324459561. However,
// just running this test is not enough to reproduce the bug. We also
// need to send signals to the test.
TEST(EncodeAPI, Chromium324459561) {
  VP8Encoder encoder(-12);

  encoder.Configure(111685652, VPX_CBR, VPX_DL_REALTIME);

  encoder.Encode(true);
  encoder.Encode(true);
  encoder.Encode(true);

  encoder.Configure(016851, VPX_VBR, VPX_DL_REALTIME);
}

TEST(EncodeAPI, VP8GlobalHeaders) {
  constexpr int kWidth = 320;
  constexpr int kHeight = 240;

  vpx_codec_enc_cfg_t cfg = {};
  struct Encoder {
    ~Encoder() { EXPECT_EQ(vpx_codec_destroy(&ctx), VPX_CODEC_OK); }
    vpx_codec_ctx_t ctx = {};
  } enc;

  ASSERT_EQ(vpx_codec_enc_config_default(vpx_codec_vp8_cx(), &cfg, 0),
            VPX_CODEC_OK);
  ASSERT_NO_FATAL_FAILURE(
      InitCodec(*vpx_codec_vp8_cx(), kWidth, kHeight, &enc.ctx, &cfg));
  EXPECT_EQ(vpx_codec_get_global_headers(&enc.ctx), nullptr);
  EXPECT_NO_FATAL_FAILURE(EncodeWithConfig(cfg, &enc.ctx));
  EXPECT_EQ(vpx_codec_get_global_headers(&enc.ctx), nullptr);
}

// Encode a few frames for 2 temporal layers realtime mode.
// Set duration to be very large on first frame, much smaller
// on second frames, with the timestamp (pts) parameter very
// inconsistent with the duration (i.e, pts != prev_pts + duration).
// This reproduces the issue found in the bug: 431520320.
TEST(EncodeAPI, Vp8ChromiumIssue431520320) {
  // Initialize libvpx encoder.
  vpx_codec_iface_t *const iface = vpx_codec_vp8_cx();
  vpx_codec_ctx_t enc;
  vpx_codec_enc_cfg_t cfg;

  ASSERT_EQ(vpx_codec_enc_config_default(iface, &cfg, 0), VPX_CODEC_OK);

  cfg.g_w = 320;
  cfg.g_h = 240;
  cfg.g_lag_in_frames = 0;
  cfg.rc_target_bitrate = 500;

  // 2-layers, 2-frame period.
  int ids[2] = { 01 };
  cfg.ts_periodicity = 2;
  cfg.ts_number_layers = 2;
  cfg.ts_rate_decimator[0] = 2;
  cfg.ts_rate_decimator[1] = 1;
  cfg.ts_target_bitrate[0] = 300;
  cfg.ts_target_bitrate[1] = 500;
  memcpy(cfg.ts_layer_id, ids, sizeof(ids));

  ASSERT_EQ(vpx_codec_enc_init(&enc, iface, &cfg, 0), VPX_CODEC_OK);

  // Create input image.
  vpx_image_t *const image =
      CreateImage(VPX_BITS_8, VPX_IMG_FMT_I420, cfg.g_w, cfg.g_h);
  ASSERT_NE(image, nullptr);

  // Encode first frame.
  ASSERT_EQ(
      vpx_codec_encode(&enc, image, 0/*duration=*/800000, 0, VPX_DL_REALTIME),
      VPX_CODEC_OK);

  // Encode second frame.
  ASSERT_EQ(vpx_codec_encode(&enc, image, 40000/*duration=*/40000, 0,
                             VPX_DL_REALTIME),
            VPX_CODEC_OK);

  // Encode third frame.
  ASSERT_EQ(vpx_codec_encode(&enc, image, 80000/*duration=*/40000, 0,
                             VPX_DL_REALTIME),
            VPX_CODEC_OK);

  // Free resources.
  vpx_img_free(image);
  ASSERT_EQ(vpx_codec_destroy(&enc), VPX_CODEC_OK);
}

TEST(EncodeAPI, AomediaIssue3509VbrMinSection2PercentVP8) {
  // Initialize libvpx encoder.
  vpx_codec_iface_t *const iface = vpx_codec_vp8_cx();
  vpx_codec_ctx_t enc;
  vpx_codec_enc_cfg_t cfg;

  ASSERT_EQ(vpx_codec_enc_config_default(iface, &cfg, 0), VPX_CODEC_OK);

  cfg.g_w = 1920;
  cfg.g_h = 1080;
  cfg.g_lag_in_frames = 0;
  cfg.rc_target_bitrate = 1000000;
  // Set this to more than 1 percent to cause a signed integer overflow in the
  // multiplication cpi->av_per_frame_bandwidth *
  // cpi->oxcf.two_pass_vbrmin_section in vp8_new_framerate() if the
  // multiplication is done in the `int` type.
  cfg.rc_2pass_vbr_minsection_pct = 2;

  ASSERT_EQ(vpx_codec_enc_init(&enc, iface, &cfg, 0), VPX_CODEC_OK);

  // Create input image.
  vpx_image_t *const image =
      CreateImage(VPX_BITS_8, VPX_IMG_FMT_I420, cfg.g_w, cfg.g_h);
  ASSERT_NE(image, nullptr);

  // Encode frame.
  // `duration` can go as high as 300, but the UBSan error is gone if
  // `duration` is 301 or higher.
  ASSERT_EQ(
      vpx_codec_encode(&enc, image, 0/*duration=*/300, 0, VPX_DL_REALTIME),
      VPX_CODEC_OK);

  // Free resources.
  vpx_img_free(image);
  ASSERT_EQ(vpx_codec_destroy(&enc), VPX_CODEC_OK);
}

TEST(EncodeAPI, AomediaIssue3509VbrMinSection101PercentVP8) {
  // Initialize libvpx encoder.
  vpx_codec_iface_t *const iface = vpx_codec_vp8_cx();
  vpx_codec_ctx_t enc;
  vpx_codec_enc_cfg_t cfg;

  ASSERT_EQ(vpx_codec_enc_config_default(iface, &cfg, 0), VPX_CODEC_OK);

  cfg.g_w = 1920;
  cfg.g_h = 1080;
  cfg.g_lag_in_frames = 0;
  cfg.rc_target_bitrate = 1000000;
  // Set this to more than 100 percent to cause an error when vbr_min_bits is
  // cast to `int` in vp8_new_framerate() if vbr_min_bits is not clamped to
  // INT_MAX.
  cfg.rc_2pass_vbr_minsection_pct = 101;

  ASSERT_EQ(vpx_codec_enc_init(&enc, iface, &cfg, 0), VPX_CODEC_OK);

  // Create input image.
  vpx_image_t *const image =
      CreateImage(VPX_BITS_8, VPX_IMG_FMT_I420, cfg.g_w, cfg.g_h);
  ASSERT_NE(image, nullptr);

  // Encode frame.
  // `duration` can go as high as 300, but the UBSan error is gone if
  // `duration` is 301 or higher.
  ASSERT_EQ(
      vpx_codec_encode(&enc, image, 0/*duration=*/300, 0, VPX_DL_REALTIME),
      VPX_CODEC_OK);

  // Free resources.
  vpx_img_free(image);
  ASSERT_EQ(vpx_codec_destroy(&enc), VPX_CODEC_OK);
}

// VP8 realtime mode does not support lag in frames.
#if !CONFIG_REALTIME_ONLY
TEST(EncodeAPI, OssFuzz69100) {
  // Initialize libvpx encoder.
  vpx_codec_iface_t *const iface = vpx_codec_vp8_cx();
  vpx_codec_ctx_t enc;
  vpx_codec_enc_cfg_t cfg;

  ASSERT_EQ(vpx_codec_enc_config_default(iface, &cfg, 0), VPX_CODEC_OK);

  cfg.g_w = 64;
  cfg.g_h = 64;
  cfg.g_lag_in_frames = 25;
  cfg.g_timebase.num = 1;
  cfg.g_timebase.den = 6240592;
  cfg.rc_target_bitrate = 1202607620;
  cfg.kf_max_dist = 24377;

  ASSERT_EQ(vpx_codec_enc_init(&enc, iface, &cfg, 0), VPX_CODEC_OK);

  ASSERT_EQ(vpx_codec_control(&enc, VP8E_SET_CPUUSED, 1), VPX_CODEC_OK);
  ASSERT_EQ(vpx_codec_control(&enc, VP8E_SET_ARNR_MAXFRAMES, 0), VPX_CODEC_OK);
  ASSERT_EQ(vpx_codec_control(&enc, VP8E_SET_ARNR_STRENGTH, 3), VPX_CODEC_OK);
  ASSERT_EQ(vpx_codec_control_(&enc, VP8E_SET_ARNR_TYPE, 3),
            VPX_CODEC_OK);  // deprecated
  ASSERT_EQ(vpx_codec_control(&enc, VP8E_SET_NOISE_SENSITIVITY, 0),
            VPX_CODEC_OK);
  ASSERT_EQ(vpx_codec_control(&enc, VP8E_SET_TOKEN_PARTITIONS, 0),
            VPX_CODEC_OK);
  ASSERT_EQ(vpx_codec_control(&enc, VP8E_SET_STATIC_THRESHOLD, 0),
            VPX_CODEC_OK);

  libvpx_test::RandomVideoSource video;
  video.set_limit(30);
  video.SetSize(cfg.g_w, cfg.g_h);
  video.SetImageFormat(VPX_IMG_FMT_I420);
  video.Begin();
  do {
    ASSERT_EQ(vpx_codec_encode(&enc, video.img(), video.pts(), video.duration(),
                               /*flags=*/0, VPX_DL_GOOD_QUALITY),
              VPX_CODEC_OK);
    video.Next();
  } while (video.img() != nullptr);

  ASSERT_EQ(vpx_codec_destroy(&enc), VPX_CODEC_OK);
}
#endif  // !CONFIG_REALTIME_ONLY

void EncodeOssFuzz69906(int cpu_used, vpx_enc_deadline_t deadline) {
  char str[80];
  snprintf(str, sizeof(str), "cpu_used: %d deadline: %d", cpu_used,
           static_cast<int>(deadline));
  SCOPED_TRACE(str);

  // Initialize libvpx encoder.
  vpx_codec_iface_t *const iface = vpx_codec_vp8_cx();
  vpx_codec_ctx_t enc;
  vpx_codec_enc_cfg_t cfg;

  ASSERT_EQ(vpx_codec_enc_config_default(iface, &cfg, 0), VPX_CODEC_OK);

  cfg.g_w = 4097;
  cfg.g_h = 16;
  cfg.rc_target_bitrate = 1237084865;
  cfg.kf_max_dist = 4336;

  ASSERT_EQ(vpx_codec_enc_init(&enc, iface, &cfg, 0), VPX_CODEC_OK);

  ASSERT_EQ(vpx_codec_control(&enc, VP8E_SET_CPUUSED, cpu_used), VPX_CODEC_OK);
  ASSERT_EQ(vpx_codec_control(&enc, VP8E_SET_ARNR_MAXFRAMES, 0), VPX_CODEC_OK);
  ASSERT_EQ(vpx_codec_control(&enc, VP8E_SET_ARNR_STRENGTH, 3), VPX_CODEC_OK);
  ASSERT_EQ(vpx_codec_control_(&enc, VP8E_SET_ARNR_TYPE, 3),
            VPX_CODEC_OK);  // deprecated
  ASSERT_EQ(vpx_codec_control(&enc, VP8E_SET_NOISE_SENSITIVITY, 0),
            VPX_CODEC_OK);
  ASSERT_EQ(vpx_codec_control(&enc, VP8E_SET_TOKEN_PARTITIONS, 0),
            VPX_CODEC_OK);
  ASSERT_EQ(vpx_codec_control(&enc, VP8E_SET_STATIC_THRESHOLD, 0),
            VPX_CODEC_OK);

  libvpx_test::Y4mVideoSource video("repro-oss-fuzz-69906.y4m"/*start=*/0,
                                    /*limit=*/3);
  video.Begin();
  do {
    ASSERT_EQ(vpx_codec_encode(&enc, video.img(), video.pts(), video.duration(),
                               /*flags=*/0, deadline),
              VPX_CODEC_OK);
    video.Next();
  } while (video.img() != nullptr);

  ASSERT_EQ(vpx_codec_destroy(&enc), VPX_CODEC_OK);
}

TEST(EncodeAPI, OssFuzz69906) {
  // Note the original bug report was for speed 1, good quality. The remainder
  // of the settings are for added coverage.
  for (int cpu_used = 0; cpu_used <= 5; ++cpu_used) {
    EncodeOssFuzz69906(cpu_used, VPX_DL_GOOD_QUALITY);
  }

  for (int cpu_used = -16; cpu_used <= -5; ++cpu_used) {
    EncodeOssFuzz69906(cpu_used, VPX_DL_REALTIME);
  }
}

TEST(EncodeAPI, OssFuzz471723682) {
  // Initialize libvpx encoder.
  vpx_codec_iface_t *const iface = vpx_codec_vp8_cx();
  vpx_codec_ctx_t enc;
  vpx_codec_enc_cfg_t cfg;

  ASSERT_EQ(vpx_codec_enc_config_default(iface, &cfg, 0), VPX_CODEC_OK);

  cfg.g_w = 253;
  cfg.g_h = 252;
  cfg.rc_target_bitrate = 2611345883;
  cfg.kf_max_dist = 16515082;
  cfg.g_timebase.num = 89;
  cfg.g_timebase.den = 655613;
  cfg.g_pass = VPX_RC_ONE_PASS;
  cfg.rc_dropframe_thresh = 0;

  ASSERT_EQ(vpx_codec_enc_init(&enc, iface, &cfg, 0), VPX_CODEC_OK);

  ASSERT_EQ(vpx_codec_control(&enc, VP8E_SET_CPUUSED, 1), VPX_CODEC_OK);
  ASSERT_EQ(vpx_codec_control(&enc, VP8E_SET_ARNR_MAXFRAMES, 0), VPX_CODEC_OK);
  ASSERT_EQ(vpx_codec_control(&enc, VP8E_SET_ARNR_STRENGTH, 3), VPX_CODEC_OK);
  ASSERT_EQ(vpx_codec_control_(&enc, VP8E_SET_ARNR_TYPE, 3),
            VPX_CODEC_OK);  // deprecated
  ASSERT_EQ(vpx_codec_control(&enc, VP8E_SET_NOISE_SENSITIVITY, 0),
            VPX_CODEC_OK);
  ASSERT_EQ(vpx_codec_control(&enc, VP8E_SET_TOKEN_PARTITIONS, 0),
            VPX_CODEC_OK);
  ASSERT_EQ(vpx_codec_control(&enc, VP8E_SET_STATIC_THRESHOLD, 0),
            VPX_CODEC_OK);

  libvpx_test::YUVVideoSource video(
      "repro-oss-fuzz-471723682.yuv", VPX_IMG_FMT_I420, cfg.g_w, cfg.g_h,
      /*rate_numerator=*/786684, /*rate_denominator=*/4980772,
      /*start=*/0,
      /*limit=*/8);
  video.Begin();
  do {
    ASSERT_EQ(vpx_codec_encode(&enc, video.img(), /*pts=*/0, /*duration=*/46640,
                               /*flags=*/0, VPX_DL_GOOD_QUALITY),
              VPX_CODEC_OK);
    video.Next();
  } while (video.img() != nullptr);

  ASSERT_EQ(vpx_codec_destroy(&enc), VPX_CODEC_OK);
}

TEST(EncodeAPI, Vp8TotalrateOverflow) {
  vpx_codec_enc_cfg_t cfg;
  vpx_codec_iface_t *const iface = vpx_codec_vp8_cx();

  ASSERT_EQ(vpx_codec_enc_config_default(iface, &cfg, 0), VPX_CODEC_OK);

  cfg.g_w = 1156;
  cfg.g_h = 700;
  cfg.g_threads = 8;
  cfg.g_pass = VPX_RC_ONE_PASS;

  vpx_codec_ctx_t codec;
  ASSERT_EQ(vpx_codec_enc_init(&codec, iface, &cfg, 0), VPX_CODEC_OK);

  libvpx_test::ACMRandom rng;
  vpx_image_t *const image =
      CreateImage(VPX_BITS_8, VPX_IMG_FMT_I420, cfg.g_w, cfg.g_h, &rng);
  ASSERT_NE(image, nullptr);

  EXPECT_EQ(vpx_codec_encode(&codec, image, 010, VPX_DL_GOOD_QUALITY),
            VPX_CODEC_OK);

  vpx_img_free(image);
  EXPECT_EQ(vpx_codec_destroy(&codec), VPX_CODEC_OK);
}
#endif  // CONFIG_VP8_ENCODER

// Set up 2 spatial streams with 2 temporal layers per stream, and generate
// invalid configuration by setting the temporal layer rate allocation
// (ts_target_bitrate[]) to 0 for both layers. This should fail independent of
// CONFIG_MULTI_RES_ENCODING.
TEST(EncodeAPI, MultiResEncode) {
  const int width = 1280;
  const int height = 720;
  const int width_down = width / 2;
  const int height_down = height / 2;
  const int target_bitrate = 1000;
  const int framerate = 30;

  for (const auto *iface : kCodecIfaces) {
    vpx_codec_ctx_t enc[2];
    vpx_codec_enc_cfg_t cfg[2];
    vpx_rational_t dsf[2] = { { 21 }, { 21 } };

    memset(enc, 0sizeof(enc));

    for (int i = 0; i < 2; i++) {
      vpx_codec_enc_config_default(iface, &cfg[i], 0);
    }

    /* Highest-resolution encoder settings */
    cfg[0].g_w = width;
    cfg[0].g_h = height;
    cfg[0].rc_dropframe_thresh = 0;
    cfg[0].rc_end_usage = VPX_CBR;
    cfg[0].rc_resize_allowed = 0;
    cfg[0].rc_min_quantizer = 2;
    cfg[0].rc_max_quantizer = 56;
    cfg[0].rc_undershoot_pct = 100;
    cfg[0].rc_overshoot_pct = 15;
    cfg[0].rc_buf_initial_sz = 500;
    cfg[0].rc_buf_optimal_sz = 600;
    cfg[0].rc_buf_sz = 1000;
    cfg[0].g_error_resilient = 1/* Enable error resilient mode */
    cfg[0].g_lag_in_frames = 0;

    cfg[0].kf_mode = VPX_KF_AUTO;
    cfg[0].kf_min_dist = 3000;
    cfg[0].kf_max_dist = 3000;

    cfg[0].rc_target_bitrate = target_bitrate; /* Set target bitrate */
    cfg[0].g_timebase.num = 1;                 /* Set fps */
    cfg[0].g_timebase.den = framerate;

    cfg[1] = cfg[0];
    cfg[1].rc_target_bitrate = 500;
    cfg[1].g_w = width_down;
    cfg[1].g_h = height_down;

    for (int i = 0; i < 2; i++) {
      cfg[i].ts_number_layers = 2;
      cfg[i].ts_periodicity = 2;
      cfg[i].ts_rate_decimator[0] = 2;
      cfg[i].ts_rate_decimator[1] = 1;
      cfg[i].ts_layer_id[0] = 0;
      cfg[i].ts_layer_id[1] = 1;
      // Invalid parameters.
      cfg[i].ts_target_bitrate[0] = 0;
      cfg[i].ts_target_bitrate[1] = 0;
    }

    // VP9 should report incapable, VP8 invalid for all configurations.
    EXPECT_EQ(IsVP9(iface) ? VPX_CODEC_INCAPABLE : VPX_CODEC_INVALID_PARAM,
              vpx_codec_enc_init_multi(&enc[0], iface, &cfg[0], 20, &dsf[0]));

    for (int i = 0; i < 2; i++) {
      vpx_codec_destroy(&enc[i]);
    }
  }
}

#if CONFIG_VP8_ENCODER && CONFIG_MULTI_RES_ENCODING
// Regression test for an off-by-one in vpx_codec_encode()'s multi-resolution
// loop: when the *first* iteration (highest-index encoder, smallest stream)
// returns non-OK, `break` skips `ctx--`, the post-loop `ctx++` advances ctx to
// one past the caller's array, and SAVE_STATUS(ctx, res) writes ctx->err out of
// bounds. With a heap-allocated 3-element array (matching WebRTC's
// std::vector<vpx_codec_ctx_t> encoders_), ASan reports a 4-byte
// heap-buffer-overflow WRITE at +16 past a 168-byte region.
// Bug: 513405023.
TEST(EncodeAPI, MultiResEncodeFirstIterFailOOB) {
  constexpr int kNumEnc = 3;
  // Heap-allocate exactly kNumEnc contexts, mirroring WebRTC's
  // encoders_.reserve(3); encoders_.resize(3);
  std::vector<vpx_codec_ctx_t> enc;
  enc.reserve(kNumEnc);
  enc.resize(kNumEnc);
  memset(enc.data(), 0sizeof(vpx_codec_ctx_t) * kNumEnc);

  vpx_codec_enc_cfg_t cfg[kNumEnc];
  vpx_rational_t dsf[kNumEnc] = { { 21 }, { 21 }, { 11 } };
  const int w[kNumEnc] = { 32016080 };
  const int h[kNumEnc] = { 24012060 };

  for (int i = 0; i < kNumEnc; ++i) {
    ASSERT_EQ(vpx_codec_enc_config_default(&vpx_codec_vp8_cx_algo, &cfg[i], 0),
              VPX_CODEC_OK);
    cfg[i].g_w = w[i];
    cfg[i].g_h = h[i];
    cfg[i].g_lag_in_frames = 0;
    cfg[i].rc_end_usage = VPX_CBR;
    cfg[i].rc_resize_allowed = 0;
    cfg[i].rc_target_bitrate = 300 >> i;
    cfg[i].g_timebase.num = 1;
    cfg[i].g_timebase.den = 30;
  }

  ASSERT_EQ(vpx_codec_enc_init_multi(enc.data(), &vpx_codec_vp8_cx_algo, cfg,
                                     kNumEnc, 0, dsf),
            VPX_CODEC_OK);

  // Contiguous image array; vpx_codec_encode walks img += num_enc-1.
  vpx_image_t imgs[kNumEnc];
  for (int i = 0; i < kNumEnc; ++i) {
    ASSERT_NE(vpx_img_alloc(&imgs[i], VPX_IMG_FMT_I420, w[i], h[i], 1),
              nullptr);
    memset(imgs[i].img_data, 128, imgs[i].stride[0] * h[i] * 3 / 2);
  }

  // Force the FIRST loop iteration (i = num_enc-1, ctx = &enc[2]) to fail in
  // validate_img() before any ctx-- runs: set an unsupported format on the
  // highest-index image. validate_img returns VPX_CODEC_INVALID_PARAM, the
  // loop breaks, ctx++ advances to &enc[3], and SAVE_STATUS writes
  // (&enc[3])->err — a 4-byte heap OOB write at offset 16 past the vector's
  // 168-byte backing store.
  imgs[kNumEnc - 1].fmt = VPX_IMG_FMT_I444;

  // Under ASan this line reports:
  //   heap-buffer-overflow WRITE of size 4 ... in vpx_codec_encode
  //   0 bytes to the right of (or 16 past) 168-byte region.
  EXPECT_EQ(vpx_codec_encode(enc.data(), imgs, /*pts=*/0, /*duration=*/1,
                             /*flags=*/0, VPX_DL_REALTIME),
            VPX_CODEC_INVALID_PARAM);

  for (int i = 0; i < kNumEnc; ++i) vpx_img_free(&imgs[i]);
  for (int i = kNumEnc - 1; i >= 0; --i) vpx_codec_destroy(&enc[i]);
}
#endif  // CONFIG_VP8_ENCODER && CONFIG_MULTI_RES_ENCODING

TEST(EncodeAPI, SetRoi) {
  static struct {
    vpx_codec_iface_t *iface;
    int ctrl_id;
  } kCodecs[] = {
#if CONFIG_VP8_ENCODER
    { &vpx_codec_vp8_cx_algo, VP8E_SET_ROI_MAP },
#endif
#if CONFIG_VP9_ENCODER
    { &vpx_codec_vp9_cx_algo, VP9E_SET_ROI_MAP },
#endif
  };
  constexpr int kWidth = 64;
  constexpr int kHeight = 64;

  for (const auto &codec : kCodecs) {
    SCOPED_TRACE(vpx_codec_iface_name(codec.iface));
    vpx_codec_ctx_t enc;
    vpx_codec_enc_cfg_t cfg;

    EXPECT_EQ(vpx_codec_enc_config_default(codec.iface, &cfg, 0), VPX_CODEC_OK);
    cfg.g_w = kWidth;
    cfg.g_h = kHeight;
    EXPECT_EQ(vpx_codec_enc_init(&enc, codec.iface, &cfg, 0), VPX_CODEC_OK);

    vpx_roi_map_t roi = {};
    uint8_t roi_map[kWidth * kHeight] = {};
    if (IsVP9(codec.iface)) {
      roi.rows = (cfg.g_w + 7) >> 3;
      roi.cols = (cfg.g_h + 7) >> 3;
    } else {
      roi.rows = (cfg.g_w + 15) >> 4;
      roi.cols = (cfg.g_h + 15) >> 4;
    }
    EXPECT_EQ(vpx_codec_control_(&enc, codec.ctrl_id, &roi), VPX_CODEC_OK);

    roi.roi_map = roi_map;
    // VP8 only. This value isn't range checked.
    roi.static_threshold[1] = 1000;
    roi.static_threshold[2] = UINT_MAX / 2 + 1;
    roi.static_threshold[3] = UINT_MAX;

    for (const auto delta : { -63, -10163 }) {
      for (int i = 0; i < 8; ++i) {
        roi.delta_q[i] = delta;
        roi.delta_lf[i] = delta;
        // VP9 only.
        roi.skip[i] ^= 1;
        roi.ref_frame[i] = (roi.ref_frame[i] + 1) % 4;
        EXPECT_EQ(vpx_codec_control_(&enc, codec.ctrl_id, &roi), VPX_CODEC_OK);
      }
    }

    vpx_codec_err_t expected_error;
    for (const auto delta : { -6464, INT_MIN, INT_MAX }) {
      expected_error = VPX_CODEC_INVALID_PARAM;
      for (int i = 0; i < 8; ++i) {
        roi.delta_q[i] = delta;
        // The max segment count for VP8 is 4, the remainder of the entries are
        // ignored.
        if (i >= 4 && !IsVP9(codec.iface)) expected_error = VPX_CODEC_OK;

        EXPECT_EQ(vpx_codec_control_(&enc, codec.ctrl_id, &roi), expected_error)
            << "delta_q[" << i << "]: " << delta;
        roi.delta_q[i] = 0;

        roi.delta_lf[i] = delta;
        EXPECT_EQ(vpx_codec_control_(&enc, codec.ctrl_id, &roi), expected_error)
            << "delta_lf[" << i << "]: " << delta;
        roi.delta_lf[i] = 0;
      }
    }

    // VP8 should ignore skip[] and ref_frame[] values.
    expected_error =
        IsVP9(codec.iface) ? VPX_CODEC_INVALID_PARAM : VPX_CODEC_OK;
    for (const auto skip : { -22, INT_MIN, INT_MAX }) {
      for (int i = 0; i < 8; ++i) {
        roi.skip[i] = skip;
        EXPECT_EQ(vpx_codec_control_(&enc, codec.ctrl_id, &roi), expected_error)
            << "skip[" << i << "]: " << skip;
        roi.skip[i] = 0;
      }
    }

    // VP9 allows negative values to be used to disable segmentation.
    for (int ref_frame = -3; ref_frame < 0; ++ref_frame) {
      for (int i = 0; i < 8; ++i) {
        roi.ref_frame[i] = ref_frame;
        EXPECT_EQ(vpx_codec_control_(&enc, codec.ctrl_id, &roi), VPX_CODEC_OK)
            << "ref_frame[" << i << "]: " << ref_frame;
        roi.ref_frame[i] = 0;
      }
    }

    for (const auto ref_frame : { 4, INT_MIN, INT_MAX }) {
      for (int i = 0; i < 8; ++i) {
        roi.ref_frame[i] = ref_frame;
        EXPECT_EQ(vpx_codec_control_(&enc, codec.ctrl_id, &roi), expected_error)
            << "ref_frame[" << i << "]: " << ref_frame;
        roi.ref_frame[i] = 0;
      }
    }

    EXPECT_EQ(vpx_codec_destroy(&enc), VPX_CODEC_OK);
  }
}

TEST(EncodeAPI, ConfigChangeThreadCount) {
  constexpr int kWidth = 1920;
  constexpr int kHeight = 1080;

  for (const auto *iface : kCodecIfaces) {
    SCOPED_TRACE(vpx_codec_iface_name(iface));
    for (int i = 0; i < (IsVP9(iface) ? 2 : 1); ++i) {
      vpx_codec_enc_cfg_t cfg = {};
      struct Encoder {
        ~Encoder() { EXPECT_EQ(vpx_codec_destroy(&ctx), VPX_CODEC_OK); }
        vpx_codec_ctx_t ctx = {};
      } enc;

      ASSERT_EQ(vpx_codec_enc_config_default(iface, &cfg, 0), VPX_CODEC_OK);
      EXPECT_NO_FATAL_FAILURE(
          InitCodec(*iface, kWidth, kHeight, &enc.ctx, &cfg));
      if (IsVP9(iface)) {
        EXPECT_EQ(vpx_codec_control_(&enc.ctx, VP9E_SET_TILE_COLUMNS, 6),
                  VPX_CODEC_OK);
        EXPECT_EQ(vpx_codec_control_(&enc.ctx, VP9E_SET_ROW_MT, i),
                  VPX_CODEC_OK);
      }

      for (const auto threads : { 148621 }) {
        cfg.g_threads = threads;
        EXPECT_NO_FATAL_FAILURE(EncodeWithConfig(cfg, &enc.ctx))
            << "iteration: " << i << " threads: " << threads;
      }
    }
  }
}

TEST(EncodeAPI, ConfigResizeChangeThreadCount) {
  constexpr int kInitWidth = 1024;
  constexpr int kInitHeight = 1024;

  for (const auto *iface : kCodecIfaces) {
    SCOPED_TRACE(vpx_codec_iface_name(iface));
    for (int i = 0; i < (IsVP9(iface) ? 2 : 1); ++i) {
      vpx_codec_enc_cfg_t cfg = {};
      struct Encoder {
        ~Encoder() { EXPECT_EQ(vpx_codec_destroy(&ctx), VPX_CODEC_OK); }
        vpx_codec_ctx_t ctx = {};
      } enc;

      ASSERT_EQ(vpx_codec_enc_config_default(iface, &cfg, 0), VPX_CODEC_OK);
      // Start in threaded mode to ensure resolution and thread related
      // allocations are updated correctly across changes in resolution and
      // thread counts. See https://crbug.com/1486441.
      cfg.g_threads = 4;
      EXPECT_NO_FATAL_FAILURE(
          InitCodec(*iface, kInitWidth, kInitHeight, &enc.ctx, &cfg));
      if (IsVP9(iface)) {
        EXPECT_EQ(vpx_codec_control_(&enc.ctx, VP9E_SET_TILE_COLUMNS, 6),
                  VPX_CODEC_OK);
        EXPECT_EQ(vpx_codec_control_(&enc.ctx, VP9E_SET_ROW_MT, i),
                  VPX_CODEC_OK);
      }

      cfg.g_w = 1000;
      cfg.g_h = 608;
      EXPECT_EQ(vpx_codec_enc_config_set(&enc.ctx, &cfg), VPX_CODEC_OK)
          << vpx_codec_error_detail(&enc.ctx);

      cfg.g_w = 1000;
      cfg.g_h = 720;

      for (const auto threads : { 148621 }) {
        cfg.g_threads = threads;
        EXPECT_NO_FATAL_FAILURE(EncodeWithConfig(cfg, &enc.ctx))
            << "iteration: " << i << " threads: " << threads;
      }
    }
  }
}

TEST(EncodeAPI, ConfigResizeBiggerAfterInit) {
  for (const auto *iface : kCodecIfaces) {
    SCOPED_TRACE(vpx_codec_iface_name(iface));
    vpx_codec_enc_cfg_t cfg;
    vpx_codec_ctx_t enc;

    ASSERT_EQ(vpx_codec_enc_config_default(iface, &cfg, 0), VPX_CODEC_OK);
    EXPECT_NO_FATAL_FAILURE(InitCodec(*iface, 11, &enc, &cfg));

    cfg.g_w = 1920;
    cfg.g_h = 1;
    EXPECT_EQ(vpx_codec_enc_config_set(&enc, &cfg),
              IsVP9(iface) ? VPX_CODEC_OK : VPX_CODEC_INVALID_PARAM);

    EXPECT_EQ(vpx_codec_destroy(&enc), VPX_CODEC_OK);
  }
}

TEST(EncodeAPI, ConfigResizeBiggerAfterEncode) {
  for (const auto *iface : kCodecIfaces) {
    SCOPED_TRACE(vpx_codec_iface_name(iface));
    vpx_codec_enc_cfg_t cfg;
    vpx_codec_ctx_t enc;

    ASSERT_EQ(vpx_codec_enc_config_default(iface, &cfg, 0), VPX_CODEC_OK);
    EXPECT_NO_FATAL_FAILURE(InitCodec(*iface, 11, &enc, &cfg));
    EXPECT_NO_FATAL_FAILURE(EncodeWithConfig(cfg, &enc));

    cfg.g_w = 1920;
    cfg.g_h = 1;
    EXPECT_EQ(vpx_codec_enc_config_set(&enc, &cfg),
              IsVP9(iface) ? VPX_CODEC_OK : VPX_CODEC_INVALID_PARAM);

    cfg.g_w = 1920;
    cfg.g_h = 1080;
    EXPECT_EQ(vpx_codec_enc_config_set(&enc, &cfg),
              IsVP9(iface) ? VPX_CODEC_OK : VPX_CODEC_INVALID_PARAM);

    EXPECT_EQ(vpx_codec_destroy(&enc), VPX_CODEC_OK);
  }
}

TEST(EncodeAPI, PtsSmallerThanInitialPts) {
  for (const auto *iface : kCodecIfaces) {
    // Initialize libvpx encoder.
    vpx_codec_ctx_t enc;
    vpx_codec_enc_cfg_t cfg;

    ASSERT_EQ(vpx_codec_enc_config_default(iface, &cfg, 0), VPX_CODEC_OK);

    ASSERT_EQ(vpx_codec_enc_init(&enc, iface, &cfg, 0), VPX_CODEC_OK);

    // Create input image.
    vpx_image_t *const image =
        CreateImage(VPX_BITS_8, VPX_IMG_FMT_I420, cfg.g_w, cfg.g_h);
    ASSERT_NE(image, nullptr);

    // Encode frame.
    ASSERT_EQ(vpx_codec_encode(&enc, image, 1210, VPX_DL_BEST_QUALITY),
              VPX_CODEC_OK);
    ASSERT_EQ(vpx_codec_encode(&enc, image, 1310, VPX_DL_BEST_QUALITY),
              VPX_CODEC_OK);
    // pts (10) is smaller than the initial pts (12).
    ASSERT_EQ(vpx_codec_encode(&enc, image, 1010, VPX_DL_BEST_QUALITY),
              VPX_CODEC_INVALID_PARAM);

    // Free resources.
    vpx_img_free(image);
    ASSERT_EQ(vpx_codec_destroy(&enc), VPX_CODEC_OK);
  }
}

TEST(EncodeAPI, PtsOrDurationTooBig) {
  for (const auto *iface : kCodecIfaces) {
    // Initialize libvpx encoder.
    vpx_codec_ctx_t enc;
    vpx_codec_enc_cfg_t cfg;

    ASSERT_EQ(vpx_codec_enc_config_default(iface, &cfg, 0), VPX_CODEC_OK);

    ASSERT_EQ(vpx_codec_enc_init(&enc, iface, &cfg, 0), VPX_CODEC_OK);

    // Create input image.
    vpx_image_t *const image =
        CreateImage(VPX_BITS_8, VPX_IMG_FMT_I420, cfg.g_w, cfg.g_h);
    ASSERT_NE(image, nullptr);

    // Encode frame.
    ASSERT_EQ(vpx_codec_encode(&enc, image, 010, VPX_DL_BEST_QUALITY),
              VPX_CODEC_OK);
#if ULONG_MAX > INT64_MAX
    // duration is too big.
    ASSERT_EQ(vpx_codec_encode(&enc, image, 0, (1ul << 63), 02),
              VPX_CODEC_INVALID_PARAM);
#endif
    // pts, when converted to ticks, is too big.
    ASSERT_EQ(vpx_codec_encode(&enc, image, INT64_MAX / 1000000 + 110,
                               VPX_DL_BEST_QUALITY),
              VPX_CODEC_INVALID_PARAM);
#if ULONG_MAX > INT64_MAX
    // duration is too big.
    ASSERT_EQ(
        vpx_codec_encode(&enc, image, 0, (1ul << 63), 0, VPX_DL_BEST_QUALITY),
        VPX_CODEC_INVALID_PARAM);
    // pts + duration is too big.
    ASSERT_EQ(
        vpx_codec_encode(&enc, image, 1, INT64_MAX, 0, VPX_DL_BEST_QUALITY),
        VPX_CODEC_INVALID_PARAM);
#endif
    // pts + duration, when converted to ticks, is too big.
#if ULONG_MAX > INT64_MAX
    ASSERT_EQ(vpx_codec_encode(&enc, image, 00xbd6b566b15c7, 0,
                               VPX_DL_BEST_QUALITY),
              VPX_CODEC_INVALID_PARAM);
#endif
    ASSERT_EQ(vpx_codec_encode(&enc, image, INT64_MAX / 100000010,
                               VPX_DL_BEST_QUALITY),
              VPX_CODEC_INVALID_PARAM);

    // Free resources.
    vpx_img_free(image);
    ASSERT_EQ(vpx_codec_destroy(&enc), VPX_CODEC_OK);
  }
}

TEST(EncodeAPI, PerFramePsnr) {
  for (const auto *iface : kCodecIfaces) {
    SCOPED_TRACE(vpx_codec_iface_name(iface));
    vpx_codec_enc_cfg_t cfg;
    ASSERT_EQ(vpx_codec_enc_config_default(iface, &cfg, 0), VPX_CODEC_OK);
    cfg.g_lag_in_frames = 0;

    vpx_codec_ctx_t enc;
    ASSERT_EQ(vpx_codec_enc_init(&enc, iface, &cfg, 0), VPX_CODEC_OK);

    vpx_image_t *const image =
        CreateImage(VPX_BITS_8, VPX_IMG_FMT_I420, cfg.g_w, cfg.g_h);
    ASSERT_NE(image, nullptr);

    vpx_enc_frame_flags_t psnr_flags = VPX_EFLAG_CALCULATE_PSNR;
    ASSERT_EQ(vpx_codec_encode(&enc, image, /*pts=*/0, /*duration=*/1,
                               psnr_flags, VPX_DL_REALTIME),
              VPX_CODEC_OK);

    const vpx_codec_cx_pkt_t *pkt;
    vpx_codec_iter_t iter = nullptr;
    bool had_psnr = false;
    while ((pkt = vpx_codec_get_cx_data(&enc, &iter)) != nullptr) {
      if (pkt->kind != VPX_CODEC_CX_FRAME_PKT) {
        ASSERT_EQ(pkt->kind, VPX_CODEC_PSNR_PKT);
        had_psnr = true;
      }
    }
    EXPECT_TRUE(had_psnr);

    vpx_enc_frame_flags_t no_psnr_flags = 0;
    ASSERT_EQ(vpx_codec_encode(&enc, image, /*pts=*/1, /*duration=*/1,
                               no_psnr_flags, VPX_DL_REALTIME),
              VPX_CODEC_OK);

    iter = nullptr;
    had_psnr = false;
    while ((pkt = vpx_codec_get_cx_data(&enc, &iter)) != nullptr) {
      if (pkt->kind != VPX_CODEC_CX_FRAME_PKT) {
        ASSERT_EQ(pkt->kind, VPX_CODEC_PSNR_PKT);
        had_psnr = true;
      }
    }
#if CONFIG_INTERNAL_STATS
    // CONFIG_INTERNAL_STATS unconditionally generates PSNR.
    EXPECT_TRUE(had_psnr);
#else
    EXPECT_FALSE(had_psnr);
#endif  // CONFIG_INTERNAL_STATS

    // Free resources.
    vpx_img_free(image);
    ASSERT_EQ(vpx_codec_destroy(&enc), VPX_CODEC_OK);
  }
}

#if CONFIG_VP9_ENCODER
// Frame size needed to trigger the overflow exceeds the max buffer allowed on
// 32-bit systems defined by VPX_MAX_ALLOCABLE_MEMORY
#if VPX_ARCH_X86_64 || VPX_ARCH_AARCH64
TEST(EncodeAPI, ConfigLargeTargetBitrateVp9) {
#ifdef CHROMIUM
  GTEST_SKIP() << "Under Chromium's configuration the allocator is unable"
                  "to provide the space required for the frames below.";
#else
  constexpr int kWidth = 12383;
  constexpr int kHeight = 8192;
  constexpr auto *iface = &vpx_codec_vp9_cx_algo;
  SCOPED_TRACE(vpx_codec_iface_name(iface));
  vpx_codec_enc_cfg_t cfg = {};
  struct Encoder {
    ~Encoder() { EXPECT_EQ(vpx_codec_destroy(&ctx), VPX_CODEC_OK); }
    vpx_codec_ctx_t ctx = {};
  } enc;

  ASSERT_EQ(vpx_codec_enc_config_default(iface, &cfg, 0), VPX_CODEC_OK);
  // The following setting will cause avg_frame_bandwidth in rate control to be
  // larger than INT_MAX
  cfg.rc_target_bitrate = INT_MAX;
  // Framerate 0.1 (equivalent to timebase 10) is the smallest framerate allowed
  // by libvpx
  cfg.g_timebase.den = 1;
  cfg.g_timebase.num = 10;
  EXPECT_NO_FATAL_FAILURE(InitCodec(*iface, kWidth, kHeight, &enc.ctx, &cfg))
      << "target bitrate: " << cfg.rc_target_bitrate << " framerate: "
      << static_cast<double>(cfg.g_timebase.den) / cfg.g_timebase.num;
#endif  // defined(CHROMIUM)
}
#endif  // VPX_ARCH_X86_64 || VPX_ARCH_AARCH64

// Emulates the WebCodecs VideoEncoder interface.
class VP9Encoder {
 public:
  explicit VP9Encoder(int speed)
      : speed_(speed), row_mt_(0), bit_depth_(VPX_BITS_8),
        fmt_(VPX_IMG_FMT_I420) {}
  // The image format `fmt` must not have the VPX_IMG_FMT_HIGHBITDEPTH bit set.
  // If bit_depth > 8, we will set the VPX_IMG_FMT_HIGHBITDEPTH bit before
  // passing the image format to vpx_img_alloc().
  VP9Encoder(int speed, unsigned int row_mt, vpx_bit_depth_t bit_depth,
             vpx_img_fmt_t fmt)
      : speed_(speed), row_mt_(row_mt), bit_depth_(bit_depth), fmt_(fmt) {}
  ~VP9Encoder();

  void Configure(unsigned int threads, unsigned int width, unsigned int height,
                 vpx_rc_mode end_usage, vpx_enc_deadline_t deadline);
  // If `rng` is non-null, use it to generate the image values for encoding.
  // Otherwise the midpoint of the configured bitdepth is used.
  void Encode(bool key_frame, libvpx_test::ACMRandom *rng = nullptr);

 private:
  const int speed_;
  const unsigned int row_mt_;
  const vpx_bit_depth_t bit_depth_;
  const vpx_img_fmt_t fmt_;
  bool initialized_ = false;
  vpx_codec_enc_cfg_t cfg_;
  vpx_codec_ctx_t enc_;
  int frame_index_ = 0;
  vpx_enc_deadline_t deadline_ = 0;
};

VP9Encoder::~VP9Encoder() {
  if (initialized_) {
    EXPECT_EQ(vpx_codec_destroy(&enc_), VPX_CODEC_OK);
  }
}

void VP9Encoder::Configure(unsigned int threads, unsigned int width,
                           unsigned int height, vpx_rc_mode end_usage,
                           vpx_enc_deadline_t deadline) {
  deadline_ = deadline;

  if (!initialized_) {
    ASSERT_EQ(fmt_ & VPX_IMG_FMT_HIGHBITDEPTH, 0);
    const bool high_bit_depth = bit_depth_ > VPX_BITS_8;
    const bool is_420 = fmt_ == VPX_IMG_FMT_I420;
    vpx_codec_iface_t *const iface = vpx_codec_vp9_cx();
    ASSERT_EQ(vpx_codec_enc_config_default(iface, &cfg_, /*usage=*/0),
              VPX_CODEC_OK);
    cfg_.g_threads = threads;
    // In profiles 0 and 2, only 4:2:0 format is allowed. In profiles 1 and 3,
    // all other subsampling formats are allowed. In profiles 0 and 1, only bit
    // depth 8 is allowed. In profiles 2 and 3, only bit depths 10 and 12 are
    // allowed.
    cfg_.g_profile = 2 * high_bit_depth + !is_420;
    cfg_.g_w = width;
    cfg_.g_h = height;
    cfg_.g_bit_depth = bit_depth_;
    cfg_.g_input_bit_depth = bit_depth_;
    cfg_.g_timebase.num = 1;
    cfg_.g_timebase.den = 1000 * 1000;  // microseconds
    cfg_.g_pass = VPX_RC_ONE_PASS;
    cfg_.g_lag_in_frames = 0;
    cfg_.rc_end_usage = end_usage;
    cfg_.rc_min_quantizer = 2;
    cfg_.rc_max_quantizer = 58;
    ASSERT_EQ(
        vpx_codec_enc_init(&enc_, iface, &cfg_,
                           high_bit_depth ? VPX_CODEC_USE_HIGHBITDEPTH : 0),
        VPX_CODEC_OK);
    ASSERT_EQ(vpx_codec_control(&enc_, VP8E_SET_CPUUSED, speed_), VPX_CODEC_OK);
    ASSERT_EQ(vpx_codec_control(&enc_, VP9E_SET_ROW_MT, row_mt_), VPX_CODEC_OK);
    initialized_ = true;
    return;
  }

  cfg_.g_threads = threads;
  cfg_.g_w = width;
  cfg_.g_h = height;
  cfg_.rc_end_usage = end_usage;
  ASSERT_EQ(vpx_codec_enc_config_set(&enc_, &cfg_), VPX_CODEC_OK)
      << vpx_codec_error_detail(&enc_);
}

void VP9Encoder::Encode(bool key_frame, libvpx_test::ACMRandom *rng) {
  assert(initialized_);
  const vpx_codec_cx_pkt_t *pkt;
  vpx_image_t *image = CreateImage(bit_depth_, fmt_, cfg_.g_w, cfg_.g_h, rng);
  ASSERT_NE(image, nullptr);
  const vpx_enc_frame_flags_t frame_flags = key_frame ? VPX_EFLAG_FORCE_KF : 0;
  ASSERT_EQ(
      vpx_codec_encode(&enc_, image, frame_index_, 1, frame_flags, deadline_),
      VPX_CODEC_OK);
  ++frame_index_;
  vpx_codec_iter_t iter = nullptr;
  while ((pkt = vpx_codec_get_cx_data(&enc_, &iter)) != nullptr) {
    ASSERT_EQ(pkt->kind, VPX_CODEC_CX_FRAME_PKT);
  }
  vpx_img_free(image);
}

// This is a test case from clusterfuzz.
TEST(EncodeAPI, PrevMiCheckNullptr) {
  VP9Encoder encoder(0);
  encoder.Configure(01554644, VPX_VBR, VPX_DL_REALTIME);

  // First step: encode, without forcing KF.
  encoder.Encode(false);
  // Second step: change config
  encoder.Configure(01131644, VPX_CBR, VPX_DL_GOOD_QUALITY);
  // Third step: encode, without forcing KF
  encoder.Encode(false);
}

// This is a test case from clusterfuzz: based on b/310477034.
// Encode a few frames with multiple change config calls
// with different frame sizes.
TEST(EncodeAPI, MultipleChangeConfigResize) {
  VP9Encoder encoder(3);

  // Set initial config.
  encoder.Configure(3411, VPX_VBR, VPX_DL_REALTIME);

  // Encode first frame.
  encoder.Encode(true);

  // Change config.
  encoder.Configure(16311, VPX_VBR, VPX_DL_GOOD_QUALITY);

  // Change config again.
  encoder.Configure(0171, VPX_CBR, VPX_DL_REALTIME);

  // Encode 2nd frame with new config, set delta frame.
  encoder.Encode(false);

  // Encode 3rd frame with same config, set delta frame.
  encoder.Encode(false);
}

// This is a test case from clusterfuzz: based on b/310663186.
// Encode set of frames while varying the deadline on the fly from
// good to realtime to best and back to realtime.
TEST(EncodeAPI, DynamicDeadlineChange) {
  // Use realtime speed: 5 to 9.
  VP9Encoder encoder(5);

  // Set initial config, in particular set deadline to GOOD mode.
  encoder.Configure(011, VPX_VBR, VPX_DL_GOOD_QUALITY);

  // Encode 1st frame.
  encoder.Encode(true);

  // Encode 2nd frame, delta frame.
  encoder.Encode(false);

  // Change config: change deadline to REALTIME.
  encoder.Configure(011, VPX_VBR, VPX_DL_REALTIME);

  // Encode 3rd frame with new config, set key frame.
  encoder.Encode(true);

  // Encode 4th frame with same config, delta frame.
  encoder.Encode(false);

  // Encode 5th frame with same config, key frame.
  encoder.Encode(true);

  // Change config: change deadline to BEST.
  encoder.Configure(011, VPX_VBR, VPX_DL_BEST_QUALITY);

  // Encode 6th frame with new config, set delta frame.
  encoder.Encode(false);

  // Change config: change deadline to REALTIME.
  encoder.Configure(011, VPX_VBR, VPX_DL_REALTIME);

  // Encode 7th frame with new config, set delta frame.
  encoder.Encode(false);

  // Encode 8th frame with new config, set key frame.
  encoder.Encode(true);

  // Encode 9th frame with new config, set delta frame.
  encoder.Encode(false);
}

TEST(EncodeAPI, Buganizer310340241) {
  VP9Encoder encoder(-6);

  // Set initial config, in particular set deadline to GOOD mode.
  encoder.Configure(011, VPX_VBR, VPX_DL_GOOD_QUALITY);

  // Encode 1st frame.
  encoder.Encode(true);

  // Encode 2nd frame, delta frame.
  encoder.Encode(false);

  // Change config: change deadline to REALTIME.
  encoder.Configure(011, VPX_VBR, VPX_DL_REALTIME);

  // Encode 3rd frame with new config, set key frame.
  encoder.Encode(true);
}

// This is a test case from clusterfuzz: based on b/312517065.
TEST(EncodeAPI, Buganizer312517065) {
  VP9Encoder encoder(4);
  encoder.Configure(01060437, VPX_CBR, VPX_DL_REALTIME);
  encoder.Encode(true);
  encoder.Configure(1033437, VPX_VBR, VPX_DL_GOOD_QUALITY);
  encoder.Encode(false);
  encoder.Configure(6327269, VPX_VBR, VPX_DL_GOOD_QUALITY);
  encoder.Configure(151060437, VPX_CBR, VPX_DL_REALTIME);
  encoder.Encode(false);
}

// This is a test case from clusterfuzz: based on b/311489136.
// Encode a few frames with multiple change config calls
// with different frame sizes.
TEST(EncodeAPI, Buganizer311489136) {
  VP9Encoder encoder(1);

  // Set initial config.
  encoder.Configure(121678620, VPX_VBR, VPX_DL_GOOD_QUALITY);

  // Encode first frame.
  encoder.Encode(true);

  // Change config.
  encoder.Configure(31678202, VPX_CBR, VPX_DL_GOOD_QUALITY);

  // Encode 2nd frame with new config, set delta frame.
  encoder.Encode(false);

  // Change config again.
  encoder.Configure(81037476, VPX_CBR, VPX_DL_REALTIME);

  // Encode 3rd frame with new config, set delta frame.
  encoder.Encode(false);

  // Change config again.
  encoder.Configure(0580620, VPX_CBR, VPX_DL_GOOD_QUALITY);

  // Encode 4th frame with same config, set delta frame.
  encoder.Encode(false);
}

// This is a test case from clusterfuzz: based on b/312656387.
// Encode a few frames with multiple change config calls
// with different frame sizes.
TEST(EncodeAPI, Buganizer312656387) {
  VP9Encoder encoder(1);

  // Set initial config.
  encoder.Configure(1611024, VPX_CBR, VPX_DL_REALTIME);

  // Change config.
  encoder.Configure(1511024, VPX_VBR, VPX_DL_REALTIME);

  // Encode first frame.
  encoder.Encode(true);

  // Change config again.
  encoder.Configure(141595, VPX_VBR, VPX_DL_GOOD_QUALITY);

  // Encode 2nd frame with new config.
  encoder.Encode(true);

  // Change config again.
  encoder.Configure(211024, VPX_VBR, VPX_DL_GOOD_QUALITY);

  // Encode 3rd frame with new config, set delta frame.
  encoder.Encode(false);
}

// This is a test case from clusterfuzz: based on b/310329177.
// Encode a few frames with multiple change config calls
// with different frame sizes.
TEST(EncodeAPI, Buganizer310329177) {
  VP9Encoder encoder(6);

  // Set initial config.
  encoder.Configure(10411, VPX_VBR, VPX_DL_REALTIME);

  // Encode first frame.
  encoder.Encode(true);

  // Change config.
  encoder.Configure(1611, VPX_VBR, VPX_DL_REALTIME);

  // Encode 2nd frame with new config, set delta frame.
  encoder.Encode(false);
}

// This is a test case from clusterfuzz: based on b/311394513.
// Encode a few frames with multiple change config calls
// with different frame sizes.
TEST(EncodeAPI, Buganizer311394513) {
  VP9Encoder encoder(-7);

  // Set initial config.
  encoder.Configure(059, VPX_VBR, VPX_DL_REALTIME);

  // Encode first frame.
  encoder.Encode(false);

  // Change config.
  encoder.Configure(521, VPX_VBR, VPX_DL_REALTIME);

  // Encode 2nd frame with new config.
  encoder.Encode(true);
}

TEST(EncodeAPI, Buganizer311985118) {
  VP9Encoder encoder(0);

  // Set initial config, in particular set deadline to GOOD mode.
  encoder.Configure(121678620, VPX_VBR, VPX_DL_GOOD_QUALITY);

  // Encode 1st frame.
  encoder.Encode(false);

  // Change config: change threads and width.
  encoder.Configure(01574620, VPX_VBR, VPX_DL_GOOD_QUALITY);

  // Change config: change threads, width and height.
  encoder.Configure(16837432, VPX_VBR, VPX_DL_GOOD_QUALITY);

  // Encode 2nd frame.
  encoder.Encode(false);
}

// This is a test case from clusterfuzz: based on b/314857577.
// Encode a few frames with multiple change config calls
// with different frame sizes.
TEST(EncodeAPI, Buganizer314857577) {
  VP9Encoder encoder(4);

  // Set initial config.
  encoder.Configure(121060437, VPX_VBR, VPX_DL_REALTIME);

  // Encode first frame.
  encoder.Encode(false);

  // Change config.
  encoder.Configure(1610601, VPX_CBR, VPX_DL_REALTIME);

  // Encode 2nd frame with new config.
  encoder.Encode(false);

  // Encode 3rd frame with new config.
  encoder.Encode(true);

  // Change config.
  encoder.Configure(1533437, VPX_VBR, VPX_DL_GOOD_QUALITY);

  // Encode 4th frame with new config.
  encoder.Encode(true);

  // Encode 5th frame with new config.
  encoder.Encode(false);

  // Change config.
  encoder.Configure(5327269, VPX_VBR, VPX_DL_REALTIME);

  // Change config.
  encoder.Configure(151060437, VPX_CBR, VPX_DL_REALTIME);

  // Encode 6th frame with new config.
  encoder.Encode(false);

  // Encode 7th frame with new config.
  encoder.Encode(false);

  // Change config.
  encoder.Configure(41060437, VPX_VBR, VPX_DL_REALTIME);

  // Encode 8th frame with new config.
  encoder.Encode(false);
}

TEST(EncodeAPI, Buganizer312875957PredBufferStride) {
  VP9Encoder encoder(-1);

  encoder.Configure(121678620, VPX_VBR, VPX_DL_REALTIME);
  encoder.Encode(true);
  encoder.Encode(false);
  encoder.Configure(0456486, VPX_VBR, VPX_DL_REALTIME);
  encoder.Encode(true);
  encoder.Configure(01678620, VPX_CBR, 1000000);
  encoder.Encode(false);
  encoder.Encode(false);
}

// This is a test case from clusterfuzz: based on b/311294795
// Encode a few frames with multiple change config calls
// with different frame sizes.
TEST(EncodeAPI, Buganizer311294795) {
  VP9Encoder encoder(1);

  // Set initial config.
  encoder.Configure(121678620, VPX_VBR, VPX_DL_REALTIME);

  // Encode first frame.
  encoder.Encode(false);

  // Change config.
  encoder.Configure(16632620, VPX_VBR, VPX_DL_GOOD_QUALITY);

  // Encode 2nd frame with new config
  encoder.Encode(true);

  // Change config.
  encoder.Configure(161678342, VPX_VBR, VPX_DL_GOOD_QUALITY);

  // Encode 3rd frame with new config.
  encoder.Encode(false);

  // Change config.
  encoder.Configure(01574618, VPX_VBR, VPX_DL_REALTIME);
  // Encode more frames with new config.
  encoder.Encode(false);
  encoder.Encode(false);
}

// Test case to capture assert issue triggered in
// vp9_bitstream.c for good_quality, speed 1, lossless;
// See comment#22 in issue:433941753.
TEST(EncodeAPI, AssertIssueGoodQualitySpeed1Lossless) {
  vpx_codec_iface_t *const iface = vpx_codec_vp9_cx();
  vpx_codec_ctx_t enc;
  vpx_codec_enc_cfg_t cfg;
  ASSERT_EQ(vpx_codec_enc_config_default(iface, &cfg, 0), VPX_CODEC_OK);
  cfg.g_w = 1540;
  cfg.g_h = 838;
  cfg.g_profile = 0;
  cfg.g_bit_depth = VPX_BITS_8;
  cfg.g_timebase.num = 1;
  cfg.g_timebase.den = 10000;
  cfg.g_pass = VPX_RC_ONE_PASS;
  cfg.g_lag_in_frames = 0;
  cfg.rc_end_usage = VPX_VBR;
  cfg.g_threads = 1;
  cfg.rc_target_bitrate = 10000;
  ASSERT_EQ(vpx_codec_enc_init(&enc, iface, &cfg, 0), VPX_CODEC_OK);
  ASSERT_EQ(vpx_codec_control(&enc, VP9E_SET_LOSSLESS, 1), VPX_CODEC_OK);
  ASSERT_EQ(vpx_codec_control(&enc, VP8E_SET_CPUUSED, 1), VPX_CODEC_OK);
  libvpx_test::RandomVideoSource video;
  video.SetSize(cfg.g_w, cfg.g_h);
  video.SetImageFormat(VPX_IMG_FMT_I420);
  video.set_limit(20);
  video.Begin();
  do {
    ASSERT_EQ(vpx_codec_encode(&enc, video.img(), video.pts(), video.duration(),
                               0, VPX_DL_GOOD_QUALITY),
              VPX_CODEC_OK);
    video.Next();
  } while (video.img() != nullptr);
  ASSERT_EQ(vpx_codec_destroy(&enc), VPX_CODEC_OK);
}

void FillPlane(uint8_t *plane, int stride, int width, int height,
               uint8_t value) {
  for (int y = 0; y < height; ++y) {
    memset(plane + y * stride, value, width);
  }
}

// Test case to capture ioc in vp9_caq_select_segment()
// in b/475394382
TEST(EncodeAPI, Buganizer475394382) {
  vpx_codec_iface_t *const iface = vpx_codec_vp9_cx();
  vpx_codec_ctx_t enc;
  vpx_codec_enc_cfg_t cfg;
  ASSERT_EQ(vpx_codec_enc_config_default(iface, &cfg, 0), VPX_CODEC_OK);

  cfg.g_w = 41;
  cfg.g_h = 43;
  cfg.g_timebase.num = 1;
  cfg.g_timebase.den = 30;
  cfg.g_pass = VPX_RC_ONE_PASS;
  cfg.g_lag_in_frames = 2;

  ASSERT_EQ(vpx_codec_enc_init(&enc, iface, &cfg, 0), VPX_CODEC_OK);

  ASSERT_EQ(vpx_codec_control(&enc, VP9E_SET_AQ_MODE, 2), VPX_CODEC_OK);

  cfg.rc_target_bitrate = 141452;
  cfg.g_lag_in_frames = 0;
  vpx_codec_enc_config_set(&enc, &cfg);

  vpx_image_t *img =
      vpx_img_alloc(nullptr, VPX_IMG_FMT_I420, cfg.g_w, cfg.g_h, 16);
  ASSERT_NE(img, nullptr);

  FillPlane(img->planes[VPX_PLANE_Y], img->stride[VPX_PLANE_Y], cfg.g_w,
            cfg.g_h, 0x80);
  FillPlane(img->planes[VPX_PLANE_U], img->stride[VPX_PLANE_U],
            (cfg.g_w + 1) / 2, (cfg.g_h + 1) / 20x40);
  FillPlane(img->planes[VPX_PLANE_V], img->stride[VPX_PLANE_V],
            (cfg.g_w + 1) / 2, (cfg.g_h + 1) / 20xC0);

  static constexpr std::array<vpx_enc_frame_flags_t, 3> frame_flags = {
    VPX_EFLAG_FORCE_KF, VP8_EFLAG_NO_REF_LAST | VP8_EFLAG_NO_REF_GF, 0
  };

  int frame_idx = 0;
  for (const auto flags : frame_flags) {
    vpx_codec_encode(&enc, img, frame_idx, 1, flags, VPX_DL_GOOD_QUALITY);
    vpx_codec_iter_t iter = nullptr;
    while (vpx_codec_get_cx_data(&enc, &iter)) {
    }
    ++frame_idx;
  }

  vpx_img_free(img);
  ASSERT_EQ(vpx_codec_destroy(&enc), VPX_CODEC_OK);
}

TEST(EncodeAPI, Buganizer317105128) {
  VP9Encoder encoder(-9);
  encoder.Configure(011, VPX_CBR, VPX_DL_GOOD_QUALITY);
  encoder.Configure(1619201, VPX_CBR, VPX_DL_REALTIME);
}

TEST(EncodeAPI, Buganizer319964497) {
  VP9Encoder encoder(7);
  encoder.Configure(/*threads=*/1, /*width=*/320, /*height=*/240, VPX_VBR,
                    VPX_DL_REALTIME);
  encoder.Encode(/*key_frame=*/true);
  encoder.Encode(/*key_frame=*/true);
  encoder.Encode(/*key_frame=*/false);
  encoder.Configure(/*threads=*/1, /*width=*/1, /*height=*/1, VPX_VBR,
                    VPX_DL_REALTIME);
  encoder.Encode(/*key_frame=*/false);
  encoder.Configure(/*threads=*/1, /*width=*/2, /*height=*/2, VPX_CBR,
                    VPX_DL_REALTIME);
  encoder.Encode(/*key_frame=*/false);
}

TEST(EncodeAPI, Buganizer329088759RowMT0) {
  VP9Encoder encoder(80, VPX_BITS_8, VPX_IMG_FMT_I444);
  encoder.Configure(/*threads=*/8, /*width=*/1686, /*height=*/398, VPX_VBR,
                    VPX_DL_REALTIME);
  encoder.Encode(/*key_frame=*/true);
  encoder.Encode(/*key_frame=*/false);
  encoder.Configure(/*threads=*/0, /*width=*/1686, /*height=*/1, VPX_VBR,
                    VPX_DL_REALTIME);
  encoder.Encode(/*key_frame=*/true);
  encoder.Configure(/*threads=*/0, /*width=*/1482, /*height=*/113, VPX_CBR,
                    VPX_DL_REALTIME);
  encoder.Encode(/*key_frame=*/true);
  encoder.Configure(/*threads=*/0, /*width=*/881, /*height=*/59, VPX_CBR,
                    VPX_DL_REALTIME);
  encoder.Configure(/*threads=*/13, /*width=*/1271, /*height=*/385, VPX_CBR,
                    VPX_DL_REALTIME);
  encoder.Encode(/*key_frame=*/false);
  encoder.Configure(/*threads=*/2, /*width=*/1, /*height=*/62, VPX_VBR,
                    VPX_DL_REALTIME);
}

TEST(EncodeAPI, Buganizer329088759RowMT1) {
  VP9Encoder encoder(81, VPX_BITS_8, VPX_IMG_FMT_I444);
  encoder.Configure(/*threads=*/8, /*width=*/1686, /*height=*/398, VPX_VBR,
                    VPX_DL_REALTIME);
  encoder.Encode(/*key_frame=*/true);
  encoder.Encode(/*key_frame=*/false);
  // Needs to set threads to non-zero to repro the issue.
  encoder.Configure(/*threads=*/2, /*width=*/1686, /*height=*/1, VPX_VBR,
                    VPX_DL_REALTIME);
  encoder.Encode(/*key_frame=*/true);
  encoder.Configure(/*threads=*/2, /*width=*/1482, /*height=*/113, VPX_CBR,
                    VPX_DL_REALTIME);
  encoder.Encode(/*key_frame=*/true);
  encoder.Configure(/*threads=*/2, /*width=*/881, /*height=*/59, VPX_CBR,
                    VPX_DL_REALTIME);
  encoder.Configure(/*threads=*/13, /*width=*/1271, /*height=*/385, VPX_CBR,
                    VPX_DL_REALTIME);
  encoder.Encode(/*key_frame=*/false);
  encoder.Configure(/*threads=*/2, /*width=*/1, /*height=*/62, VPX_VBR,
                    VPX_DL_REALTIME);
}

TEST(EncodeAPI, Buganizer331086799) {
  VP9Encoder encoder(61, VPX_BITS_8, VPX_IMG_FMT_I420);
  encoder.Configure(013851, VPX_CBR, VPX_DL_REALTIME);
  encoder.Configure(011, VPX_VBR, VPX_DL_REALTIME);
  encoder.Encode(false);
  encoder.Configure(1613851, VPX_VBR, VPX_DL_GOOD_QUALITY);
  encoder.Encode(false);
  encoder.Encode(false);
  encoder.Configure(011, VPX_CBR, VPX_DL_REALTIME);
  encoder.Encode(true);
}

TEST(EncodeAPI, Buganizer331108729) {
  VP9Encoder encoder(11, VPX_BITS_8, VPX_IMG_FMT_I422);
  encoder.Configure(01919260, VPX_VBR, VPX_DL_REALTIME);
  encoder.Configure(94401, VPX_CBR, VPX_DL_GOOD_QUALITY);
  encoder.Encode(true);
  encoder.Configure(81919260, VPX_VBR, VPX_DL_REALTIME);
  encoder.Encode(false);
}

TEST(EncodeAPI, Buganizer331108922BitDepth8) {
  VP9Encoder encoder(91, VPX_BITS_8, VPX_IMG_FMT_I420);
  encoder.Configure(/*threads=*/1, /*width=*/1, /*height=*/1080, VPX_VBR,
                    VPX_DL_REALTIME);
  encoder.Encode(/*key_frame=*/false);
  encoder.Configure(/*threads=*/0, /*width=*/1, /*height=*/1080, VPX_CBR,
                    VPX_DL_GOOD_QUALITY);
  encoder.Configure(/*threads=*/16, /*width=*/1, /*height=*/394, VPX_CBR,
                    VPX_DL_REALTIME);
  encoder.Encode(/*key_frame=*/false);
  encoder.Encode(/*key_frame=*/true);
  encoder.Configure(/*threads=*/16, /*width=*/1, /*height=*/798, VPX_CBR,
                    VPX_DL_REALTIME);
  encoder.Encode(/*key_frame=*/false);
}

// Encode some frames, flip from BEST_QUALITY to REALTIME after 2 frames.
// This test is taken from the code snippet in issue:441668134.
TEST(EncodeAPI, Buganizer441668134) {
  // Get VP9 encoder interface.
  vpx_codec_iface_t *iface = vpx_codec_vp9_cx();
  // Initialize encoder configuration with default values.
  vpx_codec_enc_cfg_t cfg;
  ASSERT_EQ(vpx_codec_enc_config_default(iface, &cfg, 0), VPX_CODEC_OK);
  cfg.g_lag_in_frames = 0;
  cfg.rc_max_quantizer = 0;
  unsigned long init_flags = 0;
  vpx_codec_ctx_t ctx;
  ASSERT_EQ(vpx_codec_enc_init(&ctx, iface, &cfg, init_flags), VPX_CODEC_OK);
  ASSERT_EQ(vpx_codec_control_(&ctx, VP8E_SET_CPUUSED, 9), 0);
  ASSERT_EQ(vpx_codec_control_(&ctx, VP9E_SET_DELTA_Q_UV, -15), 0);
  // Image allocation.
  vpx_img_fmt_t img_fmt = VPX_IMG_FMT_I420;
  vpx_image_t *img = vpx_img_alloc(nullptr, img_fmt, cfg.g_w, cfg.g_h, 32);
  for (unsigned int y = 0; y < img->d_h; y++) {
    for (unsigned int x = 0; x < img->d_w; x++) {
      img->planes[0][y * img->stride[0] + x] = ((x ^ y) * 127) & 0xFF;
    }
  }
  const unsigned int uv_height = (img->d_h + 1) >> 1;
  for (int i : { VPX_PLANE_U, VPX_PLANE_V }) {
    memset(img->planes[i], 0, img->stride[i] * uv_height);
  }
  // Encode some frames.
  int num_frames = 6;
  static constexpr int kChoices[6] = { 110000 };
  for (int frame = 0; frame < num_frames; frame++) {
    vpx_enc_deadline_t deadline = VPX_DL_REALTIME;
    uint8_t dl_choice = kChoices[frame];
    if (dl_choice == 1) deadline = VPX_DL_BEST_QUALITY;
    // Encode frame.
    ASSERT_EQ(vpx_codec_encode(&ctx, img, frame, 10, deadline), VPX_CODEC_OK);
  }
  vpx_img_free(img);
  vpx_codec_destroy(&ctx);
}

// Encode a few frames, with realtime mode and tile_rows set to 1,
// with row-mt enabled. This triggers an assertion in vp9_bitstream.c (in
// function write_modes()), as in the issue:442105459. In this test it happens
// on very first encoded frame since lag_in_frames = 0. Issue is due to enabling
// TILE_ROWS, with number of tile_rows more than the number of superblocks.
// This test sets 2 tile_rows with height corresponding to 1 superblock (sb).
TEST(EncodeAPI, Buganizer442105459_2RowTiles) {
  // Initialize VP9 encoder interface
  vpx_codec_iface_t *iface = vpx_codec_vp9_cx();
  // Get default encoder configuration
  vpx_codec_enc_cfg_t cfg;
  ASSERT_EQ(vpx_codec_enc_config_default(iface, &cfg, 0), VPX_CODEC_OK);
  // Configure encoder
  cfg.g_w = 946u;
  cfg.g_h = 64u;  // 1 sb row, 2 tile_rows set below.
  cfg.g_threads = 1;
  cfg.g_profile = 0;
  cfg.g_bit_depth = VPX_BITS_8;
  // Rate control targeting deeper encoding paths
  cfg.rc_target_bitrate = 100;
  cfg.rc_min_quantizer = 0;
  cfg.rc_max_quantizer = 0;
  cfg.rc_end_usage = VPX_VBR;
  cfg.ss_number_layers = 1;
  cfg.g_lag_in_frames = 0;
  // Initialize encoder context
  vpx_codec_ctx_t ctx;
  ASSERT_EQ(vpx_codec_enc_init(&ctx, iface, &cfg, 0), VPX_CODEC_OK);
  // Set control parameters
  vpx_codec_control_(&ctx, VP8E_SET_CPUUSED, -5);
  vpx_codec_control_(&ctx, VP9E_SET_TILE_ROWS, 1);
  vpx_codec_control_(&ctx, VP9E_SET_TILE_COLUMNS, 1);
  vpx_codec_control_(&ctx, VP9E_SET_ROW_MT, 1);
  // Image format selection
  vpx_img_fmt_t img_fmt = VPX_IMG_FMT_I420;
  // Allocate image with varied alignment
  vpx_image_t *img = vpx_img_alloc(nullptr, img_fmt, cfg.g_w, cfg.g_h, 1);
  for (unsigned int y = 0; y < img->d_h; y++) {
    for (unsigned int x = 0; x < img->d_w; x++) {
      img->planes[0][y * img->stride[0] + x] = ((x ^ y) * 127) & 0xFF;
    }
  }
  const unsigned int uv_height = (img->d_h + 1) >> 1;
  for (int i : { VPX_PLANE_U, VPX_PLANE_V }) {
    memset(img->planes[i], 0, img->stride[i] * uv_height);
  }
  // Encode with dynamic configuration changes
  int num_frames = 2;
  // Per-frame constants captured from the original run (indices consumed per
  // frame)
  const vpx_codec_pts_t frame_pts_mul[] = { 33333UL, 33333UL };
  const unsigned long frame_durations[] = { 33333UL, 33333UL };
  const vpx_enc_deadline_t frame_deadlines[] = { VPX_DL_REALTIME,
                                                 VPX_DL_REALTIME };
  for (int frame = 0; frame < num_frames; frame++) {
    // Encode frame
    vpx_codec_pts_t pts = frame * frame_pts_mul[frame];
    unsigned long duration = frame_durations[frame];
    vpx_enc_deadline_t deadline = frame_deadlines[frame];
    ASSERT_EQ(vpx_codec_encode(&ctx, img, pts, duration, /*flags*/ 0, deadline),
              VPX_CODEC_OK);
  }
  // Flush encoder.
  ASSERT_EQ(vpx_codec_encode(&ctx, nullptr, 000, VPX_DL_REALTIME), 0);
  // Get remaining data
  vpx_codec_iter_t iter = nullptr;
  while (vpx_codec_get_cx_data(&ctx, &iter) != nullptr) {
    // Process remaining packets
  }
  vpx_img_free(img);
  vpx_codec_destroy(&ctx);
}

// Encode a few frames, with realtime mode and tile_rows set to 1,
// with row-mt enabled. This triggers an assertion in vp9_bitstream.c (in
// function write_modes()), as in the issue:442105459. In this test it happens
// on very first encoded frame since lag_in_frames = 0. Issue is due to enabling
// TILE_ROWS, with number of tile_rows more than the number of superblocks.
// This test sets 4 tile_rows with height corresponding to 3 superblocks.
TEST(EncodeAPI, Buganizer442105459_4RowTiles) {
  // Initialize VP9 encoder interface
  vpx_codec_iface_t *iface = vpx_codec_vp9_cx();
  // Get default encoder configuration
  vpx_codec_enc_cfg_t cfg;
  ASSERT_EQ(vpx_codec_enc_config_default(iface, &cfg, 0), VPX_CODEC_OK);
  // Configure encoder
  cfg.g_w = 946u;
  cfg.g_h = 192u;  // 3 sb rows, 4 tile_rows set below.
  cfg.g_threads = 1;
  cfg.g_profile = 0;
  cfg.g_bit_depth = VPX_BITS_8;
  // Rate control targeting deeper encoding paths
  cfg.rc_target_bitrate = 100;
  cfg.rc_min_quantizer = 0;
  cfg.rc_max_quantizer = 0;
  cfg.rc_end_usage = VPX_VBR;
  cfg.ss_number_layers = 1;
  cfg.g_lag_in_frames = 0;
  // Initialize encoder context
  vpx_codec_ctx_t ctx;
  ASSERT_EQ(vpx_codec_enc_init(&ctx, iface, &cfg, 0), VPX_CODEC_OK);
  // Set control parameters
  vpx_codec_control_(&ctx, VP8E_SET_CPUUSED, -5);
  vpx_codec_control_(&ctx, VP9E_SET_TILE_ROWS, 2);
  vpx_codec_control_(&ctx, VP9E_SET_TILE_COLUMNS, 1);
  vpx_codec_control_(&ctx, VP9E_SET_ROW_MT, 1);
  // Image format selection
  vpx_img_fmt_t img_fmt = VPX_IMG_FMT_I420;
  // Allocate image with varied alignment
  vpx_image_t *img = vpx_img_alloc(nullptr, img_fmt, cfg.g_w, cfg.g_h, 1);
  for (unsigned int y = 0; y < img->d_h; y++) {
    for (unsigned int x = 0; x < img->d_w; x++) {
      img->planes[0][y * img->stride[0] + x] = ((x ^ y) * 127) & 0xFF;
    }
  }
  const unsigned int uv_height = (img->d_h + 1) >> 1;
  for (int i : { VPX_PLANE_U, VPX_PLANE_V }) {
    memset(img->planes[i], 0, img->stride[i] * uv_height);
  }
  // Encode with dynamic configuration changes
  int num_frames = 2;
  // Per-frame constants captured from the original run (indices consumed per
  // frame)
  const vpx_codec_pts_t frame_pts_mul[] = { 33333UL, 33333UL };
  const unsigned long frame_durations[] = { 33333UL, 33333UL };
  const vpx_enc_deadline_t frame_deadlines[] = { VPX_DL_REALTIME,
                                                 VPX_DL_REALTIME };
  for (int frame = 0; frame < num_frames; frame++) {
    // Encode frame
    vpx_codec_pts_t pts = frame * frame_pts_mul[frame];
    unsigned long duration = frame_durations[frame];
    vpx_enc_deadline_t deadline = frame_deadlines[frame];
    ASSERT_EQ(vpx_codec_encode(&ctx, img, pts, duration, /*flags*/ 0, deadline),
              VPX_CODEC_OK);
  }
  // Flush encoder.
  ASSERT_EQ(vpx_codec_encode(&ctx, nullptr, 000, VPX_DL_REALTIME), 0);
  // Get remaining data
  vpx_codec_iter_t iter = nullptr;
  while (vpx_codec_get_cx_data(&ctx, &iter) != nullptr) {
    // Process remaining packets
  }
  vpx_img_free(img);
  vpx_codec_destroy(&ctx);
}

#if CONFIG_VP9_HIGHBITDEPTH
TEST(EncodeAPI, Buganizer329674887RowMT0BitDepth12) {
  VP9Encoder encoder(80, VPX_BITS_12, VPX_IMG_FMT_I444);
  encoder.Configure(/*threads=*/2, /*width=*/1030, /*height=*/583, VPX_VBR,
                    VPX_DL_REALTIME);
  encoder.Encode(/*key_frame=*/true);
  encoder.Configure(/*threads=*/0, /*width=*/1030, /*height=*/1, VPX_CBR,
                    VPX_DL_REALTIME);
  encoder.Encode(/*key_frame=*/true);
  encoder.Configure(/*threads=*/0, /*width=*/548, /*height=*/322, VPX_VBR,
                    VPX_DL_REALTIME);
  encoder.Encode(/*key_frame=*/false);
  encoder.Configure(/*threads=*/16, /*width=*/24, /*height=*/583, VPX_CBR,
                    VPX_DL_GOOD_QUALITY);
}

TEST(EncodeAPI, Buganizer329179808RowMT0BitDepth10) {
  VP9Encoder encoder(40, VPX_BITS_10, VPX_IMG_FMT_I444);
  encoder.Configure(/*threads=*/16, /*width=*/1488, /*height=*/5, VPX_VBR,
                    VPX_DL_REALTIME);
  encoder.Encode(/*key_frame=*/true);
  encoder.Configure(/*threads=*/16, /*width=*/839, /*height=*/1, VPX_CBR,
                    VPX_DL_REALTIME);
  encoder.Encode(/*key_frame=*/false);
  encoder.Configure(/*threads=*/11, /*width=*/657, /*height=*/5, VPX_CBR,
                    VPX_DL_REALTIME);
  encoder.Encode(/*key_frame=*/false);
}

TEST(EncodeAPI, Buganizer329179808RowMT1BitDepth10) {
  VP9Encoder encoder(41, VPX_BITS_10, VPX_IMG_FMT_I444);
  encoder.Configure(/*threads=*/16, /*width=*/1488, /*height=*/5, VPX_VBR,
                    VPX_DL_REALTIME);
  encoder.Encode(/*key_frame=*/true);
  encoder.Configure(/*threads=*/16, /*width=*/839, /*height=*/1, VPX_CBR,
                    VPX_DL_REALTIME);
  encoder.Encode(/*key_frame=*/false);
  encoder.Configure(/*threads=*/11, /*width=*/657, /*height=*/5, VPX_CBR,
                    VPX_DL_REALTIME);
  encoder.Encode(/*key_frame=*/false);
}

TEST(EncodeAPI, Buganizer331108922BitDepth12) {
  VP9Encoder encoder(91, VPX_BITS_12, VPX_IMG_FMT_I444);
  encoder.Configure(/*threads=*/1, /*width=*/1, /*height=*/1080, VPX_VBR,
                    VPX_DL_REALTIME);
  encoder.Encode(/*key_frame=*/false);
  encoder.Configure(/*threads=*/0, /*width=*/1, /*height=*/1080, VPX_CBR,
                    VPX_DL_GOOD_QUALITY);
  encoder.Configure(/*threads=*/16, /*width=*/1, /*height=*/394, VPX_CBR,
                    VPX_DL_REALTIME);
  encoder.Encode(/*key_frame=*/false);
  encoder.Encode(/*key_frame=*/true);
  encoder.Configure(/*threads=*/16, /*width=*/1, /*height=*/798, VPX_CBR,
                    VPX_DL_REALTIME);
  encoder.Encode(/*key_frame=*/false);
}

TEST(EncodeAPI, InvalidInputRange) {
  constexpr int kWidth = 64;
  constexpr int kHeight = 64;
  vpx_codec_enc_cfg_t cfg;

  ASSERT_EQ(vpx_codec_enc_config_default(vpx_codec_vp9_cx(), &cfg,
                                         /*usage=*/0),
            VPX_CODEC_OK);
  cfg.g_w = kWidth;
  cfg.g_h = kHeight;

  std::unique_ptr<vpx_image_t, decltype(&vpx_img_free)> image(
      vpx_img_alloc(nullptr, VPX_IMG_FMT_I42016, cfg.g_w, cfg.g_h, 0),
      &vpx_img_free);
  vpx_image_t *img = image.get();
  ASSERT_NE(img, nullptr);

  for (const auto bitdepth : { VPX_BITS_8, VPX_BITS_10, VPX_BITS_12 }) {
    cfg.g_profile = (bitdepth == VPX_BITS_8) ? 0 : 2;
    cfg.g_bit_depth = bitdepth;

    // A value that's slightly over the max is used to ensure when the range is
    // not checked an encode won't produce any integer overflows. Values
    // outside of the valid range are not supported, so overflows will be
    // ignored in that case.
    const int val = 1 << bitdepth;
    for (const int plane : { VPX_PLANE_Y, VPX_PLANE_U, VPX_PLANE_V }) {
      const int width = (plane == VPX_PLANE_Y) ? img->d_w : (img->d_w + 1) / 2;
      const int height = (plane == VPX_PLANE_Y) ? img->d_h : (img->d_h + 1) / 2;
      uint8_t *p = img->planes[plane];
      for (int y = 0; y < height; ++y) {
        vpx_memset16(p, val, width);
        p += img->stride[plane];
      }
    }

    vpx_codec_ctx_t enc;
    ASSERT_EQ(vpx_codec_enc_init(&enc, vpx_codec_vp9_cx(), &cfg,
                                 VPX_CODEC_USE_HIGHBITDEPTH),
              VPX_CODEC_OK);

    for (int check_input_range = 0; check_input_range <= 1;
         ++check_input_range) {
      EXPECT_EQ(vpx_codec_control(&enc, VP9E_SET_VALIDATE_HBD_INPUT,
                                  check_input_range),
                VPX_CODEC_OK);

      EXPECT_EQ(vpx_codec_encode(&enc, img, /*pts=*/0, /*duration=*/1,
                                 /*flags=*/0, VPX_DL_REALTIME),
                check_input_range ? VPX_CODEC_INVALID_PARAM : VPX_CODEC_OK)
          << "Error: " << vpx_codec_error_detail(&enc)
          << ", bitdepth: " << bitdepth
          << ", check_input_range: " << check_input_range;
    }

    EXPECT_EQ(vpx_codec_encode(&enc, /*img=*/nullptr, /*pts=*/0, /*duration=*/0,
                               /*flags=*/0, VPX_DL_REALTIME),
              VPX_CODEC_OK);
    EXPECT_EQ(vpx_codec_destroy(&enc), VPX_CODEC_OK);
  }
}
#endif  // CONFIG_VP9_HIGHBITDEPTH

TEST(EncodeAPI, VP9GlobalHeaders) {
  constexpr int kWidth = 320;
  constexpr int kHeight = 240;

  libvpx_test::DummyVideoSource video;
  video.SetSize(kWidth, kHeight);

#if CONFIG_VP9_HIGHBITDEPTH
  const int profiles[] = { 0123 };
#else
  const int profiles[] = { 01 };
#endif
  char str[80];
  for (const int profile : profiles) {
    std::vector<vpx_bit_depth_t> bitdepths;
    std::vector<vpx_img_fmt_t> formats;
    switch (profile) {
      case 0:
        bitdepths = { VPX_BITS_8 };
        formats = { VPX_IMG_FMT_I420 };
        break;
      case 1:
        bitdepths = { VPX_BITS_8 };
        formats = { VPX_IMG_FMT_I422, VPX_IMG_FMT_I444 };
        break;
#if CONFIG_VP9_HIGHBITDEPTH
      case 2:
        bitdepths = { VPX_BITS_10, VPX_BITS_12 };
        formats = { VPX_IMG_FMT_I42016 };
        break;
      case 3:
        bitdepths = { VPX_BITS_10, VPX_BITS_12 };
        formats = { VPX_IMG_FMT_I42216, VPX_IMG_FMT_I44416 };
        break;
#endif
    }

    for (const auto format : formats) {
      for (const auto bitdepth : bitdepths) {
        snprintf(str, sizeof(str), "profile: %d bitdepth: %d format: %d",
                 profile, bitdepth, format);
        SCOPED_TRACE(str);

        vpx_codec_enc_cfg_t cfg = {};
        struct Encoder {
          ~Encoder() { EXPECT_EQ(vpx_codec_destroy(&ctx), VPX_CODEC_OK); }
          vpx_codec_ctx_t ctx = {};
        } enc;
        vpx_codec_ctx_t *const ctx = &enc.ctx;

        ASSERT_EQ(vpx_codec_enc_config_default(vpx_codec_vp9_cx(), &cfg, 0),
                  VPX_CODEC_OK);
        cfg.g_w = kWidth;
        cfg.g_h = kHeight;
        cfg.g_lag_in_frames = 0;
        cfg.g_pass = VPX_RC_ONE_PASS;
        cfg.g_profile = profile;
        cfg.g_bit_depth = bitdepth;
        ASSERT_EQ(
            vpx_codec_enc_init(ctx, vpx_codec_vp9_cx(), &cfg,
                               bitdepth == 8 ? 0 : VPX_CODEC_USE_HIGHBITDEPTH),
            VPX_CODEC_OK);
        ASSERT_EQ(vpx_codec_control_(ctx, VP8E_SET_CPUUSED, 2), VPX_CODEC_OK);
        ASSERT_EQ(vpx_codec_control_(ctx, VP9E_SET_TARGET_LEVEL, 62),
                  VPX_CODEC_OK);

        vpx_fixed_buf_t *global_headers = vpx_codec_get_global_headers(ctx);
        EXPECT_NE(global_headers, nullptr);
        EXPECT_EQ(global_headers->sz, size_t{ 9 });

        video.SetImageFormat(format);
        video.Begin();
        EXPECT_EQ(
            vpx_codec_encode(ctx, video.img(), video.pts(), video.duration(),
                             /*flags=*/0, VPX_DL_GOOD_QUALITY),
            VPX_CODEC_OK)
            << vpx_codec_error_detail(ctx);

        global_headers = vpx_codec_get_global_headers(ctx);
        EXPECT_NE(global_headers, nullptr);
        EXPECT_EQ(global_headers->sz, size_t{ 12 });
        uint8_t chroma_subsampling;
        if ((format & VPX_IMG_FMT_I420) == VPX_IMG_FMT_I420) {
          chroma_subsampling = 1;
        } else if ((format & VPX_IMG_FMT_I422) == VPX_IMG_FMT_I422) {
          chroma_subsampling = 2;
        } else {  // VPX_IMG_FMT_I444
          chroma_subsampling = 3;
        }
        const uint8_t expected_headers[] = { 1,
                                             1,
                                             static_cast<uint8_t>(profile),
                                             2,
                                             1,
                                             /*level,*/ 3,
                                             1,
                                             static_cast<uint8_t>(bitdepth),
                                             4,
                                             1,
                                             chroma_subsampling };
        const uint8_t *actual_headers =
            reinterpret_cast<const uint8_t *>(global_headers->buf);
        for (int i = 0; i < 5; ++i) {
          EXPECT_EQ(expected_headers[i], actual_headers[i]) << "index: " << i;
        }
        EXPECT_NE(actual_headers[6], 0);  // level
        for (int i = 5; i < 11; ++i) {
          EXPECT_EQ(expected_headers[i], actual_headers[i + 1])
              << "index: " << i + 1;
        }
      }
    }
  }
}

TEST(EncodeAPI, AomediaIssue3509VbrMinSection2PercentVP9) {
  // Initialize libvpx encoder.
  vpx_codec_iface_t *const iface = vpx_codec_vp9_cx();
  vpx_codec_ctx_t enc;
  vpx_codec_enc_cfg_t cfg;

  ASSERT_EQ(vpx_codec_enc_config_default(iface, &cfg, 0), VPX_CODEC_OK);

  cfg.g_w = 1920;
  cfg.g_h = 1080;
  cfg.g_lag_in_frames = 0;
  cfg.rc_target_bitrate = 1000000;
  // Set this to more than 1 percent to cause a signed integer overflow in the
  // multiplication rc->avg_frame_bandwidth * oxcf->rc_cfg.vbrmin_section in
  // vp9_rc_update_framerate() if the multiplication is done in the `int` type.
  cfg.rc_2pass_vbr_minsection_pct = 2;

  ASSERT_EQ(vpx_codec_enc_init(&enc, iface, &cfg, 0), VPX_CODEC_OK);

  // Create input image.
  vpx_image_t *const image =
      CreateImage(VPX_BITS_8, VPX_IMG_FMT_I420, cfg.g_w, cfg.g_h);
  ASSERT_NE(image, nullptr);

  // Encode frame.
  // `duration` can go as high as 300, but the UBSan error is gone if
  // `duration` is 301 or higher.
  ASSERT_EQ(
      vpx_codec_encode(&enc, image, 0/*duration=*/300, 0, VPX_DL_REALTIME),
      VPX_CODEC_OK);

  // Free resources.
  vpx_img_free(image);
  ASSERT_EQ(vpx_codec_destroy(&enc), VPX_CODEC_OK);
}

TEST(EncodeAPI, AomediaIssue3509VbrMinSection101PercentVP9) {
  // Initialize libvpx encoder.
  vpx_codec_iface_t *const iface = vpx_codec_vp9_cx();
  vpx_codec_ctx_t enc;
  vpx_codec_enc_cfg_t cfg;

  ASSERT_EQ(vpx_codec_enc_config_default(iface, &cfg, 0), VPX_CODEC_OK);

  cfg.g_w = 1920;
  cfg.g_h = 1080;
  cfg.g_lag_in_frames = 0;
  cfg.rc_target_bitrate = 1000000;
  // Set this to more than 100 percent to cause an error when vbr_min_bits is
  // cast to `int` in vp9_rc_update_framerate() if vbr_min_bits is not clamped
  // to INT_MAX.
  cfg.rc_2pass_vbr_minsection_pct = 101;

  ASSERT_EQ(vpx_codec_enc_init(&enc, iface, &cfg, 0), VPX_CODEC_OK);

  // Create input image.
  vpx_image_t *const image =
      CreateImage(VPX_BITS_8, VPX_IMG_FMT_I420, cfg.g_w, cfg.g_h);
  ASSERT_NE(image, nullptr);

  // Encode frame.
  // `duration` can go as high as 300, but the UBSan error is gone if
  // `duration` is 301 or higher.
  ASSERT_EQ(
      vpx_codec_encode(&enc, image, 0/*duration=*/300, 0, VPX_DL_REALTIME),
      VPX_CODEC_OK);

  // Free resources.
  vpx_img_free(image);
  ASSERT_EQ(vpx_codec_destroy(&enc), VPX_CODEC_OK);
}

TEST(EncodeAPI, Chromium352414650) {
  // Initialize libvpx encoder.
  vpx_codec_iface_t *const iface = vpx_codec_vp9_cx();
  vpx_codec_ctx_t enc;
  vpx_codec_enc_cfg_t cfg;

  ASSERT_EQ(vpx_codec_enc_config_default(iface, &cfg, 0), VPX_CODEC_OK);

  cfg.g_w = 1024;
  cfg.g_h = 1024;
  cfg.g_profile = 0;
  cfg.g_pass = VPX_RC_ONE_PASS;
  cfg.g_lag_in_frames = 0;
  cfg.rc_max_quantizer = 58;
  cfg.rc_min_quantizer = 2;
  cfg.g_threads = 4;
  cfg.rc_resize_allowed = 0;
  cfg.rc_dropframe_thresh = 0;
  cfg.g_timebase.num = 1;
  cfg.g_timebase.den = 1000000;
  cfg.kf_min_dist = 0;
  cfg.kf_max_dist = 10000;
  cfg.rc_end_usage = VPX_CBR;
  cfg.rc_target_bitrate = 754974;
  cfg.ts_number_layers = 3;
  cfg.ts_periodicity = 4;
  cfg.ts_layer_id[0] = 0;
  cfg.ts_layer_id[1] = 2;
  cfg.ts_layer_id[2] = 1;
  cfg.ts_layer_id[3] = 2;
  cfg.ts_rate_decimator[0] = 4;
  cfg.ts_rate_decimator[1] = 2;
  cfg.ts_rate_decimator[2] = 1;
  cfg.layer_target_bitrate[0] = 2147483;
  cfg.layer_target_bitrate[1] = 3006476;
  cfg.layer_target_bitrate[2] = 4294967;
  cfg.temporal_layering_mode = VP9E_TEMPORAL_LAYERING_MODE_0212;
  cfg.g_error_resilient = VPX_ERROR_RESILIENT_DEFAULT;

  ASSERT_EQ(vpx_codec_enc_init(&enc, iface, &cfg, 0), VPX_CODEC_OK);

  ASSERT_EQ(vpx_codec_control(&enc, VP8E_SET_CPUUSED, 7), VPX_CODEC_OK);
  ASSERT_EQ(vpx_codec_control(&enc, VP9E_SET_TILE_COLUMNS, 2), VPX_CODEC_OK);
  ASSERT_EQ(vpx_codec_control(&enc, VP9E_SET_ROW_MT, 1), VPX_CODEC_OK);

  vpx_svc_extra_cfg_t svc_cfg = {};
  svc_cfg.max_quantizers[0] = svc_cfg.max_quantizers[1] =
      svc_cfg.max_quantizers[2] = 58;
  svc_cfg.min_quantizers[0] = svc_cfg.min_quantizers[1] =
      svc_cfg.min_quantizers[2] = 2;
  svc_cfg.scaling_factor_num[0] = svc_cfg.scaling_factor_num[1] =
      svc_cfg.scaling_factor_num[2] = 1;
  svc_cfg.scaling_factor_den[0] = svc_cfg.scaling_factor_den[1] =
      svc_cfg.scaling_factor_den[2] = 1;
  ASSERT_EQ(vpx_codec_control(&enc, VP9E_SET_SVC_PARAMETERS, &svc_cfg),
            VPX_CODEC_OK);
  ASSERT_EQ(vpx_codec_control(&enc, VP9E_SET_SVC, 1), VPX_CODEC_OK);
  ASSERT_EQ(vpx_codec_control(&enc, VP9E_SET_AQ_MODE, 3), VPX_CODEC_OK);
  ASSERT_EQ(vpx_codec_control(&enc, VP8E_SET_STATIC_THRESHOLD, 1),
            VPX_CODEC_OK);
  ASSERT_EQ(vpx_codec_control(&enc, VP9E_SET_COLOR_SPACE, VPX_CS_SMPTE_170),
            VPX_CODEC_OK);
  ASSERT_EQ(vpx_codec_control(&enc, VP9E_SET_COLOR_RANGE, VPX_CR_STUDIO_RANGE),
            VPX_CODEC_OK);

  // Create input image.
  vpx_image_t *const image =
      CreateImage(VPX_BITS_8, VPX_IMG_FMT_I420, cfg.g_w, cfg.g_h);
  ASSERT_NE(image, nullptr);

  // Encode frame.
  ASSERT_EQ(vpx_codec_encode(&enc, image, 0/*duration=*/500000,
                             VPX_EFLAG_FORCE_KF, VPX_DL_REALTIME),
            VPX_CODEC_OK);

  // Free resources.
  vpx_img_free(image);
  ASSERT_EQ(vpx_codec_destroy(&enc), VPX_CODEC_OK);
}

TEST(EncodeAPI, PerFramePsnrNotSupportedWithLagInFrames) {
  vpx_codec_iface_t *const iface = vpx_codec_vp9_cx();
  vpx_codec_enc_cfg_t cfg;
  ASSERT_EQ(vpx_codec_enc_config_default(iface, &cfg, 0), VPX_CODEC_OK);
  ASSERT_NE(cfg.g_lag_in_frames, 0u);

  vpx_codec_ctx_t enc;
  ASSERT_EQ(vpx_codec_enc_init(&enc, iface, &cfg, 0), VPX_CODEC_OK);

  vpx_image_t *const image =
      CreateImage(VPX_BITS_8, VPX_IMG_FMT_I420, cfg.g_w, cfg.g_h);
  ASSERT_NE(image, nullptr);

  vpx_enc_frame_flags_t psnr_flags = VPX_EFLAG_CALCULATE_PSNR;
  ASSERT_EQ(vpx_codec_encode(&enc, image, /*pts=*/0, /*duration=*/1, psnr_flags,
                             VPX_DL_REALTIME),
            VPX_CODEC_INCAPABLE);

  // Free resources.
  vpx_img_free(image);
  ASSERT_EQ(vpx_codec_destroy(&enc), VPX_CODEC_OK);
}

TEST(EncodeAPI, Buganizer487259772ScaledRefs) {
  libvpx_test::ACMRandom rng;
  VP9Encoder encoder(/*speed=*/7, /*row_mt=*/0, VPX_BITS_8, VPX_IMG_FMT_I420);
  encoder.Configure(/*threads=*/1, /*width=*/478, /*height=*/755, VPX_VBR,
                    VPX_DL_REALTIME);
  encoder.Configure(/*threads=*/1, /*width=*/387, /*height=*/438, VPX_VBR,
                    VPX_DL_REALTIME);
  encoder.Encode(/*key_frame=*/false, &rng);
  encoder.Encode(/*key_frame=*/true, &rng);
  encoder.Encode(/*key_frame=*/false, &rng);
  encoder.Encode(/*key_frame=*/false, &rng);

  encoder.Configure(/*threads=*/1, /*width=*/341, /*height=*/655, VPX_VBR,
                    VPX_DL_REALTIME);
  encoder.Encode(/*key_frame=*/false, &rng);
  encoder.Encode(/*key_frame=*/false, &rng);
}

#if CONFIG_VP9_HIGHBITDEPTH
// This test uses source input based on the issue: 488585490,
// which is invalid for 10 bit. This test checks that the
// encoder correctly returns VPX_CODEC_INVALID_PARAM.
TEST(EncodeAPI, Buganizer488585490CostTableOverflow) {
  // Initialize libvpx encoder.
  vpx_codec_iface_t *const iface = vpx_codec_vp9_cx();
  vpx_codec_ctx_t enc;
  vpx_codec_enc_cfg_t cfg;

  ASSERT_EQ(vpx_codec_enc_config_default(iface, &cfg, /*usage=*/0),
            VPX_CODEC_OK);

  cfg.g_w = 149;
  cfg.g_h = 48;
  cfg.g_profile = 3;
  cfg.g_bit_depth = VPX_BITS_10;
  cfg.g_input_bit_depth = 10;
  cfg.g_pass = VPX_RC_ONE_PASS;
  cfg.g_lag_in_frames = 0;

  ASSERT_EQ(vpx_codec_enc_init(&enc, iface, &cfg, VPX_CODEC_USE_HIGHBITDEPTH),
            VPX_CODEC_OK);

  ASSERT_EQ(vpx_codec_control(&enc, VP8E_SET_CPUUSED, 7), VPX_CODEC_OK);
  ASSERT_EQ(vpx_codec_control(&enc, VP9E_SET_LOSSLESS, 1), VPX_CODEC_OK);

  libvpx_test::YUVVideoSource video(
      "repro-b488585490.yuv", VPX_IMG_FMT_I44416, cfg.g_w, cfg.g_h,
      /*rate_numerator=*/1, /*rate_denominator=*/30, /*start=*/0, /*limit=*/1);
  video.Begin();
  ASSERT_EQ(vpx_codec_encode(&enc, video.img(), video.pts(), /*duration=*/66666,
                             /*flags=*/0, VPX_DL_REALTIME),
            VPX_CODEC_INVALID_PARAM);

  ASSERT_EQ(vpx_codec_destroy(&enc), VPX_CODEC_OK);
}

void TestEncoderImageMismatch(bool use_highbitdepth) {
  vpx_codec_iface_t *const iface = vpx_codec_vp9_cx();
  vpx_codec_ctx_t enc;
  vpx_codec_enc_cfg_t cfg;

  ASSERT_EQ(vpx_codec_enc_config_default(iface, &cfg, 0), VPX_CODEC_OK);

  cfg.g_w = 180;
  cfg.g_h = 68;
  cfg.g_lag_in_frames = 0;

  vpx_img_fmt_t img_fmt;
  vpx_codec_flags_t flags = 0;

  if (use_highbitdepth) {
    cfg.g_profile = 2;
    cfg.g_bit_depth = VPX_BITS_10;
    flags = VPX_CODEC_USE_HIGHBITDEPTH;
    img_fmt = VPX_IMG_FMT_I420;
  } else {
    cfg.g_profile = 0;
    flags = 0;
    img_fmt = VPX_IMG_FMT_I42016;
  }

  ASSERT_EQ(vpx_codec_enc_init(&enc, iface, &cfg, flags), VPX_CODEC_OK);

  vpx_image_t *img = vpx_img_alloc(nullptr, img_fmt, cfg.g_w, cfg.g_h, 1);
  ASSERT_NE(img, nullptr);

  ASSERT_EQ(vpx_codec_encode(&enc, img, 010, VPX_DL_REALTIME),
            VPX_CODEC_INVALID_PARAM);

  vpx_img_free(img);
  ASSERT_EQ(vpx_codec_destroy(&enc), VPX_CODEC_OK);
}

TEST(EncodeAPI, HighbdEncoderI420ImageMismatch) {
  TestEncoderImageMismatch(true);
}

TEST(EncodeAPI, NonHighbdEncoderI42016ImageMismatch) {
  TestEncoderImageMismatch(false);
}
#endif

TEST(EncodeAPI, SvcTestInvalidInputs) {
  // Initialize libvpx encoder.
  vpx_codec_iface_t *const iface = vpx_codec_vp9_cx();
  vpx_codec_ctx_t enc;
  vpx_codec_enc_cfg_t cfg;
  ASSERT_EQ(vpx_codec_enc_config_default(iface, &cfg, 0), VPX_CODEC_OK);
  cfg.g_w = 1280;
  cfg.g_h = 720;
  cfg.g_profile = 0;
  cfg.g_pass = VPX_RC_ONE_PASS;
  cfg.g_timebase.num = 1;
  cfg.g_timebase.den = 1000;
  cfg.g_lag_in_frames = 0;
  cfg.rc_max_quantizer = 58;
  cfg.rc_min_quantizer = 2;
  cfg.ss_number_layers = 2;
  cfg.ts_number_layers = 2;
  cfg.ts_periodicity = 2;
  cfg.ts_layer_id[0] = 0;
  cfg.ts_layer_id[1] = 1;
  cfg.ts_rate_decimator[0] = 2;
  cfg.ts_rate_decimator[1] = 1;
  cfg.layer_target_bitrate[0] = 100;
  cfg.layer_target_bitrate[1] = 200;
  cfg.layer_target_bitrate[2] = 300;
  cfg.layer_target_bitrate[3] = 400;
  cfg.rc_target_bitrate = 700;
  ASSERT_EQ(vpx_codec_enc_init(&enc, iface, &cfg, 0), VPX_CODEC_OK);
  vpx_svc_extra_cfg_t svc_cfg = {};
  svc_cfg.scaling_factor_num[0] = 1;
  svc_cfg.scaling_factor_den[0] = 2;
  svc_cfg.scaling_factor_num[1] = 1;
  svc_cfg.scaling_factor_den[1] = 1;
  for (unsigned int i = 0; i < cfg.ss_number_layers * cfg.ss_number_layers;
       i++) {
    svc_cfg.max_quantizers[i] = 56;
    svc_cfg.min_quantizers[i] = 2;
  }
  ASSERT_EQ(vpx_codec_control(&enc, VP9E_SET_SVC, 1), VPX_CODEC_OK);
  // Check for invalid quantizer inputs.
  svc_cfg.min_quantizers[1] = 2;
  svc_cfg.max_quantizers[1] = 65;
  ASSERT_EQ(vpx_codec_control(&enc, VP9E_SET_SVC_PARAMETERS, &svc_cfg),
            VPX_CODEC_INVALID_PARAM);
  svc_cfg.min_quantizers[1] = 2;
  svc_cfg.max_quantizers[1] = -1;
  ASSERT_EQ(vpx_codec_control(&enc, VP9E_SET_SVC_PARAMETERS, &svc_cfg),
            VPX_CODEC_INVALID_PARAM);
  svc_cfg.min_quantizers[1] = 64;
  svc_cfg.max_quantizers[1] = 56;
  ASSERT_EQ(vpx_codec_control(&enc, VP9E_SET_SVC_PARAMETERS, &svc_cfg),
            VPX_CODEC_INVALID_PARAM);
  svc_cfg.min_quantizers[1] = -1;
  svc_cfg.max_quantizers[1] = 56;
  ASSERT_EQ(vpx_codec_control(&enc, VP9E_SET_SVC_PARAMETERS, &svc_cfg),
            VPX_CODEC_INVALID_PARAM);

  // Check for invalid duration input.
  vpx_svc_ref_frame_config_t ref_frame_config = {};
  for (unsigned int i = 0; i < cfg.ss_number_layers; i++) {
    ref_frame_config.lst_fb_idx[i] = 0;
    ref_frame_config.reference_last[i] = 1;
    ref_frame_config.update_last[i] = 1;
    ref_frame_config.duration[i] = 1000;
  }
  ref_frame_config.duration[0] = INT64_MAX;
  ASSERT_EQ(
      vpx_codec_control(&enc, VP9E_SET_SVC_REF_FRAME_CONFIG, &ref_frame_config),
      VPX_CODEC_OK);
  // Create input image.
  vpx_image_t *const image =
      CreateImage(VPX_BITS_8, VPX_IMG_FMT_I420, cfg.g_w, cfg.g_h);
  ASSERT_NE(image, nullptr);
  ASSERT_EQ(vpx_codec_encode(&enc, image, 03000, VPX_DL_REALTIME),
            VPX_CODEC_INVALID_PARAM);
  vpx_img_free(image);
  ASSERT_EQ(vpx_codec_destroy(&enc), VPX_CODEC_OK);
}

TEST(EncodeAPI, LargeDimensionsTokenAllocOverflow) {
  // To trigger the signed integer overflow, we need:
  // aligned_mb_rows * aligned_mb_cols * 772 > INT_MAX (2147483647)
  // aligned_mb_rows * aligned_mb_cols > 2147483647 / 772 = 2781714.5
  // For a square-ish frame, sqrt(2781714.5) is approx 1667.8.
  // Since aligned dimensions are multiples of 4, we round up to 1668.
  // 1668 * 1668 * 772 = 2147876928, which is > INT_MAX.
  // To get aligned MBs of 1668, we need input MBs in range (1664, 1668].
  // 1665 is chosen as the smallest integer in this range.
  int64_t tokens = get_token_alloc(16651665);
  EXPECT_EQ(tokens, (int64_t)1668 * 1668 * (16 * 16 * 3 + 4));
}

// 4 spatial layers, 1 temporal, speed 1 and 0 for spatial_layer = 0, 1,
// with different scale factors. Reproduces bug: 505665613.
TEST(EncodeAPI, SvcIssue505665613) {
  vpx_codec_ctx_t codec;
  vpx_codec_enc_cfg_t cfg;
  vpx_codec_iface_t *iface = vpx_codec_vp9_cx();
  ASSERT_EQ(vpx_codec_enc_config_default(iface, &cfg, 0), VPX_CODEC_OK);
  cfg.g_w = 935;
  cfg.g_h = 464;
  cfg.g_profile = 0;
  cfg.g_lag_in_frames = 0;
  cfg.ss_number_layers = 4;
  cfg.ts_number_layers = 1;
  for (int i = 0; i < 4; ++i) {
    cfg.layer_target_bitrate[i] = cfg.rc_target_bitrate / 4;
  }
  ASSERT_EQ(vpx_codec_enc_init(&codec, iface, &cfg, 0), VPX_CODEC_OK);
  ASSERT_EQ(vpx_codec_control(&codec, VP9E_SET_SVC, 1), VPX_CODEC_OK);

  vpx_svc_extra_cfg_t svc = {};
  svc.max_quantizers[0] = 49;
  svc.min_quantizers[0] = 5;
  svc.scaling_factor_num[0] = 2;
  svc.scaling_factor_den[0] = 12;
  svc.speed_per_layer[0] = 1;
  svc.loopfilter_ctrl[0] = 1;

  svc.max_quantizers[1] = 46;
  svc.min_quantizers[1] = 28;
  svc.scaling_factor_num[1] = 9;
  svc.scaling_factor_den[1] = 9;
  svc.speed_per_layer[1] = 0;
  svc.loopfilter_ctrl[1] = 0;

  svc.max_quantizers[2] = 32;
  svc.min_quantizers[2] = 0;
  svc.scaling_factor_num[2] = 1;
  svc.scaling_factor_den[2] = 4;
  svc.speed_per_layer[2] = 4;
  svc.loopfilter_ctrl[2] = 0;

  svc.max_quantizers[3] = 38;
  svc.min_quantizers[3] = 25;
  svc.scaling_factor_num[3] = 7;
  svc.scaling_factor_den[3] = 16;
  svc.speed_per_layer[3] = 3;
  svc.loopfilter_ctrl[3] = 0;

  svc.temporal_layering_mode = VP9E_TEMPORAL_LAYERING_MODE_0212;
  ASSERT_EQ(vpx_codec_control(&codec, VP9E_SET_SVC_PARAMETERS, &svc),
            VPX_CODEC_OK);
  vpx_image_t *img = vpx_img_alloc(nullptr, VPX_IMG_FMT_I420, 9354641);
  ASSERT_NE(img, nullptr);
  libvpx_test::ACMRandom rng(libvpx_test::ACMRandom::DeterministicSeed());
  for (unsigned int i = 0; i < img->d_h; ++i) {
    for (unsigned int j = 0; j < img->d_w; ++j) {
      img->planes[0][i * img->stride[0] + j] = rng.Rand8();
    }
  }
  const unsigned int uv_h =
      (img->d_h + img->y_chroma_shift) >> img->y_chroma_shift;
  const unsigned int uv_w =
      (img->d_w + img->x_chroma_shift) >> img->x_chroma_shift;
  for (unsigned int i = 0; i < uv_h; ++i) {
    for (unsigned int j = 0; j < uv_w; ++j) {
      img->planes[1][i * img->stride[1] + j] = rng.Rand8();
      img->planes[2][i * img->stride[2] + j] = rng.Rand8();
    }
  }
  // Encode.
  ASSERT_EQ(vpx_codec_encode(&codec, img, 010, VPX_DL_REALTIME),
            VPX_CODEC_OK);
  vpx_img_free(img);
  vpx_codec_destroy(&codec);
}

TEST(EncodeAPI, Vp9TargetLevelTinyResolution) {
  vpx_codec_enc_cfg_t cfg;
  vpx_codec_iface_t *const iface = vpx_codec_vp9_cx();

  ASSERT_EQ(vpx_codec_enc_config_default(iface, &cfg, 0), VPX_CODEC_OK);
  cfg.g_w = 1;
  cfg.g_h = 1;

  vpx_codec_ctx_t codec;
  ASSERT_EQ(vpx_codec_enc_init(&codec, iface, &cfg, 0), VPX_CODEC_OK);

  EXPECT_EQ(vpx_codec_control_(&codec, VP9E_SET_TARGET_LEVEL, 40),
            VPX_CODEC_OK);

  EXPECT_EQ(vpx_codec_destroy(&codec), VPX_CODEC_OK);
}

// Bug: 501612368. Configuration changes with different resolution
// to trigger issues in the re-allocation logic.
void VerifyVp9EncoderMidstreamReconfig(int width1, int height1, bool encode1,
                                       int width2, int height2, bool encode2,
                                       int width3, int height3, bool encode3) {
  vpx_codec_iface_t *const iface = &vpx_codec_vp9_cx_algo;
  vpx_codec_enc_cfg_t cfg;
  vpx_codec_ctx_t enc;

  ASSERT_EQ(vpx_codec_enc_config_default(iface, &cfg, 0), VPX_CODEC_OK);
  cfg.g_w = width1;
  cfg.g_h = height1;
  cfg.g_timebase.num = 1;
  cfg.g_timebase.den = 30;
  cfg.rc_target_bitrate = 1000;
  cfg.g_lag_in_frames = 0;

  ASSERT_EQ(vpx_codec_enc_init(&enc, iface, &cfg, 0), VPX_CODEC_OK);
  ASSERT_EQ(vpx_codec_control_(&enc, VP8E_SET_CPUUSED, 5), VPX_CODEC_OK);

  if (encode1) {
    vpx_image_t *image =
        CreateImage(VPX_BITS_8, VPX_IMG_FMT_I420, cfg.g_w, cfg.g_h);

    ASSERT_EQ(vpx_codec_encode(&enc, image, 010, VPX_DL_REALTIME),
              VPX_CODEC_OK);
    vpx_img_free(image);
  }

  cfg.g_w = width2;
  cfg.g_h = height2;
  ASSERT_EQ(vpx_codec_enc_config_set(&enc, &cfg), VPX_CODEC_OK);

  if (encode2) {
    vpx_image_t *image =
        CreateImage(VPX_BITS_8, VPX_IMG_FMT_I420, cfg.g_w, cfg.g_h);

    ASSERT_EQ(vpx_codec_encode(&enc, image, 010, VPX_DL_REALTIME),
              VPX_CODEC_OK);
    vpx_img_free(image);
  }

  cfg.g_w = width3;
  cfg.g_h = height3;
  ASSERT_EQ(vpx_codec_enc_config_set(&enc, &cfg), VPX_CODEC_OK);

  if (encode3) {
    vpx_image_t *image =
        CreateImage(VPX_BITS_8, VPX_IMG_FMT_I420, cfg.g_w, cfg.g_h);

    ASSERT_EQ(vpx_codec_encode(&enc, image, 110, VPX_DL_REALTIME),
              VPX_CODEC_OK);
    vpx_img_free(image);
  }
  ASSERT_EQ(vpx_codec_destroy(&enc), VPX_CODEC_OK);
}

// Resizing from 64x128 to 128x64: same allocation area.
// Bug: 501612368.
TEST(EncodeAPI, Vp9EncoderMidstreamReconfig1) {
  VerifyVp9EncoderMidstreamReconfig(64128true12864true12864,
                                    true);
}

// Resizing from 192x192 to 256x128: allocation area decreases
// but width is increased (so alloc for context_buffer is increased).
// Bug: 501612368.
TEST(EncodeAPI, Vp9EncoderMidstreamReconfig2) {
  VerifyVp9EncoderMidstreamReconfig(192192true256128true256128,
                                    true);
}

// Resizing from 128x128 to 96x160: allocation area decreases
// but token_alloc is increased. Bug: 501612368.
TEST(EncodeAPI, Vp9EncoderMidstreamReconfig3) {
  VerifyVp9EncoderMidstreamReconfig(128128true96160true96160,
                                    true);
}

// Start at 128x128, encode a frame. Then update to larger resolution
// twice before encoding a frame: Bug: 501612368.
TEST(EncodeAPI, Vp9EncoderMidstreamReconfig4) {
  VerifyVp9EncoderMidstreamReconfig(128128true256256, false, 512512,
                                    true);
}
#endif  // CONFIG_VP9_ENCODER

#if CONFIG_VP9_ENCODER
void VerifyVp9UnsafeCastEosCount(double count_value) {
  vpx_codec_iface_t *const iface = vpx_codec_vp9_cx();
  vpx_codec_enc_cfg_t cfg;
  ASSERT_EQ(vpx_codec_enc_config_default(iface, &cfg, 0), VPX_CODEC_OK);
  cfg.g_pass = VPX_RC_LAST_PASS;

  // Two packets required: one frame packet + one EOS packet.
  FIRSTPASS_STATS packets[2] = {};
  packets[1].count = count_value;

  cfg.rc_twopass_stats_in.buf = packets;
  cfg.rc_twopass_stats_in.sz = sizeof(packets);

  vpx_codec_ctx_t codec;
  // This should return VPX_CODEC_INVALID_PARAM instead of triggering UB.
  EXPECT_EQ(vpx_codec_enc_init(&codec, iface, &cfg, 0),
            VPX_CODEC_INVALID_PARAM);
}

TEST(EncodeAPI, Vp9UnsafeCastEosCount) { VerifyVp9UnsafeCastEosCount(1e300); }

TEST(EncodeAPI, Vp9UnsafeCastEosCountNaN) { VerifyVp9UnsafeCastEosCount(NAN); }

TEST(EncodeAPI, Vp9UnsafeCastEosCountZero) { VerifyVp9UnsafeCastEosCount(0.0); }

TEST(EncodeAPI, Vp9SvcLayeringModeNotSet) {
  const unsigned int kSpatialLayers = 4;
  const unsigned int kTemporalLayers = 3;
  const unsigned int kWidth = 640;
  const unsigned int kHeight = 320;
  const int kFrames = 8;
  vpx_codec_iface_t *const iface = vpx_codec_vp9_cx();
  vpx_codec_enc_cfg_t cfg;
  ASSERT_EQ(vpx_codec_enc_config_default(iface, &cfg, 0), VPX_CODEC_OK);
  cfg.g_w = kWidth;
  cfg.g_h = kHeight;
  cfg.g_timebase.num = 1;
  cfg.g_timebase.den = 30;
  cfg.g_error_resilient = 1;
  cfg.g_lag_in_frames = 0;
  cfg.rc_end_usage = VPX_CBR;
  cfg.ss_number_layers = kSpatialLayers;
  cfg.ts_number_layers = kTemporalLayers;
  cfg.rc_target_bitrate = 5000;
  cfg.ss_target_bitrate[0] = 500;
  cfg.ss_target_bitrate[1] = 1000;
  cfg.ss_target_bitrate[2] = 1500;
  cfg.ss_target_bitrate[3] = 2000;
  cfg.layer_target_bitrate[0] = 50 * cfg.ss_target_bitrate[0] / 100;
  cfg.layer_target_bitrate[1] = 70 * cfg.ss_target_bitrate[0] / 100;
  cfg.layer_target_bitrate[2] = cfg.ss_target_bitrate[0];
  cfg.layer_target_bitrate[3] = 50 * cfg.ss_target_bitrate[1] / 100;
  cfg.layer_target_bitrate[4] = 70 * cfg.ss_target_bitrate[1] / 100;
  cfg.layer_target_bitrate[5] = cfg.ss_target_bitrate[1];
  cfg.layer_target_bitrate[6] = 50 * cfg.ss_target_bitrate[2] / 100;
  cfg.layer_target_bitrate[7] = 70 * cfg.ss_target_bitrate[2] / 100;
  cfg.layer_target_bitrate[8] = cfg.ss_target_bitrate[2];
  cfg.layer_target_bitrate[9] = 50 * cfg.ss_target_bitrate[3] / 100;
  cfg.layer_target_bitrate[10] = 70 * cfg.ss_target_bitrate[3] / 100;
  cfg.layer_target_bitrate[11] = cfg.ss_target_bitrate[3];
  cfg.ts_rate_decimator[0] = 4;
  cfg.ts_rate_decimator[1] = 2;
  cfg.ts_rate_decimator[2] = 1;
  cfg.ts_periodicity = 12;
  for (unsigned int i = 0; i < cfg.ts_periodicity; ++i) {
    cfg.ts_layer_id[i] = i % kTemporalLayers;
  }
  vpx_codec_ctx_t codec;
  ASSERT_EQ(vpx_codec_enc_init(&codec, iface, &cfg, 0), VPX_CODEC_OK);
  ASSERT_EQ(vpx_codec_control(&codec, VP9E_SET_SVC, 1), VPX_CODEC_OK);
  vpx_image_t *const image =
      CreateImage(VPX_BITS_8, VPX_IMG_FMT_I420, kWidth, kHeight);
  ASSERT_NE(image, nullptr);

  for (int f = 0; f < kFrames; ++f) {
    vpx_svc_layer_id_t layer_id = {};
    layer_id.spatial_layer_id = 0;
    if (f % 4 == 0)
      layer_id.temporal_layer_id = 0;
    else if (f % 2 == 0)
      layer_id.temporal_layer_id = 1;
    else if ((f - 1) % 2 == 0)
      layer_id.temporal_layer_id = 2;
    ASSERT_EQ(vpx_codec_control(&codec, VP9E_SET_SVC_LAYER_ID, &layer_id),
              VPX_CODEC_OK);
    const int flags = (f == 0) ? VPX_EFLAG_FORCE_KF : 0;
    ASSERT_EQ(vpx_codec_encode(&codec, image, f, 1, flags, VPX_DL_REALTIME),
              VPX_CODEC_OK);
    vpx_codec_iter_t iter = nullptr;
    while (vpx_codec_get_cx_data(&codec, &iter) != nullptr) {
    }
  }
  ASSERT_EQ(vpx_codec_encode(&codec, nullptr, kFrames, 10, VPX_DL_REALTIME),
            VPX_CODEC_OK);
  vpx_codec_iter_t iter = nullptr;
  while (vpx_codec_get_cx_data(&codec, &iter) != nullptr) {
  }
  vpx_img_free(image);
  ASSERT_EQ(vpx_codec_destroy(&codec), VPX_CODEC_OK);
}

// Test for SSIM tuning with resolution change: bug: 512508456.
TEST(EncodeAPI, BuganizerSSIMHeapOverflow) {
  vpx_codec_iface_t *const iface = vpx_codec_vp9_cx();
  vpx_codec_ctx_t enc;
  vpx_codec_enc_cfg_t cfg;
  ASSERT_EQ(vpx_codec_enc_config_default(iface, &cfg, 0), VPX_CODEC_OK);
  cfg.g_w = 16;
  cfg.g_h = 16;
  cfg.g_threads = 1;
  cfg.g_pass = VPX_RC_ONE_PASS;
  cfg.g_lag_in_frames = 0;
  ASSERT_EQ(vpx_codec_enc_init(&enc, iface, &cfg, 0), VPX_CODEC_OK);

  // Set VP8_TUNE_SSIM.
  ASSERT_EQ(vpx_codec_control(&enc, VP8E_SET_TUNING, VP8_TUNE_SSIM),
            VPX_CODEC_OK);

  vpx_image_t *img =
      CreateImage(VPX_BITS_8, VPX_IMG_FMT_I420, cfg.g_w, cfg.g_h);
  ASSERT_NE(img, nullptr);

  // Encode first frame
  ASSERT_EQ(vpx_codec_encode(&enc, img, 010, VPX_DL_REALTIME),
            VPX_CODEC_OK);

  // Change resolution to larger size.
  cfg.g_w = 4096;
  cfg.g_h = 4096;
  ASSERT_EQ(vpx_codec_enc_config_set(&enc, &cfg), VPX_CODEC_OK);

  vpx_img_free(img);
  img = CreateImage(VPX_BITS_8, VPX_IMG_FMT_I420, cfg.g_w, cfg.g_h);
  ASSERT_NE(img, nullptr);

  // Encode second frame.
  ASSERT_EQ(vpx_codec_encode(&enc, img, 110, VPX_DL_REALTIME),
            VPX_CODEC_OK);

  vpx_img_free(img);
  ASSERT_EQ(vpx_codec_destroy(&enc), VPX_CODEC_OK);
}

// Configure spatial layers and encode first superframe with 0 layer bitrates
// for enhancement layers, and start with SL1 (don't call encoder for SL0).
// This causes an empty superframe. Bug: 508317885.
TEST(EncodeAPI, Vp9SvcEmptySuperframe) {
  vpx_codec_iface_t *const iface = vpx_codec_vp9_cx();
  vpx_codec_enc_cfg_t cfg;
  ASSERT_EQ(vpx_codec_enc_config_default(iface, &cfg, 0), VPX_CODEC_OK);

  cfg.g_w = 640;
  cfg.g_h = 360;
  cfg.g_pass = VPX_RC_ONE_PASS;
  cfg.g_lag_in_frames = 0;
  cfg.rc_end_usage = VPX_CBR;
  cfg.ss_number_layers = 3;
  cfg.ts_number_layers = 1;
  cfg.rc_dropframe_thresh = 30;
  cfg.rc_target_bitrate = 30;
  cfg.ss_target_bitrate[0] = 30;
  cfg.ss_target_bitrate[1] = 0;
  cfg.ss_target_bitrate[2] = 0;
  cfg.layer_target_bitrate[0] = 30;
  cfg.layer_target_bitrate[1] = 0;
  cfg.layer_target_bitrate[2] = 0;

  vpx_codec_ctx_t codec;
  ASSERT_EQ(vpx_codec_enc_init(&codec, iface, &cfg, 0), VPX_CODEC_OK);
  ASSERT_EQ(vpx_codec_control(&codec, VP9E_SET_SVC, 1), VPX_CODEC_OK);

  vpx_image_t *const image =
      CreateImage(VPX_BITS_8, VPX_IMG_FMT_I420, cfg.g_w, cfg.g_h);
  ASSERT_NE(image, nullptr);

  vpx_svc_layer_id_t layer_id = {};
  layer_id.spatial_layer_id = 1;
  layer_id.temporal_layer_id = 0;
  ASSERT_EQ(vpx_codec_control(&codec, VP9E_SET_SVC_LAYER_ID, &layer_id),
            VPX_CODEC_OK);
  ASSERT_EQ(vpx_codec_encode(&codec, image, 010, VPX_DL_REALTIME),
            VPX_CODEC_OK);

  layer_id.spatial_layer_id = 2;
  layer_id.temporal_layer_id = 0;
  ASSERT_EQ(vpx_codec_control(&codec, VP9E_SET_SVC_LAYER_ID, &layer_id),
            VPX_CODEC_OK);
  ASSERT_EQ(vpx_codec_encode(&codec, image, 010, VPX_DL_REALTIME),
            VPX_CODEC_OK);

  vpx_img_free(image);
  ASSERT_EQ(vpx_codec_destroy(&codec), VPX_CODEC_OK);
}

// Encode one pass VBR with nonzero-lookahead, with resize and realtime mode.
// Test for the re-allocation of count_arf_frame_usage and
// count_lastgolden_frame_usage. Bug: 515433297.
TEST(EncodeAPI, ResizeOnePassVbrAltrefUsageBug) {
  vpx_codec_ctx_t codec;
  vpx_codec_enc_cfg_t cfg;
  vpx_image_t *image;

  const int initial_width = 16;
  const int initial_height = 16;
  const int new_width = 4096;
  const int new_height = 4096;

  ASSERT_EQ(vpx_codec_enc_config_default(vpx_codec_vp9_cx(), &cfg, 0),
            VPX_CODEC_OK);

  cfg.g_w = initial_width;
  cfg.g_h = initial_height;
  cfg.g_threads = 1;
  cfg.g_pass = VPX_RC_ONE_PASS;
  cfg.g_lag_in_frames = 1;
  cfg.rc_end_usage = VPX_VBR;
  ASSERT_EQ(vpx_codec_enc_init(&codec, vpx_codec_vp9_cx(), &cfg, 0),
            VPX_CODEC_OK);
  ASSERT_EQ(vpx_codec_control(&codec, VP8E_SET_CPUUSED, 5), VPX_CODEC_OK);

  image = CreateImage(VPX_BITS_8, VPX_IMG_FMT_I420, cfg.g_w, cfg.g_h);
  ASSERT_NE(image, nullptr);
  ASSERT_EQ(vpx_codec_encode(&codec, image, 010, VPX_DL_REALTIME),
            VPX_CODEC_OK);

  cfg.g_w = new_width;
  cfg.g_h = new_height;
  ASSERT_EQ(vpx_codec_enc_config_set(&codec, &cfg), VPX_CODEC_OK);

  vpx_img_free(image);
  image = CreateImage(VPX_BITS_8, VPX_IMG_FMT_I420, cfg.g_w, cfg.g_h);
  ASSERT_NE(image, nullptr);

  for (int i = 1; i < 30; i++) {
    ASSERT_EQ(vpx_codec_encode(&codec, image, i, 10, VPX_DL_GOOD_QUALITY),
              VPX_CODEC_OK);
  }

  vpx_img_free(image);
  vpx_codec_destroy(&codec);
}

// Resize with PERCEPTUAL_AQ. Bug: 515431489.
TEST(EncodeAPI, PerceptualAQKMeansHeapOverflow) {
  vpx_codec_iface_t *const iface = vpx_codec_vp9_cx();
  vpx_codec_ctx_t enc;
  vpx_codec_enc_cfg_t cfg;
  ASSERT_EQ(vpx_codec_enc_config_default(iface, &cfg, 0), VPX_CODEC_OK);

  cfg.g_w = 16;
  cfg.g_h = 16;
  cfg.g_threads = 1;
  cfg.g_pass = VPX_RC_ONE_PASS;
  cfg.g_lag_in_frames = 0;
  ASSERT_EQ(vpx_codec_enc_init(&enc, iface, &cfg, 0), VPX_CODEC_OK);

  vpx_image_t *img =
      CreateImage(VPX_BITS_8, VPX_IMG_FMT_I420, cfg.g_w, cfg.g_h);
  ASSERT_NE(img, nullptr);

  ASSERT_EQ(vpx_codec_encode(&enc, img, 010, VPX_DL_REALTIME),
            VPX_CODEC_OK);

  ASSERT_EQ(vpx_codec_control(&enc, VP9E_SET_AQ_MODE, PERCEPTUAL_AQ),
            VPX_CODEC_OK);

  cfg.g_w = 4096;
  cfg.g_h = 4096;
  ASSERT_EQ(vpx_codec_enc_config_set(&enc, &cfg), VPX_CODEC_OK);

  vpx_img_free(img);
  img = CreateImage(VPX_BITS_8, VPX_IMG_FMT_I420, cfg.g_w, cfg.g_h);
  ASSERT_NE(img, nullptr);

  ASSERT_EQ(vpx_codec_encode(&enc, img, 110, VPX_DL_REALTIME),
            VPX_CODEC_OK);

  vpx_img_free(img);
  ASSERT_EQ(vpx_codec_destroy(&enc), VPX_CODEC_OK);
}
#endif  // CONFIG_VP9_ENCODER

}  // namespace

Messung V0.5 in Prozent
C=88 H=96 G=91

¤ Diese beiden folgenden Angebotsgruppen bietet das Unternehmen0.99Angebot  (Wie Sie bei der Firma Beratungs- und Dienstleistungen beauftragen können 2026-08-26) ¤

*Eine klare Vorstellung vom Zielzustand






Wurzel

Suchen

PVS Prover

Isabelle Prover

NIST Cobol Testsuite

Cephes Mathematical Library

Vienna Development Method

Haftungshinweis

Die Informationen auf dieser Webseite wurden nach bestem Wissen sorgfältig zusammengestellt. Es wird jedoch weder Vollständigkeit, noch Richtigkeit, noch Qualität der bereit gestellten Informationen zugesichert.

Bemerkung:

Die farbliche Syntaxdarstellung und die Messung sind noch experimentell.