Skip to content

Support encoding layered image with pre-scaled inputs - #1069

Open
tongyuantongyu wants to merge 18 commits into
AOMediaCodec:mainfrom
tongyuantongyu:var-frame-size
Open

tongyuantongyu wants to merge 18 commits into
AOMediaCodec:mainfrom
tongyuantongyu:var-frame-size

Conversation

@tongyuantongyu

@tongyuantongyu tongyuantongyu commented Aug 26, 2022 •

Copy link
Copy Markdown
Contributor

A layered image may use different frame for each layer, like using a small and blurred version as the first layer. It is more efficient and flexible to directly encode the small image, instead of scale it up only for the encoder to scale it back down; and it allows the scale ratio to be smaller than 1/8 as well.

Only single cell layered image is supported in this version. Layered grid may be revisited in the future.

@wantehchang

Copy link
Copy Markdown
Member

Yuan: I just merged my cleanup pull request #1070. Please rebase this pull request and then I will take a look at the crash. Thanks.

@tongyuantongyu
tongyuantongyu force-pushed the var-frame-size branch 2 times, most recently from 054b138 to 864c34e Compare August 30, 2022 02:45
@tongyuantongyu

tongyuantongyu commented Aug 31, 2022 •

Copy link
Copy Markdown
Contributor Author
Legacy content

I tried to run asan and here is the report:

=================================================================
==6200==ERROR: AddressSanitizer: heap-buffer-overflow on address 0x121cbf221290 at pc 0x7ff65da76b28 bp 0x0026a0dedb00 sp 0x0026a0dedb48
WRITE of size 8 at 0x121cbf221290 thread T0
    #0 0x7ff65da76b27 in av1_set_mb_ssim_rdmult_scaling D:/Cpp/libavif/ext/aom/av1/encoder/encoder_utils.c:1304:47
    #1 0x7ff65da3f157 in encode_frame_to_data_rate D:/Cpp/libavif/ext/aom/av1/encoder/encoder.c:3581:5
    #2 0x7ff65da3f157 in av1_encode D:/Cpp/libavif/ext/aom/av1/encoder/encoder.c:3830:9
    #3 0x7ff65e36e4d5 in av1_encode_strategy D:/Cpp/libavif/ext/aom/av1/encoder/encode_strategy.c:1605:9
    #4 0x7ff65da48d65 in av1_get_compressed_data D:/Cpp/libavif/ext/aom/av1/encoder/encoder.c:4532:22
    #5 0x7ff65d946785 in encoder_encode D:/Cpp/libavif/ext/aom/av1/av1_cx_iface.c:3029:20
    #6 0x7ff65d938bdb in aom_codec_encode D:/Cpp/libavif/ext/aom/aom/src/aom_encoder.c:176:11
    #7 0x7ff65d90d423 in aomCodecEncodeImage D:/Cpp/libavif/src/codec_aom.c:974:33
    #8 0x7ff65d8fd2d4 in avifEncoderAddImageInternal D:/Cpp/libavif/src/write.c:915:17
    #9 0x7ff65d8fb670 in avifEncoderAddImage D:/Cpp/libavif/src/write.c:934:12
    #10 0x7ff65d8d42b3 in libavif::(anonymous namespace)::ChangeSettingTest_DISABLED_ChangeDimension_Test::TestBody() D:/Cpp/libavif/tests/gtest/avifchangesettingtest.cc:186:5
    #11 0x7ffbebd58308 in void testing::internal::HandleExceptionsInMethodIfSupported<testing::Test, void>(testing::Test*, void (testing::Test::*)(), char const*) (D:\Tools\msys64\clang64\bin\libgtest.dll+0x180038308)
    #12 0x7ffbebd3ce8a in testing::Test::Run() (D:\Tools\msys64\clang64\bin\libgtest.dll+0x18001ce8a)
    #13 0x7ffbebd3e16e in testing::TestInfo::Run() (D:\Tools\msys64\clang64\bin\libgtest.dll+0x18001e16e)
    #14 0x7ffbebd3ec57 in testing::TestSuite::Run() (D:\Tools\msys64\clang64\bin\libgtest.dll+0x18001ec57)
    #15 0x7ffbebd4f2ab in testing::internal::UnitTestImpl::RunAllTests() (D:\Tools\msys64\clang64\bin\libgtest.dll+0x18002f2ab)
    #16 0x7ffbebd59168 in bool testing::internal::HandleExceptionsInMethodIfSupported<testing::internal::UnitTestImpl, bool>(testing::internal::UnitTestImpl*, bool (testing::internal::UnitTestImpl::*)(), char const*) (D:\Tools\msys64\clang64\bin\libgtest.dll+0x180039168)
    #17 0x7ffbebd4eadf in testing::UnitTest::Run() (D:\Tools\msys64\clang64\bin\libgtest.dll+0x18002eadf)
    #18 0x7ffbefab1434 in main (D:\Tools\msys64\clang64\bin\libgtest_main.dll+0x180001434)
    #19 0x7ff65d8d13ad in __tmainCRTStartup C:/M/mingw-w64-crt-git/src/mingw-w64/mingw-w64-crt/crt/crtexe.c:329:15
    #20 0x7ff65d8d14e5 in mainCRTStartup C:/M/mingw-w64-crt-git/src/mingw-w64/mingw-w64-crt/crt/crtexe.c:206:9
    #21 0x7ffc008f54df  (C:\WINDOWS\System32\KERNEL32.DLL+0x1800154df)
    #22 0x7ffc0224485a  (C:\WINDOWS\SYSTEM32\ntdll.dll+0x18000485a)

