diff --git a/ATTRIBUTION.md b/ATTRIBUTION.md index f5f91aa..9ffb154 100644 --- a/ATTRIBUTION.md +++ b/ATTRIBUTION.md @@ -20,6 +20,9 @@ deck and UI icons are original work of the SlideCraft contributors (drawn or gen | `assets/app-icon/slidecraft-1024.png`, `assets/app-icon/hicolor/*/apps/ai.storyteller.slidecraft.png` | SlideCraft contributors | rendered by SlideCraft from `assets/app-icon/icon-source.slidecraft` (shapes only) | MIT OR Apache-2.0 | Placeholder app icon (presentation board) until the mascot icon is drawn | | `assets/app-icon/slidecraft.icns`, `assets/app-icon/slidecraft.ico` | SlideCraft contributors | generated from the PNGs above by `packaging/icons.sh` (sips/iconutil, `cargo xtask ico`) | MIT OR Apache-2.0 | macOS and Windows app icons | | `assets/app-icon/icon-source.slidecraft` | SlideCraft contributors | made with `slidecraft-cli run` (see git history) | MIT OR Apache-2.0 | Source of the placeholder icon | +| `crates/media/tests/fixtures/tone*` (`tone.mp3`, `.m4a`, `.aac`, `.ogg`, `.opus`, `.flac`, `.wma`, `tone-alac.m4a`) | SlideCraft contributors | synthesised 0.5 s 440 Hz sine, encoded by `crates/media/tests/fixtures/make.sh` (ffmpeg as the encoder only) | CC0-1.0 | Audio decoding tests | +| `crates/media/tests/fixtures/clip-*` (`clip-h264.mp4`, `clip-h264.mov`, `clip-hevc.mp4`, `clip-vp9.webm`, `clip-av1.mkv`) | SlideCraft contributors | 6 frames of a colour gradient generated by `make.sh` (python3), encoded by ffmpeg (x264 / x265 / libvpx / SVT-AV1 as encoders only) | CC0-1.0 | Video decoding tests | +| `crates/media/tests/fixtures/make.sh` | SlideCraft contributors | this repository | MIT OR Apache-2.0 | Regenerates the media fixtures | ## Bundled through dependencies diff --git a/Cargo.lock b/Cargo.lock index deee629..a5e6d15 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -97,8 +97,8 @@ dependencies = [ "accesskit_consumer 0.35.0", "hashbrown 0.16.1", "static_assertions", - "windows", - "windows-core", + "windows 0.62.2", + "windows-core 0.62.2", ] [[package]] @@ -140,6 +140,28 @@ version = "0.2.21" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "683d7910e743518b0e34f1186f92494becacb047c7b6bf616c96772180fef923" +[[package]] +name = "alsa" +version = "0.9.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ed7572b7ba83a31e20d1b48970ee402d2e3e0537dcfe0a3ff4d6eb7508617d43" +dependencies = [ + "alsa-sys", + "bitflags 2.13.2", + "cfg-if", + "libc", +] + +[[package]] +name = "alsa-sys" +version = "0.3.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "db8fee663d06c4e303404ef5f40488a53e062f89ba8bfed81f42325aafad1527" +dependencies = [ + "libc", + "pkg-config", +] + [[package]] name = "android-activity" version = "0.6.1" @@ -149,7 +171,7 @@ dependencies = [ "android-properties", "bitflags 2.13.2", "cc", - "jni", + "jni 0.22.4", "libc", "log", "ndk", @@ -422,15 +444,30 @@ version = "0.22.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "72b3254f16251a8381aa12e40e3c4d2f0199f8c6508fbecb9d91f575e0fbb8c6" +[[package]] +name = "bit-set" +version = "0.8.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "08807e080ed7f9d5433fa9b275196cfc35414f66a0c79d864dc51a0d825231a3" +dependencies = [ + "bit-vec 0.8.0", +] + [[package]] name = "bit-set" version = "0.10.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "09ec2f926cc3060f09db9ebc5b52823d85268d24bb917e472c0c4bea35780a7d" dependencies = [ - "bit-vec", + "bit-vec 0.9.1", ] +[[package]] +name = "bit-vec" +version = "0.8.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5e764a1d40d510daf35e07be9eb06e75770908c27d411ee6c92109c9840eaaf7" + [[package]] name = "bit-vec" version = "0.9.1" @@ -580,6 +617,12 @@ dependencies = [ "shlex", ] +[[package]] +name = "cesu8" +version = "1.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6d43a04d8753f35258c91f8ec639f792891f748a1edbd759cf1dcea3382ad83c" + [[package]] name = "cfg-if" version = "1.0.5" @@ -705,6 +748,12 @@ dependencies = [ "libc", ] +[[package]] +name = "core_detect" +version = "1.0.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7f8f80099a98041a3d1622845c271458a2d73e688351bf3cb999266764b81d48" + [[package]] name = "core_maths" version = "0.1.1" @@ -714,6 +763,46 @@ dependencies = [ "libm", ] +[[package]] +name = "coreaudio-rs" +version = "0.13.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1aae284fbaf7d27aa0e292f7677dfbe26503b0d555026f702940805a630eac17" +dependencies = [ + "bitflags 1.3.2", + "libc", + "objc2-audio-toolbox", + "objc2-core-audio", + "objc2-core-audio-types", + "objc2-core-foundation", +] + +[[package]] +name = "cpal" +version = "0.16.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "cbd307f43cc2a697e2d1f8bc7a1d824b5269e052209e28883e5bc04d095aaa3f" +dependencies = [ + "alsa", + "coreaudio-rs", + "dasp_sample", + "jni 0.21.1", + "js-sys", + "libc", + "mach2", + "ndk", + "ndk-context", + "num-derive", + "num-traits", + "objc2-audio-toolbox", + "objc2-core-audio", + "objc2-core-audio-types", + "wasm-bindgen", + "wasm-bindgen-futures", + "web-sys", + "windows 0.54.0", +] + [[package]] name = "crc32fast" version = "1.5.2" @@ -769,6 +858,12 @@ version = "1.2.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "f27ae1dd37df86211c42e150270f82743308803d90a6f6e6651cd730d5e1732f" +[[package]] +name = "dasp_sample" +version = "0.11.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0c87e182de0887fd5361989c677c4e8f5000cd9491d6d563161a8f3a5519fc7f" + [[package]] name = "derive_arbitrary" version = "1.5.0" @@ -997,6 +1092,20 @@ dependencies = [ "bytemuck", ] +[[package]] +name = "encoding_rs" +version = "0.8.42" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8e985e0451871ad22fb8d2b6b076e2028a502a0d3950998c2c5c0a4f9b5d9679" +dependencies = [ + "cfg-if", + "core_detect", + "multiversion_no_op", + "rustversion", + "scopeguard", + "simdutf8", +] + [[package]] name = "endi" version = "1.1.1" @@ -1106,6 +1215,12 @@ dependencies = [ "pin-project-lite", ] +[[package]] +name = "extended" +version = "0.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "af9673d8203fcb076b19dfd17e38b3d4ae9f44959416ea532ce72415a6020365" + [[package]] name = "fastrand" version = "2.5.0" @@ -1165,6 +1280,12 @@ dependencies = [ "num-traits", ] +[[package]] +name = "fnv" +version = "1.0.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3f9eec918d3f24069decb9af1554cad7c880e2da24a9afd88aca000531ab82c1" + [[package]] name = "foldhash" version = "0.2.0" @@ -1456,7 +1577,7 @@ dependencies = [ "log", "presser", "thiserror 2.0.21", - "windows", + "windows 0.62.2", ] [[package]] @@ -1656,7 +1777,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "525e9ff3e1a4be2fbea1fdf0e98686a6d98b4d8f937e1bf7402245af1909e8c3" dependencies = [ "byteorder-lite", - "quick-error", + "quick-error 2.0.1", ] [[package]] @@ -1709,6 +1830,22 @@ version = "1.0.18" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "8f42a60cbdf9a97f5d2305f08a87dc4e09308d1276d28c869c684d7777685682" +[[package]] +name = "jni" +version = "0.21.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1a87aa2bb7d2af34197c04845522473242e1aa17c12f4935d5856491a7fb8c97" +dependencies = [ + "cesu8", + "cfg-if", + "combine", + "jni-sys 0.3.1", + "log", + "thiserror 1.0.69", + "walkdir", + "windows-sys 0.45.0", +] + [[package]] name = "jni" version = "0.22.4" @@ -1936,6 +2073,15 @@ version = "0.4.34" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "f9f8bd3e56ce4dfc153cf470fffbfa98c7620958b312ca5c3a4b8d5181fd13c6" +[[package]] +name = "mach2" +version = "0.4.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d640282b302c0bb0a2a8e0233ead9035e3bed871f0b7e81fe4a1ec829765db44" +dependencies = [ + "libc", +] + [[package]] name = "memchr" version = "2.8.3" @@ -1990,6 +2136,12 @@ dependencies = [ "pxfm", ] +[[package]] +name = "multiversion_no_op" +version = "1.0.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "743fb55ba31b18fb1ecef6bdc9aa2743314978ac084044301a7eee33fb99a20d" + [[package]] name = "naga" version = "30.0.1" @@ -1997,7 +2149,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "a616d2fb8c89516ac2723a581f69d6c18576046bed761bd6b305e5618e6ae130" dependencies = [ "arrayvec", - "bit-set", + "bit-set 0.10.0", "bitflags 2.13.2", "cfg-if", "cfg_aliases", @@ -2064,6 +2216,17 @@ version = "0.2.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "2bf50223579dc7cdcfb3bfcacf7069ff68243f8c363f62ffa99cf000a6b9c451" +[[package]] +name = "num-derive" +version = "0.4.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ed3955f1a9c7c0c15e092f9c887db08b1fc683305fdf6eb6684f22555355e202" +dependencies = [ + "proc-macro2", + "quote", + "syn 2.0.119", +] + [[package]] name = "num-traits" version = "0.2.19" @@ -2151,6 +2314,21 @@ dependencies = [ "objc2-foundation 0.3.2", ] +[[package]] +name = "objc2-audio-toolbox" +version = "0.3.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6948501a91121d6399b79abaa33a8aa4ea7857fe019f341b8c23ad6e81b79b08" +dependencies = [ + "bitflags 2.13.2", + "libc", + "objc2 0.6.5", + "objc2-core-audio", + "objc2-core-audio-types", + "objc2-core-foundation", + "objc2-foundation 0.3.2", +] + [[package]] name = "objc2-cloud-kit" version = "0.2.2" @@ -2175,6 +2353,28 @@ dependencies = [ "objc2-foundation 0.2.2", ] +[[package]] +name = "objc2-core-audio" +version = "0.3.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e1eebcea8b0dbff5f7c8504f3107c68fc061a3eb44932051c8cf8a68d969c3b2" +dependencies = [ + "dispatch2", + "objc2 0.6.5", + "objc2-core-audio-types", + "objc2-core-foundation", +] + +[[package]] +name = "objc2-core-audio-types" +version = "0.3.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5a89f2ec274a0cf4a32642b2991e8b351a404d290da87bb6a9a9d8632490bd1c" +dependencies = [ + "bitflags 2.13.2", + "objc2 0.6.5", +] + [[package]] name = "objc2-core-data" version = "0.2.2" @@ -2676,6 +2876,15 @@ dependencies = [ "portable-atomic", ] +[[package]] +name = "ppv-lite86" +version = "0.2.21" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "85eae3c4ed2f50dcfe72643da4befc30deadb458a9b590d720cde2f2b1e97da9" +dependencies = [ + "zerocopy", +] + [[package]] name = "presser" version = "0.3.1" @@ -2706,12 +2915,37 @@ version = "1.0.18" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "3d595e54a326bc53c1c197b32d295e14b169e3cfeaa8dc82b529f947fba6bcf5" +[[package]] +name = "proptest" +version = "1.11.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4b45fcc2344c680f5025fe57779faef368840d0bd1f42f216291f0dc4ace4744" +dependencies = [ + "bit-set 0.8.0", + "bit-vec 0.8.0", + "bitflags 2.13.2", + "num-traits", + "rand", + "rand_chacha", + "rand_xorshift", + "regex-syntax", + "rusty-fork", + "tempfile", + "unarray", +] + [[package]] name = "pxfm" version = "0.1.30" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "d55d956fa96f5ec02be2e13af0e20391a5aa83d6a074e3ad368959d0fab299ea" +[[package]] +name = "quick-error" +version = "1.2.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a1d01941d82fa2ab50be1e79e6714289dd7cde78eba4c074bc5a4374f650dfe0" + [[package]] name = "quick-error" version = "2.0.1" @@ -2757,6 +2991,44 @@ version = "6.0.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "f8dcc9c7d52a811697d2151c701e0d08956f92b0e24136cf4cf27b57a6a0d9bf" +[[package]] +name = "rand" +version = "0.9.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b9ef1d0d795eb7d84685bca4f72f3649f064e6641543d3a8c415898726a57b41" +dependencies = [ + "rand_chacha", + "rand_core", +] + +[[package]] +name = "rand_chacha" +version = "0.9.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d3022b5f1df60f26e1ffddd6c66e8aa15de382ae63b3a0c1bfc0e4d3e3f325cb" +dependencies = [ + "ppv-lite86", + "rand_core", +] + +[[package]] +name = "rand_core" +version = "0.9.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "76afc826de14238e6e8c374ddcc1fa19e374fd8dd986b0d2af0d02377261d83c" +dependencies = [ + "getrandom 0.3.4", +] + +[[package]] +name = "rand_xorshift" +version = "0.4.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "513962919efc330f829edb2535844d1b912b0fbe2ca165d613e4e8788bb05a5a" +dependencies = [ + "rand_core", +] + [[package]] name = "range-alloc" version = "0.1.5" @@ -2871,6 +3143,12 @@ dependencies = [ "bitflags 2.13.2", ] +[[package]] +name = "regex-syntax" +version = "0.8.11" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d6f6ff9a378485b298a5286656da665ba74413d36db0979633275d2e708145d4" + [[package]] name = "renderdoc-sys" version = "1.1.0" @@ -2957,6 +3235,18 @@ version = "1.0.23" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "cf54715a573b99ac80df0bc206da022bcd442c974952c7b9720069370852e21f" +[[package]] +name = "rusty-fork" +version = "0.3.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "cc6bf79ff24e648f6da1f8d1f011e9cac26491b619e6b9280f2b47f1774e6ee2" +dependencies = [ + "fnv", + "quick-error 1.2.3", + "tempfile", + "wait-timeout", +] + [[package]] name = "rustybuzz" version = "0.20.1" @@ -3162,6 +3452,7 @@ name = "slidecraft" version = "0.1.0" dependencies = [ "arboard", + "cpal", "eframe", "egui", "image", @@ -3169,6 +3460,7 @@ dependencies = [ "rfd", "serde_json", "slidecraft-engine", + "slidecraft-media", "slidecraft-ui-egui", "winit", "winresource", @@ -3183,6 +3475,22 @@ dependencies = [ "slidecraft-model", ] +[[package]] +name = "slidecraft-av1" +version = "0.1.0" +dependencies = [ + "rayon", + "slidecraft-bitstream", + "thiserror 2.0.21", +] + +[[package]] +name = "slidecraft-bitstream" +version = "0.1.0" +dependencies = [ + "proptest", +] + [[package]] name = "slidecraft-cli" version = "0.1.0" @@ -3212,6 +3520,7 @@ dependencies = [ "slidecraft-fonts", "slidecraft-format", "slidecraft-geom", + "slidecraft-media", "slidecraft-model", "slidecraft-pdf", "slidecraft-pptx", @@ -3251,6 +3560,36 @@ dependencies = [ "serde", ] +[[package]] +name = "slidecraft-h264" +version = "0.1.0" +dependencies = [ + "rayon", + "slidecraft-bitstream", + "thiserror 2.0.21", +] + +[[package]] +name = "slidecraft-hevc" +version = "0.1.0" +dependencies = [ + "rayon", + "slidecraft-bitstream", + "thiserror 2.0.21", +] + +[[package]] +name = "slidecraft-isobmff" +version = "0.1.0" +dependencies = [ + "slidecraft-bitstream", + "thiserror 2.0.21", +] + +[[package]] +name = "slidecraft-matroska" +version = "0.1.0" + [[package]] name = "slidecraft-mcp" version = "0.1.0" @@ -3261,6 +3600,24 @@ dependencies = [ "slidecraft-render", ] +[[package]] +name = "slidecraft-media" +version = "0.1.0" +dependencies = [ + "image", + "log", + "slidecraft-av1", + "slidecraft-h264", + "slidecraft-hevc", + "slidecraft-isobmff", + "slidecraft-matroska", + "slidecraft-ogg", + "slidecraft-opus", + "slidecraft-vp9", + "symphonia", + "thiserror 2.0.21", +] + [[package]] name = "slidecraft-model" version = "0.1.0" @@ -3272,6 +3629,14 @@ dependencies = [ "thiserror 2.0.21", ] +[[package]] +name = "slidecraft-ogg" +version = "0.1.0" + +[[package]] +name = "slidecraft-opus" +version = "0.1.0" + [[package]] name = "slidecraft-pdf" version = "0.1.0" @@ -3346,11 +3711,21 @@ dependencies = [ "slidecraft-engine", "slidecraft-fonts", "slidecraft-geom", + "slidecraft-media", "slidecraft-model", "slidecraft-render", "slidecraft-text", ] +[[package]] +name = "slidecraft-vp9" +version = "0.1.0" +dependencies = [ + "rayon", + "slidecraft-bitstream", + "thiserror 2.0.21", +] + [[package]] name = "slidecraft-web" version = "0.1.0" @@ -3497,6 +3872,201 @@ dependencies = [ "write-fonts", ] +[[package]] +name = "symphonia" +version = "0.5.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5773a4c030a19d9bfaa090f49746ff35c75dfddfa700df7a5939d5e076a57039" +dependencies = [ + "lazy_static", + "symphonia-bundle-flac", + "symphonia-bundle-mp3", + "symphonia-codec-aac", + "symphonia-codec-adpcm", + "symphonia-codec-alac", + "symphonia-codec-pcm", + "symphonia-codec-vorbis", + "symphonia-core", + "symphonia-format-caf", + "symphonia-format-isomp4", + "symphonia-format-mkv", + "symphonia-format-ogg", + "symphonia-format-riff", + "symphonia-metadata", +] + +[[package]] +name = "symphonia-bundle-flac" +version = "0.5.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c91565e180aea25d9b80a910c546802526ffd0072d0b8974e3ebe59b686c9976" +dependencies = [ + "log", + "symphonia-core", + "symphonia-metadata", + "symphonia-utils-xiph", +] + +[[package]] +name = "symphonia-bundle-mp3" +version = "0.5.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4872dd6bb56bf5eac799e3e957aa1981086c3e613b27e0ac23b176054f7c57ed" +dependencies = [ + "lazy_static", + "log", + "symphonia-core", + "symphonia-metadata", +] + +[[package]] +name = "symphonia-codec-aac" +version = "0.5.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4c263845aa86881416849c1729a54c7f55164f8b96111dba59de46849e73a790" +dependencies = [ + "lazy_static", + "log", + "symphonia-core", +] + +[[package]] +name = "symphonia-codec-adpcm" +version = "0.5.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2dddc50e2bbea4cfe027441eece77c46b9f319748605ab8f3443350129ddd07f" +dependencies = [ + "log", + "symphonia-core", +] + +[[package]] +name = "symphonia-codec-alac" +version = "0.5.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8413fa754942ac16a73634c9dfd1500ed5c61430956b33728567f667fdd393ab" +dependencies = [ + "log", + "symphonia-core", +] + +[[package]] +name = "symphonia-codec-pcm" +version = "0.5.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4e89d716c01541ad3ebe7c91ce4c8d38a7cf266a3f7b2f090b108fb0cb031d95" +dependencies = [ + "log", + "symphonia-core", +] + +[[package]] +name = "symphonia-codec-vorbis" +version = "0.5.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f025837c309cd69ffef572750b4a2257b59552c5399a5e49707cc5b1b85d1c73" +dependencies = [ + "log", + "symphonia-core", + "symphonia-utils-xiph", +] + +[[package]] +name = "symphonia-core" +version = "0.5.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ea00cc4f79b7f6bb7ff87eddc065a1066f3a43fe1875979056672c9ef948c2af" +dependencies = [ + "arrayvec", + "bitflags 1.3.2", + "bytemuck", + "lazy_static", + "log", +] + +[[package]] +name = "symphonia-format-caf" +version = "0.5.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b8faf379316b6b6e6bbc274d00e7a592e0d63ff1a7e182ce8ba25e24edd3d096" +dependencies = [ + "log", + "symphonia-core", + "symphonia-metadata", +] + +[[package]] +name = "symphonia-format-isomp4" +version = "0.5.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "243739585d11f81daf8dac8d9f3d18cc7898f6c09a259675fc364b382c30e0a5" +dependencies = [ + "encoding_rs", + "log", + "symphonia-core", + "symphonia-metadata", + "symphonia-utils-xiph", +] + +[[package]] +name = "symphonia-format-mkv" +version = "0.5.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "122d786d2c43a49beb6f397551b4a050d8229eaa54c7ddf9ee4b98899b8742d0" +dependencies = [ + "lazy_static", + "log", + "symphonia-core", + "symphonia-metadata", + "symphonia-utils-xiph", +] + +[[package]] +name = "symphonia-format-ogg" +version = "0.5.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2b4955c67c1ed3aa8ae8428d04ca8397fbef6a19b2b051e73b5da8b1435639cb" +dependencies = [ + "log", + "symphonia-core", + "symphonia-metadata", + "symphonia-utils-xiph", +] + +[[package]] +name = "symphonia-format-riff" +version = "0.5.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c2d7c3df0e7d94efb68401d81906eae73c02b40d5ec1a141962c592d0f11a96f" +dependencies = [ + "extended", + "log", + "symphonia-core", + "symphonia-metadata", +] + +[[package]] +name = "symphonia-metadata" +version = "0.5.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "36306ff42b9ffe6e5afc99d49e121e0bd62fe79b9db7b9681d48e29fa19e6b16" +dependencies = [ + "encoding_rs", + "lazy_static", + "log", + "symphonia-core", +] + +[[package]] +name = "symphonia-utils-xiph" +version = "0.5.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ee27c85ab799a338446b68eec77abf42e1a6f1bb490656e121c6e27bfbab9f16" +dependencies = [ + "symphonia-core", + "symphonia-metadata", +] + [[package]] name = "syn" version = "2.0.119" @@ -3610,7 +4180,7 @@ dependencies = [ "fax", "flate2", "half", - "quick-error", + "quick-error 2.0.1", "weezl", "zune-jpeg", ] @@ -3747,6 +4317,12 @@ dependencies = [ "windows-sys 0.61.2", ] +[[package]] +name = "unarray" +version = "0.1.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "eaea85b334db583fe3274d12b4cd1880032beab409c0d774be044d4480ab9a94" + [[package]] name = "unicode-bidi" version = "0.3.18" @@ -3884,6 +4460,15 @@ version = "0.9.5" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "0b928f33d975fc6ad9f86c8f283853ad26bdd5b10b7f1542aa2fa15e2289105a" +[[package]] +name = "wait-timeout" +version = "0.2.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "09ac3b126d3914f9849036f826e054cbabdc8519970b8998ddaf3b5bd3c65f11" +dependencies = [ + "libc", +] + [[package]] name = "walkdir" version = "2.5.0" @@ -4157,8 +4742,8 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "14c018fce9b6270aa203c2fdd56f3cce996713534bd757e4ea58c8560b121f14" dependencies = [ "arrayvec", - "bit-set", - "bit-vec", + "bit-set 0.10.0", + "bit-vec 0.9.1", "bitflags 2.13.2", "bytemuck", "cfg_aliases", @@ -4230,7 +4815,7 @@ dependencies = [ "android_system_properties", "arrayvec", "ash", - "bit-set", + "bit-set 0.10.0", "bitflags 2.13.2", "block2 0.6.2", "bytemuck", @@ -4272,9 +4857,9 @@ dependencies = [ "web-sys", "wgpu-naga-bridge", "wgpu-types", - "windows", - "windows-core", - "windows-result", + "windows 0.62.2", + "windows-core 0.62.2", + "windows-result 0.4.1", ] [[package]] @@ -4312,6 +4897,16 @@ dependencies = [ "windows-sys 0.61.2", ] +[[package]] +name = "windows" +version = "0.54.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9252e5725dbed82865af151df558e754e4a3c2c30818359eb17465f1346a1b49" +dependencies = [ + "windows-core 0.54.0", + "windows-targets 0.52.6", +] + [[package]] name = "windows" version = "0.62.2" @@ -4319,7 +4914,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "527fadee13e0c05939a6a05d5bd6eec6cd2e3dbd648b9f8e447c6518133d8580" dependencies = [ "windows-collections", - "windows-core", + "windows-core 0.62.2", "windows-future", "windows-numerics", ] @@ -4330,7 +4925,17 @@ version = "0.3.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "23b2d95af1a8a14a3c7367e1ed4fc9c20e0a26e79551b1454d72583c97cc6610" dependencies = [ - "windows-core", + "windows-core 0.62.2", +] + +[[package]] +name = "windows-core" +version = "0.54.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "12661b9c89351d684a50a8a643ce5f608e20243b9fb84687800163429f161d65" +dependencies = [ + "windows-result 0.1.2", + "windows-targets 0.52.6", ] [[package]] @@ -4342,7 +4947,7 @@ dependencies = [ "windows-implement", "windows-interface", "windows-link", - "windows-result", + "windows-result 0.4.1", "windows-strings", ] @@ -4352,7 +4957,7 @@ version = "0.3.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "e1d6f90251fe18a279739e78025bd6ddc52a7e22f921070ccdc67dde84c605cb" dependencies = [ - "windows-core", + "windows-core 0.62.2", "windows-link", "windows-threading", ] @@ -4391,10 +4996,19 @@ version = "0.3.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "6e2e40844ac143cdb44aead537bbf727de9b044e107a0f1220392177d15b0f26" dependencies = [ - "windows-core", + "windows-core 0.62.2", "windows-link", ] +[[package]] +name = "windows-result" +version = "0.1.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5e383302e8ec8515204254685643de10811af0ed97ea37210dc26fb0032647f8" +dependencies = [ + "windows-targets 0.52.6", +] + [[package]] name = "windows-result" version = "0.4.1" @@ -4413,6 +5027,15 @@ dependencies = [ "windows-link", ] +[[package]] +name = "windows-sys" +version = "0.45.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "75283be5efb2831d37ea142365f009c02ec203cd29a3ebecbc093d52315b66d0" +dependencies = [ + "windows-targets 0.42.2", +] + [[package]] name = "windows-sys" version = "0.52.0" @@ -4449,6 +5072,21 @@ dependencies = [ "windows-link", ] +[[package]] +name = "windows-targets" +version = "0.42.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8e5180c00cd44c9b1c88adb3693291f1cd93605ded80c250a75d472756b4d071" +dependencies = [ + "windows_aarch64_gnullvm 0.42.2", + "windows_aarch64_msvc 0.42.2", + "windows_i686_gnu 0.42.2", + "windows_i686_msvc 0.42.2", + "windows_x86_64_gnu 0.42.2", + "windows_x86_64_gnullvm 0.42.2", + "windows_x86_64_msvc 0.42.2", +] + [[package]] name = "windows-targets" version = "0.52.6" @@ -4491,6 +5129,12 @@ dependencies = [ "windows-link", ] +[[package]] +name = "windows_aarch64_gnullvm" +version = "0.42.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "597a5118570b68bc08d8d59125332c54f1ba9d9adeedeef5b99b02ba2b0698f8" + [[package]] name = "windows_aarch64_gnullvm" version = "0.52.6" @@ -4503,6 +5147,12 @@ version = "0.53.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "a9d8416fa8b42f5c947f8482c43e7d89e73a173cead56d044f6a56104a6d1b53" +[[package]] +name = "windows_aarch64_msvc" +version = "0.42.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e08e8864a60f06ef0d0ff4ba04124db8b0fb3be5776a5cd47641e942e58c4d43" + [[package]] name = "windows_aarch64_msvc" version = "0.52.6" @@ -4515,6 +5165,12 @@ version = "0.53.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "b9d782e804c2f632e395708e99a94275910eb9100b2114651e04744e9b125006" +[[package]] +name = "windows_i686_gnu" +version = "0.42.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c61d927d8da41da96a81f029489353e68739737d3beca43145c8afec9a31a84f" + [[package]] name = "windows_i686_gnu" version = "0.52.6" @@ -4539,6 +5195,12 @@ version = "0.53.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "fa7359d10048f68ab8b09fa71c3daccfb0e9b559aed648a8f95469c27057180c" +[[package]] +name = "windows_i686_msvc" +version = "0.42.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "44d840b6ec649f480a41c8d80f9c65108b92d89345dd94027bfe06ac444d1060" + [[package]] name = "windows_i686_msvc" version = "0.52.6" @@ -4551,6 +5213,12 @@ version = "0.53.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "1e7ac75179f18232fe9c285163565a57ef8d3c89254a30685b57d83a38d326c2" +[[package]] +name = "windows_x86_64_gnu" +version = "0.42.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8de912b8b8feb55c064867cf047dda097f92d51efad5b491dfb98f6bbb70cb36" + [[package]] name = "windows_x86_64_gnu" version = "0.52.6" @@ -4563,6 +5231,12 @@ version = "0.53.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "9c3842cdd74a865a8066ab39c8a7a473c0778a3f29370b5fd6b4b9aa7df4a499" +[[package]] +name = "windows_x86_64_gnullvm" +version = "0.42.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "26d41b46a36d453748aedef1486d5c7a85db22e56aff34643984ea85514e94a3" + [[package]] name = "windows_x86_64_gnullvm" version = "0.52.6" @@ -4575,6 +5249,12 @@ version = "0.53.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "0ffa179e2d07eee8ad8f57493436566c7cc30ac536a3379fdf008f47f6bb7ae1" +[[package]] +name = "windows_x86_64_msvc" +version = "0.42.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9aec5da331524158c6d1a4ac0ab1541149c0b9505fde06423b02f5ef0106b9f0" + [[package]] name = "windows_x86_64_msvc" version = "0.52.6" diff --git a/Cargo.toml b/Cargo.toml index b0d1b10..c27dc37 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -25,6 +25,17 @@ slidecraft-anim = { path = "crates/anim" } slidecraft-engine = { path = "crates/engine" } slidecraft-mcp = { path = "crates/mcp" } slidecraft-ui-egui = { path = "crates/ui-egui" } +slidecraft-media = { path = "crates/media" } +# codecs (ported from FilmCraft, same owner and licence) +slidecraft-bitstream = { path = "crates/bitstream" } +slidecraft-h264 = { path = "crates/h264" } +slidecraft-hevc = { path = "crates/hevc" } +slidecraft-vp9 = { path = "crates/vp9" } +slidecraft-av1 = { path = "crates/av1" } +slidecraft-isobmff = { path = "crates/isobmff" } +slidecraft-matroska = { path = "crates/matroska" } +slidecraft-ogg = { path = "crates/ogg" } +slidecraft-opus = { path = "crates/opus" } # foundation kurbo = { version = "0.13", features = ["serde"] } diff --git a/NOTICE b/NOTICE index 28813f3..483fbba 100644 --- a/NOTICE +++ b/NOTICE @@ -19,3 +19,11 @@ list (author, source, license) is in ATTRIBUTION.md. ATTRIBUTION.md (Inter, Carlito, Caladea, Liberation, Montserrat, Lato, Open Sans, Roboto, Merriweather, Playfair Display, Poppins, Nunito Sans, BIZ UD, Shippori Mincho, Noto Sans Arabic) under the SIL Open Font License 1.1; their licence texts ship with release packages. + +Code dependencies + +- symphonia (audio decoding for MP3, AAC, ALAC, FLAC, Vorbis, WAV, AIFF, CAF) is used unmodified + under the Mozilla Public License 2.0; its source is available at + https://github.com/pdeljanov/Symphonia. +- The H.264, HEVC, VP9, AV1, Opus, MP4, Matroska and Ogg crates under crates/ are ported from + FilmCraft (same owner, MIT OR Apache-2.0). diff --git a/ROADMAP.md b/ROADMAP.md index dcbd509..4cd7b03 100644 --- a/ROADMAP.md +++ b/ROADMAP.md @@ -26,6 +26,11 @@ format plus PPTX), scriptable by agents (CLI, JSON control channel, MCP), and av - Views: Normal (thumbnails with sections, slide, notes), Outline, Slide Sorter, Notes Page, Slide Master; zoom; grayscale. - Review: comments, accessibility checker, spelling. +- Audio and video: MP3, AAC/M4A, ALAC, WAV, AIFF, CAF, FLAC, Ogg Vorbis and Opus audio; H.264, + HEVC, VP9 and AV1 video in MP4/MOV/WebM/MKV (pure-Rust decoders); poster frames, trim, fades, + volume, loop, rewind, play across slides, hide during show, full screen; an in-place control bar + in the editor and `media.play/pause/stop/seek/info/posterFrame` for agents. WMA/WMV are + recognised and embedded but not yet playable. - PDF export: slides, notes pages and handouts (1–9 per page) with a selectable real-text layer, hyperlinks and slide bookmarks (`file.export {format: "pdf", layout}`, File › Export…). - UI: PowerPoint-style ribbon with contextual tabs, ~240 original icons, status bar, panes, command palette, light/dark. @@ -39,7 +44,7 @@ format plus PPTX), scriptable by agents (CLI, JSON control channel, MCP), and av **In progress:** PPTX import/export · first signed release run. -**Next (in order):** vector PDF artwork · audio/video playback · Format Shape pane depth · connectors with +**Next (in order):** vector PDF artwork · WMA/WMV decoding · Format Shape pane depth · connectors with glue, freeform and edit points · merge shapes · Animation Pane polish · native macOS menu bar · print · Notes/Handout masters · equations · SVG pictures. diff --git a/apps/slidecraft/Cargo.toml b/apps/slidecraft/Cargo.toml index cf518b1..e675bc6 100644 --- a/apps/slidecraft/Cargo.toml +++ b/apps/slidecraft/Cargo.toml @@ -13,6 +13,7 @@ workspace = true [dependencies] slidecraft-engine = { workspace = true } slidecraft-ui-egui = { workspace = true } +slidecraft-media = { workspace = true } eframe = { workspace = true, features = ["default_fonts", "wgpu", "accesskit", "wayland", "x11"] } egui = { workspace = true } serde_json = { workspace = true } @@ -26,6 +27,7 @@ winresource = "0.1" rfd = { workspace = true } arboard = { workspace = true, features = ["image-data"] } image = { workspace = true } +cpal = "0.16" [target.'cfg(all(unix, not(target_os = "macos")))'.dependencies] winit = { version = "0.30", default-features = false, features = ["x11"] } diff --git a/apps/slidecraft/src/audio.rs b/apps/slidecraft/src/audio.rs new file mode 100644 index 0000000..cbe9977 --- /dev/null +++ b/apps/slidecraft/src/audio.rs @@ -0,0 +1,49 @@ +//! cpal audio output for media playback: the device pulls mixed samples from the +//! `slidecraft_media::Player`, which makes it the playback clock video frames follow. + +use cpal::traits::{DeviceTrait, HostTrait, StreamTrait}; +use slidecraft_media::AudioOut; + +#[derive(Default)] +pub struct CpalOut { + stream: Option, +} + +impl AudioOut for CpalOut { + fn start(&mut self, mut fill: Box) -> Result<(), String> { + self.stop(); + let dev = cpal::default_host().default_output_device().ok_or("no audio output device")?; + let cfg = dev.default_output_config().map_err(|e| e.to_string())?; + let channels = cfg.channels() as usize; + let rate = cfg.sample_rate().0; + let config: cpal::StreamConfig = cfg.clone().into(); + let err = |e| log::warn!("audio stream error: {e}"); + let stream = match cfg.sample_format() { + cpal::SampleFormat::F32 => dev.build_output_stream(&config, move |buf: &mut [f32], _| fill(buf, channels, rate), err, None), + cpal::SampleFormat::I16 => { + let mut tmp = Vec::new(); + dev.build_output_stream( + &config, + move |buf: &mut [i16], _| { + tmp.resize(buf.len(), 0.0); + fill(&mut tmp, channels, rate); + for (o, s) in buf.iter_mut().zip(&tmp) { + *o = (s.clamp(-1.0, 1.0) * i16::MAX as f32) as i16; + } + }, + err, + None, + ) + } + other => return Err(format!("unsupported sample format {other:?}")), + } + .map_err(|e| e.to_string())?; + stream.play().map_err(|e| e.to_string())?; + self.stream = Some(stream); + Ok(()) + } + + fn stop(&mut self) { + self.stream = None; + } +} diff --git a/apps/slidecraft/src/main.rs b/apps/slidecraft/src/main.rs index 084a493..6627b18 100644 --- a/apps/slidecraft/src/main.rs +++ b/apps/slidecraft/src/main.rs @@ -9,6 +9,7 @@ #![deny(clippy::unwrap_used, clippy::expect_used, clippy::panic, clippy::unimplemented, clippy::todo, clippy::unreachable)] #![forbid(unsafe_code)] +mod audio; mod control_server; use slidecraft_engine::Session; @@ -84,8 +85,11 @@ fn services() -> Services { let d = rfd::FileDialog::new(); let d = match purpose { "picture" => d.add_filter("Pictures", &["png", "jpg", "jpeg", "gif", "webp", "bmp", "tif", "tiff"]), - "audio" => d.add_filter("Audio", &["wav", "mp3", "m4a", "aac", "flac", "ogg", "opus", "aif", "aiff"]), - "video" => d.add_filter("Video", &["mp4", "m4v", "mov", "webm"]), + "audio" => d.add_filter( + "Audio", + &["wav", "mp3", "m4a", "m4b", "aac", "flac", "ogg", "oga", "opus", "aif", "aiff", "aifc", "caf", "wma", "weba", "mka"], + ), + "video" => d.add_filter("Video", &["mp4", "m4v", "mov", "webm", "mkv", "wmv"]), _ => d .add_filter("Presentations", &["slidecraft", "pptx", "potx", "ppsx"]) .add_filter("SlideCraft", &["slidecraft"]) @@ -105,6 +109,10 @@ fn services() -> Services { rgba.write_to(&mut std::io::Cursor::new(&mut out), image::ImageFormat::Png).ok()?; Some(out) })), + // Media is mixed and clocked by the output device; `SLIDECRAFT_NO_AUDIO` plays silently. + audio_out: std::env::var_os("SLIDECRAFT_NO_AUDIO") + .is_none() + .then(|| Box::new(audio::CpalOut::default()) as Box), ..Default::default() } } diff --git a/crates/av1/Cargo.toml b/crates/av1/Cargo.toml new file mode 100644 index 0000000..c08e31d --- /dev/null +++ b/crates/av1/Cargo.toml @@ -0,0 +1,19 @@ +[package] +name = "slidecraft-av1" +description = "Clean-room AV1 decoder (AV1 Bitstream & Decoding Process Specification v1.0.0 with Errata 1)" +version.workspace = true +edition.workspace = true +license.workspace = true +rust-version.workspace = true + +[lints] +workspace = true + +[features] +default = ["threads"] +threads = ["dep:rayon"] + +[dependencies] +slidecraft-bitstream = { workspace = true } +thiserror = { workspace = true } +rayon = { workspace = true, optional = true } diff --git a/crates/av1/src/bits.rs b/crates/av1/src/bits.rs new file mode 100644 index 0000000..2bb54bb --- /dev/null +++ b/crates/av1/src/bits.rs @@ -0,0 +1,124 @@ +//! MSB-first bit reader for OBU headers and the uncompressed frame header (spec 4.10 descriptors). + +use crate::{Error, Result}; + +#[derive(Clone)] +pub(crate) struct BitReader<'a> { + data: &'a [u8], + /// Position in bits. + pos: usize, +} + +impl<'a> BitReader<'a> { + pub fn new(data: &'a [u8]) -> Self { + BitReader { data, pos: 0 } + } + + /// `get_position()`: bits consumed. + pub fn position(&self) -> usize { + self.pos + } + + pub fn byte_pos(&self) -> usize { + self.pos.div_ceil(8) + } + + #[inline] + pub fn bit(&mut self) -> Result { + let byte = *self.data.get(self.pos >> 3).ok_or(Error::Truncated)?; + let b = (byte >> (7 - (self.pos & 7))) & 1; + self.pos += 1; + Ok(b as u32) + } + + /// `f(n)`, n ≤ 32. + pub fn f(&mut self, n: u32) -> Result { + let mut x: u64 = 0; + for _ in 0..n { + x = (x << 1) | self.bit()? as u64; + } + Ok(x as u32) + } + + pub fn flag(&mut self) -> Result { + Ok(self.bit()? == 1) + } + + /// `uvlc()` + pub fn uvlc(&mut self) -> Result { + let mut leading = 0u32; + while self.bit()? == 0 { + leading += 1; + if leading > 40 { + return Err(Error::Invalid("uvlc")); + } + } + if leading >= 32 { + return Ok(u32::MAX); + } + let v = self.f(leading)?; + Ok(v + ((1u64 << leading) - 1) as u32) + } + + /// `le(n)` + pub fn le(&mut self, n: u32) -> Result { + let mut t = 0u32; + for i in 0..n { + t |= self.f(8)? << (8 * i); + } + Ok(t) + } + + /// `leb128()` + pub fn leb128(&mut self) -> Result { + let mut v = 0u64; + for i in 0..8 { + let b = self.f(8)? as u64; + v |= (b & 0x7f) << (7 * i); + if b & 0x80 == 0 { + break; + } + } + Ok(v) + } + + /// `su(n)` + pub fn su(&mut self, n: u32) -> Result { + let v = self.f(n)? as i64; + let sign = 1i64 << (n - 1); + Ok(if v & sign != 0 { (v - 2 * sign) as i32 } else { v as i32 }) + } + + /// `ns(n)` + pub fn ns(&mut self, n: u32) -> Result { + if n <= 1 { + return Ok(0); + } + let w = 32 - n.leading_zeros(); // FloorLog2(n) + 1 + let m = (1u32 << w) - n; + let v = self.f(w - 1)?; + if v < m { + return Ok(v); + } + let extra = self.bit()?; + Ok((v << 1) - m + extra) + } + + /// `byte_alignment()` + pub fn byte_align(&mut self) { + self.pos = self.pos.div_ceil(8) * 8; + } +} + +/// Read a leb128 from a byte slice; returns (value, bytes used). +pub(crate) fn leb128(data: &[u8]) -> Result<(u64, usize)> { + let mut v = 0u64; + for i in 0..8 { + let b = *data.get(i).ok_or(Error::Truncated)? as u64; + v |= (b & 0x7f) << (7 * i); + if b & 0x80 == 0 { + return Ok((v, i + 1)); + } + } + Ok((v, 8)) +} diff --git a/crates/av1/src/cdef.rs b/crates/av1/src/cdef.rs new file mode 100644 index 0000000..abae4bc --- /dev/null +++ b/crates/av1/src/cdef.rs @@ -0,0 +1,273 @@ +//! CDEF (7.15). + +use crate::frame::{FrameBuf, Plane}; +use crate::spec_tables::*; +use crate::state::FrameState; + +/// Run CDEF on `fs.cur`; returns the CdefFrame, or None when no block is filtered (CdefFrame +/// equals CurrFrame). +pub(crate) fn apply(fs: &FrameState) -> Option { + let fh = &fs.fh; + if fh.coded_lossless || fh.allow_intrabc || !fs.seq.enable_cdef { + return None; + } + let (mi_rows, mi_cols) = (fh.mi_rows as usize, fh.mi_cols as usize); + let mut out = fs.cur.clone(); + let mut any = false; + let mut r = 0; + while r < mi_rows { + let mut c = 0; + while c < mi_cols { + let idx = fs.cdef_idx(r & !15, c & !15); + if idx >= 0 && cdef_block(fs, &mut out, r, c, idx as usize) { + any = true; + } + c += 2; + } + r += 2; + } + any.then_some(out) +} + +fn cdef_block(fs: &FrameState, out: &mut FrameBuf, r: usize, c: usize, idx: usize) -> bool { + let mi = &fs.mi; + let sk = |rr: usize, cc: usize| -> bool { if rr < mi.rows && cc < mi.cols { mi.skip[mi.idx(rr, cc)] } else { true } }; + let skip = sk(r, c) && sk(r + 1, c) && sk(r, c + 1) && sk(r + 1, c + 1); + if skip { + return false; + } + let coeff_shift = fs.bit_depth - 8; + let (y_dir, var) = direction(fs, r, c); + let cp = &fs.fh.cdef; + let mut pri = (cp.y_pri[idx] << coeff_shift) as i32; + let sec = (cp.y_sec[idx] << coeff_shift) as i32; + let dir = if pri == 0 { 0 } else { y_dir }; + let var_str = if (var >> 6) != 0 { (31 - ((var >> 6) as u32).leading_zeros()).min(12) as i32 } else { 0 }; + pri = if var != 0 { (pri * (4 + var_str) + 8) >> 4 } else { 0 }; + let damping = cp.damping as i32 + coeff_shift as i32; + filter(fs, out, 0, r, c, pri, sec, damping, dir); + if fs.num_planes == 1 { + return true; + } + let pri = (cp.uv_pri[idx] << coeff_shift) as i32; + let sec = (cp.uv_sec[idx] << coeff_shift) as i32; + let dir = if pri == 0 { 0 } else { CDEF_UV_DIR[fs.ssx][fs.ssy][y_dir] as usize }; + let damping = cp.damping as i32 + coeff_shift as i32 - 1; + filter(fs, out, 1, r, c, pri, sec, damping, dir); + filter(fs, out, 2, r, c, pri, sec, damping, dir); + true +} + +fn direction(fs: &FrameState, r: usize, c: usize) -> (usize, i32) { + let mut cost = [0i32; 8]; + let mut partial = [[0i32; 15]; 8]; + let x0 = c << 2; + let y0 = r << 2; + let pl = &fs.cur.planes[0]; + let sh = fs.bit_depth - 8; + for i in 0..8 { + let row = pl.row(y0 + i); + for j in 0..8 { + let x = (row[x0 + j] as i32 >> sh) - 128; + partial[0][i + j] += x; + partial[1][i + j / 2] += x; + partial[2][i] += x; + partial[3][3 + i - j / 2] += x; + partial[4][7 + i - j] += x; + partial[5][3 - i / 2 + j] += x; + partial[6][j] += x; + partial[7][i / 2 + j] += x; + } + } + for i in 0..8 { + cost[2] += partial[2][i] * partial[2][i]; + cost[6] += partial[6][i] * partial[6][i]; + } + cost[2] *= DIV_TABLE[8] as i32; + cost[6] *= DIV_TABLE[8] as i32; + for i in 0..7 { + cost[0] += (partial[0][i] * partial[0][i] + partial[0][14 - i] * partial[0][14 - i]) * DIV_TABLE[i + 1] as i32; + cost[4] += (partial[4][i] * partial[4][i] + partial[4][14 - i] * partial[4][14 - i]) * DIV_TABLE[i + 1] as i32; + } + cost[0] += partial[0][7] * partial[0][7] * DIV_TABLE[8] as i32; + cost[4] += partial[4][7] * partial[4][7] * DIV_TABLE[8] as i32; + let mut i = 1; + while i < 8 { + for j in 0..5 { + cost[i] += partial[i][3 + j] * partial[i][3 + j]; + } + cost[i] *= DIV_TABLE[8] as i32; + for j in 0..3 { + cost[i] += (partial[i][j] * partial[i][j] + partial[i][10 - j] * partial[i][10 - j]) * DIV_TABLE[2 * j + 2] as i32; + } + i += 2; + } + let mut best = 0; + let mut y_dir = 0; + for (i, &c) in cost.iter().enumerate() { + if c > best { + best = c; + y_dir = i; + } + } + (y_dir, (best - cost[(y_dir + 4) & 7]) >> 10) +} + +/// Marks a sample outside the frame (CdefAvailable = 0) in the padded block. +const CDEF_NA: i32 = i32::MIN; +/// Padded block: 8x8 plus 2 samples on every side, row stride 12. +const PAD_STRIDE: usize = 12; + +#[inline(always)] +fn constrain(diff: i32, threshold: i32, adj: u32) -> i32 { + let mag = (threshold - (diff.abs() >> adj)).clamp(0, diff.abs()); + if diff < 0 { -mag } else { mag } +} + +#[allow(clippy::too_many_arguments)] +fn filter(fs: &FrameState, out: &mut FrameBuf, plane: usize, r: usize, c: usize, pri: i32, sec: i32, damping: i32, dir: usize) { + if pri == 0 && sec == 0 { + // Every tap contributes 0 and the clamp to [min, max] keeps x: CdefFrame = CurrFrame. + return; + } + let coeff_shift = fs.bit_depth - 8; + let (sub_x, sub_y) = if plane > 0 { (fs.ssx, fs.ssy) } else { (0, 0) }; + let x0 = (c * 4) >> sub_x; + let y0 = (r * 4) >> sub_y; + let w = 8 >> sub_x; + let h = 8 >> sub_y; + let src = &fs.cur.planes[plane]; + let (mi_rows, mi_cols) = (fs.fh.mi_rows as isize, fs.fh.mi_cols as isize); + // CdefAvailable per sample: its 4x4 block is inside the frame. + let mut buf = [CDEF_NA; PAD_STRIDE * PAD_STRIDE]; + let mut all_avail = true; + for i in 0..h + 4 { + let y = (y0 + i) as isize - 2; + let cand_r = (y << sub_y) >> 2; + if y < 0 || cand_r >= mi_rows { + all_avail = false; + continue; + } + let row = src.row(y as usize); + for j in 0..w + 4 { + let x = (x0 + j) as isize - 2; + let cand_c = (x << sub_x) >> 2; + if x >= 0 && cand_c < mi_cols { + buf[i * PAD_STRIDE + j] = row[x as usize] as i32; + } else { + all_avail = false; + } + } + } + let pri_tap = ((pri >> coeff_shift) & 1) as usize; + let off = |d: usize, k: usize| CDEF_DIRECTIONS[d][k][0] as isize * PAD_STRIDE as isize + CDEF_DIRECTIONS[d][k][1] as isize; + let pri_off = [off(dir, 0), off(dir, 1)]; + let d_lo = (dir + 6) & 7; + let d_hi = (dir + 2) & 7; + let sec_off = [[off(d_lo, 0), off(d_hi, 0)], [off(d_lo, 1), off(d_hi, 1)]]; + let pri_taps = [CDEF_PRI_TAPS[pri_tap][0] as i32, CDEF_PRI_TAPS[pri_tap][1] as i32]; + let sec_taps = [CDEF_SEC_TAPS[pri_tap][0] as i32, CDEF_SEC_TAPS[pri_tap][1] as i32]; + let adj = |t: i32| if t == 0 { 0 } else { (damping - (31 - (t as u32).leading_zeros()) as i32).max(0) as u32 }; + let (pri_adj, sec_adj) = (adj(pri), adj(sec)); + let dst = &mut out.planes[plane]; + if all_avail { + // Every tap is available: whole rows at a time (constrain() with threshold 0 is 0). + let mut taps = [(0isize, 0i32, 0i32, 0u32); 12]; + let mut n = 0; + for k in 0..2 { + for sign in [-1isize, 1] { + taps[n] = (sign * pri_off[k], pri_taps[k], pri, pri_adj); + taps[n + 1] = (sign * sec_off[k][0], sec_taps[k], sec, sec_adj); + taps[n + 2] = (sign * sec_off[k][1], sec_taps[k], sec, sec_adj); + n += 3; + } + } + let rows = (y0, x0, h); + match w { + 8 => filter_rows::<8>(&buf, &taps, dst, rows), + _ => filter_rows::<4>(&buf, &taps, dst, rows), + } + return; + } + for i in 0..h { + // The centre sample is read even where CdefAvailable is 0 (8x8 blocks straddling an odd + // MiRows / MiCols edge). + let srow = &src.row(y0 + i)[x0..x0 + w]; + let drow = &mut dst.row_mut(y0 + i)[x0..x0 + w]; + for j in 0..w { + let ci = ((i + 2) * PAD_STRIDE + j + 2) as isize; + let x = srow[j] as i32; + let mut sum = 0i32; + let mut max = x; + let mut min = x; + for k in 0..2 { + for sign in [-1isize, 1] { + let p = buf[(ci + sign * pri_off[k]) as usize]; + if p != CDEF_NA { + if pri != 0 { + sum += pri_taps[k] * constrain(p - x, pri, pri_adj); + } + max = max.max(p); + min = min.min(p); + } + for s in 0..2 { + let q = buf[(ci + sign * sec_off[k][s]) as usize]; + if q != CDEF_NA { + if sec != 0 { + sum += sec_taps[k] * constrain(q - x, sec, sec_adj); + } + max = max.max(q); + min = min.min(q); + } + } + } + } + drow[j] = (x + ((8 + sum - (sum < 0) as i32) >> 4)).clamp(min, max) as u16; + } + } +} + +/// CDEF of a block whose taps are all available, `W` samples per row. Samples (at most 12 +/// bits), differences and the weighted tap sum all fit in i16, which doubles the SIMD width. +#[inline(always)] +fn filter_rows( + buf: &[i32; PAD_STRIDE * PAD_STRIDE], + taps: &[(isize, i32, i32, u32); 12], + dst: &mut Plane, + (y0, x0, h): (usize, usize, usize), +) { + let mut b16 = [0i16; PAD_STRIDE * PAD_STRIDE]; + for (d, &s) in b16.iter_mut().zip(buf.iter()) { + *d = s as i16; + } + for i in 0..h { + let ci = ((i + 2) * PAD_STRIDE + 2) as isize; + let Some(&x) = b16.get(ci as usize..).and_then(|r| r.first_chunk::()) else { + return; + }; + let (mut sum, mut max, mut min) = ([0i16; W], x, x); + for &(off, weight, thr, adj) in taps { + let (weight, thr) = (weight as i16, thr as i16); + let start = (ci + off) as usize; + let Some(p) = b16.get(start..).and_then(|r| r.first_chunk::()) else { + return; + }; + for j in 0..W { + // constrain(): .max / .min instead of clamp (whose bound check panics), and + // the sign applied branch-free, so the loop vectorises. + let d = p[j] - x[j]; + let ad = d.abs(); + let mag = (thr - (ad >> adj)).max(0).min(ad); + let sign = d >> 15; + sum[j] += weight * ((mag ^ sign) - sign); + max[j] = max[j].max(p[j]); + min[j] = min[j].min(p[j]); + } + } + let drow = &mut dst.row_mut(y0 + i)[x0..x0 + W]; + for j in 0..W { + let s = sum[j]; + drow[j] = (x[j] + ((8 + s - (s < 0) as i16) >> 4)).max(min[j]).min(max[j]) as u16; + } + } +} diff --git a/crates/av1/src/cdf.rs b/crates/av1/src/cdf.rs new file mode 100644 index 0000000..a2d5e76 --- /dev/null +++ b/crates/av1/src/cdf.rs @@ -0,0 +1,359 @@ +//! The adaptive CDF arrays (spec 6.8.2 init_non_coeff_cdfs / init_coeff_cdfs, load_cdfs). + +use crate::spec_tables::*; + +/// Every CDF array listed by init_non_coeff_cdfs and init_coeff_cdfs. Each innermost array holds +/// N cumulative values (the last is 32768) plus the adaptation counter. +#[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 intrabc: [u16; 3], + 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 partition_w128: [[u16; 9]; 4], + pub segment_id: [[u16; 9]; 3], + pub segment_id_predicted: [[u16; 3]; 3], + pub tx_8x8: [[u16; 3]; 3], + pub tx_16x16: [[u16; 4]; 3], + pub tx_32x32: [[u16; 4]; 3], + pub tx_64x64: [[u16; 4]; 3], + pub txfm_split: [[u16; 3]; 21], + pub filter_intra_mode: [u16; 6], + pub filter_intra: [[u16; 3]; 22], + pub interp_filter: [[u16; 4]; 16], + pub motion_mode: [[u16; 4]; 22], + pub new_mv: [[u16; 3]; 6], + pub zero_mv: [[u16; 3]; 2], + pub ref_mv: [[u16; 3]; 6], + pub compound_mode: [[u16; 9]; 8], + pub drl_mode: [[u16; 3]; 3], + pub is_inter: [[u16; 3]; 4], + pub comp_mode: [[u16; 3]; 5], + pub skip_mode: [[u16; 3]; 3], + pub skip: [[u16; 3]; 3], + pub comp_ref: [[[u16; 3]; 3]; 3], + pub comp_bwd_ref: [[[u16; 3]; 2]; 3], + pub single_ref: [[[u16; 3]; 6]; 3], + pub mv_joint: [[u16; 5]; MV_CONTEXTS], + pub mv_class: [[[u16; 12]; 2]; MV_CONTEXTS], + pub mv_class0_bit: [[[u16; 3]; 2]; MV_CONTEXTS], + pub mv_fr: [[[u16; 5]; 2]; MV_CONTEXTS], + pub mv_class0_fr: [[[[u16; 5]; 2]; 2]; MV_CONTEXTS], + pub mv_class0_hp: [[[u16; 3]; 2]; MV_CONTEXTS], + pub mv_sign: [[[u16; 3]; 2]; MV_CONTEXTS], + pub mv_bit: [[[[u16; 3]; 10]; 2]; MV_CONTEXTS], + pub mv_hp: [[[u16; 3]; 2]; MV_CONTEXTS], + pub palette_y_mode: [[[u16; 3]; 3]; 7], + pub palette_uv_mode: [[u16; 3]; 2], + pub palette_y_size: [[u16; 8]; 7], + pub palette_uv_size: [[u16; 8]; 7], + pub palette_2_y_color: [[u16; 3]; 5], + pub palette_2_uv_color: [[u16; 3]; 5], + pub palette_3_y_color: [[u16; 4]; 5], + pub palette_3_uv_color: [[u16; 4]; 5], + pub palette_4_y_color: [[u16; 5]; 5], + pub palette_4_uv_color: [[u16; 5]; 5], + pub palette_5_y_color: [[u16; 6]; 5], + pub palette_5_uv_color: [[u16; 6]; 5], + pub palette_6_y_color: [[u16; 7]; 5], + pub palette_6_uv_color: [[u16; 7]; 5], + pub palette_7_y_color: [[u16; 8]; 5], + pub palette_7_uv_color: [[u16; 8]; 5], + pub palette_8_y_color: [[u16; 9]; 5], + pub palette_8_uv_color: [[u16; 9]; 5], + pub delta_q: [u16; 5], + pub delta_lf: [u16; 5], + pub delta_lf_multi: [[u16; 5]; FRAME_LF_COUNT], + pub intra_tx_type_set1: [[[u16; 8]; 13]; 2], + pub intra_tx_type_set2: [[[u16; 6]; 13]; 3], + pub inter_tx_type_set1: [[u16; 17]; 2], + pub inter_tx_type_set2: [u16; 13], + pub inter_tx_type_set3: [[u16; 3]; 4], + pub use_obmc: [[u16; 3]; 22], + pub inter_intra: [[u16; 3]; 3], + pub comp_ref_type: [[u16; 3]; 5], + pub cfl_sign: [u16; 9], + pub uni_comp_ref: [[[u16; 3]; 3]; 3], + pub wedge_inter_intra: [[u16; 3]; 22], + pub comp_group_idx: [[u16; 3]; 6], + pub compound_idx: [[u16; 3]; 6], + pub compound_type: [[u16; 3]; 22], + pub inter_intra_mode: [[u16; 5]; 3], + pub wedge_index: [[u16; 17]; 22], + pub cfl_alpha: [[u16; 17]; 6], + pub use_wiener: [u16; 3], + pub use_sgrproj: [u16; 3], + pub restoration_type: [u16; 4], + // coefficient CDFs + 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 mut c = 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, + intrabc: DEFAULT_INTRABC_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, + partition_w128: DEFAULT_PARTITION_W128_CDF, + segment_id: DEFAULT_SEGMENT_ID_CDF, + segment_id_predicted: DEFAULT_SEGMENT_ID_PREDICTED_CDF, + tx_8x8: DEFAULT_TX_8X8_CDF, + tx_16x16: DEFAULT_TX_16X16_CDF, + tx_32x32: DEFAULT_TX_32X32_CDF, + tx_64x64: DEFAULT_TX_64X64_CDF, + txfm_split: DEFAULT_TXFM_SPLIT_CDF, + filter_intra_mode: DEFAULT_FILTER_INTRA_MODE_CDF, + filter_intra: DEFAULT_FILTER_INTRA_CDF, + interp_filter: DEFAULT_INTERP_FILTER_CDF, + motion_mode: DEFAULT_MOTION_MODE_CDF, + new_mv: DEFAULT_NEW_MV_CDF, + zero_mv: DEFAULT_ZERO_MV_CDF, + ref_mv: DEFAULT_REF_MV_CDF, + compound_mode: DEFAULT_COMPOUND_MODE_CDF, + drl_mode: DEFAULT_DRL_MODE_CDF, + is_inter: DEFAULT_IS_INTER_CDF, + comp_mode: DEFAULT_COMP_MODE_CDF, + skip_mode: DEFAULT_SKIP_MODE_CDF, + skip: DEFAULT_SKIP_CDF, + comp_ref: DEFAULT_COMP_REF_CDF, + comp_bwd_ref: DEFAULT_COMP_BWD_REF_CDF, + single_ref: DEFAULT_SINGLE_REF_CDF, + mv_joint: [DEFAULT_MV_JOINT_CDF; MV_CONTEXTS], + mv_class: [DEFAULT_MV_CLASS_CDF; MV_CONTEXTS], + mv_class0_bit: [[DEFAULT_MV_CLASS0_BIT_CDF; 2]; MV_CONTEXTS], + mv_fr: [DEFAULT_MV_FR_CDF; MV_CONTEXTS], + mv_class0_fr: [DEFAULT_MV_CLASS0_FR_CDF; MV_CONTEXTS], + mv_class0_hp: [[DEFAULT_MV_CLASS0_HP_CDF; 2]; MV_CONTEXTS], + mv_sign: [[DEFAULT_MV_SIGN_CDF; 2]; MV_CONTEXTS], + mv_bit: [[DEFAULT_MV_BIT_CDF; 2]; MV_CONTEXTS], + mv_hp: [[DEFAULT_MV_HP_CDF; 2]; MV_CONTEXTS], + palette_y_mode: DEFAULT_PALETTE_Y_MODE_CDF, + palette_uv_mode: DEFAULT_PALETTE_UV_MODE_CDF, + palette_y_size: DEFAULT_PALETTE_Y_SIZE_CDF, + palette_uv_size: DEFAULT_PALETTE_UV_SIZE_CDF, + palette_2_y_color: DEFAULT_PALETTE_SIZE_2_Y_COLOR_CDF, + palette_2_uv_color: DEFAULT_PALETTE_SIZE_2_UV_COLOR_CDF, + palette_3_y_color: DEFAULT_PALETTE_SIZE_3_Y_COLOR_CDF, + palette_3_uv_color: DEFAULT_PALETTE_SIZE_3_UV_COLOR_CDF, + palette_4_y_color: DEFAULT_PALETTE_SIZE_4_Y_COLOR_CDF, + palette_4_uv_color: DEFAULT_PALETTE_SIZE_4_UV_COLOR_CDF, + palette_5_y_color: DEFAULT_PALETTE_SIZE_5_Y_COLOR_CDF, + palette_5_uv_color: DEFAULT_PALETTE_SIZE_5_UV_COLOR_CDF, + palette_6_y_color: DEFAULT_PALETTE_SIZE_6_Y_COLOR_CDF, + palette_6_uv_color: DEFAULT_PALETTE_SIZE_6_UV_COLOR_CDF, + palette_7_y_color: DEFAULT_PALETTE_SIZE_7_Y_COLOR_CDF, + palette_7_uv_color: DEFAULT_PALETTE_SIZE_7_UV_COLOR_CDF, + palette_8_y_color: DEFAULT_PALETTE_SIZE_8_Y_COLOR_CDF, + palette_8_uv_color: DEFAULT_PALETTE_SIZE_8_UV_COLOR_CDF, + delta_q: DEFAULT_DELTA_Q_CDF, + delta_lf: DEFAULT_DELTA_LF_CDF, + delta_lf_multi: [DEFAULT_DELTA_LF_CDF; FRAME_LF_COUNT], + intra_tx_type_set1: DEFAULT_INTRA_TX_TYPE_SET1_CDF, + intra_tx_type_set2: DEFAULT_INTRA_TX_TYPE_SET2_CDF, + inter_tx_type_set1: DEFAULT_INTER_TX_TYPE_SET1_CDF, + inter_tx_type_set2: DEFAULT_INTER_TX_TYPE_SET2_CDF, + inter_tx_type_set3: DEFAULT_INTER_TX_TYPE_SET3_CDF, + use_obmc: DEFAULT_USE_OBMC_CDF, + inter_intra: DEFAULT_INTER_INTRA_CDF, + comp_ref_type: DEFAULT_COMP_REF_TYPE_CDF, + cfl_sign: DEFAULT_CFL_SIGN_CDF, + uni_comp_ref: DEFAULT_UNI_COMP_REF_CDF, + wedge_inter_intra: DEFAULT_WEDGE_INTER_INTRA_CDF, + comp_group_idx: DEFAULT_COMP_GROUP_IDX_CDF, + compound_idx: DEFAULT_COMPOUND_IDX_CDF, + compound_type: DEFAULT_COMPOUND_TYPE_CDF, + inter_intra_mode: DEFAULT_INTER_INTRA_MODE_CDF, + wedge_index: DEFAULT_WEDGE_INDEX_CDF, + cfl_alpha: DEFAULT_CFL_ALPHA_CDF, + use_wiener: DEFAULT_USE_WIENER_CDF, + use_sgrproj: DEFAULT_USE_SGRPROJ_CDF, + restoration_type: DEFAULT_RESTORATION_TYPE_CDF, + txb_skip: DEFAULT_TXB_SKIP_CDF[0], + eob_pt_16: DEFAULT_EOB_PT_16_CDF[0], + eob_pt_32: DEFAULT_EOB_PT_32_CDF[0], + eob_pt_64: DEFAULT_EOB_PT_64_CDF[0], + eob_pt_128: DEFAULT_EOB_PT_128_CDF[0], + eob_pt_256: DEFAULT_EOB_PT_256_CDF[0], + eob_pt_512: DEFAULT_EOB_PT_512_CDF[0], + eob_pt_1024: DEFAULT_EOB_PT_1024_CDF[0], + eob_extra: DEFAULT_EOB_EXTRA_CDF[0], + dc_sign: DEFAULT_DC_SIGN_CDF[0], + coeff_base_eob: DEFAULT_COEFF_BASE_EOB_CDF[0], + coeff_base: DEFAULT_COEFF_BASE_CDF[0], + coeff_br: DEFAULT_COEFF_BR_CDF[0], + }); + c.init_coeff(base_q_idx); + c + } + + /// init_coeff_cdfs( ) + pub fn init_coeff(&mut self, base_q_idx: u32) { + let idx = match base_q_idx { + 0..=20 => 0, + 21..=60 => 1, + 61..=120 => 2, + _ => 3, + }; + self.txb_skip = DEFAULT_TXB_SKIP_CDF[idx]; + self.eob_pt_16 = DEFAULT_EOB_PT_16_CDF[idx]; + self.eob_pt_32 = DEFAULT_EOB_PT_32_CDF[idx]; + self.eob_pt_64 = DEFAULT_EOB_PT_64_CDF[idx]; + self.eob_pt_128 = DEFAULT_EOB_PT_128_CDF[idx]; + self.eob_pt_256 = DEFAULT_EOB_PT_256_CDF[idx]; + self.eob_pt_512 = DEFAULT_EOB_PT_512_CDF[idx]; + self.eob_pt_1024 = DEFAULT_EOB_PT_1024_CDF[idx]; + self.eob_extra = DEFAULT_EOB_EXTRA_CDF[idx]; + self.dc_sign = DEFAULT_DC_SIGN_CDF[idx]; + self.coeff_base_eob = DEFAULT_COEFF_BASE_EOB_CDF[idx]; + self.coeff_base = DEFAULT_COEFF_BASE_CDF[idx]; + self.coeff_br = DEFAULT_COEFF_BR_CDF[idx]; + } + + /// 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, + intrabc, + partition_w8, + partition_w16, + partition_w32, + partition_w64, + partition_w128, + segment_id, + segment_id_predicted, + tx_8x8, + tx_16x16, + tx_32x32, + tx_64x64, + txfm_split, + filter_intra_mode, + filter_intra, + interp_filter, + motion_mode, + new_mv, + zero_mv, + ref_mv, + compound_mode, + drl_mode, + is_inter, + comp_mode, + skip_mode, + skip, + comp_ref, + comp_bwd_ref, + single_ref, + mv_joint, + mv_class, + mv_class0_bit, + mv_fr, + mv_class0_fr, + mv_class0_hp, + mv_sign, + mv_bit, + mv_hp, + palette_y_mode, + palette_uv_mode, + palette_y_size, + palette_uv_size, + palette_2_y_color, + palette_2_uv_color, + palette_3_y_color, + palette_3_uv_color, + palette_4_y_color, + palette_4_uv_color, + palette_5_y_color, + palette_5_uv_color, + palette_6_y_color, + palette_6_uv_color, + palette_7_y_color, + palette_7_uv_color, + palette_8_y_color, + palette_8_uv_color, + delta_q, + delta_lf, + delta_lf_multi, + intra_tx_type_set1, + intra_tx_type_set2, + inter_tx_type_set1, + inter_tx_type_set2, + inter_tx_type_set3, + use_obmc, + inter_intra, + comp_ref_type, + cfl_sign, + uni_comp_ref, + wedge_inter_intra, + comp_group_idx, + compound_idx, + compound_type, + inter_intra_mode, + wedge_index, + cfl_alpha, + use_wiener, + use_sgrproj, + restoration_type, + 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/av1/src/decoder.rs b/crates/av1/src/decoder.rs new file mode 100644 index 0000000..66c9669 --- /dev/null +++ b/crates/av1/src/decoder.rs @@ -0,0 +1,949 @@ +//! Top-level decoder: OBU parsing (5.3), frame / tile group handling, reference frame update +//! (7.20), show-existing-frame and output (7.18). +//! +//! Threading: headers are parsed in order on the calling thread; each frame then becomes a job +//! that waits for the reference frames it uses and decodes on a frame worker thread, so frames +//! that do not depend on each other decode concurrently. Within a frame, tiles and the +//! post-filter row bands run on a shared worker pool. Every result depends only on finished +//! reference frames, so the output is identical whatever the scheduling. Shown pictures are +//! returned in order; with frame threads they can come out of a later `decode` call (or +//! `flush`), each carrying the `pts` of the temporal unit that showed it. + +use std::collections::VecDeque; +use std::sync::{Arc, Condvar, Mutex}; + +use crate::bits::{BitReader, leb128}; +use crate::cdf::Cdfs; +use crate::frame::FrameBuf; +use crate::header::{FilmGrainParams, FrameHeader, HeaderState, LoopFilterDeltas, RefInfo, SegmentationFeatures, SequenceHeader, default_gm_params}; +use crate::par::Pool; +use crate::spec_tables::*; +use crate::state::{FrameShared, FrameState, TileState}; +use crate::stats::{DecodeStats, Stage, Timer}; +use crate::tile::TileDecoder; +use crate::{Error, Picture, Result}; + +const OBU_SEQUENCE_HEADER_T: u32 = 1; +const OBU_TEMPORAL_DELIMITER_T: u32 = 2; +const OBU_FRAME_HEADER_T: u32 = 3; +const OBU_TILE_GROUP_T: u32 = 4; +const OBU_METADATA_T: u32 = 5; +const OBU_FRAME_T: u32 = 6; +const OBU_REDUNDANT_FRAME_HEADER_T: u32 = 7; +const OBU_TILE_LIST_T: u32 = 8; + +/// A stored reference frame (FrameStore plus the saved per-frame state). +pub(crate) struct RefFrame { + pub buf: FrameBuf, + pub cdfs: Box, + pub bit_depth: u8, + pub subsampling_x: u8, + pub subsampling_y: u8, + pub segment_ids: Vec, + pub mi_cols: usize, + pub mi_rows: usize, + pub film_grain_present: bool, + pub color: (u8, u8, u8, bool), + /// SavedRefFrames / SavedMvs (MfRefFrames / MfMvs of 7.19), mi units. + pub saved_ref_frames: Vec, + pub saved_mvs: Vec<[i32; 2]>, +} + +/// A value produced by a decoding job, waited for by later jobs and by the output. +pub(crate) struct Slot { + value: Mutex>>, + cv: Condvar, +} + +impl Slot { + fn new() -> Arc> { + Arc::new(Slot { value: Mutex::new(None), cv: Condvar::new() }) + } + + fn set(&self, v: Result) { + let mut g = self.value.lock().unwrap_or_else(|e| e.into_inner()); + *g = Some(v); + self.cv.notify_all(); + } + + fn is_set(&self) -> bool { + self.value.lock().map(|g| g.is_some()).unwrap_or(true) + } + + fn wait(&self) -> Result { + let mut g = self.value.lock().unwrap_or_else(|e| e.into_inner()); + loop { + if let Some(v) = g.as_ref() { + return v.clone(); + } + g = self.cv.wait(g).unwrap_or_else(|e| e.into_inner()); + } + } +} + +type FrameSlot = Slot>; + +/// Everything needed to decode one frame, captured when its header is parsed. +struct FrameJob { + seq: SequenceHeader, + fh: FrameHeader, + /// Reference slots at the start of the frame (only the used ones are waited for). + refs: [Option>; NUM_REF_FRAMES], + ref_info: [RefInfo; NUM_REF_FRAMES], + /// Tile payloads (tile number, data). + tiles: Vec<(usize, Vec)>, + slot: Arc, + /// The output picture, when the frame is shown. + picture: Option>>>, + pts: i64, + apply_film_grain: bool, + /// Draft mode: the frame refreshes no reference slot, so its in-loop filters are skipped. + draft: bool, +} + +/// A shown picture in output order. +#[allow(clippy::large_enum_variant)] +enum Output { + Decoded(Arc>>), + /// show_existing_frame: built from the reference slot when it is finished. + Existing { + slot: Arc, + seq: SequenceHeader, + width: usize, + height: usize, + grain: FilmGrainParams, + apply_film_grain: bool, + pts: i64, + }, +} + +impl Output { + fn is_ready(&self) -> bool { + match self { + Output::Decoded(s) => s.is_set(), + Output::Existing { slot, .. } => slot.is_set(), + } + } + + fn take(self) -> Result { + match self { + Output::Decoded(s) => s.wait().map(|p| Arc::try_unwrap(p).unwrap_or_else(|p| (*p).clone())), + Output::Existing { slot, seq, width, height, grain, apply_film_grain, pts } => { + let rf = slot.wait()?; + Ok(output_picture(&seq, &rf, width, height, &grain, apply_film_grain, pts)) + } + } + } +} + +/// Frame worker threads taking jobs in submission order. +#[cfg(all(feature = "threads", not(target_arch = "wasm32")))] +struct Workers { + tx: Option>>, + handles: Vec>, +} + +#[cfg(all(feature = "threads", not(target_arch = "wasm32")))] +impl Workers { + fn new(n: usize, pool: &Pool, stats: &Arc>) -> Option { + let (tx, rx) = std::sync::mpsc::channel::>(); + let rx = Arc::new(Mutex::new(rx)); + let mut handles = Vec::new(); + for i in 0..n { + let (rx, pool, stats) = (rx.clone(), pool.clone(), stats.clone()); + let h = std::thread::Builder::new() + .name(format!("av1-frame-{i}")) + .spawn(move || { + loop { + let job = match rx.lock() { + Ok(r) => r.recv(), + Err(_) => break, + }; + match job { + Ok(job) => run_job(*job, &pool, &stats), + Err(_) => break, + } + } + }) + .ok()?; + handles.push(h); + } + Some(Workers { tx: Some(tx), handles }) + } +} + +#[cfg(all(feature = "threads", not(target_arch = "wasm32")))] +impl Drop for Workers { + fn drop(&mut self) { + self.tx.take(); + for h in self.handles.drain(..) { + let _ = h.join(); + } + } +} + +/// An AV1 decoder. Feed it temporal units (one MP4 / Matroska sample each, or any run of +/// OBUs in the low-overhead format) and collect the shown frames. +pub struct Decoder { + seq: Option, + refs: [RefInfo; NUM_REF_FRAMES], + ref_frames: [Option>; NUM_REF_FRAMES], + lf_deltas: LoopFilterDeltas, + seg_features: SegmentationFeatures, + prev_gm_params: [[i32; 6]; 8], + current_frame_id: u32, + /// Frame being collected (between its header and its last tile group). + pending: Option>, + seen_frame_header: bool, + /// Apply film grain synthesis to output frames (default true). + pub apply_film_grain: bool, + draft: bool, + stats: Arc>, + threads: usize, + pool: Pool, + #[cfg(all(feature = "threads", not(target_arch = "wasm32")))] + workers: Option, + /// Frames submitted to the workers and not known to be finished (oldest first). + in_flight: VecDeque>, + max_in_flight: usize, + /// Shown pictures not yet returned, in output order. + out: VecDeque, + /// pts of the temporal unit being decoded. + pts: i64, +} + +fn default_threads() -> usize { + #[cfg(all(feature = "threads", not(target_arch = "wasm32")))] + { + std::thread::available_parallelism().map(|n| n.get()).unwrap_or(1).min(16) + } + #[cfg(not(all(feature = "threads", not(target_arch = "wasm32"))))] + { + 1 + } +} + +/// Frames decoded concurrently with `threads` threads (each in-flight 1080p frame holds a few +/// tens of MB of state). +fn frame_threads(threads: usize) -> usize { + if threads <= 1 { 1 } else { threads.div_ceil(2).clamp(2, 8) } +} + +impl Default for Decoder { + fn default() -> Self { + Self::new() + } +} + +impl Decoder { + /// A decoder using all available cores (with the `threads` feature). + pub fn new() -> Decoder { + Decoder::with_threads(default_threads()) + } + + /// A decoder using up to `threads` worker threads (1 = decode on the calling thread). + pub fn with_threads(threads: usize) -> Decoder { + let threads = threads.max(1); + let pool = Pool::new(threads); + let stats = Arc::new(Mutex::new(DecodeStats::default())); + let fthreads = if pool.is_parallel() { frame_threads(threads) } else { 1 }; + #[cfg(all(feature = "threads", not(target_arch = "wasm32")))] + let workers = if fthreads > 1 { Workers::new(fthreads, &pool, &stats) } else { None }; + Decoder { + seq: None, + refs: Default::default(), + ref_frames: Default::default(), + lf_deltas: LoopFilterDeltas::defaults(), + seg_features: SegmentationFeatures::default(), + prev_gm_params: default_gm_params(), + current_frame_id: 0, + pending: None, + seen_frame_header: false, + apply_film_grain: true, + draft: false, + stats, + threads, + pool, + #[cfg(all(feature = "threads", not(target_arch = "wasm32")))] + workers, + in_flight: VecDeque::new(), + max_in_flight: fthreads + 2, + out: VecDeque::new(), + pts: 0, + } + } + + /// Frame / tile counts and per-stage busy time of the frames finished so far. + /// Draft mode for reduced-resolution playback (off by default): shown frames that refresh + /// no reference slot (nothing can predict from them, read their motion vectors or CDFs) + /// skip deblocking, CDEF and loop restoration and come out flagged [`Picture::draft`]; + /// every other picture is unchanged. + pub fn set_draft(&mut self, on: bool) { + self.draft = on; + } + + pub fn stats(&self) -> DecodeStats { + self.stats.lock().map(|s| *s).unwrap_or_default() + } + + /// The active sequence header, once one has been decoded. + pub fn sequence_header(&self) -> Option<&SequenceHeader> { + self.seq.as_ref() + } + + /// Decode a chunk of OBUs; returns the frames that are ready for output, in order (with + /// frame threads, pictures shown by this chunk may come from a later call or [`flush`]). + /// + /// [`flush`]: Decoder::flush + pub fn decode(&mut self, data: &[u8]) -> Result> { + self.decode_pts(data, 0) + } + + /// [`Decoder::decode`] with a timestamp that the pictures shown by this chunk carry. + pub fn decode_pts(&mut self, data: &[u8], pts: i64) -> Result> { + self.pts = pts; + let parsed = self.parse(data); + // Hand out what is finished, then report a parse error. + let mut pics = Vec::new(); + let max_queue = 2 * self.max_in_flight; + while let Some(o) = self.out.front() { + if !o.is_ready() && self.out.len() <= max_queue { + break; + } + let Some(o) = self.out.pop_front() else { break }; + pics.push(o.take()?); + } + parsed?; + Ok(pics) + } + + /// Output every picture still held by the decoder (end of stream / before a seek); the + /// first decoding error is returned instead. + pub fn flush(&mut self) -> Vec { + self.flush_result().unwrap_or_default() + } + + /// [`Decoder::flush`] reporting decoding errors. + pub fn flush_result(&mut self) -> Result> { + let mut pics = Vec::new(); + let mut err = None; + while let Some(o) = self.out.pop_front() { + match o.take() { + Ok(p) => pics.push(p), + Err(e) => { + err.get_or_insert(e); + } + } + } + while let Some(s) = self.in_flight.pop_front() { + let _ = s.wait(); + } + match err { + Some(e) => Err(e), + None => Ok(pics), + } + } + + fn parse(&mut self, data: &[u8]) -> Result<()> { + // (output, temporal unit, spatial_id) + let mut out: Vec<(Output, usize, u32)> = Vec::new(); + let r = self.parse_obus(data, &mut out); + // Output policy (7.18.1 note): with scalability, show one frame per temporal unit, the + // highest spatial layer present. + let scalable = self.seq.as_ref().is_some_and(|s| s.op_idc != 0); + if !scalable { + self.out.extend(out.into_iter().map(|(o, _, _)| o)); + } else { + let mut last: Option<(usize, u32)> = None; + let mut sel: Vec = Vec::new(); + for (o, t, sid) in out { + match last { + Some((lt, lsid)) if lt == t => { + if sid >= lsid { + if let Some(l) = sel.last_mut() { + *l = o; + } + last = Some((t, sid)); + } + } + _ => { + sel.push(o); + last = Some((t, sid)); + } + } + } + self.out.extend(sel); + } + r + } + + fn parse_obus(&mut self, data: &[u8], out: &mut Vec<(Output, usize, u32)>) -> Result<()> { + let mut tu = 0usize; + let mut pos = 0; + while pos < data.len() { + let h = data[pos]; + if h & 0x80 != 0 { + return Err(Error::Invalid("obu_forbidden_bit")); + } + let obu_type = ((h >> 3) & 0xf) as u32; + let ext = h & 4 != 0; + let has_size = h & 2 != 0; + let mut p = pos + 1; + let (mut temporal_id, mut spatial_id) = (0, 0); + if ext { + let e = *data.get(p).ok_or(Error::Truncated)?; + temporal_id = (e >> 5) as u32; + spatial_id = ((e >> 3) & 3) as u32; + p += 1; + } + let size = if has_size { + let (v, n) = leb128(data.get(p..).ok_or(Error::Truncated)?)?; + p += n; + v as usize + } else { + data.len().checked_sub(p).ok_or(Error::Truncated)? + }; + let end = p.checked_add(size).filter(|&e| e <= data.len()).ok_or(Error::Truncated)?; + let payload = &data[p..end]; + pos = end; + if obu_type != OBU_SEQUENCE_HEADER_T + && obu_type != OBU_TEMPORAL_DELIMITER_T + && ext + && let Some(seq) = &self.seq + && seq.op_idc != 0 + { + let idc = seq.op_idc; + let in_t = (idc >> temporal_id) & 1; + let in_s = (idc >> (spatial_id + 8)) & 1; + if in_t == 0 || in_s == 0 { + continue; + } + } + match obu_type { + OBU_SEQUENCE_HEADER_T => { + let s = SequenceHeader::parse(payload)?; + self.seq = Some(s); + } + OBU_TEMPORAL_DELIMITER_T => { + self.seen_frame_header = false; + tu += 1; + } + OBU_FRAME_HEADER_T | OBU_REDUNDANT_FRAME_HEADER_T | OBU_FRAME_T => { + if self.seen_frame_header { + // frame_header_copy(): identical to the active header + if obu_type != OBU_FRAME_T { + continue; + } + } + let mut r = BitReader::new(payload); + let shown = self.frame_header(&mut r, temporal_id, spatial_id)?; + if let Some(o) = shown { + out.push((o, tu, spatial_id)); + continue; + } + if obu_type == OBU_FRAME_T { + r.byte_align(); + let rest = payload.get(r.byte_pos()..).ok_or(Error::Truncated)?; + if let Some(o) = self.tile_group(rest)? { + out.push((o, tu, spatial_id)); + } + } + } + OBU_TILE_GROUP_T => { + if let Some(o) = self.tile_group(payload)? { + out.push((o, tu, spatial_id)); + } + } + OBU_METADATA_T | OBU_TILE_LIST_T => {} + _ => {} + } + } + Ok(()) + } + + /// frame_header_obu( ): returns the output for show_existing_frame. + fn frame_header(&mut self, r: &mut BitReader, temporal_id: u32, spatial_id: u32) -> Result> { + let seq = self.seq.clone().ok_or(Error::Invalid("frame before sequence header"))?; + self.seen_frame_header = true; + let mut st = HeaderState { + seq: &seq, + refs: &mut self.refs, + lf_deltas: self.lf_deltas, + seg_features: self.seg_features, + prev_gm_params: self.prev_gm_params, + current_frame_id: self.current_frame_id, + }; + let fh = FrameHeader::parse(r, &mut st, temporal_id, spatial_id)?; + self.lf_deltas = st.lf_deltas; + self.seg_features = st.seg_features; + self.prev_gm_params = st.prev_gm_params; + self.current_frame_id = st.current_frame_id; + if fh.show_existing_frame { + self.seen_frame_header = false; + let idx = fh.frame_to_show_map_idx; + let slot = self.ref_frames[idx].clone().ok_or(Error::Invalid("show_existing_frame of an empty slot"))?; + let info = self.refs[idx].clone(); + let o = Output::Existing { + slot: slot.clone(), + seq: seq.clone(), + width: info.upscaled_width as usize, + height: info.frame_height as usize, + grain: fh.film_grain.clone(), + apply_film_grain: self.apply_film_grain, + pts: self.pts, + }; + if fh.frame_type == KEY_FRAME as u8 { + // reference frame loading process (7.21) then refresh every slot (7.20) + self.lf_deltas = info.lf_deltas; + self.seg_features = info.seg_features; + for i in 0..NUM_REF_FRAMES { + self.refs[i] = info.clone(); + self.ref_frames[i] = Some(slot.clone()); + } + } + return Ok(Some(o)); + } + // Encoders exist that code frames slightly larger than the sequence maximum (e.g. a + // 270-line stream coded as 272 lines); like other decoders we accept them. + if fh.upscaled_width > 65536 || fh.frame_height > 65536 { + return Err(Error::Invalid("frame size")); + } + if !fh.frame_is_intra { + for i in 0..REFS_PER_FRAME { + if self.ref_frames[fh.ref_frame_idx[i]].is_none() { + return Err(Error::Invalid("missing reference frame")); + } + } + } + if fh.primary_ref_frame != PRIMARY_REF_NONE && self.ref_frames[fh.ref_frame_idx[fh.primary_ref_frame]].is_none() { + return Err(Error::Invalid("primary reference frame missing")); + } + let picture = fh.show_frame.then(Slot::new); + let draft = self.draft && fh.show_frame && fh.refresh_frame_flags == 0; + self.pending = Some(Box::new(FrameJob { + seq, + fh, + refs: self.ref_frames.clone(), + ref_info: self.refs.clone(), + tiles: Vec::new(), + slot: Slot::new(), + picture, + pts: self.pts, + apply_film_grain: self.apply_film_grain, + draft, + })); + Ok(None) + } + + /// tile_group_obu( sz ): returns the output when the frame is complete and shown. + fn tile_group(&mut self, data: &[u8]) -> Result> { + let mut job = self.pending.take().ok_or(Error::Invalid("tile group without a frame header"))?; + let (cols, rows, cols_log2, rows_log2, tsb) = { + let t = &job.fh.tile_info; + (t.cols, t.rows, t.cols_log2, t.rows_log2, t.tile_size_bytes) + }; + let num_tiles = cols * rows; + let mut r = BitReader::new(data); + let mut tg_start = 0; + let mut tg_end = num_tiles - 1; + if num_tiles > 1 && r.flag()? { + let bits = cols_log2 + rows_log2; + tg_start = r.f(bits)? as usize; + tg_end = r.f(bits)? as usize; + } + r.byte_align(); + if tg_end >= num_tiles || tg_start > tg_end { + return Err(Error::Invalid("tile group range")); + } + let mut pos = r.byte_pos(); + for tile_num in tg_start..=tg_end { + let size = if tile_num == tg_end { + data.len().checked_sub(pos).ok_or(Error::Truncated)? + } else { + let mut v = 0usize; + for i in 0..tsb as usize { + v |= (*data.get(pos + i).ok_or(Error::Truncated)? as usize) << (8 * i); + } + pos += tsb as usize; + v + 1 + }; + let end = pos.checked_add(size).filter(|&e| e <= data.len()).ok_or(Error::Truncated)?; + job.tiles.push((tile_num, data[pos..end].to_vec())); + pos = end; + } + if tg_end != num_tiles - 1 { + self.pending = Some(job); + return Ok(None); + } + self.seen_frame_header = false; + Ok(self.submit(job)) + } + + /// The frame's data is complete: update the reference state (7.20) and decode it (on a + /// frame worker when there are any). + fn submit(&mut self, job: Box) -> Option { + let fh = &job.fh; + for i in 0..NUM_REF_FRAMES { + if (fh.refresh_frame_flags >> i) & 1 == 1 { + let ri = &mut self.refs[i]; + ri.valid = true; + ri.frame_id = fh.current_frame_id; + ri.upscaled_width = fh.upscaled_width; + ri.frame_width = fh.frame_width; + ri.frame_height = fh.frame_height; + ri.render_width = fh.render_width; + ri.render_height = fh.render_height; + ri.mi_cols = fh.mi_cols; + ri.mi_rows = fh.mi_rows; + ri.frame_type = fh.frame_type; + ri.order_hint = fh.order_hint; + ri.saved_order_hints = fh.order_hints; + ri.gm_params = fh.gm_params; + ri.lf_deltas = fh.lf.deltas; + ri.seg_features = fh.seg.features; + ri.grain = fh.film_grain.clone(); + self.ref_frames[i] = Some(job.slot.clone()); + } + } + let output = job.picture.clone().map(Output::Decoded); + #[cfg(all(feature = "threads", not(target_arch = "wasm32")))] + if let Some(w) = &self.workers + && let Some(tx) = &w.tx + { + let slot = job.slot.clone(); + match tx.send(job) { + Ok(()) => { + self.in_flight.push_back(slot); + // Bound the frames in flight (memory): wait for the oldest. + while self.in_flight.len() > self.max_in_flight { + let Some(s) = self.in_flight.pop_front() else { break }; + let _ = s.wait(); + } + self.in_flight.retain(|s| !s.is_set()); + } + Err(e) => run_job(*e.0, &self.pool, &self.stats), + } + return output; + } + run_job(*job, &self.pool, &self.stats); + output + } +} + +/// Decode a frame job and publish its results. +fn run_job(job: FrameJob, pool: &Pool, stats: &Mutex) { + let mut st = DecodeStats::default(); + match decode_frame(&job, pool, &mut st) { + Ok((rf, pic)) => { + if let Some(ps) = &job.picture { + ps.set(pic.map(Arc::new).ok_or(Error::Invalid("shown frame without picture"))); + } + job.slot.set(Ok(rf)); + } + Err(e) => { + if let Some(ps) = &job.picture { + ps.set(Err(e.clone())); + } + job.slot.set(Err(e)); + } + } + if let Ok(mut s) = stats.lock() { + s.merge(&st); + } +} + +/// The whole decoding process of one frame: setup, tiles, post-filters, motion vector storage +/// and the output picture. +fn decode_frame(job: &FrameJob, pool: &Pool, stats: &mut DecodeStats) -> Result<(Arc, Option)> { + let fh = &job.fh; + let seq = &job.seq; + // Wait for the reference frames this frame reads. + let mut used = [false; NUM_REF_FRAMES]; + if !fh.frame_is_intra { + for i in 0..REFS_PER_FRAME { + used[fh.ref_frame_idx[i]] = true; + } + } + if fh.primary_ref_frame != PRIMARY_REF_NONE { + used[fh.ref_frame_idx[fh.primary_ref_frame]] = true; + } + let mut refs: [Option>; NUM_REF_FRAMES] = Default::default(); + for i in 0..NUM_REF_FRAMES { + if used[i] { + let slot = job.refs[i].as_ref().ok_or(Error::Invalid("missing reference frame"))?; + refs[i] = Some(slot.wait()?); + } + } + let t = Timer::start(); + let mut fs = FrameState::new(seq, fh); + fs.refs = refs; + fs.ref_info = job.ref_info.clone(); + if fh.primary_ref_frame != PRIMARY_REF_NONE { + // load_previous_segment_ids( ) + if let Some(rf) = &fs.refs[fh.ref_frame_idx[fh.primary_ref_frame]] + && fh.seg.enabled + && rf.mi_cols == fs.mi.cols + && rf.mi_rows == fs.mi.rows + { + let ids = rf.segment_ids.clone(); + fs.prev_segment_ids.copy_from_slice(&ids); + } + } + if fh.use_ref_frame_mvs { + motion_field_estimation(&mut fs); + } + let mut cdfs = if fh.primary_ref_frame == PRIMARY_REF_NONE { + Cdfs::new(fh.quant.base_q_idx) + } else { + let rf = fs.refs[fh.ref_frame_idx[fh.primary_ref_frame]].as_ref().ok_or(Error::Invalid("primary reference frame missing"))?; + let mut c = rf.cdfs.clone(); + c.clear_counts(); + c + }; + stats.add(Stage::Setup, t.secs()); + let t = Timer::start(); + let saved = decode_tiles(pool, &mut fs, &cdfs, &job.tiles)?; + stats.tiles += job.tiles.len() as u64; + stats.add(Stage::Tiles, t.secs()); + if !fh.disable_frame_end_update_cdf + && let Some(s) = saved + { + cdfs = s; + } + crate::postfilter::apply(&mut fs, stats, pool, job.draft); + stats.draft_frames += job.draft as u64; + let t = Timer::start(); + stats.frames += 1; + if fh.seg.enabled && !fh.seg.update_map { + let prev = std::mem::take(&mut fs.prev_segment_ids); + fs.mi.segment_id.copy_from_slice(&prev); + } + let (mf_refs, mf_mvs) = motion_vector_storage(&fs); + let rf = Arc::new(RefFrame { + buf: std::mem::take(&mut fs.cur), + cdfs, + bit_depth: seq.color.bit_depth, + subsampling_x: seq.color.subsampling_x, + subsampling_y: seq.color.subsampling_y, + segment_ids: std::mem::take(&mut fs.mi.segment_id), + mi_cols: fs.mi.cols, + mi_rows: fs.mi.rows, + film_grain_present: seq.film_grain_params_present, + color: (seq.color.color_primaries, seq.color.transfer_characteristics, seq.color.matrix_coefficients, seq.color.color_range), + saved_ref_frames: mf_refs, + saved_mvs: mf_mvs, + }); + let pic = fh.show_frame.then(|| { + let mut p = output_picture(seq, &rf, fh.upscaled_width as usize, fh.frame_height as usize, &fh.film_grain, job.apply_film_grain, job.pts); + p.draft = job.draft; + p + }); + stats.add(Stage::Output, t.secs()); + Ok((rf, pic)) +} + +/// Decode the tiles of a frame (tile number, data), in parallel when several; returns the final +/// CDFs of tile context_update_tile_id. +fn decode_tiles(pool: &Pool, fs: &mut FrameState, cdfs: &Cdfs, tiles: &[(usize, Vec)]) -> Result>> { + let ti = fs.fh.tile_info.clone(); + let ctx_tile = ti.context_update_tile_id as usize; + let run = |sh: &FrameShared, t: &mut TileState, num: usize, data: &[u8]| -> Result>> { + t.intra_frame_y_mode_cdf = DEFAULT_INTRA_FRAME_Y_MODE_CDF; + let mut td = TileDecoder::new(sh, t, data, Box::new(cdfs.clone()), num / ti.cols, num % ti.cols); + td.decode_tile()?; + Ok((num == ctx_tile).then_some(td.cdf)) + }; + // Intra block copy reads the current frame through the tile's own buffer: keep whole-frame + // buffers (tiles one after another) for it. + if tiles.len() > 1 && pool.is_parallel() && !fs.fh.allow_intrabc { + let mut states: Vec = tiles + .iter() + .map(|(num, _)| { + let (row, col) = (num / ti.cols, num % ti.cols); + let (r0, r1) = (ti.mi_row_starts[row] as usize, ti.mi_row_starts[row + 1] as usize); + let (c0, c1) = (ti.mi_col_starts[col] as usize, ti.mi_col_starts[col + 1] as usize); + fs.tile_state(r0, r1.min(fs.mi.rows), c0, c1.min(fs.mi.cols)) + }) + .collect(); + let sh = &fs.sh; + let results = pool.map_mut(&mut states, |i, t| run(sh, t, tiles[i].0, &tiles[i].1)); + for t in &states { + fs.merge_tile_state(t); + } + let mut saved = None; + for r in results { + if let Some(c) = r? { + saved = Some(c); + } + } + return Ok(saved); + } + let mut t = fs.take_tile_state(); + let mut saved = None; + let mut res = Ok(()); + for (num, data) in tiles { + match run(&fs.sh, &mut t, *num, data) { + Ok(Some(c)) => saved = Some(c), + Ok(None) => {} + Err(e) => { + res = Err(e); + break; + } + } + } + fs.put_tile_state(t); + res.map(|_| saved) +} + +fn output_picture(seq: &SequenceHeader, rf: &RefFrame, w: usize, h: usize, grain: &FilmGrainParams, apply_film_grain: bool, pts: i64) -> Picture { + let ssx = rf.subsampling_x as usize; + let ssy = rf.subsampling_y as usize; + let num_planes = rf.buf.num_planes; + let mut planes: [Vec; 3] = Default::default(); + for p in 0..num_planes { + let (pw, ph) = if p == 0 { (w, h) } else { ((w + ssx) >> ssx, (h + ssy) >> ssy) }; + let src = &rf.buf.planes[p]; + let mut v = Vec::with_capacity(pw * ph); + for y in 0..ph { + v.extend_from_slice(&src.row(y)[..pw]); + } + planes[p] = v; + } + let mut pic = Picture { + width: w as u32, + height: h as u32, + bit_depth: rf.bit_depth, + subsampling_x: rf.subsampling_x, + subsampling_y: rf.subsampling_y, + mono_chrome: num_planes == 1, + planes, + color_primaries: rf.color.0, + transfer_characteristics: rf.color.1, + matrix_coefficients: rf.color.2, + full_range: rf.color.3, + pts, + draft: false, + }; + if apply_film_grain && seq.film_grain_params_present && grain.apply_grain { + crate::grain::apply(&mut pic, grain, seq); + } + pic +} + +/// Motion field motion vector storage process (7.19): (MfRefFrames, MfMvs). +fn motion_vector_storage(fs: &FrameState) -> (Vec, Vec<[i32; 2]>) { + let mi = &fs.mi; + let n = mi.rows * mi.cols; + let mut refs = vec![-1i8; n]; + let mut mvs = vec![[0i32; 2]; n]; + let fh = &fs.fh; + for i in 0..n { + for list in 0..2 { + let r = mi.ref_frame[i][list]; + if r > INTRA_FRAME as i8 { + let ref_idx = fh.ref_frame_idx[r as usize - LAST_FRAME]; + let dist = crate::header::relative_dist(&fs.seq, fs.ref_info[ref_idx].order_hint, fh.order_hint); + if dist < 0 { + let m = mi.mv[i][list]; + let (row, col) = (m.row as i32, m.col as i32); + if row.abs() <= REFMVS_LIMIT as i32 && col.abs() <= REFMVS_LIMIT as i32 { + refs[i] = r; + mvs[i] = [row, col]; + } + } + } + } + } + (refs, mvs) +} + +/// Motion field estimation process (7.9). +fn motion_field_estimation(fs: &mut FrameState) { + let w8 = fs.fh.mi_cols as usize >> 1; + let h8 = fs.fh.mi_rows as usize >> 1; + for r in LAST_FRAME..=ALTREF_FRAME { + fs.motion_field[r] = vec![[crate::mvpred::INVALID_MV, crate::mvpred::INVALID_MV]; w8 * h8]; + } + let fh = fs.fh.clone(); + let seq = fs.seq.clone(); + let rd = |a: u32, b: u32| crate::header::relative_dist(&seq, a, b); + let last_idx = fh.ref_frame_idx[0]; + let cur_gold = fh.order_hints[GOLDEN_FRAME]; + let last_alt = fs.ref_info[last_idx].saved_order_hints[ALTREF_FRAME]; + if last_alt != cur_gold { + project(fs, LAST_FRAME, -1); + } + let mut ref_stamp = MFMV_STACK_SIZE as i32 - 2; + if rd(fh.order_hints[BWDREF_FRAME], fh.order_hint) > 0 && project(fs, BWDREF_FRAME, 1) { + ref_stamp -= 1; + } + if rd(fh.order_hints[ALTREF2_FRAME], fh.order_hint) > 0 && project(fs, ALTREF2_FRAME, 1) { + ref_stamp -= 1; + } + if rd(fh.order_hints[ALTREF_FRAME], fh.order_hint) > 0 && ref_stamp >= 0 && project(fs, ALTREF_FRAME, 1) { + ref_stamp -= 1; + } + if ref_stamp >= 0 { + project(fs, LAST2_FRAME, -1); + } +} + +fn get_mv_projection(mv: [i32; 2], numerator: i32, denominator: i32) -> [i32; 2] { + let den = denominator.min(MAX_FRAME_DISTANCE as i32); + let num = numerator.clamp(-(MAX_FRAME_DISTANCE as i32), MAX_FRAME_DISTANCE as i32); + let mut out = [0i32; 2]; + for i in 0..2 { + let scaled = crate::mvpred::round2_signed64(mv[i] as i64 * num as i64 * DIV_MULT[den as usize] as i64, 14); + out[i] = (scaled as i32).clamp(-(1 << 14) + 1, (1 << 14) - 1); + } + out +} + +fn project_pos(v8: i32, delta: i32, dst_sign: i32, max8: i32, max_off8: i32) -> Option { + let base8 = (v8 >> 3) << 3; + let offset8 = if delta >= 0 { delta >> (3 + 1 + 2) } else { -((-delta) >> (3 + 1 + 2)) }; + let v = v8 + dst_sign * offset8; + if v < 0 || v >= max8 || v < base8 - max_off8 || v >= base8 + 8 + max_off8 { None } else { Some(v) } +} + +/// Projection process (7.9.2). +fn project(fs: &mut FrameState, src: usize, dst_sign: i32) -> bool { + let fh = fs.fh.clone(); + let seq = fs.seq.clone(); + let src_idx = fh.ref_frame_idx[src - LAST_FRAME]; + let w8 = fh.mi_cols as i32 >> 1; + let h8 = fh.mi_rows as i32 >> 1; + let ri = fs.ref_info[src_idx].clone(); + if ri.mi_rows != fh.mi_rows || ri.mi_cols != fh.mi_cols || ri.frame_type == INTRA_ONLY_FRAME as u8 || ri.frame_type == KEY_FRAME as u8 { + return false; + } + let Some(rf) = fs.refs[src_idx].clone() else { return false }; + let rd = |a: u32, b: u32| crate::header::relative_dist(&seq, a, b); + let mi_cols = fh.mi_cols as usize; + for y8 in 0..h8 { + for x8 in 0..w8 { + let row = (2 * y8 + 1) as usize; + let col = (2 * x8 + 1) as usize; + let src_ref = rf.saved_ref_frames[row * mi_cols + col]; + if src_ref > INTRA_FRAME as i8 { + let ref_to_cur = rd(fh.order_hints[src], fh.order_hint); + let ref_offset = rd(fh.order_hints[src], ri.saved_order_hints[src_ref as usize]); + let pos_valid = ref_to_cur.abs() <= MAX_FRAME_DISTANCE as i32 && ref_offset.abs() <= MAX_FRAME_DISTANCE as i32 && ref_offset > 0; + if pos_valid { + let mv = rf.saved_mvs[row * mi_cols + col]; + let proj = get_mv_projection(mv, ref_to_cur * dst_sign, ref_offset); + let py = project_pos(y8, proj[0], dst_sign, h8, MAX_OFFSET_HEIGHT as i32); + let px = project_pos(x8, proj[1], dst_sign, w8, MAX_OFFSET_WIDTH as i32); + if let (Some(py), Some(px)) = (py, px) { + for dst in LAST_FRAME..=ALTREF_FRAME { + let ref_to_dst = rd(fh.order_hint, fh.order_hints[dst]); + let pm = get_mv_projection(mv, ref_to_dst, ref_offset); + fs.motion_field[dst][(py * w8 + px) as usize] = pm; + } + } + } + } + } + } + true +} diff --git a/crates/av1/src/frame.rs b/crates/av1/src/frame.rs new file mode 100644 index 0000000..3cf4b9f --- /dev/null +++ b/crates/av1/src/frame.rs @@ -0,0 +1,287 @@ +//! Frame sample buffers and per-4x4 mode info storage. +//! +//! Tiles decode in parallel into private buffers that cover only the tile's area: a [`Plane`] +//! or [`MiInfo`] may hold a region of the frame (origin `ox`, `oy`), addressed with frame +//! coordinates throughout. + +/// Recycled plane allocations: every frame allocates (and every evicted reference frees) +/// megabytes of planes, and fresh large allocations cost page faults and kernel calls. Buffers +/// are zeroed again on reuse, so contents never leak between frames. +static PLANE_POOL: std::sync::Mutex>> = std::sync::Mutex::new(Vec::new()); +const POOL_MIN_LEN: usize = 1 << 16; +/// Samples kept at most (96 M samples = 192 MB, a few 4K frames). +const POOL_MAX_SAMPLES: usize = 96 << 20; + +/// A zeroed buffer of `len` samples (recycled memory for large ones). +fn zeroed(len: usize) -> Vec { + if len >= POOL_MIN_LEN { + let found = { + let mut pool = PLANE_POOL.lock().unwrap_or_else(|e| e.into_inner()); + pool.iter().position(|v| v.capacity() >= len && v.capacity() <= 2 * len).map(|i| pool.swap_remove(i)) + }; + if let Some(mut v) = found { + v.clear(); + v.resize(len, 0); + return v; + } + } + vec![0; len] +} + +/// One plane of samples (u16 at every bit depth), or a rectangular region of one. +#[derive(Default)] +pub struct Plane { + pub data: Vec, + pub stride: usize, + /// Allocated rows. + pub rows: usize, + /// Frame coordinates of `data[0]` (0 for whole planes). + pub ox: usize, + pub oy: usize, +} + +impl Clone for Plane { + fn clone(&self) -> Plane { + let mut data = zeroed(self.data.len()); + data.copy_from_slice(&self.data); + Plane { data, stride: self.stride, rows: self.rows, ox: self.ox, oy: self.oy } + } +} + +impl Drop for Plane { + fn drop(&mut self) { + if self.data.capacity() >= POOL_MIN_LEN { + let v = std::mem::take(&mut self.data); + let mut pool = PLANE_POOL.lock().unwrap_or_else(|e| e.into_inner()); + let kept: usize = pool.iter().map(|b| b.capacity()).sum(); + if kept + v.capacity() <= POOL_MAX_SAMPLES { + pool.push(v); + } + } + } +} + +impl Plane { + pub fn new(width: usize, height: usize) -> Plane { + Plane { data: zeroed(width * height), stride: width, rows: height, ox: 0, oy: 0 } + } + /// A `width` x `height` region with its top-left sample at frame position (ox, oy). + pub fn region(ox: usize, oy: usize, width: usize, height: usize) -> Plane { + Plane { data: zeroed(width * height), stride: width, rows: height, ox, oy } + } + #[inline(always)] + pub fn at(&self, x: usize, y: usize) -> u16 { + self.data[(y - self.oy) * self.stride + x - self.ox] + } + #[inline(always)] + pub fn set(&mut self, x: usize, y: usize, v: u16) { + self.data[(y - self.oy) * self.stride + x - self.ox] = v; + } + /// Row `y`; index 0 is column `ox` (whole planes: column 0). + #[inline(always)] + pub fn row(&self, y: usize) -> &[u16] { + let o = (y - self.oy) * self.stride; + &self.data[o..o + self.stride] + } + #[inline(always)] + pub fn row_mut(&mut self, y: usize) -> &mut [u16] { + let o = (y - self.oy) * self.stride; + &mut self.data[o..o + self.stride] + } + /// Row `y` from column `x` (frame coordinates) to the end of the stored row. + #[inline(always)] + pub fn row_from(&self, y: usize, x: usize) -> &[u16] { + let o = (y - self.oy) * self.stride; + &self.data[o + x - self.ox..o + self.stride] + } + #[inline(always)] + pub fn row_from_mut(&mut self, y: usize, x: usize) -> &mut [u16] { + let o = (y - self.oy) * self.stride; + &mut self.data[o + x - self.ox..o + self.stride] + } + /// Copy this region into the whole plane `dst` (same frame coordinates). + pub fn copy_into(&self, dst: &mut Plane) { + for y in 0..self.rows { + let s = &self.data[y * self.stride..(y + 1) * self.stride]; + dst.row_from_mut(self.oy + y, self.ox)[..self.stride].copy_from_slice(s); + } + } +} + +/// A frame's three planes, sized to the macroblock-aligned coded area plus margins. +#[derive(Clone, Default)] +pub struct FrameBuf { + pub planes: [Plane; 3], + pub num_planes: usize, + pub subsampling_x: usize, + pub subsampling_y: usize, + pub bit_depth: u8, + /// Frame dimensions (luma) the samples are valid for. + pub width: usize, + pub height: usize, +} + +impl FrameBuf { + /// Allocate a frame for a coded area of `width` x `height` luma samples (rounded up to the + /// 128x128 superblock grid plus 160 samples of slack for blocks extending past the edge). + pub fn new(width: usize, height: usize, num_planes: usize, subsampling_x: usize, subsampling_y: usize, bit_depth: u8) -> FrameBuf { + let aw = width.div_ceil(128) * 128 + 160; + let ah = height.div_ceil(128) * 128 + 160; + let mut planes: [Plane; 3] = Default::default(); + planes[0] = Plane::new(aw, ah); + for p in planes.iter_mut().take(num_planes).skip(1) { + *p = Plane::new(aw >> subsampling_x, ah >> subsampling_y); + } + FrameBuf { planes, num_planes, subsampling_x, subsampling_y, bit_depth, width, height } + } + + /// The region of a frame of this geometry covering luma samples [x0, x1) x [y0, y1) + /// (chroma scaled down); the bounds are multiples of 8, or the allocated size. + pub fn region_like(&self, x0: usize, y0: usize, x1: usize, y1: usize) -> FrameBuf { + let mut planes: [Plane; 3] = Default::default(); + for (p, pl) in planes.iter_mut().enumerate().take(self.num_planes) { + let (sx, sy) = if p == 0 { (0, 0) } else { (self.subsampling_x, self.subsampling_y) }; + let full = &self.planes[p]; + let (px0, py0) = (x0 >> sx, y0 >> sy); + let px1 = (x1 >> sx).min(full.stride); + let py1 = (y1 >> sy).min(full.rows); + *pl = Plane::region(px0, py0, px1 - px0, py1 - py0); + } + FrameBuf { + planes, + num_planes: self.num_planes, + subsampling_x: self.subsampling_x, + subsampling_y: self.subsampling_y, + bit_depth: self.bit_depth, + width: self.width, + height: self.height, + } + } + + pub fn plane_width(&self, plane: usize) -> usize { + if plane == 0 { self.width } else { (self.width + self.subsampling_x) >> self.subsampling_x } + } + + pub fn plane_height(&self, plane: usize) -> usize { + if plane == 0 { self.height } else { (self.height + self.subsampling_y) >> self.subsampling_y } + } +} + +/// A motion vector (row, col) in 1/8 sample units. +#[derive(Clone, Copy, Debug, Default, PartialEq, Eq)] +pub struct Mv { + pub row: i16, + pub col: i16, +} + +impl Mv { + pub const ZERO: Mv = Mv { row: 0, col: 0 }; + pub fn new(row: i32, col: i32) -> Mv { + Mv { row: row as i16, col: col as i16 } + } +} + +/// Mode info stored for every 4x4 luma position (the arrays the spec indexes by +/// `[ row ][ col ]` in mode-info units). +#[derive(Clone, Default)] +pub struct MiInfo { + /// Frame size in mode info units. + pub cols: usize, + pub rows: usize, + /// Stored region: origin and width (the whole frame unless a tile's private copy). + pub ox: usize, + pub oy: usize, + pub stride: usize, + pub region_rows: usize, + pub y_mode: Vec, + pub uv_mode: Vec, + pub ref_frame: Vec<[i8; 2]>, + pub mv: Vec<[Mv; 2]>, + pub is_inter: Vec, + pub skip_mode: Vec, + pub skip: Vec, + pub tx_size: Vec, + pub inter_tx_size: Vec, + pub mi_size: Vec, + pub segment_id: Vec, + pub palette_size: [Vec; 2], + pub palette_colors: [Vec<[u16; 8]>; 2], + pub delta_lf: Vec<[i8; 4]>, + pub comp_group_idx: Vec, + pub compound_idx: Vec, + pub interp_filter: Vec<[u8; 2]>, + pub motion_mode: Vec, + /// TxTypes[ row ][ col ] (luma 4x4 units). + pub tx_type: Vec, + /// RefFrames[ row ][ col ] has been written for this frame. + pub written: Vec, +} + +impl MiInfo { + /// `palette`: whether palette mode can occur (allow_screen_content_tools); without it the + /// palette arrays (40 bytes per unit) stay empty. + pub fn new(cols: usize, rows: usize, palette: bool) -> MiInfo { + MiInfo::region(cols, rows, 0, 0, cols, rows, palette) + } + + /// Storage for the w x h mode info units at (ox, oy) of a cols x rows frame. + pub fn region(cols: usize, rows: usize, ox: usize, oy: usize, w: usize, h: usize, palette: bool) -> MiInfo { + let n = w * h; + let np = if palette { n } else { 0 }; + MiInfo { + cols, + rows, + ox, + oy, + stride: w, + region_rows: h, + y_mode: vec![0; n], + uv_mode: vec![0; n], + ref_frame: vec![[0, -1]; n], + mv: vec![[Mv::ZERO; 2]; n], + is_inter: vec![false; n], + skip_mode: vec![false; n], + skip: vec![false; n], + tx_size: vec![0; n], + inter_tx_size: vec![0; n], + mi_size: vec![0; n], + segment_id: vec![0; n], + palette_size: [vec![0; np], vec![0; np]], + palette_colors: [vec![[0; 8]; np], vec![[0; 8]; np]], + delta_lf: vec![[0; 4]; n], + comp_group_idx: vec![0; n], + compound_idx: vec![0; n], + interp_filter: vec![[0; 2]; n], + motion_mode: vec![0; n], + tx_type: vec![0; n], + written: vec![false; n], + } + } + + #[inline(always)] + pub fn idx(&self, row: usize, col: usize) -> usize { + debug_assert!(col >= self.ox && col < self.ox + self.stride && row >= self.oy, "mode info ({row}, {col}) outside the stored region"); + (row - self.oy) * self.stride + col - self.ox + } + + /// Copy this region into the whole-frame `dst`. + pub fn copy_into(&self, dst: &mut MiInfo) { + let (w, h) = (self.stride, self.region_rows); + macro_rules! copy { + ($($f:ident)*) => {$( + for y in 0..h { + let d = dst.idx(self.oy + y, self.ox); + dst.$f[d..d + w].copy_from_slice(&self.$f[y * w..(y + 1) * w]); + } + )*}; + } + copy!(y_mode uv_mode ref_frame mv is_inter skip_mode skip tx_size inter_tx_size mi_size segment_id delta_lf comp_group_idx compound_idx interp_filter motion_mode tx_type written); + for k in (0..2).filter(|&k| !self.palette_size[k].is_empty()) { + for y in 0..h { + let d = dst.idx(self.oy + y, self.ox); + dst.palette_size[k][d..d + w].copy_from_slice(&self.palette_size[k][y * w..(y + 1) * w]); + dst.palette_colors[k][d..d + w].copy_from_slice(&self.palette_colors[k][y * w..(y + 1) * w]); + } + } + } +} diff --git a/crates/av1/src/grain.rs b/crates/av1/src/grain.rs new file mode 100644 index 0000000..d4baad6 --- /dev/null +++ b/crates/av1/src/grain.rs @@ -0,0 +1,276 @@ +//! Film grain synthesis (7.18.3), applied to output pictures only (references stay clean). + +use crate::Picture; +use crate::header::{FilmGrainParams, SequenceHeader}; +use crate::spec_tables::{GAUSSIAN_SEQUENCE, MC_IDENTITY}; + +#[inline] +fn round2(x: i32, n: u32) -> i32 { + if n == 0 { x } else { (x + (1 << (n - 1))) >> n } +} + +struct Rng(u32); + +impl Rng { + /// get_random_number( bits ) (7.18.3.2) + fn next(&mut self, bits: u32) -> i32 { + let r = self.0; + let bit = (r ^ (r >> 1) ^ (r >> 3) ^ (r >> 12)) & 1; + let r = (r >> 1) | (bit << 15); + self.0 = r; + ((r >> (16 - bits)) & ((1 << bits) - 1)) as i32 + } +} + +pub(crate) fn apply(pic: &mut Picture, g: &FilmGrainParams, seq: &SequenceHeader) { + let _ = seq; + let bd = pic.bit_depth as u32; + let mono = pic.mono_chrome; + let num_planes = if mono { 1 } else { 3 }; + let (sub_x, sub_y) = (pic.subsampling_x as usize, pic.subsampling_y as usize); + let w = pic.width as usize; + let h = pic.height as usize; + let grain_center = 128i32 << (bd - 8); + let grain_min = -grain_center; + let grain_max = (256i32 << (bd - 8)) - 1 - grain_center; + + // Generate grain (7.18.3.3). + let mut rng = Rng(g.grain_seed); + let shift = 12 - bd + g.grain_scale_shift; + let mut luma = vec![[0i32; 82]; 73]; + for row in luma.iter_mut() { + for v in row.iter_mut() { + let gv = if g.num_y_points > 0 { GAUSSIAN_SEQUENCE[rng.next(11) as usize] as i32 } else { 0 }; + *v = round2(gv, shift); + } + } + let ar_shift = g.ar_coeff_shift_minus_6 + 6; + let lag = g.ar_coeff_lag as i32; + for y in 3..73 { + for x in 3..82 - 3 { + let mut s = 0; + let mut pos = 0; + 'outer: for dr in -lag..=0 { + for dc in -lag..=lag { + if dr == 0 && dc == 0 { + break 'outer; + } + let c = g.ar_coeffs_y_plus_128[pos] as i32 - 128; + s += luma[(y as i32 + dr) as usize][(x as i32 + dc) as usize] * c; + pos += 1; + } + } + luma[y][x] = (luma[y][x] + round2(s, ar_shift)).clamp(grain_min, grain_max); + } + } + let chroma_w = if sub_x != 0 { 44 } else { 82 }; + let chroma_h = if sub_y != 0 { 38 } else { 73 }; + let mut cb = vec![[0i32; 82]; 73]; + let mut cr = vec![[0i32; 82]; 73]; + if !mono { + for (arr, seed, on) in [(&mut cb, 0xb524u32, g.num_cb_points > 0), (&mut cr, 0x49d8, g.num_cr_points > 0)] { + rng.0 = g.grain_seed ^ seed; + for row in arr.iter_mut().take(chroma_h) { + for v in row.iter_mut().take(chroma_w) { + let gv = if on || g.chroma_scaling_from_luma { GAUSSIAN_SEQUENCE[rng.next(11) as usize] as i32 } else { 0 }; + *v = round2(gv, shift); + } + } + } + for y in 3..chroma_h { + for x in 3..chroma_w - 3 { + let mut s0 = 0; + let mut s1 = 0; + let mut pos = 0; + 'outer: for dr in -lag..=0 { + for dc in -lag..=lag { + let c0 = g.ar_coeffs_cb_plus_128[pos] as i32 - 128; + let c1 = g.ar_coeffs_cr_plus_128[pos] as i32 - 128; + if dr == 0 && dc == 0 { + if g.num_y_points > 0 { + let mut l = 0; + let lx = ((x - 3) << sub_x) + 3; + let ly = ((y - 3) << sub_y) + 3; + for i in 0..=sub_y { + for j in 0..=sub_x { + l += luma[ly + i][lx + j]; + } + } + let l = round2(l, (sub_x + sub_y) as u32); + s0 += l * c0; + s1 += l * c1; + } + break 'outer; + } + let (yy, xx) = ((y as i32 + dr) as usize, (x as i32 + dc) as usize); + s0 += cb[yy][xx] * c0; + s1 += cr[yy][xx] * c1; + pos += 1; + } + } + cb[y][x] = (cb[y][x] + round2(s0, ar_shift)).clamp(grain_min, grain_max); + cr[y][x] = (cr[y][x] + round2(s1, ar_shift)).clamp(grain_min, grain_max); + } + } + } + + // Scaling lookup (7.18.3.4). + let mut lut = [[0i32; 256]; 3]; + for (plane, l) in lut.iter_mut().enumerate().take(num_planes) { + let (n, xs, ys) = if plane == 0 || g.chroma_scaling_from_luma { + (g.num_y_points, &g.point_y_value, &g.point_y_scaling) + } else if plane == 1 { + (g.num_cb_points, &g.point_cb_value, &g.point_cb_scaling) + } else { + (g.num_cr_points, &g.point_cr_value, &g.point_cr_scaling) + }; + if n == 0 { + continue; + } + for v in l.iter_mut().take(xs[0] as usize) { + *v = ys[0] as i32; + } + for i in 0..n - 1 { + let dy = ys[i + 1] as i32 - ys[i] as i32; + let dx = xs[i + 1] as i32 - xs[i] as i32; + let delta = dy * ((65536 + (dx >> 1)) / dx); + for x in 0..dx { + l[xs[i] as usize + x as usize] = ys[i] as i32 + ((x * delta + 32768) >> 16); + } + } + for v in l.iter_mut().skip(xs[n - 1] as usize) { + *v = ys[n - 1] as i32; + } + } + let scale_lut = |plane: usize, index: i32| -> i32 { + let sh = bd - 8; + let x = index >> sh; + let rem = index - (x << sh); + if bd == 8 || x == 255 { + lut[plane][x as usize] + } else { + let start = lut[plane][x as usize]; + let end = lut[plane][x as usize + 1]; + start + round2((end - start) * rem, sh) + } + }; + + // Noise stripes (7.18.3.5). + let stripe_w = w + 64; + let n_stripes = (h.div_ceil(2)).div_ceil(16).max(1); + let mut stripes: Vec<[Vec; 3]> = (0..n_stripes).map(|_| [vec![0; 34 * stripe_w], vec![0; 34 * stripe_w], vec![0; 34 * stripe_w]]).collect(); + let overlap = g.overlap_flag; + for (luma_num, stripe) in stripes.iter_mut().enumerate() { + let mut r = g.grain_seed; + r ^= (((luma_num * 37 + 178) & 255) << 8) as u32; + r ^= ((luma_num * 173 + 105) & 255) as u32; + rng.0 = r; + let mut x = 0; + while x < w.div_ceil(2) { + let rand = rng.next(8) as usize; + let off_x = rand >> 4; + let off_y = rand & 15; + for (plane, s) in stripe.iter_mut().enumerate().take(num_planes) { + let psx = if plane > 0 { sub_x } else { 0 }; + let psy = if plane > 0 { sub_y } else { 0 }; + let pox = if psx != 0 { 6 + off_x } else { 9 + off_x * 2 }; + let poy = if psy != 0 { 6 + off_y } else { 9 + off_y * 2 }; + let src = match plane { + 0 => &luma, + 1 => &cb, + _ => &cr, + }; + for i in 0..(34 >> psy) { + for j in 0..(34 >> psx) { + let mut gv = src[poy + i][pox + j]; + if psx == 0 { + let idx = i * stripe_w + x * 2 + j; + if j < 2 && overlap && x > 0 { + let old = s[idx]; + gv = if j == 0 { old * 27 + gv * 17 } else { old * 17 + gv * 27 }; + gv = round2(gv, 5).clamp(grain_min, grain_max); + } + s[idx] = gv; + } else { + let idx = i * stripe_w + x + j; + if j == 0 && overlap && x > 0 { + let old = s[idx]; + gv = round2(old * 23 + gv * 22, 5).clamp(grain_min, grain_max); + } + s[idx] = gv; + } + } + } + } + x += 16; + } + } + let noise_at = |plane: usize, y: usize, x: usize| -> i32 { + let psy = if plane > 0 { sub_y } else { 0 }; + let luma_num = y >> (5 - psy); + let i = y - (luma_num << (5 - psy)); + let mut gv = stripes[luma_num][plane][i * stripe_w + x]; + if psy == 0 { + if i < 2 && luma_num > 0 && overlap { + let old = stripes[luma_num - 1][plane][(i + 32) * stripe_w + x]; + gv = if i == 0 { old * 27 + gv * 17 } else { old * 17 + gv * 27 }; + gv = round2(gv, 5).clamp(grain_min, grain_max); + } + } else if i < 1 && luma_num > 0 && overlap { + let old = stripes[luma_num - 1][plane][(i + 16) * stripe_w + x]; + gv = round2(old * 23 + gv * 22, 5).clamp(grain_min, grain_max); + } + gv + }; + + // Blend (7.18.3.5). + let (min_value, max_luma, max_chroma) = if g.clip_to_restricted_range { + let ml = 235 << (bd - 8); + (16 << (bd - 8), ml, if pic.matrix_coefficients as usize == MC_IDENTITY { ml } else { 240 << (bd - 8) }) + } else { + let m = (256 << (bd - 8)) - 1; + (0, m, m) + }; + let pix_max = (1i32 << bd) - 1; + let scaling_shift = g.grain_scaling_minus_8 + 8; + if !mono { + let cw = (w + sub_x) >> sub_x; + let ch = (h + sub_y) >> sub_y; + let [py, pu, pv] = &mut pic.planes; + for y in 0..ch { + for x in 0..cw { + let lx = x << sub_x; + let ly = y << sub_y; + let lnx = (lx + 1).min(w - 1); + let avg = if sub_x != 0 { round2(py[ly * w + lx] as i32 + py[ly * w + lnx] as i32, 1) } else { py[ly * w + lx] as i32 }; + for (plane, out, on, luma_mult, mult, offset) in [ + (1usize, &mut *pu, g.num_cb_points > 0, g.cb_luma_mult, g.cb_mult, g.cb_offset), + (2, &mut *pv, g.num_cr_points > 0, g.cr_luma_mult, g.cr_mult, g.cr_offset), + ] { + if !(on || g.chroma_scaling_from_luma) { + continue; + } + let orig = out[y * cw + x] as i32; + let merged = if g.chroma_scaling_from_luma { + avg + } else { + let combined = avg * (luma_mult as i32 - 128) + orig * (mult as i32 - 128); + ((combined >> 6) + ((offset as i32 - 256) << (bd - 8))).clamp(0, pix_max) + }; + let noise = round2(scale_lut(plane, merged) * noise_at(plane, y, x), scaling_shift); + out[y * cw + x] = (orig + noise).clamp(min_value, max_chroma) as u16; + } + } + } + } + if g.num_y_points > 0 { + let py = &mut pic.planes[0]; + for y in 0..h { + for x in 0..w { + let orig = py[y * w + x] as i32; + let noise = round2(scale_lut(0, orig) * noise_at(0, y, x), scaling_shift); + py[y * w + x] = (orig + noise).clamp(min_value, max_luma) as u16; + } + } + } +} diff --git a/crates/av1/src/header.rs b/crates/av1/src/header.rs new file mode 100644 index 0000000..fa9db0a --- /dev/null +++ b/crates/av1/src/header.rs @@ -0,0 +1,1369 @@ +//! Sequence header (5.5) and uncompressed frame header (5.9) parsing. + +use crate::bits::BitReader; +use crate::spec_tables::*; +use crate::{Error, Result}; + +pub const EIGHTTAP: u8 = 0; +pub const BILINEAR: u8 = 3; +pub const SWITCHABLE: u8 = 4; +/// `NONE` reference frame. +pub const NONE: i8 = -1; + +/// Color configuration (5.5.2). +#[derive(Debug, Clone, Default, PartialEq, Eq)] +pub struct ColorConfig { + pub bit_depth: u8, + pub mono_chrome: bool, + pub num_planes: usize, + pub color_primaries: u8, + pub transfer_characteristics: u8, + pub matrix_coefficients: u8, + pub color_range: bool, + pub subsampling_x: u8, + pub subsampling_y: u8, + pub chroma_sample_position: u8, + pub separate_uv_delta_q: bool, +} + +/// A parsed sequence header OBU. +#[derive(Debug, Clone, Default, PartialEq, Eq)] +pub struct SequenceHeader { + pub profile: u8, + pub still_picture: bool, + pub reduced_still_picture_header: bool, + pub timing_info_present: bool, + pub decoder_model_info_present: bool, + pub equal_picture_interval: bool, + pub buffer_removal_time_length_minus_1: u32, + pub frame_presentation_time_length_minus_1: u32, + pub operating_points_cnt_minus_1: usize, + pub operating_point_idc: [u32; 32], + pub seq_level_idx: [u8; 32], + pub decoder_model_present_for_this_op: [bool; 32], + /// OperatingPointIdc of the chosen operating point (0). + pub op_idc: u32, + pub frame_width_bits: u32, + pub frame_height_bits: u32, + pub max_frame_width: u32, + pub max_frame_height: u32, + pub frame_id_numbers_present: bool, + pub delta_frame_id_length_minus_2: u32, + pub additional_frame_id_length_minus_1: u32, + pub use_128x128_superblock: bool, + pub enable_filter_intra: bool, + pub enable_intra_edge_filter: bool, + pub enable_interintra_compound: bool, + pub enable_masked_compound: bool, + pub enable_warped_motion: bool, + pub enable_dual_filter: bool, + pub enable_order_hint: bool, + pub enable_jnt_comp: bool, + pub enable_ref_frame_mvs: bool, + pub seq_force_screen_content_tools: u32, + pub seq_force_integer_mv: u32, + pub order_hint_bits: u32, + pub enable_superres: bool, + pub enable_cdef: bool, + pub enable_restoration: bool, + pub color: ColorConfig, + pub film_grain_params_present: bool, +} + +impl SequenceHeader { + pub fn parse(data: &[u8]) -> Result { + let mut r = BitReader::new(data); + let mut s = SequenceHeader { profile: r.f(3)? as u8, ..Default::default() }; + if s.profile > 2 { + return Err(Error::Unsupported("seq_profile > 2")); + } + s.still_picture = r.flag()?; + s.reduced_still_picture_header = r.flag()?; + if s.reduced_still_picture_header { + s.seq_level_idx[0] = r.f(5)? as u8; + } else { + s.timing_info_present = r.flag()?; + let mut buffer_delay_length_minus_1 = 0; + if s.timing_info_present { + r.f(32)?; // num_units_in_display_tick + r.f(32)?; // time_scale + s.equal_picture_interval = r.flag()?; + if s.equal_picture_interval { + r.uvlc()?; + } + s.decoder_model_info_present = r.flag()?; + if s.decoder_model_info_present { + buffer_delay_length_minus_1 = r.f(5)?; + r.f(32)?; // num_units_in_decoding_tick + s.buffer_removal_time_length_minus_1 = r.f(5)?; + s.frame_presentation_time_length_minus_1 = r.f(5)?; + } + } + let initial_display_delay_present = r.flag()?; + s.operating_points_cnt_minus_1 = r.f(5)? as usize; + for i in 0..=s.operating_points_cnt_minus_1 { + s.operating_point_idc[i] = r.f(12)?; + s.seq_level_idx[i] = r.f(5)? as u8; + if s.seq_level_idx[i] > 7 { + r.f(1)?; // seq_tier + } + if s.decoder_model_info_present { + s.decoder_model_present_for_this_op[i] = r.flag()?; + if s.decoder_model_present_for_this_op[i] { + let n = buffer_delay_length_minus_1 + 1; + r.f(n)?; + r.f(n)?; + r.f(1)?; + } + } + if initial_display_delay_present && r.flag()? { + r.f(4)?; + } + } + } + s.op_idc = s.operating_point_idc[0]; + s.frame_width_bits = r.f(4)? + 1; + s.frame_height_bits = r.f(4)? + 1; + s.max_frame_width = r.f(s.frame_width_bits)? + 1; + s.max_frame_height = r.f(s.frame_height_bits)? + 1; + s.frame_id_numbers_present = if s.reduced_still_picture_header { false } else { r.flag()? }; + if s.frame_id_numbers_present { + s.delta_frame_id_length_minus_2 = r.f(4)?; + s.additional_frame_id_length_minus_1 = r.f(3)?; + } + s.use_128x128_superblock = r.flag()?; + s.enable_filter_intra = r.flag()?; + s.enable_intra_edge_filter = r.flag()?; + if s.reduced_still_picture_header { + s.seq_force_screen_content_tools = SELECT_SCREEN_CONTENT_TOOLS as u32; + s.seq_force_integer_mv = SELECT_INTEGER_MV as u32; + } else { + s.enable_interintra_compound = r.flag()?; + s.enable_masked_compound = r.flag()?; + s.enable_warped_motion = r.flag()?; + s.enable_dual_filter = r.flag()?; + s.enable_order_hint = r.flag()?; + if s.enable_order_hint { + s.enable_jnt_comp = r.flag()?; + s.enable_ref_frame_mvs = r.flag()?; + } + s.seq_force_screen_content_tools = if r.flag()? { SELECT_SCREEN_CONTENT_TOOLS as u32 } else { r.f(1)? }; + if s.seq_force_screen_content_tools > 0 { + s.seq_force_integer_mv = if r.flag()? { SELECT_INTEGER_MV as u32 } else { r.f(1)? }; + } else { + s.seq_force_integer_mv = SELECT_INTEGER_MV as u32; + } + if s.enable_order_hint { + s.order_hint_bits = r.f(3)? + 1; + } + } + s.enable_superres = r.flag()?; + s.enable_cdef = r.flag()?; + s.enable_restoration = r.flag()?; + s.color = parse_color_config(&mut r, s.profile)?; + s.film_grain_params_present = r.flag()?; + Ok(s) + } +} + +fn parse_color_config(r: &mut BitReader, profile: u8) -> Result { + let mut c = ColorConfig::default(); + let high = r.flag()?; + c.bit_depth = if profile == 2 && high { + if r.flag()? { 12 } else { 10 } + } else if high { + 10 + } else { + 8 + }; + c.mono_chrome = if profile == 1 { false } else { r.flag()? }; + c.num_planes = if c.mono_chrome { 1 } else { 3 }; + if r.flag()? { + c.color_primaries = r.f(8)? as u8; + c.transfer_characteristics = r.f(8)? as u8; + c.matrix_coefficients = r.f(8)? as u8; + } else { + c.color_primaries = 2; + c.transfer_characteristics = 2; + c.matrix_coefficients = 2; + } + if c.mono_chrome { + c.color_range = r.flag()?; + c.subsampling_x = 1; + c.subsampling_y = 1; + return Ok(c); + } else if c.color_primaries == 1 && c.transfer_characteristics == 13 && c.matrix_coefficients == 0 { + c.color_range = true; + } else { + c.color_range = r.flag()?; + match profile { + 0 => { + c.subsampling_x = 1; + c.subsampling_y = 1; + } + 1 => {} + _ => { + if c.bit_depth == 12 { + c.subsampling_x = r.f(1)? as u8; + c.subsampling_y = if c.subsampling_x == 1 { r.f(1)? as u8 } else { 0 }; + } else { + c.subsampling_x = 1; + } + } + } + if c.subsampling_x == 1 && c.subsampling_y == 1 { + c.chroma_sample_position = r.f(2)? as u8; + } + } + c.separate_uv_delta_q = r.flag()?; + Ok(c) +} + +/// Tile layout (5.9.15). +#[derive(Debug, Clone, Default)] +pub struct TileInfo { + pub cols: usize, + pub rows: usize, + pub cols_log2: u32, + pub rows_log2: u32, + pub mi_col_starts: Vec, + pub mi_row_starts: Vec, + pub context_update_tile_id: u32, + pub tile_size_bytes: u32, +} + +#[derive(Debug, Clone, Default)] +pub struct QuantParams { + pub base_q_idx: u32, + pub delta_q_y_dc: i32, + pub delta_q_u_dc: i32, + pub delta_q_u_ac: i32, + pub delta_q_v_dc: i32, + pub delta_q_v_ac: i32, + pub using_qmatrix: bool, + pub qm_y: u32, + pub qm_u: u32, + pub qm_v: u32, +} + +#[derive(Debug, Clone, Copy, Default, PartialEq, Eq)] +pub struct SegmentationFeatures { + pub enabled: [[bool; SEG_LVL_MAX]; MAX_SEGMENTS], + pub data: [[i32; SEG_LVL_MAX]; MAX_SEGMENTS], +} + +#[derive(Debug, Clone, Default)] +pub struct Segmentation { + pub enabled: bool, + pub update_map: bool, + pub temporal_update: bool, + pub update_data: bool, + pub features: SegmentationFeatures, + pub seg_id_pre_skip: bool, + pub last_active_seg_id: u32, +} + +#[derive(Debug, Clone, Copy, Default, PartialEq, Eq)] +pub struct LoopFilterDeltas { + pub ref_deltas: [i32; TOTAL_REFS_PER_FRAME], + pub mode_deltas: [i32; 2], +} + +impl LoopFilterDeltas { + pub fn defaults() -> Self { + LoopFilterDeltas { ref_deltas: [1, 0, 0, 0, -1, 0, -1, -1], mode_deltas: [0, 0] } + } +} + +#[derive(Debug, Clone, Default)] +pub struct LoopFilterParams { + pub level: [u32; 4], + pub sharpness: u32, + pub delta_enabled: bool, + pub delta_update: bool, + pub deltas: LoopFilterDeltas, +} + +#[derive(Debug, Clone, Default)] +pub struct CdefParams { + pub damping: u32, + pub bits: u32, + pub y_pri: [u32; 8], + pub y_sec: [u32; 8], + pub uv_pri: [u32; 8], + pub uv_sec: [u32; 8], +} + +#[derive(Debug, Clone, Default)] +pub struct LrParams { + pub frame_restoration_type: [u8; 3], + pub uses_lr: bool, + pub loop_restoration_size: [u32; 3], +} + +/// Film grain parameters (5.9.30). +#[derive(Debug, Clone, Default, PartialEq, Eq)] +pub struct FilmGrainParams { + pub apply_grain: bool, + pub grain_seed: u32, + pub update_grain: bool, + pub num_y_points: usize, + pub point_y_value: [u8; 16], + pub point_y_scaling: [u8; 16], + pub chroma_scaling_from_luma: bool, + pub num_cb_points: usize, + pub point_cb_value: [u8; 16], + pub point_cb_scaling: [u8; 16], + pub num_cr_points: usize, + pub point_cr_value: [u8; 16], + pub point_cr_scaling: [u8; 16], + pub grain_scaling_minus_8: u32, + pub ar_coeff_lag: u32, + pub ar_coeffs_y_plus_128: [u8; 24], + pub ar_coeffs_cb_plus_128: [u8; 25], + pub ar_coeffs_cr_plus_128: [u8; 25], + pub ar_coeff_shift_minus_6: u32, + pub grain_scale_shift: u32, + pub cb_mult: u32, + pub cb_luma_mult: u32, + pub cb_offset: u32, + pub cr_mult: u32, + pub cr_luma_mult: u32, + pub cr_offset: u32, + pub overlap_flag: bool, + pub clip_to_restricted_range: bool, +} + +/// The values of one reference slot that the frame header needs (7.20). +#[derive(Debug, Clone, Default)] +pub struct RefInfo { + pub valid: bool, + pub frame_id: u32, + pub upscaled_width: u32, + pub frame_width: u32, + pub frame_height: u32, + pub render_width: u32, + pub render_height: u32, + pub mi_cols: u32, + pub mi_rows: u32, + pub frame_type: u8, + pub order_hint: u32, + pub saved_order_hints: [u32; 8], + pub gm_params: [[i32; 6]; 8], + pub lf_deltas: LoopFilterDeltas, + pub seg_features: SegmentationFeatures, + pub grain: FilmGrainParams, +} + +/// A parsed uncompressed frame header with its derived variables. +#[derive(Debug, Clone, Default)] +pub struct FrameHeader { + pub show_existing_frame: bool, + pub frame_to_show_map_idx: usize, + pub frame_type: u8, + pub frame_is_intra: bool, + pub show_frame: bool, + pub showable_frame: bool, + pub error_resilient_mode: bool, + pub disable_cdf_update: bool, + pub allow_screen_content_tools: bool, + pub force_integer_mv: bool, + pub current_frame_id: u32, + pub frame_size_override_flag: bool, + pub order_hint: u32, + pub primary_ref_frame: usize, + pub refresh_frame_flags: u32, + pub allow_intrabc: bool, + pub ref_frame_idx: [usize; REFS_PER_FRAME], + pub allow_high_precision_mv: bool, + pub interpolation_filter: u8, + pub is_motion_mode_switchable: bool, + pub use_ref_frame_mvs: bool, + /// OrderHints[ LAST_FRAME..ALTREF_FRAME ] (index 0 unused). + pub order_hints: [u32; 8], + pub ref_frame_sign_bias: [bool; 8], + pub disable_frame_end_update_cdf: bool, + pub frame_width: u32, + pub frame_height: u32, + pub upscaled_width: u32, + pub render_width: u32, + pub render_height: u32, + pub use_superres: bool, + pub superres_denom: u32, + pub mi_cols: u32, + pub mi_rows: u32, + pub tile_info: TileInfo, + pub quant: QuantParams, + pub seg: Segmentation, + pub delta_q_present: bool, + pub delta_q_res: u32, + pub delta_lf_present: bool, + pub delta_lf_res: u32, + pub delta_lf_multi: bool, + pub coded_lossless: bool, + pub all_lossless: bool, + pub lossless_array: [bool; MAX_SEGMENTS], + pub seg_qm_level: [[u32; MAX_SEGMENTS]; 3], + pub lf: LoopFilterParams, + pub cdef: CdefParams, + pub lr: LrParams, + pub tx_mode: u8, + pub reference_select: bool, + pub skip_mode_present: bool, + pub skip_mode_frame: [usize; 2], + pub allow_warped_motion: bool, + pub reduced_tx_set: bool, + pub gm_type: [u8; 8], + pub gm_params: [[i32; 6]; 8], + pub prev_gm_params: [[i32; 6]; 8], + pub film_grain: FilmGrainParams, + /// Temporal / spatial id of the OBU carrying the header. + pub temporal_id: u32, + pub spatial_id: u32, +} + +impl FrameHeader { + pub fn get_relative_dist(&self, seq: &SequenceHeader, a: u32, b: u32) -> i32 { + relative_dist(seq, a, b) + } + + /// get_qindex( ignoreDeltaQ, segmentId ) with CurrentQIndex. + pub fn get_qindex(&self, ignore_delta_q: bool, segment_id: usize, current_q_index: i32) -> i32 { + let base = self.quant.base_q_idx as i32; + if self.seg.enabled && self.seg.features.enabled[segment_id][SEG_LVL_ALT_Q] { + let data = self.seg.features.data[segment_id][SEG_LVL_ALT_Q]; + let mut q = base + data; + if !ignore_delta_q && self.delta_q_present { + q = current_q_index + data; + } + q.clamp(0, 255) + } else if !ignore_delta_q && self.delta_q_present { + current_q_index + } else { + base + } + } +} + +pub(crate) fn relative_dist(seq: &SequenceHeader, a: u32, b: u32) -> i32 { + if !seq.enable_order_hint { + return 0; + } + let diff = a as i32 - b as i32; + let m = 1i32 << (seq.order_hint_bits - 1); + (diff & (m - 1)) - (diff & m) +} + +/// Decoder state the frame header reads and updates across frames. +pub struct HeaderState<'a> { + pub seq: &'a SequenceHeader, + pub refs: &'a mut [RefInfo; NUM_REF_FRAMES], + /// Loop filter deltas carried from the previous frame (setup_past_independence / load_previous). + pub lf_deltas: LoopFilterDeltas, + pub seg_features: SegmentationFeatures, + pub prev_gm_params: [[i32; 6]; 8], + pub current_frame_id: u32, +} + +fn tile_log2(blk: u32, target: u32) -> u32 { + let mut k = 0; + while (blk << k) < target { + k += 1; + } + k +} + +impl FrameHeader { + /// uncompressed_header( ). `st` holds the cross-frame state. + pub(crate) fn parse(r: &mut BitReader, st: &mut HeaderState, temporal_id: u32, spatial_id: u32) -> Result { + let seq = st.seq; + let mut h = FrameHeader { temporal_id, spatial_id, ..Default::default() }; + let id_len = if seq.frame_id_numbers_present { seq.additional_frame_id_length_minus_1 + seq.delta_frame_id_length_minus_2 + 3 } else { 0 }; + let all_frames = (1u32 << NUM_REF_FRAMES) - 1; + if seq.reduced_still_picture_header { + h.frame_type = KEY_FRAME as u8; + h.frame_is_intra = true; + h.show_frame = true; + } else { + h.show_existing_frame = r.flag()?; + if h.show_existing_frame { + h.frame_to_show_map_idx = r.f(3)? as usize; + if seq.decoder_model_info_present && !seq.equal_picture_interval { + r.f(seq.frame_presentation_time_length_minus_1 + 1)?; + } + if seq.frame_id_numbers_present { + r.f(id_len)?; + } + let rs = &st.refs[h.frame_to_show_map_idx]; + if !rs.valid { + return Err(Error::Invalid("show_existing_frame of an empty slot")); + } + h.frame_type = rs.frame_type; + if h.frame_type == KEY_FRAME as u8 { + h.refresh_frame_flags = all_frames; + } + if seq.film_grain_params_present { + h.film_grain = rs.grain.clone(); + } + return Ok(h); + } + h.frame_type = r.f(2)? as u8; + h.frame_is_intra = h.frame_type == INTRA_ONLY_FRAME as u8 || h.frame_type == KEY_FRAME as u8; + h.show_frame = r.flag()?; + if h.show_frame && seq.decoder_model_info_present && !seq.equal_picture_interval { + r.f(seq.frame_presentation_time_length_minus_1 + 1)?; + } + h.showable_frame = if h.show_frame { h.frame_type != KEY_FRAME as u8 } else { r.flag()? }; + h.error_resilient_mode = + if h.frame_type == SWITCH_FRAME as u8 || (h.frame_type == KEY_FRAME as u8 && h.show_frame) { true } else { r.flag()? }; + } + if h.frame_type == KEY_FRAME as u8 && h.show_frame { + for rs in st.refs.iter_mut() { + rs.valid = false; + rs.order_hint = 0; + } + for i in 0..REFS_PER_FRAME { + h.order_hints[LAST_FRAME + i] = 0; + } + } + h.disable_cdf_update = r.flag()?; + h.allow_screen_content_tools = if seq.seq_force_screen_content_tools == SELECT_SCREEN_CONTENT_TOOLS as u32 { + r.flag()? + } else { + seq.seq_force_screen_content_tools != 0 + }; + if h.allow_screen_content_tools { + h.force_integer_mv = if seq.seq_force_integer_mv == SELECT_INTEGER_MV as u32 { r.flag()? } else { seq.seq_force_integer_mv != 0 }; + } + if h.frame_is_intra { + h.force_integer_mv = true; + } + if seq.frame_id_numbers_present { + h.current_frame_id = r.f(id_len)?; + // mark_ref_frames( idLen ) + let diff_len = seq.delta_frame_id_length_minus_2 + 2; + let cur = h.current_frame_id; + for rs in st.refs.iter_mut() { + if cur > (1 << diff_len) { + if rs.frame_id > cur || rs.frame_id < cur - (1 << diff_len) { + rs.valid = false; + } + } else if rs.frame_id > cur && rs.frame_id < (1 << id_len) + cur - (1 << diff_len) { + rs.valid = false; + } + } + st.current_frame_id = cur; + } + h.frame_size_override_flag = if h.frame_type == SWITCH_FRAME as u8 { + true + } else if seq.reduced_still_picture_header { + false + } else { + r.flag()? + }; + h.order_hint = r.f(seq.order_hint_bits)?; + h.primary_ref_frame = if h.frame_is_intra || h.error_resilient_mode { PRIMARY_REF_NONE } else { r.f(3)? as usize }; + if seq.decoder_model_info_present && r.flag()? { + for op in 0..=seq.operating_points_cnt_minus_1 { + if seq.decoder_model_present_for_this_op[op] { + let idc = seq.operating_point_idc[op]; + let in_t = (idc >> temporal_id) & 1; + let in_s = (idc >> (spatial_id + 8)) & 1; + if idc == 0 || (in_t == 1 && in_s == 1) { + r.f(seq.buffer_removal_time_length_minus_1 + 1)?; + } + } + } + } + h.refresh_frame_flags = + if h.frame_type == SWITCH_FRAME as u8 || (h.frame_type == KEY_FRAME as u8 && h.show_frame) { all_frames } else { r.f(8)? }; + if (!h.frame_is_intra || h.refresh_frame_flags != all_frames) && h.error_resilient_mode && seq.enable_order_hint { + for rs in st.refs.iter_mut() { + let roh = r.f(seq.order_hint_bits)?; + if roh != rs.order_hint { + rs.valid = false; + } + } + } + if h.frame_is_intra { + h.frame_size(r, seq)?; + h.render_size(r)?; + if h.allow_screen_content_tools && h.upscaled_width == h.frame_width { + h.allow_intrabc = r.flag()?; + } + } else { + let mut short = false; + if seq.enable_order_hint { + short = r.flag()?; + if short { + let last = r.f(3)? as usize; + let gold = r.f(3)? as usize; + h.set_frame_refs(seq, st.refs, last, gold); + } + } + for i in 0..REFS_PER_FRAME { + if !short { + h.ref_frame_idx[i] = r.f(3)? as usize; + } + if seq.frame_id_numbers_present { + r.f(seq.delta_frame_id_length_minus_2 + 2)?; + } + } + for i in 0..REFS_PER_FRAME { + if !st.refs[h.ref_frame_idx[i]].valid { + return Err(Error::Invalid("reference to an empty slot")); + } + } + if h.frame_size_override_flag && !h.error_resilient_mode { + // frame_size_with_refs( ) + let mut found = false; + for i in 0..REFS_PER_FRAME { + if r.flag()? { + let rs = &st.refs[h.ref_frame_idx[i]]; + h.upscaled_width = rs.upscaled_width; + h.frame_width = h.upscaled_width; + h.frame_height = rs.frame_height; + h.render_width = rs.render_width; + h.render_height = rs.render_height; + found = true; + break; + } + } + if !found { + h.frame_size(r, seq)?; + h.render_size(r)?; + } else { + h.superres_params(r, seq)?; + h.compute_image_size(); + } + } else { + h.frame_size(r, seq)?; + h.render_size(r)?; + } + h.allow_high_precision_mv = if h.force_integer_mv { false } else { r.flag()? }; + h.interpolation_filter = if r.flag()? { SWITCHABLE } else { r.f(2)? as u8 }; + h.is_motion_mode_switchable = r.flag()?; + h.use_ref_frame_mvs = if h.error_resilient_mode || !seq.enable_ref_frame_mvs { false } else { r.flag()? }; + for i in 0..REFS_PER_FRAME { + let rf = LAST_FRAME + i; + let hint = st.refs[h.ref_frame_idx[i]].order_hint; + h.order_hints[rf] = hint; + h.ref_frame_sign_bias[rf] = seq.enable_order_hint && relative_dist(seq, hint, h.order_hint) > 0; + } + } + h.disable_frame_end_update_cdf = if seq.reduced_still_picture_header || h.disable_cdf_update { true } else { r.flag()? }; + if h.primary_ref_frame == PRIMARY_REF_NONE { + // setup_past_independence( ) + st.seg_features = SegmentationFeatures::default(); + st.prev_gm_params = default_gm_params(); + st.lf_deltas = LoopFilterDeltas::defaults(); + h.lf.delta_enabled = true; + } else { + // load_previous( ) + let prev = &st.refs[h.ref_frame_idx[h.primary_ref_frame]]; + st.prev_gm_params = prev.gm_params; + st.lf_deltas = prev.lf_deltas; + st.seg_features = prev.seg_features; + } + h.prev_gm_params = st.prev_gm_params; + h.tile_info(r, seq)?; + h.quantization_params(r, seq)?; + h.segmentation_params(r, st)?; + // delta_q_params( ) + if h.quant.base_q_idx > 0 { + h.delta_q_present = r.flag()?; + } + if h.delta_q_present { + h.delta_q_res = r.f(2)?; + } + // delta_lf_params( ) + if h.delta_q_present { + if !h.allow_intrabc { + h.delta_lf_present = r.flag()?; + } + if h.delta_lf_present { + h.delta_lf_res = r.f(2)?; + h.delta_lf_multi = r.flag()?; + } + } + h.coded_lossless = true; + for seg_id in 0..MAX_SEGMENTS { + let q = h.get_qindex(true, seg_id, 0); + let ll = q == 0 + && h.quant.delta_q_y_dc == 0 + && h.quant.delta_q_u_ac == 0 + && h.quant.delta_q_u_dc == 0 + && h.quant.delta_q_v_ac == 0 + && h.quant.delta_q_v_dc == 0; + h.lossless_array[seg_id] = ll; + if !ll { + h.coded_lossless = false; + } + if h.quant.using_qmatrix { + if ll { + h.seg_qm_level[0][seg_id] = 15; + h.seg_qm_level[1][seg_id] = 15; + h.seg_qm_level[2][seg_id] = 15; + } else { + h.seg_qm_level[0][seg_id] = h.quant.qm_y; + h.seg_qm_level[1][seg_id] = h.quant.qm_u; + h.seg_qm_level[2][seg_id] = h.quant.qm_v; + } + } + } + h.all_lossless = h.coded_lossless && h.frame_width == h.upscaled_width; + h.loop_filter_params(r, seq, st)?; + h.cdef_params(r, seq)?; + h.lr_params(r, seq)?; + // read_tx_mode( ) + h.tx_mode = if h.coded_lossless { + ONLY_4X4 as u8 + } else if r.flag()? { + TX_MODE_SELECT as u8 + } else { + TX_MODE_LARGEST as u8 + }; + h.reference_select = if h.frame_is_intra { false } else { r.flag()? }; + h.skip_mode_params(r, seq, st.refs)?; + h.allow_warped_motion = if h.frame_is_intra || h.error_resilient_mode || !seq.enable_warped_motion { false } else { r.flag()? }; + h.reduced_tx_set = r.flag()?; + h.global_motion_params(r)?; + h.film_grain_params(r, seq, st.refs)?; + Ok(h) + } + + fn superres_params(&mut self, r: &mut BitReader, seq: &SequenceHeader) -> Result<()> { + self.use_superres = if seq.enable_superres { r.flag()? } else { false }; + self.superres_denom = if self.use_superres { r.f(SUPERRES_DENOM_BITS as u32)? + SUPERRES_DENOM_MIN as u32 } else { SUPERRES_NUM as u32 }; + self.upscaled_width = self.frame_width; + self.frame_width = (self.upscaled_width * SUPERRES_NUM as u32 + self.superres_denom / 2) / self.superres_denom; + Ok(()) + } + + fn compute_image_size(&mut self) { + self.mi_cols = 2 * ((self.frame_width + 7) >> 3); + self.mi_rows = 2 * ((self.frame_height + 7) >> 3); + } + + fn frame_size(&mut self, r: &mut BitReader, seq: &SequenceHeader) -> Result<()> { + if self.frame_size_override_flag { + self.frame_width = r.f(seq.frame_width_bits)? + 1; + self.frame_height = r.f(seq.frame_height_bits)? + 1; + } else { + self.frame_width = seq.max_frame_width; + self.frame_height = seq.max_frame_height; + } + self.superres_params(r, seq)?; + self.compute_image_size(); + Ok(()) + } + + fn render_size(&mut self, r: &mut BitReader) -> Result<()> { + if r.flag()? { + self.render_width = r.f(16)? + 1; + self.render_height = r.f(16)? + 1; + } else { + self.render_width = self.upscaled_width; + self.render_height = self.frame_height; + } + Ok(()) + } + + /// Set frame refs process (7.8). + fn set_frame_refs(&mut self, seq: &SequenceHeader, refs: &[RefInfo; NUM_REF_FRAMES], last: usize, gold: usize) { + let mut idx = [-1i32; REFS_PER_FRAME]; + idx[0] = last as i32; + idx[GOLDEN_FRAME - LAST_FRAME] = gold as i32; + let mut used = [false; NUM_REF_FRAMES]; + used[last] = true; + used[gold] = true; + let cur = 1i32 << (seq.order_hint_bits - 1); + let shifted: Vec = (0..NUM_REF_FRAMES).map(|i| cur + relative_dist(seq, refs[i].order_hint, self.order_hint)).collect(); + // ALTREF: latest backward + { + let mut rf = -1i32; + let mut latest = 0; + for i in 0..NUM_REF_FRAMES { + let hint = shifted[i]; + if !used[i] && hint >= cur && (rf < 0 || hint >= latest) { + rf = i as i32; + latest = hint; + } + } + if rf >= 0 { + idx[ALTREF_FRAME - LAST_FRAME] = rf; + used[rf as usize] = true; + } + } + // BWDREF then ALTREF2: earliest backward + for target in [BWDREF_FRAME, ALTREF2_FRAME] { + let mut rf = -1i32; + let mut earliest = 0; + for i in 0..NUM_REF_FRAMES { + let hint = shifted[i]; + if !used[i] && hint >= cur && (rf < 0 || hint < earliest) { + rf = i as i32; + earliest = hint; + } + } + if rf >= 0 { + idx[target - LAST_FRAME] = rf; + used[rf as usize] = true; + } + } + for &rf_frame in REF_FRAME_LIST.iter() { + let rf_frame = rf_frame as usize; + if idx[rf_frame - LAST_FRAME] < 0 { + let mut rf = -1i32; + let mut latest = 0; + for i in 0..NUM_REF_FRAMES { + let hint = shifted[i]; + if !used[i] && hint < cur && (rf < 0 || hint >= latest) { + rf = i as i32; + latest = hint; + } + } + if rf >= 0 { + idx[rf_frame - LAST_FRAME] = rf; + used[rf as usize] = true; + } + } + } + let mut rf = -1i32; + let mut earliest = 0; + for i in 0..NUM_REF_FRAMES { + let hint = shifted[i]; + if rf < 0 || hint < earliest { + rf = i as i32; + earliest = hint; + } + } + for i in 0..REFS_PER_FRAME { + if idx[i] < 0 { + idx[i] = rf; + } + self.ref_frame_idx[i] = idx[i] as usize; + } + } + + fn tile_info(&mut self, r: &mut BitReader, seq: &SequenceHeader) -> Result<()> { + let sb128 = seq.use_128x128_superblock; + let sb_cols = if sb128 { (self.mi_cols + 31) >> 5 } else { (self.mi_cols + 15) >> 4 }; + let sb_rows = if sb128 { (self.mi_rows + 31) >> 5 } else { (self.mi_rows + 15) >> 4 }; + let sb_shift = if sb128 { 5 } else { 4 }; + let sb_size = sb_shift + 2; + let max_tile_width_sb = MAX_TILE_WIDTH as u32 >> sb_size; + let mut max_tile_area_sb = MAX_TILE_AREA as u32 >> (2 * sb_size); + let min_log2_tile_cols = tile_log2(max_tile_width_sb, sb_cols); + let max_log2_tile_cols = tile_log2(1, sb_cols.min(MAX_TILE_COLS as u32)); + let max_log2_tile_rows = tile_log2(1, sb_rows.min(MAX_TILE_ROWS as u32)); + let min_log2_tiles = min_log2_tile_cols.max(tile_log2(max_tile_area_sb, sb_rows * sb_cols)); + let t = &mut self.tile_info; + t.mi_col_starts.clear(); + t.mi_row_starts.clear(); + if r.flag()? { + t.cols_log2 = min_log2_tile_cols; + while t.cols_log2 < max_log2_tile_cols { + if r.flag()? { + t.cols_log2 += 1; + } else { + break; + } + } + let tile_width_sb = (sb_cols + (1 << t.cols_log2) - 1) >> t.cols_log2; + let mut start = 0; + while start < sb_cols { + t.mi_col_starts.push(start << sb_shift); + start += tile_width_sb; + } + t.cols = t.mi_col_starts.len(); + t.mi_col_starts.push(self.mi_cols); + let min_log2_tile_rows = min_log2_tiles.saturating_sub(t.cols_log2); + t.rows_log2 = min_log2_tile_rows; + while t.rows_log2 < max_log2_tile_rows { + if r.flag()? { + t.rows_log2 += 1; + } else { + break; + } + } + let tile_height_sb = (sb_rows + (1 << t.rows_log2) - 1) >> t.rows_log2; + let mut start = 0; + while start < sb_rows { + t.mi_row_starts.push(start << sb_shift); + start += tile_height_sb; + } + t.rows = t.mi_row_starts.len(); + t.mi_row_starts.push(self.mi_rows); + } else { + let mut widest = 0; + let mut start = 0; + while start < sb_cols { + t.mi_col_starts.push(start << sb_shift); + let max_width = (sb_cols - start).min(max_tile_width_sb); + let size = r.ns(max_width)? + 1; + widest = widest.max(size); + start += size; + } + t.cols = t.mi_col_starts.len(); + t.mi_col_starts.push(self.mi_cols); + t.cols_log2 = tile_log2(1, t.cols as u32); + if min_log2_tiles > 0 { + max_tile_area_sb = (sb_rows * sb_cols) >> (min_log2_tiles + 1); + } else { + max_tile_area_sb = sb_rows * sb_cols; + } + let max_tile_height_sb = (max_tile_area_sb / widest.max(1)).max(1); + let mut start = 0; + while start < sb_rows { + t.mi_row_starts.push(start << sb_shift); + let max_height = (sb_rows - start).min(max_tile_height_sb); + let size = r.ns(max_height)? + 1; + start += size; + } + t.rows = t.mi_row_starts.len(); + t.mi_row_starts.push(self.mi_rows); + t.rows_log2 = tile_log2(1, t.rows as u32); + } + if t.cols_log2 > 0 || t.rows_log2 > 0 { + t.context_update_tile_id = r.f(t.rows_log2 + t.cols_log2)?; + t.tile_size_bytes = r.f(2)? + 1; + } else { + t.context_update_tile_id = 0; + } + if t.context_update_tile_id as usize >= t.cols * t.rows { + return Err(Error::Invalid("context_update_tile_id")); + } + Ok(()) + } + + fn quantization_params(&mut self, r: &mut BitReader, seq: &SequenceHeader) -> Result<()> { + let q = &mut self.quant; + q.base_q_idx = r.f(8)?; + let read_delta_q = |r: &mut BitReader| -> Result { if r.flag()? { r.su(7) } else { Ok(0) } }; + q.delta_q_y_dc = read_delta_q(r)?; + if seq.color.num_planes > 1 { + let diff_uv_delta = if seq.color.separate_uv_delta_q { r.flag()? } else { false }; + q.delta_q_u_dc = read_delta_q(r)?; + q.delta_q_u_ac = read_delta_q(r)?; + if diff_uv_delta { + q.delta_q_v_dc = read_delta_q(r)?; + q.delta_q_v_ac = read_delta_q(r)?; + } else { + q.delta_q_v_dc = q.delta_q_u_dc; + q.delta_q_v_ac = q.delta_q_u_ac; + } + } + q.using_qmatrix = r.flag()?; + if q.using_qmatrix { + q.qm_y = r.f(4)?; + q.qm_u = r.f(4)?; + q.qm_v = if !seq.color.separate_uv_delta_q { q.qm_u } else { r.f(4)? }; + } + Ok(()) + } + + fn segmentation_params(&mut self, r: &mut BitReader, st: &mut HeaderState) -> Result<()> { + const BITS: [u32; SEG_LVL_MAX] = [8, 6, 6, 6, 6, 3, 0, 0]; + const SIGNED: [bool; SEG_LVL_MAX] = [true, true, true, true, true, false, false, false]; + const MAX: [i32; SEG_LVL_MAX] = [255, 63, 63, 63, 63, 7, 0, 0]; + let s = &mut self.seg; + s.enabled = r.flag()?; + if s.enabled { + if self.primary_ref_frame == PRIMARY_REF_NONE { + s.update_map = true; + s.temporal_update = false; + s.update_data = true; + } else { + s.update_map = r.flag()?; + if s.update_map { + s.temporal_update = r.flag()?; + } + s.update_data = r.flag()?; + } + if s.update_data { + for i in 0..MAX_SEGMENTS { + for j in 0..SEG_LVL_MAX { + let en = r.flag()?; + st.seg_features.enabled[i][j] = en; + let mut v = 0; + if en { + let bits = BITS[j]; + let limit = MAX[j]; + if SIGNED[j] { + v = r.su(1 + bits)?.clamp(-limit, limit); + } else { + v = (r.f(bits)? as i32).clamp(0, limit); + } + } + st.seg_features.data[i][j] = v; + } + } + } + } else { + st.seg_features = SegmentationFeatures::default(); + } + s.features = st.seg_features; + s.seg_id_pre_skip = false; + s.last_active_seg_id = 0; + for i in 0..MAX_SEGMENTS { + for j in 0..SEG_LVL_MAX { + if s.features.enabled[i][j] { + s.last_active_seg_id = i as u32; + if j >= SEG_LVL_REF_FRAME { + s.seg_id_pre_skip = true; + } + } + } + } + Ok(()) + } + + fn loop_filter_params(&mut self, r: &mut BitReader, seq: &SequenceHeader, st: &mut HeaderState) -> Result<()> { + let lf = &mut self.lf; + if self.coded_lossless || self.allow_intrabc { + lf.level = [0; 4]; + st.lf_deltas = LoopFilterDeltas::defaults(); + lf.deltas = st.lf_deltas; + return Ok(()); + } + lf.level[0] = r.f(6)?; + lf.level[1] = r.f(6)?; + if seq.color.num_planes > 1 && (lf.level[0] != 0 || lf.level[1] != 0) { + lf.level[2] = r.f(6)?; + lf.level[3] = r.f(6)?; + } + lf.sharpness = r.f(3)?; + lf.delta_enabled = r.flag()?; + if lf.delta_enabled { + lf.delta_update = r.flag()?; + if lf.delta_update { + for i in 0..TOTAL_REFS_PER_FRAME { + if r.flag()? { + st.lf_deltas.ref_deltas[i] = r.su(7)?; + } + } + for i in 0..2 { + if r.flag()? { + st.lf_deltas.mode_deltas[i] = r.su(7)?; + } + } + } + } + lf.deltas = st.lf_deltas; + Ok(()) + } + + fn cdef_params(&mut self, r: &mut BitReader, seq: &SequenceHeader) -> Result<()> { + let c = &mut self.cdef; + if self.coded_lossless || self.allow_intrabc || !seq.enable_cdef { + c.bits = 0; + c.y_pri[0] = 0; + c.y_sec[0] = 0; + c.uv_pri[0] = 0; + c.uv_sec[0] = 0; + c.damping = 3; + return Ok(()); + } + c.damping = r.f(2)? + 3; + c.bits = r.f(2)?; + for i in 0..(1 << c.bits) { + c.y_pri[i] = r.f(4)?; + c.y_sec[i] = r.f(2)?; + if c.y_sec[i] == 3 { + c.y_sec[i] += 1; + } + if seq.color.num_planes > 1 { + c.uv_pri[i] = r.f(4)?; + c.uv_sec[i] = r.f(2)?; + if c.uv_sec[i] == 3 { + c.uv_sec[i] += 1; + } + } + } + Ok(()) + } + + fn lr_params(&mut self, r: &mut BitReader, seq: &SequenceHeader) -> Result<()> { + const REMAP: [u8; 4] = [RESTORE_NONE as u8, RESTORE_SWITCHABLE as u8, RESTORE_WIENER as u8, RESTORE_SGRPROJ as u8]; + let l = &mut self.lr; + if self.all_lossless || self.allow_intrabc || !seq.enable_restoration { + l.frame_restoration_type = [RESTORE_NONE as u8; 3]; + l.uses_lr = false; + return Ok(()); + } + let mut uses_chroma = false; + for i in 0..seq.color.num_planes { + let t = REMAP[r.f(2)? as usize]; + l.frame_restoration_type[i] = t; + if t != RESTORE_NONE as u8 { + l.uses_lr = true; + if i > 0 { + uses_chroma = true; + } + } + } + if l.uses_lr { + let mut shift; + if seq.use_128x128_superblock { + shift = r.f(1)?; + shift += 1; + } else { + shift = r.f(1)?; + if shift != 0 { + shift += r.f(1)?; + } + } + l.loop_restoration_size[0] = (RESTORATION_TILESIZE_MAX as u32) >> (2 - shift); + let uv_shift = if seq.color.subsampling_x == 1 && seq.color.subsampling_y == 1 && uses_chroma { r.f(1)? } else { 0 }; + l.loop_restoration_size[1] = l.loop_restoration_size[0] >> uv_shift; + l.loop_restoration_size[2] = l.loop_restoration_size[0] >> uv_shift; + } + Ok(()) + } + + fn skip_mode_params(&mut self, r: &mut BitReader, seq: &SequenceHeader, refs: &[RefInfo; NUM_REF_FRAMES]) -> Result<()> { + let mut allowed = false; + if !(self.frame_is_intra || !self.reference_select || !seq.enable_order_hint) { + let mut fwd = -1i32; + let mut bwd = -1i32; + let (mut fwd_hint, mut bwd_hint) = (0u32, 0u32); + for i in 0..REFS_PER_FRAME { + let ref_hint = refs[self.ref_frame_idx[i]].order_hint; + if relative_dist(seq, ref_hint, self.order_hint) < 0 { + if fwd < 0 || relative_dist(seq, ref_hint, fwd_hint) > 0 { + fwd = i as i32; + fwd_hint = ref_hint; + } + } else if relative_dist(seq, ref_hint, self.order_hint) > 0 && (bwd < 0 || relative_dist(seq, ref_hint, bwd_hint) < 0) { + bwd = i as i32; + bwd_hint = ref_hint; + } + } + if fwd < 0 { + allowed = false; + } else if bwd >= 0 { + allowed = true; + self.skip_mode_frame = [LAST_FRAME + fwd.min(bwd) as usize, LAST_FRAME + fwd.max(bwd) as usize]; + } else { + let mut second = -1i32; + let mut second_hint = 0u32; + for i in 0..REFS_PER_FRAME { + let ref_hint = refs[self.ref_frame_idx[i]].order_hint; + if relative_dist(seq, ref_hint, fwd_hint) < 0 && (second < 0 || relative_dist(seq, ref_hint, second_hint) > 0) { + second = i as i32; + second_hint = ref_hint; + } + } + if second >= 0 { + allowed = true; + self.skip_mode_frame = [LAST_FRAME + fwd.min(second) as usize, LAST_FRAME + fwd.max(second) as usize]; + } + } + } + self.skip_mode_present = if allowed { r.flag()? } else { false }; + Ok(()) + } + + fn global_motion_params(&mut self, r: &mut BitReader) -> Result<()> { + for rf in LAST_FRAME..=ALTREF_FRAME { + self.gm_type[rf] = IDENTITY as u8; + self.gm_params[rf] = default_gm_params()[rf]; + } + if self.frame_is_intra { + return Ok(()); + } + for rf in LAST_FRAME..=ALTREF_FRAME { + let typ = if r.flag()? { + if r.flag()? { + ROTZOOM + } else if r.flag()? { + TRANSLATION + } else { + AFFINE + } + } else { + IDENTITY + }; + self.gm_type[rf] = typ as u8; + if typ >= ROTZOOM { + self.read_global_param(r, typ, rf, 2)?; + self.read_global_param(r, typ, rf, 3)?; + if typ == AFFINE { + self.read_global_param(r, typ, rf, 4)?; + self.read_global_param(r, typ, rf, 5)?; + } else { + self.gm_params[rf][4] = -self.gm_params[rf][3]; + self.gm_params[rf][5] = self.gm_params[rf][2]; + } + } + if typ >= TRANSLATION { + self.read_global_param(r, typ, rf, 0)?; + self.read_global_param(r, typ, rf, 1)?; + } + } + Ok(()) + } + + fn read_global_param(&mut self, r: &mut BitReader, typ: usize, rf: usize, idx: usize) -> Result<()> { + let mut abs_bits = GM_ABS_ALPHA_BITS as u32; + let mut prec_bits = GM_ALPHA_PREC_BITS as u32; + if idx < 2 { + if typ == TRANSLATION { + let hp = !self.allow_high_precision_mv as u32; + abs_bits = GM_ABS_TRANS_ONLY_BITS as u32 - hp; + prec_bits = GM_TRANS_ONLY_PREC_BITS as u32 - hp; + } else { + abs_bits = GM_ABS_TRANS_BITS as u32; + prec_bits = GM_TRANS_PREC_BITS as u32; + } + } + let prec_diff = WARPEDMODEL_PREC_BITS as u32 - prec_bits; + let round = if idx % 3 == 2 { 1i32 << WARPEDMODEL_PREC_BITS } else { 0 }; + let sub = if idx % 3 == 2 { 1i32 << prec_bits } else { 0 }; + let mx = 1i32 << abs_bits; + let rr = (self.prev_gm_params[rf][idx] >> prec_diff) - sub; + let v = decode_signed_subexp_with_ref(r, -mx, mx + 1, rr)?; + self.gm_params[rf][idx] = (v << prec_diff) + round; + Ok(()) + } + + fn film_grain_params(&mut self, r: &mut BitReader, seq: &SequenceHeader, refs: &[RefInfo; NUM_REF_FRAMES]) -> Result<()> { + if !seq.film_grain_params_present || (!self.show_frame && !self.showable_frame) { + self.film_grain = FilmGrainParams::default(); + return Ok(()); + } + let mut g = FilmGrainParams { apply_grain: r.flag()?, ..Default::default() }; + if !g.apply_grain { + self.film_grain = FilmGrainParams::default(); + return Ok(()); + } + g.grain_seed = r.f(16)?; + g.update_grain = if self.frame_type == INTER_FRAME as u8 { r.flag()? } else { true }; + if !g.update_grain { + let idx = r.f(3)? as usize; + let seed = g.grain_seed; + g = refs[idx].grain.clone(); + g.grain_seed = seed; + self.film_grain = g; + return Ok(()); + } + g.num_y_points = r.f(4)? as usize; + if g.num_y_points > 14 { + return Err(Error::Invalid("num_y_points")); + } + for i in 0..g.num_y_points { + g.point_y_value[i] = r.f(8)? as u8; + g.point_y_scaling[i] = r.f(8)? as u8; + } + g.chroma_scaling_from_luma = if seq.color.mono_chrome { false } else { r.flag()? }; + if seq.color.mono_chrome + || g.chroma_scaling_from_luma + || (seq.color.subsampling_x == 1 && seq.color.subsampling_y == 1 && g.num_y_points == 0) + { + g.num_cb_points = 0; + g.num_cr_points = 0; + } else { + g.num_cb_points = r.f(4)? as usize; + if g.num_cb_points > 10 { + return Err(Error::Invalid("num_cb_points")); + } + for i in 0..g.num_cb_points { + g.point_cb_value[i] = r.f(8)? as u8; + g.point_cb_scaling[i] = r.f(8)? as u8; + } + g.num_cr_points = r.f(4)? as usize; + if g.num_cr_points > 10 { + return Err(Error::Invalid("num_cr_points")); + } + for i in 0..g.num_cr_points { + g.point_cr_value[i] = r.f(8)? as u8; + g.point_cr_scaling[i] = r.f(8)? as u8; + } + } + g.grain_scaling_minus_8 = r.f(2)?; + g.ar_coeff_lag = r.f(2)?; + let num_pos_luma = 2 * g.ar_coeff_lag * (g.ar_coeff_lag + 1); + let num_pos_chroma = if g.num_y_points > 0 { + for i in 0..num_pos_luma as usize { + g.ar_coeffs_y_plus_128[i] = r.f(8)? as u8; + } + num_pos_luma + 1 + } else { + num_pos_luma + }; + if g.chroma_scaling_from_luma || g.num_cb_points > 0 { + for i in 0..num_pos_chroma as usize { + g.ar_coeffs_cb_plus_128[i] = r.f(8)? as u8; + } + } + if g.chroma_scaling_from_luma || g.num_cr_points > 0 { + for i in 0..num_pos_chroma as usize { + g.ar_coeffs_cr_plus_128[i] = r.f(8)? as u8; + } + } + g.ar_coeff_shift_minus_6 = r.f(2)?; + g.grain_scale_shift = r.f(2)?; + if g.num_cb_points > 0 { + g.cb_mult = r.f(8)?; + g.cb_luma_mult = r.f(8)?; + g.cb_offset = r.f(9)?; + } + if g.num_cr_points > 0 { + g.cr_mult = r.f(8)?; + g.cr_luma_mult = r.f(8)?; + g.cr_offset = r.f(9)?; + } + g.overlap_flag = r.flag()?; + g.clip_to_restricted_range = r.flag()?; + self.film_grain = g; + Ok(()) + } +} + +pub(crate) fn default_gm_params() -> [[i32; 6]; 8] { + let mut p = [[0i32; 6]; 8]; + for row in p.iter_mut() { + row[2] = 1 << WARPEDMODEL_PREC_BITS; + row[5] = 1 << WARPEDMODEL_PREC_BITS; + } + p +} + +fn inverse_recenter(r: i32, v: i32) -> i32 { + if v > 2 * r { + v + } else if v & 1 == 1 { + r - ((v + 1) >> 1) + } else { + r + (v >> 1) + } +} + +fn decode_signed_subexp_with_ref(r: &mut BitReader, low: i32, high: i32, rr: i32) -> Result { + let x = decode_unsigned_subexp_with_ref(r, high - low, rr - low)?; + Ok(x + low) +} + +fn decode_unsigned_subexp_with_ref(r: &mut BitReader, mx: i32, rr: i32) -> Result { + let v = decode_subexp(r, mx)?; + Ok(if (rr << 1) <= mx { inverse_recenter(rr, v) } else { mx - 1 - inverse_recenter(mx - 1 - rr, v) }) +} + +fn decode_subexp(r: &mut BitReader, num_syms: i32) -> Result { + let mut i = 0; + let mut mk = 0; + let k = 3; + loop { + let b2 = if i > 0 { k + i - 1 } else { k }; + let a = 1 << b2; + if num_syms <= mk + 3 * a { + return Ok(r.ns((num_syms - mk) as u32)? as i32 + mk); + } else if r.flag()? { + i += 1; + mk += a; + } else { + return Ok(r.f(b2 as u32)? as i32 + mk); + } + } +} diff --git a/crates/av1/src/inter.rs b/crates/av1/src/inter.rs new file mode 100644 index 0000000..76c0ef1 --- /dev/null +++ b/crates/av1/src/inter.rs @@ -0,0 +1,817 @@ +//! Inter prediction (spec 7.11.3): motion vector scaling, block inter prediction, warped +//! motion, masks (wedge, difference weight, inter-intra), distance weights and OBMC. + +use std::sync::OnceLock; + +use crate::frame::Plane; +use crate::mvpred::round2_signed64; +use crate::spec_tables::*; +use crate::tile::TileDecoder; + +const PRED_STRIDE: usize = 128; + +#[inline(always)] +fn round2(x: i32, n: u32) -> i32 { + if n == 0 { x } else { (x + (1 << (n - 1))) >> n } +} + +/// Rounding variables (7.11.3.2): (InterRound0, InterRound1, InterPostRound). +pub(crate) fn rounding(is_compound: bool, bd: u32) -> (u32, u32, u32) { + let mut r0 = 3; + let mut r1 = if is_compound { 7 } else { 11 }; + if bd == 12 { + r0 += 2; + if !is_compound { + r1 -= 2; + } + } + (r0, r1, 2 * FILTER_BITS as u32 - (r0 + r1)) +} + +/// Resolve divisor process (7.11.3.7): (divShift, divFactor). +fn resolve_divisor(d: i64) -> (i32, i64) { + let a = d.unsigned_abs(); + let n = 63 - a.leading_zeros() as i32; + let e = a as i64 - (1i64 << n); + let f = if n > DIV_LUT_BITS as i32 { round2_signed64(e, (n - DIV_LUT_BITS as i32) as u32) } else { e << (DIV_LUT_BITS as i32 - n) }; + let shift = n + DIV_LUT_PREC_BITS as i32; + let factor = DIV_LUT[f as usize] as i64; + (shift, if d < 0 { -factor } else { factor }) +} + +/// Setup shear process (7.11.3.6): (warpValid, alpha, beta, gamma, delta). +pub(crate) fn setup_shear(p: &[i32; 6]) -> (bool, i32, i32, i32, i32) { + let prec = 1i64 << WARPEDMODEL_PREC_BITS; + let alpha0 = (p[2] as i64 - prec).clamp(-32768, 32767); + let beta0 = (p[3] as i64).clamp(-32768, 32767); + let (div_shift, div_factor) = resolve_divisor(p[2] as i64); + let v = (p[4] as i64) << WARPEDMODEL_PREC_BITS; + let gamma0 = round2_signed_wide(v as i128 * div_factor as i128, div_shift as u32).clamp(-32768, 32767); + let w = p[3] as i64 * p[4] as i64; + let delta0 = (p[5] as i64 - round2_signed_wide(w as i128 * div_factor as i128, div_shift as u32) - prec).clamp(-32768, 32767); + let rb = WARP_PARAM_REDUCE_BITS as u32; + let red = |x: i64| (round2_signed64(x, rb) << rb) as i32; + let (alpha, beta, gamma, delta) = (red(alpha0), red(beta0), red(gamma0), red(delta0)); + let valid = + !(4 * alpha.abs() + 7 * beta.abs() >= (1 << WARPEDMODEL_PREC_BITS) || 4 * gamma.abs() + 4 * delta.abs() >= (1 << WARPEDMODEL_PREC_BITS)); + (valid, alpha, beta, gamma, delta) +} + +fn round2_signed_wide(x: i128, n: u32) -> i64 { + if n == 0 { + return x as i64; + } + (if x >= 0 { (x + (1 << (n - 1))) >> n } else { -((-x + (1 << (n - 1))) >> n) }) as i64 +} + +/// WedgeMasks[ bsize ][ flipSign ][ wedge ] (7.11.3.11), stored as w*h bytes each. +fn wedge_masks() -> &'static Vec; 2]>> { + static M: OnceLock; 2]>>> = OnceLock::new(); + M.get_or_init(|| { + let n = MASK_MASTER_SIZE; + let mut master = vec![[[0u8; 64]; 64]; 6]; + for j in 0..n { + let mut shift = (n / 4) as i32; + let mut i = 0; + while i < n { + master[WEDGE_OBLIQUE63][i][j] = WEDGE_MASTER_OBLIQUE_EVEN[(j as i32 - shift).clamp(0, n as i32 - 1) as usize]; + shift -= 1; + master[WEDGE_OBLIQUE63][i + 1][j] = WEDGE_MASTER_OBLIQUE_ODD[(j as i32 - shift).clamp(0, n as i32 - 1) as usize]; + master[WEDGE_VERTICAL][i][j] = WEDGE_MASTER_VERTICAL[j]; + master[WEDGE_VERTICAL][i + 1][j] = WEDGE_MASTER_VERTICAL[j]; + i += 2; + } + } + for i in 0..n { + for j in 0..n { + let msk = master[WEDGE_OBLIQUE63][i][j]; + master[WEDGE_OBLIQUE27][j][i] = msk; + master[WEDGE_OBLIQUE117][i][n - 1 - j] = 64 - msk; + master[WEDGE_OBLIQUE153][n - 1 - j][i] = 64 - msk; + master[WEDGE_HORIZONTAL][j][i] = master[WEDGE_VERTICAL][i][j]; + } + } + let mut out = vec![Vec::new(); BLOCK_SIZES]; + for bsize in BLOCK_8X8..BLOCK_SIZES { + if WEDGE_BITS[bsize] == 0 { + continue; + } + let w = 4 * NUM_4X4_BLOCKS_WIDE[bsize] as usize; + let h = 4 * NUM_4X4_BLOCKS_HIGH[bsize] as usize; + let w4 = NUM_4X4_BLOCKS_WIDE[bsize]; + let h4 = NUM_4X4_BLOCKS_HIGH[bsize]; + let shape = if h4 > w4 { + 0 + } else if h4 < w4 { + 1 + } else { + 2 + }; + for wedge in 0..WEDGE_TYPES { + let cb = &WEDGE_CODEBOOK[shape][wedge]; + let dir = cb[0] as usize; + let xoff = n / 2 - ((cb[1] as usize * w) >> 3); + let yoff = n / 2 - ((cb[2] as usize * h) >> 3); + let mut sum = 0usize; + for i in 0..w { + sum += master[dir][yoff][xoff + i] as usize; + } + for i in 1..h { + sum += master[dir][yoff + i][xoff] as usize; + } + let avg = (sum + (w + h - 1) / 2) / (w + h - 1); + let flip = (avg < 32) as usize; + let mut a = vec![0u8; w * h]; + let mut b = vec![0u8; w * h]; + for i in 0..h { + for j in 0..w { + let m = master[dir][yoff + i][xoff + j]; + a[i * w + j] = m; + b[i * w + j] = 64 - m; + } + } + let pair = if flip == 0 { [a, b] } else { [b, a] }; + out[bsize].push(pair); + } + } + out + }) +} + +impl TileDecoder<'_, '_> { + /// Reference plane and its (lastX, lastY) for refIdx (-1: the current frame for intra BC). + fn ref_plane_dims(&self, ref_idx: i32, plane: usize) -> (i32, i32) { + let (sx, sy) = if plane > 0 { (self.fs.ssx as i32, self.fs.ssy as i32) } else { (0, 0) }; + let (uw, fh) = if ref_idx < 0 { + ((self.fs.fh.mi_cols * 4) as i32, (self.fs.fh.mi_rows * 4) as i32) + } else { + let ri = &self.fs.ref_info[ref_idx as usize]; + (ri.upscaled_width as i32, ri.frame_height as i32) + }; + (((uw + sx) >> sx) - 1, ((fh + sy) >> sy) - 1) + } + + /// Motion vector scaling process (7.11.3.3): (startX, startY, stepX, stepY). + fn mv_scaling(&self, plane: usize, ref_idx: i32, x: i32, y: i32, mv: [i32; 2]) -> (i32, i32, i32, i32) { + let fh = &self.fs.fh; + let (ref_uw, ref_h) = if ref_idx < 0 { + (fh.upscaled_width as i64, fh.frame_height as i64) + } else { + let ri = &self.fs.ref_info[ref_idx as usize]; + (ri.upscaled_width as i64, ri.frame_height as i64) + }; + let fw = fh.frame_width as i64; + let fhh = fh.frame_height as i64; + let rs = REF_SCALE_SHIFT as i64; + let x_scale = ((ref_uw << rs) + fw / 2) / fw; + let y_scale = ((ref_h << rs) + fhh / 2) / fhh; + let (sx, sy) = if plane > 0 { (self.fs.ssx as i32, self.fs.ssy as i32) } else { (0, 0) }; + let half = 1i64 << (SUBPEL_BITS - 1); + let orig_x = ((x as i64) << SUBPEL_BITS) + ((2 * mv[1]) >> sx) as i64 + half; + let orig_y = ((y as i64) << SUBPEL_BITS) + ((2 * mv[0]) >> sy) as i64 + half; + let base_x = orig_x * x_scale - (half << rs); + let base_y = orig_y * y_scale - (half << rs); + let off = (1i64 << (SCALE_SUBPEL_BITS - SUBPEL_BITS)) / 2; + let sh = (REF_SCALE_SHIFT + SUBPEL_BITS - SCALE_SUBPEL_BITS) as u32; + let start_x = round2_signed64(base_x, sh) + off; + let start_y = round2_signed64(base_y, sh) + off; + let step_x = round2_signed64(x_scale, (REF_SCALE_SHIFT - SCALE_SUBPEL_BITS) as u32); + let step_y = round2_signed64(y_scale, (REF_SCALE_SHIFT - SCALE_SUBPEL_BITS) as u32); + (start_x as i32, start_y as i32, step_x as i32, step_y as i32) + } + + fn ref_plane(&self, ref_idx: i32, plane: usize) -> &Plane { + // A missing reference (rejected earlier for valid streams) predicts from the current frame. + match usize::try_from(ref_idx).ok().and_then(|i| self.fs.refs.get(i)).and_then(|r| r.as_ref()) { + Some(r) => &r.buf.planes[plane], + None => &self.t.cur.planes[plane], + } + } + + /// Block inter prediction process (7.11.3.4) into `pred` (stride PRED_STRIDE); `tmp` holds + /// the intermediate (horizontally filtered) rows. + #[allow(clippy::too_many_arguments)] + fn block_inter_prediction( + &self, + plane: usize, + ref_idx: i32, + x: i32, + y: i32, + x_step: i32, + y_step: i32, + w: usize, + h: usize, + filters: [u8; 2], + rnd: (u32, u32, u32), + pred: &mut [i32], + tmp: &mut [i32], + ) { + let refp = self.ref_plane(ref_idx, plane); + let (last_x, last_y) = self.ref_plane_dims(ref_idx, plane); + let mut fh = filters[1] as usize; + if w <= 4 { + if fh == EIGHTTAP || fh == EIGHTTAP_SHARP { + fh = 4; + } else if fh == EIGHTTAP_SMOOTH { + fh = 5; + } + } + let mut fv = filters[0] as usize; + if h <= 4 { + if fv == EIGHTTAP || fv == EIGHTTAP_SHARP { + fv = 4; + } else if fv == EIGHTTAP_SMOOTH { + fv = 5; + } + } + let unit = 1 << SCALE_SUBPEL_BITS; + if x_step == unit && y_step == unit { + mc_unscaled( + refp, + last_x, + last_y, + x, + y, + w, + h, + &SUBPEL_FILTERS[fh][((x >> 6) & SUBPEL_MASK as i32) as usize], + &SUBPEL_FILTERS[fv][((y >> 6) & SUBPEL_MASK as i32) as usize], + rnd, + pred, + tmp, + ); + return; + } + let inter_h = ((((h as i32 - 1) * y_step + (1 << SCALE_SUBPEL_BITS) - 1) >> SCALE_SUBPEL_BITS) + 8) as usize; + for r in 0..inter_h { + let ry = ((y >> 10) + r as i32 - 3).clamp(0, last_y) as usize; + let row = refp.row(ry); + for c in 0..w { + let p = x + x_step * c as i32; + let filt = &SUBPEL_FILTERS[fh][((p >> 6) & SUBPEL_MASK as i32) as usize]; + let mut s = 0i32; + let bx = (p >> 10) - 3; + for t in 0..8 { + s += filt[t] as i32 * row[(bx + t as i32).clamp(0, last_x) as usize] as i32; + } + tmp[r * w + c] = round2(s, rnd.0); + } + } + for r in 0..h { + for c in 0..w { + let p = (y & 1023) + y_step * r as i32; + let filt = &SUBPEL_FILTERS[fv][((p >> 6) & SUBPEL_MASK as i32) as usize]; + let base = (p >> 10) as usize; + let mut s = 0i32; + for t in 0..8 { + s += filt[t] as i32 * tmp[(base + t) * w + c]; + } + pred[r * PRED_STRIDE + c] = round2(s, rnd.1); + } + } + } + + /// Block warp process (7.11.3.5) for the 8x8 section (i8, j8). + #[allow(clippy::too_many_arguments)] + fn block_warp( + &self, + params: &[i32; 6], + plane: usize, + ref_idx: i32, + x: i32, + y: i32, + i8: usize, + j8: usize, + w: usize, + h: usize, + rnd: (u32, u32, u32), + pred: &mut [i32], + ) { + let refp = self.ref_plane(ref_idx, plane); + let (last_x, last_y) = self.ref_plane_dims(ref_idx, plane); + let (sx, sy) = if plane > 0 { (self.fs.ssx as i32, self.fs.ssy as i32) } else { (0, 0) }; + let src_x = (x + j8 as i32 * 8 + 4) << sx; + let src_y = (y + i8 as i32 * 8 + 4) << sy; + let dst_x = params[2] as i64 * src_x as i64 + params[3] as i64 * src_y as i64 + params[0] as i64; + let dst_y = params[4] as i64 * src_x as i64 + params[5] as i64 * src_y as i64 + params[1] as i64; + let (_, alpha, beta, gamma, delta) = setup_shear(params); + let x4 = dst_x >> sx; + let y4 = dst_y >> sy; + let ix4 = (x4 >> WARPEDMODEL_PREC_BITS) as i32; + let sx4 = (x4 & ((1 << WARPEDMODEL_PREC_BITS) - 1)) as i32; + let iy4 = (y4 >> WARPEDMODEL_PREC_BITS) as i32; + let sy4 = (y4 & ((1 << WARPEDMODEL_PREC_BITS) - 1)) as i32; + let mut inter = [[0i32; 8]; 15]; + for i1 in -7..8i32 { + let row = refp.row((iy4 + i1).clamp(0, last_y) as usize); + for i2 in -4..4i32 { + let sxv = sx4 + alpha * i2 + beta * i1; + let offs = (round2(sxv, WARPEDDIFF_PREC_BITS as u32) + WARPEDPIXEL_PREC_SHIFTS as i32) as usize; + let mut s = 0i32; + for i3 in 0..8i32 { + s += WARPED_FILTERS[offs][i3 as usize] as i32 * row[(ix4 + i2 - 3 + i3).clamp(0, last_x) as usize] as i32; + } + inter[(i1 + 7) as usize][(i2 + 4) as usize] = round2(s, rnd.0); + } + } + let lim_i = 4.min(h as i32 - i8 as i32 * 8 - 4); + let lim_j = 4.min(w as i32 - j8 as i32 * 8 - 4); + for i1 in -4..lim_i { + for i2 in -4..lim_j { + let syv = sy4 + gamma * i2 + delta * i1; + let offs = (round2(syv, WARPEDDIFF_PREC_BITS as u32) + WARPEDPIXEL_PREC_SHIFTS as i32) as usize; + let mut s = 0i32; + for i3 in 0..8 { + s += WARPED_FILTERS[offs][i3] as i32 * inter[(i1 + i3 as i32 + 4) as usize][(i2 + 4) as usize]; + } + pred[(i8 * 8 + (i1 + 4) as usize) * PRED_STRIDE + j8 * 8 + (i2 + 4) as usize] = round2(s, rnd.1); + } + } + } + + /// Warp estimation process (7.11.3.8): sets LocalWarpParams / LocalValid. + pub(crate) fn warp_estimation(&mut self) { + let mut a = [[0i64; 2]; 2]; + let mut bx = [0i64; 2]; + let mut by = [0i64; 2]; + let mid_y = self.b.mi_row as i64 * 4 + self.b.bh4 as i64 * 2 - 1; + let mid_x = self.b.mi_col as i64 * 4 + self.b.bw4 as i64 * 2 - 1; + let suy = mid_y * 8; + let sux = mid_x * 8; + let duy = suy + self.b.mv_i[0][0] as i64; + let dux = sux + self.b.mv_i[0][1] as i64; + let ls = |a: i64, b: i64| ((a * b) >> 2) + (a + b); + for i in 0..self.mvs.num_samples { + let c = self.mvs.cand_list[i]; + let sy = c[0] as i64 - suy; + let sx = c[1] as i64 - sux; + let dy = c[2] as i64 - duy; + let dx = c[3] as i64 - dux; + if (sx - dx).abs() < LS_MV_MAX as i64 && (sy - dy).abs() < LS_MV_MAX as i64 { + a[0][0] += ls(sx, sx) + 8; + a[0][1] += ls(sx, sy) + 4; + a[1][1] += ls(sy, sy) + 8; + bx[0] += ls(sx, dx) + 8; + bx[1] += ls(sy, dx) + 4; + by[0] += ls(sx, dy) + 4; + by[1] += ls(sy, dy) + 8; + } + } + let det = a[0][0] * a[1][1] - a[0][1] * a[0][1]; + self.b.local_valid = det != 0; + if det == 0 { + return; + } + let (mut div_shift, mut div_factor) = resolve_divisor(det); + div_shift -= WARPEDMODEL_PREC_BITS as i32; + if div_shift < 0 { + div_factor <<= -div_shift; + div_shift = 0; + } + let prec = 1i64 << WARPEDMODEL_PREC_BITS; + let cl = WARPEDMODEL_NONDIAGAFFINE_CLAMP as i64; + let nondiag = |v: i64| round2_signed_wide(v as i128 * div_factor as i128, div_shift as u32).clamp(-cl + 1, cl - 1); + let diag = |v: i64| round2_signed_wide(v as i128 * div_factor as i128, div_shift as u32).clamp(prec - cl + 1, prec + cl - 1); + let mut p = [0i64; 6]; + p[2] = diag(a[1][1] * bx[0] - a[0][1] * bx[1]); + p[3] = nondiag(-a[0][1] * bx[0] + a[0][0] * bx[1]); + p[4] = nondiag(a[1][1] * by[0] - a[0][1] * by[1]); + p[5] = diag(-a[0][1] * by[0] + a[0][0] * by[1]); + let mvx = self.b.mv_i[0][1] as i64; + let mvy = self.b.mv_i[0][0] as i64; + let vx = mvx * (1 << (WARPEDMODEL_PREC_BITS - 3)) - (mid_x * (p[2] - prec) + mid_y * p[3]); + let vy = mvy * (1 << (WARPEDMODEL_PREC_BITS - 3)) - (mid_x * p[4] + mid_y * (p[5] - prec)); + let tc = WARPEDMODEL_TRANS_CLAMP as i64; + p[0] = vx.clamp(-tc, tc - 1); + p[1] = vy.clamp(-tc, tc - 1); + for i in 0..6 { + self.b.local_warp_params[i] = p[i] as i32; + } + } + + /// Inter prediction process (7.11.3.1) for a w x h region of `plane` at (x, y). + #[allow(clippy::too_many_arguments)] + pub(crate) fn predict_inter(&mut self, plane: usize, x: usize, y: usize, w: usize, h: usize, cand_row: usize, cand_col: usize) { + let bd = self.fs.bit_depth; + let mi_i = self.t.mi.idx(cand_row, cand_col); + let cand_refs = self.t.mi.ref_frame[mi_i]; + let cand_mvs = self.t.mi.mv[mi_i]; + let filters = self.t.mi.interp_filter[mi_i]; + let is_compound = cand_refs[1] > INTRA_FRAME as i8; + let rnd = rounding(is_compound, bd); + if plane == 0 && self.b.motion_mode as usize == LOCALWARP { + self.warp_estimation(); + if self.b.local_valid { + let p = self.b.local_warp_params; + self.b.local_valid = setup_shear(&p).0; + } + } + let mut preds = [std::mem::take(&mut self.pred_buf[0]), std::mem::take(&mut self.pred_buf[1])]; + let mut tmp = std::mem::take(&mut self.mc_tmp); + let fh = &self.fs.fh; + for ref_list in 0..1 + is_compound as usize { + let ref_frame = cand_refs[ref_list]; + let ym = self.b.y_mode; + let global = + (ym == GLOBALMV || ym == GLOBAL_GLOBALMV) && ref_frame > INTRA_FRAME as i8 && fh.gm_type[ref_frame as usize] as usize > TRANSLATION; + let global_valid = global && setup_shear(&fh.gm_params[ref_frame as usize]).0; + let ref_idx: i32 = if self.b.use_intrabc { -1 } else { fh.ref_frame_idx[ref_frame as usize - LAST_FRAME] as i32 }; + let use_warp = if w < 8 || h < 8 || fh.force_integer_mv { + 0 + } else if self.b.motion_mode as usize == LOCALWARP && self.b.local_valid { + 1 + } else if global && !self.is_scaled_ref(ref_idx) && global_valid { + 2 + } else { + 0 + }; + let mv = [cand_mvs[ref_list].row as i32, cand_mvs[ref_list].col as i32]; + if use_warp != 0 { + let params = if use_warp == 1 { self.b.local_warp_params } else { fh.gm_params[ref_frame as usize] }; + for i8 in 0..=((h - 1) >> 3) { + for j8 in 0..=((w - 1) >> 3) { + self.block_warp(¶ms, plane, ref_idx, x as i32, y as i32, i8, j8, w, h, rnd, &mut preds[ref_list]); + } + } + } else { + let (sx, sy, stx, sty) = self.mv_scaling(plane, ref_idx, x as i32, y as i32, mv); + self.block_inter_prediction(plane, ref_idx, sx, sy, stx, sty, w, h, filters, rnd, &mut preds[ref_list], &mut tmp); + } + } + let ct = self.b.compound_type; + if ct == COMPOUND_WEDGE && plane == 0 { + let m = &wedge_masks()[self.b.mi_size][self.b.wedge_index][self.b.wedge_sign]; + for i in 0..h { + for j in 0..w { + self.mask[i * PRED_STRIDE + j] = m[i * w + j] as i32; + } + } + } else if ct == COMPOUND_INTRA { + let size_scale = MAX_SB_SIZE / h.max(w); + let im = self.b.interintra_mode; + for i in 0..h { + for j in 0..w { + self.mask[i * PRED_STRIDE + j] = if im == II_V_PRED { + II_WEIGHTS_1D[i * size_scale] as i32 + } else if im == II_H_PRED { + II_WEIGHTS_1D[j * size_scale] as i32 + } else if im == II_SMOOTH_PRED { + II_WEIGHTS_1D[i.min(j) * size_scale] as i32 + } else { + 32 + }; + } + } + } else if ct == COMPOUND_DIFFWTD && plane == 0 { + for i in 0..h { + for j in 0..w { + let k = i * PRED_STRIDE + j; + let diff = (preds[0][k] - preds[1][k]).abs(); + let diff = round2(diff, (bd - 8) + rnd.2); + let m = (38 + diff / 16).clamp(0, 64); + self.mask[k] = if self.b.mask_type == 1 { 64 - m } else { m }; + } + } + } + let (fwd, bck) = if ct == COMPOUND_DISTANCE { self.distance_weights(cand_refs) } else { (0, 0) }; + let max = (1i32 << bd) - 1; + let is_inter_intra = self.b.is_inter && self.b.ref_frame[1] == INTRA_FRAME as i8; + if !is_compound && !is_inter_intra { + let pl = &mut self.t.cur.planes[plane]; + for i in 0..h { + let row = &mut pl.row_from_mut(y + i, x)[..w]; + for j in 0..w { + row[j] = preds[0][i * PRED_STRIDE + j].clamp(0, max) as u16; + } + } + } else if ct == COMPOUND_AVERAGE { + let pl = &mut self.t.cur.planes[plane]; + for i in 0..h { + let row = &mut pl.row_from_mut(y + i, x)[..w]; + for j in 0..w { + let k = i * PRED_STRIDE + j; + row[j] = round2(preds[0][k] + preds[1][k], 1 + rnd.2).clamp(0, max) as u16; + } + } + } else if ct == COMPOUND_DISTANCE { + let pl = &mut self.t.cur.planes[plane]; + for i in 0..h { + let row = &mut pl.row_from_mut(y + i, x)[..w]; + for j in 0..w { + let k = i * PRED_STRIDE + j; + row[j] = round2(fwd * preds[0][k] + bck * preds[1][k], 4 + rnd.2).clamp(0, max) as u16; + } + } + } else { + self.mask_blend(&preds, plane, x, y, w, h, rnd.2, max); + } + self.pred_buf = preds; + self.mc_tmp = tmp; + if self.b.motion_mode as usize == OBMC { + self.overlapped_motion_compensation(plane, w, h); + } + } + + fn is_scaled_ref(&self, ref_idx: i32) -> bool { + if ref_idx < 0 { + return false; + } + let fh = &self.fs.fh; + let ri = &self.fs.ref_info[ref_idx as usize]; + let xs = ((ri.upscaled_width << REF_SCALE_SHIFT) + fh.frame_width / 2) / fh.frame_width; + let ys = ((ri.frame_height << REF_SCALE_SHIFT) + fh.frame_height / 2) / fh.frame_height; + xs != 1 << REF_SCALE_SHIFT || ys != 1 << REF_SCALE_SHIFT + } + + #[allow(clippy::too_many_arguments)] + fn mask_blend(&mut self, preds: &[Vec; 2], plane: usize, dx: usize, dy: usize, w: usize, h: usize, post: u32, max: i32) { + let (sx, sy) = if plane > 0 { (self.fs.ssx, self.fs.ssy) } else { (0, 0) }; + let interintra = self.b.interintra; + let wedge_ii = self.b.wedge_interintra; + for yy in 0..h { + for xx in 0..w { + let mk = |r: usize, c: usize| self.mask[r * PRED_STRIDE + c]; + let m = if (sx == 0 && sy == 0) || (interintra && !wedge_ii) { + mk(yy, xx) + } else if sx == 1 && sy == 0 { + round2(mk(yy, 2 * xx) + mk(yy, 2 * xx + 1), 1) + } else { + round2(mk(2 * yy, 2 * xx) + mk(2 * yy, 2 * xx + 1) + mk(2 * yy + 1, 2 * xx) + mk(2 * yy + 1, 2 * xx + 1), 2) + }; + let k = yy * PRED_STRIDE + xx; + let pl = &mut self.t.cur.planes[plane]; + if interintra { + let p0 = round2(preds[0][k], post).clamp(0, max); + let p1 = pl.at(dx + xx, dy + yy) as i32; + pl.set(dx + xx, dy + yy, round2(m * p1 + (64 - m) * p0, 6) as u16); + } else { + let v = round2(m * preds[0][k] + (64 - m) * preds[1][k], 6 + post).clamp(0, max); + pl.set(dx + xx, dy + yy, v as u16); + } + } + } + } + + fn distance_weights(&self, refs: [i8; 2]) -> (i32, i32) { + let fh = &self.fs.fh; + let mut dist = [0i32; 2]; + for (rl, d) in dist.iter_mut().enumerate() { + let h = fh.order_hints[refs[rl] as usize]; + *d = crate::header::relative_dist(&self.fs.seq, h, fh.order_hint).abs().clamp(0, MAX_FRAME_DISTANCE as i32); + } + let d0 = dist[1]; + let d1 = dist[0]; + let order = (d0 <= d1) as usize; + if d0 == 0 || d1 == 0 { + return (QUANT_DIST_LOOKUP[3][order] as i32, QUANT_DIST_LOOKUP[3][1 - order] as i32); + } + let mut i = 0; + while i < 3 { + let c0 = QUANT_DIST_WEIGHT[i][order] as i32; + let c1 = QUANT_DIST_WEIGHT[i][1 - order] as i32; + if order == 1 { + if d0 * c0 > d1 * c1 { + break; + } + } else if d0 * c0 < d1 * c1 { + break; + } + i += 1; + } + (QUANT_DIST_LOOKUP[i][order] as i32, QUANT_DIST_LOOKUP[i][1 - order] as i32) + } + + /// Overlapped motion compensation (7.11.3.9). + fn overlapped_motion_compensation(&mut self, plane: usize, w: usize, h: usize) { + let (sx, sy) = if plane > 0 { (self.fs.ssx, self.fs.ssy) } else { (0, 0) }; + let (mi_rows, mi_cols) = (self.fs.fh.mi_rows as usize, self.fs.fh.mi_cols as usize); + let bd = self.fs.bit_depth; + let max = (1i32 << bd) - 1; + let mut obmc = std::mem::take(&mut self.pred_buf[0]); + let mut tmp = std::mem::take(&mut self.mc_tmp); + let get_mask = |len: usize| -> &'static [u8] { + match len { + 2 => &OBMC_MASK_2, + 4 => &OBMC_MASK_4, + 8 => &OBMC_MASK_8, + 16 => &OBMC_MASK_16, + _ => &OBMC_MASK_32, + } + }; + let mut jobs: Vec<(usize, usize, usize, usize, usize, usize, usize)> = Vec::new(); // (pass, cand_row, cand_col, x4, y4, predW, predH) + if self.b.avail_u && self.fs.plane_residual_size(self.b.mi_size, plane) >= BLOCK_8X8 { + let w4 = self.b.bw4; + let mut x4 = self.b.mi_col; + let y4 = self.b.mi_row; + let mut n = 0; + let limit = 4.min(MI_WIDTH_LOG2[self.b.mi_size] as usize); + while n < limit && x4 < mi_cols.min(self.b.mi_col + w4) { + let cr = self.b.mi_row - 1; + let cc = x4 | 1; + let mi = &self.t.mi; + let ci = mi.idx(cr, cc); + let step4 = (NUM_4X4_BLOCKS_WIDE[mi.mi_size[ci] as usize] as usize).clamp(2, 16); + if mi.ref_frame[ci][0] > INTRA_FRAME as i8 { + n += 1; + let pw = w.min((step4 * 4) >> sx); + let ph = (h >> 1).min(32 >> sy); + jobs.push((0, cr, cc, x4, y4, pw, ph)); + } + x4 += step4; + } + } + if self.b.avail_l { + let h4 = self.b.bh4; + let x4 = self.b.mi_col; + let mut y4 = self.b.mi_row; + let mut n = 0; + let limit = 4.min(MI_HEIGHT_LOG2[self.b.mi_size] as usize); + while n < limit && y4 < mi_rows.min(self.b.mi_row + h4) { + let cc = self.b.mi_col - 1; + let cr = y4 | 1; + let mi = &self.t.mi; + let ci = mi.idx(cr, cc); + let step4 = (NUM_4X4_BLOCKS_HIGH[mi.mi_size[ci] as usize] as usize).clamp(2, 16); + if mi.ref_frame[ci][0] > INTRA_FRAME as i8 { + n += 1; + let pw = (w >> 1).min(32 >> sx); + let ph = h.min((step4 * 4) >> sy); + jobs.push((1, cr, cc, x4, y4, pw, ph)); + } + y4 += step4; + } + } + // the above pass completes before the left pass reads samples; jobs keep that order + for (pass, cr, cc, x4, y4, pw, ph) in jobs { + let ci = self.t.mi.idx(cr, cc); + let m = self.t.mi.mv[ci][0]; + let mv = [m.row as i32, m.col as i32]; + let ref_idx = self.fs.fh.ref_frame_idx[self.t.mi.ref_frame[ci][0] as usize - LAST_FRAME] as i32; + let px = (x4 * 4) >> sx; + let py = (y4 * 4) >> sy; + let (stx, sty, sxs, sys) = self.mv_scaling(plane, ref_idx, px as i32, py as i32, mv); + let filters = self.t.mi.interp_filter[ci]; + let rnd = rounding(false, bd); + self.block_inter_prediction(plane, ref_idx, stx, sty, sxs, sys, pw, ph, filters, rnd, &mut obmc, &mut tmp); + let mask = get_mask(if pass == 0 { ph } else { pw }); + let pl = &mut self.t.cur.planes[plane]; + for i in 0..ph { + for j in 0..pw { + let mm = if pass == 0 { mask[i] } else { mask[j] } as i32; + let o = obmc[i * PRED_STRIDE + j].clamp(0, max); + let c = pl.at(px + j, py + i) as i32; + pl.set(px + j, py + i, round2(mm * c + (64 - mm) * o, 6) as u16); + } + } + } + self.pred_buf[0] = obmc; + self.mc_tmp = tmp; + } +} + +const IDENTITY_TAPS: [i16; 8] = [0, 0, 0, 128, 0, 0, 0, 0]; + +/// Horizontal pass of one row for a block `W` wide: taps outer, columns inner, fixed width so +/// the row stays in registers. +#[inline(always)] +fn hpass(src: &[u16], hf: &[i16; 8], out: &mut [i32], half: i32, sh: u32) { + let mut acc = [half; W]; + for t in 0..8 { + let f = hf[t] as i32; + let Some(p) = src.get(t..).and_then(|s| s.first_chunk::()) else { + return; + }; + for j in 0..W { + acc[j] += f * p[j] as i32; + } + } + let Some(o) = out.first_chunk_mut::() else { + return; + }; + for j in 0..W { + o[j] = acc[j] >> sh; + } +} + +/// Vertical pass of one output row from 8 intermediate rows (`rows`, stride `W`). +#[inline(always)] +fn vpass(rows: &[i32], vf: &[i16; 8], out: &mut [i32], half: i32, sh: u32) { + let mut acc = [half; W]; + for t in 0..8 { + let f = vf[t] as i32; + let Some(p) = rows.get(t * W..).and_then(|s| s.first_chunk::()) else { + return; + }; + for j in 0..W { + acc[j] += f * p[j]; + } + } + let Some(o) = out.first_chunk_mut::() else { + return; + }; + for j in 0..W { + o[j] = acc[j] >> sh; + } +} + +/// Block inter prediction without reference scaling: every column / row uses the same filter +/// phase, so the filters run over whole rows (bit-identical to the per-sample process). +#[allow(clippy::too_many_arguments)] +fn mc_unscaled( + refp: &Plane, + last_x: i32, + last_y: i32, + x: i32, + y: i32, + w: usize, + h: usize, + hf: &[i16; 8], + vf: &[i16; 8], + rnd: (u32, u32, u32), + pred: &mut [i32], + tmp: &mut [i32], +) { + let x0 = (x >> 10) - 3; + let y0 = (y >> 10) - 3; + let h_copy = *hf == IDENTITY_TAPS; + let v_copy = *vf == IDENTITY_TAPS; + // With the identity vertical phase only intermediate rows 3 .. 3 + h have a non-zero tap. + let (r_lo, r_hi) = if v_copy { (3, 3 + h) } else { (0, h + 7) }; + let inside = x0 >= 0 && x0 + w as i32 + 7 <= last_x + 1; + let mut edge = [0u16; 128 + 8]; + let mut acc = [0i32; 128]; + let (r0, r1) = (rnd.0, rnd.1); + let (half0, half1) = (1i32 << (r0 - 1), 1i32 << (r1 - 1)); + for r in r_lo..r_hi { + let ry = (y0 + r as i32).clamp(0, last_y) as usize; + let row = refp.row(ry); + let src: &[u16] = if inside { + &row[x0 as usize..x0 as usize + w + 7] + } else { + for (k, e) in edge[..w + 7].iter_mut().enumerate() { + *e = row[(x0 + k as i32).clamp(0, last_x) as usize]; + } + &edge[..w + 7] + }; + let out = &mut tmp[r * w..r * w + w]; + if h_copy { + // Round2(128 * v, InterRound0) is exact. + for (o, &v) in out.iter_mut().zip(&src[3..3 + w]) { + *o = (v as i32) << (7 - r0); + } + } else { + match w { + 2 => hpass::<2>(src, hf, out, half0, r0), + 4 => hpass::<4>(src, hf, out, half0, r0), + 8 => hpass::<8>(src, hf, out, half0, r0), + 16 => hpass::<16>(src, hf, out, half0, r0), + 32 => hpass::<32>(src, hf, out, half0, r0), + 64 => hpass::<64>(src, hf, out, half0, r0), + _ => { + let acc = &mut acc[..w]; + acc.fill(0); + for t in 0..8 { + let f = hf[t] as i32; + for (a, &v) in acc.iter_mut().zip(&src[t..t + w]) { + *a += f * v as i32; + } + } + for (o, &a) in out.iter_mut().zip(acc.iter()) { + *o = (a + half0) >> r0; + } + } + } + } + } + for r in 0..h { + let out = &mut pred[r * PRED_STRIDE..r * PRED_STRIDE + w]; + if v_copy { + for (o, &v) in out.iter_mut().zip(&tmp[(r + 3) * w..(r + 4) * w]) { + *o = (128 * v + half1) >> r1; + } + } else { + let rows = &tmp[r * w..(r + 8) * w]; + match w { + 2 => vpass::<2>(rows, vf, out, half1, r1), + 4 => vpass::<4>(rows, vf, out, half1, r1), + 8 => vpass::<8>(rows, vf, out, half1, r1), + 16 => vpass::<16>(rows, vf, out, half1, r1), + 32 => vpass::<32>(rows, vf, out, half1, r1), + 64 => vpass::<64>(rows, vf, out, half1, r1), + _ => { + let acc = &mut acc[..w]; + acc.fill(0); + for t in 0..8 { + let f = vf[t] as i32; + for (a, &v) in acc.iter_mut().zip(&tmp[(r + t) * w..(r + t + 1) * w]) { + *a += f * v; + } + } + for (o, &a) in out.iter_mut().zip(acc.iter()) { + *o = (a + half1) >> r1; + } + } + } + } + } +} diff --git a/crates/av1/src/inter_info.rs b/crates/av1/src/inter_info.rs new file mode 100644 index 0000000..6f9b274 --- /dev/null +++ b/crates/av1/src/inter_info.rs @@ -0,0 +1,770 @@ +//! Inter frame mode info syntax (5.11.7 / 5.11.18 – 5.11.33) with its CDF selection (8.3.2). + +use crate::Result; +use crate::header::{EIGHTTAP, SWITCHABLE}; +use crate::mvpred::MvP; +use crate::spec_tables::*; +use crate::tile::TileDecoder; + +fn block_width(bs: usize) -> usize { + 4 * NUM_4X4_BLOCKS_WIDE[bs] as usize +} +fn block_height(bs: usize) -> usize { + 4 * NUM_4X4_BLOCKS_HIGH[bs] as usize +} + +/// Neighbour reference frame summary (LeftRefFrame / AboveRefFrame and the derived flags). +#[derive(Clone, Copy, Default)] +pub(crate) struct Neighbours { + pub left: [i8; 2], + pub above: [i8; 2], + pub left_intra: bool, + pub above_intra: bool, + pub left_single: bool, + pub above_single: bool, +} + +impl TileDecoder<'_, '_> { + pub(crate) fn inter_frame_mode_info_impl(&mut self) -> Result<()> { + self.b.use_intrabc = false; + let (r, c) = (self.b.mi_row, self.b.mi_col); + let mi = &self.t.mi; + let mut n = Neighbours { + left: if self.b.avail_l { mi.ref_frame[mi.idx(r, c - 1)] } else { [INTRA_FRAME as i8, -1] }, + above: if self.b.avail_u { mi.ref_frame[mi.idx(r - 1, c)] } else { [INTRA_FRAME as i8, -1] }, + ..Default::default() + }; + if !self.b.avail_l { + n.left[1] = -1; + } + if !self.b.avail_u { + n.above[1] = -1; + } + n.left_intra = n.left[0] <= INTRA_FRAME as i8; + n.above_intra = n.above[0] <= INTRA_FRAME as i8; + n.left_single = n.left[1] <= INTRA_FRAME as i8; + n.above_single = n.above[1] <= INTRA_FRAME as i8; + self.nb = n; + self.b.skip = false; + self.inter_segment_id(true); + self.read_skip_mode(); + if self.b.skip_mode { + self.b.skip = true; + } else { + self.read_skip(); + } + if !self.fs.fh.seg.seg_id_pre_skip { + self.inter_segment_id(false); + } + self.b.lossless = self.fs.fh.lossless_array[self.b.segment_id]; + self.read_cdef(); + self.read_delta_qindex(); + self.read_delta_lf(); + self.read_deltas = false; + self.read_is_inter(); + if self.b.is_inter { self.inter_block_mode_info() } else { self.intra_block_mode_info() } + } + + fn get_segment_id(&self) -> usize { + let (mi_rows, mi_cols) = (self.fs.fh.mi_rows as usize, self.fs.fh.mi_cols as usize); + let x_mis = (mi_cols - self.b.mi_col).min(self.b.bw4); + let y_mis = (mi_rows - self.b.mi_row).min(self.b.bh4); + let mut seg = 7u8; + for y in 0..y_mis { + for x in 0..x_mis { + let i = (self.b.mi_row + y) * self.t.mi.cols + self.b.mi_col + x; + seg = seg.min(self.fs.prev_segment_ids[i]); + } + } + seg as usize + } + + fn set_seg_pred_context(&mut self, v: u8) { + for i in 0..self.b.bw4 { + if self.b.mi_col + i < self.t.above_seg_pred.len() { + self.t.above_seg_pred[self.b.mi_col + i] = v; + } + } + for i in 0..self.b.bh4 { + if self.b.mi_row + i < self.t.left_seg_pred.len() { + self.t.left_seg_pred[self.b.mi_row + i] = v; + } + } + } + + fn inter_segment_id(&mut self, pre_skip: bool) { + let seg = &self.fs.fh.seg; + if !seg.enabled { + self.b.segment_id = 0; + return; + } + let predicted = self.get_segment_id(); + if seg.update_map { + if pre_skip && !seg.seg_id_pre_skip { + self.b.segment_id = 0; + return; + } + if !pre_skip && self.b.skip { + self.set_seg_pred_context(0); + self.read_segment_id(); + return; + } + if seg.temporal_update { + let ctx = self.t.left_seg_pred[self.b.mi_row] as usize + self.t.above_seg_pred[self.b.mi_col] as usize; + let pred_flag = self.sd.read_symbol(&mut self.cdf.segment_id_predicted[ctx]); + if pred_flag == 1 { + self.b.segment_id = predicted; + } else { + self.read_segment_id(); + } + self.set_seg_pred_context(pred_flag as u8); + } else { + self.read_segment_id(); + } + } else { + self.b.segment_id = predicted; + } + } + + fn read_skip_mode(&mut self) { + let ms = self.b.mi_size; + if self.seg_active(SEG_LVL_SKIP) + || self.seg_active(SEG_LVL_REF_FRAME) + || self.seg_active(SEG_LVL_GLOBALMV) + || !self.fs.fh.skip_mode_present + || block_width(ms) < 8 + || block_height(ms) < 8 + { + self.b.skip_mode = false; + } else { + let (r, c) = (self.b.mi_row, self.b.mi_col); + let mi = &self.t.mi; + let mut ctx = 0; + if self.b.avail_u { + ctx += mi.skip_mode[mi.idx(r - 1, c)] as usize; + } + if self.b.avail_l { + ctx += mi.skip_mode[mi.idx(r, c - 1)] as usize; + } + self.b.skip_mode = self.sd.read_symbol(&mut self.cdf.skip_mode[ctx]) == 1; + } + } + + pub(crate) fn seg_active(&self, feature: usize) -> bool { + self.fs.fh.seg.enabled && self.fs.fh.seg.features.enabled[self.b.segment_id][feature] + } + + fn read_is_inter(&mut self) { + if self.b.skip_mode { + self.b.is_inter = true; + } else if self.seg_active(SEG_LVL_REF_FRAME) { + self.b.is_inter = self.fs.fh.seg.features.data[self.b.segment_id][SEG_LVL_REF_FRAME] != INTRA_FRAME as i32; + } else if self.seg_active(SEG_LVL_GLOBALMV) { + self.b.is_inter = true; + } else { + let n = self.nb; + let ctx = if self.b.avail_u && self.b.avail_l { + if n.left_intra && n.above_intra { 3 } else { (n.left_intra || n.above_intra) as usize } + } else if self.b.avail_u || self.b.avail_l { + 2 * (if self.b.avail_u { n.above_intra } else { n.left_intra }) as usize + } else { + 0 + }; + self.b.is_inter = self.sd.read_symbol(&mut self.cdf.is_inter[ctx]) == 1; + } + } + + fn intra_block_mode_info(&mut self) -> Result<()> { + self.b.ref_frame = [INTRA_FRAME as i8, -1]; + let ctx = SIZE_GROUP[self.b.mi_size] as usize; + self.b.y_mode = self.sd.read_symbol(&mut self.cdf.y_mode[ctx]); + self.intra_angle_info_y(); + if self.b.has_chroma { + self.read_uv_mode(); + if self.b.uv_mode == UV_CFL_PRED { + self.read_cfl_alphas(); + } + self.intra_angle_info_uv(); + } + self.b.palette_size_y = 0; + self.b.palette_size_uv = 0; + let ms = self.b.mi_size; + if ms >= BLOCK_8X8 && block_width(ms) <= 64 && block_height(ms) <= 64 && self.fs.fh.allow_screen_content_tools { + self.palette_mode_info(); + } + self.filter_intra_mode_info(); + Ok(()) + } + + fn count_refs(&self, frame_type: i8) -> usize { + let n = self.nb; + let mut c = 0; + if self.b.avail_u { + c += (n.above[0] == frame_type) as usize + (n.above[1] == frame_type) as usize; + } + if self.b.avail_l { + c += (n.left[0] == frame_type) as usize + (n.left[1] == frame_type) as usize; + } + c + } + + fn ref_count_ctx(c0: usize, c1: usize) -> usize { + match c0.cmp(&c1) { + std::cmp::Ordering::Less => 0, + std::cmp::Ordering::Equal => 1, + std::cmp::Ordering::Greater => 2, + } + } + + fn ctx_comp_ref(&self) -> usize { + Self::ref_count_ctx( + self.count_refs(LAST_FRAME as i8) + self.count_refs(LAST2_FRAME as i8), + self.count_refs(LAST3_FRAME as i8) + self.count_refs(GOLDEN_FRAME as i8), + ) + } + fn ctx_comp_ref_p1(&self) -> usize { + Self::ref_count_ctx(self.count_refs(LAST_FRAME as i8), self.count_refs(LAST2_FRAME as i8)) + } + fn ctx_comp_ref_p2(&self) -> usize { + Self::ref_count_ctx(self.count_refs(LAST3_FRAME as i8), self.count_refs(GOLDEN_FRAME as i8)) + } + fn ctx_comp_bwdref(&self) -> usize { + Self::ref_count_ctx(self.count_refs(BWDREF_FRAME as i8) + self.count_refs(ALTREF2_FRAME as i8), self.count_refs(ALTREF_FRAME as i8)) + } + fn ctx_comp_bwdref_p1(&self) -> usize { + Self::ref_count_ctx(self.count_refs(BWDREF_FRAME as i8), self.count_refs(ALTREF2_FRAME as i8)) + } + fn ctx_single_ref_p1(&self) -> usize { + let fwd = self.count_refs(LAST_FRAME as i8) + + self.count_refs(LAST2_FRAME as i8) + + self.count_refs(LAST3_FRAME as i8) + + self.count_refs(GOLDEN_FRAME as i8); + let bwd = self.count_refs(BWDREF_FRAME as i8) + self.count_refs(ALTREF2_FRAME as i8) + self.count_refs(ALTREF_FRAME as i8); + Self::ref_count_ctx(fwd, bwd) + } + + fn read_ref_frames(&mut self) { + if self.b.skip_mode { + let f = self.fs.fh.skip_mode_frame; + self.b.ref_frame = [f[0] as i8, f[1] as i8]; + } else if self.seg_active(SEG_LVL_REF_FRAME) { + self.b.ref_frame = [self.fs.fh.seg.features.data[self.b.segment_id][SEG_LVL_REF_FRAME] as i8, -1]; + } else if self.seg_active(SEG_LVL_SKIP) || self.seg_active(SEG_LVL_GLOBALMV) { + self.b.ref_frame = [LAST_FRAME as i8, -1]; + } else { + let comp_mode = if self.fs.fh.reference_select && self.b.bw4.min(self.b.bh4) >= 2 { + let ctx = self.comp_mode_ctx(); + self.sd.read_symbol(&mut self.cdf.comp_mode[ctx]) + } else { + SINGLE_REFERENCE + }; + if comp_mode == COMPOUND_REFERENCE { + let ctx = self.comp_ref_type_ctx(); + let comp_ref_type = self.sd.read_symbol(&mut self.cdf.comp_ref_type[ctx]); + if comp_ref_type == UNIDIR_COMP_REFERENCE { + let ctx = self.ctx_single_ref_p1(); + if self.sd.read_symbol(&mut self.cdf.uni_comp_ref[ctx][0]) == 1 { + self.b.ref_frame = [BWDREF_FRAME as i8, ALTREF_FRAME as i8]; + } else { + let ctx1 = Self::ref_count_ctx( + self.count_refs(LAST2_FRAME as i8), + self.count_refs(LAST3_FRAME as i8) + self.count_refs(GOLDEN_FRAME as i8), + ); + if self.sd.read_symbol(&mut self.cdf.uni_comp_ref[ctx1][1]) == 1 { + let ctx2 = self.ctx_comp_ref_p2(); + if self.sd.read_symbol(&mut self.cdf.uni_comp_ref[ctx2][2]) == 1 { + self.b.ref_frame = [LAST_FRAME as i8, GOLDEN_FRAME as i8]; + } else { + self.b.ref_frame = [LAST_FRAME as i8, LAST3_FRAME as i8]; + } + } else { + self.b.ref_frame = [LAST_FRAME as i8, LAST2_FRAME as i8]; + } + } + } else { + let ctx = self.ctx_comp_ref(); + if self.sd.read_symbol(&mut self.cdf.comp_ref[ctx][0]) == 0 { + let ctx = self.ctx_comp_ref_p1(); + let p1 = self.sd.read_symbol(&mut self.cdf.comp_ref[ctx][1]); + self.b.ref_frame[0] = if p1 == 1 { LAST2_FRAME } else { LAST_FRAME } as i8; + } else { + let ctx = self.ctx_comp_ref_p2(); + let p2 = self.sd.read_symbol(&mut self.cdf.comp_ref[ctx][2]); + self.b.ref_frame[0] = if p2 == 1 { GOLDEN_FRAME } else { LAST3_FRAME } as i8; + } + let ctx = self.ctx_comp_bwdref(); + if self.sd.read_symbol(&mut self.cdf.comp_bwd_ref[ctx][0]) == 0 { + let ctx = self.ctx_comp_bwdref_p1(); + let p1 = self.sd.read_symbol(&mut self.cdf.comp_bwd_ref[ctx][1]); + self.b.ref_frame[1] = if p1 == 1 { ALTREF2_FRAME } else { BWDREF_FRAME } as i8; + } else { + self.b.ref_frame[1] = ALTREF_FRAME as i8; + } + } + } else { + let ctx = self.ctx_single_ref_p1(); + if self.sd.read_symbol(&mut self.cdf.single_ref[ctx][0]) == 1 { + let ctx = self.ctx_comp_bwdref(); + if self.sd.read_symbol(&mut self.cdf.single_ref[ctx][1]) == 0 { + let ctx = self.ctx_comp_bwdref_p1(); + let p6 = self.sd.read_symbol(&mut self.cdf.single_ref[ctx][5]); + self.b.ref_frame[0] = if p6 == 1 { ALTREF2_FRAME } else { BWDREF_FRAME } as i8; + } else { + self.b.ref_frame[0] = ALTREF_FRAME as i8; + } + } else { + let ctx = self.ctx_comp_ref(); + if self.sd.read_symbol(&mut self.cdf.single_ref[ctx][2]) == 1 { + let ctx = self.ctx_comp_ref_p2(); + let p5 = self.sd.read_symbol(&mut self.cdf.single_ref[ctx][4]); + self.b.ref_frame[0] = if p5 == 1 { GOLDEN_FRAME } else { LAST3_FRAME } as i8; + } else { + let ctx = self.ctx_comp_ref_p1(); + let p4 = self.sd.read_symbol(&mut self.cdf.single_ref[ctx][3]); + self.b.ref_frame[0] = if p4 == 1 { LAST2_FRAME } else { LAST_FRAME } as i8; + } + } + self.b.ref_frame[1] = -1; + } + } + } + + fn check_backward(r: i8) -> bool { + r >= BWDREF_FRAME as i8 && r <= ALTREF_FRAME as i8 + } + + fn comp_mode_ctx(&self) -> usize { + let n = self.nb; + let (au, al) = (self.b.avail_u, self.b.avail_l); + if au && al { + if n.above_single && n.left_single { + (Self::check_backward(n.above[0]) ^ Self::check_backward(n.left[0])) as usize + } else if n.above_single { + 2 + (Self::check_backward(n.above[0]) || n.above_intra) as usize + } else if n.left_single { + 2 + (Self::check_backward(n.left[0]) || n.left_intra) as usize + } else { + 4 + } + } else if au { + if n.above_single { Self::check_backward(n.above[0]) as usize } else { 3 } + } else if al { + if n.left_single { Self::check_backward(n.left[0]) as usize } else { 3 } + } else { + 1 + } + } + + fn comp_ref_type_ctx(&self) -> usize { + let n = self.nb; + let (au, al) = (self.b.avail_u, self.b.avail_l); + let samedir = |a: i8, b: i8| (a >= BWDREF_FRAME as i8) == (b >= BWDREF_FRAME as i8); + let (above0, above1, left0, left1) = (n.above[0], n.above[1], n.left[0], n.left[1]); + let above_comp_inter = au && !n.above_intra && !n.above_single; + let left_comp_inter = al && !n.left_intra && !n.left_single; + let above_uni = above_comp_inter && samedir(above0, above1); + let left_uni = left_comp_inter && samedir(left0, left1); + if au && !n.above_intra && al && !n.left_intra { + let sd = samedir(above0, left0) as usize; + if !above_comp_inter && !left_comp_inter { + 1 + 2 * sd + } else if !above_comp_inter { + if !left_uni { 1 } else { 3 + sd } + } else if !left_comp_inter { + if !above_uni { 1 } else { 3 + sd } + } else if !above_uni && !left_uni { + 0 + } else if !above_uni || !left_uni { + 2 + } else { + 3 + ((above0 == BWDREF_FRAME as i8) == (left0 == BWDREF_FRAME as i8)) as usize + } + } else if au && al { + if above_comp_inter { + 1 + 2 * above_uni as usize + } else if left_comp_inter { + 1 + 2 * left_uni as usize + } else { + 2 + } + } else if above_comp_inter { + 4 * above_uni as usize + } else if left_comp_inter { + 4 * left_uni as usize + } else { + 2 + } + } + + fn inter_block_mode_info(&mut self) -> Result<()> { + self.b.palette_size_y = 0; + self.b.palette_size_uv = 0; + self.read_ref_frames(); + let is_compound = self.b.ref_frame[1] > INTRA_FRAME as i8; + self.find_mv_stack(is_compound); + if self.b.skip_mode { + self.b.y_mode = NEAREST_NEARESTMV; + } else if self.seg_active(SEG_LVL_SKIP) || self.seg_active(SEG_LVL_GLOBALMV) { + self.b.y_mode = GLOBALMV; + } else if is_compound { + let ctx = COMPOUND_MODE_CTX_MAP[self.mvs.ref_mv_context >> 1][self.mvs.new_mv_context.min(COMP_NEWMV_CTXS - 1)] as usize; + self.b.y_mode = NEAREST_NEARESTMV + self.sd.read_symbol(&mut self.cdf.compound_mode[ctx]); + } else { + let new_mv = self.sd.read_symbol(&mut self.cdf.new_mv[self.mvs.new_mv_context]); + if new_mv == 0 { + self.b.y_mode = NEWMV; + } else { + let zero_mv = self.sd.read_symbol(&mut self.cdf.zero_mv[self.mvs.zero_mv_context]); + if zero_mv == 0 { + self.b.y_mode = GLOBALMV; + } else { + let ref_mv = self.sd.read_symbol(&mut self.cdf.ref_mv[self.mvs.ref_mv_context]); + self.b.y_mode = if ref_mv == 0 { NEARESTMV } else { NEARMV }; + } + } + } + self.b.ref_mv_idx = 0; + let ym = self.b.y_mode; + if ym == NEWMV || ym == NEW_NEWMV { + for idx in 0..2 { + if self.mvs.num_mv_found > idx + 1 { + let ctx = self.mvs.drl_ctx_stack[idx]; + let drl = self.sd.read_symbol(&mut self.cdf.drl_mode[ctx]); + if drl == 0 { + self.b.ref_mv_idx = idx; + break; + } + self.b.ref_mv_idx = idx + 1; + } + } + } else if matches!(ym, NEARMV | NEAR_NEARMV | NEAR_NEWMV | NEW_NEARMV) { + self.b.ref_mv_idx = 1; + for idx in 1..3 { + if self.mvs.num_mv_found > idx + 1 { + let ctx = self.mvs.drl_ctx_stack[idx]; + let drl = self.sd.read_symbol(&mut self.cdf.drl_mode[ctx]); + if drl == 0 { + self.b.ref_mv_idx = idx; + break; + } + self.b.ref_mv_idx = idx + 1; + } + } + } + self.assign_mv(is_compound)?; + self.read_interintra_mode(is_compound); + self.read_motion_mode(is_compound); + self.read_compound_type(is_compound); + if self.fs.fh.interpolation_filter == SWITCHABLE { + let dual = self.fs.seq.enable_dual_filter; + for dir in 0..if dual { 2 } else { 1 } { + if self.needs_interp_filter() { + let ctx = self.interp_filter_ctx(dir); + self.b.interp_filter[dir] = self.sd.read_symbol(&mut self.cdf.interp_filter[ctx]) as u8; + } else { + self.b.interp_filter[dir] = EIGHTTAP; + } + } + if !dual { + self.b.interp_filter[1] = self.b.interp_filter[0]; + } + } else { + self.b.interp_filter = [self.fs.fh.interpolation_filter; 2]; + } + Ok(()) + } + + fn needs_interp_filter(&self) -> bool { + let ms = self.b.mi_size; + let large = block_width(ms).min(block_height(ms)) >= 8; + let gm = &self.fs.fh.gm_type; + if self.b.skip_mode || self.b.motion_mode as usize == LOCALWARP { + false + } else if large && self.b.y_mode == GLOBALMV { + gm[self.b.ref_frame[0] as usize] as usize == TRANSLATION + } else if large && self.b.y_mode == GLOBAL_GLOBALMV { + gm[self.b.ref_frame[0] as usize] as usize == TRANSLATION || gm[self.b.ref_frame[1] as usize] as usize == TRANSLATION + } else { + true + } + } + + fn interp_filter_ctx(&self, dir: usize) -> usize { + let mut ctx = ((dir & 1) * 2 + (self.b.ref_frame[1] > INTRA_FRAME as i8) as usize) * 4; + let mut left_type = 3u8; + let mut above_type = 3u8; + let (r, c) = (self.b.mi_row, self.b.mi_col); + let mi = &self.t.mi; + let rf0 = self.b.ref_frame[0]; + if self.b.avail_l { + let i = mi.idx(r, c - 1); + if mi.ref_frame[i][0] == rf0 || mi.ref_frame[i][1] == rf0 { + left_type = mi.interp_filter[i][dir]; + } + } + if self.b.avail_u { + let i = mi.idx(r - 1, c); + if mi.ref_frame[i][0] == rf0 || mi.ref_frame[i][1] == rf0 { + above_type = mi.interp_filter[i][dir]; + } + } + ctx += if left_type == above_type { + left_type as usize + } else if left_type == 3 { + above_type as usize + } else if above_type == 3 { + left_type as usize + } else { + 3 + }; + ctx + } + + fn get_mode(&self, ref_list: usize) -> usize { + let ym = self.b.y_mode; + if ref_list == 0 { + if ym < NEAREST_NEARESTMV { + ym + } else if matches!(ym, NEW_NEWMV | NEW_NEARESTMV | NEW_NEARMV) { + NEWMV + } else if matches!(ym, NEAREST_NEARESTMV | NEAREST_NEWMV) { + NEARESTMV + } else if matches!(ym, NEAR_NEARMV | NEAR_NEWMV) { + NEARMV + } else { + GLOBALMV + } + } else if matches!(ym, NEW_NEWMV | NEAREST_NEWMV | NEAR_NEWMV) { + NEWMV + } else if matches!(ym, NEAREST_NEARESTMV | NEW_NEARESTMV) { + NEARESTMV + } else if matches!(ym, NEAR_NEARMV | NEW_NEARMV) { + NEARMV + } else { + GLOBALMV + } + } + + pub(crate) fn assign_mv(&mut self, is_compound: bool) -> Result<()> { + for i in 0..1 + is_compound as usize { + let comp_mode = if self.b.use_intrabc { NEWMV } else { self.get_mode(i) }; + let pred: MvP = if self.b.use_intrabc { + let mut p = self.mvs.ref_stack_mv[0][0]; + if p == [0, 0] { + p = self.mvs.ref_stack_mv[1][0]; + } + if p == [0, 0] { + let sb_size = if self.fs.seq.use_128x128_superblock { BLOCK_128X128 } else { BLOCK_64X64 }; + let sb4 = NUM_4X4_BLOCKS_HIGH[sb_size] as i32; + if (self.b.mi_row as i32) - sb4 < self.mi_row_start as i32 { + p = [0, -(sb4 * 4 + INTRABC_DELAY_PIXELS as i32) * 8]; + } else { + p = [-(sb4 * 4 * 8), 0]; + } + } + p + } else if comp_mode == GLOBALMV { + self.mvs.global_mvs[i] + } else { + let mut pos = if comp_mode == NEARESTMV { 0 } else { self.b.ref_mv_idx }; + if comp_mode == NEWMV && self.mvs.num_mv_found <= 1 { + pos = 0; + } + self.mvs.ref_stack_mv[pos][i] + }; + self.b.pred_mv[i] = pred; + if comp_mode == NEWMV { + self.read_mv(i); + } else { + self.b.mv_i[i] = pred; + } + } + Ok(()) + } + + fn read_mv(&mut self, r: usize) { + let mut diff = [0i32; 2]; + let ctx = if self.b.use_intrabc { MV_INTRABC_CONTEXT } else { 0 }; + let joint = self.sd.read_symbol(&mut self.cdf.mv_joint[ctx]); + if joint == MV_JOINT_HZVNZ || joint == MV_JOINT_HNZVNZ { + diff[0] = self.read_mv_component(ctx, 0); + } + if joint == MV_JOINT_HNZVZ || joint == MV_JOINT_HNZVNZ { + diff[1] = self.read_mv_component(ctx, 1); + } + self.b.mv_i[r] = [self.b.pred_mv[r][0] + diff[0], self.b.pred_mv[r][1] + diff[1]]; + } + + fn read_mv_component(&mut self, ctx: usize, comp: usize) -> i32 { + let fh = &self.fs.fh; + let (fim, hp) = (fh.force_integer_mv, fh.allow_high_precision_mv); + let sign = self.sd.read_symbol(&mut self.cdf.mv_sign[ctx][comp]); + let class = self.sd.read_symbol(&mut self.cdf.mv_class[ctx][comp]); + let mag = if class == MV_CLASS_0 { + let b0 = self.sd.read_symbol(&mut self.cdf.mv_class0_bit[ctx][comp]) as i32; + let fr = if fim { 3 } else { self.sd.read_symbol(&mut self.cdf.mv_class0_fr[ctx][comp][b0 as usize]) as i32 }; + let h = if hp { self.sd.read_symbol(&mut self.cdf.mv_class0_hp[ctx][comp]) as i32 } else { 1 }; + ((b0 << 3) | (fr << 1) | h) + 1 + } else { + let mut d = 0i32; + for i in 0..class { + d |= (self.sd.read_symbol(&mut self.cdf.mv_bit[ctx][comp][i]) as i32) << i; + } + let mut mag = (CLASS0_SIZE as i32) << (class + 2); + let fr = if fim { 3 } else { self.sd.read_symbol(&mut self.cdf.mv_fr[ctx][comp]) as i32 }; + let h = if hp { self.sd.read_symbol(&mut self.cdf.mv_hp[ctx][comp]) as i32 } else { 1 }; + mag += ((d << 3) | (fr << 1) | h) + 1; + mag + }; + if sign == 1 { -mag } else { mag } + } + + fn read_interintra_mode(&mut self, is_compound: bool) { + let ms = self.b.mi_size; + self.b.interintra = false; + if !self.b.skip_mode && self.fs.seq.enable_interintra_compound && !is_compound && (BLOCK_8X8..=BLOCK_32X32).contains(&ms) { + let ctx = SIZE_GROUP[ms] as usize - 1; + self.b.interintra = self.sd.read_symbol(&mut self.cdf.inter_intra[ctx]) == 1; + if self.b.interintra { + self.b.interintra_mode = self.sd.read_symbol(&mut self.cdf.inter_intra_mode[ctx]); + self.b.ref_frame[1] = INTRA_FRAME as i8; + self.b.angle_delta_y = 0; + self.b.angle_delta_uv = 0; + self.b.use_filter_intra = false; + self.b.wedge_interintra = self.sd.read_symbol(&mut self.cdf.wedge_inter_intra[ms]) == 1; + if self.b.wedge_interintra { + self.b.wedge_index = self.sd.read_symbol(&mut self.cdf.wedge_index[ms]); + self.b.wedge_sign = 0; + } + } + } + } + + fn is_scaled(&self, rf: i8) -> bool { + let fh = &self.fs.fh; + let ri = &self.fs.ref_info[fh.ref_frame_idx[rf as usize - LAST_FRAME]]; + let fw = fh.frame_width; + let fhh = fh.frame_height; + let xs = ((ri.upscaled_width << REF_SCALE_SHIFT) + fw / 2) / fw; + let ys = ((ri.frame_height << REF_SCALE_SHIFT) + fhh / 2) / fhh; + let no = 1 << REF_SCALE_SHIFT; + xs != no || ys != no + } + + fn read_motion_mode(&mut self, is_compound: bool) { + self.b.motion_mode = SIMPLE as u8; + let fh = &self.fs.fh; + if self.b.skip_mode || !fh.is_motion_mode_switchable { + return; + } + let ms = self.b.mi_size; + if block_width(ms).min(block_height(ms)) < 8 { + return; + } + if !fh.force_integer_mv + && (self.b.y_mode == GLOBALMV || self.b.y_mode == GLOBAL_GLOBALMV) + && fh.gm_type[self.b.ref_frame[0] as usize] as usize > TRANSLATION + { + return; + } + if is_compound || self.b.ref_frame[1] == INTRA_FRAME as i8 || !self.has_overlappable_candidates() { + return; + } + self.find_warp_samples(); + let fh = &self.fs.fh; + if fh.force_integer_mv || self.mvs.num_samples == 0 || !fh.allow_warped_motion || self.is_scaled(self.b.ref_frame[0]) { + let use_obmc = self.sd.read_symbol(&mut self.cdf.use_obmc[ms]); + self.b.motion_mode = if use_obmc == 1 { OBMC as u8 } else { SIMPLE as u8 }; + } else { + self.b.motion_mode = self.sd.read_symbol(&mut self.cdf.motion_mode[ms]) as u8; + } + } + + fn read_compound_type(&mut self, is_compound: bool) { + self.b.comp_group_idx = 0; + self.b.compound_idx = 1; + if self.b.skip_mode { + self.b.compound_type = COMPOUND_AVERAGE; + return; + } + if is_compound { + let ms = self.b.mi_size; + let n = WEDGE_BITS[ms]; + if self.fs.seq.enable_masked_compound { + let ctx = self.comp_group_idx_ctx(); + self.b.comp_group_idx = self.sd.read_symbol(&mut self.cdf.comp_group_idx[ctx]) as u8; + } + if self.b.comp_group_idx == 0 { + if self.fs.seq.enable_jnt_comp { + let ctx = self.compound_idx_ctx(); + self.b.compound_idx = self.sd.read_symbol(&mut self.cdf.compound_idx[ctx]) as u8; + self.b.compound_type = if self.b.compound_idx == 1 { COMPOUND_AVERAGE } else { COMPOUND_DISTANCE }; + } else { + self.b.compound_type = COMPOUND_AVERAGE; + } + } else if n == 0 { + self.b.compound_type = COMPOUND_DIFFWTD; + } else { + self.b.compound_type = self.sd.read_symbol(&mut self.cdf.compound_type[ms]); + } + if self.b.compound_type == COMPOUND_WEDGE { + self.b.wedge_index = self.sd.read_symbol(&mut self.cdf.wedge_index[ms]); + self.b.wedge_sign = self.sd.read_literal(1) as usize; + } else if self.b.compound_type == COMPOUND_DIFFWTD { + self.b.mask_type = self.sd.read_literal(1) as usize; + } + } else if self.b.interintra { + self.b.compound_type = if self.b.wedge_interintra { COMPOUND_WEDGE } else { COMPOUND_INTRA }; + } else { + self.b.compound_type = COMPOUND_AVERAGE; + } + } + + fn comp_group_idx_ctx(&self) -> usize { + let n = self.nb; + let mi = &self.t.mi; + let (r, c) = (self.b.mi_row, self.b.mi_col); + let mut ctx = 0; + if self.b.avail_u { + if !n.above_single { + ctx += mi.comp_group_idx[mi.idx(r - 1, c)] as usize; + } else if n.above[0] == ALTREF_FRAME as i8 { + ctx += 3; + } + } + if self.b.avail_l { + if !n.left_single { + ctx += mi.comp_group_idx[mi.idx(r, c - 1)] as usize; + } else if n.left[0] == ALTREF_FRAME as i8 { + ctx += 3; + } + } + ctx.min(5) + } + + fn compound_idx_ctx(&self) -> usize { + let fh = &self.fs.fh; + let seq = &self.fs.seq; + let fwd = crate::header::relative_dist(seq, fh.order_hints[self.b.ref_frame[0] as usize], fh.order_hint).abs(); + let bck = crate::header::relative_dist(seq, fh.order_hints[self.b.ref_frame[1] as usize], fh.order_hint).abs(); + let mut ctx = if fwd == bck { 3 } else { 0 }; + let n = self.nb; + let mi = &self.t.mi; + let (r, c) = (self.b.mi_row, self.b.mi_col); + if self.b.avail_u { + if !n.above_single { + ctx += mi.compound_idx[mi.idx(r - 1, c)] as usize; + } else if n.above[0] == ALTREF_FRAME as i8 { + ctx += 1; + } + } + if self.b.avail_l { + if !n.left_single { + ctx += mi.compound_idx[mi.idx(r, c - 1)] as usize; + } else if n.left[0] == ALTREF_FRAME as i8 { + ctx += 1; + } + } + ctx + } +} diff --git a/crates/av1/src/intra.rs b/crates/av1/src/intra.rs new file mode 100644 index 0000000..893bb86 --- /dev/null +++ b/crates/av1/src/intra.rs @@ -0,0 +1,403 @@ +//! Intra prediction (spec 7.11.2), palette prediction (7.11.4) and chroma from luma (7.11.5). + +use crate::frame::Plane; +use crate::spec_tables::*; +use crate::transform::round2; + +/// Inputs of the intra prediction process that come from the block. +pub(crate) struct IntraParams { + pub plane: usize, + 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 mode: usize, + pub log2w: u32, + pub log2h: u32, + /// Largest valid x / y in the plane: ((MiCols * MI_SIZE) >> subX) - 1. + pub max_x: i32, + pub max_y: i32, + pub bit_depth: u32, + pub angle_delta: i32, + pub use_filter_intra: bool, + pub filter_intra_mode: usize, + pub enable_intra_edge_filter: bool, + /// Result of the intra filter type process (7.11.2.8). + pub filter_type: bool, +} + +const EDGE: usize = 32; // offset of index 0 in the edge buffers (they start at index -2 .. -1) + +#[inline(always)] +fn clip1(v: i32, bd: u32) -> u16 { + v.clamp(0, (1 << bd) - 1) as u16 +} + +pub(crate) fn is_directional_mode(mode: usize) -> bool { + (V_PRED..=D67_PRED).contains(&mode) +} + +/// Predict the w x h block at (x, y) of `pl` (writes into the plane). +pub(crate) fn predict_intra(pl: &mut Plane, p: &IntraParams) { + let w = 1usize << p.log2w; + let h = 1usize << p.log2h; + let bd = p.bit_depth; + let (x, y) = (p.x, p.y); + // edge arrays with room for index -2 .. 2*(w+h) + let mut above = [0i32; 2 * 128 + 2 * EDGE]; + let mut left = [0i32; 2 * 128 + 2 * EDGE]; + let n = w + h; + if !p.have_above && p.have_left { + let v = pl.at(x - 1, y) as i32; + above[EDGE..EDGE + n].iter_mut().for_each(|a| *a = v); + } else if !p.have_above && !p.have_left { + let v = (1 << (bd - 1)) - 1; + above[EDGE..EDGE + n].iter_mut().for_each(|a| *a = v); + } else { + let above_limit = (p.max_x).min(x as i32 + if p.have_above_right { 2 * w as i32 } else { w as i32 } - 1); + let row = pl.row_from(y - 1, x); + for i in 0..n { + above[EDGE + i] = row[(above_limit.min(x as i32 + i as i32)) as usize - x] as i32; + } + } + if !p.have_left && p.have_above { + let v = pl.at(x, y - 1) as i32; + left[EDGE..EDGE + n].iter_mut().for_each(|a| *a = v); + } else if !p.have_left && !p.have_above { + let v = (1 << (bd - 1)) + 1; + left[EDGE..EDGE + n].iter_mut().for_each(|a| *a = v); + } else { + let left_limit = (p.max_y).min(y as i32 + if p.have_below_left { 2 * h as i32 } else { h as i32 } - 1); + for i in 0..n { + left[EDGE + i] = pl.at(x - 1, left_limit.min(y as i32 + i as i32) as usize) as i32; + } + } + let corner = if p.have_above && p.have_left { + pl.at(x - 1, y - 1) as i32 + } else if p.have_above { + pl.at(x, y - 1) as i32 + } else if p.have_left { + pl.at(x - 1, y) as i32 + } else { + 1 << (bd - 1) + }; + above[EDGE - 1] = corner; + left[EDGE - 1] = corner; + + let mut pred = [0u16; 64 * 64]; + if p.plane == 0 && p.use_filter_intra { + recursive_intra(&above, &left, w, h, p.filter_intra_mode, bd, &mut pred); + } else if is_directional_mode(p.mode) { + directional(&mut above, &mut left, p, w, h, &mut pred); + } else if p.mode == SMOOTH_PRED || p.mode == SMOOTH_V_PRED || p.mode == SMOOTH_H_PRED { + smooth(&above, &left, p.mode, p.log2w, p.log2h, w, h, &mut pred); + } else if p.mode == DC_PRED { + dc(&above, &left, p.have_left, p.have_above, p.log2w, p.log2h, w, h, bd, &mut pred); + } else { + // PAETH_PRED + let tl = above[EDGE - 1]; + for i in 0..h { + for j in 0..w { + let a = above[EDGE + j]; + let l = left[EDGE + 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 + }; + } + } + } + for i in 0..h { + pl.row_from_mut(y + i, x)[..w].copy_from_slice(&pred[i * w..i * w + w]); + } +} + +fn recursive_intra(above: &[i32], left: &[i32], w: usize, h: usize, mode: usize, bd: u32, pred: &mut [u16]) { + let w4 = w >> 2; + let h2 = h >> 1; + for i2 in 0..h2 { + for j4 in 0..w4 { + let mut pv = [0i32; 7]; + for (i, v) in pv.iter_mut().enumerate() { + *v = if i < 5 { + if i2 == 0 { + above[EDGE + (j4 << 2) + i - 1] + } else if j4 == 0 && i == 0 { + left[EDGE + (i2 << 1) - 1] + } else { + pred[((i2 << 1) - 1) * w + (j4 << 2) + i - 1] as i32 + } + } else if j4 == 0 { + left[EDGE + (i2 << 1) + i - 5] + } else { + pred[((i2 << 1) + i - 5) * w + (j4 << 2) - 1] as i32 + }; + } + for i1 in 0..2 { + for j1 in 0..4 { + let mut pr = 0i32; + for i in 0..7 { + pr += INTRA_FILTER_TAPS[mode][(i1 << 2) + j1][i] as i32 * pv[i]; + } + pred[((i2 << 1) + i1) * w + (j4 << 2) + j1] = clip1(round2_signed(pr, INTRA_FILTER_SCALE_BITS as u32), bd); + } + } + } + } +} + +#[inline(always)] +pub(crate) fn round2_signed(x: i32, n: u32) -> i32 { + if x >= 0 { round2(x, n) } else { -round2(-x, n) } +} + +fn sm_weights(log2: u32) -> &'static [u8] { + match log2 { + 2 => &SM_WEIGHTS_TX_4X4, + 3 => &SM_WEIGHTS_TX_8X8, + 4 => &SM_WEIGHTS_TX_16X16, + 5 => &SM_WEIGHTS_TX_32X32, + _ => &SM_WEIGHTS_TX_64X64, + } +} + +#[allow(clippy::too_many_arguments)] +fn smooth(above: &[i32], left: &[i32], mode: usize, log2w: u32, log2h: u32, w: usize, h: usize, pred: &mut [u16]) { + let wx = sm_weights(log2w); + let wy = sm_weights(log2h); + for i in 0..h { + for j in 0..w { + let v = if mode == SMOOTH_PRED { + let s = wy[i] as i32 * above[EDGE + j] + + (256 - wy[i] as i32) * left[EDGE + h - 1] + + wx[j] as i32 * left[EDGE + i] + + (256 - wx[j] as i32) * above[EDGE + w - 1]; + round2(s, 9) + } else if mode == SMOOTH_V_PRED { + round2(wy[i] as i32 * above[EDGE + j] + (256 - wy[i] as i32) * left[EDGE + h - 1], 8) + } else { + round2(wx[j] as i32 * left[EDGE + i] + (256 - wx[j] as i32) * above[EDGE + w - 1], 8) + }; + pred[i * w + j] = v as u16; + } + } +} + +#[allow(clippy::too_many_arguments)] +fn dc(above: &[i32], left: &[i32], have_left: bool, have_above: bool, log2w: u32, log2h: u32, w: usize, h: usize, bd: u32, pred: &mut [u16]) { + let v = if have_left && have_above { + let mut sum: i32 = left[EDGE..EDGE + h].iter().sum::() + above[EDGE..EDGE + w].iter().sum::(); + sum += ((w + h) >> 1) as i32; + sum / (w + h) as i32 + } else if have_left { + let sum: i32 = left[EDGE..EDGE + h].iter().sum(); + (sum + (h >> 1) as i32) >> log2h + } else if have_above { + let sum: i32 = above[EDGE..EDGE + w].iter().sum(); + (sum + (w >> 1) as i32) >> log2w + } else { + 1 << (bd - 1) + }; + let v = clip1(v, bd); + pred[..w * h].iter_mut().for_each(|p| *p = v); +} + +fn edge_filter_strength(w: usize, h: usize, filter_type: bool, delta: i32) -> u32 { + let d = delta.abs(); + let blk_wh = w + h; + let mut strength = 0; + if !filter_type { + if blk_wh <= 8 { + if d >= 56 { + strength = 1; + } + } else if blk_wh <= 16 { + if d >= 40 { + strength = 1; + } + } else if blk_wh <= 24 { + if d >= 8 { + strength = 1; + } + if d >= 16 { + strength = 2; + } + if d >= 32 { + strength = 3; + } + } else if blk_wh <= 32 { + strength = 1; + if d >= 4 { + strength = 2; + } + if d >= 32 { + strength = 3; + } + } else { + strength = 3; + } + } else if blk_wh <= 8 { + if d >= 40 { + strength = 1; + } + if d >= 64 { + strength = 2; + } + } else if blk_wh <= 16 { + if d >= 20 { + strength = 1; + } + if d >= 48 { + strength = 2; + } + } else if blk_wh <= 24 { + if d >= 4 { + strength = 3; + } + } else { + strength = 3; + } + strength +} + +fn use_upsample(w: usize, h: usize, filter_type: bool, delta: i32) -> bool { + let d = delta.abs(); + let blk_wh = w + h; + if d <= 0 || d >= 40 { + false + } else if !filter_type { + blk_wh <= 16 + } else { + blk_wh <= 8 + } +} + +/// Intra edge filter (7.11.2.12) on buf[-1 .. sz-2] (edge[i] = buf[i - 1]). +fn edge_filter(buf: &mut [i32], sz: usize, strength: u32) { + if strength == 0 { + return; + } + let mut edge = [0i32; 2 * 128 + 2 * EDGE]; + for i in 0..sz { + edge[i] = buf[EDGE + i - 1]; + } + for i in 1..sz { + let mut s = 0; + for j in 0..INTRA_EDGE_TAPS { + let k = (i as i32 - 2 + j as i32).clamp(0, sz as i32 - 1) as usize; + s += INTRA_EDGE_KERNEL[strength as usize - 1][j] as i32 * edge[k]; + } + buf[EDGE + i - 1] = (s + 8) >> 4; + } +} + +/// Intra edge upsample (7.11.2.11). +fn edge_upsample(buf: &mut [i32], num_px: usize, bd: u32) { + let mut dup = [0i32; 2 * 128 + 8]; + dup[0] = buf[EDGE - 1]; + for i in -1..num_px as i32 { + dup[(i + 2) as usize] = buf[(EDGE as i32 + i) as usize]; + } + dup[num_px + 2] = buf[EDGE + num_px - 1]; + buf[EDGE - 2] = dup[0]; + for i in 0..num_px { + let s = -dup[i] + 9 * dup[i + 1] + 9 * dup[i + 2] - dup[i + 3]; + let s = clip1(round2(s, 4), bd) as i32; + buf[EDGE + 2 * i - 1] = s; + buf[EDGE + 2 * i] = dup[i + 2]; + } +} + +fn directional(above: &mut [i32], left: &mut [i32], p: &IntraParams, w: usize, h: usize, pred: &mut [u16]) { + let p_angle = MODE_TO_ANGLE[p.mode] as i32 + p.angle_delta * ANGLE_STEP as i32; + let mut upsample_above = 0u32; + let mut upsample_left = 0u32; + if p.enable_intra_edge_filter { + if p_angle != 90 && p_angle != 180 { + if p_angle > 90 && p_angle < 180 && (w + h) >= 24 { + let s = left[EDGE] * 5 + above[EDGE - 1] * 6 + above[EDGE] * 5; + let c = round2(s, 4); + left[EDGE - 1] = c; + above[EDGE - 1] = c; + } + if p.have_above { + let strength = edge_filter_strength(w, h, p.filter_type, p_angle - 90); + let num_px = w.min((p.max_x - p.x as i32 + 1) as usize) + if p_angle < 90 { h } else { 0 } + 1; + edge_filter(above, num_px, strength); + } + if p.have_left { + let strength = edge_filter_strength(w, h, p.filter_type, p_angle - 180); + let num_px = h.min((p.max_y - p.y as i32 + 1) as usize) + if p_angle > 180 { w } else { 0 } + 1; + edge_filter(left, num_px, strength); + } + } + if use_upsample(w, h, p.filter_type, p_angle - 90) { + upsample_above = 1; + let num_px = w + if p_angle < 90 { h } else { 0 }; + edge_upsample(above, num_px, p.bit_depth); + } + if use_upsample(w, h, p.filter_type, p_angle - 180) { + upsample_left = 1; + let num_px = h + if p_angle > 180 { w } else { 0 }; + edge_upsample(left, num_px, p.bit_depth); + } + } + let dx = if p_angle < 90 { + DR_INTRA_DERIVATIVE[p_angle as usize] as i32 + } else if p_angle > 90 && p_angle < 180 { + DR_INTRA_DERIVATIVE[(180 - p_angle) as usize] as i32 + } else { + 0 + }; + let dy = if p_angle > 90 && p_angle < 180 { + DR_INTRA_DERIVATIVE[(p_angle - 90) as usize] as i32 + } else if p_angle > 180 { + DR_INTRA_DERIVATIVE[(270 - p_angle) as usize] as i32 + } else { + 0 + }; + let a = |i: i32| above[(EDGE as i32 + i) as usize]; + let l = |i: i32| left[(EDGE as i32 + i) as usize]; + for i in 0..h as i32 { + for j in 0..w as i32 { + let v = if p_angle < 90 { + let idx = (i + 1) * dx; + let base = (idx >> (6 - upsample_above)) + (j << upsample_above); + let shift = ((idx << upsample_above) >> 1) & 0x1f; + let max_base_x = (w as i32 + h as i32 - 1) << upsample_above; + if base < max_base_x { round2(a(base) * (32 - shift) + a(base + 1) * shift, 5) } else { a(max_base_x) } + } else if p_angle > 90 && p_angle < 180 { + let idx = (j << 6) - (i + 1) * dx; + let base = idx >> (6 - upsample_above); + if base >= -(1 << upsample_above) { + let shift = ((idx << upsample_above) >> 1) & 0x1f; + round2(a(base) * (32 - shift) + a(base + 1) * shift, 5) + } else { + let idx = (i << 6) - (j + 1) * dy; + let base = idx >> (6 - upsample_left); + let shift = ((idx << upsample_left) >> 1) & 0x1f; + round2(l(base) * (32 - shift) + l(base + 1) * shift, 5) + } + } else if p_angle > 180 { + let idx = (j + 1) * dy; + let base = (idx >> (6 - upsample_left)) + (i << upsample_left); + let shift = ((idx << upsample_left) >> 1) & 0x1f; + round2(l(base) * (32 - shift) + l(base + 1) * shift, 5) + } else if p_angle == 90 { + a(j) + } else { + l(i) + }; + pred[(i as usize) * w + j as usize] = v as u16; + } + } +} diff --git a/crates/av1/src/lib.rs b/crates/av1/src/lib.rs new file mode 100644 index 0000000..a4ca61a --- /dev/null +++ b/crates/av1/src/lib.rs @@ -0,0 +1,123 @@ +//! Clean-room AV1 decoder, implemented from the AV1 Bitstream & Decoding Process Specification +//! v1.0.0 with Errata 1 (AOMediaCodec/av1-spec). +//! +//! See the crate README for the conformance status of each decoding stage. + +// Items used only by stages still being written (inter prediction, post filters). +// Spec-style indexed loops (FilmCraft allows this lint workspace-wide). +#![allow(clippy::needless_range_loop)] +#![allow(dead_code)] +#![cfg_attr(not(test), deny(clippy::unwrap_used, clippy::expect_used, clippy::panic, clippy::unimplemented, clippy::todo, clippy::unreachable))] + +mod bits; +mod cdef; +mod cdf; +mod decoder; +mod frame; +mod grain; +pub mod header; +mod inter; +mod inter_info; +mod intra; +mod mvpred; +mod par; +mod postfilter; +mod restoration; +mod spec_tables; +mod state; +mod stats; +mod symbol; +mod tile; +mod transform; + +pub use decoder::Decoder; +pub use header::{ColorConfig, FrameHeader, SequenceHeader}; +pub use stats::{DecodeStats, Stage}; + +/// Decoder errors. +#[derive(Debug, Clone, PartialEq, Eq, thiserror::Error)] +pub enum Error { + #[error("truncated AV1 data")] + Truncated, + #[error("invalid AV1 data: {0}")] + Invalid(&'static str), + #[error("unsupported AV1 feature: {0}")] + Unsupported(&'static str), +} + +pub type Result = std::result::Result; + +/// A decoded (shown) frame: planar samples without row padding, `bit_depth` significant bits in +/// `u16`. Chroma planes are `((width + ssx) >> ssx) x ((height + ssy) >> ssy)`; they are empty +/// for monochrome streams. +#[derive(Debug, Clone, PartialEq, Eq)] +pub struct Picture { + pub width: u32, + pub height: u32, + pub bit_depth: u8, + pub subsampling_x: u8, + pub subsampling_y: u8, + pub mono_chrome: bool, + pub planes: [Vec; 3], + pub color_primaries: u8, + pub transfer_characteristics: u8, + pub matrix_coefficients: u8, + pub full_range: bool, + /// Timestamp of the temporal unit that showed the picture ([`Decoder::decode_pts`]). + pub pts: i64, + /// Decoded in draft mode without deblocking / CDEF / loop restoration + /// ([`Decoder::set_draft`]): approximate, for reduced-resolution playback only. + pub draft: bool, +} + +impl Picture { + pub fn plane_width(&self, plane: usize) -> u32 { + if plane == 0 { self.width } else { (self.width + self.subsampling_x as u32) >> self.subsampling_x } + } + pub fn plane_height(&self, plane: usize) -> u32 { + if plane == 0 { self.height } else { (self.height + self.subsampling_y as u32) >> self.subsampling_y } + } +} + +/// Whether a temporal unit starts a random access point: its first frame is a shown key frame +/// (used for keyframe detection: containers may flag samples as sync that are not). +pub fn is_key_frame_unit(data: &[u8]) -> bool { + let mut pos = 0; + let mut seq_seen = false; + while pos < data.len() { + let h = data[pos]; + let obu_type = (h >> 3) & 0xf; + let ext = h & 4 != 0; + let has_size = h & 2 != 0; + let mut p = pos + 1 + ext as usize; + let size = if has_size { + match bits::leb128(data.get(p..).unwrap_or(&[])) { + Ok((v, n)) => { + p += n; + v as usize + } + Err(_) => return false, + } + } else { + data.len().saturating_sub(p) + }; + if p > data.len() { + return false; + } + match obu_type { + 1 => seq_seen = true, + 3 | 6 => { + let b = data.get(p).copied().unwrap_or(0); + // show_existing_frame = 0, frame_type = KEY_FRAME (0), show_frame = 1 + let show_existing = b >> 7; + let frame_type = (b >> 5) & 3; + let show_frame = (b >> 4) & 1; + let _ = seq_seen; + return show_existing == 0 && frame_type == 0 && show_frame == 1; + } + _ => {} + } + pos = p.saturating_add(size); + } + false +} diff --git a/crates/av1/src/mvpred.rs b/crates/av1/src/mvpred.rs new file mode 100644 index 0000000..d281003 --- /dev/null +++ b/crates/av1/src/mvpred.rs @@ -0,0 +1,734 @@ +//! Motion vector prediction processes (spec 7.10): find_mv_stack, has_overlappable_candidates +//! and find_warp_samples. + +use crate::spec_tables::*; +use crate::tile::TileDecoder; + +pub(crate) type MvP = [i32; 2]; +pub(crate) const INVALID_MV: i32 = -1 << 15; + +/// Output of find_mv_stack plus its working state. +#[derive(Clone, Default)] +pub(crate) struct MvStack { + pub ref_stack_mv: [[MvP; 2]; MAX_REF_MV_STACK_SIZE + 1], + pub weight_stack: [u32; MAX_REF_MV_STACK_SIZE + 1], + pub num_mv_found: usize, + pub new_mv_count: usize, + pub global_mvs: [MvP; 2], + pub found_match: bool, + pub close_matches: usize, + pub total_matches: usize, + pub new_mv_context: usize, + pub ref_mv_context: usize, + pub zero_mv_context: usize, + pub drl_ctx_stack: [usize; MAX_REF_MV_STACK_SIZE + 1], + ref_id_count: [usize; 2], + ref_diff_count: [usize; 2], + ref_id_mvs: [[MvP; 2]; 2], + ref_diff_mvs: [[MvP; 2]; 2], + // warp samples + pub num_samples: usize, + pub num_samples_scanned: usize, + pub cand_list: [[i32; 4]; LEAST_SQUARES_SAMPLES_MAX], +} + +fn block_width(bs: usize) -> i32 { + 4 * NUM_4X4_BLOCKS_WIDE[bs] as i32 +} +fn block_height(bs: usize) -> i32 { + 4 * NUM_4X4_BLOCKS_HIGH[bs] as i32 +} + +#[inline] +pub(crate) fn round2_signed64(x: i64, n: u32) -> i64 { + if n == 0 { + return x; + } + if x >= 0 { (x + (1 << (n - 1))) >> n } else { -((-x + (1 << (n - 1))) >> n) } +} + +pub(crate) fn has_newmv(mode: usize) -> bool { + matches!(mode, NEWMV | NEW_NEWMV | NEAR_NEWMV | NEW_NEARMV | NEAREST_NEWMV | NEW_NEARESTMV) +} + +impl TileDecoder<'_, '_> { + /// lower_mv_precision( candMv ) (7.10.2.10) + pub(crate) fn lower_mv_precision(&self, mv: &mut MvP) { + let fh = &self.fs.fh; + if fh.allow_high_precision_mv { + return; + } + for c in mv.iter_mut() { + if fh.force_integer_mv { + let a = c.abs(); + let a_int = (a + 3) >> 3; + *c = if *c > 0 { a_int << 3 } else { -(a_int << 3) }; + } else if *c & 1 != 0 { + *c += if *c > 0 { -1 } else { 1 }; + } + } + } + + /// Setup global MV process (7.10.2.1). + pub(crate) fn setup_global_mv(&self, ref_list: usize) -> MvP { + let rf = self.b.ref_frame[ref_list]; + let fh = &self.fs.fh; + let mut mv: MvP = [0, 0]; + if rf > INTRA_FRAME as i8 { + let rf = rf as usize; + let typ = fh.gm_type[rf] as usize; + let gm = &fh.gm_params[rf]; + if typ == TRANSLATION { + mv = [gm[0] >> (WARPEDMODEL_PREC_BITS - 3), gm[1] >> (WARPEDMODEL_PREC_BITS - 3)]; + } else if typ != IDENTITY { + let bw = block_width(self.b.mi_size) as i64; + let bh = block_height(self.b.mi_size) as i64; + let x = self.b.mi_col as i64 * 4 + bw / 2 - 1; + let y = self.b.mi_row as i64 * 4 + bh / 2 - 1; + let p = 1i64 << WARPEDMODEL_PREC_BITS; + let xc = (gm[2] as i64 - p) * x + gm[3] as i64 * y + gm[0] as i64; + let yc = gm[4] as i64 * x + (gm[5] as i64 - p) * y + gm[1] as i64; + if fh.allow_high_precision_mv { + mv = [round2_signed64(yc, WARPEDMODEL_PREC_BITS as u32 - 3) as i32, round2_signed64(xc, WARPEDMODEL_PREC_BITS as u32 - 3) as i32]; + } else { + mv = [ + round2_signed64(yc, WARPEDMODEL_PREC_BITS as u32 - 2) as i32 * 2, + round2_signed64(xc, WARPEDMODEL_PREC_BITS as u32 - 2) as i32 * 2, + ]; + } + } + } + self.lower_mv_precision(&mut mv); + mv + } + + /// Find MV stack process (7.10.2). + pub(crate) fn find_mv_stack(&mut self, is_compound: bool) { + let bw4 = self.b.bw4 as isize; + let bh4 = self.b.bh4 as isize; + let mut s = MvStack { global_mvs: [self.setup_global_mv(0), [0, 0]], ..Default::default() }; + if is_compound { + s.global_mvs[1] = self.setup_global_mv(1); + } + self.mvs = s; + self.mvs.found_match = false; + self.scan_row(-1, is_compound); + let mut found_above = self.mvs.found_match; + self.mvs.found_match = false; + self.scan_col(-1, is_compound); + let mut found_left = self.mvs.found_match; + self.mvs.found_match = false; + if bw4.max(bh4) <= 16 { + self.scan_point(-1, bw4, is_compound); + } + 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; + if self.fs.fh.use_ref_frame_mvs { + self.temporal_scan(is_compound); + } + self.scan_point(-1, -1, is_compound); + if self.mvs.found_match { + found_above = true; + } + self.mvs.found_match = false; + self.scan_row(-3, is_compound); + if self.mvs.found_match { + found_above = true; + } + self.mvs.found_match = false; + self.scan_col(-3, is_compound); + if self.mvs.found_match { + found_left = true; + } + self.mvs.found_match = false; + if bh4 > 1 { + self.scan_row(-5, is_compound); + } + if self.mvs.found_match { + found_above = true; + } + self.mvs.found_match = false; + if bw4 > 1 { + self.scan_col(-5, is_compound); + } + 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, is_compound); + self.sort_stack(num_nearest, n, is_compound); + if self.mvs.num_mv_found < 2 { + self.extra_search(is_compound); + } + self.context_and_clamping(is_compound, num_new); + } + + fn scan_row(&mut self, delta_row: isize, is_compound: bool) { + let bw4 = self.b.bw4 as isize; + let (mi_row, mi_col) = (self.b.mi_row as isize, self.b.mi_col as isize); + let mi_cols = self.fs.fh.mi_cols as isize; + let end4 = bw4.min(mi_cols - 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 mi = &self.t.mi; + let mut len = bw4.min(NUM_4X4_BLOCKS_WIDE[mi.mi_size[mi.idx(mv_row as usize, mv_col as usize)] as usize] as isize); + if delta_row.abs() > 1 { + len = len.max(2); + } + if use_step16 { + len = len.max(4); + } + let weight = len as u32 * 2; + self.add_ref_mv_candidate(mv_row as usize, mv_col as usize, is_compound, weight); + i += len; + } + } + + fn scan_col(&mut self, delta_col: isize, is_compound: bool) { + let bh4 = self.b.bh4 as isize; + let (mi_row, mi_col) = (self.b.mi_row as isize, self.b.mi_col as isize); + let mi_rows = self.fs.fh.mi_rows as isize; + let end4 = bh4.min(mi_rows - 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 mi = &self.t.mi; + let mut len = bh4.min(NUM_4X4_BLOCKS_HIGH[mi.mi_size[mi.idx(mv_row as usize, mv_col as usize)] as usize] as isize); + if delta_col.abs() > 1 { + len = len.max(2); + } + if use_step16 { + len = len.max(4); + } + let weight = len as u32 * 2; + self.add_ref_mv_candidate(mv_row as usize, mv_col as usize, is_compound, weight); + i += len; + } + } + + fn scan_point(&mut self, delta_row: isize, delta_col: isize, is_compound: bool) { + let mv_row = self.b.mi_row as isize + delta_row; + let mv_col = self.b.mi_col as isize + delta_col; + if self.inside(mv_row, mv_col) && self.t.mi.written[self.t.mi.idx(mv_row as usize, mv_col as usize)] { + self.add_ref_mv_candidate(mv_row as usize, mv_col as usize, is_compound, 4); + } + } + + fn temporal_scan(&mut self, is_compound: bool) { + let bw4 = self.b.bw4 as isize; + let bh4 = self.b.bh4 as isize; + let step_w4 = if bw4 >= 16 { 4 } else { 2 }; + let step_h4 = if bh4 >= 16 { 4 } else { 2 }; + let mut dr = 0; + while dr < bh4.min(16) { + let mut dc = 0; + while dc < bw4.min(16) { + self.add_tpl_ref_mv(dr, dc, is_compound); + dc += step_w4; + } + dr += step_h4; + } + let allow_extension = (2..16).contains(&bh4) && (2..16).contains(&bw4); + if allow_extension { + let pos = [(bh4, -2), (bh4, bw4), (bh4 - 2, bw4)]; + for &(dr, dc) in &pos { + let row = (self.b.mi_row as isize & 15) + dr; + let col = (self.b.mi_col as isize & 15) + dc; + if (0..16).contains(&row) && (0..16).contains(&col) { + self.add_tpl_ref_mv(dr, dc, is_compound); + } + } + } + } + + fn add_tpl_ref_mv(&mut self, delta_row: isize, delta_col: isize, is_compound: bool) { + let mv_row = (self.b.mi_row as isize + delta_row) | 1; + let mv_col = (self.b.mi_col as isize + delta_col) | 1; + if !self.inside(mv_row, mv_col) { + return; + } + let x8 = (mv_col >> 1) as usize; + let y8 = (mv_row >> 1) as usize; + let w8 = self.fs.fh.mi_cols as usize >> 1; + if delta_row == 0 && delta_col == 0 { + self.mvs.zero_mv_context = 1; + } + let rf0 = self.b.ref_frame[0] as usize; + if !is_compound { + let mut cand = self.fs.motion_field[rf0][y8 * w8 + x8]; + if cand[0] == INVALID_MV { + return; + } + self.lower_mv_precision(&mut cand); + if delta_row == 0 && delta_col == 0 { + let g = self.mvs.global_mvs[0]; + self.mvs.zero_mv_context = ((cand[0] - g[0]).abs() >= 16 || (cand[1] - g[1]).abs() >= 16) as usize; + } + let n = self.mvs.num_mv_found; + let idx = (0..n).find(|&i| self.mvs.ref_stack_mv[i][0] == cand); + if let Some(i) = idx { + self.mvs.weight_stack[i] += 2; + } else if n < MAX_REF_MV_STACK_SIZE { + self.mvs.ref_stack_mv[n][0] = cand; + self.mvs.weight_stack[n] = 2; + self.mvs.num_mv_found += 1; + } + } else { + let rf1 = self.b.ref_frame[1] as usize; + let mut c0 = self.fs.motion_field[rf0][y8 * w8 + x8]; + if c0[0] == INVALID_MV { + return; + } + let mut c1 = self.fs.motion_field[rf1][y8 * w8 + x8]; + if c1[0] == INVALID_MV { + return; + } + self.lower_mv_precision(&mut c0); + self.lower_mv_precision(&mut c1); + if delta_row == 0 && delta_col == 0 { + let g = self.mvs.global_mvs; + self.mvs.zero_mv_context = ((c0[0] - g[0][0]).abs() >= 16 + || (c0[1] - g[0][1]).abs() >= 16 + || (c1[0] - g[1][0]).abs() >= 16 + || (c1[1] - g[1][1]).abs() >= 16) as usize; + } + let n = self.mvs.num_mv_found; + let idx = (0..n).find(|&i| self.mvs.ref_stack_mv[i][0] == c0 && self.mvs.ref_stack_mv[i][1] == c1); + if let Some(i) = idx { + self.mvs.weight_stack[i] += 2; + } else if n < MAX_REF_MV_STACK_SIZE { + self.mvs.ref_stack_mv[n] = [c0, c1]; + self.mvs.weight_stack[n] = 2; + self.mvs.num_mv_found += 1; + } + } + } + + fn add_ref_mv_candidate(&mut self, mv_row: usize, mv_col: usize, is_compound: bool, weight: u32) { + let mi = &self.t.mi; + let i = mi.idx(mv_row, mv_col); + if !mi.is_inter[i] { + return; + } + let rf = mi.ref_frame[i]; + if !is_compound { + for cand_list in 0..2 { + if rf[cand_list] == self.b.ref_frame[0] { + self.search_stack(mv_row, mv_col, cand_list, weight); + } + } + } else if rf[0] == self.b.ref_frame[0] && rf[1] == self.b.ref_frame[1] { + self.compound_search_stack(mv_row, mv_col, weight); + } + } + + fn search_stack(&mut self, mv_row: usize, mv_col: usize, cand_list: usize, weight: u32) { + let mi = &self.t.mi; + let i = mi.idx(mv_row, mv_col); + let cand_mode = mi.y_mode[i] as usize; + let cand_size = mi.mi_size[i] as usize; + let large = block_width(cand_size).min(block_height(cand_size)) >= 8; + let gm0 = self.fs.fh.gm_type[self.b.ref_frame[0] as usize] as usize; + let mut cand = if (cand_mode == GLOBALMV || cand_mode == GLOBAL_GLOBALMV) && gm0 > TRANSLATION && large { + self.mvs.global_mvs[0] + } else { + let m = mi.mv[i][cand_list]; + [m.row as i32, m.col as i32] + }; + self.lower_mv_precision(&mut cand); + if has_newmv(cand_mode) { + 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][0] == cand) { + self.mvs.weight_stack[idx] += weight; + } else if n < MAX_REF_MV_STACK_SIZE { + self.mvs.ref_stack_mv[n][0] = cand; + self.mvs.weight_stack[n] = weight; + self.mvs.num_mv_found += 1; + } + } + + fn compound_search_stack(&mut self, mv_row: usize, mv_col: usize, weight: u32) { + let mi = &self.t.mi; + let i = mi.idx(mv_row, mv_col); + let mut cand = [[mi.mv[i][0].row as i32, mi.mv[i][0].col as i32], [mi.mv[i][1].row as i32, mi.mv[i][1].col as i32]]; + let cand_mode = mi.y_mode[i] as usize; + if cand_mode == GLOBAL_GLOBALMV { + for (rl, c) in cand.iter_mut().enumerate() { + if self.fs.fh.gm_type[self.b.ref_frame[rl] as usize] as usize > TRANSLATION { + *c = self.mvs.global_mvs[rl]; + } + } + } + for c in cand.iter_mut() { + self.lower_mv_precision(c); + } + 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][0] == cand[0] && self.mvs.ref_stack_mv[k][1] == cand[1]) { + 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; + } + if has_newmv(cand_mode) { + self.mvs.new_mv_count += 1; + } + } + + fn sort_stack(&mut self, start: usize, end: usize, is_compound: bool) { + 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); + for list in 0..1 + is_compound as usize { + let t = s.ref_stack_mv[idx - 1][list]; + s.ref_stack_mv[idx - 1][list] = s.ref_stack_mv[idx][list]; + s.ref_stack_mv[idx][list] = t; + } + new_end = idx; + } + } + end = new_end; + } + } + + fn extra_search(&mut self, is_compound: bool) { + self.mvs.ref_id_count = [0; 2]; + self.mvs.ref_diff_count = [0; 2]; + let (mi_row, mi_col) = (self.b.mi_row as isize, self.b.mi_col as isize); + let mut w4 = (self.b.bw4 as isize).min(16); + let mut h4 = (self.b.bh4 as isize).min(16); + w4 = w4.min(self.fs.fh.mi_cols as isize - mi_col); + h4 = h4.min(self.fs.fh.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; + } + self.add_extra_mv_candidate(mv_row as usize, mv_col as usize, is_compound); + let mi = &self.t.mi; + let sz = mi.mi_size[mi.idx(mv_row as usize, mv_col as usize)] as usize; + idx += if pass == 0 { NUM_4X4_BLOCKS_WIDE[sz] } else { NUM_4X4_BLOCKS_HIGH[sz] } as isize; + } + } + if is_compound { + let mut combined = [[[0i32; 2]; 2]; 2]; + for list in 0..2 { + let mut comp_count = 0; + for idx in 0..self.mvs.ref_id_count[list] { + combined[comp_count][list] = self.mvs.ref_id_mvs[list][idx]; + comp_count += 1; + } + let mut idx = 0; + while idx < self.mvs.ref_diff_count[list] && comp_count < 2 { + combined[comp_count][list] = self.mvs.ref_diff_mvs[list][idx]; + comp_count += 1; + idx += 1; + } + while comp_count < 2 { + combined[comp_count][list] = self.mvs.global_mvs[list]; + comp_count += 1; + } + } + let n = self.mvs.num_mv_found; + if n == 1 { + if combined[0][0] == self.mvs.ref_stack_mv[0][0] && combined[0][1] == self.mvs.ref_stack_mv[0][1] { + self.mvs.ref_stack_mv[n] = combined[1]; + } else { + self.mvs.ref_stack_mv[n] = combined[0]; + } + self.mvs.weight_stack[n] = 2; + self.mvs.num_mv_found += 1; + } else { + for c in combined { + let n = self.mvs.num_mv_found; + self.mvs.ref_stack_mv[n] = c; + self.mvs.weight_stack[n] = 2; + self.mvs.num_mv_found += 1; + } + } + } else { + for idx in self.mvs.num_mv_found..2 { + self.mvs.ref_stack_mv[idx][0] = self.mvs.global_mvs[0]; + } + } + } + + fn add_extra_mv_candidate(&mut self, mv_row: usize, mv_col: usize, is_compound: bool) { + let mi = &self.t.mi; + let i = mi.idx(mv_row, mv_col); + let rfs = mi.ref_frame[i]; + let mvs = mi.mv[i]; + let bias = &self.fs.fh.ref_frame_sign_bias; + if is_compound { + for cand_list in 0..2 { + let cand_ref = rfs[cand_list]; + if cand_ref > INTRA_FRAME as i8 { + for list in 0..2 { + let mut cand = [mvs[cand_list].row as i32, mvs[cand_list].col as i32]; + if cand_ref == self.b.ref_frame[list] && self.mvs.ref_id_count[list] < 2 { + let k = self.mvs.ref_id_count[list]; + self.mvs.ref_id_mvs[list][k] = cand; + self.mvs.ref_id_count[list] += 1; + } else if self.mvs.ref_diff_count[list] < 2 { + if bias[cand_ref as usize] != bias[self.b.ref_frame[list] as usize] { + cand = [-cand[0], -cand[1]]; + } + let k = self.mvs.ref_diff_count[list]; + self.mvs.ref_diff_mvs[list][k] = cand; + self.mvs.ref_diff_count[list] += 1; + } + } + } + } + } else { + for cand_list in 0..2 { + let cand_ref = rfs[cand_list]; + if cand_ref > INTRA_FRAME as i8 { + let mut cand = [mvs[cand_list].row as i32, mvs[cand_list].col as i32]; + if bias[cand_ref as usize] != bias[self.b.ref_frame[0] as usize] { + cand = [-cand[0], -cand[1]]; + } + let n = self.mvs.num_mv_found; + if !(0..n).any(|k| self.mvs.ref_stack_mv[k][0] == cand) { + self.mvs.ref_stack_mv[n][0] = cand; + self.mvs.weight_stack[n] = 2; + self.mvs.num_mv_found += 1; + } + } + } + } + } + + fn context_and_clamping(&mut self, is_compound: bool, num_new: usize) { + let bw = block_width(self.b.mi_size); + let bh = block_height(self.b.mi_size); + 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; + } + for list in 0..1 + is_compound as usize { + for idx in 0..n { + let mut mv = self.mvs.ref_stack_mv[idx][list]; + mv[0] = self.clamp_mv_row(mv[0], MV_BORDER as i32 + bh * 8); + mv[1] = self.clamp_mv_col(mv[1], MV_BORDER as i32 + bw * 8); + self.mvs.ref_stack_mv[idx][list] = 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; + } + } + + pub(crate) fn clamp_mv_row(&self, mvec: i32, border: i32) -> i32 { + let bh4 = self.b.bh4 as i32; + let to_top = -((self.b.mi_row as i32 * 4) * 8); + let to_bottom = ((self.fs.fh.mi_rows as i32 - bh4 - self.b.mi_row as i32) * 4) * 8; + mvec.clamp(to_top - border, to_bottom + border) + } + + pub(crate) fn clamp_mv_col(&self, mvec: i32, border: i32) -> i32 { + let bw4 = self.b.bw4 as i32; + let to_left = -((self.b.mi_col as i32 * 4) * 8); + let to_right = ((self.fs.fh.mi_cols as i32 - bw4 - self.b.mi_col as i32) * 4) * 8; + mvec.clamp(to_left - border, to_right + border) + } + + /// has_overlappable_candidates( ) (7.10.3) + pub(crate) fn has_overlappable_candidates(&self) -> bool { + let mi = &self.t.mi; + let (mi_rows, mi_cols) = (self.fs.fh.mi_rows as usize, self.fs.fh.mi_cols as usize); + if self.b.avail_u { + let mut x4 = self.b.mi_col; + while x4 < mi_cols.min(self.b.mi_col + self.b.bw4) { + let c = (x4 | 1).min(mi_cols - 1); + if mi.ref_frame[mi.idx(self.b.mi_row - 1, c)][0] > INTRA_FRAME as i8 { + return true; + } + x4 += 2; + } + } + if self.b.avail_l { + let mut y4 = self.b.mi_row; + while y4 < mi_rows.min(self.b.mi_row + self.b.bh4) { + let r = (y4 | 1).min(mi_rows - 1); + if mi.ref_frame[mi.idx(r, self.b.mi_col - 1)][0] > INTRA_FRAME as i8 { + return true; + } + y4 += 2; + } + } + false + } + + /// Find warp samples process (7.10.4). + pub(crate) fn find_warp_samples(&mut self) { + self.mvs.num_samples = 0; + self.mvs.num_samples_scanned = 0; + let w4 = self.b.bw4 as isize; + let h4 = self.b.bh4 as isize; + let (mi_row, mi_col) = (self.b.mi_row as isize, self.b.mi_col as isize); + let (mi_rows, mi_cols) = (self.fs.fh.mi_rows as isize, self.fs.fh.mi_cols as isize); + let mut do_top_left = true; + let mut do_top_right = true; + if self.b.avail_u { + let mi = &self.t.mi; + let src_w = NUM_4X4_BLOCKS_WIDE[mi.mi_size[mi.idx(mi_row as usize - 1, mi_col as usize)] as usize] as isize; + if w4 <= src_w { + let col_offset = -(mi_col & (src_w - 1)); + if col_offset < 0 { + do_top_left = false; + } + if col_offset + src_w > w4 { + do_top_right = false; + } + self.add_sample(-1, 0); + } else { + let mut i = 0; + while i < w4.min(mi_cols - mi_col) { + let mi = &self.t.mi; + let src_w = NUM_4X4_BLOCKS_WIDE[mi.mi_size[mi.idx(mi_row as usize - 1, (mi_col + i) as usize)] as usize] as isize; + let step = w4.min(src_w); + self.add_sample(-1, i); + i += step; + } + } + } + if self.b.avail_l { + let mi = &self.t.mi; + let src_h = NUM_4X4_BLOCKS_HIGH[mi.mi_size[mi.idx(mi_row as usize, mi_col as usize - 1)] as usize] as isize; + if h4 <= src_h { + let row_offset = -(mi_row & (src_h - 1)); + if row_offset < 0 { + do_top_left = false; + } + self.add_sample(0, -1); + } else { + let mut i = 0; + while i < h4.min(mi_rows - mi_row) { + let mi = &self.t.mi; + let src_h = NUM_4X4_BLOCKS_HIGH[mi.mi_size[mi.idx((mi_row + i) as usize, mi_col as usize - 1)] as usize] as isize; + let step = h4.min(src_h); + self.add_sample(i, -1); + i += step; + } + } + } + if do_top_left { + self.add_sample(-1, -1); + } + if do_top_right && w4.max(h4) <= 16 { + self.add_sample(-1, w4); + } + if self.mvs.num_samples == 0 && self.mvs.num_samples_scanned > 0 { + self.mvs.num_samples = 1; + } + } + + fn add_sample(&mut self, delta_row: isize, delta_col: isize) { + if self.mvs.num_samples_scanned >= LEAST_SQUARES_SAMPLES_MAX { + return; + } + let mv_row = self.b.mi_row as isize + delta_row; + let mv_col = self.b.mi_col as isize + delta_col; + if !self.inside(mv_row, mv_col) { + return; + } + let mi = &self.t.mi; + let i = mi.idx(mv_row as usize, mv_col as usize); + if !mi.written[i] { + return; + } + if mi.ref_frame[i][0] != self.b.ref_frame[0] || mi.ref_frame[i][1] != -1 { + return; + } + let cand_sz = mi.mi_size[i] as usize; + let cand_w4 = NUM_4X4_BLOCKS_WIDE[cand_sz] as isize; + let cand_h4 = NUM_4X4_BLOCKS_HIGH[cand_sz] as isize; + let cand_row = mv_row & !(cand_h4 - 1); + let cand_col = mv_col & !(cand_w4 - 1); + let mid_y = cand_row * 4 + cand_h4 * 2 - 1; + let mid_x = cand_col * 4 + cand_w4 * 2 - 1; + let threshold = block_width(self.b.mi_size).max(block_height(self.b.mi_size)).clamp(16, 112); + let cm = mi.mv[mi.idx(cand_row as usize, cand_col as usize)][0]; + let mv_diff_row = (cm.row as i32 - self.b.mv_i[0][0]).abs(); + let mv_diff_col = (cm.col as i32 - self.b.mv_i[0][1]).abs(); + let valid = mv_diff_row + mv_diff_col <= threshold; + let cand = [mid_y as i32 * 8, mid_x as i32 * 8, mid_y as i32 * 8 + cm.row as i32, mid_x as i32 * 8 + cm.col as i32]; + self.mvs.num_samples_scanned += 1; + if !valid && self.mvs.num_samples_scanned > 1 { + return; + } + self.mvs.cand_list[self.mvs.num_samples] = cand; + if valid { + self.mvs.num_samples += 1; + } + } + + #[inline] + pub(crate) fn inside(&self, r: isize, c: isize) -> bool { + c >= self.mi_col_start as isize && c < self.mi_col_end as isize && r >= self.mi_row_start as isize && r < self.mi_row_end as isize + } +} diff --git a/crates/av1/src/par.rs b/crates/av1/src/par.rs new file mode 100644 index 0000000..cab460d --- /dev/null +++ b/crates/av1/src/par.rs @@ -0,0 +1,72 @@ +//! Optional data parallelism (rayon behind the `threads` feature; sequential otherwise and on +//! wasm32). + +#[cfg(feature = "threads")] +use std::sync::Arc; + +/// A handle to the decoder's worker pool (or none: run on the calling thread). +#[derive(Clone, Default)] +pub(crate) struct Pool { + #[cfg(feature = "threads")] + pool: Option>, +} + +impl Pool { + /// A pool of `threads` workers (sequential when `threads` <= 1, without the `threads` + /// feature, or on wasm32). + pub fn new(threads: usize) -> Pool { + #[cfg(feature = "threads")] + { + let pool = if threads > 1 && cfg!(not(target_arch = "wasm32")) { + rayon::ThreadPoolBuilder::new().num_threads(threads).thread_name(|i| format!("av1-{i}")).build().ok().map(Arc::new) + } else { + None + }; + Pool { pool } + } + #[cfg(not(feature = "threads"))] + { + let _ = threads; + Pool {} + } + } + + pub fn is_parallel(&self) -> bool { + #[cfg(feature = "threads")] + { + self.pool.is_some() + } + #[cfg(not(feature = "threads"))] + { + false + } + } + + /// `f(i, &mut items[i])` for every item, in parallel when possible; results in order. + pub fn map_mut(&self, items: &mut [T], f: impl Fn(usize, &mut T) -> R + Sync + Send) -> Vec { + #[cfg(feature = "threads")] + if let Some(pool) = &self.pool + && items.len() > 1 + { + use rayon::prelude::*; + return pool.install(|| items.par_iter_mut().enumerate().map(|(i, t)| f(i, t)).collect()); + } + items.iter_mut().enumerate().map(|(i, t)| f(i, t)).collect() + } + + /// `f(i, chunk)` for the consecutive `chunk_len`-element chunks of `data`. + pub fn chunks(&self, data: &mut [T], chunk_len: usize, f: impl Fn(usize, &mut [T]) + Sync + Send) { + let chunk_len = chunk_len.max(1); + #[cfg(feature = "threads")] + if let Some(pool) = &self.pool + && data.len() > chunk_len + { + use rayon::prelude::*; + pool.install(|| data.par_chunks_mut(chunk_len).enumerate().for_each(|(i, c)| f(i, c))); + return; + } + for (i, c) in data.chunks_mut(chunk_len).enumerate() { + f(i, c); + } + } +} diff --git a/crates/av1/src/postfilter.rs b/crates/av1/src/postfilter.rs new file mode 100644 index 0000000..ea157de --- /dev/null +++ b/crates/av1/src/postfilter.rs @@ -0,0 +1,266 @@ +//! In-loop post filters run by decode_frame_wrapup: loop filter (7.14), CDEF (7.15), +//! super-resolution upscaling (7.16) and loop restoration (7.17). + +use crate::spec_tables::*; +use crate::state::FrameState; +use crate::stats::{DecodeStats, Stage, Timer}; + +/// The post-filters run on the frame worker; `_pool` is for row-parallel filtering (not yet used). +/// `draft`: only super-resolution (which sets the output size) runs. +pub(crate) fn apply(fs: &mut FrameState, stats: &mut DecodeStats, _pool: &crate::par::Pool, draft: bool) { + let mut t = Timer::start(); + let lvl = fs.fh.lf.level; + if (lvl[0] != 0 || lvl[1] != 0) && !draft { + loop_filter(fs); + } + stats.add(Stage::LoopFilter, t.lap()); + let cdef = if draft { None } else { crate::cdef::apply(fs) }; + stats.add(Stage::Cdef, t.lap()); + let (up_cur, up_cdef) = if fs.fh.use_superres { + let c = crate::restoration::upscale(fs, &fs.cur); + let d = cdef.as_ref().map(|f| crate::restoration::upscale(fs, f)); + (c, d) + } else { + (std::mem::take(&mut fs.cur), cdef) + }; + stats.add(Stage::Superres, t.lap()); + fs.cur = if fs.fh.lr.uses_lr && !draft { + crate::restoration::loop_restoration(fs, &up_cur, up_cdef.as_ref().unwrap_or(&up_cur)) + } else { + up_cdef.unwrap_or(up_cur) + }; + stats.add(Stage::Restoration, t.lap()); +} + +// --------------------------------------------------------------------------------------------- +// Loop filter (7.14) + +fn loop_filter(fs: &mut FrameState) { + let (mi_rows, mi_cols) = (fs.fh.mi_rows as usize, fs.fh.mi_cols as usize); + for plane in 0..fs.num_planes { + if plane == 0 || fs.fh.lf.level[1 + plane] != 0 { + for pass in 0..2 { + let row_step = if plane == 0 { 1 } else { 1 << fs.ssy }; + let col_step = if plane == 0 { 1 } else { 1 << fs.ssx }; + let mut row = 0; + while row < mi_rows { + let mut col = 0; + while col < mi_cols { + edge(fs, plane, pass, row, col); + col += col_step; + } + row += row_step; + } + } + } + } +} + +fn edge(fs: &mut FrameState, plane: usize, pass: usize, row: usize, col: usize) { + let (sub_x, sub_y) = if plane == 0 { (0, 0) } else { (fs.ssx, fs.ssy) }; + let (dx, dy) = if pass == 0 { (1i32, 0i32) } else { (0, 1) }; + let x = col * 4; + let y = row * 4; + let row = row | sub_y; + let col = col | sub_x; + let on_screen = !(x >= fs.fh.frame_width as usize || y >= fs.fh.frame_height as usize || (pass == 0 && x == 0) || (pass == 1 && y == 0)); + if !on_screen { + return; + } + let xp = x >> sub_x; + let yp = y >> sub_y; + let prev_row = row - ((dy as usize) << sub_y); + let prev_col = col - ((dx as usize) << sub_x); + let tx_sz = fs.lf_tx_size(plane, row >> sub_y, col >> sub_x); + 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) }; + if !is_tx_edge { + return; + } + let mi = &fs.mi; + let i = mi.idx(row, col); + let mi_size = mi.mi_size[i] as usize; + let plane_size = fs.plane_residual_size(mi_size, plane); + let skip = mi.skip[i]; + let is_intra = mi.ref_frame[i][0] <= INTRA_FRAME as i8; + 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) + }; + if !(is_block_edge || !skip || is_intra) { + return; + } + let prev_tx_sz = fs.lf_tx_size(plane, prev_row >> sub_y, prev_col >> sub_x); + // filter size (7.14.3) + 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) }; + let (mut lvl, mut limit, mut blimit, mut thresh) = strength(fs, row, col, plane, pass); + if lvl == 0 { + (lvl, limit, blimit, thresh) = strength(fs, prev_row, prev_col, plane, pass); + } + if lvl == 0 { + return; + } + let t = LfThresh::new(limit, blimit, thresh, fs.bit_depth); + let pl = &mut fs.cur.planes[plane]; + let stride = pl.stride; + let (step, along) = if pass == 0 { (1, stride) } else { (stride, 1) }; + let base = yp * stride + xp; + let chroma = plane > 0; + for k in 0..4 { + sample_filter(&mut pl.data, base + k * along, step, chroma, filter_size, t); + } +} + +/// Adaptive filter strength (7.14.4): (lvl, limit, blimit, thresh). +fn strength(fs: &FrameState, row: usize, col: usize, plane: usize, pass: usize) -> (i32, i32, i32, i32) { + let mi = &fs.mi; + let i = mi.idx(row, col); + let segment = mi.segment_id[i] as usize; + let rf = mi.ref_frame[i][0]; + let mode = mi.y_mode[i] as usize; + let mode_type = (mode >= NEARESTMV && mode != GLOBALMV && mode != GLOBAL_GLOBALMV) as usize; + let delta_lf = if fs.fh.delta_lf_multi { mi.delta_lf[i][if plane == 0 { pass } else { plane + 1 }] } else { mi.delta_lf[i][0] } as i32; + // 7.14.5 + let idx = if plane == 0 { pass } else { plane + 1 }; + let base = (delta_lf + fs.fh.lf.level[idx] as i32).clamp(0, MAX_LOOP_FILTER as i32); + let mut lvl_seg = base; + let feature = SEG_LVL_ALT_LF_Y_V + idx; + if fs.fh.seg.enabled && fs.fh.seg.features.enabled[segment][feature] { + lvl_seg = (fs.fh.seg.features.data[segment][feature] + lvl_seg).clamp(0, MAX_LOOP_FILTER as i32); + } + if fs.fh.lf.delta_enabled { + let n_shift = lvl_seg >> 5; + let d = &fs.fh.lf.deltas; + if rf == INTRA_FRAME as i8 { + lvl_seg += d.ref_deltas[INTRA_FRAME] << n_shift; + } else if rf > INTRA_FRAME as i8 { + lvl_seg += (d.ref_deltas[rf as usize] << n_shift) + (d.mode_deltas[mode_type] << n_shift); + } + lvl_seg = lvl_seg.clamp(0, MAX_LOOP_FILTER as i32); + } + let lvl = lvl_seg; + let sharp = fs.fh.lf.sharpness as i32; + let shift = if sharp > 4 { + 2 + } else if sharp > 0 { + 1 + } else { + 0 + }; + let limit = if sharp > 0 { (lvl >> shift).clamp(1, 9 - sharp) } else { (lvl >> shift).max(1) }; + let blimit = 2 * (lvl + 2) + limit; + let thresh = lvl >> 4; + (lvl, limit, blimit, thresh) +} + +/// Per-edge thresholds of the sample filter, scaled to the bit depth. +#[derive(Clone, Copy)] +struct LfThresh { + limit: i32, + blimit: i32, + thresh: i32, + /// Flatness threshold 1 << (BitDepth - 8). + flat: i32, + /// 0x80 << (BitDepth - 8) and 1 << (BitDepth - 1). + off: i32, + half: i32, +} + +impl LfThresh { + fn new(limit: i32, blimit: i32, thresh: i32, bd: u32) -> LfThresh { + let sh = bd - 8; + LfThresh { limit: limit << sh, blimit: blimit << sh, thresh: thresh << sh, flat: 1 << sh, off: 0x80 << sh, half: 1 << (bd - 1) } + } +} + +/// Sample filtering process (7.14.6) for one line across the edge: `base` indexes q0 and +/// `step` is the distance between samples across the edge. +#[allow(clippy::too_many_arguments)] +#[inline(always)] +fn sample_filter(d: &mut [u16], base: usize, step: usize, chroma: bool, filter_size: usize, t: LfThresh) { + let at = |d: &[u16], k: isize| -> i32 { d[(base as isize + k * step as isize) as usize] as i32 }; + let (q0, q1, p0, p1) = (at(d, 0), at(d, 1), at(d, -1), at(d, -2)); + let lim = t.limit; + // filter mask (7.14.6.2) + if (p1 - p0).abs() > lim || (q1 - q0).abs() > lim || (p0 - q0).abs() * 2 + (p1 - q1).abs() / 2 > t.blimit { + return; + } + let hev = (p1 - p0).abs() > t.thresh || (q1 - q0).abs() > t.thresh; + let mut flat = false; + if filter_size > 4 { + let (q2, p2) = (at(d, 2), at(d, -3)); + if (p2 - p1).abs() > lim || (q2 - q1).abs() > lim { + return; + } + let tf = t.flat; + flat = (p1 - p0).abs() <= tf && (q1 - q0).abs() <= tf && (p2 - p0).abs() <= tf && (q2 - q0).abs() <= tf; + if !chroma { + // filterLen >= 8 + let (q3, p3) = (at(d, 3), at(d, -4)); + if (p3 - p2).abs() > lim || (q3 - q2).abs() > lim { + return; + } + flat = flat && (p3 - p0).abs() <= tf && (q3 - q0).abs() <= tf; + } + } + if !flat { + // narrow filter (7.14.6.3) + let c = |v: i32| v.clamp(-t.half, t.half - 1); + let off = t.off; + 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] = (c(qs0 - filter1) + off) as u16; + d[base - step] = (c(ps0 + filter2) + off) as u16; + if !hev { + let f = (filter1 + 1) >> 1; + d[base + step] = (c(qs1 - f) + off) as u16; + d[base - 2 * step] = (c(ps1 + f) + off) as u16; + } + return; + } + if chroma { + wide::<2, 1, 3>(d, base, step); + } else if filter_size == 8 { + wide::<3, 0, 3>(d, base, step); + } else { + let tf = t.flat; + let q0 = at(d, 0); + let p0 = at(d, -1); + let flat2 = (4..7).all(|k| (at(d, k) - q0).abs() <= tf && (at(d, -k - 1) - p0).abs() <= tf); + if flat2 { wide::<6, 1, 4>(d, base, step) } else { wide::<3, 0, 3>(d, base, step) } + } +} + +/// Wide filter process (7.14.6.4) with n = N, the 2-weight taps |j| <= N2 and log2Size = LOG2. +#[inline(always)] +fn wide(d: &mut [u16], base: usize, step: usize) { + // v[k] = sample at offset k - (N + 1) from q0, k in 0 .. 2N + 2 + let mut v = [0i32; 14]; + let first = base - (N + 1) * step; + for (k, x) in v[..2 * N + 2].iter_mut().enumerate() { + *x = d[first + k * step] as i32; + } + // e[m] = v[clamp(m - N, 0, 2N + 1)]: the clamped tap positions as one array, so + // F[i] = sum_{jj = 0}^{2N} e[i + 1 + jj] * (2 if |jj - N| <= N2 else 1), a sliding window. + let mut e = [0i32; 25]; + for (m, x) in e[..4 * N + 1].iter_mut().enumerate() { + *x = v[m.saturating_sub(N).min(2 * N + 1)]; + } + let mut f = [0i32; 12]; + let mut win: i32 = e[1..2 * N + 2].iter().sum(); + for i in 0..2 * N { + if i > 0 { + win += e[i + 1 + 2 * N] - e[i]; + } + let centre: i32 = e[i + 1 + N - N2..=i + 1 + N + N2].iter().sum(); + f[i] = (win + centre + (1 << (LOG2 - 1))) >> LOG2; + } + let out = base - N * step; + for i in 0..2 * N { + d[out + i * step] = f[i] as u16; + } +} diff --git a/crates/av1/src/restoration.rs b/crates/av1/src/restoration.rs new file mode 100644 index 0000000..693e272 --- /dev/null +++ b/crates/av1/src/restoration.rs @@ -0,0 +1,259 @@ +//! Super-resolution upscaling (7.16) and loop restoration (7.17). + +use crate::frame::{FrameBuf, Plane}; +use crate::spec_tables::*; +use crate::state::FrameState; + +/// Upscale a frame horizontally from FrameWidth to UpscaledWidth (7.16). +pub(crate) fn upscale(fs: &FrameState, input: &FrameBuf) -> FrameBuf { + let fh = &fs.fh; + let up_w = fh.upscaled_width as usize; + let mut out = FrameBuf::new(up_w, fh.frame_height as usize, fs.num_planes, fs.ssx, fs.ssy, fs.bit_depth as u8); + let max = (1i32 << fs.bit_depth) - 1; + for plane in 0..fs.num_planes { + let (sub_x, sub_y) = if plane > 0 { (fs.ssx, fs.ssy) } else { (0, 0) }; + let down_w = ((fh.frame_width as usize) + sub_x) >> sub_x; + let up_pw = (up_w + sub_x) >> sub_x; + let plane_h = ((fh.frame_height as usize) + sub_y) >> sub_y; + let sb = SUPERRES_SCALE_BITS as i64; + let step_x = (((down_w as i64) << sb) + (up_pw as i64 / 2)) / up_pw as i64; + let err = up_pw as i64 * step_x - ((down_w as i64) << sb); + let mut initial = (-(((up_pw - down_w) as i64) << (sb - 1)) + up_pw as i64 / 2) / up_pw as i64 + (1 << (SUPERRES_EXTRA_BITS - 1)) - err / 2; + initial &= SUPERRES_SCALE_MASK as i64; + let mi_w = fh.mi_cols as usize >> sub_x; + let max_x = (mi_w * 4 - 1) as i64; + let src = &input.planes[plane]; + let dst = &mut out.planes[plane]; + for y in 0..plane_h { + let row = src.row(y); + for x in 0..up_pw { + let src_x = -(1i64 << sb) + initial + x as i64 * step_x; + let src_px = src_x >> sb; + let subpel = ((src_x & SUPERRES_SCALE_MASK as i64) >> SUPERRES_EXTRA_BITS) as usize; + let mut sum = 0i32; + for k in 0..SUPERRES_FILTER_TAPS { + let sx = (src_px + k as i64 - SUPERRES_FILTER_OFFSET as i64).clamp(0, max_x) as usize; + sum += row[sx] as i32 * UPSCALE_FILTER[subpel][k] as i32; + } + dst.set(x, y, ((sum + 64) >> FILTER_BITS).clamp(0, max) as u16); + } + } + } + out +} + +struct LrCtx<'a> { + cur: &'a Plane, + cdef: &'a Plane, + plane_end_x: i32, + plane_end_y: i32, + stripe_start_y: i32, + stripe_end_y: i32, +} + +impl LrCtx<'_> { + /// Get source sample process (7.17.6). + #[inline(always)] + fn sample(&self, x: i32, y: i32) -> i32 { + let x = x.min(self.plane_end_x).max(0) as usize; + let y = y.min(self.plane_end_y).max(0); + if y < self.stripe_start_y { + let y = (self.stripe_start_y - 2).max(y) as usize; + self.cur.at(x, y) as i32 + } else if y > self.stripe_end_y { + let y = (self.stripe_end_y + 2).min(y) as usize; + self.cur.at(x, y) as i32 + } else { + self.cdef.at(x, y as usize) as i32 + } + } +} + +/// Loop restoration (7.17): returns LrFrame given UpscaledCurrFrame and UpscaledCdefFrame. +pub(crate) fn loop_restoration(fs: &FrameState, cur: &FrameBuf, cdef: &FrameBuf) -> FrameBuf { + let fh = &fs.fh; + let mut lr = cdef.clone(); + let up_w = fh.upscaled_width as i32; + let fr_h = fh.frame_height as i32; + let bd = fs.bit_depth; + for plane in 0..fs.num_planes { + let rtype_frame = fh.lr.frame_restoration_type[plane]; + if rtype_frame == RESTORE_NONE as u8 { + continue; + } + let (sub_x, sub_y) = if plane > 0 { (fs.ssx as i32, fs.ssy as i32) } else { (0, 0) }; + let unit_size = fh.lr.loop_restoration_size[plane] as i32; + let unit_rows = fs.lr_unit_rows[plane] as i32; + let unit_cols = fs.lr_unit_cols[plane] as i32; + let plane_end_x = ((up_w + sub_x) >> sub_x) - 1; + let plane_end_y = ((fr_h + sub_y) >> sub_y) - 1; + let mut row = 0; + while row * 4 < fr_h { + let luma_y = row * 4; + let stripe_num = (luma_y + 8) / 64; + let stripe_start_y = (-8 + stripe_num * 64) >> sub_y; + let stripe_end_y = stripe_start_y + (64 >> sub_y) - 1; + let ctx = LrCtx { cur: &cur.planes[plane], cdef: &cdef.planes[plane], plane_end_x, plane_end_y, stripe_start_y, stripe_end_y }; + let unit_row = (unit_rows - 1).min(((row * 4 + 8) >> sub_y) / unit_size); + let mut col = 0; + while col * 4 < up_w { + let unit_col = (unit_cols - 1).min(((col * 4) >> sub_x) / unit_size); + let x = (col * 4) >> sub_x; + let y = (row * 4) >> sub_y; + let w = (4 >> sub_x).min(plane_end_x - x + 1); + let h = (4 >> sub_y).min(plane_end_y - y + 1); + if w > 0 && h > 0 { + let ui = (unit_row * unit_cols + unit_col) as usize; + match fs.lr.lr_type[plane][ui] { + t if t == RESTORE_WIENER as u8 => wiener(fs, &ctx, &mut lr.planes[plane], plane, ui, x, y, w, h, bd), + t if t == RESTORE_SGRPROJ as u8 => self_guided(fs, &ctx, &mut lr.planes[plane], plane, ui, x, y, w, h, bd), + _ => {} + } + } + col += 1; + } + row += 1; + } + } + lr +} + +#[allow(clippy::too_many_arguments)] +fn wiener(fs: &FrameState, ctx: &LrCtx, out: &mut Plane, plane: usize, ui: usize, x: i32, y: i32, w: i32, h: i32, bd: u32) { + let round0: u32 = if bd == 12 { 5 } else { 3 }; + let round1: u32 = if bd == 12 { 9 } else { 11 }; + let coef = &fs.lr.lr_wiener[plane][ui]; + let mk = |c: &[i8; 3]| -> [i32; 7] { + let mut f = [0i32; 7]; + f[3] = 128; + for i in 0..3 { + f[i] = c[i] as i32; + f[6 - i] = c[i] as i32; + f[3] -= 2 * c[i] as i32; + } + f + }; + let vfilter = mk(&coef[0]); + let hfilter = mk(&coef[1]); + let fb = FILTER_BITS as u32; + let offset = 1i32 << (bd + fb - round0 - 1); + let limit = (1i32 << (bd + 1 + fb - round0)) - 1; + let mut inter = [[0i32; 4]; 10]; + for r in 0..(h + 6) as usize { + for c in 0..w as usize { + let mut s = 0; + for t in 0..7 { + s += hfilter[t] * ctx.sample(x + c as i32 + t as i32 - 3, y + r as i32 - 3); + } + let v = (s + (1 << (round0 - 1))) >> round0; + inter[r][c] = v.clamp(-offset, limit - offset); + } + } + let max = (1i32 << bd) - 1; + for r in 0..h as usize { + for c in 0..w as usize { + let mut s = 0; + for t in 0..7 { + s += vfilter[t] * inter[r + t][c]; + } + let v = (s + (1 << (round1 - 1))) >> round1; + out.set(x as usize + c, y as usize + r, v.clamp(0, max) as u16); + } + } +} + +#[allow(clippy::too_many_arguments)] +fn self_guided(fs: &FrameState, ctx: &LrCtx, out: &mut Plane, plane: usize, ui: usize, x: i32, y: i32, w: i32, h: i32, bd: u32) { + let set = fs.lr.lr_sgr_set[plane][ui] as usize; + let mut flt0 = [[0i32; 4]; 4]; + let mut flt1 = [[0i32; 4]; 4]; + box_filter(ctx, x, y, w, h, set, 0, bd, &mut flt0); + box_filter(ctx, x, y, w, h, set, 1, bd, &mut flt1); + let xqd = fs.lr.lr_sgr_xqd[plane][ui]; + let w0 = xqd[0] as i32; + let w1 = xqd[1] as i32; + let w2 = (1 << SGRPROJ_PRJ_BITS) - w0 - w1; + let r0 = SGR_PARAMS[set][0]; + let r1 = SGR_PARAMS[set][2]; + let max = (1i32 << bd) - 1; + let sh = (SGRPROJ_RST_BITS + SGRPROJ_PRJ_BITS) as u32; + for i in 0..h as usize { + for j in 0..w as usize { + let u = (ctx.cdef.at(x as usize + j, y as usize + i) as i32) << SGRPROJ_RST_BITS; + let mut v = w1 * u; + v += if r0 != 0 { w0 * flt0[i][j] } else { w0 * u }; + v += if r1 != 0 { w2 * flt1[i][j] } else { w2 * u }; + let s = (v + (1 << (sh - 1))) >> sh; + out.set(x as usize + j, y as usize + i, s.clamp(0, max) as u16); + } + } +} + +#[inline(always)] +fn round2_64(x: i64, n: u32) -> i64 { + if n == 0 { x } else { (x + (1i64 << (n - 1))) >> n } +} + +#[allow(clippy::too_many_arguments)] +fn box_filter(ctx: &LrCtx, x: i32, y: i32, w: i32, h: i32, set: usize, pass: usize, bd: u32, f: &mut [[i32; 4]; 4]) { + let r = SGR_PARAMS[set][pass * 2] as i32; + if r == 0 { + return; + } + let eps = SGR_PARAMS[set][pass * 2 + 1] as i64; + let n = ((2 * r + 1) * (2 * r + 1)) as i64; + let n2e = n * n * eps; + let s = ((1i64 << SGRPROJ_MTABLE_BITS) + n2e / 2) / n2e; + let one_over_n = ((1i64 << SGRPROJ_RECIP_BITS) + n / 2) / n; + let mut a_arr = [[0i64; 6]; 6]; + let mut b_arr = [[0i64; 6]; 6]; + for i in -1..h + 1 { + for j in -1..w + 1 { + let mut a = 0i64; + let mut b = 0i64; + for dy in -r..=r { + for dx in -r..=r { + let c = ctx.sample(x + j + dx, y + i + dy) as i64; + a += c * c; + b += c; + } + } + let a = round2_64(a, 2 * (bd - 8)); + let d = round2_64(b, bd - 8); + let p = (a * n - d * d).max(0); + let z = round2_64(p * s, SGRPROJ_MTABLE_BITS as u32); + let a2 = if z >= 255 { + 256 + } else if z == 0 { + 1 + } else { + ((z << SGRPROJ_SGR_BITS) + z / 2) / (z + 1) + }; + let b2 = ((1i64 << SGRPROJ_SGR_BITS) - a2) * b * one_over_n; + a_arr[(i + 1) as usize][(j + 1) as usize] = a2; + b_arr[(i + 1) as usize][(j + 1) as usize] = round2_64(b2, SGRPROJ_RECIP_BITS as u32); + } + } + for i in 0..h { + let shift = if pass == 0 && (i & 1) == 1 { 4 } else { 5 }; + for j in 0..w { + let mut a = 0i64; + let mut b = 0i64; + for dy in -1..=1i32 { + for dx in -1..=1i32 { + let weight = if pass == 0 { + if ((i + dy) & 1) != 0 { if dx == 0 { 6 } else { 5 } } else { 0 } + } else if dx == 0 || dy == 0 { + 4 + } else { + 3 + }; + a += weight * a_arr[(i + dy + 1) as usize][(j + dx + 1) as usize]; + b += weight * b_arr[(i + dy + 1) as usize][(j + dx + 1) as usize]; + } + } + let v = a * ctx.cdef.at((x + j) as usize, (y + i) as usize) as i64 + b; + f[i as usize][j as usize] = round2_64(v, (SGRPROJ_SGR_BITS + shift - SGRPROJ_RST_BITS) as u32) as i32; + } + } +} diff --git a/crates/av1/src/spec_tables.rs b/crates/av1/src/spec_tables.rs new file mode 100644 index 0000000..8589712 --- /dev/null +++ b/crates/av1/src/spec_tables.rs @@ -0,0 +1,9195 @@ +// Generated by tools/extract_tables.py from the AV1 Bitstream & Decoding Process Specification +// v1.0.0 with Errata 1 (AOMediaCodec/av1-spec 5e04f3f). Do not edit by hand. +#![allow(dead_code, clippy::unreadable_literal, clippy::excessive_precision)] + +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 CDEF_DIRECTIONS: [[[i8; 2]; 2]; 8] = [ + [[-1, 1], [-2, 2]], + [[0, 1], [-1, 2]], + [[0, 1], [0, 2]], + [[0, 1], [1, 2]], + [[1, 1], [2, 2]], + [[1, 0], [2, 1]], + [[1, 0], [2, 0]], + [[1, 0], [2, -1]], +]; + +pub static CDEF_PRI_TAPS: [[u8; 2]; 2] = [[4, 2], [3, 3]]; + +pub static CDEF_SEC_TAPS: [[u8; 2]; 2] = [[2, 1], [2, 1]]; + +pub static CDEF_UV_DIR: [[[u8; 8]; 2]; 2] = + [[[0, 1, 2, 3, 4, 5, 6, 7], [1, 2, 2, 2, 3, 4, 6, 0]], [[7, 0, 2, 4, 5, 6, 6, 6], [0, 1, 2, 3, 4, 5, 6, 7]]]; + +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 COMPOUND_MODE_CTX_MAP: [[u8; 5]; 3] = [[0, 1, 1, 1, 1], [1, 2, 3, 4, 4], [4, 4, 5, 6, 7]]; + +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_CFL_ALPHA_CDF: [[u16; 17]; 6] = [ + [7637, 20719, 31401, 32481, 32657, 32688, 32692, 32696, 32700, 32704, 32708, 32712, 32716, 32720, 32724, 32768, 0], + [14365, 23603, 28135, 31168, 32167, 32395, 32487, 32573, 32620, 32647, 32668, 32672, 32676, 32680, 32684, 32768, 0], + [11532, 22380, 28445, 31360, 32349, 32523, 32584, 32649, 32673, 32677, 32681, 32685, 32689, 32693, 32697, 32768, 0], + [26990, 31402, 32282, 32571, 32692, 32696, 32700, 32704, 32708, 32712, 32716, 32720, 32724, 32728, 32732, 32768, 0], + [17248, 26058, 28904, 30608, 31305, 31877, 32126, 32321, 32394, 32464, 32516, 32560, 32576, 32593, 32622, 32768, 0], + [14738, 21678, 25779, 27901, 29024, 30302, 30980, 31843, 32144, 32413, 32520, 32594, 32622, 32656, 32660, 32768, 0], +]; + +pub static DEFAULT_CFL_SIGN_CDF: [u16; 9] = [1418, 2123, 13340, 18405, 26972, 28343, 32294, 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_COMP_BWD_REF_CDF: [[[u16; 3]; 2]; 3] = + [[[2235, 32768, 0], [1423, 32768, 0]], [[17182, 32768, 0], [15175, 32768, 0]], [[30606, 32768, 0], [30489, 32768, 0]]]; + +pub static DEFAULT_COMP_GROUP_IDX_CDF: [[u16; 3]; 6] = + [[26607, 32768, 0], [22891, 32768, 0], [18840, 32768, 0], [24594, 32768, 0], [19934, 32768, 0], [22674, 32768, 0]]; + +pub static DEFAULT_COMP_MODE_CDF: [[u16; 3]; 5] = [[26828, 32768, 0], [24035, 32768, 0], [12031, 32768, 0], [10640, 32768, 0], [2901, 32768, 0]]; + +pub static DEFAULT_COMP_REF_CDF: [[[u16; 3]; 3]; 3] = [ + [[4946, 32768, 0], [9468, 32768, 0], [1503, 32768, 0]], + [[19891, 32768, 0], [22441, 32768, 0], [15160, 32768, 0]], + [[30731, 32768, 0], [31059, 32768, 0], [27544, 32768, 0]], +]; + +pub static DEFAULT_COMP_REF_TYPE_CDF: [[u16; 3]; 5] = [[1198, 32768, 0], [2070, 32768, 0], [9166, 32768, 0], [7499, 32768, 0], [22475, 32768, 0]]; + +pub static DEFAULT_COMPOUND_IDX_CDF: [[u16; 3]; 6] = + [[18244, 32768, 0], [12865, 32768, 0], [7053, 32768, 0], [13259, 32768, 0], [9334, 32768, 0], [4644, 32768, 0]]; + +pub static DEFAULT_COMPOUND_MODE_CDF: [[u16; 9]; 8] = [ + [7760, 13823, 15808, 17641, 19156, 20666, 26891, 32768, 0], + [10730, 19452, 21145, 22749, 24039, 25131, 28724, 32768, 0], + [10664, 20221, 21588, 22906, 24295, 25387, 28436, 32768, 0], + [13298, 16984, 20471, 24182, 25067, 25736, 26422, 32768, 0], + [18904, 23325, 25242, 27432, 27898, 28258, 30758, 32768, 0], + [10725, 17454, 20124, 22820, 24195, 25168, 26046, 32768, 0], + [17125, 24273, 25814, 27492, 28214, 28704, 30592, 32768, 0], + [13046, 23214, 24505, 25942, 27435, 28442, 29330, 32768, 0], +]; + +pub static DEFAULT_COMPOUND_TYPE_CDF: [[u16; 3]; 22] = [ + [16384, 32768, 0], + [16384, 32768, 0], + [16384, 32768, 0], + [23431, 32768, 0], + [13171, 32768, 0], + [11470, 32768, 0], + [9770, 32768, 0], + [9100, 32768, 0], + [8233, 32768, 0], + [6172, 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], + [11820, 32768, 0], + [7701, 32768, 0], + [16384, 32768, 0], + [16384, 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_DELTA_LF_CDF: [u16; 5] = [28160, 32120, 32677, 32768, 0]; + +pub static DEFAULT_DELTA_Q_CDF: [u16; 5] = [28160, 32120, 32677, 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_FILTER_INTRA_CDF: [[u16; 3]; 22] = [ + [4621, 32768, 0], + [6743, 32768, 0], + [5893, 32768, 0], + [7866, 32768, 0], + [12551, 32768, 0], + [9394, 32768, 0], + [12408, 32768, 0], + [14301, 32768, 0], + [12756, 32768, 0], + [22343, 32768, 0], + [16384, 32768, 0], + [16384, 32768, 0], + [16384, 32768, 0], + [16384, 32768, 0], + [16384, 32768, 0], + [16384, 32768, 0], + [12770, 32768, 0], + [10368, 32768, 0], + [20229, 32768, 0], + [18101, 32768, 0], + [16384, 32768, 0], + [16384, 32768, 0], +]; + +pub static DEFAULT_FILTER_INTRA_MODE_CDF: [u16; 6] = [8949, 12776, 17211, 29558, 32768, 0]; + +pub static DEFAULT_INTER_INTRA_CDF: [[u16; 3]; 3] = [[26887, 32768, 0], [27597, 32768, 0], [30237, 32768, 0]]; + +pub static DEFAULT_INTER_INTRA_MODE_CDF: [[u16; 5]; 3] = + [[1875, 11082, 27332, 32768, 0], [2473, 9996, 26388, 32768, 0], [4238, 11537, 25926, 32768, 0]]; + +pub static DEFAULT_INTER_TX_TYPE_SET1_CDF: [[u16; 17]; 2] = [ + [4458, 5560, 7695, 9709, 13330, 14789, 17537, 20266, 21504, 22848, 23934, 25474, 27727, 28915, 30631, 32768, 0], + [1645, 2573, 4778, 5711, 7807, 8622, 10522, 15357, 17674, 20408, 22517, 25010, 27116, 28856, 30749, 32768, 0], +]; + +pub static DEFAULT_INTER_TX_TYPE_SET2_CDF: [u16; 13] = [770, 2421, 5225, 12907, 15819, 18927, 21561, 24089, 26595, 28526, 30529, 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_INTERP_FILTER_CDF: [[u16; 4]; 16] = [ + [31935, 32720, 32768, 0], + [5568, 32719, 32768, 0], + [422, 2938, 32768, 0], + [28244, 32608, 32768, 0], + [31206, 31953, 32768, 0], + [4862, 32121, 32768, 0], + [770, 1152, 32768, 0], + [20889, 25637, 32768, 0], + [31910, 32724, 32768, 0], + [4120, 32712, 32768, 0], + [305, 2247, 32768, 0], + [27403, 32636, 32768, 0], + [31022, 32009, 32768, 0], + [2963, 32093, 32768, 0], + [601, 943, 32768, 0], + [14969, 21398, 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_SET1_CDF: [[[u16; 8]; 13]; 2] = [ + [ + [1535, 8035, 9461, 12751, 23467, 27825, 32768, 0], + [564, 3335, 9709, 10870, 18143, 28094, 32768, 0], + [672, 3247, 3676, 11982, 19415, 23127, 32768, 0], + [5279, 13885, 15487, 18044, 23527, 30252, 32768, 0], + [4423, 6074, 7985, 10416, 25693, 29298, 32768, 0], + [1486, 4241, 9460, 10662, 16456, 27694, 32768, 0], + [439, 2838, 3522, 6737, 18058, 23754, 32768, 0], + [1190, 4233, 4855, 11670, 20281, 24377, 32768, 0], + [1045, 4312, 8647, 10159, 18644, 29335, 32768, 0], + [202, 3734, 4747, 7298, 17127, 24016, 32768, 0], + [447, 4312, 6819, 8884, 16010, 23858, 32768, 0], + [277, 4369, 5255, 8905, 16465, 22271, 32768, 0], + [3409, 5436, 10599, 15599, 19687, 24040, 32768, 0], + ], + [ + [1870, 13742, 14530, 16498, 23770, 27698, 32768, 0], + [326, 8796, 14632, 15079, 19272, 27486, 32768, 0], + [484, 7576, 7712, 14443, 19159, 22591, 32768, 0], + [1126, 15340, 15895, 17023, 20896, 30279, 32768, 0], + [655, 4854, 5249, 5913, 22099, 27138, 32768, 0], + [1299, 6458, 8885, 9290, 14851, 25497, 32768, 0], + [311, 5295, 5552, 6885, 16107, 22672, 32768, 0], + [883, 8059, 8270, 11258, 17289, 21549, 32768, 0], + [741, 7580, 9318, 10345, 16688, 29046, 32768, 0], + [110, 7406, 7915, 9195, 16041, 23329, 32768, 0], + [363, 7974, 9357, 10673, 15629, 24474, 32768, 0], + [153, 7647, 8112, 9936, 15307, 19996, 32768, 0], + [3511, 6332, 11165, 15335, 19323, 23594, 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_INTRABC_CDF: [u16; 3] = [30531, 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_MOTION_MODE_CDF: [[u16; 4]; 22] = [ + [10923, 21845, 32768, 0], + [10923, 21845, 32768, 0], + [10923, 21845, 32768, 0], + [7651, 24760, 32768, 0], + [4738, 24765, 32768, 0], + [5391, 25528, 32768, 0], + [19419, 26810, 32768, 0], + [5123, 23606, 32768, 0], + [11606, 24308, 32768, 0], + [26260, 29116, 32768, 0], + [20360, 28062, 32768, 0], + [21679, 26830, 32768, 0], + [29516, 30701, 32768, 0], + [28898, 30397, 32768, 0], + [30878, 31335, 32768, 0], + [32507, 32558, 32768, 0], + [10923, 21845, 32768, 0], + [10923, 21845, 32768, 0], + [28799, 31390, 32768, 0], + [26431, 30774, 32768, 0], + [28973, 31594, 32768, 0], + [29742, 31203, 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_PALETTE_SIZE_2_UV_COLOR_CDF: [[u16; 3]; 5] = + [[29089, 32768, 0], [16384, 32768, 0], [8713, 32768, 0], [29257, 32768, 0], [31610, 32768, 0]]; + +pub static DEFAULT_PALETTE_SIZE_2_Y_COLOR_CDF: [[u16; 3]; 5] = + [[28710, 32768, 0], [16384, 32768, 0], [10553, 32768, 0], [27036, 32768, 0], [31603, 32768, 0]]; + +pub static DEFAULT_PALETTE_SIZE_3_UV_COLOR_CDF: [[u16; 4]; 5] = + [[25257, 29145, 32768, 0], [12287, 27293, 32768, 0], [7033, 27960, 32768, 0], [20145, 25405, 32768, 0], [30608, 31639, 32768, 0]]; + +pub static DEFAULT_PALETTE_SIZE_3_Y_COLOR_CDF: [[u16; 4]; 5] = + [[27877, 30490, 32768, 0], [11532, 25697, 32768, 0], [6544, 30234, 32768, 0], [23018, 28072, 32768, 0], [31915, 32385, 32768, 0]]; + +pub static DEFAULT_PALETTE_SIZE_4_UV_COLOR_CDF: [[u16; 5]; 5] = [ + [24210, 27175, 29903, 32768, 0], + [9888, 22386, 27214, 32768, 0], + [5901, 26053, 29293, 32768, 0], + [18318, 22152, 28333, 32768, 0], + [30459, 31136, 31926, 32768, 0], +]; + +pub static DEFAULT_PALETTE_SIZE_4_Y_COLOR_CDF: [[u16; 5]; 5] = [ + [25572, 28046, 30045, 32768, 0], + [9478, 21590, 27256, 32768, 0], + [7248, 26837, 29824, 32768, 0], + [19167, 24486, 28349, 32768, 0], + [31400, 31825, 32250, 32768, 0], +]; + +pub static DEFAULT_PALETTE_SIZE_5_UV_COLOR_CDF: [[u16; 6]; 5] = [ + [22980, 25479, 27781, 29986, 32768, 0], + [8413, 21408, 24859, 28874, 32768, 0], + [2257, 29449, 30594, 31598, 32768, 0], + [19189, 21202, 25915, 28620, 32768, 0], + [31844, 32044, 32281, 32518, 32768, 0], +]; + +pub static DEFAULT_PALETTE_SIZE_5_Y_COLOR_CDF: [[u16; 6]; 5] = [ + [24779, 26955, 28576, 30282, 32768, 0], + [8669, 20364, 24073, 28093, 32768, 0], + [4255, 27565, 29377, 31067, 32768, 0], + [19864, 23674, 26716, 29530, 32768, 0], + [31646, 31893, 32147, 32426, 32768, 0], +]; + +pub static DEFAULT_PALETTE_SIZE_6_UV_COLOR_CDF: [[u16; 7]; 5] = [ + [22217, 24567, 26637, 28683, 30548, 32768, 0], + [7307, 16406, 19636, 24632, 28424, 32768, 0], + [4441, 25064, 26879, 28942, 30919, 32768, 0], + [17210, 20528, 23319, 26750, 29582, 32768, 0], + [30674, 30953, 31396, 31735, 32207, 32768, 0], +]; + +pub static DEFAULT_PALETTE_SIZE_6_Y_COLOR_CDF: [[u16; 7]; 5] = [ + [23132, 25407, 26970, 28435, 30073, 32768, 0], + [7443, 17242, 20717, 24762, 27982, 32768, 0], + [6300, 24862, 26944, 28784, 30671, 32768, 0], + [18916, 22895, 25267, 27435, 29652, 32768, 0], + [31270, 31550, 31808, 32059, 32353, 32768, 0], +]; + +pub static DEFAULT_PALETTE_SIZE_7_UV_COLOR_CDF: [[u16; 8]; 5] = [ + [21239, 23168, 25044, 26962, 28705, 30506, 32768, 0], + [6545, 15012, 18004, 21817, 25503, 28701, 32768, 0], + [3448, 26295, 27437, 28704, 30126, 31442, 32768, 0], + [15889, 18323, 21704, 24698, 26976, 29690, 32768, 0], + [30988, 31204, 31479, 31734, 31983, 32325, 32768, 0], +]; + +pub static DEFAULT_PALETTE_SIZE_7_Y_COLOR_CDF: [[u16; 8]; 5] = [ + [23105, 25199, 26464, 27684, 28931, 30318, 32768, 0], + [6950, 15447, 18952, 22681, 25567, 28563, 32768, 0], + [7560, 23474, 25490, 27203, 28921, 30708, 32768, 0], + [18544, 22373, 24457, 26195, 28119, 30045, 32768, 0], + [31198, 31451, 31670, 31882, 32123, 32391, 32768, 0], +]; + +pub static DEFAULT_PALETTE_SIZE_8_UV_COLOR_CDF: [[u16; 9]; 5] = [ + [21442, 23288, 24758, 26246, 27649, 28980, 30563, 32768, 0], + [5863, 14933, 17552, 20668, 23683, 26411, 29273, 32768, 0], + [3415, 25810, 26877, 27990, 29223, 30394, 31618, 32768, 0], + [17965, 20084, 22232, 23974, 26274, 28402, 30390, 32768, 0], + [31190, 31329, 31516, 31679, 31825, 32026, 32322, 32768, 0], +]; + +pub static DEFAULT_PALETTE_SIZE_8_Y_COLOR_CDF: [[u16; 9]; 5] = [ + [21689, 23883, 25163, 26352, 27506, 28827, 30195, 32768, 0], + [6892, 15385, 17840, 21606, 24287, 26753, 29204, 32768, 0], + [5651, 23182, 25042, 26518, 27982, 29392, 30900, 32768, 0], + [19349, 22578, 24418, 25994, 27524, 29031, 30448, 32768, 0], + [31028, 31270, 31504, 31705, 31927, 32153, 32392, 32768, 0], +]; + +pub static DEFAULT_PALETTE_UV_MODE_CDF: [[u16; 3]; 2] = [[32461, 32768, 0], [21488, 32768, 0]]; + +pub static DEFAULT_PALETTE_UV_SIZE_CDF: [[u16; 8]; 7] = [ + [8713, 19979, 27128, 29609, 31331, 32272, 32768, 0], + [5839, 15573, 23581, 26947, 29848, 31700, 32768, 0], + [4426, 11260, 17999, 21483, 25863, 29430, 32768, 0], + [3228, 9464, 14993, 18089, 22523, 27420, 32768, 0], + [3768, 8886, 13091, 17852, 22495, 27207, 32768, 0], + [2464, 8451, 12861, 21632, 25525, 28555, 32768, 0], + [1269, 5435, 10433, 18963, 21700, 25865, 32768, 0], +]; + +pub static DEFAULT_PALETTE_Y_MODE_CDF: [[[u16; 3]; 3]; 7] = [ + [[31676, 32768, 0], [3419, 32768, 0], [1261, 32768, 0]], + [[31912, 32768, 0], [2859, 32768, 0], [980, 32768, 0]], + [[31823, 32768, 0], [3400, 32768, 0], [781, 32768, 0]], + [[32030, 32768, 0], [3561, 32768, 0], [904, 32768, 0]], + [[32309, 32768, 0], [7337, 32768, 0], [1462, 32768, 0]], + [[32265, 32768, 0], [4015, 32768, 0], [1521, 32768, 0]], + [[32450, 32768, 0], [7946, 32768, 0], [129, 32768, 0]], +]; + +pub static DEFAULT_PALETTE_Y_SIZE_CDF: [[u16; 8]; 7] = [ + [7952, 13000, 18149, 21478, 25527, 29241, 32768, 0], + [7139, 11421, 16195, 19544, 23666, 28073, 32768, 0], + [7788, 12741, 17325, 20500, 24315, 28530, 32768, 0], + [8271, 14064, 18246, 21564, 25071, 28533, 32768, 0], + [12725, 19180, 21863, 24839, 27535, 30120, 32768, 0], + [9711, 14888, 16923, 21052, 25661, 27875, 32768, 0], + [14940, 20797, 21678, 24186, 27033, 28999, 32768, 0], +]; + +pub static DEFAULT_PARTITION_W128_CDF: [[u16; 9]; 4] = [ + [27899, 28219, 28529, 32484, 32539, 32619, 32639, 32768, 0], + [6607, 6990, 8268, 32060, 32219, 32338, 32371, 32768, 0], + [5429, 6676, 7122, 32027, 32227, 32531, 32582, 32768, 0], + [711, 966, 1172, 32448, 32538, 32617, 32664, 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_RESTORATION_TYPE_CDF: [u16; 4] = [9413, 22581, 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_16X32: [u16; 512] = [ + 0, 1, 16, 2, 17, 32, 3, 18, 33, 48, 4, 19, 34, 49, 64, 5, 20, 35, 50, 65, 80, 6, 21, 36, 51, 66, 81, 96, 7, 22, 37, 52, 67, 82, 97, 112, 8, 23, + 38, 53, 68, 83, 98, 113, 128, 9, 24, 39, 54, 69, 84, 99, 114, 129, 144, 10, 25, 40, 55, 70, 85, 100, 115, 130, 145, 160, 11, 26, 41, 56, 71, 86, + 101, 116, 131, 146, 161, 176, 12, 27, 42, 57, 72, 87, 102, 117, 132, 147, 162, 177, 192, 13, 28, 43, 58, 73, 88, 103, 118, 133, 148, 163, 178, + 193, 208, 14, 29, 44, 59, 74, 89, 104, 119, 134, 149, 164, 179, 194, 209, 224, 15, 30, 45, 60, 75, 90, 105, 120, 135, 150, 165, 180, 195, 210, + 225, 240, 31, 46, 61, 76, 91, 106, 121, 136, 151, 166, 181, 196, 211, 226, 241, 256, 47, 62, 77, 92, 107, 122, 137, 152, 167, 182, 197, 212, 227, + 242, 257, 272, 63, 78, 93, 108, 123, 138, 153, 168, 183, 198, 213, 228, 243, 258, 273, 288, 79, 94, 109, 124, 139, 154, 169, 184, 199, 214, 229, + 244, 259, 274, 289, 304, 95, 110, 125, 140, 155, 170, 185, 200, 215, 230, 245, 260, 275, 290, 305, 320, 111, 126, 141, 156, 171, 186, 201, 216, + 231, 246, 261, 276, 291, 306, 321, 336, 127, 142, 157, 172, 187, 202, 217, 232, 247, 262, 277, 292, 307, 322, 337, 352, 143, 158, 173, 188, 203, + 218, 233, 248, 263, 278, 293, 308, 323, 338, 353, 368, 159, 174, 189, 204, 219, 234, 249, 264, 279, 294, 309, 324, 339, 354, 369, 384, 175, 190, + 205, 220, 235, 250, 265, 280, 295, 310, 325, 340, 355, 370, 385, 400, 191, 206, 221, 236, 251, 266, 281, 296, 311, 326, 341, 356, 371, 386, 401, + 416, 207, 222, 237, 252, 267, 282, 297, 312, 327, 342, 357, 372, 387, 402, 417, 432, 223, 238, 253, 268, 283, 298, 313, 328, 343, 358, 373, 388, + 403, 418, 433, 448, 239, 254, 269, 284, 299, 314, 329, 344, 359, 374, 389, 404, 419, 434, 449, 464, 255, 270, 285, 300, 315, 330, 345, 360, 375, + 390, 405, 420, 435, 450, 465, 480, 271, 286, 301, 316, 331, 346, 361, 376, 391, 406, 421, 436, 451, 466, 481, 496, 287, 302, 317, 332, 347, 362, + 377, 392, 407, 422, 437, 452, 467, 482, 497, 303, 318, 333, 348, 363, 378, 393, 408, 423, 438, 453, 468, 483, 498, 319, 334, 349, 364, 379, 394, + 409, 424, 439, 454, 469, 484, 499, 335, 350, 365, 380, 395, 410, 425, 440, 455, 470, 485, 500, 351, 366, 381, 396, 411, 426, 441, 456, 471, 486, + 501, 367, 382, 397, 412, 427, 442, 457, 472, 487, 502, 383, 398, 413, 428, 443, 458, 473, 488, 503, 399, 414, 429, 444, 459, 474, 489, 504, 415, + 430, 445, 460, 475, 490, 505, 431, 446, 461, 476, 491, 506, 447, 462, 477, 492, 507, 463, 478, 493, 508, 479, 494, 509, 495, 510, 511, +]; + +pub static DEFAULT_SCAN_16X4: [u16; 64] = [ + 0, 16, 1, 32, 17, 2, 48, 33, 18, 3, 49, 34, 19, 4, 50, 35, 20, 5, 51, 36, 21, 6, 52, 37, 22, 7, 53, 38, 23, 8, 54, 39, 24, 9, 55, 40, 25, 10, 56, + 41, 26, 11, 57, 42, 27, 12, 58, 43, 28, 13, 59, 44, 29, 14, 60, 45, 30, 15, 61, 46, 31, 62, 47, 63, +]; + +pub static DEFAULT_SCAN_16X8: [u16; 128] = [ + 0, 16, 1, 32, 17, 2, 48, 33, 18, 3, 64, 49, 34, 19, 4, 80, 65, 50, 35, 20, 5, 96, 81, 66, 51, 36, 21, 6, 112, 97, 82, 67, 52, 37, 22, 7, 113, 98, + 83, 68, 53, 38, 23, 8, 114, 99, 84, 69, 54, 39, 24, 9, 115, 100, 85, 70, 55, 40, 25, 10, 116, 101, 86, 71, 56, 41, 26, 11, 117, 102, 87, 72, 57, + 42, 27, 12, 118, 103, 88, 73, 58, 43, 28, 13, 119, 104, 89, 74, 59, 44, 29, 14, 120, 105, 90, 75, 60, 45, 30, 15, 121, 106, 91, 76, 61, 46, 31, + 122, 107, 92, 77, 62, 47, 123, 108, 93, 78, 63, 124, 109, 94, 79, 125, 110, 95, 126, 111, 127, +]; + +pub static DEFAULT_SCAN_32X16: [u16; 512] = [ + 0, 32, 1, 64, 33, 2, 96, 65, 34, 3, 128, 97, 66, 35, 4, 160, 129, 98, 67, 36, 5, 192, 161, 130, 99, 68, 37, 6, 224, 193, 162, 131, 100, 69, 38, + 7, 256, 225, 194, 163, 132, 101, 70, 39, 8, 288, 257, 226, 195, 164, 133, 102, 71, 40, 9, 320, 289, 258, 227, 196, 165, 134, 103, 72, 41, 10, + 352, 321, 290, 259, 228, 197, 166, 135, 104, 73, 42, 11, 384, 353, 322, 291, 260, 229, 198, 167, 136, 105, 74, 43, 12, 416, 385, 354, 323, 292, + 261, 230, 199, 168, 137, 106, 75, 44, 13, 448, 417, 386, 355, 324, 293, 262, 231, 200, 169, 138, 107, 76, 45, 14, 480, 449, 418, 387, 356, 325, + 294, 263, 232, 201, 170, 139, 108, 77, 46, 15, 481, 450, 419, 388, 357, 326, 295, 264, 233, 202, 171, 140, 109, 78, 47, 16, 482, 451, 420, 389, + 358, 327, 296, 265, 234, 203, 172, 141, 110, 79, 48, 17, 483, 452, 421, 390, 359, 328, 297, 266, 235, 204, 173, 142, 111, 80, 49, 18, 484, 453, + 422, 391, 360, 329, 298, 267, 236, 205, 174, 143, 112, 81, 50, 19, 485, 454, 423, 392, 361, 330, 299, 268, 237, 206, 175, 144, 113, 82, 51, 20, + 486, 455, 424, 393, 362, 331, 300, 269, 238, 207, 176, 145, 114, 83, 52, 21, 487, 456, 425, 394, 363, 332, 301, 270, 239, 208, 177, 146, 115, 84, + 53, 22, 488, 457, 426, 395, 364, 333, 302, 271, 240, 209, 178, 147, 116, 85, 54, 23, 489, 458, 427, 396, 365, 334, 303, 272, 241, 210, 179, 148, + 117, 86, 55, 24, 490, 459, 428, 397, 366, 335, 304, 273, 242, 211, 180, 149, 118, 87, 56, 25, 491, 460, 429, 398, 367, 336, 305, 274, 243, 212, + 181, 150, 119, 88, 57, 26, 492, 461, 430, 399, 368, 337, 306, 275, 244, 213, 182, 151, 120, 89, 58, 27, 493, 462, 431, 400, 369, 338, 307, 276, + 245, 214, 183, 152, 121, 90, 59, 28, 494, 463, 432, 401, 370, 339, 308, 277, 246, 215, 184, 153, 122, 91, 60, 29, 495, 464, 433, 402, 371, 340, + 309, 278, 247, 216, 185, 154, 123, 92, 61, 30, 496, 465, 434, 403, 372, 341, 310, 279, 248, 217, 186, 155, 124, 93, 62, 31, 497, 466, 435, 404, + 373, 342, 311, 280, 249, 218, 187, 156, 125, 94, 63, 498, 467, 436, 405, 374, 343, 312, 281, 250, 219, 188, 157, 126, 95, 499, 468, 437, 406, + 375, 344, 313, 282, 251, 220, 189, 158, 127, 500, 469, 438, 407, 376, 345, 314, 283, 252, 221, 190, 159, 501, 470, 439, 408, 377, 346, 315, 284, + 253, 222, 191, 502, 471, 440, 409, 378, 347, 316, 285, 254, 223, 503, 472, 441, 410, 379, 348, 317, 286, 255, 504, 473, 442, 411, 380, 349, 318, + 287, 505, 474, 443, 412, 381, 350, 319, 506, 475, 444, 413, 382, 351, 507, 476, 445, 414, 383, 508, 477, 446, 415, 509, 478, 447, 510, 479, 511, +]; + +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_32X8: [u16; 256] = [ + 0, 32, 1, 64, 33, 2, 96, 65, 34, 3, 128, 97, 66, 35, 4, 160, 129, 98, 67, 36, 5, 192, 161, 130, 99, 68, 37, 6, 224, 193, 162, 131, 100, 69, 38, + 7, 225, 194, 163, 132, 101, 70, 39, 8, 226, 195, 164, 133, 102, 71, 40, 9, 227, 196, 165, 134, 103, 72, 41, 10, 228, 197, 166, 135, 104, 73, 42, + 11, 229, 198, 167, 136, 105, 74, 43, 12, 230, 199, 168, 137, 106, 75, 44, 13, 231, 200, 169, 138, 107, 76, 45, 14, 232, 201, 170, 139, 108, 77, + 46, 15, 233, 202, 171, 140, 109, 78, 47, 16, 234, 203, 172, 141, 110, 79, 48, 17, 235, 204, 173, 142, 111, 80, 49, 18, 236, 205, 174, 143, 112, + 81, 50, 19, 237, 206, 175, 144, 113, 82, 51, 20, 238, 207, 176, 145, 114, 83, 52, 21, 239, 208, 177, 146, 115, 84, 53, 22, 240, 209, 178, 147, + 116, 85, 54, 23, 241, 210, 179, 148, 117, 86, 55, 24, 242, 211, 180, 149, 118, 87, 56, 25, 243, 212, 181, 150, 119, 88, 57, 26, 244, 213, 182, + 151, 120, 89, 58, 27, 245, 214, 183, 152, 121, 90, 59, 28, 246, 215, 184, 153, 122, 91, 60, 29, 247, 216, 185, 154, 123, 92, 61, 30, 248, 217, + 186, 155, 124, 93, 62, 31, 249, 218, 187, 156, 125, 94, 63, 250, 219, 188, 157, 126, 95, 251, 220, 189, 158, 127, 252, 221, 190, 159, 253, 222, + 191, 254, 223, 255, +]; + +pub static DEFAULT_SCAN_4X16: [u16; 64] = [ + 0, 1, 4, 2, 5, 8, 3, 6, 9, 12, 7, 10, 13, 16, 11, 14, 17, 20, 15, 18, 21, 24, 19, 22, 25, 28, 23, 26, 29, 32, 27, 30, 33, 36, 31, 34, 37, 40, 35, + 38, 41, 44, 39, 42, 45, 48, 43, 46, 49, 52, 47, 50, 53, 56, 51, 54, 57, 60, 55, 58, 61, 59, 62, 63, +]; + +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_4X8: [u16; 32] = + [0, 1, 4, 2, 5, 8, 3, 6, 9, 12, 7, 10, 13, 16, 11, 14, 17, 20, 15, 18, 21, 24, 19, 22, 25, 28, 23, 26, 29, 27, 30, 31]; + +pub static DEFAULT_SCAN_8X16: [u16; 128] = [ + 0, 1, 8, 2, 9, 16, 3, 10, 17, 24, 4, 11, 18, 25, 32, 5, 12, 19, 26, 33, 40, 6, 13, 20, 27, 34, 41, 48, 7, 14, 21, 28, 35, 42, 49, 56, 15, 22, 29, + 36, 43, 50, 57, 64, 23, 30, 37, 44, 51, 58, 65, 72, 31, 38, 45, 52, 59, 66, 73, 80, 39, 46, 53, 60, 67, 74, 81, 88, 47, 54, 61, 68, 75, 82, 89, + 96, 55, 62, 69, 76, 83, 90, 97, 104, 63, 70, 77, 84, 91, 98, 105, 112, 71, 78, 85, 92, 99, 106, 113, 120, 79, 86, 93, 100, 107, 114, 121, 87, 94, + 101, 108, 115, 122, 95, 102, 109, 116, 123, 103, 110, 117, 124, 111, 118, 125, 119, 126, 127, +]; + +pub static DEFAULT_SCAN_8X32: [u16; 256] = [ + 0, 1, 8, 2, 9, 16, 3, 10, 17, 24, 4, 11, 18, 25, 32, 5, 12, 19, 26, 33, 40, 6, 13, 20, 27, 34, 41, 48, 7, 14, 21, 28, 35, 42, 49, 56, 15, 22, 29, + 36, 43, 50, 57, 64, 23, 30, 37, 44, 51, 58, 65, 72, 31, 38, 45, 52, 59, 66, 73, 80, 39, 46, 53, 60, 67, 74, 81, 88, 47, 54, 61, 68, 75, 82, 89, + 96, 55, 62, 69, 76, 83, 90, 97, 104, 63, 70, 77, 84, 91, 98, 105, 112, 71, 78, 85, 92, 99, 106, 113, 120, 79, 86, 93, 100, 107, 114, 121, 128, + 87, 94, 101, 108, 115, 122, 129, 136, 95, 102, 109, 116, 123, 130, 137, 144, 103, 110, 117, 124, 131, 138, 145, 152, 111, 118, 125, 132, 139, + 146, 153, 160, 119, 126, 133, 140, 147, 154, 161, 168, 127, 134, 141, 148, 155, 162, 169, 176, 135, 142, 149, 156, 163, 170, 177, 184, 143, 150, + 157, 164, 171, 178, 185, 192, 151, 158, 165, 172, 179, 186, 193, 200, 159, 166, 173, 180, 187, 194, 201, 208, 167, 174, 181, 188, 195, 202, 209, + 216, 175, 182, 189, 196, 203, 210, 217, 224, 183, 190, 197, 204, 211, 218, 225, 232, 191, 198, 205, 212, 219, 226, 233, 240, 199, 206, 213, 220, + 227, 234, 241, 248, 207, 214, 221, 228, 235, 242, 249, 215, 222, 229, 236, 243, 250, 223, 230, 237, 244, 251, 231, 238, 245, 252, 239, 246, 253, + 247, 254, 255, +]; + +pub static DEFAULT_SCAN_8X4: [u16; 32] = + [0, 8, 1, 16, 9, 2, 24, 17, 10, 3, 25, 18, 11, 4, 26, 19, 12, 5, 27, 20, 13, 6, 28, 21, 14, 7, 29, 22, 15, 30, 23, 31]; + +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_SEGMENT_ID_CDF: [[u16; 9]; 3] = [ + [5622, 7893, 16093, 18233, 27809, 28373, 32533, 32768, 0], + [14274, 18230, 22557, 24935, 29980, 30851, 32344, 32768, 0], + [27527, 28487, 28723, 28890, 32397, 32647, 32679, 32768, 0], +]; + +pub static DEFAULT_SEGMENT_ID_PREDICTED_CDF: [[u16; 3]; 3] = [[16384, 32768, 0], [16384, 32768, 0], [16384, 32768, 0]]; + +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_SKIP_MODE_CDF: [[u16; 3]; 3] = [[32621, 32768, 0], [20708, 32768, 0], [8127, 32768, 0]]; + +pub static DEFAULT_TX_16X16_CDF: [[u16; 4]; 3] = [[12272, 30172, 32768, 0], [12272, 30172, 32768, 0], [18677, 30848, 32768, 0]]; + +pub static DEFAULT_TX_32X32_CDF: [[u16; 4]; 3] = [[12986, 15180, 32768, 0], [12986, 15180, 32768, 0], [24302, 25602, 32768, 0]]; + +pub static DEFAULT_TX_64X64_CDF: [[u16; 4]; 3] = [[5782, 11475, 32768, 0], [5782, 11475, 32768, 0], [16803, 22759, 32768, 0]]; + +pub static DEFAULT_TX_8X8_CDF: [[u16; 3]; 3] = [[19968, 32768, 0], [19968, 32768, 0], [24320, 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_TXFM_SPLIT_CDF: [[u16; 3]; 21] = [ + [28581, 32768, 0], + [23846, 32768, 0], + [20847, 32768, 0], + [24315, 32768, 0], + [18196, 32768, 0], + [12133, 32768, 0], + [18791, 32768, 0], + [10887, 32768, 0], + [11005, 32768, 0], + [27179, 32768, 0], + [20004, 32768, 0], + [11281, 32768, 0], + [26549, 32768, 0], + [19308, 32768, 0], + [14224, 32768, 0], + [28015, 32768, 0], + [21546, 32768, 0], + [14400, 32768, 0], + [28165, 32768, 0], + [22401, 32768, 0], + [16088, 32768, 0], +]; + +pub static DEFAULT_UNI_COMP_REF_CDF: [[[u16; 3]; 3]; 3] = [ + [[5284, 32768, 0], [3865, 32768, 0], [3128, 32768, 0]], + [[23152, 32768, 0], [14173, 32768, 0], [15270, 32768, 0]], + [[31774, 32768, 0], [25120, 32768, 0], [26710, 32768, 0]], +]; + +pub static DEFAULT_USE_OBMC_CDF: [[u16; 3]; 22] = [ + [16384, 32768, 0], + [16384, 32768, 0], + [16384, 32768, 0], + [10437, 32768, 0], + [9371, 32768, 0], + [9301, 32768, 0], + [17432, 32768, 0], + [14423, 32768, 0], + [15142, 32768, 0], + [25817, 32768, 0], + [22823, 32768, 0], + [22083, 32768, 0], + [30128, 32768, 0], + [31014, 32768, 0], + [31560, 32768, 0], + [32638, 32768, 0], + [16384, 32768, 0], + [16384, 32768, 0], + [23664, 32768, 0], + [20901, 32768, 0], + [24008, 32768, 0], + [26879, 32768, 0], +]; + +pub static DEFAULT_USE_SGRPROJ_CDF: [u16; 3] = [16855, 32768, 0]; + +pub static DEFAULT_USE_WIENER_CDF: [u16; 3] = [11570, 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_WEDGE_INDEX_CDF: [[u16; 17]; 22] = [ + [2048, 4096, 6144, 8192, 10240, 12288, 14336, 16384, 18432, 20480, 22528, 24576, 26624, 28672, 30720, 32768, 0], + [2048, 4096, 6144, 8192, 10240, 12288, 14336, 16384, 18432, 20480, 22528, 24576, 26624, 28672, 30720, 32768, 0], + [2048, 4096, 6144, 8192, 10240, 12288, 14336, 16384, 18432, 20480, 22528, 24576, 26624, 28672, 30720, 32768, 0], + [2438, 4440, 6599, 8663, 11005, 12874, 15751, 18094, 20359, 22362, 24127, 25702, 27752, 29450, 31171, 32768, 0], + [806, 3266, 6005, 6738, 7218, 7367, 7771, 14588, 16323, 17367, 18452, 19422, 22839, 26127, 29629, 32768, 0], + [2779, 3738, 4683, 7213, 7775, 8017, 8655, 14357, 17939, 21332, 24520, 27470, 29456, 30529, 31656, 32768, 0], + [1684, 3625, 5675, 7108, 9302, 11274, 14429, 17144, 19163, 20961, 22884, 24471, 26719, 28714, 30877, 32768, 0], + [1142, 3491, 6277, 7314, 8089, 8355, 9023, 13624, 15369, 16730, 18114, 19313, 22521, 26012, 29550, 32768, 0], + [2742, 4195, 5727, 8035, 8980, 9336, 10146, 14124, 17270, 20533, 23434, 25972, 27944, 29570, 31416, 32768, 0], + [1727, 3948, 6101, 7796, 9841, 12344, 15766, 18944, 20638, 22038, 23963, 25311, 26988, 28766, 31012, 32768, 0], + [2048, 4096, 6144, 8192, 10240, 12288, 14336, 16384, 18432, 20480, 22528, 24576, 26624, 28672, 30720, 32768, 0], + [2048, 4096, 6144, 8192, 10240, 12288, 14336, 16384, 18432, 20480, 22528, 24576, 26624, 28672, 30720, 32768, 0], + [2048, 4096, 6144, 8192, 10240, 12288, 14336, 16384, 18432, 20480, 22528, 24576, 26624, 28672, 30720, 32768, 0], + [2048, 4096, 6144, 8192, 10240, 12288, 14336, 16384, 18432, 20480, 22528, 24576, 26624, 28672, 30720, 32768, 0], + [2048, 4096, 6144, 8192, 10240, 12288, 14336, 16384, 18432, 20480, 22528, 24576, 26624, 28672, 30720, 32768, 0], + [2048, 4096, 6144, 8192, 10240, 12288, 14336, 16384, 18432, 20480, 22528, 24576, 26624, 28672, 30720, 32768, 0], + [2048, 4096, 6144, 8192, 10240, 12288, 14336, 16384, 18432, 20480, 22528, 24576, 26624, 28672, 30720, 32768, 0], + [2048, 4096, 6144, 8192, 10240, 12288, 14336, 16384, 18432, 20480, 22528, 24576, 26624, 28672, 30720, 32768, 0], + [154, 987, 1925, 2051, 2088, 2111, 2151, 23033, 23703, 24284, 24985, 25684, 27259, 28883, 30911, 32768, 0], + [1135, 1322, 1493, 2635, 2696, 2737, 2770, 21016, 22935, 25057, 27251, 29173, 30089, 30960, 31933, 32768, 0], + [2048, 4096, 6144, 8192, 10240, 12288, 14336, 16384, 18432, 20480, 22528, 24576, 26624, 28672, 30720, 32768, 0], + [2048, 4096, 6144, 8192, 10240, 12288, 14336, 16384, 18432, 20480, 22528, 24576, 26624, 28672, 30720, 32768, 0], +]; + +pub static DEFAULT_WEDGE_INTER_INTRA_CDF: [[u16; 3]; 22] = [ + [16384, 32768, 0], + [16384, 32768, 0], + [16384, 32768, 0], + [20036, 32768, 0], + [24957, 32768, 0], + [26704, 32768, 0], + [27530, 32768, 0], + [29564, 32768, 0], + [29444, 32768, 0], + [26872, 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], + [16384, 32768, 0], + [16384, 32768, 0], + [16384, 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 DIV_LUT: [u16; 257] = [ + 16384, 16320, 16257, 16194, 16132, 16070, 16009, 15948, 15888, 15828, 15768, 15709, 15650, 15592, 15534, 15477, 15420, 15364, 15308, 15252, + 15197, 15142, 15087, 15033, 14980, 14926, 14873, 14821, 14769, 14717, 14665, 14614, 14564, 14513, 14463, 14413, 14364, 14315, 14266, 14218, + 14170, 14122, 14075, 14028, 13981, 13935, 13888, 13843, 13797, 13752, 13707, 13662, 13618, 13574, 13530, 13487, 13443, 13400, 13358, 13315, + 13273, 13231, 13190, 13148, 13107, 13066, 13026, 12985, 12945, 12906, 12866, 12827, 12788, 12749, 12710, 12672, 12633, 12596, 12558, 12520, + 12483, 12446, 12409, 12373, 12336, 12300, 12264, 12228, 12193, 12157, 12122, 12087, 12053, 12018, 11984, 11950, 11916, 11882, 11848, 11815, + 11782, 11749, 11716, 11683, 11651, 11619, 11586, 11555, 11523, 11491, 11460, 11429, 11398, 11367, 11336, 11305, 11275, 11245, 11215, 11185, + 11155, 11125, 11096, 11067, 11038, 11009, 10980, 10951, 10923, 10894, 10866, 10838, 10810, 10782, 10755, 10727, 10700, 10673, 10645, 10618, + 10592, 10565, 10538, 10512, 10486, 10460, 10434, 10408, 10382, 10356, 10331, 10305, 10280, 10255, 10230, 10205, 10180, 10156, 10131, 10107, + 10082, 10058, 10034, 10010, 9986, 9963, 9939, 9916, 9892, 9869, 9846, 9823, 9800, 9777, 9754, 9732, 9709, 9687, 9664, 9642, 9620, 9598, 9576, + 9554, 9533, 9511, 9489, 9468, 9447, 9425, 9404, 9383, 9362, 9341, 9321, 9300, 9279, 9259, 9239, 9218, 9198, 9178, 9158, 9138, 9118, 9098, 9079, + 9059, 9039, 9020, 9001, 8981, 8962, 8943, 8924, 8905, 8886, 8867, 8849, 8830, 8812, 8793, 8775, 8756, 8738, 8720, 8702, 8684, 8666, 8648, 8630, + 8613, 8595, 8577, 8560, 8542, 8525, 8508, 8490, 8473, 8456, 8439, 8422, 8405, 8389, 8372, 8355, 8339, 8322, 8306, 8289, 8273, 8257, 8240, 8224, + 8208, 8192, +]; + +pub static DIV_MULT: [u16; 32] = [ + 0, 16384, 8192, 5461, 4096, 3276, 2730, 2340, 2048, 1820, 1638, 1489, 1365, 1260, 1170, 1092, 1024, 963, 910, 862, 819, 780, 744, 712, 682, 655, + 630, 606, 585, 564, 546, 528, +]; + +pub static DIV_TABLE: [u16; 9] = [0, 840, 420, 280, 210, 168, 140, 120, 105]; + +pub static DR_INTRA_DERIVATIVE: [u16; 90] = [ + 0, 0, 0, 1023, 0, 0, 547, 0, 0, 372, 0, 0, 0, 0, 273, 0, 0, 215, 0, 0, 178, 0, 0, 151, 0, 0, 132, 0, 0, 116, 0, 0, 102, 0, 0, 0, 90, 0, 0, 80, 0, + 0, 71, 0, 0, 64, 0, 0, 57, 0, 0, 51, 0, 0, 45, 0, 0, 0, 40, 0, 0, 35, 0, 0, 31, 0, 0, 27, 0, 0, 23, 0, 0, 19, 0, 0, 15, 0, 0, 0, 0, 11, 0, 0, 7, + 0, 0, 3, 0, 0, +]; + +pub static FILTER_INTRA_MODE_TO_INTRA_DIR: [u8; 5] = [0, 1, 2, 6, 0]; + +pub static GAUSSIAN_SEQUENCE: [i16; 2048] = [ + 56, 568, -180, 172, 124, -84, 172, -64, -900, 24, 820, 224, 1248, 996, 272, -8, -916, -388, -732, -104, -188, 800, 112, -652, -320, -376, 140, + -252, 492, -168, 44, -788, 588, -584, 500, -228, 12, 680, 272, -476, 972, -100, 652, 368, 432, -196, -720, -192, 1000, -332, 652, -136, -552, + -604, -4, 192, -220, -136, 1000, -52, 372, -96, -624, 124, -24, 396, 540, -12, -104, 640, 464, 244, -208, -84, 368, -528, -740, 248, -968, -848, + 608, 376, -60, -292, -40, -156, 252, -292, 248, 224, -280, 400, -244, 244, -60, 76, -80, 212, 532, 340, 128, -36, 824, -352, -60, -264, -96, + -612, 416, -704, 220, -204, 640, -160, 1220, -408, 900, 336, 20, -336, -96, -792, 304, 48, -28, -1232, -1172, -448, 104, -292, -520, 244, 60, + -948, 0, -708, 268, 108, 356, -548, 488, -344, -136, 488, -196, -224, 656, -236, -1128, 60, 4, 140, 276, -676, -376, 168, -108, 464, 8, 564, 64, + 240, 308, -300, -400, -456, -136, 56, 120, -408, -116, 436, 504, -232, 328, 844, -164, -84, 784, -168, 232, -224, 348, -376, 128, 568, 96, -1244, + -288, 276, 848, 832, -360, 656, 464, -384, -332, -356, 728, -388, 160, -192, 468, 296, 224, 140, -776, -100, 280, 4, 196, 44, -36, -648, 932, 16, + 1428, 28, 528, 808, 772, 20, 268, 88, -332, -284, 124, -384, -448, 208, -228, -1044, -328, 660, 380, -148, -300, 588, 240, 540, 28, 136, -88, + -436, 256, 296, -1000, 1400, 0, -48, 1056, -136, 264, -528, -1108, 632, -484, -592, -344, 796, 124, -668, -768, 388, 1296, -232, -188, -200, + -288, -4, 308, 100, -168, 256, -500, 204, -508, 648, -136, 372, -272, -120, -1004, -552, -548, -384, 548, -296, 428, -108, -8, -912, -324, -224, + -88, -112, -220, -100, 996, -796, 548, 360, -216, 180, 428, -200, -212, 148, 96, 148, 284, 216, -412, -320, 120, -300, -384, -604, -572, -332, + -8, -180, -176, 696, 116, -88, 628, 76, 44, -516, 240, -208, -40, 100, -592, 344, -308, -452, -228, 20, 916, -1752, -136, -340, -804, 140, 40, + 512, 340, 248, 184, -492, 896, -156, 932, -628, 328, -688, -448, -616, -752, -100, 560, -1020, 180, -800, -64, 76, 576, 1068, 396, 660, 552, + -108, -28, 320, -628, 312, -92, -92, -472, 268, 16, 560, 516, -672, -52, 492, -100, 260, 384, 284, 292, 304, -148, 88, -152, 1012, 1064, -228, + 164, -376, -684, 592, -392, 156, 196, -524, -64, -884, 160, -176, 636, 648, 404, -396, -436, 864, 424, -728, 988, -604, 904, -592, 296, -224, + 536, -176, -920, 436, -48, 1176, -884, 416, -776, -824, -884, 524, -548, -564, -68, -164, -96, 692, 364, -692, -1012, -68, 260, -480, 876, -1116, + 452, -332, -352, 892, -1088, 1220, -676, 12, -292, 244, 496, 372, -32, 280, 200, 112, -440, -96, 24, -644, -184, 56, -432, 224, -980, 272, -260, + 144, -436, 420, 356, 364, -528, 76, 172, -744, -368, 404, -752, -416, 684, -688, 72, 540, 416, 92, 444, 480, -72, -1416, 164, -1172, -68, 24, + 424, 264, 1040, 128, -912, -524, -356, 64, 876, -12, 4, -88, 532, 272, -524, 320, 276, -508, 940, 24, -400, -120, 756, 60, 236, -412, 100, 376, + -484, 400, -100, -740, -108, -260, 328, -268, 224, -200, -416, 184, -604, -564, -20, 296, 60, 892, -888, 60, 164, 68, -760, 216, -296, 904, -336, + -28, 404, -356, -568, -208, -1480, -512, 296, 328, -360, -164, -1560, -776, 1156, -428, 164, -504, -112, 120, -216, -148, -264, 308, 32, 64, -72, + 72, 116, 176, -64, -272, 460, -536, -784, -280, 348, 108, -752, -132, 524, -540, -776, 116, -296, -1196, -288, -560, 1040, -472, 116, -848, + -1116, 116, 636, 696, 284, -176, 1016, 204, -864, -648, -248, 356, 972, -584, -204, 264, 880, 528, -24, -184, 116, 448, -144, 828, 524, 212, + -212, 52, 12, 200, 268, -488, -404, -880, 824, -672, -40, 908, -248, 500, 716, -576, 492, -576, 16, 720, -108, 384, 124, 344, 280, 576, -500, + 252, 104, -308, 196, -188, -8, 1268, 296, 1032, -1196, 436, 316, 372, -432, -200, -660, 704, -224, 596, -132, 268, 32, -452, 884, 104, -1008, + 424, -1348, -280, 4, -1168, 368, 476, 696, 300, -8, 24, 180, -592, -196, 388, 304, 500, 724, -160, 244, -84, 272, -256, -420, 320, 208, -144, + -156, 156, 364, 452, 28, 540, 316, 220, -644, -248, 464, 72, 360, 32, -388, 496, -680, -48, 208, -116, -408, 60, -604, -392, 548, -840, 784, + -460, 656, -544, -388, -264, 908, -800, -628, -612, -568, 572, -220, 164, 288, -16, -308, 308, -112, -636, -760, 280, -668, 432, 364, 240, -196, + 604, 340, 384, 196, 592, -44, -500, 432, -580, -132, 636, -76, 392, 4, -412, 540, 508, 328, -356, -36, 16, -220, -64, -248, -60, 24, -192, 368, + 1040, 92, -24, -1044, -32, 40, 104, 148, 192, -136, -520, 56, -816, -224, 732, 392, 356, 212, -80, -424, -1008, -324, 588, -1496, 576, 460, -816, + -848, 56, -580, -92, -1372, -112, -496, 200, 364, 52, -140, 48, -48, -60, 84, 72, 40, 132, -356, -268, -104, -284, -404, 732, -520, 164, -304, + -540, 120, 328, -76, -460, 756, 388, 588, 236, -436, -72, -176, -404, -316, -148, 716, -604, 404, -72, -88, -888, -68, 944, 88, -220, -344, 960, + 472, 460, -232, 704, 120, 832, -228, 692, -508, 132, -476, 844, -748, -364, -44, 1116, -1104, -1056, 76, 428, 552, -692, 60, 356, 96, -384, -188, + -612, -576, 736, 508, 892, 352, -1132, 504, -24, -352, 324, 332, -600, -312, 292, 508, -144, -8, 484, 48, 284, -260, -240, 256, -100, -292, -204, + -44, 472, -204, 908, -188, -1000, -256, 92, 1164, -392, 564, 356, 652, -28, -884, 256, 484, -192, 760, -176, 376, -524, -452, -436, 860, -736, + 212, 124, 504, -476, 468, 76, -472, 552, -692, -944, -620, 740, -240, 400, 132, 20, 192, -196, 264, -668, -1012, -60, 296, -316, -828, 76, -156, + 284, -768, -448, -832, 148, 248, 652, 616, 1236, 288, -328, -400, -124, 588, 220, 520, -696, 1032, 768, -740, -92, -272, 296, 448, -464, 412, + -200, 392, 440, -200, 264, -152, -260, 320, 1032, 216, 320, -8, -64, 156, -1016, 1084, 1172, 536, 484, -432, 132, 372, -52, -256, 84, 116, -352, + 48, 116, 304, -384, 412, 924, -300, 528, 628, 180, 648, 44, -980, -220, 1320, 48, 332, 748, 524, -268, -720, 540, -276, 564, -344, -208, -196, + 436, 896, 88, -392, 132, 80, -964, -288, 568, 56, -48, -456, 888, 8, 552, -156, -292, 948, 288, 128, -716, -292, 1192, -152, 876, 352, -600, + -260, -812, -468, -28, -120, -32, -44, 1284, 496, 192, 464, 312, -76, -516, -380, -456, -1012, -48, 308, -156, 36, 492, -156, -808, 188, 1652, + 68, -120, -116, 316, 160, -140, 352, 808, -416, 592, 316, -480, 56, 528, -204, -568, 372, -232, 752, -344, 744, -4, 324, -416, -600, 768, 268, + -248, -88, -132, -420, -432, 80, -288, 404, -316, -1216, -588, 520, -108, 92, -320, 368, -480, -216, -92, 1688, -300, 180, 1020, -176, 820, -68, + -228, -260, 436, -904, 20, 40, -508, 440, -736, 312, 332, 204, 760, -372, 728, 96, -20, -632, -520, -560, 336, 1076, -64, -532, 776, 584, 192, + 396, -728, -520, 276, -188, 80, -52, -612, -252, -48, 648, 212, -688, 228, -52, -260, 428, -412, -272, -404, 180, 816, -796, 48, 152, 484, -88, + -216, 988, 696, 188, -528, 648, -116, -180, 316, 476, 12, -564, 96, 476, -252, -364, -376, -392, 556, -256, -576, 260, -352, 120, -16, -136, + -260, -492, 72, 556, 660, 580, 616, 772, 436, 424, -32, -324, -1268, 416, -324, -80, 920, 160, 228, 724, 32, -516, 64, 384, 68, -128, 136, 240, + 248, -204, -68, 252, -932, -120, -480, -628, -84, 192, 852, -404, -288, -132, 204, 100, 168, -68, -196, -868, 460, 1080, 380, -80, 244, 0, 484, + -888, 64, 184, 352, 600, 460, 164, 604, -196, 320, -64, 588, -184, 228, 12, 372, 48, -848, -344, 224, 208, -200, 484, 128, -20, 272, -468, -840, + 384, 256, -720, -520, -464, -580, 112, -120, 644, -356, -208, -608, -528, 704, 560, -424, 392, 828, 40, 84, 200, -152, 0, -144, 584, 280, -120, + 80, -556, -972, -196, -472, 724, 80, 168, -32, 88, 160, -688, 0, 160, 356, 372, -776, 740, -128, 676, -248, -480, 4, -364, 96, 544, 232, -1032, + 956, 236, 356, 20, -40, 300, 24, -676, -596, 132, 1120, -104, 532, -1096, 568, 648, 444, 508, 380, 188, -376, -604, 1488, 424, 24, 756, -220, + -192, 716, 120, 920, 688, 168, 44, -460, 568, 284, 1144, 1160, 600, 424, 888, 656, -356, -320, 220, 316, -176, -724, -188, -816, -628, -348, + -228, -380, 1012, -452, -660, 736, 928, 404, -696, -72, -268, -892, 128, 184, -344, -780, 360, 336, 400, 344, 428, 548, -112, 136, -228, -216, + -820, -516, 340, 92, -136, 116, -300, 376, -244, 100, -316, -520, -284, -12, 824, 164, -548, -180, -128, 116, -924, -828, 268, -368, -580, 620, + 192, 160, 0, -1676, 1068, 424, -56, -360, 468, -156, 720, 288, -528, 556, -364, 548, -148, 504, 316, 152, -648, -620, -684, -24, -376, -384, + -108, -920, -1032, 768, 180, -264, -508, -1268, -260, -60, 300, -240, 988, 724, -376, -576, -212, -736, 556, 192, 1092, -620, -880, 376, -56, -4, + -216, -32, 836, 268, 396, 1332, 864, -600, 100, 56, -412, -92, 356, 180, 884, -468, -436, 292, -388, -804, -704, -840, 368, -348, 140, -724, + 1536, 940, 372, 112, -372, 436, -480, 1136, 296, -32, -228, 132, -48, -220, 868, -1016, -60, -1044, -464, 328, 916, 244, 12, -736, -296, 360, + 468, -376, -108, -92, 788, 368, -56, 544, 400, -672, -420, 728, 16, 320, 44, -284, -380, -796, 488, 132, 204, -596, -372, 88, -152, -908, -636, + -572, -624, -116, -692, -200, -56, 276, -88, 484, -324, 948, 864, 1000, -456, -184, -276, 292, -296, 156, 676, 320, 160, 908, -84, -1236, -288, + -116, 260, -372, -644, 732, -756, -96, 84, 344, -520, 348, -688, 240, -84, 216, -1044, -136, -676, -396, -1500, 960, -40, 176, 168, 1516, 420, + -504, -344, -364, -360, 1216, -940, -380, -212, 252, -660, -708, 484, -444, -152, 928, -120, 1112, 476, -260, 560, -148, -344, 108, -196, 228, + -288, 504, 560, -328, -88, 288, -1008, 460, -228, 468, -836, -196, 76, 388, 232, 412, -1168, -716, -644, 756, -172, -356, -504, 116, 432, 528, + 48, 476, -168, -608, 448, 160, -532, -272, 28, -676, -12, 828, 980, 456, 520, 104, -104, 256, -344, -4, -28, -368, -52, -524, -572, -556, -200, + 768, 1124, -208, -512, 176, 232, 248, -148, -888, 604, -600, -304, 804, -156, -212, 488, -192, -804, -256, 368, -360, -916, -328, 228, -240, + -448, -472, 856, -556, -364, 572, -12, -156, -368, -340, 432, 252, -752, -152, 288, 268, -580, -848, -592, 108, -76, 244, 312, -716, 592, -80, + 436, 360, 4, -248, 160, 516, 584, 732, 44, -468, -280, -292, -156, -588, 28, 308, 912, 24, 124, 156, 180, -252, 944, -924, -772, -520, -428, + -624, 300, -212, -1144, 32, -724, 800, -1128, -212, -1288, -848, 180, -416, 440, 192, -576, -792, -76, -1080, 80, -532, -352, -132, 380, -820, + 148, 1112, 128, 164, 456, 700, -924, 144, -668, -384, 648, -832, 508, 552, -52, -100, -656, 208, -568, 748, -88, 680, 232, 300, 192, -408, -1012, + -152, -252, -268, 272, -876, -664, -648, -332, -136, 16, 12, 1152, -28, 332, -536, 320, -672, -460, -316, 532, -260, 228, -40, 1052, -816, 180, + 88, -496, -556, -672, -368, 428, 92, 356, 404, -408, 252, 196, -176, -556, 792, 268, 32, 372, 40, 96, -332, 328, 120, 372, -900, -40, 472, -264, + -592, 952, 128, 656, 112, 664, -232, 420, 4, -344, -464, 556, 244, -416, -32, 252, 0, -412, 188, -696, 508, -476, 324, -1096, 656, -312, 560, + 264, -136, 304, 160, -64, -580, 248, 336, -720, 560, -348, -288, -276, -196, -500, 852, -544, -236, -1128, -992, -776, 116, 56, 52, 860, 884, + 212, -12, 168, 1020, 512, -552, 924, -148, 716, 188, 164, -340, -520, -184, 880, -152, -680, -208, -1156, -300, -528, -472, 364, 100, -744, + -1056, -32, 540, 280, 144, -676, -32, -232, -280, -224, 96, 568, -76, 172, 148, 148, 104, 32, -296, -32, 788, -80, 32, -16, 280, 288, 944, 428, + -484, +]; + +pub static II_WEIGHTS_1D: [u8; 128] = [ + 60, 58, 56, 54, 52, 50, 48, 47, 45, 44, 42, 41, 39, 38, 37, 35, 34, 33, 32, 31, 30, 29, 28, 27, 26, 25, 24, 23, 22, 22, 21, 20, 19, 19, 18, 18, + 17, 16, 16, 15, 15, 14, 14, 13, 13, 12, 12, 12, 11, 11, 10, 10, 10, 9, 9, 9, 8, 8, 8, 8, 7, 7, 7, 7, 6, 6, 6, 6, 6, 5, 5, 5, 5, 5, 4, 4, 4, 4, 4, + 4, 4, 4, 3, 3, 3, 3, 3, 3, 3, 3, 3, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, + 1, +]; + +pub static INTRA_EDGE_KERNEL: [[u8; 5]; 3] = [[0, 4, 8, 4, 0], [0, 5, 6, 5, 0], [2, 4, 4, 4, 2]]; + +pub static INTRA_FILTER_TAPS: [[[i8; 7]; 8]; 5] = [ + [ + [-6, 10, 0, 0, 0, 12, 0], + [-5, 2, 10, 0, 0, 9, 0], + [-3, 1, 1, 10, 0, 7, 0], + [-3, 1, 1, 2, 10, 5, 0], + [-4, 6, 0, 0, 0, 2, 12], + [-3, 2, 6, 0, 0, 2, 9], + [-3, 2, 2, 6, 0, 2, 7], + [-3, 1, 2, 2, 6, 3, 5], + ], + [ + [-10, 16, 0, 0, 0, 10, 0], + [-6, 0, 16, 0, 0, 6, 0], + [-4, 0, 0, 16, 0, 4, 0], + [-2, 0, 0, 0, 16, 2, 0], + [-10, 16, 0, 0, 0, 0, 10], + [-6, 0, 16, 0, 0, 0, 6], + [-4, 0, 0, 16, 0, 0, 4], + [-2, 0, 0, 0, 16, 0, 2], + ], + [ + [-8, 8, 0, 0, 0, 16, 0], + [-8, 0, 8, 0, 0, 16, 0], + [-8, 0, 0, 8, 0, 16, 0], + [-8, 0, 0, 0, 8, 16, 0], + [-4, 4, 0, 0, 0, 0, 16], + [-4, 0, 4, 0, 0, 0, 16], + [-4, 0, 0, 4, 0, 0, 16], + [-4, 0, 0, 0, 4, 0, 16], + ], + [ + [-2, 8, 0, 0, 0, 10, 0], + [-1, 3, 8, 0, 0, 6, 0], + [-1, 2, 3, 8, 0, 4, 0], + [0, 1, 2, 3, 8, 2, 0], + [-1, 4, 0, 0, 0, 3, 10], + [-1, 3, 4, 0, 0, 4, 6], + [-1, 2, 3, 4, 0, 4, 4], + [-1, 2, 2, 3, 4, 3, 3], + ], + [ + [-12, 14, 0, 0, 0, 14, 0], + [-10, 0, 14, 0, 0, 12, 0], + [-9, 0, 0, 14, 0, 11, 0], + [-8, 0, 0, 0, 14, 10, 0], + [-10, 12, 0, 0, 0, 0, 14], + [-9, 1, 12, 0, 0, 0, 12], + [-8, 0, 0, 12, 0, 1, 11], + [-7, 0, 0, 1, 12, 1, 9], + ], +]; + +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_DEPTH_TABLE: [u8; 22] = [0, 1, 1, 1, 2, 2, 2, 3, 3, 3, 4, 4, 4, 4, 4, 4, 2, 2, 3, 3, 4, 4]; + +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 MCOL_SCAN_16X16: [u16; 256] = [ + 0, 16, 32, 48, 64, 80, 96, 112, 128, 144, 160, 176, 192, 208, 224, 240, 1, 17, 33, 49, 65, 81, 97, 113, 129, 145, 161, 177, 193, 209, 225, 241, + 2, 18, 34, 50, 66, 82, 98, 114, 130, 146, 162, 178, 194, 210, 226, 242, 3, 19, 35, 51, 67, 83, 99, 115, 131, 147, 163, 179, 195, 211, 227, 243, + 4, 20, 36, 52, 68, 84, 100, 116, 132, 148, 164, 180, 196, 212, 228, 244, 5, 21, 37, 53, 69, 85, 101, 117, 133, 149, 165, 181, 197, 213, 229, 245, + 6, 22, 38, 54, 70, 86, 102, 118, 134, 150, 166, 182, 198, 214, 230, 246, 7, 23, 39, 55, 71, 87, 103, 119, 135, 151, 167, 183, 199, 215, 231, 247, + 8, 24, 40, 56, 72, 88, 104, 120, 136, 152, 168, 184, 200, 216, 232, 248, 9, 25, 41, 57, 73, 89, 105, 121, 137, 153, 169, 185, 201, 217, 233, 249, + 10, 26, 42, 58, 74, 90, 106, 122, 138, 154, 170, 186, 202, 218, 234, 250, 11, 27, 43, 59, 75, 91, 107, 123, 139, 155, 171, 187, 203, 219, 235, + 251, 12, 28, 44, 60, 76, 92, 108, 124, 140, 156, 172, 188, 204, 220, 236, 252, 13, 29, 45, 61, 77, 93, 109, 125, 141, 157, 173, 189, 205, 221, + 237, 253, 14, 30, 46, 62, 78, 94, 110, 126, 142, 158, 174, 190, 206, 222, 238, 254, 15, 31, 47, 63, 79, 95, 111, 127, 143, 159, 175, 191, 207, + 223, 239, 255, +]; + +pub static MCOL_SCAN_16X4: [u16; 64] = [ + 0, 16, 32, 48, 1, 17, 33, 49, 2, 18, 34, 50, 3, 19, 35, 51, 4, 20, 36, 52, 5, 21, 37, 53, 6, 22, 38, 54, 7, 23, 39, 55, 8, 24, 40, 56, 9, 25, 41, + 57, 10, 26, 42, 58, 11, 27, 43, 59, 12, 28, 44, 60, 13, 29, 45, 61, 14, 30, 46, 62, 15, 31, 47, 63, +]; + +pub static MCOL_SCAN_16X8: [u16; 128] = [ + 0, 16, 32, 48, 64, 80, 96, 112, 1, 17, 33, 49, 65, 81, 97, 113, 2, 18, 34, 50, 66, 82, 98, 114, 3, 19, 35, 51, 67, 83, 99, 115, 4, 20, 36, 52, + 68, 84, 100, 116, 5, 21, 37, 53, 69, 85, 101, 117, 6, 22, 38, 54, 70, 86, 102, 118, 7, 23, 39, 55, 71, 87, 103, 119, 8, 24, 40, 56, 72, 88, 104, + 120, 9, 25, 41, 57, 73, 89, 105, 121, 10, 26, 42, 58, 74, 90, 106, 122, 11, 27, 43, 59, 75, 91, 107, 123, 12, 28, 44, 60, 76, 92, 108, 124, 13, + 29, 45, 61, 77, 93, 109, 125, 14, 30, 46, 62, 78, 94, 110, 126, 15, 31, 47, 63, 79, 95, 111, 127, +]; + +pub static MCOL_SCAN_4X16: [u16; 64] = [ + 0, 4, 8, 12, 16, 20, 24, 28, 32, 36, 40, 44, 48, 52, 56, 60, 1, 5, 9, 13, 17, 21, 25, 29, 33, 37, 41, 45, 49, 53, 57, 61, 2, 6, 10, 14, 18, 22, + 26, 30, 34, 38, 42, 46, 50, 54, 58, 62, 3, 7, 11, 15, 19, 23, 27, 31, 35, 39, 43, 47, 51, 55, 59, 63, +]; + +pub static MCOL_SCAN_4X4: [u16; 16] = [0, 4, 8, 12, 1, 5, 9, 13, 2, 6, 10, 14, 3, 7, 11, 15]; + +pub static MCOL_SCAN_4X8: [u16; 32] = + [0, 4, 8, 12, 16, 20, 24, 28, 1, 5, 9, 13, 17, 21, 25, 29, 2, 6, 10, 14, 18, 22, 26, 30, 3, 7, 11, 15, 19, 23, 27, 31]; + +pub static MCOL_SCAN_8X16: [u16; 128] = [ + 0, 8, 16, 24, 32, 40, 48, 56, 64, 72, 80, 88, 96, 104, 112, 120, 1, 9, 17, 25, 33, 41, 49, 57, 65, 73, 81, 89, 97, 105, 113, 121, 2, 10, 18, 26, + 34, 42, 50, 58, 66, 74, 82, 90, 98, 106, 114, 122, 3, 11, 19, 27, 35, 43, 51, 59, 67, 75, 83, 91, 99, 107, 115, 123, 4, 12, 20, 28, 36, 44, 52, + 60, 68, 76, 84, 92, 100, 108, 116, 124, 5, 13, 21, 29, 37, 45, 53, 61, 69, 77, 85, 93, 101, 109, 117, 125, 6, 14, 22, 30, 38, 46, 54, 62, 70, 78, + 86, 94, 102, 110, 118, 126, 7, 15, 23, 31, 39, 47, 55, 63, 71, 79, 87, 95, 103, 111, 119, 127, +]; + +pub static MCOL_SCAN_8X4: [u16; 32] = + [0, 8, 16, 24, 1, 9, 17, 25, 2, 10, 18, 26, 3, 11, 19, 27, 4, 12, 20, 28, 5, 13, 21, 29, 6, 14, 22, 30, 7, 15, 23, 31]; + +pub static MCOL_SCAN_8X8: [u16; 64] = [ + 0, 8, 16, 24, 32, 40, 48, 56, 1, 9, 17, 25, 33, 41, 49, 57, 2, 10, 18, 26, 34, 42, 50, 58, 3, 11, 19, 27, 35, 43, 51, 59, 4, 12, 20, 28, 36, 44, + 52, 60, 5, 13, 21, 29, 37, 45, 53, 61, 6, 14, 22, 30, 38, 46, 54, 62, 7, 15, 23, 31, 39, 47, 55, 63, +]; + +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_ANGLE: [u8; 13] = [0, 90, 180, 45, 135, 113, 157, 203, 67, 0, 0, 0, 0]; + +pub static MODE_TO_TXFM: [u8; 14] = [0, 1, 2, 0, 3, 1, 2, 2, 1, 3, 1, 2, 3, 0]; + +pub static MROW_SCAN_16X16: [u16; 256] = [ + 0, 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, 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, 103, 104, 105, 106, 107, 108, + 109, 110, 111, 112, 113, 114, 115, 116, 117, 118, 119, 120, 121, 122, 123, 124, 125, 126, 127, 128, 129, 130, 131, 132, 133, 134, 135, 136, 137, + 138, 139, 140, 141, 142, 143, 144, 145, 146, 147, 148, 149, 150, 151, 152, 153, 154, 155, 156, 157, 158, 159, 160, 161, 162, 163, 164, 165, 166, + 167, 168, 169, 170, 171, 172, 173, 174, 175, 176, 177, 178, 179, 180, 181, 182, 183, 184, 185, 186, 187, 188, 189, 190, 191, 192, 193, 194, 195, + 196, 197, 198, 199, 200, 201, 202, 203, 204, 205, 206, 207, 208, 209, 210, 211, 212, 213, 214, 215, 216, 217, 218, 219, 220, 221, 222, 223, 224, + 225, 226, 227, 228, 229, 230, 231, 232, 233, 234, 235, 236, 237, 238, 239, 240, 241, 242, 243, 244, 245, 246, 247, 248, 249, 250, 251, 252, 253, + 254, 255, +]; + +pub static MROW_SCAN_16X4: [u16; 64] = [ + 0, 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, 63, +]; + +pub static MROW_SCAN_16X8: [u16; 128] = [ + 0, 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, 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, 103, 104, 105, 106, 107, 108, + 109, 110, 111, 112, 113, 114, 115, 116, 117, 118, 119, 120, 121, 122, 123, 124, 125, 126, 127, +]; + +pub static MROW_SCAN_4X16: [u16; 64] = [ + 0, 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, 63, +]; + +pub static MROW_SCAN_4X4: [u16; 16] = [0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15]; + +pub static MROW_SCAN_4X8: [u16; 32] = + [0, 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]; + +pub static MROW_SCAN_8X16: [u16; 128] = [ + 0, 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, 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, 103, 104, 105, 106, 107, 108, + 109, 110, 111, 112, 113, 114, 115, 116, 117, 118, 119, 120, 121, 122, 123, 124, 125, 126, 127, +]; + +pub static MROW_SCAN_8X4: [u16; 32] = + [0, 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]; + +pub static MROW_SCAN_8X8: [u16; 64] = [ + 0, 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, 63, +]; + +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 OBMC_MASK_16: [u8; 16] = [34, 37, 40, 43, 46, 49, 52, 54, 56, 58, 60, 61, 64, 64, 64, 64]; + +pub static OBMC_MASK_2: [u8; 2] = [45, 64]; + +pub static OBMC_MASK_32: [u8; 32] = + [33, 35, 36, 38, 40, 41, 43, 44, 45, 47, 48, 50, 51, 52, 53, 55, 56, 57, 58, 59, 60, 60, 61, 62, 64, 64, 64, 64, 64, 64, 64, 64]; + +pub static OBMC_MASK_4: [u8; 4] = [39, 50, 59, 64]; + +pub static OBMC_MASK_8: [u8; 8] = [36, 42, 48, 53, 57, 61, 64, 64]; + +pub static PALETTE_COLOR_CONTEXT: [i8; 9] = [-1, -1, 0, -1, -1, 4, 3, 2, 1]; + +pub static PALETTE_COLOR_HASH_MULTIPLIERS: [u8; 3] = [1, 2, 2]; + +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 QM_OFFSET: [u16; 19] = [0, 16, 80, 336, 336, 1360, 1392, 1424, 1552, 1680, 2192, 336, 336, 2704, 2768, 2832, 3088, 1680, 2192]; + +pub static QUANT_DIST_LOOKUP: [[u8; 2]; 4] = [[9, 7], [11, 5], [12, 4], [13, 3]]; + +pub static QUANT_DIST_WEIGHT: [[u8; 2]; 4] = [[2, 3], [2, 5], [2, 7], [1, 31]]; + +pub static QUANTIZER_MATRIX: [[[u8; 3344]; 2]; 15] = [ + [ + [ + 32, 43, 73, 97, 43, 67, 94, 110, 73, 94, 137, 150, 97, 110, 150, 200, 32, 32, 38, 51, 68, 84, 95, 109, 32, 35, 40, 49, 63, 76, 89, 102, + 38, 40, 54, 65, 78, 91, 98, 106, 51, 49, 65, 82, 97, 111, 113, 121, 68, 63, 78, 97, 117, 134, 138, 142, 84, 76, 91, 111, 134, 152, 159, + 168, 95, 89, 98, 113, 138, 159, 183, 199, 109, 102, 106, 121, 142, 168, 199, 220, 32, 31, 31, 34, 36, 44, 48, 59, 65, 80, 83, 91, 97, + 104, 111, 119, 31, 32, 32, 33, 34, 41, 44, 54, 59, 72, 75, 83, 90, 97, 104, 112, 31, 32, 33, 35, 36, 42, 45, 54, 59, 71, 74, 81, 86, 93, + 100, 107, 34, 33, 35, 39, 42, 47, 51, 58, 63, 74, 76, 81, 84, 90, 97, 105, 36, 34, 36, 42, 48, 54, 57, 64, 68, 79, 81, 88, 91, 96, 102, + 105, 44, 41, 42, 47, 54, 63, 67, 75, 79, 90, 92, 95, 100, 102, 109, 112, 48, 44, 45, 51, 57, 67, 71, 80, 85, 96, 99, 107, 108, 111, 117, + 120, 59, 54, 54, 58, 64, 75, 80, 92, 98, 110, 113, 115, 116, 122, 125, 130, 65, 59, 59, 63, 68, 79, 85, 98, 105, 118, 121, 127, 130, 134, + 135, 140, 80, 72, 71, 74, 79, 90, 96, 110, 118, 134, 137, 140, 143, 144, 146, 152, 83, 75, 74, 76, 81, 92, 99, 113, 121, 137, 140, 151, + 152, 155, 158, 165, 91, 83, 81, 81, 88, 95, 107, 115, 127, 140, 151, 159, 166, 169, 173, 179, 97, 90, 86, 84, 91, 100, 108, 116, 130, + 143, 152, 166, 174, 182, 189, 193, 104, 97, 93, 90, 96, 102, 111, 122, 134, 144, 155, 169, 182, 191, 200, 210, 111, 104, 100, 97, 102, + 109, 117, 125, 135, 146, 158, 173, 189, 200, 210, 220, 119, 112, 107, 105, 105, 112, 120, 130, 140, 152, 165, 179, 193, 210, 220, 231, + 32, 31, 31, 31, 31, 32, 34, 35, 36, 39, 44, 46, 48, 54, 59, 62, 65, 71, 80, 81, 83, 88, 91, 94, 97, 101, 104, 107, 111, 115, 119, 123, + 31, 32, 32, 32, 32, 32, 34, 34, 35, 38, 42, 44, 46, 51, 56, 59, 62, 68, 76, 77, 78, 84, 86, 89, 92, 95, 99, 102, 105, 109, 113, 116, 31, + 32, 32, 32, 32, 32, 33, 34, 34, 37, 41, 42, 44, 49, 54, 56, 59, 65, 72, 73, 75, 80, 83, 86, 90, 93, 97, 101, 104, 108, 112, 116, 31, 32, + 32, 32, 33, 33, 34, 35, 35, 38, 41, 43, 45, 49, 54, 56, 59, 64, 72, 73, 74, 79, 82, 85, 88, 91, 94, 97, 101, 104, 107, 111, 31, 32, 32, + 33, 33, 34, 35, 36, 36, 39, 42, 44, 45, 50, 54, 56, 59, 64, 71, 72, 74, 78, 81, 84, 86, 89, 93, 96, 100, 104, 107, 111, 32, 32, 32, 33, + 34, 35, 37, 37, 38, 40, 42, 44, 46, 49, 53, 55, 58, 63, 69, 70, 72, 76, 79, 82, 85, 89, 93, 96, 99, 102, 106, 109, 34, 34, 33, 34, 35, + 37, 39, 41, 42, 45, 47, 49, 51, 54, 58, 60, 63, 68, 74, 75, 76, 80, 81, 82, 84, 87, 90, 93, 97, 101, 105, 110, 35, 34, 34, 35, 36, 37, + 41, 43, 45, 47, 50, 52, 53, 57, 61, 63, 65, 70, 76, 77, 79, 82, 84, 86, 89, 91, 92, 93, 96, 100, 103, 107, 36, 35, 34, 35, 36, 38, 42, + 45, 48, 50, 54, 55, 57, 60, 64, 66, 68, 73, 79, 80, 81, 85, 88, 90, 91, 93, 96, 99, 102, 103, 105, 107, 39, 38, 37, 38, 39, 40, 45, 47, + 50, 54, 58, 59, 61, 65, 69, 71, 73, 78, 84, 85, 86, 91, 92, 92, 95, 98, 100, 101, 103, 106, 110, 114, 44, 42, 41, 41, 42, 42, 47, 50, 54, + 58, 63, 65, 67, 71, 75, 77, 79, 84, 90, 91, 92, 95, 95, 97, 100, 101, 102, 105, 109, 111, 112, 114, 46, 44, 42, 43, 44, 44, 49, 52, 55, + 59, 65, 67, 69, 74, 78, 80, 82, 87, 93, 94, 95, 98, 100, 103, 102, 105, 108, 110, 111, 113, 117, 121, 48, 46, 44, 45, 45, 46, 51, 53, 57, + 61, 67, 69, 71, 76, 80, 83, 85, 90, 96, 97, 99, 103, 107, 105, 108, 111, 111, 113, 117, 119, 120, 122, 54, 51, 49, 49, 50, 49, 54, 57, + 60, 65, 71, 74, 76, 82, 87, 89, 92, 97, 104, 105, 106, 111, 110, 111, 114, 113, 116, 120, 120, 121, 125, 130, 59, 56, 54, 54, 54, 53, 58, + 61, 64, 69, 75, 78, 80, 87, 92, 95, 98, 103, 110, 111, 113, 115, 115, 119, 116, 120, 122, 122, 125, 129, 130, 130, 62, 59, 56, 56, 56, + 55, 60, 63, 66, 71, 77, 80, 83, 89, 95, 98, 101, 107, 114, 115, 117, 119, 123, 121, 125, 126, 125, 129, 131, 131, 135, 140, 65, 62, 59, + 59, 59, 58, 63, 65, 68, 73, 79, 82, 85, 92, 98, 101, 105, 111, 118, 119, 121, 126, 127, 128, 130, 130, 134, 133, 135, 140, 140, 140, 71, + 68, 65, 64, 64, 63, 68, 70, 73, 78, 84, 87, 90, 97, 103, 107, 111, 117, 125, 126, 128, 134, 132, 136, 133, 138, 137, 140, 143, 142, 145, + 150, 80, 76, 72, 72, 71, 69, 74, 76, 79, 84, 90, 93, 96, 104, 110, 114, 118, 125, 134, 135, 137, 139, 140, 139, 143, 142, 144, 146, 146, + 151, 152, 151, 81, 77, 73, 73, 72, 70, 75, 77, 80, 85, 91, 94, 97, 105, 111, 115, 119, 126, 135, 137, 138, 144, 147, 146, 148, 149, 151, + 150, 156, 155, 157, 163, 83, 78, 75, 74, 74, 72, 76, 79, 81, 86, 92, 95, 99, 106, 113, 117, 121, 128, 137, 138, 140, 147, 151, 156, 152, + 157, 155, 161, 158, 162, 165, 164, 88, 84, 80, 79, 78, 76, 80, 82, 85, 91, 95, 98, 103, 111, 115, 119, 126, 134, 139, 144, 147, 152, 154, + 158, 163, 159, 165, 163, 168, 168, 169, 176, 91, 86, 83, 82, 81, 79, 81, 84, 88, 92, 95, 100, 107, 110, 115, 123, 127, 132, 140, 147, + 151, 154, 159, 161, 166, 171, 169, 173, 173, 176, 179, 177, 94, 89, 86, 85, 84, 82, 82, 86, 90, 92, 97, 103, 105, 111, 119, 121, 128, + 136, 139, 146, 156, 158, 161, 166, 168, 174, 179, 178, 180, 183, 183, 190, 97, 92, 90, 88, 86, 85, 84, 89, 91, 95, 100, 102, 108, 114, + 116, 125, 130, 133, 143, 148, 152, 163, 166, 168, 174, 176, 182, 187, 189, 188, 193, 191, 101, 95, 93, 91, 89, 89, 87, 91, 93, 98, 101, + 105, 111, 113, 120, 126, 130, 138, 142, 149, 157, 159, 171, 174, 176, 183, 184, 191, 195, 199, 197, 204, 104, 99, 97, 94, 93, 93, 90, 92, + 96, 100, 102, 108, 111, 116, 122, 125, 134, 137, 144, 151, 155, 165, 169, 179, 182, 184, 191, 193, 200, 204, 210, 206, 107, 102, 101, 97, + 96, 96, 93, 93, 99, 101, 105, 110, 113, 120, 122, 129, 133, 140, 146, 150, 161, 163, 173, 178, 187, 191, 193, 200, 202, 210, 214, 222, + 111, 105, 104, 101, 100, 99, 97, 96, 102, 103, 109, 111, 117, 120, 125, 131, 135, 143, 146, 156, 158, 168, 173, 180, 189, 195, 200, 202, + 210, 212, 220, 224, 115, 109, 108, 104, 104, 102, 101, 100, 103, 106, 111, 113, 119, 121, 129, 131, 140, 142, 151, 155, 162, 168, 176, + 183, 188, 199, 204, 210, 212, 220, 222, 230, 119, 113, 112, 107, 107, 106, 105, 103, 105, 110, 112, 117, 120, 125, 130, 135, 140, 145, + 152, 157, 165, 169, 179, 183, 193, 197, 210, 214, 220, 222, 231, 232, 123, 116, 116, 111, 111, 109, 110, 107, 107, 114, 114, 121, 122, + 130, 130, 140, 140, 150, 151, 163, 164, 176, 177, 190, 191, 204, 206, 222, 224, 230, 232, 242, 32, 42, 75, 91, 33, 42, 69, 86, 37, 58, + 84, 91, 49, 71, 103, 110, 65, 84, 125, 128, 80, 97, 142, 152, 91, 100, 145, 178, 104, 112, 146, 190, 32, 33, 37, 49, 65, 80, 91, 104, 42, + 42, 58, 71, 84, 97, 100, 112, 75, 69, 84, 103, 125, 142, 145, 146, 91, 86, 91, 110, 128, 152, 178, 190, 32, 32, 36, 53, 65, 87, 93, 99, + 31, 33, 34, 49, 59, 78, 86, 93, 32, 34, 36, 50, 59, 77, 82, 89, 34, 37, 42, 54, 63, 79, 80, 88, 36, 38, 48, 60, 68, 84, 86, 90, 44, 43, + 53, 71, 79, 95, 94, 97, 48, 46, 56, 76, 85, 102, 105, 105, 58, 54, 63, 87, 98, 116, 112, 115, 65, 58, 68, 92, 105, 124, 122, 124, 79, 70, + 79, 104, 118, 141, 135, 135, 82, 72, 81, 106, 121, 144, 149, 146, 91, 80, 88, 106, 130, 148, 162, 159, 97, 86, 94, 107, 128, 157, 167, + 171, 103, 93, 98, 114, 131, 150, 174, 186, 110, 100, 101, 117, 138, 161, 183, 193, 118, 107, 105, 118, 136, 157, 182, 203, 32, 31, 32, + 34, 36, 44, 48, 58, 65, 79, 82, 91, 97, 103, 110, 118, 32, 33, 34, 37, 38, 43, 46, 54, 58, 70, 72, 80, 86, 93, 100, 107, 36, 34, 36, 42, + 48, 53, 56, 63, 68, 79, 81, 88, 94, 98, 101, 105, 53, 49, 50, 54, 60, 71, 76, 87, 92, 104, 106, 106, 107, 114, 117, 118, 65, 59, 59, 63, + 68, 79, 85, 98, 105, 118, 121, 130, 128, 131, 138, 136, 87, 78, 77, 79, 84, 95, 102, 116, 124, 141, 144, 148, 157, 150, 161, 157, 93, 86, + 82, 80, 86, 94, 105, 112, 122, 135, 149, 162, 167, 174, 183, 182, 99, 93, 89, 88, 90, 97, 105, 115, 124, 135, 146, 159, 171, 186, 193, + 203, 32, 31, 32, 34, 36, 44, 53, 59, 65, 79, 87, 90, 93, 96, 99, 102, 31, 32, 32, 34, 35, 42, 51, 56, 62, 75, 82, 85, 88, 91, 94, 97, 31, + 32, 33, 33, 34, 41, 49, 54, 59, 72, 78, 82, 86, 90, 93, 97, 31, 32, 33, 34, 35, 41, 49, 54, 59, 71, 78, 81, 84, 87, 90, 93, 32, 32, 34, + 35, 36, 42, 50, 54, 59, 71, 77, 80, 82, 86, 89, 93, 32, 33, 35, 37, 38, 42, 49, 53, 58, 69, 75, 78, 82, 86, 89, 92, 34, 34, 37, 39, 42, + 48, 54, 58, 63, 73, 79, 78, 80, 83, 88, 92, 35, 34, 37, 41, 45, 50, 57, 61, 65, 76, 82, 83, 84, 84, 87, 90, 36, 34, 38, 43, 48, 54, 60, + 64, 68, 78, 84, 87, 86, 89, 90, 90, 39, 37, 40, 45, 50, 58, 65, 69, 73, 84, 89, 89, 91, 91, 93, 96, 44, 41, 43, 48, 53, 63, 71, 75, 79, + 90, 95, 93, 94, 95, 97, 97, 46, 43, 44, 49, 55, 65, 73, 78, 82, 93, 98, 100, 98, 100, 99, 103, 48, 45, 46, 51, 56, 67, 76, 80, 85, 96, + 102, 102, 105, 102, 105, 104, 53, 49, 50, 54, 60, 71, 82, 87, 92, 103, 109, 107, 107, 110, 107, 111, 58, 54, 54, 58, 63, 75, 87, 92, 98, + 110, 116, 115, 112, 111, 115, 112, 61, 57, 56, 60, 66, 77, 89, 95, 101, 114, 120, 118, 119, 118, 116, 120, 65, 60, 58, 63, 68, 79, 92, + 98, 105, 118, 124, 123, 122, 123, 124, 121, 71, 65, 63, 68, 73, 84, 97, 103, 111, 125, 132, 132, 130, 128, 127, 130, 79, 72, 70, 74, 79, + 90, 104, 110, 118, 133, 141, 136, 135, 135, 135, 131, 81, 74, 71, 75, 80, 91, 105, 112, 119, 135, 142, 140, 140, 138, 139, 142, 82, 75, + 72, 76, 81, 92, 106, 113, 121, 136, 144, 151, 149, 149, 146, 143, 88, 80, 77, 80, 85, 97, 108, 115, 126, 142, 149, 153, 153, 152, 152, + 154, 91, 83, 80, 81, 88, 100, 106, 114, 130, 142, 148, 155, 162, 160, 159, 155, 94, 85, 83, 82, 91, 100, 105, 118, 131, 137, 153, 160, + 165, 167, 166, 168, 97, 88, 86, 85, 94, 100, 107, 123, 128, 140, 157, 161, 167, 173, 171, 169, 100, 91, 89, 87, 97, 100, 111, 121, 127, + 145, 152, 164, 173, 178, 182, 181, 103, 94, 93, 90, 98, 101, 114, 120, 131, 144, 150, 170, 174, 180, 186, 183, 107, 97, 96, 93, 100, 104, + 117, 119, 136, 142, 155, 168, 177, 187, 191, 198, 110, 101, 100, 97, 101, 108, 117, 123, 138, 141, 161, 165, 183, 188, 193, 200, 114, + 104, 104, 100, 103, 112, 117, 127, 137, 146, 159, 167, 185, 190, 201, 206, 118, 108, 107, 103, 105, 115, 118, 131, 136, 151, 157, 172, + 182, 197, 203, 208, 122, 111, 111, 107, 107, 119, 119, 136, 136, 156, 156, 178, 179, 203, 204, 217, 32, 31, 31, 31, 32, 32, 34, 35, 36, + 39, 44, 46, 48, 53, 58, 61, 65, 71, 79, 81, 82, 88, 91, 94, 97, 100, 103, 107, 110, 114, 118, 122, 31, 32, 32, 32, 32, 33, 34, 34, 34, + 37, 41, 43, 45, 49, 54, 57, 60, 65, 72, 74, 75, 80, 83, 85, 88, 91, 94, 97, 101, 104, 108, 111, 32, 32, 33, 33, 34, 35, 37, 37, 38, 40, + 43, 44, 46, 50, 54, 56, 58, 63, 70, 71, 72, 77, 80, 83, 86, 89, 93, 96, 100, 104, 107, 111, 34, 34, 33, 34, 35, 37, 39, 41, 43, 45, 48, + 49, 51, 54, 58, 60, 63, 68, 74, 75, 76, 80, 81, 82, 85, 87, 90, 93, 97, 100, 103, 107, 36, 35, 34, 35, 36, 38, 42, 45, 48, 50, 53, 55, + 56, 60, 63, 66, 68, 73, 79, 80, 81, 85, 88, 91, 94, 97, 98, 100, 101, 103, 105, 107, 44, 42, 41, 41, 42, 42, 48, 50, 54, 58, 63, 65, 67, + 71, 75, 77, 79, 84, 90, 91, 92, 97, 100, 100, 100, 100, 101, 104, 108, 112, 115, 119, 53, 51, 49, 49, 50, 49, 54, 57, 60, 65, 71, 73, 76, + 82, 87, 89, 92, 97, 104, 105, 106, 108, 106, 105, 107, 111, 114, 117, 117, 117, 118, 119, 59, 56, 54, 54, 54, 53, 58, 61, 64, 69, 75, 78, + 80, 87, 92, 95, 98, 103, 110, 112, 113, 115, 114, 118, 123, 121, 120, 119, 123, 127, 131, 136, 65, 62, 59, 59, 59, 58, 63, 65, 68, 73, + 79, 82, 85, 92, 98, 101, 105, 111, 118, 119, 121, 126, 130, 131, 128, 127, 131, 136, 138, 137, 136, 136, 79, 75, 72, 71, 71, 69, 73, 76, + 78, 84, 90, 93, 96, 103, 110, 114, 118, 125, 133, 135, 136, 142, 142, 137, 140, 145, 144, 142, 141, 146, 151, 156, 87, 82, 78, 78, 77, + 75, 79, 82, 84, 89, 95, 98, 102, 109, 116, 120, 124, 132, 141, 142, 144, 149, 148, 153, 157, 152, 150, 155, 161, 159, 157, 156, 90, 85, + 82, 81, 80, 78, 78, 83, 87, 89, 93, 100, 102, 107, 115, 118, 123, 132, 136, 140, 151, 153, 155, 160, 161, 164, 170, 168, 165, 167, 172, + 178, 93, 88, 86, 84, 82, 82, 80, 84, 86, 91, 94, 98, 105, 107, 112, 119, 122, 130, 135, 140, 149, 153, 162, 165, 167, 173, 174, 177, 183, + 185, 182, 179, 96, 91, 90, 87, 86, 86, 83, 84, 89, 91, 95, 100, 102, 110, 111, 118, 123, 128, 135, 138, 149, 152, 160, 167, 173, 178, + 180, 187, 188, 190, 197, 203, 99, 94, 93, 90, 89, 89, 88, 87, 90, 93, 97, 99, 105, 107, 115, 116, 124, 127, 135, 139, 146, 152, 159, 166, + 171, 182, 186, 191, 193, 201, 203, 204, 102, 97, 97, 93, 93, 92, 92, 90, 90, 96, 97, 103, 104, 111, 112, 120, 121, 130, 131, 142, 143, + 154, 155, 168, 169, 181, 183, 198, 200, 206, 208, 217, 31, 44, 79, 96, 32, 41, 72, 90, 32, 42, 71, 86, 34, 48, 73, 83, 34, 54, 78, 89, + 41, 63, 90, 95, 45, 67, 96, 102, 54, 75, 110, 111, 60, 79, 118, 123, 72, 90, 133, 135, 75, 92, 136, 149, 83, 100, 142, 160, 88, 100, 140, + 173, 94, 101, 144, 180, 101, 108, 141, 188, 108, 115, 151, 197, 31, 32, 32, 34, 34, 41, 45, 54, 60, 72, 75, 83, 88, 94, 101, 108, 44, 41, + 42, 48, 54, 63, 67, 75, 79, 90, 92, 100, 100, 101, 108, 115, 79, 72, 71, 73, 78, 90, 96, 110, 118, 133, 136, 142, 140, 144, 141, 151, 96, + 90, 86, 83, 89, 95, 102, 111, 123, 135, 149, 160, 173, 180, 188, 197, 32, 32, 36, 53, 65, 87, 93, 99, 31, 32, 35, 51, 62, 82, 88, 94, 31, + 33, 34, 49, 59, 78, 86, 93, 31, 33, 35, 49, 59, 78, 84, 90, 32, 34, 36, 50, 59, 77, 82, 89, 32, 35, 38, 49, 58, 75, 82, 89, 34, 37, 42, + 54, 63, 79, 80, 88, 35, 37, 45, 57, 65, 82, 84, 87, 36, 38, 48, 60, 68, 84, 86, 90, 39, 40, 50, 65, 73, 89, 91, 93, 44, 43, 53, 71, 79, + 95, 94, 97, 46, 44, 55, 73, 82, 98, 98, 99, 48, 46, 56, 76, 85, 102, 105, 105, 53, 50, 60, 82, 92, 109, 107, 107, 58, 54, 63, 87, 98, + 116, 112, 115, 61, 56, 66, 89, 101, 120, 119, 116, 65, 58, 68, 92, 105, 124, 122, 124, 71, 63, 73, 97, 111, 132, 130, 127, 79, 70, 79, + 104, 118, 141, 135, 135, 81, 71, 80, 105, 119, 142, 140, 139, 82, 72, 81, 106, 121, 144, 149, 146, 88, 77, 85, 108, 126, 149, 153, 152, + 91, 80, 88, 106, 130, 148, 162, 159, 94, 83, 91, 105, 131, 153, 165, 166, 97, 86, 94, 107, 128, 157, 167, 171, 100, 89, 97, 111, 127, + 152, 173, 182, 103, 93, 98, 114, 131, 150, 174, 186, 107, 96, 100, 117, 136, 155, 177, 191, 110, 100, 101, 117, 138, 161, 183, 193, 114, + 104, 103, 117, 137, 159, 185, 201, 118, 107, 105, 118, 136, 157, 182, 203, 122, 111, 107, 119, 136, 156, 179, 204, 32, 31, 31, 31, 32, + 32, 34, 35, 36, 39, 44, 46, 48, 53, 58, 61, 65, 71, 79, 81, 82, 88, 91, 94, 97, 100, 103, 107, 110, 114, 118, 122, 32, 32, 33, 33, 34, + 35, 37, 37, 38, 40, 43, 44, 46, 50, 54, 56, 58, 63, 70, 71, 72, 77, 80, 83, 86, 89, 93, 96, 100, 104, 107, 111, 36, 35, 34, 35, 36, 38, + 42, 45, 48, 50, 53, 55, 56, 60, 63, 66, 68, 73, 79, 80, 81, 85, 88, 91, 94, 97, 98, 100, 101, 103, 105, 107, 53, 51, 49, 49, 50, 49, 54, + 57, 60, 65, 71, 73, 76, 82, 87, 89, 92, 97, 104, 105, 106, 108, 106, 105, 107, 111, 114, 117, 117, 117, 118, 119, 65, 62, 59, 59, 59, 58, + 63, 65, 68, 73, 79, 82, 85, 92, 98, 101, 105, 111, 118, 119, 121, 126, 130, 131, 128, 127, 131, 136, 138, 137, 136, 136, 87, 82, 78, 78, + 77, 75, 79, 82, 84, 89, 95, 98, 102, 109, 116, 120, 124, 132, 141, 142, 144, 149, 148, 153, 157, 152, 150, 155, 161, 159, 157, 156, 93, + 88, 86, 84, 82, 82, 80, 84, 86, 91, 94, 98, 105, 107, 112, 119, 122, 130, 135, 140, 149, 153, 162, 165, 167, 173, 174, 177, 183, 185, + 182, 179, 99, 94, 93, 90, 89, 89, 88, 87, 90, 93, 97, 99, 105, 107, 115, 116, 124, 127, 135, 139, 146, 152, 159, 166, 171, 182, 186, 191, + 193, 201, 203, 204, + ], + [ + 35, 46, 57, 66, 46, 60, 69, 71, 57, 69, 90, 90, 66, 71, 90, 109, 31, 38, 47, 50, 57, 63, 67, 71, 38, 47, 46, 47, 52, 57, 62, 67, 47, 46, + 54, 57, 61, 66, 67, 68, 50, 47, 57, 66, 72, 77, 75, 75, 57, 52, 61, 72, 82, 88, 86, 84, 63, 57, 66, 77, 88, 96, 95, 95, 67, 62, 67, 75, + 86, 95, 104, 107, 71, 67, 68, 75, 84, 95, 107, 113, 32, 30, 33, 41, 49, 49, 50, 54, 57, 63, 65, 68, 70, 72, 74, 76, 30, 32, 35, 42, 46, + 45, 46, 49, 52, 57, 58, 62, 64, 67, 70, 72, 33, 35, 39, 45, 47, 45, 46, 49, 51, 56, 57, 60, 62, 64, 66, 69, 41, 42, 45, 48, 50, 49, 50, + 52, 53, 57, 58, 59, 60, 61, 64, 67, 49, 46, 47, 50, 53, 53, 54, 55, 56, 60, 61, 64, 64, 65, 66, 66, 49, 45, 45, 49, 53, 58, 60, 62, 63, + 67, 68, 67, 69, 68, 70, 70, 50, 46, 46, 50, 54, 60, 61, 65, 67, 71, 71, 74, 73, 73, 74, 74, 54, 49, 49, 52, 55, 62, 65, 71, 73, 78, 79, + 78, 77, 78, 78, 78, 57, 52, 51, 53, 56, 63, 67, 73, 76, 82, 83, 84, 84, 84, 82, 83, 63, 57, 56, 57, 60, 67, 71, 78, 82, 89, 90, 90, 89, + 88, 87, 88, 65, 58, 57, 58, 61, 68, 71, 79, 83, 90, 91, 94, 93, 93, 92, 93, 68, 62, 60, 59, 64, 67, 74, 78, 84, 90, 94, 98, 99, 98, 98, + 98, 70, 64, 62, 60, 64, 69, 73, 77, 84, 89, 93, 99, 102, 103, 104, 104, 72, 67, 64, 61, 65, 68, 73, 78, 84, 88, 93, 98, 103, 106, 108, + 109, 74, 70, 66, 64, 66, 70, 74, 78, 82, 87, 92, 98, 104, 108, 111, 112, 76, 72, 69, 67, 66, 70, 74, 78, 83, 88, 93, 98, 104, 109, 112, + 116, 32, 31, 30, 32, 33, 36, 41, 45, 49, 48, 49, 50, 50, 52, 54, 56, 57, 60, 63, 64, 65, 67, 68, 69, 70, 71, 72, 73, 74, 75, 76, 78, 31, + 31, 31, 33, 34, 38, 42, 45, 47, 47, 47, 47, 48, 50, 52, 53, 54, 57, 60, 61, 61, 63, 64, 65, 66, 67, 68, 69, 70, 71, 72, 74, 30, 31, 32, + 33, 35, 40, 42, 44, 46, 45, 45, 45, 46, 47, 49, 51, 52, 54, 57, 58, 58, 61, 62, 63, 64, 66, 67, 68, 70, 71, 72, 74, 32, 33, 33, 35, 37, + 41, 43, 45, 47, 46, 45, 46, 46, 47, 49, 50, 51, 54, 57, 57, 58, 60, 61, 62, 63, 64, 65, 66, 67, 68, 69, 70, 33, 34, 35, 37, 39, 43, 45, + 46, 47, 46, 45, 46, 46, 47, 49, 50, 51, 53, 56, 57, 57, 59, 60, 61, 62, 63, 64, 65, 66, 68, 69, 70, 36, 38, 40, 41, 43, 47, 47, 47, 48, + 46, 45, 46, 46, 47, 48, 49, 50, 52, 54, 55, 55, 57, 58, 59, 61, 62, 64, 65, 66, 67, 68, 69, 41, 42, 42, 43, 45, 47, 48, 49, 50, 49, 49, + 49, 50, 50, 52, 52, 53, 55, 57, 58, 58, 60, 59, 59, 60, 61, 61, 63, 64, 66, 67, 69, 45, 45, 44, 45, 46, 47, 49, 50, 51, 51, 51, 51, 52, + 52, 53, 54, 55, 57, 59, 59, 60, 61, 61, 62, 63, 63, 63, 63, 63, 64, 65, 66, 49, 47, 46, 47, 47, 48, 50, 51, 53, 53, 53, 54, 54, 54, 55, + 56, 56, 58, 60, 61, 61, 63, 64, 64, 64, 64, 65, 66, 66, 66, 66, 66, 48, 47, 45, 46, 46, 46, 49, 51, 53, 54, 55, 56, 56, 57, 58, 59, 60, + 61, 63, 64, 64, 66, 66, 65, 66, 67, 67, 67, 67, 68, 69, 70, 49, 47, 45, 45, 45, 45, 49, 51, 53, 55, 58, 59, 60, 61, 62, 63, 63, 65, 67, + 67, 68, 69, 67, 68, 69, 68, 68, 69, 70, 70, 70, 70, 50, 47, 45, 46, 46, 46, 49, 51, 54, 56, 59, 60, 60, 62, 64, 64, 65, 67, 69, 69, 70, + 70, 71, 71, 70, 70, 71, 71, 71, 71, 72, 74, 50, 48, 46, 46, 46, 46, 50, 52, 54, 56, 60, 60, 61, 63, 65, 66, 67, 68, 71, 71, 71, 73, 74, + 72, 73, 74, 73, 73, 74, 74, 74, 74, 52, 50, 47, 47, 47, 47, 50, 52, 54, 57, 61, 62, 63, 66, 68, 69, 70, 72, 75, 75, 75, 77, 75, 75, 76, + 75, 75, 76, 75, 75, 76, 77, 54, 52, 49, 49, 49, 48, 52, 53, 55, 58, 62, 64, 65, 68, 71, 72, 73, 75, 78, 78, 79, 79, 78, 79, 77, 78, 78, + 77, 78, 79, 78, 78, 56, 53, 51, 50, 50, 49, 52, 54, 56, 59, 63, 64, 66, 69, 72, 73, 75, 77, 80, 80, 81, 81, 82, 80, 81, 81, 79, 81, 80, + 79, 81, 82, 57, 54, 52, 51, 51, 50, 53, 55, 56, 60, 63, 65, 67, 70, 73, 75, 76, 79, 82, 82, 83, 85, 84, 83, 84, 83, 84, 82, 82, 84, 83, + 82, 60, 57, 54, 54, 53, 52, 55, 57, 58, 61, 65, 67, 68, 72, 75, 77, 79, 82, 85, 85, 86, 88, 86, 87, 85, 86, 85, 85, 86, 84, 85, 86, 63, + 60, 57, 57, 56, 54, 57, 59, 60, 63, 67, 69, 71, 75, 78, 80, 82, 85, 89, 89, 90, 90, 90, 89, 89, 88, 88, 88, 87, 88, 88, 87, 64, 61, 58, + 57, 57, 55, 58, 59, 61, 64, 67, 69, 71, 75, 78, 80, 82, 85, 89, 90, 91, 92, 93, 92, 92, 91, 91, 90, 91, 90, 90, 92, 65, 61, 58, 58, 57, + 55, 58, 60, 61, 64, 68, 70, 71, 75, 79, 81, 83, 86, 90, 91, 91, 94, 94, 96, 93, 94, 93, 94, 92, 93, 93, 92, 67, 63, 61, 60, 59, 57, 60, + 61, 63, 66, 69, 70, 73, 77, 79, 81, 85, 88, 90, 92, 94, 96, 96, 97, 98, 95, 97, 95, 96, 95, 95, 96, 68, 64, 62, 61, 60, 58, 59, 61, 64, + 66, 67, 71, 74, 75, 78, 82, 84, 86, 90, 93, 94, 96, 98, 98, 99, 100, 98, 99, 98, 98, 98, 97, 69, 65, 63, 62, 61, 59, 59, 62, 64, 65, 68, + 71, 72, 75, 79, 80, 83, 87, 89, 92, 96, 97, 98, 100, 100, 101, 102, 101, 101, 101, 100, 102, 70, 66, 64, 63, 62, 61, 60, 63, 64, 66, 69, + 70, 73, 76, 77, 81, 84, 85, 89, 92, 93, 98, 99, 100, 102, 102, 103, 104, 104, 103, 104, 102, 71, 67, 66, 64, 63, 62, 61, 63, 64, 67, 68, + 70, 74, 75, 78, 81, 83, 86, 88, 91, 94, 95, 100, 101, 102, 104, 104, 105, 106, 107, 105, 107, 72, 68, 67, 65, 64, 64, 61, 63, 65, 67, 68, + 71, 73, 75, 78, 79, 84, 85, 88, 91, 93, 97, 98, 102, 103, 104, 106, 106, 108, 108, 109, 107, 73, 69, 68, 66, 65, 65, 63, 63, 66, 67, 69, + 71, 73, 76, 77, 81, 82, 85, 88, 90, 94, 95, 99, 101, 104, 105, 106, 109, 108, 110, 111, 112, 74, 70, 70, 67, 66, 66, 64, 63, 66, 67, 70, + 71, 74, 75, 78, 80, 82, 86, 87, 91, 92, 96, 98, 101, 104, 106, 108, 108, 111, 111, 112, 113, 75, 71, 71, 68, 68, 67, 66, 64, 66, 68, 70, + 71, 74, 75, 79, 79, 84, 84, 88, 90, 93, 95, 98, 101, 103, 107, 108, 110, 111, 113, 113, 115, 76, 72, 72, 69, 69, 68, 67, 65, 66, 69, 70, + 72, 74, 76, 78, 81, 83, 85, 88, 90, 93, 95, 98, 100, 104, 105, 109, 111, 112, 113, 116, 115, 78, 74, 74, 70, 70, 69, 69, 66, 66, 70, 70, + 74, 74, 77, 78, 82, 82, 86, 87, 92, 92, 96, 97, 102, 102, 107, 107, 112, 113, 115, 115, 118, 31, 47, 60, 66, 40, 45, 54, 61, 46, 56, 64, + 64, 48, 61, 75, 73, 54, 65, 85, 82, 61, 69, 92, 92, 64, 68, 90, 102, 68, 71, 87, 105, 31, 40, 46, 48, 54, 61, 64, 68, 47, 45, 56, 61, 65, + 69, 68, 71, 60, 54, 64, 75, 85, 92, 90, 87, 66, 61, 64, 73, 82, 92, 102, 105, 32, 37, 48, 52, 57, 66, 68, 71, 30, 40, 46, 48, 52, 60, 63, + 66, 33, 43, 47, 47, 51, 59, 60, 63, 42, 47, 50, 50, 53, 60, 59, 62, 49, 48, 53, 54, 57, 62, 62, 62, 49, 46, 53, 61, 64, 69, 66, 66, 50, + 46, 54, 64, 67, 73, 72, 70, 54, 49, 55, 68, 73, 80, 76, 75, 57, 50, 56, 70, 76, 84, 80, 79, 63, 55, 60, 75, 82, 92, 87, 84, 64, 56, 61, + 75, 83, 93, 93, 89, 68, 59, 64, 74, 86, 94, 98, 94, 70, 62, 66, 73, 83, 96, 99, 98, 72, 64, 66, 75, 83, 92, 101, 104, 74, 67, 66, 74, 84, + 94, 103, 106, 76, 69, 67, 73, 82, 91, 101, 109, 32, 30, 33, 42, 49, 49, 50, 54, 57, 63, 64, 68, 70, 72, 74, 76, 37, 40, 43, 47, 48, 46, + 46, 49, 50, 55, 56, 59, 62, 64, 67, 69, 48, 46, 47, 50, 53, 53, 54, 55, 56, 60, 61, 64, 66, 66, 66, 67, 52, 48, 47, 50, 54, 61, 64, 68, + 70, 75, 75, 74, 73, 75, 74, 73, 57, 52, 51, 53, 57, 64, 67, 73, 76, 82, 83, 86, 83, 83, 84, 82, 66, 60, 59, 60, 62, 69, 73, 80, 84, 92, + 93, 94, 96, 92, 94, 91, 68, 63, 60, 59, 62, 66, 72, 76, 80, 87, 93, 98, 99, 101, 103, 101, 71, 66, 63, 62, 62, 66, 70, 75, 79, 84, 89, + 94, 98, 104, 106, 109, 32, 31, 37, 42, 48, 49, 52, 54, 57, 63, 66, 67, 68, 69, 71, 72, 31, 31, 38, 42, 47, 47, 50, 52, 54, 60, 63, 64, + 65, 66, 67, 68, 30, 32, 40, 42, 46, 45, 48, 50, 52, 57, 60, 62, 63, 65, 66, 68, 32, 34, 41, 44, 46, 45, 48, 49, 51, 57, 59, 61, 62, 63, + 64, 65, 33, 36, 43, 45, 47, 46, 47, 49, 51, 56, 59, 60, 60, 62, 63, 65, 37, 40, 47, 47, 47, 45, 47, 48, 50, 54, 57, 58, 60, 61, 62, 63, + 42, 43, 47, 48, 50, 49, 50, 52, 53, 57, 60, 58, 59, 60, 62, 63, 45, 44, 47, 49, 51, 51, 52, 54, 55, 59, 61, 61, 61, 60, 61, 61, 49, 46, + 48, 50, 53, 53, 54, 55, 57, 60, 62, 63, 62, 63, 62, 62, 48, 46, 47, 50, 53, 56, 57, 59, 60, 64, 66, 65, 65, 64, 64, 65, 49, 45, 46, 49, + 53, 58, 61, 62, 64, 67, 69, 67, 66, 66, 66, 65, 49, 46, 46, 49, 53, 59, 62, 64, 65, 69, 71, 70, 68, 68, 67, 68, 50, 46, 46, 50, 54, 59, + 64, 65, 67, 71, 73, 72, 72, 70, 70, 69, 52, 48, 47, 50, 54, 61, 66, 68, 71, 75, 77, 74, 73, 73, 71, 72, 54, 50, 49, 52, 55, 62, 68, 71, + 73, 78, 80, 78, 76, 74, 75, 73, 55, 51, 49, 52, 56, 63, 69, 72, 75, 80, 82, 80, 79, 78, 76, 77, 57, 52, 50, 53, 56, 64, 70, 73, 76, 82, + 84, 82, 80, 80, 79, 77, 60, 54, 52, 55, 58, 65, 72, 75, 79, 85, 88, 86, 84, 82, 81, 81, 63, 57, 55, 58, 60, 67, 75, 78, 82, 89, 92, 88, + 87, 85, 84, 81, 64, 58, 55, 58, 61, 68, 75, 78, 82, 89, 92, 90, 89, 87, 86, 86, 64, 59, 56, 58, 61, 68, 75, 79, 83, 90, 93, 95, 93, 91, + 89, 87, 67, 61, 58, 60, 63, 69, 76, 79, 85, 92, 95, 96, 94, 92, 91, 91, 68, 62, 59, 60, 64, 71, 74, 78, 86, 91, 94, 96, 98, 96, 94, 91, + 69, 62, 60, 60, 65, 70, 72, 79, 85, 88, 95, 98, 99, 98, 97, 96, 70, 63, 62, 60, 66, 69, 73, 81, 83, 89, 96, 97, 99, 101, 98, 97, 71, 64, + 63, 61, 67, 68, 74, 79, 82, 90, 93, 98, 102, 102, 102, 101, 72, 65, 64, 62, 66, 68, 75, 78, 83, 89, 92, 100, 101, 103, 104, 102, 73, 66, + 65, 63, 66, 69, 75, 76, 84, 87, 93, 98, 102, 105, 106, 107, 74, 67, 67, 64, 66, 70, 74, 77, 84, 86, 94, 96, 103, 105, 106, 107, 75, 68, + 68, 65, 66, 71, 74, 78, 83, 87, 93, 96, 103, 105, 109, 109, 76, 69, 69, 66, 67, 72, 73, 80, 82, 88, 91, 97, 101, 107, 109, 110, 77, 70, + 70, 67, 67, 73, 73, 81, 81, 90, 90, 99, 99, 108, 108, 113, 32, 31, 30, 32, 33, 37, 42, 45, 49, 48, 49, 49, 50, 52, 54, 55, 57, 60, 63, + 64, 64, 67, 68, 69, 70, 71, 72, 73, 74, 75, 76, 77, 31, 31, 32, 34, 36, 40, 43, 44, 46, 46, 45, 46, 46, 48, 50, 51, 52, 54, 57, 58, 59, + 61, 62, 62, 63, 64, 65, 66, 67, 68, 69, 70, 37, 38, 40, 41, 43, 47, 47, 47, 48, 47, 46, 46, 46, 47, 49, 49, 50, 52, 55, 55, 56, 58, 59, + 60, 62, 63, 64, 65, 67, 68, 69, 70, 42, 42, 42, 44, 45, 47, 48, 49, 50, 50, 49, 49, 50, 50, 52, 52, 53, 55, 58, 58, 58, 60, 60, 60, 60, + 61, 62, 63, 64, 65, 66, 67, 48, 47, 46, 46, 47, 47, 50, 51, 53, 53, 53, 53, 54, 54, 55, 56, 56, 58, 60, 61, 61, 63, 64, 65, 66, 67, 66, + 66, 66, 66, 67, 67, 49, 47, 45, 45, 46, 45, 49, 51, 53, 56, 58, 59, 59, 61, 62, 63, 64, 65, 67, 68, 68, 69, 71, 70, 69, 68, 68, 69, 70, + 71, 72, 73, 52, 50, 48, 48, 47, 47, 50, 52, 54, 57, 61, 62, 64, 66, 68, 69, 70, 72, 75, 75, 75, 76, 74, 72, 73, 74, 75, 75, 74, 74, 73, + 73, 54, 52, 50, 49, 49, 48, 52, 54, 55, 59, 62, 64, 65, 68, 71, 72, 73, 75, 78, 78, 79, 79, 78, 79, 81, 79, 78, 76, 77, 78, 80, 81, 57, + 54, 52, 51, 51, 50, 53, 55, 57, 60, 64, 65, 67, 71, 73, 75, 76, 79, 82, 82, 83, 85, 86, 85, 83, 82, 83, 84, 84, 83, 82, 81, 63, 60, 57, + 57, 56, 54, 57, 59, 60, 64, 67, 69, 71, 75, 78, 80, 82, 85, 89, 89, 90, 92, 91, 88, 89, 90, 89, 87, 86, 87, 88, 90, 66, 63, 60, 59, 59, + 57, 60, 61, 62, 66, 69, 71, 73, 77, 80, 82, 84, 88, 92, 92, 93, 95, 94, 95, 96, 93, 92, 93, 94, 93, 91, 90, 67, 64, 62, 61, 60, 58, 58, + 61, 63, 65, 67, 70, 72, 74, 78, 80, 82, 86, 88, 90, 95, 96, 96, 98, 97, 98, 100, 98, 96, 96, 97, 99, 68, 65, 63, 62, 60, 60, 59, 61, 62, + 65, 66, 68, 72, 73, 76, 79, 80, 84, 87, 89, 93, 94, 98, 99, 99, 102, 101, 102, 103, 103, 101, 99, 69, 66, 65, 63, 62, 61, 60, 60, 63, 64, + 66, 68, 70, 73, 74, 78, 80, 82, 85, 87, 91, 92, 96, 98, 101, 102, 103, 105, 105, 105, 107, 108, 71, 67, 66, 64, 63, 62, 62, 61, 62, 64, + 66, 67, 70, 71, 75, 76, 79, 81, 84, 86, 89, 91, 94, 97, 98, 102, 104, 106, 106, 109, 109, 108, 72, 68, 68, 65, 65, 63, 63, 61, 62, 65, + 65, 68, 69, 72, 73, 77, 77, 81, 81, 86, 87, 91, 91, 96, 97, 101, 102, 107, 107, 109, 110, 113, 31, 49, 63, 69, 32, 45, 57, 65, 36, 46, + 56, 62, 43, 49, 57, 60, 46, 53, 60, 63, 45, 58, 67, 66, 46, 59, 71, 70, 50, 62, 78, 74, 52, 64, 82, 80, 57, 67, 89, 85, 59, 68, 90, 91, + 62, 71, 91, 96, 63, 69, 89, 101, 65, 68, 89, 103, 67, 70, 86, 105, 69, 72, 88, 107, 31, 32, 36, 43, 46, 45, 46, 50, 52, 57, 59, 62, 63, + 65, 67, 69, 49, 45, 46, 49, 53, 58, 59, 62, 64, 67, 68, 71, 69, 68, 70, 72, 63, 57, 56, 57, 60, 67, 71, 78, 82, 89, 90, 91, 89, 89, 86, + 88, 69, 65, 62, 60, 63, 66, 70, 74, 80, 85, 91, 96, 101, 103, 105, 107, 32, 37, 48, 52, 57, 66, 68, 71, 31, 38, 47, 50, 54, 63, 65, 67, + 30, 40, 46, 48, 52, 60, 63, 66, 32, 41, 46, 48, 51, 59, 62, 64, 33, 43, 47, 47, 51, 59, 60, 63, 37, 47, 47, 47, 50, 57, 60, 62, 42, 47, + 50, 50, 53, 60, 59, 62, 45, 47, 51, 52, 55, 61, 61, 61, 49, 48, 53, 54, 57, 62, 62, 62, 48, 47, 53, 57, 60, 66, 65, 64, 49, 46, 53, 61, + 64, 69, 66, 66, 49, 46, 53, 62, 65, 71, 68, 67, 50, 46, 54, 64, 67, 73, 72, 70, 52, 47, 54, 66, 71, 77, 73, 71, 54, 49, 55, 68, 73, 80, + 76, 75, 55, 49, 56, 69, 75, 82, 79, 76, 57, 50, 56, 70, 76, 84, 80, 79, 60, 52, 58, 72, 79, 88, 84, 81, 63, 55, 60, 75, 82, 92, 87, 84, + 64, 55, 61, 75, 82, 92, 89, 86, 64, 56, 61, 75, 83, 93, 93, 89, 67, 58, 63, 76, 85, 95, 94, 91, 68, 59, 64, 74, 86, 94, 98, 94, 69, 60, + 65, 72, 85, 95, 99, 97, 70, 62, 66, 73, 83, 96, 99, 98, 71, 63, 67, 74, 82, 93, 102, 102, 72, 64, 66, 75, 83, 92, 101, 104, 73, 65, 66, + 75, 84, 93, 102, 106, 74, 67, 66, 74, 84, 94, 103, 106, 75, 68, 66, 74, 83, 93, 103, 109, 76, 69, 67, 73, 82, 91, 101, 109, 77, 70, 67, + 73, 81, 90, 99, 108, 32, 31, 30, 32, 33, 37, 42, 45, 49, 48, 49, 49, 50, 52, 54, 55, 57, 60, 63, 64, 64, 67, 68, 69, 70, 71, 72, 73, 74, + 75, 76, 77, 37, 38, 40, 41, 43, 47, 47, 47, 48, 47, 46, 46, 46, 47, 49, 49, 50, 52, 55, 55, 56, 58, 59, 60, 62, 63, 64, 65, 67, 68, 69, + 70, 48, 47, 46, 46, 47, 47, 50, 51, 53, 53, 53, 53, 54, 54, 55, 56, 56, 58, 60, 61, 61, 63, 64, 65, 66, 67, 66, 66, 66, 66, 67, 67, 52, + 50, 48, 48, 47, 47, 50, 52, 54, 57, 61, 62, 64, 66, 68, 69, 70, 72, 75, 75, 75, 76, 74, 72, 73, 74, 75, 75, 74, 74, 73, 73, 57, 54, 52, + 51, 51, 50, 53, 55, 57, 60, 64, 65, 67, 71, 73, 75, 76, 79, 82, 82, 83, 85, 86, 85, 83, 82, 83, 84, 84, 83, 82, 81, 66, 63, 60, 59, 59, + 57, 60, 61, 62, 66, 69, 71, 73, 77, 80, 82, 84, 88, 92, 92, 93, 95, 94, 95, 96, 93, 92, 93, 94, 93, 91, 90, 68, 65, 63, 62, 60, 60, 59, + 61, 62, 65, 66, 68, 72, 73, 76, 79, 80, 84, 87, 89, 93, 94, 98, 99, 99, 102, 101, 102, 103, 103, 101, 99, 71, 67, 66, 64, 63, 62, 62, 61, + 62, 64, 66, 67, 70, 71, 75, 76, 79, 81, 84, 86, 89, 91, 94, 97, 98, 102, 104, 106, 106, 109, 109, 108, + ], + ], + [ + [ + 32, 41, 69, 92, 41, 63, 88, 103, 69, 88, 127, 140, 92, 103, 140, 184, 32, 32, 37, 47, 62, 78, 90, 102, 32, 35, 39, 46, 58, 72, 84, 96, + 37, 39, 51, 60, 71, 84, 93, 100, 47, 46, 60, 73, 87, 100, 106, 113, 62, 58, 71, 87, 105, 121, 129, 132, 78, 72, 84, 100, 121, 140, 148, + 155, 90, 84, 93, 106, 129, 148, 169, 183, 102, 96, 100, 113, 132, 155, 183, 201, 32, 31, 31, 32, 36, 39, 47, 54, 61, 71, 80, 86, 92, 98, + 104, 111, 31, 32, 32, 33, 34, 37, 44, 50, 56, 65, 73, 79, 85, 91, 98, 105, 31, 32, 33, 34, 36, 39, 45, 50, 56, 64, 71, 77, 82, 88, 94, + 100, 32, 33, 34, 36, 40, 42, 47, 51, 57, 65, 71, 76, 80, 85, 91, 98, 36, 34, 36, 40, 48, 50, 56, 60, 65, 73, 79, 84, 86, 90, 95, 98, 39, + 37, 39, 42, 50, 54, 60, 65, 70, 78, 84, 89, 95, 96, 102, 105, 47, 44, 45, 47, 56, 60, 69, 75, 81, 89, 95, 100, 102, 104, 109, 112, 54, + 50, 50, 51, 60, 65, 75, 82, 89, 97, 104, 109, 110, 114, 117, 121, 61, 56, 56, 57, 65, 70, 81, 89, 97, 106, 113, 119, 122, 126, 125, 130, + 71, 65, 64, 65, 73, 78, 89, 97, 106, 117, 125, 131, 134, 134, 136, 141, 80, 73, 71, 71, 79, 84, 95, 104, 113, 125, 134, 140, 142, 145, + 146, 152, 86, 79, 77, 76, 84, 89, 100, 109, 119, 131, 140, 147, 154, 157, 160, 165, 92, 85, 82, 80, 86, 95, 102, 110, 122, 134, 142, 154, + 162, 168, 174, 178, 98, 91, 88, 85, 90, 96, 104, 114, 126, 134, 145, 157, 168, 176, 184, 193, 104, 98, 94, 91, 95, 102, 109, 117, 125, + 136, 146, 160, 174, 184, 193, 201, 111, 105, 100, 98, 98, 105, 112, 121, 130, 141, 152, 165, 178, 193, 201, 210, 32, 31, 31, 31, 31, 32, + 32, 34, 36, 38, 39, 44, 47, 49, 54, 59, 61, 65, 71, 76, 80, 83, 86, 89, 92, 95, 98, 101, 104, 108, 111, 114, 31, 32, 32, 32, 32, 32, 33, + 34, 35, 37, 38, 42, 45, 47, 51, 56, 58, 62, 68, 72, 76, 78, 82, 85, 88, 90, 93, 96, 99, 102, 105, 109, 31, 32, 32, 32, 32, 32, 33, 33, + 34, 36, 37, 41, 44, 46, 50, 54, 56, 60, 65, 70, 73, 76, 79, 82, 85, 88, 91, 95, 98, 101, 105, 109, 31, 32, 32, 32, 32, 33, 33, 34, 35, + 36, 38, 41, 44, 45, 49, 54, 56, 59, 65, 69, 72, 75, 78, 81, 84, 86, 89, 92, 95, 98, 101, 104, 31, 32, 32, 32, 33, 34, 34, 35, 36, 38, 39, + 42, 45, 46, 50, 54, 56, 59, 64, 68, 71, 74, 77, 79, 82, 85, 88, 91, 94, 97, 100, 104, 32, 32, 32, 33, 34, 35, 36, 37, 38, 39, 40, 42, 45, + 46, 49, 53, 55, 58, 63, 66, 69, 72, 74, 78, 81, 84, 87, 90, 93, 96, 99, 102, 32, 33, 33, 33, 34, 36, 36, 38, 40, 41, 42, 44, 47, 48, 51, + 55, 57, 60, 65, 68, 71, 73, 76, 78, 80, 82, 85, 88, 91, 95, 98, 102, 34, 34, 33, 34, 35, 37, 38, 39, 42, 44, 45, 47, 50, 51, 54, 58, 60, + 63, 68, 71, 74, 76, 79, 82, 85, 86, 87, 88, 90, 93, 96, 99, 36, 35, 34, 35, 36, 38, 40, 42, 48, 50, 50, 54, 56, 57, 60, 64, 65, 68, 73, + 76, 79, 81, 84, 86, 86, 88, 90, 93, 95, 97, 98, 100, 38, 37, 36, 36, 38, 39, 41, 44, 50, 51, 52, 56, 58, 60, 63, 67, 68, 71, 76, 79, 82, + 84, 87, 87, 90, 93, 94, 95, 96, 100, 103, 106, 39, 38, 37, 38, 39, 40, 42, 45, 50, 52, 54, 58, 60, 62, 65, 69, 70, 73, 78, 81, 84, 86, + 89, 92, 95, 95, 96, 99, 102, 104, 105, 106, 44, 42, 41, 41, 42, 42, 44, 47, 54, 56, 58, 63, 66, 68, 71, 75, 77, 79, 84, 88, 90, 92, 95, + 97, 97, 99, 102, 103, 103, 106, 109, 113, 47, 45, 44, 44, 45, 45, 47, 50, 56, 58, 60, 66, 69, 71, 75, 79, 81, 84, 89, 92, 95, 97, 100, + 100, 102, 105, 104, 106, 109, 111, 112, 113, 49, 47, 46, 45, 46, 46, 48, 51, 57, 60, 62, 68, 71, 73, 77, 81, 83, 87, 92, 95, 98, 100, + 103, 105, 107, 106, 109, 112, 112, 113, 117, 120, 54, 51, 50, 49, 50, 49, 51, 54, 60, 63, 65, 71, 75, 77, 82, 87, 89, 92, 97, 101, 104, + 106, 109, 112, 110, 113, 114, 114, 117, 121, 121, 121, 59, 56, 54, 54, 54, 53, 55, 58, 64, 67, 69, 75, 79, 81, 87, 92, 94, 98, 103, 107, + 110, 113, 116, 114, 117, 118, 117, 121, 122, 122, 125, 129, 61, 58, 56, 56, 56, 55, 57, 60, 65, 68, 70, 77, 81, 83, 89, 94, 97, 101, 106, + 110, 113, 116, 119, 120, 122, 121, 126, 124, 125, 130, 130, 130, 65, 62, 60, 59, 59, 58, 60, 63, 68, 71, 73, 79, 84, 87, 92, 98, 101, + 105, 111, 115, 118, 121, 124, 128, 125, 129, 128, 131, 133, 132, 135, 139, 71, 68, 65, 65, 64, 63, 65, 68, 73, 76, 78, 84, 89, 92, 97, + 103, 106, 111, 117, 122, 125, 128, 131, 131, 134, 132, 134, 136, 136, 140, 141, 140, 76, 72, 70, 69, 68, 66, 68, 71, 76, 79, 81, 88, 92, + 95, 101, 107, 110, 115, 122, 127, 130, 133, 136, 136, 138, 139, 141, 140, 145, 143, 146, 151, 80, 76, 73, 72, 71, 69, 71, 74, 79, 82, 84, + 90, 95, 98, 104, 110, 113, 118, 125, 130, 134, 137, 140, 146, 142, 146, 145, 149, 146, 150, 152, 151, 83, 78, 76, 75, 74, 72, 73, 76, 81, + 84, 86, 92, 97, 100, 106, 113, 116, 121, 128, 133, 137, 140, 144, 147, 152, 148, 154, 151, 156, 155, 156, 162, 86, 82, 79, 78, 77, 74, + 76, 79, 84, 87, 89, 95, 100, 103, 109, 116, 119, 124, 131, 136, 140, 144, 147, 150, 154, 159, 157, 160, 160, 162, 165, 162, 89, 85, 82, + 81, 79, 78, 78, 82, 86, 87, 92, 97, 100, 105, 112, 114, 120, 128, 131, 136, 146, 147, 150, 155, 156, 161, 166, 165, 167, 169, 169, 175, + 92, 88, 85, 84, 82, 81, 80, 85, 86, 90, 95, 97, 102, 107, 110, 117, 122, 125, 134, 138, 142, 152, 154, 156, 162, 163, 168, 173, 174, 174, + 178, 176, 95, 90, 88, 86, 85, 84, 82, 86, 88, 93, 95, 99, 105, 106, 113, 118, 121, 129, 132, 139, 146, 148, 159, 161, 163, 169, 170, 176, + 180, 183, 181, 187, 98, 93, 91, 89, 88, 87, 85, 87, 90, 94, 96, 102, 104, 109, 114, 117, 126, 128, 134, 141, 145, 154, 157, 166, 168, + 170, 176, 178, 184, 188, 193, 188, 101, 96, 95, 92, 91, 90, 88, 88, 93, 95, 99, 103, 106, 112, 114, 121, 124, 131, 136, 140, 149, 151, + 160, 165, 173, 176, 178, 184, 186, 192, 196, 203, 104, 99, 98, 95, 94, 93, 91, 90, 95, 96, 102, 103, 109, 112, 117, 122, 125, 133, 136, + 145, 146, 156, 160, 167, 174, 180, 184, 186, 193, 194, 201, 204, 108, 102, 101, 98, 97, 96, 95, 93, 97, 100, 104, 106, 111, 113, 121, + 122, 130, 132, 140, 143, 150, 155, 162, 169, 174, 183, 188, 192, 194, 201, 202, 210, 111, 105, 105, 101, 100, 99, 98, 96, 98, 103, 105, + 109, 112, 117, 121, 125, 130, 135, 141, 146, 152, 156, 165, 169, 178, 181, 193, 196, 201, 202, 210, 211, 114, 109, 109, 104, 104, 102, + 102, 99, 100, 106, 106, 113, 113, 120, 121, 129, 130, 139, 140, 151, 151, 162, 162, 175, 176, 187, 188, 203, 204, 210, 211, 219, 32, 42, + 69, 88, 33, 42, 64, 83, 36, 56, 77, 88, 46, 67, 93, 105, 60, 79, 112, 122, 75, 92, 130, 144, 86, 95, 136, 167, 98, 105, 136, 177, 32, 33, + 36, 46, 60, 75, 86, 98, 42, 42, 56, 67, 79, 92, 95, 105, 69, 64, 77, 93, 112, 130, 136, 136, 88, 83, 88, 105, 122, 144, 167, 177, 32, 32, + 36, 47, 65, 79, 90, 96, 31, 32, 35, 44, 60, 72, 84, 90, 32, 34, 36, 45, 59, 71, 80, 87, 32, 35, 40, 47, 60, 71, 78, 85, 36, 37, 48, 56, + 68, 78, 83, 87, 39, 40, 50, 60, 73, 84, 91, 94, 47, 45, 56, 69, 84, 95, 101, 101, 53, 50, 60, 75, 92, 103, 108, 110, 61, 56, 65, 81, 100, + 113, 116, 118, 71, 64, 73, 89, 111, 125, 129, 129, 79, 70, 79, 95, 118, 133, 142, 138, 86, 76, 84, 100, 124, 140, 153, 150, 92, 82, 89, + 101, 121, 148, 157, 161, 98, 88, 93, 108, 124, 141, 163, 174, 104, 94, 95, 110, 129, 151, 171, 181, 110, 100, 98, 111, 127, 147, 169, + 188, 32, 31, 32, 32, 36, 39, 47, 53, 61, 71, 79, 86, 92, 98, 104, 110, 32, 32, 34, 35, 37, 40, 45, 50, 56, 64, 70, 76, 82, 88, 94, 100, + 36, 35, 36, 40, 48, 50, 56, 60, 65, 73, 79, 84, 89, 93, 95, 98, 47, 44, 45, 47, 56, 60, 69, 75, 81, 89, 95, 100, 101, 108, 110, 111, 65, + 60, 59, 60, 68, 73, 84, 92, 100, 111, 118, 124, 121, 124, 129, 127, 79, 72, 71, 71, 78, 84, 95, 103, 113, 125, 133, 140, 148, 141, 151, + 147, 90, 84, 80, 78, 83, 91, 101, 108, 116, 129, 142, 153, 157, 163, 171, 169, 96, 90, 87, 85, 87, 94, 101, 110, 118, 129, 138, 150, 161, + 174, 181, 188, 32, 31, 32, 32, 36, 44, 47, 53, 65, 73, 79, 87, 90, 93, 96, 99, 31, 32, 32, 33, 35, 42, 45, 51, 62, 69, 75, 83, 86, 88, + 91, 94, 31, 32, 32, 33, 35, 41, 44, 49, 60, 67, 72, 80, 84, 87, 90, 94, 31, 32, 33, 33, 35, 41, 44, 49, 59, 66, 71, 79, 82, 84, 87, 90, + 32, 32, 34, 34, 36, 42, 45, 50, 59, 65, 71, 78, 80, 83, 87, 90, 32, 33, 35, 36, 38, 42, 45, 49, 58, 64, 69, 76, 80, 83, 86, 88, 32, 33, + 35, 36, 40, 44, 47, 51, 60, 66, 71, 76, 78, 81, 85, 89, 34, 34, 36, 38, 42, 48, 50, 54, 63, 69, 73, 80, 82, 81, 84, 86, 36, 34, 37, 40, + 48, 54, 56, 60, 68, 74, 78, 84, 83, 86, 87, 87, 38, 36, 39, 41, 49, 56, 58, 63, 71, 77, 81, 86, 88, 88, 90, 93, 39, 37, 40, 42, 50, 58, + 60, 65, 73, 79, 84, 90, 91, 92, 94, 93, 44, 41, 42, 45, 53, 63, 66, 71, 79, 85, 90, 96, 94, 96, 96, 99, 47, 44, 45, 47, 56, 66, 69, 75, + 84, 90, 95, 99, 101, 98, 101, 99, 49, 46, 47, 48, 57, 67, 71, 77, 86, 93, 97, 103, 103, 105, 102, 106, 53, 49, 50, 51, 60, 71, 75, 82, + 92, 99, 103, 111, 108, 107, 110, 107, 58, 54, 54, 55, 63, 75, 79, 87, 98, 105, 110, 114, 114, 113, 111, 115, 61, 56, 56, 57, 65, 77, 81, + 89, 100, 107, 113, 118, 116, 117, 118, 116, 65, 60, 59, 60, 68, 79, 84, 92, 105, 112, 118, 126, 124, 122, 121, 124, 71, 65, 64, 65, 73, + 84, 89, 97, 111, 119, 125, 130, 129, 129, 129, 125, 76, 69, 68, 69, 76, 88, 92, 101, 115, 123, 130, 134, 134, 131, 132, 135, 79, 72, 70, + 71, 79, 90, 95, 104, 118, 127, 133, 143, 142, 141, 138, 136, 82, 75, 73, 74, 81, 92, 97, 106, 121, 130, 136, 146, 145, 144, 144, 145, 86, + 78, 76, 77, 84, 95, 100, 109, 124, 133, 140, 147, 153, 151, 150, 146, 89, 81, 79, 78, 87, 95, 99, 112, 124, 130, 145, 152, 156, 157, 156, + 158, 92, 84, 82, 80, 89, 95, 101, 116, 121, 132, 148, 151, 157, 163, 161, 159, 95, 86, 85, 83, 92, 95, 105, 114, 120, 136, 143, 155, 163, + 167, 171, 170, 98, 89, 88, 85, 93, 95, 108, 113, 124, 136, 141, 160, 163, 169, 174, 171, 101, 92, 91, 88, 94, 98, 110, 112, 128, 133, + 146, 158, 166, 175, 179, 185, 104, 95, 94, 91, 95, 101, 110, 115, 129, 132, 151, 154, 171, 175, 181, 186, 107, 98, 97, 94, 96, 105, 110, + 119, 128, 136, 149, 156, 173, 177, 188, 192, 110, 101, 100, 97, 98, 108, 111, 123, 127, 141, 147, 161, 169, 183, 188, 193, 114, 104, 104, + 100, 100, 111, 111, 126, 127, 145, 145, 166, 166, 189, 190, 201, 32, 31, 31, 31, 32, 32, 32, 34, 36, 38, 39, 44, 47, 49, 53, 58, 61, 65, + 71, 76, 79, 82, 86, 89, 92, 95, 98, 101, 104, 107, 110, 114, 31, 32, 32, 32, 32, 33, 33, 34, 34, 36, 37, 41, 44, 46, 49, 54, 56, 60, 65, + 69, 72, 75, 78, 81, 84, 86, 89, 92, 95, 98, 101, 104, 32, 32, 32, 33, 34, 35, 35, 36, 37, 39, 40, 42, 45, 47, 50, 54, 56, 59, 64, 68, 70, + 73, 76, 79, 82, 85, 88, 91, 94, 97, 100, 104, 32, 33, 33, 33, 34, 36, 36, 38, 40, 41, 42, 45, 47, 48, 51, 55, 57, 60, 65, 69, 71, 74, 77, + 78, 80, 83, 85, 88, 91, 94, 97, 100, 36, 35, 35, 35, 36, 38, 40, 42, 48, 49, 50, 53, 56, 57, 60, 63, 65, 68, 73, 76, 79, 81, 84, 87, 89, + 92, 93, 94, 95, 96, 98, 100, 44, 42, 41, 41, 42, 42, 44, 48, 54, 56, 58, 63, 66, 67, 71, 75, 77, 79, 84, 88, 90, 92, 95, 95, 95, 95, 95, + 98, 101, 105, 108, 111, 47, 45, 44, 44, 45, 45, 47, 50, 56, 58, 60, 66, 69, 71, 75, 79, 81, 84, 89, 92, 95, 97, 100, 99, 101, 105, 108, + 110, 110, 110, 111, 111, 53, 51, 49, 49, 50, 49, 51, 54, 60, 63, 65, 71, 75, 77, 82, 87, 89, 92, 97, 101, 104, 106, 109, 112, 116, 114, + 113, 112, 115, 119, 123, 126, 65, 62, 60, 59, 59, 58, 60, 63, 68, 71, 73, 79, 84, 86, 92, 98, 100, 105, 111, 115, 118, 121, 124, 124, + 121, 120, 124, 128, 129, 128, 127, 127, 73, 69, 67, 66, 65, 64, 66, 69, 74, 77, 79, 85, 90, 93, 99, 105, 107, 112, 119, 123, 127, 130, + 133, 130, 132, 136, 136, 133, 132, 136, 141, 145, 79, 75, 72, 71, 71, 69, 71, 73, 78, 81, 84, 90, 95, 97, 103, 110, 113, 118, 125, 130, + 133, 136, 140, 145, 148, 143, 141, 146, 151, 149, 147, 145, 87, 83, 80, 79, 78, 76, 76, 80, 84, 86, 90, 96, 99, 103, 111, 114, 118, 126, + 130, 134, 143, 146, 147, 152, 151, 155, 160, 158, 154, 156, 161, 166, 90, 86, 84, 82, 80, 80, 78, 82, 83, 88, 91, 94, 101, 103, 108, 114, + 116, 124, 129, 134, 142, 145, 153, 156, 157, 163, 163, 166, 171, 173, 169, 166, 93, 88, 87, 84, 83, 83, 81, 81, 86, 88, 92, 96, 98, 105, + 107, 113, 117, 122, 129, 131, 141, 144, 151, 157, 163, 167, 169, 175, 175, 177, 183, 189, 96, 91, 90, 87, 87, 86, 85, 84, 87, 90, 94, 96, + 101, 102, 110, 111, 118, 121, 129, 132, 138, 144, 150, 156, 161, 171, 174, 179, 181, 188, 188, 190, 99, 94, 94, 90, 90, 88, 89, 86, 87, + 93, 93, 99, 99, 106, 107, 115, 116, 124, 125, 135, 136, 145, 146, 158, 159, 170, 171, 185, 186, 192, 193, 201, 31, 44, 73, 93, 32, 41, + 67, 87, 32, 42, 65, 83, 33, 44, 66, 81, 34, 54, 74, 86, 37, 58, 79, 92, 44, 66, 90, 98, 49, 71, 99, 107, 56, 77, 107, 117, 65, 84, 119, + 129, 72, 90, 127, 141, 78, 95, 133, 151, 84, 95, 132, 163, 89, 95, 136, 169, 95, 101, 132, 175, 101, 108, 141, 183, 31, 32, 32, 33, 34, + 37, 44, 49, 56, 65, 72, 78, 84, 89, 95, 101, 44, 41, 42, 44, 54, 58, 66, 71, 77, 84, 90, 95, 95, 95, 101, 108, 73, 67, 65, 66, 74, 79, + 90, 99, 107, 119, 127, 133, 132, 136, 132, 141, 93, 87, 83, 81, 86, 92, 98, 107, 117, 129, 141, 151, 163, 169, 175, 183, 32, 32, 36, 47, + 65, 79, 90, 96, 31, 32, 35, 45, 62, 75, 86, 91, 31, 32, 35, 44, 60, 72, 84, 90, 31, 33, 35, 44, 59, 71, 82, 87, 32, 34, 36, 45, 59, 71, + 80, 87, 32, 35, 38, 45, 58, 69, 80, 86, 32, 35, 40, 47, 60, 71, 78, 85, 34, 36, 42, 50, 63, 73, 82, 84, 36, 37, 48, 56, 68, 78, 83, 87, + 38, 39, 49, 58, 71, 81, 88, 90, 39, 40, 50, 60, 73, 84, 91, 94, 44, 42, 53, 66, 79, 90, 94, 96, 47, 45, 56, 69, 84, 95, 101, 101, 49, 47, + 57, 71, 86, 97, 103, 102, 53, 50, 60, 75, 92, 103, 108, 110, 58, 54, 63, 79, 98, 110, 114, 111, 61, 56, 65, 81, 100, 113, 116, 118, 65, + 59, 68, 84, 105, 118, 124, 121, 71, 64, 73, 89, 111, 125, 129, 129, 76, 68, 76, 92, 115, 130, 134, 132, 79, 70, 79, 95, 118, 133, 142, + 138, 82, 73, 81, 97, 121, 136, 145, 144, 86, 76, 84, 100, 124, 140, 153, 150, 89, 79, 87, 99, 124, 145, 156, 156, 92, 82, 89, 101, 121, + 148, 157, 161, 95, 85, 92, 105, 120, 143, 163, 171, 98, 88, 93, 108, 124, 141, 163, 174, 101, 91, 94, 110, 128, 146, 166, 179, 104, 94, + 95, 110, 129, 151, 171, 181, 107, 97, 96, 110, 128, 149, 173, 188, 110, 100, 98, 111, 127, 147, 169, 188, 114, 104, 100, 111, 127, 145, + 166, 190, 32, 31, 31, 31, 32, 32, 32, 34, 36, 38, 39, 44, 47, 49, 53, 58, 61, 65, 71, 76, 79, 82, 86, 89, 92, 95, 98, 101, 104, 107, 110, + 114, 32, 32, 32, 33, 34, 35, 35, 36, 37, 39, 40, 42, 45, 47, 50, 54, 56, 59, 64, 68, 70, 73, 76, 79, 82, 85, 88, 91, 94, 97, 100, 104, + 36, 35, 35, 35, 36, 38, 40, 42, 48, 49, 50, 53, 56, 57, 60, 63, 65, 68, 73, 76, 79, 81, 84, 87, 89, 92, 93, 94, 95, 96, 98, 100, 47, 45, + 44, 44, 45, 45, 47, 50, 56, 58, 60, 66, 69, 71, 75, 79, 81, 84, 89, 92, 95, 97, 100, 99, 101, 105, 108, 110, 110, 110, 111, 111, 65, 62, + 60, 59, 59, 58, 60, 63, 68, 71, 73, 79, 84, 86, 92, 98, 100, 105, 111, 115, 118, 121, 124, 124, 121, 120, 124, 128, 129, 128, 127, 127, + 79, 75, 72, 71, 71, 69, 71, 73, 78, 81, 84, 90, 95, 97, 103, 110, 113, 118, 125, 130, 133, 136, 140, 145, 148, 143, 141, 146, 151, 149, + 147, 145, 90, 86, 84, 82, 80, 80, 78, 82, 83, 88, 91, 94, 101, 103, 108, 114, 116, 124, 129, 134, 142, 145, 153, 156, 157, 163, 163, 166, + 171, 173, 169, 166, 96, 91, 90, 87, 87, 86, 85, 84, 87, 90, 94, 96, 101, 102, 110, 111, 118, 121, 129, 132, 138, 144, 150, 156, 161, 171, + 174, 179, 181, 188, 188, 190, + ], + [ + 33, 45, 56, 64, 45, 58, 66, 69, 56, 66, 86, 87, 64, 69, 87, 105, 31, 38, 47, 48, 54, 61, 66, 69, 38, 47, 47, 46, 50, 55, 61, 65, 47, 47, + 53, 55, 58, 63, 65, 66, 48, 46, 55, 62, 67, 72, 73, 73, 54, 50, 58, 67, 76, 83, 84, 82, 61, 55, 63, 72, 83, 91, 92, 92, 66, 61, 65, 73, + 84, 92, 101, 103, 69, 65, 66, 73, 82, 92, 103, 109, 32, 30, 33, 38, 49, 48, 50, 52, 55, 60, 63, 66, 68, 70, 72, 74, 30, 31, 35, 41, 46, + 46, 46, 48, 51, 55, 58, 60, 63, 65, 68, 70, 33, 35, 39, 44, 47, 46, 46, 47, 50, 53, 56, 58, 60, 62, 65, 67, 38, 41, 44, 47, 49, 48, 47, + 48, 50, 53, 55, 58, 58, 60, 62, 65, 49, 46, 47, 49, 53, 53, 54, 54, 56, 58, 60, 62, 62, 63, 64, 64, 48, 46, 46, 48, 53, 54, 56, 57, 59, + 61, 63, 65, 67, 66, 68, 68, 50, 46, 46, 47, 54, 56, 61, 63, 65, 68, 70, 72, 71, 71, 72, 72, 52, 48, 47, 48, 54, 57, 63, 66, 69, 72, 75, + 76, 75, 76, 76, 76, 55, 51, 50, 50, 56, 59, 65, 69, 73, 77, 79, 81, 81, 81, 80, 80, 60, 55, 53, 53, 58, 61, 68, 72, 77, 82, 85, 87, 87, + 85, 84, 85, 63, 58, 56, 55, 60, 63, 70, 75, 79, 85, 89, 91, 91, 90, 89, 90, 66, 60, 58, 58, 62, 65, 72, 76, 81, 87, 91, 94, 96, 95, 95, + 95, 68, 63, 60, 58, 62, 67, 71, 75, 81, 87, 91, 96, 99, 100, 100, 100, 70, 65, 62, 60, 63, 66, 71, 76, 81, 85, 90, 95, 100, 103, 104, + 105, 72, 68, 65, 62, 64, 68, 72, 76, 80, 84, 89, 95, 100, 104, 107, 108, 74, 70, 67, 65, 64, 68, 72, 76, 80, 85, 90, 95, 100, 105, 108, + 111, 32, 31, 30, 31, 33, 36, 38, 41, 49, 49, 48, 49, 50, 51, 52, 54, 55, 57, 60, 62, 63, 65, 66, 67, 68, 69, 70, 71, 72, 73, 74, 75, 31, + 31, 31, 32, 34, 38, 40, 42, 47, 47, 47, 47, 48, 48, 50, 52, 53, 54, 57, 59, 60, 61, 63, 64, 65, 66, 67, 67, 68, 69, 70, 71, 30, 31, 31, + 32, 35, 39, 41, 42, 46, 46, 46, 45, 46, 47, 48, 50, 51, 52, 55, 57, 58, 59, 60, 62, 63, 64, 65, 67, 68, 69, 70, 71, 31, 32, 32, 33, 36, + 40, 41, 43, 46, 46, 45, 45, 46, 46, 47, 49, 50, 51, 54, 56, 57, 58, 59, 61, 62, 63, 63, 64, 65, 66, 67, 68, 33, 34, 35, 36, 39, 43, 44, + 45, 47, 46, 46, 45, 46, 47, 47, 49, 50, 51, 53, 55, 56, 57, 58, 59, 60, 61, 62, 63, 65, 66, 67, 68, 36, 38, 39, 40, 43, 47, 47, 47, 48, + 47, 46, 45, 46, 46, 47, 48, 49, 50, 52, 53, 54, 55, 56, 58, 59, 61, 62, 63, 64, 65, 66, 66, 38, 40, 41, 41, 44, 47, 47, 48, 49, 48, 48, + 47, 47, 47, 48, 49, 50, 51, 53, 54, 55, 56, 58, 58, 58, 59, 60, 61, 62, 64, 65, 66, 41, 42, 42, 43, 45, 47, 48, 48, 50, 50, 49, 49, 50, + 50, 50, 52, 52, 53, 55, 56, 57, 58, 59, 60, 61, 61, 61, 61, 62, 63, 63, 64, 49, 47, 46, 46, 47, 48, 49, 50, 53, 53, 53, 53, 54, 54, 54, + 55, 56, 56, 58, 59, 60, 61, 62, 63, 62, 62, 63, 64, 64, 64, 64, 64, 49, 47, 46, 46, 46, 47, 48, 50, 53, 53, 54, 55, 55, 55, 56, 57, 58, + 58, 60, 61, 62, 63, 64, 64, 64, 65, 65, 65, 65, 66, 67, 68, 48, 47, 46, 45, 46, 46, 48, 49, 53, 54, 54, 55, 56, 56, 57, 58, 59, 60, 61, + 63, 63, 64, 65, 66, 67, 66, 66, 67, 68, 68, 68, 68, 49, 47, 45, 45, 45, 45, 47, 49, 53, 55, 55, 58, 59, 60, 61, 62, 63, 63, 65, 66, 67, + 68, 69, 69, 68, 68, 69, 69, 69, 69, 70, 71, 50, 48, 46, 46, 46, 46, 47, 50, 54, 55, 56, 59, 61, 61, 63, 64, 65, 66, 68, 69, 70, 71, 72, + 71, 71, 72, 71, 71, 72, 72, 72, 71, 51, 48, 47, 46, 47, 46, 47, 50, 54, 55, 56, 60, 61, 62, 64, 66, 66, 67, 69, 70, 71, 72, 73, 73, 74, + 73, 73, 74, 73, 73, 74, 75, 52, 50, 48, 47, 47, 47, 48, 50, 54, 56, 57, 61, 63, 64, 66, 68, 69, 70, 72, 74, 75, 75, 76, 77, 75, 76, 76, + 75, 76, 77, 76, 75, 54, 52, 50, 49, 49, 48, 49, 52, 55, 57, 58, 62, 64, 66, 68, 71, 72, 73, 75, 77, 78, 79, 80, 78, 79, 78, 77, 78, 78, + 77, 78, 79, 55, 53, 51, 50, 50, 49, 50, 52, 56, 58, 59, 63, 65, 66, 69, 72, 73, 74, 77, 78, 79, 80, 81, 81, 81, 80, 81, 80, 80, 81, 80, + 79, 57, 54, 52, 51, 51, 50, 51, 53, 56, 58, 60, 63, 66, 67, 70, 73, 74, 76, 79, 80, 82, 83, 84, 85, 83, 84, 83, 83, 83, 82, 82, 83, 60, + 57, 55, 54, 53, 52, 53, 55, 58, 60, 61, 65, 68, 69, 72, 75, 77, 79, 82, 84, 85, 86, 87, 86, 87, 85, 85, 85, 84, 86, 85, 84, 62, 59, 57, + 56, 55, 53, 54, 56, 59, 61, 63, 66, 69, 70, 74, 77, 78, 80, 84, 86, 87, 88, 90, 89, 89, 88, 88, 87, 88, 87, 87, 88, 63, 60, 58, 57, 56, + 54, 55, 57, 60, 62, 63, 67, 70, 71, 75, 78, 79, 82, 85, 87, 89, 90, 91, 93, 91, 91, 90, 91, 89, 90, 90, 89, 65, 61, 59, 58, 57, 55, 56, + 58, 61, 63, 64, 68, 71, 72, 75, 79, 80, 83, 86, 88, 90, 91, 93, 94, 95, 92, 94, 92, 93, 92, 91, 93, 66, 63, 60, 59, 58, 56, 58, 59, 62, + 64, 65, 69, 72, 73, 76, 80, 81, 84, 87, 90, 91, 93, 94, 95, 96, 97, 95, 95, 95, 95, 95, 93, 67, 64, 62, 61, 59, 58, 58, 60, 63, 64, 66, + 69, 71, 73, 77, 78, 81, 85, 86, 89, 93, 94, 95, 97, 97, 98, 99, 97, 97, 97, 96, 98, 68, 65, 63, 62, 60, 59, 58, 61, 62, 64, 67, 68, 71, + 74, 75, 79, 81, 83, 87, 89, 91, 95, 96, 97, 99, 98, 100, 100, 100, 99, 100, 98, 69, 66, 64, 63, 61, 61, 59, 61, 62, 65, 66, 68, 72, 73, + 76, 78, 80, 84, 85, 88, 91, 92, 97, 98, 98, 101, 100, 102, 102, 103, 101, 102, 70, 67, 65, 63, 62, 62, 60, 61, 63, 65, 66, 69, 71, 73, + 76, 77, 81, 83, 85, 88, 90, 94, 95, 99, 100, 100, 103, 102, 104, 104, 105, 103, 71, 67, 67, 64, 63, 63, 61, 61, 64, 65, 67, 69, 71, 74, + 75, 78, 80, 83, 85, 87, 91, 92, 95, 97, 100, 102, 102, 105, 104, 106, 106, 108, 72, 68, 68, 65, 65, 64, 62, 62, 64, 65, 68, 69, 72, 73, + 76, 78, 80, 83, 84, 88, 89, 93, 95, 97, 100, 102, 104, 104, 107, 106, 108, 108, 73, 69, 69, 66, 66, 65, 64, 63, 64, 66, 68, 69, 72, 73, + 77, 77, 81, 82, 86, 87, 90, 92, 95, 97, 99, 103, 104, 106, 106, 109, 108, 110, 74, 70, 70, 67, 67, 66, 65, 63, 64, 67, 68, 70, 72, 74, + 76, 78, 80, 82, 85, 87, 90, 91, 95, 96, 100, 101, 105, 106, 108, 108, 111, 110, 75, 71, 71, 68, 68, 66, 66, 64, 64, 68, 68, 71, 71, 75, + 75, 79, 79, 83, 84, 88, 89, 93, 93, 98, 98, 102, 103, 108, 108, 110, 110, 113, 31, 47, 57, 65, 40, 45, 52, 61, 46, 55, 61, 63, 47, 60, + 70, 72, 52, 64, 79, 81, 59, 68, 87, 90, 63, 66, 88, 99, 66, 69, 85, 102, 31, 40, 46, 47, 52, 59, 63, 66, 47, 45, 55, 60, 64, 68, 66, 69, + 57, 52, 61, 70, 79, 87, 88, 85, 65, 61, 63, 72, 81, 90, 99, 102, 32, 35, 48, 50, 57, 63, 68, 70, 30, 38, 46, 46, 52, 58, 63, 65, 33, 41, + 47, 46, 51, 56, 60, 63, 39, 46, 48, 47, 51, 55, 58, 61, 49, 48, 53, 54, 57, 60, 61, 61, 48, 46, 53, 56, 60, 64, 65, 65, 50, 46, 54, 61, + 66, 70, 71, 69, 52, 47, 54, 63, 71, 75, 75, 74, 55, 49, 56, 65, 74, 79, 79, 78, 60, 53, 58, 68, 79, 85, 85, 82, 63, 55, 60, 70, 82, 89, + 91, 87, 66, 58, 62, 72, 84, 91, 95, 91, 68, 60, 64, 71, 81, 94, 97, 96, 70, 62, 65, 73, 81, 89, 98, 101, 72, 65, 65, 72, 82, 92, 100, + 103, 74, 67, 65, 71, 79, 89, 98, 105, 32, 30, 33, 39, 49, 48, 50, 52, 55, 60, 63, 66, 68, 70, 72, 74, 35, 38, 41, 46, 48, 46, 46, 47, 49, + 53, 55, 58, 60, 62, 65, 67, 48, 46, 47, 48, 53, 53, 54, 54, 56, 58, 60, 62, 64, 65, 65, 65, 50, 46, 46, 47, 54, 56, 61, 63, 65, 68, 70, + 72, 71, 73, 72, 71, 57, 52, 51, 51, 57, 60, 66, 71, 74, 79, 82, 84, 81, 81, 82, 79, 63, 58, 56, 55, 60, 64, 70, 75, 79, 85, 89, 91, 94, + 89, 92, 89, 68, 63, 60, 58, 61, 65, 71, 75, 79, 85, 91, 95, 97, 98, 100, 98, 70, 65, 63, 61, 61, 65, 69, 74, 78, 82, 87, 91, 96, 101, + 103, 105, 32, 31, 35, 38, 48, 49, 50, 52, 57, 61, 63, 67, 68, 69, 70, 71, 31, 31, 37, 40, 47, 47, 48, 50, 54, 57, 60, 63, 64, 65, 66, 67, + 30, 32, 38, 40, 46, 45, 46, 48, 52, 55, 58, 61, 63, 64, 65, 67, 31, 33, 38, 41, 46, 45, 46, 48, 52, 55, 57, 60, 61, 62, 63, 64, 33, 36, + 41, 44, 47, 46, 46, 47, 51, 54, 56, 59, 60, 61, 63, 64, 37, 40, 45, 47, 47, 45, 46, 47, 50, 52, 54, 57, 59, 61, 62, 62, 39, 41, 46, 47, + 48, 47, 47, 48, 51, 54, 55, 57, 58, 59, 61, 62, 42, 43, 46, 48, 50, 49, 50, 50, 53, 56, 57, 60, 60, 59, 60, 60, 49, 46, 48, 49, 53, 53, + 54, 54, 57, 59, 60, 63, 61, 62, 61, 61, 48, 46, 47, 48, 53, 55, 55, 56, 58, 61, 62, 64, 64, 63, 63, 64, 48, 46, 46, 48, 53, 56, 56, 57, + 60, 62, 64, 66, 65, 65, 65, 64, 49, 45, 45, 47, 53, 58, 59, 61, 64, 66, 67, 69, 67, 67, 66, 67, 50, 46, 46, 48, 54, 59, 61, 63, 66, 68, + 70, 71, 71, 68, 69, 67, 51, 47, 47, 48, 54, 60, 61, 64, 68, 70, 71, 73, 72, 72, 70, 71, 52, 48, 47, 48, 54, 61, 63, 66, 71, 73, 75, 77, + 75, 73, 74, 71, 54, 50, 49, 50, 55, 62, 65, 68, 73, 76, 78, 79, 78, 76, 74, 75, 55, 51, 49, 50, 56, 63, 65, 69, 74, 77, 79, 81, 79, 78, + 78, 75, 57, 52, 50, 51, 56, 64, 66, 70, 76, 79, 82, 85, 83, 81, 79, 79, 60, 54, 53, 53, 58, 65, 68, 72, 79, 82, 85, 87, 85, 84, 82, 80, + 62, 56, 54, 55, 60, 66, 69, 74, 81, 84, 87, 88, 87, 85, 84, 84, 63, 57, 55, 56, 60, 67, 70, 75, 82, 86, 89, 92, 91, 89, 87, 84, 64, 59, + 56, 57, 61, 68, 71, 75, 83, 87, 90, 93, 92, 90, 89, 89, 66, 60, 58, 58, 62, 69, 72, 76, 84, 88, 91, 94, 95, 93, 91, 89, 67, 61, 59, 58, + 63, 68, 71, 78, 83, 86, 93, 96, 96, 96, 94, 94, 68, 62, 60, 59, 64, 67, 71, 79, 81, 86, 94, 95, 97, 98, 96, 94, 69, 63, 61, 60, 65, 66, + 72, 77, 80, 88, 91, 96, 99, 99, 100, 98, 70, 64, 62, 60, 65, 66, 73, 76, 81, 87, 89, 97, 98, 100, 101, 99, 71, 65, 64, 61, 65, 67, 73, + 74, 82, 85, 90, 95, 99, 102, 103, 104, 72, 65, 65, 62, 65, 68, 72, 75, 82, 83, 92, 93, 100, 102, 103, 104, 73, 66, 66, 63, 65, 69, 72, + 76, 81, 85, 90, 93, 100, 102, 105, 106, 74, 67, 67, 64, 65, 70, 71, 77, 79, 86, 89, 94, 98, 103, 105, 106, 75, 68, 68, 65, 65, 71, 71, + 78, 78, 87, 87, 96, 96, 105, 105, 109, 32, 31, 30, 31, 33, 37, 39, 42, 49, 48, 48, 49, 50, 51, 52, 54, 55, 57, 60, 62, 63, 64, 66, 67, + 68, 69, 70, 71, 72, 73, 74, 75, 31, 31, 32, 33, 36, 40, 41, 43, 46, 46, 46, 45, 46, 47, 48, 50, 51, 52, 54, 56, 57, 59, 60, 61, 62, 63, + 64, 65, 65, 66, 67, 68, 35, 37, 38, 38, 41, 45, 46, 46, 48, 47, 46, 45, 46, 47, 47, 49, 49, 50, 53, 54, 55, 56, 58, 59, 60, 61, 62, 64, + 65, 66, 67, 68, 38, 40, 40, 41, 44, 47, 47, 48, 49, 48, 48, 47, 48, 48, 48, 50, 50, 51, 53, 55, 56, 57, 58, 58, 59, 60, 60, 61, 62, 63, + 64, 65, 48, 47, 46, 46, 47, 47, 48, 50, 53, 53, 53, 53, 54, 54, 54, 55, 56, 56, 58, 60, 60, 61, 62, 63, 64, 65, 65, 65, 65, 65, 65, 65, + 49, 47, 45, 45, 46, 45, 47, 49, 53, 55, 56, 58, 59, 60, 61, 62, 63, 64, 65, 66, 67, 68, 69, 68, 67, 66, 66, 67, 68, 69, 70, 71, 50, 48, + 46, 46, 46, 46, 47, 50, 54, 55, 56, 59, 61, 61, 63, 65, 65, 66, 68, 69, 70, 71, 72, 71, 71, 72, 73, 73, 72, 72, 71, 71, 52, 50, 48, 48, + 47, 47, 48, 50, 54, 56, 57, 61, 63, 64, 66, 68, 69, 70, 72, 74, 75, 75, 76, 78, 79, 77, 76, 74, 75, 76, 77, 78, 57, 54, 52, 52, 51, 50, + 51, 53, 57, 58, 60, 64, 66, 68, 71, 73, 74, 76, 79, 81, 82, 83, 84, 83, 81, 80, 81, 82, 82, 81, 79, 78, 61, 57, 55, 55, 54, 52, 54, 56, + 59, 61, 62, 66, 68, 70, 73, 76, 77, 79, 82, 84, 86, 87, 88, 86, 86, 88, 87, 85, 83, 85, 86, 87, 63, 60, 58, 57, 56, 54, 55, 57, 60, 62, + 64, 67, 70, 71, 75, 78, 79, 82, 85, 87, 89, 90, 91, 93, 94, 91, 89, 90, 92, 90, 89, 87, 67, 63, 61, 60, 59, 57, 57, 60, 63, 64, 66, 69, + 71, 73, 77, 79, 81, 85, 87, 88, 92, 93, 94, 96, 95, 96, 97, 95, 93, 93, 94, 96, 68, 64, 63, 61, 60, 59, 58, 60, 61, 64, 65, 67, 71, 72, + 75, 78, 79, 83, 85, 87, 91, 92, 95, 96, 97, 99, 98, 99, 100, 100, 98, 96, 69, 65, 64, 62, 61, 61, 59, 59, 62, 63, 65, 67, 68, 72, 73, 76, + 78, 81, 84, 85, 89, 90, 93, 96, 98, 99, 100, 102, 102, 102, 103, 105, 70, 66, 65, 63, 63, 62, 61, 60, 61, 63, 65, 66, 69, 70, 74, 74, 78, + 79, 82, 84, 87, 89, 91, 94, 96, 100, 101, 103, 103, 105, 105, 105, 71, 67, 67, 64, 64, 62, 62, 60, 61, 64, 64, 67, 67, 71, 71, 75, 75, + 79, 80, 84, 84, 89, 89, 94, 94, 98, 99, 104, 104, 106, 106, 109, 31, 49, 61, 69, 32, 45, 55, 64, 36, 46, 54, 61, 41, 47, 54, 59, 46, 53, + 59, 62, 46, 56, 62, 65, 46, 59, 68, 68, 48, 61, 73, 73, 51, 63, 77, 78, 54, 65, 82, 84, 57, 67, 86, 89, 60, 69, 88, 93, 62, 67, 86, 98, + 64, 66, 87, 100, 65, 68, 83, 102, 67, 70, 86, 103, 31, 32, 36, 41, 46, 46, 46, 48, 51, 54, 57, 60, 62, 64, 65, 67, 49, 45, 46, 47, 53, + 56, 59, 61, 63, 65, 67, 69, 67, 66, 68, 70, 61, 55, 54, 54, 59, 62, 68, 73, 77, 82, 86, 88, 86, 87, 83, 86, 69, 64, 61, 59, 62, 65, 68, + 73, 78, 84, 89, 93, 98, 100, 102, 103, 32, 35, 48, 50, 57, 63, 68, 70, 31, 37, 47, 48, 54, 60, 64, 66, 30, 38, 46, 46, 52, 58, 63, 65, + 31, 38, 46, 46, 52, 57, 61, 63, 33, 41, 47, 46, 51, 56, 60, 63, 37, 45, 47, 46, 50, 54, 59, 62, 39, 46, 48, 47, 51, 55, 58, 61, 42, 46, + 50, 50, 53, 57, 60, 60, 49, 48, 53, 54, 57, 60, 61, 61, 48, 47, 53, 55, 58, 62, 64, 63, 48, 46, 53, 56, 60, 64, 65, 65, 49, 45, 53, 59, + 64, 67, 67, 66, 50, 46, 54, 61, 66, 70, 71, 69, 51, 47, 54, 61, 68, 71, 72, 70, 52, 47, 54, 63, 71, 75, 75, 74, 54, 49, 55, 65, 73, 78, + 78, 74, 55, 49, 56, 65, 74, 79, 79, 78, 57, 50, 56, 66, 76, 82, 83, 79, 60, 53, 58, 68, 79, 85, 85, 82, 62, 54, 60, 69, 81, 87, 87, 84, + 63, 55, 60, 70, 82, 89, 91, 87, 64, 56, 61, 71, 83, 90, 92, 89, 66, 58, 62, 72, 84, 91, 95, 91, 67, 59, 63, 71, 83, 93, 96, 94, 68, 60, + 64, 71, 81, 94, 97, 96, 69, 61, 65, 72, 80, 91, 99, 100, 70, 62, 65, 73, 81, 89, 98, 101, 71, 64, 65, 73, 82, 90, 99, 103, 72, 65, 65, + 72, 82, 92, 100, 103, 73, 66, 65, 72, 81, 90, 100, 105, 74, 67, 65, 71, 79, 89, 98, 105, 75, 68, 65, 71, 78, 87, 96, 105, 32, 31, 30, 31, + 33, 37, 39, 42, 49, 48, 48, 49, 50, 51, 52, 54, 55, 57, 60, 62, 63, 64, 66, 67, 68, 69, 70, 71, 72, 73, 74, 75, 35, 37, 38, 38, 41, 45, + 46, 46, 48, 47, 46, 45, 46, 47, 47, 49, 49, 50, 53, 54, 55, 56, 58, 59, 60, 61, 62, 64, 65, 66, 67, 68, 48, 47, 46, 46, 47, 47, 48, 50, + 53, 53, 53, 53, 54, 54, 54, 55, 56, 56, 58, 60, 60, 61, 62, 63, 64, 65, 65, 65, 65, 65, 65, 65, 50, 48, 46, 46, 46, 46, 47, 50, 54, 55, + 56, 59, 61, 61, 63, 65, 65, 66, 68, 69, 70, 71, 72, 71, 71, 72, 73, 73, 72, 72, 71, 71, 57, 54, 52, 52, 51, 50, 51, 53, 57, 58, 60, 64, + 66, 68, 71, 73, 74, 76, 79, 81, 82, 83, 84, 83, 81, 80, 81, 82, 82, 81, 79, 78, 63, 60, 58, 57, 56, 54, 55, 57, 60, 62, 64, 67, 70, 71, + 75, 78, 79, 82, 85, 87, 89, 90, 91, 93, 94, 91, 89, 90, 92, 90, 89, 87, 68, 64, 63, 61, 60, 59, 58, 60, 61, 64, 65, 67, 71, 72, 75, 78, + 79, 83, 85, 87, 91, 92, 95, 96, 97, 99, 98, 99, 100, 100, 98, 96, 70, 66, 65, 63, 63, 62, 61, 60, 61, 63, 65, 66, 69, 70, 74, 74, 78, 79, + 82, 84, 87, 89, 91, 94, 96, 100, 101, 103, 103, 105, 105, 105, + ], + ], + [ + [ + 32, 38, 63, 86, 38, 56, 78, 97, 63, 78, 113, 130, 86, 97, 130, 169, 32, 32, 35, 46, 57, 76, 85, 96, 32, 34, 37, 45, 54, 70, 79, 90, 35, + 37, 48, 56, 64, 79, 87, 93, 46, 45, 56, 70, 80, 96, 100, 105, 57, 54, 64, 80, 93, 111, 121, 122, 76, 70, 79, 96, 111, 134, 138, 144, 85, + 79, 87, 100, 121, 138, 156, 168, 96, 90, 93, 105, 122, 144, 168, 184, 32, 31, 31, 32, 34, 39, 44, 49, 58, 65, 71, 81, 87, 93, 98, 104, + 31, 32, 32, 32, 34, 38, 41, 46, 54, 60, 66, 75, 81, 86, 92, 98, 31, 32, 33, 34, 36, 39, 42, 46, 53, 59, 64, 73, 78, 83, 88, 94, 32, 32, + 34, 35, 37, 40, 42, 46, 52, 58, 63, 71, 75, 80, 86, 92, 34, 34, 36, 37, 42, 47, 50, 53, 59, 65, 70, 77, 82, 85, 89, 92, 39, 38, 39, 40, + 47, 54, 58, 62, 68, 73, 78, 85, 90, 90, 96, 98, 44, 41, 42, 42, 50, 58, 63, 68, 74, 79, 84, 91, 96, 98, 102, 104, 49, 46, 46, 46, 53, 62, + 68, 73, 81, 87, 92, 99, 103, 107, 109, 112, 58, 54, 53, 52, 59, 68, 74, 81, 90, 97, 102, 110, 114, 118, 117, 121, 65, 60, 59, 58, 65, 73, + 79, 87, 97, 105, 111, 120, 125, 125, 126, 130, 71, 66, 64, 63, 70, 78, 84, 92, 102, 111, 117, 127, 133, 134, 136, 141, 81, 75, 73, 71, + 77, 85, 91, 99, 110, 120, 127, 137, 143, 145, 148, 152, 87, 81, 78, 75, 82, 90, 96, 103, 114, 125, 133, 143, 150, 156, 160, 163, 93, 86, + 83, 80, 85, 90, 98, 107, 118, 125, 134, 145, 156, 163, 169, 177, 98, 92, 88, 86, 89, 96, 102, 109, 117, 126, 136, 148, 160, 169, 176, + 184, 104, 98, 94, 92, 92, 98, 104, 112, 121, 130, 141, 152, 163, 177, 184, 191, 32, 31, 31, 31, 31, 32, 32, 34, 34, 36, 39, 41, 44, 48, + 49, 54, 58, 59, 65, 69, 71, 80, 81, 83, 87, 90, 93, 95, 98, 101, 104, 107, 31, 32, 32, 32, 32, 32, 32, 34, 34, 35, 38, 39, 42, 46, 47, + 51, 55, 57, 62, 66, 68, 76, 77, 78, 83, 85, 88, 90, 93, 96, 99, 101, 31, 32, 32, 32, 32, 32, 32, 33, 34, 34, 38, 39, 41, 45, 46, 50, 54, + 55, 60, 64, 66, 73, 75, 76, 81, 83, 86, 89, 92, 95, 98, 101, 31, 32, 32, 32, 32, 32, 32, 33, 34, 34, 37, 38, 41, 44, 45, 49, 53, 54, 59, + 63, 65, 72, 74, 75, 79, 81, 84, 86, 89, 91, 94, 97, 31, 32, 32, 32, 33, 33, 34, 35, 36, 36, 39, 40, 42, 45, 46, 50, 53, 54, 59, 63, 64, + 71, 73, 74, 78, 80, 83, 85, 88, 91, 94, 97, 32, 32, 32, 32, 33, 34, 34, 36, 36, 37, 40, 40, 42, 45, 46, 49, 53, 54, 58, 62, 63, 70, 72, + 73, 77, 79, 82, 85, 87, 90, 92, 95, 32, 32, 32, 32, 34, 34, 35, 37, 37, 38, 40, 41, 42, 45, 46, 49, 52, 54, 58, 61, 63, 69, 71, 72, 75, + 78, 80, 83, 86, 89, 92, 95, 34, 34, 33, 33, 35, 36, 37, 39, 41, 42, 45, 46, 47, 50, 51, 54, 57, 59, 63, 66, 68, 74, 75, 76, 80, 81, 82, + 83, 85, 87, 90, 93, 34, 34, 34, 34, 36, 36, 37, 41, 42, 45, 47, 48, 50, 53, 53, 56, 59, 61, 65, 68, 70, 76, 77, 78, 82, 83, 85, 88, 89, + 90, 92, 93, 36, 35, 34, 34, 36, 37, 38, 42, 45, 48, 50, 51, 54, 56, 57, 60, 63, 64, 68, 71, 73, 79, 80, 81, 85, 87, 89, 89, 90, 93, 96, + 99, 39, 38, 38, 37, 39, 40, 40, 45, 47, 50, 54, 55, 58, 61, 62, 65, 68, 69, 73, 76, 78, 84, 85, 86, 90, 89, 90, 93, 96, 97, 98, 99, 41, + 39, 39, 38, 40, 40, 41, 46, 48, 51, 55, 56, 59, 62, 63, 67, 70, 71, 75, 78, 80, 86, 87, 88, 91, 93, 96, 97, 97, 99, 102, 105, 44, 42, 41, + 41, 42, 42, 42, 47, 50, 54, 58, 59, 63, 66, 68, 71, 74, 75, 79, 83, 84, 90, 91, 92, 96, 98, 98, 99, 102, 104, 104, 105, 48, 46, 45, 44, + 45, 45, 45, 50, 53, 56, 61, 62, 66, 70, 71, 76, 79, 80, 85, 88, 90, 96, 97, 98, 101, 100, 102, 105, 105, 105, 109, 112, 49, 47, 46, 45, + 46, 46, 46, 51, 53, 57, 62, 63, 68, 71, 73, 77, 81, 82, 87, 90, 92, 98, 99, 100, 103, 106, 107, 106, 109, 112, 112, 112, 54, 51, 50, 49, + 50, 49, 49, 54, 56, 60, 65, 67, 71, 76, 77, 82, 86, 87, 92, 96, 97, 104, 105, 106, 110, 110, 109, 113, 114, 113, 116, 120, 58, 55, 54, + 53, 53, 53, 52, 57, 59, 63, 68, 70, 74, 79, 81, 86, 90, 91, 97, 100, 102, 109, 110, 111, 114, 114, 118, 116, 117, 121, 121, 120, 59, 57, + 55, 54, 54, 54, 54, 59, 61, 64, 69, 71, 75, 80, 82, 87, 91, 93, 99, 102, 104, 111, 112, 113, 117, 121, 120, 122, 124, 122, 125, 129, 65, + 62, 60, 59, 59, 58, 58, 63, 65, 68, 73, 75, 79, 85, 87, 92, 97, 99, 105, 109, 111, 118, 120, 121, 125, 124, 125, 127, 126, 130, 130, 129, + 69, 66, 64, 63, 63, 62, 61, 66, 68, 71, 76, 78, 83, 88, 90, 96, 100, 102, 109, 113, 115, 123, 125, 126, 129, 130, 131, 130, 134, 133, + 135, 139, 71, 68, 66, 65, 64, 63, 63, 68, 70, 73, 78, 80, 84, 90, 92, 97, 102, 104, 111, 115, 117, 125, 127, 128, 133, 136, 134, 139, + 136, 139, 141, 140, 80, 76, 73, 72, 71, 70, 69, 74, 76, 79, 84, 86, 90, 96, 98, 104, 109, 111, 118, 123, 125, 134, 136, 137, 142, 138, + 143, 140, 144, 144, 144, 149, 81, 77, 75, 74, 73, 72, 71, 75, 77, 80, 85, 87, 91, 97, 99, 105, 110, 112, 120, 125, 127, 136, 137, 139, + 143, 148, 145, 148, 148, 150, 152, 149, 83, 78, 76, 75, 74, 73, 72, 76, 78, 81, 86, 88, 92, 98, 100, 106, 111, 113, 121, 126, 128, 137, + 139, 140, 145, 149, 153, 153, 154, 155, 155, 161, 87, 83, 81, 79, 78, 77, 75, 80, 82, 85, 90, 91, 96, 101, 103, 110, 114, 117, 125, 129, + 133, 142, 143, 145, 150, 151, 156, 159, 160, 160, 163, 161, 90, 85, 83, 81, 80, 79, 78, 81, 83, 87, 89, 93, 98, 100, 106, 110, 114, 121, + 124, 130, 136, 138, 148, 149, 151, 156, 157, 162, 166, 168, 166, 172, 93, 88, 86, 84, 83, 82, 80, 82, 85, 89, 90, 96, 98, 102, 107, 109, + 118, 120, 125, 131, 134, 143, 145, 153, 156, 157, 163, 164, 169, 172, 177, 172, 95, 90, 89, 86, 85, 85, 83, 83, 88, 89, 93, 97, 99, 105, + 106, 113, 116, 122, 127, 130, 139, 140, 148, 153, 159, 162, 164, 169, 170, 176, 179, 185, 98, 93, 92, 89, 88, 87, 86, 85, 89, 90, 96, 97, + 102, 105, 109, 114, 117, 124, 126, 134, 136, 144, 148, 154, 160, 166, 169, 170, 176, 177, 184, 186, 101, 96, 95, 91, 91, 90, 89, 87, 90, + 93, 97, 99, 104, 105, 112, 113, 121, 122, 130, 133, 139, 144, 150, 155, 160, 168, 172, 176, 177, 184, 185, 191, 104, 99, 98, 94, 94, 92, + 92, 90, 92, 96, 98, 102, 104, 109, 112, 116, 121, 125, 130, 135, 141, 144, 152, 155, 163, 166, 177, 179, 184, 185, 191, 192, 107, 101, + 101, 97, 97, 95, 95, 93, 93, 99, 99, 105, 105, 112, 112, 120, 120, 129, 129, 139, 140, 149, 149, 161, 161, 172, 172, 185, 186, 191, 192, + 199, 32, 38, 62, 86, 32, 40, 58, 80, 34, 51, 68, 85, 44, 61, 85, 101, 54, 69, 98, 117, 72, 84, 118, 136, 82, 89, 129, 157, 92, 98, 127, + 165, 32, 32, 34, 44, 54, 72, 82, 92, 38, 40, 51, 61, 69, 84, 89, 98, 62, 58, 68, 85, 98, 118, 129, 127, 86, 80, 85, 101, 117, 136, 157, + 165, 32, 32, 36, 44, 58, 79, 88, 93, 31, 32, 35, 41, 54, 73, 81, 88, 32, 33, 36, 42, 53, 71, 78, 84, 32, 34, 38, 42, 52, 69, 76, 82, 34, + 36, 44, 50, 59, 75, 81, 84, 39, 39, 50, 58, 68, 84, 88, 90, 44, 42, 53, 63, 74, 90, 97, 97, 49, 46, 57, 67, 81, 97, 104, 105, 57, 53, 63, + 74, 90, 108, 111, 113, 65, 59, 68, 79, 97, 118, 123, 122, 71, 64, 73, 84, 102, 125, 135, 131, 81, 72, 80, 91, 110, 135, 145, 141, 87, 77, + 85, 96, 114, 140, 148, 151, 92, 83, 88, 102, 117, 133, 153, 163, 98, 88, 89, 103, 121, 141, 160, 169, 103, 94, 92, 103, 119, 137, 158, + 175, 32, 31, 32, 32, 34, 39, 44, 49, 57, 65, 71, 81, 87, 92, 98, 103, 32, 32, 33, 34, 36, 39, 42, 46, 53, 59, 64, 72, 77, 83, 88, 94, 36, + 35, 36, 38, 44, 50, 53, 57, 63, 68, 73, 80, 85, 88, 89, 92, 44, 41, 42, 42, 50, 58, 63, 67, 74, 79, 84, 91, 96, 102, 103, 103, 58, 54, + 53, 52, 59, 68, 74, 81, 90, 97, 102, 110, 114, 117, 121, 119, 79, 73, 71, 69, 75, 84, 90, 97, 108, 118, 125, 135, 140, 133, 141, 137, 88, + 81, 78, 76, 81, 88, 97, 104, 111, 123, 135, 145, 148, 153, 160, 158, 93, 88, 84, 82, 84, 90, 97, 105, 113, 122, 131, 141, 151, 163, 169, + 175, 32, 31, 32, 32, 36, 39, 44, 53, 58, 65, 79, 81, 88, 90, 93, 96, 31, 32, 32, 32, 35, 38, 42, 51, 55, 62, 75, 77, 83, 86, 88, 91, 31, + 32, 32, 32, 35, 38, 41, 50, 54, 60, 73, 75, 81, 84, 88, 91, 31, 32, 32, 33, 34, 37, 41, 49, 53, 59, 72, 74, 79, 82, 84, 87, 32, 32, 33, + 34, 36, 39, 42, 50, 53, 59, 71, 72, 78, 81, 84, 87, 32, 32, 34, 34, 37, 40, 42, 49, 53, 58, 70, 71, 77, 80, 83, 85, 32, 33, 34, 35, 38, + 40, 42, 49, 52, 58, 69, 70, 76, 78, 82, 86, 34, 34, 35, 37, 42, 45, 48, 54, 57, 63, 73, 75, 79, 79, 81, 83, 34, 34, 36, 37, 44, 47, 50, + 56, 59, 65, 75, 77, 81, 83, 84, 84, 36, 34, 37, 38, 48, 51, 54, 60, 63, 68, 78, 80, 85, 85, 86, 89, 39, 37, 39, 40, 50, 54, 58, 65, 68, + 73, 84, 85, 88, 89, 90, 89, 40, 38, 40, 41, 51, 55, 59, 67, 70, 75, 85, 87, 91, 92, 92, 95, 44, 41, 42, 43, 53, 58, 63, 71, 74, 79, 90, + 91, 97, 94, 97, 95, 47, 44, 45, 46, 56, 61, 66, 75, 79, 85, 95, 97, 99, 101, 98, 102, 49, 46, 46, 47, 57, 62, 67, 77, 81, 86, 97, 99, + 104, 102, 105, 102, 53, 49, 50, 50, 60, 65, 71, 82, 86, 92, 103, 105, 109, 108, 106, 110, 57, 53, 53, 53, 63, 68, 74, 86, 90, 97, 108, + 110, 111, 112, 113, 110, 59, 54, 54, 54, 64, 69, 75, 87, 91, 98, 111, 112, 119, 117, 115, 118, 65, 60, 59, 58, 68, 73, 79, 92, 97, 105, + 118, 119, 123, 123, 122, 119, 69, 63, 62, 62, 71, 76, 83, 96, 100, 109, 122, 124, 127, 125, 125, 128, 71, 65, 64, 63, 73, 78, 84, 97, + 102, 111, 125, 127, 135, 134, 131, 129, 79, 72, 71, 70, 79, 84, 90, 104, 109, 118, 133, 135, 137, 136, 136, 137, 81, 74, 72, 71, 80, 85, + 91, 105, 110, 120, 135, 137, 145, 143, 141, 138, 82, 75, 73, 72, 81, 86, 92, 106, 111, 121, 136, 139, 147, 148, 147, 149, 87, 79, 77, 76, + 85, 90, 96, 110, 114, 125, 140, 143, 148, 154, 151, 149, 90, 82, 80, 78, 87, 89, 99, 108, 113, 129, 135, 146, 153, 157, 160, 159, 92, 84, + 83, 81, 88, 90, 102, 106, 117, 128, 133, 150, 153, 158, 163, 160, 95, 87, 85, 83, 88, 92, 103, 105, 120, 125, 137, 148, 155, 164, 168, + 173, 98, 89, 88, 85, 89, 95, 103, 108, 121, 124, 141, 144, 160, 164, 169, 174, 100, 92, 91, 88, 90, 98, 103, 111, 120, 127, 139, 146, + 161, 165, 175, 179, 103, 94, 94, 90, 92, 101, 103, 114, 119, 131, 137, 150, 158, 170, 175, 180, 106, 97, 97, 93, 93, 104, 104, 118, 118, + 135, 135, 154, 155, 175, 176, 187, 32, 31, 31, 31, 32, 32, 32, 34, 34, 36, 39, 40, 44, 47, 49, 53, 57, 59, 65, 69, 71, 79, 81, 82, 87, + 90, 92, 95, 98, 100, 103, 106, 31, 32, 32, 32, 32, 32, 33, 34, 34, 34, 37, 38, 41, 44, 46, 49, 53, 54, 60, 63, 65, 72, 74, 75, 79, 82, + 84, 87, 89, 92, 94, 97, 32, 32, 32, 32, 33, 34, 34, 35, 36, 37, 39, 40, 42, 45, 46, 50, 53, 54, 59, 62, 64, 71, 72, 73, 77, 80, 83, 85, + 88, 91, 94, 97, 32, 32, 32, 33, 34, 34, 35, 37, 37, 38, 40, 41, 43, 46, 47, 50, 53, 54, 58, 62, 63, 70, 71, 72, 76, 78, 81, 83, 85, 88, + 90, 93, 36, 35, 35, 34, 36, 37, 38, 42, 44, 48, 50, 51, 53, 56, 57, 60, 63, 64, 68, 71, 73, 79, 80, 81, 85, 87, 88, 88, 89, 90, 92, 93, + 39, 38, 38, 37, 39, 40, 40, 45, 47, 51, 54, 55, 58, 61, 62, 65, 68, 69, 73, 76, 78, 84, 85, 86, 90, 89, 90, 92, 95, 98, 101, 104, 44, 42, + 41, 41, 42, 42, 42, 48, 50, 54, 58, 59, 63, 66, 67, 71, 74, 75, 79, 83, 84, 90, 91, 92, 96, 99, 102, 103, 103, 103, 103, 104, 53, 51, 50, + 49, 50, 49, 49, 54, 56, 60, 65, 67, 71, 75, 77, 82, 86, 87, 92, 96, 97, 104, 105, 106, 110, 108, 106, 105, 108, 111, 114, 118, 58, 55, + 54, 53, 53, 53, 52, 57, 59, 63, 68, 70, 74, 79, 81, 86, 90, 91, 97, 100, 102, 109, 110, 111, 114, 113, 117, 120, 121, 120, 119, 118, 65, + 62, 60, 59, 59, 58, 58, 63, 65, 68, 73, 75, 79, 85, 86, 92, 97, 98, 105, 109, 111, 118, 120, 121, 125, 129, 128, 125, 124, 127, 131, 135, + 79, 75, 73, 72, 71, 70, 69, 73, 75, 78, 84, 85, 90, 95, 97, 103, 108, 111, 118, 122, 125, 133, 135, 136, 140, 135, 133, 137, 141, 139, + 137, 135, 81, 77, 75, 74, 72, 71, 70, 75, 77, 80, 85, 87, 91, 97, 99, 105, 110, 112, 119, 124, 127, 135, 137, 139, 143, 146, 150, 148, + 144, 146, 150, 154, 88, 83, 81, 79, 78, 77, 76, 79, 81, 85, 88, 91, 97, 99, 104, 109, 111, 119, 123, 127, 135, 137, 145, 147, 148, 153, + 153, 155, 160, 161, 158, 155, 90, 86, 84, 82, 81, 80, 78, 79, 83, 85, 89, 92, 94, 101, 102, 108, 112, 117, 123, 125, 134, 136, 143, 148, + 154, 157, 158, 164, 164, 165, 170, 175, 93, 88, 88, 84, 84, 83, 82, 81, 84, 86, 90, 92, 97, 98, 105, 106, 113, 115, 122, 125, 131, 136, + 141, 147, 151, 160, 163, 168, 169, 175, 175, 176, 96, 91, 91, 87, 87, 85, 86, 83, 84, 89, 89, 95, 95, 102, 102, 110, 110, 118, 119, 128, + 129, 137, 138, 149, 149, 159, 160, 173, 174, 179, 180, 187, 31, 39, 65, 90, 32, 38, 60, 84, 32, 39, 59, 81, 33, 40, 58, 78, 34, 47, 65, + 83, 37, 54, 73, 89, 41, 58, 79, 94, 46, 62, 86, 102, 53, 68, 97, 112, 60, 73, 105, 123, 65, 78, 111, 134, 74, 85, 120, 143, 79, 90, 125, + 154, 84, 90, 128, 158, 89, 95, 124, 164, 94, 101, 131, 170, 31, 32, 32, 33, 34, 37, 41, 46, 53, 60, 65, 74, 79, 84, 89, 94, 39, 38, 39, + 40, 47, 54, 58, 62, 68, 73, 78, 85, 90, 90, 95, 101, 65, 60, 59, 58, 65, 73, 79, 86, 97, 105, 111, 120, 125, 128, 124, 131, 90, 84, 81, + 78, 83, 89, 94, 102, 112, 123, 134, 143, 154, 158, 164, 170, 32, 32, 36, 44, 58, 79, 88, 93, 31, 32, 35, 42, 55, 75, 83, 88, 31, 32, 35, + 41, 54, 73, 81, 88, 31, 32, 34, 41, 53, 72, 79, 84, 32, 33, 36, 42, 53, 71, 78, 84, 32, 34, 37, 42, 53, 70, 77, 83, 32, 34, 38, 42, 52, + 69, 76, 82, 34, 35, 42, 48, 57, 73, 79, 81, 34, 36, 44, 50, 59, 75, 81, 84, 36, 37, 48, 54, 63, 78, 85, 86, 39, 39, 50, 58, 68, 84, 88, + 90, 40, 40, 51, 59, 70, 85, 91, 92, 44, 42, 53, 63, 74, 90, 97, 97, 47, 45, 56, 66, 79, 95, 99, 98, 49, 46, 57, 67, 81, 97, 104, 105, 53, + 50, 60, 71, 86, 103, 109, 106, 57, 53, 63, 74, 90, 108, 111, 113, 59, 54, 64, 75, 91, 111, 119, 115, 65, 59, 68, 79, 97, 118, 123, 122, + 69, 62, 71, 83, 100, 122, 127, 125, 71, 64, 73, 84, 102, 125, 135, 131, 79, 71, 79, 90, 109, 133, 137, 136, 81, 72, 80, 91, 110, 135, + 145, 141, 82, 73, 81, 92, 111, 136, 147, 147, 87, 77, 85, 96, 114, 140, 148, 151, 90, 80, 87, 99, 113, 135, 153, 160, 92, 83, 88, 102, + 117, 133, 153, 163, 95, 85, 88, 103, 120, 137, 155, 168, 98, 88, 89, 103, 121, 141, 160, 169, 100, 91, 90, 103, 120, 139, 161, 175, 103, + 94, 92, 103, 119, 137, 158, 175, 106, 97, 93, 104, 118, 135, 155, 176, 32, 31, 31, 31, 32, 32, 32, 34, 34, 36, 39, 40, 44, 47, 49, 53, + 57, 59, 65, 69, 71, 79, 81, 82, 87, 90, 92, 95, 98, 100, 103, 106, 32, 32, 32, 32, 33, 34, 34, 35, 36, 37, 39, 40, 42, 45, 46, 50, 53, + 54, 59, 62, 64, 71, 72, 73, 77, 80, 83, 85, 88, 91, 94, 97, 36, 35, 35, 34, 36, 37, 38, 42, 44, 48, 50, 51, 53, 56, 57, 60, 63, 64, 68, + 71, 73, 79, 80, 81, 85, 87, 88, 88, 89, 90, 92, 93, 44, 42, 41, 41, 42, 42, 42, 48, 50, 54, 58, 59, 63, 66, 67, 71, 74, 75, 79, 83, 84, + 90, 91, 92, 96, 99, 102, 103, 103, 103, 103, 104, 58, 55, 54, 53, 53, 53, 52, 57, 59, 63, 68, 70, 74, 79, 81, 86, 90, 91, 97, 100, 102, + 109, 110, 111, 114, 113, 117, 120, 121, 120, 119, 118, 79, 75, 73, 72, 71, 70, 69, 73, 75, 78, 84, 85, 90, 95, 97, 103, 108, 111, 118, + 122, 125, 133, 135, 136, 140, 135, 133, 137, 141, 139, 137, 135, 88, 83, 81, 79, 78, 77, 76, 79, 81, 85, 88, 91, 97, 99, 104, 109, 111, + 119, 123, 127, 135, 137, 145, 147, 148, 153, 153, 155, 160, 161, 158, 155, 93, 88, 88, 84, 84, 83, 82, 81, 84, 86, 90, 92, 97, 98, 105, + 106, 113, 115, 122, 125, 131, 136, 141, 147, 151, 160, 163, 168, 169, 175, 175, 176, + ], + [ + 32, 45, 53, 63, 45, 55, 62, 67, 53, 62, 80, 84, 63, 67, 84, 101, 31, 36, 47, 48, 52, 60, 64, 67, 36, 43, 47, 46, 49, 55, 59, 63, 47, 47, + 53, 54, 55, 60, 63, 64, 48, 46, 54, 61, 65, 70, 71, 71, 52, 49, 55, 65, 71, 78, 81, 79, 60, 55, 60, 70, 78, 89, 89, 89, 64, 59, 63, 71, + 81, 89, 97, 99, 67, 63, 64, 71, 79, 89, 99, 104, 32, 30, 33, 36, 44, 48, 49, 51, 54, 57, 60, 64, 67, 68, 70, 72, 30, 31, 35, 39, 44, 46, + 46, 47, 50, 53, 55, 59, 61, 64, 66, 68, 33, 35, 39, 43, 46, 46, 45, 47, 49, 51, 53, 57, 59, 61, 63, 65, 36, 39, 43, 47, 47, 46, 45, 46, + 48, 50, 52, 55, 57, 58, 61, 63, 44, 44, 46, 47, 50, 51, 51, 51, 53, 54, 56, 59, 61, 61, 63, 62, 48, 46, 46, 46, 51, 54, 55, 56, 58, 60, + 61, 64, 65, 64, 66, 66, 49, 46, 45, 45, 51, 55, 58, 60, 62, 63, 65, 68, 69, 69, 69, 69, 51, 47, 47, 46, 51, 56, 60, 62, 65, 67, 69, 72, + 73, 74, 73, 73, 54, 50, 49, 48, 53, 58, 62, 65, 70, 73, 75, 78, 79, 79, 77, 77, 57, 53, 51, 50, 54, 60, 63, 67, 73, 76, 79, 82, 84, 83, + 82, 82, 60, 55, 53, 52, 56, 61, 65, 69, 75, 79, 82, 86, 88, 87, 86, 87, 64, 59, 57, 55, 59, 64, 68, 72, 78, 82, 86, 90, 93, 92, 91, 92, + 67, 61, 59, 57, 61, 65, 69, 73, 79, 84, 88, 93, 95, 96, 96, 96, 68, 64, 61, 58, 61, 64, 69, 74, 79, 83, 87, 92, 96, 99, 100, 101, 70, 66, + 63, 61, 63, 66, 69, 73, 77, 82, 86, 91, 96, 100, 103, 104, 72, 68, 65, 63, 62, 66, 69, 73, 77, 82, 87, 92, 96, 101, 104, 106, 32, 31, 30, + 30, 33, 35, 36, 41, 44, 49, 48, 48, 49, 50, 51, 52, 54, 55, 57, 59, 60, 63, 64, 65, 67, 68, 68, 69, 70, 71, 72, 73, 31, 31, 31, 31, 34, + 36, 38, 42, 44, 47, 47, 47, 47, 48, 48, 50, 51, 52, 54, 56, 57, 60, 61, 61, 63, 64, 65, 66, 67, 67, 68, 69, 30, 31, 31, 31, 35, 37, 39, + 42, 44, 47, 46, 46, 46, 47, 47, 48, 50, 51, 53, 54, 55, 58, 59, 60, 61, 63, 64, 65, 66, 67, 68, 69, 30, 31, 31, 32, 35, 37, 40, 42, 44, + 46, 45, 45, 45, 46, 46, 47, 49, 50, 52, 53, 54, 57, 58, 58, 60, 61, 62, 63, 63, 64, 65, 66, 33, 34, 35, 35, 39, 41, 43, 45, 46, 47, 46, + 46, 45, 46, 47, 47, 49, 49, 51, 53, 53, 56, 57, 57, 59, 60, 61, 62, 63, 64, 65, 66, 35, 36, 37, 37, 41, 43, 45, 46, 46, 47, 46, 46, 45, + 46, 46, 47, 48, 49, 50, 52, 53, 55, 56, 56, 58, 59, 60, 61, 62, 63, 64, 64, 36, 38, 39, 40, 43, 45, 47, 47, 47, 48, 46, 46, 45, 46, 46, + 47, 48, 48, 50, 51, 52, 54, 55, 55, 57, 58, 58, 59, 61, 62, 63, 64, 41, 42, 42, 42, 45, 46, 47, 48, 49, 50, 49, 49, 49, 50, 50, 50, 51, + 52, 53, 54, 55, 57, 58, 58, 60, 60, 59, 59, 60, 61, 61, 62, 44, 44, 44, 44, 46, 46, 47, 49, 50, 51, 51, 51, 51, 51, 51, 52, 53, 53, 54, + 56, 56, 59, 59, 59, 61, 61, 61, 62, 63, 62, 62, 62, 49, 47, 47, 46, 47, 47, 48, 50, 51, 53, 53, 53, 53, 54, 54, 54, 55, 55, 56, 58, 58, + 60, 61, 61, 63, 63, 64, 63, 63, 64, 65, 66, 48, 47, 46, 45, 46, 46, 46, 49, 51, 53, 54, 54, 55, 56, 56, 57, 58, 59, 60, 61, 61, 63, 64, + 64, 65, 65, 64, 65, 66, 66, 66, 66, 48, 47, 46, 45, 46, 46, 46, 49, 51, 53, 54, 55, 56, 57, 57, 58, 59, 60, 61, 62, 63, 65, 65, 65, 66, + 67, 68, 67, 67, 67, 68, 69, 49, 47, 46, 45, 45, 45, 45, 49, 51, 53, 55, 56, 58, 59, 60, 61, 62, 62, 63, 65, 65, 67, 68, 68, 69, 70, 69, + 69, 69, 70, 69, 69, 50, 48, 47, 46, 46, 46, 46, 50, 51, 54, 56, 57, 59, 61, 62, 63, 64, 65, 66, 68, 68, 70, 71, 71, 72, 71, 71, 72, 71, + 71, 71, 72, 51, 48, 47, 46, 47, 46, 46, 50, 51, 54, 56, 57, 60, 62, 62, 64, 65, 66, 67, 69, 69, 71, 72, 72, 73, 74, 74, 72, 73, 74, 73, + 73, 52, 50, 48, 47, 47, 47, 47, 50, 52, 54, 57, 58, 61, 63, 64, 66, 68, 68, 70, 72, 72, 75, 75, 75, 77, 76, 75, 76, 76, 74, 75, 76, 54, + 51, 50, 49, 49, 48, 48, 51, 53, 55, 58, 59, 62, 64, 65, 68, 70, 70, 73, 74, 75, 77, 78, 78, 79, 78, 79, 78, 77, 78, 77, 77, 55, 52, 51, + 50, 49, 49, 48, 52, 53, 55, 59, 60, 62, 65, 66, 68, 70, 71, 73, 75, 76, 78, 79, 79, 80, 81, 80, 80, 81, 79, 79, 81, 57, 54, 53, 52, 51, + 50, 50, 53, 54, 56, 60, 61, 63, 66, 67, 70, 73, 73, 76, 78, 79, 82, 82, 83, 84, 83, 83, 83, 82, 83, 82, 81, 59, 56, 54, 53, 53, 52, 51, + 54, 56, 58, 61, 62, 65, 68, 69, 72, 74, 75, 78, 80, 81, 84, 85, 85, 86, 86, 86, 84, 85, 84, 84, 85, 60, 57, 55, 54, 53, 53, 52, 55, 56, + 58, 61, 63, 65, 68, 69, 72, 75, 76, 79, 81, 82, 85, 86, 86, 88, 88, 87, 88, 86, 87, 87, 85, 63, 60, 58, 57, 56, 55, 54, 57, 59, 60, 63, + 65, 67, 70, 71, 75, 77, 78, 82, 84, 85, 89, 89, 90, 92, 89, 91, 89, 90, 89, 88, 89, 64, 61, 59, 58, 57, 56, 55, 58, 59, 61, 64, 65, 68, + 71, 72, 75, 78, 79, 82, 85, 86, 89, 90, 91, 93, 94, 92, 92, 91, 91, 92, 90, 65, 61, 60, 58, 57, 56, 55, 58, 59, 61, 64, 65, 68, 71, 72, + 75, 78, 79, 83, 85, 86, 90, 91, 91, 93, 94, 95, 94, 94, 94, 93, 94, 67, 63, 61, 60, 59, 58, 57, 60, 61, 63, 65, 66, 69, 72, 73, 77, 79, + 80, 84, 86, 88, 92, 93, 93, 95, 95, 96, 97, 96, 95, 96, 94, 68, 64, 63, 61, 60, 59, 58, 60, 61, 63, 65, 67, 70, 71, 74, 76, 78, 81, 83, + 86, 88, 89, 94, 94, 95, 97, 97, 98, 99, 99, 97, 99, 68, 65, 64, 62, 61, 60, 58, 59, 61, 64, 64, 68, 69, 71, 74, 75, 79, 80, 83, 86, 87, + 91, 92, 95, 96, 97, 99, 99, 100, 100, 101, 99, 69, 66, 65, 63, 62, 61, 59, 59, 62, 63, 65, 67, 69, 72, 72, 76, 78, 80, 83, 84, 88, 89, + 92, 94, 97, 98, 99, 101, 100, 102, 102, 104, 70, 67, 66, 63, 63, 62, 61, 60, 63, 63, 66, 67, 69, 71, 73, 76, 77, 81, 82, 85, 86, 90, 91, + 94, 96, 99, 100, 100, 103, 102, 104, 104, 71, 67, 67, 64, 64, 63, 62, 61, 62, 64, 66, 67, 70, 71, 74, 74, 78, 79, 83, 84, 87, 89, 91, 94, + 95, 99, 100, 102, 102, 104, 104, 106, 72, 68, 68, 65, 65, 64, 63, 61, 62, 65, 66, 68, 69, 71, 73, 75, 77, 79, 82, 84, 87, 88, 92, 93, 96, + 97, 101, 102, 104, 104, 106, 106, 73, 69, 69, 66, 66, 64, 64, 62, 62, 66, 66, 69, 69, 72, 73, 76, 77, 81, 81, 85, 85, 89, 90, 94, 94, 99, + 99, 104, 104, 106, 106, 108, 31, 47, 54, 64, 38, 46, 50, 60, 46, 53, 57, 62, 46, 56, 66, 71, 50, 59, 74, 79, 57, 64, 82, 88, 61, 65, 85, + 97, 65, 67, 82, 99, 31, 38, 46, 46, 50, 57, 61, 65, 47, 46, 53, 56, 59, 64, 65, 67, 54, 50, 57, 66, 74, 82, 85, 82, 64, 60, 62, 71, 79, + 88, 97, 99, 32, 34, 48, 49, 54, 63, 67, 69, 31, 36, 46, 46, 50, 58, 62, 65, 33, 40, 47, 46, 49, 56, 59, 62, 37, 44, 47, 45, 48, 54, 57, + 60, 44, 46, 51, 51, 53, 59, 60, 61, 48, 46, 53, 56, 58, 64, 64, 64, 49, 45, 53, 58, 62, 67, 70, 68, 51, 47, 54, 60, 65, 71, 73, 72, 54, + 49, 55, 62, 70, 77, 77, 76, 57, 51, 56, 64, 73, 82, 83, 81, 60, 53, 58, 65, 75, 85, 89, 85, 64, 57, 61, 68, 78, 89, 93, 89, 66, 59, 63, + 69, 79, 91, 94, 93, 68, 61, 63, 71, 79, 87, 96, 98, 70, 63, 63, 70, 80, 89, 97, 100, 72, 65, 63, 69, 77, 86, 95, 102, 32, 31, 33, 37, 44, + 48, 49, 51, 54, 57, 60, 64, 66, 68, 70, 72, 34, 36, 40, 44, 46, 46, 45, 47, 49, 51, 53, 57, 59, 61, 63, 65, 48, 46, 47, 47, 51, 53, 53, + 54, 55, 56, 58, 61, 63, 63, 63, 63, 49, 46, 46, 45, 51, 56, 58, 60, 62, 64, 65, 68, 69, 71, 70, 69, 54, 50, 49, 48, 53, 58, 62, 65, 70, + 73, 75, 78, 79, 79, 80, 77, 63, 58, 56, 54, 59, 64, 67, 71, 77, 82, 85, 89, 91, 87, 89, 86, 67, 62, 59, 57, 60, 64, 70, 73, 77, 83, 89, + 93, 94, 96, 97, 95, 69, 65, 62, 60, 61, 64, 68, 72, 76, 81, 85, 89, 93, 98, 100, 102, 32, 31, 34, 37, 48, 48, 49, 52, 54, 57, 63, 64, 67, + 68, 69, 69, 31, 31, 35, 38, 47, 47, 47, 50, 51, 54, 60, 61, 63, 64, 65, 66, 31, 32, 36, 39, 46, 46, 46, 48, 50, 53, 58, 59, 62, 63, 65, + 66, 30, 32, 36, 40, 46, 45, 45, 48, 49, 52, 57, 58, 60, 61, 62, 63, 33, 36, 40, 43, 47, 46, 46, 47, 49, 51, 56, 57, 59, 60, 62, 63, 35, + 38, 42, 45, 47, 46, 45, 47, 48, 50, 55, 56, 58, 60, 61, 61, 37, 40, 44, 47, 47, 46, 45, 47, 48, 50, 54, 55, 57, 58, 60, 61, 42, 43, 45, + 47, 50, 50, 49, 50, 51, 53, 57, 58, 59, 58, 59, 59, 44, 44, 46, 47, 51, 51, 51, 52, 53, 54, 59, 59, 60, 61, 61, 60, 49, 46, 47, 48, 53, + 53, 53, 54, 55, 57, 60, 61, 63, 62, 62, 63, 48, 46, 46, 47, 53, 54, 56, 57, 58, 60, 64, 64, 64, 64, 64, 63, 48, 45, 46, 46, 53, 55, 56, + 58, 59, 61, 65, 65, 66, 66, 65, 66, 49, 45, 45, 46, 53, 56, 58, 61, 62, 64, 67, 68, 70, 67, 68, 66, 50, 46, 46, 46, 54, 56, 59, 63, 65, + 66, 70, 71, 70, 71, 68, 70, 51, 47, 47, 47, 54, 57, 60, 64, 65, 68, 71, 72, 73, 71, 72, 70, 52, 48, 47, 47, 54, 57, 61, 66, 68, 71, 75, + 75, 76, 75, 73, 73, 54, 49, 49, 48, 55, 58, 62, 68, 70, 73, 77, 78, 77, 77, 76, 74, 54, 50, 49, 49, 55, 59, 62, 68, 70, 74, 78, 79, 81, + 79, 77, 78, 57, 52, 51, 50, 56, 60, 64, 70, 73, 76, 82, 82, 83, 82, 81, 78, 59, 54, 52, 52, 58, 61, 65, 72, 74, 78, 84, 85, 85, 83, 82, + 82, 60, 54, 53, 52, 58, 62, 65, 72, 75, 79, 85, 86, 89, 87, 85, 82, 63, 57, 56, 55, 60, 64, 67, 75, 77, 82, 89, 90, 90, 88, 87, 86, 64, + 58, 57, 55, 61, 64, 68, 75, 78, 82, 89, 90, 93, 91, 89, 87, 64, 59, 57, 56, 61, 65, 68, 75, 78, 83, 90, 91, 94, 93, 92, 91, 66, 60, 59, + 57, 63, 66, 69, 77, 79, 84, 91, 93, 94, 95, 93, 91, 67, 61, 60, 58, 63, 65, 70, 75, 78, 85, 88, 93, 96, 97, 97, 95, 68, 62, 61, 59, 63, + 64, 71, 74, 79, 84, 87, 94, 96, 97, 98, 96, 69, 63, 62, 60, 63, 65, 71, 72, 80, 82, 88, 93, 96, 99, 100, 101, 70, 64, 63, 60, 63, 66, 70, + 73, 80, 81, 89, 90, 97, 99, 100, 101, 71, 65, 64, 61, 63, 67, 70, 74, 78, 82, 88, 90, 97, 99, 102, 103, 72, 65, 65, 62, 63, 68, 69, 75, + 77, 83, 86, 92, 95, 100, 102, 103, 73, 66, 66, 63, 63, 69, 69, 76, 76, 84, 84, 93, 93, 101, 101, 105, 32, 31, 31, 30, 33, 35, 37, 42, 44, + 49, 48, 48, 49, 50, 51, 52, 54, 54, 57, 59, 60, 63, 64, 64, 66, 67, 68, 69, 70, 71, 72, 73, 31, 31, 32, 32, 36, 38, 40, 43, 44, 46, 46, + 45, 45, 46, 47, 48, 49, 50, 52, 54, 54, 57, 58, 59, 60, 61, 62, 63, 64, 65, 65, 66, 34, 35, 36, 36, 40, 42, 44, 45, 46, 47, 46, 46, 45, + 46, 47, 47, 49, 49, 51, 52, 53, 56, 57, 57, 59, 60, 61, 62, 63, 64, 65, 66, 37, 38, 39, 40, 43, 45, 47, 47, 47, 48, 47, 46, 46, 46, 47, + 47, 48, 49, 50, 52, 52, 55, 55, 56, 57, 58, 59, 60, 60, 61, 62, 63, 48, 47, 46, 46, 47, 47, 47, 50, 51, 53, 53, 53, 53, 54, 54, 54, 55, + 55, 56, 58, 58, 60, 61, 61, 63, 63, 63, 63, 63, 63, 63, 63, 48, 47, 46, 45, 46, 46, 46, 50, 51, 53, 54, 55, 56, 56, 57, 57, 58, 59, 60, + 61, 62, 64, 64, 65, 66, 65, 64, 65, 66, 67, 68, 69, 49, 47, 46, 45, 46, 45, 45, 49, 51, 53, 56, 56, 58, 59, 60, 61, 62, 62, 64, 65, 65, + 67, 68, 68, 69, 70, 71, 71, 70, 70, 69, 69, 52, 50, 48, 48, 47, 47, 47, 50, 52, 54, 57, 58, 61, 63, 64, 66, 68, 68, 70, 72, 72, 75, 75, + 75, 77, 75, 74, 72, 73, 74, 75, 76, 54, 51, 50, 49, 49, 48, 48, 51, 53, 55, 58, 59, 62, 65, 65, 68, 70, 70, 73, 74, 75, 77, 78, 78, 79, + 78, 79, 80, 80, 78, 77, 76, 57, 54, 53, 52, 51, 50, 50, 53, 54, 57, 60, 61, 64, 66, 68, 71, 73, 74, 76, 78, 79, 82, 82, 83, 84, 85, 84, + 82, 81, 82, 83, 84, 63, 60, 58, 57, 56, 55, 54, 57, 59, 60, 64, 65, 67, 70, 71, 75, 77, 78, 82, 84, 85, 89, 89, 90, 91, 88, 87, 88, 89, + 88, 86, 84, 64, 61, 59, 58, 57, 56, 55, 58, 59, 61, 64, 65, 68, 71, 72, 75, 78, 79, 82, 85, 86, 90, 90, 91, 93, 93, 94, 93, 90, 90, 92, + 93, 67, 63, 62, 60, 59, 58, 57, 59, 60, 63, 64, 66, 70, 70, 73, 76, 77, 81, 83, 85, 89, 90, 93, 94, 94, 96, 96, 96, 97, 97, 95, 93, 68, + 64, 63, 61, 60, 60, 58, 58, 61, 62, 64, 66, 67, 71, 71, 75, 77, 79, 82, 83, 87, 88, 91, 93, 95, 97, 97, 99, 99, 99, 100, 101, 69, 65, 65, + 62, 62, 61, 60, 59, 61, 62, 64, 65, 68, 68, 72, 73, 76, 77, 81, 82, 85, 87, 89, 92, 93, 97, 98, 100, 100, 102, 102, 101, 69, 66, 66, 63, + 63, 61, 61, 59, 60, 63, 63, 66, 66, 70, 70, 73, 74, 78, 78, 82, 82, 86, 87, 91, 91, 95, 96, 101, 101, 103, 103, 105, 31, 48, 57, 68, 32, + 46, 53, 63, 36, 46, 51, 60, 40, 46, 50, 58, 44, 51, 54, 61, 46, 54, 60, 64, 45, 56, 64, 67, 47, 57, 68, 71, 49, 58, 73, 77, 52, 60, 76, + 82, 54, 62, 79, 87, 58, 64, 82, 91, 60, 66, 84, 95, 62, 64, 84, 97, 64, 66, 81, 99, 65, 68, 83, 100, 31, 32, 36, 40, 44, 46, 45, 47, 49, + 52, 54, 58, 60, 62, 64, 65, 48, 46, 46, 46, 51, 54, 56, 57, 58, 60, 62, 64, 66, 64, 66, 68, 57, 53, 51, 50, 54, 60, 64, 68, 73, 76, 79, + 82, 84, 84, 81, 83, 68, 63, 60, 58, 61, 64, 67, 71, 77, 82, 87, 91, 95, 97, 99, 100, 32, 34, 48, 49, 54, 63, 67, 69, 31, 35, 47, 47, 51, + 60, 63, 65, 31, 36, 46, 46, 50, 58, 62, 65, 30, 36, 46, 45, 49, 57, 60, 62, 33, 40, 47, 46, 49, 56, 59, 62, 35, 42, 47, 45, 48, 55, 58, + 61, 37, 44, 47, 45, 48, 54, 57, 60, 42, 45, 50, 49, 51, 57, 59, 59, 44, 46, 51, 51, 53, 59, 60, 61, 49, 47, 53, 53, 55, 60, 63, 62, 48, + 46, 53, 56, 58, 64, 64, 64, 48, 46, 53, 56, 59, 65, 66, 65, 49, 45, 53, 58, 62, 67, 70, 68, 50, 46, 54, 59, 65, 70, 70, 68, 51, 47, 54, + 60, 65, 71, 73, 72, 52, 47, 54, 61, 68, 75, 76, 73, 54, 49, 55, 62, 70, 77, 77, 76, 54, 49, 55, 62, 70, 78, 81, 77, 57, 51, 56, 64, 73, + 82, 83, 81, 59, 52, 58, 65, 74, 84, 85, 82, 60, 53, 58, 65, 75, 85, 89, 85, 63, 56, 60, 67, 77, 89, 90, 87, 64, 57, 61, 68, 78, 89, 93, + 89, 64, 57, 61, 68, 78, 90, 94, 92, 66, 59, 63, 69, 79, 91, 94, 93, 67, 60, 63, 70, 78, 88, 96, 97, 68, 61, 63, 71, 79, 87, 96, 98, 69, + 62, 63, 71, 80, 88, 96, 100, 70, 63, 63, 70, 80, 89, 97, 100, 71, 64, 63, 70, 78, 88, 97, 102, 72, 65, 63, 69, 77, 86, 95, 102, 73, 66, + 63, 69, 76, 84, 93, 101, 32, 31, 31, 30, 33, 35, 37, 42, 44, 49, 48, 48, 49, 50, 51, 52, 54, 54, 57, 59, 60, 63, 64, 64, 66, 67, 68, 69, + 70, 71, 72, 73, 34, 35, 36, 36, 40, 42, 44, 45, 46, 47, 46, 46, 45, 46, 47, 47, 49, 49, 51, 52, 53, 56, 57, 57, 59, 60, 61, 62, 63, 64, + 65, 66, 48, 47, 46, 46, 47, 47, 47, 50, 51, 53, 53, 53, 53, 54, 54, 54, 55, 55, 56, 58, 58, 60, 61, 61, 63, 63, 63, 63, 63, 63, 63, 63, + 49, 47, 46, 45, 46, 45, 45, 49, 51, 53, 56, 56, 58, 59, 60, 61, 62, 62, 64, 65, 65, 67, 68, 68, 69, 70, 71, 71, 70, 70, 69, 69, 54, 51, + 50, 49, 49, 48, 48, 51, 53, 55, 58, 59, 62, 65, 65, 68, 70, 70, 73, 74, 75, 77, 78, 78, 79, 78, 79, 80, 80, 78, 77, 76, 63, 60, 58, 57, + 56, 55, 54, 57, 59, 60, 64, 65, 67, 70, 71, 75, 77, 78, 82, 84, 85, 89, 89, 90, 91, 88, 87, 88, 89, 88, 86, 84, 67, 63, 62, 60, 59, 58, + 57, 59, 60, 63, 64, 66, 70, 70, 73, 76, 77, 81, 83, 85, 89, 90, 93, 94, 94, 96, 96, 96, 97, 97, 95, 93, 69, 65, 65, 62, 62, 61, 60, 59, + 61, 62, 64, 65, 68, 68, 72, 73, 76, 77, 81, 82, 85, 87, 89, 92, 93, 97, 98, 100, 100, 102, 102, 101, + ], + ], + [ + [ + 32, 37, 58, 81, 37, 54, 72, 91, 58, 72, 102, 121, 81, 91, 121, 156, 32, 32, 35, 42, 53, 68, 78, 90, 32, 33, 36, 42, 51, 64, 74, 84, 35, + 36, 46, 52, 60, 72, 80, 87, 42, 42, 52, 63, 73, 84, 92, 98, 53, 51, 60, 73, 86, 100, 109, 114, 68, 64, 72, 84, 100, 117, 128, 133, 78, + 74, 80, 92, 109, 128, 140, 155, 90, 84, 87, 98, 114, 133, 155, 168, 32, 31, 31, 32, 34, 36, 41, 47, 54, 59, 65, 74, 82, 87, 92, 97, 31, + 32, 32, 32, 34, 35, 39, 45, 50, 55, 61, 69, 76, 81, 87, 92, 31, 32, 33, 33, 35, 36, 40, 44, 49, 54, 59, 67, 73, 78, 83, 88, 32, 32, 33, + 35, 37, 38, 41, 45, 49, 53, 58, 65, 71, 75, 80, 86, 34, 34, 35, 37, 39, 42, 46, 50, 54, 58, 63, 70, 76, 80, 84, 85, 36, 35, 36, 38, 42, + 48, 52, 56, 60, 64, 68, 75, 80, 85, 90, 91, 41, 39, 40, 41, 46, 52, 57, 62, 67, 71, 75, 83, 88, 92, 95, 97, 47, 45, 44, 45, 50, 56, 62, + 69, 75, 79, 84, 91, 97, 100, 102, 104, 54, 50, 49, 49, 54, 60, 67, 75, 82, 87, 92, 100, 106, 110, 109, 112, 59, 55, 54, 53, 58, 64, 71, + 79, 87, 92, 98, 106, 112, 117, 117, 121, 65, 61, 59, 58, 63, 68, 75, 84, 92, 98, 105, 114, 120, 125, 126, 130, 74, 69, 67, 65, 70, 75, + 83, 91, 100, 106, 114, 123, 131, 135, 137, 140, 82, 76, 73, 71, 76, 80, 88, 97, 106, 112, 120, 131, 139, 144, 148, 150, 87, 81, 78, 75, + 80, 85, 92, 100, 110, 117, 125, 135, 144, 150, 155, 162, 92, 87, 83, 80, 84, 90, 95, 102, 109, 117, 126, 137, 148, 155, 162, 168, 97, 92, + 88, 86, 85, 91, 97, 104, 112, 121, 130, 140, 150, 162, 168, 174, 32, 31, 31, 31, 31, 31, 32, 32, 34, 35, 36, 39, 41, 44, 47, 48, 54, 56, + 59, 64, 65, 71, 74, 80, 82, 83, 87, 90, 92, 95, 97, 100, 31, 32, 32, 32, 32, 32, 32, 33, 34, 35, 35, 38, 40, 42, 45, 46, 51, 53, 56, 61, + 62, 68, 71, 76, 78, 78, 83, 85, 88, 90, 92, 95, 31, 32, 32, 32, 32, 32, 32, 33, 34, 34, 35, 38, 39, 42, 45, 45, 50, 52, 55, 60, 61, 67, + 69, 74, 76, 77, 81, 84, 87, 89, 92, 95, 31, 32, 32, 32, 32, 32, 32, 33, 33, 34, 34, 37, 38, 41, 44, 44, 49, 51, 54, 58, 59, 65, 68, 72, + 74, 75, 79, 81, 84, 86, 88, 90, 31, 32, 32, 32, 33, 33, 33, 34, 35, 36, 36, 39, 40, 42, 44, 45, 49, 51, 54, 58, 59, 64, 67, 71, 73, 74, + 78, 80, 83, 85, 88, 90, 31, 32, 32, 32, 33, 33, 34, 34, 35, 36, 36, 39, 40, 42, 45, 45, 50, 51, 54, 58, 59, 64, 67, 71, 73, 74, 78, 80, + 82, 84, 86, 89, 32, 32, 32, 32, 33, 34, 35, 36, 37, 38, 38, 40, 41, 42, 45, 46, 49, 51, 53, 57, 58, 63, 65, 69, 71, 72, 75, 78, 80, 83, + 86, 89, 32, 33, 33, 33, 34, 34, 36, 36, 38, 39, 40, 42, 43, 44, 47, 47, 51, 53, 55, 59, 60, 65, 67, 71, 73, 73, 77, 78, 80, 82, 84, 86, + 34, 34, 34, 33, 35, 35, 37, 38, 39, 42, 42, 45, 46, 47, 50, 51, 54, 56, 58, 62, 63, 68, 70, 74, 76, 76, 80, 82, 84, 85, 85, 86, 35, 35, + 34, 34, 36, 36, 38, 39, 42, 46, 47, 49, 50, 52, 55, 55, 59, 60, 62, 66, 67, 72, 74, 78, 79, 80, 83, 84, 85, 87, 90, 92, 36, 35, 35, 34, + 36, 36, 38, 40, 42, 47, 48, 50, 52, 54, 56, 57, 60, 61, 64, 67, 68, 73, 75, 79, 80, 81, 85, 87, 90, 91, 91, 92, 39, 38, 38, 37, 39, 39, + 40, 42, 45, 49, 50, 54, 55, 58, 60, 61, 65, 66, 69, 72, 73, 78, 80, 84, 86, 86, 90, 91, 91, 92, 95, 97, 41, 40, 39, 38, 40, 40, 41, 43, + 46, 50, 52, 55, 57, 60, 62, 63, 67, 69, 71, 75, 75, 80, 83, 86, 88, 89, 92, 93, 95, 97, 97, 98, 44, 42, 42, 41, 42, 42, 42, 44, 47, 52, + 54, 58, 60, 63, 66, 67, 71, 73, 75, 79, 79, 84, 86, 90, 92, 92, 96, 98, 98, 98, 101, 104, 47, 45, 45, 44, 44, 45, 45, 47, 50, 55, 56, 60, + 62, 66, 69, 70, 75, 77, 79, 83, 84, 89, 91, 95, 97, 97, 100, 99, 102, 105, 104, 104, 48, 46, 45, 44, 45, 45, 46, 47, 51, 55, 57, 61, 63, + 67, 70, 71, 76, 78, 80, 84, 85, 90, 93, 96, 98, 99, 102, 106, 106, 105, 108, 111, 54, 51, 50, 49, 49, 50, 49, 51, 54, 59, 60, 65, 67, 71, + 75, 76, 82, 84, 87, 91, 92, 97, 100, 104, 106, 106, 110, 108, 109, 112, 112, 111, 56, 53, 52, 51, 51, 51, 51, 53, 56, 60, 61, 66, 69, 73, + 77, 78, 84, 86, 89, 93, 94, 100, 102, 106, 108, 109, 112, 113, 115, 114, 116, 119, 59, 56, 55, 54, 54, 54, 53, 55, 58, 62, 64, 69, 71, + 75, 79, 80, 87, 89, 92, 97, 98, 103, 106, 110, 112, 113, 117, 118, 117, 121, 121, 119, 64, 61, 60, 58, 58, 58, 57, 59, 62, 66, 67, 72, + 75, 79, 83, 84, 91, 93, 97, 102, 103, 109, 112, 116, 118, 119, 122, 121, 125, 123, 125, 128, 65, 62, 61, 59, 59, 59, 58, 60, 63, 67, 68, + 73, 75, 79, 84, 85, 92, 94, 98, 103, 105, 111, 114, 118, 120, 121, 125, 129, 126, 129, 130, 129, 71, 68, 67, 65, 64, 64, 63, 65, 68, 72, + 73, 78, 80, 84, 89, 90, 97, 100, 103, 109, 111, 117, 120, 125, 127, 128, 133, 130, 134, 133, 133, 137, 74, 71, 69, 68, 67, 67, 65, 67, + 70, 74, 75, 80, 83, 86, 91, 93, 100, 102, 106, 112, 114, 120, 123, 128, 131, 131, 135, 137, 137, 138, 140, 137, 80, 76, 74, 72, 71, 71, + 69, 71, 74, 78, 79, 84, 86, 90, 95, 96, 104, 106, 110, 116, 118, 125, 128, 134, 136, 137, 142, 141, 142, 143, 143, 147, 82, 78, 76, 74, + 73, 73, 71, 73, 76, 79, 80, 86, 88, 92, 97, 98, 106, 108, 112, 118, 120, 127, 131, 136, 139, 139, 144, 147, 148, 147, 150, 148, 83, 78, + 77, 75, 74, 74, 72, 73, 76, 80, 81, 86, 89, 92, 97, 99, 106, 109, 113, 119, 121, 128, 131, 137, 139, 140, 145, 150, 152, 155, 152, 157, + 87, 83, 81, 79, 78, 78, 75, 77, 80, 83, 85, 90, 92, 96, 100, 102, 110, 112, 117, 122, 125, 133, 135, 142, 144, 145, 150, 151, 155, 158, + 162, 158, 90, 85, 84, 81, 80, 80, 78, 78, 82, 84, 87, 91, 93, 98, 99, 106, 108, 113, 118, 121, 129, 130, 137, 141, 147, 150, 151, 156, + 156, 161, 164, 169, 92, 88, 87, 84, 83, 82, 80, 80, 84, 85, 90, 91, 95, 98, 102, 106, 109, 115, 117, 125, 126, 134, 137, 142, 148, 152, + 155, 156, 162, 162, 168, 170, 95, 90, 89, 86, 85, 84, 83, 82, 85, 87, 91, 92, 97, 98, 105, 105, 112, 114, 121, 123, 129, 133, 138, 143, + 147, 155, 158, 161, 162, 168, 168, 174, 97, 92, 92, 88, 88, 86, 86, 84, 85, 90, 91, 95, 97, 101, 104, 108, 112, 116, 121, 125, 130, 133, + 140, 143, 150, 152, 162, 164, 168, 168, 174, 175, 100, 95, 95, 90, 90, 89, 89, 86, 86, 92, 92, 97, 98, 104, 104, 111, 111, 119, 119, 128, + 129, 137, 137, 147, 148, 157, 158, 169, 170, 174, 175, 181, 32, 35, 59, 83, 32, 36, 57, 78, 34, 47, 65, 82, 41, 53, 78, 97, 51, 61, 92, + 111, 65, 73, 108, 129, 75, 81, 117, 148, 86, 92, 119, 154, 32, 32, 34, 41, 51, 65, 75, 86, 35, 36, 47, 53, 61, 73, 81, 92, 59, 57, 65, + 78, 92, 108, 117, 119, 83, 78, 82, 97, 111, 129, 148, 154, 32, 31, 35, 44, 53, 65, 82, 90, 31, 32, 34, 41, 50, 61, 76, 85, 31, 33, 35, + 42, 49, 59, 73, 81, 32, 34, 37, 42, 49, 58, 71, 79, 34, 35, 41, 48, 54, 63, 76, 81, 36, 36, 46, 54, 60, 68, 80, 87, 41, 40, 49, 60, 67, + 76, 88, 93, 47, 44, 53, 66, 75, 84, 97, 101, 53, 50, 57, 71, 82, 92, 106, 108, 58, 54, 61, 75, 87, 98, 112, 116, 65, 59, 66, 79, 92, 105, + 120, 124, 74, 67, 73, 86, 100, 113, 131, 134, 82, 73, 79, 92, 105, 120, 139, 142, 87, 78, 83, 96, 110, 125, 144, 153, 92, 83, 84, 97, + 114, 132, 150, 157, 97, 88, 86, 97, 111, 128, 147, 163, 32, 31, 31, 32, 34, 36, 41, 47, 53, 58, 65, 74, 82, 87, 92, 97, 31, 32, 33, 34, + 35, 36, 40, 44, 50, 54, 59, 67, 73, 78, 83, 88, 35, 34, 35, 37, 41, 46, 49, 53, 57, 61, 66, 73, 79, 83, 84, 86, 44, 41, 42, 42, 48, 54, + 60, 66, 71, 75, 79, 86, 92, 96, 97, 97, 53, 50, 49, 49, 54, 60, 67, 75, 82, 87, 92, 100, 105, 110, 114, 111, 65, 61, 59, 58, 63, 68, 76, + 84, 92, 98, 105, 113, 120, 125, 132, 128, 82, 76, 73, 71, 76, 80, 88, 97, 106, 112, 120, 131, 139, 144, 150, 147, 90, 85, 81, 79, 81, 87, + 93, 101, 108, 116, 124, 134, 142, 153, 157, 163, 32, 31, 31, 32, 35, 36, 44, 47, 53, 62, 65, 79, 82, 88, 90, 93, 31, 32, 32, 32, 35, 35, + 42, 45, 51, 59, 62, 75, 78, 83, 86, 88, 31, 32, 32, 32, 34, 35, 41, 45, 50, 58, 61, 74, 76, 82, 85, 88, 31, 32, 32, 33, 34, 34, 41, 44, + 49, 57, 59, 72, 74, 79, 82, 84, 31, 32, 33, 34, 35, 36, 42, 44, 49, 57, 59, 71, 73, 79, 81, 84, 32, 32, 33, 34, 36, 36, 42, 45, 50, 57, + 59, 71, 73, 78, 80, 82, 32, 33, 34, 35, 37, 38, 42, 45, 49, 56, 58, 69, 71, 76, 79, 83, 32, 33, 34, 36, 39, 40, 44, 47, 51, 58, 60, 71, + 73, 76, 78, 80, 34, 34, 35, 37, 41, 42, 48, 50, 54, 61, 63, 73, 76, 81, 81, 80, 35, 34, 36, 38, 45, 47, 52, 55, 59, 65, 67, 77, 79, 82, + 83, 86, 36, 34, 36, 38, 46, 48, 54, 56, 60, 66, 68, 78, 80, 85, 87, 86, 39, 37, 39, 40, 48, 50, 58, 60, 65, 71, 73, 84, 86, 89, 88, 91, + 41, 39, 40, 41, 49, 51, 60, 62, 67, 74, 76, 86, 88, 91, 93, 91, 44, 41, 42, 43, 51, 53, 63, 66, 71, 78, 79, 90, 92, 97, 94, 97, 47, 44, + 44, 45, 53, 56, 66, 69, 75, 82, 84, 95, 97, 98, 101, 98, 48, 45, 45, 46, 54, 56, 67, 70, 76, 83, 85, 96, 98, 104, 101, 105, 53, 49, 50, + 50, 57, 60, 71, 75, 82, 90, 92, 103, 106, 107, 108, 105, 55, 51, 51, 51, 59, 61, 72, 77, 84, 92, 94, 106, 108, 111, 110, 112, 58, 54, 54, + 54, 61, 63, 75, 79, 87, 95, 98, 110, 112, 117, 116, 113, 63, 58, 58, 57, 65, 67, 78, 83, 91, 100, 103, 116, 118, 119, 119, 121, 65, 60, + 59, 58, 66, 68, 79, 84, 92, 102, 105, 118, 120, 127, 124, 122, 71, 65, 64, 63, 71, 73, 84, 89, 97, 108, 111, 125, 127, 129, 129, 130, 74, + 68, 67, 66, 73, 75, 86, 91, 100, 110, 113, 128, 131, 135, 134, 130, 79, 72, 71, 70, 77, 79, 90, 95, 104, 115, 118, 133, 136, 140, 139, + 140, 82, 75, 73, 72, 79, 81, 92, 97, 105, 117, 120, 136, 139, 145, 142, 140, 82, 75, 74, 72, 79, 81, 92, 97, 106, 117, 121, 136, 139, + 148, 150, 149, 87, 79, 78, 76, 83, 85, 96, 100, 110, 120, 125, 141, 144, 148, 153, 150, 89, 82, 81, 78, 83, 87, 97, 99, 113, 118, 128, + 139, 145, 153, 157, 161, 92, 84, 83, 80, 84, 89, 97, 101, 114, 116, 132, 135, 150, 153, 157, 162, 94, 86, 85, 82, 85, 92, 97, 104, 112, + 119, 130, 136, 151, 154, 163, 166, 97, 88, 88, 85, 86, 94, 97, 107, 111, 123, 128, 140, 147, 159, 163, 167, 99, 91, 91, 87, 87, 97, 97, + 110, 110, 126, 126, 144, 144, 163, 163, 173, 32, 31, 31, 31, 31, 32, 32, 32, 34, 35, 36, 39, 41, 44, 47, 48, 53, 55, 58, 63, 65, 71, 74, + 79, 82, 82, 87, 89, 92, 94, 97, 99, 31, 32, 32, 32, 32, 32, 33, 33, 34, 34, 34, 37, 39, 41, 44, 45, 49, 51, 54, 58, 60, 65, 68, 72, 75, + 75, 79, 82, 84, 86, 88, 91, 31, 32, 32, 32, 33, 33, 34, 34, 35, 36, 36, 39, 40, 42, 44, 45, 50, 51, 54, 58, 59, 64, 67, 71, 73, 74, 78, + 81, 83, 85, 88, 91, 32, 32, 32, 33, 34, 34, 35, 36, 37, 38, 38, 40, 41, 43, 45, 46, 50, 51, 54, 57, 58, 63, 66, 70, 72, 72, 76, 78, 80, + 82, 85, 87, 35, 35, 34, 34, 35, 36, 37, 39, 41, 45, 46, 48, 49, 51, 53, 54, 57, 59, 61, 65, 66, 71, 73, 77, 79, 79, 83, 83, 84, 85, 86, + 87, 36, 35, 35, 34, 36, 36, 38, 40, 42, 47, 48, 50, 51, 53, 56, 56, 60, 61, 63, 67, 68, 73, 75, 79, 81, 81, 85, 87, 89, 92, 94, 97, 44, + 42, 41, 41, 42, 42, 42, 44, 48, 52, 54, 58, 60, 63, 66, 67, 71, 72, 75, 78, 79, 84, 86, 90, 92, 92, 96, 97, 97, 97, 97, 97, 47, 45, 45, + 44, 44, 45, 45, 47, 50, 55, 56, 60, 62, 66, 69, 70, 75, 77, 79, 83, 84, 89, 91, 95, 97, 97, 100, 99, 101, 104, 107, 110, 53, 51, 50, 49, + 49, 50, 49, 51, 54, 59, 60, 65, 67, 71, 75, 76, 82, 84, 87, 91, 92, 97, 100, 104, 105, 106, 110, 113, 114, 112, 111, 110, 62, 59, 58, 57, + 57, 57, 56, 58, 61, 65, 66, 71, 74, 78, 82, 83, 90, 92, 95, 100, 102, 108, 110, 115, 117, 117, 120, 118, 116, 119, 123, 126, 65, 62, 61, + 59, 59, 59, 58, 60, 63, 67, 68, 73, 76, 79, 84, 85, 92, 94, 98, 103, 105, 111, 113, 118, 120, 121, 125, 128, 132, 130, 128, 126, 79, 75, + 74, 72, 71, 71, 69, 71, 73, 77, 78, 84, 86, 90, 95, 96, 103, 106, 110, 116, 118, 125, 128, 133, 136, 136, 141, 139, 135, 136, 140, 144, + 82, 78, 76, 74, 73, 73, 71, 73, 76, 79, 80, 86, 88, 92, 97, 98, 106, 108, 112, 118, 120, 127, 131, 136, 139, 139, 144, 145, 150, 151, + 147, 144, 88, 83, 82, 79, 79, 78, 76, 76, 81, 82, 85, 89, 91, 97, 98, 104, 107, 111, 117, 119, 127, 129, 135, 140, 145, 148, 148, 153, + 153, 154, 159, 163, 90, 86, 85, 82, 81, 80, 79, 78, 81, 83, 87, 88, 93, 94, 101, 101, 108, 110, 116, 119, 124, 129, 134, 139, 142, 150, + 153, 157, 157, 163, 163, 163, 93, 88, 88, 84, 84, 82, 83, 80, 80, 86, 86, 91, 91, 97, 98, 105, 105, 112, 113, 121, 122, 130, 130, 140, + 140, 149, 150, 161, 162, 166, 167, 173, 31, 36, 62, 88, 32, 35, 58, 82, 32, 36, 57, 79, 33, 38, 56, 76, 34, 42, 61, 81, 34, 48, 66, 85, + 39, 51, 74, 91, 44, 56, 82, 98, 49, 60, 90, 107, 54, 63, 95, 117, 60, 68, 102, 127, 68, 75, 110, 135, 75, 81, 117, 145, 79, 85, 120, 148, + 84, 89, 116, 153, 88, 94, 123, 159, 31, 32, 32, 33, 34, 34, 39, 44, 49, 54, 60, 68, 75, 79, 84, 88, 36, 35, 36, 38, 42, 48, 51, 56, 60, + 63, 68, 75, 81, 85, 89, 94, 62, 58, 57, 56, 61, 66, 74, 82, 90, 95, 102, 110, 117, 120, 116, 123, 88, 82, 79, 76, 81, 85, 91, 98, 107, + 117, 127, 135, 145, 148, 153, 159, 32, 31, 35, 44, 53, 65, 82, 90, 31, 32, 35, 42, 51, 62, 78, 86, 31, 32, 34, 41, 50, 61, 76, 85, 31, + 32, 34, 41, 49, 59, 74, 82, 31, 33, 35, 42, 49, 59, 73, 81, 32, 33, 36, 42, 50, 59, 73, 80, 32, 34, 37, 42, 49, 58, 71, 79, 32, 34, 39, + 44, 51, 60, 73, 78, 34, 35, 41, 48, 54, 63, 76, 81, 35, 36, 45, 52, 59, 67, 79, 83, 36, 36, 46, 54, 60, 68, 80, 87, 39, 39, 48, 58, 65, + 73, 86, 88, 41, 40, 49, 60, 67, 76, 88, 93, 44, 42, 51, 63, 71, 79, 92, 94, 47, 44, 53, 66, 75, 84, 97, 101, 48, 45, 54, 67, 76, 85, 98, + 101, 53, 50, 57, 71, 82, 92, 106, 108, 55, 51, 59, 72, 84, 94, 108, 110, 58, 54, 61, 75, 87, 98, 112, 116, 63, 58, 65, 78, 91, 103, 118, + 119, 65, 59, 66, 79, 92, 105, 120, 124, 71, 64, 71, 84, 97, 111, 127, 129, 74, 67, 73, 86, 100, 113, 131, 134, 79, 71, 77, 90, 104, 118, + 136, 139, 82, 73, 79, 92, 105, 120, 139, 142, 82, 74, 79, 92, 106, 121, 139, 150, 87, 78, 83, 96, 110, 125, 144, 153, 89, 81, 83, 97, + 113, 128, 145, 157, 92, 83, 84, 97, 114, 132, 150, 157, 94, 85, 85, 97, 112, 130, 151, 163, 97, 88, 86, 97, 111, 128, 147, 163, 99, 91, + 87, 97, 110, 126, 144, 163, 32, 31, 31, 31, 31, 32, 32, 32, 34, 35, 36, 39, 41, 44, 47, 48, 53, 55, 58, 63, 65, 71, 74, 79, 82, 82, 87, + 89, 92, 94, 97, 99, 31, 32, 32, 32, 33, 33, 34, 34, 35, 36, 36, 39, 40, 42, 44, 45, 50, 51, 54, 58, 59, 64, 67, 71, 73, 74, 78, 81, 83, + 85, 88, 91, 35, 35, 34, 34, 35, 36, 37, 39, 41, 45, 46, 48, 49, 51, 53, 54, 57, 59, 61, 65, 66, 71, 73, 77, 79, 79, 83, 83, 84, 85, 86, + 87, 44, 42, 41, 41, 42, 42, 42, 44, 48, 52, 54, 58, 60, 63, 66, 67, 71, 72, 75, 78, 79, 84, 86, 90, 92, 92, 96, 97, 97, 97, 97, 97, 53, + 51, 50, 49, 49, 50, 49, 51, 54, 59, 60, 65, 67, 71, 75, 76, 82, 84, 87, 91, 92, 97, 100, 104, 105, 106, 110, 113, 114, 112, 111, 110, 65, + 62, 61, 59, 59, 59, 58, 60, 63, 67, 68, 73, 76, 79, 84, 85, 92, 94, 98, 103, 105, 111, 113, 118, 120, 121, 125, 128, 132, 130, 128, 126, + 82, 78, 76, 74, 73, 73, 71, 73, 76, 79, 80, 86, 88, 92, 97, 98, 106, 108, 112, 118, 120, 127, 131, 136, 139, 139, 144, 145, 150, 151, + 147, 144, 90, 86, 85, 82, 81, 80, 79, 78, 81, 83, 87, 88, 93, 94, 101, 101, 108, 110, 116, 119, 124, 129, 134, 139, 142, 150, 153, 157, + 157, 163, 163, 163, + ], + [ + 32, 45, 51, 61, 45, 54, 59, 65, 51, 59, 75, 81, 61, 65, 81, 97, 31, 34, 46, 47, 50, 57, 61, 65, 34, 39, 47, 45, 48, 53, 57, 61, 46, 47, + 52, 52, 54, 58, 61, 62, 47, 45, 52, 58, 62, 65, 68, 68, 50, 48, 54, 62, 68, 73, 77, 76, 57, 53, 58, 65, 73, 82, 86, 86, 61, 57, 61, 68, + 77, 86, 91, 95, 65, 61, 62, 68, 76, 86, 95, 100, 32, 31, 33, 36, 41, 49, 49, 50, 52, 54, 57, 61, 64, 67, 68, 70, 31, 31, 34, 39, 42, 47, + 46, 47, 49, 51, 53, 57, 60, 62, 64, 66, 33, 34, 37, 42, 44, 47, 46, 46, 47, 49, 51, 55, 57, 59, 61, 63, 36, 39, 42, 47, 47, 48, 46, 46, + 47, 48, 50, 53, 55, 57, 59, 61, 41, 42, 44, 47, 48, 50, 49, 50, 50, 52, 53, 56, 58, 60, 61, 60, 49, 47, 47, 48, 50, 53, 53, 54, 54, 55, + 56, 59, 61, 63, 64, 64, 49, 46, 46, 46, 49, 53, 55, 57, 59, 60, 61, 64, 66, 67, 67, 67, 50, 47, 46, 46, 50, 54, 57, 61, 63, 64, 66, 69, + 70, 72, 71, 71, 52, 49, 47, 47, 50, 54, 59, 63, 66, 68, 70, 73, 75, 77, 75, 75, 54, 51, 49, 48, 52, 55, 60, 64, 68, 71, 73, 76, 79, 80, + 79, 79, 57, 53, 51, 50, 53, 56, 61, 66, 70, 73, 76, 80, 82, 84, 83, 84, 61, 57, 55, 53, 56, 59, 64, 69, 73, 76, 80, 84, 87, 89, 88, 88, + 64, 60, 57, 55, 58, 61, 66, 70, 75, 79, 82, 87, 91, 93, 93, 93, 67, 62, 59, 57, 60, 63, 67, 72, 77, 80, 84, 89, 93, 95, 96, 97, 68, 64, + 61, 59, 61, 64, 67, 71, 75, 79, 83, 88, 93, 96, 99, 100, 70, 66, 63, 61, 60, 64, 67, 71, 75, 79, 84, 88, 93, 97, 100, 102, 32, 31, 31, + 30, 33, 33, 36, 38, 41, 47, 49, 48, 49, 49, 50, 50, 52, 53, 54, 56, 57, 60, 61, 63, 64, 65, 67, 67, 68, 69, 70, 71, 31, 31, 31, 31, 34, + 34, 38, 40, 42, 46, 47, 47, 47, 47, 48, 48, 50, 50, 52, 54, 54, 57, 58, 60, 61, 61, 63, 64, 65, 65, 66, 67, 31, 31, 31, 31, 34, 35, 39, + 40, 42, 46, 47, 46, 46, 46, 47, 47, 49, 50, 51, 53, 53, 56, 57, 59, 60, 60, 62, 63, 64, 65, 66, 67, 30, 31, 31, 32, 34, 35, 40, 41, 42, + 45, 46, 45, 45, 45, 46, 46, 47, 48, 49, 51, 52, 54, 55, 57, 58, 58, 60, 61, 62, 62, 63, 64, 33, 34, 34, 34, 37, 38, 42, 43, 44, 46, 47, + 46, 46, 45, 46, 46, 47, 48, 49, 51, 51, 53, 55, 56, 57, 57, 59, 60, 61, 62, 63, 64, 33, 34, 35, 35, 38, 39, 43, 44, 45, 47, 47, 46, 46, + 45, 46, 46, 47, 48, 49, 51, 51, 53, 54, 56, 57, 57, 59, 60, 60, 61, 62, 62, 36, 38, 39, 40, 42, 43, 47, 47, 47, 47, 48, 46, 46, 45, 46, + 46, 47, 47, 48, 49, 50, 52, 53, 54, 55, 55, 57, 58, 59, 60, 61, 62, 38, 40, 40, 41, 43, 44, 47, 47, 48, 48, 49, 48, 47, 47, 47, 47, 48, + 49, 49, 51, 51, 53, 54, 55, 56, 56, 58, 58, 58, 59, 60, 60, 41, 42, 42, 42, 44, 45, 47, 48, 48, 50, 50, 49, 49, 49, 50, 50, 50, 51, 52, + 53, 53, 55, 56, 57, 58, 58, 60, 61, 61, 61, 60, 60, 47, 46, 46, 45, 46, 47, 47, 48, 50, 52, 52, 52, 52, 52, 53, 53, 53, 54, 55, 55, 56, + 58, 58, 60, 60, 61, 62, 61, 61, 62, 63, 64, 49, 47, 47, 46, 47, 47, 48, 49, 50, 52, 53, 53, 53, 53, 54, 54, 54, 55, 55, 56, 56, 58, 59, + 60, 61, 61, 63, 63, 64, 64, 64, 64, 48, 47, 46, 45, 46, 46, 46, 48, 49, 52, 53, 54, 55, 55, 56, 56, 57, 58, 58, 59, 60, 61, 62, 63, 64, + 64, 66, 65, 65, 65, 66, 67, 49, 47, 46, 45, 46, 46, 46, 47, 49, 52, 53, 55, 55, 57, 57, 58, 59, 59, 60, 61, 61, 63, 64, 65, 66, 66, 67, + 67, 67, 68, 67, 67, 49, 47, 46, 45, 45, 45, 45, 47, 49, 52, 53, 55, 57, 58, 59, 60, 61, 62, 62, 63, 63, 65, 66, 67, 68, 68, 69, 70, 69, + 68, 69, 70, 50, 48, 47, 46, 46, 46, 46, 47, 50, 53, 54, 56, 57, 59, 61, 61, 63, 64, 64, 66, 66, 68, 69, 70, 70, 71, 72, 70, 71, 72, 71, + 70, 50, 48, 47, 46, 46, 46, 46, 47, 50, 53, 54, 56, 58, 60, 61, 61, 63, 64, 65, 66, 67, 68, 69, 71, 71, 71, 73, 74, 73, 72, 73, 74, 52, + 50, 49, 47, 47, 47, 47, 48, 50, 53, 54, 57, 59, 61, 63, 63, 66, 67, 68, 70, 70, 72, 73, 75, 75, 75, 77, 75, 75, 76, 75, 74, 53, 50, 50, + 48, 48, 48, 47, 49, 51, 54, 55, 58, 59, 62, 64, 64, 67, 68, 69, 71, 71, 73, 74, 76, 77, 77, 78, 78, 78, 76, 77, 78, 54, 52, 51, 49, 49, + 49, 48, 49, 52, 55, 55, 58, 60, 62, 64, 65, 68, 69, 71, 73, 73, 75, 76, 78, 79, 79, 80, 80, 79, 80, 79, 78, 56, 54, 53, 51, 51, 51, 49, + 51, 53, 55, 56, 59, 61, 63, 66, 66, 70, 71, 73, 75, 76, 78, 79, 81, 82, 82, 83, 81, 83, 81, 81, 82, 57, 54, 53, 52, 51, 51, 50, 51, 53, + 56, 56, 60, 61, 63, 66, 67, 70, 71, 73, 76, 76, 79, 80, 82, 82, 83, 84, 85, 83, 84, 84, 82, 60, 57, 56, 54, 53, 53, 52, 53, 55, 58, 58, + 61, 63, 65, 68, 68, 72, 73, 75, 78, 79, 82, 83, 85, 86, 86, 88, 86, 87, 86, 85, 86, 61, 58, 57, 55, 55, 54, 53, 54, 56, 58, 59, 62, 64, + 66, 69, 69, 73, 74, 76, 79, 80, 83, 84, 86, 87, 88, 89, 89, 88, 88, 88, 86, 63, 60, 59, 57, 56, 56, 54, 55, 57, 60, 60, 63, 65, 67, 70, + 71, 75, 76, 78, 81, 82, 85, 86, 89, 90, 90, 92, 91, 91, 90, 89, 91, 64, 61, 60, 58, 57, 57, 55, 56, 58, 60, 61, 64, 66, 68, 70, 71, 75, + 77, 79, 82, 82, 86, 87, 90, 91, 91, 93, 93, 93, 92, 93, 91, 65, 61, 60, 58, 57, 57, 55, 56, 58, 61, 61, 64, 66, 68, 71, 71, 75, 77, 79, + 82, 83, 86, 88, 90, 91, 91, 93, 94, 95, 95, 93, 95, 67, 63, 62, 60, 59, 59, 57, 58, 60, 62, 63, 66, 67, 69, 72, 73, 77, 78, 80, 83, 84, + 88, 89, 92, 93, 93, 95, 95, 96, 96, 97, 95, 67, 64, 63, 61, 60, 60, 58, 58, 61, 61, 63, 65, 67, 70, 70, 74, 75, 78, 80, 81, 85, 86, 89, + 91, 93, 94, 95, 97, 97, 98, 98, 100, 68, 65, 64, 62, 61, 60, 59, 58, 61, 61, 64, 65, 67, 69, 71, 73, 75, 78, 79, 83, 83, 87, 88, 91, 93, + 95, 96, 97, 99, 98, 100, 100, 69, 65, 65, 62, 62, 61, 60, 59, 61, 62, 64, 65, 68, 68, 72, 72, 76, 76, 80, 81, 84, 86, 88, 90, 92, 95, 96, + 98, 98, 100, 100, 101, 70, 66, 66, 63, 63, 62, 61, 60, 60, 63, 64, 66, 67, 69, 71, 73, 75, 77, 79, 81, 84, 85, 88, 89, 93, 93, 97, 98, + 100, 100, 102, 101, 71, 67, 67, 64, 64, 62, 62, 60, 60, 64, 64, 67, 67, 70, 70, 74, 74, 78, 78, 82, 82, 86, 86, 91, 91, 95, 95, 100, 100, + 101, 101, 104, 31, 47, 53, 63, 36, 47, 50, 59, 46, 52, 55, 61, 45, 53, 63, 70, 49, 55, 71, 77, 54, 58, 77, 86, 59, 61, 81, 94, 63, 65, + 80, 95, 31, 36, 46, 45, 49, 54, 59, 63, 47, 47, 52, 53, 55, 58, 61, 65, 53, 50, 55, 63, 71, 77, 81, 80, 63, 59, 61, 70, 77, 86, 94, 95, + 32, 33, 45, 49, 52, 57, 64, 68, 31, 34, 45, 46, 49, 53, 60, 64, 33, 37, 46, 45, 47, 51, 57, 61, 37, 43, 47, 45, 47, 50, 55, 59, 42, 44, + 49, 49, 50, 53, 58, 60, 49, 47, 52, 53, 54, 57, 61, 63, 48, 46, 51, 57, 59, 61, 66, 67, 50, 46, 52, 59, 63, 66, 71, 71, 52, 47, 53, 61, + 66, 71, 75, 74, 54, 49, 54, 62, 68, 73, 79, 79, 57, 51, 55, 64, 70, 76, 83, 83, 61, 55, 58, 66, 73, 80, 87, 87, 64, 57, 60, 68, 75, 83, + 91, 91, 66, 59, 61, 69, 77, 84, 93, 95, 68, 61, 61, 68, 77, 86, 94, 97, 70, 63, 61, 67, 75, 83, 92, 98, 32, 31, 33, 37, 42, 49, 48, 50, + 52, 54, 57, 61, 64, 66, 68, 70, 33, 34, 37, 43, 44, 47, 46, 46, 47, 49, 51, 55, 57, 59, 61, 63, 45, 45, 46, 47, 49, 52, 51, 52, 53, 54, + 55, 58, 60, 61, 61, 61, 49, 46, 45, 45, 49, 53, 57, 59, 61, 62, 64, 66, 68, 69, 68, 67, 52, 49, 47, 47, 50, 54, 59, 63, 66, 68, 70, 73, + 75, 77, 77, 75, 57, 53, 51, 50, 53, 57, 61, 66, 71, 73, 76, 80, 83, 84, 86, 83, 64, 60, 57, 55, 58, 61, 66, 71, 75, 79, 83, 87, 91, 93, + 94, 92, 68, 64, 61, 59, 60, 63, 67, 71, 74, 79, 83, 87, 91, 95, 97, 98, 32, 31, 33, 37, 45, 48, 49, 50, 52, 56, 57, 63, 64, 67, 68, 68, + 31, 31, 34, 38, 45, 47, 47, 48, 50, 53, 54, 60, 61, 63, 64, 65, 31, 32, 34, 39, 45, 46, 46, 47, 49, 52, 53, 59, 60, 62, 64, 65, 30, 32, + 35, 40, 44, 46, 45, 46, 48, 51, 52, 57, 58, 60, 61, 62, 33, 35, 37, 42, 46, 47, 45, 46, 47, 50, 51, 56, 57, 60, 61, 62, 33, 36, 38, 43, + 46, 47, 46, 46, 47, 50, 51, 56, 57, 59, 60, 60, 37, 40, 43, 47, 47, 47, 45, 46, 47, 49, 50, 54, 55, 57, 59, 61, 39, 41, 43, 47, 48, 48, + 47, 47, 48, 50, 51, 55, 56, 57, 58, 59, 42, 43, 44, 47, 49, 50, 49, 50, 50, 53, 53, 57, 58, 60, 60, 59, 47, 46, 46, 48, 51, 52, 53, 53, + 53, 55, 56, 60, 61, 61, 61, 62, 49, 46, 47, 48, 52, 53, 53, 54, 54, 56, 57, 60, 61, 63, 63, 62, 48, 46, 46, 47, 51, 53, 56, 56, 57, 59, + 60, 64, 64, 65, 64, 65, 48, 45, 46, 46, 51, 53, 57, 57, 59, 61, 61, 65, 66, 66, 67, 65, 49, 45, 45, 46, 51, 53, 58, 59, 61, 63, 64, 67, + 68, 70, 67, 68, 50, 46, 46, 46, 52, 54, 59, 61, 63, 65, 66, 70, 71, 70, 71, 68, 50, 46, 46, 46, 52, 54, 59, 61, 64, 66, 67, 71, 71, 73, + 71, 72, 52, 48, 47, 47, 53, 54, 61, 63, 66, 70, 71, 75, 75, 75, 74, 72, 53, 49, 48, 48, 53, 55, 61, 64, 67, 71, 72, 76, 77, 77, 75, 76, + 54, 50, 49, 49, 54, 55, 62, 65, 68, 72, 73, 78, 79, 80, 79, 76, 56, 51, 51, 50, 55, 56, 63, 66, 70, 74, 76, 81, 82, 81, 80, 80, 57, 52, + 51, 50, 55, 56, 64, 66, 70, 75, 76, 82, 83, 85, 83, 80, 60, 54, 54, 52, 57, 58, 65, 68, 72, 77, 79, 85, 86, 86, 85, 84, 61, 56, 55, 53, + 58, 59, 66, 69, 73, 79, 80, 86, 87, 89, 87, 84, 63, 57, 56, 55, 59, 60, 67, 70, 75, 80, 82, 89, 90, 91, 89, 89, 64, 58, 57, 56, 60, 61, + 68, 71, 75, 81, 83, 90, 91, 93, 91, 89, 64, 59, 58, 56, 60, 61, 68, 71, 75, 81, 83, 90, 91, 94, 94, 93, 66, 60, 59, 57, 61, 63, 69, 72, + 77, 82, 84, 92, 93, 94, 95, 93, 67, 61, 60, 58, 61, 63, 69, 70, 78, 80, 85, 90, 93, 96, 97, 97, 68, 62, 61, 59, 61, 64, 68, 71, 77, 79, + 86, 88, 94, 96, 97, 98, 69, 63, 62, 59, 61, 65, 68, 72, 76, 80, 85, 88, 94, 95, 99, 99, 70, 63, 63, 60, 61, 66, 67, 73, 75, 81, 83, 89, + 92, 97, 98, 99, 70, 64, 64, 61, 61, 67, 67, 74, 74, 82, 82, 90, 90, 98, 98, 102, 32, 31, 31, 30, 33, 33, 37, 39, 42, 47, 49, 48, 48, 49, + 50, 50, 52, 53, 54, 56, 57, 60, 61, 63, 64, 64, 66, 67, 68, 69, 70, 70, 31, 31, 32, 32, 35, 36, 40, 41, 43, 46, 46, 46, 45, 45, 46, 46, + 48, 49, 50, 51, 52, 54, 56, 57, 58, 59, 60, 61, 62, 63, 63, 64, 33, 34, 34, 35, 37, 38, 43, 43, 44, 46, 47, 46, 46, 45, 46, 46, 47, 48, + 49, 51, 51, 54, 55, 56, 57, 58, 59, 60, 61, 62, 63, 64, 37, 38, 39, 40, 42, 43, 47, 47, 47, 48, 48, 47, 46, 46, 46, 46, 47, 48, 49, 50, + 50, 52, 53, 55, 56, 56, 57, 58, 59, 59, 60, 61, 45, 45, 45, 44, 46, 46, 47, 48, 49, 51, 52, 51, 51, 51, 52, 52, 53, 53, 54, 55, 55, 57, + 58, 59, 60, 60, 61, 61, 61, 61, 61, 61, 48, 47, 46, 46, 47, 47, 47, 48, 50, 52, 53, 53, 53, 53, 54, 54, 54, 55, 55, 56, 56, 58, 59, 60, + 61, 61, 63, 63, 64, 65, 66, 67, 49, 47, 46, 45, 45, 46, 45, 47, 49, 53, 53, 56, 57, 58, 59, 59, 61, 61, 62, 63, 64, 65, 66, 67, 68, 68, + 69, 69, 68, 68, 67, 67, 50, 48, 47, 46, 46, 46, 46, 47, 50, 53, 54, 56, 57, 59, 61, 61, 63, 64, 65, 66, 66, 68, 69, 70, 71, 71, 72, 70, + 71, 72, 73, 74, 52, 50, 49, 48, 47, 47, 47, 48, 50, 53, 54, 57, 59, 61, 63, 64, 66, 67, 68, 70, 70, 72, 73, 75, 75, 75, 77, 78, 77, 76, + 75, 74, 56, 53, 52, 51, 50, 50, 49, 50, 53, 55, 56, 59, 61, 63, 65, 66, 70, 71, 72, 74, 75, 77, 79, 80, 81, 81, 82, 80, 79, 80, 81, 82, + 57, 54, 53, 52, 51, 51, 50, 51, 53, 56, 57, 60, 61, 64, 66, 67, 71, 72, 73, 76, 76, 79, 80, 82, 83, 83, 84, 85, 86, 85, 83, 82, 63, 60, + 59, 57, 56, 56, 54, 55, 57, 60, 60, 64, 65, 67, 70, 71, 75, 76, 78, 81, 82, 85, 86, 89, 90, 90, 92, 90, 88, 88, 89, 90, 64, 61, 60, 58, + 57, 57, 55, 56, 58, 61, 61, 64, 66, 68, 71, 71, 75, 77, 79, 82, 83, 86, 87, 90, 91, 91, 93, 93, 94, 94, 92, 90, 67, 63, 62, 60, 60, 59, + 57, 57, 60, 61, 63, 65, 66, 70, 70, 73, 75, 77, 80, 81, 85, 86, 89, 91, 93, 94, 94, 96, 96, 95, 97, 98, 68, 64, 64, 61, 61, 60, 59, 58, + 60, 61, 63, 64, 67, 67, 71, 71, 74, 75, 79, 80, 83, 85, 87, 89, 91, 94, 95, 97, 97, 99, 98, 98, 68, 65, 65, 62, 62, 60, 61, 59, 59, 62, + 62, 65, 65, 68, 68, 72, 72, 76, 76, 80, 80, 84, 84, 89, 89, 93, 93, 97, 98, 99, 99, 102, 31, 48, 56, 67, 32, 46, 52, 62, 35, 47, 50, 60, + 40, 47, 49, 57, 43, 50, 53, 60, 46, 53, 56, 63, 45, 53, 61, 66, 46, 54, 65, 70, 48, 54, 70, 75, 50, 55, 72, 80, 52, 56, 75, 85, 56, 59, + 79, 89, 58, 61, 81, 93, 60, 63, 82, 94, 62, 64, 79, 96, 63, 66, 81, 97, 31, 32, 35, 40, 43, 46, 45, 46, 48, 50, 52, 56, 58, 60, 62, 63, + 48, 46, 47, 47, 50, 53, 53, 54, 54, 55, 56, 59, 61, 63, 64, 66, 56, 52, 50, 49, 53, 56, 61, 65, 70, 72, 75, 79, 81, 82, 79, 81, 67, 62, + 60, 57, 60, 63, 66, 70, 75, 80, 85, 89, 93, 94, 96, 97, 32, 33, 45, 49, 52, 57, 64, 68, 31, 34, 45, 47, 50, 54, 61, 64, 31, 34, 45, 46, + 49, 53, 60, 64, 30, 35, 44, 45, 48, 52, 58, 61, 33, 37, 46, 45, 47, 51, 57, 61, 33, 38, 46, 46, 47, 51, 57, 60, 37, 43, 47, 45, 47, 50, + 55, 59, 39, 43, 48, 47, 48, 51, 56, 58, 42, 44, 49, 49, 50, 53, 58, 60, 47, 46, 51, 53, 53, 56, 61, 61, 49, 47, 52, 53, 54, 57, 61, 63, + 48, 46, 51, 56, 57, 60, 64, 64, 48, 46, 51, 57, 59, 61, 66, 67, 49, 45, 51, 58, 61, 64, 68, 67, 50, 46, 52, 59, 63, 66, 71, 71, 50, 46, + 52, 59, 64, 67, 71, 71, 52, 47, 53, 61, 66, 71, 75, 74, 53, 48, 53, 61, 67, 72, 77, 75, 54, 49, 54, 62, 68, 73, 79, 79, 56, 51, 55, 63, + 70, 76, 82, 80, 57, 51, 55, 64, 70, 76, 83, 83, 60, 54, 57, 65, 72, 79, 86, 85, 61, 55, 58, 66, 73, 80, 87, 87, 63, 56, 59, 67, 75, 82, + 90, 89, 64, 57, 60, 68, 75, 83, 91, 91, 64, 58, 60, 68, 75, 83, 91, 94, 66, 59, 61, 69, 77, 84, 93, 95, 67, 60, 61, 69, 78, 85, 93, 97, + 68, 61, 61, 68, 77, 86, 94, 97, 69, 62, 61, 68, 76, 85, 94, 99, 70, 63, 61, 67, 75, 83, 92, 98, 70, 64, 61, 67, 74, 82, 90, 98, 32, 31, + 31, 30, 33, 33, 37, 39, 42, 47, 49, 48, 48, 49, 50, 50, 52, 53, 54, 56, 57, 60, 61, 63, 64, 64, 66, 67, 68, 69, 70, 70, 33, 34, 34, 35, + 37, 38, 43, 43, 44, 46, 47, 46, 46, 45, 46, 46, 47, 48, 49, 51, 51, 54, 55, 56, 57, 58, 59, 60, 61, 62, 63, 64, 45, 45, 45, 44, 46, 46, + 47, 48, 49, 51, 52, 51, 51, 51, 52, 52, 53, 53, 54, 55, 55, 57, 58, 59, 60, 60, 61, 61, 61, 61, 61, 61, 49, 47, 46, 45, 45, 46, 45, 47, + 49, 53, 53, 56, 57, 58, 59, 59, 61, 61, 62, 63, 64, 65, 66, 67, 68, 68, 69, 69, 68, 68, 67, 67, 52, 50, 49, 48, 47, 47, 47, 48, 50, 53, + 54, 57, 59, 61, 63, 64, 66, 67, 68, 70, 70, 72, 73, 75, 75, 75, 77, 78, 77, 76, 75, 74, 57, 54, 53, 52, 51, 51, 50, 51, 53, 56, 57, 60, + 61, 64, 66, 67, 71, 72, 73, 76, 76, 79, 80, 82, 83, 83, 84, 85, 86, 85, 83, 82, 64, 61, 60, 58, 57, 57, 55, 56, 58, 61, 61, 64, 66, 68, + 71, 71, 75, 77, 79, 82, 83, 86, 87, 90, 91, 91, 93, 93, 94, 94, 92, 90, 68, 64, 64, 61, 61, 60, 59, 58, 60, 61, 63, 64, 67, 67, 71, 71, + 74, 75, 79, 80, 83, 85, 87, 89, 91, 94, 95, 97, 97, 99, 98, 98, + ], + ], + [ + [ + 32, 34, 53, 75, 34, 49, 64, 81, 53, 64, 91, 112, 75, 81, 112, 140, 32, 32, 34, 39, 50, 62, 76, 84, 32, 33, 35, 40, 48, 59, 71, 79, 34, + 35, 39, 46, 53, 63, 74, 81, 39, 40, 46, 56, 65, 75, 86, 92, 50, 48, 53, 65, 78, 90, 101, 106, 62, 59, 63, 75, 90, 105, 118, 123, 76, 71, + 74, 86, 101, 118, 134, 142, 84, 79, 81, 92, 106, 123, 142, 153, 32, 31, 31, 32, 33, 36, 39, 44, 48, 54, 59, 66, 74, 81, 86, 91, 31, 32, + 32, 32, 33, 35, 38, 42, 46, 51, 56, 63, 70, 77, 81, 86, 31, 32, 32, 33, 34, 35, 38, 41, 45, 49, 54, 60, 67, 73, 77, 82, 32, 32, 33, 34, + 36, 37, 40, 42, 45, 49, 53, 59, 66, 71, 75, 80, 33, 33, 34, 36, 38, 42, 44, 46, 50, 53, 57, 63, 69, 74, 78, 80, 36, 35, 35, 37, 42, 48, + 50, 54, 57, 60, 64, 69, 75, 80, 84, 85, 39, 38, 38, 40, 44, 50, 54, 58, 61, 65, 69, 74, 80, 85, 89, 91, 44, 42, 41, 42, 46, 54, 58, 63, + 67, 71, 75, 80, 86, 91, 95, 97, 48, 46, 45, 45, 50, 57, 61, 67, 71, 76, 80, 86, 93, 98, 101, 104, 54, 51, 49, 49, 53, 60, 65, 71, 76, 82, + 87, 93, 100, 105, 109, 112, 59, 56, 54, 53, 57, 64, 69, 75, 80, 87, 92, 99, 106, 112, 116, 120, 66, 63, 60, 59, 63, 69, 74, 80, 86, 93, + 99, 107, 115, 121, 125, 129, 74, 70, 67, 66, 69, 75, 80, 86, 93, 100, 106, 115, 123, 130, 135, 138, 81, 77, 73, 71, 74, 80, 85, 91, 98, + 105, 112, 121, 130, 137, 142, 148, 86, 81, 77, 75, 78, 84, 89, 95, 101, 109, 116, 125, 135, 142, 147, 153, 91, 86, 82, 80, 80, 85, 91, + 97, 104, 112, 120, 129, 138, 148, 153, 159, 32, 31, 31, 31, 31, 31, 32, 32, 33, 34, 36, 36, 39, 41, 44, 46, 48, 52, 54, 58, 59, 65, 66, + 71, 74, 80, 81, 83, 86, 89, 91, 93, 31, 32, 32, 32, 32, 32, 32, 32, 33, 34, 35, 35, 38, 39, 42, 44, 46, 50, 51, 56, 56, 62, 63, 68, 71, + 76, 77, 78, 82, 84, 86, 88, 31, 32, 32, 32, 32, 32, 32, 32, 33, 34, 35, 35, 38, 39, 42, 44, 46, 49, 51, 55, 56, 61, 63, 67, 70, 75, 77, + 78, 81, 84, 86, 88, 31, 32, 32, 32, 32, 32, 32, 32, 33, 33, 34, 34, 37, 38, 41, 42, 44, 48, 49, 53, 54, 59, 60, 65, 68, 72, 74, 75, 78, + 80, 82, 84, 31, 32, 32, 32, 32, 33, 33, 33, 34, 34, 35, 35, 38, 39, 41, 43, 45, 48, 49, 53, 54, 59, 60, 65, 67, 72, 73, 74, 77, 80, 82, + 84, 31, 32, 32, 32, 33, 33, 33, 34, 35, 35, 36, 36, 39, 40, 42, 44, 45, 48, 50, 53, 54, 59, 60, 64, 67, 71, 73, 74, 77, 79, 81, 83, 32, + 32, 32, 32, 33, 33, 34, 35, 36, 36, 37, 38, 40, 40, 42, 44, 45, 48, 49, 53, 53, 58, 59, 63, 66, 70, 71, 72, 75, 78, 80, 83, 32, 32, 32, + 32, 33, 34, 35, 35, 36, 37, 38, 38, 40, 41, 42, 44, 46, 48, 49, 53, 53, 58, 59, 63, 65, 69, 71, 72, 74, 77, 79, 80, 33, 33, 33, 33, 34, + 35, 36, 36, 38, 39, 42, 42, 44, 45, 46, 48, 50, 52, 53, 57, 57, 62, 63, 67, 69, 73, 74, 75, 78, 79, 80, 81, 34, 34, 34, 33, 34, 35, 36, + 37, 39, 39, 42, 43, 45, 46, 47, 49, 51, 53, 54, 58, 58, 63, 64, 68, 70, 74, 75, 76, 79, 81, 84, 86, 36, 35, 35, 34, 35, 36, 37, 38, 42, + 42, 48, 48, 50, 51, 54, 55, 57, 59, 60, 63, 64, 68, 69, 73, 75, 79, 80, 81, 84, 85, 85, 86, 36, 35, 35, 34, 35, 36, 38, 38, 42, 43, 48, + 49, 51, 52, 54, 55, 57, 59, 60, 64, 64, 68, 69, 73, 75, 79, 80, 81, 84, 86, 88, 91, 39, 38, 38, 37, 38, 39, 40, 40, 44, 45, 50, 51, 54, + 55, 58, 59, 61, 64, 65, 68, 69, 73, 74, 78, 80, 84, 85, 86, 89, 91, 91, 91, 41, 39, 39, 38, 39, 40, 40, 41, 45, 46, 51, 52, 55, 56, 59, + 61, 63, 65, 67, 70, 70, 75, 76, 80, 82, 86, 87, 88, 91, 92, 94, 96, 44, 42, 42, 41, 41, 42, 42, 42, 46, 47, 54, 54, 58, 59, 63, 65, 67, + 70, 71, 75, 75, 79, 80, 84, 86, 90, 91, 92, 95, 97, 97, 97, 46, 44, 44, 42, 43, 44, 44, 44, 48, 49, 55, 55, 59, 61, 65, 67, 69, 72, 74, + 77, 78, 82, 83, 87, 89, 93, 94, 95, 98, 98, 100, 103, 48, 46, 46, 44, 45, 45, 45, 46, 50, 51, 57, 57, 61, 63, 67, 69, 71, 74, 76, 80, 80, + 85, 86, 90, 93, 96, 98, 99, 101, 104, 104, 103, 52, 50, 49, 48, 48, 48, 48, 48, 52, 53, 59, 59, 64, 65, 70, 72, 74, 78, 80, 84, 85, 90, + 91, 95, 97, 101, 103, 104, 106, 106, 107, 110, 54, 51, 51, 49, 49, 50, 49, 49, 53, 54, 60, 60, 65, 67, 71, 74, 76, 80, 82, 86, 87, 92, + 93, 97, 100, 104, 105, 106, 109, 112, 112, 110, 58, 56, 55, 53, 53, 53, 53, 53, 57, 58, 63, 64, 68, 70, 75, 77, 80, 84, 86, 91, 91, 97, + 98, 103, 105, 110, 111, 112, 115, 114, 115, 118, 59, 56, 56, 54, 54, 54, 53, 53, 57, 58, 64, 64, 69, 70, 75, 78, 80, 85, 87, 91, 92, 98, + 99, 103, 106, 110, 112, 113, 116, 119, 120, 119, 65, 62, 61, 59, 59, 59, 58, 58, 62, 63, 68, 68, 73, 75, 79, 82, 85, 90, 92, 97, 98, 105, + 106, 111, 114, 118, 120, 121, 124, 123, 123, 126, 66, 63, 63, 60, 60, 60, 59, 59, 63, 64, 69, 69, 74, 76, 80, 83, 86, 91, 93, 98, 99, + 106, 107, 112, 115, 119, 121, 122, 125, 128, 129, 126, 71, 68, 67, 65, 65, 64, 63, 63, 67, 68, 73, 73, 78, 80, 84, 87, 90, 95, 97, 103, + 103, 111, 112, 117, 120, 125, 127, 128, 131, 132, 132, 135, 74, 71, 70, 68, 67, 67, 66, 65, 69, 70, 75, 75, 80, 82, 86, 89, 93, 97, 100, + 105, 106, 114, 115, 120, 123, 128, 130, 131, 135, 135, 138, 136, 80, 76, 75, 72, 72, 71, 70, 69, 73, 74, 79, 79, 84, 86, 90, 93, 96, 101, + 104, 110, 110, 118, 119, 125, 128, 134, 136, 137, 140, 142, 140, 144, 81, 77, 77, 74, 73, 73, 71, 71, 74, 75, 80, 80, 85, 87, 91, 94, 98, + 103, 105, 111, 112, 120, 121, 127, 130, 136, 137, 139, 142, 145, 148, 144, 83, 78, 78, 75, 74, 74, 72, 72, 75, 76, 81, 81, 86, 88, 92, + 95, 99, 104, 106, 112, 113, 121, 122, 128, 131, 137, 139, 140, 144, 148, 150, 155, 86, 82, 81, 78, 77, 77, 75, 74, 78, 79, 84, 84, 89, + 91, 95, 98, 101, 106, 109, 115, 116, 124, 125, 131, 135, 140, 142, 144, 147, 149, 153, 155, 89, 84, 84, 80, 80, 79, 78, 77, 79, 81, 85, + 86, 91, 92, 97, 98, 104, 106, 112, 114, 119, 123, 128, 132, 135, 142, 145, 148, 149, 153, 154, 159, 91, 86, 86, 82, 82, 81, 80, 79, 80, + 84, 85, 88, 91, 94, 97, 100, 104, 107, 112, 115, 120, 123, 129, 132, 138, 140, 148, 150, 153, 154, 159, 159, 93, 88, 88, 84, 84, 83, 83, + 80, 81, 86, 86, 91, 91, 96, 97, 103, 103, 110, 110, 118, 119, 126, 126, 135, 136, 144, 144, 155, 155, 159, 159, 164, 32, 35, 51, 77, 32, + 36, 50, 72, 34, 42, 54, 75, 38, 51, 67, 87, 48, 59, 80, 103, 60, 68, 92, 119, 72, 79, 104, 135, 81, 86, 112, 144, 32, 32, 34, 38, 48, 60, + 72, 81, 35, 36, 42, 51, 59, 68, 79, 86, 51, 50, 54, 67, 80, 92, 104, 112, 77, 72, 75, 87, 103, 119, 135, 144, 32, 31, 33, 40, 51, 65, 79, + 87, 31, 32, 33, 39, 49, 61, 74, 82, 31, 32, 34, 38, 47, 59, 71, 79, 32, 33, 36, 40, 48, 58, 69, 77, 33, 34, 38, 44, 52, 62, 72, 78, 36, + 35, 42, 51, 58, 68, 78, 84, 39, 38, 44, 54, 63, 73, 84, 89, 44, 41, 46, 59, 69, 79, 90, 96, 48, 45, 50, 62, 74, 85, 96, 103, 53, 49, 53, + 66, 79, 92, 103, 111, 58, 54, 57, 70, 84, 98, 110, 118, 66, 60, 63, 75, 90, 106, 119, 126, 74, 67, 69, 81, 97, 113, 128, 134, 81, 73, 75, + 86, 102, 120, 135, 143, 86, 78, 78, 90, 106, 124, 140, 147, 91, 82, 80, 90, 103, 119, 137, 151, 32, 31, 31, 32, 33, 36, 39, 44, 48, 53, + 58, 66, 74, 81, 86, 91, 31, 32, 32, 33, 34, 35, 38, 41, 45, 49, 54, 60, 67, 73, 78, 82, 33, 33, 34, 36, 38, 42, 44, 46, 50, 53, 57, 63, + 69, 75, 78, 80, 40, 39, 38, 40, 44, 51, 54, 59, 62, 66, 70, 75, 81, 86, 90, 90, 51, 49, 47, 48, 52, 58, 63, 69, 74, 79, 84, 90, 97, 102, + 106, 103, 65, 61, 59, 58, 62, 68, 73, 79, 85, 92, 98, 106, 113, 120, 124, 119, 79, 74, 71, 69, 72, 78, 84, 90, 96, 103, 110, 119, 128, + 135, 140, 137, 87, 82, 79, 77, 78, 84, 89, 96, 103, 111, 118, 126, 134, 143, 147, 151, 32, 31, 31, 32, 33, 36, 40, 44, 51, 53, 65, 66, + 79, 81, 87, 90, 31, 32, 32, 32, 33, 35, 39, 42, 49, 51, 62, 63, 75, 77, 83, 85, 31, 32, 32, 32, 33, 35, 39, 42, 49, 51, 61, 62, 74, 76, + 82, 85, 31, 32, 32, 33, 33, 34, 38, 41, 47, 49, 59, 60, 72, 74, 79, 81, 31, 32, 32, 33, 34, 35, 38, 41, 47, 49, 59, 60, 71, 73, 79, 81, + 32, 32, 33, 34, 35, 36, 39, 42, 48, 50, 59, 60, 71, 72, 78, 80, 32, 32, 33, 35, 36, 37, 40, 42, 48, 49, 58, 59, 69, 71, 77, 80, 32, 33, + 33, 35, 36, 38, 41, 42, 48, 49, 58, 59, 69, 70, 75, 77, 33, 33, 34, 36, 38, 41, 44, 46, 52, 53, 62, 63, 72, 74, 78, 78, 34, 34, 34, 37, + 39, 42, 45, 48, 53, 54, 63, 64, 73, 75, 80, 83, 36, 34, 35, 38, 42, 48, 51, 54, 58, 60, 68, 69, 78, 80, 84, 83, 36, 35, 35, 38, 42, 48, + 51, 54, 59, 60, 68, 69, 79, 80, 85, 87, 39, 37, 38, 40, 44, 50, 54, 58, 63, 65, 73, 74, 84, 85, 89, 88, 40, 38, 39, 41, 45, 51, 56, 59, + 65, 67, 75, 76, 85, 87, 90, 93, 44, 41, 41, 43, 46, 53, 59, 63, 69, 71, 79, 80, 90, 91, 96, 93, 46, 43, 43, 44, 48, 55, 60, 65, 72, 73, + 82, 83, 93, 94, 97, 100, 48, 45, 45, 46, 50, 56, 62, 67, 74, 76, 85, 86, 96, 98, 103, 100, 52, 48, 48, 49, 52, 59, 65, 70, 78, 80, 90, + 91, 101, 103, 105, 107, 53, 49, 49, 50, 53, 60, 66, 71, 79, 82, 92, 93, 103, 105, 111, 107, 58, 53, 53, 53, 57, 63, 69, 74, 83, 86, 97, + 98, 109, 111, 113, 115, 58, 54, 54, 54, 57, 63, 70, 75, 84, 87, 98, 99, 110, 112, 118, 115, 65, 60, 59, 58, 62, 68, 74, 79, 89, 92, 105, + 106, 118, 119, 122, 123, 66, 61, 60, 59, 63, 69, 75, 80, 90, 93, 106, 107, 119, 121, 126, 123, 71, 65, 65, 63, 67, 73, 79, 84, 94, 97, + 111, 112, 125, 127, 131, 132, 74, 68, 67, 66, 69, 75, 81, 86, 97, 100, 113, 115, 128, 130, 134, 132, 79, 72, 72, 70, 73, 79, 85, 90, 101, + 104, 118, 119, 133, 135, 141, 140, 81, 74, 73, 71, 75, 80, 86, 91, 102, 105, 120, 121, 135, 137, 143, 140, 82, 75, 74, 72, 75, 81, 87, + 92, 103, 106, 121, 122, 136, 139, 147, 151, 86, 78, 78, 75, 78, 84, 90, 95, 106, 109, 124, 125, 140, 142, 147, 151, 88, 81, 80, 77, 80, + 86, 90, 98, 105, 112, 122, 127, 140, 144, 152, 155, 91, 83, 82, 79, 80, 88, 90, 100, 103, 114, 119, 130, 137, 148, 151, 155, 93, 85, 85, + 81, 81, 90, 90, 102, 103, 117, 117, 134, 134, 151, 152, 160, 32, 31, 31, 31, 31, 32, 32, 32, 33, 34, 36, 36, 39, 40, 44, 46, 48, 52, 53, + 58, 58, 65, 66, 71, 74, 79, 81, 82, 86, 88, 91, 93, 31, 32, 32, 32, 32, 32, 32, 33, 33, 34, 34, 35, 37, 38, 41, 43, 45, 48, 49, 53, 54, + 60, 61, 65, 68, 72, 74, 75, 78, 81, 83, 85, 31, 32, 32, 32, 32, 33, 33, 33, 34, 34, 35, 35, 38, 39, 41, 43, 45, 48, 49, 53, 54, 59, 60, + 65, 67, 72, 73, 74, 78, 80, 82, 85, 32, 32, 32, 33, 33, 34, 35, 35, 36, 37, 38, 38, 40, 41, 43, 44, 46, 49, 50, 53, 54, 58, 59, 63, 66, + 70, 71, 72, 75, 77, 79, 81, 33, 33, 33, 33, 34, 35, 36, 36, 38, 39, 42, 42, 44, 45, 46, 48, 50, 52, 53, 57, 57, 62, 63, 67, 69, 73, 75, + 75, 78, 80, 80, 81, 36, 35, 35, 34, 35, 36, 37, 38, 41, 42, 48, 48, 50, 51, 53, 55, 56, 59, 60, 63, 63, 68, 69, 73, 75, 79, 80, 81, 84, + 86, 88, 90, 40, 39, 39, 38, 38, 39, 40, 41, 44, 45, 51, 51, 54, 56, 59, 60, 62, 65, 66, 69, 70, 74, 75, 79, 81, 85, 86, 87, 90, 90, 90, + 90, 44, 42, 42, 41, 41, 42, 42, 42, 46, 48, 54, 54, 58, 59, 63, 65, 67, 70, 71, 74, 75, 79, 80, 84, 86, 90, 91, 92, 95, 98, 100, 102, 51, + 49, 49, 47, 47, 48, 48, 48, 52, 53, 58, 59, 63, 65, 69, 72, 74, 78, 79, 83, 84, 89, 90, 94, 97, 101, 102, 103, 106, 105, 103, 103, 53, + 51, 51, 49, 49, 50, 49, 49, 53, 54, 60, 60, 65, 67, 71, 73, 76, 80, 82, 86, 87, 92, 93, 97, 100, 104, 105, 106, 109, 112, 114, 117, 65, + 62, 61, 59, 59, 59, 58, 58, 62, 63, 68, 68, 73, 75, 79, 82, 85, 90, 92, 97, 98, 105, 106, 111, 113, 118, 120, 121, 124, 122, 119, 117, + 66, 63, 62, 60, 60, 60, 59, 59, 63, 64, 69, 69, 74, 76, 80, 83, 86, 91, 93, 98, 99, 106, 107, 112, 115, 119, 121, 122, 125, 127, 130, + 134, 79, 75, 74, 72, 71, 71, 69, 69, 72, 73, 78, 79, 84, 85, 90, 93, 96, 101, 103, 109, 110, 118, 119, 125, 128, 133, 135, 136, 140, 140, + 137, 134, 81, 77, 76, 74, 73, 72, 71, 70, 74, 75, 80, 80, 85, 87, 91, 94, 98, 103, 105, 111, 112, 119, 121, 127, 130, 135, 137, 139, 142, + 144, 148, 151, 87, 83, 82, 79, 79, 78, 77, 75, 78, 80, 84, 85, 89, 90, 96, 97, 103, 105, 111, 113, 118, 122, 126, 131, 134, 141, 143, + 147, 147, 152, 151, 152, 90, 85, 85, 81, 81, 80, 80, 77, 78, 83, 83, 87, 88, 93, 93, 100, 100, 107, 107, 115, 115, 123, 123, 132, 132, + 140, 140, 151, 151, 155, 155, 160, 31, 36, 53, 81, 32, 35, 51, 76, 32, 35, 49, 73, 32, 37, 49, 71, 33, 41, 53, 74, 34, 48, 60, 80, 37, + 50, 65, 85, 41, 53, 71, 91, 45, 56, 76, 98, 49, 60, 82, 105, 54, 63, 87, 112, 61, 69, 93, 121, 68, 75, 100, 130, 74, 80, 105, 137, 78, + 84, 109, 142, 83, 88, 114, 148, 31, 32, 32, 32, 33, 34, 37, 41, 45, 49, 54, 61, 68, 74, 78, 83, 36, 35, 35, 37, 41, 48, 50, 53, 56, 60, + 63, 69, 75, 80, 84, 88, 53, 51, 49, 49, 53, 60, 65, 71, 76, 82, 87, 93, 100, 105, 109, 114, 81, 76, 73, 71, 74, 80, 85, 91, 98, 105, 112, + 121, 130, 137, 142, 148, 32, 31, 33, 40, 51, 65, 79, 87, 31, 32, 33, 39, 49, 62, 75, 83, 31, 32, 33, 39, 49, 61, 74, 82, 31, 32, 33, 38, + 47, 59, 72, 79, 31, 32, 34, 38, 47, 59, 71, 79, 32, 33, 35, 39, 48, 59, 71, 78, 32, 33, 36, 40, 48, 58, 69, 77, 32, 33, 36, 41, 48, 58, + 69, 75, 33, 34, 38, 44, 52, 62, 72, 78, 34, 34, 39, 45, 53, 63, 73, 80, 36, 35, 42, 51, 58, 68, 78, 84, 36, 35, 42, 51, 59, 68, 79, 85, + 39, 38, 44, 54, 63, 73, 84, 89, 40, 39, 45, 56, 65, 75, 85, 90, 44, 41, 46, 59, 69, 79, 90, 96, 46, 43, 48, 60, 72, 82, 93, 97, 48, 45, + 50, 62, 74, 85, 96, 103, 52, 48, 52, 65, 78, 90, 101, 105, 53, 49, 53, 66, 79, 92, 103, 111, 58, 53, 57, 69, 83, 97, 109, 113, 58, 54, + 57, 70, 84, 98, 110, 118, 65, 59, 62, 74, 89, 105, 118, 122, 66, 60, 63, 75, 90, 106, 119, 126, 71, 65, 67, 79, 94, 111, 125, 131, 74, + 67, 69, 81, 97, 113, 128, 134, 79, 72, 73, 85, 101, 118, 133, 141, 81, 73, 75, 86, 102, 120, 135, 143, 82, 74, 75, 87, 103, 121, 136, + 147, 86, 78, 78, 90, 106, 124, 140, 147, 88, 80, 80, 90, 105, 122, 140, 152, 91, 82, 80, 90, 103, 119, 137, 151, 93, 85, 81, 90, 103, + 117, 134, 152, 32, 31, 31, 31, 31, 32, 32, 32, 33, 34, 36, 36, 39, 40, 44, 46, 48, 52, 53, 58, 58, 65, 66, 71, 74, 79, 81, 82, 86, 88, + 91, 93, 31, 32, 32, 32, 32, 33, 33, 33, 34, 34, 35, 35, 38, 39, 41, 43, 45, 48, 49, 53, 54, 59, 60, 65, 67, 72, 73, 74, 78, 80, 82, 85, + 33, 33, 33, 33, 34, 35, 36, 36, 38, 39, 42, 42, 44, 45, 46, 48, 50, 52, 53, 57, 57, 62, 63, 67, 69, 73, 75, 75, 78, 80, 80, 81, 40, 39, + 39, 38, 38, 39, 40, 41, 44, 45, 51, 51, 54, 56, 59, 60, 62, 65, 66, 69, 70, 74, 75, 79, 81, 85, 86, 87, 90, 90, 90, 90, 51, 49, 49, 47, + 47, 48, 48, 48, 52, 53, 58, 59, 63, 65, 69, 72, 74, 78, 79, 83, 84, 89, 90, 94, 97, 101, 102, 103, 106, 105, 103, 103, 65, 62, 61, 59, + 59, 59, 58, 58, 62, 63, 68, 68, 73, 75, 79, 82, 85, 90, 92, 97, 98, 105, 106, 111, 113, 118, 120, 121, 124, 122, 119, 117, 79, 75, 74, + 72, 71, 71, 69, 69, 72, 73, 78, 79, 84, 85, 90, 93, 96, 101, 103, 109, 110, 118, 119, 125, 128, 133, 135, 136, 140, 140, 137, 134, 87, + 83, 82, 79, 79, 78, 77, 75, 78, 80, 84, 85, 89, 90, 96, 97, 103, 105, 111, 113, 118, 122, 126, 131, 134, 141, 143, 147, 147, 152, 151, + 152, + ], + [ + 32, 46, 49, 58, 46, 53, 55, 62, 49, 55, 70, 78, 58, 62, 78, 91, 31, 34, 42, 47, 49, 54, 60, 64, 34, 39, 45, 46, 47, 51, 56, 59, 42, 45, + 48, 49, 50, 53, 57, 60, 47, 46, 49, 55, 58, 61, 65, 66, 49, 47, 50, 58, 65, 69, 73, 74, 54, 51, 53, 61, 69, 76, 82, 83, 60, 56, 57, 65, + 73, 82, 89, 92, 64, 59, 60, 66, 74, 83, 92, 96, 32, 31, 31, 35, 40, 49, 48, 49, 50, 52, 54, 57, 61, 64, 66, 68, 31, 31, 32, 37, 41, 47, + 47, 46, 48, 49, 51, 54, 57, 60, 62, 64, 31, 32, 34, 39, 43, 46, 46, 45, 46, 47, 49, 52, 55, 57, 59, 61, 35, 37, 39, 44, 46, 47, 46, 45, + 46, 47, 48, 51, 53, 56, 57, 59, 40, 41, 43, 46, 48, 50, 49, 48, 49, 49, 51, 53, 55, 57, 59, 59, 49, 47, 46, 47, 50, 53, 53, 53, 54, 54, + 55, 57, 59, 61, 62, 62, 48, 47, 46, 46, 49, 53, 54, 55, 56, 57, 58, 60, 62, 64, 65, 65, 49, 46, 45, 45, 48, 53, 55, 58, 60, 61, 62, 64, + 66, 68, 69, 69, 50, 48, 46, 46, 49, 54, 56, 60, 61, 63, 65, 67, 69, 71, 72, 72, 52, 49, 47, 47, 49, 54, 57, 61, 63, 66, 68, 71, 73, 75, + 76, 77, 54, 51, 49, 48, 51, 55, 58, 62, 65, 68, 71, 74, 76, 78, 80, 81, 57, 54, 52, 51, 53, 57, 60, 64, 67, 71, 74, 77, 80, 83, 84, 85, + 61, 57, 55, 53, 55, 59, 62, 66, 69, 73, 76, 80, 84, 87, 89, 89, 64, 60, 57, 56, 57, 61, 64, 68, 71, 75, 78, 83, 87, 90, 92, 94, 66, 62, + 59, 57, 59, 62, 65, 69, 72, 76, 80, 84, 89, 92, 94, 96, 68, 64, 61, 59, 59, 62, 65, 69, 72, 77, 81, 85, 89, 94, 96, 98, 32, 31, 31, 30, + 31, 33, 35, 36, 40, 41, 49, 49, 48, 48, 49, 50, 50, 52, 52, 54, 54, 57, 57, 60, 61, 63, 64, 65, 66, 67, 68, 69, 31, 31, 31, 31, 32, 34, + 37, 38, 41, 42, 47, 47, 47, 47, 47, 47, 48, 49, 50, 52, 52, 54, 55, 57, 58, 60, 61, 61, 63, 64, 64, 65, 31, 31, 31, 31, 32, 35, 37, 39, + 41, 42, 47, 47, 47, 46, 46, 47, 48, 49, 49, 51, 51, 54, 54, 56, 57, 59, 60, 61, 62, 63, 64, 65, 30, 31, 31, 32, 33, 35, 38, 40, 42, 42, + 46, 46, 45, 45, 45, 45, 46, 47, 47, 49, 49, 52, 52, 54, 55, 57, 58, 58, 60, 61, 61, 62, 31, 32, 32, 33, 34, 37, 39, 41, 43, 43, 46, 46, + 46, 45, 45, 46, 46, 47, 47, 49, 49, 51, 52, 54, 55, 57, 57, 58, 59, 60, 61, 62, 33, 34, 35, 35, 37, 39, 41, 43, 44, 45, 47, 47, 46, 46, + 45, 46, 46, 47, 47, 49, 49, 51, 51, 53, 54, 56, 57, 57, 58, 59, 60, 61, 35, 37, 37, 38, 39, 41, 44, 46, 46, 46, 47, 47, 46, 46, 45, 46, + 46, 47, 47, 48, 48, 50, 51, 52, 53, 55, 56, 56, 57, 58, 59, 61, 36, 38, 39, 40, 41, 43, 46, 47, 47, 47, 48, 47, 46, 46, 45, 46, 46, 46, + 47, 48, 48, 50, 50, 52, 53, 54, 55, 55, 56, 57, 58, 58, 40, 41, 41, 42, 43, 44, 46, 47, 48, 48, 50, 49, 49, 49, 48, 49, 49, 49, 49, 51, + 51, 52, 53, 54, 55, 57, 57, 58, 59, 59, 59, 59, 41, 42, 42, 42, 43, 45, 46, 47, 48, 48, 50, 50, 49, 49, 49, 49, 50, 50, 50, 52, 52, 53, + 53, 55, 56, 57, 58, 58, 59, 60, 61, 62, 49, 47, 47, 46, 46, 47, 47, 48, 50, 50, 53, 53, 53, 53, 53, 54, 54, 54, 54, 55, 55, 56, 57, 58, + 59, 60, 61, 61, 62, 62, 62, 62, 49, 47, 47, 46, 46, 47, 47, 47, 49, 50, 53, 53, 53, 53, 54, 54, 54, 54, 54, 55, 56, 57, 57, 59, 59, 61, + 61, 62, 63, 63, 64, 65, 48, 47, 47, 45, 46, 46, 46, 46, 49, 49, 53, 53, 54, 54, 55, 56, 56, 57, 57, 58, 58, 60, 60, 61, 62, 63, 64, 64, + 65, 66, 65, 65, 48, 47, 46, 45, 45, 46, 46, 46, 49, 49, 53, 53, 54, 55, 56, 57, 57, 58, 58, 59, 60, 61, 61, 63, 63, 65, 65, 65, 66, 66, + 67, 68, 49, 47, 46, 45, 45, 45, 45, 45, 48, 49, 53, 54, 55, 56, 58, 59, 60, 61, 61, 62, 62, 63, 64, 65, 66, 67, 68, 68, 69, 70, 69, 68, + 50, 47, 47, 45, 46, 46, 46, 46, 49, 49, 54, 54, 56, 57, 59, 60, 60, 62, 62, 63, 64, 65, 65, 67, 68, 69, 69, 70, 70, 70, 71, 71, 50, 48, + 48, 46, 46, 46, 46, 46, 49, 50, 54, 54, 56, 57, 60, 60, 61, 63, 63, 65, 65, 67, 67, 68, 69, 71, 71, 71, 72, 73, 72, 71, 52, 49, 49, 47, + 47, 47, 47, 46, 49, 50, 54, 54, 57, 58, 61, 62, 63, 65, 65, 67, 67, 69, 70, 71, 72, 73, 74, 74, 75, 74, 74, 75, 52, 50, 49, 47, 47, 47, + 47, 47, 49, 50, 54, 54, 57, 58, 61, 62, 63, 65, 66, 68, 68, 70, 71, 72, 73, 75, 75, 75, 76, 77, 77, 75, 54, 52, 51, 49, 49, 49, 48, 48, + 51, 52, 55, 55, 58, 59, 62, 63, 65, 67, 68, 70, 70, 73, 73, 75, 76, 78, 78, 78, 79, 78, 78, 79, 54, 52, 51, 49, 49, 49, 48, 48, 51, 52, + 55, 56, 58, 60, 62, 64, 65, 67, 68, 70, 71, 73, 74, 75, 76, 78, 78, 79, 80, 81, 81, 79, 57, 54, 54, 52, 51, 51, 50, 50, 52, 53, 56, 57, + 60, 61, 63, 65, 67, 69, 70, 73, 73, 76, 77, 79, 80, 82, 82, 83, 84, 83, 82, 83, 57, 55, 54, 52, 52, 51, 51, 50, 53, 53, 57, 57, 60, 61, + 64, 65, 67, 70, 71, 73, 74, 77, 77, 79, 80, 82, 83, 83, 84, 85, 85, 83, 60, 57, 56, 54, 54, 53, 52, 52, 54, 55, 58, 59, 61, 63, 65, 67, + 68, 71, 72, 75, 75, 79, 79, 82, 83, 85, 86, 86, 87, 87, 86, 87, 61, 58, 57, 55, 55, 54, 53, 53, 55, 56, 59, 59, 62, 63, 66, 68, 69, 72, + 73, 76, 76, 80, 80, 83, 84, 86, 87, 88, 89, 89, 89, 87, 63, 60, 59, 57, 57, 56, 55, 54, 57, 57, 60, 61, 63, 65, 67, 69, 71, 73, 75, 78, + 78, 82, 82, 85, 86, 89, 89, 90, 91, 92, 90, 91, 64, 61, 60, 58, 57, 57, 56, 55, 57, 58, 61, 61, 64, 65, 68, 69, 71, 74, 75, 78, 78, 82, + 83, 86, 87, 89, 90, 91, 92, 93, 94, 91, 65, 61, 61, 58, 58, 57, 56, 55, 58, 58, 61, 62, 64, 65, 68, 70, 71, 74, 75, 78, 79, 83, 83, 86, + 88, 90, 91, 91, 93, 94, 94, 96, 66, 63, 62, 60, 59, 58, 57, 56, 59, 59, 62, 63, 65, 66, 69, 70, 72, 75, 76, 79, 80, 84, 84, 87, 89, 91, + 92, 93, 94, 94, 96, 96, 67, 64, 63, 61, 60, 59, 58, 57, 59, 60, 62, 63, 66, 66, 70, 70, 73, 74, 77, 78, 81, 83, 85, 87, 89, 92, 93, 94, + 94, 96, 96, 97, 68, 64, 64, 61, 61, 60, 59, 58, 59, 61, 62, 64, 65, 67, 69, 71, 72, 74, 77, 78, 81, 82, 85, 86, 89, 90, 94, 94, 96, 96, + 98, 97, 69, 65, 65, 62, 62, 61, 61, 58, 59, 62, 62, 65, 65, 68, 68, 71, 71, 75, 75, 79, 79, 83, 83, 87, 87, 91, 91, 96, 96, 97, 97, 99, + 31, 47, 50, 61, 36, 47, 47, 57, 43, 50, 50, 58, 45, 53, 58, 65, 47, 54, 66, 74, 52, 56, 70, 82, 57, 60, 75, 90, 61, 63, 77, 93, 31, 36, + 43, 45, 47, 52, 57, 61, 47, 47, 50, 53, 54, 56, 60, 63, 50, 47, 50, 58, 66, 70, 75, 77, 61, 57, 58, 65, 74, 82, 90, 93, 32, 32, 40, 49, + 51, 57, 63, 67, 31, 33, 41, 47, 49, 54, 59, 63, 31, 35, 43, 46, 47, 51, 57, 60, 35, 39, 46, 46, 47, 50, 55, 58, 41, 43, 48, 49, 49, 52, + 57, 59, 49, 47, 50, 53, 54, 57, 60, 62, 48, 46, 49, 54, 57, 60, 64, 65, 49, 45, 48, 56, 61, 64, 67, 69, 50, 46, 49, 57, 63, 67, 71, 73, + 52, 48, 50, 58, 65, 71, 75, 77, 54, 50, 51, 59, 67, 73, 78, 81, 57, 52, 53, 61, 69, 77, 82, 85, 61, 55, 56, 63, 72, 80, 86, 88, 64, 58, + 58, 65, 73, 82, 89, 92, 66, 59, 59, 66, 75, 84, 91, 94, 68, 61, 59, 65, 72, 81, 89, 95, 32, 31, 31, 35, 41, 49, 48, 49, 50, 52, 54, 57, + 61, 64, 66, 68, 32, 33, 35, 39, 43, 47, 46, 45, 46, 48, 50, 52, 55, 58, 59, 61, 40, 41, 43, 46, 48, 50, 49, 48, 49, 50, 51, 53, 56, 58, + 59, 59, 49, 47, 46, 46, 49, 53, 54, 56, 57, 58, 59, 61, 63, 65, 66, 65, 51, 49, 47, 47, 49, 54, 57, 61, 63, 65, 67, 69, 72, 73, 75, 72, + 57, 54, 51, 50, 52, 57, 60, 64, 67, 71, 73, 77, 80, 82, 84, 81, 63, 59, 57, 55, 57, 60, 64, 67, 71, 75, 78, 82, 86, 89, 91, 89, 67, 63, + 60, 58, 59, 62, 65, 69, 73, 77, 81, 85, 88, 92, 94, 95, 32, 31, 32, 37, 40, 48, 49, 49, 51, 52, 57, 58, 63, 64, 67, 67, 31, 31, 33, 38, + 41, 47, 47, 47, 49, 50, 54, 55, 60, 61, 63, 64, 31, 31, 33, 38, 41, 47, 47, 47, 49, 49, 54, 54, 59, 60, 63, 64, 30, 32, 33, 40, 42, 46, + 45, 45, 47, 48, 52, 52, 57, 58, 60, 61, 31, 33, 35, 41, 43, 46, 46, 45, 47, 48, 51, 52, 57, 57, 60, 61, 33, 36, 37, 43, 44, 47, 46, 46, + 47, 47, 51, 52, 56, 57, 59, 60, 35, 38, 39, 45, 46, 47, 46, 45, 47, 47, 50, 51, 55, 56, 58, 60, 37, 40, 41, 47, 47, 47, 46, 45, 46, 47, + 50, 50, 54, 55, 57, 58, 41, 42, 43, 47, 48, 49, 49, 48, 49, 50, 52, 53, 57, 57, 59, 58, 42, 43, 43, 47, 48, 50, 49, 49, 50, 50, 53, 54, + 57, 58, 60, 61, 49, 46, 47, 48, 50, 53, 53, 53, 54, 54, 57, 57, 60, 61, 62, 61, 49, 46, 47, 48, 50, 53, 53, 54, 54, 55, 57, 57, 61, 61, + 63, 64, 48, 46, 46, 47, 49, 53, 54, 56, 57, 57, 60, 60, 64, 64, 65, 64, 48, 45, 46, 46, 49, 53, 55, 56, 58, 58, 61, 61, 65, 65, 66, 67, + 49, 45, 45, 46, 48, 53, 56, 58, 61, 61, 64, 64, 67, 68, 69, 67, 49, 46, 46, 46, 49, 53, 57, 59, 62, 62, 65, 66, 69, 69, 70, 70, 50, 46, + 46, 46, 49, 54, 57, 59, 63, 64, 67, 67, 71, 71, 73, 71, 51, 47, 47, 47, 49, 54, 58, 61, 64, 66, 69, 70, 73, 74, 74, 74, 52, 48, 48, 47, + 50, 54, 58, 61, 65, 66, 71, 71, 75, 75, 77, 74, 54, 50, 49, 48, 51, 55, 59, 62, 67, 68, 73, 73, 77, 78, 78, 78, 54, 50, 50, 49, 51, 55, + 59, 62, 67, 68, 73, 74, 78, 78, 81, 78, 57, 52, 52, 50, 52, 56, 60, 64, 69, 70, 76, 77, 82, 82, 83, 82, 57, 52, 52, 51, 53, 57, 61, 64, + 69, 71, 77, 77, 82, 83, 85, 82, 60, 54, 54, 52, 55, 58, 62, 65, 71, 72, 79, 79, 85, 86, 87, 86, 61, 56, 55, 53, 56, 59, 63, 66, 72, 73, + 80, 81, 86, 87, 88, 86, 63, 57, 57, 55, 57, 60, 64, 67, 73, 75, 82, 82, 89, 90, 92, 90, 64, 58, 58, 55, 58, 61, 65, 68, 73, 75, 82, 83, + 89, 90, 92, 90, 64, 59, 58, 56, 58, 61, 65, 68, 74, 75, 83, 83, 90, 91, 94, 95, 66, 60, 59, 57, 59, 62, 66, 69, 75, 76, 84, 85, 91, 92, + 94, 95, 67, 61, 60, 58, 59, 63, 66, 70, 74, 77, 82, 85, 91, 93, 96, 96, 68, 62, 61, 58, 59, 64, 65, 71, 72, 78, 81, 86, 89, 94, 95, 96, + 68, 62, 62, 59, 59, 65, 65, 71, 71, 79, 79, 87, 87, 95, 95, 98, 32, 31, 31, 30, 31, 33, 35, 37, 41, 42, 49, 49, 48, 48, 49, 49, 50, 51, + 52, 54, 54, 57, 57, 60, 61, 63, 64, 64, 66, 67, 68, 68, 31, 31, 31, 32, 33, 36, 38, 40, 42, 43, 46, 46, 46, 45, 45, 46, 46, 47, 48, 50, + 50, 52, 52, 54, 56, 57, 58, 59, 60, 61, 62, 62, 32, 33, 33, 33, 35, 37, 39, 41, 43, 43, 47, 47, 46, 46, 45, 46, 46, 47, 48, 49, 50, 52, + 52, 54, 55, 57, 58, 58, 59, 60, 61, 62, 37, 38, 38, 40, 41, 43, 45, 47, 47, 47, 48, 48, 47, 46, 46, 46, 46, 47, 47, 48, 49, 50, 51, 52, + 53, 55, 55, 56, 57, 58, 58, 59, 40, 41, 41, 42, 43, 44, 46, 47, 48, 48, 50, 50, 49, 49, 48, 49, 49, 49, 50, 51, 51, 52, 53, 55, 56, 57, + 58, 58, 59, 59, 59, 59, 48, 47, 47, 46, 46, 47, 47, 47, 49, 50, 53, 53, 53, 53, 53, 53, 54, 54, 54, 55, 55, 56, 57, 58, 59, 60, 61, 61, + 62, 63, 64, 65, 49, 47, 47, 45, 46, 46, 46, 46, 49, 49, 53, 53, 54, 55, 56, 57, 57, 58, 58, 59, 59, 60, 61, 62, 63, 64, 65, 65, 66, 66, + 65, 65, 49, 47, 47, 45, 45, 46, 45, 45, 48, 49, 53, 54, 56, 56, 58, 59, 59, 61, 61, 62, 62, 64, 64, 65, 66, 67, 68, 68, 69, 70, 71, 71, + 51, 49, 49, 47, 47, 47, 47, 46, 49, 50, 54, 54, 57, 58, 61, 62, 63, 64, 65, 67, 67, 69, 69, 71, 72, 73, 73, 74, 75, 74, 72, 71, 52, 50, + 49, 48, 48, 47, 47, 47, 50, 50, 54, 55, 57, 58, 61, 62, 64, 66, 66, 68, 68, 70, 71, 72, 73, 75, 75, 75, 76, 77, 78, 79, 57, 54, 54, 52, + 51, 51, 50, 50, 52, 53, 57, 57, 60, 61, 64, 65, 67, 69, 71, 73, 73, 76, 77, 79, 80, 82, 82, 83, 84, 82, 81, 79, 58, 55, 54, 52, 52, 52, + 51, 50, 53, 54, 57, 57, 60, 61, 64, 66, 67, 70, 71, 73, 74, 77, 77, 79, 81, 82, 83, 83, 85, 85, 86, 87, 63, 60, 59, 57, 57, 56, 55, 54, + 57, 57, 60, 61, 64, 65, 67, 69, 71, 73, 75, 77, 78, 82, 82, 85, 86, 89, 89, 90, 91, 91, 89, 87, 64, 61, 60, 58, 57, 57, 56, 55, 57, 58, + 61, 61, 64, 65, 68, 69, 71, 74, 75, 78, 78, 82, 83, 86, 87, 90, 90, 91, 92, 93, 94, 95, 67, 63, 63, 60, 60, 59, 58, 57, 59, 60, 62, 63, + 65, 66, 69, 70, 73, 74, 77, 78, 81, 83, 85, 87, 88, 92, 92, 94, 94, 96, 95, 95, 67, 64, 64, 61, 61, 60, 60, 58, 58, 61, 61, 64, 64, 67, + 67, 70, 71, 74, 74, 78, 78, 82, 82, 86, 86, 90, 90, 95, 95, 96, 96, 98, 31, 48, 52, 64, 31, 47, 49, 60, 33, 46, 48, 57, 38, 47, 47, 56, + 42, 49, 50, 57, 46, 53, 54, 61, 46, 53, 57, 64, 45, 53, 61, 68, 46, 54, 64, 71, 48, 54, 66, 75, 50, 55, 68, 78, 52, 57, 71, 83, 56, 59, + 73, 87, 58, 61, 75, 90, 60, 62, 76, 92, 62, 64, 78, 94, 31, 31, 33, 38, 42, 46, 46, 45, 46, 48, 50, 52, 56, 58, 60, 62, 48, 47, 46, 47, + 49, 53, 53, 53, 54, 54, 55, 57, 59, 61, 62, 64, 52, 49, 48, 47, 50, 54, 57, 61, 64, 66, 68, 71, 73, 75, 76, 78, 64, 60, 57, 56, 57, 61, + 64, 68, 71, 75, 78, 83, 87, 90, 92, 94, 32, 32, 40, 49, 51, 57, 63, 67, 31, 33, 41, 47, 49, 54, 60, 63, 31, 33, 41, 47, 49, 54, 59, 63, + 30, 33, 42, 45, 47, 52, 57, 60, 31, 35, 43, 46, 47, 51, 57, 60, 33, 37, 44, 46, 47, 51, 56, 59, 35, 39, 46, 46, 47, 50, 55, 58, 37, 41, + 47, 46, 46, 50, 54, 57, 41, 43, 48, 49, 49, 52, 57, 59, 42, 43, 48, 49, 50, 53, 57, 60, 49, 47, 50, 53, 54, 57, 60, 62, 49, 47, 50, 53, + 54, 57, 61, 63, 48, 46, 49, 54, 57, 60, 64, 65, 48, 46, 49, 55, 58, 61, 65, 66, 49, 45, 48, 56, 61, 64, 67, 69, 49, 46, 49, 57, 62, 65, + 69, 70, 50, 46, 49, 57, 63, 67, 71, 73, 51, 47, 49, 58, 64, 69, 73, 74, 52, 48, 50, 58, 65, 71, 75, 77, 54, 49, 51, 59, 67, 73, 77, 78, + 54, 50, 51, 59, 67, 73, 78, 81, 57, 52, 52, 60, 69, 76, 82, 83, 57, 52, 53, 61, 69, 77, 82, 85, 60, 54, 55, 62, 71, 79, 85, 87, 61, 55, + 56, 63, 72, 80, 86, 88, 63, 57, 57, 64, 73, 82, 89, 92, 64, 58, 58, 65, 73, 82, 89, 92, 64, 58, 58, 65, 74, 83, 90, 94, 66, 59, 59, 66, + 75, 84, 91, 94, 67, 60, 59, 66, 74, 82, 91, 96, 68, 61, 59, 65, 72, 81, 89, 95, 68, 62, 59, 65, 71, 79, 87, 95, 32, 31, 31, 30, 31, 33, + 35, 37, 41, 42, 49, 49, 48, 48, 49, 49, 50, 51, 52, 54, 54, 57, 57, 60, 61, 63, 64, 64, 66, 67, 68, 68, 32, 33, 33, 33, 35, 37, 39, 41, + 43, 43, 47, 47, 46, 46, 45, 46, 46, 47, 48, 49, 50, 52, 52, 54, 55, 57, 58, 58, 59, 60, 61, 62, 40, 41, 41, 42, 43, 44, 46, 47, 48, 48, + 50, 50, 49, 49, 48, 49, 49, 49, 50, 51, 51, 52, 53, 55, 56, 57, 58, 58, 59, 59, 59, 59, 49, 47, 47, 45, 46, 46, 46, 46, 49, 49, 53, 53, + 54, 55, 56, 57, 57, 58, 58, 59, 59, 60, 61, 62, 63, 64, 65, 65, 66, 66, 65, 65, 51, 49, 49, 47, 47, 47, 47, 46, 49, 50, 54, 54, 57, 58, + 61, 62, 63, 64, 65, 67, 67, 69, 69, 71, 72, 73, 73, 74, 75, 74, 72, 71, 57, 54, 54, 52, 51, 51, 50, 50, 52, 53, 57, 57, 60, 61, 64, 65, + 67, 69, 71, 73, 73, 76, 77, 79, 80, 82, 82, 83, 84, 82, 81, 79, 63, 60, 59, 57, 57, 56, 55, 54, 57, 57, 60, 61, 64, 65, 67, 69, 71, 73, + 75, 77, 78, 82, 82, 85, 86, 89, 89, 90, 91, 91, 89, 87, 67, 63, 63, 60, 60, 59, 58, 57, 59, 60, 62, 63, 65, 66, 69, 70, 73, 74, 77, 78, + 81, 83, 85, 87, 88, 92, 92, 94, 94, 96, 95, 95, + ], + ], + [ + [ + 32, 34, 49, 72, 34, 48, 60, 79, 49, 60, 82, 104, 72, 79, 104, 134, 32, 32, 34, 38, 46, 56, 68, 78, 32, 33, 35, 39, 45, 54, 64, 74, 34, + 35, 39, 45, 51, 58, 68, 76, 38, 39, 45, 54, 61, 69, 78, 86, 46, 45, 51, 61, 71, 80, 90, 99, 56, 54, 58, 69, 80, 92, 103, 113, 68, 64, 68, + 78, 90, 103, 117, 128, 78, 74, 76, 86, 99, 113, 128, 140, 32, 31, 31, 31, 32, 34, 36, 39, 44, 48, 54, 59, 65, 71, 80, 83, 31, 32, 32, 32, + 32, 34, 35, 38, 42, 46, 51, 56, 62, 68, 76, 78, 31, 32, 32, 32, 32, 33, 34, 37, 41, 44, 49, 54, 59, 65, 72, 75, 31, 32, 32, 33, 34, 35, + 36, 39, 42, 45, 50, 54, 59, 64, 71, 74, 32, 32, 32, 34, 35, 37, 38, 40, 42, 46, 49, 53, 58, 63, 69, 72, 34, 34, 33, 35, 37, 39, 42, 45, + 47, 51, 54, 58, 63, 68, 74, 76, 36, 35, 34, 36, 38, 42, 48, 50, 54, 57, 60, 64, 68, 73, 79, 81, 39, 38, 37, 39, 40, 45, 50, 54, 58, 61, + 65, 69, 73, 78, 84, 86, 44, 42, 41, 42, 42, 47, 54, 58, 63, 67, 71, 75, 79, 84, 90, 92, 48, 46, 44, 45, 46, 51, 57, 61, 67, 71, 76, 80, + 85, 90, 96, 99, 54, 51, 49, 50, 49, 54, 60, 65, 71, 76, 82, 87, 92, 97, 104, 106, 59, 56, 54, 54, 53, 58, 64, 69, 75, 80, 87, 92, 98, + 103, 110, 113, 65, 62, 59, 59, 58, 63, 68, 73, 79, 85, 92, 98, 105, 111, 118, 121, 71, 68, 65, 64, 63, 68, 73, 78, 84, 90, 97, 103, 111, + 117, 125, 128, 80, 76, 72, 71, 69, 74, 79, 84, 90, 96, 104, 110, 118, 125, 134, 137, 83, 78, 75, 74, 72, 76, 81, 86, 92, 99, 106, 113, + 121, 128, 137, 140, 32, 31, 31, 31, 31, 31, 31, 32, 32, 34, 34, 36, 36, 39, 39, 44, 44, 48, 48, 54, 54, 59, 59, 65, 65, 71, 71, 80, 80, + 83, 83, 87, 31, 32, 32, 32, 32, 32, 32, 32, 32, 34, 34, 35, 35, 38, 38, 42, 42, 46, 46, 51, 51, 56, 56, 62, 62, 68, 68, 76, 76, 78, 78, + 83, 31, 32, 32, 32, 32, 32, 32, 32, 32, 34, 34, 35, 35, 38, 38, 42, 42, 46, 46, 51, 51, 56, 56, 62, 62, 68, 68, 76, 76, 78, 78, 83, 31, + 32, 32, 32, 32, 32, 32, 32, 32, 33, 33, 34, 34, 37, 37, 41, 41, 44, 44, 49, 49, 54, 54, 59, 59, 65, 65, 72, 72, 75, 75, 79, 31, 32, 32, + 32, 32, 32, 32, 32, 32, 33, 33, 34, 34, 37, 37, 41, 41, 44, 44, 49, 49, 54, 54, 59, 59, 65, 65, 72, 72, 75, 75, 79, 31, 32, 32, 32, 32, + 33, 33, 34, 34, 35, 35, 36, 36, 39, 39, 42, 42, 45, 45, 50, 50, 54, 54, 59, 59, 64, 64, 71, 71, 74, 74, 77, 31, 32, 32, 32, 32, 33, 33, + 34, 34, 35, 35, 36, 36, 39, 39, 42, 42, 45, 45, 50, 50, 54, 54, 59, 59, 64, 64, 71, 71, 74, 74, 77, 32, 32, 32, 32, 32, 34, 34, 35, 35, + 37, 37, 38, 38, 40, 40, 42, 42, 46, 46, 49, 49, 53, 53, 58, 58, 63, 63, 69, 69, 72, 72, 75, 32, 32, 32, 32, 32, 34, 34, 35, 35, 37, 37, + 38, 38, 40, 40, 42, 42, 46, 46, 49, 49, 53, 53, 58, 58, 63, 63, 69, 69, 72, 72, 75, 34, 34, 34, 33, 33, 35, 35, 37, 37, 39, 39, 42, 42, + 45, 45, 47, 47, 51, 51, 54, 54, 58, 58, 63, 63, 68, 68, 74, 74, 76, 76, 80, 34, 34, 34, 33, 33, 35, 35, 37, 37, 39, 39, 42, 42, 45, 45, + 47, 47, 51, 51, 54, 54, 58, 58, 63, 63, 68, 68, 74, 74, 76, 76, 80, 36, 35, 35, 34, 34, 36, 36, 38, 38, 42, 42, 48, 48, 50, 50, 54, 54, + 57, 57, 60, 60, 64, 64, 68, 68, 73, 73, 79, 79, 81, 81, 84, 36, 35, 35, 34, 34, 36, 36, 38, 38, 42, 42, 48, 48, 50, 50, 54, 54, 57, 57, + 60, 60, 64, 64, 68, 68, 73, 73, 79, 79, 81, 81, 84, 39, 38, 38, 37, 37, 39, 39, 40, 40, 45, 45, 50, 50, 54, 54, 58, 58, 61, 61, 65, 65, + 69, 69, 73, 73, 78, 78, 84, 84, 86, 86, 90, 39, 38, 38, 37, 37, 39, 39, 40, 40, 45, 45, 50, 50, 54, 54, 58, 58, 61, 61, 65, 65, 69, 69, + 73, 73, 78, 78, 84, 84, 86, 86, 90, 44, 42, 42, 41, 41, 42, 42, 42, 42, 47, 47, 54, 54, 58, 58, 63, 63, 67, 67, 71, 71, 75, 75, 79, 79, + 84, 84, 90, 90, 92, 92, 96, 44, 42, 42, 41, 41, 42, 42, 42, 42, 47, 47, 54, 54, 58, 58, 63, 63, 67, 67, 71, 71, 75, 75, 79, 79, 84, 84, + 90, 90, 92, 92, 96, 48, 46, 46, 44, 44, 45, 45, 46, 46, 51, 51, 57, 57, 61, 61, 67, 67, 71, 71, 76, 76, 80, 80, 85, 85, 90, 90, 96, 96, + 99, 99, 102, 48, 46, 46, 44, 44, 45, 45, 46, 46, 51, 51, 57, 57, 61, 61, 67, 67, 71, 71, 76, 76, 80, 80, 85, 85, 90, 90, 96, 96, 99, 99, + 102, 54, 51, 51, 49, 49, 50, 50, 49, 49, 54, 54, 60, 60, 65, 65, 71, 71, 76, 76, 82, 82, 87, 87, 92, 92, 97, 97, 104, 104, 106, 106, 109, + 54, 51, 51, 49, 49, 50, 50, 49, 49, 54, 54, 60, 60, 65, 65, 71, 71, 76, 76, 82, 82, 87, 87, 92, 92, 97, 97, 104, 104, 106, 106, 109, 59, + 56, 56, 54, 54, 54, 54, 53, 53, 58, 58, 64, 64, 69, 69, 75, 75, 80, 80, 87, 87, 92, 92, 98, 98, 103, 103, 110, 110, 113, 113, 116, 59, + 56, 56, 54, 54, 54, 54, 53, 53, 58, 58, 64, 64, 69, 69, 75, 75, 80, 80, 87, 87, 92, 92, 98, 98, 103, 103, 110, 110, 113, 113, 116, 65, + 62, 62, 59, 59, 59, 59, 58, 58, 63, 63, 68, 68, 73, 73, 79, 79, 85, 85, 92, 92, 98, 98, 105, 105, 111, 111, 118, 118, 121, 121, 124, 65, + 62, 62, 59, 59, 59, 59, 58, 58, 63, 63, 68, 68, 73, 73, 79, 79, 85, 85, 92, 92, 98, 98, 105, 105, 111, 111, 118, 118, 121, 121, 124, 71, + 68, 68, 65, 65, 64, 64, 63, 63, 68, 68, 73, 73, 78, 78, 84, 84, 90, 90, 97, 97, 103, 103, 111, 111, 117, 117, 125, 125, 128, 128, 132, + 71, 68, 68, 65, 65, 64, 64, 63, 63, 68, 68, 73, 73, 78, 78, 84, 84, 90, 90, 97, 97, 103, 103, 111, 111, 117, 117, 125, 125, 128, 128, + 132, 80, 76, 76, 72, 72, 71, 71, 69, 69, 74, 74, 79, 79, 84, 84, 90, 90, 96, 96, 104, 104, 110, 110, 118, 118, 125, 125, 134, 134, 137, + 137, 141, 80, 76, 76, 72, 72, 71, 71, 69, 69, 74, 74, 79, 79, 84, 84, 90, 90, 96, 96, 104, 104, 110, 110, 118, 118, 125, 125, 134, 134, + 137, 137, 141, 83, 78, 78, 75, 75, 74, 74, 72, 72, 76, 76, 81, 81, 86, 86, 92, 92, 99, 99, 106, 106, 113, 113, 121, 121, 128, 128, 137, + 137, 140, 140, 144, 83, 78, 78, 75, 75, 74, 74, 72, 72, 76, 76, 81, 81, 86, 86, 92, 92, 99, 99, 106, 106, 113, 113, 121, 121, 128, 128, + 137, 137, 140, 140, 144, 87, 83, 83, 79, 79, 77, 77, 75, 75, 80, 80, 84, 84, 90, 90, 96, 96, 102, 102, 109, 109, 116, 116, 124, 124, 132, + 132, 141, 141, 144, 144, 149, 32, 35, 51, 75, 32, 36, 50, 71, 34, 42, 54, 73, 37, 50, 65, 84, 45, 56, 76, 96, 54, 63, 87, 110, 65, 73, + 97, 125, 75, 81, 106, 136, 32, 32, 34, 37, 45, 54, 65, 75, 35, 36, 42, 50, 56, 63, 73, 81, 51, 50, 54, 65, 76, 87, 97, 106, 75, 71, 73, + 84, 96, 110, 125, 136, 32, 31, 32, 36, 44, 53, 65, 79, 31, 32, 32, 35, 42, 51, 62, 75, 31, 32, 33, 34, 41, 49, 59, 72, 32, 32, 34, 36, + 42, 50, 59, 71, 32, 33, 35, 38, 42, 49, 58, 69, 34, 34, 37, 42, 48, 54, 63, 73, 36, 34, 38, 48, 54, 60, 68, 78, 39, 37, 40, 50, 58, 65, + 73, 84, 44, 41, 43, 53, 63, 71, 79, 90, 48, 45, 46, 56, 67, 76, 85, 96, 53, 49, 50, 60, 71, 82, 92, 103, 58, 54, 54, 63, 75, 87, 98, 110, + 65, 60, 58, 68, 79, 92, 105, 118, 71, 65, 63, 73, 84, 97, 111, 125, 79, 72, 70, 79, 90, 104, 118, 133, 82, 75, 72, 81, 92, 106, 121, 136, + 32, 31, 31, 32, 32, 34, 36, 39, 44, 48, 53, 58, 65, 71, 79, 82, 31, 32, 32, 32, 33, 34, 34, 37, 41, 45, 49, 54, 60, 65, 72, 75, 32, 32, + 33, 34, 35, 37, 38, 40, 43, 46, 50, 54, 58, 63, 70, 72, 36, 35, 34, 36, 38, 42, 48, 50, 53, 56, 60, 63, 68, 73, 79, 81, 44, 42, 41, 42, + 42, 48, 54, 58, 63, 67, 71, 75, 79, 84, 90, 92, 53, 51, 49, 50, 49, 54, 60, 65, 71, 76, 82, 87, 92, 97, 104, 106, 65, 62, 59, 59, 58, 63, + 68, 73, 79, 85, 92, 98, 105, 111, 118, 121, 79, 75, 72, 71, 69, 73, 78, 84, 90, 96, 103, 110, 118, 125, 133, 136, 32, 31, 31, 32, 32, 36, + 36, 44, 44, 53, 53, 65, 65, 79, 79, 87, 31, 32, 32, 32, 32, 35, 35, 42, 42, 51, 51, 62, 62, 75, 75, 82, 31, 32, 32, 32, 32, 35, 35, 42, + 42, 51, 51, 62, 62, 75, 75, 82, 31, 32, 32, 33, 33, 34, 34, 41, 41, 49, 49, 59, 59, 72, 72, 78, 31, 32, 32, 33, 33, 34, 34, 41, 41, 49, + 49, 59, 59, 72, 72, 78, 32, 32, 32, 34, 34, 36, 36, 42, 42, 50, 50, 59, 59, 71, 71, 77, 32, 32, 32, 34, 34, 36, 36, 42, 42, 50, 50, 59, + 59, 71, 71, 77, 32, 33, 33, 35, 35, 38, 38, 42, 42, 49, 49, 58, 58, 69, 69, 75, 32, 33, 33, 35, 35, 38, 38, 42, 42, 49, 49, 58, 58, 69, + 69, 75, 34, 34, 34, 37, 37, 42, 42, 48, 48, 54, 54, 63, 63, 73, 73, 79, 34, 34, 34, 37, 37, 42, 42, 48, 48, 54, 54, 63, 63, 73, 73, 79, + 36, 34, 34, 38, 38, 48, 48, 54, 54, 60, 60, 68, 68, 78, 78, 84, 36, 34, 34, 38, 38, 48, 48, 54, 54, 60, 60, 68, 68, 78, 78, 84, 39, 37, + 37, 40, 40, 50, 50, 58, 58, 65, 65, 73, 73, 84, 84, 89, 39, 37, 37, 40, 40, 50, 50, 58, 58, 65, 65, 73, 73, 84, 84, 89, 44, 41, 41, 43, + 43, 53, 53, 63, 63, 71, 71, 79, 79, 90, 90, 95, 44, 41, 41, 43, 43, 53, 53, 63, 63, 71, 71, 79, 79, 90, 90, 95, 48, 45, 45, 46, 46, 56, + 56, 67, 67, 76, 76, 85, 85, 96, 96, 102, 48, 45, 45, 46, 46, 56, 56, 67, 67, 76, 76, 85, 85, 96, 96, 102, 53, 49, 49, 50, 50, 60, 60, 71, + 71, 82, 82, 92, 92, 103, 103, 109, 53, 49, 49, 50, 50, 60, 60, 71, 71, 82, 82, 92, 92, 103, 103, 109, 58, 54, 54, 54, 54, 63, 63, 75, 75, + 87, 87, 98, 98, 110, 110, 116, 58, 54, 54, 54, 54, 63, 63, 75, 75, 87, 87, 98, 98, 110, 110, 116, 65, 60, 60, 58, 58, 68, 68, 79, 79, 92, + 92, 105, 105, 118, 118, 124, 65, 60, 60, 58, 58, 68, 68, 79, 79, 92, 92, 105, 105, 118, 118, 124, 71, 65, 65, 63, 63, 73, 73, 84, 84, 97, + 97, 111, 111, 125, 125, 132, 71, 65, 65, 63, 63, 73, 73, 84, 84, 97, 97, 111, 111, 125, 125, 132, 79, 72, 72, 70, 70, 79, 79, 90, 90, + 104, 104, 118, 118, 133, 133, 141, 79, 72, 72, 70, 70, 79, 79, 90, 90, 104, 104, 118, 118, 133, 133, 141, 82, 75, 75, 72, 72, 81, 81, 92, + 92, 106, 106, 121, 121, 136, 136, 144, 82, 75, 75, 72, 72, 81, 81, 92, 92, 106, 106, 121, 121, 136, 136, 144, 87, 79, 79, 76, 76, 84, 84, + 96, 96, 109, 109, 124, 124, 141, 141, 149, 32, 31, 31, 31, 31, 32, 32, 32, 32, 34, 34, 36, 36, 39, 39, 44, 44, 48, 48, 53, 53, 58, 58, + 65, 65, 71, 71, 79, 79, 82, 82, 87, 31, 32, 32, 32, 32, 32, 32, 33, 33, 34, 34, 34, 34, 37, 37, 41, 41, 45, 45, 49, 49, 54, 54, 60, 60, + 65, 65, 72, 72, 75, 75, 79, 31, 32, 32, 32, 32, 32, 32, 33, 33, 34, 34, 34, 34, 37, 37, 41, 41, 45, 45, 49, 49, 54, 54, 60, 60, 65, 65, + 72, 72, 75, 75, 79, 32, 32, 32, 33, 33, 34, 34, 35, 35, 37, 37, 38, 38, 40, 40, 43, 43, 46, 46, 50, 50, 54, 54, 58, 58, 63, 63, 70, 70, + 72, 72, 76, 32, 32, 32, 33, 33, 34, 34, 35, 35, 37, 37, 38, 38, 40, 40, 43, 43, 46, 46, 50, 50, 54, 54, 58, 58, 63, 63, 70, 70, 72, 72, + 76, 36, 35, 35, 34, 34, 36, 36, 38, 38, 42, 42, 48, 48, 50, 50, 53, 53, 56, 56, 60, 60, 63, 63, 68, 68, 73, 73, 79, 79, 81, 81, 84, 36, + 35, 35, 34, 34, 36, 36, 38, 38, 42, 42, 48, 48, 50, 50, 53, 53, 56, 56, 60, 60, 63, 63, 68, 68, 73, 73, 79, 79, 81, 81, 84, 44, 42, 42, + 41, 41, 42, 42, 42, 42, 48, 48, 54, 54, 58, 58, 63, 63, 67, 67, 71, 71, 75, 75, 79, 79, 84, 84, 90, 90, 92, 92, 96, 44, 42, 42, 41, 41, + 42, 42, 42, 42, 48, 48, 54, 54, 58, 58, 63, 63, 67, 67, 71, 71, 75, 75, 79, 79, 84, 84, 90, 90, 92, 92, 96, 53, 51, 51, 49, 49, 50, 50, + 49, 49, 54, 54, 60, 60, 65, 65, 71, 71, 76, 76, 82, 82, 87, 87, 92, 92, 97, 97, 104, 104, 106, 106, 109, 53, 51, 51, 49, 49, 50, 50, 49, + 49, 54, 54, 60, 60, 65, 65, 71, 71, 76, 76, 82, 82, 87, 87, 92, 92, 97, 97, 104, 104, 106, 106, 109, 65, 62, 62, 59, 59, 59, 59, 58, 58, + 63, 63, 68, 68, 73, 73, 79, 79, 85, 85, 92, 92, 98, 98, 105, 105, 111, 111, 118, 118, 121, 121, 124, 65, 62, 62, 59, 59, 59, 59, 58, 58, + 63, 63, 68, 68, 73, 73, 79, 79, 85, 85, 92, 92, 98, 98, 105, 105, 111, 111, 118, 118, 121, 121, 124, 79, 75, 75, 72, 72, 71, 71, 69, 69, + 73, 73, 78, 78, 84, 84, 90, 90, 96, 96, 103, 103, 110, 110, 118, 118, 125, 125, 133, 133, 136, 136, 141, 79, 75, 75, 72, 72, 71, 71, 69, + 69, 73, 73, 78, 78, 84, 84, 90, 90, 96, 96, 103, 103, 110, 110, 118, 118, 125, 125, 133, 133, 136, 136, 141, 87, 82, 82, 78, 78, 77, 77, + 75, 75, 79, 79, 84, 84, 89, 89, 95, 95, 102, 102, 109, 109, 116, 116, 124, 124, 132, 132, 141, 141, 144, 144, 149, 31, 36, 53, 79, 32, + 35, 51, 75, 32, 34, 49, 72, 32, 36, 50, 71, 33, 38, 49, 69, 34, 42, 54, 73, 34, 48, 60, 78, 37, 50, 65, 84, 41, 53, 71, 90, 45, 56, 76, + 96, 49, 60, 82, 103, 54, 63, 87, 110, 60, 68, 92, 118, 65, 73, 97, 125, 72, 79, 104, 133, 75, 81, 106, 136, 31, 32, 32, 32, 33, 34, 34, + 37, 41, 45, 49, 54, 60, 65, 72, 75, 36, 35, 34, 36, 38, 42, 48, 50, 53, 56, 60, 63, 68, 73, 79, 81, 53, 51, 49, 50, 49, 54, 60, 65, 71, + 76, 82, 87, 92, 97, 104, 106, 79, 75, 72, 71, 69, 73, 78, 84, 90, 96, 103, 110, 118, 125, 133, 136, 32, 31, 32, 36, 44, 53, 65, 79, 31, + 32, 32, 35, 42, 51, 62, 75, 31, 32, 32, 35, 42, 51, 62, 75, 31, 32, 33, 34, 41, 49, 59, 72, 31, 32, 33, 34, 41, 49, 59, 72, 32, 32, 34, + 36, 42, 50, 59, 71, 32, 32, 34, 36, 42, 50, 59, 71, 32, 33, 35, 38, 42, 49, 58, 69, 32, 33, 35, 38, 42, 49, 58, 69, 34, 34, 37, 42, 48, + 54, 63, 73, 34, 34, 37, 42, 48, 54, 63, 73, 36, 34, 38, 48, 54, 60, 68, 78, 36, 34, 38, 48, 54, 60, 68, 78, 39, 37, 40, 50, 58, 65, 73, + 84, 39, 37, 40, 50, 58, 65, 73, 84, 44, 41, 43, 53, 63, 71, 79, 90, 44, 41, 43, 53, 63, 71, 79, 90, 48, 45, 46, 56, 67, 76, 85, 96, 48, + 45, 46, 56, 67, 76, 85, 96, 53, 49, 50, 60, 71, 82, 92, 103, 53, 49, 50, 60, 71, 82, 92, 103, 58, 54, 54, 63, 75, 87, 98, 110, 58, 54, + 54, 63, 75, 87, 98, 110, 65, 60, 58, 68, 79, 92, 105, 118, 65, 60, 58, 68, 79, 92, 105, 118, 71, 65, 63, 73, 84, 97, 111, 125, 71, 65, + 63, 73, 84, 97, 111, 125, 79, 72, 70, 79, 90, 104, 118, 133, 79, 72, 70, 79, 90, 104, 118, 133, 82, 75, 72, 81, 92, 106, 121, 136, 82, + 75, 72, 81, 92, 106, 121, 136, 87, 79, 76, 84, 96, 109, 124, 141, 32, 31, 31, 31, 31, 32, 32, 32, 32, 34, 34, 36, 36, 39, 39, 44, 44, 48, + 48, 53, 53, 58, 58, 65, 65, 71, 71, 79, 79, 82, 82, 87, 31, 32, 32, 32, 32, 32, 32, 33, 33, 34, 34, 34, 34, 37, 37, 41, 41, 45, 45, 49, + 49, 54, 54, 60, 60, 65, 65, 72, 72, 75, 75, 79, 32, 32, 32, 33, 33, 34, 34, 35, 35, 37, 37, 38, 38, 40, 40, 43, 43, 46, 46, 50, 50, 54, + 54, 58, 58, 63, 63, 70, 70, 72, 72, 76, 36, 35, 35, 34, 34, 36, 36, 38, 38, 42, 42, 48, 48, 50, 50, 53, 53, 56, 56, 60, 60, 63, 63, 68, + 68, 73, 73, 79, 79, 81, 81, 84, 44, 42, 42, 41, 41, 42, 42, 42, 42, 48, 48, 54, 54, 58, 58, 63, 63, 67, 67, 71, 71, 75, 75, 79, 79, 84, + 84, 90, 90, 92, 92, 96, 53, 51, 51, 49, 49, 50, 50, 49, 49, 54, 54, 60, 60, 65, 65, 71, 71, 76, 76, 82, 82, 87, 87, 92, 92, 97, 97, 104, + 104, 106, 106, 109, 65, 62, 62, 59, 59, 59, 59, 58, 58, 63, 63, 68, 68, 73, 73, 79, 79, 85, 85, 92, 92, 98, 98, 105, 105, 111, 111, 118, + 118, 121, 121, 124, 79, 75, 75, 72, 72, 71, 71, 69, 69, 73, 73, 78, 78, 84, 84, 90, 90, 96, 96, 103, 103, 110, 110, 118, 118, 125, 125, + 133, 133, 136, 136, 141, + ], + [ + 32, 46, 47, 57, 46, 53, 54, 60, 47, 54, 66, 75, 57, 60, 75, 89, 31, 34, 42, 47, 48, 52, 57, 61, 34, 39, 45, 46, 46, 49, 53, 57, 42, 45, + 48, 49, 50, 52, 55, 58, 47, 46, 49, 54, 56, 58, 61, 64, 48, 46, 50, 56, 61, 65, 68, 71, 52, 49, 52, 58, 65, 71, 75, 79, 57, 53, 55, 61, + 68, 75, 82, 86, 61, 57, 58, 64, 71, 79, 86, 91, 32, 31, 30, 33, 36, 41, 49, 48, 49, 50, 52, 54, 57, 60, 63, 65, 31, 31, 31, 34, 38, 42, + 47, 47, 47, 48, 50, 52, 54, 57, 60, 61, 30, 31, 32, 35, 40, 42, 46, 45, 45, 46, 47, 49, 52, 54, 57, 58, 33, 34, 35, 39, 43, 45, 47, 46, + 45, 46, 47, 49, 51, 53, 56, 57, 36, 38, 40, 43, 47, 47, 48, 46, 45, 46, 47, 48, 50, 52, 54, 55, 41, 42, 42, 45, 47, 48, 50, 49, 49, 50, + 50, 52, 53, 55, 57, 58, 49, 47, 46, 47, 48, 50, 53, 53, 53, 54, 54, 55, 56, 58, 60, 61, 48, 47, 45, 46, 46, 49, 53, 54, 55, 56, 57, 58, + 60, 61, 63, 64, 49, 47, 45, 45, 45, 49, 53, 55, 58, 60, 61, 62, 63, 65, 67, 68, 50, 48, 46, 46, 46, 50, 54, 56, 60, 61, 63, 65, 67, 68, + 71, 71, 52, 50, 47, 47, 47, 50, 54, 57, 61, 63, 66, 68, 70, 72, 75, 75, 54, 52, 49, 49, 48, 52, 55, 58, 62, 65, 68, 71, 73, 75, 78, 79, + 57, 54, 52, 51, 50, 53, 56, 60, 63, 67, 70, 73, 76, 79, 82, 83, 60, 57, 54, 53, 52, 55, 58, 61, 65, 68, 72, 75, 79, 82, 85, 86, 63, 60, + 57, 56, 54, 57, 60, 63, 67, 71, 75, 78, 82, 85, 89, 90, 65, 61, 58, 57, 55, 58, 61, 64, 68, 71, 75, 79, 83, 86, 90, 91, 32, 31, 31, 30, + 30, 33, 33, 36, 36, 41, 41, 49, 49, 48, 48, 49, 49, 50, 50, 52, 52, 54, 54, 57, 57, 60, 60, 63, 63, 65, 65, 67, 31, 31, 31, 31, 31, 34, + 34, 38, 38, 42, 42, 47, 47, 47, 47, 47, 47, 48, 48, 50, 50, 52, 52, 54, 54, 57, 57, 60, 60, 61, 61, 63, 31, 31, 31, 31, 31, 34, 34, 38, + 38, 42, 42, 47, 47, 47, 47, 47, 47, 48, 48, 50, 50, 52, 52, 54, 54, 57, 57, 60, 60, 61, 61, 63, 30, 31, 31, 32, 32, 35, 35, 40, 40, 42, + 42, 46, 46, 45, 45, 45, 45, 46, 46, 47, 47, 49, 49, 52, 52, 54, 54, 57, 57, 58, 58, 60, 30, 31, 31, 32, 32, 35, 35, 40, 40, 42, 42, 46, + 46, 45, 45, 45, 45, 46, 46, 47, 47, 49, 49, 52, 52, 54, 54, 57, 57, 58, 58, 60, 33, 34, 34, 35, 35, 39, 39, 43, 43, 45, 45, 47, 47, 46, + 46, 45, 45, 46, 46, 47, 47, 49, 49, 51, 51, 53, 53, 56, 56, 57, 57, 59, 33, 34, 34, 35, 35, 39, 39, 43, 43, 45, 45, 47, 47, 46, 46, 45, + 45, 46, 46, 47, 47, 49, 49, 51, 51, 53, 53, 56, 56, 57, 57, 59, 36, 38, 38, 40, 40, 43, 43, 47, 47, 47, 47, 48, 48, 46, 46, 45, 45, 46, + 46, 47, 47, 48, 48, 50, 50, 52, 52, 54, 54, 55, 55, 57, 36, 38, 38, 40, 40, 43, 43, 47, 47, 47, 47, 48, 48, 46, 46, 45, 45, 46, 46, 47, + 47, 48, 48, 50, 50, 52, 52, 54, 54, 55, 55, 57, 41, 42, 42, 42, 42, 45, 45, 47, 47, 48, 48, 50, 50, 49, 49, 49, 49, 50, 50, 50, 50, 52, + 52, 53, 53, 55, 55, 57, 57, 58, 58, 60, 41, 42, 42, 42, 42, 45, 45, 47, 47, 48, 48, 50, 50, 49, 49, 49, 49, 50, 50, 50, 50, 52, 52, 53, + 53, 55, 55, 57, 57, 58, 58, 60, 49, 47, 47, 46, 46, 47, 47, 48, 48, 50, 50, 53, 53, 53, 53, 53, 53, 54, 54, 54, 54, 55, 55, 56, 56, 58, + 58, 60, 60, 61, 61, 62, 49, 47, 47, 46, 46, 47, 47, 48, 48, 50, 50, 53, 53, 53, 53, 53, 53, 54, 54, 54, 54, 55, 55, 56, 56, 58, 58, 60, + 60, 61, 61, 62, 48, 47, 47, 45, 45, 46, 46, 46, 46, 49, 49, 53, 53, 54, 54, 55, 55, 56, 56, 57, 57, 58, 58, 60, 60, 61, 61, 63, 63, 64, + 64, 66, 48, 47, 47, 45, 45, 46, 46, 46, 46, 49, 49, 53, 53, 54, 54, 55, 55, 56, 56, 57, 57, 58, 58, 60, 60, 61, 61, 63, 63, 64, 64, 66, + 49, 47, 47, 45, 45, 45, 45, 45, 45, 49, 49, 53, 53, 55, 55, 58, 58, 60, 60, 61, 61, 62, 62, 63, 63, 65, 65, 67, 67, 68, 68, 69, 49, 47, + 47, 45, 45, 45, 45, 45, 45, 49, 49, 53, 53, 55, 55, 58, 58, 60, 60, 61, 61, 62, 62, 63, 63, 65, 65, 67, 67, 68, 68, 69, 50, 48, 48, 46, + 46, 46, 46, 46, 46, 50, 50, 54, 54, 56, 56, 60, 60, 61, 61, 63, 63, 65, 65, 67, 67, 68, 68, 71, 71, 71, 71, 72, 50, 48, 48, 46, 46, 46, + 46, 46, 46, 50, 50, 54, 54, 56, 56, 60, 60, 61, 61, 63, 63, 65, 65, 67, 67, 68, 68, 71, 71, 71, 71, 72, 52, 50, 50, 47, 47, 47, 47, 47, + 47, 50, 50, 54, 54, 57, 57, 61, 61, 63, 63, 66, 66, 68, 68, 70, 70, 72, 72, 75, 75, 75, 75, 76, 52, 50, 50, 47, 47, 47, 47, 47, 47, 50, + 50, 54, 54, 57, 57, 61, 61, 63, 63, 66, 66, 68, 68, 70, 70, 72, 72, 75, 75, 75, 75, 76, 54, 52, 52, 49, 49, 49, 49, 48, 48, 52, 52, 55, + 55, 58, 58, 62, 62, 65, 65, 68, 68, 71, 71, 73, 73, 75, 75, 78, 78, 79, 79, 80, 54, 52, 52, 49, 49, 49, 49, 48, 48, 52, 52, 55, 55, 58, + 58, 62, 62, 65, 65, 68, 68, 71, 71, 73, 73, 75, 75, 78, 78, 79, 79, 80, 57, 54, 54, 52, 52, 51, 51, 50, 50, 53, 53, 56, 56, 60, 60, 63, + 63, 67, 67, 70, 70, 73, 73, 76, 76, 79, 79, 82, 82, 83, 83, 84, 57, 54, 54, 52, 52, 51, 51, 50, 50, 53, 53, 56, 56, 60, 60, 63, 63, 67, + 67, 70, 70, 73, 73, 76, 76, 79, 79, 82, 82, 83, 83, 84, 60, 57, 57, 54, 54, 53, 53, 52, 52, 55, 55, 58, 58, 61, 61, 65, 65, 68, 68, 72, + 72, 75, 75, 79, 79, 82, 82, 85, 85, 86, 86, 88, 60, 57, 57, 54, 54, 53, 53, 52, 52, 55, 55, 58, 58, 61, 61, 65, 65, 68, 68, 72, 72, 75, + 75, 79, 79, 82, 82, 85, 85, 86, 86, 88, 63, 60, 60, 57, 57, 56, 56, 54, 54, 57, 57, 60, 60, 63, 63, 67, 67, 71, 71, 75, 75, 78, 78, 82, + 82, 85, 85, 89, 89, 90, 90, 92, 63, 60, 60, 57, 57, 56, 56, 54, 54, 57, 57, 60, 60, 63, 63, 67, 67, 71, 71, 75, 75, 78, 78, 82, 82, 85, + 85, 89, 89, 90, 90, 92, 65, 61, 61, 58, 58, 57, 57, 55, 55, 58, 58, 61, 61, 64, 64, 68, 68, 71, 71, 75, 75, 79, 79, 83, 83, 86, 86, 90, + 90, 91, 91, 93, 65, 61, 61, 58, 58, 57, 57, 55, 55, 58, 58, 61, 61, 64, 64, 68, 68, 71, 71, 75, 75, 79, 79, 83, 83, 86, 86, 90, 90, 91, + 91, 93, 67, 63, 63, 60, 60, 59, 59, 57, 57, 60, 60, 62, 62, 66, 66, 69, 69, 72, 72, 76, 76, 80, 80, 84, 84, 88, 88, 92, 92, 93, 93, 95, + 31, 47, 50, 60, 36, 47, 47, 56, 43, 50, 50, 57, 46, 53, 57, 64, 46, 54, 64, 71, 50, 55, 68, 78, 54, 58, 72, 85, 59, 61, 75, 90, 31, 36, + 43, 46, 46, 50, 54, 59, 47, 47, 50, 53, 54, 55, 58, 61, 50, 47, 50, 57, 64, 68, 72, 75, 60, 56, 57, 64, 71, 78, 85, 90, 32, 31, 37, 48, + 49, 52, 57, 63, 31, 31, 38, 47, 47, 50, 54, 60, 30, 32, 40, 46, 45, 48, 52, 57, 33, 36, 43, 47, 46, 47, 51, 56, 37, 40, 47, 47, 45, 47, + 50, 54, 42, 43, 47, 50, 49, 50, 53, 57, 49, 46, 48, 53, 53, 54, 57, 60, 48, 46, 47, 53, 56, 57, 60, 64, 49, 45, 46, 53, 58, 61, 64, 67, + 50, 46, 46, 54, 59, 64, 67, 71, 52, 48, 47, 54, 61, 66, 71, 75, 54, 50, 49, 55, 62, 68, 73, 78, 57, 52, 50, 56, 64, 70, 76, 82, 60, 54, + 52, 58, 65, 72, 79, 85, 63, 57, 55, 60, 67, 75, 82, 89, 64, 59, 56, 61, 68, 75, 83, 90, 32, 31, 30, 33, 37, 42, 49, 48, 49, 50, 52, 54, + 57, 60, 63, 64, 31, 31, 32, 36, 40, 43, 46, 46, 45, 46, 48, 50, 52, 54, 57, 59, 37, 38, 40, 43, 47, 47, 48, 47, 46, 46, 47, 49, 50, 52, + 55, 56, 48, 47, 46, 47, 47, 50, 53, 53, 53, 54, 54, 55, 56, 58, 60, 61, 49, 47, 45, 46, 45, 49, 53, 56, 58, 59, 61, 62, 64, 65, 67, 68, + 52, 50, 48, 47, 47, 50, 54, 57, 61, 64, 66, 68, 70, 72, 75, 75, 57, 54, 52, 51, 50, 53, 57, 60, 64, 67, 71, 73, 76, 79, 82, 83, 63, 60, + 57, 56, 54, 57, 60, 64, 67, 71, 75, 78, 82, 85, 89, 90, 32, 31, 31, 37, 37, 48, 48, 49, 49, 52, 52, 57, 57, 63, 63, 66, 31, 31, 31, 38, + 38, 47, 47, 47, 47, 50, 50, 54, 54, 60, 60, 63, 31, 31, 31, 38, 38, 47, 47, 47, 47, 50, 50, 54, 54, 60, 60, 63, 30, 32, 32, 40, 40, 46, + 46, 45, 45, 48, 48, 52, 52, 57, 57, 60, 30, 32, 32, 40, 40, 46, 46, 45, 45, 48, 48, 52, 52, 57, 57, 60, 33, 36, 36, 43, 43, 47, 47, 46, + 46, 47, 47, 51, 51, 56, 56, 59, 33, 36, 36, 43, 43, 47, 47, 46, 46, 47, 47, 51, 51, 56, 56, 59, 37, 40, 40, 47, 47, 47, 47, 45, 45, 47, + 47, 50, 50, 54, 54, 57, 37, 40, 40, 47, 47, 47, 47, 45, 45, 47, 47, 50, 50, 54, 54, 57, 42, 43, 43, 47, 47, 50, 50, 49, 49, 50, 50, 53, + 53, 57, 57, 60, 42, 43, 43, 47, 47, 50, 50, 49, 49, 50, 50, 53, 53, 57, 57, 60, 49, 46, 46, 48, 48, 53, 53, 53, 53, 54, 54, 57, 57, 60, + 60, 62, 49, 46, 46, 48, 48, 53, 53, 53, 53, 54, 54, 57, 57, 60, 60, 62, 48, 46, 46, 47, 47, 53, 53, 56, 56, 57, 57, 60, 60, 64, 64, 66, + 48, 46, 46, 47, 47, 53, 53, 56, 56, 57, 57, 60, 60, 64, 64, 66, 49, 45, 45, 46, 46, 53, 53, 58, 58, 61, 61, 64, 64, 67, 67, 69, 49, 45, + 45, 46, 46, 53, 53, 58, 58, 61, 61, 64, 64, 67, 67, 69, 50, 46, 46, 46, 46, 54, 54, 59, 59, 64, 64, 67, 67, 71, 71, 73, 50, 46, 46, 46, + 46, 54, 54, 59, 59, 64, 64, 67, 67, 71, 71, 73, 52, 48, 48, 47, 47, 54, 54, 61, 61, 66, 66, 71, 71, 75, 75, 77, 52, 48, 48, 47, 47, 54, + 54, 61, 61, 66, 66, 71, 71, 75, 75, 77, 54, 50, 50, 49, 49, 55, 55, 62, 62, 68, 68, 73, 73, 78, 78, 80, 54, 50, 50, 49, 49, 55, 55, 62, + 62, 68, 68, 73, 73, 78, 78, 80, 57, 52, 52, 50, 50, 56, 56, 64, 64, 70, 70, 76, 76, 82, 82, 84, 57, 52, 52, 50, 50, 56, 56, 64, 64, 70, + 70, 76, 76, 82, 82, 84, 60, 54, 54, 52, 52, 58, 58, 65, 65, 72, 72, 79, 79, 85, 85, 88, 60, 54, 54, 52, 52, 58, 58, 65, 65, 72, 72, 79, + 79, 85, 85, 88, 63, 57, 57, 55, 55, 60, 60, 67, 67, 75, 75, 82, 82, 89, 89, 92, 63, 57, 57, 55, 55, 60, 60, 67, 67, 75, 75, 82, 82, 89, + 89, 92, 64, 59, 59, 56, 56, 61, 61, 68, 68, 75, 75, 83, 83, 90, 90, 93, 64, 59, 59, 56, 56, 61, 61, 68, 68, 75, 75, 83, 83, 90, 90, 93, + 66, 60, 60, 57, 57, 63, 63, 69, 69, 77, 77, 84, 84, 92, 92, 95, 32, 31, 31, 30, 30, 33, 33, 37, 37, 42, 42, 49, 49, 48, 48, 49, 49, 50, + 50, 52, 52, 54, 54, 57, 57, 60, 60, 63, 63, 64, 64, 66, 31, 31, 31, 32, 32, 36, 36, 40, 40, 43, 43, 46, 46, 46, 46, 45, 45, 46, 46, 48, + 48, 50, 50, 52, 52, 54, 54, 57, 57, 59, 59, 60, 31, 31, 31, 32, 32, 36, 36, 40, 40, 43, 43, 46, 46, 46, 46, 45, 45, 46, 46, 48, 48, 50, + 50, 52, 52, 54, 54, 57, 57, 59, 59, 60, 37, 38, 38, 40, 40, 43, 43, 47, 47, 47, 47, 48, 48, 47, 47, 46, 46, 46, 46, 47, 47, 49, 49, 50, + 50, 52, 52, 55, 55, 56, 56, 57, 37, 38, 38, 40, 40, 43, 43, 47, 47, 47, 47, 48, 48, 47, 47, 46, 46, 46, 46, 47, 47, 49, 49, 50, 50, 52, + 52, 55, 55, 56, 56, 57, 48, 47, 47, 46, 46, 47, 47, 47, 47, 50, 50, 53, 53, 53, 53, 53, 53, 54, 54, 54, 54, 55, 55, 56, 56, 58, 58, 60, + 60, 61, 61, 63, 48, 47, 47, 46, 46, 47, 47, 47, 47, 50, 50, 53, 53, 53, 53, 53, 53, 54, 54, 54, 54, 55, 55, 56, 56, 58, 58, 60, 60, 61, + 61, 63, 49, 47, 47, 45, 45, 46, 46, 45, 45, 49, 49, 53, 53, 56, 56, 58, 58, 59, 59, 61, 61, 62, 62, 64, 64, 65, 65, 67, 67, 68, 68, 69, + 49, 47, 47, 45, 45, 46, 46, 45, 45, 49, 49, 53, 53, 56, 56, 58, 58, 59, 59, 61, 61, 62, 62, 64, 64, 65, 65, 67, 67, 68, 68, 69, 52, 50, + 50, 48, 48, 47, 47, 47, 47, 50, 50, 54, 54, 57, 57, 61, 61, 64, 64, 66, 66, 68, 68, 70, 70, 72, 72, 75, 75, 75, 75, 77, 52, 50, 50, 48, + 48, 47, 47, 47, 47, 50, 50, 54, 54, 57, 57, 61, 61, 64, 64, 66, 66, 68, 68, 70, 70, 72, 72, 75, 75, 75, 75, 77, 57, 54, 54, 52, 52, 51, + 51, 50, 50, 53, 53, 57, 57, 60, 60, 64, 64, 67, 67, 71, 71, 73, 73, 76, 76, 79, 79, 82, 82, 83, 83, 84, 57, 54, 54, 52, 52, 51, 51, 50, + 50, 53, 53, 57, 57, 60, 60, 64, 64, 67, 67, 71, 71, 73, 73, 76, 76, 79, 79, 82, 82, 83, 83, 84, 63, 60, 60, 57, 57, 56, 56, 54, 54, 57, + 57, 60, 60, 64, 64, 67, 67, 71, 71, 75, 75, 78, 78, 82, 82, 85, 85, 89, 89, 90, 90, 92, 63, 60, 60, 57, 57, 56, 56, 54, 54, 57, 57, 60, + 60, 64, 64, 67, 67, 71, 71, 75, 75, 78, 78, 82, 82, 85, 85, 89, 89, 90, 90, 92, 66, 63, 63, 60, 60, 59, 59, 57, 57, 60, 60, 62, 62, 66, + 66, 69, 69, 73, 73, 77, 77, 80, 80, 84, 84, 88, 88, 92, 92, 93, 93, 95, 31, 48, 52, 63, 31, 47, 50, 60, 32, 46, 48, 57, 36, 47, 47, 56, + 40, 47, 47, 54, 43, 50, 50, 57, 46, 53, 54, 60, 46, 53, 57, 64, 45, 53, 61, 67, 46, 54, 64, 71, 48, 54, 66, 75, 50, 55, 68, 78, 52, 56, + 70, 82, 54, 58, 72, 85, 57, 60, 75, 89, 59, 61, 75, 90, 31, 31, 32, 36, 40, 43, 46, 46, 45, 46, 48, 50, 52, 54, 57, 59, 48, 47, 46, 47, + 47, 50, 53, 53, 53, 54, 54, 55, 56, 58, 60, 61, 52, 50, 48, 47, 47, 50, 54, 57, 61, 64, 66, 68, 70, 72, 75, 75, 63, 60, 57, 56, 54, 57, + 60, 64, 67, 71, 75, 78, 82, 85, 89, 90, 32, 31, 37, 48, 49, 52, 57, 63, 31, 31, 38, 47, 47, 50, 54, 60, 31, 31, 38, 47, 47, 50, 54, 60, + 30, 32, 40, 46, 45, 48, 52, 57, 30, 32, 40, 46, 45, 48, 52, 57, 33, 36, 43, 47, 46, 47, 51, 56, 33, 36, 43, 47, 46, 47, 51, 56, 37, 40, + 47, 47, 45, 47, 50, 54, 37, 40, 47, 47, 45, 47, 50, 54, 42, 43, 47, 50, 49, 50, 53, 57, 42, 43, 47, 50, 49, 50, 53, 57, 49, 46, 48, 53, + 53, 54, 57, 60, 49, 46, 48, 53, 53, 54, 57, 60, 48, 46, 47, 53, 56, 57, 60, 64, 48, 46, 47, 53, 56, 57, 60, 64, 49, 45, 46, 53, 58, 61, + 64, 67, 49, 45, 46, 53, 58, 61, 64, 67, 50, 46, 46, 54, 59, 64, 67, 71, 50, 46, 46, 54, 59, 64, 67, 71, 52, 48, 47, 54, 61, 66, 71, 75, + 52, 48, 47, 54, 61, 66, 71, 75, 54, 50, 49, 55, 62, 68, 73, 78, 54, 50, 49, 55, 62, 68, 73, 78, 57, 52, 50, 56, 64, 70, 76, 82, 57, 52, + 50, 56, 64, 70, 76, 82, 60, 54, 52, 58, 65, 72, 79, 85, 60, 54, 52, 58, 65, 72, 79, 85, 63, 57, 55, 60, 67, 75, 82, 89, 63, 57, 55, 60, + 67, 75, 82, 89, 64, 59, 56, 61, 68, 75, 83, 90, 64, 59, 56, 61, 68, 75, 83, 90, 66, 60, 57, 63, 69, 77, 84, 92, 32, 31, 31, 30, 30, 33, + 33, 37, 37, 42, 42, 49, 49, 48, 48, 49, 49, 50, 50, 52, 52, 54, 54, 57, 57, 60, 60, 63, 63, 64, 64, 66, 31, 31, 31, 32, 32, 36, 36, 40, + 40, 43, 43, 46, 46, 46, 46, 45, 45, 46, 46, 48, 48, 50, 50, 52, 52, 54, 54, 57, 57, 59, 59, 60, 37, 38, 38, 40, 40, 43, 43, 47, 47, 47, + 47, 48, 48, 47, 47, 46, 46, 46, 46, 47, 47, 49, 49, 50, 50, 52, 52, 55, 55, 56, 56, 57, 48, 47, 47, 46, 46, 47, 47, 47, 47, 50, 50, 53, + 53, 53, 53, 53, 53, 54, 54, 54, 54, 55, 55, 56, 56, 58, 58, 60, 60, 61, 61, 63, 49, 47, 47, 45, 45, 46, 46, 45, 45, 49, 49, 53, 53, 56, + 56, 58, 58, 59, 59, 61, 61, 62, 62, 64, 64, 65, 65, 67, 67, 68, 68, 69, 52, 50, 50, 48, 48, 47, 47, 47, 47, 50, 50, 54, 54, 57, 57, 61, + 61, 64, 64, 66, 66, 68, 68, 70, 70, 72, 72, 75, 75, 75, 75, 77, 57, 54, 54, 52, 52, 51, 51, 50, 50, 53, 53, 57, 57, 60, 60, 64, 64, 67, + 67, 71, 71, 73, 73, 76, 76, 79, 79, 82, 82, 83, 83, 84, 63, 60, 60, 57, 57, 56, 56, 54, 54, 57, 57, 60, 60, 64, 64, 67, 67, 71, 71, 75, + 75, 78, 78, 82, 82, 85, 85, 89, 89, 90, 90, 92, + ], + ], + [ + [ + 32, 33, 45, 62, 33, 39, 51, 64, 45, 51, 71, 87, 62, 64, 87, 108, 31, 32, 32, 35, 42, 51, 59, 69, 32, 32, 33, 35, 41, 49, 56, 65, 32, 33, + 35, 38, 43, 49, 56, 64, 35, 35, 38, 48, 54, 59, 66, 73, 42, 41, 43, 54, 63, 71, 77, 85, 51, 49, 49, 59, 71, 81, 89, 97, 59, 56, 56, 66, + 77, 89, 98, 108, 69, 65, 64, 73, 85, 97, 108, 119, 32, 31, 31, 31, 32, 34, 35, 38, 41, 45, 48, 54, 59, 65, 71, 80, 31, 32, 32, 32, 32, + 34, 35, 37, 40, 43, 46, 51, 56, 62, 68, 76, 31, 32, 32, 32, 32, 33, 34, 36, 38, 41, 44, 49, 54, 59, 65, 72, 31, 32, 32, 33, 34, 35, 36, + 38, 40, 42, 45, 50, 54, 59, 64, 71, 32, 32, 32, 34, 35, 37, 38, 39, 41, 43, 46, 49, 53, 58, 63, 69, 34, 34, 33, 35, 37, 39, 42, 44, 46, + 48, 51, 54, 58, 63, 68, 74, 35, 35, 34, 36, 38, 42, 46, 48, 50, 53, 55, 59, 62, 67, 72, 78, 38, 37, 36, 38, 39, 44, 48, 51, 54, 57, 59, + 63, 67, 71, 76, 82, 41, 40, 38, 40, 41, 46, 50, 54, 57, 60, 63, 67, 71, 75, 80, 86, 45, 43, 41, 42, 43, 48, 53, 57, 60, 65, 68, 72, 76, + 81, 85, 91, 48, 46, 44, 45, 46, 51, 55, 59, 63, 68, 71, 76, 80, 85, 90, 96, 54, 51, 49, 50, 49, 54, 59, 63, 67, 72, 76, 82, 87, 92, 97, + 104, 59, 56, 54, 54, 53, 58, 62, 67, 71, 76, 80, 87, 92, 98, 103, 110, 65, 62, 59, 59, 58, 63, 67, 71, 75, 81, 85, 92, 98, 105, 111, 118, + 71, 68, 65, 64, 63, 68, 72, 76, 80, 85, 90, 97, 103, 111, 117, 125, 80, 76, 72, 71, 69, 74, 78, 82, 86, 91, 96, 104, 110, 118, 125, 134, + 32, 31, 31, 31, 31, 31, 31, 32, 32, 32, 34, 34, 35, 36, 38, 39, 41, 44, 45, 48, 48, 53, 54, 57, 59, 62, 65, 67, 71, 72, 80, 80, 31, 31, + 32, 32, 32, 32, 32, 32, 32, 32, 34, 34, 35, 35, 37, 38, 40, 42, 43, 46, 46, 51, 52, 55, 56, 59, 62, 64, 68, 69, 76, 76, 31, 32, 32, 32, + 32, 32, 32, 32, 32, 32, 34, 34, 35, 35, 37, 38, 40, 42, 43, 46, 46, 51, 51, 55, 56, 59, 62, 64, 68, 69, 76, 76, 31, 32, 32, 32, 32, 32, + 32, 32, 32, 32, 33, 33, 34, 34, 36, 38, 39, 41, 42, 45, 45, 49, 50, 53, 54, 57, 60, 62, 66, 66, 73, 73, 31, 32, 32, 32, 32, 32, 32, 32, + 32, 33, 33, 33, 34, 34, 36, 37, 38, 41, 41, 44, 44, 49, 49, 52, 54, 56, 59, 61, 65, 65, 72, 72, 31, 32, 32, 32, 32, 32, 33, 33, 33, 33, + 34, 34, 35, 35, 37, 38, 39, 41, 42, 45, 45, 49, 49, 52, 54, 56, 59, 61, 64, 65, 72, 72, 31, 32, 32, 32, 32, 33, 33, 33, 34, 34, 35, 35, + 36, 36, 38, 39, 40, 42, 42, 45, 45, 49, 50, 52, 54, 56, 59, 60, 64, 65, 71, 71, 32, 32, 32, 32, 32, 33, 33, 34, 34, 34, 35, 35, 36, 37, + 38, 39, 40, 42, 43, 45, 45, 49, 49, 52, 54, 56, 59, 60, 64, 64, 70, 70, 32, 32, 32, 32, 32, 33, 34, 34, 35, 35, 37, 37, 38, 38, 39, 40, + 41, 42, 43, 46, 46, 49, 49, 52, 53, 55, 58, 59, 63, 63, 69, 69, 32, 32, 32, 32, 33, 33, 34, 34, 35, 35, 37, 37, 38, 38, 40, 41, 41, 43, + 43, 46, 46, 49, 50, 52, 54, 56, 58, 60, 63, 64, 70, 70, 34, 34, 34, 33, 33, 34, 35, 35, 37, 37, 39, 39, 42, 42, 44, 45, 46, 47, 48, 51, + 51, 54, 54, 57, 58, 60, 63, 64, 68, 68, 74, 74, 34, 34, 34, 33, 33, 34, 35, 35, 37, 37, 39, 39, 42, 42, 44, 45, 46, 47, 48, 51, 51, 54, + 54, 57, 58, 60, 63, 64, 68, 68, 74, 74, 35, 35, 35, 34, 34, 35, 36, 36, 38, 38, 42, 42, 46, 47, 48, 49, 50, 52, 53, 55, 55, 58, 59, 61, + 62, 64, 67, 68, 72, 72, 78, 78, 36, 35, 35, 34, 34, 35, 36, 37, 38, 38, 42, 42, 47, 48, 50, 50, 52, 54, 54, 57, 57, 59, 60, 62, 64, 66, + 68, 69, 73, 73, 79, 79, 38, 37, 37, 36, 36, 37, 38, 38, 39, 40, 44, 44, 48, 50, 51, 52, 54, 56, 57, 59, 59, 62, 63, 65, 67, 69, 71, 72, + 76, 76, 82, 82, 39, 38, 38, 38, 37, 38, 39, 39, 40, 41, 45, 45, 49, 50, 52, 54, 55, 58, 58, 61, 61, 64, 65, 67, 69, 71, 73, 74, 78, 78, + 84, 84, 41, 40, 40, 39, 38, 39, 40, 40, 41, 41, 46, 46, 50, 52, 54, 55, 57, 60, 60, 63, 63, 67, 67, 70, 71, 73, 75, 77, 80, 81, 86, 86, + 44, 42, 42, 41, 41, 41, 42, 42, 42, 43, 47, 47, 52, 54, 56, 58, 60, 63, 64, 67, 67, 71, 71, 74, 75, 77, 79, 81, 84, 85, 90, 90, 45, 43, + 43, 42, 41, 42, 42, 43, 43, 43, 48, 48, 53, 54, 57, 58, 60, 64, 65, 68, 68, 72, 72, 75, 76, 78, 81, 82, 85, 86, 91, 91, 48, 46, 46, 45, + 44, 45, 45, 45, 46, 46, 51, 51, 55, 57, 59, 61, 63, 67, 68, 71, 71, 75, 76, 79, 80, 83, 85, 87, 90, 91, 96, 96, 48, 46, 46, 45, 44, 45, + 45, 45, 46, 46, 51, 51, 55, 57, 59, 61, 63, 67, 68, 71, 71, 75, 76, 79, 80, 83, 85, 87, 90, 91, 96, 96, 53, 51, 51, 49, 49, 49, 49, 49, + 49, 49, 54, 54, 58, 59, 62, 64, 67, 71, 72, 75, 75, 81, 81, 85, 86, 89, 91, 93, 97, 97, 103, 103, 54, 52, 51, 50, 49, 49, 50, 49, 49, 50, + 54, 54, 59, 60, 63, 65, 67, 71, 72, 76, 76, 81, 82, 85, 87, 89, 92, 94, 97, 98, 104, 104, 57, 55, 55, 53, 52, 52, 52, 52, 52, 52, 57, 57, + 61, 62, 65, 67, 70, 74, 75, 79, 79, 85, 85, 89, 90, 93, 96, 98, 102, 102, 108, 108, 59, 56, 56, 54, 54, 54, 54, 54, 53, 54, 58, 58, 62, + 64, 67, 69, 71, 75, 76, 80, 80, 86, 87, 90, 92, 95, 98, 99, 103, 104, 110, 110, 62, 59, 59, 57, 56, 56, 56, 56, 55, 56, 60, 60, 64, 66, + 69, 71, 73, 77, 78, 83, 83, 89, 89, 93, 95, 98, 101, 103, 107, 108, 114, 114, 65, 62, 62, 60, 59, 59, 59, 59, 58, 58, 63, 63, 67, 68, 71, + 73, 75, 79, 81, 85, 85, 91, 92, 96, 98, 101, 105, 106, 111, 111, 118, 118, 67, 64, 64, 62, 61, 61, 60, 60, 59, 60, 64, 64, 68, 69, 72, + 74, 77, 81, 82, 87, 87, 93, 94, 98, 99, 103, 106, 108, 113, 113, 120, 120, 71, 68, 68, 66, 65, 64, 64, 64, 63, 63, 68, 68, 72, 73, 76, + 78, 80, 84, 85, 90, 90, 97, 97, 102, 103, 107, 111, 113, 117, 118, 125, 125, 72, 69, 69, 66, 65, 65, 65, 64, 63, 64, 68, 68, 72, 73, 76, + 78, 81, 85, 86, 91, 91, 97, 98, 102, 104, 108, 111, 113, 118, 119, 126, 126, 80, 76, 76, 73, 72, 72, 71, 70, 69, 70, 74, 74, 78, 79, 82, + 84, 86, 90, 91, 96, 96, 103, 104, 108, 110, 114, 118, 120, 125, 126, 134, 134, 80, 76, 76, 73, 72, 72, 71, 70, 69, 70, 74, 74, 78, 79, + 82, 84, 86, 90, 91, 96, 96, 103, 104, 108, 110, 114, 118, 120, 125, 126, 134, 134, 32, 34, 43, 62, 32, 34, 42, 59, 33, 37, 44, 58, 35, + 43, 54, 68, 41, 48, 64, 79, 49, 54, 71, 91, 57, 60, 78, 101, 66, 68, 86, 111, 32, 32, 33, 35, 41, 49, 57, 66, 34, 34, 37, 43, 48, 54, 60, + 68, 43, 42, 44, 54, 64, 71, 78, 86, 62, 59, 58, 68, 79, 91, 101, 111, 32, 31, 32, 36, 44, 53, 62, 73, 31, 32, 32, 35, 42, 51, 59, 69, 31, + 32, 33, 34, 41, 49, 57, 66, 32, 32, 34, 36, 42, 50, 57, 65, 32, 33, 35, 38, 42, 49, 56, 64, 34, 34, 37, 42, 48, 54, 61, 69, 35, 34, 38, + 47, 52, 59, 65, 73, 38, 36, 40, 49, 56, 63, 69, 77, 41, 39, 41, 51, 60, 67, 74, 81, 44, 42, 43, 54, 64, 72, 79, 86, 48, 45, 46, 56, 67, + 76, 83, 91, 53, 49, 50, 60, 71, 82, 90, 99, 58, 54, 54, 63, 75, 87, 95, 105, 65, 60, 58, 68, 79, 92, 102, 112, 71, 65, 63, 73, 84, 97, + 108, 119, 79, 72, 70, 79, 90, 104, 115, 127, 32, 31, 31, 32, 32, 34, 35, 38, 41, 44, 48, 53, 58, 65, 71, 79, 31, 32, 32, 32, 33, 34, 34, + 36, 39, 42, 45, 49, 54, 60, 65, 72, 32, 32, 33, 34, 35, 37, 38, 40, 41, 43, 46, 50, 54, 58, 63, 70, 36, 35, 34, 36, 38, 42, 47, 49, 51, + 54, 56, 60, 63, 68, 73, 79, 44, 42, 41, 42, 42, 48, 52, 56, 60, 64, 67, 71, 75, 79, 84, 90, 53, 51, 49, 50, 49, 54, 59, 63, 67, 72, 76, + 82, 87, 92, 97, 104, 62, 59, 57, 57, 56, 61, 65, 69, 74, 79, 83, 90, 95, 102, 108, 115, 73, 69, 66, 65, 64, 69, 73, 77, 81, 86, 91, 99, + 105, 112, 119, 127, 32, 31, 31, 32, 32, 34, 36, 38, 44, 44, 53, 53, 62, 65, 73, 79, 31, 32, 32, 32, 32, 34, 35, 37, 42, 43, 51, 51, 60, + 62, 70, 75, 31, 32, 32, 32, 32, 34, 35, 37, 42, 43, 51, 51, 59, 62, 69, 75, 31, 32, 32, 32, 32, 33, 35, 36, 41, 42, 50, 50, 58, 60, 67, + 73, 31, 32, 32, 32, 33, 33, 34, 36, 41, 41, 49, 49, 57, 59, 66, 72, 31, 32, 32, 33, 33, 34, 35, 37, 41, 42, 49, 49, 57, 59, 66, 71, 32, + 32, 32, 33, 34, 35, 36, 38, 42, 43, 50, 50, 57, 59, 65, 71, 32, 32, 32, 34, 34, 35, 37, 38, 42, 43, 49, 49, 56, 59, 65, 70, 32, 32, 33, + 34, 35, 37, 38, 39, 42, 43, 49, 49, 56, 58, 64, 69, 32, 33, 33, 34, 35, 37, 39, 40, 43, 44, 50, 50, 56, 58, 64, 69, 34, 34, 34, 36, 37, + 39, 42, 44, 48, 48, 54, 54, 61, 63, 69, 73, 34, 34, 34, 36, 37, 39, 42, 44, 48, 48, 54, 54, 61, 63, 69, 73, 35, 34, 34, 37, 38, 42, 47, + 48, 52, 53, 59, 59, 65, 67, 73, 77, 36, 35, 34, 37, 38, 43, 48, 49, 54, 54, 60, 60, 66, 68, 74, 78, 38, 36, 36, 38, 40, 44, 49, 51, 56, + 57, 63, 63, 69, 71, 77, 81, 39, 38, 37, 40, 40, 45, 50, 52, 58, 58, 65, 65, 71, 73, 79, 84, 41, 39, 39, 41, 41, 46, 51, 54, 60, 60, 67, + 67, 74, 76, 81, 86, 44, 41, 41, 42, 43, 48, 53, 56, 63, 64, 71, 71, 78, 79, 85, 90, 44, 42, 42, 43, 43, 48, 54, 56, 64, 64, 72, 72, 79, + 81, 86, 91, 48, 45, 45, 46, 46, 51, 56, 59, 67, 67, 76, 76, 83, 85, 91, 96, 48, 45, 45, 46, 46, 51, 56, 59, 67, 67, 76, 76, 83, 85, 91, + 96, 53, 49, 49, 49, 49, 54, 59, 62, 71, 71, 81, 81, 89, 91, 98, 103, 53, 50, 49, 50, 50, 54, 60, 63, 71, 72, 82, 82, 90, 92, 99, 103, 57, + 53, 52, 52, 52, 57, 62, 65, 74, 75, 85, 85, 94, 96, 103, 108, 58, 54, 54, 54, 54, 58, 63, 67, 75, 76, 87, 87, 95, 98, 105, 110, 61, 57, + 57, 56, 56, 60, 66, 69, 77, 78, 89, 89, 98, 101, 108, 114, 65, 60, 60, 59, 58, 63, 68, 71, 79, 80, 92, 92, 102, 105, 112, 118, 67, 62, + 61, 60, 60, 64, 69, 72, 81, 82, 94, 94, 103, 106, 114, 120, 71, 66, 65, 64, 63, 68, 73, 76, 84, 85, 97, 97, 108, 111, 119, 125, 72, 66, + 66, 64, 64, 68, 73, 76, 85, 86, 98, 98, 108, 111, 119, 125, 79, 73, 72, 71, 70, 74, 79, 82, 90, 91, 104, 104, 115, 118, 127, 133, 79, 73, + 72, 71, 70, 74, 79, 82, 90, 91, 104, 104, 115, 118, 127, 133, 32, 31, 31, 31, 31, 31, 32, 32, 32, 32, 34, 34, 35, 36, 38, 39, 41, 44, 44, + 48, 48, 53, 53, 57, 58, 61, 65, 67, 71, 72, 79, 79, 31, 32, 32, 32, 32, 32, 32, 32, 32, 33, 34, 34, 34, 35, 36, 38, 39, 41, 42, 45, 45, + 49, 50, 53, 54, 57, 60, 62, 66, 66, 73, 73, 31, 32, 32, 32, 32, 32, 32, 32, 33, 33, 34, 34, 34, 34, 36, 37, 39, 41, 42, 45, 45, 49, 49, + 52, 54, 57, 60, 61, 65, 66, 72, 72, 32, 32, 32, 32, 32, 33, 33, 34, 34, 34, 36, 36, 37, 37, 38, 40, 41, 42, 43, 46, 46, 49, 50, 52, 54, + 56, 59, 60, 64, 64, 71, 71, 32, 32, 32, 32, 33, 33, 34, 34, 35, 35, 37, 37, 38, 38, 40, 40, 41, 43, 43, 46, 46, 49, 50, 52, 54, 56, 58, + 60, 63, 64, 70, 70, 34, 34, 34, 33, 33, 34, 35, 35, 37, 37, 39, 39, 42, 43, 44, 45, 46, 48, 48, 51, 51, 54, 54, 57, 58, 60, 63, 64, 68, + 68, 74, 74, 36, 35, 35, 35, 34, 35, 36, 37, 38, 39, 42, 42, 47, 48, 49, 50, 51, 53, 54, 56, 56, 59, 60, 62, 63, 66, 68, 69, 73, 73, 79, + 79, 38, 37, 37, 36, 36, 37, 38, 38, 39, 40, 44, 44, 48, 49, 51, 52, 54, 56, 56, 59, 59, 62, 63, 65, 67, 69, 71, 72, 76, 76, 82, 82, 44, + 42, 42, 41, 41, 41, 42, 42, 42, 43, 48, 48, 52, 54, 56, 58, 60, 63, 64, 67, 67, 71, 71, 74, 75, 77, 79, 81, 84, 85, 90, 90, 44, 43, 43, + 42, 41, 42, 43, 43, 43, 44, 48, 48, 53, 54, 57, 58, 60, 64, 64, 67, 67, 71, 72, 75, 76, 78, 80, 82, 85, 86, 91, 91, 53, 51, 51, 50, 49, + 49, 50, 49, 49, 50, 54, 54, 59, 60, 63, 65, 67, 71, 72, 76, 76, 81, 82, 85, 87, 89, 92, 94, 97, 98, 104, 104, 53, 51, 51, 50, 49, 49, 50, + 49, 49, 50, 54, 54, 59, 60, 63, 65, 67, 71, 72, 76, 76, 81, 82, 85, 87, 89, 92, 94, 97, 98, 104, 104, 62, 60, 59, 58, 57, 57, 57, 56, 56, + 56, 61, 61, 65, 66, 69, 71, 74, 78, 79, 83, 83, 89, 90, 94, 95, 98, 102, 103, 108, 108, 115, 115, 65, 62, 62, 60, 59, 59, 59, 59, 58, 58, + 63, 63, 67, 68, 71, 73, 76, 79, 81, 85, 85, 91, 92, 96, 98, 101, 105, 106, 111, 111, 118, 118, 73, 70, 69, 67, 66, 66, 65, 65, 64, 64, + 69, 69, 73, 74, 77, 79, 81, 85, 86, 91, 91, 98, 99, 103, 105, 108, 112, 114, 119, 119, 127, 127, 79, 75, 75, 73, 72, 71, 71, 70, 69, 69, + 73, 73, 77, 78, 81, 84, 86, 90, 91, 96, 96, 103, 103, 108, 110, 114, 118, 120, 125, 125, 133, 133, 31, 34, 44, 65, 32, 34, 43, 62, 32, + 33, 41, 59, 32, 35, 43, 59, 32, 37, 43, 58, 34, 39, 48, 63, 34, 42, 53, 67, 36, 44, 57, 71, 39, 46, 60, 76, 42, 48, 64, 81, 45, 51, 67, + 85, 50, 54, 72, 92, 54, 58, 76, 98, 60, 63, 80, 105, 66, 68, 85, 111, 73, 74, 91, 118, 31, 32, 32, 32, 32, 34, 34, 36, 39, 42, 45, 50, + 54, 60, 66, 73, 34, 34, 33, 35, 37, 39, 42, 44, 46, 48, 51, 54, 58, 63, 68, 74, 44, 43, 41, 43, 43, 48, 53, 57, 60, 64, 67, 72, 76, 80, + 85, 91, 65, 62, 59, 59, 58, 63, 67, 71, 76, 81, 85, 92, 98, 105, 111, 118, 32, 31, 32, 36, 44, 53, 62, 73, 31, 32, 32, 35, 42, 51, 60, + 70, 31, 32, 32, 35, 42, 51, 59, 69, 31, 32, 32, 35, 41, 50, 58, 67, 31, 32, 33, 34, 41, 49, 57, 66, 31, 32, 33, 35, 41, 49, 57, 66, 32, + 32, 34, 36, 42, 50, 57, 65, 32, 32, 34, 37, 42, 49, 56, 65, 32, 33, 35, 38, 42, 49, 56, 64, 32, 33, 35, 39, 43, 50, 56, 64, 34, 34, 37, + 42, 48, 54, 61, 69, 34, 34, 37, 42, 48, 54, 61, 69, 35, 34, 38, 47, 52, 59, 65, 73, 36, 34, 38, 48, 54, 60, 66, 74, 38, 36, 40, 49, 56, + 63, 69, 77, 39, 37, 40, 50, 58, 65, 71, 79, 41, 39, 41, 51, 60, 67, 74, 81, 44, 41, 43, 53, 63, 71, 78, 85, 44, 42, 43, 54, 64, 72, 79, + 86, 48, 45, 46, 56, 67, 76, 83, 91, 48, 45, 46, 56, 67, 76, 83, 91, 53, 49, 49, 59, 71, 81, 89, 98, 53, 49, 50, 60, 71, 82, 90, 99, 57, + 52, 52, 62, 74, 85, 94, 103, 58, 54, 54, 63, 75, 87, 95, 105, 61, 57, 56, 66, 77, 89, 98, 108, 65, 60, 58, 68, 79, 92, 102, 112, 67, 61, + 60, 69, 81, 94, 103, 114, 71, 65, 63, 73, 84, 97, 108, 119, 72, 66, 64, 73, 85, 98, 108, 119, 79, 72, 70, 79, 90, 104, 115, 127, 79, 72, + 70, 79, 90, 104, 115, 127, 32, 31, 31, 31, 31, 31, 32, 32, 32, 32, 34, 34, 35, 36, 38, 39, 41, 44, 44, 48, 48, 53, 53, 57, 58, 61, 65, + 67, 71, 72, 79, 79, 31, 32, 32, 32, 32, 32, 32, 32, 33, 33, 34, 34, 34, 34, 36, 37, 39, 41, 42, 45, 45, 49, 49, 52, 54, 57, 60, 61, 65, + 66, 72, 72, 32, 32, 32, 32, 33, 33, 34, 34, 35, 35, 37, 37, 38, 38, 40, 40, 41, 43, 43, 46, 46, 49, 50, 52, 54, 56, 58, 60, 63, 64, 70, + 70, 36, 35, 35, 35, 34, 35, 36, 37, 38, 39, 42, 42, 47, 48, 49, 50, 51, 53, 54, 56, 56, 59, 60, 62, 63, 66, 68, 69, 73, 73, 79, 79, 44, + 42, 42, 41, 41, 41, 42, 42, 42, 43, 48, 48, 52, 54, 56, 58, 60, 63, 64, 67, 67, 71, 71, 74, 75, 77, 79, 81, 84, 85, 90, 90, 53, 51, 51, + 50, 49, 49, 50, 49, 49, 50, 54, 54, 59, 60, 63, 65, 67, 71, 72, 76, 76, 81, 82, 85, 87, 89, 92, 94, 97, 98, 104, 104, 62, 60, 59, 58, 57, + 57, 57, 56, 56, 56, 61, 61, 65, 66, 69, 71, 74, 78, 79, 83, 83, 89, 90, 94, 95, 98, 102, 103, 108, 108, 115, 115, 73, 70, 69, 67, 66, 66, + 65, 65, 64, 64, 69, 69, 73, 74, 77, 79, 81, 85, 86, 91, 91, 98, 99, 103, 105, 108, 112, 114, 119, 119, 127, 127, + ], + [ + 31, 42, 47, 53, 42, 48, 50, 54, 47, 50, 61, 67, 53, 54, 67, 78, 31, 32, 38, 48, 47, 50, 53, 57, 32, 35, 42, 47, 45, 47, 50, 54, 38, 42, + 47, 48, 45, 47, 49, 52, 48, 47, 48, 53, 53, 54, 56, 58, 47, 45, 45, 53, 58, 61, 63, 65, 50, 47, 47, 54, 61, 66, 69, 72, 53, 50, 49, 56, + 63, 69, 73, 77, 57, 54, 52, 58, 65, 72, 77, 82, 32, 31, 30, 33, 36, 41, 47, 49, 49, 49, 50, 52, 54, 57, 60, 63, 31, 31, 31, 34, 38, 42, + 46, 47, 47, 47, 48, 50, 52, 54, 57, 60, 30, 31, 32, 35, 40, 42, 45, 46, 45, 45, 46, 47, 49, 52, 54, 57, 33, 34, 35, 39, 43, 45, 47, 46, + 46, 45, 46, 47, 49, 51, 53, 56, 36, 38, 40, 43, 47, 47, 47, 47, 46, 45, 46, 47, 48, 50, 52, 54, 41, 42, 42, 45, 47, 48, 50, 50, 49, 49, + 50, 50, 52, 53, 55, 57, 47, 46, 45, 47, 47, 50, 52, 52, 52, 52, 53, 53, 55, 56, 58, 60, 49, 47, 46, 46, 47, 50, 52, 53, 54, 55, 55, 56, + 57, 58, 60, 62, 49, 47, 45, 46, 46, 49, 52, 54, 55, 57, 58, 59, 60, 61, 63, 65, 49, 47, 45, 45, 45, 49, 52, 55, 57, 59, 60, 61, 63, 64, + 66, 68, 50, 48, 46, 46, 46, 50, 53, 55, 58, 60, 61, 63, 65, 67, 68, 71, 52, 50, 47, 47, 47, 50, 53, 56, 59, 61, 63, 66, 68, 70, 72, 75, + 54, 52, 49, 49, 48, 52, 55, 57, 60, 63, 65, 68, 71, 73, 75, 78, 57, 54, 52, 51, 50, 53, 56, 58, 61, 64, 67, 70, 73, 76, 79, 82, 60, 57, + 54, 53, 52, 55, 58, 60, 63, 66, 68, 72, 75, 79, 82, 85, 63, 60, 57, 56, 54, 57, 60, 62, 65, 68, 71, 75, 78, 82, 85, 89, 32, 31, 31, 30, + 30, 32, 33, 34, 36, 37, 41, 41, 47, 49, 49, 48, 49, 49, 49, 50, 50, 52, 52, 54, 54, 56, 57, 58, 60, 60, 63, 63, 31, 31, 31, 31, 31, 32, + 34, 35, 38, 38, 42, 42, 46, 48, 47, 47, 47, 47, 47, 48, 48, 50, 50, 51, 52, 53, 54, 55, 57, 57, 60, 60, 31, 31, 31, 31, 31, 33, 34, 35, + 38, 39, 42, 42, 46, 47, 47, 47, 47, 47, 47, 48, 48, 49, 50, 51, 52, 53, 54, 55, 57, 57, 60, 60, 30, 31, 31, 31, 31, 33, 35, 36, 39, 40, + 42, 42, 46, 47, 46, 46, 46, 45, 46, 47, 47, 48, 48, 50, 50, 51, 52, 53, 55, 55, 58, 58, 30, 31, 31, 31, 32, 33, 35, 36, 40, 40, 42, 42, + 45, 46, 46, 45, 45, 45, 45, 46, 46, 47, 47, 49, 49, 51, 52, 52, 54, 54, 57, 57, 32, 32, 33, 33, 33, 35, 37, 38, 41, 42, 43, 43, 46, 47, + 46, 46, 45, 45, 45, 46, 46, 47, 47, 49, 49, 50, 51, 52, 54, 54, 57, 57, 33, 34, 34, 35, 35, 37, 39, 40, 43, 43, 45, 45, 47, 47, 46, 46, + 46, 45, 45, 46, 46, 47, 47, 49, 49, 50, 51, 52, 53, 54, 56, 56, 34, 35, 35, 36, 36, 38, 40, 41, 44, 44, 45, 45, 47, 47, 47, 46, 46, 45, + 45, 46, 46, 47, 47, 48, 49, 50, 51, 51, 53, 53, 55, 55, 36, 38, 38, 39, 40, 41, 43, 44, 47, 47, 47, 47, 47, 48, 47, 46, 46, 45, 45, 46, + 46, 46, 47, 48, 48, 49, 50, 50, 52, 52, 54, 54, 37, 38, 39, 40, 40, 42, 43, 44, 47, 47, 47, 47, 48, 48, 47, 47, 46, 45, 46, 46, 46, 47, + 47, 48, 48, 49, 50, 51, 52, 52, 55, 55, 41, 42, 42, 42, 42, 43, 45, 45, 47, 47, 48, 48, 50, 50, 50, 49, 49, 49, 49, 50, 50, 50, 50, 51, + 52, 52, 53, 54, 55, 55, 57, 57, 41, 42, 42, 42, 42, 43, 45, 45, 47, 47, 48, 48, 50, 50, 50, 49, 49, 49, 49, 50, 50, 50, 50, 51, 52, 52, + 53, 54, 55, 55, 57, 57, 47, 46, 46, 46, 45, 46, 47, 47, 47, 48, 50, 50, 52, 52, 52, 52, 52, 52, 52, 53, 53, 53, 53, 54, 55, 55, 56, 56, + 58, 58, 60, 60, 49, 48, 47, 47, 46, 47, 47, 47, 48, 48, 50, 50, 52, 53, 53, 53, 53, 53, 53, 54, 54, 54, 54, 55, 55, 56, 56, 57, 58, 58, + 60, 60, 49, 47, 47, 46, 46, 46, 46, 47, 47, 47, 50, 50, 52, 53, 53, 54, 54, 55, 55, 55, 55, 56, 56, 57, 57, 58, 58, 59, 60, 60, 62, 62, + 48, 47, 47, 46, 45, 46, 46, 46, 46, 47, 49, 49, 52, 53, 54, 54, 55, 55, 56, 56, 56, 57, 57, 58, 58, 59, 60, 60, 61, 62, 63, 63, 49, 47, + 47, 46, 45, 45, 46, 46, 46, 46, 49, 49, 52, 53, 54, 55, 55, 57, 57, 58, 58, 59, 59, 60, 60, 61, 61, 62, 63, 63, 65, 65, 49, 47, 47, 45, + 45, 45, 45, 45, 45, 45, 49, 49, 52, 53, 55, 55, 57, 58, 59, 60, 60, 61, 61, 62, 62, 63, 63, 64, 65, 65, 67, 67, 49, 47, 47, 46, 45, 45, + 45, 45, 45, 46, 49, 49, 52, 53, 55, 56, 57, 59, 59, 60, 60, 61, 61, 62, 63, 63, 64, 65, 66, 66, 68, 68, 50, 48, 48, 47, 46, 46, 46, 46, + 46, 46, 50, 50, 53, 54, 55, 56, 58, 60, 60, 61, 61, 63, 63, 65, 65, 66, 67, 67, 68, 69, 71, 71, 50, 48, 48, 47, 46, 46, 46, 46, 46, 46, + 50, 50, 53, 54, 55, 56, 58, 60, 60, 61, 61, 63, 63, 65, 65, 66, 67, 67, 68, 69, 71, 71, 52, 50, 49, 48, 47, 47, 47, 47, 46, 47, 50, 50, + 53, 54, 56, 57, 59, 61, 61, 63, 63, 66, 66, 67, 68, 69, 70, 71, 72, 72, 74, 74, 52, 50, 50, 48, 47, 47, 47, 47, 47, 47, 50, 50, 53, 54, + 56, 57, 59, 61, 61, 63, 63, 66, 66, 68, 68, 69, 70, 71, 72, 73, 75, 75, 54, 51, 51, 50, 49, 49, 49, 48, 48, 48, 51, 51, 54, 55, 57, 58, + 60, 62, 62, 65, 65, 67, 68, 69, 70, 71, 72, 73, 74, 75, 77, 77, 54, 52, 52, 50, 49, 49, 49, 49, 48, 48, 52, 52, 55, 55, 57, 58, 60, 62, + 63, 65, 65, 68, 68, 70, 71, 72, 73, 74, 75, 76, 78, 78, 56, 53, 53, 51, 51, 50, 50, 50, 49, 49, 52, 52, 55, 56, 58, 59, 61, 63, 63, 66, + 66, 69, 69, 71, 72, 73, 75, 75, 77, 77, 80, 80, 57, 54, 54, 52, 52, 51, 51, 51, 50, 50, 53, 53, 56, 56, 58, 60, 61, 63, 64, 67, 67, 70, + 70, 72, 73, 75, 76, 77, 79, 79, 82, 82, 58, 55, 55, 53, 52, 52, 52, 51, 50, 51, 54, 54, 56, 57, 59, 60, 62, 64, 65, 67, 67, 71, 71, 73, + 74, 75, 77, 78, 80, 80, 83, 83, 60, 57, 57, 55, 54, 54, 53, 53, 52, 52, 55, 55, 58, 58, 60, 61, 63, 65, 66, 68, 68, 72, 72, 74, 75, 77, + 79, 80, 82, 82, 85, 85, 60, 57, 57, 55, 54, 54, 54, 53, 52, 52, 55, 55, 58, 58, 60, 62, 63, 65, 66, 69, 69, 72, 73, 75, 76, 77, 79, 80, + 82, 82, 85, 85, 63, 60, 60, 58, 57, 57, 56, 55, 54, 55, 57, 57, 60, 60, 62, 63, 65, 67, 68, 71, 71, 74, 75, 77, 78, 80, 82, 83, 85, 85, + 89, 89, 63, 60, 60, 58, 57, 57, 56, 55, 54, 55, 57, 57, 60, 60, 62, 63, 65, 67, 68, 71, 71, 74, 75, 77, 78, 80, 82, 83, 85, 85, 89, 89, + 31, 42, 47, 54, 33, 44, 45, 51, 40, 47, 46, 50, 47, 50, 54, 57, 45, 49, 59, 64, 48, 50, 61, 70, 51, 52, 63, 75, 55, 55, 66, 79, 31, 33, + 40, 47, 45, 48, 51, 55, 42, 44, 47, 50, 49, 50, 52, 55, 47, 45, 46, 54, 59, 61, 63, 66, 54, 51, 50, 57, 64, 70, 75, 79, 32, 31, 37, 48, + 49, 52, 56, 61, 31, 31, 38, 47, 47, 50, 53, 57, 30, 32, 40, 46, 45, 48, 51, 55, 33, 36, 43, 47, 46, 47, 50, 54, 37, 40, 47, 47, 45, 47, + 49, 52, 42, 43, 47, 50, 49, 50, 53, 56, 47, 46, 48, 52, 53, 53, 55, 58, 48, 46, 47, 53, 55, 56, 58, 61, 48, 45, 46, 53, 57, 59, 61, 63, + 49, 45, 46, 53, 58, 62, 64, 66, 50, 46, 46, 54, 59, 64, 66, 69, 52, 48, 47, 54, 61, 66, 70, 73, 54, 50, 49, 55, 62, 68, 72, 76, 57, 52, + 50, 56, 64, 70, 75, 79, 60, 54, 52, 58, 65, 72, 77, 82, 63, 57, 55, 60, 67, 75, 80, 86, 32, 31, 30, 33, 37, 42, 47, 48, 48, 49, 50, 52, + 54, 57, 60, 63, 31, 31, 32, 36, 40, 43, 46, 46, 45, 45, 46, 48, 50, 52, 54, 57, 37, 38, 40, 43, 47, 47, 48, 47, 46, 46, 46, 47, 49, 50, + 52, 55, 48, 47, 46, 47, 47, 50, 52, 53, 53, 53, 54, 54, 55, 56, 58, 60, 49, 47, 45, 46, 45, 49, 53, 55, 57, 58, 59, 61, 62, 64, 65, 67, + 52, 50, 48, 47, 47, 50, 53, 56, 59, 62, 64, 66, 68, 70, 72, 75, 56, 53, 51, 50, 49, 53, 55, 58, 61, 64, 66, 70, 72, 75, 77, 80, 61, 57, + 55, 54, 52, 56, 58, 61, 63, 66, 69, 73, 76, 79, 82, 86, 32, 31, 31, 35, 37, 42, 48, 48, 49, 49, 52, 52, 56, 57, 61, 63, 31, 31, 31, 36, + 38, 42, 47, 47, 47, 47, 50, 50, 54, 54, 58, 60, 31, 31, 31, 36, 38, 42, 47, 47, 47, 47, 50, 50, 53, 54, 57, 60, 30, 32, 32, 37, 39, 42, + 46, 46, 46, 46, 48, 48, 52, 52, 56, 58, 30, 32, 32, 37, 40, 42, 46, 46, 45, 45, 48, 48, 51, 52, 55, 57, 32, 33, 34, 39, 41, 44, 46, 46, + 45, 45, 48, 48, 51, 51, 54, 57, 33, 35, 36, 40, 43, 45, 47, 46, 46, 46, 47, 47, 50, 51, 54, 56, 34, 37, 37, 42, 44, 45, 47, 47, 45, 46, + 47, 47, 50, 51, 53, 55, 37, 40, 40, 45, 47, 47, 47, 47, 45, 46, 47, 47, 49, 50, 52, 54, 37, 40, 40, 45, 47, 47, 48, 47, 46, 46, 47, 47, + 49, 50, 53, 55, 42, 43, 43, 46, 47, 48, 50, 50, 49, 49, 50, 50, 53, 53, 56, 57, 42, 43, 43, 46, 47, 48, 50, 50, 49, 49, 50, 50, 53, 53, + 56, 57, 47, 46, 46, 47, 48, 50, 52, 52, 53, 53, 53, 53, 55, 56, 58, 60, 49, 47, 46, 47, 48, 50, 53, 53, 53, 54, 54, 54, 56, 57, 59, 60, + 48, 46, 46, 47, 47, 50, 53, 53, 55, 55, 56, 56, 58, 58, 61, 62, 48, 46, 46, 46, 47, 50, 53, 54, 56, 56, 57, 57, 59, 60, 62, 64, 48, 46, + 45, 46, 46, 49, 53, 54, 57, 57, 59, 59, 61, 61, 63, 65, 49, 45, 45, 45, 46, 49, 53, 55, 58, 59, 61, 61, 63, 64, 66, 67, 49, 46, 45, 46, + 46, 49, 53, 55, 58, 59, 62, 62, 64, 64, 66, 68, 50, 47, 46, 46, 46, 50, 54, 55, 59, 60, 64, 64, 66, 67, 69, 71, 50, 47, 46, 46, 46, 50, + 54, 55, 59, 60, 64, 64, 66, 67, 69, 71, 52, 48, 48, 47, 47, 50, 54, 56, 61, 61, 66, 66, 69, 70, 72, 74, 52, 48, 48, 47, 47, 50, 54, 56, + 61, 61, 66, 66, 70, 71, 73, 75, 53, 50, 49, 48, 48, 51, 55, 57, 62, 62, 68, 68, 71, 72, 75, 77, 54, 50, 50, 49, 49, 52, 55, 57, 62, 63, + 68, 68, 72, 73, 76, 78, 55, 51, 51, 50, 49, 52, 56, 58, 63, 63, 69, 69, 74, 75, 78, 80, 57, 52, 52, 51, 50, 53, 56, 58, 64, 64, 70, 70, + 75, 76, 79, 82, 58, 53, 53, 51, 51, 54, 57, 59, 64, 65, 71, 71, 76, 77, 80, 83, 60, 55, 54, 53, 52, 55, 58, 60, 65, 66, 72, 72, 77, 79, + 82, 85, 60, 55, 55, 53, 53, 55, 59, 60, 65, 66, 73, 73, 78, 79, 83, 85, 63, 58, 57, 56, 55, 58, 60, 62, 67, 68, 75, 75, 80, 82, 86, 89, + 63, 58, 57, 56, 55, 58, 60, 62, 67, 68, 75, 75, 80, 82, 86, 89, 32, 31, 31, 30, 30, 32, 33, 34, 37, 37, 42, 42, 47, 49, 48, 48, 48, 49, + 49, 50, 50, 52, 52, 53, 54, 55, 57, 58, 60, 60, 63, 63, 31, 31, 31, 32, 32, 33, 35, 37, 40, 40, 43, 43, 46, 47, 46, 46, 46, 45, 46, 47, + 47, 48, 48, 50, 50, 51, 52, 53, 55, 55, 58, 58, 31, 31, 31, 32, 32, 34, 36, 37, 40, 40, 43, 43, 46, 46, 46, 46, 45, 45, 45, 46, 46, 48, + 48, 49, 50, 51, 52, 53, 54, 55, 57, 57, 35, 36, 36, 37, 37, 39, 40, 42, 45, 45, 46, 46, 47, 47, 47, 46, 46, 45, 46, 46, 46, 47, 47, 48, + 49, 50, 51, 51, 53, 53, 56, 56, 37, 38, 38, 39, 40, 41, 43, 44, 47, 47, 47, 47, 48, 48, 47, 47, 46, 46, 46, 46, 46, 47, 47, 48, 49, 49, + 50, 51, 52, 53, 55, 55, 42, 42, 42, 42, 42, 44, 45, 45, 47, 47, 48, 48, 50, 50, 50, 50, 49, 49, 49, 50, 50, 50, 50, 51, 52, 52, 53, 54, + 55, 55, 58, 58, 48, 47, 47, 46, 46, 46, 47, 47, 47, 48, 50, 50, 52, 53, 53, 53, 53, 53, 53, 54, 54, 54, 54, 55, 55, 56, 56, 57, 58, 59, + 60, 60, 48, 47, 47, 46, 46, 46, 46, 47, 47, 47, 50, 50, 52, 53, 53, 54, 54, 55, 55, 55, 55, 56, 56, 57, 57, 58, 58, 59, 60, 60, 62, 62, + 49, 47, 47, 46, 45, 45, 46, 45, 45, 46, 49, 49, 53, 53, 55, 56, 57, 58, 58, 59, 59, 61, 61, 62, 62, 63, 64, 64, 65, 65, 67, 67, 49, 47, + 47, 46, 45, 45, 46, 46, 46, 46, 49, 49, 53, 54, 55, 56, 57, 59, 59, 60, 60, 61, 61, 62, 63, 63, 64, 65, 66, 66, 68, 68, 52, 50, 50, 48, + 48, 48, 47, 47, 47, 47, 50, 50, 53, 54, 56, 57, 59, 61, 62, 64, 64, 66, 66, 68, 68, 69, 70, 71, 72, 73, 75, 75, 52, 50, 50, 48, 48, 48, + 47, 47, 47, 47, 50, 50, 53, 54, 56, 57, 59, 61, 62, 64, 64, 66, 66, 68, 68, 69, 70, 71, 72, 73, 75, 75, 56, 54, 53, 52, 51, 51, 50, 50, + 49, 49, 53, 53, 55, 56, 58, 59, 61, 63, 64, 66, 66, 69, 70, 71, 72, 74, 75, 76, 77, 78, 80, 80, 57, 54, 54, 52, 52, 51, 51, 51, 50, 50, + 53, 53, 56, 57, 58, 60, 61, 64, 64, 67, 67, 70, 71, 72, 73, 75, 76, 77, 79, 79, 82, 82, 61, 58, 57, 56, 55, 54, 54, 53, 52, 53, 56, 56, + 58, 59, 61, 62, 63, 66, 66, 69, 69, 72, 73, 75, 76, 78, 79, 80, 82, 83, 86, 86, 63, 60, 60, 58, 57, 57, 56, 55, 54, 55, 57, 57, 60, 60, + 62, 64, 65, 67, 68, 71, 71, 74, 75, 77, 78, 80, 82, 83, 85, 85, 89, 89, 31, 42, 49, 57, 31, 42, 47, 54, 32, 42, 45, 52, 35, 45, 46, 51, + 40, 47, 46, 50, 43, 48, 49, 53, 46, 50, 53, 56, 46, 50, 55, 58, 46, 49, 57, 61, 46, 49, 59, 64, 47, 50, 60, 67, 48, 50, 61, 71, 50, 52, + 63, 73, 52, 53, 64, 76, 55, 55, 66, 79, 58, 58, 68, 82, 31, 31, 32, 35, 40, 43, 46, 46, 46, 46, 47, 48, 50, 52, 55, 58, 42, 42, 42, 45, + 47, 48, 50, 50, 49, 49, 50, 50, 52, 53, 55, 58, 49, 47, 45, 46, 46, 49, 53, 55, 57, 59, 60, 61, 63, 64, 66, 68, 57, 54, 52, 51, 50, 53, + 56, 58, 61, 64, 67, 71, 73, 76, 79, 82, 32, 31, 37, 48, 49, 52, 56, 61, 31, 31, 38, 47, 47, 50, 54, 58, 31, 31, 38, 47, 47, 50, 53, 57, + 30, 32, 39, 46, 46, 48, 52, 56, 30, 32, 40, 46, 45, 48, 51, 55, 32, 34, 41, 46, 45, 48, 51, 54, 33, 36, 43, 47, 46, 47, 50, 54, 34, 37, + 44, 47, 45, 47, 50, 53, 37, 40, 47, 47, 45, 47, 49, 52, 37, 40, 47, 48, 46, 47, 49, 53, 42, 43, 47, 50, 49, 50, 53, 56, 42, 43, 47, 50, + 49, 50, 53, 56, 47, 46, 48, 52, 53, 53, 55, 58, 49, 46, 48, 53, 53, 54, 56, 59, 48, 46, 47, 53, 55, 56, 58, 61, 48, 46, 47, 53, 56, 57, + 59, 62, 48, 45, 46, 53, 57, 59, 61, 63, 49, 45, 46, 53, 58, 61, 63, 66, 49, 45, 46, 53, 58, 62, 64, 66, 50, 46, 46, 54, 59, 64, 66, 69, + 50, 46, 46, 54, 59, 64, 66, 69, 52, 48, 47, 54, 61, 66, 69, 72, 52, 48, 47, 54, 61, 66, 70, 73, 53, 49, 48, 55, 62, 68, 71, 75, 54, 50, + 49, 55, 62, 68, 72, 76, 55, 51, 49, 56, 63, 69, 74, 78, 57, 52, 50, 56, 64, 70, 75, 79, 58, 53, 51, 57, 64, 71, 76, 80, 60, 54, 52, 58, + 65, 72, 77, 82, 60, 55, 53, 59, 65, 73, 78, 83, 63, 57, 55, 60, 67, 75, 80, 86, 63, 57, 55, 60, 67, 75, 80, 86, 32, 31, 31, 30, 30, 32, + 33, 34, 37, 37, 42, 42, 47, 49, 48, 48, 48, 49, 49, 50, 50, 52, 52, 53, 54, 55, 57, 58, 60, 60, 63, 63, 31, 31, 31, 32, 32, 34, 36, 37, + 40, 40, 43, 43, 46, 46, 46, 46, 45, 45, 45, 46, 46, 48, 48, 49, 50, 51, 52, 53, 54, 55, 57, 57, 37, 38, 38, 39, 40, 41, 43, 44, 47, 47, + 47, 47, 48, 48, 47, 47, 46, 46, 46, 46, 46, 47, 47, 48, 49, 49, 50, 51, 52, 53, 55, 55, 48, 47, 47, 46, 46, 46, 47, 47, 47, 48, 50, 50, + 52, 53, 53, 53, 53, 53, 53, 54, 54, 54, 54, 55, 55, 56, 56, 57, 58, 59, 60, 60, 49, 47, 47, 46, 45, 45, 46, 45, 45, 46, 49, 49, 53, 53, + 55, 56, 57, 58, 58, 59, 59, 61, 61, 62, 62, 63, 64, 64, 65, 65, 67, 67, 52, 50, 50, 48, 48, 48, 47, 47, 47, 47, 50, 50, 53, 54, 56, 57, + 59, 61, 62, 64, 64, 66, 66, 68, 68, 69, 70, 71, 72, 73, 75, 75, 56, 54, 53, 52, 51, 51, 50, 50, 49, 49, 53, 53, 55, 56, 58, 59, 61, 63, + 64, 66, 66, 69, 70, 71, 72, 74, 75, 76, 77, 78, 80, 80, 61, 58, 57, 56, 55, 54, 54, 53, 52, 53, 56, 56, 58, 59, 61, 62, 63, 66, 66, 69, + 69, 72, 73, 75, 76, 78, 79, 80, 82, 83, 86, 86, + ], + ], + [ + [ + 32, 33, 42, 55, 33, 38, 46, 57, 42, 46, 63, 75, 55, 57, 75, 92, 31, 32, 32, 34, 38, 46, 52, 63, 32, 32, 32, 34, 37, 44, 49, 59, 32, 32, + 35, 37, 40, 45, 49, 58, 34, 34, 37, 42, 47, 52, 56, 65, 38, 37, 40, 47, 54, 60, 65, 73, 46, 44, 45, 52, 60, 69, 75, 84, 52, 49, 49, 56, + 65, 75, 82, 92, 63, 59, 58, 65, 73, 84, 92, 105, 32, 31, 31, 31, 32, 32, 34, 36, 38, 41, 44, 48, 54, 58, 61, 65, 31, 32, 32, 32, 32, 32, + 34, 35, 38, 40, 42, 46, 51, 55, 58, 62, 31, 32, 32, 32, 32, 32, 33, 34, 37, 38, 41, 44, 49, 53, 56, 59, 31, 32, 32, 33, 33, 33, 35, 36, + 38, 40, 42, 45, 49, 53, 56, 59, 32, 32, 32, 33, 34, 34, 36, 37, 39, 40, 42, 45, 49, 53, 55, 59, 32, 32, 32, 33, 34, 35, 37, 38, 40, 41, + 42, 46, 49, 52, 55, 58, 34, 34, 33, 35, 36, 37, 39, 42, 44, 46, 47, 51, 54, 57, 60, 63, 36, 35, 34, 36, 37, 38, 42, 48, 50, 52, 54, 57, + 60, 63, 65, 68, 38, 38, 37, 38, 39, 40, 44, 50, 52, 54, 57, 60, 64, 67, 69, 72, 41, 40, 38, 40, 40, 41, 46, 52, 54, 57, 60, 63, 67, 70, + 73, 75, 44, 42, 41, 42, 42, 42, 47, 54, 57, 60, 63, 67, 71, 74, 77, 79, 48, 46, 44, 45, 45, 46, 51, 57, 60, 63, 67, 71, 76, 79, 82, 85, + 54, 51, 49, 49, 49, 49, 54, 60, 64, 67, 71, 76, 82, 86, 89, 92, 58, 55, 53, 53, 53, 52, 57, 63, 67, 70, 74, 79, 86, 90, 93, 97, 61, 58, + 56, 56, 55, 55, 60, 65, 69, 73, 77, 82, 89, 93, 97, 101, 65, 62, 59, 59, 59, 58, 63, 68, 72, 75, 79, 85, 92, 97, 101, 105, 32, 31, 31, + 31, 31, 31, 31, 31, 32, 32, 32, 33, 34, 34, 36, 36, 38, 39, 41, 44, 44, 47, 48, 50, 54, 54, 58, 59, 61, 65, 65, 70, 31, 31, 31, 32, 32, + 32, 32, 32, 32, 32, 32, 33, 34, 34, 35, 35, 38, 38, 40, 42, 42, 46, 47, 49, 52, 52, 56, 57, 59, 63, 63, 67, 31, 31, 32, 32, 32, 32, 32, + 32, 32, 32, 32, 33, 34, 34, 35, 35, 38, 38, 40, 42, 42, 45, 46, 48, 51, 51, 55, 56, 58, 62, 62, 67, 31, 32, 32, 32, 32, 32, 32, 32, 32, + 32, 32, 33, 34, 34, 35, 35, 37, 38, 39, 42, 42, 45, 45, 47, 50, 50, 54, 55, 57, 61, 61, 65, 31, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, + 33, 33, 34, 34, 34, 37, 37, 38, 41, 41, 44, 44, 46, 49, 49, 53, 54, 56, 59, 59, 64, 31, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 33, 33, + 34, 34, 34, 37, 37, 38, 41, 41, 44, 44, 46, 49, 49, 53, 54, 56, 59, 59, 64, 31, 32, 32, 32, 32, 32, 33, 33, 33, 33, 33, 34, 35, 35, 36, + 36, 38, 39, 40, 42, 42, 44, 45, 47, 49, 49, 53, 54, 56, 59, 59, 63, 31, 32, 32, 32, 32, 32, 33, 33, 33, 34, 34, 35, 35, 36, 36, 36, 38, + 39, 40, 42, 42, 45, 45, 47, 50, 50, 53, 54, 56, 59, 59, 63, 32, 32, 32, 32, 32, 32, 33, 33, 34, 34, 34, 35, 36, 36, 37, 37, 39, 39, 40, + 42, 42, 45, 45, 47, 49, 49, 53, 54, 55, 59, 59, 63, 32, 32, 32, 32, 32, 32, 33, 34, 34, 35, 35, 36, 37, 37, 38, 38, 40, 40, 41, 42, 42, + 45, 46, 47, 49, 49, 52, 53, 55, 58, 58, 62, 32, 32, 32, 32, 32, 32, 33, 34, 34, 35, 35, 36, 37, 37, 38, 38, 40, 40, 41, 42, 42, 45, 46, + 47, 49, 49, 52, 53, 55, 58, 58, 62, 33, 33, 33, 33, 33, 33, 34, 35, 35, 36, 36, 38, 39, 40, 42, 42, 43, 44, 45, 46, 46, 49, 50, 51, 53, + 53, 56, 57, 59, 62, 62, 66, 34, 34, 34, 34, 33, 33, 35, 35, 36, 37, 37, 39, 39, 41, 42, 42, 44, 45, 46, 47, 47, 50, 51, 52, 54, 54, 57, + 58, 60, 63, 63, 67, 34, 34, 34, 34, 34, 34, 35, 36, 36, 37, 37, 40, 41, 42, 45, 45, 46, 47, 48, 50, 50, 52, 53, 54, 56, 56, 59, 60, 62, + 65, 65, 69, 36, 35, 35, 35, 34, 34, 36, 36, 37, 38, 38, 42, 42, 45, 48, 48, 50, 50, 52, 54, 54, 56, 57, 58, 60, 60, 63, 64, 65, 68, 68, + 72, 36, 35, 35, 35, 34, 34, 36, 36, 37, 38, 38, 42, 42, 45, 48, 48, 50, 50, 52, 54, 54, 56, 57, 58, 60, 60, 63, 64, 65, 68, 68, 72, 38, + 38, 38, 37, 37, 37, 38, 38, 39, 40, 40, 43, 44, 46, 50, 50, 52, 53, 54, 57, 57, 59, 60, 61, 64, 64, 67, 68, 69, 72, 72, 76, 39, 38, 38, + 38, 37, 37, 39, 39, 39, 40, 40, 44, 45, 47, 50, 50, 53, 54, 55, 58, 58, 60, 61, 62, 65, 65, 68, 69, 70, 73, 73, 77, 41, 40, 40, 39, 38, + 38, 40, 40, 40, 41, 41, 45, 46, 48, 52, 52, 54, 55, 57, 60, 60, 62, 63, 65, 67, 67, 70, 71, 73, 75, 75, 79, 44, 42, 42, 42, 41, 41, 42, + 42, 42, 42, 42, 46, 47, 50, 54, 54, 57, 58, 60, 63, 63, 66, 67, 68, 71, 71, 74, 75, 77, 79, 79, 83, 44, 42, 42, 42, 41, 41, 42, 42, 42, + 42, 42, 46, 47, 50, 54, 54, 57, 58, 60, 63, 63, 66, 67, 68, 71, 71, 74, 75, 77, 79, 79, 83, 47, 46, 45, 45, 44, 44, 44, 45, 45, 45, 45, + 49, 50, 52, 56, 56, 59, 60, 62, 66, 66, 69, 70, 72, 75, 75, 78, 79, 81, 84, 84, 88, 48, 47, 46, 45, 44, 44, 45, 45, 45, 46, 46, 50, 51, + 53, 57, 57, 60, 61, 63, 67, 67, 70, 71, 73, 76, 76, 79, 80, 82, 85, 85, 89, 50, 49, 48, 47, 46, 46, 47, 47, 47, 47, 47, 51, 52, 54, 58, + 58, 61, 62, 65, 68, 68, 72, 73, 75, 78, 78, 82, 83, 85, 88, 88, 92, 54, 52, 51, 50, 49, 49, 49, 50, 49, 49, 49, 53, 54, 56, 60, 60, 64, + 65, 67, 71, 71, 75, 76, 78, 82, 82, 86, 87, 89, 92, 92, 96, 54, 52, 51, 50, 49, 49, 49, 50, 49, 49, 49, 53, 54, 56, 60, 60, 64, 65, 67, + 71, 71, 75, 76, 78, 82, 82, 86, 87, 89, 92, 92, 96, 58, 56, 55, 54, 53, 53, 53, 53, 53, 52, 52, 56, 57, 59, 63, 63, 67, 68, 70, 74, 74, + 78, 79, 82, 86, 86, 90, 91, 93, 97, 97, 101, 59, 57, 56, 55, 54, 54, 54, 54, 54, 53, 53, 57, 58, 60, 64, 64, 68, 69, 71, 75, 75, 79, 80, + 83, 87, 87, 91, 92, 94, 98, 98, 102, 61, 59, 58, 57, 56, 56, 56, 56, 55, 55, 55, 59, 60, 62, 65, 65, 69, 70, 73, 77, 77, 81, 82, 85, 89, + 89, 93, 94, 97, 101, 101, 105, 65, 63, 62, 61, 59, 59, 59, 59, 59, 58, 58, 62, 63, 65, 68, 68, 72, 73, 75, 79, 79, 84, 85, 88, 92, 92, + 97, 98, 101, 105, 105, 109, 65, 63, 62, 61, 59, 59, 59, 59, 59, 58, 58, 62, 63, 65, 68, 68, 72, 73, 75, 79, 79, 84, 85, 88, 92, 92, 97, + 98, 101, 105, 105, 109, 70, 67, 67, 65, 64, 64, 63, 63, 63, 62, 62, 66, 67, 69, 72, 72, 76, 77, 79, 83, 83, 88, 89, 92, 96, 96, 101, 102, + 105, 109, 109, 114, 32, 32, 42, 56, 32, 33, 41, 53, 32, 35, 42, 52, 34, 37, 50, 59, 38, 40, 58, 68, 44, 45, 66, 78, 50, 50, 71, 86, 61, + 58, 79, 97, 32, 32, 32, 34, 38, 44, 50, 61, 32, 33, 35, 37, 40, 45, 50, 58, 42, 41, 42, 50, 58, 66, 71, 79, 56, 53, 52, 59, 68, 78, 86, + 97, 32, 31, 32, 35, 39, 44, 53, 65, 31, 32, 32, 35, 38, 42, 51, 62, 31, 32, 33, 34, 37, 41, 49, 59, 31, 32, 34, 35, 38, 42, 49, 59, 32, + 32, 34, 36, 39, 42, 49, 58, 32, 33, 35, 37, 40, 42, 49, 58, 34, 34, 37, 41, 44, 48, 54, 63, 36, 34, 38, 46, 50, 54, 60, 68, 38, 37, 40, + 47, 52, 57, 64, 72, 41, 39, 41, 49, 54, 60, 67, 76, 44, 41, 43, 51, 57, 63, 71, 79, 48, 45, 46, 54, 60, 67, 76, 85, 53, 49, 50, 57, 64, + 71, 82, 92, 57, 53, 53, 60, 67, 74, 86, 97, 61, 56, 56, 63, 69, 77, 89, 100, 65, 60, 58, 66, 72, 79, 92, 105, 32, 31, 31, 31, 32, 32, 34, + 36, 38, 41, 44, 48, 53, 57, 61, 65, 31, 32, 32, 32, 32, 33, 34, 34, 37, 39, 41, 45, 49, 53, 56, 60, 32, 32, 33, 34, 34, 35, 37, 38, 40, + 41, 43, 46, 50, 53, 56, 58, 35, 35, 34, 35, 36, 37, 41, 46, 47, 49, 51, 54, 57, 60, 63, 66, 39, 38, 37, 38, 39, 40, 44, 50, 52, 54, 57, + 60, 64, 67, 69, 72, 44, 42, 41, 42, 42, 42, 48, 54, 57, 60, 63, 67, 71, 74, 77, 79, 53, 51, 49, 49, 49, 49, 54, 60, 64, 67, 71, 76, 82, + 86, 89, 92, 65, 62, 59, 59, 58, 58, 63, 68, 72, 76, 79, 85, 92, 97, 100, 105, 32, 31, 31, 31, 32, 32, 35, 36, 39, 44, 44, 51, 53, 58, 65, + 65, 31, 32, 32, 32, 32, 32, 35, 35, 38, 42, 42, 49, 52, 56, 63, 63, 31, 32, 32, 32, 32, 32, 35, 35, 38, 42, 42, 49, 51, 55, 62, 62, 31, + 32, 32, 32, 32, 32, 34, 35, 37, 41, 41, 48, 50, 54, 61, 61, 31, 32, 32, 32, 33, 33, 34, 34, 37, 41, 41, 47, 49, 53, 59, 59, 31, 32, 32, + 32, 33, 33, 34, 34, 37, 41, 41, 47, 49, 53, 59, 59, 31, 32, 32, 33, 34, 34, 35, 36, 38, 42, 42, 48, 49, 53, 59, 59, 32, 32, 32, 33, 34, + 34, 36, 36, 38, 42, 42, 48, 50, 53, 59, 59, 32, 32, 32, 33, 34, 34, 36, 37, 39, 42, 42, 48, 49, 53, 58, 58, 32, 32, 33, 34, 35, 35, 37, + 38, 40, 42, 42, 48, 49, 52, 58, 58, 32, 32, 33, 34, 35, 35, 37, 38, 40, 42, 42, 48, 49, 52, 58, 58, 33, 33, 33, 35, 36, 36, 40, 41, 43, + 46, 46, 52, 53, 56, 62, 62, 34, 34, 34, 35, 37, 37, 41, 42, 44, 48, 48, 53, 54, 57, 63, 63, 34, 34, 34, 35, 37, 37, 43, 44, 46, 50, 50, + 55, 56, 59, 65, 65, 36, 35, 34, 36, 38, 38, 46, 48, 50, 54, 54, 58, 60, 63, 68, 68, 36, 35, 34, 36, 38, 38, 46, 48, 50, 54, 54, 58, 60, + 63, 68, 68, 38, 37, 37, 38, 40, 40, 47, 50, 52, 57, 57, 62, 64, 67, 72, 72, 39, 38, 37, 39, 40, 40, 48, 50, 53, 58, 58, 63, 65, 68, 73, + 73, 41, 39, 39, 40, 41, 41, 49, 51, 54, 60, 60, 66, 67, 70, 76, 76, 44, 41, 41, 42, 43, 43, 51, 53, 57, 63, 63, 69, 71, 74, 79, 79, 44, + 41, 41, 42, 43, 43, 51, 53, 57, 63, 63, 69, 71, 74, 79, 79, 47, 44, 44, 44, 45, 45, 53, 56, 59, 66, 66, 73, 75, 78, 84, 84, 48, 45, 45, + 45, 46, 46, 54, 56, 60, 67, 67, 74, 76, 79, 85, 85, 50, 47, 46, 47, 47, 47, 55, 58, 61, 68, 68, 76, 78, 82, 88, 88, 53, 50, 49, 50, 50, + 50, 57, 60, 64, 71, 71, 79, 82, 86, 92, 92, 53, 50, 49, 50, 50, 50, 57, 60, 64, 71, 71, 79, 82, 86, 92, 92, 57, 54, 53, 53, 53, 53, 60, + 63, 67, 74, 74, 83, 86, 90, 97, 97, 58, 55, 54, 54, 54, 54, 61, 63, 68, 75, 75, 84, 87, 91, 98, 98, 61, 57, 56, 56, 56, 56, 63, 65, 69, + 77, 77, 86, 89, 93, 100, 100, 65, 61, 60, 59, 58, 58, 66, 68, 72, 79, 79, 89, 92, 97, 105, 105, 65, 61, 60, 59, 58, 58, 66, 68, 72, 79, + 79, 89, 92, 97, 105, 105, 70, 65, 64, 63, 62, 62, 70, 72, 76, 83, 83, 93, 96, 101, 109, 109, 32, 31, 31, 31, 31, 31, 31, 32, 32, 32, 32, + 33, 34, 34, 36, 36, 38, 39, 41, 44, 44, 47, 48, 50, 53, 53, 57, 58, 61, 65, 65, 70, 31, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 33, 34, + 34, 35, 35, 37, 38, 39, 41, 41, 44, 45, 47, 50, 50, 54, 55, 57, 61, 61, 65, 31, 32, 32, 32, 32, 32, 32, 32, 32, 33, 33, 33, 34, 34, 34, + 34, 37, 37, 39, 41, 41, 44, 45, 46, 49, 49, 53, 54, 56, 60, 60, 64, 31, 32, 32, 32, 32, 32, 33, 33, 33, 34, 34, 35, 35, 35, 36, 36, 38, + 39, 40, 42, 42, 44, 45, 47, 50, 50, 53, 54, 56, 59, 59, 63, 32, 32, 32, 32, 33, 33, 34, 34, 34, 35, 35, 36, 37, 37, 38, 38, 40, 40, 41, + 43, 43, 45, 46, 47, 50, 50, 53, 54, 56, 58, 58, 62, 32, 32, 32, 32, 33, 33, 34, 34, 34, 35, 35, 36, 37, 37, 38, 38, 40, 40, 41, 43, 43, + 45, 46, 47, 50, 50, 53, 54, 56, 58, 58, 62, 35, 35, 35, 34, 34, 34, 35, 36, 36, 37, 37, 40, 41, 43, 46, 46, 47, 48, 49, 51, 51, 53, 54, + 55, 57, 57, 60, 61, 63, 66, 66, 70, 36, 35, 35, 35, 34, 34, 36, 36, 37, 38, 38, 41, 42, 44, 48, 48, 50, 50, 51, 53, 53, 56, 56, 58, 60, + 60, 63, 63, 65, 68, 68, 72, 39, 38, 38, 37, 37, 37, 38, 38, 39, 40, 40, 43, 44, 46, 50, 50, 52, 53, 54, 57, 57, 59, 60, 61, 64, 64, 67, + 68, 69, 72, 72, 76, 44, 42, 42, 41, 41, 41, 42, 42, 42, 42, 42, 46, 48, 50, 54, 54, 57, 58, 60, 63, 63, 66, 67, 68, 71, 71, 74, 75, 77, + 79, 79, 83, 44, 42, 42, 41, 41, 41, 42, 42, 42, 42, 42, 46, 48, 50, 54, 54, 57, 58, 60, 63, 63, 66, 67, 68, 71, 71, 74, 75, 77, 79, 79, + 83, 51, 49, 49, 48, 47, 47, 48, 48, 48, 48, 48, 52, 53, 55, 58, 58, 62, 63, 66, 69, 69, 73, 74, 76, 79, 79, 83, 84, 86, 89, 89, 93, 53, + 52, 51, 50, 49, 49, 49, 50, 49, 49, 49, 53, 54, 56, 60, 60, 64, 65, 67, 71, 71, 75, 76, 78, 82, 82, 86, 87, 89, 92, 92, 96, 58, 56, 55, + 54, 53, 53, 53, 53, 53, 52, 52, 56, 57, 59, 63, 63, 67, 68, 70, 74, 74, 78, 79, 82, 86, 86, 90, 91, 93, 97, 97, 101, 65, 63, 62, 61, 59, + 59, 59, 59, 58, 58, 58, 62, 63, 65, 68, 68, 72, 73, 76, 79, 79, 84, 85, 88, 92, 92, 97, 98, 100, 105, 105, 109, 65, 63, 62, 61, 59, 59, + 59, 59, 58, 58, 58, 62, 63, 65, 68, 68, 72, 73, 76, 79, 79, 84, 85, 88, 92, 92, 97, 98, 100, 105, 105, 109, 31, 32, 44, 58, 32, 32, 42, + 55, 32, 33, 41, 53, 32, 34, 42, 53, 32, 34, 42, 53, 32, 35, 42, 52, 34, 37, 48, 57, 35, 38, 54, 63, 37, 40, 57, 67, 39, 41, 60, 70, 41, + 43, 63, 74, 45, 46, 67, 79, 50, 50, 71, 86, 54, 53, 74, 90, 57, 56, 77, 93, 61, 58, 79, 97, 31, 32, 32, 32, 32, 32, 34, 35, 37, 39, 41, + 45, 50, 54, 57, 61, 32, 32, 33, 34, 34, 35, 37, 38, 40, 41, 43, 46, 50, 53, 56, 58, 44, 42, 41, 42, 42, 42, 48, 54, 57, 60, 63, 67, 71, + 74, 77, 79, 58, 55, 53, 53, 53, 52, 57, 63, 67, 70, 74, 79, 86, 90, 93, 97, 32, 31, 32, 35, 39, 44, 53, 65, 31, 32, 32, 35, 38, 42, 52, + 63, 31, 32, 32, 35, 38, 42, 51, 62, 31, 32, 32, 34, 37, 41, 50, 61, 31, 32, 33, 34, 37, 41, 49, 59, 31, 32, 33, 34, 37, 41, 49, 59, 31, + 32, 34, 35, 38, 42, 49, 59, 32, 32, 34, 36, 38, 42, 50, 59, 32, 32, 34, 36, 39, 42, 49, 58, 32, 33, 35, 37, 40, 42, 49, 58, 32, 33, 35, + 37, 40, 42, 49, 58, 33, 33, 36, 40, 43, 46, 53, 62, 34, 34, 37, 41, 44, 48, 54, 63, 34, 34, 37, 43, 46, 50, 56, 65, 36, 34, 38, 46, 50, + 54, 60, 68, 36, 34, 38, 46, 50, 54, 60, 68, 38, 37, 40, 47, 52, 57, 64, 72, 39, 37, 40, 48, 53, 58, 65, 73, 41, 39, 41, 49, 54, 60, 67, + 76, 44, 41, 43, 51, 57, 63, 71, 79, 44, 41, 43, 51, 57, 63, 71, 79, 47, 44, 45, 53, 59, 66, 75, 84, 48, 45, 46, 54, 60, 67, 76, 85, 50, + 46, 47, 55, 61, 68, 78, 88, 53, 49, 50, 57, 64, 71, 82, 92, 53, 49, 50, 57, 64, 71, 82, 92, 57, 53, 53, 60, 67, 74, 86, 97, 58, 54, 54, + 61, 68, 75, 87, 98, 61, 56, 56, 63, 69, 77, 89, 100, 65, 60, 58, 66, 72, 79, 92, 105, 65, 60, 58, 66, 72, 79, 92, 105, 70, 64, 62, 70, + 76, 83, 96, 109, 32, 31, 31, 31, 31, 31, 31, 32, 32, 32, 32, 33, 34, 34, 36, 36, 38, 39, 41, 44, 44, 47, 48, 50, 53, 53, 57, 58, 61, 65, + 65, 70, 31, 32, 32, 32, 32, 32, 32, 32, 32, 33, 33, 33, 34, 34, 34, 34, 37, 37, 39, 41, 41, 44, 45, 46, 49, 49, 53, 54, 56, 60, 60, 64, + 32, 32, 32, 32, 33, 33, 34, 34, 34, 35, 35, 36, 37, 37, 38, 38, 40, 40, 41, 43, 43, 45, 46, 47, 50, 50, 53, 54, 56, 58, 58, 62, 35, 35, + 35, 34, 34, 34, 35, 36, 36, 37, 37, 40, 41, 43, 46, 46, 47, 48, 49, 51, 51, 53, 54, 55, 57, 57, 60, 61, 63, 66, 66, 70, 39, 38, 38, 37, + 37, 37, 38, 38, 39, 40, 40, 43, 44, 46, 50, 50, 52, 53, 54, 57, 57, 59, 60, 61, 64, 64, 67, 68, 69, 72, 72, 76, 44, 42, 42, 41, 41, 41, + 42, 42, 42, 42, 42, 46, 48, 50, 54, 54, 57, 58, 60, 63, 63, 66, 67, 68, 71, 71, 74, 75, 77, 79, 79, 83, 53, 52, 51, 50, 49, 49, 49, 50, + 49, 49, 49, 53, 54, 56, 60, 60, 64, 65, 67, 71, 71, 75, 76, 78, 82, 82, 86, 87, 89, 92, 92, 96, 65, 63, 62, 61, 59, 59, 59, 59, 58, 58, + 58, 62, 63, 65, 68, 68, 72, 73, 76, 79, 79, 84, 85, 88, 92, 92, 97, 98, 100, 105, 105, 109, + ], + [ + 31, 41, 46, 51, 41, 48, 48, 51, 46, 48, 58, 62, 51, 51, 62, 71, 31, 31, 38, 44, 47, 48, 50, 55, 31, 32, 40, 44, 45, 46, 47, 52, 38, 40, + 47, 47, 46, 46, 47, 50, 44, 44, 47, 50, 51, 51, 52, 54, 47, 45, 46, 51, 54, 56, 57, 60, 48, 46, 46, 51, 56, 61, 63, 66, 50, 47, 47, 52, + 57, 63, 66, 70, 55, 52, 50, 54, 60, 66, 70, 76, 32, 31, 30, 33, 34, 36, 41, 49, 48, 49, 49, 50, 52, 54, 55, 57, 31, 31, 31, 34, 36, 38, + 42, 47, 47, 47, 47, 48, 50, 51, 53, 54, 30, 31, 32, 34, 37, 40, 42, 46, 45, 45, 45, 46, 47, 49, 50, 52, 33, 34, 34, 37, 40, 42, 44, 47, + 46, 46, 45, 46, 47, 49, 50, 51, 34, 36, 37, 40, 42, 45, 46, 47, 46, 46, 45, 46, 47, 48, 49, 50, 36, 38, 40, 42, 45, 47, 47, 48, 47, 46, + 45, 46, 47, 48, 49, 50, 41, 42, 42, 44, 46, 47, 48, 50, 50, 49, 49, 50, 50, 51, 52, 53, 49, 47, 46, 47, 47, 48, 50, 53, 53, 53, 53, 54, + 54, 55, 56, 56, 48, 47, 45, 46, 46, 47, 50, 53, 54, 54, 55, 56, 57, 58, 58, 59, 49, 47, 45, 46, 46, 46, 49, 53, 54, 55, 57, 58, 59, 60, + 60, 61, 49, 47, 45, 45, 45, 45, 49, 53, 55, 57, 58, 60, 61, 62, 63, 63, 50, 48, 46, 46, 46, 46, 50, 54, 56, 58, 60, 61, 63, 65, 66, 67, + 52, 50, 47, 47, 47, 47, 50, 54, 57, 59, 61, 63, 66, 68, 69, 70, 54, 51, 49, 49, 48, 48, 51, 55, 58, 60, 62, 65, 68, 70, 71, 73, 55, 53, + 50, 50, 49, 49, 52, 56, 58, 60, 63, 66, 69, 71, 73, 74, 57, 54, 52, 51, 50, 50, 53, 56, 59, 61, 63, 67, 70, 73, 74, 76, 32, 31, 31, 31, + 30, 30, 33, 33, 34, 36, 36, 40, 41, 44, 49, 49, 48, 48, 49, 49, 49, 50, 50, 51, 52, 52, 54, 54, 55, 57, 57, 59, 31, 31, 31, 31, 31, 31, + 33, 34, 36, 38, 38, 41, 42, 44, 48, 48, 47, 47, 47, 47, 47, 48, 49, 49, 50, 50, 52, 52, 53, 55, 55, 57, 31, 31, 31, 31, 31, 31, 34, 34, + 36, 38, 38, 41, 42, 44, 47, 47, 47, 47, 47, 47, 47, 48, 48, 49, 50, 50, 51, 52, 53, 54, 54, 56, 31, 31, 31, 31, 31, 31, 34, 35, 36, 39, + 39, 41, 42, 44, 47, 47, 46, 46, 46, 46, 46, 47, 47, 48, 49, 49, 50, 51, 52, 53, 53, 55, 30, 31, 31, 31, 32, 32, 34, 35, 37, 40, 40, 42, + 42, 44, 46, 46, 45, 45, 45, 45, 45, 46, 46, 47, 47, 47, 49, 49, 50, 52, 52, 54, 30, 31, 31, 31, 32, 32, 34, 35, 37, 40, 40, 42, 42, 44, + 46, 46, 45, 45, 45, 45, 45, 46, 46, 47, 47, 47, 49, 49, 50, 52, 52, 54, 33, 33, 34, 34, 34, 34, 37, 38, 40, 42, 42, 44, 44, 45, 47, 47, + 46, 46, 46, 45, 45, 46, 46, 47, 47, 47, 49, 49, 50, 51, 51, 53, 33, 34, 34, 35, 35, 35, 38, 39, 40, 43, 43, 44, 45, 46, 47, 47, 46, 46, + 46, 45, 45, 46, 46, 47, 47, 47, 49, 49, 50, 51, 51, 53, 34, 36, 36, 36, 37, 37, 40, 40, 42, 45, 45, 45, 46, 46, 47, 47, 46, 46, 46, 45, + 45, 46, 46, 47, 47, 47, 48, 49, 49, 50, 50, 52, 36, 38, 38, 39, 40, 40, 42, 43, 45, 47, 47, 47, 47, 47, 48, 48, 47, 46, 46, 45, 45, 46, + 46, 46, 47, 47, 48, 48, 49, 50, 50, 51, 36, 38, 38, 39, 40, 40, 42, 43, 45, 47, 47, 47, 47, 47, 48, 48, 47, 46, 46, 45, 45, 46, 46, 46, + 47, 47, 48, 48, 49, 50, 50, 51, 40, 41, 41, 41, 42, 42, 44, 44, 45, 47, 47, 48, 48, 49, 50, 50, 49, 49, 49, 48, 48, 49, 49, 49, 49, 49, + 51, 51, 51, 52, 52, 54, 41, 42, 42, 42, 42, 42, 44, 45, 46, 47, 47, 48, 48, 49, 50, 50, 50, 49, 49, 49, 49, 50, 50, 50, 50, 50, 51, 52, + 52, 53, 53, 55, 44, 44, 44, 44, 44, 44, 45, 46, 46, 47, 47, 49, 49, 50, 51, 51, 51, 51, 51, 51, 51, 51, 51, 51, 52, 52, 53, 53, 54, 54, + 54, 56, 49, 48, 47, 47, 46, 46, 47, 47, 47, 48, 48, 50, 50, 51, 53, 53, 53, 53, 53, 53, 53, 54, 54, 54, 54, 54, 55, 55, 56, 56, 56, 58, + 49, 48, 47, 47, 46, 46, 47, 47, 47, 48, 48, 50, 50, 51, 53, 53, 53, 53, 53, 53, 53, 54, 54, 54, 54, 54, 55, 55, 56, 56, 56, 58, 48, 47, + 47, 46, 45, 45, 46, 46, 46, 47, 47, 49, 50, 51, 53, 53, 54, 54, 54, 55, 55, 56, 56, 56, 57, 57, 58, 58, 58, 59, 59, 60, 48, 47, 47, 46, + 45, 45, 46, 46, 46, 46, 46, 49, 49, 51, 53, 53, 54, 54, 55, 55, 55, 56, 56, 57, 57, 57, 58, 58, 59, 60, 60, 61, 49, 47, 47, 46, 45, 45, + 46, 46, 46, 46, 46, 49, 49, 51, 53, 53, 54, 55, 55, 57, 57, 57, 58, 58, 59, 59, 60, 60, 60, 61, 61, 63, 49, 47, 47, 46, 45, 45, 45, 45, + 45, 45, 45, 48, 49, 51, 53, 53, 55, 55, 57, 58, 58, 59, 60, 60, 61, 61, 62, 62, 63, 63, 63, 65, 49, 47, 47, 46, 45, 45, 45, 45, 45, 45, + 45, 48, 49, 51, 53, 53, 55, 55, 57, 58, 58, 59, 60, 60, 61, 61, 62, 62, 63, 63, 63, 65, 50, 48, 48, 47, 46, 46, 46, 46, 46, 46, 46, 49, + 50, 51, 54, 54, 56, 56, 57, 59, 59, 61, 61, 62, 63, 63, 64, 64, 65, 66, 66, 67, 50, 49, 48, 47, 46, 46, 46, 46, 46, 46, 46, 49, 50, 51, + 54, 54, 56, 56, 58, 60, 60, 61, 61, 62, 63, 63, 65, 65, 66, 67, 67, 68, 51, 49, 49, 48, 47, 47, 47, 47, 47, 46, 46, 49, 50, 51, 54, 54, + 56, 57, 58, 60, 60, 62, 62, 63, 65, 65, 66, 66, 67, 68, 68, 70, 52, 50, 50, 49, 47, 47, 47, 47, 47, 47, 47, 49, 50, 52, 54, 54, 57, 57, + 59, 61, 61, 63, 63, 65, 66, 66, 68, 68, 69, 70, 70, 72, 52, 50, 50, 49, 47, 47, 47, 47, 47, 47, 47, 49, 50, 52, 54, 54, 57, 57, 59, 61, + 61, 63, 63, 65, 66, 66, 68, 68, 69, 70, 70, 72, 54, 52, 51, 50, 49, 49, 49, 49, 48, 48, 48, 51, 51, 53, 55, 55, 58, 58, 60, 62, 62, 64, + 65, 66, 68, 68, 70, 70, 71, 73, 73, 74, 54, 52, 52, 51, 49, 49, 49, 49, 49, 48, 48, 51, 52, 53, 55, 55, 58, 58, 60, 62, 62, 64, 65, 66, + 68, 68, 70, 71, 72, 73, 73, 75, 55, 53, 53, 52, 50, 50, 50, 50, 49, 49, 49, 51, 52, 54, 56, 56, 58, 59, 60, 63, 63, 65, 66, 67, 69, 69, + 71, 72, 73, 74, 74, 76, 57, 55, 54, 53, 52, 52, 51, 51, 50, 50, 50, 52, 53, 54, 56, 56, 59, 60, 61, 63, 63, 66, 67, 68, 70, 70, 73, 73, + 74, 76, 76, 78, 57, 55, 54, 53, 52, 52, 51, 51, 50, 50, 50, 52, 53, 54, 56, 56, 59, 60, 61, 63, 63, 66, 67, 68, 70, 70, 73, 73, 74, 76, + 76, 78, 59, 57, 56, 55, 54, 54, 53, 53, 52, 51, 51, 54, 55, 56, 58, 58, 60, 61, 63, 65, 65, 67, 68, 70, 72, 72, 74, 75, 76, 78, 78, 80, + 31, 38, 47, 52, 32, 40, 45, 49, 39, 47, 45, 48, 44, 47, 51, 53, 46, 47, 56, 58, 47, 46, 59, 64, 48, 47, 61, 68, 53, 50, 64, 73, 31, 32, + 39, 44, 46, 47, 48, 53, 38, 40, 47, 47, 47, 46, 47, 50, 47, 45, 45, 51, 56, 59, 61, 64, 52, 49, 48, 53, 58, 64, 68, 73, 32, 31, 37, 45, + 48, 49, 52, 57, 31, 31, 38, 45, 47, 47, 50, 54, 30, 32, 40, 44, 45, 45, 48, 52, 33, 35, 42, 46, 46, 45, 47, 51, 35, 37, 44, 46, 46, 45, + 47, 51, 37, 40, 47, 47, 47, 45, 47, 50, 42, 43, 47, 49, 50, 49, 50, 53, 49, 46, 48, 52, 53, 53, 54, 57, 48, 46, 47, 51, 54, 55, 57, 59, + 48, 45, 46, 51, 54, 57, 59, 61, 49, 45, 46, 51, 55, 58, 61, 64, 50, 46, 46, 52, 56, 59, 64, 67, 52, 48, 47, 53, 57, 61, 66, 71, 54, 49, + 48, 54, 58, 62, 68, 73, 55, 51, 49, 54, 58, 63, 69, 74, 57, 52, 50, 55, 59, 64, 70, 76, 32, 31, 30, 33, 35, 37, 42, 49, 48, 48, 49, 50, + 52, 54, 55, 57, 31, 31, 32, 35, 37, 40, 43, 46, 46, 45, 45, 46, 48, 49, 51, 52, 37, 38, 40, 42, 44, 47, 47, 48, 47, 46, 46, 46, 47, 48, + 49, 50, 45, 45, 44, 46, 46, 47, 49, 52, 51, 51, 51, 52, 53, 54, 54, 55, 48, 47, 45, 46, 46, 47, 50, 53, 54, 54, 55, 56, 57, 58, 58, 59, + 49, 47, 45, 45, 45, 45, 49, 53, 55, 57, 58, 59, 61, 62, 63, 64, 52, 50, 48, 47, 47, 47, 50, 54, 57, 59, 61, 64, 66, 68, 69, 70, 57, 54, + 52, 51, 51, 50, 53, 57, 59, 61, 64, 67, 71, 73, 74, 76, 32, 31, 31, 33, 37, 37, 45, 48, 48, 49, 49, 51, 52, 54, 57, 57, 31, 31, 31, 34, + 38, 38, 45, 47, 47, 47, 47, 50, 50, 52, 55, 55, 31, 31, 31, 34, 38, 38, 45, 47, 47, 47, 47, 49, 50, 51, 54, 54, 31, 31, 32, 34, 39, 39, + 45, 46, 46, 46, 46, 48, 49, 51, 53, 53, 30, 32, 32, 35, 40, 40, 44, 46, 45, 45, 45, 47, 48, 49, 52, 52, 30, 32, 32, 35, 40, 40, 44, 46, + 45, 45, 45, 47, 48, 49, 52, 52, 33, 34, 35, 37, 42, 42, 46, 47, 46, 45, 45, 47, 47, 49, 51, 51, 33, 35, 36, 38, 43, 43, 46, 47, 46, 46, + 46, 47, 47, 49, 51, 51, 35, 37, 37, 40, 44, 44, 46, 47, 46, 45, 45, 47, 47, 48, 51, 51, 37, 39, 40, 43, 47, 47, 47, 47, 47, 45, 45, 46, + 47, 48, 50, 50, 37, 39, 40, 43, 47, 47, 47, 47, 47, 45, 45, 46, 47, 48, 50, 50, 41, 42, 42, 44, 47, 47, 49, 49, 49, 48, 48, 49, 50, 51, + 52, 52, 42, 42, 43, 44, 47, 47, 49, 50, 50, 49, 49, 50, 50, 51, 53, 53, 44, 44, 44, 45, 47, 47, 50, 51, 51, 51, 51, 52, 52, 53, 54, 54, + 49, 47, 46, 47, 48, 48, 52, 53, 53, 53, 53, 54, 54, 55, 57, 57, 49, 47, 46, 47, 48, 48, 52, 53, 53, 53, 53, 54, 54, 55, 57, 57, 48, 46, + 46, 46, 47, 47, 51, 53, 54, 55, 55, 56, 57, 58, 59, 59, 48, 46, 46, 46, 47, 47, 51, 53, 54, 56, 56, 57, 57, 58, 60, 60, 48, 46, 45, 46, + 46, 46, 51, 53, 54, 57, 57, 58, 59, 60, 61, 61, 49, 46, 45, 45, 46, 46, 51, 53, 55, 58, 58, 61, 61, 62, 64, 64, 49, 46, 45, 45, 46, 46, + 51, 53, 55, 58, 58, 61, 61, 62, 64, 64, 50, 47, 46, 46, 46, 46, 52, 54, 56, 59, 59, 62, 63, 64, 66, 66, 50, 47, 46, 46, 46, 46, 52, 54, + 56, 59, 59, 63, 64, 65, 67, 67, 51, 48, 47, 47, 47, 47, 52, 54, 56, 60, 60, 64, 65, 66, 68, 68, 52, 48, 48, 47, 47, 47, 53, 54, 57, 61, + 61, 65, 66, 68, 71, 71, 52, 48, 48, 47, 47, 47, 53, 54, 57, 61, 61, 65, 66, 68, 71, 71, 54, 50, 49, 49, 48, 48, 54, 55, 58, 62, 62, 67, + 68, 70, 73, 73, 54, 51, 50, 49, 49, 49, 54, 55, 58, 62, 62, 67, 68, 70, 73, 73, 55, 51, 51, 50, 49, 49, 54, 56, 58, 63, 63, 68, 69, 71, + 74, 74, 57, 53, 52, 51, 50, 50, 55, 56, 59, 64, 64, 69, 70, 73, 76, 76, 57, 53, 52, 51, 50, 50, 55, 56, 59, 64, 64, 69, 70, 73, 76, 76, + 59, 55, 54, 53, 52, 52, 57, 58, 61, 65, 65, 70, 72, 74, 78, 78, 32, 31, 31, 31, 30, 30, 33, 33, 35, 37, 37, 41, 42, 44, 49, 49, 48, 48, + 48, 49, 49, 50, 50, 51, 52, 52, 54, 54, 55, 57, 57, 59, 31, 31, 31, 31, 32, 32, 34, 35, 37, 39, 39, 42, 42, 44, 47, 47, 46, 46, 46, 46, + 46, 47, 47, 48, 48, 48, 50, 51, 51, 53, 53, 55, 31, 31, 31, 32, 32, 32, 35, 36, 37, 40, 40, 42, 43, 44, 46, 46, 46, 46, 45, 45, 45, 46, + 46, 47, 48, 48, 49, 50, 51, 52, 52, 54, 33, 34, 34, 34, 35, 35, 37, 38, 40, 43, 43, 44, 44, 45, 47, 47, 46, 46, 46, 45, 45, 46, 46, 47, + 47, 47, 49, 49, 50, 51, 51, 53, 37, 38, 38, 39, 40, 40, 42, 43, 44, 47, 47, 47, 47, 47, 48, 48, 47, 47, 46, 46, 46, 46, 46, 47, 47, 47, + 48, 49, 49, 50, 50, 52, 37, 38, 38, 39, 40, 40, 42, 43, 44, 47, 47, 47, 47, 47, 48, 48, 47, 47, 46, 46, 46, 46, 46, 47, 47, 47, 48, 49, + 49, 50, 50, 52, 45, 45, 45, 45, 44, 44, 46, 46, 46, 47, 47, 49, 49, 50, 52, 52, 51, 51, 51, 51, 51, 52, 52, 52, 53, 53, 54, 54, 54, 55, + 55, 57, 48, 47, 47, 46, 46, 46, 47, 47, 47, 47, 47, 49, 50, 51, 53, 53, 53, 53, 53, 53, 53, 54, 54, 54, 54, 54, 55, 55, 56, 56, 56, 58, + 48, 47, 47, 46, 45, 45, 46, 46, 46, 47, 47, 49, 50, 51, 53, 53, 54, 54, 54, 55, 55, 56, 56, 56, 57, 57, 58, 58, 58, 59, 59, 61, 49, 47, + 47, 46, 45, 45, 45, 46, 45, 45, 45, 48, 49, 51, 53, 53, 55, 56, 57, 58, 58, 59, 59, 60, 61, 61, 62, 62, 63, 64, 64, 65, 49, 47, 47, 46, + 45, 45, 45, 46, 45, 45, 45, 48, 49, 51, 53, 53, 55, 56, 57, 58, 58, 59, 59, 60, 61, 61, 62, 62, 63, 64, 64, 65, 51, 50, 49, 48, 47, 47, + 47, 47, 47, 46, 46, 49, 50, 52, 54, 54, 56, 57, 58, 61, 61, 62, 63, 64, 65, 65, 67, 67, 68, 69, 69, 70, 52, 50, 50, 49, 48, 48, 47, 47, + 47, 47, 47, 50, 50, 52, 54, 54, 57, 57, 59, 61, 61, 63, 64, 65, 66, 66, 68, 68, 69, 70, 70, 72, 54, 52, 51, 51, 49, 49, 49, 49, 48, 48, + 48, 51, 51, 53, 55, 55, 58, 58, 60, 62, 62, 64, 65, 66, 68, 68, 70, 70, 71, 73, 73, 74, 57, 55, 54, 53, 52, 52, 51, 51, 51, 50, 50, 52, + 53, 54, 57, 57, 59, 60, 61, 64, 64, 66, 67, 68, 71, 71, 73, 73, 74, 76, 76, 78, 57, 55, 54, 53, 52, 52, 51, 51, 51, 50, 50, 52, 53, 54, + 57, 57, 59, 60, 61, 64, 64, 66, 67, 68, 71, 71, 73, 73, 74, 76, 76, 78, 31, 37, 49, 54, 31, 38, 47, 51, 32, 40, 45, 49, 34, 42, 45, 49, + 37, 44, 45, 48, 39, 47, 45, 48, 42, 47, 49, 51, 47, 48, 53, 55, 46, 47, 55, 58, 46, 46, 57, 60, 46, 46, 58, 62, 47, 46, 59, 65, 48, 47, + 61, 68, 50, 48, 62, 70, 51, 49, 63, 71, 53, 50, 64, 73, 31, 31, 32, 34, 37, 39, 42, 47, 46, 46, 46, 47, 48, 50, 51, 53, 37, 38, 40, 42, + 44, 47, 47, 48, 47, 46, 46, 46, 47, 48, 49, 50, 49, 47, 45, 45, 45, 45, 49, 53, 55, 57, 58, 59, 61, 62, 63, 64, 54, 51, 49, 49, 48, 48, + 51, 55, 58, 60, 62, 65, 68, 70, 71, 73, 32, 31, 37, 45, 48, 49, 52, 57, 31, 31, 38, 45, 47, 47, 50, 55, 31, 31, 38, 45, 47, 47, 50, 54, + 31, 32, 39, 45, 46, 46, 49, 53, 30, 32, 40, 44, 45, 45, 48, 52, 30, 32, 40, 44, 45, 45, 48, 52, 33, 35, 42, 46, 46, 45, 47, 51, 33, 36, + 43, 46, 46, 46, 47, 51, 35, 37, 44, 46, 46, 45, 47, 51, 37, 40, 47, 47, 47, 45, 47, 50, 37, 40, 47, 47, 47, 45, 47, 50, 41, 42, 47, 49, + 49, 48, 50, 52, 42, 43, 47, 49, 50, 49, 50, 53, 44, 44, 47, 50, 51, 51, 52, 54, 49, 46, 48, 52, 53, 53, 54, 57, 49, 46, 48, 52, 53, 53, + 54, 57, 48, 46, 47, 51, 54, 55, 57, 59, 48, 46, 47, 51, 54, 56, 57, 60, 48, 45, 46, 51, 54, 57, 59, 61, 49, 45, 46, 51, 55, 58, 61, 64, + 49, 45, 46, 51, 55, 58, 61, 64, 50, 46, 46, 52, 56, 59, 63, 66, 50, 46, 46, 52, 56, 59, 64, 67, 51, 47, 47, 52, 56, 60, 65, 68, 52, 48, + 47, 53, 57, 61, 66, 71, 52, 48, 47, 53, 57, 61, 66, 71, 54, 49, 48, 54, 58, 62, 68, 73, 54, 50, 49, 54, 58, 62, 68, 73, 55, 51, 49, 54, + 58, 63, 69, 74, 57, 52, 50, 55, 59, 64, 70, 76, 57, 52, 50, 55, 59, 64, 70, 76, 59, 54, 52, 57, 61, 65, 72, 78, 32, 31, 31, 31, 30, 30, + 33, 33, 35, 37, 37, 41, 42, 44, 49, 49, 48, 48, 48, 49, 49, 50, 50, 51, 52, 52, 54, 54, 55, 57, 57, 59, 31, 31, 31, 32, 32, 32, 35, 36, + 37, 40, 40, 42, 43, 44, 46, 46, 46, 46, 45, 45, 45, 46, 46, 47, 48, 48, 49, 50, 51, 52, 52, 54, 37, 38, 38, 39, 40, 40, 42, 43, 44, 47, + 47, 47, 47, 47, 48, 48, 47, 47, 46, 46, 46, 46, 46, 47, 47, 47, 48, 49, 49, 50, 50, 52, 45, 45, 45, 45, 44, 44, 46, 46, 46, 47, 47, 49, + 49, 50, 52, 52, 51, 51, 51, 51, 51, 52, 52, 52, 53, 53, 54, 54, 54, 55, 55, 57, 48, 47, 47, 46, 45, 45, 46, 46, 46, 47, 47, 49, 50, 51, + 53, 53, 54, 54, 54, 55, 55, 56, 56, 56, 57, 57, 58, 58, 58, 59, 59, 61, 49, 47, 47, 46, 45, 45, 45, 46, 45, 45, 45, 48, 49, 51, 53, 53, + 55, 56, 57, 58, 58, 59, 59, 60, 61, 61, 62, 62, 63, 64, 64, 65, 52, 50, 50, 49, 48, 48, 47, 47, 47, 47, 47, 50, 50, 52, 54, 54, 57, 57, + 59, 61, 61, 63, 64, 65, 66, 66, 68, 68, 69, 70, 70, 72, 57, 55, 54, 53, 52, 52, 51, 51, 51, 50, 50, 52, 53, 54, 57, 57, 59, 60, 61, 64, + 64, 66, 67, 68, 71, 71, 73, 73, 74, 76, 76, 78, + ], + ], + [ + [ + 32, 32, 38, 51, 32, 35, 40, 49, 38, 40, 54, 64, 51, 49, 64, 81, 31, 32, 32, 34, 35, 41, 47, 53, 32, 32, 32, 33, 34, 40, 44, 50, 32, 32, + 34, 35, 37, 41, 45, 51, 34, 33, 35, 39, 42, 47, 51, 55, 35, 34, 37, 42, 48, 53, 57, 61, 41, 40, 41, 47, 53, 60, 65, 70, 47, 44, 45, 51, + 57, 65, 71, 77, 53, 50, 51, 55, 61, 70, 77, 85, 32, 31, 31, 31, 31, 32, 32, 34, 36, 38, 39, 44, 47, 49, 54, 59, 31, 32, 32, 32, 32, 32, + 33, 34, 35, 37, 38, 42, 45, 47, 51, 56, 31, 32, 32, 32, 32, 32, 33, 33, 34, 36, 37, 41, 44, 46, 50, 54, 31, 32, 32, 32, 32, 33, 33, 34, + 35, 36, 38, 41, 44, 45, 49, 54, 31, 32, 32, 32, 33, 34, 34, 35, 36, 38, 39, 42, 45, 46, 50, 54, 32, 32, 32, 33, 34, 35, 36, 37, 38, 39, + 40, 42, 45, 46, 49, 53, 32, 33, 33, 33, 34, 36, 36, 38, 40, 41, 42, 44, 47, 48, 51, 55, 34, 34, 33, 34, 35, 37, 38, 39, 42, 44, 45, 47, + 50, 51, 54, 58, 36, 35, 34, 35, 36, 38, 40, 42, 48, 50, 50, 54, 56, 57, 60, 64, 38, 37, 36, 36, 38, 39, 41, 44, 50, 51, 52, 56, 58, 60, + 63, 67, 39, 38, 37, 38, 39, 40, 42, 45, 50, 52, 54, 58, 60, 62, 65, 69, 44, 42, 41, 41, 42, 42, 44, 47, 54, 56, 58, 63, 66, 68, 71, 75, + 47, 45, 44, 44, 45, 45, 47, 50, 56, 58, 60, 66, 69, 71, 75, 79, 49, 47, 46, 45, 46, 46, 48, 51, 57, 60, 62, 68, 71, 73, 77, 81, 54, 51, + 50, 49, 50, 49, 51, 54, 60, 63, 65, 71, 75, 77, 82, 87, 59, 56, 54, 54, 54, 53, 55, 58, 64, 67, 69, 75, 79, 81, 87, 92, 32, 31, 31, 31, + 31, 31, 31, 31, 31, 32, 32, 32, 32, 34, 34, 35, 36, 36, 38, 39, 39, 42, 44, 44, 47, 48, 49, 53, 54, 55, 59, 59, 31, 31, 31, 31, 32, 32, + 32, 32, 32, 32, 32, 32, 33, 34, 34, 34, 35, 35, 37, 39, 39, 41, 43, 43, 46, 47, 48, 51, 52, 53, 57, 57, 31, 31, 32, 32, 32, 32, 32, 32, + 32, 32, 32, 32, 33, 34, 34, 34, 35, 35, 37, 38, 38, 41, 42, 43, 45, 46, 47, 51, 51, 53, 56, 56, 31, 31, 32, 32, 32, 32, 32, 32, 32, 32, + 32, 32, 33, 34, 34, 34, 35, 35, 37, 38, 38, 41, 42, 42, 45, 46, 47, 51, 51, 52, 56, 56, 31, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, + 33, 33, 33, 34, 34, 34, 36, 37, 37, 40, 41, 41, 44, 45, 46, 49, 50, 51, 54, 54, 31, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 33, 33, + 33, 34, 34, 34, 36, 37, 37, 40, 41, 41, 44, 44, 45, 49, 49, 50, 54, 54, 31, 32, 32, 32, 32, 32, 32, 32, 32, 33, 33, 33, 33, 34, 34, 34, + 35, 35, 36, 38, 38, 40, 41, 41, 44, 45, 45, 49, 49, 50, 54, 54, 31, 32, 32, 32, 32, 32, 32, 33, 33, 33, 34, 34, 34, 35, 35, 35, 36, 36, + 38, 39, 39, 41, 42, 42, 44, 45, 46, 49, 50, 51, 54, 54, 31, 32, 32, 32, 32, 32, 32, 33, 33, 33, 34, 34, 34, 35, 35, 36, 36, 36, 38, 39, + 39, 41, 42, 42, 45, 45, 46, 49, 50, 51, 54, 54, 32, 32, 32, 32, 32, 32, 33, 33, 33, 34, 34, 34, 35, 35, 35, 36, 37, 37, 38, 39, 39, 41, + 42, 42, 45, 45, 46, 49, 49, 51, 54, 54, 32, 32, 32, 32, 32, 32, 33, 34, 34, 34, 35, 35, 36, 37, 37, 37, 38, 38, 39, 40, 40, 42, 42, 43, + 45, 46, 46, 49, 49, 50, 53, 53, 32, 32, 32, 32, 32, 32, 33, 34, 34, 34, 35, 35, 36, 37, 37, 37, 38, 38, 39, 40, 40, 42, 42, 43, 45, 46, + 46, 49, 49, 50, 53, 53, 32, 33, 33, 33, 33, 33, 33, 34, 34, 35, 36, 36, 36, 38, 38, 39, 40, 40, 41, 42, 42, 44, 44, 45, 47, 47, 48, 51, + 51, 52, 55, 55, 34, 34, 34, 34, 33, 33, 34, 35, 35, 35, 37, 37, 38, 39, 39, 41, 42, 42, 44, 45, 45, 47, 47, 48, 50, 51, 51, 54, 54, 55, + 58, 58, 34, 34, 34, 34, 33, 33, 34, 35, 35, 35, 37, 37, 38, 39, 39, 41, 42, 42, 44, 45, 45, 47, 47, 48, 50, 51, 51, 54, 54, 55, 58, 58, + 35, 34, 34, 34, 34, 34, 34, 35, 36, 36, 37, 37, 39, 41, 41, 43, 45, 45, 47, 47, 47, 49, 50, 51, 53, 53, 54, 57, 57, 58, 61, 61, 36, 35, + 35, 35, 34, 34, 35, 36, 36, 37, 38, 38, 40, 42, 42, 45, 48, 48, 50, 50, 50, 53, 54, 54, 56, 57, 57, 59, 60, 61, 64, 64, 36, 35, 35, 35, + 34, 34, 35, 36, 36, 37, 38, 38, 40, 42, 42, 45, 48, 48, 50, 50, 50, 53, 54, 54, 56, 57, 57, 59, 60, 61, 64, 64, 38, 37, 37, 37, 36, 36, + 36, 38, 38, 38, 39, 39, 41, 44, 44, 47, 50, 50, 51, 52, 52, 55, 56, 56, 58, 59, 60, 62, 63, 64, 67, 67, 39, 39, 38, 38, 37, 37, 38, 39, + 39, 39, 40, 40, 42, 45, 45, 47, 50, 50, 52, 54, 54, 56, 58, 58, 60, 61, 62, 64, 65, 66, 69, 69, 39, 39, 38, 38, 37, 37, 38, 39, 39, 39, + 40, 40, 42, 45, 45, 47, 50, 50, 52, 54, 54, 56, 58, 58, 60, 61, 62, 64, 65, 66, 69, 69, 42, 41, 41, 41, 40, 40, 40, 41, 41, 41, 42, 42, + 44, 47, 47, 49, 53, 53, 55, 56, 56, 60, 61, 62, 64, 65, 66, 69, 69, 70, 73, 73, 44, 43, 42, 42, 41, 41, 41, 42, 42, 42, 42, 42, 44, 47, + 47, 50, 54, 54, 56, 58, 58, 61, 63, 64, 66, 67, 68, 71, 71, 72, 75, 75, 44, 43, 43, 42, 41, 41, 41, 42, 42, 42, 43, 43, 45, 48, 48, 51, + 54, 54, 56, 58, 58, 62, 64, 64, 66, 67, 68, 71, 72, 73, 76, 76, 47, 46, 45, 45, 44, 44, 44, 44, 45, 45, 45, 45, 47, 50, 50, 53, 56, 56, + 58, 60, 60, 64, 66, 66, 69, 70, 71, 74, 75, 76, 79, 79, 48, 47, 46, 46, 45, 44, 45, 45, 45, 45, 46, 46, 47, 51, 51, 53, 57, 57, 59, 61, + 61, 65, 67, 67, 70, 71, 72, 75, 76, 77, 80, 80, 49, 48, 47, 47, 46, 45, 45, 46, 46, 46, 46, 46, 48, 51, 51, 54, 57, 57, 60, 62, 62, 66, + 68, 68, 71, 72, 73, 77, 77, 78, 81, 81, 53, 51, 51, 51, 49, 49, 49, 49, 49, 49, 49, 49, 51, 54, 54, 57, 59, 59, 62, 64, 64, 69, 71, 71, + 74, 75, 77, 81, 81, 83, 86, 86, 54, 52, 51, 51, 50, 49, 49, 50, 50, 49, 49, 49, 51, 54, 54, 57, 60, 60, 63, 65, 65, 69, 71, 72, 75, 76, + 77, 81, 82, 83, 87, 87, 55, 53, 53, 52, 51, 50, 50, 51, 51, 51, 50, 50, 52, 55, 55, 58, 61, 61, 64, 66, 66, 70, 72, 73, 76, 77, 78, 83, + 83, 85, 88, 88, 59, 57, 56, 56, 54, 54, 54, 54, 54, 54, 53, 53, 55, 58, 58, 61, 64, 64, 67, 69, 69, 73, 75, 76, 79, 80, 81, 86, 87, 88, + 92, 92, 59, 57, 56, 56, 54, 54, 54, 54, 54, 54, 53, 53, 55, 58, 58, 61, 64, 64, 67, 69, 69, 73, 75, 76, 79, 80, 81, 86, 87, 88, 92, 92, + 32, 32, 37, 52, 32, 33, 36, 49, 32, 34, 38, 49, 34, 37, 44, 54, 35, 38, 49, 60, 40, 42, 55, 69, 46, 46, 59, 76, 52, 51, 64, 83, 32, 32, + 32, 34, 35, 40, 46, 52, 32, 33, 34, 37, 38, 42, 46, 51, 37, 36, 38, 44, 49, 55, 59, 64, 52, 49, 49, 54, 60, 69, 76, 83, 32, 31, 32, 32, + 36, 44, 47, 53, 31, 32, 32, 33, 35, 42, 45, 51, 31, 32, 32, 33, 35, 41, 44, 49, 31, 32, 33, 33, 35, 41, 44, 49, 32, 32, 34, 34, 36, 42, + 45, 50, 32, 33, 35, 36, 38, 42, 45, 49, 32, 33, 35, 36, 40, 44, 47, 51, 34, 34, 36, 38, 42, 48, 50, 54, 36, 34, 37, 40, 48, 54, 56, 60, + 38, 36, 39, 41, 49, 56, 58, 63, 39, 37, 40, 42, 50, 58, 60, 65, 44, 41, 42, 45, 53, 63, 66, 71, 47, 44, 45, 47, 56, 66, 69, 75, 49, 46, + 47, 48, 57, 67, 71, 77, 53, 49, 50, 51, 60, 71, 75, 82, 58, 54, 54, 55, 63, 75, 79, 87, 32, 31, 31, 31, 32, 32, 32, 34, 36, 38, 39, 44, + 47, 49, 53, 58, 31, 32, 32, 32, 32, 33, 33, 34, 34, 36, 37, 41, 44, 46, 49, 54, 32, 32, 32, 33, 34, 35, 35, 36, 37, 39, 40, 42, 45, 47, + 50, 54, 32, 33, 33, 33, 34, 36, 36, 38, 40, 41, 42, 45, 47, 48, 51, 55, 36, 35, 35, 35, 36, 38, 40, 42, 48, 49, 50, 53, 56, 57, 60, 63, + 44, 42, 41, 41, 42, 42, 44, 48, 54, 56, 58, 63, 66, 67, 71, 75, 47, 45, 44, 44, 45, 45, 47, 50, 56, 58, 60, 66, 69, 71, 75, 79, 53, 51, + 49, 49, 50, 49, 51, 54, 60, 63, 65, 71, 75, 77, 82, 87, 32, 31, 31, 31, 32, 32, 32, 35, 36, 38, 44, 44, 47, 53, 53, 59, 31, 32, 32, 32, + 32, 32, 33, 35, 35, 37, 43, 43, 46, 52, 52, 57, 31, 32, 32, 32, 32, 32, 33, 35, 35, 37, 42, 42, 45, 51, 51, 56, 31, 32, 32, 32, 32, 32, + 33, 35, 35, 37, 42, 42, 45, 51, 51, 56, 31, 32, 32, 32, 32, 32, 33, 34, 35, 36, 41, 41, 44, 49, 49, 54, 31, 32, 32, 32, 32, 33, 33, 34, + 34, 36, 41, 41, 44, 49, 49, 54, 31, 32, 32, 32, 33, 33, 33, 35, 35, 36, 41, 41, 44, 49, 49, 54, 32, 32, 32, 32, 33, 34, 34, 36, 36, 38, + 42, 42, 45, 49, 49, 54, 32, 32, 32, 33, 34, 34, 34, 36, 36, 38, 42, 42, 45, 50, 50, 54, 32, 32, 32, 33, 34, 34, 35, 37, 37, 38, 42, 42, + 45, 49, 49, 54, 32, 32, 33, 33, 35, 35, 36, 38, 38, 39, 42, 42, 45, 49, 49, 53, 32, 32, 33, 33, 35, 35, 36, 38, 38, 39, 42, 42, 45, 49, + 49, 53, 32, 33, 33, 33, 35, 36, 36, 39, 40, 41, 44, 44, 47, 51, 51, 55, 34, 34, 34, 34, 36, 37, 38, 42, 42, 44, 48, 48, 50, 54, 54, 58, + 34, 34, 34, 34, 36, 37, 38, 42, 42, 44, 48, 48, 50, 54, 54, 58, 35, 34, 34, 34, 37, 37, 39, 44, 45, 46, 50, 50, 53, 57, 57, 61, 36, 35, + 34, 35, 37, 38, 40, 47, 48, 49, 54, 54, 56, 60, 60, 64, 36, 35, 34, 35, 37, 38, 40, 47, 48, 49, 54, 54, 56, 60, 60, 64, 38, 37, 36, 37, + 39, 40, 41, 48, 49, 51, 56, 56, 58, 63, 63, 67, 39, 38, 37, 38, 40, 40, 42, 49, 50, 52, 58, 58, 60, 65, 65, 69, 39, 38, 37, 38, 40, 40, + 42, 49, 50, 52, 58, 58, 60, 65, 65, 69, 42, 40, 40, 40, 42, 42, 44, 51, 52, 55, 61, 61, 64, 69, 69, 73, 44, 42, 41, 41, 42, 43, 45, 52, + 53, 56, 63, 63, 66, 71, 71, 75, 44, 42, 41, 41, 43, 43, 45, 52, 54, 56, 63, 63, 66, 72, 72, 76, 47, 45, 44, 44, 45, 45, 47, 54, 56, 58, + 66, 66, 69, 75, 75, 79, 48, 46, 45, 45, 46, 46, 48, 55, 56, 59, 67, 67, 70, 76, 76, 80, 49, 47, 46, 46, 47, 47, 48, 56, 57, 60, 67, 67, + 71, 77, 77, 81, 53, 50, 49, 49, 49, 49, 51, 58, 59, 62, 71, 71, 74, 81, 81, 86, 53, 51, 49, 49, 50, 50, 51, 59, 60, 63, 71, 71, 75, 82, + 82, 87, 55, 52, 51, 51, 51, 51, 53, 60, 61, 64, 72, 72, 76, 83, 83, 88, 58, 55, 54, 54, 54, 54, 55, 62, 63, 67, 75, 75, 79, 87, 87, 92, + 58, 55, 54, 54, 54, 54, 55, 62, 63, 67, 75, 75, 79, 87, 87, 92, 32, 31, 31, 31, 31, 31, 31, 32, 32, 32, 32, 32, 32, 34, 34, 35, 36, 36, + 38, 39, 39, 42, 44, 44, 47, 48, 49, 53, 53, 55, 58, 58, 31, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 33, 34, 34, 34, 35, 35, 37, 38, + 38, 40, 42, 42, 45, 46, 47, 50, 51, 52, 55, 55, 31, 32, 32, 32, 32, 32, 32, 32, 32, 32, 33, 33, 33, 34, 34, 34, 34, 34, 36, 37, 37, 40, + 41, 41, 44, 45, 46, 49, 49, 51, 54, 54, 31, 32, 32, 32, 32, 32, 32, 32, 33, 33, 33, 33, 33, 34, 34, 34, 35, 35, 37, 38, 38, 40, 41, 41, + 44, 45, 46, 49, 49, 51, 54, 54, 32, 32, 32, 32, 32, 32, 33, 33, 34, 34, 35, 35, 35, 36, 36, 37, 37, 37, 39, 40, 40, 42, 42, 43, 45, 46, + 47, 49, 50, 51, 54, 54, 32, 32, 32, 32, 32, 33, 33, 34, 34, 34, 35, 35, 36, 37, 37, 37, 38, 38, 40, 40, 40, 42, 43, 43, 45, 46, 47, 49, + 50, 51, 54, 54, 32, 33, 33, 33, 33, 33, 33, 34, 34, 35, 36, 36, 36, 38, 38, 39, 40, 40, 41, 42, 42, 44, 45, 45, 47, 48, 48, 51, 51, 53, + 55, 55, 35, 35, 35, 35, 34, 34, 35, 36, 36, 37, 38, 38, 39, 42, 42, 44, 47, 47, 48, 49, 49, 51, 52, 52, 54, 55, 56, 58, 59, 60, 62, 62, + 36, 35, 35, 35, 35, 34, 35, 36, 36, 37, 38, 38, 40, 42, 42, 45, 48, 48, 49, 50, 50, 52, 53, 54, 56, 56, 57, 59, 60, 61, 63, 63, 38, 37, + 37, 37, 36, 36, 36, 38, 38, 38, 39, 39, 41, 44, 44, 46, 49, 49, 51, 52, 52, 55, 56, 56, 58, 59, 60, 62, 63, 64, 67, 67, 44, 43, 42, 42, + 41, 41, 41, 42, 42, 42, 42, 42, 44, 48, 48, 50, 54, 54, 56, 58, 58, 61, 63, 63, 66, 67, 67, 71, 71, 72, 75, 75, 44, 43, 42, 42, 41, 41, + 41, 42, 42, 42, 42, 42, 44, 48, 48, 50, 54, 54, 56, 58, 58, 61, 63, 63, 66, 67, 67, 71, 71, 72, 75, 75, 47, 46, 45, 45, 44, 44, 44, 45, + 45, 45, 45, 45, 47, 50, 50, 53, 56, 56, 58, 60, 60, 64, 66, 66, 69, 70, 71, 74, 75, 76, 79, 79, 53, 52, 51, 51, 49, 49, 49, 49, 50, 49, + 49, 49, 51, 54, 54, 57, 60, 60, 63, 65, 65, 69, 71, 72, 75, 76, 77, 81, 82, 83, 87, 87, 53, 52, 51, 51, 49, 49, 49, 49, 50, 49, 49, 49, + 51, 54, 54, 57, 60, 60, 63, 65, 65, 69, 71, 72, 75, 76, 77, 81, 82, 83, 87, 87, 59, 57, 56, 56, 54, 54, 54, 54, 54, 54, 53, 53, 55, 58, + 58, 61, 64, 64, 67, 69, 69, 73, 75, 76, 79, 80, 81, 86, 87, 88, 92, 92, 31, 32, 38, 53, 32, 32, 37, 51, 32, 32, 36, 49, 32, 33, 36, 49, + 32, 34, 38, 50, 32, 35, 39, 49, 33, 36, 41, 51, 34, 37, 44, 54, 35, 38, 49, 60, 37, 40, 51, 63, 38, 40, 52, 65, 42, 43, 56, 71, 45, 45, + 58, 75, 47, 47, 60, 77, 51, 50, 63, 82, 55, 54, 67, 87, 31, 32, 32, 32, 32, 32, 33, 34, 35, 37, 38, 42, 45, 47, 51, 55, 32, 32, 32, 33, + 34, 35, 36, 37, 38, 40, 40, 43, 45, 47, 50, 54, 38, 37, 36, 36, 38, 39, 41, 44, 49, 51, 52, 56, 58, 60, 63, 67, 53, 51, 49, 49, 50, 49, + 51, 54, 60, 63, 65, 71, 75, 77, 82, 87, 32, 31, 32, 32, 36, 44, 47, 53, 31, 32, 32, 33, 35, 43, 46, 52, 31, 32, 32, 33, 35, 42, 45, 51, + 31, 32, 32, 33, 35, 42, 45, 51, 31, 32, 32, 33, 35, 41, 44, 49, 31, 32, 32, 33, 34, 41, 44, 49, 31, 32, 33, 33, 35, 41, 44, 49, 32, 32, + 33, 34, 36, 42, 45, 49, 32, 32, 34, 34, 36, 42, 45, 50, 32, 32, 34, 35, 37, 42, 45, 49, 32, 33, 35, 36, 38, 42, 45, 49, 32, 33, 35, 36, + 38, 42, 45, 49, 32, 33, 35, 36, 40, 44, 47, 51, 34, 34, 36, 38, 42, 48, 50, 54, 34, 34, 36, 38, 42, 48, 50, 54, 35, 34, 37, 39, 45, 50, + 53, 57, 36, 34, 37, 40, 48, 54, 56, 60, 36, 34, 37, 40, 48, 54, 56, 60, 38, 36, 39, 41, 49, 56, 58, 63, 39, 37, 40, 42, 50, 58, 60, 65, + 39, 37, 40, 42, 50, 58, 60, 65, 42, 40, 42, 44, 52, 61, 64, 69, 44, 41, 42, 45, 53, 63, 66, 71, 44, 41, 43, 45, 54, 63, 66, 72, 47, 44, + 45, 47, 56, 66, 69, 75, 48, 45, 46, 48, 56, 67, 70, 76, 49, 46, 47, 48, 57, 67, 71, 77, 53, 49, 49, 51, 59, 71, 74, 81, 53, 49, 50, 51, + 60, 71, 75, 82, 55, 51, 51, 53, 61, 72, 76, 83, 58, 54, 54, 55, 63, 75, 79, 87, 58, 54, 54, 55, 63, 75, 79, 87, 32, 31, 31, 31, 31, 31, + 31, 32, 32, 32, 32, 32, 32, 34, 34, 35, 36, 36, 38, 39, 39, 42, 44, 44, 47, 48, 49, 53, 53, 55, 58, 58, 31, 32, 32, 32, 32, 32, 32, 32, + 32, 32, 33, 33, 33, 34, 34, 34, 34, 34, 36, 37, 37, 40, 41, 41, 44, 45, 46, 49, 49, 51, 54, 54, 32, 32, 32, 32, 32, 32, 33, 33, 34, 34, + 35, 35, 35, 36, 36, 37, 37, 37, 39, 40, 40, 42, 42, 43, 45, 46, 47, 49, 50, 51, 54, 54, 32, 33, 33, 33, 33, 33, 33, 34, 34, 35, 36, 36, + 36, 38, 38, 39, 40, 40, 41, 42, 42, 44, 45, 45, 47, 48, 48, 51, 51, 53, 55, 55, 36, 35, 35, 35, 35, 34, 35, 36, 36, 37, 38, 38, 40, 42, + 42, 45, 48, 48, 49, 50, 50, 52, 53, 54, 56, 56, 57, 59, 60, 61, 63, 63, 44, 43, 42, 42, 41, 41, 41, 42, 42, 42, 42, 42, 44, 48, 48, 50, + 54, 54, 56, 58, 58, 61, 63, 63, 66, 67, 67, 71, 71, 72, 75, 75, 47, 46, 45, 45, 44, 44, 44, 45, 45, 45, 45, 45, 47, 50, 50, 53, 56, 56, + 58, 60, 60, 64, 66, 66, 69, 70, 71, 74, 75, 76, 79, 79, 53, 52, 51, 51, 49, 49, 49, 49, 50, 49, 49, 49, 51, 54, 54, 57, 60, 60, 63, 65, + 65, 69, 71, 72, 75, 76, 77, 81, 82, 83, 87, 87, + ], + [ + 31, 38, 47, 49, 38, 47, 46, 46, 47, 46, 54, 57, 49, 46, 57, 66, 31, 31, 35, 42, 48, 47, 49, 51, 31, 32, 36, 42, 46, 45, 46, 48, 35, 36, + 41, 45, 47, 45, 46, 48, 42, 42, 45, 48, 50, 49, 50, 51, 48, 46, 47, 50, 53, 53, 54, 54, 47, 45, 45, 49, 53, 57, 59, 60, 49, 46, 46, 50, + 54, 59, 61, 64, 51, 48, 48, 51, 54, 60, 64, 68, 32, 31, 30, 31, 33, 36, 38, 41, 49, 49, 48, 49, 50, 51, 52, 54, 31, 31, 31, 32, 34, 38, + 40, 42, 47, 47, 47, 47, 48, 48, 50, 52, 30, 31, 31, 32, 35, 39, 41, 42, 46, 46, 46, 45, 46, 47, 48, 50, 31, 32, 32, 33, 36, 40, 41, 43, + 46, 46, 45, 45, 46, 46, 47, 49, 33, 34, 35, 36, 39, 43, 44, 45, 47, 46, 46, 45, 46, 47, 47, 49, 36, 38, 39, 40, 43, 47, 47, 47, 48, 47, + 46, 45, 46, 46, 47, 48, 38, 40, 41, 41, 44, 47, 47, 48, 49, 48, 48, 47, 47, 47, 48, 49, 41, 42, 42, 43, 45, 47, 48, 48, 50, 50, 49, 49, + 50, 50, 50, 52, 49, 47, 46, 46, 47, 48, 49, 50, 53, 53, 53, 53, 54, 54, 54, 55, 49, 47, 46, 46, 46, 47, 48, 50, 53, 53, 54, 55, 55, 55, + 56, 57, 48, 47, 46, 45, 46, 46, 48, 49, 53, 54, 54, 55, 56, 56, 57, 58, 49, 47, 45, 45, 45, 45, 47, 49, 53, 55, 55, 58, 59, 60, 61, 62, + 50, 48, 46, 46, 46, 46, 47, 50, 54, 55, 56, 59, 61, 61, 63, 64, 51, 48, 47, 46, 47, 46, 47, 50, 54, 55, 56, 60, 61, 62, 64, 66, 52, 50, + 48, 47, 47, 47, 48, 50, 54, 56, 57, 61, 63, 64, 66, 68, 54, 52, 50, 49, 49, 48, 49, 52, 55, 57, 58, 62, 64, 66, 68, 71, 32, 31, 31, 31, + 30, 30, 31, 33, 33, 34, 36, 36, 38, 41, 41, 45, 49, 49, 49, 48, 48, 49, 49, 49, 50, 50, 51, 52, 52, 53, 54, 54, 31, 31, 31, 31, 31, 31, + 31, 34, 34, 35, 38, 38, 39, 42, 42, 45, 48, 48, 47, 47, 47, 47, 47, 47, 49, 49, 49, 50, 50, 51, 53, 53, 31, 31, 31, 31, 31, 31, 32, 34, + 34, 35, 38, 38, 40, 42, 42, 45, 47, 47, 47, 47, 47, 47, 47, 47, 48, 48, 48, 49, 50, 50, 52, 52, 31, 31, 31, 31, 31, 31, 32, 34, 34, 36, + 38, 38, 40, 42, 42, 45, 47, 47, 47, 47, 47, 47, 46, 47, 48, 48, 48, 49, 49, 50, 52, 52, 30, 31, 31, 31, 31, 31, 32, 35, 35, 36, 39, 39, + 41, 42, 42, 44, 46, 46, 46, 46, 46, 45, 45, 45, 46, 47, 47, 48, 48, 48, 50, 50, 30, 31, 31, 31, 31, 32, 32, 35, 35, 36, 40, 40, 41, 42, + 42, 44, 46, 46, 46, 45, 45, 45, 45, 45, 46, 46, 46, 47, 47, 48, 49, 49, 31, 31, 32, 32, 32, 32, 33, 35, 36, 37, 40, 40, 41, 43, 43, 44, + 46, 46, 46, 45, 45, 45, 45, 45, 46, 46, 46, 47, 47, 48, 49, 49, 33, 34, 34, 34, 35, 35, 35, 38, 38, 40, 43, 43, 43, 44, 44, 46, 47, 47, + 46, 46, 46, 45, 45, 45, 46, 46, 47, 47, 47, 48, 49, 49, 33, 34, 34, 34, 35, 35, 36, 38, 39, 40, 43, 43, 44, 45, 45, 46, 47, 47, 46, 46, + 46, 45, 45, 45, 46, 46, 47, 47, 47, 48, 49, 49, 34, 35, 35, 36, 36, 36, 37, 40, 40, 41, 44, 44, 45, 45, 45, 46, 47, 47, 47, 46, 46, 45, + 45, 45, 46, 46, 46, 47, 47, 48, 49, 49, 36, 38, 38, 38, 39, 40, 40, 43, 43, 44, 47, 47, 47, 47, 47, 47, 48, 48, 47, 46, 46, 45, 45, 45, + 46, 46, 46, 46, 47, 47, 48, 48, 36, 38, 38, 38, 39, 40, 40, 43, 43, 44, 47, 47, 47, 47, 47, 47, 48, 48, 47, 46, 46, 45, 45, 45, 46, 46, + 46, 46, 47, 47, 48, 48, 38, 39, 40, 40, 41, 41, 41, 43, 44, 45, 47, 47, 47, 48, 48, 48, 49, 49, 48, 48, 48, 47, 47, 47, 47, 47, 47, 48, + 48, 48, 49, 49, 41, 42, 42, 42, 42, 42, 43, 44, 45, 45, 47, 47, 48, 48, 48, 49, 50, 50, 50, 49, 49, 49, 49, 49, 50, 50, 50, 50, 50, 51, + 52, 52, 41, 42, 42, 42, 42, 42, 43, 44, 45, 45, 47, 47, 48, 48, 48, 49, 50, 50, 50, 49, 49, 49, 49, 49, 50, 50, 50, 50, 50, 51, 52, 52, + 45, 45, 45, 45, 44, 44, 44, 46, 46, 46, 47, 47, 48, 49, 49, 50, 51, 51, 51, 51, 51, 51, 51, 51, 52, 52, 52, 52, 52, 52, 53, 53, 49, 48, + 47, 47, 46, 46, 46, 47, 47, 47, 48, 48, 49, 50, 50, 51, 53, 53, 53, 53, 53, 53, 53, 53, 54, 54, 54, 54, 54, 54, 55, 55, 49, 48, 47, 47, + 46, 46, 46, 47, 47, 47, 48, 48, 49, 50, 50, 51, 53, 53, 53, 53, 53, 53, 53, 53, 54, 54, 54, 54, 54, 54, 55, 55, 49, 47, 47, 47, 46, 46, + 46, 46, 46, 47, 47, 47, 48, 50, 50, 51, 53, 53, 53, 54, 54, 54, 55, 55, 55, 55, 55, 56, 56, 56, 57, 57, 48, 47, 47, 47, 46, 45, 45, 46, + 46, 46, 46, 46, 48, 49, 49, 51, 53, 53, 54, 54, 54, 55, 55, 56, 56, 56, 56, 57, 57, 58, 58, 58, 48, 47, 47, 47, 46, 45, 45, 46, 46, 46, + 46, 46, 48, 49, 49, 51, 53, 53, 54, 54, 54, 55, 55, 56, 56, 56, 56, 57, 57, 58, 58, 58, 49, 47, 47, 47, 45, 45, 45, 45, 45, 45, 45, 45, + 47, 49, 49, 51, 53, 53, 54, 55, 55, 57, 57, 58, 58, 59, 59, 60, 60, 60, 61, 61, 49, 47, 47, 46, 45, 45, 45, 45, 45, 45, 45, 45, 47, 49, + 49, 51, 53, 53, 55, 55, 55, 57, 58, 58, 59, 60, 60, 61, 61, 61, 62, 62, 49, 47, 47, 47, 45, 45, 45, 45, 45, 45, 45, 45, 47, 49, 49, 51, + 53, 53, 55, 56, 56, 58, 58, 59, 59, 60, 60, 61, 61, 62, 63, 63, 50, 49, 48, 48, 46, 46, 46, 46, 46, 46, 46, 46, 47, 50, 50, 52, 54, 54, + 55, 56, 56, 58, 59, 59, 61, 61, 61, 63, 63, 63, 64, 64, 50, 49, 48, 48, 47, 46, 46, 46, 46, 46, 46, 46, 47, 50, 50, 52, 54, 54, 55, 56, + 56, 59, 60, 60, 61, 61, 62, 63, 63, 64, 65, 65, 51, 49, 48, 48, 47, 46, 46, 47, 47, 46, 46, 46, 47, 50, 50, 52, 54, 54, 55, 56, 56, 59, + 60, 60, 61, 62, 62, 64, 64, 64, 66, 66, 52, 50, 49, 49, 48, 47, 47, 47, 47, 47, 46, 46, 48, 50, 50, 52, 54, 54, 56, 57, 57, 60, 61, 61, + 63, 63, 64, 66, 66, 67, 68, 68, 52, 50, 50, 49, 48, 47, 47, 47, 47, 47, 47, 47, 48, 50, 50, 52, 54, 54, 56, 57, 57, 60, 61, 61, 63, 63, + 64, 66, 66, 67, 68, 68, 53, 51, 50, 50, 48, 48, 48, 48, 48, 48, 47, 47, 48, 51, 51, 52, 54, 54, 56, 58, 58, 60, 61, 62, 63, 64, 64, 67, + 67, 68, 69, 69, 54, 53, 52, 52, 50, 49, 49, 49, 49, 49, 48, 48, 49, 52, 52, 53, 55, 55, 57, 58, 58, 61, 62, 63, 64, 65, 66, 68, 68, 69, + 71, 71, 54, 53, 52, 52, 50, 49, 49, 49, 49, 49, 48, 48, 49, 52, 52, 53, 55, 55, 57, 58, 58, 61, 62, 63, 64, 65, 66, 68, 68, 69, 71, 71, + 31, 38, 47, 50, 31, 40, 46, 48, 36, 44, 47, 47, 42, 47, 50, 50, 47, 48, 53, 54, 46, 46, 54, 60, 48, 46, 55, 64, 50, 48, 56, 67, 31, 31, + 36, 42, 47, 46, 48, 50, 38, 40, 44, 47, 48, 46, 46, 48, 47, 46, 47, 50, 53, 54, 55, 56, 50, 48, 47, 50, 54, 60, 64, 67, 32, 31, 35, 38, + 48, 49, 50, 52, 31, 31, 37, 40, 47, 47, 48, 50, 30, 32, 38, 40, 46, 45, 46, 48, 31, 33, 38, 41, 46, 45, 46, 48, 33, 36, 41, 44, 47, 46, + 46, 47, 37, 40, 45, 47, 47, 45, 46, 47, 39, 41, 46, 47, 48, 47, 47, 48, 42, 43, 46, 48, 50, 49, 50, 50, 49, 46, 48, 49, 53, 53, 54, 54, + 48, 46, 47, 48, 53, 55, 55, 56, 48, 46, 46, 48, 53, 56, 56, 57, 49, 45, 45, 47, 53, 58, 59, 61, 50, 46, 46, 48, 54, 59, 61, 63, 51, 47, + 47, 48, 54, 60, 61, 64, 52, 48, 47, 48, 54, 61, 63, 66, 54, 50, 49, 50, 55, 62, 65, 68, 32, 31, 30, 31, 33, 37, 39, 42, 49, 48, 48, 49, + 50, 51, 52, 54, 31, 31, 32, 33, 36, 40, 41, 43, 46, 46, 46, 45, 46, 47, 48, 50, 35, 37, 38, 38, 41, 45, 46, 46, 48, 47, 46, 45, 46, 47, + 47, 49, 38, 40, 40, 41, 44, 47, 47, 48, 49, 48, 48, 47, 48, 48, 48, 50, 48, 47, 46, 46, 47, 47, 48, 50, 53, 53, 53, 53, 54, 54, 54, 55, + 49, 47, 45, 45, 46, 45, 47, 49, 53, 55, 56, 58, 59, 60, 61, 62, 50, 48, 46, 46, 46, 46, 47, 50, 54, 55, 56, 59, 61, 61, 63, 65, 52, 50, + 48, 48, 47, 47, 48, 50, 54, 56, 57, 61, 63, 64, 66, 68, 32, 31, 31, 31, 35, 37, 38, 47, 48, 48, 49, 49, 50, 52, 52, 54, 31, 31, 31, 32, + 36, 38, 39, 46, 47, 47, 48, 48, 49, 50, 50, 53, 31, 31, 31, 32, 37, 38, 40, 46, 47, 47, 47, 47, 48, 50, 50, 52, 31, 31, 31, 32, 37, 38, + 40, 46, 47, 47, 47, 47, 48, 50, 50, 52, 30, 31, 32, 32, 38, 39, 40, 45, 46, 46, 45, 45, 46, 48, 48, 50, 30, 31, 32, 33, 38, 40, 41, 45, + 46, 46, 45, 45, 46, 48, 48, 50, 31, 32, 33, 33, 38, 40, 41, 45, 46, 46, 45, 45, 46, 48, 48, 50, 33, 35, 35, 36, 41, 43, 43, 46, 47, 46, + 45, 45, 46, 47, 47, 49, 33, 35, 36, 36, 41, 43, 44, 46, 47, 46, 46, 46, 46, 47, 47, 49, 34, 36, 37, 37, 42, 44, 45, 47, 47, 47, 45, 45, + 46, 47, 47, 49, 37, 39, 40, 41, 45, 47, 47, 47, 47, 47, 45, 45, 46, 47, 47, 48, 37, 39, 40, 41, 45, 47, 47, 47, 47, 47, 45, 45, 46, 47, + 47, 48, 39, 40, 41, 42, 46, 47, 47, 48, 48, 48, 47, 47, 47, 48, 48, 50, 42, 42, 43, 43, 46, 47, 48, 50, 50, 50, 49, 49, 50, 50, 50, 52, + 42, 42, 43, 43, 46, 47, 48, 50, 50, 50, 49, 49, 50, 50, 50, 52, 45, 45, 44, 45, 47, 47, 48, 51, 51, 51, 51, 51, 52, 52, 52, 54, 49, 47, + 46, 47, 48, 48, 49, 52, 53, 53, 53, 53, 54, 54, 54, 55, 49, 47, 46, 47, 48, 48, 49, 52, 53, 53, 53, 53, 54, 54, 54, 55, 48, 47, 46, 46, + 47, 47, 48, 52, 53, 53, 55, 55, 55, 56, 56, 57, 48, 46, 46, 46, 46, 47, 48, 52, 53, 54, 56, 56, 56, 57, 57, 59, 48, 46, 46, 46, 46, 47, + 48, 52, 53, 54, 56, 56, 56, 57, 57, 59, 49, 46, 45, 45, 46, 46, 47, 52, 53, 54, 57, 57, 58, 60, 60, 61, 49, 46, 45, 45, 45, 46, 47, 52, + 53, 55, 58, 58, 59, 61, 61, 62, 49, 46, 45, 45, 46, 46, 47, 52, 53, 55, 58, 58, 60, 61, 61, 63, 50, 47, 46, 46, 46, 46, 48, 53, 54, 55, + 59, 59, 61, 63, 63, 65, 50, 48, 46, 46, 46, 46, 48, 53, 54, 55, 59, 59, 61, 64, 64, 65, 51, 48, 47, 47, 47, 47, 48, 53, 54, 55, 60, 60, + 61, 64, 64, 66, 52, 49, 48, 48, 47, 47, 48, 53, 54, 56, 61, 61, 63, 66, 66, 68, 52, 49, 48, 48, 47, 47, 48, 53, 54, 56, 61, 61, 63, 66, + 66, 68, 53, 50, 48, 48, 48, 48, 49, 54, 54, 56, 61, 61, 63, 67, 67, 69, 54, 51, 50, 50, 49, 49, 50, 55, 55, 57, 62, 62, 65, 68, 68, 71, + 54, 51, 50, 50, 49, 49, 50, 55, 55, 57, 62, 62, 65, 68, 68, 71, 32, 31, 31, 31, 30, 30, 31, 33, 33, 34, 37, 37, 39, 42, 42, 45, 49, 49, + 48, 48, 48, 49, 49, 49, 50, 50, 51, 52, 52, 53, 54, 54, 31, 31, 31, 31, 31, 31, 32, 35, 35, 36, 39, 39, 40, 42, 42, 45, 47, 47, 47, 46, + 46, 46, 46, 46, 47, 48, 48, 49, 49, 50, 51, 51, 31, 31, 31, 31, 32, 32, 33, 35, 36, 37, 40, 40, 41, 43, 43, 44, 46, 46, 46, 46, 46, 45, + 45, 45, 46, 46, 47, 48, 48, 48, 50, 50, 31, 32, 32, 32, 32, 33, 33, 36, 36, 37, 41, 41, 42, 43, 43, 45, 47, 47, 46, 46, 46, 45, 45, 45, + 46, 46, 47, 48, 48, 48, 50, 50, 35, 36, 37, 37, 38, 38, 38, 41, 41, 42, 45, 45, 46, 46, 46, 47, 48, 48, 47, 46, 46, 46, 45, 46, 46, 46, + 47, 47, 47, 48, 49, 49, 37, 38, 38, 38, 39, 40, 40, 43, 43, 44, 47, 47, 47, 47, 47, 47, 48, 48, 47, 47, 47, 46, 46, 46, 46, 46, 47, 47, + 47, 48, 49, 49, 38, 39, 40, 40, 40, 41, 41, 43, 44, 45, 47, 47, 47, 48, 48, 48, 49, 49, 48, 48, 48, 47, 47, 47, 48, 48, 48, 48, 48, 49, + 50, 50, 47, 46, 46, 46, 45, 45, 45, 46, 46, 47, 47, 47, 48, 50, 50, 51, 52, 52, 52, 52, 52, 52, 52, 52, 53, 53, 53, 53, 53, 54, 55, 55, + 48, 47, 47, 47, 46, 46, 46, 47, 47, 47, 47, 47, 48, 50, 50, 51, 53, 53, 53, 53, 53, 53, 53, 53, 54, 54, 54, 54, 54, 54, 55, 55, 48, 47, + 47, 47, 46, 46, 46, 46, 46, 47, 47, 47, 48, 50, 50, 51, 53, 53, 53, 54, 54, 54, 55, 55, 55, 55, 55, 56, 56, 56, 57, 57, 49, 48, 47, 47, + 45, 45, 45, 45, 46, 45, 45, 45, 47, 49, 49, 51, 53, 53, 55, 56, 56, 57, 58, 58, 59, 59, 60, 61, 61, 61, 62, 62, 49, 48, 47, 47, 45, 45, + 45, 45, 46, 45, 45, 45, 47, 49, 49, 51, 53, 53, 55, 56, 56, 57, 58, 58, 59, 59, 60, 61, 61, 61, 62, 62, 50, 49, 48, 48, 46, 46, 46, 46, + 46, 46, 46, 46, 47, 50, 50, 52, 54, 54, 55, 56, 56, 58, 59, 60, 61, 61, 61, 63, 63, 63, 65, 65, 52, 50, 50, 50, 48, 48, 48, 47, 47, 47, + 47, 47, 48, 50, 50, 52, 54, 54, 56, 57, 57, 60, 61, 61, 63, 64, 64, 66, 66, 67, 68, 68, 52, 50, 50, 50, 48, 48, 48, 47, 47, 47, 47, 47, + 48, 50, 50, 52, 54, 54, 56, 57, 57, 60, 61, 61, 63, 64, 64, 66, 66, 67, 68, 68, 54, 53, 52, 52, 50, 50, 50, 49, 49, 49, 48, 48, 50, 52, + 52, 54, 55, 55, 57, 59, 59, 61, 62, 63, 65, 65, 66, 68, 68, 69, 71, 71, 31, 37, 48, 52, 31, 38, 47, 50, 31, 39, 46, 48, 32, 40, 46, 48, + 35, 43, 46, 47, 39, 47, 47, 47, 40, 47, 48, 48, 42, 47, 50, 50, 47, 48, 53, 54, 47, 47, 53, 56, 46, 47, 54, 57, 46, 46, 55, 61, 47, 46, + 55, 63, 48, 47, 55, 64, 49, 47, 56, 66, 51, 49, 57, 68, 31, 31, 31, 32, 35, 39, 40, 42, 47, 47, 46, 46, 47, 48, 49, 51, 37, 38, 39, 40, + 43, 47, 47, 47, 48, 47, 47, 46, 46, 47, 47, 49, 48, 47, 46, 46, 46, 47, 48, 50, 53, 53, 54, 55, 55, 55, 56, 57, 52, 50, 48, 48, 47, 47, + 48, 50, 54, 56, 57, 61, 63, 64, 66, 68, 32, 31, 35, 38, 48, 49, 50, 52, 31, 31, 36, 39, 47, 48, 49, 50, 31, 31, 37, 40, 47, 47, 48, 50, + 31, 31, 37, 40, 47, 47, 48, 50, 30, 32, 38, 40, 46, 45, 46, 48, 30, 32, 38, 41, 46, 45, 46, 48, 31, 33, 38, 41, 46, 45, 46, 48, 33, 35, + 41, 43, 47, 45, 46, 47, 33, 36, 41, 44, 47, 46, 46, 47, 34, 37, 42, 45, 47, 45, 46, 47, 37, 40, 45, 47, 47, 45, 46, 47, 37, 40, 45, 47, + 47, 45, 46, 47, 39, 41, 46, 47, 48, 47, 47, 48, 42, 43, 46, 48, 50, 49, 50, 50, 42, 43, 46, 48, 50, 49, 50, 50, 45, 44, 47, 48, 51, 51, + 52, 52, 49, 46, 48, 49, 53, 53, 54, 54, 49, 46, 48, 49, 53, 53, 54, 54, 48, 46, 47, 48, 53, 55, 55, 56, 48, 46, 46, 48, 53, 56, 56, 57, + 48, 46, 46, 48, 53, 56, 56, 57, 49, 45, 46, 47, 53, 57, 58, 60, 49, 45, 45, 47, 53, 58, 59, 61, 49, 45, 46, 47, 53, 58, 60, 61, 50, 46, + 46, 48, 54, 59, 61, 63, 50, 46, 46, 48, 54, 59, 61, 64, 51, 47, 47, 48, 54, 60, 61, 64, 52, 48, 47, 48, 54, 61, 63, 66, 52, 48, 47, 48, + 54, 61, 63, 66, 53, 48, 48, 49, 54, 61, 63, 67, 54, 50, 49, 50, 55, 62, 65, 68, 54, 50, 49, 50, 55, 62, 65, 68, 32, 31, 31, 31, 30, 30, + 31, 33, 33, 34, 37, 37, 39, 42, 42, 45, 49, 49, 48, 48, 48, 49, 49, 49, 50, 50, 51, 52, 52, 53, 54, 54, 31, 31, 31, 31, 32, 32, 33, 35, + 36, 37, 40, 40, 41, 43, 43, 44, 46, 46, 46, 46, 46, 45, 45, 45, 46, 46, 47, 48, 48, 48, 50, 50, 35, 36, 37, 37, 38, 38, 38, 41, 41, 42, + 45, 45, 46, 46, 46, 47, 48, 48, 47, 46, 46, 46, 45, 46, 46, 46, 47, 47, 47, 48, 49, 49, 38, 39, 40, 40, 40, 41, 41, 43, 44, 45, 47, 47, + 47, 48, 48, 48, 49, 49, 48, 48, 48, 47, 47, 47, 48, 48, 48, 48, 48, 49, 50, 50, 48, 47, 47, 47, 46, 46, 46, 47, 47, 47, 47, 47, 48, 50, + 50, 51, 53, 53, 53, 53, 53, 53, 53, 53, 54, 54, 54, 54, 54, 54, 55, 55, 49, 48, 47, 47, 45, 45, 45, 45, 46, 45, 45, 45, 47, 49, 49, 51, + 53, 53, 55, 56, 56, 57, 58, 58, 59, 59, 60, 61, 61, 61, 62, 62, 50, 49, 48, 48, 46, 46, 46, 46, 46, 46, 46, 46, 47, 50, 50, 52, 54, 54, + 55, 56, 56, 58, 59, 60, 61, 61, 61, 63, 63, 63, 65, 65, 52, 50, 50, 50, 48, 48, 48, 47, 47, 47, 47, 47, 48, 50, 50, 52, 54, 54, 56, 57, + 57, 60, 61, 61, 63, 64, 64, 66, 66, 67, 68, 68, + ], + ], + [ + [ + 32, 32, 35, 43, 32, 34, 37, 43, 35, 37, 48, 54, 43, 43, 54, 65, 31, 31, 32, 32, 34, 37, 43, 47, 31, 32, 32, 32, 34, 36, 41, 44, 32, 32, + 33, 34, 35, 38, 42, 45, 32, 32, 34, 35, 37, 39, 42, 46, 34, 34, 35, 37, 41, 45, 49, 52, 37, 36, 38, 39, 45, 51, 56, 59, 43, 41, 42, 42, + 49, 56, 63, 67, 47, 44, 45, 46, 52, 59, 67, 71, 32, 31, 31, 31, 31, 31, 32, 32, 34, 35, 36, 39, 41, 44, 47, 48, 31, 32, 32, 32, 32, 32, + 32, 33, 34, 35, 35, 38, 40, 42, 45, 46, 31, 32, 32, 32, 32, 32, 32, 33, 34, 34, 35, 38, 39, 42, 45, 45, 31, 32, 32, 32, 32, 32, 32, 33, + 33, 34, 34, 37, 38, 41, 44, 44, 31, 32, 32, 32, 33, 33, 33, 34, 35, 36, 36, 39, 40, 42, 44, 45, 31, 32, 32, 32, 33, 33, 34, 34, 35, 36, + 36, 39, 40, 42, 45, 45, 32, 32, 32, 32, 33, 34, 35, 36, 37, 38, 38, 40, 41, 42, 45, 46, 32, 33, 33, 33, 34, 34, 36, 36, 38, 39, 40, 42, + 43, 44, 47, 47, 34, 34, 34, 33, 35, 35, 37, 38, 39, 42, 42, 45, 46, 47, 50, 51, 35, 35, 34, 34, 36, 36, 38, 39, 42, 46, 47, 49, 50, 52, + 55, 55, 36, 35, 35, 34, 36, 36, 38, 40, 42, 47, 48, 50, 52, 54, 56, 57, 39, 38, 38, 37, 39, 39, 40, 42, 45, 49, 50, 54, 55, 58, 60, 61, + 41, 40, 39, 38, 40, 40, 41, 43, 46, 50, 52, 55, 57, 60, 62, 63, 44, 42, 42, 41, 42, 42, 42, 44, 47, 52, 54, 58, 60, 63, 66, 67, 47, 45, + 45, 44, 44, 45, 45, 47, 50, 55, 56, 60, 62, 66, 69, 70, 48, 46, 45, 44, 45, 45, 46, 47, 51, 55, 57, 61, 63, 67, 70, 71, 32, 31, 31, 31, + 31, 31, 31, 31, 31, 31, 31, 32, 32, 32, 32, 34, 34, 34, 35, 36, 36, 38, 39, 39, 41, 44, 44, 45, 47, 48, 48, 51, 31, 31, 31, 31, 31, 31, + 31, 32, 32, 32, 32, 32, 32, 32, 33, 34, 34, 34, 35, 35, 35, 37, 39, 39, 40, 43, 43, 44, 46, 47, 47, 50, 31, 31, 32, 32, 32, 32, 32, 32, + 32, 32, 32, 32, 32, 32, 33, 34, 34, 34, 35, 35, 35, 37, 38, 38, 40, 42, 42, 43, 45, 46, 46, 49, 31, 31, 32, 32, 32, 32, 32, 32, 32, 32, + 32, 32, 32, 32, 33, 34, 34, 34, 35, 35, 35, 37, 38, 38, 40, 42, 42, 43, 45, 46, 46, 49, 31, 31, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, + 32, 32, 33, 34, 34, 34, 34, 35, 35, 36, 38, 38, 39, 42, 42, 42, 45, 45, 45, 48, 31, 31, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, + 33, 33, 33, 34, 34, 34, 34, 36, 37, 37, 38, 41, 41, 41, 44, 44, 44, 47, 31, 31, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 33, 33, + 33, 34, 34, 34, 34, 36, 37, 37, 38, 41, 41, 41, 44, 44, 44, 47, 31, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 33, 33, 33, 33, 34, 34, 34, + 34, 35, 35, 36, 38, 38, 39, 41, 41, 42, 44, 45, 45, 47, 31, 32, 32, 32, 32, 32, 32, 32, 33, 33, 33, 33, 33, 33, 34, 35, 35, 35, 36, 36, + 36, 37, 39, 39, 40, 42, 42, 42, 44, 45, 45, 48, 31, 32, 32, 32, 32, 32, 32, 32, 33, 33, 33, 33, 34, 34, 34, 35, 35, 35, 36, 36, 36, 38, + 39, 39, 40, 42, 42, 42, 45, 45, 45, 48, 31, 32, 32, 32, 32, 32, 32, 32, 33, 33, 33, 33, 34, 34, 34, 35, 35, 35, 36, 36, 36, 38, 39, 39, + 40, 42, 42, 42, 45, 45, 45, 48, 32, 32, 32, 32, 32, 32, 32, 33, 33, 33, 33, 34, 35, 35, 35, 36, 36, 36, 37, 37, 37, 39, 40, 40, 41, 42, + 42, 43, 45, 45, 45, 48, 32, 32, 32, 32, 32, 32, 32, 33, 33, 34, 34, 35, 35, 35, 36, 37, 37, 37, 38, 38, 38, 39, 40, 40, 41, 42, 42, 43, + 45, 46, 46, 48, 32, 32, 32, 32, 32, 32, 32, 33, 33, 34, 34, 35, 35, 35, 36, 37, 37, 37, 38, 38, 38, 39, 40, 40, 41, 42, 42, 43, 45, 46, + 46, 48, 32, 33, 33, 33, 33, 33, 33, 33, 34, 34, 34, 35, 36, 36, 36, 38, 38, 38, 39, 40, 40, 41, 42, 42, 43, 44, 44, 45, 47, 47, 47, 50, + 34, 34, 34, 34, 34, 33, 33, 34, 35, 35, 35, 36, 37, 37, 38, 39, 39, 40, 42, 42, 42, 44, 45, 45, 46, 47, 47, 48, 50, 51, 51, 53, 34, 34, + 34, 34, 34, 33, 33, 34, 35, 35, 35, 36, 37, 37, 38, 39, 39, 40, 42, 42, 42, 44, 45, 45, 46, 47, 47, 48, 50, 51, 51, 53, 34, 34, 34, 34, + 34, 34, 34, 34, 35, 35, 35, 36, 37, 37, 38, 40, 40, 41, 43, 44, 44, 45, 46, 46, 47, 49, 49, 49, 51, 52, 52, 54, 35, 35, 35, 35, 34, 34, + 34, 34, 36, 36, 36, 37, 38, 38, 39, 42, 42, 43, 46, 47, 47, 48, 49, 49, 50, 52, 52, 53, 55, 55, 55, 57, 36, 35, 35, 35, 35, 34, 34, 35, + 36, 36, 36, 37, 38, 38, 40, 42, 42, 44, 47, 48, 48, 50, 50, 50, 52, 54, 54, 54, 56, 57, 57, 58, 36, 35, 35, 35, 35, 34, 34, 35, 36, 36, + 36, 37, 38, 38, 40, 42, 42, 44, 47, 48, 48, 50, 50, 50, 52, 54, 54, 54, 56, 57, 57, 58, 38, 37, 37, 37, 36, 36, 36, 36, 37, 38, 38, 39, + 39, 39, 41, 44, 44, 45, 48, 50, 50, 51, 52, 52, 54, 56, 56, 57, 58, 59, 59, 61, 39, 39, 38, 38, 38, 37, 37, 38, 39, 39, 39, 40, 40, 40, + 42, 45, 45, 46, 49, 50, 50, 52, 54, 54, 55, 58, 58, 58, 60, 61, 61, 63, 39, 39, 38, 38, 38, 37, 37, 38, 39, 39, 39, 40, 40, 40, 42, 45, + 45, 46, 49, 50, 50, 52, 54, 54, 55, 58, 58, 58, 60, 61, 61, 63, 41, 40, 40, 40, 39, 38, 38, 39, 40, 40, 40, 41, 41, 41, 43, 46, 46, 47, + 50, 52, 52, 54, 55, 55, 57, 60, 60, 60, 62, 63, 63, 66, 44, 43, 42, 42, 42, 41, 41, 41, 42, 42, 42, 42, 42, 42, 44, 47, 47, 49, 52, 54, + 54, 56, 58, 58, 60, 63, 63, 64, 66, 67, 67, 69, 44, 43, 42, 42, 42, 41, 41, 41, 42, 42, 42, 42, 42, 42, 44, 47, 47, 49, 52, 54, 54, 56, + 58, 58, 60, 63, 63, 64, 66, 67, 67, 69, 45, 44, 43, 43, 42, 41, 41, 42, 42, 42, 42, 43, 43, 43, 45, 48, 48, 49, 53, 54, 54, 57, 58, 58, + 60, 64, 64, 65, 67, 68, 68, 70, 47, 46, 45, 45, 45, 44, 44, 44, 44, 45, 45, 45, 45, 45, 47, 50, 50, 51, 55, 56, 56, 58, 60, 60, 62, 66, + 66, 67, 69, 70, 70, 73, 48, 47, 46, 46, 45, 44, 44, 45, 45, 45, 45, 45, 46, 46, 47, 51, 51, 52, 55, 57, 57, 59, 61, 61, 63, 67, 67, 68, + 70, 71, 71, 74, 48, 47, 46, 46, 45, 44, 44, 45, 45, 45, 45, 45, 46, 46, 47, 51, 51, 52, 55, 57, 57, 59, 61, 61, 63, 67, 67, 68, 70, 71, + 71, 74, 51, 50, 49, 49, 48, 47, 47, 47, 48, 48, 48, 48, 48, 48, 50, 53, 53, 54, 57, 58, 58, 61, 63, 63, 66, 69, 69, 70, 73, 74, 74, 77, + 31, 32, 35, 43, 32, 33, 34, 41, 32, 34, 36, 42, 32, 35, 38, 42, 34, 37, 43, 49, 37, 40, 49, 56, 42, 43, 53, 63, 46, 46, 56, 67, 31, 32, + 32, 32, 34, 37, 42, 46, 32, 33, 34, 35, 37, 40, 43, 46, 35, 34, 36, 38, 43, 49, 53, 56, 43, 41, 42, 42, 49, 56, 63, 67, 32, 31, 31, 32, + 35, 36, 44, 47, 31, 32, 32, 32, 35, 35, 42, 45, 31, 32, 32, 32, 34, 35, 41, 45, 31, 32, 32, 33, 34, 34, 41, 44, 31, 32, 33, 34, 35, 36, + 42, 44, 32, 32, 33, 34, 36, 36, 42, 45, 32, 33, 34, 35, 37, 38, 42, 45, 32, 33, 34, 36, 39, 40, 44, 47, 34, 34, 35, 37, 41, 42, 48, 50, + 35, 34, 36, 38, 45, 47, 52, 55, 36, 34, 36, 38, 46, 48, 54, 56, 39, 37, 39, 40, 48, 50, 58, 60, 41, 39, 40, 41, 49, 51, 60, 62, 44, 41, + 42, 43, 51, 53, 63, 66, 47, 44, 44, 45, 53, 56, 66, 69, 48, 45, 45, 46, 54, 56, 67, 70, 32, 31, 31, 31, 31, 32, 32, 32, 34, 35, 36, 39, + 41, 44, 47, 48, 31, 32, 32, 32, 32, 32, 33, 33, 34, 34, 34, 37, 39, 41, 44, 45, 31, 32, 32, 32, 33, 33, 34, 34, 35, 36, 36, 39, 40, 42, + 44, 45, 32, 32, 32, 33, 34, 34, 35, 36, 37, 38, 38, 40, 41, 43, 45, 46, 35, 35, 34, 34, 35, 36, 37, 39, 41, 45, 46, 48, 49, 51, 53, 54, + 36, 35, 35, 34, 36, 36, 38, 40, 42, 47, 48, 50, 51, 53, 56, 56, 44, 42, 41, 41, 42, 42, 42, 44, 48, 52, 54, 58, 60, 63, 66, 67, 47, 45, + 45, 44, 44, 45, 45, 47, 50, 55, 56, 60, 62, 66, 69, 70, 32, 31, 31, 31, 31, 32, 32, 32, 35, 36, 36, 40, 44, 44, 47, 53, 31, 31, 32, 32, + 32, 32, 32, 33, 35, 35, 35, 39, 43, 43, 46, 52, 31, 32, 32, 32, 32, 32, 32, 33, 35, 35, 35, 39, 42, 42, 45, 51, 31, 32, 32, 32, 32, 32, + 32, 33, 35, 35, 35, 39, 42, 42, 45, 51, 31, 32, 32, 32, 32, 32, 32, 33, 34, 35, 35, 39, 41, 41, 45, 50, 31, 32, 32, 32, 32, 33, 33, 33, + 34, 34, 34, 38, 41, 41, 44, 49, 31, 32, 32, 32, 32, 33, 33, 33, 34, 34, 34, 38, 41, 41, 44, 49, 31, 32, 32, 32, 32, 33, 33, 33, 34, 35, + 35, 38, 41, 41, 44, 49, 31, 32, 32, 32, 33, 34, 34, 34, 35, 36, 36, 39, 42, 42, 44, 49, 32, 32, 32, 32, 33, 34, 34, 34, 36, 36, 36, 39, + 42, 42, 45, 50, 32, 32, 32, 32, 33, 34, 34, 34, 36, 36, 36, 39, 42, 42, 45, 50, 32, 32, 32, 32, 33, 35, 35, 35, 37, 37, 37, 40, 42, 42, + 45, 49, 32, 32, 33, 33, 34, 35, 35, 36, 37, 38, 38, 41, 42, 42, 45, 49, 32, 32, 33, 33, 34, 35, 35, 36, 37, 38, 38, 41, 42, 42, 45, 49, + 32, 33, 33, 33, 34, 36, 36, 36, 39, 40, 40, 42, 44, 44, 47, 51, 34, 34, 34, 34, 35, 37, 37, 38, 41, 42, 42, 45, 48, 48, 50, 54, 34, 34, + 34, 34, 35, 37, 37, 38, 41, 42, 42, 45, 48, 48, 50, 54, 34, 34, 34, 34, 35, 37, 37, 38, 42, 43, 43, 46, 49, 49, 51, 55, 35, 35, 34, 34, + 36, 38, 38, 39, 45, 47, 47, 50, 52, 52, 55, 59, 36, 35, 34, 34, 36, 38, 38, 40, 46, 48, 48, 51, 54, 54, 56, 60, 36, 35, 34, 34, 36, 38, + 38, 40, 46, 48, 48, 51, 54, 54, 56, 60, 38, 37, 36, 36, 37, 40, 40, 41, 47, 49, 49, 53, 56, 56, 58, 63, 39, 38, 37, 37, 39, 40, 40, 42, + 48, 50, 50, 54, 58, 58, 60, 65, 39, 38, 37, 37, 39, 40, 40, 42, 48, 50, 50, 54, 58, 58, 60, 65, 41, 40, 39, 39, 40, 41, 41, 43, 49, 51, + 51, 56, 60, 60, 62, 67, 44, 42, 41, 41, 42, 43, 43, 45, 51, 53, 53, 59, 63, 63, 66, 71, 44, 42, 41, 41, 42, 43, 43, 45, 51, 53, 53, 59, + 63, 63, 66, 71, 44, 43, 42, 42, 42, 43, 43, 45, 51, 54, 54, 59, 64, 64, 67, 72, 47, 45, 44, 44, 44, 45, 45, 47, 53, 56, 56, 61, 66, 66, + 69, 75, 48, 46, 45, 45, 45, 46, 46, 48, 54, 56, 56, 62, 67, 67, 70, 76, 48, 46, 45, 45, 45, 46, 46, 48, 54, 56, 56, 62, 67, 67, 70, 76, + 51, 49, 47, 47, 48, 48, 48, 50, 56, 58, 58, 64, 69, 69, 73, 79, 32, 31, 31, 31, 31, 31, 31, 31, 31, 32, 32, 32, 32, 32, 32, 34, 34, 34, + 35, 36, 36, 38, 39, 39, 41, 44, 44, 44, 47, 48, 48, 51, 31, 31, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 33, 34, 34, 34, 35, 35, + 35, 37, 38, 38, 40, 42, 42, 43, 45, 46, 46, 49, 31, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 33, 33, 33, 34, 34, 34, 34, 34, 34, 36, + 37, 37, 39, 41, 41, 42, 44, 45, 45, 47, 31, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 33, 33, 33, 34, 34, 34, 34, 34, 34, 36, 37, 37, + 39, 41, 41, 42, 44, 45, 45, 47, 31, 32, 32, 32, 32, 32, 32, 32, 33, 33, 33, 33, 34, 34, 34, 35, 35, 35, 36, 36, 36, 37, 39, 39, 40, 42, + 42, 42, 44, 45, 45, 48, 32, 32, 32, 32, 32, 33, 33, 33, 34, 34, 34, 35, 35, 35, 36, 37, 37, 37, 38, 38, 38, 40, 40, 40, 41, 43, 43, 43, + 45, 46, 46, 48, 32, 32, 32, 32, 32, 33, 33, 33, 34, 34, 34, 35, 35, 35, 36, 37, 37, 37, 38, 38, 38, 40, 40, 40, 41, 43, 43, 43, 45, 46, + 46, 48, 32, 33, 33, 33, 33, 33, 33, 33, 34, 34, 34, 35, 36, 36, 36, 38, 38, 38, 39, 40, 40, 41, 42, 42, 43, 45, 45, 45, 47, 48, 48, 50, + 35, 35, 35, 35, 34, 34, 34, 34, 35, 36, 36, 37, 37, 37, 39, 41, 41, 42, 45, 46, 46, 47, 48, 48, 49, 51, 51, 51, 53, 54, 54, 56, 36, 35, + 35, 35, 35, 34, 34, 35, 36, 36, 36, 37, 38, 38, 40, 42, 42, 43, 47, 48, 48, 49, 50, 50, 51, 53, 53, 54, 56, 56, 56, 58, 36, 35, 35, 35, + 35, 34, 34, 35, 36, 36, 36, 37, 38, 38, 40, 42, 42, 43, 47, 48, 48, 49, 50, 50, 51, 53, 53, 54, 56, 56, 56, 58, 40, 39, 39, 39, 39, 38, + 38, 38, 39, 39, 39, 40, 41, 41, 42, 45, 45, 46, 50, 51, 51, 53, 54, 54, 56, 59, 59, 59, 61, 62, 62, 64, 44, 43, 42, 42, 41, 41, 41, 41, + 42, 42, 42, 42, 42, 42, 44, 48, 48, 49, 52, 54, 54, 56, 58, 58, 60, 63, 63, 64, 66, 67, 67, 69, 44, 43, 42, 42, 41, 41, 41, 41, 42, 42, + 42, 42, 42, 42, 44, 48, 48, 49, 52, 54, 54, 56, 58, 58, 60, 63, 63, 64, 66, 67, 67, 69, 47, 46, 45, 45, 45, 44, 44, 44, 44, 45, 45, 45, + 45, 45, 47, 50, 50, 51, 55, 56, 56, 58, 60, 60, 62, 66, 66, 67, 69, 70, 70, 73, 53, 52, 51, 51, 50, 49, 49, 49, 49, 50, 50, 49, 49, 49, + 51, 54, 54, 55, 59, 60, 60, 63, 65, 65, 67, 71, 71, 72, 75, 76, 76, 79, 31, 32, 36, 44, 32, 32, 35, 42, 32, 32, 35, 41, 32, 33, 34, 41, + 32, 34, 36, 42, 32, 34, 36, 42, 32, 35, 38, 42, 33, 36, 40, 44, 34, 37, 42, 48, 35, 38, 47, 52, 35, 38, 48, 54, 38, 40, 50, 58, 40, 41, + 51, 60, 42, 43, 53, 63, 45, 45, 56, 66, 46, 46, 56, 67, 31, 32, 32, 32, 32, 32, 32, 33, 34, 35, 35, 38, 40, 42, 45, 46, 32, 32, 32, 33, + 34, 34, 35, 36, 37, 38, 38, 40, 41, 43, 45, 46, 36, 35, 35, 34, 36, 36, 38, 40, 42, 47, 48, 50, 51, 53, 56, 56, 44, 42, 41, 41, 42, 42, + 42, 44, 48, 52, 54, 58, 60, 63, 66, 67, 32, 31, 31, 32, 35, 36, 44, 47, 31, 32, 32, 32, 35, 35, 43, 46, 31, 32, 32, 32, 35, 35, 42, 45, + 31, 32, 32, 32, 35, 35, 42, 45, 31, 32, 32, 32, 34, 35, 41, 45, 31, 32, 32, 33, 34, 34, 41, 44, 31, 32, 32, 33, 34, 34, 41, 44, 31, 32, + 32, 33, 34, 35, 41, 44, 31, 32, 33, 34, 35, 36, 42, 44, 32, 32, 33, 34, 36, 36, 42, 45, 32, 32, 33, 34, 36, 36, 42, 45, 32, 32, 33, 35, + 37, 37, 42, 45, 32, 33, 34, 35, 37, 38, 42, 45, 32, 33, 34, 35, 37, 38, 42, 45, 32, 33, 34, 36, 39, 40, 44, 47, 34, 34, 35, 37, 41, 42, + 48, 50, 34, 34, 35, 37, 41, 42, 48, 50, 34, 34, 35, 37, 42, 43, 49, 51, 35, 34, 36, 38, 45, 47, 52, 55, 36, 34, 36, 38, 46, 48, 54, 56, + 36, 34, 36, 38, 46, 48, 54, 56, 38, 36, 37, 40, 47, 49, 56, 58, 39, 37, 39, 40, 48, 50, 58, 60, 39, 37, 39, 40, 48, 50, 58, 60, 41, 39, + 40, 41, 49, 51, 60, 62, 44, 41, 42, 43, 51, 53, 63, 66, 44, 41, 42, 43, 51, 53, 63, 66, 44, 42, 42, 43, 51, 54, 64, 67, 47, 44, 44, 45, + 53, 56, 66, 69, 48, 45, 45, 46, 54, 56, 67, 70, 48, 45, 45, 46, 54, 56, 67, 70, 51, 47, 48, 48, 56, 58, 69, 73, 32, 31, 31, 31, 31, 31, + 31, 31, 31, 32, 32, 32, 32, 32, 32, 34, 34, 34, 35, 36, 36, 38, 39, 39, 41, 44, 44, 44, 47, 48, 48, 51, 31, 32, 32, 32, 32, 32, 32, 32, + 32, 32, 32, 32, 33, 33, 33, 34, 34, 34, 34, 34, 34, 36, 37, 37, 39, 41, 41, 42, 44, 45, 45, 47, 31, 32, 32, 32, 32, 32, 32, 32, 33, 33, + 33, 33, 34, 34, 34, 35, 35, 35, 36, 36, 36, 37, 39, 39, 40, 42, 42, 42, 44, 45, 45, 48, 32, 32, 32, 32, 32, 33, 33, 33, 34, 34, 34, 35, + 35, 35, 36, 37, 37, 37, 38, 38, 38, 40, 40, 40, 41, 43, 43, 43, 45, 46, 46, 48, 35, 35, 35, 35, 34, 34, 34, 34, 35, 36, 36, 37, 37, 37, + 39, 41, 41, 42, 45, 46, 46, 47, 48, 48, 49, 51, 51, 51, 53, 54, 54, 56, 36, 35, 35, 35, 35, 34, 34, 35, 36, 36, 36, 37, 38, 38, 40, 42, + 42, 43, 47, 48, 48, 49, 50, 50, 51, 53, 53, 54, 56, 56, 56, 58, 44, 43, 42, 42, 41, 41, 41, 41, 42, 42, 42, 42, 42, 42, 44, 48, 48, 49, + 52, 54, 54, 56, 58, 58, 60, 63, 63, 64, 66, 67, 67, 69, 47, 46, 45, 45, 45, 44, 44, 44, 44, 45, 45, 45, 45, 45, 47, 50, 50, 51, 55, 56, + 56, 58, 60, 60, 62, 66, 66, 67, 69, 70, 70, 73, + ], + [ + 31, 37, 47, 47, 37, 44, 47, 45, 47, 47, 53, 53, 47, 45, 53, 59, 31, 31, 34, 37, 43, 48, 47, 49, 31, 32, 35, 40, 43, 46, 45, 46, 34, 35, + 39, 43, 45, 46, 45, 46, 37, 40, 43, 47, 47, 47, 45, 46, 43, 43, 45, 47, 49, 50, 50, 50, 48, 46, 46, 47, 50, 53, 55, 55, 47, 45, 45, 45, + 50, 55, 58, 60, 49, 46, 46, 46, 50, 55, 60, 61, 32, 31, 31, 30, 33, 33, 36, 38, 41, 47, 49, 48, 49, 49, 50, 50, 31, 31, 31, 31, 34, 34, + 38, 40, 42, 46, 47, 47, 47, 47, 48, 48, 31, 31, 31, 31, 34, 35, 39, 40, 42, 46, 47, 46, 46, 46, 47, 47, 30, 31, 31, 32, 34, 35, 40, 41, + 42, 45, 46, 45, 45, 45, 46, 46, 33, 34, 34, 34, 37, 38, 42, 43, 44, 46, 47, 46, 46, 45, 46, 46, 33, 34, 35, 35, 38, 39, 43, 44, 45, 47, + 47, 46, 46, 45, 46, 46, 36, 38, 39, 40, 42, 43, 47, 47, 47, 47, 48, 46, 46, 45, 46, 46, 38, 40, 40, 41, 43, 44, 47, 47, 48, 48, 49, 48, + 47, 47, 47, 47, 41, 42, 42, 42, 44, 45, 47, 48, 48, 50, 50, 49, 49, 49, 50, 50, 47, 46, 46, 45, 46, 47, 47, 48, 50, 52, 52, 52, 52, 52, + 53, 53, 49, 47, 47, 46, 47, 47, 48, 49, 50, 52, 53, 53, 53, 53, 54, 54, 48, 47, 46, 45, 46, 46, 46, 48, 49, 52, 53, 54, 55, 55, 56, 56, + 49, 47, 46, 45, 46, 46, 46, 47, 49, 52, 53, 55, 55, 57, 57, 58, 49, 47, 46, 45, 45, 45, 45, 47, 49, 52, 53, 55, 57, 58, 59, 60, 50, 48, + 47, 46, 46, 46, 46, 47, 50, 53, 54, 56, 57, 59, 61, 61, 50, 48, 47, 46, 46, 46, 46, 47, 50, 53, 54, 56, 58, 60, 61, 61, 32, 31, 31, 31, + 31, 30, 30, 31, 33, 33, 33, 35, 36, 36, 38, 41, 41, 43, 47, 49, 49, 49, 48, 48, 49, 49, 49, 49, 50, 50, 50, 51, 31, 31, 31, 31, 31, 31, + 31, 31, 33, 34, 34, 36, 37, 37, 39, 42, 42, 43, 47, 48, 48, 48, 47, 47, 47, 47, 47, 48, 49, 49, 49, 50, 31, 31, 31, 31, 31, 31, 31, 32, + 34, 34, 34, 37, 38, 38, 40, 42, 42, 43, 46, 47, 47, 47, 47, 47, 47, 47, 47, 47, 48, 48, 48, 49, 31, 31, 31, 31, 31, 31, 31, 32, 34, 34, + 34, 37, 38, 38, 40, 42, 42, 43, 46, 47, 47, 47, 47, 47, 47, 47, 47, 47, 48, 48, 48, 49, 31, 31, 31, 31, 31, 31, 31, 32, 34, 35, 35, 37, + 39, 39, 40, 42, 42, 43, 46, 47, 47, 46, 46, 46, 46, 46, 46, 46, 47, 47, 47, 48, 30, 31, 31, 31, 31, 32, 32, 32, 34, 35, 35, 38, 40, 40, + 41, 42, 42, 43, 45, 46, 46, 46, 45, 45, 45, 45, 45, 45, 46, 46, 46, 47, 30, 31, 31, 31, 31, 32, 32, 32, 34, 35, 35, 38, 40, 40, 41, 42, + 42, 43, 45, 46, 46, 46, 45, 45, 45, 45, 45, 45, 46, 46, 46, 47, 31, 31, 32, 32, 32, 32, 32, 33, 35, 36, 36, 38, 40, 40, 41, 43, 43, 43, + 46, 46, 46, 46, 45, 45, 45, 45, 45, 45, 46, 46, 46, 47, 33, 33, 34, 34, 34, 34, 34, 35, 37, 38, 38, 41, 42, 42, 43, 44, 44, 45, 46, 47, + 47, 46, 46, 46, 46, 45, 45, 45, 46, 46, 46, 47, 33, 34, 34, 34, 35, 35, 35, 36, 38, 39, 39, 41, 43, 43, 44, 45, 45, 45, 47, 47, 47, 46, + 46, 46, 46, 45, 45, 45, 46, 46, 46, 47, 33, 34, 34, 34, 35, 35, 35, 36, 38, 39, 39, 41, 43, 43, 44, 45, 45, 45, 47, 47, 47, 46, 46, 46, + 46, 45, 45, 45, 46, 46, 46, 47, 35, 36, 37, 37, 37, 38, 38, 38, 41, 41, 41, 44, 46, 46, 46, 46, 46, 46, 47, 47, 47, 47, 46, 46, 46, 45, + 45, 45, 46, 46, 46, 47, 36, 37, 38, 38, 39, 40, 40, 40, 42, 43, 43, 46, 47, 47, 47, 47, 47, 47, 47, 48, 48, 47, 46, 46, 46, 45, 45, 45, + 46, 46, 46, 46, 36, 37, 38, 38, 39, 40, 40, 40, 42, 43, 43, 46, 47, 47, 47, 47, 47, 47, 47, 48, 48, 47, 46, 46, 46, 45, 45, 45, 46, 46, + 46, 46, 38, 39, 40, 40, 40, 41, 41, 41, 43, 44, 44, 46, 47, 47, 47, 48, 48, 48, 48, 49, 49, 48, 48, 48, 47, 47, 47, 47, 47, 47, 47, 48, + 41, 42, 42, 42, 42, 42, 42, 43, 44, 45, 45, 46, 47, 47, 48, 48, 48, 49, 50, 50, 50, 50, 49, 49, 49, 49, 49, 49, 50, 50, 50, 50, 41, 42, + 42, 42, 42, 42, 42, 43, 44, 45, 45, 46, 47, 47, 48, 48, 48, 49, 50, 50, 50, 50, 49, 49, 49, 49, 49, 49, 50, 50, 50, 50, 43, 43, 43, 43, + 43, 43, 43, 43, 45, 45, 45, 46, 47, 47, 48, 49, 49, 49, 50, 51, 51, 50, 50, 50, 50, 50, 50, 50, 50, 50, 50, 51, 47, 47, 46, 46, 46, 45, + 45, 46, 46, 47, 47, 47, 47, 47, 48, 50, 50, 50, 52, 52, 52, 52, 52, 52, 52, 52, 52, 52, 53, 53, 53, 53, 49, 48, 47, 47, 47, 46, 46, 46, + 47, 47, 47, 47, 48, 48, 49, 50, 50, 51, 52, 53, 53, 53, 53, 53, 53, 53, 53, 53, 54, 54, 54, 54, 49, 48, 47, 47, 47, 46, 46, 46, 47, 47, + 47, 47, 48, 48, 49, 50, 50, 51, 52, 53, 53, 53, 53, 53, 53, 53, 53, 53, 54, 54, 54, 54, 49, 48, 47, 47, 46, 46, 46, 46, 46, 46, 46, 47, + 47, 47, 48, 50, 50, 50, 52, 53, 53, 53, 54, 54, 54, 55, 55, 55, 55, 55, 55, 56, 48, 47, 47, 47, 46, 45, 45, 45, 46, 46, 46, 46, 46, 46, + 48, 49, 49, 50, 52, 53, 53, 54, 54, 54, 55, 55, 55, 56, 56, 56, 56, 57, 48, 47, 47, 47, 46, 45, 45, 45, 46, 46, 46, 46, 46, 46, 48, 49, + 49, 50, 52, 53, 53, 54, 54, 54, 55, 55, 55, 56, 56, 56, 56, 57, 49, 47, 47, 47, 46, 45, 45, 45, 46, 46, 46, 46, 46, 46, 47, 49, 49, 50, + 52, 53, 53, 54, 55, 55, 55, 57, 57, 57, 57, 58, 58, 58, 49, 47, 47, 47, 46, 45, 45, 45, 45, 45, 45, 45, 45, 45, 47, 49, 49, 50, 52, 53, + 53, 55, 55, 55, 57, 58, 58, 59, 59, 60, 60, 60, 49, 47, 47, 47, 46, 45, 45, 45, 45, 45, 45, 45, 45, 45, 47, 49, 49, 50, 52, 53, 53, 55, + 55, 55, 57, 58, 58, 59, 59, 60, 60, 60, 49, 48, 47, 47, 46, 45, 45, 45, 45, 45, 45, 45, 45, 45, 47, 49, 49, 50, 52, 53, 53, 55, 56, 56, + 57, 59, 59, 59, 60, 60, 60, 61, 50, 49, 48, 48, 47, 46, 46, 46, 46, 46, 46, 46, 46, 46, 47, 50, 50, 50, 53, 54, 54, 55, 56, 56, 57, 59, + 59, 60, 61, 61, 61, 62, 50, 49, 48, 48, 47, 46, 46, 46, 46, 46, 46, 46, 46, 46, 47, 50, 50, 50, 53, 54, 54, 55, 56, 56, 58, 60, 60, 60, + 61, 61, 61, 63, 50, 49, 48, 48, 47, 46, 46, 46, 46, 46, 46, 46, 46, 46, 47, 50, 50, 50, 53, 54, 54, 55, 56, 56, 58, 60, 60, 60, 61, 61, + 61, 63, 51, 50, 49, 49, 48, 47, 47, 47, 47, 47, 47, 47, 46, 46, 48, 50, 50, 51, 53, 54, 54, 56, 57, 57, 58, 60, 60, 61, 62, 63, 63, 64, + 31, 38, 47, 48, 31, 40, 46, 45, 35, 43, 47, 46, 39, 47, 47, 45, 43, 47, 50, 50, 47, 47, 53, 55, 46, 46, 53, 58, 48, 46, 54, 59, 31, 31, + 35, 39, 43, 47, 46, 48, 38, 40, 43, 47, 47, 47, 46, 46, 47, 46, 47, 47, 50, 53, 53, 54, 48, 45, 46, 45, 50, 55, 58, 59, 32, 31, 33, 37, + 45, 48, 49, 50, 31, 31, 34, 38, 45, 47, 47, 48, 31, 32, 34, 39, 45, 46, 46, 47, 30, 32, 35, 40, 44, 46, 45, 46, 33, 35, 37, 42, 46, 47, + 45, 46, 33, 36, 38, 43, 46, 47, 46, 46, 37, 40, 43, 47, 47, 47, 45, 46, 39, 41, 43, 47, 48, 48, 47, 47, 42, 43, 44, 47, 49, 50, 49, 50, + 47, 46, 46, 48, 51, 52, 53, 53, 49, 46, 47, 48, 52, 53, 53, 54, 48, 46, 46, 47, 51, 53, 56, 56, 48, 45, 46, 46, 51, 53, 57, 57, 49, 45, + 45, 46, 51, 53, 58, 59, 50, 46, 46, 46, 52, 54, 59, 61, 50, 46, 46, 46, 52, 54, 59, 61, 32, 31, 31, 30, 33, 33, 37, 39, 42, 47, 49, 48, + 48, 49, 50, 50, 31, 31, 32, 32, 35, 36, 40, 41, 43, 46, 46, 46, 45, 45, 46, 46, 33, 34, 34, 35, 37, 38, 43, 43, 44, 46, 47, 46, 46, 45, + 46, 46, 37, 38, 39, 40, 42, 43, 47, 47, 47, 48, 48, 47, 46, 46, 46, 46, 45, 45, 45, 44, 46, 46, 47, 48, 49, 51, 52, 51, 51, 51, 52, 52, + 48, 47, 46, 46, 47, 47, 47, 48, 50, 52, 53, 53, 53, 53, 54, 54, 49, 47, 46, 45, 45, 46, 45, 47, 49, 53, 53, 56, 57, 58, 59, 59, 50, 48, + 47, 46, 46, 46, 46, 47, 50, 53, 54, 56, 57, 59, 61, 61, 32, 31, 31, 31, 33, 37, 37, 38, 45, 48, 48, 49, 49, 49, 50, 52, 31, 31, 31, 31, + 33, 38, 38, 39, 45, 47, 47, 48, 48, 48, 49, 51, 31, 31, 31, 31, 34, 38, 38, 40, 45, 47, 47, 47, 47, 47, 48, 50, 31, 31, 31, 31, 34, 38, + 38, 40, 45, 47, 47, 47, 47, 47, 48, 50, 31, 31, 32, 32, 34, 39, 39, 40, 45, 46, 46, 46, 46, 46, 47, 49, 30, 31, 32, 32, 35, 40, 40, 41, + 44, 46, 46, 45, 45, 45, 46, 48, 30, 31, 32, 32, 35, 40, 40, 41, 44, 46, 46, 45, 45, 45, 46, 48, 31, 32, 33, 33, 35, 40, 40, 41, 45, 46, + 46, 45, 45, 45, 46, 48, 33, 34, 35, 35, 37, 42, 42, 43, 46, 47, 47, 46, 45, 45, 46, 47, 33, 35, 36, 36, 38, 43, 43, 44, 46, 47, 47, 46, + 46, 46, 46, 47, 33, 35, 36, 36, 38, 43, 43, 44, 46, 47, 47, 46, 46, 46, 46, 47, 35, 37, 38, 38, 41, 45, 45, 46, 47, 47, 47, 46, 45, 45, + 46, 47, 37, 39, 40, 40, 43, 47, 47, 47, 47, 47, 47, 46, 45, 45, 46, 47, 37, 39, 40, 40, 43, 47, 47, 47, 47, 47, 47, 46, 45, 45, 46, 47, + 39, 40, 41, 41, 43, 47, 47, 47, 48, 48, 48, 47, 47, 47, 47, 48, 42, 42, 43, 43, 44, 47, 47, 48, 49, 50, 50, 49, 49, 49, 50, 50, 42, 42, + 43, 43, 44, 47, 47, 48, 49, 50, 50, 49, 49, 49, 50, 50, 43, 43, 43, 43, 45, 47, 47, 48, 50, 50, 50, 50, 50, 50, 50, 51, 47, 46, 46, 46, + 46, 48, 48, 48, 51, 52, 52, 52, 53, 53, 53, 53, 49, 47, 46, 46, 47, 48, 48, 49, 52, 53, 53, 53, 53, 53, 54, 54, 49, 47, 46, 46, 47, 48, + 48, 49, 52, 53, 53, 53, 53, 53, 54, 54, 48, 47, 46, 46, 46, 47, 47, 48, 52, 53, 53, 54, 55, 55, 55, 56, 48, 47, 46, 46, 46, 47, 47, 48, + 51, 53, 53, 54, 56, 56, 56, 57, 48, 47, 46, 46, 46, 47, 47, 48, 51, 53, 53, 54, 56, 56, 56, 57, 48, 47, 45, 45, 46, 46, 46, 47, 51, 53, + 53, 55, 57, 57, 57, 59, 49, 46, 45, 45, 45, 46, 46, 47, 51, 53, 53, 56, 58, 58, 59, 61, 49, 46, 45, 45, 45, 46, 46, 47, 51, 53, 53, 56, + 58, 58, 59, 61, 49, 47, 45, 45, 45, 46, 46, 47, 52, 53, 53, 56, 58, 58, 60, 62, 50, 48, 46, 46, 46, 46, 46, 48, 52, 54, 54, 57, 59, 59, + 61, 63, 50, 48, 46, 46, 46, 46, 46, 48, 52, 54, 54, 57, 59, 59, 61, 64, 50, 48, 46, 46, 46, 46, 46, 48, 52, 54, 54, 57, 59, 59, 61, 64, + 51, 49, 47, 47, 47, 47, 47, 48, 52, 54, 54, 58, 60, 60, 62, 65, 32, 31, 31, 31, 31, 30, 30, 31, 33, 33, 33, 35, 37, 37, 39, 42, 42, 43, + 47, 49, 49, 48, 48, 48, 48, 49, 49, 49, 50, 50, 50, 51, 31, 31, 31, 31, 31, 31, 31, 32, 34, 35, 35, 37, 39, 39, 40, 42, 42, 43, 46, 47, + 47, 47, 47, 47, 47, 46, 46, 47, 48, 48, 48, 49, 31, 31, 31, 31, 32, 32, 32, 33, 35, 36, 36, 38, 40, 40, 41, 43, 43, 43, 46, 46, 46, 46, + 46, 46, 45, 45, 45, 45, 46, 46, 46, 47, 31, 31, 31, 31, 32, 32, 32, 33, 35, 36, 36, 38, 40, 40, 41, 43, 43, 43, 46, 46, 46, 46, 46, 46, + 45, 45, 45, 45, 46, 46, 46, 47, 33, 33, 34, 34, 34, 35, 35, 35, 37, 38, 38, 41, 43, 43, 43, 44, 44, 45, 46, 47, 47, 46, 46, 46, 46, 45, + 45, 45, 46, 46, 46, 47, 37, 38, 38, 38, 39, 40, 40, 40, 42, 43, 43, 45, 47, 47, 47, 47, 47, 47, 48, 48, 48, 47, 47, 47, 46, 46, 46, 46, + 46, 46, 46, 47, 37, 38, 38, 38, 39, 40, 40, 40, 42, 43, 43, 45, 47, 47, 47, 47, 47, 47, 48, 48, 48, 47, 47, 47, 46, 46, 46, 46, 46, 46, + 46, 47, 38, 39, 40, 40, 40, 41, 41, 41, 43, 44, 44, 46, 47, 47, 47, 48, 48, 48, 48, 49, 49, 48, 48, 48, 47, 47, 47, 47, 48, 48, 48, 48, + 45, 45, 45, 45, 45, 44, 44, 45, 46, 46, 46, 47, 47, 47, 48, 49, 49, 50, 51, 52, 52, 52, 51, 51, 51, 51, 51, 52, 52, 52, 52, 52, 48, 47, + 47, 47, 46, 46, 46, 46, 47, 47, 47, 47, 47, 47, 48, 50, 50, 50, 52, 53, 53, 53, 53, 53, 53, 53, 53, 53, 54, 54, 54, 54, 48, 47, 47, 47, + 46, 46, 46, 46, 47, 47, 47, 47, 47, 47, 48, 50, 50, 50, 52, 53, 53, 53, 53, 53, 53, 53, 53, 53, 54, 54, 54, 54, 49, 48, 47, 47, 46, 45, + 45, 45, 46, 46, 46, 46, 46, 46, 47, 49, 49, 50, 52, 53, 53, 54, 54, 54, 55, 56, 56, 56, 57, 57, 57, 58, 49, 48, 47, 47, 46, 45, 45, 45, + 45, 46, 46, 45, 45, 45, 47, 49, 49, 50, 53, 53, 53, 55, 56, 56, 57, 58, 58, 58, 59, 59, 59, 60, 49, 48, 47, 47, 46, 45, 45, 45, 45, 46, + 46, 45, 45, 45, 47, 49, 49, 50, 53, 53, 53, 55, 56, 56, 57, 58, 58, 58, 59, 59, 59, 60, 50, 49, 48, 48, 47, 46, 46, 46, 46, 46, 46, 46, + 46, 46, 47, 50, 50, 50, 53, 54, 54, 55, 56, 56, 57, 59, 59, 60, 61, 61, 61, 62, 52, 51, 50, 50, 49, 48, 48, 48, 47, 47, 47, 47, 47, 47, + 48, 50, 50, 51, 53, 54, 54, 56, 57, 57, 59, 61, 61, 62, 63, 64, 64, 65, 31, 37, 48, 49, 31, 38, 47, 47, 31, 39, 46, 46, 31, 40, 46, 45, + 34, 42, 47, 45, 35, 43, 47, 46, 39, 47, 47, 45, 40, 47, 48, 47, 42, 47, 50, 49, 46, 48, 52, 53, 47, 48, 53, 53, 47, 47, 53, 56, 47, 46, + 53, 57, 46, 46, 53, 58, 48, 46, 54, 59, 48, 46, 54, 59, 31, 31, 31, 31, 34, 35, 39, 40, 42, 46, 47, 47, 47, 46, 48, 48, 37, 38, 39, 40, + 42, 43, 47, 47, 47, 48, 48, 47, 46, 46, 46, 46, 48, 47, 46, 46, 47, 47, 47, 48, 50, 52, 53, 53, 53, 53, 54, 54, 49, 47, 46, 45, 45, 46, + 45, 47, 49, 53, 53, 56, 57, 58, 59, 59, 32, 31, 33, 37, 45, 48, 49, 50, 31, 31, 33, 38, 45, 47, 48, 49, 31, 31, 34, 38, 45, 47, 47, 48, + 31, 31, 34, 38, 45, 47, 47, 48, 31, 32, 34, 39, 45, 46, 46, 47, 30, 32, 35, 40, 44, 46, 45, 46, 30, 32, 35, 40, 44, 46, 45, 46, 31, 33, + 35, 40, 45, 46, 45, 46, 33, 35, 37, 42, 46, 47, 45, 46, 33, 36, 38, 43, 46, 47, 46, 46, 33, 36, 38, 43, 46, 47, 46, 46, 35, 38, 41, 45, + 47, 47, 45, 46, 37, 40, 43, 47, 47, 47, 45, 46, 37, 40, 43, 47, 47, 47, 45, 46, 39, 41, 43, 47, 48, 48, 47, 47, 42, 43, 44, 47, 49, 50, + 49, 50, 42, 43, 44, 47, 49, 50, 49, 50, 43, 43, 45, 47, 50, 50, 50, 50, 47, 46, 46, 48, 51, 52, 53, 53, 49, 46, 47, 48, 52, 53, 53, 54, + 49, 46, 47, 48, 52, 53, 53, 54, 48, 46, 46, 47, 52, 53, 55, 55, 48, 46, 46, 47, 51, 53, 56, 56, 48, 46, 46, 47, 51, 53, 56, 56, 48, 45, + 46, 46, 51, 53, 57, 57, 49, 45, 45, 46, 51, 53, 58, 59, 49, 45, 45, 46, 51, 53, 58, 59, 49, 45, 45, 46, 52, 53, 58, 60, 50, 46, 46, 46, + 52, 54, 59, 61, 50, 46, 46, 46, 52, 54, 59, 61, 50, 46, 46, 46, 52, 54, 59, 61, 51, 47, 47, 47, 52, 54, 60, 62, 32, 31, 31, 31, 31, 30, + 30, 31, 33, 33, 33, 35, 37, 37, 39, 42, 42, 43, 47, 49, 49, 48, 48, 48, 48, 49, 49, 49, 50, 50, 50, 51, 31, 31, 31, 31, 32, 32, 32, 33, + 35, 36, 36, 38, 40, 40, 41, 43, 43, 43, 46, 46, 46, 46, 46, 46, 45, 45, 45, 45, 46, 46, 46, 47, 33, 33, 34, 34, 34, 35, 35, 35, 37, 38, + 38, 41, 43, 43, 43, 44, 44, 45, 46, 47, 47, 46, 46, 46, 46, 45, 45, 45, 46, 46, 46, 47, 37, 38, 38, 38, 39, 40, 40, 40, 42, 43, 43, 45, + 47, 47, 47, 47, 47, 47, 48, 48, 48, 47, 47, 47, 46, 46, 46, 46, 46, 46, 46, 47, 45, 45, 45, 45, 45, 44, 44, 45, 46, 46, 46, 47, 47, 47, + 48, 49, 49, 50, 51, 52, 52, 52, 51, 51, 51, 51, 51, 52, 52, 52, 52, 52, 48, 47, 47, 47, 46, 46, 46, 46, 47, 47, 47, 47, 47, 47, 48, 50, + 50, 50, 52, 53, 53, 53, 53, 53, 53, 53, 53, 53, 54, 54, 54, 54, 49, 48, 47, 47, 46, 45, 45, 45, 45, 46, 46, 45, 45, 45, 47, 49, 49, 50, + 53, 53, 53, 55, 56, 56, 57, 58, 58, 58, 59, 59, 59, 60, 50, 49, 48, 48, 47, 46, 46, 46, 46, 46, 46, 46, 46, 46, 47, 50, 50, 50, 53, 54, + 54, 55, 56, 56, 57, 59, 59, 60, 61, 61, 61, 62, + ], + ], + [ + [ + 32, 32, 34, 38, 32, 33, 35, 39, 34, 35, 39, 45, 38, 39, 45, 54, 31, 31, 32, 32, 33, 34, 37, 41, 31, 32, 32, 32, 33, 34, 36, 39, 32, 32, + 32, 33, 34, 35, 37, 40, 32, 32, 33, 34, 35, 36, 38, 41, 33, 33, 34, 35, 37, 39, 41, 44, 34, 34, 35, 36, 39, 43, 46, 49, 37, 36, 37, 38, + 41, 46, 51, 54, 41, 39, 40, 41, 44, 49, 54, 58, 32, 31, 31, 31, 31, 31, 31, 32, 32, 34, 34, 36, 36, 39, 39, 44, 31, 32, 32, 32, 32, 32, + 32, 32, 32, 34, 34, 35, 35, 38, 38, 42, 31, 32, 32, 32, 32, 32, 32, 32, 32, 34, 34, 35, 35, 38, 38, 42, 31, 32, 32, 32, 32, 32, 32, 32, + 32, 33, 33, 34, 34, 37, 37, 41, 31, 32, 32, 32, 32, 32, 32, 32, 32, 33, 33, 34, 34, 37, 37, 41, 31, 32, 32, 32, 32, 33, 33, 34, 34, 35, + 35, 36, 36, 39, 39, 42, 31, 32, 32, 32, 32, 33, 33, 34, 34, 35, 35, 36, 36, 39, 39, 42, 32, 32, 32, 32, 32, 34, 34, 35, 35, 37, 37, 38, + 38, 40, 40, 42, 32, 32, 32, 32, 32, 34, 34, 35, 35, 37, 37, 38, 38, 40, 40, 42, 34, 34, 34, 33, 33, 35, 35, 37, 37, 39, 39, 42, 42, 45, + 45, 47, 34, 34, 34, 33, 33, 35, 35, 37, 37, 39, 39, 42, 42, 45, 45, 47, 36, 35, 35, 34, 34, 36, 36, 38, 38, 42, 42, 48, 48, 50, 50, 54, + 36, 35, 35, 34, 34, 36, 36, 38, 38, 42, 42, 48, 48, 50, 50, 54, 39, 38, 38, 37, 37, 39, 39, 40, 40, 45, 45, 50, 50, 54, 54, 58, 39, 38, + 38, 37, 37, 39, 39, 40, 40, 45, 45, 50, 50, 54, 54, 58, 44, 42, 42, 41, 41, 42, 42, 42, 42, 47, 47, 54, 54, 58, 58, 63, 32, 31, 31, 31, + 31, 31, 31, 31, 31, 31, 31, 31, 31, 32, 32, 32, 32, 33, 34, 34, 34, 35, 36, 36, 36, 37, 39, 39, 39, 41, 44, 44, 31, 31, 31, 31, 31, 31, + 31, 31, 31, 32, 32, 32, 32, 32, 32, 32, 32, 33, 34, 34, 34, 34, 35, 35, 35, 37, 39, 39, 39, 41, 43, 43, 31, 31, 32, 32, 32, 32, 32, 32, + 32, 32, 32, 32, 32, 32, 32, 32, 32, 33, 34, 34, 34, 34, 35, 35, 35, 37, 38, 38, 38, 40, 42, 42, 31, 31, 32, 32, 32, 32, 32, 32, 32, 32, + 32, 32, 32, 32, 32, 32, 32, 33, 34, 34, 34, 34, 35, 35, 35, 37, 38, 38, 38, 40, 42, 42, 31, 31, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, + 32, 32, 32, 32, 32, 33, 34, 34, 34, 34, 35, 35, 35, 37, 38, 38, 38, 40, 42, 42, 31, 31, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, + 32, 32, 32, 33, 34, 34, 34, 34, 35, 35, 35, 36, 38, 38, 38, 39, 41, 41, 31, 31, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, + 32, 33, 33, 33, 33, 34, 34, 34, 34, 36, 37, 37, 37, 39, 41, 41, 31, 31, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 33, + 33, 33, 33, 34, 34, 34, 34, 36, 37, 37, 37, 39, 41, 41, 31, 31, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 33, 33, 33, + 33, 34, 34, 34, 34, 36, 37, 37, 37, 39, 41, 41, 31, 32, 32, 32, 32, 32, 32, 32, 32, 32, 33, 33, 33, 33, 33, 33, 33, 34, 34, 34, 34, 35, + 35, 35, 35, 37, 38, 38, 38, 40, 41, 41, 31, 32, 32, 32, 32, 32, 32, 32, 32, 33, 33, 33, 33, 33, 34, 34, 34, 34, 35, 35, 35, 36, 36, 36, + 36, 38, 39, 39, 39, 40, 42, 42, 31, 32, 32, 32, 32, 32, 32, 32, 32, 33, 33, 33, 33, 33, 34, 34, 34, 34, 35, 35, 35, 36, 36, 36, 36, 38, + 39, 39, 39, 40, 42, 42, 31, 32, 32, 32, 32, 32, 32, 32, 32, 33, 33, 33, 33, 33, 34, 34, 34, 34, 35, 35, 35, 36, 36, 36, 36, 38, 39, 39, + 39, 40, 42, 42, 32, 32, 32, 32, 32, 32, 32, 32, 32, 33, 33, 33, 33, 34, 34, 34, 34, 35, 36, 36, 36, 36, 37, 37, 37, 38, 40, 40, 40, 41, + 42, 42, 32, 32, 32, 32, 32, 32, 32, 32, 32, 33, 34, 34, 34, 34, 35, 35, 35, 36, 37, 37, 37, 37, 38, 38, 38, 39, 40, 40, 40, 41, 42, 42, + 32, 32, 32, 32, 32, 32, 32, 32, 32, 33, 34, 34, 34, 34, 35, 35, 35, 36, 37, 37, 37, 37, 38, 38, 38, 39, 40, 40, 40, 41, 42, 42, 32, 32, + 32, 32, 32, 32, 32, 32, 32, 33, 34, 34, 34, 34, 35, 35, 35, 36, 37, 37, 37, 37, 38, 38, 38, 39, 40, 40, 40, 41, 42, 42, 33, 33, 33, 33, + 33, 33, 33, 33, 33, 34, 34, 34, 34, 35, 36, 36, 36, 37, 38, 38, 38, 39, 40, 40, 40, 41, 42, 42, 42, 44, 45, 45, 34, 34, 34, 34, 34, 34, + 33, 33, 33, 34, 35, 35, 35, 36, 37, 37, 37, 38, 39, 39, 39, 41, 42, 42, 42, 44, 45, 45, 45, 46, 47, 47, 34, 34, 34, 34, 34, 34, 33, 33, + 33, 34, 35, 35, 35, 36, 37, 37, 37, 38, 39, 39, 39, 41, 42, 42, 42, 44, 45, 45, 45, 46, 47, 47, 34, 34, 34, 34, 34, 34, 33, 33, 33, 34, + 35, 35, 35, 36, 37, 37, 37, 38, 39, 39, 39, 41, 42, 42, 42, 44, 45, 45, 45, 46, 47, 47, 35, 34, 34, 34, 34, 34, 34, 34, 34, 35, 36, 36, + 36, 36, 37, 37, 37, 39, 41, 41, 41, 43, 45, 45, 45, 46, 47, 47, 47, 49, 50, 50, 36, 35, 35, 35, 35, 35, 34, 34, 34, 35, 36, 36, 36, 37, + 38, 38, 38, 40, 42, 42, 42, 45, 48, 48, 48, 49, 50, 50, 50, 52, 54, 54, 36, 35, 35, 35, 35, 35, 34, 34, 34, 35, 36, 36, 36, 37, 38, 38, + 38, 40, 42, 42, 42, 45, 48, 48, 48, 49, 50, 50, 50, 52, 54, 54, 36, 35, 35, 35, 35, 35, 34, 34, 34, 35, 36, 36, 36, 37, 38, 38, 38, 40, + 42, 42, 42, 45, 48, 48, 48, 49, 50, 50, 50, 52, 54, 54, 37, 37, 37, 37, 37, 36, 36, 36, 36, 37, 38, 38, 38, 38, 39, 39, 39, 41, 44, 44, + 44, 46, 49, 49, 49, 51, 52, 52, 52, 54, 56, 56, 39, 39, 38, 38, 38, 38, 37, 37, 37, 38, 39, 39, 39, 40, 40, 40, 40, 42, 45, 45, 45, 47, + 50, 50, 50, 52, 54, 54, 54, 56, 58, 58, 39, 39, 38, 38, 38, 38, 37, 37, 37, 38, 39, 39, 39, 40, 40, 40, 40, 42, 45, 45, 45, 47, 50, 50, + 50, 52, 54, 54, 54, 56, 58, 58, 39, 39, 38, 38, 38, 38, 37, 37, 37, 38, 39, 39, 39, 40, 40, 40, 40, 42, 45, 45, 45, 47, 50, 50, 50, 52, + 54, 54, 54, 56, 58, 58, 41, 41, 40, 40, 40, 39, 39, 39, 39, 40, 40, 40, 40, 41, 41, 41, 41, 44, 46, 46, 46, 49, 52, 52, 52, 54, 56, 56, + 56, 58, 60, 60, 44, 43, 42, 42, 42, 41, 41, 41, 41, 41, 42, 42, 42, 42, 42, 42, 42, 45, 47, 47, 47, 50, 54, 54, 54, 56, 58, 58, 58, 60, + 63, 63, 44, 43, 42, 42, 42, 41, 41, 41, 41, 41, 42, 42, 42, 42, 42, 42, 42, 45, 47, 47, 47, 50, 54, 54, 54, 56, 58, 58, 58, 60, 63, 63, + 31, 32, 34, 39, 32, 32, 34, 38, 32, 33, 34, 38, 32, 33, 36, 40, 33, 34, 38, 42, 34, 36, 41, 47, 37, 38, 44, 52, 40, 40, 46, 56, 31, 32, + 32, 32, 33, 34, 37, 40, 32, 32, 33, 33, 34, 36, 38, 40, 34, 34, 34, 36, 38, 41, 44, 46, 39, 38, 38, 40, 42, 47, 52, 56, 32, 31, 31, 32, + 32, 36, 36, 44, 31, 32, 32, 32, 32, 35, 35, 42, 31, 32, 32, 32, 32, 35, 35, 42, 31, 32, 32, 33, 33, 34, 34, 41, 31, 32, 32, 33, 33, 34, + 34, 41, 32, 32, 32, 34, 34, 36, 36, 42, 32, 32, 32, 34, 34, 36, 36, 42, 32, 33, 33, 35, 35, 38, 38, 42, 32, 33, 33, 35, 35, 38, 38, 42, + 34, 34, 34, 37, 37, 42, 42, 48, 34, 34, 34, 37, 37, 42, 42, 48, 36, 34, 34, 38, 38, 48, 48, 54, 36, 34, 34, 38, 38, 48, 48, 54, 39, 37, + 37, 40, 40, 50, 50, 58, 39, 37, 37, 40, 40, 50, 50, 58, 44, 41, 41, 43, 43, 53, 53, 63, 32, 31, 31, 31, 31, 32, 32, 32, 32, 34, 34, 36, + 36, 39, 39, 44, 31, 32, 32, 32, 32, 32, 32, 33, 33, 34, 34, 34, 34, 37, 37, 41, 31, 32, 32, 32, 32, 32, 32, 33, 33, 34, 34, 34, 34, 37, + 37, 41, 32, 32, 32, 33, 33, 34, 34, 35, 35, 37, 37, 38, 38, 40, 40, 43, 32, 32, 32, 33, 33, 34, 34, 35, 35, 37, 37, 38, 38, 40, 40, 43, + 36, 35, 35, 34, 34, 36, 36, 38, 38, 42, 42, 48, 48, 50, 50, 53, 36, 35, 35, 34, 34, 36, 36, 38, 38, 42, 42, 48, 48, 50, 50, 53, 44, 42, + 42, 41, 41, 42, 42, 42, 42, 48, 48, 54, 54, 58, 58, 63, 32, 31, 31, 31, 31, 32, 32, 32, 32, 34, 36, 36, 36, 39, 44, 44, 31, 31, 31, 31, + 31, 32, 32, 32, 32, 34, 35, 35, 35, 39, 43, 43, 31, 32, 32, 32, 32, 32, 32, 32, 32, 34, 35, 35, 35, 38, 42, 42, 31, 32, 32, 32, 32, 32, + 32, 32, 32, 34, 35, 35, 35, 38, 42, 42, 31, 32, 32, 32, 32, 32, 32, 32, 32, 34, 35, 35, 35, 38, 42, 42, 31, 32, 32, 32, 32, 32, 32, 32, + 32, 34, 35, 35, 35, 38, 41, 41, 31, 32, 32, 32, 32, 32, 33, 33, 33, 33, 34, 34, 34, 37, 41, 41, 31, 32, 32, 32, 32, 32, 33, 33, 33, 33, + 34, 34, 34, 37, 41, 41, 31, 32, 32, 32, 32, 32, 33, 33, 33, 33, 34, 34, 34, 37, 41, 41, 31, 32, 32, 32, 32, 33, 33, 33, 33, 34, 35, 35, + 35, 38, 41, 41, 32, 32, 32, 32, 32, 33, 34, 34, 34, 35, 36, 36, 36, 39, 42, 42, 32, 32, 32, 32, 32, 33, 34, 34, 34, 35, 36, 36, 36, 39, + 42, 42, 32, 32, 32, 32, 32, 33, 34, 34, 34, 35, 36, 36, 36, 39, 42, 42, 32, 32, 32, 32, 32, 33, 34, 34, 34, 36, 37, 37, 37, 40, 42, 42, + 32, 32, 33, 33, 33, 34, 35, 35, 35, 37, 38, 38, 38, 40, 42, 42, 32, 32, 33, 33, 33, 34, 35, 35, 35, 37, 38, 38, 38, 40, 42, 42, 32, 32, + 33, 33, 33, 34, 35, 35, 35, 37, 38, 38, 38, 40, 42, 42, 33, 33, 33, 33, 33, 34, 36, 36, 36, 38, 40, 40, 40, 42, 45, 45, 34, 34, 34, 34, + 34, 35, 37, 37, 37, 39, 42, 42, 42, 45, 48, 48, 34, 34, 34, 34, 34, 35, 37, 37, 37, 39, 42, 42, 42, 45, 48, 48, 34, 34, 34, 34, 34, 35, + 37, 37, 37, 39, 42, 42, 42, 45, 48, 48, 35, 34, 34, 34, 34, 36, 37, 37, 37, 41, 45, 45, 45, 47, 50, 50, 36, 35, 34, 34, 34, 36, 38, 38, + 38, 43, 48, 48, 48, 51, 54, 54, 36, 35, 34, 34, 34, 36, 38, 38, 38, 43, 48, 48, 48, 51, 54, 54, 36, 35, 34, 34, 34, 36, 38, 38, 38, 43, + 48, 48, 48, 51, 54, 54, 37, 37, 36, 36, 36, 38, 39, 39, 39, 44, 49, 49, 49, 52, 56, 56, 39, 38, 37, 37, 37, 39, 40, 40, 40, 45, 50, 50, + 50, 54, 58, 58, 39, 38, 37, 37, 37, 39, 40, 40, 40, 45, 50, 50, 50, 54, 58, 58, 39, 38, 37, 37, 37, 39, 40, 40, 40, 45, 50, 50, 50, 54, + 58, 58, 41, 40, 39, 39, 39, 40, 42, 42, 42, 46, 52, 52, 52, 56, 60, 60, 44, 42, 41, 41, 41, 42, 43, 43, 43, 48, 53, 53, 53, 58, 63, 63, + 44, 42, 41, 41, 41, 42, 43, 43, 43, 48, 53, 53, 53, 58, 63, 63, 32, 31, 31, 31, 31, 31, 31, 31, 31, 31, 32, 32, 32, 32, 32, 32, 32, 33, + 34, 34, 34, 35, 36, 36, 36, 37, 39, 39, 39, 41, 44, 44, 31, 31, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 33, 34, 34, + 34, 34, 35, 35, 35, 37, 38, 38, 38, 40, 42, 42, 31, 31, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 33, 33, 33, 33, 34, 34, 34, 34, + 34, 34, 34, 36, 37, 37, 37, 39, 41, 41, 31, 31, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 33, 33, 33, 33, 34, 34, 34, 34, 34, 34, + 34, 36, 37, 37, 37, 39, 41, 41, 31, 31, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 33, 33, 33, 33, 34, 34, 34, 34, 34, 34, 34, 36, + 37, 37, 37, 39, 41, 41, 32, 32, 32, 32, 32, 32, 32, 32, 32, 33, 33, 33, 33, 33, 34, 34, 34, 34, 35, 35, 35, 36, 36, 36, 36, 38, 39, 39, + 39, 40, 42, 42, 32, 32, 32, 32, 32, 32, 33, 33, 33, 33, 34, 34, 34, 34, 35, 35, 35, 36, 37, 37, 37, 37, 38, 38, 38, 39, 40, 40, 40, 42, + 43, 43, 32, 32, 32, 32, 32, 32, 33, 33, 33, 33, 34, 34, 34, 34, 35, 35, 35, 36, 37, 37, 37, 37, 38, 38, 38, 39, 40, 40, 40, 42, 43, 43, + 32, 32, 32, 32, 32, 32, 33, 33, 33, 33, 34, 34, 34, 34, 35, 35, 35, 36, 37, 37, 37, 37, 38, 38, 38, 39, 40, 40, 40, 42, 43, 43, 34, 34, + 34, 34, 34, 34, 33, 33, 33, 34, 35, 35, 35, 36, 37, 37, 37, 38, 39, 39, 39, 41, 43, 43, 43, 44, 45, 45, 45, 46, 48, 48, 36, 35, 35, 35, + 35, 35, 34, 34, 34, 35, 36, 36, 36, 37, 38, 38, 38, 40, 42, 42, 42, 45, 48, 48, 48, 49, 50, 50, 50, 52, 53, 53, 36, 35, 35, 35, 35, 35, + 34, 34, 34, 35, 36, 36, 36, 37, 38, 38, 38, 40, 42, 42, 42, 45, 48, 48, 48, 49, 50, 50, 50, 52, 53, 53, 36, 35, 35, 35, 35, 35, 34, 34, + 34, 35, 36, 36, 36, 37, 38, 38, 38, 40, 42, 42, 42, 45, 48, 48, 48, 49, 50, 50, 50, 52, 53, 53, 39, 39, 38, 38, 38, 38, 37, 37, 37, 38, + 39, 39, 39, 40, 40, 40, 40, 42, 45, 45, 45, 47, 51, 51, 51, 52, 54, 54, 54, 56, 58, 58, 44, 43, 42, 42, 42, 41, 41, 41, 41, 41, 42, 42, + 42, 42, 42, 42, 42, 45, 48, 48, 48, 50, 54, 54, 54, 56, 58, 58, 58, 60, 63, 63, 44, 43, 42, 42, 42, 41, 41, 41, 41, 41, 42, 42, 42, 42, + 42, 42, 42, 45, 48, 48, 48, 50, 54, 54, 54, 56, 58, 58, 58, 60, 63, 63, 31, 32, 34, 39, 32, 32, 34, 38, 32, 32, 34, 38, 32, 32, 33, 37, + 32, 32, 33, 37, 32, 33, 35, 39, 32, 33, 35, 39, 32, 34, 37, 40, 32, 34, 37, 40, 34, 35, 39, 45, 34, 35, 39, 45, 35, 36, 43, 51, 35, 36, + 43, 51, 38, 39, 45, 54, 38, 39, 45, 54, 42, 42, 48, 58, 31, 32, 32, 32, 32, 32, 32, 32, 32, 34, 34, 35, 35, 38, 38, 42, 32, 32, 32, 32, + 32, 33, 33, 34, 34, 35, 35, 36, 36, 39, 39, 42, 34, 34, 34, 33, 33, 35, 35, 37, 37, 39, 39, 43, 43, 45, 45, 48, 39, 38, 38, 37, 37, 39, + 39, 40, 40, 45, 45, 51, 51, 54, 54, 58, 32, 31, 31, 32, 32, 36, 36, 44, 31, 31, 31, 32, 32, 35, 35, 43, 31, 32, 32, 32, 32, 35, 35, 42, + 31, 32, 32, 32, 32, 35, 35, 42, 31, 32, 32, 32, 32, 35, 35, 42, 31, 32, 32, 32, 32, 35, 35, 41, 31, 32, 32, 33, 33, 34, 34, 41, 31, 32, + 32, 33, 33, 34, 34, 41, 31, 32, 32, 33, 33, 34, 34, 41, 31, 32, 32, 33, 33, 35, 35, 41, 32, 32, 32, 34, 34, 36, 36, 42, 32, 32, 32, 34, + 34, 36, 36, 42, 32, 32, 32, 34, 34, 36, 36, 42, 32, 32, 32, 34, 34, 37, 37, 42, 32, 33, 33, 35, 35, 38, 38, 42, 32, 33, 33, 35, 35, 38, + 38, 42, 32, 33, 33, 35, 35, 38, 38, 42, 33, 33, 33, 36, 36, 40, 40, 45, 34, 34, 34, 37, 37, 42, 42, 48, 34, 34, 34, 37, 37, 42, 42, 48, + 34, 34, 34, 37, 37, 42, 42, 48, 35, 34, 34, 37, 37, 45, 45, 50, 36, 34, 34, 38, 38, 48, 48, 54, 36, 34, 34, 38, 38, 48, 48, 54, 36, 34, + 34, 38, 38, 48, 48, 54, 37, 36, 36, 39, 39, 49, 49, 56, 39, 37, 37, 40, 40, 50, 50, 58, 39, 37, 37, 40, 40, 50, 50, 58, 39, 37, 37, 40, + 40, 50, 50, 58, 41, 39, 39, 42, 42, 52, 52, 60, 44, 41, 41, 43, 43, 53, 53, 63, 44, 41, 41, 43, 43, 53, 53, 63, 32, 31, 31, 31, 31, 31, + 31, 31, 31, 31, 32, 32, 32, 32, 32, 32, 32, 33, 34, 34, 34, 35, 36, 36, 36, 37, 39, 39, 39, 41, 44, 44, 31, 31, 32, 32, 32, 32, 32, 32, + 32, 32, 32, 32, 32, 32, 33, 33, 33, 33, 34, 34, 34, 34, 34, 34, 34, 36, 37, 37, 37, 39, 41, 41, 31, 31, 32, 32, 32, 32, 32, 32, 32, 32, + 32, 32, 32, 32, 33, 33, 33, 33, 34, 34, 34, 34, 34, 34, 34, 36, 37, 37, 37, 39, 41, 41, 32, 32, 32, 32, 32, 32, 33, 33, 33, 33, 34, 34, + 34, 34, 35, 35, 35, 36, 37, 37, 37, 37, 38, 38, 38, 39, 40, 40, 40, 42, 43, 43, 32, 32, 32, 32, 32, 32, 33, 33, 33, 33, 34, 34, 34, 34, + 35, 35, 35, 36, 37, 37, 37, 37, 38, 38, 38, 39, 40, 40, 40, 42, 43, 43, 36, 35, 35, 35, 35, 35, 34, 34, 34, 35, 36, 36, 36, 37, 38, 38, + 38, 40, 42, 42, 42, 45, 48, 48, 48, 49, 50, 50, 50, 52, 53, 53, 36, 35, 35, 35, 35, 35, 34, 34, 34, 35, 36, 36, 36, 37, 38, 38, 38, 40, + 42, 42, 42, 45, 48, 48, 48, 49, 50, 50, 50, 52, 53, 53, 44, 43, 42, 42, 42, 41, 41, 41, 41, 41, 42, 42, 42, 42, 42, 42, 42, 45, 48, 48, + 48, 50, 54, 54, 54, 56, 58, 58, 58, 60, 63, 63, + ], + [ + 31, 34, 42, 47, 34, 39, 45, 46, 42, 45, 48, 49, 47, 46, 49, 54, 31, 31, 32, 35, 39, 45, 48, 48, 31, 31, 33, 37, 41, 44, 46, 46, 32, 33, + 35, 39, 42, 45, 46, 45, 35, 37, 39, 43, 45, 47, 47, 46, 39, 41, 42, 45, 47, 48, 48, 47, 45, 44, 45, 47, 48, 50, 51, 51, 48, 46, 46, 47, + 48, 51, 53, 54, 48, 46, 45, 46, 47, 51, 54, 56, 32, 31, 31, 30, 30, 33, 33, 36, 36, 41, 41, 49, 49, 48, 48, 49, 31, 31, 31, 31, 31, 34, + 34, 38, 38, 42, 42, 47, 47, 47, 47, 47, 31, 31, 31, 31, 31, 34, 34, 38, 38, 42, 42, 47, 47, 47, 47, 47, 30, 31, 31, 32, 32, 35, 35, 40, + 40, 42, 42, 46, 46, 45, 45, 45, 30, 31, 31, 32, 32, 35, 35, 40, 40, 42, 42, 46, 46, 45, 45, 45, 33, 34, 34, 35, 35, 39, 39, 43, 43, 45, + 45, 47, 47, 46, 46, 45, 33, 34, 34, 35, 35, 39, 39, 43, 43, 45, 45, 47, 47, 46, 46, 45, 36, 38, 38, 40, 40, 43, 43, 47, 47, 47, 47, 48, + 48, 46, 46, 45, 36, 38, 38, 40, 40, 43, 43, 47, 47, 47, 47, 48, 48, 46, 46, 45, 41, 42, 42, 42, 42, 45, 45, 47, 47, 48, 48, 50, 50, 49, + 49, 49, 41, 42, 42, 42, 42, 45, 45, 47, 47, 48, 48, 50, 50, 49, 49, 49, 49, 47, 47, 46, 46, 47, 47, 48, 48, 50, 50, 53, 53, 53, 53, 53, + 49, 47, 47, 46, 46, 47, 47, 48, 48, 50, 50, 53, 53, 53, 53, 53, 48, 47, 47, 45, 45, 46, 46, 46, 46, 49, 49, 53, 53, 54, 54, 55, 48, 47, + 47, 45, 45, 46, 46, 46, 46, 49, 49, 53, 53, 54, 54, 55, 49, 47, 47, 45, 45, 45, 45, 45, 45, 49, 49, 53, 53, 55, 55, 58, 32, 31, 31, 31, + 31, 31, 30, 30, 30, 32, 33, 33, 33, 35, 36, 36, 36, 39, 41, 41, 41, 45, 49, 49, 49, 49, 48, 48, 48, 49, 49, 49, 31, 31, 31, 31, 31, 31, + 31, 31, 31, 32, 34, 34, 34, 35, 37, 37, 37, 39, 42, 42, 42, 45, 48, 48, 48, 48, 48, 48, 48, 48, 48, 48, 31, 31, 31, 31, 31, 31, 31, 31, + 31, 33, 34, 34, 34, 36, 38, 38, 38, 40, 42, 42, 42, 45, 47, 47, 47, 47, 47, 47, 47, 47, 47, 47, 31, 31, 31, 31, 31, 31, 31, 31, 31, 33, + 34, 34, 34, 36, 38, 38, 38, 40, 42, 42, 42, 45, 47, 47, 47, 47, 47, 47, 47, 47, 47, 47, 31, 31, 31, 31, 31, 31, 31, 31, 31, 33, 34, 34, + 34, 36, 38, 38, 38, 40, 42, 42, 42, 45, 47, 47, 47, 47, 47, 47, 47, 47, 47, 47, 31, 31, 31, 31, 31, 31, 31, 31, 31, 33, 35, 35, 35, 37, + 39, 39, 39, 41, 42, 42, 42, 44, 47, 47, 47, 46, 46, 46, 46, 46, 46, 46, 30, 31, 31, 31, 31, 31, 32, 32, 32, 33, 35, 35, 35, 37, 40, 40, + 40, 41, 42, 42, 42, 44, 46, 46, 46, 46, 45, 45, 45, 45, 45, 45, 30, 31, 31, 31, 31, 31, 32, 32, 32, 33, 35, 35, 35, 37, 40, 40, 40, 41, + 42, 42, 42, 44, 46, 46, 46, 46, 45, 45, 45, 45, 45, 45, 30, 31, 31, 31, 31, 31, 32, 32, 32, 33, 35, 35, 35, 37, 40, 40, 40, 41, 42, 42, + 42, 44, 46, 46, 46, 46, 45, 45, 45, 45, 45, 45, 32, 32, 33, 33, 33, 33, 33, 33, 33, 35, 37, 37, 37, 39, 41, 41, 41, 42, 43, 43, 43, 45, + 47, 47, 47, 46, 46, 46, 46, 45, 45, 45, 33, 34, 34, 34, 34, 35, 35, 35, 35, 37, 39, 39, 39, 41, 43, 43, 43, 44, 45, 45, 45, 46, 47, 47, + 47, 47, 46, 46, 46, 46, 45, 45, 33, 34, 34, 34, 34, 35, 35, 35, 35, 37, 39, 39, 39, 41, 43, 43, 43, 44, 45, 45, 45, 46, 47, 47, 47, 47, + 46, 46, 46, 46, 45, 45, 33, 34, 34, 34, 34, 35, 35, 35, 35, 37, 39, 39, 39, 41, 43, 43, 43, 44, 45, 45, 45, 46, 47, 47, 47, 47, 46, 46, + 46, 46, 45, 45, 35, 35, 36, 36, 36, 37, 37, 37, 37, 39, 41, 41, 41, 43, 45, 45, 45, 45, 46, 46, 46, 47, 47, 47, 47, 47, 46, 46, 46, 46, + 45, 45, 36, 37, 38, 38, 38, 39, 40, 40, 40, 41, 43, 43, 43, 45, 47, 47, 47, 47, 47, 47, 47, 47, 48, 48, 48, 47, 46, 46, 46, 46, 45, 45, + 36, 37, 38, 38, 38, 39, 40, 40, 40, 41, 43, 43, 43, 45, 47, 47, 47, 47, 47, 47, 47, 47, 48, 48, 48, 47, 46, 46, 46, 46, 45, 45, 36, 37, + 38, 38, 38, 39, 40, 40, 40, 41, 43, 43, 43, 45, 47, 47, 47, 47, 47, 47, 47, 47, 48, 48, 48, 47, 46, 46, 46, 46, 45, 45, 39, 39, 40, 40, + 40, 41, 41, 41, 41, 42, 44, 44, 44, 45, 47, 47, 47, 47, 48, 48, 48, 48, 49, 49, 49, 48, 48, 48, 48, 47, 47, 47, 41, 42, 42, 42, 42, 42, + 42, 42, 42, 43, 45, 45, 45, 46, 47, 47, 47, 48, 48, 48, 48, 49, 50, 50, 50, 50, 49, 49, 49, 49, 49, 49, 41, 42, 42, 42, 42, 42, 42, 42, + 42, 43, 45, 45, 45, 46, 47, 47, 47, 48, 48, 48, 48, 49, 50, 50, 50, 50, 49, 49, 49, 49, 49, 49, 41, 42, 42, 42, 42, 42, 42, 42, 42, 43, + 45, 45, 45, 46, 47, 47, 47, 48, 48, 48, 48, 49, 50, 50, 50, 50, 49, 49, 49, 49, 49, 49, 45, 45, 45, 45, 45, 44, 44, 44, 44, 45, 46, 46, + 46, 47, 47, 47, 47, 48, 49, 49, 49, 50, 51, 51, 51, 51, 51, 51, 51, 51, 51, 51, 49, 48, 47, 47, 47, 47, 46, 46, 46, 47, 47, 47, 47, 47, + 48, 48, 48, 49, 50, 50, 50, 51, 53, 53, 53, 53, 53, 53, 53, 53, 53, 53, 49, 48, 47, 47, 47, 47, 46, 46, 46, 47, 47, 47, 47, 47, 48, 48, + 48, 49, 50, 50, 50, 51, 53, 53, 53, 53, 53, 53, 53, 53, 53, 53, 49, 48, 47, 47, 47, 47, 46, 46, 46, 47, 47, 47, 47, 47, 48, 48, 48, 49, + 50, 50, 50, 51, 53, 53, 53, 53, 53, 53, 53, 53, 53, 53, 49, 48, 47, 47, 47, 46, 46, 46, 46, 46, 47, 47, 47, 47, 47, 47, 47, 48, 50, 50, + 50, 51, 53, 53, 53, 53, 53, 53, 53, 54, 54, 54, 48, 48, 47, 47, 47, 46, 45, 45, 45, 46, 46, 46, 46, 46, 46, 46, 46, 48, 49, 49, 49, 51, + 53, 53, 53, 53, 54, 54, 54, 55, 55, 55, 48, 48, 47, 47, 47, 46, 45, 45, 45, 46, 46, 46, 46, 46, 46, 46, 46, 48, 49, 49, 49, 51, 53, 53, + 53, 53, 54, 54, 54, 55, 55, 55, 48, 48, 47, 47, 47, 46, 45, 45, 45, 46, 46, 46, 46, 46, 46, 46, 46, 48, 49, 49, 49, 51, 53, 53, 53, 53, + 54, 54, 54, 55, 55, 55, 49, 48, 47, 47, 47, 46, 45, 45, 45, 45, 46, 46, 46, 46, 46, 46, 46, 47, 49, 49, 49, 51, 53, 53, 53, 54, 55, 55, + 55, 56, 57, 57, 49, 48, 47, 47, 47, 46, 45, 45, 45, 45, 45, 45, 45, 45, 45, 45, 45, 47, 49, 49, 49, 51, 53, 53, 53, 54, 55, 55, 55, 57, + 58, 58, 49, 48, 47, 47, 47, 46, 45, 45, 45, 45, 45, 45, 45, 45, 45, 45, 45, 47, 49, 49, 49, 51, 53, 53, 53, 54, 55, 55, 55, 57, 58, 58, + 31, 34, 42, 48, 31, 35, 42, 46, 33, 37, 44, 46, 36, 41, 46, 46, 40, 44, 48, 48, 45, 46, 49, 51, 47, 47, 50, 54, 47, 46, 49, 55, 31, 31, + 33, 36, 40, 45, 47, 47, 34, 35, 37, 41, 44, 46, 47, 46, 42, 42, 44, 46, 48, 49, 50, 49, 48, 46, 46, 46, 48, 51, 54, 55, 32, 31, 31, 37, + 37, 48, 48, 49, 31, 31, 31, 38, 38, 47, 47, 47, 31, 31, 31, 38, 38, 47, 47, 47, 30, 32, 32, 40, 40, 46, 46, 45, 30, 32, 32, 40, 40, 46, + 46, 45, 33, 36, 36, 43, 43, 47, 47, 46, 33, 36, 36, 43, 43, 47, 47, 46, 37, 40, 40, 47, 47, 47, 47, 45, 37, 40, 40, 47, 47, 47, 47, 45, + 42, 43, 43, 47, 47, 50, 50, 49, 42, 43, 43, 47, 47, 50, 50, 49, 49, 46, 46, 48, 48, 53, 53, 53, 49, 46, 46, 48, 48, 53, 53, 53, 48, 46, + 46, 47, 47, 53, 53, 56, 48, 46, 46, 47, 47, 53, 53, 56, 49, 45, 45, 46, 46, 53, 53, 58, 32, 31, 31, 30, 30, 33, 33, 37, 37, 42, 42, 49, + 49, 48, 48, 49, 31, 31, 31, 32, 32, 36, 36, 40, 40, 43, 43, 46, 46, 46, 46, 45, 31, 31, 31, 32, 32, 36, 36, 40, 40, 43, 43, 46, 46, 46, + 46, 45, 37, 38, 38, 40, 40, 43, 43, 47, 47, 47, 47, 48, 48, 47, 47, 46, 37, 38, 38, 40, 40, 43, 43, 47, 47, 47, 47, 48, 48, 47, 47, 46, + 48, 47, 47, 46, 46, 47, 47, 47, 47, 50, 50, 53, 53, 53, 53, 53, 48, 47, 47, 46, 46, 47, 47, 47, 47, 50, 50, 53, 53, 53, 53, 53, 49, 47, + 47, 45, 45, 46, 46, 45, 45, 49, 49, 53, 53, 56, 56, 58, 32, 31, 31, 31, 31, 33, 37, 37, 37, 42, 48, 48, 48, 48, 49, 49, 31, 31, 31, 31, + 31, 34, 37, 37, 37, 42, 47, 47, 47, 48, 48, 48, 31, 31, 31, 31, 31, 34, 38, 38, 38, 42, 47, 47, 47, 47, 47, 47, 31, 31, 31, 31, 31, 34, + 38, 38, 38, 42, 47, 47, 47, 47, 47, 47, 31, 31, 31, 31, 31, 34, 38, 38, 38, 42, 47, 47, 47, 47, 47, 47, 31, 31, 32, 32, 32, 35, 39, 39, + 39, 42, 46, 46, 46, 46, 46, 46, 30, 31, 32, 32, 32, 35, 40, 40, 40, 42, 46, 46, 46, 45, 45, 45, 30, 31, 32, 32, 32, 35, 40, 40, 40, 42, + 46, 46, 46, 45, 45, 45, 30, 31, 32, 32, 32, 35, 40, 40, 40, 42, 46, 46, 46, 45, 45, 45, 32, 33, 34, 34, 34, 37, 41, 41, 41, 44, 46, 46, + 46, 46, 45, 45, 33, 34, 36, 36, 36, 39, 43, 43, 43, 45, 47, 47, 47, 46, 46, 46, 33, 34, 36, 36, 36, 39, 43, 43, 43, 45, 47, 47, 47, 46, + 46, 46, 33, 34, 36, 36, 36, 39, 43, 43, 43, 45, 47, 47, 47, 46, 46, 46, 35, 36, 38, 38, 38, 41, 45, 45, 45, 46, 47, 47, 47, 46, 45, 45, + 37, 38, 40, 40, 40, 43, 47, 47, 47, 47, 47, 47, 47, 46, 45, 45, 37, 38, 40, 40, 40, 43, 47, 47, 47, 47, 47, 47, 47, 46, 45, 45, 37, 38, + 40, 40, 40, 43, 47, 47, 47, 47, 47, 47, 47, 46, 45, 45, 39, 40, 41, 41, 41, 44, 47, 47, 47, 48, 49, 49, 49, 48, 47, 47, 42, 42, 43, 43, + 43, 45, 47, 47, 47, 48, 50, 50, 50, 50, 49, 49, 42, 42, 43, 43, 43, 45, 47, 47, 47, 48, 50, 50, 50, 50, 49, 49, 42, 42, 43, 43, 43, 45, + 47, 47, 47, 48, 50, 50, 50, 50, 49, 49, 45, 45, 44, 44, 44, 46, 47, 47, 47, 49, 51, 51, 51, 51, 51, 51, 49, 48, 46, 46, 46, 47, 48, 48, + 48, 50, 53, 53, 53, 53, 53, 53, 49, 48, 46, 46, 46, 47, 48, 48, 48, 50, 53, 53, 53, 53, 53, 53, 49, 48, 46, 46, 46, 47, 48, 48, 48, 50, + 53, 53, 53, 53, 53, 53, 48, 47, 46, 46, 46, 47, 47, 47, 47, 50, 53, 53, 53, 54, 54, 54, 48, 47, 46, 46, 46, 46, 47, 47, 47, 50, 53, 53, + 53, 54, 56, 56, 48, 47, 46, 46, 46, 46, 47, 47, 47, 50, 53, 53, 53, 54, 56, 56, 48, 47, 46, 46, 46, 46, 47, 47, 47, 50, 53, 53, 53, 54, + 56, 56, 48, 47, 45, 45, 45, 46, 46, 46, 46, 49, 53, 53, 53, 55, 57, 57, 49, 47, 45, 45, 45, 45, 46, 46, 46, 49, 53, 53, 53, 56, 58, 58, + 49, 47, 45, 45, 45, 45, 46, 46, 46, 49, 53, 53, 53, 56, 58, 58, 32, 31, 31, 31, 31, 31, 30, 30, 30, 32, 33, 33, 33, 35, 37, 37, 37, 39, + 42, 42, 42, 45, 49, 49, 49, 48, 48, 48, 48, 48, 49, 49, 31, 31, 31, 31, 31, 31, 31, 31, 31, 33, 34, 34, 34, 36, 38, 38, 38, 40, 42, 42, + 42, 45, 48, 48, 48, 47, 47, 47, 47, 47, 47, 47, 31, 31, 31, 31, 31, 32, 32, 32, 32, 34, 36, 36, 36, 38, 40, 40, 40, 41, 43, 43, 43, 44, + 46, 46, 46, 46, 46, 46, 46, 45, 45, 45, 31, 31, 31, 31, 31, 32, 32, 32, 32, 34, 36, 36, 36, 38, 40, 40, 40, 41, 43, 43, 43, 44, 46, 46, + 46, 46, 46, 46, 46, 45, 45, 45, 31, 31, 31, 31, 31, 32, 32, 32, 32, 34, 36, 36, 36, 38, 40, 40, 40, 41, 43, 43, 43, 44, 46, 46, 46, 46, + 46, 46, 46, 45, 45, 45, 33, 34, 34, 34, 34, 35, 35, 35, 35, 37, 39, 39, 39, 41, 43, 43, 43, 44, 45, 45, 45, 46, 47, 47, 47, 47, 46, 46, + 46, 46, 45, 45, 37, 37, 38, 38, 38, 39, 40, 40, 40, 41, 43, 43, 43, 45, 47, 47, 47, 47, 47, 47, 47, 47, 48, 48, 48, 47, 47, 47, 47, 46, + 46, 46, 37, 37, 38, 38, 38, 39, 40, 40, 40, 41, 43, 43, 43, 45, 47, 47, 47, 47, 47, 47, 47, 47, 48, 48, 48, 47, 47, 47, 47, 46, 46, 46, + 37, 37, 38, 38, 38, 39, 40, 40, 40, 41, 43, 43, 43, 45, 47, 47, 47, 47, 47, 47, 47, 47, 48, 48, 48, 47, 47, 47, 47, 46, 46, 46, 42, 42, + 42, 42, 42, 42, 42, 42, 42, 44, 45, 45, 45, 46, 47, 47, 47, 48, 48, 48, 48, 49, 50, 50, 50, 50, 50, 50, 50, 49, 49, 49, 48, 47, 47, 47, + 47, 46, 46, 46, 46, 46, 47, 47, 47, 47, 47, 47, 47, 49, 50, 50, 50, 51, 53, 53, 53, 53, 53, 53, 53, 53, 53, 53, 48, 47, 47, 47, 47, 46, + 46, 46, 46, 46, 47, 47, 47, 47, 47, 47, 47, 49, 50, 50, 50, 51, 53, 53, 53, 53, 53, 53, 53, 53, 53, 53, 48, 47, 47, 47, 47, 46, 46, 46, + 46, 46, 47, 47, 47, 47, 47, 47, 47, 49, 50, 50, 50, 51, 53, 53, 53, 53, 53, 53, 53, 53, 53, 53, 48, 48, 47, 47, 47, 46, 45, 45, 45, 46, + 46, 46, 46, 46, 46, 46, 46, 48, 50, 50, 50, 51, 53, 53, 53, 54, 54, 54, 54, 55, 56, 56, 49, 48, 47, 47, 47, 46, 45, 45, 45, 45, 46, 46, + 46, 45, 45, 45, 45, 47, 49, 49, 49, 51, 53, 53, 53, 54, 56, 56, 56, 57, 58, 58, 49, 48, 47, 47, 47, 46, 45, 45, 45, 45, 46, 46, 46, 45, + 45, 45, 45, 47, 49, 49, 49, 51, 53, 53, 53, 54, 56, 56, 56, 57, 58, 58, 31, 33, 42, 48, 31, 34, 42, 47, 31, 34, 42, 47, 31, 35, 42, 45, + 31, 35, 42, 45, 34, 39, 45, 46, 34, 39, 45, 46, 38, 43, 47, 46, 38, 43, 47, 46, 42, 45, 48, 50, 42, 45, 48, 50, 48, 47, 50, 53, 48, 47, + 50, 53, 47, 46, 50, 54, 47, 46, 50, 54, 47, 45, 49, 56, 31, 31, 31, 31, 31, 34, 34, 38, 38, 42, 42, 48, 48, 47, 47, 47, 33, 34, 34, 35, + 35, 39, 39, 43, 43, 45, 45, 47, 47, 46, 46, 45, 42, 42, 42, 42, 42, 45, 45, 47, 47, 48, 48, 50, 50, 50, 50, 49, 48, 47, 47, 45, 45, 46, + 46, 46, 46, 50, 50, 53, 53, 54, 54, 56, 32, 31, 31, 37, 37, 48, 48, 49, 31, 31, 31, 37, 37, 47, 47, 48, 31, 31, 31, 38, 38, 47, 47, 47, + 31, 31, 31, 38, 38, 47, 47, 47, 31, 31, 31, 38, 38, 47, 47, 47, 31, 32, 32, 39, 39, 46, 46, 46, 30, 32, 32, 40, 40, 46, 46, 45, 30, 32, + 32, 40, 40, 46, 46, 45, 30, 32, 32, 40, 40, 46, 46, 45, 32, 34, 34, 41, 41, 46, 46, 45, 33, 36, 36, 43, 43, 47, 47, 46, 33, 36, 36, 43, + 43, 47, 47, 46, 33, 36, 36, 43, 43, 47, 47, 46, 35, 38, 38, 45, 45, 47, 47, 45, 37, 40, 40, 47, 47, 47, 47, 45, 37, 40, 40, 47, 47, 47, + 47, 45, 37, 40, 40, 47, 47, 47, 47, 45, 39, 41, 41, 47, 47, 49, 49, 47, 42, 43, 43, 47, 47, 50, 50, 49, 42, 43, 43, 47, 47, 50, 50, 49, + 42, 43, 43, 47, 47, 50, 50, 49, 45, 44, 44, 47, 47, 51, 51, 51, 49, 46, 46, 48, 48, 53, 53, 53, 49, 46, 46, 48, 48, 53, 53, 53, 49, 46, + 46, 48, 48, 53, 53, 53, 48, 46, 46, 47, 47, 53, 53, 54, 48, 46, 46, 47, 47, 53, 53, 56, 48, 46, 46, 47, 47, 53, 53, 56, 48, 46, 46, 47, + 47, 53, 53, 56, 48, 45, 45, 46, 46, 53, 53, 57, 49, 45, 45, 46, 46, 53, 53, 58, 49, 45, 45, 46, 46, 53, 53, 58, 32, 31, 31, 31, 31, 31, + 30, 30, 30, 32, 33, 33, 33, 35, 37, 37, 37, 39, 42, 42, 42, 45, 49, 49, 49, 48, 48, 48, 48, 48, 49, 49, 31, 31, 31, 31, 31, 32, 32, 32, + 32, 34, 36, 36, 36, 38, 40, 40, 40, 41, 43, 43, 43, 44, 46, 46, 46, 46, 46, 46, 46, 45, 45, 45, 31, 31, 31, 31, 31, 32, 32, 32, 32, 34, + 36, 36, 36, 38, 40, 40, 40, 41, 43, 43, 43, 44, 46, 46, 46, 46, 46, 46, 46, 45, 45, 45, 37, 37, 38, 38, 38, 39, 40, 40, 40, 41, 43, 43, + 43, 45, 47, 47, 47, 47, 47, 47, 47, 47, 48, 48, 48, 47, 47, 47, 47, 46, 46, 46, 37, 37, 38, 38, 38, 39, 40, 40, 40, 41, 43, 43, 43, 45, + 47, 47, 47, 47, 47, 47, 47, 47, 48, 48, 48, 47, 47, 47, 47, 46, 46, 46, 48, 47, 47, 47, 47, 46, 46, 46, 46, 46, 47, 47, 47, 47, 47, 47, + 47, 49, 50, 50, 50, 51, 53, 53, 53, 53, 53, 53, 53, 53, 53, 53, 48, 47, 47, 47, 47, 46, 46, 46, 46, 46, 47, 47, 47, 47, 47, 47, 47, 49, + 50, 50, 50, 51, 53, 53, 53, 53, 53, 53, 53, 53, 53, 53, 49, 48, 47, 47, 47, 46, 45, 45, 45, 45, 46, 46, 46, 45, 45, 45, 45, 47, 49, 49, + 49, 51, 53, 53, 53, 54, 56, 56, 56, 57, 58, 58, + ], + ], + [ + [ + 32, 32, 32, 35, 32, 32, 33, 35, 32, 33, 35, 38, 35, 35, 38, 46, 31, 31, 31, 32, 32, 32, 34, 35, 31, 32, 32, 32, 32, 33, 34, 35, 31, 32, + 32, 32, 32, 33, 33, 34, 32, 32, 32, 33, 34, 34, 35, 36, 32, 32, 32, 34, 35, 35, 36, 38, 32, 33, 33, 34, 35, 36, 38, 40, 34, 34, 33, 35, + 36, 38, 39, 42, 35, 35, 34, 36, 38, 40, 42, 48, 32, 31, 31, 31, 31, 31, 31, 31, 32, 32, 32, 33, 34, 34, 36, 36, 31, 31, 31, 32, 32, 32, + 32, 32, 32, 32, 32, 33, 34, 34, 35, 35, 31, 31, 32, 32, 32, 32, 32, 32, 32, 32, 32, 33, 34, 34, 35, 35, 31, 32, 32, 32, 32, 32, 32, 32, + 32, 32, 32, 33, 34, 34, 35, 35, 31, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 33, 33, 34, 34, 34, 31, 32, 32, 32, 32, 32, 32, 32, 32, 32, + 32, 33, 33, 34, 34, 34, 31, 32, 32, 32, 32, 32, 33, 33, 33, 33, 33, 34, 35, 35, 36, 36, 31, 32, 32, 32, 32, 32, 33, 33, 33, 34, 34, 35, + 35, 36, 36, 36, 32, 32, 32, 32, 32, 32, 33, 33, 34, 34, 34, 35, 36, 36, 37, 37, 32, 32, 32, 32, 32, 32, 33, 34, 34, 35, 35, 36, 37, 37, + 38, 38, 32, 32, 32, 32, 32, 32, 33, 34, 34, 35, 35, 36, 37, 37, 38, 38, 33, 33, 33, 33, 33, 33, 34, 35, 35, 36, 36, 38, 39, 40, 42, 42, + 34, 34, 34, 34, 33, 33, 35, 35, 36, 37, 37, 39, 39, 41, 42, 42, 34, 34, 34, 34, 34, 34, 35, 36, 36, 37, 37, 40, 41, 42, 45, 45, 36, 35, + 35, 35, 34, 34, 36, 36, 37, 38, 38, 42, 42, 45, 48, 48, 36, 35, 35, 35, 34, 34, 36, 36, 37, 38, 38, 42, 42, 45, 48, 48, 32, 31, 31, 31, + 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 32, 32, 32, 32, 32, 32, 33, 34, 34, 34, 34, 35, 36, 36, 36, 37, 31, 31, 31, 31, 31, 31, + 31, 31, 31, 31, 31, 31, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 33, 34, 34, 34, 34, 35, 35, 35, 35, 37, 31, 31, 31, 31, 31, 31, 32, 32, + 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 33, 33, 34, 34, 34, 34, 35, 35, 35, 35, 36, 31, 31, 31, 32, 32, 32, 32, 32, 32, 32, + 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 33, 33, 34, 34, 34, 34, 35, 35, 35, 35, 36, 31, 31, 31, 32, 32, 32, 32, 32, 32, 32, 32, 32, + 32, 32, 32, 32, 32, 32, 32, 32, 32, 33, 33, 34, 34, 34, 34, 35, 35, 35, 35, 36, 31, 31, 31, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, + 32, 32, 32, 32, 32, 32, 32, 33, 33, 34, 34, 34, 34, 35, 35, 35, 35, 36, 31, 31, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, + 32, 32, 32, 32, 32, 33, 33, 34, 34, 34, 34, 34, 35, 35, 35, 36, 31, 31, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, + 32, 32, 32, 33, 33, 33, 33, 33, 34, 34, 34, 34, 34, 35, 31, 31, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, + 32, 33, 33, 33, 33, 33, 34, 34, 34, 34, 34, 35, 31, 31, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 33, + 33, 33, 33, 33, 34, 34, 34, 34, 34, 35, 31, 31, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 33, 33, 33, + 33, 33, 34, 34, 34, 34, 34, 35, 31, 31, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 33, 33, 33, 33, 33, 33, 33, 33, 33, 33, 34, 34, 34, 34, + 34, 35, 35, 35, 35, 36, 31, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 33, 33, 33, 33, 33, 33, 33, 33, 33, 33, 34, 34, 35, 35, 35, 35, 36, + 36, 36, 36, 37, 31, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 33, 33, 33, 33, 33, 33, 34, 34, 34, 34, 34, 35, 35, 35, 35, 36, 36, 36, 36, + 36, 37, 31, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 33, 33, 33, 33, 33, 33, 34, 34, 34, 34, 34, 35, 35, 35, 35, 36, 36, 36, 36, 36, 37, + 31, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 33, 33, 33, 33, 33, 33, 34, 34, 34, 34, 34, 35, 35, 35, 35, 36, 36, 36, 36, 36, 37, 32, 32, + 32, 32, 32, 32, 32, 32, 32, 32, 32, 33, 33, 33, 33, 33, 34, 34, 34, 34, 34, 35, 35, 36, 36, 36, 36, 37, 37, 37, 37, 38, 32, 32, 32, 32, + 32, 32, 32, 32, 32, 32, 32, 33, 33, 34, 34, 34, 34, 35, 35, 35, 35, 35, 36, 36, 36, 36, 37, 37, 38, 38, 38, 39, 32, 32, 32, 32, 32, 32, + 32, 32, 32, 32, 32, 33, 33, 34, 34, 34, 34, 35, 35, 35, 35, 36, 36, 37, 37, 37, 37, 38, 38, 38, 38, 39, 32, 32, 32, 32, 32, 32, 32, 32, + 32, 32, 32, 33, 33, 34, 34, 34, 34, 35, 35, 35, 35, 36, 36, 37, 37, 37, 37, 38, 38, 38, 38, 39, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, + 32, 33, 33, 34, 34, 34, 34, 35, 35, 35, 35, 36, 36, 37, 37, 37, 37, 38, 38, 38, 38, 39, 32, 32, 33, 33, 33, 33, 33, 33, 33, 33, 33, 33, + 34, 34, 34, 34, 35, 35, 36, 36, 36, 36, 37, 38, 38, 38, 38, 39, 40, 40, 40, 41, 33, 33, 33, 33, 33, 33, 33, 33, 33, 33, 33, 34, 34, 35, + 35, 35, 35, 36, 36, 36, 36, 37, 38, 39, 39, 39, 40, 41, 42, 42, 42, 42, 34, 34, 34, 34, 34, 34, 34, 33, 33, 33, 33, 34, 35, 35, 35, 35, + 36, 36, 37, 37, 37, 38, 39, 39, 39, 39, 41, 42, 42, 42, 42, 43, 34, 34, 34, 34, 34, 34, 34, 33, 33, 33, 33, 34, 35, 35, 35, 35, 36, 36, + 37, 37, 37, 38, 39, 39, 39, 39, 41, 42, 42, 42, 42, 43, 34, 34, 34, 34, 34, 34, 34, 33, 33, 33, 33, 34, 35, 35, 35, 35, 36, 36, 37, 37, + 37, 38, 39, 39, 39, 39, 41, 42, 42, 42, 42, 43, 34, 34, 34, 34, 34, 34, 34, 34, 34, 34, 34, 34, 35, 36, 36, 36, 36, 37, 37, 37, 37, 38, + 40, 41, 41, 41, 42, 44, 45, 45, 45, 45, 35, 35, 35, 35, 35, 35, 34, 34, 34, 34, 34, 35, 36, 36, 36, 36, 37, 37, 38, 38, 38, 39, 41, 42, + 42, 42, 44, 46, 47, 47, 47, 48, 36, 35, 35, 35, 35, 35, 35, 34, 34, 34, 34, 35, 36, 36, 36, 36, 37, 38, 38, 38, 38, 40, 42, 42, 42, 42, + 45, 47, 48, 48, 48, 49, 36, 35, 35, 35, 35, 35, 35, 34, 34, 34, 34, 35, 36, 36, 36, 36, 37, 38, 38, 38, 38, 40, 42, 42, 42, 42, 45, 47, + 48, 48, 48, 49, 36, 35, 35, 35, 35, 35, 35, 34, 34, 34, 34, 35, 36, 36, 36, 36, 37, 38, 38, 38, 38, 40, 42, 42, 42, 42, 45, 47, 48, 48, + 48, 49, 37, 37, 36, 36, 36, 36, 36, 35, 35, 35, 35, 36, 37, 37, 37, 37, 38, 39, 39, 39, 39, 41, 42, 43, 43, 43, 45, 48, 49, 49, 49, 50, + 31, 31, 32, 35, 32, 32, 32, 35, 32, 32, 33, 34, 32, 32, 34, 36, 32, 33, 35, 38, 33, 33, 36, 40, 34, 34, 37, 42, 35, 34, 38, 48, 31, 32, + 32, 32, 32, 33, 34, 35, 31, 32, 32, 32, 33, 33, 34, 34, 32, 32, 33, 34, 35, 36, 37, 38, 35, 35, 34, 36, 38, 40, 42, 48, 32, 31, 31, 31, + 32, 32, 35, 36, 31, 32, 32, 32, 32, 32, 35, 35, 31, 32, 32, 32, 32, 32, 35, 35, 31, 32, 32, 32, 32, 32, 34, 35, 31, 32, 32, 32, 33, 33, + 34, 34, 31, 32, 32, 32, 33, 33, 34, 34, 31, 32, 32, 33, 34, 34, 35, 36, 32, 32, 32, 33, 34, 34, 36, 36, 32, 32, 32, 33, 34, 34, 36, 37, + 32, 32, 33, 34, 35, 35, 37, 38, 32, 32, 33, 34, 35, 35, 37, 38, 33, 33, 33, 35, 36, 36, 40, 41, 34, 34, 34, 35, 37, 37, 41, 42, 34, 34, + 34, 35, 37, 37, 43, 44, 36, 35, 34, 36, 38, 38, 46, 48, 36, 35, 34, 36, 38, 38, 46, 48, 32, 31, 31, 31, 31, 31, 31, 32, 32, 32, 32, 33, + 34, 34, 36, 36, 31, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 33, 34, 34, 35, 35, 31, 32, 32, 32, 32, 32, 32, 32, 32, 33, 33, 33, 34, 34, + 34, 34, 31, 32, 32, 32, 32, 32, 33, 33, 33, 34, 34, 35, 35, 35, 36, 36, 32, 32, 32, 32, 33, 33, 34, 34, 34, 35, 35, 36, 37, 37, 38, 38, + 32, 32, 32, 32, 33, 33, 34, 34, 34, 35, 35, 36, 37, 37, 38, 38, 35, 35, 35, 34, 34, 34, 35, 36, 36, 37, 37, 40, 41, 43, 46, 46, 36, 35, + 35, 35, 34, 34, 36, 36, 37, 38, 38, 41, 42, 44, 48, 48, 32, 31, 31, 31, 31, 31, 31, 32, 32, 32, 32, 33, 35, 36, 36, 36, 31, 31, 31, 31, + 31, 31, 32, 32, 32, 32, 32, 33, 35, 35, 35, 35, 31, 31, 32, 32, 32, 32, 32, 32, 32, 32, 32, 33, 35, 35, 35, 35, 31, 32, 32, 32, 32, 32, + 32, 32, 32, 32, 32, 33, 35, 35, 35, 35, 31, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 33, 35, 35, 35, 35, 31, 32, 32, 32, 32, 32, 32, 32, + 32, 32, 32, 33, 35, 35, 35, 35, 31, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 33, 34, 35, 35, 35, 31, 32, 32, 32, 32, 32, 32, 32, 32, 32, + 32, 33, 34, 35, 35, 35, 31, 32, 32, 32, 32, 32, 32, 32, 33, 33, 33, 33, 34, 34, 34, 34, 31, 32, 32, 32, 32, 32, 32, 32, 33, 33, 33, 33, + 34, 34, 34, 34, 31, 32, 32, 32, 32, 32, 32, 32, 33, 33, 33, 33, 34, 34, 34, 34, 31, 32, 32, 32, 32, 32, 33, 33, 33, 33, 33, 34, 35, 35, + 35, 35, 31, 32, 32, 32, 32, 32, 33, 33, 34, 34, 34, 34, 35, 36, 36, 36, 32, 32, 32, 32, 32, 32, 33, 34, 34, 34, 34, 35, 36, 36, 36, 36, + 32, 32, 32, 32, 32, 32, 33, 34, 34, 34, 34, 35, 36, 36, 36, 36, 32, 32, 32, 32, 32, 32, 33, 34, 34, 34, 34, 35, 36, 36, 36, 36, 32, 32, + 32, 32, 32, 32, 33, 34, 34, 34, 34, 35, 36, 37, 37, 37, 32, 32, 32, 33, 33, 33, 33, 34, 35, 35, 35, 36, 37, 38, 38, 38, 32, 32, 32, 33, + 33, 33, 34, 35, 35, 35, 35, 36, 37, 38, 38, 38, 32, 32, 32, 33, 33, 33, 34, 35, 35, 35, 35, 36, 37, 38, 38, 38, 32, 32, 32, 33, 33, 33, + 34, 35, 35, 35, 35, 36, 37, 38, 38, 38, 32, 33, 33, 33, 33, 33, 34, 35, 36, 36, 36, 37, 39, 40, 40, 40, 33, 33, 33, 33, 33, 33, 35, 36, + 36, 36, 36, 38, 40, 41, 41, 41, 34, 34, 34, 34, 34, 34, 35, 36, 37, 37, 37, 39, 41, 42, 42, 42, 34, 34, 34, 34, 34, 34, 35, 36, 37, 37, + 37, 39, 41, 42, 42, 42, 34, 34, 34, 34, 34, 34, 35, 36, 37, 37, 37, 39, 41, 42, 42, 42, 34, 34, 34, 34, 34, 34, 35, 37, 37, 37, 37, 40, + 43, 44, 44, 44, 35, 35, 34, 34, 34, 34, 36, 37, 38, 38, 38, 41, 45, 47, 47, 47, 36, 35, 35, 34, 34, 34, 36, 37, 38, 38, 38, 42, 46, 48, + 48, 48, 36, 35, 35, 34, 34, 34, 36, 37, 38, 38, 38, 42, 46, 48, 48, 48, 36, 35, 35, 34, 34, 34, 36, 37, 38, 38, 38, 42, 46, 48, 48, 48, + 37, 36, 36, 36, 36, 36, 37, 38, 39, 39, 39, 42, 46, 49, 49, 49, 32, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 32, 32, 32, 32, 32, + 32, 32, 32, 32, 33, 34, 34, 34, 34, 35, 36, 36, 36, 37, 31, 31, 31, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, + 32, 33, 33, 34, 34, 34, 34, 35, 35, 35, 35, 36, 31, 31, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 33, + 33, 34, 34, 34, 34, 34, 35, 35, 35, 36, 31, 31, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 33, 33, 33, 33, 33, 33, 34, + 34, 34, 34, 34, 34, 34, 34, 36, 31, 31, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 33, 33, 33, 33, 33, 33, 34, 34, 34, + 34, 34, 34, 34, 34, 36, 31, 31, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 33, 33, 33, 33, 33, 33, 34, 34, 34, 34, 34, + 34, 34, 34, 36, 31, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 33, 33, 33, 33, 33, 33, 33, 34, 34, 34, 34, 35, 35, 35, 35, 35, 36, 36, 36, + 36, 37, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 33, 33, 34, 34, 34, 34, 34, 35, 35, 35, 35, 36, 36, 36, 36, 37, 37, 37, 37, 37, 38, + 32, 32, 32, 32, 32, 32, 32, 32, 33, 33, 33, 33, 34, 34, 34, 34, 34, 35, 35, 35, 35, 36, 36, 37, 37, 37, 37, 38, 38, 38, 38, 39, 32, 32, + 32, 32, 32, 32, 32, 32, 33, 33, 33, 33, 34, 34, 34, 34, 34, 35, 35, 35, 35, 36, 36, 37, 37, 37, 37, 38, 38, 38, 38, 39, 32, 32, 32, 32, + 32, 32, 32, 32, 33, 33, 33, 33, 34, 34, 34, 34, 34, 35, 35, 35, 35, 36, 36, 37, 37, 37, 37, 38, 38, 38, 38, 39, 33, 33, 33, 33, 33, 33, + 33, 33, 33, 33, 33, 34, 34, 35, 35, 35, 35, 36, 36, 36, 36, 37, 38, 39, 39, 39, 40, 41, 42, 42, 42, 42, 35, 35, 35, 35, 35, 35, 34, 34, + 34, 34, 34, 35, 35, 36, 36, 36, 36, 37, 37, 37, 37, 39, 40, 41, 41, 41, 43, 45, 46, 46, 46, 46, 36, 35, 35, 35, 35, 35, 35, 35, 34, 34, + 34, 35, 36, 36, 36, 36, 37, 38, 38, 38, 38, 40, 41, 42, 42, 42, 44, 47, 48, 48, 48, 49, 36, 35, 35, 35, 35, 35, 35, 35, 34, 34, 34, 35, + 36, 36, 36, 36, 37, 38, 38, 38, 38, 40, 41, 42, 42, 42, 44, 47, 48, 48, 48, 49, 36, 35, 35, 35, 35, 35, 35, 35, 34, 34, 34, 35, 36, 36, + 36, 36, 37, 38, 38, 38, 38, 40, 41, 42, 42, 42, 44, 47, 48, 48, 48, 49, 31, 31, 32, 36, 31, 32, 32, 35, 32, 32, 32, 35, 32, 32, 32, 35, + 32, 32, 33, 34, 32, 32, 33, 34, 32, 32, 34, 36, 32, 32, 34, 36, 32, 32, 34, 37, 32, 33, 35, 38, 32, 33, 35, 38, 33, 33, 36, 41, 34, 34, + 37, 42, 34, 34, 37, 44, 35, 34, 38, 48, 35, 34, 38, 48, 31, 31, 32, 32, 32, 32, 32, 32, 32, 32, 32, 33, 34, 34, 35, 35, 31, 32, 32, 32, + 32, 32, 32, 32, 32, 33, 33, 33, 34, 34, 34, 34, 32, 32, 32, 32, 33, 33, 34, 34, 34, 35, 35, 36, 37, 37, 38, 38, 36, 35, 35, 35, 34, 34, + 36, 36, 37, 38, 38, 41, 42, 44, 48, 48, 32, 31, 31, 31, 32, 32, 35, 36, 31, 31, 31, 32, 32, 32, 35, 35, 31, 32, 32, 32, 32, 32, 35, 35, + 31, 32, 32, 32, 32, 32, 35, 35, 31, 32, 32, 32, 32, 32, 35, 35, 31, 32, 32, 32, 32, 32, 35, 35, 31, 32, 32, 32, 32, 32, 34, 35, 31, 32, + 32, 32, 32, 32, 34, 35, 31, 32, 32, 32, 33, 33, 34, 34, 31, 32, 32, 32, 33, 33, 34, 34, 31, 32, 32, 32, 33, 33, 34, 34, 31, 32, 32, 33, + 33, 33, 35, 35, 31, 32, 32, 33, 34, 34, 35, 36, 32, 32, 32, 33, 34, 34, 36, 36, 32, 32, 32, 33, 34, 34, 36, 36, 32, 32, 32, 33, 34, 34, + 36, 36, 32, 32, 32, 33, 34, 34, 36, 37, 32, 32, 33, 33, 35, 35, 37, 38, 32, 32, 33, 34, 35, 35, 37, 38, 32, 32, 33, 34, 35, 35, 37, 38, + 32, 32, 33, 34, 35, 35, 37, 38, 32, 33, 33, 34, 36, 36, 39, 40, 33, 33, 33, 35, 36, 36, 40, 41, 34, 34, 34, 35, 37, 37, 41, 42, 34, 34, + 34, 35, 37, 37, 41, 42, 34, 34, 34, 35, 37, 37, 41, 42, 34, 34, 34, 35, 37, 37, 43, 44, 35, 34, 34, 36, 38, 38, 45, 47, 36, 35, 34, 36, + 38, 38, 46, 48, 36, 35, 34, 36, 38, 38, 46, 48, 36, 35, 34, 36, 38, 38, 46, 48, 37, 36, 36, 37, 39, 39, 46, 49, 32, 31, 31, 31, 31, 31, + 31, 31, 31, 31, 31, 31, 31, 32, 32, 32, 32, 32, 32, 32, 32, 32, 33, 34, 34, 34, 34, 35, 36, 36, 36, 37, 31, 31, 32, 32, 32, 32, 32, 32, + 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 33, 33, 34, 34, 34, 34, 34, 35, 35, 35, 36, 31, 31, 32, 32, 32, 32, 32, 32, 32, 32, + 32, 32, 32, 32, 32, 32, 32, 33, 33, 33, 33, 33, 33, 34, 34, 34, 34, 34, 34, 34, 34, 36, 31, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 33, + 33, 33, 33, 33, 33, 33, 34, 34, 34, 34, 35, 35, 35, 35, 35, 36, 36, 36, 36, 37, 32, 32, 32, 32, 32, 32, 32, 32, 33, 33, 33, 33, 34, 34, + 34, 34, 34, 35, 35, 35, 35, 36, 36, 37, 37, 37, 37, 38, 38, 38, 38, 39, 32, 32, 32, 32, 32, 32, 32, 32, 33, 33, 33, 33, 34, 34, 34, 34, + 34, 35, 35, 35, 35, 36, 36, 37, 37, 37, 37, 38, 38, 38, 38, 39, 35, 35, 35, 35, 35, 35, 34, 34, 34, 34, 34, 35, 35, 36, 36, 36, 36, 37, + 37, 37, 37, 39, 40, 41, 41, 41, 43, 45, 46, 46, 46, 46, 36, 35, 35, 35, 35, 35, 35, 35, 34, 34, 34, 35, 36, 36, 36, 36, 37, 38, 38, 38, + 38, 40, 41, 42, 42, 42, 44, 47, 48, 48, 48, 49, + ], + [ + 31, 32, 38, 46, 32, 34, 41, 46, 38, 41, 47, 47, 46, 46, 47, 52, 31, 31, 30, 34, 36, 39, 42, 48, 31, 31, 31, 34, 37, 40, 42, 47, 30, 31, + 32, 35, 39, 41, 42, 46, 34, 34, 35, 39, 42, 44, 45, 47, 36, 37, 39, 42, 46, 47, 47, 47, 39, 40, 41, 44, 47, 47, 48, 49, 42, 42, 42, 45, + 47, 48, 48, 50, 48, 47, 46, 47, 47, 49, 50, 53, 32, 31, 31, 31, 30, 30, 33, 33, 34, 36, 36, 40, 41, 44, 49, 49, 31, 31, 31, 31, 31, 31, + 33, 34, 36, 38, 38, 41, 42, 44, 48, 48, 31, 31, 31, 31, 31, 31, 34, 34, 36, 38, 38, 41, 42, 44, 47, 47, 31, 31, 31, 31, 31, 31, 34, 35, + 36, 39, 39, 41, 42, 44, 47, 47, 30, 31, 31, 31, 32, 32, 34, 35, 37, 40, 40, 42, 42, 44, 46, 46, 30, 31, 31, 31, 32, 32, 34, 35, 37, 40, + 40, 42, 42, 44, 46, 46, 33, 33, 34, 34, 34, 34, 37, 38, 40, 42, 42, 44, 44, 45, 47, 47, 33, 34, 34, 35, 35, 35, 38, 39, 40, 43, 43, 44, + 45, 46, 47, 47, 34, 36, 36, 36, 37, 37, 40, 40, 42, 45, 45, 45, 46, 46, 47, 47, 36, 38, 38, 39, 40, 40, 42, 43, 45, 47, 47, 47, 47, 47, + 48, 48, 36, 38, 38, 39, 40, 40, 42, 43, 45, 47, 47, 47, 47, 47, 48, 48, 40, 41, 41, 41, 42, 42, 44, 44, 45, 47, 47, 48, 48, 49, 50, 50, + 41, 42, 42, 42, 42, 42, 44, 45, 46, 47, 47, 48, 48, 49, 50, 50, 44, 44, 44, 44, 44, 44, 45, 46, 46, 47, 47, 49, 49, 50, 51, 51, 49, 48, + 47, 47, 46, 46, 47, 47, 47, 48, 48, 50, 50, 51, 53, 53, 49, 48, 47, 47, 46, 46, 47, 47, 47, 48, 48, 50, 50, 51, 53, 53, 32, 31, 31, 31, + 31, 31, 31, 30, 30, 30, 30, 31, 33, 33, 33, 33, 34, 36, 36, 36, 36, 38, 40, 41, 41, 41, 44, 47, 49, 49, 49, 49, 31, 31, 31, 31, 31, 31, + 31, 31, 30, 30, 30, 32, 33, 34, 34, 34, 35, 36, 37, 37, 37, 39, 41, 42, 42, 42, 44, 47, 48, 48, 48, 48, 31, 31, 31, 31, 31, 31, 31, 31, + 31, 31, 31, 32, 33, 34, 34, 34, 36, 37, 38, 38, 38, 39, 41, 42, 42, 42, 44, 46, 48, 48, 48, 47, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, + 31, 32, 34, 34, 34, 34, 36, 37, 38, 38, 38, 40, 41, 42, 42, 42, 44, 46, 47, 47, 47, 47, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 32, + 34, 34, 34, 34, 36, 37, 38, 38, 38, 40, 41, 42, 42, 42, 44, 46, 47, 47, 47, 47, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 32, 34, 34, + 34, 34, 36, 37, 38, 38, 38, 40, 41, 42, 42, 42, 44, 46, 47, 47, 47, 47, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 33, 34, 35, 35, 35, + 36, 38, 39, 39, 39, 40, 41, 42, 42, 42, 44, 46, 47, 47, 47, 47, 30, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 33, 34, 35, 35, 35, 37, 38, + 39, 39, 39, 41, 42, 42, 42, 42, 44, 46, 46, 46, 46, 46, 30, 30, 31, 31, 31, 31, 31, 31, 32, 32, 32, 33, 34, 35, 35, 35, 37, 39, 40, 40, + 40, 41, 42, 42, 42, 42, 44, 45, 46, 46, 46, 46, 30, 30, 31, 31, 31, 31, 31, 31, 32, 32, 32, 33, 34, 35, 35, 35, 37, 39, 40, 40, 40, 41, + 42, 42, 42, 42, 44, 45, 46, 46, 46, 46, 30, 30, 31, 31, 31, 31, 31, 31, 32, 32, 32, 33, 34, 35, 35, 35, 37, 39, 40, 40, 40, 41, 42, 42, + 42, 42, 44, 45, 46, 46, 46, 46, 31, 32, 32, 32, 32, 32, 33, 33, 33, 33, 33, 34, 36, 37, 37, 37, 38, 40, 41, 41, 41, 42, 43, 43, 43, 43, + 44, 46, 46, 46, 46, 46, 33, 33, 33, 34, 34, 34, 34, 34, 34, 34, 34, 36, 37, 38, 38, 38, 40, 41, 42, 42, 42, 43, 44, 44, 44, 44, 45, 46, + 47, 47, 47, 46, 33, 34, 34, 34, 34, 34, 35, 35, 35, 35, 35, 37, 38, 39, 39, 39, 40, 42, 43, 43, 43, 44, 44, 45, 45, 45, 46, 47, 47, 47, + 47, 47, 33, 34, 34, 34, 34, 34, 35, 35, 35, 35, 35, 37, 38, 39, 39, 39, 40, 42, 43, 43, 43, 44, 44, 45, 45, 45, 46, 47, 47, 47, 47, 47, + 33, 34, 34, 34, 34, 34, 35, 35, 35, 35, 35, 37, 38, 39, 39, 39, 40, 42, 43, 43, 43, 44, 44, 45, 45, 45, 46, 47, 47, 47, 47, 47, 34, 35, + 36, 36, 36, 36, 36, 37, 37, 37, 37, 38, 40, 40, 40, 40, 42, 44, 45, 45, 45, 45, 45, 46, 46, 46, 46, 47, 47, 47, 47, 47, 36, 36, 37, 37, + 37, 37, 38, 38, 39, 39, 39, 40, 41, 42, 42, 42, 44, 46, 46, 46, 46, 47, 47, 47, 47, 47, 47, 47, 47, 47, 47, 47, 36, 37, 38, 38, 38, 38, + 39, 39, 40, 40, 40, 41, 42, 43, 43, 43, 45, 46, 47, 47, 47, 47, 47, 47, 47, 47, 47, 47, 48, 48, 48, 47, 36, 37, 38, 38, 38, 38, 39, 39, + 40, 40, 40, 41, 42, 43, 43, 43, 45, 46, 47, 47, 47, 47, 47, 47, 47, 47, 47, 47, 48, 48, 48, 47, 36, 37, 38, 38, 38, 38, 39, 39, 40, 40, + 40, 41, 42, 43, 43, 43, 45, 46, 47, 47, 47, 47, 47, 47, 47, 47, 47, 47, 48, 48, 48, 47, 38, 39, 39, 40, 40, 40, 40, 41, 41, 41, 41, 42, + 43, 44, 44, 44, 45, 47, 47, 47, 47, 47, 48, 48, 48, 48, 48, 48, 49, 49, 49, 48, 40, 41, 41, 41, 41, 41, 41, 42, 42, 42, 42, 43, 44, 44, + 44, 44, 45, 47, 47, 47, 47, 48, 48, 48, 48, 48, 49, 49, 50, 50, 50, 49, 41, 42, 42, 42, 42, 42, 42, 42, 42, 42, 42, 43, 44, 45, 45, 45, + 46, 47, 47, 47, 47, 48, 48, 48, 48, 48, 49, 50, 50, 50, 50, 50, 41, 42, 42, 42, 42, 42, 42, 42, 42, 42, 42, 43, 44, 45, 45, 45, 46, 47, + 47, 47, 47, 48, 48, 48, 48, 48, 49, 50, 50, 50, 50, 50, 41, 42, 42, 42, 42, 42, 42, 42, 42, 42, 42, 43, 44, 45, 45, 45, 46, 47, 47, 47, + 47, 48, 48, 48, 48, 48, 49, 50, 50, 50, 50, 50, 44, 44, 44, 44, 44, 44, 44, 44, 44, 44, 44, 44, 45, 46, 46, 46, 46, 47, 47, 47, 47, 48, + 49, 49, 49, 49, 50, 51, 51, 51, 51, 51, 47, 47, 46, 46, 46, 46, 46, 46, 45, 45, 45, 46, 46, 47, 47, 47, 47, 47, 47, 47, 47, 48, 49, 50, + 50, 50, 51, 52, 52, 52, 52, 52, 49, 48, 48, 47, 47, 47, 47, 46, 46, 46, 46, 46, 47, 47, 47, 47, 47, 47, 48, 48, 48, 49, 50, 50, 50, 50, + 51, 52, 53, 53, 53, 53, 49, 48, 48, 47, 47, 47, 47, 46, 46, 46, 46, 46, 47, 47, 47, 47, 47, 47, 48, 48, 48, 49, 50, 50, 50, 50, 51, 52, + 53, 53, 53, 53, 49, 48, 48, 47, 47, 47, 47, 46, 46, 46, 46, 46, 47, 47, 47, 47, 47, 47, 48, 48, 48, 49, 50, 50, 50, 50, 51, 52, 53, 53, + 53, 53, 49, 48, 47, 47, 47, 47, 47, 46, 46, 46, 46, 46, 46, 47, 47, 47, 47, 47, 47, 47, 47, 48, 49, 50, 50, 50, 51, 52, 53, 53, 53, 53, + 31, 31, 37, 48, 31, 31, 38, 47, 31, 32, 40, 46, 34, 36, 43, 47, 37, 39, 46, 47, 39, 41, 47, 48, 42, 43, 47, 50, 48, 46, 48, 53, 31, 31, + 31, 34, 37, 39, 42, 48, 31, 31, 32, 36, 39, 41, 43, 46, 37, 38, 40, 43, 46, 47, 47, 48, 48, 47, 46, 47, 47, 48, 50, 53, 32, 31, 31, 33, + 37, 37, 45, 48, 31, 31, 31, 34, 38, 38, 45, 47, 31, 31, 31, 34, 38, 38, 45, 47, 31, 31, 32, 34, 39, 39, 45, 46, 30, 32, 32, 35, 40, 40, + 44, 46, 30, 32, 32, 35, 40, 40, 44, 46, 33, 34, 35, 37, 42, 42, 46, 47, 33, 35, 36, 38, 43, 43, 46, 47, 35, 37, 37, 40, 44, 44, 46, 47, + 37, 39, 40, 43, 47, 47, 47, 47, 37, 39, 40, 43, 47, 47, 47, 47, 41, 42, 42, 44, 47, 47, 49, 49, 42, 42, 43, 44, 47, 47, 49, 50, 44, 44, + 44, 45, 47, 47, 50, 51, 49, 47, 46, 47, 48, 48, 52, 53, 49, 47, 46, 47, 48, 48, 52, 53, 32, 31, 31, 31, 30, 30, 33, 33, 35, 37, 37, 41, + 42, 44, 49, 49, 31, 31, 31, 31, 32, 32, 34, 35, 37, 39, 39, 42, 42, 44, 47, 47, 31, 31, 31, 32, 32, 32, 35, 36, 37, 40, 40, 42, 43, 44, + 46, 46, 33, 34, 34, 34, 35, 35, 37, 38, 40, 43, 43, 44, 44, 45, 47, 47, 37, 38, 38, 39, 40, 40, 42, 43, 44, 47, 47, 47, 47, 47, 48, 48, + 37, 38, 38, 39, 40, 40, 42, 43, 44, 47, 47, 47, 47, 47, 48, 48, 45, 45, 45, 45, 44, 44, 46, 46, 46, 47, 47, 49, 49, 50, 52, 52, 48, 47, + 47, 46, 46, 46, 47, 47, 47, 47, 47, 49, 50, 51, 53, 53, 32, 31, 31, 31, 31, 31, 33, 35, 37, 37, 37, 40, 45, 48, 48, 48, 31, 31, 31, 31, + 31, 31, 33, 36, 37, 37, 37, 41, 45, 48, 48, 48, 31, 31, 31, 31, 31, 31, 34, 36, 38, 38, 38, 41, 45, 47, 47, 47, 31, 31, 31, 31, 31, 31, + 34, 37, 38, 38, 38, 41, 45, 47, 47, 47, 31, 31, 31, 31, 31, 31, 34, 37, 38, 38, 38, 41, 45, 47, 47, 47, 31, 31, 31, 31, 31, 31, 34, 37, + 38, 38, 38, 41, 45, 47, 47, 47, 31, 31, 31, 32, 32, 32, 34, 37, 39, 39, 39, 41, 45, 46, 46, 46, 30, 31, 31, 32, 32, 32, 34, 38, 39, 39, + 39, 42, 44, 46, 46, 46, 30, 31, 32, 32, 32, 32, 35, 38, 40, 40, 40, 42, 44, 46, 46, 46, 30, 31, 32, 32, 32, 32, 35, 38, 40, 40, 40, 42, + 44, 46, 46, 46, 30, 31, 32, 32, 32, 32, 35, 38, 40, 40, 40, 42, 44, 46, 46, 46, 31, 32, 33, 33, 33, 33, 36, 39, 41, 41, 41, 43, 45, 46, + 46, 46, 33, 34, 34, 35, 35, 35, 37, 40, 42, 42, 42, 44, 46, 47, 47, 47, 33, 34, 35, 36, 36, 36, 38, 41, 43, 43, 43, 44, 46, 47, 47, 47, + 33, 34, 35, 36, 36, 36, 38, 41, 43, 43, 43, 44, 46, 47, 47, 47, 33, 34, 35, 36, 36, 36, 38, 41, 43, 43, 43, 44, 46, 47, 47, 47, 35, 36, + 37, 37, 37, 37, 40, 43, 44, 44, 44, 45, 46, 47, 47, 47, 36, 37, 38, 39, 39, 39, 42, 44, 46, 46, 46, 47, 47, 47, 47, 47, 37, 38, 39, 40, + 40, 40, 43, 45, 47, 47, 47, 47, 47, 47, 47, 47, 37, 38, 39, 40, 40, 40, 43, 45, 47, 47, 47, 47, 47, 47, 47, 47, 37, 38, 39, 40, 40, 40, + 43, 45, 47, 47, 47, 47, 47, 47, 47, 47, 39, 39, 40, 41, 41, 41, 43, 46, 47, 47, 47, 48, 48, 48, 48, 48, 41, 41, 42, 42, 42, 42, 44, 46, + 47, 47, 47, 48, 49, 49, 49, 49, 42, 42, 42, 43, 43, 43, 44, 46, 47, 47, 47, 48, 49, 50, 50, 50, 42, 42, 42, 43, 43, 43, 44, 46, 47, 47, + 47, 48, 49, 50, 50, 50, 42, 42, 42, 43, 43, 43, 44, 46, 47, 47, 47, 48, 49, 50, 50, 50, 44, 44, 44, 44, 44, 44, 45, 47, 47, 47, 47, 49, + 50, 51, 51, 51, 47, 46, 46, 46, 46, 46, 46, 47, 48, 48, 48, 49, 51, 52, 52, 52, 49, 48, 47, 46, 46, 46, 47, 48, 48, 48, 48, 50, 52, 53, + 53, 53, 49, 48, 47, 46, 46, 46, 47, 48, 48, 48, 48, 50, 52, 53, 53, 53, 49, 48, 47, 46, 46, 46, 47, 48, 48, 48, 48, 50, 52, 53, 53, 53, + 49, 48, 47, 46, 46, 46, 47, 47, 47, 47, 47, 49, 52, 53, 53, 53, 32, 31, 31, 31, 31, 31, 31, 30, 30, 30, 30, 31, 33, 33, 33, 33, 35, 36, + 37, 37, 37, 39, 41, 42, 42, 42, 44, 47, 49, 49, 49, 49, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 32, 34, 34, 34, 34, 36, 37, 38, 38, + 38, 39, 41, 42, 42, 42, 44, 46, 48, 48, 48, 48, 31, 31, 31, 31, 31, 31, 31, 31, 32, 32, 32, 33, 34, 35, 35, 35, 37, 38, 39, 39, 39, 40, + 42, 42, 42, 42, 44, 46, 47, 47, 47, 47, 31, 31, 31, 31, 31, 31, 32, 32, 32, 32, 32, 33, 35, 36, 36, 36, 37, 39, 40, 40, 40, 41, 42, 43, + 43, 43, 44, 46, 46, 46, 46, 46, 31, 31, 31, 31, 31, 31, 32, 32, 32, 32, 32, 33, 35, 36, 36, 36, 37, 39, 40, 40, 40, 41, 42, 43, 43, 43, + 44, 46, 46, 46, 46, 46, 31, 31, 31, 31, 31, 31, 32, 32, 32, 32, 32, 33, 35, 36, 36, 36, 37, 39, 40, 40, 40, 41, 42, 43, 43, 43, 44, 46, + 46, 46, 46, 46, 33, 33, 34, 34, 34, 34, 34, 34, 35, 35, 35, 36, 37, 38, 38, 38, 40, 42, 43, 43, 43, 43, 44, 44, 44, 44, 45, 46, 47, 47, + 47, 47, 35, 36, 36, 37, 37, 37, 37, 38, 38, 38, 38, 39, 40, 41, 41, 41, 43, 44, 45, 45, 45, 46, 46, 46, 46, 46, 47, 47, 48, 48, 48, 47, + 37, 37, 38, 38, 38, 38, 39, 39, 40, 40, 40, 41, 42, 43, 43, 43, 44, 46, 47, 47, 47, 47, 47, 47, 47, 47, 47, 48, 48, 48, 48, 47, 37, 37, + 38, 38, 38, 38, 39, 39, 40, 40, 40, 41, 42, 43, 43, 43, 44, 46, 47, 47, 47, 47, 47, 47, 47, 47, 47, 48, 48, 48, 48, 47, 37, 37, 38, 38, + 38, 38, 39, 39, 40, 40, 40, 41, 42, 43, 43, 43, 44, 46, 47, 47, 47, 47, 47, 47, 47, 47, 47, 48, 48, 48, 48, 47, 40, 41, 41, 41, 41, 41, + 41, 42, 42, 42, 42, 43, 44, 44, 44, 44, 45, 47, 47, 47, 47, 48, 48, 48, 48, 48, 49, 49, 50, 50, 50, 49, 45, 45, 45, 45, 45, 45, 45, 44, + 44, 44, 44, 45, 46, 46, 46, 46, 46, 47, 47, 47, 47, 48, 49, 49, 49, 49, 50, 51, 52, 52, 52, 52, 48, 48, 47, 47, 47, 47, 46, 46, 46, 46, + 46, 46, 47, 47, 47, 47, 47, 47, 47, 47, 47, 48, 49, 50, 50, 50, 51, 52, 53, 53, 53, 53, 48, 48, 47, 47, 47, 47, 46, 46, 46, 46, 46, 46, + 47, 47, 47, 47, 47, 47, 47, 47, 47, 48, 49, 50, 50, 50, 51, 52, 53, 53, 53, 53, 48, 48, 47, 47, 47, 47, 46, 46, 46, 46, 46, 46, 47, 47, + 47, 47, 47, 47, 47, 47, 47, 48, 49, 50, 50, 50, 51, 52, 53, 53, 53, 53, 31, 31, 37, 48, 31, 31, 38, 47, 31, 31, 38, 47, 31, 32, 39, 46, + 31, 32, 40, 46, 31, 32, 40, 46, 34, 35, 42, 47, 34, 36, 43, 47, 36, 37, 44, 47, 38, 40, 47, 47, 38, 40, 47, 47, 41, 42, 47, 49, 42, 43, + 47, 50, 44, 44, 47, 51, 48, 46, 48, 53, 48, 46, 48, 53, 31, 31, 31, 31, 31, 31, 34, 34, 36, 38, 38, 41, 42, 44, 48, 48, 31, 31, 31, 32, + 32, 32, 35, 36, 37, 40, 40, 42, 43, 44, 46, 46, 37, 38, 38, 39, 40, 40, 42, 43, 44, 47, 47, 47, 47, 47, 48, 48, 48, 47, 47, 46, 46, 46, + 47, 47, 47, 47, 47, 49, 50, 51, 53, 53, 32, 31, 31, 33, 37, 37, 45, 48, 31, 31, 31, 33, 37, 37, 45, 48, 31, 31, 31, 34, 38, 38, 45, 47, + 31, 31, 31, 34, 38, 38, 45, 47, 31, 31, 31, 34, 38, 38, 45, 47, 31, 31, 31, 34, 38, 38, 45, 47, 31, 31, 32, 34, 39, 39, 45, 46, 30, 31, + 32, 34, 39, 39, 44, 46, 30, 32, 32, 35, 40, 40, 44, 46, 30, 32, 32, 35, 40, 40, 44, 46, 30, 32, 32, 35, 40, 40, 44, 46, 31, 33, 33, 36, + 41, 41, 45, 46, 33, 34, 35, 37, 42, 42, 46, 47, 33, 35, 36, 38, 43, 43, 46, 47, 33, 35, 36, 38, 43, 43, 46, 47, 33, 35, 36, 38, 43, 43, + 46, 47, 35, 37, 37, 40, 44, 44, 46, 47, 36, 38, 39, 42, 46, 46, 47, 47, 37, 39, 40, 43, 47, 47, 47, 47, 37, 39, 40, 43, 47, 47, 47, 47, + 37, 39, 40, 43, 47, 47, 47, 47, 39, 40, 41, 43, 47, 47, 48, 48, 41, 42, 42, 44, 47, 47, 49, 49, 42, 42, 43, 44, 47, 47, 49, 50, 42, 42, + 43, 44, 47, 47, 49, 50, 42, 42, 43, 44, 47, 47, 49, 50, 44, 44, 44, 45, 47, 47, 50, 51, 47, 46, 46, 46, 48, 48, 51, 52, 49, 47, 46, 47, + 48, 48, 52, 53, 49, 47, 46, 47, 48, 48, 52, 53, 49, 47, 46, 47, 48, 48, 52, 53, 49, 47, 46, 47, 47, 47, 52, 53, 32, 31, 31, 31, 31, 31, + 31, 30, 30, 30, 30, 31, 33, 33, 33, 33, 35, 36, 37, 37, 37, 39, 41, 42, 42, 42, 44, 47, 49, 49, 49, 49, 31, 31, 31, 31, 31, 31, 31, 31, + 32, 32, 32, 33, 34, 35, 35, 35, 37, 38, 39, 39, 39, 40, 42, 42, 42, 42, 44, 46, 47, 47, 47, 47, 31, 31, 31, 31, 31, 31, 32, 32, 32, 32, + 32, 33, 35, 36, 36, 36, 37, 39, 40, 40, 40, 41, 42, 43, 43, 43, 44, 46, 46, 46, 46, 46, 33, 33, 34, 34, 34, 34, 34, 34, 35, 35, 35, 36, + 37, 38, 38, 38, 40, 42, 43, 43, 43, 43, 44, 44, 44, 44, 45, 46, 47, 47, 47, 47, 37, 37, 38, 38, 38, 38, 39, 39, 40, 40, 40, 41, 42, 43, + 43, 43, 44, 46, 47, 47, 47, 47, 47, 47, 47, 47, 47, 48, 48, 48, 48, 47, 37, 37, 38, 38, 38, 38, 39, 39, 40, 40, 40, 41, 42, 43, 43, 43, + 44, 46, 47, 47, 47, 47, 47, 47, 47, 47, 47, 48, 48, 48, 48, 47, 45, 45, 45, 45, 45, 45, 45, 44, 44, 44, 44, 45, 46, 46, 46, 46, 46, 47, + 47, 47, 47, 48, 49, 49, 49, 49, 50, 51, 52, 52, 52, 52, 48, 48, 47, 47, 47, 47, 46, 46, 46, 46, 46, 46, 47, 47, 47, 47, 47, 47, 47, 47, + 47, 48, 49, 50, 50, 50, 51, 52, 53, 53, 53, 53, + ], + ], + [ + [ + 31, 32, 32, 32, 32, 32, 32, 33, 32, 32, 33, 34, 32, 33, 34, 35, 31, 31, 31, 31, 32, 32, 32, 33, 31, 32, 32, 32, 32, 32, 32, 33, 31, 32, + 32, 32, 32, 32, 32, 33, 31, 32, 32, 32, 32, 32, 32, 33, 32, 32, 32, 32, 33, 33, 34, 35, 32, 32, 32, 32, 33, 34, 34, 35, 32, 32, 32, 32, + 34, 34, 35, 36, 33, 33, 33, 33, 35, 35, 36, 38, 32, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 32, 32, 32, 32, 34, 31, 31, 31, 31, 31, 31, + 31, 32, 32, 32, 32, 32, 32, 32, 33, 34, 31, 31, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 33, 34, 31, 31, 32, 32, 32, 32, 32, 32, + 32, 32, 32, 32, 32, 32, 33, 34, 31, 31, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 33, 34, 31, 31, 32, 32, 32, 32, 32, 32, 32, 32, + 32, 32, 32, 32, 33, 33, 31, 31, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 33, 33, 31, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 33, + 33, 33, 33, 34, 31, 32, 32, 32, 32, 32, 32, 32, 33, 33, 33, 33, 33, 33, 34, 35, 31, 32, 32, 32, 32, 32, 32, 32, 33, 33, 33, 33, 34, 34, + 34, 35, 31, 32, 32, 32, 32, 32, 32, 32, 33, 33, 33, 33, 34, 34, 34, 35, 32, 32, 32, 32, 32, 32, 32, 33, 33, 33, 33, 34, 35, 35, 35, 36, + 32, 32, 32, 32, 32, 32, 32, 33, 33, 34, 34, 35, 35, 35, 36, 37, 32, 32, 32, 32, 32, 32, 32, 33, 33, 34, 34, 35, 35, 35, 36, 37, 32, 33, + 33, 33, 33, 33, 33, 33, 34, 34, 34, 35, 36, 36, 36, 38, 34, 34, 34, 34, 34, 33, 33, 34, 35, 35, 35, 36, 37, 37, 38, 39, 32, 31, 31, 31, + 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 32, 32, 32, 32, 32, 32, 32, 32, 33, 34, 34, 31, 31, 31, 31, 31, 31, + 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 33, 34, 34, 31, 31, 31, 31, 31, 31, 31, 31, + 31, 31, 31, 31, 31, 31, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 33, 33, 34, 34, 31, 31, 31, 31, 32, 32, 32, 32, 32, 32, + 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 33, 33, 34, 34, 31, 31, 31, 32, 32, 32, 32, 32, 32, 32, 32, 32, + 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 33, 33, 34, 34, 31, 31, 31, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, + 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 33, 33, 34, 34, 31, 31, 31, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, + 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 33, 33, 34, 34, 31, 31, 31, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, + 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 33, 33, 34, 34, 31, 31, 31, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, + 32, 32, 32, 32, 32, 32, 32, 32, 33, 33, 34, 34, 31, 31, 31, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, + 32, 32, 32, 32, 32, 32, 33, 33, 33, 33, 31, 31, 31, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, + 32, 32, 32, 33, 33, 33, 33, 33, 31, 31, 31, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, + 32, 33, 33, 33, 33, 33, 31, 31, 31, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 33, + 33, 33, 33, 33, 31, 31, 31, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 33, 33, 33, + 33, 33, 31, 31, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 33, 33, 33, 33, 33, 33, 33, 33, 33, 34, 34, + 31, 31, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 33, 33, 33, 33, 33, 33, 33, 33, 33, 33, 33, 33, 34, 34, 34, 34, 31, 32, + 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 33, 33, 33, 33, 33, 33, 33, 33, 33, 33, 33, 33, 34, 34, 34, 35, 35, 31, 32, 32, 32, + 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 33, 33, 33, 33, 33, 33, 33, 33, 34, 34, 34, 34, 34, 34, 35, 35, 35, 31, 32, 32, 32, 32, 32, + 32, 32, 32, 32, 32, 32, 32, 32, 32, 33, 33, 33, 33, 33, 33, 33, 33, 34, 34, 34, 34, 34, 34, 35, 35, 35, 31, 32, 32, 32, 32, 32, 32, 32, + 32, 32, 32, 32, 32, 32, 32, 33, 33, 33, 33, 33, 33, 33, 33, 34, 34, 34, 34, 34, 34, 35, 35, 35, 31, 32, 32, 32, 32, 32, 32, 32, 32, 32, + 32, 32, 32, 32, 32, 33, 33, 33, 33, 33, 33, 33, 33, 34, 34, 34, 34, 34, 34, 35, 35, 35, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, + 32, 32, 33, 33, 33, 33, 33, 33, 33, 34, 34, 34, 34, 34, 34, 34, 35, 35, 35, 35, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, + 33, 33, 33, 33, 33, 33, 33, 34, 34, 34, 35, 35, 35, 35, 35, 35, 36, 36, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 33, 33, + 33, 34, 34, 34, 34, 34, 34, 35, 35, 35, 35, 35, 35, 36, 36, 36, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 33, 33, 33, 34, + 34, 34, 34, 34, 35, 35, 35, 35, 35, 35, 36, 36, 37, 37, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 33, 33, 33, 34, 34, 34, + 34, 34, 35, 35, 35, 35, 35, 35, 36, 36, 37, 37, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 33, 33, 33, 34, 34, 34, 34, 34, + 35, 35, 35, 35, 35, 35, 36, 36, 37, 37, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 33, 33, 33, 33, 33, 33, 34, 34, 34, 34, 34, 34, 35, 35, + 35, 35, 35, 35, 36, 36, 37, 37, 32, 32, 33, 33, 33, 33, 33, 33, 33, 33, 33, 33, 33, 33, 33, 34, 34, 34, 34, 34, 34, 35, 35, 35, 36, 36, + 36, 36, 36, 37, 38, 38, 33, 33, 33, 33, 33, 33, 33, 33, 33, 33, 33, 33, 33, 33, 33, 34, 34, 35, 35, 35, 35, 35, 35, 36, 36, 36, 36, 36, + 37, 38, 38, 38, 34, 34, 34, 34, 34, 34, 34, 34, 34, 33, 33, 33, 33, 33, 34, 34, 35, 35, 35, 35, 35, 35, 36, 36, 37, 37, 37, 37, 38, 38, + 39, 39, 34, 34, 34, 34, 34, 34, 34, 34, 34, 33, 33, 33, 33, 33, 34, 34, 35, 35, 35, 35, 35, 35, 36, 36, 37, 37, 37, 37, 38, 38, 39, 39, + 31, 31, 32, 32, 31, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 33, 32, 32, 33, 34, 32, 32, 34, 34, 32, 33, 34, 35, 33, 33, 35, 36, 31, 31, + 32, 32, 32, 32, 32, 33, 31, 32, 32, 32, 32, 32, 33, 33, 32, 32, 32, 32, 33, 34, 34, 35, 32, 32, 32, 33, 34, 34, 35, 36, 32, 31, 31, 31, + 31, 32, 32, 32, 31, 31, 32, 32, 32, 32, 32, 33, 31, 32, 32, 32, 32, 32, 32, 33, 31, 32, 32, 32, 32, 32, 32, 33, 31, 32, 32, 32, 32, 32, + 32, 33, 31, 32, 32, 32, 32, 33, 33, 33, 31, 32, 32, 32, 32, 33, 33, 33, 31, 32, 32, 32, 32, 33, 33, 33, 31, 32, 32, 32, 33, 34, 34, 34, + 32, 32, 32, 32, 33, 34, 34, 34, 32, 32, 32, 32, 33, 34, 34, 34, 32, 32, 32, 32, 33, 35, 35, 35, 32, 32, 33, 33, 34, 35, 35, 36, 32, 32, + 33, 33, 34, 35, 35, 36, 32, 33, 33, 33, 34, 36, 36, 36, 34, 34, 34, 34, 35, 37, 37, 38, 32, 31, 31, 31, 31, 31, 31, 31, 31, 32, 32, 32, + 32, 32, 32, 34, 31, 31, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 33, 34, 31, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 33, 33, + 33, 34, 31, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 33, 33, 33, 34, 31, 32, 32, 32, 32, 32, 32, 32, 33, 33, 33, 33, 34, 34, 34, 35, + 32, 32, 32, 32, 32, 33, 33, 33, 34, 34, 34, 35, 35, 35, 36, 37, 32, 32, 32, 32, 32, 33, 33, 33, 34, 34, 34, 35, 35, 35, 36, 37, 32, 33, + 33, 33, 33, 33, 33, 33, 34, 34, 34, 35, 36, 36, 36, 38, 32, 31, 31, 31, 31, 31, 31, 31, 31, 32, 32, 32, 32, 32, 32, 34, 31, 31, 31, 31, + 31, 31, 31, 31, 32, 32, 32, 32, 32, 32, 33, 34, 31, 31, 31, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 33, 34, 31, 31, 32, 32, 32, 32, + 32, 32, 32, 32, 32, 32, 32, 32, 33, 34, 31, 31, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 33, 34, 31, 31, 32, 32, 32, 32, 32, 32, + 32, 32, 32, 32, 32, 32, 33, 34, 31, 31, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 33, 34, 31, 31, 32, 32, 32, 32, 32, 32, 32, 32, + 32, 32, 32, 32, 33, 34, 31, 31, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 33, 34, 31, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, + 32, 32, 33, 33, 31, 32, 32, 32, 32, 32, 32, 32, 32, 32, 33, 33, 33, 33, 33, 33, 31, 32, 32, 32, 32, 32, 32, 32, 32, 32, 33, 33, 33, 33, + 33, 33, 31, 32, 32, 32, 32, 32, 32, 32, 32, 32, 33, 33, 33, 33, 33, 33, 31, 32, 32, 32, 32, 32, 32, 32, 32, 32, 33, 33, 33, 33, 33, 33, + 31, 32, 32, 32, 32, 32, 32, 32, 32, 33, 33, 33, 33, 33, 33, 34, 31, 32, 32, 32, 32, 32, 32, 32, 33, 33, 33, 33, 33, 33, 34, 34, 31, 32, + 32, 32, 32, 32, 32, 32, 33, 33, 34, 34, 34, 34, 34, 35, 32, 32, 32, 32, 32, 32, 32, 33, 33, 33, 34, 34, 34, 34, 34, 35, 32, 32, 32, 32, + 32, 32, 32, 33, 33, 33, 34, 34, 34, 34, 34, 35, 32, 32, 32, 32, 32, 32, 32, 33, 33, 33, 34, 34, 34, 34, 34, 35, 32, 32, 32, 32, 32, 32, + 32, 33, 33, 33, 34, 34, 34, 34, 34, 35, 32, 32, 32, 32, 32, 32, 32, 33, 33, 34, 34, 34, 34, 34, 35, 35, 32, 32, 32, 32, 32, 32, 32, 33, + 33, 34, 35, 35, 35, 35, 35, 36, 32, 32, 32, 32, 33, 33, 33, 33, 33, 34, 35, 35, 35, 35, 36, 36, 32, 32, 32, 32, 33, 33, 33, 33, 34, 34, + 35, 35, 35, 35, 36, 37, 32, 32, 32, 32, 33, 33, 33, 33, 34, 34, 35, 35, 35, 35, 36, 37, 32, 32, 32, 32, 33, 33, 33, 33, 34, 34, 35, 35, + 35, 35, 36, 37, 32, 32, 32, 33, 33, 33, 33, 33, 34, 34, 35, 35, 35, 35, 36, 37, 32, 33, 33, 33, 33, 33, 33, 33, 34, 35, 36, 36, 36, 36, + 36, 38, 33, 33, 33, 33, 33, 33, 33, 34, 34, 35, 36, 36, 36, 36, 37, 38, 34, 34, 34, 34, 34, 34, 34, 34, 35, 36, 37, 37, 37, 37, 38, 39, + 34, 34, 34, 34, 34, 34, 34, 34, 35, 36, 37, 37, 37, 37, 38, 39, 32, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 32, + 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 33, 34, 34, 31, 31, 31, 31, 31, 31, 31, 31, 31, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, + 32, 32, 32, 32, 32, 32, 32, 32, 33, 33, 34, 34, 31, 31, 31, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, + 32, 32, 32, 32, 32, 32, 33, 33, 34, 34, 31, 31, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, + 32, 32, 32, 33, 33, 33, 34, 34, 31, 31, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 33, 33, 33, + 33, 33, 33, 33, 34, 34, 31, 31, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 33, 33, 33, 33, 33, + 33, 33, 34, 34, 31, 31, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 33, 33, 33, 33, 33, 33, 33, + 34, 34, 31, 31, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 33, 33, 33, 33, 33, 33, 33, 33, 33, 33, 33, 33, 34, 34, 34, + 31, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 33, 33, 33, 33, 33, 33, 33, 33, 33, 34, 34, 34, 34, 34, 34, 35, 35, 32, 32, + 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 33, 33, 33, 33, 33, 33, 33, 34, 34, 34, 34, 34, 34, 34, 35, 35, 36, 36, 32, 32, 32, 32, + 32, 32, 32, 32, 32, 32, 33, 33, 33, 33, 33, 33, 34, 34, 34, 34, 34, 34, 35, 35, 35, 35, 35, 35, 36, 36, 37, 37, 32, 32, 32, 32, 32, 32, + 32, 32, 32, 32, 33, 33, 33, 33, 33, 33, 34, 34, 34, 34, 34, 34, 35, 35, 35, 35, 35, 35, 36, 36, 37, 37, 32, 32, 32, 32, 32, 32, 32, 32, + 32, 32, 33, 33, 33, 33, 33, 33, 34, 34, 34, 34, 34, 34, 35, 35, 35, 35, 35, 35, 36, 36, 37, 37, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, + 33, 33, 33, 33, 33, 33, 34, 34, 34, 34, 34, 34, 35, 35, 35, 35, 35, 35, 36, 36, 37, 37, 32, 33, 33, 33, 33, 33, 33, 33, 33, 33, 33, 33, + 33, 33, 33, 34, 34, 34, 34, 34, 34, 35, 35, 36, 36, 36, 36, 36, 36, 37, 38, 38, 34, 34, 34, 34, 34, 34, 34, 34, 34, 33, 33, 33, 33, 33, + 34, 34, 35, 35, 35, 35, 35, 35, 36, 36, 37, 37, 37, 37, 38, 38, 39, 39, 31, 31, 32, 32, 31, 32, 32, 32, 31, 32, 32, 32, 31, 32, 32, 32, + 31, 32, 32, 32, 32, 32, 32, 33, 32, 32, 32, 33, 32, 32, 33, 33, 32, 32, 33, 34, 32, 32, 33, 34, 32, 32, 33, 34, 32, 32, 34, 35, 32, 33, + 34, 35, 32, 33, 34, 35, 33, 33, 35, 36, 34, 34, 36, 37, 31, 31, 31, 31, 31, 32, 32, 32, 32, 32, 32, 32, 32, 32, 33, 34, 31, 32, 32, 32, + 32, 32, 32, 32, 32, 32, 32, 32, 33, 33, 33, 34, 32, 32, 32, 32, 32, 32, 32, 33, 33, 33, 33, 34, 34, 34, 35, 36, 32, 32, 32, 32, 32, 33, + 33, 33, 34, 34, 34, 35, 35, 35, 36, 37, 32, 31, 31, 31, 31, 32, 32, 32, 31, 31, 31, 31, 32, 32, 32, 33, 31, 31, 32, 32, 32, 32, 32, 33, + 31, 32, 32, 32, 32, 32, 32, 33, 31, 32, 32, 32, 32, 32, 32, 33, 31, 32, 32, 32, 32, 32, 32, 33, 31, 32, 32, 32, 32, 32, 32, 33, 31, 32, + 32, 32, 32, 32, 32, 33, 31, 32, 32, 32, 32, 32, 32, 33, 31, 32, 32, 32, 32, 32, 32, 33, 31, 32, 32, 32, 32, 33, 33, 33, 31, 32, 32, 32, + 32, 33, 33, 33, 31, 32, 32, 32, 32, 33, 33, 33, 31, 32, 32, 32, 32, 33, 33, 33, 31, 32, 32, 32, 32, 33, 33, 33, 31, 32, 32, 32, 33, 33, + 33, 34, 31, 32, 32, 32, 33, 34, 34, 34, 32, 32, 32, 32, 33, 34, 34, 34, 32, 32, 32, 32, 33, 34, 34, 34, 32, 32, 32, 32, 33, 34, 34, 34, + 32, 32, 32, 32, 33, 34, 34, 34, 32, 32, 32, 32, 33, 34, 34, 35, 32, 32, 32, 32, 33, 35, 35, 35, 32, 32, 33, 33, 33, 35, 35, 36, 32, 32, + 33, 33, 34, 35, 35, 36, 32, 32, 33, 33, 34, 35, 35, 36, 32, 32, 33, 33, 34, 35, 35, 36, 32, 32, 33, 33, 34, 35, 35, 36, 32, 33, 33, 33, + 34, 36, 36, 36, 33, 33, 33, 33, 34, 36, 36, 37, 34, 34, 34, 34, 35, 37, 37, 38, 34, 34, 34, 34, 35, 37, 37, 38, 32, 31, 31, 31, 31, 31, + 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 33, 34, 34, 31, 31, 31, 32, 32, 32, 32, 32, + 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 33, 33, 34, 34, 31, 31, 32, 32, 32, 32, 32, 32, 32, 32, + 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 33, 33, 33, 33, 33, 33, 33, 34, 34, 31, 31, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, + 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 33, 33, 33, 33, 33, 33, 33, 34, 34, 31, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, + 32, 33, 33, 33, 33, 33, 33, 33, 33, 33, 34, 34, 34, 34, 34, 34, 35, 35, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 33, 33, 33, 33, 33, 33, + 34, 34, 34, 34, 34, 34, 35, 35, 35, 35, 35, 35, 36, 36, 37, 37, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 33, 33, 33, 33, 33, 33, 34, 34, + 34, 34, 34, 34, 35, 35, 35, 35, 35, 35, 36, 36, 37, 37, 32, 33, 33, 33, 33, 33, 33, 33, 33, 33, 33, 33, 33, 33, 33, 34, 34, 34, 34, 34, + 34, 35, 35, 36, 36, 36, 36, 36, 36, 37, 38, 38, + ], + [ + 31, 31, 34, 38, 31, 32, 35, 40, 34, 35, 39, 43, 38, 40, 43, 47, 31, 31, 31, 30, 34, 35, 37, 40, 31, 31, 31, 31, 34, 35, 38, 41, 31, 31, + 31, 31, 35, 36, 39, 41, 30, 31, 31, 32, 35, 36, 40, 42, 34, 34, 35, 35, 39, 40, 43, 44, 35, 35, 36, 36, 40, 41, 44, 45, 37, 38, 39, 40, + 43, 44, 47, 47, 40, 41, 41, 42, 44, 45, 47, 48, 32, 31, 31, 31, 31, 30, 30, 31, 33, 33, 33, 35, 36, 36, 38, 41, 31, 31, 31, 31, 31, 31, + 31, 31, 33, 34, 34, 36, 37, 37, 39, 42, 31, 31, 31, 31, 31, 31, 31, 32, 34, 34, 34, 37, 38, 38, 40, 42, 31, 31, 31, 31, 31, 31, 31, 32, + 34, 34, 34, 37, 38, 38, 40, 42, 31, 31, 31, 31, 31, 31, 31, 32, 34, 35, 35, 37, 39, 39, 40, 42, 30, 31, 31, 31, 31, 32, 32, 32, 34, 35, + 35, 38, 40, 40, 41, 42, 30, 31, 31, 31, 31, 32, 32, 32, 34, 35, 35, 38, 40, 40, 41, 42, 31, 31, 32, 32, 32, 32, 32, 33, 35, 36, 36, 38, + 40, 40, 41, 43, 33, 33, 34, 34, 34, 34, 34, 35, 37, 38, 38, 41, 42, 42, 43, 44, 33, 34, 34, 34, 35, 35, 35, 36, 38, 39, 39, 41, 43, 43, + 44, 45, 33, 34, 34, 34, 35, 35, 35, 36, 38, 39, 39, 41, 43, 43, 44, 45, 35, 36, 37, 37, 37, 38, 38, 38, 41, 41, 41, 44, 46, 46, 46, 46, + 36, 37, 38, 38, 39, 40, 40, 40, 42, 43, 43, 46, 47, 47, 47, 47, 36, 37, 38, 38, 39, 40, 40, 40, 42, 43, 43, 46, 47, 47, 47, 47, 38, 39, + 40, 40, 40, 41, 41, 41, 43, 44, 44, 46, 47, 47, 47, 48, 41, 42, 42, 42, 42, 42, 42, 43, 44, 45, 45, 46, 47, 47, 48, 48, 32, 31, 31, 31, + 31, 31, 31, 31, 31, 30, 30, 30, 30, 30, 31, 32, 33, 33, 33, 33, 33, 34, 35, 36, 36, 36, 36, 37, 38, 40, 41, 41, 31, 31, 31, 31, 31, 31, + 31, 31, 31, 31, 30, 30, 30, 30, 31, 32, 33, 34, 34, 34, 34, 35, 36, 37, 37, 37, 37, 37, 39, 40, 42, 42, 31, 31, 31, 31, 31, 31, 31, 31, + 31, 31, 31, 31, 31, 31, 31, 32, 33, 34, 34, 34, 34, 35, 36, 37, 37, 37, 37, 38, 39, 40, 42, 42, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, + 31, 31, 31, 31, 32, 32, 34, 34, 34, 34, 34, 35, 36, 38, 38, 38, 38, 38, 40, 41, 42, 42, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, + 31, 31, 32, 33, 34, 34, 34, 34, 34, 35, 37, 38, 38, 38, 38, 39, 40, 41, 42, 42, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, + 32, 33, 34, 34, 34, 34, 34, 35, 37, 38, 38, 38, 38, 39, 40, 41, 42, 42, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 32, 33, + 34, 34, 34, 34, 34, 35, 37, 38, 38, 38, 38, 39, 40, 41, 42, 42, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 32, 33, 34, 34, + 34, 34, 34, 36, 37, 38, 38, 38, 38, 39, 40, 41, 42, 42, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 32, 33, 34, 35, 35, 35, + 35, 36, 37, 38, 39, 39, 39, 39, 40, 41, 42, 42, 30, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 32, 33, 34, 35, 35, 35, 35, 36, + 37, 39, 39, 39, 39, 40, 40, 41, 42, 42, 30, 30, 31, 31, 31, 31, 31, 31, 31, 31, 32, 32, 32, 32, 32, 33, 34, 35, 35, 35, 35, 36, 38, 39, + 40, 40, 40, 40, 41, 42, 42, 42, 30, 30, 31, 31, 31, 31, 31, 31, 31, 31, 32, 32, 32, 32, 32, 33, 34, 35, 35, 35, 35, 36, 38, 39, 40, 40, + 40, 40, 41, 42, 42, 42, 30, 30, 31, 31, 31, 31, 31, 31, 31, 31, 32, 32, 32, 32, 32, 33, 34, 35, 35, 35, 35, 36, 38, 39, 40, 40, 40, 40, + 41, 42, 42, 42, 30, 30, 31, 31, 31, 31, 31, 31, 31, 31, 32, 32, 32, 32, 32, 33, 34, 35, 35, 35, 35, 36, 38, 39, 40, 40, 40, 40, 41, 42, + 42, 42, 31, 31, 31, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 33, 34, 35, 36, 36, 36, 36, 37, 38, 40, 40, 40, 40, 41, 41, 42, 43, 43, + 32, 32, 32, 32, 33, 33, 33, 33, 33, 33, 33, 33, 33, 33, 34, 35, 36, 37, 37, 37, 37, 38, 39, 41, 41, 41, 41, 42, 42, 43, 43, 43, 33, 33, + 33, 34, 34, 34, 34, 34, 34, 34, 34, 34, 34, 34, 35, 36, 37, 38, 38, 38, 38, 39, 41, 42, 42, 42, 42, 43, 43, 44, 44, 44, 33, 34, 34, 34, + 34, 34, 34, 34, 35, 35, 35, 35, 35, 35, 36, 37, 38, 39, 39, 39, 39, 40, 41, 43, 43, 43, 43, 43, 44, 44, 45, 45, 33, 34, 34, 34, 34, 34, + 34, 34, 35, 35, 35, 35, 35, 35, 36, 37, 38, 39, 39, 39, 39, 40, 41, 43, 43, 43, 43, 43, 44, 44, 45, 45, 33, 34, 34, 34, 34, 34, 34, 34, + 35, 35, 35, 35, 35, 35, 36, 37, 38, 39, 39, 39, 39, 40, 41, 43, 43, 43, 43, 43, 44, 44, 45, 45, 33, 34, 34, 34, 34, 34, 34, 34, 35, 35, + 35, 35, 35, 35, 36, 37, 38, 39, 39, 39, 39, 40, 41, 43, 43, 43, 43, 43, 44, 44, 45, 45, 34, 35, 35, 35, 35, 35, 35, 36, 36, 36, 36, 36, + 36, 36, 37, 38, 39, 40, 40, 40, 40, 41, 42, 44, 44, 44, 44, 44, 45, 45, 45, 45, 35, 36, 36, 36, 37, 37, 37, 37, 37, 37, 38, 38, 38, 38, + 38, 39, 41, 41, 41, 41, 41, 42, 44, 45, 46, 46, 46, 46, 46, 46, 46, 46, 36, 37, 37, 38, 38, 38, 38, 38, 38, 39, 39, 39, 39, 39, 40, 41, + 42, 43, 43, 43, 43, 44, 45, 46, 47, 47, 47, 47, 47, 47, 47, 47, 36, 37, 37, 38, 38, 38, 38, 38, 39, 39, 40, 40, 40, 40, 40, 41, 42, 43, + 43, 43, 43, 44, 46, 47, 47, 47, 47, 47, 47, 47, 47, 47, 36, 37, 37, 38, 38, 38, 38, 38, 39, 39, 40, 40, 40, 40, 40, 41, 42, 43, 43, 43, + 43, 44, 46, 47, 47, 47, 47, 47, 47, 47, 47, 47, 36, 37, 37, 38, 38, 38, 38, 38, 39, 39, 40, 40, 40, 40, 40, 41, 42, 43, 43, 43, 43, 44, + 46, 47, 47, 47, 47, 47, 47, 47, 47, 47, 37, 37, 38, 38, 39, 39, 39, 39, 39, 40, 40, 40, 40, 40, 41, 42, 43, 43, 43, 43, 43, 44, 46, 47, + 47, 47, 47, 47, 47, 47, 47, 47, 38, 39, 39, 40, 40, 40, 40, 40, 40, 40, 41, 41, 41, 41, 41, 42, 43, 44, 44, 44, 44, 45, 46, 47, 47, 47, + 47, 47, 47, 48, 48, 48, 40, 40, 40, 41, 41, 41, 41, 41, 41, 41, 42, 42, 42, 42, 42, 43, 44, 44, 44, 44, 44, 45, 46, 47, 47, 47, 47, 47, + 48, 48, 48, 48, 41, 42, 42, 42, 42, 42, 42, 42, 42, 42, 42, 42, 42, 42, 43, 43, 44, 45, 45, 45, 45, 45, 46, 47, 47, 47, 47, 47, 48, 48, + 48, 48, 41, 42, 42, 42, 42, 42, 42, 42, 42, 42, 42, 42, 42, 42, 43, 43, 44, 45, 45, 45, 45, 45, 46, 47, 47, 47, 47, 47, 48, 48, 48, 48, + 31, 31, 35, 37, 31, 31, 36, 38, 31, 32, 37, 39, 31, 32, 37, 40, 34, 36, 40, 43, 35, 37, 42, 44, 38, 40, 45, 47, 41, 42, 45, 47, 31, 31, + 31, 31, 34, 35, 38, 41, 31, 31, 32, 32, 36, 37, 40, 42, 35, 36, 37, 37, 40, 42, 45, 45, 37, 38, 39, 40, 43, 44, 47, 47, 32, 31, 31, 31, + 33, 37, 37, 38, 31, 31, 31, 31, 33, 38, 38, 39, 31, 31, 31, 31, 34, 38, 38, 40, 31, 31, 31, 31, 34, 38, 38, 40, 31, 31, 32, 32, 34, 39, + 39, 40, 30, 31, 32, 32, 35, 40, 40, 41, 30, 31, 32, 32, 35, 40, 40, 41, 31, 32, 33, 33, 35, 40, 40, 41, 33, 34, 35, 35, 37, 42, 42, 43, + 33, 35, 36, 36, 38, 43, 43, 44, 33, 35, 36, 36, 38, 43, 43, 44, 35, 37, 38, 38, 41, 45, 45, 46, 37, 39, 40, 40, 43, 47, 47, 47, 37, 39, + 40, 40, 43, 47, 47, 47, 39, 40, 41, 41, 43, 47, 47, 47, 42, 42, 43, 43, 44, 47, 47, 48, 32, 31, 31, 31, 31, 30, 30, 31, 33, 33, 33, 35, + 37, 37, 39, 42, 31, 31, 31, 31, 31, 31, 31, 32, 34, 35, 35, 37, 39, 39, 40, 42, 31, 31, 31, 31, 32, 32, 32, 33, 35, 36, 36, 38, 40, 40, + 41, 43, 31, 31, 31, 31, 32, 32, 32, 33, 35, 36, 36, 38, 40, 40, 41, 43, 33, 33, 34, 34, 34, 35, 35, 35, 37, 38, 38, 41, 43, 43, 43, 44, + 37, 38, 38, 38, 39, 40, 40, 40, 42, 43, 43, 45, 47, 47, 47, 47, 37, 38, 38, 38, 39, 40, 40, 40, 42, 43, 43, 45, 47, 47, 47, 47, 38, 39, + 40, 40, 40, 41, 41, 41, 43, 44, 44, 46, 47, 47, 47, 48, 32, 31, 31, 31, 31, 31, 31, 31, 33, 35, 37, 37, 37, 37, 38, 42, 31, 31, 31, 31, + 31, 31, 31, 31, 33, 35, 37, 37, 37, 37, 39, 42, 31, 31, 31, 31, 31, 31, 31, 32, 33, 35, 38, 38, 38, 38, 39, 42, 31, 31, 31, 31, 31, 31, + 31, 32, 34, 36, 38, 38, 38, 38, 40, 42, 31, 31, 31, 31, 31, 31, 31, 32, 34, 36, 38, 38, 38, 38, 40, 42, 31, 31, 31, 31, 31, 31, 31, 32, + 34, 36, 38, 38, 38, 38, 40, 42, 31, 31, 31, 31, 31, 31, 31, 32, 34, 36, 38, 38, 38, 38, 40, 42, 31, 31, 31, 31, 31, 31, 31, 32, 34, 36, + 38, 38, 38, 38, 40, 42, 31, 31, 31, 31, 32, 32, 32, 32, 34, 36, 39, 39, 39, 39, 40, 42, 30, 31, 31, 32, 32, 32, 32, 32, 34, 37, 39, 39, + 39, 39, 40, 42, 30, 31, 31, 32, 32, 32, 32, 33, 35, 37, 40, 40, 40, 40, 41, 42, 30, 31, 31, 32, 32, 32, 32, 33, 35, 37, 40, 40, 40, 40, + 41, 42, 30, 31, 31, 32, 32, 32, 32, 33, 35, 37, 40, 40, 40, 40, 41, 42, 30, 31, 31, 32, 32, 32, 32, 33, 35, 37, 40, 40, 40, 40, 41, 42, + 31, 31, 32, 32, 33, 33, 33, 33, 35, 38, 40, 40, 40, 40, 41, 43, 32, 32, 33, 33, 34, 34, 34, 34, 36, 39, 41, 41, 41, 41, 42, 44, 33, 33, + 34, 35, 35, 35, 35, 35, 37, 40, 42, 42, 42, 42, 43, 44, 33, 34, 35, 35, 36, 36, 36, 36, 38, 40, 43, 43, 43, 43, 44, 45, 33, 34, 35, 35, + 36, 36, 36, 36, 38, 40, 43, 43, 43, 43, 44, 45, 33, 34, 35, 35, 36, 36, 36, 36, 38, 40, 43, 43, 43, 43, 44, 45, 33, 34, 35, 35, 36, 36, + 36, 36, 38, 40, 43, 43, 43, 43, 44, 45, 34, 35, 36, 37, 37, 37, 37, 37, 39, 42, 44, 44, 44, 44, 45, 45, 35, 36, 37, 38, 38, 38, 38, 39, + 41, 43, 45, 45, 45, 45, 46, 46, 36, 37, 38, 39, 39, 39, 39, 40, 42, 44, 47, 47, 47, 47, 47, 47, 37, 38, 39, 40, 40, 40, 40, 41, 43, 45, + 47, 47, 47, 47, 47, 47, 37, 38, 39, 40, 40, 40, 40, 41, 43, 45, 47, 47, 47, 47, 47, 47, 37, 38, 39, 40, 40, 40, 40, 41, 43, 45, 47, 47, + 47, 47, 47, 47, 37, 38, 39, 40, 40, 40, 40, 41, 43, 45, 47, 47, 47, 47, 47, 47, 39, 39, 40, 41, 41, 41, 41, 42, 43, 45, 47, 47, 47, 47, + 47, 48, 40, 41, 41, 42, 42, 42, 42, 42, 44, 45, 47, 47, 47, 47, 47, 48, 42, 42, 42, 43, 43, 43, 43, 43, 44, 46, 47, 47, 47, 47, 48, 48, + 42, 42, 42, 43, 43, 43, 43, 43, 44, 46, 47, 47, 47, 47, 48, 48, 32, 31, 31, 31, 31, 31, 31, 31, 31, 30, 30, 30, 30, 30, 31, 32, 33, 33, + 33, 33, 33, 34, 35, 36, 37, 37, 37, 37, 39, 40, 42, 42, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 32, 33, 34, 34, 34, + 34, 35, 36, 37, 38, 38, 38, 38, 39, 41, 42, 42, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 32, 33, 34, 35, 35, 35, 35, 36, + 37, 38, 39, 39, 39, 39, 40, 41, 42, 42, 31, 31, 31, 31, 31, 31, 31, 31, 31, 32, 32, 32, 32, 32, 32, 33, 35, 35, 35, 35, 35, 37, 38, 39, + 40, 40, 40, 40, 41, 42, 43, 43, 31, 31, 31, 31, 31, 31, 31, 31, 32, 32, 32, 32, 32, 32, 33, 34, 35, 36, 36, 36, 36, 37, 38, 39, 40, 40, + 40, 40, 41, 42, 43, 43, 31, 31, 31, 31, 31, 31, 31, 31, 32, 32, 32, 32, 32, 32, 33, 34, 35, 36, 36, 36, 36, 37, 38, 39, 40, 40, 40, 40, + 41, 42, 43, 43, 31, 31, 31, 31, 31, 31, 31, 31, 32, 32, 32, 32, 32, 32, 33, 34, 35, 36, 36, 36, 36, 37, 38, 39, 40, 40, 40, 40, 41, 42, + 43, 43, 31, 31, 32, 32, 32, 32, 32, 32, 32, 32, 33, 33, 33, 33, 33, 34, 35, 36, 36, 36, 36, 37, 39, 40, 41, 41, 41, 41, 42, 42, 43, 43, + 33, 33, 33, 34, 34, 34, 34, 34, 34, 34, 35, 35, 35, 35, 35, 36, 37, 38, 38, 38, 38, 39, 41, 42, 43, 43, 43, 43, 43, 44, 44, 44, 35, 35, + 35, 36, 36, 36, 36, 36, 36, 37, 37, 37, 37, 37, 38, 39, 40, 40, 40, 40, 40, 42, 43, 44, 45, 45, 45, 45, 45, 45, 46, 46, 37, 37, 38, 38, + 38, 38, 38, 38, 39, 39, 40, 40, 40, 40, 40, 41, 42, 43, 43, 43, 43, 44, 45, 47, 47, 47, 47, 47, 47, 47, 47, 47, 37, 37, 38, 38, 38, 38, + 38, 38, 39, 39, 40, 40, 40, 40, 40, 41, 42, 43, 43, 43, 43, 44, 45, 47, 47, 47, 47, 47, 47, 47, 47, 47, 37, 37, 38, 38, 38, 38, 38, 38, + 39, 39, 40, 40, 40, 40, 40, 41, 42, 43, 43, 43, 43, 44, 45, 47, 47, 47, 47, 47, 47, 47, 47, 47, 37, 37, 38, 38, 38, 38, 38, 38, 39, 39, + 40, 40, 40, 40, 40, 41, 42, 43, 43, 43, 43, 44, 45, 47, 47, 47, 47, 47, 47, 47, 47, 47, 38, 39, 39, 40, 40, 40, 40, 40, 40, 40, 41, 41, + 41, 41, 41, 42, 43, 44, 44, 44, 44, 45, 46, 47, 47, 47, 47, 47, 47, 47, 48, 48, 42, 42, 42, 42, 42, 42, 42, 42, 42, 42, 42, 42, 42, 42, + 43, 44, 44, 45, 45, 45, 45, 45, 46, 47, 47, 47, 47, 47, 48, 48, 48, 48, 31, 31, 35, 37, 31, 31, 35, 38, 31, 31, 36, 38, 31, 31, 36, 38, + 31, 32, 36, 39, 31, 32, 37, 40, 31, 32, 37, 40, 31, 33, 38, 40, 33, 35, 40, 42, 34, 36, 40, 43, 34, 36, 40, 43, 36, 38, 43, 45, 38, 40, + 45, 47, 38, 40, 45, 47, 39, 41, 45, 47, 42, 43, 46, 47, 31, 31, 31, 31, 31, 31, 31, 31, 33, 34, 34, 36, 38, 38, 39, 42, 31, 31, 31, 31, + 32, 32, 32, 33, 35, 36, 36, 38, 40, 40, 41, 43, 35, 35, 36, 36, 36, 37, 37, 38, 40, 40, 40, 43, 45, 45, 45, 46, 37, 38, 38, 38, 39, 40, + 40, 40, 42, 43, 43, 45, 47, 47, 47, 47, 32, 31, 31, 31, 33, 37, 37, 38, 31, 31, 31, 31, 33, 37, 37, 39, 31, 31, 31, 31, 33, 38, 38, 39, + 31, 31, 31, 31, 34, 38, 38, 40, 31, 31, 31, 31, 34, 38, 38, 40, 31, 31, 31, 31, 34, 38, 38, 40, 31, 31, 31, 31, 34, 38, 38, 40, 31, 31, + 31, 31, 34, 38, 38, 40, 31, 31, 32, 32, 34, 39, 39, 40, 30, 31, 32, 32, 34, 39, 39, 40, 30, 31, 32, 32, 35, 40, 40, 41, 30, 31, 32, 32, + 35, 40, 40, 41, 30, 31, 32, 32, 35, 40, 40, 41, 30, 31, 32, 32, 35, 40, 40, 41, 31, 32, 33, 33, 35, 40, 40, 41, 32, 33, 34, 34, 36, 41, + 41, 42, 33, 34, 35, 35, 37, 42, 42, 43, 33, 35, 36, 36, 38, 43, 43, 44, 33, 35, 36, 36, 38, 43, 43, 44, 33, 35, 36, 36, 38, 43, 43, 44, + 33, 35, 36, 36, 38, 43, 43, 44, 34, 36, 37, 37, 39, 44, 44, 45, 35, 37, 38, 38, 41, 45, 45, 46, 36, 38, 39, 39, 42, 47, 47, 47, 37, 39, + 40, 40, 43, 47, 47, 47, 37, 39, 40, 40, 43, 47, 47, 47, 37, 39, 40, 40, 43, 47, 47, 47, 37, 39, 40, 40, 43, 47, 47, 47, 39, 40, 41, 41, + 43, 47, 47, 47, 40, 41, 42, 42, 44, 47, 47, 47, 42, 42, 43, 43, 44, 47, 47, 48, 42, 42, 43, 43, 44, 47, 47, 48, 32, 31, 31, 31, 31, 31, + 31, 31, 31, 30, 30, 30, 30, 30, 31, 32, 33, 33, 33, 33, 33, 34, 35, 36, 37, 37, 37, 37, 39, 40, 42, 42, 31, 31, 31, 31, 31, 31, 31, 31, + 31, 31, 31, 31, 31, 31, 32, 33, 34, 35, 35, 35, 35, 36, 37, 38, 39, 39, 39, 39, 40, 41, 42, 42, 31, 31, 31, 31, 31, 31, 31, 31, 32, 32, + 32, 32, 32, 32, 33, 34, 35, 36, 36, 36, 36, 37, 38, 39, 40, 40, 40, 40, 41, 42, 43, 43, 31, 31, 31, 31, 31, 31, 31, 31, 32, 32, 32, 32, + 32, 32, 33, 34, 35, 36, 36, 36, 36, 37, 38, 39, 40, 40, 40, 40, 41, 42, 43, 43, 33, 33, 33, 34, 34, 34, 34, 34, 34, 34, 35, 35, 35, 35, + 35, 36, 37, 38, 38, 38, 38, 39, 41, 42, 43, 43, 43, 43, 43, 44, 44, 44, 37, 37, 38, 38, 38, 38, 38, 38, 39, 39, 40, 40, 40, 40, 40, 41, + 42, 43, 43, 43, 43, 44, 45, 47, 47, 47, 47, 47, 47, 47, 47, 47, 37, 37, 38, 38, 38, 38, 38, 38, 39, 39, 40, 40, 40, 40, 40, 41, 42, 43, + 43, 43, 43, 44, 45, 47, 47, 47, 47, 47, 47, 47, 47, 47, 38, 39, 39, 40, 40, 40, 40, 40, 40, 40, 41, 41, 41, 41, 41, 42, 43, 44, 44, 44, + 44, 45, 46, 47, 47, 47, 47, 47, 47, 47, 48, 48, + ], + ], + [ + [ + 31, 31, 31, 32, 31, 32, 32, 32, 31, 32, 32, 32, 32, 32, 32, 33, 31, 31, 31, 31, 31, 31, 32, 32, 31, 32, 32, 32, 32, 32, 32, 32, 31, 32, + 32, 32, 32, 32, 32, 32, 31, 32, 32, 32, 32, 32, 32, 32, 31, 32, 32, 32, 32, 32, 32, 32, 31, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, + 32, 32, 33, 33, 32, 32, 32, 32, 32, 32, 33, 33, 32, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, + 31, 31, 31, 31, 31, 31, 32, 32, 32, 32, 31, 31, 31, 31, 31, 31, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 31, 31, 31, 32, 32, 32, 32, 32, + 32, 32, 32, 32, 32, 32, 32, 32, 31, 31, 31, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 31, 31, 31, 32, 32, 32, 32, 32, 32, 32, + 32, 32, 32, 32, 32, 32, 31, 31, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 31, 31, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, + 32, 32, 32, 32, 31, 31, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 31, 31, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, + 32, 32, 31, 31, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 31, 31, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 33, 33, 33, 33, + 31, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 33, 33, 33, 33, 33, 31, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 33, 33, 33, 33, 33, 31, 32, + 32, 32, 32, 32, 32, 32, 32, 32, 32, 33, 33, 33, 33, 33, 31, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 33, 33, 33, 33, 33, 32, 31, 31, 31, + 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 32, 31, 31, 31, 31, 31, 31, + 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 32, 32, 32, 32, 32, 32, 32, 32, 31, 31, 31, 31, 31, 31, 31, 31, + 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 32, 32, 32, 32, 32, 32, 32, 32, 32, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, + 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 32, + 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 31, 31, 31, 31, 31, 32, 32, 32, 32, 32, 32, 32, 32, 32, + 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 31, 31, 31, 31, 31, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, + 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 31, 31, 31, 31, 31, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, + 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 31, 31, 31, 31, 31, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, + 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 31, 31, 31, 31, 31, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, + 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 31, 31, 31, 31, 31, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, + 32, 32, 32, 32, 32, 32, 32, 32, 31, 31, 31, 31, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, + 32, 32, 32, 32, 32, 32, 31, 31, 31, 31, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, + 32, 32, 32, 32, 31, 31, 31, 31, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, + 32, 32, 31, 31, 31, 31, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, + 31, 31, 31, 31, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 31, 31, + 31, 31, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 31, 31, 31, 31, + 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 31, 31, 31, 31, 32, 32, + 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 31, 31, 31, 31, 32, 32, 32, 32, + 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 31, 31, 31, 31, 32, 32, 32, 32, 32, 32, + 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 31, 31, 31, 32, 32, 32, 32, 32, 32, 32, 32, 32, + 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 33, 31, 31, 31, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, + 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 33, 33, 33, 33, 33, 33, 33, 33, 31, 31, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, + 32, 32, 32, 32, 32, 32, 32, 33, 33, 33, 33, 33, 33, 33, 33, 33, 31, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, + 32, 32, 32, 32, 33, 33, 33, 33, 33, 33, 33, 33, 33, 33, 31, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, + 32, 32, 33, 33, 33, 33, 33, 33, 33, 33, 33, 33, 31, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, + 33, 33, 33, 33, 33, 33, 33, 33, 33, 33, 31, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 33, 33, + 33, 33, 33, 33, 33, 33, 33, 33, 31, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 33, 33, 33, 33, + 33, 33, 33, 33, 33, 33, 31, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 33, 33, 33, 33, 33, 33, + 33, 33, 33, 33, 31, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 33, 33, 33, 33, 33, 33, 33, 33, + 33, 33, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 33, 33, 33, 33, 33, 33, 33, 33, 33, 33, 33, + 31, 31, 31, 32, 31, 32, 32, 32, 31, 32, 32, 32, 31, 32, 32, 32, 31, 32, 32, 32, 31, 32, 32, 33, 32, 32, 32, 33, 32, 32, 32, 33, 31, 31, + 31, 31, 31, 31, 32, 32, 31, 32, 32, 32, 32, 32, 32, 32, 31, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 33, 33, 33, 32, 31, 31, 31, + 31, 31, 31, 32, 31, 31, 31, 31, 31, 31, 32, 32, 31, 31, 32, 32, 32, 32, 32, 32, 31, 32, 32, 32, 32, 32, 32, 32, 31, 32, 32, 32, 32, 32, + 32, 32, 31, 32, 32, 32, 32, 32, 32, 32, 31, 32, 32, 32, 32, 32, 32, 32, 31, 32, 32, 32, 32, 32, 32, 32, 31, 32, 32, 32, 32, 32, 32, 32, + 31, 32, 32, 32, 32, 32, 32, 32, 31, 32, 32, 32, 32, 32, 32, 32, 31, 32, 32, 32, 32, 32, 33, 33, 31, 32, 32, 32, 32, 32, 33, 33, 32, 32, + 32, 32, 32, 32, 33, 34, 32, 32, 32, 32, 32, 32, 33, 34, 32, 32, 32, 32, 32, 32, 33, 34, 32, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, + 31, 32, 32, 32, 31, 31, 31, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 31, 31, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, + 32, 32, 31, 31, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 31, 31, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, + 31, 31, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 31, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 33, 33, 33, 33, 33, 32, 32, + 32, 32, 32, 32, 32, 32, 32, 32, 32, 33, 33, 34, 34, 34, 32, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 32, 32, 32, 31, 31, 31, 31, + 31, 31, 31, 31, 31, 31, 31, 31, 32, 32, 32, 32, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 32, 32, 32, 32, 32, 31, 31, 31, 31, 32, 32, + 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 31, 31, 31, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 31, 31, 32, 32, 32, 32, 32, 32, + 32, 32, 32, 32, 32, 32, 32, 32, 31, 31, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 31, 31, 32, 32, 32, 32, 32, 32, 32, 32, + 32, 32, 32, 32, 32, 32, 31, 31, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 31, 31, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, + 32, 32, 32, 32, 31, 31, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 31, 31, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, + 32, 32, 31, 31, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 31, 31, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, + 31, 31, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 31, 31, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 33, 31, 31, + 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 33, 31, 31, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 33, 31, 31, 32, 32, + 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 33, 31, 31, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 33, 31, 31, 32, 32, 32, 32, + 32, 32, 32, 32, 32, 32, 32, 32, 32, 33, 31, 31, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 33, 33, 33, 31, 32, 32, 32, 32, 32, 32, 32, + 32, 32, 32, 32, 33, 33, 33, 33, 31, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 33, 33, 33, 33, 31, 32, 32, 32, 32, 32, 32, 32, 32, 32, + 32, 33, 33, 33, 33, 34, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 33, 33, 33, 34, 34, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 33, + 33, 33, 34, 34, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 33, 33, 33, 34, 34, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 33, 33, 33, + 34, 34, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 33, 33, 33, 34, 34, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 33, 33, 33, 34, 34, + 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 33, 33, 33, 34, 34, 32, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, + 31, 31, 31, 31, 31, 31, 31, 32, 32, 32, 32, 32, 32, 32, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, + 31, 31, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 31, 31, 31, 31, 31, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, + 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 31, 31, 31, 31, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, + 32, 32, 32, 32, 32, 32, 32, 32, 31, 31, 31, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, + 32, 32, 32, 32, 32, 32, 31, 31, 31, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, + 32, 32, 32, 32, 31, 31, 31, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, + 32, 32, 31, 31, 31, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, + 31, 31, 31, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 31, 31, + 31, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 31, 31, 31, 32, + 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 31, 31, 32, 32, 32, 32, + 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 33, 33, 33, 33, 33, 33, 33, 33, 31, 32, 32, 32, 32, 32, 32, 32, + 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 33, 33, 33, 33, 33, 33, 33, 33, 33, 33, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, + 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 33, 33, 33, 33, 33, 33, 33, 33, 33, 33, 33, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, + 32, 32, 32, 32, 32, 32, 32, 32, 32, 33, 33, 33, 33, 34, 34, 34, 34, 34, 34, 34, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, + 32, 33, 33, 33, 33, 33, 33, 33, 33, 33, 34, 34, 34, 34, 34, 34, 34, 34, 31, 31, 31, 32, 31, 31, 31, 32, 31, 32, 32, 32, 31, 32, 32, 32, + 31, 32, 32, 32, 31, 32, 32, 32, 31, 32, 32, 32, 31, 32, 32, 32, 31, 32, 32, 32, 31, 32, 32, 32, 31, 32, 32, 32, 32, 32, 32, 33, 32, 32, + 32, 33, 32, 32, 32, 33, 32, 32, 32, 33, 32, 32, 32, 33, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 32, 32, 32, 32, 32, 31, 31, 32, 32, + 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 31, 31, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, + 32, 32, 32, 32, 32, 33, 33, 33, 33, 33, 32, 31, 31, 31, 31, 31, 31, 32, 31, 31, 31, 31, 31, 31, 32, 32, 31, 31, 31, 31, 31, 31, 32, 32, + 31, 31, 32, 32, 32, 32, 32, 32, 31, 31, 32, 32, 32, 32, 32, 32, 31, 32, 32, 32, 32, 32, 32, 32, 31, 32, 32, 32, 32, 32, 32, 32, 31, 32, + 32, 32, 32, 32, 32, 32, 31, 32, 32, 32, 32, 32, 32, 32, 31, 32, 32, 32, 32, 32, 32, 32, 31, 32, 32, 32, 32, 32, 32, 32, 31, 32, 32, 32, + 32, 32, 32, 32, 31, 32, 32, 32, 32, 32, 32, 32, 31, 32, 32, 32, 32, 32, 32, 32, 31, 32, 32, 32, 32, 32, 32, 32, 31, 32, 32, 32, 32, 32, + 32, 32, 31, 32, 32, 32, 32, 32, 32, 32, 31, 32, 32, 32, 32, 32, 32, 32, 31, 32, 32, 32, 32, 32, 32, 32, 31, 32, 32, 32, 32, 32, 32, 32, + 31, 32, 32, 32, 32, 32, 32, 32, 31, 32, 32, 32, 32, 32, 32, 33, 31, 32, 32, 32, 32, 32, 33, 33, 31, 32, 32, 32, 32, 32, 33, 33, 31, 32, + 32, 32, 32, 32, 33, 33, 32, 32, 32, 32, 32, 32, 33, 34, 32, 32, 32, 32, 32, 32, 33, 34, 32, 32, 32, 32, 32, 32, 33, 34, 32, 32, 32, 32, + 32, 32, 33, 34, 32, 32, 32, 32, 32, 32, 33, 34, 32, 32, 32, 32, 32, 32, 33, 34, 32, 32, 32, 32, 32, 32, 33, 34, 32, 31, 31, 31, 31, 31, + 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 32, 32, 32, 32, 32, 32, 32, 31, 31, 31, 31, 31, 32, 32, 32, + 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 31, 31, 31, 32, 32, 32, 32, 32, 32, 32, + 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 31, 31, 31, 32, 32, 32, 32, 32, 32, 32, 32, 32, + 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 31, 31, 31, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, + 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 31, 31, 31, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, + 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 31, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, + 32, 32, 32, 32, 33, 33, 33, 33, 33, 33, 33, 33, 33, 33, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, + 32, 33, 33, 33, 33, 34, 34, 34, 34, 34, 34, 34, + ], + [ + 31, 31, 31, 34, 31, 31, 31, 35, 31, 31, 32, 35, 34, 35, 35, 39, 31, 31, 31, 31, 30, 31, 33, 33, 31, 31, 31, 31, 31, 32, 34, 34, 31, 31, + 31, 31, 31, 32, 34, 34, 31, 31, 31, 31, 31, 32, 35, 35, 30, 31, 31, 31, 32, 32, 35, 35, 31, 32, 32, 32, 32, 33, 36, 36, 33, 34, 34, 35, + 35, 36, 39, 39, 33, 34, 34, 35, 35, 36, 39, 39, 32, 31, 31, 31, 31, 31, 31, 30, 30, 30, 30, 31, 33, 33, 33, 33, 31, 31, 31, 31, 31, 31, + 31, 31, 30, 30, 30, 32, 33, 34, 34, 34, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 32, 33, 34, 34, 34, 31, 31, 31, 31, 31, 31, 31, 31, + 31, 31, 31, 32, 34, 34, 34, 34, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 32, 34, 34, 34, 34, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, + 31, 32, 34, 34, 34, 34, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 33, 34, 35, 35, 35, 30, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 33, + 34, 35, 35, 35, 30, 30, 31, 31, 31, 31, 31, 31, 32, 32, 32, 33, 34, 35, 35, 35, 30, 30, 31, 31, 31, 31, 31, 31, 32, 32, 32, 33, 34, 35, + 35, 35, 30, 30, 31, 31, 31, 31, 31, 31, 32, 32, 32, 33, 34, 35, 35, 35, 31, 32, 32, 32, 32, 32, 33, 33, 33, 33, 33, 34, 36, 37, 37, 37, + 33, 33, 33, 34, 34, 34, 34, 34, 34, 34, 34, 36, 37, 38, 38, 38, 33, 34, 34, 34, 34, 34, 35, 35, 35, 35, 35, 37, 38, 39, 39, 39, 33, 34, + 34, 34, 34, 34, 35, 35, 35, 35, 35, 37, 38, 39, 39, 39, 33, 34, 34, 34, 34, 34, 35, 35, 35, 35, 35, 37, 38, 39, 39, 39, 32, 31, 31, 31, + 31, 31, 31, 31, 31, 31, 31, 31, 31, 30, 30, 30, 30, 30, 30, 30, 30, 31, 31, 32, 33, 33, 33, 33, 33, 33, 33, 34, 31, 31, 31, 31, 31, 31, + 31, 31, 31, 31, 31, 31, 31, 31, 30, 30, 30, 30, 30, 30, 30, 31, 31, 32, 33, 33, 33, 33, 33, 33, 33, 34, 31, 31, 31, 31, 31, 31, 31, 31, + 31, 31, 31, 31, 31, 31, 31, 30, 30, 30, 30, 30, 30, 31, 32, 32, 33, 34, 34, 34, 34, 34, 34, 34, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, + 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 32, 33, 33, 34, 34, 34, 34, 34, 34, 35, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, + 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 32, 33, 33, 34, 34, 34, 34, 34, 34, 35, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, + 31, 31, 31, 31, 31, 31, 31, 32, 32, 33, 34, 34, 34, 34, 34, 34, 34, 35, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, + 31, 31, 31, 31, 31, 32, 32, 33, 34, 34, 34, 34, 34, 34, 34, 35, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, + 31, 31, 31, 32, 32, 33, 34, 34, 34, 34, 34, 34, 34, 35, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, + 31, 32, 32, 33, 34, 34, 34, 34, 34, 34, 34, 35, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 32, + 32, 33, 34, 34, 34, 34, 34, 34, 34, 35, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 32, 32, 33, + 34, 34, 34, 34, 34, 34, 34, 35, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 32, 32, 33, 34, 35, + 35, 35, 35, 35, 35, 35, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 32, 33, 33, 34, 35, 35, 35, + 35, 35, 35, 35, 30, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 32, 33, 33, 34, 35, 35, 35, 35, 35, + 35, 36, 30, 30, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 32, 33, 34, 34, 35, 35, 35, 35, 35, 35, 36, + 30, 30, 30, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 32, 32, 32, 32, 32, 32, 32, 33, 34, 34, 35, 35, 35, 35, 35, 35, 36, 30, 30, + 30, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 32, 32, 32, 32, 32, 32, 32, 33, 34, 34, 35, 35, 35, 35, 35, 35, 36, 30, 30, 30, 31, + 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 32, 32, 32, 32, 32, 32, 32, 33, 34, 34, 35, 35, 35, 35, 35, 35, 36, 30, 30, 30, 31, 31, 31, + 31, 31, 31, 31, 31, 31, 31, 31, 31, 32, 32, 32, 32, 32, 32, 32, 33, 34, 34, 35, 35, 35, 35, 35, 35, 36, 30, 30, 30, 31, 31, 31, 31, 31, + 31, 31, 31, 31, 31, 31, 31, 32, 32, 32, 32, 32, 32, 32, 33, 34, 34, 35, 35, 35, 35, 35, 35, 36, 30, 30, 30, 31, 31, 31, 31, 31, 31, 31, + 31, 31, 31, 31, 31, 32, 32, 32, 32, 32, 32, 32, 33, 34, 34, 35, 35, 35, 35, 35, 35, 36, 31, 31, 31, 31, 31, 32, 32, 32, 32, 32, 32, 32, + 32, 32, 32, 32, 32, 32, 32, 32, 32, 33, 34, 34, 35, 36, 36, 36, 36, 36, 36, 37, 31, 31, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 33, 33, + 33, 33, 33, 33, 33, 33, 33, 34, 34, 35, 36, 37, 37, 37, 37, 37, 37, 37, 32, 32, 32, 33, 33, 33, 33, 33, 33, 33, 33, 33, 33, 33, 34, 34, + 34, 34, 34, 34, 34, 34, 35, 36, 37, 37, 37, 37, 37, 37, 37, 38, 33, 33, 33, 33, 33, 34, 34, 34, 34, 34, 34, 34, 34, 34, 34, 34, 34, 34, + 34, 34, 34, 35, 36, 37, 37, 38, 38, 38, 38, 38, 38, 39, 33, 33, 34, 34, 34, 34, 34, 34, 34, 34, 34, 35, 35, 35, 35, 35, 35, 35, 35, 35, + 35, 36, 37, 37, 38, 39, 39, 39, 39, 39, 39, 40, 33, 33, 34, 34, 34, 34, 34, 34, 34, 34, 34, 35, 35, 35, 35, 35, 35, 35, 35, 35, 35, 36, + 37, 37, 38, 39, 39, 39, 39, 39, 39, 40, 33, 33, 34, 34, 34, 34, 34, 34, 34, 34, 34, 35, 35, 35, 35, 35, 35, 35, 35, 35, 35, 36, 37, 37, + 38, 39, 39, 39, 39, 39, 39, 40, 33, 33, 34, 34, 34, 34, 34, 34, 34, 34, 34, 35, 35, 35, 35, 35, 35, 35, 35, 35, 35, 36, 37, 37, 38, 39, + 39, 39, 39, 39, 39, 40, 33, 33, 34, 34, 34, 34, 34, 34, 34, 34, 34, 35, 35, 35, 35, 35, 35, 35, 35, 35, 35, 36, 37, 37, 38, 39, 39, 39, + 39, 39, 39, 40, 33, 33, 34, 34, 34, 34, 34, 34, 34, 34, 34, 35, 35, 35, 35, 35, 35, 35, 35, 35, 35, 36, 37, 37, 38, 39, 39, 39, 39, 39, + 39, 40, 34, 34, 34, 35, 35, 35, 35, 35, 35, 35, 35, 35, 35, 36, 36, 36, 36, 36, 36, 36, 36, 37, 37, 38, 39, 40, 40, 40, 40, 40, 40, 40, + 31, 31, 31, 34, 31, 31, 31, 35, 31, 31, 31, 35, 31, 32, 32, 36, 31, 32, 32, 36, 31, 33, 33, 37, 34, 36, 36, 40, 34, 36, 36, 40, 31, 31, + 31, 31, 31, 31, 34, 34, 31, 31, 31, 32, 32, 33, 36, 36, 31, 31, 31, 32, 32, 33, 36, 36, 34, 35, 35, 36, 36, 37, 40, 40, 32, 31, 31, 31, + 31, 31, 33, 35, 31, 31, 31, 31, 31, 31, 33, 36, 31, 31, 31, 31, 31, 31, 34, 36, 31, 31, 31, 31, 31, 31, 34, 37, 31, 31, 31, 31, 31, 31, + 34, 37, 31, 31, 31, 31, 31, 31, 34, 37, 31, 31, 31, 32, 32, 32, 34, 37, 30, 31, 31, 32, 32, 32, 34, 38, 30, 31, 32, 32, 32, 32, 35, 38, + 30, 31, 32, 32, 32, 32, 35, 38, 30, 31, 32, 32, 32, 32, 35, 38, 31, 32, 33, 33, 33, 33, 36, 39, 33, 34, 34, 35, 35, 35, 37, 40, 33, 34, + 35, 36, 36, 36, 38, 41, 33, 34, 35, 36, 36, 36, 38, 41, 33, 34, 35, 36, 36, 36, 38, 41, 32, 31, 31, 31, 31, 31, 31, 30, 30, 30, 30, 31, + 33, 33, 33, 33, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 32, 34, 34, 34, 34, 31, 31, 31, 31, 31, 31, 31, 31, 32, 32, 32, 33, 34, 35, + 35, 35, 31, 31, 31, 31, 31, 31, 32, 32, 32, 32, 32, 33, 35, 36, 36, 36, 31, 31, 31, 31, 31, 31, 32, 32, 32, 32, 32, 33, 35, 36, 36, 36, + 31, 31, 31, 31, 31, 31, 32, 32, 32, 32, 32, 33, 35, 36, 36, 36, 33, 33, 34, 34, 34, 34, 34, 34, 35, 35, 35, 36, 37, 38, 38, 38, 35, 36, + 36, 37, 37, 37, 37, 38, 38, 38, 38, 39, 40, 41, 41, 41, 32, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 32, 33, 34, 35, 37, 31, 31, 31, 31, + 31, 31, 31, 31, 31, 31, 31, 32, 33, 34, 35, 37, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 32, 33, 34, 36, 37, 31, 31, 31, 31, 31, 31, + 31, 31, 31, 31, 31, 32, 33, 35, 36, 38, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 32, 34, 35, 36, 38, 31, 31, 31, 31, 31, 31, 31, 31, + 31, 31, 31, 33, 34, 35, 37, 38, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 33, 34, 35, 37, 38, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, + 31, 33, 34, 35, 37, 38, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 33, 34, 35, 37, 38, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 33, + 34, 35, 37, 38, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 33, 34, 35, 37, 38, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 33, 34, 35, + 37, 38, 31, 31, 31, 31, 31, 32, 32, 32, 32, 32, 32, 33, 34, 36, 37, 39, 31, 31, 31, 31, 31, 32, 32, 32, 32, 32, 32, 33, 34, 36, 37, 39, + 30, 31, 31, 31, 31, 32, 32, 32, 32, 32, 32, 33, 34, 36, 38, 39, 30, 31, 31, 31, 32, 32, 32, 32, 32, 32, 32, 33, 35, 36, 38, 40, 30, 31, + 31, 31, 32, 32, 32, 32, 32, 32, 32, 33, 35, 36, 38, 40, 30, 31, 31, 31, 32, 32, 32, 32, 32, 32, 32, 33, 35, 36, 38, 40, 30, 31, 31, 31, + 32, 32, 32, 32, 32, 32, 32, 33, 35, 36, 38, 40, 30, 31, 31, 31, 32, 32, 32, 32, 32, 32, 32, 33, 35, 36, 38, 40, 30, 31, 31, 31, 32, 32, + 32, 32, 32, 32, 32, 33, 35, 36, 38, 40, 31, 31, 31, 32, 32, 33, 33, 33, 33, 33, 33, 34, 35, 37, 38, 40, 31, 32, 32, 33, 33, 33, 33, 33, + 33, 33, 33, 35, 36, 37, 39, 41, 32, 32, 33, 33, 34, 34, 34, 34, 34, 34, 34, 35, 37, 38, 40, 41, 33, 33, 34, 34, 34, 35, 35, 35, 35, 35, + 35, 36, 37, 39, 40, 42, 33, 34, 34, 35, 35, 36, 36, 36, 36, 36, 36, 37, 38, 40, 41, 43, 33, 34, 34, 35, 35, 36, 36, 36, 36, 36, 36, 37, + 38, 40, 41, 43, 33, 34, 34, 35, 35, 36, 36, 36, 36, 36, 36, 37, 38, 40, 41, 43, 33, 34, 34, 35, 35, 36, 36, 36, 36, 36, 36, 37, 38, 40, + 41, 43, 33, 34, 34, 35, 35, 36, 36, 36, 36, 36, 36, 37, 38, 40, 41, 43, 33, 34, 34, 35, 35, 36, 36, 36, 36, 36, 36, 37, 38, 40, 41, 43, + 34, 34, 35, 35, 36, 36, 36, 36, 36, 36, 36, 38, 39, 40, 42, 44, 32, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 30, 30, 30, 30, + 30, 30, 30, 31, 31, 32, 33, 33, 33, 33, 33, 33, 33, 34, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, + 31, 31, 32, 32, 33, 34, 34, 34, 34, 34, 34, 34, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, + 32, 33, 34, 34, 34, 34, 34, 34, 34, 35, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 32, 33, 33, + 34, 35, 35, 35, 35, 35, 35, 35, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 32, 32, 32, 32, 32, 32, 32, 33, 34, 34, 35, + 35, 35, 35, 35, 35, 36, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 32, 32, 32, 32, 32, 32, 32, 32, 32, 33, 33, 34, 35, 36, 36, 36, + 36, 36, 36, 36, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 32, 32, 32, 32, 32, 32, 32, 32, 32, 33, 33, 34, 35, 36, 36, 36, 36, 36, + 36, 36, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 32, 32, 32, 32, 32, 32, 32, 32, 32, 33, 33, 34, 35, 36, 36, 36, 36, 36, 36, 36, + 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 32, 32, 32, 32, 32, 32, 32, 32, 32, 33, 33, 34, 35, 36, 36, 36, 36, 36, 36, 36, 31, 31, + 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 32, 32, 32, 32, 32, 32, 32, 32, 32, 33, 33, 34, 35, 36, 36, 36, 36, 36, 36, 36, 31, 31, 31, 31, + 31, 31, 31, 31, 31, 31, 31, 31, 32, 32, 32, 32, 32, 32, 32, 32, 32, 33, 33, 34, 35, 36, 36, 36, 36, 36, 36, 36, 32, 32, 32, 32, 32, 33, + 33, 33, 33, 33, 33, 33, 33, 33, 33, 33, 33, 33, 33, 33, 33, 34, 35, 35, 36, 37, 37, 37, 37, 37, 37, 38, 33, 33, 33, 33, 34, 34, 34, 34, + 34, 34, 34, 34, 34, 34, 34, 35, 35, 35, 35, 35, 35, 35, 36, 37, 37, 38, 38, 38, 38, 38, 38, 39, 34, 34, 34, 35, 35, 35, 35, 35, 35, 35, + 35, 35, 36, 36, 36, 36, 36, 36, 36, 36, 36, 37, 37, 38, 39, 40, 40, 40, 40, 40, 40, 40, 35, 35, 36, 36, 36, 37, 37, 37, 37, 37, 37, 37, + 37, 37, 38, 38, 38, 38, 38, 38, 38, 38, 39, 40, 40, 41, 41, 41, 41, 41, 41, 42, 37, 37, 37, 38, 38, 38, 38, 38, 38, 38, 38, 38, 39, 39, + 39, 40, 40, 40, 40, 40, 40, 40, 41, 41, 42, 43, 43, 43, 43, 43, 43, 44, 31, 31, 31, 34, 31, 31, 31, 34, 31, 31, 31, 35, 31, 31, 31, 35, + 31, 31, 31, 35, 31, 31, 31, 35, 31, 32, 32, 36, 31, 32, 32, 36, 31, 32, 32, 36, 31, 32, 32, 36, 31, 32, 32, 36, 32, 33, 33, 37, 33, 35, + 35, 39, 34, 36, 36, 40, 34, 36, 36, 40, 34, 36, 36, 40, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 32, 33, 34, 34, 34, 31, 31, 31, 31, + 31, 31, 32, 32, 32, 32, 32, 33, 35, 36, 36, 36, 31, 31, 31, 31, 31, 31, 32, 32, 32, 32, 32, 33, 35, 36, 36, 36, 34, 34, 35, 35, 35, 35, + 36, 36, 36, 36, 36, 37, 39, 40, 40, 40, 32, 31, 31, 31, 31, 31, 33, 35, 31, 31, 31, 31, 31, 31, 33, 35, 31, 31, 31, 31, 31, 31, 33, 36, + 31, 31, 31, 31, 31, 31, 33, 36, 31, 31, 31, 31, 31, 31, 34, 36, 31, 31, 31, 31, 31, 31, 34, 37, 31, 31, 31, 31, 31, 31, 34, 37, 31, 31, + 31, 31, 31, 31, 34, 37, 31, 31, 31, 31, 31, 31, 34, 37, 31, 31, 31, 31, 31, 31, 34, 37, 31, 31, 31, 31, 31, 31, 34, 37, 31, 31, 31, 31, + 31, 31, 34, 37, 31, 31, 31, 32, 32, 32, 34, 37, 31, 31, 31, 32, 32, 32, 34, 37, 30, 31, 31, 32, 32, 32, 34, 38, 30, 31, 32, 32, 32, 32, + 35, 38, 30, 31, 32, 32, 32, 32, 35, 38, 30, 31, 32, 32, 32, 32, 35, 38, 30, 31, 32, 32, 32, 32, 35, 38, 30, 31, 32, 32, 32, 32, 35, 38, + 30, 31, 32, 32, 32, 32, 35, 38, 31, 31, 32, 33, 33, 33, 35, 38, 31, 32, 33, 33, 33, 33, 36, 39, 32, 33, 34, 34, 34, 34, 37, 40, 33, 34, + 34, 35, 35, 35, 37, 40, 33, 34, 35, 36, 36, 36, 38, 41, 33, 34, 35, 36, 36, 36, 38, 41, 33, 34, 35, 36, 36, 36, 38, 41, 33, 34, 35, 36, + 36, 36, 38, 41, 33, 34, 35, 36, 36, 36, 38, 41, 33, 34, 35, 36, 36, 36, 38, 41, 34, 35, 36, 36, 36, 36, 39, 42, 32, 31, 31, 31, 31, 31, + 31, 31, 31, 31, 31, 31, 31, 31, 30, 30, 30, 30, 30, 30, 30, 31, 31, 32, 33, 33, 33, 33, 33, 33, 33, 34, 31, 31, 31, 31, 31, 31, 31, 31, + 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 32, 33, 34, 34, 34, 34, 34, 34, 34, 35, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, + 31, 31, 31, 31, 31, 32, 32, 32, 32, 32, 32, 32, 33, 34, 34, 35, 35, 35, 35, 35, 35, 36, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, + 32, 32, 32, 32, 32, 32, 32, 32, 32, 33, 33, 34, 35, 36, 36, 36, 36, 36, 36, 36, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 32, 32, + 32, 32, 32, 32, 32, 32, 32, 33, 33, 34, 35, 36, 36, 36, 36, 36, 36, 36, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 32, 32, 32, 32, + 32, 32, 32, 32, 32, 33, 33, 34, 35, 36, 36, 36, 36, 36, 36, 36, 33, 33, 33, 33, 34, 34, 34, 34, 34, 34, 34, 34, 34, 34, 34, 35, 35, 35, + 35, 35, 35, 35, 36, 37, 37, 38, 38, 38, 38, 38, 38, 39, 35, 35, 36, 36, 36, 37, 37, 37, 37, 37, 37, 37, 37, 37, 38, 38, 38, 38, 38, 38, + 38, 38, 39, 40, 40, 41, 41, 41, 41, 41, 41, 42, + ], + ], + [ + [ + 31, 31, 31, 31, 31, 32, 32, 32, 31, 32, 32, 32, 31, 32, 32, 32, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, + 31, 32, 32, 32, 32, 32, 31, 31, 32, 32, 32, 32, 32, 32, 31, 31, 32, 32, 32, 32, 32, 32, 31, 31, 32, 32, 32, 32, 32, 32, 31, 31, 32, 32, + 32, 32, 32, 32, 31, 31, 32, 32, 32, 32, 32, 32, 32, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, + 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, + 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 32, 32, 32, 32, 32, 31, 31, 31, 31, 31, 32, 32, 32, 32, 32, + 32, 32, 32, 32, 32, 32, 31, 31, 31, 31, 31, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 31, 31, 31, 31, 31, 32, 32, 32, 32, 32, 32, 32, + 32, 32, 32, 32, 31, 31, 31, 31, 31, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 31, 31, 31, 31, 31, 32, 32, 32, 32, 32, 32, 32, 32, 32, + 32, 32, 31, 31, 31, 31, 31, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 31, 31, 31, 31, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, + 31, 31, 31, 31, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 31, 31, 31, 31, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 31, 31, + 31, 31, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 31, 31, 31, 31, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 31, 31, 31, + 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, + 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, + 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, + 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, + 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, + 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, + 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, + 31, 31, 31, 31, 31, 31, 31, 32, 32, 32, 32, 32, 32, 32, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, + 31, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, + 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 31, 31, 31, 31, 31, 31, 31, 31, 31, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, + 32, 32, 32, 32, 32, 32, 32, 32, 31, 31, 31, 31, 31, 31, 31, 31, 31, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, + 32, 32, 32, 32, 32, 32, 31, 31, 31, 31, 31, 31, 31, 31, 31, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, + 32, 32, 32, 32, 31, 31, 31, 31, 31, 31, 31, 31, 31, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, + 32, 32, 31, 31, 31, 31, 31, 31, 31, 31, 31, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, + 31, 31, 31, 31, 31, 31, 31, 31, 31, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 31, 31, + 31, 31, 31, 31, 31, 31, 31, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 31, 31, 31, 31, + 31, 31, 31, 31, 31, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 31, 31, 31, 31, 31, 31, + 31, 31, 31, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 31, 31, 31, 31, 31, 31, 31, 31, + 31, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 31, 31, 31, 31, 31, 31, 31, 31, 31, 32, + 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 31, 31, 31, 31, 31, 31, 31, 31, 32, 32, 32, 32, + 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 31, 31, 31, 31, 31, 31, 31, 31, 32, 32, 32, 32, 32, 32, + 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 31, 31, 31, 31, 31, 31, 31, 31, 32, 32, 32, 32, 32, 32, 32, 32, + 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 31, 31, 31, 31, 31, 31, 31, 31, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, + 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 31, 31, 31, 31, 31, 31, 31, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, + 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 31, 31, 31, 31, 31, 31, 31, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, + 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 31, 31, 31, 31, 31, 31, 31, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, + 32, 32, 32, 32, 32, 32, 32, 32, 31, 31, 31, 31, 31, 31, 31, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, + 32, 32, 32, 32, 32, 32, 31, 31, 31, 31, 31, 31, 31, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, + 32, 32, 32, 32, 31, 31, 31, 31, 31, 31, 31, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, + 32, 32, 31, 31, 31, 31, 31, 31, 31, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, + 31, 31, 31, 31, 31, 31, 31, 31, 31, 32, 32, 32, 31, 32, 32, 32, 31, 32, 32, 32, 31, 32, 32, 32, 31, 32, 32, 32, 31, 32, 32, 32, 31, 31, + 31, 31, 31, 31, 31, 31, 31, 31, 32, 32, 32, 32, 32, 32, 31, 31, 32, 32, 32, 32, 32, 32, 31, 31, 32, 32, 32, 32, 32, 32, 32, 31, 31, 31, + 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 32, 32, 32, 32, 31, 31, 31, 32, 32, 32, + 32, 32, 31, 31, 32, 32, 32, 32, 32, 32, 31, 31, 32, 32, 32, 32, 32, 32, 31, 31, 32, 32, 32, 32, 32, 32, 31, 31, 32, 32, 32, 32, 32, 32, + 31, 31, 32, 32, 32, 32, 32, 32, 31, 31, 32, 32, 32, 32, 32, 32, 31, 31, 32, 32, 32, 32, 32, 32, 31, 31, 32, 32, 32, 32, 32, 32, 31, 31, + 32, 32, 32, 32, 32, 32, 31, 31, 32, 32, 32, 32, 32, 32, 31, 31, 32, 32, 32, 32, 32, 32, 32, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, + 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 32, 32, 32, 32, 32, 32, 32, 32, 32, + 32, 32, 31, 31, 31, 31, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 31, 31, 31, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, + 31, 31, 31, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 31, 31, 31, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 31, 31, + 31, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, + 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, + 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, + 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 32, 32, 32, 32, 32, 32, 32, 32, 31, 31, 31, 31, 31, 31, 32, 32, 32, 32, + 32, 32, 32, 32, 32, 32, 31, 31, 31, 31, 31, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 31, 31, 31, 31, 32, 32, 32, 32, 32, 32, 32, 32, + 32, 32, 32, 32, 31, 31, 31, 31, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 31, 31, 31, 31, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, + 32, 32, 31, 31, 31, 31, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 31, 31, 31, 31, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, + 31, 31, 31, 31, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 31, 31, 31, 31, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 31, 31, + 31, 31, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 31, 31, 31, 31, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 31, 31, 31, 31, + 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 31, 31, 31, 31, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 31, 31, 31, 31, 32, 32, + 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 31, 31, 31, 31, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 31, 31, 31, 31, 32, 32, 32, 32, + 32, 32, 32, 32, 32, 32, 32, 32, 31, 31, 31, 31, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 31, 31, 31, 31, 32, 32, 32, 32, 32, 32, + 32, 32, 32, 32, 32, 32, 31, 31, 31, 31, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 31, 31, 31, 32, 32, 32, 32, 32, 32, 32, 32, 32, + 32, 32, 32, 32, 31, 31, 31, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 31, 31, 31, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, + 32, 32, 31, 31, 31, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 31, 31, 31, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, + 31, 31, 31, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, + 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, + 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, + 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, + 31, 31, 32, 32, 32, 32, 32, 32, 31, 31, 31, 31, 31, 31, 31, 31, 31, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, + 32, 32, 32, 32, 32, 32, 31, 31, 31, 31, 31, 31, 31, 31, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, + 32, 32, 32, 32, 31, 31, 31, 31, 31, 31, 31, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, + 32, 32, 31, 31, 31, 31, 31, 31, 31, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, + 31, 31, 31, 31, 31, 31, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 31, 31, + 31, 31, 31, 31, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 31, 31, 31, 31, + 31, 31, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 31, 31, 31, 31, 31, 31, + 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 31, 31, 31, 31, 31, 31, 32, 32, + 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 31, 31, 31, 31, 31, 31, 32, 32, 32, 32, + 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 31, 31, 31, 31, 31, 31, 32, 32, 32, 32, 32, 32, + 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 31, 31, 31, 31, 31, 31, 32, 32, 32, 32, 32, 32, 32, 32, + 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 32, 32, + 31, 32, 32, 32, 31, 32, 32, 32, 31, 32, 32, 32, 31, 32, 32, 32, 31, 32, 32, 32, 31, 32, 32, 32, 31, 32, 32, 32, 31, 32, 32, 32, 31, 32, + 32, 32, 31, 32, 32, 32, 31, 32, 32, 32, 31, 32, 32, 32, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, + 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 31, 31, 31, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 31, 31, 31, 32, 32, 32, + 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, + 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 32, 32, 32, 32, 31, 31, + 31, 32, 32, 32, 32, 32, 31, 31, 31, 32, 32, 32, 32, 32, 31, 31, 32, 32, 32, 32, 32, 32, 31, 31, 32, 32, 32, 32, 32, 32, 31, 31, 32, 32, + 32, 32, 32, 32, 31, 31, 32, 32, 32, 32, 32, 32, 31, 31, 32, 32, 32, 32, 32, 32, 31, 31, 32, 32, 32, 32, 32, 32, 31, 31, 32, 32, 32, 32, + 32, 32, 31, 31, 32, 32, 32, 32, 32, 32, 31, 31, 32, 32, 32, 32, 32, 32, 31, 31, 32, 32, 32, 32, 32, 32, 31, 31, 32, 32, 32, 32, 32, 32, + 31, 31, 32, 32, 32, 32, 32, 32, 31, 31, 32, 32, 32, 32, 32, 32, 31, 31, 32, 32, 32, 32, 32, 32, 31, 31, 32, 32, 32, 32, 32, 32, 31, 31, + 32, 32, 32, 32, 32, 32, 31, 31, 32, 32, 32, 32, 32, 32, 31, 31, 32, 32, 32, 32, 32, 32, 31, 31, 32, 32, 32, 32, 32, 32, 31, 31, 32, 32, + 32, 32, 32, 32, 31, 31, 32, 32, 32, 32, 32, 32, 31, 31, 32, 32, 32, 32, 32, 32, 31, 31, 32, 32, 32, 32, 32, 32, 32, 31, 31, 31, 31, 31, + 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, + 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 32, + 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 31, 31, 31, 31, 31, 31, 31, 32, 32, 32, 32, 32, + 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 31, 31, 31, 31, 31, 31, 32, 32, 32, 32, 32, 32, 32, 32, + 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 31, 31, 31, 31, 31, 31, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, + 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 31, 31, 31, 31, 31, 31, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, + 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 31, 31, 31, 31, 31, 31, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, + 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, + ], + [ + 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 30, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, + 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, + 31, 31, 31, 31, 30, 31, 31, 31, 31, 31, 31, 31, 32, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 30, 30, 30, 31, 31, 31, 31, 31, 31, + 31, 31, 31, 31, 31, 31, 31, 31, 30, 30, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 30, 31, 31, 31, 31, 31, 31, 31, 31, + 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, + 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, + 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, + 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, + 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 30, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 30, 30, + 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 30, 30, 30, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 32, 32, 31, 31, 31, + 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 30, 30, 30, 30, 30, 30, 31, 31, 31, 31, 31, 31, + 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 30, 30, 30, 30, 30, 30, 31, 31, 31, 31, 31, 31, 31, 31, + 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 30, 30, 30, 30, 30, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, + 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 30, 30, 30, 30, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, + 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 30, 30, 30, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, + 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, + 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, + 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, + 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, + 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, + 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, + 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, + 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, + 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, + 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, + 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, + 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, + 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, + 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, + 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, + 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, + 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, + 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, + 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, + 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 30, 30, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, + 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 30, 30, 30, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, + 31, 31, 31, 31, 31, 31, 31, 31, 30, 30, 30, 30, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, + 31, 31, 31, 31, 31, 31, 30, 30, 30, 30, 30, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, + 31, 31, 31, 31, 30, 30, 30, 30, 30, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, + 32, 32, 30, 30, 30, 30, 30, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 32, 32, + 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 32, 32, 30, 31, 32, 32, 31, 31, + 31, 31, 31, 31, 31, 30, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 32, 32, 31, 31, 31, 31, 31, 31, 32, 32, 32, 31, 31, 31, + 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, + 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, + 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 32, 32, 32, 31, 31, + 31, 31, 31, 32, 32, 32, 30, 31, 31, 31, 31, 32, 32, 32, 30, 31, 31, 31, 32, 32, 32, 32, 32, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, + 31, 31, 30, 30, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, + 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 32, + 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 32, 32, 32, 32, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 32, 32, 32, 32, 31, 31, + 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 32, 32, 32, 32, 32, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, + 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, + 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, + 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, + 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, + 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, + 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, + 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, + 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, + 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, + 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, + 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 32, 32, 32, 32, 32, 32, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, + 32, 32, 32, 32, 32, 32, 31, 31, 31, 31, 31, 31, 31, 31, 31, 32, 32, 32, 32, 32, 32, 32, 31, 31, 31, 31, 31, 31, 31, 31, 31, 32, 32, 32, + 32, 32, 32, 32, 30, 31, 31, 31, 31, 31, 31, 31, 31, 32, 32, 32, 32, 32, 32, 32, 30, 31, 31, 31, 31, 31, 31, 31, 31, 32, 32, 32, 32, 32, + 32, 32, 30, 30, 31, 31, 31, 31, 31, 31, 32, 32, 32, 32, 32, 32, 32, 32, 30, 30, 31, 31, 31, 31, 31, 31, 32, 32, 32, 32, 32, 32, 32, 32, + 30, 30, 31, 31, 31, 31, 31, 31, 32, 32, 32, 32, 32, 32, 32, 32, 32, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, + 31, 31, 31, 31, 31, 31, 31, 31, 31, 30, 30, 30, 30, 30, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, + 31, 31, 31, 31, 31, 31, 31, 31, 31, 30, 30, 30, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, + 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, + 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, + 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, + 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, + 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, + 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 32, 32, 32, 31, 31, + 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 32, 32, 32, 32, 32, 32, 32, 31, 31, 31, 31, + 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 32, 32, 32, 32, 32, 32, 32, 32, 32, 31, 31, 31, 31, 31, 31, + 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 32, 32, 32, 32, 32, 32, 32, 32, 32, 31, 31, 31, 31, 31, 31, 31, 31, + 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 32, 32, 32, 32, 32, 32, 32, 32, 32, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, + 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 32, 32, 32, 32, 32, 32, 32, 32, 32, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, + 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 32, 32, 32, 32, 32, 32, 32, 32, 32, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, + 31, 31, 31, 31, 31, 31, 31, 31, 31, 32, 32, 32, 32, 32, 32, 32, 32, 32, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, + 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, + 31, 32, 31, 31, 32, 32, 31, 31, 32, 32, 30, 31, 32, 32, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 30, 31, 31, 31, 31, + 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 32, 32, 32, 31, 31, 31, 31, 31, 31, + 31, 31, 31, 31, 31, 31, 32, 32, 32, 32, 32, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, + 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, + 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, + 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, + 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, + 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 32, 32, 32, 31, 31, + 31, 31, 31, 32, 32, 32, 31, 31, 31, 31, 31, 32, 32, 32, 31, 31, 31, 31, 31, 32, 32, 32, 30, 31, 31, 31, 31, 32, 32, 32, 30, 31, 31, 31, + 31, 32, 32, 32, 30, 31, 31, 31, 32, 32, 32, 32, 30, 31, 31, 31, 32, 32, 32, 32, 30, 31, 31, 31, 32, 32, 32, 32, 32, 31, 31, 31, 31, 31, + 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 30, 30, 30, 30, 30, 31, 31, 31, 31, 31, 31, 31, 31, + 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, + 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, + 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, + 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 32, 32, 32, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, + 31, 31, 31, 31, 31, 31, 31, 32, 32, 32, 32, 32, 32, 32, 32, 32, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, + 31, 31, 31, 31, 31, 32, 32, 32, 32, 32, 32, 32, 32, 32, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, + 31, 31, 31, 32, 32, 32, 32, 32, 32, 32, 32, 32, + ], + ], +]; + +pub static REF_FRAME_LIST: [u8; 5] = [2, 3, 5, 6, 7]; + +pub static REMAP_LR_TYPE: [u8; 4] = [0, 3, 1, 2]; + +pub static SEGMENTATION_FEATURE_BITS: [u8; 8] = [8, 6, 6, 6, 6, 3, 0, 0]; + +pub static SEGMENTATION_FEATURE_MAX: [u8; 8] = [255, 63, 63, 63, 63, 7, 0, 0]; + +pub static SEGMENTATION_FEATURE_SIGNED: [u8; 8] = [1, 1, 1, 1, 1, 0, 0, 0]; + +pub static SGR_PARAMS: [[u8; 4]; 16] = [ + [2, 12, 1, 4], + [2, 15, 1, 6], + [2, 18, 1, 8], + [2, 21, 1, 9], + [2, 24, 1, 10], + [2, 29, 1, 11], + [2, 36, 1, 12], + [2, 45, 1, 13], + [2, 56, 1, 14], + [2, 68, 1, 15], + [0, 0, 1, 5], + [0, 0, 1, 8], + [0, 0, 1, 11], + [0, 0, 1, 14], + [2, 30, 0, 0], + [2, 75, 0, 0], +]; + +pub static SGRPROJ_XQD_MAX: [u8; 2] = [31, 95]; + +pub static SGRPROJ_XQD_MID: [i8; 2] = [-32, 31]; + +pub static SGRPROJ_XQD_MIN: [i8; 2] = [-96, -32]; + +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_64X64: [u8; 64] = [ + 255, 248, 240, 233, 225, 218, 210, 203, 196, 189, 182, 176, 169, 163, 156, 150, 144, 138, 133, 127, 121, 116, 111, 106, 101, 96, 91, 86, 82, 77, + 73, 69, 65, 61, 57, 54, 50, 47, 44, 41, 38, 35, 32, 29, 27, 25, 22, 20, 18, 16, 15, 13, 12, 10, 9, 8, 7, 6, 6, 5, 5, 4, 4, 4, +]; + +pub static SM_WEIGHTS_TX_8X8: [u8; 8] = [255, 197, 146, 105, 73, 50, 37, 32]; + +pub static SPLIT_TX_SIZE: [u8; 19] = [0, 0, 1, 2, 3, 0, 0, 1, 1, 2, 2, 3, 3, 5, 6, 7, 8, 9, 10]; + +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_SET1: [u8; 16] = [9, 10, 11, 12, 13, 14, 15, 0, 1, 2, 4, 5, 3, 6, 7, 8]; + +pub static TX_TYPE_INTER_INV_SET2: [u8; 12] = [9, 10, 11, 0, 1, 2, 4, 5, 3, 6, 7, 8]; + +pub static TX_TYPE_INTER_INV_SET3: [u8; 2] = [9, 0]; + +pub static TX_TYPE_INTRA_INV_SET1: [u8; 7] = [9, 0, 10, 11, 3, 1, 2]; + +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]; + +pub static UPSCALE_FILTER: [[i16; 8]; 64] = [ + [0, 0, 0, 128, 0, 0, 0, 0], + [0, 0, -1, 128, 2, -1, 0, 0], + [0, 1, -3, 127, 4, -2, 1, 0], + [0, 1, -4, 127, 6, -3, 1, 0], + [0, 2, -6, 126, 8, -3, 1, 0], + [0, 2, -7, 125, 11, -4, 1, 0], + [-1, 2, -8, 125, 13, -5, 2, 0], + [-1, 3, -9, 124, 15, -6, 2, 0], + [-1, 3, -10, 123, 18, -6, 2, -1], + [-1, 3, -11, 122, 20, -7, 3, -1], + [-1, 4, -12, 121, 22, -8, 3, -1], + [-1, 4, -13, 120, 25, -9, 3, -1], + [-1, 4, -14, 118, 28, -9, 3, -1], + [-1, 4, -15, 117, 30, -10, 4, -1], + [-1, 5, -16, 116, 32, -11, 4, -1], + [-1, 5, -16, 114, 35, -12, 4, -1], + [-1, 5, -17, 112, 38, -12, 4, -1], + [-1, 5, -18, 111, 40, -13, 5, -1], + [-1, 5, -18, 109, 43, -14, 5, -1], + [-1, 6, -19, 107, 45, -14, 5, -1], + [-1, 6, -19, 105, 48, -15, 5, -1], + [-1, 6, -19, 103, 51, -16, 5, -1], + [-1, 6, -20, 101, 53, -16, 6, -1], + [-1, 6, -20, 99, 56, -17, 6, -1], + [-1, 6, -20, 97, 58, -17, 6, -1], + [-1, 6, -20, 95, 61, -18, 6, -1], + [-2, 7, -20, 93, 64, -18, 6, -2], + [-2, 7, -20, 91, 66, -19, 6, -1], + [-2, 7, -20, 88, 69, -19, 6, -1], + [-2, 7, -20, 86, 71, -19, 6, -1], + [-2, 7, -20, 84, 74, -20, 7, -2], + [-2, 7, -20, 81, 76, -20, 7, -1], + [-2, 7, -20, 79, 79, -20, 7, -2], + [-1, 7, -20, 76, 81, -20, 7, -2], + [-2, 7, -20, 74, 84, -20, 7, -2], + [-1, 6, -19, 71, 86, -20, 7, -2], + [-1, 6, -19, 69, 88, -20, 7, -2], + [-1, 6, -19, 66, 91, -20, 7, -2], + [-2, 6, -18, 64, 93, -20, 7, -2], + [-1, 6, -18, 61, 95, -20, 6, -1], + [-1, 6, -17, 58, 97, -20, 6, -1], + [-1, 6, -17, 56, 99, -20, 6, -1], + [-1, 6, -16, 53, 101, -20, 6, -1], + [-1, 5, -16, 51, 103, -19, 6, -1], + [-1, 5, -15, 48, 105, -19, 6, -1], + [-1, 5, -14, 45, 107, -19, 6, -1], + [-1, 5, -14, 43, 109, -18, 5, -1], + [-1, 5, -13, 40, 111, -18, 5, -1], + [-1, 4, -12, 38, 112, -17, 5, -1], + [-1, 4, -12, 35, 114, -16, 5, -1], + [-1, 4, -11, 32, 116, -16, 5, -1], + [-1, 4, -10, 30, 117, -15, 4, -1], + [-1, 3, -9, 28, 118, -14, 4, -1], + [-1, 3, -9, 25, 120, -13, 4, -1], + [-1, 3, -8, 22, 121, -12, 4, -1], + [-1, 3, -7, 20, 122, -11, 3, -1], + [-1, 2, -6, 18, 123, -10, 3, -1], + [0, 2, -6, 15, 124, -9, 3, -1], + [0, 2, -5, 13, 125, -8, 2, -1], + [0, 1, -4, 11, 125, -7, 2, 0], + [0, 1, -3, 8, 126, -6, 2, 0], + [0, 1, -3, 6, 127, -4, 1, 0], + [0, 1, -2, 4, 127, -3, 1, 0], + [0, 0, -1, 2, 128, -1, 0, 0], +]; + +pub static WARPED_FILTERS: [[i8; 8]; 193] = [ + [0, 0, 127, 1, 0, 0, 0, 0], + [0, -1, 127, 2, 0, 0, 0, 0], + [1, -3, 127, 4, -1, 0, 0, 0], + [1, -4, 126, 6, -2, 1, 0, 0], + [1, -5, 126, 8, -3, 1, 0, 0], + [1, -6, 125, 11, -4, 1, 0, 0], + [1, -7, 124, 13, -4, 1, 0, 0], + [2, -8, 123, 15, -5, 1, 0, 0], + [2, -9, 122, 18, -6, 1, 0, 0], + [2, -10, 121, 20, -6, 1, 0, 0], + [2, -11, 120, 22, -7, 2, 0, 0], + [2, -12, 119, 25, -8, 2, 0, 0], + [3, -13, 117, 27, -8, 2, 0, 0], + [3, -13, 116, 29, -9, 2, 0, 0], + [3, -14, 114, 32, -10, 3, 0, 0], + [3, -15, 113, 35, -10, 2, 0, 0], + [3, -15, 111, 37, -11, 3, 0, 0], + [3, -16, 109, 40, -11, 3, 0, 0], + [3, -16, 108, 42, -12, 3, 0, 0], + [4, -17, 106, 45, -13, 3, 0, 0], + [4, -17, 104, 47, -13, 3, 0, 0], + [4, -17, 102, 50, -14, 3, 0, 0], + [4, -17, 100, 52, -14, 3, 0, 0], + [4, -18, 98, 55, -15, 4, 0, 0], + [4, -18, 96, 58, -15, 3, 0, 0], + [4, -18, 94, 60, -16, 4, 0, 0], + [4, -18, 91, 63, -16, 4, 0, 0], + [4, -18, 89, 65, -16, 4, 0, 0], + [4, -18, 87, 68, -17, 4, 0, 0], + [4, -18, 85, 70, -17, 4, 0, 0], + [4, -18, 82, 73, -17, 4, 0, 0], + [4, -18, 80, 75, -17, 4, 0, 0], + [4, -18, 78, 78, -18, 4, 0, 0], + [4, -17, 75, 80, -18, 4, 0, 0], + [4, -17, 73, 82, -18, 4, 0, 0], + [4, -17, 70, 85, -18, 4, 0, 0], + [4, -17, 68, 87, -18, 4, 0, 0], + [4, -16, 65, 89, -18, 4, 0, 0], + [4, -16, 63, 91, -18, 4, 0, 0], + [4, -16, 60, 94, -18, 4, 0, 0], + [3, -15, 58, 96, -18, 4, 0, 0], + [4, -15, 55, 98, -18, 4, 0, 0], + [3, -14, 52, 100, -17, 4, 0, 0], + [3, -14, 50, 102, -17, 4, 0, 0], + [3, -13, 47, 104, -17, 4, 0, 0], + [3, -13, 45, 106, -17, 4, 0, 0], + [3, -12, 42, 108, -16, 3, 0, 0], + [3, -11, 40, 109, -16, 3, 0, 0], + [3, -11, 37, 111, -15, 3, 0, 0], + [2, -10, 35, 113, -15, 3, 0, 0], + [3, -10, 32, 114, -14, 3, 0, 0], + [2, -9, 29, 116, -13, 3, 0, 0], + [2, -8, 27, 117, -13, 3, 0, 0], + [2, -8, 25, 119, -12, 2, 0, 0], + [2, -7, 22, 120, -11, 2, 0, 0], + [1, -6, 20, 121, -10, 2, 0, 0], + [1, -6, 18, 122, -9, 2, 0, 0], + [1, -5, 15, 123, -8, 2, 0, 0], + [1, -4, 13, 124, -7, 1, 0, 0], + [1, -4, 11, 125, -6, 1, 0, 0], + [1, -3, 8, 126, -5, 1, 0, 0], + [1, -2, 6, 126, -4, 1, 0, 0], + [0, -1, 4, 127, -3, 1, 0, 0], + [0, 0, 2, 127, -1, 0, 0, 0], + [0, 0, 0, 127, 1, 0, 0, 0], + [0, 0, -1, 127, 2, 0, 0, 0], + [0, 1, -3, 127, 4, -2, 1, 0], + [0, 1, -5, 127, 6, -2, 1, 0], + [0, 2, -6, 126, 8, -3, 1, 0], + [-1, 2, -7, 126, 11, -4, 2, -1], + [-1, 3, -8, 125, 13, -5, 2, -1], + [-1, 3, -10, 124, 16, -6, 3, -1], + [-1, 4, -11, 123, 18, -7, 3, -1], + [-1, 4, -12, 122, 20, -7, 3, -1], + [-1, 4, -13, 121, 23, -8, 3, -1], + [-2, 5, -14, 120, 25, -9, 4, -1], + [-1, 5, -15, 119, 27, -10, 4, -1], + [-1, 5, -16, 118, 30, -11, 4, -1], + [-2, 6, -17, 116, 33, -12, 5, -1], + [-2, 6, -17, 114, 35, -12, 5, -1], + [-2, 6, -18, 113, 38, -13, 5, -1], + [-2, 7, -19, 111, 41, -14, 6, -2], + [-2, 7, -19, 110, 43, -15, 6, -2], + [-2, 7, -20, 108, 46, -15, 6, -2], + [-2, 7, -20, 106, 49, -16, 6, -2], + [-2, 7, -21, 104, 51, -16, 7, -2], + [-2, 7, -21, 102, 54, -17, 7, -2], + [-2, 8, -21, 100, 56, -18, 7, -2], + [-2, 8, -22, 98, 59, -18, 7, -2], + [-2, 8, -22, 96, 62, -19, 7, -2], + [-2, 8, -22, 94, 64, -19, 7, -2], + [-2, 8, -22, 91, 67, -20, 8, -2], + [-2, 8, -22, 89, 69, -20, 8, -2], + [-2, 8, -22, 87, 72, -21, 8, -2], + [-2, 8, -21, 84, 74, -21, 8, -2], + [-2, 8, -22, 82, 77, -21, 8, -2], + [-2, 8, -21, 79, 79, -21, 8, -2], + [-2, 8, -21, 77, 82, -22, 8, -2], + [-2, 8, -21, 74, 84, -21, 8, -2], + [-2, 8, -21, 72, 87, -22, 8, -2], + [-2, 8, -20, 69, 89, -22, 8, -2], + [-2, 8, -20, 67, 91, -22, 8, -2], + [-2, 7, -19, 64, 94, -22, 8, -2], + [-2, 7, -19, 62, 96, -22, 8, -2], + [-2, 7, -18, 59, 98, -22, 8, -2], + [-2, 7, -18, 56, 100, -21, 8, -2], + [-2, 7, -17, 54, 102, -21, 7, -2], + [-2, 7, -16, 51, 104, -21, 7, -2], + [-2, 6, -16, 49, 106, -20, 7, -2], + [-2, 6, -15, 46, 108, -20, 7, -2], + [-2, 6, -15, 43, 110, -19, 7, -2], + [-2, 6, -14, 41, 111, -19, 7, -2], + [-1, 5, -13, 38, 113, -18, 6, -2], + [-1, 5, -12, 35, 114, -17, 6, -2], + [-1, 5, -12, 33, 116, -17, 6, -2], + [-1, 4, -11, 30, 118, -16, 5, -1], + [-1, 4, -10, 27, 119, -15, 5, -1], + [-1, 4, -9, 25, 120, -14, 5, -2], + [-1, 3, -8, 23, 121, -13, 4, -1], + [-1, 3, -7, 20, 122, -12, 4, -1], + [-1, 3, -7, 18, 123, -11, 4, -1], + [-1, 3, -6, 16, 124, -10, 3, -1], + [-1, 2, -5, 13, 125, -8, 3, -1], + [-1, 2, -4, 11, 126, -7, 2, -1], + [0, 1, -3, 8, 126, -6, 2, 0], + [0, 1, -2, 6, 127, -5, 1, 0], + [0, 1, -2, 4, 127, -3, 1, 0], + [0, 0, 0, 2, 127, -1, 0, 0], + [0, 0, 0, 1, 127, 0, 0, 0], + [0, 0, 0, -1, 127, 2, 0, 0], + [0, 0, 1, -3, 127, 4, -1, 0], + [0, 0, 1, -4, 126, 6, -2, 1], + [0, 0, 1, -5, 126, 8, -3, 1], + [0, 0, 1, -6, 125, 11, -4, 1], + [0, 0, 1, -7, 124, 13, -4, 1], + [0, 0, 2, -8, 123, 15, -5, 1], + [0, 0, 2, -9, 122, 18, -6, 1], + [0, 0, 2, -10, 121, 20, -6, 1], + [0, 0, 2, -11, 120, 22, -7, 2], + [0, 0, 2, -12, 119, 25, -8, 2], + [0, 0, 3, -13, 117, 27, -8, 2], + [0, 0, 3, -13, 116, 29, -9, 2], + [0, 0, 3, -14, 114, 32, -10, 3], + [0, 0, 3, -15, 113, 35, -10, 2], + [0, 0, 3, -15, 111, 37, -11, 3], + [0, 0, 3, -16, 109, 40, -11, 3], + [0, 0, 3, -16, 108, 42, -12, 3], + [0, 0, 4, -17, 106, 45, -13, 3], + [0, 0, 4, -17, 104, 47, -13, 3], + [0, 0, 4, -17, 102, 50, -14, 3], + [0, 0, 4, -17, 100, 52, -14, 3], + [0, 0, 4, -18, 98, 55, -15, 4], + [0, 0, 4, -18, 96, 58, -15, 3], + [0, 0, 4, -18, 94, 60, -16, 4], + [0, 0, 4, -18, 91, 63, -16, 4], + [0, 0, 4, -18, 89, 65, -16, 4], + [0, 0, 4, -18, 87, 68, -17, 4], + [0, 0, 4, -18, 85, 70, -17, 4], + [0, 0, 4, -18, 82, 73, -17, 4], + [0, 0, 4, -18, 80, 75, -17, 4], + [0, 0, 4, -18, 78, 78, -18, 4], + [0, 0, 4, -17, 75, 80, -18, 4], + [0, 0, 4, -17, 73, 82, -18, 4], + [0, 0, 4, -17, 70, 85, -18, 4], + [0, 0, 4, -17, 68, 87, -18, 4], + [0, 0, 4, -16, 65, 89, -18, 4], + [0, 0, 4, -16, 63, 91, -18, 4], + [0, 0, 4, -16, 60, 94, -18, 4], + [0, 0, 3, -15, 58, 96, -18, 4], + [0, 0, 4, -15, 55, 98, -18, 4], + [0, 0, 3, -14, 52, 100, -17, 4], + [0, 0, 3, -14, 50, 102, -17, 4], + [0, 0, 3, -13, 47, 104, -17, 4], + [0, 0, 3, -13, 45, 106, -17, 4], + [0, 0, 3, -12, 42, 108, -16, 3], + [0, 0, 3, -11, 40, 109, -16, 3], + [0, 0, 3, -11, 37, 111, -15, 3], + [0, 0, 2, -10, 35, 113, -15, 3], + [0, 0, 3, -10, 32, 114, -14, 3], + [0, 0, 2, -9, 29, 116, -13, 3], + [0, 0, 2, -8, 27, 117, -13, 3], + [0, 0, 2, -8, 25, 119, -12, 2], + [0, 0, 2, -7, 22, 120, -11, 2], + [0, 0, 1, -6, 20, 121, -10, 2], + [0, 0, 1, -6, 18, 122, -9, 2], + [0, 0, 1, -5, 15, 123, -8, 2], + [0, 0, 1, -4, 13, 124, -7, 1], + [0, 0, 1, -4, 11, 125, -6, 1], + [0, 0, 1, -3, 8, 126, -5, 1], + [0, 0, 1, -2, 6, 126, -4, 1], + [0, 0, 0, -1, 4, 127, -3, 1], + [0, 0, 0, 0, 2, 127, -1, 0], + [0, 0, 0, 0, 2, 127, -1, 0], +]; + +pub static WEDGE_BITS: [u8; 22] = [0, 0, 0, 4, 4, 4, 4, 4, 4, 4, 0, 0, 0, 0, 0, 0, 0, 0, 4, 4, 0, 0]; + +pub static WEDGE_CODEBOOK: [[[u8; 3]; 16]; 3] = [ + [ + [2, 4, 4], + [3, 4, 4], + [4, 4, 4], + [5, 4, 4], + [0, 4, 2], + [0, 4, 4], + [0, 4, 6], + [1, 4, 4], + [2, 4, 2], + [2, 4, 6], + [5, 4, 2], + [5, 4, 6], + [3, 2, 4], + [3, 6, 4], + [4, 2, 4], + [4, 6, 4], + ], + [ + [2, 4, 4], + [3, 4, 4], + [4, 4, 4], + [5, 4, 4], + [1, 2, 4], + [1, 4, 4], + [1, 6, 4], + [0, 4, 4], + [2, 4, 2], + [2, 4, 6], + [5, 4, 2], + [5, 4, 6], + [3, 2, 4], + [3, 6, 4], + [4, 2, 4], + [4, 6, 4], + ], + [ + [2, 4, 4], + [3, 4, 4], + [4, 4, 4], + [5, 4, 4], + [0, 4, 2], + [0, 4, 6], + [1, 2, 4], + [1, 6, 4], + [2, 4, 2], + [2, 4, 6], + [5, 4, 2], + [5, 4, 6], + [3, 2, 4], + [3, 6, 4], + [4, 2, 4], + [4, 6, 4], + ], +]; + +pub static WEDGE_MASTER_OBLIQUE_EVEN: [u8; 64] = [ + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 4, 11, 27, 46, 58, 62, 63, 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, +]; + +pub static WEDGE_MASTER_OBLIQUE_ODD: [u8; 64] = [ + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 2, 6, 18, 37, 53, 60, 63, 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, +]; + +pub static WEDGE_MASTER_VERTICAL: [u8; 64] = [ + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 2, 7, 21, 43, 57, 62, 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, +]; + +pub static WIENER_TAPS_K: [u8; 3] = [1, 2, 3]; + +pub static WIENER_TAPS_MAX: [u8; 3] = [10, 8, 46]; + +pub static WIENER_TAPS_MID: [i8; 3] = [3, -7, 15]; + +pub static WIENER_TAPS_MIN: [i8; 3] = [-5, -23, -17]; diff --git a/crates/av1/src/state.rs b/crates/av1/src/state.rs new file mode 100644 index 0000000..df600c6 --- /dev/null +++ b/crates/av1/src/state.rs @@ -0,0 +1,294 @@ +//! Per-frame decoding state shared by the tiles and the post-filters. +//! +//! [`FrameShared`] holds what tiles only read (headers, references, projected motion field); +//! [`TileState`] what a tile writes (reconstruction, mode info, per-block filter parameters, +//! entropy contexts). With one tile the frame's own buffers move into the tile state and back; +//! with several, each tile decodes into private buffers covering its area (tiles never read +//! outside themselves), which are then copied into the frame. + +use crate::decoder::RefFrame; +use crate::frame::{FrameBuf, MiInfo}; +use crate::header::{FrameHeader, RefInfo, SequenceHeader}; +use crate::spec_tables::*; +use std::ops::{Deref, DerefMut}; +use std::sync::Arc; + +/// Read-only (during tile decoding) frame state. +pub(crate) struct FrameShared { + pub seq: SequenceHeader, + pub fh: FrameHeader, + pub num_planes: usize, + pub ssx: usize, + pub ssy: usize, + pub bit_depth: u32, + pub lr_unit_rows: [usize; 3], + pub lr_unit_cols: [usize; 3], + pub cdef_cols: usize, + /// Reference slots (FrameStore and saved state) used by this frame. + pub refs: [Option>; NUM_REF_FRAMES], + pub ref_info: [RefInfo; NUM_REF_FRAMES], + /// MotionFieldMvs[ ref ][ y8 * w8 + x8 ] (7.9); empty when unused. + pub motion_field: [Vec<[i32; 2]>; 8], + /// PrevSegmentIds (mi units). + pub prev_segment_ids: Vec, +} + +impl FrameShared { + pub fn plane_residual_size(&self, subsize: usize, plane: usize) -> usize { + let sx = if plane > 0 { self.ssx } else { 0 }; + let sy = if plane > 0 { self.ssy } else { 0 }; + SUBSAMPLED_SIZE[subsize][sx][sy] as usize + } +} + +/// A byte per 4x4 unit of a plane (LoopfilterTxSizes), possibly for a region only. +#[derive(Clone, Default)] +pub(crate) struct Grid8 { + pub data: Vec, + pub ox: usize, + pub oy: usize, + pub stride: usize, + pub rows: usize, +} + +impl Grid8 { + fn new(ox: usize, oy: usize, stride: usize, rows: usize) -> Grid8 { + Grid8 { data: vec![0; stride * rows], ox, oy, stride, rows } + } + #[inline] + pub fn get(&self, row: usize, col: usize) -> u8 { + self.data[(row - self.oy) * self.stride + col - self.ox] + } + /// Stores outside the grid are dropped (blocks reaching past the allocated margin). + #[inline] + pub fn set(&mut self, row: usize, col: usize, v: u8) { + if col >= self.ox && col < self.ox + self.stride && row >= self.oy && row < self.oy + self.rows { + self.data[(row - self.oy) * self.stride + col - self.ox] = v; + } + } + fn copy_into(&self, dst: &mut Grid8) { + for y in 0..self.rows { + let d = (self.oy + y - dst.oy) * dst.stride + self.ox - dst.ox; + dst.data[d..d + self.stride].copy_from_slice(&self.data[y * self.stride..(y + 1) * self.stride]); + } + } +} + +/// Loop restoration parameters per unit. +#[derive(Clone, Default)] +pub(crate) struct LrUnits { + pub lr_type: [Vec; 3], + pub lr_wiener: [Vec<[[i8; 3]; 2]>; 3], + pub lr_sgr_set: [Vec; 3], + pub lr_sgr_xqd: [Vec<[i8; 2]>; 3], +} + +/// What tile decoding writes. +pub(crate) struct TileState { + pub cur: FrameBuf, + pub mi: MiInfo, + /// cdef_idx per 64x64 block (-1: not coded). + pub cdef: Vec, + /// LoopfilterTxSizes[ plane ][ row ][ col ] in 4x4 units of each plane. + pub lf_tx_size: [Grid8; 3], + pub lr: LrUnits, + /// LR units this tile coded (per plane), for merging. + pub lr_written: [Vec; 3], + pub above_level: [Vec; 3], + pub above_dc: [Vec; 3], + pub above_seg_pred: Vec, + pub left_level: [Vec; 3], + pub left_dc: [Vec; 3], + pub left_seg_pred: Vec, + /// TileIntraFrameYModeCdf (reset per tile). + pub intra_frame_y_mode_cdf: [[[u16; 14]; 5]; 5], + cdef_cols: usize, +} + +impl TileState { + fn with(cur: FrameBuf, mi: MiInfo, cdef: Vec, lf_tx_size: [Grid8; 3], lr: LrUnits, cdef_cols: usize) -> TileState { + let (mi_cols, mi_rows) = (mi.cols, mi.rows); + TileState { + cur, + mi, + cdef, + lf_tx_size, + lr, + lr_written: Default::default(), + above_level: std::array::from_fn(|_| vec![0; mi_cols + 64]), + above_dc: std::array::from_fn(|_| vec![0; mi_cols + 64]), + above_seg_pred: vec![0; mi_cols + 64], + left_level: std::array::from_fn(|_| vec![0; mi_rows + 64]), + left_dc: std::array::from_fn(|_| vec![0; mi_rows + 64]), + left_seg_pred: vec![0; mi_rows + 64], + intra_frame_y_mode_cdf: DEFAULT_INTRA_FRAME_Y_MODE_CDF, + cdef_cols, + } + } + + #[inline] + pub fn cdef_idx(&self, r: usize, c: usize) -> i8 { + self.cdef[(r >> 4) * self.cdef_cols + (c >> 4)] + } + + #[inline] + pub fn set_cdef_idx(&mut self, r: usize, c: usize, v: i8) { + let i = (r >> 4) * self.cdef_cols + (c >> 4); + if i < self.cdef.len() { + self.cdef[i] = v; + } + } + + #[inline] + pub fn set_lf_tx_size(&mut self, plane: usize, row: usize, col: usize, v: u8) { + self.lf_tx_size[plane].set(row, col, v); + } +} + +pub(crate) struct FrameState { + pub sh: FrameShared, + pub cur: FrameBuf, + pub mi: MiInfo, + cdef: Vec, + lf_tx_size: [Grid8; 3], + pub lr: LrUnits, +} + +impl Deref for FrameState { + type Target = FrameShared; + fn deref(&self) -> &FrameShared { + &self.sh + } +} + +impl DerefMut for FrameState { + fn deref_mut(&mut self) -> &mut FrameShared { + &mut self.sh + } +} + +fn count_units_in_frame(unit_size: usize, frame_size: usize) -> usize { + ((frame_size + (unit_size >> 1)) / unit_size).max(1) +} + +impl FrameState { + pub fn new(seq: &SequenceHeader, fh: &FrameHeader) -> FrameState { + let c = &seq.color; + let (ssx, ssy) = (c.subsampling_x as usize, c.subsampling_y as usize); + let mi_cols = fh.mi_cols as usize; + let mi_rows = fh.mi_rows as usize; + let cur = FrameBuf::new(fh.frame_width as usize, fh.frame_height as usize, c.num_planes, ssx, ssy, c.bit_depth); + let cdef_cols = mi_cols.div_ceil(16) + 2; + let cdef_rows = mi_rows.div_ceil(16) + 2; + let mut lr_unit_rows = [0; 3]; + let mut lr_unit_cols = [0; 3]; + let mut lr = LrUnits::default(); + for plane in 0..c.num_planes { + if fh.lr.frame_restoration_type[plane] == RESTORE_NONE as u8 { + continue; + } + let sub_x = if plane == 0 { 0 } else { ssx }; + let sub_y = if plane == 0 { 0 } else { ssy }; + let unit = fh.lr.loop_restoration_size[plane] as usize; + lr_unit_rows[plane] = count_units_in_frame(unit, (fh.frame_height as usize + sub_y) >> sub_y); + lr_unit_cols[plane] = count_units_in_frame(unit, (fh.upscaled_width as usize + sub_x) >> sub_x); + let n = lr_unit_rows[plane] * lr_unit_cols[plane]; + lr.lr_type[plane] = vec![RESTORE_NONE as u8; n]; + lr.lr_wiener[plane] = vec![[[0; 3]; 2]; n]; + lr.lr_sgr_set[plane] = vec![0; n]; + lr.lr_sgr_xqd[plane] = vec![[0; 2]; n]; + } + FrameState { + sh: FrameShared { + seq: seq.clone(), + fh: fh.clone(), + num_planes: c.num_planes, + ssx, + ssy, + bit_depth: c.bit_depth as u32, + lr_unit_rows, + lr_unit_cols, + cdef_cols, + refs: Default::default(), + ref_info: Default::default(), + motion_field: Default::default(), + prev_segment_ids: vec![0; mi_cols * mi_rows], + }, + cur, + mi: MiInfo::new(mi_cols, mi_rows, fh.allow_screen_content_tools), + cdef: vec![-1; cdef_cols * cdef_rows], + lf_tx_size: std::array::from_fn(|_| Grid8::new(0, 0, mi_cols + 32, mi_rows + 32)), + lr, + } + } + + #[inline] + pub fn cdef_idx(&self, r: usize, c: usize) -> i8 { + self.cdef[(r >> 4) * self.cdef_cols + (c >> 4)] + } + + #[inline] + pub fn lf_tx_size(&self, plane: usize, row: usize, col: usize) -> usize { + self.lf_tx_size[plane].get(row, col) as usize + } + + /// Move the frame's buffers into a tile state covering the whole frame. + pub fn take_tile_state(&mut self) -> TileState { + TileState::with( + std::mem::take(&mut self.cur), + std::mem::take(&mut self.mi), + std::mem::take(&mut self.cdef), + std::mem::take(&mut self.lf_tx_size), + std::mem::take(&mut self.lr), + self.cdef_cols, + ) + } + + /// Return the buffers taken by [`FrameState::take_tile_state`]. + pub fn put_tile_state(&mut self, t: TileState) { + self.cur = t.cur; + self.mi = t.mi; + self.cdef = t.cdef; + self.lf_tx_size = t.lf_tx_size; + self.lr = t.lr; + } + + /// Private buffers for the tile covering mode info rows [r0, r1) x columns [c0, c1); tiles + /// on the right / bottom frame edge also cover the allocation margins. + pub fn tile_state(&self, r0: usize, r1: usize, c0: usize, c1: usize) -> TileState { + let (mi_cols, mi_rows) = (self.mi.cols, self.mi.rows); + let x1 = if c1 >= mi_cols { usize::MAX } else { c1 * 4 }; + let y1 = if r1 >= mi_rows { usize::MAX } else { r1 * 4 }; + let cur = self.cur.region_like(c0 * 4, r0 * 4, x1, y1); + let mi = MiInfo::region(mi_cols, mi_rows, c0, r0, c1 - c0, r1 - r0, !self.mi.palette_size[0].is_empty()); + let lf = std::array::from_fn(|p| { + let (sx, sy) = if p == 0 { (0, 0) } else { (self.ssx, self.ssy) }; + let full = &self.lf_tx_size[p]; + let (gx0, gy0) = (c0 >> sx, r0 >> sy); + let gx1 = if c1 >= mi_cols { full.stride } else { c1 >> sx }; + let gy1 = if r1 >= mi_rows { full.rows } else { r1 >> sy }; + Grid8::new(gx0, gy0, gx1 - gx0, gy1 - gy0) + }); + TileState::with(cur, mi, vec![-1; self.cdef.len()], lf, self.lr.clone(), self.cdef_cols) + } + + /// Copy a tile's private buffers into the frame. + pub fn merge_tile_state(&mut self, t: &TileState) { + for p in 0..self.num_planes { + t.cur.planes[p].copy_into(&mut self.cur.planes[p]); + t.lf_tx_size[p].copy_into(&mut self.lf_tx_size[p]); + for &u in &t.lr_written[p] { + self.lr.lr_type[p][u] = t.lr.lr_type[p][u]; + self.lr.lr_wiener[p][u] = t.lr.lr_wiener[p][u]; + self.lr.lr_sgr_set[p][u] = t.lr.lr_sgr_set[p][u]; + self.lr.lr_sgr_xqd[p][u] = t.lr.lr_sgr_xqd[p][u]; + } + } + t.mi.copy_into(&mut self.mi); + for (d, &s) in self.cdef.iter_mut().zip(&t.cdef) { + if s != -1 { + *d = s; + } + } + } +} diff --git a/crates/av1/src/stats.rs b/crates/av1/src/stats.rs new file mode 100644 index 0000000..a060f52 --- /dev/null +++ b/crates/av1/src/stats.rs @@ -0,0 +1,114 @@ +//! Decode statistics: frame / tile counts and the busy time of every decoding stage. + +/// A decoding stage (see [`DecodeStats::stage_secs`]). +#[derive(Clone, Copy, Debug, PartialEq, Eq)] +pub enum Stage { + /// Frame header follow-up: reference setup, motion field estimation, CDF initialisation. + Setup, + /// Tile decoding: mode info, coefficients, prediction and reconstruction. + Tiles, + /// Deblocking loop filter (7.14). + LoopFilter, + /// CDEF (7.15). + Cdef, + /// Super-resolution upscaling (7.16). + Superres, + /// Loop restoration (7.17). + Restoration, + /// Reference update, motion vector storage, output copy and film grain synthesis. + Output, +} + +impl Stage { + pub const COUNT: usize = 7; + pub const ALL: [Stage; Stage::COUNT] = + [Stage::Setup, Stage::Tiles, Stage::LoopFilter, Stage::Cdef, Stage::Superres, Stage::Restoration, Stage::Output]; + + pub fn name(self) -> &'static str { + match self { + Stage::Setup => "setup", + Stage::Tiles => "tiles", + Stage::LoopFilter => "deblock", + Stage::Cdef => "cdef", + Stage::Superres => "superres", + Stage::Restoration => "restoration", + Stage::Output => "output", + } + } +} + +/// Counters accumulated by a [`crate::Decoder`]. Stage times are busy time summed over every +/// thread that worked on the stage (wall-clock per piece of work), so with threads they can add +/// up to more than the elapsed time. +#[derive(Clone, Copy, Debug, Default, PartialEq)] +pub struct DecodeStats { + /// Frames decoded (excluding show_existing_frame repeats). + pub frames: u64, + /// Frames decoded in draft mode without in-loop filters. + pub draft_frames: u64, + /// Tiles decoded. + pub tiles: u64, + /// Busy seconds per [`Stage`] (index with `stage as usize`). + pub stage_secs: [f64; Stage::COUNT], +} + +impl DecodeStats { + pub fn secs(&self, stage: Stage) -> f64 { + self.stage_secs[stage as usize] + } + + pub(crate) fn add(&mut self, stage: Stage, secs: f64) { + self.stage_secs[stage as usize] += secs; + } + + pub(crate) fn merge(&mut self, o: &DecodeStats) { + self.frames += o.frames; + self.draft_frames += o.draft_frames; + self.tiles += o.tiles; + for (a, b) in self.stage_secs.iter_mut().zip(o.stage_secs.iter()) { + *a += b; + } + } + + /// Sum of all stage times. + pub fn total_secs(&self) -> f64 { + self.stage_secs.iter().sum() + } +} + +/// A wall-clock stopwatch (a no-op on wasm32, which has no clock in std). +pub(crate) struct Timer { + #[cfg(not(target_arch = "wasm32"))] + start: std::time::Instant, +} + +impl Timer { + #[inline] + pub fn start() -> Timer { + Timer { + #[cfg(not(target_arch = "wasm32"))] + start: std::time::Instant::now(), + } + } + + /// Seconds since `start` (0 on wasm32). + #[inline] + pub fn secs(&self) -> f64 { + #[cfg(not(target_arch = "wasm32"))] + { + self.start.elapsed().as_secs_f64() + } + #[cfg(target_arch = "wasm32")] + { + 0.0 + } + } + + /// Seconds since `start`, restarting the stopwatch. + #[inline] + pub fn lap(&mut self) -> f64 { + let s = self.secs(); + *self = Timer::start(); + s + } +} diff --git a/crates/av1/src/symbol.rs b/crates/av1/src/symbol.rs new file mode 100644 index 0000000..a85f7f8 --- /dev/null +++ b/crates/av1/src/symbol.rs @@ -0,0 +1,138 @@ +//! Symbol (arithmetic) decoder, spec 8.2. +//! +//! The state follows the specification literally: `SymbolValue` / `SymbolRange` are 15/16-bit +//! quantities renormalised by reading `bits` new bits at a time, and `SymbolMaxBits` counts the +//! bits still available (going negative while zero padding is consumed). + +const EC_PROB_SHIFT: u32 = 6; +const EC_MIN_PROB: u32 = 4; + +pub(crate) struct SymbolDecoder<'a> { + data: &'a [u8], + /// Next byte of `data` to load into the window. + next: usize, + /// SymbolValue followed by `k` look-ahead bits: the bitstream with every bit inverted + /// (the spec's renormalisation XORs the new bits with ones), zero bits past the end of + /// the data reading as ones. SymbolValue = win >> k. + win: u64, + k: u32, + range: u32, + pub disable_update: bool, +} + +impl<'a> SymbolDecoder<'a> { + /// `init_symbol( sz )` over `data` (the tile's bytes). + pub fn new(data: &'a [u8], disable_update: bool) -> Self { + let mut d = SymbolDecoder { data, next: 0, win: 0, k: 0, range: 1 << 15, disable_update }; + // SymbolValue = ((1 << 15) - 1) ^ (first 15 bits): 15 inverted bits. + let mut filled = 0; + while filled < 15 + 32 { + d.win = (d.win << 8) | d.inverted_byte() as u64; + filled += 8; + } + d.k = filled - 15; + d + } + + #[inline(always)] + fn inverted_byte(&mut self) -> u8 { + let b = self.data.get(self.next).copied().unwrap_or(0); + self.next += 1; + !b + } + + /// `read_symbol( cdf )`: `cdf` has N + 1 entries (the last is the adaptation counter). + #[inline] + pub fn read_symbol(&mut self, cdf: &mut [u16]) -> usize { + let n = cdf.len() - 1; + let symbol = self.decode(cdf, n); + if !self.disable_update { + update_cdf(cdf, n, symbol); + } + symbol + } + + #[inline(always)] + fn decode(&mut self, cdf: &[u16], n: usize) -> usize { + let value = (self.win >> self.k) as u32; + let mut cur = self.range; + let mut symbol = 0usize; + let mut prev; + loop { + prev = cur; + let f = (1u32 << 15) - cdf[symbol] as u32; + cur = ((self.range >> 8) * (f >> EC_PROB_SHIFT)) >> (7 - EC_PROB_SHIFT); + cur += EC_MIN_PROB * (n - symbol - 1) as u32; + if value >= cur { + break; + } + symbol += 1; + } + self.range = prev - cur; + self.win -= (cur as u64) << self.k; + self.renormalize(); + symbol + } + + /// Renormalisation (8.2.6): SymbolValue takes `bits` more (inverted) bits, which are + /// already in the window. + #[inline(always)] + fn renormalize(&mut self) { + let bits = 15 - (31 - self.range.leading_zeros()); + self.range <<= bits; + self.k -= bits; + if self.k < 16 { + // SymbolValue < 2^16, so 16 + k <= 64 bits stay in the window. + while self.k <= 40 { + self.win = (self.win << 8) | self.inverted_byte() as u64; + self.k += 8; + } + } + } + + /// `read_bool()` + #[inline] + pub fn read_bool(&mut self) -> u32 { + let cdf = [1u16 << 14, 1 << 15, 0]; + self.decode(&cdf, 2) as u32 + } + + /// `read_literal( n )` (L(n)) + pub fn read_literal(&mut self, n: u32) -> u32 { + let mut x = 0; + for _ in 0..n { + x = (x << 1) | self.read_bool(); + } + x + } + + /// NS(n) + pub fn read_ns(&mut self, n: u32) -> u32 { + if n <= 1 { + return 0; + } + let w = 32 - n.leading_zeros(); + let m = (1u32 << w) - n; + let v = self.read_literal(w - 1); + if v < m { + return v; + } + let extra = self.read_literal(1); + (v << 1) - m + extra + } +} + +/// CDF adaptation (8.2.6). +#[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); + // Entries below `symbol` move towards 0, the others towards 1 << 15 (the specification's + // comparison with tmp, resolved per entry: c >= 0 and c <= 1 << 15), without branching on + // the decoded symbol. + for (i, e) in cdf[..n - 1].iter_mut().enumerate() { + let c = *e as u32; + *e = if i < symbol { c - (c >> rate) } else { c + (((1 << 15) - c) >> rate) } as u16; + } + cdf[n] += (count < 32) as u16; +} diff --git a/crates/av1/src/tile.rs b/crates/av1/src/tile.rs new file mode 100644 index 0000000..5d73688 --- /dev/null +++ b/crates/av1/src/tile.rs @@ -0,0 +1,2283 @@ +//! Tile decoding: partitions, mode info, residual coefficients, prediction and reconstruction +//! (spec 5.11 syntax with the 8.3 CDF selection and 7.11 / 7.12 / 7.13 processes). + +use crate::Result; +use crate::cdf::Cdfs; +use crate::frame::{MiInfo, Mv}; +use crate::header::{FrameHeader, NONE, SequenceHeader}; +use crate::intra::{IntraParams, is_directional_mode, predict_intra, round2_signed}; +use crate::spec_tables::*; +use crate::state::{FrameShared, TileState}; +use crate::symbol::SymbolDecoder; +use crate::transform::inverse_transform_2d; + +/// Per-block state (the block-level variables of the syntax). +#[derive(Clone, Default)] +pub(crate) struct Block { + pub mi_row: usize, + pub mi_col: usize, + pub mi_size: usize, + pub bw4: usize, + pub bh4: usize, + pub has_chroma: bool, + pub avail_u: bool, + pub avail_l: bool, + pub avail_u_chroma: bool, + pub avail_l_chroma: bool, + pub skip: bool, + pub skip_mode: bool, + pub segment_id: usize, + pub lossless: bool, + pub is_inter: bool, + pub use_intrabc: bool, + pub y_mode: usize, + pub uv_mode: usize, + pub angle_delta_y: i32, + pub angle_delta_uv: i32, + pub use_filter_intra: bool, + pub filter_intra_mode: usize, + pub cfl_alpha_u: i32, + pub cfl_alpha_v: i32, + pub palette_size_y: usize, + pub palette_size_uv: usize, + pub palette_colors_y: [u16; 8], + pub palette_colors_u: [u16; 8], + pub palette_colors_v: [u16; 8], + pub tx_size: usize, + pub ref_frame: [i8; 2], + pub mv: [Mv; 2], + pub interp_filter: [u8; 2], + pub motion_mode: u8, + pub comp_group_idx: u8, + pub compound_idx: u8, + pub max_luma_w: usize, + pub max_luma_h: usize, + /// Mv[ ] / PredMv[ ] (row, col) for the two reference lists. + pub mv_i: [[i32; 2]; 2], + pub pred_mv: [[i32; 2]; 2], + pub ref_mv_idx: usize, + pub interintra: bool, + pub interintra_mode: usize, + pub wedge_interintra: bool, + pub wedge_index: usize, + pub wedge_sign: usize, + pub mask_type: usize, + pub compound_type: usize, + pub local_valid: bool, + pub local_warp_params: [i32; 6], +} + +/// Decoder for one tile. +pub(crate) struct TileDecoder<'a, 'f> { + pub fs: &'f FrameShared, + pub t: &'f mut TileState, + pub sd: SymbolDecoder<'a>, + pub cdf: Box, + pub mi_row_start: usize, + pub mi_row_end: usize, + pub mi_col_start: usize, + pub mi_col_end: usize, + pub current_q_index: i32, + pub delta_lf: [i32; 4], + pub read_deltas: bool, + pub ref_sgr_xqd: [[i32; 2]; 3], + pub ref_lr_wiener: [[[i32; 3]; 2]; 3], + /// BlockDecoded[ plane ][ y + 1 ][ x + 1 ] for y, x in -1..=32. + pub block_decoded: [[[bool; 35]; 35]; 3], + pub b: Block, + pub mvs: crate::mvpred::MvStack, + pub nb: crate::inter_info::Neighbours, + /// Quant[] for the current transform block (raster order, stride per Adjusted size). + quant: Vec, + dequant: Vec, + residual: Vec, + color_map_y: Vec, + color_map_uv: Vec, + plane_tx_type: usize, + pub pred_buf: [Vec; 2], + pub mask: Vec, + /// Intermediate rows of the sub-pixel filters (up to 2 x 128 + 8 rows of 128 with 2:1 scaled references). + pub mc_tmp: Vec, + /// Coefficient levels of the current transform block, padded (see TX_PAD). + levels: Vec, +} + +const SB_MAX: usize = 32; // superblock size in 4x4 units (128 / 4) + +fn block_width(bs: usize) -> usize { + 4 * NUM_4X4_BLOCKS_WIDE[bs] as usize +} +fn block_height(bs: usize) -> usize { + 4 * NUM_4X4_BLOCKS_HIGH[bs] as usize +} + +impl<'a, 'f> TileDecoder<'a, 'f> { + pub fn new(fs: &'f FrameShared, t: &'f mut TileState, data: &'a [u8], cdf: Box, tile_row: usize, tile_col: usize) -> Self { + let ti = &fs.fh.tile_info; + let (rs, re) = (ti.mi_row_starts[tile_row] as usize, ti.mi_row_starts[tile_row + 1] as usize); + let (cs, ce) = (ti.mi_col_starts[tile_col] as usize, ti.mi_col_starts[tile_col + 1] as usize); + let disable = fs.fh.disable_cdf_update; + let q = fs.fh.quant.base_q_idx as i32; + TileDecoder { + fs, + t, + sd: SymbolDecoder::new(data, disable), + cdf, + mi_row_start: rs, + mi_row_end: re, + mi_col_start: cs, + mi_col_end: ce, + current_q_index: q, + delta_lf: [0; 4], + read_deltas: false, + ref_sgr_xqd: [[0; 2]; 3], + ref_lr_wiener: [[[0; 3]; 2]; 3], + block_decoded: [[[false; 35]; 35]; 3], + b: Block::default(), + mvs: Default::default(), + nb: Default::default(), + quant: vec![0; 1024], + dequant: vec![0; 64 * 64], + residual: vec![0; 64 * 64], + color_map_y: vec![0; 64 * 64], + color_map_uv: vec![0; 64 * 64], + plane_tx_type: 0, + pred_buf: [vec![0; 128 * 128], vec![0; 128 * 128]], + mask: vec![0; 128 * 128], + mc_tmp: vec![0; (2 * 128 + 8) * 128], + levels: vec![0; (32 + TX_PAD) * (32 + TX_PAD)], + } + } + + fn seq(&self) -> &SequenceHeader { + &self.fs.seq + } + fn fh(&self) -> &FrameHeader { + &self.fs.fh + } + + #[inline] + fn is_inside(&self, r: isize, c: isize) -> bool { + c >= self.mi_col_start as isize && c < self.mi_col_end as isize && r >= self.mi_row_start as isize && r < self.mi_row_end as isize + } + + /// decode_tile( ) + pub fn decode_tile(&mut self) -> Result<()> { + // clear_above_context( ) + let t = &mut *self.t; + for p in 0..3 { + t.above_level[p].fill(0); + t.above_dc[p].fill(0); + } + t.above_seg_pred.fill(0); + self.delta_lf = [0; 4]; + for plane in 0..self.fs.num_planes { + for pass in 0..2 { + self.ref_sgr_xqd[plane][pass] = [-32, 31][pass]; + self.ref_lr_wiener[plane][pass] = [3, -7, 15]; + } + } + let sb128 = self.seq().use_128x128_superblock; + let sb_size = if sb128 { BLOCK_128X128 } else { BLOCK_64X64 }; + let sb4 = NUM_4X4_BLOCKS_WIDE[sb_size] as usize; + let mut r = self.mi_row_start; + while r < self.mi_row_end { + // clear_left_context( ) + let t = &mut *self.t; + for p in 0..3 { + t.left_level[p].fill(0); + t.left_dc[p].fill(0); + } + t.left_seg_pred.fill(0); + let mut c = self.mi_col_start; + while c < self.mi_col_end { + self.read_deltas = self.fh().delta_q_present; + self.clear_cdef(r, c); + self.clear_block_decoded_flags(r, c, sb4); + self.read_lr(r, c, sb_size); + self.decode_partition(r, c, sb_size)?; + c += sb4; + } + r += sb4; + } + Ok(()) + } + + fn clear_cdef(&mut self, r: usize, c: usize) { + let t = &mut *self.t; + t.set_cdef_idx(r, c, -1); + if self.fs.seq.use_128x128_superblock { + t.set_cdef_idx(r, c + 16, -1); + t.set_cdef_idx(r + 16, c, -1); + t.set_cdef_idx(r + 16, c + 16, -1); + } + } + + fn clear_block_decoded_flags(&mut self, r: usize, c: usize, sb4: usize) { + let (ssx, ssy) = (self.fs.ssx, self.fs.ssy); + for plane in 0..self.fs.num_planes { + let sub_x = if plane > 0 { ssx } else { 0 }; + let sub_y = if plane > 0 { ssy } else { 0 }; + let sb_w4 = (self.mi_col_end as isize - c as isize) >> sub_x; + let sb_h4 = (self.mi_row_end as isize - r as isize) >> sub_y; + let bd = &mut self.block_decoded[plane]; + for y in -1..=((sb4 >> sub_y) as isize) { + for x in -1..=((sb4 >> sub_x) 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[(sb4 >> sub_y) + 1][0] = false; + } + } + + #[inline] + pub(crate) fn block_decoded_at(&self, plane: usize, y: isize, x: isize) -> bool { + let (yy, xx) = (y + 1, x + 1); + if yy < 0 || xx < 0 || yy > SB_MAX as isize + 1 || xx > SB_MAX as isize + 1 { + return false; + } + self.block_decoded[plane][yy as usize][xx as usize] + } + + fn decode_partition(&mut self, r: usize, c: usize, bsize: usize) -> Result<()> { + let (mi_rows, mi_cols) = (self.fs.fh.mi_rows as usize, self.fs.fh.mi_cols as usize); + if r >= mi_rows || c >= mi_cols { + return Ok(()); + } + let avail_u = self.is_inside(r as isize - 1, c as isize); + let avail_l = self.is_inside(r as isize, c as isize - 1); + let num4x4 = NUM_4X4_BLOCKS_WIDE[bsize] as usize; + let half = num4x4 >> 1; + let quarter = half >> 1; + let has_rows = (r + half) < mi_rows; + let has_cols = (c + half) < mi_cols; + let partition = if bsize < BLOCK_8X8 { + PARTITION_NONE + } else { + let bsl = MI_WIDTH_LOG2[bsize] as usize; + let mi = &self.t.mi; + let above = avail_u && (MI_WIDTH_LOG2[mi.mi_size[mi.idx(r - 1, c)] as usize] as usize) < bsl; + let left = avail_l && (MI_HEIGHT_LOG2[mi.mi_size[mi.idx(r, c - 1)] as usize] as usize) < bsl; + let ctx = left as usize * 2 + above as usize; + 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], + 4 => &mut self.cdf.partition_w64[ctx], + _ => &mut self.cdf.partition_w128[ctx], + }; + self.sd.read_symbol(cdf) + } else if has_cols || has_rows { + let pcdf: Vec = match bsl { + 2 => self.cdf.partition_w16[ctx].to_vec(), + 3 => self.cdf.partition_w32[ctx].to_vec(), + 4 => self.cdf.partition_w64[ctx].to_vec(), + _ => self.cdf.partition_w128[ctx].to_vec(), + }; + // pcdf holds cumulative values; P(k) = cdf[k] - cdf[k-1] (cdf[-1] = 0) + 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 + let mut s = pr(PARTITION_VERT) + pr(PARTITION_SPLIT) + pr(PARTITION_HORZ_A) + pr(PARTITION_VERT_A) + pr(PARTITION_VERT_B); + if bsize != BLOCK_128X128 { + s += pr(PARTITION_VERT_4); + } + s + } else { + let mut s = pr(PARTITION_HORZ) + pr(PARTITION_SPLIT) + pr(PARTITION_HORZ_A) + pr(PARTITION_HORZ_B) + pr(PARTITION_VERT_A); + if bsize != BLOCK_128X128 { + s += pr(PARTITION_HORZ_4); + } + s + }; + // The spec's inverse-CDF convention: cdf values count down from 32768. + let _ = psum; + let bit = self.read_split_bool(&pcdf, has_cols, bsize); + if has_cols { + if bit { PARTITION_SPLIT } else { PARTITION_HORZ } + } else if bit { + PARTITION_SPLIT + } else { + PARTITION_VERT + } + } else { + PARTITION_SPLIT + } + }; + let sub_size = PARTITION_SUBSIZE[partition][bsize] as usize; + let split_size = PARTITION_SUBSIZE[PARTITION_SPLIT][bsize] as usize; + match partition { + PARTITION_NONE => self.decode_block(r, c, sub_size)?, + PARTITION_HORZ => { + self.decode_block(r, c, sub_size)?; + if has_rows { + self.decode_block(r + half, c, sub_size)?; + } + } + PARTITION_VERT => { + self.decode_block(r, c, sub_size)?; + if has_cols { + self.decode_block(r, c + half, sub_size)?; + } + } + PARTITION_SPLIT => { + self.decode_partition(r, c, sub_size)?; + self.decode_partition(r, c + half, sub_size)?; + self.decode_partition(r + half, c, sub_size)?; + self.decode_partition(r + half, c + half, sub_size)?; + } + PARTITION_HORZ_A => { + self.decode_block(r, c, split_size)?; + self.decode_block(r, c + half, split_size)?; + self.decode_block(r + half, c, sub_size)?; + } + PARTITION_HORZ_B => { + self.decode_block(r, c, sub_size)?; + self.decode_block(r + half, c, split_size)?; + self.decode_block(r + half, c + half, split_size)?; + } + PARTITION_VERT_A => { + self.decode_block(r, c, split_size)?; + self.decode_block(r + half, c, split_size)?; + self.decode_block(r, c + half, sub_size)?; + } + PARTITION_VERT_B => { + self.decode_block(r, c, sub_size)?; + self.decode_block(r, c + half, split_size)?; + self.decode_block(r + half, c + half, split_size)?; + } + PARTITION_HORZ_4 => { + for k in 0..4 { + let rr = r + quarter * k; + if k < 3 || rr < mi_rows { + self.decode_block(rr, c, sub_size)?; + } + } + } + _ => { + for k in 0..4 { + let cc = c + quarter * k; + if k < 3 || cc < mi_cols { + self.decode_block(r, cc, sub_size)?; + } + } + } + } + Ok(()) + } + + /// split_or_horz / split_or_vert: a binary symbol whose probability is derived from the + /// partition CDF (8.3.2). Returns true for PARTITION_SPLIT. + fn read_split_bool(&mut self, pcdf: &[u16], split_or_horz: bool, bsize: usize) -> bool { + // The spec gives cdf values as P(X <= k) * 32768 (increasing); the probability of + // partition k is cdf[k] - cdf[k - 1]. + let pr = |k: usize| pcdf[k] as i32 - if k == 0 { 0 } else { pcdf[k - 1] as i32 }; + let mut psum = if split_or_horz { + pr(PARTITION_VERT) + pr(PARTITION_SPLIT) + pr(PARTITION_HORZ_A) + pr(PARTITION_VERT_A) + pr(PARTITION_VERT_B) + } else { + pr(PARTITION_HORZ) + pr(PARTITION_SPLIT) + pr(PARTITION_HORZ_A) + pr(PARTITION_HORZ_B) + pr(PARTITION_VERT_A) + }; + if bsize != BLOCK_128X128 { + psum += if split_or_horz { pr(PARTITION_VERT_4) } else { pr(PARTITION_HORZ_4) }; + } + let mut cdf = [((1i32 << 15) - psum) as u16, 1 << 15, 0]; + // no adaptation: the array is constructed for this read only + let save = self.sd.disable_update; + self.sd.disable_update = true; + let s = self.sd.read_symbol(&mut cdf); + self.sd.disable_update = save; + s == 1 + } + + fn decode_block(&mut self, r: usize, c: usize, sub_size: usize) -> Result<()> { + let (ssx, ssy) = (self.fs.ssx, self.fs.ssy); + let num_planes = self.fs.num_planes; + let mut b = Block { mi_row: r, mi_col: c, mi_size: sub_size, ..Default::default() }; + b.bw4 = NUM_4X4_BLOCKS_WIDE[sub_size] as usize; + b.bh4 = NUM_4X4_BLOCKS_HIGH[sub_size] as usize; + b.has_chroma = if b.bh4 == 1 && ssy == 1 && (r & 1) == 0 || b.bw4 == 1 && ssx == 1 && (c & 1) == 0 { false } else { num_planes > 1 }; + b.avail_u = self.is_inside(r as isize - 1, c as isize); + b.avail_l = self.is_inside(r as isize, c as isize - 1); + b.avail_u_chroma = b.avail_u; + b.avail_l_chroma = b.avail_l; + if b.has_chroma { + if ssy == 1 && b.bh4 == 1 { + b.avail_u_chroma = self.is_inside(r as isize - 2, c as isize); + } + if ssx == 1 && b.bw4 == 1 { + b.avail_l_chroma = self.is_inside(r as isize, c as isize - 2); + } + } else { + b.avail_u_chroma = false; + b.avail_l_chroma = false; + } + b.ref_frame = [INTRA_FRAME as i8, NONE]; + self.b = b; + if self.fs.fh.frame_is_intra { + self.intra_frame_mode_info()?; + } else { + self.inter_frame_mode_info()?; + } + self.palette_tokens(); + self.read_block_tx_size(); + if self.b.skip { + self.reset_block_context(); + } + let b = &self.b; + let is_compound = b.ref_frame[1] > INTRA_FRAME as i8; + { + let mi = &mut self.t.mi; + let rows = b.bh4.min(mi.rows.saturating_sub(r)); + let cols = b.bw4.min(mi.cols.saturating_sub(c)); + let set_uv = b.ref_frame[0] == INTRA_FRAME as i8 && b.has_chroma; + let mvs = [Mv::new(b.mv_i[0][0], b.mv_i[0][1]), Mv::new(b.mv_i[1][0], b.mv_i[1][1])]; + for y in 0..rows { + let i = mi.idx(r + y, c); + let span = i..i + cols; + mi.y_mode[span.clone()].fill(b.y_mode as u8); + if set_uv { + mi.uv_mode[span.clone()].fill(b.uv_mode as u8); + } + mi.ref_frame[span.clone()].fill(b.ref_frame); + mi.written[span.clone()].fill(true); + if b.is_inter { + if !b.use_intrabc { + mi.comp_group_idx[span.clone()].fill(b.comp_group_idx); + mi.compound_idx[span.clone()].fill(b.compound_idx); + } + mi.interp_filter[span.clone()].fill(b.interp_filter); + for m in mi.mv[span].iter_mut() { + m[0] = mvs[0]; + if is_compound { + m[1] = mvs[1]; + } + } + } + } + } + self.compute_prediction()?; + self.residual()?; + let b = &self.b; + let delta_lf = self.delta_lf; + let mi = &mut self.t.mi; + let rows = b.bh4.min(mi.rows.saturating_sub(r)); + let cols = b.bw4.min(mi.cols.saturating_sub(c)); + let dlf = [delta_lf[0] as i8, delta_lf[1] as i8, delta_lf[2] as i8, delta_lf[3] as i8]; + for y in 0..rows { + let i = mi.idx(r + y, c); + let span = i..i + cols; + mi.is_inter[span.clone()].fill(b.is_inter); + mi.skip_mode[span.clone()].fill(b.skip_mode); + mi.skip[span.clone()].fill(b.skip); + mi.tx_size[span.clone()].fill(b.tx_size as u8); + mi.mi_size[span.clone()].fill(b.mi_size as u8); + mi.segment_id[span.clone()].fill(b.segment_id as u8); + if !mi.palette_size[0].is_empty() { + mi.palette_size[0][span.clone()].fill(b.palette_size_y as u8); + mi.palette_size[1][span.clone()].fill(b.palette_size_uv as u8); + mi.palette_colors[0][span.clone()].fill(b.palette_colors_y); + mi.palette_colors[1][span.clone()].fill(b.palette_colors_u); + } + mi.delta_lf[span.clone()].fill(dlf); + mi.motion_mode[span].fill(b.motion_mode); + } + Ok(()) + } + + fn reset_block_context(&mut self) { + let b = &self.b; + let (ssx, ssy) = (self.fs.ssx, self.fs.ssy); + for plane in 0..(1 + 2 * b.has_chroma as usize) { + let sub_x = if plane > 0 { ssx } else { 0 }; + let sub_y = if plane > 0 { ssy } else { 0 }; + for i in (b.mi_col >> sub_x)..((b.mi_col + b.bw4) >> sub_x) { + self.t.above_level[plane][i] = 0; + self.t.above_dc[plane][i] = 0; + } + for i in (b.mi_row >> sub_y)..((b.mi_row + b.bh4) >> sub_y) { + self.t.left_level[plane][i] = 0; + self.t.left_dc[plane][i] = 0; + } + } + } + + fn intra_frame_mode_info(&mut self) -> Result<()> { + self.b.skip = false; + let pre_skip = self.fs.fh.seg.seg_id_pre_skip; + if pre_skip { + self.intra_segment_id(); + } + self.b.skip_mode = false; + self.read_skip(); + if !pre_skip { + self.intra_segment_id(); + } + self.read_cdef(); + self.read_delta_qindex(); + self.read_delta_lf(); + self.read_deltas = false; + self.b.ref_frame = [INTRA_FRAME as i8, NONE]; + self.b.use_intrabc = if self.fs.fh.allow_intrabc { self.sd.read_symbol(&mut self.cdf.intrabc) == 1 } else { false }; + if self.b.use_intrabc { + self.b.is_inter = true; + self.b.y_mode = DC_PRED; + self.b.uv_mode = DC_PRED; + self.b.motion_mode = SIMPLE as u8; + self.b.compound_type = COMPOUND_AVERAGE; + self.b.palette_size_y = 0; + self.b.palette_size_uv = 0; + self.b.interp_filter = [crate::header::BILINEAR; 2]; + self.find_mv_stack(false); + self.assign_mv(false)?; + return Ok(()); + } + self.b.is_inter = false; + let mi = &self.t.mi; + let (r, c) = (self.b.mi_row, self.b.mi_col); + let above = if self.b.avail_u { mi.y_mode[mi.idx(r - 1, c)] as usize } else { DC_PRED }; + let left = if self.b.avail_l { mi.y_mode[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; + let mut cdf = self.t.intra_frame_y_mode_cdf[actx][lctx]; + self.b.y_mode = self.sd.read_symbol(&mut cdf); + self.t.intra_frame_y_mode_cdf[actx][lctx] = cdf; + self.intra_angle_info_y(); + if self.b.has_chroma { + self.read_uv_mode(); + if self.b.uv_mode == UV_CFL_PRED { + self.read_cfl_alphas(); + } + self.intra_angle_info_uv(); + } + self.b.palette_size_y = 0; + self.b.palette_size_uv = 0; + let ms = self.b.mi_size; + if ms >= BLOCK_8X8 && block_width(ms) <= 64 && block_height(ms) <= 64 && self.fs.fh.allow_screen_content_tools { + self.palette_mode_info(); + } + self.filter_intra_mode_info(); + Ok(()) + } + + pub(crate) fn read_uv_mode(&mut self) { + let ms = self.b.mi_size; + let cfl_allowed = if self.b.lossless && self.fs.plane_residual_size(ms, 1) == BLOCK_4X4 { + true + } else { + !self.b.lossless && block_width(ms).max(block_height(ms)) <= 32 + }; + let y = self.b.y_mode; + self.b.uv_mode = if cfl_allowed { + self.sd.read_symbol(&mut self.cdf.uv_mode_cfl_allowed[y]) + } else { + self.sd.read_symbol(&mut self.cdf.uv_mode_cfl_not_allowed[y]) + }; + } + + pub(crate) fn intra_angle_info_y(&mut self) { + self.b.angle_delta_y = 0; + if self.b.mi_size >= BLOCK_8X8 && is_directional_mode(self.b.y_mode) { + let v = self.sd.read_symbol(&mut self.cdf.angle_delta[self.b.y_mode - V_PRED]); + self.b.angle_delta_y = v as i32 - MAX_ANGLE_DELTA as i32; + } + } + + pub(crate) fn intra_angle_info_uv(&mut self) { + self.b.angle_delta_uv = 0; + if self.b.mi_size >= BLOCK_8X8 && is_directional_mode(self.b.uv_mode) { + let v = self.sd.read_symbol(&mut self.cdf.angle_delta[self.b.uv_mode - V_PRED]); + self.b.angle_delta_uv = v as i32 - MAX_ANGLE_DELTA as i32; + } + } + + pub(crate) fn read_cfl_alphas(&mut self) { + let signs = self.sd.read_symbol(&mut self.cdf.cfl_sign); + let sign_u = (signs + 1) / 3; + let sign_v = (signs + 1) % 3; + self.b.cfl_alpha_u = if sign_u != CFL_SIGN_ZERO { + let ctx = (sign_u - 1) * 3 + sign_v; + let a = 1 + self.sd.read_symbol(&mut self.cdf.cfl_alpha[ctx]) as i32; + if sign_u == CFL_SIGN_NEG { -a } else { a } + } else { + 0 + }; + self.b.cfl_alpha_v = if sign_v != CFL_SIGN_ZERO { + let ctx = (sign_v - 1) * 3 + sign_u; + let a = 1 + self.sd.read_symbol(&mut self.cdf.cfl_alpha[ctx]) as i32; + if sign_v == CFL_SIGN_NEG { -a } else { a } + } else { + 0 + }; + } + + fn intra_segment_id(&mut self) { + if self.fs.fh.seg.enabled { + self.read_segment_id(); + } else { + self.b.segment_id = 0; + } + self.b.lossless = self.fs.fh.lossless_array[self.b.segment_id]; + } + + pub(crate) fn read_segment_id(&mut self) { + let (r, c) = (self.b.mi_row, self.b.mi_col); + let mi = &self.t.mi; + let prev_ul = if self.b.avail_u && self.b.avail_l { mi.segment_id[mi.idx(r - 1, c - 1)] as i32 } else { -1 }; + let prev_u = if self.b.avail_u { mi.segment_id[mi.idx(r - 1, c)] as i32 } else { -1 }; + let prev_l = if self.b.avail_l { mi.segment_id[mi.idx(r, c - 1)] as i32 } else { -1 }; + let pred = if prev_u == -1 { + if prev_l == -1 { 0 } else { prev_l } + } else if prev_l == -1 || prev_ul == prev_u { + prev_u + } else { + prev_l + }; + if self.b.skip { + self.b.segment_id = pred as usize; + } else { + let ctx = if prev_ul < 0 { + 0 + } else if prev_ul == prev_u && prev_ul == prev_l { + 2 + } else if prev_ul == prev_u || prev_ul == prev_l || prev_u == prev_l { + 1 + } else { + 0 + }; + let s = self.sd.read_symbol(&mut self.cdf.segment_id[ctx]) as i32; + let max = self.fs.fh.seg.last_active_seg_id as i32 + 1; + self.b.segment_id = neg_deinterleave(s, pred, max).clamp(0, 7) as usize; + } + } + + fn seg_feature_active(&self, feature: usize) -> bool { + self.fs.fh.seg.enabled && self.fs.fh.seg.features.enabled[self.b.segment_id][feature] + } + + pub(crate) fn read_skip(&mut self) { + if self.fs.fh.seg.seg_id_pre_skip && self.seg_feature_active(SEG_LVL_SKIP) { + self.b.skip = true; + } else { + let (r, c) = (self.b.mi_row, self.b.mi_col); + let mi = &self.t.mi; + let mut ctx = 0; + if self.b.avail_u { + ctx += mi.skip[mi.idx(r - 1, c)] as usize; + } + if self.b.avail_l { + ctx += mi.skip[mi.idx(r, c - 1)] as usize; + } + self.b.skip = self.sd.read_symbol(&mut self.cdf.skip[ctx]) == 1; + } + } + + pub(crate) fn read_cdef(&mut self) { + let fh = &self.fs.fh; + if self.b.skip || fh.coded_lossless || !self.fs.seq.enable_cdef || fh.allow_intrabc { + return; + } + let cdef_size4 = NUM_4X4_BLOCKS_WIDE[BLOCK_64X64] as usize; + let mask = !(cdef_size4 - 1); + let r = self.b.mi_row & mask; + let c = self.b.mi_col & mask; + if self.t.cdef_idx(r, c) == -1 { + let bits = fh.cdef.bits; + let v = self.sd.read_literal(bits) as i8; + let w4 = self.b.bw4; + let h4 = self.b.bh4; + let mut i = r; + while i < r + h4 { + let mut j = c; + while j < c + w4 { + self.t.set_cdef_idx(i, j, v); + j += cdef_size4; + } + i += cdef_size4; + } + } + } + + pub(crate) fn read_delta_qindex(&mut self) { + let sb_size = if self.fs.seq.use_128x128_superblock { BLOCK_128X128 } else { BLOCK_64X64 }; + if self.b.mi_size == sb_size && self.b.skip { + return; + } + if self.read_deltas { + let mut abs = self.sd.read_symbol(&mut self.cdf.delta_q) as i32; + if abs == DELTA_Q_SMALL as i32 { + let rem_bits = self.sd.read_literal(3) + 1; + let abs_bits = self.sd.read_literal(rem_bits) as i32; + abs = abs_bits + (1 << rem_bits) + 1; + } + if abs != 0 { + let sign = self.sd.read_literal(1); + let reduced = if sign == 1 { -abs } else { abs }; + self.current_q_index = (self.current_q_index + (reduced << self.fs.fh.delta_q_res)).clamp(1, 255); + } + } + } + + pub(crate) fn read_delta_lf(&mut self) { + let sb_size = if self.fs.seq.use_128x128_superblock { BLOCK_128X128 } else { BLOCK_64X64 }; + if self.b.mi_size == sb_size && self.b.skip { + return; + } + let fh = &self.fs.fh; + if self.read_deltas && fh.delta_lf_present { + let count = if fh.delta_lf_multi { if self.fs.num_planes > 1 { FRAME_LF_COUNT } else { FRAME_LF_COUNT - 2 } } else { 1 }; + let multi = fh.delta_lf_multi; + let res = fh.delta_lf_res; + for i in 0..count { + let mut abs = + if multi { self.sd.read_symbol(&mut self.cdf.delta_lf_multi[i]) } else { self.sd.read_symbol(&mut self.cdf.delta_lf) } as i32; + if abs == DELTA_LF_SMALL as i32 { + let n = self.sd.read_literal(3) + 1; + let bits = self.sd.read_literal(n) as i32; + abs = bits + (1 << n) + 1; + } + if abs != 0 { + let sign = self.sd.read_literal(1); + let reduced = if sign == 1 { -abs } else { abs }; + self.delta_lf[i] = (self.delta_lf[i] + (reduced << res)).clamp(-(MAX_LOOP_FILTER as i32), MAX_LOOP_FILTER as i32); + } + } + } + } + + pub(crate) fn filter_intra_mode_info(&mut self) { + self.b.use_filter_intra = false; + let ms = self.b.mi_size; + if self.fs.seq.enable_filter_intra && self.b.y_mode == DC_PRED && self.b.palette_size_y == 0 && block_width(ms).max(block_height(ms)) <= 32 { + self.b.use_filter_intra = self.sd.read_symbol(&mut self.cdf.filter_intra[ms]) == 1; + if self.b.use_filter_intra { + self.b.filter_intra_mode = self.sd.read_symbol(&mut self.cdf.filter_intra_mode); + } + } + } + + // ---- palette ------------------------------------------------------------------------- + + pub(crate) fn palette_mode_info(&mut self) { + let ms = self.b.mi_size; + let bsize_ctx = MI_WIDTH_LOG2[ms] as usize + MI_HEIGHT_LOG2[ms] as usize - 2; + let bd = self.fs.bit_depth; + let (r, c) = (self.b.mi_row, self.b.mi_col); + if self.b.y_mode == DC_PRED { + let mi = &self.t.mi; + let mut ctx = 0; + if self.b.avail_u && mi.palette_size[0][mi.idx(r - 1, c)] > 0 { + ctx += 1; + } + if self.b.avail_l && mi.palette_size[0][mi.idx(r, c - 1)] > 0 { + ctx += 1; + } + if self.sd.read_symbol(&mut self.cdf.palette_y_mode[bsize_ctx][ctx]) == 1 { + let n = self.sd.read_symbol(&mut self.cdf.palette_y_size[bsize_ctx]) + 2; + self.b.palette_size_y = n; + let cache = self.get_palette_cache(0); + let mut colors = [0u16; 8]; + let mut idx = 0; + for &cv in &cache { + if idx >= n { + break; + } + if self.sd.read_literal(1) == 1 { + colors[idx] = cv; + idx += 1; + } + } + if idx < n { + colors[idx] = self.sd.read_literal(bd) as u16; + idx += 1; + } + let mut palette_bits = 0; + if idx < n { + let min_bits = bd - 3; + palette_bits = min_bits + self.sd.read_literal(2); + } + while idx < n { + let delta = self.sd.read_literal(palette_bits) as i32 + 1; + colors[idx] = (colors[idx - 1] as i32 + delta).clamp(0, (1 << bd) - 1) as u16; + let range = (1i32 << bd) - colors[idx] as i32 - 1; + palette_bits = palette_bits.min(ceil_log2(range as u32)); + idx += 1; + } + colors[..n].sort_unstable(); + self.b.palette_colors_y = colors; + } + } + if self.b.has_chroma && self.b.uv_mode == DC_PRED { + let ctx = (self.b.palette_size_y > 0) as usize; + if self.sd.read_symbol(&mut self.cdf.palette_uv_mode[ctx]) == 1 { + let n = self.sd.read_symbol(&mut self.cdf.palette_uv_size[bsize_ctx]) + 2; + self.b.palette_size_uv = n; + let cache = self.get_palette_cache(1); + let mut colors = [0u16; 8]; + let mut idx = 0; + for &cv in &cache { + if idx >= n { + break; + } + if self.sd.read_literal(1) == 1 { + colors[idx] = cv; + idx += 1; + } + } + if idx < n { + colors[idx] = self.sd.read_literal(bd) as u16; + idx += 1; + } + let mut palette_bits = 0; + if idx < n { + palette_bits = bd - 3 + self.sd.read_literal(2); + } + while idx < n { + let delta = self.sd.read_literal(palette_bits) as i32; + colors[idx] = (colors[idx - 1] as i32 + delta).clamp(0, (1 << bd) - 1) as u16; + let range = (1i32 << bd) - colors[idx] as i32; + palette_bits = palette_bits.min(ceil_log2(range as u32)); + idx += 1; + } + colors[..n].sort_unstable(); + self.b.palette_colors_u = colors; + let mut v = [0u16; 8]; + if self.sd.read_literal(1) == 1 { + let min_bits = bd - 4; + let max_val = 1i32 << bd; + let bits = min_bits + self.sd.read_literal(2); + v[0] = self.sd.read_literal(bd) as u16; + for idx in 1..n { + let mut delta = self.sd.read_literal(bits) as i32; + if delta != 0 && self.sd.read_literal(1) == 1 { + delta = -delta; + } + let mut val = v[idx - 1] as i32 + delta; + if val < 0 { + val += max_val; + } + if val >= max_val { + val -= max_val; + } + v[idx] = val.clamp(0, (1 << bd) - 1) as u16; + } + } else { + for item in v.iter_mut().take(n) { + *item = self.sd.read_literal(bd) as u16; + } + } + self.b.palette_colors_v = v; + } + } + } + + fn get_palette_cache(&self, plane: usize) -> Vec { + let (r, c) = (self.b.mi_row, self.b.mi_col); + let mi = &self.t.mi; + let above_n = if (r * 4) % 64 != 0 { mi.palette_size[plane][mi.idx(r - 1, c)] as usize } else { 0 }; + let left_n = if self.b.avail_l { mi.palette_size[plane][mi.idx(r, c - 1)] as usize } else { 0 }; + let above: &[u16] = if above_n > 0 { &mi.palette_colors[plane][mi.idx(r - 1, c)][..above_n] } else { &[] }; + let left: &[u16] = if left_n > 0 { &mi.palette_colors[plane][mi.idx(r, c - 1)][..left_n] } else { &[] }; + let (mut ai, mut li) = (0, 0); + let mut out: Vec = Vec::with_capacity(16); + while ai < above_n && li < left_n { + let (ac, lc) = (above[ai], left[li]); + if lc < ac { + if out.last() != Some(&lc) { + out.push(lc); + } + li += 1; + } else { + if out.last() != Some(&ac) { + out.push(ac); + } + ai += 1; + if lc == ac { + li += 1; + } + } + } + for &v in &above[ai..above_n] { + if out.last() != Some(&v) { + out.push(v); + } + } + for &v in &left[li..left_n] { + if out.last() != Some(&v) { + out.push(v); + } + } + out + } + + fn palette_tokens(&mut self) { + let ms = self.b.mi_size; + let bh = block_height(ms); + let bw = block_width(ms); + let (mi_rows, mi_cols) = (self.fs.fh.mi_rows as usize, self.fs.fh.mi_cols as usize); + let on_h = bh.min((mi_rows - self.b.mi_row) * 4); + let on_w = bw.min((mi_cols - self.b.mi_col) * 4); + if self.b.palette_size_y > 0 { + let n = self.b.palette_size_y; + let mut map = std::mem::take(&mut self.color_map_y); + self.read_color_map(&mut map, n, bw, bh, on_w, on_h, 0); + self.color_map_y = map; + } + if self.b.palette_size_uv > 0 { + let n = self.b.palette_size_uv; + let (ssx, ssy) = (self.fs.ssx, self.fs.ssy); + let (mut bw, mut bh, mut on_w, mut on_h) = (bw >> ssx, bh >> ssy, on_w >> ssx, on_h >> ssy); + if bw < 4 { + bw += 2; + on_w += 2; + } + if bh < 4 { + bh += 2; + on_h += 2; + } + let mut map = std::mem::take(&mut self.color_map_uv); + self.read_color_map(&mut map, n, bw, bh, on_w, on_h, 1); + self.color_map_uv = map; + } + } + + /// Color index map (stride 64). + #[allow(clippy::too_many_arguments)] + fn read_color_map(&mut self, map: &mut [u8], n: usize, bw: usize, bh: usize, on_w: usize, on_h: usize, plane: usize) { + map[0] = self.sd.read_ns(n as u32) as u8; + for i in 1..(on_h + on_w - 1) { + let jmax = i.min(on_w - 1); + let jmin = (i as isize - on_h as isize + 1).max(0) as usize; + let mut j = jmax as isize; + while j >= jmin as isize { + let (rr, cc) = (i - j as usize, j as usize); + let (order, hash) = palette_color_context(map, rr, cc, n); + let ctx = PALETTE_COLOR_CONTEXT[hash] as usize; + let cdf: &mut [u16] = match (plane, n) { + (0, 2) => &mut self.cdf.palette_2_y_color[ctx], + (0, 3) => &mut self.cdf.palette_3_y_color[ctx], + (0, 4) => &mut self.cdf.palette_4_y_color[ctx], + (0, 5) => &mut self.cdf.palette_5_y_color[ctx], + (0, 6) => &mut self.cdf.palette_6_y_color[ctx], + (0, 7) => &mut self.cdf.palette_7_y_color[ctx], + (0, _) => &mut self.cdf.palette_8_y_color[ctx], + (_, 2) => &mut self.cdf.palette_2_uv_color[ctx], + (_, 3) => &mut self.cdf.palette_3_uv_color[ctx], + (_, 4) => &mut self.cdf.palette_4_uv_color[ctx], + (_, 5) => &mut self.cdf.palette_5_uv_color[ctx], + (_, 6) => &mut self.cdf.palette_6_uv_color[ctx], + (_, 7) => &mut self.cdf.palette_7_uv_color[ctx], + _ => &mut self.cdf.palette_8_uv_color[ctx], + }; + let s = self.sd.read_symbol(cdf); + map[rr * 64 + cc] = order[s]; + j -= 1; + } + } + for i in 0..on_h { + for j in on_w..bw { + map[i * 64 + j] = map[i * 64 + on_w - 1]; + } + } + for i in on_h..bh { + for j in 0..bw { + map[i * 64 + j] = map[(on_h - 1) * 64 + j]; + } + } + } + + // ---- transform size --------------------------------------------------------------------- + + fn read_block_tx_size(&mut self) { + let b = &self.b; + let (bw4, bh4) = (b.bw4, b.bh4); + let fh = &self.fs.fh; + if fh.tx_mode == TX_MODE_SELECT as u8 && b.mi_size > BLOCK_4X4 && b.is_inter && !b.skip && !b.lossless { + let max_tx = MAX_TX_SIZE_RECT[b.mi_size] as usize; + let tw4 = TX_WIDTH[max_tx] as usize / 4; + let th4 = TX_HEIGHT[max_tx] as usize / 4; + let (r0, c0) = (b.mi_row, b.mi_col); + let mut row = r0; + while row < r0 + bh4 { + let mut col = c0; + while col < c0 + bw4 { + self.read_var_tx_size(row, col, max_tx, 0); + col += tw4; + } + row += th4; + } + } else { + let allow = !b.skip || !b.is_inter; + self.read_tx_size(allow); + let (r0, c0, ts) = (self.b.mi_row, self.b.mi_col, self.b.tx_size as u8); + let mi = &mut self.t.mi; + for row in r0..(r0 + bh4).min(mi.rows) { + for col in c0..(c0 + bw4).min(mi.cols) { + let i = mi.idx(row, col); + mi.inter_tx_size[i] = ts; + } + } + } + } + + fn read_tx_size(&mut self, allow_select: bool) { + if self.b.lossless { + self.b.tx_size = TX_4X4; + return; + } + let ms = self.b.mi_size; + let max_rect = MAX_TX_SIZE_RECT[ms] as usize; + let max_depth = MAX_TX_DEPTH_TABLE[ms] as usize; + self.b.tx_size = max_rect; + if ms > BLOCK_4X4 && allow_select && self.fs.fh.tx_mode == TX_MODE_SELECT as u8 { + let ctx = self.tx_depth_ctx(max_rect); + let depth = match max_depth { + 4 => self.sd.read_symbol(&mut self.cdf.tx_64x64[ctx]), + 3 => self.sd.read_symbol(&mut self.cdf.tx_32x32[ctx]), + 2 => self.sd.read_symbol(&mut self.cdf.tx_16x16[ctx]), + _ => self.sd.read_symbol(&mut self.cdf.tx_8x8[ctx]), + }; + for _ in 0..depth { + self.b.tx_size = SPLIT_TX_SIZE[self.b.tx_size] as usize; + } + } + } + + fn tx_depth_ctx(&self, max_rect: usize) -> usize { + let (r, c) = (self.b.mi_row, self.b.mi_col); + let mi = &self.t.mi; + let max_w = TX_WIDTH[max_rect] as usize; + let max_h = TX_HEIGHT[max_rect] as usize; + let above_w = if self.b.avail_u && mi.is_inter[mi.idx(r - 1, c)] { + block_width(mi.mi_size[mi.idx(r - 1, c)] as usize) + } else if self.b.avail_u { + self.get_above_tx_width(r, c) + } else { + 0 + }; + let left_h = if self.b.avail_l && mi.is_inter[mi.idx(r, c - 1)] { + block_height(mi.mi_size[mi.idx(r, c - 1)] as usize) + } else if self.b.avail_l { + self.get_left_tx_height(r, c) + } else { + 0 + }; + (above_w >= max_w) as usize + (left_h >= max_h) as usize + } + + fn get_above_tx_width(&self, row: usize, col: usize) -> usize { + let mi = &self.t.mi; + if row == self.b.mi_row { + if !self.b.avail_u { + return 64; + } else if mi.skip[mi.idx(row - 1, col)] && mi.is_inter[mi.idx(row - 1, col)] { + return block_width(mi.mi_size[mi.idx(row - 1, col)] as usize); + } + } + TX_WIDTH[mi.inter_tx_size[mi.idx(row - 1, col)] as usize] as usize + } + + fn get_left_tx_height(&self, row: usize, col: usize) -> usize { + let mi = &self.t.mi; + if col == self.b.mi_col { + if !self.b.avail_l { + return 64; + } else if mi.skip[mi.idx(row, col - 1)] && mi.is_inter[mi.idx(row, col - 1)] { + return block_height(mi.mi_size[mi.idx(row, col - 1)] as usize); + } + } + TX_HEIGHT[mi.inter_tx_size[mi.idx(row, col - 1)] as usize] as usize + } + + fn read_var_tx_size(&mut self, row: usize, col: usize, tx_sz: usize, depth: usize) { + let (mi_rows, mi_cols) = (self.fs.fh.mi_rows as usize, self.fs.fh.mi_cols as usize); + if row >= mi_rows || col >= mi_cols { + return; + } + let split = if tx_sz == TX_4X4 || depth == MAX_VARTX_DEPTH { + false + } else { + let above = self.get_above_tx_width(row, col) < TX_WIDTH[tx_sz] as usize; + let left = self.get_left_tx_height(row, col) < TX_HEIGHT[tx_sz] as usize; + let ms = self.b.mi_size; + let size = 64.min(block_width(ms).max(block_height(ms))); + let max_tx = find_tx_size(size, size); + let sqr_up = TX_SIZE_SQR_UP[tx_sz] as usize; + let ctx = (sqr_up != max_tx) as usize * 3 + (TX_SIZES - 1 - max_tx) * 6 + above as usize + left as usize; + self.sd.read_symbol(&mut self.cdf.txfm_split[ctx]) == 1 + }; + let w4 = TX_WIDTH[tx_sz] as usize / 4; + let h4 = TX_HEIGHT[tx_sz] as usize / 4; + if split { + let sub = SPLIT_TX_SIZE[tx_sz] as usize; + let sw = TX_WIDTH[sub] as usize / 4; + let sh = TX_HEIGHT[sub] as usize / 4; + let mut i = 0; + while i < h4 { + let mut j = 0; + while j < w4 { + self.read_var_tx_size(row + i, col + j, sub, depth + 1); + j += sw; + } + i += sh; + } + } else { + let mi = &mut self.t.mi; + for i in 0..h4 { + for j in 0..w4 { + if row + i < mi.rows && col + j < mi.cols { + let k = mi.idx(row + i, col + j); + mi.inter_tx_size[k] = tx_sz as u8; + } + } + } + self.b.tx_size = tx_sz; + } + } + + // ---- prediction and residual ---------------------------------------------------------- + + /// compute_prediction( ) (5.11.33) + fn compute_prediction(&mut self) -> Result<()> { + if !self.b.is_inter { + return Ok(()); + } + let sb_mask = if self.fs.seq.use_128x128_superblock { 31 } else { 15 }; + let sub_row = self.b.mi_row & sb_mask; + let sub_col = self.b.mi_col & sb_mask; + let (ssx, ssy) = (self.fs.ssx, self.fs.ssy); + let is_inter_intra = self.b.ref_frame[1] == INTRA_FRAME as i8; + for plane in 0..(1 + 2 * self.b.has_chroma as usize) { + let plane_sz = self.fs.plane_residual_size(self.b.mi_size, plane); + let n4w = NUM_4X4_BLOCKS_WIDE[plane_sz] as usize; + let n4h = NUM_4X4_BLOCKS_HIGH[plane_sz] as usize; + let log2w = 2 + MI_WIDTH_LOG2[plane_sz] as u32; + let log2h = 2 + MI_HEIGHT_LOG2[plane_sz] as u32; + let sub_x = if plane > 0 { ssx } else { 0 }; + let sub_y = if plane > 0 { ssy } else { 0 }; + let base_x = (self.b.mi_col >> sub_x) * 4; + let base_y = (self.b.mi_row >> sub_y) * 4; + let mut cand_row = (self.b.mi_row >> sub_y) << sub_y; + let mut cand_col = (self.b.mi_col >> sub_x) << sub_x; + if is_inter_intra { + let mode = match self.b.interintra_mode { + II_DC_PRED => DC_PRED, + II_V_PRED => V_PRED, + II_H_PRED => H_PRED, + _ => SMOOTH_PRED, + }; + let sbr = (sub_row >> sub_y) as isize; + let sbc = (sub_col >> sub_x) as isize; + let max_x = ((self.fs.fh.mi_cols as usize * 4) >> sub_x) as i32 - 1; + let max_y = ((self.fs.fh.mi_rows as usize * 4) >> sub_y) as i32 - 1; + let params = IntraParams { + plane, + x: base_x, + y: base_y, + have_left: if plane == 0 { self.b.avail_l } else { self.b.avail_l_chroma }, + have_above: if plane == 0 { self.b.avail_u } else { self.b.avail_u_chroma }, + have_above_right: self.block_decoded_at(plane, sbr - 1, sbc + n4w as isize), + have_below_left: self.block_decoded_at(plane, sbr + n4h as isize, sbc - 1), + mode, + log2w, + log2h, + max_x, + max_y, + bit_depth: self.fs.bit_depth, + angle_delta: 0, + use_filter_intra: false, + filter_intra_mode: 0, + enable_intra_edge_filter: self.fs.seq.enable_intra_edge_filter, + filter_type: if is_directional_mode(mode) && self.fs.seq.enable_intra_edge_filter { self.get_filter_type(plane) } else { false }, + }; + predict_intra(&mut self.t.cur.planes[plane], ¶ms); + } + let mut pred_w = block_width(self.b.mi_size) >> sub_x; + let mut pred_h = block_height(self.b.mi_size) >> sub_y; + let mut some_use_intra = false; + for r in 0..(n4h << sub_y) { + for c in 0..(n4w << sub_x) { + let (rr, cc) = (cand_row + r, cand_col + c); + if rr < self.t.mi.rows && cc < self.t.mi.cols && self.t.mi.ref_frame[self.t.mi.idx(rr, cc)][0] == INTRA_FRAME as i8 { + some_use_intra = true; + } + } + } + if some_use_intra { + pred_w = n4w * 4; + pred_h = n4h * 4; + cand_row = self.b.mi_row; + cand_col = self.b.mi_col; + } + let mut r = 0; + let mut y = 0; + while y < n4h * 4 { + let mut c = 0; + let mut x = 0; + while x < n4w * 4 { + self.predict_inter(plane, base_x + x, base_y + y, pred_w, pred_h, cand_row + r, cand_col + c); + x += pred_w; + c += 1; + } + y += pred_h; + r += 1; + } + } + Ok(()) + } + + fn get_tx_size(&self, plane: usize, tx_sz: usize) -> usize { + if plane == 0 { + return tx_sz; + } + let uv_tx = MAX_TX_SIZE_RECT[self.fs.plane_residual_size(self.b.mi_size, plane)] as usize; + if TX_WIDTH[uv_tx] == 64 || TX_HEIGHT[uv_tx] == 64 { + if TX_WIDTH[uv_tx] == 16 { + return TX_16X32; + } + if TX_HEIGHT[uv_tx] == 16 { + return TX_32X16; + } + return TX_32X32; + } + uv_tx + } + + fn residual(&mut self) -> Result<()> { + let sb_mask = if self.fs.seq.use_128x128_superblock { 31 } else { 15 }; + let ms = self.b.mi_size; + let width_chunks = (block_width(ms) >> 6).max(1); + let height_chunks = (block_height(ms) >> 6).max(1); + let mi_size_chunk = if width_chunks > 1 || height_chunks > 1 { BLOCK_64X64 } else { ms }; + let (ssx, ssy) = (self.fs.ssx, self.fs.ssy); + for chunk_y in 0..height_chunks { + for chunk_x in 0..width_chunks { + let mi_row_chunk = self.b.mi_row + (chunk_y << 4); + let mi_col_chunk = self.b.mi_col + (chunk_x << 4); + let _ = (mi_row_chunk & sb_mask, mi_col_chunk & sb_mask); + for plane in 0..(1 + 2 * self.b.has_chroma as usize) { + let tx_sz = if self.b.lossless { TX_4X4 } else { self.get_tx_size(plane, self.b.tx_size) }; + let step_x = TX_WIDTH[tx_sz] as usize >> 2; + let step_y = TX_HEIGHT[tx_sz] as usize >> 2; + let plane_sz = self.fs.plane_residual_size(mi_size_chunk, plane); + let num4x4_w = NUM_4X4_BLOCKS_WIDE[plane_sz] as usize; + let num4x4_h = NUM_4X4_BLOCKS_HIGH[plane_sz] as usize; + let sub_x = if plane > 0 { ssx } else { 0 }; + let sub_y = if plane > 0 { ssy } else { 0 }; + let base_x = (mi_col_chunk >> sub_x) * 4; + let base_y = (mi_row_chunk >> sub_y) * 4; + if self.b.is_inter && !self.b.lossless && plane == 0 { + self.transform_tree(base_x, base_y, num4x4_w * 4, num4x4_h * 4)?; + } else { + let base_x_block = (self.b.mi_col >> sub_x) * 4; + let base_y_block = (self.b.mi_row >> sub_y) * 4; + let mut y = 0; + while y < num4x4_h { + let mut x = 0; + while x < num4x4_w { + self.transform_block( + plane, + base_x_block, + base_y_block, + tx_sz, + x + ((chunk_x << 4) >> sub_x), + y + ((chunk_y << 4) >> sub_y), + )?; + x += step_x; + } + y += step_y; + } + } + } + } + } + Ok(()) + } + + fn transform_tree(&mut self, start_x: usize, start_y: usize, w: usize, h: usize) -> Result<()> { + let max_x = self.fs.fh.mi_cols as usize * 4; + let max_y = self.fs.fh.mi_rows as usize * 4; + if start_x >= max_x || start_y >= max_y { + return Ok(()); + } + let row = start_y >> 2; + let col = start_x >> 2; + let mi = &self.t.mi; + let luma_tx = mi.inter_tx_size[mi.idx(row, col)] as usize; + let lw = TX_WIDTH[luma_tx] as usize; + let lh = TX_HEIGHT[luma_tx] as usize; + if w <= lw && h <= lh { + let tx_sz = find_tx_size(w, h); + self.transform_block(0, start_x, start_y, tx_sz, 0, 0)?; + } else if w > h { + self.transform_tree(start_x, start_y, w / 2, h)?; + self.transform_tree(start_x + w / 2, start_y, w / 2, h)?; + } else if w < h { + self.transform_tree(start_x, start_y, w, h / 2)?; + self.transform_tree(start_x, start_y + h / 2, w, h / 2)?; + } else { + self.transform_tree(start_x, start_y, w / 2, h / 2)?; + self.transform_tree(start_x + w / 2, start_y, w / 2, h / 2)?; + self.transform_tree(start_x, start_y + h / 2, w / 2, h / 2)?; + self.transform_tree(start_x + w / 2, start_y + h / 2, w / 2, h / 2)?; + } + Ok(()) + } + + fn transform_block(&mut self, plane: usize, base_x: usize, base_y: usize, tx_sz: usize, x: usize, y: usize) -> Result<()> { + let start_x = base_x + 4 * x; + let start_y = base_y + 4 * y; + let (ssx, ssy) = (self.fs.ssx, self.fs.ssy); + let sub_x = if plane > 0 { ssx } else { 0 }; + let sub_y = if plane > 0 { ssy } else { 0 }; + let row = (start_y << sub_y) >> 2; + let col = (start_x << sub_x) >> 2; + let sb_mask = if self.fs.seq.use_128x128_superblock { 31 } else { 15 }; + let sub_block_mi_row = row & sb_mask; + let sub_block_mi_col = col & sb_mask; + let step_x = TX_WIDTH[tx_sz] as usize >> 2; + let step_y = TX_HEIGHT[tx_sz] as usize >> 2; + let max_x = (self.fs.fh.mi_cols as usize * 4) >> sub_x; + let max_y = (self.fs.fh.mi_rows as usize * 4) >> sub_y; + if start_x >= max_x || start_y >= max_y { + return Ok(()); + } + if !self.b.is_inter { + let palette = if plane == 0 { self.b.palette_size_y } else { self.b.palette_size_uv }; + if palette > 0 { + self.predict_palette(plane, start_x, start_y, x, y, tx_sz); + } else { + let is_cfl = plane > 0 && self.b.uv_mode == UV_CFL_PRED; + let mode = if plane == 0 { + self.b.y_mode + } else if is_cfl { + DC_PRED + } else { + self.b.uv_mode + }; + let log2w = TX_WIDTH_LOG2[tx_sz] as u32; + let log2h = TX_HEIGHT_LOG2[tx_sz] as u32; + let have_left = (if plane == 0 { self.b.avail_l } else { self.b.avail_l_chroma }) || x > 0; + let have_above = (if plane == 0 { self.b.avail_u } else { self.b.avail_u_chroma }) || y > 0; + let sbr = (sub_block_mi_row >> sub_y) as isize; + let sbc = (sub_block_mi_col >> sub_x) as isize; + let have_above_right = self.block_decoded_at(plane, sbr - 1, sbc + step_x as isize); + let have_below_left = self.block_decoded_at(plane, sbr + step_y as isize, sbc - 1); + let filter_type = if is_directional_mode(mode) && self.fs.seq.enable_intra_edge_filter { self.get_filter_type(plane) } else { false }; + let params = IntraParams { + plane, + x: start_x, + y: start_y, + have_left, + have_above, + have_above_right, + have_below_left, + mode, + log2w, + log2h, + max_x: max_x as i32 - 1, + max_y: max_y as i32 - 1, + bit_depth: self.fs.bit_depth, + angle_delta: if plane == 0 { self.b.angle_delta_y } else { self.b.angle_delta_uv }, + use_filter_intra: self.b.use_filter_intra, + filter_intra_mode: self.b.filter_intra_mode, + enable_intra_edge_filter: self.fs.seq.enable_intra_edge_filter, + filter_type, + }; + predict_intra(&mut self.t.cur.planes[plane], ¶ms); + if is_cfl { + self.predict_cfl(plane, start_x, start_y, tx_sz); + } + } + if plane == 0 { + self.b.max_luma_w = start_x + step_x * 4; + self.b.max_luma_h = start_y + step_y * 4; + } + } + if !self.b.skip { + let eob = self.coeffs(plane, start_x, start_y, tx_sz); + if eob > 0 { + self.reconstruct(plane, start_x, start_y, tx_sz); + } + } + for i in 0..step_y { + for j in 0..step_x { + let rr = (row >> sub_y) + i; + let cc = (col >> sub_x) + j; + self.t.set_lf_tx_size(plane, rr, cc, tx_sz as u8); + let by = (sub_block_mi_row >> sub_y) + i; + let bx = (sub_block_mi_col >> sub_x) + j; + if by + 1 < 35 && bx + 1 < 35 { + self.block_decoded[plane][by + 1][bx + 1] = true; + } + } + } + Ok(()) + } + + pub(crate) fn get_filter_type(&self, plane: usize) -> bool { + let (ssx, ssy) = (self.fs.ssx, self.fs.ssy); + let b = &self.b; + let mi = &self.t.mi; + let is_smooth = |row: usize, col: usize| -> bool { + let i = mi.idx(row, col); + let mode = if plane == 0 { + mi.y_mode[i] as usize + } else { + if mi.ref_frame[i][0] > INTRA_FRAME as i8 { + return false; + } + mi.uv_mode[i] as usize + }; + mode == SMOOTH_PRED || mode == SMOOTH_V_PRED || mode == SMOOTH_H_PRED + }; + let mut above_smooth = false; + let mut left_smooth = false; + if if plane == 0 { b.avail_u } else { b.avail_u_chroma } { + let mut r = b.mi_row as isize - 1; + let mut c = b.mi_col; + if plane > 0 { + if ssx == 1 && (b.mi_col & 1) == 0 { + c += 1; + } + if ssy == 1 && (b.mi_row & 1) == 1 { + r -= 1; + } + } + above_smooth = is_smooth(r as usize, c); + } + if if plane == 0 { b.avail_l } else { b.avail_l_chroma } { + let mut r = b.mi_row; + let mut c = b.mi_col as isize - 1; + if plane > 0 { + if ssx == 1 && (b.mi_col & 1) == 1 { + c -= 1; + } + if ssy == 1 && (b.mi_row & 1) == 0 { + r += 1; + } + } + left_smooth = is_smooth(r, c as usize); + } + above_smooth || left_smooth + } + + fn predict_palette(&mut self, plane: usize, start_x: usize, start_y: usize, x: usize, y: usize, tx_sz: usize) { + let w = TX_WIDTH[tx_sz] as usize; + let h = TX_HEIGHT[tx_sz] as usize; + let palette = match plane { + 0 => self.b.palette_colors_y, + 1 => self.b.palette_colors_u, + _ => self.b.palette_colors_v, + }; + let map = if plane == 0 { &self.color_map_y } else { &self.color_map_uv }; + let pl = &mut self.t.cur.planes[plane]; + for i in 0..h { + for j in 0..w { + let idx = map[(y * 4 + i) * 64 + x * 4 + j] as usize; + pl.set(start_x + j, start_y + i, palette[idx]); + } + } + } + + fn predict_cfl(&mut self, plane: usize, start_x: usize, start_y: usize, tx_sz: usize) { + let w = TX_WIDTH[tx_sz] as usize; + let h = TX_HEIGHT[tx_sz] as usize; + let (sub_x, sub_y) = (self.fs.ssx, self.fs.ssy); + let alpha = if plane == 1 { self.b.cfl_alpha_u } else { self.b.cfl_alpha_v }; + let bd = self.fs.bit_depth; + let mut l = [0i32; 32 * 32]; + let mut avg: i64 = 0; + { + let luma = &self.t.cur.planes[0]; + for i in 0..h { + let ly = ((start_y + i) << sub_y).min(self.b.max_luma_h - (1 << sub_y)); + for j in 0..w { + let lx = ((start_x + j) << sub_x).min(self.b.max_luma_w - (1 << sub_x)); + let mut t = 0i32; + for dy in 0..=sub_y { + for dx in 0..=sub_x { + t += luma.at(lx + dx, ly + dy) as i32; + } + } + let v = t << (3 - sub_x - sub_y); + l[i * w + j] = v; + avg += v as i64; + } + } + } + let shift = TX_WIDTH_LOG2[tx_sz] as u32 + TX_HEIGHT_LOG2[tx_sz] as u32; + let avg = ((avg + (1i64 << (shift - 1))) >> shift) as i32; + let pl = &mut self.t.cur.planes[plane]; + for i in 0..h { + for j in 0..w { + let dc = pl.at(start_x + j, start_y + i) as i32; + let scaled = round2_signed(alpha * (l[i * w + j] - avg), 6); + pl.set(start_x + j, start_y + i, (dc + scaled).clamp(0, (1 << bd) - 1) as u16); + } + } + } + + // ---- coefficients ----------------------------------------------------------------------- + + fn get_tx_set(&self, tx_sz: usize) -> usize { + let sqr = TX_SIZE_SQR[tx_sz] as usize; + let sqr_up = TX_SIZE_SQR_UP[tx_sz] as usize; + if sqr_up > TX_32X32 { + return TX_SET_DCTONLY; + } + let reduced = self.fs.fh.reduced_tx_set; + if self.b.is_inter { + if reduced || sqr_up == TX_32X32 { + TX_SET_INTER_3 + } else if sqr == TX_16X16 { + TX_SET_INTER_2 + } else { + TX_SET_INTER_1 + } + } else if sqr_up == TX_32X32 { + TX_SET_DCTONLY + } else if reduced || sqr == TX_16X16 { + TX_SET_INTRA_2 + } else { + TX_SET_INTRA_1 + } + } + + fn is_tx_type_in_set(&self, set: usize, tx_type: usize) -> bool { + if self.b.is_inter { TX_TYPE_IN_SET_INTER[set][tx_type] == 1 } else { TX_TYPE_IN_SET_INTRA[set][tx_type] == 1 } + } + + fn compute_tx_type(&self, plane: usize, tx_sz: usize, block_x: usize, block_y: usize) -> usize { + let sqr_up = TX_SIZE_SQR_UP[tx_sz] as usize; + if self.b.lossless || sqr_up > TX_32X32 { + return DCT_DCT; + } + let set = self.get_tx_set(tx_sz); + let mi = &self.t.mi; + if plane == 0 { + return mi.tx_type[mi.idx(block_y, block_x)] as usize; + } + if self.b.is_inter { + let x4 = self.b.mi_col.max(block_x << self.fs.ssx); + let y4 = self.b.mi_row.max(block_y << self.fs.ssy); + let t = mi.tx_type[mi.idx(y4, x4)] as usize; + if !self.is_tx_type_in_set(set, t) { + return DCT_DCT; + } + return t; + } + let t = MODE_TO_TXFM[self.b.uv_mode] as usize; + if !self.is_tx_type_in_set(set, t) { + return DCT_DCT; + } + t + } + + fn transform_type(&mut self, x4: usize, y4: usize, tx_sz: usize) { + let set = self.get_tx_set(tx_sz); + let qidx = if self.fs.fh.seg.enabled { + self.fs.fh.get_qindex(true, self.b.segment_id, self.current_q_index) + } else { + self.fs.fh.quant.base_q_idx as i32 + }; + let tx_type = if set > 0 && qidx > 0 { + let sqr = TX_SIZE_SQR[tx_sz] as usize; + if self.b.is_inter { + match set { + TX_SET_INTER_1 => TX_TYPE_INTER_INV_SET1[self.sd.read_symbol(&mut self.cdf.inter_tx_type_set1[sqr])] as usize, + TX_SET_INTER_2 => TX_TYPE_INTER_INV_SET2[self.sd.read_symbol(&mut self.cdf.inter_tx_type_set2)] as usize, + _ => TX_TYPE_INTER_INV_SET3[self.sd.read_symbol(&mut self.cdf.inter_tx_type_set3[sqr])] as usize, + } + } else { + let intra_dir = + if self.b.use_filter_intra { FILTER_INTRA_MODE_TO_INTRA_DIR[self.b.filter_intra_mode] as usize } else { self.b.y_mode }; + if set == TX_SET_INTRA_1 { + TX_TYPE_INTRA_INV_SET1[self.sd.read_symbol(&mut self.cdf.intra_tx_type_set1[sqr][intra_dir])] as usize + } else { + TX_TYPE_INTRA_INV_SET2[self.sd.read_symbol(&mut self.cdf.intra_tx_type_set2[sqr][intra_dir])] as usize + } + } + } else { + DCT_DCT + }; + self.set_tx_types(x4, y4, tx_sz, tx_type); + } + + fn set_tx_types(&mut self, x4: usize, y4: usize, tx_sz: usize, tx_type: usize) { + let mi = &mut self.t.mi; + 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 < mi.rows && x4 + i < mi.cols { + let k = mi.idx(y4 + j, x4 + i); + mi.tx_type[k] = tx_type as u8; + } + } + } + } + + fn get_scan(&self, tx_sz: usize) -> &'static [u16] { + if tx_sz == TX_16X64 { + return &DEFAULT_SCAN_16X32; + } + if tx_sz == TX_64X16 { + return &DEFAULT_SCAN_32X16; + } + if TX_SIZE_SQR_UP[tx_sz] as usize == TX_64X64 { + return &DEFAULT_SCAN_32X32; + } + let t = self.plane_tx_type; + if t == IDTX { + return default_scan(tx_sz); + } + let prefer_row = t == V_DCT || t == V_ADST || t == V_FLIPADST; + let prefer_col = t == H_DCT || t == H_ADST || t == H_FLIPADST; + if prefer_row { + match tx_sz { + TX_4X4 => &MROW_SCAN_4X4, + TX_4X8 => &MROW_SCAN_4X8, + TX_8X4 => &MROW_SCAN_8X4, + TX_8X8 => &MROW_SCAN_8X8, + TX_8X16 => &MROW_SCAN_8X16, + TX_16X8 => &MROW_SCAN_16X8, + TX_16X16 => &MROW_SCAN_16X16, + TX_4X16 => &MROW_SCAN_4X16, + _ => &MROW_SCAN_16X4, + } + } else if prefer_col { + match tx_sz { + TX_4X4 => &MCOL_SCAN_4X4, + TX_4X8 => &MCOL_SCAN_4X8, + TX_8X4 => &MCOL_SCAN_8X4, + TX_8X8 => &MCOL_SCAN_8X8, + TX_8X16 => &MCOL_SCAN_8X16, + TX_16X8 => &MCOL_SCAN_16X8, + TX_16X16 => &MCOL_SCAN_16X16, + TX_4X16 => &MCOL_SCAN_4X16, + _ => &MCOL_SCAN_16X4, + } + } else { + default_scan(tx_sz) + } + } + + /// coeffs( ): returns eob. + fn coeffs(&mut self, plane: usize, start_x: usize, start_y: usize, tx_sz: usize) -> usize { + 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 seg_eob = if tx_sz == TX_16X64 || tx_sz == TX_64X16 { 512 } else { 1024.min(TX_WIDTH[tx_sz] as usize * TX_HEIGHT[tx_sz] as usize) }; + self.quant[..seg_eob].iter_mut().for_each(|q| *q = 0); + let mut eob = 0usize; + let mut cul_level = 0u32; + let mut dc_category = 0u8; + let ctx = self.all_zero_ctx(plane, tx_sz, x4, y4, w4, h4); + let all_zero = self.sd.read_symbol(&mut self.cdf.txb_skip[tx_sz_ctx][ctx]) == 1; + if all_zero { + if plane == 0 { + self.set_tx_types(x4, y4, tx_sz, DCT_DCT); + } + } else { + if plane == 0 { + self.transform_type(x4, y4, tx_sz); + } + self.plane_tx_type = self.compute_tx_type(plane, tx_sz, x4, y4); + let scan = self.get_scan(tx_sz); + let eob_multisize = (TX_WIDTH_LOG2[tx_sz] as usize).min(5) + (TX_HEIGHT_LOG2[tx_sz] as usize).min(5) - 4; + let tx_class = get_tx_class(self.plane_tx_type); + let ectx = (tx_class != TX_CLASS_2D) as usize; + let eob_pt = 1 + match eob_multisize { + 0 => self.sd.read_symbol(&mut self.cdf.eob_pt_16[ptype][ectx]), + 1 => self.sd.read_symbol(&mut self.cdf.eob_pt_32[ptype][ectx]), + 2 => self.sd.read_symbol(&mut self.cdf.eob_pt_64[ptype][ectx]), + 3 => self.sd.read_symbol(&mut self.cdf.eob_pt_128[ptype][ectx]), + 4 => self.sd.read_symbol(&mut self.cdf.eob_pt_256[ptype][ectx]), + 5 => self.sd.read_symbol(&mut self.cdf.eob_pt_512[ptype]), + _ => self.sd.read_symbol(&mut self.cdf.eob_pt_1024[ptype]), + }; + eob = if eob_pt < 2 { eob_pt } else { (1 << (eob_pt - 2)) + 1 }; + let eob_shift = eob_pt as i32 - 3; + if eob_shift >= 0 { + let extra = self.sd.read_symbol(&mut self.cdf.eob_extra[tx_sz_ctx][ptype][eob_pt - 3]); + if extra == 1 { + eob += 1 << eob_shift; + } + for i in 1..(eob_pt as i32 - 2).max(0) { + let sh = (eob_pt as i32 - 2).max(0) - 1 - i; + if self.sd.read_literal(1) == 1 { + eob += 1 << sh; + } + } + } + let adj = ADJUSTED_TX_SIZE[tx_sz] as usize; + let bwl = TX_WIDTH_LOG2[adj] as usize; + let height = TX_HEIGHT[adj] as usize; + let width = 1usize << bwl; + // Levels with TX_PAD zero columns / rows past the right / bottom edge, so the + // neighbourhood sums need no bounds checks (out-of-range neighbours count as 0). + let stride = width + TX_PAD; + let mut lev = std::mem::take(&mut self.levels); + lev[..(height + TX_PAD) * stride].fill(0); + let sig = &SIG_REF_DIFF_OFFSET[tx_class]; + let sig_off: [usize; SIG_REF_DIFF_OFFSET_NUM] = std::array::from_fn(|i| sig[i][0] as usize * stride + sig[i][1] as usize); + let magr = &MAG_REF_OFFSET_WITH_TX_CLASS[tx_class]; + let mag_off: [usize; 3] = std::array::from_fn(|i| magr[i][0] as usize * stride + magr[i][1] as usize); + for c in (0..eob).rev() { + let pos = scan[c] as usize; + let row = pos >> bwl; + let col = pos & (width - 1); + let li = row * stride + col; + let mut level = if c == eob - 1 { + let ctx = coeff_base_eob_ctx(c, bwl, height); + self.sd.read_symbol(&mut self.cdf.coeff_base_eob[tx_sz_ctx][ptype][ctx]) as i32 + 1 + } else { + let mut mag = 0u32; + for o in sig_off { + mag += (lev[li + o] as u32).min(3); + } + let ctx = coeff_base_ctx(tx_sz, tx_class, row, col, mag); + self.sd.read_symbol(&mut self.cdf.coeff_base[tx_sz_ctx][ptype][ctx]) as i32 + }; + if level > NUM_BASE_LEVELS as i32 { + let mut mag = 0u32; + for o in mag_off { + mag += (lev[li + o] as u32).min((COEFF_BASE_RANGE + NUM_BASE_LEVELS + 1) as u32); + } + let ctx = coeff_br_ctx(tx_class, row, col, pos, mag); + for _ in 0..(COEFF_BASE_RANGE / (BR_CDF_SIZE - 1)) { + let br = self.sd.read_symbol(&mut self.cdf.coeff_br[tx_sz_ctx.min(TX_32X32)][ptype][ctx]) as i32; + level += br; + if br < (BR_CDF_SIZE - 1) as i32 { + break; + } + } + } + self.quant[pos] = level; + lev[li] = level as u8; + } + self.levels = lev; + for c in 0..eob { + let pos = scan[c] as usize; + let sign = if self.quant[pos] != 0 { + if c == 0 { + let ctx = self.dc_sign_ctx(plane, x4, y4, w4, h4); + self.sd.read_symbol(&mut self.cdf.dc_sign[ptype][ctx]) == 1 + } else { + self.sd.read_literal(1) == 1 + } + } else { + false + }; + if self.quant[pos] > (NUM_BASE_LEVELS + COEFF_BASE_RANGE) as i32 { + let mut length = 0; + loop { + length += 1; + if self.sd.read_literal(1) == 1 { + break; + } + if length > 32 { + break; + } + } + let mut x: u32 = 1; + for _ in (0..length - 1).rev() { + x = (x << 1) | self.sd.read_literal(1); + } + self.quant[pos] = (x as i64 + COEFF_BASE_RANGE as i64 + NUM_BASE_LEVELS as i64).min(i32::MAX as i64) as i32; + } + if pos == 0 && self.quant[pos] > 0 { + dc_category = if sign { 1 } else { 2 }; + } + self.quant[pos] &= 0xFFFFF; + cul_level += self.quant[pos] as u32; + if sign { + self.quant[pos] = -self.quant[pos]; + } + } + cul_level = cul_level.min(63); + } + for i in 0..w4 { + if x4 + i < self.t.above_level[plane].len() { + self.t.above_level[plane][x4 + i] = cul_level as u8; + self.t.above_dc[plane][x4 + i] = dc_category; + } + } + for i in 0..h4 { + if y4 + i < self.t.left_level[plane].len() { + self.t.left_level[plane][y4 + i] = cul_level as u8; + self.t.left_dc[plane][y4 + i] = dc_category; + } + } + eob + } + + fn all_zero_ctx(&self, plane: usize, tx_sz: usize, x4: usize, y4: usize, w4: usize, h4: usize) -> usize { + let mut max_x4 = self.fs.fh.mi_cols as usize; + let mut max_y4 = self.fs.fh.mi_rows as usize; + if plane > 0 { + max_x4 >>= self.fs.ssx; + max_y4 >>= self.fs.ssy; + } + let w = TX_WIDTH[tx_sz] as usize; + let h = TX_HEIGHT[tx_sz] as usize; + let bsize = self.fs.plane_residual_size(self.b.mi_size, plane); + let bw = block_width(bsize); + let bh = block_height(bsize); + let al = &self.t.above_level[plane]; + let ll = &self.t.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.t.above_dc[plane]; + let ld = &self.t.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]; + above |= ad[x4 + i]; + } + } + for i in 0..h4 { + if y4 + i < max_y4 { + left |= ll[y4 + i]; + left |= ld[y4 + i]; + } + } + let mut ctx = (above != 0) as usize + (left != 0) as usize; + ctx += 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 mut max_x4 = self.fs.fh.mi_cols as usize; + let mut max_y4 = self.fs.fh.mi_rows as usize; + if plane > 0 { + max_x4 >>= self.fs.ssx; + max_y4 >>= self.fs.ssy; + } + let mut dc_sign = 0i32; + for k in 0..w4 { + if x4 + k < max_x4 { + match self.t.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.t.left_dc[plane][y4 + k] { + 1 => dc_sign -= 1, + 2 => dc_sign += 1, + _ => {} + } + } + } + match dc_sign.signum() { + -1 => 1, + 1 => 2, + _ => 0, + } + } + + /// Reconstruct process (7.12.3). + fn reconstruct(&mut self, plane: usize, x: usize, y: usize, tx_sz: usize) { + let dq_denom: i64 = match tx_sz { + TX_32X32 | TX_16X32 | TX_32X16 | TX_16X64 | TX_64X16 => 2, + TX_64X64 | TX_32X64 | TX_64X32 => 4, + _ => 1, + }; + let log2w = TX_WIDTH_LOG2[tx_sz] as u32; + let log2h = TX_HEIGHT_LOG2[tx_sz] as u32; + let w = 1usize << log2w; + let h = 1usize << log2h; + let tw = w.min(32); + let th = h.min(32); + let t = self.plane_tx_type; + let flip_ud = matches!(t, FLIPADST_DCT | FLIPADST_ADST | V_FLIPADST | FLIPADST_FLIPADST); + let flip_lr = matches!(t, DCT_FLIPADST | ADST_FLIPADST | H_FLIPADST | FLIPADST_FLIPADST); + let bd = self.fs.bit_depth; + let fh = &self.fs.fh; + let qindex = fh.get_qindex(false, self.b.segment_id, self.current_q_index); + let (dc_delta, ac_delta) = match plane { + 0 => (fh.quant.delta_q_y_dc, 0), + 1 => (fh.quant.delta_q_u_dc, fh.quant.delta_q_u_ac), + _ => (fh.quant.delta_q_v_dc, fh.quant.delta_q_v_ac), + }; + let bd_idx = ((bd - 8) >> 1) as usize; + let dc_q = DC_QLOOKUP[bd_idx][(qindex + dc_delta).clamp(0, 255) as usize] as i64; + let ac_q = AC_QLOOKUP[bd_idx][(qindex + ac_delta).clamp(0, 255) as usize] as i64; + let qm_level = if fh.quant.using_qmatrix { fh.seg_qm_level[plane][self.b.segment_id] } else { 15 }; + let use_qm = fh.quant.using_qmatrix && t < IDTX && qm_level < 15; + let lim = 1i64 << (7 + bd); + // inverse_transform_2d reads only the top-left tw x th coefficients (stride 64) + for i in 0..th { + self.dequant[i * 64..i * 64 + tw].fill(0); + } + for i in 0..th { + for j in 0..tw { + let qv = self.quant[i * tw + j]; + if qv == 0 { + continue; + } + let q = if i == 0 && j == 0 { dc_q } else { ac_q }; + let q2 = if use_qm { + let m = QUANTIZER_MATRIX[qm_level as usize][(plane > 0) as usize][QM_OFFSET[tx_sz] as usize + i * tw + j] as i64; + (q * m + 16) >> 5 + } else { + q + }; + let dq = qv as i64 * q2; + let mag = (dq.abs() & 0xFFFFFF) / dq_denom; + let dq2 = if dq < 0 { -mag } else { mag }; + self.dequant[i * 64 + j] = dq2.clamp(-lim, lim - 1) as i32; + } + } + let lossless = self.b.lossless; + inverse_transform_2d(&self.dequant, &mut self.residual, tx_sz, t, lossless, bd); + let pl = &mut self.t.cur.planes[plane]; + let max = (1i32 << bd) - 1; + for i in 0..h { + let yy = if flip_ud { h - i - 1 } else { i }; + let row = &mut pl.row_from_mut(y + yy, x)[..w]; + let res = &self.residual[i * w..i * w + w]; + if flip_lr { + for (o, &r) in row.iter_mut().rev().zip(res) { + *o = (*o as i32 + r).max(0).min(max) as u16; + } + } else { + for (o, &r) in row.iter_mut().zip(res) { + *o = (*o as i32 + r).max(0).min(max) as u16; + } + } + } + } + + // ---- loop restoration syntax ------------------------------------------------------------- + + fn read_lr(&mut self, r: usize, c: usize, bsize: usize) { + if self.fs.fh.allow_intrabc { + return; + } + let w = NUM_4X4_BLOCKS_WIDE[bsize] as usize; + let h = NUM_4X4_BLOCKS_HIGH[bsize] as usize; + for plane in 0..self.fs.num_planes { + if self.fs.fh.lr.frame_restoration_type[plane] == RESTORE_NONE as u8 { + continue; + } + let sub_x = if plane == 0 { 0 } else { self.fs.ssx }; + let sub_y = if plane == 0 { 0 } else { self.fs.ssy }; + let unit_size = self.fs.fh.lr.loop_restoration_size[plane] as usize; + let (unit_rows, unit_cols) = (self.fs.lr_unit_rows[plane], self.fs.lr_unit_cols[plane]); + let unit_row_start = (r * (4 >> sub_y)).div_ceil(unit_size); + let unit_row_end = unit_rows.min(((r + h) * (4 >> sub_y)).div_ceil(unit_size)); + let (numerator, denominator) = if self.fs.fh.use_superres { + ((4 >> sub_x) * self.fs.fh.superres_denom as usize, unit_size * SUPERRES_NUM) + } else { + (4 >> sub_x, unit_size) + }; + let unit_col_start = (c * numerator).div_ceil(denominator); + let unit_col_end = unit_cols.min(((c + w) * numerator).div_ceil(denominator)); + for unit_row in unit_row_start..unit_row_end { + for unit_col in unit_col_start..unit_col_end { + self.read_lr_unit(plane, unit_row, unit_col); + } + } + } + } + + fn read_lr_unit(&mut self, plane: usize, unit_row: usize, unit_col: usize) { + let frt = self.fs.fh.lr.frame_restoration_type[plane]; + let rtype = if frt == RESTORE_WIENER as u8 { + if self.sd.read_symbol(&mut self.cdf.use_wiener) == 1 { RESTORE_WIENER as u8 } else { RESTORE_NONE as u8 } + } else if frt == RESTORE_SGRPROJ as u8 { + if self.sd.read_symbol(&mut self.cdf.use_sgrproj) == 1 { RESTORE_SGRPROJ as u8 } else { RESTORE_NONE as u8 } + } else { + // restoration_type symbol: 0 = NONE, 1 = WIENER, 2 = SGRPROJ + match self.sd.read_symbol(&mut self.cdf.restoration_type) { + 0 => RESTORE_NONE as u8, + 1 => RESTORE_WIENER as u8, + _ => RESTORE_SGRPROJ as u8, + } + }; + let idx = unit_row * self.fs.lr_unit_cols[plane] + unit_col; + self.t.lr.lr_type[plane][idx] = rtype; + self.t.lr_written[plane].push(idx); + if rtype == RESTORE_WIENER as u8 { + const MIN: [i32; 3] = [-5, -23, -17]; + const MAX: [i32; 3] = [10, 8, 46]; + const K: [u32; 3] = [1, 2, 3]; + for pass in 0..2 { + let first = if plane > 0 { + self.t.lr.lr_wiener[plane][idx][pass][0] = 0; + 1 + } else { + 0 + }; + for j in first..3 { + let v = self.decode_signed_subexp_with_ref_bool(MIN[j], MAX[j] + 1, K[j], self.ref_lr_wiener[plane][pass][j]); + self.t.lr.lr_wiener[plane][idx][pass][j] = v as i8; + self.ref_lr_wiener[plane][pass][j] = v; + } + } + } else if rtype == RESTORE_SGRPROJ as u8 { + let set = self.sd.read_literal(SGRPROJ_PARAMS_BITS as u32) as usize; + self.t.lr.lr_sgr_set[plane][idx] = set as u8; + const MIN: [i32; 2] = [-96, -32]; + const MAX: [i32; 2] = [31, 95]; + for i in 0..2 { + let radius = SGR_PARAMS[set][i * 2]; + let v = if radius != 0 { + self.decode_signed_subexp_with_ref_bool(MIN[i], MAX[i] + 1, SGRPROJ_PRJ_SUBEXP_K as u32, self.ref_sgr_xqd[plane][i]) + } else if i == 1 { + ((1 << SGRPROJ_PRJ_BITS) - self.ref_sgr_xqd[plane][0]).clamp(MIN[i], MAX[i]) + } else { + 0 + }; + self.t.lr.lr_sgr_xqd[plane][idx][i] = v as i8; + self.ref_sgr_xqd[plane][i] = v; + } + } + } + + fn decode_signed_subexp_with_ref_bool(&mut self, low: i32, high: i32, k: u32, r: i32) -> i32 { + let x = self.decode_unsigned_subexp_with_ref_bool(high - low, k, r - low); + x + low + } + + fn decode_unsigned_subexp_with_ref_bool(&mut self, mx: i32, k: u32, r: i32) -> i32 { + let v = self.decode_subexp_bool(mx, k); + if (r << 1) <= mx { inverse_recenter(r, v) } else { mx - 1 - inverse_recenter(mx - 1 - r, v) } + } + + fn decode_subexp_bool(&mut self, num_syms: i32, k: u32) -> i32 { + let mut i = 0u32; + let mut mk = 0i32; + loop { + let b2 = if i > 0 { k + i - 1 } else { k }; + let a = 1i32 << b2; + if num_syms <= mk + 3 * a { + return self.sd.read_ns((num_syms - mk) as u32) as i32 + mk; + } else if self.sd.read_literal(1) == 1 { + i += 1; + mk += a; + } else { + return self.sd.read_literal(b2) as i32 + mk; + } + } + } + + fn inter_frame_mode_info(&mut self) -> Result<()> { + self.inter_frame_mode_info_impl() + } +} + +fn inverse_recenter(r: i32, v: i32) -> i32 { + if v > 2 * r { + v + } else if v & 1 == 1 { + r - ((v + 1) >> 1) + } else { + r + (v >> 1) + } +} + +fn default_scan(tx_sz: usize) -> &'static [u16] { + match tx_sz { + TX_4X4 => &DEFAULT_SCAN_4X4, + TX_4X8 => &DEFAULT_SCAN_4X8, + TX_8X4 => &DEFAULT_SCAN_8X4, + TX_8X8 => &DEFAULT_SCAN_8X8, + TX_8X16 => &DEFAULT_SCAN_8X16, + TX_16X8 => &DEFAULT_SCAN_16X8, + TX_16X16 => &DEFAULT_SCAN_16X16, + TX_16X32 => &DEFAULT_SCAN_16X32, + TX_32X16 => &DEFAULT_SCAN_32X16, + TX_4X16 => &DEFAULT_SCAN_4X16, + TX_16X4 => &DEFAULT_SCAN_16X4, + TX_8X32 => &DEFAULT_SCAN_8X32, + TX_32X8 => &DEFAULT_SCAN_32X8, + _ => &DEFAULT_SCAN_32X32, + } +} + +fn get_tx_class(t: usize) -> usize { + if t == V_DCT || t == V_ADST || t == V_FLIPADST { + TX_CLASS_VERT + } else if t == H_DCT || t == H_ADST || t == H_FLIPADST { + TX_CLASS_HORIZ + } else { + TX_CLASS_2D + } +} + +/// Coefficient base context from the neighbourhood magnitude `mag` (get_coeff_base_ctx). +#[inline(always)] +fn coeff_base_ctx(tx_sz: usize, tx_class: usize, row: usize, col: usize, mag: u32) -> usize { + let ctx = ((mag + 1) >> 1).min(4) as usize; + if tx_class == TX_CLASS_2D { + if row == 0 && col == 0 { + return 0; + } + return ctx + COEFF_BASE_CTX_OFFSET[tx_sz][row.min(4)][col.min(4)] as usize; + } + let idx = if tx_class == TX_CLASS_VERT { row } else { col }; + ctx + COEFF_BASE_POS_CTX_OFFSET[idx.min(2)] as usize +} + +/// coeff_br context from the neighbourhood magnitude `mag`. +#[inline(always)] +fn coeff_br_ctx(tx_class: usize, row: usize, col: usize, pos: usize, mag: u32) -> usize { + let mag = ((mag + 1) >> 1).min(6) as usize; + if pos == 0 { + mag + } else if tx_class == 0 { + if row < 2 && col < 2 { mag + 7 } else { mag + 14 } + } else if tx_class == 1 { + if col == 0 { mag + 7 } else { mag + 14 } + } else if row == 0 { + mag + 7 + } else { + mag + 14 + } +} + +/// Zero padding (columns and rows) of the coefficient level buffer. +const TX_PAD: usize = 4; + +fn coeff_base_eob_ctx(c: usize, bwl: usize, height: usize) -> usize { + if c == 0 { + return 0; + } + if c <= (height << bwl) / 8 { + return 1; + } + if c <= (height << bwl) / 4 { + return 2; + } + 3 +} + +pub(crate) fn find_tx_size(w: usize, h: usize) -> usize { + (0..TX_SIZES_ALL).find(|&t| TX_WIDTH[t] as usize == w && TX_HEIGHT[t] as usize == h).unwrap_or(TX_4X4) +} + +fn ceil_log2(x: u32) -> u32 { + if x < 2 { + return 0; + } + let mut i = 1; + let mut p = 2; + while p < x { + i += 1; + p <<= 1; + } + i +} + +fn neg_deinterleave(diff: i32, r: i32, max: i32) -> i32 { + if r == 0 { + return diff; + } + if r >= max - 1 { + return max - diff - 1; + } + if 2 * r < max { + if diff <= 2 * r { + if diff & 1 == 1 { + return r + ((diff + 1) >> 1); + } else { + return r - (diff >> 1); + } + } + diff + } else { + if diff <= 2 * (max - r - 1) { + if diff & 1 == 1 { + return r + ((diff + 1) >> 1); + } else { + return r - (diff >> 1); + } + } + max - (diff + 1) + } +} + +/// get_palette_color_context( ): returns (ColorOrder, ColorContextHash). +fn palette_color_context(map: &[u8], r: usize, c: usize, n: usize) -> ([u8; 8], usize) { + let mut scores = [0i32; PALETTE_COLORS]; + let mut order = [0u8, 1, 2, 3, 4, 5, 6, 7]; + if c > 0 { + scores[map[r * 64 + c - 1] as usize] += 2; + } + if r > 0 && c > 0 { + scores[map[(r - 1) * 64 + c - 1] as usize] += 1; + } + if r > 0 { + scores[map[(r - 1) * 64 + c] as usize] += 2; + } + for i in 0..PALETTE_NUM_NEIGHBORS { + let mut max_score = scores[i]; + let mut max_idx = i; + for j in i + 1..n { + if scores[j] > max_score { + max_score = scores[j]; + max_idx = j; + } + } + if max_idx != i { + let max_score = scores[max_idx]; + let max_order = order[max_idx]; + for k in (i + 1..=max_idx).rev() { + scores[k] = scores[k - 1]; + order[k] = order[k - 1]; + } + scores[i] = max_score; + order[i] = max_order; + } + } + let mut hash = 0usize; + for i in 0..PALETTE_NUM_NEIGHBORS { + hash += scores[i] as usize * PALETTE_COLOR_HASH_MULTIPLIERS[i] as usize; + } + (order, hash) +} + +#[allow(dead_code)] +fn _unused(_: &MiInfo) {} diff --git a/crates/av1/src/transform.rs b/crates/av1/src/transform.rs new file mode 100644 index 0000000..626cefe --- /dev/null +++ b/crates/av1/src/transform.rs @@ -0,0 +1,733 @@ +//! Inverse transforms (spec 7.13), implemented step by step as specified. + +use crate::spec_tables::*; + +#[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) +} + +/// One value of the 1D transforms, or `L` independent columns / rows of them processed +/// together (lane-wise, so the steps vectorise). +pub(crate) trait Lane: Copy { + const ZERO: Self; + /// (Round2( a * cos - b * sin, 12 ), Round2( a * sin + b * cos, 12 )) + fn rotate(a: Self, b: Self, cos: i32, sin: i32) -> (Self, Self); + /// (Clamp( x + y ), Clamp( x - y )) to [lo, hi] + fn hadamard(x: Self, y: Self, lo: i32, hi: i32) -> (Self, Self); + fn neg(self) -> Self; + /// Round2( v * m, 12 ) + fn mul_round12(self, m: i32) -> Self; + fn mul(self, k: i32) -> Self; + fn adst4(t: &mut [Self]); +} + +impl Lane for i32 { + const ZERO: i32 = 0; + #[inline(always)] + fn rotate(a: i32, b: i32, cos: i32, sin: i32) -> (i32, i32) { + let (a, b, c, s) = (a as i64, b as i64, cos as i64, sin as i64); + (round2_64(a * c - b * s, 12), round2_64(a * s + b * c, 12)) + } + #[inline(always)] + fn hadamard(x: i32, y: i32, lo: i32, hi: i32) -> (i32, i32) { + ((x + y).clamp(lo, hi), (x - y).clamp(lo, hi)) + } + #[inline(always)] + fn neg(self) -> i32 { + -self + } + #[inline(always)] + fn mul_round12(self, m: i32) -> i32 { + round2_64(self as i64 * m as i64, 12) + } + #[inline(always)] + fn mul(self, k: i32) -> i32 { + self * k + } + fn adst4(t: &mut [i32]) { + inverse_adst4_scalar(t); + } +} + +/// Lanes in 32-bit arithmetic: exact for conforming streams up to 10 bits per sample (the +/// spec requires every intermediate value to fit in 8 + BitDepth <= 18 bits, so products with +/// the 12-bit cosines stay below 2^31). Wrapping operations keep corrupt streams from +/// panicking. +impl Lane for [i32; L] { + const ZERO: Self = [0; L]; + #[inline(always)] + fn rotate(a: Self, b: Self, cos: i32, sin: i32) -> (Self, Self) { + let mut x = [0; L]; + let mut y = [0; L]; + for l in 0..L { + let p = a[l].wrapping_mul(cos).wrapping_sub(b[l].wrapping_mul(sin)); + let q = a[l].wrapping_mul(sin).wrapping_add(b[l].wrapping_mul(cos)); + x[l] = p.wrapping_add(1 << 11) >> 12; + y[l] = q.wrapping_add(1 << 11) >> 12; + } + (x, y) + } + #[inline(always)] + fn hadamard(x: Self, y: Self, lo: i32, hi: i32) -> (Self, Self) { + let mut a = [0; L]; + let mut b = [0; L]; + for l in 0..L { + a[l] = x[l].wrapping_add(y[l]).max(lo).min(hi); + b[l] = x[l].wrapping_sub(y[l]).max(lo).min(hi); + } + (a, b) + } + #[inline(always)] + fn neg(self) -> Self { + self.map(|v| v.wrapping_neg()) + } + #[inline(always)] + fn mul_round12(self, m: i32) -> Self { + self.map(|v| v.wrapping_mul(m).wrapping_add(1 << 11) >> 12) + } + #[inline(always)] + fn mul(self, k: i32) -> Self { + self.map(|v| v.wrapping_mul(k)) + } + fn adst4(t: &mut [Self]) { + for l in 0..L { + let mut v = [t[0][l], t[1][l], t[2][l], t[3][l]]; + inverse_adst4_scalar(&mut v); + for k in 0..4 { + t[k][l] = v[k]; + } + } + } +} + +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 +} + +/// Butterfly rotation B( a, b, angle, flip ). +#[inline(always)] +fn bf(t: &mut [T], a: usize, b: usize, angle: i32, flip: bool) { + let (x, y) = T::rotate(t[a], t[b], cos128(angle) as i32, sin128(angle) as i32); + t[a] = x; + t[b] = y; + if flip { + t.swap(a, b); + } +} + +/// Hadamard rotation H( a, b, flip, r ). +#[inline(always)] +fn hd(t: &mut [T], a: usize, b: usize, flip: bool, r: u32) { + let (a, b) = if flip { (b, a) } else { (a, b) }; + let lo = -(1i32 << (r - 1)); + let hi = (1i32 << (r - 1)) - 1; + let (x, y) = T::hadamard(t[a], t[b], lo, hi); + t[a] = x; + t[b] = y; +} + +/// Inverse DCT of length 2^n (7.13.2.3), in place. +pub(crate) fn inverse_dct(t: &mut [T], n: u32, r: u32) { + // 7.13.2.2 permutation + let len = 1usize << n; + let mut copy = [T::ZERO; 64]; + copy[..len].copy_from_slice(&t[..len]); + for i in 0..len { + t[i] = copy[brev(n, i)]; + } + if n == 6 { + for i in 0..16 { + bf(t, 32 + i, 63 - i, 63 - 4 * brev(4, i) as i32, false); + } + } + 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 == 6 { + for i in 0..16 { + hd(t, 32 + i * 2, 33 + i * 2, i & 1 == 1, r); + } + } + 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 == 6 { + for i in 0..4 { + for j in 0..2 { + bf(t, 62 - i * 4 - j, 33 + i * 4 + j, 60 - 16 * brev(2, i) as i32 + 64 * j as i32, true); + } + } + } + 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); + } + } + } + if n == 6 { + for i in 0..8 { + for j in 0..2 { + hd(t, 32 + i * 4 + j, 35 + i * 4 - j, i & 1 == 1, r); + } + } + } + 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); + } + } + } + if n == 6 { + for i in 0..2 { + for j in 0..4 { + bf(t, 61 - i * 8 - j, 34 + i * 8 + j, 56 - i as i32 * 32 + (j as i32 >> 1) * 64, true); + } + } + } + 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 == 6 { + for i in 0..4 { + for j in 0..4 { + hd(t, 32 + 8 * i + j, 39 + 8 * i - j, i & 1 == 1, r); + } + } + } + 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 == 6 { + for i in 0..8 { + bf(t, 59 - i, 36 + i, if i < 4 { 48 } else { 112 }, true); + } + } + 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 == 6 { + for i in 0..8 { + hd(t, 32 + i, 47 - i, false, r); + hd(t, 48 + i, 63 - i, true, r); + } + } + if n >= 5 { + for i in 0..16 { + hd(t, i, 31 - i, false, r); + } + } + if n == 6 { + for i in 0..8 { + bf(t, 55 - i, 40 + i, 32, true); + } + } + if n == 6 { + for i in 0..32 { + hd(t, i, 63 - 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; + +fn inverse_adst4_scalar(t: &mut [i32]) { + let mut s = [0i64; 7]; + let mut x = [0i64; 4]; + let t0 = t[0] as i64; + let t1 = t[1] as i64; + let t2 = t[2] as i64; + let t3 = t[3] as i64; + s[0] = SINPI_1_9 * t0; + s[1] = SINPI_2_9 * t0; + s[2] = SINPI_3_9 * t1; + s[3] = SINPI_4_9 * t2; + s[4] = SINPI_1_9 * t2; + s[5] = SINPI_2_9 * t3; + s[6] = 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]; + x[0] = s[0] + s[3]; + x[1] = s[1] + s[3]; + x[2] = s[2]; + x[3] = s[0] + s[1]; + x[3] -= s[3]; + for i in 0..4 { + t[i] = round2_64(x[i], 12); + } +} + +fn adst_in_permute(t: &mut [T], n: u32) { + let n0 = 1usize << n; + let mut copy = [T::ZERO; 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]; + } +} + +fn adst_out_permute(t: &mut [T], n: u32) { + let n0 = 1usize << n; + let mut copy = [T::ZERO; 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].neg() } else { copy[idx] }; + } +} + +fn inverse_adst8(t: &mut [T], r: u32) { + adst_in_permute(t, 3); + 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(t, 3); +} + +fn inverse_adst16(t: &mut [T], r: u32) { + adst_in_permute(t, 4); + 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(t, 4); +} + +fn inverse_adst(t: &mut [T], n: u32, r: u32) { + match n { + 2 => T::adst4(t), + 3 => inverse_adst8(t, r), + _ => inverse_adst16(t, r), + } +} + +fn inverse_identity(t: &mut [T], n: u32) { + match n { + 2 => { + for v in t[..4].iter_mut() { + *v = v.mul_round12(5793); + } + } + 3 => { + for v in t[..8].iter_mut() { + *v = v.mul(2); + } + } + 4 => { + for v in t[..16].iter_mut() { + *v = v.mul_round12(11586); + } + } + _ => { + for v in t[..32].iter_mut() { + *v = v.mul(4); + } + } + } +} + +fn inverse_wht(t: &mut [i32], shift: u32) { + let mut a = t[0] >> shift; + let mut c = t[1] >> shift; + let mut d = t[2] >> shift; + let mut b = t[3] >> shift; + a += c; + d -= b; + let e = (a - d) >> 1; + b = e - b; + c = e - c; + a -= b; + d += c; + t[0] = a; + t[1] = b; + t[2] = c; + t[3] = d; +} + +#[derive(Clone, Copy, PartialEq, Eq)] +enum Kind1d { + Dct, + Adst, + Identity, +} + +fn row_kind(tx_type: usize) -> Kind1d { + match tx_type { + DCT_DCT | ADST_DCT | FLIPADST_DCT | H_DCT => Kind1d::Dct, + DCT_ADST | ADST_ADST | DCT_FLIPADST | FLIPADST_FLIPADST | ADST_FLIPADST | FLIPADST_ADST | H_ADST | H_FLIPADST => Kind1d::Adst, + _ => Kind1d::Identity, + } +} + +fn col_kind(tx_type: usize) -> Kind1d { + match tx_type { + DCT_DCT | DCT_ADST | DCT_FLIPADST | V_DCT => Kind1d::Dct, + ADST_DCT | ADST_ADST | FLIPADST_DCT | FLIPADST_FLIPADST | ADST_FLIPADST | FLIPADST_ADST | V_ADST | V_FLIPADST => Kind1d::Adst, + _ => Kind1d::Identity, + } +} + +/// 2D inverse transform (7.13.3). `dequant` is 64x64 (row stride 64, only the top-left 32x32 +/// may be non-zero); the residual is written to `residual` with row stride `w`. +pub(crate) fn inverse_transform_2d(dequant: &[i32], residual: &mut [i32], tx_sz: usize, tx_type: usize, lossless: bool, bit_depth: u32) { + let log2w = TX_WIDTH_LOG2[tx_sz] as u32; + let log2h = TX_HEIGHT_LOG2[tx_sz] as u32; + let w = 1usize << log2w; + let h = 1usize << log2h; + let row_shift = if lossless { 0 } else { TRANSFORM_ROW_SHIFT[tx_sz] as u32 }; + let col_shift = if lossless { 0 } else { 4 }; + let row_clamp = bit_depth + 8; + let col_clamp = (bit_depth + 6).max(16); + let rk = row_kind(tx_type); + let ck = col_kind(tx_type); + let mut t = [0i32; 64]; + let rect2 = log2w.abs_diff(log2h) == 1; + if !lossless && bit_depth <= 10 { + let p = Tx2d { w, h, log2w, log2h, row_shift, col_shift, row_clamp, col_clamp, rk, ck, rect2 }; + if w >= 8 && h >= 8 { + transform_lanes::<8>(dequant, residual, &p); + } else { + transform_lanes::<4>(dequant, residual, &p); + } + return; + } + let in_w = w.min(32); + for i in 0..h { + // Every 1D transform maps an all-zero row to zeros (each step is linear with + // Round2( 0 ) = 0), so rows without coefficients (including rows 32..63) are zero. + if i >= 32 || dequant[i * 64..i * 64 + in_w].iter().all(|&v| v == 0) { + residual[i * w..i * w + w].fill(0); + continue; + } + t[..in_w].copy_from_slice(&dequant[i * 64..i * 64 + in_w]); + t[in_w..w].fill(0); + if rect2 { + for v in t[..w].iter_mut() { + *v = round2_64(*v as i64 * 2896, 12); + } + } + if lossless { + inverse_wht(&mut t, 2); + } else { + match rk { + Kind1d::Dct => inverse_dct(&mut t, log2w, row_clamp), + Kind1d::Adst => inverse_adst(&mut t, log2w, row_clamp), + Kind1d::Identity => inverse_identity(&mut t, log2w), + } + } + let lo = -(1i32 << (col_clamp - 1)); + let hi = (1i32 << (col_clamp - 1)) - 1; + for j in 0..w { + residual[i * w + j] = round2(t[j], row_shift).clamp(lo, hi); + } + } + for j in 0..w { + for i in 0..h { + t[i] = residual[i * w + j]; + } + if lossless { + inverse_wht(&mut t, 0); + } else { + match ck { + Kind1d::Dct => inverse_dct(&mut t, log2h, col_clamp), + Kind1d::Adst => inverse_adst(&mut t, log2h, col_clamp), + Kind1d::Identity => inverse_identity(&mut t, log2h), + } + } + for i in 0..h { + residual[i * w + j] = round2(t[i], col_shift); + } + } +} + +struct Tx2d { + w: usize, + h: usize, + log2w: u32, + log2h: u32, + row_shift: u32, + col_shift: u32, + row_clamp: u32, + col_clamp: u32, + rk: Kind1d, + ck: Kind1d, + rect2: bool, +} + +#[inline(always)] +fn transform_1d(t: &mut [T], kind: Kind1d, n: u32, r: u32) { + match kind { + Kind1d::Dct => inverse_dct(t, n, r), + Kind1d::Adst => inverse_adst(t, n, r), + Kind1d::Identity => inverse_identity(t, n), + } +} + +/// The 2D inverse transform with `L` rows (row pass) or columns (column pass) at a time; +/// bit-identical to the one-at-a-time process for bit depths up to 10. +fn transform_lanes(dequant: &[i32], residual: &mut [i32], p: &Tx2d) { + let (w, h) = (p.w, p.h); + let in_w = w.min(32); + let lo = -(1i32 << (p.col_clamp - 1)); + let hi = (1i32 << (p.col_clamp - 1)) - 1; + let mut t = [[0i32; L]; 64]; + let mut i0 = 0; + while i0 < h { + // Rows without coefficients (and rows 32..63) transform to zeros. + if i0 >= 32 || (i0..i0 + L).all(|i| dequant[i * 64..i * 64 + in_w].iter().all(|&v| v == 0)) { + residual[i0 * w..(i0 + L) * w].fill(0); + i0 += L; + continue; + } + for (j, tj) in t[..w].iter_mut().enumerate() { + for l in 0..L { + tj[l] = if j < in_w { dequant[(i0 + l) * 64 + j] } else { 0 }; + } + } + if p.rect2 { + for v in t[..w].iter_mut() { + *v = v.mul_round12(2896); + } + } + transform_1d(&mut t[..], p.rk, p.log2w, p.row_clamp); + for l in 0..L { + let out = &mut residual[(i0 + l) * w..(i0 + l + 1) * w]; + for j in 0..w { + out[j] = round2(t[j][l], p.row_shift).clamp(lo, hi); + } + } + i0 += L; + } + let mut j0 = 0; + while j0 < w { + for i in 0..h { + t[i].copy_from_slice(&residual[i * w + j0..i * w + j0 + L]); + } + transform_1d(&mut t[..], p.ck, p.log2h, p.col_clamp); + for i in 0..h { + let out = &mut residual[i * w + j0..i * w + j0 + L]; + for l in 0..L { + out[l] = round2(t[i][l], p.col_shift); + } + } + j0 += L; + } +} + +#[cfg(test)] +mod tests { + use super::*; + + /// The DCT step list must implement an orthogonal transform: a DC-only input gives a flat + /// output equal to DC * cos(pi/4) (in the 2^12 fixed-point scale). + #[test] + fn dct_dc_is_flat() { + for n in 2..=6 { + let len = 1 << n; + let mut t = [0i32; 64]; + t[0] = 4096; + inverse_dct(&mut t, n, 24); + for v in &t[..len] { + assert_eq!(*v, 2896, "n={n}: {:?}", &t[..len]); + } + } + } + + /// Compare each inverse DCT basis vector with the real-valued DCT-III. + #[test] + fn dct_matches_float() { + for n in 2..=6u32 { + let len = 1usize << n; + for k in 0..len { + let mut t = [0i32; 64]; + t[k] = 1 << 12; + inverse_dct(&mut t, n, 24); + for x in 0..len { + let c = if k == 0 { std::f64::consts::FRAC_1_SQRT_2 } else { 1.0 }; + let want = 4096.0 * c * ((2 * x + 1) as f64 * k as f64 * std::f64::consts::PI / (2 * len) as f64).cos(); + assert!((t[x] as f64 - want).abs() < 4.0 + 0.5 * n as f64, "n={n} k={k} x={x}: {} vs {want}", t[x]); + } + } + } + } + + /// ADST8/16 basis vectors follow sin(pi (2i+1)(2k+1) / 4N) (scaled like the DCT). + #[test] + fn adst_basis_matches_sine() { + for n in 3..=4u32 { + let len = 1usize << n; + for k in 0..len { + let mut t = [0i32; 64]; + t[k] = 1 << 12; + inverse_adst(&mut t, n, 24); + for i in 0..len { + let want = 4096.0 * ((std::f64::consts::PI * (2 * i + 1) as f64 * (2 * k + 1) as f64) / (4 * len) as f64).sin(); + assert!((t[i] as f64 - want).abs() < 8.0, "n={n} k={k} i={i}: {} vs {want:.1}", t[i]); + } + } + } + } + + #[test] + fn adst_is_close_to_sine_transform() { + for n in 3..=4u32 { + let len = 1usize << n; + for k in 0..len { + let mut t = [0i32; 64]; + t[k] = 1 << 12; + inverse_adst(&mut t, n, 24); + let energy: f64 = t[..len].iter().map(|&v| (v as f64 / 4096.0).powi(2)).sum(); + // like the DCT, the AV1 ADST is scaled by sqrt(N / 2) + assert!((energy - len as f64 / 2.0).abs() < 0.02, "n={n} k={k} energy {energy}"); + } + } + } +} diff --git a/crates/bitstream/Cargo.toml b/crates/bitstream/Cargo.toml new file mode 100644 index 0000000..99cb98f --- /dev/null +++ b/crates/bitstream/Cargo.toml @@ -0,0 +1,14 @@ +[package] +name = "slidecraft-bitstream" +version.workspace = true +edition.workspace = true +license.workspace = true +rust-version.workspace = true + +[lints] +workspace = true + +[dependencies] + +[dev-dependencies] +proptest = { workspace = true } diff --git a/crates/bitstream/src/lib.rs b/crates/bitstream/src/lib.rs new file mode 100644 index 0000000..e5df0bb --- /dev/null +++ b/crates/bitstream/src/lib.rs @@ -0,0 +1,433 @@ +//! Bit-level reading and writing shared by SlideCraft's codec crates (ported from FilmCraft). +//! +//! - [`BitReader`]: MSB-first reader with Exp-Golomb (`ue(v)`, `se(v)`), as used by H.264/HEVC/AV1 headers. +//! - [`BitWriter`]: the matching writer (for encoders and header rewriting). +//! - [`unescape_rbsp`] / [`escape_rbsp`]: remove/insert H.264/HEVC emulation-prevention bytes. +//! - [`annexb_nals`] / [`length_prefixed_nals`]: split Annex-B byte streams and AVCC/HVCC samples into NAL units. + +#![cfg_attr(not(test), deny(clippy::unwrap_used, clippy::expect_used, clippy::panic, clippy::unimplemented, clippy::todo, clippy::unreachable))] + +use std::fmt; + +#[derive(Debug, Clone, Copy, PartialEq, Eq)] +pub enum BitError { + /// Tried to read past the end of the buffer. + Eof, + /// An Exp-Golomb code longer than 32 leading zeros. + InvalidExpGolomb, +} + +impl fmt::Display for BitError { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + match self { + BitError::Eof => f.write_str("unexpected end of bitstream"), + BitError::InvalidExpGolomb => f.write_str("invalid Exp-Golomb code"), + } + } +} +impl std::error::Error for BitError {} + +pub type Result = std::result::Result; + +/// MSB-first bit reader over a byte slice. +#[derive(Clone)] +pub struct BitReader<'a> { + data: &'a [u8], + /// Position in bits. + pos: usize, +} + +impl<'a> BitReader<'a> { + pub fn new(data: &'a [u8]) -> Self { + Self { data, pos: 0 } + } + pub fn data(&self) -> &'a [u8] { + self.data + } + /// Bit position from the start. + pub fn position(&self) -> usize { + self.pos + } + pub fn bits_left(&self) -> usize { + (self.data.len() * 8).saturating_sub(self.pos) + } + pub fn is_byte_aligned(&self) -> bool { + self.pos.is_multiple_of(8) + } + pub fn byte_align(&mut self) { + self.pos = self.pos.div_ceil(8) * 8; + } + /// Byte offset of the current position (rounded down). + pub fn byte_pos(&self) -> usize { + self.pos / 8 + } + pub fn seek_bits(&mut self, pos: usize) { + self.pos = pos; + } + + /// Peek up to 32 bits without consuming (zero-padded past the end). + #[inline] + pub fn peek(&self, n: u32) -> u32 { + debug_assert!(n <= 32); + if n == 0 { + return 0; + } + let byte = self.pos / 8; + let shift = self.pos % 8; + let mut v: u64 = 0; + for i in 0..5 { + v = (v << 8) | *self.data.get(byte + i).unwrap_or(&0) as u64; + } + // v holds 40 bits starting at `byte` + ((v << (24 + shift)) >> (64 - n)) as u32 + } + + #[inline] + pub fn read_bits(&mut self, n: u32) -> Result { + if n as usize > self.bits_left() { + return Err(BitError::Eof); + } + let v = self.peek(n); + self.pos += n as usize; + Ok(v) + } + + pub fn read_bits_u64(&mut self, n: u32) -> Result { + if n <= 32 { + return self.read_bits(n).map(u64::from); + } + let hi = self.read_bits(n - 32)? as u64; + let lo = self.read_bits(32)? as u64; + Ok((hi << 32) | lo) + } + + #[inline] + pub fn read_bit(&mut self) -> Result { + self.read_bits(1).map(|b| b == 1) + } + pub fn read_flag(&mut self) -> Result { + self.read_bit() + } + + pub fn skip(&mut self, n: usize) -> Result<()> { + if n > self.bits_left() { + return Err(BitError::Eof); + } + self.pos += n; + Ok(()) + } + + /// Unsigned Exp-Golomb `ue(v)`. + pub fn read_ue(&mut self) -> Result { + let mut zeros = 0u32; + while !self.read_bit()? { + zeros += 1; + if zeros > 32 { + return Err(BitError::InvalidExpGolomb); + } + } + if zeros == 0 { + return Ok(0); + } + let v = self.read_bits_u64(zeros)?; + let r = (1u64 << zeros) - 1 + v; + u32::try_from(r).map_err(|_| BitError::InvalidExpGolomb) + } + + /// Signed Exp-Golomb `se(v)`. + pub fn read_se(&mut self) -> Result { + let k = self.read_ue()? as i64; + Ok(if k & 1 == 1 { ((k + 1) / 2) as i32 } else { -((k / 2) as i32) }) + } + + /// Truncated Exp-Golomb `te(v)` with range `max`. + pub fn read_te(&mut self, max: u32) -> Result { + if max > 1 { self.read_ue() } else { Ok(!self.read_bit()? as u32) } + } + + /// H.264/HEVC `more_rbsp_data()`: true if there is data before the rbsp trailing bits. + pub fn more_rbsp_data(&self) -> bool { + let total = self.data.len() * 8; + if self.pos >= total { + return false; + } + // find last set bit (the rbsp_stop_one_bit) + let mut last = None; + for (i, &b) in self.data.iter().enumerate().rev() { + if b != 0 { + last = Some(i * 8 + 7 - b.trailing_zeros() as usize); + break; + } + } + match last { + Some(stop) => self.pos < stop, + None => false, + } + } +} + +/// MSB-first bit writer. +#[derive(Default, Clone)] +pub struct BitWriter { + buf: Vec, + acc: u64, + nbits: u32, +} + +impl BitWriter { + pub fn new() -> Self { + Self::default() + } + pub fn write_bits(&mut self, value: u32, n: u32) { + debug_assert!(n <= 32); + if n == 0 { + return; + } + let v = (value 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; + } + pub fn write_bit(&mut self, b: bool) { + self.write_bits(b as u32, 1); + } + pub fn write_ue(&mut self, v: u32) { + let x = v as u64 + 1; + let len = 64 - x.leading_zeros(); + let zeros = len - 1; + for _ in 0..zeros { + self.write_bit(false); + } + if len > 32 { + self.write_bits((x >> 32) as u32, len - 32); + self.write_bits(x as u32, 32); + } else { + self.write_bits(x as u32, len); + } + } + pub fn write_se(&mut self, v: i32) { + let k = if v > 0 { (v as u32) * 2 - 1 } else { (-(v as i64) as u32) * 2 }; + self.write_ue(k); + } + pub fn bit_len(&self) -> usize { + self.buf.len() * 8 + self.nbits as usize + } + pub fn is_byte_aligned(&self) -> bool { + self.nbits == 0 + } + /// Pad with zero bits to a byte boundary. + pub fn align_zero(&mut self) { + if self.nbits > 0 { + self.write_bits(0, 8 - self.nbits); + } + } + /// `rbsp_trailing_bits()`: a one bit then zeros to alignment. + pub fn rbsp_trailing(&mut self) { + self.write_bit(true); + self.align_zero(); + } + pub fn write_bytes(&mut self, bytes: &[u8]) { + if self.nbits == 0 { + self.buf.extend_from_slice(bytes); + } else { + for &b in bytes { + self.write_bits(b as u32, 8); + } + } + } + pub fn finish(mut self) -> Vec { + self.align_zero(); + self.buf + } + pub fn bytes(&self) -> &[u8] { + &self.buf + } +} + +/// Remove emulation-prevention bytes (`00 00 03` → `00 00`) from a NAL payload. +pub fn unescape_rbsp(data: &[u8]) -> Vec { + let mut out = Vec::with_capacity(data.len()); + let mut zeros = 0; + for &b in data { + if zeros >= 2 && b == 3 { + zeros = 0; + continue; + } + out.push(b); + zeros = if b == 0 { zeros + 1 } else { 0 }; + } + out +} + +/// Insert emulation-prevention bytes so no `00 00 0x` (x ≤ 3) sequence appears. +pub fn escape_rbsp(data: &[u8]) -> Vec { + let mut out = Vec::with_capacity(data.len() + data.len() / 64); + let mut zeros = 0; + for &b in data { + if zeros >= 2 && b <= 3 { + out.push(3); + zeros = 0; + } + out.push(b); + zeros = if b == 0 { zeros + 1 } else { 0 }; + } + out +} + +/// Split an Annex-B byte stream (start codes `00 00 01` / `00 00 00 01`) into NAL units (escaped payloads). +pub fn annexb_nals(data: &[u8]) -> Vec<&[u8]> { + let mut starts = Vec::new(); + let mut i = 0; + while i + 3 <= data.len() { + if data[i] == 0 && data[i + 1] == 0 && data[i + 2] == 1 { + starts.push(i + 3); + i += 3; + } else { + i += 1; + } + } + let mut out = Vec::with_capacity(starts.len()); + for (k, &s) in starts.iter().enumerate() { + let mut e = if k + 1 < starts.len() { starts[k + 1] - 3 } else { data.len() }; + // trim trailing zero bytes (part of a 4-byte start code or trailing_zero_8bits) + while e > s && data[e - 1] == 0 { + e -= 1; + } + if e > s { + out.push(&data[s..e]); + } + } + out +} + +/// Split a length-prefixed sample (AVCC/HVCC, `length_size` 1/2/4 bytes) into NAL units. +pub fn length_prefixed_nals(data: &[u8], length_size: usize) -> Result> { + let mut out = Vec::new(); + let mut i = 0; + while i < data.len() { + if i + length_size > data.len() { + return Err(BitError::Eof); + } + let mut len = 0usize; + for k in 0..length_size { + len = (len << 8) | data[i + k] as usize; + } + i += length_size; + if i + len > data.len() { + return Err(BitError::Eof); + } + out.push(&data[i..i + len]); + i += len; + } + Ok(out) +} + +/// CRC-32 (IEEE, reflected) — used by containers (e.g. Matroska CRC-32 elements, PNG). +pub fn crc32(data: &[u8]) -> u32 { + let mut crc = 0xFFFF_FFFFu32; + for &b in data { + crc ^= b as u32; + for _ in 0..8 { + crc = if crc & 1 != 0 { (crc >> 1) ^ 0xEDB8_8320 } else { crc >> 1 }; + } + } + !crc +} + +#[cfg(test)] +mod tests { + use super::*; + use proptest::prelude::*; + + #[test] + fn reads_bits() { + let d = [0b1010_1100, 0b0101_0011]; + let mut r = BitReader::new(&d); + assert_eq!(r.read_bits(1).unwrap(), 1); + assert_eq!(r.read_bits(3).unwrap(), 0b010); + assert_eq!(r.read_bits(8).unwrap(), 0b1100_0101); + assert_eq!(r.bits_left(), 4); + assert_eq!(r.read_bits(5), Err(BitError::Eof)); + } + + #[test] + fn exp_golomb_known() { + // 1 | 010 | 011 | 00100 | 00101 -> 0,1,2,3,4 + let mut w = BitWriter::new(); + for v in 0..5 { + w.write_ue(v); + } + let b = w.finish(); + let mut r = BitReader::new(&b); + for v in 0..5 { + assert_eq!(r.read_ue().unwrap(), v); + } + let mut w = BitWriter::new(); + for v in [0, 1, -1, 2, -2, 1000, -1000] { + w.write_se(v); + } + let b = w.finish(); + let mut r = BitReader::new(&b); + for v in [0, 1, -1, 2, -2, 1000, -1000] { + assert_eq!(r.read_se().unwrap(), v); + } + } + + #[test] + fn emulation_prevention() { + let raw = [0, 0, 1, 0, 0, 0, 5, 0, 0, 3]; + let esc = escape_rbsp(&raw); + assert_eq!(esc, vec![0, 0, 3, 1, 0, 0, 3, 0, 5, 0, 0, 3, 3]); + assert_eq!(unescape_rbsp(&esc), raw); + } + + #[test] + fn annexb_split() { + let s = [0, 0, 0, 1, 0x67, 1, 2, 0, 0, 1, 0x68, 3, 0, 0, 0, 1, 0x65, 9, 9]; + let n = annexb_nals(&s); + assert_eq!(n, vec![&[0x67, 1, 2][..], &[0x68, 3][..], &[0x65, 9, 9][..]]); + let l = [0, 0, 0, 2, 0x67, 1, 0, 0, 0, 1, 0x68]; + assert_eq!(length_prefixed_nals(&l, 4).unwrap(), vec![&[0x67, 1][..], &[0x68][..]]); + } + + #[test] + fn more_rbsp() { + let d = [0b1100_0000]; + let mut r = BitReader::new(&d); + assert!(r.more_rbsp_data()); + r.read_bit().unwrap(); + assert!(!r.more_rbsp_data()); + } + + #[test] + fn crc() { + assert_eq!(crc32(b"123456789"), 0xCBF4_3926); + } + + proptest! { + #[test] + fn roundtrip(vals in proptest::collection::vec((0u32..u32::MAX, 1u32..=32), 0..200), ues in proptest::collection::vec(0u32..u32::MAX - 1, 0..50)) { + let mut w = BitWriter::new(); + for &(v, n) in &vals { w.write_bits(v, n); } + for &u in &ues { w.write_ue(u); } + let b = w.finish(); + let mut r = BitReader::new(&b); + for &(v, n) in &vals { + let mask = if n == 32 { u32::MAX } else { (1 << n) - 1 }; + prop_assert_eq!(r.read_bits(n).unwrap(), v & mask); + } + for &u in &ues { prop_assert_eq!(r.read_ue().unwrap(), u); } + } + + #[test] + fn escape_roundtrip(data in proptest::collection::vec(0u8..4, 0..300)) { + let e = escape_rbsp(&data); + prop_assert_eq!(unescape_rbsp(&e), data); + for w in e.windows(3) { prop_assert!(!(w[0] == 0 && w[1] == 0 && w[2] < 3)); } + } + } +} diff --git a/crates/engine/Cargo.toml b/crates/engine/Cargo.toml index ba24456..eaedebd 100644 --- a/crates/engine/Cargo.toml +++ b/crates/engine/Cargo.toml @@ -18,6 +18,7 @@ slidecraft-render = { workspace = true } slidecraft-pdf = { workspace = true } slidecraft-format = { workspace = true } slidecraft-pptx = { workspace = true } +slidecraft-media = { workspace = true } serde = { workspace = true } serde_json = { workspace = true } i_overlay = { workspace = true } diff --git a/crates/engine/src/cmd/insert.rs b/crates/engine/src/cmd/insert.rs index 2af8515..cd0fed7 100644 --- a/crates/engine/src/cmd/insert.rs +++ b/crates/engine/src/cmd/insert.rs @@ -243,20 +243,33 @@ pub fn content_type(name: &str, bytes: &[u8]) -> &'static str { "audio/flac" } else if bytes.starts_with(b"OggS") { if l.ends_with(".ogv") { "video/ogg" } else { "audio/ogg" } + } else if bytes.len() > 1 && bytes[0] == 0xFF && bytes[1] & 0xF6 == 0xF0 { + // ADTS (layer bits 00): AAC. + "audio/aac" } else if bytes.starts_with(b"ID3") || (bytes.len() > 1 && bytes[0] == 0xFF && (bytes[1] & 0xE0) == 0xE0 && !l.ends_with(".aac")) { "audio/mpeg" } else if bytes.starts_with(b"FORM") { "audio/aiff" } else if bytes.get(4..8) == Some(b"ftyp") { - if l.ends_with(".m4a") || bytes.get(8..11) == Some(b"M4A") { + if l.ends_with(".m4a") || l.ends_with(".m4b") || bytes.get(8..11) == Some(b"M4A") { "audio/mp4" - } else if l.ends_with(".mov") { + } else if l.ends_with(".mov") || bytes.get(8..12) == Some(b"qt ") { "video/quicktime" } else { "video/mp4" } } else if bytes.starts_with(&[0x1A, 0x45, 0xDF, 0xA3]) { - "video/webm" + if l.ends_with(".mka") || l.ends_with(".weba") { + "audio/webm" + } else if l.ends_with(".mkv") { + "video/x-matroska" + } else { + "video/webm" + } + } else if bytes.starts_with(&[0x30, 0x26, 0xB2, 0x75, 0x8E, 0x66, 0xCF, 0x11]) { + if l.ends_with(".wmv") || l.ends_with(".asf") { "video/x-ms-wmv" } else { "audio/x-ms-wma" } + } else if bytes.starts_with(b"caff") { + "audio/x-caf" } else if l.ends_with(".svg") || bytes.windows(4).take(512).any(|w| w == b" Result { let cmd = if video { "insert.video" } else { "insert.audio" }; let (name, bytes) = media_bytes(p, cmd)?; let ct = content_type(&name, &bytes); - if video && !ct.starts_with("video/") { - return Err(bad(cmd, format!("{name} is not a supported video ({ct})"))); - } - if !video && !ct.starts_with("audio/") { - return Err(bad(cmd, format!("{name} is not a supported audio file ({ct})"))); + let bytes = std::sync::Arc::new(bytes); + let info = slidecraft_media::probe(&bytes); + let has_video = info.as_ref().is_ok_and(|i| i.video.is_some()); + let has_audio = info.as_ref().is_ok_and(|i| i.audio.is_some()); + match &info { + Ok(_) if video && !has_video => return Err(bad(cmd, format!("{name} has no video"))), + Ok(_) if !video && !has_audio && !has_video => return Err(bad(cmd, format!("{name} has no audio"))), + // Not something we can read: embed it anyway when the type says it's media (it still + // round-trips through PPTX), otherwise refuse. + Err(_) if video && !ct.starts_with("video/") => return Err(bad(cmd, format!("{name} is not a supported video ({ct})"))), + Err(_) if !video && !ct.starts_with("audio/") => return Err(bad(cmd, format!("{name} is not a supported audio file ({ct})"))), + _ => {} } + let (duration_ms, width, height, playable) = match &info { + Ok(i) => { + let (w, h) = i.video.as_ref().map(|v| (v.width, v.height)).unwrap_or((0, 0)); + (i.duration_ms.min(u32::MAX as u64) as u32, w, h, i.playable()) + } + Err(_) => (0, 0, 0, false), + }; + let warning = (!playable).then(|| match &info { + Ok(i) => { + let codecs: Vec<&str> = i.audio.iter().map(|a| a.codec.as_str()).chain(i.video.iter().map(|v| v.codec.as_str())).collect(); + format!("{name} was inserted, but it can't be played ({} isn't supported yet)", codecs.join(" / ")) + } + Err(e) => format!("{name} was inserted, but it can't be played ({e})"), + }); + let decodable_video = info.as_ref().is_ok_and(|i| i.video.as_ref().is_some_and(|v| v.decodable)); + let poster_png = if video && decodable_video { super::media::poster_png(&bytes, 0) } else { None }; let size = s.doc()?.doc.slide_size; let rect = rect_param(p, "rect").unwrap_or_else(|| { if video { - Xfrm::new(size.width * 0.2, size.height * 0.2, size.width * 0.6, size.width * 0.6 * 9.0 / 16.0) + // Natural size (96 dpi), fitted inside 90 % of the slide and centred. + let (w, h) = + if width > 0 && height > 0 { (width as f64 * 0.75, height as f64 * 0.75) } else { (size.width * 0.6, size.width * 0.6 * 9.0 / 16.0) }; + let k = (size.width * 0.9 / w).min(size.height * 0.9 / h).min(1.0); + let (w, h) = (w * k, h * k); + Xfrm::new((size.width - w) / 2.0, (size.height - h) / 2.0, w, h) } else { Xfrm::new((size.width - 48.0) / 2.0, (size.height - 48.0) / 2.0, 48.0, 48.0) } }); - let media = s.edit(|doc, _| Ok(doc.add_media(&name, ct, bytes)))?; - let clip = MediaClip { media, video, volume: 1.0, ..Default::default() }; + let data = std::sync::Arc::try_unwrap(bytes).unwrap_or_else(|a| (*a).clone()); + let (media, poster) = s.edit(|doc, _| { + let media = doc.add_media(&name, ct, data); + let poster = poster_png.map(|png| doc.add_media(&format!("{name}-poster.png"), "image/png", png)); + Ok((media, poster)) + })?; + let clip = MediaClip { media, video, poster, volume: 1.0, duration_ms, width, height, ..Default::default() }; let id = add_shape(s, Shape { xfrm: Some(rect), kind: ShapeKind::Media(clip), descr: name, ..Default::default() }, true)?; - Ok(json!({"id": id, "contentType": ct})) + if let Some(w) = &warning { + s.ui_requests.push(crate::UiRequest::Message { text: w.clone() }); + } + let mut r = + json!({"id": id, "contentType": ct, "durationMs": duration_ms, "width": width, "height": height, "playable": playable, "warning": warning}); + if let Ok(i) = &info { + r["probe"] = super::media::probe_json(i); + } + Ok(r) } fn audio(s: &mut Session, p: &Value) -> Result { diff --git a/crates/engine/src/cmd/media.rs b/crates/engine/src/cmd/media.rs new file mode 100644 index 0000000..1e51fe8 --- /dev/null +++ b/crates/engine/src/cmd/media.rs @@ -0,0 +1,224 @@ +//! Media playback (Playback tab, the media control bar, agents): play / pause / stop / seek are +//! requests the UI host carries out with its player ([`crate::UiRequest::Media`]); the host reports +//! back through [`Session::media_status`], which `media.info` returns with the probe. + +use serde_json::{Value, json}; +use slidecraft_model::{MediaClip, ShapeId, ShapeKind}; + +use super::*; +use crate::{EngineError, MediaStatus, Result, Session, UiRequest}; + +pub fn specs() -> Vec { + vec![ + cmd!(noundo "media.play", "Play", ["Playback", "Preview"], Some("Alt+P"), "{id?, from?: ms}", has_doc, play), + cmd!(noundo "media.pause", "Pause", ["Playback", "Preview"], None, "{id?}", has_doc, pause), + cmd!(noundo "media.toggle", "Play/Pause", [], None, "{id?}", has_doc, toggle), + cmd!(noundo "media.stop", "Stop", ["Playback", "Preview"], None, "{id?}", has_doc, stop), + cmd!(noundo "media.seek", "Seek", [], None, "{id?, ms}", has_doc, seek), + cmd!( + query "media.info", + "Media Info", + [], + None, + "{id?} → {id, name, contentType, bytes, durationMs, video, width, height, probe: {container, audio?, video?}, options, status?}", + has_doc, + info + ), + cmd!( + "media.posterFrame", + "Poster Frame", + ["Video Format", "Adjust"], + None, + "{id?, ms?: frame time (default: current position)}", + has_doc, + poster_frame + ), + ] +} + +/// The media shape a command acts on: `id` / `ids` / the selection, else the only media shape on +/// the current slide. +fn target(s: &Session, p: &Value, cmd: &str) -> Result<(ShapeId, MediaClip)> { + let st = s.doc()?; + let media_of = |id: ShapeId| st.shape(id).and_then(|sh| if let ShapeKind::Media(m) = &sh.kind { Some((id, m.clone())) } else { None }); + let ids = targets(s, p)?; + if let Some(found) = ids.iter().find_map(|id| media_of(*id)) { + return Ok(found); + } + if !ids.is_empty() && (p.get("id").is_some() || p.get("ids").is_some()) { + return Err(bad(cmd, "not a media shape")); + } + let all: Vec<(ShapeId, MediaClip)> = st.shapes().iter().filter_map(|sh| media_of(sh.id)).collect(); + match all.len() { + 1 => Ok(all.into_iter().next().ok_or_else(|| bad(cmd, "no media"))?), + 0 => Err(bad(cmd, "no audio or video on this slide")), + _ => Err(bad(cmd, "select an audio or video, or pass `id`")), + } +} + +fn request(s: &mut Session, p: &Value, cmd: &str, action: &str, ms: Option) -> Result { + let (shape, _) = target(s, p, cmd)?; + s.ui_requests.push(UiRequest::Media { action: action.into(), shape, ms }); + Ok(json!({"id": shape})) +} + +fn ms_param(p: &Value, key: &str) -> Option { + p.get(key).and_then(Value::as_f64).filter(|v| v.is_finite() && *v >= 0.0).map(|v| v as u64) +} + +fn play(s: &mut Session, p: &Value) -> Result { + request(s, p, "media.play", "play", ms_param(p, "from")) +} +fn pause(s: &mut Session, p: &Value) -> Result { + request(s, p, "media.pause", "pause", None) +} +fn toggle(s: &mut Session, p: &Value) -> Result { + request(s, p, "media.toggle", "toggle", None) +} +fn stop(s: &mut Session, p: &Value) -> Result { + request(s, p, "media.stop", "stop", None) +} +fn seek(s: &mut Session, p: &Value) -> Result { + let ms = ms_param(p, "ms").ok_or_else(|| bad("media.seek", "missing `ms`"))?; + request(s, p, "media.seek", "seek", Some(ms)) +} + +pub(crate) fn probe_json(info: &slidecraft_media::MediaInfo) -> Value { + json!({ + "container": info.container, + "durationMs": info.duration_ms, + "playable": info.playable(), + "audio": info.audio.as_ref().map(|a| json!({"codec": a.codec, "sampleRate": a.sample_rate, "channels": a.channels, "decodable": a.decodable})), + "video": info.video.as_ref().map(|v| json!({"codec": v.codec, "width": v.width, "height": v.height, "fps": (v.fps * 1000.0).round() / 1000.0, "frames": v.frames, "decodable": v.decodable})), + }) +} + +fn info(s: &mut Session, p: &Value) -> Result { + let (id, m) = target(s, p, "media.info")?; + let st = s.doc()?; + let item = st.doc.media(m.media).ok_or_else(|| EngineError::Other("the media data is missing".into()))?; + let probe = slidecraft_media::probe(&item.data).map(|i| probe_json(&i)).unwrap_or_else(|e| json!({"error": e.to_string()})); + Ok(json!({ + "id": id, + "name": item.name, + "contentType": item.content_type, + "bytes": item.data.len(), + "video": m.video, + "durationMs": m.duration_ms, + "width": m.width, + "height": m.height, + "poster": m.poster.is_some(), + "probe": probe, + "options": { + "autoplay": m.autoplay, "loop": m.loop_play, "rewind": m.rewind, "acrossSlides": m.play_across_slides, + "hide": m.hide_while_not_playing, "fullScreen": m.full_screen, "volume": m.volume, + "trimStart": m.trim_start_ms, "trimEnd": m.trim_end_ms, "fadeIn": m.fade_in_ms, "fadeOut": m.fade_out_ms, + }, + "status": s.media_status.get(&id), + })) +} + +/// A poster frame PNG for video bytes at `ms`. +pub(crate) fn poster_png(bytes: &slidecraft_media::Bytes, ms: u64) -> Option> { + match slidecraft_media::poster_png(bytes, ms as f64 / 1000.0) { + Ok(png) => Some(png), + Err(e) => { + log::info!("no poster frame: {e}"); + None + } + } +} + +fn poster_frame(s: &mut Session, p: &Value) -> Result { + let (id, m) = target(s, p, "media.posterFrame")?; + if !m.video { + return Err(bad("media.posterFrame", "only videos have poster frames")); + } + let ms = ms_param(p, "ms").or_else(|| s.media_status.get(&id).map(|st: &MediaStatus| st.position_ms)).unwrap_or(0); + let data = s.doc()?.doc.media(m.media).map(|i| i.data.clone()).ok_or_else(|| EngineError::Other("the media data is missing".into()))?; + let png = poster_png(&data, ms).ok_or_else(|| EngineError::Other("no frame could be decoded there".into()))?; + let name = format!("poster{id}.png"); + let poster = s.edit(|doc, _| Ok(doc.add_media(&name, "image/png", png)))?; + edit_shapes(s, &json!({"id": id}), "media.posterFrame", |sh| { + if let ShapeKind::Media(m) = &mut sh.kind { + m.poster = Some(poster); + } + Ok(()) + })?; + Ok(json!({"id": id, "ms": ms})) +} + +#[cfg(test)] +mod tests { + use super::*; + + fn wav() -> Vec { + let rate = 8000u32; + let samples: Vec = (0..rate / 2).map(|i| ((i as f32 * 0.3).sin() * 3000.0) as i16).collect(); + let data: Vec = samples.iter().flat_map(|s| s.to_le_bytes()).collect(); + let mut b = b"RIFF".to_vec(); + b.extend_from_slice(&(36 + data.len() as u32).to_le_bytes()); + b.extend_from_slice(b"WAVEfmt "); + for v in [ + 16u32.to_le_bytes().to_vec(), + 1u16.to_le_bytes().to_vec(), + 1u16.to_le_bytes().to_vec(), + rate.to_le_bytes().to_vec(), + (rate * 2).to_le_bytes().to_vec(), + 2u16.to_le_bytes().to_vec(), + 16u16.to_le_bytes().to_vec(), + ] { + b.extend_from_slice(&v); + } + b.extend_from_slice(b"data"); + b.extend_from_slice(&(data.len() as u32).to_le_bytes()); + b.extend_from_slice(&data); + b + } + + #[test] + fn insert_audio_probes_and_play_requests_reach_the_host() { + let mut s = Session::with_new(); + let _ = s.execute("slide.new", &json!({"layout": "blank"})); + let r = s.execute("insert.audio", &json!({"name": "tone.wav", "data": base64_encode(&wav())})).expect("insert"); + assert_eq!(r["durationMs"], 500); + assert_eq!(r["playable"], true); + let id = r["id"].as_u64().expect("id"); + s.ui_requests.clear(); + s.execute("media.play", &json!({"from": 100})).expect("play"); + assert_eq!(s.ui_requests, vec![UiRequest::Media { action: "play".into(), shape: ShapeId(id as u32), ms: Some(100) }]); + s.execute("media.seek", &json!({"id": id, "ms": 250})).expect("seek"); + assert!(s.execute("media.seek", &json!({"id": id})).is_err()); + // The host reports state; media.info returns it with the probe. + s.media_status.insert(ShapeId(id as u32), MediaStatus { state: "playing".into(), position_ms: 250, duration_ms: 500, error: None }); + let i = s.execute("media.info", &json!({})).expect("info"); + assert_eq!(i["probe"]["audio"]["sampleRate"], 8000); + assert_eq!(i["probe"]["container"], "wav"); + assert_eq!(i["status"]["positionMs"], 250); + assert_eq!(i["status"]["state"], "playing"); + assert!(s.execute("media.posterFrame", &json!({})).is_err(), "audio has no poster frame"); + } + + #[test] + fn media_commands_need_media() { + let mut s = Session::with_new(); + let _ = s.execute("slide.new", &json!({"layout": "blank"})); + assert!(s.execute("media.play", &json!({})).is_err()); + let sh = s.execute("shape.insert", &json!({"preset": "rect", "rect": [0, 0, 10, 10]})).expect("shape"); + assert!(s.execute("media.play", &json!({"id": sh["id"]})).is_err()); + } + + #[test] + fn wma_inserts_but_reports_unplayable() { + let mut s = Session::with_new(); + let _ = s.execute("slide.new", &json!({"layout": "blank"})); + // A minimal ASF header object with no children. + let mut asf = vec![]; + asf.extend_from_slice(&[0x30, 0x26, 0xB2, 0x75, 0x8E, 0x66, 0xCF, 0x11, 0xA6, 0xD9, 0x00, 0xAA, 0x00, 0x62, 0xCE, 0x6C]); + asf.extend_from_slice(&30u64.to_le_bytes()); + asf.extend_from_slice(&[0; 6]); + let r = s.execute("insert.audio", &json!({"name": "song.wma", "data": base64_encode(&asf)})).expect("insert"); + assert_eq!(r["playable"], false); + assert!(r["warning"].as_str().is_some_and(|w| w.contains("can't be played")), "{r}"); + } +} diff --git a/crates/engine/src/cmd/mod.rs b/crates/engine/src/cmd/mod.rs index 74c44ef..b382e86 100644 --- a/crates/engine/src/cmd/mod.rs +++ b/crates/engine/src/cmd/mod.rs @@ -9,6 +9,7 @@ pub mod format; pub mod insert; pub mod inspect; pub mod merge; +pub mod media; pub mod review; pub mod shape; pub mod slide; @@ -138,6 +139,7 @@ pub fn command_specs() -> &'static [CommandSpec] { v.extend(table::specs()); v.extend(review::specs()); v.extend(inspect::specs()); + v.extend(media::specs()); v }) } diff --git a/crates/engine/src/lib.rs b/crates/engine/src/lib.rs index 8c5834b..8f9d261 100644 --- a/crates/engine/src/lib.rs +++ b/crates/engine/src/lib.rs @@ -24,6 +24,7 @@ use slidecraft_model::{Presentation, Shape, ShapeId, Slide, SlideId}; pub use cmd::{CommandInfo, CommandSpec, command_specs, find_command}; pub use slidecraft_geom as geom; +pub use slidecraft_media as media; pub use slidecraft_model as model; pub use slidecraft_render as render; pub use tools::{Mods, PointerEvent, PointerKind, ToolKind}; @@ -238,11 +239,41 @@ impl Default for Prefs { #[derive(Clone, Debug, PartialEq, Serialize, Deserialize)] #[serde(rename_all = "camelCase", tag = "kind")] pub enum UiRequest { - Dialog { id: String, params: Value }, - Pane { id: String }, - StartShow { from: usize }, - Message { text: String }, - PickFile { purpose: String }, + Dialog { + id: String, + params: Value, + }, + Pane { + id: String, + }, + StartShow { + from: usize, + }, + Message { + text: String, + }, + PickFile { + purpose: String, + }, + /// Media playback for the UI host (`media.play` / `pause` / `stop` / `seek`): `action` is + /// `play`, `pause`, `toggle`, `stop` or `seek`; `ms` the media time to start from / seek to. + Media { + action: String, + shape: ShapeId, + ms: Option, + }, +} + +/// Playback state of a media shape as the UI host reports it (see [`Session::media_status`]). +#[derive(Clone, Debug, Default, PartialEq, Serialize, Deserialize)] +#[serde(rename_all = "camelCase")] +pub struct MediaStatus { + /// `loading`, `playing`, `paused` or `ended`. + pub state: String, + pub position_ms: u64, + pub duration_ms: u64, + /// Why the media is silent / blank, when it can't be decoded. + pub error: Option, } /// Clipboard contents copied within the app. @@ -276,6 +307,8 @@ pub struct Session { depth: u32, /// Crash-recovery folder (the app sets it; saving or closing a presentation removes its entry). pub recovery_dir: Option, + /// Media playback state, written by the UI host every frame (empty without a host). + pub media_status: std::collections::HashMap, } impl Default for Session { @@ -300,6 +333,7 @@ impl Session { painter: None, depth: 0, recovery_dir: None, + media_status: Default::default(), } } /// A session with one new presentation open. diff --git a/crates/h264/Cargo.toml b/crates/h264/Cargo.toml new file mode 100644 index 0000000..18ddb56 --- /dev/null +++ b/crates/h264/Cargo.toml @@ -0,0 +1,19 @@ +[package] +name = "slidecraft-h264" +description = "Clean-room pure-Rust H.264/AVC decoder (ITU-T H.264)" +version.workspace = true +edition.workspace = true +license.workspace = true +rust-version.workspace = true + +[lints] +workspace = true + +[features] +default = ["threads"] +threads = ["dep:rayon"] + +[dependencies] +slidecraft-bitstream = { workspace = true } +thiserror = { workspace = true } +rayon = { workspace = true, optional = true } diff --git a/crates/h264/src/cabac.rs b/crates/h264/src/cabac.rs new file mode 100644 index 0000000..184406a --- /dev/null +++ b/crates/h264/src/cabac.rs @@ -0,0 +1,335 @@ +//! CABAC arithmetic decoding engine (9.3.1.2, 9.3.3.2) and context initialisation (9.3.1.1). + +use crate::cabac_tables::CABAC_INIT_MN; +use crate::error::{Result, ensure}; + +/// Table 9-44 rangeTabLPS[pStateIdx][qCodIRangeIdx]. +#[rustfmt::skip] +pub(crate) 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-45 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-45 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(crate) 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 +}; + +/// Number of context variables used (0..=1023). +pub const NUM_CTX: usize = 1024; + +pub struct Cabac<'a> { + data: &'a [u8], + /// Byte position of the next byte to load into `value`. + next_byte: usize, + range: u32, + /// codIOffset scaled by 2^bits: value = (codIOffset << bits) | (bits already loaded but not yet + /// consumed by the arithmetic decoder). + value: u64, + bits: u32, + pub ctx: [u8; NUM_CTX], +} + +impl<'a> Cabac<'a> { + /// Create an engine over `data` (slice RBSP) starting at bit `pos` (must be byte aligned), + /// with contexts initialised for `slice_qp` and `init` (0..=2 cabac_init_idc, 3 = I slice). + pub fn new(data: &'a [u8], pos: usize, slice_qp: i32, init: usize) -> Result { + let mut c = Cabac { data, next_byte: pos / 8, range: 0, value: 0, bits: 0, ctx: [0; NUM_CTX] }; + c.init_contexts(slice_qp, init); + c.init_engine()?; + Ok(c) + } + + pub fn init_contexts(&mut self, slice_qp: i32, init: usize) { + let qp = slice_qp.clamp(0, 51); + let tab = &CABAC_INIT_MN[init]; + for (c, &(m, n)) in self.ctx.iter_mut().zip(tab.iter()) { + let pre = (((m as i32) * qp) >> 4) + n as i32; + let pre = pre.clamp(1, 126); + *c = if pre <= 63 { ((63 - pre) << 1) as u8 } else { (((pre - 64) << 1) | 1) as u8 }; + } + // ctxIdx 276 (end_of_slice / I_PCM) is handled by decode_terminate. + } + + /// 9.3.1.2: codIRange = 510, codIOffset = read_bits(9) (from the current byte-aligned position). + pub fn init_engine(&mut self) -> Result<()> { + self.range = 510; + self.value = 0; + self.bits = 0; + self.refill(); + // the top 9 loaded bits form codIOffset; the remaining ones stay buffered below it + self.bits -= 9; + ensure!((self.value >> self.bits) < 510, "invalid CABAC offset at init"); + Ok(()) + } + + /// Load 32 more bits (zeros past the end of the data). + #[inline(always)] + fn refill(&mut self) { + let b = self.next_byte; + let mut w = [0u8; 4]; + if let Some(src) = self.data.get(b..b + 4) { + w.copy_from_slice(src); + } else if b < self.data.len() { + let n = self.data.len() - b; + w[..n].copy_from_slice(&self.data[b..]); + } + self.next_byte += 4; + self.value = (self.value << 32) | u32::from_be_bytes(w) as u64; + self.bits += 32; + } + + /// Bit position of the next bit not consumed by the arithmetic decoder (for I_PCM alignment + /// after decode_terminate returned 1). + pub fn bit_pos(&self) -> usize { + self.next_byte * 8 - self.bits as usize + } + /// Continue reading at byte-aligned bit position `pos` (call `init_engine` afterwards). + pub fn set_bit_pos(&mut self, pos: usize) { + self.next_byte = pos / 8; + self.value = 0; + self.bits = 0; + } + pub fn data(&self) -> &'a [u8] { + self.data + } + /// True when the reader has run past the end of the data (corrupt stream). + pub fn overrun(&self) -> bool { + self.next_byte > self.data.len() + 16 + } + + #[inline(always)] + fn renorm(&mut self) { + // codIRange < 512 always, so the shift is 0 when no renormalisation is needed: no branch on + // the (unpredictable) range, only on the rare refill. + let shift = self.range.leading_zeros() - 23; + self.range <<= shift; + if self.bits < shift { + self.refill(); + } + self.bits -= shift; + } + + /// 9.3.3.2.1, branch-free: the MPS / LPS outcome of a context-coded bin is close to random + /// at high bit rates, so it selects values instead of jumping. + #[inline(always)] + pub fn decode_decision(&mut self, ctx_idx: usize) -> u32 { + // Every ctxIdx is < 1024 and every state < 128: the masks only let the compiler drop the + // bounds checks from the per-bin path. + let ctx_idx = ctx_idx & (NUM_CTX - 1); + let s = (self.ctx[ctx_idx] & 127) as usize; + let q = ((self.range >> 6) & 3) as usize; + let lps = RANGE_TAB_LPS[s >> 1][q] as u32; + let mps_range = self.range - lps; + let scaled = (mps_range as u64) << self.bits; + let is_lps = self.value >= scaled; + self.value -= scaled & (is_lps as u64).wrapping_neg(); + self.range = if is_lps { lps } else { mps_range }; + self.ctx[ctx_idx] = NEXT_STATE[s][is_lps as usize]; + self.renorm(); + (s & 1) as u32 ^ is_lps as u32 + } + + #[inline(always)] + pub fn decode_bypass(&mut self) -> u32 { + if self.bits == 0 { + self.refill(); + } + self.bits -= 1; + let scaled = (self.range as u64) << self.bits; + let one = self.value >= scaled; + self.value -= scaled & (one as u64).wrapping_neg(); + one as u32 + } + + pub fn decode_terminate(&mut self) -> u32 { + self.range -= 2; + let scaled = (self.range as u64) << self.bits; + if self.value >= scaled { + 1 + } else { + self.renorm(); + 0 + } + } +} + +#[cfg(test)] +mod tests { + use super::*; + + /// Minimal CABAC encoder (9.3.4, informative) used to round-trip test the decoder. + struct Enc { + low: u32, + range: u32, + outstanding: u32, + first: bool, + bits: Vec, + ctx: Vec, + } + impl Enc { + fn new(ctx: &[u8]) -> Self { + Enc { low: 0, range: 510, outstanding: 0, first: true, bits: vec![], ctx: ctx.to_vec() } + } + fn put(&mut self, b: bool) { + if self.first { + self.first = false; + } else { + self.bits.push(b); + } + while self.outstanding > 0 { + self.bits.push(!b); + self.outstanding -= 1; + } + } + fn renorm(&mut self) { + while self.range < 256 { + if self.low < 256 { + self.put(false); + } else if self.low >= 512 { + self.low -= 512; + self.put(true); + } else { + self.low -= 256; + self.outstanding += 1; + } + self.range <<= 1; + self.low <<= 1; + } + } + fn encode(&mut self, ctx_idx: usize, bin: u32) { + let s = self.ctx[ctx_idx] as usize; + let p = s >> 1; + let mps = (s & 1) as u32; + let q = ((self.range >> 6) & 3) as usize; + let lps = RANGE_TAB_LPS[p][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(); + } + fn bypass(&mut self, bin: u32) { + self.low <<= 1; + if bin != 0 { + self.low += self.range; + } + if self.low >= 1024 { + self.put(true); + self.low -= 1024; + } else if self.low < 512 { + self.put(false); + } else { + self.low -= 512; + self.outstanding += 1; + } + } + fn terminate_flush(&mut self) -> Vec { + // encode terminate bin = 1 then flush (9.3.4.5) + self.range -= 2; + self.low += self.range; + self.range = 2; + self.renorm(); + self.put((self.low >> 9) & 1 != 0); + self.bits.push((self.low >> 8) & 1 != 0); + self.bits.push(true); // rbsp_stop_one_bit + while !self.bits.len().is_multiple_of(8) { + self.bits.push(false); + } + self.bits.chunks(8).map(|c| c.iter().fold(0u8, |a, &b| (a << 1) | b as u8)).collect() + } + } + + #[test] + fn context_init_formula() { + let c = Cabac::new(&[0, 0, 0, 0], 0, 26, 3).unwrap(); + // ctxIdx 0: m=20, n=-15 at QP 26: ((20*26)>>4) - 15 = 32 - 15 = 17 -> pState 46, MPS 0 + assert_eq!(c.ctx[0], 46 << 1); + // ctxIdx 60: m=0,n=41 -> pre 41 -> pState 22 MPS 0 + assert_eq!(c.ctx[60], 22 << 1); + // ctxIdx 70 (I): m=0 n=11 -> pState 52 MPS 0 + assert_eq!(c.ctx[70], 52 << 1); + } + + #[test] + fn round_trip_random_bins() { + let mut seed = 12345u32; + let mut rnd = || { + seed ^= seed << 13; + seed ^= seed >> 17; + seed ^= seed << 5; + seed + }; + let dummy = Cabac::new(&[0, 0, 0, 0], 0, 30, 0).unwrap(); + let mut enc = Enc::new(&dummy.ctx); + let mut ops = Vec::new(); + for _ in 0..5000 { + let kind = rnd() % 4; + let ctx = (rnd() % 16) as usize; + // skewed bins so contexts adapt + let bin = if rnd() % 10 < 8 { (ctx & 1) as u32 } else { 1 - (ctx & 1) as u32 }; + if kind == 0 { + enc.bypass(bin); + } else { + enc.encode(ctx, bin); + } + ops.push((kind, ctx, bin)); + } + let bytes = enc.terminate_flush(); + let mut dec = Cabac::new(&bytes, 0, 30, 0).unwrap(); + for (i, &(kind, ctx, bin)) in ops.iter().enumerate() { + let got = if kind == 0 { dec.decode_bypass() } else { dec.decode_decision(ctx) }; + assert_eq!(got, bin, "bin {i}"); + } + assert_eq!(dec.decode_terminate(), 1); + } +} diff --git a/crates/h264/src/cabac_mb.rs b/crates/h264/src/cabac_mb.rs new file mode 100644 index 0000000..f1bf6f0 --- /dev/null +++ b/crates/h264/src/cabac_mb.rs @@ -0,0 +1,689 @@ +//! CABAC slice data and macroblock layer parsing (7.3.4, 7.3.5 with 9.3 binarisations and context +//! index derivations). + +use crate::cabac::Cabac; +use crate::error::{Result, ensure, invalid}; +use crate::mbtypes::*; +use crate::picture::MbKind; +use crate::slice::SliceType; +use crate::slicedec::{MbCur, SliceDecoder}; + +/// Table 9-43: significant_coeff_flag ctxIdxInc for 8x8 frame blocks. +#[rustfmt::skip] +const SIG8_FRAME: [u8; 63] = [ + 0, 1, 2, 3, 4, 5, 5, 4, 4, 3, 3, 4, 4, 4, 5, 5, 4, 4, 4, 4, 3, 3, 6, 7, 7, 7, 8, 9, 10, 9, 8, 7, + 7, 6, 11, 12, 13, 11, 6, 7, 8, 9, 14, 10, 9, 8, 6, 11, 12, 13, 11, 6, 9, 14, 10, 9, 11, 12, 13, 11, 14, 10, 12, +]; +/// Table 9-43: last_significant_coeff_flag ctxIdxInc for 8x8 blocks. +#[rustfmt::skip] +const LAST8: [u8; 63] = [ + 0, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, + 3, 3, 3, 3, 3, 3, 3, 3, 4, 4, 4, 4, 4, 4, 4, 4, 5, 5, 5, 5, 6, 6, 6, 6, 7, 7, 7, 7, 8, 8, 8, +]; + +/// ctxIdxInc = levelListIdx (ctxBlockCat 0, 1, 2, 4). +const IDENT_CTX: [u8; 64] = { + let mut t = [0u8; 64]; + let mut i = 0; + while i < 64 { + t[i] = i as u8; + i += 1; + } + t +}; +/// ctxIdxInc = Min(levelListIdx / NumC8x8, 2) for 4:2:0 chroma DC (ctxBlockCat 3). +const CHROMA_DC_CTX: [u8; 4] = [0, 1, 2, 2]; + +/// Table 9-40 ctxIdxBlockCatOffset for ctxBlockCat 0..=5: (coded_block_flag, significant/last, abs level). +const CAT_OFFSET: [(usize, usize, usize); 6] = [(0, 0, 0), (4, 15, 10), (8, 29, 20), (12, 44, 30), (16, 47, 39), (0, 0, 0)]; + +impl SliceDecoder<'_> { + /// Decode slice_data() with CABAC. `rbsp` is the whole slice RBSP. + pub fn decode_cabac(&mut self, rbsp: &[u8]) -> Result<()> { + let start = self.sh.header_bits.div_ceil(8) * 8; + let init = if self.sh.slice_type.is_intra() { 3 } else { self.sh.cabac_init_idc as usize }; + let mut c = Cabac::new(rbsp, start, self.sh.qp(self.pps), init)?; + let total = self.mb_w * self.mb_h; + let mut addr = self.sh.first_mb_in_slice as usize; + let intra_slice = self.sh.slice_type.is_intra(); + loop { + ensure!(addr < total, "macroblock address {addr} out of range"); + self.start_mb(addr); + let skip = !intra_slice && self.decode_skip_flag(&mut c); + if skip { + self.decode_skip()?; + } else { + self.decode_mb_cabac(&mut c)?; + } + self.pic.mb_done(addr); + ensure!(!c.overrun(), "CABAC read past end of slice data"); + if c.decode_terminate() == 1 { + break; + } + addr += 1; + } + Ok(()) + } + + fn decode_skip_flag(&mut self, c: &mut Cabac) -> bool { + let cond = |n: Option| n.map(|m| !self.pic.mbs[m].kind_is_skip()).unwrap_or(false) as usize; + let inc = cond(self.nb[0]) + cond(self.nb[1]); + let base = if self.sh.slice_type.is_b() { 24 } else { 11 }; + c.decode_decision(base + inc) == 1 + } + + /// Intra mb_type suffix / I-slice mb_type (Table 9-36). `base` is 3 (I slice) or 17/32 (prefix + /// offsets in P/B slices). Returns the I-slice mb_type value 0..=25. + fn decode_mb_type_i(&mut self, c: &mut Cabac, base: usize) -> u32 { + let islice = base == 3; + let b0 = if islice { + let cond = |n: Option| n.map(|m| !matches!(self.pic.mbs[m].kind, MbKind::I4x4 | MbKind::I8x8)).unwrap_or(false) as usize; + let inc = cond(self.nb[0]) + cond(self.nb[1]); + c.decode_decision(base + inc) + } else { + c.decode_decision(base) + }; + if b0 == 0 { + return 0; + } + if c.decode_terminate() == 1 { + return 25; + } + let (c_luma, c_chroma, c_chroma2, c_pred0, c_pred1) = + if islice { (base + 3, base + 4, base + 5, base + 6, base + 7) } else { (base + 1, base + 2, base + 2, base + 3, base + 3) }; + let luma = c.decode_decision(c_luma); + let mut chroma = c.decode_decision(c_chroma); + if chroma != 0 { + chroma += c.decode_decision(c_chroma2); + } + let p0 = c.decode_decision(c_pred0); + let p1 = c.decode_decision(c_pred1); + 1 + (p0 * 2 + p1) + 4 * chroma + 12 * luma + } + + fn decode_mb_type(&mut self, c: &mut Cabac) -> Result { + let info = match self.sh.slice_type { + SliceType::I | SliceType::Si => intra_mb_type(self.decode_mb_type_i(c, 3)), + SliceType::P | SliceType::Sp => { + if c.decode_decision(14) == 1 { + intra_mb_type(self.decode_mb_type_i(c, 17)) + } else if c.decode_decision(15) == 0 { + p_mb_type(if c.decode_decision(16) == 0 { 0 } else { 3 }) + } else { + p_mb_type(if c.decode_decision(17) == 1 { 1 } else { 2 }) + } + } + SliceType::B => { + let cond = + |n: Option| n.map(|m| !matches!(self.pic.mbs[m].kind, MbKind::BSkip | MbKind::BDirect16x16)).unwrap_or(false) as usize; + let inc = cond(self.nb[0]) + cond(self.nb[1]); + if c.decode_decision(27 + inc) == 0 { + b_mb_type(0) + } else if c.decode_decision(27 + 3) == 0 { + b_mb_type(1 + c.decode_decision(27 + 5)) + } else { + let b2 = c.decode_decision(27 + 4); + let b3 = c.decode_decision(27 + 5); + let b4 = c.decode_decision(27 + 5); + let b5 = c.decode_decision(27 + 5); + let v = (b2 << 3) | (b3 << 2) | (b4 << 1) | b5; + match v { + 0b1101 => intra_mb_type(self.decode_mb_type_i(c, 32)), + 0b1111 => b_mb_type(22), + 0b1110 => b_mb_type(11), + _ if b2 == 0 => b_mb_type(3 + v), + _ => { + let b6 = c.decode_decision(27 + 5); + b_mb_type(12 + (((v & 7) << 1) | b6)) + } + } + } + } + }; + match info { + Some(i) => Ok(i), + None => invalid("invalid CABAC mb_type"), + } + } + + fn decode_sub_mb_type(&mut self, c: &mut Cabac) -> Result { + let r = if self.sh.slice_type.is_b() { + if c.decode_decision(36) == 0 { + b_sub_mb_type(0) + } else if c.decode_decision(37) == 0 { + b_sub_mb_type(1 + c.decode_decision(39)) + } else if c.decode_decision(38) == 1 { + if c.decode_decision(39) == 1 { + b_sub_mb_type(11 + c.decode_decision(39)) + } else { + let b4 = c.decode_decision(39); + let b5 = c.decode_decision(39); + b_sub_mb_type(7 + (b4 << 1) + b5) + } + } else { + let b3 = c.decode_decision(39); + let b4 = c.decode_decision(39); + b_sub_mb_type(3 + (b3 << 1) + b4) + } + } else if c.decode_decision(21) == 1 { + p_sub_mb_type(0) + } else if c.decode_decision(22) == 0 { + p_sub_mb_type(1) + } else { + p_sub_mb_type(if c.decode_decision(23) == 1 { 2 } else { 3 }) + }; + match r { + Some(s) => Ok(s), + None => invalid("invalid CABAC sub_mb_type"), + } + } + + /// ref_idx_lX for the partition whose top-left 4x4 block is (x, y) (9.3.3.1.1.6). + fn decode_ref_idx(&mut self, c: &mut Cabac, list: usize, x: usize, y: usize) -> u32 { + let cond = |xx: i32, yy: i32| -> usize { + let (mb, bx, by) = if xx < 0 { + (self.nb[0], 3usize, yy as usize) + } else if yy < 0 { + (self.nb[1], xx as usize, 3usize) + } else { + (Some(self.mb_addr), xx as usize, yy as usize) + }; + let Some(m) = mb else { return 0 }; + let st = &self.pic.mbs[m]; + let b8 = (by >> 1) * 2 + (bx >> 1); + if st.kind.is_intra() || st.kind_is_skip() || st.direct8x8 & (1 << b8) != 0 { + return 0; + } + (st.ref_idx[list][b8] > 0) as usize + }; + let inc = cond(x as i32 - 1, y as i32) + 2 * cond(x as i32, y as i32 - 1); + let mut v = 0u32; + if c.decode_decision(54 + inc) == 1 { + v = 1; + if c.decode_decision(54 + 4) == 1 { + v = 2; + while c.decode_decision(54 + 5) == 1 { + v += 1; + if v > 32 { + break; + } + } + } + } + v + } + + /// One mvd component for the (sub-)partition with top-left 4x4 block (x, y) (9.3.3.1.1.7, UEG3). + fn decode_mvd(&mut self, c: &mut Cabac, list: usize, comp: usize, x: usize, y: usize) -> i32 { + let absn = |xx: i32, yy: i32| -> u32 { + let (mb, bx, by) = if xx < 0 { + (self.nb[0], 3usize, yy as usize) + } else if yy < 0 { + (self.nb[1], xx as usize, 3usize) + } else { + (Some(self.mb_addr), xx as usize, yy as usize) + }; + match mb { + Some(m) => self.pic.mbs[m].mvd[list][by * 4 + bx][comp] as u32, + None => 0, + } + }; + let sum = absn(x as i32 - 1, y as i32) + absn(x as i32, y as i32 - 1); + let inc = if sum > 32 { + 2 + } else if sum > 2 { + 1 + } else { + 0 + }; + let base = if comp == 0 { 40 } else { 47 }; + if c.decode_decision(base + inc) == 0 { + return 0; + } + let mut prefix = 1; + while prefix < 9 && c.decode_decision(base + (prefix + 2).min(6)) == 1 { + prefix += 1; + } + let mut val = prefix as i32; + if prefix >= 9 { + // EG3 suffix + let mut k = 3; + while c.decode_bypass() == 1 { + val += 1 << k; + k += 1; + if k > 24 { + break; + } + } + while k > 0 { + k -= 1; + val += (c.decode_bypass() as i32) << k; + } + } + if c.decode_bypass() == 1 { -val } else { val } + } + + fn set_cur_ref(&mut self, list: usize, x: usize, y: usize, w: usize, h: usize, r: i8) { + let st = self.mb_mut(); + for yy in (y..y + h).step_by(2) { + for xx in (x..x + w).step_by(2) { + st.ref_idx[list][(yy >> 1) * 2 + (xx >> 1)] = r; + } + } + } + + fn set_cur_mvd(&mut self, list: usize, x: usize, y: usize, w: usize, h: usize, d: [i32; 2]) { + let a = [d[0].unsigned_abs().min(127) as u8, d[1].unsigned_abs().min(127) as u8]; + let st = self.mb_mut(); + for yy in y..y + h { + for xx in x..x + w { + st.mvd[list][yy * 4 + xx] = a; + } + } + } + + fn decode_cbp(&mut self, c: &mut Cabac) -> u8 { + let mut luma = 0u8; + for b8 in 0..4usize { + let (bx, by) = ((b8 & 1) * 2, (b8 >> 1) * 2); + // A + let ca = if bx > 0 { ((luma >> (b8 - 1)) & 1 == 0) as usize } else { self.cbp_luma_cond(self.nb[0], b8 + 1) }; + let cb = if by > 0 { ((luma >> (b8 - 2)) & 1 == 0) as usize } else { self.cbp_luma_cond(self.nb[1], b8 + 2) }; + let _ = (bx, by); + let bin = c.decode_decision(73 + ca + 2 * cb); + luma |= (bin as u8) << b8; + } + let cond = |n: Option, bin: usize| -> usize { + let Some(m) = n else { return 0 }; + let st = &self.pic.mbs[m]; + if st.kind == MbKind::IPcm { + return 1; + } + if st.kind_is_skip() { + return 0; + } + let ch = st.cbp >> 4; + if bin == 0 { (ch != 0) as usize } else { (ch == 2) as usize } + }; + let inc0 = cond(self.nb[0], 0) + 2 * cond(self.nb[1], 0); + let mut chroma = 0u8; + if c.decode_decision(77 + inc0) == 1 { + let inc1 = cond(self.nb[0], 1) + 2 * cond(self.nb[1], 1) + 4; + chroma = 1 + c.decode_decision(77 + inc1) as u8; + } + luma | (chroma << 4) + } + + /// condTermFlagN for coded_block_pattern luma bin from a neighbouring MB; `b8n` is the neighbouring + /// 8x8 block index within that MB. + fn cbp_luma_cond(&self, n: Option, b8n: usize) -> usize { + let Some(m) = n else { return 0 }; + let st = &self.pic.mbs[m]; + if st.kind == MbKind::IPcm { + return 0; + } + if st.kind_is_skip() { + return 1; + } + ((st.cbp >> (b8n & 3)) & 1 == 0) as usize + } + + fn decode_qp_delta(&mut self, c: &mut Cabac) -> Result { + let inc = self.prev_qp_delta_nz as usize; + if c.decode_decision(60 + inc) == 0 { + return Ok(0); + } + let mut k = 1u32; + let mut ctx = 62; + while c.decode_decision(ctx) == 1 { + k += 1; + ctx = 63; + ensure!(k <= 128, "mb_qp_delta too large"); + } + let v = k.div_ceil(2) as i32; + Ok(if k % 2 == 1 { v } else { -v }) + } + + /// coded_block_flag ctxIdxInc condition for a neighbouring luma 4x4 block (cat 0..2) or chroma. + fn cbf_luma_cond(&self, bx: i32, by: i32, intra_cur: bool) -> usize { + if bx >= 0 && by >= 0 { + return (self.mb().nnz[(by * 4 + bx) as usize] != 0) as usize; + } + let n = if bx < 0 { self.nb[0] } else { self.nb[1] }; + let Some(m) = n else { return intra_cur as usize }; + let st = &self.pic.mbs[m]; + let (x, y) = if bx < 0 { (3, by as usize) } else { (bx as usize, 3) }; + (st.nnz[y * 4 + x] != 0) as usize + } + + fn cbf_dc_cond(&self, n: Option, bit: u8, intra_cur: bool) -> usize { + let Some(m) = n else { return intra_cur as usize }; + let st = &self.pic.mbs[m]; + if st.kind == MbKind::IPcm { + return 1; + } + (st.cbf_dc & (1 << bit) != 0) as usize + } + + fn cbf_chroma_ac_cond(&self, comp: usize, bx: i32, by: i32, intra_cur: bool) -> usize { + if bx >= 0 && by >= 0 { + return (self.mb().nnz_c[comp][(by * 2 + bx) as usize] != 0) as usize; + } + let n = if bx < 0 { self.nb[0] } else { self.nb[1] }; + let Some(m) = n else { return intra_cur as usize }; + let st = &self.pic.mbs[m]; + let (x, y) = if bx < 0 { (1, by as usize) } else { (bx as usize, 1) }; + (st.nnz_c[comp][y * 2 + x] != 0) as usize + } + + /// residual_block_cabac for ctxBlockCat `cat` (0..=5). Coefficients are written to `coeff` + /// in list order (indices 0..max_num). `cbf_inc` is the coded_block_flag ctxIdxInc (ignored for cat 5). + /// Returns the number of non-zero coefficients. + fn residual_block_cabac(&mut self, c: &mut Cabac, coeff: &mut [i32], cat: usize, max_num: usize, cbf_inc: usize) -> u8 { + if cat != 5 && c.decode_decision(85 + CAT_OFFSET[cat].0 + cbf_inc) == 0 { + return 0; + } + let (sig_base, last_base, abs_base) = + if cat == 5 { (402, 417, 426) } else { (105 + CAT_OFFSET[cat].1, 166 + CAT_OFFSET[cat].1, 227 + CAT_OFFSET[cat].2) }; + let (sig_tab, last_tab): (&[u8], &[u8]) = match cat { + 3 => (&CHROMA_DC_CTX, &CHROMA_DC_CTX), + 5 => (&SIG8_FRAME, &LAST8), + _ => (&IDENT_CTX, &IDENT_CTX), + }; + // significance map: positions of the significant coefficients in scan order + let mut pos = [0u8; 64]; + let mut n = 0usize; + let last_i = max_num - 1; + let mut found_last = false; + for i in 0..last_i { + if c.decode_decision(sig_base + sig_tab[i] as usize) == 1 { + pos[n] = i as u8; + n += 1; + if c.decode_decision(last_base + last_tab[i] as usize) == 1 { + found_last = true; + break; + } + } + } + if !found_last { + pos[n] = last_i as u8; + n += 1; + } + let mut eq1 = 0usize; + let mut gt1 = 0usize; + let gt1_cap = if cat == 3 { 3 } else { 4 }; + for &p in pos[..n].iter().rev() { + let inc0 = if gt1 != 0 { 0 } else { (1 + eq1).min(4) }; + let mut abs = 1i32; + if c.decode_decision(abs_base + inc0) == 1 { + let ctx = abs_base + 5 + gt1.min(gt1_cap); + let mut prefix = 1; + while prefix < 14 && c.decode_decision(ctx) == 1 { + prefix += 1; + } + abs = prefix + 1; + if prefix == 14 { + let mut k = 0; + let mut suf = 0i32; + while c.decode_bypass() == 1 { + suf += 1 << k; + k += 1; + if k > 24 { + break; + } + } + while k > 0 { + k -= 1; + suf += (c.decode_bypass() as i32) << k; + } + abs += suf; + } + gt1 += 1; + } else { + eq1 += 1; + } + coeff[p as usize] = if c.decode_bypass() == 1 { -abs } else { abs }; + } + n as u8 + } + + fn residual_cabac(&mut self, c: &mut Cabac, cbp: u8) -> Result<()> { + let kind = self.mb().kind; + let intra = kind.is_intra(); + let t8 = self.mb().transform_8x8; + if kind == MbKind::I16x16 { + let inc = self.cbf_dc_cond(self.nb[0], 0, intra) + 2 * self.cbf_dc_cond(self.nb[1], 0, intra); + let mut lv = [0i32; 16]; + if self.residual_block_cabac(c, &mut lv, 0, 16, inc) > 0 { + self.mb_mut().cbf_dc |= 1; + self.put_luma_dc(&lv); + } + } + for b8 in 0..4 { + let (bx8, by8) = ((b8 & 1) * 2, (b8 >> 1) * 2); + if cbp & (1 << b8) == 0 { + continue; + } + if t8 { + let mut lv = [0i32; 64]; + self.residual_block_cabac(c, &mut lv, 5, 64, 0); + let st = self.mb_mut(); + for r in [by8 * 4 + bx8, by8 * 4 + bx8 + 1, (by8 + 1) * 4 + bx8, (by8 + 1) * 4 + bx8 + 1] { + st.nnz[r] = 1; + } + self.put_luma8(b8, &lv); + } else { + for i4 in 0..4 { + let (bx, by) = (bx8 + (i4 & 1), by8 + (i4 >> 1)); + let raster = by * 4 + bx; + let inc = self.cbf_luma_cond(bx as i32 - 1, by as i32, intra) + 2 * self.cbf_luma_cond(bx as i32, by as i32 - 1, intra); + let mut lv = [0i32; 16]; + let n = if kind == MbKind::I16x16 { + self.residual_block_cabac(c, &mut lv[1..], 1, 15, inc) + } else { + self.residual_block_cabac(c, &mut lv, 2, 16, inc) + }; + self.mb_mut().nnz[raster] = n; + if n > 0 { + self.put_luma4(raster, &lv, kind == MbKind::I16x16); + } + } + } + } + let cbp_c = cbp >> 4; + if cbp_c != 0 { + for comp in 0..2 { + let bit = 1 + comp as u8; + let inc = self.cbf_dc_cond(self.nb[0], bit, intra) + 2 * self.cbf_dc_cond(self.nb[1], bit, intra); + let mut lv = [0i32; 4]; + if self.residual_block_cabac(c, &mut lv, 3, 4, inc) > 0 { + self.mb_mut().cbf_dc |= 1 << bit; + self.put_chroma_dc(comp, &lv); + } + } + } + if cbp_c == 2 { + for comp in 0..2 { + for b in 0..4 { + let (bx, by) = ((b & 1) as i32, (b >> 1) as i32); + let inc = self.cbf_chroma_ac_cond(comp, bx - 1, by, intra) + 2 * self.cbf_chroma_ac_cond(comp, bx, by - 1, intra); + let mut lv = [0i32; 16]; + let n = self.residual_block_cabac(c, &mut lv[1..], 4, 15, inc); + self.mb_mut().nnz_c[comp][b] = n; + if n > 0 { + self.put_chroma_ac(comp, b, &lv); + } + } + } + } + Ok(()) + } + + /// Parse and reconstruct one macroblock_layer() with CABAC. + fn decode_mb_cabac(&mut self, c: &mut Cabac) -> Result<()> { + let mut info = self.decode_mb_type(c)?; + self.cur = MbCur { info, ..Default::default() }; + if info.kind == MbKind::IPcm { + // pcm alignment + samples, then re-initialise the arithmetic decoder + let pos = c.bit_pos().div_ceil(8); + let data = c.data(); + ensure!(data.len() >= pos + 384, "truncated I_PCM macroblock"); + let Some(&samples) = data.get(pos..).and_then(|d| d.first_chunk::<384>()) else { + return invalid("truncated I_PCM macroblock"); + }; + c.set_bit_pos((pos + 384) * 8); + c.init_engine()?; + self.finish_pcm(); + self.write_pcm(&samples); + self.prev_qp_delta_nz = false; + return Ok(()); + } + let intra = info.kind.is_intra(); + if info.kind == MbKind::I4x4 && self.pps.transform_8x8_mode && self.decode_t8x8_flag(c) { + info.kind = MbKind::I8x8; + self.cur.info.kind = MbKind::I8x8; + self.mb_mut().transform_8x8 = true; + } + self.mb_mut().kind = info.kind; + let n_ref = self.sh.num_ref_idx_active; + match info.kind { + MbKind::I4x4 | MbKind::I8x8 => { + let n = if info.kind == MbKind::I8x8 { 4 } else { 16 }; + for i in 0..n { + self.cur.rem_mode[i] = if c.decode_decision(68) == 1 { + -1 + } else { + let b0 = c.decode_decision(69); + let b1 = c.decode_decision(69); + let b2 = c.decode_decision(69); + (b0 | (b1 << 1) | (b2 << 2)) as i8 + }; + } + self.derive_intra_modes(info.kind == MbKind::I8x8); + let m = self.decode_chroma_mode(c); + self.mb_mut().intra_chroma_mode = m; + } + MbKind::I16x16 => { + let m = self.decode_chroma_mode(c); + self.mb_mut().intra_chroma_mode = m; + } + MbKind::BDirect16x16 => { + self.mb_mut().direct8x8 = 0xf; + } + _ => { + if info.part == Part::P8x8 { + for p in 0..4 { + self.cur.sub[p] = self.decode_sub_mb_type(c)?; + } + let direct_mask: u8 = (0..4).filter(|&p| self.cur.sub[p].direct).map(|p| 1u8 << p).sum(); + self.mb_mut().direct8x8 = direct_mask; + for l in 0..2 { + for p in 0..4 { + let sub = self.cur.sub[p]; + if sub.direct || sub.pred & (1 << l) == 0 { + continue; + } + let (x, y) = ((p & 1) * 2, (p >> 1) * 2); + let r = if n_ref[l] > 1 && !info.ref0 { self.decode_ref_idx(c, l, x, y) as i8 } else { 0 }; + self.cur.ref_idx[l][p] = r; + self.set_cur_ref(l, x, y, 2, 2, r); + } + } + for l in 0..2 { + for p in 0..4 { + let sub = self.cur.sub[p]; + if sub.direct || sub.pred & (1 << l) == 0 { + continue; + } + let (x8, y8) = ((p & 1) * 2, (p >> 1) * 2); + for j in 0..sub.shape.num_parts() { + let (sx, sy, w, h) = sub.shape.rect(j); + let (x, y) = (x8 + sx, y8 + sy); + let dx = self.decode_mvd(c, l, 0, x, y); + let dy = self.decode_mvd(c, l, 1, x, y); + self.set_cur_mvd(l, x, y, w, h, [dx, dy]); + self.cur.mvd[l][p * 4 + j] = [dx as i16, dy as i16]; + } + } + } + } else { + let np = info.part.num_parts(); + for l in 0..2 { + for p in 0..np { + if info.pred[p] & (1 << l) == 0 { + continue; + } + let (x, y, w, h) = info.part.rect(p); + let r = if n_ref[l] > 1 { self.decode_ref_idx(c, l, x, y) as i8 } else { 0 }; + self.cur.ref_idx[l][p] = r; + self.set_cur_ref(l, x, y, w, h, r); + } + } + for l in 0..2 { + for p in 0..np { + if info.pred[p] & (1 << l) == 0 { + continue; + } + let (x, y, w, h) = info.part.rect(p); + let dx = self.decode_mvd(c, l, 0, x, y); + let dy = self.decode_mvd(c, l, 1, x, y); + self.set_cur_mvd(l, x, y, w, h, [dx, dy]); + self.cur.mvd[l][p * 4] = [dx as i16, dy as i16]; + } + } + } + } + } + let cbp = if info.kind == MbKind::I16x16 { info.i16_cbp } else { self.decode_cbp(c) }; + self.cur.cbp = cbp; + self.mb_mut().cbp = cbp; + if !intra && cbp & 15 != 0 && self.pps.transform_8x8_mode && self.no_sub_8x8_lt() { + let t = self.decode_t8x8_flag(c); + self.mb_mut().transform_8x8 = t; + } + if !intra { + // ref_idx written during parsing is replaced by the derived motion + self.derive_inter_motion()?; + } + if cbp != 0 || info.kind == MbKind::I16x16 { + let d = self.decode_qp_delta(c)?; + self.apply_qp_delta(d)?; + self.prev_qp_delta_nz = d != 0; + } else { + self.prev_qp_delta_nz = false; + } + self.store_qp(); + self.residual_cabac(c, cbp)?; + if intra { + self.reconstruct_intra(); + } else { + self.predict_inter(); + self.reconstruct_inter_residual(); + } + Ok(()) + } + + fn decode_t8x8_flag(&mut self, c: &mut Cabac) -> bool { + let cond = |n: Option| n.map(|m| self.pic.mbs[m].transform_8x8).unwrap_or(false) as usize; + let inc = cond(self.nb[0]) + cond(self.nb[1]); + c.decode_decision(399 + inc) == 1 + } + + fn decode_chroma_mode(&mut self, c: &mut Cabac) -> u8 { + let cond = |n: Option| { + n.map(|m| { + let st = &self.pic.mbs[m]; + st.kind.is_intra() && st.kind != MbKind::IPcm && st.intra_chroma_mode != 0 + }) + .unwrap_or(false) as usize + }; + let inc = cond(self.nb[0]) + cond(self.nb[1]); + if c.decode_decision(64 + inc) == 0 { + return 0; + } + if c.decode_decision(67) == 0 { + return 1; + } + if c.decode_decision(67) == 0 { 2 } else { 3 } + } +} diff --git a/crates/h264/src/cabac_tables.rs b/crates/h264/src/cabac_tables.rs new file mode 100644 index 0000000..c5c7205 --- /dev/null +++ b/crates/h264/src/cabac_tables.rs @@ -0,0 +1,525 @@ +// Generated from ITU-T H.264 (06/2026) Tables 9-12..9-33 (m, n) by a table-extraction script. +// Index: [0..=2] = cabac_init_idc for P/SP/B slices, [3] = I/SI slices. (0, 0) marks "na". +#[rustfmt::skip] +pub(crate) static CABAC_INIT_MN: [[(i8, i8); 1024]; 4] = [ + [ + (20, -15), (2, 54), (3, 74), (20, -15), (2, 54), (3, 74), (-28, 127), (-23, 104), + (-6, 53), (-1, 54), (7, 51), (23, 33), (23, 2), (21, 0), (1, 9), (0, 49), + (-37, 118), (5, 57), (-13, 78), (-11, 65), (1, 62), (12, 49), (-4, 73), (17, 50), + (18, 64), (9, 43), (29, 0), (26, 67), (16, 90), (9, 104), (-46, 127), (-20, 104), + (1, 67), (-13, 78), (-11, 65), (1, 62), (-6, 86), (-17, 95), (-6, 61), (9, 45), + (-3, 69), (-6, 81), (-11, 96), (6, 55), (7, 67), (-5, 86), (2, 88), (0, 58), + (-3, 76), (-10, 94), (5, 54), (4, 69), (-3, 81), (0, 88), (-7, 67), (-5, 74), + (-4, 74), (-5, 80), (-7, 72), (1, 58), (0, 41), (0, 63), (0, 63), (0, 63), + (-9, 83), (4, 86), (0, 97), (-7, 72), (13, 41), (3, 62), (0, 45), (-4, 78), + (-3, 96), (-27, 126), (-28, 98), (-25, 101), (-23, 67), (-28, 82), (-20, 94), (-16, 83), + (-22, 110), (-21, 91), (-18, 102), (-13, 93), (-29, 127), (-7, 92), (-5, 89), (-7, 96), + (-13, 108), (-3, 46), (-1, 65), (-1, 57), (-9, 93), (-3, 74), (-9, 92), (-8, 87), + (-23, 126), (5, 54), (6, 60), (6, 59), (6, 69), (-1, 48), (0, 68), (-4, 69), + (-8, 88), (-2, 85), (-6, 78), (-1, 75), (-7, 77), (2, 54), (5, 50), (-3, 68), + (1, 50), (6, 42), (-4, 81), (1, 63), (-4, 70), (0, 67), (2, 57), (-2, 76), + (11, 35), (4, 64), (1, 61), (11, 35), (18, 25), (12, 24), (13, 29), (13, 36), + (-10, 93), (-7, 73), (-2, 73), (13, 46), (9, 49), (-7, 100), (9, 53), (2, 53), + (5, 53), (-2, 61), (0, 56), (0, 56), (-13, 63), (-5, 60), (-1, 62), (4, 57), + (-6, 69), (4, 57), (14, 39), (4, 51), (13, 68), (3, 64), (1, 61), (9, 63), + (7, 50), (16, 39), (5, 44), (4, 52), (11, 48), (-5, 60), (-1, 59), (0, 59), + (22, 33), (5, 44), (14, 43), (-1, 78), (0, 60), (9, 69), (11, 28), (2, 40), + (3, 44), (0, 49), (0, 46), (2, 44), (2, 51), (0, 47), (4, 39), (2, 62), + (6, 46), (0, 54), (3, 54), (2, 58), (4, 63), (6, 51), (6, 57), (7, 53), + (6, 52), (6, 55), (11, 45), (14, 36), (8, 53), (-1, 82), (7, 55), (-3, 78), + (15, 46), (22, 31), (-1, 84), (25, 7), (30, -7), (28, 3), (28, 4), (32, 0), + (34, -1), (30, 6), (30, 6), (32, 9), (31, 19), (26, 27), (26, 30), (37, 20), + (28, 34), (17, 70), (1, 67), (5, 59), (9, 67), (16, 30), (18, 32), (18, 35), + (22, 29), (24, 31), (23, 38), (18, 43), (20, 41), (11, 63), (9, 59), (9, 64), + (-1, 94), (-2, 89), (-9, 108), (-6, 76), (-2, 44), (0, 45), (0, 52), (-3, 64), + (-2, 59), (-4, 70), (-4, 75), (-8, 82), (-17, 102), (-9, 77), (3, 24), (0, 42), + (0, 48), (0, 55), (-6, 59), (-7, 71), (-12, 83), (-11, 87), (-30, 119), (1, 58), + (-3, 29), (-1, 36), (1, 38), (2, 43), (-6, 55), (0, 58), (0, 64), (-3, 74), + (-10, 90), (0, 70), (-4, 29), (5, 31), (7, 42), (1, 59), (-2, 58), (-3, 72), + (-3, 81), (-11, 97), (0, 58), (8, 5), (10, 14), (14, 18), (13, 27), (2, 40), + (0, 58), (-3, 70), (-6, 79), (-8, 85), (0, 0), (-13, 106), (-16, 106), (-10, 87), + (-21, 114), (-18, 110), (-14, 98), (-22, 110), (-21, 106), (-18, 103), (-21, 107), (-23, 108), + (-26, 112), (-10, 96), (-12, 95), (-5, 91), (-9, 93), (-22, 94), (-5, 86), (9, 67), + (-4, 80), (-10, 85), (-1, 70), (7, 60), (9, 58), (5, 61), (12, 50), (15, 50), + (18, 49), (17, 54), (10, 41), (7, 46), (-1, 51), (7, 49), (8, 52), (9, 41), + (6, 47), (2, 55), (13, 41), (10, 44), (6, 50), (5, 53), (13, 49), (4, 63), + (6, 64), (-2, 69), (-2, 59), (6, 70), (10, 44), (9, 31), (12, 43), (3, 53), + (14, 34), (10, 38), (-3, 52), (13, 40), (17, 32), (7, 44), (7, 38), (13, 50), + (10, 57), (26, 43), (14, 11), (11, 14), (9, 11), (18, 11), (21, 9), (23, -2), + (32, -15), (32, -15), (34, -21), (39, -23), (42, -33), (41, -31), (46, -28), (38, -12), + (21, 29), (45, -24), (53, -45), (48, -26), (65, -43), (43, -19), (39, -10), (30, 9), + (18, 26), (20, 27), (0, 57), (-14, 82), (-5, 75), (-19, 97), (-35, 125), (27, 0), + (28, 0), (31, -4), (27, 6), (34, 8), (30, 10), (24, 22), (33, 19), (22, 32), + (26, 31), (21, 41), (26, 44), (23, 47), (16, 65), (14, 71), (8, 60), (6, 63), + (17, 65), (21, 24), (23, 20), (26, 23), (27, 32), (28, 23), (28, 24), (23, 40), + (24, 32), (28, 29), (23, 42), (19, 57), (22, 53), (22, 61), (11, 86), (12, 40), + (11, 51), (14, 59), (-4, 79), (-7, 71), (-5, 69), (-9, 70), (-8, 66), (-10, 68), + (-19, 73), (-12, 69), (-16, 70), (-15, 67), (-20, 62), (-19, 70), (-16, 66), (-22, 65), + (-20, 63), (9, -2), (26, -9), (33, -9), (39, -7), (41, -2), (45, 3), (49, 9), + (45, 27), (36, 59), (-6, 66), (-7, 35), (-7, 42), (-8, 45), (-5, 48), (-12, 56), + (-6, 60), (-5, 62), (-8, 66), (-8, 76), (-5, 85), (-6, 81), (-10, 77), (-7, 81), + (-17, 80), (-18, 73), (-4, 74), (-10, 83), (-9, 71), (-9, 67), (-1, 61), (-8, 66), + (-14, 66), (0, 59), (2, 59), (21, -13), (33, -14), (39, -7), (46, -2), (51, 2), + (60, 6), (61, 17), (55, 34), (42, 62), (-7, 92), (-5, 89), (-7, 96), (-13, 108), + (-3, 46), (-1, 65), (-1, 57), (-9, 93), (-3, 74), (-9, 92), (-8, 87), (-23, 126), + (-7, 92), (-5, 89), (-7, 96), (-13, 108), (-3, 46), (-1, 65), (-1, 57), (-9, 93), + (-3, 74), (-9, 92), (-8, 87), (-23, 126), (-2, 85), (-6, 78), (-1, 75), (-7, 77), + (2, 54), (5, 50), (-3, 68), (1, 50), (6, 42), (-4, 81), (1, 63), (-4, 70), + (0, 67), (2, 57), (-2, 76), (11, 35), (4, 64), (1, 61), (11, 35), (18, 25), + (12, 24), (13, 29), (13, 36), (-10, 93), (-7, 73), (-2, 73), (13, 46), (9, 49), + (-7, 100), (9, 53), (2, 53), (5, 53), (-2, 61), (0, 56), (0, 56), (-13, 63), + (-5, 60), (-1, 62), (4, 57), (-6, 69), (4, 57), (14, 39), (4, 51), (13, 68), + (-2, 85), (-6, 78), (-1, 75), (-7, 77), (2, 54), (5, 50), (-3, 68), (1, 50), + (6, 42), (-4, 81), (1, 63), (-4, 70), (0, 67), (2, 57), (-2, 76), (11, 35), + (4, 64), (1, 61), (11, 35), (18, 25), (12, 24), (13, 29), (13, 36), (-10, 93), + (-7, 73), (-2, 73), (13, 46), (9, 49), (-7, 100), (9, 53), (2, 53), (5, 53), + (-2, 61), (0, 56), (0, 56), (-13, 63), (-5, 60), (-1, 62), (4, 57), (-6, 69), + (4, 57), (14, 39), (4, 51), (13, 68), (11, 28), (2, 40), (3, 44), (0, 49), + (0, 46), (2, 44), (2, 51), (0, 47), (4, 39), (2, 62), (6, 46), (0, 54), + (3, 54), (2, 58), (4, 63), (6, 51), (6, 57), (7, 53), (6, 52), (6, 55), + (11, 45), (14, 36), (8, 53), (-1, 82), (7, 55), (-3, 78), (15, 46), (22, 31), + (-1, 84), (25, 7), (30, -7), (28, 3), (28, 4), (32, 0), (34, -1), (30, 6), + (30, 6), (32, 9), (31, 19), (26, 27), (26, 30), (37, 20), (28, 34), (17, 70), + (11, 28), (2, 40), (3, 44), (0, 49), (0, 46), (2, 44), (2, 51), (0, 47), + (4, 39), (2, 62), (6, 46), (0, 54), (3, 54), (2, 58), (4, 63), (6, 51), + (6, 57), (7, 53), (6, 52), (6, 55), (11, 45), (14, 36), (8, 53), (-1, 82), + (7, 55), (-3, 78), (15, 46), (22, 31), (-1, 84), (25, 7), (30, -7), (28, 3), + (28, 4), (32, 0), (34, -1), (30, 6), (30, 6), (32, 9), (31, 19), (26, 27), + (26, 30), (37, 20), (28, 34), (17, 70), (-4, 79), (-7, 71), (-5, 69), (-9, 70), + (-8, 66), (-10, 68), (-19, 73), (-12, 69), (-16, 70), (-15, 67), (-20, 62), (-19, 70), + (-16, 66), (-22, 65), (-20, 63), (-5, 85), (-6, 81), (-10, 77), (-7, 81), (-17, 80), + (-18, 73), (-4, 74), (-10, 83), (-9, 71), (-9, 67), (-1, 61), (-8, 66), (-14, 66), + (0, 59), (2, 59), (9, -2), (26, -9), (33, -9), (39, -7), (41, -2), (45, 3), + (49, 9), (45, 27), (36, 59), (21, -13), (33, -14), (39, -7), (46, -2), (51, 2), + (60, 6), (61, 17), (55, 34), (42, 62), (-6, 66), (-7, 35), (-7, 42), (-8, 45), + (-5, 48), (-12, 56), (-6, 60), (-5, 62), (-8, 66), (-8, 76), (-4, 79), (-7, 71), + (-5, 69), (-9, 70), (-8, 66), (-10, 68), (-19, 73), (-12, 69), (-16, 70), (-15, 67), + (-20, 62), (-19, 70), (-16, 66), (-22, 65), (-20, 63), (-5, 85), (-6, 81), (-10, 77), + (-7, 81), (-17, 80), (-18, 73), (-4, 74), (-10, 83), (-9, 71), (-9, 67), (-1, 61), + (-8, 66), (-14, 66), (0, 59), (2, 59), (9, -2), (26, -9), (33, -9), (39, -7), + (41, -2), (45, 3), (49, 9), (45, 27), (36, 59), (21, -13), (33, -14), (39, -7), + (46, -2), (51, 2), (60, 6), (61, 17), (55, 34), (42, 62), (-6, 66), (-7, 35), + (-7, 42), (-8, 45), (-5, 48), (-12, 56), (-6, 60), (-5, 62), (-8, 66), (-8, 76), + (-13, 106), (-16, 106), (-10, 87), (-21, 114), (-18, 110), (-14, 98), (-22, 110), (-21, 106), + (-18, 103), (-21, 107), (-23, 108), (-26, 112), (-10, 96), (-12, 95), (-5, 91), (-9, 93), + (-22, 94), (-5, 86), (9, 67), (-4, 80), (-10, 85), (-1, 70), (7, 60), (9, 58), + (5, 61), (12, 50), (15, 50), (18, 49), (17, 54), (10, 41), (7, 46), (-1, 51), + (7, 49), (8, 52), (9, 41), (6, 47), (2, 55), (13, 41), (10, 44), (6, 50), + (5, 53), (13, 49), (4, 63), (6, 64), (-13, 106), (-16, 106), (-10, 87), (-21, 114), + (-18, 110), (-14, 98), (-22, 110), (-21, 106), (-18, 103), (-21, 107), (-23, 108), (-26, 112), + (-10, 96), (-12, 95), (-5, 91), (-9, 93), (-22, 94), (-5, 86), (9, 67), (-4, 80), + (-10, 85), (-1, 70), (7, 60), (9, 58), (5, 61), (12, 50), (15, 50), (18, 49), + (17, 54), (10, 41), (7, 46), (-1, 51), (7, 49), (8, 52), (9, 41), (6, 47), + (2, 55), (13, 41), (10, 44), (6, 50), (5, 53), (13, 49), (4, 63), (6, 64), + (14, 11), (11, 14), (9, 11), (18, 11), (21, 9), (23, -2), (32, -15), (32, -15), + (34, -21), (39, -23), (42, -33), (41, -31), (46, -28), (38, -12), (21, 29), (45, -24), + (53, -45), (48, -26), (65, -43), (43, -19), (39, -10), (30, 9), (18, 26), (20, 27), + (0, 57), (-14, 82), (-5, 75), (-19, 97), (-35, 125), (27, 0), (28, 0), (31, -4), + (27, 6), (34, 8), (30, 10), (24, 22), (33, 19), (22, 32), (26, 31), (21, 41), + (26, 44), (23, 47), (16, 65), (14, 71), (14, 11), (11, 14), (9, 11), (18, 11), + (21, 9), (23, -2), (32, -15), (32, -15), (34, -21), (39, -23), (42, -33), (41, -31), + (46, -28), (38, -12), (21, 29), (45, -24), (53, -45), (48, -26), (65, -43), (43, -19), + (39, -10), (30, 9), (18, 26), (20, 27), (0, 57), (-14, 82), (-5, 75), (-19, 97), + (-35, 125), (27, 0), (28, 0), (31, -4), (27, 6), (34, 8), (30, 10), (24, 22), + (33, 19), (22, 32), (26, 31), (21, 41), (26, 44), (23, 47), (16, 65), (14, 71), + (-6, 76), (-2, 44), (0, 45), (0, 52), (-3, 64), (-2, 59), (-4, 70), (-4, 75), + (-8, 82), (-17, 102), (-9, 77), (3, 24), (0, 42), (0, 48), (0, 55), (-6, 59), + (-7, 71), (-12, 83), (-11, 87), (-30, 119), (1, 58), (-3, 29), (-1, 36), (1, 38), + (2, 43), (-6, 55), (0, 58), (0, 64), (-3, 74), (-10, 90), (-6, 76), (-2, 44), + (0, 45), (0, 52), (-3, 64), (-2, 59), (-4, 70), (-4, 75), (-8, 82), (-17, 102), + (-9, 77), (3, 24), (0, 42), (0, 48), (0, 55), (-6, 59), (-7, 71), (-12, 83), + (-11, 87), (-30, 119), (1, 58), (-3, 29), (-1, 36), (1, 38), (2, 43), (-6, 55), + (0, 58), (0, 64), (-3, 74), (-10, 90), (-3, 74), (-9, 92), (-8, 87), (-23, 126), + (-3, 74), (-9, 92), (-8, 87), (-23, 126), (-3, 74), (-9, 92), (-8, 87), (-23, 126), + ], + [ + (20, -15), (2, 54), (3, 74), (20, -15), (2, 54), (3, 74), (-28, 127), (-23, 104), + (-6, 53), (-1, 54), (7, 51), (22, 25), (34, 0), (16, 0), (-2, 9), (4, 41), + (-29, 118), (2, 65), (-6, 71), (-13, 79), (5, 52), (9, 50), (-3, 70), (10, 54), + (26, 34), (19, 22), (40, 0), (57, 2), (41, 36), (26, 69), (-45, 127), (-15, 101), + (-4, 76), (-6, 71), (-13, 79), (5, 52), (6, 69), (-13, 90), (0, 52), (8, 43), + (-2, 69), (-5, 82), (-10, 96), (2, 59), (2, 75), (-3, 87), (-3, 100), (1, 56), + (-3, 74), (-6, 85), (0, 59), (-3, 81), (-7, 86), (-5, 95), (-1, 66), (-1, 77), + (1, 70), (-2, 86), (-5, 72), (0, 61), (0, 41), (0, 63), (0, 63), (0, 63), + (-9, 83), (4, 86), (0, 97), (-7, 72), (13, 41), (3, 62), (13, 15), (7, 51), + (2, 80), (-39, 127), (-18, 91), (-17, 96), (-26, 81), (-35, 98), (-24, 102), (-23, 97), + (-27, 119), (-24, 99), (-21, 110), (-18, 102), (-36, 127), (0, 80), (-5, 89), (-7, 94), + (-4, 92), (0, 39), (0, 65), (-15, 84), (-35, 127), (-2, 73), (-12, 104), (-9, 91), + (-31, 127), (3, 55), (7, 56), (7, 55), (8, 61), (-3, 53), (0, 68), (-7, 74), + (-9, 88), (-13, 103), (-13, 91), (-9, 89), (-14, 92), (-8, 76), (-12, 87), (-23, 110), + (-24, 105), (-10, 78), (-20, 112), (-17, 99), (-78, 127), (-70, 127), (-50, 127), (-46, 127), + (-4, 66), (-5, 78), (-4, 71), (-8, 72), (2, 59), (-1, 55), (-7, 70), (-6, 75), + (-8, 89), (-34, 119), (-3, 75), (32, 20), (30, 22), (-44, 127), (0, 54), (-5, 61), + (0, 58), (-1, 60), (-3, 61), (-8, 67), (-25, 84), (-14, 74), (-5, 65), (5, 52), + (2, 57), (0, 61), (-9, 69), (-11, 70), (18, 55), (-4, 71), (0, 58), (7, 61), + (9, 41), (18, 25), (9, 32), (5, 43), (9, 47), (0, 44), (0, 51), (2, 46), + (19, 38), (-4, 66), (15, 38), (12, 42), (9, 34), (0, 89), (4, 45), (10, 28), + (10, 31), (33, -11), (52, -43), (18, 15), (28, 0), (35, -22), (38, -25), (34, 0), + (39, -18), (32, -12), (102, -94), (0, 0), (56, -15), (33, -4), (29, 10), (37, -5), + (51, -29), (39, -9), (52, -34), (69, -58), (67, -63), (44, -5), (32, 7), (55, -29), + (32, 1), (0, 0), (27, 36), (33, -25), (34, -30), (36, -28), (38, -28), (38, -27), + (34, -18), (35, -16), (34, -14), (32, -8), (37, -6), (35, 0), (30, 10), (28, 18), + (26, 25), (29, 41), (0, 75), (2, 72), (8, 77), (14, 35), (18, 31), (17, 35), + (21, 30), (17, 45), (20, 42), (18, 45), (27, 26), (16, 54), (7, 66), (16, 56), + (11, 73), (10, 67), (-10, 116), (-23, 112), (-15, 71), (-7, 61), (0, 53), (-5, 66), + (-11, 77), (-9, 80), (-9, 84), (-10, 87), (-34, 127), (-21, 101), (-3, 39), (-5, 53), + (-7, 61), (-11, 75), (-15, 77), (-17, 91), (-25, 107), (-25, 111), (-28, 122), (-11, 76), + (-10, 44), (-10, 52), (-10, 57), (-9, 58), (-16, 72), (-7, 69), (-4, 69), (-5, 74), + (-9, 86), (2, 66), (-9, 34), (1, 32), (11, 31), (5, 52), (-2, 55), (-2, 67), + (0, 73), (-8, 89), (3, 52), (7, 4), (10, 8), (17, 8), (16, 19), (3, 37), + (-1, 61), (-5, 73), (-1, 70), (-4, 78), (0, 0), (-21, 126), (-23, 124), (-20, 110), + (-26, 126), (-25, 124), (-17, 105), (-27, 121), (-27, 117), (-17, 102), (-26, 117), (-27, 116), + (-33, 122), (-10, 95), (-14, 100), (-8, 95), (-17, 111), (-28, 114), (-6, 89), (-2, 80), + (-4, 82), (-9, 85), (-8, 81), (-1, 72), (5, 64), (1, 67), (9, 56), (0, 69), + (1, 69), (7, 69), (-7, 69), (-6, 67), (-16, 77), (-2, 64), (2, 61), (-6, 67), + (-3, 64), (2, 57), (-3, 65), (-3, 66), (0, 62), (9, 51), (-1, 66), (-2, 71), + (-2, 75), (-1, 70), (-9, 72), (14, 60), (16, 37), (0, 47), (18, 35), (11, 37), + (12, 41), (10, 41), (2, 48), (12, 41), (13, 41), (0, 59), (3, 50), (19, 40), + (3, 66), (18, 50), (19, -6), (18, -6), (14, 0), (26, -12), (31, -16), (33, -25), + (33, -22), (37, -28), (39, -30), (42, -30), (47, -42), (45, -36), (49, -34), (41, -17), + (32, 9), (69, -71), (63, -63), (66, -64), (77, -74), (54, -39), (52, -35), (41, -10), + (36, 0), (40, -1), (30, 14), (28, 26), (23, 37), (12, 55), (11, 65), (37, -33), + (39, -36), (40, -37), (38, -30), (46, -33), (42, -30), (40, -24), (49, -29), (38, -12), + (40, -10), (38, -3), (46, -5), (31, 20), (29, 30), (25, 44), (12, 48), (11, 49), + (26, 45), (22, 22), (23, 22), (27, 21), (33, 20), (26, 28), (30, 24), (27, 34), + (18, 42), (25, 39), (18, 50), (12, 70), (21, 54), (14, 71), (11, 83), (25, 32), + (21, 49), (21, 54), (-5, 85), (-6, 81), (-10, 77), (-7, 81), (-17, 80), (-18, 73), + (-4, 74), (-10, 83), (-9, 71), (-9, 67), (-1, 61), (-8, 66), (-14, 66), (0, 59), + (2, 59), (17, -10), (32, -13), (42, -9), (49, -5), (53, 0), (64, 3), (68, 10), + (66, 27), (47, 57), (-5, 71), (0, 24), (-1, 36), (-2, 42), (-2, 52), (-9, 57), + (-6, 63), (-4, 65), (-4, 67), (-7, 82), (-3, 81), (-3, 76), (-7, 72), (-6, 78), + (-12, 72), (-14, 68), (-3, 70), (-6, 76), (-5, 66), (-5, 62), (0, 57), (-4, 61), + (-9, 60), (1, 54), (2, 58), (17, -10), (32, -13), (42, -9), (49, -5), (53, 0), + (64, 3), (68, 10), (66, 27), (47, 57), (0, 80), (-5, 89), (-7, 94), (-4, 92), + (0, 39), (0, 65), (-15, 84), (-35, 127), (-2, 73), (-12, 104), (-9, 91), (-31, 127), + (0, 80), (-5, 89), (-7, 94), (-4, 92), (0, 39), (0, 65), (-15, 84), (-35, 127), + (-2, 73), (-12, 104), (-9, 91), (-31, 127), (-13, 103), (-13, 91), (-9, 89), (-14, 92), + (-8, 76), (-12, 87), (-23, 110), (-24, 105), (-10, 78), (-20, 112), (-17, 99), (-78, 127), + (-70, 127), (-50, 127), (-46, 127), (-4, 66), (-5, 78), (-4, 71), (-8, 72), (2, 59), + (-1, 55), (-7, 70), (-6, 75), (-8, 89), (-34, 119), (-3, 75), (32, 20), (30, 22), + (-44, 127), (0, 54), (-5, 61), (0, 58), (-1, 60), (-3, 61), (-8, 67), (-25, 84), + (-14, 74), (-5, 65), (5, 52), (2, 57), (0, 61), (-9, 69), (-11, 70), (18, 55), + (-13, 103), (-13, 91), (-9, 89), (-14, 92), (-8, 76), (-12, 87), (-23, 110), (-24, 105), + (-10, 78), (-20, 112), (-17, 99), (-78, 127), (-70, 127), (-50, 127), (-46, 127), (-4, 66), + (-5, 78), (-4, 71), (-8, 72), (2, 59), (-1, 55), (-7, 70), (-6, 75), (-8, 89), + (-34, 119), (-3, 75), (32, 20), (30, 22), (-44, 127), (0, 54), (-5, 61), (0, 58), + (-1, 60), (-3, 61), (-8, 67), (-25, 84), (-14, 74), (-5, 65), (5, 52), (2, 57), + (0, 61), (-9, 69), (-11, 70), (18, 55), (4, 45), (10, 28), (10, 31), (33, -11), + (52, -43), (18, 15), (28, 0), (35, -22), (38, -25), (34, 0), (39, -18), (32, -12), + (102, -94), (0, 0), (56, -15), (33, -4), (29, 10), (37, -5), (51, -29), (39, -9), + (52, -34), (69, -58), (67, -63), (44, -5), (32, 7), (55, -29), (32, 1), (0, 0), + (27, 36), (33, -25), (34, -30), (36, -28), (38, -28), (38, -27), (34, -18), (35, -16), + (34, -14), (32, -8), (37, -6), (35, 0), (30, 10), (28, 18), (26, 25), (29, 41), + (4, 45), (10, 28), (10, 31), (33, -11), (52, -43), (18, 15), (28, 0), (35, -22), + (38, -25), (34, 0), (39, -18), (32, -12), (102, -94), (0, 0), (56, -15), (33, -4), + (29, 10), (37, -5), (51, -29), (39, -9), (52, -34), (69, -58), (67, -63), (44, -5), + (32, 7), (55, -29), (32, 1), (0, 0), (27, 36), (33, -25), (34, -30), (36, -28), + (38, -28), (38, -27), (34, -18), (35, -16), (34, -14), (32, -8), (37, -6), (35, 0), + (30, 10), (28, 18), (26, 25), (29, 41), (-5, 85), (-6, 81), (-10, 77), (-7, 81), + (-17, 80), (-18, 73), (-4, 74), (-10, 83), (-9, 71), (-9, 67), (-1, 61), (-8, 66), + (-14, 66), (0, 59), (2, 59), (-3, 81), (-3, 76), (-7, 72), (-6, 78), (-12, 72), + (-14, 68), (-3, 70), (-6, 76), (-5, 66), (-5, 62), (0, 57), (-4, 61), (-9, 60), + (1, 54), (2, 58), (17, -10), (32, -13), (42, -9), (49, -5), (53, 0), (64, 3), + (68, 10), (66, 27), (47, 57), (17, -10), (32, -13), (42, -9), (49, -5), (53, 0), + (64, 3), (68, 10), (66, 27), (47, 57), (-5, 71), (0, 24), (-1, 36), (-2, 42), + (-2, 52), (-9, 57), (-6, 63), (-4, 65), (-4, 67), (-7, 82), (-5, 85), (-6, 81), + (-10, 77), (-7, 81), (-17, 80), (-18, 73), (-4, 74), (-10, 83), (-9, 71), (-9, 67), + (-1, 61), (-8, 66), (-14, 66), (0, 59), (2, 59), (-3, 81), (-3, 76), (-7, 72), + (-6, 78), (-12, 72), (-14, 68), (-3, 70), (-6, 76), (-5, 66), (-5, 62), (0, 57), + (-4, 61), (-9, 60), (1, 54), (2, 58), (17, -10), (32, -13), (42, -9), (49, -5), + (53, 0), (64, 3), (68, 10), (66, 27), (47, 57), (17, -10), (32, -13), (42, -9), + (49, -5), (53, 0), (64, 3), (68, 10), (66, 27), (47, 57), (-5, 71), (0, 24), + (-1, 36), (-2, 42), (-2, 52), (-9, 57), (-6, 63), (-4, 65), (-4, 67), (-7, 82), + (-21, 126), (-23, 124), (-20, 110), (-26, 126), (-25, 124), (-17, 105), (-27, 121), (-27, 117), + (-17, 102), (-26, 117), (-27, 116), (-33, 122), (-10, 95), (-14, 100), (-8, 95), (-17, 111), + (-28, 114), (-6, 89), (-2, 80), (-4, 82), (-9, 85), (-8, 81), (-1, 72), (5, 64), + (1, 67), (9, 56), (0, 69), (1, 69), (7, 69), (-7, 69), (-6, 67), (-16, 77), + (-2, 64), (2, 61), (-6, 67), (-3, 64), (2, 57), (-3, 65), (-3, 66), (0, 62), + (9, 51), (-1, 66), (-2, 71), (-2, 75), (-21, 126), (-23, 124), (-20, 110), (-26, 126), + (-25, 124), (-17, 105), (-27, 121), (-27, 117), (-17, 102), (-26, 117), (-27, 116), (-33, 122), + (-10, 95), (-14, 100), (-8, 95), (-17, 111), (-28, 114), (-6, 89), (-2, 80), (-4, 82), + (-9, 85), (-8, 81), (-1, 72), (5, 64), (1, 67), (9, 56), (0, 69), (1, 69), + (7, 69), (-7, 69), (-6, 67), (-16, 77), (-2, 64), (2, 61), (-6, 67), (-3, 64), + (2, 57), (-3, 65), (-3, 66), (0, 62), (9, 51), (-1, 66), (-2, 71), (-2, 75), + (19, -6), (18, -6), (14, 0), (26, -12), (31, -16), (33, -25), (33, -22), (37, -28), + (39, -30), (42, -30), (47, -42), (45, -36), (49, -34), (41, -17), (32, 9), (69, -71), + (63, -63), (66, -64), (77, -74), (54, -39), (52, -35), (41, -10), (36, 0), (40, -1), + (30, 14), (28, 26), (23, 37), (12, 55), (11, 65), (37, -33), (39, -36), (40, -37), + (38, -30), (46, -33), (42, -30), (40, -24), (49, -29), (38, -12), (40, -10), (38, -3), + (46, -5), (31, 20), (29, 30), (25, 44), (19, -6), (18, -6), (14, 0), (26, -12), + (31, -16), (33, -25), (33, -22), (37, -28), (39, -30), (42, -30), (47, -42), (45, -36), + (49, -34), (41, -17), (32, 9), (69, -71), (63, -63), (66, -64), (77, -74), (54, -39), + (52, -35), (41, -10), (36, 0), (40, -1), (30, 14), (28, 26), (23, 37), (12, 55), + (11, 65), (37, -33), (39, -36), (40, -37), (38, -30), (46, -33), (42, -30), (40, -24), + (49, -29), (38, -12), (40, -10), (38, -3), (46, -5), (31, 20), (29, 30), (25, 44), + (-23, 112), (-15, 71), (-7, 61), (0, 53), (-5, 66), (-11, 77), (-9, 80), (-9, 84), + (-10, 87), (-34, 127), (-21, 101), (-3, 39), (-5, 53), (-7, 61), (-11, 75), (-15, 77), + (-17, 91), (-25, 107), (-25, 111), (-28, 122), (-11, 76), (-10, 44), (-10, 52), (-10, 57), + (-9, 58), (-16, 72), (-7, 69), (-4, 69), (-5, 74), (-9, 86), (-23, 112), (-15, 71), + (-7, 61), (0, 53), (-5, 66), (-11, 77), (-9, 80), (-9, 84), (-10, 87), (-34, 127), + (-21, 101), (-3, 39), (-5, 53), (-7, 61), (-11, 75), (-15, 77), (-17, 91), (-25, 107), + (-25, 111), (-28, 122), (-11, 76), (-10, 44), (-10, 52), (-10, 57), (-9, 58), (-16, 72), + (-7, 69), (-4, 69), (-5, 74), (-9, 86), (-2, 73), (-12, 104), (-9, 91), (-31, 127), + (-2, 73), (-12, 104), (-9, 91), (-31, 127), (-2, 73), (-12, 104), (-9, 91), (-31, 127), + ], + [ + (20, -15), (2, 54), (3, 74), (20, -15), (2, 54), (3, 74), (-28, 127), (-23, 104), + (-6, 53), (-1, 54), (7, 51), (29, 16), (25, 0), (14, 0), (-10, 51), (-3, 62), + (-27, 99), (26, 16), (-4, 85), (-24, 102), (5, 57), (6, 57), (-17, 73), (14, 57), + (20, 40), (20, 10), (29, 0), (54, 0), (37, 42), (12, 97), (-32, 127), (-22, 117), + (-2, 74), (-4, 85), (-24, 102), (5, 57), (-6, 93), (-14, 88), (-6, 44), (4, 55), + (-11, 89), (-15, 103), (-21, 116), (19, 57), (20, 58), (4, 84), (6, 96), (1, 63), + (-5, 85), (-13, 106), (5, 63), (6, 75), (-3, 90), (-1, 101), (3, 55), (-4, 79), + (-2, 75), (-12, 97), (-7, 50), (1, 60), (0, 41), (0, 63), (0, 63), (0, 63), + (-9, 83), (4, 86), (0, 97), (-7, 72), (13, 41), (3, 62), (7, 34), (-9, 88), + (-20, 127), (-36, 127), (-17, 91), (-14, 95), (-25, 84), (-25, 86), (-12, 89), (-17, 91), + (-31, 127), (-14, 76), (-18, 103), (-13, 90), (-37, 127), (11, 80), (5, 76), (2, 84), + (5, 78), (-6, 55), (4, 61), (-14, 83), (-37, 127), (-5, 79), (-11, 104), (-11, 91), + (-30, 127), (0, 65), (-2, 79), (0, 72), (-4, 92), (-6, 56), (3, 68), (-8, 71), + (-13, 98), (-4, 86), (-12, 88), (-5, 82), (-3, 72), (-4, 67), (-8, 72), (-16, 89), + (-9, 69), (-1, 59), (5, 66), (4, 57), (-4, 71), (-2, 71), (2, 58), (-1, 74), + (-4, 44), (-1, 69), (0, 62), (-7, 51), (-4, 47), (-6, 42), (-3, 41), (-6, 53), + (8, 76), (-9, 78), (-11, 83), (9, 52), (0, 67), (-5, 90), (1, 67), (-15, 72), + (-5, 75), (-8, 80), (-21, 83), (-21, 64), (-13, 31), (-25, 64), (-29, 94), (9, 75), + (17, 63), (-8, 74), (-5, 35), (-2, 27), (13, 91), (3, 65), (-7, 69), (8, 77), + (-10, 66), (3, 62), (-3, 68), (-20, 81), (0, 30), (1, 7), (-3, 23), (-21, 74), + (16, 66), (-23, 124), (17, 37), (44, -18), (50, -34), (-22, 127), (4, 39), (0, 42), + (7, 34), (11, 29), (8, 31), (6, 37), (7, 42), (3, 40), (8, 33), (13, 43), + (13, 36), (4, 47), (3, 55), (2, 58), (6, 60), (8, 44), (11, 44), (14, 42), + (7, 48), (4, 56), (4, 52), (13, 37), (9, 49), (19, 58), (10, 48), (12, 45), + (0, 69), (20, 33), (8, 63), (35, -18), (33, -25), (28, -3), (24, 10), (27, 0), + (34, -14), (52, -44), (39, -24), (19, 17), (31, 25), (36, 29), (24, 33), (34, 15), + (30, 20), (22, 73), (20, 34), (19, 31), (27, 44), (19, 16), (15, 36), (15, 36), + (21, 28), (25, 21), (30, 20), (31, 12), (27, 16), (24, 42), (0, 93), (14, 56), + (15, 57), (26, 38), (-24, 127), (-24, 115), (-22, 82), (-9, 62), (0, 53), (0, 59), + (-14, 85), (-13, 89), (-13, 94), (-11, 92), (-29, 127), (-21, 100), (-14, 57), (-12, 67), + (-11, 71), (-10, 77), (-21, 85), (-16, 88), (-23, 104), (-15, 98), (-37, 127), (-10, 82), + (-8, 48), (-8, 61), (-8, 66), (-7, 70), (-14, 75), (-10, 79), (-9, 83), (-12, 92), + (-18, 108), (-4, 79), (-22, 69), (-16, 75), (-2, 58), (1, 58), (-13, 78), (-9, 83), + (-4, 81), (-13, 99), (-13, 81), (-6, 38), (-13, 62), (-6, 58), (-2, 59), (-16, 73), + (-10, 76), (-13, 86), (-9, 83), (-10, 87), (0, 0), (-22, 127), (-25, 127), (-25, 120), + (-27, 127), (-19, 114), (-23, 117), (-25, 118), (-26, 117), (-24, 113), (-28, 118), (-31, 120), + (-37, 124), (-10, 94), (-15, 102), (-10, 99), (-13, 106), (-50, 127), (-5, 92), (17, 57), + (-5, 86), (-13, 94), (-12, 91), (-2, 77), (0, 71), (-1, 73), (4, 64), (-7, 81), + (5, 64), (15, 57), (1, 67), (0, 68), (-10, 67), (1, 68), (0, 77), (2, 64), + (0, 68), (-5, 78), (7, 55), (5, 59), (2, 65), (14, 54), (15, 44), (5, 60), + (2, 70), (-2, 76), (-18, 86), (12, 70), (5, 64), (-12, 70), (11, 55), (5, 56), + (0, 69), (2, 65), (-6, 74), (5, 54), (7, 54), (-6, 76), (-11, 82), (-2, 77), + (-2, 77), (25, 42), (17, -13), (16, -9), (17, -12), (27, -21), (37, -30), (41, -40), + (42, -41), (48, -47), (39, -32), (46, -40), (52, -51), (46, -41), (52, -39), (43, -19), + (32, 11), (61, -55), (56, -46), (62, -50), (81, -67), (45, -20), (35, -2), (28, 15), + (34, 1), (39, 1), (30, 17), (20, 38), (18, 45), (15, 54), (0, 79), (36, -16), + (37, -14), (37, -17), (32, 1), (34, 15), (29, 15), (24, 25), (34, 22), (31, 16), + (35, 18), (31, 28), (33, 41), (36, 28), (27, 47), (21, 62), (18, 31), (19, 26), + (36, 24), (24, 23), (27, 16), (24, 30), (31, 29), (22, 41), (22, 42), (16, 60), + (15, 52), (14, 60), (3, 78), (-16, 123), (21, 53), (22, 56), (25, 61), (21, 33), + (19, 50), (17, 61), (-3, 78), (-8, 74), (-9, 72), (-10, 72), (-18, 75), (-12, 71), + (-11, 63), (-5, 70), (-17, 75), (-14, 72), (-16, 67), (-8, 53), (-14, 59), (-9, 52), + (-11, 68), (9, -2), (30, -10), (31, -4), (33, -1), (33, 7), (31, 12), (37, 23), + (31, 38), (20, 64), (-9, 71), (-7, 37), (-8, 44), (-11, 49), (-10, 56), (-12, 59), + (-8, 63), (-9, 67), (-6, 68), (-10, 79), (-3, 78), (-8, 74), (-9, 72), (-10, 72), + (-18, 75), (-12, 71), (-11, 63), (-5, 70), (-17, 75), (-14, 72), (-16, 67), (-8, 53), + (-14, 59), (-9, 52), (-11, 68), (9, -2), (30, -10), (31, -4), (33, -1), (33, 7), + (31, 12), (37, 23), (31, 38), (20, 64), (11, 80), (5, 76), (2, 84), (5, 78), + (-6, 55), (4, 61), (-14, 83), (-37, 127), (-5, 79), (-11, 104), (-11, 91), (-30, 127), + (11, 80), (5, 76), (2, 84), (5, 78), (-6, 55), (4, 61), (-14, 83), (-37, 127), + (-5, 79), (-11, 104), (-11, 91), (-30, 127), (-4, 86), (-12, 88), (-5, 82), (-3, 72), + (-4, 67), (-8, 72), (-16, 89), (-9, 69), (-1, 59), (5, 66), (4, 57), (-4, 71), + (-2, 71), (2, 58), (-1, 74), (-4, 44), (-1, 69), (0, 62), (-7, 51), (-4, 47), + (-6, 42), (-3, 41), (-6, 53), (8, 76), (-9, 78), (-11, 83), (9, 52), (0, 67), + (-5, 90), (1, 67), (-15, 72), (-5, 75), (-8, 80), (-21, 83), (-21, 64), (-13, 31), + (-25, 64), (-29, 94), (9, 75), (17, 63), (-8, 74), (-5, 35), (-2, 27), (13, 91), + (-4, 86), (-12, 88), (-5, 82), (-3, 72), (-4, 67), (-8, 72), (-16, 89), (-9, 69), + (-1, 59), (5, 66), (4, 57), (-4, 71), (-2, 71), (2, 58), (-1, 74), (-4, 44), + (-1, 69), (0, 62), (-7, 51), (-4, 47), (-6, 42), (-3, 41), (-6, 53), (8, 76), + (-9, 78), (-11, 83), (9, 52), (0, 67), (-5, 90), (1, 67), (-15, 72), (-5, 75), + (-8, 80), (-21, 83), (-21, 64), (-13, 31), (-25, 64), (-29, 94), (9, 75), (17, 63), + (-8, 74), (-5, 35), (-2, 27), (13, 91), (4, 39), (0, 42), (7, 34), (11, 29), + (8, 31), (6, 37), (7, 42), (3, 40), (8, 33), (13, 43), (13, 36), (4, 47), + (3, 55), (2, 58), (6, 60), (8, 44), (11, 44), (14, 42), (7, 48), (4, 56), + (4, 52), (13, 37), (9, 49), (19, 58), (10, 48), (12, 45), (0, 69), (20, 33), + (8, 63), (35, -18), (33, -25), (28, -3), (24, 10), (27, 0), (34, -14), (52, -44), + (39, -24), (19, 17), (31, 25), (36, 29), (24, 33), (34, 15), (30, 20), (22, 73), + (4, 39), (0, 42), (7, 34), (11, 29), (8, 31), (6, 37), (7, 42), (3, 40), + (8, 33), (13, 43), (13, 36), (4, 47), (3, 55), (2, 58), (6, 60), (8, 44), + (11, 44), (14, 42), (7, 48), (4, 56), (4, 52), (13, 37), (9, 49), (19, 58), + (10, 48), (12, 45), (0, 69), (20, 33), (8, 63), (35, -18), (33, -25), (28, -3), + (24, 10), (27, 0), (34, -14), (52, -44), (39, -24), (19, 17), (31, 25), (36, 29), + (24, 33), (34, 15), (30, 20), (22, 73), (-3, 78), (-8, 74), (-9, 72), (-10, 72), + (-18, 75), (-12, 71), (-11, 63), (-5, 70), (-17, 75), (-14, 72), (-16, 67), (-8, 53), + (-14, 59), (-9, 52), (-11, 68), (-3, 78), (-8, 74), (-9, 72), (-10, 72), (-18, 75), + (-12, 71), (-11, 63), (-5, 70), (-17, 75), (-14, 72), (-16, 67), (-8, 53), (-14, 59), + (-9, 52), (-11, 68), (9, -2), (30, -10), (31, -4), (33, -1), (33, 7), (31, 12), + (37, 23), (31, 38), (20, 64), (9, -2), (30, -10), (31, -4), (33, -1), (33, 7), + (31, 12), (37, 23), (31, 38), (20, 64), (-9, 71), (-7, 37), (-8, 44), (-11, 49), + (-10, 56), (-12, 59), (-8, 63), (-9, 67), (-6, 68), (-10, 79), (-3, 78), (-8, 74), + (-9, 72), (-10, 72), (-18, 75), (-12, 71), (-11, 63), (-5, 70), (-17, 75), (-14, 72), + (-16, 67), (-8, 53), (-14, 59), (-9, 52), (-11, 68), (-3, 78), (-8, 74), (-9, 72), + (-10, 72), (-18, 75), (-12, 71), (-11, 63), (-5, 70), (-17, 75), (-14, 72), (-16, 67), + (-8, 53), (-14, 59), (-9, 52), (-11, 68), (9, -2), (30, -10), (31, -4), (33, -1), + (33, 7), (31, 12), (37, 23), (31, 38), (20, 64), (9, -2), (30, -10), (31, -4), + (33, -1), (33, 7), (31, 12), (37, 23), (31, 38), (20, 64), (-9, 71), (-7, 37), + (-8, 44), (-11, 49), (-10, 56), (-12, 59), (-8, 63), (-9, 67), (-6, 68), (-10, 79), + (-22, 127), (-25, 127), (-25, 120), (-27, 127), (-19, 114), (-23, 117), (-25, 118), (-26, 117), + (-24, 113), (-28, 118), (-31, 120), (-37, 124), (-10, 94), (-15, 102), (-10, 99), (-13, 106), + (-50, 127), (-5, 92), (17, 57), (-5, 86), (-13, 94), (-12, 91), (-2, 77), (0, 71), + (-1, 73), (4, 64), (-7, 81), (5, 64), (15, 57), (1, 67), (0, 68), (-10, 67), + (1, 68), (0, 77), (2, 64), (0, 68), (-5, 78), (7, 55), (5, 59), (2, 65), + (14, 54), (15, 44), (5, 60), (2, 70), (-22, 127), (-25, 127), (-25, 120), (-27, 127), + (-19, 114), (-23, 117), (-25, 118), (-26, 117), (-24, 113), (-28, 118), (-31, 120), (-37, 124), + (-10, 94), (-15, 102), (-10, 99), (-13, 106), (-50, 127), (-5, 92), (17, 57), (-5, 86), + (-13, 94), (-12, 91), (-2, 77), (0, 71), (-1, 73), (4, 64), (-7, 81), (5, 64), + (15, 57), (1, 67), (0, 68), (-10, 67), (1, 68), (0, 77), (2, 64), (0, 68), + (-5, 78), (7, 55), (5, 59), (2, 65), (14, 54), (15, 44), (5, 60), (2, 70), + (17, -13), (16, -9), (17, -12), (27, -21), (37, -30), (41, -40), (42, -41), (48, -47), + (39, -32), (46, -40), (52, -51), (46, -41), (52, -39), (43, -19), (32, 11), (61, -55), + (56, -46), (62, -50), (81, -67), (45, -20), (35, -2), (28, 15), (34, 1), (39, 1), + (30, 17), (20, 38), (18, 45), (15, 54), (0, 79), (36, -16), (37, -14), (37, -17), + (32, 1), (34, 15), (29, 15), (24, 25), (34, 22), (31, 16), (35, 18), (31, 28), + (33, 41), (36, 28), (27, 47), (21, 62), (17, -13), (16, -9), (17, -12), (27, -21), + (37, -30), (41, -40), (42, -41), (48, -47), (39, -32), (46, -40), (52, -51), (46, -41), + (52, -39), (43, -19), (32, 11), (61, -55), (56, -46), (62, -50), (81, -67), (45, -20), + (35, -2), (28, 15), (34, 1), (39, 1), (30, 17), (20, 38), (18, 45), (15, 54), + (0, 79), (36, -16), (37, -14), (37, -17), (32, 1), (34, 15), (29, 15), (24, 25), + (34, 22), (31, 16), (35, 18), (31, 28), (33, 41), (36, 28), (27, 47), (21, 62), + (-24, 115), (-22, 82), (-9, 62), (0, 53), (0, 59), (-14, 85), (-13, 89), (-13, 94), + (-11, 92), (-29, 127), (-21, 100), (-14, 57), (-12, 67), (-11, 71), (-10, 77), (-21, 85), + (-16, 88), (-23, 104), (-15, 98), (-37, 127), (-10, 82), (-8, 48), (-8, 61), (-8, 66), + (-7, 70), (-14, 75), (-10, 79), (-9, 83), (-12, 92), (-18, 108), (-24, 115), (-22, 82), + (-9, 62), (0, 53), (0, 59), (-14, 85), (-13, 89), (-13, 94), (-11, 92), (-29, 127), + (-21, 100), (-14, 57), (-12, 67), (-11, 71), (-10, 77), (-21, 85), (-16, 88), (-23, 104), + (-15, 98), (-37, 127), (-10, 82), (-8, 48), (-8, 61), (-8, 66), (-7, 70), (-14, 75), + (-10, 79), (-9, 83), (-12, 92), (-18, 108), (-5, 79), (-11, 104), (-11, 91), (-30, 127), + (-5, 79), (-11, 104), (-11, 91), (-30, 127), (-5, 79), (-11, 104), (-11, 91), (-30, 127), + ], + [ + (20, -15), (2, 54), (3, 74), (20, -15), (2, 54), (3, 74), (-28, 127), (-23, 104), + (-6, 53), (-1, 54), (7, 51), (0, 0), (0, 0), (0, 0), (0, 0), (0, 0), + (0, 0), (0, 0), (0, 0), (0, 0), (0, 0), (0, 0), (0, 0), (0, 0), + (0, 0), (0, 0), (0, 0), (0, 0), (0, 0), (0, 0), (0, 0), (0, 0), + (0, 0), (0, 0), (0, 0), (0, 0), (0, 0), (0, 0), (0, 0), (0, 0), + (0, 0), (0, 0), (0, 0), (0, 0), (0, 0), (0, 0), (0, 0), (0, 0), + (0, 0), (0, 0), (0, 0), (0, 0), (0, 0), (0, 0), (0, 0), (0, 0), + (0, 0), (0, 0), (0, 0), (0, 0), (0, 41), (0, 63), (0, 63), (0, 63), + (-9, 83), (4, 86), (0, 97), (-7, 72), (13, 41), (3, 62), (0, 11), (1, 55), + (0, 69), (-17, 127), (-13, 102), (0, 82), (-7, 74), (-21, 107), (-27, 127), (-31, 127), + (-24, 127), (-18, 95), (-27, 127), (-21, 114), (-30, 127), (-17, 123), (-12, 115), (-16, 122), + (-11, 115), (-12, 63), (-2, 68), (-15, 84), (-13, 104), (-3, 70), (-8, 93), (-10, 90), + (-30, 127), (-1, 74), (-6, 97), (-7, 91), (-20, 127), (-4, 56), (-5, 82), (-7, 76), + (-22, 125), (-7, 93), (-11, 87), (-3, 77), (-5, 71), (-4, 63), (-4, 68), (-12, 84), + (-7, 62), (-7, 65), (8, 61), (5, 56), (-2, 66), (1, 64), (0, 61), (-2, 78), + (1, 50), (7, 52), (10, 35), (0, 44), (11, 38), (1, 45), (0, 46), (5, 44), + (31, 17), (1, 51), (7, 50), (28, 19), (16, 33), (14, 62), (-13, 108), (-15, 100), + (-13, 101), (-13, 91), (-12, 94), (-10, 88), (-16, 84), (-10, 86), (-7, 83), (-13, 87), + (-19, 94), (1, 70), (0, 72), (-5, 74), (18, 59), (-8, 102), (-15, 100), (0, 95), + (-4, 75), (2, 72), (-11, 75), (-3, 71), (15, 46), (-13, 69), (0, 62), (0, 65), + (21, 37), (-15, 72), (9, 57), (16, 54), (0, 62), (12, 72), (24, 0), (15, 9), + (8, 25), (13, 18), (15, 9), (13, 19), (10, 37), (12, 18), (6, 29), (20, 33), + (15, 30), (4, 45), (1, 58), (0, 62), (7, 61), (12, 38), (11, 45), (15, 39), + (11, 42), (13, 44), (16, 45), (12, 41), (10, 49), (30, 34), (18, 42), (10, 55), + (17, 51), (17, 46), (0, 89), (26, -19), (22, -17), (26, -17), (30, -25), (28, -20), + (33, -23), (37, -27), (33, -23), (40, -28), (38, -17), (33, -11), (40, -15), (41, -6), + (38, 1), (41, 17), (30, -6), (27, 3), (26, 22), (37, -16), (35, -4), (38, -8), + (38, -3), (37, 3), (38, 5), (42, 0), (35, 16), (39, 22), (14, 48), (27, 37), + (21, 60), (12, 68), (2, 97), (-3, 71), (-6, 42), (-5, 50), (-3, 54), (-2, 62), + (0, 58), (1, 63), (-2, 72), (-1, 74), (-9, 91), (-5, 67), (-5, 27), (-3, 39), + (-2, 44), (0, 46), (-16, 64), (-8, 68), (-10, 78), (-6, 77), (-10, 86), (-12, 92), + (-15, 55), (-10, 60), (-6, 62), (-4, 65), (-12, 73), (-8, 76), (-7, 80), (-9, 88), + (-17, 110), (-11, 97), (-20, 84), (-11, 79), (-6, 73), (-4, 74), (-13, 86), (-13, 96), + (-11, 97), (-19, 117), (-8, 78), (-5, 33), (-4, 48), (-2, 53), (-3, 62), (-13, 71), + (-10, 79), (-12, 86), (-13, 90), (-14, 97), (0, 0), (-6, 93), (-6, 84), (-8, 79), + (0, 66), (-1, 71), (0, 62), (-2, 60), (-2, 59), (-5, 75), (-3, 62), (-4, 58), + (-9, 66), (-1, 79), (0, 71), (3, 68), (10, 44), (-7, 62), (15, 36), (14, 40), + (16, 27), (12, 29), (1, 44), (20, 36), (18, 32), (5, 42), (1, 48), (10, 62), + (17, 46), (9, 64), (-12, 104), (-11, 97), (-16, 96), (-7, 88), (-8, 85), (-7, 85), + (-9, 85), (-13, 88), (4, 66), (-3, 77), (-3, 76), (-6, 76), (10, 58), (-1, 76), + (-1, 83), (-7, 99), (-14, 95), (2, 95), (0, 76), (-5, 74), (0, 70), (-11, 75), + (1, 68), (0, 65), (-14, 73), (3, 62), (4, 62), (-1, 68), (-13, 75), (11, 55), + (5, 64), (12, 70), (15, 6), (6, 19), (7, 16), (12, 14), (18, 13), (13, 11), + (13, 15), (15, 16), (12, 23), (13, 23), (15, 20), (14, 26), (14, 44), (17, 40), + (17, 47), (24, 17), (21, 21), (25, 22), (31, 27), (22, 29), (19, 35), (14, 50), + (10, 57), (7, 63), (-2, 77), (-4, 82), (-3, 94), (9, 69), (-12, 109), (36, -35), + (36, -34), (32, -26), (37, -30), (44, -32), (34, -18), (34, -15), (40, -15), (33, -7), + (35, -5), (33, 0), (38, 2), (33, 13), (23, 35), (13, 58), (29, -3), (26, 0), + (22, 30), (31, -7), (35, -15), (34, -3), (34, 3), (36, -1), (34, 5), (32, 11), + (35, 5), (34, 12), (39, 11), (30, 29), (34, 26), (29, 39), (19, 66), (31, 21), + (31, 31), (25, 50), (-17, 120), (-20, 112), (-18, 114), (-11, 85), (-15, 92), (-14, 89), + (-26, 71), (-15, 81), (-14, 80), (0, 68), (-14, 70), (-24, 56), (-23, 68), (-24, 50), + (-11, 74), (23, -13), (26, -13), (40, -15), (49, -14), (44, 3), (45, 6), (44, 34), + (33, 54), (19, 82), (-3, 75), (-1, 23), (1, 34), (1, 43), (0, 54), (-2, 55), + (0, 61), (1, 64), (0, 68), (-9, 92), (-14, 106), (-13, 97), (-15, 90), (-12, 90), + (-18, 88), (-10, 73), (-9, 79), (-14, 86), (-10, 73), (-10, 70), (-10, 69), (-5, 66), + (-9, 64), (-5, 58), (2, 59), (21, -10), (24, -11), (28, -8), (28, -1), (29, 3), + (29, 9), (35, 20), (29, 36), (14, 67), (-17, 123), (-12, 115), (-16, 122), (-11, 115), + (-12, 63), (-2, 68), (-15, 84), (-13, 104), (-3, 70), (-8, 93), (-10, 90), (-30, 127), + (-17, 123), (-12, 115), (-16, 122), (-11, 115), (-12, 63), (-2, 68), (-15, 84), (-13, 104), + (-3, 70), (-8, 93), (-10, 90), (-30, 127), (-7, 93), (-11, 87), (-3, 77), (-5, 71), + (-4, 63), (-4, 68), (-12, 84), (-7, 62), (-7, 65), (8, 61), (5, 56), (-2, 66), + (1, 64), (0, 61), (-2, 78), (1, 50), (7, 52), (10, 35), (0, 44), (11, 38), + (1, 45), (0, 46), (5, 44), (31, 17), (1, 51), (7, 50), (28, 19), (16, 33), + (14, 62), (-13, 108), (-15, 100), (-13, 101), (-13, 91), (-12, 94), (-10, 88), (-16, 84), + (-10, 86), (-7, 83), (-13, 87), (-19, 94), (1, 70), (0, 72), (-5, 74), (18, 59), + (-7, 93), (-11, 87), (-3, 77), (-5, 71), (-4, 63), (-4, 68), (-12, 84), (-7, 62), + (-7, 65), (8, 61), (5, 56), (-2, 66), (1, 64), (0, 61), (-2, 78), (1, 50), + (7, 52), (10, 35), (0, 44), (11, 38), (1, 45), (0, 46), (5, 44), (31, 17), + (1, 51), (7, 50), (28, 19), (16, 33), (14, 62), (-13, 108), (-15, 100), (-13, 101), + (-13, 91), (-12, 94), (-10, 88), (-16, 84), (-10, 86), (-7, 83), (-13, 87), (-19, 94), + (1, 70), (0, 72), (-5, 74), (18, 59), (24, 0), (15, 9), (8, 25), (13, 18), + (15, 9), (13, 19), (10, 37), (12, 18), (6, 29), (20, 33), (15, 30), (4, 45), + (1, 58), (0, 62), (7, 61), (12, 38), (11, 45), (15, 39), (11, 42), (13, 44), + (16, 45), (12, 41), (10, 49), (30, 34), (18, 42), (10, 55), (17, 51), (17, 46), + (0, 89), (26, -19), (22, -17), (26, -17), (30, -25), (28, -20), (33, -23), (37, -27), + (33, -23), (40, -28), (38, -17), (33, -11), (40, -15), (41, -6), (38, 1), (41, 17), + (24, 0), (15, 9), (8, 25), (13, 18), (15, 9), (13, 19), (10, 37), (12, 18), + (6, 29), (20, 33), (15, 30), (4, 45), (1, 58), (0, 62), (7, 61), (12, 38), + (11, 45), (15, 39), (11, 42), (13, 44), (16, 45), (12, 41), (10, 49), (30, 34), + (18, 42), (10, 55), (17, 51), (17, 46), (0, 89), (26, -19), (22, -17), (26, -17), + (30, -25), (28, -20), (33, -23), (37, -27), (33, -23), (40, -28), (38, -17), (33, -11), + (40, -15), (41, -6), (38, 1), (41, 17), (-17, 120), (-20, 112), (-18, 114), (-11, 85), + (-15, 92), (-14, 89), (-26, 71), (-15, 81), (-14, 80), (0, 68), (-14, 70), (-24, 56), + (-23, 68), (-24, 50), (-11, 74), (-14, 106), (-13, 97), (-15, 90), (-12, 90), (-18, 88), + (-10, 73), (-9, 79), (-14, 86), (-10, 73), (-10, 70), (-10, 69), (-5, 66), (-9, 64), + (-5, 58), (2, 59), (23, -13), (26, -13), (40, -15), (49, -14), (44, 3), (45, 6), + (44, 34), (33, 54), (19, 82), (21, -10), (24, -11), (28, -8), (28, -1), (29, 3), + (29, 9), (35, 20), (29, 36), (14, 67), (-3, 75), (-1, 23), (1, 34), (1, 43), + (0, 54), (-2, 55), (0, 61), (1, 64), (0, 68), (-9, 92), (-17, 120), (-20, 112), + (-18, 114), (-11, 85), (-15, 92), (-14, 89), (-26, 71), (-15, 81), (-14, 80), (0, 68), + (-14, 70), (-24, 56), (-23, 68), (-24, 50), (-11, 74), (-14, 106), (-13, 97), (-15, 90), + (-12, 90), (-18, 88), (-10, 73), (-9, 79), (-14, 86), (-10, 73), (-10, 70), (-10, 69), + (-5, 66), (-9, 64), (-5, 58), (2, 59), (23, -13), (26, -13), (40, -15), (49, -14), + (44, 3), (45, 6), (44, 34), (33, 54), (19, 82), (21, -10), (24, -11), (28, -8), + (28, -1), (29, 3), (29, 9), (35, 20), (29, 36), (14, 67), (-3, 75), (-1, 23), + (1, 34), (1, 43), (0, 54), (-2, 55), (0, 61), (1, 64), (0, 68), (-9, 92), + (-6, 93), (-6, 84), (-8, 79), (0, 66), (-1, 71), (0, 62), (-2, 60), (-2, 59), + (-5, 75), (-3, 62), (-4, 58), (-9, 66), (-1, 79), (0, 71), (3, 68), (10, 44), + (-7, 62), (15, 36), (14, 40), (16, 27), (12, 29), (1, 44), (20, 36), (18, 32), + (5, 42), (1, 48), (10, 62), (17, 46), (9, 64), (-12, 104), (-11, 97), (-16, 96), + (-7, 88), (-8, 85), (-7, 85), (-9, 85), (-13, 88), (4, 66), (-3, 77), (-3, 76), + (-6, 76), (10, 58), (-1, 76), (-1, 83), (-6, 93), (-6, 84), (-8, 79), (0, 66), + (-1, 71), (0, 62), (-2, 60), (-2, 59), (-5, 75), (-3, 62), (-4, 58), (-9, 66), + (-1, 79), (0, 71), (3, 68), (10, 44), (-7, 62), (15, 36), (14, 40), (16, 27), + (12, 29), (1, 44), (20, 36), (18, 32), (5, 42), (1, 48), (10, 62), (17, 46), + (9, 64), (-12, 104), (-11, 97), (-16, 96), (-7, 88), (-8, 85), (-7, 85), (-9, 85), + (-13, 88), (4, 66), (-3, 77), (-3, 76), (-6, 76), (10, 58), (-1, 76), (-1, 83), + (15, 6), (6, 19), (7, 16), (12, 14), (18, 13), (13, 11), (13, 15), (15, 16), + (12, 23), (13, 23), (15, 20), (14, 26), (14, 44), (17, 40), (17, 47), (24, 17), + (21, 21), (25, 22), (31, 27), (22, 29), (19, 35), (14, 50), (10, 57), (7, 63), + (-2, 77), (-4, 82), (-3, 94), (9, 69), (-12, 109), (36, -35), (36, -34), (32, -26), + (37, -30), (44, -32), (34, -18), (34, -15), (40, -15), (33, -7), (35, -5), (33, 0), + (38, 2), (33, 13), (23, 35), (13, 58), (15, 6), (6, 19), (7, 16), (12, 14), + (18, 13), (13, 11), (13, 15), (15, 16), (12, 23), (13, 23), (15, 20), (14, 26), + (14, 44), (17, 40), (17, 47), (24, 17), (21, 21), (25, 22), (31, 27), (22, 29), + (19, 35), (14, 50), (10, 57), (7, 63), (-2, 77), (-4, 82), (-3, 94), (9, 69), + (-12, 109), (36, -35), (36, -34), (32, -26), (37, -30), (44, -32), (34, -18), (34, -15), + (40, -15), (33, -7), (35, -5), (33, 0), (38, 2), (33, 13), (23, 35), (13, 58), + (-3, 71), (-6, 42), (-5, 50), (-3, 54), (-2, 62), (0, 58), (1, 63), (-2, 72), + (-1, 74), (-9, 91), (-5, 67), (-5, 27), (-3, 39), (-2, 44), (0, 46), (-16, 64), + (-8, 68), (-10, 78), (-6, 77), (-10, 86), (-12, 92), (-15, 55), (-10, 60), (-6, 62), + (-4, 65), (-12, 73), (-8, 76), (-7, 80), (-9, 88), (-17, 110), (-3, 71), (-6, 42), + (-5, 50), (-3, 54), (-2, 62), (0, 58), (1, 63), (-2, 72), (-1, 74), (-9, 91), + (-5, 67), (-5, 27), (-3, 39), (-2, 44), (0, 46), (-16, 64), (-8, 68), (-10, 78), + (-6, 77), (-10, 86), (-12, 92), (-15, 55), (-10, 60), (-6, 62), (-4, 65), (-12, 73), + (-8, 76), (-7, 80), (-9, 88), (-17, 110), (-3, 70), (-8, 93), (-10, 90), (-30, 127), + (-3, 70), (-8, 93), (-10, 90), (-30, 127), (-3, 70), (-8, 93), (-10, 90), (-30, 127), + ], +]; diff --git a/crates/h264/src/cavlc.rs b/crates/h264/src/cavlc.rs new file mode 100644 index 0000000..f6aa085 --- /dev/null +++ b/crates/h264/src/cavlc.rs @@ -0,0 +1,281 @@ +//! CAVLC residual block parsing (9.2). + +use crate::cavlc_tables::*; +use crate::error::{Result, ensure}; +use slidecraft_bitstream::BitReader; +use std::sync::OnceLock; + +/// Prefix-code lookup keyed by (number of leading zeros, next 5 bits after the first one bit). +pub(crate) struct VlcTable { + /// entry = (len << 8) | value, 0 = invalid. + entries: Box<[u16; 17 * 32]>, +} + +impl VlcTable { + pub fn build(codes: &[(&str, u8)]) -> Self { + let mut entries = Box::new([0u16; 17 * 32]); + for &(code, value) in codes { + if code.is_empty() { + continue; + } + let len = code.len(); + let lz = code.bytes().take_while(|&b| b == b'0').count(); + let val = ((len as u16) << 8) | value as u16; + if lz == len { + // all-zero code: any continuation with at least `len` zeros + for l in lz..=16 { + for s in 0..32 { + entries[l * 32 + s] = val; + } + } + continue; + } + let suffix = &code[lz + 1..]; + assert!(suffix.len() <= 5, "suffix too long in {code}"); + let sbits = suffix.bytes().fold(0u32, |a, b| (a << 1) | u32::from(b == b'1')); + let free = 5 - suffix.len(); + for ext in 0..(1u32 << free) { + let s = (sbits << free) | ext; + entries[lz * 32 + s as usize] = val; + } + } + Self { entries } + } + + #[inline] + pub fn decode(&self, r: &mut BitReader) -> Result { + let bits = r.peek(32); + let lz = bits.leading_zeros().min(16); + let s = if lz >= 16 { 0 } else { ((bits << lz) << 1) >> 27 }; + let e = self.entries[(lz * 32 + s) as usize]; + ensure!(e != 0, "invalid VLC code"); + r.skip((e >> 8) as usize)?; + Ok(e as u8) + } +} + +struct Tables { + /// coeff_token per nC class; value = (TrailingOnes << 5) | TotalCoeff + coeff_token: [VlcTable; 6], + total_zeros_4x4: [VlcTable; 15], + total_zeros_2x2: [VlcTable; 3], + total_zeros_2x4: [VlcTable; 7], + run_before: [VlcTable; 7], +} + +fn tables() -> &'static Tables { + static T: OnceLock = OnceLock::new(); + T.get_or_init(|| { + let coeff_token = std::array::from_fn(|col| { + let codes: Vec<(&str, u8)> = COEFF_TOKEN.iter().map(|&(t1, tc, ref c)| (c[col], (t1 << 5) | tc)).collect(); + VlcTable::build(&codes) + }); + fn simple(row: &[&'static str; N]) -> VlcTable { + let codes: Vec<(&str, u8)> = row.iter().enumerate().map(|(i, &c)| (c, i as u8)).collect(); + VlcTable::build(&codes) + } + Tables { + coeff_token, + total_zeros_4x4: std::array::from_fn(|i| simple(&TOTAL_ZEROS_4X4[i])), + total_zeros_2x2: std::array::from_fn(|i| simple(&TOTAL_ZEROS_2X2[i])), + total_zeros_2x4: std::array::from_fn(|i| simple(&TOTAL_ZEROS_2X4[i])), + run_before: std::array::from_fn(|i| simple(&RUN_BEFORE[i])), + } + }) +} + +/// Force table construction (so the first slice does not pay for it). +pub(crate) fn init_tables() { + let _ = tables(); +} + +/// Parse residual_block_cavlc(). Coefficients are written to `coeff[start_idx..=end_idx]` in scan order +/// (entries outside the coded range are left untouched; the caller zeroes them). +/// `nc` is the nC value (-1 for 4:2:0 chroma DC, -2 for 4:2:2 chroma DC). Returns TotalCoeff. +pub(crate) fn residual_block(r: &mut BitReader, coeff: &mut [i32], start_idx: usize, end_idx: usize, max_num_coeff: usize, nc: i32) -> Result { + let t = tables(); + let col = match nc { + -1 => 4, + -2 => 5, + 0..=1 => 0, + 2..=3 => 1, + 4..=7 => 2, + _ => 3, + }; + let tok = t.coeff_token[col].decode(r)?; + let total_coeff = (tok & 31) as usize; + let trailing_ones = (tok >> 5) as usize; + if total_coeff == 0 { + return Ok(0); + } + ensure!(total_coeff <= max_num_coeff, "TotalCoeff exceeds maxNumCoeff"); + let mut level = [0i32; 16]; + let mut suffix_length: u32 = if total_coeff > 10 && trailing_ones < 3 { 1 } else { 0 }; + for (i, lv) in level.iter_mut().enumerate().take(total_coeff) { + if i < trailing_ones { + *lv = if r.read_bit()? { -1 } else { 1 }; + continue; + } + // level_prefix + let bits = r.peek(32); + let level_prefix = bits.leading_zeros(); + ensure!(level_prefix <= 25, "level_prefix too large"); + r.skip(level_prefix as usize + 1)?; + let level_suffix_size = if level_prefix == 14 && suffix_length == 0 { + 4 + } else if level_prefix >= 15 { + level_prefix - 3 + } else { + suffix_length + }; + let level_suffix = if level_suffix_size > 0 { r.read_bits(level_suffix_size)? as i32 } else { 0 }; + let mut level_code = ((level_prefix.min(15) as i32) << suffix_length) + level_suffix; + if level_prefix >= 15 && suffix_length == 0 { + level_code += 15; + } + if level_prefix >= 16 { + level_code += (1 << (level_prefix - 3)) - 4096; + } + if i == trailing_ones && trailing_ones < 3 { + level_code += 2; + } + *lv = if level_code % 2 == 0 { (level_code + 2) >> 1 } else { (-level_code - 1) >> 1 }; + if suffix_length == 0 { + suffix_length = 1; + } + if lv.abs() > (3 << (suffix_length - 1)) && suffix_length < 6 { + suffix_length += 1; + } + } + let mut zeros_left = if total_coeff < end_idx - start_idx + 1 { + let tz_idx = total_coeff - 1; + let tz = match max_num_coeff { + 4 => t.total_zeros_2x2[tz_idx].decode(r)?, + 8 => t.total_zeros_2x4[tz_idx].decode(r)?, + _ => t.total_zeros_4x4[tz_idx].decode(r)?, + } as usize; + ensure!(tz + total_coeff <= end_idx - start_idx + 1, "total_zeros out of range"); + tz + } else { + 0 + }; + // runs and placement: highest-frequency coefficient first + let mut pos = start_idx + total_coeff + zeros_left - 1; + for (i, &lv) in level.iter().enumerate().take(total_coeff) { + coeff[pos] = lv; + if i + 1 == total_coeff { + break; + } + let run = if zeros_left > 0 { + let run = t.run_before[zeros_left.min(7) - 1].decode(r)? as usize; + ensure!(run <= zeros_left, "run_before out of range"); + zeros_left -= run; + run + } else { + 0 + }; + pos -= 1 + run; + } + Ok(total_coeff as u8) +} + +#[cfg(test)] +mod tests { + use super::*; + use slidecraft_bitstream::BitWriter; + + fn kraft(codes: &[&str]) -> f64 { + codes.iter().filter(|c| !c.is_empty()).map(|c| 0.5f64.powi(c.len() as i32)).sum() + } + + fn prefix_free(codes: &[&str]) -> bool { + let c: Vec<&&str> = codes.iter().filter(|c| !c.is_empty()).collect(); + for (i, a) in c.iter().enumerate() { + for (j, b) in c.iter().enumerate() { + if i != j && b.starts_with(**a) { + return false; + } + } + } + true + } + + #[test] + fn coeff_token_tables_are_prefix_codes() { + for col in 0..6 { + let codes: Vec<&str> = COEFF_TOKEN.iter().map(|e| e.2[col]).collect(); + assert!(prefix_free(&codes), "column {col}"); + let k = kraft(&codes); + assert!(k <= 1.0 + 1e-12, "column {col} kraft {k}"); + } + // nC>=8 is a complete 6-bit fixed code except the unused 000011 pattern + let codes: Vec<&str> = COEFF_TOKEN.iter().map(|e| e.2[3]).collect(); + assert!(codes.iter().all(|c| c.len() == 6)); + } + + #[test] + fn total_zeros_and_run_before_are_complete_prefix_codes() { + for (i, row) in TOTAL_ZEROS_4X4.iter().enumerate() { + assert!(prefix_free(row)); + // tzVlcIndex 1 leaves the all-zero 9-bit code unused; all others are complete + let k = kraft(row); + if i == 0 { + assert!((k - (1.0 - 1.0 / 512.0)).abs() < 1e-12, "{row:?}"); + } else { + assert!((k - 1.0).abs() < 1e-12, "{row:?}"); + } + } + for row in TOTAL_ZEROS_2X2.iter() { + assert!(prefix_free(row)); + assert!((kraft(row) - 1.0).abs() < 1e-12); + } + for row in TOTAL_ZEROS_2X4.iter() { + assert!(prefix_free(row)); + assert!((kraft(row) - 1.0).abs() < 1e-12); + } + for row in RUN_BEFORE.iter() { + assert!(prefix_free(row)); + } + } + + fn write_code(w: &mut BitWriter, code: &str) { + for b in code.bytes() { + w.write_bit(b == b'1'); + } + } + + #[test] + fn decodes_spec_style_example() { + // Block: 0 3 -1 0 | 0 -1 1 0 | 1 0 0 0 | 0 0 0 0 (zig-zag order), nC = 0. + // TotalCoeff 5, TrailingOnes 3: coeff_token 0000 100. + // trailing ones (reverse order): +1, -1, -1 -> sign bits 0 1 1 + // level -1? No: remaining levels (reverse): 1 (idx 4?) ... use the classic example: + // coefficients in scan order: [0, 3, -1, 0, 0, -1, 1, 0, 1, 0, ...] + // reverse order non-zero: 1 (pos 8), 1 (pos 6), -1 (pos 5), -1 (pos 2), 3 (pos 1) + // TotalCoeff 5, TrailingOnes 3 (1, 1, -1). + let mut w = BitWriter::new(); + write_code(&mut w, "0000100"); // coeff_token TC=5 T1=3, nC 0..2 + write_code(&mut w, "0"); // +1 + write_code(&mut w, "0"); // +1 + write_code(&mut w, "1"); // -1 + // level -1 with suffixLength 0: levelCode = 1 (T1 == 3 so no +2) -> prefix 1 => "01" + write_code(&mut w, "01"); + // level 3 with suffixLength 1: levelCode = 4 -> prefix 2, suffix 0 => "001" "0" + write_code(&mut w, "0010"); + // total_zeros = 4 (positions 0, 3, 4, 7) with tzVlcIndex 5 -> "110" + write_code(&mut w, "110"); + // run_before: zerosLeft 4 at pos 8: run 1 -> "10"; zerosLeft 3 at pos 6: run 0 -> "11"; + // zerosLeft 3 at pos 5: run 2 -> "01"; zerosLeft 1 at pos 2: run 0 -> "1" + write_code(&mut w, "10"); + write_code(&mut w, "11"); + write_code(&mut w, "01"); + write_code(&mut w, "1"); + w.rbsp_trailing(); + let b = w.finish(); + let mut r = BitReader::new(&b); + let mut c = [0i32; 16]; + let tc = residual_block(&mut r, &mut c, 0, 15, 16, 0).unwrap(); + assert_eq!(tc, 5); + assert_eq!(&c[..10], &[0, 3, -1, 0, 0, -1, 1, 0, 1, 0]); + } +} diff --git a/crates/h264/src/cavlc_tables.rs b/crates/h264/src/cavlc_tables.rs new file mode 100644 index 0000000..ba509e2 --- /dev/null +++ b/crates/h264/src/cavlc_tables.rs @@ -0,0 +1,117 @@ +// Generated from ITU-T H.264 (06/2026) Tables 9-5, 9-7..9-10 by a table-extraction script. +// Codes are bit strings; "" marks an unused entry. + +/// (TrailingOnes, TotalCoeff, [nC 0..2, 2..4, 4..8, 8.., -1, -2]) +#[rustfmt::skip] +pub(crate) static COEFF_TOKEN: [(u8, u8, [&str; 6]); 62] = [ + (0, 0, ["1", "11", "1111", "000011", "01", "1"]), + (0, 1, ["000101", "001011", "001111", "000000", "000111", "0001111"]), + (1, 1, ["01", "10", "1110", "000001", "1", "01"]), + (0, 2, ["00000111", "000111", "001011", "000100", "000100", "0001110"]), + (1, 2, ["000100", "00111", "01111", "000101", "000110", "0001101"]), + (2, 2, ["001", "011", "1101", "000110", "001", "001"]), + (0, 3, ["000000111", "0000111", "001000", "001000", "000011", "000000111"]), + (1, 3, ["00000110", "001010", "01100", "001001", "0000011", "0001100"]), + (2, 3, ["0000101", "001001", "01110", "001010", "0000010", "0001011"]), + (3, 3, ["00011", "0101", "1100", "001011", "000101", "00001"]), + (0, 4, ["0000000111", "00000111", "0001111", "001100", "000010", "000000110"]), + (1, 4, ["000000110", "000110", "01010", "001101", "00000011", "000000101"]), + (2, 4, ["00000101", "000101", "01011", "001110", "00000010", "0001010"]), + (3, 4, ["000011", "0100", "1011", "001111", "0000000", "000001"]), + (0, 5, ["00000000111", "00000100", "0001011", "010000", "", "0000000111"]), + (1, 5, ["0000000110", "0000110", "01000", "010001", "", "0000000110"]), + (2, 5, ["000000101", "0000101", "01001", "010010", "", "000000100"]), + (3, 5, ["0000100", "00110", "1010", "010011", "", "0001001"]), + (0, 6, ["0000000001111", "000000111", "0001001", "010100", "", "00000000111"]), + (1, 6, ["00000000110", "00000110", "001110", "010101", "", "00000000110"]), + (2, 6, ["0000000101", "00000101", "001101", "010110", "", "0000000101"]), + (3, 6, ["00000100", "001000", "1001", "010111", "", "0001000"]), + (0, 7, ["0000000001011", "00000001111", "0001000", "011000", "", "000000000111"]), + (1, 7, ["0000000001110", "000000110", "001010", "011001", "", "000000000110"]), + (2, 7, ["00000000101", "000000101", "001001", "011010", "", "00000000101"]), + (3, 7, ["000000100", "000100", "1000", "011011", "", "0000000100"]), + (0, 8, ["0000000001000", "00000001011", "00001111", "011100", "", "0000000000111"]), + (1, 8, ["0000000001010", "00000001110", "0001110", "011101", "", "000000000101"]), + (2, 8, ["0000000001101", "00000001101", "0001101", "011110", "", "000000000100"]), + (3, 8, ["0000000100", "0000100", "01101", "011111", "", "00000000100"]), + (0, 9, ["00000000001111", "000000001111", "00001011", "100000", "", ""]), + (1, 9, ["00000000001110", "00000001010", "00001110", "100001", "", ""]), + (2, 9, ["0000000001001", "00000001001", "0001010", "100010", "", ""]), + (3, 9, ["00000000100", "000000100", "001100", "100011", "", ""]), + (0, 10, ["00000000001011", "000000001011", "000001111", "100100", "", ""]), + (1, 10, ["00000000001010", "000000001110", "00001010", "100101", "", ""]), + (2, 10, ["00000000001101", "000000001101", "00001101", "100110", "", ""]), + (3, 10, ["0000000001100", "00000001100", "0001100", "100111", "", ""]), + (0, 11, ["000000000001111", "000000001000", "000001011", "101000", "", ""]), + (1, 11, ["000000000001110", "000000001010", "000001110", "101001", "", ""]), + (2, 11, ["00000000001001", "000000001001", "00001001", "101010", "", ""]), + (3, 11, ["00000000001100", "00000001000", "00001100", "101011", "", ""]), + (0, 12, ["000000000001011", "0000000001111", "000001000", "101100", "", ""]), + (1, 12, ["000000000001010", "0000000001110", "000001010", "101101", "", ""]), + (2, 12, ["000000000001101", "0000000001101", "000001101", "101110", "", ""]), + (3, 12, ["00000000001000", "000000001100", "00001000", "101111", "", ""]), + (0, 13, ["0000000000001111", "0000000001011", "0000001101", "110000", "", ""]), + (1, 13, ["000000000000001", "0000000001010", "000000111", "110001", "", ""]), + (2, 13, ["000000000001001", "0000000001001", "000001001", "110010", "", ""]), + (3, 13, ["000000000001100", "0000000001100", "000001100", "110011", "", ""]), + (0, 14, ["0000000000001011", "0000000000111", "0000001001", "110100", "", ""]), + (1, 14, ["0000000000001110", "00000000001011", "0000001100", "110101", "", ""]), + (2, 14, ["0000000000001101", "0000000000110", "0000001011", "110110", "", ""]), + (3, 14, ["000000000001000", "0000000001000", "0000001010", "110111", "", ""]), + (0, 15, ["0000000000000111", "00000000001001", "0000000101", "111000", "", ""]), + (1, 15, ["0000000000001010", "00000000001000", "0000001000", "111001", "", ""]), + (2, 15, ["0000000000001001", "00000000001010", "0000000111", "111010", "", ""]), + (3, 15, ["0000000000001100", "0000000000001", "0000000110", "111011", "", ""]), + (0, 16, ["0000000000000100", "00000000000111", "0000000001", "111100", "", ""]), + (1, 16, ["0000000000000110", "00000000000110", "0000000100", "111101", "", ""]), + (2, 16, ["0000000000000101", "00000000000101", "0000000011", "111110", "", ""]), + (3, 16, ["0000000000001000", "00000000000100", "0000000010", "111111", "", ""]), +]; +/// total_zeros for 4x4 blocks: [tzVlcIndex-1][total_zeros] +#[rustfmt::skip] +pub(crate) static TOTAL_ZEROS_4X4: [[&str; 16]; 15] = [ + ["1", "011", "010", "0011", "0010", "00011", "00010", "000011", "000010", "0000011", "0000010", "00000011", "00000010", "000000011", "000000010", "000000001"], + ["111", "110", "101", "100", "011", "0101", "0100", "0011", "0010", "00011", "00010", "000011", "000010", "000001", "000000", ""], + ["0101", "111", "110", "101", "0100", "0011", "100", "011", "0010", "00011", "00010", "000001", "00001", "000000", "", ""], + ["00011", "111", "0101", "0100", "110", "101", "100", "0011", "011", "0010", "00010", "00001", "00000", "", "", ""], + ["0101", "0100", "0011", "111", "110", "101", "100", "011", "0010", "00001", "0001", "00000", "", "", "", ""], + ["000001", "00001", "111", "110", "101", "100", "011", "010", "0001", "001", "000000", "", "", "", "", ""], + ["000001", "00001", "101", "100", "011", "11", "010", "0001", "001", "000000", "", "", "", "", "", ""], + ["000001", "0001", "00001", "011", "11", "10", "010", "001", "000000", "", "", "", "", "", "", ""], + ["000001", "000000", "0001", "11", "10", "001", "01", "00001", "", "", "", "", "", "", "", ""], + ["00001", "00000", "001", "11", "10", "01", "0001", "", "", "", "", "", "", "", "", ""], + ["0000", "0001", "001", "010", "1", "011", "", "", "", "", "", "", "", "", "", ""], + ["0000", "0001", "01", "1", "001", "", "", "", "", "", "", "", "", "", "", ""], + ["000", "001", "1", "01", "", "", "", "", "", "", "", "", "", "", "", ""], + ["00", "01", "1", "", "", "", "", "", "", "", "", "", "", "", "", ""], + ["0", "1", "", "", "", "", "", "", "", "", "", "", "", "", "", ""], +]; +/// total_zeros for chroma DC 2x2: [tzVlcIndex-1][total_zeros] +#[rustfmt::skip] +pub(crate) static TOTAL_ZEROS_2X2: [[&str; 4]; 3] = [ + ["1", "01", "001", "000"], + ["1", "01", "00", ""], + ["1", "0", "", ""], +]; +/// total_zeros for chroma DC 2x4: [tzVlcIndex-1][total_zeros] +#[rustfmt::skip] +pub(crate) static TOTAL_ZEROS_2X4: [[&str; 8]; 7] = [ + ["1", "010", "011", "0010", "0011", "0001", "00001", "00000"], + ["000", "01", "001", "100", "101", "110", "111", ""], + ["000", "001", "01", "10", "110", "111", "", ""], + ["110", "00", "01", "10", "111", "", "", ""], + ["00", "01", "10", "11", "", "", "", ""], + ["00", "01", "1", "", "", "", "", ""], + ["0", "1", "", "", "", "", "", ""], +]; +/// run_before: [min(zerosLeft,7)-1][run_before] +#[rustfmt::skip] +pub(crate) static RUN_BEFORE: [[&str; 15]; 7] = [ + ["1", "0", "", "", "", "", "", "", "", "", "", "", "", "", ""], + ["1", "01", "00", "", "", "", "", "", "", "", "", "", "", "", ""], + ["11", "10", "01", "00", "", "", "", "", "", "", "", "", "", "", ""], + ["11", "10", "01", "001", "000", "", "", "", "", "", "", "", "", "", ""], + ["11", "10", "011", "010", "001", "000", "", "", "", "", "", "", "", "", ""], + ["11", "000", "001", "011", "010", "101", "100", "", "", "", "", "", "", "", ""], + ["111", "110", "101", "100", "011", "010", "001", "0001", "00001", "000001", "0000001", "00000001", "000000001", "0000000001", "00000000001"], +]; diff --git a/crates/h264/src/deblock.rs b/crates/h264/src/deblock.rs new file mode 100644 index 0000000..fb918ff --- /dev/null +++ b/crates/h264/src/deblock.rs @@ -0,0 +1,554 @@ +//! Deblocking filter (8.7) for progressive frames, 4:2:0, 8-bit. + +use crate::picture::{MbKind, MbState}; +use crate::slicedec::{PicState, SliceInfo}; +use crate::tables::{ALPHA, BETA, TC0}; + +/// Reference picture identity + mvs of one 4x4 block, for bS = 1 decisions. +#[derive(Clone, Copy)] +struct Motion { + ids: [u32; 2], + mv: [[i16; 2]; 2], + count: u8, +} + +/// Referenced picture ids per 8x8 block and list (u32::MAX = list unused). +fn ref_ids8(st: &MbState, sl: &SliceInfo) -> [[u32; 2]; 4] { + std::array::from_fn(|b8| { + std::array::from_fn(|l| { + let r = st.ref_idx[l][b8]; + if r >= 0 { sl.ref_ids[l].get(r as usize).copied().unwrap_or(u32::MAX - 1) } else { u32::MAX } + }) + }) +} + +#[inline(always)] +fn block_motion(st: &MbState, ids8: &[[u32; 2]; 4], raster: usize) -> Motion { + let b8 = (raster >> 3) * 2 + ((raster & 3) >> 1); + let ids = ids8[b8]; + let count = (ids[0] != u32::MAX) as u8 + (ids[1] != u32::MAX) as u8; + Motion { ids, mv: [st.mv[0][raster], st.mv[1][raster]], count } +} + +#[inline(always)] +fn mv_far(a: [i16; 2], b: [i16; 2]) -> bool { + (a[0] as i32 - b[0] as i32).abs() >= 4 || (a[1] as i32 - b[1] as i32).abs() >= 4 +} + +#[inline] +fn motion_bs(p: &Motion, q: &Motion) -> u8 { + if p.ids == q.ids && p.mv == q.mv { + return 0; + } + if p.count != q.count { + return 1; + } + if p.count == 1 { + let (pi, pm) = if p.ids[0] != u32::MAX { (p.ids[0], p.mv[0]) } else { (p.ids[1], p.mv[1]) }; + let (qi, qm) = if q.ids[0] != u32::MAX { (q.ids[0], q.mv[0]) } else { (q.ids[1], q.mv[1]) }; + if pi != qi { + return 1; + } + return mv_far(pm, qm) as u8; + } + if p.count == 0 { + return 0; + } + // two motion vectors each + let same_set = (p.ids[0] == q.ids[0] && p.ids[1] == q.ids[1]) || (p.ids[0] == q.ids[1] && p.ids[1] == q.ids[0]); + if !same_set { + return 1; + } + if p.ids[0] != p.ids[1] { + // two different reference pictures: compare mvs referring to the same picture + if p.ids[0] == q.ids[0] { + (mv_far(p.mv[0], q.mv[0]) || mv_far(p.mv[1], q.mv[1])) as u8 + } else { + (mv_far(p.mv[0], q.mv[1]) || mv_far(p.mv[1], q.mv[0])) as u8 + } + } else { + // both mvs refer to the same picture + ((mv_far(p.mv[0], q.mv[0]) || mv_far(p.mv[1], q.mv[1])) && (mv_far(p.mv[0], q.mv[1]) || mv_far(p.mv[1], q.mv[0]))) as u8 + } +} + +#[cfg(test)] +fn clip3(lo: i32, hi: i32, v: i32) -> i32 { + v.clamp(lo, hi) +} + +/// Filter one set of eight samples p3 p2 p1 p0 | q0 q1 q2 q3 across an edge (8.7.2.3 / 8.7.2.4): +/// the per-line reference for [`filter_lanes`]. +#[cfg(test)] +fn filter8(v: &mut [u8; 8], bs: u8, alpha: i32, beta: i32, tc0: i32, chroma: bool) { + let p0 = v[3] as i32; + let q0 = v[4] as i32; + let p1 = v[2] as i32; + let q1 = v[5] as i32; + if (p0 - q0).abs() >= alpha || (p1 - p0).abs() >= beta || (q1 - q0).abs() >= beta { + return; + } + if chroma { + if bs < 4 { + let tc = tc0 + 1; + let delta = clip3(-tc, tc, (((q0 - p0) << 2) + (p1 - q1) + 4) >> 3); + v[3] = (p0 + delta).clamp(0, 255) as u8; + v[4] = (q0 - delta).clamp(0, 255) as u8; + } else { + v[3] = ((2 * p1 + p0 + q1 + 2) >> 2) as u8; + v[4] = ((2 * q1 + q0 + p1 + 2) >> 2) as u8; + } + return; + } + let p2 = v[1] as i32; + let q2 = v[6] as i32; + let ap = (p2 - p0).abs(); + let aq = (q2 - q0).abs(); + if bs < 4 { + let tc = tc0 + (ap < beta) as i32 + (aq < beta) as i32; + let delta = clip3(-tc, tc, (((q0 - p0) << 2) + (p1 - q1) + 4) >> 3); + v[3] = (p0 + delta).clamp(0, 255) as u8; + v[4] = (q0 - delta).clamp(0, 255) as u8; + if ap < beta { + v[2] = (p1 + clip3(-tc0, tc0, (p2 + ((p0 + q0 + 1) >> 1) - (p1 << 1)) >> 1)) as u8; + } + if aq < beta { + v[5] = (q1 + clip3(-tc0, tc0, (q2 + ((p0 + q0 + 1) >> 1) - (q1 << 1)) >> 1)) as u8; + } + } else { + let strong = (p0 - q0).abs() < ((alpha >> 2) + 2); + if ap < beta && strong { + let p3 = v[0] as i32; + v[3] = ((p2 + 2 * p1 + 2 * p0 + 2 * q0 + q1 + 4) >> 3) as u8; + v[2] = ((p2 + p1 + p0 + q0 + 2) >> 2) as u8; + v[1] = ((2 * p3 + 3 * p2 + p1 + p0 + q0 + 4) >> 3) as u8; + } else { + v[3] = ((2 * p1 + p0 + q1 + 2) >> 2) as u8; + } + if aq < beta && strong { + let q3 = v[7] as i32; + v[4] = ((p1 + 2 * p0 + 2 * q0 + 2 * q1 + q2 + 4) >> 3) as u8; + v[5] = ((p0 + q0 + q1 + q2 + 2) >> 2) as u8; + v[6] = ((2 * q3 + 3 * q2 + q1 + q0 + p0 + 4) >> 3) as u8; + } else { + v[4] = ((2 * q1 + q0 + p1 + 2) >> 2) as u8; + } + } +} + +/// Edge filter parameters for `n` lines; `bs[i]` / `tc0[i]` apply to lines `i * n / 4 .. (i + 1) * n / 4`. +struct EdgeParams { + bs: [u8; 4], + tc0: [i32; 4], + alpha: i32, + beta: i32, +} + +/// Filter one edge for `N` lines at once (lane `i` = line `i` across the edge). `s[k][i]` holds +/// sample p3 p2 p1 p0 q0 q1 q2 q3 (k = 0..8) of line `i`. Branch-free per lane, so the loops +/// vectorise; bit-exact with [`filter8`] applied to every line (8.7.2.3 / 8.7.2.4). +#[inline(always)] +fn filter_lanes(s: &mut [[i16; N]; 8], ep: &EdgeParams, chroma: bool) { + let per = N / 4; + let (alpha, beta) = (ep.alpha as i16, ep.beta as i16); + let mut bs_on = [false; N]; + let mut tc0 = [0i16; N]; + for k in 0..4 { + for i in k * per..(k + 1) * per { + bs_on[i] = ep.bs[k] != 0; + tc0[i] = ep.tc0[k] as i16; + } + } + let [p3, p2, p1, p0, q0, q1, q2, q3] = *s; + // Masks are 0 / -1 per lane, selects are bitwise, and clipping to per-lane bounds uses + // min / max (`clamp` would assert its bounds per lane), so every loop below vectorises. + let lt = |x: i16, y: i16| -((x < y) as i16); + let sel = |m: i16, a: i16, b: i16| (a & m) | (b & !m); + let mut on = [0i16; N]; + let mut any = 0i16; + for i in 0..N { + on[i] = -(bs_on[i] as i16) & lt((p0[i] - q0[i]).abs(), alpha) & lt((p1[i] - p0[i]).abs(), beta) & lt((q1[i] - q0[i]).abs(), beta); + any |= on[i]; + } + if any == 0 { + return; + } + // bS 4 is only ever assigned to all four segments of an edge + let strong_edge = ep.bs[0] >= 4; + if chroma { + for i in 0..N { + let (np0, nq0) = if strong_edge { + ((2 * p1[i] + p0[i] + q1[i] + 2) >> 2, (2 * q1[i] + q0[i] + p1[i] + 2) >> 2) + } else { + let tc = tc0[i] + 1; + let delta = ((((q0[i] - p0[i]) << 2) + (p1[i] - q1[i]) + 4) >> 3).max(-tc).min(tc); + ((p0[i] + delta).clamp(0, 255), (q0[i] - delta).clamp(0, 255)) + }; + s[3][i] = sel(on[i], np0, p0[i]); + s[4][i] = sel(on[i], nq0, q0[i]); + } + return; + } + if strong_edge { + let lim = (alpha >> 2) + 2; + for i in 0..N { + let (a, b, c, d, e, f, g, h) = (p3[i], p2[i], p1[i], p0[i], q0[i], q1[i], q2[i], q3[i]); + let strong = lt((d - e).abs(), lim); + let sp = on[i] & lt((b - d).abs(), beta) & strong; + let sq = on[i] & lt((g - e).abs(), beta) & strong; + let wp0 = (2 * c + d + f + 2) >> 2; + let wq0 = (2 * f + e + c + 2) >> 2; + let sp0 = (b + 2 * c + 2 * d + 2 * e + f + 4) >> 3; + let sq0 = (c + 2 * d + 2 * e + 2 * f + g + 4) >> 3; + s[3][i] = sel(sp, sp0, sel(on[i], wp0, d)); + s[2][i] = sel(sp, (b + c + d + e + 2) >> 2, c); + s[1][i] = sel(sp, (2 * a + 3 * b + c + d + e + 4) >> 3, b); + s[4][i] = sel(sq, sq0, sel(on[i], wq0, e)); + s[5][i] = sel(sq, (d + e + f + g + 2) >> 2, f); + s[6][i] = sel(sq, (2 * h + 3 * g + f + e + d + 4) >> 3, g); + } + } else { + for i in 0..N { + let ap = lt((p2[i] - p0[i]).abs(), beta); + let aq = lt((q2[i] - q0[i]).abs(), beta); + let t0 = tc0[i]; + let tc = t0 - ap - aq; + let delta = ((((q0[i] - p0[i]) << 2) + (p1[i] - q1[i]) + 4) >> 3).max(-tc).min(tc); + let avg = (p0[i] + q0[i] + 1) >> 1; + let np1 = p1[i] + ((p2[i] + avg - (p1[i] << 1)) >> 1).max(-t0).min(t0); + let nq1 = q1[i] + ((q2[i] + avg - (q1[i] << 1)) >> 1).max(-t0).min(t0); + s[3][i] = sel(on[i], (p0[i] + delta).clamp(0, 255), p0[i]); + s[4][i] = sel(on[i], (q0[i] - delta).clamp(0, 255), q0[i]); + s[2][i] = sel(on[i] & ap, np1, p1[i]); + s[5][i] = sel(on[i] & aq, nq1, q1[i]); + } + } +} + +/// Filter a vertical edge whose q0 column is at `x`, for `N` lines starting at row `y`. +#[inline(always)] +fn filter_vertical(pix: &mut [u8], stride: usize, x: usize, y: usize, ep: &EdgeParams, chroma: bool) { + // Each line's eight samples as one little-endian word: byte k = sample k (p3 .. q3). + let mut w = [0u64; N]; + for (i, w) in w.iter_mut().enumerate() { + let o = (y + i) * stride + x - 4; + let Some(&b) = pix.get(o..).and_then(|p| p.first_chunk::<8>()) else { + return; + }; + *w = u64::from_le_bytes(b); + } + let mut s = [[0i16; N]; 8]; + for (k, row) in s.iter_mut().enumerate() { + for i in 0..N { + row[i] = ((w[i] >> (8 * k)) & 0xff) as i16; + } + } + filter_lanes(&mut s, ep, chroma); + for (i, w) in w.iter_mut().enumerate() { + *w = (0..8).fold(0u64, |acc, k| acc | ((s[k][i] as u8 as u64) << (8 * k))); + } + for (i, w) in w.iter().enumerate() { + let o = (y + i) * stride + x - 4; + pix[o..o + 8].copy_from_slice(&w.to_le_bytes()); + } +} + +/// Filter a horizontal edge whose q0 row is `y`, for `N` columns starting at `x`. +#[inline(always)] +fn filter_horizontal(pix: &mut [u8], stride: usize, x: usize, y: usize, ep: &EdgeParams, chroma: bool) { + let base = (y - 4) * stride + x; + let mut s = [[0i16; N]; 8]; + for (k, row) in s.iter_mut().enumerate() { + let Some(src) = pix.get(base + k * stride..).and_then(|p| p.first_chunk::()) else { + return; + }; + for i in 0..N { + row[i] = src[i] as i16; + } + } + filter_lanes(&mut s, ep, chroma); + let (k0, k1) = if chroma { (3, 5) } else { (1, 7) }; + for (k, row) in s.iter().enumerate().take(k1).skip(k0) { + let Some(dst) = pix.get_mut(base + k * stride..).and_then(|p| p.first_chunk_mut::()) else { + return; + }; + for i in 0..N { + dst[i] = row[i] as u8; + } + } +} + +/// Deblock one macroblock (macroblocks must be processed in raster order). +#[allow(clippy::needless_range_loop)] +pub fn deblock_mb(pic: &mut PicState, addr: usize, mb_w: usize) { + let q = &pic.mbs[addr]; + if q.slice_num == u32::MAX { + return; + } + let sq = &pic.slices[q.slice_num as usize]; + if sq.disable_deblocking_filter_idc == 1 { + return; + } + let (mx, my) = (addr % mb_w, addr / mb_w); + let usable = |n: Option| -> Option { + let n = n?; + let st = &pic.mbs[n]; + if st.slice_num == u32::MAX || (sq.disable_deblocking_filter_idc == 2 && st.slice_num != q.slice_num) { + return None; + } + Some(n) + }; + let left = usable(if mx > 0 { Some(addr - 1) } else { None }); + let top = usable(if my > 0 { Some(addr - mb_w) } else { None }); + let alpha_off = sq.alpha_offset; + let beta_off = sq.beta_offset; + let t8 = q.transform_8x8; + // bS[dir][edge][segment]; dir 0 = vertical edges (x), 1 = horizontal edges (y) + let mut bs = [[[0u8; 4]; 4]; 2]; + if q.kind.is_intra() { + for dir in 0..2 { + if (if dir == 0 { left } else { top }).is_some() { + bs[dir][0] = [4; 4]; + } + for e in 1..4 { + if !(t8 && e % 2 == 1) { + bs[dir][e] = [3; 4]; + } + } + } + } else { + let qids = ref_ids8(q, sq); + let q_uniform = (0..2).all(|l| { + let r = q.ref_idx[l]; + r[1] == r[0] && r[2] == r[0] && r[3] == r[0] && q.mv[l].iter().all(|m| *m == q.mv[l][0]) + }); + let qm: [Motion; 16] = std::array::from_fn(|r| block_motion(q, &qids, r)); + for dir in 0..2 { + let neighbor = if dir == 0 { left } else { top }; + if let Some(n) = neighbor { + let p = &pic.mbs[n]; + if p.kind.is_intra() { + bs[dir][0] = [4; 4]; + } else { + let pids = ref_ids8(p, &pic.slices[p.slice_num as usize]); + for k in 0..4 { + let (rq, rp) = if dir == 0 { (k * 4, k * 4 + 3) } else { (k, 12 + k) }; + bs[dir][0][k] = if (p.nz_mask >> rp) & 1 != 0 || (q.nz_mask >> rq) & 1 != 0 { + 2 + } else { + motion_bs(&block_motion(p, &pids, rp), &qm[rq]) + }; + } + } + } + if q_uniform && q.nz_mask == 0 { + // one motion for the whole MB and no coefficients: all internal edges have bS 0 + continue; + } + for e in 1..4 { + if t8 && e % 2 == 1 { + continue; + } + for k in 0..4 { + let (rq, rp) = if dir == 0 { (k * 4 + e, k * 4 + e - 1) } else { (e * 4 + k, e * 4 + k - 4) }; + bs[dir][e][k] = if (q.nz_mask >> rp) & 1 != 0 || (q.nz_mask >> rq) & 1 != 0 { 2 } else { motion_bs(&qm[rp], &qm[rq]) }; + } + } + } + } + let qp_of = |st: &MbState| if st.kind == MbKind::IPcm { 0 } else { st.qp as i32 }; + let q_qp = qp_of(q); + let q_qpc = q.qpc; + let p_qp = [left.map(|n| (qp_of(&pic.mbs[n]), pic.mbs[n].qpc)), top.map(|n| (qp_of(&pic.mbs[n]), pic.mbs[n].qpc))]; + let params = |qpp: i32, qpq: i32, b: [u8; 4]| { + let qpav = (qpp + qpq + 1) >> 1; + let index_a = (qpav + alpha_off).clamp(0, 51) as usize; + let index_b = (qpav + beta_off).clamp(0, 51) as usize; + let tc = |b: u8| if (1..4).contains(&b) { TC0[index_a][b as usize - 1] as i32 } else { 0 }; + EdgeParams { bs: b, tc0: [tc(b[0]), tc(b[1]), tc(b[2]), tc(b[3])], alpha: ALPHA[index_a] as i32, beta: BETA[index_b] as i32 } + }; + let width = pic.planes.width; + let cwidth = pic.planes.cwidth; + // luma + for dir in 0..2 { + for e in 0..4 { + if bs[dir][e] == [0; 4] { + continue; + } + let qpp = if e == 0 { p_qp[dir].map(|p| p.0).unwrap_or(q_qp) } else { q_qp }; + let ep = params(qpp, q_qp, bs[dir][e]); + if ep.alpha == 0 || ep.beta == 0 { + // filterSamplesFlag needs |p0 - q0| < alpha and |p1 - p0| < beta (8.7.2.2): no + // sample of the edge changes (low QPs) + continue; + } + if dir == 0 { + filter_vertical::<16>(&mut pic.planes.y, width, mx * 16 + e * 4, my * 16, &ep, false); + } else { + filter_horizontal::<16>(&mut pic.planes.y, width, mx * 16, my * 16 + e * 4, &ep, false); + } + } + } + // chroma (4:2:0): edges at chroma samples 0 and 4 use the bS of luma edges 0 and 2 + for c in 0..2 { + for dir in 0..2 { + for ce in 0..2 { + let e = ce * 2; + if bs[dir][e] == [0; 4] { + continue; + } + let qpp = if e == 0 { p_qp[dir].map(|p| p.1[c]).unwrap_or(q_qpc[c]) } else { q_qpc[c] } as i32; + let ep = params(qpp, q_qpc[c] as i32, bs[dir][e]); + if ep.alpha == 0 || ep.beta == 0 { + continue; + } + let plane = if c == 0 { &mut pic.planes.cb } else { &mut pic.planes.cr }; + if dir == 0 { + filter_vertical::<8>(plane, cwidth, mx * 8 + ce * 4, my * 8, &ep, true); + } else { + filter_horizontal::<8>(plane, cwidth, mx * 8, my * 8 + ce * 4, &ep, true); + } + } + } + } +} + +#[cfg(test)] +mod tests { + use super::*; + + /// The vectorised edge filter equals the per-line reference [`filter8`] on random edges + /// (small differences across the edge so that every filter branch is taken). + #[test] + fn lane_filter_matches_per_line_reference() { + let mut rng = 0x9e37_79b9_7f4a_7c15u64; + let mut next = move || { + rng ^= rng << 13; + rng ^= rng >> 7; + rng ^= rng << 17; + rng + }; + for iter in 0..20_000 { + let chroma = iter % 3 == 0; + let strong = iter % 4 == 1; + let bs: [u8; 4] = if strong { [4; 4] } else { std::array::from_fn(|_| (next() % 4) as u8) }; + let index_a = (next() % 52) as usize; + let index_b = (next() % 52) as usize; + let tc = |b: u8| if (1..4).contains(&b) { TC0[index_a][b as usize - 1] as i32 } else { 0 }; + let ep = EdgeParams { bs, tc0: bs.map(tc), alpha: ALPHA[index_a] as i32, beta: BETA[index_b] as i32 }; + let base = (next() % 256) as i32; + let spread = 1 + (next() % 24) as i32; + let mut lines = [[0u8; 8]; 16]; + for l in lines.iter_mut() { + for v in l.iter_mut() { + *v = (base + (next() % (2 * spread as u64 + 1)) as i32 - spread).clamp(0, 255) as u8; + } + } + let n = if chroma { 8 } else { 16 }; + let mut want = lines; + for (i, l) in want.iter_mut().enumerate().take(n) { + let k = i / (n / 4); + if bs[k] != 0 { + filter8(l, bs[k], ep.alpha, ep.beta, ep.tc0[k], chroma); + } + } + let got: Vec<[u8; 8]> = if chroma { + let mut s = [[0i16; 8]; 8]; + for i in 0..8 { + for k in 0..8 { + s[k][i] = lines[i][k] as i16; + } + } + filter_lanes(&mut s, &ep, true); + (0..8).map(|i| std::array::from_fn(|k| s[k][i] as u8)).collect() + } else { + let mut s = [[0i16; 16]; 8]; + for i in 0..16 { + for k in 0..8 { + s[k][i] = lines[i][k] as i16; + } + } + filter_lanes(&mut s, &ep, false); + (0..16).map(|i| std::array::from_fn(|k| s[k][i] as u8)).collect() + }; + assert_eq!(&got[..], &want[..n], "iteration {iter}: bs {bs:?} indexA {index_a} indexB {index_b}"); + } + } +} + +#[cfg(test)] +mod speed { + use super::*; + + /// Rough timing of the edge filters (`cargo test --release -p slidecraft-h264 --lib edge_filter_speed -- --ignored --nocapture`). + #[test] + #[ignore] + fn edge_filter_speed() { + let (w, h) = (1024usize, 256usize); + let mut pix: Vec = (0..w * h).map(|i| (128 + ((i * 7919) % 13) as i32 - 6) as u8).collect(); + let eps = [ + EdgeParams { bs: [4; 4], tc0: [0; 4], alpha: 40, beta: 9 }, + EdgeParams { bs: [3; 4], tc0: [2; 4], alpha: 40, beta: 9 }, + EdgeParams { bs: [2, 1, 0, 2], tc0: [1, 1, 0, 1], alpha: 40, beta: 9 }, + ]; + let t0 = std::time::Instant::now(); + let mut n = 0; + for _ in 0..20 { + for my in 1..h / 16 { + for mx in 1..w / 16 { + for (j, ep) in eps.iter().enumerate() { + filter_horizontal::<16>(&mut pix, w, mx * 16, my * 16 + j * 4, ep, false); + n += 1; + } + } + } + } + let dt = t0.elapsed(); + let tv = std::time::Instant::now(); + for _ in 0..20 { + for my in 1..h / 16 { + for mx in 1..w / 16 { + for (j, ep) in eps.iter().enumerate() { + filter_vertical::<16>(&mut pix, w, mx * 16 + j * 4, my * 16, ep, false); + } + } + } + } + println!("vertical: {:.1} ns/edge", tv.elapsed().as_nanos() as f64 / n as f64); + let t1 = std::time::Instant::now(); + for _ in 0..20 { + for my in 1..h / 16 { + for mx in 1..w / 16 { + for ep in eps.iter() { + for (vert, x, y) in [(true, mx * 16, my * 16), (false, mx * 16, my * 16)] { + for i in 0..16 { + let b = ep.bs[i / 4]; + if b == 0 { + continue; + } + let mut v = [0u8; 8]; + for k in 0..8 { + v[k] = if vert { pix[(y + i) * w + x - 4 + k] } else { pix[(y - 4 + k) * w + x + i] }; + } + filter8(&mut v, b, ep.alpha, ep.beta, ep.tc0[i / 4], false); + for k in 1..7 { + if vert { + pix[(y + i) * w + x - 4 + k] = v[k]; + } else { + pix[(y - 4 + k) * w + x + i] = v[k]; + } + } + } + } + } + } + } + } + println!("scalar reference (both): {:.1} ns/edge", t1.elapsed().as_nanos() as f64 / (2 * n) as f64); + println!( + "horizontal: {n} luma edges in {dt:?}: {:.1} ns/edge (checksum {})", + dt.as_nanos() as f64 / n as f64, + pix.iter().map(|&v| v as u64).sum::() + ); + } +} diff --git a/crates/h264/src/decoder.rs b/crates/h264/src/decoder.rs new file mode 100644 index 0000000..3178c64 --- /dev/null +++ b/crates/h264/src/decoder.rs @@ -0,0 +1,645 @@ +//! Top-level decoder: NAL dispatch, picture boundaries, POC, DPB and output. +//! +//! The calling thread parses headers and does all DPB / reference list bookkeeping (which only needs +//! header information). Macroblock decoding of each picture runs as a job — on a thread pool with the +//! `threads` feature — that publishes finished macroblock rows into the picture's shared [`Frame`]; +//! jobs of later pictures block per row on the reference data they need, so several pictures decode +//! concurrently (frame-level parallelism). + +use crate::dpb::{Dpb, Output, OutputMeta}; +use crate::error::{Error, Result, ensure, invalid, unsupported}; +use crate::params::{Pps, Sps}; +use crate::picture::{Frame, FrameRef, MbKind, MbState, Planes, RefPic}; +use crate::slice::{NalHeader, Poc, PocState, SliceHeader, SliceType, nal_type}; +use crate::slicedec::{PicState, SliceDecoder}; +use crate::transform::LevelScale; +use crate::{ColorInfo, Picture}; +use slidecraft_bitstream::{BitReader, annexb_nals, length_prefixed_nals, unescape_rbsp}; +use std::collections::VecDeque; +use std::sync::{Arc, Mutex}; + +/// One slice of a picture job. +struct SliceJob { + sh: SliceHeader, + pps: Arc, + sps: Arc, + rbsp: Vec, + refs: [Vec; 2], + ls: Arc, +} + +/// A picture whose slices are being collected. +struct PendingPic { + frame: FrameRef, + sps: Arc, + first: SliceHeader, + poc: Poc, + pts: i64, + key: bool, + slices: Vec, +} + +/// Counters describing what the decoder has seen (useful to check test coverage). +#[derive(Clone, Debug, Default, PartialEq, Eq)] +pub struct DecodeStats { + pub pictures: u64, + pub slices_cavlc: u64, + pub slices_cabac: u64, + pub slices_i: u64, + pub slices_p: u64, + pub slices_b: u64, + pub mb_i4x4: u64, + pub mb_i8x8: u64, + pub mb_i16x16: u64, + pub mb_pcm: u64, + pub mb_p_skip: u64, + pub mb_b_skip: u64, + pub mb_b_direct16x16: u64, + pub mb_inter: u64, + /// Inter macroblocks using the 8x8 transform. + pub mb_inter_8x8_transform: u64, + pub mmco_ops: u64, + pub long_term_marks: u64, + pub frame_num_gaps: u64, + pub weighted_slices: u64, + pub temporal_direct_slices: u64, + /// Non-reference pictures decoded in draft mode (deblocking skipped). + pub draft_pictures: u64, +} + +/// State shared with decoding jobs. +#[derive(Default)] +struct Shared { + error: Mutex>, + stats: Mutex, + /// Recycled picture buffers. + pool: Mutex>, +} + +/// H.264 decoder. +pub struct Decoder { + spss: Vec>>, + ppss: Vec>>, + nal_length_size: Option, + dpb: Dpb, + poc_state: PocState, + prev_ref_frame_num: u32, + pending: Option, + next_id: u32, + active_sps: Option>, + ls_cache: Vec<(Arc, Arc)>, + /// Pictures leaving the DPB, converted once their decoding job has finished. + out_queue: VecDeque, + shared: Arc, + #[cfg(feature = "threads")] + pool: Option, + in_flight: VecDeque, + max_in_flight: usize, + threads: usize, + /// Draft mode: skip deblocking of non-reference pictures ([`Decoder::set_draft`]). + draft: bool, +} + +impl Default for Decoder { + fn default() -> Self { + Self::new() + } +} + +/// Is `sh` the first slice of a new primary coded picture relative to `prev` (7.4.1.2.4)? +fn is_new_picture(prev: &SliceHeader, sh: &SliceHeader, sps: &Sps) -> bool { + if sh.frame_num != prev.frame_num + || sh.pps_id != prev.pps_id + || sh.field_pic != prev.field_pic + || sh.bottom_field != prev.bottom_field + || (sh.nal_ref_idc == 0) != (prev.nal_ref_idc == 0) + || sh.idr != prev.idr + || (sh.idr && prev.idr && sh.idr_pic_id != prev.idr_pic_id) + { + return true; + } + if sps.pic_order_cnt_type == 0 + && (sh.pic_order_cnt_lsb != prev.pic_order_cnt_lsb || sh.delta_pic_order_cnt_bottom != prev.delta_pic_order_cnt_bottom) + { + return true; + } + if sps.pic_order_cnt_type == 1 && sh.delta_pic_order_cnt != prev.delta_pic_order_cnt { + return true; + } + false +} + +/// The worker threads [`Decoder::new`] uses: the available cores (at most 16) with the `threads` +/// feature on native targets, 1 otherwise. +pub fn default_threads() -> usize { + #[cfg(all(feature = "threads", not(target_arch = "wasm32")))] + { + std::thread::available_parallelism().map(|n| n.get()).unwrap_or(1).min(16) + } + #[cfg(not(all(feature = "threads", not(target_arch = "wasm32"))))] + { + 1 + } +} + +impl Decoder { + /// A decoder using all available cores (with the `threads` feature). + pub fn new() -> Self { + Self::with_threads(default_threads()) + } + + /// A decoder using up to `threads` worker threads (1 = decode on the calling thread). + pub fn with_threads(threads: usize) -> Self { + crate::cavlc::init_tables(); + #[cfg(feature = "threads")] + let pool = if threads > 1 && cfg!(not(target_arch = "wasm32")) { + rayon::ThreadPoolBuilder::new().num_threads(threads).thread_name(|i| format!("h264-{i}")).build().ok() + } else { + None + }; + #[cfg(feature = "threads")] + let workers = if pool.is_some() { threads } else { 1 }; + #[cfg(not(feature = "threads"))] + let workers = 1; + Decoder { + spss: vec![None; 32], + ppss: vec![None; 256], + nal_length_size: None, + dpb: Dpb::new(), + poc_state: PocState::default(), + prev_ref_frame_num: 0, + pending: None, + next_id: 1, + active_sps: None, + ls_cache: Vec::new(), + out_queue: VecDeque::new(), + shared: Arc::new(Shared::default()), + #[cfg(feature = "threads")] + pool, + in_flight: VecDeque::new(), + max_in_flight: threads.max(1) + 2, + threads: workers, + draft: false, + } + } + + /// Worker threads decoding pictures (1: on the calling thread). + pub fn threads(&self) -> usize { + self.threads + } + + /// Draft mode (off by default) for reduced-resolution playback: pictures submitted from now + /// on that are non-reference pictures (`nal_ref_idc` 0) skip the deblocking filter (8.7). + /// Nothing predicts from a non-reference picture (and deblocking never changes the motion + /// data used for direct prediction), so every other picture stays bit-exact; the draft + /// pictures themselves are approximate and flagged [`Picture::draft`]. Never use it for + /// export or for a frame shown while paused. + pub fn set_draft(&mut self, on: bool) { + self.draft = on; + } + + pub fn draft(&self) -> bool { + self.draft + } + + /// Configure from an `avcC` (AVCDecoderConfigurationRecord) box payload. + pub fn from_avcc(avcc: &[u8]) -> Result { + let mut d = Decoder::new(); + d.configure_avcc(avcc)?; + Ok(d) + } + + /// Parse an `avcC` record: SPS/PPS and the NAL length size used by [`Decoder::decode`]. + pub fn configure_avcc(&mut self, avcc: &[u8]) -> Result<()> { + ensure!(avcc.len() >= 7, "avcC too short"); + ensure!(avcc[0] == 1, "unsupported avcC version {}", avcc[0]); + self.nal_length_size = Some((avcc[4] & 3) as usize + 1); + let mut pos = 5; + let read_sets = |pos: &mut usize, count: usize, d: &mut Decoder| -> Result<()> { + for _ in 0..count { + ensure!(*pos + 2 <= avcc.len(), "avcC truncated"); + let len = u16::from_be_bytes([avcc[*pos], avcc[*pos + 1]]) as usize; + *pos += 2; + ensure!(*pos + len <= avcc.len(), "avcC truncated"); + d.handle_nal(&avcc[*pos..*pos + len], 0)?; + *pos += len; + } + Ok(()) + }; + let nsps = (avcc[pos] & 0x1f) as usize; + pos += 1; + read_sets(&mut pos, nsps, self)?; + ensure!(pos < avcc.len(), "avcC truncated"); + let npps = avcc[pos] as usize; + pos += 1; + read_sets(&mut pos, npps, self) + } + + /// Length of NAL length prefixes (from avcC), or None for Annex-B input. + pub fn nal_length_size(&self) -> Option { + self.nal_length_size + } + + /// Decode one access unit (all NAL units of one picture, Annex-B or length-prefixed per + /// configuration; a byte stream with several complete access units is accepted too). Returns + /// pictures that became ready for output, in output order; `pts` travels with the picture of + /// this access unit through reordering. + pub fn decode(&mut self, data: &[u8], pts: i64) -> Result> { + let nals = match self.nal_length_size { + Some(n) => length_prefixed_nals(data, n)?, + None => annexb_nals(data), + }; + let mut result = Ok(()); + for nal in nals { + if let Err(e) = self.handle_nal(nal, pts) { + result = Err(e); + break; + } + } + // The access unit is complete: hand its picture to a decoding job. + self.submit_pending(); + if let Some(e) = self.take_error() { + result = result.and(Err(e)); + } + result?; + // Return finished pictures without blocking on frames still being decoded (bounded latency). + let max_queue = self.max_in_flight * 2; + let mut pics = Vec::new(); + while let Some(o) = self.out_queue.front() { + if !o.frame.is_published(o.frame.mb_h() - 1) && self.out_queue.len() <= max_queue { + break; + } + let Some(o) = self.out_queue.pop_front() else { break }; + pics.push(make_picture(&o)); + } + Ok(pics) + } + + /// Output all remaining pictures (end of stream). + pub fn flush(&mut self) -> Vec { + self.submit_pending(); + let mut outs = Vec::new(); + self.dpb.flush(&mut outs); + self.emit(outs); + let pics = self.out_queue.drain(..).map(|o| make_picture(&o)).collect(); + while let Some(f) = self.in_flight.pop_front() { + f.wait_complete(); + } + pics + } + + /// Statistics accumulated so far (macroblock counts include finished pictures only). + pub fn stats(&self) -> DecodeStats { + self.shared.stats.lock().map(|s| s.clone()).unwrap_or_default() + } + + fn stat(&self, f: impl FnOnce(&mut DecodeStats)) { + if let Ok(mut s) = self.shared.stats.lock() { + f(&mut s); + } + } + + /// First error reported by a decoding job since the last call (also returned by + /// [`Decoder::decode`]); useful after [`Decoder::flush`]. + pub fn take_error(&mut self) -> Option { + self.shared.error.lock().ok().and_then(|mut e| e.take()) + } + + fn handle_nal(&mut self, nal: &[u8], pts: i64) -> Result<()> { + if nal.is_empty() { + return Ok(()); + } + let hdr = NalHeader::parse(nal[0])?; + match hdr.nal_unit_type { + nal_type::SPS => { + let sps = Sps::parse(&unescape_rbsp(&nal[1..]))?; + let id = sps.id as usize; + self.spss[id] = Some(Arc::new(sps)); + } + nal_type::PPS => { + let rbsp = unescape_rbsp(&nal[1..]); + let spss: Vec> = self.spss.iter().map(|s| s.as_ref().map(|s| (**s).clone())).collect(); + let pps = Pps::parse(&rbsp, &spss)?; + let id = pps.id as usize; + self.ppss[id] = Some(Arc::new(pps)); + } + nal_type::SLICE | nal_type::IDR => self.handle_slice(nal, hdr, pts)?, + nal_type::SLICE_DPA | nal_type::SLICE_DPB | nal_type::SLICE_DPC => { + return unsupported("data partitioning (Extended profile)"); + } + nal_type::END_SEQ | nal_type::END_STREAM => self.submit_pending(), + // SEI, AUD, filler, SPS extension, prefix NAL, subset SPS, auxiliary and extension slices: skipped. + _ => {} + } + Ok(()) + } + + fn level_scale(&mut self, pps: &Arc) -> Arc { + if let Some((_, ls)) = self.ls_cache.iter().find(|(p, _)| Arc::ptr_eq(p, pps)) { + return ls.clone(); + } + let ls: Arc = Arc::from(LevelScale::new(&pps.scaling)); + self.ls_cache.retain(|(p, _)| self.ppss.iter().flatten().any(|q| Arc::ptr_eq(p, q))); + self.ls_cache.push((pps.clone(), ls.clone())); + ls + } + + fn handle_slice(&mut self, nal: &[u8], hdr: NalHeader, pts: i64) -> Result<()> { + let rbsp = unescape_rbsp(&nal[1..]); + let ppss = &self.ppss; + let spss = &self.spss; + let mut found: Option<(Arc, Arc)> = None; + let (sh, _, _) = SliceHeader::parse(&rbsp, hdr, |id| { + let pps = ppss[id as usize].as_ref().ok_or_else(|| Error::MissingParameterSet(format!("PPS {id}")))?; + let sps = spss[pps.sps_id as usize].as_ref().ok_or_else(|| Error::MissingParameterSet(format!("SPS {}", pps.sps_id)))?; + found = Some((pps.clone(), sps.clone())); + Ok((&**pps, &**sps)) + })?; + let Some((pps, sps)) = found else { + return Err(Error::MissingParameterSet("PPS".into())); + }; + sps.check_supported()?; + if pps.num_slice_groups > 1 { + return unsupported("slice groups (FMO)"); + } + if sh.field_pic { + return unsupported("field pictures"); + } + if matches!(sh.slice_type, SliceType::Sp | SliceType::Si) { + return unsupported("SP/SI slices"); + } + if sh.redundant_pic_cnt > 0 { + return Ok(()); // redundant slices are ignored + } + let new_pic = match &self.pending { + None => true, + Some(p) => is_new_picture(&p.first, &sh, &sps) || sh.first_mb_in_slice == 0 || !Arc::ptr_eq(&p.sps, &sps), + }; + if new_pic { + self.submit_pending(); + if sh.first_mb_in_slice != 0 && self.active_sps.is_none() { + return invalid("stream does not start with the first slice of a picture"); + } + self.start_picture(&sh, &sps, pts)?; + } + let ls = self.level_scale(&pps); + let Some(pending) = self.pending.as_mut() else { + return invalid("slice without a started picture"); + }; + let refs = self.dpb.build_ref_lists(&sh, pending.poc.frame(), sps.max_frame_num())?; + if !sh.slice_type.is_intra() { + ensure!(!refs[0].is_empty(), "no reference pictures available for inter slice"); + if sh.slice_type.is_b() { + ensure!(!refs[1].is_empty(), "empty RefPicList1 in B slice"); + } + } + let weighted = (pps.weighted_pred && sh.slice_type.is_p()) || (pps.weighted_bipred_idc != 0 && sh.slice_type.is_b()); + let temporal = sh.slice_type.is_b() && !sh.direct_spatial_mv_pred; + let (cabac, st, mmcos, lt) = ( + pps.entropy_coding_mode, + sh.slice_type, + sh.mmcos.len() as u64, + sh.long_term_reference as u64 + sh.mmcos.iter().filter(|m| m.op == 3 || m.op == 6).count() as u64, + ); + pending.slices.push(SliceJob { sh, pps, sps, rbsp, refs, ls }); + self.stat(|s| { + if cabac { + s.slices_cabac += 1; + } else { + s.slices_cavlc += 1; + } + match st { + SliceType::I | SliceType::Si => s.slices_i += 1, + SliceType::P | SliceType::Sp => s.slices_p += 1, + SliceType::B => s.slices_b += 1, + } + s.weighted_slices += weighted as u64; + s.temporal_direct_slices += temporal as u64; + s.mmco_ops += mmcos; + s.long_term_marks += lt; + }); + Ok(()) + } + + fn start_picture(&mut self, sh: &SliceHeader, sps: &Arc, pts: i64) -> Result<()> { + // Activate SPS; a resolution change flushes the DPB. + let changed = match &self.active_sps { + None => true, + Some(a) => a.width() != sps.width() || a.height() != sps.height() || a.max_dpb_frames() != sps.max_dpb_frames(), + }; + if changed && self.active_sps.is_some() { + let mut outs = Vec::new(); + self.dpb.flush(&mut outs); + self.emit(outs); + self.dpb.entries.clear(); + } + self.active_sps = Some(sps.clone()); + self.dpb.capacity = sps.max_dpb_frames(); + self.dpb.max_reorder = sps.max_num_reorder_frames().min(self.dpb.capacity); + let mb_w = sps.pic_width_in_mbs as usize; + let mb_h = sps.frame_height_in_mbs() as usize; + if sh.idr { + let mut outs = Vec::new(); + self.dpb.idr(sh.no_output_of_prior_pics, &mut outs); + self.emit(outs); + self.prev_ref_frame_num = 0; + } else { + let max = sps.max_frame_num(); + if sh.frame_num != self.prev_ref_frame_num && sh.frame_num != (self.prev_ref_frame_num + 1) % max { + // frame_num gap (8.2.5.2): insert "non-existing" frames. Their samples are never used by + // conforming streams; copy the latest decoded frame for concealment. + let meta = Arc::new(output_meta(sps, pts, false)); + let next_id = &mut self.next_id; + let poc_state = &mut self.poc_state; + let last = self.dpb.entries.iter().filter(|e| !e.non_existing).max_by_key(|e| e.frame.id).map(|e| e.frame.clone()); + let mut make = |_fnum: u32| { + let id = *next_id; + *next_id += 1; + let planes = match &last { + Some(f) if f.mb_w == mb_w && f.mb_h() == mb_h => { + let mut p = Planes::new(mb_w * 16, mb_h * 16); + let (y, u, v) = f.copy_cropped((0, 0, mb_w * 16, mb_h * 16)); + p.y = y; + p.cb = u; + p.cr = v; + p + } + _ => Planes::gray(mb_w * 16, mb_h * 16), + }; + (Arc::new(Frame::from_planes(id, 0, &planes)), meta.clone()) + }; + let mut upd = |fnum: u32| poc_state.update_gap_frame(fnum, sps); + self.shared.stats.lock().map(|mut s| s.frame_num_gaps += 1).ok(); + self.dpb.fill_frame_num_gap(self.prev_ref_frame_num, sh.frame_num, max, sps.max_num_ref_frames as usize, &mut make, &mut upd); + self.prev_ref_frame_num = (sh.frame_num + max - 1) % max; + } + } + let poc = self.poc_state.compute(sh, sps); + // MMCO5: the picture's POC becomes relative to itself (tempPicOrderCnt, 8.2.1). + let frame_poc = if sh.has_mmco5() { 0 } else { poc.frame() }; + let id = self.next_id; + self.next_id += 1; + let frame = Arc::new(Frame::new(id, frame_poc, mb_w, mb_h)); + self.pending = Some(PendingPic { frame, sps: sps.clone(), first: sh.clone(), poc, pts, key: sh.idr, slices: Vec::new() }); + Ok(()) + } + + /// Start decoding the pending picture and do its reference marking / DPB insertion. + fn submit_pending(&mut self) { + let Some(p) = self.pending.take() else { return }; + let PendingPic { frame, sps, first, poc, pts, key, slices } = p; + let draft = self.draft && first.nal_ref_idc == 0; + self.dispatch(frame.clone(), slices, draft); + self.poc_state.update(&first, &poc); + if first.nal_ref_idc != 0 { + self.prev_ref_frame_num = if first.has_mmco5() { 0 } else { first.frame_num }; + } + let meta = Arc::new(OutputMeta { draft, ..output_meta(&sps, pts, key) }); + let mut outs = Vec::new(); + let fpoc = frame.poc; + self.dpb.store_picture(&first, frame, fpoc, sps.max_frame_num(), sps.max_num_ref_frames as usize, meta, &mut outs); + self.emit(outs); + } + + fn dispatch(&mut self, frame: FrameRef, slices: Vec, draft: bool) { + let shared = self.shared.clone(); + #[cfg(feature = "threads")] + if let Some(pool) = &self.pool { + self.in_flight.retain(|f| !f.is_published(f.mb_h() - 1)); + while self.in_flight.len() >= self.max_in_flight { + if let Some(f) = self.in_flight.pop_front() { + f.wait_complete(); + } + } + self.in_flight.push_back(frame.clone()); + pool.spawn_fifo(move || run_job(frame, slices, &shared, draft)); + return; + } + run_job(frame, slices, &shared, draft); + } + + fn emit(&mut self, outs: Vec) { + self.out_queue.extend(outs); + } +} + +/// Decode all slices of one picture and publish its rows. +fn run_job(frame: FrameRef, slices: Vec, shared: &Shared, draft: bool) { + let reuse = shared.pool.lock().ok().and_then(|mut p| p.pop()); + let mut pic = match reuse { + Some(mut p) if p.mb_w == frame.mb_w && p.mb_h == frame.mb_h() => { + p.reset(frame.clone()); + p + } + _ => PicState::new(frame.clone()), + }; + pic.skip_deblock = draft; + let result = std::panic::catch_unwind(std::panic::AssertUnwindSafe(|| { + let mut first_err = None; + for s in &slices { + if let Err(e) = decode_slice(s, &mut pic) { + first_err.get_or_insert(e); + } + } + pic.flush_rows(true); + first_err + })); + let err = match result { + Ok(e) => e, + Err(_) => { + // Make sure readers never block on this frame. + let fallback = Planes::gray(frame.width, frame.height); + let blank = vec![MbState::default(); frame.mb_w * frame.mb_h()]; + for r in 0..frame.mb_h() { + if !frame.is_published(r) { + frame.publish(r, Frame::make_row(&fallback, r, &blank, &|_, _, _| u32::MAX)); + } + } + pic = PicState::new(frame.clone()); + Some(Error::Invalid("internal error while decoding a picture".into())) + } + }; + if let Ok(mut s) = shared.stats.lock() { + s.pictures += 1; + s.draft_pictures += draft as u64; + for st in &pic.mbs { + match st.kind { + MbKind::I4x4 => s.mb_i4x4 += 1, + MbKind::I8x8 => s.mb_i8x8 += 1, + MbKind::I16x16 => s.mb_i16x16 += 1, + MbKind::IPcm => s.mb_pcm += 1, + MbKind::PSkip => s.mb_p_skip += 1, + MbKind::BSkip => s.mb_b_skip += 1, + MbKind::BDirect16x16 => s.mb_b_direct16x16 += 1, + MbKind::Inter => s.mb_inter += 1, + MbKind::None => {} + } + if st.transform_8x8 && !st.kind.is_intra() { + s.mb_inter_8x8_transform += 1; + } + } + } + if let (Some(e), Ok(mut slot)) = (err, shared.error.lock()) { + slot.get_or_insert(e); + } + if let Ok(mut p) = shared.pool.lock() + && p.len() < 4 + { + p.push(pic); + } +} + +fn decode_slice(s: &SliceJob, pic: &mut PicState) -> Result<()> { + let mut sd = SliceDecoder::new(&s.sh, &s.pps, &s.sps, pic, &s.refs, &s.ls)?; + if s.pps.entropy_coding_mode { + sd.decode_cabac(&s.rbsp) + } else { + let mut r = BitReader::new(&s.rbsp); + r.seek_bits(s.sh.header_bits); + sd.decode_cavlc(&mut r) + } +} + +fn output_meta(sps: &Sps, pts: i64, key: bool) -> OutputMeta { + let vui = sps.vui.clone().unwrap_or_default(); + OutputMeta { + pts, + key, + crop: sps.crop_rect(), + full_range: vui.full_range, + colour_primaries: vui.colour_primaries, + transfer_characteristics: vui.transfer_characteristics, + matrix_coefficients: vui.matrix_coefficients, + sar: vui.sar, + draft: false, + } +} + +fn make_picture(o: &Output) -> Picture { + let (cx, cy, cw, ch) = o.meta.crop; + let (cx, cy, cw, ch) = (cx as usize, cy as usize, cw as usize, ch as usize); + let (y, u, v) = o.frame.copy_cropped((cx, cy, cw, ch)); + let (ccw, cch) = (cw.div_ceil(2), ch.div_ceil(2)); + Picture { + width: cw as u32, + height: ch as u32, + chroma_width: ccw as u32, + chroma_height: cch as u32, + y, + u, + v, + y_stride: cw, + uv_stride: ccw, + pts: o.meta.pts, + poc: o.poc, + key: o.meta.key, + color: ColorInfo { + full_range: o.meta.full_range, + primaries: o.meta.colour_primaries, + transfer: o.meta.transfer_characteristics, + matrix: o.meta.matrix_coefficients, + }, + sar: o.meta.sar, + draft: o.meta.draft, + } +} diff --git a/crates/h264/src/dpb.rs b/crates/h264/src/dpb.rs new file mode 100644 index 0000000..cb06433 --- /dev/null +++ b/crates/h264/src/dpb.rs @@ -0,0 +1,420 @@ +//! Decoded picture buffer: reference marking (8.2.5), reference list construction (8.2.4) and output +//! ordering / bumping (C.4). + +use crate::error::{Result, ensure}; +use crate::picture::{FrameRef, RefPic}; +use crate::slice::{Mmco, SliceHeader, SliceType}; +use std::sync::Arc; + +#[derive(Clone, Copy, Debug, PartialEq, Eq)] +pub enum RefMark { + Unused, + Short, + Long, +} + +/// Output-related metadata attached to a decoded frame. +#[derive(Clone, Debug)] +pub struct OutputMeta { + pub pts: i64, + pub key: bool, + pub crop: (u32, u32, u32, u32), + pub full_range: bool, + pub colour_primaries: u8, + pub transfer_characteristics: u8, + pub matrix_coefficients: u8, + pub sar: (u16, u16), + /// Decoded without deblocking (draft mode, non-reference picture). + pub draft: bool, +} + +pub struct DpbEntry { + pub frame: FrameRef, + pub poc: i32, + pub frame_num: u32, + pub long_term_frame_idx: u32, + pub mark: RefMark, + pub needed_for_output: bool, + pub non_existing: bool, + pub meta: Arc, +} + +pub struct Dpb { + pub entries: Vec, + /// None = "no long-term frame indices". + pub max_long_term_frame_idx: Option, + pub capacity: usize, + pub max_reorder: usize, +} + +/// A picture leaving the DPB for output. +pub struct Output { + pub frame: FrameRef, + pub poc: i32, + pub meta: Arc, +} + +impl Dpb { + pub fn new() -> Self { + Dpb { entries: Vec::new(), max_long_term_frame_idx: None, capacity: 16, max_reorder: 16 } + } + + fn frame_num_wrap(e: &DpbEntry, cur_frame_num: u32, max_frame_num: u32) -> i32 { + if e.frame_num > cur_frame_num { e.frame_num as i32 - max_frame_num as i32 } else { e.frame_num as i32 } + } + + fn num_refs(&self) -> usize { + self.entries.iter().filter(|e| e.mark != RefMark::Unused).count() + } + + /// Remove entries not needed for output and not used for reference. + fn prune(&mut self) { + self.entries.retain(|e| e.needed_for_output || e.mark != RefMark::Unused); + } + + /// Output the picture with the smallest POC (C.4.5.3 "bumping"). Returns false if none waits. + fn bump(&mut self, out: &mut Vec) -> bool { + let Some(i) = self.entries.iter().enumerate().filter(|(_, e)| e.needed_for_output).min_by_key(|(_, e)| e.poc).map(|(i, _)| i) else { + return false; + }; + let e = &mut self.entries[i]; + e.needed_for_output = false; + out.push(Output { frame: e.frame.clone(), poc: e.poc, meta: e.meta.clone() }); + self.prune(); + true + } + + /// Output everything in POC order and clear non-reference entries. + pub fn flush(&mut self, out: &mut Vec) { + while self.bump(out) {} + self.prune(); + } + + /// IDR: mark all references unused; output (or drop) prior pictures. + pub fn idr(&mut self, no_output_of_prior_pics: bool, out: &mut Vec) { + for e in &mut self.entries { + e.mark = RefMark::Unused; + } + if no_output_of_prior_pics { + self.entries.clear(); + } else { + self.flush(out); + } + self.max_long_term_frame_idx = None; + } + + /// Sliding window marking (8.2.5.3). + fn sliding_window(&mut self, cur_frame_num: u32, max_frame_num: u32, max_num_ref_frames: usize) { + let limit = max_num_ref_frames.max(1); + while self.num_refs() >= limit { + let victim = self + .entries + .iter() + .enumerate() + .filter(|(_, e)| e.mark == RefMark::Short) + .min_by_key(|(_, e)| Self::frame_num_wrap(e, cur_frame_num, max_frame_num)) + .map(|(i, _)| i); + match victim { + Some(i) => self.entries[i].mark = RefMark::Unused, + None => break, + } + } + self.prune(); + } + + /// Insert "non-existing" frames for a frame_num gap (8.2.5.2). + #[allow(clippy::too_many_arguments)] + pub fn fill_frame_num_gap( + &mut self, + prev_ref_frame_num: u32, + frame_num: u32, + max_frame_num: u32, + max_num_ref_frames: usize, + make_frame: &mut dyn FnMut(u32) -> (FrameRef, Arc), + poc_state: &mut dyn FnMut(u32), + ) { + let mut unused = (prev_ref_frame_num + 1) % max_frame_num; + let mut guard = 0; + while unused != frame_num && guard < max_frame_num { + self.sliding_window(unused, max_frame_num, max_num_ref_frames); + let (frame, meta) = make_frame(unused); + poc_state(unused); + self.entries.push(DpbEntry { + poc: frame.poc, + frame, + frame_num: unused, + long_term_frame_idx: 0, + mark: RefMark::Short, + needed_for_output: false, + non_existing: true, + meta, + }); + unused = (unused + 1) % max_frame_num; + guard += 1; + } + } + + /// Initial reference picture lists (8.2.4.2) + modification (8.2.4.3) for a frame slice. + pub fn build_ref_lists(&self, sh: &SliceHeader, cur_poc: i32, max_frame_num: u32) -> Result<[Vec; 2]> { + let mut lists: [Vec; 2] = [Vec::new(), Vec::new()]; + if sh.slice_type.is_intra() { + return Ok(lists); + } + let cur_fn = sh.frame_num; + let pic_num = |e: &DpbEntry| Self::frame_num_wrap(e, cur_fn, max_frame_num); + let short: Vec<&DpbEntry> = self.entries.iter().filter(|e| e.mark == RefMark::Short).collect(); + let mut long: Vec<&DpbEntry> = self.entries.iter().filter(|e| e.mark == RefMark::Long).collect(); + long.sort_by_key(|e| e.long_term_frame_idx); + let to_ref = |e: &&DpbEntry| RefPic { frame: e.frame.clone(), long_term: e.mark == RefMark::Long }; + let mut entry_lists: [Vec<&DpbEntry>; 2] = [Vec::new(), Vec::new()]; + if sh.slice_type != SliceType::B { + let mut s = short.clone(); + s.sort_by_key(|e| std::cmp::Reverse(pic_num(e))); + entry_lists[0] = s.into_iter().chain(long.iter().copied()).collect(); + } else { + let mut before: Vec<&DpbEntry> = short.iter().copied().filter(|e| e.poc < cur_poc).collect(); + let mut after: Vec<&DpbEntry> = short.iter().copied().filter(|e| e.poc > cur_poc).collect(); + before.sort_by_key(|e| std::cmp::Reverse(e.poc)); + after.sort_by_key(|e| e.poc); + entry_lists[0] = before.iter().chain(after.iter()).chain(long.iter()).copied().collect(); + entry_lists[1] = after.iter().chain(before.iter()).chain(long.iter()).copied().collect(); + if entry_lists[1].len() > 1 && entry_lists[0].len() == entry_lists[1].len() { + let same = entry_lists[0].iter().zip(entry_lists[1].iter()).all(|(a, b)| std::ptr::eq(*a, *b)); + if same { + entry_lists[1].swap(0, 1); + } + } + } + let nlists = if sh.slice_type == SliceType::B { 2 } else { 1 }; + for l in 0..nlists { + let n = sh.num_ref_idx_active[l] as usize; + let mut list: Vec> = entry_lists[l].iter().copied().map(Some).collect(); + list.truncate(n); + list.resize(n, None); + if !sh.ref_pic_list_mod[l].is_empty() { + let max_pic_num = max_frame_num as i32; + let curr_pic_num = cur_fn as i32; + let mut pred = curr_pic_num; + let mut ref_idx = 0usize; + for m in &sh.ref_pic_list_mod[l] { + ensure!(ref_idx < n, "too many ref_pic_list_modification entries"); + list.push(None); // temporarily one longer + let (target, is_long): (Option<&DpbEntry>, bool) = match m.idc { + 0 | 1 => { + let d = m.value as i32 + 1; + let no_wrap = if m.idc == 0 { + if pred - d < 0 { pred - d + max_pic_num } else { pred - d } + } else if pred + d >= max_pic_num { + pred + d - max_pic_num + } else { + pred + d + }; + pred = no_wrap; + let pn = if no_wrap > curr_pic_num { no_wrap - max_pic_num } else { no_wrap }; + (short.iter().copied().find(|e| pic_num(e) == pn), false) + } + 2 => (long.iter().copied().find(|e| e.long_term_frame_idx == m.value), true), + _ => continue, + }; + let Some(target) = target else { + // Missing picture: keep the list unchanged (error concealment). + list.pop(); + ref_idx += 1; + continue; + }; + for c in (ref_idx + 1..=n).rev() { + list[c] = list[c - 1]; + } + list[ref_idx] = Some(target); + ref_idx += 1; + let mut nidx = ref_idx; + for c in ref_idx..=n { + let keep = match list[c] { + None => true, + Some(e) => { + if is_long { + !(e.mark == RefMark::Long && e.long_term_frame_idx == target.long_term_frame_idx) + } else { + !(e.mark == RefMark::Short && std::ptr::eq(e, target)) + } + } + }; + if keep { + list[nidx] = list[c]; + nidx += 1; + } + } + list.truncate(n); + } + } + // Fill missing entries (should not happen in conforming streams) with the first valid entry. + let fallback = list.iter().flatten().next().copied().or_else(|| self.entries.last()); + let out: Vec = list.iter().filter_map(|e| e.or(fallback)).map(|e| to_ref(&e)).collect(); + lists[l] = out; + } + Ok(lists) + } + + /// Adaptive memory control (8.2.5.4). Returns true if MMCO 5 was executed. + fn apply_mmcos(&mut self, mmcos: &[Mmco], cur_frame_num: u32, max_frame_num: u32, cur_long: &mut Option) -> bool { + let mut had5 = false; + let curr_pic_num = cur_frame_num as i32; + for m in mmcos { + match m.op { + 1 => { + let pn = curr_pic_num - (m.difference_of_pic_nums_minus1 as i32 + 1); + for e in &mut self.entries { + if e.mark == RefMark::Short && Self::frame_num_wrap(e, cur_frame_num, max_frame_num) == pn { + e.mark = RefMark::Unused; + } + } + } + 2 => { + for e in &mut self.entries { + if e.mark == RefMark::Long && e.long_term_frame_idx == m.long_term_pic_num { + e.mark = RefMark::Unused; + } + } + } + 3 => { + let pn = curr_pic_num - (m.difference_of_pic_nums_minus1 as i32 + 1); + for e in &mut self.entries { + if e.mark == RefMark::Long && e.long_term_frame_idx == m.long_term_frame_idx { + e.mark = RefMark::Unused; + } + } + for e in &mut self.entries { + if e.mark == RefMark::Short && Self::frame_num_wrap(e, cur_frame_num, max_frame_num) == pn { + e.mark = RefMark::Long; + e.long_term_frame_idx = m.long_term_frame_idx; + } + } + } + 4 => { + let max = m.max_long_term_frame_idx_plus1; + for e in &mut self.entries { + if e.mark == RefMark::Long && (max == 0 || e.long_term_frame_idx > max - 1) { + e.mark = RefMark::Unused; + } + } + self.max_long_term_frame_idx = if max == 0 { None } else { Some(max - 1) }; + } + 5 => { + for e in &mut self.entries { + e.mark = RefMark::Unused; + } + self.max_long_term_frame_idx = None; + had5 = true; + } + 6 => { + for e in &mut self.entries { + if e.mark == RefMark::Long && e.long_term_frame_idx == m.long_term_frame_idx { + e.mark = RefMark::Unused; + } + } + *cur_long = Some(m.long_term_frame_idx); + } + _ => {} + } + } + self.prune(); + had5 + } + + /// Reference marking for the just-decoded picture and insertion into the DPB with output bumping. + /// Returns pictures ready for output. + #[allow(clippy::too_many_arguments)] + pub fn store_picture( + &mut self, + sh: &SliceHeader, + frame: FrameRef, + poc: i32, + max_frame_num: u32, + max_num_ref_frames: usize, + meta: Arc, + out: &mut Vec, + ) { + let mut mark = RefMark::Unused; + let mut long_idx = 0; + let mut had5 = false; + if sh.nal_ref_idc != 0 { + if sh.idr { + if sh.long_term_reference { + mark = RefMark::Long; + self.max_long_term_frame_idx = Some(0); + } else { + mark = RefMark::Short; + self.max_long_term_frame_idx = None; + } + } else { + let mut cur_long = None; + if sh.adaptive_ref_pic_marking { + had5 = self.apply_mmcos(&sh.mmcos, sh.frame_num, max_frame_num, &mut cur_long); + } else { + self.sliding_window(sh.frame_num, max_frame_num, max_num_ref_frames); + } + if let Some(i) = cur_long { + mark = RefMark::Long; + long_idx = i; + } else { + mark = RefMark::Short; + // make room if MMCOs left the DPB over-full (non-conforming streams) + if self.num_refs() >= max_num_ref_frames.max(1) && sh.adaptive_ref_pic_marking { + self.sliding_window(sh.frame_num, max_frame_num, max_num_ref_frames); + } + } + } + } + if had5 { + // All earlier pictures are output before the current one (C.4.4). + self.flush(out); + } + let frame_num = if had5 { 0 } else { sh.frame_num }; + // Bumping until there is space (C.4.5.1 / C.4.5.2). + if mark == RefMark::Unused { + loop { + let waiting = self.entries.len(); + if waiting < self.capacity { + break; + } + // non-reference picture with the smallest POC is output directly + let min_poc = self.entries.iter().filter(|e| e.needed_for_output).map(|e| e.poc).min(); + if min_poc.is_none_or(|m| poc < m) { + out.push(Output { frame, poc, meta }); + return; + } + if !self.bump(out) { + break; + } + } + } else { + while self.entries.len() >= self.capacity { + if !self.bump(out) { + break; + } + } + } + self.entries.push(DpbEntry { + frame, + poc, + frame_num, + long_term_frame_idx: long_idx, + mark, + needed_for_output: true, + non_existing: false, + meta, + }); + // Low-delay output: honour max_num_reorder_frames. + while self.entries.iter().filter(|e| e.needed_for_output).count() > self.max_reorder { + if !self.bump(out) { + break; + } + } + } +} + +impl Default for Dpb { + fn default() -> Self { + Self::new() + } +} diff --git a/crates/h264/src/error.rs b/crates/h264/src/error.rs new file mode 100644 index 0000000..f7a37c6 --- /dev/null +++ b/crates/h264/src/error.rs @@ -0,0 +1,38 @@ +use slidecraft_bitstream::BitError; + +/// Errors produced by the decoder. +#[derive(Debug, Clone, PartialEq, Eq, thiserror::Error)] +pub enum Error { + /// The bitstream ended early or contained an invalid Exp-Golomb code. + #[error("bitstream error: {0}")] + Bitstream(#[from] BitError), + /// A syntax element had a value outside its legal range. + #[error("invalid bitstream: {0}")] + Invalid(String), + /// A valid feature this decoder does not implement (yet). + #[error("unsupported: {0}")] + Unsupported(String), + /// A slice referenced a parameter set that has not been received. + #[error("missing parameter set: {0}")] + MissingParameterSet(String), +} + +pub type Result = std::result::Result; + +pub(crate) fn invalid(msg: impl Into) -> Result { + Err(Error::Invalid(msg.into())) +} + +pub(crate) fn unsupported(msg: impl Into) -> Result { + Err(Error::Unsupported(msg.into())) +} + +/// `ensure!(cond, "message")` returns `Error::Invalid` when `cond` is false. +macro_rules! ensure { + ($cond:expr, $($arg:tt)+) => { + if !$cond { + return Err($crate::error::Error::Invalid(format!($($arg)+))); + } + }; +} +pub(crate) use ensure; diff --git a/crates/h264/src/inter.rs b/crates/h264/src/inter.rs new file mode 100644 index 0000000..030d5dd --- /dev/null +++ b/crates/h264/src/inter.rs @@ -0,0 +1,513 @@ +//! Inter prediction sample interpolation (8.4.2.2) and weighted sample prediction (8.4.2.3). + +/// A reference plane (used by the plane-based helpers in tests). +#[cfg(test)] +#[derive(Clone, Copy)] +pub struct PlaneRef<'a> { + pub data: &'a [u8], + pub width: usize, + pub height: usize, + pub stride: usize, +} + +const WIN: usize = 16 + 5; + +/// Copy the (bw+5)x(bh+5) window whose top-left is (x-2, y-2) into `win` (stride WIN), clamping +/// coordinates to the picture. +#[cfg(test)] +#[inline] +fn fetch_window(p: PlaneRef, x: i32, y: i32, bw: usize, bh: usize, win: &mut [u8; WIN * WIN]) { + let x0 = x - 2; + let y0 = y - 2; + let ww = bw + 5; + let wh = bh + 5; + let maxx = p.width as i32 - 1; + let maxy = p.height as i32 - 1; + if x0 >= 0 && y0 >= 0 && x0 + ww as i32 - 1 <= maxx && y0 + wh as i32 - 1 <= maxy { + for r in 0..wh { + let src = (y0 as usize + r) * p.stride + x0 as usize; + win[r * WIN..r * WIN + ww].copy_from_slice(&p.data[src..src + ww]); + } + } else { + for r in 0..wh { + let yy = (y0 + r as i32).clamp(0, maxy) as usize; + let row = &p.data[yy * p.stride..yy * p.stride + p.width]; + for c in 0..ww { + let xx = (x0 + c as i32).clamp(0, maxx) as usize; + win[r * WIN + c] = row[xx]; + } + } + } +} + +#[inline(always)] +fn tap6(a: i32, b: i32, c: i32, d: i32, e: i32, f: i32) -> i32 { + a - 5 * b + 20 * c + 20 * d - 5 * e + f +} + +#[inline(always)] +fn clip1(v: i32) -> u8 { + v.clamp(0, 255) as u8 +} + +/// Copy `n` bytes with a fixed-size move for the common block widths. +#[inline(always)] +fn copy_n(dst: &mut [u8], src: &[u8], n: usize) { + fn fixed(dst: &mut [u8], src: &[u8]) { + if let (Some(d), Some(s)) = (dst.first_chunk_mut::(), src.first_chunk::()) { + *d = *s; + } + } + match n { + 16 => fixed::<16>(dst, src), + 8 => fixed::<8>(dst, src), + 4 => fixed::<4>(dst, src), + 2 => fixed::<2>(dst, src), + _ => dst[..n].copy_from_slice(&src[..n]), + } +} + +#[inline(always)] +fn avg(a: u8, b: u8) -> u8 { + ((a as u16 + b as u16 + 1) >> 1) as u8 +} + +/// Horizontal 6-tap (unclipped) at every position of `row` for `n` outputs: out[c] uses row[c..c + 6]. +#[inline(always)] +fn htap_row(row: &[u8], out: &mut [i32], n: usize) { + let row = &row[..n + 5]; + for (o, w) in out[..n].iter_mut().zip(row.windows(6)) { + *o = tap6(w[0] as i32, w[1] as i32, w[2] as i32, w[3] as i32, w[4] as i32, w[5] as i32); + } +} + +/// Vertical 6-tap (unclipped) over six rows at columns 0..n. +#[inline(always)] +fn vtap_row(rows: [&[u8]; 6], out: &mut [i32], n: usize) { + let [a, b, c, d, e, f] = rows.map(|r| &r[..n]); + for i in 0..n { + out[i] = tap6(a[i] as i32, b[i] as i32, c[i] as i32, d[i] as i32, e[i] as i32, f[i] as i32); + } +} + +/// Luma sample interpolation for a bw x bh block (8.4.2.2.1). (x, y) is the integer sample position +/// (block position + (mv >> 2)); (fx, fy) the quarter-sample fraction. Output stride is `os`. +#[cfg(test)] +#[allow(clippy::too_many_arguments)] +pub fn mc_luma(p: PlaneRef, x: i32, y: i32, fx: u32, fy: u32, bw: usize, bh: usize, out: &mut [u8], os: usize) { + let mut win = [0u8; WIN * WIN]; + let (x0, y0) = (x - 2, y - 2); + let inside = x0 >= 0 && y0 >= 0 && x0 as usize + bw + 5 <= p.width && y0 as usize + bh + 5 <= p.height; + let (src, ss): (&[u8], usize) = if inside { + (&p.data[y0 as usize * p.stride + x0 as usize..], p.stride) + } else { + fetch_window(p, x, y, bw, bh, &mut win); + (&win[..], WIN) + }; + mc_luma_win(src, ss, fx, fy, bw, bh, out, os); +} + +/// Luma interpolation from a prepared window: `src[0]` is the sample at (x - 2, y - 2) and `ss` the +/// window stride; the window covers (bw + 5) x (bh + 5) samples. +#[allow(clippy::too_many_arguments)] +pub fn mc_luma_win(src: &[u8], ss: usize, fx: u32, fy: u32, bw: usize, bh: usize, out: &mut [u8], os: usize) { + // window row r (0..bh+5) starts at src[r * ss]; sample G of block (r, c) is at window (r + 2, c + 2) + let wrow = |r: usize| &src[r * ss..r * ss + bw + 5]; + let mut t = [0i32; 16]; + match (fx, fy) { + (0, 0) => { + for r in 0..bh { + copy_n(&mut out[r * os..], &wrow(r + 2)[2..], bw); + } + } + (_, 0) => { + for r in 0..bh { + let row = wrow(r + 2); + htap_row(row, &mut t, bw); + let o = &mut out[r * os..r * os + bw]; + match fx { + 1 => { + for c in 0..bw { + o[c] = avg(row[c + 2], clip1((t[c] + 16) >> 5)); + } + } + 2 => { + for c in 0..bw { + o[c] = clip1((t[c] + 16) >> 5); + } + } + _ => { + for c in 0..bw { + o[c] = avg(row[c + 3], clip1((t[c] + 16) >> 5)); + } + } + } + } + } + (0, _) => { + for r in 0..bh { + let rows = [wrow(r), wrow(r + 1), wrow(r + 2), wrow(r + 3), wrow(r + 4), wrow(r + 5)].map(|x| &x[2..]); + vtap_row(rows, &mut t, bw); + let g = if fy == 1 { rows[2] } else { rows[3] }; + let o = &mut out[r * os..r * os + bw]; + if fy == 2 { + for c in 0..bw { + o[c] = clip1((t[c] + 16) >> 5); + } + } else { + for c in 0..bw { + o[c] = avg(g[c], clip1((t[c] + 16) >> 5)); + } + } + } + } + (2, _) | (_, 2) => { + // unclipped horizontal half samples b1 for window rows 0..bh+5 + let mut b1 = [[0i32; 16]; WIN]; + for (r, bt) in b1.iter_mut().enumerate().take(bh + 5) { + htap_row(wrow(r), bt, bw); + } + let mut v = [0i32; 16]; + for r in 0..bh { + let o = &mut out[r * os..r * os + bw]; + let (b0, b1r, b2, b3, b4, b5) = (&b1[r], &b1[r + 1], &b1[r + 2], &b1[r + 3], &b1[r + 4], &b1[r + 5]); + for c in 0..bw { + t[c] = tap6(b0[c], b1r[c], b2[c], b3[c], b4[c], b5[c]); + } + match (fx, fy) { + (2, 2) => { + for c in 0..bw { + o[c] = clip1((t[c] + 512) >> 10); + } + } + (2, 1) | (2, 3) => { + let bh_ = if fy == 1 { b2 } else { b3 }; + for c in 0..bw { + o[c] = avg(clip1((bh_[c] + 16) >> 5), clip1((t[c] + 512) >> 10)); + } + } + _ => { + let off = if fx == 1 { 2 } else { 3 }; + let rows = [wrow(r), wrow(r + 1), wrow(r + 2), wrow(r + 3), wrow(r + 4), wrow(r + 5)].map(|x| &x[off..]); + vtap_row(rows, &mut v, bw); + for c in 0..bw { + o[c] = avg(clip1((v[c] + 16) >> 5), clip1((t[c] + 512) >> 10)); + } + } + } + } + } + _ => { + // e, g, p, r: average of a horizontal half sample (row r or r + 1) and a vertical one (col c or c + 1) + let hoff = if fy == 1 { 2 } else { 3 }; + let voff = if fx == 1 { 2 } else { 3 }; + let mut v = [0i32; 16]; + for r in 0..bh { + htap_row(wrow(r + hoff), &mut t, bw); + let rows = [wrow(r), wrow(r + 1), wrow(r + 2), wrow(r + 3), wrow(r + 4), wrow(r + 5)].map(|x| &x[voff..]); + vtap_row(rows, &mut v, bw); + let o = &mut out[r * os..r * os + bw]; + for c in 0..bw { + o[c] = avg(clip1((t[c] + 16) >> 5), clip1((v[c] + 16) >> 5)); + } + } + } + } +} + +/// Chroma sample interpolation (8.4.2.2.2) for a bw x bh block at integer position (x, y) with +/// eighth-sample fraction (fx, fy). +#[cfg(test)] +#[allow(clippy::too_many_arguments)] +pub fn mc_chroma(p: PlaneRef, x: i32, y: i32, fx: u32, fy: u32, bw: usize, bh: usize, out: &mut [u8], os: usize) { + let maxx = p.width as i32 - 1; + let maxy = p.height as i32 - 1; + let inside = x >= 0 && y >= 0 && x + bw as i32 <= maxx && y + bh as i32 <= maxy; + // (bw+1) x (bh+1) source window; copied with edge clamping when it crosses the picture border + let mut win = [0u8; 9 * 9]; + let (src, ss): (&[u8], usize) = if inside { + (&p.data[y as usize * p.stride + x as usize..], p.stride) + } else { + for r in 0..=bh { + let yy = (y + r as i32).clamp(0, maxy) as usize; + for c in 0..=bw { + win[r * 9 + c] = p.data[yy * p.stride + (x + c as i32).clamp(0, maxx) as usize]; + } + } + (&win[..], 9) + }; + mc_chroma_win(src, ss, fx, fy, bw, bh, out, os); +} + +/// Chroma interpolation from a prepared (bw + 1) x (bh + 1) window whose first sample is the integer +/// position of the block. +#[allow(clippy::too_many_arguments)] +pub fn mc_chroma_win(src: &[u8], ss: usize, fx: u32, fy: u32, bw: usize, bh: usize, out: &mut [u8], os: usize) { + let (fx, fy) = (fx as u16, fy as u16); + let (w00, w10, w01, w11) = ((8 - fx) * (8 - fy), fx * (8 - fy), (8 - fx) * fy, fx * fy); + if fx == 0 && fy == 0 { + for r in 0..bh { + copy_n(&mut out[r * os..], &src[r * ss..], bw); + } + return; + } + for r in 0..bh { + let r0 = &src[r * ss..r * ss + bw + 1]; + let r1 = &src[(r + 1) * ss..(r + 1) * ss + bw + 1]; + let o = &mut out[r * os..r * os + bw]; + for (c, o) in o.iter_mut().enumerate() { + let v = w00 * r0[c] as u16 + w10 * r0[c + 1] as u16 + w01 * r1[c] as u16 + w11 * r1[c + 1] as u16; + *o = ((v + 32) >> 6) as u8; + } + } +} + +/// Weights for one prediction direction or pair. +#[derive(Clone, Copy, Debug, PartialEq, Eq)] +pub enum Weight { + /// Default: copy (single list) or rounded average (bi). + Default, + /// Explicit/implicit weighting: log2 denominator, weights and offsets for L0 and L1. + Weighted { log_wd: i32, w0: i32, w1: i32, o0: i32, o1: i32 }, +} + +/// Combine prediction(s) into `dst` (stride `ds`). `p0`/`p1` have stride `ps`. +#[allow(clippy::too_many_arguments)] +pub fn weighted_store(dst: &mut [u8], ds: usize, p0: Option<&[u8]>, p1: Option<&[u8]>, ps: usize, bw: usize, bh: usize, w: Weight) { + match (p0, p1, w) { + (Some(a), Some(b), Weight::Default) => { + for r in 0..bh { + let d = &mut dst[r * ds..r * ds + bw]; + let ra = &a[r * ps..r * ps + bw]; + let rb = &b[r * ps..r * ps + bw]; + for ((d, &x), &y) in d.iter_mut().zip(ra).zip(rb) { + *d = ((x as u32 + y as u32 + 1) >> 1) as u8; + } + } + } + (Some(a), Some(b), Weight::Weighted { log_wd, w0, w1, o0, o1 }) => { + let round = 1 << log_wd; + let off = (o0 + o1 + 1) >> 1; + let sh = log_wd + 1; + for r in 0..bh { + let d = &mut dst[r * ds..r * ds + bw]; + let ra = &a[r * ps..r * ps + bw]; + let rb = &b[r * ps..r * ps + bw]; + for ((d, &x), &y) in d.iter_mut().zip(ra).zip(rb) { + *d = clip1(((x as i32 * w0 + y as i32 * w1 + round) >> sh) + off); + } + } + } + (Some(a), None, w) | (None, Some(a), w) => { + let (wt, o, log_wd) = match (w, p0.is_some()) { + (Weight::Default, _) => { + for r in 0..bh { + copy_n(&mut dst[r * ds..], &a[r * ps..], bw); + } + return; + } + (Weight::Weighted { w0, o0, log_wd, .. }, true) => (w0, o0, log_wd), + (Weight::Weighted { w1, o1, log_wd, .. }, false) => (w1, o1, log_wd), + }; + let round = if log_wd >= 1 { 1 << (log_wd - 1) } else { 0 }; + for r in 0..bh { + let d = &mut dst[r * ds..r * ds + bw]; + let ra = &a[r * ps..r * ps + bw]; + for (d, &x) in d.iter_mut().zip(ra) { + *d = clip1(((x as i32 * wt + round) >> log_wd) + o); + } + } + } + (None, None, _) => {} + } +} + +#[cfg(test)] +mod reference { + use super::*; + + /// Luma sample interpolation for a bw x bh block. (x, y) is the integer sample position + /// (block position + (mv >> 2)); (fx, fy) the quarter-sample fraction. Output stride is `os`. + #[allow(clippy::too_many_arguments)] + pub fn mc_luma_ref(p: PlaneRef, x: i32, y: i32, fx: u32, fy: u32, bw: usize, bh: usize, out: &mut [u8], os: usize) { + let mut win = [0u8; WIN * WIN]; + fetch_window(p, x, y, bw, bh, &mut win); + // sample G at (r, c) in block coordinates is win[(r + 2) * WIN + c + 2] + let g = |r: usize, c: usize| win[(r + 2) * WIN + c + 2] as i32; + // unclipped horizontal half-sample b1 at block row r (-2..bh+3 via offset) and column c + let b1 = |r: isize, c: usize| { + let base = ((r + 2) as usize) * WIN + c; + tap6(win[base] as i32, win[base + 1] as i32, win[base + 2] as i32, win[base + 3] as i32, win[base + 4] as i32, win[base + 5] as i32) + }; + let h1 = |r: usize, c: isize| { + let col = (c + 2) as usize; + tap6( + win[r * WIN + col] as i32, + win[(r + 1) * WIN + col] as i32, + win[(r + 2) * WIN + col] as i32, + win[(r + 3) * WIN + col] as i32, + win[(r + 4) * WIN + col] as i32, + win[(r + 5) * WIN + col] as i32, + ) + }; + match (fx, fy) { + (0, 0) => { + for r in 0..bh { + out[r * os..r * os + bw].copy_from_slice(&win[(r + 2) * WIN + 2..(r + 2) * WIN + 2 + bw]); + } + } + (_, 0) => { + // a, b, c + for r in 0..bh { + for c in 0..bw { + let b = clip1((b1(r as isize, c) + 16) >> 5) as i32; + out[r * os + c] = match fx { + 1 => ((g(r, c) + b + 1) >> 1) as u8, + 2 => b as u8, + _ => ((g(r, c + 1) + b + 1) >> 1) as u8, + }; + } + } + } + (0, _) => { + // d, h, n + for r in 0..bh { + for c in 0..bw { + let h = clip1((h1(r, c as isize) + 16) >> 5) as i32; + out[r * os + c] = match fy { + 1 => ((g(r, c) + h + 1) >> 1) as u8, + 2 => h as u8, + _ => ((g(r + 1, c) + h + 1) >> 1) as u8, + }; + } + } + } + (2, _) | (_, 2) => { + // j-based: j needs b1 for rows -2..bh+3 + let mut bcol = [0i32; WIN * WIN]; + for r in 0..bh + 5 { + for c in 0..bw { + bcol[r * WIN + c] = b1(r as isize - 2, c); + } + } + for r in 0..bh { + for c in 0..bw { + let j1 = tap6( + bcol[r * WIN + c], + bcol[(r + 1) * WIN + c], + bcol[(r + 2) * WIN + c], + bcol[(r + 3) * WIN + c], + bcol[(r + 4) * WIN + c], + bcol[(r + 5) * WIN + c], + ); + let j = clip1((j1 + 512) >> 10) as i32; + let v = match (fx, fy) { + (2, 2) => j, + (2, 1) => (clip1((bcol[(r + 2) * WIN + c] + 16) >> 5) as i32 + j + 1) >> 1, // f + (2, 3) => (clip1((bcol[(r + 3) * WIN + c] + 16) >> 5) as i32 + j + 1) >> 1, // q + (1, 2) => (clip1((h1(r, c as isize) + 16) >> 5) as i32 + j + 1) >> 1, // i + _ => (clip1((h1(r, c as isize + 1) + 16) >> 5) as i32 + j + 1) >> 1, // k + }; + out[r * os + c] = v as u8; + } + } + } + _ => { + // e, g, p, r: average of a horizontal half-sample (b or s) and a vertical one (h or m) + for r in 0..bh { + for c in 0..bw { + let hr = if fy == 1 { r as isize } else { r as isize + 1 }; + let vc = if fx == 1 { c as isize } else { c as isize + 1 }; + let bh_ = clip1((b1(hr, c) + 16) >> 5) as i32; + let vv = clip1((h1(r, vc) + 16) >> 5) as i32; + out[r * os + c] = ((bh_ + vv + 1) >> 1) as u8; + } + } + } + } + } +} + +#[cfg(test)] +mod tests { + use super::*; + + fn plane(w: usize, h: usize, f: impl Fn(usize, usize) -> u8) -> Vec { + (0..w * h).map(|i| f(i % w, i / w)).collect() + } + + #[test] + fn full_sample_copy_and_clamp() { + let d = plane(16, 16, |x, y| (x + 16 * y) as u8); + let p = PlaneRef { data: &d, width: 16, height: 16, stride: 16 }; + let mut out = [0u8; 16]; + mc_luma(p, 2, 3, 0, 0, 4, 4, &mut out, 4); + assert_eq!(&out[..4], &[50, 51, 52, 53]); + // clamped: far outside to the top-left gives sample (0,0) + mc_luma(p, -40, -40, 0, 0, 4, 4, &mut out, 4); + assert!(out.iter().all(|&v| v == 0)); + } + + #[test] + fn half_sample_constant_is_constant() { + let d = vec![100u8; 32 * 32]; + let p = PlaneRef { data: &d, width: 32, height: 32, stride: 32 }; + for fx in 0..4 { + for fy in 0..4 { + let mut out = [0u8; 64]; + mc_luma(p, 8, 8, fx, fy, 8, 8, &mut out, 8); + assert!(out.iter().all(|&v| v == 100), "{fx},{fy}"); + mc_chroma(p, 8, 8, fx * 2, fy * 2, 8, 8, &mut out, 8); + assert!(out.iter().all(|&v| v == 100)); + } + } + } + + #[test] + fn half_sample_horizontal_ramp() { + // linear ramp: 6-tap filter of a linear function is exact: b = (G + H) / 2 (rounded) + let d = plane(32, 32, |x, _| (x * 4) as u8); + let p = PlaneRef { data: &d, width: 32, height: 32, stride: 32 }; + let mut out = [0u8; 16]; + mc_luma(p, 8, 8, 2, 0, 4, 4, &mut out, 4); + assert_eq!(&out[..4], &[34, 38, 42, 46]); + mc_luma(p, 8, 8, 1, 0, 4, 4, &mut out, 4); + assert_eq!(&out[..4], &[33, 37, 41, 45]); + } + + #[test] + fn fast_luma_matches_reference() { + let mut seed = 7u32; + let d: Vec = (0..40 * 36) + .map(|_| { + seed = seed.wrapping_mul(1664525).wrapping_add(1013904223); + (seed >> 24) as u8 + }) + .collect(); + let p = PlaneRef { data: &d, width: 40, height: 36, stride: 40 }; + for &(bw, bh) in &[(16, 16), (16, 8), (8, 16), (8, 8), (8, 4), (4, 8), (4, 4)] { + for fx in 0..4 { + for fy in 0..4 { + for &(x, y) in &[(10, 9), (-3, -5), (30, 30), (0, 0), (2, 2), (38, 1)] { + let mut a = [0u8; 256]; + let mut b = [0u8; 256]; + mc_luma(p, x, y, fx, fy, bw, bh, &mut a, 16); + reference::mc_luma_ref(p, x, y, fx, fy, bw, bh, &mut b, 16); + assert_eq!(a, b, "{bw}x{bh} frac {fx},{fy} at {x},{y}"); + } + } + } + } + } + + #[test] + fn weighting() { + let a = [100u8; 4]; + let b = [50u8; 4]; + let mut d = [0u8; 4]; + weighted_store(&mut d, 4, Some(&a), Some(&b), 4, 4, 1, Weight::Default); + assert_eq!(d, [75; 4]); + weighted_store(&mut d, 4, Some(&a), None, 4, 4, 1, Weight::Weighted { log_wd: 5, w0: 16, w1: 0, o0: 3, o1: 0 }); + assert_eq!(d, [53; 4]); + } +} diff --git a/crates/h264/src/intra.rs b/crates/h264/src/intra.rs new file mode 100644 index 0000000..decf296 --- /dev/null +++ b/crates/h264/src/intra.rs @@ -0,0 +1,462 @@ +//! Intra prediction (8.3): Intra_4x4, Intra_8x8 (with reference filtering), Intra_16x16 and chroma. + +/// Neighbouring sample availability for one block. +#[derive(Clone, Copy, Debug, Default)] +pub struct Avail { + pub left: bool, + pub top: bool, + pub top_left: bool, + pub top_right: bool, +} + +/// Neighbouring samples of an NxN block: p[x,-1] for x = 0..2N-1, p[-1,y] for y = 0..N-1, p[-1,-1]. +#[derive(Clone, Copy)] +struct Edge { + top: [i32; 16], + left: [i32; 8], + tl: i32, +} + +impl Edge { + /// p[x, -1] for x >= -1 + #[inline(always)] + fn t(&self, x: i32) -> i32 { + if x < 0 { self.tl } else { self.top[x as usize] } + } + /// p[-1, y] for y >= -1 + #[inline(always)] + fn l(&self, y: i32) -> i32 { + if y < 0 { self.tl } else { self.left[y as usize] } + } +} + +fn load_edge(plane: &[u8], stride: usize, x: usize, y: usize, n: usize, a: Avail) -> Edge { + let mut e = Edge { top: [128; 16], left: [128; 8], tl: 128 }; + if a.top { + let row = &plane[(y - 1) * stride + x..]; + for i in 0..n { + e.top[i] = row[i] as i32; + } + if a.top_right { + for i in n..2 * n { + e.top[i] = row[i] as i32; + } + } else { + let v = e.top[n - 1]; + for t in &mut e.top[n..2 * n] { + *t = v; + } + } + } + if a.left { + for i in 0..n { + e.left[i] = plane[(y + i) * stride + x - 1] as i32; + } + } + if a.top_left { + e.tl = plane[(y - 1) * stride + x - 1] as i32; + } + e +} + +/// Directional / DC prediction shared by Intra_4x4 and Intra_8x8 (8.3.1.2.x, 8.3.2.2.x). +fn pred_nxn(mode: u8, e: &Edge, n: i32, a: Avail, out: &mut [u8], stride: usize) { + let mut put = |x: i32, y: i32, v: i32| out[y as usize * stride + x as usize] = v as u8; + match mode { + 0 => { + for y in 0..n { + for x in 0..n { + put(x, y, e.t(x)); + } + } + } + 1 => { + for y in 0..n { + for x in 0..n { + put(x, y, e.l(y)); + } + } + } + 2 => { + let shift = if n == 4 { 2 } else { 3 }; + let st: i32 = (0..n).map(|x| e.t(x)).sum(); + let sl: i32 = (0..n).map(|y| e.l(y)).sum(); + let dc = match (a.top, a.left) { + (true, true) => (st + sl + n) >> (shift + 1), + (false, true) => (sl + (n >> 1)) >> shift, + (true, false) => (st + (n >> 1)) >> shift, + _ => 128, + }; + for y in 0..n { + for x in 0..n { + put(x, y, dc); + } + } + } + 3 => { + for y in 0..n { + for x in 0..n { + let v = if x == n - 1 && y == n - 1 { + (e.t(2 * n - 2) + 3 * e.t(2 * n - 1) + 2) >> 2 + } else { + (e.t(x + y) + 2 * e.t(x + y + 1) + e.t(x + y + 2) + 2) >> 2 + }; + put(x, y, v); + } + } + } + 4 => { + for y in 0..n { + for x in 0..n { + let v = if x > y { + (e.t(x - y - 2) + 2 * e.t(x - y - 1) + e.t(x - y) + 2) >> 2 + } else if x < y { + (e.l(y - x - 2) + 2 * e.l(y - x - 1) + e.l(y - x) + 2) >> 2 + } else { + (e.t(0) + 2 * e.tl + e.l(0) + 2) >> 2 + }; + put(x, y, v); + } + } + } + 5 => { + for y in 0..n { + for x in 0..n { + let z = 2 * x - y; + let v = if z >= 0 && z % 2 == 0 { + (e.t(x - (y >> 1) - 1) + e.t(x - (y >> 1)) + 1) >> 1 + } else if z > 0 { + (e.t(x - (y >> 1) - 2) + 2 * e.t(x - (y >> 1) - 1) + e.t(x - (y >> 1)) + 2) >> 2 + } else if z == -1 { + (e.l(0) + 2 * e.tl + e.t(0) + 2) >> 2 + } else { + (e.l(y - 2 * x - 1) + 2 * e.l(y - 2 * x - 2) + e.l(y - 2 * x - 3) + 2) >> 2 + }; + put(x, y, v); + } + } + } + 6 => { + for y in 0..n { + for x in 0..n { + let z = 2 * y - x; + let v = if z >= 0 && z % 2 == 0 { + (e.l(y - (x >> 1) - 1) + e.l(y - (x >> 1)) + 1) >> 1 + } else if z > 0 { + (e.l(y - (x >> 1) - 2) + 2 * e.l(y - (x >> 1) - 1) + e.l(y - (x >> 1)) + 2) >> 2 + } else if z == -1 { + (e.l(0) + 2 * e.tl + e.t(0) + 2) >> 2 + } else { + (e.t(x - 2 * y - 1) + 2 * e.t(x - 2 * y - 2) + e.t(x - 2 * y - 3) + 2) >> 2 + }; + put(x, y, v); + } + } + } + 7 => { + for y in 0..n { + for x in 0..n { + let i = x + (y >> 1); + let v = if y % 2 == 0 { (e.t(i) + e.t(i + 1) + 1) >> 1 } else { (e.t(i) + 2 * e.t(i + 1) + e.t(i + 2) + 2) >> 2 }; + put(x, y, v); + } + } + } + _ => { + let zmax = 2 * n - 3; + for y in 0..n { + for x in 0..n { + let z = x + 2 * y; + let i = y + (x >> 1); + let v = if z < zmax && z % 2 == 0 { + (e.l(i) + e.l(i + 1) + 1) >> 1 + } else if z < zmax { + (e.l(i) + 2 * e.l(i + 1) + e.l(i + 2) + 2) >> 2 + } else if z == zmax { + (e.l(n - 2) + 3 * e.l(n - 1) + 2) >> 2 + } else { + e.l(n - 1) + }; + put(x, y, v); + } + } + } + } +} + +/// Intra_4x4 prediction written into `plane` at (x, y). +pub fn pred4x4(plane: &mut [u8], stride: usize, x: usize, y: usize, mode: u8, a: Avail) { + let e = load_edge(plane, stride, x, y, 4, a); + pred_nxn(mode, &e, 4, a, &mut plane[y * stride + x..], stride); +} + +/// Intra_8x8 prediction (with reference sample filtering) written into `plane` at (x, y). +pub fn pred8x8(plane: &mut [u8], stride: usize, x: usize, y: usize, mode: u8, a: Avail) { + let p = load_edge(plane, stride, x, y, 8, a); + let mut f = p; + if a.top { + f.top[0] = if a.top_left { (p.tl + 2 * p.top[0] + p.top[1] + 2) >> 2 } else { (3 * p.top[0] + p.top[1] + 2) >> 2 }; + for i in 1..15 { + f.top[i] = (p.top[i - 1] + 2 * p.top[i] + p.top[i + 1] + 2) >> 2; + } + f.top[15] = (p.top[14] + 3 * p.top[15] + 2) >> 2; + } + if a.top_left { + f.tl = match (a.top, a.left) { + (true, true) => (p.top[0] + 2 * p.tl + p.left[0] + 2) >> 2, + (true, false) => (3 * p.tl + p.top[0] + 2) >> 2, + (false, true) => (3 * p.tl + p.left[0] + 2) >> 2, + _ => p.tl, + }; + } + if a.left { + f.left[0] = if a.top_left { (p.tl + 2 * p.left[0] + p.left[1] + 2) >> 2 } else { (3 * p.left[0] + p.left[1] + 2) >> 2 }; + for i in 1..7 { + f.left[i] = (p.left[i - 1] + 2 * p.left[i] + p.left[i + 1] + 2) >> 2; + } + f.left[7] = (p.left[6] + 3 * p.left[7] + 2) >> 2; + } + pred_nxn(mode, &f, 8, a, &mut plane[y * stride + x..], stride); +} + +#[inline(always)] +fn clip(v: i32) -> u8 { + v.clamp(0, 255) as u8 +} + +/// Intra_16x16 prediction (8.3.3) for the macroblock at luma (x, y). +pub fn pred16x16(plane: &mut [u8], stride: usize, x: usize, y: usize, mode: u8, a: Avail) { + let mut top = [0i32; 16]; + let mut left = [0i32; 16]; + if a.top { + for i in 0..16 { + top[i] = plane[(y - 1) * stride + x + i] as i32; + } + } + if a.left { + for i in 0..16 { + left[i] = plane[(y + i) * stride + x - 1] as i32; + } + } + let base = y * stride + x; + match mode { + 0 => { + for r in 0..16 { + for c in 0..16 { + plane[base + r * stride + c] = top[c] as u8; + } + } + } + 1 => { + for r in 0..16 { + plane[base + r * stride..base + r * stride + 16].fill(left[r] as u8); + } + } + 2 => { + let st: i32 = top.iter().sum(); + let sl: i32 = left.iter().sum(); + let dc = match (a.top, a.left) { + (true, true) => (st + sl + 16) >> 5, + (false, true) => (sl + 8) >> 4, + (true, false) => (st + 8) >> 4, + _ => 128, + } as u8; + for r in 0..16 { + plane[base + r * stride..base + r * stride + 16].fill(dc); + } + } + _ => { + let tl = if a.top_left { plane[(y - 1) * stride + x - 1] as i32 } else { 128 }; + let t = |i: i32| if i < 0 { tl } else { top[i as usize] }; + let l = |i: i32| if i < 0 { tl } else { left[i as usize] }; + let mut h = 0; + let mut v = 0; + for k in 0..8 { + h += (k + 1) * (t(8 + k) - t(6 - k)); + v += (k + 1) * (l(8 + k) - l(6 - k)); + } + let aa = 16 * (left[15] + top[15]); + let b = (5 * h + 32) >> 6; + let c = (5 * v + 32) >> 6; + for r in 0..16 { + for cc in 0..16 { + plane[base + r * stride + cc] = clip((aa + b * (cc as i32 - 7) + c * (r as i32 - 7) + 16) >> 5); + } + } + } + } +} + +/// Chroma intra prediction for a 4:2:0 macroblock (8x8 block at chroma (x, y)), 8.3.4. +/// Modes: 0 DC, 1 horizontal, 2 vertical, 3 plane. +pub fn pred_chroma(plane: &mut [u8], stride: usize, x: usize, y: usize, mode: u8, a: Avail) { + const W: usize = 8; + const H: usize = 8; + let mut top = [0i32; W]; + let mut left = [0i32; H]; + if a.top { + for i in 0..W { + top[i] = plane[(y - 1) * stride + x + i] as i32; + } + } + if a.left { + for i in 0..H { + left[i] = plane[(y + i) * stride + x - 1] as i32; + } + } + let base = y * stride + x; + match mode { + 0 => { + for by in 0..H / 4 { + for bx in 0..W / 4 { + let st: i32 = top[bx * 4..bx * 4 + 4].iter().sum(); + let sl: i32 = left[by * 4..by * 4 + 4].iter().sum(); + let dc = if (bx == 0 && by == 0) || (bx > 0 && by > 0) { + match (a.top, a.left) { + (true, true) => (st + sl + 4) >> 3, + (false, true) => (sl + 2) >> 2, + (true, false) => (st + 2) >> 2, + _ => 128, + } + } else if bx > 0 { + if a.top { + (st + 2) >> 2 + } else if a.left { + (sl + 2) >> 2 + } else { + 128 + } + } else if a.left { + (sl + 2) >> 2 + } else if a.top { + (st + 2) >> 2 + } else { + 128 + }; + for r in 0..4 { + let o = base + (by * 4 + r) * stride + bx * 4; + plane[o..o + 4].fill(dc as u8); + } + } + } + } + 1 => { + for r in 0..H { + plane[base + r * stride..base + r * stride + W].fill(left[r] as u8); + } + } + 2 => { + for r in 0..H { + for c in 0..W { + plane[base + r * stride + c] = top[c] as u8; + } + } + } + _ => { + let tl = if a.top_left { plane[(y - 1) * stride + x - 1] as i32 } else { 128 }; + let t = |i: i32| if i < 0 { tl } else { top[i as usize] }; + let l = |i: i32| if i < 0 { tl } else { left[i as usize] }; + let mut h = 0; + let mut v = 0; + for k in 0..4 { + h += (k + 1) * (t(4 + k) - t(2 - k)); + v += (k + 1) * (l(4 + k) - l(2 - k)); + } + let aa = 16 * (left[H - 1] + top[W - 1]); + let b = (34 * h + 32) >> 6; + let c = (34 * v + 32) >> 6; + for r in 0..H { + for cc in 0..W { + plane[base + r * stride + cc] = clip((aa + b * (cc as i32 - 3) + c * (r as i32 - 3) + 16) >> 5); + } + } + } + } +} + +#[cfg(test)] +mod tests { + use super::*; + + fn setup() -> (Vec, usize) { + // 12x12 plane; block at (4,4); top row y=3 = 10,20,..; left column x=3 = 1,2,3,... + let stride = 16; + let mut p = vec![0u8; stride * 16]; + for i in 0..12 { + p[3 * stride + 4 + i] = (10 * (i + 1)) as u8; + } + for i in 0..8 { + p[(4 + i) * stride + 3] = (i + 1) as u8; + } + p[3 * stride + 3] = 5; + (p, stride) + } + const ALL: Avail = Avail { left: true, top: true, top_left: true, top_right: true }; + + #[test] + fn i4_vertical_horizontal_dc() { + let (mut p, s) = setup(); + pred4x4(&mut p, s, 4, 4, 0, ALL); + assert_eq!(&p[4 * s + 4..4 * s + 8], &[10, 20, 30, 40]); + assert_eq!(&p[7 * s + 4..7 * s + 8], &[10, 20, 30, 40]); + pred4x4(&mut p, s, 4, 4, 1, ALL); + assert_eq!(&p[5 * s + 4..5 * s + 8], &[2, 2, 2, 2]); + pred4x4(&mut p, s, 4, 4, 2, ALL); + // (10+20+30+40 + 1+2+3+4 + 4) >> 3 = 114 >> 3 = 14 + assert_eq!(p[4 * s + 4], 14); + } + + #[test] + fn i4_diag_down_left_hand_computed() { + let (mut p, s) = setup(); + pred4x4(&mut p, s, 4, 4, 3, ALL); + // pred[0,0] = (t0 + 2 t1 + t2 + 2) >> 2 = (10 + 40 + 30 + 2) >> 2 = 20 + assert_eq!(p[4 * s + 4], 20); + // pred[3,3] = (t6 + 3 t7 + 2) >> 2 = (70 + 240 + 2) >> 2 = 78 + assert_eq!(p[7 * s + 7], 78); + } + + #[test] + fn i4_diag_down_right_hand_computed() { + let (mut p, s) = setup(); + pred4x4(&mut p, s, 4, 4, 4, ALL); + // x==y: (t0 + 2 tl + l0 + 2) >> 2 = (10 + 10 + 1 + 2) >> 2 = 5 + assert_eq!(p[4 * s + 4], 5); + // x=1,y=0: (tl + 2 t0 + t1 + 2)>>2 = (5 + 20 + 20 + 2) >> 2 = 11 + assert_eq!(p[4 * s + 5], 11); + // x=0,y=1: (tl + 2 l0 + l1 + 2) >> 2 = (5 + 2 + 2 + 2) >> 2 = 2 + assert_eq!(p[5 * s + 4], 2); + } + + #[test] + fn i4_top_right_substitution() { + let (mut p, s) = setup(); + let a = Avail { top_right: false, ..ALL }; + pred4x4(&mut p, s, 4, 4, 3, a); + // top-right replaced by t3 = 40: pred[3,3] = (40 + 120 + 2) >> 2 = 40 + assert_eq!(p[7 * s + 7], 40); + } + + #[test] + fn i16_plane_flat_is_flat() { + let s = 32; + let mut p = vec![77u8; s * 32]; + pred16x16(&mut p, s, 8, 8, 3, ALL); + assert!(p[8 * s + 8..8 * s + 24].iter().all(|&v| v == 77)); + } + + #[test] + fn chroma_dc_rules() { + let s = 16; + let mut p = vec![0u8; s * 16]; + for i in 0..8 { + p[3 * s + 4 + i] = if i < 4 { 40 } else { 80 }; + p[(4 + i) * s + 3] = if i < 4 { 8 } else { 16 }; + } + pred_chroma(&mut p, s, 4, 4, 0, ALL); + assert_eq!(p[4 * s + 4], (160 + 32 + 4) >> 3); // top-left block: both + assert_eq!(p[4 * s + 8], 80); // top-right block: top only + assert_eq!(p[8 * s + 4], 16); // bottom-left: left only + assert_eq!(p[8 * s + 8], ((320 + 64 + 4) >> 3) as u8); // bottom-right: both + } +} diff --git a/crates/h264/src/lib.rs b/crates/h264/src/lib.rs new file mode 100644 index 0000000..0173e9d --- /dev/null +++ b/crates/h264/src/lib.rs @@ -0,0 +1,86 @@ +//! Clean-room pure-Rust H.264 / AVC decoder, implemented from ITU-T Rec. H.264 (ISO/IEC 14496-10). +//! +//! Supported: progressive (frame) coding, 8-bit 4:2:0, Baseline / Main / High profiles — CAVLC and CABAC, +//! I/P/B slices, 8x8 transform, custom scaling matrices, weighted prediction, spatial/temporal direct, +//! multiple slices, long-term references and all MMCOs, deblocking. +//! +//! ```no_run +//! let mut dec = slidecraft_h264::Decoder::new(); +//! let stream = std::fs::read("video.h264").unwrap(); +//! for pic in dec.decode(&stream, 0).unwrap() { +//! println!("{}x{} poc {}", pic.width, pic.height, pic.poc); +//! } +//! for pic in dec.flush() { +//! let _ = pic; +//! } +//! ``` + +// Index loops over fixed-size blocks read more clearly than iterator chains in codec code. +#![allow(clippy::needless_range_loop, clippy::too_many_arguments)] +#![cfg_attr(not(test), deny(clippy::unwrap_used, clippy::expect_used, clippy::panic, clippy::unimplemented, clippy::todo, clippy::unreachable))] + +mod cabac; +mod cabac_mb; +#[rustfmt::skip] +mod cabac_tables; +mod cavlc; +#[rustfmt::skip] +mod cavlc_tables; +mod deblock; +mod decoder; +mod dpb; +mod error; +mod inter; +mod intra; +mod mbtypes; +pub mod params; +mod picture; +pub use picture::set_plane_allocator; +pub mod slice; +mod slicedec; +#[cfg(test)] +mod synth_tests; +mod tables; +mod transform; + +pub use decoder::{DecodeStats, Decoder, default_threads}; +pub use error::{Error, Result}; + +/// Colour description from the VUI (ITU-T H.273 code points). +#[derive(Clone, Copy, Debug, PartialEq, Eq)] +pub struct ColorInfo { + /// `video_full_range_flag`. + pub full_range: bool, + pub primaries: u8, + pub transfer: u8, + pub matrix: u8, +} + +/// A decoded, cropped picture in 8-bit planar 4:2:0. +#[derive(Clone, Debug)] +pub struct Picture { + /// Cropped luma width. + pub width: u32, + /// Cropped luma height. + pub height: u32, + pub chroma_width: u32, + pub chroma_height: u32, + pub y: Vec, + pub u: Vec, + pub v: Vec, + pub y_stride: usize, + pub uv_stride: usize, + /// Presentation timestamp passed to [`Decoder::decode`] with the access unit of this picture. + pub pts: i64, + /// Picture order count. + pub poc: i32, + /// IDR picture. + pub key: bool, + pub color: ColorInfo, + /// Sample aspect ratio (0, 0 when unspecified). + pub sar: (u16, u16), + /// Decoded in draft mode ([`Decoder::set_draft`]): a non-reference picture whose deblocking + /// filter was skipped. Its samples are approximate (not the conforming output); no other + /// picture is affected. + pub draft: bool, +} diff --git a/crates/h264/src/mbtypes.rs b/crates/h264/src/mbtypes.rs new file mode 100644 index 0000000..87aa206 --- /dev/null +++ b/crates/h264/src/mbtypes.rs @@ -0,0 +1,235 @@ +//! Macroblock / sub-macroblock type tables (7.4.5, Tables 7-11 .. 7-18). + +use crate::picture::MbKind; + +pub const PRED_L0: u8 = 1; +pub const PRED_L1: u8 = 2; +pub const PRED_BI: u8 = 3; + +/// Macroblock partitioning for inter macroblocks. +#[derive(Clone, Copy, Debug, PartialEq, Eq, Default)] +pub enum Part { + #[default] + P16x16, + P16x8, + P8x16, + P8x8, +} + +impl Part { + pub fn num_parts(self) -> usize { + match self { + Part::P16x16 => 1, + Part::P16x8 | Part::P8x16 => 2, + Part::P8x8 => 4, + } + } + /// (x, y, w, h) of partition `i` in 4x4-block units. + pub fn rect(self, i: usize) -> (usize, usize, usize, usize) { + match self { + Part::P16x16 => (0, 0, 4, 4), + Part::P16x8 => (0, 2 * i, 4, 2), + Part::P8x16 => (2 * i, 0, 2, 4), + Part::P8x8 => ((i & 1) * 2, (i >> 1) * 2, 2, 2), + } + } +} + +/// Sub-macroblock partitioning. +#[derive(Clone, Copy, Debug, PartialEq, Eq, Default)] +pub enum SubPart { + #[default] + S8x8, + S8x4, + S4x8, + S4x4, +} + +impl SubPart { + pub fn num_parts(self) -> usize { + match self { + SubPart::S8x8 => 1, + SubPart::S8x4 | SubPart::S4x8 => 2, + SubPart::S4x4 => 4, + } + } + /// (x, y, w, h) of sub-partition `j` relative to the 8x8 block, in 4x4 units. + pub fn rect(self, j: usize) -> (usize, usize, usize, usize) { + match self { + SubPart::S8x8 => (0, 0, 2, 2), + SubPart::S8x4 => (0, j, 2, 1), + SubPart::S4x8 => (j, 0, 1, 2), + SubPart::S4x4 => (j & 1, j >> 1, 1, 1), + } + } +} + +/// Decoded meaning of mb_type. +#[derive(Clone, Copy, Debug, Default)] +pub struct MbTypeInfo { + pub kind: MbKind, + /// Intra16x16PredMode. + pub i16_mode: u8, + /// coded_block_pattern implied by an Intra_16x16 mb_type. + pub i16_cbp: u8, + pub part: Part, + /// Prediction list flags per macroblock partition. + pub pred: [u8; 2], + /// P_8x8ref0. + pub ref0: bool, +} + +/// Intra mb_type (I-slice numbering 0..=25). +pub fn intra_mb_type(t: u32) -> Option { + let mut m = MbTypeInfo::default(); + match t { + 0 => m.kind = MbKind::I4x4, // or I8x8, decided by transform_size_8x8_flag + 1..=24 => { + m.kind = MbKind::I16x16; + m.i16_mode = ((t - 1) % 4) as u8; + let chroma = ((t - 1) / 4) % 3; + let luma = if t >= 13 { 15 } else { 0 }; + m.i16_cbp = (luma | (chroma << 4)) as u8; + } + 25 => m.kind = MbKind::IPcm, + _ => return None, + } + Some(m) +} + +/// P-slice mb_type 0..=4 (5.. are intra, handled by the caller). +pub fn p_mb_type(t: u32) -> Option { + let mut m = MbTypeInfo { kind: MbKind::Inter, pred: [PRED_L0; 2], ..Default::default() }; + m.part = match t { + 0 => Part::P16x16, + 1 => Part::P16x8, + 2 => Part::P8x16, + 3 => Part::P8x8, + 4 => { + m.ref0 = true; + Part::P8x8 + } + _ => return None, + }; + Some(m) +} + +/// B-slice mb_type 0..=22 (23.. are intra). +pub fn b_mb_type(t: u32) -> Option { + let mut m = MbTypeInfo { kind: MbKind::Inter, ..Default::default() }; + match t { + 0 => { + m.kind = MbKind::BDirect16x16; + m.pred = [PRED_BI; 2]; + } + 1..=3 => { + m.part = Part::P16x16; + m.pred = [t as u8; 2]; + } + 4..=21 => { + const PAIRS: [(u8, u8); 9] = [ + (PRED_L0, PRED_L0), + (PRED_L1, PRED_L1), + (PRED_L0, PRED_L1), + (PRED_L1, PRED_L0), + (PRED_L0, PRED_BI), + (PRED_L1, PRED_BI), + (PRED_BI, PRED_L0), + (PRED_BI, PRED_L1), + (PRED_BI, PRED_BI), + ]; + let (a, b) = PAIRS[((t - 4) / 2) as usize]; + m.pred = [a, b]; + m.part = if t.is_multiple_of(2) { Part::P16x8 } else { Part::P8x16 }; + } + 22 => m.part = Part::P8x8, + _ => return None, + } + Some(m) +} + +/// Sub-macroblock type: (shape, prediction flags, direct). +#[derive(Clone, Copy, Debug, Default, PartialEq, Eq)] +pub struct SubMbInfo { + pub shape: SubPart, + pub pred: u8, + pub direct: bool, +} + +pub fn p_sub_mb_type(t: u32) -> Option { + let shape = match t { + 0 => SubPart::S8x8, + 1 => SubPart::S8x4, + 2 => SubPart::S4x8, + 3 => SubPart::S4x4, + _ => return None, + }; + Some(SubMbInfo { shape, pred: PRED_L0, direct: false }) +} + +pub fn b_sub_mb_type(t: u32) -> Option { + let (shape, pred, direct) = match t { + 0 => (SubPart::S8x8, PRED_BI, true), + 1 => (SubPart::S8x8, PRED_L0, false), + 2 => (SubPart::S8x8, PRED_L1, false), + 3 => (SubPart::S8x8, PRED_BI, false), + 4 => (SubPart::S8x4, PRED_L0, false), + 5 => (SubPart::S4x8, PRED_L0, false), + 6 => (SubPart::S8x4, PRED_L1, false), + 7 => (SubPart::S4x8, PRED_L1, false), + 8 => (SubPart::S8x4, PRED_BI, false), + 9 => (SubPart::S4x8, PRED_BI, false), + 10 => (SubPart::S4x4, PRED_L0, false), + 11 => (SubPart::S4x4, PRED_L1, false), + 12 => (SubPart::S4x4, PRED_BI, false), + _ => return None, + }; + Some(SubMbInfo { shape, pred, direct }) +} + +/// Table 9-4 (a): codeNum -> coded_block_pattern for (Intra_4x4/Intra_8x8, Inter), ChromaArrayType 1 or 2. +#[rustfmt::skip] +pub const CBP_ME: [(u8, u8); 48] = [ + (47, 0), (31, 16), (15, 1), (0, 2), (23, 4), (27, 8), (29, 32), (30, 3), + (7, 5), (11, 10), (13, 12), (14, 15), (39, 47), (43, 7), (45, 11), (46, 13), + (16, 14), (3, 6), (5, 9), (10, 31), (12, 35), (19, 37), (21, 42), (26, 44), + (28, 33), (35, 34), (37, 36), (42, 40), (44, 39), (1, 43), (2, 45), (4, 46), + (8, 17), (17, 18), (18, 20), (20, 24), (24, 19), (6, 21), (9, 26), (22, 28), + (25, 23), (32, 27), (33, 29), (34, 30), (36, 22), (40, 25), (38, 38), (41, 41), +]; + +#[cfg(test)] +mod tests { + use super::*; + + #[test] + fn cbp_table_is_permutation() { + let mut a = [false; 48]; + let mut b = [false; 48]; + for &(i, p) in &CBP_ME { + a[i as usize] = true; + b[p as usize] = true; + } + assert!(a.iter().all(|&x| x) && b.iter().all(|&x| x)); + } + + #[test] + fn i16_types() { + let m = intra_mb_type(1).unwrap(); + assert_eq!((m.kind, m.i16_mode, m.i16_cbp), (MbKind::I16x16, 0, 0)); + let m = intra_mb_type(24).unwrap(); + assert_eq!((m.i16_mode, m.i16_cbp), (3, 15 | (2 << 4))); + let m = intra_mb_type(13).unwrap(); + assert_eq!((m.i16_mode, m.i16_cbp), (0, 15)); + } + + #[test] + fn b_types() { + let m = b_mb_type(12).unwrap(); + assert_eq!((m.part, m.pred), (Part::P16x8, [PRED_L0, PRED_BI])); + let m = b_mb_type(21).unwrap(); + assert_eq!((m.part, m.pred), (Part::P8x16, [PRED_BI, PRED_BI])); + let m = b_mb_type(11).unwrap(); + assert_eq!((m.part, m.pred), (Part::P8x16, [PRED_L1, PRED_L0])); + } +} diff --git a/crates/h264/src/params.rs b/crates/h264/src/params.rs new file mode 100644 index 0000000..bc5db10 --- /dev/null +++ b/crates/h264/src/params.rs @@ -0,0 +1,719 @@ +//! Sequence and picture parameter sets (7.3.2.1, 7.3.2.2) including VUI and scaling lists. + +use crate::error::{Result, ensure, unsupported}; +use crate::tables::*; +use slidecraft_bitstream::BitReader; + +/// Scaling matrices after applying fall-back rules, stored in raster order (weightScale). +#[derive(Clone, Debug, PartialEq, Eq)] +pub struct ScalingMatrices { + /// [Intra Y, Intra Cb, Intra Cr, Inter Y, Inter Cb, Inter Cr], raster order. + pub m4: [[u8; 16]; 6], + /// [Intra Y, Inter Y, Intra Cb, Inter Cb, Intra Cr, Inter Cr], raster order. + pub m8: [[u8; 64]; 6], +} + +impl ScalingMatrices { + pub fn flat() -> Self { + Self { m4: [[16; 16]; 6], m8: [[16; 64]; 6] } + } + pub fn is_flat(&self) -> bool { + self.m4.iter().all(|m| m.iter().all(|&v| v == 16)) && self.m8.iter().all(|m| m.iter().all(|&v| v == 16)) + } +} + +/// Lists as parsed, in zig-zag order, before inverse scanning. +#[derive(Clone, Debug)] +struct ZzLists { + l4: [[u8; 16]; 6], + l8: [[u8; 64]; 6], +} + +fn zz4_to_raster(l: &[u8; 16]) -> [u8; 16] { + let mut out = [0; 16]; + for (k, &v) in l.iter().enumerate() { + out[ZIGZAG4[k] as usize] = v; + } + out +} +fn zz8_to_raster(l: &[u8; 64]) -> [u8; 64] { + let mut out = [0; 64]; + for (k, &v) in l.iter().enumerate() { + out[ZIGZAG8[k] as usize] = v; + } + out +} + +impl ZzLists { + fn to_matrices(&self) -> ScalingMatrices { + let mut m = ScalingMatrices::flat(); + for i in 0..6 { + m.m4[i] = zz4_to_raster(&self.l4[i]); + m.m8[i] = zz8_to_raster(&self.l8[i]); + } + m + } + fn flat() -> Self { + Self { l4: [[16; 16]; 6], l8: [[16; 64]; 6] } + } +} + +/// Result of parsing one scaling_list(): Some(list) or None meaning "use default". +fn parse_scaling_list(r: &mut BitReader) -> Result> { + let mut list = [0u8; N]; + let mut last = 8i32; + let mut next = 8i32; + for (j, item) in list.iter_mut().enumerate() { + if next != 0 { + let delta = r.read_se()?; + ensure!((-128..=127).contains(&delta), "delta_scale out of range"); + next = (last + delta + 256) % 256; + if j == 0 && next == 0 { + return Ok(None); + } + } + *item = if next == 0 { last as u8 } else { next as u8 }; + last = *item as i32; + } + Ok(Some(list)) +} + +/// Parse scaling lists with a fall-back rule. `fallback` gives the lists used for rule B +/// (sequence-level) or None for rule A. +fn parse_scaling_lists(r: &mut BitReader, count: usize, fallback: Option<&ZzLists>) -> Result { + let mut out = ZzLists::flat(); + for i in 0..count.max(8) { + let present = if i < count { r.read_flag()? } else { false }; + if i < 6 { + let parsed = if present { Some(parse_scaling_list::<16>(r)?) } else { None }; + out.l4[i] = match parsed { + Some(Some(l)) => l, + Some(None) => { + if i < 3 { + DEFAULT_4X4_INTRA + } else { + DEFAULT_4X4_INTER + } + } + None => match (i, fallback) { + (0, None) => DEFAULT_4X4_INTRA, + (3, None) => DEFAULT_4X4_INTER, + (0 | 3, Some(f)) => f.l4[i], + _ => out.l4[i - 1], + }, + }; + } else { + let k = i - 6; + if k >= 6 { + break; + } + let parsed = if present { Some(parse_scaling_list::<64>(r)?) } else { None }; + out.l8[k] = match parsed { + Some(Some(l)) => l, + Some(None) => { + if k % 2 == 0 { + DEFAULT_8X8_INTRA + } else { + DEFAULT_8X8_INTER + } + } + None => match (k, fallback) { + (0, None) => DEFAULT_8X8_INTRA, + (1, None) => DEFAULT_8X8_INTER, + (0 | 1, Some(f)) => f.l8[k], + _ => out.l8[k - 2], + }, + }; + } + } + // 8x8 chroma lists that were never signalled (count == 8) follow rule A/B chains as well. + if count <= 8 { + for k in 2..6 { + out.l8[k] = out.l8[k - 2]; + } + } + Ok(out) +} + +#[derive(Clone, Debug, Default, PartialEq, Eq)] +pub struct HrdParameters { + pub cpb_cnt: u32, + pub bit_rate_scale: u8, + pub cpb_size_scale: u8, + pub bit_rate_value_minus1: Vec, + pub cpb_size_value_minus1: Vec, + pub cbr_flag: Vec, + pub initial_cpb_removal_delay_length: u8, + pub cpb_removal_delay_length: u8, + pub dpb_output_delay_length: u8, + pub time_offset_length: u8, +} + +fn parse_hrd(r: &mut BitReader) -> Result { + let cpb_cnt = r.read_ue()? + 1; + ensure!(cpb_cnt <= 32, "cpb_cnt_minus1 out of range"); + let mut h = HrdParameters { cpb_cnt, bit_rate_scale: r.read_bits(4)? as u8, cpb_size_scale: r.read_bits(4)? as u8, ..Default::default() }; + for _ in 0..cpb_cnt { + h.bit_rate_value_minus1.push(r.read_ue()?); + h.cpb_size_value_minus1.push(r.read_ue()?); + h.cbr_flag.push(r.read_flag()?); + } + h.initial_cpb_removal_delay_length = r.read_bits(5)? as u8 + 1; + h.cpb_removal_delay_length = r.read_bits(5)? as u8 + 1; + h.dpb_output_delay_length = r.read_bits(5)? as u8 + 1; + h.time_offset_length = r.read_bits(5)? as u8; + Ok(h) +} + +/// Video usability information (Annex E). +#[derive(Clone, Debug, PartialEq, Eq)] +pub struct Vui { + pub aspect_ratio_idc: u8, + pub sar: (u16, u16), + pub overscan_appropriate: Option, + pub video_format: u8, + pub full_range: bool, + pub colour_primaries: u8, + pub transfer_characteristics: u8, + pub matrix_coefficients: u8, + pub chroma_loc: Option<(u32, u32)>, + pub timing: Option<(u32, u32, bool)>, + pub nal_hrd: Option, + pub vcl_hrd: Option, + pub low_delay_hrd: bool, + pub pic_struct_present: bool, + pub bitstream_restriction: Option, +} + +impl Default for Vui { + fn default() -> Self { + Self { + aspect_ratio_idc: 0, + sar: (0, 0), + overscan_appropriate: None, + video_format: 5, + full_range: false, + colour_primaries: 2, + transfer_characteristics: 2, + matrix_coefficients: 2, + chroma_loc: None, + timing: None, + nal_hrd: None, + vcl_hrd: None, + low_delay_hrd: false, + pic_struct_present: false, + bitstream_restriction: None, + } + } +} + +#[derive(Clone, Debug, PartialEq, Eq)] +pub struct BitstreamRestriction { + pub motion_vectors_over_pic_boundaries: bool, + pub max_bytes_per_pic_denom: u32, + pub max_bits_per_mb_denom: u32, + pub log2_max_mv_length_horizontal: u32, + pub log2_max_mv_length_vertical: u32, + pub max_num_reorder_frames: u32, + pub max_dec_frame_buffering: u32, +} + +/// Table E-1 sample aspect ratios. +const SAR_TABLE: [(u16, u16); 17] = [ + (0, 0), + (1, 1), + (12, 11), + (10, 11), + (16, 11), + (40, 33), + (24, 11), + (20, 11), + (32, 11), + (80, 33), + (18, 11), + (15, 11), + (64, 33), + (160, 99), + (4, 3), + (3, 2), + (2, 1), +]; + +fn parse_vui(r: &mut BitReader) -> Result { + let mut v = Vui::default(); + if r.read_flag()? { + v.aspect_ratio_idc = r.read_bits(8)? as u8; + if v.aspect_ratio_idc == 255 { + v.sar = (r.read_bits(16)? as u16, r.read_bits(16)? as u16); + } else if (v.aspect_ratio_idc as usize) < SAR_TABLE.len() { + v.sar = SAR_TABLE[v.aspect_ratio_idc as usize]; + } + } + if r.read_flag()? { + v.overscan_appropriate = Some(r.read_flag()?); + } + if r.read_flag()? { + v.video_format = r.read_bits(3)? as u8; + v.full_range = r.read_flag()?; + if r.read_flag()? { + v.colour_primaries = r.read_bits(8)? as u8; + v.transfer_characteristics = r.read_bits(8)? as u8; + v.matrix_coefficients = r.read_bits(8)? as u8; + } + } + if r.read_flag()? { + v.chroma_loc = Some((r.read_ue()?, r.read_ue()?)); + } + if r.read_flag()? { + let n = r.read_bits(32)?; + let t = r.read_bits(32)?; + let fixed = r.read_flag()?; + v.timing = Some((n, t, fixed)); + } + if r.read_flag()? { + v.nal_hrd = Some(parse_hrd(r)?); + } + if r.read_flag()? { + v.vcl_hrd = Some(parse_hrd(r)?); + } + if v.nal_hrd.is_some() || v.vcl_hrd.is_some() { + v.low_delay_hrd = r.read_flag()?; + } + v.pic_struct_present = r.read_flag()?; + // Some streams truncate the VUI after this point; treat EOF as "absent". + if r.bits_left() > 0 && r.read_flag()? { + let br = (|| -> Result { + Ok(BitstreamRestriction { + motion_vectors_over_pic_boundaries: r.read_flag()?, + max_bytes_per_pic_denom: r.read_ue()?, + max_bits_per_mb_denom: r.read_ue()?, + log2_max_mv_length_horizontal: r.read_ue()?, + log2_max_mv_length_vertical: r.read_ue()?, + max_num_reorder_frames: r.read_ue()?, + max_dec_frame_buffering: r.read_ue()?, + }) + })(); + v.bitstream_restriction = br.ok(); + } + Ok(v) +} + +/// Sequence parameter set. +#[derive(Clone, Debug)] +pub struct Sps { + pub profile_idc: u8, + pub constraint_flags: u8, + pub level_idc: u8, + pub id: u32, + pub chroma_format_idc: u32, + pub separate_colour_plane: bool, + pub bit_depth_luma: u32, + pub bit_depth_chroma: u32, + pub qpprime_y_zero_transform_bypass: bool, + pub seq_scaling_matrix_present: bool, + scaling_lists: ZzLists, + pub scaling: ScalingMatrices, + pub log2_max_frame_num: u32, + pub pic_order_cnt_type: u32, + pub log2_max_poc_lsb: u32, + pub delta_pic_order_always_zero: bool, + pub offset_for_non_ref_pic: i32, + pub offset_for_top_to_bottom_field: i32, + pub offset_for_ref_frame: Vec, + pub max_num_ref_frames: u32, + pub gaps_in_frame_num_allowed: bool, + pub pic_width_in_mbs: u32, + pub pic_height_in_map_units: u32, + pub frame_mbs_only: bool, + pub mb_adaptive_frame_field: bool, + pub direct_8x8_inference: bool, + /// (left, right, top, bottom) in crop units as coded. + pub frame_crop: Option<(u32, u32, u32, u32)>, + pub vui: Option, +} + +impl Sps { + pub fn parse(rbsp: &[u8]) -> Result { + let mut r = BitReader::new(rbsp); + let profile_idc = r.read_bits(8)? as u8; + let constraint_flags = r.read_bits(8)? as u8; + let level_idc = r.read_bits(8)? as u8; + let id = r.read_ue()?; + ensure!(id < 32, "seq_parameter_set_id {id} out of range"); + let mut chroma_format_idc = 1; + let mut separate_colour_plane = false; + let mut bit_depth_luma = 8; + let mut bit_depth_chroma = 8; + let mut bypass = false; + let mut seq_scaling_matrix_present = false; + let mut scaling_lists = ZzLists::flat(); + if matches!(profile_idc, 100 | 110 | 122 | 244 | 44 | 83 | 86 | 118 | 128 | 138 | 139 | 134 | 135) { + chroma_format_idc = r.read_ue()?; + ensure!(chroma_format_idc <= 3, "chroma_format_idc out of range"); + if chroma_format_idc == 3 { + separate_colour_plane = r.read_flag()?; + } + bit_depth_luma = r.read_ue()? + 8; + bit_depth_chroma = r.read_ue()? + 8; + ensure!(bit_depth_luma <= 14 && bit_depth_chroma <= 14, "bit depth out of range"); + bypass = r.read_flag()?; + seq_scaling_matrix_present = r.read_flag()?; + if seq_scaling_matrix_present { + let count = if chroma_format_idc != 3 { 8 } else { 12 }; + scaling_lists = parse_scaling_lists(&mut r, count, None)?; + } + } + let log2_max_frame_num = r.read_ue()? + 4; + ensure!(log2_max_frame_num <= 16, "log2_max_frame_num out of range"); + let pic_order_cnt_type = r.read_ue()?; + ensure!(pic_order_cnt_type <= 2, "pic_order_cnt_type out of range"); + let mut log2_max_poc_lsb = 0; + let mut delta_pic_order_always_zero = false; + let mut offset_for_non_ref_pic = 0; + let mut offset_for_top_to_bottom_field = 0; + let mut offset_for_ref_frame = Vec::new(); + if pic_order_cnt_type == 0 { + log2_max_poc_lsb = r.read_ue()? + 4; + ensure!(log2_max_poc_lsb <= 16, "log2_max_pic_order_cnt_lsb out of range"); + } else if pic_order_cnt_type == 1 { + delta_pic_order_always_zero = r.read_flag()?; + offset_for_non_ref_pic = r.read_se()?; + offset_for_top_to_bottom_field = r.read_se()?; + let n = r.read_ue()?; + ensure!(n <= 255, "num_ref_frames_in_pic_order_cnt_cycle out of range"); + for _ in 0..n { + offset_for_ref_frame.push(r.read_se()?); + } + } + let max_num_ref_frames = r.read_ue()?; + ensure!(max_num_ref_frames <= 16, "max_num_ref_frames out of range"); + let gaps_in_frame_num_allowed = r.read_flag()?; + let pic_width_in_mbs = r.read_ue()? + 1; + let pic_height_in_map_units = r.read_ue()? + 1; + ensure!(pic_width_in_mbs <= 1024 && pic_height_in_map_units <= 1024, "picture too large"); + let frame_mbs_only = r.read_flag()?; + let mb_adaptive_frame_field = if !frame_mbs_only { r.read_flag()? } else { false }; + let direct_8x8_inference = r.read_flag()?; + let frame_crop = if r.read_flag()? { Some((r.read_ue()?, r.read_ue()?, r.read_ue()?, r.read_ue()?)) } else { None }; + let vui = if r.read_flag()? { Some(parse_vui(&mut r)?) } else { None }; + let scaling = if seq_scaling_matrix_present { scaling_lists.to_matrices() } else { ScalingMatrices::flat() }; + let sps = Sps { + profile_idc, + constraint_flags, + level_idc, + id, + chroma_format_idc, + separate_colour_plane, + bit_depth_luma, + bit_depth_chroma, + qpprime_y_zero_transform_bypass: bypass, + seq_scaling_matrix_present, + scaling_lists, + scaling, + log2_max_frame_num, + pic_order_cnt_type, + log2_max_poc_lsb, + delta_pic_order_always_zero, + offset_for_non_ref_pic, + offset_for_top_to_bottom_field, + offset_for_ref_frame, + max_num_ref_frames, + gaps_in_frame_num_allowed, + pic_width_in_mbs, + pic_height_in_map_units, + frame_mbs_only, + mb_adaptive_frame_field, + direct_8x8_inference, + frame_crop, + vui, + }; + if let Some((l, rr, t, b)) = sps.frame_crop { + let (cx, cy) = sps.crop_unit(); + ensure!((l + rr) * cx < sps.width() && (t + b) * cy < sps.height(), "frame cropping exceeds picture size"); + } + Ok(sps) + } + + /// ChromaArrayType. + pub fn chroma_array_type(&self) -> u32 { + if self.separate_colour_plane { 0 } else { self.chroma_format_idc } + } + pub fn frame_height_in_mbs(&self) -> u32 { + (2 - self.frame_mbs_only as u32) * self.pic_height_in_map_units + } + /// Decoded (uncropped) luma width. + pub fn width(&self) -> u32 { + self.pic_width_in_mbs * 16 + } + pub fn height(&self) -> u32 { + self.frame_height_in_mbs() * 16 + } + pub fn max_frame_num(&self) -> u32 { + 1 << self.log2_max_frame_num + } + /// (CropUnitX, CropUnitY). + pub fn crop_unit(&self) -> (u32, u32) { + let (sub_w, sub_h) = match self.chroma_array_type() { + 1 => (2, 2), + 2 => (2, 1), + _ => (1, 1), + }; + if self.chroma_array_type() == 0 { (1, 2 - self.frame_mbs_only as u32) } else { (sub_w, sub_h * (2 - self.frame_mbs_only as u32)) } + } + /// Cropping rectangle in luma samples: (x, y, width, height). + pub fn crop_rect(&self) -> (u32, u32, u32, u32) { + match self.frame_crop { + None => (0, 0, self.width(), self.height()), + Some((l, r, t, b)) => { + let (cx, cy) = self.crop_unit(); + (l * cx, t * cy, self.width() - (l + r) * cx, self.height() - (t + b) * cy) + } + } + } + + /// MaxDpbFrames from Table A-1 (and the VUI max_dec_frame_buffering when present). + pub fn max_dpb_frames(&self) -> usize { + let max_dpb_mbs: u32 = match self.level_idc { + 9 => 396, + 10 => 396, + 11 => { + if self.constraint_flags & 0x10 != 0 && !matches!(self.profile_idc, 100 | 110 | 122 | 244 | 44) { + 396 + } else { + 900 + } + } + 12 | 13 | 20 => 2376, + 21 => 4752, + 22 | 30 => 8100, + 31 => 18000, + 32 => 20480, + 40 | 41 => 32768, + 42 => 34816, + 50 => 110400, + 51 | 52 => 184320, + 60..=62 => 696320, + _ => 184320, + }; + let frame_mbs = self.pic_width_in_mbs * self.frame_height_in_mbs(); + let mut n = (max_dpb_mbs / frame_mbs.max(1)).clamp(1, 16) as usize; + if let Some(br) = self.vui.as_ref().and_then(|v| v.bitstream_restriction.as_ref()) { + n = (br.max_dec_frame_buffering as usize).min(16); + } + n.max(self.max_num_ref_frames as usize).max(1) + } + + /// Number of frames that may precede a frame in decoding order and follow it in output order. + pub fn max_num_reorder_frames(&self) -> usize { + if let Some(br) = self.vui.as_ref().and_then(|v| v.bitstream_restriction.as_ref()) { + return br.max_num_reorder_frames as usize; + } + // Profiles that cannot reorder (no B slices) with constraint_set3 (intra) etc. + if matches!(self.profile_idc, 44 | 86 | 100 | 110 | 122 | 244) && self.constraint_flags & 0x10 != 0 { + return 0; + } + self.max_dpb_frames() + } + + pub fn check_supported(&self) -> Result<()> { + if !self.frame_mbs_only { + return unsupported("interlaced (field / MBAFF) coding"); + } + if self.chroma_array_type() != 1 { + return unsupported(format!("chroma_format_idc {} (only 4:2:0 is implemented)", self.chroma_format_idc)); + } + if self.bit_depth_luma != 8 || self.bit_depth_chroma != 8 { + return unsupported(format!("bit depth luma {} chroma {} (only 8-bit is implemented)", self.bit_depth_luma, self.bit_depth_chroma)); + } + if self.qpprime_y_zero_transform_bypass { + return unsupported("qpprime_y_zero_transform_bypass (lossless)"); + } + Ok(()) + } +} + +/// Picture parameter set. +#[derive(Clone, Debug)] +pub struct Pps { + pub id: u32, + pub sps_id: u32, + pub entropy_coding_mode: bool, + pub bottom_field_pic_order_in_frame_present: bool, + pub num_slice_groups: u32, + pub slice_group_map_type: u32, + pub num_ref_idx_l0_default_active: u32, + pub num_ref_idx_l1_default_active: u32, + pub weighted_pred: bool, + pub weighted_bipred_idc: u32, + pub pic_init_qp: i32, + pub pic_init_qs: i32, + pub chroma_qp_index_offset: i32, + pub deblocking_filter_control_present: bool, + pub constrained_intra_pred: bool, + pub redundant_pic_cnt_present: bool, + pub transform_8x8_mode: bool, + pub pic_scaling_matrix_present: bool, + pub second_chroma_qp_index_offset: i32, + /// Effective scaling matrices (after PPS/SPS fall-back), raster order. + pub scaling: ScalingMatrices, +} + +impl Pps { + /// Parse a PPS. Needs the SPS table because the tail depends on chroma_format_idc. + pub fn parse(rbsp: &[u8], spss: &[Option]) -> Result { + let mut r = BitReader::new(rbsp); + let id = r.read_ue()?; + ensure!(id < 256, "pic_parameter_set_id out of range"); + let sps_id = r.read_ue()?; + ensure!(sps_id < 32, "seq_parameter_set_id out of range"); + let sps = spss + .get(sps_id as usize) + .and_then(|s| s.as_ref()) + .ok_or_else(|| crate::Error::MissingParameterSet(format!("SPS {sps_id} for PPS {id}")))?; + let entropy_coding_mode = r.read_flag()?; + let bottom_field_pic_order_in_frame_present = r.read_flag()?; + let num_slice_groups = r.read_ue()? + 1; + ensure!(num_slice_groups <= 8, "num_slice_groups out of range"); + let mut slice_group_map_type = 0; + if num_slice_groups > 1 { + // Parsed for completeness; FMO decoding is unsupported. + slice_group_map_type = r.read_ue()?; + match slice_group_map_type { + 0 => { + for _ in 0..num_slice_groups { + r.read_ue()?; + } + } + 2 => { + for _ in 0..num_slice_groups - 1 { + r.read_ue()?; + r.read_ue()?; + } + } + 3..=5 => { + r.read_flag()?; + r.read_ue()?; + } + 6 => { + let n = r.read_ue()? + 1; + let bits = 32 - (num_slice_groups - 1).leading_zeros(); + for _ in 0..n { + r.read_bits(bits)?; + } + } + _ => {} + } + } + let num_ref_idx_l0_default_active = r.read_ue()? + 1; + let num_ref_idx_l1_default_active = r.read_ue()? + 1; + ensure!(num_ref_idx_l0_default_active <= 32 && num_ref_idx_l1_default_active <= 32, "num_ref_idx_default_active out of range"); + let weighted_pred = r.read_flag()?; + let weighted_bipred_idc = r.read_bits(2)?; + let pic_init_qp = 26 + r.read_se()?; + let pic_init_qs = 26 + r.read_se()?; + let chroma_qp_index_offset = r.read_se()?; + ensure!((-12..=12).contains(&chroma_qp_index_offset), "chroma_qp_index_offset out of range"); + let deblocking_filter_control_present = r.read_flag()?; + let constrained_intra_pred = r.read_flag()?; + let redundant_pic_cnt_present = r.read_flag()?; + let mut transform_8x8_mode = false; + let mut pic_scaling_matrix_present = false; + let mut second_chroma_qp_index_offset = chroma_qp_index_offset; + let mut scaling = sps.scaling.clone(); + if r.more_rbsp_data() { + transform_8x8_mode = r.read_flag()?; + pic_scaling_matrix_present = r.read_flag()?; + if pic_scaling_matrix_present { + let count = 6 + if sps.chroma_format_idc != 3 { 2 } else { 6 } * transform_8x8_mode as usize; + // Fall-back rule B uses the sequence-level lists (or Flat when the SPS has none, but then + // rule A applies per 7.4.2.2). + let lists = if sps.seq_scaling_matrix_present { + parse_scaling_lists(&mut r, count, Some(&sps.scaling_lists))? + } else { + parse_scaling_lists(&mut r, count, None)? + }; + scaling = lists.to_matrices(); + } + second_chroma_qp_index_offset = r.read_se()?; + ensure!((-12..=12).contains(&second_chroma_qp_index_offset), "second_chroma_qp_index_offset out of range"); + } + Ok(Pps { + id, + sps_id, + entropy_coding_mode, + bottom_field_pic_order_in_frame_present, + num_slice_groups, + slice_group_map_type, + num_ref_idx_l0_default_active, + num_ref_idx_l1_default_active, + weighted_pred, + weighted_bipred_idc, + pic_init_qp, + pic_init_qs, + chroma_qp_index_offset, + deblocking_filter_control_present, + constrained_intra_pred, + redundant_pic_cnt_present, + transform_8x8_mode, + pic_scaling_matrix_present, + second_chroma_qp_index_offset, + scaling, + }) + } +} + +#[cfg(test)] +mod tests { + use super::*; + use slidecraft_bitstream::BitWriter; + + #[test] + fn scaling_list_default_signal() { + // delta_scale = -8 on the first entry => nextScale 0 => use default. + let mut w = BitWriter::new(); + w.write_se(-8); + w.rbsp_trailing(); + let b = w.finish(); + let mut r = BitReader::new(&b); + assert_eq!(parse_scaling_list::<16>(&mut r).unwrap(), None); + } + + #[test] + fn scaling_list_repeat_last() { + // 8 -> +2 = 10, then +0 = 10, then -10 => 0 => repeat 10 for the rest. + let mut w = BitWriter::new(); + w.write_se(2); + w.write_se(0); + w.write_se(-10); + w.rbsp_trailing(); + let b = w.finish(); + let mut r = BitReader::new(&b); + let l = parse_scaling_list::<16>(&mut r).unwrap().unwrap(); + assert_eq!(l, [10; 16]); + } + + #[test] + fn parse_minimal_baseline_sps() { + // profile 66, level 30, sps_id 0, log2_max_frame_num_minus4 0, poc type 2, 1 ref, 11x9 MBs. + let mut w = BitWriter::new(); + w.write_bits(66, 8); + w.write_bits(0xC0, 8); + w.write_bits(30, 8); + w.write_ue(0); + w.write_ue(0); + w.write_ue(2); + w.write_ue(1); + w.write_bit(false); + w.write_ue(10); + w.write_ue(8); + w.write_bit(true); // frame_mbs_only + w.write_bit(true); // direct_8x8 + w.write_bit(false); // cropping + w.write_bit(false); // vui + w.rbsp_trailing(); + let sps = Sps::parse(&w.finish()).unwrap(); + assert_eq!((sps.width(), sps.height()), (176, 144)); + assert_eq!(sps.pic_order_cnt_type, 2); + assert_eq!(sps.crop_rect(), (0, 0, 176, 144)); + } +} diff --git a/crates/h264/src/picture.rs b/crates/h264/src/picture.rs new file mode 100644 index 0000000..8a9f0a9 --- /dev/null +++ b/crates/h264/src/picture.rs @@ -0,0 +1,343 @@ +//! Picture buffers and per-macroblock state. + +use std::sync::{Arc, OnceLock}; + +/// Planar 4:2:0 8-bit sample buffers (MB-aligned dimensions, stride == width). +#[derive(Clone)] +pub struct Planes { + pub y: Vec, + pub cb: Vec, + pub cr: Vec, + pub width: usize, + pub height: usize, + pub cwidth: usize, +} + +impl Planes { + pub fn new(width: usize, height: usize) -> Self { + let (cw, ch) = (width / 2, height / 2); + Planes { y: vec![0; width * height], cb: vec![128; cw * ch], cr: vec![128; cw * ch], width, height, cwidth: cw } + } + pub fn gray(width: usize, height: usize) -> Self { + let mut p = Self::new(width, height); + p.y.fill(128); + p + } +} + +/// Macroblock coding category. +#[derive(Clone, Copy, Debug, PartialEq, Eq, Default)] +pub enum MbKind { + #[default] + None, + I4x4, + I8x8, + I16x16, + IPcm, + PSkip, + BSkip, + BDirect16x16, + /// Any other P or B inter macroblock. + Inter, +} + +impl MbKind { + #[inline] + pub fn is_intra(self) -> bool { + matches!(self, MbKind::I4x4 | MbKind::I8x8 | MbKind::I16x16 | MbKind::IPcm) + } +} + +/// Per-macroblock state kept for the whole picture (neighbour derivations, deblocking, co-located data). +#[derive(Clone, Copy)] +pub struct MbState { + /// Slice number within the picture; `u32::MAX` = not (yet) decoded. + pub slice_num: u32, + pub kind: MbKind, + pub transform_8x8: bool, + /// coded_block_pattern: bits 0..3 luma, bits 4..5 chroma. + pub cbp: u8, + pub qp: u8, + /// QPc for Cb / Cr (used by deblocking). + pub qpc: [u8; 2], + pub intra_chroma_mode: u8, + /// Intra4x4PredMode / Intra8x8PredMode per 4x4 block (raster order). + pub intra_modes: [u8; 16], + /// CAVLC: TotalCoeff per luma 4x4 block (raster). CABAC: coded_block_flag. + pub nnz: [u8; 16], + /// Same for chroma AC blocks [Cb, Cr][raster 2x2]. + pub nnz_c: [[u8; 4]; 2], + /// CABAC coded_block_flag of DC blocks: bit0 luma (I16x16), bit1 Cb, bit2 Cr. + pub cbf_dc: u8, + /// Luma 4x4 blocks (raster bit) with non-zero coefficients, for deblocking bS = 2. + pub nz_mask: u16, + pub ref_idx: [[i8; 4]; 2], + /// Motion vectors per 4x4 block (raster order). + pub mv: [[[i16; 2]; 16]; 2], + /// |mvd| per 4x4 block (raster), saturated, for CABAC context selection. + pub mvd: [[[u8; 2]; 16]; 2], + /// Bit per 8x8 block predicted in direct mode. + pub direct8x8: u8, +} + +impl MbState { + #[inline] + pub fn kind_is_skip(&self) -> bool { + matches!(self.kind, MbKind::PSkip | MbKind::BSkip) + } +} + +impl Default for MbState { + fn default() -> Self { + MbState { + slice_num: u32::MAX, + kind: MbKind::None, + transform_8x8: false, + cbp: 0, + qp: 0, + qpc: [0; 2], + intra_chroma_mode: 0, + intra_modes: [2; 16], + nnz: [0; 16], + nnz_c: [[0; 4]; 2], + cbf_dc: 0, + nz_mask: 0, + ref_idx: [[-1; 4]; 2], + mv: [[[0; 2]; 16]; 2], + mvd: [[[0; 2]; 16]; 2], + direct8x8: 0, + } + } +} + +/// One published macroblock row of a decoded frame: final (deblocked) samples plus the motion data +/// needed as co-located information for direct prediction. +pub struct FrameRow { + /// 16 luma lines. + pub y: Box<[u8]>, + /// 8 lines per chroma component. + pub cb: Box<[u8]>, + pub cr: Box<[u8]>, + /// Per 4x4 block: index mb_x * 16 + raster. + pub mv: [Box<[[i16; 2]]>; 2], + /// Per 8x8 block: index mb_x * 4 + b8. + pub ref_idx: [Box<[i8]>; 2], + /// Unique id of the referenced picture per 8x8 block (u32::MAX = none). + pub ref_id: [Box<[u32]>; 2], + pub intra: Box<[bool]>, +} + +/// A decoded (or in-progress) frame as stored in the DPB and referenced by later pictures. +/// +/// Rows are published in order by the thread decoding the picture; readers block (per row) until the +/// data they need is available, which lets several pictures decode concurrently. +pub struct Frame { + pub id: u32, + pub poc: i32, + pub width: usize, + pub height: usize, + pub cwidth: usize, + pub cheight: usize, + pub mb_w: usize, + rows: Box<[OnceLock]>, +} + +impl Frame { + pub fn new(id: u32, poc: i32, mb_w: usize, mb_h: usize) -> Self { + Frame { + id, + poc, + width: mb_w * 16, + height: mb_h * 16, + cwidth: mb_w * 8, + cheight: mb_h * 8, + mb_w, + rows: (0..mb_h).map(|_| OnceLock::new()).collect(), + } + } + + pub fn mb_h(&self) -> usize { + self.rows.len() + } + + /// Publish MB row `r` (ignored if already published). + pub fn publish(&self, r: usize, row: FrameRow) { + let _ = self.rows[r].set(row); + } + + pub fn is_published(&self, r: usize) -> bool { + self.rows[r].get().is_some() + } + + /// MB row `r`, waiting until it has been published. + #[inline] + pub fn row(&self, r: usize) -> &FrameRow { + #[cfg(feature = "threads")] + { + self.rows[r].wait() + } + #[cfg(not(feature = "threads"))] + { + self.rows[r].get().expect("reference row decoded before use") + } + } + + /// Wait until the whole frame is available. + pub fn wait_complete(&self) { + for r in 0..self.rows.len() { + self.row(r); + } + } + + /// Build a published row from a contiguous plane set (rows of MB row `r`) and motion data. + pub fn make_row(planes: &Planes, r: usize, mbs: &[MbState], ref_ids: &dyn Fn(&MbState, usize, i8) -> u32) -> FrameRow { + let w = planes.width; + let cw = planes.cwidth; + let mb_w = w / 16; + let y = planes.y[r * 16 * w..(r + 1) * 16 * w].into(); + let cb = planes.cb[r * 8 * cw..(r + 1) * 8 * cw].into(); + let cr = planes.cr[r * 8 * cw..(r + 1) * 8 * cw].into(); + let row_mbs = &mbs[r * mb_w..(r + 1) * mb_w]; + let mut mv = [vec![[0i16; 2]; mb_w * 16], vec![[0i16; 2]; mb_w * 16]]; + let mut ref_idx = [vec![-1i8; mb_w * 4], vec![-1i8; mb_w * 4]]; + let mut ref_id = [vec![u32::MAX; mb_w * 4], vec![u32::MAX; mb_w * 4]]; + let mut intra = vec![true; mb_w]; + for (x, st) in row_mbs.iter().enumerate() { + if st.slice_num == u32::MAX { + continue; + } + intra[x] = st.kind.is_intra(); + for l in 0..2 { + mv[l][x * 16..x * 16 + 16].copy_from_slice(&st.mv[l]); + for b in 0..4 { + let ri = st.ref_idx[l][b]; + ref_idx[l][x * 4 + b] = ri; + if ri >= 0 { + ref_id[l][x * 4 + b] = ref_ids(st, l, ri); + } + } + } + } + let [mv0, mv1] = mv; + let [ri0, ri1] = ref_idx; + let [id0, id1] = ref_id; + FrameRow { y, cb, cr, mv: [mv0.into(), mv1.into()], ref_idx: [ri0.into(), ri1.into()], ref_id: [id0.into(), id1.into()], intra: intra.into() } + } + + /// A frame with every row published from `planes` and no motion (intra), e.g. "non-existing" + /// frames inferred for frame_num gaps. + pub fn from_planes(id: u32, poc: i32, planes: &Planes) -> Self { + let (mb_w, mb_h) = (planes.width / 16, planes.height / 16); + let f = Frame::new(id, poc, mb_w, mb_h); + let blank = vec![MbState::default(); mb_w * mb_h]; + for r in 0..mb_h { + f.publish(r, Frame::make_row(planes, r, &blank, &|_, _, _| u32::MAX)); + } + f + } + + /// Copy the w x h luma window at (x0, y0) (clamped to the picture) into `out` (stride `os`). + #[inline] + pub fn luma_window(&self, x0: i32, y0: i32, w: usize, h: usize, out: &mut [u8], os: usize) { + let (width, height) = (self.width, self.height); + for r in 0..h { + let yy = (y0 + r as i32).clamp(0, height as i32 - 1) as usize; + let row = self.row(yy >> 4); + let line = &row.y[(yy & 15) * width..(yy & 15) * width + width]; + copy_line::<24>(line, x0, &mut out[r * os..], w); + } + } + + /// Copy the w x h window of chroma component `c` at (x0, y0) (clamped) into `out`. + #[inline] + pub fn chroma_window(&self, c: usize, x0: i32, y0: i32, w: usize, h: usize, out: &mut [u8], os: usize) { + let (cw, ch) = (self.cwidth, self.cheight); + for r in 0..h { + let yy = (y0 + r as i32).clamp(0, ch as i32 - 1) as usize; + let row = self.row(yy >> 3); + let plane = if c == 0 { &row.cb } else { &row.cr }; + let line = &plane[(yy & 7) * cw..(yy & 7) * cw + cw]; + copy_line::<16>(line, x0, &mut out[r * os..], w); + } + } + + /// Crop and copy the frame into planar buffers (waits for completion). + pub fn copy_cropped(&self, crop: (usize, usize, usize, usize)) -> (Vec, Vec, Vec) { + let (cx, cy, cw, ch) = crop; + let alloc = plane_allocator(); + let mut y = alloc(cw * ch); + for r in cy..cy + ch { + let row = self.row(r >> 4); + y.extend_from_slice(&row.y[(r & 15) * self.width + cx..(r & 15) * self.width + cx + cw]); + } + let (ccx, ccy, ccw, cch) = (cx / 2, cy / 2, cw.div_ceil(2), ch.div_ceil(2)); + let mut u = alloc(ccw * cch); + let mut v = alloc(ccw * cch); + for r in ccy..ccy + cch { + let row = self.row(r >> 3); + let o = (r & 7) * self.cwidth + ccx; + u.extend_from_slice(&row.cb[o..o + ccw]); + v.extend_from_slice(&row.cr[o..o + ccw]); + } + (y, u, v) + } +} + +static PLANE_ALLOCATOR: std::sync::OnceLock Vec> = std::sync::OnceLock::new(); + +/// Where output planes come from: an empty buffer with room for the given number of samples. +/// The host sets it once to recycle the planes of pictures it is done with; the first call wins. +pub fn set_plane_allocator(alloc: fn(usize) -> Vec) { + let _ = PLANE_ALLOCATOR.set(alloc); +} + +fn plane_allocator() -> fn(usize) -> Vec { + PLANE_ALLOCATOR.get().copied().unwrap_or(Vec::with_capacity) +} + +/// Copy `w` samples of `line` starting at x0 into `out`; when possible a fixed-size block of `N` +/// bytes is copied instead (cheaper than a variable-length copy; `out` must then have room for `N`). +#[inline(always)] +fn copy_line(line: &[u8], x0: i32, out: &mut [u8], w: usize) { + if w <= N + && x0 >= 0 + && let Some(src) = line.get(x0 as usize..).and_then(|l| l.first_chunk::()) + && let Some(dst) = out.first_chunk_mut::() + { + *dst = *src; + } else { + copy_clamped(line, x0, &mut out[..w]); + } +} + +/// Copy `out.len()` samples of `line` starting at x0, replicating edge samples outside the line. +#[inline(always)] +fn copy_clamped(line: &[u8], x0: i32, out: &mut [u8]) { + let w = out.len(); + if x0 >= 0 && x0 as usize + w <= line.len() { + out.copy_from_slice(&line[x0 as usize..x0 as usize + w]); + } else { + let max = line.len() as i32 - 1; + for (c, o) in out.iter_mut().enumerate() { + *o = line[(x0 + c as i32).clamp(0, max) as usize]; + } + } +} + +pub type FrameRef = Arc; + +/// An entry of RefPicList0/1 for the current slice. +#[derive(Clone)] +pub struct RefPic { + pub frame: FrameRef, + pub long_term: bool, +} + +impl RefPic { + pub fn poc(&self) -> i32 { + self.frame.poc + } + pub fn id(&self) -> u32 { + self.frame.id + } +} diff --git a/crates/h264/src/slice.rs b/crates/h264/src/slice.rs new file mode 100644 index 0000000..357d89f --- /dev/null +++ b/crates/h264/src/slice.rs @@ -0,0 +1,515 @@ +//! NAL unit header and slice header parsing (7.3.1, 7.3.3). + +use crate::error::{Result, ensure}; +use crate::params::{Pps, Sps}; +use slidecraft_bitstream::BitReader; + +/// NAL unit types used by the decoder. +pub mod nal_type { + pub const SLICE: u8 = 1; + pub const SLICE_DPA: u8 = 2; + pub const SLICE_DPB: u8 = 3; + pub const SLICE_DPC: u8 = 4; + pub const IDR: u8 = 5; + pub const SEI: u8 = 6; + pub const SPS: u8 = 7; + pub const PPS: u8 = 8; + pub const AUD: u8 = 9; + pub const END_SEQ: u8 = 10; + pub const END_STREAM: u8 = 11; + pub const FILLER: u8 = 12; + pub const SPS_EXT: u8 = 13; + pub const PREFIX: u8 = 14; + pub const SUBSET_SPS: u8 = 15; + pub const AUX_SLICE: u8 = 19; + pub const SLICE_EXT: u8 = 20; +} + +#[derive(Clone, Copy, Debug, PartialEq, Eq)] +pub struct NalHeader { + pub nal_ref_idc: u8, + pub nal_unit_type: u8, +} + +impl NalHeader { + pub fn parse(b: u8) -> Result { + ensure!(b & 0x80 == 0, "forbidden_zero_bit set"); + Ok(Self { nal_ref_idc: (b >> 5) & 3, nal_unit_type: b & 0x1f }) + } +} + +#[derive(Clone, Copy, Debug, PartialEq, Eq)] +pub enum SliceType { + P, + B, + I, + Sp, + Si, +} + +impl SliceType { + fn from_raw(v: u32) -> Result { + Ok(match v % 5 { + 0 => SliceType::P, + 1 => SliceType::B, + 2 => SliceType::I, + 3 => SliceType::Sp, + _ => SliceType::Si, + }) + } + pub fn is_intra(self) -> bool { + matches!(self, SliceType::I | SliceType::Si) + } + pub fn is_b(self) -> bool { + self == SliceType::B + } + pub fn is_p(self) -> bool { + matches!(self, SliceType::P | SliceType::Sp) + } +} + +/// One entry of ref_pic_list_modification(). +#[derive(Clone, Copy, Debug, PartialEq, Eq)] +pub struct RefPicListMod { + pub idc: u32, + /// abs_diff_pic_num_minus1 or long_term_pic_num. + pub value: u32, +} + +/// memory_management_control_operation entry. +#[derive(Clone, Copy, Debug, PartialEq, Eq)] +pub struct Mmco { + pub op: u32, + pub difference_of_pic_nums_minus1: u32, + pub long_term_pic_num: u32, + pub long_term_frame_idx: u32, + pub max_long_term_frame_idx_plus1: u32, +} + +#[derive(Clone, Copy, Debug, Default, PartialEq, Eq)] +pub struct WeightEntry { + pub luma_weight: i32, + pub luma_offset: i32, + pub luma_flag: bool, + pub chroma_weight: [i32; 2], + pub chroma_offset: [i32; 2], + pub chroma_flag: bool, +} + +#[derive(Clone, Debug, PartialEq, Eq)] +pub struct PredWeightTable { + pub luma_log2_denom: u32, + pub chroma_log2_denom: u32, + pub l0: Vec, + pub l1: Vec, +} + +#[derive(Clone, Debug)] +pub struct SliceHeader { + pub nal_ref_idc: u8, + pub nal_unit_type: u8, + pub idr: bool, + pub first_mb_in_slice: u32, + pub slice_type_raw: u32, + pub slice_type: SliceType, + pub pps_id: u32, + pub colour_plane_id: u32, + pub frame_num: u32, + pub field_pic: bool, + pub bottom_field: bool, + pub idr_pic_id: u32, + pub pic_order_cnt_lsb: u32, + pub delta_pic_order_cnt_bottom: i32, + pub delta_pic_order_cnt: [i32; 2], + pub redundant_pic_cnt: u32, + pub direct_spatial_mv_pred: bool, + pub num_ref_idx_active: [u32; 2], + pub ref_pic_list_mod: [Vec; 2], + pub pred_weight_table: Option, + pub no_output_of_prior_pics: bool, + pub long_term_reference: bool, + pub adaptive_ref_pic_marking: bool, + pub mmcos: Vec, + pub cabac_init_idc: u32, + pub slice_qp_delta: i32, + pub sp_for_switch: bool, + pub slice_qs_delta: i32, + pub disable_deblocking_filter_idc: u32, + pub slice_alpha_c0_offset_div2: i32, + pub slice_beta_offset_div2: i32, + pub slice_group_change_cycle: u32, + /// Bit offset of slice_data() within the RBSP. + pub header_bits: usize, +} + +impl SliceHeader { + pub fn qp(&self, pps: &Pps) -> i32 { + pps.pic_init_qp + self.slice_qp_delta + } + pub fn has_mmco5(&self) -> bool { + self.mmcos.iter().any(|m| m.op == 5) + } + + /// Parse the slice header. `pps_lookup` resolves pic_parameter_set_id to (PPS, SPS). + pub fn parse<'a>( + rbsp: &[u8], + nal: NalHeader, + pps_lookup: impl FnOnce(u32) -> Result<(&'a Pps, &'a Sps)>, + ) -> Result<(SliceHeader, &'a Pps, &'a Sps)> { + let mut r = BitReader::new(rbsp); + let idr = nal.nal_unit_type == nal_type::IDR; + let first_mb_in_slice = r.read_ue()?; + let slice_type_raw = r.read_ue()?; + ensure!(slice_type_raw <= 9, "slice_type out of range"); + let slice_type = SliceType::from_raw(slice_type_raw)?; + let pps_id = r.read_ue()?; + ensure!(pps_id < 256, "pic_parameter_set_id out of range"); + let (pps, sps) = pps_lookup(pps_id)?; + let mut colour_plane_id = 0; + if sps.separate_colour_plane { + colour_plane_id = r.read_bits(2)?; + } + let frame_num = r.read_bits(sps.log2_max_frame_num)?; + let mut field_pic = false; + let mut bottom_field = false; + if !sps.frame_mbs_only { + field_pic = r.read_flag()?; + if field_pic { + bottom_field = r.read_flag()?; + } + } + let idr_pic_id = if idr { r.read_ue()? } else { 0 }; + let mut pic_order_cnt_lsb = 0; + let mut delta_pic_order_cnt_bottom = 0; + let mut delta_pic_order_cnt = [0; 2]; + if sps.pic_order_cnt_type == 0 { + pic_order_cnt_lsb = r.read_bits(sps.log2_max_poc_lsb)?; + if pps.bottom_field_pic_order_in_frame_present && !field_pic { + delta_pic_order_cnt_bottom = r.read_se()?; + } + } + if sps.pic_order_cnt_type == 1 && !sps.delta_pic_order_always_zero { + delta_pic_order_cnt[0] = r.read_se()?; + if pps.bottom_field_pic_order_in_frame_present && !field_pic { + delta_pic_order_cnt[1] = r.read_se()?; + } + } + let redundant_pic_cnt = if pps.redundant_pic_cnt_present { r.read_ue()? } else { 0 }; + let direct_spatial_mv_pred = if slice_type.is_b() { r.read_flag()? } else { false }; + let mut num_ref_idx_active = [pps.num_ref_idx_l0_default_active, pps.num_ref_idx_l1_default_active]; + if !slice_type.is_intra() { + if r.read_flag()? { + num_ref_idx_active[0] = r.read_ue()? + 1; + if slice_type.is_b() { + num_ref_idx_active[1] = r.read_ue()? + 1; + } + } + let max = if field_pic { 32 } else { 16 }; + ensure!(num_ref_idx_active[0] <= max && num_ref_idx_active[1] <= max, "num_ref_idx_active out of range"); + } + if !slice_type.is_b() { + num_ref_idx_active[1] = 0; + } + if slice_type.is_intra() { + num_ref_idx_active[0] = 0; + } + ensure!(nal.nal_unit_type != 20 && nal.nal_unit_type != 21, "MVC slices are not supported"); + let mut ref_pic_list_mod = [Vec::new(), Vec::new()]; + for (list, mods) in ref_pic_list_mod.iter_mut().enumerate() { + let present = if list == 0 { !slice_type.is_intra() } else { slice_type.is_b() }; + if present && r.read_flag()? { + loop { + let idc = r.read_ue()?; + ensure!(idc <= 5, "modification_of_pic_nums_idc out of range"); + if idc == 3 { + break; + } + let value = r.read_ue()?; + mods.push(RefPicListMod { idc, value }); + ensure!(mods.len() <= 66, "too many ref_pic_list_modification entries"); + } + } + } + let mut pred_weight_table = None; + if (pps.weighted_pred && slice_type.is_p()) || (pps.weighted_bipred_idc == 1 && slice_type.is_b()) { + let luma_log2_denom = r.read_ue()?; + ensure!(luma_log2_denom <= 7, "luma_log2_weight_denom out of range"); + let mut chroma_log2_denom = 0; + if sps.chroma_array_type() != 0 { + chroma_log2_denom = r.read_ue()?; + ensure!(chroma_log2_denom <= 7, "chroma_log2_weight_denom out of range"); + } + let mut lists = [Vec::new(), Vec::new()]; + let nlists = if slice_type.is_b() { 2 } else { 1 }; + for (l, entries) in lists.iter_mut().enumerate().take(nlists) { + for _ in 0..num_ref_idx_active[l] { + let mut e = WeightEntry { luma_weight: 1 << luma_log2_denom, chroma_weight: [1 << chroma_log2_denom; 2], ..Default::default() }; + if r.read_flag()? { + e.luma_flag = true; + e.luma_weight = r.read_se()?; + e.luma_offset = r.read_se()?; + ensure!((-128..=127).contains(&e.luma_weight) && (-128..=127).contains(&e.luma_offset), "luma weight out of range"); + } + if sps.chroma_array_type() != 0 && r.read_flag()? { + e.chroma_flag = true; + for j in 0..2 { + e.chroma_weight[j] = r.read_se()?; + e.chroma_offset[j] = r.read_se()?; + ensure!( + (-128..=127).contains(&e.chroma_weight[j]) && (-128..=127).contains(&e.chroma_offset[j]), + "chroma weight out of range" + ); + } + } + entries.push(e); + } + } + let [l0, l1] = lists; + pred_weight_table = Some(PredWeightTable { luma_log2_denom, chroma_log2_denom, l0, l1 }); + } + let mut no_output_of_prior_pics = false; + let mut long_term_reference = false; + let mut adaptive_ref_pic_marking = false; + let mut mmcos = Vec::new(); + if nal.nal_ref_idc != 0 { + if idr { + no_output_of_prior_pics = r.read_flag()?; + long_term_reference = r.read_flag()?; + } else { + adaptive_ref_pic_marking = r.read_flag()?; + if adaptive_ref_pic_marking { + loop { + let op = r.read_ue()?; + ensure!(op <= 6, "memory_management_control_operation out of range"); + if op == 0 { + break; + } + let mut m = Mmco { + op, + difference_of_pic_nums_minus1: 0, + long_term_pic_num: 0, + long_term_frame_idx: 0, + max_long_term_frame_idx_plus1: 0, + }; + if op == 1 || op == 3 { + m.difference_of_pic_nums_minus1 = r.read_ue()?; + } + if op == 2 { + m.long_term_pic_num = r.read_ue()?; + } + if op == 3 || op == 6 { + m.long_term_frame_idx = r.read_ue()?; + } + if op == 4 { + m.max_long_term_frame_idx_plus1 = r.read_ue()?; + } + mmcos.push(m); + ensure!(mmcos.len() <= 66, "too many MMCOs"); + } + } + } + } + let mut cabac_init_idc = 0; + if pps.entropy_coding_mode && !slice_type.is_intra() { + cabac_init_idc = r.read_ue()?; + ensure!(cabac_init_idc <= 2, "cabac_init_idc out of range"); + } + let slice_qp_delta = r.read_se()?; + let qp = pps.pic_init_qp + slice_qp_delta; + ensure!((0..=51).contains(&qp), "slice QP {qp} out of range"); + let mut sp_for_switch = false; + let mut slice_qs_delta = 0; + if matches!(slice_type, SliceType::Sp | SliceType::Si) { + if slice_type == SliceType::Sp { + sp_for_switch = r.read_flag()?; + } + slice_qs_delta = r.read_se()?; + } + let mut disable_deblocking_filter_idc = 0; + let mut slice_alpha_c0_offset_div2 = 0; + let mut slice_beta_offset_div2 = 0; + if pps.deblocking_filter_control_present { + disable_deblocking_filter_idc = r.read_ue()?; + ensure!(disable_deblocking_filter_idc <= 2, "disable_deblocking_filter_idc out of range"); + if disable_deblocking_filter_idc != 1 { + slice_alpha_c0_offset_div2 = r.read_se()?; + slice_beta_offset_div2 = r.read_se()?; + ensure!( + (-6..=6).contains(&slice_alpha_c0_offset_div2) && (-6..=6).contains(&slice_beta_offset_div2), + "deblocking offsets out of range" + ); + } + } + let mut slice_group_change_cycle = 0; + if pps.num_slice_groups > 1 && (3..=5).contains(&pps.slice_group_map_type) { + // Ceil(Log2(PicSizeInMapUnits / SliceGroupChangeRate + 1)); rate is not retained, so read + // conservatively using the picture size (FMO is unsupported for decoding anyway). + let pic_size = sps.pic_width_in_mbs * sps.pic_height_in_map_units; + let bits = 32 - pic_size.leading_zeros(); + slice_group_change_cycle = r.read_bits(bits)?; + } + let header_bits = r.position(); + Ok(( + SliceHeader { + nal_ref_idc: nal.nal_ref_idc, + nal_unit_type: nal.nal_unit_type, + idr, + first_mb_in_slice, + slice_type_raw, + slice_type, + pps_id, + colour_plane_id, + frame_num, + field_pic, + bottom_field, + idr_pic_id, + pic_order_cnt_lsb, + delta_pic_order_cnt_bottom, + delta_pic_order_cnt, + redundant_pic_cnt, + direct_spatial_mv_pred, + num_ref_idx_active, + ref_pic_list_mod, + pred_weight_table, + no_output_of_prior_pics, + long_term_reference, + adaptive_ref_pic_marking, + mmcos, + cabac_init_idc, + slice_qp_delta, + sp_for_switch, + slice_qs_delta, + disable_deblocking_filter_idc, + slice_alpha_c0_offset_div2, + slice_beta_offset_div2, + slice_group_change_cycle, + header_bits, + }, + pps, + sps, + )) + } +} + +/// POC decoding state carried between pictures (8.2.1). +#[derive(Clone, Debug, Default)] +pub struct PocState { + /// PicOrderCntMsb / pic_order_cnt_lsb of the previous reference picture (type 0). + prev_ref_msb: i32, + prev_ref_lsb: i32, + /// FrameNumOffset / frame_num of the previous picture (types 1 and 2). + prev_frame_num_offset: i32, + prev_frame_num: u32, +} + +/// Output of POC computation. +#[derive(Clone, Copy, Debug, PartialEq, Eq)] +pub struct Poc { + pub top: i32, + pub bottom: i32, + msb: i32, + frame_num_offset: i32, +} + +impl Poc { + pub fn frame(&self) -> i32 { + self.top.min(self.bottom) + } +} + +impl PocState { + /// Compute TopFieldOrderCnt/BottomFieldOrderCnt of a frame (8.2.1.1-8.2.1.3). + pub fn compute(&self, sh: &SliceHeader, sps: &Sps) -> Poc { + match sps.pic_order_cnt_type { + 0 => { + let (prev_msb, prev_lsb) = if sh.idr { (0, 0) } else { (self.prev_ref_msb, self.prev_ref_lsb) }; + let max_lsb = 1i32 << sps.log2_max_poc_lsb; + let lsb = sh.pic_order_cnt_lsb as i32; + let msb = if lsb < prev_lsb && prev_lsb - lsb >= max_lsb / 2 { + prev_msb + max_lsb + } else if lsb > prev_lsb && lsb - prev_lsb > max_lsb / 2 { + prev_msb - max_lsb + } else { + prev_msb + }; + let top = msb + lsb; + let bottom = top + sh.delta_pic_order_cnt_bottom; + Poc { top, bottom, msb, frame_num_offset: 0 } + } + 1 => { + let frame_num_offset = self.frame_num_offset(sh.idr, sh.frame_num, sps); + let n = sps.offset_for_ref_frame.len() as i32; + let mut abs_frame_num = if n != 0 { frame_num_offset + sh.frame_num as i32 } else { 0 }; + if sh.nal_ref_idc == 0 && abs_frame_num > 0 { + abs_frame_num -= 1; + } + let mut expected = 0i32; + if abs_frame_num > 0 { + let cycle_cnt = (abs_frame_num - 1) / n; + let in_cycle = (abs_frame_num - 1) % n; + let delta_cycle: i32 = sps.offset_for_ref_frame.iter().sum(); + expected = cycle_cnt.wrapping_mul(delta_cycle); + for &o in &sps.offset_for_ref_frame[..=in_cycle as usize] { + expected += o; + } + } + if sh.nal_ref_idc == 0 { + expected += sps.offset_for_non_ref_pic; + } + let top = expected + sh.delta_pic_order_cnt[0]; + let bottom = top + sps.offset_for_top_to_bottom_field + sh.delta_pic_order_cnt[1]; + Poc { top, bottom, msb: 0, frame_num_offset } + } + _ => { + let frame_num_offset = self.frame_num_offset(sh.idr, sh.frame_num, sps); + let temp = if sh.idr { + 0 + } else if sh.nal_ref_idc == 0 { + 2 * (frame_num_offset + sh.frame_num as i32) - 1 + } else { + 2 * (frame_num_offset + sh.frame_num as i32) + }; + Poc { top: temp, bottom: temp, msb: 0, frame_num_offset } + } + } + } + + fn frame_num_offset(&self, idr: bool, frame_num: u32, sps: &Sps) -> i32 { + if idr { + 0 + } else if self.prev_frame_num > frame_num { + self.prev_frame_num_offset + sps.max_frame_num() as i32 + } else { + self.prev_frame_num_offset + } + } + + /// Update after a picture has been decoded. `poc` is the value returned by [`PocState::compute`] + /// (before any MMCO5 adjustment). + pub fn update(&mut self, sh: &SliceHeader, poc: &Poc) { + let mmco5 = sh.has_mmco5(); + if sh.nal_ref_idc != 0 { + if mmco5 { + self.prev_ref_msb = 0; + self.prev_ref_lsb = poc.top - poc.frame(); + } else { + self.prev_ref_msb = poc.msb; + self.prev_ref_lsb = sh.pic_order_cnt_lsb as i32; + } + } + if mmco5 { + self.prev_frame_num_offset = 0; + self.prev_frame_num = 0; + } else { + self.prev_frame_num_offset = poc.frame_num_offset; + self.prev_frame_num = sh.frame_num; + } + } + + /// Account for a "non-existing" frame inferred by the frame_num gap process (8.2.5.2). + pub fn update_gap_frame(&mut self, frame_num: u32, sps: &Sps) { + let off = self.frame_num_offset(false, frame_num, sps); + self.prev_frame_num_offset = off; + self.prev_frame_num = frame_num; + } +} diff --git a/crates/h264/src/slicedec.rs b/crates/h264/src/slicedec.rs new file mode 100644 index 0000000..16bba04 --- /dev/null +++ b/crates/h264/src/slicedec.rs @@ -0,0 +1,1469 @@ +//! Slice data decoding: macroblock parsing (CAVLC), motion vector derivation, and reconstruction. + +use crate::cavlc; +use crate::deblock; +use crate::error::{Result, ensure, invalid}; +use crate::inter::{self, Weight}; +use crate::intra::{self, Avail}; +use crate::mbtypes::*; +use crate::params::{Pps, Sps}; +use crate::picture::*; +use crate::slice::{SliceHeader, SliceType}; +use crate::tables::*; +use crate::transform::{self, LevelScale}; +use slidecraft_bitstream::BitReader; + +/// Per-slice data kept with the picture (deblocking, motion field export). +#[derive(Clone, Debug, Default)] +pub struct SliceInfo { + pub disable_deblocking_filter_idc: u32, + pub alpha_offset: i32, + pub beta_offset: i32, + /// Unique ids of the pictures in RefPicList0/1. + pub ref_ids: [Vec; 2], +} + +/// The picture under construction: private reconstruction buffers plus the shared [`Frame`] that +/// finished (deblocked) macroblock rows are published into. +pub struct PicState { + pub planes: Planes, + pub mbs: Vec, + pub slices: Vec, + pub mb_w: usize, + pub mb_h: usize, + pub poc: i32, + pub decoded_mbs: usize, + pub frame: FrameRef, + /// Decoded macroblocks per MB row. + row_count: Vec, + /// Leading MB rows that are fully reconstructed / deblocked / published. + rows_recon: usize, + rows_deblocked: usize, + rows_published: usize, + /// Draft mode for a non-reference picture: rows are published without deblocking. + pub skip_deblock: bool, +} + +impl PicState { + pub fn new(frame: FrameRef) -> Self { + let (mb_w, mb_h) = (frame.mb_w, frame.mb_h()); + PicState { + planes: Planes::new(mb_w * 16, mb_h * 16), + mbs: vec![MbState::default(); mb_w * mb_h], + slices: Vec::new(), + mb_w, + mb_h, + poc: frame.poc, + decoded_mbs: 0, + frame, + row_count: vec![0; mb_h], + rows_recon: 0, + rows_deblocked: 0, + rows_published: 0, + skip_deblock: false, + } + } + + /// Reuse the buffers of a finished picture of the same size for a new frame. + pub fn reset(&mut self, frame: FrameRef) { + debug_assert_eq!((self.mb_w, self.mb_h), (frame.mb_w, frame.mb_h())); + // start_mb() re-initialises each macroblock before use; only availability must be reset + for m in &mut self.mbs { + m.slice_num = u32::MAX; + } + self.slices.clear(); + self.poc = frame.poc; + self.decoded_mbs = 0; + self.frame = frame; + self.row_count.fill(0); + self.rows_recon = 0; + self.rows_deblocked = 0; + self.rows_published = 0; + self.skip_deblock = false; + } + + /// Record a decoded macroblock and deblock / publish rows that became final. + #[inline] + pub fn mb_done(&mut self, addr: usize) { + self.decoded_mbs += 1; + let r = addr / self.mb_w; + self.row_count[r] += 1; + if self.row_count[r] as usize >= self.mb_w && r == self.rows_recon { + while self.rows_recon < self.mb_h && self.row_count[self.rows_recon] as usize >= self.mb_w { + self.rows_recon += 1; + } + self.flush_rows(false); + } + } + + /// Deblock and publish rows. With `finished`, everything remaining is processed (also rows with + /// missing macroblocks, so that waiting readers never block forever). + pub fn flush_rows(&mut self, finished: bool) { + // Deblocking row r must wait until row r + 1 is reconstructed (its intra prediction reads the + // unfiltered bottom line of row r); row r is final once row r + 1 is deblocked. + let deblock_limit = if finished { self.mb_h } else { self.rows_recon.saturating_sub(1) }; + while self.rows_deblocked < deblock_limit { + let r = self.rows_deblocked; + if !self.skip_deblock { + for addr in r * self.mb_w..(r + 1) * self.mb_w { + deblock::deblock_mb(self, addr, self.mb_w); + } + } + self.rows_deblocked += 1; + } + let publish_limit = if finished { self.mb_h } else { self.rows_deblocked.saturating_sub(1) }; + while self.rows_published < publish_limit { + let r = self.rows_published; + let slices = &self.slices; + let ids = |st: &MbState, l: usize, ri: i8| { + slices.get(st.slice_num as usize).and_then(|s| s.ref_ids[l].get(ri as usize).copied()).unwrap_or(u32::MAX) + }; + let row = Frame::make_row(&self.planes, r, &self.mbs, &ids); + self.frame.publish(r, row); + self.rows_published += 1; + } + } +} + +#[derive(Clone, Copy, PartialEq, Eq)] +enum WpMode { + Default, + Explicit, + Implicit, +} + +/// Prediction weight tables of a slice. +struct Weights { + mode: WpMode, + luma_denom: i32, + chroma_denom: i32, + /// [list][ref] -> (weight, offset) for luma and two chroma components. + luma: [Vec<(i32, i32)>; 2], + chroma: [Vec<[(i32, i32); 2]>; 2], + /// Implicit weights (w0, w1) indexed by ref0 * n1 + ref1. + implicit: Vec<(i32, i32)>, + n1: usize, +} + +/// Current macroblock syntax (prediction part). +#[derive(Clone, Copy, Default)] +pub(crate) struct MbCur { + pub info: MbTypeInfo, + pub sub: [SubMbInfo; 4], + /// rem_intra_pred_mode per block, or -1 when prev_intra_pred_mode_flag was set. + pub rem_mode: [i8; 16], + pub cbp: u8, + pub ref_idx: [[i8; 4]; 2], + /// mvd per (partition * 4 + sub-partition). + pub mvd: [[[i16; 2]; 16]; 2], +} + +/// Coefficient scratch for one macroblock. +pub(crate) struct Scratch { + /// Luma: 16 4x4 blocks (raster block index * 16 + raster coefficient) or 4 8x8 blocks (b8 * 64 + raster). + pub coef: [i32; 256], + /// Chroma: [Cb, Cr][raster 2x2 block * 16 + raster coefficient]. + pub coef_c: [[i32; 64]; 2], + /// Luma blocks with coefficients: raster 4x4 bits, or b8 bits in 8x8 mode. + pub blk_nz: u16, + /// Chroma blocks with coefficients (bit per raster 2x2 block). + pub c_nz: [u8; 2], + pub pred: [[u8; 256]; 2], + pub pred_c: [[[u8; 64]; 2]; 2], +} + +impl Scratch { + pub fn new() -> Box { + Box::new(Scratch { coef: [0; 256], coef_c: [[0; 64]; 2], blk_nz: 0, c_nz: [0; 2], pred: [[0; 256]; 2], pred_c: [[[0; 64]; 2]; 2] }) + } +} + +#[derive(Clone, Copy, Debug)] +pub(crate) struct Nb { + pub avail: bool, + pub ref_idx: i8, + pub mv: [i16; 2], +} + +const NB_NA: Nb = Nb { avail: false, ref_idx: -1, mv: [0, 0] }; + +pub struct SliceDecoder<'a> { + pub sh: &'a SliceHeader, + pub pps: &'a Pps, + pub sps: &'a Sps, + pub pic: &'a mut PicState, + pub refs: &'a [Vec; 2], + pub ls: &'a LevelScale, + pub slice_num: u32, + pub mb_w: usize, + pub mb_h: usize, + pub qp: i32, + pub chroma_qp_offset: [i32; 2], + // current macroblock + pub mb_addr: usize, + pub mb_x: usize, + pub mb_y: usize, + /// Neighbouring macroblocks A (left), B (above), C (above right), D (above left) when available. + pub nb: [Option; 4], + pub mv_done: u16, + pub cur: MbCur, + pub s: Box, + weights: Weights, + /// Temporal direct: DistScaleFactor per refIdxL0, and whether pic0 is long-term. + tdirect: Vec<(i32, bool)>, + /// CABAC: previous MB in decoding order had a non-zero mb_qp_delta. + pub prev_qp_delta_nz: bool, +} + +/// Interpolate a bw x bh luma block at (px, py) with motion vector `mv` from reference `f` into `y`, +/// and the corresponding chroma blocks into `c` (stride `cs`). +#[allow(clippy::too_many_arguments)] +#[inline] +fn predict_block(f: &Frame, mv: [i16; 2], px: usize, py: usize, bw: usize, bh: usize, y: (&mut [u8], usize), c: [&mut [u8]; 2], cs: usize) { + const LW: usize = 24; + let mut win = [0u8; LW * 21]; + let ix = px as i32 + (mv[0] as i32 >> 2); + let iy = py as i32 + (mv[1] as i32 >> 2); + f.luma_window(ix - 2, iy - 2, bw + 5, bh + 5, &mut win, LW); + inter::mc_luma_win(&win, LW, (mv[0] & 3) as u32, (mv[1] & 3) as u32, bw, bh, y.0, y.1); + let cx = (px / 2) as i32 + (mv[0] as i32 >> 3); + let cy = (py / 2) as i32 + (mv[1] as i32 >> 3); + let (cw, ch) = (bw / 2, bh / 2); + let (fx, fy) = ((mv[0] & 7) as u32, (mv[1] & 7) as u32); + for (comp, out) in c.into_iter().enumerate() { + let mut w = [0u8; 16 * 9]; + f.chroma_window(comp, cx, cy, cw + 1, ch + 1, &mut w, 16); + inter::mc_chroma_win(&w, 16, fx, fy, cw, ch, out, cs); + } +} + +#[inline(always)] +fn median(a: i16, b: i16, c: i16) -> i16 { + a.max(b).min(a.min(b).max(c)) +} + +impl<'a> SliceDecoder<'a> { + pub fn new( + sh: &'a SliceHeader, + pps: &'a Pps, + sps: &'a Sps, + pic: &'a mut PicState, + refs: &'a [Vec; 2], + ls: &'a LevelScale, + ) -> Result { + let slice_num = pic.slices.len() as u32; + let chroma_qp_offset = [pps.chroma_qp_index_offset, pps.second_chroma_qp_index_offset]; + pic.slices.push(SliceInfo { + disable_deblocking_filter_idc: sh.disable_deblocking_filter_idc, + alpha_offset: sh.slice_alpha_c0_offset_div2 * 2, + beta_offset: sh.slice_beta_offset_div2 * 2, + ref_ids: [refs[0].iter().map(|r| r.id()).collect(), refs[1].iter().map(|r| r.id()).collect()], + }); + let weights = Self::build_weights(sh, pps, refs, pic.poc); + let mut tdirect = Vec::new(); + if sh.slice_type == SliceType::B && !sh.direct_spatial_mv_pred && !refs[1].is_empty() { + let poc1 = refs[1][0].poc(); + for r0 in refs[0].iter() { + let tb = (pic.poc - r0.poc()).clamp(-128, 127); + let td = (poc1 - r0.poc()).clamp(-128, 127); + if td == 0 || r0.long_term { + tdirect.push((256, true)); + } else { + let tx = (16384 + (td / 2).abs()) / td; + tdirect.push((((tb * tx + 32) >> 6).clamp(-1024, 1023), false)); + } + } + } + let (mb_w, mb_h) = (pic.mb_w, pic.mb_h); + Ok(SliceDecoder { + sh, + pps, + sps, + pic, + refs, + ls, + slice_num, + mb_w, + mb_h, + qp: sh.qp(pps), + chroma_qp_offset, + mb_addr: 0, + mb_x: 0, + mb_y: 0, + nb: [None; 4], + mv_done: 0, + cur: MbCur::default(), + s: Scratch::new(), + weights, + tdirect, + prev_qp_delta_nz: false, + }) + } + + fn build_weights(sh: &SliceHeader, pps: &Pps, refs: &[Vec; 2], cur_poc: i32) -> Weights { + let mut w = Weights { + mode: WpMode::Default, + luma_denom: 0, + chroma_denom: 0, + luma: [Vec::new(), Vec::new()], + chroma: [Vec::new(), Vec::new()], + implicit: Vec::new(), + n1: refs[1].len(), + }; + let explicit = (pps.weighted_pred && sh.slice_type.is_p()) || (pps.weighted_bipred_idc == 1 && sh.slice_type.is_b()); + if explicit { + if let Some(t) = &sh.pred_weight_table { + w.mode = WpMode::Explicit; + w.luma_denom = t.luma_log2_denom as i32; + w.chroma_denom = t.chroma_log2_denom as i32; + for (l, entries) in [&t.l0, &t.l1].into_iter().enumerate() { + w.luma[l] = entries.iter().map(|e| (e.luma_weight, e.luma_offset)).collect(); + w.chroma[l] = + entries.iter().map(|e| [(e.chroma_weight[0], e.chroma_offset[0]), (e.chroma_weight[1], e.chroma_offset[1])]).collect(); + } + } + } else if pps.weighted_bipred_idc == 2 && sh.slice_type.is_b() { + w.mode = WpMode::Implicit; + for r0 in refs[0].iter() { + for r1 in refs[1].iter() { + let tb = (cur_poc - r0.poc()).clamp(-128, 127); + let td = (r1.poc() - r0.poc()).clamp(-128, 127); + let pair = if td == 0 || r0.long_term || r1.long_term { + (32, 32) + } else { + let tx = (16384 + (td / 2).abs()) / td; + let dsf = ((tb * tx + 32) >> 6).clamp(-1024, 1023); + if (dsf >> 2) < -64 || (dsf >> 2) > 128 { (32, 32) } else { (64 - (dsf >> 2), dsf >> 2) } + }; + w.implicit.push(pair); + } + } + } + w + } + + // ------------------------------------------------------------------------------------------ + // Neighbour helpers + // ------------------------------------------------------------------------------------------ + + /// Set up the current macroblock address and neighbour availability. + pub fn start_mb(&mut self, addr: usize) { + self.mb_addr = addr; + self.mb_x = addr % self.mb_w; + self.mb_y = addr / self.mb_w; + let sn = self.slice_num; + let mbs = &self.pic.mbs; + let w = self.mb_w; + let (x, y) = (self.mb_x, self.mb_y); + let ok = |a: usize| if mbs[a].slice_num == sn { Some(a) } else { None }; + self.nb = [ + if x > 0 { ok(addr - 1) } else { None }, + if y > 0 { ok(addr - w) } else { None }, + if y > 0 && x + 1 < w { ok(addr - w + 1) } else { None }, + if y > 0 && x > 0 { ok(addr - w - 1) } else { None }, + ]; + self.mv_done = 0; + self.pic.mbs[addr] = MbState { slice_num: sn, ..MbState::default() }; + } + + #[inline] + pub fn mb(&self) -> &MbState { + &self.pic.mbs[self.mb_addr] + } + #[inline] + pub fn mb_mut(&mut self) -> &mut MbState { + &mut self.pic.mbs[self.mb_addr] + } + + /// Neighbour MB usable for intra prediction (constrained_intra_pred aware). + fn intra_nb(&self, n: usize) -> bool { + match self.nb[n] { + None => false, + Some(a) => !self.pps.constrained_intra_pred || self.pic.mbs[a].kind.is_intra(), + } + } + + fn chroma_qp(&self, qp: i32, c: usize) -> i32 { + QPC_TABLE[(qp + self.chroma_qp_offset[c]).clamp(0, 51) as usize] as i32 + } + + /// nC for a luma 4x4 block (raster index) - 9.2.1. + fn nc_luma(&self, raster: usize) -> i32 { + let (bx, by) = (raster & 3, raster >> 2); + let cur = self.mb(); + let a = if bx > 0 { Some(cur.nnz[raster - 1]) } else { self.nb[0].map(|m| self.pic.mbs[m].nnz[by * 4 + 3]) }; + let b = if by > 0 { Some(cur.nnz[raster - 4]) } else { self.nb[1].map(|m| self.pic.mbs[m].nnz[12 + bx]) }; + match (a, b) { + (Some(a), Some(b)) => (a as i32 + b as i32 + 1) >> 1, + (Some(a), None) => a as i32, + (None, Some(b)) => b as i32, + _ => 0, + } + } + + fn nc_chroma(&self, c: usize, raster: usize) -> i32 { + let (bx, by) = (raster & 1, raster >> 1); + let cur = self.mb(); + let a = if bx > 0 { Some(cur.nnz_c[c][raster - 1]) } else { self.nb[0].map(|m| self.pic.mbs[m].nnz_c[c][by * 2 + 1]) }; + let b = if by > 0 { Some(cur.nnz_c[c][raster - 2]) } else { self.nb[1].map(|m| self.pic.mbs[m].nnz_c[c][2 + bx]) }; + match (a, b) { + (Some(a), Some(b)) => (a as i32 + b as i32 + 1) >> 1, + (Some(a), None) => a as i32, + (None, Some(b)) => b as i32, + _ => 0, + } + } + + /// Motion data of the neighbouring 4x4 block at (x, y) (4x4 units, relative to the current MB). + pub(crate) fn nb_motion(&self, list: usize, x: i32, y: i32) -> Nb { + let (mb, bx, by) = if x < 0 { + if y < 0 { + (self.nb[3], 3, 3) + } else if y > 3 { + (None, 0, 0) + } else { + (self.nb[0], 3, y as usize) + } + } else if x > 3 { + if y < 0 { (self.nb[2], 0, 3) } else { (None, 0, 0) } + } else if y < 0 { + (self.nb[1], x as usize, 3) + } else { + let r = (y * 4 + x) as usize; + if self.mv_done & (1 << r) == 0 { + return NB_NA; + } + (Some(self.mb_addr), x as usize, y as usize) + }; + let Some(m) = mb else { return NB_NA }; + let st = &self.pic.mbs[m]; + let b8 = (by >> 1) * 2 + (bx >> 1); + let r = st.ref_idx[list][b8]; + if r < 0 { + return Nb { avail: true, ref_idx: -1, mv: [0, 0] }; + } + Nb { avail: true, ref_idx: r, mv: st.mv[list][by * 4 + bx] } + } + + /// Returns (A, B, C) neighbour motion for a partition at (x, y) of width w (4x4 units), with C + /// replaced by D when unavailable. + fn nb_abc(&self, list: usize, x: usize, y: usize, w: usize) -> (Nb, Nb, Nb) { + let (x, y, w) = (x as i32, y as i32, w as i32); + let a = self.nb_motion(list, x - 1, y); + let b = self.nb_motion(list, x, y - 1); + let mut c = self.nb_motion(list, x + w, y - 1); + if !c.avail { + c = self.nb_motion(list, x - 1, y - 1); + } + (a, b, c) + } + + /// Luma motion vector prediction (8.4.1.3). `shape`: 1 = 16x8, 2 = 8x16, 0 = other. + #[allow(clippy::too_many_arguments)] + fn mvp(&self, list: usize, x: usize, y: usize, w: usize, ref_idx: i8, shape: u8, part: usize) -> [i16; 2] { + let (a, b, c) = self.nb_abc(list, x, y, w); + if shape == 1 { + if part == 0 && b.ref_idx == ref_idx { + return b.mv; + } + if part == 1 && a.ref_idx == ref_idx { + return a.mv; + } + } else if shape == 2 { + if part == 0 && a.ref_idx == ref_idx { + return a.mv; + } + if part == 1 && c.ref_idx == ref_idx { + return c.mv; + } + } + Self::median_pred(a, b, c, ref_idx) + } + + fn median_pred(a: Nb, mut b: Nb, mut c: Nb, ref_idx: i8) -> [i16; 2] { + if !b.avail && !c.avail && a.avail { + b = a; + c = a; + } + let ma = a.ref_idx == ref_idx; + let mb = b.ref_idx == ref_idx; + let mc = c.ref_idx == ref_idx; + match (ma, mb, mc) { + (true, false, false) => a.mv, + (false, true, false) => b.mv, + (false, false, true) => c.mv, + _ => [median(a.mv[0], b.mv[0], c.mv[0]), median(a.mv[1], b.mv[1], c.mv[1])], + } + } + + /// Store motion for a rectangle (4x4 units) of the current MB and mark it decoded. + fn set_motion(&mut self, x: usize, y: usize, w: usize, h: usize, refs: [i8; 2], mvs: [[i16; 2]; 2]) { + let st = &mut self.pic.mbs[self.mb_addr]; + for yy in y..y + h { + for xx in x..x + w { + let r = yy * 4 + xx; + for l in 0..2 { + st.mv[l][r] = if refs[l] >= 0 { mvs[l] } else { [0, 0] }; + } + self.mv_done |= 1 << r; + } + } + for l in 0..2 { + // ref_idx is per 8x8; rectangles never straddle 8x8 blocks with different refs + for yy in (y..y + h).step_by(1) { + for xx in x..x + w { + st.ref_idx[l][(yy >> 1) * 2 + (xx >> 1)] = refs[l]; + } + } + } + } + + // ------------------------------------------------------------------------------------------ + // Direct prediction (8.4.1.2) + // ------------------------------------------------------------------------------------------ + + /// Co-located (mvCol, refIdxCol, referenced picture id) for 4x4 block `raster` of the current MB. + fn colocated(&self, raster: usize) -> ([i16; 2], i8, u32) { + let Some(col) = self.refs[1].first() else { return ([0, 0], -1, u32::MAX) }; + let raster = if self.sps.direct_8x8_inference { + let (bx, by) = (raster & 3, raster >> 2); + (if by >= 2 { 12 } else { 0 }) + if bx >= 2 { 3 } else { 0 } + } else { + raster + }; + if col.frame.mb_w != self.mb_w || self.mb_y >= col.frame.mb_h() { + return ([0, 0], -1, u32::MAX); + } + let m = col.frame.row(self.mb_y); + let mb = self.mb_x; + if m.intra[mb] { + return ([0, 0], -1, u32::MAX); + } + let b8 = mb * 4 + (raster >> 3) * 2 + ((raster & 3) >> 1); + let l = if m.ref_idx[0][b8] >= 0 { 0 } else { 1 }; + (m.mv[l][mb * 16 + raster], m.ref_idx[l][b8], m.ref_id[l][b8]) + } + + /// Spatial direct: MB-level reference indices, predicted mvs and directZeroPrediction (8.4.1.2.2). + fn spatial_direct_base(&self) -> ([i8; 2], [[i16; 2]; 2], bool) { + let mut refs = [-1i8; 2]; + let mut nbs = [(NB_NA, NB_NA, NB_NA); 2]; + let min_pos = |x: i8, y: i8| if x >= 0 && y >= 0 { x.min(y) } else { x.max(y) }; + for (l, nb) in nbs.iter_mut().enumerate() { + *nb = self.nb_abc(l, 0, 0, 4); + refs[l] = min_pos(nb.0.ref_idx, min_pos(nb.1.ref_idx, nb.2.ref_idx)); + } + let mut mvs = [[0i16; 2]; 2]; + if refs[0] < 0 && refs[1] < 0 { + return ([0, 0], mvs, true); + } + for l in 0..2 { + if refs[l] >= 0 { + let (a, b, c) = nbs[l]; + mvs[l] = Self::median_pred(a, b, c, refs[l]); + } + } + (refs, mvs, false) + } + + /// Derive direct-mode motion for the 8x8 blocks in `b8_mask` of the current MB. + fn derive_direct(&mut self, b8_mask: u8) -> Result<()> { + let spatial = self.sh.direct_spatial_mv_pred; + let (base_refs, base_mvs, direct_zero) = if spatial { self.spatial_direct_base() } else { ([0, 0], [[0, 0]; 2], false) }; + let col_short_term = self.refs[1].first().map(|r| !r.long_term).unwrap_or(false); + for b8 in 0..4 { + if b8_mask & (1 << b8) == 0 { + continue; + } + let (x8, y8) = ((b8 & 1) * 2, (b8 >> 1) * 2); + let unit = if self.sps.direct_8x8_inference { 2 } else { 1 }; + for sub in (0..4).filter(|s| unit == 1 || *s == 0) { + let (x, y) = (x8 + (sub & 1), y8 + (sub >> 1)); + let raster = y * 4 + x; + let (mv_col, ref_col, id_col) = self.colocated(raster); + let (refs, mvs) = if spatial { + let col_zero = col_short_term && ref_col == 0 && (-1..=1).contains(&mv_col[0]) && (-1..=1).contains(&mv_col[1]); + let mut mvs = base_mvs; + for l in 0..2 { + if direct_zero || base_refs[l] < 0 || (base_refs[l] == 0 && col_zero) { + mvs[l] = [0, 0]; + } + } + (base_refs, mvs) + } else { + let ref0 = if ref_col < 0 { + 0 + } else { + match self.refs[0].iter().position(|r| r.id() == id_col) { + Some(i) => i as i8, + None => 0, + } + }; + let (dsf, ltr) = self.tdirect.get(ref0 as usize).copied().unwrap_or((256, true)); + let (mv0, mv1) = if ltr { + (mv_col, [0, 0]) + } else { + let s = |c: i16| ((dsf * c as i32 + 128) >> 8) as i16; + let m0 = [s(mv_col[0]), s(mv_col[1])]; + (m0, [m0[0] - mv_col[0], m0[1] - mv_col[1]]) + }; + ([ref0, 0], [mv0, mv1]) + }; + self.set_motion(x, y, unit, unit, refs, mvs); + } + } + let st = &mut self.pic.mbs[self.mb_addr]; + st.direct8x8 |= b8_mask; + Ok(()) + } + + // ------------------------------------------------------------------------------------------ + // Motion vector derivation for a parsed inter macroblock + // ------------------------------------------------------------------------------------------ + + /// P_Skip motion (8.4.1.1). + pub fn derive_p_skip(&mut self) { + let a = self.nb_motion(0, -1, 0); + let b = self.nb_motion(0, 0, -1); + let mv = if self.nb[0].is_none() || self.nb[1].is_none() || (a.ref_idx == 0 && a.mv == [0, 0]) || (b.ref_idx == 0 && b.mv == [0, 0]) { + [0, 0] + } else { + self.mvp(0, 0, 0, 4, 0, 0, 0) + }; + self.set_motion(0, 0, 4, 4, [0, -1], [mv, [0, 0]]); + } + + /// Derive mvs of the current (non-skip) inter MB from `self.cur` (ref_idx, mvd). + pub fn derive_inter_motion(&mut self) -> Result<()> { + let info = self.cur.info; + if info.kind == MbKind::BDirect16x16 || info.kind == MbKind::BSkip { + return self.derive_direct(0xf); + } + let n = [self.refs[0].len(), self.refs[1].len()]; + if info.part != Part::P8x8 { + let shape = match info.part { + Part::P16x8 => 1, + Part::P8x16 => 2, + _ => 0, + }; + for p in 0..info.part.num_parts() { + let (x, y, w, h) = info.part.rect(p); + let mut refs = [-1i8; 2]; + let mut mvs = [[0i16; 2]; 2]; + for l in 0..2 { + if info.pred[p] & (1 << l) != 0 { + let r = self.cur.ref_idx[l][p]; + ensure!((r as usize) < n[l], "ref_idx {r} out of range (list {l}, size {})", n[l]); + let mvp = self.mvp(l, x, y, w, r, shape, p); + let d = self.cur.mvd[l][p * 4]; + refs[l] = r; + mvs[l] = [mvp[0].wrapping_add(d[0]), mvp[1].wrapping_add(d[1])]; + } + } + self.set_motion(x, y, w, h, refs, mvs); + } + return Ok(()); + } + for p in 0..4 { + let sub = self.cur.sub[p]; + let (x8, y8) = ((p & 1) * 2, (p >> 1) * 2); + if sub.direct { + self.derive_direct(1 << p)?; + continue; + } + let mut refs = [-1i8; 2]; + for (l, r) in refs.iter_mut().enumerate() { + if sub.pred & (1 << l) != 0 { + let v = self.cur.ref_idx[l][p]; + ensure!((v as usize) < n[l], "ref_idx {v} out of range (list {l}, size {})", n[l]); + *r = v; + } + } + for j in 0..sub.shape.num_parts() { + let (sx, sy, w, h) = sub.shape.rect(j); + let (x, y) = (x8 + sx, y8 + sy); + let mut mvs = [[0i16; 2]; 2]; + for l in 0..2 { + if refs[l] >= 0 { + let mvp = self.mvp(l, x, y, w, refs[l], 0, 0); + let d = self.cur.mvd[l][p * 4 + j]; + mvs[l] = [mvp[0].wrapping_add(d[0]), mvp[1].wrapping_add(d[1])]; + } + } + self.set_motion(x, y, w, h, refs, mvs); + } + } + Ok(()) + } + + // ------------------------------------------------------------------------------------------ + // Inter prediction + // ------------------------------------------------------------------------------------------ + + fn weight_for(&self, refs: [i8; 2], comp: usize) -> Weight { + let w = &self.weights; + match w.mode { + WpMode::Default => Weight::Default, + WpMode::Implicit => { + if refs[0] >= 0 && refs[1] >= 0 { + let (w0, w1) = w.implicit[refs[0] as usize * w.n1 + refs[1] as usize]; + Weight::Weighted { log_wd: 5, w0, w1, o0: 0, o1: 0 } + } else { + Weight::Default + } + } + WpMode::Explicit => { + let get = |l: usize| -> (i32, i32) { + if refs[l] < 0 { + return (0, 0); + } + let r = refs[l] as usize; + if comp == 0 { + w.luma[l].get(r).copied().unwrap_or((1 << w.luma_denom, 0)) + } else { + w.chroma[l].get(r).map(|c| c[comp - 1]).unwrap_or((1 << w.chroma_denom, 0)) + } + }; + let (w0, o0) = get(0); + let (w1, o1) = get(1); + let log_wd = if comp == 0 { w.luma_denom } else { w.chroma_denom }; + Weight::Weighted { log_wd, w0, w1, o0, o1 } + } + } + } + + /// Motion-compensated prediction of a rectangle (4x4 units) written into the picture. + fn mc_rect(&mut self, x4: usize, y4: usize, w4: usize, h4: usize) { + let st = &self.pic.mbs[self.mb_addr]; + let r = y4 * 4 + x4; + let b8 = (y4 >> 1) * 2 + (x4 >> 1); + let refs = [st.ref_idx[0][b8], st.ref_idx[1][b8]]; + let mvs = [st.mv[0][r], st.mv[1][r]]; + let (bw, bh) = (w4 * 4, h4 * 4); + let wts = [self.weight_for(refs, 0), self.weight_for(refs, 1), self.weight_for(refs, 2)]; + let px = self.mb_x * 16 + x4 * 4; + let py = self.mb_y * 16 + y4 * 4; + let use0 = refs[0] >= 0 && self.refs[0].get(refs[0] as usize).is_some(); + let use1 = refs[1] >= 0 && self.refs[1].get(refs[1] as usize).is_some(); + let ys = self.pic.planes.width; + let cs = self.pic.planes.cwidth; + let (cx, cy) = (px / 2, py / 2); + if use0 != use1 && wts.iter().all(|w| *w == Weight::Default) { + // single-list unweighted prediction: interpolate straight into the picture + let l = if use0 { 0 } else { 1 }; + let f = &self.refs[l][refs[l] as usize].frame; + let planes = &mut self.pic.planes; + predict_block( + f, + mvs[l], + px, + py, + bw, + bh, + (&mut planes.y[py * ys + px..], ys), + [&mut planes.cb[cy * cs + cx..], &mut planes.cr[cy * cs + cx..]], + cs, + ); + return; + } + let s = &mut *self.s; + for l in 0..2 { + if refs[l] < 0 { + continue; + } + let Some(rp) = self.refs[l].get(refs[l] as usize) else { continue }; + let [pc0, pc1] = &mut s.pred_c[l]; + predict_block(&rp.frame, mvs[l], px, py, bw, bh, (&mut s.pred[l][..], 16), [&mut pc0[..], &mut pc1[..]], 8); + } + inter::weighted_store( + &mut self.pic.planes.y[py * ys + px..], + ys, + if use0 { Some(&s.pred[0][..]) } else { None }, + if use1 { Some(&s.pred[1][..]) } else { None }, + 16, + bw, + bh, + wts[0], + ); + for c in 0..2 { + let wc = wts[c + 1]; + let a = if use0 { Some(&s.pred_c[0][c][..]) } else { None }; + let b = if use1 { Some(&s.pred_c[1][c][..]) } else { None }; + let plane = if c == 0 { &mut self.pic.planes.cb } else { &mut self.pic.planes.cr }; + inter::weighted_store(&mut plane[cy * cs + cx..], cs, a, b, 8, bw / 2, bh / 2, wc); + } + } + + /// All 16 blocks of the current MB share references and motion vectors. + fn uniform_motion(&self) -> bool { + let st = self.mb(); + (0..2).all(|l| { + let r = st.ref_idx[l]; + r[1] == r[0] && r[2] == r[0] && r[3] == r[0] && st.mv[l].iter().all(|m| *m == st.mv[l][0]) + }) + } + + /// Inter prediction for the whole current MB, using the stored motion. + pub fn predict_inter(&mut self) { + let info = self.cur.info; + let direct_unit = if self.sps.direct_8x8_inference { 2 } else { 1 }; + match info.kind { + MbKind::PSkip => self.mc_rect(0, 0, 4, 4), + MbKind::BSkip | MbKind::BDirect16x16 if self.uniform_motion() => self.mc_rect(0, 0, 4, 4), + MbKind::BSkip | MbKind::BDirect16x16 => { + for y in (0..4).step_by(direct_unit) { + for x in (0..4).step_by(direct_unit) { + self.mc_rect(x, y, direct_unit, direct_unit); + } + } + } + _ => { + if info.part != Part::P8x8 { + for p in 0..info.part.num_parts() { + let (x, y, w, h) = info.part.rect(p); + self.mc_rect(x, y, w, h); + } + } else { + for p in 0..4 { + let sub = self.cur.sub[p]; + let (x8, y8) = ((p & 1) * 2, (p >> 1) * 2); + if sub.direct { + for y in (0..2).step_by(direct_unit) { + for x in (0..2).step_by(direct_unit) { + self.mc_rect(x8 + x, y8 + y, direct_unit, direct_unit); + } + } + } else { + for j in 0..sub.shape.num_parts() { + let (sx, sy, w, h) = sub.shape.rect(j); + self.mc_rect(x8 + sx, y8 + sy, w, h); + } + } + } + } + } + } + } + + // ------------------------------------------------------------------------------------------ + // Intra prediction mode derivation (8.3.1.1 / 8.3.2.1) + // ------------------------------------------------------------------------------------------ + + fn intra_mode_nb(&self, n: usize, bx: usize, by: usize) -> Option { + // returns Some(mode) or None if dcPredModePredictedFlag must be set + let m = self.nb[n]?; + let st = &self.pic.mbs[m]; + if self.pps.constrained_intra_pred && !st.kind.is_intra() { + return None; + } + Some(match st.kind { + MbKind::I4x4 | MbKind::I8x8 => st.intra_modes[by * 4 + bx], + _ => 2, + }) + } + + /// Resolve Intra4x4PredMode / Intra8x8PredMode for the current MB from `self.cur.rem_mode`. + pub fn derive_intra_modes(&mut self, is8x8: bool) { + let step = if is8x8 { 2 } else { 1 }; + let nblk = if is8x8 { 4 } else { 16 }; + for i in 0..nblk { + let (bx, by) = if is8x8 { + ((i & 1) * 2, (i >> 1) * 2) + } else { + let (x, y) = BLK4_XY[i]; + (x as usize, y as usize) + }; + let a = if bx > 0 { Some(self.mb().intra_modes[by * 4 + bx - 1]) } else { self.intra_mode_nb(0, 3, by) }; + let b = if by > 0 { Some(self.mb().intra_modes[(by - 1) * 4 + bx]) } else { self.intra_mode_nb(1, bx, 3) }; + let pred = match (a, b) { + (Some(a), Some(b)) => a.min(b), + _ => 2, + }; + let rem = self.cur.rem_mode[i]; + let mode = if rem < 0 { + pred + } else if (rem as u8) < pred { + rem as u8 + } else { + rem as u8 + 1 + }; + let st = self.mb_mut(); + for yy in by..by + step { + for xx in bx..bx + step { + st.intra_modes[yy * 4 + xx] = mode; + } + } + } + } + + // ------------------------------------------------------------------------------------------ + // Reconstruction + // ------------------------------------------------------------------------------------------ + + fn add_luma_block4(&mut self, raster: usize) { + if self.s.blk_nz & (1 << raster) == 0 { + return; + } + let stride = self.pic.planes.width; + let px = self.mb_x * 16 + (raster & 3) * 4; + let py = self.mb_y * 16 + (raster >> 2) * 4; + let Some(blk) = self.s.coef.get_mut(raster * 16..).and_then(|c| c.first_chunk_mut::<16>()) else { + return; + }; + transform::idct4_add(blk, &mut self.pic.planes.y[py * stride + px..], stride); + blk.fill(0); + } + + fn add_luma_block8(&mut self, b8: usize) { + if self.s.blk_nz & (1 << b8) == 0 { + return; + } + let stride = self.pic.planes.width; + let px = self.mb_x * 16 + (b8 & 1) * 8; + let py = self.mb_y * 16 + (b8 >> 1) * 8; + let Some(blk) = self.s.coef.get_mut(b8 * 64..).and_then(|c| c.first_chunk_mut::<64>()) else { + return; + }; + transform::idct8_add(blk, &mut self.pic.planes.y[py * stride + px..], stride); + blk.fill(0); + } + + fn add_chroma(&mut self) { + let stride = self.pic.planes.cwidth; + for c in 0..2 { + if self.s.c_nz[c] == 0 { + continue; + } + for b in 0..4 { + if self.s.c_nz[c] & (1 << b) == 0 { + continue; + } + let px = self.mb_x * 8 + (b & 1) * 4; + let py = self.mb_y * 8 + (b >> 1) * 4; + let Some(blk) = self.s.coef_c[c].get_mut(b * 16..).and_then(|c| c.first_chunk_mut::<16>()) else { + continue; + }; + let plane = if c == 0 { &mut self.pic.planes.cb } else { &mut self.pic.planes.cr }; + transform::idct4_add(blk, &mut plane[py * stride + px..], stride); + blk.fill(0); + } + self.s.c_nz[c] = 0; + } + } + + fn luma_avail4(&self, bx: usize, by: usize) -> Avail { + let a = self.intra_nb(0); + let b = self.intra_nb(1); + let c = self.intra_nb(2); + let d = self.intra_nb(3); + let left = if bx > 0 { true } else { a }; + let top = if by > 0 { true } else { b }; + let top_left = match (bx > 0, by > 0) { + (true, true) => true, + (false, true) => a, + (true, false) => b, + (false, false) => d, + }; + let top_right = if by == 0 { + if bx < 3 { b } else { c } + } else if bx == 3 { + false + } else { + RASTER_TO_BLK4[(by - 1) * 4 + bx + 1] < RASTER_TO_BLK4[by * 4 + bx] + }; + Avail { left, top, top_left, top_right } + } + + /// Reconstruct an intra MB (prediction + residual) after its syntax has been parsed. + pub fn reconstruct_intra(&mut self) { + let kind = self.mb().kind; + let stride = self.pic.planes.width; + let (x0, y0) = (self.mb_x * 16, self.mb_y * 16); + match kind { + MbKind::I4x4 => { + for (blk, &(bx, by)) in BLK4_XY.iter().enumerate() { + let (bx, by) = (bx as usize, by as usize); + let _ = blk; + let av = self.luma_avail4(bx, by); + let mode = self.mb().intra_modes[by * 4 + bx]; + intra::pred4x4(&mut self.pic.planes.y, stride, x0 + bx * 4, y0 + by * 4, mode, av); + self.add_luma_block4(by * 4 + bx); + } + } + MbKind::I8x8 => { + for b8 in 0..4 { + let (bx, by) = ((b8 & 1) * 2, (b8 >> 1) * 2); + let mut av = self.luma_avail4(bx, by); + av.top_right = match b8 { + 0 => self.intra_nb(1), + 1 => self.intra_nb(2), + 2 => true, + _ => false, + }; + let mode = self.mb().intra_modes[by * 4 + bx]; + intra::pred8x8(&mut self.pic.planes.y, stride, x0 + bx * 4, y0 + by * 4, mode, av); + self.add_luma_block8(b8); + } + } + MbKind::I16x16 => { + let av = Avail { left: self.intra_nb(0), top: self.intra_nb(1), top_left: self.intra_nb(3), top_right: false }; + intra::pred16x16(&mut self.pic.planes.y, stride, x0, y0, self.cur.info.i16_mode, av); + for r in 0..16 { + self.add_luma_block4(r); + } + } + _ => {} + } + let av = Avail { left: self.intra_nb(0), top: self.intra_nb(1), top_left: self.intra_nb(3), top_right: false }; + let cs = self.pic.planes.cwidth; + let mode = self.mb().intra_chroma_mode; + let (cx, cy) = (self.mb_x * 8, self.mb_y * 8); + intra::pred_chroma(&mut self.pic.planes.cb, cs, cx, cy, mode, av); + intra::pred_chroma(&mut self.pic.planes.cr, cs, cx, cy, mode, av); + self.add_chroma(); + self.s.blk_nz = 0; + } + + /// Add the residual of an inter MB (prediction already written). + pub fn reconstruct_inter_residual(&mut self) { + if self.mb().transform_8x8 { + for b8 in 0..4 { + self.add_luma_block8(b8); + } + } else if self.s.blk_nz != 0 { + for r in 0..16 { + self.add_luma_block4(r); + } + } + self.add_chroma(); + self.s.blk_nz = 0; + } + + /// Write I_PCM samples. + pub fn write_pcm(&mut self, samples: &[u8]) { + let stride = self.pic.planes.width; + let (x0, y0) = (self.mb_x * 16, self.mb_y * 16); + for r in 0..16 { + self.pic.planes.y[(y0 + r) * stride + x0..(y0 + r) * stride + x0 + 16].copy_from_slice(&samples[r * 16..r * 16 + 16]); + } + let cs = self.pic.planes.cwidth; + let (cx, cy) = (self.mb_x * 8, self.mb_y * 8); + for c in 0..2 { + let src = &samples[256 + c * 64..256 + c * 64 + 64]; + let plane = if c == 0 { &mut self.pic.planes.cb } else { &mut self.pic.planes.cr }; + for r in 0..8 { + plane[(cy + r) * cs + cx..(cy + r) * cs + cx + 8].copy_from_slice(&src[r * 8..r * 8 + 8]); + } + } + } + + /// Common state for an I_PCM MB. + pub fn finish_pcm(&mut self) { + let qpc = [self.chroma_qp(0, 0) as u8, self.chroma_qp(0, 1) as u8]; + let st = self.mb_mut(); + st.kind = MbKind::IPcm; + st.qp = 0; + st.qpc = qpc; + st.nnz = [16; 16]; + st.nnz_c = [[16; 4]; 2]; + st.cbf_dc = 7; + st.cbp = 0x2f; + st.nz_mask = 0xffff; + st.ref_idx = [[-1; 4]; 2]; + } + + /// Update QP after mb_qp_delta. + pub fn apply_qp_delta(&mut self, delta: i32) -> Result<()> { + ensure!((-26..=25).contains(&delta), "mb_qp_delta {delta} out of range"); + self.qp = (self.qp + delta + 52) % 52; + Ok(()) + } + + pub fn store_qp(&mut self) { + let qp = self.qp; + let qpc = [self.chroma_qp(qp, 0) as u8, self.chroma_qp(qp, 1) as u8]; + let st = self.mb_mut(); + st.qp = qp as u8; + st.qpc = qpc; + } + + // ------------------------------------------------------------------------------------------ + // Residual storage helpers (shared by CAVLC and CABAC) + // ------------------------------------------------------------------------------------------ + + fn luma_list4(&self) -> usize { + if self.mb().kind.is_intra() { 0 } else { 3 } + } + + /// Store a 4x4 luma block given in scan order (levels[0..16]); `ac_only` skips position 0. + pub fn put_luma4(&mut self, raster: usize, levels: &[i32; 16], ac_only: bool) { + let qp = self.qp; + let ls = &self.ls.ls4[self.luma_list4()][(qp % 6) as usize]; + let blk = &mut self.s.coef[raster * 16..raster * 16 + 16]; + let mut any = false; + for (k, &lv) in levels.iter().enumerate().skip(ac_only as usize) { + if lv != 0 { + let p = ZIGZAG4[k] as usize; + blk[p] = transform::scale4(lv, ls[p], qp); + any = true; + } + } + if any { + self.s.blk_nz |= 1 << raster; + self.pic.mbs[self.mb_addr].nz_mask |= 1 << raster; + } + } + + /// Store an 8x8 luma block given in scan order. + pub fn put_luma8(&mut self, b8: usize, levels: &[i32; 64]) { + let qp = self.qp; + let list = if self.mb().kind.is_intra() { 0 } else { 1 }; + let ls = &self.ls.ls8[list][(qp % 6) as usize]; + let blk = &mut self.s.coef[b8 * 64..b8 * 64 + 64]; + let mut any = false; + for (k, &lv) in levels.iter().enumerate() { + if lv != 0 { + let p = ZIGZAG8[k] as usize; + blk[p] = transform::scale8(lv, ls[p], qp); + any = true; + } + } + if any { + self.s.blk_nz |= 1 << b8; + let (bx, by) = ((b8 & 1) * 2, (b8 >> 1) * 2); + let m = (0b11u16 << (by * 4 + bx)) | (0b11u16 << ((by + 1) * 4 + bx)); + self.pic.mbs[self.mb_addr].nz_mask |= m; + } + } + + /// Intra16x16 DC levels in scan order -> scaled DC of each 4x4 block. + pub fn put_luma_dc(&mut self, levels: &[i32; 16]) { + let mut c = [0i32; 16]; + for (k, &lv) in levels.iter().enumerate() { + c[ZIGZAG4[k] as usize] = lv; + } + let qp = self.qp; + let ls00 = self.ls.ls4[0][(qp % 6) as usize][0]; + transform::luma_dc_dequant(&mut c, qp, ls00); + for (r, &dc) in c.iter().enumerate() { + if dc != 0 { + self.s.coef[r * 16] = dc; + self.s.blk_nz |= 1 << r; + } + } + } + + /// Chroma DC levels (4:2:0: 4 values in raster order) for component `c`. + pub fn put_chroma_dc(&mut self, c: usize, levels: &[i32; 4]) { + let mut v = *levels; + let qpc = self.chroma_qp(self.qp, c); + let list = if self.mb().kind.is_intra() { 1 + c } else { 4 + c }; + let ls00 = self.ls.ls4[list][(qpc % 6) as usize][0]; + transform::chroma_dc_dequant_420(&mut v, qpc, ls00); + for (b, &dc) in v.iter().enumerate() { + if dc != 0 { + self.s.coef_c[c][b * 16] = dc; + self.s.c_nz[c] |= 1 << b; + } + } + } + + /// Chroma AC levels (scan positions 1..16 in levels[1..]) for block `b` of component `c`. + pub fn put_chroma_ac(&mut self, c: usize, b: usize, levels: &[i32; 16]) { + let qpc = self.chroma_qp(self.qp, c); + let list = if self.mb().kind.is_intra() { 1 + c } else { 4 + c }; + let ls = &self.ls.ls4[list][(qpc % 6) as usize]; + let blk = &mut self.s.coef_c[c][b * 16..b * 16 + 16]; + let mut any = false; + for (k, &lv) in levels.iter().enumerate().skip(1) { + if lv != 0 { + let p = ZIGZAG4[k] as usize; + blk[p] = transform::scale4(lv, ls[p], qpc); + any = true; + } + } + if any { + self.s.c_nz[c] |= 1 << b; + } + } + + // ------------------------------------------------------------------------------------------ + // CAVLC macroblock layer + // ------------------------------------------------------------------------------------------ + + /// Decode a P_Skip or B_Skip macroblock at the current address. + pub fn decode_skip(&mut self) -> Result<()> { + let b = self.sh.slice_type.is_b(); + self.cur = MbCur::default(); + self.cur.info.kind = if b { MbKind::BSkip } else { MbKind::PSkip }; + self.mb_mut().kind = self.cur.info.kind; + self.store_qp(); + if b { + self.derive_direct(0xf)?; + } else { + ensure!(!self.refs[0].is_empty(), "P_Skip with empty reference list"); + self.derive_p_skip(); + } + self.predict_inter(); + self.prev_qp_delta_nz = false; + Ok(()) + } + + fn read_te(r: &mut BitReader, range: u32) -> Result { + Ok(if range > 1 { r.read_ue()? } else { !r.read_bit()? as u32 }) + } + + /// Parse and reconstruct one macroblock_layer() with CAVLC. + pub fn decode_mb_cavlc(&mut self, r: &mut BitReader) -> Result<()> { + let mb_type = r.read_ue()?; + let st = self.sh.slice_type; + let info = match st { + SliceType::I | SliceType::Si => intra_mb_type(mb_type), + SliceType::P | SliceType::Sp => { + if mb_type < 5 { + p_mb_type(mb_type) + } else { + intra_mb_type(mb_type - 5) + } + } + SliceType::B => { + if mb_type < 23 { + b_mb_type(mb_type) + } else { + intra_mb_type(mb_type - 23) + } + } + }; + let Some(mut info) = info else { return invalid(format!("mb_type {mb_type} out of range")) }; + self.cur = MbCur { info, ..Default::default() }; + if info.kind == MbKind::IPcm { + r.byte_align(); + let pos = r.byte_pos(); + let data = r.data(); + ensure!(data.len() >= pos + 384, "truncated I_PCM macroblock"); + let Some(&samples) = data.get(pos..).and_then(|d| d.first_chunk::<384>()) else { + return invalid("truncated I_PCM macroblock"); + }; + r.skip(384 * 8)?; + self.finish_pcm(); + self.write_pcm(&samples); + return Ok(()); + } + let mut transform_8x8 = false; + if info.kind == MbKind::I4x4 && self.pps.transform_8x8_mode && r.read_flag()? { + transform_8x8 = true; + info.kind = MbKind::I8x8; + self.cur.info.kind = MbKind::I8x8; + } + self.mb_mut().kind = info.kind; + self.mb_mut().transform_8x8 = transform_8x8; + let n_ref = [self.sh.num_ref_idx_active[0], self.sh.num_ref_idx_active[1]]; + match info.kind { + MbKind::I4x4 | MbKind::I8x8 => { + let n = if info.kind == MbKind::I8x8 { 4 } else { 16 }; + for i in 0..n { + self.cur.rem_mode[i] = if r.read_flag()? { -1 } else { r.read_bits(3)? as i8 }; + } + self.derive_intra_modes(info.kind == MbKind::I8x8); + let m = r.read_ue()?; + ensure!(m <= 3, "intra_chroma_pred_mode out of range"); + self.mb_mut().intra_chroma_mode = m as u8; + } + MbKind::I16x16 => { + let m = r.read_ue()?; + ensure!(m <= 3, "intra_chroma_pred_mode out of range"); + self.mb_mut().intra_chroma_mode = m as u8; + } + MbKind::BDirect16x16 => {} + _ => { + if info.part == Part::P8x8 { + for p in 0..4 { + let t = r.read_ue()?; + let sub = if st.is_b() { b_sub_mb_type(t) } else { p_sub_mb_type(t) }; + let Some(sub) = sub else { return invalid(format!("sub_mb_type {t} out of range")) }; + self.cur.sub[p] = sub; + } + for l in 0..2 { + for p in 0..4 { + let sub = self.cur.sub[p]; + if sub.direct || sub.pred & (1 << l) == 0 { + continue; + } + self.cur.ref_idx[l][p] = if n_ref[l] > 1 && !info.ref0 { Self::read_te(r, n_ref[l] - 1)? as i8 } else { 0 }; + } + } + for l in 0..2 { + for p in 0..4 { + let sub = self.cur.sub[p]; + if sub.direct || sub.pred & (1 << l) == 0 { + continue; + } + for j in 0..sub.shape.num_parts() { + let x = r.read_se()?; + let y = r.read_se()?; + self.cur.mvd[l][p * 4 + j] = [x as i16, y as i16]; + } + } + } + } else { + let np = info.part.num_parts(); + for l in 0..2 { + for p in 0..np { + if info.pred[p] & (1 << l) != 0 { + self.cur.ref_idx[l][p] = if n_ref[l] > 1 { Self::read_te(r, n_ref[l] - 1)? as i8 } else { 0 }; + } + } + } + for l in 0..2 { + for p in 0..np { + if info.pred[p] & (1 << l) != 0 { + let x = r.read_se()?; + let y = r.read_se()?; + self.cur.mvd[l][p * 4] = [x as i16, y as i16]; + } + } + } + } + } + } + // coded_block_pattern + let cbp = if info.kind == MbKind::I16x16 { + info.i16_cbp + } else { + let code = r.read_ue()?; + ensure!(code < 48, "coded_block_pattern out of range"); + let (i, p) = CBP_ME[code as usize]; + if info.kind.is_intra() { i } else { p } + }; + self.cur.cbp = cbp; + self.mb_mut().cbp = cbp; + if !info.kind.is_intra() && cbp & 15 != 0 && self.pps.transform_8x8_mode && self.no_sub_8x8_lt() { + let t = r.read_flag()?; + self.mb_mut().transform_8x8 = t; + } + if !info.kind.is_intra() { + self.derive_inter_motion()?; + } + if cbp != 0 || info.kind == MbKind::I16x16 { + let d = r.read_se()?; + self.apply_qp_delta(d)?; + } + self.store_qp(); + self.residual_cavlc(r, cbp)?; + if info.kind.is_intra() { + self.reconstruct_intra(); + } else { + self.predict_inter(); + self.reconstruct_inter_residual(); + } + Ok(()) + } + + /// Whether transform_size_8x8_flag may be present for an inter MB (NoSubMbPartSizeLessThan8x8 etc.). + pub fn no_sub_8x8_lt(&self) -> bool { + let info = self.cur.info; + match info.kind { + MbKind::BDirect16x16 => self.sps.direct_8x8_inference, + MbKind::Inter if info.part == Part::P8x8 => { + self.cur.sub.iter().all(|s| if s.direct { self.sps.direct_8x8_inference } else { s.shape == SubPart::S8x8 }) + } + _ => true, + } + } + + fn residual_cavlc(&mut self, r: &mut BitReader, cbp: u8) -> Result<()> { + let kind = self.mb().kind; + let t8 = self.mb().transform_8x8; + if kind == MbKind::I16x16 { + let nc = self.nc_luma(0); + let mut lv = [0i32; 16]; + cavlc::residual_block(r, &mut lv, 0, 15, 16, nc)?; + self.put_luma_dc(&lv); + } + for b8 in 0..4 { + let (bx8, by8) = ((b8 & 1) * 2, (b8 >> 1) * 2); + if cbp & (1 << b8) == 0 { + continue; + } + if t8 { + let mut lv8 = [0i32; 64]; + for i4 in 0..4 { + let raster = (by8 + (i4 >> 1)) * 4 + bx8 + (i4 & 1); + let nc = self.nc_luma(raster); + let mut lv = [0i32; 16]; + let tc = cavlc::residual_block(r, &mut lv, 0, 15, 16, nc)?; + self.mb_mut().nnz[raster] = tc; + for (i, &v) in lv.iter().enumerate() { + lv8[4 * i + i4] = v; + } + } + self.put_luma8(b8, &lv8); + } else { + for i4 in 0..4 { + let raster = (by8 + (i4 >> 1)) * 4 + bx8 + (i4 & 1); + let nc = self.nc_luma(raster); + let mut lv = [0i32; 16]; + let tc = if kind == MbKind::I16x16 { + cavlc::residual_block(r, &mut lv[1..], 0, 14, 15, nc)? + } else { + cavlc::residual_block(r, &mut lv, 0, 15, 16, nc)? + }; + self.mb_mut().nnz[raster] = tc; + self.put_luma4(raster, &lv, kind == MbKind::I16x16); + } + } + } + let cbp_c = cbp >> 4; + if cbp_c & 3 != 0 { + for c in 0..2 { + let mut lv = [0i32; 4]; + cavlc::residual_block(r, &mut lv, 0, 3, 4, -1)?; + self.put_chroma_dc(c, &lv); + } + } + if cbp_c & 2 != 0 { + for c in 0..2 { + for b in 0..4 { + let nc = self.nc_chroma(c, b); + let mut lv = [0i32; 16]; + let tc = cavlc::residual_block(r, &mut lv[1..], 0, 14, 15, nc)?; + self.mb_mut().nnz_c[c][b] = tc; + self.put_chroma_ac(c, b, &lv); + } + } + } + Ok(()) + } + + /// Decode slice_data() with CAVLC. `r` is positioned after the slice header. + pub fn decode_cavlc(&mut self, r: &mut BitReader) -> Result<()> { + let total = self.mb_w * self.mb_h; + let mut addr = self.sh.first_mb_in_slice as usize; + let intra_slice = self.sh.slice_type.is_intra(); + loop { + ensure!(addr < total, "macroblock address {addr} out of range"); + if !intra_slice { + let run = r.read_ue()? as usize; + ensure!(addr + run <= total, "mb_skip_run out of range"); + for _ in 0..run { + self.start_mb(addr); + self.decode_skip()?; + self.pic.mb_done(addr); + addr += 1; + } + if run > 0 && !r.more_rbsp_data() { + break; + } + ensure!(addr < total, "macroblock address {addr} out of range"); + } + self.start_mb(addr); + self.decode_mb_cavlc(r)?; + self.pic.mb_done(addr); + addr += 1; + if !r.more_rbsp_data() { + break; + } + } + Ok(()) + } +} diff --git a/crates/h264/src/synth_tests.rs b/crates/h264/src/synth_tests.rs new file mode 100644 index 0000000..fba3cd5 --- /dev/null +++ b/crates/h264/src/synth_tests.rs @@ -0,0 +1,541 @@ +//! Hand-built bitstreams exercising features libx264 never emits: I_PCM (CAVLC and CABAC), long-term +//! references, MMCO operations, frame_num gaps and reference list modification. Every picture consists +//! of I_PCM and P_Skip macroblocks only, so the expected output is known exactly (I_PCM samples are +//! lossless, P_Skip copies the selected reference with a zero motion vector, and deblocking cannot +//! modify samples at these QPs). + +use crate::Decoder; +use crate::cabac::{Cabac, NEXT_STATE, RANGE_TAB_LPS}; +use slidecraft_bitstream::{BitWriter, escape_rbsp}; + +const MB_W: usize = 4; +const MB_H: usize = 3; +const NMB: usize = MB_W * MB_H; + +fn nal(ref_idc: u8, t: u8, rbsp: &[u8]) -> Vec { + let mut v = vec![0, 0, 0, 1, (ref_idc << 5) | t]; + v.extend(escape_rbsp(rbsp)); + v +} + +/// POC type used by the synthetic SPS: 0 (4-bit lsb), 1 (see below) or 2. +#[derive(Clone, Copy, PartialEq, Eq)] +enum PocType { + T0, + /// offset_for_non_ref_pic = -3, one reference frame per cycle with offset 4 + T1, + T2, +} + +fn sps(profile: u8, max_refs: u32, gaps: bool, poc: PocType) -> Vec { + let mut w = BitWriter::new(); + w.write_bits(profile as u32, 8); + w.write_bits(0, 8); + w.write_bits(30, 8); + w.write_ue(0); // sps id + w.write_ue(0); // log2_max_frame_num - 4 -> 16 + match poc { + PocType::T0 => { + w.write_ue(0); + w.write_ue(0); // log2_max_pic_order_cnt_lsb - 4 -> 4-bit lsb + } + PocType::T1 => { + w.write_ue(1); + w.write_bit(false); // delta_pic_order_always_zero_flag + w.write_se(-3); // offset_for_non_ref_pic + w.write_se(0); // offset_for_top_to_bottom_field + w.write_ue(1); // num_ref_frames_in_pic_order_cnt_cycle + w.write_se(4); + } + PocType::T2 => w.write_ue(2), // output order = decoding order + } + w.write_ue(max_refs); + w.write_bit(gaps); + w.write_ue(MB_W as u32 - 1); + w.write_ue(MB_H as u32 - 1); + w.write_bit(true); // frame_mbs_only + w.write_bit(true); // direct_8x8_inference + w.write_bit(false); // cropping + w.write_bit(false); // vui + w.rbsp_trailing(); + nal(3, 7, &w.finish()) +} + +fn pps(cabac: bool, weighted: bool) -> Vec { + let mut w = BitWriter::new(); + w.write_ue(0); + w.write_ue(0); + w.write_bit(cabac); + w.write_bit(false); + w.write_ue(0); // slice groups + w.write_ue(0); // num_ref_idx_l0_default - 1 + w.write_ue(0); + w.write_bit(weighted); // weighted_pred + w.write_bits(if weighted { 1 } else { 0 }, 2); // explicit weighted bi-prediction + w.write_se(0); // pic_init_qp - 26 + w.write_se(0); + w.write_se(0); // chroma_qp_index_offset + w.write_bit(true); // deblocking_filter_control_present + w.write_bit(false); + w.write_bit(false); + w.rbsp_trailing(); + nal(3, 8, &w.finish()) +} + +#[derive(Default, Clone)] +struct Pic { + idr: bool, + frame_num: u32, + ref_idc: u8, + long_term_ref: bool, + /// ref_pic_list_modification entries (idc, value) + mods: Vec<(u32, u32)>, + /// MMCOs: (op, a, b) with a = difference_of_pic_nums_minus1 / long_term_pic_num / + /// max_long_term_frame_idx_plus1 and b = long_term_frame_idx + mmcos: Vec<(u32, u32, u32)>, + /// Per MB: Some(samples) for I_PCM, None for P_Skip / B_Skip. + mbs: Vec>>, + /// B slice (all B_Skip). + b: bool, + poc_lsb: Option, + /// delta_pic_order_cnt[0] (POC type 1). + poc_delta: Option, + /// Explicit weights (luma, cb, cr) as (weight, offset) for refIdx 0 of list 0 and list 1; + /// log2 denominators are 5 (luma) and 3 (chroma). + wt: Option<[[(i32, i32); 3]; 2]>, +} + +impl Pic { + fn is_p(&self) -> bool { + !self.idr && !self.b && self.mbs.iter().any(|m| m.is_none()) + } + fn inter(&self) -> bool { + self.b || self.is_p() + } +} + +fn slice_header(w: &mut BitWriter, p: &Pic, cabac: bool) { + w.write_ue(0); // first_mb + w.write_ue(if p.b { + 6 + } else if p.is_p() { + 5 + } else { + 7 + }); + w.write_ue(0); // pps id + w.write_bits(p.frame_num, 4); + if p.idr { + w.write_ue(0); + } + if let Some(lsb) = p.poc_lsb { + w.write_bits(lsb, 4); + } + if let Some(d) = p.poc_delta { + w.write_se(d); + } + if p.b { + w.write_bit(true); // direct_spatial_mv_pred_flag + } + if p.inter() { + w.write_bit(false); // num_ref_idx_override + w.write_bit(!p.mods.is_empty()); + if !p.mods.is_empty() { + for &(idc, v) in &p.mods { + w.write_ue(idc); + w.write_ue(v); + } + w.write_ue(3); + } + if p.b { + w.write_bit(false); // no list 1 modification + } + } + if let (Some(wt), true) = (p.wt, p.inter()) { + w.write_ue(5); + w.write_ue(3); + for l in 0..if p.b { 2 } else { 1 } { + w.write_bit(true); + w.write_se(wt[l][0].0); + w.write_se(wt[l][0].1); + w.write_bit(true); + for c in 1..3 { + w.write_se(wt[l][c].0); + w.write_se(wt[l][c].1); + } + } + } + if p.ref_idc != 0 { + if p.idr { + w.write_bit(false); + w.write_bit(p.long_term_ref); + } else { + w.write_bit(!p.mmcos.is_empty()); + if !p.mmcos.is_empty() { + for &(op, a, b) in &p.mmcos { + w.write_ue(op); + match op { + 1 => w.write_ue(a), + 2 => w.write_ue(a), + 3 => { + w.write_ue(a); + w.write_ue(b); + } + 4 => w.write_ue(a), + 6 => w.write_ue(b), + _ => {} + } + } + w.write_ue(0); + } + } + } + if cabac && p.inter() { + w.write_ue(0); // cabac_init_idc + } + w.write_se(0); // slice_qp_delta + w.write_ue(0); // disable_deblocking_filter_idc + w.write_se(0); + w.write_se(0); +} + +/// Minimal CABAC encoder (9.3.4, informative) writing into a BitWriter. +struct Enc { + low: u32, + range: u32, + outstanding: u32, + first: bool, + ctx: [u8; 1024], +} + +impl Enc { + fn new(qp: i32, init: usize) -> Self { + let c = Cabac::new(&[0, 0, 0, 0], 0, qp, init).unwrap(); + Enc { low: 0, range: 510, outstanding: 0, first: true, ctx: c.ctx } + } + fn init(&mut self) { + self.low = 0; + self.range = 510; + self.outstanding = 0; + self.first = true; + } + fn put(&mut self, w: &mut BitWriter, b: bool) { + if self.first { + self.first = false; + } else { + w.write_bit(b); + } + while self.outstanding > 0 { + w.write_bit(!b); + self.outstanding -= 1; + } + } + fn renorm(&mut self, w: &mut BitWriter) { + while self.range < 256 { + if self.low < 256 { + self.put(w, false); + } else if self.low >= 512 { + self.low -= 512; + self.put(w, true); + } else { + self.low -= 256; + self.outstanding += 1; + } + self.range <<= 1; + self.low <<= 1; + } + } + fn encode(&mut self, w: &mut BitWriter, ctx_idx: usize, bin: u32) { + let s = self.ctx[ctx_idx] as usize; + let lps = RANGE_TAB_LPS[s >> 1][((self.range >> 6) & 3) as usize] as u32; + self.range -= lps; + if bin != (s & 1) as u32 { + 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(w); + } + fn terminate(&mut self, w: &mut BitWriter, bin: bool) { + self.range -= 2; + if bin { + self.low += self.range; + // EncodeFlush + self.range = 2; + self.renorm(w); + self.put(w, (self.low >> 9) & 1 != 0); + w.write_bits(((self.low >> 7) & 3) | 1, 2); + } else { + self.renorm(w); + } + } +} + +fn write_pcm(w: &mut BitWriter, samples: &[u8]) { + w.align_zero(); + w.write_bytes(samples); +} + +fn slice_nal(p: &Pic, cabac: bool) -> Vec { + let mut w = BitWriter::new(); + slice_header(&mut w, p, cabac); + if !cabac && p.b { + w.write_ue(NMB as u32); + w.rbsp_trailing(); + } else if !cabac { + let mut run = 0; + for mb in &p.mbs { + match mb { + None => run += 1, + Some(s) => { + if p.is_p() { + w.write_ue(run); + run = 0; + w.write_ue(30); // I_PCM in a P slice + } else { + w.write_ue(25); + } + write_pcm(&mut w, s); + } + } + } + if run > 0 { + w.write_ue(run); + } + w.rbsp_trailing(); + } else { + while !w.is_byte_aligned() { + w.write_bit(true); // cabac_alignment_one_bit + } + let mut e = Enc::new(26, if p.inter() { 0 } else { 3 }); + let skip = |i: usize| p.mbs[i].is_none(); + for (i, mb) in p.mbs.iter().enumerate() { + let (x, y) = (i % MB_W, i / MB_W); + let a = if x > 0 { Some(i - 1) } else { None }; + let b = if y > 0 { Some(i - MB_W) } else { None }; + if p.inter() { + let inc = a.map(|n| !skip(n) as usize).unwrap_or(0) + b.map(|n| !skip(n) as usize).unwrap_or(0); + e.encode(&mut w, if p.b { 24 } else { 11 } + inc, skip(i) as u32); + } + if let Some(s) = mb { + if p.is_p() { + e.encode(&mut w, 14, 1); // intra prefix + e.encode(&mut w, 17, 1); // not I_NxN + } else { + // neighbours are I_PCM (not I_NxN) -> condTerm 1 + let inc = a.is_some() as usize + b.is_some() as usize; + e.encode(&mut w, 3 + inc, 1); + } + e.terminate(&mut w, true); // I_PCM + write_pcm(&mut w, s); + e.init(); + } + e.terminate(&mut w, i + 1 == NMB); // end_of_slice_flag + } + w.align_zero(); + } + let t = if p.idr { 5 } else { 1 }; + nal(p.ref_idc, t, &w.finish()) +} + +fn random_mb(seed: &mut u32) -> Vec { + (0..384) + .map(|_| { + *seed = seed.wrapping_mul(1664525).wrapping_add(1013904223); + (*seed >> 24) as u8 + }) + .collect() +} + +/// Expected cropped planes of a picture made of PCM blocks (`None` = copy from `reference`). +fn render(mbs: &[Option>], reference: Option<&(Vec, Vec, Vec)>) -> (Vec, Vec, Vec) { + let (w, h) = (MB_W * 16, MB_H * 16); + let (mut y, mut u, mut v) = match reference { + Some(r) => r.clone(), + None => (vec![0; w * h], vec![0; w * h / 4], vec![0; w * h / 4]), + }; + for (i, mb) in mbs.iter().enumerate() { + let Some(s) = mb else { continue }; + let (x0, y0) = ((i % MB_W) * 16, (i / MB_W) * 16); + for r in 0..16 { + y[(y0 + r) * w + x0..(y0 + r) * w + x0 + 16].copy_from_slice(&s[r * 16..r * 16 + 16]); + } + for r in 0..8 { + let o = (y0 / 2 + r) * (w / 2) + x0 / 2; + u[o..o + 8].copy_from_slice(&s[256 + r * 8..256 + r * 8 + 8]); + v[o..o + 8].copy_from_slice(&s[320 + r * 8..320 + r * 8 + 8]); + } + } + (y, u, v) +} + +fn build_and_check(cabac: bool, threads: usize) { + let mut seed = 99u32; + let pcm_pic = |seed: &mut u32| (0..NMB).map(|_| Some(random_mb(seed))).collect::>(); + let a = pcm_pic(&mut seed); + let b = pcm_pic(&mut seed); + let mixed: Vec>> = (0..NMB).map(|i| if i % 3 == 1 { Some(random_mb(&mut seed)) } else { None }).collect(); + let skip = vec![None; NMB]; + let pics = vec![ + // 0: IDR, all PCM, marked long-term (LongTermFrameIdx 0) + Pic { idr: true, frame_num: 0, ref_idc: 3, long_term_ref: true, mbs: a.clone(), ..Default::default() }, + // 1: all PCM, short-term + Pic { frame_num: 1, ref_idc: 2, mbs: b.clone(), ..Default::default() }, + // 2: P_Skip from the long-term picture via modification idc 2 (default list would use pic 1) + Pic { frame_num: 2, ref_idc: 2, mods: vec![(2, 0)], mbs: skip.clone(), ..Default::default() }, + // 3: frame_num gap (3, 4 missing); P_Skip from pic 2 (PicNum 2) via idc 0: 5 - (2 + 1) = 2 + Pic { frame_num: 5, ref_idc: 2, mods: vec![(0, 2)], mbs: skip.clone(), ..Default::default() }, + // 4: mixed PCM / P_Skip from pic 1 (PicNum 1: 6 - 5); MMCO 4 raises MaxLongTermFrameIdx to 2, + // MMCO 2 drops long-term 0, MMCO 3 makes pic 1 (difference 6 - 1 - 1 = 4) long-term index 1 + Pic { frame_num: 6, ref_idc: 2, mods: vec![(0, 4)], mmcos: vec![(4, 3, 0), (2, 0, 0), (3, 4, 1)], mbs: mixed.clone(), ..Default::default() }, + // 5: non-reference P_Skip from long-term index 1 (= pic 1) + Pic { frame_num: 7, ref_idc: 0, mods: vec![(2, 1)], mbs: skip.clone(), ..Default::default() }, + // 6: P_Skip with the default list: short-term by descending PicNum -> pic 4 first + Pic { frame_num: 7, ref_idc: 2, mmcos: vec![(1, 0, 0), (6, 0, 2)], mbs: skip.clone(), ..Default::default() }, + // 7: MMCO 5 picture made of PCM, then 8: P_Skip referencing it (only reference left) + Pic { frame_num: 8, ref_idc: 2, mmcos: vec![(5, 0, 0)], mbs: pcm_pic(&mut seed), ..Default::default() }, + ]; + let mut stream = sps(if cabac { 77 } else { 66 }, 8, true, PocType::T2); + stream.extend(pps(cabac, false)); + for p in &pics { + stream.extend(slice_nal(p, cabac)); + } + let after5 = Pic { frame_num: 1, ref_idc: 2, mbs: skip.clone(), ..Default::default() }; + stream.extend(slice_nal(&after5, cabac)); + + // expected output + let pa = render(&a, None); + let pb = render(&b, None); + let p2 = pa.clone(); + let p3 = p2.clone(); + let p4 = render(&mixed, Some(&pb)); + let p5 = pb.clone(); + let p6 = p4.clone(); + let p7 = render(&pics[7].mbs, None); + let p8 = p7.clone(); + let expected = [pa, pb, p2, p3, p4, p5, p6, p7, p8]; + + if threads == 1 { + cross_check_ffmpeg(&stream, &expected, if cabac { "synth_cabac" } else { "synth_cavlc" }); + } + let mut dec = Decoder::with_threads(threads); + let mut out = dec.decode(&stream, 0).unwrap(); + out.extend(dec.flush()); + assert!(dec.take_error().is_none()); + assert_eq!(out.len(), expected.len(), "cabac={cabac}"); + for (i, (p, e)) in out.iter().zip(expected.iter()).enumerate() { + assert_eq!((p.width as usize, p.height as usize), (MB_W * 16, MB_H * 16)); + assert!(p.y == e.0 && p.u == e.1 && p.v == e.2, "picture {i} differs (cabac={cabac}, threads={threads})"); + } + let s = dec.stats(); + assert!(s.mb_pcm > 0 && s.frame_num_gaps == 1 && s.long_term_marks >= 3, "{s:?}"); +} + +type Planes3 = (Vec, Vec, Vec); + +/// Validate the hand-built stream and its expected output with ffmpeg when available. +fn cross_check_ffmpeg(stream: &[u8], expected: &[Planes3], name: &str) { + // The ffmpeg cross-check lives in FilmCraft (test oracle); this copy checks the expected planes only. + let _ = (stream, expected, name); +} + +#[test] +fn pcm_long_term_mmco_gaps_cavlc() { + build_and_check(false, 1); + build_and_check(false, 4); +} + +#[test] +fn pcm_long_term_mmco_gaps_cabac() { + build_and_check(true, 1); + build_and_check(true, 4); +} + +fn weighted_check(cabac: bool, threads: usize) { + let mut seed = 7u32; + let a: Vec>> = (0..NMB).map(|_| Some(random_mb(&mut seed))).collect(); + let b: Vec>> = (0..NMB).map(|_| Some(random_mb(&mut seed))).collect(); + let skip = vec![None; NMB]; + let wp = [[(20, 3), (5, -1), (12, 2)], [(0, 0); 3]]; + let wb = [[(40, -2), (4, 1), (9, -3)], [(30, 7), (6, 0), (2, 4)]]; + // decoding order: I (POC 0), P (POC 8), B (POC 4, both refs), non-reference weighted P_Skip (POC 12) + let pics = [ + Pic { idr: true, frame_num: 0, ref_idc: 3, poc_lsb: Some(0), mbs: a.clone(), ..Default::default() }, + Pic { frame_num: 1, ref_idc: 2, poc_lsb: Some(8), mbs: b.clone(), wt: Some(wp), ..Default::default() }, + Pic { frame_num: 2, ref_idc: 0, poc_lsb: Some(4), b: true, mbs: skip.clone(), wt: Some(wb), ..Default::default() }, + Pic { frame_num: 2, ref_idc: 0, poc_lsb: Some(12), mbs: skip.clone(), wt: Some(wp), ..Default::default() }, + ]; + let mut stream = sps(77, 2, false, PocType::T0); + stream.extend(pps(cabac, true)); + for p in &pics { + stream.extend(slice_nal(p, cabac)); + } + let pa = render(&a, None); + let pb = render(&b, None); + let clip = |v: i32| v.clamp(0, 255) as u8; + let mix = |x: &[u8], y: &[u8], c: usize| -> Vec { + let (w0, o0) = wb[0][c]; + let (w1, o1) = wb[1][c]; + let d = if c == 0 { 5 } else { 3 }; + x.iter().zip(y).map(|(&p, &q)| clip(((p as i32 * w0 + q as i32 * w1 + (1 << d)) >> (d + 1)) + ((o0 + o1 + 1) >> 1))).collect() + }; + let single = |x: &[u8], c: usize| -> Vec { + let (w, o) = wp[0][c]; + let d = if c == 0 { 5 } else { 3 }; + x.iter().map(|&p| clip(((p as i32 * w + (1 << (d - 1))) >> d) + o)).collect() + }; + let pbi = (mix(&pa.0, &pb.0, 0), mix(&pa.1, &pb.1, 1), mix(&pa.2, &pb.2, 2)); + let pp = (single(&pb.0, 0), single(&pb.1, 1), single(&pb.2, 2)); + let expected = [pa, pbi, pb, pp]; + if threads == 1 { + cross_check_ffmpeg(&stream, &expected, if cabac { "synth_wp_cabac" } else { "synth_wp_cavlc" }); + } + let mut dec = Decoder::with_threads(threads); + let mut out = dec.decode(&stream, 0).unwrap(); + out.extend(dec.flush()); + assert!(dec.take_error().is_none()); + assert_eq!(out.len(), 4); + for (i, (p, e)) in out.iter().zip(expected.iter()).enumerate() { + assert!(p.y == e.0 && p.u == e.1 && p.v == e.2, "picture {i} differs (cabac={cabac}, threads={threads})"); + } +} + +#[test] +fn explicit_weighted_prediction_p_and_b_skip() { + for cabac in [false, true] { + weighted_check(cabac, 1); + weighted_check(cabac, 3); + } +} + +/// POC type 1: non-reference pictures are displayed before the preceding reference picture. +#[test] +fn poc_type1_reordering() { + for cabac in [false, true] { + let mut seed = 5u32; + let contents: Vec>>> = (0..5).map(|_| (0..NMB).map(|_| Some(random_mb(&mut seed))).collect()).collect(); + // (frame_num, ref_idc): POCs 0, 4, 1, 8, 5 + let spec = [(0, 3), (1, 2), (2, 0), (2, 2), (3, 0)]; + let mut stream = sps(77, 2, false, PocType::T1); + stream.extend(pps(cabac, false)); + for (i, &(frame_num, ref_idc)) in spec.iter().enumerate() { + let p = Pic { idr: i == 0, frame_num, ref_idc, poc_delta: Some(0), mbs: contents[i].clone(), ..Default::default() }; + stream.extend(slice_nal(&p, cabac)); + } + let expected_order = [0usize, 2, 1, 4, 3]; + let expected_poc = [0, 1, 4, 5, 8]; + for threads in [1, 2] { + let mut dec = Decoder::with_threads(threads); + let mut out = dec.decode(&stream, 0).unwrap(); + out.extend(dec.flush()); + assert_eq!(out.len(), 5); + for (k, p) in out.iter().enumerate() { + let e = render(&contents[expected_order[k]], None); + assert_eq!(p.poc, expected_poc[k]); + assert!(p.y == e.0 && p.u == e.1 && p.v == e.2, "output {k} (cabac={cabac})"); + } + } + } +} diff --git a/crates/h264/src/tables.rs b/crates/h264/src/tables.rs new file mode 100644 index 0000000..d6e0d58 --- /dev/null +++ b/crates/h264/src/tables.rs @@ -0,0 +1,175 @@ +//! Small constant tables from the H.264 specification. + +/// 4x4 frame zig-zag scan: scan position -> raster index (row * 4 + col). +pub const ZIGZAG4: [u8; 16] = [0, 1, 4, 8, 5, 2, 3, 6, 9, 12, 13, 10, 7, 11, 14, 15]; + +/// 8x8 frame zig-zag scan: scan position -> raster index (row * 8 + col). +pub const ZIGZAG8: [u8; 64] = zigzag8(); + +const fn zigzag8() -> [u8; 64] { + let mut out = [0u8; 64]; + let mut k = 0; + let mut d: i32 = 0; // anti-diagonal index = row + col + while d < 15 { + // even diagonals go up-right (row decreasing), odd go down-left + let mut i: i32 = 0; + while i < 8 { + let (row, col) = if d % 2 == 0 { (d - i, i) } else { (i, d - i) }; + if row >= 0 && row < 8 && col >= 0 && col < 8 { + out[k] = (row * 8 + col) as u8; + k += 1; + } + i += 1; + } + d += 1; + } + out +} + +/// Table 7-3. +pub const DEFAULT_4X4_INTRA: [u8; 16] = [6, 13, 13, 20, 20, 20, 28, 28, 28, 28, 32, 32, 32, 37, 37, 42]; +pub const DEFAULT_4X4_INTER: [u8; 16] = [10, 14, 14, 20, 20, 20, 24, 24, 24, 24, 27, 27, 27, 30, 30, 34]; +/// Table 7-4. +#[rustfmt::skip] +pub const DEFAULT_8X8_INTRA: [u8; 64] = [ + 6, 10, 10, 13, 11, 13, 16, 16, 16, 16, 18, 18, 18, 18, 18, 23, + 23, 23, 23, 23, 23, 25, 25, 25, 25, 25, 25, 25, 27, 27, 27, 27, + 27, 27, 27, 27, 29, 29, 29, 29, 29, 29, 29, 31, 31, 31, 31, 31, + 31, 33, 33, 33, 33, 33, 36, 36, 36, 36, 38, 38, 38, 40, 40, 42, +]; +#[rustfmt::skip] +pub const DEFAULT_8X8_INTER: [u8; 64] = [ + 9, 13, 13, 15, 13, 15, 17, 17, 17, 17, 19, 19, 19, 19, 19, 21, + 21, 21, 21, 21, 21, 22, 22, 22, 22, 22, 22, 22, 24, 24, 24, 24, + 24, 24, 24, 24, 25, 25, 25, 25, 25, 25, 25, 27, 27, 27, 27, 27, + 27, 28, 28, 28, 28, 28, 30, 30, 30, 30, 32, 32, 32, 33, 33, 35, +]; + +/// Table 8-15: QPc as a function of qPI (0..=51). +pub const QPC_TABLE: [u8; 52] = { + let mut t = [0u8; 52]; + let tail = [29u8, 30, 31, 32, 32, 33, 34, 34, 35, 35, 36, 36, 37, 37, 37, 38, 38, 38, 39, 39, 39, 39]; + let mut i = 0; + while i < 52 { + t[i] = if i < 30 { i as u8 } else { tail[i - 30] }; + i += 1; + } + t +}; + +/// (8-315) v matrix for normAdjust4x4. +pub const NORM_ADJUST4: [[u16; 3]; 6] = [[10, 16, 13], [11, 18, 14], [13, 20, 16], [14, 23, 18], [16, 25, 20], [18, 29, 23]]; +/// (8-318) v matrix for normAdjust8x8. +pub const NORM_ADJUST8: [[u16; 6]; 6] = [ + [20, 18, 32, 19, 25, 24], + [22, 19, 35, 21, 28, 26], + [26, 23, 42, 24, 33, 31], + [28, 25, 45, 26, 35, 33], + [32, 28, 51, 30, 40, 38], + [36, 32, 58, 34, 46, 43], +]; + +/// normAdjust4x4(m, i, j) for raster position `pos` (i = row, j = col). +pub const fn norm_adjust4(m: usize, pos: usize) -> u16 { + let (i, j) = (pos / 4, pos % 4); + if i % 2 == 0 && j % 2 == 0 { + NORM_ADJUST4[m][0] + } else if i % 2 == 1 && j % 2 == 1 { + NORM_ADJUST4[m][1] + } else { + NORM_ADJUST4[m][2] + } +} + +/// normAdjust8x8(m, i, j) for raster position `pos` (i = row, j = col). +pub const fn norm_adjust8(m: usize, pos: usize) -> u16 { + let (i, j) = (pos / 8, pos % 8); + let k = if i % 4 == 0 && j % 4 == 0 { + 0 + } else if i % 2 == 1 && j % 2 == 1 { + 1 + } else if i % 4 == 2 && j % 4 == 2 { + 2 + } else if (i % 4 == 0 && j % 2 == 1) || (i % 2 == 1 && j % 4 == 0) { + 3 + } else if (i % 4 == 0 && j % 4 == 2) || (i % 4 == 2 && j % 4 == 0) { + 4 + } else { + 5 + }; + NORM_ADJUST8[m][k] +} + +/// Table 8-16 alpha' (indexA 0..=51). +#[rustfmt::skip] +pub const ALPHA: [u8; 52] = [ + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 4, 4, 5, 6, 7, 8, 9, 10, 12, 13, + 15, 17, 20, 22, 25, 28, 32, 36, 40, 45, 50, 56, 63, 71, 80, 90, 101, 113, 127, 144, 162, 182, 203, 226, 255, 255, +]; +/// Table 8-16 beta' (indexB 0..=51). +#[rustfmt::skip] +pub const BETA: [u8; 52] = [ + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 2, 2, 2, 3, 3, 3, 3, 4, 4, 4, + 6, 6, 7, 7, 8, 8, 9, 9, 10, 10, 11, 11, 12, 12, 13, 13, 14, 14, 15, 15, 16, 16, 17, 17, 18, 18, +]; +/// Table 8-17 tC0' for bS = 1, 2, 3 (indexA 0..=51). +#[rustfmt::skip] +pub const TC0: [[u8; 3]; 52] = { + let b1: [u8; 52] = [ + 0, 0, 0, 0, 0, 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, 1, 2, 2, 2, 2, 3, 3, 3, 4, 4, 4, 5, 6, 6, 7, 8, 9, 10, 11, 13, + ]; + let b2: [u8; 52] = [ + 0, 0, 0, 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, 1, 2, 2, 2, 2, 3, 3, 3, 4, 4, 5, 5, 6, 7, 8, 8, 10, 11, 12, 13, 15, 17, + ]; + let b3: [u8; 52] = [ + 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, + 1, 2, 2, 2, 2, 3, 3, 3, 4, 4, 4, 5, 6, 6, 7, 8, 9, 10, 11, 13, 14, 16, 18, 20, 23, 25, + ]; + let mut t = [[0u8; 3]; 52]; + let mut i = 0; + while i < 52 { + t[i] = [b1[i], b2[i], b3[i]]; + i += 1; + } + t +}; + +/// Luma 4x4 block index (decoding order, 6.4.3) -> (x, y) in 4-sample units. +pub const BLK4_XY: [(u8, u8); 16] = + [(0, 0), (1, 0), (0, 1), (1, 1), (2, 0), (3, 0), (2, 1), (3, 1), (0, 2), (1, 2), (0, 3), (1, 3), (2, 2), (3, 2), (2, 3), (3, 3)]; + +/// Raster 4x4 position (y * 4 + x) -> luma4x4BlkIdx. +pub const RASTER_TO_BLK4: [u8; 16] = [0, 1, 4, 5, 2, 3, 6, 7, 8, 9, 12, 13, 10, 11, 14, 15]; + +#[cfg(test)] +mod tests { + use super::*; + + #[test] + fn zigzag8_known_prefix() { + assert_eq!(&ZIGZAG8[..16], &[0, 1, 8, 16, 9, 2, 3, 10, 17, 24, 32, 25, 18, 11, 4, 5]); + assert_eq!(&ZIGZAG8[48..], &[58, 59, 52, 45, 38, 31, 39, 46, 53, 60, 61, 54, 47, 55, 62, 63]); + let mut seen = [false; 64]; + for &z in &ZIGZAG8 { + seen[z as usize] = true; + } + assert!(seen.iter().all(|&s| s)); + } + + #[test] + fn blk4_maps_consistent() { + for (b, &(x, y)) in BLK4_XY.iter().enumerate() { + let r = (y * 4 + x) as usize; + assert_eq!(RASTER_TO_BLK4[r] as usize, b); + } + } + + #[test] + fn qpc() { + assert_eq!(QPC_TABLE[29], 29); + assert_eq!(QPC_TABLE[30], 29); + assert_eq!(QPC_TABLE[51], 39); + } +} diff --git a/crates/h264/src/transform.rs b/crates/h264/src/transform.rs new file mode 100644 index 0000000..f87a1c9 --- /dev/null +++ b/crates/h264/src/transform.rs @@ -0,0 +1,307 @@ +//! Scaling (dequantisation) and inverse transforms (8.5.10 - 8.5.13). + +use crate::params::ScalingMatrices; +use crate::tables::{norm_adjust4, norm_adjust8}; + +/// LevelScale4x4 / LevelScale8x8 for all six scaling lists and qP % 6, raster order. +#[derive(Clone)] +pub struct LevelScale { + /// [list 0..6 (IntraY, IntraCb, IntraCr, InterY, InterCb, InterCr)][qp % 6][pos] + pub ls4: [[[i32; 16]; 6]; 6], + /// [list 0..6 (IntraY, InterY, IntraCb, InterCb, IntraCr, InterCr)][qp % 6][pos] + pub ls8: [[[i32; 64]; 6]; 6], +} + +impl LevelScale { + pub fn new(m: &ScalingMatrices) -> Box { + let mut s = Box::new(LevelScale { ls4: [[[0; 16]; 6]; 6], ls8: [[[0; 64]; 6]; 6] }); + for l in 0..6 { + for q in 0..6 { + for p in 0..16 { + s.ls4[l][q][p] = m.m4[l][p] as i32 * norm_adjust4(q, p) as i32; + } + for p in 0..64 { + s.ls8[l][q][p] = m.m8[l][p] as i32 * norm_adjust8(q, p) as i32; + } + } + } + s + } +} + +/// Scale one 4x4 AC coefficient (8.5.12.1, not the DC of Intra16x16 / chroma blocks). +#[inline(always)] +pub fn scale4(c: i32, ls: i32, qp: i32) -> i32 { + if qp >= 24 { c.wrapping_mul(ls) << (qp / 6 - 4) } else { c.wrapping_mul(ls).wrapping_add(1 << (3 - qp / 6)) >> (4 - qp / 6) } +} + +/// Scale one 8x8 coefficient (8.5.13.1). +#[inline(always)] +pub fn scale8(c: i32, ls: i32, qp: i32) -> i32 { + if qp >= 36 { c.wrapping_mul(ls) << (qp / 6 - 6) } else { c.wrapping_mul(ls).wrapping_add(1 << (5 - qp / 6)) >> (6 - qp / 6) } +} + +/// Intra16x16 luma DC: inverse Hadamard + scaling (8.5.10). `c` is raster order (row*4+col); +/// output replaces `c` with dcY (raster: row i -> 4x4 block row, col j -> block column). +pub fn luma_dc_dequant(c: &mut [i32; 16], qp: i32, ls00: i32) { + let mut t = [0i32; 16]; + // rows + for i in 0..4 { + let r = &c[i * 4..i * 4 + 4]; + let s01 = r[0] + r[1]; + let d01 = r[0] - r[1]; + let s23 = r[2] + r[3]; + let d23 = r[2] - r[3]; + t[i * 4] = s01 + s23; + t[i * 4 + 1] = s01 - s23; + t[i * 4 + 2] = d01 - d23; + t[i * 4 + 3] = d01 + d23; + } + // columns + for j in 0..4 { + let s01 = t[j] + t[4 + j]; + let d01 = t[j] - t[4 + j]; + let s23 = t[8 + j] + t[12 + j]; + let d23 = t[8 + j] - t[12 + j]; + let f = [s01 + s23, s01 - s23, d01 - d23, d01 + d23]; + for i in 0..4 { + c[i * 4 + j] = if qp >= 36 { + f[i].wrapping_mul(ls00) << (qp / 6 - 6) + } else { + f[i].wrapping_mul(ls00).wrapping_add(1 << (5 - qp / 6)) >> (6 - qp / 6) + }; + } + } +} + +/// 4:2:0 chroma DC: 2x2 transform + scaling (8.5.11). `c` = [c00, c01, c10, c11]. +pub fn chroma_dc_dequant_420(c: &mut [i32; 4], qp: i32, ls00: i32) { + let s0 = c[0].wrapping_add(c[1]); + let d0 = c[0].wrapping_sub(c[1]); + let s1 = c[2].wrapping_add(c[3]); + let d1 = c[2].wrapping_sub(c[3]); + let f = [s0.wrapping_add(s1), d0.wrapping_add(d1), s0.wrapping_sub(s1), d0.wrapping_sub(d1)]; + for k in 0..4 { + c[k] = (f[k].wrapping_mul(ls00) << (qp / 6)) >> 5; + } +} + +#[inline(always)] +fn clip_u8(v: i32) -> u8 { + v.clamp(0, 255) as u8 +} + +/// Inverse 4x4 transform of raster-order `d` and add to `dst` (8.5.12.2, 8.5.14). +/// +/// Rows first, then columns (the spec's order: the `>> 1` steps make it matter). The column +/// pass and the reconstruction run across all columns at once so they vectorise. +pub fn idct4_add(d: &[i32; 16], dst: &mut [u8], stride: usize) { + let mut t = [[0i32; 4]; 4]; + for (i, row) in t.iter_mut().enumerate() { + let r = &d[i * 4..i * 4 + 4]; + let e = r[0] + r[2]; + let f = r[0] - r[2]; + let g = (r[1] >> 1) - r[3]; + let h = r[1] + (r[3] >> 1); + *row = [e + h, f + g, f - g, e - h]; + } + let mut o = [[0i32; 4]; 4]; + for j in 0..4 { + let e = t[0][j] + t[2][j]; + let f = t[0][j] - t[2][j]; + let g = (t[1][j] >> 1) - t[3][j]; + let h = t[1][j] + (t[3][j] >> 1); + o[0][j] = e + h; + o[1][j] = f + g; + o[2][j] = f - g; + o[3][j] = e - h; + } + for (i, row) in o.iter().enumerate() { + let Some(line) = dst.get_mut(i * stride..).and_then(|d| d.first_chunk_mut::<4>()) else { + return; + }; + for (p, &v) in line.iter_mut().zip(row) { + *p = clip_u8(*p as i32 + ((v + 32) >> 6)); + } + } +} + +/// DC-only shortcut: all AC coefficients zero. +#[allow(dead_code)] +pub fn idct4_dc_add(dc: i32, dst: &mut [u8], stride: usize) { + let v = (dc + 32) >> 6; + for i in 0..4 { + for p in &mut dst[i * stride..i * stride + 4] { + *p = clip_u8(*p as i32 + v); + } + } +} + +#[inline(always)] +fn idct8_1d(d: [i32; 8]) -> [i32; 8] { + let a0 = d[0] + d[4]; + let a4 = d[0] - d[4]; + let a2 = (d[2] >> 1) - d[6]; + let a6 = d[2] + (d[6] >> 1); + let b0 = a0 + a6; + let b2 = a4 + a2; + let b4 = a4 - a2; + let b6 = a0 - a6; + let a1 = -d[3] + d[5] - d[7] - (d[7] >> 1); + let a3 = d[1] + d[7] - d[3] - (d[3] >> 1); + let a5 = -d[1] + d[7] + d[5] + (d[5] >> 1); + let a7 = d[3] + d[5] + d[1] + (d[1] >> 1); + let b1 = a1 + (a7 >> 2); + let b7 = a7 - (a1 >> 2); + let b3 = a3 + (a5 >> 2); + let b5 = (a3 >> 2) - a5; + [b0 + b7, b2 + b5, b4 + b3, b6 + b1, b6 - b1, b4 - b3, b2 - b5, b0 - b7] +} + +/// Inverse 8x8 transform of raster-order `d` and add to `dst` (8.5.13.2): rows, then columns, +/// the column pass and reconstruction across all columns at once (they vectorise). +pub fn idct8_add(d: &[i32; 64], dst: &mut [u8], stride: usize) { + let mut t = [[0i32; 8]; 8]; + for (row, src) in t.iter_mut().zip(d.as_chunks::<8>().0) { + *row = idct8_1d(*src); + } + let mut o = [[0i32; 8]; 8]; + for j in 0..8 { + let col = idct8_1d(std::array::from_fn(|i| t[i][j])); + for i in 0..8 { + o[i][j] = col[i]; + } + } + for (i, row) in o.iter().enumerate() { + let Some(line) = dst.get_mut(i * stride..).and_then(|d| d.first_chunk_mut::<8>()) else { + return; + }; + for (p, &v) in line.iter_mut().zip(row) { + *p = clip_u8(*p as i32 + ((v + 32) >> 6)); + } + } +} + +#[cfg(test)] +mod tests { + use super::*; + + /// The straightforward per-column formulation (8.5.12.2 / 8.5.13.2) the vectorised + /// transforms must equal. + fn reference_add(d: &[i32], n: usize, dst: &mut [u8], stride: usize) { + let one = |v: &[i32]| -> Vec { + if n == 4 { + let (e, f, g, h) = (v[0] + v[2], v[0] - v[2], (v[1] >> 1) - v[3], v[1] + (v[3] >> 1)); + vec![e + h, f + g, f - g, e - h] + } else { + idct8_1d(v.try_into().unwrap()).to_vec() + } + }; + let rows: Vec> = (0..n).map(|i| one(&d[i * n..i * n + n])).collect(); + for j in 0..n { + let col = one(&(0..n).map(|i| rows[i][j]).collect::>()); + for i in 0..n { + let p = &mut dst[i * stride + j]; + *p = (*p as i32 + ((col[i] + 32) >> 6)).clamp(0, 255) as u8; + } + } + } + + #[test] + fn vectorised_transforms_match_reference() { + let mut rng = 0x1234_5678_9abc_def1u64; + let mut next = move || { + rng ^= rng << 13; + rng ^= rng >> 7; + rng ^= rng << 17; + rng + }; + for iter in 0..5000 { + let range = [64u64, 1024, 8192, 1 << 15][iter % 4]; + let d4: [i32; 16] = std::array::from_fn(|_| (next() % (2 * range + 1)) as i32 - range as i32); + let d8: [i32; 64] = std::array::from_fn(|_| (next() % (2 * range + 1)) as i32 - range as i32); + let base: Vec = (0..24 * 8).map(|_| next() as u8).collect(); + let (mut a, mut b) = (base.clone(), base.clone()); + idct4_add(&d4, &mut a[3..], 24); + reference_add(&d4, 4, &mut b[3..], 24); + assert_eq!(a, b, "4x4 iteration {iter}"); + let (mut a, mut b) = (base.clone(), base); + idct8_add(&d8, &mut a[5..], 24); + reference_add(&d8, 8, &mut b[5..], 24); + assert_eq!(a, b, "8x8 iteration {iter}"); + } + } + + #[test] + fn idct4_dc_only_matches_shortcut() { + let mut d = [0i32; 16]; + d[0] = 640; // -> +10 everywhere + let mut a = [100u8; 16]; + let mut b = [100u8; 16]; + idct4_add(&d, &mut a, 4); + idct4_dc_add(640, &mut b, 4); + assert_eq!(a, b); + assert!(a.iter().all(|&v| v == 110)); + } + + #[test] + fn idct4_hand_example() { + // d = [[64, 64, 0, 0], 0...]: row0: e=64,f=64,g=32,h=64 -> [128, 96, 32, 0]; columns: each col only row0 set + // col j: e=f=t0j, g=0, h=0 -> all rows = t0j. Result (t+32)>>6 = [2, 2, 1, 0] for every row. + let mut d = [0i32; 16]; + d[0] = 64; + d[1] = 64; + let mut out = [0u8; 16]; + idct4_add(&d, &mut out, 4); + for i in 0..4 { + assert_eq!(&out[i * 4..i * 4 + 4], &[2, 2, 1, 0]); + } + } + + #[test] + fn idct8_dc() { + let mut d = [0i32; 64]; + d[0] = 64 * 5; + let mut out = [10u8; 64]; + idct8_add(&d, &mut out, 8); + assert!(out.iter().all(|&v| v == 15)); + } + + #[test] + fn luma_dc_flat() { + // single DC coefficient spreads equally to all 16 blocks + let mut c = [0i32; 16]; + c[0] = 4; + luma_dc_dequant(&mut c, 36, 16 * 16); + assert!(c.iter().all(|&v| v == 4 * 256)); + } + + #[test] + fn chroma_dc() { + let mut c = [1, 0, 0, 0]; + chroma_dc_dequant_420(&mut c, 6, 16 * 16); + // f = [1,1,1,1] -> ((1*256) << 1) >> 5 = 16 + assert_eq!(c, [16, 16, 16, 16]); + } + + #[test] + fn scale_functions() { + assert_eq!(scale4(2, 160, 24), 2 * 160); + assert_eq!(scale4(2, 160, 12), (2 * 160 + 2) >> 2); + assert_eq!(scale8(1, 320, 36), 320); + assert_eq!(scale8(1, 320, 30), (320 + 1) >> 1); + } + + /// Corrupt streams can carry huge coefficient levels: dequantisation wraps (as release + /// builds always did) instead of panicking with "attempt to multiply with overflow" in + /// debug builds (found by mutation fuzzing an H.264 transport stream). + #[test] + fn huge_levels_dequantise_without_overflow_panics() { + let mut c = [i32::MAX / 2, i32::MAX / 3, -i32::MAX / 2, 7]; + chroma_dc_dequant_420(&mut c, 51, 16 * 25); + let _ = scale4(i32::MAX, 16 * 25, 51); + let _ = scale4(i32::MAX, 16 * 25, 0); + let _ = scale8(i32::MIN, 16 * 25, 51); + let _ = scale8(i32::MIN, 16 * 25, 0); + } +} diff --git a/crates/hevc/Cargo.toml b/crates/hevc/Cargo.toml new file mode 100644 index 0000000..cf008d4 --- /dev/null +++ b/crates/hevc/Cargo.toml @@ -0,0 +1,19 @@ +[package] +name = "slidecraft-hevc" +description = "Clean-room pure-Rust H.265/HEVC decoder (ITU-T H.265)" +version.workspace = true +edition.workspace = true +license.workspace = true +rust-version.workspace = true + +[lints] +workspace = true + +[features] +default = ["threads"] +threads = ["dep:rayon"] + +[dependencies] +slidecraft-bitstream = { workspace = true } +thiserror = { workspace = true } +rayon = { workspace = true, optional = true } diff --git a/crates/hevc/src/cabac.rs b/crates/hevc/src/cabac.rs new file mode 100644 index 0000000..100fc2f --- /dev/null +++ b/crates/hevc/src/cabac.rs @@ -0,0 +1,352 @@ +//! CABAC arithmetic decoding engine (9.3.4.3) and context initialisation (9.3.2.2). + +use crate::error::{Result, ensure}; +use crate::spec_tables::{CABAC_INIT, NUM_CTX}; + +/// Table 9-52 (same values as H.264) rangeTabLPS[pStateIdx][qCodIRangeIdx]. +#[rustfmt::skip] +pub(crate) 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(crate) 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 +}; + +/// Context states, padded to a power of two so that the per-bin index needs no bounds check. +pub type Contexts = [u8; CTX_SLOTS]; +const CTX_SLOTS: usize = NUM_CTX.next_power_of_two(); + +/// Initialise all context variables for `slice_qp` and `init_type` (9.3.2.2). Each context is stored as +/// `pStateIdx << 1 | valMps`. +pub fn init_contexts(ctx: &mut Contexts, slice_qp: i32, init_type: usize) { + 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 }; + } +} + +pub struct Cabac<'a> { + data: &'a [u8], + /// Byte position of the next byte to load into `value`. + next_byte: usize, + range: u32, + /// ivlOffset scaled by 2^bits: value = (ivlOffset << bits) | (bits already loaded but not yet + /// consumed by the arithmetic decoder). + value: u64, + bits: u32, + pub ctx: Contexts, +} + +impl<'a> Cabac<'a> { + /// Create an engine over `data` starting at byte `pos`, with contexts initialised for `slice_qp` + /// and `init_type`. + pub fn new(data: &'a [u8], pos: usize, slice_qp: i32, init_type: usize) -> Result { + let mut c = Cabac { data, next_byte: pos, range: 0, value: 0, bits: 0, ctx: [0; CTX_SLOTS] }; + init_contexts(&mut c.ctx, slice_qp, init_type); + c.init_engine()?; + Ok(c) + } + + /// 9.3.2.6: ivlCurrRange = 510, ivlOffset = read_bits(9) (from the current byte-aligned position). + pub fn init_engine(&mut self) -> Result<()> { + self.range = 510; + self.value = 0; + self.bits = 0; + self.refill(); + self.bits -= 9; + ensure!((self.value >> self.bits) < 510, "invalid CABAC offset at init"); + Ok(()) + } + + /// Load 32 more bits (zeros past the end of the data). + #[inline(always)] + fn refill(&mut self) { + let b = self.next_byte; + let mut w = [0u8; 4]; + if let Some(src) = self.data.get(b..b + 4) { + w.copy_from_slice(src); + } else if b < self.data.len() { + let n = self.data.len() - b; + w[..n].copy_from_slice(&self.data[b..]); + } + self.next_byte += 4; + self.value = (self.value << 32) | u32::from_be_bytes(w) as u64; + self.bits += 32; + } + + /// Bit position of the next bit not consumed by the arithmetic decoder (for PCM samples and + /// substream ends after decode_terminate returned 1). + pub fn bit_pos(&self) -> usize { + self.next_byte * 8 - self.bits as usize + } + /// Continue reading at byte `pos` (call `init_engine` afterwards). + pub fn set_byte_pos(&mut self, pos: usize) { + self.next_byte = pos; + self.value = 0; + self.bits = 0; + } + pub fn data(&self) -> &'a [u8] { + self.data + } + /// True when the reader has run past the end of the data (corrupt stream). + pub fn overrun(&self) -> bool { + self.next_byte > self.data.len() + 16 + } + + #[inline(always)] + fn renorm(&mut self) { + // ivlCurrRange < 512 always, so the shift is 0 when no renormalisation is needed: no + // branch on the (unpredictable) range, only on the rare refill. + let shift = self.range.leading_zeros() - 23; + self.range <<= shift; + if self.bits < shift { + self.refill(); + } + self.bits -= shift; + } + + /// 9.3.4.3.2, branch-free: the MPS / LPS outcome of a context-coded bin is close to random + /// at high bit rates, so values are selected instead of jumping. + #[inline(always)] + pub fn decode_decision(&mut self, ctx_idx: usize) -> u32 { + let ctx_idx = ctx_idx & (CTX_SLOTS - 1); + let s = (self.ctx[ctx_idx] & 127) as usize; + let q = ((self.range >> 6) & 3) as usize; + let lps = RANGE_TAB_LPS[s >> 1][q] as u32; + let mps_range = self.range - lps; + let scaled = (mps_range as u64) << self.bits; + let is_lps = self.value >= scaled; + self.value -= scaled & (is_lps as u64).wrapping_neg(); + self.range = if is_lps { lps } else { mps_range }; + self.ctx[ctx_idx] = NEXT_STATE[s][is_lps as usize]; + self.renorm(); + (s & 1) as u32 ^ is_lps as u32 + } + + #[inline(always)] + pub fn decode_bypass(&mut self) -> u32 { + if self.bits == 0 { + self.refill(); + } + self.bits -= 1; + let scaled = (self.range as u64) << self.bits; + let one = self.value >= scaled; + self.value -= scaled & (one as u64).wrapping_neg(); + one as u32 + } + + /// `n` (<= 16) bypass bins at once, most significant first. The bins of a run of bypass + /// decodes are the binary digits of floor(ivlOffset / ivlCurrRange) in the bit-scaled + /// representation (no renormalisation happens between them), so one division replaces `n` + /// compare-subtract steps. + #[inline(always)] + pub fn decode_bypass_bits(&mut self, n: u32) -> u32 { + debug_assert!(n <= 16); + if self.bits < n { + self.refill(); + } + let s = self.bits - n; + let r = ((self.value >> s) / self.range as u64) as u32; + self.value -= ((r as u64) * self.range as u64) << s; + self.bits = s; + r + } + + pub fn decode_terminate(&mut self) -> u32 { + self.range -= 2; + let scaled = (self.range as u64) << self.bits; + if self.value >= scaled { + 1 + } else { + self.renorm(); + 0 + } + } +} + +#[cfg(test)] +mod tests { + use super::*; + + /// Minimal CABAC encoder (9.3.4, informative) used to round-trip test the decoder. + struct Enc { + low: u32, + range: u32, + outstanding: u32, + first: bool, + bits: Vec, + ctx: Vec, + } + impl Enc { + fn new(ctx: &[u8]) -> Self { + Enc { low: 0, range: 510, outstanding: 0, first: true, bits: vec![], ctx: ctx.to_vec() } + } + fn put(&mut self, b: bool) { + if self.first { + self.first = false; + } else { + self.bits.push(b); + } + while self.outstanding > 0 { + self.bits.push(!b); + self.outstanding -= 1; + } + } + fn renorm(&mut self) { + while self.range < 256 { + if self.low < 256 { + self.put(false); + } else if self.low >= 512 { + self.low -= 512; + self.put(true); + } else { + self.low -= 256; + self.outstanding += 1; + } + self.range <<= 1; + self.low <<= 1; + } + } + fn encode(&mut self, ctx_idx: usize, bin: u32) { + let s = self.ctx[ctx_idx] as usize; + let p = s >> 1; + let mps = (s & 1) as u32; + let q = ((self.range >> 6) & 3) as usize; + let lps = RANGE_TAB_LPS[p][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(); + } + fn bypass(&mut self, bin: u32) { + self.low <<= 1; + if bin != 0 { + self.low += self.range; + } + if self.low >= 1024 { + self.put(true); + self.low -= 1024; + } else if self.low < 512 { + self.put(false); + } else { + self.low -= 512; + self.outstanding += 1; + } + } + fn terminate_flush(&mut self) -> Vec { + // encode terminate bin = 1 then flush (9.3.4.5) + self.range -= 2; + self.low += self.range; + self.range = 2; + self.renorm(); + self.put((self.low >> 9) & 1 != 0); + self.bits.push((self.low >> 8) & 1 != 0); + self.bits.push(true); // rbsp_stop_one_bit + while !self.bits.len().is_multiple_of(8) { + self.bits.push(false); + } + self.bits.chunks(8).map(|c| c.iter().fold(0u8, |a, &b| (a << 1) | b as u8)).collect() + } + } + + #[test] + fn context_init_formula() { + let mut ctx: Contexts = [0u8; CTX_SLOTS]; + // initValue 154 is the "equiprobable" value: m = 0, n = 64 -> pStateIdx 0, valMps 1 + init_contexts(&mut ctx, 30, 0); + assert_eq!(ctx[crate::spec_tables::CU_TRANSQUANT_BYPASS], 1); + // split_cu_flag ctx 0 (initValue 139): slope 8, offset 11 -> m = -5, n = 72; QP 30: (-150 >> 4) + 72 = 62 + assert_eq!(ctx[crate::spec_tables::SPLIT_CU], (63 - 62) << 1); + } + + #[test] + fn round_trip_random_bins() { + let mut seed = 12345u32; + let mut rnd = || { + seed ^= seed << 13; + seed ^= seed >> 17; + seed ^= seed << 5; + seed + }; + let dummy = Cabac::new(&[0, 0, 0, 0], 0, 30, 1).unwrap(); + let mut enc = Enc::new(&dummy.ctx); + let mut ops = Vec::new(); + for _ in 0..5000 { + let kind = rnd() % 4; + let ctx = (rnd() % 16) as usize; + // skewed bins so contexts adapt + let bin = if rnd() % 10 < 8 { (ctx & 1) as u32 } else { 1 - (ctx & 1) as u32 }; + if kind == 0 { + enc.bypass(bin); + } else { + enc.encode(ctx, bin); + } + ops.push((kind, ctx, bin)); + } + let bytes = enc.terminate_flush(); + let mut dec = Cabac::new(&bytes, 0, 30, 1).unwrap(); + for (i, &(kind, ctx, bin)) in ops.iter().enumerate() { + let got = if kind == 0 { dec.decode_bypass() } else { dec.decode_decision(ctx) }; + assert_eq!(got, bin, "bin {i}"); + } + assert_eq!(dec.decode_terminate(), 1); + } +} diff --git a/crates/hevc/src/decoder.rs b/crates/hevc/src/decoder.rs new file mode 100644 index 0000000..eb0a302 --- /dev/null +++ b/crates/hevc/src/decoder.rs @@ -0,0 +1,612 @@ +//! Top-level decoder: NAL dispatch, picture boundaries, POC / RPS / DPB management and output. +//! +//! The calling thread parses parameter sets and slice headers and does all POC, RPS and DPB +//! bookkeeping. The CTU layer of each picture runs as a job (on a thread pool with the `threads` +//! feature) that publishes finished CTB rows into the picture's shared [`Frame`]; jobs of later pictures +//! block per row on the reference data they read, so several pictures decode concurrently. + +use crate::dpb::{Dpb, Output, OutputMeta, PocState, RefPicSet, build_ref_lists}; +use crate::error::{Error, Result, ensure, invalid}; +use crate::params::{Layout, Pps, Sps, Vps}; +use crate::picture::{Frame, FrameRef}; +use crate::slice::{NalHeader, SliceHeader, nal_type}; +use crate::slicedec::{F_INTRA, F_SKIP, PicState, SliceDecoder, SliceJob}; +use crate::{ColorInfo, Picture, Plane}; +use slidecraft_bitstream::{annexb_nals, length_prefixed_nals, unescape_rbsp}; +use std::collections::VecDeque; +use std::sync::{Arc, Mutex}; + +/// A picture whose slices are being collected. +struct PendingPic { + frame: FrameRef, + sps: Arc, + pps: Arc, + layout: Arc, + /// Header of the latest independent slice segment (for dependent slice segments). + last_sh: SliceHeader, + first_sh: SliceHeader, + rps: RefPicSet, + poc: i32, + output: bool, + meta: Arc, + slices: Vec, +} + +/// Counters describing what the decoder has seen (useful to check test coverage). +#[derive(Clone, Debug, Default, PartialEq, Eq)] +pub struct DecodeStats { + pub pictures: u64, + pub slices_i: u64, + pub slices_p: u64, + pub slices_b: u64, + pub dependent_slices: u64, + pub idr: u64, + pub cra: u64, + pub skipped_rasl: u64, + pub intra_4x4_blocks: u64, + pub skip_4x4_blocks: u64, + pub inter_4x4_blocks: u64, + pub weighted_slices: u64, + pub tmvp_slices: u64, + pub sao_slices: u64, + pub deblock_disabled_slices: u64, + pub tiles_pictures: u64, + pub wpp_pictures: u64, + pub scaling_list_pictures: u64, + pub transform_skip_pictures: u64, + pub long_term_slices: u64, + pub missing_refs: u64, + /// Pictures decoded in draft mode without in-loop filters ([`Decoder::set_draft`]). + pub draft_pictures: u64, +} + +#[derive(Default)] +struct Shared { + error: Mutex>, + stats: Mutex, +} + +/// H.265 / HEVC decoder. +pub struct Decoder { + vpss: Vec>>, + spss: Vec>>, + ppss: Vec>>, + nal_length_size: Option, + dpb: Dpb, + poc_state: PocState, + pending: Option, + next_id: u32, + active_sps: Option>, + first_picture: bool, + after_eos: bool, + /// RASL pictures associated with the last IRAP are skipped (it had NoRaslOutputFlag = 1). + skip_rasl: bool, + layout_cache: Vec<(Arc, Arc, Arc, Option>>>)>, + out_queue: VecDeque, + shared: Arc, + #[cfg(feature = "threads")] + pool: Option, + in_flight: VecDeque, + max_in_flight: usize, + draft: bool, +} + +impl Default for Decoder { + fn default() -> Self { + Self::new() + } +} + +fn default_threads() -> usize { + #[cfg(all(feature = "threads", not(target_arch = "wasm32")))] + { + std::thread::available_parallelism().map(|n| n.get()).unwrap_or(1).min(16) + } + #[cfg(not(all(feature = "threads", not(target_arch = "wasm32"))))] + { + 1 + } +} + +impl Decoder { + /// A decoder using all available cores (with the `threads` feature). + pub fn new() -> Self { + Self::with_threads(default_threads()) + } + + /// A decoder using up to `threads` worker threads (1 = decode on the calling thread). + pub fn with_threads(threads: usize) -> Self { + #[cfg(feature = "threads")] + let pool = if threads > 1 && cfg!(not(target_arch = "wasm32")) { + rayon::ThreadPoolBuilder::new().num_threads(threads).thread_name(|i| format!("hevc-{i}")).build().ok() + } else { + None + }; + Decoder { + vpss: vec![None; 16], + spss: vec![None; 16], + ppss: vec![None; 64], + nal_length_size: None, + dpb: Dpb::default(), + poc_state: PocState::default(), + pending: None, + next_id: 1, + active_sps: None, + first_picture: true, + after_eos: false, + skip_rasl: false, + layout_cache: Vec::new(), + out_queue: VecDeque::new(), + shared: Arc::new(Shared::default()), + #[cfg(feature = "threads")] + pool, + in_flight: VecDeque::new(), + max_in_flight: threads.max(1) + 2, + draft: false, + } + } + + /// Configure from an `hvcC` (HEVCDecoderConfigurationRecord) box payload. + pub fn from_hvcc(hvcc: &[u8]) -> Result { + let mut d = Decoder::new(); + d.configure_hvcc(hvcc)?; + Ok(d) + } + + /// Parse an `hvcC` record: parameter-set arrays and the NAL length size used by [`Decoder::decode`]. + pub fn configure_hvcc(&mut self, hvcc: &[u8]) -> Result<()> { + ensure!(hvcc.len() >= 23, "hvcC too short"); + ensure!(hvcc[0] == 1, "unsupported hvcC version {}", hvcc[0]); + self.nal_length_size = Some((hvcc[21] & 3) as usize + 1); + let num_arrays = hvcc[22] as usize; + let mut pos = 23; + for _ in 0..num_arrays { + ensure!(pos + 3 <= hvcc.len(), "hvcC truncated"); + let n = u16::from_be_bytes([hvcc[pos + 1], hvcc[pos + 2]]) as usize; + pos += 3; + for _ in 0..n { + ensure!(pos + 2 <= hvcc.len(), "hvcC truncated"); + let len = u16::from_be_bytes([hvcc[pos], hvcc[pos + 1]]) as usize; + pos += 2; + ensure!(pos + len <= hvcc.len(), "hvcC truncated"); + self.handle_nal(&hvcc[pos..pos + len], 0)?; + pos += len; + } + } + Ok(()) + } + + /// Length of NAL length prefixes (from hvcC), or None for Annex-B input. + pub fn nal_length_size(&self) -> Option { + self.nal_length_size + } + + /// Decode one access unit (all NAL units of one picture, Annex-B or length-prefixed per + /// configuration; a byte stream with several complete access units is accepted too). Returns + /// pictures that became ready for output, in output order; `pts` travels with the picture of this + /// access unit through reordering. + pub fn decode(&mut self, data: &[u8], pts: i64) -> Result> { + let nals = match self.nal_length_size { + Some(n) => length_prefixed_nals(data, n)?, + None => annexb_nals(data), + }; + let mut result = Ok(()); + for nal in nals { + if let Err(e) = self.handle_nal(nal, pts) { + result = Err(e); + break; + } + } + self.submit_pending(); + if let Some(e) = self.take_error() { + result = result.and(Err(e)); + } + result?; + let max_queue = self.max_in_flight * 2; + let mut pics = Vec::new(); + while let Some(o) = self.out_queue.front() { + if !o.frame.is_complete() && self.out_queue.len() <= max_queue { + break; + } + let Some(o) = self.out_queue.pop_front() else { break }; + pics.push(make_picture(&o)); + } + Ok(pics) + } + + /// Output all remaining pictures (end of stream). + pub fn flush(&mut self) -> Vec { + self.submit_pending(); + let mut outs = Vec::new(); + self.dpb.flush(&mut outs); + self.out_queue.extend(outs); + let pics = self.out_queue.drain(..).map(|o| make_picture(&o)).collect(); + while let Some(f) = self.in_flight.pop_front() { + f.wait_complete(); + } + self.first_picture = true; + pics + } + + /// Statistics accumulated so far. + pub fn stats(&self) -> DecodeStats { + self.shared.stats.lock().map(|s| s.clone()).unwrap_or_default() + } + + fn stat(&self, f: impl FnOnce(&mut DecodeStats)) { + if let Ok(mut s) = self.shared.stats.lock() { + f(&mut s); + } + } + + /// First error reported by a decoding job since the last call (also returned by + /// [`Decoder::decode`]); useful after [`Decoder::flush`]. + /// Draft mode for reduced-resolution playback (off by default): sub-layer non-reference + /// pictures of the highest temporal sub-layer (pictures nothing predicts from) skip + /// deblocking and SAO and come out flagged [`Picture::draft`]; every other picture is + /// unchanged. + pub fn set_draft(&mut self, on: bool) { + self.draft = on; + } + + pub fn take_error(&mut self) -> Option { + self.shared.error.lock().ok().and_then(|mut e| e.take()) + } + + fn handle_nal(&mut self, nal: &[u8], pts: i64) -> Result<()> { + if nal.len() < 2 { + return Ok(()); + } + let hdr = NalHeader::parse(nal)?; + if hdr.layer_id != 0 { + return Ok(()); + } + match hdr.nal_type { + nal_type::VPS => { + let v = Vps::parse(&unescape_rbsp(&nal[2..]))?; + let id = v.id as usize; + self.vpss[id] = Some(Arc::new(v)); + } + nal_type::SPS => { + let s = Sps::parse(&unescape_rbsp(&nal[2..]))?; + let id = s.id as usize; + self.spss[id] = Some(Arc::new(s)); + } + nal_type::PPS => { + let p = Pps::parse(&unescape_rbsp(&nal[2..]))?; + let id = p.id as usize; + self.ppss[id] = Some(Arc::new(p)); + } + nal_type::EOS | nal_type::EOB => { + self.submit_pending(); + self.after_eos = true; + } + 0..=9 | 16..=21 => self.handle_slice(nal, hdr, pts)?, + // AUD, SEI, filler, reserved and unspecified NAL units are skipped + _ => {} + } + Ok(()) + } + + fn layout_for(&mut self, pps: &Arc, sps: &Arc) -> Result<(Arc, Option>>>)> { + if let Some((_, _, l, s)) = self.layout_cache.iter().find(|(p, s, _, _)| Arc::ptr_eq(p, pps) && Arc::ptr_eq(s, sps)) { + return Ok((l.clone(), s.clone())); + } + let layout = Arc::new(Layout::new(sps, pps)?); + let scaling = if sps.scaling_list_enabled { + let list = pps.scaling_list.as_ref().or(sps.scaling_list.as_ref()).cloned().unwrap_or_else(crate::params::ScalingList::default_lists); + let mut v = Vec::with_capacity(24); + for size in 0..4 { + for m in 0..6 { + v.push(list.factor(size, m)); + } + } + Some(Arc::new(v)) + } else { + None + }; + if self.layout_cache.len() > 8 { + self.layout_cache.remove(0); + } + self.layout_cache.push((pps.clone(), sps.clone(), layout.clone(), scaling.clone())); + Ok((layout, scaling)) + } + + fn handle_slice(&mut self, nal: &[u8], hdr: NalHeader, pts: i64) -> Result<()> { + let rbsp = unescape_rbsp(&nal[2..]); + // a first_slice_segment_in_pic_flag of 1 starts a new picture + let first = rbsp.first().is_some_and(|b| b & 0x80 != 0); + if first { + self.submit_pending(); + } + if hdr.is_rasl() && (self.skip_rasl || self.first_picture) { + if first { + self.stat(|s| s.skipped_rasl += 1); + } + return Ok(()); + } + let ppss = &self.ppss; + let spss = &self.spss; + let prev = self.pending.as_ref().map(|p| &p.last_sh); + let (sh, pps, sps) = SliceHeader::parse( + &rbsp, + hdr, + |id| { + let pps = ppss[id as usize].as_ref().ok_or_else(|| Error::MissingParameterSet(format!("PPS {id}")))?; + let sps = spss[pps.sps_id as usize].as_ref().ok_or_else(|| Error::MissingParameterSet(format!("SPS {}", pps.sps_id)))?; + Ok((pps.clone(), sps.clone())) + }, + prev, + )?; + sps.check_supported()?; + pps.check_supported()?; + if first { + self.start_picture(&sh, &sps, &pps, pts)?; + } else { + let Some(p) = &self.pending else { return invalid("slice segment without the first slice of its picture") }; + ensure!(Arc::ptr_eq(&p.pps, &pps), "PPS changed within a picture"); + } + let Some(pending) = self.pending.as_mut() else { + return invalid("slice segment without the first slice of its picture"); + }; + let refs = build_ref_lists(&sh, &pending.rps)?; + if !sh.dependent { + pending.last_sh = sh.clone(); + } + let (st, dependent, weighted, tmvp, sao, dbk_off, lt) = + (sh.slice_type, sh.dependent, sh.pwt.is_some(), sh.temporal_mvp, sh.sao_luma || sh.sao_chroma, sh.deblocking_disabled, !sh.lt.is_empty()); + pending.slices.push(SliceJob { sh, pps, sps, rbsp, refs }); + self.stat(|s| { + match st { + crate::slice::SliceType::I => s.slices_i += 1, + crate::slice::SliceType::P => s.slices_p += 1, + crate::slice::SliceType::B => s.slices_b += 1, + } + s.dependent_slices += dependent as u64; + s.weighted_slices += weighted as u64; + s.tmvp_slices += tmvp as u64; + s.sao_slices += sao as u64; + s.deblock_disabled_slices += dbk_off as u64; + s.long_term_slices += lt as u64; + }); + Ok(()) + } + + fn start_picture(&mut self, sh: &SliceHeader, sps: &Arc, pps: &Arc, pts: i64) -> Result<()> { + ensure!(sh.first_slice_segment_in_pic && !sh.dependent, "invalid first slice segment"); + let hdr = sh.nal; + let changed = match &self.active_sps { + None => true, + Some(a) => { + a.width != sps.width + || a.height != sps.height + || a.bit_depth_luma != sps.bit_depth_luma + || a.bit_depth_chroma != sps.bit_depth_chroma + || a.log2_ctb != sps.log2_ctb + } + }; + let mut outs = Vec::new(); + if changed && self.active_sps.is_some() { + self.dpb.flush(&mut outs); + } + self.active_sps = Some(sps.clone()); + self.dpb.max_dec_pic_buffering = sps.max_dec_pic_buffering as usize; + self.dpb.max_num_reorder = sps.max_num_reorder as usize; + self.dpb.max_latency = if sps.max_latency_increase_plus1 != 0 { sps.max_num_reorder + sps.max_latency_increase_plus1 - 1 } else { 0 }; + let irap_no_rasl = hdr.is_irap() && (hdr.is_idr() || hdr.is_bla() || self.first_picture || self.after_eos); + if hdr.is_irap() { + self.skip_rasl = irap_no_rasl; + self.stat(|s| { + if hdr.is_idr() { + s.idr += 1; + } else { + s.cra += 1; + } + }); + } + let max_lsb = sps.max_poc_lsb(); + let poc = self.poc_state.compute(sh, max_lsb, irap_no_rasl); + let (w, h) = (sps.width as usize, sps.height as usize); + let template = Frame::new(0, 0, w, h, sps.log2_ctb, sps.bit_depth_luma, sps.bit_depth_chroma); + let next_id = &mut self.next_id; + let mut missing = 0u64; + let mut make_missing = |p: i32| -> FrameRef { + missing += 1; + let id = *next_id; + *next_id += 1; + Arc::new(Frame::gray(id, p, &template)) + }; + let rps = self.dpb.apply_rps(sh, poc, max_lsb, irap_no_rasl, &mut make_missing)?; + if missing > 0 { + self.stat(|s| s.missing_refs += missing); + } + if irap_no_rasl && !self.first_picture { + if sh.no_output_of_prior_pics && hdr.is_idr() { + self.dpb.clear(); + } else { + self.dpb.flush(&mut outs); + } + } else { + self.dpb.bump_before_decode(&mut outs); + } + self.out_queue.extend(outs); + let (layout, scaling) = self.layout_for(pps, sps)?; + let _ = scaling; + let id = self.next_id; + self.next_id += 1; + let frame = Arc::new(Frame::new(id, poc, w, h, sps.log2_ctb, sps.bit_depth_luma, sps.bit_depth_chroma)); + let output = sh.pic_output && !(hdr.is_rasl() && self.skip_rasl); + // Draft mode: a sub-layer non-reference picture of the highest sub-layer is never used + // for prediction, so its in-loop filters can be left out without changing anything else. + let draft = self.draft && hdr.is_sub_layer_non_ref() && hdr.temporal_id as u32 >= sps.max_sub_layers_minus1; + if draft { + self.stat(|s| s.draft_pictures += 1); + } + let meta = Arc::new(output_meta(sps, pts, hdr.is_irap(), draft)); + self.pending = Some(PendingPic { + frame, + sps: sps.clone(), + pps: pps.clone(), + layout, + last_sh: sh.clone(), + first_sh: sh.clone(), + rps, + poc, + output, + meta, + slices: Vec::new(), + }); + self.first_picture = false; + self.after_eos = false; + self.stat(|s| { + s.tiles_pictures += pps.tiles_enabled as u64; + s.wpp_pictures += pps.entropy_coding_sync as u64; + s.scaling_list_pictures += sps.scaling_list_enabled as u64; + s.transform_skip_pictures += pps.transform_skip as u64; + }); + Ok(()) + } + + /// Start decoding the pending picture and insert it into the DPB. + fn submit_pending(&mut self) { + let Some(p) = self.pending.take() else { return }; + let PendingPic { frame, sps, pps, layout, first_sh, poc, output, meta, slices, .. } = p; + let scaling = + self.layout_cache.iter().find(|(pp, ss, _, _)| Arc::ptr_eq(pp, &pps) && Arc::ptr_eq(ss, &sps)).and_then(|(_, _, _, s)| s.clone()); + self.dispatch(frame.clone(), sps, pps, layout, scaling, slices, meta.draft); + self.poc_state.update(&first_sh, poc); + let mut outs = Vec::new(); + self.dpb.insert(frame, poc, output, meta, &mut outs); + self.out_queue.extend(outs); + } + + #[allow(clippy::too_many_arguments)] + fn dispatch( + &mut self, + frame: FrameRef, + sps: Arc, + pps: Arc, + layout: Arc, + scaling: Option>>>, + slices: Vec, + draft: bool, + ) { + let shared = self.shared.clone(); + #[cfg(feature = "threads")] + if let Some(pool) = &self.pool { + self.in_flight.retain(|f| !f.is_complete()); + while self.in_flight.len() >= self.max_in_flight { + if let Some(f) = self.in_flight.pop_front() { + f.wait_complete(); + } + } + self.in_flight.push_back(frame.clone()); + pool.spawn_fifo(move || run_job(frame, sps, pps, layout, scaling, slices, draft, &shared)); + return; + } + run_job(frame, sps, pps, layout, scaling, slices, draft, &shared); + } +} + +/// Decode all slices of one picture and publish its rows. +#[allow(clippy::too_many_arguments)] +fn run_job( + frame: FrameRef, + sps: Arc, + pps: Arc, + layout: Arc, + scaling: Option>>>, + slices: Vec, + draft: bool, + shared: &Shared, +) { + let mut pic = PicState::new(frame.clone(), sps, pps, layout, scaling); + pic.draft = draft; + let result = std::panic::catch_unwind(std::panic::AssertUnwindSafe(|| { + let mut first_err = None; + for s in &slices { + if let Err(e) = SliceDecoder::decode(&mut pic, s) { + first_err.get_or_insert(e); + } + } + crate::filter::finish(&mut pic); + first_err + })); + let err = match result { + Ok(e) => e, + Err(_) => { + // make sure readers never block on this frame + let gray = Frame::gray(0, frame.poc, &frame); + for r in 0..frame.num_rows() { + if !frame.is_published(r) { + let row = gray.row(r); + frame.publish(r, crate::picture::FrameRow { y: row.y.clone(), cb: row.cb.clone(), cr: row.cr.clone(), col: row.col.clone() }); + } + } + Some(Error::Invalid("internal error while decoding a picture".into())) + } + }; + if let Ok(mut s) = shared.stats.lock() { + s.pictures += 1; + for b in &pic.blk { + if b.flags & F_INTRA != 0 { + s.intra_4x4_blocks += 1; + } else if b.flags & F_SKIP != 0 { + s.skip_4x4_blocks += 1; + } else if b.flags != 0 { + s.inter_4x4_blocks += 1; + } + } + } + if let (Some(e), Ok(mut slot)) = (err, shared.error.lock()) { + slot.get_or_insert(e); + } +} + +fn output_meta(sps: &Sps, pts: i64, key: bool, draft: bool) -> OutputMeta { + let vui = sps.vui.clone().unwrap_or_default(); + OutputMeta { + pts, + key, + crop: sps.crop_rect(), + full_range: vui.full_range, + colour_primaries: vui.colour_primaries, + transfer_characteristics: vui.transfer_characteristics, + matrix_coefficients: vui.matrix_coefficients, + sar: vui.sar, + bit_depth: sps.bit_depth_luma, + draft, + } +} + +fn make_picture(o: &Output) -> Picture { + let (cx, cy, cw, ch) = o.meta.crop; + let (cx, cy, cw, ch) = (cx as usize, cy as usize, cw as usize, ch as usize); + let (y, u, v) = o.frame.copy_cropped((cx, cy, cw, ch)); + let (ccw, cch) = (cw.div_ceil(2), ch.div_ceil(2)); + let bd = o.meta.bit_depth; + let conv = |p: Vec| if bd <= 8 { Plane::U8(p.into_iter().map(|v| v as u8).collect()) } else { Plane::U16(p) }; + Picture { + width: cw as u32, + height: ch as u32, + chroma_width: ccw as u32, + chroma_height: cch as u32, + bit_depth: bd, + y: conv(y), + u: conv(u), + v: conv(v), + y_stride: cw, + uv_stride: ccw, + pts: o.meta.pts, + poc: o.poc, + key: o.meta.key, + color: ColorInfo { + full_range: o.meta.full_range, + primaries: o.meta.colour_primaries, + transfer: o.meta.transfer_characteristics, + matrix: o.meta.matrix_coefficients, + }, + sar: o.meta.sar, + draft: o.meta.draft, + } +} diff --git a/crates/hevc/src/dpb.rs b/crates/hevc/src/dpb.rs new file mode 100644 index 0000000..19cfecc --- /dev/null +++ b/crates/hevc/src/dpb.rs @@ -0,0 +1,296 @@ +//! Decoded picture buffer: POC derivation (8.3.1), reference picture set (8.3.2), missing +//! reference generation (8.3.3), reference picture lists (8.3.4) and output / bumping (C.5.2). + +use crate::error::{Result, ensure}; +use crate::picture::{FrameRef, RefPic}; +use crate::slice::SliceHeader; +use std::sync::Arc; + +/// Output metadata travelling with a picture. +#[derive(Clone, Debug)] +pub struct OutputMeta { + pub pts: i64, + pub key: bool, + pub crop: (u32, u32, u32, u32), + pub full_range: bool, + pub colour_primaries: u8, + pub transfer_characteristics: u8, + pub matrix_coefficients: u8, + pub sar: (u16, u16), + pub bit_depth: u32, + /// Decoded in draft mode (no in-loop filters). + pub draft: bool, +} + +#[derive(Clone, Copy, Debug, PartialEq, Eq)] +pub enum Marking { + Unused, + Short, + Long, +} + +pub struct Entry { + pub frame: FrameRef, + pub poc: i32, + pub marking: Marking, + pub needed_for_output: bool, + pub latency: u32, + pub meta: Arc, +} + +/// A picture leaving the DPB for output. +pub struct Output { + pub frame: FrameRef, + pub poc: i32, + pub meta: Arc, +} + +/// Reference picture set of the current picture (8.3.2) as DPB indices / generated frames. +#[derive(Default)] +pub struct RefPicSet { + pub st_curr_before: Vec, + pub st_curr_after: Vec, + pub lt_curr: Vec, +} + +#[derive(Default)] +pub struct Dpb { + pub entries: Vec, + pub max_dec_pic_buffering: usize, + pub max_num_reorder: usize, + /// SpsMaxLatencyPictures (0 = no limit). + pub max_latency: u32, +} + +impl Dpb { + fn bump(&mut self, outs: &mut Vec) -> bool { + let Some(i) = (0..self.entries.len()).filter(|&i| self.entries[i].needed_for_output).min_by_key(|&i| self.entries[i].poc) else { + return false; + }; + let e = &mut self.entries[i]; + e.needed_for_output = false; + outs.push(Output { frame: e.frame.clone(), poc: e.poc, meta: e.meta.clone() }); + self.remove_unused(); + true + } + + fn remove_unused(&mut self) { + self.entries.retain(|e| e.needed_for_output || e.marking != Marking::Unused); + } + + fn num_output(&self) -> usize { + self.entries.iter().filter(|e| e.needed_for_output).count() + } + + fn latency_exceeded(&self) -> bool { + self.max_latency != 0 && self.entries.iter().any(|e| e.needed_for_output && e.latency >= self.max_latency) + } + + /// Output everything (end of stream or IRAP without no_output_of_prior_pics). + pub fn flush(&mut self, outs: &mut Vec) { + while self.bump(outs) {} + for e in &mut self.entries { + e.marking = Marking::Unused; + } + self.entries.clear(); + } + + /// Discard everything without output. + pub fn clear(&mut self) { + self.entries.clear(); + } + + /// C.5.2.2: removal of pictures before decoding the current (non-IRAP-reset) picture, after the RPS + /// has been applied. + pub fn bump_before_decode(&mut self, outs: &mut Vec) { + self.remove_unused(); + loop { + let full = self.entries.len() >= self.max_dec_pic_buffering.max(1); + if self.num_output() > self.max_num_reorder || self.latency_exceeded() || full { + if !self.bump(outs) { + // DPB full of reference pictures that are not needed for output: nothing to do + break; + } + } else { + break; + } + } + } + + /// C.5.2.3: insert the current picture and apply "additional bumping". + pub fn insert(&mut self, frame: FrameRef, poc: i32, output: bool, meta: Arc, outs: &mut Vec) { + for e in &mut self.entries { + if e.needed_for_output { + e.latency += 1; + } + } + self.entries.push(Entry { frame, poc, marking: Marking::Short, needed_for_output: output, latency: 0, meta }); + while self.num_output() > self.max_num_reorder || self.latency_exceeded() { + if !self.bump(outs) { + break; + } + } + } + + /// Apply the RPS of the current picture (8.3.2): mark pictures, find (or generate) references. + /// `make_missing` creates a replacement frame for a missing reference picture with the given POC. + pub fn apply_rps( + &mut self, + sh: &SliceHeader, + poc: i32, + max_poc_lsb: i32, + irap_no_rasl: bool, + make_missing: &mut dyn FnMut(i32) -> FrameRef, + ) -> Result { + let mut set = RefPicSet::default(); + if sh.nal.is_irap() && irap_no_rasl { + for e in &mut self.entries { + e.marking = Marking::Unused; + } + } + if sh.nal.is_idr() { + for e in &mut self.entries { + e.marking = Marking::Unused; + } + return Ok(set); + } + // Long-term entries first (they may refer to any reference picture). + let mut keep = vec![false; self.entries.len()]; + let mut lt_idx: Vec<(Option, i32, bool, bool)> = Vec::new(); // (entry, poc, used, msb_present) + for &(lsb, used, msb_present, cycle) in &sh.lt { + let mut p = lsb as i32; + if msb_present { + p += poc - (cycle as i32).wrapping_mul(max_poc_lsb) - (poc & (max_poc_lsb - 1)); + } + let found = self + .entries + .iter() + .position(|e| e.marking != Marking::Unused && if msb_present { e.poc == p } else { (e.poc & (max_poc_lsb - 1)) == p }); + lt_idx.push((found, p, used, msb_present)); + } + // Short-term entries. + let mut st: Vec<(Option, i32, bool, bool)> = Vec::new(); // (entry, poc, used, before) + for &(d, used) in &sh.st_rps.s0 { + let p = poc + d; + let found = self.entries.iter().position(|e| e.marking == Marking::Short && e.poc == p); + st.push((found, p, used, true)); + } + for &(d, used) in &sh.st_rps.s1 { + let p = poc + d; + let found = self.entries.iter().position(|e| e.marking == Marking::Short && e.poc == p); + st.push((found, p, used, false)); + } + for &(f, _, _, _) in lt_idx.iter() { + if let Some(i) = f { + keep[i] = true; + } + } + for &(f, _, _, _) in st.iter() { + if let Some(i) = f { + keep[i] = true; + } + } + for (i, e) in self.entries.iter_mut().enumerate() { + if !keep[i] { + e.marking = Marking::Unused; + } + } + for &(f, _, _, _) in <_idx { + if let Some(i) = f { + self.entries[i].marking = Marking::Long; + } + } + let mut make = |found: Option, p: i32, lt: bool, entries: &mut Vec| -> RefPic { + match found { + Some(i) => RefPic { frame: entries[i].frame.clone(), poc: entries[i].poc, long_term: lt }, + None => { + // 8.3.3: generate an unavailable reference picture + let frame = make_missing(p); + RefPic { frame, poc: p, long_term: lt } + } + } + }; + for &(f, p, used, before) in &st { + if !used { + continue; + } + let r = make(f, p, false, &mut self.entries); + if before { + set.st_curr_before.push(r); + } else { + set.st_curr_after.push(r); + } + } + for &(f, p, used, _) in <_idx { + if used { + let r = make(f, p, true, &mut self.entries); + set.lt_curr.push(r); + } + } + Ok(set) + } +} + +/// Build RefPicList0/1 (8.3.4). +pub fn build_ref_lists(sh: &SliceHeader, rps: &RefPicSet) -> Result<[Vec; 2]> { + let mut lists: [Vec; 2] = [Vec::new(), Vec::new()]; + if sh.is_intra() { + return Ok(lists); + } + let total = rps.st_curr_before.len() + rps.st_curr_after.len() + rps.lt_curr.len(); + ensure!(total > 0, "no reference pictures for an inter slice"); + for l in 0..if sh.is_b() { 2 } else { 1 } { + let n = (sh.num_ref_idx[l] as usize).max(total); + let mut temp = Vec::with_capacity(n); + let (first, second) = if l == 0 { (&rps.st_curr_before, &rps.st_curr_after) } else { (&rps.st_curr_after, &rps.st_curr_before) }; + while temp.len() < n { + for r in first.iter().chain(second.iter()).chain(rps.lt_curr.iter()) { + if temp.len() < n { + temp.push(r.clone()); + } + } + } + for i in 0..sh.num_ref_idx[l] as usize { + let idx = match &sh.list_entry[l] { + Some(e) => e[i] as usize, + None => i, + }; + ensure!(idx < temp.len(), "list_entry out of range"); + lists[l].push(temp[idx].clone()); + } + } + Ok(lists) +} + +/// POC state (8.3.1). +#[derive(Default, Clone, Copy)] +pub struct PocState { + pub prev_tid0_poc: i32, +} + +impl PocState { + pub fn compute(&self, sh: &SliceHeader, max_poc_lsb: i32, irap_no_rasl: bool) -> i32 { + let lsb = sh.poc_lsb as i32; + let msb = if sh.nal.is_irap() && irap_no_rasl { + 0 + } else { + let prev_lsb = self.prev_tid0_poc & (max_poc_lsb - 1); + let prev_msb = self.prev_tid0_poc - prev_lsb; + if lsb < prev_lsb && prev_lsb - lsb >= max_poc_lsb / 2 { + prev_msb + max_poc_lsb + } else if lsb > prev_lsb && lsb - prev_lsb > max_poc_lsb / 2 { + prev_msb - max_poc_lsb + } else { + prev_msb + } + }; + msb + lsb + } + + /// Update after decoding a picture with the given header and POC. + pub fn update(&mut self, sh: &SliceHeader, poc: i32) { + if sh.nal.temporal_id == 0 && !sh.nal.is_rasl() && !sh.nal.is_radl() && !sh.nal.is_sub_layer_non_ref() { + self.prev_tid0_poc = poc; + } + } +} diff --git a/crates/hevc/src/error.rs b/crates/hevc/src/error.rs new file mode 100644 index 0000000..f7a37c6 --- /dev/null +++ b/crates/hevc/src/error.rs @@ -0,0 +1,38 @@ +use slidecraft_bitstream::BitError; + +/// Errors produced by the decoder. +#[derive(Debug, Clone, PartialEq, Eq, thiserror::Error)] +pub enum Error { + /// The bitstream ended early or contained an invalid Exp-Golomb code. + #[error("bitstream error: {0}")] + Bitstream(#[from] BitError), + /// A syntax element had a value outside its legal range. + #[error("invalid bitstream: {0}")] + Invalid(String), + /// A valid feature this decoder does not implement (yet). + #[error("unsupported: {0}")] + Unsupported(String), + /// A slice referenced a parameter set that has not been received. + #[error("missing parameter set: {0}")] + MissingParameterSet(String), +} + +pub type Result = std::result::Result; + +pub(crate) fn invalid(msg: impl Into) -> Result { + Err(Error::Invalid(msg.into())) +} + +pub(crate) fn unsupported(msg: impl Into) -> Result { + Err(Error::Unsupported(msg.into())) +} + +/// `ensure!(cond, "message")` returns `Error::Invalid` when `cond` is false. +macro_rules! ensure { + ($cond:expr, $($arg:tt)+) => { + if !$cond { + return Err($crate::error::Error::Invalid(format!($($arg)+))); + } + }; +} +pub(crate) use ensure; diff --git a/crates/hevc/src/filter.rs b/crates/hevc/src/filter.rs new file mode 100644 index 0000000..479e269 --- /dev/null +++ b/crates/hevc/src/filter.rs @@ -0,0 +1,598 @@ +//! In-loop filters and row publication: deblocking (8.7.2) and sample adaptive offset (8.7.3), run +//! row by row behind the CTU decoding so finished CTB rows can be published to later pictures early. +//! +//! Row r is deblocked once rows 0..=r+1 are decoded (row r+1 still needs the unfiltered bottom samples +//! of row r for intra prediction); SAO of row r runs once row r+1 is deblocked; then row r is final. + +use crate::picture::{ColMv, FrameRow}; +use crate::slicedec::{E_PU_H, E_PU_V, E_TU_H, E_TU_V, F_CBF, F_CODED, F_INTRA, F_NOFILTER, MvField, PicState}; +use crate::spec_tables::{BETA_TABLE, TC_TABLE}; +use crate::tables::qpc_420; + +/// Called after each decoded CTB of CTB row `ry`. +pub fn ctb_decoded(pic: &mut PicState, ry: usize) { + pic.row_done[ry] += 1; + while pic.rows_complete < pic.hctb && pic.row_done[pic.rows_complete] as usize == pic.wctb { + pic.rows_complete += 1; + } + advance(pic, false); +} + +/// Run the filters / publication as far as the decoded rows allow (`all`: end of picture). +pub fn advance(pic: &mut PicState, all: bool) { + let h = pic.hctb; + loop { + let r = pic.rows_deblocked; + if r < h && (all || pic.rows_complete >= (r + 2).min(h)) { + deblock_row(pic, r); + pic.rows_deblocked += 1; + continue; + } + let r = pic.rows_published; + if r < h && pic.rows_deblocked >= (r + 2).min(h) { + publish_row(pic, r); + pic.rows_published += 1; + continue; + } + break; + } +} + +/// End of picture: filter and publish everything. +pub fn finish(pic: &mut PicState) { + pic.rows_complete = pic.hctb; + advance(pic, true); +} + +#[inline] +fn tc_beta(q: i32, bs: i32, tc_off: i32, beta_off: i32, bd: u32) -> (i32, i32) { + let qb = (q + beta_off).clamp(0, 51); + let qt = (q + 2 * (bs - 1) + tc_off).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. +fn boundary_strength(pic: &PicState, pi: usize, qi: usize, tu_edge: bool) -> i32 { + let (p, q) = (&pic.blk[pi], &pic.blk[qi]); + if (p.flags | q.flags) & F_INTRA != 0 { + return 2; + } + if tu_edge && (p.flags | q.flags) & F_CBF != 0 { + return 1; + } + let (mp, mq) = (&pic.mvf[pi], &pic.mvf[qi]); + motion_bs(pic, pi, mp, qi, mq) +} + +fn ref_id(pic: &PicState, blk: usize, f: &MvField, l: usize) -> u32 { + let (x, y) = ((blk % pic.w4) * 4, (blk / pic.w4) * 4); + let ctb = (y >> pic.log2_ctb) * pic.wctb + (x >> pic.log2_ctb); + let s = pic.ctb_slice[ctb]; + if s == u32::MAX { + return u32::MAX; + } + pic.slices[s as usize].ref_ids[l].get(f.ref_idx[l] as usize).copied().unwrap_or(u32::MAX - 1) +} + +fn motion_bs(pic: &PicState, pi: usize, p: &MvField, qi: usize, q: &MvField) -> i32 { + let np = p.pred(0) as u32 + p.pred(1) as u32; + let nq = q.pred(0) as u32 + q.pred(1) as u32; + if np != nq { + return 1; + } + let far = |a: [i16; 2], b: [i16; 2]| (a[0] as i32 - b[0] as i32).abs() >= 4 || (a[1] as i32 - b[1] as i32).abs() >= 4; + if np == 1 { + let lp = if p.pred(0) { 0 } else { 1 }; + let lq = if q.pred(0) { 0 } else { 1 }; + if ref_id(pic, pi, p, lp) != ref_id(pic, qi, q, lq) { + return 1; + } + return far(p.mv[lp], q.mv[lq]) as i32; + } + let (p0, p1) = (ref_id(pic, pi, p, 0), ref_id(pic, pi, p, 1)); + let (q0, q1) = (ref_id(pic, qi, q, 0), ref_id(pic, qi, q, 1)); + if !((p0 == q0 && p1 == q1) || (p0 == q1 && p1 == q0)) { + return 1; + } + if p0 != p1 { + if p0 == q0 { (far(p.mv[0], q.mv[0]) || far(p.mv[1], q.mv[1])) as i32 } else { (far(p.mv[0], q.mv[1]) || far(p.mv[1], q.mv[0])) as i32 } + } else { + ((far(p.mv[0], q.mv[0]) || far(p.mv[1], q.mv[1])) && (far(p.mv[0], q.mv[1]) || far(p.mv[1], q.mv[0]))) as i32 + } +} + +/// Whether the edge between blocks p and q (q = current block, owning the edge) is filtered, and the +/// slice-level parameters (tc offset, beta offset) of q's slice. +fn edge_params(pic: &PicState, px: usize, py: usize, qx: usize, qy: usize) -> Option<(i32, i32)> { + let s = pic.log2_ctb; + let pc = (py >> s) * pic.wctb + (px >> s); + let qc = (qy >> s) * pic.wctb + (qx >> s); + let qs = pic.ctb_slice[qc]; + let ps = pic.ctb_slice[pc]; + if qs == u32::MAX || ps == u32::MAX { + return None; + } + let sq = &pic.slices[qs as usize]; + if sq.deblock_disabled { + return None; + } + if pc != qc { + if pic.ctb_addr[pc] != pic.ctb_addr[qc] && !sq.lf_across { + return None; + } + if !pic.pps.loop_filter_across_tiles && pic.layout.tile_of_rs(pc as u32) != pic.layout.tile_of_rs(qc as u32) { + return None; + } + } + Some((sq.tc_offset, sq.beta_offset)) +} + +/// Deblock CTB row `r`: vertical edges of the row, then horizontal edges whose q side is in the row. +fn deblock_row(pic: &mut PicState, r: usize) { + if !pic.deblocking_enabled_anywhere || pic.draft { + return; + } + let ctb = 1usize << pic.log2_ctb; + let y0 = r * ctb; + let y1 = (y0 + ctb).min(pic.height); + let (w, w4) = (pic.width, pic.w4); + let (bd, bdc) = (pic.bd_y, pic.bd_c); + let (cb_off, cr_off) = (pic.pps.cb_qp_offset, pic.pps.cr_qp_offset); + // vertical edges + let mut bs_v = vec![0i8; w4 * ((y1 - y0) / 4)]; + for by in y0 / 4..y1 / 4 { + for bx in (2..w4).step_by(2) { + let qi = by * w4 + bx; + let e = pic.blk[qi].edges; + if e & (E_TU_V | E_PU_V) == 0 || pic.blk[qi].flags & F_CODED == 0 { + continue; + } + let (x, y) = (bx * 4, by * 4); + let Some((tc_off, beta_off)) = edge_params(pic, x - 1, y, x, y) else { continue }; + let bs = boundary_strength(pic, qi - 1, qi, e & E_TU_V != 0); + bs_v[(by - y0 / 4) * w4 + bx] = bs as i8; + if bs == 0 { + continue; + } + let (p, q) = (&pic.blk[qi - 1], &pic.blk[qi]); + let qpl = (p.qp as i32 + q.qp as i32 + 1) >> 1; + let (tc, beta) = tc_beta(qpl, bs, tc_off, beta_off, bd); + let (nfp, nfq) = (p.flags & F_NOFILTER != 0, q.flags & F_NOFILTER != 0); + filter_luma(&mut pic.planes[0], y * w + x, 1, w, tc, beta, nfp, nfq, bd); + } + } + // chroma vertical edges (8x8 chroma grid = 16 luma samples), bS == 2 only + let cw = pic.cwidth; + for by in (y0 / 4..y1 / 4).step_by(2) { + for bx in (4..w4).step_by(4) { + if bs_v[(by - y0 / 4) * w4 + bx] != 2 { + continue; + } + let (x, y) = (bx * 4, by * 4); + let qi = by * w4 + bx; + let Some((tc_off, _)) = edge_params(pic, x - 1, y, x, y) else { continue }; + let (p, q) = (pic.blk[qi - 1], pic.blk[qi]); + let (nfp, nfq) = (p.flags & F_NOFILTER != 0, q.flags & F_NOFILTER != 0); + for (c, off) in [(1, cb_off), (2, cr_off)] { + let qpi = ((p.qp as i32 + q.qp as i32 + 1) >> 1) + off; + let qt = (qpc_420(qpi) + 2 + tc_off).clamp(0, 53); + let tc = (TC_TABLE[qt as usize] as i32) << (bdc - 8); + filter_chroma(&mut pic.planes[c], (y / 2) * cw + x / 2, 1, cw, tc, nfp, nfq, bdc); + } + } + } + // horizontal edges with the q side in this row + let mut bs_h = vec![0i8; w4 * ((y1 - y0) / 4)]; + for by in (y0 / 4..y1 / 4).filter(|b| b % 2 == 0 && *b > 0) { + for bx in 0..w4 { + let qi = by * w4 + bx; + let e = pic.blk[qi].edges; + if e & (E_TU_H | E_PU_H) == 0 || pic.blk[qi].flags & F_CODED == 0 { + continue; + } + let (x, y) = (bx * 4, by * 4); + let Some((tc_off, beta_off)) = edge_params(pic, x, y - 1, x, y) else { continue }; + let pi = qi - w4; + let bs = boundary_strength(pic, pi, qi, e & E_TU_H != 0); + bs_h[(by - y0 / 4) * w4 + bx] = bs as i8; + if bs == 0 { + continue; + } + let (p, q) = (&pic.blk[pi], &pic.blk[qi]); + let qpl = (p.qp as i32 + q.qp as i32 + 1) >> 1; + let (tc, beta) = tc_beta(qpl, bs, tc_off, beta_off, bd); + let (nfp, nfq) = (p.flags & F_NOFILTER != 0, q.flags & F_NOFILTER != 0); + filter_luma(&mut pic.planes[0], y * w + x, w, 1, tc, beta, nfp, nfq, bd); + } + } + for by in (y0 / 4..y1 / 4).filter(|b| b % 4 == 0 && *b > 0) { + for bx in (0..w4).step_by(2) { + if bs_h[(by - y0 / 4) * w4 + bx] != 2 { + continue; + } + let (x, y) = (bx * 4, by * 4); + let qi = by * w4 + bx; + let Some((tc_off, _)) = edge_params(pic, x, y - 1, x, y) else { continue }; + let (p, q) = (pic.blk[qi - w4], pic.blk[qi]); + let (nfp, nfq) = (p.flags & F_NOFILTER != 0, q.flags & F_NOFILTER != 0); + for (c, off) in [(1, cb_off), (2, cr_off)] { + let qpi = ((p.qp as i32 + q.qp as i32 + 1) >> 1) + off; + let qt = (qpc_420(qpi) + 2 + tc_off).clamp(0, 53); + let tc = (TC_TABLE[qt as usize] as i32) << (bdc - 8); + filter_chroma(&mut pic.planes[c], (y / 2) * cw + x / 2, cw, 1, tc, nfp, nfq, bdc); + } + } + } +} + +/// Filter one 4-sample luma edge segment. `o` = offset of q0 of the first line, `step` = distance +/// between p/q samples across the edge, `along` = distance between lines. +#[allow(clippy::too_many_arguments)] +fn filter_luma(s: &mut [u16], o: usize, step: usize, along: usize, tc: i32, beta: i32, nfp: bool, nfq: bool, 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; + if !nfp { + 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; + } + if !nfq { + 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); + if !nfp { + s[base - step] = (p0 + delta).clamp(0, max) as u16; + } + if !nfq { + s[base] = (q0 - delta).clamp(0, max) as u16; + } + if dep && !nfp { + 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 && !nfq { + 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. +#[allow(clippy::too_many_arguments)] +fn filter_chroma(s: &mut [u16], o: usize, step: usize, along: usize, tc: i32, nfp: bool, nfq: bool, 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); + if !nfp { + s[base - step] = (p0 + delta).clamp(0, max) as u16; + } + if !nfq { + s[base] = (q0 - delta).clamp(0, max) as u16; + } + } +} + +/// SAO of CTB row `r` from the deblocked planes into new row buffers (luma, Cb, Cr). +fn sao_row(pic: &PicState, r: usize) -> [Vec; 3] { + let ctb = 1usize << pic.log2_ctb; + let mut out: [Vec; 3] = [Vec::new(), Vec::new(), Vec::new()]; + for c in 0..3 { + let (w, h, cs) = if c == 0 { (pic.width, pic.height, ctb) } else { (pic.cwidth, pic.cheight, ctb / 2) }; + let y0 = r * cs; + let y1 = (y0 + cs).min(h); + out[c] = pic.planes[c][y0 * w..y1 * w].to_vec(); + for rx in (0..pic.wctb).filter(|_| !pic.draft) { + let rs = r * pic.wctb + rx; + let si = pic.ctb_slice[rs]; + if si == u32::MAX { + continue; + } + let sl = &pic.slices[si as usize]; + if !(if c == 0 { sl.sao_luma } else { sl.sao_chroma }) { + continue; + } + let p = pic.sao[rs]; + if p.type_idx[c] == 0 { + continue; + } + let x0 = rx * cs; + let x1 = (x0 + cs).min(w); + sao_ctb(pic, c, rs, x0, x1, y0, y1, w, h, &p, &mut out[c]); + } + } + out +} + +#[allow(clippy::too_many_arguments)] +fn sao_ctb( + pic: &PicState, + c: usize, + rs: usize, + x0: usize, + x1: usize, + y0: usize, + y1: usize, + w: usize, + h: usize, + p: &crate::slicedec::SaoParams, + dst: &mut [u16], +) { + let bd = if c == 0 { pic.bd_y } else { pic.bd_c }; + let max = (1i32 << bd) - 1; + let sc = if c == 0 { 0 } else { 1 }; + let offs = p.offsets[c].map(|v| v as i32); + // samples of CUs that must not be modified + let nofilter = |pic: &PicState, x: usize, y: usize| pic.blk[((y << sc) >> 2) * pic.w4 + ((x << sc) >> 2)].flags & F_NOFILTER != 0; + let any_nofilter = { + let mut a = false; + for by in ((y0 << sc) >> 2)..(((y1 << sc) + 3) >> 2).min(pic.h4) { + for bx in ((x0 << sc) >> 2)..(((x1 << sc) + 3) >> 2).min(pic.w4) { + a |= pic.blk[by * pic.w4 + bx].flags & F_NOFILTER != 0; + } + } + a + }; + if p.type_idx[c] == 1 { + let shift = bd - 5; + let mut table = [0i32; 32]; + for k in 0..4 { + table[(k + p.band_pos[c] as usize) & 31] = offs[k]; + } + for y in y0..y1 { + if !any_nofilter { + let band = (p.band_pos[c] as i32, offs); + sao_band_run(&pic.planes[c][y * w + x0..y * w + x1], &mut dst[(y - y0) * w + x0..(y - y0) * w + x1], shift, band, max); + continue; + } + for x in x0..x1 { + if nofilter(pic, x, y) { + continue; + } + let v = pic.planes[c][y * w + x] as i32; + dst[(y - y0) * w + x] = (v + table[(v >> shift) as usize]).clamp(0, max) as u16; + } + } + return; + } + let (hp, vp): ([i32; 2], [i32; 2]) = match p.eo_class[c] { + 0 => ([-1, 1], [0, 0]), + 1 => ([0, 0], [-1, 1]), + 2 => ([-1, 1], [-1, 1]), + _ => ([1, -1], [-1, 1]), + }; + // Neighbouring CTBs whose samples may not be used (other slice / tile without cross-boundary + // filtering), checked per sample only near CTB borders. + let s = pic.log2_ctb as usize - sc; + let cur_slice = pic.ctb_slice[rs]; + let lf_cur = pic.slices[cur_slice as usize].lf_across; + let cur_ts = pic.layout.rs_to_ts[rs]; + let cross_ok = |pic: &PicState, xn: usize, yn: usize| -> bool { + let nrs = (yn >> s) * pic.wctb + (xn >> s); + if nrs == rs { + return true; + } + let ns = pic.ctb_slice[nrs]; + if ns == u32::MAX { + return false; + } + if pic.ctb_addr[nrs] != pic.ctb_addr[rs] { + // different slice: the later one (in decoding order) decides + let nts = pic.layout.rs_to_ts[nrs]; + if nts < cur_ts && !lf_cur { + return false; + } + if cur_ts < nts && !pic.slices[ns as usize].lf_across { + return false; + } + } + if !pic.pps.loop_filter_across_tiles && pic.layout.tile_id[pic.layout.rs_to_ts[nrs] as usize] != pic.layout.tile_id[cur_ts as usize] { + return false; + } + true + }; + let edge_map = [1usize, 2, 0, 3, 4]; + // Offset per (2 + sum of neighbour signs); edgeIdx 0 adds nothing. + let by_sum = edge_map.map(|e| if e == 0 { 0 } else { offs[e - 1] }); + // Any sample: neighbours outside the picture or in a CTB that may not be used leave it as is. + let general = |x: usize, y: usize| -> Option { + if any_nofilter && nofilter(pic, x, y) { + return None; + } + let mut sum = 0i32; + let v = pic.planes[c][y * w + x] as i32; + for k in 0..2 { + let xn = x as i32 + hp[k]; + let yn = y as i32 + vp[k]; + if xn < 0 || yn < 0 || xn >= w as i32 || yn >= h as i32 { + return None; + } + let (xn, yn) = (xn as usize, yn as usize); + if (xn < x0 || xn >= x1 || yn < y0 || yn >= y1) && !cross_ok(pic, xn, yn) { + return None; + } + sum += (v - pic.planes[c][yn * w + xn] as i32).signum(); + } + Some((v + by_sum[(2 + sum) as usize]).clamp(0, max) as u16) + }; + // Samples whose two neighbours lie inside this CTB need none of those checks: a tight loop + // (same arithmetic) for them, the general path for the CTB border. + let (mx, my) = ((hp[0] != 0) as usize, (vp[0] != 0) as usize); + let plane = &pic.planes[c]; + for y in y0..y1 { + let inner_row = !any_nofilter && y >= y0 + my && y + my < y1 && x1 - x0 > 2 * mx; + if !inner_row { + for x in x0..x1 { + if let Some(v) = general(x, y) { + dst[(y - y0) * w + x] = v; + } + } + continue; + } + for x in (x0..x0 + mx).chain(x1 - mx..x1) { + if let Some(v) = general(x, y) { + dst[(y - y0) * w + x] = v; + } + } + let (ya, yb) = ((y as i32 + vp[0]) as usize, (y as i32 + vp[1]) as usize); + let (xa, xb) = (x0 as i32 + mx as i32 + hp[0], x0 as i32 + mx as i32 + hp[1]); + let n = x1 - x0 - 2 * mx; + let cur = &plane[y * w + x0 + mx..][..n]; + let na = &plane[ya * w + xa as usize..][..n]; + let nb = &plane[yb * w + xb as usize..][..n]; + let out = &mut dst[(y - y0) * w + x0 + mx..][..n]; + sao_edge_run(cur, na, nb, out, &by_sum, max); + } +} + +/// Lanes of the SAO loops (16-bit arithmetic: samples are at most 12 bits, offsets 7 bits). +const SAO_LANES: usize = 16; + +/// Edge offset of a run of samples whose two neighbours (`na`, `nb`) are all usable: per lane +/// the sum of the two neighbour signs selects the offset (no table lookup), so the loop +/// vectorises; bit-exact with the per-sample form. +#[inline(always)] +fn sao_edge_run(cur: &[u16], na: &[u16], nb: &[u16], out: &mut [u16], by_sum: &[i32; 5], max: i32) { + let o = by_sum.map(|v| v as i16); + let max = max as i16; + let lane = |v: i16, a: i16, b: i16| -> u16 { + let s = ((v > a) as i16 - (v < a) as i16) + ((v > b) as i16 - (v < b) as i16); + let m = |k: i16| -((s == k) as i16); + let off = (o[0] & m(-2)) | (o[1] & m(-1)) | (o[3] & m(1)) | (o[4] & m(2)); + (v + off).max(0).min(max) as u16 + }; + let n = cur.len(); + let full = n - n % SAO_LANES; + for i in (0..full).step_by(SAO_LANES) { + let (Some(c), Some(a), Some(b), Some(d)) = (lanes(cur, i), lanes(na, i), lanes(nb, i), lanes_mut(out, i)) else { + return; + }; + for l in 0..SAO_LANES { + d[l] = lane(c[l] as i16, a[l] as i16, b[l] as i16); + } + } + for i in full..n { + out[i] = lane(cur[i] as i16, na[i] as i16, nb[i] as i16); + } +} + +/// `SAO_LANES` samples from `i`, if all present. +#[inline(always)] +fn lanes(s: &[u16], i: usize) -> Option<&[u16; SAO_LANES]> { + s.get(i..)?.first_chunk() +} + +#[inline(always)] +fn lanes_mut(s: &mut [u16], i: usize) -> Option<&mut [u16; SAO_LANES]> { + s.get_mut(i..)?.first_chunk_mut() +} + +/// Band offset of a run of samples: the band index relative to `band_pos` selects one of the +/// four offsets per lane. +#[inline(always)] +fn sao_band_run(src: &[u16], out: &mut [u16], shift: u32, (band_pos, offs): (i32, [i32; 4]), max: i32) { + let o = offs.map(|v| v as i16); + let (bp, max) = (band_pos as i16, max as i16); + let lane = |v: i16| -> u16 { + let k = ((v >> shift) - bp) & 31; + let m = |j: i16| -((k == j) as i16); + let off = (o[0] & m(0)) | (o[1] & m(1)) | (o[2] & m(2)) | (o[3] & m(3)); + (v + off).max(0).min(max) as u16 + }; + let n = src.len(); + let full = n - n % SAO_LANES; + for i in (0..full).step_by(SAO_LANES) { + let (Some(c), Some(d)) = (lanes(src, i), lanes_mut(out, i)) else { + return; + }; + for l in 0..SAO_LANES { + d[l] = lane(c[l] as i16); + } + } + for i in full..n { + out[i] = lane(src[i] as i16); + } +} + +/// Publish CTB row `r` (final samples + compressed motion) into the frame. +fn publish_row(pic: &mut PicState, r: usize) { + let ctb = 1usize << pic.log2_ctb; + let (y0, y1) = (r * ctb, ((r + 1) * ctb).min(pic.height)); + let (c0, c1) = (y0 / 2, y1.div_ceil(2)); + let (w, cw) = (pic.width, pic.cwidth); + let w16 = pic.width.div_ceil(16); + let rows16 = (y1 - y0).div_ceil(16); + let mut col = vec![ColMv::default(); w16 * rows16]; + for j in 0..rows16 { + for i in 0..w16 { + let (x, y) = (i * 16, y0 + j * 16); + let bi = (y >> 2) * pic.w4 + (x >> 2); + let b = &pic.blk[bi]; + if b.flags & F_CODED == 0 || b.flags & F_INTRA != 0 { + continue; + } + let f = pic.mvf[bi]; + let ctbi = (y >> pic.log2_ctb) * pic.wctb + (x >> pic.log2_ctb); + let si = pic.ctb_slice[ctbi]; + if si == u32::MAX { + continue; + } + let sl = &pic.slices[si as usize]; + let mut cm = ColMv { mv: f.mv, ref_poc: [0; 2], flags: 0 }; + for l in 0..2 { + if f.pred(l) { + let ri = f.ref_idx[l] as usize; + if ri < sl.ref_pocs[l].len() { + cm.flags |= 1 << l; + cm.ref_poc[l] = sl.ref_pocs[l][ri]; + if sl.ref_lt[l][ri] { + cm.flags |= 4 << l; + } + } + } + } + col[j * w16 + i] = cm; + } + } + let [oy, ocb, ocr] = sao_row(pic, r); + let _ = (w, cw, c0, c1); + let row = FrameRow { y: oy.into(), cb: ocb.into(), cr: ocr.into(), col: col.into() }; + pic.frame.publish(r, row); +} diff --git a/crates/hevc/src/inter.rs b/crates/hevc/src/inter.rs new file mode 100644 index 0000000..de0f28e --- /dev/null +++ b/crates/hevc/src/inter.rs @@ -0,0 +1,195 @@ +//! Fractional sample interpolation (8.5.3.3.3) and weighted sample prediction (8.5.3.3.4). + +use crate::picture::Frame; +use crate::spec_tables::{CHROMA_FILTER, LUMA_FILTER}; + +/// Max prediction block size + filter margin. +const WIN: usize = 64 + 7; + +/// Reference windows and first-pass rows, reused between blocks (no per-block zeroing). +pub struct McScratch { + win: Vec, + tmp: Vec, +} + +impl Default for McScratch { + fn default() -> Self { + McScratch { win: vec![0; WIN * WIN], tmp: vec![0; WIN * WIN] } + } +} + +/// `out[j] = (sum_k f[k] * src[j + k * step]) >> shift` for `W` outputs: taps outer, outputs +/// inner, so each tap is one vector multiply-add across the row (exact integer sums). +#[inline(always)] +fn filt_w(src: &[i16], step: usize, f: &[i8; T], shift: u32, out: &mut [i16]) { + let mut acc = [0i32; W]; + for k in 0..T { + let c = f[k] as i32; + let Some(p) = src.get(k * step..).and_then(|s| s.first_chunk::()) else { + return; + }; + for j in 0..W { + acc[j] += c * p[j] as i32; + } + } + let Some(o) = out.first_chunk_mut::() else { + return; + }; + for j in 0..W { + o[j] = (acc[j] >> shift) as i16; + } +} + +/// [`filt_w`] for a run of `w` outputs (prediction block widths are fixed sizes). +#[inline(always)] +fn filt(src: &[i16], step: usize, f: &[i8; T], shift: u32, out: &mut [i16], w: usize) { + match w { + 2 => filt_w::(src, step, f, shift, out), + 4 => filt_w::(src, step, f, shift, out), + 6 => filt_w::(src, step, f, shift, out), + 8 => filt_w::(src, step, f, shift, out), + 12 => filt_w::(src, step, f, shift, out), + 16 => filt_w::(src, step, f, shift, out), + 24 => filt_w::(src, step, f, shift, out), + 32 => filt_w::(src, step, f, shift, out), + 48 => filt_w::(src, step, f, shift, out), + 64 => filt_w::(src, step, f, shift, out), + _ => { + for (j, o) in out[..w].iter_mut().enumerate() { + let s: i32 = (0..T).map(|k| f[k] as i32 * src[j + k * step] as i32).sum(); + *o = (s >> shift) as i16; + } + } + } +} + +/// Separable sub-sample interpolation of a w x h block from `win` (stride WIN, the block's +/// top-left sample at (m, m) for a T-tap filter with m = T / 2 - 1). +#[allow(clippy::too_many_arguments)] +fn interpolate( + win: &[i16], + tmp: &mut [i16], + fx: usize, + fy: usize, + hf: &[i8; T], + vf: &[i8; T], + w: usize, + h: usize, + shift1: u32, + shift3: u32, + out: &mut [i16], +) { + let m = T / 2 - 1; + if fx == 0 && fy == 0 { + for r in 0..h { + for (o, &s) in out[r * w..r * w + w].iter_mut().zip(&win[(r + m) * WIN + m..]) { + *o = s << shift3; + } + } + } else if fy == 0 { + for r in 0..h { + filt(&win[(r + m) * WIN..], 1, hf, shift1, &mut out[r * w..], w); + } + } else if fx == 0 { + for r in 0..h { + filt(&win[r * WIN + m..], WIN, vf, shift1, &mut out[r * w..], w); + } + } else { + for r in 0..h + T - 1 { + filt(&win[r * WIN..], 1, hf, shift1, &mut tmp[r * WIN..], w); + } + for r in 0..h { + filt(&tmp[r * WIN..], WIN, vf, 6, &mut out[r * w..], w); + } + } +} + +/// Luma prediction samples (14-bit intermediate precision) of a w x h block at (x, y) displaced by the +/// quarter-sample motion vector `mv`, into `out` (stride w). +#[allow(clippy::too_many_arguments)] +pub fn mc_luma(f: &Frame, x: i32, y: i32, mv: [i16; 2], w: usize, h: usize, out: &mut [i16], sc: &mut McScratch) { + let bd = f.bit_depth; + 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; + f.luma_window(xi - 3, yi - 3, w + 7, h + 7, &mut sc.win, WIN); + interpolate::<8>(&sc.win, &mut sc.tmp, fx, fy, &LUMA_FILTER[fx], &LUMA_FILTER[fy], w, h, shift1, shift3, out); +} + +/// Chroma prediction samples of a w x h chroma block at chroma position (x, y) with a 1/8-sample +/// vector (4:2:0). +#[allow(clippy::too_many_arguments)] +pub fn mc_chroma(f: &Frame, c: usize, x: i32, y: i32, mv: [i16; 2], w: usize, h: usize, out: &mut [i16], sc: &mut McScratch) { + let bd = f.bit_depth_c; + 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; + f.chroma_window(c, xi - 1, yi - 1, w + 3, h + 3, &mut sc.win, WIN); + interpolate::<4>(&sc.win, &mut sc.tmp, fx, fy, &CHROMA_FILTER[fx], &CHROMA_FILTER[fy], w, h, shift1, shift3, out); +} + +/// Default weighted prediction, one list (8-262). +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; + // Rows as slices and min / max clipping (`clamp` asserts its bounds) so the loop vectorises. + for (d, s) in dst.chunks_mut(ds).zip(p[..w * h].chunks(w)) { + for (d, &s) in d[..w].iter_mut().zip(s) { + *d = ((s as i32 + off) >> shift).max(0).min(max) as u16; + } + } +} + +/// Default weighted prediction, bi-prediction (8-263). +pub fn put_bi(p0: &[i16], p1: &[i16], w: usize, h: usize, bd: u32, dst: &mut [u16], ds: usize) { + let shift = 15 - bd; + let off = 1 << (shift - 1); + let max = (1i32 << bd) - 1; + for ((d, a), b) in dst.chunks_mut(ds).zip(p0[..w * h].chunks(w)).zip(p1[..w * h].chunks(w)) { + for ((d, &a), &b) in d[..w].iter_mut().zip(a).zip(b) { + *d = ((a as i32 + b as i32 + off) >> shift).max(0).min(max) as u16; + } + } +} + +/// Explicit weighted prediction, one list (8-265). `o` is the offset already scaled to the bit depth. +pub fn put_weighted_uni(p: &[i16], w: usize, h: usize, bd: u32, log2wd: u32, wt: i32, o: i32, dst: &mut [u16], ds: usize) { + let max = (1i32 << bd) - 1; + for r in 0..h { + for c in 0..w { + let v = p[r * w + c] as i32 * wt; + let v = if log2wd >= 1 { ((v + (1 << (log2wd - 1))) >> log2wd) + o } else { v + o }; + dst[r * ds + c] = v.max(0).min(max) as u16; + } + } +} + +/// Explicit weighted bi-prediction (8-266). +pub fn put_weighted_bi( + p0: &[i16], + p1: &[i16], + w: usize, + h: usize, + bd: u32, + log2wd: u32, + w0: i32, + w1: i32, + o0: i32, + o1: i32, + dst: &mut [u16], + ds: usize, +) { + let max = (1i32 << bd) - 1; + for r in 0..h { + for c in 0..w { + let i = r * w + c; + let v = (p0[i] as i32 * w0 + p1[i] as i32 * w1 + ((o0 + o1 + 1) << log2wd)) >> (log2wd + 1); + dst[r * ds + c] = v.max(0).min(max) as u16; + } + } +} diff --git a/crates/hevc/src/intra.rs b/crates/hevc/src/intra.rs new file mode 100644 index 0000000..b1cb628 --- /dev/null +++ b/crates/hevc/src/intra.rs @@ -0,0 +1,253 @@ +//! Intra sample prediction (8.4.4.2): reference substitution, filtering (incl. strong intra +//! smoothing), planar, DC and the 33 angular modes. + +use crate::spec_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/hevc/src/lib.rs b/crates/hevc/src/lib.rs new file mode 100644 index 0000000..26c5658 --- /dev/null +++ b/crates/hevc/src/lib.rs @@ -0,0 +1,132 @@ +//! Clean-room pure-Rust H.265 / HEVC decoder, implemented from ITU-T Rec. H.265 (ISO/IEC 23008-2). +//! +//! Supported: Main and Main 10 profiles (4:2:0, 8/10-bit; up to 12-bit 4:2:0 without range-extension +//! tools), all coding tools of those profiles: CABAC, coding / transform quadtrees, AMP, PCM, +//! transform skip, scaling lists, sign data hiding, 35 intra modes with strong intra smoothing, +//! merge / AMVP / TMVP, weighted prediction, deblocking, SAO, tiles, wavefront parallel processing +//! (parsing), dependent slice segments, short- and long-term reference picture sets. +//! +//! ```no_run +//! let mut dec = slidecraft_hevc::Decoder::new(); +//! let stream = std::fs::read("video.h265").unwrap(); +//! for pic in dec.decode(&stream, 0).unwrap() { +//! println!("{}x{} poc {}", pic.width, pic.height, pic.poc); +//! } +//! for pic in dec.flush() { +//! let _ = pic; +//! } +//! ``` + +// Index loops over fixed-size blocks read more clearly than iterator chains in codec code. +#![allow(clippy::needless_range_loop, clippy::too_many_arguments)] +#![cfg_attr(not(test), deny(clippy::unwrap_used, clippy::expect_used, clippy::panic, clippy::unimplemented, clippy::todo, clippy::unreachable))] + +mod cabac; +mod decoder; +mod dpb; +mod error; +mod filter; +mod inter; +mod intra; +mod mvpred; +pub mod params; +mod picture; +pub mod slice; +mod slicedec; +#[rustfmt::skip] +#[allow(dead_code)] +mod spec_tables; +#[cfg(test)] +mod synth_tests; +mod tables; +mod transform; + +pub use decoder::{DecodeStats, Decoder}; +pub use error::{Error, Result}; + +/// Colour description from the VUI (ITU-T H.273 code points). +#[derive(Clone, Copy, Debug, PartialEq, Eq)] +pub struct ColorInfo { + /// `video_full_range_flag`. + pub full_range: bool, + pub primaries: u8, + pub transfer: u8, + pub matrix: u8, +} + +/// Samples of one plane: 8-bit streams produce `U8`, higher bit depths `U16` (value range +/// `0..(1 << bit_depth)`). +#[derive(Clone, Debug, PartialEq, Eq)] +pub enum Plane { + U8(Vec), + U16(Vec), +} + +impl Plane { + pub fn len(&self) -> usize { + match self { + Plane::U8(v) => v.len(), + Plane::U16(v) => v.len(), + } + } + pub fn is_empty(&self) -> bool { + self.len() == 0 + } + pub fn as_u8(&self) -> Option<&[u8]> { + match self { + Plane::U8(v) => Some(v), + Plane::U16(_) => None, + } + } + pub fn as_u16(&self) -> Option<&[u16]> { + match self { + Plane::U8(_) => None, + Plane::U16(v) => Some(v), + } + } + /// Sample `i` widened to u16. + pub fn get(&self, i: usize) -> u16 { + match self { + Plane::U8(v) => v[i] as u16, + Plane::U16(v) => v[i], + } + } + /// Raw bytes as written by ffmpeg `rawvideo` (8-bit, or 16-bit little endian). + pub fn to_le_bytes(&self) -> Vec { + match self { + Plane::U8(v) => v.clone(), + Plane::U16(v) => v.iter().flat_map(|s| s.to_le_bytes()).collect(), + } + } +} + +/// A decoded, cropped picture in planar 4:2:0. +#[derive(Clone, Debug)] +pub struct Picture { + /// Cropped luma width. + pub width: u32, + /// Cropped luma height. + pub height: u32, + pub chroma_width: u32, + pub chroma_height: u32, + /// Luma bit depth (chroma uses the same storage type). + pub bit_depth: u32, + pub y: Plane, + pub u: Plane, + pub v: Plane, + /// Strides in samples. + pub y_stride: usize, + pub uv_stride: usize, + /// Presentation timestamp passed to [`Decoder::decode`] with the access unit of this picture. + pub pts: i64, + /// Picture order count. + pub poc: i32, + /// IRAP (IDR / CRA / BLA) picture. + pub key: bool, + pub color: ColorInfo, + /// Sample aspect ratio (0, 0 when unspecified). + pub sar: (u16, u16), + /// Decoded in draft mode without deblocking / SAO ([`Decoder::set_draft`]): approximate, + /// for reduced-resolution playback only. Never set without draft mode. + pub draft: bool, +} diff --git a/crates/hevc/src/mvpred.rs b/crates/hevc/src/mvpred.rs new file mode 100644 index 0000000..3f686cb --- /dev/null +++ b/crates/hevc/src/mvpred.rs @@ -0,0 +1,355 @@ +//! Motion vector prediction: merge candidates (8.5.3.2.2 - 8.5.3.2.5), AMVP (8.5.3.2.6, 8.5.3.2.7) +//! and temporal motion vector prediction (8.5.3.2.8, 8.5.3.2.9). + +use crate::picture::RefPic; +use crate::slicedec::{F_INTRA, MvField, PartMode, SliceDecoder}; + +/// l0CandIdx / l1CandIdx of Table 8-7. +const COMB: [(usize, usize); 12] = [(0, 1), (1, 0), (0, 2), (2, 0), (1, 2), (2, 1), (0, 3), (3, 0), (1, 3), (3, 1), (2, 3), (3, 2)]; + +/// Motion vector scaling (8-179 .. 8-183). +pub fn scale_mv(mv: [i16; 2], td: i32, tb: i32) -> [i16; 2] { + let td = td.clamp(-128, 127); + let tb = tb.clamp(-128, 127); + if td == 0 { + return mv; + } + let tx = (16384 + (td.abs() >> 1)) / td; + let dsf = ((tb * tx + 32) >> 6).clamp(-4096, 4095); + let s = |c: i16| -> i16 { + let p = dsf * c as i32; + (p.signum() * ((p.abs() + 127) >> 8)).clamp(-32768, 32767) as i16 + }; + [s(mv[0]), s(mv[1])] +} + +/// Same motion (used lists only). +fn same_motion(a: &MvField, b: &MvField) -> bool { + for l in 0..2 { + if a.pred(l) != b.pred(l) { + return false; + } + if a.pred(l) && (a.ref_idx[l] != b.ref_idx[l] || a.mv[l] != b.mv[l]) { + return false; + } + } + true +} + +impl<'a> SliceDecoder<'a> { + /// Prediction block availability (6.4.2). + fn pb_avail(&self, xc: i32, yc: i32, ncb: i32, xp: i32, yp: i32, w: i32, h: i32, part_idx: usize, xn: i32, yn: i32) -> bool { + let same_cb = xc <= xn && yc <= yn && xc + ncb > xn && yc + ncb > yn; + let avail = if !same_cb { + self.pic.zavail(xp, yp, xn, yn, self.slice_addr) + } else { + !((w << 1) == ncb && (h << 1) == ncb && part_idx == 1 && yc + h <= yn && xc + w > xn) + }; + avail && self.pic.blk_at(xn, yn).flags & F_INTRA == 0 + } + + #[inline] + fn mv_at(&self, x: i32, y: i32) -> MvField { + self.pic.mvf[(y as usize >> 2) * self.pic.w4 + (x as usize >> 2)] + } + + #[inline] + fn ref_pic(&self, l: usize, idx: i8) -> &RefPic { + &self.refs[l][idx as usize] + } + + /// Merge mode motion (8.5.3.2.2). + pub fn derive_merge( + &self, + xc: i32, + yc: i32, + ncb: i32, + xp0: i32, + yp0: i32, + w0: i32, + h0: i32, + part_idx0: usize, + part: PartMode, + merge_idx: usize, + ) -> MvField { + let pml = self.pps.log2_parallel_merge_level; + let (xp, yp, w, h, part_idx) = if pml > 2 && ncb == 8 { (xc, yc, ncb, ncb, 0) } else { (xp0, yp0, w0, h0, part_idx0) }; + let same_mer = |xn: i32, yn: i32| (xp >> pml) == (xn >> pml) && (yp >> pml) == (yn >> pml); + let avail = |xn: i32, yn: i32| self.pb_avail(xc, yc, ncb, xp, yp, w, h, part_idx, xn, yn) && !same_mer(xn, yn); + let mut list: [MvField; 5] = [MvField::default(); 5]; + let mut n = 0; + let max = self.sh.max_num_merge_cand as usize; + // spatial candidates (8.5.3.2.3) + let pa1 = (xp - 1, yp + h - 1); + let av_a1 = avail(pa1.0, pa1.1) && !(matches!(part, PartMode::PNx2N | PartMode::PnLx2N | PartMode::PnRx2N) && part_idx == 1); + let m_a1 = if av_a1 { self.mv_at(pa1.0, pa1.1) } else { MvField::default() }; + if av_a1 { + list[n] = m_a1; + n += 1; + } + let pb1 = (xp + w - 1, yp - 1); + let av_b1 = avail(pb1.0, pb1.1) && !(matches!(part, PartMode::P2NxN | PartMode::P2NxnU | PartMode::P2NxnD) && part_idx == 1); + let m_b1 = if av_b1 { self.mv_at(pb1.0, pb1.1) } else { MvField::default() }; + let fl_b1 = av_b1 && !(av_a1 && same_motion(&m_a1, &m_b1)); + if fl_b1 { + list[n] = m_b1; + n += 1; + } + let pb0 = (xp + w, yp - 1); + let av_b0 = avail(pb0.0, pb0.1); + let fl_b0 = av_b0 && { + let m = self.mv_at(pb0.0, pb0.1); + !(av_b1 && same_motion(&m_b1, &m)) + }; + if fl_b0 { + list[n] = self.mv_at(pb0.0, pb0.1); + n += 1; + } + let pa0 = (xp - 1, yp + h); + let av_a0 = avail(pa0.0, pa0.1); + let fl_a0 = av_a0 && { + let m = self.mv_at(pa0.0, pa0.1); + !(av_a1 && same_motion(&m_a1, &m)) + }; + if fl_a0 { + list[n] = self.mv_at(pa0.0, pa0.1); + n += 1; + } + let pb2 = (xp - 1, yp - 1); + let av_b2 = avail(pb2.0, pb2.1); + let fl_b2 = av_b2 && { + let m = self.mv_at(pb2.0, pb2.1); + !((av_a1 && same_motion(&m_a1, &m)) || (av_b1 && same_motion(&m_b1, &m))) + && (fl_a0 as u32 + av_a1 as u32 + fl_b0 as u32 + fl_b1 as u32) != 4 + }; + if fl_b2 { + list[n] = self.mv_at(pb2.0, pb2.1); + n += 1; + } + if merge_idx < n { + return self.finish_merge(list[merge_idx], w0, h0); + } + // temporal candidate + let mut col = MvField::default(); + let mut col_avail = false; + if let Some(mv) = self.temporal(xp, yp, w, h, 0, 0) { + col.mv[0] = mv; + col.ref_idx[0] = 0; + col_avail = true; + } + if self.sh.is_b() + && let Some(mv) = self.temporal(xp, yp, w, h, 1, 0) + { + col.mv[1] = mv; + col.ref_idx[1] = 0; + col_avail = true; + } + let mut cands: Vec = list[..n].to_vec(); + if col_avail { + cands.push(col); + } + if merge_idx < cands.len() { + return self.finish_merge(cands[merge_idx], w0, h0); + } + // combined bi-predictive candidates (8.5.3.2.4) + let num_orig = cands.len(); + if self.sh.is_b() && num_orig > 1 && num_orig < max { + let mut comb = 0; + while comb < num_orig * (num_orig - 1) && cands.len() < max { + let (i0, i1) = COMB[comb]; + let (c0, c1) = (cands[i0], cands[i1]); + if c0.pred(0) && c1.pred(1) && (self.ref_pic(0, c0.ref_idx[0]).poc != self.ref_pic(1, c1.ref_idx[1]).poc || c0.mv[0] != c1.mv[1]) { + cands.push(MvField { mv: [c0.mv[0], c1.mv[1]], ref_idx: [c0.ref_idx[0], c1.ref_idx[1]] }); + } + comb += 1; + } + } + if merge_idx < cands.len() { + return self.finish_merge(cands[merge_idx], w0, h0); + } + // zero candidates (8.5.3.2.5) + let num_ref = if self.sh.is_b() { self.sh.num_ref_idx[0].min(self.sh.num_ref_idx[1]) } else { self.sh.num_ref_idx[0] } as usize; + let mut zero_idx = 0; + while cands.len() <= merge_idx { + let r = if zero_idx < num_ref { zero_idx as i8 } else { 0 }; + cands.push(MvField { mv: [[0; 2]; 2], ref_idx: [r, if self.sh.is_b() { r } else { -1 }] }); + zero_idx += 1; + } + self.finish_merge(cands[merge_idx], w0, h0) + } + + fn finish_merge(&self, mut f: MvField, w: i32, h: i32) -> MvField { + if f.pred(0) && f.pred(1) && w + h == 12 { + f.ref_idx[1] = -1; + f.mv[1] = [0, 0]; + } + for l in 0..2 { + if !f.pred(l) { + f.mv[l] = [0, 0]; + } + } + f + } + + /// Temporal luma motion vector prediction (8.5.3.2.8). + pub fn temporal(&self, xp: i32, yp: i32, w: i32, h: i32, l: usize, ref_idx: usize) -> Option<[i16; 2]> { + let col = self.col.as_ref()?; + let s = self.sps.log2_ctb; + let (xbr, ybr) = (xp + w, yp + h); + if (yp >> s) == (ybr >> s) && ybr < self.pic.height as i32 && xbr < self.pic.width as i32 { + let r = self.col_mv(col, (xbr >> 4) << 4, (ybr >> 4) << 4, l, ref_idx); + if r.is_some() { + return r; + } + } + let (xc, yc) = (xp + (w >> 1), yp + (h >> 1)); + self.col_mv(col, (xc >> 4) << 4, (yc >> 4) << 4, l, ref_idx) + } + + /// Collocated motion vectors (8.5.3.2.9). + fn col_mv(&self, col: &RefPic, x: i32, y: i32, l: usize, ref_idx: usize) -> Option<[i16; 2]> { + let f = &col.frame; + if f.width != self.pic.width || f.height != self.pic.height { + return None; + } + let cm = f.col_mv(x as usize, y as usize); + if cm.flags & 3 == 0 { + return None; + } + let list_col = if cm.flags & 1 == 0 { + 1 + } else if cm.flags & 2 == 0 { + 0 + } else if self.no_backward_pred { + l + } else { + self.sh.collocated_from_l0 as usize + }; + let mv = cm.mv[list_col]; + let col_lt = (cm.flags >> (2 + list_col)) & 1 == 1; + let cur = self.refs[l].get(ref_idx)?; + if cur.long_term != col_lt { + return None; + } + let col_diff = col.poc - cm.ref_poc[list_col]; + let cur_diff = self.pic.poc - cur.poc; + if cur.long_term || col_diff == cur_diff { Some(mv) } else { Some(scale_mv(mv, col_diff, cur_diff)) } + } + + /// AMVP predictor (8.5.3.2.6, 8.5.3.2.7). + pub fn derive_amvp( + &self, + xc: i32, + yc: i32, + ncb: i32, + xp: i32, + yp: i32, + w: i32, + h: i32, + part_idx: usize, + l: usize, + ref_idx: usize, + mvp_flag: usize, + ) -> [i16; 2] { + let target = &self.refs[l][ref_idx]; + let (tpoc, tlt) = (target.poc, target.long_term); + let cur_poc = self.pic.poc; + let y = 1 - l; + // exact reference match (no scaling) + let unscaled = |f: &MvField| -> Option<[i16; 2]> { + if f.pred(l) && self.ref_pic(l, f.ref_idx[l]).poc == tpoc { + Some(f.mv[l]) + } else if f.pred(y) && self.ref_pic(y, f.ref_idx[y]).poc == tpoc { + Some(f.mv[y]) + } else { + None + } + }; + // long-term-compatible match, scaled when both references are short-term + let scaled = |f: &MvField| -> Option<[i16; 2]> { + let pick = if f.pred(l) && self.ref_pic(l, f.ref_idx[l]).long_term == tlt { + Some((f.mv[l], self.ref_pic(l, f.ref_idx[l]))) + } else if f.pred(y) && self.ref_pic(y, f.ref_idx[y]).long_term == tlt { + Some((f.mv[y], self.ref_pic(y, f.ref_idx[y]))) + } else { + None + }; + pick.map(|(mv, r)| if r.poc != tpoc && !r.long_term && !tlt { scale_mv(mv, cur_poc - r.poc, cur_poc - tpoc) } else { mv }) + }; + let pa = [(xp - 1, yp + h), (xp - 1, yp + h - 1)]; + let av_a = [ + self.pb_avail(xc, yc, ncb, xp, yp, w, h, part_idx, pa[0].0, pa[0].1), + self.pb_avail(xc, yc, ncb, xp, yp, w, h, part_idx, 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 = unscaled(&self.mv_at(pa[k].0, pa[k].1)); + } + } + if a.is_none() { + for k in 0..2 { + if av_a[k] && a.is_none() { + a = scaled(&self.mv_at(pa[k].0, pa[k].1)); + } + } + } + let pb = [(xp + w, yp - 1), (xp + w - 1, yp - 1), (xp - 1, yp - 1)]; + let av_b = [0, 1, 2].map(|k| self.pb_avail(xc, yc, ncb, xp, yp, w, h, part_idx, pb[k].0, pb[k].1)); + let mut b = None; + for k in 0..3 { + if av_b[k] && b.is_none() { + b = unscaled(&self.mv_at(pb[k].0, pb[k].1)); + } + } + if !is_scaled && b.is_some() { + a = b; + } + if !is_scaled { + b = None; + for k in 0..3 { + if av_b[k] && b.is_none() { + b = scaled(&self.mv_at(pb[k].0, pb[k].1)); + } + } + } + let mut list: [[i16; 2]; 3] = [[0; 2]; 3]; + let mut n = 0; + if let Some(a) = a { + list[n] = a; + n += 1; + if let Some(b) = b + && b != a + { + list[n] = b; + n += 1; + } + } else if let Some(b) = b { + list[n] = b; + n += 1; + } + if n < 2 + && mvp_flag >= n + && let Some(c) = self.temporal(xp, yp, w, h, l, ref_idx) + { + list[n] = c; + n += 1; + } + let _ = n; + list[mvp_flag.min(1)] + } +} + +#[cfg(test)] +mod tests { + use super::*; + + #[test] + fn mv_scaling() { + // same distance: unchanged; double distance: doubled + assert_eq!(scale_mv([8, -4], 2, 2), [8, -4]); + assert_eq!(scale_mv([8, -4], 1, 2), [16, -8]); + assert_eq!(scale_mv([8, -4], 2, -2), [-8, 4]); + } +} diff --git a/crates/hevc/src/params.rs b/crates/hevc/src/params.rs new file mode 100644 index 0000000..7a3d1b7 --- /dev/null +++ b/crates/hevc/src/params.rs @@ -0,0 +1,935 @@ +//! Parameter sets: VPS (7.3.2.1), SPS (7.3.2.2), PPS (7.3.2.3), profile/tier/level, scaling lists +//! (7.3.4), short-term reference picture sets (7.3.7) and VUI (E.2.1). + +use crate::error::{Result, ensure, unsupported}; +use crate::spec_tables::{DEFAULT_SCALING_INTER, DEFAULT_SCALING_INTRA}; +use crate::tables::scan_diag; +use slidecraft_bitstream::BitReader; + +/// General profile / tier / level information. +#[derive(Clone, Debug, Default, PartialEq, Eq)] +pub struct ProfileTierLevel { + pub profile_space: u8, + pub tier: bool, + pub profile_idc: u8, + pub compatibility: u32, + pub progressive_source: bool, + pub interlaced_source: bool, + pub level_idc: u8, +} + +pub fn parse_ptl(r: &mut BitReader, profile_present: bool, max_sub_layers_minus1: u32) -> Result { + let mut p = ProfileTierLevel::default(); + if profile_present { + p.profile_space = r.read_bits(2)? as u8; + p.tier = r.read_flag()?; + p.profile_idc = r.read_bits(5)? as u8; + p.compatibility = r.read_bits(32)?; + p.progressive_source = r.read_flag()?; + p.interlaced_source = r.read_flag()?; + r.skip(2)?; // non_packed, frame_only + r.skip(43)?; // constraint flags / reserved bits (always 43 bits) + r.skip(1)?; // inbld / reserved + } + p.level_idc = r.read_bits(8)? as u8; + let n = max_sub_layers_minus1 as usize; + let mut sub_profile = [false; 8]; + let mut sub_level = [false; 8]; + for i in 0..n { + sub_profile[i] = r.read_flag()?; + sub_level[i] = r.read_flag()?; + } + if n > 0 { + for _ in n..8 { + r.skip(2)?; + } + } + for i in 0..n { + if sub_profile[i] { + r.skip(2 + 1 + 5 + 32 + 4 + 43 + 1)?; + } + if sub_level[i] { + r.skip(8)?; + } + } + Ok(p) +} + +fn skip_sub_layer_hrd(r: &mut BitReader, cpb_cnt: u32, sub_pic: bool) -> Result<()> { + for _ in 0..cpb_cnt { + r.read_ue()?; + r.read_ue()?; + if sub_pic { + r.read_ue()?; + r.read_ue()?; + } + r.skip(1)?; + } + Ok(()) +} + +/// hrd_parameters() (E.2.2), skipped. +pub fn skip_hrd(r: &mut BitReader, common: bool, max_sub_layers_minus1: u32) -> Result<()> { + let (mut nal, mut vcl, mut sub_pic) = (false, false, false); + if common { + nal = r.read_flag()?; + vcl = r.read_flag()?; + if nal || vcl { + sub_pic = r.read_flag()?; + if sub_pic { + r.skip(8 + 5 + 1 + 5)?; + } + r.skip(8)?; + if sub_pic { + r.skip(4)?; + } + r.skip(15)?; + } + } + for _ in 0..=max_sub_layers_minus1 { + let fixed_general = r.read_flag()?; + let fixed_cvs = if !fixed_general { r.read_flag()? } else { true }; + let mut low_delay = false; + if fixed_cvs { + r.read_ue()?; + } else { + low_delay = r.read_flag()?; + } + let mut cpb_cnt = 1; + if !low_delay { + cpb_cnt = r.read_ue()? + 1; + ensure!(cpb_cnt <= 32, "cpb_cnt_minus1 out of range"); + } + if nal { + skip_sub_layer_hrd(r, cpb_cnt, sub_pic)?; + } + if vcl { + skip_sub_layer_hrd(r, cpb_cnt, sub_pic)?; + } + } + Ok(()) +} + +/// Video parameter set (only the fields a single-layer decoder needs). +#[derive(Clone, Debug)] +pub struct Vps { + pub id: u8, + pub max_sub_layers_minus1: u32, + pub ptl: ProfileTierLevel, +} + +impl Vps { + pub fn parse(rbsp: &[u8]) -> Result { + let mut r = BitReader::new(rbsp); + let id = r.read_bits(4)? as u8; + r.skip(2 + 6)?; + let max_sub_layers_minus1 = r.read_bits(3)?; + r.skip(1 + 16)?; + let ptl = parse_ptl(&mut r, true, max_sub_layers_minus1)?; + Ok(Vps { id, max_sub_layers_minus1, ptl }) + } +} + +/// VUI fields relevant for output. +#[derive(Clone, Debug, PartialEq, Eq)] +pub struct Vui { + pub sar: (u16, u16), + pub full_range: bool, + pub colour_primaries: u8, + pub transfer_characteristics: u8, + pub matrix_coefficients: u8, + pub field_seq: bool, + pub timing: Option<(u32, u32)>, +} + +impl Default for Vui { + fn default() -> Self { + Vui { + sar: (0, 0), + full_range: false, + colour_primaries: 2, + transfer_characteristics: 2, + matrix_coefficients: 2, + field_seq: false, + timing: None, + } + } +} + +const SAR_TABLE: [(u16, u16); 17] = [ + (0, 0), + (1, 1), + (12, 11), + (10, 11), + (16, 11), + (40, 33), + (24, 11), + (20, 11), + (32, 11), + (80, 33), + (18, 11), + (15, 11), + (64, 33), + (160, 99), + (4, 3), + (3, 2), + (2, 1), +]; + +fn parse_vui(r: &mut BitReader, max_sub_layers_minus1: u32) -> Result { + let mut v = Vui::default(); + if r.read_flag()? { + let idc = r.read_bits(8)?; + if idc == 255 { + v.sar = (r.read_bits(16)? as u16, r.read_bits(16)? as u16); + } else if (idc as usize) < SAR_TABLE.len() { + v.sar = SAR_TABLE[idc as usize]; + } + } + if r.read_flag()? { + r.skip(1)?; + } + if r.read_flag()? { + r.skip(3)?; + v.full_range = r.read_flag()?; + if r.read_flag()? { + v.colour_primaries = r.read_bits(8)? as u8; + v.transfer_characteristics = r.read_bits(8)? as u8; + v.matrix_coefficients = r.read_bits(8)? as u8; + } + } + if r.read_flag()? { + r.read_ue()?; + r.read_ue()?; + } + r.skip(1)?; // neutral_chroma_indication_flag + v.field_seq = r.read_flag()?; + r.skip(1)?; // frame_field_info_present_flag + if r.read_flag()? { + for _ in 0..4 { + r.read_ue()?; + } + } + if r.read_flag()? { + let units = r.read_bits(32)?; + let scale = r.read_bits(32)?; + v.timing = Some((units, scale)); + if r.read_flag()? { + r.read_ue()?; + } + if r.read_flag()? { + skip_hrd(r, true, max_sub_layers_minus1)?; + } + } + if r.read_flag()? { + r.skip(3)?; + for _ in 0..5 { + r.read_ue()?; + } + } + Ok(v) +} + +/// Scaling list data (7.3.4) with prediction resolved: lists in up-right diagonal coefficient order. +#[derive(Clone, Debug, PartialEq, Eq)] +pub struct ScalingList { + /// [sizeId][matrixId][i]; sizeId 0 uses 16 entries. + pub lists: [[[u8; 64]; 6]; 4], + /// scaling_list_dc_coef for sizeId 2, 3. + pub dc: [[u8; 6]; 2], +} + +impl ScalingList { + /// Default lists (Tables 7-5, 7-6). + pub fn default_lists() -> Self { + let mut lists = [[[16u8; 64]; 6]; 4]; + for size in 1..4 { + for m in 0..6 { + lists[size][m] = if m < 3 { DEFAULT_SCALING_INTRA } else { DEFAULT_SCALING_INTER }; + } + } + ScalingList { lists, dc: [[16; 6]; 2] } + } + + pub fn parse(r: &mut BitReader) -> Result { + let mut sl = Self::default_lists(); + for size in 0..4 { + let step = if size == 3 { 3 } else { 1 }; + let mut m = 0; + while m < 6 { + let coef_num = if size == 0 { 16 } else { 64 }; + if !r.read_flag()? { + let delta = r.read_ue()? as usize * step; + ensure!(delta <= m, "scaling_list_pred_matrix_id_delta out of range"); + if delta == 0 { + // default list + let d = Self::default_lists(); + sl.lists[size][m] = d.lists[size][m]; + if size > 1 { + sl.dc[size - 2][m] = 16; + } + } else { + let rm = m - delta; + sl.lists[size][m] = sl.lists[size][rm]; + if size > 1 { + sl.dc[size - 2][m] = sl.dc[size - 2][rm]; + } + } + } else { + let mut next: i32 = 8; + if size > 1 { + let dc = r.read_se()?; + ensure!((-7..=247).contains(&dc), "scaling_list_dc_coef_minus8 out of range"); + next = dc + 8; + sl.dc[size - 2][m] = next as u8; + } + for i in 0..coef_num { + let d = r.read_se()?; + ensure!((-128..=127).contains(&d), "scaling_list_delta_coef out of range"); + next = (next + d + 256).rem_euclid(256); + sl.lists[size][m][i] = next as u8; + } + } + m += step; + } + } + // 32x32 chroma lists (only used for ChromaArrayType 3) are copied from the 16x16 ones. + for m in [1, 2, 4, 5] { + sl.lists[3][m] = sl.lists[2][m]; + sl.dc[1][m] = sl.dc[0][m]; + } + Ok(sl) + } + + /// ScalingFactor for sizeId (log2 size = sizeId + 2) and matrixId as a row-major [y][x] matrix + /// (7-44..7-48). + pub fn factor(&self, size_id: usize, matrix_id: usize) -> Vec { + let n = 4usize << size_id; + let mut out = vec![0u8; n * n]; + if size_id == 0 { + let scan = scan_diag(2); + for (i, &(x, y)) in scan.iter().enumerate() { + out[y as usize * 4 + x as usize] = self.lists[0][matrix_id][i]; + } + return out; + } + let rep = n / 8; + let scan = scan_diag(3); + for (i, &(x, y)) in scan.iter().enumerate() { + let v = self.lists[size_id][matrix_id][i]; + for j in 0..rep { + for k in 0..rep { + out[(y as usize * rep + j) * n + x as usize * rep + k] = v; + } + } + } + if size_id >= 2 { + out[0] = self.dc[size_id - 2][matrix_id]; + } + out + } +} + +/// A short-term reference picture set (7.4.8), resolved. +#[derive(Clone, Debug, Default, PartialEq, Eq)] +pub struct StRps { + /// DeltaPocS0 (negative, decreasing) with UsedByCurrPicS0. + pub s0: Vec<(i32, bool)>, + /// DeltaPocS1 (positive, increasing) with UsedByCurrPicS1. + pub s1: Vec<(i32, bool)>, +} + +impl StRps { + pub fn num_delta_pocs(&self) -> usize { + self.s0.len() + self.s1.len() + } + + /// st_ref_pic_set(idx) with the previously parsed candidate sets `sets` (idx == sets.len() for a + /// slice-header RPS). + pub fn parse(r: &mut BitReader, idx: usize, sets: &[StRps], num_sets: usize) -> Result { + let inter = if idx != 0 { r.read_flag()? } else { false }; + if inter { + let delta_idx = if idx == num_sets { r.read_ue()? as usize + 1 } else { 1 }; + ensure!(delta_idx <= idx, "delta_idx_minus1 out of range"); + let rf = &sets[idx - delta_idx]; + let sign = r.read_flag()?; + let abs = r.read_ue()? as i32 + 1; + ensure!(abs <= 1 << 15, "abs_delta_rps_minus1 out of range"); + let delta_rps = if sign { -abs } else { abs }; + let n = rf.num_delta_pocs(); + let mut used = vec![false; n + 1]; + let mut use_delta = vec![true; n + 1]; + for j in 0..=n { + used[j] = r.read_flag()?; + if !used[j] { + use_delta[j] = r.read_flag()?; + } + } + let nneg = rf.s0.len(); + let mut out = StRps::default(); + for j in (0..rf.s1.len()).rev() { + let d = rf.s1[j].0 + delta_rps; + if d < 0 && use_delta[nneg + j] { + out.s0.push((d, used[nneg + j])); + } + } + if delta_rps < 0 && use_delta[n] { + out.s0.push((delta_rps, used[n])); + } + for j in 0..nneg { + let d = rf.s0[j].0 + delta_rps; + if d < 0 && use_delta[j] { + out.s0.push((d, used[j])); + } + } + for j in (0..nneg).rev() { + let d = rf.s0[j].0 + delta_rps; + if d > 0 && use_delta[j] { + out.s1.push((d, used[j])); + } + } + if delta_rps > 0 && use_delta[n] { + out.s1.push((delta_rps, used[n])); + } + for j in 0..rf.s1.len() { + let d = rf.s1[j].0 + delta_rps; + if d > 0 && use_delta[nneg + j] { + out.s1.push((d, used[nneg + j])); + } + } + ensure!(out.num_delta_pocs() <= 16, "too many pictures in RPS"); + Ok(out) + } else { + let nneg = r.read_ue()? as usize; + let npos = r.read_ue()? as usize; + ensure!(nneg <= 16 && npos <= 16 && nneg + npos <= 16, "too many pictures in RPS"); + let mut out = StRps::default(); + let mut poc = 0i32; + for _ in 0..nneg { + let d = r.read_ue()? as i32 + 1; + ensure!(d <= 1 << 15, "delta_poc_s0_minus1 out of range"); + poc -= d; + out.s0.push((poc, r.read_flag()?)); + } + poc = 0; + for _ in 0..npos { + let d = r.read_ue()? as i32 + 1; + ensure!(d <= 1 << 15, "delta_poc_s1_minus1 out of range"); + poc += d; + out.s1.push((poc, r.read_flag()?)); + } + Ok(out) + } + } +} + +/// Sequence parameter set. +#[derive(Clone, Debug)] +pub struct Sps { + pub vps_id: u8, + pub max_sub_layers_minus1: u32, + pub ptl: ProfileTierLevel, + pub id: u32, + pub chroma_format_idc: u32, + pub separate_colour_plane: bool, + pub width: u32, + pub height: u32, + /// Conformance window in luma samples (left, right, top, bottom). + pub conf_win: (u32, u32, u32, u32), + pub bit_depth_luma: u32, + pub bit_depth_chroma: u32, + pub log2_max_poc_lsb: u32, + pub max_dec_pic_buffering: u32, + pub max_num_reorder: u32, + pub max_latency_increase_plus1: u32, + pub log2_min_cb: u32, + pub log2_ctb: u32, + pub log2_min_tb: u32, + pub log2_max_tb: u32, + pub max_th_depth_inter: u32, + pub max_th_depth_intra: u32, + pub scaling_list_enabled: bool, + /// SPS-level scaling list (defaults when enabled without data). + pub scaling_list: Option, + pub amp: bool, + pub sao: bool, + pub pcm: bool, + pub pcm_bit_depth_luma: u32, + pub pcm_bit_depth_chroma: u32, + pub log2_min_pcm: u32, + pub log2_max_pcm: u32, + pub pcm_loop_filter_disabled: bool, + pub st_rps: Vec, + pub long_term_refs_present: bool, + /// lt_ref_pic_poc_lsb_sps, used_by_curr_pic_lt_sps_flag. + pub lt_ref_pics: Vec<(u32, bool)>, + pub temporal_mvp: bool, + pub strong_intra_smoothing: bool, + pub vui: Option, + /// Range extension flags that change decoding (any set -> unsupported for now). + pub range_extension: bool, + pub extension_flags: u8, +} + +impl Sps { + pub fn parse(rbsp: &[u8]) -> Result { + let mut r = BitReader::new(rbsp); + let vps_id = r.read_bits(4)? as u8; + let max_sub_layers_minus1 = r.read_bits(3)?; + ensure!(max_sub_layers_minus1 <= 6, "sps_max_sub_layers_minus1 out of range"); + r.skip(1)?; + let ptl = parse_ptl(&mut r, true, max_sub_layers_minus1)?; + let id = r.read_ue()?; + ensure!(id < 16, "sps_seq_parameter_set_id out of range"); + let chroma_format_idc = r.read_ue()?; + ensure!(chroma_format_idc <= 3, "chroma_format_idc out of range"); + let separate_colour_plane = if chroma_format_idc == 3 { r.read_flag()? } else { false }; + let width = r.read_ue()?; + let height = r.read_ue()?; + ensure!(width > 0 && height > 0 && width <= 16888 && height <= 16888, "picture size {width}x{height} out of range"); + let mut conf_win = (0, 0, 0, 0); + if r.read_flag()? { + conf_win = (r.read_ue()?, r.read_ue()?, r.read_ue()?, r.read_ue()?); + } + let bit_depth_luma = r.read_ue()? + 8; + let bit_depth_chroma = r.read_ue()? + 8; + ensure!(bit_depth_luma <= 16 && bit_depth_chroma <= 16, "bit depth out of range"); + let log2_max_poc_lsb = r.read_ue()? + 4; + ensure!(log2_max_poc_lsb <= 16, "log2_max_pic_order_cnt_lsb_minus4 out of range"); + let ordering_all = r.read_flag()?; + let (mut dpb, mut reorder, mut latency) = (1, 0, 0); + let start = if ordering_all { 0 } else { max_sub_layers_minus1 }; + for _ in start..=max_sub_layers_minus1 { + // the values of the highest sub-layer are used + dpb = r.read_ue()? + 1; + reorder = r.read_ue()?; + latency = r.read_ue()?; + } + ensure!(dpb <= 16 && reorder <= 16, "sps_max_dec_pic_buffering out of range"); + let log2_min_cb = r.read_ue()? + 3; + let log2_ctb = log2_min_cb + r.read_ue()?; + let log2_min_tb = r.read_ue()? + 2; + let log2_max_tb = log2_min_tb + r.read_ue()?; + ensure!((4..=6).contains(&log2_ctb) && log2_min_cb <= log2_ctb, "CTB size out of range"); + ensure!(log2_min_tb < log2_min_cb && log2_max_tb <= log2_ctb.min(5), "transform block sizes out of range"); + ensure!(width % (1 << log2_min_cb) == 0 && height % (1 << log2_min_cb) == 0, "picture size not a multiple of MinCbSizeY"); + let max_th_depth_inter = r.read_ue()?; + let max_th_depth_intra = r.read_ue()?; + ensure!( + max_th_depth_inter <= log2_ctb - log2_min_tb && max_th_depth_intra <= log2_ctb - log2_min_tb, + "transform hierarchy depth out of range" + ); + let scaling_list_enabled = r.read_flag()?; + let mut scaling_list = None; + if scaling_list_enabled { + scaling_list = Some(if r.read_flag()? { ScalingList::parse(&mut r)? } else { ScalingList::default_lists() }); + } + let amp = r.read_flag()?; + let sao = r.read_flag()?; + let pcm = r.read_flag()?; + let (mut pcm_bit_depth_luma, mut pcm_bit_depth_chroma, mut log2_min_pcm, mut log2_max_pcm, mut pcm_loop_filter_disabled) = + (8, 8, 0, 0, false); + if pcm { + pcm_bit_depth_luma = r.read_bits(4)? + 1; + pcm_bit_depth_chroma = r.read_bits(4)? + 1; + log2_min_pcm = r.read_ue()? + 3; + log2_max_pcm = log2_min_pcm + r.read_ue()?; + pcm_loop_filter_disabled = r.read_flag()?; + ensure!(pcm_bit_depth_luma <= bit_depth_luma && pcm_bit_depth_chroma <= bit_depth_chroma, "PCM bit depth out of range"); + ensure!(log2_max_pcm <= log2_ctb.min(5), "PCM size out of range"); + } + let num_st = r.read_ue()? as usize; + ensure!(num_st <= 64, "num_short_term_ref_pic_sets out of range"); + let mut st_rps = Vec::with_capacity(num_st); + for i in 0..num_st { + let s = StRps::parse(&mut r, i, &st_rps, num_st)?; + st_rps.push(s); + } + let long_term_refs_present = r.read_flag()?; + let mut lt_ref_pics = Vec::new(); + if long_term_refs_present { + let n = r.read_ue()?; + ensure!(n <= 32, "num_long_term_ref_pics_sps out of range"); + for _ in 0..n { + lt_ref_pics.push((r.read_bits(log2_max_poc_lsb)?, r.read_flag()?)); + } + } + let temporal_mvp = r.read_flag()?; + let strong_intra_smoothing = r.read_flag()?; + let vui = if r.read_flag()? { Some(parse_vui(&mut r, max_sub_layers_minus1)?) } else { None }; + let mut range_extension = false; + let mut extension_flags = 0; + if r.read_flag()? { + extension_flags = r.read_bits(8)? as u8; + if extension_flags & 0x80 != 0 { + // sps_range_extension(): any enabled tool is outside Main / Main 10 + let bits = r.read_bits(9)?; + range_extension = bits != 0; + } + } + Ok(Sps { + vps_id, + max_sub_layers_minus1, + ptl, + id, + chroma_format_idc, + separate_colour_plane, + width, + height, + conf_win, + bit_depth_luma, + bit_depth_chroma, + log2_max_poc_lsb, + max_dec_pic_buffering: dpb, + max_num_reorder: reorder, + max_latency_increase_plus1: latency, + log2_min_cb, + log2_ctb, + log2_min_tb, + log2_max_tb, + max_th_depth_inter, + max_th_depth_intra, + scaling_list_enabled, + scaling_list, + amp, + sao, + pcm, + pcm_bit_depth_luma, + pcm_bit_depth_chroma, + log2_min_pcm, + log2_max_pcm, + pcm_loop_filter_disabled, + st_rps, + long_term_refs_present, + lt_ref_pics, + temporal_mvp, + strong_intra_smoothing, + vui, + range_extension, + extension_flags, + }) + } + + /// ChromaArrayType. + pub fn chroma_array_type(&self) -> u32 { + if self.separate_colour_plane { 0 } else { self.chroma_format_idc } + } + pub fn sub_width_c(&self) -> u32 { + if self.chroma_format_idc == 1 || self.chroma_format_idc == 2 { 2 } else { 1 } + } + pub fn sub_height_c(&self) -> u32 { + if self.chroma_format_idc == 1 { 2 } else { 1 } + } + pub fn ctb_size(&self) -> u32 { + 1 << self.log2_ctb + } + pub fn pic_width_in_ctbs(&self) -> u32 { + self.width.div_ceil(self.ctb_size()) + } + pub fn pic_height_in_ctbs(&self) -> u32 { + self.height.div_ceil(self.ctb_size()) + } + pub fn max_poc_lsb(&self) -> i32 { + 1 << self.log2_max_poc_lsb + } + + /// Cropped output rectangle (x, y, width, height) in luma samples. + pub fn crop_rect(&self) -> (u32, u32, u32, u32) { + let (l, r, t, b) = self.conf_win; + let (sw, sh) = (self.sub_width_c(), self.sub_height_c()); + let x = (l * sw).min(self.width - 1); + let y = (t * sh).min(self.height - 1); + let w = self.width.saturating_sub(x + r * sw).max(1); + let h = self.height.saturating_sub(y + b * sh).max(1); + (x, y, w, h) + } + + /// Features this decoder does not handle yet. + pub fn check_supported(&self) -> Result<()> { + if self.chroma_format_idc != 1 { + return unsupported(format!("chroma_format_idc {} (only 4:2:0)", self.chroma_format_idc)); + } + if self.bit_depth_luma > 12 || self.bit_depth_chroma > 12 { + return unsupported(format!("bit depth {}/{}", self.bit_depth_luma, self.bit_depth_chroma)); + } + if self.range_extension { + return unsupported("SPS range extension tools"); + } + if self.extension_flags & 0x7f != 0 { + return unsupported("SPS multilayer / 3D / SCC extensions"); + } + Ok(()) + } +} + +/// Picture parameter set. +#[derive(Clone, Debug)] +pub struct Pps { + pub id: u32, + pub sps_id: u32, + pub dependent_slice_segments_enabled: bool, + pub output_flag_present: bool, + pub num_extra_slice_header_bits: u32, + pub sign_data_hiding: bool, + pub cabac_init_present: bool, + pub num_ref_idx_l0_default: u32, + pub num_ref_idx_l1_default: u32, + pub init_qp: i32, + pub constrained_intra_pred: bool, + pub transform_skip: bool, + pub cu_qp_delta_enabled: bool, + pub diff_cu_qp_delta_depth: u32, + pub cb_qp_offset: i32, + pub cr_qp_offset: i32, + pub slice_chroma_qp_offsets_present: bool, + pub weighted_pred: bool, + pub weighted_bipred: bool, + pub transquant_bypass: bool, + pub tiles_enabled: bool, + pub entropy_coding_sync: bool, + pub num_tile_columns: u32, + pub num_tile_rows: u32, + pub uniform_spacing: bool, + /// column_width_minus1 + 1 (explicit spacing only). + pub column_widths: Vec, + pub row_heights: Vec, + pub loop_filter_across_tiles: bool, + pub loop_filter_across_slices: bool, + pub deblocking_control_present: bool, + pub deblocking_override_enabled: bool, + pub deblocking_disabled: bool, + pub beta_offset_div2: i32, + pub tc_offset_div2: i32, + pub scaling_list: Option, + pub lists_modification_present: bool, + pub log2_parallel_merge_level: u32, + pub slice_header_extension_present: bool, + pub range_extension: bool, + pub extension_flags: u8, +} + +impl Pps { + pub fn parse(rbsp: &[u8]) -> Result { + let mut r = BitReader::new(rbsp); + let id = r.read_ue()?; + ensure!(id < 64, "pps_pic_parameter_set_id out of range"); + let sps_id = r.read_ue()?; + ensure!(sps_id < 16, "pps_seq_parameter_set_id out of range"); + let dependent_slice_segments_enabled = r.read_flag()?; + let output_flag_present = r.read_flag()?; + let num_extra_slice_header_bits = r.read_bits(3)?; + let sign_data_hiding = r.read_flag()?; + let cabac_init_present = r.read_flag()?; + let num_ref_idx_l0_default = r.read_ue()? + 1; + let num_ref_idx_l1_default = r.read_ue()? + 1; + ensure!(num_ref_idx_l0_default <= 15 && num_ref_idx_l1_default <= 15, "num_ref_idx_default_active out of range"); + let init_qp = 26 + r.read_se()?; + let constrained_intra_pred = r.read_flag()?; + let transform_skip = r.read_flag()?; + let cu_qp_delta_enabled = r.read_flag()?; + let diff_cu_qp_delta_depth = if cu_qp_delta_enabled { r.read_ue()? } else { 0 }; + ensure!(diff_cu_qp_delta_depth <= 3, "diff_cu_qp_delta_depth out of range"); + let cb_qp_offset = r.read_se()?; + let cr_qp_offset = r.read_se()?; + ensure!((-12..=12).contains(&cb_qp_offset) && (-12..=12).contains(&cr_qp_offset), "chroma QP offset out of range"); + let slice_chroma_qp_offsets_present = r.read_flag()?; + let weighted_pred = r.read_flag()?; + let weighted_bipred = r.read_flag()?; + let transquant_bypass = r.read_flag()?; + let tiles_enabled = r.read_flag()?; + let entropy_coding_sync = r.read_flag()?; + let (mut num_tile_columns, mut num_tile_rows, mut uniform_spacing) = (1, 1, true); + let (mut column_widths, mut row_heights) = (Vec::new(), Vec::new()); + let mut loop_filter_across_tiles = true; + if tiles_enabled { + num_tile_columns = r.read_ue()? + 1; + num_tile_rows = r.read_ue()? + 1; + ensure!(num_tile_columns <= 64 && num_tile_rows <= 64, "too many tiles"); + uniform_spacing = r.read_flag()?; + if !uniform_spacing { + for _ in 0..num_tile_columns - 1 { + column_widths.push(r.read_ue()?.saturating_add(1)); + } + for _ in 0..num_tile_rows - 1 { + row_heights.push(r.read_ue()?.saturating_add(1)); + } + } + loop_filter_across_tiles = r.read_flag()?; + } + let loop_filter_across_slices = r.read_flag()?; + let deblocking_control_present = r.read_flag()?; + let (mut deblocking_override_enabled, mut deblocking_disabled, mut beta_offset_div2, mut tc_offset_div2) = (false, false, 0, 0); + if deblocking_control_present { + deblocking_override_enabled = r.read_flag()?; + deblocking_disabled = r.read_flag()?; + if !deblocking_disabled { + beta_offset_div2 = r.read_se()?; + tc_offset_div2 = r.read_se()?; + ensure!((-6..=6).contains(&beta_offset_div2) && (-6..=6).contains(&tc_offset_div2), "deblocking offsets out of range"); + } + } + let scaling_list = if r.read_flag()? { Some(ScalingList::parse(&mut r)?) } else { None }; + let lists_modification_present = r.read_flag()?; + let log2_parallel_merge_level = r.read_ue()? + 2; + ensure!(log2_parallel_merge_level <= 6, "log2_parallel_merge_level out of range"); + let slice_header_extension_present = r.read_flag()?; + let mut range_extension = false; + let mut extension_flags = 0; + if r.read_flag()? { + extension_flags = r.read_bits(8)? as u8; + if extension_flags & 0x80 != 0 { + // pps_range_extension(): treated as unsupported when it enables anything + let mut any = false; + if transform_skip { + any |= r.read_ue()? != 0; + } + any |= r.read_flag()?; // cross_component_prediction + any |= r.read_flag()?; // chroma_qp_offset_list + range_extension = any; + } + } + Ok(Pps { + id, + sps_id, + dependent_slice_segments_enabled, + output_flag_present, + num_extra_slice_header_bits, + sign_data_hiding, + cabac_init_present, + num_ref_idx_l0_default, + num_ref_idx_l1_default, + init_qp, + constrained_intra_pred, + transform_skip, + cu_qp_delta_enabled, + diff_cu_qp_delta_depth, + cb_qp_offset, + cr_qp_offset, + slice_chroma_qp_offsets_present, + weighted_pred, + weighted_bipred, + transquant_bypass, + tiles_enabled, + entropy_coding_sync, + num_tile_columns, + num_tile_rows, + uniform_spacing, + column_widths, + row_heights, + loop_filter_across_tiles, + loop_filter_across_slices, + deblocking_control_present, + deblocking_override_enabled, + deblocking_disabled, + beta_offset_div2, + tc_offset_div2, + scaling_list, + lists_modification_present, + log2_parallel_merge_level, + slice_header_extension_present, + range_extension, + extension_flags, + }) + } + + pub fn check_supported(&self) -> Result<()> { + if self.range_extension { + return unsupported("PPS range extension tools"); + } + if self.extension_flags & 0x7f != 0 { + return unsupported("PPS multilayer / 3D / SCC extensions"); + } + Ok(()) + } +} + +/// CTB addressing for a PPS/SPS pair: tile boundaries and the raster <-> tile scan conversion +/// (6.5.1). +#[derive(Clone, Debug)] +pub struct Layout { + pub width_ctbs: u32, + pub height_ctbs: u32, + /// Tile column boundaries in CTBs (num_tile_columns + 1 entries). + pub col_bd: Vec, + pub row_bd: Vec, + pub rs_to_ts: Vec, + pub ts_to_rs: Vec, + pub tile_id: Vec, +} + +impl Layout { + pub fn new(sps: &Sps, pps: &Pps) -> Result { + let (w, h) = (sps.pic_width_in_ctbs(), sps.pic_height_in_ctbs()); + let (nc, nr) = (pps.num_tile_columns, pps.num_tile_rows); + ensure!(nc <= w && nr <= h, "more tiles than CTBs"); + let mut colw = Vec::new(); + let mut rowh = Vec::new(); + if pps.uniform_spacing { + for i in 0..nc { + colw.push((i + 1) * w / nc - i * w / nc); + } + for j in 0..nr { + rowh.push((j + 1) * h / nr - j * h / nr); + } + } else { + let sw: u64 = pps.column_widths.iter().map(|&v| v as u64).sum(); + let sh: u64 = pps.row_heights.iter().map(|&v| v as u64).sum(); + ensure!(sw < w as u64 && sh < h as u64, "tile sizes exceed the picture"); + colw.extend_from_slice(&pps.column_widths); + colw.push(w - sw as u32); + rowh.extend_from_slice(&pps.row_heights); + rowh.push(h - sh as u32); + } + let mut col_bd = vec![0]; + let mut acc = 0; + for c in &colw { + acc += c; + col_bd.push(acc); + } + let mut row_bd = vec![0]; + let mut acc = 0; + for r in &rowh { + acc += r; + row_bd.push(acc); + } + let n = (w * h) as usize; + let mut rs_to_ts = vec![0u32; n]; + for rs in 0..w * h { + let (tbx, tby) = (rs % w, rs / w); + let tile_x = (0..nc as usize).rev().find(|&i| tbx >= col_bd[i]).unwrap_or(0); + let tile_y = (0..nr as usize).rev().find(|&j| tby >= row_bd[j]).unwrap_or(0); + let mut v = 0; + for i in 0..tile_x { + v += rowh[tile_y] * colw[i]; + } + for j in 0..tile_y { + v += w * rowh[j]; + } + v += (tby - row_bd[tile_y]) * colw[tile_x] + tbx - col_bd[tile_x]; + rs_to_ts[rs as usize] = v; + } + let mut ts_to_rs = vec![0u32; n]; + for rs in 0..n { + ts_to_rs[rs_to_ts[rs] as usize] = rs as u32; + } + let mut tile_id = vec![0u32; n]; + let mut tid = 0; + for j in 0..nr as usize { + for i in 0..nc as usize { + for y in row_bd[j]..row_bd[j + 1] { + for x in col_bd[i]..col_bd[i + 1] { + tile_id[rs_to_ts[(y * w + x) as usize] as usize] = tid; + } + } + tid += 1; + } + } + Ok(Layout { width_ctbs: w, height_ctbs: h, col_bd, row_bd, rs_to_ts, ts_to_rs, tile_id }) + } + + /// Tile id of the CTB at raster address `rs`. + #[inline] + pub fn tile_of_rs(&self, rs: u32) -> u32 { + self.tile_id[self.rs_to_ts[rs as usize] as usize] + } +} diff --git a/crates/hevc/src/picture.rs b/crates/hevc/src/picture.rs new file mode 100644 index 0000000..1267de8 --- /dev/null +++ b/crates/hevc/src/picture.rs @@ -0,0 +1,220 @@ +//! Decoded picture storage shared between decoding jobs: CTB rows are published once final (after +//! deblocking and SAO) together with the compressed motion field used for temporal motion vector +//! prediction. + +use std::sync::{Arc, OnceLock}; + +/// Motion data of one 16x16 block of a decoded picture (8.5.3.2.9 uses the motion of the top-left +/// 4x4 block of each 16x16 block). +#[derive(Clone, Copy, Debug, Default, PartialEq, Eq)] +pub struct ColMv { + pub mv: [[i16; 2]; 2], + /// POC of the referenced picture per list. + pub ref_poc: [i32; 2], + /// bit0/1: predFlagL0/L1, bit2/3: reference is long-term (L0/L1). 0 = intra / unavailable. + pub flags: u8, +} + +/// One published CTB row. +pub struct FrameRow { + /// Luma lines of the row (stride = picture width). + pub y: Box<[u16]>, + pub cb: Box<[u16]>, + pub cr: Box<[u16]>, + /// Motion per 16x16 block: (width16 * rows16) entries, raster within the row. + pub col: Box<[ColMv]>, +} + +/// A decoded (or in-progress) picture as stored in the DPB and referenced by later pictures. +pub struct Frame { + pub id: u32, + pub poc: i32, + pub width: usize, + pub height: usize, + pub cwidth: usize, + pub cheight: usize, + pub log2_ctb: u32, + pub bit_depth: u32, + pub bit_depth_c: u32, + rows: Box<[OnceLock]>, +} + +impl Frame { + pub fn new(id: u32, poc: i32, width: usize, height: usize, log2_ctb: u32, bit_depth: u32, bit_depth_c: u32) -> Self { + let n = height.div_ceil(1 << log2_ctb); + Frame { + id, + poc, + width, + height, + cwidth: width / 2, + cheight: height / 2, + log2_ctb, + bit_depth, + bit_depth_c, + rows: (0..n).map(|_| OnceLock::new()).collect(), + } + } + + pub fn num_rows(&self) -> usize { + self.rows.len() + } + + /// Number of luma lines in CTB row `r`. + pub fn row_lines(&self, r: usize) -> usize { + let ctb = 1usize << self.log2_ctb; + ctb.min(self.height - r * ctb) + } + + pub fn width16(&self) -> usize { + self.width.div_ceil(16) + } + + /// Publish CTB row `r` (ignored if already published). + pub fn publish(&self, r: usize, row: FrameRow) { + let _ = self.rows[r].set(row); + } + + pub fn is_published(&self, r: usize) -> bool { + self.rows[r].get().is_some() + } + + pub fn is_complete(&self) -> bool { + self.rows.last().is_none_or(|r| r.get().is_some()) + } + + /// CTB row `r`, waiting until it has been published. + #[inline] + pub fn row(&self, r: usize) -> &FrameRow { + #[cfg(feature = "threads")] + { + self.rows[r].wait() + } + #[cfg(not(feature = "threads"))] + { + self.rows[r].get().expect("reference row decoded before use") + } + } + + pub fn wait_complete(&self) { + for r in 0..self.rows.len() { + self.row(r); + } + } + + /// Motion of the 16x16 block containing luma sample (x, y) (inside the picture). + #[inline] + pub fn col_mv(&self, x: usize, y: usize) -> ColMv { + let r = y >> self.log2_ctb; + let row = self.row(r); + let y16 = (y & ((1 << self.log2_ctb) - 1)) >> 4; + row.col[y16 * self.width16() + (x >> 4)] + } + + /// Copy a w x h luma window at (x0, y0) with edge replication into `out` (stride `os`). + pub fn luma_window(&self, x0: i32, y0: i32, w: usize, h: usize, out: &mut [i16], os: usize) { + let (width, height) = (self.width as i32, self.height as i32); + let sh = self.log2_ctb; + let mask = (1i32 << sh) - 1; + for r in 0..h { + let yy = (y0 + r as i32).clamp(0, height - 1); + let row = self.row((yy >> sh) as usize); + let off = ((yy & mask) * width) as usize; + copy_line(&row.y[off..off + width as usize], x0, &mut out[r * os..r * os + w]); + } + } + + /// Copy a w x h window of chroma component `c` (0 = Cb, 1 = Cr) with edge replication. + pub fn chroma_window(&self, c: usize, x0: i32, y0: i32, w: usize, h: usize, out: &mut [i16], os: usize) { + let (cw, ch) = (self.cwidth as i32, self.cheight as i32); + let sh = self.log2_ctb - 1; + let mask = (1i32 << sh) - 1; + for r in 0..h { + let yy = (y0 + r as i32).clamp(0, ch - 1); + let row = self.row((yy >> sh) as usize); + let plane = if c == 0 { &row.cb } else { &row.cr }; + let off = ((yy & mask) * cw) as usize; + copy_line(&plane[off..off + cw as usize], x0, &mut out[r * os..r * os + w]); + } + } + + /// Crop and copy the frame into planar buffers (waits for completion). + pub fn copy_cropped(&self, crop: (usize, usize, usize, usize)) -> (Vec, Vec, Vec) { + let (cx, cy, cw, ch) = crop; + let ctb = 1usize << self.log2_ctb; + let mut y = Vec::with_capacity(cw * ch); + for r in cy..cy + ch { + let row = self.row(r / ctb); + let o = (r % ctb) * self.width + cx; + y.extend_from_slice(&row.y[o..o + cw]); + } + let (ccx, ccy, ccw, cch) = (cx / 2, cy / 2, cw.div_ceil(2), ch.div_ceil(2)); + let cctb = ctb / 2; + let mut u = Vec::with_capacity(ccw * cch); + let mut v = Vec::with_capacity(ccw * cch); + for r in ccy..ccy + cch { + let row = self.row(r / cctb); + let o = (r % cctb) * self.cwidth + ccx; + u.extend_from_slice(&row.cb[o..o + ccw]); + v.extend_from_slice(&row.cr[o..o + ccw]); + } + (y, u, v) + } + + /// A frame with every row published from full-picture planes and no motion (used to replace missing + /// reference pictures). + pub fn from_planes(id: u32, poc: i32, like: &Frame, y: &[u16], cb: &[u16], cr: &[u16]) -> Frame { + let f = Frame::new(id, poc, like.width, like.height, like.log2_ctb, like.bit_depth, like.bit_depth_c); + let ctb = 1usize << f.log2_ctb; + let w16 = f.width16(); + for r in 0..f.num_rows() { + let lines = f.row_lines(r); + let (y0, y1) = (r * ctb * f.width, (r * ctb + lines) * f.width); + let clines = lines.div_ceil(2); + let (c0, c1) = (r * ctb / 2 * f.cwidth, (r * ctb / 2 + clines) * f.cwidth); + f.publish( + r, + FrameRow { + y: y[y0..y1].into(), + cb: cb[c0..c1].into(), + cr: cr[c0..c1].into(), + col: vec![ColMv::default(); w16 * lines.div_ceil(16)].into(), + }, + ); + } + f + } + + /// A mid-grey frame with the geometry of `like`. + pub fn gray(id: u32, poc: i32, like: &Frame) -> Frame { + let y = vec![1u16 << (like.bit_depth - 1); like.width * like.height]; + let c = vec![1u16 << (like.bit_depth_c - 1); like.cwidth * like.cheight]; + Frame::from_planes(id, poc, like, &y, &c, &c) + } +} + +/// Copy `out.len()` samples of `line` starting at x0, replicating edge samples outside the line. +#[inline(always)] +fn copy_line(line: &[u16], x0: i32, out: &mut [i16]) { + let w = out.len(); + if x0 >= 0 && x0 as usize + w <= line.len() { + for (o, &s) in out.iter_mut().zip(&line[x0 as usize..x0 as usize + w]) { + *o = s as i16; + } + } else { + let max = line.len() as i32 - 1; + for (c, o) in out.iter_mut().enumerate() { + *o = line[(x0 + c as i32).clamp(0, max) as usize] as i16; + } + } +} + +pub type FrameRef = Arc; + +/// An entry of RefPicList0/1 of the current slice. +#[derive(Clone)] +pub struct RefPic { + pub frame: FrameRef, + pub poc: i32, + pub long_term: bool, +} diff --git a/crates/hevc/src/slice.rs b/crates/hevc/src/slice.rs new file mode 100644 index 0000000..976b1ba --- /dev/null +++ b/crates/hevc/src/slice.rs @@ -0,0 +1,434 @@ +//! NAL unit header (7.3.1.2) and slice segment header (7.3.6). + +use crate::error::{Result, ensure, invalid}; +use crate::params::{Pps, Sps, StRps}; +use slidecraft_bitstream::BitReader; + +pub mod nal_type { + pub const TRAIL_N: u8 = 0; + pub const RADL_N: u8 = 6; + pub const RADL_R: u8 = 7; + pub const RASL_N: u8 = 8; + pub const RASL_R: u8 = 9; + pub const RSV_VCL_N14: u8 = 14; + pub const BLA_W_LP: u8 = 16; + pub const BLA_W_RADL: u8 = 17; + pub const BLA_N_LP: u8 = 18; + pub const IDR_W_RADL: u8 = 19; + pub const IDR_N_LP: u8 = 20; + pub const CRA: u8 = 21; + pub const RSV_IRAP_23: u8 = 23; + pub const VPS: u8 = 32; + pub const SPS: u8 = 33; + pub const PPS: u8 = 34; + pub const AUD: u8 = 35; + pub const EOS: u8 = 36; + pub const EOB: u8 = 37; + pub const FD: u8 = 38; + pub const PREFIX_SEI: u8 = 39; + pub const SUFFIX_SEI: u8 = 40; +} + +#[derive(Clone, Copy, Debug, PartialEq, Eq)] +pub struct NalHeader { + pub nal_type: u8, + pub layer_id: u8, + pub temporal_id: u8, +} + +impl NalHeader { + pub fn parse(b: &[u8]) -> Result { + ensure!(b.len() >= 2, "NAL unit too short"); + ensure!(b[0] & 0x80 == 0, "forbidden_zero_bit set"); + let tid = b[1] & 7; + ensure!(tid != 0, "nuh_temporal_id_plus1 is 0"); + Ok(NalHeader { nal_type: (b[0] >> 1) & 0x3f, layer_id: ((b[0] & 1) << 5) | (b[1] >> 3), temporal_id: tid - 1 }) + } + pub fn is_vcl(&self) -> bool { + self.nal_type < 32 + } + pub fn is_irap(&self) -> bool { + (nal_type::BLA_W_LP..=nal_type::RSV_IRAP_23).contains(&self.nal_type) + } + pub fn is_idr(&self) -> bool { + self.nal_type == nal_type::IDR_W_RADL || self.nal_type == nal_type::IDR_N_LP + } + pub fn is_bla(&self) -> bool { + (nal_type::BLA_W_LP..=nal_type::BLA_N_LP).contains(&self.nal_type) + } + pub fn is_cra(&self) -> bool { + self.nal_type == nal_type::CRA + } + pub fn is_rasl(&self) -> bool { + self.nal_type == nal_type::RASL_N || self.nal_type == nal_type::RASL_R + } + pub fn is_radl(&self) -> bool { + self.nal_type == nal_type::RADL_N || self.nal_type == nal_type::RADL_R + } + /// Sub-layer non-reference picture (TRAIL_N, TSA_N, ..., RSV_VCL_N14). + pub fn is_sub_layer_non_ref(&self) -> bool { + self.nal_type <= nal_type::RSV_VCL_N14 && self.nal_type.is_multiple_of(2) + } +} + +#[derive(Clone, Copy, Debug, PartialEq, Eq)] +pub enum SliceType { + B = 0, + P = 1, + I = 2, +} + +/// Explicit weighted prediction parameters for one reference (7.4.7.3), already derived. +#[derive(Clone, Copy, Debug, Default, PartialEq, Eq)] +pub struct WpEntry { + /// LumaWeightLX, luma_offset_lX (8-bit units). + pub luma: (i32, i32), + pub luma_flag: bool, + /// ChromaWeightLX, ChromaOffsetLX (8-bit units) for Cb and Cr. + pub chroma: [(i32, i32); 2], + pub chroma_flag: bool, +} + +#[derive(Clone, Debug, Default, PartialEq, Eq)] +pub struct PredWeightTable { + pub luma_log2_denom: u32, + pub chroma_log2_denom: u32, + pub l: [Vec; 2], +} + +#[derive(Clone, Debug)] +pub struct SliceHeader { + pub nal: NalHeader, + pub first_slice_segment_in_pic: bool, + pub no_output_of_prior_pics: bool, + pub pps_id: u32, + pub dependent: bool, + pub segment_address: u32, + pub slice_type: SliceType, + pub pic_output: bool, + pub poc_lsb: u32, + /// The short-term RPS used by this picture (from the SPS or the header). + pub st_rps: StRps, + pub st_rps_bits: usize, + /// Long-term entries: (PocLsbLt, UsedByCurrPicLt, delta_poc_msb_present, DeltaPocMsbCycleLt). + pub lt: Vec<(u32, bool, bool, u32)>, + pub temporal_mvp: bool, + pub sao_luma: bool, + pub sao_chroma: bool, + pub num_ref_idx: [u32; 2], + pub list_entry: [Option>; 2], + pub mvd_l1_zero: bool, + pub cabac_init_flag: bool, + pub collocated_from_l0: bool, + pub collocated_ref_idx: u32, + pub pwt: Option, + pub max_num_merge_cand: u32, + pub qp_delta: i32, + pub cb_qp_offset: i32, + pub cr_qp_offset: i32, + pub deblocking_disabled: bool, + pub beta_offset_div2: i32, + pub tc_offset_div2: i32, + pub loop_filter_across_slices: bool, + pub entry_points: Vec, + /// Byte offset of slice_segment_data() in the RBSP. + pub data_offset: usize, +} + +fn ceil_log2(n: u32) -> u32 { + if n <= 1 { 0 } else { 32 - (n - 1).leading_zeros() } +} + +impl SliceHeader { + pub fn is_intra(&self) -> bool { + self.slice_type == SliceType::I + } + pub fn is_b(&self) -> bool { + self.slice_type == SliceType::B + } + + /// NumPicTotalCurr (7-55), without pps_curr_pic_ref. + pub fn num_pic_total_curr(&self) -> u32 { + let st = self.st_rps.s0.iter().chain(self.st_rps.s1.iter()).filter(|e| e.1).count(); + let lt = self.lt.iter().filter(|e| e.1).count(); + (st + lt) as u32 + } + + /// Parse a slice segment header. `prev` is the header of the preceding independent slice segment + /// of the same picture (needed for dependent slice segments). + pub fn parse( + rbsp: &[u8], + nal: NalHeader, + lookup: impl FnOnce(u32) -> Result<(std::sync::Arc, std::sync::Arc)>, + prev: Option<&SliceHeader>, + ) -> Result<(SliceHeader, std::sync::Arc, std::sync::Arc)> { + let mut r = BitReader::new(rbsp); + let first = r.read_flag()?; + let no_output_of_prior_pics = if nal.is_irap() { r.read_flag()? } else { false }; + let pps_id = r.read_ue()?; + ensure!(pps_id < 64, "slice_pic_parameter_set_id out of range"); + let (pps, sps) = lookup(pps_id)?; + let mut dependent = false; + let mut segment_address = 0; + if !first { + if pps.dependent_slice_segments_enabled { + dependent = r.read_flag()?; + } + let pic_size = sps.pic_width_in_ctbs() * sps.pic_height_in_ctbs(); + segment_address = r.read_bits(ceil_log2(pic_size))?; + ensure!(segment_address < pic_size, "slice_segment_address out of range"); + } + let mut sh = if dependent { + let Some(p) = prev else { return invalid("dependent slice segment without a preceding slice") }; + let mut s = p.clone(); + s.nal = nal; + s + } else { + SliceHeader { + nal, + first_slice_segment_in_pic: first, + no_output_of_prior_pics, + pps_id, + dependent: false, + segment_address, + slice_type: SliceType::I, + pic_output: true, + poc_lsb: 0, + st_rps: StRps::default(), + st_rps_bits: 0, + lt: Vec::new(), + temporal_mvp: false, + sao_luma: false, + sao_chroma: false, + num_ref_idx: [0, 0], + list_entry: [None, None], + mvd_l1_zero: false, + cabac_init_flag: false, + collocated_from_l0: true, + collocated_ref_idx: 0, + pwt: None, + max_num_merge_cand: 5, + qp_delta: 0, + cb_qp_offset: 0, + cr_qp_offset: 0, + deblocking_disabled: pps.deblocking_disabled, + beta_offset_div2: pps.beta_offset_div2, + tc_offset_div2: pps.tc_offset_div2, + loop_filter_across_slices: pps.loop_filter_across_slices, + entry_points: Vec::new(), + data_offset: 0, + } + }; + sh.first_slice_segment_in_pic = first; + sh.no_output_of_prior_pics = no_output_of_prior_pics; + sh.pps_id = pps_id; + sh.dependent = dependent; + sh.segment_address = segment_address; + if !dependent { + r.skip(pps.num_extra_slice_header_bits as usize)?; + let st = r.read_ue()?; + sh.slice_type = match st { + 0 => SliceType::B, + 1 => SliceType::P, + 2 => SliceType::I, + _ => return invalid(format!("slice_type {st}")), + }; + if nal.is_irap() { + ensure!(sh.slice_type == SliceType::I, "IRAP picture with inter slice"); + } + if pps.output_flag_present { + sh.pic_output = r.read_flag()?; + } + if sps.separate_colour_plane { + r.skip(2)?; + } + if !nal.is_idr() { + sh.poc_lsb = r.read_bits(sps.log2_max_poc_lsb)?; + let from_sps = r.read_flag()?; + if !from_sps { + let start = r.position(); + sh.st_rps = StRps::parse(&mut r, sps.st_rps.len(), &sps.st_rps, sps.st_rps.len())?; + sh.st_rps_bits = r.position() - start; + } else { + ensure!(!sps.st_rps.is_empty(), "no short-term RPS in the SPS"); + let idx = if sps.st_rps.len() > 1 { r.read_bits(ceil_log2(sps.st_rps.len() as u32))? as usize } else { 0 }; + ensure!(idx < sps.st_rps.len(), "short_term_ref_pic_set_idx out of range"); + sh.st_rps = sps.st_rps[idx].clone(); + } + if sps.long_term_refs_present { + let num_lt_sps = if !sps.lt_ref_pics.is_empty() { r.read_ue()? } else { 0 }; + ensure!(num_lt_sps as usize <= sps.lt_ref_pics.len(), "num_long_term_sps out of range"); + let num_lt_pics = r.read_ue()?; + ensure!(num_lt_sps + num_lt_pics <= 32, "too many long-term pictures"); + let mut msb_acc = 0u32; + for i in 0..num_lt_sps + num_lt_pics { + let (lsb, used) = if i < num_lt_sps { + let idx = if sps.lt_ref_pics.len() > 1 { r.read_bits(ceil_log2(sps.lt_ref_pics.len() as u32))? } else { 0 }; + ensure!((idx as usize) < sps.lt_ref_pics.len(), "lt_idx_sps out of range"); + sps.lt_ref_pics[idx as usize] + } else { + (r.read_bits(sps.log2_max_poc_lsb)?, r.read_flag()?) + }; + let msb_present = r.read_flag()?; + let cycle = if msb_present { r.read_ue()? } else { 0 }; + // DeltaPocMsbCycleLt accumulates within each of the two groups (7-52) + if i == 0 || i == num_lt_sps { + msb_acc = cycle; + } else { + msb_acc = msb_acc.wrapping_add(cycle); + } + sh.lt.push((lsb, used, msb_present, msb_acc)); + } + } + if sps.temporal_mvp { + sh.temporal_mvp = r.read_flag()?; + } + } + if sps.sao { + sh.sao_luma = r.read_flag()?; + if sps.chroma_array_type() != 0 { + sh.sao_chroma = r.read_flag()?; + } + } + if sh.slice_type != SliceType::I { + sh.num_ref_idx = [pps.num_ref_idx_l0_default, if sh.is_b() { pps.num_ref_idx_l1_default } else { 0 }]; + if r.read_flag()? { + sh.num_ref_idx[0] = r.read_ue()? + 1; + if sh.is_b() { + sh.num_ref_idx[1] = r.read_ue()? + 1; + } + } + ensure!(sh.num_ref_idx[0] <= 15 && sh.num_ref_idx[1] <= 15, "num_ref_idx_active out of range"); + let total = sh.num_pic_total_curr(); + ensure!(total > 0, "inter slice without reference pictures in the RPS"); + if pps.lists_modification_present && total > 1 { + let bits = ceil_log2(total); + for l in 0..if sh.is_b() { 2 } else { 1 } { + if r.read_flag()? { + let mut e = Vec::new(); + for _ in 0..sh.num_ref_idx[l] { + let v = r.read_bits(bits)?; + ensure!(v < total, "list_entry out of range"); + e.push(v); + } + sh.list_entry[l] = Some(e); + } + } + } + if sh.is_b() { + sh.mvd_l1_zero = r.read_flag()?; + } + if pps.cabac_init_present { + sh.cabac_init_flag = r.read_flag()?; + } + if sh.temporal_mvp { + if sh.is_b() { + sh.collocated_from_l0 = r.read_flag()?; + } + let l = if sh.collocated_from_l0 { 0 } else { 1 }; + if sh.num_ref_idx[l] > 1 { + sh.collocated_ref_idx = r.read_ue()?; + ensure!(sh.collocated_ref_idx < sh.num_ref_idx[l], "collocated_ref_idx out of range"); + } + } + if (pps.weighted_pred && sh.slice_type == SliceType::P) || (pps.weighted_bipred && sh.is_b()) { + sh.pwt = Some(parse_pwt(&mut r, &sh, &sps)?); + } + let five_minus = r.read_ue()?; + ensure!(five_minus <= 4, "five_minus_max_num_merge_cand out of range"); + sh.max_num_merge_cand = 5 - five_minus; + } + sh.qp_delta = r.read_se()?; + let qp = pps.init_qp + sh.qp_delta; + let qp_bd = 6 * (sps.bit_depth_luma as i32 - 8); + ensure!((-qp_bd..=51).contains(&qp), "SliceQpY {qp} out of range"); + if pps.slice_chroma_qp_offsets_present { + sh.cb_qp_offset = r.read_se()?; + sh.cr_qp_offset = r.read_se()?; + ensure!((-12..=12).contains(&sh.cb_qp_offset) && (-12..=12).contains(&sh.cr_qp_offset), "slice chroma QP offset out of range"); + } + let override_flag = if pps.deblocking_override_enabled { r.read_flag()? } else { false }; + if override_flag { + sh.deblocking_disabled = r.read_flag()?; + if !sh.deblocking_disabled { + sh.beta_offset_div2 = r.read_se()?; + sh.tc_offset_div2 = r.read_se()?; + ensure!((-6..=6).contains(&sh.beta_offset_div2) && (-6..=6).contains(&sh.tc_offset_div2), "deblocking offsets out of range"); + } + } + if pps.loop_filter_across_slices && (sh.sao_luma || sh.sao_chroma || !sh.deblocking_disabled) { + sh.loop_filter_across_slices = r.read_flag()?; + } + } + sh.entry_points.clear(); + if pps.tiles_enabled || pps.entropy_coding_sync { + let n = r.read_ue()?; + ensure!(n <= sps.pic_width_in_ctbs() * sps.pic_height_in_ctbs(), "num_entry_point_offsets out of range"); + if n > 0 { + let len = r.read_ue()? + 1; + ensure!(len <= 32, "offset_len_minus1 out of range"); + for _ in 0..n { + sh.entry_points.push(r.read_bits(len)? + 1); + } + } + } + if pps.slice_header_extension_present { + let len = r.read_ue()?; + ensure!(len <= 256, "slice_segment_header_extension_length out of range"); + r.skip(8 * len as usize)?; + } + // byte_alignment(): alignment_bit_equal_to_one then zeros + ensure!(r.read_flag()?, "missing alignment_bit_equal_to_one"); + r.byte_align(); + sh.data_offset = r.position() / 8; + ensure!(sh.data_offset <= rbsp.len(), "slice header runs past the NAL unit"); + Ok((sh, pps, sps)) + } +} + +fn parse_pwt(r: &mut BitReader, sh: &SliceHeader, sps: &Sps) -> Result { + let mut t = PredWeightTable { luma_log2_denom: r.read_ue()?, ..Default::default() }; + ensure!(t.luma_log2_denom <= 7, "luma_log2_weight_denom out of range"); + let chroma = sps.chroma_array_type() != 0; + if chroma { + let d = t.luma_log2_denom as i32 + r.read_se()?; + ensure!((0..=7).contains(&d), "ChromaLog2WeightDenom out of range"); + t.chroma_log2_denom = d as u32; + } + let lists = if sh.is_b() { 2 } else { 1 }; + for l in 0..lists { + let n = sh.num_ref_idx[l] as usize; + let mut e = vec![WpEntry::default(); n]; + for x in e.iter_mut() { + x.luma_flag = r.read_flag()?; + } + if chroma { + for x in e.iter_mut() { + x.chroma_flag = r.read_flag()?; + } + } + for x in e.iter_mut() { + x.luma = (1 << t.luma_log2_denom, 0); + if x.luma_flag { + let dw = r.read_se()?; + let off = r.read_se()?; + ensure!((-128..=127).contains(&dw) && (-128..=127).contains(&off), "luma weight out of range"); + x.luma = ((1 << t.luma_log2_denom) + dw, off); + } + x.chroma = [(1 << t.chroma_log2_denom, 0); 2]; + if x.chroma_flag { + let half = 128; // wpOffsetHalfRangeC (no high_precision_offsets) + for c in 0..2 { + let dw = r.read_se()?; + let doff = r.read_se()?; + ensure!((-128..=127).contains(&dw) && (-4 * half..4 * half).contains(&doff), "chroma weight out of range"); + let w = (1 << t.chroma_log2_denom) + dw; + let off = (half - ((half * w) >> t.chroma_log2_denom) + doff).clamp(-half, half - 1); + x.chroma[c] = (w, off); + } + } + } + t.l[l] = e; + } + Ok(t) +} diff --git a/crates/hevc/src/slicedec.rs b/crates/hevc/src/slicedec.rs new file mode 100644 index 0000000..f52910b --- /dev/null +++ b/crates/hevc/src/slicedec.rs @@ -0,0 +1,1511 @@ +//! Slice segment data decoding (7.3.8) with reconstruction: CTU / coding quadtree / coding unit / +//! prediction unit / transform tree parsing (CABAC, 9.3) interleaved with intra prediction, motion +//! compensation, scaling and inverse transforms. In-loop filters and row publication live in +//! `filter.rs`. + +use crate::cabac::{Cabac, Contexts, init_contexts}; +use crate::error::{Result, ensure, invalid}; +use crate::inter; +use crate::intra::{self, Refs}; +use crate::params::{Layout, Pps, Sps}; +use crate::picture::{FrameRef, RefPic}; +use crate::slice::{SliceHeader, SliceType}; +use crate::spec_tables::*; +use crate::tables::{CTX_IDX_MAP, LEVEL_SCALE, SCAN_4, qpc_420, scan_order}; +use crate::transform; +use std::sync::Arc; + +pub const F_INTRA: u8 = 1; +pub const F_SKIP: u8 = 2; +/// PCM with pcm_loop_filter_disabled_flag, or cu_transquant_bypass: in-loop filters leave the samples alone. +pub const F_NOFILTER: u8 = 4; +/// The luma transform block covering this 4x4 block has non-zero coefficients. +pub const F_CBF: u8 = 8; +/// Block has been decoded. +pub const F_CODED: u8 = 16; + +pub const E_TU_V: u8 = 1; +pub const E_PU_V: u8 = 2; +pub const E_TU_H: u8 = 4; +pub const E_PU_H: u8 = 8; + +/// Per 4x4 luma block state. +#[derive(Clone, Copy, Default)] +pub struct Blk { + pub flags: u8, + pub depth: u8, + pub qp: i8, + /// IntraPredModeY (DC for non-intra / PCM blocks, as used by the MPM derivation). + pub ipm: u8, + pub edges: u8, +} + +/// Motion of a 4x4 block. +#[derive(Clone, Copy, PartialEq, Eq, Debug)] +pub struct MvField { + pub mv: [[i16; 2]; 2], + pub ref_idx: [i8; 2], +} + +impl Default for MvField { + fn default() -> Self { + MvField { mv: [[0; 2]; 2], ref_idx: [-1, -1] } + } +} + +impl MvField { + #[inline] + pub fn pred(&self, l: usize) -> bool { + self.ref_idx[l] >= 0 + } +} + +/// SAO parameters of one CTB. +#[derive(Clone, Copy, Default, Debug)] +pub struct SaoParams { + pub type_idx: [u8; 3], + pub band_pos: [u8; 3], + pub eo_class: [u8; 3], + /// SaoOffsetVal[1..4]. + pub offsets: [[i16; 4]; 3], +} + +/// Slice-level data needed after parsing (in-loop filters, collocated motion). +pub struct SliceInfo { + pub addr_rs: u32, + pub deblock_disabled: bool, + pub beta_offset: i32, + pub tc_offset: i32, + pub lf_across: bool, + pub sao_luma: bool, + pub sao_chroma: bool, + /// Frame ids, POCs and long-term flags of RefPicList0/1. + pub ref_ids: [Vec; 2], + pub ref_pocs: [Vec; 2], + pub ref_lt: [Vec; 2], +} + +/// One slice segment of a picture job. +pub struct SliceJob { + pub sh: SliceHeader, + pub pps: Arc, + pub sps: Arc, + pub rbsp: Vec, + pub refs: [Vec; 2], +} + +/// Per-picture decoding state. +pub struct PicState { + pub frame: FrameRef, + #[allow(dead_code)] + pub sps: Arc, + pub pps: Arc, + pub layout: Arc, + pub poc: i32, + pub width: usize, + pub height: usize, + pub cwidth: usize, + pub cheight: usize, + pub w4: usize, + pub h4: usize, + pub log2_ctb: u32, + pub wctb: usize, + pub hctb: usize, + pub bd_y: u32, + pub bd_c: u32, + /// Reconstructed (pre-SAO) planes. + pub planes: [Vec; 3], + pub blk: Vec, + pub mvf: Vec, + /// Index into `slices` per CTB (u32::MAX = not decoded). + pub ctb_slice: Vec, + /// SliceAddrRs per CTB (u32::MAX = not decoded). + pub ctb_addr: Vec, + pub sao: Vec, + pub slices: Vec, + pub row_done: Vec, + pub rows_complete: usize, + pub rows_deblocked: usize, + pub rows_published: usize, + pub wpp_ctx: Option>, + pub ds_ctx: Option<(Box, i32)>, + /// Scaling factors [sizeId][matrixId] when scaling lists are enabled. + pub scaling: Option>>>, + pub deblocking_enabled_anywhere: bool, + /// Draft mode: no deblocking or SAO. + pub draft: bool, +} + +impl PicState { + pub fn new(frame: FrameRef, sps: Arc, pps: Arc, layout: Arc, scaling: Option>>>) -> Self { + let (width, height) = (sps.width as usize, sps.height as usize); + let (cwidth, cheight) = (width / 2, height / 2); + let (w4, h4) = (width.div_ceil(4), height.div_ceil(4)); + let log2_ctb = sps.log2_ctb; + let (wctb, hctb) = (sps.pic_width_in_ctbs() as usize, sps.pic_height_in_ctbs() as usize); + let (bd_y, bd_c) = (sps.bit_depth_luma, sps.bit_depth_chroma); + let gy = 1u16 << (bd_y - 1); + let gc = 1u16 << (bd_c - 1); + PicState { + poc: frame.poc, + frame, + sps, + pps, + layout, + width, + height, + cwidth, + cheight, + w4, + h4, + log2_ctb, + wctb, + hctb, + bd_y, + bd_c, + planes: [vec![gy; width * height], vec![gc; cwidth * cheight], vec![gc; cwidth * cheight]], + blk: vec![Blk::default(); w4 * h4], + mvf: vec![MvField::default(); w4 * h4], + ctb_slice: vec![u32::MAX; wctb * hctb], + ctb_addr: vec![u32::MAX; wctb * hctb], + sao: vec![SaoParams::default(); wctb * hctb], + slices: Vec::new(), + row_done: vec![0; hctb], + rows_complete: 0, + rows_deblocked: 0, + rows_published: 0, + wpp_ctx: None, + ds_ctx: None, + scaling, + deblocking_enabled_anywhere: false, + draft: false, + } + } + + /// z-scan order availability (6.4.1) of luma location (xn, yn) for the block at (xc, yc) in the slice + /// with SliceAddrRs `addr`. + #[inline] + pub fn zavail(&self, xc: i32, yc: i32, xn: i32, yn: i32, addr: u32) -> bool { + if xn < 0 || yn < 0 || xn >= self.width as i32 || yn >= self.height as i32 { + return false; + } + let s = self.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 { + let tsn = self.layout.rs_to_ts[ctb_n]; + let tsc = self.layout.rs_to_ts[ctb_c]; + tsn < tsc && self.ctb_addr[ctb_n] == addr && self.layout.tile_id[tsn as usize] == self.layout.tile_id[tsc as usize] + } 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] + pub fn blk_at(&self, x: i32, y: i32) -> &Blk { + &self.blk[(y as usize >> 2) * self.w4 + (x as usize >> 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 +} + +#[derive(Clone, Copy, PartialEq, Eq, Debug)] +pub enum PartMode { + P2Nx2N, + P2NxN, + PNx2N, + PNxN, + P2NxnU, + P2NxnD, + PnLx2N, + PnRx2N, +} + +/// Decoder of one slice segment. +pub struct SliceDecoder<'a> { + pub pic: &'a mut PicState, + pub sh: &'a SliceHeader, + pub sps: &'a Sps, + pub pps: &'a Pps, + pub refs: &'a [Vec; 2], + pub c: Cabac<'a>, + pub slice_idx: u32, + pub slice_addr: u32, + pub slice_qp: i32, + pub init_type: usize, + // quantization state + pub qp_y: i32, + pub qp_y_pred: i32, + pub last_qp: i32, + pub is_cu_qp_delta_coded: bool, + pub cu_qp_delta: i32, + pub log2_min_cu_qp_delta: u32, + pub qp_cb: i32, + pub qp_cr: i32, + // current CU + pub cu_transquant_bypass: bool, + pub cu_intra: bool, + pub intra_chroma_mode: u32, + pub max_trafo_depth: u32, + pub intra_split: bool, + pub cu_part: PartMode, + // scratch + pub coeffs: Vec, + pub pred0: Vec, + pub pred1: Vec, + pub mc: crate::inter::McScratch, + pub refs_buf: Refs, + /// Collocated picture for TMVP. + pub col: Option, + /// NoBackwardPredFlag. + pub no_backward_pred: bool, + pub ctb_x: i32, + pub ctb_y: i32, +} + +impl<'a> SliceDecoder<'a> { + #[inline(always)] + fn dd(&mut self, ctx: usize) -> u32 { + self.c.decode_decision(ctx) + } + #[inline(always)] + fn bypass(&mut self) -> u32 { + self.c.decode_bypass() + } + #[inline] + fn bypass_bits(&mut self, n: u32) -> u32 { + let mut v = 0u32; + let mut left = n; + while left > 16 { + v = (v << 16) | self.c.decode_bypass_bits(16); + left -= 16; + } + // (bits beyond 32 shift out, as the bin-by-bin loop did) + v.checked_shl(left).unwrap_or(0) | self.c.decode_bypass_bits(left) + } + + /// Decode the slice segment `job` into `pic`. + pub fn decode(pic: &'a mut PicState, job: &'a SliceJob) -> Result<()> { + let sh = &job.sh; + let (sps, pps) = (&*job.sps, &*job.pps); + ensure!(sh.data_offset < job.rbsp.len(), "empty slice segment data"); + let slice_qp = pps.init_qp + sh.qp_delta; + let init_type = match sh.slice_type { + SliceType::I => 0, + SliceType::P => { + if sh.cabac_init_flag { + 2 + } else { + 1 + } + } + SliceType::B => { + if sh.cabac_init_flag { + 1 + } else { + 2 + } + } + }; + // SliceAddrRs: address of the first CTB of the (independent) slice + let slice_addr = if sh.dependent { + match pic.slices.last() { + Some(s) => s.addr_rs, + None => return invalid("dependent slice segment without a preceding slice"), + } + } else { + sh.segment_address + }; + if !sh.dependent { + let ids = |l: usize| job.refs[l].iter().map(|r| r.frame.id).collect::>(); + let pocs = |l: usize| job.refs[l].iter().map(|r| r.poc).collect::>(); + let lts = |l: usize| job.refs[l].iter().map(|r| r.long_term).collect::>(); + pic.slices.push(SliceInfo { + addr_rs: slice_addr, + deblock_disabled: sh.deblocking_disabled, + beta_offset: sh.beta_offset_div2 * 2, + tc_offset: sh.tc_offset_div2 * 2, + lf_across: sh.loop_filter_across_slices, + sao_luma: sh.sao_luma, + sao_chroma: sh.sao_chroma, + ref_ids: [ids(0), ids(1)], + ref_pocs: [pocs(0), pocs(1)], + ref_lt: [lts(0), lts(1)], + }); + if !sh.deblocking_disabled { + pic.deblocking_enabled_anywhere = true; + } + } + let slice_idx = (pic.slices.len() - 1) as u32; + let col = if sh.temporal_mvp && !sh.is_intra() { + let l = if sh.is_b() && !sh.collocated_from_l0 { 1 } else { 0 }; + job.refs[l].get(sh.collocated_ref_idx as usize).cloned() + } else { + None + }; + let no_backward_pred = job.refs.iter().flatten().all(|r| r.poc <= pic.poc); + let log2_min_cu_qp_delta = sps.log2_ctb - pps.diff_cu_qp_delta_depth; + let c = Cabac::new(&job.rbsp, sh.data_offset, slice_qp, init_type)?; + let mut d = SliceDecoder { + pic, + sh, + sps, + pps, + refs: &job.refs, + c, + slice_idx, + slice_addr, + slice_qp, + init_type, + qp_y: slice_qp, + qp_y_pred: slice_qp, + last_qp: slice_qp, + is_cu_qp_delta_coded: false, + cu_qp_delta: 0, + log2_min_cu_qp_delta, + qp_cb: 0, + qp_cr: 0, + cu_transquant_bypass: false, + cu_intra: false, + intra_chroma_mode: 0, + max_trafo_depth: 0, + intra_split: false, + cu_part: PartMode::P2Nx2N, + coeffs: vec![0; 32 * 32], + pred0: vec![0; 64 * 64], + pred1: vec![0; 64 * 64], + mc: Default::default(), + refs_buf: Refs::default(), + col, + no_backward_pred, + ctb_x: 0, + ctb_y: 0, + }; + d.decode_data() + } + + fn decode_data(&mut self) -> Result<()> { + let layout = self.pic.layout.clone(); + let wctb = self.pic.wctb as u32; + let npic = (self.pic.wctb * self.pic.hctb) as u32; + let mut ts = layout.rs_to_ts[self.sh.segment_address as usize]; + let mut first = true; + loop { + let rs = layout.ts_to_rs[ts as usize]; + ensure!(self.pic.ctb_slice[rs as usize] == u32::MAX, "CTB {rs} decoded twice"); + let (rx, ry) = ((rs % wctb) as i32, (rs / wctb) as i32); + let ctb = 1i32 << self.sps.log2_ctb; + self.ctb_x = rx * ctb; + self.ctb_y = ry * ctb; + self.pic.ctb_slice[rs as usize] = self.slice_idx; + self.pic.ctb_addr[rs as usize] = self.slice_addr; + // context initialisation / synchronisation (9.3.1, 9.3.2.1) + let first_in_tile = ts == 0 || layout.tile_id[ts as usize] != layout.tile_id[ts as usize - 1]; + let wpp_row_start = self.pps.entropy_coding_sync + && (rs.is_multiple_of(wctb) || layout.tile_id[ts as usize] != layout.tile_id[layout.rs_to_ts[rs as usize - 1] as usize]); + if first_in_tile { + if !first { + init_contexts(&mut self.c.ctx, self.slice_qp, self.init_type); + } + self.last_qp = self.slice_qp; + } else if wpp_row_start { + let (x0, y0) = (self.ctb_x, self.ctb_y); + let avail = self.pic.zavail(x0, y0, x0 + ctb, y0 - ctb, self.slice_addr); + match (&self.pic.wpp_ctx, avail) { + (Some(saved), true) => self.c.ctx = **saved, + _ => init_contexts(&mut self.c.ctx, self.slice_qp, self.init_type), + } + self.last_qp = self.slice_qp; + } else if first && self.sh.dependent { + match &self.pic.ds_ctx { + Some((saved, qp)) => { + self.c.ctx = **saved; + self.last_qp = *qp; + } + None => return invalid("dependent slice segment without stored contexts"), + } + } + first = false; + if self.sh.sao_luma || self.sh.sao_chroma { + self.parse_sao(rx, ry, rs)?; + } + self.coding_quadtree(self.ctb_x, self.ctb_y, self.sps.log2_ctb, 0)?; + let end = self.c.decode_terminate() == 1; + ensure!(!self.c.overrun(), "slice data overrun"); + // WPP storage after the second CTB of a row in a tile (9.3.1) + if self.pps.entropy_coding_sync + && (rs % wctb == 1 || (rs > 1 && layout.tile_id[ts as usize] != layout.tile_id[layout.rs_to_ts[rs as usize - 2] as usize])) + { + self.pic.wpp_ctx = Some(Box::new(self.c.ctx)); + } + crate::filter::ctb_decoded(self.pic, ry as usize); + ts += 1; + if end { + if self.pps.dependent_slice_segments_enabled { + self.pic.ds_ctx = Some((Box::new(self.c.ctx), self.last_qp)); + } + break; + } + ensure!(ts < npic, "slice data continues past the last CTB"); + let nrs = layout.ts_to_rs[ts as usize]; + let new_tile = self.pps.tiles_enabled && layout.tile_id[ts as usize] != layout.tile_id[ts as usize - 1]; + let new_row = self.pps.entropy_coding_sync + && (nrs.is_multiple_of(wctb) || layout.tile_id[ts as usize] != layout.tile_id[layout.rs_to_ts[nrs as usize - 1] as usize]); + if new_tile || new_row { + ensure!(self.c.decode_terminate() == 1, "end_of_subset_one_bit is 0"); + let pos = self.c.bit_pos().div_ceil(8); + self.c.set_byte_pos(pos); + self.c.init_engine()?; + } + } + Ok(()) + } + + fn parse_sao(&mut self, rx: i32, ry: i32, rs: u32) -> Result<()> { + let layout = self.pic.layout.clone(); + let wctb = self.pic.wctb as u32; + let ts = layout.rs_to_ts[rs as usize]; + let mut merge_left = false; + let mut merge_up = false; + if rx > 0 { + let left_in_slice = self.pic.ctb_addr[rs as usize - 1] == self.slice_addr; + let left_in_tile = layout.tile_id[ts as usize] == layout.tile_id[layout.rs_to_ts[rs as usize - 1] as usize]; + if left_in_slice && left_in_tile { + merge_left = self.dd(SAO_MERGE) == 1; + } + } + if ry > 0 && !merge_left { + let up = rs - wctb; + let up_in_slice = self.pic.ctb_addr[up as usize] == self.slice_addr; + let up_in_tile = layout.tile_id[ts as usize] == layout.tile_id[layout.rs_to_ts[up as usize] as usize]; + if up_in_slice && up_in_tile { + merge_up = self.dd(SAO_MERGE) == 1; + } + } + if merge_left { + self.pic.sao[rs as usize] = self.pic.sao[rs as usize - 1]; + return Ok(()); + } + if merge_up { + self.pic.sao[rs as usize] = self.pic.sao[(rs - wctb) as usize]; + return Ok(()); + } + let mut p = SaoParams::default(); + for c in 0..3 { + let enabled = if c == 0 { self.sh.sao_luma } else { self.sh.sao_chroma }; + if !enabled { + continue; + } + if c == 2 { + p.type_idx[2] = p.type_idx[1]; + p.eo_class[2] = p.eo_class[1]; + } else { + p.type_idx[c] = if self.dd(SAO_TYPE) == 0 { + 0 + } else if self.bypass() == 0 { + 1 + } else { + 2 + }; + } + if p.type_idx[c] == 0 { + continue; + } + let bd = if c == 0 { self.pic.bd_y } else { self.pic.bd_c }; + let cmax = (1u32 << (bd.min(10) - 5)) - 1; + let mut abs = [0i16; 4]; + for a in abs.iter_mut() { + let mut v = 0; + while v < cmax && self.bypass() == 1 { + v += 1; + } + *a = v as i16; + } + if p.type_idx[c] == 1 { + for a in abs.iter_mut() { + if *a != 0 && self.bypass() == 1 { + *a = -*a; + } + } + p.band_pos[c] = self.bypass_bits(5) as u8; + p.offsets[c] = abs; + } else { + p.offsets[c] = [abs[0], abs[1], -abs[2], -abs[3]]; + if c == 0 { + p.eo_class[0] = self.bypass_bits(2) as u8; + } + if c == 1 { + p.eo_class[1] = self.bypass_bits(2) as u8; + } + } + } + self.pic.sao[rs as usize] = p; + Ok(()) + } + + fn coding_quadtree(&mut self, x0: i32, y0: i32, log2: u32, depth: u32) -> Result<()> { + let size = 1i32 << log2; + let (w, h) = (self.pic.width as i32, self.pic.height as i32); + let split = if x0 + size <= w && y0 + size <= h && log2 > self.sps.log2_min_cb { + // ctxInc from neighbouring depths (9.3.4.2.2) + let mut inc = 0; + if self.pic.zavail(x0, y0, x0 - 1, y0, self.slice_addr) && self.pic.blk_at(x0 - 1, y0).depth as u32 > depth { + inc += 1; + } + if self.pic.zavail(x0, y0, x0, y0 - 1, self.slice_addr) && self.pic.blk_at(x0, y0 - 1).depth as u32 > depth { + inc += 1; + } + self.dd(SPLIT_CU + inc) == 1 + } else { + log2 > self.sps.log2_min_cb + }; + if self.pps.cu_qp_delta_enabled && log2 >= self.log2_min_cu_qp_delta { + self.is_cu_qp_delta_coded = false; + self.cu_qp_delta = 0; + } + if split { + let half = size >> 1; + let (x1, y1) = (x0 + half, y0 + half); + self.coding_quadtree(x0, y0, log2 - 1, depth + 1)?; + if x1 < w { + self.coding_quadtree(x1, y0, log2 - 1, depth + 1)?; + } + if y1 < h { + self.coding_quadtree(x0, y1, log2 - 1, depth + 1)?; + } + if x1 < w && y1 < h { + self.coding_quadtree(x1, y1, log2 - 1, depth + 1)?; + } + Ok(()) + } else { + self.coding_unit(x0, y0, log2, depth) + } + } + + /// Set per-4x4 state of a rectangle (luma coordinates). + fn fill_blk(&mut self, x0: i32, y0: i32, w: i32, h: i32, f: impl Fn(&mut Blk)) { + let (bx0, by0) = ((x0 >> 2) as usize, (y0 >> 2) as usize); + let bx1 = (((x0 + w) as usize).min(self.pic.width).div_ceil(4)).max(bx0); + let by1 = (((y0 + h) as usize).min(self.pic.height).div_ceil(4)).max(by0); + for by in by0..by1 { + for b in &mut self.pic.blk[by * self.pic.w4 + bx0..by * self.pic.w4 + bx1] { + f(b); + } + } + } + + /// qPY_PRED derivation (8.6.1) at the start of a quantization group. + fn derive_qp_pred(&mut self, xq: i32, yq: i32) { + let prev = self.last_qp; + let s = self.sps.log2_ctb; + let cur_ctb = ((yq >> s), (xq >> s)); + let qa = if self.pic.zavail(xq, yq, xq - 1, yq, self.slice_addr) && ((yq >> s), ((xq - 1) >> s)) == cur_ctb { + self.pic.blk_at(xq - 1, yq).qp as i32 + } else { + prev + }; + let qb = if self.pic.zavail(xq, yq, xq, yq - 1, self.slice_addr) && (((yq - 1) >> s), (xq >> s)) == cur_ctb { + self.pic.blk_at(xq, yq - 1).qp as i32 + } else { + prev + }; + self.qp_y_pred = (qa + qb + 1) >> 1; + } + + fn update_qp(&mut self) { + let off = 6 * (self.pic.bd_y as i32 - 8); + self.qp_y = ((self.qp_y_pred + self.cu_qp_delta + 52 + 2 * off) % (52 + off)) - off; + let offc = 6 * (self.pic.bd_c as i32 - 8); + let qpi_cb = (self.qp_y + self.pps.cb_qp_offset + self.sh.cb_qp_offset).clamp(-offc, 57); + let qpi_cr = (self.qp_y + self.pps.cr_qp_offset + self.sh.cr_qp_offset).clamp(-offc, 57); + self.qp_cb = qpc_420(qpi_cb) + offc; + self.qp_cr = qpc_420(qpi_cr) + offc; + } + + fn coding_unit(&mut self, x0: i32, y0: i32, log2: u32, depth: u32) -> Result<()> { + let n = 1i32 << log2; + // quantization group start + let qg_mask = (1 << self.log2_min_cu_qp_delta) - 1; + if (x0 & qg_mask) == 0 && (y0 & qg_mask) == 0 { + self.derive_qp_pred(x0, y0); + } + self.update_qp(); + self.cu_transquant_bypass = if self.pps.transquant_bypass { self.dd(CU_TRANSQUANT_BYPASS) == 1 } else { false }; + let mut skip = false; + if !self.sh.is_intra() { + let mut inc = 0; + if self.pic.zavail(x0, y0, x0 - 1, y0, self.slice_addr) && self.pic.blk_at(x0 - 1, y0).flags & F_SKIP != 0 { + inc += 1; + } + if self.pic.zavail(x0, y0, x0, y0 - 1, self.slice_addr) && self.pic.blk_at(x0, y0 - 1).flags & F_SKIP != 0 { + inc += 1; + } + skip = self.dd(CU_SKIP + inc) == 1; + } + // CU-level block state: depth, skip, coded; CU edges are transform and prediction edges + let bypass = self.cu_transquant_bypass; + let qp = self.qp_y as i8; + self.fill_blk(x0, y0, n, n, |b| { + b.depth = depth as u8; + b.flags = F_CODED | if skip { F_SKIP } else { 0 } | if bypass { F_NOFILTER } else { 0 }; + b.qp = qp; + b.ipm = 1; + b.edges = 0; + }); + self.mark_edges(x0, y0, n, n, E_TU_V | E_PU_V, E_TU_H | E_PU_H); + if skip { + self.cu_intra = false; + self.prediction_unit(x0, y0, n, x0, y0, n, n, 0, PartMode::P2Nx2N, true)?; + self.finish_cu(x0, y0, n); + return Ok(()); + } + let intra = if !self.sh.is_intra() { self.dd(PRED_MODE) == 1 } else { true }; + self.cu_intra = intra; + let mut part = PartMode::P2Nx2N; + if !intra || log2 == self.sps.log2_min_cb { + part = self.parse_part_mode(intra, log2)?; + } + self.cu_part = part; + if intra { + self.fill_blk(x0, y0, n, n, |b| b.flags |= F_INTRA); + self.intra_split = part == PartMode::PNxN; + let pcm = part == PartMode::P2Nx2N + && self.sps.pcm + && log2 >= self.sps.log2_min_pcm + && log2 <= self.sps.log2_max_pcm + && self.c.decode_terminate() == 1; + if pcm { + self.pcm_sample(x0, y0, log2)?; + if self.sps.pcm_loop_filter_disabled { + self.fill_blk(x0, y0, n, n, |b| b.flags |= F_NOFILTER); + } + self.finish_cu(x0, y0, n); + return Ok(()); + } + self.parse_intra_modes(x0, y0, n, part)?; + self.max_trafo_depth = self.sps.max_th_depth_intra + self.intra_split as u32; + self.transform_tree(x0, y0, x0, y0, log2, 0, 0, [true, true])?; + } else { + self.intra_split = false; + let mut merge2nx2n = false; + let h = n / 2; + let q = n / 4; + let parts: &[(i32, i32, i32, i32)] = match part { + PartMode::P2Nx2N => &[(0, 0, n, n)], + PartMode::P2NxN => &[(0, 0, n, h), (0, h, n, h)], + PartMode::PNx2N => &[(0, 0, h, n), (h, 0, h, n)], + PartMode::P2NxnU => &[(0, 0, n, q), (0, q, n, n - q)], + PartMode::P2NxnD => &[(0, 0, n, n - q), (0, n - q, n, q)], + PartMode::PnLx2N => &[(0, 0, q, n), (q, 0, n - q, n)], + PartMode::PnRx2N => &[(0, 0, n - q, n), (n - q, 0, q, n)], + PartMode::PNxN => &[(0, 0, h, h), (h, 0, h, h), (0, h, h, h), (h, h, h, h)], + }; + for (i, &(px, py, pw, ph)) in parts.iter().enumerate() { + let merge = self.prediction_unit(x0, y0, n, x0 + px, y0 + py, pw, ph, i, part, false)?; + if part == PartMode::P2Nx2N { + merge2nx2n = merge; + } + if px != 0 { + self.mark_edges(x0 + px, y0 + py, pw, ph, E_PU_V, 0); + } + if py != 0 { + self.mark_edges(x0 + px, y0 + py, pw, ph, 0, E_PU_H); + } + } + let root_cbf = if !merge2nx2n { self.dd(RQT_ROOT_CBF) == 1 } else { true }; + if root_cbf { + self.max_trafo_depth = self.sps.max_th_depth_inter; + self.transform_tree(x0, y0, x0, y0, log2, 0, 0, [true, true])?; + } + } + self.finish_cu(x0, y0, n); + Ok(()) + } + + /// Store the final QpY of the CU (may have changed with cu_qp_delta). + fn finish_cu(&mut self, x0: i32, y0: i32, n: i32) { + let qp = self.qp_y as i8; + self.fill_blk(x0, y0, n, n, |b| b.qp = qp); + self.last_qp = self.qp_y; + } + + /// Mark the left (vertical) and top (horizontal) edges of a rectangle. + fn mark_edges(&mut self, x0: i32, y0: i32, w: i32, h: i32, v: u8, hz: u8) { + let w4 = self.pic.w4; + let (bx0, by0) = ((x0 >> 2) as usize, (y0 >> 2) as usize); + if v != 0 { + let by1 = ((y0 + h) as usize).min(self.pic.height).div_ceil(4); + for by in by0..by1 { + self.pic.blk[by * w4 + bx0].edges |= v; + } + } + if hz != 0 { + let bx1 = ((x0 + w) as usize).min(self.pic.width).div_ceil(4); + for b in &mut self.pic.blk[by0 * w4 + bx0..by0 * w4 + bx1] { + b.edges |= hz; + } + } + } + + fn parse_part_mode(&mut self, intra: bool, log2: u32) -> Result { + if intra { + return Ok(if self.dd(PART_MODE) == 1 { PartMode::P2Nx2N } else { PartMode::PNxN }); + } + if self.dd(PART_MODE) == 1 { + return Ok(PartMode::P2Nx2N); + } + let min = log2 == self.sps.log2_min_cb; + if min { + if self.dd(PART_MODE + 1) == 1 { + return Ok(PartMode::P2NxN); + } + if log2 == 3 { + return Ok(PartMode::PNx2N); + } + return Ok(if self.dd(PART_MODE + 2) == 1 { PartMode::PNx2N } else { PartMode::PNxN }); + } + let amp = self.sps.amp; + let hor = self.dd(PART_MODE + 1) == 1; + if !amp { + return Ok(if hor { PartMode::P2NxN } else { PartMode::PNx2N }); + } + let no_amp = self.dd(PART_MODE + 3) == 1; + if no_amp { + return Ok(if hor { PartMode::P2NxN } else { PartMode::PNx2N }); + } + let b = self.bypass(); + Ok(match (hor, b) { + (true, 0) => PartMode::P2NxnU, + (true, _) => PartMode::P2NxnD, + (false, 0) => PartMode::PnLx2N, + (false, _) => PartMode::PnRx2N, + }) + } + + fn pcm_sample(&mut self, x0: i32, y0: i32, log2: u32) -> Result<()> { + // the PCM samples start at the next byte boundary after the terminate bin + let start = self.c.bit_pos().div_ceil(8); + let data = self.c.data(); + let n = 1usize << log2; + let (bl, bc) = (self.sps.pcm_bit_depth_luma, self.sps.pcm_bit_depth_chroma); + let total_bits = n * n * bl as usize + 2 * (n / 2) * (n / 2) * bc as usize; + ensure!(start + total_bits.div_ceil(8) <= data.len(), "PCM samples truncated"); + let mut r = slidecraft_bitstream::BitReader::new(&data[start..]); + let (sy, sc) = (self.pic.bd_y - bl, self.pic.bd_c - bc); + let w = self.pic.width; + for y in 0..n { + for x in 0..n { + let v = r.read_bits(bl)? as u16; + self.pic.planes[0][(y0 as usize + y) * w + x0 as usize + x] = v << sy; + } + } + let cw = self.pic.cwidth; + for c in 1..3 { + for y in 0..n / 2 { + for x in 0..n / 2 { + let v = r.read_bits(bc)? as u16; + self.pic.planes[c][(y0 as usize / 2 + y) * cw + x0 as usize / 2 + x] = v << sc; + } + } + } + let end = start + r.position().div_ceil(8); + self.c.set_byte_pos(end); + self.c.init_engine() + } + + fn parse_intra_modes(&mut self, x0: i32, y0: i32, n: i32, part: PartMode) -> Result<()> { + let nb = if part == PartMode::PNxN { 2 } else { 1 }; + let pb = n / nb; + let mut prev = [false; 4]; + for (i, p) in prev.iter_mut().enumerate().take((nb * nb) as usize) { + let _ = i; + *p = self.dd(PREV_INTRA_LUMA) == 1; + } + for j in 0..nb { + for i in 0..nb { + let k = (j * nb + i) as usize; + let (xp, yp) = (x0 + i * pb, y0 + j * pb); + // candidate modes (8.4.2) + let cand_a = if self.pic.zavail(xp, yp, xp - 1, yp, self.slice_addr) { + let b = self.pic.blk_at(xp - 1, yp); + if b.flags & F_INTRA != 0 { b.ipm as u32 } else { 1 } + } else { + 1 + }; + let cand_b = if self.pic.zavail(xp, yp, xp, yp - 1, self.slice_addr) && yp > ((yp >> self.sps.log2_ctb) << self.sps.log2_ctb) { + let b = self.pic.blk_at(xp, yp - 1); + if b.flags & F_INTRA != 0 { b.ipm as u32 } else { 1 } + } else { + 1 + }; + let mut list = 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] + }; + let mode = if prev[k] { + let idx = if self.bypass() == 0 { + 0 + } else if self.bypass() == 0 { + 1 + } else { + 2 + }; + list[idx] + } else { + let mut m = self.bypass_bits(5); + list.sort_unstable(); + for &c in &list { + if m >= c { + m += 1; + } + } + m + }; + self.fill_blk(xp, yp, pb, pb, |b| b.ipm = mode as u8); + } + } + // chroma mode (4:2:0: one per CU, derived from the first PU's luma mode) + let icpm = if self.dd(INTRA_CHROMA) == 0 { 4 } else { self.bypass_bits(2) }; + let luma = self.pic.blk_at(x0, y0).ipm as u32; + self.intra_chroma_mode = match icpm { + 4 => luma, + _ => { + let m = [0, 26, 10, 1][icpm as usize]; + if m == luma { 34 } else { m } + } + }; + Ok(()) + } + + fn transform_tree(&mut self, x0: i32, y0: i32, xb: i32, yb: i32, log2: u32, depth: u32, blk_idx: u32, parent_cbf_c: [bool; 2]) -> Result<()> { + let split = + if log2 <= self.sps.log2_max_tb && log2 > self.sps.log2_min_tb && depth < self.max_trafo_depth && !(self.intra_split && depth == 0) { + self.dd(SPLIT_TRANSFORM + (5 - log2 as usize)) == 1 + } else { + // interSplitFlag (7.4.9.8) + let inter_split = self.sps.max_th_depth_inter == 0 && !self.cu_intra && self.cu_part != PartMode::P2Nx2N && depth == 0; + log2 > self.sps.log2_max_tb || (self.intra_split && depth == 0) || inter_split + }; + let mut cbf_c = [false, false]; + if log2 > 2 { + for (c, cbf) in cbf_c.iter_mut().enumerate() { + if depth == 0 || parent_cbf_c[c] { + *cbf = self.dd(CBF_CHROMA + depth as usize) == 1; + } + } + } else { + // 4x4 luma blocks: chroma cbfs are those of the parent (coded at blkIdx 3) + cbf_c = parent_cbf_c; + if depth == 0 { + cbf_c = [false, false]; + } + } + if split { + let h = 1 << (log2 - 1); + self.transform_tree(x0, y0, x0, y0, log2 - 1, depth + 1, 0, cbf_c)?; + self.transform_tree(x0 + h, y0, x0, y0, log2 - 1, depth + 1, 1, cbf_c)?; + self.transform_tree(x0, y0 + h, x0, y0, log2 - 1, depth + 1, 2, cbf_c)?; + self.transform_tree(x0 + h, y0 + h, x0, y0, log2 - 1, depth + 1, 3, cbf_c)?; + return Ok(()); + } + let cbf_luma = + if self.cu_intra || depth != 0 || cbf_c[0] || cbf_c[1] { self.dd(CBF_LUMA + if depth == 0 { 1 } else { 0 }) == 1 } else { true }; + let n = 1 << log2; + if depth > 0 { + self.mark_edges(x0, y0, n, n, E_TU_V, E_TU_H); + } + self.transform_unit(x0, y0, xb, yb, log2, blk_idx, cbf_luma, cbf_c) + } + + fn transform_unit(&mut self, x0: i32, y0: i32, xb: i32, yb: i32, log2: u32, blk_idx: u32, cbf_luma: bool, cbf_c: [bool; 2]) -> Result<()> { + let n = 1i32 << log2; + let chroma_here = log2 > 2 || blk_idx == 3; + let cbf_chroma = cbf_c[0] || cbf_c[1]; + if (cbf_luma || cbf_chroma) && self.pps.cu_qp_delta_enabled && !self.is_cu_qp_delta_coded { + // cu_qp_delta_abs: prefix TU(5) with ctx 0,1,1,1,1, suffix EG0 + let mut v = 0; + while v < 5 && self.dd(CU_QP_DELTA_ABS + if v == 0 { 0 } else { 1 }) == 1 { + v += 1; + } + if v == 5 { + let mut k = 0; + while self.bypass() == 1 { + v += 1 << k; + k += 1; + ensure!(k < 32, "invalid cu_qp_delta_abs"); + } + v += self.bypass_bits(k) as i32; + } + if v != 0 && self.bypass() == 1 { + v = -v; + } + let off = 6 * (self.pic.bd_y as i32 - 8); + ensure!(v >= -(26 + off / 2) && v <= 25 + off / 2, "CuQpDeltaVal out of range"); + self.is_cu_qp_delta_coded = true; + self.cu_qp_delta = v; + self.update_qp(); + } + // luma + if self.cu_intra { + let mode = self.pic.blk_at(x0, y0).ipm as u32; + self.intra_predict(x0, y0, log2, 0, mode); + } + if cbf_luma { + let qp_bd = 6 * (self.pic.bd_y as i32 - 8); + let qp = self.qp_y + qp_bd; + self.residual(x0, y0, log2, 0, qp)?; + self.fill_blk(x0, y0, n, n, |b| b.flags |= F_CBF); + } + if !chroma_here { + return Ok(()); + } + let (xc, yc, log2c) = if log2 > 2 { (x0 / 2, y0 / 2, log2 - 1) } else { (xb / 2, yb / 2, 2) }; + for c in 1..3 { + if self.cu_intra { + let mode = self.intra_chroma_mode; + self.intra_predict(xc, yc, log2c, c, mode); + } + if cbf_c[c - 1] { + let qp = if c == 1 { self.qp_cb } else { self.qp_cr }; + self.residual(xc, yc, log2c, c, qp)?; + } + } + Ok(()) + } + + /// Intra prediction of an n x n block of component `c` at component position (x0, y0). + fn intra_predict(&mut self, x0: i32, y0: i32, log2: u32, c: usize, mode: u32) { + let n = 1usize << log2; + let sc = if c == 0 { 1 } else { 2 }; + let (xl, yl) = (x0 * sc, y0 * sc); + let unit = 4 / sc as usize; // component samples per 4x4 luma block + let stride = if c == 0 { self.pic.width } else { self.pic.cwidth }; + let constrained = self.pps.constrained_intra_pred; + let mut al = [false; 129]; + let mut at = [false; 129]; + let addr = self.slice_addr; + let avail = |pic: &PicState, xn: i32, yn: i32| -> bool { + pic.zavail(xl, yl, xn, yn, addr) && (!constrained || pic.blk_at(xn, yn).flags & F_INTRA != 0) + }; + let plane = &self.pic.planes[c]; + let r = &mut self.refs_buf; + // corner + if avail(self.pic, 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 avail(self.pic, 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 avail(self.pic, 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; + } + let bd = if c == 0 { self.pic.bd_y } else { self.pic.bd_c }; + intra::substitute(r, &al, &at, n, bd); + if c == 0 { + intra::filter(r, mode, n, self.sps.strong_intra_smoothing, true, bd); + } + let off = y0 as usize * stride + x0 as usize; + intra::predict(r, mode, n, c == 0, bd, &mut self.pic.planes[c][off..], stride); + } + + /// residual_coding() + scaling + transform + reconstruction for one transform block. + fn residual(&mut self, x0: i32, y0: i32, log2: u32, c: usize, qp: i32) -> Result<()> { + let n = 1usize << log2; + let bypass = self.cu_transquant_bypass; + let ts = if self.pps.transform_skip && !bypass && log2 == 2 { self.dd(TRANSFORM_SKIP + (c > 0) as usize) == 1 } else { false }; + // last significant coefficient + 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 mut px = 0; + while px < cmax && self.dd(LAST_X_PREFIX + (off + (px >> shift)) as usize) == 1 { + px += 1; + } + let mut py = 0; + while py < cmax && self.dd(LAST_Y_PREFIX + (off + (py >> shift)) as usize) == 1 { + py += 1; + } + let mut lx = px; + if px > 3 { + let nb = (px >> 1) - 1; + lx = (1 << nb) * (2 + (px & 1)) + self.bypass_bits(nb); + } + let mut ly = py; + if py > 3 { + let nb = (py >> 1) - 1; + ly = (1 << nb) * (2 + (py & 1)) + self.bypass_bits(nb); + } + // scanIdx (7.4.9.11) + let scan_idx = if self.cu_intra && (log2 == 2 || (log2 == 3 && c == 0)) { + let m = if c == 0 { self.pic.blk_at(x0, y0).ipm as u32 } else { self.intra_chroma_mode }; + if (6..=14).contains(&m) { + 2 + } else if (22..=30).contains(&m) { + 1 + } else { + 0 + } + } else { + 0 + }; + if scan_idx == 2 { + std::mem::swap(&mut lx, &mut ly); + } + ensure!((lx as usize) < n && (ly as usize) < n, "last significant coefficient outside the block"); + let log2sb = log2 - 2; + let sb_scan = scan_order(log2sb, scan_idx); + let pos_scan = &SCAN_4[scan_idx]; + let sb_w = 1usize << log2sb; + let last_sb = sb_scan.iter().position(|&(x, y)| x as u32 == lx >> 2 && y as u32 == ly >> 2).unwrap_or(0); + let last_pos = pos_scan.iter().position(|&(x, y)| x as u32 == lx & 3 && y as u32 == ly & 3).unwrap_or(0); + self.coeffs[..n * n].fill(0); + let mut csbf = [0u8; 64]; + let mut prev_g1: Option = None; + let sdh = self.pps.sign_data_hiding && !bypass; + let (mut max_x, mut max_y) = (0usize, 0usize); + 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 mut infer_dc = false; + 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 }; + if i < last_sb && i > 0 { + let inc = (right | below).min(1) as usize + if c > 0 { 2 } else { 0 }; + csbf[ys * 8 + xs] = self.c.decode_decision(CODED_SUB_BLOCK + inc) as u8; + infer_dc = true; + } else { + csbf[ys * 8 + xs] = 1; + } + // significance map + let mut sig = [0u8; 16]; // scan positions n of significant coefficients, descending + 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 + }; + if csbf[ys * 8 + xs] != 0 { + let prev_csbf = right + (below << 1); + let mut np = start; + while np >= 0 { + let (xp, yp) = (pos_scan[np as usize].0 as usize, pos_scan[np as usize].1 as usize); + let (xc, yc) = (xs * 4 + xp, ys * 4 + yp); + if np > 0 || !infer_dc { + let sig_ctx = if log2 == 2 { + CTX_IDX_MAP[(yc << 2) + xc] as usize + } else if xc + yc == 0 { + 0 + } else { + let mut s = 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 { + s += 3; + } + s += if log2 == 3 { if scan_idx == 0 { 9 } else { 15 } } else { 21 }; + } else { + s += if log2 == 3 { 9 } else { 12 }; + } + s + }; + if self.c.decode_decision(SIG_COEFF + c_off_sig + sig_ctx) == 1 { + sig[nsig] = np as u8; + nsig += 1; + infer_dc = false; + } + } else { + // np == 0 with inferSbDcSigCoeffFlag + sig[nsig] = 0; + nsig += 1; + } + np -= 1; + } + } + if nsig == 0 { + continue; + } + // greater1 / greater2 flags + let mut ctx_set = if i == 0 || c > 0 { 0 } else { 2 }; + if let Some(g) = prev_g1 + && g == 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 inc = ctx_set * 4 + g1ctx.min(3) as usize + c_off_g1; + let f = self.c.decode_decision(GT1 + inc); + gt1[k] = f as u8; + if f == 1 { + g1ctx = 0; + if first_g1.is_none() { + first_g1 = Some(k); + } + } else if g1ctx > 0 { + g1ctx += 1; + } + } + prev_g1 = Some(g1ctx); + let mut gt2 = 0u8; + if let Some(k) = first_g1 { + gt2 = self.c.decode_decision(GT2 + ctx_set + if c > 0 { 4 } else { 0 }) as u8; + let _ = k; + } + // signs + let last_sig_pos = sig[0] as i32; + let first_sig_pos = sig[nsig - 1] as i32; + let sign_hidden = sdh && last_sig_pos - first_sig_pos > 3; + let nsigns = if sign_hidden { nsig - 1 } else { nsig }; + let mut signs = if nsigns > 0 { self.bypass_bits(nsigns as u32) << (32 - nsigns) } else { 0 }; + // remaining levels + let mut rice = 0u32; + let mut sum_abs = 0i32; + for k in 0..nsig { + let mut base = 1 + gt1[k] as i32; + if Some(k) == first_g1 { + base += gt2 as i32; + } + let thresh = if k < 8 { if Some(k) == first_g1 { 3 } else { 2 } } else { 1 }; + let mut level = base; + if base == thresh { + let rem = self.coeff_abs_level_remaining(rice)?; + level = base + rem; + if level > 3 * (1 << rice) { + rice = (rice + 1).min(4); + } + } + let np = sig[k] as usize; + let (xc, yc) = (xs * 4 + pos_scan[np].0 as usize, ys * 4 + pos_scan[np].1 as usize); + let mut v = level; + if !(sign_hidden && k == nsig - 1) { + if signs & 0x8000_0000 != 0 { + v = -v; + } + signs <<= 1; + } + if sign_hidden { + sum_abs += level; + if k == nsig - 1 && sum_abs & 1 == 1 { + v = -v; + } + } + self.coeffs[yc * n + xc] = v; + max_x = max_x.max(xc); + max_y = max_y.max(yc); + } + } + self.reconstruct(x0, y0, log2, c, qp, ts, max_x, max_y) + } + + fn coeff_abs_level_remaining(&mut self, rice: u32) -> Result { + let mut prefix = 0u32; + while prefix < 32 && self.bypass() == 1 { + prefix += 1; + } + ensure!(prefix < 32, "invalid coeff_abs_level_remaining"); + if prefix <= 3 { + Ok(((prefix << rice) + self.bypass_bits(rice)) as i32) + } else { + let nb = prefix - 3 + rice; + ensure!(nb <= 24, "coeff_abs_level_remaining too large"); + Ok(((((1u32 << (prefix - 3)) + 2) << rice) + self.bypass_bits(nb)) as i32) + } + } + + /// Scaling, transform and addition to the prediction. + fn reconstruct(&mut self, x0: i32, y0: i32, log2: u32, c: usize, qp: i32, ts: bool, max_x: usize, max_y: usize) -> Result<()> { + let n = 1usize << log2; + let bd = if c == 0 { self.pic.bd_y } else { self.pic.bd_c }; + let coeffs = &mut self.coeffs[..n * n]; + let mut dc_only = false; + if !self.cu_transquant_bypass { + // scaling (8.6.3) + let bd_shift = bd + log2 - 5; + let round = 1i64 << (bd_shift - 1); + let scale = (LEVEL_SCALE[(qp % 6) as usize] as i64) << (qp / 6); + let m: Option<&[u8]> = match &self.pic.scaling { + Some(s) if !(ts && n > 4) => { + let size_id = (log2 - 2) as usize; + let matrix_id = if self.cu_intra { c } else { 3 + c }; + Some(&s[size_id * 6 + matrix_id]) + } + _ => None, + }; + for y in 0..=max_y { + for x in 0..=max_x { + let v = coeffs[y * n + x]; + if v != 0 { + let f = m.map_or(16, |m| m[y * n + x] as i64); + coeffs[y * n + x] = ((v as i64 * f * scale + round) >> bd_shift).clamp(-32768, 32767) as i32; + } + } + } + let bd_shift = 20 - bd; + if ts { + transform::transform_skip(coeffs, n, bd_shift); + } else if max_x == 0 && max_y == 0 && !(self.cu_intra && c == 0 && n == 4) { + let v = transform::inverse_dc(coeffs[0], bd_shift); + coeffs.fill(v); + dc_only = true; + } else { + let dst = self.cu_intra && c == 0 && n == 4; + transform::inverse_transform(coeffs, n, dst, max_x, max_y, bd_shift); + } + } + let _ = dc_only; + let (stride, max) = (if c == 0 { self.pic.width } else { self.pic.cwidth }, (1i32 << bd) - 1); + let plane = &mut self.pic.planes[c]; + for y in 0..n { + let row = &mut plane[(y0 as usize + y) * stride + x0 as usize..][..n]; + for (x, s) in row.iter_mut().enumerate() { + *s = (*s as i32 + coeffs[y * n + x]).clamp(0, max) as u16; + } + } + Ok(()) + } + + /// prediction_unit(): parse, derive motion, store it and predict. Returns merge_flag. + fn prediction_unit( + &mut self, + xc: i32, + yc: i32, + ncb: i32, + xp: i32, + yp: i32, + w: i32, + h: i32, + part_idx: usize, + part: PartMode, + skip: bool, + ) -> Result { + let merge = if skip { true } else { self.dd(MERGE_FLAG) == 1 }; + let mvf = if merge { + let idx = if self.sh.max_num_merge_cand > 1 { + let mut i = 0; + if self.dd(MERGE_IDX) == 1 { + i = 1; + while i < self.sh.max_num_merge_cand - 1 && self.bypass() == 1 { + i += 1; + } + } + i + } else { + 0 + }; + self.derive_merge(xc, yc, ncb, xp, yp, w, h, part_idx, part, idx as usize) + } else { + let pred_idc = if self.sh.is_b() { + if w + h != 12 { + let depth = self.pic.blk_at(xc, yc).depth as usize; + if self.dd(INTER_PRED_IDC + depth) == 1 { 2 } else { self.dd(INTER_PRED_IDC + 4) } + } else { + self.dd(INTER_PRED_IDC + 4) + } + } else { + 0 + }; + let mut ref_idx = [-1i8; 2]; + let mut mvd = [[0i32; 2]; 2]; + let mut mvp_flag = [0u32; 2]; + for l in 0..2 { + let used = if l == 0 { pred_idc != 1 } else { pred_idc != 0 }; + if !used { + continue; + } + let nref = self.sh.num_ref_idx[l]; + let mut ri = 0; + if nref > 1 { + while ri < nref - 1 { + let bin = if ri < 2 { self.dd(REF_IDX + ri as usize) } else { self.bypass() }; + if bin == 0 { + break; + } + ri += 1; + } + } + ref_idx[l] = ri as i8; + if l == 1 && self.sh.mvd_l1_zero && pred_idc == 2 { + mvd[1] = [0, 0]; + } else { + mvd[l] = self.mvd_coding()?; + } + mvp_flag[l] = self.dd(MVP_FLAG); + } + let mut f = MvField { mv: [[0; 2]; 2], ref_idx }; + for l in 0..2 { + if ref_idx[l] < 0 { + continue; + } + let mvp = self.derive_amvp(xc, yc, ncb, xp, yp, w, h, part_idx, l, ref_idx[l] as usize, mvp_flag[l] as usize); + // mvLX = (mvp + mvd) wrapped to 16 bits (8-94) + f.mv[l] = [(mvp[0] as i32 + mvd[l][0]) as i16, (mvp[1] as i32 + mvd[l][1]) as i16]; + } + f + }; + for l in 0..2 { + if mvf.ref_idx[l] >= 0 { + ensure!((mvf.ref_idx[l] as usize) < self.refs[l].len(), "reference index out of range"); + } + } + // store motion + let w4 = self.pic.w4; + for by in (yp >> 2)..((yp + h) >> 2) { + let o = by as usize * w4; + self.pic.mvf[o + (xp >> 2) as usize..o + ((xp + w) >> 2) as usize].fill(mvf); + } + self.motion_compensate(xp, yp, w as usize, h as usize, &mvf); + Ok(merge) + } + + fn mvd_coding(&mut self) -> Result<[i32; 2]> { + let g0 = [self.dd(ABS_MVD_GT0), self.dd(ABS_MVD_GT0)]; + let mut g1 = [0, 0]; + for k in 0..2 { + if g0[k] == 1 { + g1[k] = self.dd(ABS_MVD_GT1); + } + } + let mut out = [0i32; 2]; + for k in 0..2 { + if g0[k] == 1 { + let mut v = 1i32; + if g1[k] == 1 { + // EG1 + let mut kk = 1; + let mut val = 0i32; + while self.bypass() == 1 { + val += 1 << kk; + kk += 1; + ensure!(kk < 31, "invalid abs_mvd_minus2"); + } + val += self.bypass_bits(kk) as i32; + v = val + 2; + } + if self.bypass() == 1 { + v = -v; + } + out[k] = v; + } + } + Ok(out) + } + + /// Motion compensation + weighted prediction of one PU into the picture. + fn motion_compensate(&mut self, xp: i32, yp: i32, w: usize, h: usize, f: &MvField) { + let weighted = match self.sh.slice_type { + SliceType::P => self.pps.weighted_pred, + SliceType::B => self.pps.weighted_bipred, + SliceType::I => false, + }; + let bi = f.pred(0) && f.pred(1); + for c in 0..3 { + let (x, y, bw, bh, bd) = if c == 0 { (xp, yp, w, h, self.pic.bd_y) } else { (xp / 2, yp / 2, w / 2, h / 2, self.pic.bd_c) }; + let mut lists = [false; 2]; + for l in 0..2 { + if !f.pred(l) { + continue; + } + lists[l] = true; + let frame = &self.refs[l][f.ref_idx[l] as usize].frame; + let buf = if l == 0 || !bi { &mut self.pred0 } else { &mut self.pred1 }; + if c == 0 { + inter::mc_luma(frame, x, y, f.mv[l], bw, bh, buf, &mut self.mc); + } else { + inter::mc_chroma(frame, c - 1, x, y, f.mv[l], bw, bh, buf, &mut self.mc); + } + } + let stride = if c == 0 { self.pic.width } else { self.pic.cwidth }; + let dst = &mut self.pic.planes[c][y as usize * stride + x as usize..]; + let Some(pwt) = self.sh.pwt.as_ref().filter(|_| weighted) else { + if bi { + inter::put_bi(&self.pred0, &self.pred1, bw, bh, bd, dst, stride); + } else { + inter::put_uni(&self.pred0, bw, bh, bd, dst, stride); + } + continue; + }; + let shift1 = 14 - bd; + let log2wd = if c == 0 { pwt.luma_log2_denom } else { pwt.chroma_log2_denom } + shift1; + let get = |l: usize| -> (i32, i32) { + let e = &pwt.l[l][f.ref_idx[l] as usize]; + let (wt, o) = if c == 0 { e.luma } else { e.chroma[c - 1] }; + (wt, o << (bd - 8)) + }; + if bi { + let (w0, o0) = get(0); + let (w1, o1) = get(1); + inter::put_weighted_bi(&self.pred0, &self.pred1, bw, bh, bd, log2wd, w0, w1, o0, o1, dst, stride); + } else { + let l = if lists[0] { 0 } else { 1 }; + let (wt, o) = get(l); + inter::put_weighted_uni(&self.pred0, bw, bh, bd, log2wd, wt, o, dst, stride); + } + } + } +} diff --git a/crates/hevc/src/spec_tables.rs b/crates/hevc/src/spec_tables.rs new file mode 100644 index 0000000..a68cd51 --- /dev/null +++ b/crates/hevc/src/spec_tables.rs @@ -0,0 +1,101 @@ +// Generated by crates/hevc/tools/extract_tables.py from the text of Rec. ITU-T H.265. Do not edit. + +// 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 7-6: default ScalingList[1..3][0..2] (intra) and [3..5] (inter), in up-right diagonal order. +pub static DEFAULT_SCALING_INTRA: [u8; 64] = [16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 17, 16, 17, 16, 17, 18, 17, 18, 18, 17, 18, 21, 19, 20, 21, 20, 19, 21, 24, 22, 22, 24, 24, 22, 22, 24, 25, 25, 27, 30, 27, 25, 25, 29, 31, 35, 35, 31, 29, 36, 41, 44, 41, 36, 47, 54, 54, 47, 65, 70, 65, 88, 88, 115]; +pub static DEFAULT_SCALING_INTER: [u8; 64] = [16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 17, 17, 17, 17, 17, 18, 18, 18, 18, 18, 18, 20, 20, 20, 20, 20, 20, 20, 24, 24, 24, 24, 24, 24, 24, 24, 25, 25, 25, 25, 25, 25, 25, 28, 28, 28, 28, 28, 28, 33, 33, 33, 33, 33, 41, 41, 41, 41, 54, 54, 54, 71, 71, 91]; + +/// 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]; + +/// 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]; + +/// 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], +]; diff --git a/crates/hevc/src/synth_tests.rs b/crates/hevc/src/synth_tests.rs new file mode 100644 index 0000000..892b028 --- /dev/null +++ b/crates/hevc/src/synth_tests.rs @@ -0,0 +1,620 @@ +//! Hand-built bitstreams for coding tools libx265 never produces: tiles (uniform and explicit spacing, +//! with and without loop filtering across tile boundaries), PCM (with and without in-loop filtering), +//! multiple slices, dependent slice segments, long-term reference pictures and reference list +//! modification. A small CABAC encoder (9.3.5, informative) writes the slice data. The expected output +//! is known exactly where the in-loop filters leave the samples alone, and every stream is also checked +//! against ffmpeg (when present). + +use crate::Decoder; +use crate::cabac::{Contexts, NEXT_STATE, RANGE_TAB_LPS, init_contexts}; +use crate::spec_tables::*; +use slidecraft_bitstream::{BitWriter, escape_rbsp}; + +const W: usize = 96; +const H: usize = 64; +const CTB: usize = 16; +const WC: usize = W / CTB; +const HC: usize = H / CTB; + +/// CABAC encoder. +struct Enc { + w: BitWriter, + low: u32, + range: u32, + outstanding: u32, + first: bool, + ctx: Contexts, +} + +impl Enc { + fn new(w: BitWriter, qp: i32, init_type: usize) -> Self { + let mut ctx: Contexts = [0u8; NUM_CTX.next_power_of_two()]; + init_contexts(&mut ctx, qp, init_type); + Enc { w, low: 0, range: 510, outstanding: 0, first: true, ctx } + } + fn reset_engine(&mut self) { + self.low = 0; + self.range = 510; + self.outstanding = 0; + self.first = true; + } + fn put(&mut self, b: u32) { + if self.first { + self.first = false; + } else { + self.w.write_bits(b, 1); + } + while self.outstanding > 0 { + self.w.write_bits(1 - b, 1); + self.outstanding -= 1; + } + } + fn renorm(&mut self) { + while self.range < 256 { + if self.low < 256 { + self.put(0); + } else if self.low >= 512 { + self.low -= 512; + self.put(1); + } else { + self.low -= 256; + self.outstanding += 1; + } + self.range <<= 1; + self.low <<= 1; + } + } + fn decision(&mut self, ctx: usize, bin: u32) { + let s = self.ctx[ctx] as usize; + let q = ((self.range >> 6) & 3) as usize; + let lps = RANGE_TAB_LPS[s >> 1][q] as u32; + self.range -= lps; + if bin != (s & 1) as u32 { + self.low += self.range; + self.range = lps; + self.ctx[ctx] = NEXT_STATE[s][1]; + } else { + self.ctx[ctx] = NEXT_STATE[s][0]; + } + self.renorm(); + } + /// Terminating bin; a 1 flushes the engine (9.3.5.6) — its last written bit is the stop / + /// alignment bit. + fn terminate(&mut self, bin: u32) { + self.range -= 2; + if bin != 0 { + self.low += self.range; + self.range = 2; + self.renorm(); + self.put((self.low >> 9) & 1); + self.w.write_bits(((self.low >> 7) & 3) | 1, 2); + } else { + self.renorm(); + } + } +} + +/// Deterministic test content. +fn pattern(pic: u32, c: usize, x: usize, y: usize) -> u8 { + let v = (x * (3 + c) + y * (5 + pic as usize) + ((x ^ y) & 7) * 9 + pic as usize * 37 + c * 50) % 200; + (v + 20) as u8 +} + +struct Planes { + y: Vec, + u: Vec, + v: Vec, +} + +fn content(pic: u32) -> Planes { + let mut p = Planes { y: vec![0; W * H], u: vec![0; W * H / 4], v: vec![0; W * H / 4] }; + for y in 0..H { + for x in 0..W { + p.y[y * W + x] = pattern(pic, 0, x, y); + } + } + for y in 0..H / 2 { + for x in 0..W / 2 { + p.u[y * W / 2 + x] = pattern(pic, 1, x, y); + p.v[y * W / 2 + x] = pattern(pic, 2, x, y); + } + } + p +} + +fn nal(t: u8, rbsp: &[u8]) -> Vec { + let mut out = vec![0, 0, 0, 1, t << 1, 1]; + out.extend(escape_rbsp(rbsp)); + out +} + +fn ptl(w: &mut BitWriter) { + w.write_bits(0, 2); // profile_space + w.write_bits(0, 1); // tier + w.write_bits(1, 5); // Main + w.write_bits(0x6000_0000, 32); // compatibility flags 1, 2 + w.write_bits(0b1001, 4); // progressive, interlaced, non_packed, frame_only + w.write_bits(0, 32); + w.write_bits(0, 11); + w.write_bits(0, 1); + w.write_bits(93, 8); // level 3.1 +} + +fn vps() -> Vec { + let mut w = BitWriter::new(); + w.write_bits(0, 4); + w.write_bits(3, 2); // base layer internal / available + w.write_bits(0, 6); + w.write_bits(0, 3); + w.write_bits(1, 1); + w.write_bits(0xffff, 16); + ptl(&mut w); + w.write_bits(1, 1); + w.write_ue(4); + w.write_ue(0); + w.write_ue(0); + w.write_bits(0, 6); + w.write_ue(0); + w.write_bits(0, 1); + w.write_bits(0, 1); + w.rbsp_trailing(); + nal(32, &w.finish()) +} + +fn sps(pcm_loop_filter_disabled: bool) -> Vec { + let mut w = BitWriter::new(); + w.write_bits(0, 4); + w.write_bits(0, 3); + w.write_bits(1, 1); + ptl(&mut w); + w.write_ue(0); // sps id + w.write_ue(1); // 4:2:0 + w.write_ue(W as u32); + w.write_ue(H as u32); + w.write_bits(0, 1); // conformance window + w.write_ue(0); + w.write_ue(0); + w.write_ue(4); // log2_max_poc_lsb = 8 + w.write_bits(1, 1); + w.write_ue(4); // max_dec_pic_buffering_minus1 + w.write_ue(0); + w.write_ue(0); + w.write_ue(0); // min CB 8 + w.write_ue(1); // CTB 16 + w.write_ue(0); // min TB 4 + w.write_ue(2); // max TB 16 + w.write_ue(0); + w.write_ue(0); + w.write_bits(0, 1); // scaling lists + w.write_bits(0, 1); // amp + w.write_bits(0, 1); // sao + w.write_bits(1, 1); // pcm + w.write_bits(7, 4); + w.write_bits(7, 4); + w.write_ue(0); // min PCM 8 + w.write_ue(1); // max PCM 16 + w.write_bits(pcm_loop_filter_disabled as u32, 1); + w.write_ue(0); // num_short_term_ref_pic_sets + w.write_bits(1, 1); // long_term_ref_pics_present + w.write_ue(0); + w.write_bits(0, 1); // temporal mvp + w.write_bits(0, 1); // strong intra smoothing + w.write_bits(0, 1); // vui + w.write_bits(0, 1); // extensions + w.rbsp_trailing(); + nal(33, &w.finish()) +} + +/// Tile layout: None = no tiles; Some((column widths, row heights, uniform, across)). +type Tiles = Option<(Vec, Vec, bool, bool)>; + +fn pps(id: u32, tiles: &Tiles, lists_mod: bool) -> Vec { + let mut w = BitWriter::new(); + w.write_ue(id); + w.write_ue(0); + w.write_bits(1, 1); // dependent slice segments enabled + w.write_bits(0, 1); + w.write_bits(0, 3); + w.write_bits(0, 1); // sign hiding + w.write_bits(0, 1); // cabac_init_present + w.write_ue(0); + w.write_ue(0); + w.write_se(0); // init_qp 26 + w.write_bits(0, 1); + w.write_bits(0, 1); + w.write_bits(0, 1); // cu_qp_delta + w.write_se(0); + w.write_se(0); + w.write_bits(0, 1); + w.write_bits(0, 1); + w.write_bits(0, 1); + w.write_bits(0, 1); // transquant bypass + w.write_bits(tiles.is_some() as u32, 1); + w.write_bits(0, 1); // entropy coding sync + if let Some((cols, rows, uniform, across)) = tiles { + w.write_ue(cols.len() as u32 - 1); + w.write_ue(rows.len() as u32 - 1); + w.write_bits(*uniform as u32, 1); + if !uniform { + for c in &cols[..cols.len() - 1] { + w.write_ue(*c as u32 - 1); + } + for r in &rows[..rows.len() - 1] { + w.write_ue(*r as u32 - 1); + } + } + w.write_bits(*across as u32, 1); + } + w.write_bits(1, 1); // loop filter across slices + w.write_bits(0, 1); // deblocking control + w.write_bits(0, 1); // scaling list + w.write_bits(lists_mod as u32, 1); + w.write_ue(0); + w.write_bits(0, 1); + w.write_bits(0, 1); + w.rbsp_trailing(); + nal(34, &w.finish()) +} + +/// Tile scan order of CTB raster addresses and the tile index of each CTB (6.5.1). +fn tile_scan(tiles: &Tiles) -> (Vec, Vec) { + let (cols, rows) = match tiles { + Some((c, r, _, _)) => (c.clone(), r.clone()), + None => (vec![WC], vec![HC]), + }; + let mut order = Vec::new(); + let mut tile = vec![0; WC * HC]; + let mut t = 0; + let mut y0 = 0; + for &rh in &rows { + let mut x0 = 0; + for &cw in &cols { + for y in y0..y0 + rh { + for x in x0..x0 + cw { + order.push(y * WC + x); + tile[y * WC + x] = t; + } + } + t += 1; + x0 += cw; + } + y0 += rh; + } + (order, tile) +} + +#[derive(Clone, Copy, PartialEq)] +enum Cu { + Pcm, + Skip, +} + +/// One slice segment: first CTB index in tile scan, number of CTBs, dependent flag. +struct Segment { + start: usize, + len: usize, + dependent: bool, +} + +struct PicDesc { + nal_type: u8, + poc: u32, + intra: bool, + cu: Cu, + pps_id: u32, + /// st RPS: (delta, used) negative pictures. + st: Vec<(i32, bool)>, + /// long-term: (poc lsb, used). + lt: Vec<(u32, bool)>, + num_ref: u32, + list_entry: Option>, + segments: Vec, +} + +/// Write the slice segment NAL units of a picture. +fn encode_picture(d: &PicDesc, tiles: &Tiles, pcm_content: &Planes, out: &mut Vec, saved: &mut Option) { + let (order, tile_of) = tile_scan(tiles); + let qp = 26; + let init_type = if d.intra { 0 } else { 1 }; + for seg in &d.segments { + // slice data first (entry points go into the header) + let mut e = Enc::new(BitWriter::new(), qp, init_type); + if seg.dependent { + e.ctx = saved.expect("contexts of the previous segment"); + } + let mut starts = vec![0usize]; + for k in seg.start..seg.start + seg.len { + let rs = order[k]; + let (rx, ry) = (rs % WC, rs / WC); + // the first CTU of a tile always starts from initialised contexts (9.3.1) + if k > 0 && tile_of[rs] != tile_of[order[k - 1]] { + init_contexts(&mut e.ctx, qp, init_type); + } + e.decision(SPLIT_CU, 0); + match d.cu { + Cu::Pcm => { + if !d.intra { + e.decision(CU_SKIP, 0); + e.decision(PRED_MODE, 1); + } + e.terminate(1); // pcm_flag + e.w.align_zero(); + for y in 0..CTB { + for x in 0..CTB { + e.w.write_bits(pcm_content.y[(ry * CTB + y) * W + rx * CTB + x] as u32, 8); + } + } + for plane in [&pcm_content.u, &pcm_content.v] { + for y in 0..CTB / 2 { + for x in 0..CTB / 2 { + e.w.write_bits(plane[(ry * CTB / 2 + y) * W / 2 + rx * CTB / 2 + x] as u32, 8); + } + } + } + e.reset_engine(); + } + Cu::Skip => { + // all CUs are skipped: ctxInc = available left + available above (single tile / slice) + let inc = (rx > 0) as usize + (ry > 0) as usize; + e.decision(CU_SKIP + inc, 1); + } + } + let last = k + 1 == seg.start + seg.len; + e.terminate(last as u32); + if last { + e.w.align_zero(); + } else if tile_of[order[k + 1]] != tile_of[rs] { + e.terminate(1); // end_of_subset_one_bit + e.w.align_zero(); + starts.push(e.w.bit_len() / 8); + e.reset_engine(); + } + } + *saved = Some(e.ctx); + let data = e.w.finish(); + // header, iterating until the entry point offsets (measured on the escaped NAL) are stable + let mut offsets: Vec = starts.windows(2).map(|s| (s[1] - s[0]) as u32).collect(); + loop { + let mut w = BitWriter::new(); + let first = seg.start == 0; + w.write_bit(first); + if (16..=23).contains(&d.nal_type) { + w.write_bit(false); + } + w.write_ue(d.pps_id); + if !first { + w.write_bit(seg.dependent); + w.write_bits(order[seg.start] as u32, 5); // Ceil(Log2(24)) bits + } + if !seg.dependent { + w.write_ue(if d.intra { 2 } else { 1 }); + if d.nal_type != 19 && d.nal_type != 20 { + w.write_bits(d.poc & 255, 8); + w.write_bit(false); // short_term_ref_pic_set_sps_flag + w.write_ue(d.st.len() as u32); + w.write_ue(0); + let mut prev = 0; + for &(dp, used) in &d.st { + w.write_ue((prev - dp - 1) as u32); + w.write_bit(used); + prev = dp; + } + w.write_ue(d.lt.len() as u32); + for &(lsb, used) in &d.lt { + w.write_bits(lsb, 8); + w.write_bit(used); + w.write_bit(false); + } + } + if !d.intra { + w.write_bit(true); // num_ref_idx_active_override_flag + w.write_ue(d.num_ref - 1); + let total = d.st.iter().filter(|s| s.1).count() + d.lt.iter().filter(|l| l.1).count(); + // PPS 1 has lists_modification_present_flag set + if d.list_entry.is_none() && d.pps_id == 1 && total > 1 { + w.write_bit(false); + } + if let Some(e) = &d.list_entry { + w.write_bit(true); + let bits = 32 - (total as u32 - 1).leading_zeros(); + for &v in e { + w.write_bits(v, bits); + } + } + w.write_ue(4); // five_minus_max_num_merge_cand + } + w.write_se(0); // slice_qp_delta + w.write_bit(true); // slice_loop_filter_across_slices_enabled_flag + } + if tiles.is_some() { + w.write_ue(offsets.len() as u32); + if !offsets.is_empty() { + w.write_ue(31); + for &o in &offsets { + w.write_bits(o - 1, 32); + } + } + } + w.write_bit(true); + w.align_zero(); + let mut rbsp = w.finish(); + let hdr_len = rbsp.len(); + rbsp.extend_from_slice(&data); + // escaped position of every substream start + let esc_pos = |idx: usize| escape_rbsp(&rbsp[..idx]).len(); + let s: Vec = starts.iter().map(|&s| esc_pos(hdr_len + s)).collect(); + let new: Vec = s.windows(2).map(|p| (p[1] - p[0]) as u32).collect(); + if new == offsets { + out.extend(nal(d.nal_type, &rbsp)); + break; + } + offsets = new; + } + } +} + +fn whole(n: usize) -> Vec { + vec![Segment { start: 0, len: n, dependent: false }] +} + +/// Encode the test sequence; returns the stream and the exact expected pictures (when the PCM samples +/// are not filtered). +fn sequence(tiles: &Tiles, pcm_lf_disabled: bool, segments: Vec) -> (Vec, Vec) { + let mut s = Vec::new(); + s.extend(vps()); + s.extend(sps(pcm_lf_disabled)); + s.extend(pps(0, tiles, false)); + s.extend(pps(1, &None, true)); + let n = WC * HC; + let mut saved = None; + let c0 = content(0); + let c1 = content(1); + // 0: IDR, PCM, tiles / slices / dependent segments + let p0 = PicDesc { nal_type: 19, poc: 0, intra: true, cu: Cu::Pcm, pps_id: 0, st: vec![], lt: vec![], num_ref: 0, list_entry: None, segments }; + encode_picture(&p0, tiles, &c0, &mut s, &mut saved); + // 1: intra TRAIL_R keeping picture 0 as a long-term reference (not used by this picture) + let p1 = PicDesc { + nal_type: 1, + poc: 1, + intra: true, + cu: Cu::Pcm, + pps_id: 1, + st: vec![], + lt: vec![(0, false)], + num_ref: 0, + list_entry: None, + segments: whole(n), + }; + encode_picture(&p1, &None, &c1, &mut s, &mut saved); + // 2: P, all skip, RefPicList0 = { LT picture 0 } -> a copy of picture 0 + let p2 = PicDesc { + nal_type: 1, + poc: 2, + intra: false, + cu: Cu::Skip, + pps_id: 1, + st: vec![(-1, false)], + lt: vec![(0, true)], + num_ref: 1, + list_entry: None, + segments: whole(n), + }; + encode_picture(&p2, &None, &c1, &mut s, &mut saved); + // 3: P, all skip, list modification puts the long-term picture first -> picture 0 again + let p3 = PicDesc { + nal_type: 1, + poc: 3, + intra: false, + cu: Cu::Skip, + pps_id: 1, + st: vec![(-1, true), (-2, true)], + lt: vec![(0, true)], + num_ref: 3, + list_entry: Some(vec![2, 0, 1]), + segments: whole(n), + }; + encode_picture(&p3, &None, &c1, &mut s, &mut saved); + // 4: P, all skip, default list order: refIdx 0 = picture 3 (a copy of picture 0) + let p4 = PicDesc { + nal_type: 1, + poc: 4, + intra: false, + cu: Cu::Skip, + pps_id: 1, + st: vec![(-1, true), (-3, true)], + lt: vec![(0, true)], + num_ref: 2, + list_entry: None, + segments: whole(n), + }; + encode_picture(&p4, &None, &c1, &mut s, &mut saved); + let expect = vec![content(0), content(1), content(0), content(0), content(0)]; + (s, expect) +} + +fn decode_all(stream: &[u8], threads: usize) -> Vec { + let mut dec = Decoder::with_threads(threads); + let mut pics = dec.decode(stream, 0).expect("decode"); + pics.extend(dec.flush()); + assert!(dec.take_error().is_none()); + pics +} + +fn as_u8(p: &crate::Plane) -> &[u8] { + p.as_u8().expect("8-bit output") +} + +/// Compare with ffmpeg's decode of the same stream (skipped when ffmpeg is absent). +fn check_ffmpeg(name: &str, stream: &[u8], pics: &[crate::Picture]) { + // The ffmpeg cross-check lives in FilmCraft (test oracle). + let _ = (name, stream, pics); +} + +fn run(name: &str, tiles: Tiles, pcm_lf_disabled: bool, segments: Vec) { + let (stream, expect) = sequence(&tiles, pcm_lf_disabled, segments); + if let Ok(d) = std::env::var("HEVC_SYNTH_DUMP") { + std::fs::write(format!("{d}/{name}.hevc"), &stream).unwrap(); + } + for threads in [1, 4] { + let pics = decode_all(&stream, threads); + assert_eq!(pics.len(), expect.len(), "{name}"); + for (i, (p, e)) in pics.iter().zip(&expect).enumerate() { + assert_eq!(p.poc, i as i32); + if pcm_lf_disabled { + assert!(as_u8(&p.y) == &e.y[..], "{name}: picture {i} luma"); + assert!(as_u8(&p.u) == &e.u[..] && as_u8(&p.v) == &e.v[..], "{name}: picture {i} chroma"); + } + } + if threads == 1 { + check_ffmpeg(name, &stream, &pics); + } + } +} + +#[test] +fn pcm_long_term_refs_list_modification() { + run("pcm_lt", None, true, whole(WC * HC)); +} + +#[test] +fn pcm_deblocked_single_tile() { + run("pcm_dbk", None, false, whole(WC * HC)); +} + +#[test] +fn tiles_uniform_exact() { + run("tiles_uniform", Some((vec![2, 2, 2], vec![2, 2], true, true)), true, whole(WC * HC)); +} + +#[test] +fn tiles_explicit_filter_across() { + run("tiles_across", Some((vec![1, 3, 2], vec![1, 3], false, true)), false, whole(WC * HC)); +} + +#[test] +fn tiles_explicit_no_filter_across() { + run("tiles_no_across", Some((vec![2, 3, 1], vec![3, 1], false, false)), false, whole(WC * HC)); +} + +#[test] +fn tiles_slices_and_dependent_segments() { + // tiles 2x2 (3x2 CTBs each): slice 0 = tiles 0-1, slice 1 = tiles 2-3 split into three segments, + // the dependent ones starting inside a tile (context restore) and at a tile start + let segs = vec![ + Segment { start: 0, len: 12, dependent: false }, + Segment { start: 12, len: 2, dependent: false }, + Segment { start: 14, len: 4, dependent: true }, + Segment { start: 18, len: 6, dependent: true }, + ]; + run("tiles_slices", Some((vec![3, 3], vec![2, 2], true, false)), false, segs); +} + +#[test] +fn slices_without_tiles() { + let segs = vec![ + Segment { start: 0, len: 7, dependent: false }, + Segment { start: 7, len: 9, dependent: true }, + Segment { start: 16, len: 8, dependent: false }, + ]; + run("slices", None, false, segs); +} diff --git a/crates/hevc/src/tables.rs b/crates/hevc/src/tables.rs new file mode 100644 index 0000000..bce0b7a --- /dev/null +++ b/crates/hevc/src/tables.rs @@ -0,0 +1,123 @@ +//! Derived tables: scan orders (6.5.3 - 6.5.5), chroma QP mapping and small helper tables. + +pub use crate::spec_tables::*; + +/// 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]]; + +#[cfg(test)] +mod tests { + use super::*; + + #[test] + fn diagonal_scan_4x4() { + let s = &SCAN_4[0]; + assert_eq!(&s[..6], &[(0, 0), (0, 1), (1, 0), (0, 2), (1, 1), (2, 0)]); + assert_eq!(s[15], (3, 3)); + assert_eq!(SCAN_2[0], [(0, 0), (0, 1), (1, 0), (1, 1)]); + assert_eq!(SCAN_4[2][1], (0, 1)); + assert_eq!(SCAN_4[1][1], (1, 0)); + } + + #[test] + fn dct_matrix_properties() { + // rows are nearly orthogonal with norm ~ 64^2 * 32 + for a in 0..32 { + for b in 0..32 { + let d: i64 = (0..32).map(|k| TRANS_MATRIX[a][k] as i64 * TRANS_MATRIX[b][k] as i64).sum(); + if a == b { + assert!((d - 131072).abs() < 1000, "row {a} norm {d}"); + } else { + assert!(d.abs() < 1000, "rows {a},{b}: {d}"); + } + } + } + // even basis functions are symmetric, odd ones antisymmetric + for n in 0..32 { + for m in 0..16 { + let (a, b) = (TRANS_MATRIX[n][m] as i32, TRANS_MATRIX[n][31 - m] as i32); + assert_eq!(a, if n % 2 == 0 { b } else { -b }, "{n} {m}"); + } + } + } + + #[test] + fn qpc_table() { + assert_eq!(qpc_420(29), 29); + assert_eq!(qpc_420(30), 29); + assert_eq!(qpc_420(43), 37); + assert_eq!(qpc_420(51), 45); + } +} diff --git a/crates/hevc/src/transform.rs b/crates/hevc/src/transform.rs new file mode 100644 index 0000000..5c0361f --- /dev/null +++ b/crates/hevc/src/transform.rs @@ -0,0 +1,170 @@ +//! Scaling (8.6.3), transform skip and inverse transforms (8.6.4): DCT 4..32 and DST 4x4. + +use crate::tables::{DST_MATRIX, TRANS_MATRIX}; + +/// Inverse transform of an n x n block of scaled coefficients `c` (row-major, `c[y * n + x]`) in place, +/// producing residuals after the final `bd_shift` rounding (8-299). `max_x` / `max_y` bound the +/// non-zero coefficients (inclusive). `dst`: DST-VII (intra 4x4 luma). +/// +/// Evaluated as sums of basis rows scaled by the non-zero coefficients (zero coefficients, which +/// are most of them, cost nothing), with the size a compile-time constant so the inner loops over +/// contiguous basis rows vectorise. Integer sums are exact (|sum| < 2^27), so the result equals the +/// direct matrix product of 8.6.4.2 in any summation order. +pub fn inverse_transform(c: &mut [i32], n: usize, dst: bool, max_x: usize, max_y: usize, bd_shift: u32) { + match n { + 4 => inverse::<4>(c, dst, max_x, max_y, bd_shift), + 8 => inverse::<8>(c, false, max_x, max_y, bd_shift), + 16 => inverse::<16>(c, false, max_x, max_y, bd_shift), + _ => inverse::<32>(c, false, max_x, max_y, bd_shift), + } +} + +#[inline(always)] +fn inverse(c: &mut [i32], dst: bool, max_x: usize, max_y: usize, bd_shift: u32) { + let step = 32 / N; + // basis function of coefficient index j (row j of the transform matrix, N samples) + let basis = |j: usize| -> &[i8] { if dst { &DST_MATRIX[j][..N] } else { &TRANS_MATRIX[j * step][..N] } }; + // 1. columns (8-297 ... first stage): e[x][y] = sum_j M[j][y] * d[x][j], stored transposed + // (col[x][y]) so each coefficient adds one contiguous basis row. + let mut col = [[0i32; N]; N]; + for x in 0..=max_x.min(N - 1) { + let acc = &mut col[x]; + for j in 0..=max_y.min(N - 1) { + let d = c[j * N + x]; + if d != 0 { + for (a, &m) in acc.iter_mut().zip(basis(j)) { + *a += m as i32 * d; + } + } + } + for a in acc.iter_mut() { + *a = ((*a + 64) >> 7).clamp(-32768, 32767); + } + } + // 2. rows: r[x][y] = sum_j M[j][x] * g[j][y], g[j][y] = col[j][y] + let round = 1 << (bd_shift - 1); + for y in 0..N { + let mut out = [round; N]; + for (j, g) in col.iter().enumerate().take(max_x.min(N - 1) + 1) { + let g = g[y]; + if g != 0 { + for (o, &m) in out.iter_mut().zip(basis(j)) { + *o += m as i32 * g; + } + } + } + for (r, o) in c[y * N..y * N + N].iter_mut().zip(out) { + *r = o >> bd_shift; + } + } +} + +/// DC-only inverse DCT: every residual sample gets the same value. +pub fn inverse_dc(dc: i32, bd_shift: u32) -> i32 { + let e = ((64 * dc + 64) >> 7).clamp(-32768, 32767); + (64 * e + (1 << (bd_shift - 1))) >> bd_shift +} + +/// Transform skip residual (8-298, 8-299): r = (d << tsShift + round) >> bdShift. +pub fn transform_skip(c: &mut [i32], n: usize, bd_shift: u32) { + let ts_shift = 5 + n.trailing_zeros(); + let round = 1 << (bd_shift - 1); + for v in c[..n * n].iter_mut() { + *v = ((*v << ts_shift) + round) >> bd_shift; + } +} + +#[cfg(test)] +mod tests { + use super::*; + + /// The direct matrix product of 8.6.4.2 (the original implementation), as the reference. + fn reference(c: &mut [i32], n: usize, dst: bool, max_x: usize, max_y: usize, bd_shift: u32) { + let mut tmp = [0i32; 32 * 32]; + let step = 32 / n; + for x in 0..=max_x { + for y in 0..n { + let mut s = 0i32; + for j in 0..=max_y { + 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 round = 1 << (bd_shift - 1); + for y in 0..n { + for x in 0..n { + let mut s = 0i32; + for j in 0..=max_x { + 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 + round) >> bd_shift; + } + } + } + + #[test] + fn matches_the_direct_matrix_product() { + // sparse and dense blocks, extreme coefficients, every size, DST, 8- and 10-bit shifts + let mut rng = 0x2545_f491_4f6c_dd1du64; + let mut next = move || { + rng ^= rng << 13; + rng ^= rng >> 7; + rng ^= rng << 17; + rng + }; + for &(n, dst) in &[(4usize, true), (4, false), (8, false), (16, false), (32, false)] { + for case in 0..400 { + let (max_x, max_y) = ((next() as usize) % n, (next() as usize) % n); + let density = [2u64, 8, 50, 100][case % 4]; + let mut c = vec![0i32; n * n]; + for y in 0..=max_y { + for x in 0..=max_x { + if next() % 100 < density { + c[y * n + x] = match next() % 8 { + 0 => 32767, + 1 => -32768, + _ => (next() % 2001) as i32 - 1000, + }; + } + } + } + for bd_shift in [12u32, 10] { + let (mut a, mut b) = (c.clone(), c.clone()); + inverse_transform(&mut a, n, dst, max_x, max_y, bd_shift); + reference(&mut b, n, dst, max_x, max_y, bd_shift); + assert_eq!(a, b, "n {n} dst {dst} max ({max_x}, {max_y}) shift {bd_shift}"); + } + } + } + } + + #[test] + fn dc_only_matches_full_transform() { + for &n in &[4usize, 8, 16, 32] { + for dc in [-300, -1, 1, 7, 64, 1000] { + let mut c = vec![0i32; n * n]; + c[0] = dc; + inverse_transform(&mut c, n, false, 0, 0, 12); + let v = inverse_dc(dc, 12); + assert!(c.iter().all(|&x| x == v), "n {n} dc {dc}"); + } + } + } + + #[test] + fn bounded_region_matches_full() { + let n = 16; + let mut c = vec![0i32; n * n]; + c[0] = 50; + c[1] = -20; + c[2 * n + 3] = 9; + let mut a = c.clone(); + inverse_transform(&mut a, n, false, 3, 2, 12); + let mut b = c.clone(); + inverse_transform(&mut b, n, false, n - 1, n - 1, 12); + assert_eq!(a, b); + } +} diff --git a/crates/isobmff/Cargo.toml b/crates/isobmff/Cargo.toml new file mode 100644 index 0000000..eb2dc56 --- /dev/null +++ b/crates/isobmff/Cargo.toml @@ -0,0 +1,14 @@ +[package] +name = "slidecraft-isobmff" +description = "Clean-room ISO BMFF (MP4) and QuickTime (MOV) demuxer and muxer" +version.workspace = true +edition.workspace = true +license.workspace = true +rust-version.workspace = true + +[lints] +workspace = true + +[dependencies] +slidecraft-bitstream = { workspace = true } +thiserror = { workspace = true } diff --git a/crates/isobmff/src/bytes.rs b/crates/isobmff/src/bytes.rs new file mode 100644 index 0000000..97f9269 --- /dev/null +++ b/crates/isobmff/src/bytes.rs @@ -0,0 +1,361 @@ +//! Low-level helpers: four-character codes, a bounds-checked big-endian cursor, box iteration +//! over in-memory payloads, and a box builder for the muxers. + +use crate::error::{Error, Result}; +use std::fmt; + +/// A four-character code (box type, sample entry format, brand, handler type…). +#[derive(Clone, Copy, PartialEq, Eq, Hash, PartialOrd, Ord, Default)] +pub struct FourCc(pub [u8; 4]); + +impl FourCc { + pub const fn new(b: &[u8; 4]) -> Self { + FourCc(*b) + } + pub fn as_u32(self) -> u32 { + u32::from_be_bytes(self.0) + } + /// Lossy string form; `©` (0xA9) is mapped to U+00A9, other non-printables to `?`. + pub fn to_string_lossy(self) -> String { + self.0 + .iter() + .map(|&b| match b { + 0xA9 => '©', + 0x20..=0x7E => b as char, + _ => '?', + }) + .collect() + } +} + +impl fmt::Display for FourCc { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + f.write_str(&self.to_string_lossy()) + } +} +impl fmt::Debug for FourCc { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + write!(f, "FourCc({:?})", self.to_string_lossy()) + } +} +impl From<&[u8; 4]> for FourCc { + fn from(b: &[u8; 4]) -> Self { + FourCc(*b) + } +} +impl PartialEq<[u8; 4]> for FourCc { + fn eq(&self, other: &[u8; 4]) -> bool { + &self.0 == other + } +} +impl PartialEq<&[u8; 4]> for FourCc { + fn eq(&self, other: &&[u8; 4]) -> bool { + &self.0 == *other + } +} +impl PartialEq<&&[u8; 4]> for FourCc { + fn eq(&self, other: &&&[u8; 4]) -> bool { + &self.0 == **other + } +} + +/// Bounds-checked big-endian cursor over a byte slice. +#[derive(Clone)] +pub(crate) struct Cur<'a> { + pub data: &'a [u8], + pub pos: usize, + what: &'static str, +} + +impl<'a> Cur<'a> { + pub fn new(data: &'a [u8], what: &'static str) -> Self { + Cur { data, pos: 0, what } + } + pub fn remaining(&self) -> usize { + self.data.len().saturating_sub(self.pos) + } + pub fn rest(&self) -> &'a [u8] { + self.data.get(self.pos..).unwrap_or(&[]) + } + pub fn bytes(&mut self, n: usize) -> Result<&'a [u8]> { + let end = self.pos.checked_add(n).ok_or(Error::Truncated(self.what))?; + let s = self.data.get(self.pos..end).ok_or(Error::Truncated(self.what))?; + self.pos = end; + Ok(s) + } + pub fn skip(&mut self, n: usize) -> Result<()> { + self.bytes(n).map(|_| ()) + } + pub fn array(&mut self) -> Result<[u8; N]> { + let s = self.bytes(N)?; + let mut a = [0u8; N]; + a.copy_from_slice(s); + Ok(a) + } + pub fn u8(&mut self) -> Result { + Ok(self.array::<1>()?[0]) + } + pub fn u16(&mut self) -> Result { + Ok(u16::from_be_bytes(self.array()?)) + } + pub fn i16(&mut self) -> Result { + Ok(i16::from_be_bytes(self.array()?)) + } + pub fn u24(&mut self) -> Result { + let a = self.array::<3>()?; + Ok(((a[0] as u32) << 16) | ((a[1] as u32) << 8) | a[2] as u32) + } + pub fn u32(&mut self) -> Result { + Ok(u32::from_be_bytes(self.array()?)) + } + pub fn i32(&mut self) -> Result { + Ok(i32::from_be_bytes(self.array()?)) + } + pub fn u64(&mut self) -> Result { + Ok(u64::from_be_bytes(self.array()?)) + } + pub fn i64(&mut self) -> Result { + Ok(i64::from_be_bytes(self.array()?)) + } + pub fn f64(&mut self) -> Result { + Ok(f64::from_be_bytes(self.array()?)) + } + pub fn fourcc(&mut self) -> Result { + Ok(FourCc(self.array()?)) + } + /// Full-box header: (version, flags). + pub fn full_header(&mut self) -> Result<(u8, u32)> { + let v = self.u32()?; + Ok(((v >> 24) as u8, v & 0x00FF_FFFF)) + } + /// Read a count and verify that `count * entry_size` bytes remain (allocation guard). + pub fn count(&mut self, entry_size: usize) -> Result { + let n = self.u32()? as usize; + self.check_count(n, entry_size)?; + Ok(n) + } + pub fn check_count(&self, n: usize, entry_size: usize) -> Result<()> { + match n.checked_mul(entry_size) { + Some(b) if b <= self.remaining() => Ok(()), + _ => Err(Error::Truncated(self.what)), + } + } +} + +/// Header of one box as found in a byte stream. +#[derive(Clone, Copy, Debug, PartialEq, Eq)] +pub(crate) struct BoxHeader { + pub kind: FourCc, + /// Header length including largesize and uuid extended type. + pub header_len: u64, + /// Total box size including header; `None` means "extends to end of the enclosing container". + pub size: Option, + pub uuid: Option<[u8; 16]>, +} + +/// Parse a box header from `buf` (needs up to 32 bytes; fewer if available). +pub(crate) fn parse_box_header(buf: &[u8]) -> Result { + let mut c = Cur::new(buf, "box header"); + let size32 = c.u32()?; + let kind = c.fourcc()?; + let mut header_len = 8u64; + let size = match size32 { + 0 => None, + 1 => { + header_len += 8; + let s = c.u64()?; + Some(s) + } + s => Some(s as u64), + }; + let uuid = if kind == b"uuid" { + header_len += 16; + Some(c.array::<16>()?) + } else { + None + }; + if let Some(s) = size + && s < header_len + { + return Err(Error::Invalid(format!("box '{kind}' size {s} smaller than its header"))); + } + Ok(BoxHeader { kind, header_len, size, uuid }) +} + +/// One child box inside an in-memory payload. +#[derive(Clone, Copy, Debug)] +pub(crate) struct RawBox<'a> { + pub kind: FourCc, + pub payload: &'a [u8], + #[allow(dead_code)] + pub uuid: Option<[u8; 16]>, +} + +/// Iterator over the child boxes of a payload. Yields an error for a malformed box and then stops. +pub(crate) struct Boxes<'a> { + data: &'a [u8], + pos: usize, + done: bool, +} + +pub(crate) fn boxes(data: &[u8]) -> Boxes<'_> { + Boxes { data, pos: 0, done: false } +} + +impl<'a> Iterator for Boxes<'a> { + type Item = Result>; + fn next(&mut self) -> Option { + if self.done { + return None; + } + let rest = &self.data[self.pos..]; + if rest.is_empty() { + self.done = true; + return None; + } + // QuickTime allows a 32-bit zero terminator at the end of some containers. + if rest.len() < 8 { + self.done = true; + if rest.iter().all(|&b| b == 0) { + return None; + } + return Some(Err(Error::Truncated("box header"))); + } + let h = match parse_box_header(rest) { + Ok(h) => h, + Err(e) => { + self.done = true; + return Some(Err(e)); + } + }; + let size = h.size.unwrap_or(rest.len() as u64); + if size > rest.len() as u64 { + self.done = true; + return Some(Err(Error::Invalid(format!("box '{}' size {} exceeds its container ({} bytes left)", h.kind, size, rest.len())))); + } + let size = size as usize; + let payload = &rest[h.header_len as usize..size]; + self.pos += size; + Some(Ok(RawBox { kind: h.kind, payload, uuid: h.uuid })) + } +} + +/// Find the first child of the given type (malformed trailing children are ignored). +pub(crate) fn find<'a>(data: &'a [u8], kind: &[u8; 4]) -> Option<&'a [u8]> { + boxes(data).map_while(|b| b.ok()).find(|b| b.kind == kind).map(|b| b.payload) +} + +/// Growable buffer for building boxes. +#[derive(Default, Clone, Debug)] +pub(crate) struct BoxBuf { + pub buf: Vec, +} + +impl BoxBuf { + pub fn new() -> Self { + Self::default() + } + /// Begin a box; returns a marker to pass to [`end`](Self::end). + pub fn start(&mut self, kind: &[u8; 4]) -> usize { + let at = self.buf.len(); + self.buf.extend_from_slice(&[0, 0, 0, 0]); + self.buf.extend_from_slice(kind); + at + } + pub fn start_full(&mut self, kind: &[u8; 4], version: u8, flags: u32) -> usize { + let at = self.start(kind); + self.u32(((version as u32) << 24) | (flags & 0xFF_FFFF)); + at + } + pub fn end(&mut self, at: usize) { + let size = (self.buf.len() - at) as u32; + self.buf[at..at + 4].copy_from_slice(&size.to_be_bytes()); + } + /// Append a complete box with the given payload. + pub fn leaf(&mut self, kind: &[u8; 4], payload: &[u8]) { + let at = self.start(kind); + self.bytes(payload); + self.end(at); + } + pub fn u8(&mut self, v: u8) { + self.buf.push(v); + } + pub fn u16(&mut self, v: u16) { + self.buf.extend_from_slice(&v.to_be_bytes()); + } + pub fn i16(&mut self, v: i16) { + self.buf.extend_from_slice(&v.to_be_bytes()); + } + pub fn u32(&mut self, v: u32) { + self.buf.extend_from_slice(&v.to_be_bytes()); + } + pub fn i32(&mut self, v: i32) { + self.buf.extend_from_slice(&v.to_be_bytes()); + } + pub fn u64(&mut self, v: u64) { + self.buf.extend_from_slice(&v.to_be_bytes()); + } + pub fn i64(&mut self, v: i64) { + self.buf.extend_from_slice(&v.to_be_bytes()); + } + pub fn f64(&mut self, v: f64) { + self.buf.extend_from_slice(&v.to_be_bytes()); + } + pub fn bytes(&mut self, b: &[u8]) { + self.buf.extend_from_slice(b); + } + pub fn zeros(&mut self, n: usize) { + self.buf.resize(self.buf.len() + n, 0); + } + pub fn len(&self) -> usize { + self.buf.len() + } +} + +#[cfg(test)] +mod tests { + use super::*; + + #[test] + fn header_forms() { + let h = parse_box_header(&[0, 0, 0, 16, b'f', b'r', b'e', b'e']).unwrap(); + assert_eq!((h.kind, h.header_len, h.size), (FourCc(*b"free"), 8, Some(16))); + let h = parse_box_header(&[0, 0, 0, 0, b'm', b'd', b'a', b't']).unwrap(); + assert_eq!(h.size, None); + let mut v = vec![0, 0, 0, 1, b'm', b'd', b'a', b't']; + v.extend_from_slice(&100u64.to_be_bytes()); + let h = parse_box_header(&v).unwrap(); + assert_eq!((h.header_len, h.size), (16, Some(100))); + let mut v = vec![0, 0, 0, 30, b'u', b'u', b'i', b'd']; + v.extend_from_slice(&[7u8; 16]); + let h = parse_box_header(&v).unwrap(); + assert_eq!((h.header_len, h.uuid), (24, Some([7u8; 16]))); + assert!(parse_box_header(&[0, 0, 0, 4, b'f', b'r', b'e', b'e']).is_err()); + assert!(parse_box_header(&[0, 0, 0]).is_err()); + } + + #[test] + fn iterate_children() { + let mut b = BoxBuf::new(); + b.leaf(b"aaaa", &[1, 2]); + let m = b.start(b"bbbb"); + b.leaf(b"cccc", &[]); + b.end(m); + b.u32(0); // QT terminator + let kids: Vec<_> = boxes(&b.buf).collect::>>().unwrap(); + assert_eq!(kids.len(), 2); + assert_eq!(kids[0].payload, &[1, 2]); + assert_eq!(find(&b.buf, b"bbbb").map(|p| p.len()), Some(8)); + // Oversized child → error, no panic. + let bad = [0u8, 0, 0, 99, b'x', b'x', b'x', b'x', 0]; + assert!(boxes(&bad).next().unwrap().is_err()); + } + + #[test] + fn cursor_bounds() { + let mut c = Cur::new(&[0, 0, 0, 5, 1], "t"); + assert!(c.count(1).is_err()); + let mut c = Cur::new(&[0xFF, 0xFF, 0xFF, 0xFF], "t"); + assert!(c.count(usize::MAX).is_err()); + } +} diff --git a/crates/isobmff/src/codec.rs b/crates/isobmff/src/codec.rs new file mode 100644 index 0000000..d78a3c8 --- /dev/null +++ b/crates/isobmff/src/codec.rs @@ -0,0 +1,1581 @@ +//! Sample entries (`stsd` children) and codec configuration records: parsing and serialization. + +use crate::bytes::{BoxBuf, Cur, FourCc, boxes}; +use crate::error::{Error, Result}; +use slidecraft_bitstream::BitReader; + +/// One sample description (an entry of `stsd`). +#[derive(Clone, Debug, PartialEq)] +pub struct SampleEntry { + /// Sample entry format (`avc1`, `mp4a`, `apch`, …). + pub format: FourCc, + pub data_reference_index: u16, + pub codec: CodecConfig, + /// Present for visual sample entries. + pub video: Option, + /// Present for audio sample entries. + pub audio: Option, + /// `btrt` box. + pub bitrate: Option, +} + +/// Fields common to all visual sample entries plus the optional descriptive boxes. +#[derive(Clone, Debug, PartialEq, Default)] +pub struct VideoParams { + pub width: u16, + pub height: u16, + /// Horizontal/vertical resolution in pixels per inch (16.16 fixed point; 72 dpi = 0x0048_0000). + pub horiz_resolution: u32, + pub vert_resolution: u32, + pub frame_count: u16, + pub compressor_name: String, + pub depth: u16, + /// QuickTime `vendor` / version fields (zero in ISO files). + pub vendor: FourCc, + pub temporal_quality: u32, + pub spatial_quality: u32, + pub color: Option, + /// `pasp`: (hSpacing, vSpacing). + pub pixel_aspect: Option<(u32, u32)>, + pub clean_aperture: Option, + pub field_info: Option, + /// QuickTime `gama` (16.16 fixed point). + pub gamma: Option, + /// `mdcv`: mastering display colour volume (SMPTE ST 2086). + pub mastering_display: Option, + /// `clli`: content light level (MaxCLL, MaxFALL in cd/m²; 0 = unknown). + pub content_light: Option<(u16, u16)>, +} + +/// SMPTE ST 2086 mastering display metadata, in the units of the `mdcv` box and the H.264/HEVC +/// SEI: chromaticities in 0.00002 steps (order G, B, R as the SEI), luminance in 0.0001 cd/m². +#[derive(Clone, Copy, Debug, PartialEq, Eq)] +pub struct MasteringDisplay { + pub primaries: [(u16, u16); 3], + pub white_point: (u16, u16), + pub max_luminance: u32, + pub min_luminance: u32, +} + +impl MasteringDisplay { + /// BT.2020 primaries, D65, with the given peak and black in cd/m². + pub fn bt2020(max_nits: f64, min_nits: f64) -> Self { + let q = |v: f64| (v / 0.00002).round() as u16; + MasteringDisplay { + primaries: [(q(0.170), q(0.797)), (q(0.131), q(0.046)), (q(0.708), q(0.292))], + white_point: (q(0.3127), q(0.3290)), + max_luminance: (max_nits * 10_000.0).round() as u32, + min_luminance: (min_nits * 10_000.0).round() as u32, + } + } + pub fn max_nits(&self) -> f64 { + self.max_luminance as f64 / 10_000.0 + } + /// The 24-byte payload shared by `mdcv` and the SEI message. + pub fn to_bytes(&self) -> Vec { + let mut v = Vec::with_capacity(24); + for (x, y) in self.primaries { + v.extend_from_slice(&x.to_be_bytes()); + v.extend_from_slice(&y.to_be_bytes()); + } + v.extend_from_slice(&self.white_point.0.to_be_bytes()); + v.extend_from_slice(&self.white_point.1.to_be_bytes()); + v.extend_from_slice(&self.max_luminance.to_be_bytes()); + v.extend_from_slice(&self.min_luminance.to_be_bytes()); + v + } + pub fn parse(p: &[u8]) -> Option { + if p.len() < 24 { + return None; + } + let u16_at = |i: usize| u16::from_be_bytes([p[i], p[i + 1]]); + let u32_at = |i: usize| u32::from_be_bytes([p[i], p[i + 1], p[i + 2], p[i + 3]]); + Some(MasteringDisplay { + primaries: [(u16_at(0), u16_at(2)), (u16_at(4), u16_at(6)), (u16_at(8), u16_at(10))], + white_point: (u16_at(12), u16_at(14)), + max_luminance: u32_at(16), + min_luminance: u32_at(20), + }) + } +} + +/// `colr` box contents. +#[derive(Clone, Debug, PartialEq, Eq)] +pub enum ColorInfo { + /// ISO `nclx` (with full-range flag). + Nclx { primaries: u16, transfer: u16, matrix: u16, full_range: bool }, + /// QuickTime `nclc`. + Nclc { primaries: u16, transfer: u16, matrix: u16 }, + /// Embedded ICC profile (`prof` restricted/unrestricted `rICC`). + Icc { kind: FourCc, profile: Vec }, +} + +/// `clap` clean aperture (all rationals num/den; offsets are signed). +#[derive(Clone, Copy, Debug, PartialEq, Eq)] +pub struct CleanAperture { + pub width: (u32, u32), + pub height: (u32, u32), + pub horiz_offset: (i32, u32), + pub vert_offset: (i32, u32), +} + +/// QuickTime `fiel`: field count (1 progressive, 2 interlaced) and ordering detail +/// (0, 1 = top first, 6 = bottom first, 9 / 14 per the QuickTime spec). +#[derive(Clone, Copy, Debug, PartialEq, Eq)] +pub struct FieldInfo { + pub fields: u8, + pub detail: u8, +} + +/// `btrt` box. +#[derive(Clone, Copy, Debug, PartialEq, Eq)] +pub struct BitRate { + pub buffer_size: u32, + pub max_bitrate: u32, + pub avg_bitrate: u32, +} + +/// Fields of audio sample entries (ISO v0 plus QuickTime sound description v1/v2 extensions). +#[derive(Clone, Debug, PartialEq, Default)] +pub struct AudioParams { + /// QuickTime sound description version (0, 1, 2). ISO files use 0. + pub qt_version: u16, + pub channels: u32, + /// Bits per sample from the entry (16 for most compressed formats). + pub sample_size: u16, + pub sample_rate: f64, + pub compression_id: i16, + /// v1 fields. + pub samples_per_packet: u32, + pub bytes_per_packet: u32, + pub bytes_per_frame: u32, + pub bytes_per_sample: u32, + /// v2 fields. + pub const_bits_per_channel: u32, + pub lpcm_flags: u32, + pub const_bytes_per_packet: u32, + pub const_frames_per_packet: u32, + /// `enda` little-endian flag inside a QuickTime `wave` box. + pub little_endian: Option, +} + +/// Codec configuration decoded from the sample entry. +#[derive(Clone, Debug, PartialEq)] +pub enum CodecConfig { + Avc(AvcConfig), + Hevc(HevcConfig), + Av1(Av1Config), + Vp9(VpcConfig), + /// Apple ProRes; fourcc is `apch`/`apcn`/`apcs`/`apco`/`ap4h`/`ap4x`. + ProRes { + fourcc: FourCc, + }, + /// Motion JPEG (`jpeg`, `mjpa`, `mjpb`). + Jpeg { + fourcc: FourCc, + }, + /// Avid DNxHD/DNxHR (`AVdn`, `AVdh`). + Dnx { + fourcc: FourCc, + }, + Aac(AacConfig), + /// MPEG-1/2 audio layer III (esds object type 0x69/0x6B, or QuickTime `.mp3`). + Mp3, + Pcm(PcmConfig), + /// ALAC magic cookie (ALACSpecificConfig, 24 bytes, optionally followed by a channel layout). + Alac { + cookie: Vec, + }, + Opus(OpusConfig), + Flac(FlacConfig), + /// `dac3` payload. + Ac3 { + dac3: Vec, + }, + /// `dec3` payload. + Eac3 { + dec3: Vec, + }, + Timecode(TimecodeConfig), + /// Anything else; `raw` is the full sample entry payload (after the 8-byte box header). + Unknown { + fourcc: FourCc, + raw: Vec, + }, +} + +impl CodecConfig { + /// A short human-readable codec name. + pub fn name(&self) -> &'static str { + match self { + CodecConfig::Avc(_) => "h264", + CodecConfig::Hevc(_) => "hevc", + CodecConfig::Av1(_) => "av1", + CodecConfig::Vp9(_) => "vp9", + CodecConfig::ProRes { .. } => "prores", + CodecConfig::Jpeg { .. } => "mjpeg", + CodecConfig::Dnx { .. } => "dnxhd", + CodecConfig::Aac(_) => "aac", + CodecConfig::Mp3 => "mp3", + CodecConfig::Pcm(_) => "pcm", + CodecConfig::Alac { .. } => "alac", + CodecConfig::Opus(_) => "opus", + CodecConfig::Flac(_) => "flac", + CodecConfig::Ac3 { .. } => "ac3", + CodecConfig::Eac3 { .. } => "eac3", + CodecConfig::Timecode(_) => "timecode", + CodecConfig::Unknown { .. } => "unknown", + } + } +} + +/// `avcC` — AVCDecoderConfigurationRecord (ISO/IEC 14496-15 §5.3.3). +#[derive(Clone, Debug, PartialEq, Eq, Default)] +pub struct AvcConfig { + pub profile: u8, + pub compatibility: u8, + pub level: u8, + /// NAL length field size in bytes (1, 2 or 4). + pub length_size: u8, + pub sps: Vec>, + pub pps: Vec>, + /// Bytes after the PPS list (high-profile extension: chroma format, bit depths, SPS-ext). + pub ext: Vec, +} + +impl AvcConfig { + /// Build from parameter sets (NAL units without start codes); profile/level taken from the first SPS. + pub fn new(sps: Vec>, pps: Vec>, length_size: u8) -> Self { + let (profile, compatibility, level) = sps.first().filter(|s| s.len() >= 4).map(|s| (s[1], s[2], s[3])).unwrap_or((0, 0, 0)); + AvcConfig { profile, compatibility, level, length_size, sps, pps, ext: Vec::new() } + } + + pub fn parse(data: &[u8]) -> Result { + let mut c = Cur::new(data, "avcC"); + let _version = c.u8()?; + let profile = c.u8()?; + let compatibility = c.u8()?; + let level = c.u8()?; + let length_size = (c.u8()? & 3) + 1; + let nsps = (c.u8()? & 0x1F) as usize; + let mut sps = Vec::with_capacity(nsps); + for _ in 0..nsps { + let n = c.u16()? as usize; + sps.push(c.bytes(n)?.to_vec()); + } + let npps = c.u8()? as usize; + let mut pps = Vec::with_capacity(npps); + for _ in 0..npps { + let n = c.u16()? as usize; + pps.push(c.bytes(n)?.to_vec()); + } + Ok(AvcConfig { profile, compatibility, level, length_size, sps, pps, ext: c.rest().to_vec() }) + } + + pub fn to_bytes(&self) -> Vec { + let mut b = BoxBuf::new(); + b.u8(1); + b.u8(self.profile); + b.u8(self.compatibility); + b.u8(self.level); + b.u8(0xFC | (self.length_size.clamp(1, 4) - 1)); + b.u8(0xE0 | (self.sps.len().min(31) as u8)); + for s in self.sps.iter().take(31) { + b.u16(s.len() as u16); + b.bytes(s); + } + b.u8(self.pps.len().min(255) as u8); + for p in self.pps.iter().take(255) { + b.u16(p.len() as u16); + b.bytes(p); + } + b.bytes(&self.ext); + b.buf + } +} + +/// One NAL array of an `hvcC` record. +#[derive(Clone, Debug, PartialEq, Eq, Default)] +pub struct HevcNalArray { + pub completeness: bool, + pub nal_type: u8, + pub nalus: Vec>, +} + +/// `hvcC` — HEVCDecoderConfigurationRecord (ISO/IEC 14496-15 §8.3.3). +#[derive(Clone, Debug, PartialEq, Eq, Default)] +pub struct HevcConfig { + pub general_profile_space: u8, + pub general_tier_flag: bool, + pub general_profile_idc: u8, + pub general_profile_compatibility_flags: u32, + /// 48 bits of general constraint indicator flags. + pub general_constraint_indicator_flags: u64, + pub general_level_idc: u8, + pub min_spatial_segmentation_idc: u16, + pub parallelism_type: u8, + pub chroma_format_idc: u8, + pub bit_depth_luma: u8, + pub bit_depth_chroma: u8, + pub avg_frame_rate: u16, + pub constant_frame_rate: u8, + pub num_temporal_layers: u8, + pub temporal_id_nested: bool, + pub length_size: u8, + pub arrays: Vec, +} + +impl HevcConfig { + pub fn parse(data: &[u8]) -> Result { + let mut c = Cur::new(data, "hvcC"); + let _version = c.u8()?; + let b = c.u8()?; + let compat = c.u32()?; + let hi = c.u16()? as u64; + let lo = c.u32()? as u64; + let level = c.u8()?; + let min_seg = c.u16()? & 0x0FFF; + let par = c.u8()? & 3; + let chroma = c.u8()? & 3; + let bdl = (c.u8()? & 7) + 8; + let bdc = (c.u8()? & 7) + 8; + let afr = c.u16()?; + let x = c.u8()?; + let narrays = c.u8()? as usize; + let mut arrays = Vec::new(); + for _ in 0..narrays { + let t = c.u8()?; + let n = c.u16()? as usize; + c.check_count(n, 2)?; + let mut nalus = Vec::with_capacity(n); + for _ in 0..n { + let len = c.u16()? as usize; + nalus.push(c.bytes(len)?.to_vec()); + } + arrays.push(HevcNalArray { completeness: t & 0x80 != 0, nal_type: t & 0x3F, nalus }); + } + Ok(HevcConfig { + general_profile_space: b >> 6, + general_tier_flag: b & 0x20 != 0, + general_profile_idc: b & 0x1F, + general_profile_compatibility_flags: compat, + general_constraint_indicator_flags: (hi << 32) | lo, + general_level_idc: level, + min_spatial_segmentation_idc: min_seg, + parallelism_type: par, + chroma_format_idc: chroma, + bit_depth_luma: bdl, + bit_depth_chroma: bdc, + avg_frame_rate: afr, + constant_frame_rate: x >> 6, + num_temporal_layers: (x >> 3) & 7, + temporal_id_nested: x & 4 != 0, + length_size: (x & 3) + 1, + arrays, + }) + } + + pub fn to_bytes(&self) -> Vec { + let mut b = BoxBuf::new(); + b.u8(1); + b.u8((self.general_profile_space << 6) | ((self.general_tier_flag as u8) << 5) | (self.general_profile_idc & 0x1F)); + b.u32(self.general_profile_compatibility_flags); + b.u16((self.general_constraint_indicator_flags >> 32) as u16); + b.u32(self.general_constraint_indicator_flags as u32); + b.u8(self.general_level_idc); + b.u16(0xF000 | (self.min_spatial_segmentation_idc & 0x0FFF)); + b.u8(0xFC | (self.parallelism_type & 3)); + b.u8(0xFC | (self.chroma_format_idc & 3)); + b.u8(0xF8 | (self.bit_depth_luma.saturating_sub(8) & 7)); + b.u8(0xF8 | (self.bit_depth_chroma.saturating_sub(8) & 7)); + b.u16(self.avg_frame_rate); + b.u8((self.constant_frame_rate << 6) + | ((self.num_temporal_layers & 7) << 3) + | ((self.temporal_id_nested as u8) << 2) + | (self.length_size.clamp(1, 4) - 1)); + b.u8(self.arrays.len() as u8); + for a in &self.arrays { + b.u8(((a.completeness as u8) << 7) | (a.nal_type & 0x3F)); + b.u16(a.nalus.len() as u16); + for n in &a.nalus { + b.u16(n.len() as u16); + b.bytes(n); + } + } + b.buf + } + + fn nalus_of(&self, t: u8) -> Vec<&[u8]> { + self.arrays.iter().filter(|a| a.nal_type == t).flat_map(|a| a.nalus.iter().map(|n| n.as_slice())).collect() + } + pub fn vps(&self) -> Vec<&[u8]> { + self.nalus_of(32) + } + pub fn sps(&self) -> Vec<&[u8]> { + self.nalus_of(33) + } + pub fn pps(&self) -> Vec<&[u8]> { + self.nalus_of(34) + } +} + +/// `av1C` — AV1CodecConfigurationRecord. +#[derive(Clone, Debug, PartialEq, Eq, Default)] +pub struct Av1Config { + pub seq_profile: u8, + pub seq_level_idx_0: u8, + pub seq_tier_0: bool, + pub high_bitdepth: bool, + pub twelve_bit: bool, + pub monochrome: bool, + pub chroma_subsampling_x: bool, + pub chroma_subsampling_y: bool, + pub chroma_sample_position: u8, + pub initial_presentation_delay_minus_one: Option, + pub config_obus: Vec, +} + +impl Av1Config { + pub fn parse(data: &[u8]) -> Result { + let mut c = Cur::new(data, "av1C"); + let m = c.u8()?; + if m & 0x80 == 0 { + return Err(Error::Invalid("av1C marker bit not set".into())); + } + let a = c.u8()?; + let b = c.u8()?; + let d = c.u8()?; + Ok(Av1Config { + seq_profile: a >> 5, + seq_level_idx_0: a & 0x1F, + seq_tier_0: b & 0x80 != 0, + high_bitdepth: b & 0x40 != 0, + twelve_bit: b & 0x20 != 0, + monochrome: b & 0x10 != 0, + chroma_subsampling_x: b & 0x08 != 0, + chroma_subsampling_y: b & 0x04 != 0, + chroma_sample_position: b & 3, + initial_presentation_delay_minus_one: if d & 0x10 != 0 { Some(d & 0x0F) } else { None }, + config_obus: c.rest().to_vec(), + }) + } + pub fn to_bytes(&self) -> Vec { + let mut b = BoxBuf::new(); + b.u8(0x81); + b.u8((self.seq_profile << 5) | (self.seq_level_idx_0 & 0x1F)); + b.u8(((self.seq_tier_0 as u8) << 7) + | ((self.high_bitdepth as u8) << 6) + | ((self.twelve_bit as u8) << 5) + | ((self.monochrome as u8) << 4) + | ((self.chroma_subsampling_x as u8) << 3) + | ((self.chroma_subsampling_y as u8) << 2) + | (self.chroma_sample_position & 3)); + b.u8(match self.initial_presentation_delay_minus_one { + Some(d) => 0x10 | (d & 0x0F), + None => 0, + }); + b.bytes(&self.config_obus); + b.buf + } +} + +/// `vpcC` — VP codec configuration (version 1). +#[derive(Clone, Debug, PartialEq, Eq, Default)] +pub struct VpcConfig { + pub profile: u8, + pub level: u8, + pub bit_depth: u8, + pub chroma_subsampling: u8, + pub full_range: bool, + pub colour_primaries: u8, + pub transfer_characteristics: u8, + pub matrix_coefficients: u8, + pub codec_init: Vec, +} + +impl VpcConfig { + pub fn parse(data: &[u8]) -> Result { + let mut c = Cur::new(data, "vpcC"); + let (version, _) = c.full_header()?; + if version == 0 { + // Version 0 draft layout: profile, level, bitDepth(4)|colorSpace(4), chroma(4)|transfer(3)|range(1) + let profile = c.u8()?; + let level = c.u8()?; + let a = c.u8()?; + let b = c.u8()?; + return Ok(VpcConfig { profile, level, bit_depth: a >> 4, chroma_subsampling: b >> 4, full_range: b & 1 != 0, ..Default::default() }); + } + let profile = c.u8()?; + let level = c.u8()?; + let a = c.u8()?; + let colour_primaries = c.u8()?; + let transfer_characteristics = c.u8()?; + let matrix_coefficients = c.u8()?; + let n = c.u16()? as usize; + let codec_init = c.bytes(n)?.to_vec(); + Ok(VpcConfig { + profile, + level, + bit_depth: a >> 4, + chroma_subsampling: (a >> 1) & 7, + full_range: a & 1 != 0, + colour_primaries, + transfer_characteristics, + matrix_coefficients, + codec_init, + }) + } + pub fn to_bytes(&self) -> Vec { + let mut b = BoxBuf::new(); + b.u32(1 << 24); + b.u8(self.profile); + b.u8(self.level); + b.u8((self.bit_depth << 4) | ((self.chroma_subsampling & 7) << 1) | self.full_range as u8); + b.u8(self.colour_primaries); + b.u8(self.transfer_characteristics); + b.u8(self.matrix_coefficients); + b.u16(self.codec_init.len() as u16); + b.bytes(&self.codec_init); + b.buf + } +} + +/// AAC (MPEG-4 audio) configuration from `esds`. +#[derive(Clone, Debug, PartialEq, Eq, Default)] +pub struct AacConfig { + /// AudioSpecificConfig bytes (DecoderSpecificInfo). + pub asc: Vec, + /// Audio object type (2 = AAC-LC, 5 = SBR/HE-AAC, 29 = PS …) as signalled first in the ASC. + pub object_type: u8, + /// Sampling rate signalled by the ASC (core rate; 0 if the ASC is missing). + pub sample_rate: u32, + /// Channel configuration from the ASC (0 = defined elsewhere). + pub channel_config: u8, + /// DecoderConfigDescriptor fields. + pub max_bitrate: u32, + pub avg_bitrate: u32, + pub buffer_size: u32, +} + +const AAC_RATES: [u32; 13] = [96000, 88200, 64000, 48000, 44100, 32000, 24000, 22050, 16000, 12000, 11025, 8000, 7350]; + +impl AacConfig { + /// Build from an AudioSpecificConfig, decoding its header fields. + pub fn from_asc(asc: Vec) -> Self { + let (object_type, sample_rate, channel_config) = parse_asc(&asc).unwrap_or((0, 0, 0)); + AacConfig { asc, object_type, sample_rate, channel_config, max_bitrate: 0, avg_bitrate: 0, buffer_size: 0 } + } + /// Channel count implied by the channel configuration (0 if unspecified). + pub fn channels(&self) -> u32 { + match self.channel_config { + 1..=6 => self.channel_config as u32, + 7 => 8, + _ => 0, + } + } +} + +/// Decode (audio object type, sampling frequency, channel configuration) from an AudioSpecificConfig. +pub fn parse_asc(asc: &[u8]) -> Option<(u8, u32, u8)> { + let mut r = BitReader::new(asc); + let aot = |r: &mut BitReader| -> Option { + let t = r.read_bits(5).ok()? as u8; + if t == 31 { Some(32 + r.read_bits(6).ok()? as u8) } else { Some(t) } + }; + let rate = |r: &mut BitReader| -> Option { + let i = r.read_bits(4).ok()? as usize; + if i == 15 { r.read_bits(24).ok() } else { AAC_RATES.get(i).copied() } + }; + let t = aot(&mut r)?; + let sr = rate(&mut r)?; + let ch = r.read_bits(4).ok()? as u8; + Some((t, sr, ch)) +} + +/// Uncompressed PCM description. +#[derive(Clone, Copy, Debug, PartialEq, Default)] +pub struct PcmConfig { + pub bits: u16, + pub float: bool, + pub big_endian: bool, + /// Signed integer samples (8-bit `raw ` is unsigned). + pub signed: bool, + pub channels: u32, + pub sample_rate: f64, +} + +impl PcmConfig { + /// Bytes per interleaved audio frame (all channels). + pub fn bytes_per_frame(&self) -> u32 { + (self.bits as u32).div_ceil(8) * self.channels.max(1) + } +} + +/// `dOps` — Opus specific box. +#[derive(Clone, Debug, PartialEq, Eq, Default)] +pub struct OpusConfig { + pub output_channels: u8, + pub pre_skip: u16, + pub input_sample_rate: u32, + pub output_gain: i16, + pub channel_mapping_family: u8, + /// StreamCount, CoupledCount and ChannelMapping bytes when family != 0. + pub channel_mapping: Vec, +} + +impl OpusConfig { + pub fn parse(data: &[u8]) -> Result { + let mut c = Cur::new(data, "dOps"); + let _version = c.u8()?; + Ok(OpusConfig { + output_channels: c.u8()?, + pre_skip: c.u16()?, + input_sample_rate: c.u32()?, + output_gain: c.i16()?, + channel_mapping_family: c.u8()?, + channel_mapping: c.rest().to_vec(), + }) + } + pub fn to_bytes(&self) -> Vec { + let mut b = BoxBuf::new(); + b.u8(0); + b.u8(self.output_channels); + b.u16(self.pre_skip); + b.u32(self.input_sample_rate); + b.i16(self.output_gain); + b.u8(self.channel_mapping_family); + b.bytes(&self.channel_mapping); + b.buf + } +} + +/// `dfLa` — FLAC metadata blocks, with STREAMINFO decoded. +#[derive(Clone, Debug, PartialEq, Eq, Default)] +pub struct FlacConfig { + /// Raw metadata blocks (each with its 4-byte header) as stored in `dfLa`. + pub metadata_blocks: Vec, + pub sample_rate: u32, + pub channels: u8, + pub bits_per_sample: u8, + pub total_samples: u64, +} + +impl FlacConfig { + pub fn parse(data: &[u8]) -> Result { + let mut c = Cur::new(data, "dfLa"); + c.full_header()?; + let blocks = c.rest().to_vec(); + let mut cfg = FlacConfig { metadata_blocks: blocks.clone(), ..Default::default() }; + let mut b = Cur::new(&blocks, "dfLa"); + let hdr = b.u32()?; + if (hdr >> 24) & 0x7F == 0 { + let si = b.bytes(((hdr & 0xFF_FFFF) as usize).min(34))?; + if si.len() >= 18 { + let mut r = BitReader::new(&si[10..18]); + cfg.sample_rate = r.read_bits(20).unwrap_or(0); + cfg.channels = r.read_bits(3).unwrap_or(0) as u8 + 1; + cfg.bits_per_sample = r.read_bits(5).unwrap_or(0) as u8 + 1; + cfg.total_samples = r.read_bits_u64(36).unwrap_or(0); + } + } + Ok(cfg) + } +} + +/// QuickTime timecode sample description (`tmcd`) plus the start frame read from its first sample. +#[derive(Clone, Debug, PartialEq, Eq, Default)] +pub struct TimecodeConfig { + /// Bit 0 drop frame, bit 1 24-hour max, bit 2 negative times OK, bit 3 counter. + pub flags: u32, + pub timescale: u32, + pub frame_duration: u32, + /// Nominal frames per second (`numberOfFrames`), e.g. 30 for 29.97. + pub frames_per_second: u8, + /// Frame number of the first timecode sample (filled in by the demuxer). + pub start_frame: Option, + /// Source reel name (`name` box) if present. + pub name: Option, +} + +impl TimecodeConfig { + pub fn drop_frame(&self) -> bool { + self.flags & 1 != 0 + } + /// Format a frame number as `HH:MM:SS:FF` (`;` before the frames when drop-frame). + pub fn format_frame(&self, frame: u32) -> String { + let fps = self.frames_per_second.max(1) as u64; + let mut f = frame as u64; + if self.drop_frame() && fps >= 30 { + let drop = (fps / 30) * 2; // 2 frames per minute at 30, 4 at 60 + let per_min = fps * 60 - drop; + let per_10 = per_min * 10 + drop; + let d = f / per_10; + let m = f % per_10; + f += drop * 9 * d + if m > drop { drop * ((m - drop) / per_min) } else { 0 }; + } + let ff = f % fps; + let s = (f / fps) % 60; + let mi = (f / fps / 60) % 60; + let h = (f / fps / 3600) % 24; + let sep = if self.drop_frame() { ';' } else { ':' }; + format!("{h:02}:{mi:02}:{s:02}{sep}{ff:02}") + } +} + +// --------------------------------------------------------------------------------------------- +// Parsing + +fn is_video_fourcc(f: &[u8; 4]) -> bool { + matches!( + f, + b"avc1" + | b"avc3" + | b"hvc1" + | b"hev1" + | b"av01" + | b"vp09" + | b"vp08" + | b"apch" + | b"apcn" + | b"apcs" + | b"apco" + | b"ap4h" + | b"ap4x" + | b"jpeg" + | b"mjpa" + | b"mjpb" + | b"AVdn" + | b"AVdh" + | b"mp4v" + | b"encv" + | b"raw " + | b"2vuy" + | b"v210" + ) +} + +fn is_audio_fourcc(f: &[u8; 4]) -> bool { + matches!( + f, + b"mp4a" + | b"lpcm" + | b"sowt" + | b"twos" + | b"in24" + | b"in32" + | b"fl32" + | b"fl64" + | b"ipcm" + | b"fpcm" + | b"alac" + | b"Opus" + | b"fLaC" + | b"ac-3" + | b"ec-3" + | b".mp3" + | b"enca" + | b"ulaw" + | b"alaw" + | b"NONE" + ) +} + +/// Parse one `stsd` entry. `handler` is the track handler type; `is_qt` selects QuickTime +/// interpretation of the sound description version field. +pub(crate) fn parse_sample_entry(format: FourCc, payload: &[u8], handler: FourCc, is_qt: bool) -> Result { + let mut c = Cur::new(payload, "sample entry"); + c.skip(6)?; + let data_reference_index = c.u16()?; + let f = &format.0; + let unknown = || CodecConfig::Unknown { fourcc: format, raw: payload.to_vec() }; + if f == b"tmcd" || (handler == b"tmcd" && !is_video_fourcc(f)) { + let mut tc = TimecodeConfig::default(); + c.skip(4)?; + tc.flags = c.u32()?; + tc.timescale = c.u32()?; + tc.frame_duration = c.u32()?; + tc.frames_per_second = c.u8()?; + c.skip(1)?; + for b in boxes(c.rest()).map_while(|b| b.ok()) { + if b.kind == b"name" && b.payload.len() >= 4 { + let n = u16::from_be_bytes([b.payload[0], b.payload[1]]) as usize; + let s = b.payload.get(4..4 + n).unwrap_or(&b.payload[4..]); + tc.name = Some(String::from_utf8_lossy(s).into_owned()); + } + } + return Ok(SampleEntry { format, data_reference_index, codec: CodecConfig::Timecode(tc), video: None, audio: None, bitrate: None }); + } + let video_like = is_video_fourcc(f) || (handler == b"vide" && !is_audio_fourcc(f)); + let audio_like = is_audio_fourcc(f) || (handler == b"soun" && !video_like); + if video_like { + let mut v = VideoParams::default(); + let _version = c.u16()?; + let _revision = c.u16()?; + v.vendor = c.fourcc()?; + v.temporal_quality = c.u32()?; + v.spatial_quality = c.u32()?; + v.width = c.u16()?; + v.height = c.u16()?; + v.horiz_resolution = c.u32()?; + v.vert_resolution = c.u32()?; + c.skip(4)?; + v.frame_count = c.u16()?; + let name = c.bytes(32)?; + let n = (name[0] as usize).min(31); + v.compressor_name = String::from_utf8_lossy(&name[1..1 + n]).into_owned(); + v.depth = c.u16()?; + let _ctab = c.i16()?; + let mut codec = None; + let mut bitrate = None; + for b in boxes(c.rest()).map_while(|b| b.ok()) { + let p = b.payload; + match &b.kind.0 { + b"avcC" if matches!(f, b"avc1" | b"avc3") => codec = Some(CodecConfig::Avc(AvcConfig::parse(p)?)), + b"hvcC" if matches!(f, b"hvc1" | b"hev1") => codec = Some(CodecConfig::Hevc(HevcConfig::parse(p)?)), + b"av1C" if f == b"av01" => codec = Some(CodecConfig::Av1(Av1Config::parse(p)?)), + b"vpcC" if f == b"vp09" => codec = Some(CodecConfig::Vp9(VpcConfig::parse(p)?)), + b"colr" => v.color = parse_colr(p).ok(), + b"mdcv" => v.mastering_display = MasteringDisplay::parse(p), + b"clli" if p.len() >= 4 => v.content_light = Some((u16::from_be_bytes([p[0], p[1]]), u16::from_be_bytes([p[2], p[3]]))), + b"pasp" => { + let mut c = Cur::new(p, "pasp"); + v.pixel_aspect = Some((c.u32()?, c.u32()?)); + } + b"clap" => { + let mut c = Cur::new(p, "clap"); + v.clean_aperture = Some(CleanAperture { + width: (c.u32()?, c.u32()?), + height: (c.u32()?, c.u32()?), + horiz_offset: (c.i32()?, c.u32()?), + vert_offset: (c.i32()?, c.u32()?), + }); + } + b"fiel" => { + let mut c = Cur::new(p, "fiel"); + v.field_info = Some(FieldInfo { fields: c.u8()?, detail: c.u8()? }); + } + b"gama" => v.gamma = Some(Cur::new(p, "gama").u32()?), + b"btrt" => bitrate = Some(parse_btrt(p)?), + _ => {} + } + } + let codec = match codec { + Some(c) => c, + None => match f { + b"apch" | b"apcn" | b"apcs" | b"apco" | b"ap4h" | b"ap4x" => CodecConfig::ProRes { fourcc: format }, + b"jpeg" | b"mjpa" | b"mjpb" => CodecConfig::Jpeg { fourcc: format }, + b"AVdn" | b"AVdh" => CodecConfig::Dnx { fourcc: format }, + _ => unknown(), + }, + }; + return Ok(SampleEntry { format, data_reference_index, codec, video: Some(v), audio: None, bitrate }); + } + if audio_like { + let mut a = AudioParams { qt_version: c.u16()?, ..Default::default() }; + let _revision = c.u16()?; + let _vendor = c.u32()?; + a.channels = c.u16()? as u32; + a.sample_size = c.u16()?; + a.compression_id = c.i16()?; + let _packet_size = c.u16()?; + a.sample_rate = c.u32()? as f64 / 65536.0; + if is_qt && a.qt_version == 1 { + a.samples_per_packet = c.u32()?; + a.bytes_per_packet = c.u32()?; + a.bytes_per_frame = c.u32()?; + a.bytes_per_sample = c.u32()?; + } else if is_qt && a.qt_version == 2 { + let _size_of_struct = c.u32()?; + a.sample_rate = c.f64()?; + a.channels = c.u32()?; + let _always = c.u32()?; + a.const_bits_per_channel = c.u32()?; + a.lpcm_flags = c.u32()?; + a.const_bytes_per_packet = c.u32()?; + a.const_frames_per_packet = c.u32()?; + } + // Flatten QuickTime `wave` children into the list of extension boxes. + let mut ext: Vec<(FourCc, &[u8])> = Vec::new(); + for b in boxes(c.rest()).map_while(|b| b.ok()) { + if b.kind == b"wave" { + for w in boxes(b.payload).map_while(|b| b.ok()) { + ext.push((w.kind, w.payload)); + } + } else { + ext.push((b.kind, b.payload)); + } + } + let get = |k: &[u8; 4]| ext.iter().find(|(kk, _)| kk == k).map(|(_, p)| *p); + if let Some(p) = get(b"enda") { + let v = if p.len() >= 2 { u16::from_be_bytes([p[0], p[1]]) } else { p.first().copied().unwrap_or(0) as u16 }; + a.little_endian = Some(v != 0); + } + let bitrate = get(b"btrt").and_then(|p| parse_btrt(p).ok()); + let codec = match f { + b"mp4a" => match get(b"esds") { + Some(p) => parse_esds(p)?, + None => unknown(), + }, + b".mp3" => CodecConfig::Mp3, + b"alac" => match get(b"alac") { + Some(p) if p.len() >= 4 => CodecConfig::Alac { cookie: p[4..].to_vec() }, + _ => unknown(), + }, + b"Opus" => match get(b"dOps") { + Some(p) => CodecConfig::Opus(OpusConfig::parse(p)?), + None => unknown(), + }, + b"fLaC" => match get(b"dfLa") { + Some(p) => CodecConfig::Flac(FlacConfig::parse(p)?), + None => unknown(), + }, + b"ac-3" => CodecConfig::Ac3 { dac3: get(b"dac3").unwrap_or(&[]).to_vec() }, + b"ec-3" => CodecConfig::Eac3 { dec3: get(b"dec3").unwrap_or(&[]).to_vec() }, + b"lpcm" | b"sowt" | b"twos" | b"in24" | b"in32" | b"fl32" | b"fl64" | b"raw " | b"NONE" => CodecConfig::Pcm(qt_pcm(f, &a)), + b"ipcm" | b"fpcm" => { + let mut pcm = PcmConfig { + bits: a.sample_size, + float: f == b"fpcm", + big_endian: true, + signed: true, + channels: a.channels, + sample_rate: a.sample_rate, + }; + if let Some(p) = get(b"pcmC") { + let mut c = Cur::new(p, "pcmC"); + c.full_header()?; + let flags = c.u8()?; + pcm.big_endian = flags & 1 == 0; + pcm.bits = c.u8()? as u16; + } + CodecConfig::Pcm(pcm) + } + _ => unknown(), + }; + return Ok(SampleEntry { format, data_reference_index, codec, video: None, audio: Some(a), bitrate }); + } + Ok(SampleEntry { format, data_reference_index, codec: unknown(), video: None, audio: None, bitrate: None }) +} + +fn qt_pcm(f: &[u8; 4], a: &AudioParams) -> PcmConfig { + let mut p = PcmConfig { channels: a.channels, sample_rate: a.sample_rate, signed: true, ..Default::default() }; + match f { + b"lpcm" => { + p.bits = a.const_bits_per_channel as u16; + p.float = a.lpcm_flags & 1 != 0; + p.big_endian = a.lpcm_flags & 2 != 0; + p.signed = a.lpcm_flags & 4 != 0 || p.float; + if p.bits == 0 { + p.bits = a.sample_size; + } + } + b"sowt" => { + p.bits = if a.sample_size == 0 { 16 } else { a.sample_size }; + } + b"twos" => { + p.bits = if a.sample_size == 0 { 16 } else { a.sample_size }; + p.big_endian = true; + } + b"raw " | b"NONE" => { + p.bits = if a.sample_size == 0 { 8 } else { a.sample_size }; + p.signed = p.bits != 8; + p.big_endian = true; + } + b"in24" | b"in32" | b"fl32" | b"fl64" => { + p.bits = match f { + b"in24" => 24, + b"in32" | b"fl32" => 32, + _ => 64, + }; + p.float = f[0] == b'f'; + p.big_endian = !a.little_endian.unwrap_or(false); + } + _ => {} + } + p +} + +fn parse_btrt(p: &[u8]) -> Result { + let mut c = Cur::new(p, "btrt"); + Ok(BitRate { buffer_size: c.u32()?, max_bitrate: c.u32()?, avg_bitrate: c.u32()? }) +} + +fn parse_colr(p: &[u8]) -> Result { + let mut c = Cur::new(p, "colr"); + let kind = c.fourcc()?; + Ok(match &kind.0 { + b"nclx" => { + ColorInfo::Nclx { primaries: c.u16()?, transfer: c.u16()?, matrix: c.u16()?, full_range: c.u8().map(|v| v & 0x80 != 0).unwrap_or(false) } + } + b"nclc" => ColorInfo::Nclc { primaries: c.u16()?, transfer: c.u16()?, matrix: c.u16()? }, + _ => ColorInfo::Icc { kind, profile: c.rest().to_vec() }, + }) +} + +fn descriptor(c: &mut Cur) -> Result<(u8, usize)> { + let tag = c.u8()?; + let mut len = 0usize; + for _ in 0..4 { + let b = c.u8()?; + len = (len << 7) | (b & 0x7F) as usize; + if b & 0x80 == 0 { + break; + } + } + Ok((tag, len)) +} + +fn parse_esds(p: &[u8]) -> Result { + let mut c = Cur::new(p, "esds"); + c.full_header()?; + let (tag, len) = descriptor(&mut c)?; + if tag != 3 { + return Err(Error::Invalid(format!("esds: expected ES_Descriptor, got tag {tag}"))); + } + let es = c.bytes(len.min(c.remaining()))?; + let mut c = Cur::new(es, "ES_Descriptor"); + c.u16()?; + let fl = c.u8()?; + if fl & 0x80 != 0 { + c.u16()?; + } + if fl & 0x40 != 0 { + let n = c.u8()? as usize; + c.skip(n)?; + } + if fl & 0x20 != 0 { + c.u16()?; + } + while c.remaining() > 0 { + let (tag, len) = descriptor(&mut c)?; + let body = c.bytes(len.min(c.remaining()))?; + if tag != 4 { + continue; + } + let mut d = Cur::new(body, "DecoderConfigDescriptor"); + let oti = d.u8()?; + let _stream_type = d.u8()?; + let buffer_size = d.u24()?; + let max_bitrate = d.u32()?; + let avg_bitrate = d.u32()?; + let mut asc = Vec::new(); + while d.remaining() > 0 { + let (t, l) = descriptor(&mut d)?; + let b = d.bytes(l.min(d.remaining()))?; + if t == 5 { + asc = b.to_vec(); + break; + } + } + return Ok(match oti { + 0x69 | 0x6B => CodecConfig::Mp3, + _ => { + let mut a = AacConfig::from_asc(asc); + a.max_bitrate = max_bitrate; + a.avg_bitrate = avg_bitrate; + a.buffer_size = buffer_size; + CodecConfig::Aac(a) + } + }); + } + Err(Error::Invalid("esds without DecoderConfigDescriptor".into())) +} + +// --------------------------------------------------------------------------------------------- +// Serialization (muxer) + +fn put_descriptor(b: &mut BoxBuf, tag: u8, body: &[u8]) { + b.u8(tag); + let n = body.len() as u32; + // Always use the 4-byte length form for simplicity (as many encoders do). + b.u8(0x80 | ((n >> 21) & 0x7F) as u8); + b.u8(0x80 | ((n >> 14) & 0x7F) as u8); + b.u8(0x80 | ((n >> 7) & 0x7F) as u8); + b.u8((n & 0x7F) as u8); + b.bytes(body); +} + +pub(crate) fn esds_payload(oti: u8, asc: &[u8], max_bitrate: u32, avg_bitrate: u32, buffer_size: u32) -> Vec { + let mut dcd = BoxBuf::new(); + dcd.u8(oti); + dcd.u8(0x15); // streamType audio (5) << 2 | upstream 0 | reserved 1 + dcd.u8((buffer_size >> 16) as u8); + dcd.u16(buffer_size as u16); + dcd.u32(max_bitrate); + dcd.u32(avg_bitrate); + if !asc.is_empty() { + put_descriptor(&mut dcd, 5, asc); + } + let mut es = BoxBuf::new(); + es.u16(1); // ES_ID + es.u8(0); + put_descriptor(&mut es, 4, &dcd.buf); + put_descriptor(&mut es, 6, &[2]); // SLConfigDescriptor, predefined = 2 (MP4) + let mut b = BoxBuf::new(); + b.u32(0); + put_descriptor(&mut b, 3, &es.buf); + b.buf +} + +impl SampleEntry { + /// Convenience constructor for a video entry. + pub fn video(format: FourCc, codec: CodecConfig, width: u16, height: u16) -> Self { + let compressor_name = match &codec { + CodecConfig::ProRes { fourcc } => match &fourcc.0 { + b"apch" => "Apple ProRes 422 HQ", + b"apcn" => "Apple ProRes 422", + b"apcs" => "Apple ProRes 422 LT", + b"apco" => "Apple ProRes 422 Proxy", + b"ap4h" => "Apple ProRes 4444", + _ => "Apple ProRes 4444 XQ", + }, + CodecConfig::Jpeg { .. } => "Photo - JPEG", + _ => "", + }; + SampleEntry { + format, + data_reference_index: 1, + codec, + video: Some(VideoParams { + width, + height, + horiz_resolution: 0x0048_0000, + vert_resolution: 0x0048_0000, + frame_count: 1, + compressor_name: compressor_name.into(), + depth: 24, + ..Default::default() + }), + audio: None, + bitrate: None, + } + } + + /// Convenience constructor for an audio entry (ISO v0 layout; the muxer upgrades PCM in MOV to v2). + pub fn audio(format: FourCc, codec: CodecConfig, channels: u32, sample_rate: f64, sample_size: u16) -> Self { + SampleEntry { + format, + data_reference_index: 1, + codec, + video: None, + audio: Some(AudioParams { channels, sample_rate, sample_size, ..Default::default() }), + bitrate: None, + } + } + + /// H.264 entry (`avc1`). + pub fn avc(cfg: AvcConfig, width: u16, height: u16) -> Self { + Self::video(FourCc(*b"avc1"), CodecConfig::Avc(cfg), width, height) + } + /// HEVC entry (`hvc1`, parameter sets in the config only). + pub fn hevc(cfg: HevcConfig, width: u16, height: u16) -> Self { + Self::video(FourCc(*b"hvc1"), CodecConfig::Hevc(cfg), width, height) + } + /// ProRes entry; `fourcc` one of `apch`/`apcn`/`apcs`/`apco`/`ap4h`/`ap4x`. + pub fn prores(fourcc: FourCc, width: u16, height: u16) -> Self { + let mut e = Self::video(fourcc, CodecConfig::ProRes { fourcc }, width, height); + if let Some(v) = e.video.as_mut() + && matches!(&fourcc.0, b"ap4h" | b"ap4x") + { + v.depth = 32; + } + e + } + /// Avid DNxHD / DNxHR entry (`AVdn`, or `AVdh` for DNxHR). + pub fn dnx(fourcc: FourCc, width: u16, height: u16) -> Self { + let mut e = Self::video(fourcc, CodecConfig::Dnx { fourcc }, width, height); + if let Some(v) = e.video.as_mut() { + v.depth = 24; + } + e + } + /// Motion-JPEG entry (`jpeg`). + pub fn jpeg(width: u16, height: u16) -> Self { + let f = FourCc(*b"jpeg"); + Self::video(f, CodecConfig::Jpeg { fourcc: f }, width, height) + } + /// AAC entry (`mp4a` + `esds`) from an AudioSpecificConfig. + pub fn aac(asc: Vec, channels: u32, sample_rate: u32) -> Self { + Self::audio(FourCc(*b"mp4a"), CodecConfig::Aac(AacConfig::from_asc(asc)), channels, sample_rate as f64, 16) + } + /// PCM entry. The muxer picks the on-disk format (`sowt`/`twos`/`lpcm` for MOV, `ipcm`/`fpcm` for MP4). + pub fn pcm(cfg: PcmConfig) -> Self { + Self::audio(FourCc(*b"lpcm"), CodecConfig::Pcm(cfg), cfg.channels, cfg.sample_rate, cfg.bits) + } + + /// Serialize as a complete sample entry box. For PCM the format is chosen from the brand. + pub(crate) fn write(&self, b: &mut BoxBuf, qt: bool) -> Result<()> { + if let CodecConfig::Unknown { fourcc, raw } = &self.codec { + let m = b.start(&fourcc.0); + b.bytes(raw); + b.end(m); + return Ok(()); + } + if let CodecConfig::Timecode(tc) = &self.codec { + let m = b.start(b"tmcd"); + b.zeros(6); + b.u16(self.data_reference_index); + b.u32(0); + b.u32(tc.flags); + b.u32(tc.timescale); + b.u32(tc.frame_duration); + b.u8(tc.frames_per_second); + b.u8(0); + if let Some(name) = &tc.name { + let n = b.start(b"name"); + b.u16(name.len() as u16); + b.u16(0); + b.bytes(name.as_bytes()); + b.end(n); + } + b.end(m); + return Ok(()); + } + if let Some(v) = &self.video { + let m = b.start(&self.format.0); + b.zeros(6); + b.u16(self.data_reference_index); + b.u16(0); + b.u16(0); + b.u32(v.vendor.as_u32()); + b.u32(v.temporal_quality); + b.u32(v.spatial_quality); + b.u16(v.width); + b.u16(v.height); + b.u32(v.horiz_resolution); + b.u32(v.vert_resolution); + b.u32(0); + b.u16(v.frame_count); + let name = v.compressor_name.as_bytes(); + let n = name.len().min(31); + let mut nb = [0u8; 32]; + nb[0] = n as u8; + nb[1..1 + n].copy_from_slice(&name[..n]); + b.bytes(&nb); + b.u16(v.depth); + b.i16(-1); + match &self.codec { + CodecConfig::Avc(c) => b.leaf(b"avcC", &c.to_bytes()), + CodecConfig::Hevc(c) => b.leaf(b"hvcC", &c.to_bytes()), + CodecConfig::Av1(c) => b.leaf(b"av1C", &c.to_bytes()), + CodecConfig::Vp9(c) => b.leaf(b"vpcC", &c.to_bytes()), + _ => {} + } + if let Some(g) = v.gamma { + b.leaf(b"gama", &g.to_be_bytes()); + } + if let Some(fi) = v.field_info { + b.leaf(b"fiel", &[fi.fields, fi.detail]); + } + if let Some(ci) = &v.color { + let m = b.start(b"colr"); + match ci { + ColorInfo::Nclx { primaries, transfer, matrix, full_range } => { + b.bytes(b"nclx"); + b.u16(*primaries); + b.u16(*transfer); + b.u16(*matrix); + b.u8(if *full_range { 0x80 } else { 0 }); + } + ColorInfo::Nclc { primaries, transfer, matrix } => { + b.bytes(b"nclc"); + b.u16(*primaries); + b.u16(*transfer); + b.u16(*matrix); + } + ColorInfo::Icc { kind, profile } => { + b.bytes(&kind.0); + b.bytes(profile); + } + } + b.end(m); + } + if let Some(md) = &v.mastering_display { + b.leaf(b"mdcv", &md.to_bytes()); + } + if let Some((cll, fall)) = v.content_light { + let mut d = cll.to_be_bytes().to_vec(); + d.extend_from_slice(&fall.to_be_bytes()); + b.leaf(b"clli", &d); + } + if let Some((h, vv)) = v.pixel_aspect { + let m = b.start(b"pasp"); + b.u32(h); + b.u32(vv); + b.end(m); + } + if let Some(c) = v.clean_aperture { + let m = b.start(b"clap"); + b.u32(c.width.0); + b.u32(c.width.1); + b.u32(c.height.0); + b.u32(c.height.1); + b.i32(c.horiz_offset.0); + b.u32(c.horiz_offset.1); + b.i32(c.vert_offset.0); + b.u32(c.vert_offset.1); + b.end(m); + } + if let Some(br) = self.bitrate { + write_btrt(b, br); + } + b.end(m); + return Ok(()); + } + let a = self.audio.clone().ok_or_else(|| Error::Mux("sample entry has neither video nor audio params".into()))?; + // Choose the on-disk format and sound description version. + let (format, version, pcm) = match &self.codec { + CodecConfig::Pcm(p) => { + if qt { + if p.bits == 16 && !p.float { + (if p.big_endian { *b"twos" } else { *b"sowt" }, 0u16, Some(*p)) + } else if p.bits == 8 && !p.float { + (*b"raw ", 0, Some(*p)) + } else { + (*b"lpcm", 2, Some(*p)) + } + } else { + (if p.float { *b"fpcm" } else { *b"ipcm" }, 0, Some(*p)) + } + } + _ => (self.format.0, if qt { a.qt_version.min(2) } else { 0 }, None), + }; + let m = b.start(&format); + b.zeros(6); + b.u16(self.data_reference_index); + if version == 2 { + let p = pcm.unwrap_or_default(); + b.u16(2); + b.u16(0); + b.u32(0); + b.u16(3); + b.u16(16); + b.i16(-2); + b.u16(0); + b.u32(65536); + b.u32(72); + b.f64(p.sample_rate); + b.u32(p.channels); + b.u32(0x7F00_0000); + b.u32(p.bits as u32); + let flags = (p.float as u32) | ((p.big_endian as u32) << 1) | (((p.signed && !p.float) as u32) << 2) | 8; + b.u32(flags); + b.u32(p.bytes_per_frame()); + b.u32(1); + } else { + b.u16(version); + b.u16(0); + b.u32(0); + b.u16(a.channels.min(u16::MAX as u32) as u16); + b.u16(match pcm { + Some(p) => p.bits, + None => { + if a.sample_size == 0 { + 16 + } else { + a.sample_size + } + } + }); + b.i16(if version == 1 { a.compression_id } else { 0 }); + b.u16(0); + let sr = if a.sample_rate < 65536.0 { (a.sample_rate * 65536.0).round() as u32 } else { 0 }; + b.u32(sr); + if version == 1 { + b.u32(a.samples_per_packet); + b.u32(a.bytes_per_packet); + b.u32(a.bytes_per_frame); + b.u32(a.bytes_per_sample); + } + } + match &self.codec { + CodecConfig::Aac(c) => { + b.leaf(b"esds", &esds_payload(0x40, &c.asc, c.max_bitrate, c.avg_bitrate, c.buffer_size)); + } + CodecConfig::Mp3 => { + if format == *b"mp4a" { + b.leaf(b"esds", &esds_payload(0x6B, &[], 0, 0, 0)); + } + } + CodecConfig::Alac { cookie } => { + let m = b.start_full(b"alac", 0, 0); + b.bytes(cookie); + b.end(m); + } + CodecConfig::Opus(o) => b.leaf(b"dOps", &o.to_bytes()), + CodecConfig::Flac(fl) => { + let m = b.start_full(b"dfLa", 0, 0); + b.bytes(&fl.metadata_blocks); + b.end(m); + } + CodecConfig::Ac3 { dac3 } => b.leaf(b"dac3", dac3), + CodecConfig::Eac3 { dec3 } => b.leaf(b"dec3", dec3), + CodecConfig::Pcm(p) if !qt => { + let m = b.start_full(b"pcmC", 0, 0); + b.u8(if p.big_endian { 0 } else { 1 }); + b.u8(p.bits as u8); + b.end(m); + } + CodecConfig::Pcm(p) if p.channels == 6 => { + // QuickTime channel layout: kAudioChannelLayoutTag_MPEG_5_1_A (L R C LFE Ls Rs) + let m = b.start_full(b"chan", 0, 0); + b.u32((121 << 16) | 6); + b.u32(0); + b.u32(0); + b.end(m); + } + _ => {} + } + if let Some(br) = self.bitrate { + write_btrt(b, br); + } + b.end(m); + Ok(()) + } +} + +fn write_btrt(b: &mut BoxBuf, br: BitRate) { + let m = b.start(b"btrt"); + b.u32(br.buffer_size); + b.u32(br.max_bitrate); + b.u32(br.avg_bitrate); + b.end(m); +} + +#[cfg(test)] +mod tests { + use super::*; + + fn roundtrip(e: &SampleEntry, qt: bool, handler: &[u8; 4]) -> SampleEntry { + let mut b = BoxBuf::new(); + e.write(&mut b, qt).unwrap(); + let bx = crate::bytes::boxes(&b.buf).next().unwrap().unwrap(); + parse_sample_entry(bx.kind, bx.payload, FourCc(*handler), qt).unwrap() + } + + #[test] + fn avcc_roundtrip() { + let cfg = AvcConfig::new(vec![vec![0x67, 0x64, 0x00, 0x1F, 0xAC]], vec![vec![0x68, 0xEB]], 4); + assert_eq!(cfg.profile, 0x64); + assert_eq!(AvcConfig::parse(&cfg.to_bytes()).unwrap(), cfg); + let mut e = SampleEntry::avc(cfg, 1920, 1080); + e.video.as_mut().unwrap().color = Some(ColorInfo::Nclx { primaries: 1, transfer: 1, matrix: 1, full_range: false }); + e.video.as_mut().unwrap().pixel_aspect = Some((4, 3)); + e.video.as_mut().unwrap().field_info = Some(FieldInfo { fields: 2, detail: 9 }); + e.video.as_mut().unwrap().clean_aperture = + Some(CleanAperture { width: (1920, 1), height: (1080, 1), horiz_offset: (-2, 1), vert_offset: (0, 1) }); + e.video.as_mut().unwrap().mastering_display = Some(MasteringDisplay::bt2020(1000.0, 0.0001)); + e.video.as_mut().unwrap().content_light = Some((1000, 400)); + e.bitrate = Some(BitRate { buffer_size: 1, max_bitrate: 2, avg_bitrate: 3 }); + assert_eq!(roundtrip(&e, false, b"vide"), e); + assert_eq!(MasteringDisplay::bt2020(1000.0, 0.0001).max_nits(), 1000.0); + } + + #[test] + fn hvcc_roundtrip() { + let cfg = HevcConfig { + general_profile_idc: 1, + general_profile_compatibility_flags: 0x6000_0000, + general_constraint_indicator_flags: 0x9000_0000_0000, + general_level_idc: 93, + chroma_format_idc: 1, + bit_depth_luma: 8, + bit_depth_chroma: 8, + length_size: 4, + num_temporal_layers: 1, + temporal_id_nested: true, + arrays: vec![ + HevcNalArray { completeness: true, nal_type: 32, nalus: vec![vec![0x40, 1]] }, + HevcNalArray { completeness: true, nal_type: 33, nalus: vec![vec![0x42, 1, 2]] }, + HevcNalArray { completeness: true, nal_type: 34, nalus: vec![vec![0x44, 1]] }, + ], + ..Default::default() + }; + let back = HevcConfig::parse(&cfg.to_bytes()).unwrap(); + assert_eq!(back, cfg); + assert_eq!(back.sps(), vec![&[0x42u8, 1, 2][..]]); + } + + #[test] + fn av1_vp9_opus_roundtrip() { + let a = Av1Config { seq_profile: 1, seq_level_idx_0: 8, high_bitdepth: true, config_obus: vec![1, 2], ..Default::default() }; + assert_eq!(Av1Config::parse(&a.to_bytes()).unwrap(), a); + let v = VpcConfig { + profile: 2, + level: 31, + bit_depth: 10, + chroma_subsampling: 1, + full_range: true, + colour_primaries: 9, + transfer_characteristics: 16, + matrix_coefficients: 9, + codec_init: vec![], + }; + assert_eq!(VpcConfig::parse(&v.to_bytes()).unwrap(), v); + let o = OpusConfig { + output_channels: 2, + pre_skip: 312, + input_sample_rate: 48000, + output_gain: 0, + channel_mapping_family: 0, + channel_mapping: vec![], + }; + assert_eq!(OpusConfig::parse(&o.to_bytes()).unwrap(), o); + } + + #[test] + fn aac_esds_roundtrip() { + let e = SampleEntry::aac(vec![0x12, 0x10], 2, 44100); + match &e.codec { + CodecConfig::Aac(a) => { + assert_eq!((a.object_type, a.sample_rate, a.channel_config), (2, 44100, 2)); + } + _ => panic!(), + } + assert_eq!(roundtrip(&e, false, b"soun"), e); + assert_eq!(roundtrip(&e, true, b"soun"), e); + } + + #[test] + fn pcm_entries() { + for (bits, float, be, qt) in [ + (16, false, false, true), + (16, false, true, true), + (24, false, false, true), + (32, true, false, true), + (24, false, true, false), + (32, true, false, false), + ] { + let p = PcmConfig { bits, float, big_endian: be, signed: true, channels: 2, sample_rate: 48000.0 }; + let e = SampleEntry::pcm(p); + let back = roundtrip(&e, qt, b"soun"); + assert_eq!(back.codec, CodecConfig::Pcm(p), "{bits} {float} {be} {qt}"); + } + } + + #[test] + fn timecode_and_prores() { + let tc = + TimecodeConfig { flags: 1, timescale: 30000, frame_duration: 1001, frames_per_second: 30, start_frame: None, name: Some("reel".into()) }; + let e = SampleEntry { + format: FourCc(*b"tmcd"), + data_reference_index: 1, + codec: CodecConfig::Timecode(tc.clone()), + video: None, + audio: None, + bitrate: None, + }; + assert_eq!(roundtrip(&e, true, b"tmcd"), e); + assert_eq!(tc.format_frame(107892), "01:00:00;00"); + let nd = TimecodeConfig { flags: 0, frames_per_second: 24, ..Default::default() }; + assert_eq!(nd.format_frame(86400), "01:00:00:00"); + let p = SampleEntry::prores(FourCc(*b"apch"), 1920, 1080); + assert_eq!(roundtrip(&p, true, b"vide"), p); + } + + #[test] + fn asc_parse() { + assert_eq!(parse_asc(&[0x11, 0x90]), Some((2, 48000, 2))); + assert_eq!(parse_asc(&[]), None); + } +} diff --git a/crates/isobmff/src/demux.rs b/crates/isobmff/src/demux.rs new file mode 100644 index 0000000..dc9e764 --- /dev/null +++ b/crates/isobmff/src/demux.rs @@ -0,0 +1,1173 @@ +//! Demuxer: parses box structure lazily (headers + `moov`/`moof` only) and builds per-track sample tables. + +use crate::bytes::{Cur, FourCc, boxes, find, parse_box_header}; +use crate::codec::{CodecConfig, SampleEntry, parse_sample_entry}; +use crate::error::{Error, Result, invalid}; +use crate::source::ByteSource; +use std::collections::HashMap; + +/// Hard cap on per-track sample count (after PCM chunk merging). +const MAX_SAMPLES: usize = 1 << 26; +/// Hard cap on the size of an in-memory `moov`/`moof` box. +const MAX_HEADER_BOX: u64 = 1 << 30; + +/// One sample (access unit) of a track, in the track's media timescale. +#[derive(Clone, Copy, Debug, PartialEq, Eq, Default)] +pub struct Sample { + /// Absolute byte offset in the file. + pub offset: u64, + pub size: u32, + /// Decode timestamp (raw media time, edit lists not applied). + pub dts: i64, + /// Presentation timestamp = dts + composition offset (raw media time). + pub pts: i64, + pub duration: u32, + pub is_sync: bool, + /// 1-based index into [`Track::entries`]. + pub description_index: u32, +} + +/// One edit list entry (`elst`). +#[derive(Clone, Copy, Debug, PartialEq, Eq)] +pub struct Edit { + /// Duration of this edit in the movie timescale. + pub segment_duration: u64, + /// Start time within the media (track timescale); `-1` marks an empty edit. + pub media_time: i64, + /// Media rate as 16.16 fixed point (0x0001_0000 = normal speed, 0 = dwell). + pub media_rate: i32, +} + +/// Broad classification of a track by its handler. +#[derive(Clone, Copy, Debug, PartialEq, Eq)] +pub enum TrackKind { + Video, + Audio, + Timecode, + Subtitle, + Other, +} + +/// A sample-to-group mapping (`sbgp`) with the matching descriptions (`sgpd`) kept raw. +#[derive(Clone, Debug, PartialEq, Eq, Default)] +pub struct SampleGroup { + pub grouping_type: FourCc, + /// (sample_count, group_description_index) runs. + pub runs: Vec<(u32, u32)>, + /// Raw description entries from `sgpd`. + pub descriptions: Vec>, +} + +/// A demuxed track. +#[derive(Clone, Debug, PartialEq)] +pub struct Track { + pub id: u32, + pub kind: TrackKind, + pub handler: FourCc, + pub handler_name: String, + /// Media timescale (`mdhd`). + pub timescale: u32, + /// Media duration in the media timescale (`mdhd`). + pub duration: u64, + /// Track duration in the movie timescale (`tkhd`). + pub tkhd_duration: u64, + /// ISO 639-2/T language code (e.g. `und`, `eng`). + pub language: String, + /// `tkhd` presentation size (16.16 fixed point converted to integer pixels, rounded down). + pub width: u32, + pub height: u32, + pub enabled: bool, + pub alternate_group: u16, + /// `tkhd` volume (8.8 fixed point). + pub volume: i16, + /// `tkhd` transformation matrix. + pub matrix: [i32; 9], + pub entries: Vec, + pub samples: Vec, + pub edits: Vec, + /// Offset to add to raw pts to get presentation time (track timescale), derived from the + /// leading empty edits and the first non-empty edit's `media_time`. + pub edit_offset: i64, + /// Track references (`tref`): (type, track ids). + pub references: Vec<(FourCc, Vec)>, + /// Raw `sdtp` bytes (one per sample) if present. + pub sdtp: Vec, + pub sample_groups: Vec, + /// True if uncompressed audio samples were merged into one [`Sample`] per chunk. + pub pcm_chunked: bool, + sync_index: Vec, + pts_order: Vec, +} + +impl Track { + /// The first sample entry's codec configuration. + pub fn codec(&self) -> Option<&CodecConfig> { + self.entries.first().map(|e| &e.codec) + } + pub fn video(&self) -> Option<&crate::codec::VideoParams> { + self.entries.first().and_then(|e| e.video.as_ref()) + } + /// Clockwise quarter turns (0–3) the `tkhd` matrix applies for display, or None when it is not + /// a pure 0/90/180/270° rotation (mirrored, scaled or sheared — left to the caller). + /// + /// ISO/IEC 14496-12 §8.3.2: a sample point (p, q) is displayed at p′ = a·p + c·q + x, + /// q′ = b·p + d·q + y for the matrix {a, b, u, c, d, v, x, y, w}, with y pointing down. So + /// {0, 1, −1, 0} sends the top row to the right-hand column (90° clockwise — iPhone portrait + /// video), {−1, 0, 0, −1} is 180° and {0, −1, 1, 0} is 270°. Only the signs matter: a·d − b·c + /// carries the scale, which the presentation size already reflects. + pub fn display_rotation(&self) -> Option { + let [a, b, _, c, d, _, _, _, _] = self.matrix; + match (a.signum(), b.signum(), c.signum(), d.signum()) { + (1, 0, 0, 1) => Some(0), + (0, 1, -1, 0) => Some(1), + (-1, 0, 0, -1) => Some(2), + (0, -1, 1, 0) => Some(3), + _ => None, + } + } + pub fn audio(&self) -> Option<&crate::codec::AudioParams> { + self.entries.first().and_then(|e| e.audio.as_ref()) + } + /// Presentation (edit-adjusted) pts of sample `i`. + pub fn presentation_pts(&self, i: usize) -> Option { + self.samples.get(i).map(|s| s.pts.saturating_add(self.edit_offset)) + } + /// Index of the sample being presented at raw media time `pts` (the last sample, in + /// presentation order, whose pts is ≤ `pts`; the first sample if `pts` precedes all). + pub fn sample_at_pts(&self, pts: i64) -> Option { + if self.pts_order.is_empty() { + return None; + } + let k = self.pts_order.partition_point(|&i| self.samples[i as usize].pts <= pts); + Some(self.pts_order[k.saturating_sub(1)] as usize) + } + /// Like [`sample_at_pts`](Self::sample_at_pts) but for edit-adjusted presentation time. + pub fn sample_at_presentation_time(&self, t: i64) -> Option { + self.sample_at_pts(t.saturating_sub(self.edit_offset)) + } + /// The closest sync sample at or before `index` (decode order); 0 if there is none. + pub fn sync_sample_before(&self, index: usize) -> usize { + let k = self.sync_index.partition_point(|&i| i as usize <= index); + if k == 0 { 0 } else { self.sync_index[k - 1] as usize } + } + /// Indices of all sync samples. + pub fn sync_samples(&self) -> &[u32] { + &self.sync_index + } + + fn finish_indexes(&mut self) { + self.sync_index = self.samples.iter().enumerate().filter(|(_, s)| s.is_sync).map(|(i, _)| i as u32).collect(); + let mut order: Vec = (0..self.samples.len() as u32).collect(); + order.sort_by_key(|&i| (self.samples[i as usize].pts, i)); + self.pts_order = order; + } +} + +/// Text metadata from `udta` (QuickTime `©xxx` atoms and iTunes-style `meta/ilst`, incl. `keys`). +#[derive(Clone, Debug, PartialEq, Eq, Default)] +pub struct Metadata { + pub entries: Vec<(String, String)>, +} + +impl Metadata { + pub fn get(&self, key: &str) -> Option<&str> { + self.entries.iter().find(|(k, _)| k == key).map(|(_, v)| v.as_str()) + } + /// Title from `©nam` or the `com.apple.quicktime.title` key. + pub fn title(&self) -> Option<&str> { + self.get("©nam").or_else(|| self.get("com.apple.quicktime.title")).or_else(|| self.get("title")) + } +} + +/// A parsed MP4/MOV file (headers and sample tables; media data stays in the source). +#[derive(Clone, Debug, PartialEq)] +pub struct Mp4File { + pub major_brand: FourCc, + pub minor_version: u32, + pub compatible_brands: Vec, + /// QuickTime file (major brand `qt ` or no `ftyp`). + pub is_quicktime: bool, + /// Movie timescale (`mvhd`). + pub timescale: u32, + /// Movie duration in the movie timescale (`mvhd`; for fragmented files, the longest track). + pub duration: u64, + pub tracks: Vec, + pub metadata: Metadata, + /// File uses movie fragments (`mvex` present). + pub fragmented: bool, + /// Byte offset of the `moov` box. + pub moov_offset: u64, + /// Number of `moof` boxes found. + pub fragment_count: usize, +} + +impl Mp4File { + /// Read the bytes of sample `index` of track `track` (index into [`tracks`](Self::tracks)). + pub fn read_sample(&self, src: &S, track: usize, index: usize) -> Result> { + let t = self.tracks.get(track).ok_or(Error::NoSuchTrack(track))?; + let s = t.samples.get(index).ok_or(Error::NoSuchSample(index))?; + read_range(src, s.offset, s.size as u64) + } + pub fn sample_at_pts(&self, track: usize, pts: i64) -> Option { + self.tracks.get(track)?.sample_at_pts(pts) + } + pub fn sync_sample_before(&self, track: usize, index: usize) -> Option { + Some(self.tracks.get(track)?.sync_sample_before(index)) + } + /// First track of the given kind. + pub fn track_of_kind(&self, kind: TrackKind) -> Option { + self.tracks.iter().position(|t| t.kind == kind) + } +} + +fn read_range(src: &S, offset: u64, size: u64) -> Result> { + let end = offset.checked_add(size).ok_or(Error::Truncated("sample"))?; + if end > src.len() { + return Err(Error::Truncated("sample data")); + } + let mut v = vec![0u8; size as usize]; + src.read_at(offset, &mut v)?; + Ok(v) +} + +/// Parse an MP4/MOV file. Only box headers, `ftyp`, `moov` and `moof` boxes are read. +pub fn open(src: S) -> Result { + let len = src.len(); + let mut offset = 0u64; + let mut ftyp: Option> = None; + let mut moov: Option<(u64, Vec)> = None; + let mut moofs: Vec<(u64, Vec)> = Vec::new(); + while offset + 8 <= len { + let n = (len - offset).min(32) as usize; + let mut hb = [0u8; 32]; + src.read_at(offset, &mut hb[..n])?; + let h = parse_box_header(&hb[..n])?; + let size = h.size.unwrap_or(len - offset); + let end = offset.saturating_add(size).min(len); + let body_len = end.saturating_sub(offset + h.header_len); + let read_body = |max: u64| -> Result> { + if body_len > max { + return Err(Error::TooLarge("header box")); + } + read_range(&src, offset + h.header_len, body_len) + }; + match &h.kind.0 { + b"ftyp" if ftyp.is_none() => ftyp = Some(read_body(1 << 16)?), + b"moov" if moov.is_none() => moov = Some((offset, read_body(MAX_HEADER_BOX)?)), + b"moof" => moofs.push((offset, read_body(MAX_HEADER_BOX)?)), + _ => {} + } + if h.size.is_none() { + break; + } + offset = offset.saturating_add(size); + } + let (moov_offset, moov) = moov.ok_or(Error::NoMoov)?; + let mut file = Mp4File { + major_brand: FourCc::default(), + minor_version: 0, + compatible_brands: Vec::new(), + is_quicktime: true, + timescale: 0, + duration: 0, + tracks: Vec::new(), + metadata: Metadata::default(), + fragmented: false, + moov_offset, + fragment_count: moofs.len(), + }; + if let Some(f) = &ftyp { + let mut c = Cur::new(f, "ftyp"); + file.major_brand = c.fourcc()?; + file.minor_version = c.u32().unwrap_or(0); + while c.remaining() >= 4 { + file.compatible_brands.push(c.fourcc()?); + } + file.is_quicktime = file.major_brand == b"qt "; + } + let mut trex: HashMap = HashMap::new(); + let mut trak_payloads = Vec::new(); + for b in boxes(&moov) { + let b = b?; + match &b.kind.0 { + b"cmov" => return Err(Error::Unsupported("compressed movie header (cmov)".into())), + b"mvhd" => { + let mut c = Cur::new(b.payload, "mvhd"); + let (v, _) = c.full_header()?; + if v == 1 { + c.skip(16)?; + file.timescale = c.u32()?; + file.duration = c.u64()?; + } else { + c.skip(8)?; + file.timescale = c.u32()?; + file.duration = c.u32()? as u64; + } + } + b"trak" => trak_payloads.push(b.payload), + b"mvex" => { + file.fragmented = true; + for m in boxes(b.payload).map_while(|b| b.ok()) { + if m.kind == b"trex" { + let mut c = Cur::new(m.payload, "trex"); + c.full_header()?; + let id = c.u32()?; + trex.insert(id, Trex { description_index: c.u32()?, duration: c.u32()?, size: c.u32()?, flags: c.u32()? }); + } + } + } + b"udta" => parse_udta(b.payload, &mut file.metadata), + b"meta" => parse_meta(b.payload, &mut file.metadata), + _ => {} + } + } + for p in trak_payloads { + let t = parse_trak(p, &file, len)?; + file.tracks.push(t); + } + // Movie fragments. + let mut next_dts: Vec = file.tracks.iter().map(|t| t.samples.last().map(|s| s.dts + s.duration as i64).unwrap_or(0)).collect(); + for (moof_off, moof) in &moofs { + parse_moof(*moof_off, moof, &mut file.tracks, &trex, &mut next_dts, len)?; + } + for t in &mut file.tracks { + if file.fragmented && !moofs.is_empty() { + let d: u64 = t.samples.iter().map(|s| s.duration as u64).sum(); + t.duration = t.duration.max(d); + if t.timescale > 0 && file.timescale > 0 { + let md = (t.duration as u128 * file.timescale as u128 / t.timescale as u128) as u64; + file.duration = file.duration.max(md); + } + } + t.finish_indexes(); + // Timecode start frame from the first sample. + if let Some(CodecConfig::Timecode(tc)) = t.entries.first_mut().map(|e| &mut e.codec) + && let Some(s) = t.samples.first() + && s.size >= 4 + { + let mut b = [0u8; 4]; + if src.read_at(s.offset, &mut b).is_ok() { + tc.start_frame = Some(u32::from_be_bytes(b)); + } + } + } + Ok(file) +} + +#[derive(Clone, Copy, Debug, Default)] +struct Trex { + description_index: u32, + duration: u32, + size: u32, + flags: u32, +} + +fn parse_language(code: u16) -> String { + if code < 0x400 { + // QuickTime Macintosh language code. + return if code == 0 { "eng".into() } else { "und".into() }; + } + let c = |s: u16| (((code >> s) & 0x1F) as u8 + 0x60) as char; + let s: String = [c(10), c(5), c(0)].into_iter().collect(); + if s.chars().all(|ch| ch.is_ascii_lowercase()) { s } else { "und".into() } +} + +fn rescale(v: i128, from: u32, to: u32) -> i64 { + if from == 0 { + return 0; + } + (v * to as i128 / from as i128).clamp(i64::MIN as i128, i64::MAX as i128) as i64 +} + +fn parse_trak(p: &[u8], file: &Mp4File, file_len: u64) -> Result { + let mut t = Track { + id: 0, + kind: TrackKind::Other, + handler: FourCc::default(), + handler_name: String::new(), + timescale: 0, + duration: 0, + tkhd_duration: 0, + language: "und".into(), + width: 0, + height: 0, + enabled: true, + alternate_group: 0, + volume: 0, + matrix: [0x10000, 0, 0, 0, 0x10000, 0, 0, 0, 0x4000_0000], + entries: Vec::new(), + samples: Vec::new(), + edits: Vec::new(), + edit_offset: 0, + references: Vec::new(), + sdtp: Vec::new(), + sample_groups: Vec::new(), + pcm_chunked: false, + sync_index: Vec::new(), + pts_order: Vec::new(), + }; + let mut mdia = None; + for b in boxes(p) { + let b = b?; + match &b.kind.0 { + b"tkhd" => { + let mut c = Cur::new(b.payload, "tkhd"); + let (v, flags) = c.full_header()?; + t.enabled = flags & 1 != 0; + if v == 1 { + c.skip(16)?; + t.id = c.u32()?; + c.skip(4)?; + t.tkhd_duration = c.u64()?; + } else { + c.skip(8)?; + t.id = c.u32()?; + c.skip(4)?; + t.tkhd_duration = c.u32()? as u64; + } + c.skip(8)?; + let _layer = c.i16()?; + t.alternate_group = c.u16()?; + t.volume = c.i16()?; + c.skip(2)?; + for m in t.matrix.iter_mut() { + *m = c.i32()?; + } + t.width = c.u32()? >> 16; + t.height = c.u32()? >> 16; + } + b"edts" => { + if let Some(e) = find(b.payload, b"elst") { + let mut c = Cur::new(e, "elst"); + let (v, _) = c.full_header()?; + let n = c.count(if v == 1 { 20 } else { 12 })?; + for _ in 0..n { + let (segment_duration, media_time) = if v == 1 { (c.u64()?, c.i64()?) } else { (c.u32()? as u64, c.i32()? as i64) }; + t.edits.push(Edit { segment_duration, media_time, media_rate: c.i32()? }); + } + } + } + b"tref" => { + for r in boxes(b.payload).map_while(|b| b.ok()) { + let ids = r.payload.as_chunks::<4>().0.iter().map(|c| u32::from_be_bytes([c[0], c[1], c[2], c[3]])).collect(); + t.references.push((r.kind, ids)); + } + } + b"mdia" => mdia = Some(b.payload), + _ => {} + } + } + let mdia = mdia.ok_or_else(|| Error::Invalid("trak without mdia".into()))?; + let mut minf = None; + for b in boxes(mdia) { + let b = b?; + match &b.kind.0 { + b"mdhd" => { + let mut c = Cur::new(b.payload, "mdhd"); + let (v, _) = c.full_header()?; + if v == 1 { + c.skip(16)?; + t.timescale = c.u32()?; + t.duration = c.u64()?; + } else { + c.skip(8)?; + t.timescale = c.u32()?; + t.duration = c.u32()? as u64; + } + t.language = parse_language(c.u16()?); + } + b"hdlr" => { + let mut c = Cur::new(b.payload, "hdlr"); + c.full_header()?; + let _pre = c.u32()?; + t.handler = c.fourcc()?; + c.skip(12)?; + let name = c.rest(); + // ISO: null-terminated UTF-8; QuickTime: Pascal string. + let s = if !name.is_empty() && name[0] as usize == name.len() - 1 && name.len() > 1 { + &name[1..] + } else { + name.split(|&b| b == 0).next().unwrap_or(&[]) + }; + t.handler_name = String::from_utf8_lossy(s).into_owned(); + } + b"minf" => minf = Some(b.payload), + _ => {} + } + } + t.kind = match &t.handler.0 { + b"vide" => TrackKind::Video, + b"soun" => TrackKind::Audio, + b"tmcd" => TrackKind::Timecode, + b"text" | b"sbtl" | b"subt" | b"clcp" => TrackKind::Subtitle, + _ => TrackKind::Other, + }; + // Edit offset in media timescale. + let mut empty = 0i128; + for e in &t.edits { + if e.media_time == -1 { + empty += e.segment_duration as i128; + } else { + t.edit_offset = rescale(empty, file.timescale, t.timescale).saturating_sub(e.media_time); + break; + } + } + if let Some(stbl) = minf.and_then(|m| find(m, b"stbl")) { + parse_stbl(stbl, &mut t, file.is_quicktime, file_len)?; + } + Ok(t) +} + +struct SttsCursor<'a> { + runs: &'a [(u32, u32)], + run: usize, + used: u32, +} + +impl SttsCursor<'_> { + /// Advance by `n` samples, returning the summed duration. + fn take(&mut self, mut n: u64) -> u64 { + let mut total = 0u64; + while n > 0 && self.run < self.runs.len() { + let (count, delta) = self.runs[self.run]; + let avail = (count - self.used) as u64; + let k = avail.min(n); + total = total.saturating_add(k.saturating_mul(delta as u64)); + n -= k; + self.used += k as u32; + if self.used >= count { + self.run += 1; + self.used = 0; + } + } + total + } + fn next_value(&mut self) -> u32 { + while self.run < self.runs.len() && self.runs[self.run].0 == 0 { + self.run += 1; + } + let Some(&(_, d)) = self.runs.get(self.run) else { return 0 }; + self.take(1); + d + } +} + +fn parse_stbl(stbl: &[u8], t: &mut Track, is_qt: bool, file_len: u64) -> Result<()> { + let mut stts: Vec<(u32, u32)> = Vec::new(); + let mut ctts: Vec<(u32, u32)> = Vec::new(); + let mut stss: Option> = None; + let mut stsc: Vec<(u32, u32, u32)> = Vec::new(); + let mut const_size = 0u32; + let mut sample_count = 0usize; + let mut sizes: Vec = Vec::new(); + let mut chunks: Vec = Vec::new(); + let mut sbgp: Vec<(FourCc, Vec<(u32, u32)>)> = Vec::new(); + let mut sgpd: HashMap>> = HashMap::new(); + for b in boxes(stbl) { + let b = b?; + let mut c = Cur::new(b.payload, "stbl child"); + match &b.kind.0 { + b"stsd" => { + c.full_header()?; + let n = c.count(8)?; + for e in boxes(c.rest()).take(n) { + let e = e?; + t.entries.push(parse_sample_entry(e.kind, e.payload, t.handler, is_qt)?); + } + } + b"stts" => { + c.full_header()?; + let n = c.count(8)?; + stts = (0..n).map(|_| Ok((c.u32()?, c.u32()?))).collect::>()?; + } + b"ctts" => { + c.full_header()?; + let n = c.count(8)?; + ctts = (0..n).map(|_| Ok((c.u32()?, c.u32()?))).collect::>()?; + } + b"stss" => { + c.full_header()?; + let n = c.count(4)?; + let mut v: Vec = (0..n).map(|_| c.u32()).collect::>()?; + v.sort_unstable(); + stss = Some(v); + } + b"stsc" => { + c.full_header()?; + let n = c.count(12)?; + stsc = (0..n).map(|_| Ok((c.u32()?, c.u32()?, c.u32()?))).collect::>()?; + } + b"stsz" => { + c.full_header()?; + const_size = c.u32()?; + let n = c.u32()? as usize; + if const_size == 0 { + c.check_count(n, 4)?; + sizes = (0..n).map(|_| c.u32()).collect::>()?; + sample_count = n; + } else { + // Samples of constant size must fit in the file: clamp absurd counts. + sample_count = (n as u64).min(file_len / const_size as u64) as usize; + } + } + b"stz2" => { + c.full_header()?; + let field = c.u32()? & 0xFF; + let n = c.u32()? as usize; + let bits = match field { + 4 | 8 | 16 => field as usize, + _ => return invalid(format!("stz2 field size {field}")), + }; + if n.checked_mul(bits).is_none_or(|b| b.div_ceil(8) > c.remaining()) { + return Err(Error::Truncated("stz2")); + } + let d = c.rest(); + sizes = (0..n) + .map(|i| match bits { + 4 => ((d[i / 2] >> if i % 2 == 0 { 4 } else { 0 }) & 0xF) as u32, + 8 => d[i] as u32, + _ => u16::from_be_bytes([d[2 * i], d[2 * i + 1]]) as u32, + }) + .collect(); + sample_count = n; + } + b"stco" => { + c.full_header()?; + let n = c.count(4)?; + chunks = (0..n).map(|_| c.u32().map(|v| v as u64)).collect::>()?; + } + b"co64" => { + c.full_header()?; + let n = c.count(8)?; + chunks = (0..n).map(|_| c.u64()).collect::>()?; + } + b"sdtp" => { + c.full_header()?; + t.sdtp = c.rest().to_vec(); + } + b"sbgp" => { + let (v, _) = c.full_header()?; + let gt = c.fourcc()?; + if v == 1 { + c.u32()?; + } + let n = c.count(8)?; + let runs = (0..n).map(|_| Ok((c.u32()?, c.u32()?))).collect::>()?; + sbgp.push((gt, runs)); + } + b"sgpd" => { + let (v, _) = c.full_header()?; + let gt = c.fourcc()?; + let default_len = if v >= 1 { c.u32()? } else { 0 }; + if v >= 2 { + c.u32()?; + } + let n = c.count(0)?; + let mut descs = Vec::new(); + for _ in 0..n { + let l = if v >= 1 && default_len == 0 { c.u32()? } else { default_len }; + if l == 0 { + // Version 0 without known length: keep the remainder as a single entry. + descs.push(c.rest().to_vec()); + break; + } + descs.push(c.bytes(l as usize)?.to_vec()); + } + sgpd.insert(gt, descs); + } + _ => {} + } + } + t.sample_groups = + sbgp.into_iter().map(|(gt, runs)| SampleGroup { grouping_type: gt, runs, descriptions: sgpd.remove(>).unwrap_or_default() }).collect(); + if sample_count == 0 || chunks.is_empty() || stsc.is_empty() { + return Ok(()); + } + if stsc[0].0 == 0 || stsc.windows(2).any(|w| w[1].0 <= w[0].0) { + return invalid("stsc first_chunk values are not increasing from 1"); + } + let pcm = match t.entries.first().map(|e| &e.codec) { + Some(CodecConfig::Pcm(p)) if const_size != 0 => Some(*p), + _ => None, + }; + let mut stts_c = SttsCursor { runs: &stts, run: 0, used: 0 }; + let mut ctts_c = SttsCursor { runs: &ctts, run: 0, used: 0 }; + let mut samples = Vec::new(); + let mut sidx = 0usize; + let mut dts = 0i64; + let mut stsc_i = 0usize; + if let Some(p) = pcm { + t.pcm_chunked = true; + let a = t.audio().cloned().unwrap_or_default(); + let frame_bytes = |frames: u64| -> u64 { + if a.qt_version == 1 && a.bytes_per_frame > 0 && a.samples_per_packet > 0 { + frames / a.samples_per_packet as u64 * a.bytes_per_frame as u64 + } else if const_size > 1 { + frames * const_size as u64 + } else { + frames * p.bytes_per_frame() as u64 + } + }; + if chunks.len() > MAX_SAMPLES { + return Err(Error::TooLarge("chunk count")); + } + samples.reserve(chunks.len()); + for (ci, &off) in chunks.iter().enumerate() { + while stsc_i + 1 < stsc.len() && stsc[stsc_i + 1].0 as usize <= ci + 1 { + stsc_i += 1; + } + let spc = (stsc[stsc_i].1 as usize).min(sample_count - sidx); + if spc == 0 { + continue; + } + let dur = stts_c.take(spc as u64); + let size = frame_bytes(spc as u64).min(u32::MAX as u64) as u32; + samples.push(Sample { + offset: off, + size, + dts, + pts: dts, + duration: dur.min(u32::MAX as u64) as u32, + is_sync: true, + description_index: stsc[stsc_i].2, + }); + dts = dts.saturating_add(dur.min(i64::MAX as u64) as i64); + sidx += spc; + if sidx >= sample_count { + break; + } + } + } else { + if sample_count > MAX_SAMPLES { + return Err(Error::TooLarge("sample count")); + } + samples.reserve(sample_count); + let mut stss_i = 0usize; + 'chunks: for (ci, &off) in chunks.iter().enumerate() { + while stsc_i + 1 < stsc.len() && stsc[stsc_i + 1].0 as usize <= ci + 1 { + stsc_i += 1; + } + let (_, spc, sdi) = stsc[stsc_i]; + let mut pos = off; + for _ in 0..spc { + if sidx >= sample_count { + break 'chunks; + } + let size = if const_size != 0 { const_size } else { sizes[sidx] }; + let duration = stts_c.next_value(); + let cts = ctts_c.next_value() as i32 as i64; + let is_sync = match &stss { + None => true, + Some(v) => { + while stss_i < v.len() && (v[stss_i] as usize) < sidx + 1 { + stss_i += 1; + } + stss_i < v.len() && v[stss_i] as usize == sidx + 1 + } + }; + samples.push(Sample { offset: pos, size, dts, pts: dts.saturating_add(cts), duration, is_sync, description_index: sdi }); + pos = pos.saturating_add(size as u64); + dts = dts.saturating_add(duration as i64); + sidx += 1; + } + } + } + t.samples = samples; + Ok(()) +} + +fn parse_moof(moof_off: u64, moof: &[u8], tracks: &mut [Track], trex: &HashMap, next_dts: &mut [i64], file_len: u64) -> Result<()> { + // End of data of the previous traf, for the base-offset default of subsequent trafs. + let mut prev_end: Option = None; + for b in boxes(moof) { + let b = b?; + if b.kind != b"traf" { + continue; + } + let mut tfhd = None; + let mut tfdt = None; + let mut truns = Vec::new(); + for c in boxes(b.payload) { + let c = c?; + match &c.kind.0 { + b"tfhd" => tfhd = Some(c.payload), + b"tfdt" => tfdt = Some(c.payload), + b"trun" => truns.push(c.payload), + _ => {} + } + } + let tfhd = tfhd.ok_or_else(|| Error::Invalid("traf without tfhd".into()))?; + let mut c = Cur::new(tfhd, "tfhd"); + let (_, fl) = c.full_header()?; + let id = c.u32()?; + let Some(ti) = tracks.iter().position(|t| t.id == id) else { continue }; + let d = trex.get(&id).copied().unwrap_or_default(); + let base = if fl & 1 != 0 { + c.u64()? + } else if fl & 0x20000 != 0 { + moof_off + } else { + prev_end.unwrap_or(moof_off) + }; + let sdi = if fl & 2 != 0 { c.u32()? } else { d.description_index }; + let def_dur = if fl & 8 != 0 { c.u32()? } else { d.duration }; + let def_size = if fl & 0x10 != 0 { c.u32()? } else { d.size }; + let def_flags = if fl & 0x20 != 0 { c.u32()? } else { d.flags }; + if let Some(p) = tfdt { + let mut c = Cur::new(p, "tfdt"); + let (v, _) = c.full_header()?; + let t = if v == 1 { c.u64()? } else { c.u32()? as u64 }; + next_dts[ti] = t.min(i64::MAX as u64) as i64; + } + let mut data_pos = base; + for tr in truns { + let mut c = Cur::new(tr, "trun"); + let (v, tf) = c.full_header()?; + let per = [0x100u32, 0x200, 0x400, 0x800].iter().filter(|&&f| tf & f != 0).count() * 4; + let n = c.u32()? as usize; + if per > 0 { + c.check_count(n, per)?; + } else if (n as u64).saturating_mul(def_size.max(1) as u64) > file_len { + return Err(Error::TooLarge("trun sample count")); + } + if tracks[ti].samples.len() + n > MAX_SAMPLES { + return Err(Error::TooLarge("sample count")); + } + if tf & 1 != 0 { + data_pos = base.wrapping_add_signed(c.i32()? as i64); + } + let first_flags = if tf & 4 != 0 { Some(c.u32()?) } else { None }; + let t = &mut tracks[ti]; + t.samples.reserve(n); + for i in 0..n { + let dur = if tf & 0x100 != 0 { c.u32()? } else { def_dur }; + let size = if tf & 0x200 != 0 { c.u32()? } else { def_size }; + let flags = if tf & 0x400 != 0 { c.u32()? } else { def_flags }; + // first_sample_flags overrides the flags of the first sample only. + let flags = if i == 0 { first_flags.unwrap_or(flags) } else { flags }; + let cto = if tf & 0x800 != 0 { + let raw = c.u32()?; + if v == 0 { raw as i64 } else { raw as i32 as i64 } + } else { + 0 + }; + let dts = next_dts[ti]; + t.samples.push(Sample { + offset: data_pos, + size, + dts, + pts: dts.saturating_add(cto), + duration: dur, + is_sync: flags & 0x1_0000 == 0, + description_index: sdi, + }); + data_pos = data_pos.saturating_add(size as u64); + next_dts[ti] = dts.saturating_add(dur as i64); + } + } + prev_end = Some(data_pos); + } + Ok(()) +} + +fn parse_udta(p: &[u8], md: &mut Metadata) { + for b in boxes(p).map_while(|b| b.ok()) { + if b.kind == b"meta" { + parse_meta(b.payload, md); + } else if b.kind.0[0] == 0xA9 && b.payload.len() >= 4 { + let n = u16::from_be_bytes([b.payload[0], b.payload[1]]) as usize; + if let Some(s) = b.payload.get(4..4 + n) { + md.entries.push((b.kind.to_string_lossy(), String::from_utf8_lossy(s).into_owned())); + } + } + } +} + +fn parse_meta(p: &[u8], md: &mut Metadata) { + // QuickTime `meta` is a plain box; ISO `meta` is a full box. + let body = if p.len() >= 8 && &p[4..8] == b"hdlr" { p } else { p.get(4..).unwrap_or(&[]) }; + let mut keys: Vec = Vec::new(); + if let Some(k) = find(body, b"keys") { + let mut c = Cur::new(k, "keys"); + if c.full_header().is_ok() + && let Ok(n) = c.count(8) + { + for _ in 0..n { + let Ok(sz) = c.u32() else { break }; + let Ok(_ns) = c.u32() else { break }; + let Ok(name) = c.bytes((sz as usize).saturating_sub(8)) else { break }; + keys.push(String::from_utf8_lossy(name).into_owned()); + } + } + } + let Some(ilst) = find(body, b"ilst") else { return }; + for item in boxes(ilst).map_while(|b| b.ok()) { + let idx = item.kind.as_u32() as usize; + let key = if !keys.is_empty() && idx >= 1 && idx <= keys.len() { keys[idx - 1].clone() } else { item.kind.to_string_lossy() }; + if let Some(d) = find(item.payload, b"data") + && d.len() >= 8 + { + let ty = u32::from_be_bytes([d[0], d[1], d[2], d[3]]) & 0xFF_FFFF; + if ty == 1 || ty == 0 { + md.entries.push((key, String::from_utf8_lossy(&d[8..]).into_owned())); + } + } + } +} + +#[cfg(test)] +mod tests { + use super::*; + + #[test] + fn language_codes() { + // "und" = 0x55C4 + assert_eq!(parse_language(0x55C4), "und"); + assert_eq!(parse_language(0), "eng"); + assert_eq!(parse_language(0x15C7), "eng"); + } + + #[test] + fn stts_cursor() { + let runs = [(2, 10), (0, 99), (3, 5)]; + let mut c = SttsCursor { runs: &runs, run: 0, used: 0 }; + assert_eq!(c.next_value(), 10); + assert_eq!(c.take(3), 10 + 5 + 5); + assert_eq!(c.next_value(), 5); + assert_eq!(c.next_value(), 0); + } + + #[test] + fn rejects_no_moov_and_cmov() { + let mut b = crate::bytes::BoxBuf::new(); + b.leaf(b"free", &[0; 4]); + assert!(matches!(open(b.buf.clone()), Err(Error::NoMoov))); + let m = b.start(b"moov"); + b.leaf(b"cmov", &[]); + b.end(m); + assert!(matches!(open(b.buf), Err(Error::Unsupported(_)))); + } + + #[test] + fn metadata_qt_and_ilst() { + let mut b = crate::bytes::BoxBuf::new(); + let m = b.start(&[0xA9, b'n', b'a', b'm']); + b.u16(2); + b.u16(0); + b.bytes(b"hi"); + b.end(m); + let meta = b.start_full(b"meta", 0, 0); + b.leaf(b"hdlr", &[0; 24]); + let il = b.start(b"ilst"); + let it = b.start(&[0xA9, b'A', b'R', b'T']); + let d = b.start(b"data"); + b.u32(1); + b.u32(0); + b.bytes(b"me"); + b.end(d); + b.end(it); + b.end(il); + b.end(meta); + let mut md = Metadata::default(); + parse_udta(&b.buf, &mut md); + assert_eq!(md.title(), Some("hi")); + assert_eq!(md.get("©ART"), Some("me")); + } + + use crate::bytes::BoxBuf; + + fn full(b: &mut BoxBuf, kind: &[u8; 4], v: u8, f: impl FnOnce(&mut BoxBuf)) { + let m = b.start_full(kind, v, 0); + f(b); + b.end(m); + } + + /// Hand-built file: uuid/wide/skip boxes, stz2 sizes, co64, ctts v1 with negative offsets, + /// stss, sdtp, sbgp/sgpd, and an edit list with a leading empty edit. + fn handmade(stsz_override: Option<(u32, u32)>) -> Vec { + let mut b = BoxBuf::new(); + let m = b.start(b"ftyp"); + b.bytes(b"isom"); + b.u32(0); + b.bytes(b"isom"); + b.end(m); + let m = b.start(b"uuid"); + b.bytes(&[0xAB; 16]); + b.bytes(b"payload"); + b.end(m); + b.leaf(b"wide", &[]); + b.leaf(b"skip", &[1, 2, 3]); + // mdat with 64-bit size: 4 samples of 3,1,2,4 bytes. + let data_start = b.len() as u64 + 16; + b.u32(1); + b.bytes(b"mdat"); + b.u64(16 + 10); + b.bytes(&[1, 1, 1, 2, 3, 3, 4, 4, 4, 4]); + let moov = b.start(b"moov"); + full(&mut b, b"mvhd", 0, |b| { + b.u32(0); + b.u32(0); + b.u32(1000); + b.u32(500); + b.zeros(80); + }); + let trak = b.start(b"trak"); + full(&mut b, b"tkhd", 0, |b| { + b.u32(0); + b.u32(0); + b.u32(7); + b.u32(0); + b.u32(500); + b.zeros(52); + b.u32(320 << 16); + b.u32(240 << 16); + }); + let edts = b.start(b"edts"); + full(&mut b, b"elst", 1, |b| { + b.u32(2); + b.u64(100); + b.i64(-1); + b.i32(0x10000); + b.u64(400); + b.i64(20); + b.i32(0x10000); + }); + b.end(edts); + let mdia = b.start(b"mdia"); + full(&mut b, b"mdhd", 0, |b| { + b.u32(0); + b.u32(0); + b.u32(100); + b.u32(40); + b.u16(0x15C7); + b.u16(0); + }); + full(&mut b, b"hdlr", 0, |b| { + b.u32(0); + b.bytes(b"vide"); + b.zeros(12); + b.bytes(b"Video\0"); + }); + let minf = b.start(b"minf"); + let stbl = b.start(b"stbl"); + full(&mut b, b"stsd", 0, |b| { + b.u32(1); + crate::codec::SampleEntry::jpeg(320, 240).write(b, false).unwrap(); + }); + full(&mut b, b"stts", 0, |b| { + b.u32(1); + b.u32(4); + b.u32(10); + }); + full(&mut b, b"ctts", 1, |b| { + b.u32(2); + b.u32(2); + b.i32(-5); + b.u32(2); + b.i32(5); + }); + full(&mut b, b"stss", 0, |b| { + b.u32(2); + b.u32(3); + b.u32(1); + }); + full(&mut b, b"stsc", 0, |b| { + b.u32(2); + b.u32(1); + b.u32(3); + b.u32(1); + b.u32(2); + b.u32(1); + b.u32(1); + }); + match stsz_override { + Some((size, count)) => full(&mut b, b"stsz", 0, |b| { + b.u32(size); + b.u32(count); + }), + None => full(&mut b, b"stz2", 0, |b| { + b.u32(4); + b.u32(4); + b.u8(0x31); + b.u8(0x24); + }), + } + full(&mut b, b"co64", 0, |b| { + b.u32(2); + b.u64(data_start); + b.u64(data_start + 6); + }); + full(&mut b, b"sdtp", 0, |b| b.bytes(&[0x20, 0x10, 0x10, 0x10])); + full(&mut b, b"sbgp", 0, |b| { + b.bytes(b"roll"); + b.u32(1); + b.u32(4); + b.u32(1); + }); + full(&mut b, b"sgpd", 1, |b| { + b.bytes(b"roll"); + b.u32(2); + b.u32(1); + b.i16(-1); + }); + b.end(stbl); + b.end(minf); + b.end(mdia); + b.end(trak); + b.end(moov); + // Trailing box extending to EOF (size 0). + b.u32(0); + b.bytes(b"free"); + b.bytes(&[9; 5]); + b.buf + } + + #[test] + fn handmade_sample_table() { + let d = handmade(None); + let f = open(d.as_slice()).unwrap(); + assert_eq!(f.major_brand, *b"isom"); + let t = &f.tracks[0]; + assert_eq!((t.id, t.kind, t.timescale, t.language.as_str()), (7, TrackKind::Video, 100, "eng")); + assert_eq!((t.width, t.height), (320, 240)); + assert_eq!(t.handler_name, "Video"); + let sizes: Vec = t.samples.iter().map(|s| s.size).collect(); + assert_eq!(sizes, [3, 1, 2, 4]); + let offs: Vec = t.samples.iter().map(|s| s.offset - t.samples[0].offset).collect(); + assert_eq!(offs, [0, 3, 4, 6]); + let pts: Vec = t.samples.iter().map(|s| s.pts).collect(); + assert_eq!(pts, [-5, 5, 25, 35]); + let sync: Vec = t.samples.iter().map(|s| s.is_sync).collect(); + assert_eq!(sync, [true, false, true, false]); + for (i, want) in [vec![1u8, 1, 1], vec![2], vec![3, 3], vec![4; 4]].iter().enumerate() { + assert_eq!(&f.read_sample(d.as_slice(), 0, i).unwrap(), want); + } + // Empty edit of 100 movie ticks = 10 media ticks, then media starts at 20. + assert_eq!(t.edits.len(), 2); + assert_eq!(t.edit_offset, 10 - 20); + assert_eq!(t.presentation_pts(2), Some(15)); + assert_eq!(t.sdtp.len(), 4); + assert_eq!(t.sample_groups[0].grouping_type, *b"roll"); + assert_eq!(t.sample_groups[0].descriptions, vec![vec![0xFF, 0xFF]]); + assert!(f.read_sample(d.as_slice(), 0, 4).is_err()); + assert!(f.read_sample(d.as_slice(), 1, 0).is_err()); + } + + #[test] + fn absurd_counts_are_bounded() { + // Constant-size stsz claiming 4 billion samples is clamped to what fits in the file. + let d = handmade(Some((1, u32::MAX))); + let f = open(d.as_slice()).unwrap(); + assert!(f.tracks[0].samples.len() <= d.len()); + // A table claiming more entries than its box holds fails cleanly. + let mut d = handmade(None); + let p = d.windows(4).position(|w| w == b"stts").unwrap(); + d[p + 8..p + 12].copy_from_slice(&u32::MAX.to_be_bytes()); + assert!(matches!(open(d.as_slice()), Err(Error::Truncated(_)))); + // Box larger than its parent. + let mut d = handmade(None); + let p = d.windows(4).position(|w| w == b"stsc").unwrap(); + d[p - 4..p].copy_from_slice(&0x7FFF_FFFFu32.to_be_bytes()); + assert!(open(d.as_slice()).is_err()); + } +} diff --git a/crates/isobmff/src/error.rs b/crates/isobmff/src/error.rs new file mode 100644 index 0000000..b5dfd75 --- /dev/null +++ b/crates/isobmff/src/error.rs @@ -0,0 +1,38 @@ +use std::io; + +/// Errors produced by the demuxer and muxer. +#[derive(Debug, thiserror::Error)] +pub enum Error { + #[error("I/O error: {0}")] + Io(#[from] io::Error), + /// A box or structure ended before all required fields were read. + #[error("truncated data while reading {0}")] + Truncated(&'static str), + /// Structurally invalid data. + #[error("invalid data: {0}")] + Invalid(String), + /// Valid but unsupported feature (e.g. compressed `cmov` movie header). + #[error("unsupported: {0}")] + Unsupported(String), + /// A count or size exceeds sanity limits relative to the file size. + #[error("size limit exceeded: {0}")] + TooLarge(&'static str), + /// No `moov` box was found. + #[error("no moov box found")] + NoMoov, + /// A track index was out of range. + #[error("track index {0} out of range")] + NoSuchTrack(usize), + /// A sample index was out of range. + #[error("sample index {0} out of range")] + NoSuchSample(usize), + /// Muxer usage error (e.g. invalid track config, bad sample). + #[error("muxer: {0}")] + Mux(String), +} + +pub type Result = std::result::Result; + +pub(crate) fn invalid(msg: impl Into) -> Result { + Err(Error::Invalid(msg.into())) +} diff --git a/crates/isobmff/src/lib.rs b/crates/isobmff/src/lib.rs new file mode 100644 index 0000000..b34a158 --- /dev/null +++ b/crates/isobmff/src/lib.rs @@ -0,0 +1,28 @@ +//! Clean-room ISO Base Media File Format (MP4, ISO/IEC 14496-12/14/15) and QuickTime (MOV) support. +//! +//! - **Demux:** [`open`] parses box headers plus `moov` (and `moof` for fragmented files) from a +//! [`ByteSource`] and builds per-track [`Sample`] tables; media data is read on demand with +//! [`Mp4File::read_sample`]. +//! - **Mux:** [`Mp4Writer`] writes progressive MP4/MOV (optionally faststart); +//! [`FragmentedWriter`] writes fragmented MP4. +//! +//! See the crate README for supported boxes and limitations. + +#![cfg_attr(not(test), deny(clippy::unwrap_used, clippy::expect_used, clippy::panic, clippy::unimplemented, clippy::todo, clippy::unreachable))] + +mod bytes; +mod codec; +mod demux; +mod error; +mod mux; +mod source; + +pub use bytes::FourCc; +pub use codec::{ + AacConfig, AudioParams, Av1Config, AvcConfig, BitRate, CleanAperture, CodecConfig, ColorInfo, FieldInfo, FlacConfig, HevcConfig, HevcNalArray, + MasteringDisplay, OpusConfig, PcmConfig, SampleEntry, TimecodeConfig, VideoParams, VpcConfig, parse_asc, +}; +pub use demux::{Edit, Metadata, Mp4File, Sample, SampleGroup, Track, TrackKind, open}; +pub use error::{Error, Result}; +pub use mux::{Brand, FragmentedWriter, Mp4Writer, TrackConfig, WriteSample, WriterOptions}; +pub use source::ByteSource; diff --git a/crates/isobmff/src/mux.rs b/crates/isobmff/src/mux.rs new file mode 100644 index 0000000..f3a062b --- /dev/null +++ b/crates/isobmff/src/mux.rs @@ -0,0 +1,966 @@ +//! Muxers: progressive MP4/MOV ([`Mp4Writer`], optional faststart) and fragmented MP4 ([`FragmentedWriter`]). + +use crate::bytes::BoxBuf; +use crate::codec::{CodecConfig, SampleEntry, TimecodeConfig}; +use crate::demux::Edit; +use crate::error::{Error, Result}; +use std::io::{Read, Seek, SeekFrom, Write}; + +/// Output flavour: determines brands and some sample-entry encodings. +#[derive(Clone, Copy, Debug, PartialEq, Eq)] +pub enum Brand { + /// ISO MP4: `isom` major brand, compatible `isom iso2 avc1 mp41 mp42`. + Mp4, + /// QuickTime MOV: `qt ` major brand. + Mov, +} + +/// Movie-level writer options. +#[derive(Clone, Debug)] +pub struct WriterOptions { + pub brand: Brand, + /// Movie timescale (`mvhd`); default 1000. + pub movie_timescale: u32, + /// Text metadata written to `moov/udta` (keys like `©nam`, `©ART`, `©cmt`, `©too`). + pub metadata: Vec<(String, String)>, +} + +impl WriterOptions { + pub fn new(brand: Brand) -> Self { + WriterOptions { brand, movie_timescale: 1000, metadata: Vec::new() } + } +} + +/// Per-track configuration. +#[derive(Clone, Debug)] +pub struct TrackConfig { + pub entry: SampleEntry, + /// Media timescale. For PCM it must equal the sample rate (one tick per audio frame). + pub timescale: u32, + /// ISO 639-2/T language (`und` by default). + pub language: String, + /// Explicit edit list (segment durations in the movie timescale). Takes precedence over `media_start`. + pub edits: Vec, + /// Convenience: create one edit starting at this media time (e.g. AAC encoder delay / priming, + /// or the first pts of a B-frame stream) spanning the rest of the media. + pub media_start: Option, + /// `hdlr` name; a sensible default is used when `None`. + pub handler_name: Option, +} + +impl TrackConfig { + pub fn new(entry: SampleEntry, timescale: u32) -> Self { + TrackConfig { entry, timescale, language: "und".into(), edits: Vec::new(), media_start: None, handler_name: None } + } +} + +/// A sample handed to the muxers. +#[derive(Clone, Copy, Debug)] +pub struct WriteSample<'a> { + pub data: &'a [u8], + /// Duration in the media timescale. Ignored for PCM (each frame lasts one tick). + pub duration: u32, + /// pts − dts in the media timescale. + pub composition_offset: i32, + pub is_sync: bool, +} + +#[derive(Clone, Copy, PartialEq, Eq)] +enum Handler { + Video, + Audio, + Timecode, + Other, +} + +fn handler_of(e: &SampleEntry) -> Handler { + if matches!(e.codec, CodecConfig::Timecode(_)) { + Handler::Timecode + } else if e.video.is_some() { + Handler::Video + } else if e.audio.is_some() { + Handler::Audio + } else { + Handler::Other + } +} + +/// Accumulated sample tables for one track. +#[derive(Clone, Debug, Default)] +struct Tables { + stts: Vec<(u32, u32)>, + ctts: Vec<(u32, i32)>, + sizes: Vec, + /// Constant sample size (PCM) — then `sizes` is unused. + const_size: Option, + sample_count: u64, + /// 1-based sync sample numbers. + sync: Vec, + all_sync: bool, + /// (offset relative to file start before shifting, sample count) + chunks: Vec<(u64, u32)>, + media_duration: u64, + /// End of the last sample on the composition timeline (decode time + composition offset + + /// duration, maximum over samples). + comp_end: u64, +} + +impl Tables { + fn new() -> Self { + Tables { all_sync: true, ..Default::default() } + } + fn push_run(runs: &mut Vec<(u32, T)>, count: u32, v: T) { + if let Some(last) = runs.last_mut() + && last.1 == v + && last.0.checked_add(count).is_some() + { + last.0 += count; + return; + } + runs.push((count, v)); + } +} + +struct WTrack { + cfg: TrackConfig, + id: u32, + handler: Handler, + pcm_frame_bytes: Option, + t: Tables, + /// Track id of a linked timecode track (`tref/tmcd`). + tmcd_ref: Option, + /// For timecode tracks: (video track index, start frame). + tmcd_source: Option<(usize, u32)>, +} + +fn pack_language(lang: &str) -> u16 { + let b = lang.as_bytes(); + if b.len() == 3 && b.iter().all(|c| c.is_ascii_lowercase()) { + (((b[0] - 0x60) as u16) << 10) | (((b[1] - 0x60) as u16) << 5) | ((b[2] - 0x60) as u16) + } else { + 0x55C4 // "und" + } +} + +fn rescale(v: u64, from: u32, to: u32) -> u64 { + if from == 0 { + return 0; + } + (v as u128 * to as u128 / from as u128).min(u64::MAX as u128) as u64 +} + +const MATRIX: [i32; 9] = [0x10000, 0, 0, 0, 0x10000, 0, 0, 0, 0x4000_0000]; + +fn write_ftyp(b: &mut BoxBuf, brand: Brand) { + let m = b.start(b"ftyp"); + match brand { + Brand::Mp4 => { + b.bytes(b"isom"); + b.u32(0x200); + for c in [b"isom", b"iso2", b"avc1", b"mp41", b"mp42"] { + b.bytes(c); + } + } + Brand::Mov => { + b.bytes(b"qt "); + b.u32(0x200); + b.bytes(b"qt "); + } + } + b.end(m); +} + +fn movie_duration(tracks: &[WTrack], movie_ts: u32) -> u64 { + tracks.iter().map(|t| presentation_duration(t, movie_ts)).max().unwrap_or(0) +} + +fn effective_edits(t: &WTrack, movie_ts: u32) -> Vec { + if !t.cfg.edits.is_empty() { + return t.cfg.edits.clone(); + } + if let Some(start) = t.cfg.media_start { + // presentation runs from `start` to the end of the last sample in composition order (with + // B-frame reordering that is past the end of the decode timeline) + let rest = t.t.comp_end.max(t.t.media_duration).saturating_sub(start.max(0) as u64); + return vec![Edit { segment_duration: rescale(rest, t.cfg.timescale, movie_ts), media_time: start, media_rate: 0x10000 }]; + } + Vec::new() +} + +fn presentation_duration(t: &WTrack, movie_ts: u32) -> u64 { + let e = effective_edits(t, movie_ts); + if e.is_empty() { rescale(t.t.media_duration, t.cfg.timescale, movie_ts) } else { e.iter().map(|e| e.segment_duration).sum() } +} + +fn write_mvhd(b: &mut BoxBuf, timescale: u32, duration: u64, next_id: u32) { + let v1 = duration > u32::MAX as u64; + let m = b.start_full(b"mvhd", v1 as u8, 0); + if v1 { + b.u64(0); + b.u64(0); + b.u32(timescale); + b.u64(duration); + } else { + b.u32(0); + b.u32(0); + b.u32(timescale); + b.u32(duration as u32); + } + b.u32(0x10000); + b.u16(0x100); + b.zeros(10); + for v in MATRIX { + b.i32(v); + } + b.zeros(24); + b.u32(next_id); + b.end(m); +} + +fn write_trak(b: &mut BoxBuf, t: &WTrack, movie_ts: u32, qt: bool, shift: u64, force_co64: bool) -> Result<()> { + let trak = b.start(b"trak"); + let dur = presentation_duration(t, movie_ts); + let v1 = dur > u32::MAX as u64; + let m = b.start_full(b"tkhd", v1 as u8, 3); + if v1 { + b.u64(0); + b.u64(0); + b.u32(t.id); + b.u32(0); + b.u64(dur); + } else { + b.u32(0); + b.u32(0); + b.u32(t.id); + b.u32(0); + b.u32(dur as u32); + } + b.zeros(8); + b.i16(0); + b.u16(0); + b.i16(if t.handler == Handler::Audio { 0x100 } else { 0 }); + b.u16(0); + for v in MATRIX { + b.i32(v); + } + let (w, h) = match &t.cfg.entry.video { + Some(v) => { + let w = match v.pixel_aspect { + Some((hs, vs)) if hs > 0 && vs > 0 => (v.width as u64 * hs as u64 / vs as u64) as u32, + _ => v.width as u32, + }; + (w, v.height as u32) + } + None => (0, 0), + }; + b.u32(w << 16); + b.u32(h << 16); + b.end(m); + let edits = effective_edits(t, movie_ts); + if !edits.is_empty() { + let e = b.start(b"edts"); + let v1 = edits.iter().any(|e| e.segment_duration > u32::MAX as u64 || e.media_time > i32::MAX as i64 || e.media_time < i32::MIN as i64); + let l = b.start_full(b"elst", v1 as u8, 0); + b.u32(edits.len() as u32); + for ed in &edits { + if v1 { + b.u64(ed.segment_duration); + b.i64(ed.media_time); + } else { + b.u32(ed.segment_duration as u32); + b.i32(ed.media_time as i32); + } + b.i32(ed.media_rate); + } + b.end(l); + b.end(e); + } + if let Some(id) = t.tmcd_ref { + let r = b.start(b"tref"); + let m = b.start(b"tmcd"); + b.u32(id); + b.end(m); + b.end(r); + } + let mdia = b.start(b"mdia"); + let mv1 = t.t.media_duration > u32::MAX as u64; + let m = b.start_full(b"mdhd", mv1 as u8, 0); + if mv1 { + b.u64(0); + b.u64(0); + b.u32(t.cfg.timescale); + b.u64(t.t.media_duration); + } else { + b.u32(0); + b.u32(0); + b.u32(t.cfg.timescale); + b.u32(t.t.media_duration as u32); + } + b.u16(pack_language(&t.cfg.language)); + b.u16(0); + b.end(m); + let (htype, default_name) = match t.handler { + Handler::Video => (b"vide", "VideoHandler"), + Handler::Audio => (b"soun", "SoundHandler"), + Handler::Timecode => (b"tmcd", "TimeCodeHandler"), + Handler::Other => (b"meta", "MetaHandler"), + }; + let name = t.cfg.handler_name.clone().unwrap_or_else(|| default_name.into()); + let m = b.start_full(b"hdlr", 0, 0); + b.bytes(if qt { b"mhlr" } else { &[0, 0, 0, 0] }); + b.bytes(htype); + b.zeros(12); + if qt { + let n = name.len().min(255); + b.u8(n as u8); + b.bytes(&name.as_bytes()[..n]); + } else { + b.bytes(name.as_bytes()); + b.u8(0); + } + b.end(m); + let minf = b.start(b"minf"); + match t.handler { + Handler::Video => { + let m = b.start_full(b"vmhd", 0, 1); + b.zeros(8); + b.end(m); + } + Handler::Audio => { + let m = b.start_full(b"smhd", 0, 0); + b.zeros(4); + b.end(m); + } + Handler::Timecode => { + let g = b.start(b"gmhd"); + let m = b.start_full(b"gmin", 0, 0); + b.u16(0x40); + b.u16(0x8000); + b.u16(0x8000); + b.u16(0x8000); + b.i16(0); + b.u16(0); + b.end(m); + let tm = b.start(b"tmcd"); + let m = b.start_full(b"tcmi", 0, 0); + b.u16(0); + b.u16(0); + b.u16(12); + b.u16(0); + b.bytes(&[0xFF; 6]); + b.zeros(6); + b.u8(0); + b.end(m); + b.end(tm); + b.end(g); + } + Handler::Other => { + let m = b.start_full(b"nmhd", 0, 0); + b.end(m); + } + } + if qt { + // QuickTime data handler reference. + let m = b.start_full(b"hdlr", 0, 0); + b.bytes(b"dhlr"); + b.bytes(b"url "); + b.zeros(12); + b.u8(0); + b.end(m); + } + let d = b.start(b"dinf"); + let r = b.start_full(b"dref", 0, 0); + b.u32(1); + let u = b.start_full(b"url ", 0, 1); + b.end(u); + b.end(r); + b.end(d); + let stbl = b.start(b"stbl"); + let m = b.start_full(b"stsd", 0, 0); + b.u32(1); + t.cfg.entry.write(b, qt)?; + b.end(m); + let tb = &t.t; + let m = b.start_full(b"stts", 0, 0); + b.u32(tb.stts.len() as u32); + for &(c, d) in &tb.stts { + b.u32(c); + b.u32(d); + } + b.end(m); + if tb.ctts.iter().any(|&(_, o)| o != 0) { + let neg = tb.ctts.iter().any(|&(_, o)| o < 0); + let m = b.start_full(b"ctts", neg as u8, 0); + b.u32(tb.ctts.len() as u32); + for &(c, o) in &tb.ctts { + b.u32(c); + b.i32(o); + } + b.end(m); + } + if !tb.all_sync { + let m = b.start_full(b"stss", 0, 0); + b.u32(tb.sync.len() as u32); + for &s in &tb.sync { + b.u32(s); + } + b.end(m); + } + let m = b.start_full(b"stsc", 0, 0); + let mut runs: Vec<(u32, u32)> = Vec::new(); + for (i, &(_, n)) in tb.chunks.iter().enumerate() { + if runs.last().is_none_or(|r| r.1 != n) { + runs.push((i as u32 + 1, n)); + } + } + b.u32(runs.len() as u32); + for (first, n) in runs { + b.u32(first); + b.u32(n); + b.u32(1); + } + b.end(m); + let m = b.start_full(b"stsz", 0, 0); + match tb.const_size { + Some(s) => { + b.u32(s); + b.u32(tb.sample_count.min(u32::MAX as u64) as u32); + } + None => { + let first = tb.sizes.first().copied().unwrap_or(0); + // A uniform size of 0 would mean "table follows" (ISO/IEC 14496-12 §8.7.3), so + // all-empty samples must still be written as a table. + if first != 0 && !tb.sizes.is_empty() && tb.sizes.iter().all(|&s| s == first) { + b.u32(first); + b.u32(tb.sizes.len() as u32); + } else { + b.u32(0); + b.u32(tb.sizes.len() as u32); + for &s in &tb.sizes { + b.u32(s); + } + } + } + } + b.end(m); + let co64 = force_co64 || tb.chunks.iter().any(|&(o, _)| o + shift > u32::MAX as u64); + let m = b.start_full(if co64 { b"co64" } else { b"stco" }, 0, 0); + b.u32(tb.chunks.len() as u32); + for &(o, _) in &tb.chunks { + if co64 { + b.u64(o + shift); + } else { + b.u32((o + shift) as u32); + } + } + b.end(m); + b.end(stbl); + b.end(minf); + b.end(mdia); + b.end(trak); + Ok(()) +} + +fn write_udta(b: &mut BoxBuf, md: &[(String, String)]) { + let items: Vec<([u8; 4], &str)> = md + .iter() + .filter_map(|(k, v)| { + let mut chars = k.chars(); + if chars.next()? != '©' { + return None; + } + let rest: Vec = chars.map(|c| c as u32 as u8).collect(); + (rest.len() == 3).then(|| ([0xA9, rest[0], rest[1], rest[2]], v.as_str())) + }) + .collect(); + if items.is_empty() { + return; + } + let u = b.start(b"udta"); + for (k, v) in items { + let m = b.start(&k); + b.u16(v.len() as u16); + b.u16(0x55C4); + b.bytes(v.as_bytes()); + b.end(m); + } + b.end(u); +} + +fn validate_track(cfg: &TrackConfig) -> Result> { + if cfg.timescale == 0 { + return Err(Error::Mux("timescale must be non-zero".into())); + } + if let CodecConfig::Pcm(p) = &cfg.entry.codec { + if p.bits == 0 || p.channels == 0 { + return Err(Error::Mux("PCM needs bits and channels".into())); + } + if (p.sample_rate - cfg.timescale as f64).abs() > 0.5 { + return Err(Error::Mux("PCM track timescale must equal the sample rate".into())); + } + return Ok(Some(p.bytes_per_frame())); + } + Ok(None) +} + +/// Progressive MP4/MOV writer: `ftyp`, then one `mdat` streamed to `W`, then `moov` at [`finish`](Self::finish) +/// (or moved in front of `mdat` by [`finish_faststart`](Self::finish_faststart)). +pub struct Mp4Writer { + w: W, + opts: WriterOptions, + tracks: Vec, + /// Offset of the placeholder box (8 bytes) preceding the mdat header. + pad_pos: u64, + mdat_pos: u64, + pos: u64, + last_track: Option, +} + +impl Mp4Writer { + /// Start a file: writes `ftyp`, a placeholder box and the `mdat` header. + pub fn new(mut w: W, opts: WriterOptions) -> Result { + let start = w.stream_position()?; + let mut b = BoxBuf::new(); + write_ftyp(&mut b, opts.brand); + let pad_pos = start + b.len() as u64; + b.leaf(if opts.brand == Brand::Mov { b"wide" } else { b"free" }, &[]); + let mdat_pos = start + b.len() as u64; + b.leaf(b"mdat", &[]); + w.write_all(&b.buf)?; + let pos = start + b.len() as u64; + Ok(Mp4Writer { w, opts, tracks: Vec::new(), pad_pos, mdat_pos, pos, last_track: None }) + } + + /// Add a track; returns its index (track ids are index + 1). + pub fn add_track(&mut self, cfg: TrackConfig) -> Result { + let pcm = validate_track(&cfg)?; + let handler = handler_of(&cfg.entry); + let mut t = Tables::new(); + t.const_size = pcm; + self.tracks.push(WTrack { id: self.tracks.len() as u32 + 1, handler, pcm_frame_bytes: pcm, t, cfg, tmcd_ref: None, tmcd_source: None }); + Ok(self.tracks.len() - 1) + } + + /// Add a QuickTime timecode track referenced by `video_track`. Its single sample (the start + /// frame number) is written at finish, spanning the video track's duration. + pub fn add_timecode_track(&mut self, video_track: usize, tc: TimecodeConfig, start_frame: u32) -> Result { + if video_track >= self.tracks.len() { + return Err(Error::NoSuchTrack(video_track)); + } + if tc.timescale == 0 || tc.frame_duration == 0 || tc.frames_per_second == 0 { + return Err(Error::Mux("timecode needs timescale, frame duration and fps".into())); + } + let entry = SampleEntry { + format: crate::bytes::FourCc(*b"tmcd"), + data_reference_index: 1, + codec: CodecConfig::Timecode(tc.clone()), + video: None, + audio: None, + bitrate: None, + }; + let idx = self.add_track(TrackConfig::new(entry, tc.timescale))?; + self.tracks[idx].tmcd_source = Some((video_track, start_frame)); + let id = self.tracks[idx].id; + self.tracks[video_track].tmcd_ref = Some(id); + Ok(idx) + } + + /// Append one sample to track `track` (decode order). For PCM tracks `data` may hold any whole + /// number of audio frames. + pub fn write_sample(&mut self, track: usize, s: WriteSample) -> Result<()> { + let tr = self.tracks.get(track).ok_or(Error::NoSuchTrack(track))?; + if tr.tmcd_source.is_some() { + return Err(Error::Mux("timecode track samples are written automatically".into())); + } + let (count, dur) = match tr.pcm_frame_bytes { + Some(fb) => { + if !s.data.len().is_multiple_of(fb as usize) { + return Err(Error::Mux("PCM data is not a whole number of frames".into())); + } + ((s.data.len() / fb as usize) as u32, 1u32) + } + None => { + if s.data.len() > u32::MAX as usize { + return Err(Error::Mux("sample larger than 4 GiB".into())); + } + (1, s.duration) + } + }; + if count == 0 { + return Ok(()); + } + self.w.write_all(s.data)?; + let offset = self.pos; + self.pos += s.data.len() as u64; + let contiguous = self.last_track == Some(track); + self.last_track = Some(track); + let tr = &mut self.tracks[track]; + let t = &mut tr.t; + Tables::push_run(&mut t.stts, count, dur); + Tables::push_run(&mut t.ctts, count, if tr.pcm_frame_bytes.is_some() { 0 } else { s.composition_offset }); + if tr.pcm_frame_bytes.is_none() { + t.sizes.push(s.data.len() as u32); + if s.is_sync { + t.sync.push(t.sample_count as u32 + 1); + } else { + t.all_sync = false; + } + } + t.sample_count += count as u64; + let ct = if tr.pcm_frame_bytes.is_some() { 0 } else { s.composition_offset as i64 }; + t.comp_end = t.comp_end.max((t.media_duration as i64 + ct + count as i64 * dur as i64).max(0) as u64); + t.media_duration += count as u64 * dur as u64; + match t.chunks.last_mut() { + Some(c) if contiguous => c.1 += count, + _ => t.chunks.push((offset, count)), + } + Ok(()) + } + + fn finish_mdat(&mut self) -> Result<()> { + // Timecode samples. + for i in 0..self.tracks.len() { + let Some((vi, start)) = self.tracks[i].tmcd_source else { continue }; + let v = &self.tracks[vi]; + let vd = v.t.media_duration; + let vts = v.cfg.timescale; + let tts = self.tracks[i].cfg.timescale; + let tc_dur = match &self.tracks[i].cfg.entry.codec { + CodecConfig::Timecode(tc) => tc.frame_duration, + _ => 1, + }; + let d = rescale(vd, vts, tts).max(tc_dur as u64).min(u32::MAX as u64) as u32; + self.w.write_all(&start.to_be_bytes())?; + let off = self.pos; + self.pos += 4; + self.last_track = None; + let t = &mut self.tracks[i].t; + t.stts.push((1, d)); + t.sizes.push(4); + t.sync.push(1); + t.sample_count = 1; + t.media_duration = d as u64; + t.chunks.push((off, 1)); + } + let size = self.pos - self.mdat_pos; + if size <= u32::MAX as u64 { + self.w.seek(SeekFrom::Start(self.mdat_pos))?; + self.w.write_all(&(size as u32).to_be_bytes())?; + } else { + // Merge the placeholder into a 16-byte large-size mdat header. + let mut h = [0u8; 16]; + h[..4].copy_from_slice(&1u32.to_be_bytes()); + h[4..8].copy_from_slice(b"mdat"); + h[8..].copy_from_slice(&(self.pos - self.pad_pos).to_be_bytes()); + self.w.seek(SeekFrom::Start(self.pad_pos))?; + self.w.write_all(&h)?; + } + self.w.seek(SeekFrom::Start(self.pos))?; + Ok(()) + } + + fn build_moov(&self, shift: u64, force_co64: bool) -> Result> { + let qt = self.opts.brand == Brand::Mov; + let ts = self.opts.movie_timescale.max(1); + let mut b = BoxBuf::new(); + let m = b.start(b"moov"); + write_mvhd(&mut b, ts, movie_duration(&self.tracks, ts), self.tracks.len() as u32 + 1); + for t in &self.tracks { + write_trak(&mut b, t, ts, qt, shift, force_co64)?; + } + write_udta(&mut b, &self.opts.metadata); + b.end(m); + Ok(b.buf) + } + + /// Finish the file with `moov` after `mdat`. Returns the underlying writer. + pub fn finish(mut self) -> Result { + self.finish_mdat()?; + let moov = self.build_moov(0, false)?; + self.w.write_all(&moov)?; + self.w.flush()?; + Ok(self.w) + } +} + +impl Mp4Writer { + /// Finish with `moov` in front of `mdat` ("fast start"): media data is shifted forward in place. + pub fn finish_faststart(mut self) -> Result { + self.finish_mdat()?; + let mut moov = self.build_moov(0, false)?; + let mut force = false; + loop { + let shift = moov.len() as u64; + let next = self.build_moov(shift, force)?; + if next.len() == moov.len() { + moov = next; + break; + } + // Offsets crossed the 32-bit boundary: switch to co64 (size only grows, so this converges). + force = true; + moov = next; + } + let shift = moov.len() as u64; + let start = self.pad_pos; + let end = self.pos; + let mut buf = vec![0u8; 1 << 20]; + let mut cur = end; + while cur > start { + let n = (cur - start).min(buf.len() as u64) as usize; + cur -= n as u64; + self.w.seek(SeekFrom::Start(cur))?; + self.w.read_exact(&mut buf[..n])?; + self.w.seek(SeekFrom::Start(cur + shift))?; + self.w.write_all(&buf[..n])?; + } + self.w.seek(SeekFrom::Start(start))?; + self.w.write_all(&moov)?; + self.w.seek(SeekFrom::Start(end + shift))?; + self.w.flush()?; + Ok(self.w) + } +} + +/// Buffered fragment sample: (data, duration, composition offset, sync). +type PendingSample = (Vec, u32, i32, bool); + +/// Fragmented MP4 writer: an init segment (`ftyp` + `moov` with `mvex`) followed by +/// `moof`+`mdat` fragments. Samples are buffered until [`flush_fragment`](Self::flush_fragment). +pub struct FragmentedWriter { + w: W, + opts: WriterOptions, + tracks: Vec, + pending: Vec>, + next_dts: Vec, + seq: u32, + init_written: bool, +} + +impl FragmentedWriter { + pub fn new(w: W, opts: WriterOptions) -> Self { + FragmentedWriter { w, opts, tracks: Vec::new(), pending: Vec::new(), next_dts: Vec::new(), seq: 0, init_written: false } + } + + /// Add a track (before the first fragment). PCM is not supported in fragments. + pub fn add_track(&mut self, cfg: TrackConfig) -> Result { + if self.init_written { + return Err(Error::Mux("tracks must be added before the first fragment".into())); + } + if validate_track(&cfg)?.is_some() { + return Err(Error::Mux("PCM is not supported by the fragmented writer".into())); + } + let handler = handler_of(&cfg.entry); + self.tracks.push(WTrack { + id: self.tracks.len() as u32 + 1, + handler, + pcm_frame_bytes: None, + t: Tables::new(), + cfg, + tmcd_ref: None, + tmcd_source: None, + }); + self.pending.push(Vec::new()); + self.next_dts.push(0); + Ok(self.tracks.len() - 1) + } + + fn write_init(&mut self) -> Result<()> { + let qt = self.opts.brand == Brand::Mov; + let ts = self.opts.movie_timescale.max(1); + let mut b = BoxBuf::new(); + write_ftyp(&mut b, self.opts.brand); + let m = b.start(b"moov"); + write_mvhd(&mut b, ts, 0, self.tracks.len() as u32 + 1); + for t in &self.tracks { + write_trak(&mut b, t, ts, qt, 0, false)?; + } + let mv = b.start(b"mvex"); + for t in &self.tracks { + let x = b.start_full(b"trex", 0, 0); + b.u32(t.id); + b.u32(1); + b.u32(0); + b.u32(0); + b.u32(0); + b.end(x); + } + b.end(mv); + write_udta(&mut b, &self.opts.metadata); + b.end(m); + self.w.write_all(&b.buf)?; + self.init_written = true; + Ok(()) + } + + /// Buffer a sample for the next fragment. + pub fn write_sample(&mut self, track: usize, s: WriteSample) -> Result<()> { + let p = self.pending.get_mut(track).ok_or(Error::NoSuchTrack(track))?; + if s.data.len() > u32::MAX as usize { + return Err(Error::Mux("sample larger than 4 GiB".into())); + } + p.push((s.data.to_vec(), s.duration, s.composition_offset, s.is_sync)); + Ok(()) + } + + fn build_moof(&self, data_offsets: &[i32]) -> Vec { + let mut b = BoxBuf::new(); + let m = b.start(b"moof"); + let h = b.start_full(b"mfhd", 0, 0); + b.u32(self.seq); + b.end(h); + for (i, t) in self.tracks.iter().enumerate() { + let p = &self.pending[i]; + if p.is_empty() { + continue; + } + let tf = b.start(b"traf"); + let h = b.start_full(b"tfhd", 0, 0x20000); + b.u32(t.id); + b.end(h); + let h = b.start_full(b"tfdt", 1, 0); + b.u64(self.next_dts[i]); + b.end(h); + let neg = p.iter().any(|s| s.2 < 0); + let h = b.start_full(b"trun", neg as u8, 0x1 | 0x100 | 0x200 | 0x400 | 0x800); + b.u32(p.len() as u32); + b.i32(data_offsets[i]); + for (d, dur, cto, sync) in p { + b.u32(*dur); + b.u32(d.len() as u32); + b.u32(if *sync { 0x0200_0000 } else { 0x0101_0000 }); + b.i32(*cto); + } + b.end(h); + b.end(tf); + } + b.end(m); + b.buf + } + + /// Write all buffered samples as one `moof` + `mdat` fragment (no-op if nothing is buffered). + pub fn flush_fragment(&mut self) -> Result<()> { + if !self.init_written { + self.write_init()?; + } + if self.pending.iter().all(|p| p.is_empty()) { + return Ok(()); + } + self.seq += 1; + let probe = self.build_moof(&vec![0; self.tracks.len()]); + let data_len: u64 = self.pending.iter().flatten().map(|s| s.0.len() as u64).sum(); + let large = data_len + 8 > u32::MAX as u64; + let hdr = if large { 16 } else { 8 }; + let mut offs = Vec::with_capacity(self.tracks.len()); + let mut acc = probe.len() as u64 + hdr; + for p in &self.pending { + offs.push(i32::try_from(acc).map_err(|_| Error::Mux("fragment too large".into()))?); + acc += p.iter().map(|s| s.0.len() as u64).sum::(); + } + let moof = self.build_moof(&offs); + self.w.write_all(&moof)?; + if large { + self.w.write_all(&1u32.to_be_bytes())?; + self.w.write_all(b"mdat")?; + self.w.write_all(&(data_len + 16).to_be_bytes())?; + } else { + self.w.write_all(&((data_len + 8) as u32).to_be_bytes())?; + self.w.write_all(b"mdat")?; + } + for (i, p) in self.pending.iter_mut().enumerate() { + for (d, dur, _, _) in p.drain(..) { + self.w.write_all(&d)?; + self.next_dts[i] += dur as u64; + } + } + Ok(()) + } + + /// Flush remaining samples and return the writer. + pub fn finish(mut self) -> Result { + self.flush_fragment()?; + self.w.flush()?; + Ok(self.w) + } +} + +#[cfg(test)] +mod tests { + use super::*; + use crate::codec::{AvcConfig, PcmConfig}; + use std::io::Cursor; + + fn avc_entry() -> SampleEntry { + SampleEntry::avc(AvcConfig::new(vec![vec![0x67, 0x42, 0, 0x1E]], vec![vec![0x68, 0xCE]], 4), 64, 48) + } + + #[test] + fn language_pack() { + assert_eq!(pack_language("und"), 0x55C4); + assert_eq!(pack_language("eng"), 0x15C7); + assert_eq!(pack_language("EN"), 0x55C4); + } + + #[test] + fn simple_roundtrip_and_faststart() { + for fast in [false, true] { + let mut w = Mp4Writer::new(Cursor::new(Vec::new()), WriterOptions::new(Brand::Mp4)).unwrap(); + let v = w.add_track(TrackConfig::new(avc_entry(), 12800)).unwrap(); + let pcm = PcmConfig { bits: 16, float: false, big_endian: false, signed: true, channels: 2, sample_rate: 48000.0 }; + let a = w.add_track(TrackConfig::new(SampleEntry::pcm(pcm), 48000)).unwrap(); + for i in 0..10u8 { + let data = vec![i; 100 + i as usize]; + w.write_sample( + v, + WriteSample { data: &data, duration: 512, composition_offset: if i % 2 == 0 { 1024 } else { 0 }, is_sync: i % 5 == 0 }, + ) + .unwrap(); + w.write_sample(a, WriteSample { data: &[i; 400], duration: 0, composition_offset: 0, is_sync: true }).unwrap(); + } + let out = if fast { w.finish_faststart() } else { w.finish() }.unwrap().into_inner(); + let f = crate::open(out.as_slice()).unwrap(); + assert_eq!(f.tracks.len(), 2); + let t = &f.tracks[0]; + assert_eq!(t.samples.len(), 10); + for (i, s) in t.samples.iter().enumerate() { + assert_eq!(s.size as usize, 100 + i); + assert_eq!(s.dts, 512 * i as i64); + assert_eq!(s.is_sync, i % 5 == 0); + assert_eq!(f.read_sample(out.as_slice(), 0, i).unwrap(), vec![i as u8; 100 + i]); + } + let at = &f.tracks[1]; + assert!(at.pcm_chunked); + assert_eq!(at.samples.len(), 10); + assert_eq!(at.samples[3].size, 400); + assert_eq!(at.samples[3].duration, 100); + assert_eq!(f.read_sample(out.as_slice(), 1, 3).unwrap(), vec![3u8; 400]); + if fast { + assert!(f.moov_offset < t.samples[0].offset); + } + } + } + + #[test] + fn fragmented_roundtrip() { + let mut w = FragmentedWriter::new(Vec::new(), WriterOptions::new(Brand::Mp4)); + let v = w.add_track(TrackConfig::new(avc_entry(), 1000)).unwrap(); + for i in 0..6u8 { + w.write_sample(v, WriteSample { data: &[i; 10], duration: 40, composition_offset: -(i as i32 % 2), is_sync: i % 3 == 0 }).unwrap(); + if i % 3 == 2 { + w.flush_fragment().unwrap(); + } + } + let out = w.finish().unwrap(); + let f = crate::open(out.as_slice()).unwrap(); + assert!(f.fragmented); + assert_eq!(f.fragment_count, 2); + let t = &f.tracks[0]; + assert_eq!(t.samples.len(), 6); + for (i, s) in t.samples.iter().enumerate() { + assert_eq!(s.dts, 40 * i as i64); + assert_eq!(s.pts, s.dts - (i as i64 % 2)); + assert_eq!(s.is_sync, i % 3 == 0); + assert_eq!(f.read_sample(out.as_slice(), 0, i).unwrap(), vec![i as u8; 10]); + } + assert_eq!(t.sync_sample_before(5), 3); + } +} diff --git a/crates/isobmff/src/source.rs b/crates/isobmff/src/source.rs new file mode 100644 index 0000000..0dad82b --- /dev/null +++ b/crates/isobmff/src/source.rs @@ -0,0 +1,123 @@ +//! Random-access byte sources for the demuxer. + +use std::io; +use std::sync::Arc; + +/// Random-access, read-only byte source (file, in-memory buffer, web Blob…). +/// +/// Implementations must be able to serve reads at arbitrary offsets through `&self`. +pub trait ByteSource { + /// Total length in bytes. + fn len(&self) -> u64; + /// Fill `buf` entirely with bytes starting at `offset`; fail with `UnexpectedEof` if past the end. + fn read_at(&self, offset: u64, buf: &mut [u8]) -> io::Result<()>; + /// True if the source holds no bytes. + fn is_empty(&self) -> bool { + self.len() == 0 + } +} + +fn eof() -> io::Error { + io::Error::new(io::ErrorKind::UnexpectedEof, "read past end of byte source") +} + +impl ByteSource for [u8] { + fn len(&self) -> u64 { + <[u8]>::len(self) as u64 + } + fn read_at(&self, offset: u64, buf: &mut [u8]) -> io::Result<()> { + let start = usize::try_from(offset).map_err(|_| eof())?; + let end = start.checked_add(buf.len()).ok_or_else(eof)?; + let src = self.get(start..end).ok_or_else(eof)?; + buf.copy_from_slice(src); + Ok(()) + } +} + +impl ByteSource for Vec { + fn len(&self) -> u64 { + self.as_slice().len() as u64 + } + fn read_at(&self, offset: u64, buf: &mut [u8]) -> io::Result<()> { + self.as_slice().read_at(offset, buf) + } +} + +impl ByteSource for &T { + fn len(&self) -> u64 { + (**self).len() + } + fn read_at(&self, offset: u64, buf: &mut [u8]) -> io::Result<()> { + (**self).read_at(offset, buf) + } +} + +impl ByteSource for Arc { + fn len(&self) -> u64 { + (**self).len() + } + fn read_at(&self, offset: u64, buf: &mut [u8]) -> io::Result<()> { + (**self).read_at(offset, buf) + } +} + +impl ByteSource for Box { + fn len(&self) -> u64 { + (**self).len() + } + fn read_at(&self, offset: u64, buf: &mut [u8]) -> io::Result<()> { + (**self).read_at(offset, buf) + } +} + +#[cfg(not(target_arch = "wasm32"))] +impl ByteSource for std::fs::File { + fn len(&self) -> u64 { + self.metadata().map(|m| m.len()).unwrap_or(0) + } + #[cfg(unix)] + fn read_at(&self, offset: u64, buf: &mut [u8]) -> io::Result<()> { + std::os::unix::fs::FileExt::read_exact_at(self, buf, offset) + } + #[cfg(windows)] + fn read_at(&self, mut offset: u64, mut buf: &mut [u8]) -> io::Result<()> { + use std::os::windows::fs::FileExt; + while !buf.is_empty() { + let n = self.seek_read(buf, offset)?; + if n == 0 { + return Err(eof()); + } + offset += n as u64; + buf = &mut buf[n..]; + } + Ok(()) + } + #[cfg(not(any(unix, windows)))] + fn read_at(&self, offset: u64, buf: &mut [u8]) -> io::Result<()> { + use std::io::{Read, Seek, SeekFrom}; + let mut f = self; + f.seek(SeekFrom::Start(offset))?; + f.read_exact(buf) + } +} + +#[cfg(test)] +mod tests { + use super::*; + + #[test] + fn slice_reads() { + let v = vec![1u8, 2, 3, 4]; + let mut b = [0u8; 2]; + v.read_at(1, &mut b).unwrap(); + assert_eq!(b, [2, 3]); + assert!(v.read_at(3, &mut b).is_err()); + assert!(v.read_at(u64::MAX, &mut b).is_err()); + let a: Arc<[u8]> = Arc::from(v.clone()); + a.read_at(2, &mut b).unwrap(); + assert_eq!(b, [3, 4]); + assert_eq!(ByteSource::len(&a), 4); + let s: &[u8] = &v; + assert_eq!(ByteSource::len(&s), 4); + } +} diff --git a/crates/matroska/Cargo.toml b/crates/matroska/Cargo.toml new file mode 100644 index 0000000..258a21a --- /dev/null +++ b/crates/matroska/Cargo.toml @@ -0,0 +1,12 @@ +[package] +name = "slidecraft-matroska" +description = "Clean-room Matroska (MKV) and WebM demuxer and muxer" +version.workspace = true +edition.workspace = true +license.workspace = true +rust-version.workspace = true + +[lints] +workspace = true + +[dependencies] diff --git a/crates/matroska/src/codec.rs b/crates/matroska/src/codec.rs new file mode 100644 index 0000000..e44f47e --- /dev/null +++ b/crates/matroska/src/codec.rs @@ -0,0 +1,225 @@ +//! Matroska CodecID → codec mapping (Matroska codec specifications, RFC 9559 §5.1.4.1.21 and the +//! codec mappings registry). + +/// Codec of a track, derived from `CodecID` and `CodecPrivate`. +/// +/// Configuration records are passed through as the raw bytes found in `CodecPrivate`, which for +/// the ISO codecs are exactly the ISOBMFF configuration boxes' payloads (avcC, hvcC, av1C, ASC…). +#[derive(Clone, Debug, PartialEq)] +pub enum Codec { + /// `V_MPEG4/ISO/AVC`; payload is an `AVCDecoderConfigurationRecord` (avcC). + Avc { avcc: Vec }, + /// `V_MPEGH/ISO/HEVC`; payload is an `HEVCDecoderConfigurationRecord` (hvcC). + Hevc { hvcc: Vec }, + /// `V_VP8`. + Vp8, + /// `V_VP9`; `CodecPrivate` (optional) holds VP9 codec feature metadata. + Vp9 { private: Vec }, + /// `V_AV1`; payload is an `AV1CodecConfigurationRecord` (av1C). + Av1 { av1c: Vec }, + /// `V_PRORES`; `fourcc` from `CodecPrivate` (e.g. `apcn`), if present. + ProRes { fourcc: Option<[u8; 4]> }, + /// `V_MJPEG`, or `V_MS/VFW/FOURCC` with an `MJPG` compression FourCC. + Mjpeg, + /// `V_MS/VFW/FOURCC` with any other FourCC; `bitmap_info_header` is the whole `CodecPrivate`. + VfwFourcc { fourcc: [u8; 4], bitmap_info_header: Vec }, + /// `A_AAC` (and the legacy `A_AAC/MPEG{2,4}/` IDs); `asc` is the + /// `AudioSpecificConfig` (synthesised for legacy IDs without `CodecPrivate`). + Aac { asc: Vec }, + /// `A_OPUS`; `head` is the `OpusHead` identification header. + Opus { head: Vec }, + /// `A_VORBIS`; the three Vorbis headers (identification, comment, setup), split from the + /// Xiph-laced `CodecPrivate`. + Vorbis { headers: Vec> }, + /// `A_FLAC`; `private` is `fLaC` + metadata blocks (STREAMINFO first). + Flac { private: Vec }, + /// `A_PCM/INT/LIT`, `A_PCM/INT/BIG`, `A_PCM/FLOAT/IEEE`. Bit depth comes from the Audio element. + Pcm { float: bool, big_endian: bool, bits: u16 }, + /// `A_AC3`. + Ac3, + /// `A_EAC3`. + Eac3, + /// `A_MPEG/L3`. + Mp3, + /// `A_MPEG/L2`. + Mp2, + /// `S_TEXT/UTF8` (SubRip-style plain text). + SubRip, + /// `S_TEXT/WEBVTT`, or WebM's `D_WEBVTT/{SUBTITLES,CAPTIONS,DESCRIPTIONS,METADATA}`. + WebVtt, + /// `S_TEXT/ASS` / `S_TEXT/SSA`; `header` is the script header from `CodecPrivate`. + Ass { header: Vec, ssa: bool }, + /// Any other CodecID (kept verbatim along with `Track::codec_private`). + Other(String), +} + +impl Codec { + /// Short codec name, matching FFmpeg's `codec_name` where one exists (useful for logs/UI). + pub fn name(&self) -> String { + match self { + Codec::Avc { .. } => "h264".into(), + Codec::Hevc { .. } => "hevc".into(), + Codec::Vp8 => "vp8".into(), + Codec::Vp9 { .. } => "vp9".into(), + Codec::Av1 { .. } => "av1".into(), + Codec::ProRes { .. } => "prores".into(), + Codec::Mjpeg => "mjpeg".into(), + Codec::VfwFourcc { fourcc, .. } => String::from_utf8_lossy(fourcc).trim().to_ascii_lowercase(), + Codec::Aac { .. } => "aac".into(), + Codec::Opus { .. } => "opus".into(), + Codec::Vorbis { .. } => "vorbis".into(), + Codec::Flac { .. } => "flac".into(), + Codec::Pcm { float, big_endian, bits } => { + let k = if *float { + 'f' + } else if *bits == 8 { + 'u' + } else { + 's' + }; + if *bits == 8 { format!("pcm_{k}8") } else { format!("pcm_{k}{bits}{}", if *big_endian { "be" } else { "le" }) } + } + Codec::Ac3 => "ac3".into(), + Codec::Eac3 => "eac3".into(), + Codec::Mp3 => "mp3".into(), + Codec::Mp2 => "mp2".into(), + Codec::SubRip => "subrip".into(), + Codec::WebVtt => "webvtt".into(), + Codec::Ass { ssa, .. } => if *ssa { "ssa" } else { "ass" }.into(), + Codec::Other(id) => id.clone(), + } + } +} + +const AAC_RATES: [u32; 13] = [96000, 88200, 64000, 48000, 44100, 32000, 24000, 22050, 16000, 12000, 11025, 8000, 7350]; + +/// Build a 2-byte (or 5-byte with SBR extension) AudioSpecificConfig for legacy `A_AAC/...` IDs. +fn synth_asc(object_type: u8, rate: f64, out_rate: Option, channels: u64, sbr: bool) -> Vec { + let rate_index = |r: f64| AAC_RATES.iter().position(|&x| x as f64 == r).unwrap_or(4) as u8; + let ri = rate_index(rate); + let ch = channels.min(7) as u8; + let mut v = vec![(object_type << 3) | (ri >> 1), ((ri & 1) << 7) | (ch << 3)]; + if sbr { + // backward-compatible explicit signalling: sync 0x2b7, SBR object type 5, flag, ext rate index + let ext = rate_index(out_rate.unwrap_or(rate * 2.0)); + v.push(0x56); + v.push(0xE5); + v.push(0x80 | (ext << 3)); + } + v +} + +/// Split a Xiph-laced `CodecPrivate` (Vorbis/Theora headers). +fn split_xiph(p: &[u8]) -> Vec> { + let Some(&n) = p.first() else { return Vec::new() }; + let mut pos = 1; + let mut sizes = Vec::new(); + for _ in 0..n { + let mut s = 0usize; + loop { + let Some(&b) = p.get(pos) else { return Vec::new() }; + pos += 1; + s += b as usize; + if b != 255 { + break; + } + } + sizes.push(s); + } + let mut out = Vec::new(); + for s in sizes { + let Some(h) = p.get(pos..pos + s) else { return Vec::new() }; + out.push(h.to_vec()); + pos += s; + } + out.push(p[pos.min(p.len())..].to_vec()); + out +} + +/// Map a CodecID + CodecPrivate (+ audio params for legacy AAC / PCM) to a [`Codec`]. +pub(crate) fn map_codec(id: &str, private: &[u8], audio: Option<&crate::AudioInfo>) -> Codec { + let p = private.to_vec(); + match id { + "V_MPEG4/ISO/AVC" => Codec::Avc { avcc: p }, + "V_MPEGH/ISO/HEVC" => Codec::Hevc { hvcc: p }, + "V_VP8" => Codec::Vp8, + "V_VP9" => Codec::Vp9 { private: p }, + "V_AV1" => Codec::Av1 { av1c: p }, + "V_PRORES" => Codec::ProRes { fourcc: private.get(..4).and_then(|f| f.try_into().ok()) }, + "V_MJPEG" => Codec::Mjpeg, + "V_MS/VFW/FOURCC" => { + let fourcc: [u8; 4] = private.get(16..20).and_then(|f| f.try_into().ok()).unwrap_or(*b" "); + match &fourcc { + b"MJPG" | b"mjpg" | b"AVRn" | b"AVDJ" => Codec::Mjpeg, + b"H264" | b"h264" | b"avc1" | b"X264" | b"x264" => Codec::Avc { avcc: Vec::new() }, + b"apch" | b"apcn" | b"apcs" | b"apco" | b"ap4h" | b"ap4x" => Codec::ProRes { fourcc: Some(fourcc) }, + _ => Codec::VfwFourcc { fourcc, bitmap_info_header: p }, + } + } + "A_AAC" => Codec::Aac { asc: p }, + "A_OPUS" => Codec::Opus { head: p }, + "A_VORBIS" => Codec::Vorbis { headers: split_xiph(private) }, + "A_FLAC" => Codec::Flac { private: p }, + "A_PCM/INT/LIT" | "A_PCM/INT/BIG" | "A_PCM/FLOAT/IEEE" => { + let bits = audio.and_then(|a| a.bit_depth).unwrap_or(if id == "A_PCM/FLOAT/IEEE" { 32 } else { 16 }) as u16; + Codec::Pcm { float: id == "A_PCM/FLOAT/IEEE", big_endian: id == "A_PCM/INT/BIG", bits } + } + "A_AC3" | "A_AC3/BSID9" | "A_AC3/BSID10" => Codec::Ac3, + "A_EAC3" => Codec::Eac3, + "A_MPEG/L3" => Codec::Mp3, + "A_MPEG/L2" => Codec::Mp2, + "S_TEXT/UTF8" => Codec::SubRip, + "S_TEXT/WEBVTT" | "D_WEBVTT/SUBTITLES" | "D_WEBVTT/CAPTIONS" | "D_WEBVTT/DESCRIPTIONS" | "D_WEBVTT/METADATA" => Codec::WebVtt, + "S_TEXT/ASS" | "S_ASS" => Codec::Ass { header: p, ssa: false }, + "S_TEXT/SSA" | "S_SSA" => Codec::Ass { header: p, ssa: true }, + _ if id.starts_with("A_AAC/") => { + if !private.is_empty() { + return Codec::Aac { asc: p }; + } + let (rate, out_rate, ch) = audio.map_or((48000.0, None, 2), |a| (a.sampling_frequency, a.output_sampling_frequency, a.channels)); + let object_type = if id.ends_with("/MAIN") { + 1 + } else if id.ends_with("/SSR") { + 3 + } else if id.ends_with("/LTP") { + 4 + } else { + 2 + }; + let sbr = id.ends_with("/SBR"); + Codec::Aac { asc: synth_asc(object_type, rate, out_rate, ch, sbr) } + } + _ => Codec::Other(id.to_string()), + } +} + +#[cfg(test)] +mod tests { + use super::*; + + #[test] + fn mapping() { + assert_eq!(map_codec("V_MPEG4/ISO/AVC", &[1, 2], None), Codec::Avc { avcc: vec![1, 2] }); + assert_eq!(map_codec("V_PRORES", b"apcn", None), Codec::ProRes { fourcc: Some(*b"apcn") }); + let mut bih = vec![0u8; 40]; + bih[16..20].copy_from_slice(b"MJPG"); + assert_eq!(map_codec("V_MS/VFW/FOURCC", &bih, None), Codec::Mjpeg); + bih[16..20].copy_from_slice(b"FFV1"); + assert!(matches!(map_codec("V_MS/VFW/FOURCC", &bih, None), Codec::VfwFourcc { fourcc, .. } if &fourcc == b"FFV1")); + let a = crate::AudioInfo { sampling_frequency: 44100.0, channels: 2, bit_depth: Some(24), output_sampling_frequency: None }; + assert_eq!(map_codec("A_PCM/INT/LIT", &[], Some(&a)).name(), "pcm_s24le"); + assert_eq!(map_codec("A_PCM/FLOAT/IEEE", &[], Some(&a)), Codec::Pcm { float: true, big_endian: false, bits: 24 }); + // legacy AAC LC 44.1k stereo → 0x12 0x10 + assert_eq!(map_codec("A_AAC/MPEG4/LC", &[], Some(&a)), Codec::Aac { asc: vec![0x12, 0x10] }); + assert_eq!(map_codec("S_TEXT/UTF8", &[], None).name(), "subrip"); + assert_eq!(map_codec("X_WHATEVER", &[], None), Codec::Other("X_WHATEVER".into())); + } + + #[test] + fn xiph_split() { + let mut p = vec![2u8, 3, 2]; + p.extend_from_slice(&[1, 1, 1, 2, 2, 3, 3, 3, 3]); + let h = split_xiph(&p); + assert_eq!(h, vec![vec![1, 1, 1], vec![2, 2], vec![3, 3, 3, 3]]); + } +} diff --git a/crates/matroska/src/demux.rs b/crates/matroska/src/demux.rs new file mode 100644 index 0000000..7d5241b --- /dev/null +++ b/crates/matroska/src/demux.rs @@ -0,0 +1,1167 @@ +//! Matroska / WebM demuxer: EBML header, Segment walk, cluster scanning, packet reading, seeking. + +use std::collections::{HashSet, VecDeque}; + +use crate::ebml::{self, Children, Lacing, Size, parse_lacing, read_id, read_size}; +use crate::error::{Error, Result}; +use crate::ids::*; +use crate::meta::*; +use crate::source::{ByteSource, ReadSeekSource}; +use crate::track::{Sample, Track, TrackKind, gcd}; + +const WIN: usize = 64 * 1024; +/// Upper bound for level-1 metadata elements read into memory (Info, Tracks, Cues, Tags…). +const MAX_META: u64 = 256 << 20; + +/// Options for [`open_with`] / [`Demuxer::with_options`]. +#[derive(Clone, Debug)] +pub struct OpenOptions { + /// Scan every cluster at open to build per-track sample tables and keyframe indices + /// (reads only element and block headers, never frame payloads). Default `true`. + /// + /// With `false`, open stops at the first Cluster and uses the SeekHead to find Cues, Tags, + /// Chapters… placed after the clusters; [`Demuxer::seek`] then uses Cues (or builds the index + /// on first use if there are none). + pub index: bool, + /// Verify CRC-32 elements of level-1 elements (and clusters while indexing). Mismatches are + /// recorded in [`MkvFile::crc_errors`]; parsing continues. Default `false`. + pub verify_crc: bool, +} + +impl Default for OpenOptions { + fn default() -> Self { + Self { index: true, verify_crc: false } + } +} + +/// A parsed Matroska / WebM file (first Segment). +#[derive(Clone, Debug)] +pub struct MkvFile { + /// EBML `DocType`: `matroska` or `webm`. + pub doc_type: String, + pub doc_type_version: u64, + pub doc_type_read_version: u64, + /// Absolute offset of the Segment's data (base for SeekHead and Cues positions). + pub segment_data_start: u64, + /// Absolute end of the Segment (file end for unknown-size / live segments). + pub segment_end: u64, + /// Segment size field was "unknown" (live / streamed output). + pub live: bool, + pub info: SegmentInfo, + pub tracks: Vec, + pub seek_head: Vec, + /// Cue points, sorted by time. + pub cues: Vec, + /// Clusters in file order (complete only when [`MkvFile::indexed`]). + pub clusters: Vec, + pub chapters: Vec, + pub tags: Vec, + pub attachments: Vec, + /// Absolute offset of the first Cluster. + pub first_cluster: Option, + /// Sample tables have been built by scanning all clusters. + pub indexed: bool, + /// Number of CRC-32 elements verified (with `verify_crc`). + pub crc_checked: u32, + /// Absolute offsets of elements whose CRC-32 did not match. + pub crc_errors: Vec, + /// Non-fatal problems met while parsing (damage, resyncs, truncation). + pub warnings: Vec, +} + +/// One demuxed frame. +#[derive(Clone, Debug, PartialEq)] +pub struct Packet { + /// Index into [`MkvFile::tracks`]. + pub track: usize, + /// Matroska `TrackNumber`. + pub track_number: u64, + /// Presentation timestamp in timestamp ticks (the track timebase, [`Track::timebase`]). + pub pts: i64, + /// Presentation timestamp in nanoseconds. + pub pts_ns: i64, + /// Duration in ticks (0 if unknown). + pub duration: u64, + /// Duration in nanoseconds (0 if unknown). + pub duration_ns: u64, + pub keyframe: bool, + pub discardable: bool, + pub invisible: bool, + /// `DiscardPadding` (ns) from the enclosing BlockGroup. + pub discard_padding_ns: Option, + /// Absolute offset of the stored frame data. + pub offset: u64, + /// Index of the frame within a laced block (0 when not laced). Laced frames after the first + /// only have exact timestamps when the block or track carries a duration; otherwise they + /// repeat the block timestamp and `duration` is 0. + pub lace: u16, + /// Frame bytes with header stripping undone. Frames of tracks with other content encodings + /// (zlib, encryption) are returned as stored; see [`Track::frames_readable`]. + pub data: Vec, +} + +/// Result of [`Demuxer::seek`]. +#[derive(Clone, Copy, Debug, PartialEq, Eq)] +pub struct SeekPoint { + pub track: usize, + /// Keyframe timestamp in ticks and ns. + pub pts: i64, + pub pts_ns: i64, + /// Sample index of the keyframe when the file is indexed. + pub sample: Option, + pub cluster_offset: u64, + pub block_offset: u64, +} + +/// A keyframe index entry ([`Demuxer::keyframe_index`]). +#[derive(Clone, Copy, Debug, PartialEq, Eq)] +pub struct Keyframe { + pub sample: usize, + pub pts: i64, + pub pts_ns: i64, + pub block_offset: u64, +} + +// --------------------------------------------------------------------------------------------- +// I/O with a small read window +// --------------------------------------------------------------------------------------------- + +#[derive(Default, Clone, Debug)] +struct Cache { + buf: Vec, + start: u64, +} + +struct Io<'a, S: ByteSource + ?Sized> { + src: &'a S, + len: u64, + cache: &'a mut Cache, +} + +#[derive(Clone, Copy, Debug)] +struct Hdr { + id: u32, + pos: u64, + size: Size, + hlen: usize, +} + +impl Hdr { + fn data_start(&self) -> u64 { + self.pos + self.hlen as u64 + } + /// End offset, clamped to `limit`; unknown sizes run to `limit`. + fn end(&self, limit: u64) -> u64 { + match self.size { + Size::Known(n) => self.data_start().saturating_add(n).min(limit), + Size::Unknown => limit, + } + } + fn overruns(&self, limit: u64) -> bool { + matches!(self.size, Size::Known(n) if self.data_start().saturating_add(n) > limit) + } +} + +impl<'a, S: ByteSource + ?Sized> Io<'a, S> { + fn new(src: &'a S, cache: &'a mut Cache) -> Self { + let len = src.len(); + Self { src, len, cache } + } + + /// Up to `n` (≤ WIN) bytes at `pos` (fewer at EOF). + fn peek(&mut self, pos: u64, n: usize) -> Result<&[u8]> { + let n = n.min(WIN); + if pos >= self.len { + return Ok(&[]); + } + let want_end = (pos + n as u64).min(self.len); + let c = &*self.cache; + if !(pos >= c.start && want_end <= c.start + c.buf.len() as u64) { + let fill = ((self.len - pos) as usize).min(WIN); + let mut buf = std::mem::take(&mut self.cache.buf); + buf.resize(fill, 0); + self.src.read_at(pos, &mut buf)?; + self.cache.buf = buf; + self.cache.start = pos; + } + let o = (pos - self.cache.start) as usize; + Ok(&self.cache.buf[o..o + (want_end - pos) as usize]) + } + + /// Exactly `n` bytes at `pos`. + fn read_vec(&mut self, pos: u64, n: u64) -> Result> { + if pos.checked_add(n).is_none_or(|e| e > self.len) { + return Err(Error::Io(std::io::Error::new(std::io::ErrorKind::UnexpectedEof, "read past end of file"))); + } + if n as usize <= WIN / 2 { + return Ok(self.peek(pos, n as usize)?.to_vec()); + } + let mut v = vec![0u8; n as usize]; + self.src.read_at(pos, &mut v)?; + Ok(v) + } + + /// Element header at `pos` (`None` if undecodable or past `limit`). + fn header(&mut self, pos: u64, limit: u64) -> Result> { + if pos >= limit { + return Ok(None); + } + let b = self.peek(pos, 12)?; + let Some((id, il)) = read_id(b) else { return Ok(None) }; + let Some((size, sl)) = read_size(&b[il..]) else { return Ok(None) }; + let hlen = il + sl; + if pos + hlen as u64 > limit { + return Ok(None); + } + Ok(Some(Hdr { id, pos, size, hlen })) + } + + /// Scan forward from `from` for a plausible level-1 element (Cluster preferred), up to `limit`. + fn resync(&mut self, from: u64, limit: u64) -> Result> { + let mut pos = from; + while pos + 4 <= limit { + let chunk = self.peek(pos, WIN)?.to_vec(); + if chunk.len() < 4 { + return Ok(None); + } + for i in 0..chunk.len() - 3 { + if chunk[i] & 0xF0 != 0x10 { + continue; + } + let id = u32::from_be_bytes([chunk[i], chunk[i + 1], chunk[i + 2], chunk[i + 3]]); + if !is_top_level(id) || id == EBML || id == SEGMENT { + continue; + } + let p = pos + i as u64; + if self.plausible(p, id, limit)? { + return Ok(Some(p)); + } + } + pos += (chunk.len() - 3) as u64; + } + Ok(None) + } + + fn plausible(&mut self, p: u64, id: u32, limit: u64) -> Result { + let Some(h) = self.header(p, limit)? else { return Ok(false) }; + if h.id != id || h.overruns(limit) { + return Ok(false); + } + if h.size == Size::Unknown && id != CLUSTER { + return Ok(false); + } + // first child must decode and fit; clusters must start with Timestamp (after CRC-32/Void) + let mut cp = h.data_start(); + let end = h.end(limit); + for _ in 0..3 { + let Some(c) = self.header(cp, end)? else { return Ok(false) }; + if c.overruns(end) { + return Ok(false); + } + if id != CLUSTER { + return Ok(true); + } + match c.id { + TIMESTAMP => return Ok(true), + CRC32 | VOID => cp = c.end(end), + _ => return Ok(false), + } + } + Ok(false) + } + + /// End of an unknown-size master element: the first child that is a top-level ID or undecodable. + fn unknown_end(&mut self, data_start: u64, limit: u64) -> Result { + let mut pos = data_start; + while pos < limit { + let Some(h) = self.header(pos, limit)? else { return Ok(pos) }; + if is_top_level(h.id) || h.size == Size::Unknown { + return Ok(pos); + } + pos = h.end(limit); + } + Ok(limit) + } +} + +// --------------------------------------------------------------------------------------------- +// Blocks +// --------------------------------------------------------------------------------------------- + +#[derive(Clone, Debug)] +struct BlockRef { + track: usize, + block_offset: u64, + /// Timestamp in ticks (cluster + relative). + ts: i64, + /// Absolute (offset, size) of each frame. + frames: Vec<(u64, u32)>, + block_duration: Option, + keyframe: bool, + discardable: bool, + invisible: bool, + discard_padding: Option, +} + +#[derive(Clone, Copy, Debug)] +struct FrameRef { + track: usize, + offset: u64, + size: u32, + pts: i64, + pts_ns: i64, + duration: u64, + duration_ns: u64, + keyframe: bool, + discardable: bool, + invisible: bool, + discard_padding: Option, + block_offset: u64, + lace: u16, +} + +fn track_lookup(tracks: &[Track]) -> impl Fn(u64) -> Option + '_ { + move |n| tracks.iter().position(|t| t.number == n) +} + +/// Parse the Block/SimpleBlock header at `data_start` (`len` bytes). +/// Returns (track index, relative ts, flags, frames) or `None` for unknown tracks / damage. +fn parse_block( + io: &mut Io<'_, S>, + data_start: u64, + len: u64, + lookup: &dyn Fn(u64) -> Option, +) -> Result)>> { + let head = io.peek(data_start, 16.min(len as usize))?; + let Some((tn, tl, _)) = ebml::read_vint(head) else { return Ok(None) }; + if head.len() < tl + 3 { + return Ok(None); + } + let rel = i16::from_be_bytes([head[tl], head[tl + 1]]); + let flags = head[tl + 2]; + let Some(track) = lookup(tn) else { return Ok(None) }; + let hdr = (tl + 3) as u64; + let lacing = Lacing::from_flags(flags); + let frames = if lacing == Lacing::None { + vec![(data_start + hdr, (len - hdr) as u32)] + } else { + let body = io.read_vec(data_start + hdr, len - hdr)?; + match parse_lacing(lacing, &body) { + Ok(f) => f.into_iter().map(|(o, l)| (data_start + hdr + o as u64, l as u32)).collect(), + Err(_) => return Ok(None), + } + }; + Ok(Some((track, rel, flags, frames))) +} + +/// Parse one SimpleBlock or BlockGroup element. +fn parse_block_elem( + io: &mut Io<'_, S>, + h: &Hdr, + end: u64, + cluster_ts: i64, + lookup: &dyn Fn(u64) -> Option, +) -> Result> { + let ds = h.data_start(); + match h.id { + SIMPLE_BLOCK => { + let Some((track, rel, flags, frames)) = parse_block(io, ds, end - ds, lookup)? else { return Ok(None) }; + Ok(Some(BlockRef { + track, + block_offset: h.pos, + ts: cluster_ts + rel as i64, + frames, + block_duration: None, + keyframe: flags & 0x80 != 0, + discardable: flags & 0x01 != 0, + invisible: flags & 0x08 != 0, + discard_padding: None, + })) + } + BLOCK_GROUP => { + let mut pos = ds; + let mut block = None; + let (mut dur, mut has_ref, mut pad) = (None, false, None); + while pos < end { + let Some(c) = io.header(pos, end)? else { break }; + let ce = c.end(end); + match c.id { + BLOCK => block = parse_block(io, c.data_start(), ce - c.data_start(), lookup)?, + BLOCK_DURATION => dur = Some(ebml::uint(io.peek(c.data_start(), (ce - c.data_start()) as usize)?)), + REFERENCE_BLOCK => has_ref = true, + DISCARD_PADDING => pad = Some(ebml::int(io.peek(c.data_start(), (ce - c.data_start()) as usize)?)), + _ => {} + } + pos = ce; + } + let Some((track, rel, flags, frames)) = block else { return Ok(None) }; + Ok(Some(BlockRef { + track, + block_offset: h.pos, + ts: cluster_ts + rel as i64, + frames, + block_duration: dur, + keyframe: !has_ref, + discardable: false, + invisible: flags & 0x08 != 0, + discard_padding: pad, + })) + } + _ => Ok(None), + } +} + +/// Expand a block into frames with per-lace timestamps. +fn expand(b: &BlockRef, t: &Track, scale: u64, skip: u16, out: &mut impl Extend) { + let n = b.frames.len() as u64; + // total block duration in ns + let block_ns: Option = match (b.block_duration, t.default_duration_ns) { + (Some(d), _) => Some(d as u128 * scale as u128), + (None, Some(dd)) => Some(dd as u128 * n as u128), + _ => None, + }; + let scale_i = scale as i128; + // round half away from zero + let to_ticks = |ns: i128| -> i64 { (if ns >= 0 { (ns + scale_i / 2) / scale_i } else { (ns - scale_i / 2) / scale_i }) as i64 }; + // CodecDelay is subtracted from every timestamp (RFC 9559 §5.1.4.1.25); in ticks it is rounded + // once, so tick timestamps stay on the block grid. + let delay_ns = t.codec_delay_ns as i128; + let delay_ticks = to_ticks(delay_ns); + let base_ns = b.ts as i128 * scale_i; + out.extend(b.frames.iter().enumerate().skip(skip as usize).map(|(i, &(offset, size))| { + let (start, end) = match block_ns { + Some(total) => ((total * i as u128 / n as u128) as i128, Some((total * (i as u128 + 1) / n as u128) as i128)), + None => (0, None), + }; + let pts_ns = base_ns + start - delay_ns; + let pts = to_ticks(base_ns + start) - delay_ticks; + let duration = end.map_or(0, |e| (to_ticks(e) - to_ticks(start)).max(0) as u64); + FrameRef { + track: b.track, + offset, + size, + pts, + pts_ns: pts_ns as i64, + duration, + duration_ns: end.map_or(0, |e| (e - start).max(0) as u64), + keyframe: b.keyframe, + discardable: b.discardable, + invisible: b.invisible, + discard_padding: b.discard_padding, + block_offset: b.block_offset, + lace: i as u16, + } + })); +} + +/// Scan a whole cluster (headers only), returning its info and blocks. +fn scan_cluster( + io: &mut Io<'_, S>, + h: &Hdr, + limit: u64, + tracks: &[Track], + warnings: &mut Vec, +) -> Result<(ClusterInfo, Vec)> { + let lookup = track_lookup(tracks); + let ds = h.data_start(); + let known_end = match h.size { + Size::Known(_) => Some(h.end(limit)), + Size::Unknown => None, + }; + let lim = known_end.unwrap_or(limit); + let mut ts: i64 = 0; + let mut blocks = Vec::new(); + let mut pos = ds; + let end = loop { + if pos >= lim { + break lim; + } + let Some(c) = io.header(pos, lim)? else { + warnings.push(format!("damaged cluster at {}: undecodable element at {pos}", h.pos)); + break io.resync(pos + 1, limit)?.unwrap_or(limit); + }; + if known_end.is_none() && is_top_level(c.id) { + break pos; + } + if c.size == Size::Unknown || c.overruns(lim) { + warnings.push(format!("damaged cluster at {}: element 0x{:X} at {pos} overruns", h.pos, c.id)); + if c.overruns(lim) && matches!(c.id, SIMPLE_BLOCK | BLOCK_GROUP) && io.len <= c.data_start() + c.size.known().unwrap_or(0) { + break lim; // truncated file: drop the partial block + } + break io.resync(pos + 1, limit)?.unwrap_or(limit); + } + let ce = c.end(lim); + match c.id { + TIMESTAMP => ts = ebml::uint(io.peek(c.data_start(), (ce - c.data_start()) as usize)?) as i64, + SIMPLE_BLOCK | BLOCK_GROUP => { + if let Some(b) = parse_block_elem(io, &c, ce, ts, &lookup)? { + blocks.push(b); + } + } + _ => {} + } + pos = ce; + }; + Ok((ClusterInfo { offset: h.pos, data_start: ds, end, timestamp: ts.max(0) as u64 }, blocks)) +} + +// --------------------------------------------------------------------------------------------- +// Open +// --------------------------------------------------------------------------------------------- + +/// Open a Matroska/WebM file with default options (full sample index). +pub fn open(src: S) -> Result { + open_with(src, &OpenOptions::default()) +} + +/// Open with explicit options. +pub fn open_with(src: S, opts: &OpenOptions) -> Result { + let mut cache = Cache::default(); + open_impl(&src, opts, &mut cache) +} + +fn open_impl(src: &S, opts: &OpenOptions, cache: &mut Cache) -> Result { + let mut io = Io::new(src, cache); + let len = io.len; + // EBML header (tolerate leading junk in the first 64 KiB) + let start = { + let b = io.peek(0, WIN)?; + let magic = [0x1A, 0x45, 0xDF, 0xA3]; + b.windows(4).position(|w| w == magic).ok_or_else(|| Error::NotMatroska("no EBML header".into()))? as u64 + }; + let eh = io.header(start, len)?.ok_or_else(|| Error::NotMatroska("bad EBML header".into()))?; + let hdr_end = eh.end(len); + let hdr = io.read_vec(eh.data_start(), hdr_end - eh.data_start())?; + let (mut doc_type, mut dtv, mut dtrv) = ("matroska".to_string(), 1, 1); + for e in Children::new(&hdr) { + match e.id { + DOC_TYPE => doc_type = ebml::string(e.data), + DOC_TYPE_VERSION => dtv = ebml::uint(e.data), + DOC_TYPE_READ_VERSION => dtrv = ebml::uint(e.data), + EBML_MAX_ID_LENGTH if ebml::uint(e.data) > 4 => return Err(Error::Unsupported("EBMLMaxIDLength > 4".into())), + EBML_MAX_SIZE_LENGTH if ebml::uint(e.data) > 8 => return Err(Error::Unsupported("EBMLMaxSizeLength > 8".into())), + _ => {} + } + } + if doc_type != "matroska" && doc_type != "webm" { + return Err(Error::NotMatroska(format!("DocType {doc_type:?}"))); + } + // Segment + let mut pos = hdr_end; + let seg = loop { + let h = io.header(pos, len)?.ok_or_else(|| Error::Invalid("no Segment".into()))?; + if h.id == SEGMENT { + break h; + } + if h.size == Size::Unknown { + return Err(Error::Invalid("unknown-size element before Segment".into())); + } + pos = h.end(len); + }; + let mut file = MkvFile { + doc_type, + doc_type_version: dtv, + doc_type_read_version: dtrv, + segment_data_start: seg.data_start(), + segment_end: seg.end(len), + live: seg.size == Size::Unknown, + info: SegmentInfo::default(), + tracks: Vec::new(), + seek_head: Vec::new(), + cues: Vec::new(), + clusters: Vec::new(), + chapters: Vec::new(), + tags: Vec::new(), + attachments: Vec::new(), + first_cluster: None, + indexed: false, + crc_checked: 0, + crc_errors: Vec::new(), + warnings: Vec::new(), + }; + if seg.overruns(len) { + file.warnings.push("Segment extends past end of file (truncated)".into()); + } + let mut w = Walker { opts, parsed: HashSet::new(), blocks_seen: false }; + w.walk(&mut io, &mut file)?; + finish_tracks(&mut file); + Ok(file) +} + +struct Walker<'o> { + opts: &'o OpenOptions, + parsed: HashSet, + blocks_seen: bool, +} + +impl Walker<'_> { + fn walk(&mut self, io: &mut Io<'_, S>, file: &mut MkvFile) -> Result<()> { + let seg_end = file.segment_end; + let mut pos = file.segment_data_start; + while pos < seg_end { + let Some(h) = io.header(pos, seg_end)? else { + match io.resync(pos + 1, seg_end)? { + Some(p) => { + file.warnings.push(format!("undecodable element at {pos}; resynced at {p}")); + pos = p; + continue; + } + None => break, + } + }; + match h.id { + CLUSTER => { + if file.first_cluster.is_none() { + file.first_cluster = Some(h.pos); + // make sure Info/Tracks are known before interpreting blocks + self.follow_seek_head(io, file, &[INFO, TRACKS])?; + if !self.opts.index { + break; + } + } + let (ci, blocks) = scan_cluster(io, &h, seg_end, &file.tracks, &mut file.warnings)?; + if self.opts.verify_crc && h.size != Size::Unknown { + let data = io.read_vec(h.data_start(), ci.end - h.data_start())?; + self.check_crc(file, h.pos, &data); + } + add_blocks(file, file.clusters.len() as u32, &blocks); + self.blocks_seen = true; + file.clusters.push(ci); + pos = ci.end.max(h.data_start()); + } + SEEK_HEAD | INFO | TRACKS | CUES | CHAPTERS | TAGS | ATTACHMENTS => { + pos = self.level1(io, file, &h, seg_end)?; + } + EBML | SEGMENT => break, // chained segment: only the first one is read + _ => { + if h.size == Size::Unknown { + pos = io.unknown_end(h.data_start(), seg_end)?; + } else if h.overruns(seg_end) && h.id != VOID { + match io.resync(pos + 1, seg_end)? { + Some(p) => { + file.warnings.push(format!("oversized element 0x{:X} at {pos}; resynced at {p}", h.id)); + pos = p; + } + None => break, + } + } else { + pos = h.end(seg_end); + } + } + } + } + self.follow_seek_head(io, file, &[INFO, TRACKS, CUES, CHAPTERS, TAGS, ATTACHMENTS, SEEK_HEAD])?; + // follow a second-level SeekHead reached through the first one + self.follow_seek_head(io, file, &[CUES, CHAPTERS, TAGS, ATTACHMENTS])?; + file.cues.sort_by_key(|c| c.time); + file.indexed = self.opts.index; + Ok(()) + } + + fn follow_seek_head(&mut self, io: &mut Io<'_, S>, file: &mut MkvFile, ids: &[u32]) -> Result<()> { + let entries = file.seek_head.clone(); + for e in entries { + if !ids.contains(&e.id) { + continue; + } + let Some(abs) = file.segment_data_start.checked_add(e.position) else { continue }; + if self.parsed.contains(&abs) || abs >= file.segment_end { + continue; + } + match io.header(abs, file.segment_end)? { + Some(h) if h.id == e.id => { + self.level1(io, file, &h, file.segment_end)?; + } + _ => file.warnings.push(format!("SeekHead entry 0x{:X} -> {abs} does not point at that element", e.id)), + } + } + Ok(()) + } + + fn check_crc(&self, file: &mut MkvFile, pos: u64, data: &[u8]) { + if let Some(ok) = ebml::verify_crc(data) { + file.crc_checked += 1; + if !ok { + file.crc_errors.push(pos); + file.warnings.push(format!("CRC-32 mismatch in element at {pos}")); + } + } + } + + /// Parse a level-1 metadata element; returns its end. + fn level1(&mut self, io: &mut Io<'_, S>, file: &mut MkvFile, h: &Hdr, limit: u64) -> Result { + let end = if h.size == Size::Unknown { io.unknown_end(h.data_start(), limit)? } else { h.end(limit) }; + if h.overruns(limit) { + file.warnings.push(format!("element 0x{:X} at {} truncated", h.id, h.pos)); + } + if !self.parsed.insert(h.pos) { + return Ok(end); + } + if h.id == ATTACHMENTS { + self.attachments(io, file, h.data_start(), end)?; + return Ok(end); + } + let n = end - h.data_start(); + if n > MAX_META { + file.warnings.push(format!("element 0x{:X} at {} too large ({n} bytes); skipped", h.id, h.pos)); + return Ok(end); + } + let data = io.read_vec(h.data_start(), n)?; + if self.opts.verify_crc { + self.check_crc(file, h.pos, &data); + } + match h.id { + SEEK_HEAD => file.seek_head.extend(parse_seekhead(&data)), + INFO => file.info = parse_info(&data), + TRACKS => { + if file.tracks.is_empty() { + file.tracks = parse_tracks(&data, file.info.timestamp_scale); + } + } + CUES => parse_cues(&data, &mut file.cues), + CHAPTERS => parse_chapters(&data, &mut file.chapters), + TAGS => parse_tags(&data, &mut file.tags), + _ => {} + } + Ok(end) + } + + fn attachments(&mut self, io: &mut Io<'_, S>, file: &mut MkvFile, start: u64, end: u64) -> Result<()> { + let mut pos = start; + while pos < end { + let Some(h) = io.header(pos, end)? else { break }; + let he = h.end(end); + if h.id == ATTACHED_FILE { + let mut a = Attachment::default(); + let mut p = h.data_start(); + while p < he { + let Some(c) = io.header(p, he)? else { break }; + let ce = c.end(he); + let n = ce - c.data_start(); + if c.id == FILE_DATA { + a.data_offset = c.data_start(); + a.data_size = n; + } else if n <= 4096 { + let d = io.peek(c.data_start(), n as usize)?; + match c.id { + FILE_NAME => a.name = ebml::string(d), + FILE_MEDIA_TYPE => a.media_type = ebml::string(d), + FILE_DESCRIPTION => a.description = Some(ebml::string(d)), + FILE_UID => a.uid = ebml::uint(d), + _ => {} + } + } + p = ce; + } + file.attachments.push(a); + } + pos = he; + } + Ok(()) + } +} + +fn add_blocks(file: &mut MkvFile, cluster: u32, blocks: &[BlockRef]) { + let scale = file.info.timestamp_scale; + let mut frames: Vec = Vec::new(); + for b in blocks { + frames.clear(); + let t = &file.tracks[b.track]; + expand(b, t, scale, 0, &mut frames); + let t = &mut file.tracks[b.track]; + for f in &frames { + if f.keyframe { + t.sync.push(t.samples.len() as u32); + } + t.samples.push(Sample { + offset: f.offset, + size: f.size, + pts: f.pts, + duration: f.duration, + keyframe: f.keyframe, + cluster, + block_offset: f.block_offset, + lace: f.lace, + }); + } + } +} + +fn finish_tracks(file: &mut MkvFile) { + let scale = file.info.timestamp_scale; + let g = gcd(scale, 1_000_000_000); + for t in &mut file.tracks { + t.timebase = (scale / g, 1_000_000_000 / g); + } +} + +// --------------------------------------------------------------------------------------------- +// MkvFile helpers +// --------------------------------------------------------------------------------------------- + +impl MkvFile { + /// True for WebM (`DocType` = `webm`). + pub fn is_webm(&self) -> bool { + self.doc_type == "webm" + } + + /// Segment duration in ns: `Info/Duration` if present, else the end of the last indexed sample. + pub fn duration_ns(&self) -> Option { + if let Some(d) = self.info.duration { + return Some((d * self.info.timestamp_scale as f64).round() as u64); + } + let end = self.tracks.iter().flat_map(|t| t.samples.iter().map(|s| s.pts + s.duration as i64)).max()?; + Some((end.max(0) as u64).saturating_mul(self.info.timestamp_scale)) + } + + /// Index of the track with this `TrackNumber`. + pub fn track_by_number(&self, number: u64) -> Option { + self.tracks.iter().position(|t| t.number == number) + } + + /// Index of the first track of the given kind (preferring the default flag). + pub fn track_of_kind(&self, kind: TrackKind) -> Option { + let mut it = self.tracks.iter().enumerate().filter(|(_, t)| t.kind == kind); + let first = it.clone().next().map(|x| x.0); + it.find(|(_, t)| t.default).map(|x| x.0).or(first) + } + + /// Read sample `index` of `track` (header stripping undone). + pub fn read_sample(&self, src: &S, track: usize, index: usize) -> Result> { + let t = self.tracks.get(track).ok_or(Error::NoSuchTrack(track))?; + let s = t.samples.get(index).ok_or(Error::NoSuchSample(index))?; + if !t.frames_readable() { + return Err(Error::Unsupported(format!("content encoding of track {} (compression/encryption)", t.number))); + } + let mut data = t.frame_prefix(s.size as u64); + let at = data.len(); + data.resize(at + s.size as usize, 0); + src.read_at(s.offset, &mut data[at..])?; + Ok(data) + } + + /// Keyframe sample of `track` with the greatest pts ≤ `time_ns` (indexed files only). + pub fn keyframe_before(&self, track: usize, time_ns: i64) -> Option { + let t = self.tracks.get(track)?; + t.keyframe_before_pts(t.ns_to_ticks(time_ns)) + } +} + +// --------------------------------------------------------------------------------------------- +// Demuxer (streaming packets + seeking) +// --------------------------------------------------------------------------------------------- + +#[derive(Clone, Copy, Debug)] +struct InCluster { + ts: i64, + end: Option, +} + +/// Packet reader over a [`ByteSource`]: iterates frames in file order and seeks to keyframes. +pub struct Demuxer { + src: S, + file: MkvFile, + opts: OpenOptions, + cache: Cache, + pos: u64, + cluster: Option, + pending: VecDeque, + skip_laces: Option<(u64, u16)>, + done: bool, +} + +impl Demuxer> { + /// Open from any `Read + Seek` stream. + pub fn from_reader(r: R) -> Result { + Self::new(ReadSeekSource::new(r)?) + } +} + +impl<'a> Demuxer<&'a [u8]> { + /// Open from an in-memory byte slice. + pub fn from_slice(data: &'a [u8]) -> Result { + Self::new(data) + } +} + +impl Demuxer { + /// Open with default options (full index). + pub fn new(src: S) -> Result { + Self::with_options(src, OpenOptions::default()) + } + + pub fn with_options(src: S, opts: OpenOptions) -> Result { + let mut cache = Cache::default(); + let file = open_impl(&src, &opts, &mut cache)?; + let pos = file.first_cluster.unwrap_or(file.segment_end); + Ok(Self { src, file, opts, cache, pos, cluster: None, pending: VecDeque::new(), skip_laces: None, done: false }) + } + + pub fn file(&self) -> &MkvFile { + &self.file + } + pub fn tracks(&self) -> &[Track] { + &self.file.tracks + } + pub fn source(&self) -> &S { + &self.src + } + pub fn into_parts(self) -> (S, MkvFile) { + (self.src, self.file) + } + /// Segment duration in ns. + pub fn duration_ns(&self) -> Option { + self.file.duration_ns() + } + + /// Scan all clusters and build sample tables (no-op if already indexed). + pub fn build_index(&mut self) -> Result<()> { + if self.file.indexed { + return Ok(()); + } + let opts = OpenOptions { index: true, verify_crc: self.opts.verify_crc }; + self.file = open_impl(&self.src, &opts, &mut self.cache)?; + Ok(()) + } + + /// Keyframes of `track` (builds the index if needed). + pub fn keyframe_index(&mut self, track: usize) -> Result> { + self.build_index()?; + let t = self.file.tracks.get(track).ok_or(Error::NoSuchTrack(track))?; + Ok(t.keyframes() + .iter() + .map(|&k| { + let s = &t.samples[k as usize]; + Keyframe { sample: k as usize, pts: s.pts, pts_ns: t.ticks_to_ns(s.pts), block_offset: s.block_offset } + }) + .collect()) + } + + /// Rewind to the first cluster. + pub fn rewind(&mut self) { + self.pos = self.file.first_cluster.unwrap_or(self.file.segment_end); + self.cluster = None; + self.pending.clear(); + self.skip_laces = None; + self.done = false; + } + + /// Read sample `index` of `track` (requires an index). + pub fn read_sample(&self, track: usize, index: usize) -> Result> { + self.file.read_sample(&self.src, track, index) + } + + /// Position the packet stream at the keyframe of `track` with the greatest pts ≤ `time_ns` + /// (or the track's first keyframe if none precedes it). The next [`Demuxer::next_packet`] + /// call returns packets starting at that keyframe's block (all tracks). + /// + /// Uses the sample index when present, else Cues (refined by scanning the cued cluster), else + /// builds the index first. + pub fn seek(&mut self, track: usize, time_ns: i64) -> Result { + let t = self.file.tracks.get(track).ok_or(Error::NoSuchTrack(track))?; + let ticks = t.ns_to_ticks(time_ns); + if !self.file.indexed { + let tn = t.number; + let cue = self + .file + .cues + .iter() + .filter(|c| c.time as i64 <= ticks) + .filter_map(|c| c.positions.iter().find(|p| p.track == tn).map(|p| (c.time, *p))) + .next_back() + .or_else(|| self.file.cues.iter().find_map(|c| c.positions.iter().find(|p| p.track == tn).map(|p| (c.time, *p)))); + if let Some((_, cp)) = cue + && let Some(sp) = self.seek_via_cue(track, ticks, cp.cluster_position)? + { + return Ok(sp); + } + self.build_index()?; + } + let t = &self.file.tracks[track]; + let k = t.keyframe_before_pts(ticks).ok_or_else(|| Error::Invalid(format!("track {} has no keyframes", t.number)))?; + let s = t.samples[k]; + let c = self.file.clusters[s.cluster as usize]; + let sp = + SeekPoint { track, pts: s.pts, pts_ns: t.ticks_to_ns(s.pts), sample: Some(k), cluster_offset: c.offset, block_offset: s.block_offset }; + self.position_at(c, s.block_offset, s.lace); + Ok(sp) + } + + fn position_at(&mut self, c: ClusterInfo, block_offset: u64, lace: u16) { + self.pending.clear(); + self.done = false; + self.pos = block_offset; + let known = c.end; + self.cluster = Some(InCluster { ts: c.timestamp as i64, end: Some(known) }); + self.skip_laces = (lace > 0).then_some((block_offset, lace)); + } + + fn seek_via_cue(&mut self, track: usize, ticks: i64, cluster_position: u64) -> Result> { + let abs = self.file.segment_data_start + cluster_position; + let mut io = Io::new(&self.src, &mut self.cache); + let Some(h) = io.header(abs, self.file.segment_end)? else { return Ok(None) }; + if h.id != CLUSTER { + self.file.warnings.push(format!("cue points at {abs}, which is not a Cluster")); + return Ok(None); + } + let mut warnings = Vec::new(); + let (ci, blocks) = scan_cluster(&mut io, &h, self.file.segment_end, &self.file.tracks, &mut warnings)?; + let t = &self.file.tracks[track]; + let mut frames = Vec::new(); + for b in blocks.iter().filter(|b| b.track == track && b.keyframe) { + expand(b, t, self.file.info.timestamp_scale, 0, &mut frames); + } + let best = frames.iter().filter(|f| f.pts <= ticks).max_by_key(|f| f.pts).or_else(|| frames.iter().min_by_key(|f| f.pts)).copied(); + let Some(f) = best else { return Ok(None) }; + let sp = SeekPoint { track, pts: f.pts, pts_ns: f.pts_ns, sample: None, cluster_offset: ci.offset, block_offset: f.block_offset }; + self.position_at(ci, f.block_offset, f.lace); + Ok(Some(sp)) + } + + /// Next frame in file order, or `None` at the end of the Segment. + pub fn next_packet(&mut self) -> Result> { + loop { + if let Some(f) = self.pending.pop_front() { + return self.materialize(f).map(Some); + } + if self.done || !self.fill()? { + self.done = true; + return Ok(None); + } + } + } + + fn materialize(&mut self, f: FrameRef) -> Result { + let t = &self.file.tracks[f.track]; + let mut data = if t.frames_readable() { t.frame_prefix(f.size as u64) } else { Vec::new() }; + let at = data.len(); + data.resize(at + f.size as usize, 0); + let mut io = Io::new(&self.src, &mut self.cache); + if (f.size as usize) <= WIN / 2 { + let b = io.peek(f.offset, f.size as usize)?; + if b.len() != f.size as usize { + return Err(Error::Invalid("frame data past end of file".into())); + } + data[at..].copy_from_slice(b); + } else { + self.src.read_at(f.offset, &mut data[at..])?; + } + Ok(Packet { + track: f.track, + track_number: t.number, + pts: f.pts, + pts_ns: f.pts_ns, + duration: f.duration, + duration_ns: f.duration_ns, + keyframe: f.keyframe, + discardable: f.discardable, + invisible: f.invisible, + discard_padding_ns: f.discard_padding, + offset: f.offset, + lace: f.lace, + data, + }) + } + + /// Advance over elements until at least one frame is pending. Returns false at the end. + fn fill(&mut self) -> Result { + let seg_end = self.file.segment_end; + let scale = self.file.info.timestamp_scale; + let mut io = Io::new(&self.src, &mut self.cache); + let lookup = track_lookup(&self.file.tracks); + loop { + if let Some(c) = self.cluster { + let lim = c.end.unwrap_or(seg_end); + if self.pos >= lim { + self.cluster = None; + continue; + } + let Some(h) = io.header(self.pos, lim)? else { + self.cluster = None; + match io.resync(self.pos + 1, seg_end)? { + Some(p) => { + self.pos = p; + continue; + } + None => return Ok(false), + } + }; + if c.end.is_none() && is_top_level(h.id) { + self.cluster = None; + continue; + } + if h.size == Size::Unknown || h.overruns(lim) { + self.cluster = None; + match io.resync(self.pos + 1, seg_end)? { + Some(p) => { + self.pos = p; + continue; + } + None => return Ok(false), + } + } + let he = h.end(lim); + self.pos = he; + match h.id { + TIMESTAMP => { + let v = ebml::uint(io.peek(h.data_start(), (he - h.data_start()) as usize)?) as i64; + self.cluster = Some(InCluster { ts: v, end: c.end }); + } + SIMPLE_BLOCK | BLOCK_GROUP => { + if let Some(b) = parse_block_elem(&mut io, &h, he, c.ts, &lookup)? { + let skip = match self.skip_laces.take() { + Some((off, n)) if off == h.pos => n, + _ => 0, + }; + expand(&b, &self.file.tracks[b.track], scale, skip, &mut self.pending); + if !self.pending.is_empty() { + return Ok(true); + } + } + } + _ => {} + } + continue; + } + if self.pos >= seg_end { + return Ok(false); + } + let Some(h) = io.header(self.pos, seg_end)? else { + match io.resync(self.pos + 1, seg_end)? { + Some(p) => { + self.pos = p; + continue; + } + None => return Ok(false), + } + }; + match h.id { + CLUSTER => { + self.cluster = Some(InCluster { ts: 0, end: h.size.known().map(|_| h.end(seg_end)) }); + self.pos = h.data_start(); + } + EBML | SEGMENT => return Ok(false), + _ => { + self.pos = if h.size == Size::Unknown { io.unknown_end(h.data_start(), seg_end)? } else { h.end(seg_end) }; + if self.pos <= h.pos { + return Ok(false); + } + } + } + } + } +} + +impl Iterator for Demuxer { + type Item = Result; + fn next(&mut self) -> Option> { + match self.next_packet() { + Ok(Some(p)) => Some(Ok(p)), + Ok(None) => None, + Err(e) => { + self.done = true; + Some(Err(e)) + } + } + } +} diff --git a/crates/matroska/src/ebml.rs b/crates/matroska/src/ebml.rs new file mode 100644 index 0000000..6c2db76 --- /dev/null +++ b/crates/matroska/src/ebml.rs @@ -0,0 +1,476 @@ +//! EBML primitives (RFC 8794): variable-length integers, element headers, typed values, CRC-32, +//! and Matroska block lacing (RFC 9559 §10.3). + +use crate::error::{Result, invalid}; + +/// Length in bytes of a VINT whose first byte is `b` (1..=8), or `None` for a zero first byte. +#[inline] +pub fn vint_len(b: u8) -> Option { + if b == 0 { None } else { Some(b.leading_zeros() as usize + 1) } +} + +/// Element data size: known, or "unknown" (all value bits set). +#[derive(Clone, Copy, Debug, PartialEq, Eq)] +pub enum Size { + Known(u64), + Unknown, +} + +impl Size { + pub fn known(self) -> Option { + match self { + Size::Known(n) => Some(n), + Size::Unknown => None, + } + } +} + +/// Decode an unsigned VINT (marker bit stripped). Returns `(value, length, all_ones)`. +pub fn read_vint(data: &[u8]) -> Option<(u64, usize, bool)> { + let first = *data.first()?; + let len = vint_len(first)?; + if data.len() < len { + return None; + } + let mask = if len == 8 { 0 } else { 0xFFu8 >> len }; + let mut v = (first & mask) as u64; + for &b in &data[1..len] { + v = (v << 8) | b as u64; + } + let all_ones = v == (1u64 << (7 * len)) - 1; + Some((v, len, all_ones)) +} + +/// Decode an element data size. All-ones is the reserved "unknown size". +pub fn read_size(data: &[u8]) -> Option<(Size, usize)> { + let (v, len, ones) = read_vint(data)?; + Some((if ones { Size::Unknown } else { Size::Known(v) }, len)) +} + +/// Decode an element ID (marker bits kept, 1..=4 bytes). Rejects all-ones value bits (reserved). +/// All-zero value bits are accepted, because Matroska's `ChapterDisplay` uses ID `0x80`. +pub fn read_id(data: &[u8]) -> Option<(u32, usize)> { + let first = *data.first()?; + let len = vint_len(first)?; + if len > 4 || data.len() < len { + return None; + } + let (v, _, ones) = read_vint(&data[..len])?; + if ones || (v == 0 && len > 1) { + return None; + } + let mut id = 0u32; + for &b in &data[..len] { + id = (id << 8) | b as u32; + } + Some((id, len)) +} + +/// Decode a signed VINT as used by EBML lacing (value − (2^(7·len−1) − 1)). +pub fn read_svint(data: &[u8]) -> Option<(i64, usize)> { + let (v, len, _) = read_vint(data)?; + let bias = (1i64 << (7 * len - 1)) - 1; + Some((v as i64 - bias, len)) +} + +/// Encode an element ID (already carrying its marker bits). +pub fn write_id(out: &mut Vec, id: u32) { + let n = if id >= 0x100_0000 { + 4 + } else if id >= 0x1_0000 { + 3 + } else if id >= 0x100 { + 2 + } else { + 1 + }; + out.extend_from_slice(&id.to_be_bytes()[4 - n..]); +} + +/// Encode a data size as a VINT using the shortest length (or `min_len` if larger). +pub fn write_size(out: &mut Vec, size: u64, min_len: usize) { + let mut len = 1; + while len < 8 && size >= (1u64 << (7 * len)) - 1 { + len += 1; + } + let len = len.max(min_len).min(8); + let v = size | (1u64 << (7 * len)); + out.extend_from_slice(&v.to_be_bytes()[8 - len..]); +} + +/// Encode the "unknown size" marker of the given length. +pub fn write_unknown_size(out: &mut Vec, len: usize) { + out.push(0xFFu8 >> (len - 1)); + out.extend(std::iter::repeat_n(0xFF, len - 1)); +} + +/// Big-endian unsigned integer of 0..=8 bytes. +pub fn uint(data: &[u8]) -> u64 { + data.iter().take(8).fold(0u64, |a, &b| (a << 8) | b as u64) +} + +/// Big-endian two's-complement signed integer of 0..=8 bytes. +pub fn int(data: &[u8]) -> i64 { + let n = data.len().min(8); + if n == 0 { + return 0; + } + let v = uint(&data[..n]); + let shift = 64 - 8 * n as u32; + ((v << shift) as i64) >> shift +} + +/// IEEE float of 0, 4 or 8 bytes. +pub fn float(data: &[u8]) -> f64 { + match data.len() { + 4 => f32::from_be_bytes(data.try_into().unwrap_or([0; 4])) as f64, + 8 => f64::from_be_bytes(data.try_into().unwrap_or([0; 8])), + _ => 0.0, + } +} + +/// String / UTF-8 value: bytes up to the first NUL, lossily decoded. +pub fn string(data: &[u8]) -> String { + let end = data.iter().position(|&b| b == 0).unwrap_or(data.len()); + String::from_utf8_lossy(&data[..end]).into_owned() +} + +/// One element inside an in-memory master element. +#[derive(Clone, Copy, Debug)] +pub struct Elem<'a> { + pub id: u32, + /// Offset of the element header, relative to the start of the parent's data. + pub offset: usize, + pub header_len: usize, + pub data: &'a [u8], + /// The size field was "unknown" (data runs to the end of the parent). + pub unknown_size: bool, + /// The declared size overran the parent and was truncated. + pub truncated: bool, +} + +/// Iterator over the children of an in-memory master element. +/// +/// Damaged data ends the iteration (see [`Children::damaged`]); oversize elements are truncated to +/// the parent's end. +pub struct Children<'a> { + data: &'a [u8], + pos: usize, + damaged: bool, +} + +impl<'a> Children<'a> { + pub fn new(data: &'a [u8]) -> Self { + Self { data, pos: 0, damaged: false } + } + /// True if iteration stopped on an undecodable header. + pub fn damaged(&self) -> bool { + self.damaged + } +} + +impl<'a> Iterator for Children<'a> { + type Item = Elem<'a>; + fn next(&mut self) -> Option> { + let rest = self.data.get(self.pos..)?; + if rest.is_empty() { + return None; + } + let Some((id, il)) = read_id(rest) else { + self.damaged = true; + self.pos = self.data.len(); + return None; + }; + let Some((size, sl)) = read_size(&rest[il..]) else { + self.damaged = true; + self.pos = self.data.len(); + return None; + }; + let hl = il + sl; + let avail = (rest.len() - hl) as u64; + let (len, unknown, truncated) = match size { + Size::Known(n) if n <= avail => (n as usize, false, false), + Size::Known(_) => (avail as usize, false, true), + Size::Unknown => (avail as usize, true, false), + }; + let e = Elem { id, offset: self.pos, header_len: hl, data: &rest[hl..hl + len], unknown_size: unknown, truncated }; + self.pos += hl + len; + Some(e) + } +} + +/// CRC-32 (ISO 3309 / IEEE 802.3, reflected polynomial 0xEDB88320), as used by EBML CRC-32 elements. +pub fn crc32(data: &[u8]) -> u32 { + crc32_update(0xFFFF_FFFF, data) ^ 0xFFFF_FFFF +} + +/// Incremental form: start with `0xFFFF_FFFF`, finish with `^ 0xFFFF_FFFF`. +pub fn crc32_update(mut crc: u32, data: &[u8]) -> u32 { + static TABLE: std::sync::OnceLock<[u32; 256]> = std::sync::OnceLock::new(); + let t = TABLE.get_or_init(|| { + let mut t = [0u32; 256]; + for (i, e) in t.iter_mut().enumerate() { + let mut c = i as u32; + for _ in 0..8 { + c = if c & 1 != 0 { 0xEDB8_8320 ^ (c >> 1) } else { c >> 1 }; + } + *e = c; + } + t + }); + for &b in data { + crc = t[((crc ^ b as u32) & 0xFF) as usize] ^ (crc >> 8); + } + crc +} + +/// Check a master element's leading CRC-32 child, if present. `Some(true/false)` if there is one. +pub fn verify_crc(master_data: &[u8]) -> Option { + let mut it = Children::new(master_data); + let first = it.next()?; + if first.id != crate::ids::CRC32 || first.data.len() != 4 { + return None; + } + let stored = u32::from_le_bytes(first.data.try_into().ok()?); + let rest = &master_data[first.offset + first.header_len + 4..]; + Some(crc32(rest) == stored) +} + +/// Block lacing mode (flags bits 1–2). +#[derive(Clone, Copy, Debug, PartialEq, Eq)] +pub enum Lacing { + None, + Xiph, + FixedSize, + Ebml, +} + +impl Lacing { + pub fn from_flags(flags: u8) -> Lacing { + match (flags >> 1) & 3 { + 0 => Lacing::None, + 1 => Lacing::Xiph, + 2 => Lacing::FixedSize, + _ => Lacing::Ebml, + } + } +} + +/// Split laced block payload. `data` starts right after the block's flags byte and runs to the end +/// of the block. Returns `(offset, len)` of each frame relative to `data`. +pub fn parse_lacing(lacing: Lacing, data: &[u8]) -> Result> { + if lacing == Lacing::None { + return Ok(vec![(0, data.len())]); + } + let Some(&n1) = data.first() else { return invalid("laced block without frame count") }; + let count = n1 as usize + 1; + let mut pos = 1usize; + let mut sizes = Vec::with_capacity(count); + match lacing { + Lacing::None => return Ok(vec![(0, data.len())]), + Lacing::Xiph => { + for _ in 0..count - 1 { + let mut s = 0usize; + loop { + let Some(&b) = data.get(pos) else { return invalid("truncated Xiph lace header") }; + pos += 1; + s += b as usize; + if b != 255 { + break; + } + } + sizes.push(s); + } + } + Lacing::Ebml => { + if count > 1 { + let Some((first, l, _)) = read_vint(&data[pos..]) else { return invalid("bad EBML lace size") }; + pos += l; + let mut prev = first as i64; + sizes.push(first as usize); + for _ in 1..count - 1 { + let Some((d, l)) = read_svint(&data[pos..]) else { return invalid("bad EBML lace delta") }; + pos += l; + prev += d; + if prev < 0 { + return invalid("negative EBML lace size"); + } + sizes.push(prev as usize); + } + } + } + Lacing::FixedSize => { + let rest = data.len() - pos; + if !rest.is_multiple_of(count) { + return invalid("fixed-size lacing does not divide block"); + } + sizes.extend(std::iter::repeat_n(rest / count, count - 1)); + } + } + let used: usize = sizes.iter().try_fold(0usize, |a, &s| a.checked_add(s)).unwrap_or(usize::MAX); + if pos > data.len() || used > data.len() - pos { + return invalid("lace sizes exceed block"); + } + sizes.push(data.len() - pos - used); + let mut out = Vec::with_capacity(count); + for s in sizes { + out.push((pos, s)); + pos += s; + } + Ok(out) +} + +#[cfg(test)] +mod tests { + use super::*; + + #[test] + fn vint_roundtrip() { + for &v in &[0u64, 1, 126, 127, 128, 16382, 16383, 16384, 1 << 20, (1 << 56) - 2] { + let mut b = Vec::new(); + write_size(&mut b, v, 1); + let (s, l) = read_size(&b).unwrap(); + assert_eq!(s, Size::Known(v), "value {v}"); + assert_eq!(l, b.len()); + } + // 127 needs 2 bytes because 0xFF is "unknown" + let mut b = Vec::new(); + write_size(&mut b, 127, 1); + assert_eq!(b, [0x40, 0x7F]); + // padded length + let mut b = Vec::new(); + write_size(&mut b, 5, 8); + assert_eq!(b, [1, 0, 0, 0, 0, 0, 0, 5]); + assert_eq!(read_size(&b).unwrap(), (Size::Known(5), 8)); + } + + #[test] + fn vint_unknown_and_invalid() { + assert_eq!(read_size(&[0xFF]).unwrap(), (Size::Unknown, 1)); + assert_eq!(read_size(&[0x01, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF]).unwrap(), (Size::Unknown, 8)); + for l in 1..=8 { + let mut b = Vec::new(); + write_unknown_size(&mut b, l); + assert_eq!(read_size(&b).unwrap(), (Size::Unknown, l)); + } + assert!(read_size(&[0x00, 1, 2]).is_none()); + assert!(read_size(&[0x40]).is_none()); // truncated + assert_eq!(read_vint(&[0x81]).unwrap(), (1, 1, false)); + assert_eq!(read_vint(&[0x10, 0x00, 0x00, 0x01]).unwrap(), (1, 4, false)); + } + + #[test] + fn ids() { + assert_eq!(read_id(&[0x1A, 0x45, 0xDF, 0xA3, 0x99]).unwrap(), (crate::ids::EBML, 4)); + assert_eq!(read_id(&[0xA3]).unwrap(), (0xA3, 1)); + assert_eq!(read_id(&[0x42, 0x86]).unwrap(), (0x4286, 2)); + assert!(read_id(&[0xFF]).is_none()); // reserved + assert_eq!(read_id(&[0x80]).unwrap(), (0x80, 1)); // ChapterDisplay + assert!(read_id(&[0x40, 0x00]).is_none()); // zero value + assert!(read_id(&[0x08, 0, 0, 0, 1]).is_none()); // 5-byte ID + for id in [0xA3u32, 0x4286, 0x2AD7B1, 0x1F43B675] { + let mut b = Vec::new(); + write_id(&mut b, id); + assert_eq!(read_id(&b).unwrap(), (id, b.len())); + } + } + + #[test] + fn signed_vint() { + assert_eq!(read_svint(&[0xBF]).unwrap(), (0, 1)); // 63 - 63 + assert_eq!(read_svint(&[0x80]).unwrap(), (-63, 1)); + assert_eq!(read_svint(&[0xC0]).unwrap(), (1, 1)); + assert_eq!(read_svint(&[0x5F, 0xFF]).unwrap(), (0, 2)); + assert_eq!(read_svint(&[0x60, 0x00]).unwrap(), (1, 2)); + } + + #[test] + fn values() { + assert_eq!(uint(&[]), 0); + assert_eq!(uint(&[1, 0]), 256); + assert_eq!(int(&[0xFF]), -1); + assert_eq!(int(&[0xFF, 0xFE]), -2); + assert_eq!(int(&[0x7F]), 127); + assert_eq!(float(&1.5f32.to_be_bytes()), 1.5); + assert_eq!(float(&2.25f64.to_be_bytes()), 2.25); + assert_eq!(string(b"abc\0\0"), "abc"); + } + + #[test] + fn crc() { + assert_eq!(crc32(b"123456789"), 0xCBF4_3926); + let payload = [0x42u8, 0x86, 0x81, 0x01]; + let mut m = vec![0xBF, 0x84]; + m.extend_from_slice(&crc32(&payload).to_le_bytes()); + m.extend_from_slice(&payload); + assert_eq!(verify_crc(&m), Some(true)); + m[7] ^= 1; + assert_eq!(verify_crc(&m), Some(false)); + assert_eq!(verify_crc(&payload), None); + } + + #[test] + fn children_iter() { + // two elements, then one whose size overruns + let d = [0x42, 0x86, 0x81, 0x01, 0xEC, 0x82, 0, 0, 0xA3, 0x85, 1, 2]; + let v: Vec<_> = Children::new(&d).collect(); + assert_eq!(v.len(), 3); + assert_eq!(v[0].id, 0x4286); + assert_eq!(v[0].data, &[1]); + assert_eq!(v[1].id, 0xEC); + assert_eq!(v[2].data, &[1, 2]); + assert!(v[2].truncated); + let mut it = Children::new(&[0x00, 0x01]); + assert!(it.next().is_none()); + assert!(it.damaged()); + } + + fn frames<'a>(d: &'a [u8], f: &[(usize, usize)]) -> Vec<&'a [u8]> { + f.iter().map(|&(o, l)| &d[o..o + l]).collect() + } + + #[test] + fn lacing_xiph() { + // 3 frames: 300, 2, rest(3) + let mut d = vec![2u8, 255, 45, 2]; + d.extend(std::iter::repeat_n(7u8, 300)); + d.extend_from_slice(&[1, 2]); + d.extend_from_slice(&[3, 4, 5]); + let f = parse_lacing(Lacing::Xiph, &d).unwrap(); + assert_eq!(f.iter().map(|x| x.1).collect::>(), [300, 2, 3]); + assert_eq!(frames(&d, &f)[2], &[3, 4, 5]); + // exact multiple of 255 needs a terminating 0 + let mut d = vec![1u8, 255, 0]; + d.extend(std::iter::repeat_n(1u8, 255)); + d.push(9); + let f = parse_lacing(Lacing::Xiph, &d).unwrap(); + assert_eq!(f.iter().map(|x| x.1).collect::>(), [255, 1]); + assert!(parse_lacing(Lacing::Xiph, &[1, 200, 1]).is_err()); + } + + #[test] + fn lacing_ebml() { + // 4 frames: 5, 3 (delta -2), 4 (delta +1), rest 2 + let mut d = vec![3u8, 0x85, 0xBF - 2, 0xBF + 1]; + d.extend_from_slice(&[1; 5]); + d.extend_from_slice(&[2; 3]); + d.extend_from_slice(&[3; 4]); + d.extend_from_slice(&[4; 2]); + let f = parse_lacing(Lacing::Ebml, &d).unwrap(); + assert_eq!(f.iter().map(|x| x.1).collect::>(), [5, 3, 4, 2]); + assert_eq!(frames(&d, &f), [&[1u8; 5][..], &[2; 3], &[3; 4], &[4; 2]]); + assert!(parse_lacing(Lacing::Ebml, &[1, 0x90]).is_err()); + } + + #[test] + fn lacing_fixed_and_none() { + let d = [2u8, 1, 1, 2, 2, 3, 3]; + let f = parse_lacing(Lacing::FixedSize, &d).unwrap(); + assert_eq!(frames(&d, &f), [&[1u8, 1][..], &[2, 2], &[3, 3]]); + assert!(parse_lacing(Lacing::FixedSize, &[2, 1, 1]).is_err()); + assert_eq!(parse_lacing(Lacing::None, &[9, 9]).unwrap(), [(0, 2)]); + assert_eq!(Lacing::from_flags(0x80 | 0x06), Lacing::Ebml); + assert_eq!(Lacing::from_flags(0x02), Lacing::Xiph); + assert_eq!(Lacing::from_flags(0x04), Lacing::FixedSize); + } +} diff --git a/crates/matroska/src/error.rs b/crates/matroska/src/error.rs new file mode 100644 index 0000000..718c5c5 --- /dev/null +++ b/crates/matroska/src/error.rs @@ -0,0 +1,53 @@ +use std::fmt; +use std::io; + +/// Errors produced by the demuxer. +#[derive(Debug)] +pub enum Error { + /// Underlying I/O failure. + Io(io::Error), + /// Structurally invalid data that could not be recovered from. + Invalid(String), + /// Valid but unsupported feature (e.g. zlib-compressed or encrypted tracks when reading data). + Unsupported(String), + /// Not an EBML file, or an EBML file whose DocType is not `matroska`/`webm`. + NotMatroska(String), + /// A track index was out of range. + NoSuchTrack(usize), + /// A sample index was out of range. + NoSuchSample(usize), +} + +impl fmt::Display for Error { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + match self { + Error::Io(e) => write!(f, "I/O error: {e}"), + Error::Invalid(s) => write!(f, "invalid data: {s}"), + Error::Unsupported(s) => write!(f, "unsupported: {s}"), + Error::NotMatroska(s) => write!(f, "not a Matroska/WebM file: {s}"), + Error::NoSuchTrack(i) => write!(f, "track index {i} out of range"), + Error::NoSuchSample(i) => write!(f, "sample index {i} out of range"), + } + } +} + +impl std::error::Error for Error { + fn source(&self) -> Option<&(dyn std::error::Error + 'static)> { + match self { + Error::Io(e) => Some(e), + _ => None, + } + } +} + +impl From for Error { + fn from(e: io::Error) -> Self { + Error::Io(e) + } +} + +pub type Result = std::result::Result; + +pub(crate) fn invalid(msg: impl Into) -> Result { + Err(Error::Invalid(msg.into())) +} diff --git a/crates/matroska/src/ids.rs b/crates/matroska/src/ids.rs new file mode 100644 index 0000000..6334f45 --- /dev/null +++ b/crates/matroska/src/ids.rs @@ -0,0 +1,211 @@ +//! EBML / Matroska element IDs (RFC 8794, RFC 9559). IDs keep their VINT marker bits. +#![allow(dead_code)] + +// EBML header + global elements +pub const EBML: u32 = 0x1A45_DFA3; +pub const EBML_VERSION: u32 = 0x4286; +pub const EBML_READ_VERSION: u32 = 0x42F7; +pub const EBML_MAX_ID_LENGTH: u32 = 0x42F2; +pub const EBML_MAX_SIZE_LENGTH: u32 = 0x42F3; +pub const DOC_TYPE: u32 = 0x4282; +pub const DOC_TYPE_VERSION: u32 = 0x4287; +pub const DOC_TYPE_READ_VERSION: u32 = 0x4285; +pub const CRC32: u32 = 0xBF; +pub const VOID: u32 = 0xEC; + +// Segment and level-1 elements +pub const SEGMENT: u32 = 0x1853_8067; +pub const SEEK_HEAD: u32 = 0x114D_9B74; +pub const INFO: u32 = 0x1549_A966; +pub const TRACKS: u32 = 0x1654_AE6B; +pub const CLUSTER: u32 = 0x1F43_B675; +pub const CUES: u32 = 0x1C53_BB6B; +pub const ATTACHMENTS: u32 = 0x1941_A469; +pub const CHAPTERS: u32 = 0x1043_A770; +pub const TAGS: u32 = 0x1254_C367; + +/// True for IDs that can only appear directly inside a Segment (or are the EBML/Segment headers): +/// used to find the end of unknown-size elements. +pub fn is_top_level(id: u32) -> bool { + matches!(id, EBML | SEGMENT | SEEK_HEAD | INFO | TRACKS | CLUSTER | CUES | ATTACHMENTS | CHAPTERS | TAGS) +} + +// SeekHead +pub const SEEK: u32 = 0x4DBB; +pub const SEEK_ID: u32 = 0x53AB; +pub const SEEK_POSITION: u32 = 0x53AC; + +// Info +pub const SEGMENT_UUID: u32 = 0x73A4; +pub const SEGMENT_FILENAME: u32 = 0x7384; +pub const TIMESTAMP_SCALE: u32 = 0x2A_D7B1; +pub const DURATION: u32 = 0x4489; +pub const DATE_UTC: u32 = 0x4461; +pub const TITLE: u32 = 0x7BA9; +pub const MUXING_APP: u32 = 0x4D80; +pub const WRITING_APP: u32 = 0x5741; + +// Cluster +pub const TIMESTAMP: u32 = 0xE7; +pub const POSITION: u32 = 0xA7; +pub const PREV_SIZE: u32 = 0xAB; +pub const SIMPLE_BLOCK: u32 = 0xA3; +pub const BLOCK_GROUP: u32 = 0xA0; +pub const ENCRYPTED_BLOCK: u32 = 0xAF; +pub const SILENT_TRACKS: u32 = 0x5854; +pub const BLOCK: u32 = 0xA1; +pub const BLOCK_ADDITIONS: u32 = 0x75A1; +pub const BLOCK_DURATION: u32 = 0x9B; +pub const REFERENCE_PRIORITY: u32 = 0xFA; +pub const REFERENCE_BLOCK: u32 = 0xFB; +pub const CODEC_STATE: u32 = 0xA4; +pub const DISCARD_PADDING: u32 = 0x75A2; + +pub fn is_cluster_child(id: u32) -> bool { + matches!(id, TIMESTAMP | POSITION | PREV_SIZE | SIMPLE_BLOCK | BLOCK_GROUP | ENCRYPTED_BLOCK | SILENT_TRACKS | CRC32 | VOID) +} + +// Tracks +pub const TRACK_ENTRY: u32 = 0xAE; +pub const TRACK_NUMBER: u32 = 0xD7; +pub const TRACK_UID: u32 = 0x73C5; +pub const TRACK_TYPE: u32 = 0x83; +pub const FLAG_ENABLED: u32 = 0xB9; +pub const FLAG_DEFAULT: u32 = 0x88; +pub const FLAG_FORCED: u32 = 0x55AA; +pub const FLAG_HEARING_IMPAIRED: u32 = 0x55AB; +pub const FLAG_VISUAL_IMPAIRED: u32 = 0x55AC; +pub const FLAG_ORIGINAL: u32 = 0x55AE; +pub const FLAG_COMMENTARY: u32 = 0x55AF; +pub const FLAG_LACING: u32 = 0x9C; +pub const DEFAULT_DURATION: u32 = 0x23_E383; +pub const TRACK_TIMESTAMP_SCALE: u32 = 0x23_314F; +pub const NAME: u32 = 0x536E; +pub const LANGUAGE: u32 = 0x22_B59C; +pub const LANGUAGE_BCP47: u32 = 0x22_B59D; +pub const CODEC_ID: u32 = 0x86; +pub const CODEC_PRIVATE: u32 = 0x63A2; +pub const CODEC_NAME: u32 = 0x25_8688; +pub const CODEC_DELAY: u32 = 0x56AA; +pub const SEEK_PRE_ROLL: u32 = 0x56BB; + +// Video +pub const VIDEO: u32 = 0xE0; +pub const FLAG_INTERLACED: u32 = 0x9A; +pub const FIELD_ORDER: u32 = 0x9D; +pub const STEREO_MODE: u32 = 0x53B8; +pub const ALPHA_MODE: u32 = 0x53C0; +pub const PIXEL_WIDTH: u32 = 0xB0; +pub const PIXEL_HEIGHT: u32 = 0xBA; +pub const PIXEL_CROP_BOTTOM: u32 = 0x54AA; +pub const PIXEL_CROP_TOP: u32 = 0x54BB; +pub const PIXEL_CROP_LEFT: u32 = 0x54CC; +pub const PIXEL_CROP_RIGHT: u32 = 0x54DD; +pub const DISPLAY_WIDTH: u32 = 0x54B0; +pub const DISPLAY_HEIGHT: u32 = 0x54BA; +pub const DISPLAY_UNIT: u32 = 0x54B2; +pub const COLOUR_SPACE: u32 = 0x2E_B524; +pub const COLOUR: u32 = 0x55B0; +pub const PROJECTION: u32 = 0x7670; +pub const PROJECTION_TYPE: u32 = 0x7671; +pub const PROJECTION_POSE_YAW: u32 = 0x7673; +pub const PROJECTION_POSE_PITCH: u32 = 0x7674; +pub const PROJECTION_POSE_ROLL: u32 = 0x7675; + +// Colour +pub const MATRIX_COEFFICIENTS: u32 = 0x55B1; +pub const BITS_PER_CHANNEL: u32 = 0x55B2; +pub const CHROMA_SUBSAMPLING_HORZ: u32 = 0x55B3; +pub const CHROMA_SUBSAMPLING_VERT: u32 = 0x55B4; +pub const CB_SUBSAMPLING_HORZ: u32 = 0x55B5; +pub const CB_SUBSAMPLING_VERT: u32 = 0x55B6; +pub const CHROMA_SITING_HORZ: u32 = 0x55B7; +pub const CHROMA_SITING_VERT: u32 = 0x55B8; +pub const RANGE: u32 = 0x55B9; +pub const TRANSFER_CHARACTERISTICS: u32 = 0x55BA; +pub const PRIMARIES: u32 = 0x55BB; +pub const MAX_CLL: u32 = 0x55BC; +pub const MAX_FALL: u32 = 0x55BD; +pub const MASTERING_METADATA: u32 = 0x55D0; +pub const PRIMARY_R_X: u32 = 0x55D1; +pub const PRIMARY_R_Y: u32 = 0x55D2; +pub const PRIMARY_G_X: u32 = 0x55D3; +pub const PRIMARY_G_Y: u32 = 0x55D4; +pub const PRIMARY_B_X: u32 = 0x55D5; +pub const PRIMARY_B_Y: u32 = 0x55D6; +pub const WHITE_POINT_X: u32 = 0x55D7; +pub const WHITE_POINT_Y: u32 = 0x55D8; +pub const LUMINANCE_MAX: u32 = 0x55D9; +pub const LUMINANCE_MIN: u32 = 0x55DA; + +// Audio +pub const AUDIO: u32 = 0xE1; +pub const SAMPLING_FREQUENCY: u32 = 0xB5; +pub const OUTPUT_SAMPLING_FREQUENCY: u32 = 0x78B5; +pub const CHANNELS: u32 = 0x9F; +pub const BIT_DEPTH: u32 = 0x6264; + +// ContentEncodings +pub const CONTENT_ENCODINGS: u32 = 0x6D80; +pub const CONTENT_ENCODING: u32 = 0x6240; +pub const CONTENT_ENCODING_ORDER: u32 = 0x5031; +pub const CONTENT_ENCODING_SCOPE: u32 = 0x5032; +pub const CONTENT_ENCODING_TYPE: u32 = 0x5033; +pub const CONTENT_COMPRESSION: u32 = 0x5034; +pub const CONTENT_COMP_ALGO: u32 = 0x4254; +pub const CONTENT_COMP_SETTINGS: u32 = 0x4255; +pub const CONTENT_ENCRYPTION: u32 = 0x5035; + +// Cues +pub const CUE_POINT: u32 = 0xBB; +pub const CUE_TIME: u32 = 0xB3; +pub const CUE_TRACK_POSITIONS: u32 = 0xB7; +pub const CUE_TRACK: u32 = 0xF7; +pub const CUE_CLUSTER_POSITION: u32 = 0xF1; +pub const CUE_RELATIVE_POSITION: u32 = 0xF0; +pub const CUE_DURATION: u32 = 0xB2; +pub const CUE_BLOCK_NUMBER: u32 = 0x5378; + +// Attachments +pub const ATTACHED_FILE: u32 = 0x61A7; +pub const FILE_DESCRIPTION: u32 = 0x467E; +pub const FILE_NAME: u32 = 0x466E; +pub const FILE_MEDIA_TYPE: u32 = 0x4660; +pub const FILE_DATA: u32 = 0x465C; +pub const FILE_UID: u32 = 0x46AE; + +// Chapters +pub const EDITION_ENTRY: u32 = 0x45B9; +pub const EDITION_UID: u32 = 0x45BC; +pub const EDITION_FLAG_HIDDEN: u32 = 0x45BD; +pub const EDITION_FLAG_DEFAULT: u32 = 0x45DB; +pub const EDITION_FLAG_ORDERED: u32 = 0x45DD; +pub const CHAPTER_ATOM: u32 = 0xB6; +pub const CHAPTER_UID: u32 = 0x73C4; +pub const CHAPTER_STRING_UID: u32 = 0x5654; +pub const CHAPTER_TIME_START: u32 = 0x91; +pub const CHAPTER_TIME_END: u32 = 0x92; +pub const CHAPTER_FLAG_HIDDEN: u32 = 0x98; +pub const CHAPTER_FLAG_ENABLED: u32 = 0x4598; +pub const CHAPTER_DISPLAY: u32 = 0x80; +pub const CHAP_STRING: u32 = 0x85; +pub const CHAP_LANGUAGE: u32 = 0x437C; +pub const CHAP_LANGUAGE_BCP47: u32 = 0x437D; +pub const CHAP_COUNTRY: u32 = 0x437E; + +// Tags +pub const TAG: u32 = 0x7373; +pub const TARGETS: u32 = 0x63C0; +pub const TARGET_TYPE_VALUE: u32 = 0x68CA; +pub const TARGET_TYPE: u32 = 0x63CA; +pub const TAG_TRACK_UID: u32 = 0x63C5; +pub const TAG_EDITION_UID: u32 = 0x63C9; +pub const TAG_CHAPTER_UID: u32 = 0x63C4; +pub const TAG_ATTACHMENT_UID: u32 = 0x63C6; +pub const SIMPLE_TAG: u32 = 0x67C8; +pub const TAG_NAME: u32 = 0x45A3; +pub const TAG_LANGUAGE: u32 = 0x447A; +pub const TAG_LANGUAGE_BCP47: u32 = 0x447B; +pub const TAG_DEFAULT: u32 = 0x4484; +pub const TAG_STRING: u32 = 0x4487; +pub const TAG_BINARY: u32 = 0x4485; diff --git a/crates/matroska/src/lib.rs b/crates/matroska/src/lib.rs new file mode 100644 index 0000000..7f2604c --- /dev/null +++ b/crates/matroska/src/lib.rs @@ -0,0 +1,40 @@ +//! Clean-room Matroska (MKV) and WebM demuxer, written from RFC 8794 (EBML), RFC 9559 (Matroska) +//! and the WebM container guidelines. +//! +//! - [`open`] / [`open_with`] parse the EBML header and the first Segment (Info, Tracks, SeekHead, +//! Cues, Chapters, Tags, Attachments) and, by default, scan every Cluster's block headers to build +//! per-track [`Sample`] tables — the same shape as `slidecraft-isobmff`, so callers can treat both +//! containers alike. Frame data is read on demand with [`MkvFile::read_sample`]. +//! - [`Demuxer`] iterates [`Packet`]s in file order (from any [`ByteSource`], `Read + Seek`, or a +//! byte slice) and seeks to the keyframe preceding a time, via the index, Cues, or a cluster scan. +//! +//! Layer L0: no dependencies beyond `std`, no `unsafe`, builds for `wasm32-unknown-unknown`. + +#![cfg_attr(not(test), deny(clippy::unwrap_used, clippy::expect_used, clippy::panic, clippy::unimplemented, clippy::todo, clippy::unreachable))] + +mod codec; +mod demux; +pub mod ebml; +mod error; +mod ids; +mod meta; +mod mux; +mod source; +#[cfg(test)] +mod synthetic_tests; +mod track; + +pub use codec::Codec; +pub use demux::{Demuxer, Keyframe, MkvFile, OpenOptions, Packet, SeekPoint, open, open_with}; +pub use error::{Error, Result}; +pub use meta::{ + Attachment, Chapter, ChapterDisplay, ClusterInfo, CuePoint, CuePosition, Edition, SeekEntry, SegmentInfo, SimpleTag, Tag, TagTargets, +}; +pub use mux::{LacingMode, MkvWriter, MuxOptions, TrackSpec}; +pub use source::{ByteSource, ReadSeekSource}; +pub use track::{AudioInfo, Colour, ContentEncoding, MasteringMetadata, Projection, Sample, Track, TrackKind, VideoInfo}; + +/// Element IDs used by the demuxer (marker bits included), for callers inspecting raw elements. +pub mod element_ids { + pub use crate::ids::*; +} diff --git a/crates/matroska/src/meta.rs b/crates/matroska/src/meta.rs new file mode 100644 index 0000000..081759b --- /dev/null +++ b/crates/matroska/src/meta.rs @@ -0,0 +1,551 @@ +//! Segment-level metadata: Info, SeekHead, Cues, Chapters, Tags, Attachments — types and parsers +//! for their in-memory element data. + +use crate::codec::map_codec; +use crate::ebml::{Children, float, int, string, uint}; +use crate::ids::*; +use crate::track::{AudioInfo, Colour, ContentEncoding, MasteringMetadata, Projection, Track, TrackKind, VideoInfo, gcd}; + +/// `Info` element. +#[derive(Clone, Debug, PartialEq)] +pub struct SegmentInfo { + /// Nanoseconds per timestamp tick (default 1 000 000 = 1 ms). + pub timestamp_scale: u64, + /// `Duration` in timestamp ticks (float), if present. + pub duration: Option, + pub title: Option, + pub muxing_app: String, + pub writing_app: String, + /// `DateUTC`: nanoseconds since 2001-01-01T00:00:00 UTC. + pub date_utc: Option, + pub segment_uuid: Option<[u8; 16]>, +} + +impl Default for SegmentInfo { + fn default() -> Self { + Self { + timestamp_scale: 1_000_000, + duration: None, + title: None, + muxing_app: String::new(), + writing_app: String::new(), + date_utc: None, + segment_uuid: None, + } + } +} + +/// One `SeekHead` entry: level-1 element ID and its position relative to the Segment data start. +#[derive(Clone, Copy, Debug, PartialEq, Eq)] +pub struct SeekEntry { + pub id: u32, + pub position: u64, +} + +/// One `CueTrackPositions` of a `CuePoint`. +#[derive(Clone, Copy, Debug, PartialEq, Eq)] +pub struct CuePosition { + pub track: u64, + /// Cluster position relative to the Segment data start. + pub cluster_position: u64, + /// Block position relative to the Cluster data start. + pub relative_position: Option, + pub block_number: Option, + pub duration: Option, +} + +/// One `CuePoint`. +#[derive(Clone, Debug, PartialEq, Eq)] +pub struct CuePoint { + /// In timestamp ticks. + pub time: u64, + pub positions: Vec, +} + +/// A cluster found while scanning. +#[derive(Clone, Copy, Debug, PartialEq, Eq)] +pub struct ClusterInfo { + /// Absolute offset of the Cluster element header. + pub offset: u64, + /// Absolute offset of the first child. + pub data_start: u64, + /// Absolute end offset (for unknown-size clusters, where the next top-level element starts). + pub end: u64, + /// Cluster `Timestamp` in ticks. + pub timestamp: u64, +} + +/// One `ChapterDisplay`. +#[derive(Clone, Debug, Default, PartialEq)] +pub struct ChapterDisplay { + pub string: String, + pub languages: Vec, + pub languages_bcp47: Vec, + pub countries: Vec, +} + +/// `ChapterAtom` (possibly nested). +#[derive(Clone, Debug, PartialEq)] +pub struct Chapter { + pub uid: u64, + pub string_uid: Option, + pub start_ns: u64, + pub end_ns: Option, + pub hidden: bool, + pub enabled: bool, + pub displays: Vec, + pub children: Vec, +} + +impl Chapter { + /// First display string, if any. + pub fn title(&self) -> Option<&str> { + self.displays.first().map(|d| d.string.as_str()) + } +} + +/// `EditionEntry`. +#[derive(Clone, Debug, Default, PartialEq)] +pub struct Edition { + pub uid: Option, + pub hidden: bool, + pub default: bool, + pub ordered: bool, + pub chapters: Vec, +} + +/// `Targets` of a `Tag`. Empty UID lists mean "the whole segment". +#[derive(Clone, Debug, PartialEq)] +pub struct TagTargets { + pub type_value: u64, + pub type_name: Option, + pub track_uids: Vec, + pub edition_uids: Vec, + pub chapter_uids: Vec, + pub attachment_uids: Vec, +} + +impl Default for TagTargets { + fn default() -> Self { + Self { + type_value: 50, + type_name: None, + track_uids: Vec::new(), + edition_uids: Vec::new(), + chapter_uids: Vec::new(), + attachment_uids: Vec::new(), + } + } +} + +/// `SimpleTag` (possibly nested). +#[derive(Clone, Debug, PartialEq)] +pub struct SimpleTag { + pub name: String, + pub language: String, + pub default: bool, + pub string: Option, + pub binary: Option>, + pub children: Vec, +} + +/// `Tag`. +#[derive(Clone, Debug, Default, PartialEq)] +pub struct Tag { + pub targets: TagTargets, + pub simple: Vec, +} + +/// `AttachedFile` (listed only; the payload is located by `data_offset`/`data_size`). +#[derive(Clone, Debug, Default, PartialEq)] +pub struct Attachment { + pub uid: u64, + pub name: String, + pub media_type: String, + pub description: Option, + pub data_offset: u64, + pub data_size: u64, +} + +pub(crate) fn parse_info(d: &[u8]) -> SegmentInfo { + let mut info = SegmentInfo::default(); + for e in Children::new(d) { + match e.id { + TIMESTAMP_SCALE => { + let v = uint(e.data); + if v > 0 { + info.timestamp_scale = v; + } + } + DURATION => info.duration = Some(float(e.data)).filter(|v| v.is_finite() && *v >= 0.0), + TITLE => info.title = Some(string(e.data)), + MUXING_APP => info.muxing_app = string(e.data), + WRITING_APP => info.writing_app = string(e.data), + DATE_UTC => info.date_utc = Some(int(e.data)), + SEGMENT_UUID => info.segment_uuid = e.data.try_into().ok(), + _ => {} + } + } + info +} + +pub(crate) fn parse_seekhead(d: &[u8]) -> Vec { + let mut out = Vec::new(); + for s in Children::new(d).filter(|e| e.id == SEEK) { + let (mut id, mut pos) = (None, None); + for e in Children::new(s.data) { + match e.id { + SEEK_ID => id = Some(uint(e.data) as u32), + SEEK_POSITION => pos = Some(uint(e.data)), + _ => {} + } + } + if let (Some(id), Some(position)) = (id, pos) { + out.push(SeekEntry { id, position }); + } + } + out +} + +pub(crate) fn parse_cues(d: &[u8], out: &mut Vec) { + for cp in Children::new(d).filter(|e| e.id == CUE_POINT) { + let mut time = None; + let mut positions = Vec::new(); + for e in Children::new(cp.data) { + match e.id { + CUE_TIME => time = Some(uint(e.data)), + CUE_TRACK_POSITIONS => { + let mut p = CuePosition { track: 0, cluster_position: u64::MAX, relative_position: None, block_number: None, duration: None }; + for c in Children::new(e.data) { + match c.id { + CUE_TRACK => p.track = uint(c.data), + CUE_CLUSTER_POSITION => p.cluster_position = uint(c.data), + CUE_RELATIVE_POSITION => p.relative_position = Some(uint(c.data)), + CUE_BLOCK_NUMBER => p.block_number = Some(uint(c.data)), + CUE_DURATION => p.duration = Some(uint(c.data)), + _ => {} + } + } + if p.track != 0 && p.cluster_position != u64::MAX { + positions.push(p); + } + } + _ => {} + } + } + if let Some(time) = time + && !positions.is_empty() + { + out.push(CuePoint { time, positions }); + } + } +} + +fn parse_chapter(d: &[u8]) -> Chapter { + let mut c = + Chapter { uid: 0, string_uid: None, start_ns: 0, end_ns: None, hidden: false, enabled: true, displays: Vec::new(), children: Vec::new() }; + for e in Children::new(d) { + match e.id { + CHAPTER_UID => c.uid = uint(e.data), + CHAPTER_STRING_UID => c.string_uid = Some(string(e.data)), + CHAPTER_TIME_START => c.start_ns = uint(e.data), + CHAPTER_TIME_END => c.end_ns = Some(uint(e.data)), + CHAPTER_FLAG_HIDDEN => c.hidden = uint(e.data) != 0, + CHAPTER_FLAG_ENABLED => c.enabled = uint(e.data) != 0, + CHAPTER_DISPLAY => { + let mut disp = ChapterDisplay::default(); + for x in Children::new(e.data) { + match x.id { + CHAP_STRING => disp.string = string(x.data), + CHAP_LANGUAGE => disp.languages.push(string(x.data)), + CHAP_LANGUAGE_BCP47 => disp.languages_bcp47.push(string(x.data)), + CHAP_COUNTRY => disp.countries.push(string(x.data)), + _ => {} + } + } + if disp.languages.is_empty() { + disp.languages.push("eng".into()); + } + c.displays.push(disp); + } + CHAPTER_ATOM => c.children.push(parse_chapter(e.data)), + _ => {} + } + } + c +} + +pub(crate) fn parse_chapters(d: &[u8], out: &mut Vec) { + for ed in Children::new(d).filter(|e| e.id == EDITION_ENTRY) { + let mut edition = Edition::default(); + for e in Children::new(ed.data) { + match e.id { + EDITION_UID => edition.uid = Some(uint(e.data)), + EDITION_FLAG_HIDDEN => edition.hidden = uint(e.data) != 0, + EDITION_FLAG_DEFAULT => edition.default = uint(e.data) != 0, + EDITION_FLAG_ORDERED => edition.ordered = uint(e.data) != 0, + CHAPTER_ATOM => edition.chapters.push(parse_chapter(e.data)), + _ => {} + } + } + out.push(edition); + } +} + +fn parse_simple_tag(d: &[u8]) -> SimpleTag { + let mut t = SimpleTag { name: String::new(), language: "und".into(), default: true, string: None, binary: None, children: Vec::new() }; + for e in Children::new(d) { + match e.id { + TAG_NAME => t.name = string(e.data), + TAG_LANGUAGE => t.language = string(e.data), + TAG_DEFAULT => t.default = uint(e.data) != 0, + TAG_STRING => t.string = Some(string(e.data)), + TAG_BINARY => t.binary = Some(e.data.to_vec()), + SIMPLE_TAG => t.children.push(parse_simple_tag(e.data)), + _ => {} + } + } + t +} + +pub(crate) fn parse_tags(d: &[u8], out: &mut Vec) { + for tg in Children::new(d).filter(|e| e.id == TAG) { + let mut tag = Tag::default(); + for e in Children::new(tg.data) { + match e.id { + TARGETS => { + for x in Children::new(e.data) { + match x.id { + TARGET_TYPE_VALUE => tag.targets.type_value = uint(x.data), + TARGET_TYPE => tag.targets.type_name = Some(string(x.data)), + TAG_TRACK_UID => tag.targets.track_uids.push(uint(x.data)), + TAG_EDITION_UID => tag.targets.edition_uids.push(uint(x.data)), + TAG_CHAPTER_UID => tag.targets.chapter_uids.push(uint(x.data)), + TAG_ATTACHMENT_UID => tag.targets.attachment_uids.push(uint(x.data)), + _ => {} + } + } + } + SIMPLE_TAG => tag.simple.push(parse_simple_tag(e.data)), + _ => {} + } + } + out.push(tag); + } +} + +fn parse_colour(d: &[u8]) -> Colour { + let mut c = Colour::default(); + for e in Children::new(d) { + let v = || Some(uint(e.data)); + match e.id { + MATRIX_COEFFICIENTS => c.matrix_coefficients = v(), + BITS_PER_CHANNEL => c.bits_per_channel = v(), + CHROMA_SUBSAMPLING_HORZ => c.chroma_subsampling_horz = v(), + CHROMA_SUBSAMPLING_VERT => c.chroma_subsampling_vert = v(), + CB_SUBSAMPLING_HORZ => c.cb_subsampling_horz = v(), + CB_SUBSAMPLING_VERT => c.cb_subsampling_vert = v(), + CHROMA_SITING_HORZ => c.chroma_siting_horz = v(), + CHROMA_SITING_VERT => c.chroma_siting_vert = v(), + RANGE => c.range = v(), + TRANSFER_CHARACTERISTICS => c.transfer_characteristics = v(), + PRIMARIES => c.primaries = v(), + MAX_CLL => c.max_cll = v(), + MAX_FALL => c.max_fall = v(), + MASTERING_METADATA => { + let mut m = MasteringMetadata::default(); + for x in Children::new(e.data) { + let f = float(x.data); + match x.id { + PRIMARY_R_X => m.primaries[0].0 = f, + PRIMARY_R_Y => m.primaries[0].1 = f, + PRIMARY_G_X => m.primaries[1].0 = f, + PRIMARY_G_Y => m.primaries[1].1 = f, + PRIMARY_B_X => m.primaries[2].0 = f, + PRIMARY_B_Y => m.primaries[2].1 = f, + WHITE_POINT_X => m.white_point.0 = f, + WHITE_POINT_Y => m.white_point.1 = f, + LUMINANCE_MAX => m.luminance_max = f, + LUMINANCE_MIN => m.luminance_min = f, + _ => {} + } + } + c.mastering = Some(m); + } + _ => {} + } + } + c +} + +fn parse_video(d: &[u8]) -> VideoInfo { + let mut v = VideoInfo::default(); + for e in Children::new(d) { + let u = uint(e.data); + match e.id { + PIXEL_WIDTH => v.pixel_width = u as u32, + PIXEL_HEIGHT => v.pixel_height = u as u32, + DISPLAY_WIDTH => v.display_width = Some(u as u32), + DISPLAY_HEIGHT => v.display_height = Some(u as u32), + DISPLAY_UNIT => v.display_unit = u, + PIXEL_CROP_TOP => v.crop.0 = u as u32, + PIXEL_CROP_BOTTOM => v.crop.1 = u as u32, + PIXEL_CROP_LEFT => v.crop.2 = u as u32, + PIXEL_CROP_RIGHT => v.crop.3 = u as u32, + FLAG_INTERLACED => v.flag_interlaced = u, + FIELD_ORDER => v.field_order = Some(u), + STEREO_MODE => v.stereo_mode = Some(u), + ALPHA_MODE => v.alpha_mode = u, + COLOUR_SPACE => v.colour_space = e.data.try_into().ok(), + COLOUR => v.colour = Some(parse_colour(e.data)), + PROJECTION => { + let mut p = Projection::default(); + for x in Children::new(e.data) { + match x.id { + PROJECTION_TYPE => p.projection_type = uint(x.data), + PROJECTION_POSE_YAW => p.yaw = float(x.data), + PROJECTION_POSE_PITCH => p.pitch = float(x.data), + PROJECTION_POSE_ROLL => p.roll = float(x.data), + _ => {} + } + } + v.projection = Some(p); + } + _ => {} + } + } + v +} + +fn parse_audio(d: &[u8]) -> AudioInfo { + let mut a = AudioInfo::default(); + for e in Children::new(d) { + match e.id { + SAMPLING_FREQUENCY => a.sampling_frequency = float(e.data), + OUTPUT_SAMPLING_FREQUENCY => a.output_sampling_frequency = Some(float(e.data)), + CHANNELS => a.channels = uint(e.data), + BIT_DEPTH => a.bit_depth = Some(uint(e.data)), + _ => {} + } + } + a +} + +fn parse_encodings(d: &[u8]) -> Vec { + let mut list: Vec<(u64, ContentEncoding)> = Vec::new(); + for ce in Children::new(d).filter(|e| e.id == CONTENT_ENCODING) { + let (mut order, mut scope, mut ty) = (0u64, 1u64, 0u64); + let mut comp: Option<(u64, Vec)> = None; + for e in Children::new(ce.data) { + match e.id { + CONTENT_ENCODING_ORDER => order = uint(e.data), + CONTENT_ENCODING_SCOPE => scope = uint(e.data), + CONTENT_ENCODING_TYPE => ty = uint(e.data), + CONTENT_COMPRESSION => { + let (mut algo, mut settings) = (0u64, Vec::new()); + for x in Children::new(e.data) { + match x.id { + CONTENT_COMP_ALGO => algo = uint(x.data), + CONTENT_COMP_SETTINGS => settings = x.data.to_vec(), + _ => {} + } + } + comp = Some((algo, settings)); + } + _ => {} + } + } + let enc = if ty == 1 { + ContentEncoding::Encryption { scope } + } else { + let (algo, settings) = comp.unwrap_or((0, Vec::new())); + if algo == 3 { + ContentEncoding::HeaderStripping { bytes: settings, scope } + } else { + ContentEncoding::Compression { algo, settings, scope } + } + }; + list.push((order, enc)); + } + // highest order is applied last on muxing → first to undo; keep decode order + list.sort_by_key(|e| std::cmp::Reverse(e.0)); + list.into_iter().map(|e| e.1).collect() +} + +pub(crate) fn parse_tracks(d: &[u8], timestamp_scale: u64) -> Vec { + let g = gcd(timestamp_scale, 1_000_000_000); + let timebase = (timestamp_scale / g, 1_000_000_000 / g); + let mut out = Vec::new(); + for te in Children::new(d).filter(|e| e.id == TRACK_ENTRY) { + let mut t = Track { + number: 0, + uid: 0, + kind: TrackKind::Other(0), + enabled: true, + default: true, + forced: false, + hearing_impaired: false, + lacing: true, + default_duration_ns: None, + name: String::new(), + language: "eng".into(), + language_bcp47: None, + codec_id: String::new(), + codec_private: Vec::new(), + codec_name: String::new(), + codec: crate::Codec::Other(String::new()), + codec_delay_ns: 0, + seek_pre_roll_ns: 0, + video: None, + audio: None, + encodings: Vec::new(), + timebase, + samples: Vec::new(), + sync: Vec::new(), + }; + for e in Children::new(te.data) { + match e.id { + TRACK_NUMBER => t.number = uint(e.data), + TRACK_UID => t.uid = uint(e.data), + TRACK_TYPE => t.kind = TrackKind::from_type(uint(e.data)), + FLAG_ENABLED => t.enabled = uint(e.data) != 0, + FLAG_DEFAULT => t.default = uint(e.data) != 0, + FLAG_FORCED => t.forced = uint(e.data) != 0, + FLAG_HEARING_IMPAIRED => t.hearing_impaired = uint(e.data) != 0, + FLAG_LACING => t.lacing = uint(e.data) != 0, + DEFAULT_DURATION => t.default_duration_ns = Some(uint(e.data)).filter(|&v| v > 0), + NAME => t.name = string(e.data), + LANGUAGE => t.language = string(e.data), + LANGUAGE_BCP47 => t.language_bcp47 = Some(string(e.data)), + CODEC_ID => t.codec_id = string(e.data), + CODEC_PRIVATE => t.codec_private = e.data.to_vec(), + CODEC_NAME => t.codec_name = string(e.data), + CODEC_DELAY => t.codec_delay_ns = uint(e.data), + SEEK_PRE_ROLL => t.seek_pre_roll_ns = uint(e.data), + VIDEO => t.video = Some(parse_video(e.data)), + AUDIO => t.audio = Some(parse_audio(e.data)), + CONTENT_ENCODINGS => t.encodings = parse_encodings(e.data), + _ => {} + } + } + if t.number == 0 { + continue; + } + if t.kind == TrackKind::Audio && t.audio.is_none() { + t.audio = Some(AudioInfo::default()); + } + // CodecPrivate may itself be header-stripped (scope bit 2 = private data) + for enc in &t.encodings { + if let ContentEncoding::HeaderStripping { bytes, scope } = enc + && scope & 2 != 0 + { + let mut p = bytes.clone(); + p.extend_from_slice(&t.codec_private); + t.codec_private = p; + } + } + t.codec = map_codec(&t.codec_id, &t.codec_private, t.audio.as_ref()); + out.push(t); + } + out +} diff --git a/crates/matroska/src/mux.rs b/crates/matroska/src/mux.rs new file mode 100644 index 0000000..d64443c --- /dev/null +++ b/crates/matroska/src/mux.rs @@ -0,0 +1,539 @@ +//! Minimal Matroska / WebM muxer: SimpleBlocks (optionally laced), BlockGroups for explicit +//! durations, Cues, SeekHead, Info with Duration. Output must be `Write + Seek` (sizes and the +//! SeekHead are back-patched on [`MkvWriter::finish`]). + +use std::io::{Seek, SeekFrom, Write}; + +use crate::ebml::{write_id, write_size, write_unknown_size}; +use crate::error::{Error, Result}; +use crate::ids::*; +use crate::track::TrackKind; + +/// Lacing used for audio tracks. +#[derive(Clone, Copy, Debug, PartialEq, Eq)] +pub enum LacingMode { + None, + Xiph, + Ebml, + /// Fixed-size lacing when consecutive frames have equal sizes, otherwise EBML lacing. + FixedOrEbml, +} + +/// Track description for [`MkvWriter`]. +#[derive(Clone, Debug)] +pub struct TrackSpec { + pub kind: TrackKind, + pub codec_id: String, + pub codec_private: Vec, + pub default_duration_ns: Option, + pub language: Option, + pub name: Option, + /// Pixel width/height for video tracks. + pub video_size: Option<(u32, u32)>, + /// (sampling frequency, channels, bit depth) for audio tracks. + pub audio: Option<(f64, u64, Option)>, + pub codec_delay_ns: u64, + pub seek_pre_roll_ns: u64, +} + +impl TrackSpec { + pub fn new(kind: TrackKind, codec_id: impl Into) -> Self { + Self { + kind, + codec_id: codec_id.into(), + codec_private: Vec::new(), + default_duration_ns: None, + language: None, + name: None, + video_size: None, + audio: None, + codec_delay_ns: 0, + seek_pre_roll_ns: 0, + } + } +} + +/// Muxer options. +#[derive(Clone, Debug)] +pub struct MuxOptions { + /// `webm` or `matroska`. + pub doc_type: String, + /// Nanoseconds per tick (default 1 000 000). + pub timestamp_scale: u64, + /// Start a new cluster at the next video keyframe (or any frame without video) after this long. + pub cluster_duration_ns: u64, + pub lacing: LacingMode, + /// Maximum frames per laced block. + pub max_lace_frames: usize, + pub title: Option, + pub writing_app: String, +} + +impl Default for MuxOptions { + fn default() -> Self { + Self { + doc_type: "matroska".into(), + timestamp_scale: 1_000_000, + cluster_duration_ns: 1_000_000_000, + lacing: LacingMode::None, + max_lace_frames: 8, + title: None, + writing_app: "slidecraft".into(), + } + } +} + +pub(crate) fn el(out: &mut Vec, id: u32, data: &[u8]) { + write_id(out, id); + write_size(out, data.len() as u64, 1); + out.extend_from_slice(data); +} + +pub(crate) fn el_uint(out: &mut Vec, id: u32, v: u64) { + let n = (8 - v.leading_zeros() as usize / 8).max(1); + el(out, id, &v.to_be_bytes()[8 - n..]); +} + +pub(crate) fn el_float(out: &mut Vec, id: u32, v: f64) { + el(out, id, &v.to_be_bytes()); +} + +pub(crate) fn el_str(out: &mut Vec, id: u32, s: &str) { + el(out, id, s.as_bytes()); +} + +fn void(out: &mut Vec, total: usize) { + debug_assert!(total >= 2); + let sl = if total - 2 <= 126 { 1 } else { 8 }; + write_id(out, VOID); + write_size(out, (total - 1 - sl) as u64, sl); + out.resize(out.len() + total - 1 - sl, 0); +} + +#[derive(Clone, Debug)] +struct Pending { + track: usize, + ts: i64, + frames: Vec>, +} + +const SEEK_HEAD_SPACE: usize = 128; + +/// Streaming Matroska/WebM writer. +pub struct MkvWriter { + w: W, + opts: MuxOptions, + tracks: Vec, + segment_size_pos: u64, + segment_data_start: u64, + seek_head_pos: u64, + duration_pos: u64, + info_pos: u64, + tracks_pos: u64, + cluster: Vec, + cluster_ts: Option, + cluster_start_ns: i64, + pending: Option, + cues: Vec<(u64, u64, u64)>, // (time ticks, track number, cluster position) + cued_tracks_in_cluster: Vec, + end_ns: i64, + has_video: bool, +} + +impl MkvWriter { + pub fn new(mut w: W, tracks: Vec, opts: MuxOptions) -> Result { + if tracks.is_empty() || opts.timestamp_scale == 0 { + return Err(Error::Invalid("muxer needs at least one track and a non-zero timestamp scale".into())); + } + let mut out = Vec::new(); + let mut h = Vec::new(); + el_uint(&mut h, EBML_VERSION, 1); + el_uint(&mut h, EBML_READ_VERSION, 1); + el_uint(&mut h, EBML_MAX_ID_LENGTH, 4); + el_uint(&mut h, EBML_MAX_SIZE_LENGTH, 8); + el_str(&mut h, DOC_TYPE, &opts.doc_type); + el_uint(&mut h, DOC_TYPE_VERSION, 4); + el_uint(&mut h, DOC_TYPE_READ_VERSION, 2); + el(&mut out, EBML, &h); + write_id(&mut out, SEGMENT); + let base = w.stream_position()?; + let segment_size_pos = base + out.len() as u64; + write_unknown_size(&mut out, 8); + let segment_data_start = base + out.len() as u64; + let seek_head_pos = segment_data_start; + void(&mut out, SEEK_HEAD_SPACE); + // Info + let info_pos = base + out.len() as u64; + let mut info = Vec::new(); + el_uint(&mut info, TIMESTAMP_SCALE, opts.timestamp_scale); + el_str(&mut info, MUXING_APP, "slidecraft-matroska"); + el_str(&mut info, WRITING_APP, &opts.writing_app); + if let Some(t) = &opts.title { + el_str(&mut info, TITLE, t); + } + let dur_off_in_info = info.len() + 2 + 1; // ID (2) + size (1) + el_float(&mut info, DURATION, 0.0); + write_id(&mut out, INFO); + write_size(&mut out, info.len() as u64, 1); + let duration_pos = base + out.len() as u64 + dur_off_in_info as u64; + out.extend_from_slice(&info); + // Tracks + let tracks_pos = base + out.len() as u64; + let mut tr = Vec::new(); + for (i, t) in tracks.iter().enumerate() { + let mut e = Vec::new(); + el_uint(&mut e, TRACK_NUMBER, i as u64 + 1); + el_uint(&mut e, TRACK_UID, 0x1000 + i as u64 + 1); + let ty = match t.kind { + TrackKind::Video => 1, + TrackKind::Audio => 2, + TrackKind::Subtitle => 0x11, + TrackKind::Complex => 3, + TrackKind::Logo => 0x10, + TrackKind::Buttons => 0x12, + TrackKind::Control => 0x20, + TrackKind::Metadata => 0x21, + TrackKind::Other(n) => n as u64, + }; + el_uint(&mut e, TRACK_TYPE, ty); + el_uint(&mut e, FLAG_LACING, (t.kind == TrackKind::Audio && opts.lacing != LacingMode::None) as u64); + if let Some(l) = &t.language { + el_str(&mut e, LANGUAGE, l); + } + if let Some(n) = &t.name { + el_str(&mut e, NAME, n); + } + el_str(&mut e, CODEC_ID, &t.codec_id); + if !t.codec_private.is_empty() { + el(&mut e, CODEC_PRIVATE, &t.codec_private); + } + if let Some(d) = t.default_duration_ns { + el_uint(&mut e, DEFAULT_DURATION, d); + } + if t.codec_delay_ns > 0 { + el_uint(&mut e, CODEC_DELAY, t.codec_delay_ns); + } + if t.seek_pre_roll_ns > 0 { + el_uint(&mut e, SEEK_PRE_ROLL, t.seek_pre_roll_ns); + } + if let Some((pw, ph)) = t.video_size { + let mut v = Vec::new(); + el_uint(&mut v, PIXEL_WIDTH, pw as u64); + el_uint(&mut v, PIXEL_HEIGHT, ph as u64); + el(&mut e, VIDEO, &v); + } + if let Some((rate, ch, bits)) = t.audio { + let mut a = Vec::new(); + el_float(&mut a, SAMPLING_FREQUENCY, rate); + el_uint(&mut a, CHANNELS, ch); + if let Some(b) = bits { + el_uint(&mut a, BIT_DEPTH, b); + } + el(&mut e, AUDIO, &a); + } + el(&mut tr, TRACK_ENTRY, &e); + } + el(&mut out, TRACKS, &tr); + w.write_all(&out)?; + let has_video = tracks.iter().any(|t| t.kind == TrackKind::Video); + let n = tracks.len(); + Ok(Self { + w, + opts, + tracks, + segment_size_pos, + segment_data_start, + seek_head_pos, + duration_pos, + info_pos, + tracks_pos, + cluster: Vec::new(), + cluster_ts: None, + cluster_start_ns: 0, + pending: None, + cues: Vec::new(), + cued_tracks_in_cluster: vec![false; n], + end_ns: 0, + has_video, + }) + } + + /// Add one frame. `pts_ns` is the presentation time (codec delay is added back for storage); + /// frames must be supplied in the desired file order. `duration_ns` forces a BlockGroup with a + /// BlockDuration when it differs from the track's DefaultDuration. + pub fn write_frame(&mut self, track: usize, pts_ns: i64, keyframe: bool, data: &[u8], duration_ns: Option) -> Result<()> { + let spec = self.tracks.get(track).ok_or(Error::NoSuchTrack(track))?; + // the Matroska ProRes mapping stores frames without the 8-byte `size`+`icpf` header + let data = if spec.codec_id == "V_PRORES" && data.get(4..8) == Some(b"icpf") { &data[8..] } else { data }; + let scale = self.opts.timestamp_scale as i64; + let stored_ns = pts_ns + spec.codec_delay_ns as i64; + if stored_ns < 0 { + return Err(Error::Invalid("negative timestamp".into())); + } + let ts = (stored_ns + scale / 2) / scale; + let dur_end = pts_ns + duration_ns.or(spec.default_duration_ns).unwrap_or(0) as i64; + self.end_ns = self.end_ns.max(dur_end).max(pts_ns); + let is_video = spec.kind == TrackKind::Video; + let lacing = spec.kind == TrackKind::Audio && self.opts.lacing != LacingMode::None; + let explicit_dur = duration_ns.filter(|&d| Some(d) != spec.default_duration_ns); + // new cluster? + let start_new = match self.cluster_ts { + None => true, + Some(cts) => { + let long = stored_ns - self.cluster_start_ns >= self.opts.cluster_duration_ns as i64; + (long && (!self.has_video || (is_video && keyframe))) || ts - cts > 30_000 || ts < cts + } + }; + if start_new { + self.flush_cluster()?; + self.cluster_ts = Some(ts); + self.cluster_start_ns = stored_ns; + el_uint(&mut self.cluster, TIMESTAMP, ts as u64); + } + let cts = self.cluster_ts.unwrap_or(0); + // cue: video keyframes, or the first frame of each track per cluster when there is no video + if (is_video && keyframe) || (!self.has_video && !self.cued_tracks_in_cluster[track]) { + self.cued_tracks_in_cluster[track] = true; + let pos = self.w.stream_position()? - self.segment_data_start; + if self.cues.last().is_none_or(|c| c.2 != pos || c.1 != track as u64 + 1) { + self.cues.push((ts as u64, track as u64 + 1, pos)); + } + } + if lacing && explicit_dur.is_none() { + let same = self.pending.as_ref().is_some_and(|p| p.track == track && p.frames.len() < self.opts.max_lace_frames); + if !same { + self.flush_pending(); + } + match &mut self.pending { + Some(p) => p.frames.push(data.to_vec()), + None => self.pending = Some(Pending { track, ts: ts - cts, frames: vec![data.to_vec()] }), + } + return Ok(()); + } + self.flush_pending(); + let mut block = Vec::new(); + write_size(&mut block, track as u64 + 1, 1); + block.extend_from_slice(&((ts - cts) as i16).to_be_bytes()); + if let Some(d) = explicit_dur { + block.push(0); + block.extend_from_slice(data); + let mut g = Vec::new(); + el(&mut g, BLOCK, &block); + el_uint(&mut g, BLOCK_DURATION, (d as i64 / scale) as u64); + if !keyframe { + el(&mut g, REFERENCE_BLOCK, &[0xFF]); + } + el(&mut self.cluster, BLOCK_GROUP, &g); + } else { + block.push(if keyframe { 0x80 } else { 0 }); + block.extend_from_slice(data); + el(&mut self.cluster, SIMPLE_BLOCK, &block); + } + Ok(()) + } + + fn flush_pending(&mut self) { + let Some(p) = self.pending.take() else { return }; + let mut block = Vec::new(); + write_size(&mut block, p.track as u64 + 1, 1); + block.extend_from_slice(&(p.ts as i16).to_be_bytes()); + let n = p.frames.len(); + let mode = if n == 1 { LacingMode::None } else { self.opts.lacing }; + let fixed = p.frames.iter().all(|f| f.len() == p.frames[0].len()); + match mode { + LacingMode::None => block.push(0x80), + LacingMode::Xiph => { + block.push(0x80 | 0x02); + block.push((n - 1) as u8); + for f in &p.frames[..n - 1] { + let mut s = f.len(); + while s >= 255 { + block.push(255); + s -= 255; + } + block.push(s as u8); + } + } + LacingMode::FixedOrEbml if fixed => { + block.push(0x80 | 0x04); + block.push((n - 1) as u8); + } + LacingMode::Ebml | LacingMode::FixedOrEbml => { + block.push(0x80 | 0x06); + block.push((n - 1) as u8); + write_size(&mut block, p.frames[0].len() as u64, 1); + for i in 1..n - 1 { + let d = p.frames[i].len() as i64 - p.frames[i - 1].len() as i64; + // signed VINT: smallest length whose range holds d + let mut len = 1; + while len < 8 && d.unsigned_abs() >= (1u64 << (7 * len - 1)) - 1 { + len += 1; + } + let v = (d + (1i64 << (7 * len - 1)) - 1) as u64 | (1u64 << (7 * len)); + block.extend_from_slice(&v.to_be_bytes()[8 - len..]); + } + } + } + for f in &p.frames { + block.extend_from_slice(f); + } + el(&mut self.cluster, SIMPLE_BLOCK, &block); + } + + fn flush_cluster(&mut self) -> Result<()> { + self.flush_pending(); + if self.cluster_ts.is_some() { + let mut out = Vec::new(); + el(&mut out, CLUSTER, &self.cluster); + self.w.write_all(&out)?; + } + self.cluster.clear(); + self.cluster_ts = None; + self.cued_tracks_in_cluster.iter_mut().for_each(|c| *c = false); + Ok(()) + } + + /// Write Cues, back-patch SeekHead / Segment size / Duration, and return the writer. + pub fn finish(mut self) -> Result { + self.flush_cluster()?; + let cues_pos = self.w.stream_position()?; + let mut cues = Vec::new(); + for &(time, track, pos) in &self.cues { + let mut ctp = Vec::new(); + el_uint(&mut ctp, CUE_TRACK, track); + el_uint(&mut ctp, CUE_CLUSTER_POSITION, pos); + let mut cp = Vec::new(); + el_uint(&mut cp, CUE_TIME, time); + el(&mut cp, CUE_TRACK_POSITIONS, &ctp); + el(&mut cues, CUE_POINT, &cp); + } + let has_cues = !cues.is_empty(); + if has_cues { + let mut out = Vec::new(); + el(&mut out, CUES, &cues); + self.w.write_all(&out)?; + } + let end = self.w.stream_position()?; + // SeekHead + let mut sh = Vec::new(); + let mut entries = vec![(INFO, self.info_pos), (TRACKS, self.tracks_pos)]; + if has_cues { + entries.push((CUES, cues_pos)); + } + for (id, pos) in entries { + let mut s = Vec::new(); + let mut idb = Vec::new(); + write_id(&mut idb, id); + el(&mut s, SEEK_ID, &idb); + el_uint(&mut s, SEEK_POSITION, pos - self.segment_data_start); + el(&mut sh, SEEK, &s); + } + let mut out = Vec::new(); + el(&mut out, SEEK_HEAD, &sh); + if out.len() + 2 > SEEK_HEAD_SPACE { + return Err(Error::Invalid("SeekHead does not fit its reserved space".into())); + } + let rest = SEEK_HEAD_SPACE - out.len(); + void(&mut out, rest); + self.w.seek(SeekFrom::Start(self.seek_head_pos))?; + self.w.write_all(&out)?; + // Duration + let dur = self.end_ns as f64 / self.opts.timestamp_scale as f64; + self.w.seek(SeekFrom::Start(self.duration_pos))?; + self.w.write_all(&dur.to_be_bytes())?; + // Segment size + let mut sz = Vec::new(); + write_size(&mut sz, end - self.segment_data_start, 8); + self.w.seek(SeekFrom::Start(self.segment_size_pos))?; + self.w.write_all(&sz)?; + self.w.seek(SeekFrom::Start(end))?; + self.w.flush()?; + Ok(self.w) + } +} + +#[cfg(test)] +mod tests { + use super::*; + use crate::{Demuxer, TrackKind}; + use std::io::Cursor; + + fn roundtrip(lacing: LacingMode) { + let mut v = TrackSpec::new(TrackKind::Video, "V_MJPEG"); + v.video_size = Some((16, 16)); + v.default_duration_ns = Some(40_000_000); + let mut a = TrackSpec::new(TrackKind::Audio, "A_PCM/INT/LIT"); + a.audio = Some((48000.0, 1, Some(16))); + a.default_duration_ns = Some(10_000_000); + let mut s = TrackSpec::new(TrackKind::Subtitle, "S_TEXT/UTF8"); + s.language = Some("fre".into()); + let opts = MuxOptions { lacing, ..Default::default() }; + let mut m = MkvWriter::new(Cursor::new(Vec::new()), vec![v, a, s], opts).unwrap(); + let mut expect = Vec::new(); + for i in 0..50i64 { + let vf = vec![i as u8; 100 + i as usize]; + m.write_frame(0, i * 40_000_000, i % 10 == 0, &vf, None).unwrap(); + expect.push((0usize, i * 40, vf, i % 10 == 0)); + for k in 0..4 { + let n = if lacing == LacingMode::FixedOrEbml { 960 } else { 900 + (i as usize * 7 + k * 13) % 300 }; + let af = vec![(i * 4 + k as i64) as u8; n]; + let t = i * 40 + k as i64 * 10; + m.write_frame(1, t * 1_000_000, true, &af, None).unwrap(); + expect.push((1, t, af, true)); + } + if i % 20 == 5 { + m.write_frame(2, i * 40_000_000, true, b"hello", Some(500_000_000)).unwrap(); + expect.push((2, i * 40, b"hello".to_vec(), true)); + } + } + let bytes = m.finish().unwrap().into_inner(); + let mut d = Demuxer::from_slice(&bytes).unwrap(); + assert!(d.file().warnings.is_empty(), "{:?}", d.file().warnings); + assert_eq!(d.tracks()[2].language, "fre"); + assert!(!d.file().cues.is_empty()); + assert_eq!(d.duration_ns(), Some(2_300_000_000)); + let got: Vec<_> = d.by_ref().map(|p| p.unwrap()).collect(); + assert_eq!(got.len(), expect.len()); + for (g, e) in got.iter().zip(&expect) { + assert_eq!((g.track, g.pts, &g.data, g.keyframe), (e.0, e.1, &e.2, e.3)); + } + let sub = got.iter().find(|p| p.track == 2).unwrap(); + assert_eq!(sub.duration, 500); + // the index agrees with the packet stream + let f = d.file(); + for (ti, t) in f.tracks.iter().enumerate() { + let pk: Vec<_> = got.iter().filter(|p| p.track == ti).collect(); + assert_eq!(t.samples.len(), pk.len()); + for (i, p) in pk.iter().enumerate() { + assert_eq!(f.read_sample(&bytes[..], ti, i).unwrap(), p.data); + assert_eq!(t.samples[i].pts, p.pts); + } + } + // seeking + let sp = d.seek(0, 1_230_000_000).unwrap(); + assert_eq!(sp.pts, 1200); + let p = d.next_packet().unwrap().unwrap(); + assert_eq!((p.track, p.pts, p.keyframe), (0, 1200, true)); + let sp = d.seek(1, 1_235_000_000).unwrap(); + assert_eq!(sp.pts, 1230); + let p = d.next_packet().unwrap().unwrap(); + assert_eq!((p.track, p.pts), (1, 1230)); + } + + #[test] + fn roundtrip_unlaced() { + roundtrip(LacingMode::None); + } + #[test] + fn roundtrip_xiph() { + roundtrip(LacingMode::Xiph); + } + #[test] + fn roundtrip_ebml() { + roundtrip(LacingMode::Ebml); + } + #[test] + fn roundtrip_fixed() { + roundtrip(LacingMode::FixedOrEbml); + } +} diff --git a/crates/matroska/src/source.rs b/crates/matroska/src/source.rs new file mode 100644 index 0000000..e6f056f --- /dev/null +++ b/crates/matroska/src/source.rs @@ -0,0 +1,173 @@ +//! Random-access byte sources for the demuxer. + +use std::cell::RefCell; +use std::io::{self, Read, Seek, SeekFrom}; +use std::sync::Arc; + +/// Random-access, read-only byte source (file, in-memory buffer, web Blob…). +/// +/// Same shape as `slidecraft_isobmff::ByteSource`, so one adapter can serve both demuxers. +pub trait ByteSource { + /// Total length in bytes. + fn len(&self) -> u64; + /// Fill `buf` entirely with bytes starting at `offset`; fail with `UnexpectedEof` if past the end. + fn read_at(&self, offset: u64, buf: &mut [u8]) -> io::Result<()>; + /// True if the source holds no bytes. + fn is_empty(&self) -> bool { + self.len() == 0 + } +} + +fn eof() -> io::Error { + io::Error::new(io::ErrorKind::UnexpectedEof, "read past end of byte source") +} + +impl ByteSource for [u8] { + fn len(&self) -> u64 { + <[u8]>::len(self) as u64 + } + fn read_at(&self, offset: u64, buf: &mut [u8]) -> io::Result<()> { + let start = usize::try_from(offset).map_err(|_| eof())?; + let end = start.checked_add(buf.len()).ok_or_else(eof)?; + let src = self.get(start..end).ok_or_else(eof)?; + buf.copy_from_slice(src); + Ok(()) + } +} + +impl ByteSource for Vec { + fn len(&self) -> u64 { + self.as_slice().len() as u64 + } + fn read_at(&self, offset: u64, buf: &mut [u8]) -> io::Result<()> { + self.as_slice().read_at(offset, buf) + } +} + +impl ByteSource for &T { + fn len(&self) -> u64 { + (**self).len() + } + fn read_at(&self, offset: u64, buf: &mut [u8]) -> io::Result<()> { + (**self).read_at(offset, buf) + } +} + +impl ByteSource for Arc { + fn len(&self) -> u64 { + (**self).len() + } + fn read_at(&self, offset: u64, buf: &mut [u8]) -> io::Result<()> { + (**self).read_at(offset, buf) + } +} + +impl ByteSource for Box { + fn len(&self) -> u64 { + (**self).len() + } + fn read_at(&self, offset: u64, buf: &mut [u8]) -> io::Result<()> { + (**self).read_at(offset, buf) + } +} + +#[cfg(not(target_arch = "wasm32"))] +impl ByteSource for std::fs::File { + fn len(&self) -> u64 { + self.metadata().map(|m| m.len()).unwrap_or(0) + } + #[cfg(unix)] + fn read_at(&self, offset: u64, buf: &mut [u8]) -> io::Result<()> { + std::os::unix::fs::FileExt::read_exact_at(self, buf, offset) + } + #[cfg(windows)] + fn read_at(&self, mut offset: u64, mut buf: &mut [u8]) -> io::Result<()> { + use std::os::windows::fs::FileExt; + while !buf.is_empty() { + let n = self.seek_read(buf, offset)?; + if n == 0 { + return Err(eof()); + } + offset += n as u64; + buf = &mut buf[n..]; + } + Ok(()) + } + #[cfg(not(any(unix, windows)))] + fn read_at(&self, offset: u64, buf: &mut [u8]) -> io::Result<()> { + let mut f = self; + f.seek(SeekFrom::Start(offset))?; + f.read_exact(buf) + } +} + +/// Adapts any `Read + Seek` (e.g. `BufReader`, `Cursor>`) into a [`ByteSource`]. +/// +/// The length is taken once at construction; the stream position is tracked so sequential reads +/// do not issue redundant seeks. +pub struct ReadSeekSource { + inner: RefCell<(R, u64)>, + len: u64, +} + +impl ReadSeekSource { + /// Wrap a reader; seeks to the end once to learn the length. + pub fn new(mut r: R) -> io::Result { + let len = r.seek(SeekFrom::End(0))?; + Ok(Self { inner: RefCell::new((r, len)), len }) + } + /// Unwrap the reader. + pub fn into_inner(self) -> R { + self.inner.into_inner().0 + } +} + +impl ByteSource for ReadSeekSource { + fn len(&self) -> u64 { + self.len + } + fn read_at(&self, offset: u64, buf: &mut [u8]) -> io::Result<()> { + if offset.checked_add(buf.len() as u64).is_none_or(|e| e > self.len) { + return Err(eof()); + } + let mut g = self.inner.borrow_mut(); + let (r, pos) = &mut *g; + if *pos != offset { + r.seek(SeekFrom::Start(offset))?; + *pos = offset; + } + let res = r.read_exact(buf); + match res { + Ok(()) => { + *pos = offset + buf.len() as u64; + Ok(()) + } + Err(e) => { + *pos = u64::MAX; // unknown; force a seek next time + Err(e) + } + } + } +} + +#[cfg(test)] +mod tests { + use super::*; + + #[test] + fn slice_and_reader_sources() { + let v = vec![1u8, 2, 3, 4]; + let mut b = [0u8; 2]; + v.read_at(1, &mut b).unwrap(); + assert_eq!(b, [2, 3]); + assert!(v.read_at(3, &mut b).is_err()); + assert!(v.read_at(u64::MAX, &mut b).is_err()); + let r = ReadSeekSource::new(io::Cursor::new(v.clone())).unwrap(); + assert_eq!(r.len(), 4); + r.read_at(2, &mut b).unwrap(); + assert_eq!(b, [3, 4]); + r.read_at(0, &mut b).unwrap(); + assert_eq!(b, [1, 2]); + assert!(r.read_at(3, &mut b).is_err()); + } +} diff --git a/crates/matroska/src/synthetic_tests.rs b/crates/matroska/src/synthetic_tests.rs new file mode 100644 index 0000000..e267f8c --- /dev/null +++ b/crates/matroska/src/synthetic_tests.rs @@ -0,0 +1,215 @@ +//! Hand-built files exercising features FFmpeg does not write: unknown-size Segment/Clusters, +//! header stripping, BlockGroups, Colour + mastering metadata, Chapters, Tags, Attachments, CRC-32. + +use crate::codec::Codec; +use crate::ebml::{self, write_id, write_unknown_size}; +use crate::ids::*; +use crate::mux::{el, el_float, el_str, el_uint}; +use crate::track::ContentEncoding; +use crate::{Demuxer, OpenOptions, Packet, open_with}; + +fn block(track: u8, rel: i16, flags: u8, data: &[u8]) -> Vec { + let mut b = vec![0x80 | track]; + b.extend_from_slice(&rel.to_be_bytes()); + b.push(flags); + b.extend_from_slice(data); + b +} + +fn synthetic() -> Vec { + let mut f = Vec::new(); + let mut h = Vec::new(); + el_str(&mut h, DOC_TYPE, "matroska"); + el_uint(&mut h, DOC_TYPE_VERSION, 4); + el(&mut f, EBML, &h); + write_id(&mut f, SEGMENT); + write_unknown_size(&mut f, 8); + // Info with a correct CRC-32 and a Void + let mut info = Vec::new(); + el_uint(&mut info, TIMESTAMP_SCALE, 1_000_000); + el_str(&mut info, TITLE, "Synthetic"); + el(&mut info, VOID, &[0; 3]); + let mut crc_info = Vec::new(); + el(&mut crc_info, CRC32, &ebml::crc32(&info).to_le_bytes()); + crc_info.extend_from_slice(&info); + el(&mut f, INFO, &crc_info); + // Tracks + let mut v = Vec::new(); + el_uint(&mut v, TRACK_NUMBER, 1); + el_uint(&mut v, TRACK_UID, 11); + el_uint(&mut v, TRACK_TYPE, 1); + el_str(&mut v, CODEC_ID, "V_MPEG4/ISO/AVC"); + el(&mut v, CODEC_PRIVATE, &[1, 0x64, 0, 0x1F]); + el_uint(&mut v, DEFAULT_DURATION, 40_000_000); + let mut mm = Vec::new(); + for (id, val) in + [(PRIMARY_R_X, 0.708), (PRIMARY_R_Y, 0.292), (PRIMARY_G_X, 0.17), (PRIMARY_G_Y, 0.797), (PRIMARY_B_X, 0.131), (PRIMARY_B_Y, 0.046)] + { + el_float(&mut mm, id, val); + } + el_float(&mut mm, WHITE_POINT_X, 0.3127); + el_float(&mut mm, WHITE_POINT_Y, 0.329); + el_float(&mut mm, LUMINANCE_MAX, 1000.0); + el_float(&mut mm, LUMINANCE_MIN, 0.0001); + let mut col = Vec::new(); + el_uint(&mut col, MATRIX_COEFFICIENTS, 9); + el_uint(&mut col, RANGE, 2); + el_uint(&mut col, TRANSFER_CHARACTERISTICS, 16); + el_uint(&mut col, PRIMARIES, 9); + el_uint(&mut col, MAX_CLL, 1000); + el_uint(&mut col, MAX_FALL, 400); + el(&mut col, MASTERING_METADATA, &mm); + let mut vid = Vec::new(); + el_uint(&mut vid, PIXEL_WIDTH, 1440); + el_uint(&mut vid, PIXEL_HEIGHT, 1080); + el_uint(&mut vid, DISPLAY_WIDTH, 1920); + el_uint(&mut vid, DISPLAY_HEIGHT, 1080); + el(&mut vid, COLOUR, &col); + // rectangular, a quarter turn clockwise (portrait phone video) + let mut proj = Vec::new(); + el_uint(&mut proj, PROJECTION_TYPE, 0); + el_float(&mut proj, PROJECTION_POSE_ROLL, -90.0); + el(&mut vid, PROJECTION, &proj); + el(&mut v, VIDEO, &vid); + // header stripping: every frame starts with 00 00 00 01 + let mut comp = Vec::new(); + el_uint(&mut comp, CONTENT_COMP_ALGO, 3); + el(&mut comp, CONTENT_COMP_SETTINGS, &[0, 0, 0, 1]); + let mut enc = Vec::new(); + el(&mut enc, CONTENT_COMPRESSION, &comp); + let mut encs = Vec::new(); + el(&mut encs, CONTENT_ENCODING, &enc); + el(&mut v, CONTENT_ENCODINGS, &encs); + let mut a = Vec::new(); + el_uint(&mut a, TRACK_NUMBER, 2); + el_uint(&mut a, TRACK_TYPE, 2); + el_str(&mut a, CODEC_ID, "A_AAC/MPEG4/LC/SBR"); + el_str(&mut a, LANGUAGE, "ger"); + let mut au = Vec::new(); + el_float(&mut au, SAMPLING_FREQUENCY, 24000.0); + el_float(&mut au, OUTPUT_SAMPLING_FREQUENCY, 48000.0); + el_uint(&mut au, CHANNELS, 2); + el(&mut a, AUDIO, &au); + let mut tr = Vec::new(); + el(&mut tr, TRACK_ENTRY, &v); + el(&mut tr, TRACK_ENTRY, &a); + el(&mut f, TRACKS, &tr); + // Chapters + let mut disp = Vec::new(); + el_str(&mut disp, CHAP_STRING, "Intro"); + let mut atom = Vec::new(); + el_uint(&mut atom, CHAPTER_UID, 5); + el_uint(&mut atom, CHAPTER_TIME_START, 0); + el_uint(&mut atom, CHAPTER_TIME_END, 40_000_000); + el(&mut atom, CHAPTER_DISPLAY, &disp); + let mut ed = Vec::new(); + el(&mut ed, CHAPTER_ATOM, &atom); + let mut ch = Vec::new(); + el(&mut ch, EDITION_ENTRY, &ed); + el(&mut f, CHAPTERS, &ch); + // Attachments + let mut af = Vec::new(); + el_str(&mut af, FILE_NAME, "font.ttf"); + el_str(&mut af, FILE_MEDIA_TYPE, "font/ttf"); + el_uint(&mut af, FILE_UID, 77); + el(&mut af, FILE_DATA, &[9; 100]); + let mut att = Vec::new(); + el(&mut att, ATTACHED_FILE, &af); + el(&mut f, ATTACHMENTS, &att); + // Cluster 1 (unknown size): keyframe SimpleBlock, BlockGroup with ReferenceBlock + duration + write_id(&mut f, CLUSTER); + write_unknown_size(&mut f, 1); + el_uint(&mut f, TIMESTAMP, 1000); + el(&mut f, SIMPLE_BLOCK, &block(1, 0, 0x80, &[0x65, 1, 2])); + el(&mut f, SIMPLE_BLOCK, &block(2, 5, 0x80, &[0xAA; 10])); + let mut g = Vec::new(); + el(&mut g, BLOCK, &block(1, 40, 0, &[0x41, 3])); + el(&mut g, REFERENCE_BLOCK, &[0xD8]); + el_uint(&mut g, BLOCK_DURATION, 80); + el(&mut f, BLOCK_GROUP, &g); + el(&mut f, SIMPLE_BLOCK, &block(9, 0, 0x80, &[1])); // unknown track: ignored + // Cluster 2 (unknown size), ended by Tags + write_id(&mut f, CLUSTER); + write_unknown_size(&mut f, 8); + el_uint(&mut f, TIMESTAMP, 1120); + el(&mut f, SIMPLE_BLOCK, &block(1, 0, 0x80, &[0x65, 4])); + let mut st = Vec::new(); + el_str(&mut st, TAG_NAME, "ARTIST"); + el_str(&mut st, TAG_STRING, "Nobody"); + let mut tg = Vec::new(); + let mut tgt = Vec::new(); + el_uint(&mut tgt, TAG_TRACK_UID, 11); + el(&mut tg, TARGETS, &tgt); + el(&mut tg, SIMPLE_TAG, &st); + let mut tags = Vec::new(); + el(&mut tags, TAG, &tg); + el(&mut f, TAGS, &tags); + f +} + +#[test] +fn synthetic_file() { + let bytes = synthetic(); + let f = open_with(&bytes[..], &OpenOptions { index: true, verify_crc: true }).unwrap(); + assert!(f.live); + assert!(f.warnings.is_empty(), "{:?}", f.warnings); + assert_eq!((f.crc_checked, f.crc_errors.len()), (1, 0)); + assert_eq!(f.info.title.as_deref(), Some("Synthetic")); + let v = &f.tracks[0]; + assert!(matches!(&v.codec, Codec::Avc { avcc } if avcc[1] == 0x64)); + let vi = v.video.as_ref().unwrap(); + assert_eq!(vi.pixel_aspect(), (4, 3)); + assert_eq!(vi.projection.as_ref().map(|p| p.roll), Some(-90.0)); + assert_eq!(vi.display_rotation(), Some(1)); + let c = vi.colour.as_ref().unwrap(); + assert!(c.full_range()); + assert_eq!( + (c.matrix_coefficients, c.transfer_characteristics, c.primaries, c.max_cll, c.max_fall), + (Some(9), Some(16), Some(9), Some(1000), Some(400)) + ); + let m = c.mastering.as_ref().unwrap(); + assert_eq!(m.primaries[1], (0.17, 0.797)); + assert_eq!(m.white_point, (0.3127, 0.329)); + assert_eq!((m.luminance_max, m.luminance_min), (1000.0, 0.0001)); + assert_eq!(v.encodings, vec![ContentEncoding::HeaderStripping { bytes: vec![0, 0, 0, 1], scope: 1 }]); + // legacy AAC ID: ASC synthesised with explicit SBR signalling (LC, 24 kHz, stereo → 48 kHz) + assert_eq!(f.tracks[1].codec, Codec::Aac { asc: vec![0x13, 0x10, 0x56, 0xE5, 0x98] }); + assert_eq!(f.tracks[1].language, "ger"); + assert_eq!(f.chapters[0].chapters[0].title(), Some("Intro")); + assert_eq!(f.chapters[0].chapters[0].end_ns, Some(40_000_000)); + assert_eq!(f.attachments.len(), 1); + let at = &f.attachments[0]; + assert_eq!((at.name.as_str(), at.media_type.as_str(), at.uid), ("font.ttf", "font/ttf", 77)); + assert_eq!(&bytes[at.data_offset as usize..][..100], &[9; 100]); + assert_eq!(f.tags[0].targets.track_uids, vec![11]); + assert_eq!(f.tags[0].simple[0].string.as_deref(), Some("Nobody")); + assert_eq!(f.clusters.len(), 2); + assert_eq!(v.samples.len(), 3); + assert_eq!(v.keyframes(), &[0, 2]); + + let d = Demuxer::from_slice(&bytes).unwrap(); + let p: Vec = d.map(|p| p.unwrap()).collect(); + assert_eq!(p.len(), 4); + assert_eq!((p[0].track, p[0].pts, p[0].keyframe, p[0].duration), (0, 1000, true, 40)); + assert_eq!(p[0].data, [0, 0, 0, 1, 0x65, 1, 2]); + assert_eq!((p[1].track, p[1].pts, p[1].data.len()), (1, 1005, 10)); + assert_eq!((p[2].pts, p[2].keyframe, p[2].duration), (1040, false, 80)); + assert_eq!(p[2].data, [0, 0, 0, 1, 0x41, 3]); + assert_eq!((p[3].pts, p[3].pts_ns), (1120, 1_120_000_000)); + assert_eq!(f.read_sample(&bytes[..], 0, 1).unwrap(), p[2].data); + + // CRC mismatch is reported, not fatal + let mut bad = bytes.clone(); + let title = bad.windows(9).position(|w| w == b"Synthetic").unwrap(); + bad[title] = b's'; + let f = open_with(&bad[..], &OpenOptions { index: true, verify_crc: true }).unwrap(); + assert_eq!(f.crc_errors.len(), 1); + + // seeking without index or cues builds the index + let mut d = Demuxer::with_options(&bytes[..], OpenOptions { index: false, verify_crc: false }).unwrap(); + assert_eq!(d.file().tracks.len(), 2); + assert_eq!(d.seek(0, 1_100_000_000).unwrap().pts, 1000); + assert_eq!(d.seek(0, 1_130_000_000).unwrap().pts, 1120); + assert_eq!(d.next_packet().unwrap().unwrap().pts, 1120); + assert!(d.file().indexed); +} diff --git a/crates/matroska/src/track.rs b/crates/matroska/src/track.rs new file mode 100644 index 0000000..4317d9a --- /dev/null +++ b/crates/matroska/src/track.rs @@ -0,0 +1,348 @@ +//! Track descriptions (RFC 9559 §5.1.4). + +use crate::codec::Codec; + +/// Track type (`TrackType`). +#[derive(Clone, Copy, Debug, PartialEq, Eq)] +pub enum TrackKind { + Video, + Audio, + Complex, + Logo, + Subtitle, + Buttons, + Control, + Metadata, + Other(u8), +} + +impl TrackKind { + pub(crate) fn from_type(t: u64) -> TrackKind { + match t { + 1 => TrackKind::Video, + 2 => TrackKind::Audio, + 3 => TrackKind::Complex, + 0x10 => TrackKind::Logo, + 0x11 => TrackKind::Subtitle, + 0x12 => TrackKind::Buttons, + 0x20 => TrackKind::Control, + 0x21 => TrackKind::Metadata, + n => TrackKind::Other(n as u8), + } + } +} + +/// SMPTE ST 2086 mastering display metadata (`MasteringMetadata`). Chromaticities are CIE 1931 xy, +/// luminance in cd/m². +#[derive(Clone, Debug, Default, PartialEq)] +pub struct MasteringMetadata { + /// Red, green, blue primaries as (x, y). + pub primaries: [(f64, f64); 3], + pub white_point: (f64, f64), + pub luminance_max: f64, + pub luminance_min: f64, +} + +/// `Colour` element. Code points use ITU-T H.273 values (as Matroska does). +#[derive(Clone, Debug, Default, PartialEq)] +pub struct Colour { + /// `MatrixCoefficients` (H.273; 2 = unspecified). + pub matrix_coefficients: Option, + pub bits_per_channel: Option, + pub chroma_subsampling_horz: Option, + pub chroma_subsampling_vert: Option, + pub cb_subsampling_horz: Option, + pub cb_subsampling_vert: Option, + pub chroma_siting_horz: Option, + pub chroma_siting_vert: Option, + /// `Range`: 0 unspecified, 1 broadcast (limited), 2 full, 3 defined by matrix/transfer. + pub range: Option, + /// `TransferCharacteristics` (H.273). + pub transfer_characteristics: Option, + /// `Primaries` (H.273). + pub primaries: Option, + /// Maximum content light level (cd/m²). + pub max_cll: Option, + /// Maximum frame-average light level (cd/m²). + pub max_fall: Option, + pub mastering: Option, +} + +impl Colour { + /// True if `Range` says full range. + pub fn full_range(&self) -> bool { + self.range == Some(2) + } +} + +/// `Video` element. +#[derive(Clone, Debug, Default, PartialEq)] +pub struct VideoInfo { + pub pixel_width: u32, + pub pixel_height: u32, + /// `DisplayWidth`/`DisplayHeight`, in `display_unit` (0 = pixels, 3 = display aspect ratio). + pub display_width: Option, + pub display_height: Option, + pub display_unit: u64, + /// Crop (top, bottom, left, right) in pixels. + pub crop: (u32, u32, u32, u32), + /// `FlagInterlaced`: 0 undetermined, 1 interlaced, 2 progressive. + pub flag_interlaced: u64, + /// `FieldOrder` (0 progressive, 1 tff, 2 undetermined, 6 bff, 9/14 interleaved). + pub field_order: Option, + pub stereo_mode: Option, + /// `AlphaMode`: 1 if BlockAdditions carry alpha. + pub alpha_mode: u64, + /// `ColourSpace` FourCC (uncompressed video). + pub colour_space: Option<[u8; 4]>, + pub colour: Option, + pub projection: Option, +} + +/// `Projection` element (RFC 9559 §5.1.4.1.28.41): pose angles in degrees. +#[derive(Clone, Debug, Default, PartialEq)] +pub struct Projection { + /// 0 rectangular, 1 equirectangular, 2 cubemap, 3 mesh. + pub projection_type: u64, + /// Clockwise around the up vector. + pub yaw: f64, + /// Counter-clockwise around the right vector. + pub pitch: f64, + /// Counter-clockwise around the forward vector. + pub roll: f64, +} + +impl VideoInfo { + /// Clockwise quarter turns (0–3) to display a rectangular video, from `ProjectionPoseRoll` + /// (a counter-clockwise angle, so −90 is a quarter turn clockwise). None for other + /// projections, flips (non-zero yaw / pitch) or angles that are not a multiple of 90°. + pub fn display_rotation(&self) -> Option { + let Some(p) = &self.projection else { return Some(0) }; + if p.projection_type != 0 || p.yaw != 0.0 || p.pitch != 0.0 || p.roll % 90.0 != 0.0 { + return None; + } + Some((-(p.roll / 90.0) as i64).rem_euclid(4) as u8) + } + + /// Pixel aspect ratio derived from the display size (pixels or aspect-ratio units), reduced. + pub fn pixel_aspect(&self) -> (u32, u32) { + let (pw, ph) = + (self.pixel_width.saturating_sub(self.crop.2 + self.crop.3) as u64, self.pixel_height.saturating_sub(self.crop.0 + self.crop.1) as u64); + let (Some(dw), Some(dh)) = (self.display_width, self.display_height) else { return (1, 1) }; + if pw == 0 || ph == 0 || dw == 0 || dh == 0 || !matches!(self.display_unit, 0 | 3) { + return (1, 1); + } + // PAR = (dw/dh) / (pw/ph) + let (n, d) = (dw as u64 * ph, dh as u64 * pw); + let g = gcd(n, d); + ((n / g) as u32, (d / g) as u32) + } +} + +pub(crate) fn gcd(mut a: u64, mut b: u64) -> u64 { + while b != 0 { + (a, b) = (b, a % b); + } + a.max(1) +} + +/// `Audio` element. +#[derive(Clone, Debug, PartialEq)] +pub struct AudioInfo { + /// `SamplingFrequency` (Hz). + pub sampling_frequency: f64, + /// `OutputSamplingFrequency` (e.g. SBR output rate). + pub output_sampling_frequency: Option, + pub channels: u64, + pub bit_depth: Option, +} + +impl Default for AudioInfo { + fn default() -> Self { + Self { sampling_frequency: 8000.0, output_sampling_frequency: None, channels: 1, bit_depth: None } + } +} + +/// One `ContentEncoding`. +#[derive(Clone, Debug, PartialEq)] +pub enum ContentEncoding { + /// `ContentCompAlgo` 3: the given bytes were stripped from the start of every frame. + HeaderStripping { bytes: Vec, scope: u64 }, + /// Other compression (0 zlib, 1 bzlib, 2 lzo1x): not supported for reading frame data. + Compression { algo: u64, settings: Vec, scope: u64 }, + /// Encryption (not supported for reading frame data). + Encryption { scope: u64 }, +} + +/// One entry of a track's sample (frame) table. +#[derive(Clone, Copy, Debug, PartialEq, Eq)] +pub struct Sample { + /// Byte offset of the (stored) frame data in the file. + pub offset: u64, + /// Stored size (excluding header-stripped bytes; see [`Track::sample_size`]). + pub size: u32, + /// Presentation timestamp in the track timebase (`TimestampScale` ticks). + pub pts: i64, + /// Duration in timebase ticks (from `BlockDuration` or `DefaultDuration`), 0 if unknown. + pub duration: u64, + pub keyframe: bool, + /// Index of the owning cluster in [`crate::MkvFile::clusters`]. + pub cluster: u32, + /// Offset of the block element (SimpleBlock / BlockGroup) header. + pub block_offset: u64, + /// Index of this frame within a laced block. + pub lace: u16, +} + +/// A Matroska track (`TrackEntry`) plus, once indexed, its sample table. +#[derive(Clone, Debug)] +pub struct Track { + pub number: u64, + pub uid: u64, + pub kind: TrackKind, + pub enabled: bool, + pub default: bool, + pub forced: bool, + pub hearing_impaired: bool, + pub lacing: bool, + /// `DefaultDuration` in nanoseconds. + pub default_duration_ns: Option, + pub name: String, + /// `Language` (ISO 639-2, default `eng`). + pub language: String, + pub language_bcp47: Option, + pub codec_id: String, + pub codec_private: Vec, + pub codec_name: String, + pub codec: Codec, + /// `CodecDelay` in nanoseconds. + pub codec_delay_ns: u64, + /// `SeekPreRoll` in nanoseconds. + pub seek_pre_roll_ns: u64, + pub video: Option, + pub audio: Option, + pub encodings: Vec, + /// Timebase numerator/denominator in seconds: `TimestampScale / 1e9`, reduced. + pub timebase: (u64, u64), + /// Frame table (empty unless the file was opened with indexing). + pub samples: Vec, + pub(crate) sync: Vec, +} + +impl Track { + /// Bytes prepended to every frame by header-stripping compression, if any. + pub fn stripped_header(&self) -> Option<&[u8]> { + self.encodings.iter().find_map(|e| match e { + ContentEncoding::HeaderStripping { bytes, scope } if scope & 1 != 0 => Some(bytes.as_slice()), + _ => None, + }) + } + + /// True if frames can be returned verbatim or with header stripping undone. + pub fn frames_readable(&self) -> bool { + self.encodings.iter().all(|e| match e { + ContentEncoding::HeaderStripping { .. } => true, + ContentEncoding::Compression { scope, .. } | ContentEncoding::Encryption { scope } => scope & 1 == 0, + }) + } + + /// Bytes to put in front of a stored frame of `stored_size` bytes to get the codec frame: + /// header-stripped bytes, or for `V_PRORES` the 8-byte frame header (`size` + `icpf`) that the + /// Matroska ProRes mapping omits. + pub fn frame_prefix(&self, stored_size: u64) -> Vec { + let mut p = self.stripped_header().map(<[u8]>::to_vec).unwrap_or_default(); + if matches!(self.codec, Codec::ProRes { .. }) { + let total = stored_size + p.len() as u64 + 8; + let mut h = (total as u32).to_be_bytes().to_vec(); + h.extend_from_slice(b"icpf"); + h.extend_from_slice(&p); + p = h; + } + p + } + + /// Frame size of sample `i` as returned by `read_sample` / packets (stored size plus prefix). + pub fn sample_size(&self, i: usize) -> Option { + let s = self.samples.get(i)?; + Some(s.size as u64 + self.frame_prefix(s.size as u64).len() as u64) + } + + /// Indices of keyframe samples, ascending by file order. + pub fn keyframes(&self) -> &[u32] { + &self.sync + } + + /// Timebase ticks → nanoseconds. + pub fn ticks_to_ns(&self, t: i64) -> i64 { + (t as i128 * self.timebase.0 as i128 * 1_000_000_000 / self.timebase.1 as i128) as i64 + } + + /// Nanoseconds → timebase ticks (floor). + pub fn ns_to_ticks(&self, ns: i64) -> i64 { + let n = ns as i128 * self.timebase.1 as i128; + let d = self.timebase.0 as i128 * 1_000_000_000; + n.div_euclid(d) as i64 + } + + /// Index of the last sample with `pts <= t` (in file order), or the first sample. + pub fn sample_at_pts(&self, t: i64) -> Option { + if self.samples.is_empty() { + return None; + } + let mut best: Option = None; + for (i, s) in self.samples.iter().enumerate() { + if s.pts <= t && best.is_none_or(|b| s.pts >= self.samples[b].pts) { + best = Some(i); + } + } + Some(best.unwrap_or(0)) + } + + /// Index of the keyframe with the greatest `pts <= t` (the first keyframe if none precedes `t`). + pub fn keyframe_before_pts(&self, t: i64) -> Option { + let mut best: Option = None; + for &k in &self.sync { + let s = &self.samples[k as usize]; + if s.pts <= t && best.is_none_or(|b| s.pts >= self.samples[b].pts) { + best = Some(k as usize); + } + } + best.or_else(|| self.sync.first().map(|&k| k as usize)) + } + + /// Index of the nearest keyframe at or before sample `index` in file order. + pub fn sync_sample_before(&self, index: usize) -> usize { + match self.sync.binary_search(&(index as u32)) { + Ok(i) => self.sync[i] as usize, + Err(0) => 0, + Err(i) => self.sync[i - 1] as usize, + } + } +} + +#[cfg(test)] +mod rotation_tests { + use super::{Projection, VideoInfo}; + + fn with_roll(projection_type: u64, yaw: f64, pitch: f64, roll: f64) -> VideoInfo { + VideoInfo { projection: Some(Projection { projection_type, yaw, pitch, roll }), ..Default::default() } + } + + #[test] + fn display_rotation_maps_rectangular_roll_to_clockwise_quarter_turns() { + assert_eq!(VideoInfo::default().display_rotation(), Some(0), "no Projection: as stored"); + // ProjectionPoseRoll is counter-clockwise: -90 is a quarter turn clockwise + for (roll, turns) in [(0.0, 0), (-90.0, 1), (180.0, 2), (-180.0, 2), (90.0, 3), (-270.0, 3), (270.0, 1), (360.0, 0)] { + assert_eq!(with_roll(0, 0.0, 0.0, roll).display_rotation(), Some(turns), "roll {roll}"); + } + } + + #[test] + fn display_rotation_leaves_non_rectangular_flipped_and_odd_angles_alone() { + assert_eq!(with_roll(1, 0.0, 0.0, -90.0).display_rotation(), None, "equirectangular"); + assert_eq!(with_roll(0, 180.0, 0.0, 0.0).display_rotation(), None, "yaw flip"); + assert_eq!(with_roll(0, 0.0, 180.0, 0.0).display_rotation(), None, "pitch flip"); + assert_eq!(with_roll(0, 0.0, 0.0, 45.0).display_rotation(), None, "not a quarter turn"); + assert_eq!(with_roll(0, 0.0, 0.0, f64::NAN).display_rotation(), None, "malformed angle"); + } +} diff --git a/crates/media/Cargo.toml b/crates/media/Cargo.toml new file mode 100644 index 0000000..0b57d48 --- /dev/null +++ b/crates/media/Cargo.toml @@ -0,0 +1,25 @@ +[package] +name = "slidecraft-media" +version.workspace = true +edition.workspace = true +license.workspace = true +description = "SlideCraft media: probing, audio decoding (MP3, AAC/M4A, WAV, AIFF, FLAC, ALAC, Vorbis, Opus) to f32 PCM, H.264/HEVC/VP9/AV1 video decoding, poster frames, trim/fade/volume and the playback mixer and clock" + +[lints] +workspace = true + +[dependencies] +slidecraft-isobmff = { workspace = true } +slidecraft-matroska = { workspace = true } +slidecraft-ogg = { workspace = true } +slidecraft-opus = { workspace = true } +slidecraft-h264 = { workspace = true } +slidecraft-hevc = { workspace = true } +slidecraft-vp9 = { workspace = true } +slidecraft-av1 = { workspace = true } +thiserror = { workspace = true } +log = { workspace = true } +image = { workspace = true } +# Audio decoding for the formats we have no decoder of our own for (MPL-2.0, used unmodified, +# as in FilmCraft). +symphonia = { version = "0.5.5", default-features = false, features = ["aac", "adpcm", "alac", "flac", "mp1", "mp2", "mp3", "pcm", "vorbis", "wav", "aiff", "caf", "ogg", "mkv", "isomp4"] } diff --git a/crates/media/src/audio.rs b/crates/media/src/audio.rs new file mode 100644 index 0000000..18c81c0 --- /dev/null +++ b/crates/media/src/audio.rs @@ -0,0 +1,287 @@ +//! Audio decoding to interleaved f32 PCM. +//! +//! Opus (Ogg, WebM / Matroska) uses our own decoder; everything else — MP3, AAC (M4A / MP4 / MOV / +//! ADTS), ALAC, FLAC, WAV, AIFF, CAF, Vorbis, PCM in MP4 / Matroska — goes through symphonia +//! (MPL-2.0, used unmodified), with gapless trimming on. + +use std::io::Cursor; + +use symphonia::core::audio::SampleBuffer; +use symphonia::core::codecs::{CODEC_TYPE_NULL, DecoderOptions}; +use symphonia::core::errors::Error as SymError; +use symphonia::core::formats::FormatOptions; +use symphonia::core::io::{MediaSourceStream, MediaSourceStreamOptions}; +use symphonia::core::meta::MetadataOptions; +use symphonia::core::probe::Hint; + +use crate::{Bytes, Container, MediaError, Result}; + +/// Decoded audio: interleaved f32 samples in −1..1. +#[derive(Clone, Debug, Default, PartialEq)] +pub struct Pcm { + pub rate: u32, + pub channels: u16, + pub samples: Vec, +} + +impl Pcm { + /// Sample frames (samples per channel). + pub fn frames(&self) -> usize { + self.samples.len() / self.channels.max(1) as usize + } + pub fn duration(&self) -> f64 { + self.frames() as f64 / self.rate.max(1) as f64 + } + /// Sample `frame` of channel `ch` (0 past the end). + pub fn at(&self, frame: usize, ch: usize) -> f32 { + let c = self.channels.max(1) as usize; + self.samples.get(frame * c + ch.min(c - 1)).copied().unwrap_or(0.0) + } + /// Root-mean-square level over all samples. + pub fn rms(&self) -> f32 { + if self.samples.is_empty() { + return 0.0; + } + (self.samples.iter().map(|s| s * s).sum::() / self.samples.len() as f32).sqrt() + } + + fn push_planar(planes: &[Vec], out: &mut Vec) { + let n = planes.iter().map(Vec::len).min().unwrap_or(0); + out.reserve(n * planes.len()); + for i in 0..n { + for p in planes { + out.push(p[i]); + } + } + } +} + +/// Bytes as a symphonia source without copying them. +struct Shared(Bytes); + +impl AsRef<[u8]> for Shared { + fn as_ref(&self) -> &[u8] { + &self.0 + } +} + +/// Decode the audio of `bytes` (an audio file, or the audio track of a video) to PCM. +pub fn decode(bytes: &Bytes) -> Result { + match Container::sniff(bytes) { + Container::Asf => Err(MediaError::Unsupported("Windows Media Audio (WMA)".into())), + Container::Ogg => match decode_ogg_opus(bytes) { + Some(r) => r, + None => decode_symphonia(bytes, "ogg"), + }, + Container::Matroska => match decode_mkv_opus(bytes) { + Some(r) => r, + None => decode_symphonia(bytes, "mkv"), + }, + c => decode_symphonia(bytes, c.name()), + } +} + +/// Ogg Opus with our decoder; `None` when the file holds no Opus stream. +fn decode_ogg_opus(bytes: &Bytes) -> Option> { + let src: &[u8] = bytes; + let file = slidecraft_ogg::open(src).ok()?; + let s = file.stream_of(slidecraft_ogg::Codec::Opus)?; + let stream = file.streams.get(s)?; + Some((|| { + let head = stream.headers.first().ok_or(MediaError::Corrupt("Opus header missing".into()))?; + let mut dec = slidecraft_opus::Decoder::new(head).map_err(|e| MediaError::Corrupt(e.to_string()))?; + dec.set_trim_pre_skip(true); + let channels = dec.channels() as u16; + let timing = slidecraft_ogg::OpusTiming::of(stream, dec.pre_skip() as u32); + let mut out = Vec::new(); + for i in 0..stream.packets.len() { + let Ok(p) = file.read_packet(src, s, i) else { continue }; + match dec.decode(Some(&p)) { + Ok(planes) => Pcm::push_planar(&planes, &mut out), + Err(e) => log::debug!("opus packet {i}: {e}"), + } + } + // End trimming (RFC 7845 §4.5). + let total = timing.total.max(0) as usize * channels.max(1) as usize; + if total > 0 && total < out.len() { + out.truncate(total); + } + Ok(Pcm { rate: 48_000, channels, samples: out }) + })()) +} + +/// WebM / Matroska Opus with our decoder; `None` when there is no Opus track. +fn decode_mkv_opus(bytes: &Bytes) -> Option> { + let src: &[u8] = bytes; + let file = slidecraft_matroska::open(src).ok()?; + let (ti, head) = file.tracks.iter().enumerate().find_map(|(i, t)| match &t.codec { + slidecraft_matroska::Codec::Opus { head } => Some((i, head.clone())), + _ => None, + })?; + Some((|| { + let mut dec = slidecraft_opus::Decoder::new(&head).map_err(|e| MediaError::Corrupt(e.to_string()))?; + dec.set_trim_pre_skip(true); + let channels = dec.channels() as u16; + let mut out = Vec::new(); + let n = file.tracks.get(ti).map(|t| t.samples.len()).unwrap_or(0); + for i in 0..n { + let Ok(p) = file.read_sample(src, ti, i) else { continue }; + match dec.decode(Some(&p)) { + Ok(planes) => Pcm::push_planar(&planes, &mut out), + Err(e) => log::debug!("opus packet {i}: {e}"), + } + } + Ok(Pcm { rate: 48_000, channels, samples: out }) + })()) +} + +fn sym_err(e: SymError) -> MediaError { + match e { + SymError::Unsupported(what) => MediaError::Unsupported(what.to_string()), + other => MediaError::Corrupt(other.to_string()), + } +} + +fn decode_symphonia(bytes: &Bytes, ext: &str) -> Result { + let mss = MediaSourceStream::new(Box::new(Cursor::new(Shared(bytes.clone()))), MediaSourceStreamOptions::default()); + let mut hint = Hint::new(); + match ext { + "unknown" => {} + "mp4" => { + hint.with_extension("mp4"); + } + e => { + hint.with_extension(e); + } + } + let opts = FormatOptions { enable_gapless: true, ..Default::default() }; + let probed = symphonia::default::get_probe().format(&hint, mss, &opts, &MetadataOptions::default()).map_err(|e| match e { + SymError::Unsupported(_) => MediaError::Unsupported("this audio format".into()), + other => sym_err(other), + })?; + let mut format = probed.format; + let codecs = symphonia::default::get_codecs(); + // The first track we can decode (a video's audio track follows its video track). + let (track_id, mut decoder) = format + .tracks() + .iter() + .filter(|t| t.codec_params.codec != CODEC_TYPE_NULL) + .find_map(|t| codecs.make(&t.codec_params, &DecoderOptions::default()).ok().map(|d| (t.id, d))) + .ok_or(MediaError::Missing("audio track we can decode"))?; + let mut out: Vec = Vec::new(); + let mut rate = 0; + let mut channels = 0u16; + let mut buf: Option> = None; + loop { + let packet = match format.next_packet() { + Ok(p) => p, + Err(SymError::IoError(_)) | Err(SymError::ResetRequired) => break, + Err(SymError::DecodeError(e)) => { + log::debug!("audio demux: {e}"); + continue; + } + Err(e) => { + if out.is_empty() { + return Err(sym_err(e)); + } + break; + } + }; + if packet.track_id() != track_id { + continue; + } + match decoder.decode(&packet) { + Ok(decoded) => { + let spec = *decoded.spec(); + let ch = spec.channels.count() as u16; + if rate == 0 { + rate = spec.rate; + channels = ch; + } + if ch != channels || decoded.frames() == 0 { + continue; + } + let need = decoded.capacity() as u64; + if buf.as_ref().is_none_or(|b| (b.capacity() as u64) < need * ch as u64) { + buf = Some(SampleBuffer::::new(need, spec)); + } + if let Some(b) = buf.as_mut() { + b.copy_interleaved_ref(decoded); + out.extend_from_slice(b.samples()); + } + } + Err(SymError::DecodeError(e)) => log::debug!("audio decode: {e}"), + Err(SymError::IoError(_)) => continue, + Err(e) => { + if out.is_empty() { + return Err(sym_err(e)); + } + break; + } + } + } + if rate == 0 { + return Err(MediaError::Corrupt("no audio could be decoded".into())); + } + Ok(Pcm { rate, channels: channels.max(1), samples: out }) +} + +/// Sample rate, channels and frame count from symphonia's container probe (no decoding). +pub(crate) fn probe_symphonia(bytes: &Bytes, ext: &str) -> Option<(String, u32, u16, Option)> { + let mss = MediaSourceStream::new(Box::new(Cursor::new(Shared(bytes.clone()))), MediaSourceStreamOptions::default()); + let mut hint = Hint::new(); + if ext != "unknown" { + hint.with_extension(ext); + } + let opts = FormatOptions { enable_gapless: true, ..Default::default() }; + let probed = symphonia::default::get_probe().format(&hint, mss, &opts, &MetadataOptions::default()).ok()?; + let codecs = symphonia::default::get_codecs(); + let t = probed.format.tracks().iter().find(|t| t.codec_params.codec != CODEC_TYPE_NULL)?; + let p = &t.codec_params; + let name = codecs.get_codec(p.codec).map(|d| d.short_name.to_string()).unwrap_or_else(|| "unknown".into()); + Some((name, p.sample_rate.unwrap_or(0), p.channels.map(|c| c.count() as u16).unwrap_or(0), p.n_frames)) +} + +/// Interleaved PCM resampled (linear) to `rate`. +pub fn resample(pcm: &Pcm, rate: u32) -> Pcm { + if pcm.rate == rate || pcm.rate == 0 || rate == 0 { + return pcm.clone(); + } + let c = pcm.channels.max(1) as usize; + let n_in = pcm.frames(); + let n_out = (n_in as u64 * rate as u64 / pcm.rate as u64) as usize; + let step = pcm.rate as f64 / rate as f64; + let mut out = Vec::with_capacity(n_out * c); + for i in 0..n_out { + let x = i as f64 * step; + let k = x as usize; + let f = (x - k as f64) as f32; + for ch in 0..c { + let a = pcm.at(k, ch); + let b = if k + 1 < n_in { pcm.at(k + 1, ch) } else { a }; + out.push(a + (b - a) * f); + } + } + Pcm { rate, channels: pcm.channels, samples: out } +} + +#[cfg(test)] +mod tests { + use super::*; + + #[test] + fn resample_keeps_duration_and_shape() { + let pcm = Pcm { rate: 100, channels: 1, samples: (0..100).map(|i| i as f32 / 100.0).collect() }; + let r = resample(&pcm, 200); + assert_eq!(r.frames(), 200); + assert!((r.duration() - 1.0).abs() < 1e-9); + assert!((r.at(101, 0) - 0.505).abs() < 1e-3); + } + + #[test] + fn wma_is_reported_unsupported() { + let mut b = crate::ASF_HEADER.to_vec(); + b.extend_from_slice(&[0; 64]); + assert!(matches!(decode(&std::sync::Arc::new(b)), Err(MediaError::Unsupported(_)))); + } +} diff --git a/crates/media/src/clip.rs b/crates/media/src/clip.rs new file mode 100644 index 0000000..12d42d1 --- /dev/null +++ b/crates/media/src/clip.rs @@ -0,0 +1,104 @@ +//! Trim, fade and volume: the playback range of a clip and its gain over time. + +/// How a clip plays (PowerPoint's Playback tab). Times in seconds. +#[derive(Clone, Copy, Debug, PartialEq)] +pub struct ClipParams { + /// Trimmed from the start. + pub trim_start: f64, + /// Trimmed from the end. + pub trim_end: f64, + pub fade_in: f64, + pub fade_out: f64, + /// 0..1. + pub volume: f32, +} + +impl Default for ClipParams { + fn default() -> Self { + ClipParams { trim_start: 0.0, trim_end: 0.0, fade_in: 0.0, fade_out: 0.0, volume: 1.0 } + } +} + +impl ClipParams { + /// From the model's millisecond fields. + pub fn from_ms(trim_start: u32, trim_end: u32, fade_in: u32, fade_out: u32, volume: f64) -> ClipParams { + ClipParams { + trim_start: trim_start as f64 / 1000.0, + trim_end: trim_end as f64 / 1000.0, + fade_in: fade_in as f64 / 1000.0, + fade_out: fade_out as f64 / 1000.0, + volume: volume.clamp(0.0, 1.0) as f32, + } + } + + /// The played range `[start, end)` in media seconds for a clip `duration` long. Trims that + /// would leave nothing are ignored (the whole clip plays); an unknown duration (0) gives an + /// open end (`f64::INFINITY`). + pub fn range(&self, duration: f64) -> (f64, f64) { + if duration.is_nan() || duration <= 0.0 { + return (self.trim_start.max(0.0), f64::INFINITY); + } + let start = self.trim_start.clamp(0.0, duration); + let end = (duration - self.trim_end.max(0.0)).clamp(0.0, duration); + if end - start < 0.01 { (0.0, duration) } else { (start, end) } + } + + /// Gain at media time `t`: volume × fade-in ramp × fade-out ramp (linear). Fades longer than + /// half the played range are shortened so they never overlap. + pub fn gain(&self, t: f64, duration: f64) -> f32 { + let (start, end) = self.range(duration); + let len = end - start; + let half = if len.is_finite() { len / 2.0 } else { f64::INFINITY }; + let fi = self.fade_in.max(0.0).min(half); + let fo = self.fade_out.max(0.0).min(half); + let mut g = 1.0; + if fi > 0.0 { + g *= ((t - start) / fi).clamp(0.0, 1.0); + } + if fo > 0.0 && end.is_finite() { + g *= ((end - t) / fo).clamp(0.0, 1.0); + } + self.volume * g as f32 + } +} + +#[cfg(test)] +mod tests { + use super::*; + + #[test] + fn range_applies_trims() { + let c = ClipParams { trim_start: 1.0, trim_end: 2.0, ..Default::default() }; + assert_eq!(c.range(10.0), (1.0, 8.0)); + // Trims longer than the clip are ignored. + let c = ClipParams { trim_start: 6.0, trim_end: 6.0, ..Default::default() }; + assert_eq!(c.range(10.0), (0.0, 10.0)); + // Unknown duration: open end. + assert_eq!(ClipParams::default().range(0.0), (0.0, f64::INFINITY)); + } + + #[test] + fn gain_fades_and_volume() { + let c = ClipParams { trim_start: 1.0, fade_in: 2.0, fade_out: 1.0, volume: 0.5, ..Default::default() }; + let d = 10.0; + assert_eq!(c.gain(1.0, d), 0.0); + assert!((c.gain(2.0, d) - 0.25).abs() < 1e-6); + assert!((c.gain(5.0, d) - 0.5).abs() < 1e-6); + assert!((c.gain(9.5, d) - 0.25).abs() < 1e-6); + assert_eq!(c.gain(10.0, d), 0.0); + } + + #[test] + fn long_fades_never_overlap() { + let c = ClipParams { fade_in: 5.0, fade_out: 5.0, ..Default::default() }; + // 2 s clip: each fade is capped at 1 s, so the middle reaches full volume. + assert!((c.gain(1.0, 2.0) - 1.0).abs() < 1e-6); + assert!((c.gain(0.5, 2.0) - 0.5).abs() < 1e-6); + } + + #[test] + fn from_ms_converts() { + let c = ClipParams::from_ms(1500, 250, 0, 1000, 2.0); + assert_eq!((c.trim_start, c.trim_end, c.fade_out, c.volume), (1.5, 0.25, 1.0, 1.0)); + } +} diff --git a/crates/media/src/feed.rs b/crates/media/src/feed.rs new file mode 100644 index 0000000..b430995 --- /dev/null +++ b/crates/media/src/feed.rs @@ -0,0 +1,180 @@ +//! Video frames for a playback clock: decoding runs ahead on a worker thread (inline on wasm), and +//! [`VideoFeed::frame_at`] hands out the frame due at the clock's time, seeking when the clock +//! jumps (seek, loop). + +use std::sync::Arc; + +use crate::video::{Frame, VideoDecoder}; +use crate::{Bytes, Result}; + +/// Frames decoded ahead of the clock. +#[cfg(not(target_arch = "wasm32"))] +const AHEAD: usize = 6; + +/// A jump forward larger than this (seconds) seeks instead of decoding through. +const MAX_CATCH_UP: f64 = 1.5; + +#[cfg(not(target_arch = "wasm32"))] +enum Cmd { + Seek(f64, u64), +} + +pub struct VideoFeed { + size: (u32, u32), + current: Option>, + next: Option>, + generation: u64, + ended: bool, + #[cfg(not(target_arch = "wasm32"))] + tx: std::sync::mpsc::Sender, + #[cfg(not(target_arch = "wasm32"))] + rx: std::sync::mpsc::Receiver<(u64, Option)>, + #[cfg(target_arch = "wasm32")] + dec: VideoDecoder, +} + +impl VideoFeed { + pub fn new(bytes: Bytes) -> Result { + let dec = VideoDecoder::open(bytes)?; + let size = dec.size(); + #[cfg(not(target_arch = "wasm32"))] + { + let (tx, cmd_rx) = std::sync::mpsc::channel::(); + let (frame_tx, rx) = std::sync::mpsc::sync_channel::<(u64, Option)>(AHEAD); + let worker = move || worker(dec, cmd_rx, frame_tx); + if let Err(e) = std::thread::Builder::new().name("slidecraft-video-decode".into()).spawn(worker) { + return Err(crate::MediaError::Corrupt(format!("couldn't start the decoder: {e}"))); + } + Ok(VideoFeed { size, current: None, next: None, generation: 0, ended: false, tx, rx }) + } + #[cfg(target_arch = "wasm32")] + { + Ok(VideoFeed { size, current: None, next: None, generation: 0, ended: false, dec }) + } + } + + pub fn size(&self) -> (u32, u32) { + self.size + } + + /// Restart decoding at `t`. + pub fn seek(&mut self, t: f64) { + self.generation += 1; + self.current = None; + self.next = None; + self.ended = false; + #[cfg(not(target_arch = "wasm32"))] + { + let _ = self.tx.send(Cmd::Seek(t, self.generation)); + // Unblock the worker if it's waiting to hand over a stale frame. + while self.rx.try_recv().is_ok() {} + } + #[cfg(target_arch = "wasm32")] + self.dec.seek(t); + } + + /// The next decoded frame, if one is ready (`Err(())` at the end of the stream). + fn pull(&mut self) -> std::result::Result>, ()> { + #[cfg(not(target_arch = "wasm32"))] + loop { + match self.rx.try_recv() { + Ok((g, _)) if g != self.generation => continue, + Ok((_, Some(f))) => return Ok(Some(Arc::new(f))), + Ok((_, None)) => return Err(()), + Err(std::sync::mpsc::TryRecvError::Empty) => return Ok(None), + Err(std::sync::mpsc::TryRecvError::Disconnected) => return Err(()), + } + } + #[cfg(target_arch = "wasm32")] + match self.dec.next_frame() { + Ok(Some(f)) => Ok(Some(Arc::new(f))), + _ => Err(()), + } + } + + /// The frame on screen at media time `t` (seconds): the last one whose time is ≤ `t`, or the + /// first one while nothing earlier exists. `None` until the first frame has decoded. + pub fn frame_at(&mut self, t: f64) -> Option> { + let behind = self.current.as_ref().is_some_and(|c| t + 0.02 < c.time); + let far = self.next.as_ref().or(self.current.as_ref()).is_some_and(|c| t > c.time + MAX_CATCH_UP); + if behind || far { + self.seek(t); + } + loop { + if self.next.is_none() && !self.ended { + match self.pull() { + Ok(Some(f)) => self.next = Some(f), + Ok(None) => break, + Err(()) => self.ended = true, + } + } + match &self.next { + Some(n) if n.time <= t + 1e-3 || self.current.is_none() => { + self.current = self.next.take(); + } + _ => break, + } + } + self.current.clone() + } + + /// The decoder has delivered its last frame. + pub fn ended(&self) -> bool { + self.ended && self.next.is_none() + } + + /// Wait (up to `timeout`) for the frame at `t` — for tests and offline rendering. + pub fn frame_at_blocking(&mut self, t: f64, timeout: std::time::Duration) -> Option> { + let mut waited = std::time::Duration::ZERO; + loop { + let f = self.frame_at(t); + let done = self.ended || self.next.is_some(); + if done || waited >= timeout || !crate::THREADS { + return f; + } + std::thread::sleep(std::time::Duration::from_millis(2)); + waited += std::time::Duration::from_millis(2); + } + } +} + +#[cfg(not(target_arch = "wasm32"))] +fn worker(mut dec: VideoDecoder, cmds: std::sync::mpsc::Receiver, frames: std::sync::mpsc::SyncSender<(u64, Option)>) { + let mut generation = 0u64; + let mut at_end = false; + loop { + // Commands first; block on them once the stream has ended. + let cmd = if at_end { + match cmds.recv() { + Ok(c) => Some(c), + Err(_) => return, + } + } else { + match cmds.try_recv() { + Ok(c) => Some(c), + Err(std::sync::mpsc::TryRecvError::Empty) => None, + Err(std::sync::mpsc::TryRecvError::Disconnected) => return, + } + }; + if let Some(Cmd::Seek(t, g)) = cmd { + // Only the latest seek matters. + let (mut t, mut g) = (t, g); + while let Ok(Cmd::Seek(t2, g2)) = cmds.try_recv() { + (t, g) = (t2, g2); + } + dec.seek(t); + generation = g; + } + let frame = match dec.next_frame() { + Ok(f) => f, + Err(e) => { + log::debug!("video decode stopped: {e}"); + None + } + }; + at_end = frame.is_none(); + if frames.send((generation, frame)).is_err() { + return; + } + } +} diff --git a/crates/media/src/lib.rs b/crates/media/src/lib.rs new file mode 100644 index 0000000..91a9241 --- /dev/null +++ b/crates/media/src/lib.rs @@ -0,0 +1,127 @@ +//! SlideCraft media: everything between embedded media bytes and the speakers / the screen. +//! +//! - [`probe`]: container, duration, audio channels / sample rate, video size / frame rate. +//! - [`audio::decode`]: every common audio format to interleaved f32 [`audio::Pcm`] — MP3, AAC +//! (M4A, ADTS), ALAC, WAV, AIFF, CAF, FLAC, Ogg Vorbis, Opus (Ogg / WebM / Matroska) and the audio +//! track of MP4/MOV/WebM videos. WMA (ASF) is recognised and probed but not decoded. +//! - [`video::VideoDecoder`]: H.264, HEVC, VP9 and AV1 in MP4/MOV and WebM/Matroska to RGBA +//! [`video::Frame`]s (our own decoders, ported from FilmCraft); [`video::poster_png`]. +//! - [`clip::ClipParams`]: trim, fade in/out and volume. +//! - [`player::Player`]: the playback mixer and clock the UI host drives, over an +//! [`player::AudioOut`] it injects (cpal on desktop; none on the web or in tests, where the clock +//! runs on wall time); [`feed::VideoFeed`] decodes video ahead of that clock. +//! +//! Layer L2: depends only on the codec crates, never on the model or the UI. Builds for +//! `wasm32-unknown-unknown` (no threads there: decoding runs inline). +#![deny(clippy::unwrap_used, clippy::expect_used, clippy::panic, clippy::unimplemented, clippy::todo, clippy::unreachable)] +#![forbid(unsafe_code)] + +pub mod audio; +pub mod clip; +pub mod feed; +pub mod player; +pub mod probe; +pub mod video; +mod yuv; + +use std::sync::Arc; + +pub use audio::Pcm; +pub use clip::ClipParams; +pub use feed::VideoFeed; +pub use player::{AudioOut, PlayState, Player, VoiceSpec, VoiceStatus}; +pub use probe::{AudioInfo, MediaInfo, VideoInfo, probe}; +pub use video::{Frame, VideoDecoder, poster_frame, poster_png}; + +/// Shared media bytes (the model's `MediaItem::data`). +pub type Bytes = Arc>; + +#[derive(Debug, Clone, PartialEq, thiserror::Error)] +pub enum MediaError { + #[error("{0} isn't supported")] + Unsupported(String), + #[error("the media can't be read: {0}")] + Corrupt(String), + #[error("the file has no {0}")] + Missing(&'static str), +} + +pub type Result = std::result::Result; + +/// Container formats recognised from the first bytes. +#[derive(Clone, Copy, Debug, PartialEq, Eq)] +pub enum Container { + /// ISO BMFF: MP4, M4A, MOV, 3GP. + Mp4, + /// Matroska / WebM. + Matroska, + Ogg, + Wav, + Aiff, + Caf, + Flac, + Mp3, + /// AAC in ADTS frames (`.aac`). + Adts, + /// Advanced Systems Format (WMA / WMV). + Asf, + Unknown, +} + +/// The ASF header object GUID (30 26 B2 75 8E 66 CF 11 A6 D9 00 AA 00 62 CE 6C). +pub(crate) const ASF_HEADER: [u8; 16] = [0x30, 0x26, 0xB2, 0x75, 0x8E, 0x66, 0xCF, 0x11, 0xA6, 0xD9, 0x00, 0xAA, 0x00, 0x62, 0xCE, 0x6C]; + +impl Container { + pub fn sniff(b: &[u8]) -> Container { + let at = |o: usize, m: &[u8]| b.get(o..o + m.len()) == Some(m); + if at(4, b"ftyp") || at(4, b"moov") || at(4, b"mdat") || at(4, b"wide") || at(4, b"free") { + Container::Mp4 + } else if at(0, &[0x1A, 0x45, 0xDF, 0xA3]) { + Container::Matroska + } else if at(0, b"OggS") { + Container::Ogg + } else if (at(0, b"RIFF") || at(0, b"RF64")) && at(8, b"WAVE") { + Container::Wav + } else if at(0, b"FORM") && (at(8, b"AIFF") || at(8, b"AIFC")) { + Container::Aiff + } else if at(0, b"caff") { + Container::Caf + } else if at(0, b"fLaC") { + Container::Flac + } else if at(0, &ASF_HEADER) { + Container::Asf + } else if at(0, b"ID3") { + // ID3v2 is followed by MP3 frames (or, rarely, ADTS). + let size = b.get(6..10).map(|s| s.iter().fold(0usize, |a, &x| (a << 7) | (x & 0x7f) as usize)).unwrap_or(0); + match Container::sniff(b.get(10 + size..).unwrap_or(&[])) { + Container::Adts => Container::Adts, + _ => Container::Mp3, + } + } else if b.len() > 1 && b[0] == 0xFF && b[1] & 0xF0 == 0xF0 && b[1] & 0x06 == 0 { + Container::Adts + } else if b.len() > 1 && b[0] == 0xFF && b[1] & 0xE0 == 0xE0 && b[1] & 0x06 != 0 { + Container::Mp3 + } else { + Container::Unknown + } + } + + pub fn name(self) -> &'static str { + match self { + Container::Mp4 => "mp4", + Container::Matroska => "matroska", + Container::Ogg => "ogg", + Container::Wav => "wav", + Container::Aiff => "aiff", + Container::Caf => "caf", + Container::Flac => "flac", + Container::Mp3 => "mp3", + Container::Adts => "adts", + Container::Asf => "asf", + Container::Unknown => "unknown", + } + } +} + +/// Worker threads are available (not on wasm32). +pub(crate) const THREADS: bool = !cfg!(target_arch = "wasm32"); diff --git a/crates/media/src/player.rs b/crates/media/src/player.rs new file mode 100644 index 0000000..9a07e6f --- /dev/null +++ b/crates/media/src/player.rs @@ -0,0 +1,505 @@ +//! The playback mixer and clock. +//! +//! The UI host owns a [`Player`] and an optional [`AudioOut`] (cpal on desktop). Each playing +//! media shape is a *voice* with its own clock (`position`, in media seconds), advanced by the +//! audio callback as it pulls samples — so video frames synced to it stay in step with what is +//! heard. Without an output device (web, CI, no sound card) [`Player::tick`] advances the clocks on +//! wall time instead, so playback, looping and video still work silently. +//! +//! Audio is decoded once per media item (on a worker thread where there are threads) and cached; +//! a voice whose audio is still decoding holds at its start (`Loading`), one whose audio can't be +//! decoded runs silently. + +use std::collections::HashMap; +use std::sync::{Arc, Mutex, OnceLock}; + +use crate::{Bytes, ClipParams, Pcm}; + +/// An audio output device. `fill(buffer, channels, sample_rate)` is called from the device's +/// thread to fill an interleaved f32 buffer. +pub trait AudioOut { + fn start(&mut self, fill: Box) -> Result<(), String>; + fn stop(&mut self); +} + +#[derive(Clone, Copy, Debug, PartialEq, Eq)] +pub enum PlayState { + /// Waiting for the audio to decode. + Loading, + Playing, + Paused, + /// Reached the end (not looping). + Ended, +} + +impl PlayState { + pub fn name(self) -> &'static str { + match self { + PlayState::Loading => "loading", + PlayState::Playing => "playing", + PlayState::Paused => "paused", + PlayState::Ended => "ended", + } + } +} + +/// What to play. +#[derive(Clone, Copy, Debug, PartialEq)] +pub struct VoiceSpec { + pub clip: ClipParams, + pub looping: bool, + /// Media duration in seconds from the probe (0 = unknown: taken from the decoded audio). + pub duration: f64, + /// Decode and mix the audio (false for videos without sound, or muted). + pub audio: bool, +} + +#[derive(Clone, Debug, PartialEq)] +pub struct VoiceStatus { + pub state: PlayState, + /// Media time in seconds. + pub position: f64, + pub duration: f64, + /// Why the audio is silent, if it couldn't be decoded. + pub error: Option, +} + +type Slot = Arc, String>>>; + +struct Voice { + key: u64, + audio: Option, + spec: VoiceSpec, + t: f64, + state: PlayState, +} + +impl Voice { + fn duration(&self) -> f64 { + if self.spec.duration > 0.0 { + return self.spec.duration; + } + match self.audio.as_ref().and_then(|s| s.get()) { + Some(Ok(p)) => p.duration(), + _ => 0.0, + } + } + fn pcm(&self) -> Option<&Arc> { + self.audio.as_ref().and_then(|s| s.get()).and_then(|r| r.as_ref().ok()) + } + fn loading(&self) -> bool { + self.audio.as_ref().is_some_and(|s| s.get().is_none()) + } +} + +/// The voices, shared with the audio callback. +#[derive(Default)] +pub struct Mixer { + voices: Vec, +} + +impl Mixer { + /// Mix `frames` frames at `rate` into `out` (interleaved, `channels` wide; `None`: advance the + /// clocks only). + pub fn mix(&mut self, mut out: Option<&mut [f32]>, frames: usize, channels: usize, rate: u32) { + if let Some(o) = out.as_deref_mut() { + o.fill(0.0); + } + let step = 1.0 / rate.max(1) as f64; + let channels = channels.max(1); + for v in &mut self.voices { + if v.state != PlayState::Playing || v.loading() { + continue; + } + let dur = v.duration(); + let (start, end) = v.spec.clip.range(dur); + if v.t < start { + v.t = start; + } + let pcm = v.pcm().cloned(); + match (&mut out, pcm) { + (Some(o), Some(pcm)) => { + let src_ch = pcm.channels.max(1) as usize; + let n_src = pcm.frames(); + for f in 0..frames { + if v.t >= end { + if v.spec.looping { + v.t = start; + } else { + v.state = PlayState::Ended; + v.t = end; + break; + } + } + let g = v.spec.clip.gain(v.t, dur); + let x = v.t * pcm.rate as f64; + let k = x as usize; + if g > 0.0 && k < n_src { + let fr = (x - k as f64) as f32; + for c in 0..channels { + let sc = if src_ch == 1 { + 0 + } else if c < src_ch { + c + } else { + continue; + }; + let a = pcm.at(k, sc); + let b = if k + 1 < n_src { pcm.at(k + 1, sc) } else { a }; + if let Some(s) = o.get_mut(f * channels + c) { + *s += (a + (b - a) * fr) * g; + } + } + } + v.t += step; + } + } + _ => { + // Silent: just run the clock. + let mut left = frames as f64 * step; + while left > 0.0 { + let room = end - v.t; + if left < room { + v.t += left; + break; + } + left -= room.max(0.0); + if v.spec.looping && end.is_finite() && end - start > 1e-3 { + v.t = start; + } else { + v.t = end; + v.state = PlayState::Ended; + break; + } + } + } + } + } + if let Some(o) = out { + for s in o.iter_mut() { + *s = s.clamp(-1.0, 1.0); + } + } + } +} + +/// Default mix rate when no device says otherwise. +const DEFAULT_RATE: u32 = 48_000; + +pub struct Player { + out: Option>, + streaming: bool, + mixer: Arc>, + last_tick: Option, + idle_since: Option, + cache: HashMap, +} + +impl Player { + /// A player over `out` (`None`: silent, clocked by [`Player::tick`]). + pub fn new(out: Option>) -> Player { + Player { out, streaming: false, mixer: Arc::new(Mutex::new(Mixer::default())), last_tick: None, idle_since: None, cache: HashMap::new() } + } + + pub fn has_output(&self) -> bool { + self.out.is_some() + } + + fn with(&self, f: impl FnOnce(&mut Mixer) -> R) -> R { + let mut g = self.mixer.lock().unwrap_or_else(|e| e.into_inner()); + f(&mut g) + } + + /// The decoded audio of `bytes`, cached by `media_key` (decoding starts now). + fn slot(&mut self, media_key: u64, bytes: &Bytes) -> Slot { + if self.cache.len() > 16 { + // Forget audio no voice uses. + let used: Vec = self.cache.iter().filter(|(_, s)| Arc::strong_count(s) > 1).map(|(k, _)| *k).collect(); + self.cache.retain(|k, _| used.contains(k)); + } + self.cache + .entry(media_key) + .or_insert_with(|| { + let slot: Slot = Arc::new(OnceLock::new()); + let (s, b) = (slot.clone(), bytes.clone()); + let job = move || { + let _ = s.set(crate::audio::decode(&b).map(Arc::new).map_err(|e| e.to_string())); + }; + if crate::THREADS { + if std::thread::Builder::new().name("slidecraft-audio-decode".into()).spawn(job).is_err() { + let _ = slot.set(Err("couldn't start the decoder".into())); + } + } else { + job(); + } + slot + }) + .clone() + } + + /// Play voice `key` (a shape) from `from` seconds (default: the trimmed start). Resumes a + /// paused voice when `from` is `None`. + pub fn play(&mut self, key: u64, media_key: u64, bytes: &Bytes, spec: VoiceSpec, from: Option) { + let audio = spec.audio.then(|| self.slot(media_key, bytes)); + self.with(|m| { + if let Some(v) = m.voices.iter_mut().find(|v| v.key == key) { + v.spec = spec; + if v.audio.is_none() { + v.audio = audio; + } + if let Some(t) = from { + v.t = t; + } else if v.state == PlayState::Ended { + v.t = spec.clip.range(v.duration()).0; + } + v.state = PlayState::Playing; + } else { + let t = from.unwrap_or(spec.clip.range(spec.duration).0); + m.voices.push(Voice { key, audio, spec, t, state: PlayState::Playing }); + } + }); + self.ensure_stream(); + } + + fn ensure_stream(&mut self) { + if self.streaming { + return; + } + let Some(out) = self.out.as_mut() else { return }; + let m = self.mixer.clone(); + let fill = Box::new(move |buf: &mut [f32], channels: usize, rate: u32| match m.lock() { + Ok(mut g) => { + let frames = buf.len() / channels.max(1); + g.mix(Some(buf), frames, channels, rate) + } + Err(_) => buf.fill(0.0), + }); + match out.start(fill) { + Ok(()) => self.streaming = true, + Err(e) => { + log::warn!("audio output unavailable ({e}); media plays silently"); + self.out = None; + } + } + } + + pub fn pause(&mut self, key: u64) { + self.with(|m| { + if let Some(v) = m.voices.iter_mut().find(|v| v.key == key && v.state == PlayState::Playing) { + v.state = PlayState::Paused; + } + }); + } + + /// Remove voice `key`. + pub fn stop(&mut self, key: u64) { + self.with(|m| m.voices.retain(|v| v.key != key)); + } + + pub fn stop_all(&mut self) { + self.with(|m| m.voices.clear()); + } + + /// Move voice `key` to `t` seconds (clamped to its played range). + pub fn seek(&mut self, key: u64, t: f64) { + self.with(|m| { + if let Some(v) = m.voices.iter_mut().find(|v| v.key == key) { + let (s, e) = v.spec.clip.range(v.duration()); + v.t = t.clamp(s, if e.is_finite() { e } else { f64::MAX }); + if v.state == PlayState::Ended { + v.state = PlayState::Paused; + } + } + }); + } + + pub fn status(&self, key: u64) -> Option { + self.with(|m| { + m.voices.iter().find(|v| v.key == key).map(|v| VoiceStatus { + state: if v.state == PlayState::Playing && v.loading() { PlayState::Loading } else { v.state }, + position: v.t, + duration: v.duration(), + error: v.audio.as_ref().and_then(|s| s.get()).and_then(|r| r.as_ref().err().cloned()), + }) + }) + } + + /// Keys of all voices (playing, paused or ended). + pub fn keys(&self) -> Vec { + self.with(|m| m.voices.iter().map(|v| v.key).collect()) + } + + pub fn any_playing(&self) -> bool { + self.with(|m| m.voices.iter().any(|v| v.state == PlayState::Playing)) + } + + /// Call every frame with the host's clock (seconds): runs the voices' clocks when there is no + /// audio device, and releases the device after a few idle seconds. + pub fn tick(&mut self, now: f64) { + let dt = self.last_tick.map(|l| (now - l).clamp(0.0, 0.25)).unwrap_or(0.0); + self.last_tick = Some(now); + if !self.streaming && dt > 0.0 { + let frames = (dt * DEFAULT_RATE as f64).round() as usize; + self.with(|m| m.mix(None, frames, 2, DEFAULT_RATE)); + } + if self.streaming { + if self.any_playing() { + self.idle_since = None; + } else if now - *self.idle_since.get_or_insert(now) > 3.0 { + if let Some(o) = self.out.as_mut() { + o.stop(); + } + self.streaming = false; + self.idle_since = None; + } + } + } + + /// Block until voice `key`'s audio has decoded (tests, offline rendering). + pub fn wait_loaded(&self, key: u64, timeout: std::time::Duration) -> bool { + let slot = self.with(|m| m.voices.iter().find(|v| v.key == key).and_then(|v| v.audio.clone())); + let Some(slot) = slot else { return true }; + let t0 = web_now(); + while slot.get().is_none() { + if web_now() - t0 > timeout.as_secs_f64() || !crate::THREADS { + return slot.get().is_some(); + } + std::thread::sleep(std::time::Duration::from_millis(1)); + } + true + } + + /// Mix into `buf` directly (offline rendering and tests; the device callback does the same). + pub fn render(&self, buf: &mut [f32], channels: usize, rate: u32) { + let frames = buf.len() / channels.max(1); + self.with(|m| m.mix(Some(buf), frames, channels, rate)); + } +} + +/// Seconds on a monotonic clock where there is one (std's Instant panics on wasm32). +fn web_now() -> f64 { + #[cfg(not(target_arch = "wasm32"))] + { + static T0: OnceLock = OnceLock::new(); + T0.get_or_init(std::time::Instant::now).elapsed().as_secs_f64() + } + #[cfg(target_arch = "wasm32")] + { + 0.0 + } +} + +impl Drop for Player { + fn drop(&mut self) { + if let Some(o) = self.out.as_mut() { + o.stop(); + } + } +} + +#[cfg(test)] +mod tests { + use super::*; + + fn spec(duration: f64, looping: bool) -> VoiceSpec { + VoiceSpec { clip: ClipParams::default(), looping, duration, audio: false } + } + + #[test] + fn wall_clock_advances_pauses_and_seeks() { + let mut p = Player::new(None); + let b: Bytes = Arc::new(vec![]); + p.play(1, 1, &b, spec(2.0, false), None); + p.tick(10.0); + p.tick(10.2); + p.tick(10.4); + let s = p.status(1).map(|s| s.position).unwrap_or(-1.0); + assert!((s - 0.4).abs() < 1e-3, "{s}"); + p.pause(1); + p.tick(10.6); + assert!((p.status(1).map(|s| s.position).unwrap_or(-1.0) - 0.4).abs() < 1e-3); + assert_eq!(p.status(1).map(|s| s.state), Some(PlayState::Paused)); + p.seek(1, 1.5); + p.play(1, 1, &b, spec(2.0, false), None); + p.tick(10.8); + p.tick(11.0); + p.tick(11.2); + p.tick(11.4); + let st = p.status(1); + assert_eq!(st.as_ref().map(|s| s.state), Some(PlayState::Ended)); + assert_eq!(st.map(|s| s.position), Some(2.0)); + // Playing an ended voice starts over. + p.play(1, 1, &b, spec(2.0, false), None); + assert_eq!(p.status(1).map(|s| s.position), Some(0.0)); + } + + #[test] + fn looping_wraps_within_the_trimmed_range() { + let mut p = Player::new(None); + let b: Bytes = Arc::new(vec![]); + let mut s = spec(1.0, true); + s.clip.trim_start = 0.5; + p.play(7, 7, &b, s, None); + p.tick(0.0); + for i in 1..=4 { + p.tick(i as f64 * 0.2); + } + // 0.8 s played in a 0.5 s loop starting at 0.5: 0.5 + (0.8 mod 0.5) = 0.8. + let pos = p.status(7).map(|s| s.position).unwrap_or(-1.0); + assert!((pos - 0.8).abs() < 1e-3, "{pos}"); + assert_eq!(p.status(7).map(|s| s.state), Some(PlayState::Playing)); + } + + #[test] + fn mixer_applies_volume_fade_and_channel_mapping() { + let pcm = Arc::new(Pcm { rate: 10, channels: 1, samples: vec![1.0; 10] }); + let slot: Slot = Arc::new(OnceLock::new()); + let _ = slot.set(Ok(pcm)); + let clip = ClipParams { fade_in: 0.5, volume: 0.5, ..Default::default() }; + let mut m = Mixer { + voices: vec![Voice { + key: 1, + audio: Some(slot), + spec: VoiceSpec { clip, looping: false, duration: 1.0, audio: true }, + t: 0.0, + state: PlayState::Playing, + }], + }; + let mut buf = vec![0.0f32; 2 * 12]; + m.mix(Some(&mut buf), 12, 2, 10); + // Frame 0: fade starts at 0; frame 3: 0.3/0.5 × 0.5 = 0.3; both channels carry the mono source. + assert_eq!(buf[0], 0.0); + assert!((buf[6] - 0.3).abs() < 1e-5 && (buf[7] - 0.3).abs() < 1e-5, "{buf:?}"); + assert!((buf[16] - 0.5).abs() < 1e-5); + // Past the end: silence, and the voice has ended. + assert_eq!(buf[22], 0.0); + assert_eq!(m.voices[0].state, PlayState::Ended); + } + + #[test] + fn device_clock_drives_position() { + struct Fake(Arc>>>); + impl AudioOut for Fake { + fn start(&mut self, fill: Box) -> Result<(), String> { + *self.0.lock().map_err(|e| e.to_string())? = Some(fill); + Ok(()) + } + fn stop(&mut self) {} + } + let cb = Arc::new(Mutex::new(None)); + let mut p = Player::new(Some(Box::new(Fake(cb.clone())))); + let b: Bytes = Arc::new(vec![]); + p.play(3, 3, &b, spec(10.0, false), None); + // Wall time alone doesn't move a device-clocked voice… + p.tick(0.0); + p.tick(0.2); + assert_eq!(p.status(3).map(|s| s.position), Some(0.0)); + // …the device pulling 4800 frames at 48 kHz does. + let mut buf = vec![0.0; 4800 * 2]; + if let Some(f) = cb.lock().ok().and_then(|mut g| g.take()).as_mut() { + f(&mut buf, 2, 48_000); + } + let pos = p.status(3).map(|s| s.position).unwrap_or(-1.0); + assert!((pos - 0.1).abs() < 1e-6, "{pos}"); + } +} diff --git a/crates/media/src/probe.rs b/crates/media/src/probe.rs new file mode 100644 index 0000000..2b8ad5c --- /dev/null +++ b/crates/media/src/probe.rs @@ -0,0 +1,231 @@ +//! Media probing: container, duration, audio and video stream parameters — without decoding +//! (except for audio files whose container doesn't state a length). + +use crate::video::{codec_name, mkv_codec_name}; +use crate::{ASF_HEADER, Bytes, Container, MediaError, Result}; + +#[derive(Clone, Debug, Default, PartialEq)] +pub struct AudioInfo { + pub codec: String, + pub sample_rate: u32, + pub channels: u16, + /// SlideCraft can decode it. + pub decodable: bool, +} + +#[derive(Clone, Debug, Default, PartialEq)] +pub struct VideoInfo { + pub codec: String, + pub width: u32, + pub height: u32, + pub fps: f64, + pub frames: u64, + pub decodable: bool, +} + +#[derive(Clone, Debug, Default, PartialEq)] +pub struct MediaInfo { + pub container: &'static str, + /// Duration in milliseconds (0 when unknown). + pub duration_ms: u64, + pub audio: Option, + pub video: Option, +} + +impl MediaInfo { + pub fn duration(&self) -> f64 { + self.duration_ms as f64 / 1000.0 + } + /// Something in it can be played. + pub fn playable(&self) -> bool { + self.audio.as_ref().is_some_and(|a| a.decodable) || self.video.as_ref().is_some_and(|v| v.decodable) + } +} + +fn ms(secs: f64) -> u64 { + if secs.is_finite() && secs > 0.0 { (secs * 1000.0).round() as u64 } else { 0 } +} + +/// Probe `bytes`. +pub fn probe(bytes: &Bytes) -> Result { + let c = Container::sniff(bytes); + let mut info = match c { + Container::Mp4 => probe_mp4(bytes)?, + Container::Matroska => probe_mkv(bytes)?, + Container::Asf => probe_asf(bytes)?, + Container::Ogg => probe_ogg(bytes).unwrap_or_else(|| MediaInfo { container: "ogg", ..Default::default() }), + _ => MediaInfo { container: c.name(), ..Default::default() }, + }; + if info.audio.is_none() && info.video.is_none() || info.audio.as_ref().is_some_and(|a| a.sample_rate == 0) { + // Plain audio files (and anything our demuxers didn't describe): symphonia's probe. + let (codec, rate, channels, frames) = + crate::audio::probe_symphonia(bytes, c.name()).ok_or_else(|| MediaError::Unsupported("this kind of media file".into()))?; + info.audio = Some(AudioInfo { codec: codec_label(&codec), sample_rate: rate, channels, decodable: true }); + if info.duration_ms == 0 + && let Some(n) = frames + && rate > 0 + { + info.duration_ms = ms(n as f64 / rate as f64); + } + } + if info.duration_ms == 0 && info.audio.as_ref().is_some_and(|a| a.decodable) && info.video.is_none() { + // No length in the header (raw MP3 / ADTS without an index): decode to measure. + if let Ok(pcm) = crate::audio::decode(bytes) { + info.duration_ms = ms(pcm.duration()); + } + } + Ok(info) +} + +fn codec_label(short: &str) -> String { + match short { + "mp3" => "MP3".into(), + "mp2" => "MP2".into(), + "mp1" => "MP1".into(), + "aac" => "AAC".into(), + "alac" => "ALAC".into(), + "flac" => "FLAC".into(), + "vorbis" => "Vorbis".into(), + s if s.starts_with("pcm") => "PCM".into(), + s if s.starts_with("adpcm") => "ADPCM".into(), + s => s.to_string(), + } +} + +fn probe_mp4(bytes: &Bytes) -> Result { + use slidecraft_isobmff::{CodecConfig, TrackKind}; + let src: &[u8] = bytes; + let f = slidecraft_isobmff::open(src).map_err(|e| MediaError::Corrupt(e.to_string()))?; + let mut info = MediaInfo { container: if f.is_quicktime { "mov" } else { "mp4" }, ..Default::default() }; + let mut dur = if f.timescale > 0 { f.duration as f64 / f.timescale as f64 } else { 0.0 }; + if let Some(ti) = f.track_of_kind(TrackKind::Video) { + let t = &f.tracks[ti]; + let secs = if t.timescale > 0 { t.duration as f64 / t.timescale as f64 } else { 0.0 }; + if let Some(e) = t.entries.first() { + let (w, h) = e.video.as_ref().map(|v| (v.width as u32, v.height as u32)).unwrap_or((t.width, t.height)); + let decodable = matches!(e.codec, CodecConfig::Avc(_) | CodecConfig::Hevc(_) | CodecConfig::Vp9(_) | CodecConfig::Av1(_)); + let frames = t.samples.len() as u64; + info.video = Some(VideoInfo { + codec: codec_name(&e.codec), + width: w, + height: h, + fps: if secs > 0.0 { frames as f64 / secs } else { 0.0 }, + frames, + decodable, + }); + } + dur = dur.max(secs); + } + if let Some(ti) = f.track_of_kind(TrackKind::Audio) { + let t = &f.tracks[ti]; + if let Some(e) = t.entries.first() { + let (rate, channels) = e.audio.as_ref().map(|a| (a.sample_rate.round() as u32, a.channels as u16)).unwrap_or((0, 0)); + let decodable = + matches!(e.codec, CodecConfig::Aac(_) | CodecConfig::Mp3 | CodecConfig::Pcm(_) | CodecConfig::Alac { .. } | CodecConfig::Flac(_)); + info.audio = Some(AudioInfo { codec: codec_name(&e.codec), sample_rate: rate, channels, decodable }); + } + if t.timescale > 0 { + dur = dur.max(t.duration as f64 / t.timescale as f64); + } + } + info.duration_ms = ms(dur); + Ok(info) +} + +fn probe_mkv(bytes: &Bytes) -> Result { + use slidecraft_matroska::{Codec, TrackKind}; + let src: &[u8] = bytes; + let f = slidecraft_matroska::open(src).map_err(|e| MediaError::Corrupt(e.to_string()))?; + let mut info = MediaInfo { container: if f.is_webm() { "webm" } else { "matroska" }, ..Default::default() }; + let dur = f.duration_ns().map(|n| n as f64 / 1e9).unwrap_or(0.0); + info.duration_ms = ms(dur); + if let Some(t) = f.tracks.iter().find(|t| t.kind == TrackKind::Video) { + let (w, h) = t.video.as_ref().map(|v| (v.pixel_width, v.pixel_height)).unwrap_or((0, 0)); + let frames = t.samples.len() as u64; + let fps = match t.default_duration_ns { + Some(d) if d > 0 => 1e9 / d as f64, + _ if dur > 0.0 => frames as f64 / dur, + _ => 0.0, + }; + let decodable = matches!(t.codec, Codec::Avc { .. } | Codec::Hevc { .. } | Codec::Vp9 { .. } | Codec::Av1 { .. }); + info.video = Some(VideoInfo { codec: mkv_codec_name(&t.codec), width: w, height: h, fps, frames, decodable }); + } + if let Some(t) = f.tracks.iter().find(|t| t.kind == TrackKind::Audio) { + let (rate, channels) = t + .audio + .as_ref() + .map(|a| (a.output_sampling_frequency.unwrap_or(a.sampling_frequency).round() as u32, a.channels as u16)) + .unwrap_or((0, 0)); + let rate = if matches!(t.codec, Codec::Opus { .. }) { 48_000 } else { rate }; + let decodable = + matches!(t.codec, Codec::Opus { .. } | Codec::Vorbis { .. } | Codec::Aac { .. } | Codec::Flac { .. } | Codec::Mp3 | Codec::Pcm { .. }); + info.audio = Some(AudioInfo { codec: mkv_codec_name(&t.codec), sample_rate: rate, channels, decodable }); + } + Ok(info) +} + +fn probe_ogg(bytes: &Bytes) -> Option { + let src: &[u8] = bytes; + let f = slidecraft_ogg::open(src).ok()?; + let s = f.stream_of(slidecraft_ogg::Codec::Opus)?; + let st = f.streams.get(s)?; + let head = slidecraft_opus::OpusHead::parse(st.headers.first()?).ok()?; + let timing = slidecraft_ogg::OpusTiming::of(st, head.pre_skip as u32); + Some(MediaInfo { + container: "ogg", + duration_ms: ms(timing.total as f64 / 48_000.0), + audio: Some(AudioInfo { codec: "Opus".into(), sample_rate: 48_000, channels: head.channels as u16, decodable: true }), + video: None, + }) +} + +/// Windows Media (ASF): enough of the header to describe the file. We have no WMA/WMV decoder. +fn probe_asf(b: &[u8]) -> Result { + const FILE_PROPS: [u8; 16] = [0xA1, 0xDC, 0xAB, 0x8C, 0x47, 0xA9, 0xCF, 0x11, 0x8E, 0xE4, 0x00, 0xC0, 0x0C, 0x20, 0x53, 0x65]; + const STREAM_PROPS: [u8; 16] = [0x91, 0x07, 0xDC, 0xB7, 0xB7, 0xA9, 0xCF, 0x11, 0x8E, 0xE6, 0x00, 0xC0, 0x0C, 0x20, 0x53, 0x65]; + const AUDIO_MEDIA: [u8; 16] = [0x40, 0x9E, 0x69, 0xF8, 0x4D, 0x5B, 0xCF, 0x11, 0xA8, 0xFD, 0x00, 0x80, 0x5F, 0x5C, 0x44, 0x2B]; + const VIDEO_MEDIA: [u8; 16] = [0xC0, 0xEF, 0x19, 0xBC, 0x4D, 0x5B, 0xCF, 0x11, 0xA8, 0xFD, 0x00, 0x80, 0x5F, 0x5C, 0x44, 0x2B]; + let u16le = |o: usize| b.get(o..o + 2).map(|s| u16::from_le_bytes([s[0], s[1]])).unwrap_or(0); + let u32le = |o: usize| b.get(o..o + 4).map(|s| u32::from_le_bytes([s[0], s[1], s[2], s[3]])).unwrap_or(0); + let u64le = |o: usize| b.get(o..o + 8).and_then(|s| s.try_into().ok()).map(u64::from_le_bytes).unwrap_or(0); + if b.get(..16) != Some(&ASF_HEADER[..]) { + return Err(MediaError::Corrupt("not an ASF file".into())); + } + let header_end = (u64le(16) as usize).min(b.len()); + let mut info = MediaInfo { container: "asf", ..Default::default() }; + let mut pos = 30; + while pos + 24 <= header_end { + let guid = &b[pos..pos + 16]; + let size = u64le(pos + 16) as usize; + if size < 24 { + break; + } + let d = pos + 24; + if guid == FILE_PROPS { + // file id 16, file size 8, creation 8, packets 8, play duration 8 (100 ns), send 8, preroll 8 (ms) + let play = u64le(d + 40) as f64 / 1e7; + let preroll = u64le(d + 56) as f64 / 1e3; + info.duration_ms = ms(play - preroll); + } else if guid == STREAM_PROPS { + let kind = b.get(d..d + 16); + // type 16, error correction type 16, time offset 8, type data len 4, ec len 4, flags 2, reserved 4 + let td = d + 54; + if kind == Some(&AUDIO_MEDIA[..]) { + let tag = u16le(td); + let codec = match tag { + 0x0160 => "WMA 1", + 0x0161 => "WMA 2", + 0x0162 => "WMA Pro", + 0x0163 => "WMA Lossless", + 0x000A => "WMA Voice", + _ => "WMA", + }; + info.audio = Some(AudioInfo { codec: codec.into(), sample_rate: u32le(td + 4), channels: u16le(td + 2), decodable: false }); + } else if kind == Some(&VIDEO_MEDIA[..]) { + info.video = Some(VideoInfo { codec: "WMV".into(), width: u32le(td), height: u32le(td + 4), decodable: false, ..Default::default() }); + } + } + pos += size; + } + Ok(info) +} diff --git a/crates/media/src/video.rs b/crates/media/src/video.rs new file mode 100644 index 0000000..882f5b3 --- /dev/null +++ b/crates/media/src/video.rs @@ -0,0 +1,491 @@ +//! Video decoding: MP4/MOV and WebM/Matroska demuxing, H.264 / HEVC / VP9 / AV1 decoding (our own +//! decoders), RGBA frames and poster frames. + +use std::collections::VecDeque; + +use slidecraft_isobmff::CodecConfig; +use slidecraft_matroska::Codec as MkvCodec; + +use crate::yuv::{self, Matrix, Planes}; +use crate::{Bytes, Container, MediaError, Result}; + +/// A decoded picture, RGBA8 (straight alpha, always opaque), with its presentation time. +#[derive(Clone, Debug, PartialEq)] +pub struct Frame { + /// Presentation time in seconds from the start of the media. + pub time: f64, + pub width: u32, + pub height: u32, + pub rgba: Vec, +} + +/// A picture from a codec, before colour conversion; `pts` in track ticks. +struct Pic { + pts: i64, + width: u32, + height: u32, + rgba: Vec, +} + +trait Codec: Send { + fn decode(&mut self, sample: &[u8], pts: i64) -> Result>; + fn flush(&mut self) -> Vec; + fn reset(&mut self); +} + +fn dec_err(e: impl std::fmt::Display) -> MediaError { + MediaError::Corrupt(e.to_string()) +} + +// ---------- codecs ---------- + +struct H264 { + avcc: Vec, + dec: slidecraft_h264::Decoder, +} + +impl H264 { + fn new(avcc: Vec) -> Result { + let dec = slidecraft_h264::Decoder::from_avcc(&avcc).map_err(dec_err)?; + Ok(H264 { avcc, dec }) + } + fn convert(p: slidecraft_h264::Picture) -> Pic { + let (w, h) = (p.width as usize, p.height as usize); + let planes = Planes { + y: &p.y, + u: &p.u, + v: &p.v, + y_stride: p.y_stride, + c_stride: p.uv_stride, + width: w, + height: h, + sx: u32::from(p.chroma_width < p.width), + sy: u32::from(p.chroma_height < p.height), + bits: 8, + mono: p.chroma_width == 0, + }; + let rgba = yuv::to_rgba(&planes, Matrix::from_code(p.color.matrix, p.height), p.color.full_range); + Pic { pts: p.pts, width: p.width, height: p.height, rgba } + } +} + +impl Codec for H264 { + fn decode(&mut self, sample: &[u8], pts: i64) -> Result> { + Ok(self.dec.decode(sample, pts).map_err(dec_err)?.into_iter().map(H264::convert).collect()) + } + fn flush(&mut self) -> Vec { + self.dec.flush().into_iter().map(H264::convert).collect() + } + fn reset(&mut self) { + if let Ok(d) = slidecraft_h264::Decoder::from_avcc(&self.avcc) { + self.dec = d; + } + } +} + +/// Convert an HEVC / VP9 style picture (planes of u8 or u16 with strides). +macro_rules! plane_pic { + ($p:expr, $Plane:path, $sx:expr, $sy:expr, $matrix:expr, $full:expr, $mono:expr) => {{ + let p = $p; + use $Plane as P; + let (w, h) = (p.width as usize, p.height as usize); + let rgba = match (&p.y, &p.u, &p.v) { + (P::U8(y), P::U8(u), P::U8(v)) => yuv::to_rgba( + &Planes { y, u, v, y_stride: p.y_stride, c_stride: p.uv_stride, width: w, height: h, sx: $sx, sy: $sy, bits: 8, mono: $mono }, + $matrix, + $full, + ), + (P::U16(y), P::U16(u), P::U16(v)) => yuv::to_rgba( + &Planes { + y, + u, + v, + y_stride: p.y_stride, + c_stride: p.uv_stride, + width: w, + height: h, + sx: $sx, + sy: $sy, + bits: p.bit_depth as u32, + mono: $mono, + }, + $matrix, + $full, + ), + _ => vec![0; w * h * 4], + }; + Pic { pts: p.pts, width: p.width, height: p.height, rgba } + }}; +} + +struct Hevc { + hvcc: Vec, + dec: slidecraft_hevc::Decoder, +} + +impl Hevc { + fn new(hvcc: Vec) -> Result { + let dec = slidecraft_hevc::Decoder::from_hvcc(&hvcc).map_err(dec_err)?; + Ok(Hevc { hvcc, dec }) + } + fn convert(p: slidecraft_hevc::Picture) -> Pic { + let sx = u32::from(p.chroma_width < p.width); + let sy = u32::from(p.chroma_height < p.height); + let m = Matrix::from_code(p.color.matrix, p.height); + plane_pic!(&p, slidecraft_hevc::Plane, sx, sy, m, p.color.full_range, p.chroma_width == 0) + } +} + +impl Codec for Hevc { + fn decode(&mut self, sample: &[u8], pts: i64) -> Result> { + Ok(self.dec.decode(sample, pts).map_err(dec_err)?.into_iter().map(Hevc::convert).collect()) + } + fn flush(&mut self) -> Vec { + self.dec.flush().into_iter().map(Hevc::convert).collect() + } + fn reset(&mut self) { + if let Ok(d) = slidecraft_hevc::Decoder::from_hvcc(&self.hvcc) { + self.dec = d; + } + } +} + +struct Vp9 { + dec: slidecraft_vp9::Decoder, +} + +impl Vp9 { + fn convert(p: slidecraft_vp9::Picture) -> Pic { + let (w, h) = (p.width as usize, p.height as usize); + if p.color.color_space == 7 { + // RGB (4:4:4): the planes carry G, B, R. + let shift = p.bit_depth.saturating_sub(8); + let at = |pl: &slidecraft_vp9::Plane, i: usize| if i < pl.len() { (pl.get(i) >> shift) as u8 } else { 0 }; + let mut rgba = Vec::with_capacity(w * h * 4); + for row in 0..h { + for col in 0..w { + let i = row * p.y_stride + col; + let c = row * p.uv_stride + col; + rgba.extend_from_slice(&[at(&p.v, c), at(&p.y, i), at(&p.u, c), 255]); + } + } + return Pic { pts: p.pts, width: p.width, height: p.height, rgba }; + } + // color_space (VP9 §7.2.2): 1 BT.601, 2 BT.709, 3 SMPTE-170, 4 SMPTE-240, 5 BT.2020. + let m = match p.color.color_space { + 1 | 3 => Matrix::Bt601, + 2 | 4 => Matrix::Bt709, + 5 => Matrix::Bt2020, + _ => Matrix::from_code(2, p.height), + }; + plane_pic!(&p, slidecraft_vp9::Plane, u32::from(p.subsampling_x), u32::from(p.subsampling_y), m, p.color.full_range, false) + } +} + +impl Codec for Vp9 { + fn decode(&mut self, sample: &[u8], pts: i64) -> Result> { + Ok(self.dec.decode(sample, pts).map_err(dec_err)?.into_iter().map(Vp9::convert).collect()) + } + fn flush(&mut self) -> Vec { + self.dec.flush().into_iter().map(Vp9::convert).collect() + } + fn reset(&mut self) { + self.dec.reset(); + } +} + +struct Av1 { + dec: slidecraft_av1::Decoder, + config_obus: Vec, + primed: bool, +} + +impl Av1 { + fn convert(p: slidecraft_av1::Picture) -> Pic { + let (w, h) = (p.width as usize, p.height as usize); + let cw = p.plane_width(1) as usize; + let rgba = yuv::to_rgba( + &Planes { + y: &p.planes[0], + u: &p.planes[1], + v: &p.planes[2], + y_stride: w, + c_stride: cw, + width: w, + height: h, + sx: p.subsampling_x as u32, + sy: p.subsampling_y as u32, + bits: p.bit_depth as u32, + mono: p.mono_chrome, + }, + Matrix::from_code(p.matrix_coefficients, p.height), + p.full_range, + ); + Pic { pts: p.pts, width: p.width, height: p.height, rgba } + } +} + +impl Codec for Av1 { + fn decode(&mut self, sample: &[u8], pts: i64) -> Result> { + if !self.primed { + self.primed = true; + if !self.config_obus.is_empty() { + self.dec.decode(&self.config_obus).map_err(dec_err)?; + } + } + Ok(self.dec.decode_pts(sample, pts).map_err(dec_err)?.into_iter().map(Av1::convert).collect()) + } + fn flush(&mut self) -> Vec { + self.dec.flush().into_iter().map(Av1::convert).collect() + } + fn reset(&mut self) { + self.dec = slidecraft_av1::Decoder::new(); + self.primed = false; + } +} + +// ---------- containers ---------- + +/// The video track's samples in decode order: (pts in ticks, sync). +struct Track { + samples: Vec<(i64, bool)>, + /// Seconds per tick (num, den). + timebase: (i64, i64), + width: u32, + height: u32, + codec: String, +} + +enum Demux { + Mp4(Box, usize), + Mkv(Box, usize), +} + +impl Demux { + fn read(&self, bytes: &[u8], i: usize) -> Result> { + match self { + Demux::Mp4(f, t) => f.read_sample(bytes, *t, i).map_err(dec_err), + Demux::Mkv(f, t) => f.read_sample(bytes, *t, i).map_err(dec_err), + } + } +} + +fn open_track(bytes: &[u8]) -> Result<(Demux, Track, Box)> { + match Container::sniff(bytes) { + Container::Mp4 => { + let f = slidecraft_isobmff::open(bytes).map_err(dec_err)?; + let ti = f.track_of_kind(slidecraft_isobmff::TrackKind::Video).ok_or(MediaError::Missing("video track"))?; + let t = &f.tracks[ti]; + let entry = t.entries.first().ok_or(MediaError::Missing("video track"))?; + let codec: Box = match &entry.codec { + CodecConfig::Avc(a) => Box::new(H264::new(a.to_bytes())?), + CodecConfig::Hevc(c) => Box::new(Hevc::new(c.to_bytes())?), + CodecConfig::Vp9(_) => Box::new(Vp9 { dec: slidecraft_vp9::Decoder::new() }), + CodecConfig::Av1(c) => Box::new(Av1 { dec: slidecraft_av1::Decoder::new(), config_obus: c.config_obus.clone(), primed: false }), + other => return Err(MediaError::Unsupported(format!("{} video", codec_name(other)))), + }; + let (w, h) = entry.video.as_ref().map(|v| (v.width as u32, v.height as u32)).unwrap_or((t.width, t.height)); + let samples = (0..t.samples.len()).map(|i| (t.presentation_pts(i).unwrap_or(0), t.samples[i].is_sync)).collect(); + let track = Track { samples, timebase: (1, t.timescale.max(1) as i64), width: w, height: h, codec: codec_name(&entry.codec) }; + Ok((Demux::Mp4(Box::new(f), ti), track, codec)) + } + Container::Matroska => { + let f = slidecraft_matroska::open(bytes).map_err(dec_err)?; + let ti = f + .tracks + .iter() + .position(|t| t.kind == slidecraft_matroska::TrackKind::Video && !t.samples.is_empty()) + .ok_or(MediaError::Missing("video track"))?; + let t = &f.tracks[ti]; + let codec: Box = match &t.codec { + MkvCodec::Avc { avcc } => Box::new(H264::new(avcc.clone())?), + MkvCodec::Hevc { hvcc } => Box::new(Hevc::new(hvcc.clone())?), + MkvCodec::Vp9 { .. } => Box::new(Vp9 { dec: slidecraft_vp9::Decoder::new() }), + MkvCodec::Av1 { av1c } => Box::new(Av1 { + dec: slidecraft_av1::Decoder::new(), + config_obus: slidecraft_isobmff::Av1Config::parse(av1c).map(|c| c.config_obus).unwrap_or_default(), + primed: false, + }), + other => return Err(MediaError::Unsupported(format!("{} video", mkv_codec_name(other)))), + }; + let (w, h) = t.video.as_ref().map(|v| (v.pixel_width, v.pixel_height)).unwrap_or((0, 0)); + let samples = t.samples.iter().map(|s| (s.pts, s.keyframe)).collect(); + let track = Track { + samples, + timebase: (t.timebase.0.max(1) as i64, t.timebase.1.max(1) as i64), + width: w, + height: h, + codec: mkv_codec_name(&t.codec), + }; + Ok((Demux::Mkv(Box::new(f), ti), track, codec)) + } + Container::Asf => Err(MediaError::Unsupported("Windows Media Video (WMV)".into())), + _ => Err(MediaError::Missing("video track")), + } +} + +pub(crate) fn codec_name(c: &CodecConfig) -> String { + match c { + CodecConfig::Avc(_) => "H.264".into(), + CodecConfig::Hevc(_) => "HEVC".into(), + CodecConfig::Av1(_) => "AV1".into(), + CodecConfig::Vp9(_) => "VP9".into(), + CodecConfig::ProRes { .. } => "Apple ProRes".into(), + CodecConfig::Jpeg { .. } => "Motion JPEG".into(), + CodecConfig::Dnx { .. } => "DNxHD".into(), + CodecConfig::Aac(_) => "AAC".into(), + CodecConfig::Mp3 => "MP3".into(), + CodecConfig::Pcm(_) => "PCM".into(), + CodecConfig::Alac { .. } => "ALAC".into(), + CodecConfig::Opus(_) => "Opus".into(), + CodecConfig::Flac(_) => "FLAC".into(), + CodecConfig::Ac3 { .. } => "AC-3".into(), + CodecConfig::Eac3 { .. } => "E-AC-3".into(), + CodecConfig::Timecode(_) => "timecode".into(), + CodecConfig::Unknown { fourcc, .. } => format!("{fourcc:?}"), + } +} + +pub(crate) fn mkv_codec_name(c: &MkvCodec) -> String { + match c { + MkvCodec::Avc { .. } => "H.264".into(), + MkvCodec::Hevc { .. } => "HEVC".into(), + MkvCodec::Vp8 => "VP8".into(), + MkvCodec::Vp9 { .. } => "VP9".into(), + MkvCodec::Av1 { .. } => "AV1".into(), + MkvCodec::Aac { .. } => "AAC".into(), + MkvCodec::Opus { .. } => "Opus".into(), + MkvCodec::Vorbis { .. } => "Vorbis".into(), + MkvCodec::Flac { .. } => "FLAC".into(), + MkvCodec::Mp3 => "MP3".into(), + MkvCodec::Pcm { .. } => "PCM".into(), + other => other.name(), + } +} + +/// Decodes the video track of a file, frame by frame in presentation order. +pub struct VideoDecoder { + bytes: Bytes, + demux: Demux, + track: Track, + codec: Box, + /// Next sample to feed (decode order). + next: usize, + /// Decoded pictures waiting, in presentation order. + ready: VecDeque, + /// Frames before this time (s) are dropped (after a seek). + skip_before: f64, + eos: bool, + errors: u32, +} + +impl VideoDecoder { + pub fn open(bytes: Bytes) -> Result { + let (demux, track, codec) = open_track(&bytes)?; + Ok(VideoDecoder { bytes, demux, track, codec, next: 0, ready: VecDeque::new(), skip_before: f64::NEG_INFINITY, eos: false, errors: 0 }) + } + pub fn size(&self) -> (u32, u32) { + (self.track.width, self.track.height) + } + pub fn codec(&self) -> &str { + &self.track.codec + } + pub fn frame_count(&self) -> usize { + self.track.samples.len() + } + fn secs(&self, pts: i64) -> f64 { + pts as f64 * self.track.timebase.0 as f64 / self.track.timebase.1 as f64 + } + + /// The next frame in presentation order, `None` at the end. + pub fn next_frame(&mut self) -> Result> { + loop { + // A picture is safe to emit once the decoder has returned it (they come out in + // presentation order); keep the queue sorted for decoders that emit in batches. + if let Some(p) = self.ready.pop_front() { + let time = self.secs(p.pts); + if time + 1e-6 < self.skip_before { + continue; + } + return Ok(Some(Frame { time, width: p.width, height: p.height, rgba: p.rgba })); + } + if self.eos { + return Ok(None); + } + if self.next >= self.track.samples.len() { + self.eos = true; + let mut pics = self.codec.flush(); + pics.sort_by_key(|p| p.pts); + self.ready.extend(pics); + continue; + } + let i = self.next; + self.next += 1; + let pts = self.track.samples[i].0; + let data = self.demux.read(&self.bytes, i)?; + match self.codec.decode(&data, pts) { + Ok(mut pics) => { + pics.sort_by_key(|p| p.pts); + self.ready.extend(pics); + } + Err(e) => { + // A corrupt sample: carry on (the picture is lost), give up after many. + self.errors += 1; + log::debug!("video sample {i}: {e}"); + if self.errors > 50 { + return Err(e); + } + } + } + } + } + + /// Position so that the next frame is the one shown at `t` seconds (decoding resumes at the + /// preceding sync sample). + pub fn seek(&mut self, t: f64) { + let target = (t * self.track.timebase.1 as f64 / self.track.timebase.0 as f64) as i64; + // The last sync sample whose pts is at or before the target. + let mut start = 0; + for (i, (pts, sync)) in self.track.samples.iter().enumerate() { + if *sync && *pts <= target { + start = i; + } + } + self.codec.reset(); + self.ready.clear(); + self.next = start; + self.eos = false; + // Keep the frame on screen at `t`: the one whose pts is the last ≤ t. + let shown = self.track.samples.iter().map(|s| s.0).filter(|p| *p <= target).max(); + self.skip_before = shown.map(|p| self.secs(p)).unwrap_or(f64::NEG_INFINITY); + } +} + +/// The frame shown at `t` seconds (the first frame when `t` is 0). +pub fn poster_frame(bytes: &Bytes, t: f64) -> Result { + let mut d = VideoDecoder::open(bytes.clone())?; + if t > 0.0 { + d.seek(t); + } + let mut last = None; + while let Some(f) = d.next_frame()? { + if f.time > t + 1e-6 && last.is_some() { + break; + } + let done = f.time + 1e-6 >= t; + last = Some(f); + if done { + break; + } + } + last.ok_or(MediaError::Corrupt("no picture could be decoded".into())) +} + +/// [`poster_frame`] as PNG bytes. +pub fn poster_png(bytes: &Bytes, t: f64) -> Result> { + let f = poster_frame(bytes, t)?; + let img = image::RgbaImage::from_raw(f.width, f.height, f.rgba).ok_or(MediaError::Corrupt("bad picture size".into()))?; + let mut out = Vec::new(); + image::DynamicImage::ImageRgba8(img).write_to(&mut std::io::Cursor::new(&mut out), image::ImageFormat::Png).map_err(dec_err)?; + Ok(out) +} diff --git a/crates/media/src/yuv.rs b/crates/media/src/yuv.rs new file mode 100644 index 0000000..6a36c54 --- /dev/null +++ b/crates/media/src/yuv.rs @@ -0,0 +1,120 @@ +//! Y'CbCr → RGBA8 conversion for decoded pictures. + +/// Y'CbCr matrix (ISO/IEC 23091-2 MatrixCoefficients). +#[derive(Clone, Copy, Debug, PartialEq, Eq)] +pub enum Matrix { + Bt601, + Bt709, + Bt2020, +} + +impl Matrix { + /// From a `matrix_coefficients` code; unspecified codes pick by picture height (SD → 601). + pub fn from_code(code: u8, height: u32) -> Matrix { + match code { + 1 => Matrix::Bt709, + 5 | 6 => Matrix::Bt601, + 9 | 10 => Matrix::Bt2020, + _ if height <= 576 => Matrix::Bt601, + _ => Matrix::Bt709, + } + } + /// (Kr, Kb). + fn k(self) -> (f32, f32) { + match self { + Matrix::Bt601 => (0.299, 0.114), + Matrix::Bt709 => (0.2126, 0.0722), + Matrix::Bt2020 => (0.2627, 0.0593), + } + } +} + +/// Borrowed planes of one picture. Chroma planes are `width >> sx` (rounded up) wide. +pub struct Planes<'a, T> { + pub y: &'a [T], + pub u: &'a [T], + pub v: &'a [T], + pub y_stride: usize, + pub c_stride: usize, + pub width: usize, + pub height: usize, + /// Chroma subsampling shifts (1 = halved). + pub sx: u32, + pub sy: u32, + pub bits: u32, + /// No chroma: grey. + pub mono: bool, +} + +/// Convert to tightly packed RGBA8. +pub fn to_rgba>(p: &Planes, m: Matrix, full_range: bool) -> Vec { + let (kr, kb) = m.k(); + let kg = 1.0 - kr - kb; + let max = ((1u32 << p.bits) - 1) as f32; + let scale = (1u32 << p.bits.saturating_sub(8)) as f32; + // Normalised luma / chroma: Y in 0..1, Cb/Cr in -0.5..0.5. + let (y_off, y_mul, c_mul) = if full_range { (0.0, 1.0 / max, 1.0 / max) } else { (16.0 * scale, 1.0 / (219.0 * scale), 1.0 / (224.0 * scale)) }; + let mid = (1u32 << (p.bits - 1)) as f32; + let cr_r = 2.0 * (1.0 - kr); + let cb_b = 2.0 * (1.0 - kb); + let cb_g = -2.0 * kb * (1.0 - kb) / kg; + let cr_g = -2.0 * kr * (1.0 - kr) / kg; + let mut out = vec![0u8; p.width * p.height * 4]; + let px = |v: f32| (v * 255.0 + 0.5).clamp(0.0, 255.0) as u8; + for row in 0..p.height { + let yrow = row * p.y_stride; + let crow = (row >> p.sy) * p.c_stride; + let o = &mut out[row * p.width * 4..(row + 1) * p.width * 4]; + for col in 0..p.width { + let yv: u32 = p.y.get(yrow + col).copied().map(Into::into).unwrap_or(0); + let yn = (yv as f32 - y_off) * y_mul; + let (cb, cr) = if p.mono { + (0.0, 0.0) + } else { + let ci = crow + (col >> p.sx); + let u: u32 = p.u.get(ci).copied().map(Into::into).unwrap_or(mid as u32); + let v: u32 = p.v.get(ci).copied().map(Into::into).unwrap_or(mid as u32); + ((u as f32 - mid) * c_mul, (v as f32 - mid) * c_mul) + }; + let k = col * 4; + o[k] = px(yn + cr_r * cr); + o[k + 1] = px(yn + cb_g * cb + cr_g * cr); + o[k + 2] = px(yn + cb_b * cb); + o[k + 3] = 255; + } + } + out +} + +#[cfg(test)] +mod tests { + use super::*; + + fn one(y: u8, u: u8, v: u8, full: bool) -> [u8; 4] { + let p = Planes { y: &[y], u: &[u], v: &[v], y_stride: 1, c_stride: 1, width: 1, height: 1, sx: 0, sy: 0, bits: 8, mono: false }; + let o = to_rgba(&p, Matrix::Bt709, full); + [o[0], o[1], o[2], o[3]] + } + + #[test] + fn limited_range_black_white_and_red() { + assert_eq!(one(16, 128, 128, false), [0, 0, 0, 255]); + assert_eq!(one(235, 128, 128, false), [255, 255, 255, 255]); + // BT.709 red: Y 63, Cb 102, Cr 240. + let r = one(63, 102, 240, false); + assert!(r[0] > 250 && r[1] < 5 && r[2] < 5, "{r:?}"); + } + + #[test] + fn full_range_grey() { + assert_eq!(one(128, 128, 128, true), [128, 128, 128, 255]); + } + + #[test] + fn ten_bit_white() { + let y = [940u16]; + let c = [512u16]; + let p = Planes { y: &y, u: &c, v: &c, y_stride: 1, c_stride: 1, width: 1, height: 1, sx: 0, sy: 0, bits: 10, mono: false }; + assert_eq!(to_rgba(&p, Matrix::Bt709, false), vec![255, 255, 255, 255]); + } +} diff --git a/crates/media/tests/decode.rs b/crates/media/tests/decode.rs new file mode 100644 index 0000000..e3a6be7 --- /dev/null +++ b/crates/media/tests/decode.rs @@ -0,0 +1,327 @@ +//! Decoding and probing every supported format. +//! +//! WAV and AIFF are written here; the compressed fixtures in `tests/fixtures` are a 0.5 s, 440 Hz +//! sine (amplitude 1/8) and a 6-frame 64×48 colour gradient we synthesised and encoded with +//! `fixtures/make.sh` (CC0, see ATTRIBUTION.md). + +use std::sync::Arc; +use std::time::Duration; + +use slidecraft_media::{Bytes, Container, MediaError, audio, probe, video}; + +fn fixture(name: &str) -> Bytes { + let p = std::path::Path::new(env!("CARGO_MANIFEST_DIR")).join("tests/fixtures").join(name); + Arc::new(std::fs::read(&p).unwrap_or_else(|e| panic!("{}: {e}", p.display()))) +} + +fn sine(rate: u32, secs: f64) -> Vec { + (0..(rate as f64 * secs) as usize).map(|i| (i as f64 * 440.0 * std::f64::consts::TAU / rate as f64).sin() as f32 * 0.125).collect() +} + +fn wav(rate: u32, channels: u16, samples: &[f32]) -> Vec { + let data: Vec = samples.iter().flat_map(|s| ((s * 32767.0) as i16).to_le_bytes()).collect(); + let mut b = Vec::new(); + b.extend_from_slice(b"RIFF"); + b.extend_from_slice(&(36 + data.len() as u32).to_le_bytes()); + b.extend_from_slice(b"WAVEfmt "); + b.extend_from_slice(&16u32.to_le_bytes()); + b.extend_from_slice(&1u16.to_le_bytes()); + b.extend_from_slice(&channels.to_le_bytes()); + b.extend_from_slice(&rate.to_le_bytes()); + b.extend_from_slice(&(rate * channels as u32 * 2).to_le_bytes()); + b.extend_from_slice(&(channels * 2).to_le_bytes()); + b.extend_from_slice(&16u16.to_le_bytes()); + b.extend_from_slice(b"data"); + b.extend_from_slice(&(data.len() as u32).to_le_bytes()); + b.extend_from_slice(&data); + b +} + +/// 80-bit IEEE extended sample rate for AIFF's COMM chunk. +fn ext80(v: u32) -> [u8; 10] { + let mut out = [0u8; 10]; + let e = 31 - v.leading_zeros(); + let exp = (16383 + e) as u16; + out[..2].copy_from_slice(&exp.to_be_bytes()); + let mant = (v as u64) << (63 - e); + out[2..].copy_from_slice(&mant.to_be_bytes()); + out +} + +fn aiff(rate: u32, samples: &[f32]) -> Vec { + let data: Vec = samples.iter().flat_map(|s| ((s * 32767.0) as i16).to_be_bytes()).collect(); + let mut comm = Vec::new(); + comm.extend_from_slice(&1u16.to_be_bytes()); + comm.extend_from_slice(&(samples.len() as u32).to_be_bytes()); + comm.extend_from_slice(&16u16.to_be_bytes()); + comm.extend_from_slice(&ext80(rate)); + let mut b = Vec::new(); + b.extend_from_slice(b"FORM"); + b.extend_from_slice(&((4 + 8 + comm.len() + 8 + 8 + data.len()) as u32).to_be_bytes()); + b.extend_from_slice(b"AIFF"); + b.extend_from_slice(b"COMM"); + b.extend_from_slice(&(comm.len() as u32).to_be_bytes()); + b.extend_from_slice(&comm); + b.extend_from_slice(b"SSND"); + b.extend_from_slice(&((8 + data.len()) as u32).to_be_bytes()); + b.extend_from_slice(&[0; 8]); + b.extend_from_slice(&data); + b +} + +/// Frequency of channel 0 from its zero crossings (with hysteresis, so codec noise around silence +/// doesn't count), over the span between the first and last crossing. +fn frequency(p: &audio::Pcm) -> f64 { + let mut sign = 0i8; + let mut crossings = vec![]; + for i in 0..p.frames() { + let s = p.at(i, 0); + let now = if s > 0.03 { + 1 + } else if s < -0.03 { + -1 + } else { + 0 + }; + if now != 0 && now != sign { + if sign != 0 { + crossings.push(i); + } + sign = now; + } + } + match (crossings.first(), crossings.last()) { + (Some(a), Some(b)) if b > a => (crossings.len() - 1) as f64 / 2.0 / ((b - a) as f64 / p.rate as f64), + _ => 0.0, + } +} + +/// The tone decodes to ~0.5 s of a 440 Hz sine at amplitude 1/8. +fn check_tone(name: &str, bytes: &Bytes, rate: u32, channels: u16) { + let p = audio::decode(bytes).unwrap_or_else(|e| panic!("{name}: {e}")); + assert_eq!((p.rate, p.channels), (rate, channels), "{name}"); + assert!((p.duration() - 0.5).abs() < 0.06, "{name}: {} s", p.duration()); + let f = frequency(&p); + assert!((f - 440.0).abs() < 15.0, "{name}: {f} Hz"); + let rms = p.rms(); + // 1/8 / √2 (ffmpeg's mono → stereo upmix for the Vorbis fixture lowers it by another √2). + assert!((0.055..0.105).contains(&rms), "{name}: rms {rms}"); + let info = probe(bytes).unwrap_or_else(|e| panic!("{name}: probe {e}")); + let a = info.audio.unwrap_or_else(|| panic!("{name}: no audio info")); + assert!(a.decodable, "{name}"); + assert_eq!((a.sample_rate, a.channels), (rate, channels), "{name}"); + assert!((info.duration_ms as i64 - 500).abs() <= 60, "{name}: {} ms", info.duration_ms); + assert!(info.video.is_none(), "{name}"); +} + +#[test] +fn wav_written_in_code() { + let b: Bytes = Arc::new(wav(44_100, 1, &sine(44_100, 0.5))); + assert_eq!(Container::sniff(&b), Container::Wav); + check_tone("wav", &b, 44_100, 1); + // Stereo. + let st: Vec = sine(22_050, 0.5).iter().flat_map(|s| [*s, *s]).collect(); + check_tone("wav stereo", &Arc::new(wav(22_050, 2, &st)), 22_050, 2); +} + +#[test] +fn aiff_written_in_code() { + let b: Bytes = Arc::new(aiff(48_000, &sine(48_000, 0.5))); + assert_eq!(Container::sniff(&b), Container::Aiff); + check_tone("aiff", &b, 48_000, 1); +} + +#[test] +fn mp3() { + let b = fixture("tone.mp3"); + assert_eq!(Container::sniff(&b), Container::Mp3); + check_tone("mp3", &b, 44_100, 1); +} + +#[test] +fn aac_m4a_and_adts() { + check_tone("m4a", &fixture("tone.m4a"), 44_100, 1); + let adts = fixture("tone.aac"); + assert_eq!(Container::sniff(&adts), Container::Adts); + check_tone("aac", &adts, 44_100, 1); +} + +#[test] +fn alac() { + check_tone("alac", &fixture("tone-alac.m4a"), 44_100, 1); +} + +#[test] +fn flac() { + check_tone("flac", &fixture("tone.flac"), 44_100, 1); +} + +#[test] +fn ogg_vorbis() { + check_tone("vorbis", &fixture("tone.ogg"), 44_100, 2); +} + +#[test] +fn ogg_opus() { + check_tone("opus", &fixture("tone.opus"), 48_000, 1); +} + +#[test] +fn wma_is_probed_but_not_decoded() { + let b = fixture("tone.wma"); + assert_eq!(Container::sniff(&b), Container::Asf); + let info = probe(&b).expect("probe"); + let a = info.audio.clone().expect("audio"); + assert_eq!((a.codec.as_str(), a.sample_rate, a.channels, a.decodable), ("WMA 2", 44_100, 1, false)); + assert!((info.duration_ms as i64 - 500).abs() <= 120, "{}", info.duration_ms); + assert!(!info.playable()); + assert!(matches!(audio::decode(&b), Err(MediaError::Unsupported(_)))); +} + +#[test] +fn garbage_is_an_error_not_a_panic() { + let b: Bytes = Arc::new((0..4000u32).map(|i| (i * 7919 % 251) as u8).collect()); + assert!(probe(&b).is_err()); + assert!(audio::decode(&b).is_err()); + assert!(video::VideoDecoder::open(b.clone()).is_err()); + // Truncated real files too. + for name in ["tone.mp3", "tone.m4a", "tone.opus", "clip-h264.mp4", "clip-vp9.webm"] { + let full = fixture(name); + let cut: Bytes = Arc::new(full[..full.len() / 3].to_vec()); + let _ = probe(&cut); + let _ = audio::decode(&cut); + if let Ok(mut d) = video::VideoDecoder::open(cut) { + while let Ok(Some(_)) = d.next_frame() {} + } + } +} + +/// Mean colour of a 16×16 block in the middle of the frame. +fn centre(f: &video::Frame) -> [f64; 3] { + let (w, h) = (f.width as usize, f.height as usize); + let mut s = [0.0; 3]; + let mut n = 0.0; + for y in h / 2 - 8..h / 2 + 8 { + for x in w / 2 - 8..w / 2 + 8 { + for (c, v) in s.iter_mut().enumerate() { + *v += f.rgba[(y * w + x) * 4 + c] as f64; + } + n += 1.0; + } + } + s.map(|v| v / n) +} + +/// Our gradient's mean over the same block in frame `k`: R = (4x + 20k) mod 256, G = 5y mod 256, +/// B = 40k mod 256. +fn expected_centre(w: usize, h: usize, k: usize) -> [f64; 3] { + let mut s = [0.0; 3]; + let mut n = 0.0; + for y in h / 2 - 8..h / 2 + 8 { + for x in w / 2 - 8..w / 2 + 8 { + s[0] += ((x * 4 + k * 20) % 256) as f64; + s[1] += ((y * 5) % 256) as f64; + s[2] += ((k * 40) % 256) as f64; + n += 1.0; + } + } + s.map(|v| v / n) +} + +fn check_video(name: &str, w: u32, h: u32, frames: usize, codec: &str) { + let b = fixture(name); + let info = probe(&b).unwrap_or_else(|e| panic!("{name}: {e}")); + let v = info.video.clone().unwrap_or_else(|| panic!("{name}: no video info")); + assert_eq!((v.codec.as_str(), v.width, v.height, v.decodable), (codec, w, h, true), "{name}"); + assert!((v.fps - 10.0).abs() < 0.6, "{name}: {} fps", v.fps); + assert!((info.duration_ms as i64 - frames as i64 * 100).abs() <= 120, "{name}: {} ms", info.duration_ms); + let mut d = video::VideoDecoder::open(b.clone()).unwrap_or_else(|e| panic!("{name}: {e}")); + let mut got = vec![]; + while let Some(f) = d.next_frame().unwrap_or_else(|e| panic!("{name}: {e}")) { + assert_eq!((f.width, f.height, f.rgba.len()), (w, h, (w * h * 4) as usize), "{name}"); + got.push(f); + } + assert_eq!(got.len(), frames, "{name}"); + for (k, f) in got.iter().enumerate() { + assert!((f.time - k as f64 * 0.1).abs() < 0.02, "{name}: frame {k} at {}", f.time); + let (c, e) = (centre(f), expected_centre(w as usize, h as usize, k)); + for i in 0..3 { + assert!((c[i] - e[i]).abs() < 24.0, "{name}: frame {k} channel {i}: {c:?} vs {e:?}"); + } + } + // Seeking lands on the frame shown at that time. + d.seek(0.35); + let f = d.next_frame().ok().flatten().unwrap_or_else(|| panic!("{name}: no frame after seek")); + assert!((f.time - 0.3).abs() < 0.02, "{name}: seek gave {}", f.time); + // Poster frames. + let p = video::poster_frame(&b, 0.25).unwrap_or_else(|e| panic!("{name}: {e}")); + assert!((p.time - 0.2).abs() < 0.02, "{name}: poster at {}", p.time); + let png = video::poster_png(&b, 0.0).unwrap_or_else(|e| panic!("{name}: {e}")); + assert!(png.starts_with(&[0x89, b'P', b'N', b'G'])); +} + +#[test] +fn h264_mp4_with_b_frames_and_aac_audio() { + check_video("clip-h264.mp4", 64, 48, 5, "H.264"); + // Its audio track decodes too. + let b = fixture("clip-h264.mp4"); + let info = probe(&b).expect("probe"); + assert_eq!(info.audio.as_ref().map(|a| (a.codec.as_str(), a.decodable)), Some(("AAC", true))); + let p = audio::decode(&b).expect("audio of the video"); + assert!((frequency(&p) - 440.0).abs() < 15.0); +} + +#[test] +fn h264_mov() { + check_video("clip-h264.mov", 64, 48, 6, "H.264"); + assert_eq!(probe(&fixture("clip-h264.mov")).map(|i| i.container), Ok("mov")); +} + +#[test] +fn hevc_mp4() { + check_video("clip-hevc.mp4", 64, 48, 6, "HEVC"); +} + +#[test] +fn vp9_webm_with_opus_audio() { + check_video("clip-vp9.webm", 64, 48, 5, "VP9"); + let b = fixture("clip-vp9.webm"); + assert_eq!(probe(&b).ok().and_then(|i| i.audio).map(|a| (a.codec, a.sample_rate)), Some(("Opus".to_string(), 48_000))); + let p = audio::decode(&b).expect("opus in webm"); + assert_eq!(p.rate, 48_000); + assert!((frequency(&p) - 440.0).abs() < 15.0); +} + +#[test] +fn av1_mkv() { + check_video("clip-av1.mkv", 64, 64, 6, "AV1"); +} + +#[test] +fn video_feed_follows_the_clock() { + let mut feed = slidecraft_media::VideoFeed::new(fixture("clip-h264.mov")).expect("feed"); + assert_eq!(feed.size(), (64, 48)); + let t = Duration::from_secs(5); + assert!(feed.frame_at_blocking(0.0, t).is_some_and(|f| f.time == 0.0)); + let f = feed.frame_at_blocking(0.25, t).map(|f| f.time); + assert!(f.is_some_and(|v| (v - 0.2).abs() < 0.02), "{f:?}"); + // Jumping back (a loop) seeks. + let f = feed.frame_at_blocking(0.05, t).map(|f| f.time); + assert_eq!(f, Some(0.0)); + let f = feed.frame_at_blocking(0.55, t).map(|f| f.time); + assert!(f.is_some_and(|v| (v - 0.5).abs() < 0.02), "{f:?}"); +} + +#[test] +fn player_plays_decoded_audio_through_the_mixer() { + use slidecraft_media::{ClipParams, Player, VoiceSpec}; + let b: Bytes = Arc::new(wav(48_000, 1, &sine(48_000, 0.5))); + let mut p = Player::new(None); + p.play(1, 1, &b, VoiceSpec { clip: ClipParams { volume: 0.5, ..Default::default() }, looping: false, duration: 0.5, audio: true }, None); + assert!(p.wait_loaded(1, Duration::from_secs(5))); + let mut buf = vec![0.0f32; 4800 * 2]; + p.render(&mut buf, 2, 48_000); + let rms = (buf.iter().map(|s| s * s).sum::() / buf.len() as f32).sqrt(); + assert!((rms - 0.0884 * 0.5).abs() < 0.005, "{rms}"); + assert!((p.status(1).map(|s| s.position).unwrap_or(0.0) - 0.1).abs() < 1e-6); +} diff --git a/crates/media/tests/fixtures/clip-av1.mkv b/crates/media/tests/fixtures/clip-av1.mkv new file mode 100644 index 0000000..cb3c9f6 Binary files /dev/null and b/crates/media/tests/fixtures/clip-av1.mkv differ diff --git a/crates/media/tests/fixtures/clip-h264.mov b/crates/media/tests/fixtures/clip-h264.mov new file mode 100644 index 0000000..a35b422 Binary files /dev/null and b/crates/media/tests/fixtures/clip-h264.mov differ diff --git a/crates/media/tests/fixtures/clip-h264.mp4 b/crates/media/tests/fixtures/clip-h264.mp4 new file mode 100644 index 0000000..d8bd775 Binary files /dev/null and b/crates/media/tests/fixtures/clip-h264.mp4 differ diff --git a/crates/media/tests/fixtures/clip-hevc.mp4 b/crates/media/tests/fixtures/clip-hevc.mp4 new file mode 100644 index 0000000..02c866c Binary files /dev/null and b/crates/media/tests/fixtures/clip-hevc.mp4 differ diff --git a/crates/media/tests/fixtures/clip-vp9.webm b/crates/media/tests/fixtures/clip-vp9.webm new file mode 100644 index 0000000..5b63f4a Binary files /dev/null and b/crates/media/tests/fixtures/clip-vp9.webm differ diff --git a/crates/media/tests/fixtures/make.sh b/crates/media/tests/fixtures/make.sh new file mode 100755 index 0000000..463cffb --- /dev/null +++ b/crates/media/tests/fixtures/make.sh @@ -0,0 +1,37 @@ +#!/bin/sh +# Regenerates the media test fixtures. Every input is synthesised here (a 440 Hz sine and +# a moving colour gradient), so the files are contributor-original (CC0-1.0); see +# ATTRIBUTION.md. The encoders only transcode our own signal. WAV and AIFF fixtures are written +# by the tests themselves. +set -e +cd "$(dirname "$0")" +FF="ffmpeg -hide_banner -loglevel error -y" +TONE="-f lavfi -i sine=frequency=440:sample_rate=48000:duration=0.5" +TONE44="-f lavfi -i sine=frequency=440:sample_rate=44100:duration=0.5" +META="-map_metadata -1 -fflags +bitexact -flags:v +bitexact -flags:a +bitexact" +$FF $TONE44 -c:a libmp3lame -b:a 64k $META tone.mp3 +$FF $TONE44 -c:a aac -b:a 64k $META tone.m4a +$FF $TONE44 -c:a aac -b:a 64k $META -f adts tone.aac +$FF $TONE44 -c:a alac $META tone-alac.m4a +$FF $TONE44 -c:a flac $META tone.flac +$FF $TONE44 -c:a vorbis -strict -2 -ac 2 $META tone.ogg +$FF $TONE -c:a libopus -b:a 32k $META tone.opus +$FF $TONE44 -c:a wmav2 -b:a 64k $META tone.wma +# Video: 64x48 (AV1: 64x64), 10 fps, 6 frames of our own moving colour gradient (raw RGB from +# python3 below), so every pixel is contributor-original. +frames() { + python3 -c " +import sys +w, h = $1, $2 +for f in range(6): + sys.stdout.buffer.write(bytes(v for y in range(h) for x in range(w) for v in ((x * 4 + f * 20) % 256, (y * 5) % 256, (f * 40) % 256))) +" > "$3" +} +frames 64 48 /tmp/slidecraft-fixture-64x48.rgb +frames 64 64 /tmp/slidecraft-fixture-64x64.rgb +VID="-f rawvideo -pix_fmt rgb24 -s 64x48 -r 10 -i /tmp/slidecraft-fixture-64x48.rgb" +$FF $VID $TONE44 -c:v libx264 -pix_fmt yuv420p -g 5 -bf 2 -c:a aac -b:a 48k -shortest $META clip-h264.mp4 +$FF $VID -c:v libx264 -pix_fmt yuv420p -g 5 -bf 0 $META clip-h264.mov +$FF $VID -c:v libx265 -pix_fmt yuv420p -x265-params log-level=error:keyint=5 -tag:v hvc1 $META clip-hevc.mp4 +$FF $VID $TONE -c:v libvpx-vp9 -pix_fmt yuv420p -g 5 -b:v 100k -c:a libopus -b:a 32k -shortest $META clip-vp9.webm +$FF -f rawvideo -pix_fmt rgb24 -s 64x64 -r 10 -i /tmp/slidecraft-fixture-64x64.rgb -c:v libsvtav1 -pix_fmt yuv420p -g 5 -svtav1-params loglevel=0 $META clip-av1.mkv diff --git a/crates/media/tests/fixtures/tone-alac.m4a b/crates/media/tests/fixtures/tone-alac.m4a new file mode 100644 index 0000000..baa06d2 Binary files /dev/null and b/crates/media/tests/fixtures/tone-alac.m4a differ diff --git a/crates/media/tests/fixtures/tone.aac b/crates/media/tests/fixtures/tone.aac new file mode 100644 index 0000000..cd165cd Binary files /dev/null and b/crates/media/tests/fixtures/tone.aac differ diff --git a/crates/media/tests/fixtures/tone.flac b/crates/media/tests/fixtures/tone.flac new file mode 100644 index 0000000..d11d71a Binary files /dev/null and b/crates/media/tests/fixtures/tone.flac differ diff --git a/crates/media/tests/fixtures/tone.m4a b/crates/media/tests/fixtures/tone.m4a new file mode 100644 index 0000000..bcd0cd7 Binary files /dev/null and b/crates/media/tests/fixtures/tone.m4a differ diff --git a/crates/media/tests/fixtures/tone.mp3 b/crates/media/tests/fixtures/tone.mp3 new file mode 100644 index 0000000..55a5433 Binary files /dev/null and b/crates/media/tests/fixtures/tone.mp3 differ diff --git a/crates/media/tests/fixtures/tone.ogg b/crates/media/tests/fixtures/tone.ogg new file mode 100644 index 0000000..000604b Binary files /dev/null and b/crates/media/tests/fixtures/tone.ogg differ diff --git a/crates/media/tests/fixtures/tone.opus b/crates/media/tests/fixtures/tone.opus new file mode 100644 index 0000000..51f186d Binary files /dev/null and b/crates/media/tests/fixtures/tone.opus differ diff --git a/crates/media/tests/fixtures/tone.wma b/crates/media/tests/fixtures/tone.wma new file mode 100644 index 0000000..1a566cf Binary files /dev/null and b/crates/media/tests/fixtures/tone.wma differ diff --git a/crates/model/src/lib.rs b/crates/model/src/lib.rs index c69eb24..a7845cc 100644 --- a/crates/model/src/lib.rs +++ b/crates/model/src/lib.rs @@ -240,6 +240,11 @@ pub struct MediaClip { pub full_screen: bool, /// Bookmarks (name, ms). pub bookmarks: Vec<(String, u32)>, + /// Probed length in ms (0 = unknown). + pub duration_ms: u32, + /// Probed picture size of a video in pixels (0 = unknown). + pub width: u32, + pub height: u32, } /// One ink stroke (Draw tab): points in slide coordinates with pressure. diff --git a/crates/ogg/Cargo.toml b/crates/ogg/Cargo.toml new file mode 100644 index 0000000..f30ddf4 --- /dev/null +++ b/crates/ogg/Cargo.toml @@ -0,0 +1,12 @@ +[package] +name = "slidecraft-ogg" +description = "Clean-room Ogg (RFC 3533) demuxer with Ogg Opus (RFC 7845) and Ogg Vorbis stream mapping" +version.workspace = true +edition.workspace = true +license.workspace = true +rust-version.workspace = true + +[lints] +workspace = true + +[dependencies] diff --git a/crates/ogg/src/lib.rs b/crates/ogg/src/lib.rs new file mode 100644 index 0000000..6100fd4 --- /dev/null +++ b/crates/ogg/src/lib.rs @@ -0,0 +1,494 @@ +//! Clean-room Ogg demuxer, written from RFC 3533 (the Ogg encapsulation format), RFC 7845 (Ogg +//! Opus) and the Vorbis I specification's Ogg mapping (section A). +//! +//! [`open`] reads every page (capture pattern, header, lacing values, CRC-32 checked; damaged +//! regions are skipped by searching for the next `OggS`), groups them by logical bitstream and +//! assembles packets — including packets that continue across pages — as lists of byte ranges, +//! with the granule position of the page each packet completes on. Codec headers (Opus: +//! `OpusHead` + `OpusTags`; Vorbis: identification, comment, setup) are collected separately from +//! the audio packets. +//! +//! [`OpusTiming`] implements RFC 7845 §4: packet start positions from granule positions and +//! TOC-derived durations, the pre-skip, and end trimming of the final page, so a player can seek +//! to any 48 kHz sample. +//! +//! Layer L0: no dependencies beyond `std`, no `unsafe`, builds for `wasm32-unknown-unknown`. + +#![cfg_attr(not(test), deny(clippy::unwrap_used, clippy::expect_used, clippy::panic, clippy::unimplemented, clippy::todo, clippy::unreachable))] + +use std::collections::HashMap; +use std::fmt; +use std::io; + +/// Random-access, read-only byte source (same shape as the other FilmCraft demuxers'). +pub trait ByteSource { + fn len(&self) -> u64; + fn read_at(&self, offset: u64, buf: &mut [u8]) -> io::Result<()>; + fn is_empty(&self) -> bool { + self.len() == 0 + } +} + +impl ByteSource for [u8] { + fn len(&self) -> u64 { + <[u8]>::len(self) as u64 + } + fn read_at(&self, offset: u64, buf: &mut [u8]) -> io::Result<()> { + let eof = || io::Error::new(io::ErrorKind::UnexpectedEof, "read past end"); + let a = usize::try_from(offset).map_err(|_| eof())?; + buf.copy_from_slice(self.get(a..a.checked_add(buf.len()).ok_or_else(eof)?).ok_or_else(eof)?); + Ok(()) + } +} + +impl ByteSource for Vec { + fn len(&self) -> u64 { + self.as_slice().len() as u64 + } + fn read_at(&self, offset: u64, buf: &mut [u8]) -> io::Result<()> { + self.as_slice().read_at(offset, buf) + } +} + +impl ByteSource for &T { + fn len(&self) -> u64 { + (**self).len() + } + fn read_at(&self, offset: u64, buf: &mut [u8]) -> io::Result<()> { + (**self).read_at(offset, buf) + } +} + +#[derive(Debug)] +pub enum Error { + Io(io::Error), + /// No Ogg page found. + NotOgg, + Invalid(String), +} + +impl fmt::Display for Error { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + match self { + Error::Io(e) => write!(f, "I/O error: {e}"), + Error::NotOgg => write!(f, "not an Ogg file"), + Error::Invalid(s) => write!(f, "invalid Ogg: {s}"), + } + } +} + +impl std::error::Error for Error {} + +impl From for Error { + fn from(e: io::Error) -> Self { + Error::Io(e) + } +} + +pub type Result = std::result::Result; + +/// The capture pattern at the start of a file. +pub fn sniff(head: &[u8]) -> bool { + head.len() >= 27 && &head[..4] == b"OggS" && head[4] == 0 +} + +/// CRC-32 of RFC 3533 §6: polynomial 0x04C11DB7, initial value 0, no reflection, no final XOR. +pub fn crc32(data: &[u8]) -> u32 { + static TABLE: std::sync::OnceLock<[u32; 256]> = std::sync::OnceLock::new(); + let t = TABLE.get_or_init(|| { + let mut t = [0u32; 256]; + for (i, e) in t.iter_mut().enumerate() { + let mut r = (i as u32) << 24; + for _ in 0..8 { + r = if r & 0x8000_0000 != 0 { (r << 1) ^ 0x04C1_1DB7 } else { r << 1 }; + } + *e = r; + } + t + }); + data.iter().fold(0u32, |c, &b| (c << 8) ^ t[((c >> 24) as u8 ^ b) as usize]) +} + +/// The codec of a logical bitstream (from its first packet). +#[derive(Clone, Copy, Debug, PartialEq, Eq)] +pub enum Codec { + Opus, + Vorbis, + Flac, + Theora, + Speex, + Unknown, +} + +impl Codec { + fn of(first: &[u8]) -> Codec { + if first.starts_with(b"OpusHead") { + Codec::Opus + } else if first.starts_with(b"\x01vorbis") { + Codec::Vorbis + } else if first.starts_with(b"\x7fFLAC") { + Codec::Flac + } else if first.starts_with(b"\x80theora") { + Codec::Theora + } else if first.starts_with(b"Speex ") { + Codec::Speex + } else { + Codec::Unknown + } + } + /// Header packets before the audio packets. + fn header_count(self) -> usize { + match self { + Codec::Opus => 2, + Codec::Vorbis | Codec::Theora => 3, + Codec::Speex => 2, + Codec::Flac | Codec::Unknown => 1, + } + } + pub fn name(self) -> &'static str { + match self { + Codec::Opus => "Opus", + Codec::Vorbis => "Vorbis", + Codec::Flac => "FLAC", + Codec::Theora => "Theora", + Codec::Speex => "Speex", + Codec::Unknown => "unknown", + } + } +} + +/// A packet: its byte ranges (a packet may span pages) and the granule position of the page it +/// completes on when it is that page's last completed packet. +#[derive(Clone, Debug, PartialEq, Eq)] +pub struct Packet { + pub parts: Vec<(u64, u32)>, + pub size: u32, + /// Granule position of the completing page if this is the last packet completed on it. + pub granule: Option, + /// Index of the page (within the stream) the packet completes on. + pub page: u32, + /// The packet completes on the stream's end-of-stream page. + pub eos: bool, + /// The first bytes of the packet (TOC for Opus, packet type for Vorbis). + pub prefix: [u8; 2], +} + +/// A logical bitstream. +#[derive(Clone, Debug)] +pub struct Stream { + pub serial: u32, + pub codec: Codec, + /// Codec header packets (contents). + pub headers: Vec>, + /// Data packets after the headers. + pub packets: Vec, + pub pages: u32, + /// Granule position of the last page with one. + pub last_granule: Option, + pub saw_eos: bool, +} + +/// An opened Ogg file. +#[derive(Clone, Debug)] +pub struct OggFile { + pub streams: Vec, + /// Pages dropped for a bad CRC, lost sync, truncation… + pub warnings: Vec, +} + +struct Partial { + parts: Vec<(u64, u32)>, + size: u32, + prefix: Vec, +} + +/// Read every page and assemble the packets of every logical bitstream. +pub fn open(src: &(impl ByteSource + ?Sized)) -> Result { + let len = src.len(); + let mut streams: Vec = Vec::new(); + let mut by_serial: HashMap = HashMap::new(); + let mut partial: HashMap = HashMap::new(); + let mut warnings = Vec::new(); + let mut pos = 0u64; + let mut found_any = false; + let mut hdr = [0u8; 27 + 255]; + while pos + 27 <= len { + src.read_at(pos, &mut hdr[..27])?; + if &hdr[..4] != b"OggS" || hdr[4] != 0 { + match find_capture(src, pos + 1, len)? { + Some(p) => { + if found_any { + warnings.push(format!("lost sync at byte {pos}; resumed at {p}")); + } + pos = p; + continue; + } + None => break, + } + } + let nseg = hdr[26] as usize; + if pos + 27 + nseg as u64 > len { + warnings.push(format!("truncated page header at byte {pos}")); + break; + } + src.read_at(pos + 27, &mut hdr[27..27 + nseg])?; + let body_len: u64 = hdr[27..27 + nseg].iter().map(|&b| b as u64).sum(); + let page_len = 27 + nseg as u64 + body_len; + if pos + page_len > len { + warnings.push(format!("truncated page at byte {pos}")); + break; + } + let mut page = vec![0u8; page_len as usize]; + src.read_at(pos, &mut page)?; + let stored_crc = u32::from_le_bytes([page[22], page[23], page[24], page[25]]); + page[22..26].fill(0); + if crc32(&page) != stored_crc { + warnings.push(format!("bad CRC on page at byte {pos}")); + pos += 1; + continue; + } + found_any = true; + let flags = page[5]; + let granule = i64::from_le_bytes(page[6..14].try_into().unwrap_or([0; 8])); + let serial = u32::from_le_bytes([page[14], page[15], page[16], page[17]]); + let lacing = page[27..27 + nseg].to_vec(); + let body_off = pos + 27 + nseg as u64; + let si = *by_serial.entry(serial).or_insert_with(|| { + streams.push(Stream { + serial, + codec: Codec::Unknown, + headers: Vec::new(), + packets: Vec::new(), + pages: 0, + last_granule: None, + saw_eos: false, + }); + streams.len() - 1 + }); + let st = &mut streams[si]; + let page_index = st.pages; + st.pages += 1; + if flags & 0x04 != 0 { + st.saw_eos = true; + } + // a continued page without a pending packet (lost a page): drop the fragment + let continued = flags & 0x01 != 0; + if !continued && partial.remove(&serial).is_some() { + warnings.push(format!("incomplete packet before page at byte {pos}")); + } + let mut completed: Vec = Vec::new(); + let mut off = 0u64; + let mut seg_start = 0u64; + let mut skip_fragment = continued && !partial.contains_key(&serial); + for (k, &l) in lacing.iter().enumerate() { + off += l as u64; + if l < 255 || k + 1 == nseg { + let (a, n) = (seg_start, off - seg_start); + seg_start = off; + if skip_fragment { + if l < 255 { + skip_fragment = false; + } + continue; + } + let p = partial.entry(serial).or_insert_with(|| Partial { parts: Vec::new(), size: 0, prefix: Vec::new() }); + if n > 0 { + p.parts.push((body_off + a, n as u32)); + p.size += n as u32; + let s = (body_off - pos + a) as usize; + let need = 2usize.saturating_sub(p.prefix.len()).min(n as usize); + p.prefix.extend_from_slice(&page[s..s + need]); + } + if l < 255 + && let Some(p) = partial.remove(&serial) + { + let mut prefix = [0u8; 2]; + prefix[..p.prefix.len().min(2)].copy_from_slice(&p.prefix[..p.prefix.len().min(2)]); + completed.push(Packet { parts: p.parts, size: p.size, granule: None, page: page_index, eos: flags & 0x04 != 0, prefix }); + } + } + } + if granule != -1 { + st.last_granule = Some(granule); + if let Some(last) = completed.last_mut() { + last.granule = Some(granule); + } + } + for pk in completed { + if st.headers.is_empty() && st.packets.is_empty() { + let data = read_parts(src, &pk.parts)?; + st.codec = Codec::of(&data); + } + if st.headers.len() < st.codec.header_count() && st.packets.is_empty() { + st.headers.push(read_parts(src, &pk.parts)?); + } else { + st.packets.push(pk); + } + } + pos += page_len; + } + if !found_any { + return Err(Error::NotOgg); + } + if !partial.is_empty() { + warnings.push("file ends inside a packet".into()); + } + Ok(OggFile { streams, warnings }) +} + +fn find_capture(src: &(impl ByteSource + ?Sized), from: u64, len: u64) -> Result> { + let mut at = from; + while at + 4 <= len { + let n = ((len - at) as usize).min(1 << 20); + let mut buf = vec![0u8; n]; + src.read_at(at, &mut buf)?; + if let Some(i) = buf.windows(4).position(|w| w == b"OggS") { + return Ok(Some(at + i as u64)); + } + if n < 4 { + break; + } + at += (n - 3) as u64; + } + Ok(None) +} + +fn read_parts(src: &(impl ByteSource + ?Sized), parts: &[(u64, u32)]) -> Result> { + let mut v = Vec::with_capacity(parts.iter().map(|p| p.1 as usize).sum()); + for &(o, n) in parts { + let a = v.len(); + v.resize(a + n as usize, 0); + src.read_at(o, &mut v[a..])?; + } + Ok(v) +} + +impl OggFile { + /// The first stream of `codec`. + pub fn stream_of(&self, codec: Codec) -> Option { + self.streams.iter().position(|s| s.codec == codec) + } + /// The bytes of packet `i` of stream `s`. + pub fn read_packet(&self, src: &(impl ByteSource + ?Sized), s: usize, i: usize) -> Result> { + let p = self.streams.get(s).and_then(|st| st.packets.get(i)).ok_or_else(|| Error::Invalid(format!("no packet {i} in stream {s}")))?; + read_parts(src, &p.parts) + } +} + +/// Duration of an Opus packet in 48 kHz samples from its TOC (RFC 6716 §3.1); `None` if invalid. +pub fn opus_packet_samples(prefix: [u8; 2], size: u32) -> Option { + if size == 0 { + return None; + } + let toc = prefix[0]; + let config = toc >> 3; + let frame = match config { + 0..=11 => [480, 960, 1920, 2880][(config & 3) as usize], + 12..=15 => [480, 960][(config & 1) as usize], + _ => [120, 240, 480, 960][(config & 3) as usize], + }; + let frames = match toc & 3 { + 0 => 1, + 1 | 2 => 2, + _ => { + if size < 2 { + return None; + } + (prefix[1] & 0x3F) as u32 + } + }; + let n = frame * frames; + (n > 0 && n <= 5760).then_some(n) +} + +/// RFC 7845 §4 timing of an Opus stream. +#[derive(Clone, Debug, PartialEq, Eq)] +pub struct OpusTiming { + pub pre_skip: u32, + /// Start of each packet's decoded output in output samples (48 kHz): 0 is the first sample + /// after the pre-skip; the pre-skip samples have negative positions. + pub starts: Vec, + /// Decoded samples of each packet. + pub durations: Vec, + /// Playable samples (end trimming applied). + pub total: i64, +} + +impl OpusTiming { + /// Timing of the Opus stream `s` (`pre_skip` from its `OpusHead`). + pub fn of(stream: &Stream, pre_skip: u32) -> OpusTiming { + let durations: Vec = stream.packets.iter().map(|p| opus_packet_samples(p.prefix, p.size).unwrap_or(0)).collect(); + let n = durations.len(); + let mut ends: Vec> = vec![None; n]; + let mut i = 0; + let mut cursor: Option = None; + let mut trimmed_end: Option = None; + while i < n { + // packets completing on the same page: i..j, the last carries the page granule + let mut j = i; + while j < n && stream.packets[j].granule.is_none() { + j += 1; + } + if j == n { + // trailing packets without a granule (truncated file): continue from the cursor + let mut c = cursor.unwrap_or(0); + for k in i..n { + c += durations[k] as i64; + ends[k] = Some(c); + } + break; + } + let Some(g) = stream.packets[j].granule else { break }; + let sum: i64 = durations[i..=j].iter().map(|&d| d as i64).sum(); + let forward = cursor.map(|c| c + sum); + let end = match forward { + // end trimming on the last page (RFC 7845 §4.5): keep the forward positions + Some(f) if stream.packets[j].eos && g < f => { + trimmed_end = Some(g); + f + } + Some(f) if f == g => f, + // first page, or a discontinuity: trust the granule + _ => g, + }; + let mut e = end; + for k in (i..=j).rev() { + ends[k] = Some(e); + e -= durations[k] as i64; + } + cursor = Some(end); + i = j + 1; + } + let raw_starts: Vec = ends.iter().zip(&durations).map(|(e, &d)| e.unwrap_or(0) - d as i64).collect(); + let base = raw_starts.first().copied().unwrap_or(0).max(0); + let shift = base + pre_skip as i64; + let starts: Vec = raw_starts.iter().map(|s| s - shift).collect(); + let end_all = trimmed_end.or(cursor).unwrap_or(0); + let total = (end_all - shift).max(0); + OpusTiming { pre_skip, starts, durations, total } + } +} + +/// Vorbis identification header fields (Vorbis I §4.2.2). +#[derive(Clone, Copy, Debug, PartialEq, Eq)] +pub struct VorbisInfo { + pub channels: u8, + pub sample_rate: u32, + pub bitrate_nominal: i32, +} + +impl VorbisInfo { + pub fn parse(h: &[u8]) -> Option { + if h.len() < 30 || !h.starts_with(b"\x01vorbis") { + return None; + } + Some(VorbisInfo { + channels: h[11], + sample_rate: u32::from_le_bytes([h[12], h[13], h[14], h[15]]), + bitrate_nominal: i32::from_le_bytes([h[20], h[21], h[22], h[23]]), + }) + } +} + +#[cfg(test)] +mod tests; diff --git a/crates/ogg/src/tests.rs b/crates/ogg/src/tests.rs new file mode 100644 index 0000000..f1879b3 --- /dev/null +++ b/crates/ogg/src/tests.rs @@ -0,0 +1,181 @@ +//! Synthetic Ogg streams: a small page writer (RFC 3533) and Opus-like packets. + +use super::*; + +/// Write one page holding `segments` (lacing already computed by `lace`). +fn page(out: &mut Vec, flags: u8, granule: i64, serial: u32, seq: u32, lacing: &[u8], body: &[u8]) { + let start = out.len(); + out.extend_from_slice(b"OggS"); + out.push(0); + out.push(flags); + out.extend_from_slice(&granule.to_le_bytes()); + out.extend_from_slice(&serial.to_le_bytes()); + out.extend_from_slice(&seq.to_le_bytes()); + out.extend_from_slice(&[0; 4]); + out.push(lacing.len() as u8); + out.extend_from_slice(lacing); + out.extend_from_slice(body); + let crc = crc32(&out[start..]); + out[start + 22..start + 26].copy_from_slice(&crc.to_le_bytes()); +} + +fn lace(packets: &[&[u8]], last_continues: bool) -> (Vec, Vec) { + let mut l = Vec::new(); + let mut b = Vec::new(); + for (i, p) in packets.iter().enumerate() { + let mut n = p.len(); + while n >= 255 { + l.push(255); + n -= 255; + } + if !(last_continues && i + 1 == packets.len()) { + l.push(n as u8); + } + b.extend_from_slice(p); + } + (l, b) +} + +fn opus_head(pre_skip: u16) -> Vec { + let mut h = b"OpusHead".to_vec(); + h.push(1); + h.push(2); + h.extend_from_slice(&pre_skip.to_le_bytes()); + h.extend_from_slice(&48_000u32.to_le_bytes()); + h.extend_from_slice(&0i16.to_le_bytes()); + h.push(0); + h +} + +/// A CELT 20 ms packet (960 samples): TOC config 31, code 0. +fn pkt(i: usize, len: usize) -> Vec { + let mut p = vec![0xF8]; + p.extend((0..len.saturating_sub(1)).map(|k| (i * 7 + k) as u8)); + p +} + +/// Header pages, then pages of 3 packets each; the last page's granule trims `trim` samples. +fn opus_file(npk: usize, pre_skip: u16, trim: i64) -> Vec { + let mut f = Vec::new(); + let head = opus_head(pre_skip); + let (l, b) = lace(&[&head], false); + page(&mut f, 0x02, 0, 9, 0, &l, &b); + let tags = b"OpusTags\x00\x00\x00\x00\x00\x00\x00\x00".to_vec(); + let (l, b) = lace(&[&tags], false); + page(&mut f, 0, 0, 9, 1, &l, &b); + let pkts: Vec> = (0..npk).map(|i| pkt(i, 40 + i % 5)).collect(); + let mut done = 0; + for (seq, chunk) in (2..).zip(pkts.chunks(3)) { + done += chunk.len(); + let last = done == npk; + let mut g = done as i64 * 960; + if last { + g -= trim; + } + let refs: Vec<&[u8]> = chunk.iter().map(Vec::as_slice).collect(); + let (l, b) = lace(&refs, false); + page(&mut f, if last { 0x04 } else { 0 }, g, 9, seq, &l, &b); + } + f +} + +#[test] +fn crc_matches_known_value() { + // CRC-32/CKSUM parameters without its final XOR (check value 0x765E7680 ^ 0xFFFFFFFF) + assert_eq!(crc32(b"123456789"), 0x89A1_897F); + assert_eq!(crc32(b""), 0); + assert_ne!(crc32(b"OggS"), crc32(b"OggT")); +} + +#[test] +fn opus_pages_packets_and_timing() { + let f = opus_file(10, 312, 500); + assert!(sniff(&f)); + let o = open(&f).unwrap(); + assert!(o.warnings.is_empty(), "{:?}", o.warnings); + let s = o.stream_of(Codec::Opus).unwrap(); + let st = &o.streams[s]; + assert_eq!(st.headers.len(), 2); + assert_eq!(st.packets.len(), 10); + assert!(st.saw_eos); + assert_eq!(o.read_packet(&f, s, 4).unwrap(), pkt(4, 44)); + let t = OpusTiming::of(st, 312); + assert_eq!(t.durations, vec![960; 10]); + assert_eq!(t.starts[0], -312); + assert_eq!(t.starts[9], 9 * 960 - 312); + // end trimming: the last page granule is 500 short + assert_eq!(t.total, 10 * 960 - 500 - 312); +} + +#[test] +fn packets_spanning_pages() { + let mut f = Vec::new(); + let head = opus_head(0); + let (l, b) = lace(&[&head], false); + page(&mut f, 0x02, 0, 1, 0, &l, &b); + let (l, b) = lace(&[b"OpusTags"], false); + page(&mut f, 0, 0, 1, 1, &l, &b); + let big = pkt(0, 700); + // first 510 bytes on one page (two 255 lacing values, packet continues), rest on the next + let (l, b) = (vec![255, 255], big[..510].to_vec()); + page(&mut f, 0, -1, 1, 2, &l, &b); + let rest = &big[510..]; + let small = pkt(1, 30); + let (l, b) = lace(&[rest, &small], false); + page(&mut f, 0x01 | 0x04, 1920, 1, 3, &l, &b); + let o = open(&f).unwrap(); + let st = &o.streams[0]; + assert_eq!(st.packets.len(), 2); + assert_eq!(st.packets[0].parts.len(), 2); + assert_eq!(o.read_packet(&f, 0, 0).unwrap(), big); + assert_eq!(o.read_packet(&f, 0, 1).unwrap(), small); + let t = OpusTiming::of(st, 0); + assert_eq!(t.starts, vec![0, 960]); + assert_eq!(t.total, 1920); +} + +#[test] +fn corrupt_pages_are_skipped_and_truncation_is_safe() { + let f = opus_file(12, 312, 0); + // flip a byte inside the third data page's body: that page fails its CRC and is dropped + let mut g = f.clone(); + let third = g.windows(4).enumerate().filter(|(_, w)| *w == b"OggS").map(|(i, _)| i).nth(4).unwrap(); + g[third + 40] ^= 0xFF; + let o = open(&g).unwrap(); + assert!(o.warnings.iter().any(|w| w.contains("CRC")), "{:?}", o.warnings); + assert_eq!(o.streams[0].packets.len(), 9); + let t = OpusTiming::of(&o.streams[0], 312); + // the granules after the gap put the later packets at their true positions + assert_eq!(*t.starts.last().unwrap(), 11 * 960 - 312); + for cut in 0..f.len() { + if let Ok(o) = open(&f[..cut].to_vec()) { + for st in &o.streams { + let t = OpusTiming::of(st, 312); + assert_eq!(t.starts.len(), st.packets.len()); + } + } + } + let mut seed = 99u64; + for _ in 0..300 { + let mut g = f.clone(); + seed = seed.wrapping_mul(6364136223846793005).wrapping_add(1442695040888963407); + let at = (seed >> 33) as usize % g.len(); + g[at] = (seed >> 20) as u8; + if let Ok(o) = open(&g) { + for st in &o.streams { + let _ = OpusTiming::of(st, 312); + } + } + } + assert!(matches!(open(&b"not ogg at all, no capture pattern".to_vec()), Err(Error::NotOgg))); +} + +#[test] +fn opus_toc_durations() { + assert_eq!(opus_packet_samples([0x00, 0], 10), Some(480)); // SILK NB 10 ms + assert_eq!(opus_packet_samples([0x18, 0], 10), Some(2880)); // SILK NB 60 ms + assert_eq!(opus_packet_samples([0x78 | 1, 0], 10), Some(1920)); // hybrid 20 ms × 2 + assert_eq!(opus_packet_samples([0x80 | 3, 6], 10), Some(720)); // CELT 2.5 ms × 6 + assert_eq!(opus_packet_samples([0xF8 | 3, 7], 10), None); // 140 ms: invalid + assert_eq!(opus_packet_samples([0xF8, 0], 0), None); +} diff --git a/crates/opus/Cargo.toml b/crates/opus/Cargo.toml new file mode 100644 index 0000000..e911357 --- /dev/null +++ b/crates/opus/Cargo.toml @@ -0,0 +1,12 @@ +[package] +name = "slidecraft-opus" +description = "Clean-room pure-Rust Opus decoder (RFC 6716 / RFC 8251; OpusHead, multistream surround)" +version.workspace = true +edition.workspace = true +license.workspace = true +rust-version.workspace = true + +[lints] +workspace = true + +[dependencies] diff --git a/crates/opus/src/celt/bands.rs b/crates/opus/src/celt/bands.rs new file mode 100644 index 0000000..74f1909 --- /dev/null +++ b/crates/opus/src/celt/bands.rs @@ -0,0 +1,988 @@ +//! Band shape decoding: PVQ, spreading, TF changes, band splitting, stereo, folding +//! (RFC 6716 §4.3.4 – §4.3.6). + +use super::rate::{Mode, QTHETA_OFFSET, QTHETA_OFFSET_TWOPHASE, get_pulses}; +use super::tables::*; +use crate::range::{BITRES, RangeDecoder, ilog}; + +pub const SPREAD_NONE: usize = 0; +pub const SPREAD_AGGRESSIVE: usize = 3; + +#[inline] +pub fn lcg_rand(seed: u32) -> u32 { + seed.wrapping_mul(1_664_525).wrapping_add(1_013_904_223) +} + +/// Decodes a PVQ codeword of `k` pulses in `n` dimensions; returns the sum of squares. +fn decode_pulses(y: &mut [i32], n: usize, k: usize, dec: &mut RangeDecoder, v: &mut Vec) -> f32 { + // Table of V(nn, kk) for nn in 0..=n, kk in 0..=k (saturating). + let w = k + 1; + v.clear(); + v.resize((n + 1) * w, 0); + v[0] = 1; + for nn in 1..=n { + v[nn * w] = 1; + for kk in 1..=k { + let a = v[(nn - 1) * w + kk]; + let b = v[nn * w + kk - 1]; + let c = v[(nn - 1) * w + kk - 1]; + v[nn * w + kk] = a.saturating_add(b).saturating_add(c); + } + } + let total = v[n * w + k]; + let ft = total.min(u32::MAX as u64) as u32; + let mut i = if ft >= 2 { dec.uint(ft) as u64 } else { 0 }; + let mut kk = k; + let mut ryy = 0f32; + for j in 0..n { + let rem = n - j; + if kk == 0 { + y[j] = 0; + continue; + } + let vv = |nn: usize, kx: usize| v[nn * w + kx]; + let mut p = (vv(rem - 1, kk) + vv(rem, kk)) / 2; + let neg = i >= p; + if neg { + i -= p; + } + let k0 = kk; + p -= vv(rem - 1, kk); + while p > i && kk > 0 { + kk -= 1; + p -= vv(rem - 1, kk); + } + let mag = (k0 - kk) as i32; + y[j] = if neg { -mag } else { mag }; + i -= p.min(i); + ryy += (mag * mag) as f32; + } + ryy +} + +fn exp_rotation1(x: &mut [f32], len: usize, stride: usize, c: f32, s: f32) { + let ms = -s; + if len <= stride { + return; + } + for i in 0..len - stride { + let x1 = x[i]; + let x2 = x[i + stride]; + x[i + stride] = c * x2 + s * x1; + x[i] = c * x1 + ms * x2; + } + if len >= 2 * stride + 1 { + for i in (0..=len - 2 * stride - 1).rev() { + let x1 = x[i]; + let x2 = x[i + stride]; + x[i + stride] = c * x2 + s * x1; + x[i] = c * x1 + ms * x2; + } + } +} + +/// Inverse spreading rotation (decoder direction). +fn exp_rotation(x: &mut [f32], len: usize, stride: usize, k: usize, spread: usize) { + const SPREAD_FACTOR: [usize; 3] = [15, 10, 5]; + if 2 * k >= len || spread == SPREAD_NONE { + return; + } + let factor = SPREAD_FACTOR[spread - 1]; + let gain = len as f32 / (len + factor * k) as f32; + let theta = 0.5 * gain * gain; + let c = (0.5 * std::f32::consts::PI * theta).cos(); + let s = (0.5 * std::f32::consts::PI * (1.0 - theta)).cos(); + let mut stride2 = 0; + if len >= 8 * stride { + stride2 = 1; + while (stride2 * stride2 + stride2) * stride + (stride >> 2) < len { + stride2 += 1; + } + } + let sub = len / stride; + for i in 0..stride { + let xs = &mut x[i * sub..(i + 1) * sub]; + if stride2 != 0 { + exp_rotation1(xs, sub, stride2, s, c); + } + exp_rotation1(xs, sub, 1, c, s); + } +} + +fn extract_collapse_mask(iy: &[i32], n: usize, b: usize) -> u32 { + if b <= 1 { + return 1; + } + let n0 = n / b; + let mut mask = 0u32; + for i in 0..b { + if iy[i * n0..(i + 1) * n0].iter().any(|&v| v != 0) { + mask |= 1 << i; + } + } + mask +} + +pub fn renormalise_vector(x: &mut [f32], gain: f32) { + let e: f32 = 1e-15 + x.iter().map(|v| v * v).sum::(); + let g = gain / e.sqrt(); + for v in x.iter_mut() { + *v *= g; + } +} + +fn haar1(x: &mut [f32], n0: usize, stride: usize) { + let n0 = n0 >> 1; + const S: f32 = std::f32::consts::FRAC_1_SQRT_2; + for i in 0..stride { + for j in 0..n0 { + let a = x[stride * 2 * j + i] * S; + let b = x[stride * (2 * j + 1) + i] * S; + x[stride * 2 * j + i] = a + b; + x[stride * (2 * j + 1) + i] = a - b; + } + } +} + +fn ordery(stride: usize) -> &'static [usize] { + &ORDERY[stride - 2..2 * stride - 2] +} + +fn deinterleave_hadamard(x: &mut [f32], n0: usize, stride: usize, hadamard: bool, tmp: &mut Vec) { + let n = n0 * stride; + tmp.clear(); + tmp.resize(n, 0.0); + if hadamard { + let ord = ordery(stride); + for i in 0..stride { + for j in 0..n0 { + tmp[ord[i] * n0 + j] = x[j * stride + i]; + } + } + } else { + for i in 0..stride { + for j in 0..n0 { + tmp[i * n0 + j] = x[j * stride + i]; + } + } + } + x[..n].copy_from_slice(tmp); +} + +fn interleave_hadamard(x: &mut [f32], n0: usize, stride: usize, hadamard: bool, tmp: &mut Vec) { + let n = n0 * stride; + tmp.clear(); + tmp.resize(n, 0.0); + if hadamard { + let ord = ordery(stride); + for i in 0..stride { + for j in 0..n0 { + tmp[j * stride + i] = x[ord[i] * n0 + j]; + } + } + } else { + for i in 0..stride { + for j in 0..n0 { + tmp[j * stride + i] = x[i * n0 + j]; + } + } + } + x[..n].copy_from_slice(tmp); +} + +#[inline] +fn frac_mul16(a: i32, b: i32) -> i32 { + (16384 + (a as i16 as i32) * (b as i16 as i32)) >> 15 +} + +fn bitexact_cos(x: i32) -> i32 { + let tmp = (4096 + x * x) >> 13; + let x2 = tmp; + let x2 = (32767 - x2) + frac_mul16(x2, -7651 + frac_mul16(x2, 8277 + frac_mul16(-626, x2))); + 1 + x2 +} + +fn bitexact_log2tan(isin: i32, icos: i32) -> i32 { + let lc = ilog(icos as u32); + let ls = ilog(isin as u32); + let icos = icos << (15 - lc); + let isin = isin << (15 - ls); + (ls - lc) * (1 << 11) + frac_mul16(isin, frac_mul16(isin, -2597) + 7932) - frac_mul16(icos, frac_mul16(icos, -2597) + 7932) +} + +fn isqrt32(v: u32) -> u32 { + let mut r = (v as f64).sqrt() as u32; + while (r as u64) * (r as u64) > v as u64 { + r -= 1; + } + while ((r + 1) as u64) * ((r + 1) as u64) <= v as u64 { + r += 1; + } + r +} + +fn compute_qn(n: i32, b: i32, offset: i32, pulse_cap: i32, stereo: bool) -> i32 { + const EXP2_TABLE8: [i32; 8] = [16384, 17866, 19483, 21247, 23170, 25267, 27554, 30048]; + let mut n2 = 2 * n - 1; + if stereo && n == 2 { + n2 -= 1; + } + let mut qb = sdiv(b + n2 * offset, n2); + qb = qb.min(b - pulse_cap - (4 << BITRES)); + qb = qb.min(8 << BITRES); + if qb < (1 << BITRES >> 1) { + 1 + } else { + let qn = EXP2_TABLE8[(qb & 0x7) as usize] >> (14 - (qb >> BITRES)); + (qn + 1) >> 1 << 1 + } +} + +/// C-style truncating signed division. +#[inline] +fn sdiv(a: i32, b: i32) -> i32 { + a / b +} + +pub struct BandCtx<'m, 'd, 'b> { + pub m: &'m Mode, + pub dec: &'d mut RangeDecoder<'b>, + pub i: usize, + pub intensity: usize, + pub spread: usize, + pub tf_change: i32, + pub remaining_bits: i32, + pub seed: u32, + pub disable_inv: bool, + pub avoid_split_noise: bool, + pub pvq_v: Vec, + pub iy: Vec, + pub tmp: Vec, +} + +struct Split { + inv: bool, + imid: i32, + iside: i32, + delta: i32, + itheta: i32, + qalloc: i32, +} + +impl BandCtx<'_, '_, '_> { + fn compute_theta(&mut self, n: i32, b: &mut i32, bb: usize, b0: usize, lm: i32, stereo: bool, fill: &mut u32) -> Split { + let i = self.i; + let pulse_cap = self.m.log_n[i] + lm * (1 << BITRES); + let offset = (pulse_cap >> 1) - if stereo && n == 2 { QTHETA_OFFSET_TWOPHASE } else { QTHETA_OFFSET }; + let mut qn = compute_qn(n, *b, offset, pulse_cap, stereo); + if stereo && i >= self.intensity { + qn = 1; + } + let tell = self.dec.tell_frac() as i32; + let mut itheta = 0i32; + let mut inv = false; + if qn != 1 { + if stereo && n > 2 { + let p0 = 3u32; + let x0 = (qn / 2) as u32; + let ft = p0 * (x0 + 1) + x0; + let fs = self.dec.decode(ft); + let x = if fs < (x0 + 1) * p0 { fs / p0 } else { x0 + 1 + (fs - (x0 + 1) * p0) }; + let (fl, fh) = if x <= x0 { (p0 * x, p0 * (x + 1)) } else { ((x - 1 - x0) + (x0 + 1) * p0, (x - x0) + (x0 + 1) * p0) }; + self.dec.update(fl, fh, ft); + itheta = x as i32; + } else if b0 > 1 || stereo { + itheta = self.dec.uint(qn as u32 + 1) as i32; + } else { + let half = (qn >> 1) as u32; + let ft = (half + 1) * (half + 1); + let fm = self.dec.decode(ft); + let (fl, fs); + if fm < (half * (half + 1)) >> 1 { + let it = (isqrt32(8 * fm + 1) - 1) >> 1; + fs = it + 1; + fl = it * (it + 1) >> 1; + itheta = it as i32; + } else { + let q = qn as u32; + let it = (2 * (q + 1) - isqrt32(8 * (ft - fm - 1) + 1)) >> 1; + fs = q + 1 - it; + fl = ft - ((q + 1 - it) * (q + 2 - it) >> 1); + itheta = it as i32; + } + self.dec.update(fl, fl + fs, ft); + } + itheta = ((itheta as u32 * 16384) / qn as u32) as i32; + } else if stereo { + if *b > 2 << BITRES && self.remaining_bits > 2 << BITRES { + inv = self.dec.bit_logp(2); + } + if self.disable_inv { + inv = false; + } + itheta = 0; + } + let qalloc = self.dec.tell_frac() as i32 - tell; + *b -= qalloc; + let (imid, iside, delta); + if itheta == 0 { + imid = 32767; + iside = 0; + *fill &= (1 << bb) - 1; + delta = -16384; + } else if itheta == 16384 { + imid = 0; + iside = 32767; + *fill &= ((1 << bb) - 1) << bb; + delta = 16384; + } else { + imid = bitexact_cos(itheta); + iside = bitexact_cos(16384 - itheta); + delta = frac_mul16((n - 1) << 7, bitexact_log2tan(iside, imid)); + } + Split { inv, imid, iside, delta, itheta, qalloc } + } + + fn quant_band_n1(&mut self, x: &mut [f32], y: Option<&mut [f32]>, lowband_out: Option<&mut [f32]>) -> u32 { + let dec_sign = |ctx: &mut Self| -> f32 { + let mut sign = 0; + if ctx.remaining_bits >= 1 << BITRES { + sign = ctx.dec.bits(1); + ctx.remaining_bits -= 1 << BITRES; + } + if sign != 0 { -1.0 } else { 1.0 } + }; + x[0] = dec_sign(self); + if let Some(y) = y { + y[0] = dec_sign(self); + } + if let Some(lo) = lowband_out { + lo[0] = x[0]; + } + 1 + } + + #[allow(clippy::too_many_arguments)] + fn quant_partition( + &mut self, + x: &mut [f32], + n: usize, + mut b: i32, + mut bb: usize, + lowband: Option<&[f32]>, + mut lm: i32, + gain: f32, + mut fill: u32, + ) -> u32 { + let b0 = bb; + let i = self.i; + let cache = self.m.cache(lm, i); + let cache_max = cache[cache[0] as usize] as i32; + if lm != -1 && b > cache_max + 12 && n > 2 { + let n = n >> 1; + let (xa, ya) = x.split_at_mut(n); + lm -= 1; + if bb == 1 { + fill = (fill & 1) | (fill << 1); + } + bb = (bb + 1) >> 1; + let sp = self.compute_theta(n as i32, &mut b, bb, b0, lm, false, &mut fill); + let mid = sp.imid as f32 / 32768.0; + let side = sp.iside as f32 / 32768.0; + let mut delta = sp.delta; + if b0 > 1 && (sp.itheta & 0x3fff) != 0 { + if sp.itheta > 8192 { + delta -= delta >> (4 - lm); + } else { + delta = 0.min(delta + ((n as i32) << BITRES >> (5 - lm))); + } + } + let mut mbits = 0.max(b.min((b - delta) / 2)); + let mut sbits = b - mbits; + self.remaining_bits -= sp.qalloc; + let next_lowband2 = lowband.map(|l| &l[n..]); + let mut rebalance = self.remaining_bits; + let mut cm; + if mbits >= sbits { + cm = self.quant_partition(xa, n, mbits, bb, lowband, lm, gain * mid, fill); + rebalance = mbits - (rebalance - self.remaining_bits); + if rebalance > 3 << BITRES && sp.itheta != 0 { + sbits += rebalance - (3 << BITRES); + } + cm |= self.quant_partition(ya, n, sbits, bb, next_lowband2, lm, gain * side, fill >> bb) << (b0 >> 1); + } else { + cm = self.quant_partition(ya, n, sbits, bb, next_lowband2, lm, gain * side, fill >> bb) << (b0 >> 1); + rebalance = sbits - (rebalance - self.remaining_bits); + if rebalance > 3 << BITRES && sp.itheta != 16384 { + mbits += rebalance - (3 << BITRES); + } + cm |= self.quant_partition(xa, n, mbits, bb, lowband, lm, gain * mid, fill); + } + return cm; + } + // No split. + let m = self.m; + let mut q = m.bits2pulses(i, lm, b); + let mut curr_bits = m.pulses2bits(i, lm, q); + self.remaining_bits -= curr_bits; + while self.remaining_bits < 0 && q > 0 { + self.remaining_bits += curr_bits; + q -= 1; + curr_bits = m.pulses2bits(i, lm, q); + self.remaining_bits -= curr_bits; + } + if q != 0 { + let k = get_pulses(q) as usize; + let mut iy = std::mem::take(&mut self.iy); + iy.clear(); + iy.resize(n, 0); + let ryy = decode_pulses(&mut iy, n, k, self.dec, &mut self.pvq_v); + let g = gain / ryy.sqrt(); + for (xv, &yv) in x[..n].iter_mut().zip(iy.iter()) { + *xv = g * yv as f32; + } + exp_rotation(&mut x[..n], n, bb, k, self.spread); + let cm = extract_collapse_mask(&iy, n, bb); + self.iy = iy; + cm + } else { + let cm_mask = (1u32 << bb) - 1; + fill &= cm_mask; + if fill == 0 { + x[..n].fill(0.0); + 0 + } else { + let cm = match lowband { + None => { + for v in x[..n].iter_mut() { + self.seed = lcg_rand(self.seed); + *v = ((self.seed as i32) >> 20) as f32; + } + cm_mask + } + Some(lb) => { + for j in 0..n { + self.seed = lcg_rand(self.seed); + let tmp = if self.seed & 0x8000 != 0 { 1.0 / 256.0 } else { -1.0 / 256.0 }; + x[j] = lb[j] + tmp; + } + fill + } + }; + renormalise_vector(&mut x[..n], gain); + cm + } + } + } + + #[allow(clippy::too_many_arguments)] + pub fn quant_band( + &mut self, + x: &mut [f32], + n: usize, + b: i32, + mut bb: usize, + mut lowband: Option<&mut [f32]>, + lm: i32, + lowband_out: Option<&mut [f32]>, + gain: f32, + mut fill: u32, + ) -> u32 { + let n0 = n; + let mut n_b = n; + let b0_orig = bb; + let mut time_divide = 0; + let mut recombine = 0; + let long_blocks = b0_orig == 1; + let mut tf_change = self.tf_change; + n_b /= bb; + if n == 1 { + return self.quant_band_n1(x, None, lowband_out); + } + if tf_change > 0 { + recombine = tf_change; + } + const BIT_INTERLEAVE: [u32; 16] = [0, 1, 1, 1, 2, 3, 3, 3, 2, 3, 3, 3, 2, 3, 3, 3]; + for k in 0..recombine { + if let Some(lb) = lowband.as_deref_mut() { + haar1(lb, n >> k, 1 << k); + } + fill = BIT_INTERLEAVE[(fill & 0xF) as usize] | (BIT_INTERLEAVE[(fill >> 4) as usize] << 2); + } + bb >>= recombine; + n_b <<= recombine; + while (n_b & 1) == 0 && tf_change < 0 { + if let Some(lb) = lowband.as_deref_mut() { + haar1(lb, n_b, bb); + } + fill |= fill << bb; + bb <<= 1; + n_b >>= 1; + time_divide += 1; + tf_change += 1; + } + let b0 = bb; + let n_b0 = n_b; + let mut tmp = std::mem::take(&mut self.tmp); + if b0 > 1 + && let Some(lb) = lowband.as_deref_mut() + { + deinterleave_hadamard(lb, n_b >> recombine, b0 << recombine, long_blocks, &mut tmp); + } + let mut cm = self.quant_partition(x, n, b, bb, lowband.as_deref(), lm, gain, fill); + if b0 > 1 { + interleave_hadamard(x, n_b >> recombine, b0 << recombine, long_blocks, &mut tmp); + } + self.tmp = tmp; + let mut n_b = n_b0; + bb = b0; + for _ in 0..time_divide { + bb >>= 1; + n_b <<= 1; + cm |= cm >> bb; + haar1(x, n_b, bb); + } + const BIT_DEINTERLEAVE: [u32; 16] = [0x00, 0x03, 0x0C, 0x0F, 0x30, 0x33, 0x3C, 0x3F, 0xC0, 0xC3, 0xCC, 0xCF, 0xF0, 0xF3, 0xFC, 0xFF]; + for k in 0..recombine { + cm = BIT_DEINTERLEAVE[(cm & 0xF) as usize]; + haar1(x, n0 >> k, 1 << k); + } + bb <<= recombine; + if let Some(lo) = lowband_out { + let s = (n0 as f32).sqrt(); + for j in 0..n0 { + lo[j] = s * x[j]; + } + } + cm & ((1u32 << bb) - 1) + } + + #[allow(clippy::too_many_arguments)] + pub fn quant_band_stereo( + &mut self, + x: &mut [f32], + y: &mut [f32], + n: usize, + mut b: i32, + bb: usize, + lowband: Option<&mut [f32]>, + lm: i32, + lowband_out: Option<&mut [f32]>, + mut fill: u32, + ) -> u32 { + let orig_fill = fill; + if n == 1 { + return self.quant_band_n1(x, Some(y), lowband_out); + } + let sp = self.compute_theta(n as i32, &mut b, bb, bb, lm, true, &mut fill); + let mid = sp.imid as f32 / 32768.0; + let side = sp.iside as f32 / 32768.0; + let mut cm; + if n == 2 { + let mut mbits = b; + let mut sbits = 0; + if sp.itheta != 0 && sp.itheta != 16384 { + sbits = 1 << BITRES; + } + mbits -= sbits; + let c = sp.itheta > 8192; + self.remaining_bits -= sp.qalloc + sbits; + let mut sign = 0; + if sbits != 0 { + sign = self.dec.bits(1) as i32; + } + let sign = (1 - 2 * sign) as f32; + if c { + cm = self.quant_band(y, n, mbits, bb, lowband, lm, lowband_out, 1.0, orig_fill); + x[0] = -sign * y[1]; + x[1] = sign * y[0]; + } else { + cm = self.quant_band(x, n, mbits, bb, lowband, lm, lowband_out, 1.0, orig_fill); + y[0] = -sign * x[1]; + y[1] = sign * x[0]; + } + x[0] *= mid; + x[1] *= mid; + y[0] *= side; + y[1] *= side; + let t = x[0]; + x[0] = t - y[0]; + y[0] += t; + let t = x[1]; + x[1] = t - y[1]; + y[1] += t; + } else { + let mut mbits = 0.max(b.min((b - sp.delta) / 2)); + let mut sbits = b - mbits; + self.remaining_bits -= sp.qalloc; + let mut rebalance = self.remaining_bits; + if mbits >= sbits { + cm = self.quant_band(x, n, mbits, bb, lowband, lm, lowband_out, 1.0, fill); + rebalance = mbits - (rebalance - self.remaining_bits); + if rebalance > 3 << BITRES && sp.itheta != 0 { + sbits += rebalance - (3 << BITRES); + } + cm |= self.quant_band(y, n, sbits, bb, None, lm, None, side, fill >> bb); + } else { + cm = self.quant_band(y, n, sbits, bb, None, lm, None, side, fill >> bb); + rebalance = sbits - (rebalance - self.remaining_bits); + if rebalance > 3 << BITRES && sp.itheta != 16384 { + mbits += rebalance - (3 << BITRES); + } + cm |= self.quant_band(x, n, mbits, bb, lowband, lm, lowband_out, 1.0, fill); + } + } + if n != 2 { + stereo_merge(&mut x[..n], &mut y[..n], mid); + } + if sp.inv { + for v in y[..n].iter_mut() { + *v = -*v; + } + } + cm + } +} + +fn stereo_merge(x: &mut [f32], y: &mut [f32], mid: f32) { + let mut xp = 0f32; + let mut side = 0f32; + for (a, b) in x.iter().zip(y.iter()) { + xp += a * b; + side += b * b; + } + xp *= mid; + let mid2 = mid; + let el = mid2 * mid2 + side - 2.0 * xp; + let er = mid2 * mid2 + side + 2.0 * xp; + if er < 6e-4 || el < 6e-4 { + y.copy_from_slice(x); + return; + } + let lgain = 1.0 / el.sqrt(); + let rgain = 1.0 / er.sqrt(); + for (a, b) in x.iter_mut().zip(y.iter_mut()) { + let l = mid * *a; + let r = *b; + *a = lgain * (l - r); + *b = rgain * (l + r); + } +} + +/// Decodes all band shapes (`quant_all_bands`, decoder side). +#[allow(clippy::too_many_arguments)] +pub fn quant_all_bands( + m: &Mode, + start: usize, + end: usize, + x_buf: &mut [f32], + y_buf: Option<&mut [f32]>, + collapse_masks: &mut [u32], + pulses: &[i32; NB_EBANDS], + short_blocks: bool, + spread: usize, + mut dual_stereo: bool, + intensity: usize, + tf_res: &[i32; NB_EBANDS], + total_bits: i32, + mut balance: i32, + dec: &mut RangeDecoder, + lm: usize, + coded_bands: usize, + seed: &mut u32, + disable_inv: bool, +) { + let mm = 1usize << lm; + let bb = if short_blocks { mm } else { 1 }; + let norm_offset = mm * EBANDS[start] as usize; + let norm_len = mm * EBANDS[NB_EBANDS - 1] as usize - norm_offset; + let c = if y_buf.is_some() { 2 } else { 1 }; + let mut norm = vec![0f32; norm_len]; + let mut norm2 = vec![0f32; if c == 2 { norm_len } else { 0 }]; + let mut scratch = vec![0f32; mm * (EBANDS[NB_EBANDS] - EBANDS[NB_EBANDS - 1]) as usize]; + let mut scratch2 = scratch.clone(); + let mut y_buf = y_buf; + let mut lowband_offset = 0usize; + let mut update_lowband = true; + let mut ctx = BandCtx { + m, + dec, + i: 0, + intensity, + spread, + tf_change: 0, + remaining_bits: 0, + seed: *seed, + disable_inv, + avoid_split_noise: bb > 1, + pvq_v: Vec::new(), + iy: Vec::new(), + tmp: Vec::new(), + }; + for i in start..end { + ctx.i = i; + let last = i == end - 1; + let xo = mm * EBANDS[i] as usize; + let n = mm * EBANDS[i + 1] as usize - xo; + let tell = ctx.dec.tell_frac() as i32; + if i != start { + balance -= tell; + } + let remaining_bits = total_bits - tell - 1; + ctx.remaining_bits = remaining_bits; + let b = if i < coded_bands { + let curr_balance = sdiv(balance, 3.min(coded_bands as i32 - i as i32)); + 0.max(16383.min((remaining_bits + 1).min(pulses[i] + curr_balance))) + } else { + 0 + }; + if (mm * EBANDS[i] as usize >= n + norm_offset || i == start + 1) && (update_lowband || lowband_offset == 0) { + lowband_offset = i; + } + if i == start + 1 { + let n1 = mm * (EBANDS[start + 1] - EBANDS[start]) as usize; + let n2 = mm * (EBANDS[start + 2] - EBANDS[start + 1]) as usize; + if n2 > n1 { + norm.copy_within(2 * n1 - n2..n1, n1); + if c == 2 { + norm2.copy_within(2 * n1 - n2..n1, n1); + } + } + } + let tf_change = tf_res[i]; + ctx.tf_change = tf_change; + let mut effective_lowband: Option = None; + let (mut x_cm, mut y_cm); + if lowband_offset != 0 && (spread != SPREAD_AGGRESSIVE || bb > 1 || tf_change < 0) { + let eff = (mm * EBANDS[lowband_offset] as usize).saturating_sub(norm_offset + n); + effective_lowband = Some(eff); + let mut fold_start = lowband_offset; + loop { + fold_start -= 1; + if mm * (EBANDS[fold_start] as usize) <= eff + norm_offset { + break; + } + } + let mut fold_end = lowband_offset - 1; + loop { + fold_end += 1; + if !(fold_end < i && mm * (EBANDS[fold_end] as usize) < eff + norm_offset + n) { + break; + } + } + x_cm = 0; + y_cm = 0; + let mut fi = fold_start; + loop { + x_cm |= collapse_masks[fi * c]; + y_cm |= collapse_masks[fi * c + c - 1]; + fi += 1; + if fi >= fold_end { + break; + } + } + } else { + x_cm = (1u32 << bb) - 1; + y_cm = x_cm; + } + if dual_stereo && i == intensity { + dual_stereo = false; + for j in 0..(mm * EBANDS[i] as usize - norm_offset) { + norm[j] = 0.5 * (norm[j] + norm2[j]); + } + } + let out_off = xo - norm_offset; + let x = &mut x_buf[xo..xo + n]; + if dual_stereo && let Some(y_buf) = y_buf.as_deref_mut() { + let y = &mut y_buf[xo..xo + n]; + let lb = effective_lowband.map(|e| { + scratch[..n].copy_from_slice(&norm[e..e + n]); + &mut scratch[..n] + }); + let lo = if last { None } else { Some(&mut norm[out_off..out_off + n]) }; + x_cm = ctx.quant_band(x, n, b / 2, bb, lb, lm as i32, lo, 1.0, x_cm); + let lb2 = effective_lowband.map(|e| { + scratch2[..n].copy_from_slice(&norm2[e..e + n]); + &mut scratch2[..n] + }); + let lo2 = if last { None } else { Some(&mut norm2[out_off..out_off + n]) }; + y_cm = ctx.quant_band(y, n, b / 2, bb, lb2, lm as i32, lo2, 1.0, y_cm); + } else { + let lb = effective_lowband.map(|e| { + scratch[..n].copy_from_slice(&norm[e..e + n]); + &mut scratch[..n] + }); + let lo = if last { None } else { Some(&mut norm[out_off..out_off + n]) }; + if let Some(yb) = y_buf.as_deref_mut() { + let y = &mut yb[xo..xo + n]; + x_cm = ctx.quant_band_stereo(x, y, n, b, bb, lb, lm as i32, lo, x_cm | y_cm); + } else { + x_cm = ctx.quant_band(x, n, b, bb, lb, lm as i32, lo, 1.0, x_cm | y_cm); + } + y_cm = x_cm; + } + collapse_masks[i * c] = x_cm; + collapse_masks[i * c + c - 1] = y_cm; + balance += pulses[i] + tell; + update_lowband = b > (n as i32) << BITRES; + ctx.avoid_split_noise = false; + } + *seed = ctx.seed; +} + +/// Anti-collapse noise injection (RFC 6716 §4.3.5). +#[allow(clippy::too_many_arguments)] +pub fn anti_collapse( + x_buf: &mut [f32], + collapse_masks: &[u32], + lm: usize, + c: usize, + size: usize, + start: usize, + end: usize, + log_e: &[f32], + prev1: &[f32], + prev2: &[f32], + pulses: &[i32; NB_EBANDS], + mut seed: u32, +) { + for i in start..end { + let n0 = (EBANDS[i + 1] - EBANDS[i]) as usize; + let depth = ((1 + pulses[i]) as u32 / n0 as u32) >> lm; + let thresh = 0.5 * (-0.125 * depth as f32).exp2(); + let sqrt_1 = 1.0 / ((n0 << lm) as f32).sqrt(); + for ch in 0..c { + let mut p1 = prev1[ch * NB_EBANDS + i]; + let mut p2 = prev2[ch * NB_EBANDS + i]; + if c == 1 { + p1 = p1.max(prev1[NB_EBANDS + i]); + p2 = p2.max(prev2[NB_EBANDS + i]); + } + let ediff = (log_e[ch * NB_EBANDS + i] - p1.min(p2)).max(0.0); + let mut r = 2.0 * (-ediff).exp2(); + if lm == 3 { + r *= std::f32::consts::SQRT_2; + } + r = r.min(thresh); + r *= sqrt_1; + let off = ch * size + ((EBANDS[i] as usize) << lm); + let xb = &mut x_buf[off..off + (n0 << lm)]; + let mut renorm = false; + for k in 0..(1usize << lm) { + if collapse_masks[i * c + ch] & (1 << k) == 0 { + for j in 0..n0 { + seed = lcg_rand(seed); + xb[(j << lm) + k] = if seed & 0x8000 != 0 { r } else { -r }; + } + renorm = true; + } + } + if renorm { + renormalise_vector(xb, 1.0); + } + } + } +} + +/// Multiplies the normalised shapes by the band energies (`denormalise_bands`). +#[allow(clippy::too_many_arguments)] +pub fn denormalise_bands( + x: &[f32], + freq: &mut [f32], + band_log_e: &[f32], + mut start: usize, + mut end: usize, + mm: usize, + downsample: usize, + silence: bool, +) { + let n = mm * SHORT_MDCT; + let mut bound = mm * EBANDS[end] as usize; + if downsample != 1 { + bound = bound.min(n / downsample); + } + if silence { + bound = 0; + start = 0; + end = 0; + } + let s0 = mm * EBANDS[start] as usize; + freq[..s0].fill(0.0); + for i in start..end { + let lo = mm * EBANDS[i] as usize; + let hi = mm * EBANDS[i + 1] as usize; + let lg = band_log_e[i] + E_MEANS[i]; + let g = lg.min(32.0).exp2(); + for j in lo..hi { + freq[j] = x[j] * g; + } + } + let from = bound; + if from < n { + freq[from..n].fill(0.0); + } +} + +#[cfg(test)] +mod tests { + use super::*; + + #[test] + fn bitexact_trig_endpoints() { + assert_eq!(bitexact_cos(0), 32768); + + let mid = bitexact_cos(8192); + assert!((mid - 23170).abs() < 20, "{mid}"); + assert_eq!(bitexact_log2tan(16384, 16384), 0); + } + + #[test] + fn isqrt_exact() { + for v in [0u32, 1, 2, 3, 4, 15, 16, 17, 99, 100, 1 << 20, u32::MAX] { + let r = isqrt32(v) as u64; + assert!(r * r <= v as u64 && (r + 1) * (r + 1) > v as u64); + } + } + + #[test] + fn pvq_roundtrip_all_codewords_small() { + use crate::range::enc::RangeEncoder; + // Enumerate every index for small N,K, decode, and check uniqueness + sum |y| == K. + for n in 1..6usize { + for k in 1..5usize { + let total = super::super::rate::pvq_v(n, k) as u32; + let mut seen = std::collections::HashSet::new(); + for idx in 0..total { + let mut e = RangeEncoder::new(16); + e.uint(idx, total); + let buf = e.done(); + let mut d = RangeDecoder::new(&buf); + let mut y = vec![0i32; n]; + let mut v = Vec::new(); + let ryy = decode_pulses(&mut y, n, k, &mut d, &mut v); + assert_eq!(y.iter().map(|v| v.unsigned_abs() as usize).sum::(), k); + assert_eq!(ryy as i32, y.iter().map(|v| v * v).sum::()); + assert!(seen.insert(y.clone()), "duplicate codeword n={n} k={k} idx={idx}"); + } + } + } + } + + #[test] + fn haar_is_involution() { + let mut x: Vec = (0..16).map(|i| i as f32).collect(); + let orig = x.clone(); + haar1(&mut x, 16, 1); + haar1(&mut x, 16, 1); + for (a, b) in x.iter().zip(&orig) { + assert!((a - b).abs() < 1e-4); + } + let mut tmp = Vec::new(); + for &h in &[false, true] { + let mut y = orig.clone(); + deinterleave_hadamard(&mut y, 4, 4, h, &mut tmp); + interleave_hadamard(&mut y, 4, 4, h, &mut tmp); + assert_eq!(y, orig); + } + } +} diff --git a/crates/opus/src/celt/energy.rs b/crates/opus/src/celt/energy.rs new file mode 100644 index 0000000..9260a5b --- /dev/null +++ b/crates/opus/src/celt/energy.rs @@ -0,0 +1,171 @@ +//! Band energy decoding: Laplace-coded coarse energy, fine energy and the final refinement +//! (RFC 6716 §4.3.2). + +use super::rate::MAX_FINE_BITS; +use super::tables::*; +use crate::range::RangeDecoder; + +const LAPLACE_MINP: u32 = 1; +const LAPLACE_NMIN: u32 = 16; + +fn laplace_get_freq1(fs0: u32, decay: u32) -> u32 { + let ft = 32768 - LAPLACE_MINP * (2 * LAPLACE_NMIN) - fs0; + (ft * (16384 - decay)) >> 15 +} + +/// Decodes a Laplace-distributed integer (`fs` = probability of 0 in Q15, `decay` in Q14). +pub fn laplace_decode(dec: &mut RangeDecoder, mut fs: u32, decay: u32) -> i32 { + let mut val = 0i32; + let fm = dec.decode_bin(15); + let mut fl = 0u32; + if fm >= fs { + val += 1; + fl = fs; + fs = laplace_get_freq1(fs, decay) + LAPLACE_MINP; + while fs > LAPLACE_MINP && fm >= fl + 2 * fs { + fs *= 2; + fl += fs; + fs = (((fs - 2 * LAPLACE_MINP) * decay) >> 15) + LAPLACE_MINP; + val += 1; + } + if fs <= LAPLACE_MINP { + let di = (fm - fl) >> 1; + val += di as i32; + fl += 2 * di * LAPLACE_MINP; + } + if fm < fl + fs { + val = -val; + } else { + fl += fs; + } + } + dec.update_bin(fl, (fl + fs).min(32768), 15); + val +} + +/// `unquant_coarse_energy`. +#[allow(clippy::too_many_arguments)] +pub fn unquant_coarse_energy(start: usize, end: usize, old_e: &mut [f32], intra: bool, dec: &mut RangeDecoder, c: usize, lm: usize) { + let prob = &E_PROB_MODEL[lm][intra as usize]; + let (coef, beta) = if intra { (0.0, BETA_INTRA) } else { (PRED_COEF[lm], BETA_COEF[lm]) }; + let budget = dec.storage() as i32 * 8; + let mut prev = [0f32; 2]; + for i in start..end { + for ch in 0..c { + let tell = dec.tell(); + let qi = if budget - tell >= 15 { + let pi = 2 * i.min(20); + laplace_decode(dec, (prob[pi] as u32) << 7, (prob[pi + 1] as u32) << 6) + } else if budget - tell >= 2 { + let q = dec.icdf(&SMALL_ENERGY_ICDF, 2) as i32; + (q >> 1) ^ -(q & 1) + } else if budget - tell >= 1 { + -(dec.bit_logp(1) as i32) + } else { + -1 + }; + let q = qi as f32; + let idx = i + ch * NB_EBANDS; + old_e[idx] = old_e[idx].max(-9.0); + let tmp = coef * old_e[idx] + prev[ch] + q; + old_e[idx] = tmp; + prev[ch] = prev[ch] + q - beta * q; + } + } +} + +pub fn unquant_fine_energy(start: usize, end: usize, old_e: &mut [f32], fine_quant: &[i32; NB_EBANDS], dec: &mut RangeDecoder, c: usize) { + for i in start..end { + let fq = fine_quant[i]; + if fq <= 0 { + continue; + } + for ch in 0..c { + let q2 = dec.bits(fq as u32); + let offset = (q2 as f32 + 0.5) * (1 << (14 - fq)) as f32 / 16384.0 - 0.5; + old_e[i + ch * NB_EBANDS] += offset; + } + } +} + +#[allow(clippy::too_many_arguments)] +pub fn unquant_energy_finalise( + start: usize, + end: usize, + old_e: &mut [f32], + fine_quant: &[i32; NB_EBANDS], + fine_priority: &[i32; NB_EBANDS], + mut bits_left: i32, + dec: &mut RangeDecoder, + c: usize, +) { + for prio in 0..2 { + let mut i = start; + while i < end && bits_left >= c as i32 { + if fine_quant[i] >= MAX_FINE_BITS || fine_priority[i] != prio { + i += 1; + continue; + } + for ch in 0..c { + let q2 = dec.bits(1); + let offset = (q2 as f32 - 0.5) * (1 << (14 - fine_quant[i] - 1)) as f32 / 16384.0; + old_e[i + ch * NB_EBANDS] += offset; + bits_left -= 1; + } + i += 1; + } + } +} + +#[cfg(test)] +mod tests { + use super::*; + use crate::range::enc::RangeEncoder; + + fn laplace_encode(e: &mut RangeEncoder, value: i32, mut fs: u32, decay: u32) { + let mut fl = 0u32; + let mut val = value; + if val != 0 { + let s: i32 = if val < 0 { -1 } else { 0 }; + val = val.abs(); + fl = fs; + fs = laplace_get_freq1(fs, decay); + let mut i = 1; + while fs > 0 && i < val { + fs *= 2; + fl += fs + 2 * LAPLACE_MINP; + fs = (fs * decay) >> 15; + i += 1; + } + if fs == 0 { + let ndi_max = (32768 - fl + LAPLACE_MINP - 1) as i32; + let ndi_max = (ndi_max - s) >> 1; + let di = (val - i).min(ndi_max - 1); + fl = (fl as i32 + (2 * di + 1 + s) * LAPLACE_MINP as i32) as u32; + fs = LAPLACE_MINP.min(32768 - fl); + } else { + fs += LAPLACE_MINP; + if s == 0 { + fl += fs; + } + } + } + e.encode(fl, fl + fs, 32768); + } + + #[test] + fn laplace_roundtrip() { + for &(fs, decay) in &[(72u32 << 7, 127u32 << 6), (24 << 7, 179 << 6), (177 << 7, 11 << 6)] { + let vals: Vec = (-12..=12).collect(); + let mut e = RangeEncoder::new(400); + for &v in &vals { + laplace_encode(&mut e, v, fs, decay); + } + let buf = e.done(); + let mut d = RangeDecoder::new(&buf); + for &v in &vals { + assert_eq!(laplace_decode(&mut d, fs, decay), v, "fs={fs} v={v}"); + } + } + } +} diff --git a/crates/opus/src/celt/mdct.rs b/crates/opus/src/celt/mdct.rs new file mode 100644 index 0000000..13dc4a3 --- /dev/null +++ b/crates/opus/src/celt/mdct.rs @@ -0,0 +1,205 @@ +//! Inverse MDCT with the low-overlap window and in-place TDAC (RFC 6716 §4.3.7). +//! +//! The IMDCT is computed through an N/4-point complex FFT (pre-rotation, FFT, post-rotation), +//! producing the N/2 "folded" time samples; the window overlap region is then unfolded against the +//! folded tail of the previous block, which the caller keeps in place in the output buffer. + +#[derive(Clone, Copy, Default, Debug)] +pub struct Cpx { + pub re: f32, + pub im: f32, +} + +impl Cpx { + #[inline] + fn mul(self, o: Cpx) -> Cpx { + Cpx { re: self.re * o.re - self.im * o.im, im: self.re * o.im + self.im * o.re } + } + #[inline] + fn add(self, o: Cpx) -> Cpx { + Cpx { re: self.re + o.re, im: self.im + o.im } + } +} + +/// Mixed-radix (2, 3, 4, 5) forward complex FFT, unscaled. +pub struct Fft { + n: usize, + factors: Vec, + twiddle: Vec, +} + +impl Fft { + pub fn new(n: usize) -> Fft { + let mut factors = Vec::new(); + let mut m = n; + for p in [4usize, 2, 3, 5] { + while m.is_multiple_of(p) { + factors.push(p); + m /= p; + } + } + assert_eq!(m, 1, "unsupported FFT size {n}"); + let twiddle = (0..n) + .map(|k| { + let ph = -2.0 * std::f64::consts::PI * k as f64 / n as f64; + Cpx { re: ph.cos() as f32, im: ph.sin() as f32 } + }) + .collect(); + Fft { n, factors, twiddle } + } + + pub fn process(&self, data: &mut [Cpx], scratch: &mut Vec) { + scratch.clear(); + scratch.extend_from_slice(&data[..self.n]); + self.rec(&mut data[..self.n], scratch, 1, self.n, 0, 1); + } + + fn rec(&self, out: &mut [Cpx], inp: &[Cpx], in_stride: usize, n: usize, fi: usize, tw_stride: usize) { + let p = self.factors[fi]; + let m = n / p; + if m == 1 { + for (i, o) in out.iter_mut().enumerate().take(p) { + *o = inp[i * in_stride]; + } + } else { + for q in 0..p { + self.rec(&mut out[q * m..(q + 1) * m], &inp[q * in_stride..], in_stride * p, m, fi + 1, tw_stride * p); + } + } + let big = self.n; + let mut t = [Cpx::default(); 5]; + for k in 0..m { + for q in 0..p { + let w = self.twiddle[(q * k * tw_stride) % big]; + t[q] = out[q * m + k].mul(w); + } + for q2 in 0..p { + let mut acc = Cpx::default(); + for (q, tq) in t.iter().enumerate().take(p) { + let w = self.twiddle[((q * q2) % p) * (big / p)]; + acc = acc.add(tq.mul(w)); + } + out[q2 * m + k] = acc; + } + } + } +} + +pub struct Imdct { + /// Full MDCT length (2x the number of coefficients). + n: usize, + trig: Vec, + fft: Fft, +} + +impl Imdct { + pub fn new(n: usize) -> Imdct { + let trig = (0..n / 2).map(|i| (2.0 * std::f64::consts::PI * (i as f64 + 0.125) / n as f64).cos() as f32).collect(); + Imdct { n, trig, fft: Fft::new(n / 4) } + } + + /// Inverse transform of `n/2` coefficients read from `input` at `stride`. + /// + /// Writes the folded block to `out[overlap/2 .. overlap/2 + n/2]` and unfolds the first + /// `overlap` samples against the previous block's folded tail already stored in + /// `out[0 .. overlap/2]`. + pub fn backward(&self, input: &[f32], stride: usize, out: &mut [f32], window: &[f32], overlap: usize, work: &mut ImdctScratch) { + let n2 = self.n >> 1; + let n4 = self.n >> 2; + let t = &self.trig; + let z = &mut work.z; + z.clear(); + for i in 0..n4 { + let x1 = input[2 * i * stride]; + let x2 = input[stride * (n2 - 1 - 2 * i)]; + let yr = x2 * t[i] + x1 * t[n4 + i]; + let yi = x1 * t[i] - x2 * t[n4 + i]; + z.push(Cpx { re: yi, im: yr }); + } + self.fft.process(z, &mut work.scratch); + let base = overlap >> 1; + let y = &mut out[base..base + n2]; + for i in 0..n4.div_ceil(2) { + let front = z[i]; + let back = z[n4 - 1 - i]; + let (re, im) = (front.im, front.re); + let (t0, t1) = (t[i], t[n4 + i]); + let yr0 = re * t0 + im * t1; + let yi0 = re * t1 - im * t0; + let (re, im) = (back.im, back.re); + let (t0, t1) = (t[n4 - i - 1], t[n2 - i - 1]); + let yr1 = re * t0 + im * t1; + let yi1 = re * t1 - im * t0; + y[2 * i] = yr0; + y[n2 - 1 - 2 * i] = yi0; + y[n2 - 2 - 2 * i] = yr1; + y[2 * i + 1] = yi1; + } + // Mirror on both sides for TDAC. + for i in 0..overlap / 2 { + let x1 = out[overlap - 1 - i]; + let x2 = out[i]; + let w1 = window[i]; + let w2 = window[overlap - 1 - i]; + out[i] = x2 * w2 - x1 * w1; + out[overlap - 1 - i] = x2 * w1 + x1 * w2; + } + } +} + +#[derive(Default)] +pub struct ImdctScratch { + z: Vec, + scratch: Vec, +} + +#[cfg(test)] +mod tests { + use super::*; + + #[test] + fn fft_matches_dft() { + for &n in &[60usize, 120, 240, 480] { + let fft = Fft::new(n); + let x: Vec = (0..n).map(|i| Cpx { re: ((i * 7) % 13) as f32 - 6.0, im: ((i * 3) % 5) as f32 - 2.0 }).collect(); + let mut y = x.clone(); + fft.process(&mut y, &mut Vec::new()); + for k in [0, 1, 7, n / 2, n - 1] { + let mut acc = (0f64, 0f64); + for (i, v) in x.iter().enumerate() { + let ph = -2.0 * std::f64::consts::PI * (i * k) as f64 / n as f64; + acc.0 += v.re as f64 * ph.cos() - v.im as f64 * ph.sin(); + acc.1 += v.re as f64 * ph.sin() + v.im as f64 * ph.cos(); + } + assert!((acc.0 - y[k].re as f64).abs() < 1e-2 && (acc.1 - y[k].im as f64).abs() < 1e-2, "n={n} k={k}"); + } + } + } + + /// The folded output must equal the direct IMDCT formula folded by TDAC symmetry. + #[test] + fn imdct_matches_direct_formula() { + let n = 240; + let n2 = n / 2; + let im = Imdct::new(n); + let x: Vec = (0..n2).map(|k| ((k * 37 % 11) as f32 - 5.0) / 5.0).collect(); + let overlap = 0; + let mut out = vec![0f32; n2]; + im.backward(&x, 1, &mut out, &[], overlap, &mut ImdctScratch::default()); + // Direct IMDCT y[t] = sum X[k] cos(2pi/N (t + 1/2 + N/4)(k + 1/2)), t in 0..N. + let y: Vec = (0..n) + .map(|t| { + (0..n2) + .map(|k| x[k] as f64 * (2.0 * std::f64::consts::PI / n as f64 * (t as f64 + 0.5 + n as f64 / 4.0) * (k as f64 + 0.5)).cos()) + .sum() + }) + .collect(); + // The folded half: out[i] relates to y in the middle half; check proportionality. + let mid: Vec = (0..n2).map(|i| y[n / 4 + i]).collect(); + let dot: f64 = mid.iter().zip(&out).map(|(a, b)| a * *b as f64).sum(); + let ea: f64 = mid.iter().map(|a| a * a).sum(); + let eb: f64 = out.iter().map(|b| (*b as f64).powi(2)).sum(); + let corr = dot.abs() / (ea.sqrt() * eb.sqrt()); + assert!(corr > 0.999, "corr {corr}"); + } +} diff --git a/crates/opus/src/celt/mod.rs b/crates/opus/src/celt/mod.rs new file mode 100644 index 0000000..7110f5f --- /dev/null +++ b/crates/opus/src/celt/mod.rs @@ -0,0 +1,651 @@ +//! CELT layer decoder (RFC 6716 §4.3). + +pub(crate) mod bands; +pub(crate) mod energy; +pub(crate) mod mdct; +pub(crate) mod rate; +pub(crate) mod tables; + +use std::sync::OnceLock; + +use bands::{anti_collapse, denormalise_bands, quant_all_bands, renormalise_vector}; +use energy::{unquant_coarse_energy, unquant_energy_finalise, unquant_fine_energy}; +use mdct::ImdctScratch; +use rate::{Mode, compute_allocation}; +use tables::*; + +use crate::range::{BITRES, RangeDecoder}; + +const DECODE_BUFFER_SIZE: usize = 2048; +const COMBFILTER_MINPERIOD: i32 = 15; +const PLC_PITCH_LAG_MAX: usize = 720; +const PLC_PITCH_LAG_MIN: usize = 100; + +pub(crate) fn mode() -> &'static Mode { + static MODE: OnceLock = OnceLock::new(); + MODE.get_or_init(Mode::new) +} + +/// CELT decoder state for one (mono or stereo) stream. +pub struct CeltDecoder { + m: &'static Mode, + /// Output channels. + channels: usize, + /// Channels coded in the stream. + pub stream_channels: usize, + downsample: usize, + pub start: usize, + pub end: usize, + pub disable_inv: bool, + pub rng: u32, + loss_count: u32, + skip_plc: bool, + postfilter_period: i32, + postfilter_period_old: i32, + postfilter_gain: f32, + postfilter_gain_old: f32, + postfilter_tapset: usize, + postfilter_tapset_old: usize, + preemph_mem: [f32; 2], + decode_mem: Vec, + old_band_e: [f32; 2 * NB_EBANDS], + old_log_e: [f32; 2 * NB_EBANDS], + old_log_e2: [f32; 2 * NB_EBANDS], + background_log_e: [f32; 2 * NB_EBANDS], + // scratch + x: Vec, + freq: Vec, + imdct: ImdctScratch, + pcm_scratch: Vec, +} + +impl CeltDecoder { + /// `sample_rate` is the output rate (48000, 24000, 16000, 12000 or 8000). + pub fn new(sample_rate: u32, channels: usize) -> CeltDecoder { + let downsample = (48000 / sample_rate) as usize; + let mut d = CeltDecoder { + m: mode(), + channels, + stream_channels: channels, + downsample, + start: 0, + end: NB_EBANDS, + disable_inv: channels == 1, + rng: 0, + loss_count: 0, + skip_plc: true, + postfilter_period: 0, + postfilter_period_old: 0, + postfilter_gain: 0.0, + postfilter_gain_old: 0.0, + postfilter_tapset: 0, + postfilter_tapset_old: 0, + preemph_mem: [0.0; 2], + decode_mem: vec![0.0; channels * (DECODE_BUFFER_SIZE + OVERLAP)], + old_band_e: [0.0; 2 * NB_EBANDS], + old_log_e: [0.0; 2 * NB_EBANDS], + old_log_e2: [0.0; 2 * NB_EBANDS], + background_log_e: [0.0; 2 * NB_EBANDS], + x: Vec::new(), + freq: Vec::new(), + imdct: ImdctScratch::default(), + pcm_scratch: Vec::new(), + }; + d.reset(); + d + } + + /// `OPUS_RESET_STATE`. + pub fn reset(&mut self) { + self.rng = 0; + self.loss_count = 0; + self.skip_plc = true; + self.postfilter_period = 0; + self.postfilter_period_old = 0; + self.postfilter_gain = 0.0; + self.postfilter_gain_old = 0.0; + self.postfilter_tapset = 0; + self.postfilter_tapset_old = 0; + self.preemph_mem = [0.0; 2]; + self.decode_mem.fill(0.0); + self.old_band_e = [0.0; 2 * NB_EBANDS]; + self.old_log_e = [-28.0; 2 * NB_EBANDS]; + self.old_log_e2 = [-28.0; 2 * NB_EBANDS]; + self.background_log_e = [0.0; 2 * NB_EBANDS]; + } + + fn mem_len(&self) -> usize { + DECODE_BUFFER_SIZE + OVERLAP + } + + /// Decodes one CELT frame of `frame_size` samples (at the output rate) into interleaved `pcm` + /// (`channels` wide). With `accum`, the output is added to `pcm`. `data = None` (or fewer than + /// two bytes) runs the packet-loss concealment. + pub fn decode( + &mut self, + data: Option<&[u8]>, + frame_size: usize, + pcm: &mut [f32], + accum: bool, + dec: Option<&mut RangeDecoder>, + ) -> Result { + let n_full = frame_size * self.downsample; + let lm = match n_full { + 120 => 0, + 240 => 1, + 480 => 2, + 960 => 3, + _ => return Err(crate::Error::InvalidArgument("CELT frame size")), + }; + let data = match data { + Some(d) if d.len() > 1 => d, + _ => { + self.decode_lost(n_full, lm); + self.deemphasis(n_full, pcm, accum); + return Ok(frame_size); + } + }; + if data.len() > 1275 { + return Err(crate::Error::InvalidPacket("CELT frame too long")); + } + match dec { + Some(d) => self.decode_frame(d, data.len(), n_full, lm), + None => { + let mut own = RangeDecoder::new(data); + self.decode_frame(&mut own, data.len(), n_full, lm) + } + } + self.deemphasis(n_full, pcm, accum); + Ok(frame_size) + } + + fn decode_frame(&mut self, dec: &mut RangeDecoder, len: usize, n: usize, lm: usize) { + let cc = self.channels; + let c = self.stream_channels; + let m = self.m; + let mm = 1usize << lm; + let start = self.start; + let end = self.end; + let eff_end = end.min(NB_EBANDS); + if self.loss_count == 0 { + self.skip_plc = false; + } + if c == 1 { + for i in 0..NB_EBANDS { + self.old_band_e[i] = self.old_band_e[i].max(self.old_band_e[NB_EBANDS + i]); + } + } + let total_bits_raw = len as i32 * 8; + let mut tell = dec.tell(); + let silence = if tell >= total_bits_raw { + true + } else if tell == 1 { + dec.bit_logp(15) + } else { + false + }; + if silence { + // Pretend we've read all the remaining bits. + dec.skip_to_end(total_bits_raw); + tell = total_bits_raw; + } + let mut postfilter_gain = 0.0f32; + let mut postfilter_pitch = 0i32; + let mut postfilter_tapset = 0usize; + if start == 0 && tell + 16 <= total_bits_raw { + if dec.bit_logp(1) { + let octave = dec.uint(6); + postfilter_pitch = ((16 << octave) + dec.bits(4 + octave)) as i32 - 1; + let qg = dec.bits(3); + if dec.tell() + 2 <= total_bits_raw { + postfilter_tapset = dec.icdf(&TAPSET_ICDF, 2); + } + postfilter_gain = 0.09375 * (qg + 1) as f32; + } + tell = dec.tell(); + } + let is_transient = if lm > 0 && tell + 3 <= total_bits_raw { + let t = dec.bit_logp(3); + tell = dec.tell(); + t + } else { + false + }; + let short_blocks = is_transient; + let intra = if tell + 3 <= total_bits_raw { dec.bit_logp(3) } else { false }; + unquant_coarse_energy(start, end, &mut self.old_band_e, intra, dec, c, lm); + let mut tf_res = [0i32; NB_EBANDS]; + tf_decode(start, end, is_transient, &mut tf_res, lm, dec); + tell = dec.tell(); + let mut spread = 2usize; + if tell + 4 <= total_bits_raw { + spread = dec.icdf(&SPREAD_ICDF, 5); + } + let cap = m.init_caps(lm, c); + let mut offsets = [0i32; NB_EBANDS]; + let mut dynalloc_logp = 6; + let mut total_bits = total_bits_raw << BITRES; + let mut tellf = dec.tell_frac() as i32; + for i in start..end { + let width = (c as i32 * (EBANDS[i + 1] - EBANDS[i])) << lm; + let quanta = (width << BITRES).min((6 << BITRES).max(width)); + let mut loop_logp = dynalloc_logp; + let mut boost = 0; + while tellf + (loop_logp << BITRES) < total_bits && boost < cap[i] { + let flag = dec.bit_logp(loop_logp as u32); + tellf = dec.tell_frac() as i32; + if !flag { + break; + } + boost += quanta; + total_bits -= quanta; + loop_logp = 1; + } + offsets[i] = boost; + if boost > 0 { + dynalloc_logp = 2.max(dynalloc_logp - 1); + } + } + let alloc_trim = if tellf + (6 << BITRES) <= total_bits { dec.icdf(&TRIM_ICDF, 7) as i32 } else { 5 }; + let mut bits = ((len as i32 * 8) << BITRES) - dec.tell_frac() as i32 - 1; + let anti_collapse_rsv = if is_transient && lm >= 2 && bits >= ((lm as i32 + 2) << BITRES) { 1 << BITRES } else { 0 }; + bits -= anti_collapse_rsv; + let alloc = compute_allocation(m, start, end, &offsets, &cap, alloc_trim, bits, c, lm, dec); + unquant_fine_energy(start, end, &mut self.old_band_e, &alloc.fine_quant, dec, c); + + // Shift the decode memory by one frame. + let ml = self.mem_len(); + for ch in 0..cc { + let mem = &mut self.decode_mem[ch * ml..(ch + 1) * ml]; + mem.copy_within(n..DECODE_BUFFER_SIZE + OVERLAP / 2, 0); + } + + let mut collapse_masks = vec![0u32; c * NB_EBANDS]; + let mut x = std::mem::take(&mut self.x); + x.clear(); + x.resize(c * n, 0.0); + { + let (xa, xb) = x.split_at_mut(n); + let yb = if c == 2 { Some(xb) } else { None }; + let mut seed = self.rng; + quant_all_bands( + m, + start, + end, + xa, + yb, + &mut collapse_masks, + &alloc.pulses, + short_blocks, + spread, + alloc.dual_stereo, + alloc.intensity, + &tf_res, + ((len as i32) * (8 << BITRES)) - anti_collapse_rsv, + alloc.balance, + dec, + lm, + alloc.coded_bands, + &mut seed, + self.disable_inv, + ); + self.rng = seed; + } + let mut anti_collapse_on = false; + if anti_collapse_rsv > 0 { + anti_collapse_on = dec.bits(1) != 0; + } + let bits_left = len as i32 * 8 - dec.tell(); + unquant_energy_finalise(start, end, &mut self.old_band_e, &alloc.fine_quant, &alloc.fine_priority, bits_left, dec, c); + if anti_collapse_on { + anti_collapse( + &mut x, + &collapse_masks, + lm, + c, + n, + start, + end, + &self.old_band_e, + &self.old_log_e, + &self.old_log_e2, + &alloc.pulses, + self.rng, + ); + } + if silence { + for v in self.old_band_e[..c * NB_EBANDS].iter_mut() { + *v = -28.0; + } + } + self.synthesis(&x, n, start, eff_end, c, is_transient, lm, silence); + self.x = x; + + // Post-filter. + for ch in 0..cc { + self.postfilter_period = self.postfilter_period.max(COMBFILTER_MINPERIOD); + self.postfilter_period_old = self.postfilter_period_old.max(COMBFILTER_MINPERIOD); + let mem = &mut self.decode_mem[ch * ml..(ch + 1) * ml]; + let off = DECODE_BUFFER_SIZE - n; + comb_filter( + mem, + off, + self.postfilter_period_old, + self.postfilter_period, + SHORT_MDCT, + self.postfilter_gain_old, + self.postfilter_gain, + self.postfilter_tapset_old, + self.postfilter_tapset, + &m.window, + ); + if lm != 0 { + comb_filter( + mem, + off + SHORT_MDCT, + self.postfilter_period, + postfilter_pitch, + n - SHORT_MDCT, + self.postfilter_gain, + postfilter_gain, + self.postfilter_tapset, + postfilter_tapset, + &m.window, + ); + } + } + self.postfilter_period_old = self.postfilter_period; + self.postfilter_gain_old = self.postfilter_gain; + self.postfilter_tapset_old = self.postfilter_tapset; + self.postfilter_period = postfilter_pitch; + self.postfilter_gain = postfilter_gain; + self.postfilter_tapset = postfilter_tapset; + if lm != 0 { + self.postfilter_period_old = self.postfilter_period; + self.postfilter_gain_old = self.postfilter_gain; + self.postfilter_tapset_old = self.postfilter_tapset; + } + + if c == 1 { + let (a, b) = self.old_band_e.split_at_mut(NB_EBANDS); + b.copy_from_slice(a); + } + if !is_transient { + self.old_log_e2 = self.old_log_e; + self.old_log_e = self.old_band_e; + for i in 0..2 * NB_EBANDS { + self.background_log_e[i] = (self.background_log_e[i] + mm as f32 * 0.001).min(self.old_band_e[i]); + } + } else { + for i in 0..2 * NB_EBANDS { + self.old_log_e[i] = self.old_log_e[i].min(self.old_band_e[i]); + } + } + for ch in 0..2 { + for i in (0..start).chain(end..NB_EBANDS) { + self.old_band_e[ch * NB_EBANDS + i] = 0.0; + self.old_log_e[ch * NB_EBANDS + i] = -28.0; + self.old_log_e2[ch * NB_EBANDS + i] = -28.0; + } + } + self.rng = dec.rng(); + self.loss_count = 0; + } + + /// Denormalisation + IMDCT into the decode memory (`celt_synthesis`). + #[allow(clippy::too_many_arguments)] + fn synthesis(&mut self, x: &[f32], n: usize, start: usize, eff_end: usize, c: usize, is_transient: bool, lm: usize, silence: bool) { + let cc = self.channels; + let m = self.m; + let mm = 1usize << lm; + let (bcount, nb, shift) = if is_transient { (mm, SHORT_MDCT, MAX_LM) } else { (1, SHORT_MDCT << lm, MAX_LM - lm) }; + let ml = self.mem_len(); + let mut freq = std::mem::take(&mut self.freq); + freq.clear(); + freq.resize(2 * n, 0.0); + let (f0, f1) = freq.split_at_mut(n); + let out_off = DECODE_BUFFER_SIZE - n; + if cc == 2 && c == 1 { + denormalise_bands(x, f0, &self.old_band_e, start, eff_end, mm, self.downsample, silence); + f1.copy_from_slice(f0); + for (ch, f) in [(0usize, &*f1), (1usize, &*f0)] { + let mem = &mut self.decode_mem[ch * ml..(ch + 1) * ml]; + for b in 0..bcount { + m.mdct[shift].backward(&f[b..], bcount, &mut mem[out_off + nb * b..], &m.window, OVERLAP, &mut self.imdct); + } + } + } else if cc == 1 && c == 2 { + denormalise_bands(x, f0, &self.old_band_e, start, eff_end, mm, self.downsample, silence); + denormalise_bands(&x[n..], f1, &self.old_band_e[NB_EBANDS..], start, eff_end, mm, self.downsample, silence); + for i in 0..n { + f0[i] = 0.5 * (f0[i] + f1[i]); + } + let mem = &mut self.decode_mem[..ml]; + for b in 0..bcount { + m.mdct[shift].backward(&f0[b..], bcount, &mut mem[out_off + nb * b..], &m.window, OVERLAP, &mut self.imdct); + } + } else { + for ch in 0..cc { + denormalise_bands(&x[ch * n..], f0, &self.old_band_e[ch * NB_EBANDS..], start, eff_end, mm, self.downsample, silence); + let mem = &mut self.decode_mem[ch * ml..(ch + 1) * ml]; + for b in 0..bcount { + m.mdct[shift].backward(&f0[b..], bcount, &mut mem[out_off + nb * b..], &m.window, OVERLAP, &mut self.imdct); + } + } + } + self.freq = freq; + } + + fn deemphasis(&mut self, n: usize, pcm: &mut [f32], accum: bool) { + let cc = self.channels; + let ml = self.mem_len(); + let ds = self.downsample; + let out_off = DECODE_BUFFER_SIZE - n; + let mut scratch = std::mem::take(&mut self.pcm_scratch); + for ch in 0..cc { + let mem = &self.decode_mem[ch * ml + out_off..ch * ml + out_off + n]; + let mut mstate = self.preemph_mem[ch]; + scratch.clear(); + for &v in mem { + let tmp = v + 1e-30 + mstate; + mstate = PREEMPH * tmp; + scratch.push(tmp); + } + self.preemph_mem[ch] = mstate; + for j in 0..n / ds { + let v = scratch[j * ds] * (1.0 / 32768.0); + let o = &mut pcm[j * cc + ch]; + if accum { + *o += v; + } else { + *o = v; + } + } + } + self.pcm_scratch = scratch; + } + + /// Packet-loss concealment: pitch-periodic extension for the first lost frames, then + /// decaying shaped noise. + fn decode_lost(&mut self, n: usize, lm: usize) { + let cc = self.channels; + let ml = self.mem_len(); + let m = self.m; + let pitch_based = self.loss_count < 5 && self.start == 0 && !self.skip_plc; + if !pitch_based { + let decay = if self.loss_count == 0 { 1.5 } else { 0.5 }; + for ch in 0..cc { + for i in self.start..self.end { + let k = ch * NB_EBANDS + i; + self.old_band_e[k] = self.background_log_e[k].max(self.old_band_e[k] - decay); + } + } + let mut seed = self.rng; + let mut x = std::mem::take(&mut self.x); + x.clear(); + x.resize(cc * n, 0.0); + let eff_end = self.end.min(NB_EBANDS); + for ch in 0..cc { + for i in self.start..eff_end { + let off = n * ch + ((EBANDS[i] as usize) << lm); + let blen = ((EBANDS[i + 1] - EBANDS[i]) as usize) << lm; + for v in x[off..off + blen].iter_mut() { + seed = bands::lcg_rand(seed); + *v = ((seed as i32) >> 20) as f32; + } + renormalise_vector(&mut x[off..off + blen], 1.0); + } + } + self.rng = seed; + for ch in 0..cc { + let mem = &mut self.decode_mem[ch * ml..(ch + 1) * ml]; + mem.copy_within(n..DECODE_BUFFER_SIZE + OVERLAP / 2, 0); + } + let saved_sc = self.stream_channels; + self.stream_channels = cc; + self.synthesis(&x, n, self.start, eff_end, cc, false, lm, false); + self.stream_channels = saved_sc; + self.x = x; + } else { + // Pitch search on the (mono-mixed) history. + let hist_len = 1024usize; + let base = DECODE_BUFFER_SIZE - hist_len; + let mut mono = vec![0f32; hist_len]; + for ch in 0..cc { + for (i, v) in mono.iter_mut().enumerate() { + *v += self.decode_mem[ch * ml + base + i]; + } + } + let period = if self.loss_count == 0 { find_pitch(&mono) } else { self.postfilter_period_old.max(PLC_PITCH_LAG_MIN as i32) as usize }; + let period = period.clamp(PLC_PITCH_LAG_MIN, PLC_PITCH_LAG_MAX); + // Remember the period for subsequent losses in the (otherwise unused) old period slot. + self.postfilter_period_old = period as i32; + let fade = 0.8f32.powi(self.loss_count as i32 + 1); + let mut ext = vec![0f32; n + OVERLAP]; + for ch in 0..cc { + let mem = &mut self.decode_mem[ch * ml..(ch + 1) * ml]; + // Energy decay across the last two periods. + let e1: f32 = mem[DECODE_BUFFER_SIZE - period..DECODE_BUFFER_SIZE].iter().map(|v| v * v).sum(); + let e0: f32 = mem[DECODE_BUFFER_SIZE - 2 * period..DECODE_BUFFER_SIZE - period].iter().map(|v| v * v).sum(); + let decay = if e0 > 0.0 { (e1 / e0).sqrt().min(1.0) } else { 0.0 }; + let mut g = 1.0f32; + for (i, e) in ext.iter_mut().enumerate() { + if i > 0 && i % period == 0 { + g *= decay; + } + let src = DECODE_BUFFER_SIZE - period + (i % period); + *e = mem[src] * g * fade; + } + mem.copy_within(n..DECODE_BUFFER_SIZE + OVERLAP / 2, 0); + let out_off = DECODE_BUFFER_SIZE - n; + // Cross-fade the start with the previous overlap so the transition is smooth. + mem[out_off..out_off + n].copy_from_slice(&ext[..n]); + let tail = &ext[n..n + OVERLAP]; + for i in 0..OVERLAP / 2 { + mem[DECODE_BUFFER_SIZE + i] = m.window[i] * tail[OVERLAP - 1 - i] + m.window[OVERLAP - 1 - i] * tail[i]; + } + } + } + self.loss_count += 1; + } +} + +fn find_pitch(x: &[f32]) -> usize { + let n = x.len(); + let win = 512.min(n - PLC_PITCH_LAG_MAX); + let tgt = &x[n - win..]; + let mut best = PLC_PITCH_LAG_MIN; + let mut best_score = f32::MIN; + let e_t: f32 = tgt.iter().map(|v| v * v).sum::() + 1e-9; + for lag in PLC_PITCH_LAG_MIN..=PLC_PITCH_LAG_MAX { + let src = &x[n - win - lag..n - lag]; + let mut xy = 0f32; + let mut yy = 1e-9f32; + for (a, b) in tgt.iter().zip(src) { + xy += a * b; + yy += b * b; + } + let score = xy / (e_t * yy).sqrt(); + if score > best_score { + best_score = score; + best = lag; + } + } + best +} + +fn tf_decode(start: usize, end: usize, is_transient: bool, tf_res: &mut [i32; NB_EBANDS], lm: usize, dec: &mut RangeDecoder) { + let mut budget = dec.storage() as i32 * 8; + let mut tell = dec.tell(); + let mut logp: u32 = if is_transient { 2 } else { 4 }; + let tf_select_rsv = lm > 0 && tell + logp as i32 + 1 <= budget; + budget -= tf_select_rsv as i32; + let mut tf_changed = 0; + let mut curr = 0; + for r in tf_res.iter_mut().take(end).skip(start) { + if tell + logp as i32 <= budget { + curr ^= dec.bit_logp(logp) as i32; + tell = dec.tell(); + tf_changed |= curr; + } + *r = curr; + logp = if is_transient { 4 } else { 5 }; + } + let mut tf_select = 0; + let ti = 4 * is_transient as usize; + if tf_select_rsv && TF_SELECT_TABLE[lm][ti + tf_changed as usize] != TF_SELECT_TABLE[lm][ti + 2 + tf_changed as usize] { + tf_select = dec.bit_logp(1) as usize; + } + for r in tf_res.iter_mut().take(end).skip(start) { + *r = TF_SELECT_TABLE[lm][ti + 2 * tf_select + *r as usize] as i32; + } +} + +/// Pitch post-filter (in place on `buf[off .. off + n]`, reading history before `off`). +#[allow(clippy::too_many_arguments)] +fn comb_filter(buf: &mut [f32], off: usize, t0: i32, t1: i32, n: usize, g0: f32, g1: f32, tapset0: usize, tapset1: usize, window: &[f32; OVERLAP]) { + if g0 == 0.0 && g1 == 0.0 { + return; + } + let t0 = t0.max(COMBFILTER_MINPERIOD) as usize; + let t1 = t1.max(COMBFILTER_MINPERIOD) as usize; + let g00 = g0 * COMB_GAINS[tapset0][0]; + let g01 = g0 * COMB_GAINS[tapset0][1]; + let g02 = g0 * COMB_GAINS[tapset0][2]; + let g10 = g1 * COMB_GAINS[tapset1][0]; + let g11 = g1 * COMB_GAINS[tapset1][1]; + let g12 = g1 * COMB_GAINS[tapset1][2]; + let mut x1 = buf[off + 1 - t1]; + let mut x2 = buf[off - t1]; + let mut x3 = buf[off - t1 - 1]; + let mut x4 = buf[off - t1 - 2]; + let overlap = if g0 == g1 && t0 == t1 && tapset0 == tapset1 { 0 } else { OVERLAP.min(n) }; + let mut i = 0; + while i < overlap { + let p = off + i; + let x0 = buf[p + 2 - t1]; + let f = window[i] * window[i]; + let a = 1.0 - f; + buf[p] = buf[p] + + a * g00 * buf[p - t0] + + a * g01 * (buf[p + 1 - t0] + buf[p - t0 - 1]) + + a * g02 * (buf[p + 2 - t0] + buf[p - t0 - 2]) + + f * g10 * x2 + + f * g11 * (x1 + x3) + + f * g12 * (x0 + x4); + x4 = x3; + x3 = x2; + x2 = x1; + x1 = x0; + i += 1; + } + if g1 == 0.0 { + return; + } + // Constant filter for the rest of the block. + let t = t1; + for i in overlap..n { + let p = off + i; + buf[p] = buf[p] + g10 * buf[p - t] + g11 * (buf[p + 1 - t] + buf[p - t - 1]) + g12 * (buf[p + 2 - t] + buf[p - t - 2]); + } +} diff --git a/crates/opus/src/celt/rate.rs b/crates/opus/src/celt/rate.rs new file mode 100644 index 0000000..f5103fa --- /dev/null +++ b/crates/opus/src/celt/rate.rs @@ -0,0 +1,584 @@ +//! Pulse cache and bit allocation (RFC 6716 §4.3.3, §4.3.4.1). + +use super::tables::*; +use crate::range::{BITRES, RangeDecoder}; + +const MAX_PSEUDO: i32 = 40; +const LOG_MAX_PSEUDO: i32 = 6; +pub const MAX_FINE_BITS: i32 = 8; +const FINE_OFFSET: i32 = 21; +pub const QTHETA_OFFSET: i32 = 4; +pub const QTHETA_OFFSET_TWOPHASE: i32 = 16; +const ALLOC_STEPS: i32 = 6; + +/// Number of pulses represented by pseudo-pulse index `i`. +pub fn get_pulses(i: i32) -> i32 { + if i < 8 { i } else { (8 + (i & 7)) << ((i >> 3) - 1) } +} + +/// Conservative (rounded up) log2 of `val` with `frac` fractional bits. +pub fn log2_frac(mut val: u32, mut frac: u32) -> i32 { + let mut l = crate::range::ilog(val); + if val & val.wrapping_sub(1) != 0 { + if l > 16 { + val = ((val - 1) >> (l - 16)) + 1; + } else { + val <<= 16 - l; + } + let mut l = (l - 1) << frac; + loop { + let b = (val >> 16) as i32; + l += b << frac; + val = (val + b as u32) >> b; + val = (val * val + 0x7FFF) >> 15; + if frac == 0 { + break; + } + frac -= 1; + } + l + (val > 0x8000) as i32 + } else { + l -= 1; + l << frac + } +} + +/// PVQ codebook size V(N, K) as u128 (saturating far above 2^32). +pub fn pvq_v(n: usize, k: usize) -> u128 { + // V(N,K) = V(N-1,K) + V(N,K-1) + V(N-1,K-1); computed row by row. + if k == 0 { + return 1; + } + if n == 0 { + return 0; + } + let mut row = vec![0u128; k + 1]; // V(0, k) + row[0] = 1; + for _ in 0..n { + let mut prev_diag = row[0]; // V(n-1, 0) + // V(n, 0) = 1 + for kk in 1..=k { + let up = row[kk]; // V(n-1, kk) + let v = up.saturating_add(row[kk - 1]).saturating_add(prev_diag); + prev_diag = up; + row[kk] = v.min(u128::MAX >> 8); + } + row[0] = 1; + } + row[k] +} + +fn fits_in32(n: usize, k: i32) -> bool { + pvq_v(n, k as usize) <= u32::MAX as u128 +} + +/// Precomputed per-mode data shared by all CELT decoders. +pub struct Mode { + /// log2 of band width in 1/8 bits. + pub log_n: [i32; NB_EBANDS], + /// Index into `cache_bits` for `(LM+1)*NB_EBANDS + band` (-1 when unused). + pub cache_index: Vec, + /// `cache[0]` = max pseudo-pulses; `cache[k]` = bits(k) - 1 in 1/8 bits. + pub cache_bits: Vec, + /// Per-band maximum allocation: `[(2*LM + C-1)*NB_EBANDS + band]`. + pub caps: Vec, + pub window: [f32; OVERLAP], + pub mdct: [super::mdct::Imdct; MAX_LM + 1], +} + +impl Mode { + pub fn new() -> Mode { + let mut log_n = [0i32; NB_EBANDS]; + for (i, l) in log_n.iter_mut().enumerate() { + *l = log2_frac((EBANDS[i + 1] - EBANDS[i]) as u32, BITRES); + } + let (cache_index, cache_bits) = compute_pulse_cache(MAX_LM as i32); + let mut m = Mode { + log_n, + cache_index, + cache_bits, + caps: Vec::new(), + window: [0.0; OVERLAP], + mdct: [super::mdct::Imdct::new(1920), super::mdct::Imdct::new(960), super::mdct::Imdct::new(480), super::mdct::Imdct::new(240)], + }; + m.caps = compute_caps(&m); + for i in 0..OVERLAP { + let x = std::f64::consts::FRAC_PI_2 * (i as f64 + 0.5) / OVERLAP as f64; + let s = x.sin(); + m.window[i] = (std::f64::consts::FRAC_PI_2 * s * s).sin() as f32; + } + m + } + + #[inline] + pub fn cache(&self, lm: i32, band: usize) -> &[u8] { + let idx = self.cache_index[((lm + 1) as usize) * NB_EBANDS + band]; + &self.cache_bits[idx.max(0) as usize..] + } + + pub fn bits2pulses(&self, band: usize, lm: i32, bits: i32) -> i32 { + let cache = self.cache(lm, band); + let mut lo = 0i32; + let mut hi = cache[0] as i32; + let bits = bits - 1; + for _ in 0..LOG_MAX_PSEUDO { + let mid = (lo + hi + 1) >> 1; + if cache[mid as usize] as i32 >= bits { + hi = mid; + } else { + lo = mid; + } + } + let lo_bits = if lo == 0 { -1 } else { cache[lo as usize] as i32 }; + if bits - lo_bits <= cache[hi as usize] as i32 - bits { lo } else { hi } + } + + pub fn pulses2bits(&self, band: usize, lm: i32, pulses: i32) -> i32 { + if pulses == 0 { 0 } else { self.cache(lm, band)[pulses as usize] as i32 + 1 } + } + + /// Per-band allocation caps for this frame (`init_caps`). + pub fn init_caps(&self, lm: usize, c: usize) -> [i32; NB_EBANDS] { + let mut cap = [0i32; NB_EBANDS]; + for (i, cp) in cap.iter_mut().enumerate() { + let n = (EBANDS[i + 1] - EBANDS[i]) << lm; + *cp = (self.caps[NB_EBANDS * (2 * lm + c - 1) + i] as i32 + 64) * c as i32 * n >> 2; + } + cap + } +} + +fn compute_pulse_cache(lm_max: i32) -> (Vec, Vec) { + let nb = NB_EBANDS; + let rows = (lm_max + 2) as usize; + let mut index = vec![-1i16; nb * rows]; + let mut entries: Vec<(usize, i32, usize)> = Vec::new(); // (N, K, offset) + let mut curr = 0usize; + for i in 0..rows { + for j in 0..nb { + let n = (((EBANDS[j + 1] - EBANDS[j]) << i) >> 1) as usize; + index[i * nb + j] = -1; + // Reuse the entry of an earlier band with the same size. + 'search: for k in 0..=i { + for nn in 0..nb { + if k == i && nn >= j { + break; + } + if n == (((EBANDS[nn + 1] - EBANDS[nn]) << k) >> 1) as usize { + index[i * nb + j] = index[k * nb + nn]; + break 'search; + } + } + } + if index[i * nb + j] == -1 && n != 0 { + let mut k = 0; + while k < MAX_PSEUDO && fits_in32(n, get_pulses(k + 1)) { + k += 1; + } + index[i * nb + j] = curr as i16; + entries.push((n, k, curr)); + curr += (k + 1) as usize; + } + } + } + let mut bits = vec![0u8; curr]; + for &(n, k, off) in &entries { + let maxk = get_pulses(k) as usize; + let tmp = required_bits(n, maxk, BITRES); + for j in 1..=k { + bits[off + j as usize] = (tmp[get_pulses(j) as usize] - 1) as u8; + } + bits[off] = k as u8; + } + (index, bits) +} + +/// Bits (1/2^frac units, rounded up) needed for each K in `0..=maxk` for dimension `n`. +fn required_bits(n: usize, maxk: usize, frac: u32) -> Vec { + let mut out = vec![0i32; maxk + 1]; + if n == 1 { + for o in out.iter_mut().skip(1) { + *o = 1 << frac; + } + } else { + for (k, o) in out.iter_mut().enumerate().skip(1) { + let v = pvq_v(n, k); + *o = log2_frac(v as u32, frac); + } + } + out +} + +fn compute_caps(m: &Mode) -> Vec { + let nb = NB_EBANDS; + let mut caps = vec![0u8; (MAX_LM + 1) * 2 * nb]; + for i in 0..=MAX_LM as i32 { + for c in 1..=2i32 { + for j in 0..nb { + let mut n0 = EBANDS[j + 1] - EBANDS[j]; + let max_bits = if n0 << i == 1 { + c * (1 + MAX_FINE_BITS) << BITRES + } else { + let mut lm0 = 0; + if n0 > 2 { + n0 >>= 1; + lm0 -= 1; + } else if n0 <= 1 { + lm0 = i.min(1); + n0 <<= lm0; + } + let pcache = m.cache(lm0, j); + let mut mb = pcache[pcache[0] as usize] as i32 + 1; + let mut n = n0; + for k in 0..(i - lm0) { + mb <<= 1; + let offset = ((m.log_n[j] + ((lm0 + k) << BITRES)) >> 1) - QTHETA_OFFSET; + let num = 459 * ((2 * n - 1) * offset + mb); + let den = ((2 * n - 1) << 9) - 459; + let qb = ((num + (den >> 1)) / den).min(57); + mb += qb; + n <<= 1; + } + if c == 2 { + mb <<= 1; + let offset = ((m.log_n[j] + (i << BITRES)) >> 1) - if n == 2 { QTHETA_OFFSET_TWOPHASE } else { QTHETA_OFFSET }; + let ndof = 2 * n - 1 - (n == 2) as i32; + let p = if n == 2 { 512 } else { 487 }; + let num = p * (mb + ndof * offset); + let den = (ndof << 9) - p; + let qb = ((num + (den >> 1)) / den).min(if n == 2 { 64 } else { 61 }); + mb += qb; + } + let ndof = c * n + if c == 2 && n > 2 { 1 } else { 0 }; + let mut offset = ((m.log_n[j] + (i << BITRES)) >> 1) - FINE_OFFSET; + if n == 2 { + offset += (1 << BITRES) >> 2; + } + let num = mb + ndof * offset; + let den = (ndof - 1) << BITRES; + let qb = ((num + (den >> 1)) / den).min(MAX_FINE_BITS); + mb += c * qb << BITRES; + mb + }; + let w = c * ((EBANDS[j + 1] - EBANDS[j]) << i); + let v = (4 * max_bits / w) - 64; + caps[((i * 2 + c - 1) as usize) * nb + j] = v.clamp(0, 255) as u8; + } + } + } + caps +} + +/// Result of the allocation process. +pub struct Allocation { + pub coded_bands: usize, + pub intensity: usize, + pub dual_stereo: bool, + pub balance: i32, + pub pulses: [i32; NB_EBANDS], + pub fine_quant: [i32; NB_EBANDS], + pub fine_priority: [i32; NB_EBANDS], +} + +/// `clt_compute_allocation` (decoder side). +#[allow(clippy::too_many_arguments)] +pub fn compute_allocation( + m: &Mode, + start: usize, + end: usize, + offsets: &[i32; NB_EBANDS], + cap: &[i32; NB_EBANDS], + alloc_trim: i32, + total: i32, + c: usize, + lm: usize, + dec: &mut RangeDecoder, +) -> Allocation { + let ci = c as i32; + let lmi = lm as i32; + let mut total = total.max(0); + let mut skip_start = start; + let skip_rsv = if total >= 1 << BITRES { 1 << BITRES } else { 0 }; + total -= skip_rsv; + let mut intensity_rsv = 0; + let mut dual_stereo_rsv = 0; + if c == 2 { + intensity_rsv = LOG2_FRAC_TABLE[end - start]; + if intensity_rsv > total { + intensity_rsv = 0; + } else { + total -= intensity_rsv; + dual_stereo_rsv = if total >= 1 << BITRES { 1 << BITRES } else { 0 }; + total -= dual_stereo_rsv; + } + } + let mut bits1 = [0i32; NB_EBANDS]; + let mut bits2 = [0i32; NB_EBANDS]; + let mut thresh = [0i32; NB_EBANDS]; + let mut trim_offset = [0i32; NB_EBANDS]; + for j in start..end { + let w = EBANDS[j + 1] - EBANDS[j]; + thresh[j] = (ci << BITRES).max((3 * w << lmi << BITRES) >> 4); + trim_offset[j] = ci * w * (alloc_trim - 5 - lmi) * (end as i32 - j as i32 - 1) * (1 << (lmi + BITRES as i32)) >> 6; + if w << lmi == 1 { + trim_offset[j] -= ci << BITRES; + } + } + let nb_alloc = ALLOC.len() as i32; + let mut lo = 1; + let mut hi = nb_alloc - 1; + loop { + let mut done = false; + let mut psum = 0; + let mid = (lo + hi) >> 1; + for j in (start..end).rev() { + let n = EBANDS[j + 1] - EBANDS[j]; + let mut bitsj = ci * n * (ALLOC[mid as usize][j] as i32) << lmi >> 2; + if bitsj > 0 { + bitsj = (bitsj + trim_offset[j]).max(0); + } + bitsj += offsets[j]; + if bitsj >= thresh[j] || done { + done = true; + psum += bitsj.min(cap[j]); + } else if bitsj >= ci << BITRES { + psum += ci << BITRES; + } + } + if psum > total { + hi = mid - 1; + } else { + lo = mid + 1; + } + if lo > hi { + break; + } + } + hi = lo; + lo -= 1; + for j in start..end { + let n = EBANDS[j + 1] - EBANDS[j]; + let mut bits1j = ci * n * (ALLOC[lo as usize][j] as i32) << lmi >> 2; + let mut bits2j = if hi >= nb_alloc { cap[j] } else { ci * n * (ALLOC[hi as usize][j] as i32) << lmi >> 2 }; + if bits1j > 0 { + bits1j = (bits1j + trim_offset[j]).max(0); + } + if bits2j > 0 { + bits2j = (bits2j + trim_offset[j]).max(0); + } + if lo > 0 { + bits1j += offsets[j]; + } + bits2j += offsets[j]; + if offsets[j] > 0 { + skip_start = j; + } + bits2j = (bits2j - bits1j).max(0); + bits1[j] = bits1j; + bits2[j] = bits2j; + } + interp_bits2pulses(m, start, end, skip_start, &bits1, &bits2, &thresh, cap, total, skip_rsv, intensity_rsv, dual_stereo_rsv, c, lm, dec) +} + +#[allow(clippy::too_many_arguments)] +fn interp_bits2pulses( + m: &Mode, + start: usize, + end: usize, + skip_start: usize, + bits1: &[i32; NB_EBANDS], + bits2: &[i32; NB_EBANDS], + thresh: &[i32; NB_EBANDS], + cap: &[i32; NB_EBANDS], + mut total: i32, + skip_rsv: i32, + mut intensity_rsv: i32, + mut dual_stereo_rsv: i32, + c: usize, + lm: usize, + dec: &mut RangeDecoder, +) -> Allocation { + let ci = c as i32; + let alloc_floor = ci << BITRES; + let stereo = (c > 1) as i32; + let log_m = (lm as i32) << BITRES; + let mut lo = 0; + let mut hi = 1 << ALLOC_STEPS; + for _ in 0..ALLOC_STEPS { + let mid = (lo + hi) >> 1; + let mut psum = 0; + let mut done = false; + for j in (start..end).rev() { + let tmp = bits1[j] + (mid * bits2[j] >> ALLOC_STEPS); + if tmp >= thresh[j] || done { + done = true; + psum += tmp.min(cap[j]); + } else if tmp >= alloc_floor { + psum += alloc_floor; + } + } + if psum > total { + hi = mid; + } else { + lo = mid; + } + } + let mut bits = [0i32; NB_EBANDS]; + let mut psum = 0; + let mut done = false; + for j in (start..end).rev() { + let mut tmp = bits1[j] + (lo * bits2[j] >> ALLOC_STEPS); + if tmp < thresh[j] && !done { + tmp = if tmp >= alloc_floor { alloc_floor } else { 0 }; + } else { + done = true; + } + tmp = tmp.min(cap[j]); + bits[j] = tmp; + psum += tmp; + } + let s = start; + let mut coded_bands = end; + loop { + let j = coded_bands - 1; + if j <= skip_start { + total += skip_rsv; + break; + } + let mut left = total - psum; + let span = EBANDS[coded_bands] - EBANDS[s]; + let percoeff = left / span; + left -= span * percoeff; + let rem = (left - (EBANDS[j] - EBANDS[s])).max(0); + let band_width = EBANDS[coded_bands] - EBANDS[j]; + let mut band_bits = bits[j] + percoeff * band_width + rem; + if band_bits >= thresh[j].max(alloc_floor + (1 << BITRES)) { + if dec.bit_logp(1) { + break; + } + psum += 1 << BITRES; + band_bits -= 1 << BITRES; + } + psum -= bits[j] + intensity_rsv; + if intensity_rsv > 0 { + intensity_rsv = LOG2_FRAC_TABLE[j - s]; + } + psum += intensity_rsv; + if band_bits >= alloc_floor { + psum += alloc_floor; + bits[j] = alloc_floor; + } else { + bits[j] = 0; + } + coded_bands -= 1; + } + let intensity = if intensity_rsv > 0 { s + dec.uint((coded_bands + 1 - s) as u32) as usize } else { 0 }; + if intensity <= s { + total += dual_stereo_rsv; + dual_stereo_rsv = 0; + } + let dual_stereo = if dual_stereo_rsv > 0 { dec.bit_logp(1) } else { false }; + let mut left = total - psum; + let span = EBANDS[coded_bands] - EBANDS[s]; + let percoeff = left / span; + left -= span * percoeff; + for j in s..coded_bands { + bits[j] += percoeff * (EBANDS[j + 1] - EBANDS[j]); + } + for j in s..coded_bands { + let tmp = left.min(EBANDS[j + 1] - EBANDS[j]); + bits[j] += tmp; + left -= tmp; + } + let mut ebits = [0i32; NB_EBANDS]; + let mut fine_priority = [0i32; NB_EBANDS]; + let mut balance = 0; + for j in s..coded_bands { + let n0 = EBANDS[j + 1] - EBANDS[j]; + let n = n0 << lm; + let bit = bits[j] + balance; + let mut excess; + if n > 1 { + excess = (bit - cap[j]).max(0); + bits[j] = bit - excess; + let den = ci * n + if c == 2 && n > 2 && !dual_stereo && j < intensity { 1 } else { 0 }; + let nclogn = den * (m.log_n[j] + log_m); + let mut offset = (nclogn >> 1) - den * FINE_OFFSET; + if n == 2 { + offset += (den << BITRES) >> 2; + } + if bits[j] + offset < den * 2 << BITRES { + offset += nclogn >> 2; + } else if bits[j] + offset < den * 3 << BITRES { + offset += nclogn >> 3; + } + let mut eb = (bits[j] + offset + (den << (BITRES - 1))).max(0); + eb = (eb / den) >> BITRES; + if ci * eb > (bits[j] >> BITRES) { + eb = bits[j] >> stereo >> BITRES; + } + eb = eb.min(MAX_FINE_BITS); + ebits[j] = eb; + fine_priority[j] = (eb * (den << BITRES) >= bits[j] + offset) as i32; + bits[j] -= ci * eb << BITRES; + } else { + excess = (bit - (ci << BITRES)).max(0); + bits[j] = bit - excess; + ebits[j] = 0; + fine_priority[j] = 1; + } + if excess > 0 { + let extra_fine = (excess >> (stereo + BITRES as i32)).min(MAX_FINE_BITS - ebits[j]); + ebits[j] += extra_fine; + let extra_bits = extra_fine * ci << BITRES; + fine_priority[j] = (extra_bits >= excess - balance) as i32; + excess -= extra_bits; + } + balance = excess; + } + for j in coded_bands..end { + ebits[j] = bits[j] >> stereo >> BITRES; + bits[j] = 0; + fine_priority[j] = (ebits[j] < 1) as i32; + } + Allocation { coded_bands, intensity, dual_stereo, balance, pulses: bits, fine_quant: ebits, fine_priority } +} + +#[cfg(test)] +mod tests { + use super::*; + + #[test] + fn log2_frac_values() { + assert_eq!(log2_frac(1, 3), 0); + assert_eq!(log2_frac(2, 3), 8); + assert_eq!(log2_frac(4, 3), 16); + assert_eq!(log2_frac(6, 3), 21); + assert_eq!(log2_frac(18, 3), 34); + assert_eq!(log2_frac(22, 3), 36); + } + + #[test] + fn pvq_counts() { + // V(N,1) = 2N, V(1,K) = 2 (K>0), V(2,K) = 4K. + assert_eq!(pvq_v(5, 1), 10); + assert_eq!(pvq_v(1, 7), 2); + assert_eq!(pvq_v(2, 9), 36); + assert_eq!(pvq_v(3, 2), 18); + assert_eq!(pvq_v(0, 0), 1); + assert_eq!(pvq_v(0, 3), 0); + } + + #[test] + fn caps_match_reference_shape() { + let m = Mode::new(); + // 1-bin bands at LM=0 (mono and stereo) are capped at 224. + assert!(m.caps[..8].iter().all(|&c| c == 224)); + assert!(m.caps[21..29].iter().all(|&c| c == 224)); + let first_rows: [u8; 42] = [ + 224, 224, 224, 224, 224, 224, 224, 224, 160, 160, 160, 160, 185, 185, 185, 178, 178, 168, 134, 61, 37, 224, 224, 224, 224, 224, 224, 224, + 224, 240, 240, 240, 240, 207, 207, 207, 198, 198, 183, 144, 66, 40, + ]; + assert_eq!(&m.caps[..42], &first_rows[..]); + } +} diff --git a/crates/opus/src/celt/tables.rs b/crates/opus/src/celt/tables.rs new file mode 100644 index 0000000..f2381f7 --- /dev/null +++ b/crates/opus/src/celt/tables.rs @@ -0,0 +1,130 @@ +//! Constant tables of the 48 kHz CELT mode (RFC 6716 §4.3). + +/// Number of energy bands. +pub const NB_EBANDS: usize = 21; +/// Band edges in units of 2.5 ms MDCT bins (Table 55). +pub const EBANDS: [i32; NB_EBANDS + 1] = [0, 1, 2, 3, 4, 5, 6, 7, 8, 10, 12, 14, 16, 20, 24, 28, 34, 40, 48, 60, 78, 100]; +/// Overlap of the low-overlap window (samples at 48 kHz). +pub const OVERLAP: usize = 120; +/// Size of the shortest MDCT (2.5 ms). +pub const SHORT_MDCT: usize = 120; +/// Largest LM (20 ms frames). +pub const MAX_LM: usize = 3; +/// Pre-emphasis coefficient. +pub const PREEMPH: f32 = 0.850_006_1; + +/// Static allocation table (Table 57), indexed `[quality][band]`, in 1/32 bit per MDCT bin. +pub const ALLOC: [[u8; NB_EBANDS]; 11] = transpose([ + [0, 90, 110, 118, 126, 134, 144, 152, 162, 172, 200], + [0, 80, 100, 110, 119, 127, 137, 145, 155, 165, 200], + [0, 75, 90, 103, 112, 120, 130, 138, 148, 158, 200], + [0, 69, 84, 93, 104, 114, 124, 132, 142, 152, 200], + [0, 63, 78, 86, 95, 103, 113, 123, 133, 143, 200], + [0, 56, 71, 80, 89, 97, 107, 117, 127, 137, 200], + [0, 49, 65, 75, 83, 91, 101, 111, 121, 131, 200], + [0, 40, 58, 70, 78, 85, 95, 105, 115, 125, 200], + [0, 34, 51, 65, 72, 78, 88, 98, 108, 118, 198], + [0, 29, 45, 59, 66, 72, 82, 92, 102, 112, 193], + [0, 20, 39, 53, 60, 66, 76, 86, 96, 106, 188], + [0, 18, 32, 47, 54, 60, 70, 80, 90, 100, 183], + [0, 10, 26, 40, 47, 54, 64, 74, 84, 94, 178], + [0, 0, 20, 31, 39, 47, 57, 67, 77, 87, 173], + [0, 0, 12, 23, 32, 41, 51, 61, 71, 81, 168], + [0, 0, 0, 15, 25, 35, 45, 55, 65, 75, 163], + [0, 0, 0, 4, 17, 29, 39, 49, 59, 69, 158], + [0, 0, 0, 0, 12, 23, 33, 43, 53, 63, 153], + [0, 0, 0, 0, 1, 16, 26, 36, 46, 56, 148], + [0, 0, 0, 0, 0, 10, 15, 20, 30, 45, 129], + [0, 0, 0, 0, 0, 1, 1, 1, 1, 20, 104], +]); + +const fn transpose(t: [[u8; 11]; NB_EBANDS]) -> [[u8; NB_EBANDS]; 11] { + let mut out = [[0u8; NB_EBANDS]; 11]; + let mut b = 0; + while b < NB_EBANDS { + let mut q = 0; + while q < 11 { + out[q][b] = t[b][q]; + q += 1; + } + b += 1; + } + out +} + +/// Mean band energy (log2 domain) removed before coarse quantisation. +pub const E_MEANS: [f32; 25] = [ + 6.4375, 6.25, 5.75, 5.3125, 5.0625, 4.8125, 4.5, 4.375, 4.875, 4.6875, 4.5625, 4.4375, 4.875, 4.625, 4.3125, 4.5, 4.375, 4.625, 4.75, 4.4375, + 3.75, 3.75, 3.75, 3.75, 3.75, +]; + +/// Inter-frame prediction coefficient (alpha) per LM, Q15. +pub const PRED_COEF: [f32; 4] = [29440.0 / 32768.0, 26112.0 / 32768.0, 21248.0 / 32768.0, 16384.0 / 32768.0]; +/// Inter-band prediction coefficient (beta) per LM for inter frames, Q15. +pub const BETA_COEF: [f32; 4] = [30147.0 / 32768.0, 22282.0 / 32768.0, 12124.0 / 32768.0, 6554.0 / 32768.0]; +/// Beta for intra frames. +pub const BETA_INTRA: f32 = 4915.0 / 32768.0; + +/// Laplace model parameters for coarse energy: `[LM][intra][2*band + {0: prob, 1: decay}]`. +pub const E_PROB_MODEL: [[[u8; 42]; 2]; 4] = [ + [ + [ + 72, 127, 65, 129, 66, 128, 65, 128, 64, 128, 62, 128, 64, 128, 64, 128, 92, 78, 92, 79, 92, 78, 90, 79, 116, 41, 115, 40, 114, 40, 132, + 26, 132, 26, 145, 17, 161, 12, 176, 10, 177, 11, + ], + [ + 24, 179, 48, 138, 54, 135, 54, 132, 53, 134, 56, 133, 55, 132, 55, 132, 61, 114, 70, 96, 74, 88, 75, 88, 87, 74, 89, 66, 91, 67, 100, 59, + 108, 50, 120, 40, 122, 37, 97, 43, 78, 50, + ], + ], + [ + [ + 83, 78, 84, 81, 88, 75, 86, 74, 87, 71, 90, 73, 93, 74, 93, 74, 109, 40, 114, 36, 117, 34, 117, 34, 143, 17, 145, 18, 146, 19, 162, 12, + 165, 10, 178, 7, 189, 6, 190, 8, 177, 9, + ], + [ + 23, 178, 54, 115, 63, 102, 66, 98, 69, 99, 74, 89, 71, 91, 73, 91, 78, 89, 86, 80, 92, 66, 93, 64, 102, 59, 103, 60, 104, 60, 117, 52, + 123, 44, 138, 35, 133, 31, 97, 38, 77, 45, + ], + ], + [ + [ + 61, 90, 93, 60, 105, 42, 107, 41, 110, 45, 116, 38, 113, 38, 112, 38, 124, 26, 132, 27, 136, 19, 140, 20, 155, 14, 159, 16, 158, 18, 170, + 13, 177, 10, 187, 8, 192, 6, 175, 9, 159, 10, + ], + [ + 21, 178, 59, 110, 71, 86, 75, 85, 84, 83, 91, 66, 88, 73, 87, 72, 92, 75, 98, 72, 105, 58, 107, 54, 115, 52, 114, 55, 112, 56, 129, 51, + 132, 40, 150, 33, 140, 29, 98, 35, 77, 42, + ], + ], + [ + [ + 42, 121, 96, 66, 108, 43, 111, 40, 117, 44, 123, 32, 120, 36, 119, 33, 127, 33, 134, 34, 139, 21, 147, 23, 152, 20, 158, 25, 154, 26, + 166, 21, 173, 16, 184, 13, 184, 10, 150, 13, 139, 15, + ], + [ + 22, 178, 63, 114, 74, 82, 84, 83, 92, 82, 103, 62, 96, 72, 96, 67, 101, 73, 107, 72, 113, 55, 118, 52, 125, 52, 118, 52, 117, 55, 135, + 49, 137, 39, 157, 32, 145, 29, 97, 33, 77, 40, + ], + ], +]; + +/// Inverse CDF for coarse energy when only a few bits remain. +pub const SMALL_ENERGY_ICDF: [u8; 3] = [2, 1, 0]; +/// Conservative log2 in 1/8 bits of the number of coded bands (intensity reservation). +pub const LOG2_FRAC_TABLE: [i32; 24] = [0, 8, 13, 16, 19, 21, 23, 24, 26, 27, 28, 29, 30, 31, 32, 32, 33, 34, 34, 35, 36, 36, 37, 37]; +/// TF resolution changes: `[LM][4*transient + 2*tf_select + tf_flag]`. +pub const TF_SELECT_TABLE: [[i8; 8]; 4] = + [[0, -1, 0, -1, 0, -1, 0, -1], [0, -1, 0, -2, 1, 0, 1, -1], [0, -2, 0, -3, 2, 0, 1, -1], [0, -2, 0, -3, 3, 0, 1, -1]]; +/// Spreading decision `{7, 2, 21, 2}/32`. +pub const SPREAD_ICDF: [u8; 4] = [25, 23, 2, 0]; +/// Allocation trim (Table 58). +pub const TRIM_ICDF: [u8; 11] = [126, 124, 119, 109, 87, 41, 19, 9, 4, 2, 0]; +/// Post-filter tapset `{2, 1, 1}/4`. +pub const TAPSET_ICDF: [u8; 3] = [2, 1, 0]; +/// Post-filter tap gains per tapset. +pub const COMB_GAINS: [[f32; 3]; 3] = + [[0.306_640_62, 0.217_041_02, 0.129_638_67], [0.463_867_2, 0.268_066_4, 0.0], [0.799_804_7, 0.100_097_656, 0.0]]; + +/// Hadamard "sequency" ordering for 2, 4, 8 and 16 blocks, concatenated. +pub const ORDERY: [usize; 30] = [1, 0, 3, 0, 2, 1, 7, 0, 4, 3, 6, 1, 5, 2, 15, 0, 8, 7, 12, 3, 11, 4, 14, 1, 9, 6, 13, 2, 10, 5]; diff --git a/crates/opus/src/decoder.rs b/crates/opus/src/decoder.rs new file mode 100644 index 0000000..573d536 --- /dev/null +++ b/crates/opus/src/decoder.rs @@ -0,0 +1,416 @@ +//! Single-stream Opus decoder: mode switching, hybrid summation, redundancy and transitions +//! (RFC 6716 §4, §4.5). + +use crate::celt::CeltDecoder; +use crate::celt::tables::OVERLAP; +use crate::packet::{Bandwidth, Mode, Packet}; +use crate::range::RangeDecoder; +use crate::silk::{LostFlag, SilkDecoder}; +use crate::{Error, Result}; + +/// Output sample rates supported by the decoder. +pub const SAMPLE_RATES: [u32; 5] = [8000, 12000, 16000, 24000, 48000]; + +/// Decoder for one elementary Opus stream (mono or stereo), like `OpusDecoder`. +pub struct StreamDecoder { + rate: u32, + channels: usize, + celt: CeltDecoder, + silk: SilkDecoder, + stream_channels: usize, + bandwidth: Bandwidth, + mode: Option, + prev_mode: Option, + frame_size: usize, + prev_redundancy: bool, + last_packet_duration: usize, + range_final: u32, + /// Output gain in Q8 dB (OpusHead output gain). + gain_q8: i16, +} + +impl StreamDecoder { + /// Creates a decoder producing `channels` (1 or 2) at `rate` (8/12/16/24/48 kHz). + pub fn new(rate: u32, channels: usize) -> Result { + if !SAMPLE_RATES.contains(&rate) { + return Err(Error::InvalidArgument("sample rate must be 8000, 12000, 16000, 24000 or 48000")); + } + if !(1..=2).contains(&channels) { + return Err(Error::InvalidArgument("channels must be 1 or 2")); + } + Ok(StreamDecoder { + rate, + channels, + celt: CeltDecoder::new(rate, channels), + silk: SilkDecoder::new(rate, channels), + stream_channels: channels, + bandwidth: Bandwidth::Fullband, + mode: None, + prev_mode: None, + frame_size: rate as usize / 400, + prev_redundancy: false, + last_packet_duration: 0, + range_final: 0, + gain_q8: 0, + }) + } + + pub fn sample_rate(&self) -> u32 { + self.rate + } + + pub fn channels(&self) -> usize { + self.channels + } + + /// Final range coder state of the last decoded frame (for conformance checks). + pub fn final_range(&self) -> u32 { + self.range_final + } + + /// Duration (samples per channel) of the last decoded packet. + pub fn last_packet_duration(&self) -> usize { + self.last_packet_duration + } + + /// Sets the output gain in Q8 dB (as in the OpusHead header). + pub fn set_gain_q8(&mut self, gain: i16) { + self.gain_q8 = gain; + } + + /// Disables the 180-degree intensity-stereo phase inversion (RFC 8251 §10). + pub fn set_phase_inversion_disabled(&mut self, disabled: bool) { + self.celt.disable_inv = disabled; + } + + /// `OPUS_RESET_STATE`. + pub fn reset(&mut self) { + self.celt.reset(); + self.silk.reset(); + self.stream_channels = self.channels; + self.bandwidth = Bandwidth::Fullband; + self.mode = None; + self.prev_mode = None; + self.frame_size = self.rate as usize / 400; + self.prev_redundancy = false; + self.last_packet_duration = 0; + self.range_final = 0; + } + + /// Decodes a packet into interleaved samples appended to `out`. `None` conceals one lost + /// packet of the last packet's duration. Returns samples per channel. + pub fn decode_interleaved(&mut self, packet: Option<&[u8]>, out: &mut Vec) -> Result { + match packet { + Some(p) if !p.is_empty() => self.decode_packet(p, out), + _ => { + let n = if self.last_packet_duration > 0 { self.last_packet_duration } else { self.rate as usize / 50 }; + self.conceal(n, out) + } + } + } + + /// Conceals `n` samples (a multiple of 2.5 ms). + pub fn conceal(&mut self, n: usize, out: &mut Vec) -> Result { + let f2_5 = self.rate as usize / 400; + if n == 0 || !n.is_multiple_of(f2_5) { + return Err(Error::InvalidArgument("concealment length must be a multiple of 2.5 ms")); + } + let start = out.len(); + out.resize(start + n * self.channels, 0.0); + let mut done = 0; + while done < n { + let r = self.decode_frame(None, &mut out[start + done * self.channels..], n - done, false)?; + done += r; + } + self.last_packet_duration = n; + Ok(n) + } + + /// Decodes the in-band FEC (LBRR) of `packet` for the previous (lost) packet of `n` samples. + pub fn decode_fec(&mut self, packet: &[u8], n: usize, out: &mut Vec) -> Result { + let p = Packet::parse(packet)?; + let pfs = p.toc.frame_samples(self.rate); + if n < pfs || p.toc.mode == Mode::CeltOnly || self.mode == Some(Mode::CeltOnly) { + return self.conceal(n, out); + } + let start = out.len(); + if n > pfs { + self.conceal(n - pfs, out)?; + } + self.mode = Some(p.toc.mode); + self.bandwidth = p.toc.bandwidth; + self.frame_size = pfs; + self.stream_channels = p.toc.channels(); + let off = out.len(); + out.resize(off + pfs * self.channels, 0.0); + self.decode_frame(Some(p.frames[0]), &mut out[off..], pfs, true)?; + self.last_packet_duration = n; + debug_assert_eq!(out.len() - start, n * self.channels); + Ok(n) + } + + fn decode_packet(&mut self, data: &[u8], out: &mut Vec) -> Result { + let p = Packet::parse(data)?; + let fs = p.toc.frame_samples(self.rate); + self.mode = Some(p.toc.mode); + self.bandwidth = p.toc.bandwidth; + self.frame_size = fs; + self.stream_channels = p.toc.channels(); + let start = out.len(); + out.resize(start + fs * p.frames.len() * self.channels, 0.0); + let mut nb = 0; + for f in &p.frames { + let r = self.decode_frame(Some(f), &mut out[start + nb * self.channels..], fs, false)?; + nb += r; + } + out.truncate(start + nb * self.channels); + self.last_packet_duration = nb; + Ok(nb) + } + + /// `opus_decode_frame`: decodes one frame (or conceals when `data` is `None`/≤1 byte). + fn decode_frame(&mut self, data: Option<&[u8]>, pcm: &mut [f32], frame_size: usize, fec: bool) -> Result { + let ch = self.channels; + let fs = self.rate as usize; + let f20 = fs / 50; + let f10 = f20 >> 1; + let f5 = f10 >> 1; + let f2_5 = f5 >> 1; + let mut frame_size = frame_size.min(fs / 25 * 3); + let data = match data { + Some(d) if d.len() > 1 => Some(d), + _ => { + frame_size = frame_size.min(self.frame_size); + None + } + }; + let (mut audiosize, mode, bandwidth); + match data { + Some(_) => { + audiosize = self.frame_size; + mode = self.mode.unwrap_or(Mode::CeltOnly); + bandwidth = Some(self.bandwidth); + } + None => { + audiosize = frame_size; + bandwidth = None; + mode = match self.prev_mode { + None => { + pcm[..audiosize * ch].fill(0.0); + return Ok(audiosize); + } + Some(m) => m, + }; + if audiosize > f20 { + let mut done = 0; + while done < audiosize { + let r = self.decode_frame(None, &mut pcm[done * ch..], (audiosize - done).min(f20), false)?; + done += r; + } + return Ok(frame_size); + } else if audiosize < f20 { + if audiosize > f10 { + audiosize = f10; + } else if mode != Mode::SilkOnly && audiosize > f5 && audiosize < f10 { + audiosize = f5; + } + } + } + } + let data_len = data.map_or(0, |d| d.len()); + let mut dec_storage; + let mut dec: Option<&mut RangeDecoder> = match data { + Some(d) => { + dec_storage = RangeDecoder::new(d); + Some(&mut dec_storage) + } + None => None, + }; + let mut transition = false; + if data.is_some() + && self.prev_mode.is_some() + && ((mode == Mode::CeltOnly && self.prev_mode != Some(Mode::CeltOnly) && !self.prev_redundancy) + || (mode != Mode::CeltOnly && self.prev_mode == Some(Mode::CeltOnly))) + { + transition = true; + } + let mut pcm_transition = Vec::new(); + if transition && mode == Mode::CeltOnly { + pcm_transition.resize(f5 * ch, 0.0); + let n = f5.min(audiosize); + self.decode_frame(None, &mut pcm_transition, n, false)?; + } + if audiosize > frame_size { + return Err(Error::InvalidArgument("frame larger than output buffer")); + } + frame_size = audiosize; + + // SILK. + let mut pcm_silk: Vec = Vec::new(); + if mode != Mode::CeltOnly { + pcm_silk.resize(frame_size.max(f10) * ch, 0.0); + if self.prev_mode == Some(Mode::CeltOnly) { + self.silk.reset(); + } + let payload_ms = (1000 * audiosize / fs).max(10); + let internal_rate = if mode == Mode::SilkOnly { + match bandwidth { + Some(Bandwidth::Narrowband) => 8000, + Some(Bandwidth::Mediumband) => 12000, + _ => 16000, + } + } else { + 16000 + }; + if data.is_some() { + self.silk.set_stream(self.stream_channels, internal_rate); + } + let lost = if data.is_none() { + LostFlag::Lost + } else if fec { + LostFlag::Lbrr + } else { + LostFlag::Normal + }; + let mut decoded = 0; + while decoded < frame_size { + let first = decoded == 0; + let n = match self.silk.decode(dec.as_deref_mut(), lost, first, payload_ms, &mut pcm_silk[decoded * ch..]) { + Ok(n) => n, + Err(e) => { + if lost != LostFlag::Normal { + let n = frame_size - decoded; + pcm_silk[decoded * ch..(decoded + n) * ch].fill(0.0); + n + } else { + return Err(e); + } + } + }; + if n == 0 { + break; + } + decoded += n; + } + } + + let mut start_band = 0; + let mut redundancy = false; + let mut celt_to_silk = false; + let mut redundancy_bytes = 0usize; + let mut len = data_len; + if !fec + && mode != Mode::CeltOnly + && let Some(d) = dec.as_deref_mut() + && d.tell() + 17 + 20 * (self.mode == Some(Mode::Hybrid)) as i32 <= 8 * len as i32 + { + redundancy = if mode == Mode::Hybrid { d.bit_logp(12) } else { true }; + if redundancy { + celt_to_silk = d.bit_logp(1); + redundancy_bytes = if mode == Mode::Hybrid { d.uint(256) as usize + 2 } else { len - ((d.tell() as usize + 7) >> 3) }; + if redundancy_bytes > len || (len - redundancy_bytes) * 8 < d.tell() as usize { + len = 0; + redundancy_bytes = 0; + redundancy = false; + } else { + len -= redundancy_bytes; + } + d.shrink_storage(redundancy_bytes); + } + } + if mode != Mode::CeltOnly { + start_band = 17; + } + if redundancy { + transition = false; + } + if transition && mode != Mode::CeltOnly { + pcm_transition.resize(f5 * ch, 0.0); + let n = f5.min(audiosize); + self.decode_frame(None, &mut pcm_transition, n, false)?; + } + if let Some(bw) = bandwidth { + self.celt.end = bw.celt_end_band(); + } + self.celt.stream_channels = self.stream_channels; + let mut redundant_audio = vec![0f32; if redundancy { f5 * ch } else { 0 }]; + let mut redundant_rng = 0u32; + let red_data = data.map(|d| &d[len..len + redundancy_bytes]); + if redundancy && celt_to_silk { + self.celt.start = 0; + self.celt.decode(red_data, f5, &mut redundant_audio, false, None)?; + redundant_rng = self.celt.rng; + } + self.celt.start = start_band; + + if mode != Mode::SilkOnly { + let celt_frame = f20.min(frame_size); + if self.prev_mode.is_some() && Some(mode) != self.prev_mode && !self.prev_redundancy { + self.celt.reset(); + } + let cdata = if fec { None } else { data.map(|d| &d[..len]) }; + let cdec = if fec { None } else { dec.as_deref_mut() }; + self.celt.decode(cdata, celt_frame, pcm, false, cdec)?; + } else { + pcm[..frame_size * ch].fill(0.0); + if self.prev_mode == Some(Mode::Hybrid) && !(redundancy && celt_to_silk && self.prev_redundancy) { + self.celt.start = 0; + let silence = [0xFFu8, 0xFF]; + self.celt.decode(Some(&silence), f2_5, pcm, true, None)?; + } + } + if mode != Mode::CeltOnly { + for (o, s) in pcm[..frame_size * ch].iter_mut().zip(&pcm_silk) { + *o += *s; + } + } + let window = &crate::celt::mode().window; + if redundancy && !celt_to_silk { + self.celt.reset(); + self.celt.start = 0; + self.celt.decode(red_data, f5, &mut redundant_audio, false, None)?; + redundant_rng = self.celt.rng; + let o = ch * (frame_size - f2_5); + let tail: Vec = pcm[o..o + f2_5 * ch].to_vec(); + smooth_fade(&tail, &redundant_audio[ch * f2_5..], &mut pcm[o..], f2_5, ch, window, self.rate); + } + if redundancy && celt_to_silk { + pcm[..f2_5 * ch].copy_from_slice(&redundant_audio[..f2_5 * ch]); + let cur: Vec = pcm[ch * f2_5..ch * 2 * f2_5].to_vec(); + smooth_fade(&redundant_audio[ch * f2_5..], &cur, &mut pcm[ch * f2_5..], f2_5, ch, window, self.rate); + } + if transition { + if audiosize >= f5 { + pcm[..f2_5 * ch].copy_from_slice(&pcm_transition[..f2_5 * ch]); + let cur: Vec = pcm[ch * f2_5..ch * 2 * f2_5].to_vec(); + smooth_fade(&pcm_transition[ch * f2_5..], &cur, &mut pcm[ch * f2_5..], f2_5, ch, window, self.rate); + } else { + let cur: Vec = pcm[..ch * f2_5].to_vec(); + smooth_fade(&pcm_transition, &cur, pcm, f2_5, ch, window, self.rate); + } + } + if self.gain_q8 != 0 { + let g = (6.488_141e-4f32 * self.gain_q8 as f32).exp2(); + for v in pcm[..frame_size * ch].iter_mut() { + *v *= g; + } + } + self.range_final = match dec.as_deref() { + Some(d) if data_len > 1 => d.rng() ^ redundant_rng, + _ => 0, + }; + self.prev_mode = Some(mode); + self.prev_redundancy = redundancy && !celt_to_silk; + Ok(audiosize) + } +} + +fn smooth_fade(in1: &[f32], in2: &[f32], out: &mut [f32], overlap: usize, ch: usize, window: &[f32; OVERLAP], rate: u32) { + let inc = (48000 / rate) as usize; + for c in 0..ch { + for i in 0..overlap { + let w = window[i * inc] * window[i * inc]; + out[i * ch + c] = w * in2[i * ch + c] + (1.0 - w) * in1[i * ch + c]; + } + } +} diff --git a/crates/opus/src/header.rs b/crates/opus/src/header.rs new file mode 100644 index 0000000..2fe0c75 --- /dev/null +++ b/crates/opus/src/header.rs @@ -0,0 +1,179 @@ +//! Opus identification header (`OpusHead`, RFC 7845 §5.1) and the ISO-BMFF `dOps` box payload. + +use crate::{Error, Result}; + +/// Decoder configuration: channel layout, multistream mapping, pre-skip and output gain. +#[derive(Clone, Debug, PartialEq, Eq)] +pub struct OpusHead { + /// Output channel count (1..=255). + pub channels: usize, + /// Samples (at 48 kHz) to discard from the start of the decoded stream. + pub pre_skip: u16, + /// Sample rate of the original input (informational only). + pub input_sample_rate: u32, + /// Output gain in Q7.8 dB. + pub output_gain: i16, + /// Channel mapping family (0: mono/stereo, 1: Vorbis order surround, 255: unspecified order). + pub mapping_family: u8, + /// Number of elementary streams. + pub streams: usize, + /// Number of coupled (stereo) streams; these come first. + pub coupled_streams: usize, + /// Output channel → decoded channel index (255 = silence). + pub mapping: Vec, +} + +impl OpusHead { + /// A family-0 configuration for plain mono/stereo streams (no header present). + pub fn simple(channels: usize) -> Result { + if !(1..=2).contains(&channels) { + return Err(Error::InvalidArgument("family 0 supports 1 or 2 channels")); + } + Ok(OpusHead { + channels, + pre_skip: 0, + input_sample_rate: 48000, + output_gain: 0, + mapping_family: 0, + streams: 1, + coupled_streams: channels - 1, + mapping: (0..channels as u8).collect(), + }) + } + + /// Parses an `OpusHead` packet (Ogg first packet, Matroska `CodecPrivate`). + pub fn parse(data: &[u8]) -> Result { + if data.len() < 19 || &data[..8] != b"OpusHead" { + return Err(Error::InvalidHeader("missing OpusHead magic")); + } + let version = data[8]; + if version >> 4 != 0 { + return Err(Error::Unsupported("OpusHead major version")); + } + let channels = data[9] as usize; + let pre_skip = u16::from_le_bytes([data[10], data[11]]); + let input_sample_rate = u32::from_le_bytes([data[12], data[13], data[14], data[15]]); + let output_gain = i16::from_le_bytes([data[16], data[17]]); + let family = data[18]; + Self::build(channels, pre_skip, input_sample_rate, output_gain, family, &data[19..]) + } + + /// Parses the payload of an ISO-BMFF `dOps` box (Opus in MP4, big-endian fields). + pub fn from_dops(data: &[u8]) -> Result { + if data.len() < 11 { + return Err(Error::InvalidHeader("dOps too short")); + } + if data[0] != 0 { + return Err(Error::Unsupported("dOps version")); + } + let channels = data[1] as usize; + let pre_skip = u16::from_be_bytes([data[2], data[3]]); + let input_sample_rate = u32::from_be_bytes([data[4], data[5], data[6], data[7]]); + let output_gain = i16::from_be_bytes([data[8], data[9]]); + Self::build(channels, pre_skip, input_sample_rate, output_gain, data[10], &data[11..]) + } + + /// Serialises as an `OpusHead` packet. + pub fn to_bytes(&self) -> Vec { + let mut v = b"OpusHead".to_vec(); + v.push(1); + v.push(self.channels as u8); + v.extend(self.pre_skip.to_le_bytes()); + v.extend(self.input_sample_rate.to_le_bytes()); + v.extend(self.output_gain.to_le_bytes()); + v.push(self.mapping_family); + if self.mapping_family != 0 { + v.push(self.streams as u8); + v.push(self.coupled_streams as u8); + v.extend(&self.mapping); + } + v + } + + fn build(channels: usize, pre_skip: u16, input_sample_rate: u32, output_gain: i16, family: u8, rest: &[u8]) -> Result { + if channels == 0 { + return Err(Error::InvalidHeader("zero channels")); + } + let (streams, coupled, mapping) = if family == 0 { + if channels > 2 { + return Err(Error::InvalidHeader("family 0 allows at most 2 channels")); + } + (1, channels - 1, (0..channels as u8).collect::>()) + } else { + if family == 3 { + return Err(Error::Unsupported("channel mapping family 3 (projection)")); + } + if rest.len() < 2 + channels { + return Err(Error::InvalidHeader("truncated channel mapping table")); + } + let streams = rest[0] as usize; + let coupled = rest[1] as usize; + if streams == 0 || coupled > streams || streams + coupled > 255 { + return Err(Error::InvalidHeader("bad stream counts")); + } + if family == 1 && !(1..=8).contains(&channels) { + return Err(Error::InvalidHeader("family 1 allows 1 to 8 channels")); + } + let mapping = rest[2..2 + channels].to_vec(); + if mapping.iter().any(|&m| m != 255 && m as usize >= streams + coupled) { + return Err(Error::InvalidHeader("channel mapping out of range")); + } + (streams, coupled, mapping) + }; + Ok(OpusHead { channels, pre_skip, input_sample_rate, output_gain, mapping_family: family, streams, coupled_streams: coupled, mapping }) + } + + /// Linear output gain factor. + pub fn gain_linear(&self) -> f32 { + 10f32.powf(self.output_gain as f32 / (20.0 * 256.0)) + } +} + +#[cfg(test)] +mod tests { + use super::*; + + #[test] + fn parse_family0_and_roundtrip() { + let mut h = b"OpusHead".to_vec(); + h.extend([1, 2, 0x38, 0x01, 0x80, 0xBB, 0, 0, 0x00, 0x01, 0]); + let head = OpusHead::parse(&h).unwrap(); + assert_eq!(head.channels, 2); + assert_eq!(head.pre_skip, 312); + assert_eq!(head.input_sample_rate, 48000); + assert_eq!(head.output_gain, 256); + assert_eq!((head.streams, head.coupled_streams), (1, 1)); + assert_eq!(head.to_bytes(), h); + assert!((head.gain_linear() - 1.122).abs() < 1e-3); + } + + #[test] + fn parse_family1_51() { + let mut h = b"OpusHead".to_vec(); + h.extend([1, 6, 0x38, 0x01, 0x80, 0xBB, 0, 0, 0, 0, 1, 4, 2, 0, 4, 1, 2, 3, 5]); + let head = OpusHead::parse(&h).unwrap(); + assert_eq!((head.channels, head.streams, head.coupled_streams), (6, 4, 2)); + assert_eq!(head.mapping, vec![0, 4, 1, 2, 3, 5]); + assert_eq!(OpusHead::parse(&head.to_bytes()).unwrap(), head); + } + + #[test] + fn rejects_garbage() { + assert!(OpusHead::parse(b"OpusHea").is_err()); + assert!(OpusHead::parse(b"OpusTags...........").is_err()); + let mut h = b"OpusHead".to_vec(); + h.extend([1, 3, 0, 0, 0, 0, 0, 0, 0, 0, 0]); + assert!(OpusHead::parse(&h).is_err()); + let mut h = b"OpusHead".to_vec(); + h.extend([1, 2, 0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 0, 0, 5]); + assert!(OpusHead::parse(&h).is_err()); + } + + #[test] + fn dops() { + let d = [0u8, 2, 0x01, 0x38, 0, 0, 0xBB, 0x80, 0, 0, 0]; + let head = OpusHead::from_dops(&d).unwrap(); + assert_eq!(head.pre_skip, 312); + assert_eq!(head.channels, 2); + } +} diff --git a/crates/opus/src/lib.rs b/crates/opus/src/lib.rs new file mode 100644 index 0000000..6a909ea --- /dev/null +++ b/crates/opus/src/lib.rs @@ -0,0 +1,46 @@ +//! Clean-room pure-Rust Opus decoder (RFC 6716, updated by RFC 8251). +//! +//! Layer L0: no dependencies beyond `std`; no `unsafe`; builds for `wasm32-unknown-unknown`. + +// Spec-style indexed loops (FilmCraft allows this lint workspace-wide). +#![allow(clippy::needless_range_loop)] +#![cfg_attr(not(test), deny(clippy::unwrap_used, clippy::expect_used, clippy::panic, clippy::unimplemented, clippy::todo, clippy::unreachable))] +// The fixed-point parts mirror the normative integer arithmetic; keep C-like shift expressions. +#![allow(clippy::precedence, clippy::int_plus_one)] + +pub(crate) mod celt; +mod decoder; +mod header; +mod multistream; +pub mod packet; +pub(crate) mod range; +pub(crate) mod silk; + +pub use decoder::{SAMPLE_RATES, StreamDecoder}; +pub use header::OpusHead; +pub use multistream::Decoder; +pub use packet::{Bandwidth, Mode, Packet, Toc}; + +/// Errors from header parsing and decoding. +#[derive(Debug, Clone, PartialEq, Eq)] +pub enum Error { + InvalidArgument(&'static str), + InvalidPacket(&'static str), + InvalidHeader(&'static str), + Unsupported(&'static str), +} + +impl std::fmt::Display for Error { + fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { + match self { + Error::InvalidArgument(s) => write!(f, "invalid argument: {s}"), + Error::InvalidPacket(s) => write!(f, "invalid packet: {s}"), + Error::InvalidHeader(s) => write!(f, "invalid header: {s}"), + Error::Unsupported(s) => write!(f, "unsupported: {s}"), + } + } +} + +impl std::error::Error for Error {} + +pub type Result = std::result::Result; diff --git a/crates/opus/src/multistream.rs b/crates/opus/src/multistream.rs new file mode 100644 index 0000000..a41ba10 --- /dev/null +++ b/crates/opus/src/multistream.rs @@ -0,0 +1,171 @@ +//! Public decoder: OpusHead-configured (multistream) decoding to planar f32. + +use crate::decoder::StreamDecoder; +use crate::header::OpusHead; +use crate::packet::Packet; +use crate::{Error, Result}; + +/// Opus decoder for an `OpusHead` configuration (mono, stereo, or multistream surround). +/// +/// Produces planar f32 at the chosen output rate. Pre-skip is *not* removed unless +/// [`Decoder::set_trim_pre_skip`] is enabled (containers usually handle it via edit lists / +/// granule positions). The header's output gain is applied. +pub struct Decoder { + head: OpusHead, + rate: u32, + streams: Vec, + trim_pre_skip: bool, + skip_remaining: usize, + scratch: Vec>, +} + +impl Decoder { + /// Creates a 48 kHz decoder from an `OpusHead` packet (Ogg / Matroska `CodecPrivate`). + pub fn new(opus_head: &[u8]) -> Result { + Self::from_head(OpusHead::parse(opus_head)?, 48000) + } + + /// Creates a decoder for plain mono/stereo packets without a header. + pub fn simple(sample_rate: u32, channels: usize) -> Result { + Self::from_head(OpusHead::simple(channels)?, sample_rate) + } + + /// Creates a decoder for a parsed header at `sample_rate` (8/12/16/24/48 kHz). + pub fn from_head(head: OpusHead, sample_rate: u32) -> Result { + let mut streams = Vec::with_capacity(head.streams); + for s in 0..head.streams { + let ch = if s < head.coupled_streams { 2 } else { 1 }; + let mut d = StreamDecoder::new(sample_rate, ch)?; + d.set_gain_q8(head.output_gain); + streams.push(d); + } + Ok(Decoder { rate: sample_rate, streams, trim_pre_skip: false, skip_remaining: 0, scratch: vec![Vec::new(); head.streams], head }) + } + + pub fn head(&self) -> &OpusHead { + &self.head + } + + pub fn sample_rate(&self) -> u32 { + self.rate + } + + pub fn channels(&self) -> usize { + self.head.channels + } + + /// Pre-skip in samples at the output rate. + pub fn pre_skip(&self) -> usize { + self.head.pre_skip as usize * self.rate as usize / 48000 + } + + /// When enabled, the first [`Self::pre_skip`] output samples are dropped. + pub fn set_trim_pre_skip(&mut self, enable: bool) { + self.trim_pre_skip = enable; + self.skip_remaining = if enable { self.pre_skip() } else { 0 }; + } + + /// Disables intensity-stereo phase inversion (useful when downmixing; RFC 8251 section 10). + pub fn set_phase_inversion_disabled(&mut self, disabled: bool) { + for s in &mut self.streams { + s.set_phase_inversion_disabled(disabled); + } + } + + /// Final range coder state of the first stream (conformance testing). + pub fn final_range(&self) -> u32 { + self.streams.first().map_or(0, |s| s.final_range()) + } + + pub fn reset(&mut self) { + for s in &mut self.streams { + s.reset(); + } + self.skip_remaining = if self.trim_pre_skip { self.pre_skip() } else { 0 }; + } + + /// Decodes one packet (`None` = lost packet, concealed) into planar channels. + pub fn decode(&mut self, packet: Option<&[u8]>) -> Result>> { + let n = self.streams.len(); + for b in &mut self.scratch { + b.clear(); + } + let mut samples = None; + match packet { + Some(data) if !data.is_empty() => { + let mut off = 0; + for s in 0..n { + let rest = &data[off..]; + if rest.is_empty() { + return Err(Error::InvalidPacket("truncated multistream packet")); + } + let used = if s + 1 < n { Packet::parse_self_delimited(rest)?.1 } else { rest.len() }; + let pkt = &rest[..used]; + let r = if s + 1 < n { + // Re-frame the self-delimited packet as a regular one for the stream decoder. + let (p, _) = Packet::parse_self_delimited(pkt)?; + let regular = reframe(&p); + self.streams[s].decode_interleaved(Some(®ular), &mut self.scratch[s])? + } else { + self.streams[s].decode_interleaved(Some(pkt), &mut self.scratch[s])? + }; + if *samples.get_or_insert(r) != r { + return Err(Error::InvalidPacket("multistream frame durations differ")); + } + off += used; + } + } + _ => { + let dur = match self.streams[0].last_packet_duration() { + 0 => self.rate as usize / 50, + d => d, + }; + for s in 0..n { + self.streams[s].conceal(dur, &mut self.scratch[s])?; + } + samples = Some(dur); + } + } + let samples = samples.unwrap_or(0); + let mut out = vec![vec![0f32; samples]; self.head.channels]; + for (c, o) in out.iter_mut().enumerate() { + let m = self.head.mapping[c] as usize; + if m == 255 { + continue; + } + let (s, ch, nch) = if m < 2 * self.head.coupled_streams { (m / 2, m % 2, 2) } else { (m - self.head.coupled_streams, 0, 1) }; + let src = &self.scratch[s]; + for (i, v) in o.iter_mut().enumerate() { + *v = src[i * nch + ch]; + } + } + if self.skip_remaining > 0 { + let k = self.skip_remaining.min(samples); + for o in &mut out { + o.drain(..k); + } + self.skip_remaining -= k; + } + Ok(out) + } +} + +/// Rebuilds a regular (code 3 VBR) packet from a parsed self-delimited one. +fn reframe(p: &Packet) -> Vec { + let mut v = vec![(p.toc.config << 3) | if p.toc.stereo { 4 } else { 0 } | 3]; + v.push(0x80 | p.frames.len() as u8); + for f in &p.frames[..p.frames.len() - 1] { + let s = f.len(); + if s < 252 { + v.push(s as u8); + } else { + let b0 = 252 + (s & 3); + v.push(b0 as u8); + v.push(((s - b0) >> 2) as u8); + } + } + for f in &p.frames { + v.extend_from_slice(f); + } + v +} diff --git a/crates/opus/src/packet.rs b/crates/opus/src/packet.rs new file mode 100644 index 0000000..a4a3f2e --- /dev/null +++ b/crates/opus/src/packet.rs @@ -0,0 +1,340 @@ +//! TOC byte and frame packing (RFC 6716 §3). + +use crate::Error; + +/// Coding mode of an Opus frame. +#[derive(Clone, Copy, Debug, PartialEq, Eq)] +pub enum Mode { + SilkOnly, + Hybrid, + CeltOnly, +} + +/// Audio bandwidth. +#[derive(Clone, Copy, Debug, PartialEq, Eq, PartialOrd, Ord)] +pub enum Bandwidth { + /// 4 kHz. + Narrowband, + /// 6 kHz. + Mediumband, + /// 8 kHz. + Wideband, + /// 12 kHz. + SuperWideband, + /// 20 kHz. + Fullband, +} + +impl Bandwidth { + /// Number of CELT bands coded for this bandwidth. + pub fn celt_end_band(self) -> usize { + match self { + Bandwidth::Narrowband => 13, + Bandwidth::Mediumband | Bandwidth::Wideband => 17, + Bandwidth::SuperWideband => 19, + Bandwidth::Fullband => 21, + } + } +} + +/// Decoded table-of-contents byte. +#[derive(Clone, Copy, Debug, PartialEq, Eq)] +pub struct Toc { + pub config: u8, + pub mode: Mode, + pub bandwidth: Bandwidth, + pub stereo: bool, + /// Frame count code (0..=3). + pub code: u8, +} + +impl Toc { + pub fn parse(b: u8) -> Toc { + let config = b >> 3; + let (mode, bandwidth) = match config { + 0..=3 => (Mode::SilkOnly, Bandwidth::Narrowband), + 4..=7 => (Mode::SilkOnly, Bandwidth::Mediumband), + 8..=11 => (Mode::SilkOnly, Bandwidth::Wideband), + 12..=13 => (Mode::Hybrid, Bandwidth::SuperWideband), + 14..=15 => (Mode::Hybrid, Bandwidth::Fullband), + 16..=19 => (Mode::CeltOnly, Bandwidth::Narrowband), + 20..=23 => (Mode::CeltOnly, Bandwidth::Wideband), + 24..=27 => (Mode::CeltOnly, Bandwidth::SuperWideband), + _ => (Mode::CeltOnly, Bandwidth::Fullband), + }; + Toc { config, mode, bandwidth, stereo: b & 4 != 0, code: b & 3 } + } + + /// Samples per frame at 48 kHz. + pub fn frame_samples_48k(&self) -> usize { + self.frame_samples(48000) + } + + /// Samples per frame at `rate`. + pub fn frame_samples(&self, rate: u32) -> usize { + let rate = rate as usize; + let c = self.config as usize; + match self.mode { + Mode::CeltOnly => (rate << (c & 3)) / 400, + Mode::Hybrid => { + if c & 1 != 0 { + rate / 50 + } else { + rate / 100 + } + } + Mode::SilkOnly => match c & 3 { + 3 => rate * 60 / 1000, + s => (rate << s) / 100, + }, + } + } + + pub fn channels(&self) -> usize { + if self.stereo { 2 } else { 1 } + } +} + +/// A parsed packet: TOC plus the byte ranges of each frame. +#[derive(Clone, Debug)] +pub struct Packet<'a> { + pub toc: Toc, + pub frames: Vec<&'a [u8]>, + /// Bytes of padding (code 3 only). + pub padding: usize, +} + +fn parse_size(data: &[u8]) -> Option<(usize, usize)> { + match data.first() { + None => None, + Some(&b) if b < 252 => Some((b as usize, 1)), + Some(&b) => data.get(1).map(|&b1| (4 * b1 as usize + b as usize, 2)), + } +} + +impl<'a> Packet<'a> { + /// Parses a packet (RFC 6716 section 3.2), enforcing requirements R1 to R7. + pub fn parse(data: &'a [u8]) -> Result, Error> { + Self::parse_impl(data, false).map(|(p, _)| p) + } + + /// Parses a self-delimited packet (RFC 6716 Appendix B, used for all but the last stream of + /// a multistream packet); returns the packet and the number of bytes it occupies. + pub fn parse_self_delimited(data: &'a [u8]) -> Result<(Packet<'a>, usize), Error> { + Self::parse_impl(data, true) + } + + fn parse_impl(data: &'a [u8], self_delimited: bool) -> Result<(Packet<'a>, usize), Error> { + const BAD: Error = Error::InvalidPacket("malformed packet framing"); + if data.is_empty() { + return Err(Error::InvalidPacket("empty packet")); + } + let toc = Toc::parse(data[0]); + let fs = toc.frame_samples_48k(); + let mut pos = 1usize; + let mut len = data.len() as isize - 1; + let mut sizes: Vec = Vec::new(); + let mut padding = 0usize; + let mut cbr = false; + let count; + let mut last_size = len; + let read_size = |pos: &mut usize, len: &mut isize| -> Result { + let avail = &data[*pos..(*pos + (*len).max(0) as usize).min(data.len())]; + let (s, n) = parse_size(avail).ok_or(BAD)?; + *pos += n; + *len -= n as isize; + Ok(s as isize) + }; + match toc.code { + 0 => count = 1, + 1 => { + count = 2; + cbr = true; + if !self_delimited { + if len & 1 != 0 { + return Err(BAD); + } + last_size = len / 2; + sizes.push(last_size); + } + } + 2 => { + count = 2; + let s0 = read_size(&mut pos, &mut len)?; + if s0 > len { + return Err(BAD); + } + sizes.push(s0); + last_size = len - s0; + } + _ => { + if len < 1 { + return Err(BAD); + } + let ch = data[pos]; + pos += 1; + len -= 1; + count = (ch & 0x3F) as usize; + if count == 0 || fs * count > 5760 { + return Err(BAD); + } + if ch & 0x40 != 0 { + loop { + if len <= 0 { + return Err(BAD); + } + let b = data[pos]; + pos += 1; + len -= 1; + let tmp = if b == 255 { 254 } else { b as isize }; + len -= tmp; + padding += tmp as usize; + if b != 255 { + break; + } + } + } + if len < 0 { + return Err(BAD); + } + cbr = ch & 0x80 == 0; + if !cbr { + last_size = len; + for _ in 0..count - 1 { + let before = len; + let s = read_size(&mut pos, &mut len)?; + if s > len { + return Err(BAD); + } + sizes.push(s); + last_size -= (before - len) + s; + } + if last_size < 0 { + return Err(BAD); + } + } else if !self_delimited { + last_size = len / count as isize; + if last_size * count as isize != len { + return Err(BAD); + } + for _ in 0..count - 1 { + sizes.push(last_size); + } + } + } + } + if self_delimited { + let before = len; + let s = read_size(&mut pos, &mut len)?; + if s > len { + return Err(BAD); + } + if cbr { + if s * count as isize > len { + return Err(BAD); + } + sizes = vec![s; count - 1]; + } else if (before - len) + s > last_size { + return Err(BAD); + } + last_size = s; + } + if last_size > 1275 || sizes.iter().any(|&s| s > 1275) { + return Err(BAD); + } + sizes.push(last_size); + let mut frames = Vec::with_capacity(count); + for &s in &sizes { + let s = s as usize; + if pos + s > data.len() { + return Err(BAD); + } + frames.push(&data[pos..pos + s]); + pos += s; + } + let total = if self_delimited { pos + padding } else { data.len() }; + if total > data.len() { + return Err(BAD); + } + Ok((Packet { toc, frames, padding }, total)) + } +} + +/// Number of samples (at 48 kHz) in a packet, or an error for malformed framing. +pub fn packet_samples_48k(data: &[u8]) -> Result { + let p = Packet::parse(data)?; + Ok(p.toc.frame_samples_48k() * p.frames.len()) +} + +#[cfg(test)] +mod tests { + use super::*; + + #[test] + fn toc_configs() { + for cfg in 0u8..32 { + let t = Toc::parse(cfg << 3); + let ms10 = t.frame_samples(48000) * 10 / 48; // tenths of ms + let expect = match cfg { + 0..=11 => [100, 200, 400, 600][cfg as usize & 3], + 12..=15 => [100, 200][cfg as usize & 1], + _ => [25, 50, 100, 200][cfg as usize & 3], + }; + assert_eq!(ms10, expect, "config {cfg}"); + } + assert!(Toc::parse(0x04).stereo); + } + + #[test] + fn frame_codes() { + // code 0 + let p = Packet::parse(&[0x00, 1, 2, 3]).unwrap(); + assert_eq!(p.frames, vec![&[1u8, 2, 3][..]]); + // code 1 (odd payload is invalid) + assert!(Packet::parse(&[0x01, 1, 2, 3]).is_err()); + let p = Packet::parse(&[0x01, 1, 2, 3, 4]).unwrap(); + assert_eq!(p.frames.len(), 2); + // code 2 + let p = Packet::parse(&[0x02, 1, 9, 8, 7]).unwrap(); + assert_eq!(p.frames, vec![&[9u8][..], &[8u8, 7][..]]); + assert!(Packet::parse(&[0x02, 5, 1]).is_err()); + // code 3 CBR with padding: 2 frames of 2 bytes, 3 bytes padding + let p = Packet::parse(&[0x03, 0x42, 3, 1, 2, 3, 4, 0, 0, 0]).unwrap(); + assert_eq!(p.frames, vec![&[1u8, 2][..], &[3u8, 4][..]]); + assert_eq!(p.padding, 3); + // code 3 VBR + let p = Packet::parse(&[0x03, 0x83, 1, 2, 10, 20, 21, 30]).unwrap(); + assert_eq!(p.frames, vec![&[10u8][..], &[20u8, 21][..], &[30u8][..]]); + // too many frames (> 120 ms) + assert!(Packet::parse(&[0x03 | (3 << 3), 0x03, 0]).is_err()); + // zero frames + assert!(Packet::parse(&[0x03, 0x00]).is_err()); + assert!(Packet::parse(&[]).is_err()); + } + + #[test] + fn two_byte_sizes() { + let mut pkt = vec![0x02u8, 252, 1]; + pkt.extend(std::iter::repeat_n(7u8, 256)); + pkt.extend([1, 2]); + let p = Packet::parse(&pkt).unwrap(); + assert_eq!(p.frames[0].len(), 256); + assert_eq!(p.frames[1], &[1, 2]); + } + + #[test] + fn self_delimited() { + // code 0 with explicit size, followed by trailing data of the next stream. + let (p, used) = Packet::parse_self_delimited(&[0x00, 2, 5, 6, 0xAA, 0xBB]).unwrap(); + assert_eq!(p.frames, vec![&[5u8, 6][..]]); + assert_eq!(used, 4); + // code 1: one size for both frames. + let (p, used) = Packet::parse_self_delimited(&[0x01, 1, 5, 6, 9]).unwrap(); + assert_eq!(p.frames, vec![&[5u8][..], &[6u8][..]]); + assert_eq!(used, 4); + // code 3 VBR, 2 frames: size of frame 0, then size of last frame. + let (p, used) = Packet::parse_self_delimited(&[0x03, 0x82, 1, 2, 7, 8, 9, 0xFF]).unwrap(); + assert_eq!(p.frames, vec![&[7u8][..], &[8u8, 9][..]]); + assert_eq!(used, 7); + } +} diff --git a/crates/opus/src/range.rs b/crates/opus/src/range.rs new file mode 100644 index 0000000..33ba14f --- /dev/null +++ b/crates/opus/src/range.rs @@ -0,0 +1,497 @@ +//! Range decoder (RFC 6716 §4.1). +//! +//! The decoder reads range-coded symbols from the front of the buffer and raw bits from the back. +//! Reading past either end yields zeros (never panics); callers detect corruption through +//! [`RangeDecoder::tell`] exceeding the budget or through out-of-range values. + +const SYM_BITS: u32 = 8; +const CODE_BITS: u32 = 32; +const SYM_MAX: u32 = (1 << SYM_BITS) - 1; +#[cfg(test)] +const CODE_SHIFT: u32 = CODE_BITS - SYM_BITS - 1; +const CODE_TOP: u32 = 1 << (CODE_BITS - 1); +const CODE_BOT: u32 = CODE_TOP >> SYM_BITS; +const CODE_EXTRA: u32 = (CODE_BITS - 2) % SYM_BITS + 1; +/// Bit resolution of [`RangeDecoder::tell_frac`] (1/8 bit). +pub const BITRES: u32 = 3; + +/// Number of bits needed to represent `x` (0 for 0). +#[inline] +pub fn ilog(x: u32) -> i32 { + (32 - x.leading_zeros()) as i32 +} + +#[derive(Clone, Debug)] +pub struct RangeDecoder<'a> { + buf: &'a [u8], + /// Bytes available to the decoder (may be reduced, e.g. for redundancy). + storage: usize, + offs: usize, + end_offs: usize, + end_window: u32, + nend_bits: i32, + nbits_total: i32, + rng: u32, + val: u32, + rem: u32, + /// Set when an out-of-range value was decoded. + pub error: bool, +} + +impl<'a> RangeDecoder<'a> { + pub fn new(buf: &'a [u8]) -> Self { + let mut d = RangeDecoder { + buf, + storage: buf.len(), + offs: 0, + end_offs: 0, + end_window: 0, + nend_bits: 0, + nbits_total: (CODE_BITS + 1 - ((CODE_BITS - CODE_EXTRA) / SYM_BITS) * SYM_BITS) as i32, + rng: 1 << CODE_EXTRA, + val: 0, + rem: 0, + error: false, + }; + d.rem = d.read_byte(); + d.val = d.rng - 1 - (d.rem >> (SYM_BITS - CODE_EXTRA)); + d.normalize(); + d + } + + /// Size of the buffer in bytes as seen by the decoder. + pub fn storage(&self) -> usize { + self.storage + } + + /// Shrinks the buffer seen by the decoder (raw bits are then read from the new end). + pub fn shrink_storage(&mut self, by: usize) { + self.storage = self.storage.saturating_sub(by); + } + + pub fn rng(&self) -> u32 { + self.rng + } + + #[inline] + fn read_byte(&mut self) -> u32 { + if self.offs < self.storage { + let b = self.buf[self.offs]; + self.offs += 1; + b as u32 + } else { + 0 + } + } + + #[inline] + fn read_byte_from_end(&mut self) -> u32 { + if self.end_offs < self.storage { + self.end_offs += 1; + self.buf[self.storage - self.end_offs] as u32 + } else { + 0 + } + } + + #[inline] + fn normalize(&mut self) { + while self.rng <= CODE_BOT { + self.nbits_total += SYM_BITS as i32; + self.rng <<= SYM_BITS; + let sym = self.rem; + self.rem = self.read_byte(); + let sym = ((sym << SYM_BITS) | self.rem) >> (SYM_BITS - CODE_EXTRA); + self.val = ((self.val << SYM_BITS).wrapping_add(SYM_MAX & !sym)) & (CODE_TOP - 1); + } + } + + /// Returns the cumulative frequency of the next symbol for total `ft` (call [`Self::update`] next). + #[inline] + pub fn decode(&mut self, ft: u32) -> u32 { + let ext = self.rng / ft; + let s = self.val / ext; + // stash ext in rem-free form: recomputed by update() + ft - (s + 1).min(ft) + } + + /// Like [`Self::decode`] with `ft = 1 << bits`. + #[inline] + pub fn decode_bin(&mut self, bits: u32) -> u32 { + let ext = self.rng >> bits; + let s = self.val / ext; + (1 << bits) - (s + 1).min(1 << bits) + } + + /// Consumes the symbol with cumulative range `[fl, fh)` of total `ft`. + #[inline] + pub fn update(&mut self, fl: u32, fh: u32, ft: u32) { + let ext = self.rng / ft; + let s = ext * (ft - fh); + self.val = self.val.wrapping_sub(s); + self.rng = if fl > 0 { ext * (fh - fl) } else { self.rng - s }; + self.normalize(); + } + + /// Like [`Self::update`] for a symbol decoded with [`Self::decode_bin`]. + #[inline] + pub fn update_bin(&mut self, fl: u32, fh: u32, bits: u32) { + let ext = self.rng >> bits; + let ft = 1u32 << bits; + let s = ext * (ft - fh); + self.val = self.val.wrapping_sub(s); + self.rng = if fl > 0 { ext * (fh - fl) } else { self.rng - s }; + self.normalize(); + } + + /// Decodes a binary symbol whose probability of being 1 is `1/2^logp`. + #[inline] + pub fn bit_logp(&mut self, logp: u32) -> bool { + let r = self.rng; + let d = self.val; + let s = r >> logp; + let ret = d < s; + if !ret { + self.val = d - s; + } + self.rng = if ret { s } else { r - s }; + self.normalize(); + ret + } + + /// Decodes a symbol with an "inverse CDF" table (`icdf[i] = ft - cdf[i+1]`, `ft = 1 << ftb`). + #[inline] + pub fn icdf(&mut self, icdf: &[u8], ftb: u32) -> usize { + let mut s = self.rng; + let d = self.val; + let r = s >> ftb; + let mut ret = 0usize; + let mut t; + loop { + t = s; + let v = icdf.get(ret).copied().unwrap_or(0) as u32; + s = r * v; + if d >= s || ret + 1 >= icdf.len() { + break; + } + ret += 1; + } + // If the table is exhausted without d >= s, force the last symbol (only on corrupt tables). + if d < s { + s = 0; + } + self.val = d - s; + self.rng = t - s; + self.normalize(); + ret + } + + /// Decodes a uniformly distributed integer in `[0, ft)` (`ft >= 2`). + pub fn uint(&mut self, ft: u32) -> u32 { + debug_assert!(ft > 1); + let ft1 = ft - 1; + let ftb = ilog(ft1); + if ftb > 8 { + let ftb = (ftb - 8) as u32; + let top = (ft1 >> ftb) + 1; + let s = self.decode(top); + self.update(s, s + 1, top); + let t = (s << ftb) | self.bits(ftb); + if t <= ft1 { + return t; + } + self.error = true; + ft1 + } else { + let s = self.decode(ft); + self.update(s, s + 1, ft); + s + } + } + + /// Reads `bits` raw bits (0..=25) from the end of the buffer. + pub fn bits(&mut self, bits: u32) -> u32 { + if bits == 0 { + return 0; + } + let mut window = self.end_window; + let mut available = self.nend_bits; + if (available as u32) < bits { + loop { + window |= self.read_byte_from_end() << available; + available += SYM_BITS as i32; + if available > (CODE_BITS - SYM_BITS) as i32 { + break; + } + } + } + let ret = window & ((1u32 << bits) - 1); + window >>= bits; + available -= bits as i32; + self.end_window = window; + self.nend_bits = available; + self.nbits_total += bits as i32; + ret + } + + /// Advances the bit counter so that [`Self::tell`] reports `total` (CELT silence frames). + pub fn skip_to_end(&mut self, total: i32) { + let t = self.tell(); + self.nbits_total += total - t; + } + + /// Number of bits consumed so far (rounded up). + #[inline] + pub fn tell(&self) -> i32 { + self.nbits_total - ilog(self.rng) + } + + /// Number of 1/8 bits consumed so far (rounded up). + pub fn tell_frac(&self) -> u32 { + let nbits = (self.nbits_total as u32) << BITRES; + let mut l = ilog(self.rng) as u32; + let mut r = self.rng >> (l - 16); + for _ in 0..BITRES { + r = (r * r) >> 15; + let b = r >> 16; + l = (l << 1) | b; + r >>= b; + } + nbits.wrapping_sub(l) + } +} + +#[cfg(test)] +pub(crate) mod enc { + //! Minimal range encoder (RFC 6716 §5.1), used only to build test streams. + use super::*; + + pub struct RangeEncoder { + out: Vec, + end: Vec, + end_window: u32, + nend_bits: i32, + rng: u32, + low: u32, + rem: i32, + ext: u32, + size: usize, + } + + impl RangeEncoder { + pub fn new(size: usize) -> Self { + RangeEncoder { out: Vec::new(), end: Vec::new(), end_window: 0, nend_bits: 0, rng: CODE_TOP, low: 0, rem: -1, ext: 0, size } + } + fn carry_out(&mut self, c: u32) { + if c != SYM_MAX { + let carry = c >> SYM_BITS; + if self.rem >= 0 { + self.out.push((self.rem as u32 + carry) as u8); + } + if self.ext > 0 { + let sym = (SYM_MAX + carry) & SYM_MAX; + for _ in 0..self.ext { + self.out.push(sym as u8); + } + self.ext = 0; + } + self.rem = (c & SYM_MAX) as i32; + } else { + self.ext += 1; + } + } + fn normalize(&mut self) { + while self.rng <= CODE_BOT { + self.carry_out(self.low >> CODE_SHIFT); + self.low = (self.low << SYM_BITS) & (CODE_TOP - 1); + self.rng <<= SYM_BITS; + } + } + pub fn encode(&mut self, fl: u32, fh: u32, ft: u32) { + let r = self.rng / ft; + if fl > 0 { + self.low += self.rng - r * (ft - fl); + self.rng = r * (fh - fl); + } else { + self.rng -= r * (ft - fh); + } + self.normalize(); + } + pub fn bit_logp(&mut self, val: bool, logp: u32) { + let r = self.rng; + let s = r >> logp; + let r2 = r - s; + if val { + self.low += r2; + self.rng = s; + } else { + self.rng = r2; + } + self.normalize(); + } + pub fn icdf(&mut self, s: usize, icdf: &[u8], ftb: u32) { + let r = self.rng >> ftb; + if s > 0 { + self.low += self.rng - r * icdf[s - 1] as u32; + self.rng = r * (icdf[s - 1] as u32 - icdf[s] as u32); + } else { + self.rng -= r * icdf[s] as u32; + } + self.normalize(); + } + pub fn uint(&mut self, fl: u32, ft: u32) { + let ft1 = ft - 1; + let ftb = ilog(ft1); + if ftb > 8 { + let ftb = (ftb - 8) as u32; + let top = (ft1 >> ftb) + 1; + let v = fl >> ftb; + self.encode(v, v + 1, top); + self.bits(fl & ((1 << ftb) - 1), ftb); + } else { + self.encode(fl, fl + 1, ft); + } + } + pub fn bits(&mut self, val: u32, bits: u32) { + let mut window = self.end_window; + let mut used = self.nend_bits; + if used as u32 + bits > CODE_BITS { + while used >= SYM_BITS as i32 { + self.end.push((window & SYM_MAX) as u8); + window >>= SYM_BITS; + used -= SYM_BITS as i32; + } + } + window |= val << used; + used += bits as i32; + self.end_window = window; + self.nend_bits = used; + } + /// Finalises and returns a buffer of exactly `size` bytes. + pub fn done(mut self) -> Vec { + let mut l = CODE_BITS as i32 - ilog(self.rng); + let mut msk = (CODE_TOP - 1) >> l; + let mut end = (self.low + msk) & !msk; + if (end | msk) >= self.low + self.rng { + l += 1; + msk >>= 1; + end = (self.low + msk) & !msk; + } + while l > 0 { + self.carry_out(end >> CODE_SHIFT); + end = (end << SYM_BITS) & (CODE_TOP - 1); + l -= SYM_BITS as i32; + } + if self.rem >= 0 || self.ext > 0 { + self.carry_out(0); + } + let mut window = self.end_window; + let mut used = self.nend_bits; + while used > 0 { + self.end.push((window & SYM_MAX) as u8); + window >>= SYM_BITS; + used -= SYM_BITS as i32; + } + assert!(self.out.len() + self.end.len() <= self.size, "range encoder overflow"); + let mut buf = self.out; + buf.resize(self.size - self.end.len(), 0); + for &b in self.end.iter().rev() { + buf.push(b); + } + buf + } + } +} + +#[cfg(test)] +mod tests { + use super::enc::RangeEncoder; + use super::*; + + struct Lcg(u32); + impl Lcg { + fn next(&mut self) -> u32 { + self.0 = self.0.wrapping_mul(1664525).wrapping_add(1013904223); + self.0 >> 8 + } + } + + #[test] + fn roundtrip_mixed_symbols() { + let mut rng = Lcg(1); + for trial in 0..200 { + let n = 1 + (rng.next() % 300) as usize; + let mut ops = Vec::new(); + for _ in 0..n { + match rng.next() % 5 { + 0 => { + let ft = 2 + rng.next() % 1000; + let v = rng.next() % ft; + ops.push((0u8, v, ft)); + } + 1 => { + let logp = 1 + rng.next() % 15; + ops.push((1, rng.next().is_multiple_of(7) as u32, logp)); + } + 2 => { + let bits = 1 + rng.next() % 24; + ops.push((2, rng.next() & ((1 << bits) - 1), bits)); + } + 3 => { + let ft = 2 + rng.next() % 100_000_000; + ops.push((3, rng.next() % ft, ft)); + } + _ => ops.push((4, rng.next() % 4, 0)), + } + } + let icdf: [u8; 4] = [200, 120, 30, 0]; + let mut e = RangeEncoder::new(8 * n + 16); + for &(k, v, p) in &ops { + match k { + 0 => e.encode(v, v + 1, p), + 1 => e.bit_logp(v != 0, p), + 2 => e.bits(v, p), + 3 => e.uint(v, p), + _ => e.icdf(v as usize, &icdf, 8), + } + } + let buf = e.done(); + let mut d = RangeDecoder::new(&buf); + for (i, &(k, v, p)) in ops.iter().enumerate() { + let got = match k { + 0 => { + let s = d.decode(p); + d.update(s, s + 1, p); + s + } + 1 => d.bit_logp(p) as u32, + 2 => d.bits(p), + 3 => d.uint(p), + _ => d.icdf(&icdf, 8) as u32, + }; + assert_eq!(got, v, "trial {trial} op {i} kind {k}"); + } + assert!(!d.error); + } + } + + #[test] + fn tell_starts_at_one_bit() { + let buf = [0u8; 8]; + let d = RangeDecoder::new(&buf); + assert_eq!(d.tell(), 1); + assert_eq!(d.tell_frac(), 8); + } + + #[test] + fn empty_and_garbage_never_panic() { + let mut d = RangeDecoder::new(&[]); + for i in 0..100 { + d.bits(1 + i % 20); + d.uint(3 + i * 77); + d.icdf(&[250, 100, 0], 8); + d.bit_logp(1 + i % 15); + } + let _ = d.tell_frac(); + } +} diff --git a/crates/opus/src/silk/lpc.rs b/crates/opus/src/silk/lpc.rs new file mode 100644 index 0000000..29a1e81 --- /dev/null +++ b/crates/opus/src/silk/lpc.rs @@ -0,0 +1,374 @@ +//! Fixed-point NLSF decoding, NLSF→LPC conversion and filter stabilisation +//! (RFC 6716 §4.2.7.5, with the RFC 8251 §6/§7 overflow fixes). + +use super::tables::*; + +pub const MAX_LPC_ORDER: usize = 16; + +#[inline] +pub fn smulwb(a: i32, b: i32) -> i32 { + ((a as i64 * (b as i16) as i64) >> 16) as i32 +} +#[inline] +pub fn smlawb(a: i32, b: i32, c: i32) -> i32 { + a.wrapping_add(smulwb(b, c)) +} +#[inline] +pub fn smulww(a: i32, b: i32) -> i32 { + ((a as i64 * b as i64) >> 16) as i32 +} +#[inline] +pub fn smmul(a: i32, b: i32) -> i32 { + ((a as i64 * b as i64) >> 32) as i32 +} +#[inline] +pub fn rshift_round(a: i32, s: u32) -> i32 { + if s == 1 { (a >> 1) + (a & 1) } else { ((a >> (s - 1)) + 1) >> 1 } +} +#[inline] +pub fn rshift_round64(a: i64, s: u32) -> i64 { + if s == 1 { (a >> 1) + (a & 1) } else { ((a >> (s - 1)) + 1) >> 1 } +} +#[inline] +pub fn sat16(a: i32) -> i32 { + a.clamp(-32768, 32767) +} + +/// `silk_INVERSE32_varQ`: approximation of `(1 << q_res) / b32`. +pub fn inverse32_varq(b32: i32, q_res: i32) -> i32 { + let b_headrm = b32.unsigned_abs().leading_zeros() as i32 - 1; + let b32_nrm = b32.wrapping_shl(b_headrm as u32); + let b32_inv = (i32::MAX >> 2) / (b32_nrm >> 16); + let mut result = b32_inv << 16; + let err_q32 = ((1i32 << 29).wrapping_sub(smulwb(b32_nrm, b32_inv))).wrapping_shl(3); + result = result.wrapping_add(((err_q32 as i64 * b32_inv as i64) >> 16) as i32); + let lshift = 61 - b_headrm - q_res; + if lshift <= 0 { + let s = (-lshift) as u32; + let lim_hi = i32::MAX >> s; + let lim_lo = i32::MIN >> s; + result.clamp(lim_lo, lim_hi) << s + } else if lshift < 32 { + result >> lshift + } else { + 0 + } +} + +/// `silk_DIV32_varQ`: approximation of `(a32 << q_res) / b32`. +pub fn div32_varq(a32: i32, b32: i32, q_res: i32) -> i32 { + let a_headrm = a32.unsigned_abs().leading_zeros() as i32 - 1; + let a32_nrm = a32.wrapping_shl(a_headrm as u32); + let b_headrm = b32.unsigned_abs().leading_zeros() as i32 - 1; + let b32_nrm = b32.wrapping_shl(b_headrm as u32); + let b32_inv = (i32::MAX >> 2) / (b32_nrm >> 16); + let mut result = smulwb(a32_nrm, b32_inv); + let a32_nrm = a32_nrm.wrapping_sub(((smmul(b32_nrm, result) as i64) << 3) as i32); + result = smlawb(result, a32_nrm, b32_inv); + let lshift = 29 + a_headrm - b_headrm - q_res; + if lshift < 0 { + let s = (-lshift) as u32; + let lim_hi = i32::MAX >> s; + let lim_lo = i32::MIN >> s; + result.clamp(lim_lo, lim_hi) << s + } else if lshift < 32 { + result >> lshift + } else { + 0 + } +} + +fn sqrt_approx(x: i32) -> i32 { + if x <= 0 { + return 0; + } + let lz = x.leading_zeros() as i32; + let frac = (x.rotate_right((24 - lz).rem_euclid(32) as u32)) & 0x7f; + let mut y = if lz & 1 != 0 { 32768 } else { 46214 }; + y >>= lz >> 1; + smlawb(y, y, 213 * frac) +} + +/// Decodes the stage-1/stage-2 indices into stabilised NLSFs (Q15). +pub fn nlsf_decode(indices: &[i32], wb: bool) -> [i32; MAX_LPC_ORDER] { + let order = if wb { 16 } else { 10 }; + let i1 = indices[0] as usize; + let cb1: &[i32] = if wb { &NLSF_CB_WB[i1] } else { &NLSF_CB_NBMB[i1] }; + let qstep = if wb { 9830 } else { 11796 }; + // Residual dequantisation with backwards prediction. + let mut res_q10 = [0i32; MAX_LPC_ORDER]; + let mut out_q10 = 0i32; + for i in (0..order).rev() { + let pred = if i + 1 < order { + let sel = if wb { NLSF_PSEL_WB[i1][i] } else { NLSF_PSEL_NBMB[i1][i] } as usize; + let w = if wb { NLSF_PRED_WB[sel][i] } else { NLSF_PRED_NBMB[sel][i] }; + (out_q10 * w) >> 8 + } else { + 0 + }; + let mut v = indices[i + 1] << 10; + if v > 0 { + v -= 102; + } else if v < 0 { + v += 102; + } + out_q10 = smlawb(pred, v, qstep); + res_q10[i] = out_q10; + } + // Laroia weights. + let mut nlsf = [0i32; MAX_LPC_ORDER]; + for k in 0..order { + let prev = if k == 0 { 0 } else { cb1[k - 1] }; + let next = if k + 1 == order { 256 } else { cb1[k + 1] }; + let w = (1024 / (cb1[k] - prev).max(1) + 1024 / (next - cb1[k]).max(1)).min(32767); + let w_q9 = sqrt_approx(w << 16); + let v = (cb1[k] << 7) + (res_q10[k] << 14) / w_q9.max(1); + nlsf[k] = v.clamp(0, 32767); + } + let dmin: &[i32] = if wb { &NLSF_MIN_SPACING_WB } else { &NLSF_MIN_SPACING_NBMB }; + nlsf_stabilize(&mut nlsf[..order], dmin); + nlsf +} + +pub fn nlsf_stabilize(nlsf: &mut [i32], dmin: &[i32]) { + let l = nlsf.len(); + for _ in 0..20 { + let mut min_diff = nlsf[0] - dmin[0]; + let mut idx = 0; + for i in 1..l { + let d = nlsf[i] - (nlsf[i - 1] + dmin[i]); + if d < min_diff { + min_diff = d; + idx = i; + } + } + let d = 32768 - (nlsf[l - 1] + dmin[l]); + if d < min_diff { + min_diff = d; + idx = l; + } + if min_diff >= 0 { + return; + } + if idx == 0 { + nlsf[0] = dmin[0]; + } else if idx == l { + nlsf[l - 1] = 32768 - dmin[l]; + } else { + let mut min_center = dmin[..idx].iter().sum::(); + min_center += dmin[idx] >> 1; + let mut max_center = 32768 - dmin[idx + 1..=l].iter().sum::(); + max_center -= dmin[idx] >> 1; + let c = rshift_round(nlsf[idx - 1] + nlsf[idx], 1); + let c = if min_center > max_center { c.clamp(max_center, min_center) } else { c.clamp(min_center, max_center) }; + nlsf[idx - 1] = c - (dmin[idx] >> 1); + nlsf[idx] = nlsf[idx - 1] + dmin[idx]; + } + } + // Fallback. + nlsf.sort_unstable(); + nlsf[0] = nlsf[0].max(dmin[0]); + for i in 1..l { + nlsf[i] = nlsf[i].max(sat16(nlsf[i - 1] + dmin[i])); + } + nlsf[l - 1] = nlsf[l - 1].min(32768 - dmin[l]); + for i in (0..l - 1).rev() { + nlsf[i] = nlsf[i].min(nlsf[i + 1] - dmin[i + 1]); + } +} + +fn bwexpander_32(ar: &mut [i32], mut chirp_q16: i32) { + let d = ar.len(); + let chirp_minus_one = chirp_q16 - 65536; + for v in ar.iter_mut().take(d - 1) { + *v = smulww(chirp_q16, *v); + chirp_q16 += rshift_round(chirp_q16.wrapping_mul(chirp_minus_one), 16); + } + ar[d - 1] = smulww(chirp_q16, ar[d - 1]); +} + +/// `silk_bwexpander` on Q12 coefficients (16-bit). +pub fn bwexpander(ar: &mut [i32], mut chirp_q16: i32) { + let d = ar.len(); + let chirp_minus_one = chirp_q16 - 65536; + for v in ar.iter_mut().take(d - 1) { + *v = rshift_round(chirp_q16.wrapping_mul(*v), 16); + chirp_q16 += rshift_round(chirp_q16.wrapping_mul(chirp_minus_one), 16); + } + ar[d - 1] = rshift_round(chirp_q16.wrapping_mul(ar[d - 1]), 16); +} + +fn lpc_inverse_pred_gain(a_q12: &[i32]) -> i32 { + const QA: u32 = 24; + const A_LIMIT: i32 = 16_773_022; + let order = a_q12.len(); + let mut a = [0i32; MAX_LPC_ORDER]; + let mut dc = 0; + for k in 0..order { + dc += a_q12[k]; + a[k] = a_q12[k] << (QA - 12); + } + if dc >= 4096 { + return 0; + } + let mut inv_gain: i32 = 1 << 30; + let mul32_frac_q31 = |x: i32, y: i32| rshift_round64(x as i64 * y as i64, 31) as i32; + for k in (1..order).rev() { + if a[k] > A_LIMIT || a[k] < -A_LIMIT { + return 0; + } + let rc_q31 = -(a[k] << (31 - QA)); + let rc_mult1 = (1i32 << 30) - smmul(rc_q31, rc_q31); + inv_gain = smmul(inv_gain, rc_mult1) << 2; + if inv_gain < 107_374 { + return 0; + } + let mult2q = 32 - rc_mult1.unsigned_abs().leading_zeros() as i32; + let rc_mult2 = inverse32_varq(rc_mult1, mult2q + 30); + for n in 0..(k + 1) >> 1 { + let t1 = a[n]; + let t2 = a[k - n - 1]; + let v1 = rshift_round64(t1.saturating_sub(mul32_frac_q31(t2, rc_q31)) as i64 * rc_mult2 as i64, mult2q as u32); + if v1 > i32::MAX as i64 || v1 < i32::MIN as i64 { + return 0; + } + let v2 = rshift_round64(t2.saturating_sub(mul32_frac_q31(t1, rc_q31)) as i64 * rc_mult2 as i64, mult2q as u32); + if v2 > i32::MAX as i64 || v2 < i32::MIN as i64 { + return 0; + } + a[n] = v1 as i32; + a[k - n - 1] = v2 as i32; + } + } + if a[0] > A_LIMIT || a[0] < -A_LIMIT { + return 0; + } + let rc_q31 = -(a[0] << (31 - QA)); + let rc_mult1 = (1i32 << 30) - smmul(rc_q31, rc_q31); + inv_gain = smmul(inv_gain, rc_mult1) << 2; + if inv_gain < 107_374 { + return 0; + } + inv_gain +} + +/// Converts NLSFs (Q15) to stable Q12 LPC coefficients (`silk_NLSF2A`). +pub fn nlsf2a(nlsf: &[i32]) -> [i32; MAX_LPC_ORDER] { + let d = nlsf.len(); + let ordering: &[usize] = if d == 16 { &LSF_ORDER_WB } else { &LSF_ORDER_NBMB }; + let mut c_q17 = [0i32; MAX_LPC_ORDER]; + for k in 0..d { + let i = (nlsf[k] >> 8) as usize; + let f = nlsf[k] & 255; + let c0 = COS_Q12[i]; + let c1 = COS_Q12[(i + 1).min(128)]; + c_q17[ordering[k]] = (c0 * 256 + (c1 - c0) * f + 4) >> 3; + } + let d2 = d / 2; + let find_poly = |off: usize| -> [i64; 10] { + let mut out = [0i64; 10]; + out[0] = 1 << 16; + out[1] = -(c_q17[off] as i64); + for k in 1..d2 { + let ftmp = c_q17[2 * k + off] as i64; + out[k + 1] = 2 * out[k - 1] - ((ftmp * out[k] + 32768) >> 16); + for n in (2..=k).rev() { + out[n] += out[n - 2] - ((ftmp * out[n - 1] + 32768) >> 16); + } + out[1] -= ftmp; + } + out + }; + let p = find_poly(0); + let q = find_poly(1); + let mut a32 = [0i32; MAX_LPC_ORDER]; + for k in 0..d2 { + let ptmp = p[k + 1] + p[k]; + let qtmp = q[k + 1] - q[k]; + a32[k] = (-qtmp - ptmp) as i32; + a32[d - k - 1] = (qtmp - ptmp) as i32; + } + let a32 = &mut a32[..d]; + // Limit the dynamic range (silk_LPC_fit with QIN = 17, QOUT = 12). + let mut a_q12 = [0i32; MAX_LPC_ORDER]; + let mut i = 0; + while i < 10 { + let mut maxabs = 0i32; + let mut idx = 0; + for (k, &v) in a32.iter().enumerate() { + let av = v.saturating_abs(); + if av > maxabs { + maxabs = av; + idx = k; + } + } + let maxabs = rshift_round(maxabs, 5); + if maxabs > 32767 { + let maxabs = maxabs.min(163_838); + let chirp = 65470 - ((maxabs - 32767) << 14) / ((maxabs * (idx as i32 + 1)) >> 2); + bwexpander_32(a32, chirp); + } else { + break; + } + i += 1; + } + if i == 10 { + for k in 0..d { + a_q12[k] = sat16(rshift_round(a32[k], 5)); + a32[k] = a_q12[k] << 5; + } + } else { + for k in 0..d { + a_q12[k] = rshift_round(a32[k], 5); + } + } + for i in 0..16 { + if lpc_inverse_pred_gain(&a_q12[..d]) == 0 { + bwexpander_32(a32, 65536 - (2 << i)); + for k in 0..d { + a_q12[k] = rshift_round(a32[k], 5); + } + } else { + break; + } + } + a_q12 +} + +#[cfg(test)] +mod tests { + use super::*; + + #[test] + fn flat_nlsf_gives_small_lpc() { + // Uniformly spaced NLSFs correspond to an (almost) flat spectrum. + let nlsf: Vec = (1..=16).map(|k| k * 32768 / 17).collect(); + let a = nlsf2a(&nlsf); + assert!(a.iter().all(|v| v.abs() < 600), "{a:?}"); + let nlsf: Vec = (1..=10).map(|k| k * 32768 / 11).collect(); + let a = nlsf2a(&nlsf); + assert!(a[..10].iter().all(|v| v.abs() < 600), "{a:?}"); + } + + #[test] + fn stabilize_enforces_spacing() { + let mut v = [100, 90, 80, 30000, 30001, 30002, 31000, 32000, 32700, 32760]; + nlsf_stabilize(&mut v, &NLSF_MIN_SPACING_NBMB); + assert!(v[0] >= NLSF_MIN_SPACING_NBMB[0]); + for i in 1..10 { + assert!(v[i] - v[i - 1] >= NLSF_MIN_SPACING_NBMB[i], "{v:?}"); + } + assert!(32768 - v[9] >= NLSF_MIN_SPACING_NBMB[10]); + } + + #[test] + fn inverse_approximations() { + for &b in &[65536, 100_000, 1 << 20, 81920, 1_686_110_208] { + let inv = inverse32_varq(b, 47) as f64; + let exact = 2f64.powi(47) / b as f64; + assert!((inv - exact).abs() / exact < 1e-3, "{b}"); + } + let q = div32_varq(65536, 131072, 16) as f64; + assert!((q - 32768.0).abs() < 2.0); + } +} diff --git a/crates/opus/src/silk/mod.rs b/crates/opus/src/silk/mod.rs new file mode 100644 index 0000000..15f5de9 --- /dev/null +++ b/crates/opus/src/silk/mod.rs @@ -0,0 +1,867 @@ +//! SILK layer decoder (RFC 6716 §4.2). +//! +//! Parameter decoding and reconstruction follow the normative fixed-point arithmetic so that the +//! output matches the reference decoder sample for sample at the internal rate. Resampling to the +//! output rate uses our own linear-phase interpolator with the delays of RFC 6716 Table 54. + +pub(crate) mod lpc; +mod resampler; +#[allow(dead_code)] +pub(crate) mod tables; + +use lpc::*; +use resampler::Resampler; +use tables::*; + +use crate::range::RangeDecoder; +use crate::{Error, Result}; + +const MAX_FRAME: usize = 320; +const MAX_SUBFR: usize = 80; +const LTP_ORDER: usize = 5; +const OUT_BUF: usize = MAX_FRAME + 2 * MAX_SUBFR; + +#[derive(Clone, Copy, Debug, PartialEq, Eq)] +pub enum LostFlag { + Normal, + Lost, + Lbrr, +} + +#[derive(Clone, Copy, PartialEq, Eq)] +enum Cond { + Independently, + IndependentlyNoLtpScaling, + Conditionally, +} + +#[derive(Clone, Default)] +struct Indices { + gains: [i32; 4], + ltp_index: [usize; 4], + nlsf: [i32; 17], + lag_index: i32, + contour_index: usize, + signal_type: usize, + quant_offset_type: usize, + nlsf_interp_q2: i32, + per_index: usize, + ltp_scale_index: usize, + seed: i32, +} + +#[derive(Clone)] +struct Channel { + fs_khz: usize, + nb_subfr: usize, + frame_length: usize, + subfr_length: usize, + ltp_mem_length: usize, + lpc_order: usize, + prev_gain_q16: i32, + s_lpc_q14: [i32; MAX_LPC_ORDER], + out_buf: [i16; OUT_BUF], + lag_prev: i32, + last_gain_index: i32, + prev_signal_type: usize, + first_frame_after_reset: bool, + prev_nlsf_q15: [i32; MAX_LPC_ORDER], + ec_prev_signal_type: usize, + ec_prev_lag_index: i32, + vad_flags: [bool; 3], + lbrr_flag: bool, + lbrr_flags: [bool; 3], + n_frames_decoded: usize, + n_frames_per_packet: usize, + loss_cnt: u32, + indices: Indices, + resampler: Resampler, + // PLC memory. + plc_lpc_q12: [i32; MAX_LPC_ORDER], + plc_gain_q16: i32, + plc_pitch: i32, + plc_rand: u32, +} + +impl Channel { + fn new() -> Channel { + Channel { + fs_khz: 0, + nb_subfr: 4, + frame_length: 0, + subfr_length: 0, + ltp_mem_length: 0, + lpc_order: 0, + prev_gain_q16: 65536, + s_lpc_q14: [0; MAX_LPC_ORDER], + out_buf: [0; OUT_BUF], + lag_prev: 100, + last_gain_index: 10, + prev_signal_type: 0, + first_frame_after_reset: true, + prev_nlsf_q15: [0; MAX_LPC_ORDER], + ec_prev_signal_type: 0, + ec_prev_lag_index: 0, + vad_flags: [false; 3], + lbrr_flag: false, + lbrr_flags: [false; 3], + n_frames_decoded: 0, + n_frames_per_packet: 1, + loss_cnt: 0, + indices: Indices::default(), + resampler: Resampler::new(16000, 16000), + plc_lpc_q12: [0; MAX_LPC_ORDER], + plc_gain_q16: 0, + plc_pitch: 0, + plc_rand: 22222, + } + } + + fn set_fs(&mut self, fs_khz: usize, api_rate: u32) { + self.subfr_length = 5 * fs_khz; + let frame_length = self.nb_subfr * self.subfr_length; + if self.fs_khz != fs_khz || self.resampler.rates() != (fs_khz as u32 * 1000, api_rate) { + self.resampler = Resampler::new(fs_khz as u32 * 1000, api_rate); + } + if self.fs_khz != fs_khz || frame_length != self.frame_length { + if self.fs_khz != fs_khz { + self.ltp_mem_length = 20 * fs_khz; + self.lpc_order = if fs_khz == 16 { 16 } else { 10 }; + self.first_frame_after_reset = true; + self.lag_prev = 100; + self.last_gain_index = 10; + self.prev_signal_type = 0; + self.out_buf = [0; OUT_BUF]; + self.s_lpc_q14 = [0; MAX_LPC_ORDER]; + } + self.fs_khz = fs_khz; + self.frame_length = frame_length; + } + } + + fn decode_indices(&mut self, dec: &mut RangeDecoder, frame_index: usize, decode_lbrr: bool, cond: Cond) { + let ix = if decode_lbrr || self.vad_flags[frame_index] { dec.icdf(&TYPE_ACTIVE, 8) + 2 } else { dec.icdf(&TYPE_INACTIVE, 8) }; + let ind = &mut self.indices; + ind.signal_type = ix >> 1; + ind.quant_offset_type = ix & 1; + if cond == Cond::Conditionally { + ind.gains[0] = dec.icdf(&DELTA_GAIN, 8) as i32; + } else { + ind.gains[0] = (dec.icdf(GAIN_MSB[ind.signal_type], 8) as i32) << 3; + ind.gains[0] += dec.icdf(&UNIFORM8, 8) as i32; + } + for i in 1..self.nb_subfr { + ind.gains[i] = dec.icdf(&DELTA_GAIN, 8) as i32; + } + let wb = self.lpc_order == 16; + let s1 = (ind.signal_type >> 1) + if wb { 2 } else { 0 }; + let i1 = dec.icdf(NLSF_STAGE1[s1], 8); + ind.nlsf[0] = i1 as i32; + for i in 0..self.lpc_order { + let cb = if wb { 8 + NLSF_SEL_WB[i1][i] as usize } else { NLSF_SEL_NBMB[i1][i] as usize }; + let mut v = dec.icdf(NLSF_STAGE2[cb], 8) as i32; + if v == 0 { + v -= dec.icdf(&NLSF_EXT, 8) as i32; + } else if v == 8 { + v += dec.icdf(&NLSF_EXT, 8) as i32; + } + ind.nlsf[i + 1] = v - 4; + } + ind.nlsf_interp_q2 = if self.nb_subfr == 4 { dec.icdf(&NLSF_INTERP, 8) as i32 } else { 4 }; + if ind.signal_type == 2 { + let mut absolute = true; + if cond == Cond::Conditionally && self.ec_prev_signal_type == 2 { + let d = dec.icdf(&PITCH_DELTA, 8) as i32; + if d > 0 { + ind.lag_index = self.ec_prev_lag_index + d - 9; + absolute = false; + } + } + if absolute { + ind.lag_index = dec.icdf(&PITCH_HIGH, 8) as i32 * (self.fs_khz as i32 >> 1); + let low = match self.fs_khz { + 8 => PITCH_LOW[0], + 12 => PITCH_LOW[1], + _ => PITCH_LOW[2], + }; + ind.lag_index += dec.icdf(low, 8) as i32; + } + self.ec_prev_lag_index = ind.lag_index; + let contour = match (self.fs_khz, self.nb_subfr) { + (8, 2) => PITCH_CONTOUR[0], + (8, _) => PITCH_CONTOUR[1], + (_, 2) => PITCH_CONTOUR[2], + _ => PITCH_CONTOUR[3], + }; + ind.contour_index = dec.icdf(contour, 8); + ind.per_index = dec.icdf(<P_PERIODICITY, 8); + for k in 0..self.nb_subfr { + ind.ltp_index[k] = dec.icdf(LTP_FILTER[ind.per_index], 8); + } + ind.ltp_scale_index = if cond == Cond::Independently { dec.icdf(<P_SCALE, 8) } else { 0 }; + } + self.ec_prev_signal_type = ind.signal_type; + ind.seed = dec.icdf(&UNIFORM4, 8) as i32; + } + + fn decode_frame(&mut self, dec: Option<&mut RangeDecoder>, out: &mut [i16], lost: LostFlag, cond: Cond) { + let l = self.frame_length; + let decode = match (lost, &dec) { + (LostFlag::Normal, Some(_)) => true, + (LostFlag::Lbrr, Some(_)) => self.lbrr_flags[self.n_frames_decoded], + _ => false, + }; + if decode && let Some(dec) = dec { + self.decode_indices(dec, self.n_frames_decoded, lost == LostFlag::Lbrr, cond); + let mut pulses = [0i32; MAX_FRAME + 16]; + decode_pulses(dec, &mut pulses, self.indices.signal_type, self.indices.quant_offset_type, l); + let ctrl = self.decode_parameters(cond); + self.decode_core(&ctrl, &pulses, out); + self.plc_lpc_q12 = ctrl.pred_coef_q12[1]; + self.plc_gain_q16 = ctrl.gains_q16[self.nb_subfr - 1]; + self.plc_pitch = if self.indices.signal_type == 2 { ctrl.pitch_l[self.nb_subfr - 1] } else { 0 }; + self.loss_cnt = 0; + self.prev_signal_type = self.indices.signal_type; + self.first_frame_after_reset = false; + self.lag_prev = ctrl.pitch_l[self.nb_subfr - 1]; + } else { + self.conceal(out); + } + // Update the output buffer. + let mv = self.ltp_mem_length - l; + self.out_buf.copy_within(l..l + mv, 0); + self.out_buf[mv..mv + l].copy_from_slice(&out[..l]); + } + + fn decode_parameters(&mut self, cond: Cond) -> Ctrl { + let mut ctrl = Ctrl::default(); + let nb = self.nb_subfr; + // Gains. + let mut prev = self.last_gain_index; + for k in 0..nb { + let ind = self.indices.gains[k]; + if k == 0 && cond != Cond::Conditionally { + prev = ind.max(prev - 16); + } else { + let tmp = ind - 4; + let thr = 2 * 36 - 64 + prev; + if tmp > thr { + prev += (tmp << 1) - thr; + } else { + prev += tmp; + } + } + prev = prev.clamp(0, 63); + ctrl.gains_q16[k] = log2lin((smulwb(0x1D1C71, prev) + 2090).min(3967)); + } + self.last_gain_index = prev; + // LPC. + let order = self.lpc_order; + let nlsf = nlsf_decode(&self.indices.nlsf, order == 16); + ctrl.pred_coef_q12[1] = nlsf2a(&nlsf[..order]); + if self.first_frame_after_reset { + self.indices.nlsf_interp_q2 = 4; + } + if self.indices.nlsf_interp_q2 < 4 { + let mut n0 = [0i32; MAX_LPC_ORDER]; + for i in 0..order { + n0[i] = self.prev_nlsf_q15[i] + ((self.indices.nlsf_interp_q2 * (nlsf[i] - self.prev_nlsf_q15[i])) >> 2); + } + ctrl.pred_coef_q12[0] = nlsf2a(&n0[..order]); + } else { + ctrl.pred_coef_q12[0] = ctrl.pred_coef_q12[1]; + } + self.prev_nlsf_q15 = nlsf; + if self.loss_cnt > 0 { + bwexpander(&mut ctrl.pred_coef_q12[0][..order], 63570); + bwexpander(&mut ctrl.pred_coef_q12[1][..order], 63570); + } + if self.indices.signal_type == 2 { + let fs = self.fs_khz as i32; + let min_lag = 2 * fs; + let max_lag = 18 * fs; + let lag = min_lag + self.indices.lag_index; + for k in 0..nb { + let off = match (self.fs_khz, nb) { + (8, 2) => CB_NB_10[self.indices.contour_index][k], + (8, _) => CB_NB_20[self.indices.contour_index][k], + (_, 2) => CB_WB_10[self.indices.contour_index][k], + _ => CB_WB_20[self.indices.contour_index][k], + }; + ctrl.pitch_l[k] = (lag + off).clamp(min_lag, max_lag); + } + for k in 0..nb { + let ix = self.indices.ltp_index[k]; + let taps = match self.indices.per_index { + 0 => LTP_TAPS_0[ix.min(7)], + 1 => LTP_TAPS_1[ix.min(15)], + _ => LTP_TAPS_2[ix.min(31)], + }; + for i in 0..LTP_ORDER { + ctrl.ltp_coef_q14[k * LTP_ORDER + i] = taps[i] << 7; + } + } + ctrl.ltp_scale_q14 = [15565, 12288, 8192][self.indices.ltp_scale_index]; + } + ctrl + } + + fn decode_core(&mut self, ctrl: &Ctrl, pulses: &[i32], xq: &mut [i16]) { + let ind = &self.indices; + let l = self.frame_length; + let sub = self.subfr_length; + let order = self.lpc_order; + let ltp_mem = self.ltp_mem_length; + let mut s_ltp = [0i16; MAX_FRAME]; + let mut s_ltp_q15 = [0i32; 2 * MAX_FRAME]; + let mut res_q14 = [0i32; MAX_SUBFR]; + let mut s_lpc = [0i32; MAX_SUBFR + MAX_LPC_ORDER]; + let offset_q10 = [[100, 240], [32, 100]][ind.signal_type >> 1][ind.quant_offset_type]; + let interp = ind.nlsf_interp_q2 < 4; + let mut exc = [0i32; MAX_FRAME]; + let mut seed = ind.seed; + for i in 0..l { + seed = seed.wrapping_mul(196_314_165).wrapping_add(907_633_515); + let mut e = pulses[i] << 14; + if e > 0 { + e -= 80 << 4; + } else if e < 0 { + e += 80 << 4; + } + e += offset_q10 << 4; + if seed < 0 { + e = -e; + } + exc[i] = e; + seed = seed.wrapping_add(pulses[i]); + } + s_lpc[..MAX_LPC_ORDER].copy_from_slice(&self.s_lpc_q14); + let mut ltp_buf_idx = ltp_mem; + for k in 0..self.nb_subfr { + let a_q12 = &ctrl.pred_coef_q12[k >> 1]; + let mut b_q14 = [0i32; LTP_ORDER]; + b_q14.copy_from_slice(&ctrl.ltp_coef_q14[k * LTP_ORDER..(k + 1) * LTP_ORDER]); + let mut signal_type = ind.signal_type; + let gain_q10 = ctrl.gains_q16[k] >> 6; + let mut inv_gain_q31 = inverse32_varq(ctrl.gains_q16[k], 47); + let gain_adj_q16 = if ctrl.gains_q16[k] != self.prev_gain_q16 { + let g = div32_varq(self.prev_gain_q16, ctrl.gains_q16[k], 16); + for v in s_lpc[..MAX_LPC_ORDER].iter_mut() { + *v = smulww(g, *v); + } + g + } else { + 1 << 16 + }; + self.prev_gain_q16 = ctrl.gains_q16[k]; + let mut lag = ctrl.pitch_l[k]; + if self.loss_cnt > 0 && self.prev_signal_type == 2 && signal_type != 2 && k < 2 { + b_q14 = [0; LTP_ORDER]; + b_q14[LTP_ORDER / 2] = 1 << 12; + signal_type = 2; + lag = self.lag_prev; + } + if signal_type == 2 { + let lag_u = lag as usize; + if k == 0 || (k == 2 && interp) { + let start_idx = ltp_mem as isize - lag as isize - order as isize - (LTP_ORDER / 2) as isize; + let start_idx = start_idx.max(0) as usize; + if k == 2 { + self.out_buf[ltp_mem..ltp_mem + 2 * sub].copy_from_slice(&xq[..2 * sub]); + } + let src_off = start_idx + k * sub; + lpc_analysis_filter(&mut s_ltp[start_idx..ltp_mem], &self.out_buf[src_off..src_off + ltp_mem - start_idx], a_q12, order); + if k == 0 { + inv_gain_q31 = smulwb(inv_gain_q31, ctrl.ltp_scale_q14) << 2; + } + for i in 0..(lag_u + LTP_ORDER / 2).min(ltp_mem) { + s_ltp_q15[ltp_buf_idx - i - 1] = smulwb(inv_gain_q31, s_ltp[ltp_mem - i - 1] as i32); + } + } else if gain_adj_q16 != 1 << 16 { + for i in 0..(lag_u + LTP_ORDER / 2).min(ltp_buf_idx) { + let p = ltp_buf_idx - i - 1; + s_ltp_q15[p] = smulww(gain_adj_q16, s_ltp_q15[p]); + } + } + let base = ltp_buf_idx as isize - lag as isize + (LTP_ORDER / 2) as isize; + for i in 0..sub { + let p = base + i as isize; + let g = |o: isize| -> i32 { if p - o >= 0 { s_ltp_q15[(p - o) as usize] } else { 0 } }; + let mut pred = 2i32; + pred = smlawb(pred, g(0), b_q14[0]); + pred = smlawb(pred, g(1), b_q14[1]); + pred = smlawb(pred, g(2), b_q14[2]); + pred = smlawb(pred, g(3), b_q14[3]); + pred = smlawb(pred, g(4), b_q14[4]); + res_q14[i] = exc[k * sub + i].wrapping_add(pred << 1); + s_ltp_q15[ltp_buf_idx] = res_q14[i] << 1; + ltp_buf_idx += 1; + } + } else { + res_q14[..sub].copy_from_slice(&exc[k * sub..(k + 1) * sub]); + } + for i in 0..sub { + let mut pred = (order >> 1) as i32; + for j in 0..order { + pred = smlawb(pred, s_lpc[MAX_LPC_ORDER + i - 1 - j], a_q12[j]); + } + let v = res_q14[i].saturating_add(pred.saturating_mul(16)); + s_lpc[MAX_LPC_ORDER + i] = v; + xq[k * sub + i] = sat16(rshift_round(smulww(v, gain_q10), 8)) as i16; + } + s_lpc.copy_within(sub..sub + MAX_LPC_ORDER, 0); + } + self.s_lpc_q14.copy_from_slice(&s_lpc[..MAX_LPC_ORDER]); + } + + /// Basic concealment: pitch-periodic repetition for voiced history, otherwise LPC-shaped + /// noise at the residual level of the history; both fade out over successive losses. + fn conceal(&mut self, out: &mut [i16]) { + let l = self.frame_length; + if l == 0 { + return; + } + self.loss_cnt += 1; + let order = self.lpc_order; + let mem = self.ltp_mem_length; + let fade = 0.85f32.powi(self.loss_cnt as i32); + let hist: Vec = self.out_buf[..mem].iter().map(|&v| v as f32).collect(); + let pitch = self.plc_pitch as usize; + let mut y = hist.clone(); + if pitch > 0 && pitch < mem { + for _ in 0..l { + let v = y[y.len() - pitch] * fade; + y.push(v); + } + } else { + let mut a12 = self.plc_lpc_q12; + bwexpander(&mut a12[..order], 64880); + self.plc_lpc_q12 = a12; + let a: Vec = a12[..order].iter().map(|&v| v as f32 / 4096.0).collect(); + let mut e = 0f32; + for i in order..mem { + let mut r = hist[i]; + for j in 0..order { + r -= a[j] * hist[i - 1 - j]; + } + e += r * r; + } + let rms = (e / (mem - order).max(1) as f32).sqrt() * fade; + for _ in 0..l { + self.plc_rand = self.plc_rand.wrapping_mul(196_314_165).wrapping_add(907_633_515); + let mut v = ((self.plc_rand as i32) >> 16) as f32 / 32768.0 * rms * 1.7; + for j in 0..order { + v += a[j] * y[y.len() - 1 - j]; + } + y.push(v.clamp(-32768.0, 32767.0)); + } + } + for (o, v) in out.iter_mut().zip(&y[mem..mem + l]) { + *o = v.clamp(-32768.0, 32767.0) as i16; + } + // Keep the LPC synthesis state consistent for the next decoded frame. + let g = self.prev_gain_q16.max(1) as i64; + for j in 0..MAX_LPC_ORDER { + let v = out[l - MAX_LPC_ORDER + j] as i64; + self.s_lpc_q14[j] = ((v << 30) / g).clamp(i32::MIN as i64, i32::MAX as i64) as i32; + } + self.lag_prev = self.plc_pitch.max(1); + self.first_frame_after_reset = false; + } +} + +#[derive(Default)] +struct Ctrl { + gains_q16: [i32; 4], + pred_coef_q12: [[i32; MAX_LPC_ORDER]; 2], + pitch_l: [i32; 4], + ltp_coef_q14: [i32; 4 * LTP_ORDER], + ltp_scale_q14: i32, +} + +fn log2lin(in_log_q7: i32) -> i32 { + if in_log_q7 < 0 { + return 0; + } + if in_log_q7 >= 3967 { + return i32::MAX; + } + let out = 1i32 << (in_log_q7 >> 7); + let frac = in_log_q7 & 0x7F; + if in_log_q7 < 2048 { + out + ((out * smlawb(frac, frac * (128 - frac), -174)) >> 7) + } else { + out + (out >> 7) * smlawb(frac, frac * (128 - frac), -174) + } +} + +fn lpc_analysis_filter(out: &mut [i16], input: &[i16], b: &[i32], d: usize) { + let len = out.len(); + for ix in d..len { + let mut acc: i32 = 0; + for j in 0..d { + acc = acc.wrapping_add((input[ix - 1 - j] as i32).wrapping_mul(b[j])); + } + let v = ((input[ix] as i32) << 12).wrapping_sub(acc); + out[ix] = sat16(rshift_round(v, 12)) as i16; + } + for v in out.iter_mut().take(d.min(len)) { + *v = 0; + } +} + +fn decode_pulses(dec: &mut RangeDecoder, pulses: &mut [i32], signal_type: usize, quant_offset_type: usize, frame_length: usize) { + let rate_level = dec.icdf(RATE_LEVEL[signal_type >> 1], 8); + let mut iter = frame_length >> 4; + if iter * 16 < frame_length { + iter += 1; + } + let mut sum_pulses = [0i32; 20]; + let mut n_lshifts = [0i32; 20]; + for i in 0..iter { + let mut s = dec.icdf(PULSE_COUNT[rate_level], 8) as i32; + while s == 17 { + n_lshifts[i] += 1; + let tab = if n_lshifts[i] == 10 { PULSE_COUNT[10] } else { PULSE_COUNT[9] }; + s = dec.icdf(tab, 8) as i32; + } + sum_pulses[i] = s; + } + for i in 0..iter { + let blk = &mut pulses[i * 16..(i + 1) * 16]; + if sum_pulses[i] > 0 { + shell_decode(dec, blk, sum_pulses[i]); + } else { + blk.fill(0); + } + } + for i in 0..iter { + let nls = n_lshifts[i]; + if nls > 0 { + for p in pulses[i * 16..(i + 1) * 16].iter_mut() { + let mut a = *p; + for _ in 0..nls { + a = (a << 1) + dec.icdf(&EXC_LSB, 8) as i32; + } + *p = a; + } + sum_pulses[i] |= nls << 5; + } + } + // Signs. + let base = 7 * (quant_offset_type + (signal_type << 1)); + let len = (frame_length + 8) >> 4; + for i in 0..len { + let p = sum_pulses[i]; + if p > 0 { + let tab = EXC_SIGN[base + ((p & 0x1F) as usize).min(6)]; + for q in pulses[i * 16..(i + 1) * 16].iter_mut() { + if *q > 0 && dec.icdf(tab, 8) == 0 { + *q = -*q; + } + } + } + } +} + +fn shell_decode(dec: &mut RangeDecoder, out: &mut [i32], total: i32) { + fn split(dec: &mut RangeDecoder, out: &mut [i32], n: usize, count: i32) { + if n == 1 { + out[0] = count; + return; + } + if count == 0 { + out[..n].fill(0); + return; + } + let tab = match n { + 16 => SHELL_16[(count - 1) as usize], + 8 => SHELL_8[(count - 1) as usize], + 4 => SHELL_4[(count - 1) as usize], + _ => SHELL_2[(count - 1) as usize], + }; + let left = dec.icdf(tab, 8) as i32; + let h = n / 2; + let (a, b) = out.split_at_mut(h); + split(dec, a, h, left); + split(dec, b, h, count - left); + } + split(dec, out, 16, total.min(16)); +} + +#[derive(Default, Clone)] +struct Stereo { + pred_prev_q13: [i32; 2], + s_mid: [i16; 2], + s_side: [i16; 2], +} + +fn stereo_decode_pred(dec: &mut RangeDecoder) -> [i32; 2] { + let n = dec.icdf(&STEREO_PRED_JOINT, 8); + let mut ix = [[0usize; 3]; 2]; + ix[0][2] = n / 5; + ix[1][2] = n - 5 * ix[0][2]; + for v in ix.iter_mut() { + v[0] = dec.icdf(&UNIFORM3, 8); + v[1] = dec.icdf(&UNIFORM5, 8); + } + let mut pred = [0i32; 2]; + for (p, v) in pred.iter_mut().zip(ix.iter_mut()) { + v[0] += 3 * v[2]; + let low = STEREO_WEIGHTS_Q13[v[0]]; + let step = smulwb(STEREO_WEIGHTS_Q13[v[0] + 1] - low, 6554); + *p = low + step * (2 * v[1] as i32 + 1); + } + pred[0] -= pred[1]; + pred +} + +fn stereo_ms_to_lr(st: &mut Stereo, x1: &mut [i16], x2: &mut [i16], pred_q13: [i32; 2], fs_khz: usize, len: usize) { + x1[..2].copy_from_slice(&st.s_mid); + x2[..2].copy_from_slice(&st.s_side); + st.s_mid.copy_from_slice(&x1[len..len + 2]); + st.s_side.copy_from_slice(&x2[len..len + 2]); + let mut p0 = st.pred_prev_q13[0]; + let mut p1 = st.pred_prev_q13[1]; + let denom_q16 = (1 << 16) / (8 * fs_khz as i32); + let d0 = rshift_round((pred_q13[0] - st.pred_prev_q13[0]) as i16 as i32 * denom_q16 as i16 as i32, 16); + let d1 = rshift_round((pred_q13[1] - st.pred_prev_q13[1]) as i16 as i32 * denom_q16 as i16 as i32, 16); + let interp = 8 * fs_khz; + for n in 0..len { + if n < interp { + p0 += d0; + p1 += d1; + } else if n == interp { + p0 = pred_q13[0]; + p1 = pred_q13[1]; + } + let sum = ((x1[n] as i32 + x1[n + 2] as i32) + ((x1[n + 1] as i32) << 1)) << 9; + let mut s = smlawb((x2[n + 1] as i32) << 8, sum, p0); + s = smlawb(s, (x1[n + 1] as i32) << 11, p1); + x2[n + 1] = sat16(rshift_round(s, 8)) as i16; + } + st.pred_prev_q13 = pred_q13; + for n in 0..len { + let a = x1[n + 1] as i32; + let b = x2[n + 1] as i32; + x1[n + 1] = sat16(a + b) as i16; + x2[n + 1] = sat16(a - b) as i16; + } +} + +/// SILK decoder for up to two internal channels (`silk_decoder`). +pub struct SilkDecoder { + api_rate: u32, + api_channels: usize, + ch: [Channel; 2], + stereo: Stereo, + channels_internal: usize, + channels_internal_prev: usize, + internal_rate: u32, + prev_decode_only_middle: bool, + ms_pred: [i32; 2], + decode_only_middle: bool, +} + +impl SilkDecoder { + pub fn new(api_rate: u32, api_channels: usize) -> SilkDecoder { + SilkDecoder { + api_rate, + api_channels, + ch: [Channel::new(), Channel::new()], + stereo: Stereo::default(), + channels_internal: api_channels, + channels_internal_prev: 0, + internal_rate: 16000, + prev_decode_only_middle: false, + ms_pred: [0; 2], + decode_only_middle: false, + } + } + + pub fn reset(&mut self) { + self.ch = [Channel::new(), Channel::new()]; + self.stereo = Stereo::default(); + self.prev_decode_only_middle = false; + self.channels_internal_prev = 0; + } + + pub fn set_stream(&mut self, channels: usize, internal_rate: u32) { + self.channels_internal = channels; + self.internal_rate = internal_rate; + } + + /// Decodes one SILK frame (10 or 20 ms) into interleaved `out` at the API rate. + pub fn decode( + &mut self, + mut dec: Option<&mut RangeDecoder>, + lost: LostFlag, + new_packet: bool, + payload_ms: usize, + out: &mut [f32], + ) -> Result { + let nci = self.channels_internal; + if new_packet { + for c in self.ch.iter_mut() { + c.n_frames_decoded = 0; + } + } + if nci > self.channels_internal_prev && self.channels_internal_prev != 0 { + self.ch[1] = Channel::new(); + } + let stereo_to_mono = nci == 1 && self.channels_internal_prev == 2 && self.internal_rate == 1000 * self.ch[0].fs_khz as u32; + if self.ch[0].n_frames_decoded == 0 { + for c in self.ch.iter_mut().take(nci) { + let (nfpp, nb) = match payload_ms { + 10 => (1, 2), + 20 => (1, 4), + 40 => (2, 4), + 60 => (3, 4), + _ => return Err(Error::InvalidPacket("bad SILK frame size")), + }; + c.n_frames_per_packet = nfpp; + c.nb_subfr = nb; + let fs_khz = ((self.internal_rate >> 10) + 1) as usize; + c.set_fs(fs_khz, self.api_rate); + } + } + if self.api_channels == 2 && nci == 2 && self.channels_internal_prev == 1 { + self.stereo.pred_prev_q13 = [0; 2]; + self.stereo.s_side = [0; 2]; + self.ch[1].resampler = self.ch[0].resampler.clone(); + } + self.channels_internal_prev = nci; + + if lost != LostFlag::Lost && self.ch[0].n_frames_decoded == 0 { + let d = dec.as_deref_mut().ok_or(Error::InvalidPacket("missing SILK data"))?; + for c in self.ch.iter_mut().take(nci) { + for i in 0..c.n_frames_per_packet { + c.vad_flags[i] = d.bit_logp(1); + } + c.lbrr_flag = d.bit_logp(1); + } + for c in self.ch.iter_mut().take(nci) { + c.lbrr_flags = [false; 3]; + if c.lbrr_flag { + if c.n_frames_per_packet == 1 { + c.lbrr_flags[0] = true; + } else { + let sym = d.icdf(LBRR_FLAGS[c.n_frames_per_packet - 2], 8) + 1; + for i in 0..c.n_frames_per_packet { + c.lbrr_flags[i] = (sym >> i) & 1 != 0; + } + } + } + } + if lost == LostFlag::Normal { + // Skip LBRR data. + for i in 0..self.ch[0].n_frames_per_packet { + for n in 0..nci { + if self.ch[n].lbrr_flags[i] { + if nci == 2 && n == 0 { + stereo_decode_pred(d); + if !self.ch[1].lbrr_flags[i] { + d.icdf(&MID_ONLY, 8); + } + } + let cond = if i > 0 && self.ch[n].lbrr_flags[i - 1] { Cond::Conditionally } else { Cond::Independently }; + let c = &mut self.ch[n]; + c.decode_indices(d, i, true, cond); + let mut pulses = [0i32; MAX_FRAME + 16]; + decode_pulses(d, &mut pulses, c.indices.signal_type, c.indices.quant_offset_type, c.frame_length); + } + } + } + } + } + // Stereo prediction. + if nci == 2 { + let fi = self.ch[0].n_frames_decoded; + let coded = lost == LostFlag::Normal || (lost == LostFlag::Lbrr && self.ch[0].lbrr_flags[fi]); + match dec.as_deref_mut() { + Some(d) if coded => { + self.ms_pred = stereo_decode_pred(d); + let side_inactive = + (lost == LostFlag::Normal && !self.ch[1].vad_flags[fi]) || (lost == LostFlag::Lbrr && !self.ch[1].lbrr_flags[fi]); + self.decode_only_middle = if side_inactive { d.icdf(&MID_ONLY, 8) == 1 } else { false }; + } + _ => { + self.ms_pred = self.stereo.pred_prev_q13; + } + } + } + if nci == 2 && !self.decode_only_middle && self.prev_decode_only_middle { + let c = &mut self.ch[1]; + c.out_buf = [0; OUT_BUF]; + c.s_lpc_q14 = [0; MAX_LPC_ORDER]; + c.lag_prev = 100; + c.last_gain_index = 10; + c.prev_signal_type = 0; + c.first_frame_after_reset = true; + } + let l = self.ch[0].frame_length; + let mut buf = [[0i16; MAX_FRAME + 2]; 2]; + let has_side = if lost == LostFlag::Normal { + !self.decode_only_middle + } else { + !self.prev_decode_only_middle || (nci == 2 && lost == LostFlag::Lbrr && self.ch[1].lbrr_flags[self.ch[1].n_frames_decoded]) + }; + for n in 0..nci { + if n == 0 || has_side { + let frame_index = self.ch[0].n_frames_decoded as isize - n as isize; + let cond = if frame_index <= 0 { + Cond::Independently + } else if lost == LostFlag::Lbrr { + if self.ch[n].lbrr_flags[frame_index as usize - 1] { Cond::Conditionally } else { Cond::Independently } + } else if n > 0 && self.prev_decode_only_middle { + Cond::IndependentlyNoLtpScaling + } else { + Cond::Conditionally + }; + let c = &mut self.ch[n]; + c.decode_frame(dec.as_deref_mut(), &mut buf[n][2..], lost, cond); + } else { + buf[n][2..2 + l].fill(0); + } + self.ch[n].n_frames_decoded += 1; + } + if self.api_channels == 2 && nci == 2 { + let (a, b) = buf.split_at_mut(1); + stereo_ms_to_lr(&mut self.stereo, &mut a[0], &mut b[0], self.ms_pred, self.ch[0].fs_khz, l); + } else { + buf[0][..2].copy_from_slice(&self.stereo.s_mid); + self.stereo.s_mid.copy_from_slice(&buf[0][l..l + 2]); + } + let n_out = l * self.api_rate as usize / (self.ch[0].fs_khz * 1000); + let ach = self.api_channels; + let mut tmp = Vec::with_capacity(n_out); + for n in 0..ach.min(nci) { + tmp.clear(); + self.ch[n].resampler.process(&buf[n][1..1 + l], &mut tmp); + for (i, &v) in tmp.iter().enumerate().take(n_out) { + out[i * ach + n] = v / 32768.0; + } + } + if ach == 2 && nci == 1 { + if stereo_to_mono { + tmp.clear(); + self.ch[1].resampler.process(&buf[0][1..1 + l], &mut tmp); + for (i, &v) in tmp.iter().enumerate().take(n_out) { + out[i * 2 + 1] = v / 32768.0; + } + } else { + for i in 0..n_out { + out[i * 2 + 1] = out[i * 2]; + } + } + } + if lost == LostFlag::Lost { + for c in self.ch.iter_mut().take(nci) { + c.last_gain_index = 10; + } + } else { + self.prev_decode_only_middle = self.decode_only_middle; + } + Ok(n_out) + } +} diff --git a/crates/opus/src/silk/resampler.rs b/crates/opus/src/silk/resampler.rs new file mode 100644 index 0000000..b8ae6f5 --- /dev/null +++ b/crates/opus/src/silk/resampler.rs @@ -0,0 +1,205 @@ +//! SILK output resampler (RFC 6716 §4.2.9: non-normative filter, normative delay budget). +//! +//! A windowed-sinc polyphase interpolator whose group delay matches the delay allocation of +//! RFC 6716 Table 54 (and the integer delays used when no rate conversion is needed), so that the +//! SILK output lines up with the CELT layer in Hybrid mode. + +#[derive(Clone)] +pub struct Resampler { + fin: u32, + fout: u32, + /// Pure-delay mode (equal rates): delay line. + copy_delay: usize, + /// Polyphase taps per output phase. + phases: Vec<(i64, Vec)>, + period: u64, + /// Total delay in units of 1/fout input samples. + delay_q: i64, + half: i64, + future: i64, + hist: Vec, + /// Absolute input index of `hist[0]`. + hist_start: i64, + n_in: i64, + n_out: u64, +} + +fn gcd(a: u32, b: u32) -> u32 { + if b == 0 { a } else { gcd(b, a % b) } +} + +/// Least-squares fractional-delay interpolator: taps at offsets `-past+1 ..= future` (in input +/// samples) approximating a delay of `frac` samples over `[0, wp]`, with zero response weighted +/// over `[ws, pi]` when `ws < pi` (decimation). +fn design_fractional(past: i64, future: i64, frac: f64, wp: f64, ws: f64) -> Vec { + let offs: Vec = ((-past + 1)..=future).map(|m| m as f64).collect(); + let n = offs.len(); + // \int_a^b cos(w x) dw + let ic = |x: f64, a: f64, b: f64| if x.abs() < 1e-12 { b - a } else { ((b * x).sin() - (a * x).sin()) / x }; + let stop_w = 30.0; + const LOW_W: f64 = 0.05; + let mut q = vec![vec![0f64; n + 1]; n]; + for i in 0..n { + for k in 0..n { + let d = offs[i] - offs[k]; + let mut v = ic(d, 0.0, 0.6 * wp) + LOW_W * ic(d, 0.6 * wp, wp); + if ws < std::f64::consts::PI { + v += stop_w * ic(d, ws, std::f64::consts::PI); + } + q[i][k] = v + if i == k { 1e-9 } else { 0.0 }; + } + q[i][n] = ic(offs[i] - frac, 0.0, 0.6 * wp) + LOW_W * ic(offs[i] - frac, 0.6 * wp, wp); + } + // Gaussian elimination with partial pivoting. + for c in 0..n { + let p = (c..n).max_by(|&a, &b| q[a][c].abs().total_cmp(&q[b][c].abs())).unwrap_or(c); + q.swap(c, p); + let piv = q[c][c]; + for r in 0..n { + if r != c { + let f = q[r][c] / piv; + if f != 0.0 { + for k in c..=n { + q[r][k] -= f * q[c][k]; + } + } + } + } + } + (0..n).map(|i| q[i][n] / q[i][i]).collect() +} + +impl Resampler { + pub fn new(fin: u32, fout: u32) -> Resampler { + let mut r = Resampler { + fin, + fout, + copy_delay: 0, + phases: Vec::new(), + period: 1, + delay_q: 0, + half: 0, + future: 0, + hist: Vec::new(), + hist_start: 0, + n_in: 0, + n_out: 0, + }; + if fin == fout { + r.copy_delay = match fin { + 8000 => 4, + 12000 => 9, + _ => 12, + }; + r.hist = vec![0.0; r.copy_delay]; + return r; + } + // Delay in milliseconds per internal bandwidth (RFC 6716 Table 54). + let delay_ms = match fin { + 8000 => 0.538, + 12000 => 0.692, + _ => 0.706, + }; + let d_in = delay_ms * fin as f64 / 1000.0; // input samples + // Future taps are limited by the delay budget; past taps are free. + let future = (d_in.floor() as i64).max(1); + let past = 24i64; + r.half = past; + r.future = future; + r.delay_q = (d_in * fout as f64).round() as i64; + let g = gcd(fin, fout); + r.period = (fout / g) as u64; + let pi = std::f64::consts::PI; + // Passband edge and (for decimation) stopband edge in radians per input sample. + let (wp, ws) = if fout > fin { (0.94 * pi, pi) } else { (0.88 * pi * fout as f64 / fin as f64, pi * fout as f64 / fin as f64) }; + for j in 0..r.period as i64 { + let num = j * fin as i64 - r.delay_q; + let base = num.div_euclid(fout as i64); + let frac = num.rem_euclid(fout as i64) as f64 / fout as f64; + let taps = design_fractional(past, future, frac, wp, ws); + r.phases.push((base, taps.iter().map(|&t| t as f32).collect())); + } + r.hist = vec![0.0; (past + future + 4) as usize]; + r.hist_start = -(past + future + 4); + r + } + + pub fn rates(&self) -> (u32, u32) { + (self.fin, self.fout) + } + + /// Resamples `input` (16-bit scale) and appends the output samples. + pub fn process(&mut self, input: &[i16], out: &mut Vec) { + if self.fin == self.fout { + for &v in input { + self.hist.push(v as f32); + } + let n = self.hist.len() - self.copy_delay; + out.extend_from_slice(&self.hist[..n]); + self.hist.drain(..n); + return; + } + self.hist.extend(input.iter().map(|&v| v as f32)); + self.n_in += input.len() as i64; + let total_out = (self.n_in as u64 * self.fout as u64) / self.fin as u64; + while self.n_out < total_out { + let j = self.n_out; + let cycle = (j / self.period) as i64; + let (base0, ref taps) = self.phases[(j % self.period) as usize]; + let base = base0 + cycle * self.fin as i64 * self.period as i64 / self.fout as i64; + let first = base - self.half + 1; + let mut acc = 0f32; + for (m, &t) in taps.iter().enumerate() { + let idx = first + m as i64 - self.hist_start; + if idx >= 0 && (idx as usize) < self.hist.len() { + acc += self.hist[idx as usize] * t; + } + } + out.push(acc); + self.n_out += 1; + } + // Trim history, keeping enough for the next taps. + let keep_from = self.n_in - (self.half + self.future + 4); + if keep_from > self.hist_start { + let drop = (keep_from - self.hist_start) as usize; + self.hist.drain(..drop.min(self.hist.len())); + self.hist_start = keep_from; + } + } +} + +#[cfg(test)] +mod tests { + use super::*; + + #[test] + fn upsample_sine_and_delay() { + for &(fin, fout) in &[(16000u32, 48000u32), (8000, 48000), (12000, 48000), (16000, 24000), (16000, 8000), (12000, 16000)] { + let mut r = Resampler::new(fin, fout); + let f = 440.0; + let n = fin as usize / 10; + let x: Vec = (0..n).map(|i| ((2.0 * std::f64::consts::PI * f * i as f64 / fin as f64).sin() * 10000.0) as i16).collect(); + let mut y = Vec::new(); + for c in x.chunks(fin as usize / 50) { + r.process(c, &mut y); + } + assert_eq!(y.len(), n * fout as usize / fin as usize); + // Compare against the ideal delayed sine. + let delay_ms = match fin { + 8000 => 0.538, + 12000 => 0.692, + _ => 0.706, + }; + let mut err = 0f64; + let mut sig = 0f64; + for (j, &v) in y.iter().enumerate().skip(fout as usize / 100) { + let t = j as f64 / fout as f64 - delay_ms / 1000.0; + let ideal = (2.0 * std::f64::consts::PI * f * t).sin() * 10000.0; + err += (v as f64 - ideal).powi(2); + sig += ideal * ideal; + } + let snr = 10.0 * (sig / err).log10(); + assert!(snr > 35.0, "{fin}->{fout}: {snr} dB"); + } + } +} diff --git a/crates/opus/src/silk/tables.rs b/crates/opus/src/silk/tables.rs new file mode 100644 index 0000000..a4e4e27 --- /dev/null +++ b/crates/opus/src/silk/tables.rs @@ -0,0 +1,540 @@ +//! SILK probability tables and codebooks, transcribed from RFC 6716 §4.2 (Tables 4–53). +//! +//! PDFs are written as in the RFC (`{...}/256`) and converted at compile time to the inverse-CDF +//! form consumed by the range decoder. + +/// Converts a PDF (summing to 256) into an inverse CDF (`256 - cdf[i+1]`). +pub const fn icdf(pdf: &[u8; N]) -> [u8; N] { + let mut out = [0u8; N]; + let mut acc: u32 = 0; + let mut i = 0; + while i < N { + acc += pdf[i] as u32; + out[i] = (256 - acc) as u8; + i += 1; + } + out +} + +pub const STEREO_PRED_JOINT: [u8; 25] = icdf(&[7, 2, 1, 1, 1, 10, 24, 8, 1, 1, 3, 23, 92, 23, 3, 1, 1, 8, 24, 10, 1, 1, 1, 2, 7]); +pub const UNIFORM3: [u8; 3] = icdf(&[85, 86, 85]); +pub const UNIFORM5: [u8; 5] = icdf(&[51, 51, 52, 51, 51]); +pub const STEREO_WEIGHTS_Q13: [i32; 16] = [-13732, -10050, -8266, -7526, -6500, -5000, -2950, -820, 820, 2950, 5000, 6500, 7526, 8266, 10050, 13732]; +pub const MID_ONLY: [u8; 2] = icdf(&[192, 64]); +pub const TYPE_INACTIVE: [u8; 2] = icdf(&[26, 230]); +pub const TYPE_ACTIVE: [u8; 4] = icdf(&[24, 74, 148, 10]); +pub const GAIN_MSB: [&[u8]; 3] = + [&icdf::<8>(&[32, 112, 68, 29, 12, 1, 1, 1]), &icdf::<8>(&[2, 17, 45, 60, 62, 47, 19, 4]), &icdf::<8>(&[1, 3, 26, 71, 94, 50, 9, 2])]; +pub const UNIFORM8: [u8; 8] = icdf(&[32, 32, 32, 32, 32, 32, 32, 32]); +pub const DELTA_GAIN: [u8; 41] = + icdf(&[6, 5, 11, 31, 132, 21, 8, 4, 3, 2, 2, 2, 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, 1]); +pub const NLSF_STAGE1: [&[u8]; 4] = [ + &icdf::<32>(&[44, 34, 30, 19, 21, 12, 11, 3, 3, 2, 16, 2, 2, 1, 5, 2, 1, 3, 3, 1, 1, 2, 2, 2, 3, 1, 9, 9, 2, 7, 2, 1]), + &icdf::<32>(&[1, 10, 1, 8, 3, 8, 8, 14, 13, 14, 1, 14, 12, 13, 11, 11, 12, 11, 10, 10, 11, 8, 9, 8, 7, 8, 1, 1, 6, 1, 6, 5]), + &icdf::<32>(&[31, 21, 3, 17, 1, 8, 17, 4, 1, 18, 16, 4, 2, 3, 1, 10, 1, 3, 16, 11, 16, 2, 2, 3, 2, 11, 1, 4, 9, 8, 7, 3]), + &icdf::<32>(&[1, 4, 16, 5, 18, 11, 5, 14, 15, 1, 3, 12, 13, 14, 14, 6, 14, 12, 2, 6, 1, 12, 12, 11, 10, 3, 10, 5, 1, 1, 1, 3]), +]; +pub const NLSF_STAGE2: [&[u8]; 16] = [ + &icdf::<9>(&[1, 1, 1, 15, 224, 11, 1, 1, 1]), + &icdf::<9>(&[1, 1, 2, 34, 183, 32, 1, 1, 1]), + &icdf::<9>(&[1, 1, 4, 42, 149, 55, 2, 1, 1]), + &icdf::<9>(&[1, 1, 8, 52, 123, 61, 8, 1, 1]), + &icdf::<9>(&[1, 3, 16, 53, 101, 74, 6, 1, 1]), + &icdf::<9>(&[1, 3, 17, 55, 90, 73, 15, 1, 1]), + &icdf::<9>(&[1, 7, 24, 53, 74, 67, 26, 3, 1]), + &icdf::<9>(&[1, 1, 18, 63, 78, 58, 30, 6, 1]), + &icdf::<9>(&[1, 1, 1, 9, 232, 9, 1, 1, 1]), + &icdf::<9>(&[1, 1, 2, 28, 186, 35, 1, 1, 1]), + &icdf::<9>(&[1, 1, 3, 42, 152, 53, 2, 1, 1]), + &icdf::<9>(&[1, 1, 10, 49, 126, 65, 2, 1, 1]), + &icdf::<9>(&[1, 4, 19, 48, 100, 77, 5, 1, 1]), + &icdf::<9>(&[1, 1, 14, 54, 100, 72, 12, 1, 1]), + &icdf::<9>(&[1, 1, 15, 61, 87, 61, 25, 4, 1]), + &icdf::<9>(&[1, 7, 21, 50, 77, 81, 17, 1, 1]), +]; +pub const NLSF_SEL_NBMB: [[u8; 10]; 32] = [ + [0, 0, 0, 0, 0, 0, 0, 0, 0, 0], + [1, 3, 1, 2, 2, 1, 2, 1, 1, 1], + [2, 1, 1, 1, 1, 1, 1, 1, 1, 1], + [1, 2, 2, 2, 2, 1, 2, 1, 1, 1], + [2, 3, 3, 3, 3, 2, 2, 2, 2, 2], + [0, 5, 3, 3, 2, 2, 2, 2, 1, 1], + [0, 2, 2, 2, 2, 2, 2, 2, 2, 1], + [2, 3, 6, 4, 4, 4, 5, 4, 5, 5], + [2, 4, 5, 5, 4, 5, 4, 6, 4, 4], + [2, 4, 4, 7, 4, 5, 4, 5, 5, 4], + [4, 3, 3, 3, 2, 3, 2, 2, 2, 2], + [1, 5, 5, 6, 4, 5, 4, 5, 5, 5], + [2, 7, 4, 6, 5, 5, 5, 5, 5, 5], + [2, 7, 5, 5, 5, 5, 5, 6, 5, 4], + [3, 3, 5, 4, 4, 5, 4, 5, 4, 4], + [2, 3, 3, 5, 5, 4, 4, 4, 4, 4], + [2, 4, 4, 6, 4, 5, 4, 5, 5, 5], + [2, 5, 4, 6, 5, 5, 5, 4, 5, 4], + [2, 7, 4, 5, 4, 5, 4, 5, 5, 5], + [2, 5, 4, 6, 7, 6, 5, 6, 5, 4], + [3, 6, 7, 4, 6, 5, 5, 6, 4, 5], + [2, 7, 6, 4, 4, 4, 5, 4, 5, 5], + [4, 5, 5, 4, 6, 6, 5, 6, 5, 4], + [2, 5, 5, 6, 5, 6, 4, 6, 4, 4], + [4, 5, 5, 5, 3, 7, 4, 5, 5, 4], + [2, 3, 4, 5, 5, 6, 4, 5, 5, 4], + [2, 3, 2, 3, 3, 4, 2, 3, 3, 3], + [1, 1, 2, 2, 2, 2, 2, 3, 2, 2], + [4, 5, 5, 6, 6, 6, 5, 6, 4, 5], + [3, 5, 5, 4, 4, 4, 4, 3, 3, 2], + [2, 5, 3, 7, 5, 5, 4, 4, 5, 4], + [4, 4, 5, 4, 5, 6, 5, 6, 5, 4], +]; +pub const NLSF_SEL_WB: [[u8; 16]; 32] = [ + [0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0], + [2, 3, 3, 3, 3, 3, 2, 2, 2, 2, 2, 1, 1, 1, 0, 3], + [2, 5, 5, 3, 7, 4, 4, 5, 2, 5, 4, 5, 5, 4, 3, 3], + [0, 2, 1, 2, 2, 1, 1, 1, 1, 1, 0, 0, 0, 0, 0, 1], + [0, 6, 5, 4, 6, 4, 7, 5, 4, 4, 4, 5, 5, 4, 4, 3], + [0, 3, 5, 5, 4, 3, 3, 5, 3, 3, 3, 3, 3, 3, 2, 4], + [0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0], + [0, 2, 6, 3, 7, 2, 5, 3, 4, 5, 5, 4, 3, 3, 2, 3], + [0, 6, 2, 6, 6, 4, 5, 4, 6, 5, 4, 4, 5, 3, 3, 3], + [2, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0], + [0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1], + [2, 2, 3, 4, 5, 3, 3, 3, 3, 3, 3, 3, 2, 2, 1, 3], + [2, 2, 3, 3, 4, 3, 3, 3, 3, 3, 3, 3, 3, 2, 1, 3], + [3, 4, 4, 4, 6, 4, 4, 5, 3, 5, 4, 4, 5, 4, 3, 4], + [0, 6, 4, 5, 4, 7, 5, 2, 6, 5, 7, 4, 4, 3, 5, 3], + [0, 1, 0, 1, 1, 1, 1, 1, 1, 1, 0, 0, 0, 0, 1, 0], + [1, 6, 5, 7, 5, 4, 5, 3, 4, 5, 4, 4, 4, 3, 3, 4], + [1, 3, 3, 4, 4, 3, 3, 5, 2, 3, 3, 5, 5, 5, 3, 4], + [2, 3, 3, 2, 2, 2, 3, 2, 1, 2, 1, 2, 1, 1, 1, 4], + [0, 2, 3, 5, 3, 3, 2, 2, 2, 1, 1, 0, 0, 0, 0, 0], + [3, 4, 3, 5, 3, 3, 2, 2, 1, 1, 1, 1, 1, 2, 2, 4], + [2, 6, 3, 7, 7, 4, 5, 4, 5, 3, 5, 3, 3, 2, 3, 3], + [2, 3, 5, 6, 6, 3, 5, 3, 4, 4, 3, 3, 3, 3, 2, 4], + [1, 3, 3, 4, 4, 4, 4, 3, 5, 5, 5, 3, 1, 1, 1, 1], + [2, 5, 3, 6, 6, 4, 7, 4, 4, 5, 3, 4, 4, 3, 3, 3], + [0, 6, 1, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0], + [0, 6, 6, 3, 5, 2, 5, 5, 3, 4, 4, 7, 7, 4, 4, 4], + [3, 3, 7, 3, 5, 4, 3, 3, 3, 2, 2, 3, 3, 3, 2, 3], + [0, 0, 1, 0, 0, 0, 2, 1, 2, 1, 1, 2, 2, 2, 1, 1], + [0, 3, 2, 5, 3, 3, 2, 3, 2, 1, 0, 0, 1, 0, 0, 1], + [3, 5, 5, 4, 7, 5, 3, 3, 2, 3, 2, 2, 1, 0, 1, 0], + [2, 3, 5, 3, 4, 3, 3, 3, 2, 1, 2, 6, 4, 0, 0, 0], +]; +pub const NLSF_EXT: [u8; 7] = icdf(&[156, 60, 24, 9, 4, 2, 1]); +pub const NLSF_PRED_NBMB: [[i32; 9]; 2] = [[179, 138, 140, 148, 151, 149, 153, 151, 163], [116, 67, 82, 59, 92, 72, 100, 89, 92]]; +pub const NLSF_PRED_WB: [[i32; 15]; 2] = [ + [175, 148, 160, 176, 178, 173, 174, 164, 177, 174, 196, 182, 198, 192, 182], + [68, 62, 66, 60, 72, 117, 85, 90, 118, 136, 151, 142, 160, 142, 155], +]; +pub const NLSF_PSEL_NBMB: [[u8; 9]; 32] = [ + [0, 1, 0, 0, 0, 0, 0, 0, 0], + [1, 0, 0, 0, 0, 0, 0, 0, 0], + [0, 0, 0, 0, 0, 0, 0, 0, 0], + [1, 1, 1, 0, 0, 0, 0, 1, 0], + [0, 1, 0, 0, 0, 0, 0, 0, 0], + [0, 1, 0, 0, 0, 0, 0, 0, 0], + [1, 0, 1, 1, 0, 0, 0, 1, 0], + [0, 1, 1, 0, 0, 1, 1, 0, 0], + [0, 0, 1, 1, 0, 1, 0, 1, 1], + [0, 0, 1, 1, 0, 0, 1, 1, 1], + [0, 0, 0, 0, 0, 0, 0, 0, 0], + [0, 1, 0, 1, 1, 1, 1, 1, 0], + [0, 1, 0, 1, 1, 1, 1, 1, 0], + [0, 1, 1, 1, 1, 1, 1, 1, 0], + [1, 0, 1, 1, 0, 1, 1, 1, 1], + [0, 1, 1, 1, 1, 1, 0, 1, 0], + [0, 0, 1, 1, 0, 1, 0, 1, 0], + [0, 0, 1, 1, 1, 0, 1, 1, 1], + [0, 1, 1, 0, 0, 1, 1, 1, 0], + [0, 0, 0, 1, 1, 1, 0, 1, 0], + [0, 1, 1, 0, 0, 1, 0, 1, 0], + [0, 1, 1, 0, 0, 0, 1, 1, 0], + [0, 0, 0, 0, 0, 1, 1, 1, 1], + [0, 0, 1, 1, 0, 0, 0, 1, 1], + [0, 0, 0, 1, 0, 1, 1, 1, 1], + [0, 1, 1, 1, 1, 1, 1, 1, 0], + [0, 0, 0, 0, 0, 0, 0, 0, 0], + [0, 0, 0, 0, 0, 0, 0, 0, 0], + [0, 0, 1, 0, 1, 1, 0, 1, 0], + [1, 0, 0, 1, 0, 0, 0, 0, 0], + [0, 0, 0, 1, 1, 0, 1, 0, 1], + [1, 0, 1, 1, 0, 1, 1, 1, 1], +]; +pub const NLSF_PSEL_WB: [[u8; 15]; 32] = [ + [0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1], + [0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0], + [0, 0, 1, 0, 0, 1, 1, 1, 0, 1, 1, 1, 1, 0, 0], + [0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0], + [0, 1, 1, 0, 1, 0, 1, 1, 0, 1, 1, 1, 1, 1, 0], + [0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0], + [1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 1, 0], + [0, 1, 1, 0, 0, 0, 1, 0, 1, 1, 1, 0, 1, 0, 1], + [0, 1, 0, 1, 1, 0, 1, 0, 1, 0, 1, 1, 1, 1, 1], + [0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1], + [0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0], + [0, 0, 1, 0, 1, 1, 1, 1, 1, 1, 1, 0, 1, 0, 0], + [0, 0, 1, 0, 0, 1, 0, 1, 0, 1, 0, 0, 1, 0, 0], + [0, 0, 0, 0, 1, 1, 0, 1, 0, 1, 1, 1, 1, 0, 0], + [0, 1, 0, 0, 0, 1, 1, 0, 1, 1, 1, 0, 1, 1, 1], + [0, 0, 1, 1, 0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 0], + [0, 1, 1, 0, 1, 0, 1, 1, 1, 1, 1, 0, 1, 0, 0], + [0, 0, 1, 0, 0, 0, 0, 1, 0, 0, 1, 1, 1, 0, 0], + [0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1], + [0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0], + [0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0], + [0, 1, 0, 1, 0, 1, 1, 0, 1, 0, 1, 0, 1, 1, 0], + [0, 0, 1, 1, 1, 1, 0, 1, 1, 0, 0, 1, 1, 0, 0], + [0, 1, 1, 0, 1, 0, 1, 0, 1, 0, 0, 0, 0, 1, 0], + [0, 0, 0, 1, 1, 0, 1, 0, 1, 1, 1, 1, 1, 1, 1], + [0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1], + [0, 1, 1, 0, 0, 0, 1, 1, 0, 0, 1, 1, 1, 1, 1], + [0, 0, 0, 0, 0, 1, 0, 1, 1, 1, 1, 0, 1, 1, 1], + [0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1], + [0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1], + [1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0], + [0, 0, 1, 0, 0, 1, 1, 1, 0, 0, 1, 0, 0, 1, 0], +]; +pub const NLSF_CB_NBMB: [[i32; 10]; 32] = [ + [12, 35, 60, 83, 108, 132, 157, 180, 206, 228], + [15, 32, 55, 77, 101, 125, 151, 175, 201, 225], + [19, 42, 66, 89, 114, 137, 162, 184, 209, 230], + [12, 25, 50, 72, 97, 120, 147, 172, 200, 223], + [26, 44, 69, 90, 114, 135, 159, 180, 205, 225], + [13, 22, 53, 80, 106, 130, 156, 180, 205, 228], + [15, 25, 44, 64, 90, 115, 142, 168, 196, 222], + [19, 24, 62, 82, 100, 120, 145, 168, 190, 214], + [22, 31, 50, 79, 103, 120, 151, 170, 203, 227], + [21, 29, 45, 65, 106, 124, 150, 171, 196, 224], + [30, 49, 75, 97, 121, 142, 165, 186, 209, 229], + [19, 25, 52, 70, 93, 116, 143, 166, 192, 219], + [26, 34, 62, 75, 97, 118, 145, 167, 194, 217], + [25, 33, 56, 70, 91, 113, 143, 165, 196, 223], + [21, 34, 51, 72, 97, 117, 145, 171, 196, 222], + [20, 29, 50, 67, 90, 117, 144, 168, 197, 221], + [22, 31, 48, 66, 95, 117, 146, 168, 196, 222], + [24, 33, 51, 77, 116, 134, 158, 180, 200, 224], + [21, 28, 70, 87, 106, 124, 149, 170, 194, 217], + [26, 33, 53, 64, 83, 117, 152, 173, 204, 225], + [27, 34, 65, 95, 108, 129, 155, 174, 210, 225], + [20, 26, 72, 99, 113, 131, 154, 176, 200, 219], + [34, 43, 61, 78, 93, 114, 155, 177, 205, 229], + [23, 29, 54, 97, 124, 138, 163, 179, 209, 229], + [30, 38, 56, 89, 118, 129, 158, 178, 200, 231], + [21, 29, 49, 63, 85, 111, 142, 163, 193, 222], + [27, 48, 77, 103, 133, 158, 179, 196, 215, 232], + [29, 47, 74, 99, 124, 151, 176, 198, 220, 237], + [33, 42, 61, 76, 93, 121, 155, 174, 207, 225], + [29, 53, 87, 112, 136, 154, 170, 188, 208, 227], + [24, 30, 52, 84, 131, 150, 166, 186, 203, 229], + [37, 48, 64, 84, 104, 118, 156, 177, 201, 230], +]; +pub const NLSF_CB_WB: [[i32; 16]; 32] = [ + [7, 23, 38, 54, 69, 85, 100, 116, 131, 147, 162, 178, 193, 208, 223, 239], + [13, 25, 41, 55, 69, 83, 98, 112, 127, 142, 157, 171, 187, 203, 220, 236], + [15, 21, 34, 51, 61, 78, 92, 106, 126, 136, 152, 167, 185, 205, 225, 240], + [10, 21, 36, 50, 63, 79, 95, 110, 126, 141, 157, 173, 189, 205, 221, 237], + [17, 20, 37, 51, 59, 78, 89, 107, 123, 134, 150, 164, 184, 205, 224, 240], + [10, 15, 32, 51, 67, 81, 96, 112, 129, 142, 158, 173, 189, 204, 220, 236], + [8, 21, 37, 51, 65, 79, 98, 113, 126, 138, 155, 168, 179, 192, 209, 218], + [12, 15, 34, 55, 63, 78, 87, 108, 118, 131, 148, 167, 185, 203, 219, 236], + [16, 19, 32, 36, 56, 79, 91, 108, 118, 136, 154, 171, 186, 204, 220, 237], + [11, 28, 43, 58, 74, 89, 105, 120, 135, 150, 165, 180, 196, 211, 226, 241], + [6, 16, 33, 46, 60, 75, 92, 107, 123, 137, 156, 169, 185, 199, 214, 225], + [11, 19, 30, 44, 57, 74, 89, 105, 121, 135, 152, 169, 186, 202, 218, 234], + [12, 19, 29, 46, 57, 71, 88, 100, 120, 132, 148, 165, 182, 199, 216, 233], + [17, 23, 35, 46, 56, 77, 92, 106, 123, 134, 152, 167, 185, 204, 222, 237], + [14, 17, 45, 53, 63, 75, 89, 107, 115, 132, 151, 171, 188, 206, 221, 240], + [9, 16, 29, 40, 56, 71, 88, 103, 119, 137, 154, 171, 189, 205, 222, 237], + [16, 19, 36, 48, 57, 76, 87, 105, 118, 132, 150, 167, 185, 202, 218, 236], + [12, 17, 29, 54, 71, 81, 94, 104, 126, 136, 149, 164, 182, 201, 221, 237], + [15, 28, 47, 62, 79, 97, 115, 129, 142, 155, 168, 180, 194, 208, 223, 238], + [8, 14, 30, 45, 62, 78, 94, 111, 127, 143, 159, 175, 192, 207, 223, 239], + [17, 30, 49, 62, 79, 92, 107, 119, 132, 145, 160, 174, 190, 204, 220, 235], + [14, 19, 36, 45, 61, 76, 91, 108, 121, 138, 154, 172, 189, 205, 222, 238], + [12, 18, 31, 45, 60, 76, 91, 107, 123, 138, 154, 171, 187, 204, 221, 236], + [13, 17, 31, 43, 53, 70, 83, 103, 114, 131, 149, 167, 185, 203, 220, 237], + [17, 22, 35, 42, 58, 78, 93, 110, 125, 139, 155, 170, 188, 206, 224, 240], + [8, 15, 34, 50, 67, 83, 99, 115, 131, 146, 162, 178, 193, 209, 224, 239], + [13, 16, 41, 66, 73, 86, 95, 111, 128, 137, 150, 163, 183, 206, 225, 241], + [17, 25, 37, 52, 63, 75, 92, 102, 119, 132, 144, 160, 175, 191, 212, 231], + [19, 31, 49, 65, 83, 100, 117, 133, 147, 161, 174, 187, 200, 213, 227, 242], + [18, 31, 52, 68, 88, 103, 117, 126, 138, 149, 163, 177, 192, 207, 223, 239], + [16, 29, 47, 61, 76, 90, 106, 119, 133, 147, 161, 176, 193, 209, 224, 240], + [15, 21, 35, 50, 61, 73, 86, 97, 110, 119, 129, 141, 175, 198, 218, 237], +]; +pub const NLSF_MIN_SPACING_NBMB: [i32; 11] = [250, 3, 6, 3, 3, 3, 4, 3, 3, 3, 461]; +pub const NLSF_MIN_SPACING_WB: [i32; 17] = [100, 3, 40, 3, 3, 3, 5, 14, 14, 10, 11, 3, 8, 9, 7, 3, 347]; +pub const NLSF_INTERP: [u8; 5] = icdf(&[13, 22, 29, 11, 181]); +pub const LSF_ORDER_NBMB: [usize; 10] = [0, 9, 6, 3, 4, 5, 8, 1, 2, 7]; +pub const LSF_ORDER_WB: [usize; 16] = [0, 15, 8, 7, 4, 11, 12, 3, 2, 13, 10, 5, 6, 9, 14, 1]; +pub const COS_Q12: [i32; 129] = [ + 4096, 4095, 4091, 4085, 4076, 4065, 4052, 4036, 4017, 3997, 3973, 3948, 3920, 3889, 3857, 3822, 3784, 3745, 3703, 3659, 3613, 3564, 3513, 3461, + 3406, 3349, 3290, 3229, 3166, 3102, 3035, 2967, 2896, 2824, 2751, 2676, 2599, 2520, 2440, 2359, 2276, 2191, 2106, 2019, 1931, 1842, 1751, 1660, + 1568, 1474, 1380, 1285, 1189, 1093, 995, 897, 799, 700, 601, 501, 401, 301, 201, 101, 0, -101, -201, -301, -401, -501, -601, -700, -799, -897, + -995, -1093, -1189, -1285, -1380, -1474, -1568, -1660, -1751, -1842, -1931, -2019, -2106, -2191, -2276, -2359, -2440, -2520, -2599, -2676, -2751, + -2824, -2896, -2967, -3035, -3102, -3166, -3229, -3290, -3349, -3406, -3461, -3513, -3564, -3613, -3659, -3703, -3745, -3784, -3822, -3857, + -3889, -3920, -3948, -3973, -3997, -4017, -4036, -4052, -4065, -4076, -4085, -4091, -4095, -4096, +]; +pub const PITCH_HIGH: [u8; 32] = icdf(&[3, 3, 6, 11, 21, 30, 32, 19, 11, 10, 12, 13, 13, 12, 11, 9, 8, 7, 6, 4, 2, 2, 2, 1, 1, 1, 1, 1, 1, 1, 1, 1]); +pub const PITCH_LOW: [&[u8]; 3] = + [&icdf::<4>(&[64, 64, 64, 64]), &icdf::<6>(&[43, 42, 43, 43, 42, 43]), &icdf::<8>(&[32, 32, 32, 32, 32, 32, 32, 32])]; +pub const PITCH_DELTA: [u8; 21] = icdf(&[46, 2, 2, 3, 4, 6, 10, 15, 26, 38, 30, 22, 15, 10, 7, 6, 4, 4, 2, 2, 2]); +pub const PITCH_CONTOUR: [&[u8]; 4] = [ + &icdf::<3>(&[143, 50, 63]), + &icdf::<11>(&[68, 12, 21, 17, 19, 22, 30, 24, 17, 16, 10]), + &icdf::<12>(&[91, 46, 39, 19, 14, 12, 8, 7, 6, 5, 5, 4]), + &icdf::<34>(&[33, 22, 18, 16, 15, 14, 14, 13, 13, 10, 9, 9, 8, 6, 6, 6, 5, 4, 4, 4, 3, 3, 3, 2, 2, 2, 2, 2, 2, 2, 1, 1, 1, 1]), +]; +pub const CB_NB_10: [[i32; 2]; 3] = [[0, 0], [1, 0], [0, 1]]; +pub const CB_NB_20: [[i32; 4]; 11] = [ + [0, 0, 0, 0], + [2, 1, 0, -1], + [-1, 0, 1, 2], + [-1, 0, 0, 1], + [-1, 0, 0, 0], + [0, 0, 0, 1], + [0, 0, 1, 1], + [1, 1, 0, 0], + [1, 0, 0, 0], + [0, 0, 0, -1], + [1, 0, 0, -1], +]; +pub const CB_WB_10: [[i32; 2]; 12] = [[0, 0], [0, 1], [1, 0], [-1, 1], [1, -1], [-1, 2], [2, -1], [-2, 2], [2, -2], [-2, 3], [3, -2], [-3, 3]]; +pub const CB_WB_20: [[i32; 4]; 34] = [ + [0, 0, 0, 0], + [0, 0, 1, 1], + [1, 1, 0, 0], + [-1, 0, 0, 0], + [0, 0, 0, 1], + [1, 0, 0, 0], + [-1, 0, 0, 1], + [0, 0, 0, -1], + [-1, 0, 1, 2], + [1, 0, 0, -1], + [-2, -1, 1, 2], + [2, 1, 0, -1], + [-2, 0, 0, 2], + [-2, 0, 1, 3], + [2, 1, -1, -2], + [-3, -1, 1, 3], + [2, 0, 0, -2], + [3, 1, 0, -2], + [-3, -1, 2, 4], + [-4, -1, 1, 4], + [3, 1, -1, -3], + [-4, -1, 2, 5], + [4, 2, -1, -3], + [4, 1, -1, -4], + [-5, -1, 2, 6], + [5, 2, -1, -4], + [-6, -2, 2, 6], + [-5, -2, 2, 5], + [6, 2, -1, -5], + [-7, -2, 3, 8], + [6, 2, -2, -6], + [5, 2, -2, -5], + [8, 3, -2, -7], + [-9, -3, 3, 9], +]; +pub const LTP_PERIODICITY: [u8; 3] = icdf(&[77, 80, 99]); +pub const LTP_FILTER: [&[u8]; 3] = [ + &icdf::<8>(&[185, 15, 13, 13, 9, 9, 6, 6]), + &icdf::<16>(&[57, 34, 21, 20, 15, 13, 12, 13, 10, 10, 9, 10, 9, 8, 7, 8]), + &icdf::<32>(&[15, 16, 14, 12, 12, 12, 11, 11, 11, 10, 9, 9, 9, 9, 8, 8, 8, 8, 7, 7, 6, 6, 5, 4, 5, 4, 4, 4, 3, 4, 3, 2]), +]; +pub const LTP_TAPS_0: [[i32; 5]; 8] = [ + [4, 6, 24, 7, 5], + [0, 0, 2, 0, 0], + [12, 28, 41, 13, -4], + [-9, 15, 42, 25, 14], + [1, -2, 62, 41, -9], + [-10, 37, 65, -4, 3], + [-6, 4, 66, 7, -8], + [16, 14, 38, -3, 33], +]; +pub const LTP_TAPS_1: [[i32; 5]; 16] = [ + [13, 22, 39, 23, 12], + [-1, 36, 64, 27, -6], + [-7, 10, 55, 43, 17], + [1, 1, 8, 1, 1], + [6, -11, 74, 53, -9], + [-12, 55, 76, -12, 8], + [-3, 3, 93, 27, -4], + [26, 39, 59, 3, -8], + [2, 0, 77, 11, 9], + [-8, 22, 44, -6, 7], + [40, 9, 26, 3, 9], + [-7, 20, 101, -7, 4], + [3, -8, 42, 26, 0], + [-15, 33, 68, 2, 23], + [-2, 55, 46, -2, 15], + [3, -1, 21, 16, 41], +]; +pub const LTP_TAPS_2: [[i32; 5]; 32] = [ + [-6, 27, 61, 39, 5], + [-11, 42, 88, 4, 1], + [-2, 60, 65, 6, -4], + [-1, -5, 73, 56, 1], + [-9, 19, 94, 29, -9], + [0, 12, 99, 6, 4], + [8, -19, 102, 46, -13], + [3, 2, 13, 3, 2], + [9, -21, 84, 72, -18], + [-11, 46, 104, -22, 8], + [18, 38, 48, 23, 0], + [-16, 70, 83, -21, 11], + [5, -11, 117, 22, -8], + [-6, 23, 117, -12, 3], + [3, -8, 95, 28, 4], + [-10, 15, 77, 60, -15], + [-1, 4, 124, 2, -4], + [3, 38, 84, 24, -25], + [2, 13, 42, 13, 31], + [21, -4, 56, 46, -1], + [-1, 35, 79, -13, 19], + [-7, 65, 88, -9, -14], + [20, 4, 81, 49, -29], + [20, 0, 75, 3, -17], + [5, -9, 44, 92, -8], + [1, -3, 22, 69, 31], + [-6, 95, 41, -12, 5], + [39, 67, 16, -4, 1], + [0, -6, 120, 55, -36], + [-13, 44, 122, 4, -24], + [81, 5, 11, 3, 7], + [2, 0, 9, 10, 88], +]; +pub const LTP_SCALE: [u8; 3] = icdf(&[128, 64, 64]); +pub const UNIFORM4: [u8; 4] = icdf(&[64, 64, 64, 64]); +pub const RATE_LEVEL: [&[u8]; 2] = [&icdf::<9>(&[15, 51, 12, 46, 45, 13, 33, 27, 14]), &icdf::<9>(&[33, 30, 36, 17, 34, 49, 18, 21, 18])]; +pub const PULSE_COUNT: [&[u8]; 11] = [ + &icdf::<18>(&[131, 74, 25, 8, 3, 3, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1]), + &icdf::<18>(&[58, 93, 60, 23, 7, 3, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1]), + &icdf::<18>(&[43, 51, 46, 33, 24, 16, 11, 8, 6, 3, 3, 3, 2, 1, 1, 2, 1, 2]), + &icdf::<18>(&[17, 52, 71, 57, 31, 12, 5, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1]), + &icdf::<18>(&[6, 21, 41, 53, 49, 35, 21, 11, 6, 3, 2, 2, 1, 1, 1, 1, 1, 1]), + &icdf::<18>(&[7, 14, 22, 28, 29, 28, 25, 20, 17, 13, 11, 9, 7, 5, 4, 4, 3, 10]), + &icdf::<18>(&[2, 5, 14, 29, 42, 46, 41, 31, 19, 11, 6, 3, 2, 1, 1, 1, 1, 1]), + &icdf::<18>(&[1, 2, 4, 10, 19, 29, 35, 37, 34, 28, 20, 14, 8, 5, 4, 2, 2, 2]), + &icdf::<18>(&[1, 2, 2, 5, 9, 14, 20, 24, 27, 28, 26, 23, 20, 15, 11, 8, 6, 15]), + &icdf::<18>(&[1, 1, 1, 6, 27, 58, 56, 39, 25, 14, 10, 6, 3, 3, 2, 1, 1, 2]), + &icdf::<18>(&[2, 1, 6, 27, 58, 56, 39, 25, 14, 10, 6, 3, 3, 2, 1, 1, 2, 0]), +]; +pub const SHELL_16: [&[u8]; 16] = [ + &icdf::<2>(&[126, 130]), + &icdf::<3>(&[56, 142, 58]), + &icdf::<4>(&[25, 101, 104, 26]), + &icdf::<5>(&[12, 60, 108, 64, 12]), + &icdf::<6>(&[7, 35, 84, 87, 37, 6]), + &icdf::<7>(&[4, 20, 59, 86, 63, 21, 3]), + &icdf::<8>(&[3, 12, 38, 72, 75, 42, 12, 2]), + &icdf::<9>(&[2, 8, 25, 54, 73, 59, 27, 7, 1]), + &icdf::<10>(&[2, 5, 17, 39, 63, 65, 42, 18, 4, 1]), + &icdf::<11>(&[1, 4, 12, 28, 49, 63, 54, 30, 11, 3, 1]), + &icdf::<12>(&[1, 4, 8, 20, 37, 55, 57, 41, 22, 8, 2, 1]), + &icdf::<13>(&[1, 3, 7, 15, 28, 44, 53, 48, 33, 16, 6, 1, 1]), + &icdf::<14>(&[1, 2, 6, 12, 21, 35, 47, 48, 40, 25, 12, 5, 1, 1]), + &icdf::<15>(&[1, 1, 4, 10, 17, 27, 37, 47, 43, 33, 21, 9, 4, 1, 1]), + &icdf::<16>(&[1, 1, 1, 8, 14, 22, 33, 40, 43, 38, 28, 16, 8, 1, 1, 1]), + &icdf::<17>(&[1, 1, 1, 1, 13, 18, 27, 36, 41, 41, 34, 24, 14, 1, 1, 1, 1]), +]; +pub const SHELL_8: [&[u8]; 16] = [ + &icdf::<2>(&[127, 129]), + &icdf::<3>(&[53, 149, 54]), + &icdf::<4>(&[22, 105, 106, 23]), + &icdf::<5>(&[11, 61, 111, 63, 10]), + &icdf::<6>(&[6, 35, 86, 88, 36, 5]), + &icdf::<7>(&[4, 20, 59, 87, 62, 21, 3]), + &icdf::<8>(&[3, 13, 40, 71, 73, 41, 13, 2]), + &icdf::<9>(&[3, 9, 27, 53, 70, 56, 28, 9, 1]), + &icdf::<10>(&[3, 8, 19, 37, 57, 61, 44, 20, 6, 1]), + &icdf::<11>(&[3, 7, 15, 28, 44, 54, 49, 33, 17, 5, 1]), + &icdf::<12>(&[1, 7, 13, 22, 34, 46, 48, 38, 28, 14, 4, 1]), + &icdf::<13>(&[1, 1, 11, 22, 27, 35, 42, 47, 33, 25, 10, 1, 1]), + &icdf::<14>(&[1, 1, 6, 14, 26, 37, 43, 43, 37, 26, 14, 6, 1, 1]), + &icdf::<15>(&[1, 1, 4, 10, 20, 31, 40, 42, 40, 31, 20, 10, 4, 1, 1]), + &icdf::<16>(&[1, 1, 3, 8, 16, 26, 35, 38, 38, 35, 26, 16, 8, 3, 1, 1]), + &icdf::<17>(&[1, 1, 2, 6, 12, 21, 30, 36, 38, 36, 30, 21, 12, 6, 2, 1, 1]), +]; +pub const SHELL_4: [&[u8]; 16] = [ + &icdf::<2>(&[127, 129]), + &icdf::<3>(&[49, 157, 50]), + &icdf::<4>(&[20, 107, 109, 20]), + &icdf::<5>(&[11, 60, 113, 62, 10]), + &icdf::<6>(&[7, 36, 84, 87, 36, 6]), + &icdf::<7>(&[6, 24, 57, 82, 60, 23, 4]), + &icdf::<8>(&[5, 18, 39, 64, 68, 42, 16, 4]), + &icdf::<9>(&[6, 14, 29, 47, 61, 52, 30, 14, 3]), + &icdf::<10>(&[1, 15, 23, 35, 51, 50, 40, 30, 10, 1]), + &icdf::<11>(&[1, 1, 21, 32, 42, 52, 46, 41, 18, 1, 1]), + &icdf::<12>(&[1, 6, 16, 27, 36, 42, 42, 36, 27, 16, 6, 1]), + &icdf::<13>(&[1, 5, 12, 21, 31, 38, 40, 38, 31, 21, 12, 5, 1]), + &icdf::<14>(&[1, 3, 9, 17, 26, 34, 38, 38, 34, 26, 17, 9, 3, 1]), + &icdf::<15>(&[1, 3, 7, 14, 22, 29, 34, 36, 34, 29, 22, 14, 7, 3, 1]), + &icdf::<16>(&[1, 2, 5, 11, 18, 25, 31, 35, 35, 31, 25, 18, 11, 5, 2, 1]), + &icdf::<17>(&[1, 1, 4, 9, 15, 21, 28, 32, 34, 32, 28, 21, 15, 9, 4, 1, 1]), +]; +pub const SHELL_2: [&[u8]; 16] = [ + &icdf::<2>(&[128, 128]), + &icdf::<3>(&[42, 172, 42]), + &icdf::<4>(&[21, 107, 107, 21]), + &icdf::<5>(&[12, 60, 112, 61, 11]), + &icdf::<6>(&[8, 34, 86, 86, 35, 7]), + &icdf::<7>(&[8, 23, 55, 90, 55, 20, 5]), + &icdf::<8>(&[5, 15, 38, 72, 72, 36, 15, 3]), + &icdf::<9>(&[6, 12, 27, 52, 77, 47, 20, 10, 5]), + &icdf::<10>(&[6, 19, 28, 35, 40, 40, 35, 28, 19, 6]), + &icdf::<11>(&[4, 14, 22, 31, 37, 40, 37, 31, 22, 14, 4]), + &icdf::<12>(&[3, 10, 18, 26, 33, 38, 38, 33, 26, 18, 10, 3]), + &icdf::<13>(&[2, 8, 13, 21, 29, 36, 38, 36, 29, 21, 13, 8, 2]), + &icdf::<14>(&[1, 5, 10, 17, 25, 32, 38, 38, 32, 25, 17, 10, 5, 1]), + &icdf::<15>(&[1, 4, 7, 13, 21, 29, 35, 36, 35, 29, 21, 13, 7, 4, 1]), + &icdf::<16>(&[1, 2, 5, 10, 17, 25, 32, 36, 36, 32, 25, 17, 10, 5, 2, 1]), + &icdf::<17>(&[1, 2, 4, 7, 13, 21, 28, 34, 36, 34, 28, 21, 13, 7, 4, 2, 1]), +]; +pub const EXC_LSB: [u8; 2] = icdf(&[136, 120]); +pub const EXC_SIGN: [&[u8]; 42] = [ + &icdf::<2>(&[2, 254]), + &icdf::<2>(&[207, 49]), + &icdf::<2>(&[189, 67]), + &icdf::<2>(&[179, 77]), + &icdf::<2>(&[174, 82]), + &icdf::<2>(&[163, 93]), + &icdf::<2>(&[157, 99]), + &icdf::<2>(&[58, 198]), + &icdf::<2>(&[245, 11]), + &icdf::<2>(&[238, 18]), + &icdf::<2>(&[232, 24]), + &icdf::<2>(&[225, 31]), + &icdf::<2>(&[220, 36]), + &icdf::<2>(&[211, 45]), + &icdf::<2>(&[1, 255]), + &icdf::<2>(&[210, 46]), + &icdf::<2>(&[190, 66]), + &icdf::<2>(&[178, 78]), + &icdf::<2>(&[169, 87]), + &icdf::<2>(&[162, 94]), + &icdf::<2>(&[152, 104]), + &icdf::<2>(&[48, 208]), + &icdf::<2>(&[242, 14]), + &icdf::<2>(&[235, 21]), + &icdf::<2>(&[224, 32]), + &icdf::<2>(&[214, 42]), + &icdf::<2>(&[205, 51]), + &icdf::<2>(&[190, 66]), + &icdf::<2>(&[1, 255]), + &icdf::<2>(&[162, 94]), + &icdf::<2>(&[152, 104]), + &icdf::<2>(&[147, 109]), + &icdf::<2>(&[144, 112]), + &icdf::<2>(&[141, 115]), + &icdf::<2>(&[138, 118]), + &icdf::<2>(&[8, 248]), + &icdf::<2>(&[203, 53]), + &icdf::<2>(&[187, 69]), + &icdf::<2>(&[176, 80]), + &icdf::<2>(&[168, 88]), + &icdf::<2>(&[161, 95]), + &icdf::<2>(&[154, 102]), +]; +pub const LBRR_FLAGS: [&[u8]; 2] = [&icdf::<3>(&[53, 53, 150]), &icdf::<7>(&[41, 20, 29, 41, 15, 28, 82])]; diff --git a/crates/ui-egui/Cargo.toml b/crates/ui-egui/Cargo.toml index 854f945..16702be 100644 --- a/crates/ui-egui/Cargo.toml +++ b/crates/ui-egui/Cargo.toml @@ -17,6 +17,7 @@ slidecraft-text = { workspace = true } slidecraft-render = { workspace = true } slidecraft-anim = { workspace = true } slidecraft-engine = { workspace = true } +slidecraft-media = { workspace = true } egui = { workspace = true } serde = { workspace = true } serde_json = { workspace = true } diff --git a/crates/ui-egui/src/canvas.rs b/crates/ui-egui/src/canvas.rs index 94e0d31..e3301c3 100644 --- a/crates/ui-egui/src/canvas.rs +++ b/crates/ui-egui/src/canvas.rs @@ -115,6 +115,7 @@ pub fn show(app: &mut SlideApp, ui: &mut Ui) { painter.image(tex.id(), slide_rect, Rect::from_min_max(pos2(0.0, 0.0), pos2(1.0, 1.0)), Color32::WHITE); } app.perf.render_ms = app.textures.last_render_ms; + media_frames(app, &painter, xf); if app.ui.gridlines { let step = 72.0 / 2.0 * scale; let mut x = slide_rect.min.x + step; @@ -144,9 +145,60 @@ pub fn show(app: &mut SlideApp, ui: &mut Ui) { } pointer(app, ui, &resp, xf); overlays(app, ui, &painter, xf, &t); + media_bar(app, ui, xf); context_menu(app, &resp); } +/// Screen rect of a slide-space box (rotation ignored). +fn screen_rect(xf: Xf, x: &Xfrm) -> Rect { + Rect::from_min_max(xf.to_screen(Point::new(x.x, x.y)), xf.to_screen(Point::new(x.x + x.w, x.y + x.h))) +} + +/// Video playing in the editor: its current frame over the poster. +fn media_frames(app: &SlideApp, painter: &egui::Painter, xf: Xf) { + let Some(d) = app.session.active() else { return }; + if d.selection.target != Target::Slides || !app.media.any_active() { + return; + } + for (id, x, clip) in crate::media::slide_media(&d.doc, d.selection.slide) { + if clip.video { + crate::media::paint_frame(painter, &app.media, id, screen_rect(xf, &x)); + } + } +} + +/// The play/pause and timeline bar under a selected audio or video (PowerPoint shows it on +/// selection or hover). +fn media_bar(app: &mut SlideApp, ui: &mut Ui, xf: Xf) { + let Some(d) = app.session.active() else { return }; + if d.selection.target != Target::Slides || d.selection.text.is_some() || app.session.tool.dragging() { + return; + } + let pointer = ui.input(|i| i.pointer.hover_pos()); + let media = crate::media::slide_media(&d.doc, d.selection.slide); + let pick = media.iter().find(|(id, _, _)| d.selection.shapes.len() == 1 && d.selection.shapes[0] == *id).or_else(|| { + media.iter().find(|(id, x, _)| { + let r = screen_rect(xf, x); + app.media.status(*id).is_some() || pointer.is_some_and(|p| r.expand2(vec2(0.0, 40.0)).contains(p)) + }) + }); + let Some((id, x, clip)) = pick.cloned() else { return }; + let doc = d.doc.clone(); + let r = screen_rect(xf, &x); + let w = r.width().max(230.0); + let bar = Rect::from_min_size(pos2(r.center().x - w / 2.0, r.max.y + 6.0), vec2(w, 26.0)); + let status = app.media.status(id); + match crate::media::control_bar(ui, bar, status.as_ref(), &clip) { + Some(crate::media::BarAction::Toggle) => app.media.toggle(&doc, id, &clip, crate::media::Owner::Editor), + Some(crate::media::BarAction::Seek(t)) => app.media.seek(&doc, id, &clip, t, crate::media::Owner::Editor), + Some(crate::media::BarAction::Nudge(dt)) => { + let pos = status.map(|s| s.position).unwrap_or(0.0); + app.media.seek(&doc, id, &clip, (pos + dt).max(0.0), crate::media::Owner::Editor); + } + None => {} + } +} + fn master_tex( ctx: &egui::Context, doc: &slidecraft_model::Presentation, diff --git a/crates/ui-egui/src/lib.rs b/crates/ui-egui/src/lib.rs index d4431fc..5b4f422 100644 --- a/crates/ui-egui/src/lib.rs +++ b/crates/ui-egui/src/lib.rs @@ -11,6 +11,7 @@ pub mod control; pub mod dialogs; pub mod fillui; pub mod icons; +pub mod media; pub mod menus; pub mod panes; pub mod ribbon; @@ -54,6 +55,8 @@ pub struct Services { pub inbox: Option, /// Image on the system clipboard (PNG bytes), when the host can read one. pub clipboard_image: Option Option>>>, + /// Audio output for media playback (cpal on desktop); without one media plays silently. + pub audio_out: Option>, } #[derive(Clone, Copy, Debug, Default, PartialEq, Eq, Serialize, Deserialize)] @@ -173,6 +176,8 @@ pub struct SlideApp { pub control_rx: Option>, pub dialog: Option, pub show: Option, + /// Audio/video playback (editor control bar, slide show, `media.*` commands). + pub media: media::MediaHost, pub status: Option<(String, f64)>, pub perf: Perf, /// Synthetic input events (control channel `ui.click` etc.), injected one per frame. @@ -202,7 +207,8 @@ pub struct SlideApp { } impl SlideApp { - pub fn new(session: Session, services: Services) -> Self { + pub fn new(session: Session, mut services: Services) -> Self { + let audio_out = services.audio_out.take(); SlideApp { session, services, @@ -211,6 +217,7 @@ impl SlideApp { control_rx: None, dialog: None, show: None, + media: media::MediaHost::new(audio_out), status: None, perf: Perf { frame_ms: 0.0, render_ms: 0.0, fps: 0.0 }, synthetic: vec![], @@ -313,7 +320,10 @@ impl SlideApp { self.start_show(from, false); } "show.presenter" => self.ui.presenter_view = self.flag(p, self.ui.presenter_view), - "show.end" => self.show = None, + "show.end" => { + self.show = None; + self.media.stop_owned(media::Owner::Show); + } "app.about" => self.dialog = Some(dialogs::Dialog::new("about")), "app.palette" => self.palette = Some((String::new(), 0)), "app.preferences" => self.dialog = Some(dialogs::Dialog::new("preferences")), @@ -337,6 +347,7 @@ impl SlideApp { } pub fn start_show(&mut self, from: usize, reading: bool) { + self.media.stop_owned(media::Owner::Editor); if let Some(d) = self.session.active() { self.show = Some(show::Show::new(&d.doc, from, reading, self.ui.presenter_view && !reading)); } @@ -447,6 +458,12 @@ impl SlideApp { self.save_as_dialog("slidecraft"); } } + UiRequest::Media { action, shape, ms } => { + let owner = if self.show.is_some() { media::Owner::Show } else { media::Owner::Editor }; + if let Some(doc) = self.session.active().map(|d| d.doc.clone()) { + self.media.request(&doc, &action, shape, ms, owner); + } + } } } } @@ -632,6 +649,7 @@ impl SlideApp { } let t0 = now_ms(); let t = theme::Tokens::get(&ctx); + self.tick_media(&ctx); if self.show.is_some() { show::ui(self, ui); self.perf.frame_ms = now_ms() - t0; @@ -678,6 +696,29 @@ impl SlideApp { self.perf.frame_ms = now_ms() - t0; } + /// Media clocks and video textures; the editor's media stops when its slide is left. + fn tick_media(&mut self, ctx: &egui::Context) { + if !self.media.any_active() { + self.session.media_status.clear(); + return; + } + if self.show.is_none() + && let Some(d) = self.session.active() + { + let here: Vec = if d.selection.target == slidecraft_engine::Target::Slides { + media::slide_media(&d.doc, d.selection.slide).into_iter().map(|m| m.0).collect() + } else { + vec![] + }; + let gone: Vec<_> = + self.media.ids().into_iter().filter(|id| !here.contains(id) && self.media.owner(*id) == Some(media::Owner::Editor)).collect(); + for id in gone { + self.media.stop(id); + } + } + self.media.tick(ctx, &mut self.session); + } + pub fn open_url(&mut self, url: &str) { self.pending_urls.push(url.to_string()); } diff --git a/crates/ui-egui/src/media.rs b/crates/ui-egui/src/media.rs new file mode 100644 index 0000000..a470259 --- /dev/null +++ b/crates/ui-egui/src/media.rs @@ -0,0 +1,357 @@ +//! Media playback in the UI host: the [`slidecraft_media::Player`] (audio out + clocks), video +//! feeds and their textures, the editor's media control bar, and the engine's `media.*` requests. + +use std::collections::HashMap; +use std::sync::Arc; + +use egui::{Align2, Color32, CornerRadius, Pos2, Rect, Sense, Stroke, TextureHandle, Ui, pos2, vec2}; +use slidecraft_engine::{MediaStatus, Session}; +use slidecraft_media::{AudioOut, ClipParams, Frame, PlayState, Player, VideoFeed, VoiceSpec, VoiceStatus}; +use slidecraft_model::{MediaClip, Presentation, ShapeId, ShapeKind}; + +use crate::icons::{self, Icon}; + +/// Who started a voice: the editor's control bar stops when you leave the slide, the slide show +/// manages its own. +#[derive(Clone, Copy, Debug, PartialEq, Eq)] +pub enum Owner { + Editor, + Show, +} + +struct Video { + feed: Option, + frame: Option>, + tex: Option, +} + +pub struct MediaHost { + player: Player, + owners: HashMap, + videos: HashMap, + /// Probed durations by media bytes (for media imported without a probe). + durations: HashMap, +} + +impl Default for MediaHost { + fn default() -> Self { + MediaHost::new(None) + } +} + +fn key(id: ShapeId) -> u64 { + id.0 as u64 +} + +impl MediaHost { + pub fn new(out: Option>) -> MediaHost { + MediaHost { player: Player::new(out), owners: HashMap::new(), videos: HashMap::new(), durations: HashMap::new() } + } + + /// Replace the audio output (the host injects cpal after start-up). + pub fn set_output(&mut self, out: Option>) { + self.player.stop_all(); + self.owners.clear(); + self.videos.clear(); + self.player = Player::new(out); + } + + pub fn status(&self, id: ShapeId) -> Option { + self.player.status(key(id)) + } + + /// Playing or loading (not paused / ended). + pub fn is_playing(&self, id: ShapeId) -> bool { + self.status(id).is_some_and(|s| matches!(s.state, PlayState::Playing | PlayState::Loading)) + } + + pub fn owner(&self, id: ShapeId) -> Option { + self.owners.get(&id).map(|o| o.0) + } + + /// Shapes with a voice (playing, paused or ended). + pub fn ids(&self) -> Vec { + self.owners.keys().copied().collect() + } + + pub fn any_active(&self) -> bool { + !self.owners.is_empty() + } + + /// Start (or resume) shape `id` from `from` seconds. + pub fn play(&mut self, doc: &Presentation, id: ShapeId, clip: &MediaClip, from: Option, owner: Owner) { + let Some(item) = doc.media(clip.media) else { return }; + let bytes = item.data.clone(); + let media_key = Arc::as_ptr(&bytes) as usize; + let duration = if clip.duration_ms > 0 { + clip.duration_ms as f64 / 1000.0 + } else { + *self.durations.entry(media_key).or_insert_with(|| slidecraft_media::probe(&bytes).map(|i| i.duration()).unwrap_or(0.0)) + }; + let spec = VoiceSpec { + clip: ClipParams::from_ms(clip.trim_start_ms, clip.trim_end_ms, clip.fade_in_ms, clip.fade_out_ms, clip.volume), + looping: clip.loop_play, + duration, + audio: clip.volume > 0.0, + }; + if clip.video && !self.videos.contains_key(&id) { + let feed = match VideoFeed::new(bytes.clone()) { + Ok(f) => Some(f), + Err(e) => { + log::info!("video {id}: {e}"); + None + } + }; + self.videos.insert(id, Video { feed, frame: None, tex: None }); + } + self.player.play(key(id), media_key as u64, &bytes, spec, from); + self.owners.insert(id, (owner, clip.clone())); + } + + pub fn pause(&mut self, id: ShapeId) { + self.player.pause(key(id)); + } + + pub fn toggle(&mut self, doc: &Presentation, id: ShapeId, clip: &MediaClip, owner: Owner) { + if self.is_playing(id) { + self.pause(id); + } else { + self.play(doc, id, clip, None, owner); + } + } + + pub fn stop(&mut self, id: ShapeId) { + self.player.stop(key(id)); + self.owners.remove(&id); + self.videos.remove(&id); + } + + pub fn stop_owned(&mut self, owner: Owner) { + let ids: Vec = self.owners.iter().filter(|(_, o)| o.0 == owner).map(|(id, _)| *id).collect(); + for id in ids { + self.stop(id); + } + } + + /// Move to `t` seconds; a shape that isn't playing is cued there, paused. + pub fn seek(&mut self, doc: &Presentation, id: ShapeId, clip: &MediaClip, t: f64, owner: Owner) { + if self.status(id).is_none() { + self.play(doc, id, clip, Some(t), owner); + self.pause(id); + } + self.player.seek(key(id), t); + } + + /// The current video frame of shape `id`, once one has decoded. + pub fn texture(&self, id: ShapeId) -> Option<&TextureHandle> { + self.videos.get(&id).and_then(|v| v.tex.as_ref()) + } + + /// Per frame: run the clocks, upload due video frames, apply loop/rewind, and report state to + /// the engine. Returns whether anything is playing (the caller keeps repainting). + pub fn tick(&mut self, ctx: &egui::Context, session: &mut Session) -> bool { + let now = ctx.input(|i| i.time); + self.player.tick(now); + let mut ended = vec![]; + for (id, (_, clip)) in &self.owners { + if let Some(st) = self.player.status(key(*id)) + && st.state == PlayState::Ended + && clip.rewind + { + ended.push(*id); + } + } + // Rewind after playing: back to the poster. + for id in ended { + self.stop(id); + } + for (id, v) in &mut self.videos { + let Some(st) = self.player.status(key(*id)) else { continue }; + let Some(feed) = v.feed.as_mut() else { continue }; + let f = feed.frame_at(st.position); + if let Some(f) = f + && v.frame.as_ref().is_none_or(|old| !Arc::ptr_eq(old, &f)) + { + let img = egui::ColorImage::from_rgba_unmultiplied([f.width as usize, f.height as usize], &f.rgba); + match v.tex.as_mut() { + Some(t) => t.set(img, egui::TextureOptions::LINEAR), + None => v.tex = Some(ctx.load_texture(format!("media-{id}"), img, egui::TextureOptions::LINEAR)), + } + v.frame = Some(f); + } + } + session.media_status = self + .owners + .keys() + .filter_map(|id| { + self.player.status(key(*id)).map(|s| { + ( + *id, + MediaStatus { + state: s.state.name().into(), + position_ms: (s.position.max(0.0) * 1000.0).round() as u64, + duration_ms: (s.duration.max(0.0) * 1000.0).round() as u64, + error: s.error, + }, + ) + }) + }) + .collect(); + let playing = self.owners.keys().any(|id| self.is_playing(*id)); + if playing { + ctx.request_repaint_after(std::time::Duration::from_millis(15)); + } + playing + } + + /// Carry out a `media.*` request from the engine. + pub fn request(&mut self, doc: &Presentation, action: &str, id: ShapeId, ms: Option, owner: Owner) { + let Some(clip) = find_clip(doc, id) else { return }; + let t = ms.map(|v| v as f64 / 1000.0); + match action { + "play" => self.play(doc, id, &clip, t, owner), + "pause" => self.pause(id), + "toggle" => self.toggle(doc, id, &clip, owner), + "stop" => self.stop(id), + "seek" => self.seek(doc, id, &clip, t.unwrap_or(0.0), owner), + _ => {} + } + } +} + +/// The clip of media shape `id` anywhere in the deck. +pub fn find_clip(doc: &Presentation, id: ShapeId) -> Option { + fn walk(shapes: &[slidecraft_model::Shape], id: ShapeId) -> Option { + shapes.iter().find_map(|s| match &s.kind { + ShapeKind::Media(m) if s.id == id => Some(m.clone()), + ShapeKind::Group { children, .. } => walk(children, id), + _ => None, + }) + } + doc.slides.iter().find_map(|s| walk(&s.shapes, id)) +} + +/// Media shapes directly on slide `index`: (id, box in slide points, clip). +pub fn slide_media(doc: &Presentation, index: usize) -> Vec<(ShapeId, slidecraft_geom::Xfrm, MediaClip)> { + let Some(s) = doc.slides.get(index) else { return vec![] }; + s.shapes + .iter() + .filter_map(|sh| match (&sh.kind, sh.xfrm) { + (ShapeKind::Media(m), Some(x)) => Some((sh.id, x, m.clone())), + _ => None, + }) + .collect() +} + +/// "1:05.25" style time. +pub fn clock(secs: f64) -> String { + let s = secs.max(0.0); + format!("{:02}:{:05.2}", (s / 60.0) as u32, s % 60.0) +} + +/// Draw the playing video frame of `id` into `rect` (letterboxed to the frame's aspect). +pub fn paint_frame(painter: &egui::Painter, host: &MediaHost, id: ShapeId, rect: Rect) -> bool { + let Some(tex) = host.texture(id) else { return false }; + let [w, h] = tex.size(); + let (w, h) = (w.max(1) as f32, h.max(1) as f32); + let k = (rect.width() / w).min(rect.height() / h); + let r = Rect::from_center_size(rect.center(), vec2(w * k, h * k)); + painter.rect_filled(rect, CornerRadius::ZERO, Color32::BLACK); + painter.image(tex.id(), r, Rect::from_min_max(pos2(0.0, 0.0), pos2(1.0, 1.0)), Color32::WHITE); + true +} + +/// What the control bar asks for. +pub enum BarAction { + Toggle, + Seek(f64), + Nudge(f64), +} + +/// PowerPoint-style media controls: play/pause, ¼-second nudges, a seekable timeline and the time. +pub fn control_bar(ui: &mut Ui, rect: Rect, status: Option<&VoiceStatus>, clip: &MediaClip) -> Option { + let painter = ui.painter_at(rect.expand(2.0)); + painter.rect_filled(rect, CornerRadius::same(4), Color32::from_rgba_unmultiplied(28, 28, 32, 230)); + let playing = status.is_some_and(|s| matches!(s.state, PlayState::Playing | PlayState::Loading)); + let duration = status.map(|s| s.duration).filter(|d| *d > 0.0).unwrap_or(clip.duration_ms as f64 / 1000.0); + let pos = status.map(|s| s.position).unwrap_or(clip.trim_start_ms as f64 / 1000.0); + let mut action = None; + let h = rect.height(); + let mut x = rect.min.x + 4.0; + let button = |ui: &mut Ui, x: &mut f32, id: &str, paint: &dyn Fn(&egui::Painter, Rect)| -> bool { + let r = Rect::from_min_size(pos2(*x, rect.min.y + 2.0), vec2(h - 4.0, h - 4.0)); + *x += h - 2.0; + let resp = ui.interact(r, ui.id().with(("media_bar", id)), Sense::click()); + if resp.hovered() { + painter.rect_filled(r, CornerRadius::same(3), Color32::from_white_alpha(28)); + } + paint(&painter, r.shrink(4.0)); + resp.clicked() + }; + let white = Color32::from_gray(235); + if button(ui, &mut x, "play", &|p, r| { + if playing { + let w = r.width() * 0.3; + p.rect_filled(Rect::from_min_size(r.min, vec2(w, r.height())), CornerRadius::ZERO, white); + p.rect_filled(Rect::from_min_size(pos2(r.max.x - w, r.min.y), vec2(w, r.height())), CornerRadius::ZERO, white); + } else { + icons::paint(p, r, Icon::Play, white, false); + } + }) { + action = Some(BarAction::Toggle); + } + let tri = |p: &egui::Painter, r: Rect, left: bool| { + let (a, b) = if left { (r.max.x, r.min.x) } else { (r.min.x, r.max.x) }; + let pts: Vec = vec![pos2(a, r.min.y + 2.0), pos2(b, r.center().y), pos2(a, r.max.y - 2.0)]; + p.add(egui::Shape::convex_polygon(pts, white, Stroke::NONE)); + }; + if button(ui, &mut x, "back", &|p, r| tri(p, r.shrink(2.0), true)) { + action = Some(BarAction::Nudge(-0.25)); + } + if button(ui, &mut x, "fwd", &|p, r| tri(p, r.shrink(2.0), false)) { + action = Some(BarAction::Nudge(0.25)); + } + // Time label on the right, timeline between. + let label = clock(pos); + let label_w = 64.0; + painter.text(pos2(rect.max.x - 6.0, rect.center().y), Align2::RIGHT_CENTER, label, egui::FontId::monospace(11.0), white); + let track = Rect::from_min_max(pos2(x + 6.0, rect.center().y - 3.0), pos2(rect.max.x - label_w - 6.0, rect.center().y + 3.0)); + if track.width() > 10.0 { + painter.rect_filled(track, CornerRadius::same(3), Color32::from_gray(90)); + let frac = if duration > 0.0 { (pos / duration).clamp(0.0, 1.0) as f32 } else { 0.0 }; + let done = Rect::from_min_max(track.min, pos2(track.min.x + track.width() * frac, track.max.y)); + painter.rect_filled(done, CornerRadius::same(3), Color32::from_rgb(0x4C, 0x9A, 0xFF)); + // Trim marks. + if duration > 0.0 { + for t in [clip.trim_start_ms as f64 / 1000.0, duration - clip.trim_end_ms as f64 / 1000.0] { + if t > 0.0 && t < duration { + let tx = track.min.x + track.width() * (t / duration) as f32; + painter.line_segment( + [pos2(tx, track.min.y - 3.0), pos2(tx, track.max.y + 3.0)], + Stroke::new(1.5, Color32::from_rgb(0xF2, 0xB1, 0x34)), + ); + } + } + } + painter.circle_filled(pos2(done.max.x, track.center().y), 5.0, white); + let hit = track.expand2(vec2(4.0, 8.0)); + let resp = ui.interact(hit, ui.id().with("media_bar_track"), Sense::click_and_drag()); + if (resp.clicked() || resp.dragged()) + && duration > 0.0 + && let Some(p) = resp.interact_pointer_pos() + { + let f = ((p.x - track.min.x) / track.width()).clamp(0.0, 1.0) as f64; + action = Some(BarAction::Seek(f * duration)); + } + } + if let Some(err) = status.and_then(|s| s.error.as_ref()) { + painter.text( + pos2(rect.min.x, rect.max.y + 3.0), + Align2::LEFT_TOP, + format!("No sound: {err}"), + egui::FontId::proportional(10.0), + Color32::from_rgb(0xC0, 0x40, 0x40), + ); + } + action +} diff --git a/crates/ui-egui/src/ribbon.rs b/crates/ui-egui/src/ribbon.rs index aa01ed3..2dc7eaa 100644 --- a/crates/ui-egui/src/ribbon.rs +++ b/crates/ui-egui/src/ribbon.rs @@ -2292,8 +2292,9 @@ fn playback(app: &mut SlideApp, ui: &mut Ui) { .active() .and_then(|d| d.selected_shapes().into_iter().find_map(|s| if let ShapeKind::Media(m) = &s.kind { Some(m.clone()) } else { None })); group(ui, |ui| { - if big_button(ui, Icon::Play, "Play", m.is_some()).clicked() { - app.set_status("Media plays in the slide show (Present)."); + let playing = app.session.active().and_then(|d| d.selection.shapes.first().copied()).is_some_and(|id| app.media.is_playing(id)); + if big_button(ui, if playing { Icon::Pause } else { Icon::Play }, if playing { "Pause" } else { "Play" }, m.is_some()).clicked() { + run(app, "media.toggle", json!({})); } big(app, ui, Icon::TrimMedia, "Trim", "app.dialog", json!({"id": "trim"})); }); diff --git a/crates/ui-egui/src/show.rs b/crates/ui-egui/src/show.rs index e09d137..610d652 100644 --- a/crates/ui-egui/src/show.rs +++ b/crates/ui-egui/src/show.rs @@ -35,6 +35,8 @@ pub struct Show { timings: Vec<(usize, f64)>, last_move: f64, pub ended: bool, + /// The slide whose media was last started (auto-play runs once per visit). + media_slide: Option, } #[derive(Clone, PartialEq)] @@ -43,6 +45,8 @@ struct Key { step: usize, size: (u32, u32), doc: usize, + /// Media hidden while not playing. + hidden: Vec, } struct Transition { @@ -78,6 +82,7 @@ impl Show { timings: vec![], last_move: 0.0, ended: false, + media_slide: None, } } @@ -268,7 +273,10 @@ pub fn ui(app: &mut SlideApp, ui: &mut Ui) { _ => {} } } - if clicked && !show.pen && !show.reading_bar_hit(&ctx) { + let media_click = if clicked && !show.pen && !show.ended && show.blank.is_none() { media_hit(&ctx, ui, &show, &doc) } else { None }; + if let Some((id, clip)) = media_click { + app.media.toggle(&doc, id, &clip, crate::media::Owner::Show); + } else if clicked && !show.pen && !show.reading_bar_hit(&ctx) { if show.ended { keep = false; } else if show.blank.is_some() { @@ -307,6 +315,7 @@ pub fn ui(app: &mut SlideApp, ui: &mut Ui) { keep = false; } } + show_media(app, &mut show, &doc); // Draw. let full = ui.max_rect(); let painter = ui.painter_at(full); @@ -328,12 +337,27 @@ pub fn ui(app: &mut SlideApp, ui: &mut Ui) { let step = show.state.step; let animating = show.state.playing; let t = if animating { now - show.step_start } else { 0.0 }; - let key = Key { slide: idx, step, size, doc: std::sync::Arc::as_ptr(&doc) as usize }; + let media = crate::media::slide_media(&doc, idx); + let hidden: Vec = media + .iter() + .filter(|(id, _, clip)| { + clip.hide_while_not_playing && !app.media.status(*id).is_some_and(|s| s.state != slidecraft_media::PlayState::Ended) + }) + .map(|m| m.0) + .collect(); + let key = Key { slide: idx, step, size, doc: std::sync::Arc::as_ptr(&doc) as usize, hidden: hidden.clone() }; let tex = if !animating && show.tex.as_ref().is_some_and(|(k, _)| *k == key) { show.tex.as_ref().map(|(_, t)| t.clone()) } else { let tl = show.timeline(&doc).clone(); - let f = move |id: ShapeId| state_for(&tl, id, step, t); + let hide = hidden.clone(); + let f = move |id: ShapeId| { + let mut st = state_for(&tl, id, step, t); + if hide.contains(&id) { + st.visible = false; + } + st + }; let threads = if cfg!(target_arch = "wasm32") { 0 } else { std::thread::available_parallelism().map(|n| n.get().min(8) as u16).unwrap_or(0) }; let img = slidecraft_render::render_slide( @@ -383,6 +407,19 @@ pub fn ui(app: &mut SlideApp, ui: &mut Ui) { if let Some(tex) = &tex { painter.image(tex.id(), srect, Rect::from_min_max(pos2(0.0, 0.0), pos2(1.0, 1.0)), Color32::WHITE); } + // Playing video: the current frame in its box, or full screen. + let k = srect.width() / doc.slide_size.width.max(1.0) as f32; + for (id, x, clip) in &media { + if !clip.video || hidden.contains(id) { + continue; + } + let r = if clip.full_screen && app.media.is_playing(*id) { + full + } else { + Rect::from_min_size(pos2(srect.min.x + x.x as f32 * k, srect.min.y + x.y as f32 * k), vec2(x.w as f32 * k, x.h as f32 * k)) + }; + crate::media::paint_frame(&painter, &app.media, *id, r); + } } if animating { ctx.request_repaint(); @@ -489,6 +526,7 @@ pub fn ui(app: &mut SlideApp, ui: &mut Ui) { if keep { app.show = Some(show); } else { + app.media.stop_owned(crate::media::Owner::Show); if !show.reading { ctx.send_viewport_cmd(egui::ViewportCommand::Fullscreen(false)); } @@ -507,6 +545,45 @@ pub fn ui(app: &mut SlideApp, ui: &mut Ui) { } } +/// Start the media of a newly shown slide: media from the previous slide stops unless it plays +/// across slides; media set to start automatically plays. Everything stops at the end. +fn show_media(app: &mut SlideApp, show: &mut Show, doc: &Presentation) { + let cur = if show.ended { None } else { Some(show.state.slide) }; + if show.media_slide == cur { + return; + } + show.media_slide = cur; + let keep: Vec = match cur { + Some(_) => app.media.ids().into_iter().filter(|id| crate::media::find_clip(doc, *id).is_some_and(|c| c.play_across_slides)).collect(), + None => vec![], + }; + for id in app.media.ids() { + if !keep.contains(&id) { + app.media.stop(id); + } + } + if let Some(i) = cur { + for (id, _, clip) in crate::media::slide_media(doc, i) { + if clip.autoplay && !keep.contains(&id) { + app.media.play(doc, id, &clip, None, crate::media::Owner::Show); + } + } + } +} + +/// The media shape under the pointer on the shown slide. +fn media_hit(ctx: &egui::Context, ui: &Ui, show: &Show, doc: &Presentation) -> Option<(ShapeId, slidecraft_model::MediaClip)> { + let p = ctx.input(|i| i.pointer.interact_pos())?; + let full = ui.max_rect(); + let avail = if show.reading { Rect::from_min_max(full.min, pos2(full.max.x, full.max.y - 34.0)) } else { full }; + let srect = fit(avail, doc); + let k = srect.width() / doc.slide_size.width.max(1.0) as f32; + crate::media::slide_media(doc, show.state.slide).into_iter().rev().find_map(|(id, x, clip)| { + let r = Rect::from_min_size(pos2(srect.min.x + x.x as f32 * k, srect.min.y + x.y as f32 * k), vec2(x.w as f32 * k, x.h as f32 * k)); + r.contains(p).then_some((id, clip)) + }) +} + impl Show { fn reading_bar_hit(&self, ctx: &egui::Context) -> bool { // Clicks on the on-screen controls must not also advance. diff --git a/crates/vp9/Cargo.toml b/crates/vp9/Cargo.toml new file mode 100644 index 0000000..e21196f --- /dev/null +++ b/crates/vp9/Cargo.toml @@ -0,0 +1,19 @@ +[package] +name = "slidecraft-vp9" +description = "Clean-room pure-Rust VP9 decoder (VP9 Bitstream & Decoding Process Specification)" +version.workspace = true +edition.workspace = true +license.workspace = true +rust-version.workspace = true + +[lints] +workspace = true + +[features] +default = ["threads"] +threads = ["dep:rayon"] + +[dependencies] +slidecraft-bitstream = { workspace = true } +thiserror = { workspace = true } +rayon = { workspace = true, optional = true } diff --git a/crates/vp9/src/boolcoder.rs b/crates/vp9/src/boolcoder.rs new file mode 100644 index 0000000..de56d5c --- /dev/null +++ b/crates/vp9/src/boolcoder.rs @@ -0,0 +1,170 @@ +//! Boolean (arithmetic) decoder of section 9.2. +//! +//! The specification keeps an 8-bit `BoolValue` and shifts in one bit per renormalisation step. +//! This implementation keeps `BoolValue` in the top 8 bits of a 64-bit window whose lower bits +//! hold the next bits of the input, which is equivalent and needs a refill only every few bytes. +//! Reading past the end of the data yields zero bits, as the specification does once +//! `BoolMaxBits` is exhausted. + +pub struct BoolDecoder<'a> { + data: &'a [u8], + pos: usize, + /// BoolValue in bits 56..63, followed by `count` valid input bits. + value: u64, + count: i32, + range: u32, +} + +impl<'a> BoolDecoder<'a> { + /// init_bool (9.2.1): the marker bit is read and returned in `Err` position when non-zero is + /// not treated as fatal (libvpx ignores it too), so this only returns the decoder. + pub fn new(data: &'a [u8]) -> Self { + let mut d = BoolDecoder { data, pos: 0, value: 0, count: -8, range: 255 }; + d.fill(); + d.read_bool(128); + d + } + + #[inline] + fn fill(&mut self) { + // Room for whole bytes below the top 8 bits and the `count` valid bits. + let mut shift = 48 - self.count; + if let Some(&bytes) = self.data.get(self.pos..).and_then(|d| d.first_chunk::<8>()) { + let v = u64::from_be_bytes(bytes); + let n = ((shift + 8) / 8) as usize; // bytes that fit + let n = n.min(7); + // Take the top n bytes of v. + let take = v >> (64 - 8 * n); + self.value |= take << (shift + 8 - 8 * n as i32); + self.pos += n; + self.count += 8 * n as i32; + return; + } + while shift >= 0 { + let b = if self.pos < self.data.len() { + let b = self.data[self.pos]; + self.pos += 1; + b + } else { + 0 + }; + self.value |= (b as u64) << shift; + self.count += 8; + shift -= 8; + } + } + + /// read_bool (9.2.2). + #[inline(always)] + pub fn read_bool(&mut self, p: u8) -> bool { + if self.count < 8 { + self.fill(); + } + let split = 1 + (((self.range - 1) * p as u32) >> 8); + let bigsplit = (split as u64) << 56; + // The outcome is close to random: select instead of branching on it. + let bit = self.value >= bigsplit; + let m = (bit as u64).wrapping_neg(); + self.value -= bigsplit & m; + let m32 = m as u32; + self.range = ((self.range - split) & m32) | (split & !m32); + let shift = self.range.leading_zeros() - 24; + self.range <<= shift; + self.value <<= shift; + self.count -= shift as i32; + bit + } + + /// read_literal (9.2.4). + pub fn read_literal(&mut self, n: u32) -> u32 { + let mut x = 0; + for _ in 0..n { + x = (x << 1) | self.read_bool(128) as u32; + } + x + } + + /// Tree decoding process (9.3.3) with probabilities `probs[node]`. + #[inline] + pub fn read_tree(&mut self, tree: &[i8], probs: &[u8]) -> u8 { + let mut n = 0i8; + loop { + n = tree[(n + self.read_bool(probs[(n >> 1) as usize]) as i8) as usize]; + if n <= 0 { + return (-n) as u8; + } + } + } +} + +#[cfg(test)] +mod tests { + use super::*; + + /// Straightforward implementation of 9.2.2 for comparison. + struct SpecDecoder<'a> { + data: &'a [u8], + bitpos: usize, + value: u32, + range: u32, + max_bits: i64, + } + + impl<'a> SpecDecoder<'a> { + fn new(data: &'a [u8]) -> Self { + let mut d = SpecDecoder { data, bitpos: 0, value: 0, range: 255, max_bits: 8 * data.len() as i64 - 8 }; + for _ in 0..8 { + d.value = (d.value << 1) | d.bit(); + } + d.read(128); + d + } + fn bit(&mut self) -> u32 { + let b = self.data.get(self.bitpos / 8).map(|b| (b >> (7 - self.bitpos % 8)) & 1).unwrap_or(0); + self.bitpos += 1; + b as u32 + } + fn read(&mut self, p: u8) -> bool { + let split = 1 + (((self.range - 1) * p as u32) >> 8); + let bit = if self.value < split { + self.range = split; + false + } else { + self.range -= split; + self.value -= split; + true + }; + while self.range < 128 { + let nb = if self.max_bits > 0 { + self.max_bits -= 1; + self.bit() + } else { + 0 + }; + self.range *= 2; + self.value = (self.value << 1) + nb; + } + bit + } + } + + #[test] + fn matches_spec_model() { + let mut seed = 0x1234_5678u32; + let mut rnd = || { + seed ^= seed << 13; + seed ^= seed >> 17; + seed ^= seed << 5; + seed + }; + for len in [1usize, 2, 3, 7, 8, 9, 15, 16, 17, 100, 1000] { + let data: Vec = (0..len).map(|_| rnd() as u8).collect(); + let mut a = BoolDecoder::new(&data); + let mut b = SpecDecoder::new(&data); + for i in 0..len * 12 { + let p = (rnd() % 255 + 1) as u8; + assert_eq!(a.read_bool(p), b.read(p), "len {len} bit {i}"); + } + } + } +} diff --git a/crates/vp9/src/decoder.rs b/crates/vp9/src/decoder.rs new file mode 100644 index 0000000..bd9cd1a --- /dev/null +++ b/crates/vp9/src/decoder.rs @@ -0,0 +1,762 @@ +//! Top-level decoder: superframes, frame header handling, tile scheduling (tile columns in +//! parallel), probability adaptation, reference frame management, and the per-frame "post" stage +//! (band assembly, loop filter, publication, output picture) that runs behind the next frame +//! (frame threading). + +use crate::error::{Error, Result, ensure}; +use crate::frame::{BAND_PAD, Band, Frame, FrameInfo, MiGrid, MiInfo, Pools}; +use crate::header::{FrameHeader, HeaderState, KEY_FRAME, RefInfo, parse_compressed, parse_uncompressed, split_superframe}; +use crate::loopfilter::{LfFrame, PlaneView, filter_superblock}; +use crate::probs::{Counts, adapt_coef_probs, adapt_noncoef_probs}; +use crate::tables::*; +use crate::tile::{FrameShared, RefUse, Strip, TileDecoder}; +use crate::{ColorInfo, Picture, Plane}; +use std::collections::VecDeque; +use std::sync::Arc; + +/// Counters describing what the decoder has seen (useful to check test coverage). +#[derive(Clone, Debug, Default, PartialEq, Eq)] +pub struct DecodeStats { + pub frames: u64, + pub shown: u64, + pub hidden: u64, + pub key_frames: u64, + pub intra_only: u64, + pub inter_frames: u64, + pub show_existing: u64, + pub superframes: u64, + pub error_resilient: u64, + pub no_backward_adaptation: u64, + pub size_changes: u64, + pub scaled_ref_blocks: u64, + pub compound_blocks: u64, + pub intra_blocks: u64, + pub inter_blocks: u64, + pub lossless_frames: u64, + pub max_tile_cols: u32, + pub max_tile_rows: u32, + pub segmentation_frames: u64, + pub tx_select_frames: u64, + pub switchable_interp_frames: u64, + pub high_precision_mv_frames: u64, + pub compound_frames: u64, + pub bit_depths: [u64; 3], + pub profiles: [u64; 4], + /// Frames decoded in draft mode without the loop filter ([`Decoder::set_draft`]). + pub draft_frames: u64, +} + +/// A picture on its way out, in output order. +enum Pending { + Ready(Picture), + /// Produced by a post job running on another thread. + #[cfg(all(feature = "threads", not(target_arch = "wasm32")))] + Job(std::sync::mpsc::Receiver), + /// show_existing_frame: converted once the frame is complete. + Existing(Arc, i64, bool), +} + +/// VP9 decoder. +pub struct Decoder { + st: HeaderState, + slots: [Option>; 8], + prev_mi: Option>, + prev_seg_ids: Vec, + last_size: Option<(u32, u32)>, + last_show_frame: bool, + stats: DecodeStats, + threads: usize, + bufs: Arc, + draft: bool, + pending: VecDeque, + #[cfg(all(feature = "threads", not(target_arch = "wasm32")))] + jobs: VecDeque>, + #[cfg(feature = "threads")] + pool: Option, +} + +impl Default for Decoder { + fn default() -> Self { + Self::new() + } +} + +fn default_threads() -> usize { + #[cfg(all(feature = "threads", not(target_arch = "wasm32")))] + { + std::thread::available_parallelism().map(|n| n.get()).unwrap_or(1).min(16) + } + #[cfg(not(all(feature = "threads", not(target_arch = "wasm32"))))] + { + 1 + } +} + +/// get_tile_offset (6.4.1). +fn tile_offset(i: usize, mis: usize, log2: u32) -> usize { + let sbs = (mis + 7) >> 3; + (((i * sbs) >> log2) << 3).min(mis) +} + +impl Decoder { + /// A decoder using all available cores (with the `threads` feature). + pub fn new() -> Self { + Self::with_threads(default_threads()) + } + + /// A decoder using up to `threads` worker threads (1 = decode on the calling thread, and + /// every picture is returned by the `decode` call that completes it). + pub fn with_threads(threads: usize) -> Self { + #[cfg(feature = "threads")] + let pool = if threads > 1 && cfg!(not(target_arch = "wasm32")) { + rayon::ThreadPoolBuilder::new().num_threads(threads).thread_name(|i| format!("vp9-{i}")).build().ok() + } else { + None + }; + Decoder { + st: HeaderState::default(), + slots: Default::default(), + prev_mi: None, + prev_seg_ids: Vec::new(), + last_size: None, + last_show_frame: false, + stats: DecodeStats::default(), + threads: threads.max(1), + bufs: Arc::default(), + draft: false, + pending: VecDeque::new(), + #[cfg(all(feature = "threads", not(target_arch = "wasm32")))] + jobs: VecDeque::new(), + #[cfg(feature = "threads")] + pool, + } + } + + /// Statistics accumulated so far. + pub fn stats(&self) -> DecodeStats { + self.stats.clone() + } + + /// Worker threads in use (1: everything runs on the calling thread). + pub fn threads(&self) -> usize { + if self.frame_threads() { self.threads } else { 1 } + } + + /// Draft mode for reduced-resolution playback (off by default): frames that no other frame + /// can reference (`refresh_frame_flags` 0) skip the loop filter and come out flagged + /// [`Picture::draft`]. Every other picture is unchanged: the loop filter changes neither the + /// mode info, segmentation map nor probabilities that later frames read. + pub fn set_draft(&mut self, on: bool) { + self.draft = on; + } + + /// Decode one chunk (a frame or a superframe, as stored in one IVF / WebM / MP4 sample). + /// Returns the pictures finished so far, in order; each carries the `pts` of the chunk that + /// showed it. With frame threads a picture may come out of a later call or [`Self::flush`]. + pub fn decode(&mut self, data: &[u8], pts: i64) -> Result> { + let frames = split_superframe(data); + if frames.len() > 1 { + self.stats.superframes += 1; + } + for f in frames { + if f.is_empty() { + continue; + } + self.decode_frame(f, pts)?; + } + Ok(self.drain(false)) + } + + /// End of stream: every picture still in flight. + pub fn flush(&mut self) -> Vec { + let out = self.drain(true); + #[cfg(all(feature = "threads", not(target_arch = "wasm32")))] + for j in self.jobs.drain(..) { + let _ = j.join(); + } + out + } + + /// Forget all decoding state (references, probability contexts, segmentation) and pictures + /// in flight, e.g. before decoding from another key frame after a seek. The thread pool and + /// statistics are kept. + pub fn reset(&mut self) { + self.st = HeaderState::default(); + self.slots = Default::default(); + self.prev_mi = None; + self.prev_seg_ids = Vec::new(); + self.last_size = None; + self.last_show_frame = false; + self.pending.clear(); + } + + fn frame_threads(&self) -> bool { + #[cfg(all(feature = "threads", not(target_arch = "wasm32")))] + { + self.pool.is_some() + } + #[cfg(not(all(feature = "threads", not(target_arch = "wasm32"))))] + { + false + } + } + + /// Pictures at the front of the queue that are finished (`all`: wait for every one). The + /// queue is kept short: beyond a few pictures in flight the oldest is waited for. + fn drain(&mut self, all: bool) -> Vec { + let max_lag = (self.threads / 2).clamp(2, 6); + let mut out = Vec::new(); + loop { + let must = all || self.pending.len() > max_lag; + let Some(front) = self.pending.front_mut() else { break }; + let pic = match front { + Pending::Ready(_) => match self.pending.pop_front() { + Some(Pending::Ready(p)) => Some(p), + _ => None, + }, + #[cfg(all(feature = "threads", not(target_arch = "wasm32")))] + Pending::Job(rx) => { + let p = if must { + rx.recv().ok() + } else { + match rx.try_recv() { + Ok(p) => Some(p), + Err(std::sync::mpsc::TryRecvError::Empty) => break, + Err(std::sync::mpsc::TryRecvError::Disconnected) => None, + } + }; + self.pending.pop_front(); + p + } + Pending::Existing(f, pts, draft) => { + if !must && !f.is_complete() { + break; + } + let p = picture_of(f, *pts, *draft); + self.pending.pop_front(); + Some(p) + } + }; + out.extend(pic); + } + out + } + + fn decode_frame(&mut self, data: &[u8], pts: i64) -> Result<()> { + let refs: [Option; 8] = + std::array::from_fn(|i| self.slots[i].as_ref().map(|f| RefInfo { width: f.info.width, height: f.info.height })); + let mut h = parse_uncompressed(data, &mut self.st, &refs)?; + if h.show_existing_frame { + self.stats.show_existing += 1; + let f = self.slots[h.frame_to_show_map_idx as usize] + .clone() + .ok_or_else(|| Error::MissingReference(format!("show_existing_frame of empty slot {}", h.frame_to_show_map_idx)))?; + self.stats.shown += 1; + self.pending.push_back(Pending::Existing(f, pts, false)); + return Ok(()); + } + ensure!(h.width <= 16384 && h.height <= 16384, "frame size {}x{} too large", h.width, h.height); + let (mi_rows, mi_cols) = (h.mi_rows as usize, h.mi_cols as usize); + // compute_image_size semantics (7.2.6). + let size = (h.width, h.height); + let size_changed = self.last_size != Some(size); + if size_changed && self.last_size.is_some() { + self.stats.size_changes += 1; + } + if self.st.reset_segment_map || size_changed || self.prev_seg_ids.len() != mi_rows * mi_cols { + self.prev_seg_ids = vec![0; mi_rows * mi_cols]; + self.st.reset_segment_map = false; + } + let use_prev = !size_changed + && self.last_show_frame + && !h.error_resilient_mode + && !h.frame_is_intra + && self.prev_mi.as_ref().is_some_and(|m| m.rows == mi_rows && m.cols == mi_cols); + self.last_size = Some(size); + self.last_show_frame = h.show_frame; + + let off = h.uncompressed_size; + let hs = h.header_size_in_bytes as usize; + ensure!(off + hs <= data.len(), "compressed header exceeds frame data"); + let mut fc = self.st.contexts[h.frame_context_idx as usize].clone(); + parse_compressed(&data[off..off + hs], &mut h, &mut fc)?; + self.count_frame(&h); + + // References (8.5.2.3). + let mut refs: [Option; 3] = [None, None, None]; + let ref_frames: Vec>> = + (0..3).map(|i| if h.frame_is_intra { None } else { self.slots[h.ref_frame_idx[i] as usize].clone() }).collect(); + if !h.frame_is_intra { + for i in 0..3 { + let Some(rf) = &ref_frames[i] else { + return Err(Error::MissingReference(format!("reference slot {} is empty", h.ref_frame_idx[i]))); + }; + let ri = &rf.info; + ensure!( + ri.bit_depth == h.color.bit_depth && ri.ss_x == h.color.subsampling_x && ri.ss_y == h.color.subsampling_y, + "reference frame format differs from the current frame" + ); + let (rw, rh) = (ri.width as i64, ri.height as i64); + let (w, hh) = (h.width as i64, h.height as i64); + if 2 * w >= rw && 2 * hh >= rh && w <= 16 * rw && hh <= 16 * rh { + let x_scale = ((rw << 14) / w) as i32; + let y_scale = ((rh << 14) / hh) as i32; + refs[i] = Some(RefUse { frame: rf, x_scale, y_scale, x_step: (16 * x_scale) >> 14, y_step: (16 * y_scale) >> 14 }); + } + } + } + // Quantizers per segment (8.6.1). + let bd_idx = ((h.color.bit_depth - 8) >> 1) as usize; + let seg = self.st.seg.clone(); + let mut seg_q = [[[0i32; 2]; 2]; 8]; + for (s, q) in seg_q.iter_mut().enumerate() { + let qindex = if seg.feature_active(s as u8, SEG_LVL_ALT_Q) { + let d = seg.feature_data[s][SEG_LVL_ALT_Q] as i32; + (if seg.abs_or_delta_update { d } else { h.base_q_idx as i32 + d }).clamp(0, 255) + } else { + h.base_q_idx as i32 + }; + let dc = |b: i32| DC_QLOOKUP[bd_idx * 256 + b.clamp(0, 255) as usize]; + let ac = |b: i32| AC_QLOOKUP[bd_idx * 256 + b.clamp(0, 255) as usize]; + q[0] = [dc(qindex + h.delta_q_y_dc as i32), ac(qindex)]; + q[1] = [dc(qindex + h.delta_q_uv_dc as i32), ac(qindex + h.delta_q_uv_ac as i32)]; + } + // Tile layout and data (6.4). + let tile_cols = 1usize << h.tile_cols_log2; + let tile_rows = 1usize << h.tile_rows_log2; + let mut pos = off + hs; + let mut tile_data: Vec> = vec![Vec::with_capacity(tile_rows); tile_cols]; + for tr in 0..tile_rows { + for (tc, td) in tile_data.iter_mut().enumerate() { + let last = tr == tile_rows - 1 && tc == tile_cols - 1; + let sz = if last { + data.len() - pos + } else { + let Some(&b) = data.get(pos..).and_then(|d| d.first_chunk::<4>()) else { + return Err(Error::Invalid("tile size beyond frame data".into())); + }; + let s = u32::from_be_bytes(b) as usize; + pos += 4; + ensure!(s <= data.len() - pos, "tile size {s} beyond frame data"); + s + }; + td.push(&data[pos..pos + sz]); + pos += sz; + } + } + let counting = !h.error_resilient_mode && !h.frame_parallel_decoding_mode; + let prev_mi = if use_prev { self.prev_mi.clone() } else { None }; + let shared = FrameShared { + h: &h, + fc: &fc, + seg: &seg, + prev_seg_ids: &self.prev_seg_ids, + prev_mi: prev_mi.as_deref(), + refs, + seg_q, + counting, + pools: &self.bufs, + }; + let col_bounds: Vec<(usize, usize)> = + (0..tile_cols).map(|i| (tile_offset(i, mi_cols, h.tile_cols_log2), tile_offset(i + 1, mi_cols, h.tile_cols_log2))).collect(); + let row_bounds: Vec<(usize, usize)> = + (0..tile_rows).map(|i| (tile_offset(i, mi_rows, h.tile_rows_log2), tile_offset(i + 1, mi_rows, h.tile_rows_log2))).collect(); + let decode_col = |ci: usize| -> (Strip, Option) { + let (cs, ce) = col_bounds[ci]; + let mut td = TileDecoder::new(&shared, cs, ce); + let mut err = None; + if ce > cs { + for (ri, &(rs, re)) in row_bounds.iter().enumerate() { + if !td.decode_tile(tile_data[ci][ri], rs, re) { + err = Some(Error::Invalid("tile data exhausted".into())); + break; + } + } + } + let e = td.error.take().or(err); + (td.strip, e) + }; + let results: Vec<(Strip, Option)> = self.run_parallel(tile_cols, &decode_col); + let mut counts = Counts::default(); + let mut strips = Vec::with_capacity(results.len()); + let mut error = None; + for (s, e) in results { + if let Some(e) = e { + error.get_or_insert(e); + } + if counting { + counts.add(&s.counts); + } + self.stats.compound_blocks += s.compound_blocks; + self.stats.scaled_ref_blocks += s.scaled_blocks; + self.stats.intra_blocks += s.intra_blocks; + self.stats.inter_blocks += s.inter_blocks; + strips.push(s); + } + if let Some(e) = error { + recycle_strips(strips, &self.bufs); + return Err(e); + } + // Release the references before the slots are refreshed, so that evicted frames can be + // recycled. + drop(ref_frames); + drop(prev_mi); + let (mi, seg_ids) = assemble_mi(&mut strips, mi_rows, mi_cols, &self.bufs); + let mi = Arc::new(mi); + let frame = Arc::new(Frame::new( + FrameInfo { + width: h.width, + height: h.height, + ss_x: h.color.subsampling_x, + ss_y: h.color.subsampling_y, + bit_depth: h.color.bit_depth, + color_space: h.color.color_space, + color_range: h.color.color_range, + render_width: h.render_width, + render_height: h.render_height, + key: h.frame_type == KEY_FRAME, + intra_only: h.intra_only, + }, + self.bufs.clone(), + )); + // Loop filter (8.8), skipped in draft mode for frames nothing can reference. + let draft = self.draft && h.refresh_frame_flags == 0 && h.show_frame && self.st.lf.level > 0; + self.stats.draft_frames += draft as u64; + let lf = (self.st.lf.level > 0 && !draft) + .then(|| LfFrame::new(&self.st.lf, &seg, mi_rows, mi_cols, h.color.subsampling_x, h.color.subsampling_y, h.color.bit_depth)); + let job = PostJob { frame: frame.clone(), strips, mi: mi.clone(), lf, out: h.show_frame.then_some((pts, draft)), pools: self.bufs.clone() }; + self.run_post(job); + // refresh_probs (6.1.2). + if counting { + let saved = &self.st.contexts[h.frame_context_idx as usize]; + let mut a = saved.clone(); + a.tx = fc.tx; + a.skip = fc.skip; + adapt_coef_probs(&mut a, &counts, h.frame_is_intra, self.st.last_frame_type == KEY_FRAME); + if !h.frame_is_intra { + a.tx = saved.tx; + a.skip = saved.skip; + adapt_noncoef_probs(&mut a, &counts, h.interp_filter == SWITCHABLE, h.tx_mode == TX_MODE_SELECT, h.allow_high_precision_mv); + } + fc = a; + } + if h.refresh_frame_context { + self.st.contexts[h.frame_context_idx as usize] = fc; + } + if seg.enabled && seg.update_map { + self.prev_seg_ids = seg_ids; + } + for i in 0..8 { + if (h.refresh_frame_flags >> i) & 1 == 1 { + self.slots[i] = Some(frame.clone()); + } + } + if let Some(old) = self.prev_mi.replace(mi) + && let Ok(old) = Arc::try_unwrap(old) + { + self.bufs.mi.put(old.mi); + } + if h.show_frame { + self.stats.shown += 1; + } else { + self.stats.hidden += 1; + } + Ok(()) + } + + /// Run the post stage of a frame: on its own thread with frame threads (later frames wait + /// per band for the samples they read), else right here. + fn run_post(&mut self, job: PostJob) { + #[cfg(all(feature = "threads", not(target_arch = "wasm32")))] + let job = if self.frame_threads() { + // Bound the post stages in flight (each is one thread). + while self.jobs.front().is_some_and(|j| j.is_finished()) { + let _ = self.jobs.pop_front().map(|j| j.join()); + } + let max_jobs = (self.threads / 3).clamp(2, 6); + while self.jobs.len() >= max_jobs { + let _ = self.jobs.pop_front().map(|j| j.join()); + } + let shown = job.out.is_some(); + let (tx, rx) = std::sync::mpsc::channel(); + // The job reaches the thread over a channel, so it is still here if no thread can + // be spawned and then runs inline below. + let (job_tx, job_rx) = std::sync::mpsc::channel::(); + let spawned = std::thread::Builder::new().name("vp9-post".into()).spawn(move || { + if let Ok(job) = job_rx.recv() + && let Some(p) = job.run() + { + let _ = tx.send(p); + } + }); + match spawned { + Ok(handle) => match job_tx.send(job) { + Ok(()) => { + self.jobs.push_back(handle); + if shown { + self.pending.push_back(Pending::Job(rx)); + } + return; + } + // The thread is gone already: take the job back and run it here. + Err(std::sync::mpsc::SendError(back)) => back, + }, + // No thread available: run it here. + Err(_) => job, + } + } else { + job + }; + if let Some(p) = job.run() { + self.pending.push_back(Pending::Ready(p)); + } + } + + fn count_frame(&mut self, h: &FrameHeader) { + let s = &mut self.stats; + s.frames += 1; + if h.frame_type == KEY_FRAME { + s.key_frames += 1; + } else if h.intra_only { + s.intra_only += 1; + } else { + s.inter_frames += 1; + } + s.error_resilient += h.error_resilient_mode as u64; + s.no_backward_adaptation += (h.error_resilient_mode || h.frame_parallel_decoding_mode) as u64; + s.lossless_frames += h.lossless as u64; + s.max_tile_cols = s.max_tile_cols.max(1 << h.tile_cols_log2); + s.max_tile_rows = s.max_tile_rows.max(1 << h.tile_rows_log2); + s.segmentation_frames += self.st.seg.enabled as u64; + s.tx_select_frames += (h.tx_mode == TX_MODE_SELECT) as u64; + s.switchable_interp_frames += (!h.frame_is_intra && h.interp_filter == SWITCHABLE) as u64; + s.high_precision_mv_frames += (!h.frame_is_intra && h.allow_high_precision_mv) as u64; + s.compound_frames += (!h.frame_is_intra && h.reference_mode != 0) as u64; + s.bit_depths[((h.color.bit_depth - 8) >> 1) as usize] += 1; + s.profiles[h.profile as usize] += 1; + } + + /// Run `f(0..n)`, in parallel when a pool is available. + fn run_parallel(&self, n: usize, f: &(dyn Fn(usize) -> T + Sync)) -> Vec { + #[cfg(feature = "threads")] + if let Some(pool) = &self.pool + && n > 1 + { + use rayon::prelude::*; + return pool.install(|| (0..n).into_par_iter().map(f).collect()); + } + (0..n).map(f).collect() + } +} + +impl Drop for Decoder { + fn drop(&mut self) { + // Post jobs own everything they use; let them finish rather than leaving threads behind. + #[cfg(all(feature = "threads", not(target_arch = "wasm32")))] + for j in self.jobs.drain(..) { + let _ = j.join(); + } + } +} + +fn recycle_strips(strips: Vec, pools: &Pools) { + for s in strips { + for p in s.planes { + pools.samples.put(p); + } + pools.mi.put(s.mi); + } +} + +/// The post stage of one frame: build each band from the tile column strips, loop filter it +/// (superblocks in raster order, so the result is the specification's), publish it once the +/// next band's filtering can no longer change it, and copy it into the output picture. +struct PostJob { + frame: Arc, + strips: Vec, + mi: Arc, + lf: Option, + /// (pts, draft) when the frame is shown. + out: Option<(i64, bool)>, + pools: Arc, +} + +/// Publishes placeholder bands if a post job dies, so that waiting frames cannot hang. +struct PublishGuard<'a>(&'a Frame); + +impl Drop for PublishGuard<'_> { + fn drop(&mut self) { + for r in 0..self.0.sb_rows() { + if !self.0.is_published(r) { + self.0.publish(r, self.0.new_band()); + } + } + } +} + +impl PostJob { + fn run(self) -> Option { + let f = &*self.frame; + let _guard = PublishGuard(f); + let mut pic = self.out.map(|(pts, draft)| PictureBuilder::new(f, pts, draft)); + let sb_rows = f.sb_rows(); + let mut held: Option = None; + for r in 0..sb_rows { + let mut band = f.new_band(); + for p in 0..3 { + let (fs, bh) = (f.strides[p], f.band_h[p]); + let dst = &mut band.planes[p]; + for s in &self.strips { + let ss = s.strides[p]; + let x0 = s.x_off[p]; + let w = ss.min(fs - x0); + for k in 0..bh { + let y = r * bh + k; + let d = (BAND_PAD + k) * fs + x0; + dst[d..d + w].copy_from_slice(&s.planes[p][y * ss..y * ss + w]); + } + } + } + if let Some(lf) = &self.lf { + if let Some(prev) = &held { + // The bottom rows of the previous band, which this row's filter may change. + for p in 0..3 { + let (fs, bh) = (f.strides[p], f.band_h[p]); + band.planes[p][..BAND_PAD * fs].copy_from_slice(&prev.planes[p][bh * fs..(bh + BAND_PAD) * fs]); + } + } + let [a, b, c] = &mut band.planes; + let mut views = [band_view(f, a, 0, r), band_view(f, b, 1, r), band_view(f, c, 2, r)]; + for c in 0..lf.mi_cols.div_ceil(8) { + filter_superblock(&mut views, &self.mi, lf, r * 8, c * 8); + } + if let Some(prev) = &mut held { + for p in 0..3 { + let (fs, bh) = (f.strides[p], f.band_h[p]); + prev.planes[p][bh * fs..(bh + BAND_PAD) * fs].copy_from_slice(&band.planes[p][..BAND_PAD * fs]); + } + } + } + if let Some(prev) = held.take() { + if let Some(pic) = &mut pic { + pic.add(f, r - 1, &prev); + } + f.publish(r - 1, prev); + } + held = Some(band); + } + if let Some(last) = held { + if let Some(pic) = &mut pic { + pic.add(f, sb_rows - 1, &last); + } + f.publish(sb_rows - 1, last); + } + for s in self.strips { + for p in s.planes { + self.pools.samples.put(p); + } + } + pic.map(PictureBuilder::finish) + } +} + +/// The loop filter's view of band `r` of plane `p`: the band's rows plus (below row 0) the +/// scratch rows holding the bottom of band r - 1. +fn band_view<'a>(f: &Frame, d: &'a mut [u16], p: usize, r: usize) -> PlaneView<'a> { + let (fs, bh) = (f.strides[p], f.band_h[p]); + if r == 0 { + PlaneView { data: &mut d[BAND_PAD * fs..], stride: fs, ox: 0, oy: 0 } + } else { + PlaneView { data: d, stride: fs, ox: 0, oy: r * bh - BAND_PAD } + } +} + +/// Mode info and segment ids of the whole frame from the tile column strips (whose mode info +/// buffers go back to the pool). +fn assemble_mi(strips: &mut [Strip], mi_rows: usize, mi_cols: usize, pools: &Pools) -> (MiGrid, Vec) { + if strips.len() == 1 { + let s = &mut strips[0]; + return (MiGrid { cols: mi_cols, rows: mi_rows, mi: std::mem::take(&mut s.mi) }, std::mem::take(&mut s.seg_ids)); + } + let mut mi = pools.mi.take(mi_rows * mi_cols, MiInfo::default()); + let mut seg = vec![0u8; mi_rows * mi_cols]; + for s in strips.iter_mut() { + let mw = s.mi_col_end - s.mi_col_start; + for r in 0..mi_rows { + mi[r * mi_cols + s.mi_col_start..r * mi_cols + s.mi_col_end].copy_from_slice(&s.mi[r * mw..r * mw + mw]); + seg[r * mi_cols + s.mi_col_start..r * mi_cols + s.mi_col_end].copy_from_slice(&s.seg_ids[r * mw..r * mw + mw]); + } + pools.mi.put(std::mem::take(&mut s.mi)); + } + (MiGrid { cols: mi_cols, rows: mi_rows, mi }, seg) +} + +/// Builds the cropped output picture band by band (narrowing 8-bit samples). +struct PictureBuilder { + planes: [Plane; 3], + info: FrameInfo, + vis: [(usize, usize); 3], + pts: i64, + draft: bool, +} + +impl PictureBuilder { + fn new(f: &Frame, pts: i64, draft: bool) -> Self { + let planes = f.vis.map(|(w, h)| if f.info.bit_depth == 8 { Plane::U8(vec![0; w * h]) } else { Plane::U16(vec![0; w * h]) }); + PictureBuilder { planes, info: f.info, vis: f.vis, pts, draft } + } + + fn add(&mut self, f: &Frame, r: usize, band: &Band) { + for p in 0..3 { + let (w, h) = f.vis[p]; + let (fs, bh) = (f.strides[p], f.band_h[p]); + let y0 = r * bh; + for y in y0..(y0 + bh).min(h) { + let src = &band.planes[p][(y - y0 + BAND_PAD) * fs..][..w]; + match &mut self.planes[p] { + Plane::U8(d) => { + for (d, &s) in d[y * w..y * w + w].iter_mut().zip(src) { + *d = s as u8; + } + } + Plane::U16(d) => d[y * w..y * w + w].copy_from_slice(src), + } + } + } + } + + fn finish(self) -> Picture { + let i = self.info; + let [y, u, v] = self.planes; + Picture { + width: i.width, + height: i.height, + chroma_width: self.vis[1].0 as u32, + chroma_height: self.vis[1].1 as u32, + bit_depth: i.bit_depth as u32, + subsampling_x: i.ss_x, + subsampling_y: i.ss_y, + y, + u, + v, + y_stride: self.vis[0].0, + uv_stride: self.vis[1].0, + pts: self.pts, + key: i.key, + intra_only: i.intra_only, + color: ColorInfo { color_space: i.color_space, full_range: i.color_range }, + render_width: i.render_width, + render_height: i.render_height, + draft: self.draft, + } + } +} + +/// The output picture of a complete (or completing) frame (show_existing_frame). +fn picture_of(f: &Frame, pts: i64, draft: bool) -> Picture { + let mut b = PictureBuilder::new(f, pts, draft); + for r in 0..f.sb_rows() { + b.add(f, r, f.band(r)); + } + b.finish() +} diff --git a/crates/vp9/src/error.rs b/crates/vp9/src/error.rs new file mode 100644 index 0000000..660be1a --- /dev/null +++ b/crates/vp9/src/error.rs @@ -0,0 +1,31 @@ +use slidecraft_bitstream::BitError; + +/// Errors produced by the decoder. +#[derive(Debug, Clone, PartialEq, Eq, thiserror::Error)] +pub enum Error { + /// The uncompressed header ended early. + #[error("bitstream error: {0}")] + Bitstream(#[from] BitError), + /// A syntax element had a value outside its legal range, or a size did not fit the data. + #[error("invalid bitstream: {0}")] + Invalid(String), + /// A valid feature this decoder does not implement. + #[error("unsupported: {0}")] + Unsupported(String), + /// An inter frame referenced a reference slot that was never filled (e.g. decoding started + /// at a non-key frame). + #[error("missing reference frame: {0}")] + MissingReference(String), +} + +pub type Result = std::result::Result; + +/// `ensure!(cond, "message")` returns `Error::Invalid` when `cond` is false. +macro_rules! ensure { + ($cond:expr, $($arg:tt)+) => { + if !$cond { + return Err($crate::error::Error::Invalid(format!($($arg)+))); + } + }; +} +pub(crate) use ensure; diff --git a/crates/vp9/src/frame.rs b/crates/vp9/src/frame.rs new file mode 100644 index 0000000..aaf881c --- /dev/null +++ b/crates/vp9/src/frame.rs @@ -0,0 +1,302 @@ +//! Decoded frame storage and the per-8x8 mode info grid. + +use crate::tables::{INTRA_FRAME, NONE}; + +/// A motion vector in 1/8 sample units. +#[derive(Clone, Copy, Debug, Default, PartialEq, Eq)] +pub struct Mv { + pub row: i16, + pub col: i16, +} + +impl Mv { + pub const ZERO: Mv = Mv { row: 0, col: 0 }; + pub fn new(row: i32, col: i32) -> Mv { + Mv { row: row as i16, col: col as i16 } + } +} + +/// Mode info of one 8x8 block (the arrays of 6.4.4: Skips, TxSizes, MiSizes, YModes, SubModes, +/// RefFrames, InterpFilters, Mvs / SubMvs). +#[derive(Clone, Copy, Debug)] +pub struct MiInfo { + pub sb_size: u8, + pub skip: bool, + pub tx_size: u8, + pub y_mode: u8, + pub sub_modes: [u8; 4], + pub seg_id: u8, + pub ref_frame: [i8; 2], + pub interp_filter: u8, + /// SubMvs[refList][b]; Mvs[refList] is `mv[refList][3]`. + pub mv: [[Mv; 4]; 2], +} + +impl Default for MiInfo { + fn default() -> Self { + MiInfo { + sb_size: 0, + skip: false, + tx_size: 0, + y_mode: 0, + sub_modes: [0; 4], + seg_id: 0, + ref_frame: [INTRA_FRAME, NONE], + interp_filter: 0, + mv: [[Mv::ZERO; 4]; 2], + } + } +} + +/// Mode info of a whole frame (MiRows x MiCols). +#[derive(Clone, Debug, Default)] +pub struct MiGrid { + pub cols: usize, + pub rows: usize, + pub mi: Vec, +} + +impl MiGrid { + #[inline] + pub fn at(&self, r: usize, c: usize) -> &MiInfo { + &self.mi[r * self.cols + c] + } +} + +/// Scratch rows above the samples of every band: the loop filter of superblock row r reads 8 +/// and modifies up to 7 rows of row r - 1 (the 16-wide filter of the horizontal edges on top of +/// row r), so those rows are copied there while row r is filtered (see `decoder::post`). +pub const BAND_PAD: usize = 8; + +/// One superblock row (64 luma rows) of a frame: per plane `BAND_PAD` scratch rows followed by +/// the band's rows, all `Frame::strides[p]` samples wide. +pub struct Band { + pub planes: [Vec; 3], +} + +/// Frame metadata from the frame header. +#[derive(Clone, Copy, Debug)] +pub struct FrameInfo { + pub width: u32, + pub height: u32, + pub ss_x: bool, + pub ss_y: bool, + pub bit_depth: u8, + pub color_space: u8, + pub color_range: bool, + pub render_width: u32, + pub render_height: u32, + pub key: bool, + pub intra_only: bool, +} + +/// A decoded frame (also used as reference). Its samples are published one superblock row +/// ("band") at a time, once loop filtered, so that later frames can predict from the finished +/// top of a frame while its bottom is still being filtered (frame threading). +pub struct Frame { + pub info: FrameInfo, + /// Allocated plane widths (whole superblocks) = row strides. + pub strides: [usize; 3], + /// Rows per band of each plane. + pub band_h: [usize; 3], + /// Visible (cropped) plane sizes. + pub vis: [(usize, usize); 3], + bands: Box<[std::sync::OnceLock]>, + pools: std::sync::Arc, +} + +impl Frame { + pub fn new(info: FrameInfo, pools: std::sync::Arc) -> Frame { + let geo = plane_geometry(info.width, info.height, info.ss_x, info.ss_y); + let sb_rows = (info.height as usize).div_ceil(64); + Frame { + info, + strides: geo.map(|g| g.0), + band_h: [64, 64 >> info.ss_y as usize, 64 >> info.ss_y as usize], + vis: geo.map(|g| (g.2, g.3)), + bands: (0..sb_rows).map(|_| std::sync::OnceLock::new()).collect(), + pools, + } + } + + pub fn sb_rows(&self) -> usize { + self.bands.len() + } + + /// A band buffer for this frame's geometry (recycled memory, contents unspecified). + pub fn new_band(&self) -> Band { + Band { planes: std::array::from_fn(|p| self.pools.bands.take_any((self.band_h[p] + BAND_PAD) * self.strides[p], 0)) } + } + + /// Publish band `r` (ignored if already published). + pub fn publish(&self, r: usize, band: Band) { + if let Err(b) = self.bands[r].set(band) { + for p in b.planes { + self.pools.bands.put(p); + } + } + } + + pub fn is_published(&self, r: usize) -> bool { + self.bands[r].get().is_some() + } + + pub fn is_complete(&self) -> bool { + self.bands.iter().all(|b| b.get().is_some()) + } + + /// Band `r`, waiting until it has been published. + #[inline] + pub fn band(&self, r: usize) -> &Band { + #[cfg(feature = "threads")] + { + self.bands[r].wait() + } + #[cfg(not(feature = "threads"))] + { + // Bands are decoded in order before use; an unpublished one reads as empty. + static EMPTY: Band = Band { planes: [Vec::new(), Vec::new(), Vec::new()] }; + self.bands[r].get().unwrap_or(&EMPTY) + } + } + + /// Row `y` of plane `p` (all `strides[p]` samples), waiting until it is final. + #[inline] + pub fn row(&self, p: usize, y: usize) -> &[u16] { + let bh = self.band_h[p]; + let s = self.strides[p]; + let o = (y % bh + BAND_PAD) * s; + &self.band(y / bh).planes[p][o..o + s] + } + + /// Rows `y0..y1` of plane `p` as one slice with stride `strides[p]` when they lie in one band: + /// (band samples, offset of row y0). + #[inline] + pub fn span(&self, p: usize, y0: usize, y1: usize) -> Option<(&[u16], usize)> { + let bh = self.band_h[p]; + let r = y0 / bh; + if (y1 - 1) / bh != r { + return None; + } + Some((&self.band(r).planes[p], (y0 % bh + BAND_PAD) * self.strides[p])) + } + + /// A complete frame from full planes (tests). + #[cfg(test)] + pub fn from_planes(info: FrameInfo, planes: [&[u16]; 3]) -> Frame { + let f = Frame::new(info, Default::default()); + for r in 0..f.sb_rows() { + let mut b = f.new_band(); + for p in 0..3 { + let (s, bh) = (f.strides[p], f.band_h[p]); + for k in 0..bh { + let y = r * bh + k; + if let Some(src) = planes[p].get(y * s..y * s + s) { + b.planes[p][(k + BAND_PAD) * s..(k + BAND_PAD + 1) * s].copy_from_slice(src); + } + } + } + f.publish(r, b); + } + f + } +} + +impl Drop for Frame { + fn drop(&mut self) { + for b in std::mem::take(&mut self.bands).into_vec().into_iter().filter_map(|b| b.into_inner()) { + for p in b.planes { + self.pools.bands.put(p); + } + } + } +} + +/// Geometry of the frame planes: allocation sizes for (MiCols, MiRows) rounded to superblocks. +pub fn plane_geometry(width: u32, height: u32, ss_x: bool, ss_y: bool) -> [(usize, usize, usize, usize); 3] { + let sb_cols = (width as usize).div_ceil(64); + let sb_rows = (height as usize).div_ceil(64); + let (aw, ah) = (sb_cols * 64, sb_rows * 64); + let (sx, sy) = (ss_x as usize, ss_y as usize); + let cw = (width as usize + sx) >> sx; + let ch = (height as usize + sy) >> sy; + [(aw, ah, width as usize, height as usize), (aw >> sx, ah >> sy, cw, ch), (aw >> sx, ah >> sy, cw, ch)] +} + +/// Recycled buffers (strip / frame planes, mode info), so that steady-state decoding does not +/// allocate (and page-fault) megabytes per frame. +pub struct Pool { + bufs: std::sync::Mutex>>, + max: usize, +} + +impl Default for Pool { + fn default() -> Self { + Pool::with_max(32) + } +} + +impl Pool { + /// A pool keeping at most `max` buffers. + pub fn with_max(max: usize) -> Self { + Pool { bufs: Default::default(), max } + } +} + +impl Pool { + /// A buffer of `len` elements with unspecified contents (recycled memory when available; + /// `fill` only initialises fresh memory). + pub fn take_any(&self, len: usize, fill: T) -> Vec { + let found = { + let mut b = self.bufs.lock().unwrap_or_else(std::sync::PoisonError::into_inner); + b.iter().position(|v| v.len() >= len).map(|i| b.swap_remove(i)) + }; + match found { + Some(mut v) => { + v.truncate(len); + v + } + None => vec![fill; len], + } + } + + /// A buffer of `len` copies of `fill` (recycled memory when available). + pub fn take(&self, len: usize, fill: T) -> Vec { + let found = { + let mut b = self.bufs.lock().unwrap_or_else(std::sync::PoisonError::into_inner); + b.iter().position(|v| v.capacity() >= len).map(|i| b.swap_remove(i)) + }; + match found { + Some(mut v) => { + v.clear(); + v.resize(len, fill); + v + } + None => vec![fill; len], + } + } + + pub fn put(&self, v: Vec) { + if v.capacity() == 0 { + return; + } + let mut b = self.bufs.lock().unwrap_or_else(std::sync::PoisonError::into_inner); + if b.len() < self.max { + b.push(v); + } + } +} + +/// The decoder's buffer pools. +pub struct Pools { + pub samples: Pool, + /// Frame bands (many small buffers per frame). + pub bands: Pool, + pub mi: Pool, +} + +impl Default for Pools { + fn default() -> Self { + Pools { samples: Pool::default(), bands: Pool::with_max(1024), mi: Pool::default() } + } +} diff --git a/crates/vp9/src/header.rs b/crates/vp9/src/header.rs new file mode 100644 index 0000000..8cbf9c3 --- /dev/null +++ b/crates/vp9/src/header.rs @@ -0,0 +1,661 @@ +//! Uncompressed header (6.2) and compressed header (6.3) parsing, plus the state that persists +//! between frames (loop filter deltas, segmentation parameters, colour configuration). + +use crate::boolcoder::BoolDecoder; +use crate::error::{Error, Result, ensure}; +use crate::probs::FrameContext; +use crate::tables::*; +use slidecraft_bitstream::BitReader; + +pub const KEY_FRAME: u8 = 0; + +/// Loop filter parameters (6.2.8); the deltas persist across frames. +#[derive(Clone, Debug, Default, PartialEq, Eq)] +pub struct LoopFilterParams { + pub level: u8, + pub sharpness: u8, + pub delta_enabled: bool, + pub ref_deltas: [i8; 4], + pub mode_deltas: [i8; 2], +} + +/// Segmentation parameters (6.2.11); feature data persists across frames. +#[derive(Clone, Debug, Default, PartialEq, Eq)] +pub struct Segmentation { + pub enabled: bool, + pub update_map: bool, + pub tree_probs: [u8; 7], + pub pred_probs: [u8; 3], + pub temporal_update: bool, + pub abs_or_delta_update: bool, + pub feature_enabled: [[bool; 4]; 8], + pub feature_data: [[i16; 4]; 8], +} + +impl Segmentation { + #[inline] + pub fn feature_active(&self, segment_id: u8, feature: usize) -> bool { + self.enabled && self.feature_enabled[segment_id as usize][feature] + } +} + +/// Colour configuration (6.2.2); persists for inter frames. +#[derive(Clone, Copy, Debug, PartialEq, Eq)] +pub struct ColorConfig { + pub bit_depth: u8, + pub color_space: u8, + pub color_range: bool, + pub subsampling_x: bool, + pub subsampling_y: bool, +} + +impl Default for ColorConfig { + fn default() -> Self { + ColorConfig { bit_depth: 8, color_space: 0, color_range: false, subsampling_x: true, subsampling_y: true } + } +} + +/// Everything read from the headers of one frame. +#[derive(Clone, Debug, Default)] +pub struct FrameHeader { + pub profile: u8, + pub show_existing_frame: bool, + pub frame_to_show_map_idx: u8, + pub frame_type: u8, + pub show_frame: bool, + pub error_resilient_mode: bool, + pub intra_only: bool, + pub reset_frame_context: u8, + pub color: ColorConfig, + pub refresh_frame_flags: u8, + pub ref_frame_idx: [u8; 3], + /// Indexed by reference frame (LAST_FRAME..ALTREF_FRAME); index 0 unused. + pub ref_frame_sign_bias: [bool; 4], + pub width: u32, + pub height: u32, + pub render_width: u32, + pub render_height: u32, + pub allow_high_precision_mv: bool, + pub interp_filter: u8, + pub refresh_frame_context: bool, + pub frame_parallel_decoding_mode: bool, + pub frame_context_idx: u8, + pub base_q_idx: u8, + pub delta_q_y_dc: i8, + pub delta_q_uv_dc: i8, + pub delta_q_uv_ac: i8, + pub lossless: bool, + pub tile_cols_log2: u32, + pub tile_rows_log2: u32, + pub header_size_in_bytes: u32, + /// Size of the uncompressed header in bytes. + pub uncompressed_size: usize, + // Compressed header. + pub tx_mode: u8, + pub reference_mode: u8, + pub comp_fixed_ref: i8, + pub comp_var_ref: [i8; 2], + // Derived. + pub mi_cols: u32, + pub mi_rows: u32, + pub sb64_cols: u32, + pub sb64_rows: u32, + pub frame_is_intra: bool, + /// The frame size was taken from reference `found_ref` (for statistics). + pub size_from_ref: bool, +} + +/// Reference slot information needed while parsing (frame_size_with_refs). +#[derive(Clone, Copy, Debug)] +pub struct RefInfo { + pub width: u32, + pub height: u32, +} + +/// State carried between frames that header parsing reads and updates. +pub struct HeaderState { + pub contexts: [FrameContext; 4], + pub lf: LoopFilterParams, + pub seg: Segmentation, + pub color: ColorConfig, + pub last_frame_type: u8, + pub frame_type: u8, + /// Set by setup_past_independence; cleared once consumed by the decoder (PrevSegmentIds reset). + pub reset_segment_map: bool, +} + +impl Default for HeaderState { + fn default() -> Self { + let fc = FrameContext::default(); + HeaderState { + contexts: [fc.clone(), fc.clone(), fc.clone(), fc], + lf: LoopFilterParams { ref_deltas: [1, 0, -1, -1], delta_enabled: true, ..Default::default() }, + seg: Segmentation::default(), + color: ColorConfig::default(), + last_frame_type: KEY_FRAME, + frame_type: KEY_FRAME, + reset_segment_map: false, + } + } +} + +fn read_s(r: &mut BitReader, bits: u32) -> Result { + let v = r.read_bits(bits)? as i32; + Ok(if r.read_flag()? { -v } else { v }) +} + +fn read_delta_q(r: &mut BitReader) -> Result { + Ok(if r.read_flag()? { read_s(r, 4)? as i8 } else { 0 }) +} + +fn read_prob(r: &mut BitReader) -> Result { + Ok(if r.read_flag()? { r.read_bits(8)? as u8 } else { 255 }) +} + +fn frame_sync_code(r: &mut BitReader) -> Result<()> { + let (a, b, c) = (r.read_bits(8)?, r.read_bits(8)?, r.read_bits(8)?); + ensure!(a == 0x49 && b == 0x83 && c == 0x42, "invalid frame sync code"); + Ok(()) +} + +fn color_config(r: &mut BitReader, profile: u8) -> Result { + let bit_depth = if profile >= 2 { if r.read_flag()? { 12 } else { 10 } } else { 8 }; + let color_space = r.read_bits(3)? as u8; + let mut c = ColorConfig { bit_depth, color_space, color_range: true, subsampling_x: true, subsampling_y: true }; + if color_space != 7 { + c.color_range = r.read_flag()?; + if profile == 1 || profile == 3 { + c.subsampling_x = r.read_flag()?; + c.subsampling_y = r.read_flag()?; + ensure!(!(c.subsampling_x && c.subsampling_y), "4:2:0 subsampling in profile {profile}"); + ensure!(!r.read_flag()?, "reserved bit set in color config"); + } + } else { + ensure!(profile == 1 || profile == 3, "RGB color space in profile {profile}"); + c.subsampling_x = false; + c.subsampling_y = false; + ensure!(!r.read_flag()?, "reserved bit set in color config"); + } + Ok(c) +} + +fn frame_size(r: &mut BitReader, h: &mut FrameHeader) -> Result<()> { + h.width = r.read_bits(16)? + 1; + h.height = r.read_bits(16)? + 1; + Ok(()) +} + +fn render_size(r: &mut BitReader, h: &mut FrameHeader) -> Result<()> { + if r.read_flag()? { + h.render_width = r.read_bits(16)? + 1; + h.render_height = r.read_bits(16)? + 1; + } else { + h.render_width = h.width; + h.render_height = h.height; + } + Ok(()) +} + +fn compute_image_size(h: &mut FrameHeader) { + h.mi_cols = (h.width + 7) >> 3; + h.mi_rows = (h.height + 7) >> 3; + h.sb64_cols = (h.mi_cols + 7) >> 3; + h.sb64_rows = (h.mi_rows + 7) >> 3; +} + +/// setup_past_independence (7.2): resets segmentation features, loop filter deltas and the +/// probabilities of `fc`. +fn setup_past_independence(st: &mut HeaderState, fc: &mut FrameContext) { + st.seg.feature_data = [[0; 4]; 8]; + st.seg.feature_enabled = [[false; 4]; 8]; + st.seg.abs_or_delta_update = false; + st.lf.delta_enabled = true; + st.lf.ref_deltas = [1, 0, -1, -1]; + st.lf.mode_deltas = [0, 0]; + st.reset_segment_map = true; + *fc = FrameContext::default(); +} + +/// Parse the uncompressed header (6.2). `refs` gives the sizes of the eight reference slots. +/// Updates the persistent state (probability contexts on reset, loop filter deltas, +/// segmentation, colour configuration). +pub fn parse_uncompressed(data: &[u8], st: &mut HeaderState, refs: &[Option; 8]) -> Result { + let mut r = BitReader::new(data); + let mut h = FrameHeader::default(); + ensure!(r.read_bits(2)? == 2, "invalid frame marker"); + let lo = r.read_bits(1)? as u8; + let hi = r.read_bits(1)? as u8; + h.profile = (hi << 1) | lo; + if h.profile == 3 { + ensure!(r.read_bits(1)? == 0, "reserved bit set after profile"); + } + h.show_existing_frame = r.read_flag()?; + if h.show_existing_frame { + h.frame_to_show_map_idx = r.read_bits(3)? as u8; + h.show_frame = true; + h.uncompressed_size = r.position().div_ceil(8); + return Ok(h); + } + st.last_frame_type = st.frame_type; + h.frame_type = r.read_bits(1)? as u8; + h.show_frame = r.read_flag()?; + h.error_resilient_mode = r.read_flag()?; + if h.frame_type == KEY_FRAME { + frame_sync_code(&mut r)?; + h.color = color_config(&mut r, h.profile)?; + frame_size(&mut r, &mut h)?; + render_size(&mut r, &mut h)?; + h.refresh_frame_flags = 0xff; + h.frame_is_intra = true; + } else { + h.intra_only = if h.show_frame { false } else { r.read_flag()? }; + h.frame_is_intra = h.intra_only; + h.reset_frame_context = if h.error_resilient_mode { 0 } else { r.read_bits(2)? as u8 }; + if h.intra_only { + frame_sync_code(&mut r)?; + h.color = if h.profile > 0 { + color_config(&mut r, h.profile)? + } else { + ColorConfig { bit_depth: 8, color_space: 1, color_range: false, subsampling_x: true, subsampling_y: true } + }; + h.refresh_frame_flags = r.read_bits(8)? as u8; + frame_size(&mut r, &mut h)?; + render_size(&mut r, &mut h)?; + } else { + h.color = st.color; + h.refresh_frame_flags = r.read_bits(8)? as u8; + for i in 0..3 { + h.ref_frame_idx[i] = r.read_bits(3)? as u8; + h.ref_frame_sign_bias[LAST_FRAME as usize + i] = r.read_flag()?; + } + // frame_size_with_refs (6.2.5) + let mut found = false; + for i in 0..3 { + if r.read_flag()? { + let idx = h.ref_frame_idx[i] as usize; + let Some(info) = refs[idx] else { + return Err(Error::MissingReference(format!("frame size from empty slot {idx}"))); + }; + h.width = info.width; + h.height = info.height; + found = true; + break; + } + } + if !found { + frame_size(&mut r, &mut h)?; + } + h.size_from_ref = found; + render_size(&mut r, &mut h)?; + h.allow_high_precision_mv = r.read_flag()?; + // read_interpolation_filter (6.2.7) + h.interp_filter = if r.read_flag()? { SWITCHABLE } else { LITERAL_TO_TYPE[r.read_bits(2)? as usize] }; + } + } + compute_image_size(&mut h); + if !h.error_resilient_mode { + h.refresh_frame_context = r.read_flag()?; + h.frame_parallel_decoding_mode = r.read_flag()?; + } else { + h.refresh_frame_context = false; + h.frame_parallel_decoding_mode = true; + } + h.frame_context_idx = r.read_bits(2)? as u8; + if h.frame_is_intra || h.error_resilient_mode { + let mut fc = FrameContext::default(); + setup_past_independence(st, &mut fc); + if h.frame_type == KEY_FRAME || h.error_resilient_mode || h.reset_frame_context == 3 { + for c in st.contexts.iter_mut() { + *c = fc.clone(); + } + } else if h.reset_frame_context == 2 { + st.contexts[h.frame_context_idx as usize] = fc.clone(); + } + h.frame_context_idx = 0; + } + // loop_filter_params (6.2.8) + st.lf.level = r.read_bits(6)? as u8; + st.lf.sharpness = r.read_bits(3)? as u8; + st.lf.delta_enabled = r.read_flag()?; + if st.lf.delta_enabled && r.read_flag()? { + for i in 0..4 { + if r.read_flag()? { + st.lf.ref_deltas[i] = read_s(&mut r, 6)? as i8; + } + } + for i in 0..2 { + if r.read_flag()? { + st.lf.mode_deltas[i] = read_s(&mut r, 6)? as i8; + } + } + } + // quantization_params (6.2.9) + h.base_q_idx = r.read_bits(8)? as u8; + h.delta_q_y_dc = read_delta_q(&mut r)?; + h.delta_q_uv_dc = read_delta_q(&mut r)?; + h.delta_q_uv_ac = read_delta_q(&mut r)?; + h.lossless = h.base_q_idx == 0 && h.delta_q_y_dc == 0 && h.delta_q_uv_dc == 0 && h.delta_q_uv_ac == 0; + // segmentation_params (6.2.11) + let seg = &mut st.seg; + seg.enabled = r.read_flag()?; + seg.update_map = false; + seg.temporal_update = false; + if seg.enabled { + seg.update_map = r.read_flag()?; + if seg.update_map { + for i in 0..7 { + seg.tree_probs[i] = read_prob(&mut r)?; + } + seg.temporal_update = r.read_flag()?; + for i in 0..3 { + seg.pred_probs[i] = if seg.temporal_update { read_prob(&mut r)? } else { 255 }; + } + } + if r.read_flag()? { + seg.abs_or_delta_update = r.read_flag()?; + for i in 0..MAX_SEGMENTS { + for j in 0..4 { + let mut value = 0i32; + let enabled = r.read_flag()?; + seg.feature_enabled[i][j] = enabled; + if enabled { + value = r.read_bits(SEGMENTATION_FEATURE_BITS[j])? as i32; + if SEGMENTATION_FEATURE_SIGNED[j] && r.read_flag()? { + value = -value; + } + } + seg.feature_data[i][j] = value as i16; + } + } + } + } + // tile_info (6.2.13) + let mut min_log2 = 0; + while (MAX_TILE_WIDTH_B64 << min_log2) < h.sb64_cols { + min_log2 += 1; + } + let mut max_log2 = 1; + while (h.sb64_cols >> max_log2) >= MIN_TILE_WIDTH_B64 { + max_log2 += 1; + } + max_log2 -= 1; + h.tile_cols_log2 = min_log2; + while h.tile_cols_log2 < max_log2 { + if r.read_flag()? { + h.tile_cols_log2 += 1; + } else { + break; + } + } + h.tile_rows_log2 = r.read_bits(1)?; + if h.tile_rows_log2 == 1 { + h.tile_rows_log2 += r.read_bits(1)?; + } + h.header_size_in_bytes = r.read_bits(16)?; + // trailing_bits + h.uncompressed_size = r.position().div_ceil(8); + if h.frame_is_intra { + st.color = h.color; + } + st.frame_type = h.frame_type; + ensure!(h.header_size_in_bytes > 0, "empty compressed header"); + Ok(h) +} + +/// diff_update_prob (6.3.3). +fn diff_update_prob(bd: &mut BoolDecoder, prob: &mut u8) { + if bd.read_bool(252) { + let delta = decode_term_subexp(bd); + *prob = inv_remap_prob(delta, *prob); + } +} + +fn decode_term_subexp(bd: &mut BoolDecoder) -> usize { + if bd.read_literal(1) == 0 { + return bd.read_literal(4) as usize; + } + if bd.read_literal(1) == 0 { + return bd.read_literal(4) as usize + 16; + } + if bd.read_literal(1) == 0 { + return bd.read_literal(5) as usize + 32; + } + let v = bd.read_literal(7) as usize; + if v < 65 { + return v + 64; + } + let bit = bd.read_literal(1) as usize; + (v << 1) - 1 + bit +} + +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) + } +} + +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 +} + +/// update_mv_prob (6.3.17). +fn update_mv_prob(bd: &mut BoolDecoder, prob: &mut u8) { + if bd.read_bool(252) { + *prob = ((bd.read_literal(7) << 1) | 1) as u8; + } +} + +/// Parse the compressed header (6.3) into `h` and the probabilities `fc`. +pub fn parse_compressed(data: &[u8], h: &mut FrameHeader, fc: &mut FrameContext) -> Result<()> { + let mut bd = BoolDecoder::new(data); + // read_tx_mode (6.3.1) + h.tx_mode = if h.lossless { + 0 + } else { + let mut m = bd.read_literal(2) as u8; + if m == 3 { + m += bd.read_literal(1) as u8; + } + m + }; + if h.tx_mode == TX_MODE_SELECT { + for i in 0..2 { + diff_update_prob(&mut bd, &mut fc.tx[1][i][0]); + } + for i in 0..2 { + for j in 0..2 { + diff_update_prob(&mut bd, &mut fc.tx[2][i][j]); + } + } + for i in 0..2 { + for j in 0..3 { + diff_update_prob(&mut bd, &mut fc.tx[3][i][j]); + } + } + } + // read_coef_probs (6.3.7) + let max_tx = TX_MODE_TO_BIGGEST_TX_SIZE[h.tx_mode as usize] as usize; + for tx in 0..=max_tx { + if bd.read_literal(1) == 1 { + for i in 0..2 { + for j in 0..2 { + for k in 0..6 { + let max_l = if k == 0 { 3 } else { 6 }; + for l in 0..max_l { + for m in 0..3 { + diff_update_prob(&mut bd, &mut fc.coef[tx][i][j][k][l][m]); + } + } + } + } + } + } + } + for i in 0..3 { + diff_update_prob(&mut bd, &mut fc.skip[i]); + } + if !h.frame_is_intra { + for i in 0..7 { + for j in 0..3 { + diff_update_prob(&mut bd, &mut fc.inter_mode[i][j]); + } + } + if h.interp_filter == SWITCHABLE { + for j in 0..4 { + for i in 0..2 { + diff_update_prob(&mut bd, &mut fc.interp_filter[j][i]); + } + } + } + for i in 0..4 { + diff_update_prob(&mut bd, &mut fc.is_inter[i]); + } + // frame_reference_mode (6.3.12) + let bias = h.ref_frame_sign_bias; + let compound_allowed = bias[2] != bias[1] || bias[3] != bias[1]; + h.reference_mode = 0; + if compound_allowed { + if bd.read_literal(1) == 1 { + h.reference_mode = if bd.read_literal(1) == 1 { 2 } else { 1 }; + } + // setup_compound_reference_mode (6.3.18) + if bias[LAST_FRAME as usize] == bias[GOLDEN_FRAME as usize] { + h.comp_fixed_ref = ALTREF_FRAME; + h.comp_var_ref = [LAST_FRAME, GOLDEN_FRAME]; + } else if bias[LAST_FRAME as usize] == bias[ALTREF_FRAME as usize] { + h.comp_fixed_ref = GOLDEN_FRAME; + h.comp_var_ref = [LAST_FRAME, ALTREF_FRAME]; + } else { + h.comp_fixed_ref = LAST_FRAME; + h.comp_var_ref = [GOLDEN_FRAME, ALTREF_FRAME]; + } + } + // frame_reference_mode_probs (6.3.13) + if h.reference_mode == 2 { + for i in 0..5 { + diff_update_prob(&mut bd, &mut fc.comp_mode[i]); + } + } + if h.reference_mode != 1 { + for i in 0..5 { + diff_update_prob(&mut bd, &mut fc.single_ref[i][0]); + diff_update_prob(&mut bd, &mut fc.single_ref[i][1]); + } + } + if h.reference_mode != 0 { + for i in 0..5 { + diff_update_prob(&mut bd, &mut fc.comp_ref[i]); + } + } + for i in 0..4 { + for j in 0..9 { + diff_update_prob(&mut bd, &mut fc.y_mode[i][j]); + } + } + for i in 0..16 { + for j in 0..3 { + diff_update_prob(&mut bd, &mut fc.partition[i][j]); + } + } + // mv_probs (6.3.16) + for j in 0..3 { + update_mv_prob(&mut bd, &mut fc.mv_joint[j]); + } + for i in 0..2 { + update_mv_prob(&mut bd, &mut fc.mv_sign[i]); + for j in 0..10 { + update_mv_prob(&mut bd, &mut fc.mv_class[i][j]); + } + update_mv_prob(&mut bd, &mut fc.mv_class0_bit[i]); + for j in 0..10 { + update_mv_prob(&mut bd, &mut fc.mv_bits[i][j]); + } + } + for i in 0..2 { + for j in 0..2 { + for k in 0..3 { + update_mv_prob(&mut bd, &mut fc.mv_class0_fr[i][j][k]); + } + } + for k in 0..3 { + update_mv_prob(&mut bd, &mut fc.mv_fr[i][k]); + } + } + if h.allow_high_precision_mv { + for i in 0..2 { + update_mv_prob(&mut bd, &mut fc.mv_class0_hp[i]); + update_mv_prob(&mut bd, &mut fc.mv_hp[i]); + } + } + } + Ok(()) +} + +/// Superframe index parsing (Annex B): the frames of a chunk. +pub fn split_superframe(data: &[u8]) -> Vec<&[u8]> { + if let Some(&last) = data.last() + && last & 0xe0 == 0xc0 + { + let sz_bytes = ((last >> 3) & 3) as usize + 1; + let num = (last & 7) as usize + 1; + let index_size = 2 + num * sz_bytes; + if data.len() >= index_size && data[data.len() - index_size] == last { + let mut frames = Vec::with_capacity(num); + let mut pos = 0usize; + let mut p = data.len() - index_size + 1; + let end = data.len() - index_size; + for _ in 0..num { + let mut sz = 0usize; + for b in 0..sz_bytes { + sz |= (data[p + b] as usize) << (8 * b); + } + p += sz_bytes; + if pos + sz > end { + frames.push(&data[pos..end]); + return frames; + } + frames.push(&data[pos..pos + sz]); + pos += sz; + } + return frames; + } + } + vec![data] +} + +#[cfg(test)] +mod tests { + use super::*; + + #[test] + fn superframe_index() { + // Two frames of 3 and 2 bytes, 1 byte per size. + let mut d = vec![1u8, 2, 3, 4, 5]; + let marker = 0xc0 | 1; // 2 frames, 1 byte sizes + d.extend([marker, 3, 2, marker]); + let f = split_superframe(&d); + assert_eq!(f, vec![&[1u8, 2, 3][..], &[4u8, 5][..]]); + // Not a superframe: last byte is a marker but the first index byte does not match. + let d2 = vec![9u8, 9, 0xc1]; + assert_eq!(split_superframe(&d2), vec![&d2[..]]); + } + + #[test] + fn inv_remap_is_bijective_per_prob() { + for p in 1..=255u8 { + let mut seen = std::collections::HashSet::new(); + for d in 0..254 { + seen.insert(inv_remap_prob(d, p)); + } + // Every other probability value is reachable. + assert!(seen.len() >= 253, "p {p}: {}", seen.len()); + } + } +} diff --git a/crates/vp9/src/inter.rs b/crates/vp9/src/inter.rs new file mode 100644 index 0000000..14f2ee2 --- /dev/null +++ b/crates/vp9/src/inter.rs @@ -0,0 +1,326 @@ +//! Block inter prediction process (8.5.2.4): separable 8-tap sub-sample interpolation with edge +//! clamping, for unscaled (step 16) and scaled references. + +use crate::frame::Frame; +use crate::tables::SUBPEL_FILTERS; + +/// Filter taps of `filter` (0..3) at sub-sample position `frac` (0..15). +#[inline(always)] +fn taps(filter: u8, frac: usize) -> &'static [i16] { + let o = (filter as usize * 16 + frac) * 8; + &SUBPEL_FILTERS[o..o + 8] +} + +/// Plane `plane` of a reference frame, valid for x <= last_x, y <= last_y. Rows are read through +/// [`Frame::row`] / [`Frame::span`], which wait until the reference has published them. +pub struct RefPlane<'a> { + pub frame: &'a Frame, + pub plane: usize, + pub last_x: i32, + pub last_y: i32, +} + +impl RefPlane<'_> { + #[inline] + fn row(&self, y: usize) -> &[u16] { + self.frame.row(self.plane, y) + } +} + +/// Predict a `w` x `h` block whose top-left sample is at (`x`, `y`) in 1/16 sample units of the +/// reference, stepping `step_x` / `step_y` (16 = unscaled). Output samples go to `out` (stride +/// `w`). `tmp` must hold at least `(h * step_y / 16 + 8) * w` entries, `win` at least +/// `(w + 7) * (h + 7)` (edge-clamped source window). +#[allow(clippy::too_many_arguments)] +pub fn predict( + r: &RefPlane, + x: i32, + y: i32, + step_x: i32, + step_y: i32, + w: usize, + h: usize, + filter: u8, + bit_depth: u8, + out: &mut [u16], + tmp: &mut [u16], + win: &mut [u16], +) { + if step_x == 16 && step_y == 16 { + predict_unscaled(r, x, y, w, h, filter, bit_depth, out, tmp, win); + } else { + predict_scaled(r, x, y, step_x, step_y, w, h, filter, bit_depth, out, tmp); + } +} + +/// Copy the (w + 7) x (h + 7) source window starting at (x0 - 3, y0 - 3) with edge clamping. +fn fetch_window(r: &RefPlane, x0: i32, y0: i32, w: usize, h: usize, win: &mut [u16]) -> usize { + let ws = w + 7; + for row in 0..h + 7 { + let yy = (y0 - 3 + row as i32).clamp(0, r.last_y) as usize; + let src = r.row(yy); + let dst = &mut win[row * ws..row * ws + ws]; + let xs = x0 - 3; + if xs >= 0 && xs + ws as i32 - 1 <= r.last_x { + dst.copy_from_slice(&src[xs as usize..xs as usize + ws]); + } else { + for (c, d) in dst.iter_mut().enumerate() { + *d = src[(xs + c as i32).clamp(0, r.last_x) as usize]; + } + } + } + ws +} + +#[allow(clippy::too_many_arguments)] +fn predict_unscaled(r: &RefPlane, x: i32, y: i32, w: usize, h: usize, filter: u8, bit_depth: u8, out: &mut [u16], tmp: &mut [u16], win: &mut [u16]) { + let (x0, y0) = (x >> 4, y >> 4); + let (fx, fy) = ((x & 15) as usize, (y & 15) as usize); + let max = (1i32 << bit_depth) - 1; + let inside = x0 - 3 >= 0 && y0 - 3 >= 0 && x0 + w as i32 + 4 <= r.last_x && y0 + h as i32 + 4 <= r.last_y; + // Source view with origin at (x0 - 3, y0 - 3): the reference band itself when the window + // lies inside the picture and inside one band. + let span = if inside { r.frame.span(r.plane, (y0 - 3) as usize, (y0 + h as i32 + 5) as usize) } else { None }; + let (src, ss, so): (&[u16], usize, usize) = match span { + Some((data, o)) => (data, r.frame.strides[r.plane], o + (x0 - 3) as usize), + None => { + let ws = fetch_window(r, x0, y0, w, h, win); + (&win[..], ws, 0) + } + }; + match (fx, fy) { + (0, 0) => { + for i in 0..h { + let s = so + (i + 3) * ss + 3; + out[i * w..i * w + w].copy_from_slice(&src[s..s + w]); + } + } + (_, 0) => { + let f = taps(filter, fx); + for i in 0..h { + filter_h(&src[so + (i + 3) * ss..], f, max, &mut out[i * w..i * w + w]); + } + } + (0, _) => { + let f = taps(filter, fy); + for i in 0..h { + filter_v(&src[so + i * ss + 3..], ss, f, max, &mut out[i * w..i * w + w]); + } + } + _ => { + let fh = taps(filter, fx); + let fv = taps(filter, fy); + for i in 0..h + 7 { + filter_h(&src[so + i * ss..], fh, max, &mut tmp[i * w..i * w + w]); + } + for i in 0..h { + filter_v(&tmp[i * w..], w, fv, max, &mut out[i * w..i * w + w]); + } + } + } +} + +/// Horizontal 8-tap filter of one row: `o[j]` from `s[j..j + 8]`. The taps are the outer loop +/// so that the row vectorizes (same sums, same order of the exact integer additions). +#[inline(always)] +fn filter_h(s: &[u16], f: &[i16], max: i32, o: &mut [u16]) { + match o.len() { + 4 => filter_h_n::<4>(s, f, max, o), + 8 => filter_h_n::<8>(s, f, max, o), + 16 => filter_h_n::<16>(s, f, max, o), + 32 => filter_h_n::<32>(s, f, max, o), + 64 => filter_h_n::<64>(s, f, max, o), + _ => { + for (j, d) in o.iter_mut().enumerate() { + let acc: i32 = (0..8).map(|t| f[t] as i32 * s[j + t] as i32).sum(); + *d = ((acc + 64) >> 7).clamp(0, max) as u16; + } + } + } +} + +#[inline(always)] +fn filter_h_n(s: &[u16], f: &[i16], max: i32, o: &mut [u16]) { + let Some(o) = o.first_chunk_mut::() else { + return; + }; + if max == 255 { + let mut acc = [64u16; W]; + for t in 0..8 { + let c = f[t] as u16; + let Some(p) = s.get(t..).and_then(|s| s.first_chunk::()) else { + return; + }; + for j in 0..W { + acc[j] = acc[j].wrapping_add(c.wrapping_mul(p[j])); + } + } + narrow8(&acc, o); + return; + } + let mut acc = [64i32; W]; + for t in 0..8 { + let c = f[t] as i32; + let Some(p) = s.get(t..).and_then(|s| s.first_chunk::()) else { + return; + }; + for j in 0..W { + acc[j] += c * p[j] as i32; + } + } + for j in 0..W { + o[j] = (acc[j] >> 7).clamp(0, max) as u16; + } +} + +/// Round2(sum, 7) clipped to 8 bits, from 8-bit filter sums kept modulo 2^16 (8 lanes per +/// vector instead of 4). With 8-bit samples every sum of the VP9 filters (whose positive taps +/// add up to at most 182 and negative taps to at most -54) plus the rounding term 64 lies in +/// [-13706, 46474]: wrapped, the non-negative ones stay below 0xC000 and the negative ones +/// (which clip to 0) land above it. +#[inline(always)] +fn narrow8(acc: &[u16; W], o: &mut [u16; W]) { + for j in 0..W { + o[j] = if acc[j] < 0xC000 { (acc[j] >> 7).min(255) } else { 0 }; + } +} + +/// Vertical 8-tap filter of one row: `o[j]` from `s[j + t * ss]`, t = 0..8. +#[inline(always)] +fn filter_v(s: &[u16], ss: usize, f: &[i16], max: i32, o: &mut [u16]) { + match o.len() { + 4 => filter_v_n::<4>(s, ss, f, max, o), + 8 => filter_v_n::<8>(s, ss, f, max, o), + 16 => filter_v_n::<16>(s, ss, f, max, o), + 32 => filter_v_n::<32>(s, ss, f, max, o), + 64 => filter_v_n::<64>(s, ss, f, max, o), + _ => { + for (j, d) in o.iter_mut().enumerate() { + let acc: i32 = (0..8).map(|t| f[t] as i32 * s[j + t * ss] as i32).sum(); + *d = ((acc + 64) >> 7).clamp(0, max) as u16; + } + } + } +} + +#[inline(always)] +fn filter_v_n(s: &[u16], ss: usize, f: &[i16], max: i32, o: &mut [u16]) { + let Some(o) = o.first_chunk_mut::() else { + return; + }; + if max == 255 { + let mut acc = [64u16; W]; + for t in 0..8 { + let c = f[t] as u16; + let Some(p) = s.get(t * ss..).and_then(|s| s.first_chunk::()) else { + return; + }; + for j in 0..W { + acc[j] = acc[j].wrapping_add(c.wrapping_mul(p[j])); + } + } + narrow8(&acc, o); + return; + } + let mut acc = [64i32; W]; + for t in 0..8 { + let c = f[t] as i32; + let Some(p) = s.get(t * ss..).and_then(|s| s.first_chunk::()) else { + return; + }; + for j in 0..W { + acc[j] += c * p[j] as i32; + } + } + for j in 0..W { + o[j] = (acc[j] >> 7).clamp(0, max) as u16; + } +} + +/// General process of 8.5.2.4 (any step). +#[allow(clippy::too_many_arguments)] +fn predict_scaled( + r: &RefPlane, + x: i32, + y: i32, + step_x: i32, + step_y: i32, + w: usize, + h: usize, + filter: u8, + bit_depth: u8, + out: &mut [u16], + tmp: &mut [u16], +) { + let max = (1i32 << bit_depth) - 1; + let ih = ((((h as i32 - 1) * step_y + 15) >> 4) + 8) as usize; + for row in 0..ih { + let yy = ((y >> 4) + row as i32 - 3).clamp(0, r.last_y) as usize; + let src = r.row(yy); + for c in 0..w { + let p = x + step_x * c as i32; + let f = taps(filter, (p & 15) as usize); + let mut acc = 0i32; + for t in 0..8 { + acc += f[t] as i32 * src[((p >> 4) + t as i32 - 3).clamp(0, r.last_x) as usize] as i32; + } + tmp[row * w + c] = ((acc + 64) >> 7).clamp(0, max) as u16; + } + } + for rr in 0..h { + let p = (y & 15) + step_y * rr as i32; + let f = taps(filter, (p & 15) as usize); + let base = (p >> 4) as usize; + for c in 0..w { + let mut acc = 0i32; + for t in 0..8 { + acc += f[t] as i32 * tmp[(base + t) * w + c] as i32; + } + out[rr * w + c] = ((acc + 64) >> 7).clamp(0, max) as u16; + } + } +} + +#[cfg(test)] +mod tests { + use super::*; + + #[test] + fn scaled_path_with_unit_step_matches_unscaled() { + let (w, h) = (40usize, 30usize); + let data: Vec = (0..w * h).map(|i| ((i * 37 + (i / w) * 11) % 256) as u16).collect(); + let info = crate::frame::FrameInfo { + width: w as u32, + height: h as u32, + ss_x: true, + ss_y: true, + bit_depth: 8, + color_space: 0, + color_range: false, + render_width: w as u32, + render_height: h as u32, + key: true, + intra_only: false, + }; + // Planes are allocated in whole superblocks: copy the test picture into a 64x64 plane. + let mut full = vec![0u16; 64 * 64]; + for y in 0..h { + full[y * 64..y * 64 + w].copy_from_slice(&data[y * w..y * w + w]); + } + let chroma = vec![0u16; 32 * 32]; + let frame = Frame::from_planes(info, [&full, &chroma, &chroma]); + let r = RefPlane { frame: &frame, plane: 0, last_x: w as i32 - 1, last_y: h as i32 - 1 }; + let mut tmp = vec![0u16; 80 * 80]; + for filter in 0..4u8 { + for &(x, y) in &[(0, 0), (5, 3), (16 * 7 + 5, 16 * 9 + 11), (-40, -3), (16 * 36 + 1, 16 * 25 + 15), (16 * 10, 16 * 10 + 8)] { + for &(bw, bh) in &[(4usize, 4usize), (8, 8), (16, 8)] { + let mut a = vec![0u16; bw * bh]; + let mut b = vec![0u16; bw * bh]; + predict_unscaled(&r, x, y, bw, bh, filter, 8, &mut a, &mut tmp, &mut [0u16; 71 * 71]); + predict_scaled(&r, x, y, 16, 16, bw, bh, filter, 8, &mut b, &mut tmp); + assert_eq!(a, b, "filter {filter} pos ({x},{y}) {bw}x{bh}"); + } + } + } + } +} diff --git a/crates/vp9/src/intra.rs b/crates/vp9/src/intra.rs new file mode 100644 index 0000000..9c797ee --- /dev/null +++ b/crates/vp9/src/intra.rs @@ -0,0 +1,214 @@ +//! Intra prediction process (8.5.1). + +use crate::tables::*; + +/// Edge availability and geometry of one intra-predicted transform block. +pub struct IntraEdge { + pub have_left: bool, + pub have_above: bool, + pub not_on_right: bool, + pub tx_size: u8, + /// Last valid column / row of the plane (in plane coordinates). + pub max_x: usize, + pub max_y: usize, + pub bit_depth: u8, +} + +/// Predict a `4 << tx_size` square block at (`x`, `y`) of a plane buffer. `buf` is addressed as +/// `buf[y * stride + x - x_off]` (the buffer may be a tile-column strip starting at column `x_off`). +pub fn predict(buf: &mut [u16], stride: usize, x_off: usize, x: usize, y: usize, mode: u8, e: &IntraEdge) { + let log2 = e.tx_size as usize + 2; + let size = 1usize << log2; + let base = 1i32 << (e.bit_depth - 1); + let at = |xx: usize, yy: usize| yy * stride + xx - x_off; + // aboveRow[-1..2*size) stored at above[0..2*size+1); leftCol[0..size). + let mut above = [0i32; 65]; + let mut left = [0i32; 32]; + if !e.have_above { + above[1..=2 * size].fill(base - 1); + } else { + let row = (y - 1) * stride; + for i in 0..size { + above[1 + i] = buf[row + (x + i).min(e.max_x) - x_off] as i32; + } + if e.not_on_right && e.tx_size == 0 { + for i in size..2 * size { + above[1 + i] = buf[row + (x + i).min(e.max_x) - x_off] as i32; + } + } else { + let v = above[size]; + above[1 + size..=2 * size].fill(v); + } + } + above[0] = if e.have_above && e.have_left { + buf[at((x - 1).min(e.max_x), y - 1)] as i32 + } else if e.have_above { + base + 1 + } else { + base - 1 + }; + if e.have_left { + for i in 0..size { + left[i] = buf[at(x - 1, (y + i).min(e.max_y))] as i32; + } + } else { + left[..size].fill(base + 1); + } + let a = |i: isize| above[(i + 1) as usize]; + let max = (1i32 << e.bit_depth) - 1; + let o = at(x, y); + let mut put = |i: usize, j: usize, v: i32| buf[o + i * stride + j] = v as u16; + let r1 = |v: i32| (v + 1) >> 1; + let r2 = |v: i32| (v + 2) >> 2; + match mode { + V_PRED => { + for i in 0..size { + for j in 0..size { + put(i, j, a(j as isize)); + } + } + } + H_PRED => { + for i in 0..size { + for j in 0..size { + put(i, j, left[i]); + } + } + } + D207_PRED => { + let mut p = [[0i32; 32]; 32]; + for j in 0..size { + p[size - 1][j] = left[size - 1]; + } + for i in 0..size - 1 { + p[i][0] = r1(left[i] + left[i + 1]); + } + for i in 0..size.saturating_sub(2) { + p[i][1] = r2(left[i] + 2 * left[i + 1] + left[i + 2]); + } + p[size - 2][1] = r2(left[size - 2] + 3 * left[size - 1]); + for j in 2..size { + for i in (0..size - 1).rev() { + p[i][j] = p[i + 1][j - 2]; + } + } + for i in 0..size { + for j in 0..size { + put(i, j, p[i][j]); + } + } + } + D45_PRED => { + for i in 0..size { + for j in 0..size { + let k = (i + j) as isize; + let v = if i + j + 2 < 2 * size { r2(a(k) + 2 * a(k + 1) + a(k + 2)) } else { a(2 * size as isize - 1) }; + put(i, j, v); + } + } + } + D63_PRED => { + for i in 0..size { + for j in 0..size { + let k = (i / 2 + j) as isize; + let v = if i & 1 == 1 { r2(a(k) + 2 * a(k + 1) + a(k + 2)) } else { r1(a(k) + a(k + 1)) }; + put(i, j, v); + } + } + } + D117_PRED => { + let mut p = [[0i32; 32]; 32]; + for j in 0..size { + p[0][j] = r1(a(j as isize - 1) + a(j as isize)); + } + p[1][0] = r2(left[0] + 2 * a(-1) + a(0)); + for j in 1..size { + p[1][j] = r2(a(j as isize - 2) + 2 * a(j as isize - 1) + a(j as isize)); + } + p[2][0] = r2(a(-1) + 2 * left[0] + left[1]); + for i in 3..size { + p[i][0] = r2(left[i - 3] + 2 * left[i - 2] + left[i - 1]); + } + for i in 2..size { + for j in 1..size { + p[i][j] = p[i - 2][j - 1]; + } + } + for i in 0..size { + for j in 0..size { + put(i, j, p[i][j]); + } + } + } + D135_PRED => { + let mut p = [[0i32; 32]; 32]; + p[0][0] = r2(left[0] + 2 * a(-1) + a(0)); + for j in 1..size { + p[0][j] = r2(a(j as isize - 2) + 2 * a(j as isize - 1) + a(j as isize)); + } + p[1][0] = r2(a(-1) + 2 * left[0] + left[1]); + for i in 2..size { + p[i][0] = r2(left[i - 2] + 2 * left[i - 1] + left[i]); + } + for i in 1..size { + for j in 1..size { + p[i][j] = p[i - 1][j - 1]; + } + } + for i in 0..size { + for j in 0..size { + put(i, j, p[i][j]); + } + } + } + D153_PRED => { + let mut p = [[0i32; 32]; 32]; + p[0][0] = r1(left[0] + a(-1)); + for i in 1..size { + p[i][0] = r1(left[i - 1] + left[i]); + } + p[0][1] = r2(left[0] + 2 * a(-1) + a(0)); + p[1][1] = r2(a(-1) + 2 * left[0] + left[1]); + for i in 2..size { + p[i][1] = r2(left[i - 2] + 2 * left[i - 1] + left[i]); + } + for j in 2..size { + p[0][j] = r2(a(j as isize - 3) + 2 * a(j as isize - 2) + a(j as isize - 1)); + } + for i in 1..size { + for j in 2..size { + p[i][j] = p[i - 1][j - 2]; + } + } + for i in 0..size { + for j in 0..size { + put(i, j, p[i][j]); + } + } + } + TM_PRED => { + for i in 0..size { + for j in 0..size { + put(i, j, (a(j as isize) + left[i] - a(-1)).clamp(0, max)); + } + } + } + _ => { + // DC_PRED + let v = match (e.have_left, e.have_above) { + (true, true) => { + let sum: i32 = left[..size].iter().sum::() + above[1..=size].iter().sum::(); + (sum + size as i32) >> (log2 + 1) + } + (true, false) => (left[..size].iter().sum::() + (1 << (log2 - 1))) >> log2, + (false, true) => (above[1..=size].iter().sum::() + (1 << (log2 - 1))) >> log2, + (false, false) => base, + }; + for i in 0..size { + for j in 0..size { + put(i, j, v); + } + } + } + } +} diff --git a/crates/vp9/src/lib.rs b/crates/vp9/src/lib.rs new file mode 100644 index 0000000..af15be2 --- /dev/null +++ b/crates/vp9/src/lib.rs @@ -0,0 +1,170 @@ +//! Clean-room pure-Rust VP9 decoder, implemented from the public VP9 Bitstream & Decoding Process +//! Specification (v0.6, WebM project). +//! +//! Supported: profiles 0-3 (8 / 10 / 12-bit; 4:2:0, 4:2:2, 4:4:0, 4:4:4), all coding tools: +//! boolean decoder, forward and backward probability adaptation, segmentation, loop filter with +//! deltas, all intra modes, inter prediction (8-tap regular / smooth / sharp and bilinear, +//! scaled references), compound prediction, motion vector prediction, DCT / ADST 4..32 and the +//! Walsh-Hadamard lossless transform, tiles (tile columns decode in parallel), superframes, +//! hidden frames, show_existing_frame, intra-only frames, reference frame resizing and error +//! resilient / frame parallel modes. +//! +//! ```no_run +//! let mut dec = slidecraft_vp9::Decoder::new(); +//! let chunk: Vec = Vec::new(); // one IVF / WebM frame +//! for pic in dec.decode(&chunk, 0).unwrap() { +//! println!("{}x{} {}-bit", pic.width, pic.height, pic.bit_depth); +//! } +//! ``` + +#![allow(clippy::needless_range_loop, clippy::too_many_arguments)] +#![cfg_attr(not(test), deny(clippy::unwrap_used, clippy::expect_used, clippy::panic, clippy::unimplemented, clippy::todo, clippy::unreachable))] + +mod boolcoder; +mod decoder; +mod error; +mod frame; +mod header; +mod inter; +mod intra; +mod loopfilter; +mod probs; +#[rustfmt::skip] +#[allow(dead_code)] +mod spec_tables; +mod tables; +mod tile; +mod transform; +#[rustfmt::skip] +#[allow(dead_code, clippy::all)] +mod transform_gen; + +pub use decoder::{DecodeStats, Decoder}; + +/// Stream parameters read from a key frame's uncompressed header (see [`keyframe_info`]). +#[derive(Clone, Copy, Debug, PartialEq, Eq)] +pub struct KeyframeInfo { + pub profile: u8, + pub width: u32, + pub height: u32, + pub render_width: u32, + pub render_height: u32, + pub bit_depth: u32, + pub subsampling_x: bool, + pub subsampling_y: bool, + pub color: ColorInfo, +} + +/// If the first frame of `chunk` (one container sample: a frame or a superframe) is a key frame, +/// i.e. a random access point, its stream parameters. Intra-only frames and show_existing_frame +/// are not random access points. +pub fn keyframe_info(chunk: &[u8]) -> Option { + let first = header::split_superframe(chunk).into_iter().find(|f| !f.is_empty())?; + let mut st = header::HeaderState::default(); + let h = header::parse_uncompressed(first, &mut st, &[None; 8]).ok()?; + if h.show_existing_frame || h.frame_type != header::KEY_FRAME { + return None; + } + Some(KeyframeInfo { + profile: h.profile, + width: h.width, + height: h.height, + render_width: h.render_width, + render_height: h.render_height, + bit_depth: h.color.bit_depth as u32, + subsampling_x: h.color.subsampling_x, + subsampling_y: h.color.subsampling_y, + color: ColorInfo { color_space: h.color.color_space, full_range: h.color.color_range }, + }) +} + +/// Whether `chunk` starts with a key frame (decoding can start there). +pub fn is_keyframe(chunk: &[u8]) -> bool { + keyframe_info(chunk).is_some() +} +pub use error::{Error, Result}; + +/// Colour information from the uncompressed header. +#[derive(Clone, Copy, Debug, PartialEq, Eq)] +pub struct ColorInfo { + /// `color_space` (0 unknown, 1 BT.601, 2 BT.709, 3 SMPTE-170, 4 SMPTE-240, 5 BT.2020, + /// 6 reserved, 7 sRGB). + pub color_space: u8, + /// `color_range`: full swing when true, studio swing otherwise. + pub full_range: bool, +} + +/// Samples of one plane: 8-bit streams produce `U8`, higher bit depths `U16`. +#[derive(Clone, Debug, PartialEq, Eq)] +pub enum Plane { + U8(Vec), + U16(Vec), +} + +impl Plane { + pub fn len(&self) -> usize { + match self { + Plane::U8(v) => v.len(), + Plane::U16(v) => v.len(), + } + } + pub fn is_empty(&self) -> bool { + self.len() == 0 + } + pub fn as_u8(&self) -> Option<&[u8]> { + match self { + Plane::U8(v) => Some(v), + Plane::U16(_) => None, + } + } + pub fn as_u16(&self) -> Option<&[u16]> { + match self { + Plane::U8(_) => None, + Plane::U16(v) => Some(v), + } + } + /// Sample `i` widened to u16. + pub fn get(&self, i: usize) -> u16 { + match self { + Plane::U8(v) => v[i] as u16, + Plane::U16(v) => v[i], + } + } + /// Raw bytes as written by ffmpeg `rawvideo` (8-bit, or 16-bit little endian). + pub fn to_le_bytes(&self) -> Vec { + match self { + Plane::U8(v) => v.clone(), + Plane::U16(v) => v.iter().flat_map(|s| s.to_le_bytes()).collect(), + } + } +} + +/// A decoded, cropped planar picture. +#[derive(Clone, Debug)] +pub struct Picture { + /// Frame width / height (decoded size). + pub width: u32, + pub height: u32, + pub chroma_width: u32, + pub chroma_height: u32, + pub bit_depth: u32, + pub subsampling_x: bool, + pub subsampling_y: bool, + pub y: Plane, + pub u: Plane, + pub v: Plane, + /// Strides in samples. + pub y_stride: usize, + pub uv_stride: usize, + /// `pts` passed to [`Decoder::decode`] with the chunk that produced this picture. + pub pts: i64, + pub key: bool, + pub intra_only: bool, + pub color: ColorInfo, + /// Intended display size (render_size); has no effect on decoding. + pub render_width: u32, + pub render_height: u32, + /// Decoded in draft mode without the loop filter ([`Decoder::set_draft`]): approximate, for + /// reduced-resolution playback only. Never set without draft mode. + pub draft: bool, +} diff --git a/crates/vp9/src/loopfilter.rs b/crates/vp9/src/loopfilter.rs new file mode 100644 index 0000000..6d1a240 --- /dev/null +++ b/crates/vp9/src/loopfilter.rs @@ -0,0 +1,375 @@ +//! Loop filter process (8.8). + +use crate::frame::{MiGrid, MiInfo}; +use crate::header::{LoopFilterParams, Segmentation}; +use crate::tables::*; + +/// Frame-level loop filter setup (8.8.1) plus the per-level limits of 8.8.4. +pub struct LfFrame { + /// LvlLookup[segment_id][ref][modeType]. + pub lvl: [[[u8; 2]; 4]; 8], + /// (limit, blimit, thresh) per filter level. + pub limits: [(i32, i32, i32); 64], + pub mi_rows: usize, + pub mi_cols: usize, + pub ss_x: usize, + pub ss_y: usize, + pub bit_depth: u8, +} + +impl LfFrame { + pub fn new(lf: &LoopFilterParams, seg: &Segmentation, mi_rows: usize, mi_cols: usize, ss_x: bool, ss_y: bool, bit_depth: u8) -> LfFrame { + let mut lvl = [[[0u8; 2]; 4]; 8]; + let n_shift = lf.level >> 5; + for (seg_id, l) in lvl.iter_mut().enumerate() { + let mut lvl_seg = lf.level as i32; + if seg.feature_active(seg_id as u8, SEG_LVL_ALT_L) { + let d = seg.feature_data[seg_id][SEG_LVL_ALT_L] as i32; + lvl_seg = if seg.abs_or_delta_update { d } else { d + lf.level as i32 }; + lvl_seg = lvl_seg.clamp(0, MAX_LOOP_FILTER); + } + if !lf.delta_enabled { + *l = [[lvl_seg as u8; 2]; 4]; + } else { + let intra = lvl_seg + ((lf.ref_deltas[0] as i32) << n_shift); + l[0][0] = intra.clamp(0, MAX_LOOP_FILTER) as u8; + l[0][1] = l[0][0]; + for rf in 1..4 { + for mode in 0..2 { + let v = lvl_seg + ((lf.ref_deltas[rf] as i32) << n_shift) + ((lf.mode_deltas[mode] as i32) << n_shift); + l[rf][mode] = v.clamp(0, MAX_LOOP_FILTER) as u8; + } + } + } + } + let mut limits = [(0, 0, 0); 64]; + let sh = lf.sharpness as i32; + let shift = if sh > 4 { + 2 + } else if sh > 0 { + 1 + } else { + 0 + }; + for (l, e) in limits.iter_mut().enumerate() { + let l = l as i32; + let limit = if sh > 0 { (l >> shift).clamp(1, 9 - sh) } else { (l >> shift).max(1) }; + *e = (limit, 2 * (l + 2) + limit, l >> 4); + } + LfFrame { lvl, limits, mi_rows, mi_cols, ss_x: ss_x as usize, ss_y: ss_y as usize, bit_depth } + } + + fn level(&self, mi: &MiInfo) -> u8 { + let mode_type = matches!(mi.y_mode, NEARESTMV | NEARMV | NEWMV) as usize; + let rf = mi.ref_frame[0].max(0) as usize; + self.lvl[mi.seg_id as usize & 7][rf][mode_type] + } +} + +/// A mutable view of one plane: sample (x, y) of the plane is `data[(y - oy) * stride + x - ox]`. +pub struct PlaneView<'a> { + pub data: &'a mut [u16], + pub stride: usize, + pub ox: usize, + pub oy: usize, +} + +/// Loop filter one superblock (all planes, both passes) at (`row`, `col`) in MI units (8.8.2). +pub fn filter_superblock(views: &mut [PlaneView; 3], mi: &MiGrid, f: &LfFrame, row: usize, col: usize) { + for (plane, view) in views.iter_mut().enumerate() { + for pass in 0..2 { + filter_sb_plane(view, mi, f, plane, pass, row, col); + } + } +} + +fn filter_sb_plane(v: &mut PlaneView, mi: &MiGrid, f: &LfFrame, plane: usize, pass: usize, row: usize, col: usize) { + let (sub_x, sub_y) = if plane > 0 { (f.ss_x, f.ss_y) } else { (0, 0) }; + let (sub, edge_len) = if pass == 0 { (sub_x, 64 >> sub_y) } else { (sub_y, 64 >> sub_x) }; + let (mi_rows, mi_cols) = (f.mi_rows, f.mi_cols); + // Step between the samples across the edge. + let across = if pass == 0 { 1isize } else { v.stride as isize }; + for edge in 0..(16 >> sub) { + let mut i = 0; + while i < edge_len { + // Group of 8 samples sharing loopRow / loopCol. + let (x, y) = if pass == 0 { + (col * 8 + edge * (4 << sub_x), row * 8 + (i << sub_y)) + } else { + (col * 8 + (i << sub_x), row * 8 + edge * (4 << sub_y)) + }; + // Skip groups entirely off screen (the varying coordinate is re-checked per sample). + if x >= 8 * mi_cols || y >= 8 * mi_rows || (pass == 0 && x == 0) || (pass == 1 && y == 0) { + i += 8; + continue; + } + let loop_col = ((x >> 3) >> sub_x) << sub_x; + let loop_row = ((y >> 3) >> sub_y) << sub_y; + let m = mi.at(loop_row, loop_col); + let tx_sz = if plane > 0 { + if m.sb_size < BLOCK_8X8 { 0 } else { m.tx_size.min(MAX_TXSIZE[SS_SIZE_LOOKUP[m.sb_size as usize][f.ss_x][f.ss_y].min(12) as usize]) } + } else { + m.tx_size + }; + let sb_size = if sub == 0 { m.sb_size } else { m.sb_size.max(BLOCK_16X16) } as usize; + let is_intra = m.ref_frame[0] <= INTRA_FRAME; + // Block sizes are powers of two, so the modulo is a mask. + let is_block_edge = + if pass == 0 { x & (8 * NUM_8X8_WIDE[sb_size] as usize - 1) == 0 } else { y & (8 * NUM_8X8_HIGH[sb_size] as usize - 1) == 0 }; + let tx_ok = edge & ((1 << tx_sz) - 1) == 0 && (is_intra || !m.skip); + if !is_block_edge && !tx_ok { + i += 8; + continue; + } + let lvl = f.level(m); + if lvl == 0 { + i += 8; + continue; + } + let is32 = edge % 8 == 0; + let base_size = if tx_sz == TX_4X4 && is32 { TX_8X8 } else { tx_sz.min(TX_16X16) }; + let filter_size = if base_size == TX_16X16 + && ((pass == 0 && sub_x == 1 && (x >> 3) == mi_cols - 1) || (pass == 1 && sub_y == 1 && (y >> 3) == mi_rows - 1)) + { + TX_8X8 + } else { + base_size + }; + let (limit, blimit, thresh) = f.limits[lvl as usize]; + // Lanes of the group (the 8 samples along the edge) inside the MI grid. + let n = + if pass == 0 { (8 * mi_rows - y).div_ceil(1 << sub_y) } else { (8 * mi_cols - x).div_ceil(1 << sub_x) }.min(LANES).min(edge_len - i); + // A transform edge is not filtered in the last odd chroma column (8.8.2). + let odd_last = pass == 1 && sub_x == 1 && mi_cols & 1 == 1 && edge & 1 == 1; + 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 << sub_x) + 8 >= mi_cols * 8); + *a = is_block_edge || is_tx_edge; + } + if apply.iter().any(|&a| a) { + let px = x >> sub_x; + let py = y >> sub_y; + let pos = (py - v.oy) * v.stride + px - v.ox; + let along = if pass == 0 { v.stride } else { 1 }; + let lim = Limits { apply, limit, blimit, thresh, filter_size, bit_depth: f.bit_depth }; + let (d, st) = (&mut *v.data, across as usize); + match (f.bit_depth <= 10, filter_size == TX_16X16, pass == 1) { + (true, false, false) => lanes16::filter_group::<4, false>(d, pos, along, st, &lim), + (true, false, true) => lanes16::filter_group::<4, true>(d, pos, along, st, &lim), + (true, true, false) => lanes16::filter_group::<8, false>(d, pos, along, st, &lim), + (true, true, true) => lanes16::filter_group::<8, true>(d, pos, along, st, &lim), + (false, false, false) => lanes32::filter_group::<4, false>(d, pos, along, st, &lim), + (false, false, true) => lanes32::filter_group::<4, true>(d, pos, along, st, &lim), + (false, true, false) => lanes32::filter_group::<8, false>(d, pos, along, st, &lim), + (false, true, true) => lanes32::filter_group::<8, true>(d, pos, along, st, &lim), + } + } + i += 8; + } + } +} + +/// Samples along an edge filtered together. +const LANES: usize = 8; + +/// Per-group filter parameters. +struct Limits { + apply: [bool; LANES], + limit: i32, + blimit: i32, + thresh: i32, + filter_size: u8, + bit_depth: u8, +} + +/// The group filter, instantiated for 16-bit lanes (bit depths 8 and 10: every intermediate, +/// including the 16-tap sums of at most 16 * 1023 + 8, fits) and 32-bit lanes (12-bit). +macro_rules! group_filter { + ($m:ident, $t:ty) => { + mod $m { + use super::{LANES, Limits}; + use crate::tables::*; + type T = $t; + + /// Sample filtering process (8.8.5) for the up to 8 lanes of a group whose `apply` flag is set. + /// Lane `l` has its q0 sample at `pos + l * along`; samples across the edge are `step` apart + /// (`ROWS`: `along` is 1, i.e. a horizontal edge). `R` samples on each side are read (8 for + /// 16x16 filters, else 4). Every lane is computed with every filter and the result chosen per + /// lane (8.8.5.1 decides which), so the arithmetic is branch-free and vectorizes; unmodified + /// samples are written back unchanged. + #[inline(always)] + pub(super) fn filter_group(d: &mut [u16], pos: usize, along: usize, step: usize, g: &Limits) { + let Limits { apply, limit, blimit, thresh, filter_size, bit_depth } = *g; + let (limit, blimit, thresh) = (limit as T, blimit as T, thresh as T); + // v[8 + k][l] = sample at offset k across the edge in lane l. + let mut v = [[0 as T; LANES]; 16]; + let start = pos - R * step; + if ROWS { + for k in 0..2 * R { + let row: &[u16; LANES] = d[start + k * step..start + k * step + LANES].try_into().expect("lanes"); + v[8 - R + k] = std::array::from_fn(|l| row[l] as T); + } + } else { + for l in 0..LANES { + let s = start + l * along; + let line: &[u16] = &d[s..s + 2 * R]; + for k in 0..2 * R { + v[8 - R + k][l] = line[k] as T; + } + } + } + let shift = bit_depth as u32 - 8; + let limit_bd = limit << shift; + let blimit_bd = blimit << shift; + let thresh_bd = thresh << shift; + let one = 1 << shift; + let (p3, p2, p1, p0, q0, q1, q2, q3) = (&v[4], &v[5], &v[6], &v[7], &v[8], &v[9], &v[10], &v[11]); + // Filter mask process (8.8.5.1). Masks are 0 / -1 per lane, selects are bitwise + // and clipping uses min / max (`clamp` asserts its bounds), so every lane loop + // below compiles to vector instructions. + let le = |a: T, b: T| -((a <= b) as T); + let sel = |m: T, a: T, b: T| (a & m) | (b & !m); + let mut mask = [0 as T; LANES]; + let mut hev = [0 as T; LANES]; + let mut flat = [0 as T; LANES]; + let mut flat2 = [0 as T; LANES]; + let can_flat = -((filter_size >= TX_8X8) as T); + let can_flat2 = R == 8 && filter_size >= TX_16X16; + let apply: [T; LANES] = apply.map(|a| -(a as T)); + for l in 0..LANES { + let d_p1p0 = (p1[l] - p0[l]).abs(); + let d_q1q0 = (q1[l] - q0[l]).abs(); + mask[l] = apply[l] + & le((p3[l] - p2[l]).abs(), limit_bd) + & le((p2[l] - p1[l]).abs(), limit_bd) + & le(d_p1p0, limit_bd) + & le(d_q1q0, limit_bd) + & le((q2[l] - q1[l]).abs(), limit_bd) + & le((q3[l] - q2[l]).abs(), limit_bd) + & le((p0[l] - q0[l]).abs() * 2 + ((p1[l] - q1[l]).abs() >> 1), blimit_bd); + hev[l] = !(le(d_p1p0, thresh_bd) & le(d_q1q0, thresh_bd)); + flat[l] = can_flat + & mask[l] + & le(d_p1p0, one) + & le(d_q1q0, one) + & le((p2[l] - p0[l]).abs(), one) + & le((q2[l] - q0[l]).abs(), one) + & le((p3[l] - p0[l]).abs(), one) + & le((q3[l] - q0[l]).abs(), one); + } + if mask.iter().fold(0, |a, &m| a | m) == 0 { + return; + } + if can_flat2 { + for l in 0..LANES { + flat2[l] = flat[l] + & le((v[0][l] - p0[l]).abs(), one) + & le((v[1][l] - p0[l]).abs(), one) + & le((v[2][l] - p0[l]).abs(), one) + & le((v[3][l] - p0[l]).abs(), one) + & le((v[12][l] - q0[l]).abs(), one) + & le((v[13][l] - q0[l]).abs(), one) + & le((v[14][l] - q0[l]).abs(), one) + & le((v[15][l] - q0[l]).abs(), one); + } + } + let any_flat = flat.iter().fold(0, |a, &f| a | f) != 0; + let any_flat2 = flat2.iter().fold(0, |a, &f| a | f) != 0; + // Results per position across the edge; start from the unfiltered samples. + let mut out = v; + // Narrow filter (8.8.5.2) for lanes that are masked but not flat. + { + let lo = -(1 << (bit_depth - 1)) as T; + let hi = ((1 << (bit_depth - 1)) - 1) as T; + let c = |x: T| x.max(lo).min(hi); + let off: T = 0x80 << shift; + for l in 0..LANES { + let (ps1, ps0, qs0, qs1) = (p1[l] - off, p0[l] - off, q0[l] - off, q1[l] - off); + let filter = c((c(ps1 - qs1) & hev[l]) + 3 * (qs0 - ps0)); + let filter1 = c(filter + 4) >> 3; + let filter2 = c(filter + 3) >> 3; + let f = (filter1 + 1) >> 1; + let narrow = mask[l] & !flat[l]; + let outer = narrow & !hev[l]; + out[8][l] = sel(narrow, c(qs0 - filter1) + off, out[8][l]); + out[7][l] = sel(narrow, c(ps0 + filter2) + off, out[7][l]); + out[9][l] = sel(outer, c(qs1 - f) + off, out[9][l]); + out[6][l] = sel(outer, c(ps1 + f) + off, out[6][l]); + } + } + // Wide filter (8.8.5.3) with 8 taps for flat lanes (p2..q2) ... + if any_flat { + let w8 = wide_filter::<3>(&v); + for k in 5..11 { + for l in 0..LANES { + out[k][l] = sel(flat[l] & !flat2[l], w8[k][l], out[k][l]); + } + } + } + // ... and 16 taps for flat2 lanes (p6..q6). + if any_flat2 { + let w16 = wide_filter::<4>(&v); + for k in 1..15 { + for l in 0..LANES { + out[k][l] = sel(flat2[l], w16[k][l], out[k][l]); + } + } + } + let (lo, hi) = if any_flat2 { + (1, 15) + } else if any_flat { + (5, 11) + } else { + (6, 10) + }; + if ROWS { + for k in lo..hi { + let o = start + (k + R - 8) * step; // k >= 8 - R + let row: &mut [u16; LANES] = (&mut d[o..o + LANES]).try_into().expect("lanes"); + for l in 0..LANES { + row[l] = out[k][l] as u16; + } + } + } else { + // Whole lines: unmodified samples are rewritten with their own value. + for l in 0..LANES { + let s = start + l * along; + let line: &mut [u16] = &mut d[s..s + 2 * R]; + for k in 0..2 * R { + line[k] = out[8 - R + k][l] as u16; + } + } + } + } + + /// Wide filter process (8.8.5.3) with 2^LOG2 taps on gathered lanes (positions 8 - 2^(LOG2-1) + /// .. 8 + 2^(LOG2-1) - 2 are produced); the sum over j of the specification is computed as a + /// sliding window over the clamped indices (identical results). + #[inline(always)] + fn wide_filter(v: &[[T; LANES]; 16]) -> [[T; LANES]; 16] { + let n = (1isize << (LOG2 - 1)) - 1; + let s = |k: isize| &v[(k.clamp(-(n + 1), n) + 8) as usize]; + let mut out = [[0 as T; LANES]; 16]; + let mut sum = [0 as T; LANES]; + for j in -n..=n { + let a = s(-n + j); + for l in 0..LANES { + sum[l] += a[l]; + } + } + let round: T = 1 << (LOG2 - 1); + for i in -n..n { + let (cur, add, sub) = (s(i), s(i + 1 + n), s(i - n)); + let o = &mut out[(i + 8) as usize]; + for l in 0..LANES { + o[l] = (sum[l] + cur[l] + round) >> LOG2; + sum[l] += add[l] - sub[l]; + } + } + out + } + } + }; +} +group_filter!(lanes16, i16); +group_filter!(lanes32, i32); diff --git a/crates/vp9/src/probs.rs b/crates/vp9/src/probs.rs new file mode 100644 index 0000000..5ac3a39 --- /dev/null +++ b/crates/vp9/src/probs.rs @@ -0,0 +1,315 @@ +//! Probability tables (a "frame context"), syntax element counters and the backward adaptation +//! processes of section 8.4. + +use crate::tables::*; + +/// coef_probs[txSz][plane > 0][is_inter][band][ctx][node]. +pub type CoefProbs = [[[[[[u8; 3]; 6]; 6]; 2]; 2]; 4]; + +/// All adaptive probabilities of one frame context (10.5). +#[derive(Clone, PartialEq, Eq, Debug)] +pub struct FrameContext { + /// tx_probs[maxTxSize][ctx][node] (index 0 unused). + pub tx: [[[u8; 3]; 2]; 4], + pub coef: CoefProbs, + pub skip: [u8; 3], + pub inter_mode: [[u8; 3]; 7], + pub interp_filter: [[u8; 2]; 4], + pub is_inter: [u8; 4], + pub comp_mode: [u8; 5], + pub single_ref: [[u8; 2]; 5], + pub comp_ref: [u8; 5], + pub y_mode: [[u8; 9]; 4], + pub uv_mode: [[u8; 9]; 10], + pub partition: [[u8; 3]; 16], + pub mv_joint: [u8; 3], + pub mv_sign: [u8; 2], + pub mv_class: [[u8; 10]; 2], + pub mv_class0_bit: [u8; 2], + pub mv_bits: [[u8; 10]; 2], + pub mv_class0_fr: [[[u8; 3]; 2]; 2], + pub mv_fr: [[u8; 3]; 2], + pub mv_class0_hp: [u8; 2], + pub mv_hp: [u8; 2], +} + +fn arr(s: &[u8]) -> [u8; N] { + s.first_chunk::().copied().unwrap_or([128; N]) +} + +impl Default for FrameContext { + fn default() -> Self { + let mut coef = [[[[[[0u8; 3]; 6]; 6]; 2]; 2]; 4]; + let mut k = 0; + for t in coef.iter_mut() { + for i in t.iter_mut() { + for j in i.iter_mut() { + for b in j.iter_mut() { + for c in b.iter_mut() { + *c = arr(&DEFAULT_COEF_PROBS[k..k + 3]); + k += 3; + } + } + } + } + } + let mut tx = [[[0u8; 3]; 2]; 4]; + for (m, t) in tx.iter_mut().enumerate() { + for (c, p) in t.iter_mut().enumerate() { + *p = arr(&DEFAULT_TX_PROBS[(m * 2 + c) * 3..(m * 2 + c) * 3 + 3]); + } + } + FrameContext { + tx, + coef, + skip: DEFAULT_SKIP_PROB, + inter_mode: std::array::from_fn(|i| arr(&DEFAULT_INTER_MODE_PROBS[i * 3..i * 3 + 3])), + interp_filter: std::array::from_fn(|i| arr(&DEFAULT_INTERP_FILTER_PROBS[i * 2..i * 2 + 2])), + is_inter: DEFAULT_IS_INTER_PROB, + comp_mode: DEFAULT_COMP_MODE_PROB, + single_ref: std::array::from_fn(|i| arr(&DEFAULT_SINGLE_REF_PROB[i * 2..i * 2 + 2])), + comp_ref: DEFAULT_COMP_REF_PROB, + y_mode: std::array::from_fn(|i| arr(&DEFAULT_Y_MODE_PROBS[i * 9..i * 9 + 9])), + uv_mode: std::array::from_fn(|i| arr(&DEFAULT_UV_MODE_PROBS[i * 9..i * 9 + 9])), + partition: std::array::from_fn(|i| arr(&DEFAULT_PARTITION_PROBS[i * 3..i * 3 + 3])), + mv_joint: DEFAULT_MV_JOINT_PROBS, + mv_sign: DEFAULT_MV_SIGN_PROB, + mv_class: std::array::from_fn(|i| arr(&DEFAULT_MV_CLASS_PROBS[i * 10..i * 10 + 10])), + mv_class0_bit: DEFAULT_MV_CLASS0_BIT_PROB, + mv_bits: std::array::from_fn(|i| arr(&DEFAULT_MV_BITS_PROB[i * 10..i * 10 + 10])), + mv_class0_fr: std::array::from_fn(|i| std::array::from_fn(|j| arr(&DEFAULT_MV_CLASS0_FR_PROBS[(i * 2 + j) * 3..(i * 2 + j) * 3 + 3]))), + mv_fr: std::array::from_fn(|i| arr(&DEFAULT_MV_FR_PROBS[i * 3..i * 3 + 3])), + mv_class0_hp: DEFAULT_MV_CLASS0_HP_PROB, + mv_hp: DEFAULT_MV_HP_PROB, + } + } +} + +/// Syntax element counters (8.3). +#[derive(Clone, Default)] +pub struct Counts { + pub intra_mode: [[u32; 10]; 4], + pub uv_mode: [[u32; 10]; 10], + pub partition: [[u32; 4]; 16], + pub interp_filter: [[u32; 3]; 4], + pub inter_mode: [[u32; 4]; 7], + /// tx_size[maxTxSize][ctx][value]. + pub tx: [[[u32; 4]; 2]; 4], + pub is_inter: [[u32; 2]; 4], + pub comp_mode: [[u32; 2]; 5], + pub single_ref: [[[u32; 2]; 2]; 5], + pub comp_ref: [[u32; 2]; 5], + pub skip: [[u32; 2]; 3], + pub mv_joint: [u32; 4], + pub mv_sign: [[u32; 2]; 2], + pub mv_class: [[u32; 11]; 2], + pub mv_class0_bit: [[u32; 2]; 2], + pub mv_class0_fr: [[[u32; 4]; 2]; 2], + pub mv_class0_hp: [[u32; 2]; 2], + pub mv_bits: [[[u32; 2]; 10]; 2], + pub mv_fr: [[u32; 4]; 2], + pub mv_hp: [[u32; 2]; 2], + /// token[txSz][plane>0][is_inter][band][ctx][min(2, token)]. + pub token: [[[[[[u32; 3]; 6]; 6]; 2]; 2]; 4], + /// more_coefs[txSz][plane>0][is_inter][band][ctx][value]. + pub more_coefs: [[[[[[u32; 2]; 6]; 6]; 2]; 2]; 4], +} + +fn add_slice(a: &mut [u32], b: &[u32]) { + for (x, y) in a.iter_mut().zip(b) { + *x += y; + } +} + +macro_rules! add_nested { + ($a:expr, $b:expr, 1) => { + add_slice(&mut $a[..], &$b[..]) + }; + ($a:expr, $b:expr, 2) => { + for (x, y) in $a.iter_mut().zip($b.iter()) { + add_slice(&mut x[..], &y[..]); + } + }; + ($a:expr, $b:expr, 3) => { + for (x, y) in $a.iter_mut().zip($b.iter()) { + add_nested!(x, y, 2); + } + }; +} + +impl Counts { + /// Accumulate the counts of another tile. + pub fn add(&mut self, o: &Counts) { + add_nested!(self.intra_mode, o.intra_mode, 2); + add_nested!(self.uv_mode, o.uv_mode, 2); + add_nested!(self.partition, o.partition, 2); + add_nested!(self.interp_filter, o.interp_filter, 2); + add_nested!(self.inter_mode, o.inter_mode, 2); + add_nested!(self.tx, o.tx, 3); + add_nested!(self.is_inter, o.is_inter, 2); + add_nested!(self.comp_mode, o.comp_mode, 2); + add_nested!(self.single_ref, o.single_ref, 3); + add_nested!(self.comp_ref, o.comp_ref, 2); + add_nested!(self.skip, o.skip, 2); + add_nested!(self.mv_joint, o.mv_joint, 1); + add_nested!(self.mv_sign, o.mv_sign, 2); + add_nested!(self.mv_class, o.mv_class, 2); + add_nested!(self.mv_class0_bit, o.mv_class0_bit, 2); + add_nested!(self.mv_class0_fr, o.mv_class0_fr, 3); + add_nested!(self.mv_class0_hp, o.mv_class0_hp, 2); + add_nested!(self.mv_bits, o.mv_bits, 3); + add_nested!(self.mv_fr, o.mv_fr, 2); + add_nested!(self.mv_hp, o.mv_hp, 2); + for (a, b) in self.token.iter_mut().zip(o.token.iter()) { + for (a, b) in a.iter_mut().zip(b.iter()) { + for (a, b) in a.iter_mut().zip(b.iter()) { + for (a, b) in a.iter_mut().zip(b.iter()) { + add_nested!(a, b, 2); + } + } + } + } + for (a, b) in self.more_coefs.iter_mut().zip(o.more_coefs.iter()) { + for (a, b) in a.iter_mut().zip(b.iter()) { + for (a, b) in a.iter_mut().zip(b.iter()) { + for (a, b) in a.iter_mut().zip(b.iter()) { + add_nested!(a, b, 2); + } + } + } + } + } +} + +/// merge_prob (8.4.1). +fn merge_prob(pre: u8, ct0: u32, ct1: u32, count_sat: u32, max_update_factor: u32) -> u8 { + let den = ct0 + ct1; + let prob = if den == 0 { 128 } else { ((ct0 as u64 * 256 + (den as u64 >> 1)) / den as u64).clamp(1, 255) as u32 }; + let count = den.min(count_sat); + let factor = max_update_factor * count / count_sat; + ((pre as u32 * (256 - factor) + prob * factor + 128) >> 8) as u8 +} + +/// merge_probs (8.4.2). +fn merge_probs(tree: &[i8], i: usize, probs: &mut [u8], counts: &[u32], count_sat: u32, max_update_factor: u32) -> u32 { + let s = tree[i]; + let left = if s <= 0 { counts[(-s) as usize] } else { merge_probs(tree, s as usize, probs, counts, count_sat, max_update_factor) }; + let r = tree[i + 1]; + let right = if r <= 0 { counts[(-r) as usize] } else { merge_probs(tree, r as usize, probs, counts, count_sat, max_update_factor) }; + probs[i >> 1] = merge_prob(probs[i >> 1], left, right, count_sat, max_update_factor); + left + right +} + +fn adapt_probs(tree: &[i8], probs: &mut [u8], counts: &[u32]) { + merge_probs(tree, 0, probs, counts, COUNT_SAT, MAX_UPDATE_FACTOR); +} + +fn adapt_prob(prob: &mut u8, counts: &[u32; 2]) { + *prob = merge_prob(*prob, counts[0], counts[1], COUNT_SAT, MAX_UPDATE_FACTOR); +} + +/// Coefficient probability adaptation (8.4.3). `fc` holds the probabilities loaded from the +/// saved frame context. +pub fn adapt_coef_probs(fc: &mut FrameContext, counts: &Counts, frame_is_intra: bool, last_frame_was_key: bool) { + let update_factor = if frame_is_intra { + 112 + } else if last_frame_was_key { + 128 + } else { + 112 + }; + for t in 0..4 { + for i in 0..2 { + for j in 0..2 { + for k in 0..6 { + let max_l = if k == 0 { 3 } else { 6 }; + for l in 0..max_l { + let p = &mut fc.coef[t][i][j][k][l]; + merge_probs(&SMALL_TOKEN_TREE, 2, p, &counts.token[t][i][j][k][l], 24, update_factor); + merge_probs(&BINARY_TREE, 0, p, &counts.more_coefs[t][i][j][k][l], 24, update_factor); + } + } + } + } + } +} + +/// Non-coefficient probability adaptation (8.4.4). +pub fn adapt_noncoef_probs(fc: &mut FrameContext, c: &Counts, interp_switchable: bool, tx_select: bool, allow_hp: bool) { + for i in 0..4 { + adapt_prob(&mut fc.is_inter[i], &c.is_inter[i]); + } + for i in 0..5 { + adapt_prob(&mut fc.comp_mode[i], &c.comp_mode[i]); + adapt_prob(&mut fc.comp_ref[i], &c.comp_ref[i]); + for j in 0..2 { + adapt_prob(&mut fc.single_ref[i][j], &c.single_ref[i][j]); + } + } + for i in 0..7 { + adapt_probs(&INTER_MODE_TREE, &mut fc.inter_mode[i], &c.inter_mode[i]); + } + for i in 0..4 { + adapt_probs(&INTRA_MODE_TREE, &mut fc.y_mode[i], &c.intra_mode[i]); + } + for i in 0..10 { + adapt_probs(&INTRA_MODE_TREE, &mut fc.uv_mode[i], &c.uv_mode[i]); + } + for i in 0..16 { + adapt_probs(&PARTITION_TREE, &mut fc.partition[i], &c.partition[i]); + } + for i in 0..3 { + adapt_prob(&mut fc.skip[i], &c.skip[i]); + } + if interp_switchable { + for i in 0..4 { + adapt_probs(&INTERP_FILTER_TREE, &mut fc.interp_filter[i], &c.interp_filter[i]); + } + } + if tx_select { + for i in 0..2 { + adapt_probs(&TX_SIZE_8_TREE, &mut fc.tx[1][i], &c.tx[1][i]); + adapt_probs(&TX_SIZE_16_TREE, &mut fc.tx[2][i], &c.tx[2][i]); + adapt_probs(&TX_SIZE_32_TREE, &mut fc.tx[3][i], &c.tx[3][i]); + } + } + adapt_probs(&MV_JOINT_TREE, &mut fc.mv_joint, &c.mv_joint); + for i in 0..2 { + adapt_prob(&mut fc.mv_sign[i], &c.mv_sign[i]); + adapt_probs(&MV_CLASS_TREE, &mut fc.mv_class[i], &c.mv_class[i]); + adapt_prob(&mut fc.mv_class0_bit[i], &c.mv_class0_bit[i]); + for j in 0..10 { + adapt_prob(&mut fc.mv_bits[i][j], &c.mv_bits[i][j]); + } + for j in 0..2 { + adapt_probs(&MV_FR_TREE, &mut fc.mv_class0_fr[i][j], &c.mv_class0_fr[i][j]); + } + adapt_probs(&MV_FR_TREE, &mut fc.mv_fr[i], &c.mv_fr[i]); + if allow_hp { + adapt_prob(&mut fc.mv_class0_hp[i], &c.mv_class0_hp[i]); + adapt_prob(&mut fc.mv_hp[i], &c.mv_hp[i]); + } + } +} + +#[cfg(test)] +mod tests { + use super::*; + + #[test] + fn merge_prob_identity_without_counts() { + for p in 1..=255u8 { + assert_eq!(merge_prob(p, 0, 0, 20, 128), p); + } + // Saturated counts move halfway towards the observed probability. + assert_eq!(merge_prob(128, 20, 0, 20, 128), 192); + } + + #[test] + fn defaults_load() { + let fc = FrameContext::default(); + assert_eq!(fc.coef[0][0][0][0][0], [195, 29, 183]); + assert_eq!(fc.tx[3][1], [5, 52, 13]); + assert_eq!(fc.mv_class0_fr[1][1], [96, 112, 64]); + } +} diff --git a/crates/vp9/src/spec_tables.rs b/crates/vp9/src/spec_tables.rs new file mode 100644 index 0000000..686d64f --- /dev/null +++ b/crates/vp9/src/spec_tables.rs @@ -0,0 +1,1427 @@ +// Generated by tools/extract_tables.py from the VP9 Bitstream & Decoding Process Specification +// v0.6 (section numbers refer to that document). Do not edit by hand. + +pub const INV_MAP_TABLE: [u8; 255] = [ + 7, 20, 33, 46, 59, 72, 85, 98, 111, 124, 137, 150, 163, 176, 189, 202, + 215, 228, 241, 254, 1, 2, 3, 4, 5, 6, 8, 9, 10, 11, 12, 13, + 14, 15, 16, 17, 18, 19, 21, 22, 23, 24, 25, 26, 27, 28, 29, 30, + 31, 32, 34, 35, 36, 37, 38, 39, 40, 41, 42, 43, 44, 45, 47, 48, + 49, 50, 51, 52, 53, 54, 55, 56, 57, 58, 60, 61, 62, 63, 64, 65, + 66, 67, 68, 69, 70, 71, 73, 74, 75, 76, 77, 78, 79, 80, 81, 82, + 83, 84, 86, 87, 88, 89, 90, 91, 92, 93, 94, 95, 96, 97, 99, 100, + 101, 102, 103, 104, 105, 106, 107, 108, 109, 110, 112, 113, 114, 115, 116, 117, + 118, 119, 120, 121, 122, 123, 125, 126, 127, 128, 129, 130, 131, 132, 133, 134, + 135, 136, 138, 139, 140, 141, 142, 143, 144, 145, 146, 147, 148, 149, 151, 152, + 153, 154, 155, 156, 157, 158, 159, 160, 161, 162, 164, 165, 166, 167, 168, 169, + 170, 171, 172, 173, 174, 175, 177, 178, 179, 180, 181, 182, 183, 184, 185, 186, + 187, 188, 190, 191, 192, 193, 194, 195, 196, 197, 198, 199, 200, 201, 203, 204, + 205, 206, 207, 208, 209, 210, 211, 212, 213, 214, 216, 217, 218, 219, 220, 221, + 222, 223, 224, 225, 226, 227, 229, 230, 231, 232, 233, 234, 235, 236, 237, 238, + 239, 240, 242, 243, 244, 245, 246, 247, 248, 249, 250, 251, 252, 253, 253, +]; + +pub const SUBPEL_FILTERS: [i16; 512] = [ + 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, + 0, 0, 0, 128, 0, 0, 0, 0, + -3, -1, 32, 64, 38, 1, -3, 0, + -2, -2, 29, 63, 41, 2, -3, 0, + -2, -2, 26, 63, 43, 4, -4, 0, + -2, -3, 24, 62, 46, 5, -4, 0, + -2, -3, 21, 60, 49, 7, -4, 0, + -1, -4, 18, 59, 51, 9, -4, 0, + -1, -4, 16, 57, 53, 12, -4, -1, + -1, -4, 14, 55, 55, 14, -4, -1, + -1, -4, 12, 53, 57, 16, -4, -1, + 0, -4, 9, 51, 59, 18, -4, -1, + 0, -4, 7, 49, 60, 21, -3, -2, + 0, -4, 5, 46, 62, 24, -3, -2, + 0, -4, 4, 43, 63, 26, -2, -2, + 0, -3, 2, 41, 63, 29, -2, -2, + 0, -3, 1, 38, 64, 32, -1, -3, + 0, 0, 0, 128, 0, 0, 0, 0, + -1, 3, -7, 127, 8, -3, 1, 0, + -2, 5, -13, 125, 17, -6, 3, -1, + -3, 7, -17, 121, 27, -10, 5, -2, + -4, 9, -20, 115, 37, -13, 6, -2, + -4, 10, -23, 108, 48, -16, 8, -3, + -4, 10, -24, 100, 59, -19, 9, -3, + -4, 11, -24, 90, 70, -21, 10, -4, + -4, 11, -23, 80, 80, -23, 11, -4, + -4, 10, -21, 70, 90, -24, 11, -4, + -3, 9, -19, 59, 100, -24, 10, -4, + -3, 8, -16, 48, 108, -23, 10, -4, + -2, 6, -13, 37, 115, -20, 9, -4, + -2, 5, -10, 27, 121, -17, 7, -3, + -1, 3, -6, 17, 125, -13, 5, -2, + 0, 1, -3, 8, 127, -7, 3, -1, + 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, +]; + +pub const DC_QLOOKUP: [i32; 768] = [ + 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 const AC_QLOOKUP: [i32; 768] = [ + 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 const COS64_LOOKUP: [i32; 33] = [ + 16384, 16364, 16305, 16207, 16069, 15893, 15679, 15426, 15137, 14811, 14449, 14053, 13623, 13160, 12665, 12140, + 11585, 11003, 10394, 9760, 9102, 8423, 7723, 7005, 6270, 5520, 4756, 3981, 3196, 2404, 1606, 804, + 0, +]; + +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 DEFAULT_SCAN_8X8: [u16; 64] = [ + 0, 8, 1, 16, 9, 2, 17, 24, 10, 3, 18, 25, 32, 11, 4, 26, + 33, 19, 40, 12, 34, 27, 5, 41, 20, 48, 13, 35, 42, 28, 21, 6, + 49, 56, 36, 43, 29, 7, 14, 50, 57, 44, 22, 37, 15, 51, 58, 30, + 45, 23, 52, 59, 38, 31, 60, 53, 46, 39, 61, 54, 47, 62, 55, 63, +]; + +pub const COL_SCAN_8X8: [u16; 64] = [ + 0, 8, 16, 1, 24, 9, 32, 17, 2, 40, 25, 10, 33, 18, 48, 3, + 26, 41, 11, 56, 19, 34, 4, 49, 27, 42, 12, 35, 20, 57, 50, 28, + 5, 43, 13, 36, 58, 51, 21, 44, 6, 29, 59, 37, 14, 52, 22, 7, + 45, 60, 30, 15, 38, 53, 23, 46, 31, 61, 39, 54, 47, 62, 55, 63, +]; + +pub const ROW_SCAN_8X8: [u16; 64] = [ + 0, 1, 2, 8, 9, 3, 16, 10, 4, 17, 11, 24, 5, 18, 25, 12, + 19, 26, 32, 6, 13, 20, 33, 27, 7, 34, 40, 21, 28, 41, 14, 35, + 48, 42, 29, 36, 49, 22, 43, 15, 56, 37, 50, 44, 30, 57, 23, 51, + 58, 45, 38, 52, 31, 59, 53, 46, 60, 39, 61, 47, 54, 55, 62, 63, +]; + +pub const DEFAULT_SCAN_16X16: [u16; 256] = [ + 0, 16, 1, 32, 17, 2, 48, 33, 18, 3, 64, 34, 49, 19, 65, 80, + 50, 4, 35, 66, 20, 81, 96, 51, 5, 36, 82, 97, 67, 112, 21, 52, + 98, 37, 83, 113, 6, 68, 128, 53, 22, 99, 114, 84, 7, 129, 38, 69, + 100, 115, 144, 130, 85, 54, 23, 8, 145, 39, 70, 116, 101, 131, 160, 146, + 55, 86, 24, 71, 132, 117, 161, 40, 9, 102, 147, 176, 162, 87, 56, 25, + 133, 118, 177, 148, 72, 103, 41, 163, 10, 192, 178, 88, 57, 134, 149, 119, + 26, 164, 73, 104, 193, 42, 179, 208, 11, 135, 89, 165, 120, 150, 58, 194, + 180, 27, 74, 209, 105, 151, 136, 43, 90, 224, 166, 195, 181, 121, 210, 59, + 12, 152, 106, 167, 196, 75, 137, 225, 211, 240, 182, 122, 91, 28, 197, 13, + 226, 168, 183, 153, 44, 212, 138, 107, 241, 60, 29, 123, 198, 184, 227, 169, + 242, 76, 213, 154, 45, 92, 14, 199, 139, 61, 228, 214, 170, 185, 243, 108, + 77, 155, 30, 15, 200, 229, 124, 215, 244, 93, 46, 186, 171, 201, 109, 140, + 230, 62, 216, 245, 31, 125, 78, 156, 231, 47, 187, 202, 217, 94, 246, 141, + 63, 232, 172, 110, 247, 157, 79, 218, 203, 126, 233, 188, 248, 95, 173, 142, + 219, 111, 249, 234, 158, 127, 189, 204, 250, 235, 143, 174, 220, 205, 159, 251, + 190, 221, 175, 236, 237, 191, 206, 252, 222, 253, 207, 238, 223, 254, 239, 255, +]; + +pub const COL_SCAN_16X16: [u16; 256] = [ + 0, 16, 32, 48, 1, 64, 17, 80, 33, 96, 49, 2, 65, 112, 18, 81, + 34, 128, 50, 97, 3, 66, 144, 19, 113, 35, 82, 160, 98, 51, 129, 4, + 67, 176, 20, 114, 145, 83, 36, 99, 130, 52, 192, 5, 161, 68, 115, 21, + 146, 84, 208, 177, 37, 131, 100, 53, 162, 224, 69, 6, 116, 193, 147, 85, + 22, 240, 132, 38, 178, 101, 163, 54, 209, 117, 70, 7, 148, 194, 86, 179, + 225, 23, 133, 39, 164, 8, 102, 210, 241, 55, 195, 118, 149, 71, 180, 24, + 87, 226, 134, 165, 211, 40, 103, 56, 72, 150, 196, 242, 119, 9, 181, 227, + 88, 166, 25, 135, 41, 104, 212, 57, 151, 197, 120, 73, 243, 182, 136, 167, + 213, 89, 10, 228, 105, 152, 198, 26, 42, 121, 183, 244, 168, 58, 137, 229, + 74, 214, 90, 153, 199, 184, 11, 106, 245, 27, 122, 230, 169, 43, 215, 59, + 200, 138, 185, 246, 75, 12, 91, 154, 216, 231, 107, 28, 44, 201, 123, 170, + 60, 247, 232, 76, 139, 13, 92, 217, 186, 248, 155, 108, 29, 124, 45, 202, + 233, 171, 61, 14, 77, 140, 15, 249, 93, 30, 187, 156, 218, 46, 109, 125, + 62, 172, 78, 203, 31, 141, 234, 94, 47, 188, 63, 157, 110, 250, 219, 79, + 126, 204, 173, 142, 95, 189, 111, 235, 158, 220, 251, 127, 174, 143, 205, 236, + 159, 190, 221, 252, 175, 206, 237, 191, 253, 222, 238, 207, 254, 223, 239, 255, +]; + +pub const ROW_SCAN_16X16: [u16; 256] = [ + 0, 1, 2, 16, 3, 17, 4, 18, 32, 5, 33, 19, 6, 34, 48, 20, + 49, 7, 35, 21, 50, 64, 8, 36, 65, 22, 51, 37, 80, 9, 66, 52, + 23, 38, 81, 67, 10, 53, 24, 82, 68, 96, 39, 11, 54, 83, 97, 69, + 25, 98, 84, 40, 112, 55, 12, 70, 99, 113, 85, 26, 41, 56, 114, 100, + 13, 71, 128, 86, 27, 115, 101, 129, 42, 57, 72, 116, 14, 87, 130, 102, + 144, 73, 131, 117, 28, 58, 15, 88, 43, 145, 103, 132, 146, 118, 74, 160, + 89, 133, 104, 29, 59, 147, 119, 44, 161, 148, 90, 105, 134, 162, 120, 176, + 75, 135, 149, 30, 60, 163, 177, 45, 121, 91, 106, 164, 178, 150, 192, 136, + 165, 179, 31, 151, 193, 76, 122, 61, 137, 194, 107, 152, 180, 208, 46, 166, + 167, 195, 92, 181, 138, 209, 123, 153, 224, 196, 77, 168, 210, 182, 240, 108, + 197, 62, 154, 225, 183, 169, 211, 47, 139, 93, 184, 226, 212, 241, 198, 170, + 124, 155, 199, 78, 213, 185, 109, 227, 200, 63, 228, 242, 140, 214, 171, 186, + 156, 229, 243, 125, 94, 201, 244, 215, 216, 230, 141, 187, 202, 79, 172, 110, + 157, 245, 217, 231, 95, 246, 232, 126, 203, 247, 233, 173, 218, 142, 111, 158, + 188, 248, 127, 234, 219, 249, 189, 204, 143, 174, 159, 250, 235, 205, 220, 175, + 190, 251, 221, 191, 206, 236, 207, 237, 252, 222, 253, 223, 238, 239, 254, 255, +]; + +pub const DEFAULT_SCAN_32X32: [u16; 1024] = [ + 0, 32, 1, 64, 33, 2, 96, 65, 34, 128, 3, 97, 66, 160, 129, 35, + 98, 4, 67, 130, 161, 192, 36, 99, 224, 5, 162, 193, 68, 131, 37, 100, + 225, 194, 256, 163, 69, 132, 6, 226, 257, 288, 195, 101, 164, 38, 258, 7, + 227, 289, 133, 320, 70, 196, 165, 290, 259, 228, 39, 321, 102, 352, 8, 197, + 71, 134, 322, 291, 260, 353, 384, 229, 166, 103, 40, 354, 323, 292, 135, 385, + 198, 261, 72, 9, 416, 167, 386, 355, 230, 324, 104, 293, 41, 417, 199, 136, + 262, 387, 448, 325, 356, 10, 73, 418, 231, 168, 449, 294, 388, 105, 419, 263, + 42, 200, 357, 450, 137, 480, 74, 326, 232, 11, 389, 169, 295, 420, 106, 451, + 481, 358, 264, 327, 201, 43, 138, 512, 482, 390, 296, 233, 170, 421, 75, 452, + 359, 12, 513, 265, 483, 328, 107, 202, 514, 544, 422, 391, 453, 139, 44, 234, + 484, 297, 360, 171, 76, 515, 545, 266, 329, 454, 13, 423, 203, 108, 546, 485, + 576, 298, 235, 140, 361, 330, 172, 547, 45, 455, 267, 577, 486, 77, 204, 362, + 608, 14, 299, 578, 109, 236, 487, 609, 331, 141, 579, 46, 15, 173, 610, 363, + 78, 205, 16, 110, 237, 611, 142, 47, 174, 79, 206, 17, 111, 238, 48, 143, + 80, 175, 112, 207, 49, 18, 239, 81, 113, 19, 50, 82, 114, 51, 83, 115, + 640, 516, 392, 268, 144, 20, 672, 641, 548, 517, 424, 393, 300, 269, 176, 145, + 52, 21, 704, 673, 642, 580, 549, 518, 456, 425, 394, 332, 301, 270, 208, 177, + 146, 84, 53, 22, 736, 705, 674, 643, 612, 581, 550, 519, 488, 457, 426, 395, + 364, 333, 302, 271, 240, 209, 178, 147, 116, 85, 54, 23, 737, 706, 675, 613, + 582, 551, 489, 458, 427, 365, 334, 303, 241, 210, 179, 117, 86, 55, 738, 707, + 614, 583, 490, 459, 366, 335, 242, 211, 118, 87, 739, 615, 491, 367, 243, 119, + 768, 644, 520, 396, 272, 148, 24, 800, 769, 676, 645, 552, 521, 428, 397, 304, + 273, 180, 149, 56, 25, 832, 801, 770, 708, 677, 646, 584, 553, 522, 460, 429, + 398, 336, 305, 274, 212, 181, 150, 88, 57, 26, 864, 833, 802, 771, 740, 709, + 678, 647, 616, 585, 554, 523, 492, 461, 430, 399, 368, 337, 306, 275, 244, 213, + 182, 151, 120, 89, 58, 27, 865, 834, 803, 741, 710, 679, 617, 586, 555, 493, + 462, 431, 369, 338, 307, 245, 214, 183, 121, 90, 59, 866, 835, 742, 711, 618, + 587, 494, 463, 370, 339, 246, 215, 122, 91, 867, 743, 619, 495, 371, 247, 123, + 896, 772, 648, 524, 400, 276, 152, 28, 928, 897, 804, 773, 680, 649, 556, 525, + 432, 401, 308, 277, 184, 153, 60, 29, 960, 929, 898, 836, 805, 774, 712, 681, + 650, 588, 557, 526, 464, 433, 402, 340, 309, 278, 216, 185, 154, 92, 61, 30, + 992, 961, 930, 899, 868, 837, 806, 775, 744, 713, 682, 651, 620, 589, 558, 527, + 496, 465, 434, 403, 372, 341, 310, 279, 248, 217, 186, 155, 124, 93, 62, 31, + 993, 962, 931, 869, 838, 807, 745, 714, 683, 621, 590, 559, 497, 466, 435, 373, + 342, 311, 249, 218, 187, 125, 94, 63, 994, 963, 870, 839, 746, 715, 622, 591, + 498, 467, 374, 343, 250, 219, 126, 95, 995, 871, 747, 623, 499, 375, 251, 127, + 900, 776, 652, 528, 404, 280, 156, 932, 901, 808, 777, 684, 653, 560, 529, 436, + 405, 312, 281, 188, 157, 964, 933, 902, 840, 809, 778, 716, 685, 654, 592, 561, + 530, 468, 437, 406, 344, 313, 282, 220, 189, 158, 996, 965, 934, 903, 872, 841, + 810, 779, 748, 717, 686, 655, 624, 593, 562, 531, 500, 469, 438, 407, 376, 345, + 314, 283, 252, 221, 190, 159, 997, 966, 935, 873, 842, 811, 749, 718, 687, 625, + 594, 563, 501, 470, 439, 377, 346, 315, 253, 222, 191, 998, 967, 874, 843, 750, + 719, 626, 595, 502, 471, 378, 347, 254, 223, 999, 875, 751, 627, 503, 379, 255, + 904, 780, 656, 532, 408, 284, 936, 905, 812, 781, 688, 657, 564, 533, 440, 409, + 316, 285, 968, 937, 906, 844, 813, 782, 720, 689, 658, 596, 565, 534, 472, 441, + 410, 348, 317, 286, 1000, 969, 938, 907, 876, 845, 814, 783, 752, 721, 690, 659, + 628, 597, 566, 535, 504, 473, 442, 411, 380, 349, 318, 287, 1001, 970, 939, 877, + 846, 815, 753, 722, 691, 629, 598, 567, 505, 474, 443, 381, 350, 319, 1002, 971, + 878, 847, 754, 723, 630, 599, 506, 475, 382, 351, 1003, 879, 755, 631, 507, 383, + 908, 784, 660, 536, 412, 940, 909, 816, 785, 692, 661, 568, 537, 444, 413, 972, + 941, 910, 848, 817, 786, 724, 693, 662, 600, 569, 538, 476, 445, 414, 1004, 973, + 942, 911, 880, 849, 818, 787, 756, 725, 694, 663, 632, 601, 570, 539, 508, 477, + 446, 415, 1005, 974, 943, 881, 850, 819, 757, 726, 695, 633, 602, 571, 509, 478, + 447, 1006, 975, 882, 851, 758, 727, 634, 603, 510, 479, 1007, 883, 759, 635, 511, + 912, 788, 664, 540, 944, 913, 820, 789, 696, 665, 572, 541, 976, 945, 914, 852, + 821, 790, 728, 697, 666, 604, 573, 542, 1008, 977, 946, 915, 884, 853, 822, 791, + 760, 729, 698, 667, 636, 605, 574, 543, 1009, 978, 947, 885, 854, 823, 761, 730, + 699, 637, 606, 575, 1010, 979, 886, 855, 762, 731, 638, 607, 1011, 887, 763, 639, + 916, 792, 668, 948, 917, 824, 793, 700, 669, 980, 949, 918, 856, 825, 794, 732, + 701, 670, 1012, 981, 950, 919, 888, 857, 826, 795, 764, 733, 702, 671, 1013, 982, + 951, 889, 858, 827, 765, 734, 703, 1014, 983, 890, 859, 766, 735, 1015, 891, 767, + 920, 796, 952, 921, 828, 797, 984, 953, 922, 860, 829, 798, 1016, 985, 954, 923, + 892, 861, 830, 799, 1017, 986, 955, 893, 862, 831, 1018, 987, 894, 863, 1019, 895, + 924, 956, 925, 988, 957, 926, 1020, 989, 958, 927, 1021, 990, 959, 1022, 991, 1023, +]; + +pub const COEFBAND_4X4: [u8; 16] = [ + 0, 1, 1, 2, 2, 2, 3, 3, 3, 3, 4, 4, 4, 5, 5, 5, +]; + +pub const COEFBAND_8X8PLUS: [u8; 1024] = [ + 0, 1, 1, 2, 2, 2, 3, 3, 3, 3, 4, 4, 4, 4, 4, 4, + 4, 4, 4, 4, 4, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, + 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, + 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, + 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, + 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, + 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, + 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, + 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, + 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, + 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, + 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, + 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, + 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, + 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, + 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, + 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, + 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, + 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, + 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, + 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, + 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, + 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, + 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, + 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, + 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, + 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, + 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, + 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, + 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, + 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, + 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, + 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, + 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, + 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, + 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, + 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, + 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, + 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, + 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, + 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, + 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, + 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, + 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, + 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, + 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, + 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, + 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, + 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, + 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, + 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, + 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, + 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, + 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, + 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, + 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, + 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, + 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, + 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, + 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, + 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, + 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, + 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, + 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 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 MV_REF_BLOCKS: [i8; 208] = [ + -1, 0, 0, -1, -1, -1, -2, 0, 0, -2, -2, -1, -1, -2, -2, -2, + -1, 0, 0, -1, -1, -1, -2, 0, 0, -2, -2, -1, -1, -2, -2, -2, + -1, 0, 0, -1, -1, -1, -2, 0, 0, -2, -2, -1, -1, -2, -2, -2, + -1, 0, 0, -1, -1, -1, -2, 0, 0, -2, -2, -1, -1, -2, -2, -2, + 0, -1, -1, 0, 1, -1, -1, -1, 0, -2, -2, 0, -2, -1, -1, -2, + -1, 0, 0, -1, -1, 1, -1, -1, -2, 0, 0, -2, -1, -2, -2, -1, + -1, 0, 0, -1, -1, 1, 1, -1, -1, -1, -3, 0, 0, -3, -3, -3, + 0, -1, -1, 0, 2, -1, -1, -1, -1, 1, 0, -3, -3, 0, -3, -3, + -1, 0, 0, -1, -1, 2, -1, -1, 1, -1, -3, 0, 0, -3, -3, -3, + -1, 1, 1, -1, -1, 2, 2, -1, -1, -1, -3, 0, 0, -3, -3, -3, + 0, -1, -1, 0, 4, -1, -1, 2, -1, -1, 0, -3, -3, 0, 2, -1, + -1, 0, 0, -1, -1, 4, 2, -1, -1, -1, -3, 0, 0, -3, -1, 2, + -1, 3, 3, -1, -1, 4, 4, -1, -1, -1, -1, 0, 0, -1, -1, 6, +]; + +pub const MODE_2_COUNTER: [u8; 14] = [ + 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 0, 0, 3, 1, +]; + +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_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_Y_MODE_PROBS: [u8; 36] = [ + 65, 32, 18, 144, 162, 194, 41, 51, 98, + 132, 68, 18, 165, 217, 196, 45, 40, 78, + 173, 80, 19, 176, 240, 193, 64, 35, 46, + 221, 135, 38, 194, 248, 121, 96, 85, 29, +]; + +pub const DEFAULT_UV_MODE_PROBS: [u8; 90] = [ + 120, 7, 76, 176, 208, 126, 28, 54, 103, + 48, 12, 154, 155, 139, 90, 34, 117, 119, + 67, 6, 25, 204, 243, 158, 13, 21, 96, + 97, 5, 44, 131, 176, 139, 48, 68, 97, + 83, 5, 42, 156, 111, 152, 26, 49, 152, + 80, 5, 58, 178, 74, 83, 33, 62, 145, + 86, 5, 32, 154, 192, 168, 14, 22, 163, + 85, 5, 32, 156, 216, 148, 19, 29, 73, + 77, 7, 64, 116, 132, 122, 37, 126, 120, + 101, 21, 107, 181, 192, 103, 19, 67, 125, +]; + +pub const DEFAULT_SKIP_PROB: [u8; 3] = [ + 192, 128, 64, +]; + +pub const DEFAULT_IS_INTER_PROB: [u8; 4] = [ + 9, 102, 187, 225, +]; + +pub const DEFAULT_COMP_MODE_PROB: [u8; 5] = [ + 239, 183, 119, 96, 41, +]; + +pub const DEFAULT_COMP_REF_PROB: [u8; 5] = [ + 50, 126, 123, 221, 226, +]; + +pub const DEFAULT_SINGLE_REF_PROB: [u8; 10] = [ + 33, 16, + 77, 74, + 142, 142, + 172, 170, + 238, 247, +]; + +pub const DEFAULT_MV_SIGN_PROB: [u8; 2] = [ + 128, 128, +]; + +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_BIT_PROB: [u8; 2] = [ + 216, 208, +]; + +pub const DEFAULT_TX_PROBS: [u8; 24] = [ + 0, 0, 0, + 0, 0, 0, + 100, 0, 0, + 66, 0, 0, + 20, 152, 0, + 15, 101, 0, + 3, 136, 37, + 5, 52, 13, +]; + +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_INTERP_FILTER_PROBS: [u8; 8] = [ + 235, 162, + 36, 255, + 34, 3, + 149, 144, +]; + +pub const DEFAULT_MV_JOINT_PROBS: [u8; 3] = [ + 32, 64, 96, +]; + +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_FR_PROBS: [u8; 12] = [ + 128, 128, 64, + 96, 112, 64, + 128, 128, 64, + 96, 112, 64, +]; + +pub const DEFAULT_MV_CLASS0_HP_PROB: [u8; 2] = [ + 160, 160, +]; + +pub const DEFAULT_MV_FR_PROBS: [u8; 6] = [ + 64, 96, 64, + 64, 96, 64, +]; + +pub const DEFAULT_MV_HP_PROB: [u8; 2] = [ + 128, 128, +]; + +pub const DEFAULT_COEF_PROBS: [u8; 1728] = [ + 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, + 125, 34, 187, + 52, 41, 133, + 6, 31, 56, + 0, 0, 0, + 0, 0, 0, + 0, 0, 0, + 37, 109, 153, + 51, 102, 147, + 23, 87, 128, + 8, 67, 101, + 1, 41, 63, + 1, 19, 29, + 31, 154, 185, + 17, 127, 175, + 6, 96, 145, + 2, 73, 114, + 1, 51, 82, + 1, 28, 45, + 23, 163, 200, + 10, 131, 185, + 2, 93, 148, + 1, 67, 111, + 1, 41, 69, + 1, 14, 24, + 29, 176, 217, + 12, 145, 201, + 3, 101, 156, + 1, 69, 111, + 1, 39, 63, + 1, 14, 23, + 57, 192, 233, + 25, 154, 215, + 6, 109, 167, + 3, 78, 118, + 1, 48, 69, + 1, 21, 29, + 202, 105, 245, + 108, 106, 216, + 18, 90, 144, + 0, 0, 0, + 0, 0, 0, + 0, 0, 0, + 33, 172, 219, + 64, 149, 206, + 14, 117, 177, + 5, 90, 141, + 2, 61, 95, + 1, 37, 57, + 33, 179, 220, + 11, 140, 198, + 1, 89, 148, + 1, 60, 104, + 1, 33, 57, + 1, 12, 21, + 30, 181, 221, + 8, 141, 198, + 1, 87, 145, + 1, 58, 100, + 1, 31, 55, + 1, 12, 20, + 32, 186, 224, + 7, 142, 198, + 1, 86, 143, + 1, 58, 100, + 1, 31, 55, + 1, 12, 22, + 57, 192, 227, + 20, 143, 204, + 3, 96, 154, + 1, 68, 112, + 1, 42, 69, + 1, 19, 32, + 212, 35, 215, + 113, 47, 169, + 29, 48, 105, + 0, 0, 0, + 0, 0, 0, + 0, 0, 0, + 74, 129, 203, + 106, 120, 203, + 49, 107, 178, + 19, 84, 144, + 4, 50, 84, + 1, 15, 25, + 71, 172, 217, + 44, 141, 209, + 15, 102, 173, + 6, 76, 133, + 2, 51, 89, + 1, 24, 42, + 64, 185, 231, + 31, 148, 216, + 8, 103, 175, + 3, 74, 131, + 1, 46, 81, + 1, 18, 30, + 65, 196, 235, + 25, 157, 221, + 5, 105, 174, + 1, 67, 120, + 1, 38, 69, + 1, 15, 30, + 65, 204, 238, + 30, 156, 224, + 7, 107, 177, + 2, 70, 124, + 1, 42, 73, + 1, 18, 34, + 225, 86, 251, + 144, 104, 235, + 42, 99, 181, + 0, 0, 0, + 0, 0, 0, + 0, 0, 0, + 85, 175, 239, + 112, 165, 229, + 29, 136, 200, + 12, 103, 162, + 6, 77, 123, + 2, 53, 84, + 75, 183, 239, + 30, 155, 221, + 3, 106, 171, + 1, 74, 128, + 1, 44, 76, + 1, 17, 28, + 73, 185, 240, + 27, 159, 222, + 2, 107, 172, + 1, 75, 127, + 1, 42, 73, + 1, 17, 29, + 62, 190, 238, + 21, 159, 222, + 2, 107, 172, + 1, 72, 122, + 1, 40, 71, + 1, 18, 32, + 61, 199, 240, + 27, 161, 226, + 4, 113, 180, + 1, 76, 129, + 1, 46, 80, + 1, 23, 41, + 7, 27, 153, + 5, 30, 95, + 1, 16, 30, + 0, 0, 0, + 0, 0, 0, + 0, 0, 0, + 50, 75, 127, + 57, 75, 124, + 27, 67, 108, + 10, 54, 86, + 1, 33, 52, + 1, 12, 18, + 43, 125, 151, + 26, 108, 148, + 7, 83, 122, + 2, 59, 89, + 1, 38, 60, + 1, 17, 27, + 23, 144, 163, + 13, 112, 154, + 2, 75, 117, + 1, 50, 81, + 1, 31, 51, + 1, 14, 23, + 18, 162, 185, + 6, 123, 171, + 1, 78, 125, + 1, 51, 86, + 1, 31, 54, + 1, 14, 23, + 15, 199, 227, + 3, 150, 204, + 1, 91, 146, + 1, 55, 95, + 1, 30, 53, + 1, 11, 20, + 19, 55, 240, + 19, 59, 196, + 3, 52, 105, + 0, 0, 0, + 0, 0, 0, + 0, 0, 0, + 41, 166, 207, + 104, 153, 199, + 31, 123, 181, + 14, 101, 152, + 5, 72, 106, + 1, 36, 52, + 35, 176, 211, + 12, 131, 190, + 2, 88, 144, + 1, 60, 101, + 1, 36, 60, + 1, 16, 28, + 28, 183, 213, + 8, 134, 191, + 1, 86, 142, + 1, 56, 96, + 1, 30, 53, + 1, 12, 20, + 20, 190, 215, + 4, 135, 192, + 1, 84, 139, + 1, 53, 91, + 1, 28, 49, + 1, 11, 20, + 13, 196, 216, + 2, 137, 192, + 1, 86, 143, + 1, 57, 99, + 1, 32, 56, + 1, 13, 24, + 211, 29, 217, + 96, 47, 156, + 22, 43, 87, + 0, 0, 0, + 0, 0, 0, + 0, 0, 0, + 78, 120, 193, + 111, 116, 186, + 46, 102, 164, + 15, 80, 128, + 2, 49, 76, + 1, 18, 28, + 71, 161, 203, + 42, 132, 192, + 10, 98, 150, + 3, 69, 109, + 1, 44, 70, + 1, 18, 29, + 57, 186, 211, + 30, 140, 196, + 4, 93, 146, + 1, 62, 102, + 1, 38, 65, + 1, 16, 27, + 47, 199, 217, + 14, 145, 196, + 1, 88, 142, + 1, 57, 98, + 1, 36, 62, + 1, 15, 26, + 26, 219, 229, + 5, 155, 207, + 1, 94, 151, + 1, 60, 104, + 1, 36, 62, + 1, 16, 28, + 233, 29, 248, + 146, 47, 220, + 43, 52, 140, + 0, 0, 0, + 0, 0, 0, + 0, 0, 0, + 100, 163, 232, + 179, 161, 222, + 63, 142, 204, + 37, 113, 174, + 26, 89, 137, + 18, 68, 97, + 85, 181, 230, + 32, 146, 209, + 7, 100, 164, + 3, 71, 121, + 1, 45, 77, + 1, 18, 30, + 65, 187, 230, + 20, 148, 207, + 2, 97, 159, + 1, 68, 116, + 1, 40, 70, + 1, 14, 29, + 40, 194, 227, + 8, 147, 204, + 1, 94, 155, + 1, 65, 112, + 1, 39, 66, + 1, 14, 26, + 16, 208, 228, + 3, 151, 207, + 1, 98, 160, + 1, 67, 117, + 1, 41, 74, + 1, 17, 31, + 17, 38, 140, + 7, 34, 80, + 1, 17, 29, + 0, 0, 0, + 0, 0, 0, + 0, 0, 0, + 37, 75, 128, + 41, 76, 128, + 26, 66, 116, + 12, 52, 94, + 2, 32, 55, + 1, 10, 16, + 50, 127, 154, + 37, 109, 152, + 16, 82, 121, + 5, 59, 85, + 1, 35, 54, + 1, 13, 20, + 40, 142, 167, + 17, 110, 157, + 2, 71, 112, + 1, 44, 72, + 1, 27, 45, + 1, 11, 17, + 30, 175, 188, + 9, 124, 169, + 1, 74, 116, + 1, 48, 78, + 1, 30, 49, + 1, 11, 18, + 10, 222, 223, + 2, 150, 194, + 1, 83, 128, + 1, 48, 79, + 1, 27, 45, + 1, 11, 17, + 36, 41, 235, + 29, 36, 193, + 10, 27, 111, + 0, 0, 0, + 0, 0, 0, + 0, 0, 0, + 85, 165, 222, + 177, 162, 215, + 110, 135, 195, + 57, 113, 168, + 23, 83, 120, + 10, 49, 61, + 85, 190, 223, + 36, 139, 200, + 5, 90, 146, + 1, 60, 103, + 1, 38, 65, + 1, 18, 30, + 72, 202, 223, + 23, 141, 199, + 2, 86, 140, + 1, 56, 97, + 1, 36, 61, + 1, 16, 27, + 55, 218, 225, + 13, 145, 200, + 1, 86, 141, + 1, 57, 99, + 1, 35, 61, + 1, 13, 22, + 15, 235, 212, + 1, 132, 184, + 1, 84, 139, + 1, 57, 97, + 1, 34, 56, + 1, 14, 23, + 181, 21, 201, + 61, 37, 123, + 10, 38, 71, + 0, 0, 0, + 0, 0, 0, + 0, 0, 0, + 47, 106, 172, + 95, 104, 173, + 42, 93, 159, + 18, 77, 131, + 4, 50, 81, + 1, 17, 23, + 62, 147, 199, + 44, 130, 189, + 28, 102, 154, + 18, 75, 115, + 2, 44, 65, + 1, 12, 19, + 55, 153, 210, + 24, 130, 194, + 3, 93, 146, + 1, 61, 97, + 1, 31, 50, + 1, 10, 16, + 49, 186, 223, + 17, 148, 204, + 1, 96, 142, + 1, 53, 83, + 1, 26, 44, + 1, 11, 17, + 13, 217, 212, + 2, 136, 180, + 1, 78, 124, + 1, 50, 83, + 1, 29, 49, + 1, 14, 23, + 197, 13, 247, + 82, 17, 222, + 25, 17, 162, + 0, 0, 0, + 0, 0, 0, + 0, 0, 0, + 126, 186, 247, + 234, 191, 243, + 176, 177, 234, + 104, 158, 220, + 66, 128, 186, + 55, 90, 137, + 111, 197, 242, + 46, 158, 219, + 9, 104, 171, + 2, 65, 125, + 1, 44, 80, + 1, 17, 91, + 104, 208, 245, + 39, 168, 224, + 3, 109, 162, + 1, 79, 124, + 1, 50, 102, + 1, 43, 102, + 84, 220, 246, + 31, 177, 231, + 2, 115, 180, + 1, 79, 134, + 1, 55, 77, + 1, 60, 79, + 43, 243, 240, + 8, 180, 217, + 1, 115, 166, + 1, 84, 121, + 1, 51, 67, + 1, 16, 6, +]; + diff --git a/crates/vp9/src/tables.rs b/crates/vp9/src/tables.rs new file mode 100644 index 0000000..71ceabe --- /dev/null +++ b/crates/vp9/src/tables.rs @@ -0,0 +1,288 @@ +//! Constants, small lookup tables and decode trees of the VP9 specification (sections 3, 6, 9.3 +//! and 10.2). The large tables are extracted from the specification text into `spec_tables`. + +pub use crate::spec_tables::*; + +// Block sizes (7.4.3). +pub const BLOCK_8X8: u8 = 3; +pub const BLOCK_16X16: u8 = 6; +pub const BLOCK_64X64: u8 = 12; +pub const BLOCK_INVALID: u8 = 14; + +// Transform sizes / modes. +pub const TX_4X4: u8 = 0; +pub const TX_8X8: u8 = 1; +pub const TX_16X16: u8 = 2; +pub const TX_32X32: u8 = 3; +pub const TX_MODE_SELECT: u8 = 4; + +// Prediction modes (7.4.5, 7.4.11). +pub const DC_PRED: u8 = 0; +pub const V_PRED: u8 = 1; +pub const H_PRED: u8 = 2; +pub const D45_PRED: u8 = 3; +pub const D135_PRED: u8 = 4; +pub const D117_PRED: u8 = 5; +pub const D153_PRED: u8 = 6; +pub const D207_PRED: u8 = 7; +pub const D63_PRED: u8 = 8; +pub const TM_PRED: u8 = 9; +pub const NEARESTMV: u8 = 10; +pub const NEARMV: u8 = 11; +pub const ZEROMV: u8 = 12; +pub const NEWMV: u8 = 13; + +// Reference frames. +pub const NONE: i8 = -1; +pub const INTRA_FRAME: i8 = 0; +pub const LAST_FRAME: i8 = 1; +pub const GOLDEN_FRAME: i8 = 2; +pub const ALTREF_FRAME: i8 = 3; + +// Transform types. +pub const DCT_DCT: u8 = 0; +pub const ADST_DCT: u8 = 1; +pub const DCT_ADST: u8 = 2; +pub const ADST_ADST: u8 = 3; + +// Interpolation filters. +pub const SWITCHABLE: u8 = 4; + +// Segmentation features. +pub const SEG_LVL_ALT_Q: usize = 0; +pub const SEG_LVL_ALT_L: usize = 1; +pub const SEG_LVL_REF_FRAME: usize = 2; +pub const SEG_LVL_SKIP: usize = 3; +pub const MAX_SEGMENTS: usize = 8; + +pub const MAX_LOOP_FILTER: i32 = 63; +pub const MIN_TILE_WIDTH_B64: u32 = 4; +pub const MAX_TILE_WIDTH_B64: u32 = 64; +pub const COMPANDED_MVREF_THRESH: i32 = 8; +pub const MV_BORDER: i32 = 128; +pub const INTERP_EXTEND: i32 = 4; +pub const BORDERINPIXELS: i32 = 160; +pub const COUNT_SAT: u32 = 20; +pub const MAX_UPDATE_FACTOR: u32 = 128; + +pub const B_WIDTH_LOG2: [u8; 13] = [0, 0, 1, 1, 1, 2, 2, 2, 3, 3, 3, 4, 4]; +pub const B_HEIGHT_LOG2: [u8; 13] = [0, 1, 0, 1, 2, 1, 2, 3, 2, 3, 4, 3, 4]; +pub const NUM_4X4_WIDE: [u8; 13] = [1, 1, 2, 2, 2, 4, 4, 4, 8, 8, 8, 16, 16]; +pub const NUM_4X4_HIGH: [u8; 13] = [1, 2, 1, 2, 4, 2, 4, 8, 4, 8, 16, 8, 16]; +pub const MI_WIDTH_LOG2: [u8; 13] = [0, 0, 0, 0, 0, 1, 1, 1, 2, 2, 2, 3, 3]; +pub const NUM_8X8_WIDE: [u8; 13] = [1, 1, 1, 1, 1, 2, 2, 2, 4, 4, 4, 8, 8]; +pub const NUM_8X8_HIGH: [u8; 13] = [1, 1, 1, 1, 2, 1, 2, 4, 2, 4, 8, 4, 8]; +pub const SIZE_GROUP: [u8; 13] = [0, 0, 0, 1, 1, 1, 2, 2, 2, 3, 3, 3, 3]; +pub const MAX_TXSIZE: [u8; 13] = [0, 0, 0, 1, 1, 1, 2, 2, 2, 3, 3, 3, 3]; +pub const TX_MODE_TO_BIGGEST_TX_SIZE: [u8; 5] = [0, 1, 2, 3, 3]; + +/// subsize_lookup[partition][bsize] (10.2). +pub const SUBSIZE_LOOKUP: [[u8; 13]; 4] = [ + [0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12], + [14, 14, 14, 2, 14, 14, 5, 14, 14, 8, 14, 14, 11], + [14, 14, 14, 1, 14, 14, 4, 14, 14, 7, 14, 14, 10], + [14, 14, 14, 0, 14, 14, 3, 14, 14, 6, 14, 14, 9], +]; + +/// ss_size_lookup[bsize][subsampling_x][subsampling_y] (6.4.23). +pub const SS_SIZE_LOOKUP: [[[u8; 2]; 2]; 13] = [ + [[0, 14], [14, 14]], + [[1, 0], [14, 14]], + [[2, 14], [0, 14]], + [[3, 2], [1, 0]], + [[4, 3], [14, 1]], + [[5, 14], [3, 2]], + [[6, 5], [4, 3]], + [[7, 6], [14, 4]], + [[8, 14], [6, 5]], + [[9, 8], [7, 6]], + [[10, 9], [14, 7]], + [[11, 14], [9, 8]], + [[12, 11], [10, 9]], +]; + +/// mode2txfm_map (10.2). +pub const MODE2TXFM: [u8; 14] = + [DCT_DCT, ADST_DCT, DCT_ADST, DCT_DCT, ADST_ADST, ADST_DCT, DCT_ADST, DCT_ADST, ADST_DCT, ADST_ADST, DCT_DCT, DCT_DCT, DCT_DCT, DCT_DCT]; + +/// counter_to_context (6.5.1); 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]; + +/// idx_n_column_to_subblock (6.5.11). +pub const IDX_N_COLUMN_TO_SUBBLOCK: [[u8; 2]; 4] = [[1, 2], [1, 3], [3, 2], [3, 3]]; + +/// literal_to_type (6.2.7). +pub const LITERAL_TO_TYPE: [u8; 4] = [1, 0, 2, 3]; + +pub const SEGMENTATION_FEATURE_BITS: [u32; 4] = [8, 6, 2, 0]; +pub const SEGMENTATION_FEATURE_SIGNED: [bool; 4] = [true, true, false, false]; + +/// extra_bits[token] = (cat, numExtra, base) (6.4.26). +pub 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)]; + +/// cat_probs (6.4.26). +pub 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], +]; + +// Decode trees (9.3.1); leaves are stored negated. +pub const PARTITION_TREE: [i8; 6] = [0, 2, -1, 4, -2, -3]; +pub const INTRA_MODE_TREE: [i8; 18] = [ + -(DC_PRED as i8), + 2, + -(TM_PRED as i8), + 4, + -(V_PRED as i8), + 6, + 8, + 12, + -(H_PRED as i8), + 10, + -(D135_PRED as i8), + -(D117_PRED as i8), + -(D45_PRED as i8), + 14, + -(D63_PRED as i8), + 16, + -(D153_PRED as i8), + -(D207_PRED as i8), +]; +pub const SEGMENT_TREE: [i8; 14] = [2, 4, 6, 8, 10, 12, 0, -1, -2, -3, -4, -5, -6, -7]; +pub const TX_SIZE_32_TREE: [i8; 6] = [0, 2, -1, 4, -2, -3]; +pub const TX_SIZE_16_TREE: [i8; 4] = [0, 2, -1, -2]; +pub const TX_SIZE_8_TREE: [i8; 2] = [0, -1]; +/// inter_mode_tree: values are inter_mode = y_mode - NEARESTMV. +pub const INTER_MODE_TREE: [i8; 6] = [-2, 2, 0, 4, -1, -3]; +pub const INTERP_FILTER_TREE: [i8; 4] = [0, 2, -1, -2]; +pub const MV_JOINT_TREE: [i8; 6] = [0, 2, -1, 4, -2, -3]; +pub 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]; +pub const MV_FR_TREE: [i8; 6] = [0, 2, -1, 4, -2, -3]; +/// small_token_tree used by coefficient probability adaptation (8.4.3). +pub const SMALL_TOKEN_TREE: [i8; 6] = [0, 0, 0, 4, -1, -2]; +pub const BINARY_TREE: [i8; 2] = [0, -1]; + +/// Probabilities of the full token tree for each model probability (9.3.2 pareto): row +/// `p - 1` holds the probabilities of token-tree nodes 2..9 when `coef_probs[..][2] == p`. +pub fn pareto_full() -> &'static [[u8; 8]; 255] { + use std::sync::OnceLock; + static T: OnceLock<[[u8; 8]; 255]> = OnceLock::new(); + T.get_or_init(|| { + let mut t = [[0u8; 8]; 255]; + for p in 1..=255usize { + let x = (p - 1) / 2; + for n in 0..8 { + t[p - 1][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 + }) +} + +/// Scan order for a transform size and type (6.4.25). +pub fn scan(tx_size: u8, tx_type: u8) -> &'static [u16] { + match (tx_size, tx_type) { + (0, ADST_DCT) => &ROW_SCAN_4X4, + (0, DCT_ADST) => &COL_SCAN_4X4, + (0, _) => &DEFAULT_SCAN_4X4, + (1, ADST_DCT) => &ROW_SCAN_8X8, + (1, DCT_ADST) => &COL_SCAN_8X8, + (1, _) => &DEFAULT_SCAN_8X8, + (2, ADST_DCT) => &ROW_SCAN_16X16, + (2, DCT_ADST) => &COL_SCAN_16X16, + (2, _) => &DEFAULT_SCAN_16X16, + _ => &DEFAULT_SCAN_32X32, + } +} + +/// Coefficient context neighbours (9.3.2, `nb`) for every scan position of a transform size and +/// type: `[c] = (nb0, nb1)` positions in the block. +pub fn neighbors(tx_size: u8, tx_type: u8) -> &'static [(u16, u16)] { + use std::sync::OnceLock; + static T: OnceLock>> = OnceLock::new(); + let all = T.get_or_init(|| { + let mut v = Vec::new(); + for tx in 0..4u8 { + for ty in 0..4u8 { + let sc = scan(tx, ty); + let n = 4usize << tx; + let mut nb = vec![(0u16, 0u16); sc.len()]; + for (c, &pos) in sc.iter().enumerate().skip(1) { + let pos = pos as usize; + let (i, j) = (pos / n, pos % n); + nb[c] = if i > 0 && j > 0 { + let a = ((i - 1) * n + j) as u16; + let a2 = (i * n + j - 1) as u16; + // Only the 4x4..16x16 scans depend on the type; 32x32 is always DCT_DCT. + let ty = if tx == 3 { DCT_DCT } else { ty }; + match ty { + DCT_ADST => (a, a), + ADST_DCT => (a2, a2), + _ => (a, a2), + } + } else if i > 0 { + let a = ((i - 1) * n + j) as u16; + (a, a) + } else { + let a = (i * n + j - 1) as u16; + (a, a) + }; + } + v.push(nb); + } + } + v + }); + &all[(tx_size * 4 + tx_type) as usize] +} + +#[cfg(test)] +mod tests { + use super::*; + + #[test] + fn trees_are_consistent() { + // Every leaf value appears exactly once. + fn leaves(t: &[i8]) -> Vec { + let mut v: Vec = + t.iter().enumerate().filter(|(i, x)| **x <= 0 && !(*i == 0 && t.len() > 2 && t[0] == 0 && t[1] == 0)).map(|(_, x)| -x).collect(); + v.sort(); + v + } + assert_eq!(leaves(&INTRA_MODE_TREE), (0..10).collect::>()); + assert_eq!(leaves(&MV_CLASS_TREE), (0..11).collect::>()); + assert_eq!(leaves(&PARTITION_TREE), (0..4).collect::>()); + assert_eq!(leaves(&INTER_MODE_TREE), (0..4).collect::>()); + } + + #[test] + fn pareto_rows() { + let p = pareto_full(); + assert_eq!(p[0], [3, 86, 128, 6, 86, 23, 88, 29]); + assert_eq!(p[254][..], PARETO_TABLE[127 * 8..128 * 8]); + } + + #[test] + fn default_coef_probs_unused_are_zero() { + for (k, chunk) in DEFAULT_COEF_PROBS.chunks(3 * 6).enumerate() { + let band = k % 6; + for (ctx, p) in chunk.chunks(3).enumerate() { + if band == 0 && ctx >= 3 { + assert_eq!(p, [0, 0, 0]); + } else { + assert!(p.iter().all(|&x| x > 0), "band {band} ctx {ctx}"); + } + } + } + } +} diff --git a/crates/vp9/src/tile.rs b/crates/vp9/src/tile.rs new file mode 100644 index 0000000..7d9827d --- /dev/null +++ b/crates/vp9/src/tile.rs @@ -0,0 +1,1087 @@ +//! Tile decoding: partition trees, mode info (6.4), and block reconstruction. One `TileDecoder` +//! decodes all tiles of one tile column (tile columns are independent of each other; tile rows +//! of a column are decoded in order). + +use crate::boolcoder::BoolDecoder; +use crate::frame::{Frame, MiGrid, MiInfo, Mv}; +use crate::header::{FrameHeader, Segmentation}; +use crate::probs::{Counts, FrameContext}; +use crate::tables::*; + +mod recon; + +/// A reference frame as used by the current frame, with its scale factors (8.5.2.3). +pub struct RefUse<'a> { + pub frame: &'a Frame, + pub x_scale: i32, + pub y_scale: i32, + pub x_step: i32, + pub y_step: i32, +} + +/// Read-only data shared by all tile columns of a frame. +pub struct FrameShared<'a> { + pub h: &'a FrameHeader, + pub fc: &'a FrameContext, + pub seg: &'a Segmentation, + /// PrevSegmentIds (MiRows x MiCols). + pub prev_seg_ids: &'a [u8], + /// Mode info of the previous frame when UsePrevFrameMvs is 1. + pub prev_mi: Option<&'a MiGrid>, + /// References for LAST, GOLDEN, ALTREF. + pub refs: [Option>; 3], + /// Quantizers per segment: [segment][plane > 0][dc = 0 / ac = 1]. + pub seg_q: [[[i32; 2]; 2]; 8], + /// Whether syntax elements need to be counted (backward adaptation enabled). + pub counting: bool, + pub pools: &'a crate::frame::Pools, +} + +/// Output of one tile column: samples, mode info and segment ids of its columns. +pub struct Strip { + pub mi_col_start: usize, + pub mi_col_end: usize, + /// Mode info, (mi_col_end - mi_col_start) x MiRows. + pub mi: Vec, + pub seg_ids: Vec, + pub planes: [Vec; 3], + pub strides: [usize; 3], + /// Plane x coordinate of the first column of each plane buffer. + pub x_off: [usize; 3], + pub counts: Box, + /// Statistics. + pub compound_blocks: u64, + pub scaled_blocks: u64, + pub intra_blocks: u64, + pub inter_blocks: u64, +} + +/// Per-block state (the variables of 6.4.4 - 6.4.20). +#[derive(Clone, Copy, Default)] +struct Blk { + r: usize, + c: usize, + size: u8, + avail_u: bool, + avail_l: bool, + seg_id: u8, + skip: bool, + tx_size: u8, + is_inter: bool, + y_mode: u8, + sub_modes: [u8; 4], + uv_mode: u8, + ref_frame: [i8; 2], + interp_filter: u8, + block_mvs: [[Mv; 4]; 2], + left_ref: [i8; 2], + above_ref: [i8; 2], + left_intra: bool, + above_intra: bool, + left_single: bool, + above_single: bool, + eob_total: u32, +} + +pub struct TileDecoder<'a> { + s: &'a FrameShared<'a>, + h: &'a FrameHeader, + fc: &'a FrameContext, + pub strip: Strip, + mi_cols: usize, + mi_rows: usize, + ss_x: usize, + ss_y: usize, + bit_depth: u8, + // Contexts (indexed by absolute position). + above_nonzero: [Vec; 3], + left_nonzero: [Vec; 3], + above_partition: Vec, + left_partition: Vec, + above_seg_pred: Vec, + left_seg_pred: Vec, + // MV prediction state. + ref_list_mv: [Mv; 2], + ref_mv_count: usize, + mode_context: [u8; 4], + nearest_mv: [Mv; 2], + near_mv: [Mv; 2], + best_mv: [Mv; 2], + // Scratch. + coefs: Vec, + token_cache: Vec, + pred: Vec, + pred2: Vec, + mc_tmp: Vec, + mc_win: Vec, + /// First error met while decoding (e.g. a block referencing an unusable reference frame). + pub error: Option, +} + +impl<'a> TileDecoder<'a> { + pub fn new(s: &'a FrameShared<'a>, mi_col_start: usize, mi_col_end: usize) -> Self { + let h = s.h; + let (mi_cols, mi_rows) = (h.mi_cols as usize, h.mi_rows as usize); + let (ss_x, ss_y) = (h.color.subsampling_x as usize, h.color.subsampling_y as usize); + let sb_rows = h.sb64_rows as usize; + let sb_cols = h.sb64_cols as usize; + let strip_w = (mi_col_end - mi_col_start).div_ceil(8) * 64; + let strip_h = sb_rows * 64; + let x0 = mi_col_start * 8; + let uv_len = (strip_w >> ss_x) * (strip_h >> ss_y); + let planes = [s.pools.samples.take(strip_w * strip_h, 0), s.pools.samples.take(uv_len, 0), s.pools.samples.take(uv_len, 0)]; + let mi_w = mi_col_end - mi_col_start; + let strip = Strip { + mi_col_start, + mi_col_end, + mi: s.pools.mi.take(mi_w * mi_rows, MiInfo::default()), + seg_ids: vec![0; mi_w * mi_rows], + planes, + strides: [strip_w, strip_w >> ss_x, strip_w >> ss_x], + x_off: [x0, x0 >> ss_x, x0 >> ss_x], + counts: Box::default(), + compound_blocks: 0, + scaled_blocks: 0, + intra_blocks: 0, + inter_blocks: 0, + }; + let an = sb_cols * 16 + 16; + let ln = sb_rows * 16 + 16; + TileDecoder { + s, + h, + fc: s.fc, + strip, + mi_cols, + mi_rows, + ss_x, + ss_y, + bit_depth: h.color.bit_depth, + above_nonzero: [vec![0; an], vec![0; an], vec![0; an]], + left_nonzero: [vec![0; ln], vec![0; ln], vec![0; ln]], + above_partition: vec![0; sb_cols * 8 + 8], + left_partition: vec![0; sb_rows * 8 + 8], + above_seg_pred: vec![0; sb_cols * 8 + 8], + left_seg_pred: vec![0; sb_rows * 8 + 8], + ref_list_mv: [Mv::ZERO; 2], + ref_mv_count: 0, + mode_context: [0; 4], + nearest_mv: [Mv::ZERO; 2], + near_mv: [Mv::ZERO; 2], + best_mv: [Mv::ZERO; 2], + coefs: vec![0; 1024], + token_cache: vec![0; 1024], + pred: vec![0; 64 * 64], + pred2: vec![0; 64 * 64], + // Scaled prediction reads up to 5x the block height (8.5.2.3), unscaled h + 7 rows. + mc_tmp: vec![0; if s.refs.iter().flatten().any(|r| r.x_scale != 1 << 14 || r.y_scale != 1 << 14) { 64 * (64 * 5 + 16) } else { 64 * 71 }], + mc_win: vec![0; 71 * 71], + error: None, + } + } + + #[inline] + fn mi_at(&self, r: usize, c: usize) -> &MiInfo { + &self.strip.mi[r * (self.strip.mi_col_end - self.strip.mi_col_start) + c - self.strip.mi_col_start] + } + + /// decode_tile (6.4.2) for one tile of this column. Returns false if the data ran out badly. + pub fn decode_tile(&mut self, data: &[u8], mi_row_start: usize, mi_row_end: usize) -> bool { + let mut bd = BoolDecoder::new(data); + let (cs, ce) = (self.strip.mi_col_start, self.strip.mi_col_end); + for r in (mi_row_start..mi_row_end).step_by(8) { + // clear_left_context + for p in 0..3 { + let sy = if p > 0 { self.ss_y } else { 0 }; + let s = (r * 2) >> sy; + let e = (s + (16 >> sy)).min(self.left_nonzero[p].len()); + self.left_nonzero[p][s..e].fill(0); + } + self.left_partition[r..r + 8].fill(0); + self.left_seg_pred[r..r + 8].fill(0); + for c in (cs..ce).step_by(8) { + self.decode_partition(&mut bd, r, c, BLOCK_64X64); + } + } + true + } + + fn decode_partition(&mut self, bd: &mut BoolDecoder, r: usize, c: usize, bsize: u8) { + if r >= self.mi_rows || c >= self.mi_cols { + return; + } + let num8x8 = NUM_8X8_WIDE[bsize as usize] as usize; + let half = num8x8 >> 1; + let has_rows = (r + half) < self.mi_rows; + let has_cols = (c + half) < self.mi_cols; + let bsl = MI_WIDTH_LOG2[bsize as usize] as usize; + let boffset = 3 - bsl; + let mut above = 0u8; + let mut left = 0u8; + for i in 0..num8x8 { + above |= self.above_partition[c + i]; + left |= self.left_partition[r + i]; + } + let above = (above >> boffset) & 1; + let left = (left >> boffset) & 1; + let ctx = bsl * 4 + left as usize * 2 + above as usize; + let probs: &[u8] = if self.h.frame_is_intra { &KF_PARTITION_PROBS[ctx * 3..ctx * 3 + 3] } else { &self.fc.partition[ctx] }; + let partition = if has_rows && has_cols { + bd.read_tree(&PARTITION_TREE, probs) + } else if has_cols { + if bd.read_bool(probs[1]) { 3 } else { 1 } + } else if has_rows { + if bd.read_bool(probs[2]) { 3 } else { 2 } + } else { + 3 + }; + if self.s.counting { + self.strip.counts.partition[ctx][partition as usize] += 1; + } + let subsize = SUBSIZE_LOOKUP[partition as usize][bsize as usize]; + if subsize < BLOCK_8X8 || partition == 0 { + self.decode_block(bd, r, c, subsize); + } else if partition == 1 { + self.decode_block(bd, r, c, subsize); + if has_rows { + self.decode_block(bd, r + half, c, subsize); + } + } else if partition == 2 { + self.decode_block(bd, r, c, subsize); + if has_cols { + self.decode_block(bd, r, c + half, subsize); + } + } else { + self.decode_partition(bd, r, c, subsize); + self.decode_partition(bd, r, c + half, subsize); + self.decode_partition(bd, r + half, c, subsize); + self.decode_partition(bd, r + half, c + half, subsize); + } + if bsize == BLOCK_8X8 || partition != 3 { + let a = 15 >> B_WIDTH_LOG2[subsize as usize]; + let l = 15 >> B_HEIGHT_LOG2[subsize as usize]; + self.above_partition[c..c + num8x8].fill(a); + self.left_partition[r..r + num8x8].fill(l); + } + } + + fn decode_block(&mut self, bd: &mut BoolDecoder, r: usize, c: usize, size: u8) { + let mut b = Blk { r, c, size, avail_u: r > 0, avail_l: c > self.strip.mi_col_start, ..Default::default() }; + // Chroma block size must be valid (7.4.3); treat violations as a damaged stream. + if size >= BLOCK_8X8 && SS_SIZE_LOOKUP[size as usize][self.ss_x][self.ss_y] == BLOCK_INVALID { + b.size = BLOCK_8X8; + } + if self.h.frame_is_intra { + self.intra_frame_mode_info(bd, &mut b); + } else { + self.inter_frame_mode_info(bd, &mut b); + } + if b.is_inter { + self.strip.inter_blocks += 1; + } else { + self.strip.intra_blocks += 1; + } + self.residual(bd, &mut b); + if b.is_inter && b.size >= BLOCK_8X8 && b.eob_total == 0 { + b.skip = true; + } + let info = MiInfo { + sb_size: b.size, + skip: b.skip, + tx_size: b.tx_size, + y_mode: b.y_mode, + sub_modes: b.sub_modes, + seg_id: b.seg_id, + ref_frame: b.ref_frame, + interp_filter: b.interp_filter, + mv: b.block_mvs, + }; + let bh = NUM_8X8_HIGH[b.size as usize] as usize; + let bw = NUM_8X8_WIDE[b.size as usize] as usize; + let mi_w = self.strip.mi_col_end - self.strip.mi_col_start; + let (y1, x1) = ((r + bh).min(self.mi_rows), (c + bw).min(self.strip.mi_col_end)); + for y in r..y1 { + let row = y * mi_w; + for x in c..x1 { + self.strip.mi[row + x - self.strip.mi_col_start] = info; + self.strip.seg_ids[row + x - self.strip.mi_col_start] = b.seg_id; + } + } + } + + // ---------------------------------------------------------------- mode info (6.4.5 - 6.4.20) + + fn intra_frame_mode_info(&mut self, bd: &mut BoolDecoder, b: &mut Blk) { + // intra_segment_id + b.seg_id = if self.s.seg.enabled && self.s.seg.update_map { bd.read_tree(&SEGMENT_TREE, &self.s.seg.tree_probs) } else { 0 }; + self.read_skip(bd, b); + self.read_tx_size(bd, b, true); + b.ref_frame = [INTRA_FRAME, NONE]; + b.is_inter = false; + let above_mi = if b.avail_u { Some(*self.mi_at(b.r - 1, b.c)) } else { None }; + let left_mi = if b.avail_l { Some(*self.mi_at(b.r, b.c - 1)) } else { None }; + if b.size >= BLOCK_8X8 { + let above = above_mi.map_or(DC_PRED, |m| m.sub_modes[2]); + let left = left_mi.map_or(DC_PRED, |m| m.sub_modes[1]); + let o = (above as usize * 10 + left as usize) * 9; + let mode = bd.read_tree(&INTRA_MODE_TREE, &KF_Y_MODE_PROBS[o..o + 9]); + b.y_mode = mode; + b.sub_modes = [mode; 4]; + } else { + let n4w = NUM_4X4_WIDE[b.size as usize] as usize; + let n4h = NUM_4X4_HIGH[b.size as usize] as usize; + let mut mode = 0; + let mut idy = 0; + while idy < 2 { + let mut idx = 0; + while idx < 2 { + let above = if idy > 0 { b.sub_modes[idx] } else { above_mi.map_or(DC_PRED, |m| m.sub_modes[2 + idx]) }; + let left = if idx > 0 { b.sub_modes[idy * 2] } else { left_mi.map_or(DC_PRED, |m| m.sub_modes[1 + idy * 2]) }; + let o = (above as usize * 10 + left as usize) * 9; + mode = bd.read_tree(&INTRA_MODE_TREE, &KF_Y_MODE_PROBS[o..o + 9]); + for y2 in 0..n4h { + for x2 in 0..n4w { + b.sub_modes[(idy + y2) * 2 + idx + x2] = mode; + } + } + idx += n4w; + } + idy += n4h; + } + b.y_mode = mode; + } + let o = b.y_mode as usize * 9; + b.uv_mode = bd.read_tree(&INTRA_MODE_TREE, &KF_UV_MODE_PROBS[o..o + 9]); + } + + fn read_skip(&mut self, bd: &mut BoolDecoder, b: &mut Blk) { + if self.s.seg.feature_active(b.seg_id, SEG_LVL_SKIP) { + b.skip = true; + } else { + let mut ctx = 0; + if b.avail_u { + ctx += self.mi_at(b.r - 1, b.c).skip as usize; + } + if b.avail_l { + ctx += self.mi_at(b.r, b.c - 1).skip as usize; + } + b.skip = bd.read_bool(self.fc.skip[ctx]); + if self.s.counting { + self.strip.counts.skip[ctx][b.skip as usize] += 1; + } + } + } + + fn read_tx_size(&mut self, bd: &mut BoolDecoder, b: &mut Blk, allow_select: bool) { + let max_tx = MAX_TXSIZE[b.size as usize]; + if allow_select && self.h.tx_mode == TX_MODE_SELECT && b.size >= BLOCK_8X8 { + let mut above = max_tx; + let mut left = max_tx; + if b.avail_u { + let m = self.mi_at(b.r - 1, b.c); + if !m.skip { + above = m.tx_size; + } + } + if b.avail_l { + let m = self.mi_at(b.r, b.c - 1); + if !m.skip { + left = m.tx_size; + } + } + if !b.avail_l { + left = above; + } + if !b.avail_u { + above = left; + } + let ctx = ((above + left) > max_tx) as usize; + let tree: &[i8] = match max_tx { + 3 => &TX_SIZE_32_TREE, + 2 => &TX_SIZE_16_TREE, + _ => &TX_SIZE_8_TREE, + }; + b.tx_size = bd.read_tree(tree, &self.fc.tx[max_tx as usize][ctx]); + if self.s.counting { + self.strip.counts.tx[max_tx as usize][ctx][b.tx_size as usize] += 1; + } + } else { + b.tx_size = max_tx.min(TX_MODE_TO_BIGGEST_TX_SIZE[self.h.tx_mode as usize]); + } + } + + fn inter_frame_mode_info(&mut self, bd: &mut BoolDecoder, b: &mut Blk) { + b.left_ref = if b.avail_l { self.mi_at(b.r, b.c - 1).ref_frame } else { [INTRA_FRAME, NONE] }; + b.above_ref = if b.avail_u { self.mi_at(b.r - 1, b.c).ref_frame } else { [INTRA_FRAME, NONE] }; + b.left_intra = b.left_ref[0] <= INTRA_FRAME; + b.above_intra = b.above_ref[0] <= INTRA_FRAME; + b.left_single = b.left_ref[1] <= NONE; + b.above_single = b.above_ref[1] <= NONE; + self.inter_segment_id(bd, b); + self.read_skip(bd, b); + self.read_is_inter(bd, b); + let allow = !b.skip || !b.is_inter; + self.read_tx_size(bd, b, allow); + if b.is_inter { + self.inter_block_mode_info(bd, b); + } else { + self.intra_block_mode_info(bd, b); + } + } + + fn get_segment_id(&self, b: &Blk) -> u8 { + let bw = NUM_8X8_WIDE[b.size as usize] as usize; + let bh = NUM_8X8_HIGH[b.size as usize] as usize; + let xmis = (self.mi_cols - b.c).min(bw); + let ymis = (self.mi_rows - b.r).min(bh); + let mut seg = 7; + let prev = self.s.prev_seg_ids; + if prev.is_empty() { + return 0; + } + for y in 0..ymis { + for x in 0..xmis { + seg = seg.min(prev[(b.r + y) * self.mi_cols + b.c + x]); + } + } + seg + } + + fn inter_segment_id(&mut self, bd: &mut BoolDecoder, b: &mut Blk) { + let seg = self.s.seg; + if !seg.enabled { + b.seg_id = 0; + return; + } + let predicted = self.get_segment_id(b); + if !seg.update_map { + b.seg_id = predicted; + return; + } + if seg.temporal_update { + let ctx = (self.left_seg_pred[b.r] + self.above_seg_pred[b.c]) as usize; + let pred_flag = bd.read_bool(seg.pred_probs[ctx]); + b.seg_id = if pred_flag { predicted } else { bd.read_tree(&SEGMENT_TREE, &seg.tree_probs) }; + let bw = NUM_8X8_WIDE[b.size as usize] as usize; + let bh = NUM_8X8_HIGH[b.size as usize] as usize; + self.above_seg_pred[b.c..b.c + bw].fill(pred_flag as u8); + self.left_seg_pred[b.r..b.r + bh].fill(pred_flag as u8); + } else { + b.seg_id = bd.read_tree(&SEGMENT_TREE, &seg.tree_probs); + } + } + + fn read_is_inter(&mut self, bd: &mut BoolDecoder, b: &mut Blk) { + if self.s.seg.feature_active(b.seg_id, SEG_LVL_REF_FRAME) { + b.is_inter = self.s.seg.feature_data[b.seg_id as usize][SEG_LVL_REF_FRAME] != INTRA_FRAME as i16; + } else { + let ctx = if b.avail_u && b.avail_l { + if b.left_intra && b.above_intra { 3 } else { (b.left_intra || b.above_intra) as usize } + } else if b.avail_u || b.avail_l { + 2 * (if b.avail_u { b.above_intra } else { b.left_intra }) as usize + } else { + 0 + }; + b.is_inter = bd.read_bool(self.fc.is_inter[ctx]); + if self.s.counting { + self.strip.counts.is_inter[ctx][b.is_inter as usize] += 1; + } + } + } + + fn intra_block_mode_info(&mut self, bd: &mut BoolDecoder, b: &mut Blk) { + b.ref_frame = [INTRA_FRAME, NONE]; + if b.size >= BLOCK_8X8 { + let ctx = SIZE_GROUP[b.size as usize] as usize; + let mode = bd.read_tree(&INTRA_MODE_TREE, &self.fc.y_mode[ctx]); + if self.s.counting { + self.strip.counts.intra_mode[ctx][mode as usize] += 1; + } + b.y_mode = mode; + b.sub_modes = [mode; 4]; + } else { + let n4w = NUM_4X4_WIDE[b.size as usize] as usize; + let n4h = NUM_4X4_HIGH[b.size as usize] as usize; + let mut mode = 0; + let mut idy = 0; + while idy < 2 { + let mut idx = 0; + while idx < 2 { + mode = bd.read_tree(&INTRA_MODE_TREE, &self.fc.y_mode[0]); + if self.s.counting { + self.strip.counts.intra_mode[0][mode as usize] += 1; + } + for y2 in 0..n4h { + for x2 in 0..n4w { + b.sub_modes[(idy + y2) * 2 + idx + x2] = mode; + } + } + idx += n4w; + } + idy += n4h; + } + b.y_mode = mode; + } + b.uv_mode = bd.read_tree(&INTRA_MODE_TREE, &self.fc.uv_mode[b.y_mode as usize]); + if self.s.counting { + self.strip.counts.uv_mode[b.y_mode as usize][b.uv_mode as usize] += 1; + } + } + + fn inter_block_mode_info(&mut self, bd: &mut BoolDecoder, b: &mut Blk) { + self.read_ref_frames(bd, b); + let is_compound = b.ref_frame[1] > INTRA_FRAME; + if is_compound { + self.strip.compound_blocks += 1; + } + for j in 0..1 + is_compound as usize { + let rf = b.ref_frame[j]; + self.find_mv_refs(b, rf, -1); + self.find_best_ref_mvs(b, j); + } + b.sub_modes = [DC_PRED; 4]; + if self.s.seg.feature_active(b.seg_id, SEG_LVL_SKIP) { + b.y_mode = ZEROMV; + } else if b.size >= BLOCK_8X8 { + let ctx = self.mode_context[b.ref_frame[0] as usize] as usize; + let inter_mode = bd.read_tree(&INTER_MODE_TREE, &self.fc.inter_mode[ctx.min(6)]); + if self.s.counting { + self.strip.counts.inter_mode[ctx.min(6)][inter_mode as usize] += 1; + } + b.y_mode = NEARESTMV + inter_mode; + } + b.interp_filter = if self.h.interp_filter == SWITCHABLE { + let left = if b.avail_l && b.left_ref[0] > INTRA_FRAME { self.mi_at(b.r, b.c - 1).interp_filter } else { 3 }; + let above = if b.avail_u && b.above_ref[0] > INTRA_FRAME { self.mi_at(b.r - 1, b.c).interp_filter } else { 3 }; + let ctx = if left == above { + left + } else if left == 3 && above != 3 { + above + } else if left != 3 && above == 3 { + left + } else { + 3 + } as usize; + let f = bd.read_tree(&INTERP_FILTER_TREE, &self.fc.interp_filter[ctx]); + if self.s.counting { + self.strip.counts.interp_filter[ctx][f as usize] += 1; + } + f + } else { + self.h.interp_filter + }; + b.block_mvs = [[Mv::ZERO; 4]; 2]; + if b.size < BLOCK_8X8 { + let n4w = NUM_4X4_WIDE[b.size as usize] as usize; + let n4h = NUM_4X4_HIGH[b.size as usize] as usize; + let mut idy = 0; + while idy < 2 { + let mut idx = 0; + while idx < 2 { + let ctx = self.mode_context[b.ref_frame[0] as usize] as usize; + let inter_mode = bd.read_tree(&INTER_MODE_TREE, &self.fc.inter_mode[ctx.min(6)]); + if self.s.counting { + self.strip.counts.inter_mode[ctx.min(6)][inter_mode as usize] += 1; + } + b.y_mode = NEARESTMV + inter_mode; + if b.y_mode == NEARESTMV || b.y_mode == NEARMV { + for j in 0..1 + is_compound as usize { + self.append_sub8x8_mvs(b, (idy * 2 + idx) as i32, j); + } + } + let mv = self.assign_mv(bd, b, is_compound); + for y2 in 0..n4h { + for x2 in 0..n4w { + let blk = (idy + y2) * 2 + idx + x2; + for l in 0..1 + is_compound as usize { + b.block_mvs[l][blk] = mv[l]; + } + } + } + idx += n4w; + } + idy += n4h; + } + } else { + let mv = self.assign_mv(bd, b, is_compound); + for l in 0..1 + is_compound as usize { + b.block_mvs[l] = [mv[l]; 4]; + } + } + } + + fn read_ref_frames(&mut self, bd: &mut BoolDecoder, b: &mut Blk) { + let seg = self.s.seg; + if seg.feature_active(b.seg_id, SEG_LVL_REF_FRAME) { + b.ref_frame = [(seg.feature_data[b.seg_id as usize][SEG_LVL_REF_FRAME] as i8).clamp(LAST_FRAME, ALTREF_FRAME), NONE]; + return; + } + let h = self.h; + let comp_mode = if h.reference_mode == 2 { + let ctx = self.comp_mode_ctx(b); + let v = bd.read_bool(self.fc.comp_mode[ctx]); + if self.s.counting { + self.strip.counts.comp_mode[ctx][v as usize] += 1; + } + v as u8 + } else { + h.reference_mode + }; + if comp_mode == 1 { + let idx = h.ref_frame_sign_bias[h.comp_fixed_ref as usize] as usize; + let ctx = self.comp_ref_ctx(b); + let comp_ref = bd.read_bool(self.fc.comp_ref[ctx]); + if self.s.counting { + self.strip.counts.comp_ref[ctx][comp_ref as usize] += 1; + } + b.ref_frame[idx] = h.comp_fixed_ref; + b.ref_frame[1 - idx] = h.comp_var_ref[comp_ref as usize]; + } else { + let ctx = self.single_ref_p1_ctx(b); + let p1 = bd.read_bool(self.fc.single_ref[ctx][0]); + if self.s.counting { + self.strip.counts.single_ref[ctx][0][p1 as usize] += 1; + } + b.ref_frame[0] = if p1 { + let ctx2 = self.single_ref_p2_ctx(b); + let p2 = bd.read_bool(self.fc.single_ref[ctx2][1]); + if self.s.counting { + self.strip.counts.single_ref[ctx2][1][p2 as usize] += 1; + } + if p2 { ALTREF_FRAME } else { GOLDEN_FRAME } + } else { + LAST_FRAME + }; + b.ref_frame[1] = NONE; + } + } + + fn comp_mode_ctx(&self, b: &Blk) -> usize { + let fixed = self.h.comp_fixed_ref; + (if b.avail_u && b.avail_l { + if b.above_single && b.left_single { + ((b.above_ref[0] == fixed) ^ (b.left_ref[0] == fixed)) as u8 + } else if b.above_single { + 2 + (b.above_ref[0] == fixed || b.above_intra) as u8 + } else if b.left_single { + 2 + (b.left_ref[0] == fixed || b.left_intra) as u8 + } else { + 4 + } + } else if b.avail_u { + if b.above_single { (b.above_ref[0] == fixed) as u8 } else { 3 } + } else if b.avail_l { + if b.left_single { (b.left_ref[0] == fixed) as u8 } else { 3 } + } else { + 1 + }) as usize + } + + fn comp_ref_ctx(&self, b: &Blk) -> usize { + let h = self.h; + let fix_ref_idx = h.ref_frame_sign_bias[h.comp_fixed_ref as usize] as usize; + let var_ref_idx = 1 - fix_ref_idx; + let var0 = h.comp_var_ref[0]; + let var1 = h.comp_var_ref[1]; + let fixed = h.comp_fixed_ref; + (if b.avail_u && b.avail_l { + if b.above_intra && b.left_intra { + 2 + } else if b.left_intra { + if b.above_single { 1 + 2 * (b.above_ref[0] != var1) as u8 } else { 1 + 2 * (b.above_ref[var_ref_idx] != var1) as u8 } + } else if b.above_intra { + if b.left_single { 1 + 2 * (b.left_ref[0] != var1) as u8 } else { 1 + 2 * (b.left_ref[var_ref_idx] != var1) as u8 } + } else { + let vrfa = if b.above_single { b.above_ref[0] } else { b.above_ref[var_ref_idx] }; + let vrfl = if b.left_single { b.left_ref[0] } else { b.left_ref[var_ref_idx] }; + if vrfa == vrfl && var1 == vrfa { + 0 + } else if b.left_single && b.above_single { + if (vrfa == fixed && vrfl == var0) || (vrfl == fixed && vrfa == var0) { + 4 + } else if vrfa == vrfl { + 3 + } else { + 1 + } + } else if b.left_single || b.above_single { + let vrfc = if b.left_single { vrfa } else { vrfl }; + let rfs = if b.above_single { vrfa } else { vrfl }; + if vrfc == var1 && rfs != var1 { + 1 + } else if rfs == var1 && vrfc != var1 { + 2 + } else { + 4 + } + } else if vrfa == vrfl { + 4 + } else { + 2 + } + } + } else if b.avail_u { + if b.above_intra { + 2 + } else if b.above_single { + 3 * (b.above_ref[0] != var1) as u8 + } else { + 4 * (b.above_ref[var_ref_idx] != var1) as u8 + } + } else if b.avail_l { + if b.left_intra { + 2 + } else if b.left_single { + 3 * (b.left_ref[0] != var1) as u8 + } else { + 4 * (b.left_ref[var_ref_idx] != var1) as u8 + } + } else { + 2 + }) as usize + } + + fn single_ref_p1_ctx(&self, b: &Blk) -> usize { + let (a, l) = (b.above_ref, b.left_ref); + (if b.avail_u && b.avail_l { + if b.above_intra && b.left_intra { + 2 + } else if b.left_intra { + if b.above_single { 4 * (a[0] == LAST_FRAME) as u8 } else { 1 + (a[0] == LAST_FRAME || a[1] == LAST_FRAME) as u8 } + } else if b.above_intra { + if b.left_single { 4 * (l[0] == LAST_FRAME) as u8 } else { 1 + (l[0] == LAST_FRAME || l[1] == LAST_FRAME) as u8 } + } else if b.above_single && b.left_single { + 2 * (a[0] == LAST_FRAME) as u8 + 2 * (l[0] == LAST_FRAME) as u8 + } else if !b.above_single && !b.left_single { + 1 + (a[0] == LAST_FRAME || a[1] == LAST_FRAME || l[0] == LAST_FRAME || l[1] == LAST_FRAME) as u8 + } else { + let rfs = if b.above_single { a[0] } else { l[0] }; + let crf1 = if b.above_single { l[0] } else { a[0] }; + let crf2 = if b.above_single { l[1] } else { a[1] }; + if rfs == LAST_FRAME { + 3 + (crf1 == LAST_FRAME || crf2 == LAST_FRAME) as u8 + } else { + (crf1 == LAST_FRAME || crf2 == LAST_FRAME) as u8 + } + } + } else if b.avail_u { + if b.above_intra { + 2 + } else if b.above_single { + 4 * (a[0] == LAST_FRAME) as u8 + } else { + 1 + (a[0] == LAST_FRAME || a[1] == LAST_FRAME) as u8 + } + } else if b.avail_l { + if b.left_intra { + 2 + } else if b.left_single { + 4 * (l[0] == LAST_FRAME) as u8 + } else { + 1 + (l[0] == LAST_FRAME || l[1] == LAST_FRAME) as u8 + } + } else { + 2 + }) as usize + } + + fn single_ref_p2_ctx(&self, b: &Blk) -> usize { + let (a, l) = (b.above_ref, b.left_ref); + const G: i8 = GOLDEN_FRAME; + const L: i8 = LAST_FRAME; + (if b.avail_u && b.avail_l { + if b.above_intra && b.left_intra { + 2 + } else if b.left_intra { + if b.above_single { if a[0] == L { 3 } else { 4 * (a[0] == G) as u8 } } else { 1 + 2 * (a[0] == G || a[1] == G) as u8 } + } else if b.above_intra { + if b.left_single { if l[0] == L { 3 } else { 4 * (l[0] == G) as u8 } } else { 1 + 2 * (l[0] == G || l[1] == G) as u8 } + } else if b.above_single && b.left_single { + if a[0] == L && l[0] == L { + 3 + } else if a[0] == L { + 4 * (l[0] == G) as u8 + } else if l[0] == L { + 4 * (a[0] == G) as u8 + } else { + 2 * (a[0] == G) as u8 + 2 * (l[0] == G) as u8 + } + } else if !b.above_single && !b.left_single { + if a[0] == l[0] && a[1] == l[1] { 3 * (a[0] == G || a[1] == G) as u8 } else { 2 } + } else { + let rfs = if b.above_single { a[0] } else { l[0] }; + let crf1 = if b.above_single { l[0] } else { a[0] }; + let crf2 = if b.above_single { l[1] } else { a[1] }; + if rfs == G { + 3 + (crf1 == G || crf2 == G) as u8 + } else if rfs == ALTREF_FRAME { + (crf1 == G || crf2 == G) as u8 + } else { + 1 + 2 * (crf1 == G || crf2 == G) as u8 + } + } + } else if b.avail_u { + if b.above_intra || (a[0] == L && b.above_single) { + 2 + } else if b.above_single { + 4 * (a[0] == G) as u8 + } else { + 3 * (a[0] == G || a[1] == G) as u8 + } + } else if b.avail_l { + if b.left_intra || (l[0] == L && b.left_single) { + 2 + } else if b.left_single { + 4 * (l[0] == G) as u8 + } else { + 3 * (l[0] == G || l[1] == G) as u8 + } + } else { + 2 + }) as usize + } + + /// assign_mv (6.4.18): returns Mv[0..2]. + fn assign_mv(&mut self, bd: &mut BoolDecoder, b: &Blk, is_compound: bool) -> [Mv; 2] { + let mut mv = [Mv::ZERO; 2]; + for i in 0..1 + is_compound as usize { + mv[i] = match b.y_mode { + NEWMV => self.read_mv(bd, i), + NEARESTMV => self.nearest_mv[i], + NEARMV => self.near_mv[i], + _ => Mv::ZERO, + }; + } + mv + } + + fn read_mv(&mut self, bd: &mut BoolDecoder, r: usize) -> Mv { + let use_hp = self.h.allow_high_precision_mv && use_mv_hp(self.best_mv[r]); + let joint = bd.read_tree(&MV_JOINT_TREE, &self.fc.mv_joint); + if self.s.counting { + self.strip.counts.mv_joint[joint as usize] += 1; + } + let mut diff = [0i32; 2]; + if joint == 2 || joint == 3 { + diff[0] = self.read_mv_component(bd, 0, use_hp); + } + if joint == 1 || joint == 3 { + diff[1] = self.read_mv_component(bd, 1, use_hp); + } + Mv::new(self.best_mv[r].row as i32 + diff[0], self.best_mv[r].col as i32 + diff[1]) + } + + fn read_mv_component(&mut self, bd: &mut BoolDecoder, comp: usize, use_hp: bool) -> i32 { + let fc = self.fc; + let counting = self.s.counting; + let counts = &mut self.strip.counts; + let sign = bd.read_bool(fc.mv_sign[comp]); + let class = bd.read_tree(&MV_CLASS_TREE, &fc.mv_class[comp]) as usize; + let mag = if class == 0 { + let c0 = bd.read_bool(fc.mv_class0_bit[comp]) as usize; + let fr = bd.read_tree(&MV_FR_TREE, &fc.mv_class0_fr[comp][c0]) as usize; + let hp = if use_hp { bd.read_bool(fc.mv_class0_hp[comp]) as usize } else { 1 }; + if counting { + counts.mv_class0_bit[comp][c0] += 1; + counts.mv_class0_fr[comp][c0][fr] += 1; + counts.mv_class0_hp[comp][hp] += 1; + } + ((c0 << 3) | (fr << 1) | hp) + 1 + } else { + let mut d = 0usize; + for i in 0..class { + let bit = bd.read_bool(fc.mv_bits[comp][i]) as usize; + if counting { + counts.mv_bits[comp][i][bit] += 1; + } + d |= bit << i; + } + let base = 2usize << (class + 2); + let fr = bd.read_tree(&MV_FR_TREE, &fc.mv_fr[comp]) as usize; + let hp = if use_hp { bd.read_bool(fc.mv_hp[comp]) as usize } else { 1 }; + if counting { + counts.mv_fr[comp][fr] += 1; + counts.mv_hp[comp][hp] += 1; + } + base + ((d << 3) | (fr << 1) | hp) + 1 + }; + if counting { + counts.mv_sign[comp][sign as usize] += 1; + counts.mv_class[comp][class] += 1; + } + if sign { -(mag as i32) } else { mag as i32 } + } + + // ------------------------------------------------------------ motion vector prediction (6.5) + + #[inline] + fn is_inside(&self, r: isize, c: isize) -> bool { + r >= 0 && (r as usize) < self.mi_rows && c >= self.strip.mi_col_start as isize && (c as usize) < self.strip.mi_col_end + } + + #[inline] + fn add_mv_ref_list(&mut self, mv: Mv) { + if self.ref_mv_count >= 2 { + return; + } + if self.ref_mv_count > 0 && mv == self.ref_list_mv[0] { + return; + } + self.ref_list_mv[self.ref_mv_count] = mv; + self.ref_mv_count += 1; + } + + fn if_same_ref_frame_add(&mut self, m: &MiInfo, ref_frame: i8) { + for j in 0..2 { + if m.ref_frame[j] == ref_frame { + self.add_mv_ref_list(m.mv[j][3]); + return; + } + } + } + + fn if_diff_ref_frame_add(&mut self, m: &MiInfo, ref_frame: i8) { + let bias = &self.h.ref_frame_sign_bias; + let mvs = [m.mv[0][3], m.mv[1][3]]; + let same = mvs[0] == mvs[1]; + let scale = + |f: i8, mv: Mv| -> Mv { if bias[f as usize] != bias[ref_frame as usize] { Mv::new(-(mv.row as i32), -(mv.col as i32)) } else { mv } }; + if m.ref_frame[0] > INTRA_FRAME && m.ref_frame[0] != ref_frame { + let mv = scale(m.ref_frame[0], mvs[0]); + self.add_mv_ref_list(mv); + } + if m.ref_frame[1] > INTRA_FRAME && m.ref_frame[1] != ref_frame && !same { + let mv = scale(m.ref_frame[1], mvs[1]); + self.add_mv_ref_list(mv); + } + } + + fn clamp_mv_row(&self, b: &Blk, v: i32, border: i32) -> i32 { + let bh = NUM_8X8_HIGH[b.size as usize] as i32; + let to_top = -((b.r as i32 * 8) * 8); + let to_bottom = ((self.mi_rows as i32 - bh - b.r as i32) * 8) * 8; + v.clamp(to_top - border, (to_bottom + border).max(to_top - border)) + } + + fn clamp_mv_col(&self, b: &Blk, v: i32, border: i32) -> i32 { + let bw = NUM_8X8_WIDE[b.size as usize] as i32; + let to_left = -((b.c as i32 * 8) * 8); + let to_right = ((self.mi_cols as i32 - bw - b.c as i32) * 8) * 8; + v.clamp(to_left - border, (to_right + border).max(to_left - border)) + } + + /// find_mv_refs (6.5.1) for `ref_frame`; `block` is -1 or the sub-8x8 block index. + fn find_mv_refs(&mut self, b: &Blk, ref_frame: i8, block: i32) { + self.ref_mv_count = 0; + self.ref_list_mv = [Mv::ZERO; 2]; + let mut different_ref_found = false; + let mut context_counter = 0usize; + let search = &MV_REF_BLOCKS[b.size as usize * 16..b.size as usize * 16 + 16]; + let (r, c) = (b.r as isize, b.c as isize); + for i in 0..2 { + let (cr, cc) = (r + search[i * 2] as isize, c + search[i * 2 + 1] as isize); + if self.is_inside(cr, cc) { + let m = *self.mi_at(cr as usize, cc as usize); + different_ref_found = true; + context_counter += MODE_2_COUNTER[m.y_mode as usize] as usize; + for j in 0..2 { + if m.ref_frame[j] == ref_frame { + let idx = if block >= 0 { IDX_N_COLUMN_TO_SUBBLOCK[block as usize][(search[i * 2 + 1] == 0) as usize] as usize } else { 3 }; + self.add_mv_ref_list(m.mv[j][idx]); + break; + } + } + } + } + for i in 2..8 { + let (cr, cc) = (r + search[i * 2] as isize, c + search[i * 2 + 1] as isize); + if self.is_inside(cr, cc) { + different_ref_found = true; + let m = *self.mi_at(cr as usize, cc as usize); + self.if_same_ref_frame_add(&m, ref_frame); + } + } + let prev = self.s.prev_mi.map(|g| *g.at(b.r, b.c)); + if let Some(p) = &prev { + self.if_same_ref_frame_add(p, ref_frame); + } + if different_ref_found { + for i in 0..8 { + let (cr, cc) = (r + search[i * 2] as isize, c + search[i * 2 + 1] as isize); + if self.is_inside(cr, cc) { + let m = *self.mi_at(cr as usize, cc as usize); + self.if_diff_ref_frame_add(&m, ref_frame); + } + } + } + if let Some(p) = &prev { + self.if_diff_ref_frame_add(p, ref_frame); + } + self.mode_context[ref_frame as usize] = COUNTER_TO_CONTEXT[context_counter.min(18)]; + for i in 0..2 { + let mv = self.ref_list_mv[i]; + self.ref_list_mv[i] = Mv::new(self.clamp_mv_row(b, mv.row as i32, MV_BORDER), self.clamp_mv_col(b, mv.col as i32, MV_BORDER)); + } + } + + /// find_best_ref_mvs (6.5.12). + fn find_best_ref_mvs(&mut self, b: &Blk, ref_list: usize) { + for i in 0..2 { + let mv = self.ref_list_mv[i]; + let (mut dr, mut dc) = (mv.row as i32, mv.col as i32); + if !self.h.allow_high_precision_mv || !use_mv_hp(mv) { + if dr & 1 != 0 { + dr += if dr > 0 { -1 } else { 1 }; + } + if dc & 1 != 0 { + dc += if dc > 0 { -1 } else { 1 }; + } + } + let border = (BORDERINPIXELS - INTERP_EXTEND) << 3; + self.ref_list_mv[i] = Mv::new(self.clamp_mv_row(b, dr, border), self.clamp_mv_col(b, dc, border)); + } + self.nearest_mv[ref_list] = self.ref_list_mv[0]; + self.near_mv[ref_list] = self.ref_list_mv[1]; + self.best_mv[ref_list] = self.ref_list_mv[0]; + } + + /// append_sub8x8_mvs (6.5.14). + fn append_sub8x8_mvs(&mut self, b: &Blk, block: i32, ref_list: usize) { + self.find_mv_refs(b, b.ref_frame[ref_list], block); + let mut sub = [Mv::ZERO; 2]; + let mut dst; + let bm = &b.block_mvs[ref_list]; + if block == 0 { + sub = self.ref_list_mv; + dst = 2; + } else if block <= 2 { + sub[0] = bm[0]; + dst = 1; + } else { + sub[0] = bm[2]; + dst = 1; + for idx in [1usize, 0] { + if dst < 2 && bm[idx] != sub[0] { + sub[dst] = bm[idx]; + dst += 1; + } + } + } + for n in 0..2 { + if dst < 2 && self.ref_list_mv[n] != sub[0] { + sub[dst] = self.ref_list_mv[n]; + dst += 1; + } + } + if dst < 2 { + sub[dst] = Mv::ZERO; + } + self.nearest_mv[ref_list] = sub[0]; + self.near_mv[ref_list] = sub[1]; + } +} + +#[inline] +pub fn use_mv_hp(mv: Mv) -> bool { + ((mv.row as i32).abs() >> 3) < COMPANDED_MVREF_THRESH && ((mv.col as i32).abs() >> 3) < COMPANDED_MVREF_THRESH +} diff --git a/crates/vp9/src/tile/recon.rs b/crates/vp9/src/tile/recon.rs new file mode 100644 index 0000000..99c3228 --- /dev/null +++ b/crates/vp9/src/tile/recon.rs @@ -0,0 +1,345 @@ +//! Residual syntax (6.4.21 - 6.4.26) with the prediction and reconstruction processes it +//! triggers (8.5, 8.6). + +use super::{Blk, TileDecoder}; +use crate::boolcoder::BoolDecoder; +use crate::error::Error; +use crate::frame::Mv; +use crate::inter::{self, RefPlane}; +use crate::intra::{self, IntraEdge}; +use crate::tables::*; +use crate::transform; + +/// round_mv_comp_q2 / q4 (8.5.2.1). +fn round_q2(v: i32) -> i32 { + (if v < 0 { v - 1 } else { v + 1 }) / 2 +} +fn round_q4(v: i32) -> i32 { + (if v < 0 { v - 2 } else { v + 2 }) / 4 +} + +impl<'a> TileDecoder<'a> { + pub(super) fn residual(&mut self, bd: &mut BoolDecoder, b: &mut Blk) { + let bsize = b.size.max(BLOCK_8X8) as usize; + let (ssx, ssy) = (self.ss_x, self.ss_y); + let uv_tx = if b.size < BLOCK_8X8 { 0 } else { b.tx_size.min(MAX_TXSIZE[SS_SIZE_LOOKUP[b.size as usize][ssx][ssy] as usize]) }; + for plane in 0..3 { + let tx_sz = if plane > 0 { uv_tx } else { b.tx_size }; + let step = 1usize << tx_sz; + let (sx, sy) = if plane > 0 { (ssx, ssy) } else { (0, 0) }; + let plane_sz = if plane > 0 { SS_SIZE_LOOKUP[bsize][ssx][ssy] as usize } else { bsize }; + let n4w = NUM_4X4_WIDE[plane_sz] as usize; + let n4h = NUM_4X4_HIGH[plane_sz] as usize; + let base_x = (b.c * 8) >> sx; + let base_y = (b.r * 8) >> sy; + if b.is_inter { + if b.size < BLOCK_8X8 { + for y in 0..n4h { + for x in 0..n4w { + self.predict_inter(b, plane, base_x + 4 * x, base_y + 4 * y, 4, 4, y * n4w + x); + } + } + } else { + self.predict_inter(b, plane, base_x, base_y, n4w * 4, n4h * 4, 0); + } + } + let max_x = (self.mi_cols * 8) >> sx; + let max_y = (self.mi_rows * 8) >> sy; + let mut block_idx = 0; + for y in (0..n4h).step_by(step) { + for x in (0..n4w).step_by(step) { + let start_x = base_x + 4 * x; + let start_y = base_y + 4 * y; + let mut nonzero = false; + if start_x < max_x && start_y < max_y { + let tx_type = self.tx_type(b, plane, tx_sz, block_idx); + if !b.is_inter { + let mode = if plane > 0 { + b.uv_mode + } else if b.size >= BLOCK_8X8 { + b.y_mode + } else { + b.sub_modes[block_idx] + }; + let edge = IntraEdge { + have_left: b.avail_l || x > 0, + have_above: b.avail_u || y > 0, + not_on_right: x + step < n4w, + tx_size: tx_sz, + max_x: max_x - 1, + max_y: max_y - 1, + bit_depth: self.bit_depth, + }; + let (stride, x_off) = (self.strip.strides[plane], self.strip.x_off[plane]); + intra::predict(&mut self.strip.planes[plane], stride, x_off, start_x, start_y, mode, &edge); + } + if !b.skip { + let (eob, rows) = self.tokens(bd, b, plane, start_x, start_y, tx_sz, tx_type); + nonzero = eob > 0; + b.eob_total += nonzero as u32; + if eob > 0 { + let (stride, x_off) = (self.strip.strides[plane], self.strip.x_off[plane]); + let o = start_y * stride + start_x - x_off; + transform::inverse_transform_add( + &self.coefs, + tx_sz, + tx_type, + self.h.lossless, + eob, + rows, + self.bit_depth, + &mut self.strip.planes[plane][o..], + stride, + ); + let sc = scan(tx_sz, tx_type); + for &p in &sc[..eob] { + self.coefs[p as usize] = 0; + } + } + } + } + let ax = start_x >> 2; + let ly = start_y >> 2; + let an = &mut self.above_nonzero[plane]; + let e = (ax + step).min(an.len()); + an[ax.min(e)..e].fill(nonzero as u8); + let ln = &mut self.left_nonzero[plane]; + let e = (ly + step).min(ln.len()); + ln[ly.min(e)..e].fill(nonzero as u8); + block_idx += 1; + } + } + } + } + + /// Transform type (6.4.25). + fn tx_type(&self, b: &Blk, plane: usize, tx_sz: u8, block_idx: usize) -> u8 { + if plane > 0 || tx_sz == TX_32X32 { + DCT_DCT + } else if tx_sz == TX_4X4 { + if self.h.lossless || b.is_inter { + DCT_DCT + } else { + MODE2TXFM[if b.size < BLOCK_8X8 { b.sub_modes[block_idx] } else { b.y_mode } as usize] + } + } else { + MODE2TXFM[b.y_mode as usize] + } + } + + /// tokens (6.4.24): reads and dequantizes the coefficients into `self.coefs`. Returns the end + /// of block position and the number of leading coefficient rows that may be non-zero. + #[allow(clippy::too_many_arguments)] + fn tokens(&mut self, bd: &mut BoolDecoder, b: &Blk, plane: usize, start_x: usize, start_y: usize, tx_sz: u8, tx_type: u8) -> (usize, usize) { + let seg_eob = 16usize << (tx_sz << 1); + let sc = scan(tx_sz, tx_type); + let nb = neighbors(tx_sz, tx_type); + let bands: &[u8] = if tx_sz == 0 { &COEFBAND_4X4 } else { &COEFBAND_8X8PLUS }; + let ptype = (plane > 0) as usize; + let rtype = b.is_inter as usize; + let probs = &self.fc.coef[tx_sz as usize][ptype][rtype]; + let (sx, sy) = if plane > 0 { (self.ss_x, self.ss_y) } else { (0, 0) }; + let max_x4 = (2 * self.mi_cols) >> sx; + let max_y4 = (2 * self.mi_rows) >> sy; + let n = 1usize << tx_sz; + let (x4, y4) = (start_x >> 2, start_y >> 2); + let mut above = 0u8; + let mut left = 0u8; + for i in 0..n { + if x4 + i < max_x4 { + above |= self.above_nonzero[plane][x4 + i]; + } + if y4 + i < max_y4 { + left |= self.left_nonzero[plane][y4 + i]; + } + } + let ctx0 = (above + left) as usize; + let q = self.s.seg_q[b.seg_id as usize][ptype]; + let dq_shift = (tx_sz == TX_32X32) as u32; + let pareto = pareto_full(); + let counting = self.s.counting; + let log2n = 2 + tx_sz as usize; + let bit_depth = self.bit_depth; + let mut max_row = 0usize; + let mut check_eob = true; + let mut c = 0usize; + let tc = &mut self.token_cache; + let coefs = &mut self.coefs; + let counts = &mut self.strip.counts; + while c < seg_eob { + let pos = sc[c] as usize; + let band = bands[c] as usize; + let ctx = if c == 0 { + ctx0 + } else { + let (a, bb) = nb[c]; + ((1 + tc[a as usize] + tc[bb as usize]) >> 1) as usize + }; + let p = &probs[band][ctx]; + if check_eob { + let more = bd.read_bool(p[0]); + if counting { + counts.more_coefs[tx_sz as usize][ptype][rtype][band][ctx][more as usize] += 1; + } + if !more { + break; + } + } + if !bd.read_bool(p[1]) { + tc[pos] = 0; + if counting { + counts.token[tx_sz as usize][ptype][rtype][band][ctx][0] += 1; + } + check_eob = false; + c += 1; + continue; + } + check_eob = true; + let token: usize; + let coef: i64; + if !bd.read_bool(p[2]) { + if counting { + counts.token[tx_sz as usize][ptype][rtype][band][ctx][1] += 1; + } + token = 1; + coef = 1; + } else { + if counting { + counts.token[tx_sz as usize][ptype][rtype][band][ctx][2] += 1; + } + let pp = &pareto[p[2].max(1) as usize - 1]; + token = if !bd.read_bool(pp[0]) { + if !bd.read_bool(pp[1]) { + 2 + } else if !bd.read_bool(pp[2]) { + 3 + } else { + 4 + } + } else if !bd.read_bool(pp[3]) { + if !bd.read_bool(pp[4]) { 5 } else { 6 } + } else if !bd.read_bool(pp[5]) { + if !bd.read_bool(pp[6]) { 7 } else { 8 } + } else if !bd.read_bool(pp[7]) { + 9 + } else { + 10 + }; + // read_coef (6.4.26) + let (cat, num_extra, base) = EXTRA_BITS[token]; + let mut v = base as i64; + if token == 10 { + for e in 0..(bit_depth as u32 - 8) { + if bd.read_bool(255) { + v += 1 << (5 + bit_depth as u32 - e); + } + } + } + let cp = CAT_PROBS[cat as usize]; + for e in 0..num_extra as usize { + if bd.read_bool(cp[e]) { + v += 1 << (num_extra as usize - 1 - e); + } + } + coef = v; + } + tc[pos] = ENERGY_CLASS[token]; + let sign = bd.read_bool(128); + let qv = if pos == 0 { q[0] } else { q[1] } as i64; + let d = ((coef * qv) >> dq_shift) as i32; + coefs[pos] = if sign { d.wrapping_neg() } else { d }; + max_row = max_row.max(pos >> log2n); + c += 1; + } + (c, max_row + 1) + } + + /// Inter prediction process (8.5.2) for one block region. + #[allow(clippy::too_many_arguments)] + fn predict_inter(&mut self, b: &Blk, plane: usize, x: usize, y: usize, w: usize, h: usize, block_idx: usize) { + let is_compound = b.ref_frame[1] > INTRA_FRAME; + let (sx, sy) = if plane > 0 { (self.ss_x, self.ss_y) } else { (0, 0) }; + for rl in 0..1 + is_compound as usize { + // Motion vector selection (8.5.2.1). + let bm = &b.block_mvs[rl]; + let mv = if plane == 0 || b.size >= BLOCK_8X8 || (sx == 0 && sy == 0) { + bm[block_idx] + } else if sx == 0 && sy == 1 { + Mv::new( + round_q2(bm[block_idx].row as i32 + bm[block_idx + 2].row as i32), + round_q2(bm[block_idx].col as i32 + bm[block_idx + 2].col as i32), + ) + } else if sx == 1 && sy == 0 { + Mv::new( + round_q2(bm[block_idx].row as i32 + bm[block_idx + 1].row as i32), + round_q2(bm[block_idx].col as i32 + bm[block_idx + 1].col as i32), + ) + } else { + let sr: i32 = bm.iter().map(|m| m.row as i32).sum(); + let sc: i32 = bm.iter().map(|m| m.col as i32).sum(); + Mv::new(round_q4(sr), round_q4(sc)) + }; + // Motion vector clamping (8.5.2.2). + let bh = NUM_8X8_HIGH[b.size as usize] as i32; + let bw = NUM_8X8_WIDE[b.size as usize] as i32; + let (r, c) = (b.r as i32, b.c as i32); + let to_top = (-(r * 8 * 16)) >> sy; + let to_bottom = (((self.mi_rows as i32 - bh - r) * 8) * 16) >> sy; + let to_left = (-(c * 8 * 16)) >> sx; + let to_right = (((self.mi_cols as i32 - bw - c) * 8) * 16) >> sx; + let spel_left = (INTERP_EXTEND + ((bw * 8) >> sx)) << 4; + let spel_right = spel_left - 16; + let spel_top = (INTERP_EXTEND + ((bh * 8) >> sy)) << 4; + let spel_bottom = spel_top - 16; + let cmv_row = ((2 * mv.row as i32) >> sy).clamp(to_top - spel_top, (to_bottom + spel_bottom).max(to_top - spel_top)); + let cmv_col = ((2 * mv.col as i32) >> sx).clamp(to_left - spel_left, (to_right + spel_right).max(to_left - spel_left)); + let ri = (b.ref_frame[rl] - 1).clamp(0, 2) as usize; + let Some(rf) = &self.s.refs[ri] else { + if self.error.is_none() { + self.error = Some(Error::MissingReference(format!("block uses unusable reference {}", b.ref_frame[rl]))); + } + let out = if rl == 0 { &mut self.pred } else { &mut self.pred2 }; + out[..w * h].fill(1 << (self.bit_depth - 1)); + continue; + }; + // Motion vector scaling (8.5.2.3). + let (x_scale, y_scale) = (rf.x_scale as i64, rf.y_scale as i64); + let (start_x, start_y, step_x, step_y) = if rf.x_step == 16 && rf.y_step == 16 && x_scale == 1 << 14 && y_scale == 1 << 14 { + (((x as i32) << 4) + cmv_col, ((y as i32) << 4) + cmv_row, 16, 16) + } else { + self.strip.scaled_blocks += 1; + let base_x = (x as i64 * x_scale) >> 14; + let base_y = (y as i64 * y_scale) >> 14; + let luma_x = (x << sx) as i64; + let luma_y = (y << sy) as i64; + let frac_x = ((16 * luma_x * x_scale) >> 14) & 15; + let frac_y = ((16 * luma_y * y_scale) >> 14) & 15; + let dx = ((cmv_col as i64 * x_scale) >> 14) + frac_x; + let dy = ((cmv_row as i64 * y_scale) >> 14) + frac_y; + (((base_x << 4) + dx) as i32, ((base_y << 4) + dy) as i32, rf.x_step, rf.y_step) + }; + let f = rf.frame; + let rp = RefPlane { + frame: f, + plane, + last_x: ((f.info.width as i32 + sx as i32) >> sx) - 1, + last_y: ((f.info.height as i32 + sy as i32) >> sy) - 1, + }; + let out = if rl == 0 { &mut self.pred } else { &mut self.pred2 }; + inter::predict(&rp, start_x, start_y, step_x, step_y, w, h, b.interp_filter, self.bit_depth, out, &mut self.mc_tmp, &mut self.mc_win); + } + let (stride, x_off) = (self.strip.strides[plane], self.strip.x_off[plane]); + let dst = &mut self.strip.planes[plane]; + for i in 0..h { + let o = (y + i) * stride + x - x_off; + let row = &mut dst[o..o + w]; + if is_compound { + for ((d, &a), &bb) in row.iter_mut().zip(&self.pred[i * w..i * w + w]).zip(&self.pred2[i * w..i * w + w]) { + *d = ((a as u32 + bb as u32 + 1) >> 1) as u16; + } + } else { + row.copy_from_slice(&self.pred[i * w..i * w + w]); + } + } + } +} diff --git a/crates/vp9/src/transform.rs b/crates/vp9/src/transform.rs new file mode 100644 index 0000000..30658a9 --- /dev/null +++ b/crates/vp9/src/transform.rs @@ -0,0 +1,534 @@ +//! 2D inverse transforms and reconstruction (8.6.2, 8.7.2). The 1D DCT / ADST butterflies are +//! generated from the specification's step lists (`transform_gen.rs`); ADST4 (8.7.1.6) and the +//! Walsh-Hadamard transform (8.7.1.10) are written out here. + +use crate::tables::{ADST_ADST, ADST_DCT, DCT_ADST, DCT_DCT}; +use crate::transform_gen::{narrow, wide}; + +const SINPI_1_9: i64 = 5283; +const SINPI_2_9: i64 = 9929; +const SINPI_3_9: i64 = 13377; +const SINPI_4_9: i64 = 15212; + +macro_rules! adst4 { + ($name:ident, $t:ty) => { + #[inline(always)] + fn $name(x: [$t; 4]) -> [$t; 4] { + let (s1_9, s2_9, s3_9, s4_9) = (SINPI_1_9 as $t, SINPI_2_9 as $t, SINPI_3_9 as $t, SINPI_4_9 as $t); + let s0 = s1_9.wrapping_mul(x[0]); + let s1 = s2_9.wrapping_mul(x[0]); + let s2 = s3_9.wrapping_mul(x[1]); + let s3 = s4_9.wrapping_mul(x[2]); + let s4 = s1_9.wrapping_mul(x[2]); + let s5 = s2_9.wrapping_mul(x[3]); + let s6 = s4_9.wrapping_mul(x[3]); + let v = x[0].wrapping_sub(x[2]).wrapping_add(x[3]); + let s7 = s3_9.wrapping_mul(v); + let x0 = s0.wrapping_add(s3).wrapping_add(s5); + let x1 = s1.wrapping_sub(s4).wrapping_sub(s6); + let x2 = s7; + let x3 = s2; + let r = |v: $t| v.wrapping_add(1 << 13) >> 14; + [r(x0.wrapping_add(x3)), r(x1.wrapping_add(x3)), r(x2), r(x0.wrapping_add(x1).wrapping_sub(x3))] + } + }; +} +adst4!(iadst4_narrow, i32); +adst4!(iadst4_wide, i64); + +/// Inverse Walsh-Hadamard transform (8.7.1.10). +fn iwht4(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 = a.wrapping_add(c); + d = d.wrapping_sub(b); + let e = a.wrapping_sub(d) >> 1; + b = e.wrapping_sub(b); + c = e.wrapping_sub(c); + a = a.wrapping_sub(b); + d = d.wrapping_add(c); + [a, b, c, d] +} + +/// Apply a 1D transform of length `n` (4, 8, 16, 32) in place. +#[inline(always)] +fn tx1d_narrow(buf: &mut [i32], n: usize, adst: bool) { + match (n, adst) { + (4, false) => { + if let Some(a) = buf.first_chunk_mut::<4>() { + *a = narrow::idct4(*a); + } + } + (4, true) => { + if let Some(a) = buf.first_chunk_mut::<4>() { + *a = iadst4_narrow(*a); + } + } + (8, false) => { + if let Some(a) = buf.first_chunk_mut::<8>() { + *a = narrow::idct8(*a); + } + } + (8, true) => { + if let Some(a) = buf.first_chunk_mut::<8>() { + *a = narrow::iadst8(*a); + } + } + (16, false) => { + if let Some(a) = buf.first_chunk_mut::<16>() { + *a = narrow::idct16(*a); + } + } + (16, true) => { + if let Some(a) = buf.first_chunk_mut::<16>() { + *a = narrow::iadst16(*a); + } + } + _ => { + if let Some(a) = buf.first_chunk_mut::<32>() { + *a = narrow::idct32(*a); + } + } + } +} + +#[inline(always)] +fn tx1d_wide(buf: &mut [i64], n: usize, adst: bool) { + match (n, adst) { + (4, false) => { + if let Some(a) = buf.first_chunk_mut::<4>() { + *a = wide::idct4(*a); + } + } + (4, true) => { + if let Some(a) = buf.first_chunk_mut::<4>() { + *a = iadst4_wide(*a); + } + } + (8, false) => { + if let Some(a) = buf.first_chunk_mut::<8>() { + *a = wide::idct8(*a); + } + } + (8, true) => { + if let Some(a) = buf.first_chunk_mut::<8>() { + *a = wide::iadst8(*a); + } + } + (16, false) => { + if let Some(a) = buf.first_chunk_mut::<16>() { + *a = wide::idct16(*a); + } + } + (16, true) => { + if let Some(a) = buf.first_chunk_mut::<16>() { + *a = wide::iadst16(*a); + } + } + _ => { + if let Some(a) = buf.first_chunk_mut::<32>() { + *a = wide::idct32(*a); + } + } + } +} + +/// Clamp coefficients so that garbage streams cannot overflow the 32-bit arithmetic (conformant +/// streams keep far smaller values, 8 + BitDepth bits). +#[inline] +fn sat(v: i32, lim: i32) -> i32 { + v.clamp(-lim, lim) +} + +/// Inverse transform `coefs` (row-major n x n dequantized coefficients, `rows` = number of leading +/// rows that can hold non-zero values) and add the residual to `dst` (8.6.2 step 4). +#[allow(clippy::too_many_arguments)] +pub fn inverse_transform_add( + coefs: &[i32], + tx_size: u8, + tx_type: u8, + lossless: bool, + eob: usize, + rows: usize, + bit_depth: u8, + dst: &mut [u16], + stride: usize, +) { + let n = 4usize << tx_size; + let max = (1i32 << bit_depth) - 1; + if lossless { + let mut t = [0i32; 16]; + for i in 0..4 { + let c = coefs.get(i * 4..).and_then(|c| c.first_chunk::<4>()).copied().unwrap_or_default(); + let r = iwht4(c, 2); + t[i * 4..i * 4 + 4].copy_from_slice(&r); + } + for j in 0..4 { + let r = iwht4([t[j], t[4 + j], t[8 + j], t[12 + j]], 0); + for i in 0..4 { + let p = &mut dst[i * stride + j]; + *p = (*p as i32).saturating_add(r[i]).clamp(0, max) as u16; + } + } + return; + } + let shift = (tx_size as u32 + 4).min(6); + let round = 1i32 << (shift - 1); + if eob == 1 && tx_type == DCT_DCT { + // Only the DC coefficient is present: every output of both passes is equal (8.7.2 with a + // single non-zero input). + let dc = coefs[0] as i64; + let a = (dc * 11585 + (1 << 13)) >> 14; + let b = (a * 11585 + (1 << 13)) >> 14; + let v = ((b + round as i64) >> shift).clamp(-(1 << 20), 1 << 20) as i32; + for i in 0..n { + for p in dst[i * stride..i * stride + n].iter_mut() { + *p = (*p as i32 + v).clamp(0, max) as u16; + } + } + return; + } + let row_adst = matches!(tx_type, DCT_ADST | ADST_ADST); + let col_adst = matches!(tx_type, ADST_DCT | ADST_ADST); + let p = TxParams { row_adst, col_adst, rows, shift, round, max }; + match (tx_size, bit_depth == 8) { + (0, true) => itx_narrow::<4>(coefs, &p, dst, stride), + (1, true) => itx_narrow::<8>(coefs, &p, dst, stride), + (2, true) => itx_narrow::<16>(coefs, &p, dst, stride), + (_, true) => itx_narrow::<32>(coefs, &p, dst, stride), + (0, false) => itx_wide::<4>(coefs, &p, dst, stride), + (1, false) => itx_wide::<8>(coefs, &p, dst, stride), + (2, false) => itx_wide::<16>(coefs, &p, dst, stride), + (_, false) => itx_wide::<32>(coefs, &p, dst, stride), + } +} + +struct TxParams { + row_adst: bool, + col_adst: bool, + rows: usize, + shift: u32, + round: i32, + max: i32, +} + +/// 2D inverse transform of size N with 32-bit intermediates (8-bit streams). +fn itx_narrow(coefs: &[i32], p: &TxParams, dst: &mut [u16], stride: usize) { + let lim = 1 << 24; + let mut t = [[0i32; N]; N]; + for (i, row) in t.iter_mut().enumerate().take(p.rows.min(N)) { + for (d, s) in row.iter_mut().zip(&coefs[i * N..i * N + N]) { + *d = sat(*s, lim); + } + tx1d_narrow(row, N, p.row_adst); + for v in row.iter_mut() { + *v = sat(*v, lim); + } + } + // Column pass across 4 / 8 columns at once (the same butterflies on lane vectors), then + // the rounding, residual add and clipping per row of lanes. + if N == 4 { + let x: [Cols<4>; N] = std::array::from_fn(|i| Cols(t[i].first_chunk::<4>().copied().unwrap_or_default())); + let y = col_tx4(x, p.col_adst); + add_cols(&y, 0, p, dst, stride); + } else { + for c0 in (0..N).step_by(8) { + let x: [Cols<8>; N] = std::array::from_fn(|i| Cols(t[i].get(c0..).and_then(|r| r.first_chunk::<8>()).copied().unwrap_or_default())); + let y = col_tx8(x, p.col_adst); + add_cols(&y, c0, p, dst, stride); + } + } +} + +/// Round the column pass output `y` (columns c0..c0 + W of every row) and add it to `dst`. +#[inline(always)] +fn add_cols(y: &[Cols; N], c0: usize, p: &TxParams, dst: &mut [u16], stride: usize) { + for (i, v) in y.iter().enumerate() { + let Some(d) = dst.get_mut(i * stride + c0..).and_then(|d| d.first_chunk_mut::()) else { + return; + }; + for j in 0..W { + let r = (v.0[j].wrapping_add(p.round) >> p.shift).clamp(-(1 << 20), 1 << 20); + d[j] = (d[j] as i32 + r).max(0).min(p.max) as u16; + } + } +} + +/// `W` columns of 32-bit values processed together: the generated butterflies +/// (`transform_gen::cols4` / `cols8`) run on these lane vectors with the scalar arithmetic of +/// `narrow` applied per lane, so the column pass vectorises. +#[derive(Clone, Copy)] +pub struct Cols(pub [i32; W]); + +impl From for Cols { + #[inline(always)] + fn from(v: i32) -> Self { + Cols([v; W]) + } +} + +impl Cols { + #[inline(always)] + pub fn wrapping_add>(self, o: O) -> Self { + let o = o.into(); + Cols(std::array::from_fn(|i| self.0[i].wrapping_add(o.0[i]))) + } + #[inline(always)] + pub fn wrapping_sub(self, o: Self) -> Self { + Cols(std::array::from_fn(|i| self.0[i].wrapping_sub(o.0[i]))) + } + #[inline(always)] + pub fn wrapping_mul(self, c: i32) -> Self { + Cols(self.0.map(|v| v.wrapping_mul(c))) + } + #[inline(always)] + pub fn wrapping_neg(self) -> Self { + Cols(self.0.map(|v| v.wrapping_neg())) + } +} + +impl std::ops::Shr for Cols { + type Output = Self; + #[inline(always)] + fn shr(self, s: i32) -> Self { + Cols(self.0.map(|v| v >> s)) + } +} + +/// `x` (N rows of 4 columns) through the 1D column transform. +#[inline(always)] +fn col_tx4(x: [Cols<4>; N], adst: bool) -> [Cols<4>; N] { + use crate::transform_gen::cols4; + let r = if adst { iadst4_cols(fixed(&x)) } else { cols4::idct4(fixed(&x)) }; + std::array::from_fn(|i| r[i]) +} + +/// `x` (N rows of 8 columns) through the 1D column transform. +#[inline(always)] +fn col_tx8(x: [Cols<8>; N], adst: bool) -> [Cols<8>; N] { + use crate::transform_gen::cols8; + let mut out = x; + match (N, adst) { + (8, false) => out.copy_from_slice(&cols8::idct8(fixed(&x))), + (8, true) => out.copy_from_slice(&cols8::iadst8(fixed(&x))), + (16, false) => out.copy_from_slice(&cols8::idct16(fixed(&x))), + (16, true) => out.copy_from_slice(&cols8::iadst16(fixed(&x))), + _ => out.copy_from_slice(&cols8::idct32(fixed(&x))), + } + out +} + +/// A slice of known length as an array. +#[inline(always)] +fn fixed, const M: usize>(x: &[T]) -> [T; M] { + x.first_chunk::().copied().unwrap_or([T::from(0); M]) +} + +/// ADST4 (8.7.1.6) on 4 columns. +#[inline(always)] +fn iadst4_cols(x: [Cols<4>; 4]) -> [Cols<4>; 4] { + let s0 = x[0].wrapping_mul(SINPI_1_9 as i32); + let s1 = x[0].wrapping_mul(SINPI_2_9 as i32); + let s2 = x[1].wrapping_mul(SINPI_3_9 as i32); + let s3 = x[2].wrapping_mul(SINPI_4_9 as i32); + let s4 = x[2].wrapping_mul(SINPI_1_9 as i32); + let s5 = x[3].wrapping_mul(SINPI_2_9 as i32); + let s6 = x[3].wrapping_mul(SINPI_4_9 as i32); + let v = x[0].wrapping_sub(x[2]).wrapping_add(x[3]); + let s7 = v.wrapping_mul(SINPI_3_9 as i32); + let x0 = s0.wrapping_add(s3).wrapping_add(s5); + let x1 = s1.wrapping_sub(s4).wrapping_sub(s6); + let x2 = s7; + let x3 = s2; + let r = |v: Cols<4>| v.wrapping_add(1 << 13) >> 14; + [r(x0.wrapping_add(x3)), r(x1.wrapping_add(x3)), r(x2), r(x0.wrapping_add(x1).wrapping_sub(x3))] +} + +/// 2D inverse transform of size N with 64-bit intermediates (high bit depth). +fn itx_wide(coefs: &[i32], p: &TxParams, dst: &mut [u16], stride: usize) { + let lim = 1i64 << 40; + let mut t = [[0i64; N]; N]; + for (i, row) in t.iter_mut().enumerate().take(p.rows.min(N)) { + for (d, s) in row.iter_mut().zip(&coefs[i * N..i * N + N]) { + *d = *s as i64; + } + tx1d_wide(row, N, p.row_adst); + for v in row.iter_mut() { + *v = (*v).clamp(-lim, lim); + } + } + let mut col = [0i64; N]; + for j in 0..N { + for i in 0..N { + col[i] = t[i][j]; + } + tx1d_wide(&mut col, N, p.col_adst); + for i in 0..N { + let d = &mut dst[i * stride + j]; + let r = ((col[i].clamp(-lim, lim) + p.round as i64) >> p.shift).clamp(-(1 << 30), 1 << 30) as i32; + *d = (*d as i32 + r).clamp(0, p.max) as u16; + } + } +} + +#[cfg(test)] +mod tests { + use super::*; + + /// Reference inverse DCT (orthonormal, scaled like VP9: DC gain 1/sqrt(2) per pass times + /// sqrt(2) ... ) — compare basis functions up to rounding. + fn float_idct(input: &[f64]) -> Vec { + let n = input.len(); + (0..n) + .map(|x| { + let mut s = input[0] / 2f64.sqrt(); + for (k, &v) in input.iter().enumerate().skip(1) { + s += v * ((2 * x + 1) as f64 * k as f64 * std::f64::consts::PI / (2 * n) as f64).cos(); + } + s + }) + .collect() + } + + #[test] + fn idct_matches_float_reference() { + for n in [4usize, 8, 16, 32] { + for k in 0..n { + let mut buf = vec![0i32; n]; + buf[k] = 1000; + tx1d_narrow(&mut buf, n, false); + let mut inp = vec![0f64; n]; + inp[k] = 1000.0; + let r = float_idct(&inp); + for (a, b) in buf.iter().zip(&r) { + assert!((*a as f64 - b).abs() <= 3.0, "n {n} k {k}: {buf:?} vs {r:?}"); + } + let mut w: Vec = vec![0; n]; + w[k] = 1000; + tx1d_wide(&mut w, n, false); + assert_eq!(w.iter().map(|&v| v as i32).collect::>(), buf); + } + } + } + + #[test] + fn adst_is_sine_basis() { + // VP9's ADST (for n = 8, 16) is a DST-IV variant: out[x] ~ sum in[k] sin(pi (2x+1)(2k+1) / 4n). + for n in [8usize, 16] { + for k in 0..n { + let mut buf = vec![0i32; n]; + buf[k] = 1000; + tx1d_narrow(&mut buf, n, true); + let r: Vec = (0..n) + .map(|x| { + 1000.0 * ((2 * x + 1) as f64 * (2 * k + 1) as f64 * std::f64::consts::PI / (4 * n) as f64).sin() * 2f64.sqrt() / 2f64.sqrt() + }) + .collect(); + for (a, b) in buf.iter().zip(&r) { + assert!((*a as f64 - b).abs() <= 4.0, "n {n} k {k}: {buf:?} vs {r:?}"); + } + } + } + // ADST4 is the sine transform sin(pi (x+1)(2k+1) / 9) scaled. + for k in 0..4 { + let mut buf = vec![0i32; 4]; + buf[k] = 1000; + tx1d_narrow(&mut buf, 4, true); + let r: Vec = + (0..4).map(|x| 1000.0 * (std::f64::consts::PI * (x + 1) as f64 * (2 * k + 1) as f64 / 9.0).sin() * 2.0 * 2f64.sqrt() / 3.0).collect(); + for (a, b) in buf.iter().zip(&r) { + assert!((*a as f64 - b).abs() <= 3.0, "k {k}: {buf:?} vs {r:?}"); + } + } + } + + #[test] + fn dc_only_shortcut_matches_full_transform() { + for tx in 0..4u8 { + let n = 4usize << tx; + for dc in [-5000, -37, -1, 1, 2, 57, 1234, 9999] { + let mut coefs = vec![0i32; n * n]; + coefs[0] = dc; + let mut a = vec![128u16; n * n]; + let mut b = vec![128u16; n * n]; + inverse_transform_add(&coefs, tx, DCT_DCT, false, 1, 1, 8, &mut a, n); + inverse_transform_add(&coefs, tx, DCT_DCT, false, 2, n, 8, &mut b, n); + assert_eq!(a, b, "tx {tx} dc {dc}"); + } + } + } + + /// The per-column formulation the vectorised column pass replaced. + fn itx_narrow_ref(coefs: &[i32], p: &TxParams, dst: &mut [u16], stride: usize) { + let lim = 1 << 24; + let mut t = [[0i32; N]; N]; + for (i, row) in t.iter_mut().enumerate().take(p.rows.min(N)) { + for (d, s) in row.iter_mut().zip(&coefs[i * N..i * N + N]) { + *d = sat(*s, lim); + } + tx1d_narrow(row, N, p.row_adst); + for v in row.iter_mut() { + *v = sat(*v, lim); + } + } + let mut col = [0i32; N]; + for j in 0..N { + for i in 0..N { + col[i] = t[i][j]; + } + tx1d_narrow(&mut col, N, p.col_adst); + for i in 0..N { + let d = &mut dst[i * stride + j]; + *d = (*d as i32 + (col[i].wrapping_add(p.round) >> p.shift).clamp(-(1 << 20), 1 << 20)).clamp(0, p.max) as u16; + } + } + } + + fn check_cols(seed: &mut u32) { + let mut rnd = || { + *seed ^= *seed << 13; + *seed ^= *seed >> 17; + *seed ^= *seed << 5; + *seed + }; + let tx_size = N.trailing_zeros() - 2; + let shift = (tx_size + 4).min(6); + for case in 0..200 { + let (row_adst, col_adst) = if N == 32 { (false, false) } else { (case & 1 == 1, case & 2 == 2) }; + let rows = 1 + rnd() as usize % N; + let big = if case % 7 == 0 { 1 << 22 } else { 2000 }; + let coefs: Vec = + (0..N * N).map(|i| if i / N < rows && rnd() % 3 == 0 { (rnd() % (2 * big)) as i32 - big as i32 } else { 0 }).collect(); + let p = TxParams { row_adst, col_adst, rows, shift, round: 1 << (shift - 1), max: 255 }; + let stride = N + 5; + let base: Vec = (0..N * stride).map(|_| (rnd() % 256) as u16).collect(); + let (mut a, mut b) = (base.clone(), base); + itx_narrow::(&coefs, &p, &mut a, stride); + itx_narrow_ref::(&coefs, &p, &mut b, stride); + assert_eq!(a, b, "N {N} case {case}"); + } + } + + #[test] + fn column_pass_matches_per_column_transform() { + let mut seed = 0x9e37_79b9u32; + check_cols::<4>(&mut seed); + check_cols::<8>(&mut seed); + check_cols::<16>(&mut seed); + check_cols::<32>(&mut seed); + } + + #[test] + fn wht_round_trip_identity() { + // A single DC coefficient of 4 * v spreads v / 4 ... check invertibility on a simple case: + // input [4, 0, ..] (after the >> 2 pre-scaling a unit impulse). + let mut coefs = [0i32; 16]; + coefs[0] = 4 * 4; + let mut dst = [100u16; 16]; + inverse_transform_add(&coefs, 0, DCT_DCT, true, 1, 4, 8, &mut dst, 4); + let sum: i32 = dst.iter().map(|&v| v as i32 - 100).sum(); + assert_eq!(sum, 16); + } +} diff --git a/crates/vp9/src/transform_gen.rs b/crates/vp9/src/transform_gen.rs new file mode 100644 index 0000000..9a26462 --- /dev/null +++ b/crates/vp9/src/transform_gen.rs @@ -0,0 +1,489 @@ +// Generated by tools/gen_transforms.py from the butterfly descriptions of the VP9 specification +// v0.6 section 8.7.1. Do not edit by hand. + +macro_rules! transforms_1d { + ($t:ty) => { + #[inline(always)] + fn r14(x: $t) -> $t { + x.wrapping_add(1 << 13) >> 14 + } + + #[inline(always)] + pub fn idct4(x: [$t; 4]) -> [$t; 4] { + let v0 = r14(x[0].wrapping_mul(11585).wrapping_add(x[2].wrapping_mul(-11585))); + let v1 = r14(x[0].wrapping_mul(11585).wrapping_add(x[2].wrapping_mul(11585))); + let v2 = r14(x[1].wrapping_mul(6270).wrapping_add(x[3].wrapping_mul(-15137))); + let v3 = r14(x[1].wrapping_mul(15137).wrapping_add(x[3].wrapping_mul(6270))); + let v4 = v1.wrapping_add(v3); + let v5 = v1.wrapping_sub(v3); + let v6 = v0.wrapping_add(v2); + let v7 = v0.wrapping_sub(v2); + [v4, v6, v7, v5] + } + + #[inline(always)] + pub fn idct8(x: [$t; 8]) -> [$t; 8] { + let v0 = r14(x[0].wrapping_mul(11585).wrapping_add(x[4].wrapping_mul(-11585))); + let v1 = r14(x[0].wrapping_mul(11585).wrapping_add(x[4].wrapping_mul(11585))); + let v2 = r14(x[2].wrapping_mul(6270).wrapping_add(x[6].wrapping_mul(-15137))); + let v3 = r14(x[2].wrapping_mul(15137).wrapping_add(x[6].wrapping_mul(6270))); + let v4 = v1.wrapping_add(v3); + let v5 = v1.wrapping_sub(v3); + let v6 = v0.wrapping_add(v2); + let v7 = v0.wrapping_sub(v2); + let v8 = r14(x[1].wrapping_mul(3196).wrapping_add(x[7].wrapping_mul(-16069))); + let v9 = r14(x[1].wrapping_mul(16069).wrapping_add(x[7].wrapping_mul(3196))); + let v10 = r14(x[5].wrapping_mul(13623).wrapping_add(x[3].wrapping_mul(-9102))); + let v11 = r14(x[5].wrapping_mul(9102).wrapping_add(x[3].wrapping_mul(13623))); + let v12 = v8.wrapping_add(v10); + let v13 = v8.wrapping_sub(v10); + let v14 = v9.wrapping_add(v11); + let v15 = v9.wrapping_sub(v11); + let v16 = r14(v15.wrapping_mul(11585).wrapping_add(v13.wrapping_mul(-11585))); + let v17 = r14(v15.wrapping_mul(11585).wrapping_add(v13.wrapping_mul(11585))); + let v18 = v4.wrapping_add(v14); + let v19 = v4.wrapping_sub(v14); + let v20 = v6.wrapping_add(v17); + let v21 = v6.wrapping_sub(v17); + let v22 = v7.wrapping_add(v16); + let v23 = v7.wrapping_sub(v16); + let v24 = v5.wrapping_add(v12); + let v25 = v5.wrapping_sub(v12); + [v18, v20, v22, v24, v25, v23, v21, v19] + } + + #[inline(always)] + pub fn idct16(x: [$t; 16]) -> [$t; 16] { + let v0 = r14(x[0].wrapping_mul(11585).wrapping_add(x[8].wrapping_mul(-11585))); + let v1 = r14(x[0].wrapping_mul(11585).wrapping_add(x[8].wrapping_mul(11585))); + let v2 = r14(x[4].wrapping_mul(6270).wrapping_add(x[12].wrapping_mul(-15137))); + let v3 = r14(x[4].wrapping_mul(15137).wrapping_add(x[12].wrapping_mul(6270))); + let v4 = v1.wrapping_add(v3); + let v5 = v1.wrapping_sub(v3); + let v6 = v0.wrapping_add(v2); + let v7 = v0.wrapping_sub(v2); + let v8 = r14(x[2].wrapping_mul(3196).wrapping_add(x[14].wrapping_mul(-16069))); + let v9 = r14(x[2].wrapping_mul(16069).wrapping_add(x[14].wrapping_mul(3196))); + let v10 = r14(x[10].wrapping_mul(13623).wrapping_add(x[6].wrapping_mul(-9102))); + let v11 = r14(x[10].wrapping_mul(9102).wrapping_add(x[6].wrapping_mul(13623))); + let v12 = v8.wrapping_add(v10); + let v13 = v8.wrapping_sub(v10); + let v14 = v9.wrapping_add(v11); + let v15 = v9.wrapping_sub(v11); + let v16 = r14(v15.wrapping_mul(11585).wrapping_add(v13.wrapping_mul(-11585))); + let v17 = r14(v15.wrapping_mul(11585).wrapping_add(v13.wrapping_mul(11585))); + let v18 = v4.wrapping_add(v14); + let v19 = v4.wrapping_sub(v14); + let v20 = v6.wrapping_add(v17); + let v21 = v6.wrapping_sub(v17); + let v22 = v7.wrapping_add(v16); + let v23 = v7.wrapping_sub(v16); + let v24 = v5.wrapping_add(v12); + let v25 = v5.wrapping_sub(v12); + let v26 = r14(x[1].wrapping_mul(1606).wrapping_add(x[15].wrapping_mul(-16305))); + let v27 = r14(x[1].wrapping_mul(16305).wrapping_add(x[15].wrapping_mul(1606))); + let v28 = r14(x[9].wrapping_mul(12665).wrapping_add(x[7].wrapping_mul(-10394))); + let v29 = r14(x[9].wrapping_mul(10394).wrapping_add(x[7].wrapping_mul(12665))); + let v30 = r14(x[5].wrapping_mul(7723).wrapping_add(x[11].wrapping_mul(-14449))); + let v31 = r14(x[5].wrapping_mul(14449).wrapping_add(x[11].wrapping_mul(7723))); + let v32 = r14(x[13].wrapping_mul(15679).wrapping_add(x[3].wrapping_mul(-4756))); + let v33 = r14(x[13].wrapping_mul(4756).wrapping_add(x[3].wrapping_mul(15679))); + let v34 = v26.wrapping_add(v28); + let v35 = v26.wrapping_sub(v28); + let v36 = v32.wrapping_add(v30); + let v37 = v32.wrapping_sub(v30); + let v38 = v33.wrapping_add(v31); + let v39 = v33.wrapping_sub(v31); + let v40 = v27.wrapping_add(v29); + let v41 = v27.wrapping_sub(v29); + let v42 = r14(v41.wrapping_mul(6270).wrapping_add(v35.wrapping_mul(-15137))); + let v43 = r14(v41.wrapping_mul(15137).wrapping_add(v35.wrapping_mul(6270))); + let v44 = r14(v37.wrapping_mul(-15137).wrapping_add(v39.wrapping_mul(6270))); + let v45 = r14(v37.wrapping_mul(-6270).wrapping_add(v39.wrapping_mul(-15137))); + let v46 = v34.wrapping_add(v36); + let v47 = v34.wrapping_sub(v36); + let v48 = v40.wrapping_add(v38); + let v49 = v40.wrapping_sub(v38); + let v50 = v42.wrapping_add(v45); + let v51 = v42.wrapping_sub(v45); + let v52 = v43.wrapping_add(v44); + let v53 = v43.wrapping_sub(v44); + let v54 = r14(v53.wrapping_mul(11585).wrapping_add(v51.wrapping_mul(-11585))); + let v55 = r14(v53.wrapping_mul(11585).wrapping_add(v51.wrapping_mul(11585))); + let v56 = r14(v49.wrapping_mul(11585).wrapping_add(v47.wrapping_mul(-11585))); + let v57 = r14(v49.wrapping_mul(11585).wrapping_add(v47.wrapping_mul(11585))); + let v58 = v18.wrapping_add(v48); + let v59 = v18.wrapping_sub(v48); + let v60 = v20.wrapping_add(v52); + let v61 = v20.wrapping_sub(v52); + let v62 = v22.wrapping_add(v55); + let v63 = v22.wrapping_sub(v55); + let v64 = v24.wrapping_add(v57); + let v65 = v24.wrapping_sub(v57); + let v66 = v25.wrapping_add(v56); + let v67 = v25.wrapping_sub(v56); + let v68 = v23.wrapping_add(v54); + let v69 = v23.wrapping_sub(v54); + let v70 = v21.wrapping_add(v50); + let v71 = v21.wrapping_sub(v50); + let v72 = v19.wrapping_add(v46); + let v73 = v19.wrapping_sub(v46); + [v58, v60, v62, v64, v66, v68, v70, v72, v73, v71, v69, v67, v65, v63, v61, v59] + } + + #[inline(always)] + pub fn idct32(x: [$t; 32]) -> [$t; 32] { + let v0 = r14(x[0].wrapping_mul(11585).wrapping_add(x[16].wrapping_mul(-11585))); + let v1 = r14(x[0].wrapping_mul(11585).wrapping_add(x[16].wrapping_mul(11585))); + let v2 = r14(x[8].wrapping_mul(6270).wrapping_add(x[24].wrapping_mul(-15137))); + let v3 = r14(x[8].wrapping_mul(15137).wrapping_add(x[24].wrapping_mul(6270))); + let v4 = v1.wrapping_add(v3); + let v5 = v1.wrapping_sub(v3); + let v6 = v0.wrapping_add(v2); + let v7 = v0.wrapping_sub(v2); + let v8 = r14(x[4].wrapping_mul(3196).wrapping_add(x[28].wrapping_mul(-16069))); + let v9 = r14(x[4].wrapping_mul(16069).wrapping_add(x[28].wrapping_mul(3196))); + let v10 = r14(x[20].wrapping_mul(13623).wrapping_add(x[12].wrapping_mul(-9102))); + let v11 = r14(x[20].wrapping_mul(9102).wrapping_add(x[12].wrapping_mul(13623))); + let v12 = v8.wrapping_add(v10); + let v13 = v8.wrapping_sub(v10); + let v14 = v9.wrapping_add(v11); + let v15 = v9.wrapping_sub(v11); + let v16 = r14(v15.wrapping_mul(11585).wrapping_add(v13.wrapping_mul(-11585))); + let v17 = r14(v15.wrapping_mul(11585).wrapping_add(v13.wrapping_mul(11585))); + let v18 = v4.wrapping_add(v14); + let v19 = v4.wrapping_sub(v14); + let v20 = v6.wrapping_add(v17); + let v21 = v6.wrapping_sub(v17); + let v22 = v7.wrapping_add(v16); + let v23 = v7.wrapping_sub(v16); + let v24 = v5.wrapping_add(v12); + let v25 = v5.wrapping_sub(v12); + let v26 = r14(x[2].wrapping_mul(1606).wrapping_add(x[30].wrapping_mul(-16305))); + let v27 = r14(x[2].wrapping_mul(16305).wrapping_add(x[30].wrapping_mul(1606))); + let v28 = r14(x[18].wrapping_mul(12665).wrapping_add(x[14].wrapping_mul(-10394))); + let v29 = r14(x[18].wrapping_mul(10394).wrapping_add(x[14].wrapping_mul(12665))); + let v30 = r14(x[10].wrapping_mul(7723).wrapping_add(x[22].wrapping_mul(-14449))); + let v31 = r14(x[10].wrapping_mul(14449).wrapping_add(x[22].wrapping_mul(7723))); + let v32 = r14(x[26].wrapping_mul(15679).wrapping_add(x[6].wrapping_mul(-4756))); + let v33 = r14(x[26].wrapping_mul(4756).wrapping_add(x[6].wrapping_mul(15679))); + let v34 = v26.wrapping_add(v28); + let v35 = v26.wrapping_sub(v28); + let v36 = v32.wrapping_add(v30); + let v37 = v32.wrapping_sub(v30); + let v38 = v33.wrapping_add(v31); + let v39 = v33.wrapping_sub(v31); + let v40 = v27.wrapping_add(v29); + let v41 = v27.wrapping_sub(v29); + let v42 = r14(v41.wrapping_mul(6270).wrapping_add(v35.wrapping_mul(-15137))); + let v43 = r14(v41.wrapping_mul(15137).wrapping_add(v35.wrapping_mul(6270))); + let v44 = r14(v37.wrapping_mul(-15137).wrapping_add(v39.wrapping_mul(6270))); + let v45 = r14(v37.wrapping_mul(-6270).wrapping_add(v39.wrapping_mul(-15137))); + let v46 = v34.wrapping_add(v36); + let v47 = v34.wrapping_sub(v36); + let v48 = v40.wrapping_add(v38); + let v49 = v40.wrapping_sub(v38); + let v50 = v42.wrapping_add(v45); + let v51 = v42.wrapping_sub(v45); + let v52 = v43.wrapping_add(v44); + let v53 = v43.wrapping_sub(v44); + let v54 = r14(v53.wrapping_mul(11585).wrapping_add(v51.wrapping_mul(-11585))); + let v55 = r14(v53.wrapping_mul(11585).wrapping_add(v51.wrapping_mul(11585))); + let v56 = r14(v49.wrapping_mul(11585).wrapping_add(v47.wrapping_mul(-11585))); + let v57 = r14(v49.wrapping_mul(11585).wrapping_add(v47.wrapping_mul(11585))); + let v58 = v18.wrapping_add(v48); + let v59 = v18.wrapping_sub(v48); + let v60 = v20.wrapping_add(v52); + let v61 = v20.wrapping_sub(v52); + let v62 = v22.wrapping_add(v55); + let v63 = v22.wrapping_sub(v55); + let v64 = v24.wrapping_add(v57); + let v65 = v24.wrapping_sub(v57); + let v66 = v25.wrapping_add(v56); + let v67 = v25.wrapping_sub(v56); + let v68 = v23.wrapping_add(v54); + let v69 = v23.wrapping_sub(v54); + let v70 = v21.wrapping_add(v50); + let v71 = v21.wrapping_sub(v50); + let v72 = v19.wrapping_add(v46); + let v73 = v19.wrapping_sub(v46); + let v74 = r14(x[1].wrapping_mul(804).wrapping_add(x[31].wrapping_mul(-16364))); + let v75 = r14(x[1].wrapping_mul(16364).wrapping_add(x[31].wrapping_mul(804))); + let v76 = r14(x[17].wrapping_mul(12140).wrapping_add(x[15].wrapping_mul(-11003))); + let v77 = r14(x[17].wrapping_mul(11003).wrapping_add(x[15].wrapping_mul(12140))); + let v78 = r14(x[9].wrapping_mul(7005).wrapping_add(x[23].wrapping_mul(-14811))); + let v79 = r14(x[9].wrapping_mul(14811).wrapping_add(x[23].wrapping_mul(7005))); + let v80 = r14(x[25].wrapping_mul(15426).wrapping_add(x[7].wrapping_mul(-5520))); + let v81 = r14(x[25].wrapping_mul(5520).wrapping_add(x[7].wrapping_mul(15426))); + let v82 = r14(x[5].wrapping_mul(3981).wrapping_add(x[27].wrapping_mul(-15893))); + let v83 = r14(x[5].wrapping_mul(15893).wrapping_add(x[27].wrapping_mul(3981))); + let v84 = r14(x[21].wrapping_mul(14053).wrapping_add(x[11].wrapping_mul(-8423))); + let v85 = r14(x[21].wrapping_mul(8423).wrapping_add(x[11].wrapping_mul(14053))); + let v86 = r14(x[13].wrapping_mul(9760).wrapping_add(x[19].wrapping_mul(-13160))); + let v87 = r14(x[13].wrapping_mul(13160).wrapping_add(x[19].wrapping_mul(9760))); + let v88 = r14(x[29].wrapping_mul(16207).wrapping_add(x[3].wrapping_mul(-2404))); + let v89 = r14(x[29].wrapping_mul(2404).wrapping_add(x[3].wrapping_mul(16207))); + let v90 = v74.wrapping_add(v76); + let v91 = v74.wrapping_sub(v76); + let v92 = v80.wrapping_add(v78); + let v93 = v80.wrapping_sub(v78); + let v94 = v82.wrapping_add(v84); + let v95 = v82.wrapping_sub(v84); + let v96 = v88.wrapping_add(v86); + let v97 = v88.wrapping_sub(v86); + let v98 = v89.wrapping_add(v87); + let v99 = v89.wrapping_sub(v87); + let v100 = v83.wrapping_add(v85); + let v101 = v83.wrapping_sub(v85); + let v102 = v81.wrapping_add(v79); + let v103 = v81.wrapping_sub(v79); + let v104 = v75.wrapping_add(v77); + let v105 = v75.wrapping_sub(v77); + let v106 = r14(v105.wrapping_mul(3196).wrapping_add(v91.wrapping_mul(-16069))); + let v107 = r14(v105.wrapping_mul(16069).wrapping_add(v91.wrapping_mul(3196))); + let v108 = r14(v97.wrapping_mul(-9102).wrapping_add(v99.wrapping_mul(13623))); + let v109 = r14(v97.wrapping_mul(-13623).wrapping_add(v99.wrapping_mul(-9102))); + let v110 = r14(v101.wrapping_mul(13623).wrapping_add(v95.wrapping_mul(-9102))); + let v111 = r14(v101.wrapping_mul(9102).wrapping_add(v95.wrapping_mul(13623))); + let v112 = r14(v93.wrapping_mul(-16069).wrapping_add(v103.wrapping_mul(3196))); + let v113 = r14(v93.wrapping_mul(-3196).wrapping_add(v103.wrapping_mul(-16069))); + let v114 = v90.wrapping_add(v92); + let v115 = v90.wrapping_sub(v92); + let v116 = v96.wrapping_add(v94); + let v117 = v96.wrapping_sub(v94); + let v118 = v98.wrapping_add(v100); + let v119 = v98.wrapping_sub(v100); + let v120 = v104.wrapping_add(v102); + let v121 = v104.wrapping_sub(v102); + let v122 = v106.wrapping_add(v113); + let v123 = v106.wrapping_sub(v113); + let v124 = v109.wrapping_add(v110); + let v125 = v109.wrapping_sub(v110); + let v126 = v108.wrapping_add(v111); + let v127 = v108.wrapping_sub(v111); + let v128 = v107.wrapping_add(v112); + let v129 = v107.wrapping_sub(v112); + let v130 = r14(v129.wrapping_mul(6270).wrapping_add(v123.wrapping_mul(-15137))); + let v131 = r14(v129.wrapping_mul(15137).wrapping_add(v123.wrapping_mul(6270))); + let v132 = r14(v125.wrapping_mul(-15137).wrapping_add(v127.wrapping_mul(6270))); + let v133 = r14(v125.wrapping_mul(-6270).wrapping_add(v127.wrapping_mul(-15137))); + let v134 = r14(v121.wrapping_mul(6270).wrapping_add(v115.wrapping_mul(-15137))); + let v135 = r14(v121.wrapping_mul(15137).wrapping_add(v115.wrapping_mul(6270))); + let v136 = r14(v117.wrapping_mul(-15137).wrapping_add(v119.wrapping_mul(6270))); + let v137 = r14(v117.wrapping_mul(-6270).wrapping_add(v119.wrapping_mul(-15137))); + let v138 = v114.wrapping_add(v116); + let v139 = v114.wrapping_sub(v116); + let v140 = v120.wrapping_add(v118); + let v141 = v120.wrapping_sub(v118); + let v142 = v122.wrapping_add(v124); + let v143 = v122.wrapping_sub(v124); + let v144 = v128.wrapping_add(v126); + let v145 = v128.wrapping_sub(v126); + let v146 = v130.wrapping_add(v133); + let v147 = v130.wrapping_sub(v133); + let v148 = v131.wrapping_add(v132); + let v149 = v131.wrapping_sub(v132); + let v150 = v134.wrapping_add(v137); + let v151 = v134.wrapping_sub(v137); + let v152 = v135.wrapping_add(v136); + let v153 = v135.wrapping_sub(v136); + let v154 = r14(v153.wrapping_mul(11585).wrapping_add(v151.wrapping_mul(-11585))); + let v155 = r14(v153.wrapping_mul(11585).wrapping_add(v151.wrapping_mul(11585))); + let v156 = r14(v149.wrapping_mul(11585).wrapping_add(v147.wrapping_mul(-11585))); + let v157 = r14(v149.wrapping_mul(11585).wrapping_add(v147.wrapping_mul(11585))); + let v158 = r14(v145.wrapping_mul(11585).wrapping_add(v143.wrapping_mul(-11585))); + let v159 = r14(v145.wrapping_mul(11585).wrapping_add(v143.wrapping_mul(11585))); + let v160 = r14(v141.wrapping_mul(11585).wrapping_add(v139.wrapping_mul(-11585))); + let v161 = r14(v141.wrapping_mul(11585).wrapping_add(v139.wrapping_mul(11585))); + let v162 = v58.wrapping_add(v140); + let v163 = v58.wrapping_sub(v140); + let v164 = v60.wrapping_add(v144); + let v165 = v60.wrapping_sub(v144); + let v166 = v62.wrapping_add(v148); + let v167 = v62.wrapping_sub(v148); + let v168 = v64.wrapping_add(v152); + let v169 = v64.wrapping_sub(v152); + let v170 = v66.wrapping_add(v155); + let v171 = v66.wrapping_sub(v155); + let v172 = v68.wrapping_add(v157); + let v173 = v68.wrapping_sub(v157); + let v174 = v70.wrapping_add(v159); + let v175 = v70.wrapping_sub(v159); + let v176 = v72.wrapping_add(v161); + let v177 = v72.wrapping_sub(v161); + let v178 = v73.wrapping_add(v160); + let v179 = v73.wrapping_sub(v160); + let v180 = v71.wrapping_add(v158); + let v181 = v71.wrapping_sub(v158); + let v182 = v69.wrapping_add(v156); + let v183 = v69.wrapping_sub(v156); + let v184 = v67.wrapping_add(v154); + let v185 = v67.wrapping_sub(v154); + let v186 = v65.wrapping_add(v150); + let v187 = v65.wrapping_sub(v150); + let v188 = v63.wrapping_add(v146); + let v189 = v63.wrapping_sub(v146); + let v190 = v61.wrapping_add(v142); + let v191 = v61.wrapping_sub(v142); + let v192 = v59.wrapping_add(v138); + let v193 = v59.wrapping_sub(v138); + [v162, v164, v166, v168, v170, v172, v174, v176, v178, v180, v182, v184, v186, v188, v190, v192, v193, v191, v189, v187, v185, v183, v181, v179, v177, v175, v173, v171, v169, v167, v165, v163] + } + + #[inline(always)] + pub fn iadst8(x: [$t; 8]) -> [$t; 8] { + let v0 = x[7].wrapping_mul(1606).wrapping_add(x[0].wrapping_mul(-16305)); + let v1 = x[7].wrapping_mul(16305).wrapping_add(x[0].wrapping_mul(1606)); + let v2 = x[5].wrapping_mul(7723).wrapping_add(x[2].wrapping_mul(-14449)); + let v3 = x[5].wrapping_mul(14449).wrapping_add(x[2].wrapping_mul(7723)); + let v4 = x[3].wrapping_mul(12665).wrapping_add(x[4].wrapping_mul(-10394)); + let v5 = x[3].wrapping_mul(10394).wrapping_add(x[4].wrapping_mul(12665)); + let v6 = x[1].wrapping_mul(15679).wrapping_add(x[6].wrapping_mul(-4756)); + let v7 = x[1].wrapping_mul(4756).wrapping_add(x[6].wrapping_mul(15679)); + let v8 = r14(v1.wrapping_add(v5)); + let v9 = r14(v1.wrapping_sub(v5)); + let v10 = r14(v0.wrapping_add(v4)); + let v11 = r14(v0.wrapping_sub(v4)); + let v12 = r14(v3.wrapping_add(v7)); + let v13 = r14(v3.wrapping_sub(v7)); + let v14 = r14(v2.wrapping_add(v6)); + let v15 = r14(v2.wrapping_sub(v6)); + let v16 = v9.wrapping_mul(6270).wrapping_add(v11.wrapping_mul(-15137)); + let v17 = v9.wrapping_mul(15137).wrapping_add(v11.wrapping_mul(6270)); + let v18 = v15.wrapping_mul(15137).wrapping_add(v13.wrapping_mul(-6270)); + let v19 = v15.wrapping_mul(6270).wrapping_add(v13.wrapping_mul(15137)); + let v20 = r14(v17.wrapping_add(v18)); + let v21 = r14(v17.wrapping_sub(v18)); + let v22 = r14(v16.wrapping_add(v19)); + let v23 = r14(v16.wrapping_sub(v19)); + let v24 = v8.wrapping_add(v12); + let v25 = v8.wrapping_sub(v12); + let v26 = v10.wrapping_add(v14); + let v27 = v10.wrapping_sub(v14); + let v28 = r14(v25.wrapping_mul(11585).wrapping_add(v27.wrapping_mul(-11585))); + let v29 = r14(v25.wrapping_mul(11585).wrapping_add(v27.wrapping_mul(11585))); + let v30 = r14(v21.wrapping_mul(11585).wrapping_add(v23.wrapping_mul(-11585))); + let v31 = r14(v21.wrapping_mul(11585).wrapping_add(v23.wrapping_mul(11585))); + let v32 = v20.wrapping_neg(); + let v33 = v29.wrapping_neg(); + let v34 = v30.wrapping_neg(); + let v35 = v26.wrapping_neg(); + [v24, v32, v31, v33, v28, v34, v22, v35] + } + + #[inline(always)] + pub fn iadst16(x: [$t; 16]) -> [$t; 16] { + let v0 = x[15].wrapping_mul(804).wrapping_add(x[0].wrapping_mul(-16364)); + let v1 = x[15].wrapping_mul(16364).wrapping_add(x[0].wrapping_mul(804)); + let v2 = x[13].wrapping_mul(3981).wrapping_add(x[2].wrapping_mul(-15893)); + let v3 = x[13].wrapping_mul(15893).wrapping_add(x[2].wrapping_mul(3981)); + let v4 = x[11].wrapping_mul(7005).wrapping_add(x[4].wrapping_mul(-14811)); + let v5 = x[11].wrapping_mul(14811).wrapping_add(x[4].wrapping_mul(7005)); + let v6 = x[9].wrapping_mul(9760).wrapping_add(x[6].wrapping_mul(-13160)); + let v7 = x[9].wrapping_mul(13160).wrapping_add(x[6].wrapping_mul(9760)); + let v8 = x[7].wrapping_mul(12140).wrapping_add(x[8].wrapping_mul(-11003)); + let v9 = x[7].wrapping_mul(11003).wrapping_add(x[8].wrapping_mul(12140)); + let v10 = x[5].wrapping_mul(14053).wrapping_add(x[10].wrapping_mul(-8423)); + let v11 = x[5].wrapping_mul(8423).wrapping_add(x[10].wrapping_mul(14053)); + let v12 = x[3].wrapping_mul(15426).wrapping_add(x[12].wrapping_mul(-5520)); + let v13 = x[3].wrapping_mul(5520).wrapping_add(x[12].wrapping_mul(15426)); + let v14 = x[1].wrapping_mul(16207).wrapping_add(x[14].wrapping_mul(-2404)); + let v15 = x[1].wrapping_mul(2404).wrapping_add(x[14].wrapping_mul(16207)); + let v16 = r14(v1.wrapping_add(v9)); + let v17 = r14(v1.wrapping_sub(v9)); + let v18 = r14(v0.wrapping_add(v8)); + let v19 = r14(v0.wrapping_sub(v8)); + let v20 = r14(v3.wrapping_add(v11)); + let v21 = r14(v3.wrapping_sub(v11)); + let v22 = r14(v2.wrapping_add(v10)); + let v23 = r14(v2.wrapping_sub(v10)); + let v24 = r14(v5.wrapping_add(v13)); + let v25 = r14(v5.wrapping_sub(v13)); + let v26 = r14(v4.wrapping_add(v12)); + let v27 = r14(v4.wrapping_sub(v12)); + let v28 = r14(v7.wrapping_add(v15)); + let v29 = r14(v7.wrapping_sub(v15)); + let v30 = r14(v6.wrapping_add(v14)); + let v31 = r14(v6.wrapping_sub(v14)); + let v32 = v17.wrapping_mul(3196).wrapping_add(v19.wrapping_mul(-16069)); + let v33 = v17.wrapping_mul(16069).wrapping_add(v19.wrapping_mul(3196)); + let v34 = v21.wrapping_mul(13623).wrapping_add(v23.wrapping_mul(-9102)); + let v35 = v21.wrapping_mul(9102).wrapping_add(v23.wrapping_mul(13623)); + let v36 = v25.wrapping_mul(16069).wrapping_add(v27.wrapping_mul(3196)); + let v37 = v25.wrapping_mul(-3196).wrapping_add(v27.wrapping_mul(16069)); + let v38 = v29.wrapping_mul(9102).wrapping_add(v31.wrapping_mul(13623)); + let v39 = v29.wrapping_mul(-13623).wrapping_add(v31.wrapping_mul(9102)); + let v40 = r14(v33.wrapping_add(v37)); + let v41 = r14(v33.wrapping_sub(v37)); + let v42 = r14(v32.wrapping_add(v36)); + let v43 = r14(v32.wrapping_sub(v36)); + let v44 = r14(v35.wrapping_add(v39)); + let v45 = r14(v35.wrapping_sub(v39)); + let v46 = r14(v34.wrapping_add(v38)); + let v47 = r14(v34.wrapping_sub(v38)); + let v48 = v16.wrapping_add(v24); + let v49 = v16.wrapping_sub(v24); + let v50 = v18.wrapping_add(v26); + let v51 = v18.wrapping_sub(v26); + let v52 = v20.wrapping_add(v28); + let v53 = v20.wrapping_sub(v28); + let v54 = v22.wrapping_add(v30); + let v55 = v22.wrapping_sub(v30); + let v56 = v49.wrapping_mul(6270).wrapping_add(v51.wrapping_mul(-15137)); + let v57 = v49.wrapping_mul(15137).wrapping_add(v51.wrapping_mul(6270)); + let v58 = v55.wrapping_mul(15137).wrapping_add(v53.wrapping_mul(-6270)); + let v59 = v55.wrapping_mul(6270).wrapping_add(v53.wrapping_mul(15137)); + let v60 = v41.wrapping_mul(6270).wrapping_add(v43.wrapping_mul(-15137)); + let v61 = v41.wrapping_mul(15137).wrapping_add(v43.wrapping_mul(6270)); + let v62 = v47.wrapping_mul(15137).wrapping_add(v45.wrapping_mul(-6270)); + let v63 = v47.wrapping_mul(6270).wrapping_add(v45.wrapping_mul(15137)); + let v64 = r14(v57.wrapping_add(v58)); + let v65 = r14(v57.wrapping_sub(v58)); + let v66 = r14(v61.wrapping_add(v62)); + let v67 = r14(v61.wrapping_sub(v62)); + let v68 = r14(v56.wrapping_add(v59)); + let v69 = r14(v56.wrapping_sub(v59)); + let v70 = r14(v60.wrapping_add(v63)); + let v71 = r14(v60.wrapping_sub(v63)); + let v72 = v48.wrapping_add(v52); + let v73 = v48.wrapping_sub(v52); + let v74 = v40.wrapping_add(v44); + let v75 = v40.wrapping_sub(v44); + let v76 = v50.wrapping_add(v54); + let v77 = v50.wrapping_sub(v54); + let v78 = v42.wrapping_add(v46); + let v79 = v42.wrapping_sub(v46); + let v80 = r14(v73.wrapping_mul(-11585).wrapping_add(v77.wrapping_mul(-11585))); + let v81 = r14(v73.wrapping_mul(11585).wrapping_add(v77.wrapping_mul(-11585))); + let v82 = r14(v65.wrapping_mul(11585).wrapping_add(v69.wrapping_mul(11585))); + let v83 = r14(v65.wrapping_mul(-11585).wrapping_add(v69.wrapping_mul(11585))); + let v84 = r14(v75.wrapping_mul(11585).wrapping_add(v79.wrapping_mul(11585))); + let v85 = r14(v75.wrapping_mul(-11585).wrapping_add(v79.wrapping_mul(11585))); + let v86 = r14(v67.wrapping_mul(-11585).wrapping_add(v71.wrapping_mul(-11585))); + let v87 = r14(v67.wrapping_mul(11585).wrapping_add(v71.wrapping_mul(-11585))); + let v88 = v74.wrapping_neg(); + let v89 = v70.wrapping_neg(); + let v90 = v64.wrapping_neg(); + let v91 = v76.wrapping_neg(); + [v72, v88, v66, v90, v82, v86, v84, v80, v81, v85, v87, v83, v68, v89, v78, v91] + } + + }; +} + +pub(crate) mod narrow { + transforms_1d!(i32); +} + +pub(crate) mod wide { + transforms_1d!(i64); +} + +/// The same butterflies on 4 / 8 columns at once (column pass of the 2D transforms). +pub(crate) mod cols4 { + transforms_1d!(crate::transform::Cols<4>); +} + +pub(crate) mod cols8 { + transforms_1d!(crate::transform::Cols<8>); +} diff --git a/xtask/src/layers.rs b/xtask/src/layers.rs index 5180294..53d5872 100644 --- a/xtask/src/layers.rs +++ b/xtask/src/layers.rs @@ -34,6 +34,18 @@ impl Class { /// prefix. pub const TABLE: &[(&str, Class)] = &[ ("geom", Class::Layer(0)), + // Codecs and containers (ported from FilmCraft): L0 standalone leaves, L1 codecs over + // `bitstream`, and `media` (probe, decode, playback mixer) at L2. + ("bitstream", Class::Standalone), + ("matroska", Class::Standalone), + ("ogg", Class::Standalone), + ("opus", Class::Standalone), + ("h264", Class::Layer(1)), + ("hevc", Class::Layer(1)), + ("vp9", Class::Layer(1)), + ("av1", Class::Layer(1)), + ("isobmff", Class::Layer(1)), + ("media", Class::Layer(2)), ("color", Class::Layer(0)), ("model", Class::Layer(1)), ("fonts", Class::Layer(1)),