diff options
52 files changed, 1384 insertions, 290 deletions
@@ -76,6 +76,7 @@ Hangyu Kuang <hkuang@google.com> Hanno Böck <hanno@hboeck.de> Han Shen <shenhan@google.com> Hao Chen <chenhao@loongson.cn> +Hari Limaye <hari.limaye@arm.com> Harish Mahendrakar <harish.mahendrakar@ittiam.com> Henrik Lundin <hlundin@google.com> Hien Ho <hienho@google.com> @@ -1,3 +1,30 @@ +2024-05-21 v1.14.1 "Venetian Duck" + This release includes enhancements and bug fixes. + + - Upgrading: + This release is ABI compatible with the previous release. + + - Enhancement: + Improved the detection of compiler support for AArch64 extensions, + particularly SVE. + + Added vpx_codec_get_global_headers() support for VP9. + + - Bug fixes: + Added buffer bounds checks to vpx_writer and vpx_write_bit_buffer. + Fix to GetSegmentationData() crash in aq_mode=0 for RTC rate control. + Fix to alloc for row_base_thresh_freq_fac. + Free row mt memory before freeing cpi->tile_data. + Fix to buffer alloc for vp9_bitstream_worker_data. + Fix to VP8 race issue for multi-thread with pnsr_calc. + Fix to uv width/height in vp9_scale_and_extend_frame_ssse3. + Fix to integer division by zero and overflow in calc_pframe_target_size(). + Fix to integer overflow in vpx_img_alloc() & vpx_img_wrap()(CVE-2024-5197). + Fix to UBSan error in vp9_rc_update_framerate(). + Fix to UBSan errors in vp8_new_framerate(). + Fix to integer overflow in vp8 encodeframe.c. + Handle EINTR from sem_wait(). + 2024-01-02 v1.14.0 "Venetian Duck" This release drops support for old C compilers, such as Visual Studio 2012 and older, that disallow mixing variable declarations and statements (a C99 @@ -11,12 +11,12 @@ third_party { } last_upgrade_date { year: 2024 - month: 1 - day: 22 + month: 6 + day: 3 } identifier { type: "Git" value: "https://chromium.googlesource.com/webm/libvpx" - version: "v1.14.0" + version: "v1.14.1" } } diff --git a/README.android b/README.android index 5706119b4..7a01a008a 100644 --- a/README.android +++ b/README.android @@ -1,12 +1,12 @@ Name: libvpx URL: http://www.webmproject.org -Version: v1.14.0 +Version: v1.14.1 License: BSD License File: libvpx/LICENSE -Date: Monday January 22 2024 -Branch: v1.14.0 -Commit: 602e2e8979d111b02c959470da5322797dd96a19 +Date: Monday June 3 2024 +Branch: v1.14.1 +Commit: 12f3a2ac603e8f10742105519e0cd03c3b8f71dd Description: Contains the sources used to compile libvpx. diff --git a/TEST_MAPPING b/TEST_MAPPING new file mode 100644 index 000000000..eaaa4619e --- /dev/null +++ b/TEST_MAPPING @@ -0,0 +1,12 @@ +{ + "postsubmit": [ + { + "name": "MctsMediaV2TestCases", + "options": [ + { + "instrumentation-arg": "codec-filter:=c2\\.android\\.vp[89]" + } + ] + } + ] +} diff --git a/build/make/configure.sh b/build/make/configure.sh index b645a666f..93643f3de 100644 --- a/build/make/configure.sh +++ b/build/make/configure.sh @@ -429,6 +429,40 @@ check_gcc_machine_options() { fi } +check_neon_sve_bridge_compiles() { + if enabled sve; then + check_cc -march=armv8.2-a+dotprod+i8mm+sve <<EOF +#ifndef __ARM_NEON_SVE_BRIDGE +#error 1 +#endif +#include <arm_sve.h> +#include <arm_neon_sve_bridge.h> +EOF + compile_result=$? + if [ ${compile_result} -eq 0 ]; then + # Check whether the compiler can compile SVE functions that require + # backup/restore of SVE registers according to AAPCS. Clang for Windows + # used to fail this, see + # https://github.com/llvm/llvm-project/issues/80009. + check_cc -march=armv8.2-a+dotprod+i8mm+sve <<EOF +#include <arm_sve.h> +void other(void); +svfloat32_t func(svfloat32_t a) { + other(); + return a; +} +EOF + compile_result=$? + fi + + if [ ${compile_result} -ne 0 ]; then + log_echo " disabling sve: arm_neon_sve_bridge.h not supported by compiler" + disable_feature sve + RTCD_OPTIONS="${RTCD_OPTIONS}--disable-sve " + fi + fi +} + check_gcc_avx512_compiles() { if disabled gcc; then return @@ -980,36 +1014,18 @@ EOF case ${toolchain} in arm*) soft_enable runtime_cpu_detect - # Arm ISA extensions are treated as supersets. - case ${tgt_isa} in - arm64|armv8) - for ext in ${ARCH_EXT_LIST_AARCH64}; do - # Disable higher order extensions to simplify dependencies. - if [ "$disable_exts" = "yes" ]; then - if ! disabled $ext; then - RTCD_OPTIONS="${RTCD_OPTIONS}--disable-${ext} " - disable_feature $ext - fi - elif disabled $ext; then - disable_exts="yes" - else - soft_enable $ext - fi - done - ;; - armv7|armv7s) - soft_enable neon - # Only enable neon_asm when neon is also enabled. - enabled neon && soft_enable neon_asm - # If someone tries to force it through, die. - if disabled neon && enabled neon_asm; then - die "Disabling neon while keeping neon-asm is not supported" - fi - ;; - esac - asm_conversion_cmd="cat" + if [ ${tgt_isa} = "armv7" ] || [ ${tgt_isa} = "armv7s" ]; then + soft_enable neon + # Only enable neon_asm when neon is also enabled. + enabled neon && soft_enable neon_asm + # If someone tries to force it through, die. + if disabled neon && enabled neon_asm; then + die "Disabling neon while keeping neon-asm is not supported" + fi + fi + asm_conversion_cmd="cat" case ${tgt_cc} in gcc) link_with_cc=gcc @@ -1228,6 +1244,35 @@ EOF fi ;; esac + + # AArch64 ISA extensions are treated as supersets. + if [ ${tgt_isa} = "arm64" ] || [ ${tgt_isa} = "armv8" ]; then + aarch64_arch_flag_neon="arch=armv8-a" + aarch64_arch_flag_neon_dotprod="arch=armv8.2-a+dotprod" + aarch64_arch_flag_neon_i8mm="arch=armv8.2-a+dotprod+i8mm" + aarch64_arch_flag_sve="arch=armv8.2-a+dotprod+i8mm+sve" + for ext in ${ARCH_EXT_LIST_AARCH64}; do + if [ "$disable_exts" = "yes" ]; then + RTCD_OPTIONS="${RTCD_OPTIONS}--disable-${ext} " + soft_disable $ext + else + # Check the compiler supports the -march flag for the extension. + # This needs to happen after toolchain/OS inspection so we handle + # $CROSS etc correctly when checking for flags, else these will + # always fail. + flag="$(eval echo \$"aarch64_arch_flag_${ext}")" + check_gcc_machine_option "${flag}" "${ext}" + if ! enabled $ext; then + # Disable higher order extensions to simplify dependencies. + disable_exts="yes" + RTCD_OPTIONS="${RTCD_OPTIONS}--disable-${ext} " + soft_disable $ext + fi + fi + done + check_neon_sve_bridge_compiles + fi + ;; mips*) link_with_cc=gcc diff --git a/config/arm-neon/vpx_dsp_rtcd.h b/config/arm-neon/vpx_dsp_rtcd.h index 578f1c5dc..f0800ab88 100644 --- a/config/arm-neon/vpx_dsp_rtcd.h +++ b/config/arm-neon/vpx_dsp_rtcd.h @@ -1968,8 +1968,8 @@ void vpx_scaled_horiz_c(const uint8_t *src, ptrdiff_t src_stride, uint8_t *dst, void vpx_scaled_vert_c(const uint8_t *src, ptrdiff_t src_stride, uint8_t *dst, ptrdiff_t dst_stride, const InterpKernel *filter, int x0_q4, int x_step_q4, int y0_q4, int y_step_q4, int w, int h); #define vpx_scaled_vert vpx_scaled_vert_c -int64_t vpx_sse_c(const uint8_t *a, int a_stride, const uint8_t *b,int b_stride, int width, int height); -int64_t vpx_sse_neon(const uint8_t *a, int a_stride, const uint8_t *b,int b_stride, int width, int height); +int64_t vpx_sse_c(const uint8_t *src, int src_stride, const uint8_t *ref, int ref_stride, int width, int height); +int64_t vpx_sse_neon(const uint8_t *src, int src_stride, const uint8_t *ref, int ref_stride, int width, int height); #define vpx_sse vpx_sse_neon uint32_t vpx_sub_pixel_avg_variance16x16_c(const uint8_t *src_ptr, int src_stride, int x_offset, int y_offset, const uint8_t *ref_ptr, int ref_stride, uint32_t *sse, const uint8_t *second_pred); diff --git a/config/arm-neon/vpx_version.h b/config/arm-neon/vpx_version.h index 00ab40fe2..a06c0a0f1 100644 --- a/config/arm-neon/vpx_version.h +++ b/config/arm-neon/vpx_version.h @@ -1,8 +1,8 @@ // This file is generated. Do not edit. #define VERSION_MAJOR 1 #define VERSION_MINOR 14 -#define VERSION_PATCH 0 -#define VERSION_EXTRA "1616-g26104bbc9d" +#define VERSION_PATCH 1 +#define VERSION_EXTRA "1650-g0e5dcd1f52" #define VERSION_PACKED ((VERSION_MAJOR<<16)|(VERSION_MINOR<<8)|(VERSION_PATCH)) -#define VERSION_STRING_NOSP "v1.14.0-1616-g26104bbc9d" -#define VERSION_STRING " v1.14.0-1616-g26104bbc9d" +#define VERSION_STRING_NOSP "v1.14.1-1650-g0e5dcd1f52" +#define VERSION_STRING " v1.14.1-1650-g0e5dcd1f52" diff --git a/config/arm64/vpx_dsp_rtcd.h b/config/arm64/vpx_dsp_rtcd.h index 578f1c5dc..f0800ab88 100644 --- a/config/arm64/vpx_dsp_rtcd.h +++ b/config/arm64/vpx_dsp_rtcd.h @@ -1968,8 +1968,8 @@ void vpx_scaled_horiz_c(const uint8_t *src, ptrdiff_t src_stride, uint8_t *dst, void vpx_scaled_vert_c(const uint8_t *src, ptrdiff_t src_stride, uint8_t *dst, ptrdiff_t dst_stride, const InterpKernel *filter, int x0_q4, int x_step_q4, int y0_q4, int y_step_q4, int w, int h); #define vpx_scaled_vert vpx_scaled_vert_c -int64_t vpx_sse_c(const uint8_t *a, int a_stride, const uint8_t *b,int b_stride, int width, int height); -int64_t vpx_sse_neon(const uint8_t *a, int a_stride, const uint8_t *b,int b_stride, int width, int height); +int64_t vpx_sse_c(const uint8_t *src, int src_stride, const uint8_t *ref, int ref_stride, int width, int height); +int64_t vpx_sse_neon(const uint8_t *src, int src_stride, const uint8_t *ref, int ref_stride, int width, int height); #define vpx_sse vpx_sse_neon uint32_t vpx_sub_pixel_avg_variance16x16_c(const uint8_t *src_ptr, int src_stride, int x_offset, int y_offset, const uint8_t *ref_ptr, int ref_stride, uint32_t *sse, const uint8_t *second_pred); diff --git a/config/arm64/vpx_version.h b/config/arm64/vpx_version.h index 00ab40fe2..a06c0a0f1 100644 --- a/config/arm64/vpx_version.h +++ b/config/arm64/vpx_version.h @@ -1,8 +1,8 @@ // This file is generated. Do not edit. #define VERSION_MAJOR 1 #define VERSION_MINOR 14 -#define VERSION_PATCH 0 -#define VERSION_EXTRA "1616-g26104bbc9d" +#define VERSION_PATCH 1 +#define VERSION_EXTRA "1650-g0e5dcd1f52" #define VERSION_PACKED ((VERSION_MAJOR<<16)|(VERSION_MINOR<<8)|(VERSION_PATCH)) -#define VERSION_STRING_NOSP "v1.14.0-1616-g26104bbc9d" -#define VERSION_STRING " v1.14.0-1616-g26104bbc9d" +#define VERSION_STRING_NOSP "v1.14.1-1650-g0e5dcd1f52" +#define VERSION_STRING " v1.14.1-1650-g0e5dcd1f52" diff --git a/config/generic/vpx_dsp_rtcd.h b/config/generic/vpx_dsp_rtcd.h index 256cbdfa5..7f8cc7b7a 100644 --- a/config/generic/vpx_dsp_rtcd.h +++ b/config/generic/vpx_dsp_rtcd.h @@ -1492,7 +1492,7 @@ void vpx_scaled_horiz_c(const uint8_t *src, ptrdiff_t src_stride, uint8_t *dst, void vpx_scaled_vert_c(const uint8_t *src, ptrdiff_t src_stride, uint8_t *dst, ptrdiff_t dst_stride, const InterpKernel *filter, int x0_q4, int x_step_q4, int y0_q4, int y_step_q4, int w, int h); #define vpx_scaled_vert vpx_scaled_vert_c -int64_t vpx_sse_c(const uint8_t *a, int a_stride, const uint8_t *b,int b_stride, int width, int height); +int64_t vpx_sse_c(const uint8_t *src, int src_stride, const uint8_t *ref, int ref_stride, int width, int height); #define vpx_sse vpx_sse_c uint32_t vpx_sub_pixel_avg_variance16x16_c(const uint8_t *src_ptr, int src_stride, int x_offset, int y_offset, const uint8_t *ref_ptr, int ref_stride, uint32_t *sse, const uint8_t *second_pred); diff --git a/config/generic/vpx_version.h b/config/generic/vpx_version.h index 00ab40fe2..a06c0a0f1 100644 --- a/config/generic/vpx_version.h +++ b/config/generic/vpx_version.h @@ -1,8 +1,8 @@ // This file is generated. Do not edit. #define VERSION_MAJOR 1 #define VERSION_MINOR 14 -#define VERSION_PATCH 0 -#define VERSION_EXTRA "1616-g26104bbc9d" +#define VERSION_PATCH 1 +#define VERSION_EXTRA "1650-g0e5dcd1f52" #define VERSION_PACKED ((VERSION_MAJOR<<16)|(VERSION_MINOR<<8)|(VERSION_PATCH)) -#define VERSION_STRING_NOSP "v1.14.0-1616-g26104bbc9d" -#define VERSION_STRING " v1.14.0-1616-g26104bbc9d" +#define VERSION_STRING_NOSP "v1.14.1-1650-g0e5dcd1f52" +#define VERSION_STRING " v1.14.1-1650-g0e5dcd1f52" diff --git a/config/x86/vpx_dsp_rtcd.h b/config/x86/vpx_dsp_rtcd.h index 67c150490..5c6993a70 100644 --- a/config/x86/vpx_dsp_rtcd.h +++ b/config/x86/vpx_dsp_rtcd.h @@ -1931,7 +1931,7 @@ void vpx_scaled_horiz_c(const uint8_t *src, ptrdiff_t src_stride, uint8_t *dst, void vpx_scaled_vert_c(const uint8_t *src, ptrdiff_t src_stride, uint8_t *dst, ptrdiff_t dst_stride, const InterpKernel *filter, int x0_q4, int x_step_q4, int y0_q4, int y_step_q4, int w, int h); #define vpx_scaled_vert vpx_scaled_vert_c -int64_t vpx_sse_c(const uint8_t *a, int a_stride, const uint8_t *b,int b_stride, int width, int height); +int64_t vpx_sse_c(const uint8_t *src, int src_stride, const uint8_t *ref, int ref_stride, int width, int height); #define vpx_sse vpx_sse_c uint32_t vpx_sub_pixel_avg_variance16x16_c(const uint8_t *src_ptr, int src_stride, int x_offset, int y_offset, const uint8_t *ref_ptr, int ref_stride, uint32_t *sse, const uint8_t *second_pred); diff --git a/config/x86/vpx_version.h b/config/x86/vpx_version.h index 00ab40fe2..a06c0a0f1 100644 --- a/config/x86/vpx_version.h +++ b/config/x86/vpx_version.h @@ -1,8 +1,8 @@ // This file is generated. Do not edit. #define VERSION_MAJOR 1 #define VERSION_MINOR 14 -#define VERSION_PATCH 0 -#define VERSION_EXTRA "1616-g26104bbc9d" +#define VERSION_PATCH 1 +#define VERSION_EXTRA "1650-g0e5dcd1f52" #define VERSION_PACKED ((VERSION_MAJOR<<16)|(VERSION_MINOR<<8)|(VERSION_PATCH)) -#define VERSION_STRING_NOSP "v1.14.0-1616-g26104bbc9d" -#define VERSION_STRING " v1.14.0-1616-g26104bbc9d" +#define VERSION_STRING_NOSP "v1.14.1-1650-g0e5dcd1f52" +#define VERSION_STRING " v1.14.1-1650-g0e5dcd1f52" diff --git a/config/x86_64/vpx_dsp_rtcd.h b/config/x86_64/vpx_dsp_rtcd.h index 5eb512172..134856f05 100644 --- a/config/x86_64/vpx_dsp_rtcd.h +++ b/config/x86_64/vpx_dsp_rtcd.h @@ -1938,7 +1938,7 @@ void vpx_scaled_horiz_c(const uint8_t *src, ptrdiff_t src_stride, uint8_t *dst, void vpx_scaled_vert_c(const uint8_t *src, ptrdiff_t src_stride, uint8_t *dst, ptrdiff_t dst_stride, const InterpKernel *filter, int x0_q4, int x_step_q4, int y0_q4, int y_step_q4, int w, int h); #define vpx_scaled_vert vpx_scaled_vert_c -int64_t vpx_sse_c(const uint8_t *a, int a_stride, const uint8_t *b,int b_stride, int width, int height); +int64_t vpx_sse_c(const uint8_t *src, int src_stride, const uint8_t *ref, int ref_stride, int width, int height); #define vpx_sse vpx_sse_c uint32_t vpx_sub_pixel_avg_variance16x16_c(const uint8_t *src_ptr, int src_stride, int x_offset, int y_offset, const uint8_t *ref_ptr, int ref_stride, uint32_t *sse, const uint8_t *second_pred); diff --git a/config/x86_64/vpx_version.h b/config/x86_64/vpx_version.h index 00ab40fe2..a06c0a0f1 100644 --- a/config/x86_64/vpx_version.h +++ b/config/x86_64/vpx_version.h @@ -1,8 +1,8 @@ // This file is generated. Do not edit. #define VERSION_MAJOR 1 #define VERSION_MINOR 14 -#define VERSION_PATCH 0 -#define VERSION_EXTRA "1616-g26104bbc9d" +#define VERSION_PATCH 1 +#define VERSION_EXTRA "1650-g0e5dcd1f52" #define VERSION_PACKED ((VERSION_MAJOR<<16)|(VERSION_MINOR<<8)|(VERSION_PATCH)) -#define VERSION_STRING_NOSP "v1.14.0-1616-g26104bbc9d" -#define VERSION_STRING " v1.14.0-1616-g26104bbc9d" +#define VERSION_STRING_NOSP "v1.14.1-1650-g0e5dcd1f52" +#define VERSION_STRING " v1.14.1-1650-g0e5dcd1f52" @@ -315,7 +315,7 @@ $(BUILD_PFX)libvpx_g.a: $(LIBVPX_OBJS) # (c1, a1, r1) and set MAJOR to [c1-a1], MINOR to a1 and PATCH to r1 SO_VERSION_MAJOR := 9 SO_VERSION_MINOR := 0 -SO_VERSION_PATCH := 0 +SO_VERSION_PATCH := 1 ifeq ($(filter darwin%,$(TGT_OS)),$(TGT_OS)) LIBVPX_SO := libvpx.$(SO_VERSION_MAJOR).dylib SHARED_LIB_SUF := .dylib diff --git a/test/encode_api_test.cc b/test/encode_api_test.cc index a25dbc625..3bc38c537 100644 --- a/test/encode_api_test.cc +++ b/test/encode_api_test.cc @@ -8,13 +8,16 @@ * be found in the AUTHORS file in the root of the source tree. */ +#include <cassert> #include <climits> +#include <cstdint> #include <cstring> #include <initializer_list> #include <new> #include <vector> #include "third_party/googletest/src/include/gtest/gtest.h" +#include "test/acm_random.h" #include "test/codec_factory.h" #include "test/encode_test_driver.h" #include "test/i420_video_source.h" @@ -45,6 +48,49 @@ bool IsVP9(vpx_codec_iface_t *iface) { 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) { + 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; + + const int val = 1 << (bit_depth - 1); + 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, image->d_w); + } + for (unsigned int i = 0; i < uv_h; ++i) { + Memset16(image->planes[1] + i * image->stride[1], val, uv_w); + Memset16(image->planes[2] + i * image->stride[2], val, uv_w); + } + } else { + for (unsigned int i = 0; i < image->d_h; ++i) { + memset(image->planes[0] + i * image->stride[0], val, image->d_w); + } + for (unsigned int i = 0; i < uv_h; ++i) { + memset(image->planes[1] + i * image->stride[1], val, uv_w); + memset(image->planes[2] + i * image->stride[2], val, 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; @@ -210,6 +256,211 @@ TEST(EncodeAPI, HugeFramerateVp8) { 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); +} + +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, 0, 500000, flags, VPX_DL_REALTIME), + VPX_CODEC_OK); + ASSERT_EQ(vpx_codec_encode(&enc, nullptr, -1, 0, 0, 0), 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, 0, 500000, 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, unsigned long 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; + unsigned long 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, + unsigned long 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(11, 1685, 652, VPX_CBR, VPX_DL_REALTIME); + + encoder.Encode(true); + encoder.Encode(true); + encoder.Encode(true); + + encoder.Configure(0, 1685, 1, VPX_VBR, VPX_DL_REALTIME); +} + TEST(EncodeAPI, VP8GlobalHeaders) { constexpr int kWidth = 320; constexpr int kHeight = 240; @@ -228,6 +479,79 @@ TEST(EncodeAPI, VP8GlobalHeaders) { EXPECT_NO_FATAL_FAILURE(EncodeWithConfig(cfg, &enc.ctx)); EXPECT_EQ(vpx_codec_get_global_headers(&enc.ctx), nullptr); } + +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); +} #endif // CONFIG_VP8_ENCODER // Set up 2 spatial streams with 2 temporal layers per stream, and generate @@ -488,6 +812,48 @@ TEST(EncodeAPI, ConfigResizeChangeThreadCount) { } } +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, 1, 1, &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, 1, 1, &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); + } +} + #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 @@ -517,28 +883,18 @@ TEST(EncodeAPI, ConfigLargeTargetBitrateVp9) { } #endif // VPX_ARCH_X86_64 || VPX_ARCH_AARCH64 -vpx_image_t *CreateImage(const unsigned int width, const unsigned int height) { - vpx_image_t *image = - vpx_img_alloc(nullptr, VPX_IMG_FMT_I420, width, height, 1); - if (!image) return image; - - 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); - } - - return image; -} - // Emulates the WebCodecs VideoEncoder interface. class VP9Encoder { public: - VP9Encoder(int speed) : speed_(speed) {} + 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, @@ -547,6 +903,9 @@ class VP9Encoder { 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_; @@ -566,12 +925,22 @@ void VP9Encoder::Configure(unsigned int threads, unsigned int width, 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; @@ -579,8 +948,12 @@ void VP9Encoder::Configure(unsigned int threads, unsigned int width, 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_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; } @@ -594,14 +967,15 @@ void VP9Encoder::Configure(unsigned int threads, unsigned int width, } void VP9Encoder::Encode(bool key_frame) { + assert(initialized_); const vpx_codec_cx_pkt_t *pkt; - vpx_image_t *image = CreateImage(cfg_.g_w, cfg_.g_h); + vpx_image_t *image = CreateImage(bit_depth_, fmt_, cfg_.g_w, cfg_.g_h); 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_++; + ++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); @@ -934,6 +1308,166 @@ TEST(EncodeAPI, Buganizer311294795) { encoder.Encode(false); } +TEST(EncodeAPI, Buganizer317105128) { + VP9Encoder encoder(-9); + encoder.Configure(0, 1, 1, VPX_CBR, VPX_DL_GOOD_QUALITY); + encoder.Configure(16, 1920, 1, 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(8, 0, 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(8, 1, 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(6, 1, VPX_BITS_8, VPX_IMG_FMT_I420); + encoder.Configure(0, 1385, 1, VPX_CBR, VPX_DL_REALTIME); + encoder.Configure(0, 1, 1, VPX_VBR, VPX_DL_REALTIME); + encoder.Encode(false); + encoder.Configure(16, 1385, 1, VPX_VBR, VPX_DL_GOOD_QUALITY); + encoder.Encode(false); + encoder.Encode(false); + encoder.Configure(0, 1, 1, VPX_CBR, VPX_DL_REALTIME); + encoder.Encode(true); +} + +TEST(EncodeAPI, Buganizer331108729) { + VP9Encoder encoder(1, 1, VPX_BITS_8, VPX_IMG_FMT_I422); + encoder.Configure(0, 1919, 260, VPX_VBR, VPX_DL_REALTIME); + encoder.Configure(9, 440, 1, VPX_CBR, VPX_DL_GOOD_QUALITY); + encoder.Encode(true); + encoder.Configure(8, 1919, 260, VPX_VBR, VPX_DL_REALTIME); + encoder.Encode(false); +} + +TEST(EncodeAPI, Buganizer331108922BitDepth8) { + VP9Encoder encoder(9, 1, 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); +} + +#if CONFIG_VP9_HIGHBITDEPTH +TEST(EncodeAPI, Buganizer329674887RowMT0BitDepth12) { + VP9Encoder encoder(8, 0, 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(4, 0, 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(4, 1, 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(9, 1, 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); +} +#endif + TEST(EncodeAPI, VP9GlobalHeaders) { constexpr int kWidth = 320; constexpr int kHeight = 240; @@ -1049,6 +1583,78 @@ TEST(EncodeAPI, VP9GlobalHeaders) { } } +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); +} + #endif // CONFIG_VP9_ENCODER } // namespace diff --git a/test/sum_squares_test.cc b/test/sum_squares_test.cc index 725d5eb85..d3c76a34d 100644 --- a/test/sum_squares_test.cc +++ b/test/sum_squares_test.cc @@ -9,6 +9,7 @@ */ #include <cmath> +#include <cstdint> #include <cstdlib> #include <string> #include <tuple> diff --git a/test/test.mk b/test/test.mk index d4521f08b..fa5bf5633 100644 --- a/test/test.mk +++ b/test/test.mk @@ -21,6 +21,7 @@ LIBVPX_TEST_SRCS-yes += video_source.h ## Black box tests only use the public API. ## LIBVPX_TEST_SRCS-yes += ../md5_utils.h ../md5_utils.c +LIBVPX_TEST_SRCS-yes += vpx_image_test.cc LIBVPX_TEST_SRCS-$(CONFIG_DECODERS) += ivf_video_source.h LIBVPX_TEST_SRCS-$(CONFIG_ENCODERS) += ../y4minput.h ../y4minput.c LIBVPX_TEST_SRCS-$(CONFIG_ENCODERS) += altref_test.cc diff --git a/test/video_source.h b/test/video_source.h index 2194126f1..2c035910d 100644 --- a/test/video_source.h +++ b/test/video_source.h @@ -236,7 +236,6 @@ class RandomVideoSource : public DummyVideoSource { RandomVideoSource(int seed = ACMRandom::DeterministicSeed()) : rnd_(seed), seed_(seed) {} - protected: // Reset the RNG to get a matching stream for the second pass void Begin() override { frame_ = 0; @@ -244,6 +243,7 @@ class RandomVideoSource : public DummyVideoSource { FillFrame(); } + protected: // 15 frames of noise, followed by 15 static frames. Reset to 0 rather // than holding previous frames to encourage keyframes to be thrown. void FillFrame() override { diff --git a/test/vp8_datarate_test.cc b/test/vp8_datarate_test.cc index aee27af66..f7225bb3d 100644 --- a/test/vp8_datarate_test.cc +++ b/test/vp8_datarate_test.cc @@ -260,6 +260,27 @@ class DatarateTestLarge << " The datarate for the file missed the target!"; } + virtual void MultiThreadsPSNRTest() { + denoiser_on_ = 0; + cfg_.rc_buf_initial_sz = 500; + cfg_.rc_dropframe_thresh = 0; + cfg_.rc_max_quantizer = 56; + cfg_.rc_end_usage = VPX_CBR; + cfg_.g_threads = 4; + init_flags_ = VPX_CODEC_USE_PSNR; + + ::libvpx_test::I420VideoSource video("desktop_office1.1280_720-020.yuv", + 1280, 720, 30, 1, 0, 30); + cfg_.rc_target_bitrate = 1000; + ResetModel(); + ASSERT_NO_FATAL_FAILURE(RunLoop(&video)); + ASSERT_GE(cfg_.rc_target_bitrate, effective_datarate_ * 0.5) + << " The datarate for the file exceeds the target!"; + + ASSERT_LE(cfg_.rc_target_bitrate, file_datarate_ * 2.0) + << " The datarate for the file missed the target!"; + } + vpx_codec_pts_t last_pts_; int64_t bits_in_buffer_model_; double timebase_; @@ -324,6 +345,8 @@ TEST_P(DatarateTestRealTime, DropFramesMultiThreads) { DropFramesMultiThreadsTest(); } +TEST_P(DatarateTestRealTime, MultiThreadsPSNR) { MultiThreadsPSNRTest(); } + TEST_P(DatarateTestRealTime, RegionOfInterest) { denoiser_on_ = 0; cfg_.rc_buf_initial_sz = 500; diff --git a/test/vp9_boolcoder_test.cc b/test/vp9_boolcoder_test.cc index 6ba171a00..aeff0d7a5 100644 --- a/test/vp9_boolcoder_test.cc +++ b/test/vp9_boolcoder_test.cc @@ -53,7 +53,7 @@ TEST(VP9, TestBitIO) { ACMRandom bit_rnd(random_seed); vpx_writer bw; uint8_t bw_buffer[kBufferSize]; - vpx_start_encode(&bw, bw_buffer); + vpx_start_encode(&bw, bw_buffer, sizeof(bw_buffer)); int bit = (bit_method == 0) ? 0 : (bit_method == 1) ? 1 : 0; for (int i = 0; i < kBitsToTest; ++i) { @@ -65,7 +65,7 @@ TEST(VP9, TestBitIO) { vpx_write(&bw, bit, static_cast<int>(probas[i])); } - vpx_stop_encode(&bw); + GTEST_ASSERT_EQ(vpx_stop_encode(&bw), 0); // vpx_reader_fill() may read into uninitialized data that // isn't used meaningfully, but may trigger an MSan warning. memset(bw_buffer + bw.pos, 0, sizeof(BD_VALUE) - 1); @@ -90,3 +90,24 @@ TEST(VP9, TestBitIO) { } } } + +TEST(VP9, TestBitIOBufferSize0) { + vpx_writer bw; + uint8_t bw_buffer[1]; + vpx_start_encode(&bw, bw_buffer, 0); + GTEST_ASSERT_EQ(vpx_stop_encode(&bw), -1); +} + +TEST(VP9, TestBitIOBufferSize1) { + vpx_writer bw; + uint8_t bw_buffer[1]; + vpx_start_encode(&bw, bw_buffer, sizeof(bw_buffer)); + GTEST_ASSERT_EQ(vpx_stop_encode(&bw), -1); +} + +TEST(VP9, TestBitIOBufferSize2) { + vpx_writer bw; + uint8_t bw_buffer[2]; + vpx_start_encode(&bw, bw_buffer, sizeof(bw_buffer)); + GTEST_ASSERT_EQ(vpx_stop_encode(&bw), 0); +} diff --git a/test/vp9_scale_test.cc b/test/vp9_scale_test.cc index 049a10a61..a5a18a7e9 100644 --- a/test/vp9_scale_test.cc +++ b/test/vp9_scale_test.cc @@ -48,12 +48,11 @@ class ScaleTest : public VpxScaleBase, } void RunTest(INTERP_FILTER filter_type) { - static const int kNumSizesToTest = 20; + static const int kNumSizesToTest = 22; static const int kNumScaleFactorsToTest = 4; - static const int kSizesToTest[] = { - 2, 4, 6, 8, 10, 12, 14, 16, 18, 20, - 22, 24, 26, 28, 30, 32, 34, 68, 128, 134 - }; + static const int kSizesToTest[] = { 1, 2, 3, 4, 6, 8, 10, 12, + 14, 16, 18, 20, 22, 24, 26, 28, + 30, 32, 34, 68, 128, 134 }; static const int kScaleFactors[] = { 1, 2, 3, 4 }; for (int phase_scaler = 0; phase_scaler < 16; ++phase_scaler) { for (int h = 0; h < kNumSizesToTest; ++h) { diff --git a/test/vpx_image_test.cc b/test/vpx_image_test.cc new file mode 100644 index 000000000..3d24b239a --- /dev/null +++ b/test/vpx_image_test.cc @@ -0,0 +1,127 @@ +/* + * Copyright (c) 2024 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 <climits> + +#include "vpx/vpx_image.h" +#include "third_party/googletest/src/include/gtest/gtest.h" + +TEST(VpxImageTest, VpxImgWrapInvalidAlign) { + const int kWidth = 128; + const int kHeight = 128; + unsigned char buf[kWidth * kHeight * 3]; + + vpx_image_t img; + // Set img_data and img_data_owner to junk values. vpx_img_wrap() should + // not read these values on failure. + unsigned char empty[] = ""; + img.img_data = empty; + img.img_data_owner = 1; + + vpx_img_fmt_t format = VPX_IMG_FMT_I444; + // 'align' must be a power of 2 but is not. This causes the vpx_img_wrap() + // call to fail. The test verifies we do not read the junk values in 'img'. + unsigned int align = 31; + EXPECT_EQ(vpx_img_wrap(&img, format, kWidth, kHeight, align, buf), nullptr); +} + +TEST(VpxImageTest, VpxImgSetRectOverflow) { + const int kWidth = 128; + const int kHeight = 128; + unsigned char buf[kWidth * kHeight * 3]; + + vpx_image_t img; + vpx_img_fmt_t format = VPX_IMG_FMT_I444; + unsigned int align = 32; + EXPECT_EQ(vpx_img_wrap(&img, format, kWidth, kHeight, align, buf), &img); + + EXPECT_EQ(vpx_img_set_rect(&img, 0, 0, kWidth, kHeight), 0); + // This would result in overflow because -1 is cast to UINT_MAX. + EXPECT_NE(vpx_img_set_rect(&img, static_cast<unsigned int>(-1), + static_cast<unsigned int>(-1), kWidth, kHeight), + 0); +} + +TEST(VpxImageTest, VpxImgAllocNone) { + const int kWidth = 128; + const int kHeight = 128; + + vpx_image_t img; + vpx_img_fmt_t format = VPX_IMG_FMT_NONE; + unsigned int align = 32; + ASSERT_EQ(vpx_img_alloc(&img, format, kWidth, kHeight, align), nullptr); +} + +TEST(VpxImageTest, VpxImgAllocNv12) { + const int kWidth = 128; + const int kHeight = 128; + + vpx_image_t img; + vpx_img_fmt_t format = VPX_IMG_FMT_NV12; + unsigned int align = 32; + EXPECT_EQ(vpx_img_alloc(&img, format, kWidth, kHeight, align), &img); + EXPECT_EQ(img.stride[VPX_PLANE_U], img.stride[VPX_PLANE_Y]); + EXPECT_EQ(img.stride[VPX_PLANE_V], img.stride[VPX_PLANE_U]); + EXPECT_EQ(img.planes[VPX_PLANE_V], img.planes[VPX_PLANE_U] + 1); + vpx_img_free(&img); +} + +TEST(VpxImageTest, VpxImgAllocHugeWidth) { + // The stride (0x80000000 * 2) would overflow unsigned int. + vpx_image_t *image = + vpx_img_alloc(nullptr, VPX_IMG_FMT_I42016, 0x80000000, 1, 1); + ASSERT_EQ(image, nullptr); + + // The stride (0x80000000) would overflow int. + image = vpx_img_alloc(nullptr, VPX_IMG_FMT_I420, 0x80000000, 1, 1); + ASSERT_EQ(image, nullptr); + + // The aligned width (UINT_MAX + 1) would overflow unsigned int. + image = vpx_img_alloc(nullptr, VPX_IMG_FMT_I420, UINT_MAX, 1, 1); + ASSERT_EQ(image, nullptr); + + image = vpx_img_alloc(nullptr, VPX_IMG_FMT_I420, 0x7ffffffe, 1, 1); + if (image) { + vpx_img_free(image); + } + + image = vpx_img_alloc(nullptr, VPX_IMG_FMT_I420, 285245883, 64, 1); + if (image) { + vpx_img_free(image); + } + + image = vpx_img_alloc(nullptr, VPX_IMG_FMT_NV12, 285245883, 64, 1); + if (image) { + vpx_img_free(image); + } + + image = vpx_img_alloc(nullptr, VPX_IMG_FMT_YV12, 285245883, 64, 1); + if (image) { + vpx_img_free(image); + } + + image = vpx_img_alloc(nullptr, VPX_IMG_FMT_I42016, 65536, 2, 1); + if (image) { + uint16_t *y_plane = + reinterpret_cast<uint16_t *>(image->planes[VPX_PLANE_Y]); + y_plane[0] = 0; + y_plane[image->d_w - 1] = 0; + vpx_img_free(image); + } + + image = vpx_img_alloc(nullptr, VPX_IMG_FMT_I42016, 285245883, 2, 1); + if (image) { + uint16_t *y_plane = + reinterpret_cast<uint16_t *>(image->planes[VPX_PLANE_Y]); + y_plane[0] = 0; + y_plane[image->d_w - 1] = 0; + vpx_img_free(image); + } +} diff --git a/vp8/decoder/threading.c b/vp8/decoder/threading.c index 6ccb080cf..f7f5ebea8 100644 --- a/vp8/decoder/threading.c +++ b/vp8/decoder/threading.c @@ -8,6 +8,8 @@ * be found in the AUTHORS file in the root of the source tree. */ +#include <errno.h> + #include "vpx_config.h" #include "vp8_rtcd.h" #if !defined(_WIN32) && CONFIG_OS_SUPPORT == 1 @@ -892,16 +894,23 @@ int vp8mt_decode_mb_rows(VP8D_COMP *pbi, MACROBLOCKD *xd) { // Wait for other threads to finish. This prevents other threads decoding // the current frame while the main thread starts decoding the next frame, // which causes a data race. - for (i = 0; i < pbi->decoding_thread_count; ++i) - sem_wait(&pbi->h_event_end_decoding); + for (i = 0; i < pbi->decoding_thread_count; ++i) { + errno = 0; + while (sem_wait(&pbi->h_event_end_decoding) != 0 && errno == EINTR) { + } + } return -1; } xd->error_info.setjmp = 1; mt_decode_mb_rows(pbi, xd, 0); - for (i = 0; i < pbi->decoding_thread_count + 1; ++i) - sem_wait(&pbi->h_event_end_decoding); /* add back for each frame */ + for (i = 0; i < pbi->decoding_thread_count + 1; ++i) { + /* add back for each frame */ + errno = 0; + while (sem_wait(&pbi->h_event_end_decoding) != 0 && errno == EINTR) { + } + } return 0; } diff --git a/vp8/encoder/encodeframe.c b/vp8/encoder/encodeframe.c index 5c973940e..e3960de90 100644 --- a/vp8/encoder/encodeframe.c +++ b/vp8/encoder/encodeframe.c @@ -7,6 +7,7 @@ * in the file PATENTS. All contributing project authors may * be found in the AUTHORS file in the root of the source tree. */ +#include <errno.h> #include <stdio.h> #include <limits.h> @@ -447,13 +448,21 @@ static void encode_mb_row(VP8_COMP *cpi, VP8_COMMON *cm, int mb_row, x->active_ptr = cpi->active_map + map_index + mb_col; if (cm->frame_type == KEY_FRAME) { - *totalrate += vp8cx_encode_intra_macroblock(cpi, x, tp); + const int intra_rate_cost = vp8cx_encode_intra_macroblock(cpi, x, tp); + if (INT_MAX - *totalrate > intra_rate_cost) + *totalrate += intra_rate_cost; + else + *totalrate = INT_MAX; #ifdef MODE_STATS y_modes[xd->mbmi.mode]++; #endif } else { - *totalrate += vp8cx_encode_inter_macroblock( + const int inter_rate_cost = vp8cx_encode_inter_macroblock( cpi, x, tp, recon_yoffset, recon_uvoffset, mb_row, mb_col); + if (INT_MAX - *totalrate > inter_rate_cost) + *totalrate += inter_rate_cost; + else + *totalrate = INT_MAX; #ifdef MODE_STATS inter_y_modes[xd->mbmi.mode]++; @@ -798,7 +807,9 @@ void vp8_encode_frame(VP8_COMP *cpi) { } /* Wait for all the threads to finish. */ for (i = 0; i < cpi->encoding_thread_count; ++i) { - sem_wait(&cpi->h_event_end_encoding[i]); + errno = 0; + while (sem_wait(&cpi->h_event_end_encoding[i]) != 0 && errno == EINTR) { + } } for (mb_row = 0; mb_row < cm->mb_rows; ++mb_row) { diff --git a/vp8/encoder/onyx_if.c b/vp8/encoder/onyx_if.c index 4e128e3c4..cb64aca31 100644 --- a/vp8/encoder/onyx_if.c +++ b/vp8/encoder/onyx_if.c @@ -55,6 +55,7 @@ #endif #include <assert.h> +#include <errno.h> #include <math.h> #include <stdio.h> #include <limits.h> @@ -267,7 +268,11 @@ static int rescale(int val, int num, int denom) { int64_t llden = denom; int64_t llval = val; - return (int)(llval * llnum / llden); + int64_t result = (llval * llnum / llden); + if (result <= INT_MAX) + return (int)result; + else + return INT_MAX; } void vp8_init_temporal_layer_context(VP8_COMP *cpi, const VP8_CONFIG *oxcf, @@ -276,7 +281,10 @@ void vp8_init_temporal_layer_context(VP8_COMP *cpi, const VP8_CONFIG *oxcf, LAYER_CONTEXT *lc = &cpi->layer_context[layer]; lc->framerate = cpi->output_framerate / cpi->oxcf.rate_decimator[layer]; - lc->target_bandwidth = cpi->oxcf.target_bitrate[layer] * 1000; + if (cpi->oxcf.target_bitrate[layer] > INT_MAX / 1000) + lc->target_bandwidth = INT_MAX; + else + lc->target_bandwidth = cpi->oxcf.target_bitrate[layer] * 1000; lc->starting_buffer_level_in_ms = oxcf->starting_buffer_level; lc->optimal_buffer_level_in_ms = oxcf->optimal_buffer_level; @@ -1259,11 +1267,13 @@ void vp8_new_framerate(VP8_COMP *cpi, double framerate) { cpi->framerate = framerate; cpi->output_framerate = framerate; - cpi->per_frame_bandwidth = - (int)round(cpi->oxcf.target_bandwidth / cpi->output_framerate); + const double per_frame_bandwidth = + round(cpi->oxcf.target_bandwidth / cpi->output_framerate); + cpi->per_frame_bandwidth = (int)VPXMIN(per_frame_bandwidth, INT_MAX); cpi->av_per_frame_bandwidth = cpi->per_frame_bandwidth; - cpi->min_frame_bandwidth = (int)(cpi->av_per_frame_bandwidth * - cpi->oxcf.two_pass_vbrmin_section / 100); + const int64_t vbr_min_bits = (int64_t)cpi->av_per_frame_bandwidth * + cpi->oxcf.two_pass_vbrmin_section / 100; + cpi->min_frame_bandwidth = (int)VPXMIN(vbr_min_bits, INT_MAX); /* Set Maximum gf/arf interval */ cpi->max_gf_interval = ((int)(cpi->output_framerate / 2.0) + 2); @@ -1381,7 +1391,10 @@ void vp8_update_layer_contexts(VP8_COMP *cpi) { LAYER_CONTEXT *lc = &cpi->layer_context[i]; lc->framerate = cpi->ref_framerate / oxcf->rate_decimator[i]; - lc->target_bandwidth = oxcf->target_bitrate[i] * 1000; + if (oxcf->target_bitrate[i] > INT_MAX / 1000) + lc->target_bandwidth = INT_MAX; + else + lc->target_bandwidth = oxcf->target_bitrate[i] * 1000; lc->starting_buffer_level = rescale( (int)oxcf->starting_buffer_level_in_ms, lc->target_bandwidth, 1000); @@ -3339,10 +3352,12 @@ static void encode_frame_to_data_rate(VP8_COMP *cpi, size_t *size, } break; #endif // !CONFIG_REALTIME_ONLY - default: - cpi->per_frame_bandwidth = - (int)round(cpi->target_bandwidth / cpi->output_framerate); + default: { + const double per_frame_bandwidth = + round(cpi->target_bandwidth / cpi->output_framerate); + cpi->per_frame_bandwidth = (int)VPXMIN(per_frame_bandwidth, INT_MAX); break; + } } /* Default turn off buffer to buffer copying */ @@ -4391,7 +4406,9 @@ static void encode_frame_to_data_rate(VP8_COMP *cpi, size_t *size, cpi->b_lpf_running = 1; /* wait for the filter_level to be picked so that we can continue with * stream packing */ - sem_wait(&cpi->h_event_end_lpf); + errno = 0; + while (sem_wait(&cpi->h_event_end_lpf) != 0 && errno == EINTR) { + } } else #endif { @@ -5120,6 +5137,16 @@ int vp8_get_compressed_data(VP8_COMP *cpi, unsigned int *frame_flags, vpx_usec_timer_mark(&cmptimer); cpi->time_compress_data += vpx_usec_timer_elapsed(&cmptimer); +#if CONFIG_MULTITHREAD + /* wait for the lpf thread done */ + if (vpx_atomic_load_acquire(&cpi->b_multi_threaded) && cpi->b_lpf_running) { + errno = 0; + while (sem_wait(&cpi->h_event_end_lpf) != 0 && errno == EINTR) { + } + cpi->b_lpf_running = 0; + } +#endif + if (cpi->b_calculate_psnr && cpi->pass != 1 && cm->show_frame) { generate_psnr_packet(cpi); } @@ -5249,14 +5276,6 @@ int vp8_get_compressed_data(VP8_COMP *cpi, unsigned int *frame_flags, cpi->common.error.setjmp = 0; -#if CONFIG_MULTITHREAD - /* wait for the lpf thread done */ - if (vpx_atomic_load_acquire(&cpi->b_multi_threaded) && cpi->b_lpf_running) { - sem_wait(&cpi->h_event_end_lpf); - cpi->b_lpf_running = 0; - } -#endif - return 0; } diff --git a/vp8/encoder/ratectrl.c b/vp8/encoder/ratectrl.c index fcd4eb04e..7ba7a308a 100644 --- a/vp8/encoder/ratectrl.c +++ b/vp8/encoder/ratectrl.c @@ -791,8 +791,12 @@ static void calc_pframe_target_size(VP8_COMP *cpi) { (int)((cpi->buffer_level - cpi->oxcf.optimal_buffer_level) / one_percent_bits); } else if (cpi->bits_off_target > cpi->oxcf.optimal_buffer_level) { - percent_high = - (int)((100 * cpi->bits_off_target) / (cpi->total_byte_count * 8)); + if (cpi->total_byte_count > 0) { + percent_high = (int)((100 * cpi->bits_off_target) / + (cpi->total_byte_count * 8)); + } else { + percent_high = cpi->oxcf.over_shoot_pct; + } } if (percent_high > cpi->oxcf.over_shoot_pct) { @@ -1190,10 +1194,13 @@ int vp8_regulate_q(VP8_COMP *cpi, int target_bits_per_frame) { /* Calculate required scaling factor based on target frame size and * size of frame produced using previous Q */ - if (target_bits_per_frame >= (INT_MAX >> BPER_MB_NORMBITS)) { - /* Case where we would overflow int */ - target_bits_per_mb = (target_bits_per_frame / cpi->common.MBs) - << BPER_MB_NORMBITS; + if (target_bits_per_frame > (INT_MAX >> BPER_MB_NORMBITS)) { + int temp = target_bits_per_frame / cpi->common.MBs; + if (temp > (INT_MAX >> BPER_MB_NORMBITS)) { + target_bits_per_mb = INT_MAX; + } else { + target_bits_per_mb = temp << BPER_MB_NORMBITS; + } } else { target_bits_per_mb = (target_bits_per_frame << BPER_MB_NORMBITS) / cpi->common.MBs; @@ -1534,9 +1541,13 @@ int vp8_drop_encodedframe_overshoot(VP8_COMP *cpi, int Q) { // undershoots significantly, and then we end up dropping every other // frame because the QP/rate_correction_factor may have been too low // before the drop and then takes too long to come up. - if (target_size >= (INT_MAX >> BPER_MB_NORMBITS)) { - target_bits_per_mb = (target_size / cpi->common.MBs) - << BPER_MB_NORMBITS; + if (target_size > (INT_MAX >> BPER_MB_NORMBITS)) { + int temp = target_size / cpi->common.MBs; + if (temp > (INT_MAX >> BPER_MB_NORMBITS)) { + target_bits_per_mb = INT_MAX; + } else { + target_bits_per_mb = temp << BPER_MB_NORMBITS; + } } else { target_bits_per_mb = (target_size << BPER_MB_NORMBITS) / cpi->common.MBs; diff --git a/vp9/encoder/vp9_bitstream.c b/vp9/encoder/vp9_bitstream.c index ca56d14aa..1b07b9a34 100644 --- a/vp9/encoder/vp9_bitstream.c +++ b/vp9/encoder/vp9_bitstream.c @@ -9,6 +9,7 @@ */ #include <assert.h> +#include <stdint.h> #include <stdio.h> #include <limits.h> @@ -943,12 +944,11 @@ static int encode_tile_worker(void *arg1, void *arg2) { VP9BitstreamWorkerData *data = (VP9BitstreamWorkerData *)arg2; MACROBLOCKD *const xd = &data->xd; const int tile_row = 0; - vpx_start_encode(&data->bit_writer, data->dest); + vpx_start_encode(&data->bit_writer, data->dest, data->dest_size); write_modes(cpi, xd, &cpi->tile_data[data->tile_idx].tile_info, &data->bit_writer, tile_row, data->tile_idx, &data->max_mv_magnitude, data->interp_filter_selected); - vpx_stop_encode(&data->bit_writer); - return 1; + return vpx_stop_encode(&data->bit_writer) == 0; } void vp9_bitstream_encode_tiles_buffer_dealloc(VP9_COMP *const cpi) { @@ -962,6 +962,16 @@ void vp9_bitstream_encode_tiles_buffer_dealloc(VP9_COMP *const cpi) { } } +static int encode_tiles_buffer_alloc_size(VP9_COMP *const cpi) { + VP9_COMMON *const cm = &cpi->common; + const int image_bps = + (8 + 2 * (8 >> (cm->subsampling_x + cm->subsampling_y))) * + (1 + (cm->bit_depth > 8)); + const int64_t size = + (int64_t)cpi->oxcf.width * cpi->oxcf.height * image_bps / 8; + return (int)size; +} + static void encode_tiles_buffer_alloc(VP9_COMP *const cpi) { VP9_COMMON *const cm = &cpi->common; int i; @@ -972,23 +982,25 @@ static void encode_tiles_buffer_alloc(VP9_COMP *const cpi) { memset(cpi->vp9_bitstream_worker_data, 0, worker_data_size); for (i = 1; i < cpi->num_workers; ++i) { cpi->vp9_bitstream_worker_data[i].dest_size = - cpi->oxcf.width * cpi->oxcf.height; + encode_tiles_buffer_alloc_size(cpi); CHECK_MEM_ERROR(&cm->error, cpi->vp9_bitstream_worker_data[i].dest, vpx_malloc(cpi->vp9_bitstream_worker_data[i].dest_size)); } } -static size_t encode_tiles_mt(VP9_COMP *cpi, uint8_t *data_ptr) { +static size_t encode_tiles_mt(VP9_COMP *cpi, uint8_t *data_ptr, + size_t data_size) { const VPxWorkerInterface *const winterface = vpx_get_worker_interface(); VP9_COMMON *const cm = &cpi->common; const int tile_cols = 1 << cm->log2_tile_cols; const int num_workers = cpi->num_workers; size_t total_size = 0; int tile_col = 0; + int error = 0; if (!cpi->vp9_bitstream_worker_data || - cpi->vp9_bitstream_worker_data[1].dest_size > - (cpi->oxcf.width * cpi->oxcf.height)) { + cpi->vp9_bitstream_worker_data[1].dest_size != + encode_tiles_buffer_alloc_size(cpi)) { vp9_bitstream_encode_tiles_buffer_dealloc(cpi); encode_tiles_buffer_alloc(cpi); } @@ -1010,8 +1022,13 @@ static size_t encode_tiles_mt(VP9_COMP *cpi, uint8_t *data_ptr) { if (i == 0) { // If this worker happens to be for the last tile, then do not offset it // by 4 for the tile size. - data->dest = - data_ptr + total_size + (tile_col == tile_cols - 1 ? 0 : 4); + const size_t offset = total_size + (tile_col == tile_cols - 1 ? 0 : 4); + if (data_size < offset) { + vpx_internal_error(&cm->error, VPX_CODEC_ERROR, + "encode_tiles_mt: output buffer full"); + } + data->dest = data_ptr + offset; + data->dest_size = data_size - offset; } worker->data1 = cpi; worker->data2 = data; @@ -1032,7 +1049,11 @@ static size_t encode_tiles_mt(VP9_COMP *cpi, uint8_t *data_ptr) { uint32_t tile_size; int k; - if (!winterface->sync(worker)) return 0; + if (!winterface->sync(worker)) { + error = 1; + continue; + } + tile_size = data->bit_writer.pos; // Aggregate per-thread bitstream stats. @@ -1044,19 +1065,31 @@ static size_t encode_tiles_mt(VP9_COMP *cpi, uint8_t *data_ptr) { // Prefix the size of the tile on all but the last. if (tile_col != tile_cols || j < i - 1) { + if (data_size - total_size < 4) { + error = 1; + continue; + } mem_put_be32(data_ptr + total_size, tile_size); total_size += 4; } if (j > 0) { + if (data_size - total_size < tile_size) { + error = 1; + continue; + } memcpy(data_ptr + total_size, data->dest, tile_size); } total_size += tile_size; } + if (error) { + vpx_internal_error(&cm->error, VPX_CODEC_ERROR, + "encode_tiles_mt: output buffer full"); + } } return total_size; } -static size_t encode_tiles(VP9_COMP *cpi, uint8_t *data_ptr) { +static size_t encode_tiles(VP9_COMP *cpi, uint8_t *data_ptr, size_t data_size) { VP9_COMMON *const cm = &cpi->common; MACROBLOCKD *const xd = &cpi->td.mb.e_mbd; vpx_writer residual_bc; @@ -1073,23 +1106,32 @@ static size_t encode_tiles(VP9_COMP *cpi, uint8_t *data_ptr) { // that it does not make the overall process worse in any case. if (cpi->oxcf.mode == REALTIME && cpi->num_workers > 1 && tile_rows == 1 && tile_cols > 1) { - return encode_tiles_mt(cpi, data_ptr); + return encode_tiles_mt(cpi, data_ptr, data_size); } for (tile_row = 0; tile_row < tile_rows; tile_row++) { for (tile_col = 0; tile_col < tile_cols; tile_col++) { int tile_idx = tile_row * tile_cols + tile_col; + size_t offset; if (tile_col < tile_cols - 1 || tile_row < tile_rows - 1) - vpx_start_encode(&residual_bc, data_ptr + total_size + 4); + offset = total_size + 4; else - vpx_start_encode(&residual_bc, data_ptr + total_size); + offset = total_size; + if (data_size < offset) { + vpx_internal_error(&cm->error, VPX_CODEC_ERROR, + "encode_tiles: output buffer full"); + } + vpx_start_encode(&residual_bc, data_ptr + offset, data_size - offset); write_modes(cpi, xd, &cpi->tile_data[tile_idx].tile_info, &residual_bc, tile_row, tile_col, &cpi->max_mv_magnitude, cpi->interp_filter_selected); - vpx_stop_encode(&residual_bc); + if (vpx_stop_encode(&residual_bc)) { + vpx_internal_error(&cm->error, VPX_CODEC_ERROR, + "encode_tiles: output buffer full"); + } if (tile_col < tile_cols - 1 || tile_row < tile_rows - 1) { // size of this tile mem_put_be32(data_ptr + total_size, residual_bc.pos); @@ -1271,14 +1313,15 @@ static void write_uncompressed_header(VP9_COMP *cpi, write_tile_info(cm, wb); } -static size_t write_compressed_header(VP9_COMP *cpi, uint8_t *data) { +static size_t write_compressed_header(VP9_COMP *cpi, uint8_t *data, + size_t data_size) { VP9_COMMON *const cm = &cpi->common; MACROBLOCKD *const xd = &cpi->td.mb.e_mbd; FRAME_CONTEXT *const fc = cm->fc; FRAME_COUNTS *counts = cpi->td.counts; vpx_writer header_bc; - vpx_start_encode(&header_bc, data); + vpx_start_encode(&header_bc, data, data_size); if (xd->lossless) cm->tx_mode = ONLY_4X4; @@ -1342,26 +1385,36 @@ static size_t write_compressed_header(VP9_COMP *cpi, uint8_t *data) { &counts->mv); } - vpx_stop_encode(&header_bc); - assert(header_bc.pos <= 0xffff); + if (vpx_stop_encode(&header_bc)) { + vpx_internal_error(&cm->error, VPX_CODEC_ERROR, + "write_compressed_header: output buffer full"); + } return header_bc.pos; } -void vp9_pack_bitstream(VP9_COMP *cpi, uint8_t *dest, size_t *size) { +void vp9_pack_bitstream(VP9_COMP *cpi, uint8_t *dest, size_t dest_size, + size_t *size) { + VP9_COMMON *const cm = &cpi->common; uint8_t *data = dest; - size_t first_part_size, uncompressed_hdr_size; - struct vpx_write_bit_buffer wb = { data, 0 }; + size_t data_size = dest_size; + size_t uncompressed_hdr_size, compressed_hdr_size; + struct vpx_write_bit_buffer wb; struct vpx_write_bit_buffer saved_wb; #if CONFIG_BITSTREAM_DEBUG bitstream_queue_reset_write(); #endif + vpx_wb_init(&wb, data, data_size); write_uncompressed_header(cpi, &wb); + if (vpx_wb_has_error(&wb)) { + vpx_internal_error(&cm->error, VPX_CODEC_ERROR, + "vp9_pack_bitstream: output buffer full"); + } // Skip the rest coding process if use show existing frame. - if (cpi->common.show_existing_frame) { + if (cm->show_existing_frame) { uncompressed_hdr_size = vpx_wb_bytes_written(&wb); data += uncompressed_hdr_size; *size = data - dest; @@ -1369,19 +1422,30 @@ void vp9_pack_bitstream(VP9_COMP *cpi, uint8_t *dest, size_t *size) { } saved_wb = wb; - vpx_wb_write_literal(&wb, 0, 16); // don't know in advance first part. size + // don't know in advance compressed header size + vpx_wb_write_literal(&wb, 0, 16); + if (vpx_wb_has_error(&wb)) { + vpx_internal_error(&cm->error, VPX_CODEC_ERROR, + "vp9_pack_bitstream: output buffer full"); + } uncompressed_hdr_size = vpx_wb_bytes_written(&wb); data += uncompressed_hdr_size; + data_size -= uncompressed_hdr_size; vpx_clear_system_state(); - first_part_size = write_compressed_header(cpi, data); - data += first_part_size; - // TODO(jbb): Figure out what to do if first_part_size > 16 bits. - vpx_wb_write_literal(&saved_wb, (int)first_part_size, 16); + compressed_hdr_size = write_compressed_header(cpi, data, data_size); + data += compressed_hdr_size; + data_size -= compressed_hdr_size; + if (compressed_hdr_size > UINT16_MAX) { + vpx_internal_error(&cm->error, VPX_CODEC_ERROR, + "compressed_hdr_size > 16 bits"); + } + vpx_wb_write_literal(&saved_wb, (int)compressed_hdr_size, 16); + assert(!vpx_wb_has_error(&saved_wb)); - data += encode_tiles(cpi, data); + data += encode_tiles(cpi, data, data_size); *size = data - dest; } diff --git a/vp9/encoder/vp9_bitstream.h b/vp9/encoder/vp9_bitstream.h index 208651dc2..e367abc30 100644 --- a/vp9/encoder/vp9_bitstream.h +++ b/vp9/encoder/vp9_bitstream.h @@ -35,7 +35,8 @@ int vp9_get_refresh_mask(VP9_COMP *cpi); void vp9_bitstream_encode_tiles_buffer_dealloc(VP9_COMP *const cpi); -void vp9_pack_bitstream(VP9_COMP *cpi, uint8_t *dest, size_t *size); +void vp9_pack_bitstream(VP9_COMP *cpi, uint8_t *dest, size_t dest_size, + size_t *size); static INLINE int vp9_preserve_existing_gf(VP9_COMP *cpi) { return cpi->refresh_golden_frame && cpi->rc.is_src_frame_alt_ref && diff --git a/vp9/encoder/vp9_encodeframe.c b/vp9/encoder/vp9_encodeframe.c index 7ff5f00ed..e8f29d5d2 100644 --- a/vp9/encoder/vp9_encodeframe.c +++ b/vp9/encoder/vp9_encodeframe.c @@ -48,6 +48,7 @@ #include "vp9/encoder/vp9_encodeframe.h" #include "vp9/encoder/vp9_encodemb.h" #include "vp9/encoder/vp9_encodemv.h" +#include "vp9/encoder/vp9_encoder.h" #include "vp9/encoder/vp9_ethread.h" #include "vp9/encoder/vp9_extend.h" #include "vp9/encoder/vp9_multi_thread.h" @@ -5851,7 +5852,12 @@ void vp9_init_tile_data(VP9_COMP *cpi) { int tplist_count = 0; if (cpi->tile_data == NULL || cpi->allocated_tiles < tile_cols * tile_rows) { - if (cpi->tile_data != NULL) vpx_free(cpi->tile_data); + if (cpi->tile_data != NULL) { + // Free the row mt memory in cpi->tile_data first. + vp9_row_mt_mem_dealloc(cpi); + vpx_free(cpi->tile_data); + } + cpi->allocated_tiles = 0; CHECK_MEM_ERROR( &cm->error, cpi->tile_data, vpx_malloc(tile_cols * tile_rows * sizeof(*cpi->tile_data))); @@ -5881,9 +5887,9 @@ void vp9_init_tile_data(VP9_COMP *cpi) { for (tile_col = 0; tile_col < tile_cols; ++tile_col) { TileDataEnc *this_tile = &cpi->tile_data[tile_row * tile_cols + tile_col]; TileInfo *tile_info = &this_tile->tile_info; - if (cpi->sf.adaptive_rd_thresh_row_mt && - this_tile->row_base_thresh_freq_fact == NULL) + if (cpi->sf.adaptive_rd_thresh_row_mt) { vp9_row_mt_alloc_rd_thresh(cpi, this_tile); + } vp9_tile_init(tile_info, cm, tile_row, tile_col); cpi->tile_tok[tile_row][tile_col] = pre_tok + tile_tok; diff --git a/vp9/encoder/vp9_encoder.c b/vp9/encoder/vp9_encoder.c index 152d42bc9..42a2770d1 100644 --- a/vp9/encoder/vp9_encoder.c +++ b/vp9/encoder/vp9_encoder.c @@ -2134,24 +2134,22 @@ void vp9_change_config(struct VP9_COMP *cpi, const VP9EncoderConfig *oxcf) { cpi->external_resize = 1; } - if (cpi->initial_width) { - int new_mi_size = 0; - vp9_set_mb_mi(cm, cm->width, cm->height); - new_mi_size = cm->mi_stride * calc_mi_size(cm->mi_rows); - if (cm->mi_alloc_size < new_mi_size) { - vp9_free_context_buffers(cm); - vp9_free_pc_tree(&cpi->td); - vpx_free(cpi->mbmi_ext_base); - alloc_compressor_data(cpi); - realloc_segmentation_maps(cpi); - cpi->initial_width = cpi->initial_height = 0; - cpi->external_resize = 0; - } else if (cm->mi_alloc_size == new_mi_size && - (cpi->oxcf.width > last_w || cpi->oxcf.height > last_h)) { - if (vp9_alloc_loop_filter(cm)) { - vpx_internal_error(&cm->error, VPX_CODEC_MEM_ERROR, - "Failed to allocate loop filter data"); - } + int new_mi_size = 0; + vp9_set_mb_mi(cm, cm->width, cm->height); + new_mi_size = cm->mi_stride * calc_mi_size(cm->mi_rows); + if (cm->mi_alloc_size < new_mi_size) { + vp9_free_context_buffers(cm); + vp9_free_pc_tree(&cpi->td); + vpx_free(cpi->mbmi_ext_base); + alloc_compressor_data(cpi); + realloc_segmentation_maps(cpi); + cpi->initial_width = cpi->initial_height = 0; + cpi->external_resize = 0; + } else if (cm->mi_alloc_size == new_mi_size && + (cpi->oxcf.width > last_w || cpi->oxcf.height > last_h)) { + if (vp9_alloc_loop_filter(cm)) { + vpx_internal_error(&cm->error, VPX_CODEC_MEM_ERROR, + "Failed to allocate loop filter data"); } } @@ -3958,7 +3956,7 @@ static INLINE void set_raw_source_frame(VP9_COMP *cpi) { } static int encode_without_recode_loop(VP9_COMP *cpi, size_t *size, - uint8_t *dest) { + uint8_t *dest, size_t dest_size) { VP9_COMMON *const cm = &cpi->common; SVC *const svc = &cpi->svc; int q = 0, bottom_index = 0, top_index = 0; @@ -4254,7 +4252,7 @@ static int encode_without_recode_loop(VP9_COMP *cpi, size_t *size, int frame_size = 0; // Get an estimate of the encoded frame size. save_coding_context(cpi); - vp9_pack_bitstream(cpi, dest, size); + vp9_pack_bitstream(cpi, dest, dest_size, size); restore_coding_context(cpi); frame_size = (int)(*size) << 3; // Check if encoded frame will overshoot too much, and if so, set the q and @@ -4457,7 +4455,8 @@ static void rq_model_update(const RATE_QINDEX_HISTORY *rq_history, } #endif // CONFIG_RATE_CTRL -static void encode_with_recode_loop(VP9_COMP *cpi, size_t *size, uint8_t *dest +static void encode_with_recode_loop(VP9_COMP *cpi, size_t *size, uint8_t *dest, + size_t dest_size #if CONFIG_RATE_CTRL , RATE_QINDEX_HISTORY *rq_history @@ -4680,7 +4679,8 @@ static void encode_with_recode_loop(VP9_COMP *cpi, size_t *size, uint8_t *dest // to recode. if (cpi->sf.recode_loop >= ALLOW_RECODE_KFARFGF) { save_coding_context(cpi); - if (!cpi->sf.use_nonrd_pick_mode) vp9_pack_bitstream(cpi, dest, size); + if (!cpi->sf.use_nonrd_pick_mode) + vp9_pack_bitstream(cpi, dest, dest_size, size); rc->projected_frame_size = (int)(*size) << 3; @@ -5231,7 +5231,7 @@ static void spatial_denoise_frame(VP9_COMP *cpi) { #if !CONFIG_REALTIME_ONLY static void vp9_try_disable_lookahead_aq(VP9_COMP *cpi, size_t *size, - uint8_t *dest) { + uint8_t *dest, size_t dest_size) { if (cpi->common.seg.enabled) if (ALT_REF_AQ_PROTECT_GAIN) { size_t nsize = *size; @@ -5242,7 +5242,7 @@ static void vp9_try_disable_lookahead_aq(VP9_COMP *cpi, size_t *size, save_coding_context(cpi); vp9_disable_segmentation(&cpi->common.seg); - vp9_pack_bitstream(cpi, dest, &nsize); + vp9_pack_bitstream(cpi, dest, dest_size, &nsize); restore_coding_context(cpi); overhead = (int)*size - (int)nsize; @@ -5535,8 +5535,8 @@ static void update_encode_frame_result_simple_encode( #endif // !CONFIG_REALTIME_ONLY static void encode_frame_to_data_rate( - VP9_COMP *cpi, size_t *size, uint8_t *dest, unsigned int *frame_flags, - ENCODE_FRAME_RESULT *encode_frame_result) { + VP9_COMP *cpi, size_t *size, uint8_t *dest, size_t dest_size, + unsigned int *frame_flags, ENCODE_FRAME_RESULT *encode_frame_result) { VP9_COMMON *const cm = &cpi->common; const VP9EncoderConfig *const oxcf = &cpi->oxcf; struct segmentation *const seg = &cm->seg; @@ -5643,16 +5643,17 @@ static void encode_frame_to_data_rate( } if (cpi->sf.recode_loop == DISALLOW_RECODE) { - if (!encode_without_recode_loop(cpi, size, dest)) return; + if (!encode_without_recode_loop(cpi, size, dest, dest_size)) return; } else { #if !CONFIG_REALTIME_ONLY #if CONFIG_RATE_CTRL - encode_with_recode_loop(cpi, size, dest, &encode_frame_result->rq_history); + encode_with_recode_loop(cpi, size, dest, dest_size, + &encode_frame_result->rq_history); #else // CONFIG_RATE_CTRL #if CONFIG_COLLECT_COMPONENT_TIMING start_timing(cpi, encode_with_recode_loop_time); #endif - encode_with_recode_loop(cpi, size, dest); + encode_with_recode_loop(cpi, size, dest, dest_size); #if CONFIG_COLLECT_COMPONENT_TIMING end_timing(cpi, encode_with_recode_loop_time); #endif @@ -5672,7 +5673,7 @@ static void encode_frame_to_data_rate( #if !CONFIG_REALTIME_ONLY // Disable segmentation if it decrease rate/distortion ratio if (cpi->oxcf.aq_mode == LOOKAHEAD_AQ) - vp9_try_disable_lookahead_aq(cpi, size, dest); + vp9_try_disable_lookahead_aq(cpi, size, dest, dest_size); #endif #if CONFIG_VP9_TEMPORAL_DENOISING @@ -5729,7 +5730,7 @@ static void encode_frame_to_data_rate( start_timing(cpi, vp9_pack_bitstream_time); #endif // build the bitstream - vp9_pack_bitstream(cpi, dest, size); + vp9_pack_bitstream(cpi, dest, dest_size, size); #if CONFIG_COLLECT_COMPONENT_TIMING end_timing(cpi, vp9_pack_bitstream_time); #endif @@ -5920,32 +5921,33 @@ static void encode_frame_to_data_rate( } static void SvcEncode(VP9_COMP *cpi, size_t *size, uint8_t *dest, - unsigned int *frame_flags) { + size_t dest_size, unsigned int *frame_flags) { vp9_rc_get_svc_params(cpi); - encode_frame_to_data_rate(cpi, size, dest, frame_flags, + encode_frame_to_data_rate(cpi, size, dest, dest_size, frame_flags, /*encode_frame_result = */ NULL); } static void Pass0Encode(VP9_COMP *cpi, size_t *size, uint8_t *dest, - unsigned int *frame_flags) { + size_t dest_size, unsigned int *frame_flags) { if (cpi->oxcf.rc_mode == VPX_CBR) { vp9_rc_get_one_pass_cbr_params(cpi); } else { vp9_rc_get_one_pass_vbr_params(cpi); } - encode_frame_to_data_rate(cpi, size, dest, frame_flags, + encode_frame_to_data_rate(cpi, size, dest, dest_size, frame_flags, /*encode_frame_result = */ NULL); } #if !CONFIG_REALTIME_ONLY static void Pass2Encode(VP9_COMP *cpi, size_t *size, uint8_t *dest, - unsigned int *frame_flags, + size_t dest_size, unsigned int *frame_flags, ENCODE_FRAME_RESULT *encode_frame_result) { cpi->allow_encode_breakout = ENCODE_BREAKOUT_ENABLED; #if CONFIG_MISMATCH_DEBUG mismatch_move_frame_idx_w(); #endif - encode_frame_to_data_rate(cpi, size, dest, frame_flags, encode_frame_result); + encode_frame_to_data_rate(cpi, size, dest, dest_size, frame_flags, + encode_frame_result); } #endif // !CONFIG_REALTIME_ONLY @@ -6358,8 +6360,8 @@ void vp9_init_encode_frame_result(ENCODE_FRAME_RESULT *encode_frame_result) { } int vp9_get_compressed_data(VP9_COMP *cpi, unsigned int *frame_flags, - size_t *size, uint8_t *dest, int64_t *time_stamp, - int64_t *time_end, int flush, + size_t *size, uint8_t *dest, size_t dest_size, + int64_t *time_stamp, int64_t *time_end, int flush, ENCODE_FRAME_RESULT *encode_frame_result) { const VP9EncoderConfig *const oxcf = &cpi->oxcf; VP9_COMMON *const cm = &cpi->common; @@ -6636,10 +6638,10 @@ int vp9_get_compressed_data(VP9_COMP *cpi, unsigned int *frame_flags, #if CONFIG_REALTIME_ONLY (void)encode_frame_result; if (cpi->use_svc) { - SvcEncode(cpi, size, dest, frame_flags); + SvcEncode(cpi, size, dest, dest_size, frame_flags); } else { // One pass encode - Pass0Encode(cpi, size, dest, frame_flags); + Pass0Encode(cpi, size, dest, dest_size, frame_flags); } #else // !CONFIG_REALTIME_ONLY if (oxcf->pass == 1 && !cpi->use_svc) { @@ -6662,16 +6664,16 @@ int vp9_get_compressed_data(VP9_COMP *cpi, unsigned int *frame_flags, // Accumulate 2nd pass time in 2-pass case. start_timing(cpi, Pass2Encode_time); #endif - Pass2Encode(cpi, size, dest, frame_flags, encode_frame_result); + Pass2Encode(cpi, size, dest, dest_size, frame_flags, encode_frame_result); vp9_twopass_postencode_update(cpi); #if CONFIG_COLLECT_COMPONENT_TIMING end_timing(cpi, Pass2Encode_time); #endif } else if (cpi->use_svc) { - SvcEncode(cpi, size, dest, frame_flags); + SvcEncode(cpi, size, dest, dest_size, frame_flags); } else { // One pass encode - Pass0Encode(cpi, size, dest, frame_flags); + Pass0Encode(cpi, size, dest, dest_size, frame_flags); } #endif // CONFIG_REALTIME_ONLY diff --git a/vp9/encoder/vp9_encoder.h b/vp9/encoder/vp9_encoder.h index 7136f7faa..141f3429c 100644 --- a/vp9/encoder/vp9_encoder.h +++ b/vp9/encoder/vp9_encoder.h @@ -338,6 +338,10 @@ typedef struct TileDataEnc { // Used for adaptive_rd_thresh with row multithreading int *row_base_thresh_freq_fact; + // The value of sb_rows when row_base_thresh_freq_fact is allocated. + // The row_base_thresh_freq_fact array has sb_rows * BLOCK_SIZES * MAX_MODES + // elements. + int sb_rows; MV firstpass_top_mv; } TileDataEnc; @@ -1218,8 +1222,8 @@ int vp9_receive_raw_frame(VP9_COMP *cpi, vpx_enc_frame_flags_t frame_flags, int64_t end_time); int vp9_get_compressed_data(VP9_COMP *cpi, unsigned int *frame_flags, - size_t *size, uint8_t *dest, int64_t *time_stamp, - int64_t *time_end, int flush, + size_t *size, uint8_t *dest, size_t dest_size, + int64_t *time_stamp, int64_t *time_end, int flush, ENCODE_FRAME_RESULT *encode_frame_result); int vp9_get_preview_raw_frame(VP9_COMP *cpi, YV12_BUFFER_CONFIG *dest, diff --git a/vp9/encoder/vp9_multi_thread.c b/vp9/encoder/vp9_multi_thread.c index 0843cd97e..10fbc927c 100644 --- a/vp9/encoder/vp9_multi_thread.c +++ b/vp9/encoder/vp9_multi_thread.c @@ -54,16 +54,23 @@ void *vp9_enc_grp_get_next_job(MultiThreadHandle *multi_thread_ctxt, void vp9_row_mt_alloc_rd_thresh(VP9_COMP *const cpi, TileDataEnc *const this_tile) { VP9_COMMON *const cm = &cpi->common; - const int sb_rows = - (mi_cols_aligned_to_sb(cm->mi_rows) >> MI_BLOCK_SIZE_LOG2) + 1; + const int sb_rows = mi_cols_aligned_to_sb(cm->mi_rows) >> MI_BLOCK_SIZE_LOG2; int i; + if (this_tile->row_base_thresh_freq_fact != NULL) { + if (sb_rows <= this_tile->sb_rows) { + return; + } + vpx_free(this_tile->row_base_thresh_freq_fact); + this_tile->row_base_thresh_freq_fact = NULL; + } CHECK_MEM_ERROR( &cm->error, this_tile->row_base_thresh_freq_fact, (int *)vpx_calloc(sb_rows * BLOCK_SIZES * MAX_MODES, sizeof(*(this_tile->row_base_thresh_freq_fact)))); for (i = 0; i < sb_rows * BLOCK_SIZES * MAX_MODES; i++) this_tile->row_base_thresh_freq_fact[i] = RD_THRESH_INIT_FACT; + this_tile->sb_rows = sb_rows; } void vp9_row_mt_mem_alloc(VP9_COMP *cpi) { @@ -100,13 +107,6 @@ void vp9_row_mt_mem_alloc(VP9_COMP *cpi) { for (tile_col = 0; tile_col < tile_cols; tile_col++) { TileDataEnc *this_tile = &cpi->tile_data[tile_col]; vp9_row_mt_sync_mem_alloc(&this_tile->row_mt_sync, cm, jobs_per_tile_col); - if (cpi->sf.adaptive_rd_thresh_row_mt) { - if (this_tile->row_base_thresh_freq_fact != NULL) { - vpx_free(this_tile->row_base_thresh_freq_fact); - this_tile->row_base_thresh_freq_fact = NULL; - } - vp9_row_mt_alloc_rd_thresh(cpi, this_tile); - } } // Assign the sync pointer of tile row zero for every tile row > 0 @@ -135,14 +135,17 @@ void vp9_row_mt_mem_dealloc(VP9_COMP *cpi) { #endif // Deallocate memory for job queue - if (multi_thread_ctxt->job_queue) vpx_free(multi_thread_ctxt->job_queue); + if (multi_thread_ctxt->job_queue) { + vpx_free(multi_thread_ctxt->job_queue); + multi_thread_ctxt->job_queue = NULL; + } #if CONFIG_MULTITHREAD // Destroy mutex for each tile for (tile_col = 0; tile_col < multi_thread_ctxt->allocated_tile_cols; tile_col++) { RowMTInfo *row_mt_info = &multi_thread_ctxt->row_mt_info[tile_col]; - if (row_mt_info) pthread_mutex_destroy(&row_mt_info->job_mutex); + pthread_mutex_destroy(&row_mt_info->job_mutex); } #endif @@ -168,6 +171,10 @@ void vp9_row_mt_mem_dealloc(VP9_COMP *cpi) { } } #endif + + multi_thread_ctxt->allocated_tile_cols = 0; + multi_thread_ctxt->allocated_tile_rows = 0; + multi_thread_ctxt->allocated_vert_unit_rows = 0; } void vp9_multi_thread_tile_init(VP9_COMP *cpi) { diff --git a/vp9/encoder/vp9_ratectrl.c b/vp9/encoder/vp9_ratectrl.c index 6452e349d..44f52f96b 100644 --- a/vp9/encoder/vp9_ratectrl.c +++ b/vp9/encoder/vp9_ratectrl.c @@ -2651,11 +2651,12 @@ void vp9_rc_update_framerate(VP9_COMP *cpi) { rc->avg_frame_bandwidth = (int)VPXMIN(oxcf->target_bandwidth / cpi->framerate, INT_MAX); - rc->min_frame_bandwidth = - (int)(rc->avg_frame_bandwidth * oxcf->two_pass_vbrmin_section / 100); - rc->min_frame_bandwidth = - VPXMAX(rc->min_frame_bandwidth, FRAME_OVERHEAD_BITS); + int64_t vbr_min_bits = + (int64_t)rc->avg_frame_bandwidth * oxcf->two_pass_vbrmin_section / 100; + vbr_min_bits = VPXMIN(vbr_min_bits, INT_MAX); + + rc->min_frame_bandwidth = VPXMAX((int)vbr_min_bits, FRAME_OVERHEAD_BITS); // A maximum bitrate for a frame is defined. // However this limit is extended if a very high rate is given on the command diff --git a/vp9/encoder/x86/vp9_frame_scale_ssse3.c b/vp9/encoder/x86/vp9_frame_scale_ssse3.c index 94506aad0..628dc4fea 100644 --- a/vp9/encoder/x86/vp9_frame_scale_ssse3.c +++ b/vp9/encoder/x86/vp9_frame_scale_ssse3.c @@ -886,14 +886,14 @@ void vp9_scale_and_extend_frame_ssse3(const YV12_BUFFER_CONFIG *src, scale_plane_1_to_2_phase_0( src->y_buffer, src->y_stride, dst->y_buffer, dst->y_stride, src_w, src_h, vp9_filter_kernels[filter_type][8], temp_buffer); - scale_plane_1_to_2_phase_0(src->u_buffer, src->uv_stride, dst->u_buffer, - dst->uv_stride, src_w / 2, src_h / 2, - vp9_filter_kernels[filter_type][8], - temp_buffer); - scale_plane_1_to_2_phase_0(src->v_buffer, src->uv_stride, dst->v_buffer, - dst->uv_stride, src_w / 2, src_h / 2, - vp9_filter_kernels[filter_type][8], - temp_buffer); + const int src_uv_w = src->uv_crop_width; + const int src_uv_h = src->uv_crop_height; + scale_plane_1_to_2_phase_0( + src->u_buffer, src->uv_stride, dst->u_buffer, dst->uv_stride, + src_uv_w, src_uv_h, vp9_filter_kernels[filter_type][8], temp_buffer); + scale_plane_1_to_2_phase_0( + src->v_buffer, src->uv_stride, dst->v_buffer, dst->uv_stride, + src_uv_w, src_uv_h, vp9_filter_kernels[filter_type][8], temp_buffer); free(temp_buffer); } } diff --git a/vp9/ratectrl_rtc.cc b/vp9/ratectrl_rtc.cc index fd81bce7b..316205f21 100644 --- a/vp9/ratectrl_rtc.cc +++ b/vp9/ratectrl_rtc.cc @@ -303,7 +303,9 @@ int VP9RateControlRTC::GetLoopfilterLevel() const { bool VP9RateControlRTC::GetSegmentationData( VP9SegmentationData *segmentation_data) const { - if (!cpi_->cyclic_refresh->apply_cyclic_refresh) return false; + if (!cpi_->cyclic_refresh || !cpi_->cyclic_refresh->apply_cyclic_refresh) { + return false; + } segmentation_data->segmentation_map = cpi_->segmentation_map; segmentation_data->segmentation_map_size = diff --git a/vp9/simple_encode.cc b/vp9/simple_encode.cc index 2e6f9a451..9b78891c9 100644 --- a/vp9/simple_encode.cc +++ b/vp9/simple_encode.cc @@ -498,6 +498,7 @@ static bool init_encode_frame_result(EncodeFrameResult *encode_frame_result, encode_frame_result->coding_data.reset( new (std::nothrow) uint8_t[max_coding_data_byte_size]); + encode_frame_result->max_coding_data_byte_size = max_coding_data_byte_size; encode_frame_result->num_rows_4x4 = get_num_unit_4x4(frame_height); encode_frame_result->num_cols_4x4 = get_num_unit_4x4(frame_width); @@ -508,6 +509,7 @@ static bool init_encode_frame_result(EncodeFrameResult *encode_frame_result, encode_frame_result->tpl_stats_info.resize(MAX_LAG_BUFFERS); if (encode_frame_result->coding_data.get() == nullptr) { + encode_frame_result->max_coding_data_byte_size = 0; return false; } return init_image_buffer(&encode_frame_result->coded_frame, frame_width, @@ -911,7 +913,7 @@ void SimpleEncode::ComputeFirstPassStats() { ENCODE_FRAME_RESULT encode_frame_info; vp9_init_encode_frame_result(&encode_frame_info); // TODO(angiebird): Call vp9_first_pass directly - vp9_get_compressed_data(impl_ptr_->cpi, &frame_flags, &size, nullptr, + vp9_get_compressed_data(impl_ptr_->cpi, &frame_flags, &size, nullptr, 0, &time_stamp, &time_end, flush, &encode_frame_info); // vp9_get_compressed_data only generates first pass stats not @@ -1193,8 +1195,9 @@ void SimpleEncode::EncodeFrame(EncodeFrameResult *encode_frame_result) { &encode_frame_info.coded_frame); vp9_get_compressed_data(cpi, &frame_flags, &encode_frame_result->coding_data_byte_size, - encode_frame_result->coding_data.get(), &time_stamp, - &time_end, flush, &encode_frame_info); + encode_frame_result->coding_data.get(), + encode_frame_result->max_coding_data_byte_size, + &time_stamp, &time_end, flush, &encode_frame_info); if (out_file_ != nullptr) { ivf_write_frame_header(out_file_, time_stamp, encode_frame_result->coding_data_byte_size); @@ -1208,10 +1211,8 @@ void SimpleEncode::EncodeFrame(EncodeFrameResult *encode_frame_result) { fprintf(stderr, "Coding data size <= 0\n"); abort(); } - const size_t max_coding_data_byte_size = - get_max_coding_data_byte_size(frame_width_, frame_height_); if (encode_frame_result->coding_data_byte_size > - max_coding_data_byte_size) { + encode_frame_result->max_coding_data_byte_size) { fprintf(stderr, "Coding data size exceeds the maximum.\n"); abort(); } diff --git a/vp9/simple_encode.h b/vp9/simple_encode.h index d610a5e15..94ecbf284 100644 --- a/vp9/simple_encode.h +++ b/vp9/simple_encode.h @@ -263,6 +263,7 @@ struct EncodeFrameResult { // The EncodeFrame will allocate a buffer, write the coding data into the // buffer and give the ownership of the buffer to coding_data. std::unique_ptr<unsigned char[]> coding_data; + size_t max_coding_data_byte_size; double psnr; uint64_t sse; int quantize_index; diff --git a/vp9/vp9_cx_iface.c b/vp9/vp9_cx_iface.c index 3a462c372..9532dffa6 100644 --- a/vp9/vp9_cx_iface.c +++ b/vp9/vp9_cx_iface.c @@ -1434,8 +1434,8 @@ static vpx_codec_err_t encoder_encode(vpx_codec_alg_priv_t *ctx, cx_data += ctx->pending_cx_data_sz; cx_data_sz -= ctx->pending_cx_data_sz; - /* TODO: this is a minimal check, the underlying codec doesn't respect - * the buffer size anyway. + /* TODO(webm:1844): this is a minimal check, the underlying codec doesn't + * respect the buffer size anyway. */ if (cx_data_sz < ctx->cx_data_sz / 2) { vpx_internal_error(&cpi->common.error, VPX_CODEC_ERROR, @@ -1454,9 +1454,9 @@ static vpx_codec_err_t encoder_encode(vpx_codec_alg_priv_t *ctx, ENCODE_FRAME_RESULT encode_frame_result; vp9_init_encode_frame_result(&encode_frame_result); // TODO(angiebird): Call vp9_first_pass directly - ret = vp9_get_compressed_data(cpi, &lib_flags, &size, cx_data, - &dst_time_stamp, &dst_end_time_stamp, - !img, &encode_frame_result); + ret = vp9_get_compressed_data( + cpi, &lib_flags, &size, cx_data, cx_data_sz, &dst_time_stamp, + &dst_end_time_stamp, !img, &encode_frame_result); assert(size == 0); // There is no compressed data in the first pass (void)ret; assert(ret == 0); @@ -1479,8 +1479,9 @@ static vpx_codec_err_t encoder_encode(vpx_codec_alg_priv_t *ctx, vp9_init_encode_frame_result(&encode_frame_result); while (cx_data_sz >= ctx->cx_data_sz / 2 && -1 != vp9_get_compressed_data(cpi, &lib_flags, &size, cx_data, - &dst_time_stamp, &dst_end_time_stamp, - !img, &encode_frame_result)) { + cx_data_sz, &dst_time_stamp, + &dst_end_time_stamp, !img, + &encode_frame_result)) { // Pack psnr pkt if (size > 0 && !cpi->use_svc) { // TODO(angiebird): Figure out while we don't need psnr pkt when diff --git a/vpx/src/vpx_image.c b/vpx/src/vpx_image.c index f9f0dd602..9a42c3f91 100644 --- a/vpx/src/vpx_image.c +++ b/vpx/src/vpx_image.c @@ -8,6 +8,7 @@ * be found in the AUTHORS file in the root of the source tree. */ +#include <assert.h> #include <limits.h> #include <stdlib.h> #include <string.h> @@ -21,12 +22,23 @@ static vpx_image_t *img_alloc_helper(vpx_image_t *img, vpx_img_fmt_t fmt, unsigned int buf_align, unsigned int stride_align, unsigned char *img_data) { - unsigned int h, w, s, xcs, ycs, bps; - unsigned int stride_in_bytes; + unsigned int h, w, xcs, ycs, bps; + uint64_t s; + int stride_in_bytes; unsigned int align; if (img != NULL) memset(img, 0, sizeof(vpx_image_t)); + if (fmt == VPX_IMG_FMT_NONE) goto fail; + + /* Impose maximum values on input parameters so that this function can + * perform arithmetic operations without worrying about overflows. + */ + if (d_w > 0x08000000 || d_h > 0x08000000 || buf_align > 65536 || + stride_align > 65536) { + goto fail; + } + /* Treat align==0 like align==1 */ if (!buf_align) buf_align = 1; @@ -76,13 +88,28 @@ static vpx_image_t *img_alloc_helper(vpx_image_t *img, vpx_img_fmt_t fmt, default: ycs = 0; break; } - /* Calculate storage sizes. If the buffer was allocated externally, the width - * and height shouldn't be adjusted. */ - w = d_w; - h = d_h; - s = (fmt & VPX_IMG_FMT_PLANAR) ? w : bps * w / 8; - s = (s + stride_align - 1) & ~(stride_align - 1); - stride_in_bytes = (fmt & VPX_IMG_FMT_HIGHBITDEPTH) ? s * 2 : s; + /* Calculate storage sizes. */ + if (img_data) { + /* If the buffer was allocated externally, the width and height shouldn't + * be adjusted. */ + w = d_w; + h = d_h; + } else { + /* Calculate storage sizes given the chroma subsampling */ + align = (1 << xcs) - 1; + w = (d_w + align) & ~align; + assert(d_w <= w); + align = (1 << ycs) - 1; + h = (d_h + align) & ~align; + assert(d_h <= h); + } + + s = (fmt & VPX_IMG_FMT_PLANAR) ? w : (uint64_t)bps * w / 8; + s = (fmt & VPX_IMG_FMT_HIGHBITDEPTH) ? s * 2 : s; + s = (s + stride_align - 1) & ~((uint64_t)stride_align - 1); + if (s > INT_MAX) goto fail; + stride_in_bytes = (int)s; + s = (fmt & VPX_IMG_FMT_HIGHBITDEPTH) ? s / 2 : s; /* Allocate the new image */ if (!img) { @@ -97,15 +124,6 @@ static vpx_image_t *img_alloc_helper(vpx_image_t *img, vpx_img_fmt_t fmt, if (!img_data) { uint64_t alloc_size; - /* Calculate storage sizes given the chroma subsampling */ - align = (1 << xcs) - 1; - w = (d_w + align) & ~align; - align = (1 << ycs) - 1; - h = (d_h + align) & ~align; - - s = (fmt & VPX_IMG_FMT_PLANAR) ? w : bps * w / 8; - s = (s + stride_align - 1) & ~(stride_align - 1); - stride_in_bytes = (fmt & VPX_IMG_FMT_HIGHBITDEPTH) ? s * 2 : s; alloc_size = (fmt & VPX_IMG_FMT_PLANAR) ? (uint64_t)h * s * bps / 8 : (uint64_t)h * s; @@ -170,12 +188,12 @@ int vpx_img_set_rect(vpx_image_t *img, unsigned int x, unsigned int y, if (img->fmt & VPX_IMG_FMT_HAS_ALPHA) { img->planes[VPX_PLANE_ALPHA] = data + x * bytes_per_sample + y * img->stride[VPX_PLANE_ALPHA]; - data += img->h * img->stride[VPX_PLANE_ALPHA]; + data += (size_t)img->h * img->stride[VPX_PLANE_ALPHA]; } img->planes[VPX_PLANE_Y] = data + x * bytes_per_sample + y * img->stride[VPX_PLANE_Y]; - data += img->h * img->stride[VPX_PLANE_Y]; + data += (size_t)img->h * img->stride[VPX_PLANE_Y]; if (img->fmt == VPX_IMG_FMT_NV12) { img->planes[VPX_PLANE_U] = @@ -186,7 +204,8 @@ int vpx_img_set_rect(vpx_image_t *img, unsigned int x, unsigned int y, img->planes[VPX_PLANE_U] = data + (x >> img->x_chroma_shift) * bytes_per_sample + (y >> img->y_chroma_shift) * img->stride[VPX_PLANE_U]; - data += (img->h >> img->y_chroma_shift) * img->stride[VPX_PLANE_U]; + data += + (size_t)(img->h >> img->y_chroma_shift) * img->stride[VPX_PLANE_U]; img->planes[VPX_PLANE_V] = data + (x >> img->x_chroma_shift) * bytes_per_sample + (y >> img->y_chroma_shift) * img->stride[VPX_PLANE_V]; @@ -194,7 +213,8 @@ int vpx_img_set_rect(vpx_image_t *img, unsigned int x, unsigned int y, img->planes[VPX_PLANE_V] = data + (x >> img->x_chroma_shift) * bytes_per_sample + (y >> img->y_chroma_shift) * img->stride[VPX_PLANE_V]; - data += (img->h >> img->y_chroma_shift) * img->stride[VPX_PLANE_V]; + data += + (size_t)(img->h >> img->y_chroma_shift) * img->stride[VPX_PLANE_V]; img->planes[VPX_PLANE_U] = data + (x >> img->x_chroma_shift) * bytes_per_sample + (y >> img->y_chroma_shift) * img->stride[VPX_PLANE_U]; diff --git a/vpx/vpx_image.h b/vpx/vpx_image.h index 1adc9b9d9..a0c16c346 100644 --- a/vpx/vpx_image.h +++ b/vpx/vpx_image.h @@ -132,10 +132,13 @@ typedef struct vpx_image_rect { * is NULL, the storage for the descriptor will be * allocated on the heap. * \param[in] fmt Format for the image - * \param[in] d_w Width of the image - * \param[in] d_h Height of the image + * \param[in] d_w Width of the image. Must not exceed 0x08000000 + * (2^27). + * \param[in] d_h Height of the image. Must not exceed 0x08000000 + * (2^27). * \param[in] align Alignment, in bytes, of the image buffer and - * each row in the image(stride). + * each row in the image (stride). Must not exceed + * 65536. * * \return Returns a pointer to the initialized image descriptor. If the img * parameter is non-null, the value of the img parameter will be @@ -155,9 +158,12 @@ vpx_image_t *vpx_img_alloc(vpx_image_t *img, vpx_img_fmt_t fmt, * parameter is NULL, the storage for the descriptor * will be allocated on the heap. * \param[in] fmt Format for the image - * \param[in] d_w Width of the image - * \param[in] d_h Height of the image - * \param[in] stride_align Alignment, in bytes, of each row in the image. + * \param[in] d_w Width of the image. Must not exceed 0x08000000 + * (2^27). + * \param[in] d_h Height of the image. Must not exceed 0x08000000 + * (2^27). + * \param[in] stride_align Alignment, in bytes, of each row in the image + * (stride). Must not exceed 65536. * \param[in] img_data Storage to use for the image * * \return Returns a pointer to the initialized image descriptor. If the img diff --git a/vpx_dsp/arm/highbd_sse_neon.c b/vpx_dsp/arm/highbd_sse_neon.c index ee76bed58..91dfebf90 100644 --- a/vpx_dsp/arm/highbd_sse_neon.c +++ b/vpx_dsp/arm/highbd_sse_neon.c @@ -9,6 +9,7 @@ */ #include <arm_neon.h> +#include <stdint.h> #include "./vpx_dsp_rtcd.h" #include "vpx_dsp/arm/sum_neon.h" diff --git a/vpx_dsp/arm/sse_neon.c b/vpx_dsp/arm/sse_neon.c index f686dc350..2dd57e596 100644 --- a/vpx_dsp/arm/sse_neon.c +++ b/vpx_dsp/arm/sse_neon.c @@ -9,7 +9,9 @@ */ #include <arm_neon.h> +#include <stdint.h> +#include "./vpx_config.h" #include "./vpx_dsp_rtcd.h" #include "vpx_dsp/arm/mem_neon.h" #include "vpx_dsp/arm/sum_neon.h" diff --git a/vpx_dsp/bitwriter.c b/vpx_dsp/bitwriter.c index 5b41aa54d..d3ef9bd89 100644 --- a/vpx_dsp/bitwriter.c +++ b/vpx_dsp/bitwriter.c @@ -9,6 +9,7 @@ */ #include <assert.h> +#include <limits.h> #include "./bitwriter.h" @@ -16,16 +17,20 @@ #include "vpx_util/vpx_debug_util.h" #endif -void vpx_start_encode(vpx_writer *br, uint8_t *source) { +void vpx_start_encode(vpx_writer *br, uint8_t *source, size_t size) { br->lowvalue = 0; br->range = 255; br->count = -24; - br->buffer = source; + br->error = 0; br->pos = 0; + // Make sure it is safe to cast br->pos to int in vpx_write(). + if (size > INT_MAX) size = INT_MAX; + br->size = (unsigned int)size; + br->buffer = source; vpx_write_bit(br, 0); } -void vpx_stop_encode(vpx_writer *br) { +int vpx_stop_encode(vpx_writer *br) { int i; #if CONFIG_BITSTREAM_DEBUG @@ -34,9 +39,17 @@ void vpx_stop_encode(vpx_writer *br) { for (i = 0; i < 32; i++) vpx_write_bit(br, 0); // Ensure there's no ambigous collision with any index marker bytes - if ((br->buffer[br->pos - 1] & 0xe0) == 0xc0) br->buffer[br->pos++] = 0; + if (!br->error && (br->buffer[br->pos - 1] & 0xe0) == 0xc0) { + if (br->pos < br->size) { + br->buffer[br->pos++] = 0; + } else { + br->error = 1; + } + } #if CONFIG_BITSTREAM_DEBUG bitstream_queue_set_skip_write(0); #endif + + return br->error ? -1 : 0; } diff --git a/vpx_dsp/bitwriter.h b/vpx_dsp/bitwriter.h index 5f1ee69ec..daff331da 100644 --- a/vpx_dsp/bitwriter.h +++ b/vpx_dsp/bitwriter.h @@ -29,12 +29,19 @@ typedef struct vpx_writer { unsigned int lowvalue; unsigned int range; int count; + // Whether there has been an error. + int error; + // We maintain the invariant that pos <= size, i.e., we never write beyond + // the end of the buffer. If pos would be incremented to be greater than + // size, leave pos unchanged and set error to 1. unsigned int pos; + unsigned int size; uint8_t *buffer; } vpx_writer; -void vpx_start_encode(vpx_writer *br, uint8_t *source); -void vpx_stop_encode(vpx_writer *br); +void vpx_start_encode(vpx_writer *br, uint8_t *source, size_t size); +// Returns 0 on success and returns -1 in case of error. +int vpx_stop_encode(vpx_writer *br); static INLINE VPX_NO_UNSIGNED_SHIFT_CHECK void vpx_write(vpx_writer *br, int bit, @@ -77,18 +84,25 @@ static INLINE VPX_NO_UNSIGNED_SHIFT_CHECK void vpx_write(vpx_writer *br, if (count >= 0) { int offset = shift - count; - if ((lowvalue << (offset - 1)) & 0x80000000) { - int x = br->pos - 1; + if (!br->error) { + if ((lowvalue << (offset - 1)) & 0x80000000) { + int x = (int)br->pos - 1; - while (x >= 0 && br->buffer[x] == 0xff) { - br->buffer[x] = 0; - x--; + while (x >= 0 && br->buffer[x] == 0xff) { + br->buffer[x] = 0; + x--; + } + + // TODO(wtc): How to prove x >= 0? + br->buffer[x] += 1; } - br->buffer[x] += 1; + if (br->pos < br->size) { + br->buffer[br->pos++] = (lowvalue >> (24 - offset)) & 0xff; + } else { + br->error = 1; + } } - - br->buffer[br->pos++] = (lowvalue >> (24 - offset)) & 0xff; lowvalue <<= offset; shift = count; lowvalue &= 0xffffff; diff --git a/vpx_dsp/bitwriter_buffer.c b/vpx_dsp/bitwriter_buffer.c index 7a7e96f02..b3a2490f2 100644 --- a/vpx_dsp/bitwriter_buffer.c +++ b/vpx_dsp/bitwriter_buffer.c @@ -8,24 +8,43 @@ * be found in the AUTHORS file in the root of the source tree. */ +#include <assert.h> #include <limits.h> #include <stdlib.h> #include "./vpx_config.h" #include "./bitwriter_buffer.h" +void vpx_wb_init(struct vpx_write_bit_buffer *wb, uint8_t *bit_buffer, + size_t size) { + wb->error = 0; + wb->bit_offset = 0; + wb->size = size; + wb->bit_buffer = bit_buffer; +} + +int vpx_wb_has_error(const struct vpx_write_bit_buffer *wb) { + return wb->error; +} + size_t vpx_wb_bytes_written(const struct vpx_write_bit_buffer *wb) { + assert(!wb->error); return wb->bit_offset / CHAR_BIT + (wb->bit_offset % CHAR_BIT > 0); } void vpx_wb_write_bit(struct vpx_write_bit_buffer *wb, int bit) { + if (wb->error) return; const int off = (int)wb->bit_offset; const int p = off / CHAR_BIT; const int q = CHAR_BIT - 1 - off % CHAR_BIT; + if ((size_t)p >= wb->size) { + wb->error = 1; + return; + } if (q == CHAR_BIT - 1) { wb->bit_buffer[p] = bit << q; } else { - wb->bit_buffer[p] &= ~(1 << q); + assert((wb->bit_buffer[p] & (1 << q)) == 0); wb->bit_buffer[p] |= bit << q; } wb->bit_offset = off + 1; diff --git a/vpx_dsp/bitwriter_buffer.h b/vpx_dsp/bitwriter_buffer.h index 3662cb64d..3ee0e9658 100644 --- a/vpx_dsp/bitwriter_buffer.h +++ b/vpx_dsp/bitwriter_buffer.h @@ -18,10 +18,24 @@ extern "C" { #endif struct vpx_write_bit_buffer { - uint8_t *bit_buffer; + // Whether there has been an error. + int error; + // We maintain the invariant that bit_offset <= size * CHAR_BIT, i.e., we + // never write beyond the end of bit_buffer. If bit_offset would be + // incremented to be greater than size * CHAR_BIT, leave bit_offset unchanged + // and set error to 1. size_t bit_offset; + // Size of bit_buffer in bytes. + size_t size; + uint8_t *bit_buffer; }; +void vpx_wb_init(struct vpx_write_bit_buffer *wb, uint8_t *bit_buffer, + size_t size); + +int vpx_wb_has_error(const struct vpx_write_bit_buffer *wb); + +// Must not be called if vpx_wb_has_error(wb) returns true. size_t vpx_wb_bytes_written(const struct vpx_write_bit_buffer *wb); void vpx_wb_write_bit(struct vpx_write_bit_buffer *wb, int bit); diff --git a/vpx_dsp/sse.c b/vpx_dsp/sse.c index 6cb4b705f..c9d751859 100644 --- a/vpx_dsp/sse.c +++ b/vpx_dsp/sse.c @@ -19,6 +19,7 @@ #include "./vpx_dsp_rtcd.h" #include "vpx/vpx_integer.h" +#include "vpx_ports/mem.h" int64_t vpx_sse_c(const uint8_t *a, int a_stride, const uint8_t *b, int b_stride, int width, int height) { diff --git a/vpx_dsp/vpx_dsp_rtcd_defs.pl b/vpx_dsp/vpx_dsp_rtcd_defs.pl index e9d63f6ef..18087e25d 100644 --- a/vpx_dsp/vpx_dsp_rtcd_defs.pl +++ b/vpx_dsp/vpx_dsp_rtcd_defs.pl @@ -744,7 +744,7 @@ if (vpx_config("CONFIG_ENCODERS") eq "yes") { add_proto qw/void vpx_subtract_block/, "int rows, int cols, int16_t *diff_ptr, ptrdiff_t diff_stride, const uint8_t *src_ptr, ptrdiff_t src_stride, const uint8_t *pred_ptr, ptrdiff_t pred_stride"; specialize qw/vpx_subtract_block neon msa mmi sse2 avx2 vsx lsx/; -add_proto qw/int64_t/, "vpx_sse", "const uint8_t *a, int a_stride, const uint8_t *b,int b_stride, int width, int height"; +add_proto qw/int64_t/, "vpx_sse", "const uint8_t *src, int src_stride, const uint8_t *ref, int ref_stride, int width, int height"; specialize qw/vpx_sse sse4_1 avx2 neon neon_dotprod/; # diff --git a/vpx_dsp/x86/sse_avx2.c b/vpx_dsp/x86/sse_avx2.c index 917ff0ef1..dfe45b611 100644 --- a/vpx_dsp/x86/sse_avx2.c +++ b/vpx_dsp/x86/sse_avx2.c @@ -8,8 +8,9 @@ * be found in the AUTHORS file in the root of the source tree. */ -#include <smmintrin.h> #include <immintrin.h> +#include <smmintrin.h> +#include <stdint.h> #include "./vpx_config.h" #include "./vpx_dsp_rtcd.h" |