0x121cbf221297 is located 0 bytes to the right of 2071-byte region [0x121cbf220a80,0x121cbf221297)
allocated by thread T0 here:
    #0 0x7ffb7dec100d in malloc (D:\Tools\msys64\clang64\bin\libclang_rt.asan_dynamic-x86_64.dll+0x18004100d)
    #1 0x7ff65d9ab06a in aom_memalign D:/Cpp/libavif/ext/aom/aom_mem/aom_mem.c:59:22
    #2 0x7ff65d9ab06a in aom_malloc D:/Cpp/libavif/ext/aom/aom_mem/aom_mem.c:67:40
    #3 0x7ff65d9ab06a in aom_calloc D:/Cpp/libavif/ext/aom/aom_mem/aom_mem.c:72:19
    #4 0x7ff65da38265 in av1_create_compressor D:/Cpp/libavif/ext/aom/av1/encoder/encoder.c:1400:5
    #5 0x7ff65d94389e in av1_create_context_and_bufferpool D:/Cpp/libavif/ext/aom/av1/av1_cx_iface.c:2487:12
    #6 0x7ff65d94389e in encoder_init D:/Cpp/libavif/ext/aom/av1/av1_cx_iface.c:2600:13
    #7 0x7ff65d938732 in aom_codec_enc_init_ver D:/Cpp/libavif/ext/aom/aom/src/aom_encoder.c:80:11
    #8 0x7ff65d90bcb0 in aomCodecEncodeImage D:/Cpp/libavif/src/codec_aom.c:726:13
    #9 0x7ff65d8fd2d4 in avifEncoderAddImageInternal D:/Cpp/libavif/src/write.c:915:17
    #10 0x7ff65d8fb670 in avifEncoderAddImage D:/Cpp/libavif/src/write.c:934:12
    #11 0x7ff65d8d4158 in libavif::(anonymous namespace)::ChangeSettingTest_DISABLED_ChangeDimension_Test::TestBody() D:/Cpp/libavif/tests/gtest/avifchangesettingtest.cc:183:5
    #12 0x7ffbebd58308 in void testing::internal::HandleExceptionsInMethodIfSupported<testing::Test, void>(testing::Test*, void (testing::Test::*)(), char const*) (D:\Tools\msys64\clang64\bin\libgtest.dll+0x180038308)
    #13 0x7ffbebd3ce8a in testing::Test::Run() (D:\Tools\msys64\clang64\bin\libgtest.dll+0x18001ce8a)
    #14 0x7ffbebd3e16e in testing::TestInfo::Run() (D:\Tools\msys64\clang64\bin\libgtest.dll+0x18001e16e)
    #15 0x7ffbebd3ec57 in testing::TestSuite::Run() (D:\Tools\msys64\clang64\bin\libgtest.dll+0x18001ec57)
    #16 0x7ffbebd4f2ab in testing::internal::UnitTestImpl::RunAllTests() (D:\Tools\msys64\clang64\bin\libgtest.dll+0x18002f2ab)
    #17 0x7ffbebd59168 in bool testing::internal::HandleExceptionsInMethodIfSupported<testing::internal::UnitTestImpl, bool>(testing::internal::UnitTestImpl*, bool (testing::internal::UnitTestImpl::*)(), char const*) (D:\Tools\msys64\clang64\bin\libgtest.dll+0x180039168)
    #18 0x7ffbebd4eadf in testing::UnitTest::Run() (D:\Tools\msys64\clang64\bin\libgtest.dll+0x18002eadf)
    #19 0x7ffbefab1434 in main (D:\Tools\msys64\clang64\bin\libgtest_main.dll+0x180001434)
    #20 0x7ff65d8d13ad in __tmainCRTStartup C:/M/mingw-w64-crt-git/src/mingw-w64/mingw-w64-crt/crt/crtexe.c:329:15
    #21 0x7ff65d8d14e5 in mainCRTStartup C:/M/mingw-w64-crt-git/src/mingw-w64/mingw-w64-crt/crt/crtexe.c:206:9
    #22 0x7ffc008f54df  (C:\WINDOWS\System32\KERNEL32.DLL+0x1800154df)
    #23 0x7ffc0224485a  (C:\WINDOWS\SYSTEM32\ntdll.dll+0x18000485a)

SUMMARY: AddressSanitizer: heap-buffer-overflow D:/Cpp/libavif/ext/aom/av1/encoder/encoder_utils.c:1304:47 in av1_set_mb_ssim_rdmult_scaling
Shadow bytes around the buggy address:
  0x042657044200: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
  0x042657044210: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
  0x042657044220: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
  0x042657044230: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
  0x042657044240: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
