From 79c42749a85ee0f8df50bc1b9eb2b01db088d2cb Mon Sep 17 00:00:00 2001 From: "Dylan M. Taylor" Date: Wed, 11 Nov 2026 18:46:50 -0500 Subject: [PATCH] Export, Media: codecs and containers from craft-libs; remove hevcenc, av1enc, vp9enc (#3522) * Export, Media: H.264, ProRes, HEVC, VP9, AV1, AAC, MP4 and Matroska come from craft-libs crates/export and crates/media use craft-video, craft-audio and craft-media (one craft-libs rev) instead of filmcraft-h264enc, -prores, -aac, -isobmff, -matroska and -opus. The av1enc, hevcenc and vp9enc crates are removed: craft-video carries the same encoders, with the later changes merged in storytold/craft-libs#829. Their round-trip tests move to crates/export/tests and run against craft-video. opusenc stays (craft-libs has no Opus encoder yet), as do filmcraft-mpegts and the filmcraft-vp9, -hevc and -av1 decoders the tests use as oracles. * Pin craft-libs at the main commit that has the encoder colour description (e7c3dda7) --- Cargo.lock | 102 +- Cargo.toml | 63 +- README.md | 2 +- crates/av1enc/Cargo.toml | 16 - crates/av1enc/README.md | 76 - crates/av1enc/src/bitw.rs | 87 - crates/av1enc/src/cdf.rs | 172 - crates/av1enc/src/decide.rs | 1273 ----- crates/av1enc/src/lib.rs | 670 --- crates/av1enc/src/loopfilter.rs | 209 - crates/av1enc/src/pred.rs | 445 -- crates/av1enc/src/symbol.rs | 267 - crates/av1enc/src/tables.rs | 4805 ----------------- crates/av1enc/src/transform.rs | 575 -- crates/av1enc/src/writer.rs | 1040 ---- crates/av1enc/tests/it/main.rs | 6 - crates/export/Cargo.toml | 15 +- crates/export/src/encode.rs | 52 +- crates/export/src/hevc_av1.rs | 34 +- crates/export/src/lib.rs | 8 +- crates/export/src/uncompressed.rs | 2 +- crates/export/src/webm.rs | 12 +- .../tests/it/av1_encoder}/bench.rs | 8 +- .../tests/it/av1_encoder/common.rs} | 4 +- .../tests/it/av1_encoder}/ffmpeg_oracle.rs | 6 +- crates/export/tests/it/av1_encoder/mod.rs | 6 + .../tests/it/av1_encoder}/roundtrip.rs | 10 +- .../tests/it/hevc_encoder.rs} | 8 +- crates/export/tests/it/main.rs | 2 + crates/export/tests/it/movie_bytes.rs | 6 +- crates/export/tests/it/movie_color.rs | 4 +- crates/export/tests/it/ms_rounded_footage.rs | 10 +- crates/export/tests/it/opus_roundtrip.rs | 4 +- crates/export/tests/it/prores_color.rs | 4 +- crates/export/tests/it/roundtrip.rs | 8 +- crates/export/tests/it/uncompressed_mov.rs | 6 +- crates/export/tests/it/vp9_roundtrip.rs | 4 +- crates/hevcenc/Cargo.toml | 15 - crates/hevcenc/README.md | 88 - crates/hevcenc/src/bitstream.rs | 135 - crates/hevcenc/src/cabac.rs | 220 - crates/hevcenc/src/deblock.rs | 148 - crates/hevcenc/src/frame.rs | 1481 ----- crates/hevcenc/src/inter.rs | 157 - crates/hevcenc/src/intra.rs | 254 - crates/hevcenc/src/lib.rs | 365 -- crates/hevcenc/src/me.rs | 144 - crates/hevcenc/src/params.rs | 265 - crates/hevcenc/src/tables.rs | 236 - crates/hevcenc/src/transform.rs | 328 -- crates/host/src/lib.rs | 2 +- crates/media/Cargo.toml | 5 +- crates/media/src/aac.rs | 30 +- crates/media/src/downmix.rs | 4 +- crates/media/src/probe.rs | 16 +- crates/media/tests/it/aac_layout.rs | 20 +- crates/media/tests/it/downmix.rs | 6 +- crates/media/tests/it/file_timecode.rs | 2 +- crates/opusenc/Cargo.toml | 2 +- crates/opusenc/README.md | 6 +- crates/opusenc/tests/it/common/mod.rs | 4 +- crates/pdf/README.md | 2 +- crates/vp9enc/Cargo.toml | 12 - crates/vp9enc/README.md | 47 - crates/vp9enc/src/bool.rs | 259 - crates/vp9enc/src/lf.rs | 299 - crates/vp9enc/src/lib.rs | 503 -- crates/vp9enc/src/mc.rs | 265 - crates/vp9enc/src/probs.rs | 465 -- crates/vp9enc/src/tables.rs | 201 - crates/vp9enc/src/tile.rs | 940 ---- crates/vp9enc/src/transform.rs | 270 - docs/architecture.md | 3 - xtask/src/main.rs | 3 - xtask/src/pdf_deps.rs | 2 +- 75 files changed, 228 insertions(+), 16997 deletions(-) delete mode 100644 crates/av1enc/Cargo.toml delete mode 100644 crates/av1enc/README.md delete mode 100644 crates/av1enc/src/bitw.rs delete mode 100644 crates/av1enc/src/cdf.rs delete mode 100644 crates/av1enc/src/decide.rs delete mode 100644 crates/av1enc/src/lib.rs delete mode 100644 crates/av1enc/src/loopfilter.rs delete mode 100644 crates/av1enc/src/pred.rs delete mode 100644 crates/av1enc/src/symbol.rs delete mode 100644 crates/av1enc/src/tables.rs delete mode 100644 crates/av1enc/src/transform.rs delete mode 100644 crates/av1enc/src/writer.rs delete mode 100644 crates/av1enc/tests/it/main.rs rename crates/{av1enc/tests/it => export/tests/it/av1_encoder}/bench.rs (94%) rename crates/{av1enc/tests/it/common/mod.rs => export/tests/it/av1_encoder/common.rs} (97%) rename crates/{av1enc/tests/it => export/tests/it/av1_encoder}/ffmpeg_oracle.rs (97%) create mode 100644 crates/export/tests/it/av1_encoder/mod.rs rename crates/{av1enc/tests/it => export/tests/it/av1_encoder}/roundtrip.rs (93%) rename crates/{hevcenc/tests/roundtrip.rs => export/tests/it/hevc_encoder.rs} (98%) delete mode 100644 crates/hevcenc/Cargo.toml delete mode 100644 crates/hevcenc/README.md delete mode 100644 crates/hevcenc/src/bitstream.rs delete mode 100644 crates/hevcenc/src/cabac.rs delete mode 100644 crates/hevcenc/src/deblock.rs delete mode 100644 crates/hevcenc/src/frame.rs delete mode 100644 crates/hevcenc/src/inter.rs delete mode 100644 crates/hevcenc/src/intra.rs delete mode 100644 crates/hevcenc/src/lib.rs delete mode 100644 crates/hevcenc/src/me.rs delete mode 100644 crates/hevcenc/src/params.rs delete mode 100644 crates/hevcenc/src/tables.rs delete mode 100644 crates/hevcenc/src/transform.rs delete mode 100644 crates/vp9enc/Cargo.toml delete mode 100644 crates/vp9enc/README.md delete mode 100644 crates/vp9enc/src/bool.rs delete mode 100644 crates/vp9enc/src/lf.rs delete mode 100644 crates/vp9enc/src/lib.rs delete mode 100644 crates/vp9enc/src/mc.rs delete mode 100644 crates/vp9enc/src/probs.rs delete mode 100644 crates/vp9enc/src/tables.rs delete mode 100644 crates/vp9enc/src/tile.rs delete mode 100644 crates/vp9enc/src/transform.rs diff --git a/Cargo.lock b/Cargo.lock index fb12590be..79691772a 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -1089,15 +1089,24 @@ dependencies = [ "libc", ] +[[package]] +name = "craft-audio" +version = "0.1.0" +source = "git+https://github.com/storytold/craft-libs?rev=e7c3dda7f3e339dd2c8d40fc4eb405472be1a1e4#e7c3dda7f3e339dd2c8d40fc4eb405472be1a1e4" +dependencies = [ + "craft-media 0.1.0 (git+https://github.com/storytold/craft-libs?rev=e7c3dda7f3e339dd2c8d40fc4eb405472be1a1e4)", + "thiserror 2.0.21", +] + [[package]] name = "craft-docpath" version = "0.1.0" -source = "git+https://github.com/storytold/craft-libs?rev=e11f9d9aebea5f6b8e74643c1ea1ed172ba5f545#e11f9d9aebea5f6b8e74643c1ea1ed172ba5f545" +source = "git+https://github.com/storytold/craft-libs?rev=e7c3dda7f3e339dd2c8d40fc4eb405472be1a1e4#e7c3dda7f3e339dd2c8d40fc4eb405472be1a1e4" [[package]] name = "craft-ecproj-format" version = "0.1.0" -source = "git+https://github.com/storytold/craft-libs?rev=ca1b5e2edb8568335c6c14f390307bd5beae8238#ca1b5e2edb8568335c6c14f390307bd5beae8238" +source = "git+https://github.com/storytold/craft-libs?rev=e7c3dda7f3e339dd2c8d40fc4eb405472be1a1e4#e7c3dda7f3e339dd2c8d40fc4eb405472be1a1e4" dependencies = [ "serde", "serde_json", @@ -1107,12 +1116,12 @@ dependencies = [ [[package]] name = "craft-fonts-build" version = "0.1.0" -source = "git+https://github.com/storytold/craft-libs?rev=e11f9d9aebea5f6b8e74643c1ea1ed172ba5f545#e11f9d9aebea5f6b8e74643c1ea1ed172ba5f545" +source = "git+https://github.com/storytold/craft-libs?rev=e7c3dda7f3e339dd2c8d40fc4eb405472be1a1e4#e7c3dda7f3e339dd2c8d40fc4eb405472be1a1e4" [[package]] name = "craft-i18n" version = "0.1.0" -source = "git+https://github.com/storytold/craft-libs?rev=e11f9d9aebea5f6b8e74643c1ea1ed172ba5f545#e11f9d9aebea5f6b8e74643c1ea1ed172ba5f545" +source = "git+https://github.com/storytold/craft-libs?rev=e7c3dda7f3e339dd2c8d40fc4eb405472be1a1e4#e7c3dda7f3e339dd2c8d40fc4eb405472be1a1e4" dependencies = [ "fixed_decimal", "icu_decimal", @@ -1132,6 +1141,14 @@ dependencies = [ "writeable", ] +[[package]] +name = "craft-media" +version = "0.1.0" +source = "git+https://github.com/storytold/craft-libs?rev=e7c3dda7f3e339dd2c8d40fc4eb405472be1a1e4#e7c3dda7f3e339dd2c8d40fc4eb405472be1a1e4" +dependencies = [ + "thiserror 2.0.21", +] + [[package]] name = "craft-media" version = "0.1.0" @@ -1143,7 +1160,7 @@ dependencies = [ [[package]] name = "craft-pdf" version = "0.1.0" -source = "git+https://github.com/storytold/craft-libs?rev=e11f9d9aebea5f6b8e74643c1ea1ed172ba5f545#e11f9d9aebea5f6b8e74643c1ea1ed172ba5f545" +source = "git+https://github.com/storytold/craft-libs?rev=e7c3dda7f3e339dd2c8d40fc4eb405472be1a1e4#e7c3dda7f3e339dd2c8d40fc4eb405472be1a1e4" dependencies = [ "hayro-syntax", "thiserror 2.0.21", @@ -1152,7 +1169,7 @@ dependencies = [ [[package]] name = "craft-quit" version = "0.1.0" -source = "git+https://github.com/storytold/craft-libs?rev=e11f9d9aebea5f6b8e74643c1ea1ed172ba5f545#e11f9d9aebea5f6b8e74643c1ea1ed172ba5f545" +source = "git+https://github.com/storytold/craft-libs?rev=e7c3dda7f3e339dd2c8d40fc4eb405472be1a1e4#e7c3dda7f3e339dd2c8d40fc4eb405472be1a1e4" dependencies = [ "dispatch2", "egui", @@ -1163,7 +1180,7 @@ dependencies = [ [[package]] name = "craft-ui" version = "0.1.0" -source = "git+https://github.com/storytold/craft-libs?rev=e11f9d9aebea5f6b8e74643c1ea1ed172ba5f545#e11f9d9aebea5f6b8e74643c1ea1ed172ba5f545" +source = "git+https://github.com/storytold/craft-libs?rev=e7c3dda7f3e339dd2c8d40fc4eb405472be1a1e4#e7c3dda7f3e339dd2c8d40fc4eb405472be1a1e4" dependencies = [ "craft-fonts-build", "craft-i18n", @@ -1184,7 +1201,7 @@ dependencies = [ [[package]] name = "craft-update" version = "0.1.0" -source = "git+https://github.com/storytold/craft-libs?rev=6e03f65bf45181db77ba6229716e6624db9eb25b#6e03f65bf45181db77ba6229716e6624db9eb25b" +source = "git+https://github.com/storytold/craft-libs?rev=e7c3dda7f3e339dd2c8d40fc4eb405472be1a1e4#e7c3dda7f3e339dd2c8d40fc4eb405472be1a1e4" dependencies = [ "chrono", "dunce", @@ -1208,6 +1225,16 @@ dependencies = [ "zip 9.1.3", ] +[[package]] +name = "craft-video" +version = "0.1.0" +source = "git+https://github.com/storytold/craft-libs?rev=e7c3dda7f3e339dd2c8d40fc4eb405472be1a1e4#e7c3dda7f3e339dd2c8d40fc4eb405472be1a1e4" +dependencies = [ + "craft-media 0.1.0 (git+https://github.com/storytold/craft-libs?rev=e7c3dda7f3e339dd2c8d40fc4eb405472be1a1e4)", + "rayon", + "thiserror 2.0.21", +] + [[package]] name = "crc32fast" version = "1.5.2" @@ -1475,14 +1502,6 @@ dependencies = [ "thiserror 2.0.21", ] -[[package]] -name = "effectcraft-av1enc" -version = "0.24.1" -dependencies = [ - "filmcraft-av1", - "tempfile", -] - [[package]] name = "effectcraft-cli" version = "0.24.1" @@ -1571,10 +1590,11 @@ dependencies = [ name = "effectcraft-export" version = "0.24.1" dependencies = [ - "effectcraft-av1enc", + "craft-audio", + "craft-media 0.1.0 (git+https://github.com/storytold/craft-libs?rev=e7c3dda7f3e339dd2c8d40fc4eb405472be1a1e4)", + "craft-video", "effectcraft-color", "effectcraft-effects", - "effectcraft-hevcenc", "effectcraft-keyframe", "effectcraft-media", "effectcraft-opusenc", @@ -1582,13 +1602,9 @@ dependencies = [ "effectcraft-raster", "effectcraft-render", "effectcraft-time", - "effectcraft-vp9enc", "exr", - "filmcraft-aac", - "filmcraft-h264enc", - "filmcraft-isobmff", - "filmcraft-opus", - "filmcraft-prores", + "filmcraft-av1", + "filmcraft-hevc", "filmcraft-vp9", "gif", "image", @@ -1658,13 +1674,6 @@ dependencies = [ "serde_json", ] -[[package]] -name = "effectcraft-hevcenc" -version = "0.24.1" -dependencies = [ - "filmcraft-hevc", -] - [[package]] name = "effectcraft-host" version = "0.24.1" @@ -1745,7 +1754,9 @@ dependencies = [ name = "effectcraft-media" version = "0.24.1" dependencies = [ + "craft-audio", "craft-docpath", + "craft-media 0.1.0 (git+https://github.com/storytold/craft-libs?rev=e7c3dda7f3e339dd2c8d40fc4eb405472be1a1e4)", "effectcraft-color", "effectcraft-effects", "effectcraft-model", @@ -1757,12 +1768,9 @@ dependencies = [ "effectcraft-svg", "effectcraft-time", "exr", - "filmcraft-aac", "filmcraft-codecs", "filmcraft-color", "filmcraft-frame", - "filmcraft-isobmff", - "filmcraft-matroska", "filmcraft-media", "filmcraft-mpegts", "filmcraft-time", @@ -1792,7 +1800,7 @@ dependencies = [ name = "effectcraft-opusenc" version = "0.24.1" dependencies = [ - "filmcraft-opus", + "craft-audio", ] [[package]] @@ -2017,10 +2025,6 @@ dependencies = [ "web-time", ] -[[package]] -name = "effectcraft-vp9enc" -version = "0.24.1" - [[package]] name = "effectcraft-web" version = "0.24.1" @@ -2488,7 +2492,7 @@ name = "filmcraft-bitstream" version = "0.12.0" source = "git+https://github.com/storytold/filmcraft?rev=d5ead4ba1ee2f1cd13cb86a4239aee2730a3d9f5#d5ead4ba1ee2f1cd13cb86a4239aee2730a3d9f5" dependencies = [ - "craft-media", + "craft-media 0.1.0 (git+https://github.com/storytold/craft-libs?rev=f0ad6c1e1d297908915f4be315b0fcfc9be7b5d4)", ] [[package]] @@ -2589,16 +2593,6 @@ dependencies = [ "thiserror 2.0.21", ] -[[package]] -name = "filmcraft-h264enc" -version = "0.12.0" -source = "git+https://github.com/storytold/filmcraft?rev=d5ead4ba1ee2f1cd13cb86a4239aee2730a3d9f5#d5ead4ba1ee2f1cd13cb86a4239aee2730a3d9f5" -dependencies = [ - "filmcraft-bitstream", - "rayon", - "thiserror 2.0.21", -] - [[package]] name = "filmcraft-hevc" version = "0.12.0" @@ -2631,7 +2625,7 @@ name = "filmcraft-isobmff" version = "0.12.0" source = "git+https://github.com/storytold/filmcraft?rev=d5ead4ba1ee2f1cd13cb86a4239aee2730a3d9f5#d5ead4ba1ee2f1cd13cb86a4239aee2730a3d9f5" dependencies = [ - "craft-media", + "craft-media 0.1.0 (git+https://github.com/storytold/craft-libs?rev=f0ad6c1e1d297908915f4be315b0fcfc9be7b5d4)", ] [[package]] @@ -2639,7 +2633,7 @@ name = "filmcraft-matroska" version = "0.12.0" source = "git+https://github.com/storytold/filmcraft?rev=d5ead4ba1ee2f1cd13cb86a4239aee2730a3d9f5#d5ead4ba1ee2f1cd13cb86a4239aee2730a3d9f5" dependencies = [ - "craft-media", + "craft-media 0.1.0 (git+https://github.com/storytold/craft-libs?rev=f0ad6c1e1d297908915f4be315b0fcfc9be7b5d4)", ] [[package]] @@ -3328,7 +3322,7 @@ checksum = "9f4d0e94ddd48749f06bbe4e5389fb9799a0c45bcaf00495042076ef05e3241a" [[package]] name = "hayro-jbig2" version = "0.3.0" -source = "git+https://github.com/storytold/craft-libs?rev=e11f9d9aebea5f6b8e74643c1ea1ed172ba5f545#e11f9d9aebea5f6b8e74643c1ea1ed172ba5f545" +source = "git+https://github.com/storytold/craft-libs?rev=e7c3dda7f3e339dd2c8d40fc4eb405472be1a1e4#e7c3dda7f3e339dd2c8d40fc4eb405472be1a1e4" dependencies = [ "hayro-ccitt", ] @@ -3342,7 +3336,7 @@ checksum = "c75ab947623ef4ccaa7acf0579edf7cbb5a73838e3839a7be73335e522f433a1" [[package]] name = "hayro-syntax" version = "0.7.2" -source = "git+https://github.com/storytold/craft-libs?rev=e11f9d9aebea5f6b8e74643c1ea1ed172ba5f545#e11f9d9aebea5f6b8e74643c1ea1ed172ba5f545" +source = "git+https://github.com/storytold/craft-libs?rev=e7c3dda7f3e339dd2c8d40fc4eb405472be1a1e4#e7c3dda7f3e339dd2c8d40fc4eb405472be1a1e4" dependencies = [ "hayro-jbig2", "hayro-jpeg2000", diff --git a/Cargo.toml b/Cargo.toml index 6cb650067..c812a3294 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -11,12 +11,12 @@ rust-version = "1.95" repository = "https://github.com/storytold/effectcraft" [workspace.dependencies] -craft-pdf = { git = "https://github.com/storytold/craft-libs", rev = "e11f9d9aebea5f6b8e74643c1ea1ed172ba5f545", version = "=0.1.0", default-features = false, features = ["read"] } -craft-docpath = { version = "0.1.0", git = "https://github.com/storytold/craft-libs", rev = "e11f9d9aebea5f6b8e74643c1ea1ed172ba5f545" } -craft-quit = { version = "0.1.0", git = "https://github.com/storytold/craft-libs", rev = "e11f9d9aebea5f6b8e74643c1ea1ed172ba5f545" } -craft-ecproj-format = { version = "0.1.0", git = "https://github.com/storytold/craft-libs", rev = "ca1b5e2edb8568335c6c14f390307bd5beae8238" } +craft-pdf = { git = "https://github.com/storytold/craft-libs", rev = "e7c3dda7f3e339dd2c8d40fc4eb405472be1a1e4", version = "=0.1.0", default-features = false, features = ["read"] } +craft-docpath = { version = "0.1.0", git = "https://github.com/storytold/craft-libs", rev = "e7c3dda7f3e339dd2c8d40fc4eb405472be1a1e4" } +craft-quit = { version = "0.1.0", git = "https://github.com/storytold/craft-libs", rev = "e7c3dda7f3e339dd2c8d40fc4eb405472be1a1e4" } +craft-ecproj-format = { version = "0.1.0", git = "https://github.com/storytold/craft-libs", rev = "e7c3dda7f3e339dd2c8d40fc4eb405472be1a1e4" } # craft-update: app-specific pretend versions apply to startup and manual checks. -craft-update = { git = "https://github.com/storytold/craft-libs", version = "=0.1.0", rev = "6e03f65bf45181db77ba6229716e6624db9eb25b", features = ["egui"] } +craft-update = { git = "https://github.com/storytold/craft-libs", version = "=0.1.0", rev = "e7c3dda7f3e339dd2c8d40fc4eb405472be1a1e4", features = ["egui"] } # internal effectcraft-time = { path = "crates/time" } effectcraft-geom = { path = "crates/geom" } @@ -41,7 +41,6 @@ effectcraft-interchange = { path = "crates/interchange" } effectcraft-pdf = { path = "crates/pdf" } effectcraft-psd = { path = "crates/psd" } effectcraft-aep = { path = "crates/aep" } -effectcraft-vp9enc = { path = "crates/vp9enc" } effectcraft-svg = { path = "crates/svg" } effectcraft-engine = { path = "crates/engine" } effectcraft-host = { path = "crates/host" } @@ -49,8 +48,6 @@ effectcraft-ui-egui = { path = "crates/ui-egui" } effectcraft-automation = { path = "crates/automation" } effectcraft-opusenc = { path = "crates/opusenc" } effectcraft-script = { path = "crates/script" } -effectcraft-hevcenc = { path = "crates/hevcenc" } -effectcraft-av1enc = { path = "crates/av1enc" } effectcraft-plugin = { path = "crates/plugin" } effectcraft-helper-host = { path = "crates/helper-host" } @@ -82,25 +79,23 @@ image = { version = "0.25", default-features = false, features = ["png", "jpeg"] same-file = "1.0" tempfile = "3.27" -# media codecs + containers: FilmCraft's pure-Rust crates, pinned to a revision (plan/adr/0001). +# codecs + containers shared by the Craft apps (crates/media, crates/export); keep all three on one rev. +craft-media = { version = "0.1.0", git = "https://github.com/storytold/craft-libs", rev = "e7c3dda7f3e339dd2c8d40fc4eb405472be1a1e4", default-features = false, features = ["isobmff", "matroska"] } +craft-audio = { version = "0.1.0", git = "https://github.com/storytold/craft-libs", rev = "e7c3dda7f3e339dd2c8d40fc4eb405472be1a1e4", default-features = false, features = ["aac-decode", "aac-encode", "opus-decode"] } +craft-video = { version = "0.1.0", git = "https://github.com/storytold/craft-libs", rev = "e7c3dda7f3e339dd2c8d40fc4eb405472be1a1e4", features = ["h264-encode", "prores", "hevc-encode", "vp9-encode", "av1", "threads"] } + +# footage decoding: FilmCraft's pure-Rust crates, pinned to a revision (plan/adr/0001). # Only `crates/media`, `crates/export` and `crates/interchange` may depend on these. filmcraft-codecs = { git = "https://github.com/storytold/filmcraft", rev = "d5ead4ba1ee2f1cd13cb86a4239aee2730a3d9f5" } filmcraft-media = { git = "https://github.com/storytold/filmcraft", rev = "d5ead4ba1ee2f1cd13cb86a4239aee2730a3d9f5" } filmcraft-frame = { git = "https://github.com/storytold/filmcraft", rev = "d5ead4ba1ee2f1cd13cb86a4239aee2730a3d9f5" } filmcraft-color = { git = "https://github.com/storytold/filmcraft", rev = "d5ead4ba1ee2f1cd13cb86a4239aee2730a3d9f5" } filmcraft-time = { git = "https://github.com/storytold/filmcraft", rev = "d5ead4ba1ee2f1cd13cb86a4239aee2730a3d9f5" } -# encoders + muxer (crates/export) -filmcraft-h264enc = { git = "https://github.com/storytold/filmcraft", rev = "d5ead4ba1ee2f1cd13cb86a4239aee2730a3d9f5" } -filmcraft-prores = { git = "https://github.com/storytold/filmcraft", rev = "d5ead4ba1ee2f1cd13cb86a4239aee2730a3d9f5" } -filmcraft-aac = { git = "https://github.com/storytold/filmcraft", rev = "d5ead4ba1ee2f1cd13cb86a4239aee2730a3d9f5" } -filmcraft-isobmff = { git = "https://github.com/storytold/filmcraft", rev = "d5ead4ba1ee2f1cd13cb86a4239aee2730a3d9f5" } -# container headers read for AAC channel layouts (crates/media) -filmcraft-matroska = { git = "https://github.com/storytold/filmcraft", rev = "d5ead4ba1ee2f1cd13cb86a4239aee2730a3d9f5" } +# MPEG TS / PS headers read for AAC channel layouts (crates/media); craft-media has no MPEG TS reader filmcraft-mpegts = { git = "https://github.com/storytold/filmcraft", rev = "d5ead4ba1ee2f1cd13cb86a4239aee2730a3d9f5" } -# decoder used as the round-trip oracle of effectcraft-opusenc (crates/export dev-dependency) -filmcraft-opus = { git = "https://github.com/storytold/filmcraft", rev = "d5ead4ba1ee2f1cd13cb86a4239aee2730a3d9f5" } +# decoders used as the round-trip oracles of craft-video's VP9, HEVC and AV1 encoders (crates/export +# dev-dependencies only); craft-video has no decoder for them filmcraft-vp9 = { git = "https://github.com/storytold/filmcraft", rev = "d5ead4ba1ee2f1cd13cb86a4239aee2730a3d9f5" } -# decoders used as the round-trip oracles of effectcraft-hevcenc / effectcraft-av1enc (dev-dependencies only) filmcraft-hevc = { git = "https://github.com/storytold/filmcraft", rev = "d5ead4ba1ee2f1cd13cb86a4239aee2730a3d9f5" } filmcraft-av1 = { git = "https://github.com/storytold/filmcraft", rev = "d5ead4ba1ee2f1cd13cb86a4239aee2730a3d9f5" } # timeline interchange (EDL, FCP7 XML, FCPXML, OTIO) for Premiere Pro interop (crates/interchange only) @@ -117,7 +112,7 @@ half = "2.7" # ui # Shared frontend components; keep this dependency above the engine boundary. # Shared UI and the complete egui family select reviewed immutable commits below. -craft-ui = { version = "0.1.0", git = "https://github.com/storytold/craft-libs", rev = "e11f9d9aebea5f6b8e74643c1ea1ed172ba5f545", features = ["serde"] } +craft-ui = { version = "0.1.0", git = "https://github.com/storytold/craft-libs", rev = "e7c3dda7f3e339dd2c8d40fc4eb405472be1a1e4", features = ["serde"] } # `default_fonts` and `monochrome_emoji_fonts` are explicit: the app's `FontDefinitions::default()` # and its `✔`/`▣` chrome glyphs need both, and kittest's own dev-dependencies do not propagate. egui = { version = "0.37", default-features = false, features = ["default_fonts", "monochrome_emoji_fonts"] } @@ -137,11 +132,6 @@ sys-locale = "0.3" # filmcraft-frame = { path = "../filmcraft/crates/frame" } # filmcraft-color = { path = "../filmcraft/crates/color" } # filmcraft-time = { path = "../filmcraft/crates/time" } -# filmcraft-h264enc = { path = "../filmcraft/crates/h264enc" } -# filmcraft-prores = { path = "../filmcraft/crates/prores" } -# filmcraft-aac = { path = "../filmcraft/crates/aac" } -# filmcraft-isobmff = { path = "../filmcraft/crates/isobmff" } -# filmcraft-matroska = { path = "../filmcraft/crates/matroska" } # filmcraft-mpegts = { path = "../filmcraft/crates/mpegts" } [workspace.lints.rust] @@ -200,50 +190,39 @@ opt-level = 3 [profile.dev.package.effectcraft-export] opt-level = 3 -[profile.dev.package.effectcraft-vp9enc] +# craft-libs codec hot loops. +[profile.dev.package.craft-video] opt-level = 3 -[profile.dev.package.effectcraft-hevcenc] +[profile.dev.package.craft-audio] opt-level = 3 -[profile.dev.package.effectcraft-av1enc] +[profile.dev.package.craft-media] opt-level = 3 # FilmCraft codec hot loops (same settings as FilmCraft's own workspace). [profile.dev.package.filmcraft-h264] opt-level = 3 -[profile.dev.package.filmcraft-h264enc] -opt-level = 3 - [profile.dev.package.filmcraft-hevc] opt-level = 3 [profile.dev.package.filmcraft-vp9] opt-level = 3 -[profile.dev.package.filmcraft-prores] -opt-level = 3 - [profile.dev.package.filmcraft-dnx] opt-level = 3 [profile.dev.package.filmcraft-av1] opt-level = 3 -[profile.dev.package.filmcraft-aac] -opt-level = 3 - -[profile.dev.package.filmcraft-opus] -opt-level = 3 - [profile.release] lto = "thin" codegen-units = 1 [patch.crates-io] -hayro-syntax = { git = "https://github.com/storytold/craft-libs", rev = "e11f9d9aebea5f6b8e74643c1ea1ed172ba5f545", version = "=0.7.2" } -hayro-jbig2 = { git = "https://github.com/storytold/craft-libs", rev = "e11f9d9aebea5f6b8e74643c1ea1ed172ba5f545", version = "=0.3.0" } +hayro-syntax = { git = "https://github.com/storytold/craft-libs", rev = "e7c3dda7f3e339dd2c8d40fc4eb405472be1a1e4", version = "=0.7.2" } +hayro-jbig2 = { git = "https://github.com/storytold/craft-libs", rev = "e7c3dda7f3e339dd2c8d40fc4eb405472be1a1e4", version = "=0.3.0" } ecolor = { git = "https://github.com/storytold/egui.git", rev = "6d518fc799681571963842f95b55a6b68431b15a" } emath = { git = "https://github.com/storytold/egui.git", rev = "6d518fc799681571963842f95b55a6b68431b15a" } epaint = { git = "https://github.com/storytold/egui.git", rev = "6d518fc799681571963842f95b55a6b68431b15a" } diff --git a/README.md b/README.md index 913f8aa06..57eddebdc 100644 --- a/README.md +++ b/README.md @@ -156,7 +156,7 @@ MP4, **WebM** (VP9 with inter frames and alpha, or AV1; Opus audio), PNG, JPEG, rendering with 3:2 pulldown, effect/solo/guide/depth overrides, crop, region of interest and resize, Render Settings and Output Module templates with defaults, post-render actions, storage overflow and render logs. The same queue runs from the command line. Every encoder is pure -Rust (FilmCraft's H.264, ProRes and AAC; EffectCraft's own VP9, AV1, HEVC and Opus); there is no +Rust (craft-libs' H.264, ProRes, HEVC, VP9, AV1 and AAC; EffectCraft's own Opus); there is no FFmpeg inside. **Lottie** goes both ways: File ▸ Export ▸ Lottie JSON… writes a composition (precomps, shape, diff --git a/crates/av1enc/Cargo.toml b/crates/av1enc/Cargo.toml deleted file mode 100644 index c70b885ce..000000000 --- a/crates/av1enc/Cargo.toml +++ /dev/null @@ -1,16 +0,0 @@ -[package] -name = "effectcraft-av1enc" -description = "Clean-room pure-Rust AV1 encoder (Main profile, key and inter frames) for MP4/WebM export, from the AV1 Bitstream & Decoding Process Specification" -version.workspace = true -edition.workspace = true -license.workspace = true -rust-version.workspace = true - -[lints] -workspace = true - -[dependencies] - -[dev-dependencies] -tempfile = { workspace = true } -filmcraft-av1 = { workspace = true } diff --git a/crates/av1enc/README.md b/crates/av1enc/README.md deleted file mode 100644 index dcc343693..000000000 --- a/crates/av1enc/README.md +++ /dev/null @@ -1,76 +0,0 @@ -# effectcraft-av1enc - -Clean-room, pure-Rust AV1 encoder for EffectCraft's MP4 / WebM export (layer L0, `std` only, -no `unsafe`, builds for `wasm32-unknown-unknown`). - -Output is decoded **bit-exactly** by libdav1d (`ffmpeg -c:v libdav1d`, checked in -`tests/it/ffmpeg_oracle.rs` when ffmpeg is installed) and by FilmCraft's spec-derived -`filmcraft-av1` decoder (`tests/it/roundtrip.rs`): every decoded frame equals the encoder's own -reconstruction, so prediction never drifts. - -## What is implemented - -- **Stream**: Main profile (`seq_profile` 0), 4:2:0, 8 or 10 bit, BT.709 colour description, - limited or full range. Sequence header with one operating point; `seq_level_idx` given or the - lowest Annex A level whose MaxPicSize / MaxH/VSize / MaxDisplayRate (and, in bitrate mode, Main - tier MaxBitrate) fit. Order hints, film grain, superres, CDEF, loop restoration, intra edge - filter, filter intra, inter-intra, masked / distance compound, warped motion and dual filter - are disabled in the sequence header. -- **Packets**: one temporal unit per frame as stored in MP4 / Matroska samples: OBUs with size - fields, no temporal delimiter; key frames start with the sequence header OBU, followed by one - `OBU_FRAME` (frame header + tile group). Every frame is shown, in input order. - `Encoder::av1c()` returns the `AV1CodecConfigurationRecord` (also the WebM `CodecPrivate`). -- **Frames**: a `KEY_FRAME` every `keyint` frames, `INTER_FRAME`s otherwise. Inter frames use - slot 0 (the previous reconstruction) for all seven references and refresh slot 0 only, so - `LAST_FRAME` is always the previous frame. CDFs adapt per symbol and are saved at the end of - every frame; inter frames load them from `LAST_FRAME` (`primary_ref_frame` = 0). One tile, - `TX_MODE_LARGEST`, `reduced_tx_set`, no segmentation, no delta q / lf. -- **Deblocking**: the normative loop filter (7.14) runs on the reconstruction; the frame level - (the same for both luma directions and both chroma planes, no deltas, sharpness 0) is searched - per frame for the lowest luma error. -- **Tiles**: the multi-symbol arithmetic encoder is the exact inverse of the spec's symbol - decoder (8.2), including the exit process padding; CDF adaptation as in 8.2.6. -- **Partitions**: 64x64 superblocks, `PARTITION_NONE` / `PARTITION_SPLIT` down to 8x8 chosen by - a rate-distortion cost (SSE + λ·estimated bits); blocks crossing the frame edge are split as - the syntax requires. 64x64 blocks are only used as inter skip blocks. -- **Intra**: DC, V, H, Paeth, smooth, smooth-V, smooth-H for luma and chroma, - ranked by SATD with a rate-distortion choice among the best three; the spec's edge preparation - and above-right / below-left availability. -- **Inter**: single reference (`LAST_FRAME`), `NEWMV` / `NEARESTMV` / `NEARMV` / `GLOBALMV` - chosen from the spec's motion vector prediction stack (7.10.2, mirrored exactly); motion search - from neighbour / co-located / parent candidates, a whole-sample diamond search and half- then - quarter-sample refinement (`allow_high_precision_mv` = 0); the normative 8-tap `EIGHTTAP` - prediction for luma and chroma; skip blocks; per-block intra / inter choice. -- **Residual**: one transform per plane and block (4x4 ... 32x32): for intra luma `DCT_DCT` or - the mode's preferred `ADST` combination (rate-distortion choice over the two best modes), the - mode-dependent type for intra chroma, `DCT_DCT` for inter blocks; dead-zone quantisation with the - spec's `Dc_Qlookup` / `Ac_Qlookup`; the normative dequantisation and inverse transforms in the - reconstruction loop; full coefficient coding (all_zero, tx type, eob_pt, eob_extra, - coeff_base_eob, coeff_base, coeff_br, dc_sign, Golomb) with the exact contexts. -- **Rate control**: `ConstantQ(base_q_idx)`; `Bitrate { kbps }` adapts `base_q_idx` per frame - towards the average target (key frames get a lower quantizer and a larger share of the budget of - their group of `keyint` frames, the inter frames the rest; the first key frame is searched in a - few passes, a later key frame that misses its budget by more than 19 % is coded again; the - deviation from the budget is paid back over 16 frames). - -## Limits - -- One tile: width and height up to 4096 and at most 2304 superblocks (4096x2304 samples). -- No CDEF or loop restoration, no compound prediction, no rectangular or 4xN blocks, no - directional intra modes other than V / H, no CfL or palette, no transform-size splits, so - compression is below mature encoders. -- 4:2:0 only (Main profile), no 12 bit. - -## Specification and provenance - -Implemented from the **AV1 Bitstream & Decoding Process Specification, version 1.0.0 with -Errata 1** (AOMediaCodec, 2019; the edition FilmCraft's decoder follows) and the **AV1 Codec -ISO Media File Format Binding** (v1.2.0) for `av1C`. - -The constant tables (default CDFs, scan orders, quantizer lookup tables, interpolation filter -taps, smooth weights, block / transform size tables, ...) and the decoder-side processes the -encoder must reproduce exactly (inverse transforms, intra and inter prediction rounding, context -derivations, motion vector prediction) are mirrored from FilmCraft's first-party, spec-derived -`filmcraft-av1` decoder (MIT OR Apache-2.0), which is also the round-trip oracle. No source of -libaom, dav1d, rav1e, SVT-AV1, ffmpeg or any other third-party encoder or decoder was consulted; -ffmpeg / libdav1d is used only as an external test oracle. diff --git a/crates/av1enc/src/bitw.rs b/crates/av1enc/src/bitw.rs deleted file mode 100644 index e4c8a1e69..000000000 --- a/crates/av1enc/src/bitw.rs +++ /dev/null @@ -1,87 +0,0 @@ -//! MSB-first bit writer for the OBU headers (spec 4.10 `f(n)` descriptors) and `leb128()`. - -#[derive(Default)] -pub(crate) struct BitWriter { - pub buf: Vec, - nbits: usize, -} - -impl BitWriter { - pub fn new() -> Self { - BitWriter::default() - } - - /// `f(n)`: writes the low `n` bits of `v`, most significant first. - pub fn f(&mut self, n: u32, v: u32) { - for i in (0..n).rev() { - self.bit((v >> i) & 1 != 0); - } - } - - pub fn bit(&mut self, b: bool) { - if self.nbits.is_multiple_of(8) { - self.buf.push(0); - } - if b { - let last = self.buf.len() - 1; - self.buf[last] |= 0x80 >> (self.nbits % 8); - } - self.nbits += 1; - } - - /// `byte_alignment()`: zero bits up to the next byte boundary. - pub fn byte_align(&mut self) { - while !self.nbits.is_multiple_of(8) { - self.bit(false); - } - } - - /// `trailing_bits()`: a one bit followed by zero bits up to the byte boundary. - pub fn trailing_bits(&mut self) { - self.bit(true); - self.byte_align(); - } - - pub fn into_bytes(self) -> Vec { - self.buf - } -} - -/// `leb128()` encoding of an OBU size. -pub(crate) fn leb128(mut v: usize, out: &mut Vec) { - loop { - let mut b = (v & 0x7f) as u8; - v >>= 7; - if v != 0 { - b |= 0x80; - } - out.push(b); - if v == 0 { - break; - } - } -} - -/// An OBU with `obu_has_size_field` = 1 and no extension header. -pub(crate) fn obu(obu_type: u8, payload: &[u8], out: &mut Vec) { - out.push((obu_type << 3) | 0x02); - leb128(payload.len(), out); - out.extend_from_slice(payload); -} - -#[cfg(test)] -mod tests { - use super::*; - - #[test] - fn bits_and_leb128() { - let mut w = BitWriter::new(); - w.f(3, 0b101); - w.f(6, 0b110011); - w.trailing_bits(); - assert_eq!(w.into_bytes(), vec![0b1011_1001, 0b1100_0000]); - let mut v = Vec::new(); - leb128(300, &mut v); - assert_eq!(v, vec![0xAC, 0x02]); - } -} diff --git a/crates/av1enc/src/cdf.rs b/crates/av1enc/src/cdf.rs deleted file mode 100644 index 777164d1a..000000000 --- a/crates/av1enc/src/cdf.rs +++ /dev/null @@ -1,172 +0,0 @@ -//! The adaptive CDFs this encoder codes with (the subset of spec 6.8.2 init_non_coeff_cdfs / -//! init_coeff_cdfs that its syntax uses). Layout as in filmcraft-av1: each innermost array -//! holds N cumulative values (the last is 32768) plus the adaptation counter. - -use crate::tables::{ - DEFAULT_ANGLE_DELTA_CDF, DEFAULT_COEFF_BASE_CDF, DEFAULT_COEFF_BASE_EOB_CDF, DEFAULT_COEFF_BR_CDF, DEFAULT_DC_SIGN_CDF, DEFAULT_DRL_MODE_CDF, - DEFAULT_EOB_EXTRA_CDF, DEFAULT_EOB_PT_16_CDF, DEFAULT_EOB_PT_32_CDF, DEFAULT_EOB_PT_64_CDF, DEFAULT_EOB_PT_128_CDF, DEFAULT_EOB_PT_256_CDF, - DEFAULT_EOB_PT_512_CDF, DEFAULT_EOB_PT_1024_CDF, DEFAULT_INTER_TX_TYPE_SET3_CDF, DEFAULT_INTRA_TX_TYPE_SET2_CDF, DEFAULT_IS_INTER_CDF, DEFAULT_MV_BIT_CDF, - DEFAULT_MV_CLASS_CDF, DEFAULT_MV_CLASS0_BIT_CDF, DEFAULT_MV_CLASS0_FR_CDF, DEFAULT_MV_FR_CDF, DEFAULT_MV_JOINT_CDF, DEFAULT_MV_SIGN_CDF, - DEFAULT_NEW_MV_CDF, DEFAULT_PARTITION_W8_CDF, DEFAULT_PARTITION_W16_CDF, DEFAULT_PARTITION_W32_CDF, DEFAULT_PARTITION_W64_CDF, DEFAULT_REF_MV_CDF, - DEFAULT_SINGLE_REF_CDF, DEFAULT_SKIP_CDF, DEFAULT_TXB_SKIP_CDF, DEFAULT_UV_MODE_CFL_ALLOWED_CDF, DEFAULT_UV_MODE_CFL_NOT_ALLOWED_CDF, DEFAULT_Y_MODE_CDF, - DEFAULT_ZERO_MV_CDF, -}; - -#[derive(Clone)] -pub(crate) struct Cdfs { - pub y_mode: [[u16; 14]; 4], - pub uv_mode_cfl_not_allowed: [[u16; 14]; 13], - pub uv_mode_cfl_allowed: [[u16; 15]; 13], - pub angle_delta: [[u16; 8]; 8], - pub partition_w8: [[u16; 5]; 4], - pub partition_w16: [[u16; 11]; 4], - pub partition_w32: [[u16; 11]; 4], - pub partition_w64: [[u16; 11]; 4], - pub skip: [[u16; 3]; 3], - pub is_inter: [[u16; 3]; 4], - pub single_ref: [[[u16; 3]; 6]; 3], - pub new_mv: [[u16; 3]; 6], - pub zero_mv: [[u16; 3]; 2], - pub ref_mv: [[u16; 3]; 6], - pub drl_mode: [[u16; 3]; 3], - pub mv_joint: [u16; 5], - pub mv_class: [[u16; 12]; 2], - pub mv_class0_bit: [[u16; 3]; 2], - pub mv_fr: [[u16; 5]; 2], - pub mv_class0_fr: [[[u16; 5]; 2]; 2], - pub mv_sign: [[u16; 3]; 2], - pub mv_bit: [[[u16; 3]; 10]; 2], - pub intra_tx_type_set2: [[[u16; 6]; 13]; 3], - pub inter_tx_type_set3: [[u16; 3]; 4], - pub txb_skip: [[[u16; 3]; 13]; 5], - pub eob_pt_16: [[[u16; 6]; 2]; 2], - pub eob_pt_32: [[[u16; 7]; 2]; 2], - pub eob_pt_64: [[[u16; 8]; 2]; 2], - pub eob_pt_128: [[[u16; 9]; 2]; 2], - pub eob_pt_256: [[[u16; 10]; 2]; 2], - pub eob_pt_512: [[u16; 11]; 2], - pub eob_pt_1024: [[u16; 12]; 2], - pub eob_extra: [[[[u16; 3]; 9]; 2]; 5], - pub dc_sign: [[[u16; 3]; 3]; 2], - pub coeff_base_eob: [[[[u16; 4]; 4]; 2]; 5], - pub coeff_base: [[[[u16; 5]; 42]; 2]; 5], - pub coeff_br: [[[[u16; 5]; 21]; 2]; 5], -} - -impl Cdfs { - /// init_non_coeff_cdfs( ) followed by init_coeff_cdfs( ) for `base_q_idx`. - pub fn new(base_q_idx: u32) -> Box { - let idx = match base_q_idx { - 0..=20 => 0, - 21..=60 => 1, - 61..=120 => 2, - _ => 3, - }; - Box::new(Cdfs { - y_mode: DEFAULT_Y_MODE_CDF, - uv_mode_cfl_not_allowed: DEFAULT_UV_MODE_CFL_NOT_ALLOWED_CDF, - uv_mode_cfl_allowed: DEFAULT_UV_MODE_CFL_ALLOWED_CDF, - angle_delta: DEFAULT_ANGLE_DELTA_CDF, - partition_w8: DEFAULT_PARTITION_W8_CDF, - partition_w16: DEFAULT_PARTITION_W16_CDF, - partition_w32: DEFAULT_PARTITION_W32_CDF, - partition_w64: DEFAULT_PARTITION_W64_CDF, - skip: DEFAULT_SKIP_CDF, - is_inter: DEFAULT_IS_INTER_CDF, - single_ref: DEFAULT_SINGLE_REF_CDF, - new_mv: DEFAULT_NEW_MV_CDF, - zero_mv: DEFAULT_ZERO_MV_CDF, - ref_mv: DEFAULT_REF_MV_CDF, - drl_mode: DEFAULT_DRL_MODE_CDF, - mv_joint: DEFAULT_MV_JOINT_CDF, - mv_class: DEFAULT_MV_CLASS_CDF, - mv_class0_bit: [DEFAULT_MV_CLASS0_BIT_CDF; 2], - mv_fr: DEFAULT_MV_FR_CDF, - mv_class0_fr: DEFAULT_MV_CLASS0_FR_CDF, - mv_sign: [DEFAULT_MV_SIGN_CDF; 2], - mv_bit: [DEFAULT_MV_BIT_CDF; 2], - intra_tx_type_set2: DEFAULT_INTRA_TX_TYPE_SET2_CDF, - inter_tx_type_set3: DEFAULT_INTER_TX_TYPE_SET3_CDF, - txb_skip: DEFAULT_TXB_SKIP_CDF[idx], - eob_pt_16: DEFAULT_EOB_PT_16_CDF[idx], - eob_pt_32: DEFAULT_EOB_PT_32_CDF[idx], - eob_pt_64: DEFAULT_EOB_PT_64_CDF[idx], - eob_pt_128: DEFAULT_EOB_PT_128_CDF[idx], - eob_pt_256: DEFAULT_EOB_PT_256_CDF[idx], - eob_pt_512: DEFAULT_EOB_PT_512_CDF[idx], - eob_pt_1024: DEFAULT_EOB_PT_1024_CDF[idx], - eob_extra: DEFAULT_EOB_EXTRA_CDF[idx], - dc_sign: DEFAULT_DC_SIGN_CDF[idx], - coeff_base_eob: DEFAULT_COEFF_BASE_EOB_CDF[idx], - coeff_base: DEFAULT_COEFF_BASE_CDF[idx], - coeff_br: DEFAULT_COEFF_BR_CDF[idx], - }) - } -} - -impl Cdfs { - /// The counter reset of load_cdfs( ): the last entry of every CDF array is set to 0. - pub fn clear_counts(&mut self) { - macro_rules! clr { - ($($f:ident),*) => { $( self.$f.clear_count(); )* }; - } - clr!( - y_mode, - uv_mode_cfl_not_allowed, - uv_mode_cfl_allowed, - angle_delta, - partition_w8, - partition_w16, - partition_w32, - partition_w64, - skip, - is_inter, - single_ref, - new_mv, - zero_mv, - ref_mv, - drl_mode, - mv_joint, - mv_class, - mv_class0_bit, - mv_fr, - mv_class0_fr, - mv_sign, - mv_bit, - intra_tx_type_set2, - inter_tx_type_set3, - txb_skip, - eob_pt_16, - eob_pt_32, - eob_pt_64, - eob_pt_128, - eob_pt_256, - eob_pt_512, - eob_pt_1024, - eob_extra, - dc_sign, - coeff_base_eob, - coeff_base, - coeff_br - ); - } -} - -/// Zeroes the adaptation counter (last element) of every innermost CDF array. -trait ClearCount { - fn clear_count(&mut self); -} - -impl ClearCount for [u16; N] { - fn clear_count(&mut self) { - self[N - 1] = 0; - } -} - -impl ClearCount for [T; M] { - fn clear_count(&mut self) { - for e in self.iter_mut() { - e.clear_count(); - } - } -} diff --git a/crates/av1enc/src/decide.rs b/crates/av1enc/src/decide.rs deleted file mode 100644 index b06852809..000000000 --- a/crates/av1enc/src/decide.rs +++ /dev/null @@ -1,1273 +0,0 @@ -//! Mode decision and reconstruction (not normative): partitions, intra modes, motion vectors, -//! transform coefficients. Every prediction and reconstruction step uses the normative -//! processes, so the reconstruction built here is exactly what a decoder produces; the syntax -//! is written afterwards by `writer.rs`. - -use crate::pred::{INTRA_MODES_USED, InterBlock, IntraEdge, Plane, edges, half_planes, predict_inter, predict_intra}; -use crate::tables::{ - AC_QLOOKUP, BLOCK_8X8, BLOCK_64X64, DC_PRED, DC_QLOOKUP, DCT_DCT, DEFAULT_SCAN_4X4, DEFAULT_SCAN_8X8, DEFAULT_SCAN_16X16, DEFAULT_SCAN_32X32, MODE_TO_TXFM, - NEWMV, NUM_4X4_BLOCKS_WIDE, PARTITION_SPLIT, PARTITION_SUBSIZE, TX_4X4, TX_8X8, TX_16X16, TX_32X32, TX_64X64, TX_SET_DCTONLY, TX_SET_INTER_3, - TX_SET_INTRA_2, TX_SIZE_SQR_UP, TX_TYPE_IN_SET_INTER, TX_TYPE_IN_SET_INTRA, -}; -use crate::transform::{forward_2d, inverse_2d}; - -/// Decisions for one coded block. -#[derive(Clone)] -pub(crate) struct Leaf { - pub bsize: usize, - pub is_inter: bool, - pub y_mode: usize, - pub uv_mode: usize, - pub tx_type_y: usize, - /// Motion vector [row, col] in 1/8 luma samples (inter blocks). - pub mv: [i32; 2], - pub skip: bool, - /// Quantised coefficients of the single transform block of each plane (raster order). - pub coefs: [Vec; 3], -} - -pub(crate) enum Node { - Out, - Leaf(Box), - Split(Box<[Node; 4]>), -} - -/// A reconstructed frame kept as a reference. -pub(crate) struct RefFrame { - pub planes: [Plane; 3], - /// Motion vectors chosen for the frame (per 4x4), used as search candidates. - pub mvs: Vec<[i32; 2]>, - /// The frame's loop filter level. - pub lf_level: u32, - /// The CDFs saved at the end of the frame (loaded by the next frame). - pub cdfs: Box, -} - -pub(crate) struct FrameGeom { - pub width: usize, - pub height: usize, - pub mi_cols: usize, - pub mi_rows: usize, - pub sb_cols: usize, - pub sb_rows: usize, -} - -impl FrameGeom { - pub fn new(width: usize, height: usize) -> FrameGeom { - let mi_cols = 2 * width.div_ceil(8); - let mi_rows = 2 * height.div_ceil(8); - FrameGeom { width, height, mi_cols, mi_rows, sb_cols: mi_cols.div_ceil(16), sb_rows: mi_rows.div_ceil(16) } - } - /// Padded plane size (whole superblocks). - pub fn padded(&self, plane: usize) -> (usize, usize) { - let s = (plane > 0) as usize; - ((self.sb_cols * 64) >> s, (self.sb_rows * 64) >> s) - } - /// Visible plane size. - pub fn visible(&self, plane: usize) -> (usize, usize) { - if plane == 0 { (self.width, self.height) } else { (self.width.div_ceil(2), self.height.div_ceil(2)) } - } -} - -pub(crate) fn tx_size_for(log2: u32) -> usize { - match log2 { - 2 => TX_4X4, - 3 => TX_8X8, - 4 => TX_16X16, - 5 => TX_32X32, - _ => TX_64X64, - } -} - -/// get_tx_set( ) for the square sizes used here with reduced_tx_set = 1. -pub(crate) fn tx_set(tx_sz: usize, is_inter: bool) -> usize { - let sqr_up = TX_SIZE_SQR_UP[tx_sz] as usize; - if sqr_up > TX_32X32 { - return TX_SET_DCTONLY; - } - if is_inter { - TX_SET_INTER_3 - } else if sqr_up == TX_32X32 { - TX_SET_DCTONLY - } else { - TX_SET_INTRA_2 - } -} - -pub(crate) fn tx_in_set(set: usize, is_inter: bool, tx_type: usize) -> bool { - if is_inter { TX_TYPE_IN_SET_INTER[set][tx_type] == 1 } else { TX_TYPE_IN_SET_INTRA[set][tx_type] == 1 } -} - -/// The chroma transform type of an intra block (compute_tx_type for plane > 0). -pub(crate) fn intra_uv_tx_type(uv_mode: usize, uv_tx: usize) -> usize { - let t = MODE_TO_TXFM[uv_mode] as usize; - if tx_in_set(tx_set(uv_tx, false), false, t) { t } else { DCT_DCT } -} - -/// Geometry and coding mode of one square transform block. -#[derive(Clone, Copy)] -struct TransformBlock { - plane: usize, - x: usize, - y: usize, - log2: u32, - tx_type: usize, - intra: bool, -} - -struct TxResult { - levels: Vec, - nonzero: bool, - dist: f64, - bits: f64, -} - -/// The output and scratch buffers of one reconstruction, each `n * n` entries long. -struct ReconBuffers<'a> { - deq: &'a mut [i32], - resid: &'a mut [i32], - recon: &'a mut [u16], -} - -/// Outcome of trying one transform block; the levels and the reconstruction stay in `TxScratch`. -#[derive(Clone, Copy)] -struct TxEval { - nonzero: bool, - dist: f64, - bits: f64, -} - -/// Buffers of the transform path, sized for the largest coded transform block (32x32). Only the -/// first `n * n` entries are used; they are reused across candidates instead of being allocated. -struct TxScratch { - res: [i32; 1024], - coef: [f32; 1024], - levels: [i32; 1024], - deq: [i32; 1024], - resid: [i32; 1024], - recon: [u16; 1024], -} - -impl Default for TxScratch { - fn default() -> Self { - TxScratch { res: [0; 1024], coef: [0.0; 1024], levels: [0; 1024], deq: [0; 1024], resid: [0; 1024], recon: [0; 1024] } - } -} - -/// Saved encoder state of a block area (reconstruction, BlockDecoded flags). -struct Snapshot { - planes: [Vec; 3], - /// BlockDecoded flags of the block area only (the only flags coding inside it can change). - decoded: [[[bool; 16]; 16]; 3], -} - -pub(crate) struct Decider<'a> { - pub g: &'a FrameGeom, - pub bd: u32, - pub intra_only: bool, - pub src: &'a [Plane; 3], - pub rec: [Plane; 3], - pub refr: Option<&'a RefFrame>, - decoded: Box<[[[bool; 35]; 35]; 3]>, - /// Decision motion vectors per 4x4 (search candidates). - pub mvs: Vec<[i32; 2]>, - dc_q: f32, - ac_q: f32, - dc_qi: i64, - ac_qi: i64, - /// Smallest magnitude that quantises to a non-zero level, `[dc, ac][intra]`. - dead_zone: [[f32; 2]; 2], - lambda: f64, - lambda_sad: f64, - /// Half-sample interpolations of the reference luma (visible area): (8, 0), (0, 8), (8, 8). - half: [Vec; 3], - /// Transform buffers, taken out while a block is being tried (see `code_tx`). - scratch: Option>, -} - -const MODE_BITS_INTRA: f64 = 3.0; -/// Dead-zone quantiser rounding offsets (below one half) for inter and intra blocks. -const INTER_RND: f32 = 0.30; -const INTRA_RND: f32 = 0.38; - -impl<'a> Decider<'a> { - pub fn new(g: &'a FrameGeom, bd: u32, qidx: u8, intra_only: bool, src: &'a [Plane; 3], refr: Option<&'a RefFrame>) -> Self { - let bdi = ((bd - 8) >> 1) as usize; - let dc_qi = DC_QLOOKUP[bdi][qidx as usize] as i64; - let ac_qi = AC_QLOOKUP[bdi][qidx as usize] as i64; - // Lagrangian multiplier in squared-sample units of the coded bit depth. - let step = ac_qi as f64 / 8.0; - let lambda = 0.25 * step * step; - let rec = std::array::from_fn(|p| { - let (w, h) = g.padded(p); - Plane::new(w, h) - }); - Decider { - g, - bd, - intra_only, - src, - rec, - refr, - decoded: Box::new([[[false; 35]; 35]; 3]), - mvs: vec![[0, 0]; g.mi_cols * g.mi_rows], - dc_q: dc_qi as f32, - ac_q: ac_qi as f32, - dc_qi, - ac_qi, - dead_zone: [dc_qi, ac_qi].map(|q| [INTER_RND, INTRA_RND].map(|rnd| zero_threshold(q as f32, rnd))), - lambda, - lambda_sad: lambda.sqrt(), - half: match refr { - Some(rf) => half_planes(&rf.planes[0], g.width, g.height, bd), - None => Default::default(), - }, - scratch: None, - } - } - - /// Decides one 64x64 superblock (reconstruction is written to `rec`). - pub fn superblock(&mut self, r: usize, c: usize) -> Node { - // clear_block_decoded_flags( r, c, sbSize4 ) - for plane in 0..3 { - let s = (plane > 0) as usize; - let sb_w4 = (self.g.mi_cols as isize - c as isize) >> s; - let sb_h4 = (self.g.mi_rows as isize - r as isize) >> s; - let bd = &mut self.decoded[plane]; - for y in -1..=((16 >> s) as isize) { - for x in -1..=((16 >> s) as isize) { - let v = if y < 0 && x < sb_w4 { true } else { x < 0 && y < sb_h4 }; - bd[(y + 1) as usize][(x + 1) as usize] = v; - } - } - bd[(16 >> s) + 1][0] = false; - } - self.partition(r, c, BLOCK_64X64, None).0 - } - - fn decoded_at(&self, plane: usize, y: isize, x: isize) -> bool { - let (yy, xx) = (y + 1, x + 1); - if yy < 0 || xx < 0 || yy > 34 || xx > 34 { - return false; - } - self.decoded[plane][yy as usize][xx as usize] - } - - /// Marks the transform block of `plane` covering the block at (r, c) as decoded. - fn mark_decoded(&mut self, plane: usize, r: usize, c: usize, n4: usize) { - let s = (plane > 0) as usize; - let by = (r & 15) >> s; - let bx = (c & 15) >> s; - let step = (n4 >> s).max(1); - for i in 0..step { - for j in 0..step { - if by + i + 1 < 35 && bx + j + 1 < 35 { - self.decoded[plane][by + i + 1][bx + j + 1] = true; - } - } - } - } - - /// The BlockDecoded cells `mark_decoded` can set for blocks inside (r, c, n4): rows and - /// columns `by + 1 .. by + 1 + step` / `bx + 1 ..` of `decoded[plane]`. - fn decoded_region(plane: usize, r: usize, c: usize, n4: usize) -> (usize, usize, usize) { - let s = (plane > 0) as usize; - ((r & 15) >> s, (c & 15) >> s, (n4 >> s).max(1)) - } - - fn snapshot(&self, r: usize, c: usize, n4: usize) -> Snapshot { - let planes = std::array::from_fn(|p| { - let s = (p > 0) as usize; - let (x0, y0, n) = ((c * 4) >> s, (r * 4) >> s, (n4 * 4) >> s); - let pl = &self.rec[p]; - let mut v = Vec::with_capacity(n * n); - for y in y0..y0 + n { - v.extend_from_slice(&pl.data[y * pl.stride + x0..y * pl.stride + x0 + n]); - } - v - }); - let mut decoded = [[[false; 16]; 16]; 3]; - for (p, d) in decoded.iter_mut().enumerate() { - let (by, bx, step) = Self::decoded_region(p, r, c, n4); - for i in 0..step { - d[i][..step].copy_from_slice(&self.decoded[p][by + i + 1][bx + 1..bx + 1 + step]); - } - } - Snapshot { planes, decoded } - } - - fn restore(&mut self, snap: &Snapshot, r: usize, c: usize, n4: usize) { - for p in 0..3 { - let s = (p > 0) as usize; - let (x0, y0, n) = ((c * 4) >> s, (r * 4) >> s, (n4 * 4) >> s); - let pl = &mut self.rec[p]; - for (i, y) in (y0..y0 + n).enumerate() { - pl.data[y * pl.stride + x0..y * pl.stride + x0 + n].copy_from_slice(&snap.planes[p][i * n..i * n + n]); - } - } - for p in 0..3 { - let (by, bx, step) = Self::decoded_region(p, r, c, n4); - for i in 0..step { - self.decoded[p][by + i + 1][bx + 1..bx + 1 + step].copy_from_slice(&snap.decoded[p][i][..step]); - } - } - } - - fn set_mvs(&mut self, r: usize, c: usize, n4: usize, mv: [i32; 2]) { - for y in r..(r + n4).min(self.g.mi_rows) { - for x in c..(c + n4).min(self.g.mi_cols) { - self.mvs[y * self.g.mi_cols + x] = mv; - } - } - } - - fn partition(&mut self, r: usize, c: usize, bsize: usize, parent_mv: Option<[i32; 2]>) -> (Node, f64) { - let g = self.g; - if r >= g.mi_rows || c >= g.mi_cols { - return (Node::Out, 0.0); - } - let n4 = NUM_4X4_BLOCKS_WIDE[bsize] as usize; - let half = n4 >> 1; - let has_rows = r + half < g.mi_rows; - let has_cols = c + half < g.mi_cols; - let can_none = has_rows && has_cols && (bsize != BLOCK_64X64 || !self.intra_only); - let can_split = bsize > BLOCK_8X8; - let sub = PARTITION_SUBSIZE[PARTITION_SPLIT][bsize] as usize; - if !can_none { - return self.split(r, c, sub, half, parent_mv); - } - let before = self.snapshot(r, c, n4); - let (leaf, cost_none) = self.leaf(r, c, bsize, parent_mv); - let cost_none = cost_none + self.lambda * 1.0; - if !can_split { - return (Node::Leaf(Box::new(leaf)), cost_none); - } - // a cheap skip block rarely gains from splitting - if leaf.skip && cost_none < self.lambda * 8.0 { - return (Node::Leaf(Box::new(leaf)), cost_none); - } - let after_none = self.snapshot(r, c, n4); - self.restore(&before, r, c, n4); - let pm = if leaf.is_inter { Some(leaf.mv) } else { parent_mv }; - let (node, cost_split) = self.split(r, c, sub, half, pm); - let cost_split = cost_split + self.lambda * 2.0; - if cost_split < cost_none { - (node, cost_split) - } else { - self.restore(&after_none, r, c, n4); - self.set_mvs(r, c, n4, if leaf.is_inter { leaf.mv } else { [0, 0] }); - (Node::Leaf(Box::new(leaf)), cost_none) - } - } - - fn split(&mut self, r: usize, c: usize, sub: usize, half: usize, pm: Option<[i32; 2]>) -> (Node, f64) { - let (n0, c0) = self.partition(r, c, sub, pm); - let (n1, c1) = self.partition(r, c + half, sub, pm); - let (n2, c2) = self.partition(r + half, c, sub, pm); - let (n3, c3) = self.partition(r + half, c + half, sub, pm); - (Node::Split(Box::new([n0, n1, n2, n3])), c0 + c1 + c2 + c3) - } - - /// Best coding of the block as a single (NONE partition) block. - fn leaf(&mut self, r: usize, c: usize, bsize: usize, parent_mv: Option<[i32; 2]>) -> (Leaf, f64) { - let n4 = NUM_4X4_BLOCKS_WIDE[bsize] as usize; - if self.intra_only { - let res = self.intra_leaf(r, c, bsize); - self.set_mvs(r, c, n4, [0, 0]); - return res; - } - let before = self.snapshot(r, c, n4); - let (inter, cost_inter) = self.inter_leaf(r, c, bsize, parent_mv); - if bsize == BLOCK_64X64 || inter.skip { - self.set_mvs(r, c, n4, inter.mv); - return (inter, cost_inter); - } - let after_inter = self.snapshot(r, c, n4); - self.restore(&before, r, c, n4); - let (intra, cost_intra) = self.intra_leaf(r, c, bsize); - if cost_intra < cost_inter { - self.set_mvs(r, c, n4, [0, 0]); - (intra, cost_intra) - } else { - self.restore(&after_inter, r, c, n4); - self.set_mvs(r, c, n4, inter.mv); - (inter, cost_inter) - } - } - - fn intra_leaf(&mut self, r: usize, c: usize, bsize: usize) -> (Leaf, f64) { - let n4 = NUM_4X4_BLOCKS_WIDE[bsize] as usize; - let log2 = (n4 * 4).trailing_zeros(); - let (sbr, sbc) = ((r & 15) as isize, (c & 15) as isize); - let bd = self.bd; - let mut pred = [0u16; 32 * 32]; - let mut best_pred = [0u16; 32 * 32]; - let mut best_levels = [0i32; 32 * 32]; - let mut best_recon = [0u16; 32 * 32]; - let n = 1usize << log2; - // luma - let e = IntraEdge { - x: c * 4, - y: r * 4, - have_left: c > 0, - have_above: r > 0, - have_above_right: self.decoded_at(0, sbr - 1, sbc + n4 as isize), - have_below_left: self.decoded_at(0, sbr + n4 as isize, sbc - 1), - log2, - max_x: (self.g.mi_cols * 4) as i32 - 1, - max_y: (self.g.mi_rows * 4) as i32 - 1, - bit_depth: bd, - }; - let ed = edges(&self.rec[0], &e); - // modes ranked by SATD, then a rate-distortion choice of mode and transform type among - // the best three - let mut ranked: [(f64, usize); INTRA_MODES_USED.len()] = INTRA_MODES_USED.map(|mode| { - predict_intra(&ed, &e, mode, &mut pred); - let sad = self.satd(0, c * 4, r * 4, n, &pred[..n * n]) as f64; - (sad + self.lambda_sad * if mode == DC_PRED { 1.0 } else { MODE_BITS_INTRA }, mode) - }); - ranked.sort_by(|a, b| a.0.total_cmp(&b.0)); - let tx_y = tx_size_for(log2); - let mut best: Option<(f64, usize, usize, TxEval, bool)> = None; - let mut sc = self.scratch.take().unwrap_or_default(); - for &(_, mode) in ranked.iter().take(3) { - predict_intra(&ed, &e, mode, &mut pred); - let mut types = [DCT_DCT; 2]; - let mut n_types = 1; - let alt = MODE_TO_TXFM[mode] as usize; - if alt != DCT_DCT && tx_in_set(tx_set(tx_y, false), false, alt) { - types[1] = alt; - n_types = 2; - } - for &t in &types[..n_types] { - let (res, has_recon) = self.try_tx(&mut sc, TransformBlock { plane: 0, x: c * 4, y: r * 4, log2, tx_type: t, intra: true }, &pred[..n * n]); - let type_bits = if !res.nonzero || tx_set(tx_y, false) == TX_SET_DCTONLY { - 0.0 - } else if t == DCT_DCT { - 1.0 - } else { - 2.0 - }; - let mode_bits = if mode == DC_PRED { 1.0 } else { MODE_BITS_INTRA }; - let cost = res.dist + self.lambda * (res.bits + type_bits + mode_bits); - if best.as_ref().is_none_or(|b| cost < b.0) { - best_pred[..n * n].copy_from_slice(&pred[..n * n]); - best_levels[..n * n].copy_from_slice(&sc.levels[..n * n]); - if has_recon { - best_recon[..n * n].copy_from_slice(&sc.recon[..n * n]); - } - best = Some((cost, mode, t, res, has_recon)); - } - } - } - self.scratch = Some(sc); - // Every ranked mode tries DCT_DCT, so `best` is always set; without a candidate the block - // can't be coded intra (an infinite cost keeps it from being chosen). - let Some((_, y_mode, tx_type_y, ty, has_recon)) = best else { - let leaf = - Leaf { bsize, is_inter: false, y_mode: DC_PRED, uv_mode: DC_PRED, tx_type_y: DCT_DCT, mv: [0, 0], skip: true, coefs: Default::default() }; - return (leaf, f64::INFINITY); - }; - self.put(0, c * 4, r * 4, n, if has_recon { &best_recon[..n * n] } else { &best_pred[..n * n] }); - let ty = TxResult { levels: best_levels[..n * n].to_vec(), nonzero: ty.nonzero, dist: ty.dist, bits: ty.bits }; - let tx_type_y = if ty.nonzero { tx_type_y } else { DCT_DCT }; - self.mark_decoded(0, r, c, n4); - // chroma (one mode for both planes) - let cn = n >> 1; - let clog2 = log2 - 1; - let (csbr, csbc) = (sbr >> 1, sbc >> 1); - let ce: [IntraEdge; 2] = std::array::from_fn(|i| IntraEdge { - x: c * 2, - y: r * 2, - have_left: c > 0, - have_above: r > 0, - have_above_right: self.decoded_at(1 + i, csbr - 1, csbc + (n4 >> 1) as isize), - have_below_left: self.decoded_at(1 + i, csbr + (n4 >> 1) as isize, csbc - 1), - log2: clog2, - max_x: (self.g.mi_cols * 2) as i32 - 1, - max_y: (self.g.mi_rows * 2) as i32 - 1, - bit_depth: bd, - }); - let ced = [edges(&self.rec[1], &ce[0]), edges(&self.rec[2], &ce[1])]; - let mut best_uv = (f64::MAX, DC_PRED); - for &mode in INTRA_MODES_USED.iter() { - let mut sad = 0u64; - for i in 0..2 { - predict_intra(&ced[i], &ce[i], mode, &mut pred); - sad += self.satd(1 + i, c * 2, r * 2, cn, &pred[..cn * cn]); - } - let cost = sad as f64 + self.lambda_sad * if mode == DC_PRED { 1.0 } else { 2.5 }; - if cost < best_uv.0 { - best_uv = (cost, mode); - } - } - let uv_mode = best_uv.1; - let uv_tx = tx_size_for(clog2); - let uv_type = intra_uv_tx_type(uv_mode, uv_tx); - let mut code_uv = |i: usize| { - predict_intra(&ced[i], &ce[i], uv_mode, &mut pred); - let t = self.code_tx(TransformBlock { plane: 1 + i, x: c * 2, y: r * 2, log2: clog2, tx_type: uv_type, intra: true }, &pred[..cn * cn]); - self.mark_decoded(1 + i, r, c, n4); - t - }; - let u = code_uv(0); - let v = code_uv(1); - let skip = !ty.nonzero && !u.nonzero && !v.nonzero; - let dist = ty.dist + u.dist + v.dist; - let bits = if skip { 1.0 } else { ty.bits + u.bits + v.bits + 1.0 } + MODE_BITS_INTRA + 2.0; - let leaf = Leaf { bsize, is_inter: false, y_mode, uv_mode, tx_type_y, mv: [0, 0], skip, coefs: [ty.levels, u.levels, v.levels] }; - (leaf, dist + self.lambda * bits) - } - - fn inter_leaf(&mut self, r: usize, c: usize, bsize: usize, parent_mv: Option<[i32; 2]>) -> (Leaf, f64) { - let n4 = NUM_4X4_BLOCKS_WIDE[bsize] as usize; - let n = n4 * 4; - let log2 = n.trailing_zeros(); - // Inter blocks are only decided with a reference frame; without one, code intra. - let Some(refr) = self.refr else { return self.intra_leaf(r, c, bsize) }; - let (mv, mv_bits) = self.motion_search(r, c, n, parent_mv); - let mut pred = vec![0u16; n * n]; - let mut tx = Vec::with_capacity(3); - let mut dist = 0.0; - for p in 0..3 { - let s = (p > 0) as usize; - let (vw, vh) = self.g.visible(p); - let pn = n >> s; - let (x, y) = ((c * 4) >> s, (r * 4) >> s); - predict_inter( - &refr.planes[p], - &InterBlock { last_x: vw as i32 - 1, last_y: vh as i32 - 1, x, y, width: pn, height: pn, mv, subsampling: s, bit_depth: self.bd }, - &mut pred[..pn * pn], - ); - if bsize == BLOCK_64X64 { - // coded as a skip block: the prediction is the reconstruction - dist += self.sse(p, x, y, pn, &pred[..pn * pn]); - self.put(p, x, y, pn, &pred[..pn * pn]); - } else { - let t = self.code_tx(TransformBlock { plane: p, x, y, log2: log2 - s as u32, tx_type: DCT_DCT, intra: false }, &pred[..pn * pn]); - dist += t.dist; - tx.push(t); - } - self.mark_decoded(p, r, c, n4); - } - let mode_bits = if mv == [0, 0] { 1.5 } else { 2.0 + mv_bits }; - if bsize == BLOCK_64X64 { - let leaf = Leaf { bsize, is_inter: true, y_mode: NEWMV, uv_mode: DC_PRED, tx_type_y: DCT_DCT, mv, skip: true, coefs: Default::default() }; - return (leaf, dist + self.lambda * (mode_bits + 1.0)); - } - let skip = tx.iter().all(|t| !t.nonzero); - let bits = mode_bits + if skip { 1.0 } else { 1.0 + tx.iter().map(|t| t.bits).sum::() }; - let [y, u, v]: [TxResult; 3] = match tx.try_into() { - Ok(planes) => planes, - // One transform block per plane was coded above. - Err(_) => { - return ( - Leaf { bsize, is_inter: true, y_mode: NEWMV, uv_mode: DC_PRED, tx_type_y: DCT_DCT, mv, skip: true, coefs: Default::default() }, - f64::INFINITY, - ); - } - }; - let leaf = Leaf { bsize, is_inter: true, y_mode: NEWMV, uv_mode: DC_PRED, tx_type_y: DCT_DCT, mv, skip, coefs: [y.levels, u.levels, v.levels] }; - (leaf, dist + self.lambda * bits) - } - - /// Full-sample motion search: candidate vectors then a shrinking diamond search. - /// Returns the vector and an estimate of its coding cost in bits. - fn motion_search(&self, r: usize, c: usize, n: usize, parent_mv: Option<[i32; 2]>) -> ([i32; 2], f64) { - let g = self.g; - let Some(refr) = self.refr else { return ([0, 0], 0.0) }; - let mi = |rr: usize, cc: usize| self.mvs[rr * g.mi_cols + cc]; - let mut pred_mv = [0, 0]; - let mut cands: Vec<[i32; 2]> = vec![[0, 0]]; - if c > 0 { - pred_mv = mi(r, c - 1); - cands.push(pred_mv); - } - if r > 0 { - cands.push(mi(r - 1, c)); - let cr = c + n / 4; - if cr < g.mi_cols { - cands.push(mi(r - 1, cr)); - } - } - if let Some(p) = parent_mv { - cands.push(p); - } - cands.push(refr.mvs[r * g.mi_cols + c]); - let (x0, y0) = ((c * 4) as i32, (r * 4) as i32); - let (w, h) = (g.width as i32, g.height as i32); - // keep the referenced block within 64 samples of the picture - let lim_x = (-x0 - n as i32 - 64, w - x0 + 64); - let lim_y = (-y0 - n as i32 - 64, h - y0 + 64); - let valid = |m: [i32; 2]| { - let (dy, dx) = (m[0] >> 3, m[1] >> 3); - dx >= lim_x.0 && dx <= lim_x.1 && dy >= lim_y.0 && dy <= lim_y.1 - }; - let lg = log2_table(); - let mv_bits = |m: [i32; 2]| -> f64 { - let comp = |d: i32| if d == 0 { 0.5 } else { 2.0 + 2.0 * log2_of(lg, (d.abs() / 8) as usize + 1) }; - comp(m[0] - pred_mv[0]) + comp(m[1] - pred_mv[1]) - }; - // `bound` is the cost to beat: the SAD stops early once the cost cannot be below it (the - // callers only accept a strictly lower cost, so the result is unchanged) - let cost = |m: [i32; 2], bound: f64| -> f64 { - let lb = self.lambda_sad * mv_bits(m); - self.sad_ref(&refr.planes[0], [x0 + (m[1] >> 3), y0 + (m[0] >> 3)], [c * 4, r * 4], n, [lb, bound]) as f64 + lb - }; - // positions already evaluated cannot beat `best` again (costs are deterministic) - let mut seen: Vec<[i32; 2]> = Vec::with_capacity(64); - let mut best = ([0, 0], f64::MAX); - for &m in &cands { - let m = [m[0] & !7, m[1] & !7]; - if !valid(m) || seen.contains(&m) { - continue; - } - seen.push(m); - let cst = cost(m, best.1); - if cst < best.1 { - best = (m, cst); - } - } - let mut step = 8 * if n >= 32 { - 16 - } else if n == 16 { - 8 - } else { - 4 - }; - while step >= 8 { - let mut moved = true; - let mut iters = 0; - while moved && iters < 16 { - moved = false; - iters += 1; - let center = best.0; - for d in [[-step, 0], [step, 0], [0, -step], [0, step]] { - let m = [center[0] + d[0], center[1] + d[1]]; - if !valid(m) || seen.contains(&m) { - continue; - } - seen.push(m); - let cst = cost(m, best.1); - if cst < best.1 { - best = (m, cst); - moved = true; - } - } - } - step >>= 1; - } - // half- then quarter-sample refinement (plus the exact neighbour candidates) - let mut buf = vec![0u16; n * n]; - let sub_cost = |m: [i32; 2], buf: &mut Vec, bound: f64| -> f64 { - self.search_block(m, c * 4, r * 4, n, buf); - let lb = self.lambda_sad * mv_bits(m); - self.sad_bounded(0, [c * 4, r * 4], n, buf, [lb, bound]) as f64 + lb - }; - let mut seen_sub: Vec<[i32; 2]> = Vec::with_capacity(24); - for m in cands { - if m[0] & 7 != 0 || m[1] & 7 != 0 { - let m = [m[0] & !1, m[1] & !1]; - if valid(m) && !seen_sub.contains(&m) { - seen_sub.push(m); - let cst = sub_cost(m, &mut buf, best.1); - if cst < best.1 { - best = (m, cst); - } - } - } - } - for step in [4, 2] { - let center = best.0; - for d in [[-1, -1], [-1, 0], [-1, 1], [0, -1], [0, 1], [1, -1], [1, 0], [1, 1]] { - let m = [center[0] + d[0] * step, center[1] + d[1] * step]; - if !valid(m) || seen_sub.contains(&m) { - continue; - } - seen_sub.push(m); - let cst = sub_cost(m, &mut buf, best.1); - if cst < best.1 { - best = (m, cst); - } - } - } - (best.0, mv_bits(best.0)) - } - - /// Approximate luma prediction for motion search: exact at whole and half-sample - /// positions (precomputed planes), averaged neighbours at quarter-sample positions. - fn search_block(&self, mv: [i32; 2], x: usize, y: usize, n: usize, out: &mut [u16]) { - let Some(refr) = self.refr else { return }; - let (w, h) = (self.g.width as i32, self.g.height as i32); - // per axis: up to two (integer offset, half) taps - let axis = |v: i32| -> ([(i32, bool); 2], usize) { - let p = 2 * v; // 1/16 units - let (i, f) = (p >> 4, p & 15); - match f { - 0 => ([(i, false), (i, false)], 1), - 8 => ([(i, true), (i, true)], 1), - 4 => ([(i, false), (i, true)], 2), - _ => ([(i, true), (i + 1, false)], 2), - } - }; - let (ax, nx) = axis(mv[1]); - let (ay, ny) = axis(mv[0]); - let taps = nx.max(ny); - let fetch = |k: usize, xx: i32, yy: i32| -> u16 { - let (ox, hx) = ax[k.min(nx - 1)]; - let (oy, hy) = ay[k.min(ny - 1)]; - let sx = (xx + ox).clamp(0, w - 1) as usize; - let sy = (yy + oy).clamp(0, h - 1) as usize; - match (hx, hy) { - (false, false) => refr.planes[0].at(sx, sy), - (true, false) => self.half[0][sy * w as usize + sx], - (false, true) => self.half[1][sy * w as usize + sx], - (true, true) => self.half[2][sy * w as usize + sx], - } - }; - let vw = n.min(self.g.width.saturating_sub(x)); - let vh = n.min(self.g.height.saturating_sub(y)); - // fast path: every tap inside the picture - let (x0, y0) = (x as i32, y as i32); - let inside = (0..taps).all(|k| { - let (ox, _) = ax[k.min(nx - 1)]; - let (oy, _) = ay[k.min(ny - 1)]; - x0 + ox >= 0 && y0 + oy >= 0 && x0 + ox + vw as i32 <= w && y0 + oy + vh as i32 <= h - }); - if inside { - let src = |k: usize| -> (&[u16], usize, usize) { - let (ox, hx) = ax[k.min(nx - 1)]; - let (oy, hy) = ay[k.min(ny - 1)]; - let (sx, sy) = ((x0 + ox) as usize, (y0 + oy) as usize); - match (hx, hy) { - (false, false) => (&refr.planes[0].data, refr.planes[0].stride, sy * refr.planes[0].stride + sx), - (true, false) => (&self.half[0], w as usize, sy * w as usize + sx), - (false, true) => (&self.half[1], w as usize, sy * w as usize + sx), - (true, true) => (&self.half[2], w as usize, sy * w as usize + sx), - } - }; - let (a, sa, oa) = src(0); - if taps == 1 { - for i in 0..vh { - out[i * n..i * n + vw].copy_from_slice(&a[oa + i * sa..oa + i * sa + vw]); - } - } else { - let (b, sb, ob) = src(1); - for i in 0..vh { - let (ra, rb) = (&a[oa + i * sa..oa + i * sa + vw], &b[ob + i * sb..ob + i * sb + vw]); - for (o, (&p, &q)) in out[i * n..i * n + vw].iter_mut().zip(ra.iter().zip(rb)) { - *o = ((p as u32 + q as u32 + 1) >> 1) as u16; - } - } - } - return; - } - for i in 0..vh { - for j in 0..vw { - let (xx, yy) = (x0 + j as i32, y0 + i as i32); - out[i * n + j] = if taps == 1 { fetch(0, xx, yy) } else { ((fetch(0, xx, yy) as u32 + fetch(1, xx, yy) as u32 + 1) >> 1) as u16 }; - } - } - } - - /// SAD of the source block at (x, y) (luma) against the reference block at (rx, ry). - /// Stops after a row once `sad + lb >= bound` (the cost can no longer win). - fn sad_ref(&self, refp: &Plane, [rx, ry]: [i32; 2], [x, y]: [usize; 2], n: usize, [lb, bound]: [f64; 2]) -> u64 { - let (vw, vh) = (self.g.width, self.g.height); - let w = n.min(vw.saturating_sub(x)); - let h = n.min(vh.saturating_sub(y)); - let src = &self.src[0]; - let (lx, ly) = (vw as i32 - 1, vh as i32 - 1); - let mut sad = 0u64; - let inside = rx >= 0 && ry >= 0 && rx + w as i32 - 1 <= lx && ry + h as i32 - 1 <= ly; - for i in 0..h { - let s = &src.row(y + i)[x..x + w]; - let rr = refp.row((ry + i as i32).clamp(0, ly) as usize); - if inside { - let rrow = &rr[rx as usize..rx as usize + w]; - sad += s.iter().zip(rrow).map(|(&a, &b)| (a as i32 - b as i32).unsigned_abs()).sum::() as u64; - } else { - for j in 0..w { - let b = rr[(rx + j as i32).clamp(0, lx) as usize]; - sad += (s[j] as i32 - b as i32).unsigned_abs() as u64; - } - } - if sad as f64 + lb >= bound { - return sad; - } - } - sad - } - - /// SAD of a prediction against the source over the visible part of the block. Stops after a row - /// once `sad + lb >= bound` (the cost can no longer win). - fn sad_bounded(&self, p: usize, [x, y]: [usize; 2], n: usize, pred: &[u16], [lb, bound]: [f64; 2]) -> u64 { - let (vw, vh) = self.g.visible(p); - let w = n.min(vw.saturating_sub(x)); - let h = n.min(vh.saturating_sub(y)); - let src = &self.src[p]; - let mut s = 0u64; - for i in 0..h { - let row = &src.row(y + i)[x..x + w]; - s += row.iter().zip(&pred[i * n..i * n + w]).map(|(&a, &b)| (a as i32 - b as i32).unsigned_abs()).sum::() as u64; - if s as f64 + lb >= bound { - return s; - } - } - s - } - - /// Sum of absolute Hadamard-transformed differences (4x4 or 8x8 sub-blocks) of a prediction - /// against the source, scaled to about the size of the SAD. - fn satd(&self, p: usize, x: usize, y: usize, n: usize, pred: &[u16]) -> u64 { - if n >= 8 { self.satd_k::<8>(p, x, y, n, pred) } else { self.satd_k::<4>(p, x, y, n, pred) } - } - - /// `satd` with K x K Hadamard sub-blocks (fixed sizes so the butterflies vectorise). - fn satd_k(&self, p: usize, x: usize, y: usize, n: usize, pred: &[u16]) -> u64 { - let src = &self.src[p]; - let mut total = 0u64; - for by in (0..n).step_by(K) { - for bx in (0..n).step_by(K) { - // d[i] holds row i of the differences - let mut d = [[0i32; K]; K]; - for (i, di) in d.iter_mut().enumerate() { - let row = &src.row(y + by + i)[x + bx..x + bx + K]; - let pr = &pred[(by + i) * n + bx..(by + i) * n + bx + K]; - for j in 0..K { - di[j] = row[j] as i32 - pr[j] as i32; - } - } - // vertical butterflies across rows (all columns at once) - let mut h = 1; - while h < K { - for i0 in (0..K).step_by(2 * h) { - for i in i0..i0 + h { - let (a, b) = (d[i], d[i + h]); - for j in 0..K { - d[i][j] = a[j] + b[j]; - d[i + h][j] = a[j] - b[j]; - } - } - } - h *= 2; - } - // horizontal butterflies within each row - let mut s = 0u64; - for di in d.iter_mut() { - hadamard(di); - s += di.iter().map(|v| v.unsigned_abs() as u64).sum::(); - } - total += s / (K as u64 / 2); - } - } - total - } - - fn sse(&self, p: usize, x: usize, y: usize, n: usize, blk: &[u16]) -> f64 { - let (vw, vh) = self.g.visible(p); - let w = n.min(vw.saturating_sub(x)); - let h = n.min(vh.saturating_sub(y)); - let src = &self.src[p]; - let mut s = 0u64; - for i in 0..h { - let row = &src.row(y + i)[x..x + w]; - s += row - .iter() - .zip(&blk[i * n..i * n + w]) - .map(|(&a, &b)| { - let d = a as i64 - b as i64; - (d * d) as u64 - }) - .sum::(); - } - s as f64 - } - - fn put(&mut self, p: usize, x: usize, y: usize, n: usize, blk: &[u16]) { - let pl = &mut self.rec[p]; - for i in 0..n { - let o = (y + i) * pl.stride + x; - pl.data[o..o + n].copy_from_slice(&blk[i * n..i * n + n]); - } - } - - /// Transform, quantise and reconstruct one square transform block of `plane` at (x, y) - /// over the prediction `pred`; the reconstruction is written to `rec`. - fn code_tx(&mut self, block: TransformBlock, pred: &[u16]) -> TxResult { - let TransformBlock { plane: p, x, y, log2, .. } = block; - let mut sc = self.scratch.take().unwrap_or_default(); - let (res, has_recon) = self.try_tx(&mut sc, block, pred); - let n = 1usize << log2; - self.put(p, x, y, n, if has_recon { &sc.recon[..n * n] } else { pred }); - let levels = sc.levels[..n * n].to_vec(); - self.scratch = Some(sc); - TxResult { levels, nonzero: res.nonzero, dist: res.dist, bits: res.bits } - } - - /// Transform and quantise one block without writing it. The levels are left in `sc.levels`; - /// the flag tells whether `sc.recon` holds the reconstruction (false: it is the prediction). - fn try_tx(&self, sc: &mut TxScratch, block: TransformBlock, pred: &[u16]) -> (TxEval, bool) { - let TransformBlock { plane: p, x, y, log2, tx_type, intra } = block; - let n = 1usize << log2; - let nn = n * n; - let tx_sz = tx_size_for(log2); - let bd = self.bd; - let src = &self.src[p]; - let TxScratch { res, coef, levels, deq, resid, recon } = sc; - let (res, coef, levels) = (&mut res[..nn], &mut coef[..nn], &mut levels[..nn]); - for i in 0..n { - let row = &src.row(y + i)[x..x + n]; - for j in 0..n { - res[i * n + j] = row[j] as i32 - pred[i * n + j] as i32; - } - } - forward_2d(res, coef, n, tx_type); - // dead-zone quantiser: rounding offset below one half - let rnd = if intra { INTRA_RND } else { INTER_RND }; - let [dc_thr, ac_thr] = [self.dead_zone[0][intra as usize], self.dead_zone[1][intra as usize]]; - levels.fill(0); - let mut nonzero = false; - for (k, (&cf, l)) in coef.iter().zip(levels.iter_mut()).enumerate() { - let (q, thr) = if k == 0 { (self.dc_q, dc_thr) } else { (self.ac_q, ac_thr) }; - // dead zone: below the exact threshold the level is zero, no division needed - if cf.abs() < thr { - continue; - } - // the operand is >= 0, so truncation equals floor - let a = (cf.abs() / q + rnd) as i32; - if a != 0 { - *l = if cf < 0.0 { -a.min(1 << 16) } else { a.min(1 << 16) }; - nonzero = true; - } - } - let zero_dist = self.sse(p, x, y, n, pred); - if !nonzero { - return (TxEval { nonzero, dist: zero_dist, bits: 1.0 }, false); - } - let recon = &mut recon[..nn]; - self.reconstruct(levels, ReconBuffers { deq: &mut deq[..nn], resid: &mut resid[..nn], recon }, tx_sz, tx_type, pred, bd); - let dist = self.sse(p, x, y, n, recon); - let bits = estimate_bits(levels, tx_sz); - if dist + self.lambda * bits >= zero_dist + self.lambda { - levels.fill(0); - return (TxEval { nonzero: false, dist: zero_dist, bits: 1.0 }, false); - } - (TxEval { nonzero: true, dist, bits }, true) - } - - /// Dequantisation (7.12.3) and the inverse transform, added to the prediction; the result is - /// written to `recon`. `deq` and `resid` are scratch buffers of `n * n` entries. - fn reconstruct(&self, levels: &[i32], bufs: ReconBuffers, tx_sz: usize, tx_type: usize, pred: &[u16], bd: u32) { - let ReconBuffers { deq, resid, recon } = bufs; - let denom: i64 = if tx_sz == TX_32X32 { 2 } else { 1 }; - let lim = 1i64 << (7 + bd); - deq.fill(0); - for (k, (&l, d)) in levels.iter().zip(deq.iter_mut()).enumerate() { - if l == 0 { - continue; - } - let q = if k == 0 { self.dc_qi } else { self.ac_qi }; - let dq = l as i64 * q; - let mag = (dq.abs() & 0xFF_FFFF) / denom; - *d = (if dq < 0 { -mag } else { mag }).clamp(-lim, lim - 1) as i32; - } - inverse_2d(deq, resid, tx_sz, tx_type, bd); - let max = (1i32 << bd) - 1; - for ((o, &p), &r) in recon.iter_mut().zip(pred).zip(resid.iter()) { - *o = (p as i32 + r).clamp(0, max) as u16; - } - } -} - -/// Smallest `x >= 0` with `(x / q + rnd) as i32 >= 1` in f32 arithmetic. The expression is -/// monotone in `x`, so a binary search over the bit patterns finds the exact boundary. -fn zero_threshold(q: f32, rnd: f32) -> f32 { - let (mut lo, mut hi) = (0u32, f32::MAX.to_bits()); - while lo < hi { - let mid = lo + (hi - lo) / 2; - if (f32::from_bits(mid) / q + rnd) as i32 >= 1 { - hi = mid; - } else { - lo = mid + 1; - } - } - f32::from_bits(lo) -} - -const LOG2_TABLE_LEN: usize = 1025; - -/// `log2(i)` for `i < LOG2_TABLE_LEN`, computed with the same `f64::log2` call as the direct form. -fn log2_table() -> &'static [f64; LOG2_TABLE_LEN] { - static TABLE: std::sync::OnceLock<[f64; LOG2_TABLE_LEN]> = std::sync::OnceLock::new(); - TABLE.get_or_init(|| std::array::from_fn(|i| (i as f64).log2())) -} - -fn log2_of(table: &[f64; LOG2_TABLE_LEN], v: usize) -> f64 { - if v < LOG2_TABLE_LEN { table[v] } else { (v as f64).log2() } -} - -/// Unnormalised Walsh-Hadamard transform of 4 or 8 values, in place. -fn hadamard(v: &mut [i32]) { - let n = v.len(); - let mut h = 1; - while h < n { - for i in (0..n).step_by(2 * h) { - for j in i..i + h { - let (a, b) = (v[j], v[j + h]); - v[j] = a + b; - v[j + h] = a - b; - } - } - h *= 2; - } -} - -/// Rough coefficient coding cost in bits. -fn estimate_bits(levels: &[i32], tx_sz: usize) -> f64 { - let scan = scan_for(tx_sz); - let mut eob = 0; - for (i, &pos) in scan.iter().enumerate() { - if levels[pos as usize] != 0 { - eob = i + 1; - } - } - if eob == 0 { - return 1.0; - } - let lg = log2_table(); - let mut bits = 3.0 + log2_of(lg, eob); - for &pos in &scan[..eob] { - let a = levels[pos as usize].unsigned_abs(); - bits += match a { - 0 => 0.7, - 1 => 2.5, - _ => 3.5 + 1.6 * log2_of(lg, a as usize), - }; - } - bits -} - -pub(crate) fn scan_for(tx_sz: usize) -> &'static [u16] { - match tx_sz { - TX_4X4 => &DEFAULT_SCAN_4X4, - TX_8X8 => &DEFAULT_SCAN_8X8, - TX_16X16 => &DEFAULT_SCAN_16X16, - _ => &DEFAULT_SCAN_32X32, - } -} - -#[cfg(test)] -mod tests { - use super::*; - use crate::tables::{AC_QLOOKUP, DC_QLOOKUP}; - - /// The quantiser as it was before the dead-zone threshold and the truncation. - fn quant_ref(cf: f32, q: f32, rnd: f32) -> i32 { - (cf.abs() / q + rnd).floor() as i32 - } - - fn quant_new(cf: f32, q: f32, rnd: f32, thr: f32) -> i32 { - if cf.abs() < thr { - return 0; - } - (cf.abs() / q + rnd) as i32 - } - - #[test] - fn dead_zone_threshold_is_exact() { - let mut state = 0x1234_5678_9abc_def0u64; - let mut next = move || { - state ^= state << 13; - state ^= state >> 7; - state ^= state << 17; - state - }; - for table in AC_QLOOKUP.iter().chain(DC_QLOOKUP.iter()) { - for &qi in table.iter() { - let q = qi as f32; - for rnd in [INTER_RND, INTRA_RND] { - let thr = zero_threshold(q, rnd); - // the boundary itself and its neighbours - let b = thr.to_bits(); - for bits in b.saturating_sub(3)..=b + 3 { - let cf = f32::from_bits(bits); - assert_eq!(quant_ref(cf, q, rnd), quant_new(cf, q, rnd, thr), "q {q} rnd {rnd} cf {cf}"); - assert_eq!(quant_ref(-cf, q, rnd), quant_new(-cf, q, rnd, thr)); - } - for _ in 0..200 { - let cf = f32::from_bits((next() >> 32) as u32 & 0x7fff_ffff); - if cf.is_nan() { - continue; - } - assert_eq!(quant_ref(cf, q, rnd), quant_new(cf, q, rnd, thr)); - let cf = (next() % 100_000) as f32 / 100.0; - assert_eq!(quant_ref(cf, q, rnd), quant_new(cf, q, rnd, thr)); - } - } - } - } - } - - #[test] - fn log2_table_matches_direct_log2() { - let t = log2_table(); - for v in 1..3000usize { - assert_eq!(log2_of(t, v).to_bits(), (v as f64).log2().to_bits(), "{v}"); - } - } - - /// The SATD as it was before the fixed-size version: runtime block size, column gather. - fn satd_ref(src: &Plane, x: usize, y: usize, n: usize, pred: &[u16]) -> u64 { - let k = n.min(8); - let mut total = 0u64; - let mut d = [0i32; 64]; - for by in (0..n).step_by(k) { - for bx in (0..n).step_by(k) { - for i in 0..k { - let row = &src.row(y + by + i)[x + bx..x + bx + k]; - for j in 0..k { - d[i * k + j] = row[j] as i32 - pred[(by + i) * n + bx + j] as i32; - } - } - for i in 0..k { - hadamard(&mut d[i * k..i * k + k]); - } - let mut col = [0i32; 8]; - let mut s = 0u64; - for j in 0..k { - for i in 0..k { - col[i] = d[i * k + j]; - } - hadamard(&mut col[..k]); - s += col[..k].iter().map(|v| v.unsigned_abs() as u64).sum::(); - } - total += s / (k as u64 / 2); - } - } - total - } - - #[test] - fn satd_fixed_size_matches_runtime_size() { - let g = FrameGeom::new(100, 80); - let mut state = 0x9e37_79b9_7f4a_7c15u64; - let mut next = move || { - state ^= state << 13; - state ^= state >> 7; - state ^= state << 17; - state - }; - for bd in [8u32, 10] { - let max = (1u64 << bd) - 1; - let mut mk = |w: usize, h: usize| { - let mut p = Plane::new(w, h); - p.data.iter_mut().for_each(|v| *v = (next() % (max + 1)) as u16); - p - }; - let src = [mk(128, 128), mk(64, 64), mk(64, 64)]; - let dec = Decider::new(&g, bd, 100, true, &src, None); - for n in [4usize, 8, 16, 32] { - let pred: Vec = (0..n * n).map(|_| (next() % (max + 1)) as u16).collect(); - for (p, x, y) in [(0, 0, 0), (0, 32, 40), (1, 8, 4)] { - assert_eq!(dec.satd(p, x, y, n, &pred), satd_ref(&src[p], x, y, n, &pred), "n {n} plane {p}"); - } - } - } - } - - fn xorshift(seed: u64) -> impl FnMut() -> u64 { - let mut state = seed; - move || { - state ^= state << 13; - state ^= state >> 7; - state ^= state << 17; - state - } - } - - /// A snapshot keeps only the BlockDecoded cells of the block area. Coding inside the block - /// marks sub-blocks of every smaller size; restoring must give back the whole 3x35x35 array - /// as it was, for every block size of a 64x64 superblock and every aligned position. - #[test] - fn snapshot_restore_brings_back_every_blockdecoded_flag() { - let g = FrameGeom::new(256, 256); - let mut next = xorshift(0x2545_f491_4f6c_dd1d); - let src = [Plane::new(256, 256), Plane::new(128, 128), Plane::new(128, 128)]; - let mut dec = Decider::new(&g, 8, 100, true, &src, None); - for random_start in [false, true] { - for n4 in [1usize, 2, 4, 8, 16] { - for sb in [0usize, 16] { - for r in (sb..sb + 16).step_by(n4) { - for c in (sb..sb + 16).step_by(n4) { - for plane in dec.decoded.iter_mut() { - for row in plane.iter_mut() { - for v in row.iter_mut() { - *v = random_start && next() & 1 == 1; - } - } - } - let before = dec.decoded.clone(); - let snap = dec.snapshot(r, c, n4); - // every sub-block of every size inside the block, as coding marks them - let mut m = n4; - loop { - for rr in (r..r + n4).step_by(m) { - for cc in (c..c + n4).step_by(m) { - for p in 0..3 { - dec.mark_decoded(p, rr, cc, m); - } - } - } - if m == 1 { - break; - } - m /= 2; - } - dec.restore(&snap, r, c, n4); - assert!(dec.decoded == before, "n4 {n4} at ({r}, {c}), random start {random_start}"); - } - } - } - } - } - } - - /// The bounded SADs return the full SAD whenever the cost can still win (`sad + lb < bound`) - /// and otherwise a partial sum that already loses, so the callers' strict `<` decides the same. - #[test] - fn bounded_sad_stops_only_once_the_cost_cannot_win() { - let g = FrameGeom::new(100, 80); - let mut next = xorshift(0x9e37_79b9_7f4a_7c15); - let mut mk = |w: usize, h: usize| { - let mut p = Plane::new(w, h); - p.data.iter_mut().for_each(|v| *v = (next() % 256) as u16); - p - }; - let src = [mk(128, 128), mk(64, 64), mk(64, 64)]; - let refp = mk(128, 128); - let mut next = xorshift(0x1234_5678_9abc_def1); - let dec = Decider::new(&g, 8, 100, false, &src, None); - for n in [4usize, 8, 16, 32] { - for (x, y, rx, ry) in [(0usize, 0usize, 0i32, 0i32), (32, 40, 29, 43), (96, 72, 90, 70), (8, 8, -5, -3)] { - let full_ref = dec.sad_ref(&refp, [rx, ry], [x, y], n, [0.0, f64::INFINITY]); - let pred: Vec = (0..n * n).map(|_| (next() % 256) as u16).collect(); - let full_pred = dec.sad_bounded(0, [x, y], n, &pred, [0.0, f64::INFINITY]); - for _ in 0..50 { - let lb = (next() % 64) as f64 * 0.75; - let b_ref = (next() % (2 * full_ref + 2)) as f64; - let b_pred = (next() % (2 * full_pred + 2)) as f64; - let got_ref = dec.sad_ref(&refp, [rx, ry], [x, y], n, [lb, b_ref]); - let got_pred = dec.sad_bounded(0, [x, y], n, &pred, [lb, b_pred]); - for (got, full, bound) in [(got_ref, full_ref, b_ref), (got_pred, full_pred, b_pred)] { - if full as f64 + lb < bound { - assert_eq!(got, full, "n {n} at ({x}, {y}) bound {bound}"); - } else { - assert!(got as f64 + lb >= bound && got <= full, "n {n} at ({x}, {y}) bound {bound}"); - } - } - } - } - } - } -} diff --git a/crates/av1enc/src/lib.rs b/crates/av1enc/src/lib.rs deleted file mode 100644 index 2962833d9..000000000 --- a/crates/av1enc/src/lib.rs +++ /dev/null @@ -1,670 +0,0 @@ -//! Clean-room pure-Rust AV1 encoder (Main profile, 4:2:0, 8 and 10 bit), implemented from the -//! AV1 Bitstream & Decoding Process Specification v1.0.0 with Errata 1. -//! -//! Key frames and single-reference inter frames (LAST_FRAME = the previous frame), 64x64 -//! superblocks split down to 8x8 by rate-distortion decisions, intra modes DC / V / H / Paeth / -//! smooth, full-sample motion search with the spec's motion vector prediction, DCT / ADST -//! transforms up to 32x32, one tile per frame, in-loop filters off. See the crate README. -//! -//! The public API below is the contract `crates/export` codes against; keep it stable. - -#![deny(clippy::unwrap_used, clippy::expect_used, clippy::panic, clippy::unimplemented, clippy::todo, clippy::unreachable)] - -mod bitw; -mod cdf; -mod decide; -mod loopfilter; -mod pred; -mod symbol; -mod tables; -mod transform; -mod writer; - -use bitw::{BitWriter, obu}; -use decide::{Decider, FrameGeom, RefFrame}; -use loopfilter::{LfInfo, loop_filter}; -use pred::Plane; -use tables::AC_QLOOKUP; -use writer::TileWriter; - -/// Rate control. -#[derive(Clone, Copy, Debug, PartialEq, Eq)] -pub enum RateControl { - /// Constant quantizer index (`base_q_idx`, 1–255; lower is better). - ConstantQ(u8), - /// Average bitrate target in kbit/s (frame-level quantizer adaptation). - Bitrate { kbps: u32 }, -} - -/// Encoder settings. Main profile (seq_profile 0), 4:2:0. -#[derive(Clone, Debug, PartialEq)] -pub struct EncoderConfig { - pub width: u32, - pub height: u32, - pub fps_num: u32, - pub fps_den: u32, - /// 8 or 10. - pub bit_depth: u8, - /// `seq_level_idx` (e.g. 8 = level 4.0); `None` picks the lowest level that fits. - pub level_idx: Option, - pub rate: RateControl, - /// Frames between key frames (1 = all key frames). - pub keyint: u32, - /// BT.709 limited range is signalled unless this is set. - pub full_range: bool, - /// The colour description of `color_config` (ITU-T H.273 code points); BT.709 by default. - pub color_primaries: u8, - pub transfer_characteristics: u8, - pub matrix_coefficients: u8, -} - -impl EncoderConfig { - pub fn new(width: u32, height: u32, fps_num: u32, fps_den: u32) -> Self { - EncoderConfig { - width, - height, - fps_num, - fps_den, - bit_depth: 8, - level_idx: None, - rate: RateControl::ConstantQ(100), - keyint: 60, - full_range: false, - color_primaries: 1, - transfer_characteristics: 1, - matrix_coefficients: 1, - } - } -} - -/// One input picture, 4:2:0, samples at the configured bit depth. -#[derive(Clone, Copy, Debug)] -pub struct Frame<'a> { - pub y: &'a [u16], - pub u: &'a [u16], - pub v: &'a [u16], - pub y_stride: usize, - pub uv_stride: usize, -} - -/// One temporal unit as stored in MP4 / WebM samples: OBUs with `obu_has_size_field` = 1 and -/// no temporal delimiter (key frames start with the sequence header OBU). -#[derive(Clone, Debug, PartialEq, Eq)] -pub struct Packet { - pub data: Vec, - pub keyframe: bool, -} - -#[derive(Debug, PartialEq, Eq)] -pub enum Error { - InvalidConfig(String), -} - -impl std::fmt::Display for Error { - fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { - match self { - Error::InvalidConfig(s) => write!(f, "invalid AV1 encoder config: {s}"), - } - } -} - -impl std::error::Error for Error {} - -const OBU_SEQUENCE_HEADER: u8 = 1; -const OBU_FRAME: u8 = 6; - -/// Annex A level limits: (seq_level_idx, MaxPicSize, MaxHSize, MaxVSize, MaxDisplayRate, -/// MaxBitrate (Main tier, Mbit/s)). -const LEVELS: [(u8, u64, u32, u32, u64, u64); 14] = [ - (0, 147_456, 2048, 1152, 4_423_680, 1_500_000), - (1, 278_784, 2816, 1584, 8_363_520, 3_000_000), - (4, 665_856, 4352, 2448, 19_975_680, 6_000_000), - (5, 1_065_024, 5504, 3096, 31_950_720, 10_000_000), - (8, 2_359_296, 6144, 3456, 70_778_880, 12_000_000), - (9, 2_359_296, 6144, 3456, 141_557_760, 20_000_000), - (12, 8_912_896, 8192, 4352, 267_386_880, 30_000_000), - (13, 8_912_896, 8192, 4352, 534_773_760, 40_000_000), - (14, 8_912_896, 8192, 4352, 1_069_547_520, 60_000_000), - (15, 8_912_896, 8192, 4352, 1_069_547_520, 60_000_000), - (16, 35_651_584, 16384, 8704, 1_069_547_520, 60_000_000), - (17, 35_651_584, 16384, 8704, 2_139_095_040, 100_000_000), - (18, 35_651_584, 16384, 8704, 4_278_190_080, 160_000_000), - (19, 35_651_584, 16384, 8704, 4_278_190_080, 160_000_000), -]; - -/// The lowest level whose limits fit the stream (31 = no level limits when none does). -fn pick_level(cfg: &EncoderConfig) -> u8 { - let pic = cfg.width as u64 * cfg.height as u64; - let rate = pic * cfg.fps_num as u64 / cfg.fps_den.max(1) as u64; - let bitrate = match cfg.rate { - RateControl::Bitrate { kbps } => kbps as u64 * 1000, - RateControl::ConstantQ(_) => 0, - }; - for &(idx, max_pic, max_h, max_v, max_rate, max_br) in LEVELS.iter() { - if pic <= max_pic && cfg.width <= max_h && cfg.height <= max_v && rate <= max_rate && bitrate <= max_br { - return idx; - } - } - 31 -} - -/// Frame-level rate control state for `RateControl::Bitrate`. -struct RateState { - /// Bits per frame at the target rate. - frame_bits: f64, - /// Quantizer index for inter frames (continuous). - q: f64, - /// Key frame quantizer index minus the inter frame one. - key_off: f64, - /// Quantizer index steps per halving of the frame size, measured from consecutive inter - /// frames (the size/quantizer curve is much steeper than the default on noisy content). - slope: f64, - /// Quantizer index and size (in bits) of the previous inter frame. - prev_inter: Option<(f64, f64)>, - /// Accumulated (spent - budget) bits. - debt: f64, -} - -/// Quantizer index steps per halving of the frame size before any measurement. -const RC_SLOPE: f64 = 28.0; -/// Fraction of the quantizer step the size error asks for that is applied per frame. -const RC_GAIN: f64 = 0.8; -/// Frames over which an accumulated deviation from the budget is paid back. -const RC_PAYBACK: f64 = 16.0; - -impl RateState { - /// The nominal bit budget and the starting quantizer index of a key or inter frame. A key - /// frame gets a larger share, and the inter frames of the group get the rest, so that a whole - /// group of `keyint` frames adds up to `keyint * frame_bits`. - fn frame_budget(&self, keyint: u32, key: bool) -> (f64, f64) { - let keyint = keyint.max(1) as f64; - let boost = (1.0 + (keyint.clamp(1.0, 60.0) - 1.0).sqrt() * 0.75).min(0.5 * keyint + 0.5); - if key { - (self.frame_bits * boost, (self.q + self.key_off).clamp(1.0, 255.0)) - } else if keyint > 1.0 { - (self.frame_bits * (keyint - boost) / (keyint - 1.0), self.q) - } else { - (self.frame_bits, self.q) - } - } - - /// Adapts the quantizers to the size of the frame just coded at `q_used` against `target`. - fn update(&mut self, key: bool, first: bool, q_used: f64, bits: f64, target: f64) { - let err = (bits / target).log2().clamp(-1.5, 1.5); - if first && key { - // four passes ran: the key frame quantizer is known, the inter one follows from the offset - self.q = (q_used - self.key_off).clamp(1.0, 255.0); - } else if key { - self.key_off = q_used - self.q + RC_GAIN * self.slope * err; - } else { - if let Some((pq, pbits)) = self.prev_inter { - let (dq, dr) = (q_used - pq, (pbits / bits).log2()); - if dq.abs() >= 3.0 && dr.abs() >= 0.15 && dq * dr > 0.0 { - self.slope = (0.6 * self.slope + 0.4 * (dq / dr)).clamp(3.0, 45.0); - } - } - self.prev_inter = Some((q_used, bits)); - self.q = (q_used + RC_GAIN * self.slope * err).clamp(1.0, 255.0); - } - } -} - -/// Most passes on the first frame, and how far from the target (octaves, 3 %) its size may be -/// before it is accepted. -const RC_FIRST_PASSES: usize = 4; -const RC_FIRST_TOLERANCE: f64 = 0.04; -/// Quantizer index steps per halving of the size, for the first correction of the first frame -/// (the starting quantizer comes from the bits per sample; the first pass shows how far off it -/// is, and from there on the passes are placed by `next_probe_q`). -const RC_FIRST_SLOPE: f64 = 30.0; -/// The same for a key frame after the first: coded again when its size is more than 0.25 octaves -/// (19 %) off the target. -const RC_KEY_PASSES: usize = 3; -const RC_KEY_TOLERANCE: f64 = 0.25; -/// Octaves of frame size per octave of quantizer step assumed for the first correction of a frame. -const RC_PROBE_SLOPE: f64 = 1.0; -/// Largest change of the quantizer step (octaves) between two passes on one frame. -const RC_PROBE_MAX_STEP: f64 = 2.0; - -/// The quantizer index whose step size is closest to `2^x`. -fn q_for_step(bit_depth: u8, x: f64) -> u8 { - let table = &AC_QLOOKUP[((bit_depth - 8) / 2) as usize]; - let mut best = 1; - for q in 1..=255usize { - if ((table[q] as f64).log2() - x).abs() < ((table[best] as f64).log2() - x).abs() { - best = q; - } - } - best as u8 -} - -/// The next quantizer to try for a frame, from the `(quantizer, log2(size / target))` -/// pairs coded so far. The size follows the quantizer step (not the index, which is linear in the -/// step only below about 60) in a near log-log line: false position between the closest pair of -/// passes that enclose the target, otherwise a step from the latest one with the slope of the -/// last two. `None` when no untried quantizer lies between the enclosing passes. -fn next_probe_q(bit_depth: u8, tried: &[(u8, f64)]) -> Option { - let table = &AC_QLOOKUP[((bit_depth - 8) / 2) as usize]; - let x = |q: u8| (table[q as usize] as f64).log2(); - let over = tried.iter().filter(|t| t.1 > 0.0).max_by_key(|t| t.0); - let under = tried.iter().filter(|t| t.1 <= 0.0).min_by_key(|t| t.0); - if let (Some(&(q_lo, e_lo)), Some(&(q_hi, e_hi))) = (over, under) { - if q_hi - q_lo < 2 { - return None; - } - let next = x(q_lo) + (x(q_hi) - x(q_lo)) * e_lo / (e_lo - e_hi); - return Some(q_for_step(bit_depth, next).clamp(q_lo + 1, q_hi - 1)); - } - let &(q, err) = tried.last()?; - let mut slope = RC_PROBE_SLOPE; - if let [.., (q0, e0), (q1, e1)] = tried - && (x(*q1) - x(*q0)).abs() > 0.05 - && (e0 - e1) / (x(*q1) - x(*q0)) > 0.0 - { - slope = ((e0 - e1) / (x(*q1) - x(*q0))).clamp(0.25, 3.0); - } - Some(q_for_step(bit_depth, x(q) + (err / slope).clamp(-RC_PROBE_MAX_STEP, RC_PROBE_MAX_STEP))) -} - -/// AV1 encoder. Frames come out in input order (no hidden/reordered frames). -pub struct Encoder { - cfg: EncoderConfig, - geom: FrameGeom, - level: u8, - seq_obu: Vec, - frame_num: u64, - refr: Option, - rc: Option, -} - -impl Encoder { - pub fn new(cfg: EncoderConfig) -> Result { - if cfg.width == 0 || cfg.height == 0 { - return Err(Error::InvalidConfig("empty frame".into())); - } - if cfg.width > 4096 || cfg.height > 4096 { - return Err(Error::InvalidConfig(format!("{}x{} exceeds the single-tile limit (4096 samples per side)", cfg.width, cfg.height))); - } - if cfg.bit_depth != 8 && cfg.bit_depth != 10 { - return Err(Error::InvalidConfig(format!("bit depth {} (8 or 10 supported)", cfg.bit_depth))); - } - if cfg.fps_num == 0 || cfg.fps_den == 0 { - return Err(Error::InvalidConfig("frame rate".into())); - } - let geom = FrameGeom::new(cfg.width as usize, cfg.height as usize); - if geom.sb_cols * geom.sb_rows > 2304 { - return Err(Error::InvalidConfig(format!("{}x{} exceeds the single-tile area limit (MAX_TILE_AREA)", cfg.width, cfg.height))); - } - if let RateControl::Bitrate { kbps } = cfg.rate - && kbps == 0 - { - return Err(Error::InvalidConfig("zero bitrate".into())); - } - let level = cfg.level_idx.unwrap_or_else(|| pick_level(&cfg)); - if level > 31 { - return Err(Error::InvalidConfig(format!("seq_level_idx {level}"))); - } - let rc = match cfg.rate { - RateControl::ConstantQ(_) => None, - RateControl::Bitrate { kbps } => { - let frame_bits = kbps as f64 * 1000.0 * cfg.fps_den as f64 / cfg.fps_num as f64; - // initial guess from the bits per sample - let bpp = frame_bits / (cfg.width as f64 * cfg.height as f64); - let q = (110.0 - 40.0 * (bpp / 0.1).log2()).clamp(20.0, 230.0); - Some(RateState { frame_bits, q, key_off: -24.0, slope: RC_SLOPE, prev_inter: None, debt: 0.0 }) - } - }; - let mut enc = Encoder { cfg, geom, level, seq_obu: Vec::new(), frame_num: 0, refr: None, rc }; - let payload = enc.sequence_header_payload(); - obu(OBU_SEQUENCE_HEADER, &payload, &mut enc.seq_obu); - Ok(enc) - } - - pub fn config(&self) -> &EncoderConfig { - &self.cfg - } - - /// The sequence header OBU (with size field). - pub fn sequence_header_obu(&self) -> Vec { - self.seq_obu.clone() - } - - /// AV1CodecConfigurationRecord (`av1C` payload, also the WebM CodecPrivate). - pub fn av1c(&self) -> Vec { - let high = (self.cfg.bit_depth > 8) as u8; - let mut v = vec![ - 0x81, // marker (1), version (1) - self.level & 0x1f, // seq_profile 0, seq_level_idx_0 - (high << 6) | (1 << 3) | (1 << 2), // tier 0, high_bitdepth, twelve_bit 0, mono 0, ss_x 1, ss_y 1, csp 0 - 0, // no initial_presentation_delay - ]; - v.extend_from_slice(&self.seq_obu); - v - } - - /// sequence_header_obu( ) payload (5.5) including trailing bits. - fn sequence_header_payload(&self) -> Vec { - let c = &self.cfg; - let mut w = BitWriter::new(); - w.f(3, 0); // seq_profile - w.f(1, 0); // still_picture - w.f(1, 0); // reduced_still_picture_header - w.f(1, 0); // timing_info_present_flag - w.f(1, 0); // initial_display_delay_present_flag - w.f(5, 0); // operating_points_cnt_minus_1 - w.f(12, 0); // operating_point_idc[ 0 ] - w.f(5, self.level as u32); // seq_level_idx[ 0 ] - if self.level > 7 { - w.f(1, 0); // seq_tier[ 0 ] - } - let wbits = 32 - (c.width - 1).max(1).leading_zeros(); - let hbits = 32 - (c.height - 1).max(1).leading_zeros(); - w.f(4, wbits - 1); - w.f(4, hbits - 1); - w.f(wbits, c.width - 1); - w.f(hbits, c.height - 1); - w.f(1, 0); // frame_id_numbers_present_flag - w.f(1, 0); // use_128x128_superblock - w.f(1, 0); // enable_filter_intra - w.f(1, 0); // enable_intra_edge_filter - w.f(1, 0); // enable_interintra_compound - w.f(1, 0); // enable_masked_compound - w.f(1, 0); // enable_warped_motion - w.f(1, 0); // enable_dual_filter - w.f(1, 0); // enable_order_hint - w.f(1, 0); // seq_choose_screen_content_tools - w.f(1, 0); // seq_force_screen_content_tools - w.f(1, 0); // enable_superres - w.f(1, 0); // enable_cdef - w.f(1, 0); // enable_restoration - // color_config( ) - w.f(1, (c.bit_depth > 8) as u32); // high_bitdepth - w.f(1, 0); // mono_chrome - w.f(1, 1); // color_description_present_flag - w.f(8, c.color_primaries as u32); // color_primaries - w.f(8, c.transfer_characteristics as u32); // transfer_characteristics - w.f(8, c.matrix_coefficients as u32); // matrix_coefficients - w.f(1, c.full_range as u32); // color_range - w.f(2, 0); // chroma_sample_position: unknown - w.f(1, 0); // separate_uv_delta_q - w.f(1, 0); // film_grain_params_present - w.trailing_bits(); - w.into_bytes() - } - - /// uncompressed_header( ) (5.9) for a shown KEY_FRAME or an INTER_FRAME referencing slot 0. - fn frame_header(&self, w: &mut BitWriter, key: bool, qidx: u8, lf_level: u32) { - w.f(1, 0); // show_existing_frame - w.f(2, if key { 0 } else { 1 }); // frame_type - w.f(1, 1); // show_frame - if !key { - w.f(1, 0); // error_resilient_mode - } - w.f(1, 0); // disable_cdf_update - w.f(1, 0); // frame_size_override_flag - if !key { - w.f(3, 0); // primary_ref_frame: LAST_FRAME's saved CDFs - w.f(8, 1); // refresh_frame_flags: slot 0 becomes LAST for the next frame - for _ in 0..7 { - w.f(3, 0); // ref_frame_idx[ i ] - } - w.f(1, 0); // render_and_frame_size_different - w.f(1, 0); // allow_high_precision_mv - w.f(1, 0); // is_filter_switchable - w.f(2, 0); // interpolation_filter = EIGHTTAP - w.f(1, 0); // is_motion_mode_switchable - } else { - w.f(1, 0); // render_and_frame_size_different - } - w.f(1, 0); // disable_frame_end_update_cdf - // tile_info( ): uniform spacing, one tile - let g = &self.geom; - let tile_log2 = |blk: usize, target: usize| { - let mut k = 0; - while (blk << k) < target { - k += 1; - } - k - }; - w.f(1, 1); // uniform_tile_spacing_flag - if tile_log2(1, g.sb_cols.min(64)) > 0 { - w.f(1, 0); // increment_tile_cols_log2 - } - if tile_log2(1, g.sb_rows.min(64)) > 0 { - w.f(1, 0); // increment_tile_rows_log2 - } - // quantization_params( ) - w.f(8, qidx as u32); - w.f(1, 0); // DeltaQYDc - w.f(1, 0); // DeltaQUDc - w.f(1, 0); // DeltaQUAc - w.f(1, 0); // using_qmatrix - w.f(1, 0); // segmentation_enabled - w.f(1, 0); // delta_q_present (base_q_idx > 0) - // loop_filter_params( ): the same level for every plane and direction - w.f(6, lf_level); - w.f(6, lf_level); - if lf_level != 0 { - w.f(6, lf_level); - w.f(6, lf_level); - } - w.f(3, 0); // loop_filter_sharpness - w.f(1, 0); // loop_filter_delta_enabled - w.f(1, 0); // tx_mode_select (TX_MODE_LARGEST) - if !key { - w.f(1, 0); // reference_select - } - w.f(1, 1); // reduced_tx_set - if !key { - for _ in 0..7 { - w.f(1, 0); // is_global - } - } - } - - /// Copies a picture into superblock-padded planes (edge replication). - fn load_source(&self, frame: &Frame) -> [Plane; 3] { - let maxv = (1u16 << self.cfg.bit_depth) - 1; - std::array::from_fn(|p| { - let (vw, vh) = self.geom.visible(p); - let (pw, ph) = self.geom.padded(p); - let (data, stride) = match p { - 0 => (frame.y, frame.y_stride), - 1 => (frame.u, frame.uv_stride), - _ => (frame.v, frame.uv_stride), - }; - assert!(stride >= vw && data.len() >= stride * (vh - 1) + vw, "plane {p} is too small for {vw}x{vh}"); - let mut pl = Plane::new(pw, ph); - for y in 0..ph { - let sy = y.min(vh - 1); - let src = &data[sy * stride..sy * stride + vw]; - let row = &mut pl.data[y * pw..y * pw + pw]; - for (x, d) in row.iter_mut().enumerate() { - *d = src[x.min(vw - 1)].min(maxv); - } - } - pl - }) - } - - /// Codes one frame at `qidx`; returns the frame OBU and the reconstruction. - fn encode_frame(&self, src: &[Plane; 3], key: bool, qidx: u8) -> (Vec, RefFrame) { - let g = &self.geom; - let refr = if key { None } else { self.refr.as_ref() }; - let mut dec = Decider::new(g, self.cfg.bit_depth as u32, qidx, key, src, refr); - let mut tw = TileWriter::new(g, key, qidx, refr.map(|r| &*r.cdfs)); - for sbr in 0..g.sb_rows { - tw.start_sb_row(); - for sbc in 0..g.sb_cols { - let node = dec.superblock(sbr * 16, sbc * 16); - tw.superblock(sbr * 16, sbc * 16, &node); - } - } - let lf_level = self.pick_filter_level(&dec.rec, &tw.lf_info(), src, qidx); - loop_filter(&mut dec.rec, &tw.lf_info(), [lf_level; 4], self.cfg.bit_depth as u32, false, 1); - let (tile, cdfs) = tw.finish(); - let mut w = BitWriter::new(); - self.frame_header(&mut w, key, qidx, lf_level); - w.byte_align(); - // tile_group_obu( ): a single tile, no tile_start_and_end_present_flag - let mut payload = w.into_bytes(); - payload.extend_from_slice(&tile); - let mut out = Vec::with_capacity(payload.len() + 8); - obu(OBU_FRAME, &payload, &mut out); - let rf = RefFrame { planes: dec.rec, mvs: dec.mvs, lf_level, cdfs }; - (out, rf) - } - - /// The loop filter level minimising the luma error (searched around a guess from the - /// quantizer, on the luma of a subset of the superblock rows). - fn pick_filter_level(&self, rec: &[Plane; 3], info: &LfInfo, src: &[Plane; 3], qidx: u8) -> u32 { - let (w, h) = (self.geom.width, self.geom.height); - let sb_row_step = if self.geom.sb_rows >= 9 { 3 } else { 1 }; - let sse = |pl: &Plane| -> u64 { - let mut s = 0u64; - for y in 0..h { - if !(y >> 6).is_multiple_of(sb_row_step) { - continue; - } - for (a, b) in pl.row(y)[..w].iter().zip(&src[0].row(y)[..w]) { - let d = *a as i64 - *b as i64; - s += (d * d) as u64; - } - } - s - }; - let bd = self.cfg.bit_depth as u32; - let mut cache: Vec<(u32, u64)> = vec![(0, sse(&rec[0]))]; - // one working copy of the luma plane for all candidate levels - let mut planes = [rec[0].clone(), Plane::new(0, 0), Plane::new(0, 0)]; - let mut eval = |lvl: u32| -> u64 { - if let Some(&(_, e)) = cache.iter().find(|(l, _)| *l == lvl) { - return e; - } - planes[0].data.copy_from_slice(&rec[0].data); - loop_filter(&mut planes, info, [lvl; 4], bd, true, sb_row_step); - let e = sse(&planes[0]); - cache.push((lvl, e)); - e - }; - let guess = ((qidx as u32 * 3) / 16).clamp(2, 50); - let mut best = (0u32, eval(0)); - let mut step = (guess / 2).max(1); - let mut center = guess; - let e = eval(center); - if e < best.1 { - best = (center, e); - } - while step >= 2 { - for lvl in [center.saturating_sub(step), (center + step).min(63)] { - let e = eval(lvl); - if e < best.1 { - best = (lvl, e); - } - } - center = best.0.max(1); - step /= 2; - } - best.0 - } - - /// Encodes one frame. - pub fn encode(&mut self, frame: &Frame) -> Packet { - let key = self.frame_num.is_multiple_of(self.cfg.keyint.max(1) as u64); - let src = self.load_source(frame); - let (frame_obu, rf) = match self.cfg.rate { - RateControl::ConstantQ(q) => self.encode_frame(&src, key, q.max(1)), - RateControl::Bitrate { .. } => match self.rc.take() { - Some(mut rc) => { - let out = self.encode_rate_controlled(&src, key, &mut rc); - self.rc = Some(rc); - out - } - // The state is created with the encoder for bitrate mode; without it, encode at a - // middle quantizer rather than fail. - None => self.encode_frame(&src, key, 128), - }, - }; - self.refr = Some(rf); - self.frame_num += 1; - let mut data = Vec::with_capacity(frame_obu.len() + self.seq_obu.len()); - if key { - data.extend_from_slice(&self.seq_obu); - } - data.extend_from_slice(&frame_obu); - Packet { data, keyframe: key } - } - - fn encode_rate_controlled(&mut self, src: &[Plane; 3], key: bool, rc: &mut RateState) -> (Vec, RefFrame) { - let first = self.frame_num == 0; - let (budget, q_start) = rc.frame_budget(self.cfg.keyint, key); - // pay back the accumulated deviation over the next RC_PAYBACK frames - let target = (budget - rc.debt / RC_PAYBACK).clamp(budget * 0.35, budget * 2.0); - let mut q = q_start; - let mut best: Option<(Vec, RefFrame, f64, f64)> = None; - // The first frame is coded up to four times to find its quantizer (it fixes the - // quantizers of everything after it), a later key frame up to three times (it is a large - // part of the budget of its group). A frame is coded again only while its size is far from - // the target and an untried quantizer is left to try; other frames are coded once. - let (max_passes, tolerance) = if first { - (RC_FIRST_PASSES, RC_FIRST_TOLERANCE) - } else if key { - (RC_KEY_PASSES, RC_KEY_TOLERANCE) - } else { - (1, 0.0) - }; - let mut tried: Vec<(u8, f64)> = Vec::new(); - for _ in 0..max_passes { - let qi = q.round().clamp(1.0, 255.0) as u8; - if tried.iter().any(|t| t.0 == qi) { - break; - } - let (obu, rf) = self.encode_frame(src, key, qi); - let bits = obu.len() as f64 * 8.0; - let err = (bits / target).log2(); - tried.push((qi, err)); - if best.as_ref().is_none_or(|b| err.abs() < (b.2 / target).log2().abs()) { - best = Some((obu, rf, bits, qi as f64)); - } - if err.abs() <= tolerance { - break; - } - let next = if first && tried.len() == 1 { - Some((q + RC_FIRST_SLOPE * err).clamp(1.0, 255.0)) - } else { - next_probe_q(self.cfg.bit_depth, &tried).map(f64::from) - }; - match next { - Some(next) => q = next, - None => break, - } - } - // At least one pass ran, so `best` is set. - let Some((obu, rf, bits, q_used)) = best else { return self.encode_frame(src, key, q.round().clamp(1.0, 255.0) as u8) }; - rc.debt += bits - budget; - rc.update(key, first, q_used, bits, target); - (obu, rf) - } - - /// The loop filter level of the last encoded frame. For tests. - #[doc(hidden)] - pub fn last_loop_filter_level(&self) -> Option { - self.refr.as_ref().map(|r| r.lf_level) - } - - /// The reconstruction of the last encoded frame (visible area, planes Y, U, V), equal to - /// what a decoder outputs. For tests. - #[doc(hidden)] - pub fn last_reconstruction(&self) -> Option<[Vec; 3]> { - let rf = self.refr.as_ref()?; - Some(std::array::from_fn(|p| { - let (vw, vh) = self.geom.visible(p); - let pl = &rf.planes[p]; - let mut v = Vec::with_capacity(vw * vh); - for y in 0..vh { - v.extend_from_slice(&pl.row(y)[..vw]); - } - v - })) - } -} diff --git a/crates/av1enc/src/loopfilter.rs b/crates/av1enc/src/loopfilter.rs deleted file mode 100644 index cf350fbd2..000000000 --- a/crates/av1enc/src/loopfilter.rs +++ /dev/null @@ -1,209 +0,0 @@ -//! Deblocking loop filter (spec 7.14), mirrored from the decoder process so that the encoder's -//! references equal a decoder's. Segmentation and loop filter deltas are off and the sharpness -//! is 0 in the frames this encoder writes. - -use crate::pred::Plane; -use crate::tables::{MAX_LOOP_FILTER, NUM_4X4_BLOCKS_HIGH, NUM_4X4_BLOCKS_WIDE, SUBSAMPLED_SIZE, TX_HEIGHT, TX_WIDTH}; - -/// Mode info the loop filter reads (per 4x4 luma unit, row stride `mi_cols`), and the -/// transform size per 4x4 unit of each plane (`LoopfilterTxSizes`, row stride `mi_cols + 32`). -pub(crate) struct LfInfo<'a> { - pub mi_cols: usize, - pub mi_rows: usize, - pub width: usize, - pub height: usize, - pub mi_size: &'a [u8], - pub skip: &'a [bool], - pub is_inter: &'a [bool], - pub tx: [&'a [u8]; 3], -} - -/// Applies the loop filter with `loop_filter_level[ 0..4 ]` to `planes`. For level search -/// (`luma_only`), only plane 0 and only every `sb_row_step`-th superblock row are filtered. -pub(crate) fn loop_filter(planes: &mut [Plane; 3], info: &LfInfo, level: [u32; 4], bit_depth: u32, luma_only: bool, sb_row_step: usize) { - if level[0] == 0 && level[1] == 0 { - return; - } - let nplanes = if luma_only { 1 } else { 3 }; - for (plane, pl) in planes.iter_mut().enumerate().take(nplanes) { - if plane > 0 && level[1 + plane] == 0 { - continue; - } - for pass in 0..2 { - let step = if plane == 0 { 1 } else { 2 }; - let mut row = 0; - while row < info.mi_rows { - if !(row >> 4).is_multiple_of(sb_row_step) { - row += step; - continue; - } - let mut col = 0; - while col < info.mi_cols { - edge(pl, info, level, bit_depth, plane, pass, (row, col)); - col += step; - } - row += step; - } - } - } -} - -fn edge(pl: &mut Plane, info: &LfInfo, level: [u32; 4], bd: u32, plane: usize, pass: usize, position: (usize, usize)) { - let (row, col) = position; - let s = (plane > 0) as usize; - let (dx, dy) = if pass == 0 { (1usize, 0usize) } else { (0, 1) }; - let x = col * 4; - let y = row * 4; - let row = row | s; - let col = col | s; - let on_screen = !(x >= info.width || y >= info.height || (pass == 0 && x == 0) || (pass == 1 && y == 0)); - if !on_screen { - return; - } - let xp = x >> s; - let yp = y >> s; - let prev_row = row - (dy << s); - let prev_col = col - (dx << s); - let i = row * info.mi_cols + col; - let mi_size = info.mi_size[i] as usize; - let tx_stride = info.mi_cols + 32; - let tx_sz = info.tx[plane][(row >> s) * tx_stride + (col >> s)] as usize; - let plane_size = SUBSAMPLED_SIZE[mi_size][s][s] as usize; - let skip = info.skip[i]; - let is_intra = !info.is_inter[i]; - let prev_tx_sz = info.tx[plane][(prev_row >> s) * tx_stride + (prev_col >> s)] as usize; - let is_block_edge = if pass == 0 { - xp.is_multiple_of(4 * NUM_4X4_BLOCKS_WIDE[plane_size] as usize) - } else { - yp.is_multiple_of(4 * NUM_4X4_BLOCKS_HIGH[plane_size] as usize) - }; - let is_tx_edge = if pass == 0 { xp.is_multiple_of(TX_WIDTH[tx_sz] as usize) } else { yp.is_multiple_of(TX_HEIGHT[tx_sz] as usize) }; - let apply_filter = is_tx_edge && (is_block_edge || !skip || is_intra); - let base_size = if pass == 0 { TX_WIDTH[prev_tx_sz].min(TX_WIDTH[tx_sz]) } else { TX_HEIGHT[prev_tx_sz].min(TX_HEIGHT[tx_sz]) } as usize; - let filter_size = if plane == 0 { 16.min(base_size) } else { 8.min(base_size) }; - // 7.14.4 with no deltas, segmentation or sharpness: the same strength for every block - let lvl = level[if plane == 0 { pass } else { plane + 1 }].min(MAX_LOOP_FILTER as u32) as i32; - if !apply_filter || lvl == 0 { - return; - } - let limit = lvl.max(1); - let blimit = 2 * (lvl + 2) + limit; - let thresh = lvl >> 4; - let strength = FilterStrength { limit, blimit, thresh }; - for k in 0..4 { - sample_filter(pl, (xp + dy * k, yp + dx * k), plane, &strength, (dx as isize, dy as isize), filter_size, bd); - } -} - -/// Thresholds derived from the loop-filter level (spec 7.14.4). -struct FilterStrength { - limit: i32, - blimit: i32, - thresh: i32, -} - -#[inline] -fn sample_filter(pl: &mut Plane, position: (usize, usize), plane: usize, strength: &FilterStrength, direction: (isize, isize), filter_size: usize, bd: u32) { - let (x, y) = position; - let (dx, dy) = direction; - let FilterStrength { limit, blimit, thresh } = *strength; - let stride = pl.stride as isize; - let step = dy * stride + dx; - let base = y as isize * stride + x as isize; - let reach: isize = if filter_size >= 16 { 7 } else { 4 }; - let mut v = [0i32; 16]; - for k in -reach..reach { - v[(k + 8) as usize] = pl.data[(base + k * step) as usize] as i32; - } - let at = |k: isize| -> i32 { v[(k + 8) as usize] }; - let (q0, q1, q2, q3) = (at(0), at(1), at(2), at(3)); - let (p0, p1, p2, p3) = (at(-1), at(-2), at(-3), at(-4)); - let d = &mut pl.data; - let sh = bd - 8; - let thresh_bd = thresh << sh; - let hev = (p1 - p0).abs() > thresh_bd || (q1 - q0).abs() > thresh_bd; - let filter_len = if filter_size == 4 { - 4 - } else if plane != 0 { - 6 - } else if filter_size == 8 { - 8 - } else { - 16 - }; - let limit_bd = limit << sh; - let blimit_bd = blimit << sh; - let mut mask = (p1 - p0).abs() > limit_bd || (q1 - q0).abs() > limit_bd || (p0 - q0).abs() * 2 + (p1 - q1).abs() / 2 > blimit_bd; - if filter_len >= 6 { - mask |= (p2 - p1).abs() > limit_bd || (q2 - q1).abs() > limit_bd; - } - if filter_len >= 8 { - mask |= (p3 - p2).abs() > limit_bd || (q3 - q2).abs() > limit_bd; - } - if mask { - return; - } - let t_bd = 1 << sh; - let flat = if filter_size >= 8 { - let mut m = (p1 - p0).abs() > t_bd || (q1 - q0).abs() > t_bd || (p2 - p0).abs() > t_bd || (q2 - q0).abs() > t_bd; - if filter_len >= 8 { - m |= (p3 - p0).abs() > t_bd || (q3 - q0).abs() > t_bd; - } - !m - } else { - false - }; - let flat2 = if filter_size >= 16 { - let (q4, q5, q6, p4, p5, p6) = (at(4), at(5), at(6), at(-5), at(-6), at(-7)); - !((p6 - p0).abs() > t_bd - || (q6 - q0).abs() > t_bd - || (p5 - p0).abs() > t_bd - || (q5 - q0).abs() > t_bd - || (p4 - p0).abs() > t_bd - || (q4 - q0).abs() > t_bd) - } else { - false - }; - if filter_size == 4 || !flat { - // narrow filter (7.14.6.3) - let half = 1i32 << (bd - 1); - let c = |v: i32| v.clamp(-half, half - 1); - let off = 0x80 << sh; - let (ps1, ps0, qs0, qs1) = (p1 - off, p0 - off, q0 - off, q1 - off); - let mut filter = if hev { c(ps1 - qs1) } else { 0 }; - filter = c(filter + 3 * (qs0 - ps0)); - let filter1 = c(filter + 4) >> 3; - let filter2 = c(filter + 3) >> 3; - d[base as usize] = (c(qs0 - filter1) + off) as u16; - d[(base - step) as usize] = (c(ps0 + filter2) + off) as u16; - if !hev { - let f = (filter1 + 1) >> 1; - d[(base + step) as usize] = (c(qs1 - f) + off) as u16; - d[(base - 2 * step) as usize] = (c(ps1 + f) + off) as u16; - } - } else { - // wide filter (7.14.6.4) - let log2 = if filter_size == 8 || !flat2 { 3 } else { 4 }; - let n: isize = if log2 == 4 { - 6 - } else if plane == 0 { - 3 - } else { - 2 - }; - let n2: isize = if log2 == 3 && plane == 0 { 0 } else { 1 }; - let mut f = [0i32; 12]; - for i in -n..n { - let mut t = 0; - for j in -n..=n { - let p = (i + j).clamp(-(n + 1), n); - let tap = if j.abs() <= n2 { 2 } else { 1 }; - t += at(p) * tap; - } - f[(i + n) as usize] = (t + (1 << (log2 - 1))) >> log2; - } - for i in -n..n { - d[(base + i * step) as usize] = f[(i + n) as usize] as u16; - } - } -} diff --git a/crates/av1enc/src/pred.rs b/crates/av1enc/src/pred.rs deleted file mode 100644 index b899202f4..000000000 --- a/crates/av1enc/src/pred.rs +++ /dev/null @@ -1,445 +0,0 @@ -//! Prediction processes, mirrored from the spec (7.11.2 intra prediction for the non-directional -//! modes plus V_PRED / H_PRED without edge filtering, 7.11.3 block inter prediction without -//! reference scaling) so that the encoder predicts exactly like a decoder. - -use crate::tables::{ - DC_PRED, EIGHTTAP, H_PRED, PAETH_PRED, SM_WEIGHTS_TX_4X4, SM_WEIGHTS_TX_8X8, SM_WEIGHTS_TX_16X16, SM_WEIGHTS_TX_32X32, SMOOTH_H_PRED, SMOOTH_PRED, - SMOOTH_V_PRED, SUBPEL_FILTERS, V_PRED, -}; -use crate::transform::round2; - -/// A plane buffer (padded to whole superblocks). -#[derive(Clone)] -pub(crate) struct Plane { - pub data: Vec, - pub stride: usize, -} - -impl Plane { - pub fn new(w: usize, h: usize) -> Plane { - Plane { data: vec![0; w * h], stride: w } - } - #[inline(always)] - pub fn at(&self, x: usize, y: usize) -> u16 { - self.data[y * self.stride + x] - } - #[inline(always)] - pub fn row(&self, y: usize) -> &[u16] { - &self.data[y * self.stride..(y + 1) * self.stride] - } -} - -/// The intra modes this encoder uses (values are the spec's y / uv mode numbers). -pub(crate) const INTRA_MODES_USED: [usize; 7] = [DC_PRED, V_PRED, H_PRED, PAETH_PRED, SMOOTH_PRED, SMOOTH_V_PRED, SMOOTH_H_PRED]; - -/// Inputs of the intra prediction process. -pub(crate) struct IntraEdge { - pub x: usize, - pub y: usize, - pub have_left: bool, - pub have_above: bool, - pub have_above_right: bool, - pub have_below_left: bool, - pub log2: u32, - /// ((MiCols * MI_SIZE) >> subX) - 1 and the same for rows. - pub max_x: i32, - pub max_y: i32, - pub bit_depth: u32, -} - -/// Edge samples: `above[0..2n]`, `left[0..2n]` and the top-left corner. -pub(crate) struct Edges { - above: [i32; 64], - left: [i32; 64], - corner: i32, -} - -pub(crate) fn edges(pl: &Plane, e: &IntraEdge) -> Edges { - let w = 1usize << e.log2; - let h = w; - let bd = e.bit_depth; - let (x, y) = (e.x, e.y); - let n = w + h; - let mut above = [0i32; 64]; - let mut left = [0i32; 64]; - if !e.have_above && e.have_left { - let v = pl.at(x - 1, y) as i32; - above[..n].iter_mut().for_each(|a| *a = v); - } else if !e.have_above && !e.have_left { - let v = (1 << (bd - 1)) - 1; - above[..n].iter_mut().for_each(|a| *a = v); - } else { - let above_limit = e.max_x.min(x as i32 + if e.have_above_right { 2 * w as i32 } else { w as i32 } - 1); - let row = pl.row(y - 1); - for (i, a) in above[..n].iter_mut().enumerate() { - *a = row[above_limit.min(x as i32 + i as i32) as usize] as i32; - } - } - if !e.have_left && e.have_above { - let v = pl.at(x, y - 1) as i32; - left[..n].iter_mut().for_each(|a| *a = v); - } else if !e.have_left && !e.have_above { - let v = (1 << (bd - 1)) + 1; - left[..n].iter_mut().for_each(|a| *a = v); - } else { - let left_limit = e.max_y.min(y as i32 + if e.have_below_left { 2 * h as i32 } else { h as i32 } - 1); - for (i, l) in left[..n].iter_mut().enumerate() { - *l = pl.at(x - 1, left_limit.min(y as i32 + i as i32) as usize) as i32; - } - } - let corner = if e.have_above && e.have_left { - pl.at(x - 1, y - 1) as i32 - } else if e.have_above { - pl.at(x, y - 1) as i32 - } else if e.have_left { - pl.at(x - 1, y) as i32 - } else { - 1 << (bd - 1) - }; - Edges { above, left, corner } -} - -fn sm_weights(log2: u32) -> &'static [u8] { - match log2 { - 2 => &SM_WEIGHTS_TX_4X4, - 3 => &SM_WEIGHTS_TX_8X8, - 4 => &SM_WEIGHTS_TX_16X16, - _ => &SM_WEIGHTS_TX_32X32, - } -} - -/// Predicts a square `2^log2` block with `mode` into `pred` (row stride = block width). -pub(crate) fn predict_intra(ed: &Edges, e: &IntraEdge, mode: usize, pred: &mut [u16]) { - let log2 = e.log2; - let w = 1usize << log2; - let h = w; - let bd = e.bit_depth; - let (above, left) = (&ed.above, &ed.left); - match mode { - DC_PRED => { - let v = if e.have_left && e.have_above { - let sum: i32 = left[..h].iter().sum::() + above[..w].iter().sum::() + ((w + h) >> 1) as i32; - sum / (w + h) as i32 - } else if e.have_left { - (left[..h].iter().sum::() + (h >> 1) as i32) >> log2 - } else if e.have_above { - (above[..w].iter().sum::() + (w >> 1) as i32) >> log2 - } else { - 1 << (bd - 1) - }; - let v = v.clamp(0, (1 << bd) - 1) as u16; - pred[..w * h].iter_mut().for_each(|p| *p = v); - } - V_PRED => { - for i in 0..h { - for j in 0..w { - pred[i * w + j] = above[j] as u16; - } - } - } - H_PRED => { - for i in 0..h { - pred[i * w..i * w + w].iter_mut().for_each(|p| *p = left[i] as u16); - } - } - PAETH_PRED => { - let tl = ed.corner; - for i in 0..h { - for j in 0..w { - let a = above[j]; - let l = left[i]; - let base = a + l - tl; - let p_left = (base - l).abs(); - let p_top = (base - a).abs(); - let p_tl = (base - tl).abs(); - pred[i * w + j] = if p_left <= p_top && p_left <= p_tl { - l as u16 - } else if p_top <= p_tl { - a as u16 - } else { - tl as u16 - }; - } - } - } - _ => { - let wx = sm_weights(log2); - let wy = wx; - for i in 0..h { - for j in 0..w { - let v = if mode == SMOOTH_PRED { - let s = wy[i] as i32 * above[j] + (256 - wy[i] as i32) * left[h - 1] + wx[j] as i32 * left[i] + (256 - wx[j] as i32) * above[w - 1]; - round2(s, 9) - } else if mode == SMOOTH_V_PRED { - round2(wy[i] as i32 * above[j] + (256 - wy[i] as i32) * left[h - 1], 8) - } else { - round2(wx[j] as i32 * left[i] + (256 - wx[j] as i32) * above[w - 1], 8) - }; - pred[i * w + j] = v as u16; - } - } - } - } -} - -/// Block inter prediction (7.11.3.3 motion vector scaling without scaling, 7.11.3.4 block -/// inter prediction with the EIGHTTAP filter, single reference so InterRound1 = 11 and the -/// final rounding is the identity) of the `w x h` block at plane position (x, y) displaced by -/// `mv` (1/8 luma sample units, [row, col]). `last_x` / `last_y` are the largest sample -/// coordinates of the reference plane; the result goes to `pred` (row stride `w`). -pub(crate) struct InterBlock { - pub last_x: i32, - pub last_y: i32, - pub x: usize, - pub y: usize, - pub width: usize, - pub height: usize, - pub mv: [i32; 2], - pub subsampling: usize, - pub bit_depth: u32, -} - -pub(crate) fn predict_inter(refp: &Plane, block: &InterBlock, pred: &mut [u16]) { - let InterBlock { last_x, last_y, x, y, width: w, height: h, mv, subsampling: ss, bit_depth } = *block; - // positions in 1/16 sample units ((startX >> 6) & 15 is the filter phase) - let px = ((x as i32) << 4) + ((2 * mv[1]) >> ss); - let py = ((y as i32) << 4) + ((2 * mv[0]) >> ss); - let (fx, fy) = ((px & 15) as usize, (py & 15) as usize); - let (ix, iy) = (px >> 4, py >> 4); - let max = (1i32 << bit_depth) - 1; - if fx == 0 && fy == 0 { - // the filters' phase-0 kernel is a unit impulse: a clamped copy - for r in 0..h { - let ry = (iy + r as i32).clamp(0, last_y) as usize; - let row = refp.row(ry); - let out = &mut pred[r * w..r * w + w]; - let x0 = ix; - if x0 >= 0 && x0 + w as i32 - 1 <= last_x { - out.copy_from_slice(&row[x0 as usize..x0 as usize + w]); - } else { - for (c, o) in out.iter_mut().enumerate() { - *o = row[(x0 + c as i32).clamp(0, last_x) as usize]; - } - } - } - return; - } - let filt_h = if w <= 4 { 4 } else { EIGHTTAP }; - let filt_v = if h <= 4 { 4 } else { EIGHTTAP }; - let kh = &SUBPEL_FILTERS[filt_h][fx]; - let kv = &SUBPEL_FILTERS[filt_v][fy]; - let bx = ix - 3; - let x_inside = bx >= 0 && bx + w as i32 + 7 <= last_x + 1; - // horizontal pass of one reference row into `out` (Round2( sum, InterRound0 = 3 )) - let hfilter = |ry: usize, out: &mut [i32]| { - let row = refp.row(ry); - if x_inside { - let seg = &row[bx as usize..bx as usize + w + 7]; - for (c, o) in out.iter_mut().enumerate() { - let mut s = 0i32; - for t in 0..8 { - s += kh[t] as i32 * seg[c + t] as i32; - } - *o = round2(s, 3); - } - } else { - for (c, o) in out.iter_mut().enumerate() { - let mut s = 0i32; - for t in 0..8 { - s += kh[t] as i32 * row[(bx + c as i32 + t as i32).clamp(0, last_x) as usize] as i32; - } - *o = round2(s, 3); - } - } - }; - let mut inter = [0i32; (64 + 7) * 64]; - if fy == 0 { - // the vertical kernel is 128 at the centre: Round2( 128 * v, 11 ) = Round2( v, 4 ) - for r in 0..h { - let ry = (iy + r as i32).clamp(0, last_y) as usize; - hfilter(ry, &mut inter[..w]); - for (p, &v) in pred[r * w..r * w + w].iter_mut().zip(&inter[..w]) { - *p = round2(v, 4).clamp(0, max) as u16; - } - } - return; - } - if fx == 0 { - // the horizontal pass is exactly 16 * sample: Round2( 16 * sum, 11 ) = Round2( sum, 7 ) - for r in 0..h { - let rows: [&[u16]; 8] = std::array::from_fn(|t| refp.row((iy + r as i32 + t as i32 - 3).clamp(0, last_y) as usize)); - for c in 0..w { - let xx = if x_inside { (ix + c as i32) as usize } else { (ix + c as i32).clamp(0, last_x) as usize }; - let mut s = 0i32; - for t in 0..8 { - s += kv[t] as i32 * rows[t][xx] as i32; - } - pred[r * w + c] = round2(s, 7).clamp(0, max) as u16; - } - } - return; - } - let inter_h = h + 7; - for r in 0..inter_h { - let ry = (iy + r as i32 - 3).clamp(0, last_y) as usize; - hfilter(ry, &mut inter[r * w..r * w + w]); - } - for r in 0..h { - for c in 0..w { - let mut s = 0i32; - for t in 0..8 { - s += kv[t] as i32 * inter[(r + t) * w + c]; - } - pred[r * w + c] = round2(s, 11).clamp(0, max) as u16; - } - } -} - -/// The visible `w x h` area of a reference plane interpolated at the sub-sample phase -/// (`fx`, `fy`) (1/16 units) with the EIGHTTAP filter, as `predict_inter` computes it for -/// blocks wider and taller than 4. The reference for `half_planes`, which the encoder uses. -#[cfg(test)] -pub(crate) fn subpel_plane(refp: &Plane, w: usize, h: usize, fx: usize, fy: usize, bit_depth: u32) -> Vec { - let kh = &SUBPEL_FILTERS[EIGHTTAP][fx]; - let kv = &SUBPEL_FILTERS[EIGHTTAP][fy]; - let max = (1i32 << bit_depth) - 1; - let (lx, ly) = (w as i32 - 1, h as i32 - 1); - let mut inter = vec![0i32; w * h]; - for y in 0..h { - let row = refp.row(y); - for x in 0..w { - let mut s = 0i32; - for t in 0..8 { - s += kh[t] as i32 * row[(x as i32 + t as i32 - 3).clamp(0, lx) as usize] as i32; - } - inter[y * w + x] = round2(s, 3); - } - } - let mut out = vec![0u16; w * h]; - for y in 0..h { - for x in 0..w { - let mut s = 0i32; - for t in 0..8 { - s += kv[t] as i32 * inter[(y as i32 + t as i32 - 3).clamp(0, ly) as usize * w + x]; - } - out[y * w + x] = round2(s, 11).clamp(0, max) as u16; - } - } - out -} - -/// The three half-sample planes (8, 0), (0, 8), (8, 8) of `subpel_plane` in one pass: the -/// horizontal half-sample pass is shared by (8, 0) and (8, 8), and the phase-0 kernels reduce to -/// exact shifts (Round2( 128 * v, 11 ) = Round2( v, 4 ), Round2( 16 * s, 11 ) = Round2( s, 7 )), -/// so the result equals three `subpel_plane` calls bit for bit. -pub(crate) fn half_planes(refp: &Plane, w: usize, h: usize, bit_depth: u32) -> [Vec; 3] { - let k = &SUBPEL_FILTERS[EIGHTTAP][8]; - let max = (1i32 << bit_depth) - 1; - let (lx, ly) = (w as i32 - 1, h as i32 - 1); - // horizontal pass at phase 8: Round2( sum, 3 ) - let mut hp = vec![0i32; w * h]; - for y in 0..h { - let row = &refp.row(y)[..w]; - let out = &mut hp[y * w..y * w + w]; - let tap = |x: i32| -> i32 { - let mut s = 0i32; - for t in 0..8 { - s += k[t] as i32 * row[(x + t as i32 - 3).clamp(0, lx) as usize] as i32; - } - round2(s, 3) - }; - if w >= 8 { - for x in 0..3 { - out[x] = tap(x as i32); - } - for (x, o) in out.iter_mut().enumerate().take(w - 4).skip(3) { - let seg = &row[x - 3..x + 5]; - let mut s = 0i32; - for t in 0..8 { - s += k[t] as i32 * seg[t] as i32; - } - *o = round2(s, 3); - } - for x in w - 4..w { - out[x] = tap(x as i32); - } - } else { - for (x, o) in out.iter_mut().enumerate() { - *o = tap(x as i32); - } - } - } - let p80: Vec = hp.iter().map(|&v| round2(v, 4).clamp(0, max) as u16).collect(); - let mut p08 = vec![0u16; w * h]; - let mut p88 = vec![0u16; w * h]; - for y in 0..h { - let rows: [usize; 8] = std::array::from_fn(|t| (y as i32 + t as i32 - 3).clamp(0, ly) as usize); - let (o08, o88) = (&mut p08[y * w..y * w + w], &mut p88[y * w..y * w + w]); - for x in 0..w { - let (mut a, mut b) = (0i32, 0i32); - for t in 0..8 { - a += k[t] as i32 * refp.data[rows[t] * refp.stride + x] as i32; - b += k[t] as i32 * hp[rows[t] * w + x]; - } - o08[x] = round2(a, 7).clamp(0, max) as u16; - o88[x] = round2(b, 11).clamp(0, max) as u16; - } - } - [p80, p08, p88] -} - -#[cfg(test)] -mod tests { - use super::*; - - /// Whole-plane interpolation equals block inter prediction at the same phase. - #[test] - fn subpel_plane_matches_block_prediction() { - let (w, h) = (37, 29); - let mut p = Plane::new(64, 64); - for (i, v) in p.data.iter_mut().enumerate() { - *v = ((i * 7919) % 256) as u16; - } - for (fx, fy) in [(8, 0), (0, 8), (8, 8), (4, 12)] { - let plane = subpel_plane(&p, w, h, fx, fy, 8); - for (bx, by) in [(0i32, 0i32), (8, 8), (24, 16), (-5, 20), (30, -3)] { - let mut pred = vec![0u16; 64]; - let mv = [by * 8 + fy as i32 / 2, bx * 8 + fx as i32 / 2]; - predict_inter( - &p, - &InterBlock { last_x: w as i32 - 1, last_y: h as i32 - 1, x: 0, y: 0, width: 8, height: 8, mv, subsampling: 0, bit_depth: 8 }, - &mut pred, - ); - for r in 0..8 { - for c in 0..8 { - let (x, y) = ((bx + c).clamp(0, w as i32 - 1), (by + r).clamp(0, h as i32 - 1)); - if bx + c == x && by + r == y { - assert_eq!(pred[(r * 8 + c) as usize], plane[y as usize * w + x as usize], "phase ({fx}, {fy}) at ({}, {})", bx + c, by + r); - } - } - } - } - } - } - - #[test] - fn half_planes_match_subpel_plane() { - for (w, h) in [(37usize, 29usize), (5, 3), (1, 1), (64, 64)] { - let mut p = Plane::new(64, 64); - for (i, v) in p.data.iter_mut().enumerate() { - *v = ((i * 7919) % 1024) as u16; - } - let hp = half_planes(&p, w, h, 10); - for (i, (fx, fy)) in [(8, 0), (0, 8), (8, 8)].into_iter().enumerate() { - assert_eq!(hp[i], subpel_plane(&p, w, h, fx, fy, 10), "{w}x{h} phase ({fx}, {fy})"); - } - } - } - - #[test] - fn phase_zero_filters_are_impulses() { - for f in SUBPEL_FILTERS.iter() { - assert_eq!(f[0], [0, 0, 0, 128, 0, 0, 0, 0]); - } - } -} diff --git a/crates/av1enc/src/symbol.rs b/crates/av1enc/src/symbol.rs deleted file mode 100644 index 579a51e20..000000000 --- a/crates/av1enc/src/symbol.rs +++ /dev/null @@ -1,267 +0,0 @@ -//! Multi-symbol arithmetic encoder: the exact inverse of the symbol decoder of spec 8.2. -//! -//! The decoder keeps `SymbolValue`, the bitwise complement of a 15-bit window into the tile data -//! minus the lower bounds of the symbols decoded so far, and selects symbol `k` when -//! `cur(k) <= SymbolValue < cur(k - 1)` with -//! `cur(k) = ((SymbolRange >> 8) * ((32768 - cdf[k]) >> 6) >> 1) + 4 * (N - k - 1)` and -//! `cur(-1) = SymbolRange`. The encoder therefore runs an ordinary carry-propagating range -//! coder over the complemented bit stream: coding symbol `k` adds `cur(k)` to `low` and sets the -//! range to `cur(k - 1) - cur(k)`; both are renormalised exactly like the decoder's range. -//! -//! At the end (spec 8.2.4 exit process) the decoder has consumed `S` renormalisation bits plus a -//! 15-bit window, and the bits from position `S` on must be a one followed by zeros. In the -//! complemented domain that window is `0b011_1111_1111_1111`, so the encoder picks the `S`-bit -//! prefix `D` with `D * 2^15 + 2^14 - 1` inside `[low, low + range)`, i.e. -//! `D = (low + 2^14) >> 15`, and writes `!D`, a one bit and zero padding. - -pub(crate) struct SymbolWriter { - /// Low end of the interval, holding `15 + cnt` bits not yet moved to `out` (plus carries). - low: u64, - rng: u32, - cnt: u32, - out: Vec, - pub disable_update: bool, -} - -impl SymbolWriter { - pub fn new(disable_update: bool) -> Self { - SymbolWriter { low: 0, rng: 1 << 15, cnt: 0, out: Vec::with_capacity(1 << 16), disable_update } - } - - /// Adds `k` to the bytes already output (carry propagation). - fn carry(&mut self, mut k: u64) { - let mut i = self.out.len(); - while k != 0 { - i -= 1; - let t = self.out[i] as u64 + k; - self.out[i] = t as u8; - k = t >> 8; - } - } - - #[inline(always)] - fn encode(&mut self, cdf: &[u16], n: usize, s: usize) { - let r8 = self.rng >> 8; - let cur = |k: usize| -> u32 { ((r8 * ((32768 - cdf[k] as u32) >> 6)) >> 1) + 4 * (n - k - 1) as u32 }; - let hi = if s == 0 { self.rng } else { cur(s - 1) }; - let lo = cur(s); - debug_assert!(hi > lo, "symbol {s} has an empty interval"); - self.low += lo as u64; - self.rng = hi - lo; - let d = 15 - (31 - self.rng.leading_zeros()); - if d == 0 { - return; - } - self.rng <<= d; - self.low <<= d; - self.cnt += d; - while self.cnt >= 8 { - let sh = 15 + self.cnt - 8; - let c = self.low >> sh; - self.low &= (1u64 << sh) - 1; - self.cnt -= 8; - self.carry(c >> 8); - self.out.push((c & 0xff) as u8); - } - } - - /// Codes `s` with the adaptive CDF `cdf` (N values plus the adaptation counter). - #[inline] - pub fn symbol(&mut self, s: usize, cdf: &mut [u16]) { - let n = cdf.len() - 1; - debug_assert!(s < n); - self.encode(cdf, n, s); - if !self.disable_update { - update_cdf(cdf, n, s); - } - } - - /// Codes `s` with a CDF that is not adapted. - pub fn symbol_fixed(&mut self, s: usize, cdf: &[u16]) { - let n = cdf.len() - 1; - self.encode(cdf, n, s); - } - - /// `read_bool()` counterpart. - pub fn bool(&mut self, b: bool) { - self.encode(&[1 << 14, 1 << 15, 0], 2, b as usize); - } - - /// `L(n)` counterpart (most significant bit first). - #[cfg(test)] - pub fn literal(&mut self, n: u32, v: u32) { - for i in (0..n).rev() { - self.bool((v >> i) & 1 != 0); - } - } - - /// Finishes the tile: returns its bytes (with the trailing one bit and zero padding). - pub fn finish(mut self) -> Vec { - let low = self.low + (1 << 14); - let mut tail = low >> 15; - self.carry(tail >> self.cnt); - tail &= (1u64 << self.cnt) - 1; - let mut bytes: Vec = self.out.iter().map(|b| !b).collect(); - let n = self.cnt + 1; // 1..=8 bits: !tail then the trailing one bit - let v = (((!tail) & ((1u64 << self.cnt) - 1)) << 1) | 1; - bytes.push((v << (8 - n)) as u8); - bytes - } -} - -/// CDF adaptation (spec 8.2.6, as in the decoder). -#[inline(always)] -pub(crate) fn update_cdf(cdf: &mut [u16], n: usize, symbol: usize) { - let count = cdf[n]; - let rate = 3 + (count > 15) as u32 + (count > 31) as u32 + (31 - (n as u32).leading_zeros()).min(2); - let mut tmp = 0u32; - for i in 0..n - 1 { - if i == symbol { - tmp = 1 << 15; - } - let c = cdf[i] as u32; - if tmp < c { - cdf[i] = (c - ((c - tmp) >> rate)) as u16; - } else { - cdf[i] = (c + ((tmp - c) >> rate)) as u16; - } - } - cdf[n] += (count < 32) as u16; -} - -#[cfg(test)] -mod tests { - use super::*; - - /// The spec's symbol decoder (8.2.2 - 8.2.4), for checking the encoder. - struct Dec<'a> { - data: &'a [u8], - pos: usize, - value: u32, - range: u32, - max_bits: i64, - } - - impl<'a> Dec<'a> { - fn new(data: &'a [u8]) -> Self { - let mut d = Dec { data, pos: 0, value: 0, range: 1 << 15, max_bits: 8 * data.len() as i64 - 15 }; - let nb = (data.len() * 8).min(15) as u32; - let buf = d.bits(nb); - d.value = ((1 << 15) - 1) ^ (buf << (15 - nb)); - d - } - fn bits(&mut self, n: u32) -> u32 { - let mut x = 0; - for _ in 0..n { - let b = if self.pos < self.data.len() * 8 { (self.data[self.pos >> 3] >> (7 - (self.pos & 7))) & 1 } else { 0 }; - self.pos += 1; - x = (x << 1) | b as u32; - } - x - } - fn symbol(&mut self, cdf: &mut [u16], adapt: bool) -> usize { - let n = cdf.len() - 1; - let mut cur = self.range; - let mut s = 0; - let mut prev; - loop { - prev = cur; - let f = (1u32 << 15) - cdf[s] as u32; - cur = (((self.range >> 8) * (f >> 6)) >> 1) + 4 * (n - s - 1) as u32; - if self.value >= cur { - break; - } - s += 1; - } - self.range = prev - cur; - self.value -= cur; - let b = 15 - (31 - self.range.leading_zeros()); - if b > 0 { - self.range <<= b; - let nb = (b as i64).min(self.max_bits.max(0)) as u32; - let nd = self.bits(nb) << (b - nb); - self.value = nd ^ (((self.value + 1) << b) - 1); - self.max_bits -= b as i64; - } - if adapt { - update_cdf(cdf, n, s); - } - s - } - /// Position of the trailing one bit (8.2.4). - fn trailing_position(&self) -> i64 { - let consumed = (self.pos as i64).min(self.data.len() as i64 * 8); - consumed - (15i64).min(self.max_bits + 15) - } - } - - #[test] - fn bools_only() { - for len in 1..200 { - let bits: Vec = (0..len).map(|i| (i * 7 + len) % 3 == 0).collect(); - let mut w = SymbolWriter::new(false); - for &b in &bits { - w.bool(b); - } - let data = w.finish(); - let mut d = Dec::new(&data); - for (i, &b) in bits.iter().enumerate() { - assert_eq!(d.symbol(&mut [1 << 14, 1 << 15, 0], false) == 1, b, "len {len} bit {i} data {data:?}"); - } - } - } - - #[test] - fn round_trip_random_symbols() { - let mut seed = 0x1234_5678u32; - let mut rnd = || { - seed ^= seed << 13; - seed ^= seed >> 17; - seed ^= seed << 5; - seed - }; - for trial in 0..300 { - let count = 1 + (rnd() % 3000) as usize; - let mut cdfs_e = vec![[4096u16, 11264, 19328, 32768, 0]; 4]; - let mut cdfs_d = cdfs_e.clone(); - let mut syms = Vec::new(); - let mut w = SymbolWriter::new(false); - for _ in 0..count { - let kind = rnd() % 3; - let ctx = (rnd() % 4) as usize; - // skewed symbols so that the range also gets small - let s = if rnd() % 4 == 0 { (rnd() % 4) as usize } else { 0 }; - match kind { - 0 => w.symbol(s, &mut cdfs_e[ctx]), - 1 => w.bool(s & 1 == 1), - _ => w.literal(5, s as u32 * 7), - } - syms.push((kind, ctx, s)); - } - let data = w.finish(); - let mut d = Dec::new(&data); - for (idx, &(kind, ctx, s)) in syms.iter().enumerate() { - let got = match kind { - 0 => d.symbol(&mut cdfs_d[ctx], true), - 1 => d.symbol(&mut [1 << 14, 1 << 15, 0], false), - _ => { - let mut v = 0; - for _ in 0..5 { - v = (v << 1) | d.symbol(&mut [1 << 14, 1 << 15, 0], false); - } - v / 7 - } - }; - let want = if kind == 1 { s & 1 } else { s }; - assert_eq!(got, want, "trial {trial} idx {idx} kind {kind} of {}", syms.len()); - } - let tp = d.trailing_position(); - assert!(tp >= 0 && (tp as usize) < data.len() * 8, "trial {trial}: trailing bit position {tp}"); - let tp = tp as usize; - assert_eq!((data[tp >> 3] >> (7 - (tp & 7))) & 1, 1, "trial {trial}: trailing one bit"); - for p in tp + 1..data.len() * 8 { - assert_eq!((data[p >> 3] >> (7 - (p & 7))) & 1, 0, "trial {trial}: padding"); - } - } - } -} diff --git a/crates/av1enc/src/tables.rs b/crates/av1enc/src/tables.rs deleted file mode 100644 index 3e333a519..000000000 --- a/crates/av1enc/src/tables.rs +++ /dev/null @@ -1,4805 +0,0 @@ -// Tables mirrored from filmcraft-av1's spec_tables.rs, which extracts them from the AV1 Bitstream & -// Decoding Process Specification v1.0.0 with Errata 1. Only the tables this encoder uses are kept. -#![allow(dead_code, clippy::unreadable_literal)] - -pub const ADST_ADST: usize = 3; -pub const ADST_DCT: usize = 1; -pub const ADST_FLIPADST: usize = 7; -pub const AFFINE: usize = 3; -pub const ALTREF2_FRAME: usize = 6; -pub const ALTREF_FRAME: usize = 7; -pub const ANGLE_STEP: usize = 3; -pub const BIDIR_COMP_REFERENCE: usize = 1; -pub const BILINEAR: usize = 3; -pub const BLOCK_128X128: usize = 15; -pub const BLOCK_128X64: usize = 14; -pub const BLOCK_16X16: usize = 6; -pub const BLOCK_16X32: usize = 7; -pub const BLOCK_16X4: usize = 17; -pub const BLOCK_16X64: usize = 20; -pub const BLOCK_16X8: usize = 5; -pub const BLOCK_32X16: usize = 8; -pub const BLOCK_32X32: usize = 9; -pub const BLOCK_32X64: usize = 10; -pub const BLOCK_32X8: usize = 19; -pub const BLOCK_4X16: usize = 16; -pub const BLOCK_4X4: usize = 0; -pub const BLOCK_4X8: usize = 1; -pub const BLOCK_64X128: usize = 13; -pub const BLOCK_64X16: usize = 21; -pub const BLOCK_64X32: usize = 11; -pub const BLOCK_64X64: usize = 12; -pub const BLOCK_8X16: usize = 4; -pub const BLOCK_8X32: usize = 18; -pub const BLOCK_8X4: usize = 2; -pub const BLOCK_8X8: usize = 3; -pub const BLOCK_INVALID: usize = 22; -pub const BLOCK_SIZES: usize = 22; -pub const BLOCK_SIZE_GROUPS: usize = 4; -pub const BR_CDF_SIZE: usize = 4; -pub const BUFFER_POOL_MAX_SIZE: usize = 10; -pub const BWDREF_FRAME: usize = 5; -pub const BWD_REFS: usize = 3; -pub const CFL_ALPHABET_SIZE: usize = 16; -pub const CFL_ALPHA_CONTEXTS: usize = 6; -pub const CFL_JOINT_SIGNS: usize = 8; -pub const CFL_SIGN_NEG: usize = 1; -pub const CFL_SIGN_POS: usize = 2; -pub const CFL_SIGN_ZERO: usize = 0; -pub const CLASS0_SIZE: usize = 2; -pub const COEFF_BASE_RANGE: usize = 12; -pub const COEFF_CDF_Q_CTXS: usize = 4; -pub const COMPOUND_AVERAGE: usize = 2; -pub const COMPOUND_DIFFWTD: usize = 1; -pub const COMPOUND_DISTANCE: usize = 4; -pub const COMPOUND_IDX_CONTEXTS: usize = 6; -pub const COMPOUND_INTRA: usize = 3; -pub const COMPOUND_MODES: usize = 8; -pub const COMPOUND_MODE_CONTEXTS: usize = 8; -pub const COMPOUND_REFERENCE: usize = 1; -pub const COMPOUND_TYPES: usize = 2; -pub const COMPOUND_WEDGE: usize = 0; -pub const COMP_GROUP_IDX_CONTEXTS: usize = 6; -pub const COMP_INTER_CONTEXTS: usize = 5; -pub const COMP_NEWMV_CTXS: usize = 5; -pub const COMP_REF_TYPE_CONTEXTS: usize = 5; -pub const CP_BT_2020: usize = 9; -pub const CP_BT_470_B_G: usize = 5; -pub const CP_BT_470_M: usize = 4; -pub const CP_BT_601: usize = 6; -pub const CP_BT_709: usize = 1; -pub const CP_EBU_3213: usize = 22; -pub const CP_GENERIC_FILM: usize = 8; -pub const CP_SMPTE_240: usize = 7; -pub const CP_SMPTE_431: usize = 11; -pub const CP_SMPTE_432: usize = 12; -pub const CP_UNSPECIFIED: usize = 2; -pub const CP_XYZ: usize = 10; -pub const CSP_COLOCATED: usize = 2; -pub const CSP_RESERVED: usize = 3; -pub const CSP_UNKNOWN: usize = 0; -pub const CSP_VERTICAL: usize = 1; -pub const D113_PRED: usize = 5; -pub const D135_PRED: usize = 4; -pub const D157_PRED: usize = 6; -pub const D203_PRED: usize = 7; -pub const D45_PRED: usize = 3; -pub const D67_PRED: usize = 8; -pub const DCT_ADST: usize = 2; -pub const DCT_DCT: usize = 0; -pub const DCT_FLIPADST: usize = 5; -pub const DC_PRED: usize = 0; -pub const DC_SIGN_CONTEXTS: usize = 3; -pub const DELTA_LF_SMALL: usize = 3; -pub const DELTA_Q_SMALL: usize = 3; -pub const DIRECTIONAL_MODES: usize = 8; -pub const DIV_LUT_BITS: usize = 8; -pub const DIV_LUT_NUM: usize = 257; -pub const DIV_LUT_PREC_BITS: usize = 14; -pub const DRL_MODE_CONTEXTS: usize = 3; -pub const EC_MIN_PROB: usize = 4; -pub const EC_PROB_SHIFT: usize = 6; -pub const EIGHTTAP: usize = 0; -pub const EIGHTTAP_SHARP: usize = 2; -pub const EIGHTTAP_SMOOTH: usize = 1; -pub const EOB_COEF_CONTEXTS: usize = 9; -pub const FILTER_BITS: usize = 7; -pub const FILTER_D157_PRED: usize = 3; -pub const FILTER_DC_PRED: usize = 0; -pub const FILTER_H_PRED: usize = 2; -pub const FILTER_PAETH_PRED: usize = 4; -pub const FILTER_V_PRED: usize = 1; -pub const FLIPADST_ADST: usize = 8; -pub const FLIPADST_DCT: usize = 4; -pub const FLIPADST_FLIPADST: usize = 6; -pub const FRAME_LF_COUNT: usize = 4; -pub const FWD_REFS: usize = 4; -pub const GLOBALMV: usize = 16; -pub const GLOBAL_GLOBALMV: usize = 24; -pub const GM_ABS_ALPHA_BITS: usize = 12; -pub const GM_ABS_TRANS_BITS: usize = 12; -pub const GM_ABS_TRANS_ONLY_BITS: usize = 9; -pub const GM_ALPHA_PREC_BITS: usize = 15; -pub const GM_TRANS_ONLY_PREC_BITS: usize = 3; -pub const GM_TRANS_PREC_BITS: usize = 6; -pub const GOLDEN_FRAME: usize = 4; -pub const H_ADST: usize = 13; -pub const H_DCT: usize = 11; -pub const H_FLIPADST: usize = 15; -pub const H_PRED: usize = 2; -pub const IDENTITY: usize = 0; -pub const IDTX: usize = 9; -pub const II_DC_PRED: usize = 0; -pub const II_H_PRED: usize = 2; -pub const II_SMOOTH_PRED: usize = 3; -pub const II_V_PRED: usize = 1; -pub const INTERINTRA_MODES: usize = 4; -pub const INTERP_FILTERS: usize = 3; -pub const INTERP_FILTER_CONTEXTS: usize = 16; -pub const INTER_FRAME: usize = 1; -pub const INTRABC_DELAY_PIXELS: usize = 256; -pub const INTRABC_DELAY_SB64: usize = 4; -pub const INTRA_EDGE_KERNELS: usize = 3; -pub const INTRA_EDGE_TAPS: usize = 5; -pub const INTRA_FILTER_MODES: usize = 5; -pub const INTRA_FILTER_SCALE_BITS: usize = 4; -pub const INTRA_FRAME: usize = 0; -pub const INTRA_MODES: usize = 13; -pub const INTRA_MODE_CONTEXTS: usize = 5; -pub const INTRA_ONLY_FRAME: usize = 2; -pub const IS_INTER_CONTEXTS: usize = 4; -pub const KEY_FRAME: usize = 0; -pub const LAST2_FRAME: usize = 2; -pub const LAST3_FRAME: usize = 3; -pub const LAST_FRAME: usize = 1; -pub const LEAST_SQUARES_SAMPLES_MAX: usize = 8; -pub const LEVEL_CONTEXTS: usize = 21; -pub const LOCALWARP: usize = 2; -pub const LS_MV_MAX: usize = 256; -pub const MASK_MASTER_SIZE: usize = 64; -pub const MAX_ANGLE_DELTA: usize = 3; -pub const MAX_FRAME_DISTANCE: usize = 31; -pub const MAX_LOOP_FILTER: usize = 63; -pub const MAX_OFFSET_HEIGHT: usize = 0; -pub const MAX_OFFSET_WIDTH: usize = 8; -pub const MAX_REF_MV_STACK_SIZE: usize = 8; -pub const MAX_SB_SIZE: usize = 128; -pub const MAX_SEGMENTS: usize = 8; -pub const MAX_TILE_AREA: usize = 9437184; -pub const MAX_TILE_COLS: usize = 64; -pub const MAX_TILE_ROWS: usize = 64; -pub const MAX_TILE_WIDTH: usize = 4096; -pub const MAX_TX_DEPTH: usize = 2; -pub const MAX_VARTX_DEPTH: usize = 2; -pub const MB_MODE_COUNT: usize = 17; -pub const MC_BT_2020_CL: usize = 10; -pub const MC_BT_2020_NCL: usize = 9; -pub const MC_BT_470_B_G: usize = 5; -pub const MC_BT_601: usize = 6; -pub const MC_BT_709: usize = 1; -pub const MC_CHROMAT_CL: usize = 13; -pub const MC_CHROMAT_NCL: usize = 12; -pub const MC_FCC: usize = 4; -pub const MC_ICTCP: usize = 14; -pub const MC_IDENTITY: usize = 0; -pub const MC_RESERVED_3: usize = 3; -pub const MC_SMPTE_2085: usize = 11; -pub const MC_SMPTE_240: usize = 7; -pub const MC_SMPTE_YCGCO: usize = 8; -pub const MC_UNSPECIFIED: usize = 2; -pub const METADATA_TYPE_HDR_CLL: usize = 1; -pub const METADATA_TYPE_HDR_MDCV: usize = 2; -pub const METADATA_TYPE_ITUT_T35: usize = 4; -pub const METADATA_TYPE_SCALABILITY: usize = 3; -pub const METADATA_TYPE_TIMECODE: usize = 5; -pub const MFMV_STACK_SIZE: usize = 3; -pub const MI_SIZE: usize = 4; -pub const MI_SIZE_LOG2: usize = 2; -pub const MOTION_MODES: usize = 3; -pub const MV_BORDER: usize = 128; -pub const MV_CLASSES: usize = 11; -pub const MV_CLASS_0: usize = 0; -pub const MV_CLASS_1: usize = 1; -pub const MV_CLASS_10: usize = 10; -pub const MV_CLASS_2: usize = 2; -pub const MV_CLASS_3: usize = 3; -pub const MV_CLASS_4: usize = 4; -pub const MV_CLASS_5: usize = 5; -pub const MV_CLASS_6: usize = 6; -pub const MV_CLASS_7: usize = 7; -pub const MV_CLASS_8: usize = 8; -pub const MV_CLASS_9: usize = 9; -pub const MV_CONTEXTS: usize = 2; -pub const MV_INTRABC_CONTEXT: usize = 1; -pub const MV_JOINTS: usize = 4; -pub const MV_JOINT_HNZVNZ: usize = 3; -pub const MV_JOINT_HNZVZ: usize = 1; -pub const MV_JOINT_HZVNZ: usize = 2; -pub const MV_JOINT_ZERO: usize = 0; -pub const MV_OFFSET_BITS: usize = 10; -pub const NEARESTMV: usize = 14; -pub const NEAREST_NEARESTMV: usize = 18; -pub const NEAREST_NEWMV: usize = 20; -pub const NEARMV: usize = 15; -pub const NEAR_NEARMV: usize = 19; -pub const NEAR_NEWMV: usize = 22; -pub const NEWMV: usize = 17; -pub const NEW_MV_CONTEXTS: usize = 6; -pub const NEW_NEARESTMV: usize = 21; -pub const NEW_NEARMV: usize = 23; -pub const NEW_NEWMV: usize = 25; -pub const NUM_BASE_LEVELS: usize = 2; -pub const NUM_REF_FRAMES: usize = 8; -pub const OBMC: usize = 1; -pub const OBU_FRAME: usize = 6; -pub const OBU_FRAME_HEADER: usize = 3; -pub const OBU_METADATA: usize = 5; -pub const OBU_PADDING: usize = 15; -pub const OBU_REDUNDANT_FRAME_HEADER: usize = 7; -pub const OBU_SEQUENCE_HEADER: usize = 1; -pub const OBU_TEMPORAL_DELIMITER: usize = 2; -pub const OBU_TILE_GROUP: usize = 4; -pub const OBU_TILE_LIST: usize = 8; -pub const ONLY_4X4: usize = 0; -pub const PAETH_PRED: usize = 12; -pub const PALETTE_BLOCK_SIZE_CONTEXTS: usize = 7; -pub const PALETTE_COLORS: usize = 8; -pub const PALETTE_COLOR_CONTEXTS: usize = 5; -pub const PALETTE_MAX_COLOR_CONTEXT_HASH: usize = 8; -pub const PALETTE_NUM_NEIGHBORS: usize = 3; -pub const PALETTE_SIZES: usize = 7; -pub const PALETTE_UV_MODE_CONTEXTS: usize = 2; -pub const PALETTE_Y_MODE_CONTEXTS: usize = 3; -pub const PARTITION_CONTEXTS: usize = 4; -pub const PARTITION_HORZ: usize = 1; -pub const PARTITION_HORZ_4: usize = 8; -pub const PARTITION_HORZ_A: usize = 4; -pub const PARTITION_HORZ_B: usize = 5; -pub const PARTITION_NONE: usize = 0; -pub const PARTITION_SPLIT: usize = 3; -pub const PARTITION_VERT: usize = 2; -pub const PARTITION_VERT_4: usize = 9; -pub const PARTITION_VERT_A: usize = 6; -pub const PARTITION_VERT_B: usize = 7; -pub const PLANE_TYPES: usize = 2; -pub const PRIMARY_REF_NONE: usize = 7; -pub const QM_TOTAL_SIZE: usize = 3344; -pub const REFMVS_LIMIT: usize = 4095; -pub const REFS_PER_FRAME: usize = 7; -pub const REF_CAT_LEVEL: usize = 640; -pub const REF_CONTEXTS: usize = 3; -pub const REF_MV_CONTEXTS: usize = 6; -pub const REF_SCALE_SHIFT: usize = 14; -pub const RESTORATION_TILESIZE_MAX: usize = 256; -pub const RESTORE_NONE: usize = 0; -pub const RESTORE_SGRPROJ: usize = 2; -pub const RESTORE_SWITCHABLE: usize = 3; -pub const RESTORE_WIENER: usize = 1; -pub const ROTZOOM: usize = 2; -pub const SCALABILITY_L1T2: usize = 0; -pub const SCALABILITY_L1T3: usize = 1; -pub const SCALABILITY_L2T1: usize = 2; -pub const SCALABILITY_L2T2: usize = 3; -pub const SCALABILITY_L2T3: usize = 4; -pub const SCALABILITY_L3T1: usize = 15; -pub const SCALABILITY_L3T2: usize = 16; -pub const SCALABILITY_L3T2_KEY: usize = 21; -pub const SCALABILITY_L3T2_KEY_SHIFT: usize = 25; -pub const SCALABILITY_L3T3: usize = 17; -pub const SCALABILITY_L3T3_KEY: usize = 22; -pub const SCALABILITY_L3T3_KEY_SHIFT: usize = 26; -pub const SCALABILITY_L4T5_KEY: usize = 23; -pub const SCALABILITY_L4T5_KEY_SHIFT: usize = 27; -pub const SCALABILITY_L4T7_KEY: usize = 24; -pub const SCALABILITY_L4T7_KEY_SHIFT: usize = 28; -pub const SCALABILITY_S2T1: usize = 5; -pub const SCALABILITY_S2T2: usize = 6; -pub const SCALABILITY_S2T3: usize = 7; -pub const SCALABILITY_S3T1: usize = 18; -pub const SCALABILITY_S3T2: usize = 19; -pub const SCALABILITY_S3T3: usize = 20; -pub const SCALABILITY_SS: usize = 14; -pub const SCALE_SUBPEL_BITS: usize = 10; -pub const SEGMENT_ID_CONTEXTS: usize = 3; -pub const SEGMENT_ID_PREDICTED_CONTEXTS: usize = 3; -pub const SEG_LVL_ALT_LF_Y_V: usize = 1; -pub const SEG_LVL_ALT_Q: usize = 0; -pub const SEG_LVL_GLOBALMV: usize = 7; -pub const SEG_LVL_MAX: usize = 8; -pub const SEG_LVL_REF_FRAME: usize = 5; -pub const SEG_LVL_SKIP: usize = 6; -pub const SELECT_INTEGER_MV: usize = 2; -pub const SELECT_SCREEN_CONTENT_TOOLS: usize = 2; -pub const SGRPROJ_MTABLE_BITS: usize = 20; -pub const SGRPROJ_PARAMS_BITS: usize = 4; -pub const SGRPROJ_PRJ_BITS: usize = 7; -pub const SGRPROJ_PRJ_SUBEXP_K: usize = 4; -pub const SGRPROJ_RECIP_BITS: usize = 12; -pub const SGRPROJ_RST_BITS: usize = 4; -pub const SGRPROJ_SGR_BITS: usize = 8; -pub const SIG_COEF_CONTEXTS: usize = 42; -pub const SIG_COEF_CONTEXTS_2D: usize = 26; -pub const SIG_COEF_CONTEXTS_EOB: usize = 4; -pub const SIG_REF_DIFF_OFFSET_NUM: usize = 5; -pub const SIMPLE: usize = 0; -pub const SINGLE_REFERENCE: usize = 0; -pub const SINGLE_REFS: usize = 7; -pub const SINPI_1_9: usize = 1321; -pub const SINPI_2_9: usize = 2482; -pub const SINPI_3_9: usize = 3344; -pub const SINPI_4_9: usize = 3803; -pub const SKIP_CONTEXTS: usize = 3; -pub const SKIP_MODE_CONTEXTS: usize = 3; -pub const SMOOTH_H_PRED: usize = 11; -pub const SMOOTH_PRED: usize = 9; -pub const SMOOTH_V_PRED: usize = 10; -pub const SUBPEL_BITS: usize = 4; -pub const SUBPEL_MASK: usize = 15; -pub const SUPERRES_DENOM_BITS: usize = 3; -pub const SUPERRES_DENOM_MIN: usize = 9; -pub const SUPERRES_EXTRA_BITS: usize = 8; -pub const SUPERRES_FILTER_BITS: usize = 6; -pub const SUPERRES_FILTER_OFFSET: usize = 3; -pub const SUPERRES_FILTER_SHIFTS: usize = 64; -pub const SUPERRES_FILTER_TAPS: usize = 8; -pub const SUPERRES_NUM: usize = 8; -pub const SUPERRES_SCALE_BITS: usize = 14; -pub const SUPERRES_SCALE_MASK: usize = 16383; -pub const SWITCHABLE: usize = 4; -pub const SWITCH_FRAME: usize = 3; -pub const TC_BT_1361: usize = 12; -pub const TC_BT_2020_10_BIT: usize = 14; -pub const TC_BT_2020_12_BIT: usize = 15; -pub const TC_BT_470_B_G: usize = 5; -pub const TC_BT_470_M: usize = 4; -pub const TC_BT_601: usize = 6; -pub const TC_BT_709: usize = 1; -pub const TC_HLG: usize = 18; -pub const TC_IEC_61966: usize = 11; -pub const TC_LINEAR: usize = 8; -pub const TC_LOG_100: usize = 9; -pub const TC_LOG_100_SQRT10: usize = 10; -pub const TC_RESERVED_0: usize = 0; -pub const TC_RESERVED_3: usize = 3; -pub const TC_SMPTE_2084: usize = 16; -pub const TC_SMPTE_240: usize = 7; -pub const TC_SMPTE_428: usize = 17; -pub const TC_SRGB: usize = 13; -pub const TC_UNSPECIFIED: usize = 2; -pub const TOTAL_REFS_PER_FRAME: usize = 8; -pub const TRANSLATION: usize = 1; -pub const TXB_SKIP_CONTEXTS: usize = 13; -pub const TXFM_PARTITION_CONTEXTS: usize = 21; -pub const TX_16X16: usize = 2; -pub const TX_16X32: usize = 9; -pub const TX_16X4: usize = 14; -pub const TX_16X64: usize = 17; -pub const TX_16X8: usize = 8; -pub const TX_32X16: usize = 10; -pub const TX_32X32: usize = 3; -pub const TX_32X64: usize = 11; -pub const TX_32X8: usize = 16; -pub const TX_4X16: usize = 13; -pub const TX_4X4: usize = 0; -pub const TX_4X8: usize = 5; -pub const TX_64X16: usize = 18; -pub const TX_64X32: usize = 12; -pub const TX_64X64: usize = 4; -pub const TX_8X16: usize = 7; -pub const TX_8X32: usize = 15; -pub const TX_8X4: usize = 6; -pub const TX_8X8: usize = 1; -pub const TX_CLASS_2D: usize = 0; -pub const TX_CLASS_HORIZ: usize = 1; -pub const TX_CLASS_VERT: usize = 2; -pub const TX_MODES: usize = 3; -pub const TX_MODE_LARGEST: usize = 1; -pub const TX_MODE_SELECT: usize = 2; -pub const TX_SET_DCTONLY: usize = 0; -pub const TX_SET_INTER_1: usize = 1; -pub const TX_SET_INTER_2: usize = 2; -pub const TX_SET_INTER_3: usize = 3; -pub const TX_SET_INTRA_1: usize = 1; -pub const TX_SET_INTRA_2: usize = 2; -pub const TX_SET_TYPES_INTER: usize = 4; -pub const TX_SET_TYPES_INTRA: usize = 3; -pub const TX_SIZES: usize = 5; -pub const TX_SIZES_ALL: usize = 19; -pub const TX_SIZE_CONTEXTS: usize = 3; -pub const TX_TYPES: usize = 16; -pub const UNIDIR_COMP_REFERENCE: usize = 0; -pub const UNIDIR_COMP_REFS: usize = 4; -pub const UNIFORM_45: usize = 0; -pub const UNIFORM_45_INV: usize = 1; -pub const UV_CFL_PRED: usize = 13; -pub const UV_INTRA_MODES_CFL_ALLOWED: usize = 14; -pub const UV_INTRA_MODES_CFL_NOT_ALLOWED: usize = 13; -pub const V_ADST: usize = 12; -pub const V_DCT: usize = 10; -pub const V_FLIPADST: usize = 14; -pub const V_PRED: usize = 1; -pub const WARPEDDIFF_PREC_BITS: usize = 10; -pub const WARPEDMODEL_NONDIAGAFFINE_CLAMP: usize = 8192; -pub const WARPEDMODEL_PREC_BITS: usize = 16; -pub const WARPEDMODEL_TRANS_CLAMP: usize = 8388608; -pub const WARPEDPIXEL_PREC_SHIFTS: usize = 64; -pub const WARP_PARAM_REDUCE_BITS: usize = 6; -pub const WEDGE_HORIZONTAL: usize = 0; -pub const WEDGE_OBLIQUE117: usize = 4; -pub const WEDGE_OBLIQUE153: usize = 5; -pub const WEDGE_OBLIQUE27: usize = 2; -pub const WEDGE_OBLIQUE63: usize = 3; -pub const WEDGE_TYPES: usize = 16; -pub const WEDGE_VERTICAL: usize = 1; -pub const WIENER_COEFFS: usize = 3; -pub const ZERO_MV_CONTEXTS: usize = 2; - -pub static AC_QLOOKUP: [[u16; 256]; 3] = [ - [ - 4, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23, 24, 25, 26, 27, 28, 29, 30, 31, 32, 33, 34, 35, 36, 37, 38, 39, 40, 41, 42, 43, 44, - 45, 46, 47, 48, 49, 50, 51, 52, 53, 54, 55, 56, 57, 58, 59, 60, 61, 62, 63, 64, 65, 66, 67, 68, 69, 70, 71, 72, 73, 74, 75, 76, 77, 78, 79, 80, 81, 82, - 83, 84, 85, 86, 87, 88, 89, 90, 91, 92, 93, 94, 95, 96, 97, 98, 99, 100, 101, 102, 104, 106, 108, 110, 112, 114, 116, 118, 120, 122, 124, 126, 128, - 130, 132, 134, 136, 138, 140, 142, 144, 146, 148, 150, 152, 155, 158, 161, 164, 167, 170, 173, 176, 179, 182, 185, 188, 191, 194, 197, 200, 203, 207, - 211, 215, 219, 223, 227, 231, 235, 239, 243, 247, 251, 255, 260, 265, 270, 275, 280, 285, 290, 295, 300, 305, 311, 317, 323, 329, 335, 341, 347, 353, - 359, 366, 373, 380, 387, 394, 401, 408, 416, 424, 432, 440, 448, 456, 465, 474, 483, 492, 501, 510, 520, 530, 540, 550, 560, 571, 582, 593, 604, 615, - 627, 639, 651, 663, 676, 689, 702, 715, 729, 743, 757, 771, 786, 801, 816, 832, 848, 864, 881, 898, 915, 933, 951, 969, 988, 1007, 1026, 1046, 1066, - 1087, 1108, 1129, 1151, 1173, 1196, 1219, 1243, 1267, 1292, 1317, 1343, 1369, 1396, 1423, 1451, 1479, 1508, 1537, 1567, 1597, 1628, 1660, 1692, 1725, - 1759, 1793, 1828, - ], - [ - 4, 9, 11, 13, 16, 18, 21, 24, 27, 30, 33, 37, 40, 44, 48, 51, 55, 59, 63, 67, 71, 75, 79, 83, 88, 92, 96, 100, 105, 109, 114, 118, 122, 127, 131, 136, - 140, 145, 149, 154, 158, 163, 168, 172, 177, 181, 186, 190, 195, 199, 204, 208, 213, 217, 222, 226, 231, 235, 240, 244, 249, 253, 258, 262, 267, 271, - 275, 280, 284, 289, 293, 297, 302, 306, 311, 315, 319, 324, 328, 332, 337, 341, 345, 349, 354, 358, 362, 367, 371, 375, 379, 384, 388, 392, 396, 401, - 409, 417, 425, 433, 441, 449, 458, 466, 474, 482, 490, 498, 506, 514, 523, 531, 539, 547, 555, 563, 571, 579, 588, 596, 604, 616, 628, 640, 652, 664, - 676, 688, 700, 713, 725, 737, 749, 761, 773, 785, 797, 809, 825, 841, 857, 873, 889, 905, 922, 938, 954, 970, 986, 1002, 1018, 1038, 1058, 1078, 1098, - 1118, 1138, 1158, 1178, 1198, 1218, 1242, 1266, 1290, 1314, 1338, 1362, 1386, 1411, 1435, 1463, 1491, 1519, 1547, 1575, 1603, 1631, 1663, 1695, 1727, - 1759, 1791, 1823, 1859, 1895, 1931, 1967, 2003, 2039, 2079, 2119, 2159, 2199, 2239, 2283, 2327, 2371, 2415, 2459, 2507, 2555, 2603, 2651, 2703, 2755, - 2807, 2859, 2915, 2971, 3027, 3083, 3143, 3203, 3263, 3327, 3391, 3455, 3523, 3591, 3659, 3731, 3803, 3876, 3952, 4028, 4104, 4184, 4264, 4348, 4432, - 4516, 4604, 4692, 4784, 4876, 4972, 5068, 5168, 5268, 5372, 5476, 5584, 5692, 5804, 5916, 6032, 6148, 6268, 6388, 6512, 6640, 6768, 6900, 7036, 7172, - 7312, - ], - [ - 4, 13, 19, 27, 35, 44, 54, 64, 75, 87, 99, 112, 126, 139, 154, 168, 183, 199, 214, 230, 247, 263, 280, 297, 314, 331, 349, 366, 384, 402, 420, 438, - 456, 475, 493, 511, 530, 548, 567, 586, 604, 623, 642, 660, 679, 698, 716, 735, 753, 772, 791, 809, 828, 846, 865, 884, 902, 920, 939, 957, 976, 994, - 1012, 1030, 1049, 1067, 1085, 1103, 1121, 1139, 1157, 1175, 1193, 1211, 1229, 1246, 1264, 1282, 1299, 1317, 1335, 1352, 1370, 1387, 1405, 1422, 1440, - 1457, 1474, 1491, 1509, 1526, 1543, 1560, 1577, 1595, 1627, 1660, 1693, 1725, 1758, 1791, 1824, 1856, 1889, 1922, 1954, 1987, 2020, 2052, 2085, 2118, - 2150, 2183, 2216, 2248, 2281, 2313, 2346, 2378, 2411, 2459, 2508, 2556, 2605, 2653, 2701, 2750, 2798, 2847, 2895, 2943, 2992, 3040, 3088, 3137, 3185, - 3234, 3298, 3362, 3426, 3491, 3555, 3619, 3684, 3748, 3812, 3876, 3941, 4005, 4069, 4149, 4230, 4310, 4390, 4470, 4550, 4631, 4711, 4791, 4871, 4967, - 5064, 5160, 5256, 5352, 5448, 5544, 5641, 5737, 5849, 5961, 6073, 6185, 6297, 6410, 6522, 6650, 6778, 6906, 7034, 7162, 7290, 7435, 7579, 7723, 7867, - 8011, 8155, 8315, 8475, 8635, 8795, 8956, 9132, 9308, 9484, 9660, 9836, 10028, 10220, 10412, 10604, 10812, 11020, 11228, 11437, 11661, 11885, 12109, - 12333, 12573, 12813, 13053, 13309, 13565, 13821, 14093, 14365, 14637, 14925, 15213, 15502, 15806, 16110, 16414, 16734, 17054, 17390, 17726, 18062, - 18414, 18766, 19134, 19502, 19886, 20270, 20670, 21070, 21486, 21902, 22334, 22766, 23214, 23662, 24126, 24590, 25070, 25551, 26047, 26559, 27071, - 27599, 28143, 28687, 29247, - ], -]; - -pub static ADJUSTED_TX_SIZE: [u8; 19] = [0, 1, 2, 3, 3, 5, 6, 7, 8, 9, 10, 3, 3, 13, 14, 15, 16, 9, 10]; - -pub static COEFF_BASE_CTX_OFFSET: [[[u8; 5]; 5]; 19] = [ - [[0, 1, 6, 6, 0], [1, 6, 6, 21, 0], [6, 6, 21, 21, 0], [6, 21, 21, 21, 0], [0, 0, 0, 0, 0]], - [[0, 1, 6, 6, 21], [1, 6, 6, 21, 21], [6, 6, 21, 21, 21], [6, 21, 21, 21, 21], [21, 21, 21, 21, 21]], - [[0, 1, 6, 6, 21], [1, 6, 6, 21, 21], [6, 6, 21, 21, 21], [6, 21, 21, 21, 21], [21, 21, 21, 21, 21]], - [[0, 1, 6, 6, 21], [1, 6, 6, 21, 21], [6, 6, 21, 21, 21], [6, 21, 21, 21, 21], [21, 21, 21, 21, 21]], - [[0, 1, 6, 6, 21], [1, 6, 6, 21, 21], [6, 6, 21, 21, 21], [6, 21, 21, 21, 21], [21, 21, 21, 21, 21]], - [[0, 11, 11, 11, 0], [11, 11, 11, 11, 0], [6, 6, 21, 21, 0], [6, 21, 21, 21, 0], [21, 21, 21, 21, 0]], - [[0, 16, 6, 6, 21], [16, 16, 6, 21, 21], [16, 16, 21, 21, 21], [16, 16, 21, 21, 21], [0, 0, 0, 0, 0]], - [[0, 11, 11, 11, 11], [11, 11, 11, 11, 11], [6, 6, 21, 21, 21], [6, 21, 21, 21, 21], [21, 21, 21, 21, 21]], - [[0, 16, 6, 6, 21], [16, 16, 6, 21, 21], [16, 16, 21, 21, 21], [16, 16, 21, 21, 21], [16, 16, 21, 21, 21]], - [[0, 11, 11, 11, 11], [11, 11, 11, 11, 11], [6, 6, 21, 21, 21], [6, 21, 21, 21, 21], [21, 21, 21, 21, 21]], - [[0, 16, 6, 6, 21], [16, 16, 6, 21, 21], [16, 16, 21, 21, 21], [16, 16, 21, 21, 21], [16, 16, 21, 21, 21]], - [[0, 11, 11, 11, 11], [11, 11, 11, 11, 11], [6, 6, 21, 21, 21], [6, 21, 21, 21, 21], [21, 21, 21, 21, 21]], - [[0, 16, 6, 6, 21], [16, 16, 6, 21, 21], [16, 16, 21, 21, 21], [16, 16, 21, 21, 21], [16, 16, 21, 21, 21]], - [[0, 11, 11, 11, 0], [11, 11, 11, 11, 0], [6, 6, 21, 21, 0], [6, 21, 21, 21, 0], [21, 21, 21, 21, 0]], - [[0, 16, 6, 6, 21], [16, 16, 6, 21, 21], [16, 16, 21, 21, 21], [16, 16, 21, 21, 21], [0, 0, 0, 0, 0]], - [[0, 11, 11, 11, 11], [11, 11, 11, 11, 11], [6, 6, 21, 21, 21], [6, 21, 21, 21, 21], [21, 21, 21, 21, 21]], - [[0, 16, 6, 6, 21], [16, 16, 6, 21, 21], [16, 16, 21, 21, 21], [16, 16, 21, 21, 21], [16, 16, 21, 21, 21]], - [[0, 11, 11, 11, 11], [11, 11, 11, 11, 11], [6, 6, 21, 21, 21], [6, 21, 21, 21, 21], [21, 21, 21, 21, 21]], - [[0, 16, 6, 6, 21], [16, 16, 6, 21, 21], [16, 16, 21, 21, 21], [16, 16, 21, 21, 21], [16, 16, 21, 21, 21]], -]; - -pub static COEFF_BASE_POS_CTX_OFFSET: [u8; 3] = [26, 31, 36]; - -pub static COS128_LOOKUP: [u16; 65] = [ - 4096, 4095, 4091, 4085, 4076, 4065, 4052, 4036, 4017, 3996, 3973, 3948, 3920, 3889, 3857, 3822, 3784, 3745, 3703, 3659, 3612, 3564, 3513, 3461, 3406, 3349, - 3290, 3229, 3166, 3102, 3035, 2967, 2896, 2824, 2751, 2675, 2598, 2520, 2440, 2359, 2276, 2191, 2106, 2019, 1931, 1842, 1751, 1660, 1567, 1474, 1380, 1285, - 1189, 1092, 995, 897, 799, 700, 601, 501, 401, 301, 201, 101, 0, -]; - -pub static DC_QLOOKUP: [[u16; 256]; 3] = [ - [ - 4, 8, 8, 9, 10, 11, 12, 12, 13, 14, 15, 16, 17, 18, 19, 19, 20, 21, 22, 23, 24, 25, 26, 26, 27, 28, 29, 30, 31, 32, 32, 33, 34, 35, 36, 37, 38, 38, 39, - 40, 41, 42, 43, 43, 44, 45, 46, 47, 48, 48, 49, 50, 51, 52, 53, 53, 54, 55, 56, 57, 57, 58, 59, 60, 61, 62, 62, 63, 64, 65, 66, 66, 67, 68, 69, 70, 70, - 71, 72, 73, 74, 74, 75, 76, 77, 78, 78, 79, 80, 81, 81, 82, 83, 84, 85, 85, 87, 88, 90, 92, 93, 95, 96, 98, 99, 101, 102, 104, 105, 107, 108, 110, 111, - 113, 114, 116, 117, 118, 120, 121, 123, 125, 127, 129, 131, 134, 136, 138, 140, 142, 144, 146, 148, 150, 152, 154, 156, 158, 161, 164, 166, 169, 172, - 174, 177, 180, 182, 185, 187, 190, 192, 195, 199, 202, 205, 208, 211, 214, 217, 220, 223, 226, 230, 233, 237, 240, 243, 247, 250, 253, 257, 261, 265, - 269, 272, 276, 280, 284, 288, 292, 296, 300, 304, 309, 313, 317, 322, 326, 330, 335, 340, 344, 349, 354, 359, 364, 369, 374, 379, 384, 389, 395, 400, - 406, 411, 417, 423, 429, 435, 441, 447, 454, 461, 467, 475, 482, 489, 497, 505, 513, 522, 530, 539, 549, 559, 569, 579, 590, 602, 614, 626, 640, 654, - 668, 684, 700, 717, 736, 755, 775, 796, 819, 843, 869, 896, 925, 955, 988, 1022, 1058, 1098, 1139, 1184, 1232, 1282, 1336, - ], - [ - 4, 9, 10, 13, 15, 17, 20, 22, 25, 28, 31, 34, 37, 40, 43, 47, 50, 53, 57, 60, 64, 68, 71, 75, 78, 82, 86, 90, 93, 97, 101, 105, 109, 113, 116, 120, - 124, 128, 132, 136, 140, 143, 147, 151, 155, 159, 163, 166, 170, 174, 178, 182, 185, 189, 193, 197, 200, 204, 208, 212, 215, 219, 223, 226, 230, 233, - 237, 241, 244, 248, 251, 255, 259, 262, 266, 269, 273, 276, 280, 283, 287, 290, 293, 297, 300, 304, 307, 310, 314, 317, 321, 324, 327, 331, 334, 337, - 343, 350, 356, 362, 369, 375, 381, 387, 394, 400, 406, 412, 418, 424, 430, 436, 442, 448, 454, 460, 466, 472, 478, 484, 490, 499, 507, 516, 525, 533, - 542, 550, 559, 567, 576, 584, 592, 601, 609, 617, 625, 634, 644, 655, 666, 676, 687, 698, 708, 718, 729, 739, 749, 759, 770, 782, 795, 807, 819, 831, - 844, 856, 868, 880, 891, 906, 920, 933, 947, 961, 975, 988, 1001, 1015, 1030, 1045, 1061, 1076, 1090, 1105, 1120, 1137, 1153, 1170, 1186, 1202, 1218, - 1236, 1253, 1271, 1288, 1306, 1323, 1342, 1361, 1379, 1398, 1416, 1436, 1456, 1476, 1496, 1516, 1537, 1559, 1580, 1601, 1624, 1647, 1670, 1692, 1717, - 1741, 1766, 1791, 1817, 1844, 1871, 1900, 1929, 1958, 1990, 2021, 2054, 2088, 2123, 2159, 2197, 2236, 2276, 2319, 2363, 2410, 2458, 2508, 2561, 2616, - 2675, 2737, 2802, 2871, 2944, 3020, 3102, 3188, 3280, 3375, 3478, 3586, 3702, 3823, 3953, 4089, 4236, 4394, 4559, 4737, 4929, 5130, 5347, - ], - [ - 4, 12, 18, 25, 33, 41, 50, 60, 70, 80, 91, 103, 115, 127, 140, 153, 166, 180, 194, 208, 222, 237, 251, 266, 281, 296, 312, 327, 343, 358, 374, 390, - 405, 421, 437, 453, 469, 484, 500, 516, 532, 548, 564, 580, 596, 611, 627, 643, 659, 674, 690, 706, 721, 737, 752, 768, 783, 798, 814, 829, 844, 859, - 874, 889, 904, 919, 934, 949, 964, 978, 993, 1008, 1022, 1037, 1051, 1065, 1080, 1094, 1108, 1122, 1136, 1151, 1165, 1179, 1192, 1206, 1220, 1234, - 1248, 1261, 1275, 1288, 1302, 1315, 1329, 1342, 1368, 1393, 1419, 1444, 1469, 1494, 1519, 1544, 1569, 1594, 1618, 1643, 1668, 1692, 1717, 1741, 1765, - 1789, 1814, 1838, 1862, 1885, 1909, 1933, 1957, 1992, 2027, 2061, 2096, 2130, 2165, 2199, 2233, 2267, 2300, 2334, 2367, 2400, 2434, 2467, 2499, 2532, - 2575, 2618, 2661, 2704, 2746, 2788, 2830, 2872, 2913, 2954, 2995, 3036, 3076, 3127, 3177, 3226, 3275, 3324, 3373, 3421, 3469, 3517, 3565, 3621, 3677, - 3733, 3788, 3843, 3897, 3951, 4005, 4058, 4119, 4181, 4241, 4301, 4361, 4420, 4479, 4546, 4612, 4677, 4742, 4807, 4871, 4942, 5013, 5083, 5153, 5222, - 5291, 5367, 5442, 5517, 5591, 5665, 5745, 5825, 5905, 5984, 6063, 6149, 6234, 6319, 6404, 6495, 6587, 6678, 6769, 6867, 6966, 7064, 7163, 7269, 7376, - 7483, 7599, 7715, 7832, 7958, 8085, 8214, 8352, 8492, 8635, 8788, 8945, 9104, 9275, 9450, 9639, 9832, 10031, 10245, 10465, 10702, 10946, 11210, 11482, - 11776, 12081, 12409, 12750, 13118, 13501, 13913, 14343, 14807, 15290, 15812, 16356, 16943, 17575, 18237, 18949, 19718, 20521, 21387, - ], -]; - -pub static DEFAULT_ANGLE_DELTA_CDF: [[u16; 8]; 8] = [ - [2180, 5032, 7567, 22776, 26989, 30217, 32768, 0], - [2301, 5608, 8801, 23487, 26974, 30330, 32768, 0], - [3780, 11018, 13699, 19354, 23083, 31286, 32768, 0], - [4581, 11226, 15147, 17138, 21834, 28397, 32768, 0], - [1737, 10927, 14509, 19588, 22745, 28823, 32768, 0], - [2664, 10176, 12485, 17650, 21600, 30495, 32768, 0], - [2240, 11096, 15453, 20341, 22561, 28917, 32768, 0], - [3605, 10428, 12459, 17676, 21244, 30655, 32768, 0], -]; - -pub static DEFAULT_COEFF_BASE_CDF: [[[[[u16; 5]; 42]; 2]; 5]; 4] = [ - [ - [ - [ - [4034, 8930, 12727, 32768, 0], - [18082, 29741, 31877, 32768, 0], - [12596, 26124, 30493, 32768, 0], - [9446, 21118, 27005, 32768, 0], - [6308, 15141, 21279, 32768, 0], - [2463, 6357, 9783, 32768, 0], - [20667, 30546, 31929, 32768, 0], - [13043, 26123, 30134, 32768, 0], - [8151, 18757, 24778, 32768, 0], - [5255, 12839, 18632, 32768, 0], - [2820, 7206, 11161, 32768, 0], - [8192, 16384, 24576, 32768, 0], - [8192, 16384, 24576, 32768, 0], - [8192, 16384, 24576, 32768, 0], - [8192, 16384, 24576, 32768, 0], - [8192, 16384, 24576, 32768, 0], - [8192, 16384, 24576, 32768, 0], - [8192, 16384, 24576, 32768, 0], - [8192, 16384, 24576, 32768, 0], - [8192, 16384, 24576, 32768, 0], - [8192, 16384, 24576, 32768, 0], - [15736, 27553, 30604, 32768, 0], - [11210, 23794, 28787, 32768, 0], - [5947, 13874, 19701, 32768, 0], - [4215, 9323, 13891, 32768, 0], - [2833, 6462, 10059, 32768, 0], - [19605, 30393, 31582, 32768, 0], - [13523, 26252, 30248, 32768, 0], - [8446, 18622, 24512, 32768, 0], - [3818, 10343, 15974, 32768, 0], - [1481, 4117, 6796, 32768, 0], - [22649, 31302, 32190, 32768, 0], - [14829, 27127, 30449, 32768, 0], - [8313, 17702, 23304, 32768, 0], - [3022, 8301, 12786, 32768, 0], - [1536, 4412, 7184, 32768, 0], - [22354, 29774, 31372, 32768, 0], - [14723, 25472, 29214, 32768, 0], - [6673, 13745, 18662, 32768, 0], - [2068, 5766, 9322, 32768, 0], - [8192, 16384, 24576, 32768, 0], - [8192, 16384, 24576, 32768, 0], - ], - [ - [6302, 16444, 21761, 32768, 0], - [23040, 31538, 32475, 32768, 0], - [15196, 28452, 31496, 32768, 0], - [10020, 22946, 28514, 32768, 0], - [6533, 16862, 23501, 32768, 0], - [3538, 9816, 15076, 32768, 0], - [24444, 31875, 32525, 32768, 0], - [15881, 28924, 31635, 32768, 0], - [9922, 22873, 28466, 32768, 0], - [6527, 16966, 23691, 32768, 0], - [4114, 11303, 17220, 32768, 0], - [8192, 16384, 24576, 32768, 0], - [8192, 16384, 24576, 32768, 0], - [8192, 16384, 24576, 32768, 0], - [8192, 16384, 24576, 32768, 0], - [8192, 16384, 24576, 32768, 0], - [8192, 16384, 24576, 32768, 0], - [8192, 16384, 24576, 32768, 0], - [8192, 16384, 24576, 32768, 0], - [8192, 16384, 24576, 32768, 0], - [8192, 16384, 24576, 32768, 0], - [20201, 30770, 32209, 32768, 0], - [14754, 28071, 31258, 32768, 0], - [8378, 20186, 26517, 32768, 0], - [5916, 15299, 21978, 32768, 0], - [4268, 11583, 17901, 32768, 0], - [24361, 32025, 32581, 32768, 0], - [18673, 30105, 31943, 32768, 0], - [10196, 22244, 27576, 32768, 0], - [5495, 14349, 20417, 32768, 0], - [2676, 7415, 11498, 32768, 0], - [24678, 31958, 32585, 32768, 0], - [18629, 29906, 31831, 32768, 0], - [9364, 20724, 26315, 32768, 0], - [4641, 12318, 18094, 32768, 0], - [2758, 7387, 11579, 32768, 0], - [25433, 31842, 32469, 32768, 0], - [18795, 29289, 31411, 32768, 0], - [7644, 17584, 23592, 32768, 0], - [3408, 9014, 15047, 32768, 0], - [8192, 16384, 24576, 32768, 0], - [8192, 16384, 24576, 32768, 0], - ], - ], - [ - [ - [4536, 10072, 14001, 32768, 0], - [25459, 31416, 32206, 32768, 0], - [16605, 28048, 30818, 32768, 0], - [11008, 22857, 27719, 32768, 0], - [6915, 16268, 22315, 32768, 0], - [2625, 6812, 10537, 32768, 0], - [24257, 31788, 32499, 32768, 0], - [16880, 29454, 31879, 32768, 0], - [11958, 25054, 29778, 32768, 0], - [7916, 18718, 25084, 32768, 0], - [3383, 8777, 13446, 32768, 0], - [22720, 31603, 32393, 32768, 0], - [14960, 28125, 31335, 32768, 0], - [9731, 22210, 27928, 32768, 0], - [6304, 15832, 22277, 32768, 0], - [2910, 7818, 12166, 32768, 0], - [20375, 30627, 32131, 32768, 0], - [13904, 27284, 30887, 32768, 0], - [9368, 21558, 27144, 32768, 0], - [5937, 14966, 21119, 32768, 0], - [2667, 7225, 11319, 32768, 0], - [23970, 31470, 32378, 32768, 0], - [17173, 29734, 32018, 32768, 0], - [12795, 25441, 29965, 32768, 0], - [8981, 19680, 25893, 32768, 0], - [4728, 11372, 16902, 32768, 0], - [24287, 31797, 32439, 32768, 0], - [16703, 29145, 31696, 32768, 0], - [10833, 23554, 28725, 32768, 0], - [6468, 16566, 23057, 32768, 0], - [2415, 6562, 10278, 32768, 0], - [26610, 32395, 32659, 32768, 0], - [18590, 30498, 32117, 32768, 0], - [12420, 25756, 29950, 32768, 0], - [7639, 18746, 24710, 32768, 0], - [3001, 8086, 12347, 32768, 0], - [25076, 32064, 32580, 32768, 0], - [17946, 30128, 32028, 32768, 0], - [12024, 24985, 29378, 32768, 0], - [7517, 18390, 24304, 32768, 0], - [3243, 8781, 13331, 32768, 0], - [8192, 16384, 24576, 32768, 0], - ], - [ - [6037, 16771, 21957, 32768, 0], - [24774, 31704, 32426, 32768, 0], - [16830, 28589, 31056, 32768, 0], - [10602, 22828, 27760, 32768, 0], - [6733, 16829, 23071, 32768, 0], - [3250, 8914, 13556, 32768, 0], - [25582, 32220, 32668, 32768, 0], - [18659, 30342, 32223, 32768, 0], - [12546, 26149, 30515, 32768, 0], - [8420, 20451, 26801, 32768, 0], - [4636, 12420, 18344, 32768, 0], - [27581, 32362, 32639, 32768, 0], - [18987, 30083, 31978, 32768, 0], - [11327, 24248, 29084, 32768, 0], - [7264, 17719, 24120, 32768, 0], - [3995, 10768, 16169, 32768, 0], - [25893, 31831, 32487, 32768, 0], - [16577, 28587, 31379, 32768, 0], - [10189, 22748, 28182, 32768, 0], - [6832, 17094, 23556, 32768, 0], - [3708, 10110, 15334, 32768, 0], - [25904, 32282, 32656, 32768, 0], - [19721, 30792, 32276, 32768, 0], - [12819, 26243, 30411, 32768, 0], - [8572, 20614, 26891, 32768, 0], - [5364, 14059, 20467, 32768, 0], - [26580, 32438, 32677, 32768, 0], - [20852, 31225, 32340, 32768, 0], - [12435, 25700, 29967, 32768, 0], - [8691, 20825, 26976, 32768, 0], - [4446, 12209, 17269, 32768, 0], - [27350, 32429, 32696, 32768, 0], - [21372, 30977, 32272, 32768, 0], - [12673, 25270, 29853, 32768, 0], - [9208, 20925, 26640, 32768, 0], - [5018, 13351, 18732, 32768, 0], - [27351, 32479, 32713, 32768, 0], - [21398, 31209, 32387, 32768, 0], - [12162, 25047, 29842, 32768, 0], - [7896, 18691, 25319, 32768, 0], - [4670, 12882, 18881, 32768, 0], - [8192, 16384, 24576, 32768, 0], - ], - ], - [ - [ - [5487, 10460, 13708, 32768, 0], - [21597, 28303, 30674, 32768, 0], - [11037, 21953, 26476, 32768, 0], - [8147, 17962, 22952, 32768, 0], - [5242, 13061, 18532, 32768, 0], - [1889, 5208, 8182, 32768, 0], - [26774, 32133, 32590, 32768, 0], - [17844, 29564, 31767, 32768, 0], - [11690, 24438, 29171, 32768, 0], - [7542, 18215, 24459, 32768, 0], - [2993, 8050, 12319, 32768, 0], - [28023, 32328, 32591, 32768, 0], - [18651, 30126, 31954, 32768, 0], - [12164, 25146, 29589, 32768, 0], - [7762, 18530, 24771, 32768, 0], - [3492, 9183, 13920, 32768, 0], - [27591, 32008, 32491, 32768, 0], - [17149, 28853, 31510, 32768, 0], - [11485, 24003, 28860, 32768, 0], - [7697, 18086, 24210, 32768, 0], - [3075, 7999, 12218, 32768, 0], - [28268, 32482, 32654, 32768, 0], - [19631, 31051, 32404, 32768, 0], - [13860, 27260, 31020, 32768, 0], - [9605, 21613, 27594, 32768, 0], - [4876, 12162, 17908, 32768, 0], - [27248, 32316, 32576, 32768, 0], - [18955, 30457, 32075, 32768, 0], - [11824, 23997, 28795, 32768, 0], - [7346, 18196, 24647, 32768, 0], - [3403, 9247, 14111, 32768, 0], - [29711, 32655, 32735, 32768, 0], - [21169, 31394, 32417, 32768, 0], - [13487, 27198, 30957, 32768, 0], - [8828, 21683, 27614, 32768, 0], - [4270, 11451, 17038, 32768, 0], - [28708, 32578, 32731, 32768, 0], - [20120, 31241, 32482, 32768, 0], - [13692, 27550, 31321, 32768, 0], - [9418, 22514, 28439, 32768, 0], - [4999, 13283, 19462, 32768, 0], - [8192, 16384, 24576, 32768, 0], - ], - [ - [5673, 14302, 19711, 32768, 0], - [26251, 30701, 31834, 32768, 0], - [12782, 23783, 27803, 32768, 0], - [9127, 20657, 25808, 32768, 0], - [6368, 16208, 21462, 32768, 0], - [2465, 7177, 10822, 32768, 0], - [29961, 32563, 32719, 32768, 0], - [18318, 29891, 31949, 32768, 0], - [11361, 24514, 29357, 32768, 0], - [7900, 19603, 25607, 32768, 0], - [4002, 10590, 15546, 32768, 0], - [29637, 32310, 32595, 32768, 0], - [18296, 29913, 31809, 32768, 0], - [10144, 21515, 26871, 32768, 0], - [5358, 14322, 20394, 32768, 0], - [3067, 8362, 13346, 32768, 0], - [28652, 32470, 32676, 32768, 0], - [17538, 30771, 32209, 32768, 0], - [13924, 26882, 30494, 32768, 0], - [10496, 22837, 27869, 32768, 0], - [7236, 16396, 21621, 32768, 0], - [30743, 32687, 32746, 32768, 0], - [23006, 31676, 32489, 32768, 0], - [14494, 27828, 31120, 32768, 0], - [10174, 22801, 28352, 32768, 0], - [6242, 15281, 21043, 32768, 0], - [25817, 32243, 32720, 32768, 0], - [18618, 31367, 32325, 32768, 0], - [13997, 28318, 31878, 32768, 0], - [12255, 26534, 31383, 32768, 0], - [9561, 21588, 28450, 32768, 0], - [28188, 32635, 32724, 32768, 0], - [22060, 32365, 32728, 32768, 0], - [18102, 30690, 32528, 32768, 0], - [14196, 28864, 31999, 32768, 0], - [12262, 25792, 30865, 32768, 0], - [24176, 32109, 32628, 32768, 0], - [18280, 29681, 31963, 32768, 0], - [10205, 23703, 29664, 32768, 0], - [7889, 20025, 27676, 32768, 0], - [6060, 16743, 23970, 32768, 0], - [8192, 16384, 24576, 32768, 0], - ], - ], - [ - [ - [5141, 7096, 8260, 32768, 0], - [27186, 29022, 29789, 32768, 0], - [6668, 12568, 15682, 32768, 0], - [2172, 6181, 8638, 32768, 0], - [1126, 3379, 4531, 32768, 0], - [443, 1361, 2254, 32768, 0], - [26083, 31153, 32436, 32768, 0], - [13486, 24603, 28483, 32768, 0], - [6508, 14840, 19910, 32768, 0], - [3386, 8800, 13286, 32768, 0], - [1530, 4322, 7054, 32768, 0], - [29639, 32080, 32548, 32768, 0], - [15897, 27552, 30290, 32768, 0], - [8588, 20047, 25383, 32768, 0], - [4889, 13339, 19269, 32768, 0], - [2240, 6871, 10498, 32768, 0], - [28165, 32197, 32517, 32768, 0], - [20735, 30427, 31568, 32768, 0], - [14325, 24671, 27692, 32768, 0], - [5119, 12554, 17805, 32768, 0], - [1810, 5441, 8261, 32768, 0], - [31212, 32724, 32748, 32768, 0], - [23352, 31766, 32545, 32768, 0], - [14669, 27570, 31059, 32768, 0], - [8492, 20894, 27272, 32768, 0], - [3644, 10194, 15204, 32768, 0], - [8192, 16384, 24576, 32768, 0], - [8192, 16384, 24576, 32768, 0], - [8192, 16384, 24576, 32768, 0], - [8192, 16384, 24576, 32768, 0], - [8192, 16384, 24576, 32768, 0], - [8192, 16384, 24576, 32768, 0], - [8192, 16384, 24576, 32768, 0], - [8192, 16384, 24576, 32768, 0], - [8192, 16384, 24576, 32768, 0], - [8192, 16384, 24576, 32768, 0], - [8192, 16384, 24576, 32768, 0], - [8192, 16384, 24576, 32768, 0], - [8192, 16384, 24576, 32768, 0], - [8192, 16384, 24576, 32768, 0], - [8192, 16384, 24576, 32768, 0], - [8192, 16384, 24576, 32768, 0], - ], - [ - [2461, 7013, 9371, 32768, 0], - [24749, 29600, 30986, 32768, 0], - [9466, 19037, 22417, 32768, 0], - [3584, 9280, 14400, 32768, 0], - [1505, 3929, 5433, 32768, 0], - [677, 1500, 2736, 32768, 0], - [23987, 30702, 32117, 32768, 0], - [13554, 24571, 29263, 32768, 0], - [6211, 14556, 21155, 32768, 0], - [3135, 10972, 15625, 32768, 0], - [2435, 7127, 11427, 32768, 0], - [31300, 32532, 32550, 32768, 0], - [14757, 30365, 31954, 32768, 0], - [4405, 11612, 18553, 32768, 0], - [580, 4132, 7322, 32768, 0], - [1695, 10169, 14124, 32768, 0], - [30008, 32282, 32591, 32768, 0], - [19244, 30108, 31748, 32768, 0], - [11180, 24158, 29555, 32768, 0], - [5650, 14972, 19209, 32768, 0], - [2114, 5109, 8456, 32768, 0], - [31856, 32716, 32748, 32768, 0], - [23012, 31664, 32572, 32768, 0], - [13694, 26656, 30636, 32768, 0], - [8142, 19508, 26093, 32768, 0], - [4253, 10955, 16724, 32768, 0], - [8192, 16384, 24576, 32768, 0], - [8192, 16384, 24576, 32768, 0], - [8192, 16384, 24576, 32768, 0], - [8192, 16384, 24576, 32768, 0], - [8192, 16384, 24576, 32768, 0], - [8192, 16384, 24576, 32768, 0], - [8192, 16384, 24576, 32768, 0], - [8192, 16384, 24576, 32768, 0], - [8192, 16384, 24576, 32768, 0], - [8192, 16384, 24576, 32768, 0], - [8192, 16384, 24576, 32768, 0], - [8192, 16384, 24576, 32768, 0], - [8192, 16384, 24576, 32768, 0], - [8192, 16384, 24576, 32768, 0], - [8192, 16384, 24576, 32768, 0], - [8192, 16384, 24576, 32768, 0], - ], - ], - [ - [ - [601, 983, 1311, 32768, 0], - [18725, 23406, 28087, 32768, 0], - [5461, 8192, 10923, 32768, 0], - [3781, 15124, 21425, 32768, 0], - [2587, 7761, 12072, 32768, 0], - [106, 458, 810, 32768, 0], - [22282, 29710, 31894, 32768, 0], - [8508, 20926, 25984, 32768, 0], - [3726, 12713, 18083, 32768, 0], - [1620, 7112, 10893, 32768, 0], - [729, 2236, 3495, 32768, 0], - [30163, 32474, 32684, 32768, 0], - [18304, 30464, 32000, 32768, 0], - [11443, 26526, 29647, 32768, 0], - [6007, 15292, 21299, 32768, 0], - [2234, 6703, 8937, 32768, 0], - [30954, 32177, 32571, 32768, 0], - [17363, 29562, 31076, 32768, 0], - [9686, 22464, 27410, 32768, 0], - [8192, 16384, 21390, 32768, 0], - [1755, 8046, 11264, 32768, 0], - [31168, 32734, 32748, 32768, 0], - [22486, 31441, 32471, 32768, 0], - [12833, 25627, 29738, 32768, 0], - [6980, 17379, 23122, 32768, 0], - [3111, 8887, 13479, 32768, 0], - [8192, 16384, 24576, 32768, 0], - [8192, 16384, 24576, 32768, 0], - [8192, 16384, 24576, 32768, 0], - [8192, 16384, 24576, 32768, 0], - [8192, 16384, 24576, 32768, 0], - [8192, 16384, 24576, 32768, 0], - [8192, 16384, 24576, 32768, 0], - [8192, 16384, 24576, 32768, 0], - [8192, 16384, 24576, 32768, 0], - [8192, 16384, 24576, 32768, 0], - [8192, 16384, 24576, 32768, 0], - [8192, 16384, 24576, 32768, 0], - [8192, 16384, 24576, 32768, 0], - [8192, 16384, 24576, 32768, 0], - [8192, 16384, 24576, 32768, 0], - [8192, 16384, 24576, 32768, 0], - ], - [ - [8192, 16384, 24576, 32768, 0], - [8192, 16384, 24576, 32768, 0], - [8192, 16384, 24576, 32768, 0], - [8192, 16384, 24576, 32768, 0], - [8192, 16384, 24576, 32768, 0], - [8192, 16384, 24576, 32768, 0], - [8192, 16384, 24576, 32768, 0], - [8192, 16384, 24576, 32768, 0], - [8192, 16384, 24576, 32768, 0], - [8192, 16384, 24576, 32768, 0], - [8192, 16384, 24576, 32768, 0], - [8192, 16384, 24576, 32768, 0], - [8192, 16384, 24576, 32768, 0], - [8192, 16384, 24576, 32768, 0], - [8192, 16384, 24576, 32768, 0], - [8192, 16384, 24576, 32768, 0], - [8192, 16384, 24576, 32768, 0], - [8192, 16384, 24576, 32768, 0], - [8192, 16384, 24576, 32768, 0], - [8192, 16384, 24576, 32768, 0], - [8192, 16384, 24576, 32768, 0], - [8192, 16384, 24576, 32768, 0], - [8192, 16384, 24576, 32768, 0], - [8192, 16384, 24576, 32768, 0], - [8192, 16384, 24576, 32768, 0], - [8192, 16384, 24576, 32768, 0], - [8192, 16384, 24576, 32768, 0], - [8192, 16384, 24576, 32768, 0], - [8192, 16384, 24576, 32768, 0], - [8192, 16384, 24576, 32768, 0], - [8192, 16384, 24576, 32768, 0], - [8192, 16384, 24576, 32768, 0], - [8192, 16384, 24576, 32768, 0], - [8192, 16384, 24576, 32768, 0], - [8192, 16384, 24576, 32768, 0], - [8192, 16384, 24576, 32768, 0], - [8192, 16384, 24576, 32768, 0], - [8192, 16384, 24576, 32768, 0], - [8192, 16384, 24576, 32768, 0], - [8192, 16384, 24576, 32768, 0], - [8192, 16384, 24576, 32768, 0], - [8192, 16384, 24576, 32768, 0], - ], - ], - ], - [ - [ - [ - [6041, 11854, 15927, 32768, 0], - [20326, 30905, 32251, 32768, 0], - [14164, 26831, 30725, 32768, 0], - [9760, 20647, 26585, 32768, 0], - [6416, 14953, 21219, 32768, 0], - [2966, 7151, 10891, 32768, 0], - [23567, 31374, 32254, 32768, 0], - [14978, 27416, 30946, 32768, 0], - [9434, 20225, 26254, 32768, 0], - [6658, 14558, 20535, 32768, 0], - [3916, 8677, 12989, 32768, 0], - [8192, 16384, 24576, 32768, 0], - [8192, 16384, 24576, 32768, 0], - [8192, 16384, 24576, 32768, 0], - [8192, 16384, 24576, 32768, 0], - [8192, 16384, 24576, 32768, 0], - [8192, 16384, 24576, 32768, 0], - [8192, 16384, 24576, 32768, 0], - [8192, 16384, 24576, 32768, 0], - [8192, 16384, 24576, 32768, 0], - [8192, 16384, 24576, 32768, 0], - [18088, 29545, 31587, 32768, 0], - [13062, 25843, 30073, 32768, 0], - [8940, 16827, 22251, 32768, 0], - [7654, 13220, 17973, 32768, 0], - [5733, 10316, 14456, 32768, 0], - [22879, 31388, 32114, 32768, 0], - [15215, 27993, 30955, 32768, 0], - [9397, 19445, 24978, 32768, 0], - [3442, 9813, 15344, 32768, 0], - [1368, 3936, 6532, 32768, 0], - [25494, 32033, 32406, 32768, 0], - [16772, 27963, 30718, 32768, 0], - [9419, 18165, 23260, 32768, 0], - [2677, 7501, 11797, 32768, 0], - [1516, 4344, 7170, 32768, 0], - [26556, 31454, 32101, 32768, 0], - [17128, 27035, 30108, 32768, 0], - [8324, 15344, 20249, 32768, 0], - [1903, 5696, 9469, 32768, 0], - [8192, 16384, 24576, 32768, 0], - [8192, 16384, 24576, 32768, 0], - ], - [ - [8455, 19003, 24368, 32768, 0], - [23563, 32021, 32604, 32768, 0], - [16237, 29446, 31935, 32768, 0], - [10724, 23999, 29358, 32768, 0], - [6725, 17528, 24416, 32768, 0], - [3927, 10927, 16825, 32768, 0], - [26313, 32288, 32634, 32768, 0], - [17430, 30095, 32095, 32768, 0], - [11116, 24606, 29679, 32768, 0], - [7195, 18384, 25269, 32768, 0], - [4726, 12852, 19315, 32768, 0], - [8192, 16384, 24576, 32768, 0], - [8192, 16384, 24576, 32768, 0], - [8192, 16384, 24576, 32768, 0], - [8192, 16384, 24576, 32768, 0], - [8192, 16384, 24576, 32768, 0], - [8192, 16384, 24576, 32768, 0], - [8192, 16384, 24576, 32768, 0], - [8192, 16384, 24576, 32768, 0], - [8192, 16384, 24576, 32768, 0], - [8192, 16384, 24576, 32768, 0], - [22822, 31648, 32483, 32768, 0], - [16724, 29633, 31929, 32768, 0], - [10261, 23033, 28725, 32768, 0], - [7029, 17840, 24528, 32768, 0], - [4867, 13886, 21502, 32768, 0], - [25298, 31892, 32491, 32768, 0], - [17809, 29330, 31512, 32768, 0], - [9668, 21329, 26579, 32768, 0], - [4774, 12956, 18976, 32768, 0], - [2322, 7030, 11540, 32768, 0], - [25472, 31920, 32543, 32768, 0], - [17957, 29387, 31632, 32768, 0], - [9196, 20593, 26400, 32768, 0], - [4680, 12705, 19202, 32768, 0], - [2917, 8456, 13436, 32768, 0], - [26471, 32059, 32574, 32768, 0], - [18458, 29783, 31909, 32768, 0], - [8400, 19464, 25956, 32768, 0], - [3812, 10973, 17206, 32768, 0], - [8192, 16384, 24576, 32768, 0], - [8192, 16384, 24576, 32768, 0], - ], - ], - [ - [ - [6779, 13743, 17678, 32768, 0], - [24806, 31797, 32457, 32768, 0], - [17616, 29047, 31372, 32768, 0], - [11063, 23175, 28003, 32768, 0], - [6521, 16110, 22324, 32768, 0], - [2764, 7504, 11654, 32768, 0], - [25266, 32367, 32637, 32768, 0], - [19054, 30553, 32175, 32768, 0], - [12139, 25212, 29807, 32768, 0], - [7311, 18162, 24704, 32768, 0], - [3397, 9164, 14074, 32768, 0], - [25988, 32208, 32522, 32768, 0], - [16253, 28912, 31526, 32768, 0], - [9151, 21387, 27372, 32768, 0], - [5688, 14915, 21496, 32768, 0], - [2717, 7627, 12004, 32768, 0], - [23144, 31855, 32443, 32768, 0], - [16070, 28491, 31325, 32768, 0], - [8702, 20467, 26517, 32768, 0], - [5243, 13956, 20367, 32768, 0], - [2621, 7335, 11567, 32768, 0], - [26636, 32340, 32630, 32768, 0], - [19990, 31050, 32341, 32768, 0], - [13243, 26105, 30315, 32768, 0], - [8588, 19521, 25918, 32768, 0], - [4717, 11585, 17304, 32768, 0], - [25844, 32292, 32582, 32768, 0], - [19090, 30635, 32097, 32768, 0], - [11963, 24546, 28939, 32768, 0], - [6218, 16087, 22354, 32768, 0], - [2340, 6608, 10426, 32768, 0], - [28046, 32576, 32694, 32768, 0], - [21178, 31313, 32296, 32768, 0], - [13486, 26184, 29870, 32768, 0], - [7149, 17871, 23723, 32768, 0], - [2833, 7958, 12259, 32768, 0], - [27710, 32528, 32686, 32768, 0], - [20674, 31076, 32268, 32768, 0], - [12413, 24955, 29243, 32768, 0], - [6676, 16927, 23097, 32768, 0], - [2966, 8333, 12919, 32768, 0], - [8192, 16384, 24576, 32768, 0], - ], - [ - [8639, 19339, 24429, 32768, 0], - [24404, 31837, 32525, 32768, 0], - [16997, 29425, 31784, 32768, 0], - [11253, 24234, 29149, 32768, 0], - [6751, 17394, 24028, 32768, 0], - [3490, 9830, 15191, 32768, 0], - [26283, 32471, 32714, 32768, 0], - [19599, 31168, 32442, 32768, 0], - [13146, 26954, 30893, 32768, 0], - [8214, 20588, 26890, 32768, 0], - [4699, 13081, 19300, 32768, 0], - [28212, 32458, 32669, 32768, 0], - [18594, 30316, 32100, 32768, 0], - [11219, 24408, 29234, 32768, 0], - [6865, 17656, 24149, 32768, 0], - [3678, 10362, 16006, 32768, 0], - [25825, 32136, 32616, 32768, 0], - [17313, 29853, 32021, 32768, 0], - [11197, 24471, 29472, 32768, 0], - [6947, 17781, 24405, 32768, 0], - [3768, 10660, 16261, 32768, 0], - [27352, 32500, 32706, 32768, 0], - [20850, 31468, 32469, 32768, 0], - [14021, 27707, 31133, 32768, 0], - [8964, 21748, 27838, 32768, 0], - [5437, 14665, 21187, 32768, 0], - [26304, 32492, 32698, 32768, 0], - [20409, 31380, 32385, 32768, 0], - [13682, 27222, 30632, 32768, 0], - [8974, 21236, 26685, 32768, 0], - [4234, 11665, 16934, 32768, 0], - [26273, 32357, 32711, 32768, 0], - [20672, 31242, 32441, 32768, 0], - [14172, 27254, 30902, 32768, 0], - [9870, 21898, 27275, 32768, 0], - [5164, 13506, 19270, 32768, 0], - [26725, 32459, 32728, 32768, 0], - [20991, 31442, 32527, 32768, 0], - [13071, 26434, 30811, 32768, 0], - [8184, 20090, 26742, 32768, 0], - [4803, 13255, 19895, 32768, 0], - [8192, 16384, 24576, 32768, 0], - ], - ], - [ - [ - [7555, 14942, 18501, 32768, 0], - [24410, 31178, 32287, 32768, 0], - [14394, 26738, 30253, 32768, 0], - [8413, 19554, 25195, 32768, 0], - [4766, 12924, 18785, 32768, 0], - [2029, 5806, 9207, 32768, 0], - [26776, 32364, 32663, 32768, 0], - [18732, 29967, 31931, 32768, 0], - [11005, 23786, 28852, 32768, 0], - [6466, 16909, 23510, 32768, 0], - [3044, 8638, 13419, 32768, 0], - [29208, 32582, 32704, 32768, 0], - [20068, 30857, 32208, 32768, 0], - [12003, 25085, 29595, 32768, 0], - [6947, 17750, 24189, 32768, 0], - [3245, 9103, 14007, 32768, 0], - [27359, 32465, 32669, 32768, 0], - [19421, 30614, 32174, 32768, 0], - [11915, 25010, 29579, 32768, 0], - [6950, 17676, 24074, 32768, 0], - [3007, 8473, 13096, 32768, 0], - [29002, 32676, 32735, 32768, 0], - [22102, 31849, 32576, 32768, 0], - [14408, 28009, 31405, 32768, 0], - [9027, 21679, 27931, 32768, 0], - [4694, 12678, 18748, 32768, 0], - [28216, 32528, 32682, 32768, 0], - [20849, 31264, 32318, 32768, 0], - [12756, 25815, 29751, 32768, 0], - [7565, 18801, 24923, 32768, 0], - [3509, 9533, 14477, 32768, 0], - [30133, 32687, 32739, 32768, 0], - [23063, 31910, 32515, 32768, 0], - [14588, 28051, 31132, 32768, 0], - [9085, 21649, 27457, 32768, 0], - [4261, 11654, 17264, 32768, 0], - [29518, 32691, 32748, 32768, 0], - [22451, 31959, 32613, 32768, 0], - [14864, 28722, 31700, 32768, 0], - [9695, 22964, 28716, 32768, 0], - [4932, 13358, 19502, 32768, 0], - [8192, 16384, 24576, 32768, 0], - ], - [ - [6465, 16958, 21688, 32768, 0], - [25199, 31514, 32360, 32768, 0], - [14774, 27149, 30607, 32768, 0], - [9257, 21438, 26972, 32768, 0], - [5723, 15183, 21882, 32768, 0], - [3150, 8879, 13731, 32768, 0], - [26989, 32262, 32682, 32768, 0], - [17396, 29937, 32085, 32768, 0], - [11387, 24901, 29784, 32768, 0], - [7289, 18821, 25548, 32768, 0], - [3734, 10577, 16086, 32768, 0], - [29728, 32501, 32695, 32768, 0], - [17431, 29701, 31903, 32768, 0], - [9921, 22826, 28300, 32768, 0], - [5896, 15434, 22068, 32768, 0], - [3430, 9646, 14757, 32768, 0], - [28614, 32511, 32705, 32768, 0], - [19364, 30638, 32263, 32768, 0], - [13129, 26254, 30402, 32768, 0], - [8754, 20484, 26440, 32768, 0], - [4378, 11607, 17110, 32768, 0], - [30292, 32671, 32744, 32768, 0], - [21780, 31603, 32501, 32768, 0], - [14314, 27829, 31291, 32768, 0], - [9611, 22327, 28263, 32768, 0], - [4890, 13087, 19065, 32768, 0], - [25862, 32567, 32733, 32768, 0], - [20794, 32050, 32567, 32768, 0], - [17243, 30625, 32254, 32768, 0], - [13283, 27628, 31474, 32768, 0], - [9669, 22532, 28918, 32768, 0], - [27435, 32697, 32748, 32768, 0], - [24922, 32390, 32714, 32768, 0], - [21449, 31504, 32536, 32768, 0], - [16392, 29729, 31832, 32768, 0], - [11692, 24884, 29076, 32768, 0], - [24193, 32290, 32735, 32768, 0], - [18909, 31104, 32563, 32768, 0], - [12236, 26841, 31403, 32768, 0], - [8171, 21840, 29082, 32768, 0], - [7224, 17280, 25275, 32768, 0], - [8192, 16384, 24576, 32768, 0], - ], - ], - [ - [ - [3078, 6839, 9890, 32768, 0], - [13837, 20450, 24479, 32768, 0], - [5914, 14222, 19328, 32768, 0], - [3866, 10267, 14762, 32768, 0], - [2612, 7208, 11042, 32768, 0], - [1067, 2991, 4776, 32768, 0], - [25817, 31646, 32529, 32768, 0], - [13708, 26338, 30385, 32768, 0], - [7328, 18585, 24870, 32768, 0], - [4691, 13080, 19276, 32768, 0], - [1825, 5253, 8352, 32768, 0], - [29386, 32315, 32624, 32768, 0], - [17160, 29001, 31360, 32768, 0], - [9602, 21862, 27396, 32768, 0], - [5915, 15772, 22148, 32768, 0], - [2786, 7779, 12047, 32768, 0], - [29246, 32450, 32663, 32768, 0], - [18696, 29929, 31818, 32768, 0], - [10510, 23369, 28560, 32768, 0], - [6229, 16499, 23125, 32768, 0], - [2608, 7448, 11705, 32768, 0], - [30753, 32710, 32748, 32768, 0], - [21638, 31487, 32503, 32768, 0], - [12937, 26854, 30870, 32768, 0], - [8182, 20596, 26970, 32768, 0], - [3637, 10269, 15497, 32768, 0], - [8192, 16384, 24576, 32768, 0], - [8192, 16384, 24576, 32768, 0], - [8192, 16384, 24576, 32768, 0], - [8192, 16384, 24576, 32768, 0], - [8192, 16384, 24576, 32768, 0], - [8192, 16384, 24576, 32768, 0], - [8192, 16384, 24576, 32768, 0], - [8192, 16384, 24576, 32768, 0], - [8192, 16384, 24576, 32768, 0], - [8192, 16384, 24576, 32768, 0], - [8192, 16384, 24576, 32768, 0], - [8192, 16384, 24576, 32768, 0], - [8192, 16384, 24576, 32768, 0], - [8192, 16384, 24576, 32768, 0], - [8192, 16384, 24576, 32768, 0], - [8192, 16384, 24576, 32768, 0], - ], - [ - [5244, 12150, 16906, 32768, 0], - [20486, 26858, 29701, 32768, 0], - [7756, 18317, 23735, 32768, 0], - [3452, 9256, 13146, 32768, 0], - [2020, 5206, 8229, 32768, 0], - [1801, 4993, 7903, 32768, 0], - [27051, 31858, 32531, 32768, 0], - [15988, 27531, 30619, 32768, 0], - [9188, 21484, 26719, 32768, 0], - [6273, 17186, 23800, 32768, 0], - [3108, 9355, 14764, 32768, 0], - [31076, 32520, 32680, 32768, 0], - [18119, 30037, 31850, 32768, 0], - [10244, 22969, 27472, 32768, 0], - [4692, 14077, 19273, 32768, 0], - [3694, 11677, 17556, 32768, 0], - [30060, 32581, 32720, 32768, 0], - [21011, 30775, 32120, 32768, 0], - [11931, 24820, 29289, 32768, 0], - [7119, 17662, 24356, 32768, 0], - [3833, 10706, 16304, 32768, 0], - [31954, 32731, 32748, 32768, 0], - [23913, 31724, 32489, 32768, 0], - [15520, 28060, 31286, 32768, 0], - [11517, 23008, 28571, 32768, 0], - [6193, 14508, 20629, 32768, 0], - [8192, 16384, 24576, 32768, 0], - [8192, 16384, 24576, 32768, 0], - [8192, 16384, 24576, 32768, 0], - [8192, 16384, 24576, 32768, 0], - [8192, 16384, 24576, 32768, 0], - [8192, 16384, 24576, 32768, 0], - [8192, 16384, 24576, 32768, 0], - [8192, 16384, 24576, 32768, 0], - [8192, 16384, 24576, 32768, 0], - [8192, 16384, 24576, 32768, 0], - [8192, 16384, 24576, 32768, 0], - [8192, 16384, 24576, 32768, 0], - [8192, 16384, 24576, 32768, 0], - [8192, 16384, 24576, 32768, 0], - [8192, 16384, 24576, 32768, 0], - [8192, 16384, 24576, 32768, 0], - ], - ], - [ - [ - [1035, 2807, 4156, 32768, 0], - [13162, 18138, 20939, 32768, 0], - [2696, 6633, 8755, 32768, 0], - [1373, 4161, 6853, 32768, 0], - [1099, 2746, 4716, 32768, 0], - [340, 1021, 1599, 32768, 0], - [22826, 30419, 32135, 32768, 0], - [10395, 21762, 26942, 32768, 0], - [4726, 12407, 17361, 32768, 0], - [2447, 7080, 10593, 32768, 0], - [1227, 3717, 6011, 32768, 0], - [28156, 31424, 31934, 32768, 0], - [16915, 27754, 30373, 32768, 0], - [9148, 20990, 26431, 32768, 0], - [5950, 15515, 21148, 32768, 0], - [2492, 7327, 11526, 32768, 0], - [30602, 32477, 32670, 32768, 0], - [20026, 29955, 31568, 32768, 0], - [11220, 23628, 28105, 32768, 0], - [6652, 17019, 22973, 32768, 0], - [3064, 8536, 13043, 32768, 0], - [31769, 32724, 32748, 32768, 0], - [22230, 30887, 32373, 32768, 0], - [12234, 25079, 29731, 32768, 0], - [7326, 18816, 25353, 32768, 0], - [3933, 10907, 16616, 32768, 0], - [8192, 16384, 24576, 32768, 0], - [8192, 16384, 24576, 32768, 0], - [8192, 16384, 24576, 32768, 0], - [8192, 16384, 24576, 32768, 0], - [8192, 16384, 24576, 32768, 0], - [8192, 16384, 24576, 32768, 0], - [8192, 16384, 24576, 32768, 0], - [8192, 16384, 24576, 32768, 0], - [8192, 16384, 24576, 32768, 0], - [8192, 16384, 24576, 32768, 0], - [8192, 16384, 24576, 32768, 0], - [8192, 16384, 24576, 32768, 0], - [8192, 16384, 24576, 32768, 0], - [8192, 16384, 24576, 32768, 0], - [8192, 16384, 24576, 32768, 0], - [8192, 16384, 24576, 32768, 0], - ], - [ - [8192, 16384, 24576, 32768, 0], - [8192, 16384, 24576, 32768, 0], - [8192, 16384, 24576, 32768, 0], - [8192, 16384, 24576, 32768, 0], - [8192, 16384, 24576, 32768, 0], - [8192, 16384, 24576, 32768, 0], - [8192, 16384, 24576, 32768, 0], - [8192, 16384, 24576, 32768, 0], - [8192, 16384, 24576, 32768, 0], - [8192, 16384, 24576, 32768, 0], - [8192, 16384, 24576, 32768, 0], - [8192, 16384, 24576, 32768, 0], - [8192, 16384, 24576, 32768, 0], - [8192, 16384, 24576, 32768, 0], - [8192, 16384, 24576, 32768, 0], - [8192, 16384, 24576, 32768, 0], - [8192, 16384, 24576, 32768, 0], - [8192, 16384, 24576, 32768, 0], - [8192, 16384, 24576, 32768, 0], - [8192, 16384, 24576, 32768, 0], - [8192, 16384, 24576, 32768, 0], - [8192, 16384, 24576, 32768, 0], - [8192, 16384, 24576, 32768, 0], - [8192, 16384, 24576, 32768, 0], - [8192, 16384, 24576, 32768, 0], - [8192, 16384, 24576, 32768, 0], - [8192, 16384, 24576, 32768, 0], - [8192, 16384, 24576, 32768, 0], - [8192, 16384, 24576, 32768, 0], - [8192, 16384, 24576, 32768, 0], - [8192, 16384, 24576, 32768, 0], - [8192, 16384, 24576, 32768, 0], - [8192, 16384, 24576, 32768, 0], - [8192, 16384, 24576, 32768, 0], - [8192, 16384, 24576, 32768, 0], - [8192, 16384, 24576, 32768, 0], - [8192, 16384, 24576, 32768, 0], - [8192, 16384, 24576, 32768, 0], - [8192, 16384, 24576, 32768, 0], - [8192, 16384, 24576, 32768, 0], - [8192, 16384, 24576, 32768, 0], - [8192, 16384, 24576, 32768, 0], - ], - ], - ], - [ - [ - [ - [8896, 16227, 20630, 32768, 0], - [23629, 31782, 32527, 32768, 0], - [15173, 27755, 31321, 32768, 0], - [10158, 21233, 27382, 32768, 0], - [6420, 14857, 21558, 32768, 0], - [3269, 8155, 12646, 32768, 0], - [24835, 32009, 32496, 32768, 0], - [16509, 28421, 31579, 32768, 0], - [10957, 21514, 27418, 32768, 0], - [7881, 15930, 22096, 32768, 0], - [5388, 10960, 15918, 32768, 0], - [8192, 16384, 24576, 32768, 0], - [8192, 16384, 24576, 32768, 0], - [8192, 16384, 24576, 32768, 0], - [8192, 16384, 24576, 32768, 0], - [8192, 16384, 24576, 32768, 0], - [8192, 16384, 24576, 32768, 0], - [8192, 16384, 24576, 32768, 0], - [8192, 16384, 24576, 32768, 0], - [8192, 16384, 24576, 32768, 0], - [8192, 16384, 24576, 32768, 0], - [20745, 30773, 32093, 32768, 0], - [15200, 27221, 30861, 32768, 0], - [13032, 20873, 25667, 32768, 0], - [12285, 18663, 23494, 32768, 0], - [11563, 17481, 21489, 32768, 0], - [26260, 31982, 32320, 32768, 0], - [15397, 28083, 31100, 32768, 0], - [9742, 19217, 24824, 32768, 0], - [3261, 9629, 15362, 32768, 0], - [1480, 4322, 7499, 32768, 0], - [27599, 32256, 32460, 32768, 0], - [16857, 27659, 30774, 32768, 0], - [9551, 18290, 23748, 32768, 0], - [3052, 8933, 14103, 32768, 0], - [2021, 5910, 9787, 32768, 0], - [29005, 32015, 32392, 32768, 0], - [17677, 27694, 30863, 32768, 0], - [9204, 17356, 23219, 32768, 0], - [2403, 7516, 12814, 32768, 0], - [8192, 16384, 24576, 32768, 0], - [8192, 16384, 24576, 32768, 0], - ], - [ - [10808, 22056, 26896, 32768, 0], - [25739, 32313, 32676, 32768, 0], - [17288, 30203, 32221, 32768, 0], - [11359, 24878, 29896, 32768, 0], - [6949, 17767, 24893, 32768, 0], - [4287, 11796, 18071, 32768, 0], - [27880, 32521, 32705, 32768, 0], - [19038, 31004, 32414, 32768, 0], - [12564, 26345, 30768, 32768, 0], - [8269, 19947, 26779, 32768, 0], - [5674, 14657, 21674, 32768, 0], - [8192, 16384, 24576, 32768, 0], - [8192, 16384, 24576, 32768, 0], - [8192, 16384, 24576, 32768, 0], - [8192, 16384, 24576, 32768, 0], - [8192, 16384, 24576, 32768, 0], - [8192, 16384, 24576, 32768, 0], - [8192, 16384, 24576, 32768, 0], - [8192, 16384, 24576, 32768, 0], - [8192, 16384, 24576, 32768, 0], - [8192, 16384, 24576, 32768, 0], - [25742, 32319, 32671, 32768, 0], - [19557, 31164, 32454, 32768, 0], - [13381, 26381, 30755, 32768, 0], - [10101, 21466, 26722, 32768, 0], - [9209, 19650, 26825, 32768, 0], - [27107, 31917, 32432, 32768, 0], - [18056, 28893, 31203, 32768, 0], - [10200, 21434, 26764, 32768, 0], - [4660, 12913, 19502, 32768, 0], - [2368, 6930, 12504, 32768, 0], - [26960, 32158, 32613, 32768, 0], - [18628, 30005, 32031, 32768, 0], - [10233, 22442, 28232, 32768, 0], - [5471, 14630, 21516, 32768, 0], - [3235, 10767, 17109, 32768, 0], - [27696, 32440, 32692, 32768, 0], - [20032, 31167, 32438, 32768, 0], - [8700, 21341, 28442, 32768, 0], - [5662, 14831, 21795, 32768, 0], - [8192, 16384, 24576, 32768, 0], - [8192, 16384, 24576, 32768, 0], - ], - ], - [ - [ - [9704, 17294, 21132, 32768, 0], - [26762, 32278, 32633, 32768, 0], - [18382, 29620, 31819, 32768, 0], - [10891, 23475, 28723, 32768, 0], - [6358, 16583, 23309, 32768, 0], - [3248, 9118, 14141, 32768, 0], - [27204, 32573, 32699, 32768, 0], - [19818, 30824, 32329, 32768, 0], - [11772, 25120, 30041, 32768, 0], - [6995, 18033, 25039, 32768, 0], - [3752, 10442, 16098, 32768, 0], - [27222, 32256, 32559, 32768, 0], - [15356, 28399, 31475, 32768, 0], - [8821, 20635, 27057, 32768, 0], - [5511, 14404, 21239, 32768, 0], - [2935, 8222, 13051, 32768, 0], - [24875, 32120, 32529, 32768, 0], - [15233, 28265, 31445, 32768, 0], - [8605, 20570, 26932, 32768, 0], - [5431, 14413, 21196, 32768, 0], - [2994, 8341, 13223, 32768, 0], - [28201, 32604, 32700, 32768, 0], - [21041, 31446, 32456, 32768, 0], - [13221, 26213, 30475, 32768, 0], - [8255, 19385, 26037, 32768, 0], - [4930, 12585, 18830, 32768, 0], - [28768, 32448, 32627, 32768, 0], - [19705, 30561, 32021, 32768, 0], - [11572, 23589, 28220, 32768, 0], - [5532, 15034, 21446, 32768, 0], - [2460, 7150, 11456, 32768, 0], - [29874, 32619, 32699, 32768, 0], - [21621, 31071, 32201, 32768, 0], - [12511, 24747, 28992, 32768, 0], - [6281, 16395, 22748, 32768, 0], - [3246, 9278, 14497, 32768, 0], - [29715, 32625, 32712, 32768, 0], - [20958, 31011, 32283, 32768, 0], - [11233, 23671, 28806, 32768, 0], - [6012, 16128, 22868, 32768, 0], - [3427, 9851, 15414, 32768, 0], - [8192, 16384, 24576, 32768, 0], - ], - [ - [11016, 22111, 26794, 32768, 0], - [25946, 32357, 32677, 32768, 0], - [17890, 30452, 32252, 32768, 0], - [11678, 25142, 29816, 32768, 0], - [6720, 17534, 24584, 32768, 0], - [4230, 11665, 17820, 32768, 0], - [28400, 32623, 32747, 32768, 0], - [21164, 31668, 32575, 32768, 0], - [13572, 27388, 31182, 32768, 0], - [8234, 20750, 27358, 32768, 0], - [5065, 14055, 20897, 32768, 0], - [28981, 32547, 32705, 32768, 0], - [18681, 30543, 32239, 32768, 0], - [10919, 24075, 29286, 32768, 0], - [6431, 17199, 24077, 32768, 0], - [3819, 10464, 16618, 32768, 0], - [26870, 32467, 32693, 32768, 0], - [19041, 30831, 32347, 32768, 0], - [11794, 25211, 30016, 32768, 0], - [6888, 18019, 24970, 32768, 0], - [4370, 12363, 18992, 32768, 0], - [29578, 32670, 32744, 32768, 0], - [23159, 32007, 32613, 32768, 0], - [15315, 28669, 31676, 32768, 0], - [9298, 22607, 28782, 32768, 0], - [6144, 15913, 22968, 32768, 0], - [28110, 32499, 32669, 32768, 0], - [21574, 30937, 32015, 32768, 0], - [12759, 24818, 28727, 32768, 0], - [6545, 16761, 23042, 32768, 0], - [3649, 10597, 16833, 32768, 0], - [28163, 32552, 32728, 32768, 0], - [22101, 31469, 32464, 32768, 0], - [13160, 25472, 30143, 32768, 0], - [7303, 18684, 25468, 32768, 0], - [5241, 13975, 20955, 32768, 0], - [28400, 32631, 32744, 32768, 0], - [22104, 31793, 32603, 32768, 0], - [13557, 26571, 30846, 32768, 0], - [7749, 19861, 26675, 32768, 0], - [4873, 14030, 21234, 32768, 0], - [8192, 16384, 24576, 32768, 0], - ], - ], - [ - [ - [9800, 17635, 21073, 32768, 0], - [26153, 31885, 32527, 32768, 0], - [15038, 27852, 31006, 32768, 0], - [8718, 20564, 26486, 32768, 0], - [5128, 14076, 20514, 32768, 0], - [2636, 7566, 11925, 32768, 0], - [27551, 32504, 32701, 32768, 0], - [18310, 30054, 32100, 32768, 0], - [10211, 23420, 29082, 32768, 0], - [6222, 16876, 23916, 32768, 0], - [3462, 9954, 15498, 32768, 0], - [29991, 32633, 32721, 32768, 0], - [19883, 30751, 32201, 32768, 0], - [11141, 24184, 29285, 32768, 0], - [6420, 16940, 23774, 32768, 0], - [3392, 9753, 15118, 32768, 0], - [28465, 32616, 32712, 32768, 0], - [19850, 30702, 32244, 32768, 0], - [10983, 24024, 29223, 32768, 0], - [6294, 16770, 23582, 32768, 0], - [3244, 9283, 14509, 32768, 0], - [30023, 32717, 32748, 32768, 0], - [22940, 32032, 32626, 32768, 0], - [14282, 27928, 31473, 32768, 0], - [8562, 21327, 27914, 32768, 0], - [4846, 13393, 19919, 32768, 0], - [29981, 32590, 32695, 32768, 0], - [20465, 30963, 32166, 32768, 0], - [11479, 23579, 28195, 32768, 0], - [5916, 15648, 22073, 32768, 0], - [3031, 8605, 13398, 32768, 0], - [31146, 32691, 32739, 32768, 0], - [23106, 31724, 32444, 32768, 0], - [13783, 26738, 30439, 32768, 0], - [7852, 19468, 25807, 32768, 0], - [3860, 11124, 16853, 32768, 0], - [31014, 32724, 32748, 32768, 0], - [23629, 32109, 32628, 32768, 0], - [14747, 28115, 31403, 32768, 0], - [8545, 21242, 27478, 32768, 0], - [4574, 12781, 19067, 32768, 0], - [8192, 16384, 24576, 32768, 0], - ], - [ - [9185, 19694, 24688, 32768, 0], - [26081, 31985, 32621, 32768, 0], - [16015, 29000, 31787, 32768, 0], - [10542, 23690, 29206, 32768, 0], - [6732, 17945, 24677, 32768, 0], - [3916, 11039, 16722, 32768, 0], - [28224, 32566, 32744, 32768, 0], - [19100, 31138, 32485, 32768, 0], - [12528, 26620, 30879, 32768, 0], - [7741, 20277, 26885, 32768, 0], - [4566, 12845, 18990, 32768, 0], - [29933, 32593, 32718, 32768, 0], - [17670, 30333, 32155, 32768, 0], - [10385, 23600, 28909, 32768, 0], - [6243, 16236, 22407, 32768, 0], - [3976, 10389, 16017, 32768, 0], - [28377, 32561, 32738, 32768, 0], - [19366, 31175, 32482, 32768, 0], - [13327, 27175, 31094, 32768, 0], - [8258, 20769, 27143, 32768, 0], - [4703, 13198, 19527, 32768, 0], - [31086, 32706, 32748, 32768, 0], - [22853, 31902, 32583, 32768, 0], - [14759, 28186, 31419, 32768, 0], - [9284, 22382, 28348, 32768, 0], - [5585, 15192, 21868, 32768, 0], - [28291, 32652, 32746, 32768, 0], - [19849, 32107, 32571, 32768, 0], - [14834, 26818, 29214, 32768, 0], - [10306, 22594, 28672, 32768, 0], - [6615, 17384, 23384, 32768, 0], - [28947, 32604, 32745, 32768, 0], - [25625, 32289, 32646, 32768, 0], - [18758, 28672, 31403, 32768, 0], - [10017, 23430, 28523, 32768, 0], - [6862, 15269, 22131, 32768, 0], - [23933, 32509, 32739, 32768, 0], - [19927, 31495, 32631, 32768, 0], - [11903, 26023, 30621, 32768, 0], - [7026, 20094, 27252, 32768, 0], - [5998, 18106, 24437, 32768, 0], - [8192, 16384, 24576, 32768, 0], - ], - ], - [ - [ - [4456, 11274, 15533, 32768, 0], - [21219, 29079, 31616, 32768, 0], - [11173, 23774, 28567, 32768, 0], - [7282, 18293, 24263, 32768, 0], - [4890, 13286, 19115, 32768, 0], - [1890, 5508, 8659, 32768, 0], - [26651, 32136, 32647, 32768, 0], - [14630, 28254, 31455, 32768, 0], - [8716, 21287, 27395, 32768, 0], - [5615, 15331, 22008, 32768, 0], - [2675, 7700, 12150, 32768, 0], - [29954, 32526, 32690, 32768, 0], - [16126, 28982, 31633, 32768, 0], - [9030, 21361, 27352, 32768, 0], - [5411, 14793, 21271, 32768, 0], - [2943, 8422, 13163, 32768, 0], - [29539, 32601, 32730, 32768, 0], - [18125, 30385, 32201, 32768, 0], - [10422, 24090, 29468, 32768, 0], - [6468, 17487, 24438, 32768, 0], - [2970, 8653, 13531, 32768, 0], - [30912, 32715, 32748, 32768, 0], - [20666, 31373, 32497, 32768, 0], - [12509, 26640, 30917, 32768, 0], - [8058, 20629, 27290, 32768, 0], - [4231, 12006, 18052, 32768, 0], - [8192, 16384, 24576, 32768, 0], - [8192, 16384, 24576, 32768, 0], - [8192, 16384, 24576, 32768, 0], - [8192, 16384, 24576, 32768, 0], - [8192, 16384, 24576, 32768, 0], - [8192, 16384, 24576, 32768, 0], - [8192, 16384, 24576, 32768, 0], - [8192, 16384, 24576, 32768, 0], - [8192, 16384, 24576, 32768, 0], - [8192, 16384, 24576, 32768, 0], - [8192, 16384, 24576, 32768, 0], - [8192, 16384, 24576, 32768, 0], - [8192, 16384, 24576, 32768, 0], - [8192, 16384, 24576, 32768, 0], - [8192, 16384, 24576, 32768, 0], - [8192, 16384, 24576, 32768, 0], - ], - [ - [10202, 20633, 25484, 32768, 0], - [27336, 31445, 32352, 32768, 0], - [12420, 24384, 28552, 32768, 0], - [7648, 18115, 23856, 32768, 0], - [5662, 14341, 19902, 32768, 0], - [3611, 10328, 15390, 32768, 0], - [30945, 32616, 32736, 32768, 0], - [18682, 30505, 32253, 32768, 0], - [11513, 25336, 30203, 32768, 0], - [7449, 19452, 26148, 32768, 0], - [4482, 13051, 18886, 32768, 0], - [32022, 32690, 32747, 32768, 0], - [18578, 30501, 32146, 32768, 0], - [11249, 23368, 28631, 32768, 0], - [5645, 16958, 22158, 32768, 0], - [5009, 11444, 16637, 32768, 0], - [31357, 32710, 32748, 32768, 0], - [21552, 31494, 32504, 32768, 0], - [13891, 27677, 31340, 32768, 0], - [9051, 22098, 28172, 32768, 0], - [5190, 13377, 19486, 32768, 0], - [32364, 32740, 32748, 32768, 0], - [24839, 31907, 32551, 32768, 0], - [17160, 28779, 31696, 32768, 0], - [12452, 24137, 29602, 32768, 0], - [6165, 15389, 22477, 32768, 0], - [8192, 16384, 24576, 32768, 0], - [8192, 16384, 24576, 32768, 0], - [8192, 16384, 24576, 32768, 0], - [8192, 16384, 24576, 32768, 0], - [8192, 16384, 24576, 32768, 0], - [8192, 16384, 24576, 32768, 0], - [8192, 16384, 24576, 32768, 0], - [8192, 16384, 24576, 32768, 0], - [8192, 16384, 24576, 32768, 0], - [8192, 16384, 24576, 32768, 0], - [8192, 16384, 24576, 32768, 0], - [8192, 16384, 24576, 32768, 0], - [8192, 16384, 24576, 32768, 0], - [8192, 16384, 24576, 32768, 0], - [8192, 16384, 24576, 32768, 0], - [8192, 16384, 24576, 32768, 0], - ], - ], - [ - [ - [2575, 7281, 11077, 32768, 0], - [14002, 20866, 25402, 32768, 0], - [6343, 15056, 19658, 32768, 0], - [4474, 11858, 17041, 32768, 0], - [2865, 8299, 12534, 32768, 0], - [1344, 3949, 6391, 32768, 0], - [24720, 31239, 32459, 32768, 0], - [12585, 25356, 29968, 32768, 0], - [7181, 18246, 24444, 32768, 0], - [5025, 13667, 19885, 32768, 0], - [2521, 7304, 11605, 32768, 0], - [29908, 32252, 32584, 32768, 0], - [17421, 29156, 31575, 32768, 0], - [9889, 22188, 27782, 32768, 0], - [5878, 15647, 22123, 32768, 0], - [2814, 8665, 13323, 32768, 0], - [30183, 32568, 32713, 32768, 0], - [18528, 30195, 32049, 32768, 0], - [10982, 24606, 29657, 32768, 0], - [6957, 18165, 25231, 32768, 0], - [3508, 10118, 15468, 32768, 0], - [31761, 32736, 32748, 32768, 0], - [21041, 31328, 32546, 32768, 0], - [12568, 26732, 31166, 32768, 0], - [8052, 20720, 27733, 32768, 0], - [4336, 12192, 18396, 32768, 0], - [8192, 16384, 24576, 32768, 0], - [8192, 16384, 24576, 32768, 0], - [8192, 16384, 24576, 32768, 0], - [8192, 16384, 24576, 32768, 0], - [8192, 16384, 24576, 32768, 0], - [8192, 16384, 24576, 32768, 0], - [8192, 16384, 24576, 32768, 0], - [8192, 16384, 24576, 32768, 0], - [8192, 16384, 24576, 32768, 0], - [8192, 16384, 24576, 32768, 0], - [8192, 16384, 24576, 32768, 0], - [8192, 16384, 24576, 32768, 0], - [8192, 16384, 24576, 32768, 0], - [8192, 16384, 24576, 32768, 0], - [8192, 16384, 24576, 32768, 0], - [8192, 16384, 24576, 32768, 0], - ], - [ - [8192, 16384, 24576, 32768, 0], - [8192, 16384, 24576, 32768, 0], - [8192, 16384, 24576, 32768, 0], - [8192, 16384, 24576, 32768, 0], - [8192, 16384, 24576, 32768, 0], - [8192, 16384, 24576, 32768, 0], - [8192, 16384, 24576, 32768, 0], - [8192, 16384, 24576, 32768, 0], - [8192, 16384, 24576, 32768, 0], - [8192, 16384, 24576, 32768, 0], - [8192, 16384, 24576, 32768, 0], - [8192, 16384, 24576, 32768, 0], - [8192, 16384, 24576, 32768, 0], - [8192, 16384, 24576, 32768, 0], - [8192, 16384, 24576, 32768, 0], - [8192, 16384, 24576, 32768, 0], - [8192, 16384, 24576, 32768, 0], - [8192, 16384, 24576, 32768, 0], - [8192, 16384, 24576, 32768, 0], - [8192, 16384, 24576, 32768, 0], - [8192, 16384, 24576, 32768, 0], - [8192, 16384, 24576, 32768, 0], - [8192, 16384, 24576, 32768, 0], - [8192, 16384, 24576, 32768, 0], - [8192, 16384, 24576, 32768, 0], - [8192, 16384, 24576, 32768, 0], - [8192, 16384, 24576, 32768, 0], - [8192, 16384, 24576, 32768, 0], - [8192, 16384, 24576, 32768, 0], - [8192, 16384, 24576, 32768, 0], - [8192, 16384, 24576, 32768, 0], - [8192, 16384, 24576, 32768, 0], - [8192, 16384, 24576, 32768, 0], - [8192, 16384, 24576, 32768, 0], - [8192, 16384, 24576, 32768, 0], - [8192, 16384, 24576, 32768, 0], - [8192, 16384, 24576, 32768, 0], - [8192, 16384, 24576, 32768, 0], - [8192, 16384, 24576, 32768, 0], - [8192, 16384, 24576, 32768, 0], - [8192, 16384, 24576, 32768, 0], - [8192, 16384, 24576, 32768, 0], - ], - ], - ], - [ - [ - [ - [7062, 16472, 22319, 32768, 0], - [24538, 32261, 32674, 32768, 0], - [13675, 28041, 31779, 32768, 0], - [8590, 20674, 27631, 32768, 0], - [5685, 14675, 22013, 32768, 0], - [3655, 9898, 15731, 32768, 0], - [26493, 32418, 32658, 32768, 0], - [16376, 29342, 32090, 32768, 0], - [10594, 22649, 28970, 32768, 0], - [8176, 17170, 24303, 32768, 0], - [5605, 12694, 19139, 32768, 0], - [8192, 16384, 24576, 32768, 0], - [8192, 16384, 24576, 32768, 0], - [8192, 16384, 24576, 32768, 0], - [8192, 16384, 24576, 32768, 0], - [8192, 16384, 24576, 32768, 0], - [8192, 16384, 24576, 32768, 0], - [8192, 16384, 24576, 32768, 0], - [8192, 16384, 24576, 32768, 0], - [8192, 16384, 24576, 32768, 0], - [8192, 16384, 24576, 32768, 0], - [23888, 31902, 32542, 32768, 0], - [18612, 29687, 31987, 32768, 0], - [16245, 24852, 29249, 32768, 0], - [15765, 22608, 27559, 32768, 0], - [19895, 24699, 27510, 32768, 0], - [28401, 32212, 32457, 32768, 0], - [15274, 27825, 30980, 32768, 0], - [9364, 18128, 24332, 32768, 0], - [2283, 8193, 15082, 32768, 0], - [1228, 3972, 7881, 32768, 0], - [29455, 32469, 32620, 32768, 0], - [17981, 28245, 31388, 32768, 0], - [10921, 20098, 26240, 32768, 0], - [3743, 11829, 18657, 32768, 0], - [2374, 9593, 15715, 32768, 0], - [31068, 32466, 32635, 32768, 0], - [20321, 29572, 31971, 32768, 0], - [10771, 20255, 27119, 32768, 0], - [2795, 10410, 17361, 32768, 0], - [8192, 16384, 24576, 32768, 0], - [8192, 16384, 24576, 32768, 0], - ], - [ - [9320, 22102, 27840, 32768, 0], - [27057, 32464, 32724, 32768, 0], - [16331, 30268, 32309, 32768, 0], - [10319, 23935, 29720, 32768, 0], - [6189, 16448, 24106, 32768, 0], - [3589, 10884, 18808, 32768, 0], - [29026, 32624, 32748, 32768, 0], - [19226, 31507, 32587, 32768, 0], - [12692, 26921, 31203, 32768, 0], - [7049, 19532, 27635, 32768, 0], - [7727, 15669, 23252, 32768, 0], - [8192, 16384, 24576, 32768, 0], - [8192, 16384, 24576, 32768, 0], - [8192, 16384, 24576, 32768, 0], - [8192, 16384, 24576, 32768, 0], - [8192, 16384, 24576, 32768, 0], - [8192, 16384, 24576, 32768, 0], - [8192, 16384, 24576, 32768, 0], - [8192, 16384, 24576, 32768, 0], - [8192, 16384, 24576, 32768, 0], - [8192, 16384, 24576, 32768, 0], - [28056, 32625, 32748, 32768, 0], - [22383, 32075, 32669, 32768, 0], - [15417, 27098, 31749, 32768, 0], - [18127, 26493, 27190, 32768, 0], - [5461, 16384, 21845, 32768, 0], - [27982, 32091, 32584, 32768, 0], - [19045, 29868, 31972, 32768, 0], - [10397, 22266, 27932, 32768, 0], - [5990, 13697, 21500, 32768, 0], - [1792, 6912, 15104, 32768, 0], - [28198, 32501, 32718, 32768, 0], - [21534, 31521, 32569, 32768, 0], - [11109, 25217, 30017, 32768, 0], - [5671, 15124, 26151, 32768, 0], - [4681, 14043, 18725, 32768, 0], - [28688, 32580, 32741, 32768, 0], - [22576, 32079, 32661, 32768, 0], - [10627, 22141, 28340, 32768, 0], - [9362, 14043, 28087, 32768, 0], - [8192, 16384, 24576, 32768, 0], - [8192, 16384, 24576, 32768, 0], - ], - ], - [ - [ - [7754, 16948, 22142, 32768, 0], - [25670, 32330, 32691, 32768, 0], - [15663, 29225, 31994, 32768, 0], - [9878, 23288, 29158, 32768, 0], - [6419, 17088, 24336, 32768, 0], - [3859, 11003, 17039, 32768, 0], - [27562, 32595, 32725, 32768, 0], - [17575, 30588, 32399, 32768, 0], - [10819, 24838, 30309, 32768, 0], - [7124, 18686, 25916, 32768, 0], - [4479, 12688, 19340, 32768, 0], - [28385, 32476, 32673, 32768, 0], - [15306, 29005, 31938, 32768, 0], - [8937, 21615, 28322, 32768, 0], - [5982, 15603, 22786, 32768, 0], - [3620, 10267, 16136, 32768, 0], - [27280, 32464, 32667, 32768, 0], - [15607, 29160, 32004, 32768, 0], - [9091, 22135, 28740, 32768, 0], - [6232, 16632, 24020, 32768, 0], - [4047, 11377, 17672, 32768, 0], - [29220, 32630, 32718, 32768, 0], - [19650, 31220, 32462, 32768, 0], - [13050, 26312, 30827, 32768, 0], - [9228, 20870, 27468, 32768, 0], - [6146, 15149, 21971, 32768, 0], - [30169, 32481, 32623, 32768, 0], - [17212, 29311, 31554, 32768, 0], - [9911, 21311, 26882, 32768, 0], - [4487, 13314, 20372, 32768, 0], - [2570, 7772, 12889, 32768, 0], - [30924, 32613, 32708, 32768, 0], - [19490, 30206, 32107, 32768, 0], - [11232, 23998, 29276, 32768, 0], - [6769, 17955, 25035, 32768, 0], - [4398, 12623, 19214, 32768, 0], - [30609, 32627, 32722, 32768, 0], - [19370, 30582, 32287, 32768, 0], - [10457, 23619, 29409, 32768, 0], - [6443, 17637, 24834, 32768, 0], - [4645, 13236, 20106, 32768, 0], - [8192, 16384, 24576, 32768, 0], - ], - [ - [8626, 20271, 26216, 32768, 0], - [26707, 32406, 32711, 32768, 0], - [16999, 30329, 32286, 32768, 0], - [11445, 25123, 30286, 32768, 0], - [6411, 18828, 25601, 32768, 0], - [6801, 12458, 20248, 32768, 0], - [29918, 32682, 32748, 32768, 0], - [20649, 31739, 32618, 32768, 0], - [12879, 27773, 31581, 32768, 0], - [7896, 21751, 28244, 32768, 0], - [5260, 14870, 23698, 32768, 0], - [29252, 32593, 32731, 32768, 0], - [17072, 30460, 32294, 32768, 0], - [10653, 24143, 29365, 32768, 0], - [6536, 17490, 23983, 32768, 0], - [4929, 13170, 20085, 32768, 0], - [28137, 32518, 32715, 32768, 0], - [18171, 30784, 32407, 32768, 0], - [11437, 25436, 30459, 32768, 0], - [7252, 18534, 26176, 32768, 0], - [4126, 13353, 20978, 32768, 0], - [31162, 32726, 32748, 32768, 0], - [23017, 32222, 32701, 32768, 0], - [15629, 29233, 32046, 32768, 0], - [9387, 22621, 29480, 32768, 0], - [6922, 17616, 25010, 32768, 0], - [28838, 32265, 32614, 32768, 0], - [19701, 30206, 31920, 32768, 0], - [11214, 22410, 27933, 32768, 0], - [5320, 14177, 23034, 32768, 0], - [5049, 12881, 17827, 32768, 0], - [27484, 32471, 32734, 32768, 0], - [21076, 31526, 32561, 32768, 0], - [12707, 26303, 31211, 32768, 0], - [8169, 21722, 28219, 32768, 0], - [6045, 19406, 27042, 32768, 0], - [27753, 32572, 32745, 32768, 0], - [20832, 31878, 32653, 32768, 0], - [13250, 27356, 31674, 32768, 0], - [7718, 21508, 29858, 32768, 0], - [7209, 18350, 25559, 32768, 0], - [8192, 16384, 24576, 32768, 0], - ], - ], - [ - [ - [7876, 16901, 21741, 32768, 0], - [24001, 31898, 32625, 32768, 0], - [14529, 27959, 31451, 32768, 0], - [8273, 20818, 27258, 32768, 0], - [5278, 14673, 21510, 32768, 0], - [2983, 8843, 14039, 32768, 0], - [28016, 32574, 32732, 32768, 0], - [17471, 30306, 32301, 32768, 0], - [10224, 24063, 29728, 32768, 0], - [6602, 17954, 25052, 32768, 0], - [4002, 11585, 17759, 32768, 0], - [30190, 32634, 32739, 32768, 0], - [17497, 30282, 32270, 32768, 0], - [10229, 23729, 29538, 32768, 0], - [6344, 17211, 24440, 32768, 0], - [3849, 11189, 17108, 32768, 0], - [28570, 32583, 32726, 32768, 0], - [17521, 30161, 32238, 32768, 0], - [10153, 23565, 29378, 32768, 0], - [6455, 17341, 24443, 32768, 0], - [3907, 11042, 17024, 32768, 0], - [30689, 32715, 32748, 32768, 0], - [21546, 31840, 32610, 32768, 0], - [13547, 27581, 31459, 32768, 0], - [8912, 21757, 28309, 32768, 0], - [5548, 15080, 22046, 32768, 0], - [30783, 32540, 32685, 32768, 0], - [17540, 29528, 31668, 32768, 0], - [10160, 21468, 26783, 32768, 0], - [4724, 13393, 20054, 32768, 0], - [2702, 8174, 13102, 32768, 0], - [31648, 32686, 32742, 32768, 0], - [20954, 31094, 32337, 32768, 0], - [12420, 25698, 30179, 32768, 0], - [7304, 19320, 26248, 32768, 0], - [4366, 12261, 18864, 32768, 0], - [31581, 32723, 32748, 32768, 0], - [21373, 31586, 32525, 32768, 0], - [12744, 26625, 30885, 32768, 0], - [7431, 20322, 26950, 32768, 0], - [4692, 13323, 20111, 32768, 0], - [8192, 16384, 24576, 32768, 0], - ], - [ - [7833, 18369, 24095, 32768, 0], - [26650, 32273, 32702, 32768, 0], - [16371, 29961, 32191, 32768, 0], - [11055, 24082, 29629, 32768, 0], - [6892, 18644, 25400, 32768, 0], - [5006, 13057, 19240, 32768, 0], - [29834, 32666, 32748, 32768, 0], - [19577, 31335, 32570, 32768, 0], - [12253, 26509, 31122, 32768, 0], - [7991, 20772, 27711, 32768, 0], - [5677, 15910, 23059, 32768, 0], - [30109, 32532, 32720, 32768, 0], - [16747, 30166, 32252, 32768, 0], - [10134, 23542, 29184, 32768, 0], - [5791, 16176, 23556, 32768, 0], - [4362, 10414, 17284, 32768, 0], - [29492, 32626, 32748, 32768, 0], - [19894, 31402, 32525, 32768, 0], - [12942, 27071, 30869, 32768, 0], - [8346, 21216, 27405, 32768, 0], - [6572, 17087, 23859, 32768, 0], - [32035, 32735, 32748, 32768, 0], - [22957, 31838, 32618, 32768, 0], - [14724, 28572, 31772, 32768, 0], - [10364, 23999, 29553, 32768, 0], - [7004, 18433, 25655, 32768, 0], - [27528, 32277, 32681, 32768, 0], - [16959, 31171, 32096, 32768, 0], - [10486, 23593, 27962, 32768, 0], - [8192, 16384, 23211, 32768, 0], - [8937, 17873, 20852, 32768, 0], - [27715, 32002, 32615, 32768, 0], - [15073, 29491, 31676, 32768, 0], - [11264, 24576, 28672, 32768, 0], - [2341, 18725, 23406, 32768, 0], - [7282, 18204, 25486, 32768, 0], - [28547, 32213, 32657, 32768, 0], - [20788, 29773, 32239, 32768, 0], - [6780, 21469, 30508, 32768, 0], - [5958, 14895, 23831, 32768, 0], - [16384, 21845, 27307, 32768, 0], - [8192, 16384, 24576, 32768, 0], - ], - ], - [ - [ - [5992, 14304, 19765, 32768, 0], - [22612, 31238, 32456, 32768, 0], - [13456, 27162, 31087, 32768, 0], - [8001, 20062, 26504, 32768, 0], - [5168, 14105, 20764, 32768, 0], - [2632, 7771, 12385, 32768, 0], - [27034, 32344, 32709, 32768, 0], - [15850, 29415, 31997, 32768, 0], - [9494, 22776, 28841, 32768, 0], - [6151, 16830, 23969, 32768, 0], - [3461, 10039, 15722, 32768, 0], - [30134, 32569, 32731, 32768, 0], - [15638, 29422, 31945, 32768, 0], - [9150, 21865, 28218, 32768, 0], - [5647, 15719, 22676, 32768, 0], - [3402, 9772, 15477, 32768, 0], - [28530, 32586, 32735, 32768, 0], - [17139, 30298, 32292, 32768, 0], - [10200, 24039, 29685, 32768, 0], - [6419, 17674, 24786, 32768, 0], - [3544, 10225, 15824, 32768, 0], - [31333, 32726, 32748, 32768, 0], - [20618, 31487, 32544, 32768, 0], - [12901, 27217, 31232, 32768, 0], - [8624, 21734, 28171, 32768, 0], - [5104, 14191, 20748, 32768, 0], - [8192, 16384, 24576, 32768, 0], - [8192, 16384, 24576, 32768, 0], - [8192, 16384, 24576, 32768, 0], - [8192, 16384, 24576, 32768, 0], - [8192, 16384, 24576, 32768, 0], - [8192, 16384, 24576, 32768, 0], - [8192, 16384, 24576, 32768, 0], - [8192, 16384, 24576, 32768, 0], - [8192, 16384, 24576, 32768, 0], - [8192, 16384, 24576, 32768, 0], - [8192, 16384, 24576, 32768, 0], - [8192, 16384, 24576, 32768, 0], - [8192, 16384, 24576, 32768, 0], - [8192, 16384, 24576, 32768, 0], - [8192, 16384, 24576, 32768, 0], - [8192, 16384, 24576, 32768, 0], - ], - [ - [11206, 21090, 26561, 32768, 0], - [28759, 32279, 32671, 32768, 0], - [14171, 27952, 31569, 32768, 0], - [9743, 22907, 29141, 32768, 0], - [6871, 17886, 24868, 32768, 0], - [4960, 13152, 19315, 32768, 0], - [31077, 32661, 32748, 32768, 0], - [19400, 31195, 32515, 32768, 0], - [12752, 26858, 31040, 32768, 0], - [8370, 22098, 28591, 32768, 0], - [5457, 15373, 22298, 32768, 0], - [31697, 32706, 32748, 32768, 0], - [17860, 30657, 32333, 32768, 0], - [12510, 24812, 29261, 32768, 0], - [6180, 19124, 24722, 32768, 0], - [5041, 13548, 17959, 32768, 0], - [31552, 32716, 32748, 32768, 0], - [21908, 31769, 32623, 32768, 0], - [14470, 28201, 31565, 32768, 0], - [9493, 22982, 28608, 32768, 0], - [6858, 17240, 24137, 32768, 0], - [32543, 32752, 32756, 32768, 0], - [24286, 32097, 32666, 32768, 0], - [15958, 29217, 32024, 32768, 0], - [10207, 24234, 29958, 32768, 0], - [6929, 18305, 25652, 32768, 0], - [8192, 16384, 24576, 32768, 0], - [8192, 16384, 24576, 32768, 0], - [8192, 16384, 24576, 32768, 0], - [8192, 16384, 24576, 32768, 0], - [8192, 16384, 24576, 32768, 0], - [8192, 16384, 24576, 32768, 0], - [8192, 16384, 24576, 32768, 0], - [8192, 16384, 24576, 32768, 0], - [8192, 16384, 24576, 32768, 0], - [8192, 16384, 24576, 32768, 0], - [8192, 16384, 24576, 32768, 0], - [8192, 16384, 24576, 32768, 0], - [8192, 16384, 24576, 32768, 0], - [8192, 16384, 24576, 32768, 0], - [8192, 16384, 24576, 32768, 0], - [8192, 16384, 24576, 32768, 0], - ], - ], - [ - [ - [4137, 10847, 15682, 32768, 0], - [17824, 27001, 30058, 32768, 0], - [10204, 22796, 28291, 32768, 0], - [6076, 15935, 22125, 32768, 0], - [3852, 10937, 16816, 32768, 0], - [2252, 6324, 10131, 32768, 0], - [25840, 32016, 32662, 32768, 0], - [15109, 28268, 31531, 32768, 0], - [9385, 22231, 28340, 32768, 0], - [6082, 16672, 23479, 32768, 0], - [3318, 9427, 14681, 32768, 0], - [30594, 32574, 32718, 32768, 0], - [16836, 29552, 31859, 32768, 0], - [9556, 22542, 28356, 32768, 0], - [6305, 16725, 23540, 32768, 0], - [3376, 9895, 15184, 32768, 0], - [29383, 32617, 32745, 32768, 0], - [18891, 30809, 32401, 32768, 0], - [11688, 25942, 30687, 32768, 0], - [7468, 19469, 26651, 32768, 0], - [3909, 11358, 17012, 32768, 0], - [31564, 32736, 32748, 32768, 0], - [20906, 31611, 32600, 32768, 0], - [13191, 27621, 31537, 32768, 0], - [8768, 22029, 28676, 32768, 0], - [5079, 14109, 20906, 32768, 0], - [8192, 16384, 24576, 32768, 0], - [8192, 16384, 24576, 32768, 0], - [8192, 16384, 24576, 32768, 0], - [8192, 16384, 24576, 32768, 0], - [8192, 16384, 24576, 32768, 0], - [8192, 16384, 24576, 32768, 0], - [8192, 16384, 24576, 32768, 0], - [8192, 16384, 24576, 32768, 0], - [8192, 16384, 24576, 32768, 0], - [8192, 16384, 24576, 32768, 0], - [8192, 16384, 24576, 32768, 0], - [8192, 16384, 24576, 32768, 0], - [8192, 16384, 24576, 32768, 0], - [8192, 16384, 24576, 32768, 0], - [8192, 16384, 24576, 32768, 0], - [8192, 16384, 24576, 32768, 0], - ], - [ - [8192, 16384, 24576, 32768, 0], - [8192, 16384, 24576, 32768, 0], - [8192, 16384, 24576, 32768, 0], - [8192, 16384, 24576, 32768, 0], - [8192, 16384, 24576, 32768, 0], - [8192, 16384, 24576, 32768, 0], - [8192, 16384, 24576, 32768, 0], - [8192, 16384, 24576, 32768, 0], - [8192, 16384, 24576, 32768, 0], - [8192, 16384, 24576, 32768, 0], - [8192, 16384, 24576, 32768, 0], - [8192, 16384, 24576, 32768, 0], - [8192, 16384, 24576, 32768, 0], - [8192, 16384, 24576, 32768, 0], - [8192, 16384, 24576, 32768, 0], - [8192, 16384, 24576, 32768, 0], - [8192, 16384, 24576, 32768, 0], - [8192, 16384, 24576, 32768, 0], - [8192, 16384, 24576, 32768, 0], - [8192, 16384, 24576, 32768, 0], - [8192, 16384, 24576, 32768, 0], - [8192, 16384, 24576, 32768, 0], - [8192, 16384, 24576, 32768, 0], - [8192, 16384, 24576, 32768, 0], - [8192, 16384, 24576, 32768, 0], - [8192, 16384, 24576, 32768, 0], - [8192, 16384, 24576, 32768, 0], - [8192, 16384, 24576, 32768, 0], - [8192, 16384, 24576, 32768, 0], - [8192, 16384, 24576, 32768, 0], - [8192, 16384, 24576, 32768, 0], - [8192, 16384, 24576, 32768, 0], - [8192, 16384, 24576, 32768, 0], - [8192, 16384, 24576, 32768, 0], - [8192, 16384, 24576, 32768, 0], - [8192, 16384, 24576, 32768, 0], - [8192, 16384, 24576, 32768, 0], - [8192, 16384, 24576, 32768, 0], - [8192, 16384, 24576, 32768, 0], - [8192, 16384, 24576, 32768, 0], - [8192, 16384, 24576, 32768, 0], - [8192, 16384, 24576, 32768, 0], - ], - ], - ], -]; - -pub static DEFAULT_COEFF_BASE_EOB_CDF: [[[[[u16; 4]; 4]; 2]; 5]; 4] = [ - [ - [ - [[17837, 29055, 32768, 0], [29600, 31446, 32768, 0], [30844, 31878, 32768, 0], [24926, 28948, 32768, 0]], - [[21365, 30026, 32768, 0], [30512, 32423, 32768, 0], [31658, 32621, 32768, 0], [29630, 31881, 32768, 0]], - ], - [ - [[5717, 26477, 32768, 0], [30491, 31703, 32768, 0], [31550, 32158, 32768, 0], [29648, 31491, 32768, 0]], - [[12608, 27820, 32768, 0], [30680, 32225, 32768, 0], [30809, 32335, 32768, 0], [31299, 32423, 32768, 0]], - ], - [ - [[1786, 12612, 32768, 0], [30663, 31625, 32768, 0], [32339, 32468, 32768, 0], [31148, 31833, 32768, 0]], - [[18857, 23865, 32768, 0], [31428, 32428, 32768, 0], [31744, 32373, 32768, 0], [31775, 32526, 32768, 0]], - ], - [ - [[1787, 2532, 32768, 0], [30832, 31662, 32768, 0], [31824, 32682, 32768, 0], [32133, 32569, 32768, 0]], - [[13751, 22235, 32768, 0], [32089, 32409, 32768, 0], [27084, 27920, 32768, 0], [29291, 32594, 32768, 0]], - ], - [ - [[1725, 3449, 32768, 0], [31102, 31935, 32768, 0], [32457, 32613, 32768, 0], [32412, 32649, 32768, 0]], - [[10923, 21845, 32768, 0], [10923, 21845, 32768, 0], [10923, 21845, 32768, 0], [10923, 21845, 32768, 0]], - ], - ], - [ - [ - [[17560, 29888, 32768, 0], [29671, 31549, 32768, 0], [31007, 32056, 32768, 0], [27286, 30006, 32768, 0]], - [[26594, 31212, 32768, 0], [31208, 32582, 32768, 0], [31835, 32637, 32768, 0], [30595, 32206, 32768, 0]], - ], - [ - [[15239, 29932, 32768, 0], [31315, 32095, 32768, 0], [32130, 32434, 32768, 0], [30864, 31996, 32768, 0]], - [[26279, 30968, 32768, 0], [31142, 32495, 32768, 0], [31713, 32540, 32768, 0], [31929, 32594, 32768, 0]], - ], - [ - [[2644, 25198, 32768, 0], [32038, 32451, 32768, 0], [32639, 32695, 32768, 0], [32166, 32518, 32768, 0]], - [[17187, 27668, 32768, 0], [31714, 32550, 32768, 0], [32283, 32678, 32768, 0], [31930, 32563, 32768, 0]], - ], - [ - [[1044, 2257, 32768, 0], [30755, 31923, 32768, 0], [32208, 32693, 32768, 0], [32244, 32615, 32768, 0]], - [[21317, 26207, 32768, 0], [29133, 30868, 32768, 0], [29311, 31231, 32768, 0], [29657, 31087, 32768, 0]], - ], - [ - [[478, 1834, 32768, 0], [31005, 31987, 32768, 0], [32317, 32724, 32768, 0], [30865, 32648, 32768, 0]], - [[10923, 21845, 32768, 0], [10923, 21845, 32768, 0], [10923, 21845, 32768, 0], [10923, 21845, 32768, 0]], - ], - ], - [ - [ - [[20092, 30774, 32768, 0], [30695, 32020, 32768, 0], [31131, 32103, 32768, 0], [28666, 30870, 32768, 0]], - [[27258, 31095, 32768, 0], [31804, 32623, 32768, 0], [31763, 32528, 32768, 0], [31438, 32506, 32768, 0]], - ], - [ - [[18049, 30489, 32768, 0], [31706, 32286, 32768, 0], [32163, 32473, 32768, 0], [31550, 32184, 32768, 0]], - [[27116, 30842, 32768, 0], [31971, 32598, 32768, 0], [32088, 32576, 32768, 0], [32067, 32664, 32768, 0]], - ], - [ - [[12854, 29093, 32768, 0], [32272, 32558, 32768, 0], [32667, 32729, 32768, 0], [32306, 32585, 32768, 0]], - [[25476, 30366, 32768, 0], [32169, 32687, 32768, 0], [32479, 32689, 32768, 0], [31673, 32634, 32768, 0]], - ], - [ - [[2809, 19301, 32768, 0], [32205, 32622, 32768, 0], [32338, 32730, 32768, 0], [31786, 32616, 32768, 0]], - [[22737, 29105, 32768, 0], [30810, 32362, 32768, 0], [30014, 32627, 32768, 0], [30528, 32574, 32768, 0]], - ], - [ - [[935, 3382, 32768, 0], [30789, 31909, 32768, 0], [32466, 32756, 32768, 0], [30860, 32513, 32768, 0]], - [[10923, 21845, 32768, 0], [10923, 21845, 32768, 0], [10923, 21845, 32768, 0], [10923, 21845, 32768, 0]], - ], - ], - [ - [ - [[22497, 31198, 32768, 0], [31715, 32495, 32768, 0], [31606, 32337, 32768, 0], [30388, 31990, 32768, 0]], - [[27877, 31584, 32768, 0], [32170, 32728, 32768, 0], [32155, 32688, 32768, 0], [32219, 32702, 32768, 0]], - ], - [ - [[21457, 31043, 32768, 0], [31951, 32483, 32768, 0], [32153, 32562, 32768, 0], [31473, 32215, 32768, 0]], - [[27558, 31151, 32768, 0], [32020, 32640, 32768, 0], [32097, 32575, 32768, 0], [32242, 32719, 32768, 0]], - ], - [ - [[19980, 30591, 32768, 0], [32219, 32597, 32768, 0], [32581, 32706, 32768, 0], [31803, 32287, 32768, 0]], - [[26473, 30507, 32768, 0], [32431, 32723, 32768, 0], [32196, 32611, 32768, 0], [31588, 32528, 32768, 0]], - ], - [ - [[24647, 30463, 32768, 0], [32412, 32695, 32768, 0], [32468, 32720, 32768, 0], [31269, 32523, 32768, 0]], - [[28482, 31505, 32768, 0], [32152, 32701, 32768, 0], [31732, 32598, 32768, 0], [31767, 32712, 32768, 0]], - ], - [ - [[12358, 24977, 32768, 0], [31331, 32385, 32768, 0], [32634, 32756, 32768, 0], [30411, 32548, 32768, 0]], - [[10923, 21845, 32768, 0], [10923, 21845, 32768, 0], [10923, 21845, 32768, 0], [10923, 21845, 32768, 0]], - ], - ], -]; - -pub static DEFAULT_COEFF_BR_CDF: [[[[[u16; 5]; 21]; 2]; 5]; 4] = [ - [ - [ - [ - [14298, 20718, 24174, 32768, 0], - [12536, 19601, 23789, 32768, 0], - [8712, 15051, 19503, 32768, 0], - [6170, 11327, 15434, 32768, 0], - [4742, 8926, 12538, 32768, 0], - [3803, 7317, 10546, 32768, 0], - [1696, 3317, 4871, 32768, 0], - [14392, 19951, 22756, 32768, 0], - [15978, 23218, 26818, 32768, 0], - [12187, 19474, 23889, 32768, 0], - [9176, 15640, 20259, 32768, 0], - [7068, 12655, 17028, 32768, 0], - [5656, 10442, 14472, 32768, 0], - [2580, 4992, 7244, 32768, 0], - [12136, 18049, 21426, 32768, 0], - [13784, 20721, 24481, 32768, 0], - [10836, 17621, 21900, 32768, 0], - [8372, 14444, 18847, 32768, 0], - [6523, 11779, 16000, 32768, 0], - [5337, 9898, 13760, 32768, 0], - [3034, 5860, 8462, 32768, 0], - ], - [ - [15967, 22905, 26286, 32768, 0], - [13534, 20654, 24579, 32768, 0], - [9504, 16092, 20535, 32768, 0], - [6975, 12568, 16903, 32768, 0], - [5364, 10091, 14020, 32768, 0], - [4357, 8370, 11857, 32768, 0], - [2506, 4934, 7218, 32768, 0], - [23032, 28815, 30936, 32768, 0], - [19540, 26704, 29719, 32768, 0], - [15158, 22969, 27097, 32768, 0], - [11408, 18865, 23650, 32768, 0], - [8885, 15448, 20250, 32768, 0], - [7108, 12853, 17416, 32768, 0], - [4231, 8041, 11480, 32768, 0], - [19823, 26490, 29156, 32768, 0], - [18890, 25929, 28932, 32768, 0], - [15660, 23491, 27433, 32768, 0], - [12147, 19776, 24488, 32768, 0], - [9728, 16774, 21649, 32768, 0], - [7919, 14277, 19066, 32768, 0], - [5440, 10170, 14185, 32768, 0], - ], - ], - [ - [ - [14406, 20862, 24414, 32768, 0], - [11824, 18907, 23109, 32768, 0], - [8257, 14393, 18803, 32768, 0], - [5860, 10747, 14778, 32768, 0], - [4475, 8486, 11984, 32768, 0], - [3606, 6954, 10043, 32768, 0], - [1736, 3410, 5048, 32768, 0], - [14430, 20046, 22882, 32768, 0], - [15593, 22899, 26709, 32768, 0], - [12102, 19368, 23811, 32768, 0], - [9059, 15584, 20262, 32768, 0], - [6999, 12603, 17048, 32768, 0], - [5684, 10497, 14553, 32768, 0], - [2822, 5438, 7862, 32768, 0], - [15785, 21585, 24359, 32768, 0], - [18347, 25229, 28266, 32768, 0], - [14974, 22487, 26389, 32768, 0], - [11423, 18681, 23271, 32768, 0], - [8863, 15350, 20008, 32768, 0], - [7153, 12852, 17278, 32768, 0], - [3707, 7036, 9982, 32768, 0], - ], - [ - [15460, 21696, 25469, 32768, 0], - [12170, 19249, 23191, 32768, 0], - [8723, 15027, 19332, 32768, 0], - [6428, 11704, 15874, 32768, 0], - [4922, 9292, 13052, 32768, 0], - [4139, 7695, 11010, 32768, 0], - [2291, 4508, 6598, 32768, 0], - [19856, 26920, 29828, 32768, 0], - [17923, 25289, 28792, 32768, 0], - [14278, 21968, 26297, 32768, 0], - [10910, 18136, 22950, 32768, 0], - [8423, 14815, 19627, 32768, 0], - [6771, 12283, 16774, 32768, 0], - [4074, 7750, 11081, 32768, 0], - [19852, 26074, 28672, 32768, 0], - [19371, 26110, 28989, 32768, 0], - [16265, 23873, 27663, 32768, 0], - [12758, 20378, 24952, 32768, 0], - [10095, 17098, 21961, 32768, 0], - [8250, 14628, 19451, 32768, 0], - [5205, 9745, 13622, 32768, 0], - ], - ], - [ - [ - [10563, 16233, 19763, 32768, 0], - [9794, 16022, 19804, 32768, 0], - [6750, 11945, 15759, 32768, 0], - [4963, 9186, 12752, 32768, 0], - [3845, 7435, 10627, 32768, 0], - [3051, 6085, 8834, 32768, 0], - [1311, 2596, 3830, 32768, 0], - [11246, 16404, 19689, 32768, 0], - [12315, 18911, 22731, 32768, 0], - [10557, 17095, 21289, 32768, 0], - [8136, 14006, 18249, 32768, 0], - [6348, 11474, 15565, 32768, 0], - [5196, 9655, 13400, 32768, 0], - [2349, 4526, 6587, 32768, 0], - [13337, 18730, 21569, 32768, 0], - [19306, 26071, 28882, 32768, 0], - [15952, 23540, 27254, 32768, 0], - [12409, 19934, 24430, 32768, 0], - [9760, 16706, 21389, 32768, 0], - [8004, 14220, 18818, 32768, 0], - [4138, 7794, 10961, 32768, 0], - ], - [ - [10870, 16684, 20949, 32768, 0], - [9664, 15230, 18680, 32768, 0], - [6886, 12109, 15408, 32768, 0], - [4825, 8900, 12305, 32768, 0], - [3630, 7162, 10314, 32768, 0], - [3036, 6429, 9387, 32768, 0], - [1671, 3296, 4940, 32768, 0], - [13819, 19159, 23026, 32768, 0], - [11984, 19108, 23120, 32768, 0], - [10690, 17210, 21663, 32768, 0], - [7984, 14154, 18333, 32768, 0], - [6868, 12294, 16124, 32768, 0], - [5274, 8994, 12868, 32768, 0], - [2988, 5771, 8424, 32768, 0], - [19736, 26647, 29141, 32768, 0], - [18933, 26070, 28984, 32768, 0], - [15779, 23048, 27200, 32768, 0], - [12638, 20061, 24532, 32768, 0], - [10692, 17545, 22220, 32768, 0], - [9217, 15251, 20054, 32768, 0], - [5078, 9284, 12594, 32768, 0], - ], - ], - [ - [ - [2331, 3662, 5244, 32768, 0], - [2891, 4771, 6145, 32768, 0], - [4598, 7623, 9729, 32768, 0], - [3520, 6845, 9199, 32768, 0], - [3417, 6119, 9324, 32768, 0], - [2601, 5412, 7385, 32768, 0], - [600, 1173, 1744, 32768, 0], - [7672, 13286, 17469, 32768, 0], - [4232, 7792, 10793, 32768, 0], - [2915, 5317, 7397, 32768, 0], - [2318, 4356, 6152, 32768, 0], - [2127, 4000, 5554, 32768, 0], - [1850, 3478, 5275, 32768, 0], - [977, 1933, 2843, 32768, 0], - [18280, 24387, 27989, 32768, 0], - [15852, 22671, 26185, 32768, 0], - [13845, 20951, 24789, 32768, 0], - [11055, 17966, 22129, 32768, 0], - [9138, 15422, 19801, 32768, 0], - [7454, 13145, 17456, 32768, 0], - [3370, 6393, 9013, 32768, 0], - ], - [ - [5842, 9229, 10838, 32768, 0], - [2313, 3491, 4276, 32768, 0], - [2998, 6104, 7496, 32768, 0], - [2420, 7447, 9868, 32768, 0], - [3034, 8495, 10923, 32768, 0], - [4076, 8937, 10975, 32768, 0], - [1086, 2370, 3299, 32768, 0], - [9714, 17254, 20444, 32768, 0], - [8543, 13698, 17123, 32768, 0], - [4918, 9007, 11910, 32768, 0], - [4129, 7532, 10553, 32768, 0], - [2364, 5533, 8058, 32768, 0], - [1834, 3546, 5563, 32768, 0], - [1473, 2908, 4133, 32768, 0], - [15405, 21193, 25619, 32768, 0], - [15691, 21952, 26561, 32768, 0], - [12962, 19194, 24165, 32768, 0], - [10272, 17855, 22129, 32768, 0], - [8588, 15270, 20718, 32768, 0], - [8682, 14669, 19500, 32768, 0], - [4870, 9636, 13205, 32768, 0], - ], - ], - [ - [ - [8192, 16384, 24576, 32768, 0], - [8192, 16384, 24576, 32768, 0], - [8192, 16384, 24576, 32768, 0], - [8192, 16384, 24576, 32768, 0], - [8192, 16384, 24576, 32768, 0], - [8192, 16384, 24576, 32768, 0], - [8192, 16384, 24576, 32768, 0], - [8192, 16384, 24576, 32768, 0], - [8192, 16384, 24576, 32768, 0], - [8192, 16384, 24576, 32768, 0], - [8192, 16384, 24576, 32768, 0], - [8192, 16384, 24576, 32768, 0], - [8192, 16384, 24576, 32768, 0], - [8192, 16384, 24576, 32768, 0], - [8192, 16384, 24576, 32768, 0], - [8192, 16384, 24576, 32768, 0], - [8192, 16384, 24576, 32768, 0], - [8192, 16384, 24576, 32768, 0], - [8192, 16384, 24576, 32768, 0], - [8192, 16384, 24576, 32768, 0], - [8192, 16384, 24576, 32768, 0], - ], - [ - [8192, 16384, 24576, 32768, 0], - [8192, 16384, 24576, 32768, 0], - [8192, 16384, 24576, 32768, 0], - [8192, 16384, 24576, 32768, 0], - [8192, 16384, 24576, 32768, 0], - [8192, 16384, 24576, 32768, 0], - [8192, 16384, 24576, 32768, 0], - [8192, 16384, 24576, 32768, 0], - [8192, 16384, 24576, 32768, 0], - [8192, 16384, 24576, 32768, 0], - [8192, 16384, 24576, 32768, 0], - [8192, 16384, 24576, 32768, 0], - [8192, 16384, 24576, 32768, 0], - [8192, 16384, 24576, 32768, 0], - [8192, 16384, 24576, 32768, 0], - [8192, 16384, 24576, 32768, 0], - [8192, 16384, 24576, 32768, 0], - [8192, 16384, 24576, 32768, 0], - [8192, 16384, 24576, 32768, 0], - [8192, 16384, 24576, 32768, 0], - [8192, 16384, 24576, 32768, 0], - ], - ], - ], - [ - [ - [ - [14995, 21341, 24749, 32768, 0], - [13158, 20289, 24601, 32768, 0], - [8941, 15326, 19876, 32768, 0], - [6297, 11541, 15807, 32768, 0], - [4817, 9029, 12776, 32768, 0], - [3731, 7273, 10627, 32768, 0], - [1847, 3617, 5354, 32768, 0], - [14472, 19659, 22343, 32768, 0], - [16806, 24162, 27533, 32768, 0], - [12900, 20404, 24713, 32768, 0], - [9411, 16112, 20797, 32768, 0], - [7056, 12697, 17148, 32768, 0], - [5544, 10339, 14460, 32768, 0], - [2954, 5704, 8319, 32768, 0], - [12464, 18071, 21354, 32768, 0], - [15482, 22528, 26034, 32768, 0], - [12070, 19269, 23624, 32768, 0], - [8953, 15406, 20106, 32768, 0], - [7027, 12730, 17220, 32768, 0], - [5887, 10913, 15140, 32768, 0], - [3793, 7278, 10447, 32768, 0], - ], - [ - [15571, 22232, 25749, 32768, 0], - [14506, 21575, 25374, 32768, 0], - [10189, 17089, 21569, 32768, 0], - [7316, 13301, 17915, 32768, 0], - [5783, 10912, 15190, 32768, 0], - [4760, 9155, 13088, 32768, 0], - [2993, 5966, 8774, 32768, 0], - [23424, 28903, 30778, 32768, 0], - [20775, 27666, 30290, 32768, 0], - [16474, 24410, 28299, 32768, 0], - [12471, 20180, 24987, 32768, 0], - [9410, 16487, 21439, 32768, 0], - [7536, 13614, 18529, 32768, 0], - [5048, 9586, 13549, 32768, 0], - [21090, 27290, 29756, 32768, 0], - [20796, 27402, 30026, 32768, 0], - [17819, 25485, 28969, 32768, 0], - [13860, 21909, 26462, 32768, 0], - [11002, 18494, 23529, 32768, 0], - [8953, 15929, 20897, 32768, 0], - [6448, 11918, 16454, 32768, 0], - ], - ], - [ - [ - [15999, 22208, 25449, 32768, 0], - [13050, 19988, 24122, 32768, 0], - [8594, 14864, 19378, 32768, 0], - [6033, 11079, 15238, 32768, 0], - [4554, 8683, 12347, 32768, 0], - [3672, 7139, 10337, 32768, 0], - [1900, 3771, 5576, 32768, 0], - [15788, 21340, 23949, 32768, 0], - [16825, 24235, 27758, 32768, 0], - [12873, 20402, 24810, 32768, 0], - [9590, 16363, 21094, 32768, 0], - [7352, 13209, 17733, 32768, 0], - [5960, 10989, 15184, 32768, 0], - [3232, 6234, 9007, 32768, 0], - [15761, 20716, 23224, 32768, 0], - [19318, 25989, 28759, 32768, 0], - [15529, 23094, 26929, 32768, 0], - [11662, 18989, 23641, 32768, 0], - [8955, 15568, 20366, 32768, 0], - [7281, 13106, 17708, 32768, 0], - [4248, 8059, 11440, 32768, 0], - ], - [ - [14899, 21217, 24503, 32768, 0], - [13519, 20283, 24047, 32768, 0], - [9429, 15966, 20365, 32768, 0], - [6700, 12355, 16652, 32768, 0], - [5088, 9704, 13716, 32768, 0], - [4243, 8154, 11731, 32768, 0], - [2702, 5364, 7861, 32768, 0], - [22745, 28388, 30454, 32768, 0], - [20235, 27146, 29922, 32768, 0], - [15896, 23715, 27637, 32768, 0], - [11840, 19350, 24131, 32768, 0], - [9122, 15932, 20880, 32768, 0], - [7488, 13581, 18362, 32768, 0], - [5114, 9568, 13370, 32768, 0], - [20845, 26553, 28932, 32768, 0], - [20981, 27372, 29884, 32768, 0], - [17781, 25335, 28785, 32768, 0], - [13760, 21708, 26297, 32768, 0], - [10975, 18415, 23365, 32768, 0], - [9045, 15789, 20686, 32768, 0], - [6130, 11199, 15423, 32768, 0], - ], - ], - [ - [ - [13549, 19724, 23158, 32768, 0], - [11844, 18382, 22246, 32768, 0], - [7919, 13619, 17773, 32768, 0], - [5486, 10143, 13946, 32768, 0], - [4166, 7983, 11324, 32768, 0], - [3364, 6506, 9427, 32768, 0], - [1598, 3160, 4674, 32768, 0], - [15281, 20979, 23781, 32768, 0], - [14939, 22119, 25952, 32768, 0], - [11363, 18407, 22812, 32768, 0], - [8609, 14857, 19370, 32768, 0], - [6737, 12184, 16480, 32768, 0], - [5506, 10263, 14262, 32768, 0], - [2990, 5786, 8380, 32768, 0], - [20249, 25253, 27417, 32768, 0], - [21070, 27518, 30001, 32768, 0], - [16854, 24469, 28074, 32768, 0], - [12864, 20486, 25000, 32768, 0], - [9962, 16978, 21778, 32768, 0], - [8074, 14338, 19048, 32768, 0], - [4494, 8479, 11906, 32768, 0], - ], - [ - [13960, 19617, 22829, 32768, 0], - [11150, 17341, 21228, 32768, 0], - [7150, 12964, 17190, 32768, 0], - [5331, 10002, 13867, 32768, 0], - [4167, 7744, 11057, 32768, 0], - [3480, 6629, 9646, 32768, 0], - [1883, 3784, 5686, 32768, 0], - [18752, 25660, 28912, 32768, 0], - [16968, 24586, 28030, 32768, 0], - [13520, 21055, 25313, 32768, 0], - [10453, 17626, 22280, 32768, 0], - [8386, 14505, 19116, 32768, 0], - [6742, 12595, 17008, 32768, 0], - [4273, 8140, 11499, 32768, 0], - [22120, 27827, 30233, 32768, 0], - [20563, 27358, 29895, 32768, 0], - [17076, 24644, 28153, 32768, 0], - [13362, 20942, 25309, 32768, 0], - [10794, 17965, 22695, 32768, 0], - [9014, 15652, 20319, 32768, 0], - [5708, 10512, 14497, 32768, 0], - ], - ], - [ - [ - [5705, 10930, 15725, 32768, 0], - [7946, 12765, 16115, 32768, 0], - [6801, 12123, 16226, 32768, 0], - [5462, 10135, 14200, 32768, 0], - [4189, 8011, 11507, 32768, 0], - [3191, 6229, 9408, 32768, 0], - [1057, 2137, 3212, 32768, 0], - [10018, 17067, 21491, 32768, 0], - [7380, 12582, 16453, 32768, 0], - [6068, 10845, 14339, 32768, 0], - [5098, 9198, 12555, 32768, 0], - [4312, 8010, 11119, 32768, 0], - [3700, 6966, 9781, 32768, 0], - [1693, 3326, 4887, 32768, 0], - [18757, 24930, 27774, 32768, 0], - [17648, 24596, 27817, 32768, 0], - [14707, 22052, 26026, 32768, 0], - [11720, 18852, 23292, 32768, 0], - [9357, 15952, 20525, 32768, 0], - [7810, 13753, 18210, 32768, 0], - [3879, 7333, 10328, 32768, 0], - ], - [ - [8278, 13242, 15922, 32768, 0], - [10547, 15867, 18919, 32768, 0], - [9106, 15842, 20609, 32768, 0], - [6833, 13007, 17218, 32768, 0], - [4811, 9712, 13923, 32768, 0], - [3985, 7352, 11128, 32768, 0], - [1688, 3458, 5262, 32768, 0], - [12951, 21861, 26510, 32768, 0], - [9788, 16044, 20276, 32768, 0], - [6309, 11244, 14870, 32768, 0], - [5183, 9349, 12566, 32768, 0], - [4389, 8229, 11492, 32768, 0], - [3633, 6945, 10620, 32768, 0], - [3600, 6847, 9907, 32768, 0], - [21748, 28137, 30255, 32768, 0], - [19436, 26581, 29560, 32768, 0], - [16359, 24201, 27953, 32768, 0], - [13961, 21693, 25871, 32768, 0], - [11544, 18686, 23322, 32768, 0], - [9372, 16462, 20952, 32768, 0], - [6138, 11210, 15390, 32768, 0], - ], - ], - [ - [ - [8192, 16384, 24576, 32768, 0], - [8192, 16384, 24576, 32768, 0], - [8192, 16384, 24576, 32768, 0], - [8192, 16384, 24576, 32768, 0], - [8192, 16384, 24576, 32768, 0], - [8192, 16384, 24576, 32768, 0], - [8192, 16384, 24576, 32768, 0], - [8192, 16384, 24576, 32768, 0], - [8192, 16384, 24576, 32768, 0], - [8192, 16384, 24576, 32768, 0], - [8192, 16384, 24576, 32768, 0], - [8192, 16384, 24576, 32768, 0], - [8192, 16384, 24576, 32768, 0], - [8192, 16384, 24576, 32768, 0], - [8192, 16384, 24576, 32768, 0], - [8192, 16384, 24576, 32768, 0], - [8192, 16384, 24576, 32768, 0], - [8192, 16384, 24576, 32768, 0], - [8192, 16384, 24576, 32768, 0], - [8192, 16384, 24576, 32768, 0], - [8192, 16384, 24576, 32768, 0], - ], - [ - [8192, 16384, 24576, 32768, 0], - [8192, 16384, 24576, 32768, 0], - [8192, 16384, 24576, 32768, 0], - [8192, 16384, 24576, 32768, 0], - [8192, 16384, 24576, 32768, 0], - [8192, 16384, 24576, 32768, 0], - [8192, 16384, 24576, 32768, 0], - [8192, 16384, 24576, 32768, 0], - [8192, 16384, 24576, 32768, 0], - [8192, 16384, 24576, 32768, 0], - [8192, 16384, 24576, 32768, 0], - [8192, 16384, 24576, 32768, 0], - [8192, 16384, 24576, 32768, 0], - [8192, 16384, 24576, 32768, 0], - [8192, 16384, 24576, 32768, 0], - [8192, 16384, 24576, 32768, 0], - [8192, 16384, 24576, 32768, 0], - [8192, 16384, 24576, 32768, 0], - [8192, 16384, 24576, 32768, 0], - [8192, 16384, 24576, 32768, 0], - [8192, 16384, 24576, 32768, 0], - ], - ], - ], - [ - [ - [ - [16138, 22223, 25509, 32768, 0], - [15347, 22430, 26332, 32768, 0], - [9614, 16736, 21332, 32768, 0], - [6600, 12275, 16907, 32768, 0], - [4811, 9424, 13547, 32768, 0], - [3748, 7809, 11420, 32768, 0], - [2254, 4587, 6890, 32768, 0], - [15196, 20284, 23177, 32768, 0], - [18317, 25469, 28451, 32768, 0], - [13918, 21651, 25842, 32768, 0], - [10052, 17150, 21995, 32768, 0], - [7499, 13630, 18587, 32768, 0], - [6158, 11417, 16003, 32768, 0], - [4014, 7785, 11252, 32768, 0], - [15048, 21067, 24384, 32768, 0], - [18202, 25346, 28553, 32768, 0], - [14302, 22019, 26356, 32768, 0], - [10839, 18139, 23166, 32768, 0], - [8715, 15744, 20806, 32768, 0], - [7536, 13576, 18544, 32768, 0], - [5413, 10335, 14498, 32768, 0], - ], - [ - [17394, 24501, 27895, 32768, 0], - [15889, 23420, 27185, 32768, 0], - [11561, 19133, 23870, 32768, 0], - [8285, 14812, 19844, 32768, 0], - [6496, 12043, 16550, 32768, 0], - [4771, 9574, 13677, 32768, 0], - [3603, 6830, 10144, 32768, 0], - [21656, 27704, 30200, 32768, 0], - [21324, 27915, 30511, 32768, 0], - [17327, 25336, 28997, 32768, 0], - [13417, 21381, 26033, 32768, 0], - [10132, 17425, 22338, 32768, 0], - [8580, 15016, 19633, 32768, 0], - [5694, 11477, 16411, 32768, 0], - [24116, 29780, 31450, 32768, 0], - [23853, 29695, 31591, 32768, 0], - [20085, 27614, 30428, 32768, 0], - [15326, 24335, 28575, 32768, 0], - [11814, 19472, 24810, 32768, 0], - [10221, 18611, 24767, 32768, 0], - [7689, 14558, 20321, 32768, 0], - ], - ], - [ - [ - [16214, 22380, 25770, 32768, 0], - [14213, 21304, 25295, 32768, 0], - [9213, 15823, 20455, 32768, 0], - [6395, 11758, 16139, 32768, 0], - [4779, 9187, 13066, 32768, 0], - [3821, 7501, 10953, 32768, 0], - [2293, 4567, 6795, 32768, 0], - [15859, 21283, 23820, 32768, 0], - [18404, 25602, 28726, 32768, 0], - [14325, 21980, 26206, 32768, 0], - [10669, 17937, 22720, 32768, 0], - [8297, 14642, 19447, 32768, 0], - [6746, 12389, 16893, 32768, 0], - [4324, 8251, 11770, 32768, 0], - [16532, 21631, 24475, 32768, 0], - [20667, 27150, 29668, 32768, 0], - [16728, 24510, 28175, 32768, 0], - [12861, 20645, 25332, 32768, 0], - [10076, 17361, 22417, 32768, 0], - [8395, 14940, 19963, 32768, 0], - [5731, 10683, 14912, 32768, 0], - ], - [ - [14433, 21155, 24938, 32768, 0], - [14658, 21716, 25545, 32768, 0], - [9923, 16824, 21557, 32768, 0], - [6982, 13052, 17721, 32768, 0], - [5419, 10503, 15050, 32768, 0], - [4852, 9162, 13014, 32768, 0], - [3271, 6395, 9630, 32768, 0], - [22210, 27833, 30109, 32768, 0], - [20750, 27368, 29821, 32768, 0], - [16894, 24828, 28573, 32768, 0], - [13247, 21276, 25757, 32768, 0], - [10038, 17265, 22563, 32768, 0], - [8587, 14947, 20327, 32768, 0], - [5645, 11371, 15252, 32768, 0], - [22027, 27526, 29714, 32768, 0], - [23098, 29146, 31221, 32768, 0], - [19886, 27341, 30272, 32768, 0], - [15609, 23747, 28046, 32768, 0], - [11993, 20065, 24939, 32768, 0], - [9637, 18267, 23671, 32768, 0], - [7625, 13801, 19144, 32768, 0], - ], - ], - [ - [ - [14438, 20798, 24089, 32768, 0], - [12621, 19203, 23097, 32768, 0], - [8177, 14125, 18402, 32768, 0], - [5674, 10501, 14456, 32768, 0], - [4236, 8239, 11733, 32768, 0], - [3447, 6750, 9806, 32768, 0], - [1986, 3950, 5864, 32768, 0], - [16208, 22099, 24930, 32768, 0], - [16537, 24025, 27585, 32768, 0], - [12780, 20381, 24867, 32768, 0], - [9767, 16612, 21416, 32768, 0], - [7686, 13738, 18398, 32768, 0], - [6333, 11614, 15964, 32768, 0], - [3941, 7571, 10836, 32768, 0], - [22819, 27422, 29202, 32768, 0], - [22224, 28514, 30721, 32768, 0], - [17660, 25433, 28913, 32768, 0], - [13574, 21482, 26002, 32768, 0], - [10629, 17977, 22938, 32768, 0], - [8612, 15298, 20265, 32768, 0], - [5607, 10491, 14596, 32768, 0], - ], - [ - [13569, 19800, 23206, 32768, 0], - [13128, 19924, 23869, 32768, 0], - [8329, 14841, 19403, 32768, 0], - [6130, 10976, 15057, 32768, 0], - [4682, 8839, 12518, 32768, 0], - [3656, 7409, 10588, 32768, 0], - [2577, 5099, 7412, 32768, 0], - [22427, 28684, 30585, 32768, 0], - [20913, 27750, 30139, 32768, 0], - [15840, 24109, 27834, 32768, 0], - [12308, 20029, 24569, 32768, 0], - [10216, 16785, 21458, 32768, 0], - [8309, 14203, 19113, 32768, 0], - [6043, 11168, 15307, 32768, 0], - [23166, 28901, 30998, 32768, 0], - [21899, 28405, 30751, 32768, 0], - [18413, 26091, 29443, 32768, 0], - [15233, 23114, 27352, 32768, 0], - [12683, 20472, 25288, 32768, 0], - [10702, 18259, 23409, 32768, 0], - [8125, 14464, 19226, 32768, 0], - ], - ], - [ - [ - [9040, 14786, 18360, 32768, 0], - [9979, 15718, 19415, 32768, 0], - [7913, 13918, 18311, 32768, 0], - [5859, 10889, 15184, 32768, 0], - [4593, 8677, 12510, 32768, 0], - [3820, 7396, 10791, 32768, 0], - [1730, 3471, 5192, 32768, 0], - [11803, 18365, 22709, 32768, 0], - [11419, 18058, 22225, 32768, 0], - [9418, 15774, 20243, 32768, 0], - [7539, 13325, 17657, 32768, 0], - [6233, 11317, 15384, 32768, 0], - [5137, 9656, 13545, 32768, 0], - [2977, 5774, 8349, 32768, 0], - [21207, 27246, 29640, 32768, 0], - [19547, 26578, 29497, 32768, 0], - [16169, 23871, 27690, 32768, 0], - [12820, 20458, 25018, 32768, 0], - [10224, 17332, 22214, 32768, 0], - [8526, 15048, 19884, 32768, 0], - [5037, 9410, 13118, 32768, 0], - ], - [ - [12339, 17329, 20140, 32768, 0], - [13505, 19895, 23225, 32768, 0], - [9847, 16944, 21564, 32768, 0], - [7280, 13256, 18348, 32768, 0], - [4712, 10009, 14454, 32768, 0], - [4361, 7914, 12477, 32768, 0], - [2870, 5628, 7995, 32768, 0], - [20061, 25504, 28526, 32768, 0], - [15235, 22878, 26145, 32768, 0], - [12985, 19958, 24155, 32768, 0], - [9782, 16641, 21403, 32768, 0], - [9456, 16360, 20760, 32768, 0], - [6855, 12940, 18557, 32768, 0], - [5661, 10564, 15002, 32768, 0], - [25656, 30602, 31894, 32768, 0], - [22570, 29107, 31092, 32768, 0], - [18917, 26423, 29541, 32768, 0], - [15940, 23649, 27754, 32768, 0], - [12803, 20581, 25219, 32768, 0], - [11082, 18695, 23376, 32768, 0], - [7939, 14373, 19005, 32768, 0], - ], - ], - [ - [ - [8192, 16384, 24576, 32768, 0], - [8192, 16384, 24576, 32768, 0], - [8192, 16384, 24576, 32768, 0], - [8192, 16384, 24576, 32768, 0], - [8192, 16384, 24576, 32768, 0], - [8192, 16384, 24576, 32768, 0], - [8192, 16384, 24576, 32768, 0], - [8192, 16384, 24576, 32768, 0], - [8192, 16384, 24576, 32768, 0], - [8192, 16384, 24576, 32768, 0], - [8192, 16384, 24576, 32768, 0], - [8192, 16384, 24576, 32768, 0], - [8192, 16384, 24576, 32768, 0], - [8192, 16384, 24576, 32768, 0], - [8192, 16384, 24576, 32768, 0], - [8192, 16384, 24576, 32768, 0], - [8192, 16384, 24576, 32768, 0], - [8192, 16384, 24576, 32768, 0], - [8192, 16384, 24576, 32768, 0], - [8192, 16384, 24576, 32768, 0], - [8192, 16384, 24576, 32768, 0], - ], - [ - [8192, 16384, 24576, 32768, 0], - [8192, 16384, 24576, 32768, 0], - [8192, 16384, 24576, 32768, 0], - [8192, 16384, 24576, 32768, 0], - [8192, 16384, 24576, 32768, 0], - [8192, 16384, 24576, 32768, 0], - [8192, 16384, 24576, 32768, 0], - [8192, 16384, 24576, 32768, 0], - [8192, 16384, 24576, 32768, 0], - [8192, 16384, 24576, 32768, 0], - [8192, 16384, 24576, 32768, 0], - [8192, 16384, 24576, 32768, 0], - [8192, 16384, 24576, 32768, 0], - [8192, 16384, 24576, 32768, 0], - [8192, 16384, 24576, 32768, 0], - [8192, 16384, 24576, 32768, 0], - [8192, 16384, 24576, 32768, 0], - [8192, 16384, 24576, 32768, 0], - [8192, 16384, 24576, 32768, 0], - [8192, 16384, 24576, 32768, 0], - [8192, 16384, 24576, 32768, 0], - ], - ], - ], - [ - [ - [ - [18315, 24289, 27551, 32768, 0], - [16854, 24068, 27835, 32768, 0], - [10140, 17927, 23173, 32768, 0], - [6722, 12982, 18267, 32768, 0], - [4661, 9826, 14706, 32768, 0], - [3832, 8165, 12294, 32768, 0], - [2795, 6098, 9245, 32768, 0], - [17145, 23326, 26672, 32768, 0], - [20733, 27680, 30308, 32768, 0], - [16032, 24461, 28546, 32768, 0], - [11653, 20093, 25081, 32768, 0], - [9290, 16429, 22086, 32768, 0], - [7796, 14598, 19982, 32768, 0], - [6502, 12378, 17441, 32768, 0], - [21681, 27732, 30320, 32768, 0], - [22389, 29044, 31261, 32768, 0], - [19027, 26731, 30087, 32768, 0], - [14739, 23755, 28624, 32768, 0], - [11358, 20778, 25511, 32768, 0], - [10995, 18073, 24190, 32768, 0], - [9162, 14990, 20617, 32768, 0], - ], - [ - [21425, 27952, 30388, 32768, 0], - [18062, 25838, 29034, 32768, 0], - [11956, 19881, 24808, 32768, 0], - [7718, 15000, 20980, 32768, 0], - [5702, 11254, 16143, 32768, 0], - [4898, 9088, 16864, 32768, 0], - [3679, 6776, 11907, 32768, 0], - [23294, 30160, 31663, 32768, 0], - [24397, 29896, 31836, 32768, 0], - [19245, 27128, 30593, 32768, 0], - [13202, 19825, 26404, 32768, 0], - [11578, 19297, 23957, 32768, 0], - [8073, 13297, 21370, 32768, 0], - [5461, 10923, 19745, 32768, 0], - [27367, 30521, 31934, 32768, 0], - [24904, 30671, 31940, 32768, 0], - [23075, 28460, 31299, 32768, 0], - [14400, 23658, 30417, 32768, 0], - [13885, 23882, 28325, 32768, 0], - [14746, 22938, 27853, 32768, 0], - [5461, 16384, 27307, 32768, 0], - ], - ], - [ - [ - [18274, 24813, 27890, 32768, 0], - [15537, 23149, 27003, 32768, 0], - [9449, 16740, 21827, 32768, 0], - [6700, 12498, 17261, 32768, 0], - [4988, 9866, 14198, 32768, 0], - [4236, 8147, 11902, 32768, 0], - [2867, 5860, 8654, 32768, 0], - [17124, 23171, 26101, 32768, 0], - [20396, 27477, 30148, 32768, 0], - [16573, 24629, 28492, 32768, 0], - [12749, 20846, 25674, 32768, 0], - [10233, 17878, 22818, 32768, 0], - [8525, 15332, 20363, 32768, 0], - [6283, 11632, 16255, 32768, 0], - [20466, 26511, 29286, 32768, 0], - [23059, 29174, 31191, 32768, 0], - [19481, 27263, 30241, 32768, 0], - [15458, 23631, 28137, 32768, 0], - [12416, 20608, 25693, 32768, 0], - [10261, 18011, 23261, 32768, 0], - [8016, 14655, 19666, 32768, 0], - ], - [ - [17616, 24586, 28112, 32768, 0], - [15809, 23299, 27155, 32768, 0], - [10767, 18890, 23793, 32768, 0], - [7727, 14255, 18865, 32768, 0], - [6129, 11926, 16882, 32768, 0], - [4482, 9704, 14861, 32768, 0], - [3277, 7452, 11522, 32768, 0], - [22956, 28551, 30730, 32768, 0], - [22724, 28937, 30961, 32768, 0], - [18467, 26324, 29580, 32768, 0], - [13234, 20713, 25649, 32768, 0], - [11181, 17592, 22481, 32768, 0], - [8291, 18358, 24576, 32768, 0], - [7568, 11881, 14984, 32768, 0], - [24948, 29001, 31147, 32768, 0], - [25674, 30619, 32151, 32768, 0], - [20841, 26793, 29603, 32768, 0], - [14669, 24356, 28666, 32768, 0], - [11334, 23593, 28219, 32768, 0], - [8922, 14762, 22873, 32768, 0], - [8301, 13544, 20535, 32768, 0], - ], - ], - [ - [ - [17113, 23733, 27081, 32768, 0], - [14139, 21406, 25452, 32768, 0], - [8552, 15002, 19776, 32768, 0], - [5871, 11120, 15378, 32768, 0], - [4455, 8616, 12253, 32768, 0], - [3469, 6910, 10386, 32768, 0], - [2255, 4553, 6782, 32768, 0], - [18224, 24376, 27053, 32768, 0], - [19290, 26710, 29614, 32768, 0], - [14936, 22991, 27184, 32768, 0], - [11238, 18951, 23762, 32768, 0], - [8786, 15617, 20588, 32768, 0], - [7317, 13228, 18003, 32768, 0], - [5101, 9512, 13493, 32768, 0], - [22639, 28222, 30210, 32768, 0], - [23216, 29331, 31307, 32768, 0], - [19075, 26762, 29895, 32768, 0], - [15014, 23113, 27457, 32768, 0], - [11938, 19857, 24752, 32768, 0], - [9942, 17280, 22282, 32768, 0], - [7167, 13144, 17752, 32768, 0], - ], - [ - [15820, 22738, 26488, 32768, 0], - [13530, 20885, 25216, 32768, 0], - [8395, 15530, 20452, 32768, 0], - [6574, 12321, 16380, 32768, 0], - [5353, 10419, 14568, 32768, 0], - [4613, 8446, 12381, 32768, 0], - [3440, 7158, 9903, 32768, 0], - [24247, 29051, 31224, 32768, 0], - [22118, 28058, 30369, 32768, 0], - [16498, 24768, 28389, 32768, 0], - [12920, 21175, 26137, 32768, 0], - [10730, 18619, 25352, 32768, 0], - [10187, 16279, 22791, 32768, 0], - [9310, 14631, 22127, 32768, 0], - [24970, 30558, 32057, 32768, 0], - [24801, 29942, 31698, 32768, 0], - [22432, 28453, 30855, 32768, 0], - [19054, 25680, 29580, 32768, 0], - [14392, 23036, 28109, 32768, 0], - [12495, 20947, 26650, 32768, 0], - [12442, 20326, 26214, 32768, 0], - ], - ], - [ - [ - [12162, 18785, 22648, 32768, 0], - [12749, 19697, 23806, 32768, 0], - [8580, 15297, 20346, 32768, 0], - [6169, 11749, 16543, 32768, 0], - [4836, 9391, 13448, 32768, 0], - [3821, 7711, 11613, 32768, 0], - [2228, 4601, 7070, 32768, 0], - [16319, 24725, 28280, 32768, 0], - [15698, 23277, 27168, 32768, 0], - [12726, 20368, 25047, 32768, 0], - [9912, 17015, 21976, 32768, 0], - [7888, 14220, 19179, 32768, 0], - [6777, 12284, 17018, 32768, 0], - [4492, 8590, 12252, 32768, 0], - [23249, 28904, 30947, 32768, 0], - [21050, 27908, 30512, 32768, 0], - [17440, 25340, 28949, 32768, 0], - [14059, 22018, 26541, 32768, 0], - [11288, 18903, 23898, 32768, 0], - [9411, 16342, 21428, 32768, 0], - [6278, 11588, 15944, 32768, 0], - ], - [ - [13981, 20067, 23226, 32768, 0], - [16922, 23580, 26783, 32768, 0], - [11005, 19039, 24487, 32768, 0], - [7389, 14218, 19798, 32768, 0], - [5598, 11505, 17206, 32768, 0], - [6090, 11213, 15659, 32768, 0], - [3820, 7371, 10119, 32768, 0], - [21082, 26925, 29675, 32768, 0], - [21262, 28627, 31128, 32768, 0], - [18392, 26454, 30437, 32768, 0], - [14870, 22910, 27096, 32768, 0], - [12620, 19484, 24908, 32768, 0], - [9290, 16553, 22802, 32768, 0], - [6668, 14288, 20004, 32768, 0], - [27704, 31055, 31949, 32768, 0], - [24709, 29978, 31788, 32768, 0], - [21668, 29264, 31657, 32768, 0], - [18295, 26968, 30074, 32768, 0], - [16399, 24422, 29313, 32768, 0], - [14347, 23026, 28104, 32768, 0], - [12370, 19806, 24477, 32768, 0], - ], - ], - [ - [ - [8192, 16384, 24576, 32768, 0], - [8192, 16384, 24576, 32768, 0], - [8192, 16384, 24576, 32768, 0], - [8192, 16384, 24576, 32768, 0], - [8192, 16384, 24576, 32768, 0], - [8192, 16384, 24576, 32768, 0], - [8192, 16384, 24576, 32768, 0], - [8192, 16384, 24576, 32768, 0], - [8192, 16384, 24576, 32768, 0], - [8192, 16384, 24576, 32768, 0], - [8192, 16384, 24576, 32768, 0], - [8192, 16384, 24576, 32768, 0], - [8192, 16384, 24576, 32768, 0], - [8192, 16384, 24576, 32768, 0], - [8192, 16384, 24576, 32768, 0], - [8192, 16384, 24576, 32768, 0], - [8192, 16384, 24576, 32768, 0], - [8192, 16384, 24576, 32768, 0], - [8192, 16384, 24576, 32768, 0], - [8192, 16384, 24576, 32768, 0], - [8192, 16384, 24576, 32768, 0], - ], - [ - [8192, 16384, 24576, 32768, 0], - [8192, 16384, 24576, 32768, 0], - [8192, 16384, 24576, 32768, 0], - [8192, 16384, 24576, 32768, 0], - [8192, 16384, 24576, 32768, 0], - [8192, 16384, 24576, 32768, 0], - [8192, 16384, 24576, 32768, 0], - [8192, 16384, 24576, 32768, 0], - [8192, 16384, 24576, 32768, 0], - [8192, 16384, 24576, 32768, 0], - [8192, 16384, 24576, 32768, 0], - [8192, 16384, 24576, 32768, 0], - [8192, 16384, 24576, 32768, 0], - [8192, 16384, 24576, 32768, 0], - [8192, 16384, 24576, 32768, 0], - [8192, 16384, 24576, 32768, 0], - [8192, 16384, 24576, 32768, 0], - [8192, 16384, 24576, 32768, 0], - [8192, 16384, 24576, 32768, 0], - [8192, 16384, 24576, 32768, 0], - [8192, 16384, 24576, 32768, 0], - ], - ], - ], -]; - -pub static DEFAULT_DC_SIGN_CDF: [[[[u16; 3]; 3]; 2]; 4] = [ - [[[16000, 32768, 0], [13056, 32768, 0], [18816, 32768, 0]], [[15232, 32768, 0], [12928, 32768, 0], [17280, 32768, 0]]], - [[[16000, 32768, 0], [13056, 32768, 0], [18816, 32768, 0]], [[15232, 32768, 0], [12928, 32768, 0], [17280, 32768, 0]]], - [[[16000, 32768, 0], [13056, 32768, 0], [18816, 32768, 0]], [[15232, 32768, 0], [12928, 32768, 0], [17280, 32768, 0]]], - [[[16000, 32768, 0], [13056, 32768, 0], [18816, 32768, 0]], [[15232, 32768, 0], [12928, 32768, 0], [17280, 32768, 0]]], -]; - -pub static DEFAULT_DRL_MODE_CDF: [[u16; 3]; 3] = [[13104, 32768, 0], [24560, 32768, 0], [18945, 32768, 0]]; - -pub static DEFAULT_EOB_EXTRA_CDF: [[[[[u16; 3]; 9]; 2]; 5]; 4] = [ - [ - [ - [ - [16961, 32768, 0], - [17223, 32768, 0], - [7621, 32768, 0], - [16384, 32768, 0], - [16384, 32768, 0], - [16384, 32768, 0], - [16384, 32768, 0], - [16384, 32768, 0], - [16384, 32768, 0], - ], - [ - [19069, 32768, 0], - [22525, 32768, 0], - [13377, 32768, 0], - [16384, 32768, 0], - [16384, 32768, 0], - [16384, 32768, 0], - [16384, 32768, 0], - [16384, 32768, 0], - [16384, 32768, 0], - ], - ], - [ - [ - [20401, 32768, 0], - [17025, 32768, 0], - [12845, 32768, 0], - [12873, 32768, 0], - [14094, 32768, 0], - [16384, 32768, 0], - [16384, 32768, 0], - [16384, 32768, 0], - [16384, 32768, 0], - ], - [ - [20681, 32768, 0], - [20701, 32768, 0], - [15250, 32768, 0], - [15017, 32768, 0], - [14928, 32768, 0], - [16384, 32768, 0], - [16384, 32768, 0], - [16384, 32768, 0], - [16384, 32768, 0], - ], - ], - [ - [ - [23905, 32768, 0], - [17194, 32768, 0], - [16170, 32768, 0], - [17695, 32768, 0], - [13826, 32768, 0], - [15810, 32768, 0], - [12036, 32768, 0], - [16384, 32768, 0], - [16384, 32768, 0], - ], - [ - [23959, 32768, 0], - [20799, 32768, 0], - [19021, 32768, 0], - [16203, 32768, 0], - [17886, 32768, 0], - [14144, 32768, 0], - [12010, 32768, 0], - [16384, 32768, 0], - [16384, 32768, 0], - ], - ], - [ - [ - [27399, 32768, 0], - [16327, 32768, 0], - [18071, 32768, 0], - [19584, 32768, 0], - [20721, 32768, 0], - [18432, 32768, 0], - [19560, 32768, 0], - [10150, 32768, 0], - [8805, 32768, 0], - ], - [ - [24932, 32768, 0], - [20833, 32768, 0], - [12027, 32768, 0], - [16670, 32768, 0], - [19914, 32768, 0], - [15106, 32768, 0], - [17662, 32768, 0], - [13783, 32768, 0], - [28756, 32768, 0], - ], - ], - [ - [ - [23406, 32768, 0], - [21845, 32768, 0], - [18432, 32768, 0], - [16384, 32768, 0], - [17096, 32768, 0], - [12561, 32768, 0], - [17320, 32768, 0], - [22395, 32768, 0], - [21370, 32768, 0], - ], - [ - [16384, 32768, 0], - [16384, 32768, 0], - [16384, 32768, 0], - [16384, 32768, 0], - [16384, 32768, 0], - [16384, 32768, 0], - [16384, 32768, 0], - [16384, 32768, 0], - [16384, 32768, 0], - ], - ], - ], - [ - [ - [ - [17471, 32768, 0], - [20223, 32768, 0], - [11357, 32768, 0], - [16384, 32768, 0], - [16384, 32768, 0], - [16384, 32768, 0], - [16384, 32768, 0], - [16384, 32768, 0], - [16384, 32768, 0], - ], - [ - [20335, 32768, 0], - [21667, 32768, 0], - [14818, 32768, 0], - [16384, 32768, 0], - [16384, 32768, 0], - [16384, 32768, 0], - [16384, 32768, 0], - [16384, 32768, 0], - [16384, 32768, 0], - ], - ], - [ - [ - [20430, 32768, 0], - [20662, 32768, 0], - [15367, 32768, 0], - [16970, 32768, 0], - [14657, 32768, 0], - [16384, 32768, 0], - [16384, 32768, 0], - [16384, 32768, 0], - [16384, 32768, 0], - ], - [ - [22117, 32768, 0], - [22028, 32768, 0], - [18650, 32768, 0], - [16042, 32768, 0], - [15885, 32768, 0], - [16384, 32768, 0], - [16384, 32768, 0], - [16384, 32768, 0], - [16384, 32768, 0], - ], - ], - [ - [ - [22409, 32768, 0], - [21012, 32768, 0], - [15650, 32768, 0], - [17395, 32768, 0], - [15469, 32768, 0], - [20205, 32768, 0], - [19511, 32768, 0], - [16384, 32768, 0], - [16384, 32768, 0], - ], - [ - [24220, 32768, 0], - [22480, 32768, 0], - [17737, 32768, 0], - [18916, 32768, 0], - [19268, 32768, 0], - [18412, 32768, 0], - [18844, 32768, 0], - [16384, 32768, 0], - [16384, 32768, 0], - ], - ], - [ - [ - [25991, 32768, 0], - [20314, 32768, 0], - [17731, 32768, 0], - [19678, 32768, 0], - [18649, 32768, 0], - [17307, 32768, 0], - [21798, 32768, 0], - [17549, 32768, 0], - [15630, 32768, 0], - ], - [ - [26585, 32768, 0], - [21469, 32768, 0], - [20432, 32768, 0], - [17735, 32768, 0], - [19280, 32768, 0], - [15235, 32768, 0], - [20297, 32768, 0], - [22471, 32768, 0], - [28997, 32768, 0], - ], - ], - [ - [ - [26605, 32768, 0], - [11304, 32768, 0], - [16726, 32768, 0], - [16560, 32768, 0], - [20866, 32768, 0], - [23524, 32768, 0], - [19878, 32768, 0], - [13469, 32768, 0], - [23084, 32768, 0], - ], - [ - [16384, 32768, 0], - [16384, 32768, 0], - [16384, 32768, 0], - [16384, 32768, 0], - [16384, 32768, 0], - [16384, 32768, 0], - [16384, 32768, 0], - [16384, 32768, 0], - [16384, 32768, 0], - ], - ], - ], - [ - [ - [ - [18983, 32768, 0], - [20512, 32768, 0], - [14885, 32768, 0], - [16384, 32768, 0], - [16384, 32768, 0], - [16384, 32768, 0], - [16384, 32768, 0], - [16384, 32768, 0], - [16384, 32768, 0], - ], - [ - [20090, 32768, 0], - [19444, 32768, 0], - [17286, 32768, 0], - [16384, 32768, 0], - [16384, 32768, 0], - [16384, 32768, 0], - [16384, 32768, 0], - [16384, 32768, 0], - [16384, 32768, 0], - ], - ], - [ - [ - [19139, 32768, 0], - [21487, 32768, 0], - [18959, 32768, 0], - [20910, 32768, 0], - [19089, 32768, 0], - [16384, 32768, 0], - [16384, 32768, 0], - [16384, 32768, 0], - [16384, 32768, 0], - ], - [ - [20536, 32768, 0], - [20664, 32768, 0], - [20625, 32768, 0], - [19123, 32768, 0], - [14862, 32768, 0], - [16384, 32768, 0], - [16384, 32768, 0], - [16384, 32768, 0], - [16384, 32768, 0], - ], - ], - [ - [ - [19833, 32768, 0], - [21502, 32768, 0], - [17485, 32768, 0], - [20267, 32768, 0], - [18353, 32768, 0], - [23329, 32768, 0], - [21478, 32768, 0], - [16384, 32768, 0], - [16384, 32768, 0], - ], - [ - [22041, 32768, 0], - [23434, 32768, 0], - [20001, 32768, 0], - [20554, 32768, 0], - [20951, 32768, 0], - [20145, 32768, 0], - [15562, 32768, 0], - [16384, 32768, 0], - [16384, 32768, 0], - ], - ], - [ - [ - [23312, 32768, 0], - [21607, 32768, 0], - [16526, 32768, 0], - [18957, 32768, 0], - [18034, 32768, 0], - [18934, 32768, 0], - [24247, 32768, 0], - [16921, 32768, 0], - [17080, 32768, 0], - ], - [ - [26579, 32768, 0], - [24910, 32768, 0], - [18637, 32768, 0], - [19800, 32768, 0], - [20388, 32768, 0], - [9887, 32768, 0], - [15642, 32768, 0], - [30198, 32768, 0], - [24721, 32768, 0], - ], - ], - [ - [ - [26998, 32768, 0], - [16737, 32768, 0], - [17838, 32768, 0], - [18922, 32768, 0], - [19515, 32768, 0], - [18636, 32768, 0], - [17333, 32768, 0], - [15776, 32768, 0], - [22658, 32768, 0], - ], - [ - [16384, 32768, 0], - [16384, 32768, 0], - [16384, 32768, 0], - [16384, 32768, 0], - [16384, 32768, 0], - [16384, 32768, 0], - [16384, 32768, 0], - [16384, 32768, 0], - [16384, 32768, 0], - ], - ], - ], - [ - [ - [ - [20177, 32768, 0], - [20789, 32768, 0], - [20262, 32768, 0], - [16384, 32768, 0], - [16384, 32768, 0], - [16384, 32768, 0], - [16384, 32768, 0], - [16384, 32768, 0], - [16384, 32768, 0], - ], - [ - [21416, 32768, 0], - [20855, 32768, 0], - [23410, 32768, 0], - [16384, 32768, 0], - [16384, 32768, 0], - [16384, 32768, 0], - [16384, 32768, 0], - [16384, 32768, 0], - [16384, 32768, 0], - ], - ], - [ - [ - [20238, 32768, 0], - [21057, 32768, 0], - [19159, 32768, 0], - [22337, 32768, 0], - [20159, 32768, 0], - [16384, 32768, 0], - [16384, 32768, 0], - [16384, 32768, 0], - [16384, 32768, 0], - ], - [ - [20125, 32768, 0], - [20559, 32768, 0], - [21707, 32768, 0], - [22296, 32768, 0], - [17333, 32768, 0], - [16384, 32768, 0], - [16384, 32768, 0], - [16384, 32768, 0], - [16384, 32768, 0], - ], - ], - [ - [ - [19941, 32768, 0], - [20527, 32768, 0], - [21470, 32768, 0], - [22487, 32768, 0], - [19558, 32768, 0], - [22354, 32768, 0], - [20331, 32768, 0], - [16384, 32768, 0], - [16384, 32768, 0], - ], - [ - [22752, 32768, 0], - [25006, 32768, 0], - [22075, 32768, 0], - [21576, 32768, 0], - [17740, 32768, 0], - [21690, 32768, 0], - [19211, 32768, 0], - [16384, 32768, 0], - [16384, 32768, 0], - ], - ], - [ - [ - [21442, 32768, 0], - [22358, 32768, 0], - [18503, 32768, 0], - [20291, 32768, 0], - [19945, 32768, 0], - [21294, 32768, 0], - [21178, 32768, 0], - [19400, 32768, 0], - [10556, 32768, 0], - ], - [ - [24648, 32768, 0], - [24949, 32768, 0], - [20708, 32768, 0], - [23905, 32768, 0], - [20501, 32768, 0], - [9558, 32768, 0], - [9423, 32768, 0], - [30365, 32768, 0], - [19253, 32768, 0], - ], - ], - [ - [ - [26064, 32768, 0], - [22098, 32768, 0], - [19613, 32768, 0], - [20525, 32768, 0], - [17595, 32768, 0], - [16618, 32768, 0], - [20497, 32768, 0], - [18989, 32768, 0], - [15513, 32768, 0], - ], - [ - [16384, 32768, 0], - [16384, 32768, 0], - [16384, 32768, 0], - [16384, 32768, 0], - [16384, 32768, 0], - [16384, 32768, 0], - [16384, 32768, 0], - [16384, 32768, 0], - [16384, 32768, 0], - ], - ], - ], -]; - -pub static DEFAULT_EOB_PT_1024_CDF: [[[u16; 12]; 2]; 4] = [ - [[393, 421, 751, 1623, 3160, 6352, 13345, 18047, 22571, 25830, 32768, 0], [1865, 1988, 2930, 4242, 10533, 16538, 21354, 27255, 28546, 31784, 32768, 0]], - [[696, 948, 3145, 5702, 9706, 13217, 17851, 21856, 25692, 28034, 32768, 0], [2672, 3591, 9330, 17084, 22725, 24284, 26527, 28027, 28377, 30876, 32768, 0]], - [ - [2784, 3831, 7041, 10521, 14847, 18844, 23155, 26682, 29229, 31045, 32768, 0], - [9577, 12466, 17739, 20750, 22061, 23215, 24601, 25483, 25843, 32056, 32768, 0], - ], - [ - [6698, 8334, 11961, 15762, 20186, 23862, 27434, 29326, 31082, 32050, 32768, 0], - [20569, 22426, 25569, 26859, 28053, 28913, 29486, 29724, 29807, 32570, 32768, 0], - ], -]; - -pub static DEFAULT_EOB_PT_128_CDF: [[[[u16; 9]; 2]; 2]; 4] = [ - [ - [[219, 482, 1140, 2091, 3680, 6028, 12586, 32768, 0], [371, 699, 1254, 4830, 9479, 12562, 17497, 32768, 0]], - [[5245, 7456, 12880, 15852, 20033, 23932, 27608, 32768, 0], [2054, 3472, 5869, 14232, 18242, 20590, 26752, 32768, 0]], - ], - [ - [[685, 933, 1488, 2714, 4766, 8562, 19254, 32768, 0], [217, 352, 618, 2303, 5261, 9969, 17472, 32768, 0]], - [[8045, 11200, 15497, 19595, 23948, 27408, 30938, 32768, 0], [2310, 4160, 7471, 14997, 17931, 20768, 30240, 32768, 0]], - ], - [ - [[1366, 1738, 2527, 5016, 9355, 15797, 24643, 32768, 0], [354, 558, 944, 2760, 7287, 14037, 21779, 32768, 0]], - [[13627, 16246, 20173, 24429, 27948, 30415, 31863, 32768, 0], [6275, 9889, 14769, 23164, 27988, 30493, 32272, 32768, 0]], - ], - [ - [[3472, 4885, 7489, 12481, 18517, 24536, 29635, 32768, 0], [886, 1731, 3271, 8469, 15569, 22126, 28383, 32768, 0]], - [[24313, 26062, 28385, 30107, 31217, 31898, 32345, 32768, 0], [9165, 13282, 21150, 30286, 31894, 32571, 32712, 32768, 0]], - ], -]; - -pub static DEFAULT_EOB_PT_16_CDF: [[[[u16; 6]; 2]; 2]; 4] = [ - [[[840, 1039, 1980, 4895, 32768, 0], [370, 671, 1883, 4471, 32768, 0]], [[3247, 4950, 9688, 14563, 32768, 0], [1904, 3354, 7763, 14647, 32768, 0]]], - [[[2125, 2551, 5165, 8946, 32768, 0], [513, 765, 1859, 6339, 32768, 0]], [[7637, 9498, 14259, 19108, 32768, 0], [2497, 4096, 8866, 16993, 32768, 0]]], - [[[4016, 4897, 8881, 14968, 32768, 0], [716, 1105, 2646, 10056, 32768, 0]], [[11139, 13270, 18241, 23566, 32768, 0], [3192, 5032, 10297, 19755, 32768, 0]]], - [ - [[6708, 8958, 14746, 22133, 32768, 0], [1222, 2074, 4783, 15410, 32768, 0]], - [[19575, 21766, 26044, 29709, 32768, 0], [7297, 10767, 19273, 28194, 32768, 0]], - ], -]; - -pub static DEFAULT_EOB_PT_256_CDF: [[[[u16; 10]; 2]; 2]; 4] = [ - [ - [[310, 584, 1887, 3589, 6168, 8611, 11352, 15652, 32768, 0], [998, 1850, 2998, 5604, 17341, 19888, 22899, 25583, 32768, 0]], - [[2520, 3240, 5952, 8870, 12577, 17558, 19954, 24168, 32768, 0], [2203, 4130, 7435, 10739, 20652, 23681, 25609, 27261, 32768, 0]], - ], - [ - [[1448, 2109, 4151, 6263, 9329, 13260, 17944, 23300, 32768, 0], [399, 1019, 1749, 3038, 10444, 15546, 22739, 27294, 32768, 0]], - [[6402, 8148, 12623, 15072, 18728, 22847, 26447, 29377, 32768, 0], [1674, 3252, 5734, 10159, 22397, 23802, 24821, 30940, 32768, 0]], - ], - [ - [[3089, 3920, 6038, 9460, 14266, 19881, 25766, 29176, 32768, 0], [1084, 2358, 3488, 5122, 11483, 18103, 26023, 29799, 32768, 0]], - [[11514, 13794, 17480, 20754, 24361, 27378, 29492, 31277, 32768, 0], [6571, 9610, 15516, 21826, 29092, 30829, 31842, 32708, 32768, 0]], - ], - [ - [[5348, 7113, 11820, 15924, 22106, 26777, 30334, 31757, 32768, 0], [2453, 4474, 6307, 8777, 16474, 22975, 29000, 31547, 32768, 0]], - [[23110, 24597, 27140, 28894, 30167, 30927, 31392, 32094, 32768, 0], [9998, 17661, 25178, 28097, 31308, 32038, 32403, 32695, 32768, 0]], - ], -]; - -pub static DEFAULT_EOB_PT_32_CDF: [[[[u16; 7]; 2]; 2]; 4] = [ - [ - [[400, 520, 977, 2102, 6542, 32768, 0], [210, 405, 1315, 3326, 7537, 32768, 0]], - [[2636, 4273, 7588, 11794, 20401, 32768, 0], [1786, 3179, 6902, 11357, 19054, 32768, 0]], - ], - [ - [[989, 1249, 2019, 4151, 10785, 32768, 0], [313, 441, 1099, 2917, 8562, 32768, 0]], - [[8394, 10352, 13932, 18855, 26014, 32768, 0], [2578, 4124, 8181, 13670, 24234, 32768, 0]], - ], - [ - [[2515, 3003, 4452, 8162, 16041, 32768, 0], [574, 821, 1836, 5089, 13128, 32768, 0]], - [[13468, 16303, 20361, 25105, 29281, 32768, 0], [3542, 5502, 10415, 16760, 25644, 32768, 0]], - ], - [ - [[4617, 5709, 8446, 13584, 23135, 32768, 0], [1156, 1702, 3675, 9274, 20539, 32768, 0]], - [[22086, 24282, 27010, 29770, 31743, 32768, 0], [7699, 10897, 20891, 26926, 31628, 32768, 0]], - ], -]; - -pub static DEFAULT_EOB_PT_512_CDF: [[[u16; 11]; 2]; 4] = [ - [[641, 983, 3707, 5430, 10234, 14958, 18788, 23412, 26061, 32768, 0], [5095, 6446, 9996, 13354, 16017, 17986, 20919, 26129, 29140, 32768, 0]], - [[1230, 2278, 5035, 7776, 11871, 15346, 19590, 24584, 28749, 32768, 0], [7265, 9979, 15819, 19250, 21780, 23846, 26478, 28396, 31811, 32768, 0]], - [[2624, 3936, 6480, 9686, 13979, 17726, 23267, 28410, 31078, 32768, 0], [12015, 14769, 19588, 22052, 24222, 25812, 27300, 29219, 32114, 32768, 0]], - [[5927, 7809, 10923, 14597, 19439, 24135, 28456, 31142, 32060, 32768, 0], [21093, 23043, 25742, 27658, 29097, 29716, 30073, 30820, 31956, 32768, 0]], -]; - -pub static DEFAULT_EOB_PT_64_CDF: [[[[u16; 8]; 2]; 2]; 4] = [ - [ - [[329, 498, 1101, 1784, 3265, 7758, 32768, 0], [335, 730, 1459, 5494, 8755, 12997, 32768, 0]], - [[3505, 5304, 10086, 13814, 17684, 23370, 32768, 0], [1563, 2700, 4876, 10911, 14706, 22480, 32768, 0]], - ], - [ - [[1260, 1446, 2253, 3712, 6652, 13369, 32768, 0], [401, 605, 1029, 2563, 5845, 12626, 32768, 0]], - [[8609, 10612, 14624, 18714, 22614, 29024, 32768, 0], [1923, 3127, 5867, 9703, 14277, 27100, 32768, 0]], - ], - [ - [[2374, 2772, 4583, 7276, 12288, 19706, 32768, 0], [497, 810, 1315, 3000, 7004, 15641, 32768, 0]], - [[15050, 17126, 21410, 24886, 28156, 30726, 32768, 0], [4034, 6290, 10235, 14982, 21214, 28491, 32768, 0]], - ], - [ - [[6307, 7541, 12060, 16358, 22553, 27865, 32768, 0], [1289, 2320, 3971, 7926, 14153, 24291, 32768, 0]], - [[24212, 25708, 28268, 30035, 31307, 32049, 32768, 0], [8726, 12378, 19409, 26450, 30038, 32462, 32768, 0]], - ], -]; - -pub static DEFAULT_INTER_TX_TYPE_SET3_CDF: [[u16; 3]; 4] = [[16384, 32768, 0], [4167, 32768, 0], [1998, 32768, 0], [748, 32768, 0]]; - -pub static DEFAULT_INTRA_FRAME_Y_MODE_CDF: [[[u16; 14]; 5]; 5] = [ - [ - [15588, 17027, 19338, 20218, 20682, 21110, 21825, 23244, 24189, 28165, 29093, 30466, 32768, 0], - [12016, 18066, 19516, 20303, 20719, 21444, 21888, 23032, 24434, 28658, 30172, 31409, 32768, 0], - [10052, 10771, 22296, 22788, 23055, 23239, 24133, 25620, 26160, 29336, 29929, 31567, 32768, 0], - [14091, 15406, 16442, 18808, 19136, 19546, 19998, 22096, 24746, 29585, 30958, 32462, 32768, 0], - [12122, 13265, 15603, 16501, 18609, 20033, 22391, 25583, 26437, 30261, 31073, 32475, 32768, 0], - ], - [ - [10023, 19585, 20848, 21440, 21832, 22760, 23089, 24023, 25381, 29014, 30482, 31436, 32768, 0], - [5983, 24099, 24560, 24886, 25066, 25795, 25913, 26423, 27610, 29905, 31276, 31794, 32768, 0], - [7444, 12781, 20177, 20728, 21077, 21607, 22170, 23405, 24469, 27915, 29090, 30492, 32768, 0], - [8537, 14689, 15432, 17087, 17408, 18172, 18408, 19825, 24649, 29153, 31096, 32210, 32768, 0], - [7543, 14231, 15496, 16195, 17905, 20717, 21984, 24516, 26001, 29675, 30981, 31994, 32768, 0], - ], - [ - [12613, 13591, 21383, 22004, 22312, 22577, 23401, 25055, 25729, 29538, 30305, 32077, 32768, 0], - [9687, 13470, 18506, 19230, 19604, 20147, 20695, 22062, 23219, 27743, 29211, 30907, 32768, 0], - [6183, 6505, 26024, 26252, 26366, 26434, 27082, 28354, 28555, 30467, 30794, 32086, 32768, 0], - [10718, 11734, 14954, 17224, 17565, 17924, 18561, 21523, 23878, 28975, 30287, 32252, 32768, 0], - [9194, 9858, 16501, 17263, 18424, 19171, 21563, 25961, 26561, 30072, 30737, 32463, 32768, 0], - ], - [ - [12602, 14399, 15488, 18381, 18778, 19315, 19724, 21419, 25060, 29696, 30917, 32409, 32768, 0], - [8203, 13821, 14524, 17105, 17439, 18131, 18404, 19468, 25225, 29485, 31158, 32342, 32768, 0], - [8451, 9731, 15004, 17643, 18012, 18425, 19070, 21538, 24605, 29118, 30078, 32018, 32768, 0], - [7714, 9048, 9516, 16667, 16817, 16994, 17153, 18767, 26743, 30389, 31536, 32528, 32768, 0], - [8843, 10280, 11496, 15317, 16652, 17943, 19108, 22718, 25769, 29953, 30983, 32485, 32768, 0], - ], - [ - [12578, 13671, 15979, 16834, 19075, 20913, 22989, 25449, 26219, 30214, 31150, 32477, 32768, 0], - [9563, 13626, 15080, 15892, 17756, 20863, 22207, 24236, 25380, 29653, 31143, 32277, 32768, 0], - [8356, 8901, 17616, 18256, 19350, 20106, 22598, 25947, 26466, 29900, 30523, 32261, 32768, 0], - [10835, 11815, 13124, 16042, 17018, 18039, 18947, 22753, 24615, 29489, 30883, 32482, 32768, 0], - [7618, 8288, 9859, 10509, 15386, 18657, 22903, 28776, 29180, 31355, 31802, 32593, 32768, 0], - ], -]; - -pub static DEFAULT_INTRA_TX_TYPE_SET2_CDF: [[[u16; 6]; 13]; 3] = [ - [ - [6554, 13107, 19661, 26214, 32768, 0], - [6554, 13107, 19661, 26214, 32768, 0], - [6554, 13107, 19661, 26214, 32768, 0], - [6554, 13107, 19661, 26214, 32768, 0], - [6554, 13107, 19661, 26214, 32768, 0], - [6554, 13107, 19661, 26214, 32768, 0], - [6554, 13107, 19661, 26214, 32768, 0], - [6554, 13107, 19661, 26214, 32768, 0], - [6554, 13107, 19661, 26214, 32768, 0], - [6554, 13107, 19661, 26214, 32768, 0], - [6554, 13107, 19661, 26214, 32768, 0], - [6554, 13107, 19661, 26214, 32768, 0], - [6554, 13107, 19661, 26214, 32768, 0], - ], - [ - [6554, 13107, 19661, 26214, 32768, 0], - [6554, 13107, 19661, 26214, 32768, 0], - [6554, 13107, 19661, 26214, 32768, 0], - [6554, 13107, 19661, 26214, 32768, 0], - [6554, 13107, 19661, 26214, 32768, 0], - [6554, 13107, 19661, 26214, 32768, 0], - [6554, 13107, 19661, 26214, 32768, 0], - [6554, 13107, 19661, 26214, 32768, 0], - [6554, 13107, 19661, 26214, 32768, 0], - [6554, 13107, 19661, 26214, 32768, 0], - [6554, 13107, 19661, 26214, 32768, 0], - [6554, 13107, 19661, 26214, 32768, 0], - [6554, 13107, 19661, 26214, 32768, 0], - ], - [ - [1127, 12814, 22772, 27483, 32768, 0], - [145, 6761, 11980, 26667, 32768, 0], - [362, 5887, 11678, 16725, 32768, 0], - [385, 15213, 18587, 30693, 32768, 0], - [25, 2914, 23134, 27903, 32768, 0], - [60, 4470, 11749, 23991, 32768, 0], - [37, 3332, 14511, 21448, 32768, 0], - [157, 6320, 13036, 17439, 32768, 0], - [119, 6719, 12906, 29396, 32768, 0], - [47, 5537, 12576, 21499, 32768, 0], - [269, 6076, 11258, 23115, 32768, 0], - [83, 5615, 12001, 17228, 32768, 0], - [1968, 5556, 12023, 18547, 32768, 0], - ], -]; - -pub static DEFAULT_IS_INTER_CDF: [[u16; 3]; 4] = [[806, 32768, 0], [16662, 32768, 0], [20186, 32768, 0], [26538, 32768, 0]]; - -pub static DEFAULT_MV_BIT_CDF: [[u16; 3]; 10] = [ - [17408, 32768, 0], - [17920, 32768, 0], - [18944, 32768, 0], - [20480, 32768, 0], - [22528, 32768, 0], - [24576, 32768, 0], - [28672, 32768, 0], - [29952, 32768, 0], - [29952, 32768, 0], - [30720, 32768, 0], -]; - -pub static DEFAULT_MV_CLASS0_BIT_CDF: [u16; 3] = [27648, 32768, 0]; - -pub static DEFAULT_MV_CLASS0_FR_CDF: [[[u16; 5]; 2]; 2] = - [[[16384, 24576, 26624, 32768, 0], [12288, 21248, 24128, 32768, 0]], [[16384, 24576, 26624, 32768, 0], [12288, 21248, 24128, 32768, 0]]]; - -pub static DEFAULT_MV_CLASS0_HP_CDF: [u16; 3] = [20480, 32768, 0]; - -pub static DEFAULT_MV_CLASS_CDF: [[u16; 12]; 2] = [ - [28672, 30976, 31858, 32320, 32551, 32656, 32740, 32757, 32762, 32767, 32768, 0], - [28672, 30976, 31858, 32320, 32551, 32656, 32740, 32757, 32762, 32767, 32768, 0], -]; - -pub static DEFAULT_MV_FR_CDF: [[u16; 5]; 2] = [[8192, 17408, 21248, 32768, 0], [8192, 17408, 21248, 32768, 0]]; - -pub static DEFAULT_MV_HP_CDF: [u16; 3] = [16384, 32768, 0]; - -pub static DEFAULT_MV_JOINT_CDF: [u16; 5] = [4096, 11264, 19328, 32768, 0]; - -pub static DEFAULT_MV_SIGN_CDF: [u16; 3] = [16384, 32768, 0]; - -pub static DEFAULT_NEW_MV_CDF: [[u16; 3]; 6] = [[24035, 32768, 0], [16630, 32768, 0], [15339, 32768, 0], [8386, 32768, 0], [12222, 32768, 0], [4676, 32768, 0]]; - -pub static DEFAULT_PARTITION_W16_CDF: [[u16; 11]; 4] = [ - [15597, 20929, 24571, 26706, 27664, 28821, 29601, 30571, 31902, 32768, 0], - [7925, 11043, 16785, 22470, 23971, 25043, 26651, 28701, 29834, 32768, 0], - [5414, 13269, 15111, 20488, 22360, 24500, 25537, 26336, 32117, 32768, 0], - [2662, 6362, 8614, 20860, 23053, 24778, 26436, 27829, 31171, 32768, 0], -]; - -pub static DEFAULT_PARTITION_W32_CDF: [[u16; 11]; 4] = [ - [18462, 20920, 23124, 27647, 28227, 29049, 29519, 30178, 31544, 32768, 0], - [7689, 9060, 12056, 24992, 25660, 26182, 26951, 28041, 29052, 32768, 0], - [6015, 9009, 10062, 24544, 25409, 26545, 27071, 27526, 32047, 32768, 0], - [1394, 2208, 2796, 28614, 29061, 29466, 29840, 30185, 31899, 32768, 0], -]; - -pub static DEFAULT_PARTITION_W64_CDF: [[u16; 11]; 4] = [ - [20137, 21547, 23078, 29566, 29837, 30261, 30524, 30892, 31724, 32768, 0], - [6732, 7490, 9497, 27944, 28250, 28515, 28969, 29630, 30104, 32768, 0], - [5945, 7663, 8348, 28683, 29117, 29749, 30064, 30298, 32238, 32768, 0], - [870, 1212, 1487, 31198, 31394, 31574, 31743, 31881, 32332, 32768, 0], -]; - -pub static DEFAULT_PARTITION_W8_CDF: [[u16; 5]; 4] = - [[19132, 25510, 30392, 32768, 0], [13928, 19855, 28540, 32768, 0], [12522, 23679, 28629, 32768, 0], [9896, 18783, 25853, 32768, 0]]; - -pub static DEFAULT_REF_MV_CDF: [[u16; 3]; 6] = - [[23974, 32768, 0], [24188, 32768, 0], [17848, 32768, 0], [28622, 32768, 0], [24312, 32768, 0], [19923, 32768, 0]]; - -pub static DEFAULT_SCAN_16X16: [u16; 256] = [ - 0, 1, 16, 32, 17, 2, 3, 18, 33, 48, 64, 49, 34, 19, 4, 5, 20, 35, 50, 65, 80, 96, 81, 66, 51, 36, 21, 6, 7, 22, 37, 52, 67, 82, 97, 112, 128, 113, 98, 83, - 68, 53, 38, 23, 8, 9, 24, 39, 54, 69, 84, 99, 114, 129, 144, 160, 145, 130, 115, 100, 85, 70, 55, 40, 25, 10, 11, 26, 41, 56, 71, 86, 101, 116, 131, 146, - 161, 176, 192, 177, 162, 147, 132, 117, 102, 87, 72, 57, 42, 27, 12, 13, 28, 43, 58, 73, 88, 103, 118, 133, 148, 163, 178, 193, 208, 224, 209, 194, 179, - 164, 149, 134, 119, 104, 89, 74, 59, 44, 29, 14, 15, 30, 45, 60, 75, 90, 105, 120, 135, 150, 165, 180, 195, 210, 225, 240, 241, 226, 211, 196, 181, 166, - 151, 136, 121, 106, 91, 76, 61, 46, 31, 47, 62, 77, 92, 107, 122, 137, 152, 167, 182, 197, 212, 227, 242, 243, 228, 213, 198, 183, 168, 153, 138, 123, 108, - 93, 78, 63, 79, 94, 109, 124, 139, 154, 169, 184, 199, 214, 229, 244, 245, 230, 215, 200, 185, 170, 155, 140, 125, 110, 95, 111, 126, 141, 156, 171, 186, - 201, 216, 231, 246, 247, 232, 217, 202, 187, 172, 157, 142, 127, 143, 158, 173, 188, 203, 218, 233, 248, 249, 234, 219, 204, 189, 174, 159, 175, 190, 205, - 220, 235, 250, 251, 236, 221, 206, 191, 207, 222, 237, 252, 253, 238, 223, 239, 254, 255, -]; - -pub static DEFAULT_SCAN_32X32: [u16; 1024] = [ - 0, 1, 32, 64, 33, 2, 3, 34, 65, 96, 128, 97, 66, 35, 4, 5, 36, 67, 98, 129, 160, 192, 161, 130, 99, 68, 37, 6, 7, 38, 69, 100, 131, 162, 193, 224, 256, - 225, 194, 163, 132, 101, 70, 39, 8, 9, 40, 71, 102, 133, 164, 195, 226, 257, 288, 320, 289, 258, 227, 196, 165, 134, 103, 72, 41, 10, 11, 42, 73, 104, 135, - 166, 197, 228, 259, 290, 321, 352, 384, 353, 322, 291, 260, 229, 198, 167, 136, 105, 74, 43, 12, 13, 44, 75, 106, 137, 168, 199, 230, 261, 292, 323, 354, - 385, 416, 448, 417, 386, 355, 324, 293, 262, 231, 200, 169, 138, 107, 76, 45, 14, 15, 46, 77, 108, 139, 170, 201, 232, 263, 294, 325, 356, 387, 418, 449, - 480, 512, 481, 450, 419, 388, 357, 326, 295, 264, 233, 202, 171, 140, 109, 78, 47, 16, 17, 48, 79, 110, 141, 172, 203, 234, 265, 296, 327, 358, 389, 420, - 451, 482, 513, 544, 576, 545, 514, 483, 452, 421, 390, 359, 328, 297, 266, 235, 204, 173, 142, 111, 80, 49, 18, 19, 50, 81, 112, 143, 174, 205, 236, 267, - 298, 329, 360, 391, 422, 453, 484, 515, 546, 577, 608, 640, 609, 578, 547, 516, 485, 454, 423, 392, 361, 330, 299, 268, 237, 206, 175, 144, 113, 82, 51, - 20, 21, 52, 83, 114, 145, 176, 207, 238, 269, 300, 331, 362, 393, 424, 455, 486, 517, 548, 579, 610, 641, 672, 704, 673, 642, 611, 580, 549, 518, 487, 456, - 425, 394, 363, 332, 301, 270, 239, 208, 177, 146, 115, 84, 53, 22, 23, 54, 85, 116, 147, 178, 209, 240, 271, 302, 333, 364, 395, 426, 457, 488, 519, 550, - 581, 612, 643, 674, 705, 736, 768, 737, 706, 675, 644, 613, 582, 551, 520, 489, 458, 427, 396, 365, 334, 303, 272, 241, 210, 179, 148, 117, 86, 55, 24, 25, - 56, 87, 118, 149, 180, 211, 242, 273, 304, 335, 366, 397, 428, 459, 490, 521, 552, 583, 614, 645, 676, 707, 738, 769, 800, 832, 801, 770, 739, 708, 677, - 646, 615, 584, 553, 522, 491, 460, 429, 398, 367, 336, 305, 274, 243, 212, 181, 150, 119, 88, 57, 26, 27, 58, 89, 120, 151, 182, 213, 244, 275, 306, 337, - 368, 399, 430, 461, 492, 523, 554, 585, 616, 647, 678, 709, 740, 771, 802, 833, 864, 896, 865, 834, 803, 772, 741, 710, 679, 648, 617, 586, 555, 524, 493, - 462, 431, 400, 369, 338, 307, 276, 245, 214, 183, 152, 121, 90, 59, 28, 29, 60, 91, 122, 153, 184, 215, 246, 277, 308, 339, 370, 401, 432, 463, 494, 525, - 556, 587, 618, 649, 680, 711, 742, 773, 804, 835, 866, 897, 928, 960, 929, 898, 867, 836, 805, 774, 743, 712, 681, 650, 619, 588, 557, 526, 495, 464, 433, - 402, 371, 340, 309, 278, 247, 216, 185, 154, 123, 92, 61, 30, 31, 62, 93, 124, 155, 186, 217, 248, 279, 310, 341, 372, 403, 434, 465, 496, 527, 558, 589, - 620, 651, 682, 713, 744, 775, 806, 837, 868, 899, 930, 961, 992, 993, 962, 931, 900, 869, 838, 807, 776, 745, 714, 683, 652, 621, 590, 559, 528, 497, 466, - 435, 404, 373, 342, 311, 280, 249, 218, 187, 156, 125, 94, 63, 95, 126, 157, 188, 219, 250, 281, 312, 343, 374, 405, 436, 467, 498, 529, 560, 591, 622, - 653, 684, 715, 746, 777, 808, 839, 870, 901, 932, 963, 994, 995, 964, 933, 902, 871, 840, 809, 778, 747, 716, 685, 654, 623, 592, 561, 530, 499, 468, 437, - 406, 375, 344, 313, 282, 251, 220, 189, 158, 127, 159, 190, 221, 252, 283, 314, 345, 376, 407, 438, 469, 500, 531, 562, 593, 624, 655, 686, 717, 748, 779, - 810, 841, 872, 903, 934, 965, 996, 997, 966, 935, 904, 873, 842, 811, 780, 749, 718, 687, 656, 625, 594, 563, 532, 501, 470, 439, 408, 377, 346, 315, 284, - 253, 222, 191, 223, 254, 285, 316, 347, 378, 409, 440, 471, 502, 533, 564, 595, 626, 657, 688, 719, 750, 781, 812, 843, 874, 905, 936, 967, 998, 999, 968, - 937, 906, 875, 844, 813, 782, 751, 720, 689, 658, 627, 596, 565, 534, 503, 472, 441, 410, 379, 348, 317, 286, 255, 287, 318, 349, 380, 411, 442, 473, 504, - 535, 566, 597, 628, 659, 690, 721, 752, 783, 814, 845, 876, 907, 938, 969, 1000, 1001, 970, 939, 908, 877, 846, 815, 784, 753, 722, 691, 660, 629, 598, - 567, 536, 505, 474, 443, 412, 381, 350, 319, 351, 382, 413, 444, 475, 506, 537, 568, 599, 630, 661, 692, 723, 754, 785, 816, 847, 878, 909, 940, 971, 1002, - 1003, 972, 941, 910, 879, 848, 817, 786, 755, 724, 693, 662, 631, 600, 569, 538, 507, 476, 445, 414, 383, 415, 446, 477, 508, 539, 570, 601, 632, 663, 694, - 725, 756, 787, 818, 849, 880, 911, 942, 973, 1004, 1005, 974, 943, 912, 881, 850, 819, 788, 757, 726, 695, 664, 633, 602, 571, 540, 509, 478, 447, 479, - 510, 541, 572, 603, 634, 665, 696, 727, 758, 789, 820, 851, 882, 913, 944, 975, 1006, 1007, 976, 945, 914, 883, 852, 821, 790, 759, 728, 697, 666, 635, - 604, 573, 542, 511, 543, 574, 605, 636, 667, 698, 729, 760, 791, 822, 853, 884, 915, 946, 977, 1008, 1009, 978, 947, 916, 885, 854, 823, 792, 761, 730, - 699, 668, 637, 606, 575, 607, 638, 669, 700, 731, 762, 793, 824, 855, 886, 917, 948, 979, 1010, 1011, 980, 949, 918, 887, 856, 825, 794, 763, 732, 701, - 670, 639, 671, 702, 733, 764, 795, 826, 857, 888, 919, 950, 981, 1012, 1013, 982, 951, 920, 889, 858, 827, 796, 765, 734, 703, 735, 766, 797, 828, 859, - 890, 921, 952, 983, 1014, 1015, 984, 953, 922, 891, 860, 829, 798, 767, 799, 830, 861, 892, 923, 954, 985, 1016, 1017, 986, 955, 924, 893, 862, 831, 863, - 894, 925, 956, 987, 1018, 1019, 988, 957, 926, 895, 927, 958, 989, 1020, 1021, 990, 959, 991, 1022, 1023, -]; - -pub static DEFAULT_SCAN_4X4: [u16; 16] = [0, 1, 4, 8, 5, 2, 3, 6, 9, 12, 13, 10, 7, 11, 14, 15]; - -pub static DEFAULT_SCAN_8X8: [u16; 64] = [ - 0, 1, 8, 16, 9, 2, 3, 10, 17, 24, 32, 25, 18, 11, 4, 5, 12, 19, 26, 33, 40, 48, 41, 34, 27, 20, 13, 6, 7, 14, 21, 28, 35, 42, 49, 56, 57, 50, 43, 36, 29, - 22, 15, 23, 30, 37, 44, 51, 58, 59, 52, 45, 38, 31, 39, 46, 53, 60, 61, 54, 47, 55, 62, 63, -]; - -pub static DEFAULT_SINGLE_REF_CDF: [[[u16; 3]; 6]; 3] = [ - [[4897, 32768, 0], [1555, 32768, 0], [4236, 32768, 0], [8650, 32768, 0], [904, 32768, 0], [1444, 32768, 0]], - [[16973, 32768, 0], [16751, 32768, 0], [19647, 32768, 0], [24773, 32768, 0], [11014, 32768, 0], [15087, 32768, 0]], - [[29744, 32768, 0], [30279, 32768, 0], [31194, 32768, 0], [31895, 32768, 0], [26875, 32768, 0], [30304, 32768, 0]], -]; - -pub static DEFAULT_SKIP_CDF: [[u16; 3]; 3] = [[31671, 32768, 0], [16515, 32768, 0], [4576, 32768, 0]]; - -pub static DEFAULT_TXB_SKIP_CDF: [[[[u16; 3]; 13]; 5]; 4] = [ - [ - [ - [31849, 32768, 0], - [5892, 32768, 0], - [12112, 32768, 0], - [21935, 32768, 0], - [20289, 32768, 0], - [27473, 32768, 0], - [32487, 32768, 0], - [7654, 32768, 0], - [19473, 32768, 0], - [29984, 32768, 0], - [9961, 32768, 0], - [30242, 32768, 0], - [32117, 32768, 0], - ], - [ - [31548, 32768, 0], - [1549, 32768, 0], - [10130, 32768, 0], - [16656, 32768, 0], - [18591, 32768, 0], - [26308, 32768, 0], - [32537, 32768, 0], - [5403, 32768, 0], - [18096, 32768, 0], - [30003, 32768, 0], - [16384, 32768, 0], - [16384, 32768, 0], - [16384, 32768, 0], - ], - [ - [29957, 32768, 0], - [5391, 32768, 0], - [18039, 32768, 0], - [23566, 32768, 0], - [22431, 32768, 0], - [25822, 32768, 0], - [32197, 32768, 0], - [3778, 32768, 0], - [15336, 32768, 0], - [28981, 32768, 0], - [16384, 32768, 0], - [16384, 32768, 0], - [16384, 32768, 0], - ], - [ - [17920, 32768, 0], - [1818, 32768, 0], - [7282, 32768, 0], - [25273, 32768, 0], - [10923, 32768, 0], - [31554, 32768, 0], - [32624, 32768, 0], - [1366, 32768, 0], - [15628, 32768, 0], - [30462, 32768, 0], - [146, 32768, 0], - [5132, 32768, 0], - [31657, 32768, 0], - ], - [ - [6308, 32768, 0], - [117, 32768, 0], - [1638, 32768, 0], - [2161, 32768, 0], - [16384, 32768, 0], - [10923, 32768, 0], - [30247, 32768, 0], - [16384, 32768, 0], - [16384, 32768, 0], - [16384, 32768, 0], - [16384, 32768, 0], - [16384, 32768, 0], - [16384, 32768, 0], - ], - ], - [ - [ - [30371, 32768, 0], - [7570, 32768, 0], - [13155, 32768, 0], - [20751, 32768, 0], - [20969, 32768, 0], - [27067, 32768, 0], - [32013, 32768, 0], - [5495, 32768, 0], - [17942, 32768, 0], - [28280, 32768, 0], - [16384, 32768, 0], - [16384, 32768, 0], - [16384, 32768, 0], - ], - [ - [31782, 32768, 0], - [1836, 32768, 0], - [10689, 32768, 0], - [17604, 32768, 0], - [21622, 32768, 0], - [27518, 32768, 0], - [32399, 32768, 0], - [4419, 32768, 0], - [16294, 32768, 0], - [28345, 32768, 0], - [16384, 32768, 0], - [16384, 32768, 0], - [16384, 32768, 0], - ], - [ - [31901, 32768, 0], - [10311, 32768, 0], - [18047, 32768, 0], - [24806, 32768, 0], - [23288, 32768, 0], - [27914, 32768, 0], - [32296, 32768, 0], - [4215, 32768, 0], - [15756, 32768, 0], - [28341, 32768, 0], - [16384, 32768, 0], - [16384, 32768, 0], - [16384, 32768, 0], - ], - [ - [26726, 32768, 0], - [1045, 32768, 0], - [11703, 32768, 0], - [20590, 32768, 0], - [18554, 32768, 0], - [25970, 32768, 0], - [31938, 32768, 0], - [5583, 32768, 0], - [21313, 32768, 0], - [29390, 32768, 0], - [641, 32768, 0], - [22265, 32768, 0], - [31452, 32768, 0], - ], - [ - [26584, 32768, 0], - [188, 32768, 0], - [8847, 32768, 0], - [24519, 32768, 0], - [22938, 32768, 0], - [30583, 32768, 0], - [32608, 32768, 0], - [16384, 32768, 0], - [16384, 32768, 0], - [16384, 32768, 0], - [16384, 32768, 0], - [16384, 32768, 0], - [16384, 32768, 0], - ], - ], - [ - [ - [29614, 32768, 0], - [9068, 32768, 0], - [12924, 32768, 0], - [19538, 32768, 0], - [17737, 32768, 0], - [24619, 32768, 0], - [30642, 32768, 0], - [4119, 32768, 0], - [16026, 32768, 0], - [25657, 32768, 0], - [16384, 32768, 0], - [16384, 32768, 0], - [16384, 32768, 0], - ], - [ - [31957, 32768, 0], - [3230, 32768, 0], - [11153, 32768, 0], - [18123, 32768, 0], - [20143, 32768, 0], - [26536, 32768, 0], - [31986, 32768, 0], - [3050, 32768, 0], - [14603, 32768, 0], - [25155, 32768, 0], - [16384, 32768, 0], - [16384, 32768, 0], - [16384, 32768, 0], - ], - [ - [32363, 32768, 0], - [10692, 32768, 0], - [19090, 32768, 0], - [24357, 32768, 0], - [24442, 32768, 0], - [28312, 32768, 0], - [32169, 32768, 0], - [3648, 32768, 0], - [15690, 32768, 0], - [26815, 32768, 0], - [16384, 32768, 0], - [16384, 32768, 0], - [16384, 32768, 0], - ], - [ - [30669, 32768, 0], - [3832, 32768, 0], - [11663, 32768, 0], - [18889, 32768, 0], - [19782, 32768, 0], - [23313, 32768, 0], - [31330, 32768, 0], - [5124, 32768, 0], - [18719, 32768, 0], - [28468, 32768, 0], - [3082, 32768, 0], - [20982, 32768, 0], - [29443, 32768, 0], - ], - [ - [28573, 32768, 0], - [3183, 32768, 0], - [17802, 32768, 0], - [25977, 32768, 0], - [26677, 32768, 0], - [27832, 32768, 0], - [32387, 32768, 0], - [16384, 32768, 0], - [16384, 32768, 0], - [16384, 32768, 0], - [16384, 32768, 0], - [16384, 32768, 0], - [16384, 32768, 0], - ], - ], - [ - [ - [26887, 32768, 0], - [6729, 32768, 0], - [10361, 32768, 0], - [17442, 32768, 0], - [15045, 32768, 0], - [22478, 32768, 0], - [29072, 32768, 0], - [2713, 32768, 0], - [11861, 32768, 0], - [20773, 32768, 0], - [16384, 32768, 0], - [16384, 32768, 0], - [16384, 32768, 0], - ], - [ - [31903, 32768, 0], - [2044, 32768, 0], - [7528, 32768, 0], - [14618, 32768, 0], - [16182, 32768, 0], - [24168, 32768, 0], - [31037, 32768, 0], - [2786, 32768, 0], - [11194, 32768, 0], - [20155, 32768, 0], - [16384, 32768, 0], - [16384, 32768, 0], - [16384, 32768, 0], - ], - [ - [32510, 32768, 0], - [8430, 32768, 0], - [17318, 32768, 0], - [24154, 32768, 0], - [23674, 32768, 0], - [28789, 32768, 0], - [32139, 32768, 0], - [3440, 32768, 0], - [13117, 32768, 0], - [22702, 32768, 0], - [16384, 32768, 0], - [16384, 32768, 0], - [16384, 32768, 0], - ], - [ - [31671, 32768, 0], - [2056, 32768, 0], - [11746, 32768, 0], - [16852, 32768, 0], - [18635, 32768, 0], - [24715, 32768, 0], - [31484, 32768, 0], - [4656, 32768, 0], - [16074, 32768, 0], - [24704, 32768, 0], - [1806, 32768, 0], - [14645, 32768, 0], - [25336, 32768, 0], - ], - [ - [31539, 32768, 0], - [8433, 32768, 0], - [20576, 32768, 0], - [27904, 32768, 0], - [27852, 32768, 0], - [30026, 32768, 0], - [32441, 32768, 0], - [16384, 32768, 0], - [16384, 32768, 0], - [16384, 32768, 0], - [16384, 32768, 0], - [16384, 32768, 0], - [16384, 32768, 0], - ], - ], -]; - -pub static DEFAULT_UV_MODE_CFL_ALLOWED_CDF: [[u16; 15]; 13] = [ - [10407, 11208, 12900, 13181, 13823, 14175, 14899, 15656, 15986, 20086, 20995, 22455, 24212, 32768, 0], - [4532, 19780, 20057, 20215, 20428, 21071, 21199, 21451, 22099, 24228, 24693, 27032, 29472, 32768, 0], - [5273, 5379, 20177, 20270, 20385, 20439, 20949, 21695, 21774, 23138, 24256, 24703, 26679, 32768, 0], - [6740, 7167, 7662, 14152, 14536, 14785, 15034, 16741, 18371, 21520, 22206, 23389, 24182, 32768, 0], - [4987, 5368, 5928, 6068, 19114, 20315, 21857, 22253, 22411, 24911, 25380, 26027, 26376, 32768, 0], - [5370, 6889, 7247, 7393, 9498, 21114, 21402, 21753, 21981, 24780, 25386, 26517, 27176, 32768, 0], - [4816, 4961, 7204, 7326, 8765, 8930, 20169, 20682, 20803, 23188, 23763, 24455, 24940, 32768, 0], - [6608, 6740, 8529, 9049, 9257, 9356, 9735, 18827, 19059, 22336, 23204, 23964, 24793, 32768, 0], - [5998, 7419, 7781, 8933, 9255, 9549, 9753, 10417, 18898, 22494, 23139, 24764, 25989, 32768, 0], - [10660, 11298, 12550, 12957, 13322, 13624, 14040, 15004, 15534, 20714, 21789, 23443, 24861, 32768, 0], - [10522, 11530, 12552, 12963, 13378, 13779, 14245, 15235, 15902, 20102, 22696, 23774, 25838, 32768, 0], - [10099, 10691, 12639, 13049, 13386, 13665, 14125, 15163, 15636, 19676, 20474, 23519, 25208, 32768, 0], - [3144, 5087, 7382, 7504, 7593, 7690, 7801, 8064, 8232, 9248, 9875, 10521, 29048, 32768, 0], -]; - -pub static DEFAULT_UV_MODE_CFL_NOT_ALLOWED_CDF: [[u16; 14]; 13] = [ - [22631, 24152, 25378, 25661, 25986, 26520, 27055, 27923, 28244, 30059, 30941, 31961, 32768, 0], - [9513, 26881, 26973, 27046, 27118, 27664, 27739, 27824, 28359, 29505, 29800, 31796, 32768, 0], - [9845, 9915, 28663, 28704, 28757, 28780, 29198, 29822, 29854, 30764, 31777, 32029, 32768, 0], - [13639, 13897, 14171, 25331, 25606, 25727, 25953, 27148, 28577, 30612, 31355, 32493, 32768, 0], - [9764, 9835, 9930, 9954, 25386, 27053, 27958, 28148, 28243, 31101, 31744, 32363, 32768, 0], - [11825, 13589, 13677, 13720, 15048, 29213, 29301, 29458, 29711, 31161, 31441, 32550, 32768, 0], - [14175, 14399, 16608, 16821, 17718, 17775, 28551, 30200, 30245, 31837, 32342, 32667, 32768, 0], - [12885, 13038, 14978, 15590, 15673, 15748, 16176, 29128, 29267, 30643, 31961, 32461, 32768, 0], - [12026, 13661, 13874, 15305, 15490, 15726, 15995, 16273, 28443, 30388, 30767, 32416, 32768, 0], - [19052, 19840, 20579, 20916, 21150, 21467, 21885, 22719, 23174, 28861, 30379, 32175, 32768, 0], - [18627, 19649, 20974, 21219, 21492, 21816, 22199, 23119, 23527, 27053, 31397, 32148, 32768, 0], - [17026, 19004, 19997, 20339, 20586, 21103, 21349, 21907, 22482, 25896, 26541, 31819, 32768, 0], - [12124, 13759, 14959, 14992, 15007, 15051, 15078, 15166, 15255, 15753, 16039, 16606, 32768, 0], -]; - -pub static DEFAULT_Y_MODE_CDF: [[u16; 14]; 4] = [ - [22801, 23489, 24293, 24756, 25601, 26123, 26606, 27418, 27945, 29228, 29685, 30349, 32768, 0], - [18673, 19845, 22631, 23318, 23950, 24649, 25527, 27364, 28152, 29701, 29984, 30852, 32768, 0], - [19770, 20979, 23396, 23939, 24241, 24654, 25136, 27073, 27830, 29360, 29730, 30659, 32768, 0], - [20155, 21301, 22838, 23178, 23261, 23533, 23703, 24804, 25352, 26575, 27016, 28049, 32768, 0], -]; - -pub static DEFAULT_ZERO_MV_CDF: [[u16; 3]; 2] = [[2175, 32768, 0], [1054, 32768, 0]]; - -pub static INTRA_MODE_CONTEXT: [u8; 13] = [0, 1, 2, 3, 4, 4, 4, 4, 3, 0, 1, 2, 0]; - -pub static MAG_REF_OFFSET_WITH_TX_CLASS: [[[u8; 2]; 3]; 3] = [[[0, 1], [1, 0], [1, 1]], [[0, 1], [1, 0], [0, 2]], [[0, 1], [1, 0], [2, 0]]]; - -pub static MAX_TX_SIZE_RECT: [u8; 22] = [0, 5, 6, 1, 7, 8, 2, 9, 10, 3, 11, 12, 4, 4, 4, 4, 13, 14, 15, 16, 17, 18]; - -pub static MI_HEIGHT_LOG2: [u8; 22] = [0, 1, 0, 1, 2, 1, 2, 3, 2, 3, 4, 3, 4, 5, 4, 5, 2, 0, 3, 1, 4, 2]; - -pub static MI_WIDTH_LOG2: [u8; 22] = [0, 0, 1, 1, 1, 2, 2, 2, 3, 3, 3, 4, 4, 4, 5, 5, 0, 2, 1, 3, 2, 4]; - -pub static MODE_TO_TXFM: [u8; 14] = [0, 1, 2, 0, 3, 1, 2, 2, 1, 3, 1, 2, 3, 0]; - -pub static NUM_4X4_BLOCKS_HIGH: [u8; 22] = [1, 2, 1, 2, 4, 2, 4, 8, 4, 8, 16, 8, 16, 32, 16, 32, 4, 1, 8, 2, 16, 4]; - -pub static NUM_4X4_BLOCKS_WIDE: [u8; 22] = [1, 1, 2, 2, 2, 4, 4, 4, 8, 8, 8, 16, 16, 16, 32, 32, 1, 4, 2, 8, 4, 16]; - -pub static PARTITION_SUBSIZE: [[u8; 22]; 10] = [ - [0, 22, 22, 3, 22, 22, 6, 22, 22, 9, 22, 22, 12, 22, 22, 15, 22, 22, 22, 22, 22, 22], - [22, 22, 22, 2, 22, 22, 5, 22, 22, 8, 22, 22, 11, 22, 22, 14, 22, 22, 22, 22, 22, 22], - [22, 22, 22, 1, 22, 22, 4, 22, 22, 7, 22, 22, 10, 22, 22, 13, 22, 22, 22, 22, 22, 22], - [22, 22, 22, 0, 22, 22, 3, 22, 22, 6, 22, 22, 9, 22, 22, 12, 22, 22, 22, 22, 22, 22], - [22, 22, 22, 2, 22, 22, 5, 22, 22, 8, 22, 22, 11, 22, 22, 14, 22, 22, 22, 22, 22, 22], - [22, 22, 22, 2, 22, 22, 5, 22, 22, 8, 22, 22, 11, 22, 22, 14, 22, 22, 22, 22, 22, 22], - [22, 22, 22, 1, 22, 22, 4, 22, 22, 7, 22, 22, 10, 22, 22, 13, 22, 22, 22, 22, 22, 22], - [22, 22, 22, 1, 22, 22, 4, 22, 22, 7, 22, 22, 10, 22, 22, 13, 22, 22, 22, 22, 22, 22], - [22, 22, 22, 22, 22, 22, 17, 22, 22, 19, 22, 22, 21, 22, 22, 22, 22, 22, 22, 22, 22, 22], - [22, 22, 22, 22, 22, 22, 16, 22, 22, 18, 22, 22, 20, 22, 22, 22, 22, 22, 22, 22, 22, 22], -]; - -pub static SIG_REF_DIFF_OFFSET: [[[u8; 2]; 5]; 3] = - [[[0, 1], [1, 0], [1, 1], [0, 2], [2, 0]], [[0, 1], [1, 0], [0, 2], [0, 3], [0, 4]], [[0, 1], [1, 0], [2, 0], [3, 0], [4, 0]]]; - -pub static SIZE_GROUP: [u8; 22] = [0, 0, 0, 1, 1, 1, 2, 2, 2, 3, 3, 3, 3, 3, 3, 3, 0, 0, 1, 1, 2, 2]; - -pub static SM_WEIGHTS_TX_16X16: [u8; 16] = [255, 225, 196, 170, 145, 123, 102, 84, 68, 54, 43, 33, 26, 20, 17, 16]; - -pub static SM_WEIGHTS_TX_32X32: [u8; 32] = - [255, 240, 225, 210, 196, 182, 169, 157, 145, 133, 122, 111, 101, 92, 83, 74, 66, 59, 52, 45, 39, 34, 29, 25, 21, 17, 14, 12, 10, 9, 8, 8]; - -pub static SM_WEIGHTS_TX_4X4: [u8; 4] = [255, 149, 85, 64]; - -pub static SM_WEIGHTS_TX_8X8: [u8; 8] = [255, 197, 146, 105, 73, 50, 37, 32]; - -pub static SUBPEL_FILTERS: [[[i16; 8]; 16]; 6] = [ - [ - [0, 0, 0, 128, 0, 0, 0, 0], - [0, 2, -6, 126, 8, -2, 0, 0], - [0, 2, -10, 122, 18, -4, 0, 0], - [0, 2, -12, 116, 28, -8, 2, 0], - [0, 2, -14, 110, 38, -10, 2, 0], - [0, 2, -14, 102, 48, -12, 2, 0], - [0, 2, -16, 94, 58, -12, 2, 0], - [0, 2, -14, 84, 66, -12, 2, 0], - [0, 2, -14, 76, 76, -14, 2, 0], - [0, 2, -12, 66, 84, -14, 2, 0], - [0, 2, -12, 58, 94, -16, 2, 0], - [0, 2, -12, 48, 102, -14, 2, 0], - [0, 2, -10, 38, 110, -14, 2, 0], - [0, 2, -8, 28, 116, -12, 2, 0], - [0, 0, -4, 18, 122, -10, 2, 0], - [0, 0, -2, 8, 126, -6, 2, 0], - ], - [ - [0, 0, 0, 128, 0, 0, 0, 0], - [0, 2, 28, 62, 34, 2, 0, 0], - [0, 0, 26, 62, 36, 4, 0, 0], - [0, 0, 22, 62, 40, 4, 0, 0], - [0, 0, 20, 60, 42, 6, 0, 0], - [0, 0, 18, 58, 44, 8, 0, 0], - [0, 0, 16, 56, 46, 10, 0, 0], - [0, -2, 16, 54, 48, 12, 0, 0], - [0, -2, 14, 52, 52, 14, -2, 0], - [0, 0, 12, 48, 54, 16, -2, 0], - [0, 0, 10, 46, 56, 16, 0, 0], - [0, 0, 8, 44, 58, 18, 0, 0], - [0, 0, 6, 42, 60, 20, 0, 0], - [0, 0, 4, 40, 62, 22, 0, 0], - [0, 0, 4, 36, 62, 26, 0, 0], - [0, 0, 2, 34, 62, 28, 2, 0], - ], - [ - [0, 0, 0, 128, 0, 0, 0, 0], - [-2, 2, -6, 126, 8, -2, 2, 0], - [-2, 6, -12, 124, 16, -6, 4, -2], - [-2, 8, -18, 120, 26, -10, 6, -2], - [-4, 10, -22, 116, 38, -14, 6, -2], - [-4, 10, -22, 108, 48, -18, 8, -2], - [-4, 10, -24, 100, 60, -20, 8, -2], - [-4, 10, -24, 90, 70, -22, 10, -2], - [-4, 12, -24, 80, 80, -24, 12, -4], - [-2, 10, -22, 70, 90, -24, 10, -4], - [-2, 8, -20, 60, 100, -24, 10, -4], - [-2, 8, -18, 48, 108, -22, 10, -4], - [-2, 6, -14, 38, 116, -22, 10, -4], - [-2, 6, -10, 26, 120, -18, 8, -2], - [-2, 4, -6, 16, 124, -12, 6, -2], - [0, 2, -2, 8, 126, -6, 2, -2], - ], - [ - [0, 0, 0, 128, 0, 0, 0, 0], - [0, 0, 0, 120, 8, 0, 0, 0], - [0, 0, 0, 112, 16, 0, 0, 0], - [0, 0, 0, 104, 24, 0, 0, 0], - [0, 0, 0, 96, 32, 0, 0, 0], - [0, 0, 0, 88, 40, 0, 0, 0], - [0, 0, 0, 80, 48, 0, 0, 0], - [0, 0, 0, 72, 56, 0, 0, 0], - [0, 0, 0, 64, 64, 0, 0, 0], - [0, 0, 0, 56, 72, 0, 0, 0], - [0, 0, 0, 48, 80, 0, 0, 0], - [0, 0, 0, 40, 88, 0, 0, 0], - [0, 0, 0, 32, 96, 0, 0, 0], - [0, 0, 0, 24, 104, 0, 0, 0], - [0, 0, 0, 16, 112, 0, 0, 0], - [0, 0, 0, 8, 120, 0, 0, 0], - ], - [ - [0, 0, 0, 128, 0, 0, 0, 0], - [0, 0, -4, 126, 8, -2, 0, 0], - [0, 0, -8, 122, 18, -4, 0, 0], - [0, 0, -10, 116, 28, -6, 0, 0], - [0, 0, -12, 110, 38, -8, 0, 0], - [0, 0, -12, 102, 48, -10, 0, 0], - [0, 0, -14, 94, 58, -10, 0, 0], - [0, 0, -12, 84, 66, -10, 0, 0], - [0, 0, -12, 76, 76, -12, 0, 0], - [0, 0, -10, 66, 84, -12, 0, 0], - [0, 0, -10, 58, 94, -14, 0, 0], - [0, 0, -10, 48, 102, -12, 0, 0], - [0, 0, -8, 38, 110, -12, 0, 0], - [0, 0, -6, 28, 116, -10, 0, 0], - [0, 0, -4, 18, 122, -8, 0, 0], - [0, 0, -2, 8, 126, -4, 0, 0], - ], - [ - [0, 0, 0, 128, 0, 0, 0, 0], - [0, 0, 30, 62, 34, 2, 0, 0], - [0, 0, 26, 62, 36, 4, 0, 0], - [0, 0, 22, 62, 40, 4, 0, 0], - [0, 0, 20, 60, 42, 6, 0, 0], - [0, 0, 18, 58, 44, 8, 0, 0], - [0, 0, 16, 56, 46, 10, 0, 0], - [0, 0, 14, 54, 48, 12, 0, 0], - [0, 0, 12, 52, 52, 12, 0, 0], - [0, 0, 12, 48, 54, 14, 0, 0], - [0, 0, 10, 46, 56, 16, 0, 0], - [0, 0, 8, 44, 58, 18, 0, 0], - [0, 0, 6, 42, 60, 20, 0, 0], - [0, 0, 4, 40, 62, 22, 0, 0], - [0, 0, 4, 36, 62, 26, 0, 0], - [0, 0, 2, 34, 62, 30, 0, 0], - ], -]; - -pub static SUBSAMPLED_SIZE: [[[u8; 2]; 2]; 22] = [ - [[0, 0], [0, 0]], - [[1, 0], [22, 0]], - [[2, 22], [0, 0]], - [[3, 2], [1, 0]], - [[4, 3], [22, 1]], - [[5, 22], [3, 2]], - [[6, 5], [4, 3]], - [[7, 6], [22, 4]], - [[8, 22], [6, 5]], - [[9, 8], [7, 6]], - [[10, 9], [22, 7]], - [[11, 22], [9, 8]], - [[12, 11], [10, 9]], - [[13, 12], [22, 10]], - [[14, 22], [12, 11]], - [[15, 14], [13, 12]], - [[16, 1], [22, 1]], - [[17, 22], [2, 2]], - [[18, 4], [22, 16]], - [[19, 22], [5, 17]], - [[20, 7], [22, 18]], - [[21, 22], [8, 19]], -]; - -pub static TRANSFORM_ROW_SHIFT: [u8; 19] = [0, 1, 2, 2, 2, 0, 0, 1, 1, 1, 1, 1, 1, 1, 1, 2, 2, 2, 2]; - -pub static TX_HEIGHT: [u8; 19] = [4, 8, 16, 32, 64, 8, 4, 16, 8, 32, 16, 64, 32, 16, 4, 32, 8, 64, 16]; - -pub static TX_HEIGHT_LOG2: [u8; 19] = [2, 3, 4, 5, 6, 3, 2, 4, 3, 5, 4, 6, 5, 4, 2, 5, 3, 6, 4]; - -pub static TX_SIZE_SQR: [u8; 19] = [0, 1, 2, 3, 4, 0, 0, 1, 1, 2, 2, 3, 3, 0, 0, 1, 1, 2, 2]; - -pub static TX_SIZE_SQR_UP: [u8; 19] = [0, 1, 2, 3, 4, 1, 1, 2, 2, 3, 3, 4, 4, 2, 2, 3, 3, 4, 4]; - -pub static TX_TYPE_IN_SET_INTER: [[u8; 16]; 4] = [ - [1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0], - [1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1], - [1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 0, 0, 0, 0], - [1, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0], -]; - -pub static TX_TYPE_IN_SET_INTRA: [[u8; 16]; 3] = - [[1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0], [1, 1, 1, 1, 0, 0, 0, 0, 0, 1, 1, 1, 0, 0, 0, 0], [1, 1, 1, 1, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0]]; - -pub static TX_TYPE_INTER_INV_SET3: [u8; 2] = [9, 0]; - -pub static TX_TYPE_INTRA_INV_SET2: [u8; 5] = [9, 0, 3, 1, 2]; - -pub static TX_WIDTH: [u8; 19] = [4, 8, 16, 32, 64, 4, 8, 8, 16, 16, 32, 32, 64, 4, 16, 8, 32, 16, 64]; - -pub static TX_WIDTH_LOG2: [u8; 19] = [2, 3, 4, 5, 6, 2, 3, 3, 4, 4, 5, 5, 6, 2, 4, 3, 5, 4, 6]; diff --git a/crates/av1enc/src/transform.rs b/crates/av1enc/src/transform.rs deleted file mode 100644 index 9d81ead0b..000000000 --- a/crates/av1enc/src/transform.rs +++ /dev/null @@ -1,575 +0,0 @@ -//! Transforms. The inverse transforms are the normative ones of spec 7.13 (mirrored from -//! filmcraft-av1, so that the encoder reconstructs exactly like a decoder); the forward -//! transforms are floating-point matrices scaled to match them (not normative). - -use std::sync::OnceLock; - -use crate::tables::{ADST_ADST, ADST_DCT, COS128_LOOKUP, DCT_ADST, DCT_DCT, TRANSFORM_ROW_SHIFT, TX_WIDTH_LOG2}; - -#[inline(always)] -fn round2_64(x: i64, n: u32) -> i32 { - if n == 0 { x as i32 } else { ((x + (1i64 << (n - 1))) >> n) as i32 } -} - -#[inline(always)] -pub(crate) fn round2(x: i32, n: u32) -> i32 { - if n == 0 { x } else { (x + (1 << (n - 1))) >> n } -} - -#[inline(always)] -fn cos128(angle: i32) -> i64 { - let a = (angle & 255) as usize; - (match a { - 0..=64 => COS128_LOOKUP[a] as i32, - 65..=128 => -(COS128_LOOKUP[128 - a] as i32), - 129..=192 => -(COS128_LOOKUP[a - 128] as i32), - _ => COS128_LOOKUP[256 - a] as i32, - }) as i64 -} - -#[inline(always)] -fn sin128(angle: i32) -> i64 { - cos128(angle - 64) -} - -#[inline(always)] -fn brev(num_bits: u32, x: usize) -> usize { - let mut t = 0; - for i in 0..num_bits { - let bit = (x >> i) & 1; - t += bit << (num_bits - 1 - i); - } - t -} - -#[inline(always)] -fn bf(t: &mut [i32], a: usize, b: usize, angle: i32, flip: bool) { - let x = t[a] as i64 * cos128(angle) - t[b] as i64 * sin128(angle); - let y = t[a] as i64 * sin128(angle) + t[b] as i64 * cos128(angle); - t[a] = round2_64(x, 12); - t[b] = round2_64(y, 12); - if flip { - t.swap(a, b); - } -} - -#[inline(always)] -fn hd(t: &mut [i32], a: usize, b: usize, flip: bool, r: u32) { - let (a, b) = if flip { (b, a) } else { (a, b) }; - let x = t[a]; - let y = t[b]; - let lo = -(1i32 << (r - 1)); - let hi = (1i32 << (r - 1)) - 1; - t[a] = (x + y).clamp(lo, hi); - t[b] = (x - y).clamp(lo, hi); -} - -/// Inverse DCT of length 2^N, N = 2..=5 (7.13.2.3), in place. `N` is a const generic so that the -/// bit-reversal permutations and loop bounds fold at compile time. -#[inline(always)] -fn inverse_dct_n(t: &mut [i32], r: u32) { - let n = N; - let len = 1usize << n; - let mut copy = [0i32; 32]; - copy[..len].copy_from_slice(&t[..len]); - for i in 0..len { - t[i] = copy[brev(n, i)]; - } - inverse_dct_initial_stages(t, n, r); - inverse_dct_final_stages(t, n, r); -} - -/// Rotate the high-frequency branches before the four-point core. -#[inline(always)] -fn inverse_dct_initial_stages(t: &mut [i32], n: u32, r: u32) { - if n >= 5 { - for i in 0..8 { - bf(t, 16 + i, 31 - i, 6 + ((brev(3, 7 - i) as i32) << 3), false); - } - } - if n >= 4 { - for i in 0..4 { - bf(t, 8 + i, 15 - i, 12 + ((brev(2, 3 - i) as i32) << 4), false); - } - } - if n >= 5 { - for i in 0..8 { - hd(t, 16 + 2 * i, 17 + 2 * i, i & 1 == 1, r); - } - } - if n >= 3 { - for i in 0..2 { - bf(t, 4 + i, 7 - i, 56 - 32 * i as i32, false); - } - } - if n >= 4 { - for i in 0..4 { - hd(t, 8 + 2 * i, 9 + 2 * i, i & 1 == 1, r); - } - } - if n >= 5 { - for i in 0..2 { - for j in 0..2 { - bf(t, 30 - 4 * i - j, 17 + 4 * i + j, 24 + ((j as i32) << 6) + (((1 - i) as i32) << 5), true); - } - } - } -} - -/// Evaluate the four-point core and merge progressively larger branches. -#[inline(always)] -fn inverse_dct_final_stages(t: &mut [i32], n: u32, r: u32) { - for i in 0..2 { - bf(t, 2 * i, 2 * i + 1, 32 + 16 * i as i32, i == 0); - } - if n >= 3 { - for i in 0..2 { - hd(t, 4 + 2 * i, 5 + 2 * i, i == 1, r); - } - } - if n >= 4 { - for i in 0..2 { - bf(t, 14 - i, 9 + i, 48 + 64 * i as i32, true); - } - } - if n >= 5 { - for i in 0..4 { - for j in 0..2 { - hd(t, 16 + 4 * i + j, 19 + 4 * i - j, i & 1 == 1, r); - } - } - } - for i in 0..2 { - hd(t, i, 3 - i, false, r); - } - if n >= 3 { - bf(t, 6, 5, 32, true); - } - if n >= 4 { - for i in 0..2 { - for j in 0..2 { - hd(t, 8 + 4 * i + j, 11 + 4 * i - j, i == 1, r); - } - } - } - if n >= 5 { - for i in 0..4 { - bf(t, 29 - i, 18 + i, 48 + (i as i32 >> 1) * 64, true); - } - } - if n >= 3 { - for i in 0..4 { - hd(t, i, 7 - i, false, r); - } - } - if n >= 4 { - for i in 0..2 { - bf(t, 13 - i, 10 + i, 32, true); - } - } - if n >= 5 { - for i in 0..2 { - for j in 0..4 { - hd(t, 16 + i * 8 + j, 23 + i * 8 - j, i == 1, r); - } - } - } - if n >= 4 { - for i in 0..8 { - hd(t, i, 15 - i, false, r); - } - } - if n >= 5 { - for i in 0..4 { - bf(t, 27 - i, 20 + i, 32, true); - } - } - if n >= 5 { - for i in 0..16 { - hd(t, i, 31 - i, false, r); - } - } -} - -const SINPI_1_9: i64 = 1321; -const SINPI_2_9: i64 = 2482; -const SINPI_3_9: i64 = 3344; -const SINPI_4_9: i64 = 3803; - -#[inline(always)] -fn inverse_adst4(t: &mut [i32]) { - let (t0, t1, t2, t3) = (t[0] as i64, t[1] as i64, t[2] as i64, t[3] as i64); - let mut s = [SINPI_1_9 * t0, SINPI_2_9 * t0, SINPI_3_9 * t1, SINPI_4_9 * t2, SINPI_1_9 * t2, SINPI_2_9 * t3, SINPI_4_9 * t3]; - let a7 = t0 - t2; - let b7 = a7 + t3; - s[0] += s[3]; - s[1] -= s[4]; - s[3] = s[2]; - s[2] = SINPI_3_9 * b7; - s[0] += s[5]; - s[1] -= s[6]; - let x = [s[0] + s[3], s[1] + s[3], s[2], s[0] + s[1] - s[3]]; - for i in 0..4 { - t[i] = round2_64(x[i], 12); - } -} - -#[inline(always)] -fn adst_in_permute(t: &mut [i32]) { - let n = N; - let n0 = 1usize << n; - let mut copy = [0i32; 16]; - copy[..n0].copy_from_slice(&t[..n0]); - for i in 0..n0 { - let idx = if i & 1 == 1 { i - 1 } else { n0 - i - 1 }; - t[i] = copy[idx]; - } -} - -#[inline(always)] -fn adst_out_permute(t: &mut [i32]) { - let n = N; - let n0 = 1usize << n; - let mut copy = [0i32; 16]; - copy[..n0].copy_from_slice(&t[..n0]); - for i in 0..n0 { - let a = (i >> 3) & 1; - let b = ((i >> 2) & 1) ^ ((i >> 3) & 1); - let c = ((i >> 1) & 1) ^ ((i >> 2) & 1); - let d = (i & 1) ^ ((i >> 1) & 1); - let idx = ((d << 3) | (c << 2) | (b << 1) | a) >> (4 - n); - t[i] = if i & 1 == 1 { -copy[idx] } else { copy[idx] }; - } -} - -#[inline(always)] -fn inverse_adst8(t: &mut [i32], r: u32) { - adst_in_permute::<3>(t); - for i in 0..4 { - bf(t, 2 * i, 2 * i + 1, 60 - 16 * i as i32, true); - } - for i in 0..4 { - hd(t, i, 4 + i, false, r); - } - for i in 0..2 { - bf(t, 4 + 3 * i, 5 + i, 48 - 32 * i as i32, true); - } - for i in 0..2 { - for j in 0..2 { - hd(t, 4 * j + i, 2 + 4 * j + i, false, r); - } - } - for i in 0..2 { - bf(t, 2 + 4 * i, 3 + 4 * i, 32, true); - } - adst_out_permute::<3>(t); -} - -#[inline(always)] -fn inverse_adst16(t: &mut [i32], r: u32) { - adst_in_permute::<4>(t); - for i in 0..8 { - bf(t, 2 * i, 2 * i + 1, 62 - 8 * i as i32, true); - } - for i in 0..8 { - hd(t, i, 8 + i, false, r); - } - for i in 0..2 { - bf(t, 8 + 2 * i, 9 + 2 * i, 56 - 32 * i as i32, true); - bf(t, 13 + 2 * i, 12 + 2 * i, 8 + 32 * i as i32, true); - } - for i in 0..4 { - for j in 0..2 { - hd(t, 8 * j + i, 4 + 8 * j + i, false, r); - } - } - for i in 0..2 { - for j in 0..2 { - bf(t, 4 + 8 * j + 3 * i, 5 + 8 * j + i, 48 - 32 * i as i32, true); - } - } - for i in 0..2 { - for j in 0..4 { - hd(t, 4 * j + i, 2 + 4 * j + i, false, r); - } - } - for i in 0..4 { - bf(t, 2 + 4 * i, 3 + 4 * i, 32, true); - } - adst_out_permute::<4>(t); -} - -/// 1-D transform kind. -#[derive(Clone, Copy, PartialEq, Eq, Debug)] -pub(crate) enum Kind { - Dct, - Adst, -} - -/// (vertical / column kind, horizontal / row kind) of a transform type. -pub(crate) fn kinds(tx_type: usize) -> (Kind, Kind) { - match tx_type { - DCT_DCT => (Kind::Dct, Kind::Dct), - ADST_DCT => (Kind::Adst, Kind::Dct), - DCT_ADST => (Kind::Dct, Kind::Adst), - ADST_ADST => (Kind::Adst, Kind::Adst), - // The encoder only chooses the four types above. - _ => { - debug_assert!(false, "unsupported transform type {tx_type}"); - (Kind::Dct, Kind::Dct) - } - } -} - -#[inline(always)] -fn inverse_dct(t: &mut [i32], n: u32, r: u32) { - match n { - 2 => inverse_dct_n::<2>(t, r), - 3 => inverse_dct_n::<3>(t, r), - 4 => inverse_dct_n::<4>(t, r), - _ => inverse_dct_n::<5>(t, r), - } -} - -#[inline(always)] -fn inverse_1d(t: &mut [i32], kind: Kind, n: u32, r: u32) { - match kind { - Kind::Dct => inverse_dct(t, n, r), - Kind::Adst => match n { - 2 => inverse_adst4(t), - 3 => inverse_adst8(t, r), - _ => inverse_adst16(t, r), - }, - } -} - -/// 2-D inverse transform (7.13.3) of a square `n x n` block (n = 4..=32) of dequantised -/// coefficients (row stride `n`) into `residual` (row stride `n`). -pub(crate) fn inverse_2d(dequant: &[i32], residual: &mut [i32], tx_sz: usize, tx_type: usize, bit_depth: u32) { - match TX_WIDTH_LOG2[tx_sz] { - 2 => inverse_2d_n::<2>(dequant, residual, tx_sz, tx_type, bit_depth), - 3 => inverse_2d_n::<3>(dequant, residual, tx_sz, tx_type, bit_depth), - 4 => inverse_2d_n::<4>(dequant, residual, tx_sz, tx_type, bit_depth), - _ => inverse_2d_n::<5>(dequant, residual, tx_sz, tx_type, bit_depth), - } -} - -/// `inverse_2d` for a block of width `1 << LOG2`. -fn inverse_2d_n(dequant: &[i32], residual: &mut [i32], tx_sz: usize, tx_type: usize, bit_depth: u32) { - let log2 = LOG2; - let n = 1usize << log2; - let row_shift = TRANSFORM_ROW_SHIFT[tx_sz] as u32; - let row_clamp = bit_depth + 8; - let col_clamp = (bit_depth + 6).max(16); - let (ck, rk) = kinds(tx_type); - let lo = -(1i32 << (col_clamp - 1)); - let hi = (1i32 << (col_clamp - 1)) - 1; - let mut t = [0i32; 32]; - for i in 0..n { - let row = &dequant[i * n..i * n + n]; - if row.iter().all(|&v| v == 0) { - residual[i * n..i * n + n].iter_mut().for_each(|v| *v = 0); - continue; - } - t[..n].copy_from_slice(row); - inverse_1d(&mut t, rk, log2, row_clamp); - for j in 0..n { - residual[i * n + j] = round2(t[j], row_shift).clamp(lo, hi); - } - } - for j in 0..n { - for i in 0..n { - t[i] = residual[i * n + j]; - } - inverse_1d(&mut t, ck, log2, col_clamp); - for i in 0..n { - residual[i * n + j] = round2(t[i], 4); - } - } -} - -/// Orthonormal forward basis matrices `[k][i]` (row k = basis function k), sizes 4..=32. -struct Bases { - dct: [Vec; 4], - adst: [Vec; 3], - /// The same matrices transposed (`[i][k]`). - dct_t: [Vec; 4], - adst_t: [Vec; 3], -} - -fn transpose(m: &[f32]) -> Vec { - let n = (m.len() as f64).sqrt() as usize; - let mut t = vec![0f32; n * n]; - for k in 0..n { - for i in 0..n { - t[i * n + k] = m[k * n + i]; - } - } - t -} - -fn bases() -> &'static Bases { - static B: OnceLock = OnceLock::new(); - B.get_or_init(|| { - let pi = std::f64::consts::PI; - let dct = std::array::from_fn(|l| { - let n = 4usize << l; - let mut m = vec![0f32; n * n]; - for k in 0..n { - let c = if k == 0 { std::f64::consts::FRAC_1_SQRT_2 } else { 1.0 }; - for i in 0..n { - m[k * n + i] = (c * (2.0 / n as f64).sqrt() * (pi * (2 * i + 1) as f64 * k as f64 / (2 * n) as f64).cos()) as f32; - } - } - m - }); - let adst = std::array::from_fn(|l| { - let n = 4usize << l; - let mut m = vec![0f32; n * n]; - for k in 0..n { - for i in 0..n { - m[k * n + i] = if n == 4 { - // the AV1 4-point ADST (sin(pi (i + 1)(2k + 1) / 9) basis) - (2.0 / 3.0 * (pi * ((i + 1) * (2 * k + 1)) as f64 / 9.0).sin()) as f32 - } else { - ((2.0 / n as f64).sqrt() * (pi * ((2 * i + 1) * (2 * k + 1)) as f64 / (4 * n) as f64).sin()) as f32 - }; - } - } - m - }); - let dct_t = std::array::from_fn(|l| transpose(&dct[l])); - let adst_t = std::array::from_fn(|l| transpose(&adst[l])); - Bases { dct, adst, dct_t, adst_t } - }) -} - -fn basis_t(kind: Kind, n: usize) -> &'static [f32] { - let l = n.trailing_zeros() as usize - 2; - match kind { - Kind::Dct => &bases().dct_t[l], - Kind::Adst => &bases().adst_t[l], - } -} - -fn basis(kind: Kind, n: usize) -> &'static [f32] { - let l = n.trailing_zeros() as usize - 2; - match kind { - Kind::Dct => &bases().dct[l], - Kind::Adst => &bases().adst[l], - } -} - -/// Forward 2-D transform of the `n x n` residual (row stride `n`) into orthonormal-domain -/// coefficients scaled by 8 (so that `level = out / q` with the spec's dequantiser). -pub(crate) fn forward_2d(residual: &[i32], out: &mut [f32], n: usize, tx_type: usize) { - match n { - 4 => forward_2d_n::<4, 16>(residual, out, tx_type), - 8 => forward_2d_n::<8, 64>(residual, out, tx_type), - 16 => forward_2d_n::<16, 256>(residual, out, tx_type), - _ => forward_2d_dyn(residual, out, n, tx_type), - } -} - -/// `forward_2d` for a fixed size, so `tmp` is exactly `NN = N * N` floats (no 4 KiB zeroing). -#[inline(always)] -fn forward_2d_n(residual: &[i32], out: &mut [f32], tx_type: usize) { - let n = N; - let (ck, rk) = kinds(tx_type); - let bc = &basis(ck, n)[..NN]; - let brt = &basis_t(rk, n)[..NN]; - let residual = &residual[..NN]; - let out = &mut out[..NN]; - let mut tmp = [0f32; NN]; - for i in 0..n { - let t = &mut tmp[i * n..i * n + n]; - for j in 0..n { - let r = residual[i * n + j]; - if r == 0 { - continue; - } - let r = r as f32; - for (o, &b) in t.iter_mut().zip(&brt[j * n..j * n + n]) { - *o += r * b; - } - } - } - out.iter_mut().for_each(|v| *v = 0.0); - for k in 0..n { - let o = &mut out[k * n..k * n + n]; - for i in 0..n { - let b = 8.0 * bc[k * n + i]; - for (v, &t) in o.iter_mut().zip(&tmp[i * n..i * n + n]) { - *v += b * t; - } - } - } -} - -/// 32x32 (and any other size): a const-generic 32 version measured slower. -fn forward_2d_dyn(residual: &[i32], out: &mut [f32], n: usize, tx_type: usize) { - let (ck, rk) = kinds(tx_type); - let bc = basis(ck, n); - let brt = basis_t(rk, n); - // tmp[i][k] = sum_j residual[i][j] * br[k][j] (horizontal; written as row updates so that - // the inner loops vectorise) - let mut tmp = [0f32; 32 * 32]; - for i in 0..n { - let t = &mut tmp[i * n..i * n + n]; - for j in 0..n { - let r = residual[i * n + j]; - if r == 0 { - continue; - } - let r = r as f32; - for (o, &b) in t.iter_mut().zip(&brt[j * n..j * n + n]) { - *o += r * b; - } - } - } - // out[k][j] = 8 * sum_i bc[k][i] * tmp[i][j] (vertical) - out[..n * n].iter_mut().for_each(|v| *v = 0.0); - for k in 0..n { - let o = &mut out[k * n..k * n + n]; - for i in 0..n { - let b = 8.0 * bc[k * n + i]; - for (v, &t) in o.iter_mut().zip(&tmp[i * n..i * n + n]) { - *v += b * t; - } - } - } -} - -#[cfg(test)] -mod tests { - use super::*; - use crate::tables::{TX_4X4, TX_8X8, TX_16X16, TX_32X32}; - - /// forward (float) then the normative inverse reproduces the residual: the forward basis - /// and scale match the decoder's inverse transforms for every supported size and type. - #[test] - fn forward_matches_inverse() { - let mut seed = 99u32; - let mut rnd = || { - seed = seed.wrapping_mul(1_103_515_245).wrapping_add(12345); - ((seed >> 16) % 201) as i32 - 100 - }; - for (tx, n) in [(TX_4X4, 4), (TX_8X8, 8), (TX_16X16, 16), (TX_32X32, 32)] { - let denom = if tx == TX_32X32 { 2.0 } else { 1.0 }; - for tx_type in [DCT_DCT, ADST_DCT, DCT_ADST, ADST_ADST] { - if n == 32 && tx_type != DCT_DCT { - continue; - } - let res: Vec = (0..n * n).map(|_| rnd()).collect(); - let mut coef = vec![0f32; n * n]; - forward_2d(&res, &mut coef, n, tx_type); - // q = 8 / denom keeps full precision: dequant = level * q / denom = coef - let deq: Vec = coef.iter().map(|&c| (c / denom).round() as i32).collect(); - let mut out = vec![0i32; n * n]; - inverse_2d(&deq, &mut out, tx, tx_type, 8); - let err: i32 = res.iter().zip(&out).map(|(a, b)| (a - b).abs()).max().unwrap(); - assert!(err <= 1, "tx {tx} type {tx_type}: max error {err}"); - } - } - } -} diff --git a/crates/av1enc/src/writer.rs b/crates/av1enc/src/writer.rs deleted file mode 100644 index a10bd2e9a..000000000 --- a/crates/av1enc/src/writer.rs +++ /dev/null @@ -1,1040 +0,0 @@ -//! Tile syntax writer (spec 5.11 with the 8.3 CDF selection). It keeps the same per-4x4 mode -//! info, coefficient contexts and adaptive CDFs as a decoder and writes the decisions taken by -//! `decide.rs` as symbols. The context derivations and the motion vector prediction process -//! (7.10.2) mirror the decoder exactly, since symbols and coded motion vector differences -//! depend on them. - -use crate::cdf::Cdfs; -use crate::decide::{FrameGeom, Leaf, Node, intra_uv_tx_type, scan_for, tx_in_set, tx_set, tx_size_for}; -use crate::loopfilter::LfInfo; -use crate::symbol::SymbolWriter; -use crate::tables::{ - ADST_ADST, ADST_DCT, ALTREF_FRAME, ALTREF2_FRAME, BLOCK_8X8, BLOCK_64X64, BR_CDF_SIZE, BWDREF_FRAME, CLASS0_SIZE, COEFF_BASE_CTX_OFFSET, COEFF_BASE_RANGE, - D67_PRED, DC_PRED, DCT_ADST, DCT_DCT, DEFAULT_INTRA_FRAME_Y_MODE_CDF, GLOBALMV, GOLDEN_FRAME, INTRA_FRAME, INTRA_MODE_CONTEXT, LAST_FRAME, LAST2_FRAME, - LAST3_FRAME, MAG_REF_OFFSET_WITH_TX_CLASS, MAX_ANGLE_DELTA, MAX_REF_MV_STACK_SIZE, MI_HEIGHT_LOG2, MI_WIDTH_LOG2, MV_BORDER, MV_CLASS_0, NEARESTMV, NEARMV, - NEWMV, NUM_4X4_BLOCKS_HIGH, NUM_4X4_BLOCKS_WIDE, NUM_BASE_LEVELS, PARTITION_HORZ, PARTITION_HORZ_4, PARTITION_HORZ_A, PARTITION_HORZ_B, PARTITION_NONE, - PARTITION_SPLIT, PARTITION_SUBSIZE, PARTITION_VERT, PARTITION_VERT_4, PARTITION_VERT_A, PARTITION_VERT_B, REF_CAT_LEVEL, SIG_REF_DIFF_OFFSET, SIZE_GROUP, - SUBSAMPLED_SIZE, TX_32X32, TX_CLASS_2D, TX_HEIGHT, TX_HEIGHT_LOG2, TX_SIZE_SQR, TX_SIZE_SQR_UP, TX_TYPE_INTER_INV_SET3, TX_TYPE_INTRA_INV_SET2, TX_WIDTH, - TX_WIDTH_LOG2, V_PRED, -}; - -type MvP = [i32; 2]; - -/// Mode info per 4x4 (MiSizes, YModes, RefFrames, Mvs, ...). -struct Mi { - cols: usize, - mi_size: Vec, - y_mode: Vec, - ref_frame: Vec<[i8; 2]>, - mv: Vec, - skip: Vec, - is_inter: Vec, - tx_type: Vec, - written: Vec, -} - -impl Mi { - #[inline(always)] - fn idx(&self, r: usize, c: usize) -> usize { - r * self.cols + c - } -} - -/// Output of find_mv_stack. -#[derive(Default)] -struct MvStack { - ref_stack_mv: [MvP; MAX_REF_MV_STACK_SIZE + 1], - weight_stack: [u32; MAX_REF_MV_STACK_SIZE + 1], - num_mv_found: usize, - new_mv_count: usize, - found_match: bool, - close_matches: usize, - total_matches: usize, - new_mv_context: usize, - ref_mv_context: usize, - zero_mv_context: usize, - drl_ctx_stack: [usize; MAX_REF_MV_STACK_SIZE + 1], -} - -pub(crate) struct TileWriter<'a> { - g: &'a FrameGeom, - intra_only: bool, - qidx: u8, - sw: SymbolWriter, - cdf: Box, - kf_y_mode: [[[u16; 14]; 5]; 5], - mi: Mi, - above_level: [Vec; 3], - above_dc: [Vec; 3], - left_level: [Vec; 3], - left_dc: [Vec; 3], - /// LoopfilterTxSizes per plane (row stride mi_cols + 32). - lf_tx: [Vec; 3], - /// Scratch for the levels coded so far in `coeffs`. - quant: Vec, - // current block - r: usize, - c: usize, - bsize: usize, - avail_u: bool, - avail_l: bool, - mvs: MvStack, -} - -fn block_size_px(bs: usize) -> usize { - 4 * NUM_4X4_BLOCKS_WIDE[bs] as usize -} - -fn is_directional(mode: usize) -> bool { - (V_PRED..=D67_PRED).contains(&mode) -} - -impl<'a> TileWriter<'a> { - /// `init`: the CDFs saved by the primary reference frame (load_cdfs), or None for the - /// defaults (init_non_coeff_cdfs / init_coeff_cdfs). - pub fn new(g: &'a FrameGeom, intra_only: bool, qidx: u8, init: Option<&Cdfs>) -> Self { - let n = g.mi_cols * g.mi_rows; - TileWriter { - g, - intra_only, - qidx, - sw: SymbolWriter::new(false), - cdf: match init { - Some(c) => { - let mut c = Box::new(c.clone()); - c.clear_counts(); - c - } - None => Cdfs::new(qidx as u32), - }, - kf_y_mode: DEFAULT_INTRA_FRAME_Y_MODE_CDF, - mi: Mi { - cols: g.mi_cols, - mi_size: vec![0; n], - y_mode: vec![0; n], - ref_frame: vec![[0, -1]; n], - mv: vec![[0, 0]; n], - skip: vec![false; n], - is_inter: vec![false; n], - tx_type: vec![0; n], - written: vec![false; n], - }, - above_level: std::array::from_fn(|_| vec![0; g.mi_cols + 64]), - above_dc: std::array::from_fn(|_| vec![0; g.mi_cols + 64]), - left_level: std::array::from_fn(|_| vec![0; g.mi_rows + 64]), - left_dc: std::array::from_fn(|_| vec![0; g.mi_rows + 64]), - lf_tx: std::array::from_fn(|_| vec![0; (g.mi_cols + 32) * (g.mi_rows + 32)]), - quant: Vec::new(), - r: 0, - c: 0, - bsize: 0, - avail_u: false, - avail_l: false, - mvs: MvStack::default(), - } - } - - /// The tile data and the final CDFs (saved for the frame-end CDF update). - pub fn finish(self) -> (Vec, Box) { - (self.sw.finish(), self.cdf) - } - - /// The mode info the loop filter needs. - pub fn lf_info(&self) -> LfInfo<'_> { - LfInfo { - mi_cols: self.g.mi_cols, - mi_rows: self.g.mi_rows, - width: self.g.width, - height: self.g.height, - mi_size: &self.mi.mi_size, - skip: &self.mi.skip, - is_inter: &self.mi.is_inter, - tx: [&self.lf_tx[0], &self.lf_tx[1], &self.lf_tx[2]], - } - } - - /// Start of a superblock row (clear_left_context). - pub fn start_sb_row(&mut self) { - for p in 0..3 { - self.left_level[p].iter_mut().for_each(|v| *v = 0); - self.left_dc[p].iter_mut().for_each(|v| *v = 0); - } - } - - pub fn superblock(&mut self, r: usize, c: usize, node: &Node) { - self.partition(r, c, BLOCK_64X64, node); - } - - fn partition(&mut self, r: usize, c: usize, bsize: usize, node: &Node) { - let (mi_rows, mi_cols) = (self.g.mi_rows, self.g.mi_cols); - if r >= mi_rows || c >= mi_cols { - return; - } - let avail_u = r > 0; - let avail_l = c > 0; - let n4 = NUM_4X4_BLOCKS_WIDE[bsize] as usize; - let half = n4 >> 1; - let has_rows = r + half < mi_rows; - let has_cols = c + half < mi_cols; - let split = matches!(node, Node::Split(_)); - let bsl = MI_WIDTH_LOG2[bsize] as usize; - let above = avail_u && (MI_WIDTH_LOG2[self.mi.mi_size[self.mi.idx(r - 1, c)] as usize] as usize) < bsl; - let left = avail_l && (MI_HEIGHT_LOG2[self.mi.mi_size[self.mi.idx(r, c - 1)] as usize] as usize) < bsl; - let ctx = left as usize * 2 + above as usize; - let partition = if split { PARTITION_SPLIT } else { PARTITION_NONE }; - if has_rows && has_cols { - let cdf: &mut [u16] = match bsl { - 1 => &mut self.cdf.partition_w8[ctx], - 2 => &mut self.cdf.partition_w16[ctx], - 3 => &mut self.cdf.partition_w32[ctx], - _ => &mut self.cdf.partition_w64[ctx], - }; - self.sw.symbol(partition, cdf); - } else if has_cols || has_rows { - assert!(split, "blocks crossing the frame edge are split"); - let pcdf: &[u16] = match bsl { - 2 => &self.cdf.partition_w16[ctx], - 3 => &self.cdf.partition_w32[ctx], - _ => &self.cdf.partition_w64[ctx], - }; - let pr = |k: usize| pcdf[k] as i32 - if k == 0 { 0 } else { pcdf[k - 1] as i32 }; - let psum = if has_cols { - // split_or_horz - pr(PARTITION_VERT) + pr(PARTITION_SPLIT) + pr(PARTITION_HORZ_A) + pr(PARTITION_VERT_A) + pr(PARTITION_VERT_B) + pr(PARTITION_VERT_4) - } else { - // split_or_vert - pr(PARTITION_HORZ) + pr(PARTITION_SPLIT) + pr(PARTITION_HORZ_A) + pr(PARTITION_HORZ_B) + pr(PARTITION_VERT_A) + pr(PARTITION_HORZ_4) - }; - let cdf = [((1i32 << 15) - psum) as u16, 1 << 15, 0]; - self.sw.symbol_fixed(1, &cdf); - } else { - assert!(split); - } - match node { - Node::Split(ch) => { - let sub = PARTITION_SUBSIZE[PARTITION_SPLIT][bsize] as usize; - self.partition(r, c, sub, &ch[0]); - self.partition(r, c + half, sub, &ch[1]); - self.partition(r + half, c, sub, &ch[2]); - self.partition(r + half, c + half, sub, &ch[3]); - } - Node::Leaf(leaf) => { - debug_assert_eq!(leaf.bsize, bsize); - self.block(r, c, leaf); - } - // Blocks inside the frame are always decided; nothing is coded outside it. - Node::Out => debug_assert!(false, "undecided block inside the frame"), - } - } - - fn block(&mut self, r: usize, c: usize, b: &Leaf) { - self.r = r; - self.c = c; - self.bsize = b.bsize; - self.avail_u = r > 0; - self.avail_l = c > 0; - let bsize = b.bsize; - let n4 = NUM_4X4_BLOCKS_WIDE[bsize] as usize; - // skip - let mut ctx = 0; - if self.avail_u { - ctx += self.mi.skip[self.mi.idx(r - 1, c)] as usize; - } - if self.avail_l { - ctx += self.mi.skip[self.mi.idx(r, c - 1)] as usize; - } - let mut y_mode = b.y_mode; - if self.intra_only { - self.sw.symbol(b.skip as usize, &mut self.cdf.skip[ctx]); - let above = if self.avail_u { self.mi.y_mode[self.mi.idx(r - 1, c)] as usize } else { DC_PRED }; - let left = if self.avail_l { self.mi.y_mode[self.mi.idx(r, c - 1)] as usize } else { DC_PRED }; - let actx = INTRA_MODE_CONTEXT[above] as usize; - let lctx = INTRA_MODE_CONTEXT[left] as usize; - self.sw.symbol(b.y_mode, &mut self.kf_y_mode[actx][lctx]); - self.intra_angle_and_uv(b); - } else { - self.sw.symbol(b.skip as usize, &mut self.cdf.skip[ctx]); - // is_inter - let left = if self.avail_l { self.mi.ref_frame[self.mi.idx(r, c - 1)] } else { [INTRA_FRAME as i8, -1] }; - let above = if self.avail_u { self.mi.ref_frame[self.mi.idx(r - 1, c)] } else { [INTRA_FRAME as i8, -1] }; - let left_intra = left[0] <= INTRA_FRAME as i8; - let above_intra = above[0] <= INTRA_FRAME as i8; - let ctx = if self.avail_u && self.avail_l { - if left_intra && above_intra { 3 } else { (left_intra || above_intra) as usize } - } else if self.avail_u || self.avail_l { - 2 * (if self.avail_u { above_intra } else { left_intra }) as usize - } else { - 0 - }; - self.sw.symbol(b.is_inter as usize, &mut self.cdf.is_inter[ctx]); - if b.is_inter { - y_mode = self.inter_block_mode_info(b, left, above); - } else { - let ctx = SIZE_GROUP[bsize] as usize; - self.sw.symbol(b.y_mode, &mut self.cdf.y_mode[ctx]); - self.intra_angle_and_uv(b); - } - } - // mode info stored before the residual - let (rows, cols) = (n4.min(self.g.mi_rows - r), n4.min(self.g.mi_cols - c)); - for y in 0..rows { - for x in 0..cols { - let i = self.mi.idx(r + y, c + x); - self.mi.y_mode[i] = y_mode as u8; - self.mi.ref_frame[i] = if b.is_inter { [LAST_FRAME as i8, -1] } else { [INTRA_FRAME as i8, -1] }; - self.mi.written[i] = true; - if b.is_inter { - self.mi.mv[i] = b.mv; - } - } - } - if b.skip { - // reset_block_context( ) - for plane in 0..3 { - let s = (plane > 0) as usize; - for i in (c >> s)..((c + n4) >> s) { - self.above_level[plane][i] = 0; - self.above_dc[plane][i] = 0; - } - for i in (r >> s)..((r + n4) >> s) { - self.left_level[plane][i] = 0; - self.left_dc[plane][i] = 0; - } - } - } else { - let log2 = (n4 * 4).trailing_zeros(); - assert!(log2 <= 5, "64x64 blocks are coded as skip blocks"); - for plane in 0..3 { - let s = (plane > 0) as u32; - self.coeffs(plane, (c * 4) >> s, (r * 4) >> s, tx_size_for(log2 - s), b, &b.coefs[plane]); - } - } - for y in 0..rows { - for x in 0..cols { - let i = self.mi.idx(r + y, c + x); - self.mi.is_inter[i] = b.is_inter; - self.mi.skip[i] = b.skip; - self.mi.mi_size[i] = bsize as u8; - } - } - // LoopfilterTxSizes: one transform block per plane - let log2 = (n4 * 4).trailing_zeros(); - let stride = self.g.mi_cols + 32; - for plane in 0..3 { - let s = (plane > 0) as usize; - let tx = tx_size_for(log2 - s as u32) as u8; - let (r0, c0, m) = (r >> s, c >> s, n4 >> s); - for y in r0..r0 + m { - self.lf_tx[plane][y * stride + c0..y * stride + c0 + m].iter_mut().for_each(|v| *v = tx); - } - } - } - - fn intra_angle_and_uv(&mut self, b: &Leaf) { - if self.bsize >= BLOCK_8X8 && is_directional(b.y_mode) { - self.sw.symbol(MAX_ANGLE_DELTA, &mut self.cdf.angle_delta[b.y_mode - V_PRED]); - } - let bs = block_size_px(self.bsize); - if bs <= 32 { - self.sw.symbol(b.uv_mode, &mut self.cdf.uv_mode_cfl_allowed[b.y_mode]); - } else { - self.sw.symbol(b.uv_mode, &mut self.cdf.uv_mode_cfl_not_allowed[b.y_mode]); - } - if self.bsize >= BLOCK_8X8 && is_directional(b.uv_mode) { - self.sw.symbol(MAX_ANGLE_DELTA, &mut self.cdf.angle_delta[b.uv_mode - V_PRED]); - } - } - - /// read_ref_frames (LAST_FRAME), find_mv_stack, the inter mode, DRL and motion vector. - /// Returns the inter mode (YMode). - fn inter_block_mode_info(&mut self, b: &Leaf, left: [i8; 2], above: [i8; 2]) -> usize { - let count = |ft: i8| -> usize { - let mut n = 0; - if self.avail_u { - n += (above[0] == ft) as usize + (above[1] == ft) as usize; - } - if self.avail_l { - n += (left[0] == ft) as usize + (left[1] == ft) as usize; - } - n - }; - let cmp = |a: usize, b: usize| match a.cmp(&b) { - std::cmp::Ordering::Less => 0, - std::cmp::Ordering::Equal => 1, - std::cmp::Ordering::Greater => 2, - }; - let (last, last2, last3, gold) = (count(LAST_FRAME as i8), count(LAST2_FRAME as i8), count(LAST3_FRAME as i8), count(GOLDEN_FRAME as i8)); - let (bwd, alt2, alt) = (count(BWDREF_FRAME as i8), count(ALTREF2_FRAME as i8), count(ALTREF_FRAME as i8)); - let ctx_p1 = cmp(last + last2 + last3 + gold, bwd + alt2 + alt); - let ctx_comp_ref = cmp(last + last2, last3 + gold); - let ctx_comp_ref_p1 = cmp(last, last2); - self.sw.symbol(0, &mut self.cdf.single_ref[ctx_p1][0]); - self.sw.symbol(0, &mut self.cdf.single_ref[ctx_comp_ref][2]); - self.sw.symbol(0, &mut self.cdf.single_ref[ctx_comp_ref_p1][3]); - self.find_mv_stack(); - let mv = b.mv; - let s = &self.mvs; - let mode = if mv == [0, 0] { - GLOBALMV - } else if mv == s.ref_stack_mv[0] { - NEARESTMV - } else if mv == s.ref_stack_mv[1] { - NEARMV - } else { - NEWMV - }; - let (nctx, zctx, rctx) = (s.new_mv_context, s.zero_mv_context, s.ref_mv_context); - self.sw.symbol((mode != NEWMV) as usize, &mut self.cdf.new_mv[nctx]); - if mode != NEWMV { - self.sw.symbol((mode != GLOBALMV) as usize, &mut self.cdf.zero_mv[zctx]); - if mode != GLOBALMV { - self.sw.symbol((mode == NEARMV) as usize, &mut self.cdf.ref_mv[rctx]); - } - } - // DRL: ref_mv_idx 0 for NEWMV, 1 for NEARMV - if mode == NEWMV { - if self.mvs.num_mv_found > 1 { - let ctx = self.mvs.drl_ctx_stack[0]; - self.sw.symbol(0, &mut self.cdf.drl_mode[ctx]); - } - } else if mode == NEARMV && self.mvs.num_mv_found > 2 { - let ctx = self.mvs.drl_ctx_stack[1]; - self.sw.symbol(0, &mut self.cdf.drl_mode[ctx]); - } - if mode == NEWMV { - let pred = self.mvs.ref_stack_mv[0]; - self.write_mv([mv[0] - pred[0], mv[1] - pred[1]]); - } - mode - } - - fn write_mv(&mut self, diff: MvP) { - let joint = ((diff[0] != 0) as usize) << 1 | (diff[1] != 0) as usize; - // MV_JOINT_ZERO, MV_JOINT_HNZVZ (col only), MV_JOINT_HZVNZ (row only), MV_JOINT_HNZVNZ - self.sw.symbol(joint, &mut self.cdf.mv_joint); - for (comp, &d) in diff.iter().enumerate() { - if d != 0 { - self.write_mv_component(comp, d); - } - } - } - - fn write_mv_component(&mut self, comp: usize, v: i32) { - debug_assert!(v % 2 == 0, "quarter-sample precision"); - self.sw.symbol((v < 0) as usize, &mut self.cdf.mv_sign[comp]); - let m = v.unsigned_abs() - 1; // odd: the implied hp bit is 1 - let fr = ((m >> 1) & 3) as usize; - let int = m >> 3; - if int < CLASS0_SIZE as u32 { - self.sw.symbol(MV_CLASS_0, &mut self.cdf.mv_class[comp]); - self.sw.symbol(int as usize, &mut self.cdf.mv_class0_bit[comp]); - self.sw.symbol(fr, &mut self.cdf.mv_class0_fr[comp][int as usize]); - } else { - let class = 31 - int.leading_zeros(); - let d = int - (1 << class); - self.sw.symbol(class as usize, &mut self.cdf.mv_class[comp]); - for i in 0..class as usize { - self.sw.symbol(((d >> i) & 1) as usize, &mut self.cdf.mv_bit[comp][i]); - } - self.sw.symbol(fr, &mut self.cdf.mv_fr[comp]); - } - } - - // ---- coefficients ---------------------------------------------------------------------- - - fn coeffs(&mut self, plane: usize, start_x: usize, start_y: usize, tx_sz: usize, b: &Leaf, levels: &[i32]) { - let x4 = start_x >> 2; - let y4 = start_y >> 2; - let w4 = TX_WIDTH[tx_sz] as usize >> 2; - let h4 = TX_HEIGHT[tx_sz] as usize >> 2; - let tx_sz_ctx = (TX_SIZE_SQR[tx_sz] as usize + TX_SIZE_SQR_UP[tx_sz] as usize + 1) >> 1; - let ptype = (plane > 0) as usize; - let scan = scan_for(tx_sz); - let mut eob = 0; - for (i, &pos) in scan.iter().enumerate() { - if levels[pos as usize] != 0 { - eob = i + 1; - } - } - let ctx = self.all_zero_ctx(plane, tx_sz, x4, y4, w4, h4); - self.sw.symbol((eob == 0) as usize, &mut self.cdf.txb_skip[tx_sz_ctx][ctx]); - let mut cul_level = 0u32; - let mut dc_category = 0u8; - if eob == 0 { - if plane == 0 { - self.set_tx_types(x4, y4, tx_sz, DCT_DCT); - } - } else { - if plane == 0 { - self.write_transform_type(x4, y4, tx_sz, b); - } - let plane_tx_type = self.compute_tx_type(plane, tx_sz, x4, y4, b); - debug_assert!(plane_tx_type == DCT_DCT || matches!(plane_tx_type, ADST_DCT | DCT_ADST | ADST_ADST)); - let eob_multisize = (TX_WIDTH_LOG2[tx_sz] as usize).min(5) + (TX_HEIGHT_LOG2[tx_sz] as usize).min(5) - 4; - let eob_pt = if eob < 2 { eob } else { (32 - ((eob - 1) as u32).leading_zeros()) as usize + 1 }; - let ectx = 0; // TX_CLASS_2D - let s = eob_pt - 1; - match eob_multisize { - 0 => self.sw.symbol(s, &mut self.cdf.eob_pt_16[ptype][ectx]), - 1 => self.sw.symbol(s, &mut self.cdf.eob_pt_32[ptype][ectx]), - 2 => self.sw.symbol(s, &mut self.cdf.eob_pt_64[ptype][ectx]), - 3 => self.sw.symbol(s, &mut self.cdf.eob_pt_128[ptype][ectx]), - 4 => self.sw.symbol(s, &mut self.cdf.eob_pt_256[ptype][ectx]), - 5 => self.sw.symbol(s, &mut self.cdf.eob_pt_512[ptype]), - _ => self.sw.symbol(s, &mut self.cdf.eob_pt_1024[ptype]), - } - if eob_pt >= 3 { - let eob_shift = eob_pt - 3; - let offset = eob - ((1 << (eob_pt - 2)) + 1); - self.sw.symbol((offset >> eob_shift) & 1, &mut self.cdf.eob_extra[tx_sz_ctx][ptype][eob_pt - 3]); - for i in 1..eob_pt - 2 { - let sh = eob_pt - 2 - 1 - i; - self.sw.bool((offset >> sh) & 1 == 1); - } - } - let bwl = TX_WIDTH_LOG2[tx_sz] as usize; - let height = TX_HEIGHT[tx_sz] as usize; - let width = 1usize << bwl; - // Quant[] as the decoder sees it while parsing: the levels coded so far (capped) - let mut quant = std::mem::take(&mut self.quant); - quant.clear(); - quant.resize(width * height, 0); - for c in (0..eob).rev() { - let pos = scan[c] as usize; - let a = levels[pos].unsigned_abs().min(15) as i32; - if c == eob - 1 { - let ctx = coeff_base_eob_ctx(c, bwl, height); - self.sw.symbol((a.min(3) - 1) as usize, &mut self.cdf.coeff_base_eob[tx_sz_ctx][ptype][ctx]); - } else { - let ctx = coeff_base_ctx(&quant, tx_sz, bwl, width, height, pos); - self.sw.symbol(a.min(3) as usize, &mut self.cdf.coeff_base[tx_sz_ctx][ptype][ctx]); - } - if a > NUM_BASE_LEVELS as i32 { - let ctx = coeff_br_ctx(&quant, bwl, height, pos); - let mut rem = a - 3; - for _ in 0..(COEFF_BASE_RANGE / (BR_CDF_SIZE - 1)) { - let br = rem.min(3); - self.sw.symbol(br as usize, &mut self.cdf.coeff_br[tx_sz_ctx.min(TX_32X32)][ptype][ctx]); - rem -= br; - if br < 3 { - break; - } - } - } - quant[pos] = a; - } - self.quant = quant; - for c in 0..eob { - let pos = scan[c] as usize; - let v = levels[pos]; - if v != 0 { - if c == 0 { - let ctx = self.dc_sign_ctx(plane, x4, y4, w4, h4); - self.sw.symbol((v < 0) as usize, &mut self.cdf.dc_sign[ptype][ctx]); - } else { - self.sw.bool(v < 0); - } - } - let a = v.unsigned_abs(); - if a > (NUM_BASE_LEVELS + COEFF_BASE_RANGE) as u32 { - // Golomb code of a - 15 - let x = a - 15 + 1; - let length = 32 - x.leading_zeros(); - for _ in 0..length - 1 { - self.sw.bool(false); - } - self.sw.bool(true); - for i in (0..length - 1).rev() { - self.sw.bool((x >> i) & 1 == 1); - } - } - if pos == 0 && a > 0 { - dc_category = if v < 0 { 1 } else { 2 }; - } - cul_level += a & 0xFFFFF; - } - cul_level = cul_level.min(63); - } - for i in 0..w4 { - if x4 + i < self.above_level[plane].len() { - self.above_level[plane][x4 + i] = cul_level as u8; - self.above_dc[plane][x4 + i] = dc_category; - } - } - for i in 0..h4 { - if y4 + i < self.left_level[plane].len() { - self.left_level[plane][y4 + i] = cul_level as u8; - self.left_dc[plane][y4 + i] = dc_category; - } - } - } - - fn write_transform_type(&mut self, x4: usize, y4: usize, tx_sz: usize, b: &Leaf) { - // transform_type( ) - let set = tx_set(tx_sz, b.is_inter); - let t = b.tx_type_y; - if set > 0 && self.qidx > 0 { - let sqr = TX_SIZE_SQR[tx_sz] as usize; - if b.is_inter { - // The decision only picks types of this set (DCT_DCT otherwise). - let s = TX_TYPE_INTER_INV_SET3.iter().position(|&v| v as usize == t).unwrap_or_default(); - self.sw.symbol(s, &mut self.cdf.inter_tx_type_set3[sqr]); - } else { - let s = TX_TYPE_INTRA_INV_SET2.iter().position(|&v| v as usize == t).unwrap_or_default(); - self.sw.symbol(s, &mut self.cdf.intra_tx_type_set2[sqr][b.y_mode]); - } - } else { - assert_eq!(t, DCT_DCT); - } - self.set_tx_types(x4, y4, tx_sz, t); - } - - fn set_tx_types(&mut self, x4: usize, y4: usize, tx_sz: usize, t: usize) { - for i in 0..(TX_WIDTH[tx_sz] as usize >> 2) { - for j in 0..(TX_HEIGHT[tx_sz] as usize >> 2) { - if y4 + j < self.g.mi_rows && x4 + i < self.g.mi_cols { - let k = self.mi.idx(y4 + j, x4 + i); - self.mi.tx_type[k] = t as u8; - } - } - } - } - - fn compute_tx_type(&self, plane: usize, tx_sz: usize, x4: usize, y4: usize, b: &Leaf) -> usize { - if TX_SIZE_SQR_UP[tx_sz] as usize > TX_32X32 { - return DCT_DCT; - } - if plane == 0 { - return self.mi.tx_type[self.mi.idx(y4, x4)] as usize; - } - if b.is_inter { - let set = tx_set(tx_sz, true); - let xx = self.c.max(x4 << 1); - let yy = self.r.max(y4 << 1); - let t = self.mi.tx_type[self.mi.idx(yy, xx)] as usize; - return if tx_in_set(set, true, t) { t } else { DCT_DCT }; - } - intra_uv_tx_type(b.uv_mode, tx_sz) - } - - fn all_zero_ctx(&self, plane: usize, tx_sz: usize, x4: usize, y4: usize, w4: usize, h4: usize) -> usize { - let s = (plane > 0) as usize; - let max_x4 = self.g.mi_cols >> s; - let max_y4 = self.g.mi_rows >> s; - let w = TX_WIDTH[tx_sz] as usize; - let h = TX_HEIGHT[tx_sz] as usize; - let bsize = SUBSAMPLED_SIZE[self.bsize][s][s] as usize; - let bw = block_size_px(bsize); - let bh = 4 * NUM_4X4_BLOCKS_HIGH[bsize] as usize; - let al = &self.above_level[plane]; - let ll = &self.left_level[plane]; - if plane == 0 { - let mut top = 0u32; - let mut left = 0u32; - for k in 0..w4 { - if x4 + k < max_x4 { - top = top.max(al[x4 + k] as u32); - } - } - for k in 0..h4 { - if y4 + k < max_y4 { - left = left.max(ll[y4 + k] as u32); - } - } - top = top.min(255); - left = left.min(255); - if bw == w && bh == h { - 0 - } else if top == 0 && left == 0 { - 1 - } else if top == 0 || left == 0 { - 2 + (top.max(left) > 3) as usize - } else if top.max(left) <= 3 { - 4 - } else if top.min(left) <= 3 { - 5 - } else { - 6 - } - } else { - let ad = &self.above_dc[plane]; - let ld = &self.left_dc[plane]; - let mut above = 0u8; - let mut left = 0u8; - for i in 0..w4 { - if x4 + i < max_x4 { - above |= al[x4 + i] | ad[x4 + i]; - } - } - for i in 0..h4 { - if y4 + i < max_y4 { - left |= ll[y4 + i] | ld[y4 + i]; - } - } - let mut ctx = (above != 0) as usize + (left != 0) as usize + 7; - if bw * bh > w * h { - ctx += 3; - } - ctx - } - } - - fn dc_sign_ctx(&self, plane: usize, x4: usize, y4: usize, w4: usize, h4: usize) -> usize { - let s = (plane > 0) as usize; - let max_x4 = self.g.mi_cols >> s; - let max_y4 = self.g.mi_rows >> s; - let mut dc_sign = 0i32; - for k in 0..w4 { - if x4 + k < max_x4 { - match self.above_dc[plane][x4 + k] { - 1 => dc_sign -= 1, - 2 => dc_sign += 1, - _ => {} - } - } - } - for k in 0..h4 { - if y4 + k < max_y4 { - match self.left_dc[plane][y4 + k] { - 1 => dc_sign -= 1, - 2 => dc_sign += 1, - _ => {} - } - } - } - match dc_sign.signum() { - -1 => 1, - 1 => 2, - _ => 0, - } - } - - // ---- motion vector prediction (7.10.2), single reference LAST_FRAME -------------------- - - fn inside(&self, r: isize, c: isize) -> bool { - c >= 0 && c < self.g.mi_cols as isize && r >= 0 && r < self.g.mi_rows as isize - } - - fn find_mv_stack(&mut self) { - let n4 = NUM_4X4_BLOCKS_WIDE[self.bsize] as isize; - let (bw4, bh4) = (n4, n4); - self.mvs = MvStack::default(); - self.scan_row(-1); - let mut found_above = self.mvs.found_match; - self.mvs.found_match = false; - self.scan_col(-1); - let mut found_left = self.mvs.found_match; - self.mvs.found_match = false; - if bw4.max(bh4) <= 16 { - self.scan_point(-1, bw4); - } - if self.mvs.found_match { - found_above = true; - } - self.mvs.close_matches = found_above as usize + found_left as usize; - let num_nearest = self.mvs.num_mv_found; - let num_new = self.mvs.new_mv_count; - for idx in 0..num_nearest { - self.mvs.weight_stack[idx] += REF_CAT_LEVEL as u32; - } - self.mvs.zero_mv_context = 0; - // use_ref_frame_mvs = 0: no temporal candidates - self.scan_point(-1, -1); - if self.mvs.found_match { - found_above = true; - } - self.mvs.found_match = false; - self.scan_row(-3); - if self.mvs.found_match { - found_above = true; - } - self.mvs.found_match = false; - self.scan_col(-3); - if self.mvs.found_match { - found_left = true; - } - self.mvs.found_match = false; - if bh4 > 1 { - self.scan_row(-5); - } - if self.mvs.found_match { - found_above = true; - } - self.mvs.found_match = false; - if bw4 > 1 { - self.scan_col(-5); - } - if self.mvs.found_match { - found_left = true; - } - self.mvs.total_matches = found_above as usize + found_left as usize; - let n = self.mvs.num_mv_found; - self.sort_stack(0, num_nearest); - self.sort_stack(num_nearest, n); - if self.mvs.num_mv_found < 2 { - self.extra_search(); - } - self.context_and_clamping(num_new); - } - - fn scan_row(&mut self, delta_row: isize) { - let bw4 = NUM_4X4_BLOCKS_WIDE[self.bsize] as isize; - let (mi_row, mi_col) = (self.r as isize, self.c as isize); - let end4 = bw4.min(self.g.mi_cols as isize - mi_col).min(16); - let mut delta_col = 0; - let use_step16 = bw4 >= 16; - let mut delta_row = delta_row; - if delta_row.abs() > 1 { - delta_row += mi_row & 1; - delta_col = 1 - (mi_col & 1); - } - let mut i = 0; - while i < end4 { - let mv_row = mi_row + delta_row; - let mv_col = mi_col + delta_col + i; - if !self.inside(mv_row, mv_col) { - break; - } - let k = self.mi.idx(mv_row as usize, mv_col as usize); - let mut len = bw4.min(NUM_4X4_BLOCKS_WIDE[self.mi.mi_size[k] as usize] as isize); - if delta_row.abs() > 1 { - len = len.max(2); - } - if use_step16 { - len = len.max(4); - } - self.add_ref_mv_candidate(mv_row as usize, mv_col as usize, len as u32 * 2); - i += len; - } - } - - fn scan_col(&mut self, delta_col: isize) { - let bh4 = NUM_4X4_BLOCKS_HIGH[self.bsize] as isize; - let (mi_row, mi_col) = (self.r as isize, self.c as isize); - let end4 = bh4.min(self.g.mi_rows as isize - mi_row).min(16); - let mut delta_row = 0; - let use_step16 = bh4 >= 16; - let mut delta_col = delta_col; - if delta_col.abs() > 1 { - delta_row = 1 - (mi_row & 1); - delta_col += mi_col & 1; - } - let mut i = 0; - while i < end4 { - let mv_row = mi_row + delta_row + i; - let mv_col = mi_col + delta_col; - if !self.inside(mv_row, mv_col) { - break; - } - let k = self.mi.idx(mv_row as usize, mv_col as usize); - let mut len = bh4.min(NUM_4X4_BLOCKS_HIGH[self.mi.mi_size[k] as usize] as isize); - if delta_col.abs() > 1 { - len = len.max(2); - } - if use_step16 { - len = len.max(4); - } - self.add_ref_mv_candidate(mv_row as usize, mv_col as usize, len as u32 * 2); - i += len; - } - } - - fn scan_point(&mut self, delta_row: isize, delta_col: isize) { - let mv_row = self.r as isize + delta_row; - let mv_col = self.c as isize + delta_col; - if self.inside(mv_row, mv_col) && self.mi.written[self.mi.idx(mv_row as usize, mv_col as usize)] { - self.add_ref_mv_candidate(mv_row as usize, mv_col as usize, 4); - } - } - - fn add_ref_mv_candidate(&mut self, mv_row: usize, mv_col: usize, weight: u32) { - let i = self.mi.idx(mv_row, mv_col); - if !self.mi.is_inter[i] { - return; - } - let rf = self.mi.ref_frame[i]; - for cand_list in 0..2 { - if rf[cand_list] == LAST_FRAME as i8 { - self.search_stack(i, cand_list, weight); - } - } - } - - fn search_stack(&mut self, i: usize, cand_list: usize, weight: u32) { - debug_assert_eq!(cand_list, 0); - let cand_mode = self.mi.y_mode[i] as usize; - // gm_type is IDENTITY: the stored motion vector is used - let mut cand = self.mi.mv[i]; - lower_mv_precision(&mut cand); - if cand_mode == NEWMV { - self.mvs.new_mv_count += 1; - } - self.mvs.found_match = true; - let n = self.mvs.num_mv_found; - if let Some(idx) = (0..n).find(|&k| self.mvs.ref_stack_mv[k] == cand) { - self.mvs.weight_stack[idx] += weight; - } else if n < MAX_REF_MV_STACK_SIZE { - self.mvs.ref_stack_mv[n] = cand; - self.mvs.weight_stack[n] = weight; - self.mvs.num_mv_found += 1; - } - } - - fn sort_stack(&mut self, start: usize, end: usize) { - let mut end = end; - let s = &mut self.mvs; - while end > start { - let mut new_end = start; - for idx in start + 1..end { - if s.weight_stack[idx - 1] < s.weight_stack[idx] { - s.weight_stack.swap(idx - 1, idx); - s.ref_stack_mv.swap(idx - 1, idx); - new_end = idx; - } - } - end = new_end; - } - } - - fn extra_search(&mut self) { - let (mi_row, mi_col) = (self.r as isize, self.c as isize); - let n4 = NUM_4X4_BLOCKS_WIDE[self.bsize] as isize; - let w4 = n4.min(16).min(self.g.mi_cols as isize - mi_col); - let h4 = n4.min(16).min(self.g.mi_rows as isize - mi_row); - let num4x4 = w4.min(h4); - for pass in 0..2 { - let mut idx = 0; - while idx < num4x4 && self.mvs.num_mv_found < 2 { - let (mv_row, mv_col) = if pass == 0 { (mi_row - 1, mi_col + idx) } else { (mi_row + idx, mi_col - 1) }; - if !self.inside(mv_row, mv_col) { - break; - } - let k = self.mi.idx(mv_row as usize, mv_col as usize); - // add_extra_mv_candidate( ) (no sign bias: a single forward reference) - let rfs = self.mi.ref_frame[k]; - for &cand_ref in rfs.iter().take(2) { - if cand_ref > INTRA_FRAME as i8 { - let cand = self.mi.mv[k]; - let n = self.mvs.num_mv_found; - if !(0..n).any(|j| self.mvs.ref_stack_mv[j] == cand) { - self.mvs.ref_stack_mv[n] = cand; - self.mvs.weight_stack[n] = 2; - self.mvs.num_mv_found += 1; - } - } - } - let sz = self.mi.mi_size[k] as usize; - idx += if pass == 0 { NUM_4X4_BLOCKS_WIDE[sz] } else { NUM_4X4_BLOCKS_HIGH[sz] } as isize; - } - } - for idx in self.mvs.num_mv_found..2 { - self.mvs.ref_stack_mv[idx] = [0, 0]; // GlobalMvs[ 0 ] - } - } - - fn context_and_clamping(&mut self, num_new: usize) { - let bs = block_size_px(self.bsize) as i32; - let n = self.mvs.num_mv_found; - for idx in 0..n { - let mut z = 0; - if idx + 1 < n { - let w0 = self.mvs.weight_stack[idx]; - let w1 = self.mvs.weight_stack[idx + 1]; - if w0 >= REF_CAT_LEVEL as u32 { - if w1 < REF_CAT_LEVEL as u32 { - z = 1; - } - } else { - z = 2; - } - } - self.mvs.drl_ctx_stack[idx] = z; - } - let n4 = NUM_4X4_BLOCKS_WIDE[self.bsize] as i32; - for idx in 0..n { - let mut mv = self.mvs.ref_stack_mv[idx]; - let border_r = MV_BORDER as i32 + bs * 8; - let to_top = -((self.r as i32 * 4) * 8); - let to_bottom = ((self.g.mi_rows as i32 - n4 - self.r as i32) * 4) * 8; - mv[0] = mv[0].clamp(to_top - border_r, to_bottom + border_r); - let to_left = -((self.c as i32 * 4) * 8); - let to_right = ((self.g.mi_cols as i32 - n4 - self.c as i32) * 4) * 8; - mv[1] = mv[1].clamp(to_left - border_r, to_right + border_r); - self.mvs.ref_stack_mv[idx] = mv; - } - let (close, total) = (self.mvs.close_matches, self.mvs.total_matches); - if close == 0 { - self.mvs.new_mv_context = total.min(1); - self.mvs.ref_mv_context = total; - } else if close == 1 { - self.mvs.new_mv_context = 3 - num_new.min(1); - self.mvs.ref_mv_context = 2 + total; - } else { - self.mvs.new_mv_context = 5 - num_new.min(1); - self.mvs.ref_mv_context = 5; - } - } -} - -/// lower_mv_precision( ) with allow_high_precision_mv = 0 and force_integer_mv = 0. -fn lower_mv_precision(mv: &mut MvP) { - for c in mv.iter_mut() { - if *c & 1 != 0 { - *c += if *c > 0 { -1 } else { 1 }; - } - } -} - -fn coeff_base_eob_ctx(c: usize, bwl: usize, height: usize) -> usize { - if c == 0 { - 0 - } else if c <= (height << bwl) / 8 { - 1 - } else if c <= (height << bwl) / 4 { - 2 - } else { - 3 - } -} - -/// Context of coeff_base for TX_CLASS_2D (8.3.2). -fn coeff_base_ctx(quant: &[i32], tx_sz: usize, bwl: usize, width: usize, height: usize, pos: usize) -> usize { - let row = pos >> bwl; - let col = pos - (row << bwl); - let mut mag = 0i32; - for off in SIG_REF_DIFF_OFFSET[TX_CLASS_2D].iter() { - let ref_row = row + off[0] as usize; - let ref_col = col + off[1] as usize; - if ref_row < height && ref_col < width { - mag += quant[(ref_row << bwl) + ref_col].abs().min(3); - } - } - let ctx = ((mag + 1) >> 1).min(4) as usize; - if row == 0 && col == 0 { - return 0; - } - ctx + COEFF_BASE_CTX_OFFSET[tx_sz][row.min(4)][col.min(4)] as usize -} - -/// Context of coeff_br for TX_CLASS_2D. -fn coeff_br_ctx(quant: &[i32], bwl: usize, txh: usize, pos: usize) -> usize { - let txw = 1usize << bwl; - let row = pos >> bwl; - let col = pos - (row << bwl); - let mut mag = 0i32; - for off in MAG_REF_OFFSET_WITH_TX_CLASS[TX_CLASS_2D].iter() { - let ref_row = row + off[0] as usize; - let ref_col = col + off[1] as usize; - if ref_row < txh && ref_col < txw { - mag += quant[ref_row * txw + ref_col].min((COEFF_BASE_RANGE + NUM_BASE_LEVELS + 1) as i32); - } - } - let mag = ((mag + 1) >> 1).min(6) as usize; - if pos == 0 { - mag - } else if row < 2 && col < 2 { - mag + 7 - } else { - mag + 14 - } -} diff --git a/crates/av1enc/tests/it/main.rs b/crates/av1enc/tests/it/main.rs deleted file mode 100644 index c66bacde5..000000000 --- a/crates/av1enc/tests/it/main.rs +++ /dev/null @@ -1,6 +0,0 @@ -//! The crate's integration tests, one binary (each module is a former `tests/.rs`). - -mod bench; -mod common; -mod ffmpeg_oracle; -mod roundtrip; diff --git a/crates/export/Cargo.toml b/crates/export/Cargo.toml index e0be45551..164b10a7f 100644 --- a/crates/export/Cargo.toml +++ b/crates/export/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "effectcraft-export" -description = "Render Queue jobs and output modules: H.264 MP4 (+AAC), ProRes and uncompressed MOV (+PCM) via FilmCraft, HEVC / AV1 MP4 (+AAC) and WebM (VP9 or AV1 + Opus) via EffectCraft encoders, PNG/JPEG/TIFF/EXR/Targa sequences, animated GIF, WAV/AIFF" +description = "Render Queue jobs and output modules: H.264 MP4 (+AAC), ProRes and uncompressed MOV (+PCM) , HEVC / AV1 MP4 (+AAC) and WebM (VP9 or AV1) via craft-libs codecs, Opus via effectcraft-opusenc, PNG/JPEG/TIFF/EXR/Targa sequences, animated GIF, WAV/AIFF" version.workspace = true edition.workspace = true license.workspace = true @@ -14,14 +14,10 @@ effectcraft-time = { workspace = true } effectcraft-project = { workspace = true } effectcraft-raster = { workspace = true } effectcraft-render = { workspace = true } -filmcraft-h264enc = { workspace = true } -filmcraft-prores = { workspace = true } -filmcraft-aac = { workspace = true } -filmcraft-isobmff = { workspace = true } +craft-video = { workspace = true } +craft-audio = { workspace = true } +craft-media = { workspace = true } effectcraft-opusenc = { workspace = true } -effectcraft-vp9enc = { workspace = true } -effectcraft-hevcenc = { workspace = true } -effectcraft-av1enc = { workspace = true } image = { workspace = true, features = ["png", "jpeg", "tiff", "exr", "tga"] } exr = { workspace = true } gif = { workspace = true } @@ -35,6 +31,7 @@ tempfile = { workspace = true } effectcraft-media = { workspace = true } effectcraft-effects = { workspace = true } effectcraft-color = { workspace = true } -filmcraft-opus = { workspace = true } +filmcraft-hevc = { workspace = true } +filmcraft-av1 = { workspace = true } filmcraft-vp9 = { workspace = true } effectcraft-keyframe = { workspace = true } diff --git a/crates/export/src/encode.rs b/crates/export/src/encode.rs index dd972d326..8caad8dea 100644 --- a/crates/export/src/encode.rs +++ b/crates/export/src/encode.rs @@ -1,13 +1,13 @@ -//! Movie export: FilmCraft H.264, HEVC (`effectcraft-hevcenc`) and AV1 (`effectcraft-av1enc`) +//! Movie export: H.264 (`craft_video::h264`), HEVC (`craft_video::hevc`) and AV1 (`craft_video::av1`) //! → MP4 (+ AAC), ProRes and uncompressed `raw ` → MOV (+ PCM), muxed by -//! FilmCraft's ISO BMFF / QuickTime writer. +//! craft-media's ISO BMFF / QuickTime writer. use std::io::Write; +use craft_media::isobmff::{Brand, ColorInfo, FourCc, Mp4Writer, PcmConfig, SampleEntry, TrackConfig, WriteSample, WriterOptions}; use effectcraft_project::render_queue::{AudioFormat, Channels, OutputFormat, ProResProfile}; use effectcraft_project::{Comp, ProjectSettings}; use effectcraft_time::{FrameRate, Tick}; -use filmcraft_isobmff::{Brand, ColorInfo, FourCc, Mp4Writer, PcmConfig, SampleEntry, TrackConfig, WriteSample, WriterOptions}; use rayon::prelude::*; use crate::{Cx, ExportError, Report, Result, State, batch_size, wants_audio}; @@ -117,7 +117,7 @@ pub(crate) trait VideoEncoder: Send { // ---------------------------------------------------------------- H.264 struct H264 { - enc: filmcraft_h264enc::Encoder, + enc: craft_video::h264::Encoder, w: u32, h: u32, rate: FrameRate, @@ -129,26 +129,26 @@ struct H264 { impl H264 { fn new(w: u32, h: u32, rate: FrameRate, kbps: u32, codes: Codes) -> Result { - let mut cfg = filmcraft_h264enc::EncoderConfig::new(w, h, rate.num as u32, rate.den as u32); - cfg.format = filmcraft_h264enc::PacketFormat::LengthPrefixed; + let mut cfg = craft_video::h264::EncoderConfig::new(w, h, rate.num as u32, rate.den as u32); + cfg.format = craft_video::h264::PacketFormat::LengthPrefixed; cfg.aud = false; cfg.keyint = (rate.as_f64() * 2.0).round().max(1.0) as u32; let kbps = kbps.max(100); - cfg.rate = filmcraft_h264enc::RateControl::Vbr { target_kbps: kbps, max_kbps: kbps * 3 / 2 }; - cfg.color = filmcraft_h264enc::ColorConfig { primaries: codes.primaries, transfer: codes.transfer, matrix: codes.matrix, full_range: false }; - let enc = filmcraft_h264enc::Encoder::new(cfg).map_err(enc)?; + cfg.rate = craft_video::h264::RateControl::Vbr { target_kbps: kbps, max_kbps: kbps * 3 / 2 }; + cfg.color = craft_video::h264::ColorConfig { primaries: codes.primaries, transfer: codes.transfer, matrix: codes.matrix, full_range: false }; + let enc = craft_video::h264::Encoder::new(cfg).map_err(enc)?; Ok(H264 { enc, w, h, rate, codes, y: vec![], u: vec![], v: vec![] }) } - fn packets(&self, ps: Vec) -> Vec { + fn packets(&self, ps: Vec) -> Vec { ps.into_iter().map(|p| Packet { data: p.data, key: p.keyframe, cto: (p.pts - p.dts) as i32 }).collect() } } impl VideoEncoder for H264 { fn sample_entry(&self) -> SampleEntry { - let cfg = filmcraft_isobmff::AvcConfig::parse(&self.enc.avcc()).unwrap_or_else(|_| { + let cfg = craft_media::isobmff::AvcConfig::parse(&self.enc.avcc()).unwrap_or_else(|_| { let (sps, pps) = self.enc.sps_pps(); - filmcraft_isobmff::AvcConfig::new(vec![sps], vec![pps], 4) + craft_media::isobmff::AvcConfig::new(vec![sps], vec![pps], 4) }); let mut entry = SampleEntry::avc(cfg, self.w as u16, self.h as u16); tag_nclx_limited(&mut entry, self.codes); @@ -170,7 +170,7 @@ impl VideoEncoder for H264 { fn encode_yuv420(&mut self, p: &Yuv420, index: u64) -> Result> { let (w, cw) = (self.w as usize, (self.w as usize).div_ceil(2)); let (y, u, v) = p.slices(w, self.h as usize); - let frame = filmcraft_h264enc::YuvFrame { y, u, v, y_stride: w, uv_stride: cw }; + let frame = craft_video::h264::YuvFrame { y, u, v, y_stride: w, uv_stride: cw }; let ps = self.enc.try_encode(&frame, index as i64 * self.rate.den).map_err(enc)?; Ok(self.packets(ps)) } @@ -188,8 +188,8 @@ pub(crate) use effectcraft_raster::yuv::rgba_to_yuv420; // ---------------------------------------------------------------- ProRes struct ProRes { - enc: filmcraft_prores::Encoder, - profile: filmcraft_prores::Profile, + enc: craft_video::prores::Encoder, + profile: craft_video::prores::Profile, w: u32, h: u32, alpha: bool, @@ -202,8 +202,8 @@ fn prores_colr(c: Codes) -> ColorInfo { ColorInfo::Nclc { primaries: c.primaries.into(), transfer: c.transfer.into(), matrix: c.matrix.into() } } -fn prores_profile(p: ProResProfile) -> filmcraft_prores::Profile { - use filmcraft_prores::Profile; +fn prores_profile(p: ProResProfile) -> craft_video::prores::Profile { + use craft_video::prores::Profile; match p { ProResProfile::Proxy => Profile::Proxy, ProResProfile::Lt => Profile::Lt, @@ -220,18 +220,18 @@ impl ProRes { let profile = if channels == Channels::Rgba && !profile.is_4444() { ProResProfile::P4444 } else { profile }; let profile = prores_profile(profile); let alpha = channels == Channels::Rgba; - let mut cfg = filmcraft_prores::EncoderConfig::new(profile, w, h); + let mut cfg = craft_video::prores::EncoderConfig::new(profile, w, h); cfg.encode_alpha = alpha; // The frame header carries the same H.273 codes as the `colr` atom, so readers that trust // either one agree. if let ColorInfo::Nclc { primaries, transfer, matrix } = colr { let code = |c| u8::try_from(c).unwrap_or(2); - cfg.color = filmcraft_prores::ColorInfo { primaries: code(primaries), transfer: code(transfer), matrix: code(matrix) }; + cfg.color = craft_video::prores::ColorInfo { primaries: code(primaries), transfer: code(transfer), matrix: code(matrix) }; } - ProRes { enc: filmcraft_prores::Encoder::with_config(cfg), profile, w, h, alpha, colr } + ProRes { enc: craft_video::prores::Encoder::with_config(cfg), profile, w, h, alpha, colr } } fn is_444(&self) -> bool { - matches!(self.profile, filmcraft_prores::Profile::P4444 | filmcraft_prores::Profile::P4444Xq) + matches!(self.profile, craft_video::prores::Profile::P4444 | craft_video::prores::Profile::P4444Xq) } } @@ -244,8 +244,8 @@ impl VideoEncoder for ProRes { entry } fn encode(&mut self, rgba: &[u8], _index: u64) -> Result> { - let chroma = if self.is_444() { filmcraft_prores::ChromaFormat::Yuv444 } else { filmcraft_prores::ChromaFormat::Yuv422 }; - let mut fr = filmcraft_prores::Frame::new(self.w, self.h, chroma, 10, self.alpha); + let chroma = if self.is_444() { craft_video::prores::ChromaFormat::Yuv444 } else { craft_video::prores::ChromaFormat::Yuv422 }; + let mut fr = craft_video::prores::Frame::new(self.w, self.h, chroma, 10, self.alpha).map_err(enc)?; rgba_to_yuv_10(rgba, self.w as usize, self.h as usize, self.is_444(), &mut fr.y, &mut fr.cb, &mut fr.cr); if let Some(a) = fr.alpha.as_mut() { for (d, s) in a.iter_mut().zip(rgba.as_chunks::<4>().0.iter()) { @@ -291,7 +291,7 @@ fn rgba_to_yuv_10(rgba: &[u8], w: usize, h: usize, full_chroma: bool, y: &mut [u // ---------------------------------------------------------------- audio enum Audio { - Aac(Box), + Aac(Box), Pcm, } @@ -401,7 +401,7 @@ pub(crate) async fn movie(job: &Cx<'_>, comp: &Comp, w: u32, h: u32, st: &mut St // from the media duration and drop the last frame. let frames_ts = st.total as i128 * rate.den as i128; let seg = (frames_ts * movie_ts + rate.num as i128 / 2) / rate.num as i128; - vcfg.edits = vec![filmcraft_isobmff::Edit { segment_duration: seg as u64, media_time: start, media_rate: 0x10000 }]; + vcfg.edits = vec![craft_media::isobmff::Edit { segment_duration: seg as u64, media_time: start, media_rate: 0x10000 }]; } let vt = mux.add_track(vcfg).map_err(mux_err)?; let sr = job.output.audio_sample_rate.clamp(8_000, 192_000); @@ -411,7 +411,7 @@ pub(crate) async fn movie(job: &Cx<'_>, comp: &Comp, w: u32, h: u32, st: &mut St let mut audio = None; if with_audio { let (a, entry, start) = if brand == Brand::Mp4 { - let e = filmcraft_aac::Encoder::new(filmcraft_aac::EncoderConfig::cbr(sr, chans as usize, 160_000 * chans)).map_err(enc)?; + let e = craft_audio::aac::Encoder::new(craft_audio::aac::EncoderConfig::cbr(sr, chans as usize, 160_000 * chans)).map_err(enc)?; let entry = SampleEntry::aac(e.audio_specific_config(), chans, sr); let priming = e.priming_samples() as i64; (Audio::Aac(Box::new(e)), entry, Some(priming)) diff --git a/crates/export/src/hevc_av1.rs b/crates/export/src/hevc_av1.rs index d43b1c5ed..539b6e383 100644 --- a/crates/export/src/hevc_av1.rs +++ b/crates/export/src/hevc_av1.rs @@ -1,10 +1,10 @@ -//! HEVC (`effectcraft-hevcenc`) and AV1 (`effectcraft-av1enc`) video encoders for the MP4 +//! HEVC (`craft_video::hevc`) and AV1 (`craft_video::av1`) video encoders for the MP4 //! movie writer (`hvc1` / `av01` sample entries) and AV1 in WebM, configured from the Output //! Module's [`VideoCodecOptions`]. +use craft_media::isobmff::{Av1Config, CodecConfig, FourCc, HevcConfig, SampleEntry}; use effectcraft_project::render_queue::{CodecProfile, OutputModule, RateControlMode, VideoCodecOptions}; use effectcraft_time::FrameRate; -use filmcraft_isobmff::{Av1Config, CodecConfig, FourCc, HevcConfig, SampleEntry}; use rayon::prelude::*; use crate::encode::{Codes, Packet, VideoEncoder}; @@ -55,7 +55,7 @@ struct Planes { // ---------------------------------------------------------------- HEVC pub(crate) struct Hevc { - enc: effectcraft_hevcenc::Encoder, + enc: craft_video::hevc::Encoder, w: u32, h: u32, bits: u8, @@ -64,8 +64,8 @@ pub(crate) struct Hevc { } /// The HEVC encoder settings for an output module. -pub(crate) fn hevc_config(w: u32, h: u32, rate: FrameRate, om: &OutputModule) -> effectcraft_hevcenc::EncoderConfig { - use effectcraft_hevcenc as he; +pub(crate) fn hevc_config(w: u32, h: u32, rate: FrameRate, om: &OutputModule) -> craft_video::hevc::EncoderConfig { + use craft_video::hevc as he; let o: &VideoCodecOptions = &om.codec; let mut cfg = he::EncoderConfig::new(w, h, rate.num as u32, rate.den as u32); cfg.profile = match o.profile { @@ -86,7 +86,7 @@ impl Hevc { let mut cfg = hevc_config(w, h, rate, om); (cfg.colour_primaries, cfg.transfer_characteristics, cfg.matrix_coeffs) = (codes.primaries, codes.transfer, codes.matrix); let bits = cfg.profile.bit_depth(); - let enc = effectcraft_hevcenc::Encoder::new(cfg).map_err(enc_err)?; + let enc = craft_video::hevc::Encoder::new(cfg).map_err(enc_err)?; Ok(Hevc { enc, w, h, bits, codes, planes: Planes::default() }) } } @@ -102,8 +102,8 @@ impl VideoEncoder for Hevc { let (w, h) = (self.w as usize, self.h as usize); let p = &mut self.planes; rgba_to_yuv420_bits(rgba, w, h, self.bits, &mut p.y, &mut p.u, &mut p.v); - let frame = effectcraft_hevcenc::Frame { y: &p.y, u: &p.u, v: &p.v, y_stride: w, uv_stride: w.div_ceil(2) }; - let pk = self.enc.encode(&frame); + let frame = craft_video::hevc::Frame { y: &p.y, u: &p.u, v: &p.v, y_stride: w, uv_stride: w.div_ceil(2) }; + let pk = self.enc.encode(&frame).map_err(enc_err)?; Ok(vec![Packet { data: pk.data, key: pk.keyframe, cto: 0 }]) } fn flush(&mut self) -> Result> { @@ -114,7 +114,7 @@ impl VideoEncoder for Hevc { // ---------------------------------------------------------------- AV1 pub(crate) struct Av1 { - enc: effectcraft_av1enc::Encoder, + enc: craft_video::av1::Encoder, w: u32, h: u32, bits: u8, @@ -123,8 +123,8 @@ pub(crate) struct Av1 { } /// The AV1 encoder settings for an output module. -pub(crate) fn av1_config(w: u32, h: u32, rate: FrameRate, om: &OutputModule) -> effectcraft_av1enc::EncoderConfig { - use effectcraft_av1enc as ae; +pub(crate) fn av1_config(w: u32, h: u32, rate: FrameRate, om: &OutputModule) -> craft_video::av1::EncoderConfig { + use craft_video::av1 as ae; let o: &VideoCodecOptions = &om.codec; let mut cfg = ae::EncoderConfig::new(w, h, rate.num as u32, rate.den as u32); cfg.bit_depth = o.profile.bit_depth(); @@ -142,7 +142,7 @@ impl Av1 { let mut cfg = av1_config(w, h, rate, om); (cfg.color_primaries, cfg.transfer_characteristics, cfg.matrix_coefficients) = (codes.primaries, codes.transfer, codes.matrix); let bits = cfg.bit_depth; - let enc = effectcraft_av1enc::Encoder::new(cfg).map_err(enc_err)?; + let enc = craft_video::av1::Encoder::new(cfg).map_err(enc_err)?; Ok(Av1 { enc, w, h, bits, codes, planes: Planes::default() }) } /// The `av1C` record (also the WebM CodecPrivate). @@ -162,8 +162,8 @@ impl VideoEncoder for Av1 { let (w, h) = (self.w as usize, self.h as usize); let p = &mut self.planes; rgba_to_yuv420_bits(rgba, w, h, self.bits, &mut p.y, &mut p.u, &mut p.v); - let frame = effectcraft_av1enc::Frame { y: &p.y, u: &p.u, v: &p.v, y_stride: w, uv_stride: w.div_ceil(2) }; - let pk = self.enc.encode(&frame); + let frame = craft_video::av1::Frame { y: &p.y, u: &p.u, v: &p.v, y_stride: w, uv_stride: w.div_ceil(2) }; + let pk = self.enc.encode(&frame).map_err(enc_err)?; Ok(vec![Packet { data: pk.data, key: pk.keyframe, cto: 0 }]) } fn flush(&mut self) -> Result> { @@ -196,9 +196,9 @@ mod tests { om.keyframe_interval = 12; let rate = FrameRate::new(30, 1); let h = hevc_config(64, 32, rate, &om); - assert_eq!(h.profile, effectcraft_hevcenc::Profile::Main10); + assert_eq!(h.profile, craft_video::hevc::Profile::Main10); assert_eq!(h.level_idc, Some(123)); - assert_eq!(h.rate, effectcraft_hevcenc::RateControl::ConstantQp(4)); + assert_eq!(h.rate, craft_video::hevc::RateControl::ConstantQp(4)); assert_eq!(h.keyint, 12); let a = av1_config(64, 32, rate, &om); assert_eq!((a.bit_depth, a.level_idx, a.keyint), (10, Some(9), 12)); @@ -206,6 +206,6 @@ mod tests { om.keyframe_interval = 0; om.bitrate_kbps = 3000; let a = av1_config(64, 32, rate, &om); - assert_eq!((a.rate, a.keyint), (effectcraft_av1enc::RateControl::Bitrate { kbps: 3000 }, 60)); + assert_eq!((a.rate, a.keyint), (craft_video::av1::RateControl::Bitrate { kbps: 3000 }, 60)); } } diff --git a/crates/export/src/lib.rs b/crates/export/src/lib.rs index 48cb07ac4..2687eadad 100644 --- a/crates/export/src/lib.rs +++ b/crates/export/src/lib.rs @@ -2,16 +2,16 @@ //! //! | Format | Container / files | Video | Alpha | Audio | //! |---|---|---|---|---| -//! | H.264 | MP4 | FilmCraft H.264 High, 8-bit 4:2:0 BT.709 limited, VBR | no | AAC-LC (FilmCraft), mono/stereo | +//! | H.264 | MP4 | `craft_video::h264` High, 8-bit 4:2:0 BT.709 limited, VBR | no | AAC-LC (`craft_audio::aac`), mono/stereo | //! | ProRes | QuickTime MOV | FilmCraft ProRes 422 Proxy/LT/422/HQ, 10-bit; 4444/4444 XQ 4:4:4 | 4444 (16-bit alpha coding) | PCM 16/24-bit or 32-bit float, mono/stereo | //! | Uncompressed | QuickTime MOV (`raw `) | 8-bit RGB (depth 24, rows padded to even bytes) or ARGB (depth 32) | RGB + Alpha (straight or premultiplied) | PCM 16/24-bit or 32-bit float, mono/stereo | //! | PNG / JPEG / TIFF sequence | one file per frame | 8-bit | PNG, TIFF | — | //! | OpenEXR sequence | one file per frame, scan lines | 32-bit float (default) or half float, linear light, premultiplied; compression None, RLE, ZIPS, ZIP (default), PIZ, PXR24, B44, B44A, DWAA, DWAB | yes | — | //! | Targa sequence | one file per frame | uncompressed 8-bit: 24-bit RGB, 32-bit RGB + Alpha (8 alpha bits), 8-bit grey for Alpha; top-left origin | yes (straight or premultiplied) | — | //! | Animated GIF | GIF89a | 256-colour palette per frame (NeuQuant) | 1-bit | — | -//! | WebM | WebM (Matroska) | VP9 profile 0 (`effectcraft-vp9enc`: key + inter frames, a key frame every 2 s, loop filter), 8-bit 4:2:0; or AV1 (`effectcraft-av1enc`) | VP9 alpha (BlockAdditional) | Opus (`effectcraft-opusenc`: SILK / hybrid / CELT by bitrate and application), 48 kHz | -//! | HEVC | MP4 (`hvc1`) | `effectcraft-hevcenc` Main / Main 10 4:2:0, I + P slices, bitrate or constant quality, level, key-frame interval | no | AAC-LC (FilmCraft) | -//! | AV1 | MP4 (`av01`) | `effectcraft-av1enc` Main 8/10-bit 4:2:0, key + inter frames | no | AAC-LC (FilmCraft) | +//! | WebM | WebM (Matroska) | VP9 profile 0 (`craft_video::vp9`: key + inter frames, a key frame every 2 s, loop filter), 8-bit 4:2:0; or AV1 (`craft_video::av1`) | VP9 alpha (BlockAdditional) | Opus (`effectcraft-opusenc`: SILK / hybrid / CELT by bitrate and application), 48 kHz | +//! | HEVC | MP4 (`hvc1`) | `craft_video::hevc` Main / Main 10 4:2:0, I + P slices, bitrate or constant quality, level, key-frame interval | no | AAC-LC (`craft_audio::aac`) | +//! | AV1 | MP4 (`av01`) | `craft_video::av1` Main 8/10-bit 4:2:0, key + inter frames | no | AAC-LC (`craft_audio::aac`) | //! | WAV / AIFF | RIFF WAVE / AIFF (AIFF-C for float) | — | — | PCM 16/24-bit or 32-bit float, mono/stereo (audio only) | //! //! Every frame goes through the internal `pipeline` module: the Render Settings overrides (Effects, Solo Switches, diff --git a/crates/export/src/uncompressed.rs b/crates/export/src/uncompressed.rs index 1bd1d9bad..f5cfc33d5 100644 --- a/crates/export/src/uncompressed.rs +++ b/crates/export/src/uncompressed.rs @@ -7,7 +7,7 @@ //! A R G B / R G B order, and each row is padded to an even number of bytes (only depth 24 at odd //! widths needs it). -use filmcraft_isobmff::{CodecConfig, FourCc, SampleEntry, VideoParams}; +use craft_media::isobmff::{CodecConfig, FourCc, SampleEntry, VideoParams}; use crate::encode::{Packet, VideoEncoder}; use crate::{ExportError, Result}; diff --git a/crates/export/src/webm.rs b/crates/export/src/webm.rs index 976eaf6e0..2802ac928 100644 --- a/crates/export/src/webm.rs +++ b/crates/export/src/webm.rs @@ -5,7 +5,7 @@ //! (size patched at the end), Info, Tracks and one Cluster per render batch. Video frames are //! SimpleBlocks; with alpha (Channels: RGB + Alpha) each frame is a BlockGroup whose //! BlockAdditional (BlockAddID 1) carries a second VP9 frame coding the alpha channel as luma, -//! with `AlphaMode` 1 on the track, as WebM defines. VP9 comes from `effectcraft-vp9enc` (key +//! with `AlphaMode` 1 on the track, as WebM defines. VP9 comes from `craft_video::vp9` (key //! frames every two seconds, inter frames between them: SimpleBlocks carry the key flag, inter //! BlockGroups a ReferenceBlock; the alpha stream's key frames line up with the colour stream's), //! Opus from `effectcraft-opusenc` (CELT, 48 kHz stereo, 20 ms packets, @@ -142,7 +142,7 @@ pub(crate) async fn webm(job: &Cx<'_>, comp: &Comp, w: u32, h: u32, st: &mut Sta let colour_channels = if job.output.channels == Channels::Alpha { Channels::Alpha } else { Channels::Rgb }; let quality = job.output.quality.clamp(1, 100); let fps = rate.as_f64().max(1.0); - let cfg = effectcraft_vp9enc::EncoderConfig { + let cfg = craft_video::vp9::EncoderConfig { width: w, height: h, quality, @@ -152,8 +152,8 @@ pub(crate) async fn webm(job: &Cx<'_>, comp: &Comp, w: u32, h: u32, st: &mut Sta frame_rate: fps, loop_filter: true, }; - let mut color_enc = effectcraft_vp9enc::Vp9Encoder::new(cfg.clone()); - let mut alpha_enc = alpha.then(|| effectcraft_vp9enc::Vp9Encoder::new(cfg.clone())); + let mut color_enc = craft_video::vp9::Vp9Encoder::new(cfg.clone()); + let mut alpha_enc = alpha.then(|| craft_video::vp9::Vp9Encoder::new(cfg.clone())); let with_audio = wants_audio(job); let opus_channels = if job.output.audio_channels == 1 { 1usize } else { 2 }; let mut opus = with_audio.then(|| opus_encoder(job)); @@ -266,12 +266,12 @@ pub(crate) async fn webm(job: &Cx<'_>, comp: &Comp, w: u32, h: u32, st: &mut Sta for (j, px) in pixels.iter().enumerate() { let (color, a) = rayon::join( || { - let (y, u, vv) = effectcraft_vp9enc::rgba_to_yuv420(px, w, h, false); + let (y, u, vv) = craft_video::vp9::rgba_to_yuv420(px, w, h, false); color_enc.encode_frame(&y, &u, &vv, false) }, || { alpha_enc.as_mut().map(|ea| { - let (y, u, vv) = effectcraft_vp9enc::alpha_to_yuv420(px, w, h); + let (y, u, vv) = craft_video::vp9::alpha_to_yuv420(px, w, h); ea.encode_frame(&y, &u, &vv, false).data }) }, diff --git a/crates/av1enc/tests/it/bench.rs b/crates/export/tests/it/av1_encoder/bench.rs similarity index 94% rename from crates/av1enc/tests/it/bench.rs rename to crates/export/tests/it/av1_encoder/bench.rs index b29817e1a..302308c50 100644 --- a/crates/av1enc/tests/it/bench.rs +++ b/crates/export/tests/it/av1_encoder/bench.rs @@ -1,13 +1,13 @@ //! Speed / efficiency measurements on ffmpeg-generated test sources (ignored by default): -//! `cargo test -p effectcraft-av1enc --test it bench:: -- --ignored --nocapture`. +//! `cargo test -p effectcraft-export --test it av1_encoder::bench:: -- --ignored --nocapture`. -use crate::common; +use super::common; use std::process::Command; use std::time::Instant; use common::*; -use effectcraft_av1enc::{Encoder, EncoderConfig, RateControl}; +use craft_video::av1::{Encoder, EncoderConfig, RateControl}; /// Raw 8-bit 4:2:0 frames of an ffmpeg lavfi source, or None without ffmpeg. fn lavfi(src: &str, w: usize, h: usize, frames: usize) -> Option> { @@ -52,7 +52,7 @@ fn run(name: &str, pics: &[Pic], rate: RateControl, keyint: u32) { let mut sizes = Vec::new(); for p in pics { let t = Instant::now(); - let pkt = enc.encode(&p.frame()); + let pkt = enc.encode(&p.frame()).expect("encode"); let dt = t.elapsed().as_secs_f64(); if pkt.keyframe { t_key += dt; diff --git a/crates/av1enc/tests/it/common/mod.rs b/crates/export/tests/it/av1_encoder/common.rs similarity index 97% rename from crates/av1enc/tests/it/common/mod.rs rename to crates/export/tests/it/av1_encoder/common.rs index 048e3de17..3cd42a584 100644 --- a/crates/av1enc/tests/it/common/mod.rs +++ b/crates/export/tests/it/av1_encoder/common.rs @@ -1,6 +1,6 @@ #![allow(dead_code)] -use effectcraft_av1enc::{Encoder, EncoderConfig, Frame, Packet}; +use craft_video::av1::{Encoder, EncoderConfig, Frame, Packet}; /// A synthetic 4:2:0 picture. pub struct Pic { @@ -95,7 +95,7 @@ pub fn encode_and_check(cfg: EncoderConfig, pics: &[Pic]) -> Run { let mut dec = filmcraft_av1::Decoder::with_threads(1); let mut run = Run { packets: Vec::new(), recons: Vec::new(), psnr_y: Vec::new(), lf_levels: Vec::new() }; for (i, p) in pics.iter().enumerate() { - let pkt = enc.encode(&p.frame()); + let pkt = enc.encode(&p.frame()).expect("encode"); let rec = enc.last_reconstruction().expect("reconstruction"); let out = dec.decode(&pkt.data).unwrap_or_else(|e| panic!("frame {i}: decode error {e:?}")); assert_eq!(out.len(), 1, "frame {i}: one picture per packet"); diff --git a/crates/av1enc/tests/it/ffmpeg_oracle.rs b/crates/export/tests/it/av1_encoder/ffmpeg_oracle.rs similarity index 97% rename from crates/av1enc/tests/it/ffmpeg_oracle.rs rename to crates/export/tests/it/av1_encoder/ffmpeg_oracle.rs index ba206a680..6e235ed3b 100644 --- a/crates/av1enc/tests/it/ffmpeg_oracle.rs +++ b/crates/export/tests/it/av1_encoder/ffmpeg_oracle.rs @@ -2,12 +2,12 @@ //! bit for bit. Skipped when `ffmpeg` (with libdav1d) is not installed. ffmpeg's native `av1` //! decoder only drives hardware decoders, so it is not used. -use crate::common; +use super::common; use std::process::Command; use common::*; -use effectcraft_av1enc::{Encoder, EncoderConfig, Packet, RateControl}; +use craft_video::av1::{Encoder, EncoderConfig, Packet, RateControl}; fn have_libdav1d() -> bool { let Ok(out) = Command::new("ffmpeg").args(["-hide_banner", "-decoders"]).output() else { @@ -53,7 +53,7 @@ fn check(name: &str, cfg: EncoderConfig, pics: &[Pic]) -> Vec { let mut recons = Vec::new(); let mut levels = Vec::new(); for p in pics { - packets.push(enc.encode(&p.frame())); + packets.push(enc.encode(&p.frame()).expect("encode")); recons.push(enc.last_reconstruction().expect("reconstruction")); levels.push(enc.last_loop_filter_level().expect("level")); } diff --git a/crates/export/tests/it/av1_encoder/mod.rs b/crates/export/tests/it/av1_encoder/mod.rs new file mode 100644 index 000000000..d33ea0c55 --- /dev/null +++ b/crates/export/tests/it/av1_encoder/mod.rs @@ -0,0 +1,6 @@ +//! craft-video's AV1 encoder against the filmcraft-av1 decoder and ffmpeg's libdav1d. + +mod bench; +mod common; +mod ffmpeg_oracle; +mod roundtrip; diff --git a/crates/av1enc/tests/it/roundtrip.rs b/crates/export/tests/it/av1_encoder/roundtrip.rs similarity index 93% rename from crates/av1enc/tests/it/roundtrip.rs rename to crates/export/tests/it/av1_encoder/roundtrip.rs index edc98c370..8e7dcc418 100644 --- a/crates/av1enc/tests/it/roundtrip.rs +++ b/crates/export/tests/it/av1_encoder/roundtrip.rs @@ -1,10 +1,10 @@ //! Round trips through FilmCraft's spec-derived AV1 decoder: decoded frames must equal the //! encoder's own reconstruction bit for bit. -use crate::common; +use super::common; use common::*; -use effectcraft_av1enc::{EncoderConfig, RateControl}; +use craft_video::av1::{EncoderConfig, RateControl}; fn cfg(w: u32, h: u32, bd: u8, q: u8, keyint: u32) -> EncoderConfig { let mut c = EncoderConfig::new(w, h, 30, 1); @@ -89,8 +89,8 @@ fn bitrate_mode_is_accurate_over_many_groups() { let pics: Vec = (0..n).map(|t| synth(192, 128, bd, t)).collect(); let mut c = cfg(192, 128, bd as u8, 0, keyint); c.rate = RateControl::Bitrate { kbps }; - let mut enc = effectcraft_av1enc::Encoder::new(c).unwrap(); - let bits: usize = pics.iter().map(|p| enc.encode(&p.frame()).data.len() * 8).sum(); + let mut enc = craft_video::av1::Encoder::new(c).unwrap(); + let bits: usize = pics.iter().map(|p| enc.encode(&p.frame()).expect("encode").data.len() * 8).sum(); let got = bits as f64 / (n as f64 / 30.0) / 1000.0; eprintln!("{bd} bit, keyint {keyint}: {got:.1} kbit/s for {kbps}"); assert!((got / kbps as f64 - 1.0).abs() < 0.05, "{got:.1} kbit/s for a {kbps} kbit/s target ({bd} bit, keyint {keyint})"); @@ -99,7 +99,7 @@ fn bitrate_mode_is_accurate_over_many_groups() { #[test] fn headers() { - let enc = effectcraft_av1enc::Encoder::new(cfg(1920, 1080, 10, 100, 60)).unwrap(); + let enc = craft_video::av1::Encoder::new(cfg(1920, 1080, 10, 100, 60)).unwrap(); let c = enc.av1c(); assert_eq!(c[0], 0x81); assert_eq!(c[1] & 0x1f, 8, "1080p30 is level 4.0"); diff --git a/crates/hevcenc/tests/roundtrip.rs b/crates/export/tests/it/hevc_encoder.rs similarity index 98% rename from crates/hevcenc/tests/roundtrip.rs rename to crates/export/tests/it/hevc_encoder.rs index 7591517e9..c19ff9cbd 100644 --- a/crates/hevcenc/tests/roundtrip.rs +++ b/crates/export/tests/it/hevc_encoder.rs @@ -1,7 +1,7 @@ //! Round trips through the filmcraft-hevc decoder and (when installed) ffmpeg: decoded pictures must //! equal the encoder's own reconstruction bit-exactly. -use effectcraft_hevcenc::{Encoder, EncoderConfig, Frame, Profile, RateControl}; +use craft_video::hevc::{Encoder, EncoderConfig, Frame, Profile, RateControl}; use std::process::Command; struct Pic { @@ -74,7 +74,7 @@ fn encode(cfg: EncoderConfig, pics: &[Pic]) -> Run { let mut enc = Encoder::new(cfg).unwrap(); let mut run = Run { packets: vec![], keys: vec![], recons: vec![], hvcc: enc.hvcc(), sets: enc.parameter_sets(), psnr_y: vec![] }; for p in pics { - let pkt = enc.encode(&p.frame()); + let pkt = enc.encode(&p.frame()).expect("encode"); let rec = enc.last_reconstruction().unwrap().clone(); run.psnr_y.push(psnr(&p.y, &rec[0], bd)); run.packets.push(pkt.data); @@ -393,7 +393,7 @@ fn hvcc_layout() { } /// Speed / size / quality report on ffmpeg-generated 1080p test content: -/// `cargo test -p effectcraft-hevcenc --test roundtrip bench_1080p -- --ignored --nocapture` +/// `cargo test -p effectcraft-export --test it hevc_encoder::bench_1080p -- --ignored --nocapture` /// (`HEVCENC_BENCH_SRC` selects another lavfi source). #[test] #[ignore] @@ -424,7 +424,7 @@ fn bench_1080p() { let mut enc = Encoder::new(cfg(w as u32, h as u32, qp, 30, Profile::Main)).unwrap(); for (i, p) in pics.iter().enumerate() { let t = std::time::Instant::now(); - let pkt = enc.encode(&p.frame()); + let pkt = enc.encode(&p.frame()).expect("encode"); let dt = t.elapsed(); let rec = enc.last_reconstruction().unwrap(); eprintln!( diff --git a/crates/export/tests/it/main.rs b/crates/export/tests/it/main.rs index 07ee96c05..77dc22b49 100644 --- a/crates/export/tests/it/main.rs +++ b/crates/export/tests/it/main.rs @@ -1,10 +1,12 @@ //! The crate's integration tests, one binary (each module is a former `tests/.rs`). mod audio_sample_positions; +mod av1_encoder; mod banding; mod exr_scene_linear; mod h264_gpu_yuv; mod hevc_av1_roundtrip; +mod hevc_encoder; #[cfg(not(target_arch = "wasm32"))] mod mov_audio_sample_positions; mod movie_bytes; diff --git a/crates/export/tests/it/movie_bytes.rs b/crates/export/tests/it/movie_bytes.rs index 76b89d1c7..00e19b396 100644 --- a/crates/export/tests/it/movie_bytes.rs +++ b/crates/export/tests/it/movie_bytes.rs @@ -97,7 +97,11 @@ fn h264_movie_bytes_are_pinned() { // FilmCraft 09aaf9b's encoder lists its settings in the first frame's user-data SEI (211 // bytes where 0.1.1's name took 50): 161 bytes more than the FilmCraft 2c5ba7a pin. The encoder's // version string "0.8.0" -> "0.12.0" in that SEI adds one more byte (and one to the size fields). - assert_eq!((data.len(), fnv1a(&data)), (104_342, 17_327_612_315_319_115_693), "H.264 MP4"); + // craft-video's encoder (craft-libs) is that encoder at a later FilmCraft revision than the + // d5ead4b pin: it names itself "craft-h264enc 0.1.0" in the SEI (5 bytes fewer), and with these + // settings its packets match the pin's for the first ten frames and differ after (24 bytes more + // in all, with the size fields). + assert_eq!((data.len(), fnv1a(&data)), (104_366, 4_413_091_804_733_884_658), "H.264 MP4"); } #[test] diff --git a/crates/export/tests/it/movie_color.rs b/crates/export/tests/it/movie_color.rs index 1a5371d91..e93384093 100644 --- a/crates/export/tests/it/movie_color.rs +++ b/crates/export/tests/it/movie_color.rs @@ -6,13 +6,13 @@ use std::path::Path; use std::process::Command; +use craft_media::isobmff::{ColorInfo, TrackKind}; use effectcraft_color::Label; use effectcraft_export::render_queue::{OutputFormat, OutputModule, RenderSettings, TimeSpan}; use effectcraft_export::{Job, export}; use effectcraft_project::{ColorSpace, Comp, ItemKind, LayerSource, Project, Solid, build}; use effectcraft_render::NoFootage; use effectcraft_time::{FrameRate, Tick}; -use filmcraft_isobmff::{ColorInfo, TrackKind}; /// (name, Output color space, H.273 primaries / transfer / matrix, the same as ffprobe names them) const ROWS: &[(&str, Option, [u16; 3], [&str; 3])] = &[ @@ -81,7 +81,7 @@ fn mp4_tags_describe_the_frames() { render(output, format, &path); let bytes = std::fs::read(&path).expect("read movie"); - let mp4 = filmcraft_isobmff::open(bytes.as_slice()).expect("demux"); + let mp4 = craft_media::isobmff::open(bytes.as_slice()).expect("demux"); let track = mp4.tracks.iter().find(|t| t.kind == TrackKind::Video).expect("video track"); let colr = track.entries[0].video.as_ref().and_then(|v| v.color.clone()); assert_eq!(colr, Some(ColorInfo::Nclx { primaries, transfer, matrix, full_range: false }), "{codec} {name}: colr"); diff --git a/crates/export/tests/it/ms_rounded_footage.rs b/crates/export/tests/it/ms_rounded_footage.rs index 3d6880232..2be6c3cd8 100644 --- a/crates/export/tests/it/ms_rounded_footage.rs +++ b/crates/export/tests/it/ms_rounded_footage.rs @@ -9,6 +9,7 @@ use std::path::{Path, PathBuf}; use std::sync::Arc; +use craft_media::isobmff::{AvcConfig, Brand, Mp4Writer, SampleEntry, TrackConfig, WriteSample, WriterOptions}; use effectcraft_color::Label; use effectcraft_export::render_queue::{OutputFormat, OutputModule, RenderSettings, TimeSpan}; use effectcraft_export::{Job, export}; @@ -16,7 +17,6 @@ use effectcraft_media::MediaPool; use effectcraft_project::{Comp, Footage, ItemKind, LayerSource, Project, build}; use effectcraft_raster::Image; use effectcraft_time::{FrameRate, Tick}; -use filmcraft_isobmff::{AvcConfig, Brand, Mp4Writer, SampleEntry, TrackConfig, WriteSample, WriterOptions}; const W: u32 = 128; const H: u32 = 32; @@ -38,18 +38,18 @@ fn ms_rounded(rate: FrameRate, n: i64) -> Vec { /// An H.264 MP4 (video track timescale 1000) with one frame per entry of `durations` (ms); /// frame k shows k in binary. fn write_movie(path: &Path, durations: &[u32]) { - let mut cfg = filmcraft_h264enc::EncoderConfig::new(W, H, 30, 1); - cfg.format = filmcraft_h264enc::PacketFormat::LengthPrefixed; + let mut cfg = craft_video::h264::EncoderConfig::new(W, H, 30, 1); + cfg.format = craft_video::h264::PacketFormat::LengthPrefixed; cfg.aud = false; cfg.bframes = 0; cfg.keyint = 30; cfg.scenecut = false; - let mut enc = filmcraft_h264enc::Encoder::new(cfg).expect("encoder"); + let mut enc = craft_video::h264::Encoder::new(cfg).expect("encoder"); let (u, v) = (vec![128u8; (W * H / 4) as usize], vec![128u8; (W * H / 4) as usize]); let mut packets = Vec::new(); for k in 0..durations.len() { let y: Vec = (0..W * H).map(|i| if (k >> (i % W * BARS / W)) & 1 == 1 { 235 } else { 16 }).collect(); - let frame = filmcraft_h264enc::YuvFrame { y: &y, u: &u, v: &v, y_stride: W as usize, uv_stride: W as usize / 2 }; + let frame = craft_video::h264::YuvFrame { y: &y, u: &u, v: &v, y_stride: W as usize, uv_stride: W as usize / 2 }; packets.extend(enc.try_encode(&frame, k as i64).expect("encode")); } packets.extend(enc.flush()); diff --git a/crates/export/tests/it/opus_roundtrip.rs b/crates/export/tests/it/opus_roundtrip.rs index 4dfbc32ff..c333946d8 100644 --- a/crates/export/tests/it/opus_roundtrip.rs +++ b/crates/export/tests/it/opus_roundtrip.rs @@ -20,9 +20,9 @@ fn encode(pcm: &[f32], channels: u8, bitrate: u32) -> (OpusEncoder, Vec> (enc, packets) } -/// Decodes with filmcraft-opus, drops the pre-skip and returns planar channels of `len` samples. +/// Decodes with craft-audio, drops the pre-skip and returns planar channels of `len` samples. fn decode(head: &[u8], packets: &[Vec], len: usize) -> Vec> { - let mut dec = filmcraft_opus::Decoder::new(head).expect("OpusHead"); + let mut dec = craft_audio::opus::Decoder::new(head).expect("OpusHead"); dec.set_trim_pre_skip(true); let mut out: Vec> = vec![Vec::new(); dec.channels()]; for p in packets { diff --git a/crates/export/tests/it/prores_color.rs b/crates/export/tests/it/prores_color.rs index a54a17293..b2033c015 100644 --- a/crates/export/tests/it/prores_color.rs +++ b/crates/export/tests/it/prores_color.rs @@ -5,6 +5,7 @@ use std::path::Path; use std::process::Command; +use craft_media::isobmff::{ColorInfo, TrackKind}; use effectcraft_color::Label; use effectcraft_export::render_queue::{Channels, OutputFormat, OutputModule, RenderSettings, TimeSpan, sequence_path}; use effectcraft_export::{Job, export}; @@ -12,7 +13,6 @@ use effectcraft_media::MediaPool; use effectcraft_project::{ColorSpace, Comp, ItemKind, LayerSource, Project, Solid, build}; use effectcraft_render::{FootageSource, NoFootage}; use effectcraft_time::{FrameRate, Tick}; -use filmcraft_isobmff::{ColorInfo, TrackKind}; struct Row { name: &'static str, @@ -114,7 +114,7 @@ fn prores_colr_describes_the_frames() { render(row, &path); let bytes = std::fs::read(&path).expect("read movie"); - let mov = filmcraft_isobmff::open(bytes.as_slice()).expect("demux"); + let mov = craft_media::isobmff::open(bytes.as_slice()).expect("demux"); let track = mov.tracks.iter().find(|t| t.kind == TrackKind::Video).expect("video track"); let [primaries, transfer, matrix] = row.codes; let colr = track.entries[0].video.as_ref().and_then(|v| v.color.clone()); diff --git a/crates/export/tests/it/roundtrip.rs b/crates/export/tests/it/roundtrip.rs index c8634fa21..bcf7b9d90 100644 --- a/crates/export/tests/it/roundtrip.rs +++ b/crates/export/tests/it/roundtrip.rs @@ -288,15 +288,15 @@ fn prores_alpha_final_run_is_present_in_exported_movies() { run_frames(&project, comp, &MediaPool::new(), &output, &path, 1); let bytes = std::fs::read(&path).expect("exported MOV"); - let movie = filmcraft_isobmff::open(bytes.as_slice()).expect("demux exported MOV"); - let video = movie.tracks.iter().find(|track| track.kind == filmcraft_isobmff::TrackKind::Video).expect("video track"); + let movie = craft_media::isobmff::open(bytes.as_slice()).expect("demux exported MOV"); + let video = movie.tracks.iter().find(|track| track.kind == craft_media::isobmff::TrackKind::Video).expect("video track"); assert_eq!(video.samples.len(), 1); let sample = &video.samples[0]; let frame = &bytes[sample.offset as usize..][..sample.size as usize]; - let (header, start, size) = filmcraft_prores::FrameHeader::parse(frame).expect("ProRes header"); + let (header, start, size) = craft_video::prores::FrameHeader::parse(frame).expect("ProRes header"); assert_eq!((header.width, header.height), (16, 16)); let picture = &frame[start..size]; - let header = filmcraft_prores::PictureHeader::parse(picture).expect("picture header"); + let header = craft_video::prores::PictureHeader::parse(picture).expect("picture header"); assert_eq!(header.num_slices, 1); let slice = &picture[header.header_size + 2..]; let color_bytes: usize = slice[2..8].as_chunks::<2>().0.iter().map(|b| u16::from_be_bytes(*b) as usize).sum(); diff --git a/crates/export/tests/it/uncompressed_mov.rs b/crates/export/tests/it/uncompressed_mov.rs index 1f7a66576..200d3116a 100644 --- a/crates/export/tests/it/uncompressed_mov.rs +++ b/crates/export/tests/it/uncompressed_mov.rs @@ -8,6 +8,7 @@ use std::path::{Path, PathBuf}; use std::process::Command; +use craft_media::isobmff::TrackKind; use effectcraft_color::Label; use effectcraft_export::render_queue::{AudioFormat, AudioOutput, Channels, OutputFormat, OutputModule, RenderSettings, TimeSpan, sequence_path}; use effectcraft_export::{Job, export}; @@ -15,7 +16,6 @@ use effectcraft_media::MediaPool; use effectcraft_project::{Comp, ItemId, ItemKind, LayerSource, Project, Solid, build}; use effectcraft_render::FootageSource; use effectcraft_time::{FrameRate, Tick}; -use filmcraft_isobmff::TrackKind; const W: u32 = 33; const H: u32 = 20; @@ -103,7 +103,7 @@ fn reference_frames(p: &Project, cid: ItemId, footage: &dyn FootageSource, om: & /// The frames as stored in the movie, unpacked to RGBA (rows padded to even bytes). fn stored_frames(path: &Path, alpha: bool) -> Vec> { let bytes = std::fs::read(path).expect("read movie"); - let mov = filmcraft_isobmff::open(bytes.as_slice()).expect("demux"); + let mov = craft_media::isobmff::open(bytes.as_slice()).expect("demux"); assert!(mov.is_quicktime); let t = mov.tracks.iter().find(|t| t.kind == TrackKind::Video).expect("video track"); let e = &t.entries[0]; @@ -204,7 +204,7 @@ fn uncompressed_with_pcm_audio() { let path = d.join("comp.mov"); assert!(run(&p, cid, &pool, &om, &path).audio); let bytes = std::fs::read(&path).expect("read"); - let mov = filmcraft_isobmff::open(bytes.as_slice()).expect("demux"); + let mov = craft_media::isobmff::open(bytes.as_slice()).expect("demux"); let a = mov.tracks.iter().find(|t| t.kind == TrackKind::Audio).expect("audio track"); assert_eq!(a.timescale, 48_000); // 0.6 s of audio at 48 kHz. diff --git a/crates/export/tests/it/vp9_roundtrip.rs b/crates/export/tests/it/vp9_roundtrip.rs index c1c6c142c..f9d36770d 100644 --- a/crates/export/tests/it/vp9_roundtrip.rs +++ b/crates/export/tests/it/vp9_roundtrip.rs @@ -1,7 +1,7 @@ -//! The VP9 intra encoder (`effectcraft-vp9enc`) round-trips through FilmCraft's VP9 decoder (and +//! The VP9 intra encoder (`craft_video::vp9`) round-trips through FilmCraft's VP9 decoder (and //! ffmpeg as an external oracle when installed): sizes, lossless exactness, PSNR. -use effectcraft_vp9enc::{EncoderConfig, Vp9Encoder}; +use craft_video::vp9::{EncoderConfig, Vp9Encoder}; use filmcraft_vp9::{Decoder, Plane}; fn plane_u8(p: &Plane) -> Vec { diff --git a/crates/hevcenc/Cargo.toml b/crates/hevcenc/Cargo.toml deleted file mode 100644 index 136611ff2..000000000 --- a/crates/hevcenc/Cargo.toml +++ /dev/null @@ -1,15 +0,0 @@ -[package] -name = "effectcraft-hevcenc" -description = "Clean-room pure-Rust HEVC (H.265) encoder (Main / Main 10, I and P slices, CABAC) for MP4 export, from ITU-T H.265" -version.workspace = true -edition.workspace = true -license.workspace = true -rust-version.workspace = true - -[lints] -workspace = true - -[dependencies] - -[dev-dependencies] -filmcraft-hevc = { workspace = true } diff --git a/crates/hevcenc/README.md b/crates/hevcenc/README.md deleted file mode 100644 index 5c4e5debe..000000000 --- a/crates/hevcenc/README.md +++ /dev/null @@ -1,88 +0,0 @@ -# effectcraft-hevcenc - -Clean-room, pure-Rust HEVC / H.265 encoder for EffectCraft's MP4 export (layer L0, `std` only, -no `unsafe`, builds for `wasm32-unknown-unknown`). - -## What it produces - -- **Profiles:** Main (8-bit) and Main 10 (10-bit), 4:2:0, Main tier. `general_level_idc` is taken - from the config or chosen as the lowest level whose MaxLumaPs, MaxLumaSr (and, in bitrate mode, - MaxBR) fit (Table A.8). -- **Parameter sets:** VPS, SPS (with VUI: BT.709 colour description, `video_full_range_flag`, - timing info from the frame rate) and PPS, as raw NAL units (`Encoder::parameter_sets`) and as an - `hvcC` record (ISO/IEC 14496-15 §8.3.3.1, `lengthSizeMinusOne` 3, complete VPS / SPS / PPS - arrays, `Encoder::hvcc`). -- **Pictures:** one slice per picture; `IDR_N_LP` every `keyint` pictures (`1` = all intra, `0` = - only the first), `TRAIL_R` P pictures otherwise, each referencing the previous picture (one - short-term RPS in the SPS, `num_ref_idx_l0_active` 1, no reordering, pts = dts). Packets are - length-prefixed (4-byte big-endian) NAL units without parameter sets. -- **Picture size:** any even width / height; the coded size is padded to a multiple of 8 by edge - replication and cropped with the conformance window. Odd sizes are rejected with - `Error::InvalidConfig` (4:2:0 conformance-window offsets are in units of two luma samples). - -## Coding tools - -- 64x64 CTUs, coding units 64x64 .. 8x8 chosen by rate-distortion optimisation: every candidate is - actually predicted, transformed, quantised and reconstructed, and its rate is measured with a - CABAC bit estimator that evolves the same context states as the real coder. Split decisions - compare the best unsplit candidate with the recursively coded children (early exit after a skip). -- **Intra:** all 35 modes (SATD search on a coarse grid plus refinement and the MPMs), 2Nx2N at - 8..32 and NxN (four 4x4 PUs) at 8x8, chroma in DM mode, MPM / remaining-mode coding, reference - substitution, [1 2 1] and strong (32x32) smoothing, DC / angular boundary filters. -- **Inter (P):** skip and merge (spatial candidates + zero candidates, `MaxNumMergeCand` 5) and AMVP - with mvd coding; integer motion search (predictor starts + square pattern, steps 16..1) and - half / quarter-sample refinement on pre-interpolated half-sample planes; the coded prediction uses - the normative 8-tap / 4-tap interpolation filters and reference-picture edge clamping. -- **Transforms:** DST-VII 4x4 (intra luma) and DCT 4..32 (inverse by even/odd decomposition, - bit-identical to the matrix form), dead-zone quantisation (1/3 intra, 1/6 inter), flat scaling, - sign data hiding, inferred transform splits only (`max_transform_hierarchy_depth_*` 0, so 64x64 - CUs use four 32x32 TUs). -- **Entropy coding:** CABAC (context initialisation by slice QP and init type, decision / bypass / - terminate bins, flush), `end_of_slice_segment_flag`, byte alignment, emulation prevention. -- **In-loop filters:** the deblocking filter is on (the encoder deblocks its own reconstruction, so - references match the decoder); SAO is disabled. -- **Rate control:** `ConstantQp(qp)` or `Bitrate { kbps }`, a per-picture QP from a bits-x-Qstep - model per picture type (I pictures get roughly 4x the bits of a P picture) with a buffer that - spreads over- and undershoot over the following pictures. - -Not implemented: B pictures / reordering, multiple references, TMVP, AMP and other inter partition -shapes, residual quadtree splits, transform skip, scaling lists, PCM, SAO, RDOQ, cu_qp_delta / -adaptive quantisation, tiles / WPP / multiple slices, range extensions (4:2:2, 4:4:4, > 10 bits). - -## Measurements - -On a shared, heavily loaded machine, opt-level 3: about 0.45 s of CPU per 1920x1080 picture -(ffmpeg `testsrc2`, QP 22 / 32). On ffmpeg's `mandelbrot` 1080p source (8 pictures, IPPP, QP 32) -the stream is about 12 % larger at about 0.2 dB lower mean PSNR-Y than x265 `--preset medium` at -the same QP without psy tuning (110.5 kB / 42.3 dB vs 98.3 kB / 42.5 dB). The synthetic test image -reaches 45.8 dB PSNR-Y at QP 22. - -## Tests - -`cargo test -p effectcraft-hevcenc`: - -- every stream is decoded with the first-party `filmcraft-hevc` decoder and compared bit-exactly - with the encoder's reconstruction (`Encoder::last_reconstruction`, hidden API); -- when `ffmpeg` is on `PATH`, the Annex-B elementary stream is decoded with - `ffmpeg -v error -xerror` (no stderr output allowed) and compared bit-exactly as well (skipped - otherwise). ffmpeg is used only as an external test oracle; -- covered: 8-bit and 10-bit, all-intra and IP, QP 0 noise (escape codes), QP 0..51, strong - deblocking at high QP, sizes 8x8 .. 200x136 with cropping, POC wrap (300 pictures without IDR), - bitrate mode within 2x of the target, `hvcC` layout; -- `bench_1080p` (ignored) reports speed, size and PSNR on 1080p content generated by ffmpeg. - -## Specifications - -- ITU-T H.265 (V8, 08/2021) | ISO/IEC 23008-2, *High efficiency video coding*: 7 (syntax and - semantics), 8.4 - 8.7 (intra / inter prediction, scaling and transforms, deblocking), 9.3 (CABAC, - including the informative encoder description of 9.3.4), Annex A (profiles, tiers and levels), - Annex E (VUI). -- ISO/IEC 14496-15 (2019), §8.3.3.1 HEVCDecoderConfigurationRecord. - -Implemented from the specification text. Constant tables (CABAC context layout and initValues, -rangeTabLPS / transIdx, scan orders, the DCT / DST matrices, intra angles, interpolation filters, -the chroma QP table, the deblocking beta / tC tables) and the decoder-side reconstruction -arithmetic (intra reference substitution / filtering / prediction, fractional interpolation, -deblocking) are mirrored from FilmCraft's first-party, spec-derived `filmcraft-hevc` decoder (same -organisation, MIT OR Apache-2.0), so that encoder and decoder reconstruct identically. No -third-party encoder or decoder source (x265, libde265, ffmpeg, HM, ...) was consulted. diff --git a/crates/hevcenc/src/bitstream.rs b/crates/hevcenc/src/bitstream.rs deleted file mode 100644 index 27ed3063e..000000000 --- a/crates/hevcenc/src/bitstream.rs +++ /dev/null @@ -1,135 +0,0 @@ -//! RBSP bit writer (7.2, 9.2 Exp-Golomb) and NAL unit encapsulation with emulation prevention (7.3.1). - -/// MSB-first bit writer. -#[derive(Default)] -pub struct BitWriter { - buf: Vec, - acc: u64, - nbits: u32, -} - -impl BitWriter { - pub fn new() -> Self { - Self::default() - } - - /// Write the `n` (<= 32) low bits of `v`. - #[inline] - pub fn put(&mut self, v: u32, n: u32) { - debug_assert!(n <= 32); - if n == 0 { - return; - } - let v = (v as u64) & ((1u64 << n) - 1); - self.acc = (self.acc << n) | v; - self.nbits += n; - while self.nbits >= 8 { - self.nbits -= 8; - self.buf.push((self.acc >> self.nbits) as u8); - } - self.acc &= (1u64 << self.nbits) - 1; - } - - #[inline] - pub fn bit(&mut self, b: u32) { - self.put(b & 1, 1); - } - - pub fn flag(&mut self, b: bool) { - self.put(b as u32, 1); - } - - /// ue(v). - pub fn ue(&mut self, v: u32) { - let x = v as u64 + 1; - let len = 64 - x.leading_zeros(); // bits of x - // len - 1 leading zeros, then x in len bits - self.put(0, len - 1); - if len > 32 { - self.put((x >> 32) as u32, len - 32); - self.put(x as u32, 32); - } else { - self.put(x as u32, len); - } - } - - /// se(v). - pub fn se(&mut self, v: i32) { - let k = if v > 0 { 2 * v as u32 - 1 } else { (-(v as i64) as u32) * 2 }; - self.ue(k); - } - - pub fn is_aligned(&self) -> bool { - self.nbits == 0 - } - - /// rbsp_trailing_bits(): stop bit and zero alignment. - pub fn trailing(&mut self) { - self.bit(1); - self.align_zero(); - } - - pub fn align_zero(&mut self) { - if self.nbits > 0 { - self.put(0, 8 - self.nbits); - } - } - - pub fn into_bytes(mut self) -> Vec { - self.align_zero(); - self.buf - } -} - -/// Build a NAL unit (2-byte header, `nuh_layer_id` 0, `TemporalId` 0) from an RBSP, inserting -/// emulation prevention bytes. -pub fn nal_unit(nal_type: u8, rbsp: &[u8]) -> Vec { - let mut out = Vec::with_capacity(rbsp.len() + rbsp.len() / 64 + 4); - out.push(nal_type << 1); - out.push(1); - let mut zeros = 0; - for &b in rbsp { - if zeros >= 2 && b <= 3 { - out.push(3); - zeros = 0; - } - out.push(b); - if b == 0 { - zeros += 1; - } else { - zeros = 0; - } - } - // cabac_zero_words / trailing zero bytes never occur: every RBSP ends with the stop bit. - out -} - -pub const NAL_TRAIL_R: u8 = 1; -pub const NAL_IDR_N_LP: u8 = 20; -pub const NAL_VPS: u8 = 32; -pub const NAL_SPS: u8 = 33; -pub const NAL_PPS: u8 = 34; - -#[cfg(test)] -mod tests { - use super::*; - - #[test] - fn exp_golomb() { - let mut w = BitWriter::new(); - w.ue(0); // 1 - w.ue(1); // 010 - w.ue(2); // 011 - w.ue(3); // 00100 - w.se(-1); // ue(2) = 011 - w.trailing(); - // 1 010 011 00100 011 + stop bit: 1010 0110 0100 0111 - assert_eq!(w.into_bytes(), vec![0xa6, 0x47]); - } - - #[test] - fn emulation_prevention() { - let n = nal_unit(1, &[0, 0, 1, 0, 0, 0, 0, 0, 4]); - assert_eq!(&n[2..], &[0, 0, 3, 1, 0, 0, 3, 0, 0, 3, 0, 4]); - } -} diff --git a/crates/hevcenc/src/cabac.rs b/crates/hevcenc/src/cabac.rs deleted file mode 100644 index 220b999cd..000000000 --- a/crates/hevcenc/src/cabac.rs +++ /dev/null @@ -1,220 +0,0 @@ -//! CABAC: context initialisation (9.3.2.2), the arithmetic encoder (9.3.4.x, informative encoder -//! description: EncodeDecision, EncodeBypass, EncodeTerminate, EncodeFlush) and a fractional-bit -//! estimator with the same context-state evolution, used for rate-distortion decisions. - -use crate::bitstream::BitWriter; -use crate::tables::{CABAC_INIT, NEXT_STATE, NUM_CTX, RANGE_TAB_LPS}; -use std::sync::OnceLock; - -/// Context states, each `pStateIdx << 1 | valMps`. -pub type Contexts = [u8; NUM_CTX]; - -/// Initialise all context variables for `slice_qp` and `init_type` (9.3.2.2). -pub fn init_contexts(slice_qp: i32, init_type: usize) -> Contexts { - let mut ctx = [0u8; NUM_CTX]; - let qp = slice_qp.clamp(0, 51); - for (c, &v) in ctx.iter_mut().zip(CABAC_INIT[init_type].iter()) { - let slope = (v >> 4) as i32; - let offset = (v & 15) as i32; - let m = slope * 5 - 45; - let n = (offset << 3) - 16; - let pre = (((m * qp) >> 4) + n).clamp(1, 126); - *c = if pre <= 63 { ((63 - pre) << 1) as u8 } else { (((pre - 64) << 1) | 1) as u8 }; - } - ctx -} - -/// Destination of binarised syntax elements: the real arithmetic coder or the bit estimator. -pub trait Sink { - fn decision(&mut self, ctx: usize, bin: u32); - fn bypass(&mut self, bin: u32); - /// `n` bypass bins carrying the low `n` bits of `v`, MSB first. - fn bypass_bits(&mut self, v: u32, n: u32) { - for i in (0..n).rev() { - self.bypass((v >> i) & 1); - } - } - fn terminate(&mut self, bin: u32); -} - -/// Arithmetic encoder writing into a bit writer. -pub struct CabacEncoder { - pub ctx: Contexts, - low: u32, - range: u32, - outstanding: u32, - first: bool, - pub w: BitWriter, -} - -impl CabacEncoder { - /// Start encoding slice data into `w` (which must be byte aligned). - pub fn new(w: BitWriter, ctx: Contexts) -> Self { - debug_assert!(w.is_aligned()); - CabacEncoder { ctx, low: 0, range: 510, outstanding: 0, first: true, w } - } - - #[inline] - fn put_bit(&mut self, b: u32) { - if self.first { - self.first = false; - } else { - self.w.bit(b); - } - while self.outstanding > 0 { - self.w.bit(1 - b); - self.outstanding -= 1; - } - } - - #[inline] - fn renorm(&mut self) { - while self.range < 256 { - if self.low < 256 { - self.put_bit(0); - } else if self.low >= 512 { - self.low -= 512; - self.put_bit(1); - } else { - self.low -= 256; - self.outstanding += 1; - } - self.range <<= 1; - self.low <<= 1; - } - } - - /// EncodeFlush after a terminating bin equal to 1; the final bit written is the - /// rbsp_stop_one_bit. Returns the writer (byte aligned with zero bits). - pub fn finish(mut self) -> BitWriter { - self.range = 2; - self.renorm(); - let b = (self.low >> 9) & 1; - self.put_bit(b); - self.w.put(((self.low >> 7) & 3) | 1, 2); - self.w.align_zero(); - self.w - } -} - -impl Sink for CabacEncoder { - #[inline] - fn decision(&mut self, ctx_idx: usize, bin: u32) { - let s = self.ctx[ctx_idx] as usize; - let mps = (s & 1) as u32; - let q = ((self.range >> 6) & 3) as usize; - let lps = RANGE_TAB_LPS[s >> 1][q] as u32; - self.range -= lps; - if bin != mps { - self.low += self.range; - self.range = lps; - self.ctx[ctx_idx] = NEXT_STATE[s][1]; - } else { - self.ctx[ctx_idx] = NEXT_STATE[s][0]; - } - self.renorm(); - } - - #[inline] - fn bypass(&mut self, bin: u32) { - self.low <<= 1; - if bin != 0 { - self.low += self.range; - } - if self.low >= 1024 { - self.put_bit(1); - self.low -= 1024; - } else if self.low < 512 { - self.put_bit(0); - } else { - self.low -= 512; - self.outstanding += 1; - } - } - - fn terminate(&mut self, bin: u32) { - self.range -= 2; - if bin != 0 { - self.low += self.range; - // the caller flushes with finish() - } else { - self.renorm(); - } - } -} - -/// Fractional bit cost (1/32768 bit units) of coding the MPS / LPS in each state. -fn cost_table() -> &'static [[u32; 2]; 64] { - static T: OnceLock<[[u32; 2]; 64]> = OnceLock::new(); - T.get_or_init(|| { - let mut t = [[0u32; 2]; 64]; - // pLPS(s) = 0.5 * alpha^s with alpha = (0.01875 / 0.5)^(1/63) (9.3.4.3 design) - let alpha = (0.01875f64 / 0.5).powf(1.0 / 63.0); - for (s, e) in t.iter_mut().enumerate() { - let p = 0.5 * alpha.powi(s as i32); - e[0] = (-(1.0 - p).log2() * 32768.0).round() as u32; - e[1] = (-p.log2() * 32768.0).round() as u32; - } - t - }) -} - -pub const BIT: u64 = 32768; - -/// Bit estimator: tracks context states like the encoder and accumulates the ideal code length. -#[derive(Clone)] -pub struct Estimator { - pub ctx: Contexts, - /// Accumulated cost in 1/32768 bits. - pub bits: u64, - table: &'static [[u32; 2]; 64], -} - -impl Estimator { - pub fn new(ctx: Contexts) -> Self { - Estimator { ctx, bits: 0, table: cost_table() } - } -} - -impl Sink for Estimator { - #[inline] - fn decision(&mut self, ctx_idx: usize, bin: u32) { - let s = self.ctx[ctx_idx] as usize; - let lps = (bin != (s & 1) as u32) as usize; - self.bits += self.table[s >> 1][lps] as u64; - self.ctx[ctx_idx] = NEXT_STATE[s][lps]; - } - #[inline] - fn bypass(&mut self, _bin: u32) { - self.bits += BIT; - } - #[inline] - fn bypass_bits(&mut self, _v: u32, n: u32) { - self.bits += BIT * n as u64; - } - fn terminate(&mut self, _bin: u32) { - self.bits += 7; // ~0 bits for a 0 bin - } -} - -#[cfg(test)] -mod tests { - use super::*; - - #[test] - fn context_init_formula() { - let ctx = init_contexts(30, 0); - // initValue 154: m = 0, n = 64 -> pStateIdx 0, valMps 1 - assert_eq!(ctx[crate::tables::CU_TRANSQUANT_BYPASS], 1); - // split_cu_flag ctx 0 (initValue 139) at QP 30: preCtxState 62 - assert_eq!(ctx[crate::tables::SPLIT_CU], (63 - 62) << 1); - } - - #[test] - fn costs_are_sane() { - let t = cost_table(); - assert_eq!(t[0][0], 32768); - assert_eq!(t[0][1], 32768); - assert!(t[62][0] < 1000 && t[62][1] > 5 * 32768); - } -} diff --git a/crates/hevcenc/src/deblock.rs b/crates/hevcenc/src/deblock.rs deleted file mode 100644 index 6d326e5d4..000000000 --- a/crates/hevcenc/src/deblock.rs +++ /dev/null @@ -1,148 +0,0 @@ -//! Deblocking filter (8.7.2) over a whole reconstructed picture, mirrored from the filmcraft-hevc -//! decoder: all vertical edges on the 8x8 grid first, then all horizontal edges. One slice, one QP, -//! uni-directional prediction from a single reference picture, offsets 0. - -use crate::frame::{Blk, E_EDGE_H, E_EDGE_V, F_CBF, F_INTRA}; -use crate::tables::{BETA_TABLE, TC_TABLE, qpc_420}; - -#[inline] -fn tc_beta(q: i32, bs: i32, bd: u32) -> (i32, i32) { - let qb = q.clamp(0, 51); - let qt = (q + 2 * (bs - 1)).clamp(0, 53); - ((TC_TABLE[qt as usize] as i32) << (bd - 8), (BETA_TABLE[qb as usize] as i32) << (bd - 8)) -} - -/// Boundary strength (8.7.2.4) between 4x4 blocks p and q on a transform / prediction edge. -fn boundary_strength(p: &Blk, q: &Blk) -> i32 { - if (p.flags | q.flags) & F_INTRA != 0 { - return 2; - } - if (p.flags | q.flags) & F_CBF != 0 { - return 1; - } - let far = (p.mv[0] as i32 - q.mv[0] as i32).abs() >= 4 || (p.mv[1] as i32 - q.mv[1] as i32).abs() >= 4; - far as i32 -} - -/// Deblock the planes in place. `qp` is the slice QpY (without QpBdOffset). -pub fn deblock(planes: &mut [Vec; 3], blk: &[Blk], w: usize, h: usize, qp: i32, bd: u32) { - let (w4, h4) = (w / 4, h / 4); - let cw = w / 2; - // chroma tc (bS 2 only; QpC from the averaged luma QP, no offsets) - let qt = (qpc_420(qp) + 2).clamp(0, 53); - let tc_c = (TC_TABLE[qt as usize] as i32) << (bd - 8); - // vertical edges - for by in 0..h4 { - for bx in (2..w4).step_by(2) { - let qi = by * w4 + bx; - if blk[qi].flags & E_EDGE_V == 0 { - continue; - } - let bs = boundary_strength(&blk[qi - 1], &blk[qi]); - if bs == 0 { - continue; - } - let (x, y) = (bx * 4, by * 4); - let (tc, beta) = tc_beta(qp, bs, bd); - filter_luma(&mut planes[0], y * w + x, 1, w, tc, beta, bd); - // chroma: 8x8 chroma grid (16 luma samples), one 4-sample chroma segment per 8 luma rows - if bs == 2 && bx % 4 == 0 && by % 2 == 0 { - for c in 1..3 { - filter_chroma(&mut planes[c], (y / 2) * cw + x / 2, 1, cw, tc_c, bd); - } - } - } - } - // horizontal edges - for by in (2..h4).step_by(2) { - for bx in 0..w4 { - let qi = by * w4 + bx; - if blk[qi].flags & E_EDGE_H == 0 { - continue; - } - let bs = boundary_strength(&blk[qi - w4], &blk[qi]); - if bs == 0 { - continue; - } - let (x, y) = (bx * 4, by * 4); - let (tc, beta) = tc_beta(qp, bs, bd); - filter_luma(&mut planes[0], y * w + x, w, 1, tc, beta, bd); - if bs == 2 && by % 4 == 0 && bx % 2 == 0 { - for c in 1..3 { - filter_chroma(&mut planes[c], (y / 2) * cw + x / 2, cw, 1, tc_c, bd); - } - } - } - } -} - -/// Filter one 4-sample luma edge segment. `o` = offset of q0 of the first line, `step` = distance -/// between samples across the edge, `along` = distance between lines. -fn filter_luma(s: &mut [u16], o: usize, step: usize, along: usize, tc: i32, beta: i32, bd: u32) { - let px = |s: &[u16], k: usize, i: usize| s[o + k * along - (i + 1) * step] as i32; - let qx = |s: &[u16], k: usize, i: usize| s[o + k * along + i * step] as i32; - let dp0 = (px(s, 0, 2) - 2 * px(s, 0, 1) + px(s, 0, 0)).abs(); - let dp3 = (px(s, 3, 2) - 2 * px(s, 3, 1) + px(s, 3, 0)).abs(); - let dq0 = (qx(s, 0, 2) - 2 * qx(s, 0, 1) + qx(s, 0, 0)).abs(); - let dq3 = (qx(s, 3, 2) - 2 * qx(s, 3, 1) + qx(s, 3, 0)).abs(); - let (dpq0, dpq3) = (dp0 + dq0, dp3 + dq3); - let (dp, dq) = (dp0 + dp3, dq0 + dq3); - let d = dpq0 + dpq3; - if d >= beta { - return; - } - let dsam = |s: &[u16], k: usize, dpq: i32| -> bool { - dpq < (beta >> 2) - && (px(s, k, 3) - px(s, k, 0)).abs() + (qx(s, k, 0) - qx(s, k, 3)).abs() < (beta >> 3) - && (px(s, k, 0) - qx(s, k, 0)).abs() < ((5 * tc + 1) >> 1) - }; - let strong = dsam(s, 0, 2 * dpq0) && dsam(s, 3, 2 * dpq3); - let dep = dp < ((beta + (beta >> 1)) >> 3); - let deq = dq < ((beta + (beta >> 1)) >> 3); - let max = (1i32 << bd) - 1; - for k in 0..4 { - let base = o + k * along; - let p = |i: usize| s[base - (i + 1) * step] as i32; - let q = |i: usize| s[base + i * step] as i32; - let (p0, p1, p2, p3) = (p(0), p(1), p(2), p(3)); - let (q0, q1, q2, q3) = (q(0), q(1), q(2), q(3)); - if strong { - let t2 = 2 * tc; - s[base - step] = ((p2 + 2 * p1 + 2 * p0 + 2 * q0 + q1 + 4) >> 3).clamp(p0 - t2, p0 + t2) as u16; - s[base - 2 * step] = ((p2 + p1 + p0 + q0 + 2) >> 2).clamp(p1 - t2, p1 + t2) as u16; - s[base - 3 * step] = ((2 * p3 + 3 * p2 + p1 + p0 + q0 + 4) >> 3).clamp(p2 - t2, p2 + t2) as u16; - s[base] = ((p1 + 2 * p0 + 2 * q0 + 2 * q1 + q2 + 4) >> 3).clamp(q0 - t2, q0 + t2) as u16; - s[base + step] = ((p0 + q0 + q1 + q2 + 2) >> 2).clamp(q1 - t2, q1 + t2) as u16; - s[base + 2 * step] = ((p0 + q0 + q1 + 3 * q2 + 2 * q3 + 4) >> 3).clamp(q2 - t2, q2 + t2) as u16; - } else { - let mut delta = (9 * (q0 - p0) - 3 * (q1 - p1) + 8) >> 4; - if delta.abs() >= tc * 10 { - continue; - } - delta = delta.clamp(-tc, tc); - s[base - step] = (p0 + delta).clamp(0, max) as u16; - s[base] = (q0 - delta).clamp(0, max) as u16; - if dep { - let dp = ((((p2 + p0 + 1) >> 1) - p1 + delta) >> 1).clamp(-(tc >> 1), tc >> 1); - s[base - 2 * step] = (p1 + dp).clamp(0, max) as u16; - } - if deq { - let dq = ((((q2 + q0 + 1) >> 1) - q1 - delta) >> 1).clamp(-(tc >> 1), tc >> 1); - s[base + step] = (q1 + dq).clamp(0, max) as u16; - } - } - } -} - -/// Filter one 4-sample chroma edge segment. -fn filter_chroma(s: &mut [u16], o: usize, step: usize, along: usize, tc: i32, bd: u32) { - let max = (1i32 << bd) - 1; - for k in 0..4 { - let base = o + k * along; - let (p0, p1) = (s[base - step] as i32, s[base - 2 * step] as i32); - let (q0, q1) = (s[base] as i32, s[base + step] as i32); - let delta = ((((q0 - p0) << 2) + p1 - q1 + 4) >> 3).clamp(-tc, tc); - s[base - step] = (p0 + delta).clamp(0, max) as u16; - s[base] = (q0 - delta).clamp(0, max) as u16; - } -} diff --git a/crates/hevcenc/src/frame.rs b/crates/hevcenc/src/frame.rs deleted file mode 100644 index ee141e3e7..000000000 --- a/crates/hevcenc/src/frame.rs +++ /dev/null @@ -1,1481 +0,0 @@ -//! Picture coding: CTU loop, rate-distortion mode decisions (with a CABAC bit estimator) and the -//! slice_segment_data() syntax (7.3.8): coding quadtree, coding unit, prediction unit, transform tree -//! and residual coding. Reconstruction mirrors the decoding process exactly (no in-loop filters). - -use crate::cabac::{BIT, CabacEncoder, Estimator, Sink}; -use crate::inter::{self, PlaneRef}; -use crate::intra::{self, Refs}; -use crate::me::MeRef; -use crate::params::{LOG2_CTB, LOG2_MAX_TB, LOG2_MIN_CB, MAX_MERGE_CAND}; -use crate::tables::{ - ABS_MVD_GT0, ABS_MVD_GT1, CBF_CHROMA, CBF_LUMA, CODED_SUB_BLOCK, CTX_IDX_MAP, CU_SKIP, GT1, GT2, INTRA_CHROMA, LAST_X_PREFIX, LAST_Y_PREFIX, MERGE_FLAG, - MERGE_IDX, MVP_FLAG, PART_MODE, PRED_MODE, PREV_INTRA_LUMA, RQT_ROOT_CBF, SCAN_4, SIG_COEFF, SPLIT_CU, qpc_420, scan_order, -}; -use crate::transform; - -pub const F_INTRA: u8 = 1; -pub const F_SKIP: u8 = 2; -/// The luma transform block covering this 4x4 block has non-zero coefficients. -pub const F_CBF: u8 = 4; -/// The left / top edge of this 4x4 block is a transform (and prediction) block edge. -pub const E_EDGE_V: u8 = 8; -pub const E_EDGE_H: u8 = 16; - -/// Per 4x4 luma block state of the current picture. -#[derive(Clone, Copy, Default)] -pub struct Blk { - pub depth: u8, - pub flags: u8, - /// IntraPredModeY (DC for inter blocks). - pub ipm: u8, - /// L0 motion vector (quarter samples) of inter blocks. - pub mv: [i16; 2], -} - -#[derive(Clone, Copy, Debug, PartialEq, Eq)] -pub enum CuMode { - Intra { nxn: bool, modes: [u8; 4] }, - Skip { idx: u8 }, - Merge { idx: u8 }, - Amvp { mv: [i16; 2], mvp: u8 }, -} - -#[derive(Clone, Copy, Debug)] -struct CuDec { - x: u32, - y: u32, - log2: u32, - mode: CuMode, -} - -/// One coded transform block of the current CU. -#[derive(Clone, Copy)] -struct Tb { - c: u8, - /// Luma-coordinate region the block belongs to (for cbf derivation). - lx: u32, - ly: u32, - log2: u32, - nz: bool, - off: usize, - scan: u8, -} - -/// Saved samples and block state of a square region. -struct Snap { - x: usize, - y: usize, - n: usize, - planes: [Vec; 3], - blk: Vec, -} - -#[derive(Clone, Copy, PartialEq, Eq)] -pub enum SliceKind { - I, - P, -} - -/// Picture geometry and slice coding parameters shared by the CTU encoder. -pub struct FrameConfig { - pub bit_depth: u32, - pub width: usize, - pub height: usize, - pub kind: SliceKind, - pub slice_qp: i32, -} - -/// A square node in the coding or transform quadtree. -#[derive(Clone, Copy)] -struct TreeNode { - x: usize, - y: usize, - log2: u32, - depth: u32, -} - -impl TreeNode { - fn children(self) -> impl Iterator { - let h = 1usize << (self.log2 - 1); - [(0, 0), (h, 0), (0, h), (h, h)].into_iter().map(move |(dx, dy)| Self { x: self.x + dx, y: self.y + dy, log2: self.log2 - 1, depth: self.depth + 1 }) - } -} - -/// Transform syntax inherited from the parent node and the enclosing CU. -#[derive(Clone, Copy)] -struct TransformState { - blk_idx: u32, - parent: [bool; 2], - intra: bool, - nxn: bool, -} - -pub struct FrameCoder<'a> { - pub bd: u32, - pub w: usize, - pub h: usize, - pub cw: usize, - pub ch: usize, - w4: usize, - wctb: usize, - hctb: usize, - pub kind: SliceKind, - /// QP' (with QpBdOffset) for luma and chroma. - slice_qp: i32, - qp_y: i32, - qp_c: i32, - lambda: f64, - lambda_sad: f64, - src: &'a [Vec; 3], - pub rec: [Vec; 3], - refp: Option<&'a [Vec; 3]>, - me: Option<&'a MeRef>, - blk: Vec, - tbs: Vec, - coefs: Vec, - pred: Vec, - scratch: [Vec; 2], -} - -#[inline] -fn morton(x: u32, y: u32) -> u32 { - let mut r = 0; - for i in 0..5 { - r |= ((x >> i) & 1) << (2 * i); - r |= ((y >> i) & 1) << (2 * i + 1); - } - r -} - -/// Intra modes tried in the coarse pass of the SATD search. -const COARSE_MODES: [u32; 11] = [0, 1, 2, 6, 10, 14, 18, 22, 26, 30, 34]; - -impl<'a> FrameCoder<'a> { - pub fn new(config: FrameConfig, src: &'a [Vec; 3], refp: Option<&'a [Vec; 3]>, me: Option<&'a MeRef>) -> Self { - let FrameConfig { bit_depth: bd, width: w, height: h, kind, slice_qp } = config; - let off = 6 * (bd as i32 - 8); - let qpi = slice_qp.clamp(-off, 57); - let qp_c = qpc_420(qpi) + off; - let base = if kind == SliceKind::I { 0.57 } else { 0.62 }; - let lambda = base * 2f64.powf((slice_qp as f64 - 12.0) / 3.0) * (1u64 << (2 * (bd - 8))) as f64; - let ctb = 1usize << LOG2_CTB; - FrameCoder { - bd, - w, - h, - cw: w / 2, - ch: h / 2, - w4: w / 4, - wctb: w.div_ceil(ctb), - hctb: h.div_ceil(ctb), - kind, - slice_qp, - qp_y: slice_qp + off, - qp_c, - lambda, - lambda_sad: lambda.sqrt(), - src, - rec: [vec![0; w * h], vec![0; w * h / 4], vec![0; w * h / 4]], - refp, - me, - blk: vec![Blk::default(); (w / 4) * (h / 4)], - tbs: Vec::with_capacity(16), - coefs: Vec::with_capacity(64 * 64 * 2), - pred: vec![0; 64 * 64], - scratch: [vec![0; 32 * 32], vec![0; 32 * 32]], - } - } - - // --------------------------------------------------------------------------------------- - // neighbourhood - - /// z-scan order availability (6.4.1) of (xn, yn) for the block at (xc, yc): single slice, no tiles. - #[inline] - fn zavail(&self, xc: i32, yc: i32, xn: i32, yn: i32) -> bool { - if xn < 0 || yn < 0 || xn >= self.w as i32 || yn >= self.h as i32 { - return false; - } - let s = LOG2_CTB; - let ctb_n = (yn >> s) as usize * self.wctb + (xn >> s) as usize; - let ctb_c = (yc >> s) as usize * self.wctb + (xc >> s) as usize; - if ctb_n != ctb_c { - ctb_n < ctb_c - } else { - let m = (1 << s) - 1; - morton(((xn & m) >> 2) as u32, ((yn & m) >> 2) as u32) <= morton(((xc & m) >> 2) as u32, ((yc & m) >> 2) as u32) - } - } - - #[inline] - fn blk_at(&self, x: i32, y: i32) -> &Blk { - &self.blk[(y as usize >> 2) * self.w4 + (x as usize >> 2)] - } - - fn fill(&mut self, x: usize, y: usize, n: usize, f: impl Fn(&mut Blk)) { - for by in (y >> 2)..((y + n) >> 2) { - for b in &mut self.blk[by * self.w4 + (x >> 2)..by * self.w4 + ((x + n) >> 2)] { - f(b); - } - } - } - - /// Mark the CU's left / top edges and, for CUs larger than the maximum transform size, the - /// inner transform edges (deblocking). - fn mark_edges(&mut self, x: usize, y: usize, log2: u32) { - let n = 1usize << log2; - let t = 1usize << log2.min(LOG2_MAX_TB); - for ty in (y..y + n).step_by(t) { - for tx in (x..x + n).step_by(t) { - for by in ty / 4..(ty + t) / 4 { - self.blk[by * self.w4 + tx / 4].flags |= E_EDGE_V; - } - for b in &mut self.blk[(ty / 4) * self.w4 + tx / 4..(ty / 4) * self.w4 + (tx + t) / 4] { - b.flags |= E_EDGE_H; - } - } - } - } - - /// In-loop deblocking of the finished picture. - pub fn deblock(&mut self) { - crate::deblock::deblock(&mut self.rec, &self.blk, self.w, self.h, self.slice_qp, self.bd); - } - - fn snap(&self, x: usize, y: usize, n: usize) -> Snap { - let mut planes: [Vec; 3] = Default::default(); - for (c, p) in planes.iter_mut().enumerate() { - let (px, py, pn, stride) = if c == 0 { (x, y, n, self.w) } else { (x / 2, y / 2, n / 2, self.cw) }; - p.reserve(pn * pn); - for r in 0..pn { - p.extend_from_slice(&self.rec[c][(py + r) * stride + px..][..pn]); - } - } - let mut blk = Vec::with_capacity((n / 4) * (n / 4)); - for by in (y >> 2)..((y + n) >> 2) { - blk.extend_from_slice(&self.blk[by * self.w4 + (x >> 2)..by * self.w4 + ((x + n) >> 2)]); - } - Snap { x, y, n, planes, blk } - } - - fn restore(&mut self, s: &Snap) { - let (x, y, n) = (s.x, s.y, s.n); - for c in 0..3 { - let (px, py, pn, stride) = if c == 0 { (x, y, n, self.w) } else { (x / 2, y / 2, n / 2, self.cw) }; - for r in 0..pn { - self.rec[c][(py + r) * stride + px..][..pn].copy_from_slice(&s.planes[c][r * pn..r * pn + pn]); - } - } - let bw = n / 4; - for (i, by) in ((y >> 2)..((y + n) >> 2)).enumerate() { - self.blk[by * self.w4 + (x >> 2)..by * self.w4 + (x >> 2) + bw].copy_from_slice(&s.blk[i * bw..i * bw + bw]); - } - } - - // --------------------------------------------------------------------------------------- - // picture loop - - /// Code all CTUs of the picture into `enc` (one slice); the caller flushes the encoder. - pub fn encode(&mut self, enc: &mut CabacEncoder) { - let ctb = 1usize << LOG2_CTB; - let total = self.wctb * self.hctb; - for i in 0..total { - let (x, y) = ((i % self.wctb) * ctb, (i / self.wctb) * ctb); - let mut est = Estimator::new(enc.ctx); - let mut decs = Vec::with_capacity(64); - self.decide(x, y, LOG2_CTB, 0, &mut est, &mut decs); - let mut idx = 0; - self.write_cq(enc, TreeNode { x, y, log2: LOG2_CTB, depth: 0 }, &decs, &mut idx); - debug_assert_eq!(idx, decs.len()); - debug_assert_eq!(est.ctx, enc.ctx); - enc.terminate((i + 1 == total) as u32); // end_of_slice_segment_flag - } - } - - fn write_split(&self, s: &mut S, x: usize, y: usize, depth: u32, split: bool) { - let (x, y) = (x as i32, y as i32); - let mut inc = 0; - if self.zavail(x, y, x - 1, y) && self.blk_at(x - 1, y).depth as u32 > depth { - inc += 1; - } - if self.zavail(x, y, x, y - 1) && self.blk_at(x, y - 1).depth as u32 > depth { - inc += 1; - } - s.decision(SPLIT_CU + inc, split as u32); - } - - /// Final pass: re-code the decided quadtree with the real arithmetic coder. - fn write_cq(&mut self, enc: &mut CabacEncoder, node: TreeNode, decs: &[CuDec], idx: &mut usize) { - let TreeNode { x, y, log2, depth } = node; - if x >= self.w || y >= self.h { - return; - } - let n = 1usize << log2; - let inside = x + n <= self.w && y + n <= self.h; - let d = decs[*idx]; - let leaf = d.x as usize == x && d.y as usize == y && d.log2 == log2; - if inside && log2 > LOG2_MIN_CB { - self.write_split(enc, x, y, depth, !leaf); - } else { - debug_assert_eq!(leaf, log2 == LOG2_MIN_CB); - } - if leaf { - *idx += 1; - let (eff, _) = self.encode_cu(enc, x, y, log2, depth, d.mode); - debug_assert_eq!(eff, d.mode); - } else { - for child in node.children() { - self.write_cq(enc, child, decs, idx); - } - } - } - - #[inline] - fn cost(&self, dist: u64, bits: u64) -> f64 { - dist as f64 + self.lambda * bits as f64 / BIT as f64 - } - - /// Rate-distortion decision for the quadtree node at (x, y); leaves the chosen reconstruction - /// and block state in place, advances `est` and appends the decided CUs. Returns the RD cost. - fn decide(&mut self, x: usize, y: usize, log2: u32, depth: u32, est: &mut Estimator, decs: &mut Vec) -> f64 { - if x >= self.w || y >= self.h { - return 0.0; - } - let n = 1usize << log2; - let inside = x + n <= self.w && y + n <= self.h; - let can_split = log2 > LOG2_MIN_CB; - let base = est.bits; - let leaf_ok = inside && (self.kind == SliceKind::P || log2 <= LOG2_MAX_TB); - let mut best: Option<(f64, Estimator, Snap, CuMode)> = None; - if leaf_ok { - let mut e0 = est.clone(); - if can_split { - self.write_split(&mut e0, x, y, depth, false); - } - let try_mode = |me: &mut Self, mode: CuMode, best: &mut Option<(f64, Estimator, Snap, CuMode)>| { - let mut e = e0.clone(); - let (eff, dist) = me.encode_cu(&mut e, x, y, log2, depth, mode); - let cost = me.cost(dist, e.bits - base); - if best.as_ref().is_none_or(|b| cost < b.0) { - *best = Some((cost, e, me.snap(x, y, n), eff)); - } - }; - let mut inter_satd = u64::MAX; - if self.kind == SliceKind::P { - let merge = self.merge_list(x, y, n); - let (midx, msatd) = self.best_merge(x, y, n, &merge); - try_mode(self, CuMode::Skip { idx: midx as u8 }, &mut best); - try_mode(self, CuMode::Merge { idx: midx as u8 }, &mut best); - let amvp = self.amvp(x, y, n); - let (mv, mvp, asatd) = self.motion_search(x, y, n, &amvp, &merge); - if mv != merge[midx] { - try_mode(self, CuMode::Amvp { mv, mvp }, &mut best); - } - inter_satd = msatd.min(asatd); - } - if log2 <= LOG2_MAX_TB && !matches!(best, Some((_, _, _, CuMode::Skip { .. }))) { - let (mode, satd) = self.search_intra_mode(x, y, log2); - if satd < inter_satd { - try_mode(self, CuMode::Intra { nxn: false, modes: [mode as u8; 4] }, &mut best); - if log2 == LOG2_MIN_CB { - let modes = self.search_nxn_modes(x, y); - try_mode(self, CuMode::Intra { nxn: true, modes }, &mut best); - } - } - } - if let Some(b) = &best { - self.restore(&b.2); - } - } - let early = matches!(best, Some((_, _, _, CuMode::Skip { .. }))); - if can_split && (!leaf_ok || !early) { - let mut e = est.clone(); - if inside { - self.write_split(&mut e, x, y, depth, true); - } - let limit = best.as_ref().map_or(f64::INFINITY, |b| b.0); - let mut cost = self.cost(0, e.bits - base); - let mut d2 = Vec::new(); - let h = n / 2; - for (dx, dy) in [(0, 0), (h, 0), (0, h), (h, h)] { - cost += self.decide(x + dx, y + dy, log2 - 1, depth + 1, &mut e, &mut d2); - if cost >= limit { - break; - } - } - if cost < limit { - *est = e; - decs.extend(d2); - return cost; - } - } - // A coding unit always has at least one candidate mode (intra, or skip/merge in P - // slices); without one, report an infinite cost. - let Some((cost, e, snap, mode)) = best else { return f64::INFINITY }; - self.restore(&snap); - *est = e; - decs.push(CuDec { x: x as u32, y: y as u32, log2, mode }); - cost - } - - // --------------------------------------------------------------------------------------- - // coding unit - - fn skip_ctx(&self, x: usize, y: usize) -> usize { - let (x, y) = (x as i32, y as i32); - let mut inc = 0; - if self.zavail(x, y, x - 1, y) && self.blk_at(x - 1, y).flags & F_SKIP != 0 { - inc += 1; - } - if self.zavail(x, y, x, y - 1) && self.blk_at(x, y - 1).flags & F_SKIP != 0 { - inc += 1; - } - inc - } - - /// Predict, transform, quantise and reconstruct one CU and write its syntax into `s`. - /// Returns the effective mode (a residual-free merge becomes a skip) and the SSE. - fn encode_cu(&mut self, s: &mut S, x: usize, y: usize, log2: u32, depth: u32, mode: CuMode) -> (CuMode, u64) { - let n = 1usize << log2; - self.tbs.clear(); - self.coefs.clear(); - let p_slice = self.kind == SliceKind::P; - let skip_ctx = if p_slice { self.skip_ctx(x, y) } else { 0 }; - let d8 = depth as u8; - match mode { - CuMode::Intra { nxn, modes } => { - self.fill(x, y, n, |b| { - b.depth = d8; - b.flags = F_INTRA; - b.ipm = modes[0]; - }); - self.mark_edges(x, y, log2); - if nxn { - let h = n / 2; - for k in 1..4 { - let m = modes[k]; - self.fill(x + (k & 1) * h, y + (k >> 1) * h, h, |b| b.ipm = m); - } - for k in 0..4 { - let (px, py) = (x + (k & 1) * h, y + (k >> 1) * h); - self.code_tb(0, px, py, 2, Some(modes[k] as u32), (x, y)); - } - for c in 1..3 { - self.code_tb(c, x / 2, y / 2, 2, Some(modes[0] as u32), (x, y)); - } - } else { - self.code_tus(x, y, log2, Some(modes[0] as u32)); - } - if p_slice { - s.decision(CU_SKIP + skip_ctx, 0); - s.decision(PRED_MODE, 1); - } - if log2 == LOG2_MIN_CB { - s.decision(PART_MODE, (!nxn) as u32); - } - self.write_intra_modes(s, x, y, n, nxn, &modes); - s.decision(INTRA_CHROMA, 0); // intra_chroma_pred_mode 4 (DM) - let mut ti = 0; - self.write_tt(s, TreeNode { x, y, log2, depth: 0 }, TransformState { blk_idx: 0, parent: [true, true], intra: true, nxn }, &mut ti); - (mode, self.cu_dist(x, y, n)) - } - _ => { - let mv = match mode { - CuMode::Skip { idx } | CuMode::Merge { idx } => self.merge_list(x, y, n)[idx as usize], - CuMode::Amvp { mv, .. } => mv, - // Handled by the arm above. - CuMode::Intra { .. } => [0, 0], - }; - let amvp = if let CuMode::Amvp { .. } = mode { self.amvp(x, y, n) } else { [[0; 2]; 2] }; - let skip = matches!(mode, CuMode::Skip { .. }); - self.fill(x, y, n, |b| { - b.depth = d8; - b.flags = if skip { F_SKIP } else { 0 }; - b.ipm = 1; - b.mv = mv; - }); - self.mark_edges(x, y, log2); - self.predict_inter(x, y, n, mv); - let mut eff = mode; - let mut root = false; - if !skip { - self.code_tus(x, y, log2, None); - root = self.tbs.iter().any(|t| t.nz); - if !root && let CuMode::Merge { idx } = mode { - // rqt_root_cbf would be inferred: code a skipped CU instead - eff = CuMode::Skip { idx }; - self.fill(x, y, n, |b| b.flags |= F_SKIP); - } - } - match eff { - CuMode::Skip { idx } => { - s.decision(CU_SKIP + skip_ctx, 1); - write_merge_idx(s, idx as usize); - } - CuMode::Merge { idx } => { - s.decision(CU_SKIP + skip_ctx, 0); - s.decision(PRED_MODE, 0); - s.decision(PART_MODE, 1); // PART_2Nx2N - s.decision(MERGE_FLAG, 1); - write_merge_idx(s, idx as usize); - let mut ti = 0; - self.write_tt( - s, - TreeNode { x, y, log2, depth: 0 }, - TransformState { blk_idx: 0, parent: [true, true], intra: false, nxn: false }, - &mut ti, - ); - } - CuMode::Amvp { mv, mvp } => { - s.decision(CU_SKIP + skip_ctx, 0); - s.decision(PRED_MODE, 0); - s.decision(PART_MODE, 1); - s.decision(MERGE_FLAG, 0); - let p = amvp[mvp as usize]; - write_mvd(s, [mv[0] as i32 - p[0] as i32, mv[1] as i32 - p[1] as i32]); - s.decision(MVP_FLAG, mvp as u32); - s.decision(RQT_ROOT_CBF, root as u32); - if root { - let mut ti = 0; - self.write_tt( - s, - TreeNode { x, y, log2, depth: 0 }, - TransformState { blk_idx: 0, parent: [true, true], intra: false, nxn: false }, - &mut ti, - ); - } - } - // Handled by the arm above. - CuMode::Intra { .. } => {} - } - (eff, self.cu_dist(x, y, n)) - } - } - } - - /// Transform units of a 2Nx2N CU (split to the maximum TB size), luma then Cb, Cr per TU. - fn code_tus(&mut self, x: usize, y: usize, log2: u32, intra_mode: Option) { - let tl = log2.min(LOG2_MAX_TB); - let t = 1usize << tl; - let k = 1usize << (log2 - tl); - for i in 0..k * k { - // z-order of at most 2x2 TUs - let (tx, ty) = (x + (i & 1) * t, y + (i >> 1) * t); - self.code_tb(0, tx, ty, tl, intra_mode, (tx, ty)); - self.code_tb(1, tx / 2, ty / 2, tl - 1, intra_mode, (tx, ty)); - self.code_tb(2, tx / 2, ty / 2, tl - 1, intra_mode, (tx, ty)); - } - } - - fn cu_dist(&self, x: usize, y: usize, n: usize) -> u64 { - let mut d = 0u64; - for c in 0..3 { - let (px, py, pn, stride) = if c == 0 { (x, y, n, self.w) } else { (x / 2, y / 2, n / 2, self.cw) }; - for r in 0..pn { - let o = (py + r) * stride + px; - let a = &self.src[c][o..o + pn]; - let b = &self.rec[c][o..o + pn]; - d += a.iter().zip(b).map(|(&p, &q)| (p as i64 - q as i64).pow(2) as u64).sum::(); - } - } - d - } - - /// Code one transform block of component `c` at component position (x, y): optional intra - /// prediction, residual, transform, quantisation and reconstruction. - fn code_tb(&mut self, c: usize, x: usize, y: usize, log2: u32, intra_mode: Option, lpos: (usize, usize)) { - let n = 1usize << log2; - let stride = if c == 0 { self.w } else { self.cw }; - if let Some(m) = intra_mode { - let refs = self.build_refs(c, x, y, log2); - let bd = self.bd; - predict_from_refs(&refs, c, m, n, bd, &mut self.rec[c][y * stride + x..], stride); - } - let mut scr = std::mem::take(&mut self.scratch); - let [r, coef] = &mut scr; - for yy in 0..n { - let o = (y + yy) * stride + x; - for xx in 0..n { - r[yy * n + xx] = self.src[c][o + xx] as i32 - self.rec[c][o + xx] as i32; - } - } - let intra = intra_mode.is_some(); - let dst = intra && c == 0 && n == 4; - transform::forward(&r[..n * n], &mut coef[..n * n], n, dst, self.bd); - let qp = if c == 0 { self.qp_y } else { self.qp_c }; - let off = self.coefs.len(); - self.coefs.resize(off + n * n, 0); - let lv = &mut self.coefs[off..off + n * n]; - let scan = match intra_mode { - Some(m) if log2 == 2 || (log2 == 3 && c == 0) => scan_idx_for_mode(m), - _ => 0, - }; - let nz = transform::quantize(&coef[..n * n], lv, n, qp, intra, self.bd) > 0; - if nz { - transform::sign_hiding(&coef[..n * n], lv, log2, scan as usize, qp, self.bd); - } - if nz { - let d = &mut r[..n * n]; - transform::dequantize(lv, d, n, qp, self.bd); - transform::inverse(d, n, dst, self.bd); - let max = (1i32 << self.bd) - 1; - for yy in 0..n { - let o = (y + yy) * stride + x; - for xx in 0..n { - let v = &mut self.rec[c][o + xx]; - *v = (*v as i32 + d[yy * n + xx]).clamp(0, max) as u16; - } - } - } - self.scratch = scr; - if nz && c == 0 { - self.fill(x, y, n, |b| b.flags |= F_CBF); - } - self.tbs.push(Tb { c: c as u8, lx: lpos.0 as u32, ly: lpos.1 as u32, log2, nz, off, scan }); - } - - /// Reference samples (substituted, unfiltered) of an intra block, as in 8.4.4.2.1 / 8.4.4.2.2. - fn build_refs(&self, c: usize, x0: usize, y0: usize, log2: u32) -> Refs { - let n = 1usize << log2; - let sc = if c == 0 { 1 } else { 2 }; - let (x0, y0) = (x0 as i32, y0 as i32); - let (xl, yl) = (x0 * sc, y0 * sc); - let unit = 4 / sc as usize; - let stride = if c == 0 { self.w } else { self.cw }; - let mut al = [false; 129]; - let mut at = [false; 129]; - let plane = &self.rec[c]; - let mut r = Refs::default(); - if self.zavail(xl, yl, xl - 1, yl - 1) { - al[0] = true; - at[0] = true; - let v = plane[(y0 - 1) as usize * stride + (x0 - 1) as usize]; - r.left[0] = v; - r.top[0] = v; - } - let mut k = 0; - while k < 2 * n { - let yn = y0 + k as i32; - if self.zavail(xl, yl, xl - 1, yn * sc) { - for i in 0..unit { - al[1 + k + i] = true; - r.left[1 + k + i] = plane[(yn as usize + i) * stride + (x0 - 1) as usize]; - } - } - let xn = x0 + k as i32; - if self.zavail(xl, yl, xn * sc, yl - 1) { - let o = (y0 - 1) as usize * stride + xn as usize; - for i in 0..unit { - at[1 + k + i] = true; - r.top[1 + k + i] = plane[o + i]; - } - } - k += unit; - } - intra::substitute(&mut r, &al, &at, n, self.bd); - r - } - - // --------------------------------------------------------------------------------------- - // syntax writers - - /// Candidate modes candModeList (8.4.2) of the prediction block at (xp, yp). - fn mpm(&self, xp: usize, yp: usize) -> [u32; 3] { - let (xp, yp) = (xp as i32, yp as i32); - let cand_a = if self.zavail(xp, yp, xp - 1, yp) { - let b = self.blk_at(xp - 1, yp); - if b.flags & F_INTRA != 0 { b.ipm as u32 } else { 1 } - } else { - 1 - }; - let cand_b = if self.zavail(xp, yp, xp, yp - 1) && yp > ((yp >> LOG2_CTB) << LOG2_CTB) { - let b = self.blk_at(xp, yp - 1); - if b.flags & F_INTRA != 0 { b.ipm as u32 } else { 1 } - } else { - 1 - }; - if cand_a == cand_b { - if cand_a < 2 { [0, 1, 26] } else { [cand_a, 2 + ((cand_a + 29) % 32), 2 + ((cand_a - 2 + 1) % 32)] } - } else { - let c = if cand_a != 0 && cand_b != 0 { - 0 - } else if cand_a != 1 && cand_b != 1 { - 1 - } else { - 26 - }; - [cand_a, cand_b, c] - } - } - - fn write_intra_modes(&self, s: &mut S, x: usize, y: usize, n: usize, nxn: bool, modes: &[u8; 4]) { - let nb = if nxn { 2 } else { 1 }; - let pb = n / nb; - let mut codes = [(false, 0u32); 4]; - for j in 0..nb { - for i in 0..nb { - let k = j * nb + i; - let list = self.mpm(x + i * pb, y + j * pb); - let m = modes[k] as u32; - codes[k] = match list.iter().position(|&c| c == m) { - Some(idx) => (true, idx as u32), - None => { - let mut sorted = list; - sorted.sort_unstable(); - let mut r = m; - for &c in sorted.iter().rev() { - if r > c { - r -= 1; - } - } - (false, r) - } - }; - } - } - for code in codes.iter().take(nb * nb) { - s.decision(PREV_INTRA_LUMA, code.0 as u32); - } - for &(prev, v) in codes.iter().take(nb * nb) { - if prev { - s.bypass((v > 0) as u32); - if v > 0 { - s.bypass((v > 1) as u32); - } - } else { - s.bypass_bits(v, 5); - } - } - } - - fn chroma_cbf(&self, c: u8, x0: usize, y0: usize, n: usize) -> bool { - self.tbs.iter().any(|t| t.c == c && t.nz && (t.lx as usize) >= x0 && (t.lx as usize) < x0 + n && (t.ly as usize) >= y0 && (t.ly as usize) < y0 + n) - } - - /// transform_tree() (7.3.8.8) over the coded transform blocks of the CU; no split flag is ever - /// coded (max_transform_hierarchy_depth_* = 0), splits are inferred. - fn write_tt(&self, s: &mut S, node: TreeNode, state: TransformState, ti: &mut usize) { - let TreeNode { x: x0, y: y0, log2, depth } = node; - let TransformState { blk_idx, parent, intra, nxn } = state; - let n = 1usize << log2; - let split = log2 > LOG2_MAX_TB || (nxn && depth == 0); - let mut cbf_c = [false; 2]; - if log2 > 2 { - for c in 0..2 { - if depth == 0 || parent[c] { - cbf_c[c] = self.chroma_cbf(c as u8 + 1, x0, y0, n); - s.decision(CBF_CHROMA + depth as usize, cbf_c[c] as u32); - } - } - } else if depth > 0 { - cbf_c = parent; - } - if split { - for (i, child) in node.children().enumerate() { - self.write_tt(s, child, TransformState { blk_idx: i as u32, parent: cbf_c, ..state }, ti); - } - return; - } - let tb = self.tbs[*ti]; - *ti += 1; - debug_assert_eq!(tb.c, 0); - if intra || depth != 0 || cbf_c[0] || cbf_c[1] { - s.decision(CBF_LUMA + (depth == 0) as usize, tb.nz as u32); - } else { - debug_assert!(tb.nz, "inferred cbf_luma needs coefficients"); - } - if tb.nz { - let n2 = 1usize << (2 * tb.log2); - residual_coding(s, &self.coefs[tb.off..tb.off + n2], tb.log2, 0, tb.scan as usize); - } - if log2 > 2 || blk_idx == 3 { - for c in 0..2 { - let tb = self.tbs[*ti]; - *ti += 1; - debug_assert_eq!(tb.c as usize, c + 1); - debug_assert_eq!(tb.nz, cbf_c[c]); - if cbf_c[c] { - let n2 = 1usize << (2 * tb.log2); - residual_coding(s, &self.coefs[tb.off..tb.off + n2], tb.log2, c + 1, tb.scan as usize); - } - } - } - } - - // --------------------------------------------------------------------------------------- - // inter prediction - - /// Prediction block availability (6.4.2) for a 2Nx2N CU: available and not intra. - #[inline] - fn pb_avail(&self, xp: i32, yp: i32, xn: i32, yn: i32) -> bool { - self.zavail(xp, yp, xn, yn) && self.blk_at(xn, yn).flags & F_INTRA == 0 - } - - /// Merge candidate list (8.5.3.2.2 - 8.5.3.2.5) of a 2Nx2N P-slice CU: spatial candidates and - /// zero candidates (no temporal candidate, one reference picture). - pub fn merge_list(&self, x: usize, y: usize, n: usize) -> [[i16; 2]; MAX_MERGE_CAND] { - let (xp, yp, w, h) = (x as i32, y as i32, n as i32, n as i32); - let mv = |p: (i32, i32)| self.blk_at(p.0, p.1).mv; - let mut list = [[0i16; 2]; MAX_MERGE_CAND]; - let mut k = 0; - let pa1 = (xp - 1, yp + h - 1); - let av_a1 = self.pb_avail(xp, yp, pa1.0, pa1.1); - if av_a1 { - list[k] = mv(pa1); - k += 1; - } - let pb1 = (xp + w - 1, yp - 1); - let av_b1 = self.pb_avail(xp, yp, pb1.0, pb1.1); - let fl_b1 = av_b1 && !(av_a1 && mv(pa1) == mv(pb1)); - if fl_b1 { - list[k] = mv(pb1); - k += 1; - } - let pb0 = (xp + w, yp - 1); - let fl_b0 = self.pb_avail(xp, yp, pb0.0, pb0.1) && !(av_b1 && mv(pb1) == mv(pb0)); - if fl_b0 { - list[k] = mv(pb0); - k += 1; - } - let pa0 = (xp - 1, yp + h); - let fl_a0 = self.pb_avail(xp, yp, pa0.0, pa0.1) && !(av_a1 && mv(pa1) == mv(pa0)); - if fl_a0 { - list[k] = mv(pa0); - k += 1; - } - let pb2 = (xp - 1, yp - 1); - let fl_b2 = self.pb_avail(xp, yp, pb2.0, pb2.1) - && !((av_a1 && mv(pa1) == mv(pb2)) || (av_b1 && mv(pb1) == mv(pb2))) - && (fl_a0 as u32 + av_a1 as u32 + fl_b0 as u32 + fl_b1 as u32) != 4; - if fl_b2 { - list[k] = mv(pb2); - } - // remaining entries are zero candidates with refIdx 0 - list - } - - /// AMVP candidates (8.5.3.2.6, 8.5.3.2.7) of a 2Nx2N CU with one reference picture. - pub fn amvp(&self, x: usize, y: usize, n: usize) -> [[i16; 2]; 2] { - let (xp, yp, w, h) = (x as i32, y as i32, n as i32, n as i32); - let pa = [(xp - 1, yp + h), (xp - 1, yp + h - 1)]; - let av_a = [self.pb_avail(xp, yp, pa[0].0, pa[0].1), self.pb_avail(xp, yp, pa[1].0, pa[1].1)]; - let is_scaled = av_a[0] || av_a[1]; - let mut a = None; - for k in 0..2 { - if av_a[k] && a.is_none() { - a = Some(self.blk_at(pa[k].0, pa[k].1).mv); - } - } - let pb = [(xp + w, yp - 1), (xp + w - 1, yp - 1), (xp - 1, yp - 1)]; - let mut b = None; - for p in pb { - if b.is_none() && self.pb_avail(xp, yp, p.0, p.1) { - b = Some(self.blk_at(p.0, p.1).mv); - } - } - if !is_scaled && b.is_some() { - a = b; - } - let mut list = [[0i16; 2]; 2]; - match (a, b) { - (Some(a), Some(b)) => { - list[0] = a; - if b != a { - list[1] = b; - } - } - (Some(a), None) => list[0] = a, - (None, Some(b)) => list[0] = b, - (None, None) => {} - } - list - } - - fn predict_inter(&mut self, x: usize, y: usize, n: usize, mv: [i16; 2]) { - // Inter modes are only tried in P slices, which have a reference picture. - let Some(refp) = self.refp else { return }; - for c in 0..3 { - let (px, py, pn, pw, ph) = if c == 0 { (x, y, n, self.w, self.h) } else { (x / 2, y / 2, n / 2, self.cw, self.ch) }; - let pr = PlaneRef { data: &refp[c], w: pw, h: ph }; - if c == 0 { - inter::mc_luma(pr, self.bd, px as i32, py as i32, mv, (pn, pn), &mut self.pred); - } else { - inter::mc_chroma(pr, self.bd, px as i32, py as i32, mv, (pn, pn), &mut self.pred); - } - inter::put_uni(&self.pred, pn, pn, self.bd, &mut self.rec[c][py * pw + px..], pw); - } - } - - // --------------------------------------------------------------------------------------- - // estimation helpers - - /// Best merge index by SATD of the (approximate) luma prediction. - fn best_merge(&self, x: usize, y: usize, n: usize, list: &[[i16; 2]; MAX_MERGE_CAND]) -> (usize, u64) { - let Some(me) = self.me else { return (0, u64::MAX / 4) }; - let mut buf = vec![0u16; n * n]; - let mut best = (0, u64::MAX); - for (i, &mv) in list.iter().enumerate() { - if list[..i].contains(&mv) { - continue; - } - me.approx_pred(x as i32, y as i32, n, mv, &mut buf); - let satd = satd(&self.src[0][y * self.w + x..], self.w, &buf, n, n) as u64; - let cost = satd + (self.lambda_sad * (i + 1) as f64) as u64; - if cost < best.1 { - best = (i, cost); - } - } - best - } - - /// Integer then fractional motion search. Returns (mv, mvp index, SATD cost). - fn motion_search(&self, x: usize, y: usize, n: usize, amvp: &[[i16; 2]; 2], merge: &[[i16; 2]; MAX_MERGE_CAND]) -> ([i16; 2], u8, u64) { - let Some(me) = self.me else { return (amvp[0], 0, u64::MAX / 4) }; - let (xi, yi) = (x as i32, y as i32); - let src = &self.src[0][y * self.w + x..]; - let ls = self.lambda_sad; - let mv_bits = |mv: [i32; 2]| -> f64 { amvp.iter().map(|p| mvd_bits(mv[0] - p[0] as i32) + mvd_bits(mv[1] - p[1] as i32)).fold(f64::MAX, f64::min) }; - let (lo_x, hi_x, lo_y, hi_y) = me.int_range(xi, yi, n); - let clamp_i = |v: [i32; 2]| [v[0].clamp(lo_x, hi_x), v[1].clamp(lo_y, hi_y)]; - let sad_cost = |v: [i32; 2]| -> f64 { me.sad(src, self.w, xi + v[0], yi + v[1], n) as f64 + ls * mv_bits([v[0] * 4, v[1] * 4]) }; - // starting points - let mut best = [0i32; 2]; - let mut best_cost = sad_cost(best); - for mv in amvp.iter().chain(merge.iter()) { - let v = clamp_i([(mv[0] as i32 + 2) >> 2, (mv[1] as i32 + 2) >> 2]); - let c = sad_cost(v); - if c < best_cost { - best_cost = c; - best = v; - } - } - // square pattern search with decreasing steps - let mut step = 16; - while step >= 1 { - let mut moved = true; - let mut iters = 0; - while moved && iters < 4 { - moved = false; - iters += 1; - let center = best; - for (dx, dy) in [(-1, -1), (0, -1), (1, -1), (-1, 0), (1, 0), (-1, 1), (0, 1), (1, 1)] { - let v = clamp_i([center[0] + dx * step, center[1] + dy * step]); - if v == center { - continue; - } - let c = sad_cost(v); - if c < best_cost { - best_cost = c; - best = v; - moved = true; - } - } - } - step /= 2; - } - // fractional refinement with SATD on the approximate interpolated planes - let mut buf = vec![0u16; n * n]; - let mut satd_cost = |mv: [i32; 2]| -> f64 { - me.approx_pred(xi, yi, n, [mv[0] as i16, mv[1] as i16], &mut buf); - satd(src, self.w, &buf, n, n) as f64 + ls * mv_bits(mv) - }; - let mut bq = [best[0] * 4, best[1] * 4]; - let mut bc = satd_cost(bq); - for step in [2, 1] { - let center = bq; - for (dx, dy) in [(-1, -1), (0, -1), (1, -1), (-1, 0), (1, 0), (-1, 1), (0, 1), (1, 1)] { - let v = [center[0] + dx * step, center[1] + dy * step]; - let c = satd_cost(v); - if c < bc { - bc = c; - bq = v; - } - } - } - let mv = [bq[0] as i16, bq[1] as i16]; - let bits = |p: &[i16; 2]| mvd_bits(bq[0] - p[0] as i32) + mvd_bits(bq[1] - p[1] as i32); - let mvp = if bits(&amvp[1]) < bits(&amvp[0]) { 1 } else { 0 }; - (mv, mvp, bc as u64) - } - - /// Best 2Nx2N intra luma mode by SATD (coarse grid, then refinement around the best angle). - fn search_intra_mode(&self, x: usize, y: usize, log2: u32) -> (u32, u64) { - let n = 1usize << log2; - let refs = self.build_refs(0, x, y, log2); - let mpm = self.mpm(x, y); - let mut buf = vec![0u16; n * n]; - let src = &self.src[0][y * self.w + x..]; - let mut eval = |m: u32| -> u64 { - predict_from_refs(&refs, 0, m, n, self.bd, &mut buf, n); - let bits = match mpm.iter().position(|&c| c == m) { - Some(0) => 2.0, - Some(_) => 3.0, - None => 6.0, - }; - satd(src, self.w, &buf, n, n) as u64 + (self.lambda_sad * bits) as u64 - }; - let mut best = (1u32, u64::MAX); - let mut tried = [false; 35]; - for &m in COARSE_MODES.iter().chain(mpm.iter()) { - if !tried[m as usize] { - tried[m as usize] = true; - let c = eval(m); - if c < best.1 { - best = (m, c); - } - } - } - if best.0 >= 2 { - for d in [2i32, 1] { - let center = best.0 as i32; - for m in [center - d, center + d] { - if (2..=34).contains(&m) && !tried[m as usize] { - tried[m as usize] = true; - let c = eval(m as u32); - if c < best.1 { - best = (m as u32, c); - } - } - } - } - } - best - } - - /// Modes of the four 4x4 PUs of an NxN intra CU, chosen sequentially (each PU reconstructed - /// before the next one is searched, as in the real coding order). - fn search_nxn_modes(&mut self, x: usize, y: usize) -> [u8; 4] { - let mut modes = [1u8; 4]; - self.fill(x, y, 8, |b| { - b.flags = F_INTRA; - b.ipm = 1; - }); - for k in 0..4 { - let (px, py) = (x + (k & 1) * 4, y + (k >> 1) * 4); - let (m, _) = self.search_intra_mode(px, py, 2); - modes[k] = m as u8; - self.fill(px, py, 4, |b| b.ipm = m as u8); - self.tbs.clear(); - self.coefs.clear(); - self.code_tb(0, px, py, 2, Some(m), (px, py)); - } - modes - } -} - -/// Filter (luma) and predict an intra block from substituted references. -fn predict_from_refs(refs: &Refs, c: usize, mode: u32, n: usize, bd: u32, out: &mut [u16], os: usize) { - if c == 0 { - let mut r = refs.clone(); - intra::filter(&mut r, mode, n, true, true, bd); - intra::predict(&r, mode, n, true, bd, out, os); - } else { - intra::predict(refs, mode, n, false, bd, out, os); - } -} - -/// scanIdx (7.4.9.11) for an intra prediction mode. -fn scan_idx_for_mode(m: u32) -> u8 { - if (6..=14).contains(&m) { - 2 - } else if (22..=30).contains(&m) { - 1 - } else { - 0 - } -} - -/// Approximate bits of one motion vector difference component. -fn mvd_bits(v: i32) -> f64 { - if v == 0 { 1.0 } else { (2 * (32 - (v.unsigned_abs()).leading_zeros()) + 1) as f64 } -} - -fn write_merge_idx(s: &mut S, idx: usize) { - if MAX_MERGE_CAND > 1 { - s.decision(MERGE_IDX, (idx > 0) as u32); - if idx > 0 { - let mut i = 1; - while i < MAX_MERGE_CAND - 1 { - let b = idx > i; - s.bypass(b as u32); - if !b { - break; - } - i += 1; - } - } - } -} - -/// mvd_coding() (7.3.8.9). -fn write_mvd(s: &mut S, d: [i32; 2]) { - let g0 = [d[0] != 0, d[1] != 0]; - s.decision(ABS_MVD_GT0, g0[0] as u32); - s.decision(ABS_MVD_GT0, g0[1] as u32); - for k in 0..2 { - if g0[k] { - s.decision(ABS_MVD_GT1, (d[k].abs() > 1) as u32); - } - } - for k in 0..2 { - if g0[k] { - let a = d[k].unsigned_abs(); - if a > 1 { - // abs_mvd_minus2: EG1 - let mut v = a - 2; - let mut kk = 1; - while v >= (1 << kk) { - s.bypass(1); - v -= 1 << kk; - kk += 1; - } - s.bypass(0); - s.bypass_bits(v, kk); - } - s.bypass((d[k] < 0) as u32); - } - } -} - -/// coeff_abs_level_remaining binarisation (9.3.3.11): TR prefix up to 3 with Rice suffix, then -/// an escape with an Exp-Golomb-like suffix. -fn write_remaining(s: &mut S, v: u32, rice: u32) { - if v < (4 << rice) { - let p = v >> rice; - for _ in 0..p { - s.bypass(1); - } - s.bypass(0); - s.bypass_bits(v & ((1 << rice) - 1), rice); - } else { - let u = v - (2 << rice); - let kr = 31 - u.leading_zeros(); // k + rice - let k = kr - rice; - for _ in 0..k + 3 { - s.bypass(1); - } - s.bypass(0); - s.bypass_bits(u - (1 << kr), kr); - } -} - -/// last_sig_coeff_{x,y}_prefix value and suffix for a position. -fn last_prefix(v: u32) -> (u32, u32, u32) { - if v < 4 { - return (v, 0, 0); - } - let mut p = 4; - loop { - let nb = (p >> 1) - 1; - let min = (1 << nb) * (2 + (p & 1)); - let next_nb = ((p + 1) >> 1) - 1; - let next = (1 << next_nb) * (2 + ((p + 1) & 1)); - if v < next { - return (p, v - min, nb); - } - p += 1; - } -} - -/// Context increment for significant_coeff_flag (H.265 9.3.4.2). -fn significance_context(group: (usize, usize), pos: (usize, usize), log2: u32, prev_csbf: u8, c: usize, scan_idx: usize) -> usize { - let (xs, ys) = group; - let (xp, yp) = pos; - let (xc, yc) = (xs * 4 + xp, ys * 4 + yp); - if log2 == 2 { - CTX_IDX_MAP[(yc << 2) + xc] as usize - } else if xc + yc == 0 { - 0 - } else { - let mut sc = match prev_csbf { - 0 => { - if xp + yp == 0 { - 2 - } else if xp + yp < 3 { - 1 - } else { - 0 - } - } - 1 => { - if yp == 0 { - 2 - } else if yp == 1 { - 1 - } else { - 0 - } - } - 2 => { - if xp == 0 { - 2 - } else if xp == 1 { - 1 - } else { - 0 - } - } - _ => 2, - }; - if c == 0 { - if xs + ys > 0 { - sc += 3; - } - sc += if log2 == 3 { if scan_idx == 0 { 9 } else { 15 } } else { 21 }; - } else { - sc += if log2 == 3 { 9 } else { 12 }; - } - sc - } -} - -/// Absolute levels, signs and Rice remainders for one significant coefficient group. -fn write_coefficient_levels(s: &mut S, sig: &[u8], at: impl Fn(usize) -> i32, i: usize, c: usize, prev_g1: &mut Option) { - let nsig = sig.len(); - let abs_at = |k: usize| at(sig[k] as usize).unsigned_abs(); - let mut ctx_set = if i == 0 || c > 0 { 0 } else { 2 }; - if *prev_g1 == Some(0) { - ctx_set += 1; - } - let mut g1ctx = 1u32; - let mut gt1 = [0u8; 16]; - let mut first_g1: Option = None; - let c_off_g1 = if c > 0 { 16 } else { 0 }; - for k in 0..nsig.min(8) { - let f = abs_at(k) > 1; - s.decision(GT1 + ctx_set * 4 + g1ctx.min(3) as usize + c_off_g1, f as u32); - gt1[k] = f as u8; - if f { - g1ctx = 0; - if first_g1.is_none() { - first_g1 = Some(k); - } - } else if g1ctx > 0 { - g1ctx += 1; - } - } - *prev_g1 = Some(g1ctx); - let mut gt2 = 0u32; - if let Some(k) = first_g1 { - gt2 = (abs_at(k) > 2) as u32; - s.decision(GT2 + ctx_set + if c > 0 { 4 } else { 0 }, gt2); - } - // sign data hiding: the sign of the first coefficient in scan order (sig[nsig - 1]) is - // inferred from the parity of the sum of absolute levels - let hidden = sig[0] as i32 - sig[nsig - 1] as i32 > 3; - let nsigns = if hidden { nsig - 1 } else { nsig }; - for k in 0..nsigns { - s.bypass((at(sig[k] as usize) < 0) as u32); - } - if hidden { - let sum: u32 = (0..nsig).map(abs_at).sum(); - debug_assert_eq!(at(sig[nsig - 1] as usize) < 0, sum & 1 == 1, "sign hiding parity"); - } - let mut rice = 0u32; - for k in 0..nsig { - let mut base = 1 + gt1[k] as u32; - if Some(k) == first_g1 { - base += gt2; - } - let thresh = if k < 8 { if Some(k) == first_g1 { 3 } else { 2 } } else { 1 }; - if base == thresh { - let level = abs_at(k); - write_remaining(s, level - base, rice); - if level > 3 * (1 << rice) { - rice = (rice + 1).min(4); - } - } - } -} - -/// residual_coding() (7.3.8.11) of one transform block of levels (row-major, n x n). No transform -/// skip; signs use sign data hiding where the coefficient span permits it. -fn residual_coding(s: &mut S, lv: &[i32], log2: u32, c: usize, scan_idx: usize) { - let n = 1usize << log2; - let log2sb = log2 - 2; - let sb_w = 1usize << log2sb; - let sb_scan = scan_order(log2sb, scan_idx); - let pos_scan = &SCAN_4[scan_idx]; - let at = |xs: usize, ys: usize, p: usize| lv[(ys * 4 + pos_scan[p].1 as usize) * n + xs * 4 + pos_scan[p].0 as usize]; - // last significant coefficient in scan order - let mut last = None; - 'outer: for i in (0..sb_scan.len()).rev() { - let (xs, ys) = (sb_scan[i].0 as usize, sb_scan[i].1 as usize); - for p in (0..16).rev() { - if at(xs, ys, p) != 0 { - last = Some((i, p)); - break 'outer; - } - } - } - // Callers only code blocks with a nonzero level (coded_block_flag = 1). - let Some((last_sb, last_pos)) = last else { return }; - let lx = sb_scan[last_sb].0 as u32 * 4 + pos_scan[last_pos].0 as u32; - let ly = sb_scan[last_sb].1 as u32 * 4 + pos_scan[last_pos].1 as u32; - let (cx, cy) = if scan_idx == 2 { (ly, lx) } else { (lx, ly) }; - let cmax = (log2 << 1) - 1; - let (off, shift) = if c == 0 { (3 * (log2 - 2) + ((log2 - 1) >> 2), (log2 + 1) >> 2) } else { (15, log2 - 2) }; - let (px, sx, nbx) = last_prefix(cx); - let (py, sy, nby) = last_prefix(cy); - for (p, base) in [(px, LAST_X_PREFIX), (py, LAST_Y_PREFIX)] { - for i in 0..p { - s.decision(base + (off + (i >> shift)) as usize, 1); - } - if p < cmax { - s.decision(base + (off + (p >> shift)) as usize, 0); - } - } - if px > 3 { - s.bypass_bits(sx, nbx); - } - if py > 3 { - s.bypass_bits(sy, nby); - } - let mut csbf = [0u8; 64]; - let mut prev_g1: Option = None; - let c_off_sig = if c == 0 { 0 } else { 27 }; - for i in (0..=last_sb).rev() { - let (xs, ys) = (sb_scan[i].0 as usize, sb_scan[i].1 as usize); - let right = if xs + 1 < sb_w { csbf[ys * 8 + xs + 1] } else { 0 }; - let below = if ys + 1 < sb_w { csbf[(ys + 1) * 8 + xs] } else { 0 }; - let mut infer_dc = false; - if i < last_sb && i > 0 { - let has = (0..16).any(|p| at(xs, ys, p) != 0); - let inc = (right | below).min(1) as usize + if c > 0 { 2 } else { 0 }; - s.decision(CODED_SUB_BLOCK + inc, has as u32); - csbf[ys * 8 + xs] = has as u8; - infer_dc = true; - } else { - csbf[ys * 8 + xs] = 1; - } - if csbf[ys * 8 + xs] == 0 { - continue; - } - let mut sig = [0u8; 16]; - let mut nsig = 0; - let start: i32 = if i == last_sb { - sig[0] = last_pos as u8; - nsig = 1; - last_pos as i32 - 1 - } else { - 15 - }; - let prev_csbf = right + (below << 1); - let mut np = start; - while np >= 0 { - let p = np as usize; - let (xp, yp) = (pos_scan[p].0 as usize, pos_scan[p].1 as usize); - let (xc, yc) = (xs * 4 + xp, ys * 4 + yp); - let v = lv[yc * n + xc] != 0; - if np > 0 || !infer_dc { - let sig_ctx = significance_context((xs, ys), (xp, yp), log2, prev_csbf, c, scan_idx); - s.decision(SIG_COEFF + c_off_sig + sig_ctx, v as u32); - if v { - sig[nsig] = p as u8; - nsig += 1; - infer_dc = false; - } - } else { - debug_assert!(v, "inferred DC coefficient must be non-zero"); - sig[nsig] = 0; - nsig += 1; - } - np -= 1; - } - if nsig == 0 { - continue; - } - write_coefficient_levels(s, &sig[..nsig], |p| at(xs, ys, p), i, c, &mut prev_g1); - } -} - -/// Sum of absolute Hadamard-transformed differences (8x8 blocks, 4x4 for 4-sample blocks). -pub fn satd(src: &[u16], ss: usize, pred: &[u16], ps: usize, n: usize) -> u32 { - if n == 4 { - return satd4(src, ss, pred, ps); - } - let mut total = 0; - for by in (0..n).step_by(8) { - for bx in (0..n).step_by(8) { - total += satd8(&src[by * ss + bx..], ss, &pred[by * ps + bx..], ps); - } - } - total -} - -fn satd4(src: &[u16], ss: usize, pred: &[u16], ps: usize) -> u32 { - let mut d = [0i32; 16]; - for y in 0..4 { - for x in 0..4 { - d[y * 4 + x] = src[y * ss + x] as i32 - pred[y * ps + x] as i32; - } - } - let mut m = [0i32; 16]; - for y in 0..4 { - let r = &d[y * 4..y * 4 + 4]; - let (a, b, c, e) = (r[0] + r[3], r[1] + r[2], r[1] - r[2], r[0] - r[3]); - m[y * 4] = a + b; - m[y * 4 + 1] = e + c; - m[y * 4 + 2] = a - b; - m[y * 4 + 3] = e - c; - } - let mut sum = 0; - for x in 0..4 { - let (r0, r1, r2, r3) = (m[x], m[4 + x], m[8 + x], m[12 + x]); - let (a, b, c, e) = (r0 + r3, r1 + r2, r1 - r2, r0 - r3); - sum += (a + b).abs() + (e + c).abs() + (a - b).abs() + (e - c).abs(); - } - ((sum + 1) >> 1) as u32 -} - -fn hadamard8(v: &mut [i32; 8]) { - let mut t = *v; - let mut h = 1; - while h < 8 { - for i in (0..8).step_by(h * 2) { - for j in i..i + h { - let (a, b) = (t[j], t[j + h]); - t[j] = a + b; - t[j + h] = a - b; - } - } - h *= 2; - } - *v = t; -} - -fn satd8(src: &[u16], ss: usize, pred: &[u16], ps: usize) -> u32 { - let mut m = [[0i32; 8]; 8]; - for y in 0..8 { - for x in 0..8 { - m[y][x] = src[y * ss + x] as i32 - pred[y * ps + x] as i32; - } - hadamard8(&mut m[y]); - } - let mut sum = 0; - for x in 0..8 { - let mut col = [0i32; 8]; - for y in 0..8 { - col[y] = m[y][x]; - } - hadamard8(&mut col); - sum += col.iter().map(|v| v.abs()).sum::(); - } - ((sum + 2) >> 2) as u32 -} - -#[cfg(test)] -mod tests { - use super::*; - - #[test] - fn last_prefix_groups() { - assert_eq!(last_prefix(3), (3, 0, 0)); - assert_eq!(last_prefix(4), (4, 0, 1)); - assert_eq!(last_prefix(5), (4, 1, 1)); - assert_eq!(last_prefix(7), (5, 1, 1)); - assert_eq!(last_prefix(8), (6, 0, 2)); - assert_eq!(last_prefix(15), (7, 3, 2)); - assert_eq!(last_prefix(16), (8, 0, 3)); - assert_eq!(last_prefix(31), (9, 7, 3)); - } -} diff --git a/crates/hevcenc/src/inter.rs b/crates/hevcenc/src/inter.rs deleted file mode 100644 index 03bf50ffb..000000000 --- a/crates/hevcenc/src/inter.rs +++ /dev/null @@ -1,157 +0,0 @@ -//! Fractional sample interpolation (8.5.3.3.3) and default weighted prediction (8-262), mirrored -//! from the filmcraft-hevc decoder. Reference sample fetches clamp to the picture (8-228, 8-229). - -use crate::tables::{CHROMA_FILTER, LUMA_FILTER}; - -/// One plane of a reference picture. -#[derive(Clone, Copy)] -pub struct PlaneRef<'a> { - pub data: &'a [u16], - pub w: usize, - pub h: usize, -} - -impl PlaneRef<'_> { - /// Copy a w x h window at (x0, y0) with edge replication. - fn window(&self, x0: i32, y0: i32, w: usize, h: usize, out: &mut [i16], os: usize) { - let (pw, ph) = (self.w as i32, self.h as i32); - let inside_x = x0 >= 0 && x0 + w as i32 <= pw; - for r in 0..h { - let yy = (y0 + r as i32).clamp(0, ph - 1) as usize; - let row = &self.data[yy * self.w..yy * self.w + self.w]; - let dst = &mut out[r * os..r * os + w]; - if inside_x { - for (d, &s) in dst.iter_mut().zip(&row[x0 as usize..x0 as usize + w]) { - *d = s as i16; - } - } else { - for (c, d) in dst.iter_mut().enumerate() { - *d = row[(x0 + c as i32).clamp(0, pw - 1) as usize] as i16; - } - } - } - } -} - -/// Luma prediction samples (14-bit intermediate) of a w x h block at (x, y) displaced by the -/// quarter-sample vector `mv`, into `out` (stride w). -pub fn mc_luma(f: PlaneRef, bd: u32, x: i32, y: i32, mv: [i16; 2], size: (usize, usize), out: &mut [i16]) { - let (w, h) = size; - let (fx, fy) = ((mv[0] & 3) as usize, (mv[1] & 3) as usize); - let xi = x + (mv[0] as i32 >> 2); - let yi = y + (mv[1] as i32 >> 2); - let shift1 = bd.min(12) - 8; - let shift3 = 14 - bd; - let ws = w + 7; - let mut win = vec![0i16; ws * (h + 7)]; - if fx == 0 && fy == 0 { - f.window(xi, yi, w, h, &mut win, ws); - for r in 0..h { - for c in 0..w { - out[r * w + c] = win[r * ws + c] << shift3; - } - } - return; - } - f.window(xi - 3, yi - 3, w + 7, h + 7, &mut win, ws); - let hf = &LUMA_FILTER[fx]; - let vf = &LUMA_FILTER[fy]; - if fy == 0 { - for r in 0..h { - let row = &win[(r + 3) * ws..]; - for c in 0..w { - let s: i32 = (0..8).map(|k| hf[k] as i32 * row[c + k] as i32).sum(); - out[r * w + c] = (s >> shift1) as i16; - } - } - } else if fx == 0 { - for r in 0..h { - for c in 0..w { - let s: i32 = (0..8).map(|k| vf[k] as i32 * win[(r + k) * ws + c + 3] as i32).sum(); - out[r * w + c] = (s >> shift1) as i16; - } - } - } else { - let mut tmp = vec![0i16; ws * (h + 7)]; - for r in 0..h + 7 { - let row = &win[r * ws..]; - for c in 0..w { - let s: i32 = (0..8).map(|k| hf[k] as i32 * row[c + k] as i32).sum(); - tmp[r * ws + c] = (s >> shift1) as i16; - } - } - for r in 0..h { - for c in 0..w { - let s: i32 = (0..8).map(|k| vf[k] as i32 * tmp[(r + k) * ws + c] as i32).sum(); - out[r * w + c] = (s >> 6) as i16; - } - } - } -} - -/// Chroma prediction samples of a w x h block at chroma position (x, y) with a 1/8-sample vector. -pub fn mc_chroma(f: PlaneRef, bd: u32, x: i32, y: i32, mv: [i16; 2], size: (usize, usize), out: &mut [i16]) { - let (w, h) = size; - let (fx, fy) = ((mv[0] & 7) as usize, (mv[1] & 7) as usize); - let xi = x + (mv[0] as i32 >> 3); - let yi = y + (mv[1] as i32 >> 3); - let shift1 = bd.min(12) - 8; - let shift3 = 14 - bd; - let ws = w + 7; - let mut win = vec![0i16; ws * (h + 7)]; - if fx == 0 && fy == 0 { - f.window(xi, yi, w, h, &mut win, ws); - for r in 0..h { - for c in 0..w { - out[r * w + c] = win[r * ws + c] << shift3; - } - } - return; - } - f.window(xi - 1, yi - 1, w + 3, h + 3, &mut win, ws); - let hf = &CHROMA_FILTER[fx]; - let vf = &CHROMA_FILTER[fy]; - if fy == 0 { - for r in 0..h { - let row = &win[(r + 1) * ws..]; - for c in 0..w { - let s: i32 = (0..4).map(|k| hf[k] as i32 * row[c + k] as i32).sum(); - out[r * w + c] = (s >> shift1) as i16; - } - } - } else if fx == 0 { - for r in 0..h { - for c in 0..w { - let s: i32 = (0..4).map(|k| vf[k] as i32 * win[(r + k) * ws + c + 1] as i32).sum(); - out[r * w + c] = (s >> shift1) as i16; - } - } - } else { - let mut tmp = vec![0i16; ws * (h + 7)]; - for r in 0..h + 3 { - let row = &win[r * ws..]; - for c in 0..w { - let s: i32 = (0..4).map(|k| hf[k] as i32 * row[c + k] as i32).sum(); - tmp[r * ws + c] = (s >> shift1) as i16; - } - } - for r in 0..h { - for c in 0..w { - let s: i32 = (0..4).map(|k| vf[k] as i32 * tmp[(r + k) * ws + c] as i32).sum(); - out[r * w + c] = (s >> 6) as i16; - } - } - } -} - -/// Default weighted prediction, one list (8-262): 14-bit samples to `bd`-bit samples. -pub fn put_uni(p: &[i16], w: usize, h: usize, bd: u32, dst: &mut [u16], ds: usize) { - let shift = 14 - bd; - let off = if shift > 0 { 1 << (shift - 1) } else { 0 }; - let max = (1i32 << bd) - 1; - for r in 0..h { - for c in 0..w { - dst[r * ds + c] = ((p[r * w + c] as i32 + off) >> shift).clamp(0, max) as u16; - } - } -} diff --git a/crates/hevcenc/src/intra.rs b/crates/hevcenc/src/intra.rs deleted file mode 100644 index f1fe9e4cb..000000000 --- a/crates/hevcenc/src/intra.rs +++ /dev/null @@ -1,254 +0,0 @@ -//! Intra sample prediction (8.4.4.2), mirrored from the filmcraft-hevc decoder so the encoder -//! reconstructs exactly like a decoder: reference substitution, filtering (incl. strong intra -//! smoothing), planar, DC and the 33 angular modes. - -use crate::tables::{INTRA_PRED_ANGLE, INV_ANGLE}; - -/// Reference samples of an nTbS block: `left[0]` = `top[0]` = p[-1][-1], `left[1 + y]` = p[-1][y], -/// `top[1 + x]` = p[x][-1] for x, y = 0..2*nTbS-1. -#[derive(Clone)] -pub struct Refs { - pub left: [u16; 129], - pub top: [u16; 129], -} - -impl Default for Refs { - fn default() -> Self { - Refs { left: [0; 129], top: [0; 129] } - } -} - -/// Substitution process (8.4.4.2.2). `avail_left[1 + y]` / `avail_top[1 + x]` (index 0 = corner) tell -/// which samples are available; unavailable ones are replaced. -pub fn substitute(r: &mut Refs, avail_left: &[bool; 129], avail_top: &[bool; 129], n: usize, bit_depth: u32) { - let n2 = 2 * n; - // Sequence order: p[-1][2n-1] .. p[-1][-1], p[0][-1] .. p[2n-1][-1] - let any = avail_left[..=n2].iter().any(|&a| a) || avail_top[1..=n2].iter().any(|&a| a); - if !any { - let v = 1u16 << (bit_depth - 1); - r.left[..=n2].fill(v); - r.top[..=n2].fill(v); - return; - } - if !avail_left[n2] { - // search upwards then along the top - let mut found = None; - for y in (0..n2).rev() { - if avail_left[y] { - found = Some(r.left[y]); - break; - } - } - if found.is_none() { - for x in 1..=n2 { - if avail_top[x] { - found = Some(r.top[x]); - break; - } - } - } - r.left[n2] = found.unwrap_or(0); - } - for y in (0..n2).rev() { - if !avail_left[y] { - r.left[y] = r.left[y + 1]; - } - } - r.top[0] = r.left[0]; - for x in 1..=n2 { - if !avail_top[x] { - r.top[x] = r.top[x - 1]; - } - } -} - -/// Filtering process of neighbouring samples (8.4.4.2.3) for luma (and 4:4:4 chroma). -pub fn filter(r: &mut Refs, mode: u32, n: usize, strong_enabled: bool, is_luma: bool, bit_depth: u32) { - if mode == 1 || n == 4 { - return; - } - let min_dist = (mode as i32 - 26).abs().min((mode as i32 - 10).abs()); - let thres = match n { - 8 => 7, - 16 => 1, - _ => 0, - }; - if min_dist <= thres { - return; - } - let n2 = 2 * n; - let c = r.left[0] as i32; - if strong_enabled && is_luma && n == 32 { - let (bl, tr) = (r.left[n2] as i32, r.top[n2] as i32); - let thr = 1 << (bit_depth - 5); - if (c + tr - 2 * r.top[n] as i32).abs() < thr && (c + bl - 2 * r.left[n] as i32).abs() < thr { - for i in 0..63 { - r.left[1 + i] = (((63 - i as i32) * c + (i as i32 + 1) * bl + 32) >> 6) as u16; - r.top[1 + i] = (((63 - i as i32) * c + (i as i32 + 1) * tr + 32) >> 6) as u16; - } - return; - } - } - let (l, t) = (r.left, r.top); - let corner = ((l[1] as u32 + 2 * c as u32 + t[1] as u32 + 2) >> 2) as u16; - for i in 1..n2 { - r.left[i] = ((l[i - 1] as u32 + 2 * l[i] as u32 + l[i + 1] as u32 + 2) >> 2) as u16; - r.top[i] = ((t[i - 1] as u32 + 2 * t[i] as u32 + t[i + 1] as u32 + 2) >> 2) as u16; - } - r.left[0] = corner; - r.top[0] = corner; -} - -/// Predict an n x n block with `mode` into `out` (stride `os`). `edge_filters`: DC / horizontal / -/// vertical boundary smoothing (luma blocks smaller than 32x32). -pub fn predict(r: &Refs, mode: u32, n: usize, edge_filters: bool, bit_depth: u32, out: &mut [u16], os: usize) { - let max = (1i32 << bit_depth) - 1; - match mode { - 0 => { - // planar - let log2 = n.trailing_zeros(); - let tr = r.top[1 + n] as i32; - let bl = r.left[1 + n] as i32; - for y in 0..n { - let l = r.left[1 + y] as i32; - for x in 0..n { - let t = r.top[1 + x] as i32; - let v = ((n - 1 - x) as i32 * l + (x as i32 + 1) * tr + (n - 1 - y) as i32 * t + (y as i32 + 1) * bl + n as i32) >> (log2 + 1); - out[y * os + x] = v as u16; - } - } - } - 1 => { - let log2 = n.trailing_zeros(); - let sum: u32 = r.top[1..=n].iter().map(|&v| v as u32).sum::() + r.left[1..=n].iter().map(|&v| v as u32).sum::(); - let dc = ((sum + n as u32) >> (log2 + 1)) as u16; - for y in 0..n { - out[y * os..y * os + n].fill(dc); - } - if edge_filters && n < 32 { - let d = dc as u32; - out[0] = ((r.left[1] as u32 + 2 * d + r.top[1] as u32 + 2) >> 2) as u16; - for x in 1..n { - out[x] = ((r.top[1 + x] as u32 + 3 * d + 2) >> 2) as u16; - } - for y in 1..n { - out[y * os] = ((r.left[1 + y] as u32 + 3 * d + 2) >> 2) as u16; - } - } - } - _ => { - let angle = INTRA_PRED_ANGLE[mode as usize] as i32; - // reference array ref[-n..=2n] stored at offset n - let mut refa = [0i32; 3 * 64 + 1]; - let base = n as i32; - let (main, side) = if mode >= 18 { (&r.top, &r.left) } else { (&r.left, &r.top) }; - for x in 0..=n { - refa[(base + x as i32) as usize] = main[x] as i32; // ref[x] = p[-1+x][-1] - } - if angle < 0 { - let lim = (n as i32 * angle) >> 5; - if lim < -1 { - let inv = INV_ANGLE[(mode - 11) as usize] as i32; - for x in lim..=-1 { - let idx = -1 + ((x * inv + 128) >> 8); - refa[(base + x) as usize] = side[(idx + 1) as usize] as i32; - } - } - } else { - for x in n + 1..=2 * n { - refa[(base + x as i32) as usize] = main[x] as i32; - } - } - let horizontal = mode < 18; - for j in 0..n { - let pos = (j as i32 + 1) * angle; - let idx = pos >> 5; - let fact = pos & 31; - for i in 0..n { - let k = (base + i as i32 + idx + 1) as usize; - let v = if fact != 0 { ((32 - fact) * refa[k] + fact * refa[k + 1] + 16) >> 5 } else { refa[k] }; - if horizontal { - out[i * os + j] = v as u16; - } else { - out[j * os + i] = v as u16; - } - } - } - if edge_filters && n < 32 { - if mode == 26 { - for y in 0..n { - let v = r.top[1] as i32 + ((r.left[1 + y] as i32 - r.left[0] as i32) >> 1); - out[y * os] = v.clamp(0, max) as u16; - } - } else if mode == 10 { - for x in 0..n { - let v = r.left[1] as i32 + ((r.top[1 + x] as i32 - r.top[0] as i32) >> 1); - out[x] = v.clamp(0, max) as u16; - } - } - } - } - } -} - -#[cfg(test)] -mod tests { - use super::*; - - fn refs(f: impl Fn(i32) -> u16) -> Refs { - let mut r = Refs::default(); - r.left[0] = f(0); - r.top[0] = f(0); - for i in 1..129 { - r.left[i] = f(-(i as i32)); - r.top[i] = f(i as i32); - } - r - } - - #[test] - fn dc_and_planar_flat() { - let r = refs(|_| 100); - let mut out = [0u16; 64]; - predict(&r, 1, 8, true, 8, &mut out, 8); - assert!(out.iter().all(|&v| v == 100)); - predict(&r, 0, 8, true, 8, &mut out, 8); - assert!(out.iter().all(|&v| v == 100)); - } - - #[test] - fn pure_vertical_and_horizontal() { - let r = refs(|i| (128 + i) as u16); - let mut out = [0u16; 16]; - predict(&r, 26, 4, false, 8, &mut out, 4); - for y in 0..4 { - for x in 0..4 { - assert_eq!(out[y * 4 + x], r.top[1 + x]); - } - } - predict(&r, 10, 4, false, 8, &mut out, 4); - for y in 0..4 { - for x in 0..4 { - assert_eq!(out[y * 4 + x], r.left[1 + y]); - } - } - // diagonal mode 34 reads top[x + y + 2] - predict(&r, 34, 4, false, 8, &mut out, 4); - assert_eq!(out[4 + 1], r.top[1 + 1 + 1 + 1]); - } - - #[test] - fn substitution() { - let mut r = Refs::default(); - let mut al = [false; 129]; - let at = [false; 129]; - r.left[3] = 77; - al[3] = true; - substitute(&mut r, &al, &at, 4, 8); - assert!(r.left[..=8].iter().all(|&v| v == 77)); - assert!(r.top[..=8].iter().all(|&v| v == 77)); - let mut r = Refs::default(); - substitute(&mut r, &[false; 129], &[false; 129], 4, 10); - assert_eq!(r.top[5], 512); - } -} diff --git a/crates/hevcenc/src/lib.rs b/crates/hevcenc/src/lib.rs deleted file mode 100644 index 1934143a0..000000000 --- a/crates/hevcenc/src/lib.rs +++ /dev/null @@ -1,365 +0,0 @@ -//! Clean-room pure-Rust HEVC (H.265) encoder, implemented from ITU-T Rec. H.265 (ISO/IEC 23008-2). -//! -//! Main and Main 10 profiles, 4:2:0, one slice per picture: IDR pictures and single-reference P -//! pictures (no reordering), 64x64 CTUs with rate-distortion coding-unit decisions (8x8 .. 64x64), -//! intra (35 modes, NxN at 8x8), merge / skip and AMVP inter prediction with quarter-sample motion, -//! DCT / DST transforms and CABAC. The deblocking filter is on; SAO is not used. -//! -//! ``` -//! use effectcraft_hevcenc::{Encoder, EncoderConfig, Frame}; -//! let mut enc = Encoder::new(EncoderConfig::new(64, 48, 30, 1)).unwrap(); -//! let (y, c) = (vec![128u16; 64 * 48], vec![128u16; 32 * 24]); -//! let packet = enc.encode(&Frame { y: &y, u: &c, v: &c, y_stride: 64, uv_stride: 32 }); -//! assert!(packet.keyframe && !packet.data.is_empty()); -//! let _hvcc = enc.hvcc(); -//! ``` - -#![deny(clippy::unwrap_used, clippy::expect_used, clippy::panic, clippy::unimplemented, clippy::todo, clippy::unreachable)] - -mod bitstream; -mod cabac; -mod deblock; -mod frame; -mod inter; -mod intra; -mod me; -mod params; -#[rustfmt::skip] -mod tables; -mod transform; - -use bitstream::{BitWriter, NAL_IDR_N_LP, NAL_TRAIL_R, nal_unit}; -use frame::{FrameCoder, SliceKind}; -use params::{LOG2_MAX_POC_LSB, LOG2_MIN_CB, MAX_MERGE_CAND, SeqParams}; - -/// Profile written in the parameter sets. -#[derive(Clone, Copy, Debug, Default, PartialEq, Eq)] -pub enum Profile { - /// Main: 8-bit 4:2:0. - #[default] - Main, - /// Main 10: 10-bit 4:2:0. - Main10, -} - -impl Profile { - pub fn bit_depth(self) -> u8 { - match self { - Profile::Main => 8, - Profile::Main10 => 10, - } - } - fn idc(self) -> u8 { - match self { - Profile::Main => 1, - Profile::Main10 => 2, - } - } -} - -/// Rate control. -#[derive(Clone, Copy, Debug, PartialEq, Eq)] -pub enum RateControl { - /// Constant QP (0–51; lower is better). - ConstantQp(u8), - /// Average bitrate target in kbit/s (frame-level QP adaptation). - Bitrate { kbps: u32 }, -} - -/// Encoder settings. -#[derive(Clone, Debug, PartialEq)] -pub struct EncoderConfig { - /// Picture size; must be even (4:2:0 cropping is in units of two luma samples). - pub width: u32, - pub height: u32, - /// Frame rate numerator / denominator (VUI timing and rate control). - pub fps_num: u32, - pub fps_den: u32, - pub profile: Profile, - /// `general_level_idc` (30 × level, e.g. 93 = level 3.1); `None` picks the lowest level that fits. - pub level_idc: Option, - pub rate: RateControl, - /// Frames between IDR pictures (1 = all intra, 0 = only the first picture). - pub keyint: u32, - /// BT.709 limited range is signalled in the VUI unless this is set. - pub full_range: bool, - /// The VUI colour description (ITU-T H.273 code points); BT.709 by default. - pub colour_primaries: u8, - pub transfer_characteristics: u8, - pub matrix_coeffs: u8, -} - -impl EncoderConfig { - pub fn new(width: u32, height: u32, fps_num: u32, fps_den: u32) -> Self { - EncoderConfig { - width, - height, - fps_num, - fps_den, - profile: Profile::Main, - level_idc: None, - rate: RateControl::ConstantQp(28), - keyint: 60, - full_range: false, - colour_primaries: 1, - transfer_characteristics: 1, - matrix_coeffs: 1, - } - } -} - -/// One input picture, 4:2:0, samples at the profile's bit depth (8-bit values 0–255 for Main). -/// Luma is `width` x `height`, chroma `width / 2` x `height / 2`. -#[derive(Clone, Copy, Debug)] -pub struct Frame<'a> { - pub y: &'a [u16], - pub u: &'a [u16], - pub v: &'a [u16], - pub y_stride: usize, - pub uv_stride: usize, -} - -/// One coded picture (an access unit): NAL units, each prefixed by a 4-byte big-endian length -/// (ISO/IEC 14496-15 sample format; parameter sets are only in [`Encoder::hvcc`]). -#[derive(Clone, Debug, PartialEq, Eq)] -pub struct Packet { - pub data: Vec, - /// IDR picture. - pub keyframe: bool, -} - -#[derive(Debug, PartialEq, Eq)] -pub enum Error { - InvalidConfig(String), -} - -impl std::fmt::Display for Error { - fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { - match self { - Error::InvalidConfig(s) => write!(f, "invalid HEVC encoder config: {s}"), - } - } -} - -impl std::error::Error for Error {} - -/// Frame-level rate control for [`RateControl::Bitrate`]: a bits ≈ X / Qstep model per picture type -/// and a buffer that spreads over- / undershoot over the following pictures. -struct RateCtl { - target: f64, - /// sum(actual) - sum(target), bits. - buffer: f64, - /// Complexity X = bits * Qstep for I and P pictures. - x: [Option; 2], - last_qp: [i32; 2], - init_qp: i32, -} - -fn qstep(qp: i32) -> f64 { - 2f64.powf((qp as f64 - 4.0) / 6.0) -} - -impl RateCtl { - fn new(kbps: u32, cfg: &EncoderConfig) -> Self { - let fps = cfg.fps_num as f64 / cfg.fps_den.max(1) as f64; - let target = kbps as f64 * 1000.0 / fps.max(0.001); - let bpp = target / (cfg.width as f64 * cfg.height as f64); - let init_qp = (28.0 - 6.0 * (bpp / 0.08).log2()).round().clamp(10.0, 46.0) as i32; - RateCtl { target, buffer: 0.0, x: [None, None], last_qp: [init_qp - 3, init_qp], init_qp } - } - - fn qp(&self, idr: bool) -> i32 { - let t = idr as usize ^ 1; // 0 = I, 1 = P - let tp = (self.target - self.buffer / 12.0).clamp(0.3 * self.target, 2.0 * self.target); - let want = if idr { 4.0 * tp } else { tp }; - let x = match (self.x[t], self.x[0]) { - (Some(x), _) => x, - (None, Some(xi)) => xi / 5.0, - (None, None) => return if idr { self.init_qp - 3 } else { self.init_qp }, - }; - let qp = (4.0 + 6.0 * (x / want).log2()).round() as i32; - qp.clamp(self.last_qp[t] - 6, self.last_qp[t] + 6).clamp(1, 51) - } - - fn update(&mut self, idr: bool, qp: i32, bits: f64) { - let t = idr as usize ^ 1; - let x = bits * qstep(qp); - self.x[t] = Some(match self.x[t] { - Some(old) => 0.5 * old + 0.5 * x, - None => x, - }); - self.last_qp[t] = qp; - self.buffer += bits - self.target; - } -} - -/// HEVC encoder. Pictures come out in input order (no reordering: I and P only). -pub struct Encoder { - cfg: EncoderConfig, - seq: SeqParams, - sets: [Vec; 3], - frames: u64, - /// POC of the previous picture (relative to the last IDR). - poc: u32, - reference: Option<[Vec; 3]>, - recon: Option<[Vec; 3]>, - rc: Option, -} - -impl Encoder { - pub fn new(cfg: EncoderConfig) -> Result { - if cfg.width == 0 || cfg.height == 0 { - return Err(Error::InvalidConfig("empty frame".into())); - } - if !cfg.width.is_multiple_of(2) || !cfg.height.is_multiple_of(2) { - return Err(Error::InvalidConfig(format!("{}x{}: 4:2:0 HEVC needs even dimensions", cfg.width, cfg.height))); - } - if cfg.width > 16384 || cfg.height > 16384 { - return Err(Error::InvalidConfig("picture larger than 16384 samples".into())); - } - if cfg.fps_num == 0 || cfg.fps_den == 0 { - return Err(Error::InvalidConfig("zero frame rate".into())); - } - match cfg.rate { - RateControl::ConstantQp(q) if q > 51 => return Err(Error::InvalidConfig(format!("QP {q} > 51"))), - RateControl::Bitrate { kbps: 0 } => return Err(Error::InvalidConfig("zero bitrate".into())), - _ => {} - } - let min_cb = 1u32 << LOG2_MIN_CB; - let (coded_width, coded_height) = (cfg.width.div_ceil(min_cb) * min_cb, cfg.height.div_ceil(min_cb) * min_cb); - let kbps = match cfg.rate { - RateControl::Bitrate { kbps } => Some(kbps), - RateControl::ConstantQp(_) => None, - }; - let level_idc = cfg.level_idc.unwrap_or_else(|| params::choose_level(coded_width, coded_height, cfg.fps_num, cfg.fps_den, kbps)); - let seq = SeqParams { - coded_width, - coded_height, - width: cfg.width, - height: cfg.height, - bit_depth: cfg.profile.bit_depth() as u32, - profile_idc: cfg.profile.idc(), - level_idc, - full_range: cfg.full_range, - colour_primaries: cfg.colour_primaries, - transfer_characteristics: cfg.transfer_characteristics, - matrix_coeffs: cfg.matrix_coeffs, - fps_num: cfg.fps_num, - fps_den: cfg.fps_den, - }; - let sets = [params::vps(&seq), params::sps(&seq), params::pps()]; - let rc = kbps.map(|k| RateCtl::new(k, &cfg)); - Ok(Encoder { cfg, seq, sets, frames: 0, poc: 0, reference: None, recon: None, rc }) - } - - pub fn config(&self) -> &EncoderConfig { - &self.cfg - } - - /// VPS, SPS, PPS NAL units (2-byte NAL header, emulation prevention, no start codes or length - /// prefixes). - pub fn parameter_sets(&self) -> [Vec; 3] { - self.sets.clone() - } - - /// HEVCDecoderConfigurationRecord (`hvcC` payload) with the parameter sets. - pub fn hvcc(&self) -> Vec { - params::hvcc(&self.seq, &self.sets) - } - - /// `general_level_idc` written in the parameter sets. - pub fn level_idc(&self) -> u8 { - self.seq.level_idc - } - - /// Reconstruction of the last encoded picture (what a decoder outputs), cropped planes - /// `[Y, Cb, Cr]` with strides `width` and `width / 2`. For tests. - #[doc(hidden)] - pub fn last_reconstruction(&self) -> Option<&[Vec; 3]> { - self.recon.as_ref() - } - - /// Copy the input into coded-size planes, replicating the right / bottom edges. - fn pad_input(&self, f: &Frame) -> [Vec; 3] { - let max = (1u32 << self.seq.bit_depth) - 1; - let mut out: [Vec; 3] = Default::default(); - for (c, plane) in out.iter_mut().enumerate() { - let (src, stride, w, h, cw, ch) = if c == 0 { - (f.y, f.y_stride, self.seq.width, self.seq.height, self.seq.coded_width, self.seq.coded_height) - } else { - let s = if c == 1 { f.u } else { f.v }; - (s, f.uv_stride, self.seq.width / 2, self.seq.height / 2, self.seq.coded_width / 2, self.seq.coded_height / 2) - }; - let (w, h, cw, ch) = (w as usize, h as usize, cw as usize, ch as usize); - assert!(stride >= w && src.len() >= (h - 1) * stride + w, "HEVC encoder: plane {c} smaller than {w}x{h} (stride {stride})"); - plane.reserve(cw * ch); - for y in 0..ch { - let row = &src[y.min(h - 1) * stride..][..w]; - plane.extend(row.iter().map(|&v| (v as u32).min(max) as u16)); - let last = plane.last().copied().unwrap_or(0); - plane.extend(std::iter::repeat_n(last, cw - w)); - } - } - out - } - - /// Encodes one picture. - pub fn encode(&mut self, frame: &Frame) -> Packet { - let src = self.pad_input(frame); - let keyint = self.cfg.keyint as u64; - let idr = self.reference.is_none() || (keyint > 0 && self.frames.is_multiple_of(keyint)); - self.poc = if idr { 0 } else { self.poc + 1 }; - let qp = match (&self.rc, self.cfg.rate) { - (Some(rc), _) => rc.qp(idr), - (None, RateControl::ConstantQp(q)) => q as i32, - (None, RateControl::Bitrate { .. }) => 28, - }; - let (w, h) = (self.seq.coded_width as usize, self.seq.coded_height as usize); - let bd = self.seq.bit_depth; - let kind = if idr { SliceKind::I } else { SliceKind::P }; - let me = if idr { None } else { self.reference.as_ref().map(|r| me::MeRef::new(&r[0], w, h, bd)) }; - let refp = if idr { None } else { self.reference.as_ref() }; - let mut fc = FrameCoder::new(frame::FrameConfig { bit_depth: bd, width: w, height: h, kind, slice_qp: qp }, &src, refp, me.as_ref()); - - // slice_segment_header() (7.3.6.1) - let mut bw = BitWriter::new(); - bw.put(1, 1); // first_slice_segment_in_pic_flag - if idr { - bw.put(0, 1); // no_output_of_prior_pics_flag - } - bw.ue(0); // slice_pic_parameter_set_id - bw.ue(if idr { 2 } else { 1 }); // slice_type I / P - if !idr { - bw.put(self.poc & ((1 << LOG2_MAX_POC_LSB) - 1), LOG2_MAX_POC_LSB); // slice_pic_order_cnt_lsb - bw.put(1, 1); // short_term_ref_pic_set_sps_flag (the only SPS set, no index coded) - bw.put(0, 1); // num_ref_idx_active_override_flag - bw.ue(5 - MAX_MERGE_CAND as u32); // five_minus_max_num_merge_cand - } - bw.se(qp - 26); // slice_qp_delta (init_qp = 26) - // byte_alignment() - bw.put(1, 1); - bw.align_zero(); - - let ctx = cabac::init_contexts(qp, if idr { 0 } else { 1 }); - let mut enc = cabac::CabacEncoder::new(bw, ctx); - fc.encode(&mut enc); - fc.deblock(); - let rbsp = enc.finish().into_bytes(); - let nal = nal_unit(if idr { NAL_IDR_N_LP } else { NAL_TRAIL_R }, &rbsp); - let mut data = Vec::with_capacity(nal.len() + 4); - data.extend_from_slice(&(nal.len() as u32).to_be_bytes()); - data.extend_from_slice(&nal); - - if let Some(rc) = &mut self.rc { - rc.update(idr, qp, data.len() as f64 * 8.0); - } - // output picture: cropped reconstruction - let rec = std::mem::take(&mut fc.rec); - let (ow, oh) = (self.seq.width as usize, self.seq.height as usize); - let crop = |p: &[u16], stride: usize, w: usize, h: usize| -> Vec { (0..h).flat_map(|y| p[y * stride..y * stride + w].iter().copied()).collect() }; - self.recon = Some([crop(&rec[0], w, ow, oh), crop(&rec[1], w / 2, ow / 2, oh / 2), crop(&rec[2], w / 2, ow / 2, oh / 2)]); - self.reference = Some(rec); - self.frames += 1; - Packet { data, keyframe: idr } - } -} diff --git a/crates/hevcenc/src/me.rs b/crates/hevcenc/src/me.rs deleted file mode 100644 index 5f4cb2ce3..000000000 --- a/crates/hevcenc/src/me.rs +++ /dev/null @@ -1,144 +0,0 @@ -//! Motion search reference: the previous reconstructed luma plane padded by edge replication, plus -//! half-sample planes interpolated with the 8-tap half-sample filter. Quarter-sample positions are -//! approximated by averaging neighbouring half-grid samples. Used for estimation only; the coded -//! prediction always uses the normative interpolation in `inter`. - -use crate::tables::LUMA_FILTER; - -const MARGIN: i32 = 80; - -pub struct MeRef { - /// [integer, horizontal half, vertical half, diagonal half] samples, each `stride` x `rows`. - planes: [Vec; 4], - stride: usize, - rows: usize, - w: i32, - h: i32, -} - -impl MeRef { - pub fn new(y: &[u16], w: usize, h: usize, bd: u32) -> MeRef { - let m = MARGIN as usize; - let (stride, rows) = (w + 2 * m, h + 2 * m); - let mut pad = vec![0u16; stride * rows]; - for r in 0..rows { - let sy = (r as i32 - MARGIN).clamp(0, h as i32 - 1) as usize; - let src = &y[sy * w..sy * w + w]; - let row = &mut pad[r * stride..r * stride + stride]; - row[..m].fill(src[0]); - row[m..m + w].copy_from_slice(src); - row[m + w..].fill(src[w - 1]); - } - let max = (1i32 << bd) - 1; - let f = &LUMA_FILTER[2]; - let filt_h = |p: &[u16]| -> Vec { - let mut o = vec![0u16; stride * rows]; - for r in 0..rows { - let row = &p[r * stride..r * stride + stride]; - for x in 0..stride { - let mut s = 0i32; - for k in 0..8 { - let xx = (x as i32 - 3 + k as i32).clamp(0, stride as i32 - 1) as usize; - s += f[k] as i32 * row[xx] as i32; - } - o[r * stride + x] = ((s + 32) >> 6).clamp(0, max) as u16; - } - } - o - }; - let filt_v = |p: &[u16]| -> Vec { - let mut o = vec![0u16; stride * rows]; - let mut acc = vec![0i32; stride]; - for r in 0..rows { - acc.fill(0); - for k in 0..8 { - let rr = (r as i32 - 3 + k as i32).clamp(0, rows as i32 - 1) as usize; - let src = &p[rr * stride..rr * stride + stride]; - let coef = f[k] as i32; - for (a, &s) in acc.iter_mut().zip(src) { - *a += coef * s as i32; - } - } - for (d, &a) in o[r * stride..r * stride + stride].iter_mut().zip(&acc) { - *d = ((a + 32) >> 6).clamp(0, max) as u16; - } - } - o - }; - let hp = filt_h(&pad); - let vp = filt_v(&pad); - let dp = filt_v(&hp); - MeRef { planes: [pad, hp, vp, dp], stride, rows, w: w as i32, h: h as i32 } - } - - /// Integer motion vector range keeping an n x n block (plus filter margin) inside the padding. - pub fn int_range(&self, x: i32, y: i32, n: usize) -> (i32, i32, i32, i32) { - let n = n as i32; - let lo_x = -MARGIN + 4 - x; - let hi_x = self.w + MARGIN - n - 4 - x; - let lo_y = -MARGIN + 4 - y; - let hi_y = self.h + MARGIN - n - 4 - y; - (lo_x.max(-256), hi_x.min(256), lo_y.max(-256), hi_y.min(256)) - } - - /// SAD of the source block against the integer-position reference block at (rx, ry). - pub fn sad(&self, src: &[u16], ss: usize, rx: i32, ry: i32, n: usize) -> u32 { - let p = &self.planes[0]; - let base = (ry + MARGIN) as usize * self.stride + (rx + MARGIN) as usize; - let mut s = 0u32; - for r in 0..n { - let a = &src[r * ss..r * ss + n]; - let b = &p[base + r * self.stride..base + r * self.stride + n]; - s += a.iter().zip(b).map(|(&x, &y)| (x as i32 - y as i32).unsigned_abs()).sum::(); - } - s - } - - /// Approximate luma prediction of an n x n block at (x, y) with quarter-sample vector `mv`. - pub fn approx_pred(&self, x: i32, y: i32, n: usize, mv: [i16; 2], out: &mut [u16]) { - // per axis: one or two (half-sample fraction, integer offset) taps - let axis = |v: i32| -> [(usize, i32); 2] { - if v & 1 == 0 { - let hv = v >> 1; - [((hv & 1) as usize, hv >> 1); 2] - } else { - let (a, b) = ((v - 1) >> 1, (v + 1) >> 1); - [((a & 1) as usize, a >> 1), ((b & 1) as usize, b >> 1)] - } - }; - let (mx, my) = (mv[0] as i32, mv[1] as i32); - let ax = axis(mx); - let ay = axis(my); - let two = mx & 1 != 0 || my & 1 != 0; - let taps = [(ax[0], ay[0]), (ax[1], ay[1])]; - let ntaps = if two { 2 } else { 1 }; - let mut first = true; - for &((fx, ox), (fy, oy)) in taps.iter().take(ntaps) { - let plane = &self.planes[fx + 2 * fy]; - let (bx, by) = (x + ox + MARGIN, y + oy + MARGIN); - let inside = bx >= 0 && by >= 0 && bx as usize + n <= self.stride && by as usize + n <= self.rows; - for r in 0..n { - let dst = &mut out[r * n..r * n + n]; - if inside { - let o = (by as usize + r) * self.stride + bx as usize; - let src = &plane[o..o + n]; - if first { - dst.copy_from_slice(src); - } else { - for (d, &s) in dst.iter_mut().zip(src) { - *d = ((*d as u32 + s as u32 + 1) >> 1) as u16; - } - } - } else { - let yy = (by + r as i32).clamp(0, self.rows as i32 - 1) as usize; - for (c, d) in dst.iter_mut().enumerate() { - let xx = (bx + c as i32).clamp(0, self.stride as i32 - 1) as usize; - let s = plane[yy * self.stride + xx]; - *d = if first { s } else { ((*d as u32 + s as u32 + 1) >> 1) as u16 }; - } - } - } - first = false; - } - } -} diff --git a/crates/hevcenc/src/params.rs b/crates/hevcenc/src/params.rs deleted file mode 100644 index 89bac938e..000000000 --- a/crates/hevcenc/src/params.rs +++ /dev/null @@ -1,265 +0,0 @@ -//! Parameter sets (7.3.2.1 VPS, 7.3.2.2 SPS with E.2.1 VUI, 7.3.2.3 PPS), profile_tier_level (7.3.3), -//! level selection (Annex A, Table A.8) and the `hvcC` record (ISO/IEC 14496-15 §8.3.3.1). - -use crate::bitstream::{BitWriter, NAL_PPS, NAL_SPS, NAL_VPS, nal_unit}; - -/// Fixed coding structure of the encoder. -pub const LOG2_CTB: u32 = 6; -pub const LOG2_MIN_CB: u32 = 3; -pub const LOG2_MIN_TB: u32 = 2; -pub const LOG2_MAX_TB: u32 = 5; -pub const LOG2_MAX_POC_LSB: u32 = 8; -/// MaxNumMergeCand. -pub const MAX_MERGE_CAND: usize = 5; - -/// Sequence-level settings shared by the parameter-set writers. -#[derive(Clone, Debug)] -pub struct SeqParams { - /// Coded (padded) size, multiples of the minimum CB size. - pub coded_width: u32, - pub coded_height: u32, - /// Output (cropped) size. - pub width: u32, - pub height: u32, - pub bit_depth: u32, - pub profile_idc: u8, - pub level_idc: u8, - pub full_range: bool, - pub colour_primaries: u8, - pub transfer_characteristics: u8, - pub matrix_coeffs: u8, - pub fps_num: u32, - pub fps_den: u32, -} - -/// (level_idc, MaxLumaPs, MaxLumaSr, MaxBR main tier in kbit/s) of Table A.8 (and A.9 for luma rates). -const LEVELS: [(u8, u64, u64, u64); 13] = [ - (30, 36_864, 552_960, 128), - (60, 122_880, 3_686_400, 1_500), - (63, 245_760, 7_372_800, 3_000), - (90, 552_960, 16_588_800, 6_000), - (93, 983_040, 33_177_600, 10_000), - (120, 2_228_224, 66_846_720, 12_000), - (123, 2_228_224, 133_693_440, 20_000), - (150, 8_912_896, 267_386_880, 25_000), - (153, 8_912_896, 534_773_760, 40_000), - (156, 8_912_896, 1_069_547_520, 60_000), - (180, 35_651_584, 1_069_547_520, 60_000), - (183, 35_651_584, 2_139_095_040, 120_000), - (186, 35_651_584, 4_278_190_080, 240_000), -]; - -/// Lowest level whose picture size, sample rate and (when known) bitrate limits fit. -pub fn choose_level(width: u32, height: u32, fps_num: u32, fps_den: u32, kbps: Option) -> u8 { - let ps = width as u64 * height as u64; - let sr = (ps as f64 * fps_num as f64 / fps_den.max(1) as f64).ceil() as u64; - for &(idc, max_ps, max_sr, max_br) in &LEVELS { - let max_dim = ((max_ps * 8) as f64).sqrt() as u64; - if ps <= max_ps && (width as u64) <= max_dim && (height as u64) <= max_dim && sr <= max_sr && kbps.is_none_or(|k| k as u64 <= max_br) { - return idc; - } - } - 186 -} - -fn profile_tier_level(w: &mut BitWriter, p: &SeqParams) { - w.put(0, 2); // general_profile_space - w.put(0, 1); // general_tier_flag (Main tier) - w.put(p.profile_idc as u32, 5); - w.put(compat_flags(p.profile_idc), 32); - w.put(1, 1); // general_progressive_source_flag - w.put(0, 1); // general_interlaced_source_flag - w.put(0, 1); // general_non_packed_constraint_flag - w.put(1, 1); // general_frame_only_constraint_flag - w.put(0, 32); // general_reserved_zero_43bits ... - w.put(0, 11); - w.put(0, 1); // general_inbld_flag / reserved - w.put(p.level_idc as u32, 8); - // sps_max_sub_layers_minus1 == 0: no sub-layer flags -} - -/// general_profile_compatibility_flag[j] as a 32-bit word (flag 0 is the MSB). A Main stream is also -/// Main 10 compatible. -fn compat_flags(profile_idc: u8) -> u32 { - let mut f = 1u32 << (31 - profile_idc as u32); - if profile_idc == 1 { - f |= 1 << (31 - 2); - } - f -} - -pub fn vps(p: &SeqParams) -> Vec { - let mut w = BitWriter::new(); - w.put(0, 4); // vps_video_parameter_set_id - w.put(1, 1); // vps_base_layer_internal_flag - w.put(1, 1); // vps_base_layer_available_flag - w.put(0, 6); // vps_max_layers_minus1 - w.put(0, 3); // vps_max_sub_layers_minus1 - w.put(1, 1); // vps_temporal_id_nesting_flag - w.put(0xffff, 16); - profile_tier_level(&mut w, p); - w.put(1, 1); // vps_sub_layer_ordering_info_present_flag - w.ue(1); // vps_max_dec_pic_buffering_minus1 - w.ue(0); // vps_max_num_reorder_pics - w.ue(0); // vps_max_latency_increase_plus1 - w.put(0, 6); // vps_max_layer_id - w.ue(0); // vps_num_layer_sets_minus1 - w.put(0, 1); // vps_timing_info_present_flag - w.put(0, 1); // vps_extension_flag - w.trailing(); - nal_unit(NAL_VPS, &w.into_bytes()) -} - -pub fn sps(p: &SeqParams) -> Vec { - let mut w = BitWriter::new(); - w.put(0, 4); // sps_video_parameter_set_id - w.put(0, 3); // sps_max_sub_layers_minus1 - w.put(1, 1); // sps_temporal_id_nesting_flag - profile_tier_level(&mut w, p); - w.ue(0); // sps_seq_parameter_set_id - w.ue(1); // chroma_format_idc 4:2:0 - w.ue(p.coded_width); - w.ue(p.coded_height); - let crop = p.coded_width != p.width || p.coded_height != p.height; - w.flag(crop); // conformance_window_flag - if crop { - // offsets in chroma sample units (SubWidthC = SubHeightC = 2) - w.ue(0); - w.ue((p.coded_width - p.width) / 2); - w.ue(0); - w.ue((p.coded_height - p.height) / 2); - } - w.ue(p.bit_depth - 8); // bit_depth_luma_minus8 - w.ue(p.bit_depth - 8); // bit_depth_chroma_minus8 - w.ue(LOG2_MAX_POC_LSB - 4); - w.put(1, 1); // sps_sub_layer_ordering_info_present_flag - w.ue(1); // sps_max_dec_pic_buffering_minus1 - w.ue(0); // sps_max_num_reorder_pics - w.ue(0); // sps_max_latency_increase_plus1 - w.ue(LOG2_MIN_CB - 3); - w.ue(LOG2_CTB - LOG2_MIN_CB); - w.ue(LOG2_MIN_TB - 2); - w.ue(LOG2_MAX_TB - LOG2_MIN_TB); - w.ue(0); // max_transform_hierarchy_depth_inter - w.ue(0); // max_transform_hierarchy_depth_intra - w.put(0, 1); // scaling_list_enabled_flag - w.put(0, 1); // amp_enabled_flag - w.put(0, 1); // sample_adaptive_offset_enabled_flag - w.put(0, 1); // pcm_enabled_flag - w.ue(1); // num_short_term_ref_pic_sets - // st_ref_pic_set(0): one negative picture at delta POC -1, used by the current picture - w.ue(1); // num_negative_pics - w.ue(0); // num_positive_pics - w.ue(0); // delta_poc_s0_minus1 - w.put(1, 1); // used_by_curr_pic_s0_flag - w.put(0, 1); // long_term_ref_pics_present_flag - w.put(0, 1); // sps_temporal_mvp_enabled_flag - w.put(1, 1); // strong_intra_smoothing_enabled_flag - w.put(1, 1); // vui_parameters_present_flag - vui(&mut w, p); - w.put(0, 1); // sps_extension_present_flag - w.trailing(); - nal_unit(NAL_SPS, &w.into_bytes()) -} - -fn vui(w: &mut BitWriter, p: &SeqParams) { - w.put(0, 1); // aspect_ratio_info_present_flag - w.put(0, 1); // overscan_info_present_flag - w.put(1, 1); // video_signal_type_present_flag - w.put(5, 3); // video_format: unspecified - w.flag(p.full_range); - w.put(1, 1); // colour_description_present_flag - w.put(p.colour_primaries as u32, 8); // colour_primaries - w.put(p.transfer_characteristics as u32, 8); // transfer_characteristics - w.put(p.matrix_coeffs as u32, 8); // matrix_coeffs - w.put(0, 1); // chroma_loc_info_present_flag - w.put(0, 1); // neutral_chroma_indication_flag - w.put(0, 1); // field_seq_flag - w.put(0, 1); // frame_field_info_present_flag - w.put(0, 1); // default_display_window_flag - w.put(1, 1); // vui_timing_info_present_flag - w.put(p.fps_den, 32); // vui_num_units_in_tick - w.put(p.fps_num, 32); // vui_time_scale - w.put(0, 1); // vui_poc_proportional_to_timing_flag - w.put(0, 1); // vui_hrd_parameters_present_flag - w.put(0, 1); // bitstream_restriction_flag -} - -pub fn pps() -> Vec { - let mut w = BitWriter::new(); - w.ue(0); // pps_pic_parameter_set_id - w.ue(0); // pps_seq_parameter_set_id - w.put(0, 1); // dependent_slice_segments_enabled_flag - w.put(0, 1); // output_flag_present_flag - w.put(0, 3); // num_extra_slice_header_bits - w.put(1, 1); // sign_data_hiding_enabled_flag - w.put(0, 1); // cabac_init_present_flag - w.ue(0); // num_ref_idx_l0_default_active_minus1 - w.ue(0); // num_ref_idx_l1_default_active_minus1 - w.se(0); // init_qp_minus26 - w.put(0, 1); // constrained_intra_pred_flag - w.put(0, 1); // transform_skip_enabled_flag - w.put(0, 1); // cu_qp_delta_enabled_flag - w.se(0); // pps_cb_qp_offset - w.se(0); // pps_cr_qp_offset - w.put(0, 1); // pps_slice_chroma_qp_offsets_present_flag - w.put(0, 1); // weighted_pred_flag - w.put(0, 1); // weighted_bipred_flag - w.put(0, 1); // transquant_bypass_enabled_flag - w.put(0, 1); // tiles_enabled_flag - w.put(0, 1); // entropy_coding_sync_enabled_flag - w.put(0, 1); // pps_loop_filter_across_slices_enabled_flag - w.put(1, 1); // deblocking_filter_control_present_flag - w.put(0, 1); // deblocking_filter_override_enabled_flag - w.put(0, 1); // pps_deblocking_filter_disabled_flag - w.se(0); // pps_beta_offset_div2 - w.se(0); // pps_tc_offset_div2 - w.put(0, 1); // pps_scaling_list_data_present_flag - w.put(0, 1); // lists_modification_present_flag - w.ue(0); // log2_parallel_merge_level_minus2 - w.put(0, 1); // slice_segment_header_extension_present_flag - w.put(0, 1); // pps_extension_present_flag - w.trailing(); - nal_unit(NAL_PPS, &w.into_bytes()) -} - -/// HEVCDecoderConfigurationRecord with one VPS, SPS and PPS (lengthSizeMinusOne = 3). -pub fn hvcc(p: &SeqParams, sets: &[Vec; 3]) -> Vec { - let mut o = vec![1u8]; // configurationVersion - o.push(p.profile_idc); // profile_space 0, tier 0, profile_idc - o.extend_from_slice(&compat_flags(p.profile_idc).to_be_bytes()); - // general_constraint_indicator_flags (48 bits): progressive, interlaced, non_packed, frame_only - o.extend_from_slice(&[0x90, 0, 0, 0, 0, 0]); - o.push(p.level_idc); - o.extend_from_slice(&[0xf0, 0x00]); // reserved + min_spatial_segmentation_idc 0 - o.push(0xfc); // reserved + parallelismType 0 - o.push(0xfc | 1); // reserved + chroma_format_idc 1 - o.push(0xf8 | (p.bit_depth - 8) as u8); - o.push(0xf8 | (p.bit_depth - 8) as u8); - o.extend_from_slice(&[0, 0]); // avgFrameRate unspecified - // constantFrameRate 0, numTemporalLayers 1, temporalIdNested 1, lengthSizeMinusOne 3 - o.push((1 << 3) | (1 << 2) | 3); - o.push(3); // numOfArrays - for (set, ty) in sets.iter().zip([NAL_VPS, NAL_SPS, NAL_PPS]) { - o.push(0x80 | ty); // array_completeness 1 - o.extend_from_slice(&1u16.to_be_bytes()); - o.extend_from_slice(&(set.len() as u16).to_be_bytes()); - o.extend_from_slice(set); - } - o -} - -#[cfg(test)] -mod tests { - use super::*; - - #[test] - fn levels() { - assert_eq!(choose_level(1920, 1080, 30, 1, None), 120); - assert_eq!(choose_level(1920, 1080, 60, 1, None), 123); - assert_eq!(choose_level(3840, 2160, 30, 1, None), 150); - assert_eq!(choose_level(1280, 720, 30, 1, None), 93); - assert_eq!(choose_level(64, 64, 30, 1, None), 30); - assert_eq!(choose_level(1920, 1080, 30, 1, Some(15_000)), 123); - } -} diff --git a/crates/hevcenc/src/tables.rs b/crates/hevcenc/src/tables.rs deleted file mode 100644 index 7c2fc4470..000000000 --- a/crates/hevcenc/src/tables.rs +++ /dev/null @@ -1,236 +0,0 @@ -//! Constant tables of ITU-T H.265, mirrored from the first-party, spec-derived `filmcraft-hevc` -//! decoder (same organisation, MIT OR Apache-2.0): CABAC context layout and initValues (9.3.2.2), -//! rangeTabLPS / transIdx (Tables 9-52, 9-53), scans (6.5.3 - 6.5.5), transform matrices (8.6.4.2), -//! intra angles (Tables 8-5, 8-6), interpolation filters (8.5.3.3.3) and the chroma QP mapping (Table 8-10). - -#![allow(dead_code)] - -/// Table 9-52 (same values as H.264) rangeTabLPS[pStateIdx][qCodIRangeIdx]. -#[rustfmt::skip] -pub static RANGE_TAB_LPS: [[u8; 4]; 64] = [ - [128, 176, 208, 240], [128, 167, 197, 227], [128, 158, 187, 216], [123, 150, 178, 205], - [116, 142, 169, 195], [111, 135, 160, 185], [105, 128, 152, 175], [100, 122, 144, 166], - [95, 116, 137, 158], [90, 110, 130, 150], [85, 104, 123, 142], [81, 99, 117, 135], - [77, 94, 111, 128], [73, 89, 105, 122], [69, 85, 100, 116], [66, 80, 95, 110], - [62, 76, 90, 104], [59, 72, 86, 99], [56, 69, 81, 94], [53, 65, 77, 89], - [51, 62, 73, 85], [48, 59, 69, 80], [46, 56, 66, 76], [43, 53, 63, 72], - [41, 50, 59, 69], [39, 48, 56, 65], [37, 45, 54, 62], [35, 43, 51, 59], - [33, 41, 48, 56], [32, 39, 46, 53], [30, 37, 43, 50], [29, 35, 41, 48], - [27, 33, 39, 45], [26, 31, 37, 43], [24, 30, 35, 41], [23, 28, 33, 39], - [22, 27, 32, 37], [21, 26, 30, 35], [20, 24, 29, 33], [19, 23, 27, 31], - [18, 22, 26, 30], [17, 21, 25, 28], [16, 20, 23, 27], [15, 19, 22, 25], - [14, 18, 21, 24], [14, 17, 20, 23], [13, 16, 19, 22], [12, 15, 18, 21], - [12, 14, 17, 20], [11, 14, 16, 19], [11, 13, 15, 18], [10, 12, 15, 17], - [10, 12, 14, 16], [9, 11, 13, 15], [9, 11, 12, 14], [8, 10, 12, 14], - [8, 9, 11, 13], [7, 9, 11, 12], [7, 9, 10, 12], [7, 8, 10, 11], - [6, 8, 9, 11], [6, 7, 9, 10], [6, 7, 8, 9], [2, 2, 2, 2], -]; - -/// Table 9-53 transIdxLPS. -#[rustfmt::skip] -static TRANS_IDX_LPS: [u8; 64] = [ - 0, 0, 1, 2, 2, 4, 4, 5, 6, 7, 8, 9, 9, 11, 11, 12, - 13, 13, 15, 15, 16, 16, 18, 18, 19, 19, 21, 21, 22, 22, 23, 24, - 24, 25, 26, 26, 27, 27, 28, 29, 29, 30, 30, 30, 31, 32, 32, 33, - 33, 33, 34, 34, 35, 35, 35, 36, 36, 36, 37, 37, 37, 38, 38, 63, -]; - -/// Table 9-53 transIdxMPS. -#[rustfmt::skip] -static TRANS_IDX_MPS: [u8; 64] = [ - 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, - 17, 18, 19, 20, 21, 22, 23, 24, 25, 26, 27, 28, 29, 30, 31, 32, - 33, 34, 35, 36, 37, 38, 39, 40, 41, 42, 43, 44, 45, 46, 47, 48, - 49, 50, 51, 52, 53, 54, 55, 56, 57, 58, 59, 60, 61, 62, 62, 63, -]; - -/// Combined state transition: index = (pStateIdx << 1 | valMPS) * 2 + bin_is_lps. -/// Each context is stored as `pStateIdx << 1 | valMPS`. -pub static NEXT_STATE: [[u8; 2]; 128] = { - let mut t = [[0u8; 2]; 128]; - let mut s = 0; - while s < 128 { - let p = s >> 1; - let mps = s & 1; - // MPS path - t[s][0] = (TRANS_IDX_MPS[p] << 1) | mps as u8; - // LPS path - let new_mps = if p == 0 { 1 - mps } else { mps }; - t[s][1] = (TRANS_IDX_LPS[p] << 1) | new_mps as u8; - s += 1; - } - t -}; - -/// Up-right diagonal scan (6.5.3) of a `N x N` block as (x, y) pairs. -const fn diag(n: usize) -> [(u8, u8); L] { - let mut out = [(0u8, 0u8); L]; - let mut i = 0; - let mut x: i32 = 0; - let mut y: i32 = 0; - while i < L { - while y >= 0 { - if (x as usize) < n && (y as usize) < n { - out[i] = (x as u8, y as u8); - i += 1; - } - y -= 1; - x += 1; - } - y = x; - x = 0; - } - out -} - -/// Horizontal (6.5.4, `vertical == false`) or vertical (6.5.5) scan. -const fn hv(n: usize, vertical: bool) -> [(u8, u8); L] { - let mut out = [(0u8, 0u8); L]; - let mut i = 0; - while i < L { - let (a, b) = ((i % n) as u8, (i / n) as u8); - out[i] = if vertical { (b, a) } else { (a, b) }; - i += 1; - } - out -} - -pub static SCAN_1: [[(u8, u8); 1]; 3] = [[(0, 0)], [(0, 0)], [(0, 0)]]; -pub static SCAN_2: [[(u8, u8); 4]; 3] = [diag::<4>(2), hv::<4>(2, false), hv::<4>(2, true)]; -pub static SCAN_4: [[(u8, u8); 16]; 3] = [diag::<16>(4), hv::<16>(4, false), hv::<16>(4, true)]; -pub static SCAN_8: [[(u8, u8); 64]; 3] = [diag::<64>(8), hv::<64>(8, false), hv::<64>(8, true)]; - -/// ScanOrder[log2BlockSize][scanIdx] (log2BlockSize 0..=3). -#[inline] -pub fn scan_order(log2: u32, scan_idx: usize) -> &'static [(u8, u8)] { - match log2 { - 0 => &SCAN_1[scan_idx], - 1 => &SCAN_2[scan_idx], - 2 => &SCAN_4[scan_idx], - _ => &SCAN_8[scan_idx], - } -} - -/// Up-right diagonal scan for log2 size 2 or 3. -pub fn scan_diag(log2: u32) -> &'static [(u8, u8)] { - scan_order(log2, 0) -} - -/// Table 8-10: QpC as a function of qPi (ChromaArrayType 1). -#[inline] -pub fn qpc_420(qpi: i32) -> i32 { - if qpi < 30 { - qpi - } else if qpi > 43 { - qpi - 6 - } else { - QPC_30_43[(qpi - 30) as usize] as i32 - } -} - -/// Table 9-50 ctxIdxMap for 4x4 sig_coeff_flag contexts. -pub static CTX_IDX_MAP: [u8; 16] = [0, 1, 4, 5, 2, 3, 4, 5, 6, 6, 8, 8, 7, 7, 8, 8]; - -/// levelScale (8-309). -pub static LEVEL_SCALE: [i32; 6] = [40, 45, 51, 57, 64, 72]; - -/// DST-VII 4x4 matrix (8.6.4.2, trType 1). -pub static DST_MATRIX: [[i8; 4]; 4] = [[29, 55, 74, 84], [74, 74, 0, -74], [84, -29, -74, 55], [55, -84, 74, -29]]; - - -// CABAC context layout: offset of each syntax element in the context array (Table 9-4). -pub const SAO_MERGE: usize = 0; -pub const SAO_TYPE: usize = 1; -pub const SPLIT_CU: usize = 2; -pub const CU_TRANSQUANT_BYPASS: usize = 5; -pub const CU_SKIP: usize = 6; -pub const PRED_MODE: usize = 9; -pub const PART_MODE: usize = 10; -pub const PREV_INTRA_LUMA: usize = 14; -pub const INTRA_CHROMA: usize = 15; -pub const RQT_ROOT_CBF: usize = 16; -pub const MERGE_FLAG: usize = 17; -pub const MERGE_IDX: usize = 18; -pub const INTER_PRED_IDC: usize = 19; -pub const REF_IDX: usize = 24; -pub const MVP_FLAG: usize = 26; -pub const SPLIT_TRANSFORM: usize = 27; -pub const CBF_LUMA: usize = 30; -pub const CBF_CHROMA: usize = 32; -pub const ABS_MVD_GT0: usize = 37; -pub const ABS_MVD_GT1: usize = 38; -pub const CU_QP_DELTA_ABS: usize = 39; -pub const TRANSFORM_SKIP: usize = 41; -pub const LAST_X_PREFIX: usize = 43; -pub const LAST_Y_PREFIX: usize = 61; -pub const CODED_SUB_BLOCK: usize = 79; -pub const SIG_COEFF: usize = 83; -pub const GT1: usize = 127; -pub const GT2: usize = 151; -pub const EXPLICIT_RDPCM: usize = 157; -pub const EXPLICIT_RDPCM_DIR: usize = 159; -pub const CU_CHROMA_QP_OFFSET_FLAG: usize = 161; -pub const CU_CHROMA_QP_OFFSET_IDX: usize = 162; -pub const NUM_CTX: usize = 163; - -/// initValue per initType (9.3.2.2). -pub static CABAC_INIT: [[u8; NUM_CTX]; 3] = [ - [153, 200, 139, 141, 157, 154, 154, 154, 154, 154, 184, 154, 154, 154, 184, 63, 154, 154, 154, 154, 154, 154, 154, 154, 154, 154, 154, 153, 138, 138, 111, 141, 94, 138, 182, 154, 154, 154, 154, 154, 154, 139, 139, 110, 110, 124, 125, 140, 153, 125, 127, 140, 109, 111, 143, 127, 111, 79, 108, 123, 63, 110, 110, 124, 125, 140, 153, 125, 127, 140, 109, 111, 143, 127, 111, 79, 108, 123, 63, 91, 171, 134, 141, 111, 111, 125, 110, 110, 94, 124, 108, 124, 107, 125, 141, 179, 153, 125, 107, 125, 141, 179, 153, 125, 107, 125, 141, 179, 153, 125, 140, 139, 182, 182, 152, 136, 152, 136, 153, 136, 139, 111, 136, 139, 111, 141, 111, 140, 92, 137, 138, 140, 152, 138, 139, 153, 74, 149, 92, 139, 107, 122, 152, 140, 179, 166, 182, 140, 227, 122, 197, 138, 153, 136, 167, 152, 152, 154, 154, 154, 154, 154, 154], - [153, 185, 107, 139, 126, 154, 197, 185, 201, 149, 154, 139, 154, 154, 154, 152, 79, 110, 122, 95, 79, 63, 31, 31, 153, 153, 168, 124, 138, 94, 153, 111, 149, 107, 167, 154, 154, 140, 198, 154, 154, 139, 139, 125, 110, 94, 110, 95, 79, 125, 111, 110, 78, 110, 111, 111, 95, 94, 108, 123, 108, 125, 110, 94, 110, 95, 79, 125, 111, 110, 78, 110, 111, 111, 95, 94, 108, 123, 108, 121, 140, 61, 154, 155, 154, 139, 153, 139, 123, 123, 63, 153, 166, 183, 140, 136, 153, 154, 166, 183, 140, 136, 153, 154, 166, 183, 140, 136, 153, 154, 170, 153, 123, 123, 107, 121, 107, 121, 167, 151, 183, 140, 151, 183, 140, 140, 140, 154, 196, 196, 167, 154, 152, 167, 182, 182, 134, 149, 136, 153, 121, 136, 137, 169, 194, 166, 167, 154, 167, 137, 182, 107, 167, 91, 122, 107, 167, 139, 139, 139, 139, 154, 154], - [153, 160, 107, 139, 126, 154, 197, 185, 201, 134, 154, 139, 154, 154, 183, 152, 79, 154, 137, 95, 79, 63, 31, 31, 153, 153, 168, 224, 167, 122, 153, 111, 149, 92, 167, 154, 154, 169, 198, 154, 154, 139, 139, 125, 110, 124, 110, 95, 94, 125, 111, 111, 79, 125, 126, 111, 111, 79, 108, 123, 93, 125, 110, 124, 110, 95, 94, 125, 111, 111, 79, 125, 126, 111, 111, 79, 108, 123, 93, 121, 140, 61, 154, 170, 154, 139, 153, 139, 123, 123, 63, 124, 166, 183, 140, 136, 153, 154, 166, 183, 140, 136, 153, 154, 166, 183, 140, 136, 153, 154, 170, 153, 138, 138, 122, 121, 122, 121, 167, 151, 183, 140, 151, 183, 140, 140, 140, 154, 196, 167, 167, 154, 152, 167, 182, 182, 134, 149, 136, 153, 121, 136, 122, 169, 208, 166, 167, 154, 152, 167, 182, 107, 167, 91, 107, 107, 167, 139, 139, 139, 139, 154, 154], -]; - - -/// Table 8-5: intraPredAngle for predModeIntra 0..34 (0 for planar / DC). -pub static INTRA_PRED_ANGLE: [i8; 35] = [0, 0, 32, 26, 21, 17, 13, 9, 5, 2, 0, -2, -5, -9, -13, -17, -21, -26, -32, -26, -21, -17, -13, -9, -5, -2, 0, 2, 5, 9, 13, 17, 21, 26, 32]; -/// Table 8-6: invAngle for predModeIntra 11..25 (index = mode - 11). -pub static INV_ANGLE: [i16; 15] = [-4096, -1638, -910, -630, -482, -390, -315, -256, -315, -390, -482, -630, -910, -1638, -4096]; - -/// Table 8-10: QpC for qPi = 30..43 (qPi < 30: QpC = qPi; qPi > 43: QpC = qPi - 6). -pub static QPC_30_43: [u8; 14] = [29, 30, 31, 32, 33, 33, 34, 34, 35, 35, 36, 36, 37, 37]; - - -/// Luma 8-tap interpolation filter fL[xFrac][k] (8-236..8-238), taps at offsets -3..4. -pub static LUMA_FILTER: [[i8; 8]; 4] = [[0, 0, 0, 64, 0, 0, 0, 0], [-1, 4, -10, 58, 17, -5, 1, 0], [-1, 4, -11, 40, 40, -11, 4, -1], [0, 1, -5, 17, 58, -10, 4, -1]]; -/// Chroma 4-tap interpolation filter fC[xFrac][k] (8-255..8-261), taps at offsets -1..2. -pub static CHROMA_FILTER: [[i8; 4]; 8] = [[0, 64, 0, 0], [-2, 58, 10, -2], [-4, 54, 16, -2], [-6, 46, 28, -4], [-4, 36, 36, -4], [-4, 28, 46, -6], [-2, 16, 54, -4], [-2, 10, 58, -2]]; - -/// DCT matrix (8-319, 8-321): TRANS_MATRIX[n][m] = transMatrix[m][n] (row n = basis function). -pub static TRANS_MATRIX: [[i8; 32]; 32] = [ - [64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64], - [90, 90, 88, 85, 82, 78, 73, 67, 61, 54, 46, 38, 31, 22, 13, 4, -4, -13, -22, -31, -38, -46, -54, -61, -67, -73, -78, -82, -85, -88, -90, -90], - [90, 87, 80, 70, 57, 43, 25, 9, -9, -25, -43, -57, -70, -80, -87, -90, -90, -87, -80, -70, -57, -43, -25, -9, 9, 25, 43, 57, 70, 80, 87, 90], - [90, 82, 67, 46, 22, -4, -31, -54, -73, -85, -90, -88, -78, -61, -38, -13, 13, 38, 61, 78, 88, 90, 85, 73, 54, 31, 4, -22, -46, -67, -82, -90], - [89, 75, 50, 18, -18, -50, -75, -89, -89, -75, -50, -18, 18, 50, 75, 89, 89, 75, 50, 18, -18, -50, -75, -89, -89, -75, -50, -18, 18, 50, 75, 89], - [88, 67, 31, -13, -54, -82, -90, -78, -46, -4, 38, 73, 90, 85, 61, 22, -22, -61, -85, -90, -73, -38, 4, 46, 78, 90, 82, 54, 13, -31, -67, -88], - [87, 57, 9, -43, -80, -90, -70, -25, 25, 70, 90, 80, 43, -9, -57, -87, -87, -57, -9, 43, 80, 90, 70, 25, -25, -70, -90, -80, -43, 9, 57, 87], - [85, 46, -13, -67, -90, -73, -22, 38, 82, 88, 54, -4, -61, -90, -78, -31, 31, 78, 90, 61, 4, -54, -88, -82, -38, 22, 73, 90, 67, 13, -46, -85], - [83, 36, -36, -83, -83, -36, 36, 83, 83, 36, -36, -83, -83, -36, 36, 83, 83, 36, -36, -83, -83, -36, 36, 83, 83, 36, -36, -83, -83, -36, 36, 83], - [82, 22, -54, -90, -61, 13, 78, 85, 31, -46, -90, -67, 4, 73, 88, 38, -38, -88, -73, -4, 67, 90, 46, -31, -85, -78, -13, 61, 90, 54, -22, -82], - [80, 9, -70, -87, -25, 57, 90, 43, -43, -90, -57, 25, 87, 70, -9, -80, -80, -9, 70, 87, 25, -57, -90, -43, 43, 90, 57, -25, -87, -70, 9, 80], - [78, -4, -82, -73, 13, 85, 67, -22, -88, -61, 31, 90, 54, -38, -90, -46, 46, 90, 38, -54, -90, -31, 61, 88, 22, -67, -85, -13, 73, 82, 4, -78], - [75, -18, -89, -50, 50, 89, 18, -75, -75, 18, 89, 50, -50, -89, -18, 75, 75, -18, -89, -50, 50, 89, 18, -75, -75, 18, 89, 50, -50, -89, -18, 75], - [73, -31, -90, -22, 78, 67, -38, -90, -13, 82, 61, -46, -88, -4, 85, 54, -54, -85, 4, 88, 46, -61, -82, 13, 90, 38, -67, -78, 22, 90, 31, -73], - [70, -43, -87, 9, 90, 25, -80, -57, 57, 80, -25, -90, -9, 87, 43, -70, -70, 43, 87, -9, -90, -25, 80, 57, -57, -80, 25, 90, 9, -87, -43, 70], - [67, -54, -78, 38, 85, -22, -90, 4, 90, 13, -88, -31, 82, 46, -73, -61, 61, 73, -46, -82, 31, 88, -13, -90, -4, 90, 22, -85, -38, 78, 54, -67], - [64, -64, -64, 64, 64, -64, -64, 64, 64, -64, -64, 64, 64, -64, -64, 64, 64, -64, -64, 64, 64, -64, -64, 64, 64, -64, -64, 64, 64, -64, -64, 64], - [61, -73, -46, 82, 31, -88, -13, 90, -4, -90, 22, 85, -38, -78, 54, 67, -67, -54, 78, 38, -85, -22, 90, 4, -90, 13, 88, -31, -82, 46, 73, -61], - [57, -80, -25, 90, -9, -87, 43, 70, -70, -43, 87, 9, -90, 25, 80, -57, -57, 80, 25, -90, 9, 87, -43, -70, 70, 43, -87, -9, 90, -25, -80, 57], - [54, -85, -4, 88, -46, -61, 82, 13, -90, 38, 67, -78, -22, 90, -31, -73, 73, 31, -90, 22, 78, -67, -38, 90, -13, -82, 61, 46, -88, 4, 85, -54], - [50, -89, 18, 75, -75, -18, 89, -50, -50, 89, -18, -75, 75, 18, -89, 50, 50, -89, 18, 75, -75, -18, 89, -50, -50, 89, -18, -75, 75, 18, -89, 50], - [46, -90, 38, 54, -90, 31, 61, -88, 22, 67, -85, 13, 73, -82, 4, 78, -78, -4, 82, -73, -13, 85, -67, -22, 88, -61, -31, 90, -54, -38, 90, -46], - [43, -90, 57, 25, -87, 70, 9, -80, 80, -9, -70, 87, -25, -57, 90, -43, -43, 90, -57, -25, 87, -70, -9, 80, -80, 9, 70, -87, 25, 57, -90, 43], - [38, -88, 73, -4, -67, 90, -46, -31, 85, -78, 13, 61, -90, 54, 22, -82, 82, -22, -54, 90, -61, -13, 78, -85, 31, 46, -90, 67, 4, -73, 88, -38], - [36, -83, 83, -36, -36, 83, -83, 36, 36, -83, 83, -36, -36, 83, -83, 36, 36, -83, 83, -36, -36, 83, -83, 36, 36, -83, 83, -36, -36, 83, -83, 36], - [31, -78, 90, -61, 4, 54, -88, 82, -38, -22, 73, -90, 67, -13, -46, 85, -85, 46, 13, -67, 90, -73, 22, 38, -82, 88, -54, -4, 61, -90, 78, -31], - [25, -70, 90, -80, 43, 9, -57, 87, -87, 57, -9, -43, 80, -90, 70, -25, -25, 70, -90, 80, -43, -9, 57, -87, 87, -57, 9, 43, -80, 90, -70, 25], - [22, -61, 85, -90, 73, -38, -4, 46, -78, 90, -82, 54, -13, -31, 67, -88, 88, -67, 31, 13, -54, 82, -90, 78, -46, 4, 38, -73, 90, -85, 61, -22], - [18, -50, 75, -89, 89, -75, 50, -18, -18, 50, -75, 89, -89, 75, -50, 18, 18, -50, 75, -89, 89, -75, 50, -18, -18, 50, -75, 89, -89, 75, -50, 18], - [13, -38, 61, -78, 88, -90, 85, -73, 54, -31, 4, 22, -46, 67, -82, 90, -90, 82, -67, 46, -22, -4, 31, -54, 73, -85, 90, -88, 78, -61, 38, -13], - [9, -25, 43, -57, 70, -80, 87, -90, 90, -87, 80, -70, 57, -43, 25, -9, -9, 25, -43, 57, -70, 80, -87, 90, -90, 87, -80, 70, -57, 43, -25, 9], - [4, -13, 22, -31, 38, -46, 54, -61, 67, -73, 78, -82, 85, -88, 90, -90, 90, -90, 88, -85, 82, -78, 73, -67, 61, -54, 46, -38, 31, -22, 13, -4], -]; -/// Table 8-12: beta' for Q = 0..51. -pub static BETA_TABLE: [u8; 52] = [0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 20, 22, 24, 26, 28, 30, 32, 34, 36, 38, 40, 42, 44, 46, 48, 50, 52, 54, 56, 58, 60, 62, 64]; -/// Table 8-12: tC' for Q = 0..53. -pub static TC_TABLE: [u8; 54] = [0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 1, 1, 1, 1, 1, 1, 1, 2, 2, 2, 2, 3, 3, 3, 3, 4, 4, 4, 5, 5, 6, 6, 7, 8, 9, 10, 11, 13, 14, 16, 18, 20, 22, 24]; diff --git a/crates/hevcenc/src/transform.rs b/crates/hevcenc/src/transform.rs deleted file mode 100644 index cffea75c2..000000000 --- a/crates/hevcenc/src/transform.rs +++ /dev/null @@ -1,328 +0,0 @@ -//! Forward transforms (encoder side, any integer approximation is allowed), quantisation, and the -//! normative scaling (8.6.3) and inverse transforms (8.6.4.2) the decoder applies. The inverse path -//! is exact integer arithmetic: the even/odd decomposition computes the same sums as the matrix -//! products of the specification. - -use crate::tables::{DST_MATRIX, LEVEL_SCALE, TRANS_MATRIX}; - -#[inline] -fn m(n: usize, k: usize, i: usize) -> i32 { - // k-th basis function of the n-point DCT, sample i - TRANS_MATRIX[k * (32 / n)][i] as i32 -} - -/// 1-D forward DCT of `x` (length n) into `out`. -fn fdct1d(x: &[i32], out: &mut [i32], n: usize) { - if n <= 4 { - for k in 0..n { - let mut s = 0; - for i in 0..n { - s += m(n, k, i) * x[i]; - } - out[k] = s; - } - return; - } - let h = n / 2; - let mut e = [0i32; 16]; - let mut o = [0i32; 16]; - for i in 0..h { - e[i] = x[i] + x[n - 1 - i]; - o[i] = x[i] - x[n - 1 - i]; - } - let mut ev = [0i32; 16]; - fdct1d(&e[..h], &mut ev[..h], h); - for k in 0..h { - out[2 * k] = ev[k]; - let kk = 2 * k + 1; - let mut s = 0; - for i in 0..h { - s += m(n, kk, i) * o[i]; - } - out[kk] = s; - } -} - -/// 1-D inverse DCT: x[i] = sum_k M[k][i] * c[k]. -fn idct1d(c: &[i32], out: &mut [i32], n: usize) { - if n <= 4 { - for i in 0..n { - let mut s = 0; - for k in 0..n { - s += m(n, k, i) * c[k]; - } - out[i] = s; - } - return; - } - let h = n / 2; - let mut ce = [0i32; 16]; - for k in 0..h { - ce[k] = c[2 * k]; - } - let mut e = [0i32; 16]; - idct1d(&ce[..h], &mut e[..h], h); - for i in 0..h { - let mut o = 0; - let mut k = 1; - while k < n { - let v = c[k]; - if v != 0 { - o += m(n, k, i) * v; - } - k += 2; - } - out[i] = e[i] + o; - out[n - 1 - i] = e[i] - o; - } -} - -fn fdst1d(x: &[i32], out: &mut [i32]) { - for k in 0..4 { - out[k] = (0..4).map(|i| DST_MATRIX[k][i] as i32 * x[i]).sum(); - } -} - -fn idst1d(c: &[i32], out: &mut [i32]) { - for i in 0..4 { - out[i] = (0..4).map(|k| DST_MATRIX[k][i] as i32 * c[k]).sum(); - } -} - -/// Forward 2-D transform of the n x n residual `r` (row-major) into `c`. -pub fn forward(r: &[i32], c: &mut [i32], n: usize, dst: bool, bit_depth: u32) { - let log2 = n.trailing_zeros(); - let shift1 = log2 + bit_depth - 9; - let shift2 = log2 + 6; - let (r1, r2) = (1i32 << (shift1 - 1), 1i32 << (shift2 - 1)); - let mut tmp = vec![0i32; n * n]; - let mut out = [0i32; 32]; - // rows (horizontal frequencies) - for y in 0..n { - let row = &r[y * n..y * n + n]; - if dst { - fdst1d(row, &mut out); - } else { - fdct1d(row, &mut out, n); - } - for k in 0..n { - tmp[k * n + y] = (out[k] + r1) >> shift1; // transposed: tmp[kx][y] - } - } - // columns - let mut col = [0i32; 32]; - for kx in 0..n { - col[..n].copy_from_slice(&tmp[kx * n..kx * n + n]); - if dst { - fdst1d(&col, &mut out); - } else { - fdct1d(&col, &mut out, n); - } - for ky in 0..n { - c[ky * n + kx] = (out[ky] + r2) >> shift2; - } - } -} - -/// Inverse 2-D transform (8.6.4.2) of scaled coefficients `d` in place, producing residuals. -pub fn inverse(d: &mut [i32], n: usize, dst: bool, bit_depth: u32) { - let bd_shift = 20 - bit_depth; - let mut tmp = vec![0i32; n * n]; - let mut col = [0i32; 32]; - let mut out = [0i32; 32]; - // max non-zero row, to skip empty work - let mut nz_cols = [false; 32]; - for y in 0..n { - for x in 0..n { - if d[y * n + x] != 0 { - nz_cols[x] = true; - } - } - } - // 1. columns - for x in 0..n { - if !nz_cols[x] { - continue; // tmp column stays zero - } - for j in 0..n { - col[j] = d[j * n + x]; - } - if dst { - idst1d(&col, &mut out); - } else { - idct1d(&col, &mut out, n); - } - for y in 0..n { - tmp[y * n + x] = ((out[y] + 64) >> 7).clamp(-32768, 32767); - } - } - // 2. rows - let round = 1 << (bd_shift - 1); - for y in 0..n { - let row = &tmp[y * n..y * n + n]; - if dst { - idst1d(row, &mut out); - } else { - idct1d(row, &mut out, n); - } - for x in 0..n { - d[y * n + x] = (out[x] + round) >> bd_shift; - } - } -} - -const QUANT_SCALE: [i64; 6] = [26214, 23302, 20560, 18396, 16384, 14564]; - -/// Quantise transform coefficients `c` into levels `l` with a dead-zone rounding offset -/// (`intra`: 1/3, otherwise 1/6). `qp` includes QpBdOffset. Returns the number of non-zero levels. -pub fn quantize(c: &[i32], l: &mut [i32], n: usize, qp: i32, intra: bool, bit_depth: u32) -> usize { - let log2 = n.trailing_zeros() as i32; - let tshift = 15 - bit_depth as i32 - log2; - let qbits = 14 + qp / 6 + tshift; - let scale = QUANT_SCALE[(qp % 6) as usize]; - let offset = (if intra { 171i64 } else { 85 }) << (qbits - 9); - let mut nz = 0; - for i in 0..n * n { - let v = c[i] as i64; - let a = ((v.abs() * scale + offset) >> qbits).min(32767); - if a != 0 { - nz += 1; - } - l[i] = if v < 0 { -(a as i32) } else { a as i32 }; - } - nz -} - -/// Sign data hiding (7.3.8.11 signHidden): in every 4x4 sub-block whose first and last non-zero -/// coefficients (in scan order) are more than 3 positions apart, the sign of the first one is not -/// coded but inferred from the parity of the sum of absolute levels. Fix the parity where needed by -/// the +-1 level change that adds the least quantisation error, without moving the first / last -/// non-zero positions. -pub fn sign_hiding(c: &[i32], l: &mut [i32], log2: u32, scan_idx: usize, qp: i32, bit_depth: u32) { - use crate::tables::{SCAN_4, scan_order}; - let n = 1usize << log2; - let tshift = 15 - bit_depth as i32 - log2 as i32; - let qbits = 14 + qp / 6 + tshift; - let scale = QUANT_SCALE[(qp % 6) as usize]; - let sb_scan = scan_order(log2 - 2, scan_idx); - let pos = &SCAN_4[scan_idx]; - for &(xs, ys) in sb_scan { - let idx = |p: usize| (ys as usize * 4 + pos[p].1 as usize) * n + xs as usize * 4 + pos[p].0 as usize; - let (Some(first), Some(last)) = ((0..16).find(|&p| l[idx(p)] != 0), (0..16).rev().find(|&p| l[idx(p)] != 0)) else { continue }; - if last - first <= 3 { - continue; - } - let sum: i32 = (0..16).map(|p| l[idx(p)].abs()).sum(); - let negative = l[idx(first)] < 0; - if negative == (sum & 1 == 1) { - continue; - } - // cost of +-1 changes in 1/256 units of quantisation error - let mut best: Option<(i64, usize, i32)> = None; - for p in first..=last { - let i = idx(p); - let q = ((c[i] as i64).abs() * scale) >> (qbits - 8); - let a = l[i].abs() as i64; - let err = |lv: i64| (q - (lv << 8)).abs(); - let mut consider = |cost: i64, d: i32| { - if best.is_none_or(|b| cost < b.0) { - best = Some((cost, i, d)); - } - }; - if a < 32767 && (a > 0 || (p > first && p < last)) { - consider(err(a + 1) - err(a), 1); - } - if a > 1 || (a == 1 && p > first && p < last) { - consider(err(a - 1) - err(a), -1); - } - } - if let Some((_, i, d)) = best { - let a = l[i].abs() + d; - l[i] = if c[i] < 0 { -a } else { a }; - } - } -} - -/// Scaling process (8.6.2 / 8.6.3, flat scaling matrix): levels to scaled coefficients. -pub fn dequantize(l: &[i32], d: &mut [i32], n: usize, qp: i32, bit_depth: u32) { - let log2 = n.trailing_zeros(); - let bd_shift = bit_depth + log2 - 5; - let round = 1i64 << (bd_shift - 1); - let scale = (LEVEL_SCALE[(qp % 6) as usize] as i64) << (qp / 6); - for i in 0..n * n { - let v = l[i]; - d[i] = if v == 0 { 0 } else { ((v as i64 * 16 * scale + round) >> bd_shift).clamp(-32768, 32767) as i32 }; - } -} - -#[cfg(test)] -mod tests { - use super::*; - - /// Direct matrix form of the inverse transform, as written in the specification. - fn inverse_ref(c: &mut [i32], n: usize, dst: bool, bd: u32) { - let mut tmp = vec![0i32; n * n]; - let step = 32 / n; - for x in 0..n { - for y in 0..n { - let mut s = 0i32; - for j in 0..n { - let m = if dst { DST_MATRIX[j][y] as i32 } else { TRANS_MATRIX[j * step][y] as i32 }; - s += m * c[j * n + x]; - } - tmp[y * n + x] = ((s + 64) >> 7).clamp(-32768, 32767); - } - } - let bd_shift = 20 - bd; - for y in 0..n { - for x in 0..n { - let mut s = 0i32; - for j in 0..n { - let m = if dst { DST_MATRIX[j][x] as i32 } else { TRANS_MATRIX[j * step][x] as i32 }; - s += m * tmp[y * n + j]; - } - c[y * n + x] = (s + (1 << (bd_shift - 1))) >> bd_shift; - } - } - } - - #[test] - fn inverse_matches_matrix_form() { - let mut seed = 7u32; - for &(n, dst) in &[(4usize, true), (4, false), (8, false), (16, false), (32, false)] { - for _ in 0..20 { - let mut c = vec![0i32; n * n]; - for v in c.iter_mut() { - seed = seed.wrapping_mul(1664525).wrapping_add(1013904223); - if (seed >> 24).is_multiple_of(3) { - *v = ((seed >> 8) % 2001) as i32 - 1000; - } - } - let mut a = c.clone(); - let mut b = c.clone(); - inverse(&mut a, n, dst, 8); - inverse_ref(&mut b, n, dst, 8); - assert_eq!(a, b, "n {n} dst {dst}"); - } - } - } - - #[test] - fn round_trip_is_close() { - for &(n, dst) in &[(4usize, true), (8, false), (32, false)] { - for bd in [8u32, 10] { - let r: Vec = (0..n * n).map(|i| ((i * 37 + i / n * 11) % 61) as i32 - 30).collect(); - let mut c = vec![0; n * n]; - forward(&r, &mut c, n, dst, bd); - let mut l = vec![0; n * n]; - let qp = 4; - quantize(&c, &mut l, n, qp, true, bd); - let mut d = vec![0; n * n]; - dequantize(&l, &mut d, n, qp, bd); - inverse(&mut d, n, dst, bd); - let err: i32 = r.iter().zip(&d).map(|(a, b)| (a - b).abs()).max().unwrap(); - assert!(err <= 2, "n {n} bd {bd} err {err}"); - } - } - } -} diff --git a/crates/host/src/lib.rs b/crates/host/src/lib.rs index 0233f834c..843a06123 100644 --- a/crates/host/src/lib.rs +++ b/crates/host/src/lib.rs @@ -1,6 +1,6 @@ //! A fully wired [`Session`]: footage decoding through `effectcraft-media` (FilmCraft's codecs), //! the media importer, the expression engine, JavaScript scripting (`effectcraft-script`) and -//! Render Queue export through `effectcraft-export` (FilmCraft's encoders). Frontends (desktop, +//! Render Queue export through `effectcraft-export` (craft-libs' encoders). Frontends (desktop, //! CLI, MCP, web) start here. #![deny(clippy::unwrap_used, clippy::expect_used, clippy::panic, clippy::unimplemented, clippy::todo, clippy::unreachable)] diff --git a/crates/media/Cargo.toml b/crates/media/Cargo.toml index 2fa54e0eb..76cce90fa 100644 --- a/crates/media/Cargo.toml +++ b/crates/media/Cargo.toml @@ -22,9 +22,8 @@ effectcraft-pdf = { workspace = true } effectcraft-model = { workspace = true } filmcraft-codecs = { workspace = true } filmcraft-media = { workspace = true } -filmcraft-aac = { workspace = true } -filmcraft-isobmff = { workspace = true } -filmcraft-matroska = { workspace = true } +craft-audio = { workspace = true } +craft-media = { workspace = true } filmcraft-mpegts = { workspace = true } filmcraft-frame = { workspace = true } filmcraft-color = { workspace = true } diff --git a/crates/media/src/aac.rs b/crates/media/src/aac.rs index 625c37b43..a1793b7af 100644 --- a/crates/media/src/aac.rs +++ b/crates/media/src/aac.rs @@ -6,7 +6,7 @@ use std::io; -use filmcraft_aac::AudioSpecificConfig; +use craft_audio::aac::AudioSpecificConfig; use filmcraft_media::ByteReader; /// How far into an MPEG TS / PS file the audio streams are looked for: the stream is indexed as @@ -34,15 +34,16 @@ pub(crate) fn config(header: Option<&[u8]>, file: Option<&dyn ByteReader>) -> Op /// are in its fragments, not the `moov`). fn mp4(header: &[u8], file: Option<&dyn ByteReader>) -> Option> { let mp4 = match file { - Some(f) => filmcraft_isobmff::open(Head(f, f.len())), - None => filmcraft_isobmff::open(header), + Some(f) => craft_media::isobmff::open(Head(f, f.len())), + None => craft_media::isobmff::open(header), }; let mp4 = mp4.ok()?; - let playable = - |t: &&filmcraft_isobmff::Track| t.kind == filmcraft_isobmff::TrackKind::Audio && !t.samples.is_empty() && !t.entries.is_empty() && t.timescale > 0; + let playable = |t: &&craft_media::isobmff::Track| { + t.kind == craft_media::isobmff::TrackKind::Audio && !t.samples.is_empty() && !t.entries.is_empty() && t.timescale > 0 + }; let track = mp4.tracks.iter().find(playable)?; match &track.entries.first()?.codec { - filmcraft_isobmff::CodecConfig::Aac(a) => Some(a.asc.clone()), + craft_media::isobmff::CodecConfig::Aac(a) => Some(a.asc.clone()), _ => None, } } @@ -50,9 +51,9 @@ fn mp4(header: &[u8], file: Option<&dyn ByteReader>) -> Option> { /// The track FilmCraft's Matroska source plays: the first audio track with frames, found by /// indexing the clusters as that source does. fn matroska(file: &dyn ByteReader) -> Option> { - let mkv = filmcraft_matroska::open(Head(file, file.len())).ok()?; - match &mkv.tracks.iter().find(|t| t.kind == filmcraft_matroska::TrackKind::Audio && !t.samples.is_empty())?.codec { - filmcraft_matroska::Codec::Aac { asc } => Some(asc.clone()), + let mkv = craft_media::matroska::open(Head(file, file.len())).ok()?; + match &mkv.tracks.iter().find(|t| t.kind == craft_media::matroska::TrackKind::Audio && !t.samples.is_empty())?.codec { + craft_media::matroska::Codec::Aac { asc } => Some(asc.clone()), _ => None, } } @@ -88,16 +89,7 @@ impl Head<'_> { } } -impl filmcraft_isobmff::ByteSource for Head<'_> { - fn len(&self) -> u64 { - self.1 - } - fn read_at(&self, offset: u64, buf: &mut [u8]) -> io::Result<()> { - self.read(offset, buf) - } -} - -impl filmcraft_matroska::ByteSource for Head<'_> { +impl craft_media::ByteSource for Head<'_> { fn len(&self) -> u64 { self.1 } diff --git a/crates/media/src/downmix.rs b/crates/media/src/downmix.rs index 09bb1e1f6..5e086bdaf 100644 --- a/crates/media/src/downmix.rs +++ b/crates/media/src/downmix.rs @@ -19,7 +19,7 @@ use std::f32::consts::FRAC_1_SQRT_2; use std::sync::Arc; -use filmcraft_aac::{AudioSpecificConfig, ProgramConfig}; +use craft_audio::aac::{AudioSpecificConfig, ProgramConfig}; /// A source channel's contribution to the left and right outputs, before normalisation. pub type Pair = [f32; 2]; @@ -185,7 +185,7 @@ mod tests { } } - /// AudioSpecificConfigs as ffmpeg 8.1 and FilmCraft's encoder write them (48 kHz AAC-LC). + /// AudioSpecificConfigs as ffmpeg 8.1 and craft-audio's encoder write them (48 kHz AAC-LC). const PCE_FFMPEG_61: &str = "118004c848002000c4400d4c61766336322e32382e31303256e500"; const PCE_FILMCRAFT_61: &str = "118004c845000108840000"; diff --git a/crates/media/src/probe.rs b/crates/media/src/probe.rs index 1fc945aa1..2c7b2a402 100644 --- a/crates/media/src/probe.rs +++ b/crates/media/src/probe.rs @@ -134,12 +134,12 @@ fn trim_to_edits(f: &mut Footage, shown: Option) { /// How long the video track's edit list shows its media (`elst` segments, movie timescale): /// `None` without edits, or with an empty edit (a delayed start keeps the reader's duration). fn video_shown(bytes: &[u8]) -> Option { - let file = filmcraft_isobmff::open(bytes).ok()?; - let t = file.tracks.iter().find(|t| t.kind == filmcraft_isobmff::TrackKind::Video && !t.samples.is_empty())?; + let file = craft_media::isobmff::open(bytes).ok()?; + let t = file.tracks.iter().find(|t| t.kind == craft_media::isobmff::TrackKind::Video && !t.samples.is_empty())?; edits_shown(&t.edits, file.timescale) } -fn edits_shown(edits: &[filmcraft_isobmff::Edit], timescale: u32) -> Option { +fn edits_shown(edits: &[craft_media::isobmff::Edit], timescale: u32) -> Option { if edits.is_empty() || timescale == 0 || edits.iter().any(|e| e.media_time < 0) { return None; } @@ -303,8 +303,8 @@ pub(crate) fn load_oriented(bytes: &[u8], fmt: Option) -> im /// containers have none. FilmCraft turns the frames of a pure 0/90/180/270° header itself, so this /// is only the quarter turn nearest to a header it does not turn. pub(crate) fn video_turn(bytes: &[u8]) -> Orientation { - let Ok(file) = filmcraft_isobmff::open(bytes) else { return Orientation::NoTransforms }; - let Some(track) = file.tracks.iter().find(|t| t.kind == filmcraft_isobmff::TrackKind::Video) else { + let Ok(file) = craft_media::isobmff::open(bytes) else { return Orientation::NoTransforms }; + let Some(track) = file.tracks.iter().find(|t| t.kind == craft_media::isobmff::TrackKind::Video) else { return Orientation::NoTransforms; }; // A pure rotation is already applied by FilmCraft's decoder (frames and size come out @@ -329,8 +329,8 @@ pub(crate) fn video_turn(bytes: &[u8]) -> Orientation { /// median reads as 30.303 or 58.824 fps. `None` for other containers and for movies the median /// reads right. fn video_rate(bytes: &[u8]) -> Option { - let file = filmcraft_isobmff::open(bytes).ok()?; - let t = file.tracks.iter().find(|t| t.kind == filmcraft_isobmff::TrackKind::Video && !t.samples.is_empty())?; + let file = craft_media::isobmff::open(bytes).ok()?; + let t = file.tracks.iter().find(|t| t.kind == craft_media::isobmff::TrackKind::Video && !t.samples.is_empty())?; averaged_rate(t.timescale, t.samples.iter().take(240).map(|s| s.duration)) } @@ -368,7 +368,7 @@ mod tests { /// 1000 timescale), though its media runs a frame longer; empty edits and no edits leave it. #[test] fn the_edit_list_trims_a_movie_to_the_frames_it_shows() { - let edit = |segment_duration, media_time| filmcraft_isobmff::Edit { segment_duration, media_time, media_rate: 0x0001_0000 }; + let edit = |segment_duration, media_time| craft_media::isobmff::Edit { segment_duration, media_time, media_rate: 0x0001_0000 }; let shown = edits_shown(&[edit(3000, 1024)], 1000); assert_eq!(shown, Some(Tick::from_units(3, 1))); let mut f = Footage { kind: FootageKind::Video, duration: Tick::from_units(76, 25), ..Footage::default() }; diff --git a/crates/media/tests/it/aac_layout.rs b/crates/media/tests/it/aac_layout.rs index fb3e078a6..896e4f0a7 100644 --- a/crates/media/tests/it/aac_layout.rs +++ b/crates/media/tests/it/aac_layout.rs @@ -1,5 +1,5 @@ //! Multichannel AAC reaches stereo by where each channel plays, whatever order its channel -//! configuration decodes in (#1701). Fixtures are tones encoded with FilmCraft's AAC encoder (no +//! configuration decodes in (#1701). Fixtures are tones encoded with craft-audio's AAC encoder (no //! ffmpeg needed, so nothing is skipped), with the configuration rewritten where the coded //! elements fit another: configurations 11 and 12 code the same elements as the encoder's 7- and //! 8-channel output, and a program config element can name any of them. @@ -9,9 +9,9 @@ use std::io::Cursor; use std::path::PathBuf; use std::sync::Arc; +use craft_audio::aac::{AudioSpecificConfig, Encoder, EncoderConfig, ProgramConfig}; use effectcraft_media::{MediaPool, probe, probe_bytes}; use effectcraft_time::Tick; -use filmcraft_aac::{AudioSpecificConfig, Encoder, EncoderConfig, ProgramConfig}; const RATE: u32 = 48_000; /// Tone frequencies by channel; an LFE gets [`LFE_TONE`], which its element can carry. @@ -61,7 +61,9 @@ fn lc(config: u8) -> Option { fn pce(front: Vec<(bool, u8)>, side: Vec<(bool, u8)>, back: Vec<(bool, u8)>, lfe: Vec, comment: &[u8]) -> Option { let pce = ProgramConfig { object_type: 1, sf_index: 3, front, side, back, lfe, comment: comment.to_vec(), ..Default::default() }; - Some(AudioSpecificConfig { pce: Some(pce), ..AudioSpecificConfig::lc(3, 0) }) + let mut asc = AudioSpecificConfig::lc(3, 0); + asc.pce = Some(pce); + Some(asc) } use Ch::*; @@ -115,7 +117,7 @@ fn mp4(asc: Vec, channels: u32, units: &[Vec]) -> Vec { /// As [`mp4`], after an audio track with the configuration and channel count `empty` and no /// samples, if any. fn mp4_after(empty: Option<(Vec, u32)>, asc: Vec, channels: u32, units: &[Vec]) -> Vec { - use filmcraft_isobmff::{Brand, Mp4Writer, SampleEntry, TrackConfig, WriteSample, WriterOptions}; + use craft_media::isobmff::{Brand, Mp4Writer, SampleEntry, TrackConfig, WriteSample, WriterOptions}; let mut w = Mp4Writer::new(Cursor::new(Vec::new()), WriterOptions::new(Brand::Mp4)).expect("writer"); if let Some((asc, channels)) = empty { w.add_track(TrackConfig::new(SampleEntry::aac(asc, channels, RATE), RATE)).expect("empty track"); @@ -134,7 +136,7 @@ fn mkv(asc: Vec, channels: u32, units: &[Vec]) -> Vec { /// As [`mkv`], after an audio track with the configuration and channel count `empty` and no /// frames, if any. fn mkv_after(empty: Option<(Vec, u32)>, asc: Vec, channels: u32, units: &[Vec]) -> Vec { - use filmcraft_matroska::{MkvWriter, MuxOptions, TrackKind, TrackSpec}; + use craft_media::matroska::{MkvWriter, MuxOptions, TrackKind, TrackSpec}; let track = |asc: Vec, channels: u32| { let mut t = TrackSpec::new(TrackKind::Audio, "A_AAC"); t.codec_private = asc; @@ -210,7 +212,7 @@ fn lfe_is_coded(case: &Case, bytes: Vec) { fn every_aac_layout_mixes_by_speaker_in_mp4() { for case in cases() { let (asc, units) = encode(case.content, false); - let asc = case.asc.as_ref().map_or(asc, AudioSpecificConfig::to_bytes); + let asc = case.asc.as_ref().map_or(asc, |a| a.to_bytes().expect("ASC")); let bytes = mp4(asc, case.content.len() as u32, &units); lfe_is_coded(&case, bytes.clone()); let p = write(&format!("{}.mp4", case.name), &bytes); @@ -227,7 +229,7 @@ fn every_aac_layout_mixes_by_speaker_in_mp4() { fn matroska_aac_layouts_mix_by_speaker() { for case in cases().into_iter().filter(|c| matches!(c.name, "config12" | "pce61-centre-first" | "pce61-ffmpeg")) { let (asc, units) = encode(case.content, false); - let asc = case.asc.as_ref().map_or(asc, AudioSpecificConfig::to_bytes); + let asc = case.asc.as_ref().map_or(asc, |a| a.to_bytes().expect("ASC")); let p = write(&format!("{}.mkv", case.name), &mkv(asc, case.content.len() as u32, &units)); check(case.name, case.content, &mix(&MediaPool::new(), &probe(&p).expect("probe"))); } @@ -251,9 +253,9 @@ fn an_empty_audio_track_lends_its_configuration_to_no_other() { for (name, empty) in [("config12", 7), ("pce61-centre-first", 11)] { let case = cases().into_iter().find(|c| c.name == name).expect("case"); let n = case.content.len() as u32; - let empty = Some((AudioSpecificConfig::lc(3, empty).to_bytes(), n)); + let empty = Some((AudioSpecificConfig::lc(3, empty).to_bytes().expect("ASC"), n)); let (asc, units) = encode(case.content, false); - let asc = case.asc.as_ref().map_or(asc, AudioSpecificConfig::to_bytes); + let asc = case.asc.as_ref().map_or(asc, |a| a.to_bytes().expect("ASC")); let p = write(&format!("{name}-after-empty.mp4"), &mp4_after(empty.clone(), asc.clone(), n, &units)); check(&format!("{name} MP4"), case.content, &mix(&MediaPool::new(), &probe(&p).expect("probe"))); let p = write(&format!("{name}-after-empty.mkv"), &mkv_after(empty, asc, n, &units)); diff --git a/crates/media/tests/it/downmix.rs b/crates/media/tests/it/downmix.rs index 05205ab6a..f974c6cb0 100644 --- a/crates/media/tests/it/downmix.rs +++ b/crates/media/tests/it/downmix.rs @@ -174,7 +174,7 @@ fn each_channel_reaches_stereo_at_its_bs775_gain() { fn whole_pce_entry(p: &Path, channels: usize) -> PathBuf { let mut bytes = std::fs::read(p).expect("read"); let Some((asc, 2)) = aac_entry(&bytes) else { return p.to_path_buf() }; - if channels <= 2 || filmcraft_aac::AudioSpecificConfig::parse(&asc).map(|a| a.channel_config).ok() != Some(0) { + if channels <= 2 || craft_audio::aac::AudioSpecificConfig::parse(&asc).map(|a| a.channel_config).ok() != Some(0) { return p.to_path_buf(); } // the `mp4a` entry's channelcount follows its 6 reserved bytes, data reference index and @@ -189,9 +189,9 @@ fn whole_pce_entry(p: &Path, channels: usize) -> PathBuf { /// The AudioSpecificConfig and sample entry channel count of an MP4's first AAC track. fn aac_entry(bytes: &[u8]) -> Option<(Vec, u32)> { - let file = filmcraft_isobmff::open(bytes).ok()?; + let file = craft_media::isobmff::open(bytes).ok()?; file.tracks.iter().flat_map(|t| &t.entries).find_map(|e| match &e.codec { - filmcraft_isobmff::CodecConfig::Aac(a) => Some((a.asc.clone(), e.audio.as_ref()?.channels)), + craft_media::isobmff::CodecConfig::Aac(a) => Some((a.asc.clone(), e.audio.as_ref()?.channels)), _ => None, }) } diff --git a/crates/media/tests/it/file_timecode.rs b/crates/media/tests/it/file_timecode.rs index e5cbfa92d..dae3335ba 100644 --- a/crates/media/tests/it/file_timecode.rs +++ b/crates/media/tests/it/file_timecode.rs @@ -2,8 +2,8 @@ use std::io::Cursor; +use craft_media::isobmff::{Brand, Mp4Writer, SampleEntry, TimecodeConfig, TrackConfig, WriteSample, WriterOptions}; use effectcraft_project::FootageKind; -use filmcraft_isobmff::{Brand, Mp4Writer, SampleEntry, TimecodeConfig, TrackConfig, WriteSample, WriterOptions}; /// A two-frame 24 fps Motion JPEG MOV, with a timecode track from `start_frame` when given. fn mov(start_frame: Option) -> Vec { diff --git a/crates/opusenc/Cargo.toml b/crates/opusenc/Cargo.toml index 489c648ea..db7f2b7f8 100644 --- a/crates/opusenc/Cargo.toml +++ b/crates/opusenc/Cargo.toml @@ -12,4 +12,4 @@ workspace = true [dependencies] [dev-dependencies] -filmcraft-opus = { workspace = true } +craft-audio = { workspace = true } diff --git a/crates/opusenc/README.md b/crates/opusenc/README.md index 1ebf6fedb..8a822ceac 100644 --- a/crates/opusenc/README.md +++ b/crates/opusenc/README.md @@ -85,7 +85,7 @@ recursive band splitting, greedy PVQ search. Not implemented: transient frames, pitch post-filter, intensity stereo, dynamic allocation, VBR. All-zero CELT frames become 3-byte silence packets. -## Measured (tests in `tests/`, FilmCraft decoder) +## Measured (tests in `tests/`, craft-audio's Opus decoder) Synthetic speech (glottal pulses 100–200 Hz through three moving formants, syllable envelopes, pauses) and music (three-note harmonic chords); waveform SNR against the input band-limited to @@ -129,8 +129,8 @@ stage-1/2 codebooks and prediction weights, gain, pitch, contour, LTP and pulse weights) and the decoder-side normative arithmetic the encoder must reproduce bit-exactly (CELT allocation; SILK NLSF dequantisation and stabilisation, NLSF → LPC conversion, `log2lin`, the fixed-point division/inverse helpers and the LPC analysis filter) are mirrored from FilmCraft's -first-party, spec-derived `filmcraft-opus` decoder (MIT OR Apache-2.0), which is also the -round-trip oracle in `tests/` and `crates/export/tests/it/opus_roundtrip.rs` (ffmpeg is a second, +first-party, spec-derived `filmcraft-opus` decoder (MIT OR Apache-2.0). That decoder, now +`craft_audio::opus` in craft-libs, is also the round-trip oracle in `tests/` and `crates/export/tests/it/opus_roundtrip.rs` (ffmpeg is a second, external oracle when installed). All encoder-side analysis and search (pitch, LPC, NLSF and LTP search, noise feedback, rate control, stereo prediction, the decimator) is our own design. No third-party encoder or decoder source (libopus or otherwise) was consulted. diff --git a/crates/opusenc/tests/it/common/mod.rs b/crates/opusenc/tests/it/common/mod.rs index 9b420f31b..8f00b88e9 100644 --- a/crates/opusenc/tests/it/common/mod.rs +++ b/crates/opusenc/tests/it/common/mod.rs @@ -124,9 +124,9 @@ pub fn encode(mut enc: OpusEncoder, planar: &[Vec]) -> (OpusEncoder, Vec]) -> Vec> { - let mut dec = filmcraft_opus::Decoder::new(head).expect("OpusHead"); + let mut dec = craft_audio::opus::Decoder::new(head).expect("OpusHead"); dec.set_trim_pre_skip(true); let mut out: Vec> = vec![Vec::new(); dec.channels()]; for p in packets { diff --git a/crates/pdf/README.md b/crates/pdf/README.md index b38c9032e..a88adff0b 100644 --- a/crates/pdf/README.md +++ b/crates/pdf/README.md @@ -144,7 +144,7 @@ ffmpeg elsewhere) to check the code tables against an independent implementation ## Shared parser setup and bounds The workspace pins `craft-pdf` to `craft-libs` Git revision -`e11f9d9aebea5f6b8e74643c1ea1ed172ba5f545`, with `default-features = false` and only the explicit `read` +`e7c3dda7f3e339dd2c8d40fc4eb405472be1a1e4`, with `default-features = false` and only the explicit `read` feature. This excludes the backend's bundled fonts and Adobe CMaps and preserves EffectCraft's font and asset policies. The shared parser does not change the supported drawing operators, image codecs, continuously rasterized SVG tree, editable shapes, or EPS interpreter. diff --git a/crates/vp9enc/Cargo.toml b/crates/vp9enc/Cargo.toml deleted file mode 100644 index fe2e767c5..000000000 --- a/crates/vp9enc/Cargo.toml +++ /dev/null @@ -1,12 +0,0 @@ -[package] -name = "effectcraft-vp9enc" -description = "VP9 encoder (profile 0, 8-bit 4:2:0; intra + inter frames, loop filter, rate control) for WebM export, from the VP9 Bitstream & Decoding Process Specification" -version.workspace = true -edition.workspace = true -license.workspace = true -rust-version.workspace = true - -[lints] -workspace = true - -[dependencies] diff --git a/crates/vp9enc/README.md b/crates/vp9enc/README.md deleted file mode 100644 index b9f94faea..000000000 --- a/crates/vp9enc/README.md +++ /dev/null @@ -1,47 +0,0 @@ -# effectcraft-vp9enc - -A small VP9 encoder for WebM export (profile 0, 8-bit 4:2:0, BT.709). - -* **Key frames**: 8×8 intra blocks (DC / V / H / TM prediction chosen per block), 4×4 DCT/ADST - transforms, or the Walsh–Hadamard transform at quality 100 (mathematically lossless). -* **Inter frames**: one reference (the previous frame, LAST), 16×16 or 8×8 blocks chosen by a - cost comparison; motion search from the predicted vectors (NearestMv / NearMv / last frame's - co-located vector) with a whole-sample cross/diamond search refined to half and quarter - samples with the regular 8-tap interpolation filters; NEARESTMV / NEARMV / ZEROMV / NEWMV - chosen by bit cost; blocks with no coefficients worth coding are skipped; 4×4 DCT residual - with a rate–distortion check per transform block. Inter frames are coded error resilient - (`error_resilient_mode` = 1: no dependence on earlier probability contexts or motion vectors), - so `UsePrevFrameMvs` is never needed. No high-precision (1/8) vectors, no compound prediction, - no intra blocks inside inter frames. -* **Loop filter**: the in-loop deblocking filter (§8.8) on every lossy frame; the level is the - best of three trials against the source (sharpness 0, no mode/reference deltas). -* **Probabilities**: per-frame forward updates in the compressed header (`diff_update_prob` / - `update_mv_prob`) of the coefficient, skip, inter-mode, is-inter, reference, partition and - motion-vector probabilities. Tiles are recorded as symbols first, the frame's own counts pick - the updates that pay for their signalling, then the symbols are entropy coded with the updated - probabilities. There is no backward adaptation (`refresh_frame_context` = 0). -* **Rate control**: constant quality (quality → `base_q_idx`) or a target bitrate: a per-frame - q index from a bits ≈ complexity / step model per frame type, key frames getting a larger share - of each key-frame interval, with the running over/undershoot paid back over about a second. -* **Key frame interval** is configurable (1 = every frame a key frame, the old intra-only mode). - -The encoder reconstructs exactly what a decoder reconstructs (inter prediction with the -decoder's vector clamping and edge-clamped reference fetches, integer inverse transforms, the -loop filter) and exposes it (`Vp9Encoder::reconstruction`); the tests require FilmCraft's VP9 -decoder (and ffmpeg, when installed) to output exactly that reconstruction, frame by frame, for -moving synthetic sequences at several sizes and qualities, with tile columns, and lossless. - -On a moving-gradient sequence (192×108, 24 frames) inter coding is 3.9–7× smaller than all-intra -at the same quality setting with equal or better PSNR. - -Written from the **VP9 Bitstream & Decoding Process Specification, version 0.6 (31 March -2016)**: the encoder emits the uncompressed and compressed headers (§6.2, §6.3), partition, mode -info, motion vector and token syntax (§6.4), derives motion vector candidates with the §6.4 -`find_mv_refs` / `find_best_ref_mvs` processes, and reconstructs with the specification's intra -and inter prediction (§8.5), integer inverse transforms (§8.7) and loop filter (§8.8) so it stays -in step with any conformant decoder. The boolean encoder is the inverse of the §9.2 decoder in -the form given in RFC 6386 §7.3. Spec tables (§10) are the specification's; the key-frame tables -were transcribed from FilmCraft's first-party decoder `filmcraft-vp9` (MIT OR Apache-2.0), which -is also the decoder the tests round-trip through and whose behaviour (loop filter edge rules, -vector candidate order) the encoder was checked against; ffmpeg is used only as an external test -oracle. No libvpx or other third-party encoder/decoder source was consulted. diff --git a/crates/vp9enc/src/bool.rs b/crates/vp9enc/src/bool.rs deleted file mode 100644 index 9eda40541..000000000 --- a/crates/vp9enc/src/bool.rs +++ /dev/null @@ -1,259 +0,0 @@ -//! Boolean (arithmetic) encoder: the inverse of the VP9 boolean decoder (spec §9.2), written as -//! the classic carry-propagating range coder described in RFC 6386 §7.3 (VP8 and VP9 share the -//! bool coder). - -pub struct BoolEncoder { - out: Vec, - range: u32, - bottom: u32, - bit_count: i32, - /// Start of the run of 0xFF bytes at the end of `out` (`out.len()` when it ends in another - /// byte). A carry only touches that run and the byte before it. - ff_start: usize, -} - -impl Default for BoolEncoder { - fn default() -> Self { - BoolEncoder::new() - } -} - -impl BoolEncoder { - pub fn new() -> Self { - BoolEncoder { out: Vec::new(), range: 255, bottom: 0, bit_count: 24, ff_start: 0 } - } - - fn push_byte(&mut self, b: u8) { - if b != 255 { - self.ff_start = self.out.len() + 1; - } - self.out.push(b); - } - - /// Carry into the bytes written so far: the trailing 0xFF run becomes 0x00 and the byte - /// before it grows by one (a carry out of the first byte is dropped). Each 0xFF byte is - /// cleared once, so a long run costs O(1) per byte instead of a rescan for every carry. - fn add_one(&mut self) { - let end = self.out.len(); - let start = self.ff_start.min(end); - if let Some(run) = self.out.get_mut(start..) { - run.fill(0); - } - self.ff_start = end; - if let Some(i) = start.checked_sub(1) - && let Some(b) = self.out.get_mut(i) - { - *b = b.wrapping_add(1); - if *b == 255 { - // The grown byte joins the 0xFF bytes before it. - let mut s = i; - while s > 0 && self.out.get(s - 1) == Some(&255) { - s -= 1; - } - self.ff_start = s; - } - } - } - - /// Write `bit` with probability `prob`/256 of it being 0. - #[inline] - pub fn write(&mut self, bit: bool, prob: u8) { - let split = 1 + (((self.range - 1) * prob as u32) >> 8); - if bit { - self.bottom = self.bottom.wrapping_add(split); - self.range -= split; - } else { - self.range = split; - } - while self.range < 128 { - self.range <<= 1; - if self.bottom & (1 << 31) != 0 { - self.add_one(); - } - self.bottom <<= 1; - self.bit_count -= 1; - if self.bit_count == 0 { - self.push_byte((self.bottom >> 24) as u8); - self.bottom &= (1 << 24) - 1; - self.bit_count = 8; - } - } - } - - pub fn literal(&mut self, v: u32, bits: u32) { - for i in (0..bits).rev() { - self.write((v >> i) & 1 != 0, 128); - } - } - - /// Flush (RFC 6386 §7.3) and return the bytes. - pub fn finish(mut self) -> Vec { - let mut c = self.bit_count; - let mut v = self.bottom; - if v & (1u32 << (32 - c)) != 0 { - self.add_one(); - } - v <<= c & 7; - c >>= 3; - while { - c -= 1; - c >= 0 - } { - v <<= 8; - } - for _ in 0..4 { - self.push_byte((v >> 24) as u8); - v <<= 8; - } - self.out - } -} - -#[cfg(test)] -mod tests { - use super::*; - - /// A straightforward decoder per VP9 spec §9.2. - struct Dec<'a> { - d: &'a [u8], - pos: usize, - value: u64, - bits: i32, - range: u32, - } - impl<'a> Dec<'a> { - fn new(d: &'a [u8]) -> Self { - let mut x = Dec { d, pos: 0, value: 0, bits: 0, range: 255 }; - x.fill(); - x - } - fn fill(&mut self) { - while self.bits <= 48 { - let b = if self.pos < self.d.len() { self.d[self.pos] } else { 0 }; - self.pos += 1; - self.value |= (b as u64) << (56 - self.bits); - self.bits += 8; - } - } - fn read(&mut self, p: u8) -> bool { - let split = 1 + (((self.range - 1) * p as u32) >> 8); - let big = (split as u64) << 56; - let bit = if self.value >= big { - self.range -= split; - self.value -= big; - true - } else { - self.range = split; - false - }; - while self.range < 128 { - self.range <<= 1; - self.value <<= 1; - self.bits -= 1; - } - self.fill(); - bit - } - } - - #[test] - fn round_trip() { - let mut e = BoolEncoder::new(); - let mut seq = vec![]; - let mut s = 12345u32; - for i in 0..20000 { - s = s.wrapping_mul(1103515245).wrapping_add(12345); - let p = ((s >> 8) % 255 + 1) as u8; - let bit = (s >> 20) % 256 >= p as u32 || i % 97 == 0; - seq.push((bit, p)); - e.write(bit, p); - } - let d = e.finish(); - let mut dec = Dec::new(&d); - for (i, (b, p)) in seq.into_iter().enumerate() { - assert_eq!(dec.read(p), b, "bit {i}"); - } - } - - #[test] - fn all_ones_carry() { - let mut e = BoolEncoder::new(); - for _ in 0..5000 { - e.write(true, 1); - } - let d = e.finish(); - let mut dec = Dec::new(&d); - for _ in 0..5000 { - assert!(dec.read(1)); - } - } - - /// The encoder as it was before the carry run was tracked: a carry rescans the bytes. - struct Scan { - out: Vec, - range: u32, - bottom: u32, - bit_count: i32, - } - impl Scan { - fn add_one(&mut self) { - let mut i = self.out.len(); - while i > 0 { - i -= 1; - if self.out[i] == 255 { - self.out[i] = 0; - } else { - self.out[i] += 1; - return; - } - } - } - fn write(&mut self, bit: bool, prob: u8) { - let split = 1 + (((self.range - 1) * prob as u32) >> 8); - if bit { - self.bottom = self.bottom.wrapping_add(split); - self.range -= split; - } else { - self.range = split; - } - while self.range < 128 { - self.range <<= 1; - if self.bottom & (1 << 31) != 0 { - self.add_one(); - } - self.bottom <<= 1; - self.bit_count -= 1; - if self.bit_count == 0 { - self.out.push((self.bottom >> 24) as u8); - self.bottom &= (1 << 24) - 1; - self.bit_count = 8; - } - } - } - } - - #[test] - fn carry_run_tracking_matches_the_rescan() { - let mut seed = 99u32; - for case in 0..300 { - let mut fast = BoolEncoder::new(); - let mut slow = Scan { out: vec![], range: 255, bottom: 0, bit_count: 24 }; - // Skewed probabilities make long 0xFF runs and carries likely. - let extreme = case % 3; - for _ in 0..4000 { - seed ^= seed << 13; - seed ^= seed >> 17; - seed ^= seed << 5; - let p = match extreme { - 0 => (seed >> 8) as u8 | 1, - 1 => 1 + (seed >> 8) as u8 % 4, - _ => 255 - (seed >> 8) as u8 % 4, - }; - let bit = (seed >> 24) % 256 >= p as u32 || (seed >> 3).is_multiple_of(11); - fast.write(bit, p); - slow.write(bit, p); - assert_eq!(fast.out, slow.out, "case {case}"); - } - } - } -} diff --git a/crates/vp9enc/src/lf.rs b/crates/vp9enc/src/lf.rs deleted file mode 100644 index d48c57db5..000000000 --- a/crates/vp9enc/src/lf.rs +++ /dev/null @@ -1,299 +0,0 @@ -//! The in-loop deblocking filter (spec 8.8), as the decoder applies it to a frame coded with 4×4 -//! transforms, 8×8 / 16×16 blocks, no segmentation and no reference/mode deltas: superblocks in -//! raster order, for each plane the vertical edges (pass 0) then the horizontal edges (pass 1). -//! Transform edges inside a skipped inter block are left alone; every 4×4 edge uses the 4-tap -//! filter except edges on a 32-sample boundary, which get the 8-tap (flat) filter. - -use crate::{BLOCK_16X16, Mi, Plane}; - -#[derive(Clone, Copy)] -struct Limits { - limit: i32, - blimit: i32, - thresh: i32, -} - -/// Mode-info grid and thresholds shared by every superblock of this frame. -struct FrameFilter<'a> { - mi: &'a [Mi], - mi_rows: usize, - mi_cols: usize, - limits: Limits, -} - -/// Filter the reconstructed planes (Y, U, V, or a prefix of them) in place at `level` (0 = off; -/// sharpness 0). -pub fn filter_frame(planes: &mut [Plane], mi: &[Mi], mi_rows: usize, mi_cols: usize, level: u8) { - if level == 0 { - return; - } - let l = level as i32; - let limit = l.max(1); - let lim = Limits { limit, blimit: 2 * (l + 2) + limit, thresh: l >> 4 }; - let filter = FrameFilter { mi, mi_rows, mi_cols, limits: lim }; - for sbr in 0..mi_rows.div_ceil(8) { - for sbc in 0..mi_cols.div_ceil(8) { - for (plane, p) in planes.iter_mut().enumerate() { - for pass in 0..2 { - filter.sb_plane(p, plane, pass, sbr * 8, sbc * 8); - } - } - } - } -} - -impl FrameFilter<'_> { - fn sb_plane(&self, p: &mut Plane, plane: usize, pass: usize, row: usize, col: usize) { - let Self { mi, mi_rows, mi_cols, limits: lim } = *self; - let (sx, sy) = if plane > 0 { (1, 1) } else { (0, 0) }; - let (sub, edge_len) = if pass == 0 { (sx, 64 >> sy) } else { (sy, 64 >> sx) }; - let stride = p.w; - for edge in 0..(16usize >> sub) { - let mut i = 0; - while i < edge_len { - let (x, y) = if pass == 0 { (col * 8 + edge * (4 << sx), row * 8 + (i << sy)) } else { (col * 8 + (i << sx), row * 8 + edge * (4 << sy)) }; - if x >= 8 * mi_cols || y >= 8 * mi_rows || (pass == 0 && x == 0) || (pass == 1 && y == 0) { - i += 8; - continue; - } - let lc = ((x >> 3) >> sx) << sx; - let lr = ((y >> 3) >> sy) << sy; - let m = mi[lr * mi_cols + lc]; - let size = if sub == 0 { m.size } else { m.size.max(BLOCK_16X16) }; - let n8 = if size >= BLOCK_16X16 { 2 } else { 1 }; - let is_block_edge = if pass == 0 { x & (8 * n8 - 1) == 0 } else { y & (8 * n8 - 1) == 0 }; - let tx_ok = !m.inter || !m.skip; - if !is_block_edge && !tx_ok { - i += 8; - continue; - } - let wide = edge % 8 == 0; - let n = if pass == 0 { (8 * mi_rows - y).div_ceil(1 << sy) } else { (8 * mi_cols - x).div_ceil(1 << sx) }.min(8).min(edge_len - i); - // A transform edge is not filtered in the last odd chroma column. - let odd_last = pass == 1 && sx == 1 && mi_cols & 1 == 1 && edge & 1 == 1; - let (px, py) = (x >> sx, y >> sy); - let mut apply = [false; LANES]; - for (l, a) in apply.iter_mut().enumerate().take(n) { - let is_tx_edge = tx_ok && !(odd_last && x + (l << sx) + 8 >= mi_cols * 8); - *a = is_block_edge || is_tx_edge; - } - if apply.iter().any(|&a| a) { - let pos = py * stride + px; - if pass == 0 { - filter_group::(&mut p.rec, pos, stride, 1, apply, wide, lim); - } else { - filter_group::(&mut p.rec, pos, 1, stride, apply, wide, lim); - } - } - i += 8; - } - } - } -} - -const LANES: usize = 8; - -/// [`filter_lane`] on the 8 lanes of a group at once (lane `l` has its q0 at `pos + l * along`; -/// samples across the edge are `step` apart; `ROWS`: `along` is 1), for lanes whose `apply` is -/// set. Every lane is computed with both filters and the result selected per lane (0 / -1 -/// masks), so the arithmetic vectorizes; lanes left alone are written back unchanged. -#[inline(always)] -fn filter_group(d: &mut [u8], pos: usize, along: usize, step: usize, apply: [bool; LANES], wide: bool, lim: Limits) { - type T = i16; - let mut v = [[0 as T; LANES]; 8]; - let start = pos - 4 * step; - if ROWS { - for (k, vk) in v.iter_mut().enumerate() { - let Some(row) = d.get(start + k * step..).and_then(|r| r.first_chunk::()) else { return }; - *vk = row.map(|s| s as T); - } - } else { - for l in 0..LANES { - let Some(line) = d.get(start + l * along..).and_then(|r| r.first_chunk::<8>()) else { return }; - for k in 0..8 { - v[k][l] = line[k] as T; - } - } - } - let (limit, blimit, thresh) = (lim.limit as T, lim.blimit as T, lim.thresh as T); - let le = |a: T, b: T| -((a <= b) as T); - let sel = |m: T, a: T, b: T| (a & m) | (b & !m); - let (p3, p2, p1, p0, q0, q1, q2, q3) = (&v[0], &v[1], &v[2], &v[3], &v[4], &v[5], &v[6], &v[7]); - let apply: [T; LANES] = apply.map(|a| -(a as T)); - let can_flat = -(wide as T); - let mut mask = [0 as T; LANES]; - let mut hev = [0 as T; LANES]; - let mut flat = [0 as T; LANES]; - for l in 0..LANES { - let d10 = (p1[l] - p0[l]).abs(); - let e10 = (q1[l] - q0[l]).abs(); - mask[l] = apply[l] - & le((p3[l] - p2[l]).abs(), limit) - & le((p2[l] - p1[l]).abs(), limit) - & le(d10, limit) - & le(e10, limit) - & le((q2[l] - q1[l]).abs(), limit) - & le((q3[l] - q2[l]).abs(), limit) - & le((p0[l] - q0[l]).abs() * 2 + (p1[l] - q1[l]).abs() / 2, blimit); - hev[l] = !(le(d10, thresh) & le(e10, thresh)); - flat[l] = can_flat - & mask[l] - & le(d10, 1) - & le(e10, 1) - & le((p2[l] - p0[l]).abs(), 1) - & le((q2[l] - q0[l]).abs(), 1) - & le((p3[l] - p0[l]).abs(), 1) - & le((q3[l] - q0[l]).abs(), 1); - } - if mask.iter().fold(0, |a, &m| a | m) == 0 { - return; - } - let mut out = v; - let c = |x: T| x.clamp(-128, 127); - for l in 0..LANES { - let (ps1, ps0, qs0, qs1) = (p1[l] - 128, p0[l] - 128, q0[l] - 128, q1[l] - 128); - let filter = c((c(ps1 - qs1) & hev[l]) + 3 * (qs0 - ps0)); - let f1 = c(filter + 4) >> 3; - let f2 = c(filter + 3) >> 3; - let f = (f1 + 1) >> 1; - let narrow = mask[l] & !flat[l]; - let outer = narrow & !hev[l]; - out[4][l] = sel(narrow, c(qs0 - f1) + 128, out[4][l]); - out[3][l] = sel(narrow, c(ps0 + f2) + 128, out[3][l]); - out[5][l] = sel(outer, c(qs1 - f) + 128, out[5][l]); - out[2][l] = sel(outer, c(ps1 + f) + 128, out[2][l]); - } - if flat.iter().fold(0, |a, &f| a | f) != 0 { - // 8-tap: positions p2..q2 (indices 1..7), each the rounded mean of a 7-wide window - // (indices clamped to 0..8) plus itself, as a sliding sum. - let s = |k: isize| &v[k.clamp(0, 7) as usize]; - let mut sum = [0 as T; LANES]; - for j in -2isize..=4 { - let a = s(j); - for l in 0..LANES { - sum[l] += a[l]; - } - } - for k in 1usize..7 { - let ki = k as isize; - let (cur, add, sub) = (s(ki), s(ki + 4), s(ki - 3)); - for l in 0..LANES { - let w8 = (sum[l] + cur[l] + 4) >> 3; - out[k][l] = sel(flat[l], w8, out[k][l]); - sum[l] += add[l] - sub[l]; - } - } - } - if ROWS { - for (k, ok) in out.iter().enumerate().take(7).skip(1) { - if let Some(row) = d.get_mut(start + k * step..).and_then(|r| r.first_chunk_mut::()) { - for l in 0..LANES { - row[l] = ok[l] as u8; - } - } - } - } else { - for l in 0..LANES { - if let Some(line) = d.get_mut(start + l * along..).and_then(|r| r.first_chunk_mut::<8>()) { - for k in 1..7 { - line[k] = out[k][l] as u8; - } - } - } - } -} - -/// Filter mask, narrow (4-tap) and 8-tap flat filters of spec 8.8.5 across one edge position; -/// `pos` is q0, samples across the edge are `step` apart. -#[cfg(test)] -fn filter_lane(d: &mut [u8], pos: usize, step: usize, wide: bool, lim: Limits) { - let at = |k: isize| d[(pos as isize + k * step as isize) as usize] as i32; - let (p3, p2, p1, p0) = (at(-4), at(-3), at(-2), at(-1)); - let (q0, q1, q2, q3) = (at(0), at(1), at(2), at(3)); - let Limits { limit, blimit, thresh } = lim; - let mask = (p3 - p2).abs() <= limit - && (p2 - p1).abs() <= limit - && (p1 - p0).abs() <= limit - && (q1 - q0).abs() <= limit - && (q2 - q1).abs() <= limit - && (q3 - q2).abs() <= limit - && (p0 - q0).abs() * 2 + (p1 - q1).abs() / 2 <= blimit; - if !mask { - return; - } - let flat = - wide && (p1 - p0).abs() <= 1 && (q1 - q0).abs() <= 1 && (p2 - p0).abs() <= 1 && (q2 - q0).abs() <= 1 && (p3 - p0).abs() <= 1 && (q3 - q0).abs() <= 1; - let mut set = |k: isize, v: i32| d[(pos as isize + k * step as isize) as usize] = v as u8; - if flat { - // 8-tap: positions p2..q2, each the rounded mean of a 7-wide clamped window plus itself. - let s = [p3, p2, p1, p0, q0, q1, q2, q3]; - let px = |k: isize| s[(k.clamp(-4, 3) + 4) as usize]; - for k in -3isize..3 { - let mut sum = px(k); - for j in -3..=3 { - sum += px(k + j); - } - set(k, (sum + 4) >> 3); - } - return; - } - let c = |v: i32| v.clamp(-128, 127); - let hev = (p1 - p0).abs() > thresh || (q1 - q0).abs() > thresh; - let (ps1, ps0, qs0, qs1) = (p1 - 128, p0 - 128, q0 - 128, q1 - 128); - let filter = c(if hev { c(ps1 - qs1) } else { 0 } + 3 * (qs0 - ps0)); - let f1 = c(filter + 4) >> 3; - let f2 = c(filter + 3) >> 3; - set(0, c(qs0 - f1) + 128); - set(-1, c(ps0 + f2) + 128); - if !hev { - let f = (f1 + 1) >> 1; - set(1, c(qs1 - f) + 128); - set(-2, c(ps1 + f) + 128); - } -} - -#[cfg(test)] -mod tests { - use super::*; - - #[test] - fn group_matches_per_lane_filter() { - let mut seed = 0x1234_5678u32; - let mut rnd = || { - seed ^= seed << 13; - seed ^= seed >> 17; - seed ^= seed << 5; - seed - }; - for case in 0..20000 { - let stride = 24; - let base: Vec = { - let flatish = case % 3 == 0; - let c = (rnd() % 256) as i32; - (0..stride * 24) - .map(|_| if flatish { (c + (rnd() % 3) as i32 - 1).clamp(0, 255) as u8 } else { (c + (rnd() % 40) as i32 - 20).clamp(0, 255) as u8 }) - .collect() - }; - let l = (rnd() % 64) as i32; - let lim = Limits { limit: l.max(1), blimit: 2 * (l + 2) + l.max(1), thresh: l >> 4 }; - let wide = rnd() & 1 == 1; - let apply: [bool; LANES] = std::array::from_fn(|_| rnd() % 4 != 0); - for pass in 0..2 { - let (mut a, mut b) = (base.clone(), base.clone()); - let pos = 8 * stride + 8; - for (l, &ap) in apply.iter().enumerate() { - if ap { - let (p, st) = if pass == 0 { (pos + l * stride, 1) } else { (pos + l, stride) }; - filter_lane(&mut a, p, st, wide, lim); - } - } - if pass == 0 { - filter_group::(&mut b, pos, stride, 1, apply, wide, lim); - } else { - filter_group::(&mut b, pos, 1, stride, apply, wide, lim); - } - assert_eq!(a, b, "case {case} pass {pass}"); - } - } - } -} diff --git a/crates/vp9enc/src/lib.rs b/crates/vp9enc/src/lib.rs deleted file mode 100644 index bf24309a9..000000000 --- a/crates/vp9enc/src/lib.rs +++ /dev/null @@ -1,503 +0,0 @@ -//! A VP9 encoder (profile 0, 8-bit 4:2:0), written from the *VP9 Bitstream & Decoding Process -//! Specification* v0.6 by mirroring its decoding process in reverse (see README.md). -//! -//! * **Key frames**: 64×64 superblocks split to 8×8 intra blocks (DC / V / H / TM chosen per -//! block), 4×4 DCT/ADST transforms (the Walsh–Hadamard transform at quality 100: lossless). -//! * **Inter frames** (error resilient, single reference: the previous frame as LAST): 16×16 or -//! 8×8 blocks with motion vectors found by a candidate + diamond search refined to quarter -//! samples with the regular 8-tap filters; NEARESTMV / NEARMV / ZEROMV / NEWMV chosen by cost, -//! motion vector prediction exactly as the decoder derives it; skipped blocks; 4×4 DCT residual. -//! * **In-loop deblocking** (8.8) on every lossy frame; the level is picked per frame by trial. -//! * **Probabilities**: per-frame forward updates (compressed header) of the coefficient, skip, -//! inter mode, reference, partition and motion vector probabilities, chosen from the frame's -//! own symbol counts (no backward adaptation: `refresh_frame_context` = 0). -//! * **Rate control**: constant quality (quality → base_q_idx), or a target bitrate with a -//! per-frame q_idx model. -//! -//! The encoder reconstructs exactly what a decoder reconstructs (prediction, integer inverse -//! transforms, loop filter), so prediction never drifts; [`Vp9Encoder::reconstruction`] returns -//! it. - -#![deny(clippy::unwrap_used, clippy::expect_used, clippy::panic, clippy::unimplemented, clippy::todo, clippy::unreachable)] - -mod bool; -mod lf; -mod mc; -mod probs; -mod tables; -mod tile; -mod transform; - -pub use mc::Mv; -use probs::Probs; -use tables::{AC_QLOOKUP, DC_QLOOKUP}; -use transform::Quant; - -/// Largest width or height the 16-bit frame size fields can hold. -const MAX_DIMENSION: u32 = 1 << 16; - -pub(crate) const BLOCK_8X8: u8 = 3; -pub(crate) const BLOCK_16X16: u8 = 6; - -/// Encoder settings. -#[derive(Clone, Debug, PartialEq)] -pub struct EncoderConfig { - /// Frame size in samples; the stream stores size - 1 in 16 bits, so values above 65536 are - /// clamped to 65536 by [`Vp9Encoder::new`]. - pub width: u32, - pub height: u32, - /// 1..=100; 100 is lossless. With a target bitrate: the quality of the first frame. - pub quality: u8, - /// Full-range (0–255) rather than limited (16–235) YUV; only signalled. - pub full_range: bool, - /// A key frame every this many frames (1 = every frame is a key frame; 0 = only the first). - pub keyframe_interval: u32, - /// Target bitrate in kbit/s (`None`: constant quality). - pub target_kbps: Option, - /// Frames per second (rate control). - pub frame_rate: f64, - /// The in-loop deblocking filter. - pub loop_filter: bool, -} - -impl Default for EncoderConfig { - fn default() -> Self { - EncoderConfig { width: 0, height: 0, quality: 80, full_range: false, keyframe_interval: 60, target_kbps: None, frame_rate: 30.0, loop_filter: true } - } -} - -/// One coded frame. -#[derive(Clone, Debug)] -pub struct EncodedFrame { - pub data: Vec, - pub key: bool, - pub q_idx: u8, - pub filter_level: u8, -} - -/// A plane being coded: source and reconstruction at superblock-aligned size. -pub(crate) struct Plane { - /// Stride (superblock-aligned width). - pub w: usize, - /// Last valid column / row for intra prediction edges (the 8-aligned mode-info grid). - pub max_x: usize, - pub max_y: usize, - /// Visible size. - pub vis_w: usize, - pub vis_h: usize, - pub src: Vec, - pub rec: Vec, -} - -impl Plane { - fn new(data: &[u8], vw: usize, vh: usize, w: usize, h: usize, max_x: usize, max_y: usize) -> Plane { - let mut src = vec![0u8; w * h]; - for y in 0..h { - let sy = y.min(vh.saturating_sub(1)); - for x in 0..w { - let sx = x.min(vw.saturating_sub(1)); - src[y * w + x] = data.get(sy * vw + sx).copied().unwrap_or(128); - } - } - Plane { w, max_x, max_y, vis_w: vw, vis_h: vh, src, rec: vec![0; w * h] } - } -} - -/// Mode info of one 8×8 position. -#[derive(Clone, Copy, Debug, Default)] -pub(crate) struct Mi { - pub inter: bool, - /// Intra mode (0..=9) or inter mode (NEARESTMV..=NEWMV). - pub mode: u8, - pub mv: Mv, - /// Skip as the loop filter sees it (inter blocks without coefficients). - pub skip: bool, - pub size: u8, -} - -/// Rate control state (target bitrate mode). -#[derive(Clone, Debug)] -struct RateControl { - /// bits ≈ complexity / step: per frame type, from the last frame of that type. - key_complexity: Option, - inter_complexity: Option, - /// Bits spent minus bits budgeted so far. - debt: f64, - last_inter_q: Option, -} - -pub struct Vp9Encoder { - cfg: EncoderConfig, - base_qidx: u8, - lossless: bool, - frames: u64, - since_key: u32, - /// The reconstruction of the last frame (the LAST reference): planes at their strides. - reference: Option<[Plane; 3]>, - mvs: Vec, - rc: RateControl, -} - -fn quality_to_qidx(q: u8) -> u8 { - // 99 ≈ 3, 90 ≈ 30, 50 ≈ 150, 1 ≈ 255. - ((100 - q.clamp(1, 99) as i32) * 255 / 85).clamp(1, 255) as u8 -} - -impl Vp9Encoder { - pub fn new(mut cfg: EncoderConfig) -> Self { - // The frame header stores width - 1 and height - 1 in 16 bits. - cfg.width = cfg.width.min(MAX_DIMENSION); - cfg.height = cfg.height.min(MAX_DIMENSION); - let q = cfg.quality.clamp(1, 100); - let lossless = q >= 100 && cfg.target_kbps.is_none(); - let base_qidx = if lossless { 0 } else { quality_to_qidx(q) }; - Vp9Encoder { - cfg, - base_qidx, - lossless, - frames: 0, - since_key: 0, - reference: None, - mvs: vec![], - rc: RateControl { key_complexity: None, inter_complexity: None, debt: 0.0, last_inter_q: None }, - } - } - - pub fn config(&self) -> &EncoderConfig { - &self.cfg - } - - /// Encode one frame from planar 8-bit 4:2:0 (`y`: width×height, `u`/`v`: - /// ((w+1)/2)×((h+1)/2), tightly packed). - pub fn encode_yuv420(&mut self, y: &[u8], u: &[u8], v: &[u8]) -> Vec { - self.encode_frame(y, u, v, false).data - } - - /// The decoder's reconstruction of the last coded frame (visible area, tightly packed Y, U, - /// V). - pub fn reconstruction(&self) -> Option<[Vec; 3]> { - let r = self.reference.as_ref()?; - Some(std::array::from_fn(|i| { - let p = &r[i]; - (0..p.vis_h).flat_map(|y| p.rec[y * p.w..y * p.w + p.vis_w].iter().copied()).collect() - })) - } - - fn is_key_due(&self, force: bool) -> bool { - force || self.reference.is_none() || self.cfg.keyframe_interval == 1 || (self.cfg.keyframe_interval > 1 && self.since_key >= self.cfg.keyframe_interval) - } - - /// The q index of the next frame. - fn pick_qidx(&mut self, key: bool) -> u8 { - if self.lossless { - return 0; - } - let Some(kbps) = self.cfg.target_kbps else { return self.base_qidx }; - let per_frame = kbps as f64 * 1000.0 / self.cfg.frame_rate.max(1.0); - let interval = if self.cfg.keyframe_interval == 0 { 300.0 } else { self.cfg.keyframe_interval as f64 }; - // Key frames get a larger share of the group's budget. - let boost = (interval / 6.0).clamp(1.0, 5.0); - let group = per_frame * interval; - let key_share = group * boost / (boost + interval - 1.0); - let inter_share = (group - key_share) / (interval - 1.0).max(1.0); - let mut target = if key { key_share } else { inter_share }; - // Pay back (or spend) the running difference over about a second. - let horizon = self.cfg.frame_rate.max(1.0); - target = (target - self.rc.debt / horizon).max(target * 0.25); - let complexity = if key { self.rc.key_complexity } else { self.rc.inter_complexity.or(self.rc.key_complexity.map(|c| c / (boost * 2.0))) }; - let Some(cx) = complexity else { return self.base_qidx }; - let step = (cx / target).max(4.0); - let mut q = (0..=255u8).min_by_key(|&q| ((AC_QLOOKUP[q as usize] as f64 - step).abs() * 16.0) as i64).unwrap_or(self.base_qidx); - if !key && let Some(last) = self.rc.last_inter_q { - q = q.clamp(last.saturating_sub(24), last.saturating_add(24)); - } - q.max(1) - } - - fn rc_update(&mut self, key: bool, qidx: u8, bytes: usize) { - let Some(kbps) = self.cfg.target_kbps else { return }; - let bits = bytes as f64 * 8.0; - let cx = bits * AC_QLOOKUP[qidx as usize] as f64; - if key { - self.rc.key_complexity = Some(cx); - } else { - self.rc.inter_complexity = Some(match self.rc.inter_complexity { - Some(c) => c * 0.5 + cx * 0.5, - None => cx, - }); - self.rc.last_inter_q = Some(qidx); - } - let per_frame = kbps as f64 * 1000.0 / self.cfg.frame_rate.max(1.0); - self.rc.debt += bits - per_frame; - } - - /// Encode one frame; `force_key` starts a new key frame. Planes shorter than the configured - /// size encode nothing (an empty frame): the coding planes are not sized and filled from - /// samples that are not there. - pub fn encode_frame(&mut self, y: &[u8], u: &[u8], v: &[u8], force_key: bool) -> EncodedFrame { - let (w, h) = (self.cfg.width.max(1) as usize, self.cfg.height.max(1) as usize); - let empty = || EncodedFrame { data: vec![], key: false, q_idx: 0, filter_level: 0 }; - // Checked: on 32-bit targets (wasm32) a huge size would wrap to a small one. - let (Some(luma), Some(chroma)) = (w.checked_mul(h), w.div_ceil(2).checked_mul(h.div_ceil(2))) else { - return empty(); - }; - if y.len() < luma || u.len() < chroma || v.len() < chroma { - return empty(); - } - let key = self.is_key_due(force_key); - let qidx = self.pick_qidx(key); - let lossless = qidx == 0; - let mi_cols = w.div_ceil(8); - let mi_rows = h.div_ceil(8); - let sb_cols = mi_cols.div_ceil(8); - let sb_rows = mi_rows.div_ceil(8); - let (pw, ph) = (sb_cols * 64, sb_rows * 64); - let (cw, ch) = (w.div_ceil(2), h.div_ceil(2)); - let mut planes = [ - Plane::new(y, w, h, pw, ph, mi_cols * 8 - 1, mi_rows * 8 - 1), - Plane::new(u, cw, ch, pw / 2, ph / 2, mi_cols * 4 - 1, mi_rows * 4 - 1), - Plane::new(v, cw, ch, pw / 2, ph / 2, mi_cols * 4 - 1, mi_rows * 4 - 1), - ]; - // Tile columns: as few as the width allows (tile_info, 6.2.13). - let mut min_log2 = 0; - while (64usize << min_log2) < sb_cols { - min_log2 += 1; - } - let mut max_log2 = 1; - while (sb_cols >> max_log2) >= 4 { - max_log2 += 1; - } - max_log2 -= 1; - let tile_cols = 1usize << min_log2; - let quant = Quant { dcq: DC_QLOOKUP[qidx as usize], acq: AC_QLOOKUP[qidx as usize], lossless, round_dc: 0.5, round_ac: 0.38 }; - let step = AC_QLOOKUP[qidx as usize] as f64 / 8.0; - let lambda_sse = 0.35 * step * step; - let refs = if key { - None - } else { - self.reference.as_ref().map(|r| std::array::from_fn::<_, 3, _>(|i| mc::RefPlane { data: &r[i].rec, stride: r[i].w, w: r[i].vis_w, h: r[i].vis_h })) - }; - let prev = (!key && self.mvs.len() == mi_cols * mi_rows).then_some(&self.mvs[..]); - let phases = refs.as_ref().map(|r| mc::PhasePlanes::new(&r[0], 48)); - let fp = tile::FrameParams { key, quant, mi_cols, mi_rows, refs, phases, prev_mvs: prev, lambda_sad: lambda_sse.sqrt(), lambda_sse }; - let mut mi = vec![Mi::default(); mi_cols * mi_rows]; - let mut writers = vec![]; - for t in 0..tile_cols { - let start = (((t * sb_cols) >> min_log2) * 8).min(mi_cols); - let end = ((((t + 1) * sb_cols) >> min_log2) * 8).min(mi_cols); - let mut tile = tile::Tile::new(&fp, &mut planes, &mut mi, start, end); - tile.code(); - writers.push(tile.w); - } - // Forward probability updates from this frame's counts. - let defaults = Probs::defaults(); - let mut counts = vec![[0u32; 2]; probs::NPROBS]; - for wr in &writers { - wr.count(&mut counts); - } - let wanted = probs::plan_updates(&defaults, &counts, !key); - let (compressed, used) = probs::compressed_header(&defaults, &wanted, lossless, !key); - let tiles: Vec> = writers.iter().map(|wr| wr.emit(&used)).collect(); - drop(fp); - // Loop filter: the level with the least error against the source. - let level = if lossless || !self.cfg.loop_filter { 0 } else { choose_level(&mut planes, &mi, mi_rows, mi_cols, qidx, key) }; - lf::filter_frame(&mut planes, &mi, mi_rows, mi_cols, level); - // Uncompressed header. - let mut bw = BitWriter::default(); - bw.put(2, 2); // frame_marker - bw.put(0, 1); // profile low bit - bw.put(0, 1); // profile high bit - bw.put(0, 1); // show_existing_frame - bw.put(!key as u32, 1); // frame_type - bw.put(1, 1); // show_frame - if key { - bw.put(0, 1); // error_resilient_mode - bw.put(0x49, 8); - bw.put(0x83, 8); - bw.put(0x42, 8); - bw.put(2, 3); // color_space CS_BT_709 - bw.put(self.cfg.full_range as u32, 1); - bw.put(w as u32 - 1, 16); - bw.put(h as u32 - 1, 16); - bw.put(0, 1); // render_and_frame_size_different - bw.put(0, 1); // refresh_frame_context - bw.put(1, 1); // frame_parallel_decoding_mode - } else { - bw.put(1, 1); // error_resilient_mode (no reliance on earlier contexts or vectors) - bw.put(0x01, 8); // refresh_frame_flags: slot 0 becomes the new LAST - for _ in 0..3 { - bw.put(0, 3); // ref_frame_idx: slot 0 - bw.put(0, 1); // sign bias - } - bw.put(1, 1); // found_ref: the size of LAST - bw.put(0, 1); // render_and_frame_size_different - bw.put(0, 1); // allow_high_precision_mv - bw.put(0, 1); // is_filter_switchable - bw.put(1, 2); // raw_interpolation_filter → EIGHTTAP (regular) - } - bw.put(0, 2); // frame_context_idx - bw.put(level as u32, 6); // loop_filter_level - bw.put(0, 3); // sharpness - bw.put(0, 1); // mode_ref_delta_enabled - bw.put(qidx as u32, 8); - bw.put(0, 1); // delta_q_y_dc - bw.put(0, 1); // delta_q_uv_dc - bw.put(0, 1); // delta_q_uv_ac - bw.put(0, 1); // segmentation_enabled - if min_log2 < max_log2 { - bw.put(0, 1); // no extra tile columns - } - bw.put(0, 1); // tile_rows_log2 = 0 - bw.put(compressed.len() as u32, 16); - let mut out = bw.finish(); - out.extend_from_slice(&compressed); - let n = tiles.len(); - for (i, t) in tiles.into_iter().enumerate() { - if i + 1 < n { - out.extend_from_slice(&(t.len() as u32).to_be_bytes()); - } - out.extend_from_slice(&t); - } - // A frame mustn't end in a byte that reads as a superframe marker (0b110xxxxx, Annex B): - // decoders would take its last bytes for a superframe index and reject the frame. The - // last tile's bool-coded data may end in zero padding (9.2.3), so a zero byte fixes it. - if out.last().is_some_and(|b| b & 0xe0 == 0xc0) { - out.push(0); - } - // State for the next frame. - self.mvs = mi.iter().map(|m| if m.inter { m.mv } else { Mv::ZERO }).collect(); - self.reference = Some(planes); - self.frames += 1; - self.since_key = if key { 1 } else { self.since_key + 1 }; - self.rc_update(key, qidx, out.len()); - EncodedFrame { data: out, key, q_idx: qidx, filter_level: level } - } -} - -/// Try a few loop filter levels on the luma reconstruction; the one closest to the source -/// (squared error over the visible area) wins (0 when filtering doesn't help). -fn choose_level(planes: &mut [Plane; 3], mi: &[Mi], mi_rows: usize, mi_cols: usize, qidx: u8, key: bool) -> u8 { - let guess = ((qidx as f64 * if key { 0.25 } else { 0.2 }).round() as u8).clamp(1, 63); - let sse = |p: &Plane| -> u64 { - let mut s = 0u64; - for y in 0..p.vis_h { - for x in 0..p.vis_w { - let d = p.src[y * p.w + x] as i64 - p.rec[y * p.w + x] as i64; - s += (d * d) as u64; - } - } - s - }; - let mut best = (sse(&planes[0]), 0u8); - let saved = planes[0].rec.clone(); - for level in [guess / 2, guess, (guess as u16 * 3 / 2).min(63) as u8] { - if level == 0 { - continue; - } - lf::filter_frame(&mut planes[..1], mi, mi_rows, mi_cols, level); - let s = sse(&planes[0]); - planes[0].rec.copy_from_slice(&saved); - if s < best.0 { - best = (s, level); - } - } - best.1 -} - -#[derive(Default)] -struct BitWriter { - out: Vec, - acc: u32, - n: u32, -} - -impl BitWriter { - fn put(&mut self, v: u32, bits: u32) { - for i in (0..bits).rev() { - self.acc = (self.acc << 1) | ((v >> i) & 1); - self.n += 1; - if self.n == 8 { - self.out.push(self.acc as u8); - self.acc = 0; - self.n = 0; - } - } - } - fn finish(mut self) -> Vec { - if self.n > 0 { - self.out.push((self.acc << (8 - self.n)) as u8); - } - self.out - } -} - -// ---------------------------------------------------------------- colour conversion - -/// BT.709 RGB(A) 8-bit → planar YUV 4:2:0 (limited or full range). -pub fn rgba_to_yuv420(rgba: &[u8], w: u32, h: u32, full_range: bool) -> (Vec, Vec, Vec) { - let (w, h) = (w as usize, h as usize); - let (cw, ch) = (w.div_ceil(2), h.div_ceil(2)); - let mut y = vec![0u8; w * h]; - let mut u = vec![0u8; cw * ch]; - let mut v = vec![0u8; cw * ch]; - let (ys, yo, cs) = if full_range { (255.0, 0.0, 255.0) } else { (219.0, 16.0, 224.0) }; - let px = |x: usize, yy: usize| -> [f32; 3] { - let i = (yy.min(h - 1) * w + x.min(w - 1)) * 4; - [rgba[i] as f32 / 255.0, rgba[i + 1] as f32 / 255.0, rgba[i + 2] as f32 / 255.0] - }; - let luma = |c: [f32; 3]| 0.2126 * c[0] + 0.7152 * c[1] + 0.0722 * c[2]; - for yy in 0..h { - for x in 0..w { - y[yy * w + x] = (luma(px(x, yy)) * ys + yo).round().clamp(0.0, 255.0) as u8; - } - } - for cy in 0..ch { - for cx in 0..cw { - let mut acc = [0.0f32; 3]; - for (dx, dy) in [(0, 0), (1, 0), (0, 1), (1, 1)] { - let c = px(cx * 2 + dx, cy * 2 + dy); - for k in 0..3 { - acc[k] += c[k] / 4.0; - } - } - let l = luma(acc); - let cb = (acc[2] - l) / 1.8556; - let cr = (acc[0] - l) / 1.5748; - u[cy * cw + cx] = (cb * cs + 128.0).round().clamp(0.0, 255.0) as u8; - v[cy * cw + cx] = (cr * cs + 128.0).round().clamp(0.0, 255.0) as u8; - } - } - (y, u, v) -} - -/// The alpha channel as a luma plane (full range) with neutral chroma — the second VP9 stream of -/// WebM alpha. -pub fn alpha_to_yuv420(rgba: &[u8], w: u32, h: u32) -> (Vec, Vec, Vec) { - let (w, h) = (w as usize, h as usize); - let (cw, ch) = (w.div_ceil(2), h.div_ceil(2)); - let y: Vec = rgba.as_chunks::<4>().0.iter().take(w * h).map(|p| p[3]).collect(); - (y, vec![128; cw * ch], vec![128; cw * ch]) -} - -#[cfg(test)] -mod tests { - use super::*; - - #[test] - fn sizes_beyond_the_16_bit_header_fields_are_clamped() { - let enc = Vp9Encoder::new(EncoderConfig { width: 70_000, height: u32::MAX, ..Default::default() }); - assert_eq!((enc.cfg.width, enc.cfg.height), (65_536, 65_536)); - let enc = Vp9Encoder::new(EncoderConfig { width: 65_536, height: 1, ..Default::default() }); - assert_eq!((enc.cfg.width, enc.cfg.height), (65_536, 1)); - } - - #[test] - fn planes_shorter_than_the_frame_encode_nothing() { - // 65536×65536 declared, a few bytes given: no 4 GiB coding planes are made. - let mut enc = Vp9Encoder::new(EncoderConfig { width: 65_536, height: 65_536, ..Default::default() }); - let f = enc.encode_frame(&[1, 2, 3], &[], &[], true); - assert!(f.data.is_empty()); - let mut enc = Vp9Encoder::new(EncoderConfig { width: 16, height: 16, ..Default::default() }); - assert!(!enc.encode_frame(&[128; 256], &[128; 64], &[128; 64], true).data.is_empty()); - } -} diff --git a/crates/vp9enc/src/mc.rs b/crates/vp9enc/src/mc.rs deleted file mode 100644 index 5d897f854..000000000 --- a/crates/vp9enc/src/mc.rs +++ /dev/null @@ -1,265 +0,0 @@ -//! Inter prediction exactly as the decoder forms it: motion vector clamping (spec 8.5.2.2) and -//! the unscaled block inter prediction process (8.5.2.4) with the regular 8-tap filters and -//! reference samples clamped to the reference frame's visible area. - -use crate::tables::SUBPEL_REGULAR; - -/// A motion vector in 1/8 luma samples. -#[derive(Clone, Copy, Debug, Default, PartialEq, Eq)] -pub struct Mv { - pub row: i32, - pub col: i32, -} - -impl Mv { - pub const ZERO: Mv = Mv { row: 0, col: 0 }; - pub fn new(row: i32, col: i32) -> Mv { - Mv { row, col } - } - pub fn minus(self, o: Mv) -> Mv { - Mv::new(self.row - o.row, self.col - o.col) - } -} - -/// A reference plane: `w`×`h` visible samples at `stride`. -pub struct RefPlane<'a> { - pub data: &'a [u8], - pub stride: usize, - pub w: usize, - pub h: usize, -} - -/// The block's motion vector for `plane` in 1/16 samples of that plane, clamped as the -/// decoder clamps it. `n8`: block size in 8×8 units. -pub fn clamp_for_plane(mv: Mv, r: usize, c: usize, n8: usize, mi_rows: usize, mi_cols: usize, chroma: bool) -> (i32, i32) { - let s = chroma as i32; - let (r, c, b) = (r as i32, c as i32, n8 as i32); - let to_top = (-(r * 8 * 16)) >> s; - let to_bottom = (((mi_rows as i32 - b - r) * 8) * 16) >> s; - let to_left = (-(c * 8 * 16)) >> s; - let to_right = (((mi_cols as i32 - b - c) * 8) * 16) >> s; - let spel = (4 + ((b * 8) >> s)) << 4; - let row = ((2 * mv.row) >> s).clamp(to_top - spel, (to_bottom + spel - 16).max(to_top - spel)); - let col = ((2 * mv.col) >> s).clamp(to_left - spel, (to_right + spel - 16).max(to_left - spel)); - (row, col) -} - -/// Predict a `w`×`h` block whose top-left sample is at (`x16`, `y16`) (1/16 samples) into `out` -/// (stride `w`). -pub fn predict(r: &RefPlane, x16: i32, y16: i32, w: usize, h: usize, out: &mut [u8]) { - let (x0, y0) = (x16 >> 4, y16 >> 4); - let (fx, fy) = ((x16 & 15) as usize, (y16 & 15) as usize); - let (lx, ly) = (r.w as i32 - 1, r.h as i32 - 1); - let inside = x0 - 3 >= 0 && y0 - 3 >= 0 && x0 + w as i32 + 4 <= lx && y0 + h as i32 + 4 <= ly; - let at = |x: i32, y: i32| -> i32 { - if inside { r.data[y as usize * r.stride + x as usize] as i32 } else { r.data[y.clamp(0, ly) as usize * r.stride + x.clamp(0, lx) as usize] as i32 } - }; - let fh = &SUBPEL_REGULAR[fx]; - let fv = &SUBPEL_REGULAR[fy]; - if inside && w <= 16 && h <= 16 { - // Rows straight from the reference (no edge clamping needed). - let (xs, ys) = ((x0 - 3) as usize, (y0 - 3) as usize); - let row = |i: usize| &r.data[(ys + i) * r.stride + xs..]; - if fx == 0 && fy == 0 { - for i in 0..h { - out[i * w..i * w + w].copy_from_slice(&row(i + 3)[3..3 + w]); - } - return; - } - let mut tmp = [0u8; 23 * 16]; - for i in 0..h + 7 { - let s = row(i); - filt8(std::array::from_fn(|t| &s[t..]), fh, &mut tmp[i * w..i * w + w]); - } - for i in 0..h { - filt8(std::array::from_fn(|t| &tmp[(i + t) * w..]), fv, &mut out[i * w..i * w + w]); - } - return; - } - if fx == 0 && fy == 0 { - for i in 0..h { - for j in 0..w { - out[i * w + j] = at(x0 + j as i32, y0 + i as i32) as u8; - } - } - return; - } - // Horizontal pass over h + 7 rows (from y0 - 3), then vertical. - let mut tmp = [0u8; 23 * 16]; - for i in 0..h + 7 { - let y = y0 - 3 + i as i32; - for j in 0..w { - let x = x0 + j as i32 - 3; - let mut acc = 64; - for t in 0..8 { - acc += fh[t] * at(x + t as i32, y); - } - tmp[i * w + j] = (acc >> 7).clamp(0, 255) as u8; - } - } - for i in 0..h { - for j in 0..w { - let mut acc = 64; - for t in 0..8 { - acc += fv[t] * tmp[(i + t) * w + j] as i32; - } - out[i * w + j] = (acc >> 7).clamp(0, 255) as u8; - } - } -} - -/// The reference luma pre-interpolated at the 16 quarter-sample phases the encoder uses (motion -/// vectors are even in 1/8 units: fractions 0, 4, 8, 12 in 1/16), over the visible area plus a -/// border. Each phase plane holds exactly what [`predict`] returns at that phase (the decoder's -/// separable filter with edge clamping), so motion search reads predictions directly. -pub struct PhasePlanes { - border: usize, - stride: usize, - rows: usize, - planes: Vec>, -} - -impl PhasePlanes { - pub fn new(r: &RefPlane, border: usize) -> PhasePlanes { - const M: usize = 4; - let (pw, ph) = (r.w + 2 * border, r.h + 2 * border); - // Edge-replicated source with M more samples on every side for the filter taps. - let rs = pw + 2 * M; - let rh = ph + 2 * M; - let off = (border + M) as i32; - let mut raw = vec![0u8; rs * rh]; - let o = off as usize; - for (y, row) in raw.chunks_exact_mut(rs).enumerate() { - let sy = (y as i32 - off).clamp(0, r.h as i32 - 1) as usize; - let src = &r.data[sy * r.stride..sy * r.stride + r.w]; - // Edge replication: src[clamp(x - off, 0, w - 1)]. - let (left, rest) = row.split_at_mut(o.min(rs)); - left.fill(src[0]); - let n = r.w.min(rest.len()); - rest[..n].copy_from_slice(&src[..n]); - rest[n..].fill(src[r.w - 1]); - } - // Horizontal phases over every raw row (inner columns). - let hpass = |fx: usize| -> Vec { - let f = &SUBPEL_REGULAR[fx]; - let mut out = vec![0u8; pw * rh]; - for (row, o) in raw.chunks_exact(rs).zip(out.chunks_exact_mut(pw)) { - filt8(std::array::from_fn(|t| &row[M - 3 + t..]), f, o); - } - out - }; - let unfiltered: Vec = (0..rh).flat_map(|y| raw[y * rs + M..y * rs + M + pw].iter().copied()).collect(); - let h: Vec> = (0..4).map(|k| if k == 0 { unfiltered.clone() } else { hpass(k * 4) }).collect(); - let mut planes = Vec::with_capacity(16); - for fy in 0..4 { - for hp in &h { - if fy == 0 { - planes.push(hp[M * pw..(M + ph) * pw].to_vec()); - continue; - } - let f = &SUBPEL_REGULAR[fy * 4]; - let mut out = vec![0u8; pw * ph]; - for (y, o) in out.chunks_exact_mut(pw).enumerate() { - filt8(std::array::from_fn(|t| &hp[(y + M + t - 3) * pw..]), f, o); - } - planes.push(out); - } - } - PhasePlanes { border, stride: pw, rows: ph, planes } - } - - /// The prediction of the `n`×`n` block at (`x16`, `y16`) (fractions multiples of 4) as rows of - /// a phase plane: (plane, offset of the first sample, stride). `None` beyond the border. - pub fn block(&self, x16: i32, y16: i32, n: usize) -> Option<(&[u8], usize, usize)> { - let (x0, y0) = ((x16 >> 4) + self.border as i32, (y16 >> 4) + self.border as i32); - let (fx, fy) = ((x16 & 15) as usize, (y16 & 15) as usize); - if fx % 4 != 0 || fy % 4 != 0 || x0 < 0 || y0 < 0 || x0 as usize + n > self.stride || y0 as usize + n > self.rows { - return None; - } - Some((&self.planes[(fy / 4) * 4 + fx / 4], y0 as usize * self.stride + x0 as usize, self.stride)) - } -} - -/// `out[x] = clip(Round2(sum_t f[t] * s[t][x], 7))` for 8-bit samples. Lanes are 16-bit sums -/// kept modulo 2^16: with the regular filter every sum plus the rounding term lies in -/// [-13706, 46474], so wrapped non-negative sums stay below 0xC000 and negative ones (clipped -/// to 0) land above it (the decoder's `narrow8`). -#[inline] -fn filt8(s: [&[u8]; 8], f: &[i32; 8], out: &mut [u8]) { - let n = out.len(); - let mut x = 0; - while x + 32 <= n { - filt_chunk::<32>(&s, f, out, x); - x += 32; - } - for w in [16, 8, 4] { - if x + w <= n { - match w { - 16 => filt_chunk::<16>(&s, f, out, x), - 8 => filt_chunk::<8>(&s, f, out, x), - _ => filt_chunk::<4>(&s, f, out, x), - } - x += w; - } - } - for j in x..n { - let acc: i32 = 64 + (0..8).map(|t| f[t] * s[t][j] as i32).sum::(); - out[j] = (acc >> 7).clamp(0, 255) as u8; - } -} - -#[inline(always)] -fn filt_chunk(s: &[&[u8]; 8], f: &[i32; 8], out: &mut [u8], x: usize) { - let mut acc = [64u16; W]; - for t in 0..8 { - let c = f[t] as u16; - let Some(p) = s[t].get(x..).and_then(|p| p.first_chunk::()) else { return }; - for j in 0..W { - acc[j] = acc[j].wrapping_add(c.wrapping_mul(p[j] as u16)); - } - } - if let Some(o) = out.get_mut(x..).and_then(|o| o.first_chunk_mut::()) { - for j in 0..W { - o[j] = if acc[j] < 0xC000 { (acc[j] >> 7).min(255) as u8 } else { 0 }; - } - } -} - -#[cfg(test)] -mod tests { - #[test] - fn phase_planes_match_predict() { - let (w, h) = (37usize, 29usize); - let data: Vec = (0..w * h).map(|i| ((i * 7919) % 251) as u8).collect(); - let r = RefPlane { data: &data, stride: w, w, h }; - let pp = PhasePlanes::new(&r, 24); - let mut out = [0u8; 64]; - for y16 in (-20 * 16..(h as i32 + 10) * 16).step_by(4 * 7) { - for x16 in (-20 * 16..(w as i32 + 10) * 16).step_by(4 * 5) { - let Some((p, o, s)) = pp.block(x16, y16, 8) else { continue }; - predict(&r, x16, y16, 8, 8, &mut out); - for i in 0..8 { - assert_eq!(&p[o + i * s..o + i * s + 8], &out[i * 8..i * 8 + 8], "({x16}, {y16}) row {i}"); - } - } - } - } - - use super::*; - - #[test] - fn whole_sample_and_linear_ramps() { - let (w, h) = (40usize, 30usize); - let data: Vec = (0..w * h).map(|i| ((i % w) * 4 + (i / w)) as u8).collect(); - let r = RefPlane { data: &data, stride: w, w, h }; - let mut out = [0u8; 64]; - predict(&r, 10 << 4, 8 << 4, 8, 8, &mut out); - assert_eq!(out[0], data[8 * w + 10]); - // Half a sample right on a linear ramp: exact midpoint. - predict(&r, (10 << 4) + 8, 8 << 4, 8, 8, &mut out); - assert_eq!(out[0], data[8 * w + 10] + 2); - // Off the left edge: clamped to column 0. - predict(&r, -(20 << 4), 0, 4, 4, &mut out); - assert_eq!(out[0], data[0]); - } -} diff --git a/crates/vp9enc/src/probs.rs b/crates/vp9enc/src/probs.rs deleted file mode 100644 index c089cb96f..000000000 --- a/crates/vp9enc/src/probs.rs +++ /dev/null @@ -1,465 +0,0 @@ -//! Probabilities: the frame's adaptable probability table (one flat array laid out in the order -//! the compressed header updates them), a symbol recorder that codes every boolean decision -//! against either a fixed probability or an entry of that table, bit-cost estimates, and the -//! per-frame forward updates (spec 6.3: `diff_update_prob` with `decode_term_subexp` / -//! `inv_remap_prob`, and `update_mv_prob`). -//! -//! Tiles are first recorded, then the encoder counts how often each table entry coded a 0 or a -//! 1, picks updates that pay for themselves, writes them in the compressed header and finally -//! entropy codes the recorded symbols with the updated probabilities. - -use crate::bool::BoolEncoder; -use crate::tables::{ - DEFAULT_COEF_PROBS, DEFAULT_INTER_MODE_PROBS, DEFAULT_IS_INTER_PROB, DEFAULT_MV_BITS_PROB, DEFAULT_MV_CLASS_PROBS, DEFAULT_MV_CLASS0_BIT_PROB, - DEFAULT_MV_CLASS0_FR_PROBS, DEFAULT_MV_FR_PROBS, DEFAULT_MV_JOINT_PROBS, DEFAULT_MV_SIGN_PROB, DEFAULT_PARTITION_PROBS, DEFAULT_SINGLE_REF_PROB, - DEFAULT_SKIP_PROB, PARETO_TABLE, inv_map_table, -}; - -/// Coefficient probabilities `[ptype][ref][band][ctx][3]` for 4×4 transforms (band 0 uses -/// contexts 0..3 only). -pub const COEF: usize = 0; -pub const SKIP: usize = 432; -pub const INTER_MODE: usize = SKIP + 3; -pub const IS_INTER: usize = INTER_MODE + 21; -pub const SINGLE_REF: usize = IS_INTER + 4; -pub const PARTITION: usize = SINGLE_REF + 10; -pub const MV_JOINT: usize = PARTITION + 48; -pub const MV_SIGN: usize = MV_JOINT + 3; -pub const MV_CLASS: usize = MV_SIGN + 2; -pub const MV_CLASS0_BIT: usize = MV_CLASS + 20; -pub const MV_BITS: usize = MV_CLASS0_BIT + 2; -pub const MV_CLASS0_FR: usize = MV_BITS + 20; -pub const MV_FR: usize = MV_CLASS0_FR + 12; -pub const NPROBS: usize = MV_FR + 6; - -/// Index of the first of the three coefficient probabilities of a context. -pub fn coef_index(ptype: usize, inter: bool, band: usize, ctx: usize) -> usize { - COEF + (((ptype * 2 + inter as usize) * 6 + band) * 6 + ctx) * 3 -} - -#[derive(Clone)] -pub struct Probs(pub [u8; NPROBS]); - -impl Probs { - /// The default probabilities (setup_past_independence / key frames). - pub fn defaults() -> Probs { - let mut p = [128u8; NPROBS]; - p[COEF..COEF + 432].copy_from_slice(&DEFAULT_COEF_PROBS); - p[SKIP..SKIP + 3].copy_from_slice(&DEFAULT_SKIP_PROB); - p[INTER_MODE..INTER_MODE + 21].copy_from_slice(&DEFAULT_INTER_MODE_PROBS); - p[IS_INTER..IS_INTER + 4].copy_from_slice(&DEFAULT_IS_INTER_PROB); - p[SINGLE_REF..SINGLE_REF + 10].copy_from_slice(&DEFAULT_SINGLE_REF_PROB); - p[PARTITION..PARTITION + 48].copy_from_slice(&DEFAULT_PARTITION_PROBS); - p[MV_JOINT..MV_JOINT + 3].copy_from_slice(&DEFAULT_MV_JOINT_PROBS); - p[MV_SIGN..MV_SIGN + 2].copy_from_slice(&DEFAULT_MV_SIGN_PROB); - p[MV_CLASS..MV_CLASS + 20].copy_from_slice(&DEFAULT_MV_CLASS_PROBS); - p[MV_CLASS0_BIT..MV_CLASS0_BIT + 2].copy_from_slice(&DEFAULT_MV_CLASS0_BIT_PROB); - p[MV_BITS..MV_BITS + 20].copy_from_slice(&DEFAULT_MV_BITS_PROB); - p[MV_CLASS0_FR..MV_CLASS0_FR + 12].copy_from_slice(&DEFAULT_MV_CLASS0_FR_PROBS); - p[MV_FR..MV_FR + 6].copy_from_slice(&DEFAULT_MV_FR_PROBS); - Probs(p) - } -} - -/// Probabilities of the full token tree (9.3.2) for model probability `p`. -pub fn pareto(p: u8) -> [u8; 8] { - let p = p.max(1) as usize; - let x = (p - 1) / 2; - let mut t = [0u8; 8]; - for n in 0..8 { - t[n] = if p & 1 == 1 { PARETO_TABLE[x * 8 + n] } else { ((PARETO_TABLE[x * 8 + n] as u32 + PARETO_TABLE[(x + 1) * 8 + n] as u32) >> 1) as u8 }; - } - t -} - -/// The probability a recorded symbol is coded with. -#[derive(Clone, Copy, Debug)] -pub enum P { - Fixed(u8), - /// An entry of the frame's [`Probs`]. - Ctx(u16), - /// Node `n` of the Pareto token tree derived from the coefficient probability at the index. - Pareto(u16, u8), -} - -#[derive(Clone, Copy)] -struct Sym { - bit: bool, - p: P, -} - -/// Records the boolean decisions of a tile. -#[derive(Default)] -pub struct Writer { - syms: Vec, -} - -impl Writer { - #[inline] - pub fn put(&mut self, bit: bool, p: P) { - self.syms.push(Sym { bit, p }); - } - #[inline] - pub fn fixed(&mut self, bit: bool, prob: u8) { - self.put(bit, P::Fixed(prob)); - } - #[inline] - pub fn ctx(&mut self, bit: bool, idx: usize) { - self.put(bit, P::Ctx(idx as u16)); - } - /// Write `value` with `tree` (leaves stored negated); node `i` uses `prob(i)`. - pub fn tree(&mut self, tree: &[i8], value: u8, prob: impl Fn(usize) -> P) { - for (node, bit) in tree_path(tree, value) { - self.put(bit, prob(node)); - } - } - - /// Add the 0/1 counts of every table entry this tile used. - pub fn count(&self, counts: &mut [[u32; 2]]) { - for s in &self.syms { - if let P::Ctx(i) = s.p { - counts[i as usize][s.bit as usize] += 1; - } - } - } - - /// Entropy code the recorded symbols. - pub fn emit(&self, probs: &Probs) -> Vec { - let mut bc = BoolEncoder::new(); - bc.write(false, 128); // marker bit - for s in &self.syms { - let p = match s.p { - P::Fixed(p) => p, - P::Ctx(i) => probs.0[i as usize], - P::Pareto(i, n) => pareto(probs.0[i as usize])[n as usize], - }; - bc.write(s.bit, p); - } - bc.finish() - } -} - -/// The (node, bit) path to `value` in a tree (node = tree index / 2). -pub fn tree_path(tree: &[i8], value: u8) -> impl Iterator { - struct Path { - steps: [(u8, bool); 16], - len: usize, - } - fn walk(tree: &[i8], node: usize, value: u8, out: &mut Path) -> bool { - for b in 0..2 { - let t = tree[node + b]; - out.steps[out.len] = ((node >> 1) as u8, b == 1); - out.len += 1; - if t <= 0 { - if (-t) as u8 == value { - return true; - } - } else if walk(tree, t as usize, value, out) { - return true; - } - out.len -= 1; - } - false - } - let mut p = Path { steps: [(0, false); 16], len: 0 }; - walk(tree, 0, value, &mut p); - let Path { steps, len } = p; - steps.into_iter().take(len).map(|(n, b)| (n as usize, b)) -} - -// ---------------------------------------------------------------- costs - -/// Cost in 1/256 bit of coding a 0 (`[p][0]`) or a 1 (`[p][1]`) with probability `p`. -pub fn cost_table() -> &'static [[u32; 2]; 256] { - use std::sync::OnceLock; - static T: OnceLock<[[u32; 2]; 256]> = OnceLock::new(); - T.get_or_init(|| { - let mut t = [[0u32; 2]; 256]; - for (p, e) in t.iter_mut().enumerate() { - let q = (p.max(1) as f64) / 256.0; - e[0] = (-q.log2() * 256.0).round() as u32; - e[1] = (-(1.0 - q).log2() * 256.0).round() as u32; - } - t - }) -} - -#[inline] -pub fn cost(bit: bool, p: u8) -> u32 { - cost_table()[p as usize][bit as usize] -} - -/// Cost of `value` in a tree with probabilities `probs` (node i → probs[i]). -pub fn tree_cost(tree: &[i8], probs: &[u8], value: u8) -> u32 { - tree_path(tree, value).map(|(n, b)| cost(b, probs[n])).sum() -} - -// ---------------------------------------------------------------- forward updates - -fn inv_recenter_nonneg(v: i32, m: i32) -> i32 { - if v > 2 * m { - v - } else if v & 1 != 0 { - m - ((v + 1) >> 1) - } else { - m + (v >> 1) - } -} - -/// `inv_remap_prob` (6.3.5). -fn inv_remap_prob(delta: usize, prob: u8) -> u8 { - let v = inv_map_table()[delta.min(254)] as i32; - let m = prob as i32 - 1; - let r = if (m << 1) <= 255 { 1 + inv_recenter_nonneg(v, m) } else { 255 - inv_recenter_nonneg(v, 255 - 1 - m) }; - r.clamp(1, 255) as u8 -} - -/// The `decode_term_subexp` value that moves `old` to `new`. -fn delta_for(old: u8, new: u8) -> Option { - match delta_table().get(old as usize * 256 + new as usize) { - Some(&d) if d != NO_DELTA => Some(d as usize), - _ => None, - } -} - -/// Marks "no delta reaches this probability" in [`delta_table`]. -const NO_DELTA: u8 = 255; - -/// `delta_for` for every (old, new) pair, indexed `old * 256 + new`: the smallest delta that -/// moves `old` to `new`. Filled from the largest delta down so the smallest one wins. -fn delta_table() -> &'static [u8] { - use std::sync::OnceLock; - static T: OnceLock> = OnceLock::new(); - T.get_or_init(|| { - let mut t = vec![NO_DELTA; 256 * 256]; - for old in 0..=255u8 { - for d in (0..255usize).rev() { - let new = inv_remap_prob(d, old); - if let Some(e) = t.get_mut(old as usize * 256 + new as usize) { - *e = d as u8; - } - } - } - t - }) -} - -/// Bits of `decode_term_subexp` for `d`. -fn subexp_bits(d: usize) -> u32 { - match d { - 0..16 => 5, - 16..32 => 6, - 32..64 => 8, - 64..129 => 10, - _ => 11, - } -} - -fn write_subexp(bc: &mut BoolEncoder, d: usize) { - let d = d as u32; - if d < 16 { - bc.literal(0, 1); - bc.literal(d, 4); - } else if d < 32 { - bc.literal(2, 2); - bc.literal(d - 16, 4); - } else if d < 64 { - bc.literal(6, 3); - bc.literal(d - 32, 5); - } else { - bc.literal(7, 3); - if d < 129 { - bc.literal(d - 64, 7); - } else { - let v = (d + 1) >> 1; - bc.literal(v, 7); - bc.literal((d + 1) & 1, 1); - } - } -} - -fn counts_cost(c: [u32; 2], p: u8) -> u64 { - c[0] as u64 * cost(false, p) as u64 + c[1] as u64 * cost(true, p) as u64 -} - -/// Whether table entry `i` is an MV probability (7-bit `update_mv_prob` rather than -/// `diff_update_prob`). -fn is_mv(i: usize) -> bool { - i >= MV_JOINT -} - -/// Choose updated probabilities: an entry changes when the bits saved on this frame's symbols -/// exceed the cost of signalling the change. `inter`: the inter-frame tables are updatable. -pub fn plan_updates(old: &Probs, counts: &[[u32; 2]], inter: bool) -> Probs { - let mut new = old.clone(); - let flag = cost(true, 252) as i64 - cost(false, 252) as i64; - let limit = if inter { NPROBS } else { INTER_MODE }; - for i in 0..limit { - let c = counts[i]; - let n = c[0] + c[1]; - if n == 0 { - continue; - } - let o = old.0[i]; - let ideal = ((c[0] as f64 * 256.0 / n as f64).round() as i32).clamp(1, 255) as u8; - let cands: Vec = if is_mv(i) { vec![ideal | 1, ideal.saturating_sub(1).max(1) | 1] } else { vec![ideal] }; - let base = counts_cost(c, o) as i64; - let mut best = (0i64, o); - for p in cands { - if p == o { - continue; - } - let signal = if is_mv(i) { - 7 * 256 - } else { - match delta_for(o, p) { - Some(d) => subexp_bits(d) as i64 * 256, - None => continue, - } - }; - let gain = base - counts_cost(c, p) as i64 - signal - flag; - if gain > best.0 { - best = (gain, p); - } - } - new.0[i] = best.1; - } - new -} - -/// `diff_update_prob` for one entry. -fn diff_update(bc: &mut BoolEncoder, old: &Probs, new: &Probs, i: usize) { - let (o, n) = (old.0[i], new.0[i]); - match (o != n).then(|| delta_for(o, n)).flatten() { - Some(d) => { - bc.write(true, 252); - write_subexp(bc, d); - } - None => bc.write(false, 252), - } -} - -/// `update_mv_prob` for one entry. -fn mv_update(bc: &mut BoolEncoder, old: &Probs, new: &Probs, i: usize) { - let n = new.0[i]; - if n != old.0[i] && n & 1 == 1 { - bc.write(true, 252); - bc.literal(n as u32 >> 1, 7); - } else { - bc.write(false, 252); - } -} - -/// The compressed header (6.3) of a frame coded with ONLY_4X4 transforms, single LAST -/// references and a fixed interpolation filter, moving the probabilities from `old` to `new`. -/// Entries `new` cannot signal are left at `old` (and must be coded with `old`): the caller -/// codes the tiles with the probabilities this returns. -pub fn compressed_header(old: &Probs, new: &Probs, lossless: bool, inter: bool) -> (Vec, Probs) { - let mut bc = BoolEncoder::new(); - bc.write(false, 128); - if !lossless { - bc.literal(0, 2); // tx_mode = ONLY_4X4 - } - // read_coef_probs: TX_4X4 only. - let coef_changed = (COEF..COEF + 432).any(|i| new.0[i] != old.0[i]); - bc.literal(coef_changed as u32, 1); - if coef_changed { - for i in 0..2 { - for j in 0..2 { - for k in 0..6 { - for l in 0..if k == 0 { 3 } else { 6 } { - for m in 0..3 { - diff_update(&mut bc, old, new, coef_index(i, j == 1, k, l) + m); - } - } - } - } - } - } - for i in 0..3 { - diff_update(&mut bc, old, new, SKIP + i); - } - if inter { - for i in 0..21 { - diff_update(&mut bc, old, new, INTER_MODE + i); - } - for i in 0..4 { - diff_update(&mut bc, old, new, IS_INTER + i); - } - // reference_mode is SINGLE_REFERENCE (no compound: equal sign biases). - for i in 0..10 { - diff_update(&mut bc, old, new, SINGLE_REF + i); - } - for _ in 0..36 { - bc.write(false, 252); // y mode probabilities (no intra blocks in inter frames) - } - for i in 0..48 { - diff_update(&mut bc, old, new, PARTITION + i); - } - for j in 0..3 { - mv_update(&mut bc, old, new, MV_JOINT + j); - } - for i in 0..2 { - mv_update(&mut bc, old, new, MV_SIGN + i); - for j in 0..10 { - mv_update(&mut bc, old, new, MV_CLASS + i * 10 + j); - } - mv_update(&mut bc, old, new, MV_CLASS0_BIT + i); - for j in 0..10 { - mv_update(&mut bc, old, new, MV_BITS + i * 10 + j); - } - } - for i in 0..2 { - for j in 0..6 { - mv_update(&mut bc, old, new, MV_CLASS0_FR + i * 6 + j); - } - for j in 0..3 { - mv_update(&mut bc, old, new, MV_FR + i * 3 + j); - } - } - } - // What the decoder ends up with. - let mut used = old.clone(); - for i in 0..NPROBS { - let ok = if is_mv(i) { new.0[i] & 1 == 1 } else { delta_for(old.0[i], new.0[i]).is_some() }; - if ok && (inter || i < INTER_MODE) { - used.0[i] = new.0[i]; - } - } - (bc.finish(), used) -} - -#[cfg(test)] -mod tests { - use super::*; - - #[test] - fn remap_reaches_every_probability() { - for old in 1..=255u8 { - for new in 1..=255u8 { - if new != old { - assert!(delta_for(old, new).is_some(), "{old} -> {new}"); - } - } - } - } - - #[test] - fn delta_table_matches_the_linear_search() { - for old in 0..=255u8 { - for new in 0..=255u8 { - let slow = (0..255).find(|&d| inv_remap_prob(d, old) == new); - assert_eq!(delta_for(old, new), slow, "{old} -> {new}"); - } - } - } - - #[test] - fn inv_map_table_shape() { - let t = inv_map_table(); - assert_eq!(&t[..4], &[7, 20, 33, 46]); - assert_eq!(t[19], 254); - assert_eq!(&t[20..27], &[1, 2, 3, 4, 5, 6, 8]); - assert_eq!(&t[252..], &[252, 253, 253]); - } -} diff --git a/crates/vp9enc/src/tables.rs b/crates/vp9enc/src/tables.rs deleted file mode 100644 index c70d7d8c2..000000000 --- a/crates/vp9enc/src/tables.rs +++ /dev/null @@ -1,201 +0,0 @@ -//! Tables from the VP9 Bitstream & Decoding Process Specification v0.6 (section 10), as -//! transcribed in FilmCraft's first-party decoder `filmcraft-vp9` (MIT OR Apache-2.0, the same -//! licence as this crate). - -pub const DC_QLOOKUP: [i32; 256] = [ - 4, 8, 8, 9, 10, 11, 12, 12, 13, 14, 15, 16, 17, 18, 19, 19, 20, 21, 22, 23, 24, 25, 26, 26, 27, 28, 29, 30, 31, 32, 32, 33, 34, 35, 36, 37, 38, 38, 39, 40, - 41, 42, 43, 43, 44, 45, 46, 47, 48, 48, 49, 50, 51, 52, 53, 53, 54, 55, 56, 57, 57, 58, 59, 60, 61, 62, 62, 63, 64, 65, 66, 66, 67, 68, 69, 70, 70, 71, 72, - 73, 74, 74, 75, 76, 77, 78, 78, 79, 80, 81, 81, 82, 83, 84, 85, 85, 87, 88, 90, 92, 93, 95, 96, 98, 99, 101, 102, 104, 105, 107, 108, 110, 111, 113, 114, - 116, 117, 118, 120, 121, 123, 125, 127, 129, 131, 134, 136, 138, 140, 142, 144, 146, 148, 150, 152, 154, 156, 158, 161, 164, 166, 169, 172, 174, 177, 180, - 182, 185, 187, 190, 192, 195, 199, 202, 205, 208, 211, 214, 217, 220, 223, 226, 230, 233, 237, 240, 243, 247, 250, 253, 257, 261, 265, 269, 272, 276, 280, - 284, 288, 292, 296, 300, 304, 309, 313, 317, 322, 326, 330, 335, 340, 344, 349, 354, 359, 364, 369, 374, 379, 384, 389, 395, 400, 406, 411, 417, 423, 429, - 435, 441, 447, 454, 461, 467, 475, 482, 489, 497, 505, 513, 522, 530, 539, 549, 559, 569, 579, 590, 602, 614, 626, 640, 654, 668, 684, 700, 717, 736, 755, - 775, 796, 819, 843, 869, 896, 925, 955, 988, 1022, 1058, 1098, 1139, 1184, 1232, 1282, 1336, -]; - -pub const AC_QLOOKUP: [i32; 256] = [ - 4, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23, 24, 25, 26, 27, 28, 29, 30, 31, 32, 33, 34, 35, 36, 37, 38, 39, 40, 41, 42, 43, 44, 45, - 46, 47, 48, 49, 50, 51, 52, 53, 54, 55, 56, 57, 58, 59, 60, 61, 62, 63, 64, 65, 66, 67, 68, 69, 70, 71, 72, 73, 74, 75, 76, 77, 78, 79, 80, 81, 82, 83, 84, - 85, 86, 87, 88, 89, 90, 91, 92, 93, 94, 95, 96, 97, 98, 99, 100, 101, 102, 104, 106, 108, 110, 112, 114, 116, 118, 120, 122, 124, 126, 128, 130, 132, 134, - 136, 138, 140, 142, 144, 146, 148, 150, 152, 155, 158, 161, 164, 167, 170, 173, 176, 179, 182, 185, 188, 191, 194, 197, 200, 203, 207, 211, 215, 219, 223, - 227, 231, 235, 239, 243, 247, 251, 255, 260, 265, 270, 275, 280, 285, 290, 295, 300, 305, 311, 317, 323, 329, 335, 341, 347, 353, 359, 366, 373, 380, 387, - 394, 401, 408, 416, 424, 432, 440, 448, 456, 465, 474, 483, 492, 501, 510, 520, 530, 540, 550, 560, 571, 582, 593, 604, 615, 627, 639, 651, 663, 676, 689, - 702, 715, 729, 743, 757, 771, 786, 801, 816, 832, 848, 864, 881, 898, 915, 933, 951, 969, 988, 1007, 1026, 1046, 1066, 1087, 1108, 1129, 1151, 1173, 1196, - 1219, 1243, 1267, 1292, 1317, 1343, 1369, 1396, 1423, 1451, 1479, 1508, 1537, 1567, 1597, 1628, 1660, 1692, 1725, 1759, 1793, 1828, -]; - -pub const DEFAULT_SCAN_4X4: [u16; 16] = [0, 4, 1, 5, 8, 2, 12, 9, 3, 6, 13, 10, 7, 14, 11, 15]; - -pub const COL_SCAN_4X4: [u16; 16] = [0, 4, 8, 1, 12, 5, 9, 2, 13, 6, 10, 3, 7, 14, 11, 15]; - -pub const ROW_SCAN_4X4: [u16; 16] = [0, 1, 4, 2, 5, 3, 6, 8, 9, 7, 12, 10, 13, 11, 14, 15]; - -pub const COEFBAND_4X4: [u8; 16] = [0, 1, 1, 2, 2, 2, 3, 3, 3, 3, 4, 4, 4, 5, 5, 5]; - -pub const ENERGY_CLASS: [u8; 12] = [0, 1, 2, 3, 3, 4, 4, 5, 5, 5, 5, 5]; - -pub const PARETO_TABLE: [u8; 1024] = [ - 3, 86, 128, 6, 86, 23, 88, 29, 9, 86, 129, 17, 88, 61, 94, 76, 15, 87, 129, 28, 89, 93, 100, 110, 20, 88, 130, 38, 91, 118, 106, 136, 26, 89, 131, 48, 92, - 139, 111, 156, 31, 90, 131, 58, 94, 156, 117, 171, 37, 90, 132, 66, 95, 171, 122, 184, 42, 91, 132, 75, 97, 183, 127, 194, 47, 92, 133, 83, 98, 193, 132, - 202, 52, 93, 133, 90, 100, 201, 137, 208, 57, 94, 134, 98, 101, 208, 142, 214, 62, 94, 135, 105, 103, 214, 146, 218, 66, 95, 135, 111, 104, 219, 151, 222, - 71, 96, 136, 117, 106, 224, 155, 225, 76, 97, 136, 123, 107, 227, 159, 228, 80, 98, 137, 129, 109, 231, 162, 231, 84, 98, 138, 134, 110, 234, 166, 233, 89, - 99, 138, 140, 112, 236, 170, 235, 93, 100, 139, 145, 113, 238, 173, 236, 97, 101, 140, 149, 115, 240, 176, 238, 101, 102, 140, 154, 116, 242, 179, 239, - 105, 103, 141, 158, 118, 243, 182, 240, 109, 104, 141, 162, 119, 244, 185, 241, 113, 104, 142, 166, 120, 245, 187, 242, 116, 105, 143, 170, 122, 246, 190, - 243, 120, 106, 143, 173, 123, 247, 192, 244, 123, 107, 144, 177, 125, 248, 195, 244, 127, 108, 145, 180, 126, 249, 197, 245, 130, 109, 145, 183, 128, 249, - 199, 245, 134, 110, 146, 186, 129, 250, 201, 246, 137, 111, 147, 189, 131, 251, 203, 246, 140, 112, 147, 192, 132, 251, 205, 247, 143, 113, 148, 194, 133, - 251, 207, 247, 146, 114, 149, 197, 135, 252, 208, 248, 149, 115, 149, 199, 136, 252, 210, 248, 152, 115, 150, 201, 138, 252, 211, 248, 155, 116, 151, 204, - 139, 253, 213, 249, 158, 117, 151, 206, 140, 253, 214, 249, 161, 118, 152, 208, 142, 253, 216, 249, 163, 119, 153, 210, 143, 253, 217, 249, 166, 120, 153, - 212, 144, 254, 218, 250, 168, 121, 154, 213, 146, 254, 220, 250, 171, 122, 155, 215, 147, 254, 221, 250, 173, 123, 155, 217, 148, 254, 222, 250, 176, 124, - 156, 218, 150, 254, 223, 250, 178, 125, 157, 220, 151, 254, 224, 251, 180, 126, 157, 221, 152, 254, 225, 251, 183, 127, 158, 222, 153, 254, 226, 251, 185, - 128, 159, 224, 155, 255, 227, 251, 187, 129, 160, 225, 156, 255, 228, 251, 189, 131, 160, 226, 157, 255, 228, 251, 191, 132, 161, 227, 159, 255, 229, 251, - 193, 133, 162, 228, 160, 255, 230, 252, 195, 134, 163, 230, 161, 255, 231, 252, 197, 135, 163, 231, 162, 255, 231, 252, 199, 136, 164, 232, 163, 255, 232, - 252, 201, 137, 165, 233, 165, 255, 233, 252, 202, 138, 166, 233, 166, 255, 233, 252, 204, 139, 166, 234, 167, 255, 234, 252, 206, 140, 167, 235, 168, 255, - 235, 252, 207, 141, 168, 236, 169, 255, 235, 252, 209, 142, 169, 237, 171, 255, 236, 252, 210, 144, 169, 237, 172, 255, 236, 252, 212, 145, 170, 238, 173, - 255, 237, 252, 214, 146, 171, 239, 174, 255, 237, 253, 215, 147, 172, 240, 175, 255, 238, 253, 216, 148, 173, 240, 176, 255, 238, 253, 218, 149, 173, 241, - 177, 255, 239, 253, 219, 150, 174, 241, 179, 255, 239, 253, 220, 152, 175, 242, 180, 255, 240, 253, 222, 153, 176, 242, 181, 255, 240, 253, 223, 154, 177, - 243, 182, 255, 240, 253, 224, 155, 178, 244, 183, 255, 241, 253, 225, 156, 178, 244, 184, 255, 241, 253, 226, 158, 179, 244, 185, 255, 242, 253, 228, 159, - 180, 245, 186, 255, 242, 253, 229, 160, 181, 245, 187, 255, 242, 253, 230, 161, 182, 246, 188, 255, 243, 253, 231, 163, 183, 246, 189, 255, 243, 253, 232, - 164, 184, 247, 190, 255, 243, 253, 233, 165, 185, 247, 191, 255, 244, 253, 234, 166, 185, 247, 192, 255, 244, 253, 235, 168, 186, 248, 193, 255, 244, 253, - 236, 169, 187, 248, 194, 255, 244, 253, 236, 170, 188, 248, 195, 255, 245, 253, 237, 171, 189, 249, 196, 255, 245, 254, 238, 173, 190, 249, 197, 255, 245, - 254, 239, 174, 191, 249, 198, 255, 245, 254, 240, 175, 192, 249, 199, 255, 246, 254, 240, 177, 193, 250, 200, 255, 246, 254, 241, 178, 194, 250, 201, 255, - 246, 254, 242, 179, 195, 250, 202, 255, 246, 254, 242, 181, 196, 250, 203, 255, 247, 254, 243, 182, 197, 251, 204, 255, 247, 254, 244, 184, 198, 251, 205, - 255, 247, 254, 244, 185, 199, 251, 206, 255, 247, 254, 245, 186, 200, 251, 207, 255, 247, 254, 246, 188, 201, 252, 207, 255, 248, 254, 246, 189, 202, 252, - 208, 255, 248, 254, 247, 191, 203, 252, 209, 255, 248, 254, 247, 192, 204, 252, 210, 255, 248, 254, 248, 194, 205, 252, 211, 255, 248, 254, 248, 195, 206, - 252, 212, 255, 249, 254, 249, 197, 207, 253, 213, 255, 249, 254, 249, 198, 208, 253, 214, 255, 249, 254, 250, 200, 210, 253, 215, 255, 249, 254, 250, 201, - 211, 253, 215, 255, 249, 254, 250, 203, 212, 253, 216, 255, 249, 254, 251, 204, 213, 253, 217, 255, 250, 254, 251, 206, 214, 254, 218, 255, 250, 254, 252, - 207, 216, 254, 219, 255, 250, 254, 252, 209, 217, 254, 220, 255, 250, 254, 252, 211, 218, 254, 221, 255, 250, 254, 253, 213, 219, 254, 222, 255, 250, 254, - 253, 214, 221, 254, 223, 255, 250, 254, 253, 216, 222, 254, 224, 255, 251, 254, 253, 218, 224, 254, 225, 255, 251, 254, 254, 220, 225, 254, 225, 255, 251, - 254, 254, 222, 227, 255, 226, 255, 251, 254, 254, 224, 228, 255, 227, 255, 251, 254, 254, 226, 230, 255, 228, 255, 251, 254, 255, 228, 231, 255, 230, 255, - 251, 254, 255, 230, 233, 255, 231, 255, 252, 254, 255, 232, 235, 255, 232, 255, 252, 254, 255, 235, 237, 255, 233, 255, 252, 254, 255, 238, 240, 255, 235, - 255, 252, 255, 255, 241, 243, 255, 236, 255, 252, 254, 255, 246, 247, 255, 239, 255, 253, 255, -]; - -pub const KF_PARTITION_PROBS: [u8; 48] = [ - 158, 97, 94, 93, 24, 99, 85, 119, 44, 62, 59, 67, 149, 53, 53, 94, 20, 48, 83, 53, 24, 52, 18, 18, 150, 40, 39, 78, 12, 26, 67, 33, 11, 24, 7, 5, 174, 35, - 49, 68, 11, 27, 57, 15, 9, 12, 3, 3, -]; - -pub const KF_Y_MODE_PROBS: [u8; 900] = [ - 137, 30, 42, 148, 151, 207, 70, 52, 91, 92, 45, 102, 136, 116, 180, 74, 90, 100, 73, 32, 19, 187, 222, 215, 46, 34, 100, 91, 30, 32, 116, 121, 186, 93, 86, - 94, 72, 35, 36, 149, 68, 206, 68, 63, 105, 73, 31, 28, 138, 57, 124, 55, 122, 151, 67, 23, 21, 140, 126, 197, 40, 37, 171, 86, 27, 28, 128, 154, 212, 45, - 43, 53, 74, 32, 27, 107, 86, 160, 63, 134, 102, 59, 67, 44, 140, 161, 202, 78, 67, 119, 63, 36, 126, 146, 123, 158, 60, 90, 96, 43, 46, 168, 134, 107, 128, - 69, 142, 92, 44, 29, 68, 159, 201, 177, 50, 57, 77, 58, 38, 76, 114, 97, 172, 78, 133, 92, 46, 41, 76, 140, 63, 184, 69, 112, 57, 38, 32, 85, 140, 46, 112, - 54, 151, 133, 39, 27, 61, 131, 110, 175, 44, 75, 136, 52, 30, 74, 113, 130, 175, 51, 64, 58, 47, 35, 80, 100, 74, 143, 64, 163, 74, 36, 61, 116, 114, 128, - 162, 80, 125, 82, 82, 26, 26, 171, 208, 204, 44, 32, 105, 55, 44, 68, 166, 179, 192, 57, 57, 108, 42, 26, 11, 199, 241, 228, 23, 15, 85, 68, 42, 19, 131, - 160, 199, 55, 52, 83, 58, 50, 25, 139, 115, 232, 39, 52, 118, 50, 35, 33, 153, 104, 162, 64, 59, 131, 44, 24, 16, 150, 177, 202, 33, 19, 156, 55, 27, 12, - 153, 203, 218, 26, 27, 49, 53, 49, 21, 110, 116, 168, 59, 80, 76, 38, 72, 19, 168, 203, 212, 50, 50, 107, 103, 26, 36, 129, 132, 201, 83, 80, 93, 59, 38, - 83, 112, 103, 162, 98, 136, 90, 62, 30, 23, 158, 200, 207, 59, 57, 50, 67, 30, 29, 84, 86, 191, 102, 91, 59, 60, 32, 33, 112, 71, 220, 64, 89, 104, 53, 26, - 34, 130, 56, 149, 84, 120, 103, 53, 21, 23, 133, 109, 210, 56, 77, 172, 77, 19, 29, 112, 142, 228, 55, 66, 36, 61, 29, 29, 93, 97, 165, 83, 175, 162, 47, - 47, 43, 114, 137, 181, 100, 99, 95, 69, 23, 29, 128, 83, 199, 46, 44, 101, 53, 40, 55, 139, 69, 183, 61, 80, 110, 40, 29, 19, 161, 180, 207, 43, 24, 91, - 60, 34, 19, 105, 61, 198, 53, 64, 89, 52, 31, 22, 158, 40, 209, 58, 62, 89, 44, 31, 29, 147, 46, 158, 56, 102, 198, 35, 19, 12, 135, 87, 209, 41, 45, 167, - 55, 25, 21, 118, 95, 215, 38, 39, 66, 51, 38, 25, 113, 58, 164, 70, 93, 97, 47, 54, 34, 146, 108, 203, 72, 103, 151, 64, 19, 37, 156, 66, 138, 49, 95, 133, - 46, 27, 80, 150, 55, 124, 55, 121, 135, 36, 23, 27, 165, 149, 166, 54, 64, 118, 53, 21, 36, 131, 63, 163, 60, 109, 81, 40, 26, 35, 154, 40, 185, 51, 97, - 123, 35, 19, 34, 179, 19, 97, 48, 129, 124, 36, 20, 26, 136, 62, 164, 33, 77, 154, 45, 18, 32, 130, 90, 157, 40, 79, 91, 45, 26, 28, 129, 45, 129, 49, 147, - 123, 38, 44, 51, 136, 74, 162, 57, 97, 121, 75, 17, 22, 136, 138, 185, 32, 34, 166, 56, 39, 58, 133, 117, 173, 48, 53, 187, 35, 21, 12, 161, 212, 207, 20, - 23, 145, 56, 29, 19, 117, 109, 181, 55, 68, 112, 47, 29, 17, 153, 64, 220, 59, 51, 114, 46, 16, 24, 136, 76, 147, 41, 64, 172, 34, 17, 11, 108, 152, 187, - 13, 15, 209, 51, 24, 14, 115, 133, 209, 32, 26, 104, 55, 30, 18, 122, 79, 179, 44, 88, 116, 37, 49, 25, 129, 168, 164, 41, 54, 148, 82, 22, 32, 127, 143, - 213, 39, 41, 70, 62, 44, 61, 123, 105, 189, 48, 57, 64, 47, 25, 17, 175, 222, 220, 24, 30, 86, 68, 36, 17, 106, 102, 206, 59, 74, 74, 57, 39, 23, 151, 68, - 216, 55, 63, 58, 49, 30, 35, 141, 70, 168, 82, 40, 115, 51, 25, 15, 136, 129, 202, 38, 35, 139, 68, 26, 16, 111, 141, 215, 29, 28, 28, 59, 39, 19, 114, 75, - 180, 77, 104, 42, 40, 61, 26, 126, 152, 206, 61, 59, 93, 78, 23, 39, 111, 117, 170, 74, 124, 94, 48, 34, 86, 101, 92, 146, 78, 179, 134, 47, 22, 24, 138, - 187, 178, 68, 69, 59, 56, 25, 33, 105, 112, 187, 95, 177, 129, 48, 31, 27, 114, 63, 183, 82, 116, 56, 43, 28, 37, 121, 63, 123, 61, 192, 169, 42, 17, 24, - 109, 97, 177, 56, 76, 122, 58, 18, 28, 105, 139, 182, 70, 92, 63, 46, 23, 32, 74, 86, 150, 67, 183, 88, 36, 38, 48, 92, 122, 165, 88, 137, 91, 65, 70, 60, - 155, 159, 199, 61, 60, 81, 44, 78, 115, 132, 119, 173, 71, 112, 93, 39, 38, 21, 184, 227, 206, 42, 32, 64, 58, 47, 36, 124, 137, 193, 80, 82, 78, 49, 50, - 35, 144, 95, 205, 63, 78, 59, 41, 53, 52, 148, 71, 142, 65, 128, 51, 40, 36, 28, 143, 143, 202, 40, 55, 137, 52, 34, 29, 129, 183, 227, 42, 35, 43, 42, 44, - 44, 104, 105, 164, 64, 130, 80, 43, 81, 53, 140, 169, 204, 68, 84, 72, -]; - -pub const KF_UV_MODE_PROBS: [u8; 90] = [ - 144, 11, 54, 157, 195, 130, 46, 58, 108, 118, 15, 123, 148, 131, 101, 44, 93, 131, 113, 12, 23, 188, 226, 142, 26, 32, 125, 120, 11, 50, 123, 163, 135, 64, - 77, 103, 113, 9, 36, 155, 111, 157, 32, 44, 161, 116, 9, 55, 176, 76, 96, 37, 61, 149, 115, 9, 28, 141, 161, 167, 21, 25, 193, 120, 12, 32, 145, 195, 142, - 32, 38, 86, 116, 12, 64, 120, 140, 125, 49, 115, 121, 102, 19, 66, 162, 182, 122, 35, 59, 128, -]; - -pub const DEFAULT_SKIP_PROB: [u8; 3] = [192, 128, 64]; - -pub const DEFAULT_COEF_PROBS: [u8; 432] = [ - 195, 29, 183, 84, 49, 136, 8, 42, 71, 0, 0, 0, 0, 0, 0, 0, 0, 0, 31, 107, 169, 35, 99, 159, 17, 82, 140, 8, 66, 114, 2, 44, 76, 1, 19, 32, 40, 132, 201, - 29, 114, 187, 13, 91, 157, 7, 75, 127, 3, 58, 95, 1, 28, 47, 69, 142, 221, 42, 122, 201, 15, 91, 159, 6, 67, 121, 1, 42, 77, 1, 17, 31, 102, 148, 228, 67, - 117, 204, 17, 82, 154, 6, 59, 114, 2, 39, 75, 1, 15, 29, 156, 57, 233, 119, 57, 212, 58, 48, 163, 29, 40, 124, 12, 30, 81, 3, 12, 31, 191, 107, 226, 124, - 117, 204, 25, 99, 155, 0, 0, 0, 0, 0, 0, 0, 0, 0, 29, 148, 210, 37, 126, 194, 8, 93, 157, 2, 68, 118, 1, 39, 69, 1, 17, 33, 41, 151, 213, 27, 123, 193, 3, - 82, 144, 1, 58, 105, 1, 32, 60, 1, 13, 26, 59, 159, 220, 23, 126, 198, 4, 88, 151, 1, 66, 114, 1, 38, 71, 1, 18, 34, 114, 136, 232, 51, 114, 207, 11, 83, - 155, 3, 56, 105, 1, 33, 65, 1, 17, 34, 149, 65, 234, 121, 57, 215, 61, 49, 166, 28, 36, 114, 12, 25, 76, 3, 16, 42, 214, 49, 220, 132, 63, 188, 42, 65, - 137, 0, 0, 0, 0, 0, 0, 0, 0, 0, 85, 137, 221, 104, 131, 216, 49, 111, 192, 21, 87, 155, 2, 49, 87, 1, 16, 28, 89, 163, 230, 90, 137, 220, 29, 100, 183, 10, - 70, 135, 2, 42, 81, 1, 17, 33, 108, 167, 237, 55, 133, 222, 15, 97, 179, 4, 72, 135, 1, 45, 85, 1, 19, 38, 124, 146, 240, 66, 124, 224, 17, 88, 175, 4, 58, - 122, 1, 36, 75, 1, 18, 37, 141, 79, 241, 126, 70, 227, 66, 58, 182, 30, 44, 136, 12, 34, 96, 2, 20, 47, 229, 99, 249, 143, 111, 235, 46, 109, 192, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 82, 158, 236, 94, 146, 224, 25, 117, 191, 9, 87, 149, 3, 56, 99, 1, 33, 57, 83, 167, 237, 68, 145, 222, 10, 103, 177, 2, 72, 131, 1, 41, - 79, 1, 20, 39, 99, 167, 239, 47, 141, 224, 10, 104, 178, 2, 73, 133, 1, 44, 85, 1, 22, 47, 127, 145, 243, 71, 129, 228, 17, 93, 177, 3, 61, 124, 1, 41, 84, - 1, 21, 52, 157, 78, 244, 140, 72, 231, 69, 58, 184, 31, 44, 137, 14, 38, 105, 8, 23, 61, -]; - -// ---------------------------------------------------------------- inter frames (spec section 10.5) - -/// `inv_map_table` (used by `inv_remap_prob`): the 20 values 7 + 13k first, then every other value -/// 1..=253 in increasing order, then 253 again. -pub fn inv_map_table() -> &'static [u8; 255] { - use std::sync::OnceLock; - static T: OnceLock<[u8; 255]> = OnceLock::new(); - T.get_or_init(|| { - let mut t = [253u8; 255]; - let mut n = 0; - for k in 0..20 { - t[n] = 7 + 13 * k; - n += 1; - } - for v in 1..=253u8 { - if v < 7 || (v - 7) % 13 != 0 { - t[n] = v; - n += 1; - } - } - t - }) -} - -/// The regular 8-tap sub-sample interpolation filters (`subpel_filters[EIGHTTAP]`), 16 positions. -pub const SUBPEL_REGULAR: [[i32; 8]; 16] = [ - [0, 0, 0, 128, 0, 0, 0, 0], - [0, 1, -5, 126, 8, -3, 1, 0], - [-1, 3, -10, 122, 18, -6, 2, 0], - [-1, 4, -13, 118, 27, -9, 3, -1], - [-1, 4, -16, 112, 37, -11, 4, -1], - [-1, 5, -18, 105, 48, -14, 4, -1], - [-1, 5, -19, 97, 58, -16, 5, -1], - [-1, 6, -19, 88, 68, -18, 5, -1], - [-1, 6, -19, 78, 78, -19, 6, -1], - [-1, 5, -18, 68, 88, -19, 6, -1], - [-1, 5, -16, 58, 97, -19, 5, -1], - [-1, 4, -14, 48, 105, -18, 5, -1], - [-1, 4, -11, 37, 112, -16, 4, -1], - [-1, 3, -9, 27, 118, -13, 4, -1], - [0, 2, -6, 18, 122, -10, 3, -1], - [0, 1, -3, 8, 126, -5, 1, 0], -]; - -/// `mv_ref_blocks` (row, column offsets) for BLOCK_8X8 and BLOCK_16X16. -pub const MV_REF_8X8: [(i32, i32); 8] = [(-1, 0), (0, -1), (-1, -1), (-2, 0), (0, -2), (-2, -1), (-1, -2), (-2, -2)]; -pub const MV_REF_16X16: [(i32, i32); 8] = [(-1, 0), (0, -1), (-1, 1), (1, -1), (-1, -1), (-3, 0), (0, -3), (-3, -3)]; - -/// `mode_2_counter` indexed by y mode (intra modes 0..=9, then NEARESTMV, NEARMV, ZEROMV, NEWMV). -pub const MODE_2_COUNTER: [u8; 14] = [9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 0, 0, 3, 1]; - -/// `counter_to_context`; INVALID_CASE entries are 9. -pub const COUNTER_TO_CONTEXT: [u8; 19] = [2, 3, 4, 1, 3, 9, 0, 9, 9, 5, 5, 9, 5, 9, 9, 9, 9, 9, 6]; - -pub const DEFAULT_PARTITION_PROBS: [u8; 48] = [ - 199, 122, 141, 147, 63, 159, 148, 133, 118, 121, 104, 114, 174, 73, 87, 92, 41, 83, 82, 99, 50, 53, 39, 39, 177, 58, 59, 68, 26, 63, 52, 79, 25, 17, 14, - 12, 222, 34, 30, 72, 16, 44, 58, 32, 12, 10, 7, 6, -]; - -pub const DEFAULT_IS_INTER_PROB: [u8; 4] = [9, 102, 187, 225]; - -pub const DEFAULT_SINGLE_REF_PROB: [u8; 10] = [33, 16, 77, 74, 142, 142, 172, 170, 238, 247]; - -pub const DEFAULT_INTER_MODE_PROBS: [u8; 21] = [2, 173, 34, 7, 145, 85, 7, 166, 63, 7, 94, 66, 8, 64, 46, 17, 81, 31, 25, 29, 30]; - -pub const DEFAULT_MV_JOINT_PROBS: [u8; 3] = [32, 64, 96]; -pub const DEFAULT_MV_SIGN_PROB: [u8; 2] = [128, 128]; -pub const DEFAULT_MV_CLASS_PROBS: [u8; 20] = [224, 144, 192, 168, 192, 176, 192, 198, 198, 245, 216, 128, 176, 160, 176, 176, 192, 198, 198, 208]; -pub const DEFAULT_MV_CLASS0_BIT_PROB: [u8; 2] = [216, 208]; -pub const DEFAULT_MV_BITS_PROB: [u8; 20] = [136, 140, 148, 160, 176, 192, 224, 234, 234, 240, 136, 140, 148, 160, 176, 192, 224, 234, 234, 240]; -pub const DEFAULT_MV_CLASS0_FR_PROBS: [u8; 12] = [128, 128, 64, 96, 112, 64, 128, 128, 64, 96, 112, 64]; -pub const DEFAULT_MV_FR_PROBS: [u8; 6] = [64, 96, 64, 64, 96, 64]; diff --git a/crates/vp9enc/src/tile.rs b/crates/vp9enc/src/tile.rs deleted file mode 100644 index 082d0bd83..000000000 --- a/crates/vp9enc/src/tile.rs +++ /dev/null @@ -1,940 +0,0 @@ -//! Tile coding: partitions, intra blocks (key frames) and inter blocks (motion search, mode -//! decision, motion vector prediction, residual), recorded as symbols (see [`crate::probs`]). -//! -//! Mode info, motion vector candidate lists and contexts follow the decoding process of the -//! VP9 specification (6.4, 6.4.x `find_mv_refs` / `find_best_ref_mvs`) for the subset of the -//! syntax this encoder emits: 8×8 and 16×16 blocks, single LAST references, no -//! `UsePrevFrameMvs` (inter frames are error resilient), no high-precision vectors. - -use crate::mc::{Mv, PhasePlanes, RefPlane, clamp_for_plane, predict}; -use crate::probs::{self, P, Writer, cost, tree_cost}; -use crate::tables::{ - COEFBAND_4X4, COL_SCAN_4X4, COUNTER_TO_CONTEXT, DEFAULT_SCAN_4X4, ENERGY_CLASS, KF_PARTITION_PROBS, KF_UV_MODE_PROBS, KF_Y_MODE_PROBS, MODE_2_COUNTER, - MV_REF_8X8, MV_REF_16X16, ROW_SCAN_4X4, -}; -use crate::transform::{ADST_ADST, ADST_DCT, DCT_ADST, DCT_DCT, Quant}; -use crate::{BLOCK_8X8, BLOCK_16X16, Mi, Plane}; - -pub const DC_PRED: u8 = 0; -pub const V_PRED: u8 = 1; -pub const H_PRED: u8 = 2; -pub const TM_PRED: u8 = 9; -const MODES: [u8; 4] = [DC_PRED, V_PRED, H_PRED, TM_PRED]; -pub const NEARESTMV: u8 = 10; -pub const NEARMV: u8 = 11; -pub const ZEROMV: u8 = 12; -pub const NEWMV: u8 = 13; - -/// Which reconstructed neighbours are available for intra prediction. -#[derive(Clone, Copy)] -struct IntraEdges { - left: bool, - above: bool, -} - -const PARTITION_TREE: [i8; 6] = [0, 2, -1, 4, -2, -3]; -const INTRA_MODE_TREE: [i8; 18] = [0, 2, -9, 4, -1, 6, 8, 12, -2, 10, -4, -5, -3, 14, -8, 16, -6, -7]; -/// Leaves are `mode - NEARESTMV`. -const INTER_MODE_TREE: [i8; 6] = [-2, 2, 0, 4, -1, -3]; -const MV_JOINT_TREE: [i8; 6] = [0, 2, -1, 4, -2, -3]; -const MV_CLASS_TREE: [i8; 20] = [0, 2, -1, 4, 6, 8, -2, -3, 10, 12, -4, -5, -6, 14, 16, 18, -7, -8, -9, -10]; -const MV_FR_TREE: [i8; 6] = [0, 2, -1, 4, -2, -3]; - -/// extra_bits[token] = (cat, numExtra, base). -const EXTRA_BITS: [(u8, u8, u16); 11] = - [(0, 0, 0), (0, 0, 1), (0, 0, 2), (0, 0, 3), (0, 0, 4), (1, 1, 5), (2, 2, 7), (3, 3, 11), (4, 4, 19), (5, 5, 35), (6, 14, 67)]; -const CAT_PROBS: [&[u8]; 7] = [ - &[], - &[159], - &[165, 145], - &[173, 148, 140], - &[176, 155, 140, 135], - &[180, 157, 141, 134, 130], - &[254, 254, 254, 252, 249, 243, 230, 196, 177, 153, 140, 133, 130, 129], -]; - -/// mode2txfm_map for the intra modes used here (inter blocks use DCT_DCT). -fn mode_tx_type(mode: u8) -> u8 { - match mode { - V_PRED => ADST_DCT, - H_PRED => DCT_ADST, - TM_PRED => ADST_ADST, - _ => DCT_DCT, - } -} - -fn scan(tx_type: u8) -> &'static [u16; 16] { - match tx_type { - ADST_DCT => &ROW_SCAN_4X4, - DCT_ADST => &COL_SCAN_4X4, - _ => &DEFAULT_SCAN_4X4, - } -} - -/// Coefficient context neighbours (9.3.2) of each scan position. -fn neighbors(tx_type: u8) -> [(usize, usize); 16] { - let sc = scan(tx_type); - let mut nb = [(0, 0); 16]; - for (c, &pos) in sc.iter().enumerate().skip(1) { - let pos = pos as usize; - let (i, j) = (pos / 4, pos % 4); - nb[c] = if i > 0 && j > 0 { - let a = (i - 1) * 4 + j; - let b = i * 4 + j - 1; - match tx_type { - DCT_ADST => (a, a), - ADST_DCT => (b, b), - _ => (a, b), - } - } else if i > 0 { - ((i - 1) * 4 + j, (i - 1) * 4 + j) - } else { - (j - 1, j - 1) - }; - } - nb -} - -fn eob_of(levels: &[i32; 16], tx_type: u8) -> usize { - let sc = scan(tx_type); - (0..16).rev().find(|&i| levels[sc[i] as usize] != 0).map_or(0, |i| i + 1) -} - -/// Rough coefficient bit cost (1/256 bit) for rate-distortion decisions. -fn levels_cost(levels: &[i32; 16], eob: usize) -> u32 { - if eob == 0 { - return 0; - } - let mut bits = 256; - for &l in levels { - let a = l.unsigned_abs(); - bits += if a == 0 { 160 } else { 640 + 384 * (32 - a.leading_zeros()) }; - } - bits -} - -/// Intra prediction (8.5.1) of a square block for DC / V / H / TM. -fn predict_intra(p: &Plane, x: usize, y: usize, log2: usize, mode: u8, edges: IntraEdges) -> [i32; 64] { - let IntraEdges { left: have_left, above: have_above } = edges; - let size = 1usize << log2; - let base = 128i32; - let buf = &p.rec; - let w = p.w; - let mut above = [0i32; 8]; - let mut left = [0i32; 8]; - if have_above { - for i in 0..size { - above[i] = buf[(y - 1) * w + (x + i).min(p.max_x)] as i32; - } - } else { - above[..size].fill(base - 1); - } - let top_left = if have_above && have_left { - buf[(y - 1) * w + (x - 1).min(p.max_x)] as i32 - } else if have_above { - base + 1 - } else { - base - 1 - }; - if have_left { - for i in 0..size { - left[i] = buf[(y + i).min(p.max_y) * w + x - 1] as i32; - } - } else { - left[..size].fill(base + 1); - } - let mut out = [0i32; 64]; - for i in 0..size { - for j in 0..size { - out[i * size + j] = match mode { - V_PRED => above[j], - H_PRED => left[i], - TM_PRED => (above[j] + left[i] - top_left).clamp(0, 255), - _ => 0, - }; - } - } - if mode == DC_PRED { - let v = match (have_left, have_above) { - (true, true) => (left[..size].iter().sum::() + above[..size].iter().sum::() + size as i32) >> (log2 + 1), - (true, false) => (left[..size].iter().sum::() + (1 << (log2 - 1))) >> log2, - (false, true) => (above[..size].iter().sum::() + (1 << (log2 - 1))) >> log2, - (false, false) => base, - }; - out[..size * size].fill(v); - } - out -} - -// ---------------------------------------------------------------- motion vector coding - -/// `mv_class` of an offset (|component| - 1). -fn mv_class(offset: u32) -> u32 { - if offset < 16 { 0 } else { (31 - offset.leading_zeros()) - 3 } -} - -fn mv_component_cost(i: usize, v: i32) -> u32 { - let d = &probs_defaults().0; - let offset = v.unsigned_abs() - 1; - let c = mv_class(offset); - let mut bits = cost(v < 0, d[probs::MV_SIGN + i]); - bits += tree_cost(&MV_CLASS_TREE, &d[probs::MV_CLASS + i * 10..], c as u8); - let fr = ((offset >> 1) & 3) as u8; - if c == 0 { - let b = offset >> 3; - bits += cost(b == 1, d[probs::MV_CLASS0_BIT + i]); - bits += tree_cost(&MV_FR_TREE, &d[probs::MV_CLASS0_FR + i * 6 + b as usize * 3..], fr); - } else { - let dd = (offset - (1 << (c + 3))) >> 3; - for b in 0..c { - bits += cost((dd >> b) & 1 == 1, d[probs::MV_BITS + i * 10 + b as usize]); - } - bits += tree_cost(&MV_FR_TREE, &d[probs::MV_FR + i * 3..], fr); - } - bits -} - -const MV_TABLE: i32 = 8192; - -/// Component costs for |v| ≤ MV_TABLE (index v + MV_TABLE) and the four joint costs. -fn mv_tables() -> &'static ([Vec; 2], [u32; 4]) { - use std::sync::OnceLock; - static T: OnceLock<([Vec; 2], [u32; 4])> = OnceLock::new(); - T.get_or_init(|| { - let comp = std::array::from_fn(|i| (-MV_TABLE..=MV_TABLE).map(|v| if v == 0 { 0 } else { mv_component_cost(i, v) }).collect()); - let d = &probs_defaults().0; - let joints = std::array::from_fn(|j| tree_cost(&MV_JOINT_TREE, &d[probs::MV_JOINT..], j as u8)); - (comp, joints) - }) -} - -/// Bits (1/256) of a NEWMV difference with the default probabilities. -pub fn mv_cost(diff: Mv) -> u32 { - let (comp, joints) = mv_tables(); - let joint = (diff.row != 0) as usize * 2 + (diff.col != 0) as usize; - let c = |i: usize, v: i32| if v.abs() <= MV_TABLE { comp[i][(v + MV_TABLE) as usize] } else { mv_component_cost(i, v) }; - joints[joint] + if diff.row != 0 { c(0, diff.row) } else { 0 } + if diff.col != 0 { c(1, diff.col) } else { 0 } -} - -fn probs_defaults() -> &'static probs::Probs { - use std::sync::OnceLock; - static D: OnceLock = OnceLock::new(); - D.get_or_init(probs::Probs::defaults) -} - -/// SAD of two N×N blocks: per-column 16-bit sums (at most 16 · 255), summed once. -#[inline(always)] -fn sad_n(a: &[u8], sa: usize, b: &[u8], sb: usize) -> u32 { - let mut acc = [0u16; N]; - for i in 0..N { - let (Some(ra), Some(rb)) = (a.get(i * sa..).and_then(|r| r.first_chunk::()), b.get(i * sb..).and_then(|r| r.first_chunk::())) else { - return u32::MAX / 4; - }; - for j in 0..N { - acc[j] += ra[j].abs_diff(rb[j]) as u16; - } - } - acc.iter().map(|&v| v as u32).sum() -} - -// ---------------------------------------------------------------- the tile - -/// Per-frame coding parameters shared by the tiles. -pub struct FrameParams<'a> { - pub key: bool, - pub quant: Quant, - pub mi_cols: usize, - pub mi_rows: usize, - /// Reference planes (inter frames). - pub refs: Option<[RefPlane<'a>; 3]>, - /// The reference luma at every quarter-sample phase (motion search). - pub phases: Option, - /// Encoder-side motion hints from the previous frame (by MI position). - pub prev_mvs: Option<&'a [Mv]>, - /// Lagrangian multipliers: per bit, in SAD and in squared-error units. - pub lambda_sad: f64, - pub lambda_sse: f64, -} - -pub struct Tile<'a> { - pub f: &'a FrameParams<'a>, - pub w: Writer, - pub planes: &'a mut [Plane; 3], - pub mi: &'a mut [Mi], - pub col_start: usize, - pub col_end: usize, - above_nz: [Vec; 3], - left_nz: [Vec; 3], - above_part: Vec, - left_part: Vec, - /// Motion vectors of the four 8×8 blocks evaluated for the current 16×16 area. - pending: [(usize, usize, Mv); 4], -} - -impl<'a> Tile<'a> { - pub fn new(f: &'a FrameParams<'a>, planes: &'a mut [Plane; 3], mi: &'a mut [Mi], col_start: usize, col_end: usize) -> Tile<'a> { - let n = f.mi_cols * 2 + 16; - Tile { - f, - w: Writer::default(), - planes, - mi, - col_start, - col_end, - above_nz: [vec![0; n], vec![0; n], vec![0; n]], - left_nz: [vec![0; 16], vec![0; 16], vec![0; 16]], - above_part: vec![0; f.mi_cols + 8], - left_part: vec![0; 8], - pending: [(usize::MAX, 0, Mv::ZERO); 4], - } - } - - /// Code the tile's superblocks. - pub fn code(&mut self) { - for r in (0..self.f.mi_rows).step_by(8) { - for p in 0..3 { - self.left_nz[p].fill(0); - } - self.left_part.fill(0); - for c in (self.col_start..self.col_end).step_by(8) { - self.partition(r, c, 12); - } - } - } - - fn mi_at(&self, r: usize, c: usize) -> Mi { - self.mi[r * self.f.mi_cols + c] - } - - fn set_mi(&mut self, r: usize, c: usize, n8: usize, m: Mi) { - for dr in 0..n8 { - for dc in 0..n8 { - if r + dr < self.f.mi_rows && c + dc < self.f.mi_cols { - self.mi[(r + dr) * self.f.mi_cols + c + dc] = m; - } - } - } - } - - fn partition(&mut self, r: usize, c: usize, bsize: u8) { - let (mi_rows, mi_cols) = (self.f.mi_rows, self.f.mi_cols); - if r >= mi_rows || c >= mi_cols { - return; - } - let num8 = [1usize, 1, 1, 1, 1, 2, 2, 2, 4, 4, 4, 8, 8][bsize as usize]; - let half = num8 >> 1; - let has_rows = (r + half) < mi_rows; - let has_cols = (c + half) < mi_cols; - let bsl = [0usize, 0, 0, 0, 0, 1, 1, 1, 2, 2, 2, 3, 3][bsize as usize]; - let boffset = 3 - bsl; - let mut above = 0u8; - let mut left = 0u8; - for i in 0..num8 { - above |= self.above_part[c + i]; - left |= self.left_part[(r & 7) + i]; - } - let ctx = bsl * 4 + (((left >> boffset) & 1) as usize) * 2 + ((above >> boffset) & 1) as usize; - let key = self.f.key; - let prob = |i: usize| if key { P::Fixed(KF_PARTITION_PROBS[ctx * 3 + i]) } else { P::Ctx((probs::PARTITION + ctx * 3 + i) as u16) }; - if bsize == BLOCK_8X8 { - self.w.put(false, prob(0)); // PARTITION_NONE - if key { - self.intra_block(r, c); - } else { - let mv = self.pending.iter().find(|p| p.0 == r && p.1 == c).map(|p| p.2); - let mv = mv.unwrap_or_else(|| self.search(r, c, BLOCK_8X8).0); - self.inter_block(r, c, BLOCK_8X8, mv); - } - self.above_part[c] = 15 >> 1; - self.left_part[r & 7] = 15 >> 1; - return; - } - if !key && bsize == BLOCK_16X16 && has_rows && has_cols { - if let Some(mv) = self.decide16(r, c) { - self.w.put(false, prob(0)); // PARTITION_NONE - self.inter_block(r, c, BLOCK_16X16, mv); - for i in 0..2 { - self.above_part[c + i] = 15 >> 2; - self.left_part[(r & 7) + i] = 15 >> 2; - } - return; - } - } else if !key && bsize == BLOCK_16X16 { - self.pending = [(usize::MAX, 0, Mv::ZERO); 4]; - } - // PARTITION_SPLIT. - if has_rows && has_cols { - self.w.tree(&PARTITION_TREE, 3, prob); - } else if has_cols { - self.w.put(true, prob(1)); - } else if has_rows { - self.w.put(true, prob(2)); - } - let sub = bsize - 3; - self.partition(r, c, sub); - self.partition(r, c + half, sub); - self.partition(r + half, c, sub); - self.partition(r + half, c + half, sub); - } - - // ------------------------------------------------------------ intra (key frames) - - fn intra_block(&mut self, r: usize, c: usize) { - let avail_u = r > 0; - let avail_l = c > self.col_start; - let y_mode = self.choose_intra(0, c * 8, r * 8, 3, IntraEdges { left: avail_l, above: avail_u }); - let uv_mode = { - let mut best = (i64::MAX, DC_PRED); - for m in MODES { - let cost: i64 = (1..3).map(|p| self.intra_cost(p, c * 4, r * 4, 2, m, IntraEdges { left: avail_l, above: avail_u })).sum(); - if cost < best.0 { - best = (cost, m); - } - } - best.1 - }; - // skip = 0 (neighbours never skip in key frames: context 0); tx_size implied (4×4). - self.w.ctx(false, probs::SKIP); - let above_mode = if avail_u { self.mi_at(r - 1, c).mode } else { DC_PRED }; - let left_mode = if avail_l { self.mi_at(r, c - 1).mode } else { DC_PRED }; - let o = (above_mode as usize * 10 + left_mode as usize) * 9; - self.w.tree(&INTRA_MODE_TREE, y_mode, |i| P::Fixed(KF_Y_MODE_PROBS[o + i])); - let o = y_mode as usize * 9; - self.w.tree(&INTRA_MODE_TREE, uv_mode, |i| P::Fixed(KF_UV_MODE_PROBS[o + i])); - self.set_mi(r, c, 1, Mi { inter: false, mode: y_mode, mv: Mv::ZERO, skip: false, size: BLOCK_8X8 }); - // Residual: luma 2×2 transform blocks, then one 4×4 block per chroma plane. - for (dy, dx) in [(0, 0), (0, 1), (1, 0), (1, 1)] { - let tx = if self.f.quant.lossless { DCT_DCT } else { mode_tx_type(y_mode) }; - self.intra_tx(0, c * 8 + dx * 4, r * 8 + dy * 4, y_mode, tx, IntraEdges { left: avail_l || dx > 0, above: avail_u || dy > 0 }); - } - for p in 1..3 { - self.intra_tx(p, c * 4, r * 4, uv_mode, DCT_DCT, IntraEdges { left: avail_l, above: avail_u }); - } - } - - fn intra_cost(&self, plane: usize, x: usize, y: usize, log2: usize, mode: u8, edges: IntraEdges) -> i64 { - let p = &self.planes[plane]; - let pred = predict_intra(p, x, y, log2, mode, edges); - let size = 1 << log2; - let mut s = 0i64; - for i in 0..size { - for j in 0..size { - s += (p.src[(y + i) * p.w + x + j] as i32 - pred[i * size + j]).abs() as i64; - } - } - s - } - - fn choose_intra(&self, plane: usize, x: usize, y: usize, log2: usize, edges: IntraEdges) -> u8 { - let mut best = (i64::MAX, DC_PRED); - for m in MODES { - let cost = self.intra_cost(plane, x, y, log2, m, edges); - if cost < best.0 { - best = (cost, m); - } - } - best.1 - } - - fn intra_tx(&mut self, plane: usize, x: usize, y: usize, mode: u8, tx_type: u8, edges: IntraEdges) { - let ss = (plane > 0) as usize; - let max_x = (self.f.mi_cols * 8) >> ss; - let max_y = (self.f.mi_rows * 8) >> ss; - let (x4, y4) = (x >> 2, y >> 2); - let ly = y4 & (15 >> ss); - if x >= max_x || y >= max_y { - self.above_nz[plane][x4] = 0; - self.left_nz[plane][ly] = 0; - return; - } - let p = &self.planes[plane]; - let pred = predict_intra(p, x, y, 2, mode, edges); - let mut res = [0i32; 16]; - for i in 0..4 { - for j in 0..4 { - res[i * 4 + j] = p.src[(y + i) * p.w + x + j] as i32 - pred[i * 4 + j]; - } - } - let q = Quant { round_dc: 0.5, round_ac: 0.38, ..self.f.quant }; - let levels = q.levels(&res, tx_type); - let eob = eob_of(&levels, tx_type); - let ctx0 = (self.above_nz[plane][x4] + self.left_nz[plane][ly]) as usize; - self.tokens(plane, false, tx_type, &levels, eob, ctx0); - let resid = q.residual(&levels, tx_type, eob); - let p = &mut self.planes[plane]; - for i in 0..4 { - for j in 0..4 { - p.rec[(y + i) * p.w + x + j] = (pred[i * 4 + j] + resid[i * 4 + j]).clamp(0, 255) as u8; - } - } - let nz = (eob > 0) as u8; - self.above_nz[plane][x4] = nz; - self.left_nz[plane][ly] = nz; - } - - fn tokens(&mut self, plane: usize, inter: bool, tx_type: u8, levels: &[i32; 16], eob: usize, ctx0: usize) { - let sc = scan(tx_type); - let nb = neighbors(tx_type); - let ptype = (plane > 0) as usize; - let mut tc = [0u8; 16]; - let mut check_eob = true; - let mut c = 0; - while c < 16 { - let pos = sc[c] as usize; - let band = COEFBAND_4X4[c] as usize; - let ctx = if c == 0 { ctx0 } else { ((1 + tc[nb[c].0] + tc[nb[c].1]) >> 1) as usize }; - let o = probs::coef_index(ptype, inter, band, ctx); - if check_eob { - let more = c < eob; - self.w.ctx(more, o); - if !more { - break; - } - } - let v = levels[pos].unsigned_abs(); - if v == 0 { - self.w.ctx(false, o + 1); - tc[pos] = 0; - check_eob = false; - c += 1; - continue; - } - self.w.ctx(true, o + 1); - check_eob = true; - let token = match v { - 1 => 1, - 2 => 2, - 3 => 3, - 4 => 4, - 5..=6 => 5, - 7..=10 => 6, - 11..=18 => 7, - 19..=34 => 8, - 35..=66 => 9, - _ => 10, - }; - if token == 1 { - self.w.ctx(false, o + 2); - } else { - self.w.ctx(true, o + 2); - let path: &[(u8, bool)] = match token { - 2 => &[(0, false), (1, false)], - 3 => &[(0, false), (1, true), (2, false)], - 4 => &[(0, false), (1, true), (2, true)], - 5 => &[(0, true), (3, false), (4, false)], - 6 => &[(0, true), (3, false), (4, true)], - 7 => &[(0, true), (3, true), (5, false), (6, false)], - 8 => &[(0, true), (3, true), (5, false), (6, true)], - 9 => &[(0, true), (3, true), (5, true), (7, false)], - _ => &[(0, true), (3, true), (5, true), (7, true)], - }; - for &(n, b) in path { - self.w.put(b, P::Pareto((o + 2) as u16, n)); - } - let (cat, num_extra, base_v) = EXTRA_BITS[token]; - let extra = (v - base_v as u32).min((1 << num_extra) - 1); - let cp = CAT_PROBS[cat as usize]; - for e in 0..num_extra as usize { - self.w.fixed((extra >> (num_extra as usize - 1 - e)) & 1 != 0, cp[e]); - } - } - tc[pos] = ENERGY_CLASS[token]; - self.w.fixed(levels[pos] < 0, 128); - c += 1; - } - } - - // ------------------------------------------------------------ inter - - fn is_inside(&self, r: i32, c: i32) -> bool { - r >= 0 && (r as usize) < self.f.mi_rows && c >= self.col_start as i32 && (c as usize) < self.col_end - } - - /// `find_mv_refs` + `find_best_ref_mvs` for LAST_FRAME: (NearestMv, NearMv, mode context). - fn mv_refs(&self, r: usize, c: usize, bsize: u8) -> (Mv, Mv, usize) { - let search = if bsize == BLOCK_16X16 { &MV_REF_16X16 } else { &MV_REF_8X8 }; - let mut list = [Mv::ZERO; 2]; - let mut count = 0; - let add = |mv: Mv, list: &mut [Mv; 2], count: &mut usize| { - if *count >= 2 || (*count > 0 && mv == list[0]) { - return; - } - list[*count] = mv; - *count += 1; - }; - let mut counter = 0usize; - for (i, &(dr, dc)) in search.iter().enumerate() { - let (cr, cc) = (r as i32 + dr, c as i32 + dc); - if !self.is_inside(cr, cc) { - continue; - } - let m = self.mi_at(cr as usize, cc as usize); - if i < 2 { - counter += MODE_2_COUNTER[m.mode as usize] as usize; - } - if m.inter { - add(m.mv, &mut list, &mut count); - } - } - let ctx = COUNTER_TO_CONTEXT[counter.min(18)] as usize; - let n8 = if bsize == BLOCK_16X16 { 2 } else { 1 }; - let clamp = |mv: Mv, border: i32| -> Mv { - let (r, c, b) = (r as i32, c as i32, n8); - let to_top = -(r * 64); - let to_bottom = (self.f.mi_rows as i32 - b - r) * 64; - let to_left = -(c * 64); - let to_right = (self.f.mi_cols as i32 - b - c) * 64; - Mv::new( - mv.row.clamp(to_top - border, (to_bottom + border).max(to_top - border)), - mv.col.clamp(to_left - border, (to_right + border).max(to_left - border)), - ) - }; - for mv in list.iter_mut() { - *mv = clamp(*mv, 128); - // No high precision: odd components move toward zero. - let even = |v: i32| if v & 1 != 0 { v + if v > 0 { -1 } else { 1 } } else { v }; - *mv = clamp(Mv::new(even(mv.row), even(mv.col)), (160 - 4) << 3); - } - (list[0], list[1], ctx) - } - - /// The allowed motion vector range of a block (no decoder clamping inside it). - fn mv_range(&self, r: usize, c: usize, n: i32) -> (i32, i32, i32, i32) { - let n8 = n / 8; - let (r, c) = (r as i32, c as i32); - let lo_c = -(c * 64) - (4 + n) * 8; - let hi_c = (self.f.mi_cols as i32 - n8 - c) * 64 + (4 + n) * 8 - 8; - let lo_r = -(r * 64) - (4 + n) * 8; - let hi_r = (self.f.mi_rows as i32 - n8 - r) * 64 + (4 + n) * 8 - 8; - let lim = 8 * 1000; - (lo_r.max(-lim), hi_r.min(lim), lo_c.max(-lim), hi_c.min(lim)) - } - - /// SAD of the luma prediction at `mv` against the source. - fn sad(&self, r: usize, c: usize, n: usize, mv: Mv) -> u32 { - let p = &self.planes[0]; - let (x16, y16) = (((c * 8) << 4) as i32 + 2 * mv.col, ((r * 8) << 4) as i32 + 2 * mv.row); - let mut s = 0u32; - if let Some((pl, o, st)) = self.f.phases.as_ref().and_then(|ph| ph.block(x16, y16, n)) { - let a = &p.src[(r * 8) * p.w + c * 8..]; - let b = &pl[o..]; - return match n { - 8 => sad_n::<8>(a, p.w, b, st), - 16 => sad_n::<16>(a, p.w, b, st), - _ => { - for i in 0..n { - s += a[i * p.w..][..n].iter().zip(&b[i * st..][..n]).map(|(a, b)| a.abs_diff(*b) as u32).sum::(); - } - s - } - }; - } - // Inter search only runs on frames with a reference. - let Some(refs) = self.f.refs.as_ref() else { return u32::MAX / 4 }; - let mut pred = [0u8; 256]; - predict(&refs[0], x16, y16, n, n, &mut pred); - for i in 0..n { - let a = &p.src[(r * 8 + i) * p.w + c * 8..][..n]; - s += a.iter().zip(&pred[i * n..i * n + n]).map(|(a, b)| (*a as i32 - *b as i32).unsigned_abs()).sum::(); - } - s - } - - /// Motion search for the block at (`r`, `c`): the best vector and its cost - /// (SAD + λ·bits). - fn search(&self, r: usize, c: usize, bsize: u8) -> (Mv, f64) { - let n = if bsize == BLOCK_16X16 { 16 } else { 8 }; - let (nearest, near, mctx) = self.mv_refs(r, c, bsize); - let (lo_r, hi_r, lo_c, hi_c) = self.mv_range(r, c, n as i32); - let inside = |mv: Mv| mv.row >= lo_r && mv.row <= hi_r && mv.col >= lo_c && mv.col <= hi_c; - let lambda = self.f.lambda_sad / 256.0; - let d = &probs_defaults().0; - let mb: [u32; 4] = std::array::from_fn(|m| tree_cost(&INTER_MODE_TREE, &d[probs::INTER_MODE + mctx * 3..], m as u8)); - let mode_bits = |m: u8| mb[(m - NEARESTMV) as usize]; - let cost_raw = |mv: Mv| -> f64 { - let bits = if mv == nearest { - mode_bits(NEARESTMV) - } else if mv == Mv::ZERO { - mode_bits(ZEROMV) - } else if mv == near { - mode_bits(NEARMV) - } else { - mode_bits(NEWMV) + mv_cost(mv.minus(nearest)) - }; - self.sad(r, c, n, mv) as f64 + lambda * bits as f64 - }; - // The cost is a pure function of the vector: points the search revisits (the centre it - // came from, candidates on the diamond) are looked up instead of predicted again. - let mut seen = [(Mv::ZERO, 0.0f64); 64]; - let mut nseen = 0usize; - let mut cost = |mv: Mv| -> f64 { - if let Some(&(_, j)) = seen[..nseen].iter().find(|e| e.0 == mv) { - return j; - } - let j = cost_raw(mv); - if nseen < seen.len() { - seen[nseen] = (mv, j); - nseen += 1; - } - j - }; - let mut best = (Mv::ZERO, cost(Mv::ZERO)); - let prev = self.f.prev_mvs.map(|p| p[r * self.f.mi_cols + c]); - for cand in [Some(nearest), Some(near), prev].into_iter().flatten() { - if cand != Mv::ZERO && inside(cand) { - let j = cost(cand); - if j < best.1 { - best = (cand, j); - } - } - } - // A perfect match needs no search. - let good_enough = lambda * 256.0 * 0.5; - if best.1 > good_enough { - // Whole-sample search from the best candidate: a coarse cross, then a small diamond. - let round8 = |v: i32| (v + 4).div_euclid(8) * 8; - let mut cur = Mv::new(round8(best.0.row), round8(best.0.col)); - let mut cur_j = if inside(cur) { cost(cur) } else { f64::MAX }; - if cur_j < best.1 { - best = (cur, cur_j); - } else { - cur = best.0; - cur_j = best.1; - } - for (step, max_iters) in [(16, 1), (8, 2), (4, 3), (2, 4), (1, 8)] { - let s = step * 8; - for _ in 0..max_iters { - let mut moved = false; - let center = cur; - for (dr, dc) in [(-s, 0), (s, 0), (0, -s), (0, s)] { - let cand = Mv::new(center.row + dr, center.col + dc); - if !inside(cand) { - continue; - } - let j = cost(cand); - if j < cur_j { - cur = cand; - cur_j = j; - moved = true; - } - } - if !moved { - break; - } - } - } - if cur_j < best.1 { - best = (cur, cur_j); - } - } - // Sub-sample refinement: half then quarter samples (1/8 units, even: no high precision). - for s in [4, 2] { - let center = best.0; - for (dr, dc) in [(-s, 0), (s, 0), (0, -s), (0, s), (-s, -s), (-s, s), (s, -s), (s, s)] { - let cand = Mv::new(center.row + dr, center.col + dc); - if !inside(cand) { - continue; - } - let j = cost(cand); - if j < best.1 { - best = (cand, j); - } - } - } - best - } - - /// Choose between one 16×16 block and four 8×8 blocks at (`r`, `c`): `Some(mv)` for 16×16. - fn decide16(&mut self, r: usize, c: usize) -> Option { - let (mv16, j16) = self.search(r, c, BLOCK_16X16); - let lambda = self.f.lambda_sad; - // A near-perfect 16×16 prediction needs no split. - if j16 < 2.0 * 256.0 / 256.0 * 16.0 { - return Some(mv16); - } - let mut j8 = lambda * 4.0; // partition split + three more blocks of mode info (≈ 4 bits) - for (k, (dr, dc)) in [(0, 0), (0, 1), (1, 0), (1, 1)].into_iter().enumerate() { - let (rr, cc) = (r + dr, c + dc); - let (mv, j) = self.search(rr, cc, BLOCK_8X8); - j8 += j; - self.pending[k] = (rr, cc, mv); - // Tentative mode info so the next 8×8 blocks see this one as a candidate. - self.set_mi(rr, cc, 1, Mi { inter: true, mode: NEWMV, mv, skip: false, size: BLOCK_8X8 }); - } - if j16 <= j8 { - self.pending = [(usize::MAX, 0, Mv::ZERO); 4]; - Some(mv16) - } else { - None - } - } - - /// Code an inter block with motion vector `mv`. - fn inter_block(&mut self, r: usize, c: usize, bsize: u8, mv: Mv) { - let (mi_rows, mi_cols) = (self.f.mi_rows, self.f.mi_cols); - let n8 = if bsize == BLOCK_16X16 { 2 } else { 1 }; - let n = 8 * n8; - let (nearest, near, mctx) = self.mv_refs(r, c, bsize); - let d = &probs_defaults().0; - let mode_bits = |m: u8| tree_cost(&INTER_MODE_TREE, &d[probs::INTER_MODE + mctx * 3..], m - NEARESTMV); - let mut mode = (NEWMV, mode_bits(NEWMV) + mv_cost(mv.minus(nearest))); - for (m, ok) in [(NEARESTMV, mv == nearest), (NEARMV, mv == near), (ZEROMV, mv == Mv::ZERO)] { - if ok && mode_bits(m) < mode.1 { - mode = (m, mode_bits(m)); - } - } - let mode = mode.0; - // Prediction of every plane (written straight into the reconstruction). - let Some(refs) = self.f.refs.as_ref() else { return }; - for plane in 0..3 { - let ch = plane > 0; - let (row16, col16) = clamp_for_plane(mv, r, c, n8, mi_rows, mi_cols, ch); - let bn = if ch { n / 2 } else { n }; - let (bx, by) = if ch { (c * 4, r * 4) } else { (c * 8, r * 8) }; - let mut pred = [0u8; 256]; - predict(&refs[plane], ((bx as i32) << 4) + col16, ((by as i32) << 4) + row16, bn, bn, &mut pred); - let p = &mut self.planes[plane]; - for i in 0..bn { - p.rec[(by + i) * p.w + bx..][..bn].copy_from_slice(&pred[i * bn..i * bn + bn]); - } - } - // Residual: quantise every 4×4, dropping blocks whose coefficients don't pay for themselves. - let q = Quant { round_dc: 0.5, round_ac: 0.42, ..self.f.quant }; - // Weight of the rate term when dropping a transform block's coefficients (low: keep quality on par - // with key frames at the same q index). - let zl = 0.07; - let tx = DCT_DCT; - let mut blocks: Vec<(usize, usize, usize, [i32; 16], usize)> = Vec::with_capacity(24); - let mut any = false; - for plane in 0..3 { - let ch = plane > 0; - let bn = if ch { n / 2 } else { n }; - let (bx, by) = if ch { (c * 4, r * 4) } else { (c * 8, r * 8) }; - let p = &self.planes[plane]; - for ty in (0..bn).step_by(4) { - for tx4 in (0..bn).step_by(4) { - let (x, y) = (bx + tx4, by + ty); - let mut res = [0i32; 16]; - for i in 0..4 { - for j in 0..4 { - let k = (y + i) * p.w + x + j; - res[i * 4 + j] = p.src[k] as i32 - p.rec[k] as i32; - } - } - let mut levels = q.levels(&res, tx); - let mut eob = eob_of(&levels, tx); - if eob > 0 && !q.lossless { - let back = q.residual(&levels, tx, eob); - let d_skip: i64 = res.iter().map(|v| (*v as i64) * (*v as i64)).sum(); - let d_code: i64 = res.iter().zip(&back).map(|(a, b)| ((a - b) as i64).pow(2)).sum(); - if d_skip as f64 <= d_code as f64 + zl * self.f.lambda_sse * levels_cost(&levels, eob) as f64 / 256.0 { - levels = [0; 16]; - eob = 0; - } - } - any |= eob > 0; - blocks.push((plane, x, y, levels, eob)); - } - } - } - let skip = !any; - // Mode info. - let avail_u = r > 0; - let avail_l = c > self.col_start; - let above = avail_u.then(|| self.mi_at(r - 1, c)); - let left = avail_l.then(|| self.mi_at(r, c - 1)); - let skip_ctx = above.is_some_and(|m| m.skip) as usize + left.is_some_and(|m| m.skip) as usize; - self.w.ctx(skip, probs::SKIP + skip_ctx); - // is_inter context: neighbours are inter (no intra blocks in inter frames). - let intra = |m: Option| m.is_some_and(|m| !m.inter); - let inter_ctx = match (above, left) { - (Some(_), Some(_)) => { - if intra(above) && intra(left) { - 3 - } else { - (intra(above) || intra(left)) as usize - } - } - (Some(_), None) => 2 * intra(above) as usize, - (None, Some(_)) => 2 * intra(left) as usize, - (None, None) => 0, - }; - self.w.ctx(true, probs::IS_INTER + inter_ctx); - // single_ref_p1 = 0 (LAST_FRAME); context for single-reference LAST neighbours. - let ref_ctx = match (above, left) { - (Some(a), Some(l)) => match (a.inter, l.inter) { - (false, false) => 2, - (false, true) => 4, - (true, false) => 4, - (true, true) => 4, - }, - (Some(m), None) | (None, Some(m)) => { - if m.inter { - 4 - } else { - 2 - } - } - (None, None) => 2, - }; - self.w.ctx(false, probs::SINGLE_REF + ref_ctx * 2); - self.w.tree(&INTER_MODE_TREE, mode - NEARESTMV, |i| P::Ctx((probs::INTER_MODE + mctx * 3 + i) as u16)); - if mode == NEWMV { - self.write_mv(mv.minus(nearest)); - } - self.set_mi(r, c, n8, Mi { inter: true, mode, mv, skip, size: bsize }); - // Tokens and reconstruction. - for (plane, x, y, levels, eob) in blocks { - let ss = (plane > 0) as usize; - let (x4, ly) = (x >> 2, (y >> 2) & (15 >> ss)); - if !skip { - let ctx0 = (self.above_nz[plane][x4] + self.left_nz[plane][ly]) as usize; - self.tokens(plane, true, tx, &levels, eob, ctx0); - if eob > 0 { - let resid = q.residual(&levels, tx, eob); - let p = &mut self.planes[plane]; - for i in 0..4 { - for j in 0..4 { - let k = (y + i) * p.w + x + j; - p.rec[k] = (p.rec[k] as i32 + resid[i * 4 + j]).clamp(0, 255) as u8; - } - } - } - } - let nz = (!skip && eob > 0) as u8; - self.above_nz[plane][x4] = nz; - self.left_nz[plane][ly] = nz; - } - } - - fn write_mv(&mut self, diff: Mv) { - let joint = (diff.row != 0) as u8 * 2 + (diff.col != 0) as u8; - self.w.tree(&MV_JOINT_TREE, joint, |i| P::Ctx((probs::MV_JOINT + i) as u16)); - if diff.row != 0 { - self.write_mv_component(0, diff.row); - } - if diff.col != 0 { - self.write_mv_component(1, diff.col); - } - } - - fn write_mv_component(&mut self, i: usize, v: i32) { - debug_assert!(v != 0 && v & 1 == 0, "vectors are even without high precision"); - let offset = v.unsigned_abs() - 1; - let c = mv_class(offset); - self.w.ctx(v < 0, probs::MV_SIGN + i); - self.w.tree(&MV_CLASS_TREE, c as u8, |k| P::Ctx((probs::MV_CLASS + i * 10 + k) as u16)); - let fr = ((offset >> 1) & 3) as u8; - if c == 0 { - let b = (offset >> 3) as usize; - self.w.ctx(b == 1, probs::MV_CLASS0_BIT + i); - self.w.tree(&MV_FR_TREE, fr, |k| P::Ctx((probs::MV_CLASS0_FR + i * 6 + b * 3 + k) as u16)); - } else { - let dd = (offset - (1 << (c + 3))) >> 3; - for b in 0..c { - self.w.ctx((dd >> b) & 1 == 1, probs::MV_BITS + i * 10 + b as usize); - } - self.w.tree(&MV_FR_TREE, fr, |k| P::Ctx((probs::MV_FR + i * 3 + k) as u16)); - } - } -} diff --git a/crates/vp9enc/src/transform.rs b/crates/vp9enc/src/transform.rs deleted file mode 100644 index 20996d60b..000000000 --- a/crates/vp9enc/src/transform.rs +++ /dev/null @@ -1,270 +0,0 @@ -//! 4×4 transforms: the decoder's integer inverse DCT / ADST (spec 8.7.1) and Walsh–Hadamard -//! transform (8.7.1.10), and their forward counterparts (the exact inverse matrices of the -//! decoder's linear transforms, so `inverse4(forward(res)) ≈ res`; the WHT inverse is exact). - -pub const DCT_DCT: u8 = 0; -pub const ADST_DCT: u8 = 1; -pub const DCT_ADST: u8 = 2; -pub const ADST_ADST: u8 = 3; - -fn r14(v: i32) -> i32 { - (v + (1 << 13)) >> 14 -} - -fn idct4(x: [i32; 4]) -> [i32; 4] { - let v0 = r14(x[0] * 11585 - x[2] * 11585); - let v1 = r14(x[0] * 11585 + x[2] * 11585); - let v2 = r14(x[1] * 6270 - x[3] * 15137); - let v3 = r14(x[1] * 15137 + x[3] * 6270); - [v1 + v3, v0 + v2, v0 - v2, v1 - v3] -} - -fn iadst4(x: [i32; 4]) -> [i32; 4] { - let (s19, s29, s39, s49) = (5283i64, 9929i64, 13377i64, 15212i64); - let x: [i64; 4] = x.map(|v| v as i64); - let s0 = s19 * x[0]; - let s1 = s29 * x[0]; - let s2 = s39 * x[1]; - let s3 = s49 * x[2]; - let s4 = s19 * x[2]; - let s5 = s29 * x[3]; - let s6 = s49 * x[3]; - let s7 = s39 * (x[0] - x[2] + x[3]); - let x0 = s0 + s3 + s5; - let x1 = s1 - s4 - s6; - let x2 = s7; - let x3 = s2; - let r = |v: i64| ((v + (1 << 13)) >> 14) as i32; - [r(x0 + x3), r(x1 + x3), r(x2), r(x0 + x1 - x3)] -} - -/// The decoder's 4×4 inverse transform of dequantised coefficients (row-major) to a residual. -pub fn inverse4(coefs: &[i32; 16], tx_type: u8) -> [i32; 16] { - let row_adst = matches!(tx_type, DCT_ADST | ADST_ADST); - let col_adst = matches!(tx_type, ADST_DCT | ADST_ADST); - let mut t = [[0i32; 4]; 4]; - for i in 0..4 { - let r = [coefs[i * 4], coefs[i * 4 + 1], coefs[i * 4 + 2], coefs[i * 4 + 3]]; - t[i] = if row_adst { iadst4(r) } else { idct4(r) }; - } - let mut out = [0i32; 16]; - for j in 0..4 { - let c = [t[0][j], t[1][j], t[2][j], t[3][j]]; - let c = if col_adst { iadst4(c) } else { idct4(c) }; - for i in 0..4 { - out[i * 4 + j] = (c[i] + 8) >> 4; - } - } - out -} - -/// Inverse Walsh–Hadamard (8.7.1.10) on one line. -fn iwht(x: [i32; 4], shift: u32) -> [i32; 4] { - let mut a = x[0] >> shift; - let mut c = x[1] >> shift; - let mut d = x[2] >> shift; - let mut b = x[3] >> shift; - a += c; - d -= b; - let e = (a - d) >> 1; - b = e - b; - c = e - c; - a -= b; - d += c; - [a, b, c, d] -} - -/// The exact inverse of [`iwht`] (with shift 0): inputs that produce outputs `y`. -fn fwht(y: [i32; 4]) -> [i32; 4] { - let (aa, bb, cc, dd) = (y[0], y[1], y[2], y[3]); - let d1 = dd - cc; - let a1 = aa + bb; - let e = (a1 - d1) >> 1; - let b0 = e - bb; - let c0 = e - cc; - let a0 = a1 - c0; - let d0 = d1 + b0; - [a0, c0, d0, b0] -} - -pub fn inverse_wht(coefs: &[i32; 16]) -> [i32; 16] { - let mut t = [[0i32; 4]; 4]; - for i in 0..4 { - t[i] = iwht([coefs[i * 4], coefs[i * 4 + 1], coefs[i * 4 + 2], coefs[i * 4 + 3]], 2); - } - let mut out = [0i32; 16]; - for j in 0..4 { - let r = iwht([t[0][j], t[1][j], t[2][j], t[3][j]], 0); - for i in 0..4 { - out[i * 4 + j] = r[i]; - } - } - out -} - -/// Lossless forward transform: levels (dequantised by 4) whose inverse is exactly `res`. -pub fn forward_wht(res: &[i32; 16]) -> [i32; 16] { - let mut t = [[0i32; 4]; 4]; - for j in 0..4 { - let col = fwht([res[j], res[4 + j], res[8 + j], res[12 + j]]); - for i in 0..4 { - t[i][j] = col[i]; - } - } - let mut lv = [0i32; 16]; - for i in 0..4 { - let r = fwht(t[i]); - lv[i * 4..i * 4 + 4].copy_from_slice(&r); - } - lv -} - -/// Forward transform matrices: the inverses of the decoder's (linear part of the) inverse -/// transforms. -pub fn forward_matrices() -> &'static [[[f64; 16]; 16]; 4] { - use std::sync::OnceLock; - static M: OnceLock<[[[f64; 16]; 16]; 4]> = OnceLock::new(); - M.get_or_init(|| { - let mut out = [[[0.0; 16]; 16]; 4]; - for (ty, o) in out.iter_mut().enumerate() { - // Columns of the inverse: response to a scaled unit coefficient. - let k = 1 << 16; - let mut inv = [[0.0f64; 16]; 16]; - for c in 0..16 { - let mut e = [0i32; 16]; - e[c] = k; - let r = inverse4(&e, ty as u8); - for p in 0..16 { - inv[p][c] = r[p] as f64 / k as f64; - } - } - *o = invert16(inv); - } - out - }) -} - -/// [`forward_matrices`] transposed: `[type][q][k]`. -fn forward_matrices_t() -> &'static [[[f64; 16]; 16]; 4] { - use std::sync::OnceLock; - static M: OnceLock<[[[f64; 16]; 16]; 4]> = OnceLock::new(); - M.get_or_init(|| { - let f = forward_matrices(); - std::array::from_fn(|t| std::array::from_fn(|q| std::array::from_fn(|k| f[t][k][q]))) - }) -} - -fn invert16(m: [[f64; 16]; 16]) -> [[f64; 16]; 16] { - let mut a = m; - let mut inv = [[0.0; 16]; 16]; - for (i, r) in inv.iter_mut().enumerate() { - r[i] = 1.0; - } - for col in 0..16 { - let piv = (col..16).max_by(|&x, &y| a[x][col].abs().total_cmp(&a[y][col].abs())).unwrap_or(col); - a.swap(col, piv); - inv.swap(col, piv); - let d = a[col][col]; - for j in 0..16 { - a[col][j] /= d; - inv[col][j] /= d; - } - for r in 0..16 { - if r != col { - let f = a[r][col]; - if f != 0.0 { - for j in 0..16 { - a[r][j] -= f * a[col][j]; - inv[r][j] -= f * inv[col][j]; - } - } - } - } - } - inv -} - -/// Quantisation settings of one block. -#[derive(Clone, Copy)] -pub struct Quant { - pub dcq: i32, - pub acq: i32, - pub lossless: bool, - /// Rounding offsets (fractions of a step) for DC and AC; smaller = wider dead zone. - pub round_dc: f64, - pub round_ac: f64, -} - -impl Quant { - /// Quantised levels (raster order) of a residual. - pub fn levels(&self, res: &[i32; 16], tx_type: u8) -> [i32; 16] { - if self.lossless { - return forward_wht(res); - } - let ft = &forward_matrices_t()[tx_type as usize]; - // Every output accumulates its 16 products in the same order (q ascending) as a - // per-output sum, but the 16 outputs advance together: 16 independent chains that - // vectorize instead of one serial chain per output. - let mut acc = [-0.0f64; 16]; - for (q, col) in ft.iter().enumerate() { - let r = res[q] as f64; - for k in 0..16 { - acc[k] += col[k] * r; - } - } - let mut lv = [0i32; 16]; - for k in 0..16 { - let coef = acc[k]; - let (qs, rnd) = if k == 0 { (self.dcq, self.round_dc) } else { (self.acq, self.round_ac) }; - // Non-negative: truncation is floor. - let l = (coef.abs() / qs as f64 + rnd) as i32; - lv[k] = if coef < 0.0 { -l } else { l }; - } - lv - } - - /// The decoder's residual for `levels` (all zero when `eob` is 0). - pub fn residual(&self, levels: &[i32; 16], tx_type: u8, eob: usize) -> [i32; 16] { - if eob == 0 { - return [0; 16]; - } - let mut deq = [0i32; 16]; - for k in 0..16 { - deq[k] = levels[k] * if k == 0 { self.dcq } else { self.acq }; - } - if self.lossless { inverse_wht(&deq) } else { inverse4(&deq, tx_type) } - } -} - -#[cfg(test)] -mod tests { - use super::*; - - #[test] - fn wht_is_exactly_invertible() { - let mut s = 7u32; - for _ in 0..2000 { - let mut r = [0i32; 16]; - for v in &mut r { - s = s.wrapping_mul(1664525).wrapping_add(1013904223); - *v = (s >> 16) as i32 % 511 - 255; - } - let lv = forward_wht(&r); - let deq = lv.map(|l| l * 4); - assert_eq!(inverse_wht(&deq), r); - } - } - - #[test] - fn forward_transforms_invert_the_decoder() { - for ty in 0..4u8 { - let f = &forward_matrices()[ty as usize]; - let r: [i32; 16] = std::array::from_fn(|i| (i as i32 * 37 % 61) - 30); - let c: [i32; 16] = std::array::from_fn(|k| (0..16).map(|q| f[k][q] * r[q] as f64).sum::().round() as i32); - let back = inverse4(&c, ty); - for i in 0..16 { - assert!((back[i] - r[i]).abs() <= 1, "type {ty}: {back:?} vs {r:?}"); - } - } - } -} diff --git a/docs/architecture.md b/docs/architecture.md index f50c38f64..0d9a23c1b 100644 --- a/docs/architecture.md +++ b/docs/architecture.md @@ -18,10 +18,7 @@ cpal or muda. Everything in L0 to L4, the egui UI and the web app also build for | L0 | `time` | `Tick` (254 016 000 000 per second), rational frame rates incl. NTSC, SMPTE and drop-frame timecode | | L0 | `geom` | Vectors, matrices, quaternions, the layer transform (anchor, position, scale, orientation, rotation) | | L0 | `color` | sRGB and linear, HSL/HSV, luminance, the 38 blend modes, label colors | -| L0 | `vp9enc` | VP9 encoder (profile 0, 8-bit 4:2:0, key and inter frames with motion search, loop filter and rate control; lossless at quality 100) for WebM export, from the VP9 bitstream specification | | L0 | `opusenc` | Opus encoder: RFC 6716 SILK (NB/MB/WB), hybrid (SWB/FB) and CELT (FB) modes chosen by bitrate and application (audio / voice), 48 kHz 20 ms packets, mono/stereo, and the RFC 7845 `OpusHead`, for WebM export audio | -| L0 | `hevcenc` | HEVC (H.265) encoder from ITU-T H.265: Main / Main 10 4:2:0, IDR + P slices (quarter-pel motion, merge/AMVP), CABAC, deblocking, bitrate or constant-QP rate control, for MP4 (`hvc1`) export | -| L0 | `av1enc` | AV1 encoder from the AV1 bitstream specification: Main profile 8/10-bit 4:2:0, key + inter frames (quarter-pel motion), deblocking, for MP4 (`av01`) and WebM export | | L1 | `raster` | Premultiplied float images, sampling, affine and projective warps, blurs, compositing (parallel with rayon) | | L1 | `keyframe` | Animated values, keyframes with temporal ease and spatial Bezier, roving, hold, velocity | | L1 | `segment` | Swappable trained models for Roto Brush and face tracking: the `MaskModel` and `FaceModel` interfaces, a registry of open-licensed models (authors, licence, URL, size, SHA-256), PyTorch checkpoint and TensorFlow Lite readers (with an interpreter), SHA-256, MobileSAM (TinyViT + Segment Anything decoder) and MediaPipe Face Landmarker (BlazeFace + Face Mesh V2) on rayon and ndarray's safe GEMM | diff --git a/xtask/src/main.rs b/xtask/src/main.rs index 16e28fa07..e346ca897 100644 --- a/xtask/src/main.rs +++ b/xtask/src/main.rs @@ -36,11 +36,8 @@ const LAYERS: &[(&str, u8)] = &[ ("time", 0), ("geom", 0), ("color", 0), - ("vp9enc", 0), ("testkit", 0), ("opusenc", 0), - ("hevcenc", 0), - ("av1enc", 0), ("raster", 1), ("keyframe", 1), ("path", 1), diff --git a/xtask/src/pdf_deps.rs b/xtask/src/pdf_deps.rs index e61ab5012..beb1e0cc9 100644 --- a/xtask/src/pdf_deps.rs +++ b/xtask/src/pdf_deps.rs @@ -5,7 +5,7 @@ use std::process::Command; use serde_json::Value; -const REV: &str = "e11f9d9aebea5f6b8e74643c1ea1ed172ba5f545"; +const REV: &str = "e7c3dda7f3e339dd2c8d40fc4eb405472be1a1e4"; const PACKAGES: &[(&str, &str)] = &[("craft-pdf", "0.1.0"), ("hayro-syntax", "0.7.2"), ("hayro-jbig2", "0.3.0")]; pub fn run(root: &Path) -> Result<(), String> {