=>0x042657044250: 00 00[07]fa fa fa fa fa fa fa fa fa fa fa fa fa
  0x042657044260: fa fa fa fa fa fa fa fa fa fa fa fa fa fa fa fa
  0x042657044270: fa fa fa fa fa fa fa fa fa fa fa fa fa fa fa fa
  0x042657044280: fa fa fa fa fa fa fa fa fa fa fa fa fa fa fa fa
  0x042657044290: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
  0x0426570442a0: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
Shadow byte legend (one shadow byte represents 8 application bytes):
  Addressable:           00
  Partially addressable: 01 02 03 04 05 06 07 
  Heap left redzone:       fa
  Freed heap region:       fd
  Stack left redzone:      f1
  Stack mid redzone:       f2
  Stack right redzone:     f3
  Stack after return:      f5
  Stack use after scope:   f8
  Global redzone:          f9
  Global init order:       f6
  Poisoned by user:        f7
  Container overflow:      fc
  Array cookie:            ac
  Intra object redzone:    bb
  ASan internal:           fe
  Left alloca redzone:     ca
  Right alloca redzone:    cb
==6200==ABORTING

The function in question seems related to ssim, but if I set tune to psnr, it fails in another function:

=================================================================
==62176==ERROR: AddressSanitizer: heap-buffer-overflow on address 0x126071520997 at pc 0x7ffb7dec0b8c bp 0x00cb7aefcb00 sp 0x00cb7aefcb40
WRITE of size 4096 at 0x126071520997 thread T0
    #0 0x7ffb7dec0b8b in __asan_memset (D:\Tools\msys64\clang64\bin\libclang_rt.asan_dynamic-x86_64.dll+0x180040b8b)
    #1 0x7ff7eabdef80 in encode_without_recode D:/Cpp/libavif/ext/aom/av1/encoder/encoder.c:2406:5
    #2 0x7ff7eabdef80 in encode_with_recode_loop_and_filter D:/Cpp/libavif/ext/aom/av1/encoder/encoder.c:3014:11
    #3 0x7ff7eabc0d05 in encode_frame_to_data_rate D:/Cpp/libavif/ext/aom/av1/encoder/encoder.c:3678:9
    #4 0x7ff7eabc0d05 in av1_encode D:/Cpp/libavif/ext/aom/av1/encoder/encoder.c:3830:9
    #5 0x7ff7eb4ee4f5 in av1_encode_strategy D:/Cpp/libavif/ext/aom/av1/encoder/encode_strategy.c:1605:9
    #6 0x7ff7eabc8d85 in av1_get_compressed_data D:/Cpp/libavif/ext/aom/av1/encoder/encoder.c:4532:22
    #7 0x7ff7eaac67a5 in encoder_encode D:/Cpp/libavif/ext/aom/av1/av1_cx_iface.c:3029:20
    #8 0x7ff7eaab8bfb in aom_codec_encode D:/Cpp/libavif/ext/aom/aom/src/aom_encoder.c:176:11
    #9 0x7ff7eaa8d443 in aomCodecEncodeImage D:/Cpp/libavif/src/codec_aom.c:974:33
    #10 0x7ff7eaa7d2f4 in avifEncoderAddImageInternal D:/Cpp/libavif/src/write.c:915:17
    #11 0x7ff7eaa7b690 in avifEncoderAddImage D:/Cpp/libavif/src/write.c:934:12
    #12 0x7ff7eaa542cb in libavif::(anonymous namespace)::ChangeSettingTest_DISABLED_ChangeDimension_Test::TestBody() D:/Cpp/libavif/tests/gtest/avifchangesettingtest.cc:186:5
    #13 0x7ffbebd58308 in void testing::internal::HandleExceptionsInMethodIfSupported<testing::Test, void>(testing::Test*, void (testing::Test::*)(), char const*) (D:\Tools\msys64\clang64\bin\libgtest.dll+0x180038308)
    #14 0x7ffbebd3ce8a in testing::Test::Run() (D:\Tools\msys64\clang64\bin\libgtest.dll+0x18001ce8a)
    #15 0x7ffbebd3e16e in testing::TestInfo::Run() (D:\Tools\msys64\clang64\bin\libgtest.dll+0x18001e16e)
    #16 0x7ffbebd3ec57 in testing::TestSuite::Run() (D:\Tools\msys64\clang64\bin\libgtest.dll+0x18001ec57)
    #17 0x7ffbebd4f2ab in testing::internal::UnitTestImpl::RunAllTests() (D:\Tools\msys64\clang64\bin\libgtest.dll+0x18002f2ab)
    #18 0x7ffbebd59168 in bool testing::internal::HandleExceptionsInMethodIfSupported<testing::internal::UnitTestImpl, bool>(testing::internal::UnitTestImpl*, bool (testing::internal::UnitTestImpl::*)(), char const*) (D:\Tools\msys64\clang64\bin\libgtest.dll+0x180039168)
    #19 0x7ffbebd4eadf in testing::UnitTest::Run() (D:\Tools\msys64\clang64\bin\libgtest.dll+0x18002eadf)
    #20 0x7ffbefab1434 in main (D:\Tools\msys64\clang64\bin\libgtest_main.dll+0x180001434)
    #21 0x7ff7eaa513ad in __tmainCRTStartup C:/M/mingw-w64-crt-git/src/mingw-w64/mingw-w64-crt/crt/crtexe.c:329:15
    #22 0x7ff7eaa514e5 in mainCRTStartup C:/M/mingw-w64-crt-git/src/mingw-w64/mingw-w64-crt/crt/crtexe.c:206:9
    #23 0x7ffc008f54df  (C:\WINDOWS\System32\KERNEL32.DLL+0x1800154df)
    #24 0x7ffc0224485a  (C:\WINDOWS\SYSTEM32\ntdll.dll+0x18000485a)

0x126071520997 is located 0 bytes to the right of 1047-byte region [0x126071520580,0x126071520997)
allocated by thread T0 here:
    #0 0x7ffb7dec100d in malloc (D:\Tools\msys64\clang64\bin\libclang_rt.asan_dynamic-x86_64.dll+0x18004100d)
    #1 0x7ff7eab2b08a in aom_memalign D:/Cpp/libavif/ext/aom/aom_mem/aom_mem.c:59:22
    #2 0x7ff7eab2b08a in aom_malloc D:/Cpp/libavif/ext/aom/aom_mem/aom_mem.c:67:40
    #3 0x7ff7eab2b08a in aom_calloc D:/Cpp/libavif/ext/aom/aom_mem/aom_mem.c:72:19
    #4 0x7ff7eabb8152 in av1_create_compressor D:/Cpp/libavif/ext/aom/av1/encoder/encoder.c:1387:3
    #5 0x7ff7eaac38be in av1_create_context_and_bufferpool D:/Cpp/libavif/ext/aom/av1/av1_cx_iface.c:2487:12
    #6 0x7ff7eaac38be in encoder_init D:/Cpp/libavif/ext/aom/av1/av1_cx_iface.c:2600:13
    #7 0x7ff7eaab8752 in aom_codec_enc_init_ver D:/Cpp/libavif/ext/aom/aom/src/aom_encoder.c:80:11
    #8 0x7ff7eaa8bcd0 in aomCodecEncodeImage D:/Cpp/libavif/src/codec_aom.c:726:13
    #9 0x7ff7eaa7d2f4 in avifEncoderAddImageInternal D:/Cpp/libavif/src/write.c:915:17
    #10 0x7ff7eaa7b690 in avifEncoderAddImage D:/Cpp/libavif/src/write.c:934:12
    #11 0x7ff7eaa54170 in libavif::(anonymous namespace)::ChangeSettingTest_DISABLED_ChangeDimension_Test::TestBody() D:/Cpp/libavif/tests/gtest/avifchangesettingtest.cc:183:5
    #12 0x7ffbebd58308 in void testing::internal::HandleExceptionsInMethodIfSupported<testing::Test, void>(testing::Test*, void (testing::Test::*)(), char const*) (D:\Tools\msys64\clang64\bin\libgtest.dll+0x180038308)
    #13 0x7ffbebd3ce8a in testing::Test::Run() (D:\Tools\msys64\clang64\bin\libgtest.dll+0x18001ce8a)
    #14 0x7ffbebd3e16e in testing::TestInfo::Run() (D:\Tools\msys64\clang64\bin\libgtest.dll+0x18001e16e)
    #15 0x7ffbebd3ec57 in testing::TestSuite::Run() (D:\Tools\msys64\clang64\bin\libgtest.dll+0x18001ec57)
    #16 0x7ffbebd4f2ab in testing::internal::UnitTestImpl::RunAllTests() (D:\Tools\msys64\clang64\bin\libgtest.dll+0x18002f2ab)
    #17 0x7ffbebd59168 in bool testing::internal::HandleExceptionsInMethodIfSupported<testing::internal::UnitTestImpl, bool>(testing::internal::UnitTestImpl*, bool (testing::internal::UnitTestImpl::*)(), char const*) (D:\Tools\msys64\clang64\bin\libgtest.dll+0x180039168)
    #18 0x7ffbebd4eadf in testing::UnitTest::Run() (D:\Tools\msys64\clang64\bin\libgtest.dll+0x18002eadf)
    #19 0x7ffbefab1434 in main (D:\Tools\msys64\clang64\bin\libgtest_main.dll+0x180001434)
    #20 0x7ff7eaa513ad in __tmainCRTStartup C:/M/mingw-w64-crt-git/src/mingw-w64/mingw-w64-crt/crt/crtexe.c:329:15
    #21 0x7ff7eaa514e5 in mainCRTStartup C:/M/mingw-w64-crt-git/src/mingw-w64/mingw-w64-crt/crt/crtexe.c:206:9
    #22 0x7ffc008f54df  (C:\WINDOWS\System32\KERNEL32.DLL+0x1800154df)
    #23 0x7ffc0224485a  (C:\WINDOWS\SYSTEM32\ntdll.dll+0x18000485a)

SUMMARY: AddressSanitizer: heap-buffer-overflow (D:\Tools\msys64\clang64\bin\libclang_rt.asan_dynamic-x86_64.dll+0x180040b8b) in __asan_memset
Shadow bytes around the buggy address:
  0x047a7f7a40e0: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
  0x047a7f7a40f0: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
  0x047a7f7a4100: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
  0x047a7f7a4110: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
  0x047a7f7a4120: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
=>0x047a7f7a4130: 00 00[07]fa fa fa fa fa fa fa fa fa fa fa fa fa
  0x047a7f7a4140: fa fa fa fa fa fa fa fa fa fa fa fa fa fa fa fa
  0x047a7f7a4150: fa fa fa fa fa fa fa fa fa fa fa fa fa fa fa fa
  0x047a7f7a4160: fa fa fa fa fa fa fa fa fa fa fa fa fa fa fa fa
  0x047a7f7a4170: fa fa fa fa fa fa fa fa fa fa fa fa fa fa fa fa
  0x047a7f7a4180: fa fa fa fa fa fa fa fa fa fa fa fa fa fa fa fa
Shadow byte legend (one shadow byte represents 8 application bytes):
  Addressable:           00
  Partially addressable: 01 02 03 04 05 06 07 
  Heap left redzone:       fa
  Freed heap region:       fd
  Stack left redzone:      f1
  Stack mid redzone:       f2
  Stack right redzone:     f3
  Stack after return:      f5
  Stack use after scope:   f8
  Global redzone:          f9
  Global init order:       f6
  Poisoned by user:        f7
  Container overflow:      fc
  Array cookie:            ac
  Intra object redzone:    bb
  ASan internal:           fe
  Left alloca redzone:     ca
  Right alloca redzone:    cb
==62176==ABORTING

@wantehchang wantehchang left a comment

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Yuan: I reviewed everything except the avifRWStreamWrite parts of src/write.c and the decoder part of the new test in avifchangesettingtest.cc. I believe your changes to src/codec_aom.c. So this looks like a libaom bug that certain arrays are allocated to the size of the initial values of cfg.g_w and cfg.g_h.

Comment thread src/write.c Outdated
Comment thread include/avif/avif.h Outdated
Comment thread src/write.c Outdated
@wantehchang

Copy link
Copy Markdown
Member

Yuan: Please test this libaom patch. It is likely to be incomplete (e.g., the VMAF and BUTTERAUGLI code may also need the same changes).

diff --git a/av1/encoder/encoder.c b/av1/encoder/encoder.c
index 65d3a10..8e866a4 100644
--- a/av1/encoder/encoder.c
+++ b/av1/encoder/encoder.c
@@ -1355,8 +1355,21 @@ AV1_COMP *av1_create_compressor(AV1_PRIMARY *ppi, const AV1EncoderConfig *oxcf,
   av1_set_speed_features_framesize_independent(cpi, oxcf->speed);
   av1_set_speed_features_framesize_dependent(cpi, oxcf->speed);
 
+  int max_mi_cols = mi_params->mi_cols;
+  int max_mi_rows = mi_params->mi_rows;
+  if (oxcf->frm_dim_cfg.forced_max_frame_width) {
+    const int aligned_width =
+        ALIGN_POWER_OF_TWO(oxcf->frm_dim_cfg.forced_max_frame_width, 3);
+    max_mi_cols = aligned_width >> MI_SIZE_LOG2;
+  }
+  if (oxcf->frm_dim_cfg.forced_max_frame_height) {
+    const int aligned_height =
+        ALIGN_POWER_OF_TWO(oxcf->frm_dim_cfg.forced_max_frame_height, 3);
+    max_mi_rows = aligned_height >> MI_SIZE_LOG2;
+  }
+
   CHECK_MEM_ERROR(cm, cpi->consec_zero_mv,
-                  aom_calloc((mi_params->mi_rows * mi_params->mi_cols) >> 2,
+                  aom_calloc((max_mi_rows * max_mi_cols) >> 2,
                              sizeof(*cpi->consec_zero_mv)));
 
   cpi->mb_weber_stats = NULL;
@@ -1366,8 +1379,8 @@ AV1_COMP *av1_create_compressor(AV1_PRIMARY *ppi, const AV1EncoderConfig *oxcf,
     const int bsize = BLOCK_16X16;
     const int w = mi_size_wide[bsize];
     const int h = mi_size_high[bsize];
-    const int num_cols = (mi_params->mi_cols + w - 1) / w;
-    const int num_rows = (mi_params->mi_rows + h - 1) / h;
+    const int num_cols = (max_mi_cols + w - 1) / w;
+    const int num_rows = (max_mi_rows + h - 1) / h;
     CHECK_MEM_ERROR(cm, cpi->ssim_rdmult_scaling_factors,
                     aom_calloc(num_rows * num_cols,
                                sizeof(*cpi->ssim_rdmult_scaling_factors)));

@wantehchang

Copy link
Copy Markdown
Member

I discussed this issue with my colleague Marco Paniconi, who works on real-time encoding and SVC in libaom. Marco told me that they don't use g_forced_max_frame_width and g_forced_max_frame_height. Instead, they set g_w and g_h to the fixed size of the frames (the frames come from the camera and therefore have the same size) and then let libaom downscale the frames internally for certain spatial layers. Please see examples/svc_encoder_rtc.c in libaom. We should switch to this approach.

Although AVIF allows the images in an image sequence to have different sizes, the libavif encoder does not need to support that. (The libavif decoder supports that.)

@tongyuantongyu tongyuantongyu left a comment

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Please test this libaom patch

Thaks for the patch. I confirm that after patch libaom encodes correctly.

let libaom downscale the frames internally for certain spatial layers

Actually I'm planning to support both. Use libaom internal scaler is more convenient for basic usage with only one input image, but if user wants more flexibility (e.g. using small blurred "thumbnail" as first layer) input image of different size can be both more efficient and convenient.

Earlier this year I reported two bugs in libaom's internal scaler. I see aomedia:3210 is fixed by a recent commit https://aomedia-review.googlesource.com/c/aom/+/161961. For aomedia:3203 I have an old CL, but I can't figure out how to update it because it's targeting master branch, so I created a new one instead.

Comment thread include/avif/avif.h Outdated
@tongyuantongyu

Copy link
Copy Markdown
Contributor Author

With the latest tip of libaom all added test cases passes.

to the fixed size of the frames (the frames come from the camera and therefore have the same size) then let libaom downscale the frames internally for certain spatial layers. Please see examples/svc_encoder_rtc.c in libaom. We should switch to this approach.

AVIF use case is different here: we want to use different images for each layer (like the low resolution blurry preview example I mentioned) - instead of sending the same frame many times to encode scalable video. So the SVC approach puts user into an awkward situation: they need to upscale the first layer to match frame size, and then libaom downscales it back. This is inefficient, and upscale-downscale roundtrip can be lossy.

@tongyuantongyu
tongyuantongyu force-pushed the var-frame-size branch 2 times, most recently from 4761eb5 to 5e208db Compare January 15, 2023 12:50
@tongyuantongyu
tongyuantongyu marked this pull request as ready for review January 15, 2023 13:25
@tongyuantongyu
tongyuantongyu marked this pull request as draft September 13, 2023 14:25
cyh5272 pushed a commit to cyh5272/aom that referenced this pull request May 6, 2024
Based on the libavif avifchangedimensiontest
AOMDecreasing/ChangeDimensionTest.EncodeDecode/0 in
AOMediaCodec/libavif#1069
by Yuan Tong.

Test:
test_libaom --gtest_filter=*DimensionDecreasing \
    --gtest_also_run_disabled_tests

Bug: aomedia:3348
Change-Id: I8be3c092234f99402c02aa03340c916f629ce0f2
cyh5272 pushed a commit to cyh5272/aom that referenced this pull request May 6, 2024
Based on the libavif avifchangedimensiontest
AOMDecreasing/ChangeDimensionTest.EncodeDecode/0 in
AOMediaCodec/libavif#1069
by Yuan Tong.

Test:
test_libaom --gtest_filter=*DimensionDecreasing \
    --gtest_also_run_disabled_tests

Bug: aomedia:3348
Change-Id: I79aea87012cc3bea43d565911ac88816c860e737
cyh5272 pushed a commit to cyh5272/aom that referenced this pull request May 6, 2024
Based on the libavif avifchangedimensiontest
AOMDecreasing/ChangeDimensionTest.EncodeDecode/0 in
AOMediaCodec/libavif#1069
by Yuan Tong.

Test:
test_libaom --gtest_filter=*DimensionDecreasing \
    --gtest_also_run_disabled_tests

Bug: aomedia:3348
Change-Id: I79aea87012cc3bea43d565911ac88816c860e737
@tongyuantongyu
tongyuantongyu force-pushed the var-frame-size branch 6 times, most recently from 7958220 to c2a0f52 Compare March 19, 2026 15:36
@tongyuantongyu tongyuantongyu changed the title Support changing frame size during encoding Support different sizes for each image layer Mar 19, 2026
@tongyuantongyu

Copy link
Copy Markdown
Contributor Author

@wantehchang I'm reviving this PR as recent development in AOM seem to have fixed the crash.

@juliobbv-p Thank you for your hard work!

@tongyuantongyu
tongyuantongyu marked this pull request as ready for review March 23, 2026 13:26
@tongyuantongyu

Copy link
Copy Markdown
Contributor Author

If avifPeekImage() and the related peek functions are needed by avifenc.c and cannot be cleanly moved to a prerequisite or follow-up PR

The addition of avifPeekImage() can be extracted into its own PR as prerequisite, but nobody else uses it. Not sure if this is what you prefer.

that avifenc.c needs the new avifImagePeek() and related peek functions

"If avifEncoder->width/height must equal to the size of the last layer, then peek it instead of asking user for that known info". That's the whole idea for the addition of avifImagePeek().

@wantehchang

Copy link
Copy Markdown
Member

Yuan: Thank you for the reply. Please keep avifImagePeek() and the related peek functions in this PR.

@wantehchang wantehchang left a comment

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Yuan: I reviewed your new changes to the src/ directory today. I have some questions and suggestions. I will review your new changes to the apps/ directory next (hopefully tomorrow).

Comment thread src/codec_aom.c
Comment thread src/write.c
Comment thread src/write.c Outdated
Comment thread src/write.c Outdated
Comment thread src/write.c Outdated
Comment thread src/write.c Outdated
Comment thread src/write.c Outdated
AVIF_CHECKRES(avifEncoderValidateSize(encoder, gridCols, gridRows, firstCell));
// Reject mixing scalingMode and adding input images of different sizes.
// They are different ways the achieve the same result, so mixing them
// creates confusion without providing any benefit.

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Can we move this comment to the avifEncoderValidateConsistentSize() function definition?

Also change "the achieve" to "to achieve".

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I merged them since they largely share the theme, are called back-to-back and are only called here.

Comment thread apps/shared/avifjpeg.c
Comment thread CHANGELOG.md

* Add the ignoreICC option to avifDecoder
* Support ignoring alpha in avifDecoder::imageContentToDecode
* Support encoding layered image with pre-scaled inputs

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Yuan: Can you rebase this pull request to the tip of the main branch? This will fix the build-android-jni CI workflow failure. Do not make any other changes, because I am about to finish my review and send you the final review comments. Thanks!

Status update: I may not have time to send you the final review comments until this weekend. I have reviewed everything except avifenc.c. I only have some questions about avifImagePeek() and the new parameters of avifImageDump(). I will ask you to just add some comments so we can merge this PR with minimal changes. But I will also suggest possible follow-up cleanup.

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Let me do a merge instead so the commits you are looking at won't change. You probably will squash when merging this PR, so there shouldn't be other differences.

@tongyuantongyu tongyuantongyu Sep 17, 2026 •

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Let me guess your questions:

About avifPeekImage(): the semi-public API avifReadImage() is already getting a bit too many arguments, so I avoided overloading it further.

avifAppFileFormat avifReadImage(const char * filename,
avifAppFileFormat inputFormat,
avifPixelFormat requestedFormat,
int requestedDepth,
avifChromaDownsampling chromaDownsampling,
avifBool ignoreColorProfile,
avifBool ignoreExif,
avifBool ignoreXMP,
avifBool ignoreAlpha,
avifBool ignoreGainMap,
uint32_t imageSizeLimit,
avifImage * image,
uint32_t * outDepth,
avifAppSourceTiming * sourceTiming,
struct y4mFrameIterator ** frameIter)


About avifImageDump(): since gain map info need gridCols/gridRows, we cannot precompute the image size and eliminate them:

uint32_t gainMapWidth = gainMapImage->width;
uint32_t gainMapHeight = gainMapImage->height;
if (gridCols && gridRows) {
gainMapWidth *= gridCols;
gainMapHeight *= gridRows;
}

@tongyuantongyu

Copy link
Copy Markdown
Contributor Author

The newly added layered image encoding fuzz test is unstable due to #3344. I created #3368 to avoid the issue in fuzz code.

wantehchang added a commit that referenced this pull request Sep 17, 2026
Use cinfo->output_width/height instead of cinfo->image_width/height in
avifJPEGCopyPixels() because cinfo->output_width/height is the
resolution of the pixels jpeg_read_raw_data() produces.

Suggested by Yuan Tong in
#1069 (comment).
Comment thread apps/avifgainmaputil/imageio.cc
Comment thread apps/shared/avifutil.h
Comment thread apps/shared/avifutil.h
Comment thread apps/avifenc.c
Comment thread apps/avifenc.c
Comment thread include/avif/avif.h
// are interpreted relative to that size rather than to this intermediate layer's own
// width/height. No special handling is needed during decode: the decoded layer is
// automatically scaled to the configured size, so these transformations are relative to the
// decoded avifImage's width/height as usual.

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Note: This is just a comment, not a request for change.

If I understand it correctly, libavif also scales a non-layered still image to the size in the ispe box. I believe this scaling is a "nonstandard" behavior of libavif. Do you agree?

Comment thread tests/gtest/avifprogressivetest.cc Outdated
Comment thread tests/gtest/avifprogressivetest.cc
Comment thread apps/shared/avifpng.c
Comment thread apps/shared/avifutil.c
Comment thread apps/shared/avifutil.c Outdated
Comment thread apps/shared/avifutil.h Outdated
.WithDomains(ArbitraryAvifLayered(), ArbitraryScalingModes(),
ArbitraryAvifEncoder(), ArbitraryAvifDecoder());

FUZZ_TEST(LayeredEncodeDecodeAvifFuzzTest, EncodeDecodeDimensionChangeExternal)

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This test crashed with a segmentation fault in the CI workflow. I don't know how to reproduce it. I can merge this PR first and debug it when oss-fuzz reproduces the crash.

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

FuzzTest prints the seed it used, and you can reproduce the exact case with that seed:

FUZZTEST_PRNG_SEED=Au4QmbGabXIyniIaXGJf3kW1n2GlG3xMpoj3E8l38Hc ./tests/avif_fuzztest_enc_dec_layered --gtest_filter="*EncodeDecodeDimensionChangeExternal*"

Thr crash is inside AOM, and likely related to extremely small intermediate layer size with large tiling. The case encodes a 3rd layer with size 1x141, with encoder size 8x256 and tileRowsLog2=6. It also allows maxThreads=64 workers.

Thread 7 received signal SIGSEGV, Segmentation fault.
[Switching to Thread 8964.0x7030]
0x00007ff67f55009e in encode_sb_row ()
#0  0x00007ff67f55009e in encode_sb_row ()
#1  0x00007ff67f550c8c in av1_encode_sb_row ()
#2  0x00007ff67f599837 in enc_row_mt_worker_hook ()
#3  0x00007ff67fa6eb28 in thread_loop ()

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Yuan: Thank you! I reproduced the crash now. I will debug it.

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

cc: @jzern

This crash can be fixed by https://aomedia-review.googlesource.com/c/aom/+/216921. I don't know why. Ideally we should figure out a way for libavif to avoid this crash.

The stack trace of the crash is:

lowbd_compute_gradient_info_sb (x=0x55555710b100, sb_size=BLOCK_64X64, plane=0 '\000')
    at aom/av1/encoder/intra_mode_search_utils.h:208
208           grad_info_sb[r * sb_width + c].is_dx_zero = (dx == 0);
(gdb) bt
#0  lowbd_compute_gradient_info_sb (x=0x55555710b100, sb_size=BLOCK_64X64, plane=0 '\000')
    at aom/av1/encoder/intra_mode_search_utils.h:208
#1  0x00005555559c6ed8 in compute_gradient_info_sb (x=0x55555710b100, sb_size=BLOCK_64X64, plane=0 '\000')
    at aom/av1/encoder/intra_mode_search_utils.h:312
#2  0x00005555559c7010 in produce_gradients_for_sb (cpi=0x555556f017e0, x=0x55555710b100, sb_size=BLOCK_64X64, mi_row=16, 
    mi_col=32) at aom/av1/encoder/intra_mode_search_utils.h:353
#3  0x00005555559cb205 in encode_sb_row (cpi=0x555556f017e0, td=0x55555710b100, tile_data=0x5555581ace00, mi_row=16, 
    tp=0x7ffff6805cd8) at aom/av1/encoder/encodeframe.c:1330
#4  0x00005555559cbca4 in av1_encode_sb_row (cpi=0x555556f017e0, td=0x55555710b100, tile_row=1, tile_col=2, mi_row=16)
    at aom/av1/encoder/encodeframe.c:1524
#5  0x0000555555a3a121 in enc_row_mt_worker_hook (arg1=0x55555784de30, unused=0x0)
    at aom/av1/encoder/ethread.c:727
#6  0x0000555555f04389 in execute (worker=0x555556cd73a0)
    at aom/aom_util/aom_thread.c:199
#7  0x0000555555f03f52 in thread_loop (ptr=0x555556cd73a0)
    at aom/aom_util/aom_thread.c:84
#8  0x00007ffff789e918 in start_thread (arg=<optimized out>) at ./nptl/pthread_create.c:454
#9  0x00007ffff791cfd8 in __GI___clone3 () at ../sysdeps/unix/sysv/linux/x86_64/clone3.S:78
(gdb) p grad_info_sb
$1 = (PixelLevelGradientInfo * const) 0x0
(gdb) up
#1  0x00005555559c6ed8 in compute_gradient_info_sb (x=0x55555710b100, sb_size=BLOCK_64X64, plane=0 '\000')
    at aom/av1/encoder/intra_mode_search_utils.h:312
312       lowbd_compute_gradient_info_sb(x, sb_size, plane);
(gdb) p x->pixel_gradient_info 
$2 = (PixelLevelGradientInfo *) 0x0
(gdb

@tongyuantongyu tongyuantongyu Sep 24, 2026 •

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The crash actually does not necessarily require a change to the input size. It needs g_lag_in_frames <= 1, but we also set that when encoding an animation with alpha That also happens when encoding an animation, and larger g_lag_in_frames only delays the number of frames needed for it to happen:

magick -size 128x1024 xc:gray -alpha set -channel A -evaluate set 60% +channel flat.png                                                                                                                                                                                                                                                                        
avifenc -s 6 -j 8 -k 2 flat.png flat.png --tilerowslog2:u 2 flat.png -o flat.avif   # Segmentation fault         

It happens when the number of encoding worker threads grows (happens when new frame is larger or number of tiles increased) while the previous frame was not a keyframe. In that case the new workers are allocated without their gradient cache, so any later keyframe crashes.

Current AOM release forces keyframe when new frame is larger, unless cpi->svc.number_spatial_layers > 1, but libavif uses AOME_SET_NUMBER_SPATIAL_LAYERS(ppi->number_spatial_layers) instead, so this can only be triggered by

  • Increasing tileRowsLog2/tileColsLog2
  • Encoding layer 0 and layer 1 of the same size and layer 2 of a larger size.

We can track whether the previous frame was a keyframe, and reject increasing frame size or changing tiling config if the previous frame was not.

Comment thread src/codec_aom.c
}
}
} else {
// Detect the libaom bug before v3.15.2 described in

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I'm not sure if the next version will be v3.15.2 or v3.16.0.

@tongyuantongyu

Copy link
Copy Markdown
Contributor Author

@wantehchang while debugging, I found that the current release of AOM will make every larger frame a keyframe when using pre-scaled inputs, and https://aomedia-review.googlesource.com/c/aom/+/216241 only lifted that restriction for REALTIME usage. There's actually only a little work left to enable that for GOOD_QUALITY as well, so I submitted a change to do that: https://aomedia-review.googlesource.com/c/aom/+/217061.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants